]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
Merge branch 'sched-locking-for-linus' of git://git.kernel.org/pub/scm/linux/kernel...
authorLinus Torvalds <torvalds@linux-foundation.org>
Wed, 24 Jun 2015 21:46:01 +0000 (14:46 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Wed, 24 Jun 2015 21:46:01 +0000 (14:46 -0700)
Pull locking updates from Thomas Gleixner:
 "These locking updates depend on the alreay merged sched/core branch:

   - Lockless top waiter wakeup for rtmutex (Davidlohr)

   - Reduce hash bucket lock contention for PI futexes (Sebastian)

   - Documentation update (Davidlohr)"

* 'sched-locking-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
  locking/rtmutex: Update stale plist comments
  futex: Lower the lock contention on the HB lock during wake up
  locking/rtmutex: Implement lockless top-waiter wakeup

2647 files changed:
Documentation/ABI/testing/sysfs-class-cxl
Documentation/ABI/testing/sysfs-class-scsi_tape [new file with mode: 0644]
Documentation/ABI/testing/sysfs-driver-hid-logitech-lg4ff
Documentation/ABI/testing/sysfs-firmware-efi
Documentation/ABI/testing/sysfs-firmware-efi-esrt [new file with mode: 0644]
Documentation/DMA-API-HOWTO.txt
Documentation/DMA-API.txt
Documentation/DocBook/crypto-API.tmpl
Documentation/RCU/arrayRCU.txt
Documentation/RCU/lockdep.txt
Documentation/RCU/rcu_dereference.txt
Documentation/RCU/whatisRCU.txt
Documentation/acpi/enumeration.txt
Documentation/arm64/booting.txt
Documentation/cpu-freq/user-guide.txt
Documentation/devicetree/bindings/arm/armv7m_systick.txt [new file with mode: 0644]
Documentation/devicetree/bindings/clock/at91-clock.txt
Documentation/devicetree/bindings/crypto/fsl-sec2.txt
Documentation/devicetree/bindings/crypto/marvell-cesa.txt [new file with mode: 0644]
Documentation/devicetree/bindings/crypto/mv_cesa.txt
Documentation/devicetree/bindings/gpio/brcm,brcmstb-gpio.txt [new file with mode: 0644]
Documentation/devicetree/bindings/gpio/gpio-etraxfs.txt [new file with mode: 0644]
Documentation/devicetree/bindings/gpio/gpio-xlp.txt [new file with mode: 0644]
Documentation/devicetree/bindings/gpio/gpio-zynq.txt
Documentation/devicetree/bindings/gpio/nxp,lpc1850-gpio.txt [new file with mode: 0644]
Documentation/devicetree/bindings/hwmon/ntc_thermistor.txt
Documentation/devicetree/bindings/input/touchscreen/tsc2005.txt
Documentation/devicetree/bindings/interrupt-controller/atmel,aic.txt
Documentation/devicetree/bindings/interrupt-controller/renesas,intc-irqpin.txt
Documentation/devicetree/bindings/iommu/arm,smmu-v3.txt [new file with mode: 0644]
Documentation/devicetree/bindings/mfd/arizona.txt
Documentation/devicetree/bindings/mfd/axp20x.txt
Documentation/devicetree/bindings/mfd/cros-ec.txt
Documentation/devicetree/bindings/mfd/da9063.txt
Documentation/devicetree/bindings/mfd/max77686.txt
Documentation/devicetree/bindings/mfd/max77693.txt
Documentation/devicetree/bindings/mmc/arasan,sdhci.txt
Documentation/devicetree/bindings/mmc/fsl-imx-esdhc.txt
Documentation/devicetree/bindings/mmc/k3-dw-mshc.txt
Documentation/devicetree/bindings/mmc/mmc-pwrseq-simple.txt
Documentation/devicetree/bindings/mmc/mmc.txt
Documentation/devicetree/bindings/mmc/mtk-sd.txt [new file with mode: 0644]
Documentation/devicetree/bindings/mmc/renesas,mmcif.txt
Documentation/devicetree/bindings/mtd/brcm,brcmnand.txt [new file with mode: 0644]
Documentation/devicetree/bindings/pci/xgene-pci-msi.txt [new file with mode: 0644]
Documentation/devicetree/bindings/power/bq24257.txt [new file with mode: 0644]
Documentation/devicetree/bindings/power/bq25890.txt [new file with mode: 0644]
Documentation/devicetree/bindings/power/opp.txt
Documentation/devicetree/bindings/power/rt9455_charger.txt [new file with mode: 0644]
Documentation/devicetree/bindings/power_supply/max17042_battery.txt
Documentation/devicetree/bindings/powerpc/fsl/fman.txt
Documentation/devicetree/bindings/powerpc/fsl/guts.txt
Documentation/devicetree/bindings/regulator/max8973-regulator.txt
Documentation/devicetree/bindings/regulator/qcom,spmi-regulator.txt [new file with mode: 0644]
Documentation/devicetree/bindings/regulator/regulator.txt
Documentation/devicetree/bindings/rtc/rtc-st-lpc.txt [new file with mode: 0644]
Documentation/devicetree/bindings/soc/fsl/qman-portals.txt
Documentation/devicetree/bindings/spi/spi-ath79.txt [new file with mode: 0644]
Documentation/devicetree/bindings/spi/spi-fsl-dspi.txt
Documentation/devicetree/bindings/spi/spi-orion.txt
Documentation/devicetree/bindings/spi/spi-sirf.txt
Documentation/devicetree/bindings/spi/spi-zynqmp-qspi.txt [new file with mode: 0644]
Documentation/devicetree/bindings/spi/spi_atmel.txt
Documentation/devicetree/bindings/spi/spi_pl022.txt
Documentation/devicetree/bindings/timer/nxp,lpc3220-timer.txt [new file with mode: 0644]
Documentation/devicetree/bindings/timer/st,stm32-timer.txt [new file with mode: 0644]
Documentation/devicetree/bindings/usb/renesas_usbhs.txt
Documentation/devicetree/bindings/vendor-prefixes.txt
Documentation/devicetree/bindings/video/ssd1307fb.txt
Documentation/devicetree/bindings/watchdog/st_lpc_wdt.txt [new file with mode: 0644]
Documentation/filesystems/Locking
Documentation/filesystems/automount-support.txt
Documentation/filesystems/porting
Documentation/filesystems/vfs.txt
Documentation/gpio/consumer.txt
Documentation/gpio/gpio-legacy.txt
Documentation/gpio/sysfs.txt
Documentation/hwmon/ntc_thermistor
Documentation/hwmon/tc74 [new file with mode: 0644]
Documentation/i2c/slave-interface
Documentation/kernel-parameters.txt
Documentation/memory-barriers.txt
Documentation/networking/udplite.txt
Documentation/power/runtime_pm.txt
Documentation/powerpc/00-INDEX
Documentation/powerpc/cxl.txt
Documentation/powerpc/dscr.txt [new file with mode: 0644]
Documentation/powerpc/transactional_memory.txt
Documentation/preempt-locking.txt
Documentation/scsi/st.txt
Documentation/target/tcm_mod_builder.py
Documentation/vfio.txt
Documentation/virtual/kvm/api.txt
Documentation/virtual/kvm/mmu.txt
Documentation/x86/boot.txt
Documentation/x86/entry_64.txt
Documentation/x86/kernel-stacks [moved from Documentation/x86/x86_64/kernel-stacks with 68% similarity]
Documentation/x86/mtrr.txt
Documentation/x86/pat.txt
Documentation/x86/x86_64/boot-options.txt
Documentation/zh_CN/gpio.txt
Kbuild
MAINTAINERS
Makefile
arch/alpha/include/asm/cmpxchg.h
arch/alpha/include/asm/pci.h
arch/alpha/kernel/core_irongate.c
arch/alpha/kernel/sys_eiger.c
arch/alpha/kernel/sys_nautilus.c
arch/arc/include/asm/io.h
arch/arm/boot/dts/am335x-bone-common.dtsi
arch/arm/boot/dts/am35xx-clocks.dtsi
arch/arm/boot/dts/am57xx-beagle-x15.dts
arch/arm/boot/dts/armada-xp-linksys-mamba.dts
arch/arm/boot/dts/dm816x.dtsi
arch/arm/boot/dts/dra7.dtsi
arch/arm/boot/dts/dra72-evm.dts
arch/arm/boot/dts/dra72x.dtsi
arch/arm/boot/dts/dra74x.dtsi
arch/arm/boot/dts/dra7xx-clocks.dtsi
arch/arm/boot/dts/imx28-cfa10036.dts
arch/arm/boot/dts/omap3-n900.dts
arch/arm/common/sa1111.c
arch/arm/crypto/Kconfig
arch/arm/crypto/Makefile
arch/arm/crypto/aes-ce-core.S
arch/arm/crypto/sha512-armv4.pl [new file with mode: 0644]
arch/arm/crypto/sha512-armv7-neon.S [deleted file]
arch/arm/crypto/sha512-core.S_shipped [new file with mode: 0644]
arch/arm/crypto/sha512-glue.c [new file with mode: 0644]
arch/arm/crypto/sha512-neon-glue.c [new file with mode: 0644]
arch/arm/crypto/sha512.h [new file with mode: 0644]
arch/arm/crypto/sha512_neon_glue.c [deleted file]
arch/arm/include/asm/barrier.h
arch/arm/include/asm/io.h
arch/arm/include/asm/kvm_asm.h
arch/arm/include/asm/kvm_host.h
arch/arm/include/asm/pci.h
arch/arm/kvm/Kconfig
arch/arm/kvm/Makefile
arch/arm/kvm/arm.c
arch/arm/kvm/interrupts.S
arch/arm/kvm/interrupts_head.S
arch/arm/kvm/mmu.c
arch/arm/kvm/psci.c
arch/arm/lib/lib1funcs.S
arch/arm/mach-davinci/da850.c
arch/arm/mach-davinci/pm_domain.c
arch/arm/mach-exynos/suspend.c
arch/arm/mach-gemini/gpio.c
arch/arm/mach-keystone/pm_domain.c
arch/arm/mach-lpc32xx/clock.c
arch/arm/mach-omap1/board-nokia770.c
arch/arm/mach-omap1/pm_bus.c
arch/arm/mach-omap2/clkt2xxx_virt_prcm_set.c
arch/arm/mach-omap2/display.c
arch/arm/mach-omap2/omap_device.c
arch/arm/mach-omap2/omap_hwmod_7xx_data.c
arch/arm/mach-omap2/sleep34xx.S
arch/arm/mach-pxa/eseries.c
arch/arm/mach-pxa/lubbock.c
arch/arm/mach-pxa/tosa.c
arch/arm/mach-sa1100/neponset.c
arch/arm/mm/init.c
arch/arm/plat-orion/common.c
arch/arm64/Kconfig
arch/arm64/boot/dts/apm/apm-storm.dtsi
arch/arm64/boot/dts/mediatek/mt8173-evb.dts
arch/arm64/configs/defconfig
arch/arm64/crypto/aes-ce-ccm-glue.c
arch/arm64/include/asm/acpi.h
arch/arm64/include/asm/alternative-asm.h [deleted file]
arch/arm64/include/asm/alternative.h
arch/arm64/include/asm/barrier.h
arch/arm64/include/asm/boot.h [new file with mode: 0644]
arch/arm64/include/asm/cacheflush.h
arch/arm64/include/asm/cpu_ops.h
arch/arm64/include/asm/cpufeature.h
arch/arm64/include/asm/cpuidle.h
arch/arm64/include/asm/dma-mapping.h
arch/arm64/include/asm/fixmap.h
arch/arm64/include/asm/insn.h
arch/arm64/include/asm/io.h
arch/arm64/include/asm/kvm_asm.h
arch/arm64/include/asm/kvm_host.h
arch/arm64/include/asm/mmu.h
arch/arm64/include/asm/perf_event.h
arch/arm64/include/asm/proc-fns.h
arch/arm64/include/asm/processor.h
arch/arm64/include/asm/psci.h
arch/arm64/include/asm/smp.h
arch/arm64/include/asm/smp_plat.h
arch/arm64/include/asm/suspend.h
arch/arm64/include/asm/system_misc.h
arch/arm64/include/asm/tlbflush.h
arch/arm64/kernel/acpi.c
arch/arm64/kernel/alternative.c
arch/arm64/kernel/asm-offsets.c
arch/arm64/kernel/cpu_ops.c
arch/arm64/kernel/cpufeature.c
arch/arm64/kernel/cpuidle.c
arch/arm64/kernel/entry.S
arch/arm64/kernel/fpsimd.c
arch/arm64/kernel/head.S
arch/arm64/kernel/insn.c
arch/arm64/kernel/perf_event.c
arch/arm64/kernel/process.c
arch/arm64/kernel/psci.c
arch/arm64/kernel/setup.c
arch/arm64/kernel/signal32.c
arch/arm64/kernel/sleep.S
arch/arm64/kernel/smp.c
arch/arm64/kernel/smp_spin_table.c
arch/arm64/kernel/suspend.c
arch/arm64/kernel/traps.c
arch/arm64/kernel/vdso/Makefile
arch/arm64/kernel/vmlinux.lds.S
arch/arm64/kvm/Kconfig
arch/arm64/kvm/Makefile
arch/arm64/kvm/hyp.S
arch/arm64/kvm/vgic-v2-switch.S
arch/arm64/kvm/vgic-v3-switch.S
arch/arm64/mm/Makefile
arch/arm64/mm/cache.S
arch/arm64/mm/context.c
arch/arm64/mm/dma-mapping.c
arch/arm64/mm/fault.c
arch/arm64/mm/flush.c
arch/arm64/mm/init.c
arch/arm64/mm/mmu.c
arch/arm64/mm/proc.S
arch/avr32/include/asm/cmpxchg.h
arch/avr32/include/asm/io.h
arch/blackfin/include/asm/io.h
arch/frv/include/asm/io.h
arch/frv/include/asm/pci.h
arch/hexagon/include/asm/cmpxchg.h
arch/ia64/Kconfig
arch/ia64/include/asm/barrier.h
arch/ia64/include/asm/hw_irq.h
arch/ia64/include/asm/intrinsics.h
arch/ia64/include/asm/iosapic.h
arch/ia64/include/asm/irq_remapping.h
arch/ia64/include/asm/module.h
arch/ia64/include/asm/native/inst.h
arch/ia64/include/asm/native/pvchk_inst.h [deleted file]
arch/ia64/include/asm/paravirt.h [deleted file]
arch/ia64/include/asm/paravirt_patch.h [deleted file]
arch/ia64/include/asm/paravirt_privop.h [deleted file]
arch/ia64/include/asm/pci.h
arch/ia64/include/uapi/asm/cmpxchg.h
arch/ia64/kernel/Makefile
arch/ia64/kernel/efi.c
arch/ia64/kernel/entry.S
arch/ia64/kernel/fsys.S
arch/ia64/kernel/gate.S
arch/ia64/kernel/gate.lds.S
arch/ia64/kernel/head.S
arch/ia64/kernel/ivt.S
arch/ia64/kernel/minstate.h
arch/ia64/kernel/module.c
arch/ia64/kernel/msi_ia64.c
arch/ia64/kernel/paravirt.c [deleted file]
arch/ia64/kernel/paravirt_inst.h [deleted file]
arch/ia64/kernel/paravirt_patch.c [deleted file]
arch/ia64/kernel/paravirt_patchlist.c [deleted file]
arch/ia64/kernel/paravirt_patchlist.h [deleted file]
arch/ia64/kernel/paravirtentry.S [deleted file]
arch/ia64/kernel/patch.c
arch/ia64/kernel/setup.c
arch/ia64/kernel/smpboot.c
arch/ia64/kernel/time.c
arch/ia64/kernel/vmlinux.lds.S
arch/ia64/mm/init.c
arch/ia64/scripts/pvcheck.sed [deleted file]
arch/m32r/include/asm/cmpxchg.h
arch/m32r/include/asm/io.h
arch/m68k/configs/amiga_defconfig
arch/m68k/configs/apollo_defconfig
arch/m68k/configs/atari_defconfig
arch/m68k/configs/bvme6000_defconfig
arch/m68k/configs/hp300_defconfig
arch/m68k/configs/mac_defconfig
arch/m68k/configs/multi_defconfig
arch/m68k/configs/mvme147_defconfig
arch/m68k/configs/mvme16x_defconfig
arch/m68k/configs/q40_defconfig
arch/m68k/configs/sun3_defconfig
arch/m68k/configs/sun3x_defconfig
arch/m68k/include/asm/cmpxchg.h
arch/m68k/include/asm/io_mm.h
arch/m68k/include/asm/io_no.h
arch/m68k/kernel/dma.c
arch/metag/include/asm/barrier.h
arch/metag/include/asm/cmpxchg.h
arch/metag/include/asm/io.h
arch/microblaze/include/asm/io.h
arch/microblaze/include/asm/pci.h
arch/microblaze/kernel/cpu/cpuinfo.c
arch/microblaze/kernel/dma.c
arch/microblaze/kernel/kgdb.c
arch/mips/ath79/setup.c
arch/mips/cavium-octeon/crypto/octeon-md5.c
arch/mips/cobalt/Makefile
arch/mips/include/asm/barrier.h
arch/mips/include/asm/cmpxchg.h
arch/mips/include/asm/kvm_host.h
arch/mips/include/asm/mach-ath79/ath79_spi_platform.h
arch/mips/include/asm/pci.h
arch/mips/include/asm/pgtable-bits.h
arch/mips/include/asm/switch_to.h
arch/mips/kernel/cpu-probe.c
arch/mips/kernel/irq.c
arch/mips/kvm/emulate.c
arch/mips/kvm/mips.c
arch/mips/loongson/common/Makefile
arch/mips/loongson/loongson-3/smp.c
arch/mips/mm/c-r4k.c
arch/mips/net/bpf_jit.c
arch/mips/pci/fixup-cobalt.c
arch/mips/pci/ops-mace.c
arch/mips/pci/pci-lantiq.c
arch/mips/ralink/ill_acc.c
arch/mn10300/include/asm/io.h
arch/mn10300/include/asm/pci.h
arch/nios2/include/asm/io.h
arch/nios2/kernel/time.c
arch/parisc/include/asm/cmpxchg.h
arch/parisc/include/asm/pci.h
arch/powerpc/Kconfig.debug
arch/powerpc/Makefile
arch/powerpc/boot/dts/b4qds.dtsi
arch/powerpc/boot/dts/fsl/b4420si-post.dtsi
arch/powerpc/boot/dts/fsl/b4860si-post.dtsi
arch/powerpc/boot/dts/fsl/b4si-post.dtsi
arch/powerpc/boot/dts/fsl/p1023si-post.dtsi
arch/powerpc/boot/dts/fsl/p2041si-post.dtsi
arch/powerpc/boot/dts/fsl/p3041si-post.dtsi
arch/powerpc/boot/dts/fsl/p4080si-post.dtsi
arch/powerpc/boot/dts/fsl/p5020si-post.dtsi
arch/powerpc/boot/dts/fsl/p5040si-post.dtsi
arch/powerpc/boot/dts/fsl/qoriq-qman1-portals.dtsi
arch/powerpc/boot/dts/fsl/t1023si-post.dtsi [new file with mode: 0644]
arch/powerpc/boot/dts/fsl/t1024si-post.dtsi [new file with mode: 0644]
arch/powerpc/boot/dts/fsl/t102xsi-pre.dtsi [new file with mode: 0644]
arch/powerpc/boot/dts/fsl/t1040si-post.dtsi
arch/powerpc/boot/dts/fsl/t2081si-post.dtsi
arch/powerpc/boot/dts/fsl/t4240si-post.dtsi
arch/powerpc/boot/dts/kmcoge4.dts
arch/powerpc/boot/dts/oca4080.dts
arch/powerpc/boot/dts/p1023rdb.dts
arch/powerpc/boot/dts/p2041rdb.dts
arch/powerpc/boot/dts/p3041ds.dts
arch/powerpc/boot/dts/p4080ds.dts
arch/powerpc/boot/dts/p5020ds.dts
arch/powerpc/boot/dts/p5040ds.dts
arch/powerpc/boot/dts/t1023rdb.dts [new file with mode: 0644]
arch/powerpc/boot/dts/t1024qds.dts [new file with mode: 0644]
arch/powerpc/boot/dts/t1024rdb.dts [new file with mode: 0644]
arch/powerpc/boot/dts/t104xqds.dtsi
arch/powerpc/boot/dts/t104xrdb.dtsi
arch/powerpc/boot/dts/t208xqds.dtsi
arch/powerpc/boot/dts/t208xrdb.dtsi
arch/powerpc/boot/dts/t4240qds.dts
arch/powerpc/boot/dts/t4240rdb.dts
arch/powerpc/configs/85xx/xes_mpc85xx_defconfig
arch/powerpc/configs/le.config [new file with mode: 0644]
arch/powerpc/configs/ppc64_defconfig
arch/powerpc/configs/pseries_defconfig
arch/powerpc/configs/pseries_le_defconfig [deleted file]
arch/powerpc/crypto/md5-glue.c
arch/powerpc/include/asm/barrier.h
arch/powerpc/include/asm/cmpxchg.h
arch/powerpc/include/asm/cputable.h
arch/powerpc/include/asm/cputhreads.h
arch/powerpc/include/asm/device.h
arch/powerpc/include/asm/eeh.h
arch/powerpc/include/asm/icswx.h [new file with mode: 0644]
arch/powerpc/include/asm/iommu.h
arch/powerpc/include/asm/kvm_book3s_64.h
arch/powerpc/include/asm/kvm_host.h
arch/powerpc/include/asm/kvm_ppc.h
arch/powerpc/include/asm/machdep.h
arch/powerpc/include/asm/mmu-8xx.h
arch/powerpc/include/asm/mmu-hash64.h
arch/powerpc/include/asm/mmu_context.h
arch/powerpc/include/asm/opal-api.h
arch/powerpc/include/asm/opal.h
arch/powerpc/include/asm/page.h
arch/powerpc/include/asm/pci-bridge.h
arch/powerpc/include/asm/pci.h
arch/powerpc/include/asm/pgtable-ppc32.h
arch/powerpc/include/asm/pgtable-ppc64.h
arch/powerpc/include/asm/pnv-pci.h
arch/powerpc/include/asm/ppc-opcode.h
arch/powerpc/include/asm/processor.h
arch/powerpc/include/asm/pte-8xx.h
arch/powerpc/include/asm/pte-book3e.h
arch/powerpc/include/asm/pte-common.h
arch/powerpc/include/asm/pte-hash64.h
arch/powerpc/include/asm/systbl.h
arch/powerpc/include/asm/trace.h
arch/powerpc/include/asm/uaccess.h
arch/powerpc/include/uapi/asm/Kbuild
arch/powerpc/include/uapi/asm/cputable.h
arch/powerpc/include/uapi/asm/eeh.h [new file with mode: 0644]
arch/powerpc/include/uapi/asm/opal-prd.h [new file with mode: 0644]
arch/powerpc/include/uapi/asm/tm.h
arch/powerpc/kernel/Makefile
arch/powerpc/kernel/asm-offsets.c
arch/powerpc/kernel/cputable.c
arch/powerpc/kernel/dma.c
arch/powerpc/kernel/eeh.c
arch/powerpc/kernel/eeh_cache.c
arch/powerpc/kernel/eeh_driver.c
arch/powerpc/kernel/entry_64.S
arch/powerpc/kernel/exceptions-64s.S
arch/powerpc/kernel/head_8xx.S
arch/powerpc/kernel/idle_e500.S
arch/powerpc/kernel/iommu.c
arch/powerpc/kernel/msi.c
arch/powerpc/kernel/pci-common.c
arch/powerpc/kernel/pci-hotplug.c
arch/powerpc/kernel/process.c
arch/powerpc/kernel/prom.c
arch/powerpc/kernel/prom_init.c
arch/powerpc/kernel/setup_64.c
arch/powerpc/kernel/sysfs.c
arch/powerpc/kernel/tm.S
arch/powerpc/kernel/traps.c
arch/powerpc/kernel/vdso.c
arch/powerpc/kernel/vio.c
arch/powerpc/kvm/book3s.c
arch/powerpc/kvm/book3s_64_mmu_hv.c
arch/powerpc/kvm/book3s_hv.c
arch/powerpc/kvm/book3s_hv_rmhandlers.S
arch/powerpc/kvm/book3s_pr.c
arch/powerpc/kvm/booke.c
arch/powerpc/kvm/powerpc.c
arch/powerpc/lib/Makefile
arch/powerpc/mm/Makefile
arch/powerpc/mm/copro_fault.c
arch/powerpc/mm/hash_native_64.c
arch/powerpc/mm/hash_utils_64.c
arch/powerpc/mm/mem.c
arch/powerpc/mm/mmu_context_hash64.c
arch/powerpc/mm/mmu_context_iommu.c [new file with mode: 0644]
arch/powerpc/mm/tlb_low_64e.S
arch/powerpc/perf/core-book3s.c
arch/powerpc/platforms/52xx/mpc52xx_gpt.c
arch/powerpc/platforms/52xx/mpc52xx_pci.c
arch/powerpc/platforms/85xx/Kconfig
arch/powerpc/platforms/85xx/corenet_generic.c
arch/powerpc/platforms/85xx/smp.c
arch/powerpc/platforms/85xx/twr_p102x.c
arch/powerpc/platforms/Kconfig.cputype
arch/powerpc/platforms/cell/axon_msi.c
arch/powerpc/platforms/cell/iommu.c
arch/powerpc/platforms/embedded6xx/hlwd-pic.c
arch/powerpc/platforms/pasemi/Makefile
arch/powerpc/platforms/pasemi/iommu.c
arch/powerpc/platforms/pasemi/msi.c [moved from arch/powerpc/sysdev/mpic_pasemi_msi.c with 93% similarity]
arch/powerpc/platforms/powernv/Kconfig
arch/powerpc/platforms/powernv/Makefile
arch/powerpc/platforms/powernv/eeh-powernv.c
arch/powerpc/platforms/powernv/idle.c [new file with mode: 0644]
arch/powerpc/platforms/powernv/opal-async.c
arch/powerpc/platforms/powernv/opal-dump.c
arch/powerpc/platforms/powernv/opal-elog.c
arch/powerpc/platforms/powernv/opal-hmi.c
arch/powerpc/platforms/powernv/opal-irqchip.c [new file with mode: 0644]
arch/powerpc/platforms/powernv/opal-memory-errors.c
arch/powerpc/platforms/powernv/opal-prd.c [new file with mode: 0644]
arch/powerpc/platforms/powernv/opal-sensor.c
arch/powerpc/platforms/powernv/opal-sysparam.c
arch/powerpc/platforms/powernv/opal-wrappers.S
arch/powerpc/platforms/powernv/opal.c
arch/powerpc/platforms/powernv/pci-ioda.c
arch/powerpc/platforms/powernv/pci-p5ioc2.c
arch/powerpc/platforms/powernv/pci.c
arch/powerpc/platforms/powernv/pci.h
arch/powerpc/platforms/powernv/powernv.h
arch/powerpc/platforms/powernv/setup.c
arch/powerpc/platforms/pseries/dlpar.c
arch/powerpc/platforms/pseries/eeh_pseries.c
arch/powerpc/platforms/pseries/iommu.c
arch/powerpc/platforms/pseries/msi.c
arch/powerpc/sysdev/Makefile
arch/powerpc/sysdev/dart_iommu.c
arch/powerpc/sysdev/fsl_msi.c
arch/powerpc/sysdev/i8259.c
arch/powerpc/sysdev/ipic.c
arch/powerpc/sysdev/mpc8xx_pic.c
arch/powerpc/sysdev/mpic.c
arch/powerpc/sysdev/mpic.h
arch/powerpc/sysdev/mpic_u3msi.c
arch/powerpc/sysdev/mv64x60_pic.c
arch/powerpc/sysdev/ppc4xx_hsta_msi.c
arch/powerpc/sysdev/ppc4xx_msi.c
arch/powerpc/sysdev/qe_lib/qe_ic.c
arch/powerpc/sysdev/tsi108_pci.c
arch/powerpc/sysdev/uic.c
arch/powerpc/sysdev/xics/icp-native.c
arch/powerpc/sysdev/xics/xics-common.c
arch/powerpc/sysdev/xilinx_intc.c
arch/s390/hypfs/hypfs_sprp.c
arch/s390/include/asm/barrier.h
arch/s390/include/asm/cmpxchg.h
arch/s390/include/asm/io.h
arch/s390/include/asm/kvm_host.h
arch/s390/include/asm/pgtable.h
arch/s390/include/asm/sclp.h
arch/s390/include/asm/timex.h
arch/s390/kernel/crash_dump.c
arch/s390/kernel/debug.c
arch/s390/kernel/entry.S
arch/s390/kernel/setup.c
arch/s390/kernel/smp.c
arch/s390/kernel/suspend.c
arch/s390/kernel/time.c
arch/s390/kvm/intercept.c
arch/s390/kvm/interrupt.c
arch/s390/kvm/kvm-s390.c
arch/s390/kvm/kvm-s390.h
arch/s390/kvm/priv.c
arch/s390/mm/init.c
arch/s390/mm/mem_detect.c
arch/s390/net/bpf_jit.h
arch/s390/net/bpf_jit_comp.c
arch/s390/pci/pci_event.c
arch/score/include/asm/cmpxchg.h
arch/score/lib/string.S
arch/sh/drivers/pci/ops-sh5.c
arch/sh/drivers/pci/pci-sh5.c
arch/sh/include/asm/barrier.h
arch/sh/include/asm/cmpxchg.h
arch/sh/include/asm/pci.h
arch/sh/kernel/cpu/sh4a/clock-sh7734.c
arch/sh/kernel/cpu/sh4a/clock-sh7757.c
arch/sh/kernel/cpu/sh4a/clock-sh7785.c
arch/sh/kernel/cpu/sh4a/clock-sh7786.c
arch/sh/kernel/cpu/sh4a/clock-shx3.c
arch/sparc/crypto/md5_glue.c
arch/sparc/include/asm/barrier_64.h
arch/sparc/include/asm/cmpxchg_32.h
arch/sparc/include/asm/cmpxchg_64.h
arch/sparc/include/asm/io_32.h
arch/sparc/include/asm/io_64.h
arch/sparc/include/asm/pci_32.h
arch/sparc/include/asm/pci_64.h
arch/tile/include/asm/atomic_64.h
arch/tile/include/asm/io.h
arch/unicore32/include/asm/pci.h
arch/x86/Kbuild
arch/x86/Kconfig
arch/x86/Kconfig.debug
arch/x86/Makefile
arch/x86/boot/compressed/misc.h
arch/x86/crypto/aesni-intel_glue.c
arch/x86/crypto/camellia_aesni_avx2_glue.c
arch/x86/crypto/camellia_aesni_avx_glue.c
arch/x86/crypto/cast5_avx_glue.c
arch/x86/crypto/cast6_avx_glue.c
arch/x86/crypto/crc32-pclmul_glue.c
arch/x86/crypto/crc32c-intel_glue.c
arch/x86/crypto/crct10dif-pclmul_glue.c
arch/x86/crypto/fpu.c
arch/x86/crypto/ghash-clmulni-intel_glue.c
arch/x86/crypto/serpent_avx2_glue.c
arch/x86/crypto/serpent_avx_glue.c
arch/x86/crypto/sha-mb/sha1_mb.c
arch/x86/crypto/sha1_ssse3_glue.c
arch/x86/crypto/sha256_ssse3_glue.c
arch/x86/crypto/sha512_ssse3_glue.c
arch/x86/crypto/twofish_avx_glue.c
arch/x86/entry/Makefile [new file with mode: 0644]
arch/x86/entry/calling.h [moved from arch/x86/include/asm/calling.h with 80% similarity]
arch/x86/entry/entry_32.S [new file with mode: 0644]
arch/x86/entry/entry_64.S [moved from arch/x86/kernel/entry_64.S with 58% similarity]
arch/x86/entry/entry_64_compat.S [new file with mode: 0644]
arch/x86/entry/syscall_32.c [moved from arch/x86/kernel/syscall_32.c with 79% similarity]
arch/x86/entry/syscall_64.c [moved from arch/x86/kernel/syscall_64.c with 100% similarity]
arch/x86/entry/syscalls/Makefile [moved from arch/x86/syscalls/Makefile with 95% similarity]
arch/x86/entry/syscalls/syscall_32.tbl [moved from arch/x86/syscalls/syscall_32.tbl with 100% similarity]
arch/x86/entry/syscalls/syscall_64.tbl [moved from arch/x86/syscalls/syscall_64.tbl with 100% similarity]
arch/x86/entry/syscalls/syscallhdr.sh [moved from arch/x86/syscalls/syscallhdr.sh with 100% similarity]
arch/x86/entry/syscalls/syscalltbl.sh [moved from arch/x86/syscalls/syscalltbl.sh with 100% similarity]
arch/x86/entry/thunk_32.S [moved from arch/x86/lib/thunk_32.S with 83% similarity]
arch/x86/entry/thunk_64.S [moved from arch/x86/lib/thunk_64.S with 71% similarity]
arch/x86/entry/vdso/.gitignore [moved from arch/x86/vdso/.gitignore with 100% similarity]
arch/x86/entry/vdso/Makefile [moved from arch/x86/vdso/Makefile with 100% similarity]
arch/x86/entry/vdso/checkundef.sh [moved from arch/x86/vdso/checkundef.sh with 100% similarity]
arch/x86/entry/vdso/vclock_gettime.c [moved from arch/x86/vdso/vclock_gettime.c with 100% similarity]
arch/x86/entry/vdso/vdso-layout.lds.S [moved from arch/x86/vdso/vdso-layout.lds.S with 100% similarity]
arch/x86/entry/vdso/vdso-note.S [moved from arch/x86/vdso/vdso-note.S with 100% similarity]
arch/x86/entry/vdso/vdso.lds.S [moved from arch/x86/vdso/vdso.lds.S with 100% similarity]
arch/x86/entry/vdso/vdso2c.c [moved from arch/x86/vdso/vdso2c.c with 100% similarity]
arch/x86/entry/vdso/vdso2c.h [moved from arch/x86/vdso/vdso2c.h with 100% similarity]
arch/x86/entry/vdso/vdso32-setup.c [moved from arch/x86/vdso/vdso32-setup.c with 100% similarity]
arch/x86/entry/vdso/vdso32/.gitignore [moved from arch/x86/vdso/vdso32/.gitignore with 100% similarity]
arch/x86/entry/vdso/vdso32/int80.S [moved from arch/x86/vdso/vdso32/int80.S with 100% similarity]
arch/x86/entry/vdso/vdso32/note.S [moved from arch/x86/vdso/vdso32/note.S with 100% similarity]
arch/x86/entry/vdso/vdso32/sigreturn.S [moved from arch/x86/vdso/vdso32/sigreturn.S with 100% similarity]
arch/x86/entry/vdso/vdso32/syscall.S [moved from arch/x86/vdso/vdso32/syscall.S with 100% similarity]
arch/x86/entry/vdso/vdso32/sysenter.S [moved from arch/x86/vdso/vdso32/sysenter.S with 100% similarity]
arch/x86/entry/vdso/vdso32/vclock_gettime.c [moved from arch/x86/vdso/vdso32/vclock_gettime.c with 100% similarity]
arch/x86/entry/vdso/vdso32/vdso-fakesections.c [moved from arch/x86/vdso/vdso32/vdso-fakesections.c with 100% similarity]
arch/x86/entry/vdso/vdso32/vdso32.lds.S [moved from arch/x86/vdso/vdso32/vdso32.lds.S with 100% similarity]
arch/x86/entry/vdso/vdsox32.lds.S [moved from arch/x86/vdso/vdsox32.lds.S with 100% similarity]
arch/x86/entry/vdso/vgetcpu.c [moved from arch/x86/vdso/vgetcpu.c with 100% similarity]
arch/x86/entry/vdso/vma.c [moved from arch/x86/vdso/vma.c with 100% similarity]
arch/x86/entry/vsyscall/Makefile [new file with mode: 0644]
arch/x86/entry/vsyscall/vsyscall_64.c [moved from arch/x86/kernel/vsyscall_64.c with 100% similarity]
arch/x86/entry/vsyscall/vsyscall_emu_64.S [moved from arch/x86/kernel/vsyscall_emu_64.S with 100% similarity]
arch/x86/entry/vsyscall/vsyscall_gtod.c [moved from arch/x86/kernel/vsyscall_gtod.c with 100% similarity]
arch/x86/entry/vsyscall/vsyscall_trace.h [moved from arch/x86/kernel/vsyscall_trace.h with 89% similarity]
arch/x86/ia32/Makefile
arch/x86/ia32/ia32_signal.c
arch/x86/ia32/ia32entry.S [deleted file]
arch/x86/include/asm/alternative-asm.h
arch/x86/include/asm/alternative.h
arch/x86/include/asm/amd_nb.h
arch/x86/include/asm/apic.h
arch/x86/include/asm/asm.h
arch/x86/include/asm/atomic.h
arch/x86/include/asm/atomic64_64.h
arch/x86/include/asm/barrier.h
arch/x86/include/asm/cacheflush.h
arch/x86/include/asm/cmpxchg.h
arch/x86/include/asm/crypto/glue_helper.h
arch/x86/include/asm/dma-mapping.h
arch/x86/include/asm/dwarf2.h [deleted file]
arch/x86/include/asm/efi.h
arch/x86/include/asm/entry_arch.h
arch/x86/include/asm/fpu-internal.h [deleted file]
arch/x86/include/asm/fpu/api.h [new file with mode: 0644]
arch/x86/include/asm/fpu/internal.h [new file with mode: 0644]
arch/x86/include/asm/fpu/regset.h [new file with mode: 0644]
arch/x86/include/asm/fpu/signal.h [new file with mode: 0644]
arch/x86/include/asm/fpu/types.h [new file with mode: 0644]
arch/x86/include/asm/fpu/xstate.h [new file with mode: 0644]
arch/x86/include/asm/frame.h
arch/x86/include/asm/hardirq.h
arch/x86/include/asm/hpet.h
arch/x86/include/asm/hw_irq.h
arch/x86/include/asm/i387.h [deleted file]
arch/x86/include/asm/io.h
arch/x86/include/asm/io_apic.h
arch/x86/include/asm/irq.h
arch/x86/include/asm/irq_remapping.h
arch/x86/include/asm/irq_vectors.h
arch/x86/include/asm/irqdomain.h [new file with mode: 0644]
arch/x86/include/asm/kvm_emulate.h
arch/x86/include/asm/kvm_host.h
arch/x86/include/asm/livepatch.h
arch/x86/include/asm/mce.h
arch/x86/include/asm/microcode.h
arch/x86/include/asm/microcode_amd.h
arch/x86/include/asm/microcode_intel.h
arch/x86/include/asm/mmu_context.h
arch/x86/include/asm/mpx.h
arch/x86/include/asm/msi.h [new file with mode: 0644]
arch/x86/include/asm/msr-index.h [moved from arch/x86/include/uapi/asm/msr-index.h with 99% similarity]
arch/x86/include/asm/msr.h
arch/x86/include/asm/mtrr.h
arch/x86/include/asm/paravirt.h
arch/x86/include/asm/paravirt_types.h
arch/x86/include/asm/pat.h
arch/x86/include/asm/pci.h
arch/x86/include/asm/pgtable.h
arch/x86/include/asm/pgtable_types.h
arch/x86/include/asm/processor.h
arch/x86/include/asm/proto.h
arch/x86/include/asm/ptrace.h
arch/x86/include/asm/pvclock-abi.h
arch/x86/include/asm/pvclock.h
arch/x86/include/asm/qspinlock.h [new file with mode: 0644]
arch/x86/include/asm/qspinlock_paravirt.h [new file with mode: 0644]
arch/x86/include/asm/segment.h
arch/x86/include/asm/setup.h
arch/x86/include/asm/simd.h
arch/x86/include/asm/special_insns.h
arch/x86/include/asm/spinlock.h
arch/x86/include/asm/spinlock_types.h
arch/x86/include/asm/stackprotector.h
arch/x86/include/asm/suspend_32.h
arch/x86/include/asm/suspend_64.h
arch/x86/include/asm/thread_info.h
arch/x86/include/asm/topology.h
arch/x86/include/asm/trace/irq_vectors.h
arch/x86/include/asm/trace/mpx.h [new file with mode: 0644]
arch/x86/include/asm/traps.h
arch/x86/include/asm/uaccess_32.h
arch/x86/include/asm/user.h
arch/x86/include/asm/x86_init.h
arch/x86/include/asm/xcr.h [deleted file]
arch/x86/include/asm/xor.h
arch/x86/include/asm/xor_32.h
arch/x86/include/asm/xor_avx.h
arch/x86/include/asm/xsave.h [deleted file]
arch/x86/include/uapi/asm/kvm.h
arch/x86/include/uapi/asm/msr.h
arch/x86/include/uapi/asm/mtrr.h
arch/x86/include/uapi/asm/sigcontext.h
arch/x86/kernel/Makefile
arch/x86/kernel/acpi/boot.c
arch/x86/kernel/acpi/wakeup_32.S
arch/x86/kernel/acpi/wakeup_64.S
arch/x86/kernel/alternative.c
arch/x86/kernel/amd_nb.c
arch/x86/kernel/apb_timer.c
arch/x86/kernel/aperture_64.c
arch/x86/kernel/apic/htirq.c
arch/x86/kernel/apic/io_apic.c
arch/x86/kernel/apic/msi.c
arch/x86/kernel/apic/vector.c
arch/x86/kernel/apic/x2apic_phys.c
arch/x86/kernel/asm-offsets.c
arch/x86/kernel/asm-offsets_32.c
arch/x86/kernel/asm-offsets_64.c
arch/x86/kernel/cpu/amd.c
arch/x86/kernel/cpu/bugs.c
arch/x86/kernel/cpu/common.c
arch/x86/kernel/cpu/intel_cacheinfo.c
arch/x86/kernel/cpu/mcheck/mce.c
arch/x86/kernel/cpu/mcheck/mce_amd.c
arch/x86/kernel/cpu/mcheck/mce_intel.c
arch/x86/kernel/cpu/microcode/amd_early.c
arch/x86/kernel/cpu/microcode/core.c
arch/x86/kernel/cpu/microcode/core_early.c
arch/x86/kernel/cpu/microcode/intel.c
arch/x86/kernel/cpu/microcode/intel_early.c
arch/x86/kernel/cpu/microcode/intel_lib.c
arch/x86/kernel/cpu/mshyperv.c
arch/x86/kernel/cpu/mtrr/cleanup.c
arch/x86/kernel/cpu/mtrr/generic.c
arch/x86/kernel/cpu/mtrr/main.c
arch/x86/kernel/cpu/mtrr/mtrr.h
arch/x86/kernel/cpu/perf_event.c
arch/x86/kernel/cpu/perf_event.h
arch/x86/kernel/cpu/perf_event_intel.c
arch/x86/kernel/cpu/perf_event_intel_bts.c
arch/x86/kernel/cpu/perf_event_intel_cqm.c
arch/x86/kernel/cpu/perf_event_intel_ds.c
arch/x86/kernel/cpu/perf_event_intel_lbr.c
arch/x86/kernel/cpu/perf_event_intel_pt.c
arch/x86/kernel/cpu/perf_event_intel_rapl.c
arch/x86/kernel/cpu/perf_event_intel_uncore.c
arch/x86/kernel/cpu/perf_event_intel_uncore.h
arch/x86/kernel/cpu/perf_event_intel_uncore_snb.c
arch/x86/kernel/cpu/perf_event_intel_uncore_snbep.c
arch/x86/kernel/crash.c
arch/x86/kernel/devicetree.c
arch/x86/kernel/early-quirks.c
arch/x86/kernel/entry_32.S [deleted file]
arch/x86/kernel/fpu/Makefile [new file with mode: 0644]
arch/x86/kernel/fpu/bugs.c [new file with mode: 0644]
arch/x86/kernel/fpu/core.c [new file with mode: 0644]
arch/x86/kernel/fpu/init.c [new file with mode: 0644]
arch/x86/kernel/fpu/regset.c [new file with mode: 0644]
arch/x86/kernel/fpu/signal.c [new file with mode: 0644]
arch/x86/kernel/fpu/xstate.c [new file with mode: 0644]
arch/x86/kernel/head64.c
arch/x86/kernel/head_32.S
arch/x86/kernel/head_64.S
arch/x86/kernel/hpet.c
arch/x86/kernel/i387.c [deleted file]
arch/x86/kernel/i8259.c
arch/x86/kernel/irq.c
arch/x86/kernel/irq_32.c
arch/x86/kernel/irq_64.c
arch/x86/kernel/irq_work.c
arch/x86/kernel/irqinit.c
arch/x86/kernel/kvm.c
arch/x86/kernel/kvmclock.c
arch/x86/kernel/machine_kexec_64.c
arch/x86/kernel/mpparse.c
arch/x86/kernel/paravirt-spinlocks.c
arch/x86/kernel/paravirt.c
arch/x86/kernel/paravirt_patch_32.c
arch/x86/kernel/paravirt_patch_64.c
arch/x86/kernel/pci-dma.c
arch/x86/kernel/pci-swiotlb.c
arch/x86/kernel/process.c
arch/x86/kernel/process_32.c
arch/x86/kernel/process_64.c
arch/x86/kernel/ptrace.c
arch/x86/kernel/setup.c
arch/x86/kernel/signal.c
arch/x86/kernel/smp.c
arch/x86/kernel/smpboot.c
arch/x86/kernel/traps.c
arch/x86/kernel/uprobes.c
arch/x86/kernel/x86_init.c
arch/x86/kernel/xsave.c [deleted file]
arch/x86/kvm/Kconfig
arch/x86/kvm/Makefile
arch/x86/kvm/cpuid.c
arch/x86/kvm/cpuid.h
arch/x86/kvm/emulate.c
arch/x86/kvm/ioapic.c
arch/x86/kvm/irq_comm.c
arch/x86/kvm/kvm_cache_regs.h
arch/x86/kvm/lapic.c
arch/x86/kvm/lapic.h
arch/x86/kvm/mmu.c
arch/x86/kvm/mmu.h
arch/x86/kvm/mmu_audit.c
arch/x86/kvm/mtrr.c [new file with mode: 0644]
arch/x86/kvm/paging_tmpl.h
arch/x86/kvm/pmu.c
arch/x86/kvm/pmu.h [new file with mode: 0644]
arch/x86/kvm/pmu_amd.c [new file with mode: 0644]
arch/x86/kvm/pmu_intel.c [new file with mode: 0644]
arch/x86/kvm/svm.c
arch/x86/kvm/trace.h
arch/x86/kvm/vmx.c
arch/x86/kvm/x86.c
arch/x86/kvm/x86.h
arch/x86/lguest/boot.c
arch/x86/lib/Makefile
arch/x86/lib/atomic64_386_32.S
arch/x86/lib/atomic64_cx8_32.S
arch/x86/lib/checksum_32.S
arch/x86/lib/clear_page_64.S
arch/x86/lib/cmpxchg16b_emu.S
arch/x86/lib/cmpxchg8b_emu.S
arch/x86/lib/copy_page_64.S
arch/x86/lib/copy_user_64.S
arch/x86/lib/copy_user_nocache_64.S [deleted file]
arch/x86/lib/csum-copy_64.S
arch/x86/lib/getuser.S
arch/x86/lib/iomap_copy_64.S
arch/x86/lib/memcpy_64.S
arch/x86/lib/memmove_64.S
arch/x86/lib/memset_64.S
arch/x86/lib/mmx_32.c
arch/x86/lib/msr-reg.S
arch/x86/lib/putuser.S
arch/x86/lib/rwsem.S
arch/x86/math-emu/fpu_aux.c
arch/x86/math-emu/fpu_entry.c
arch/x86/math-emu/fpu_system.h
arch/x86/mm/init.c
arch/x86/mm/iomap_32.c
arch/x86/mm/ioremap.c
arch/x86/mm/mpx.c
arch/x86/mm/pageattr-test.c
arch/x86/mm/pageattr.c
arch/x86/mm/pat.c
arch/x86/mm/pat_internal.h
arch/x86/mm/pat_rbtree.c
arch/x86/mm/pgtable.c
arch/x86/net/bpf_jit.S
arch/x86/pci/acpi.c
arch/x86/pci/i386.c
arch/x86/pci/intel_mid_pci.c
arch/x86/pci/irq.c
arch/x86/platform/Makefile
arch/x86/platform/atom/Makefile [new file with mode: 0644]
arch/x86/platform/atom/punit_atom_debug.c [new file with mode: 0644]
arch/x86/platform/efi/efi.c
arch/x86/platform/intel-mid/device_libs/platform_wdt.c
arch/x86/platform/intel-mid/intel-mid.c
arch/x86/platform/intel-mid/sfi.c
arch/x86/platform/sfi/sfi.c
arch/x86/platform/uv/uv_irq.c
arch/x86/power/cpu.c
arch/x86/power/hibernate_asm_64.S
arch/x86/um/Makefile
arch/x86/um/asm/barrier.h
arch/x86/xen/enlighten.c
arch/x86/xen/p2m.c
arch/x86/xen/spinlock.c
arch/x86/xen/xen-asm_64.S
arch/x86/xen/xen-ops.h
arch/xtensa/Kconfig
arch/xtensa/include/asm/io.h
block/blk-mq.c
block/genhd.c
crypto/842.c
crypto/Kconfig
crypto/Makefile
crypto/ablkcipher.c
crypto/aead.c
crypto/af_alg.c
crypto/akcipher.c [new file with mode: 0644]
crypto/algapi.c
crypto/algif_aead.c
crypto/algif_rng.c
crypto/ansi_cprng.c
crypto/authenc.c
crypto/authencesn.c
crypto/blkcipher.c
crypto/ccm.c
crypto/chacha20_generic.c [new file with mode: 0644]
crypto/chacha20poly1305.c [new file with mode: 0644]
crypto/chainiv.c
crypto/cryptd.c
crypto/crypto_null.c
crypto/crypto_user.c
crypto/drbg.c
crypto/echainiv.c [new file with mode: 0644]
crypto/eseqiv.c
crypto/fips.c
crypto/gcm.c
crypto/internal.h
crypto/jitterentropy.c [new file with mode: 0644]
crypto/krng.c [deleted file]
crypto/md5.c
crypto/pcompress.c
crypto/pcrypt.c
crypto/poly1305_generic.c [new file with mode: 0644]
crypto/proc.c
crypto/rng.c
crypto/rsa.c [new file with mode: 0644]
crypto/rsa_helper.c [new file with mode: 0644]
crypto/rsakey.asn1 [new file with mode: 0644]
crypto/scatterwalk.c
crypto/seqiv.c
crypto/shash.c
crypto/tcrypt.c
crypto/tcrypt.h
crypto/testmgr.c
crypto/testmgr.h
crypto/zlib.c
drivers/acpi/Kconfig
drivers/acpi/Makefile
drivers/acpi/ac.c
drivers/acpi/acpi_lpss.c
drivers/acpi/acpi_platform.c
drivers/acpi/acpi_processor.c
drivers/acpi/acpi_video.c [moved from drivers/acpi/video.c with 88% similarity]
drivers/acpi/acpica/acdebug.h
drivers/acpi/acpica/aclocal.h
drivers/acpi/acpica/acparser.h
drivers/acpi/acpica/acpredef.h
drivers/acpi/acpica/acutils.h
drivers/acpi/acpica/dsmethod.c
drivers/acpi/acpica/hwpci.c
drivers/acpi/acpica/nsprepkg.c
drivers/acpi/acpica/nsrepair.c
drivers/acpi/acpica/psopinfo.c
drivers/acpi/acpica/utfileio.c
drivers/acpi/acpica/uthex.c
drivers/acpi/acpica/utxferror.c
drivers/acpi/apei/erst.c
drivers/acpi/apei/ghes.c
drivers/acpi/battery.c
drivers/acpi/bus.c
drivers/acpi/device_pm.c
drivers/acpi/ec.c
drivers/acpi/fan.c
drivers/acpi/glue.c
drivers/acpi/hed.c
drivers/acpi/internal.h
drivers/acpi/osl.c
drivers/acpi/pci_irq.c
drivers/acpi/power.c
drivers/acpi/processor_core.c
drivers/acpi/processor_idle.c
drivers/acpi/processor_pdc.c
drivers/acpi/property.c
drivers/acpi/resource.c
drivers/acpi/scan.c
drivers/acpi/utils.c
drivers/acpi/video_detect.c
drivers/ata/ahci_mvebu.c
drivers/ata/pata_octeon_cf.c
drivers/base/cacheinfo.c
drivers/base/init.c
drivers/base/power/Makefile
drivers/base/power/clock_ops.c
drivers/base/power/domain.c
drivers/base/power/main.c
drivers/base/power/power.h
drivers/base/power/runtime.c
drivers/base/power/wakeirq.c [new file with mode: 0644]
drivers/base/power/wakeup.c
drivers/base/property.c
drivers/base/regmap/internal.h
drivers/base/regmap/regcache.c
drivers/base/regmap/regmap-irq.c
drivers/base/regmap/regmap.c
drivers/block/Kconfig
drivers/block/cciss.c
drivers/block/cciss_scsi.c
drivers/block/nvme-core.c
drivers/block/pmem.c
drivers/block/zram/zram_drv.c
drivers/bus/mvebu-mbus.c
drivers/bus/omap_l3_noc.c
drivers/char/hw_random/via-rng.c
drivers/char/ipmi/ipmi_powernv.c
drivers/char/pcmcia/cm4040_cs.c
drivers/char/random.c
drivers/clk/at91/clk-peripheral.c
drivers/clk/at91/clk-pll.c
drivers/clk/at91/pmc.h
drivers/clk/clk-s2mps11.c
drivers/clk/clkdev.c
drivers/clk/ti/clk-7xx.c
drivers/clocksource/Kconfig
drivers/clocksource/Makefile
drivers/clocksource/armv7m_systick.c [new file with mode: 0644]
drivers/clocksource/asm9260_timer.c
drivers/clocksource/exynos_mct.c
drivers/clocksource/qcom-timer.c
drivers/clocksource/time-lpc32xx.c [new file with mode: 0644]
drivers/clocksource/timer-integrator-ap.c
drivers/clocksource/timer-stm32.c [new file with mode: 0644]
drivers/clocksource/timer-sun5i.c
drivers/cpufreq/Kconfig.arm
drivers/cpufreq/arm_big_little.c
drivers/cpufreq/cpufreq-dt.c
drivers/cpufreq/cpufreq-nforce2.c
drivers/cpufreq/cpufreq.c
drivers/cpufreq/cpufreq_conservative.c
drivers/cpufreq/cpufreq_governor.c
drivers/cpufreq/cpufreq_governor.h
drivers/cpufreq/cpufreq_ondemand.c
drivers/cpufreq/gx-suspmod.c
drivers/cpufreq/intel_pstate.c
drivers/cpufreq/pxa2xx-cpufreq.c
drivers/cpufreq/qoriq-cpufreq.c
drivers/cpuidle/cpuidle-powernv.c
drivers/cpuidle/cpuidle-pseries.c
drivers/cpuidle/cpuidle.c
drivers/cpuidle/governors/menu.c
drivers/crypto/Kconfig
drivers/crypto/Makefile
drivers/crypto/caam/Kconfig
drivers/crypto/caam/caamalg.c
drivers/crypto/caam/caamhash.c
drivers/crypto/caam/caamrng.c
drivers/crypto/caam/compat.h
drivers/crypto/caam/ctrl.c
drivers/crypto/caam/regs.h
drivers/crypto/caam/sg_sw_sec4.h
drivers/crypto/ccp/Kconfig
drivers/crypto/ccp/ccp-ops.c
drivers/crypto/ccp/ccp-platform.c
drivers/crypto/ixp4xx_crypto.c
drivers/crypto/marvell/Makefile [new file with mode: 0644]
drivers/crypto/marvell/cesa.c [new file with mode: 0644]
drivers/crypto/marvell/cesa.h [new file with mode: 0644]
drivers/crypto/marvell/cipher.c [new file with mode: 0644]
drivers/crypto/marvell/hash.c [new file with mode: 0644]
drivers/crypto/marvell/tdma.c [new file with mode: 0644]
drivers/crypto/mv_cesa.c
drivers/crypto/n2_core.c
drivers/crypto/nx/Kconfig
drivers/crypto/nx/Makefile
drivers/crypto/nx/nx-842-crypto.c [new file with mode: 0644]
drivers/crypto/nx/nx-842-platform.c [new file with mode: 0644]
drivers/crypto/nx/nx-842-powernv.c [new file with mode: 0644]
drivers/crypto/nx/nx-842-pseries.c [new file with mode: 0644]
drivers/crypto/nx/nx-842.c
drivers/crypto/nx/nx-842.h [new file with mode: 0644]
drivers/crypto/nx/nx-aes-gcm.c
drivers/crypto/nx/nx-sha256.c
drivers/crypto/nx/nx-sha512.c
drivers/crypto/nx/nx.c
drivers/crypto/nx/nx.h
drivers/crypto/omap-sham.c
drivers/crypto/padlock-aes.c
drivers/crypto/padlock-sha.c
drivers/crypto/picoxcell_crypto.c
drivers/crypto/qat/Kconfig
drivers/crypto/qat/qat_common/adf_accel_devices.h
drivers/crypto/qat/qat_common/adf_cfg_user.h
drivers/crypto/qat/qat_common/adf_common_drv.h
drivers/crypto/qat/qat_common/adf_ctl_drv.c
drivers/crypto/qat/qat_common/qat_algs.c
drivers/crypto/qat/qat_dh895xcc/adf_drv.c
drivers/crypto/sahara.c
drivers/crypto/talitos.c
drivers/crypto/talitos.h
drivers/crypto/ux500/Kconfig
drivers/crypto/vmx/Kconfig
drivers/crypto/vmx/Makefile
drivers/crypto/vmx/aes.c
drivers/crypto/vmx/aes_cbc.c
drivers/crypto/vmx/aes_ctr.c
drivers/crypto/vmx/aesp8-ppc.h
drivers/crypto/vmx/ghash.c
drivers/crypto/vmx/vmx.c
drivers/dma/at_xdmac.c
drivers/dma/dmaengine.c
drivers/dma/hsu/hsu.c
drivers/dma/mic_x100_dma.c
drivers/dma/pl330.c
drivers/firewire/sbp2.c
drivers/firmware/efi/Kconfig
drivers/firmware/efi/Makefile
drivers/firmware/efi/efi.c
drivers/firmware/efi/efivars.c
drivers/firmware/efi/esrt.c [new file with mode: 0644]
drivers/firmware/iscsi_ibft.c
drivers/gpio/Kconfig
drivers/gpio/Makefile
drivers/gpio/gpio-altera.c
drivers/gpio/gpio-bcm-kona.c
drivers/gpio/gpio-brcmstb.c [new file with mode: 0644]
drivers/gpio/gpio-crystalcove.c
drivers/gpio/gpio-dln2.c
drivers/gpio/gpio-etraxfs.c [new file with mode: 0644]
drivers/gpio/gpio-f7188x.c
drivers/gpio/gpio-generic.c
drivers/gpio/gpio-it8761e.c
drivers/gpio/gpio-lpc18xx.c [new file with mode: 0644]
drivers/gpio/gpio-lynxpoint.c
drivers/gpio/gpio-max732x.c
drivers/gpio/gpio-moxart.c
drivers/gpio/gpio-mxc.c
drivers/gpio/gpio-mxs.c
drivers/gpio/gpio-omap.c
drivers/gpio/gpio-pca953x.c
drivers/gpio/gpio-pcf857x.c
drivers/gpio/gpio-rcar.c
drivers/gpio/gpio-stp-xway.c
drivers/gpio/gpio-tb10x.c
drivers/gpio/gpio-tegra.c
drivers/gpio/gpio-ts5500.c
drivers/gpio/gpio-xgene-sb.c
drivers/gpio/gpio-xilinx.c
drivers/gpio/gpio-xlp.c [new file with mode: 0644]
drivers/gpio/gpio-zynq.c
drivers/gpio/gpiolib-acpi.c
drivers/gpio/gpiolib-of.c
drivers/gpio/gpiolib-sysfs.c
drivers/gpio/gpiolib.c
drivers/gpio/gpiolib.h
drivers/gpu/drm/amd/amdkfd/kfd_topology.c
drivers/gpu/drm/drm_ioctl.c
drivers/gpu/drm/drm_sysfs.c
drivers/gpu/drm/i915/i915_debugfs.c
drivers/gpu/drm/i915/i915_gem.c
drivers/gpu/drm/i915/intel_dp.c
drivers/gpu/drm/i915/intel_i2c.c
drivers/gpu/drm/i915/intel_lrc.c
drivers/gpu/drm/i915/intel_opregion.c
drivers/gpu/drm/i915/intel_ringbuffer.c
drivers/gpu/drm/i915/intel_sdvo.c
drivers/gpu/drm/mgag200/mgag200_mode.c
drivers/gpu/drm/radeon/atombios.h
drivers/gpu/drm/radeon/atombios_crtc.c
drivers/gpu/drm/radeon/dce3_1_afmt.c
drivers/gpu/drm/radeon/radeon_device.c
drivers/gpu/drm/radeon/radeon_dp_mst.c
drivers/gpu/drm/radeon/radeon_kms.c
drivers/gpu/drm/radeon/radeon_vm.c
drivers/gpu/ipu-v3/ipu-common.c
drivers/hid/Kconfig
drivers/hid/Makefile
drivers/hid/hid-core.c
drivers/hid/hid-cypress.c
drivers/hid/hid-ids.h
drivers/hid/hid-input.c
drivers/hid/hid-lenovo.c
drivers/hid/hid-lg.c
drivers/hid/hid-lg4ff.c
drivers/hid/hid-lg4ff.h
drivers/hid/hid-logitech-hidpp.c
drivers/hid/hid-microsoft.c
drivers/hid/hid-plantronics.c
drivers/hid/hid-prodikeys.c
drivers/hid/hid-rmi.c
drivers/hid/hid-sjoy.c
drivers/hid/hid-sony.c
drivers/hid/i2c-hid/i2c-hid.c
drivers/hid/usbhid/hid-quirks.c
drivers/hid/wacom.h
drivers/hid/wacom_sys.c
drivers/hid/wacom_wac.c
drivers/hid/wacom_wac.h
drivers/hsi/clients/cmt_speech.c
drivers/hsi/clients/nokia-modem.c
drivers/hwmon/Kconfig
drivers/hwmon/Makefile
drivers/hwmon/atxp1.c
drivers/hwmon/max197.c
drivers/hwmon/ntc_thermistor.c
drivers/hwmon/sht15.c
drivers/hwmon/tc74.c [new file with mode: 0644]
drivers/i2c/busses/Kconfig
drivers/i2c/busses/i2c-cros-ec-tunnel.c
drivers/i2c/busses/i2c-hix5hd2.c
drivers/i2c/busses/i2c-piix4.c
drivers/i2c/busses/i2c-s3c2410.c
drivers/i2c/i2c-core.c
drivers/iio/adc/twl6030-gpadc.c
drivers/iio/imu/adis16400.h
drivers/iio/imu/adis16400_buffer.c
drivers/iio/imu/adis16400_core.c
drivers/infiniband/core/addr.c
drivers/infiniband/core/agent.c
drivers/infiniband/core/agent.h
drivers/infiniband/core/cache.c
drivers/infiniband/core/cm.c
drivers/infiniband/core/cma.c
drivers/infiniband/core/device.c
drivers/infiniband/core/mad.c
drivers/infiniband/core/mad_priv.h
drivers/infiniband/core/mad_rmpp.c
drivers/infiniband/core/multicast.c
drivers/infiniband/core/opa_smi.h [new file with mode: 0644]
drivers/infiniband/core/sa_query.c
drivers/infiniband/core/smi.c
drivers/infiniband/core/sysfs.c
drivers/infiniband/core/ucm.c
drivers/infiniband/core/ucma.c
drivers/infiniband/core/user_mad.c
drivers/infiniband/core/uverbs.h
drivers/infiniband/core/uverbs_cmd.c
drivers/infiniband/core/uverbs_main.c
drivers/infiniband/core/verbs.c
drivers/infiniband/hw/amso1100/c2_provider.c
drivers/infiniband/hw/cxgb3/iwch_provider.c
drivers/infiniband/hw/cxgb4/cq.c
drivers/infiniband/hw/cxgb4/device.c
drivers/infiniband/hw/cxgb4/iw_cxgb4.h
drivers/infiniband/hw/cxgb4/provider.c
drivers/infiniband/hw/cxgb4/qp.c
drivers/infiniband/hw/cxgb4/t4.h
drivers/infiniband/hw/ehca/ehca_cq.c
drivers/infiniband/hw/ehca/ehca_hca.c
drivers/infiniband/hw/ehca/ehca_iverbs.h
drivers/infiniband/hw/ehca/ehca_main.c
drivers/infiniband/hw/ehca/ehca_sqp.c
drivers/infiniband/hw/ipath/Kconfig
drivers/infiniband/hw/ipath/ipath_cq.c
drivers/infiniband/hw/ipath/ipath_driver.c
drivers/infiniband/hw/ipath/ipath_kernel.h
drivers/infiniband/hw/ipath/ipath_mad.c
drivers/infiniband/hw/ipath/ipath_verbs.c
drivers/infiniband/hw/ipath/ipath_verbs.h
drivers/infiniband/hw/ipath/ipath_wc_x86_64.c
drivers/infiniband/hw/mlx4/cq.c
drivers/infiniband/hw/mlx4/mad.c
drivers/infiniband/hw/mlx4/main.c
drivers/infiniband/hw/mlx4/mlx4_ib.h
drivers/infiniband/hw/mlx5/cq.c
drivers/infiniband/hw/mlx5/mad.c
drivers/infiniband/hw/mlx5/main.c
drivers/infiniband/hw/mlx5/mlx5_ib.h
drivers/infiniband/hw/mthca/mthca_cmd.c
drivers/infiniband/hw/mthca/mthca_cmd.h
drivers/infiniband/hw/mthca/mthca_dev.h
drivers/infiniband/hw/mthca/mthca_mad.c
drivers/infiniband/hw/mthca/mthca_profile.c
drivers/infiniband/hw/mthca/mthca_provider.c
drivers/infiniband/hw/nes/nes_cm.c
drivers/infiniband/hw/nes/nes_cm.h
drivers/infiniband/hw/nes/nes_verbs.c
drivers/infiniband/hw/ocrdma/ocrdma_ah.c
drivers/infiniband/hw/ocrdma/ocrdma_ah.h
drivers/infiniband/hw/ocrdma/ocrdma_main.c
drivers/infiniband/hw/ocrdma/ocrdma_verbs.c
drivers/infiniband/hw/ocrdma/ocrdma_verbs.h
drivers/infiniband/hw/qib/qib_cq.c
drivers/infiniband/hw/qib/qib_iba7322.c
drivers/infiniband/hw/qib/qib_mad.c
drivers/infiniband/hw/qib/qib_verbs.c
drivers/infiniband/hw/qib/qib_verbs.h
drivers/infiniband/hw/usnic/usnic_ib_main.c
drivers/infiniband/hw/usnic/usnic_ib_verbs.c
drivers/infiniband/hw/usnic/usnic_ib_verbs.h
drivers/infiniband/hw/usnic/usnic_uiom.c
drivers/infiniband/ulp/ipoib/ipoib_main.c
drivers/infiniband/ulp/ipoib/ipoib_verbs.c
drivers/infiniband/ulp/iser/iser_verbs.c
drivers/infiniband/ulp/isert/ib_isert.c
drivers/infiniband/ulp/srp/ib_srp.c
drivers/infiniband/ulp/srp/ib_srp.h
drivers/infiniband/ulp/srpt/ib_srpt.c
drivers/infiniband/ulp/srpt/ib_srpt.h
drivers/input/keyboard/Kconfig
drivers/input/keyboard/cros_ec_keyb.c
drivers/input/mouse/alps.c
drivers/input/mouse/elantech.c
drivers/input/mouse/synaptics.c
drivers/iommu/Kconfig
drivers/iommu/Makefile
drivers/iommu/amd_iommu.c
drivers/iommu/amd_iommu_init.c
drivers/iommu/amd_iommu_proto.h
drivers/iommu/amd_iommu_types.h
drivers/iommu/arm-smmu-v3.c [new file with mode: 0644]
drivers/iommu/arm-smmu.c
drivers/iommu/dmar.c
drivers/iommu/exynos-iommu.c
drivers/iommu/intel-iommu.c
drivers/iommu/intel_irq_remapping.c
drivers/iommu/iommu.c
drivers/iommu/iova.c
drivers/iommu/irq_remapping.c
drivers/iommu/irq_remapping.h
drivers/iommu/rockchip-iommu.c
drivers/irqchip/Kconfig
drivers/irqchip/exynos-combiner.c
drivers/irqchip/irq-armada-370-xp.c
drivers/irqchip/irq-atmel-aic5.c
drivers/irqchip/irq-bcm2835.c
drivers/irqchip/irq-gic-common.c
drivers/irqchip/irq-gic-v3.c
drivers/irqchip/irq-gic.c
drivers/irqchip/irq-hip04.c
drivers/irqchip/irq-keystone.c
drivers/irqchip/irq-mips-gic.c
drivers/irqchip/irq-mtk-sysirq.c
drivers/irqchip/irq-mxs.c
drivers/irqchip/irq-nvic.c
drivers/irqchip/irq-renesas-intc-irqpin.c
drivers/irqchip/irq-renesas-irqc.c
drivers/irqchip/irq-s3c24xx.c
drivers/irqchip/irq-sun4i.c
drivers/irqchip/irq-sunxi-nmi.c
drivers/irqchip/irq-versatile-fpga.c
drivers/irqchip/irq-vf610-mscm-ir.c
drivers/irqchip/irq-vic.c
drivers/irqchip/irq-vt8500.c
drivers/irqchip/spear-shirq.c
drivers/isdn/i4l/isdn_net.c
drivers/leds/led-class.c
drivers/lguest/interrupts_and_traps.c
drivers/lguest/x86/core.c
drivers/macintosh/nvram.c [deleted file]
drivers/md/md.c
drivers/md/raid10.c
drivers/md/raid5.c
drivers/media/Kconfig
drivers/media/pci/cx25821/cx25821-medusa-reg.h
drivers/media/pci/ivtv/Kconfig
drivers/media/pci/ivtv/ivtvfb.c
drivers/message/fusion/mptbase.c
drivers/message/fusion/mptbase.h
drivers/message/fusion/mptsas.c
drivers/mfd/88pm860x-core.c
drivers/mfd/Kconfig
drivers/mfd/Makefile
drivers/mfd/ab8500-core.c
drivers/mfd/ab8500-debugfs.c
drivers/mfd/ab8500-gpadc.c
drivers/mfd/arizona-core.c
drivers/mfd/arizona-irq.c
drivers/mfd/axp20x.c
drivers/mfd/cros_ec.c
drivers/mfd/cros_ec_i2c.c
drivers/mfd/cros_ec_spi.c
drivers/mfd/da9052-irq.c
drivers/mfd/da9055-core.c
drivers/mfd/da9063-core.c
drivers/mfd/da9063-irq.c
drivers/mfd/da9150-core.c
drivers/mfd/db8500-prcmu.c
drivers/mfd/htc-i2cpld.c
drivers/mfd/intel_soc_pmic_core.h
drivers/mfd/intel_soc_pmic_crc.c
drivers/mfd/lp8788-irq.c
drivers/mfd/lpc_ich.c
drivers/mfd/max8925-core.c
drivers/mfd/max8997-irq.c
drivers/mfd/max8998-irq.c
drivers/mfd/mc13xxx-core.c
drivers/mfd/mfd-core.c
drivers/mfd/mt6397-core.c
drivers/mfd/si476x-i2c.c
drivers/mfd/stmpe.c
drivers/mfd/tc3589x.c
drivers/mfd/tps6586x.c
drivers/mfd/twl4030-irq.c
drivers/mfd/twl4030-power.c
drivers/mfd/twl6030-irq.c
drivers/mfd/ucb1x00-core.c
drivers/mfd/wm831x-auxadc.c
drivers/mfd/wm831x-irq.c
drivers/mfd/wm8350-core.c
drivers/mfd/wm8994-irq.c
drivers/misc/cxl/Kconfig
drivers/misc/cxl/Makefile
drivers/misc/cxl/api.c [new file with mode: 0644]
drivers/misc/cxl/base.c
drivers/misc/cxl/context.c
drivers/misc/cxl/cxl.h
drivers/misc/cxl/fault.c
drivers/misc/cxl/file.c
drivers/misc/cxl/irq.c
drivers/misc/cxl/main.c
drivers/misc/cxl/native.c
drivers/misc/cxl/pci.c
drivers/misc/cxl/sysfs.c
drivers/misc/cxl/vphb.c [new file with mode: 0644]
drivers/mmc/card/block.c
drivers/mmc/card/mmc_test.c
drivers/mmc/card/queue.c
drivers/mmc/card/queue.h
drivers/mmc/core/core.c
drivers/mmc/core/core.h
drivers/mmc/core/host.c
drivers/mmc/core/host.h
drivers/mmc/core/mmc.c
drivers/mmc/core/mmc_ops.c
drivers/mmc/core/mmc_ops.h
drivers/mmc/core/sd.c
drivers/mmc/core/sdio.c
drivers/mmc/core/sdio_bus.c
drivers/mmc/host/Kconfig
drivers/mmc/host/Makefile
drivers/mmc/host/davinci_mmc.c
drivers/mmc/host/dw_mmc-exynos.c
drivers/mmc/host/dw_mmc-k3.c
drivers/mmc/host/dw_mmc-rockchip.c
drivers/mmc/host/dw_mmc.c
drivers/mmc/host/dw_mmc.h
drivers/mmc/host/mtk-sd.c [new file with mode: 0644]
drivers/mmc/host/mxcmmc.c
drivers/mmc/host/mxs-mmc.c
drivers/mmc/host/rtsx_pci_sdmmc.c
drivers/mmc/host/rtsx_usb_sdmmc.c
drivers/mmc/host/s3cmci.c
drivers/mmc/host/sdhci-bcm2835.c
drivers/mmc/host/sdhci-esdhc-imx.c
drivers/mmc/host/sdhci-of-arasan.c
drivers/mmc/host/sdhci-of-esdhc.c
drivers/mmc/host/sdhci-pci-data.c
drivers/mmc/host/sdhci-pci.c
drivers/mmc/host/sdhci-pci.h
drivers/mmc/host/sdhci-pxav2.c
drivers/mmc/host/sdhci-pxav3.c
drivers/mmc/host/sdhci-s3c.c
drivers/mmc/host/sdhci-sirf.c
drivers/mmc/host/sdhci-st.c
drivers/mmc/host/sdhci.c
drivers/mmc/host/sdhci.h
drivers/mmc/host/sdhci_f_sdh30.c
drivers/mmc/host/sh_mmcif.c
drivers/mmc/host/tmio_mmc.c
drivers/mmc/host/tmio_mmc_pio.c
drivers/mtd/chips/Kconfig
drivers/mtd/chips/cfi_cmdset_0002.c
drivers/mtd/chips/cfi_util.c
drivers/mtd/devices/Kconfig
drivers/mtd/devices/Makefile
drivers/mtd/devices/docg3.c
drivers/mtd/devices/m25p80.c
drivers/mtd/devices/powernv_flash.c [new file with mode: 0644]
drivers/mtd/devices/spear_smi.c
drivers/mtd/maps/Kconfig
drivers/mtd/maps/amd76xrom.c
drivers/mtd/maps/dc21285.c
drivers/mtd/maps/esb2rom.c
drivers/mtd/maps/ichxrom.c
drivers/mtd/maps/lantiq-flash.c
drivers/mtd/maps/physmap_of.c
drivers/mtd/mtd_blkdevs.c
drivers/mtd/mtdcore.c
drivers/mtd/nand/Kconfig
drivers/mtd/nand/Makefile
drivers/mtd/nand/brcmnand/Makefile [new file with mode: 0644]
drivers/mtd/nand/brcmnand/bcm63138_nand.c [new file with mode: 0644]
drivers/mtd/nand/brcmnand/brcmnand.c [new file with mode: 0644]
drivers/mtd/nand/brcmnand/brcmnand.h [new file with mode: 0644]
drivers/mtd/nand/brcmnand/brcmstb_nand.c [new file with mode: 0644]
drivers/mtd/nand/brcmnand/iproc_nand.c [new file with mode: 0644]
drivers/mtd/nand/cs553x_nand.c
drivers/mtd/nand/diskonchip.c
drivers/mtd/nand/fsmc_nand.c
drivers/mtd/nand/mpc5121_nfc.c
drivers/mtd/nand/mxc_nand.c
drivers/mtd/nand/nand_base.c
drivers/mtd/nand/nand_bbt.c
drivers/mtd/nand/nand_ids.c
drivers/mtd/nand/nandsim.c
drivers/mtd/nand/ndfc.c
drivers/mtd/nand/plat_nand.c
drivers/mtd/nand/pxa3xx_nand.c
drivers/mtd/nand/r852.c
drivers/mtd/nand/s3c2410.c
drivers/mtd/nand/xway_nand.c
drivers/mtd/onenand/samsung.c
drivers/mtd/spi-nor/fsl-quadspi.c
drivers/mtd/spi-nor/spi-nor.c
drivers/net/ethernet/amd/xgbe/xgbe-drv.c
drivers/net/ethernet/amd/xgbe/xgbe-main.c
drivers/net/ethernet/broadcom/b44.c
drivers/net/ethernet/broadcom/genet/bcmmii.c
drivers/net/ethernet/chelsio/cxgb4/cxgb4.h
drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.h
drivers/net/ethernet/chelsio/cxgb4/sge.c
drivers/net/ethernet/chelsio/cxgb4/t4_hw.c
drivers/net/ethernet/cisco/enic/enic_ethtool.c
drivers/net/ethernet/cisco/enic/enic_main.c
drivers/net/ethernet/cisco/enic/vnic_rq.c
drivers/net/ethernet/emulex/benet/be_cmds.c
drivers/net/ethernet/emulex/benet/be_ethtool.c
drivers/net/ethernet/emulex/benet/be_main.c
drivers/net/ethernet/intel/i40e/i40e.h
drivers/net/ethernet/intel/i40e/i40e_debugfs.c
drivers/net/ethernet/intel/i40e/i40e_main.c
drivers/net/ethernet/intel/i40e/i40e_txrx.c
drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c
drivers/net/ethernet/intel/i40evf/i40e_txrx.c
drivers/net/ethernet/intel/igb/igb_ptp.c
drivers/net/ethernet/mellanox/mlx4/main.c
drivers/net/ethernet/mellanox/mlx5/core/mad.c
drivers/net/ethernet/sun/cassini.c
drivers/net/hyperv/netvsc.c
drivers/net/hyperv/rndis_filter.c
drivers/net/phy/mdio-mux-gpio.c
drivers/ntb/ntb_hw.c
drivers/of/address.c
drivers/of/base.c
drivers/of/dynamic.c
drivers/of/fdt.c
drivers/pci/Kconfig
drivers/pci/bus.c
drivers/pci/host/Kconfig
drivers/pci/host/Makefile
drivers/pci/host/pci-dra7xx.c
drivers/pci/host/pci-exynos.c
drivers/pci/host/pci-imx6.c
drivers/pci/host/pci-keystone.c
drivers/pci/host/pci-layerscape.c
drivers/pci/host/pci-mvebu.c
drivers/pci/host/pci-tegra.c
drivers/pci/host/pci-xgene-msi.c [new file with mode: 0644]
drivers/pci/host/pci-xgene.c
drivers/pci/host/pcie-designware.c
drivers/pci/host/pcie-iproc-bcma.c [new file with mode: 0644]
drivers/pci/host/pcie-iproc-platform.c
drivers/pci/host/pcie-iproc.c
drivers/pci/host/pcie-iproc.h
drivers/pci/host/pcie-spear13xx.c
drivers/pci/hotplug/Makefile
drivers/pci/hotplug/acpiphp_glue.c
drivers/pci/hotplug/pciehp.h
drivers/pci/hotplug/pciehp_acpi.c [deleted file]
drivers/pci/hotplug/pciehp_core.c
drivers/pci/hotplug/pciehp_ctrl.c
drivers/pci/hotplug/pciehp_hpc.c
drivers/pci/htirq.c
drivers/pci/msi.c
drivers/pci/pci-acpi.c
drivers/pci/pci.c
drivers/pci/pci.h
drivers/pci/pcie/aer/aerdrv_core.c
drivers/pci/pcie/aspm.c
drivers/pci/probe.c
drivers/pci/quirks.c
drivers/pci/setup-bus.c
drivers/pci/vc.c
drivers/pci/xen-pcifront.c
drivers/pcmcia/cistpl.c
drivers/pcmcia/cs.c
drivers/pcmcia/ds.c
drivers/pcmcia/electra_cf.c
drivers/pcmcia/i82365.c
drivers/pcmcia/m32r_cfc.c
drivers/pcmcia/m32r_pcc.c
drivers/pcmcia/pcmcia_cis.c
drivers/pcmcia/pcmcia_resource.c
drivers/pcmcia/rsrc_nonstatic.c
drivers/pcmcia/ti113x.h
drivers/pcmcia/topic.h
drivers/pcmcia/vrc4171_card.c
drivers/pcmcia/yenta_socket.c
drivers/phy/Kconfig
drivers/phy/phy-core.c
drivers/phy/phy-omap-usb2.c
drivers/phy/phy-rcar-gen2.c
drivers/platform/chrome/Kconfig
drivers/platform/chrome/Makefile
drivers/platform/chrome/cros_ec_dev.c
drivers/platform/chrome/cros_ec_dev.h
drivers/platform/chrome/cros_ec_lightbar.c
drivers/platform/chrome/cros_ec_lpc.c
drivers/platform/chrome/cros_ec_proto.c [new file with mode: 0644]
drivers/platform/chrome/cros_ec_sysfs.c
drivers/platform/x86/Kconfig
drivers/platform/x86/acer-wmi.c
drivers/platform/x86/apple-gmux.c
drivers/platform/x86/asus-laptop.c
drivers/platform/x86/asus-wmi.c
drivers/platform/x86/compal-laptop.c
drivers/platform/x86/dell-laptop.c
drivers/platform/x86/dell-wmi.c
drivers/platform/x86/eeepc-laptop.c
drivers/platform/x86/fujitsu-laptop.c
drivers/platform/x86/ideapad-laptop.c
drivers/platform/x86/intel_oaktrail.c
drivers/platform/x86/msi-laptop.c
drivers/platform/x86/msi-wmi.c
drivers/platform/x86/samsung-laptop.c
drivers/platform/x86/sony-laptop.c
drivers/platform/x86/thinkpad_acpi.c
drivers/platform/x86/toshiba_acpi.c
drivers/pnp/pnpacpi/rsparser.c
drivers/pnp/system.c
drivers/power/88pm860x_charger.c
drivers/power/Kconfig
drivers/power/Makefile
drivers/power/axp288_charger.c [new file with mode: 0644]
drivers/power/axp288_fuel_gauge.c
drivers/power/bq2415x_charger.c
drivers/power/bq24190_charger.c
drivers/power/bq24257_charger.c [new file with mode: 0644]
drivers/power/bq25890_charger.c [new file with mode: 0644]
drivers/power/charger-manager.c
drivers/power/max17042_battery.c
drivers/power/power_supply_core.c
drivers/power/power_supply_leds.c
drivers/power/power_supply_sysfs.c
drivers/power/reset/at91-reset.c
drivers/power/reset/gpio-poweroff.c
drivers/power/reset/gpio-restart.c
drivers/power/reset/ltc2952-poweroff.c
drivers/power/rt9455_charger.c [new file with mode: 0644]
drivers/power/sbs-battery.c
drivers/power/wm831x_power.c
drivers/powercap/intel_rapl.c
drivers/pwm/core.c
drivers/pwm/pwm-atmel.c
drivers/pwm/pwm-bcm-kona.c
drivers/pwm/pwm-lpss-pci.c
drivers/pwm/pwm-samsung.c
drivers/rapidio/rio-scan.c
drivers/regulator/88pm8607.c
drivers/regulator/Kconfig
drivers/regulator/Makefile
drivers/regulator/arizona-ldo1.c
drivers/regulator/axp20x-regulator.c
drivers/regulator/core.c
drivers/regulator/da9062-regulator.c [new file with mode: 0644]
drivers/regulator/da9063-regulator.c
drivers/regulator/fan53555.c
drivers/regulator/helpers.c
drivers/regulator/lp8755.c
drivers/regulator/max14577.c
drivers/regulator/max77686.c
drivers/regulator/max77693.c
drivers/regulator/max77843.c
drivers/regulator/max8973-regulator.c
drivers/regulator/of_regulator.c
drivers/regulator/pwm-regulator.c
drivers/regulator/qcom_spmi-regulator.c [new file with mode: 0644]
drivers/regulator/s2mps11.c
drivers/regulator/wm831x-dcdc.c
drivers/regulator/wm831x-isink.c
drivers/regulator/wm831x-ldo.c
drivers/rtc/Kconfig
drivers/rtc/Makefile
drivers/rtc/rtc-st-lpc.c [new file with mode: 0644]
drivers/s390/block/dasd.c
drivers/s390/char/keyboard.c
drivers/s390/char/sclp.c
drivers/s390/char/sclp.h
drivers/s390/char/sclp_cmd.c
drivers/s390/char/sclp_early.c
drivers/s390/char/sclp_sdias.c
drivers/s390/char/zcore.c
drivers/s390/crypto/zcrypt_pcicc.c
drivers/s390/kvm/kvm_virtio.c
drivers/s390/scsi/zfcp_scsi.c
drivers/scsi/Kconfig
drivers/scsi/Makefile
drivers/scsi/NCR53c406a.c
drivers/scsi/a100u2w.c
drivers/scsi/aacraid/src.c
drivers/scsi/advansys.c
drivers/scsi/aha152x.c
drivers/scsi/aha1542.c
drivers/scsi/aha1740.c
drivers/scsi/aha1740.h
drivers/scsi/aic94xx/aic94xx_init.c
drivers/scsi/arm/arxescsi.c
drivers/scsi/arm/cumana_2.c
drivers/scsi/arm/eesox.c
drivers/scsi/atp870u.c
drivers/scsi/atp870u.h
drivers/scsi/be2iscsi/be_cmds.c
drivers/scsi/be2iscsi/be_cmds.h
drivers/scsi/be2iscsi/be_main.c
drivers/scsi/be2iscsi/be_main.h
drivers/scsi/be2iscsi/be_mgmt.c
drivers/scsi/be2iscsi/be_mgmt.h
drivers/scsi/bnx2i/bnx2i_iscsi.c
drivers/scsi/csiostor/csio_hw.c
drivers/scsi/cxgbi/cxgb3i/cxgb3i.c
drivers/scsi/cxgbi/cxgb3i/cxgb3i.h
drivers/scsi/cxgbi/cxgb4i/cxgb4i.c
drivers/scsi/cxgbi/cxgb4i/cxgb4i.h
drivers/scsi/cxgbi/libcxgbi.c
drivers/scsi/cxgbi/libcxgbi.h
drivers/scsi/dpt_i2o.c
drivers/scsi/fdomain.c
drivers/scsi/fnic/fnic_debugfs.c
drivers/scsi/fnic/fnic_trace.c
drivers/scsi/hpsa.c
drivers/scsi/hpsa.h
drivers/scsi/hpsa_cmd.h
drivers/scsi/ibmvscsi/ibmvscsi.c
drivers/scsi/imm.c
drivers/scsi/initio.c
drivers/scsi/ipr.h
drivers/scsi/ips.c
drivers/scsi/isci/init.c
drivers/scsi/lpfc/lpfc.h
drivers/scsi/lpfc/lpfc_crtn.h
drivers/scsi/lpfc/lpfc_debugfs.c
drivers/scsi/lpfc/lpfc_disc.h
drivers/scsi/lpfc/lpfc_els.c
drivers/scsi/lpfc/lpfc_hbadisc.c
drivers/scsi/lpfc/lpfc_hw.h
drivers/scsi/lpfc/lpfc_hw4.h
drivers/scsi/lpfc/lpfc_init.c
drivers/scsi/lpfc/lpfc_mbox.c
drivers/scsi/lpfc/lpfc_nportdisc.c
drivers/scsi/lpfc/lpfc_scsi.c
drivers/scsi/lpfc/lpfc_scsi.h
drivers/scsi/lpfc/lpfc_sli.c
drivers/scsi/lpfc/lpfc_sli4.h
drivers/scsi/lpfc/lpfc_version.h
drivers/scsi/lpfc/lpfc_vport.c
drivers/scsi/mac53c94.c
drivers/scsi/megaraid/megaraid_sas.h
drivers/scsi/megaraid/megaraid_sas_base.c
drivers/scsi/megaraid/megaraid_sas_fp.c
drivers/scsi/megaraid/megaraid_sas_fusion.c
drivers/scsi/megaraid/megaraid_sas_fusion.h
drivers/scsi/mvsas/mv_init.c
drivers/scsi/nsp32.c
drivers/scsi/pcmcia/nsp_cs.c
drivers/scsi/pcmcia/qlogic_stub.c
drivers/scsi/pcmcia/sym53c500_cs.c
drivers/scsi/pm8001/pm8001_init.c
drivers/scsi/ppa.c
drivers/scsi/ps3rom.c
drivers/scsi/qla1280.c
drivers/scsi/qla2xxx/qla_init.c
drivers/scsi/qla2xxx/qla_iocb.c
drivers/scsi/qla2xxx/qla_isr.c
drivers/scsi/qla2xxx/qla_nx.c
drivers/scsi/qla2xxx/qla_nx2.c
drivers/scsi/qla2xxx/qla_os.c
drivers/scsi/qla2xxx/qla_target.c
drivers/scsi/qla4xxx/ql4_83xx.c
drivers/scsi/qla4xxx/ql4_bsg.c
drivers/scsi/qlogicfas.c
drivers/scsi/qlogicpti.c
drivers/scsi/scsi.c
drivers/scsi/scsi_common.c [new file with mode: 0644]
drivers/scsi/scsi_error.c
drivers/scsi/scsi_scan.c
drivers/scsi/scsi_transport_iscsi.c
drivers/scsi/scsi_transport_srp.c
drivers/scsi/sd.c
drivers/scsi/snic/Makefile [new file with mode: 0644]
drivers/scsi/snic/cq_desc.h [new file with mode: 0644]
drivers/scsi/snic/cq_enet_desc.h [new file with mode: 0644]
drivers/scsi/snic/snic.h [new file with mode: 0644]
drivers/scsi/snic/snic_attrs.c [new file with mode: 0644]
drivers/scsi/snic/snic_ctl.c [new file with mode: 0644]
drivers/scsi/snic/snic_debugfs.c [new file with mode: 0644]
drivers/scsi/snic/snic_disc.c [new file with mode: 0644]
drivers/scsi/snic/snic_disc.h [new file with mode: 0644]
drivers/scsi/snic/snic_fwint.h [new file with mode: 0644]
drivers/scsi/snic/snic_io.c [new file with mode: 0644]
drivers/scsi/snic/snic_io.h [new file with mode: 0644]
drivers/scsi/snic/snic_isr.c [new file with mode: 0644]
drivers/scsi/snic/snic_main.c [new file with mode: 0644]
drivers/scsi/snic/snic_res.c [new file with mode: 0644]
drivers/scsi/snic/snic_res.h [new file with mode: 0644]
drivers/scsi/snic/snic_scsi.c [new file with mode: 0644]
drivers/scsi/snic/snic_stats.h [new file with mode: 0644]
drivers/scsi/snic/snic_trc.c [new file with mode: 0644]
drivers/scsi/snic/snic_trc.h [new file with mode: 0644]
drivers/scsi/snic/vnic_cq.c [new file with mode: 0644]
drivers/scsi/snic/vnic_cq.h [new file with mode: 0644]
drivers/scsi/snic/vnic_cq_fw.h [new file with mode: 0644]
drivers/scsi/snic/vnic_dev.c [new file with mode: 0644]
drivers/scsi/snic/vnic_dev.h [new file with mode: 0644]
drivers/scsi/snic/vnic_devcmd.h [new file with mode: 0644]
drivers/scsi/snic/vnic_intr.c [new file with mode: 0644]
drivers/scsi/snic/vnic_intr.h [new file with mode: 0644]
drivers/scsi/snic/vnic_resource.h [new file with mode: 0644]
drivers/scsi/snic/vnic_snic.h [new file with mode: 0644]
drivers/scsi/snic/vnic_stats.h [new file with mode: 0644]
drivers/scsi/snic/vnic_wq.c [new file with mode: 0644]
drivers/scsi/snic/vnic_wq.h [new file with mode: 0644]
drivers/scsi/snic/wq_enet_desc.h [new file with mode: 0644]
drivers/scsi/st.c
drivers/scsi/st.h
drivers/scsi/sym53c416.c
drivers/scsi/ufs/Kconfig
drivers/scsi/ufs/ufs-qcom.c
drivers/scsi/ufs/ufshcd.c
drivers/scsi/ufs/ufshcd.h
drivers/scsi/ufs/ufshci.h
drivers/scsi/ufs/unipro.h
drivers/scsi/virtio_scsi.c
drivers/scsi/wd719x.c
drivers/scsi/wd719x.h
drivers/sh/pm_runtime.c
drivers/soc/mediatek/Kconfig
drivers/soc/mediatek/mtk-pmic-wrap.c
drivers/spi/Kconfig
drivers/spi/Makefile
drivers/spi/spi-ath79.c
drivers/spi/spi-atmel.c
drivers/spi/spi-bcm2835.c
drivers/spi/spi-davinci.c
drivers/spi/spi-fsl-dspi.c
drivers/spi/spi-fsl-espi.c
drivers/spi/spi-imx.c
drivers/spi/spi-omap2-mcspi.c
drivers/spi/spi-orion.c
drivers/spi/spi-pxa2xx-pci.c
drivers/spi/spi-pxa2xx-pxadma.c [deleted file]
drivers/spi/spi-pxa2xx.c
drivers/spi/spi-pxa2xx.h
drivers/spi/spi-rb4xx.c [new file with mode: 0644]
drivers/spi/spi-rspi.c
drivers/spi/spi-s3c64xx.c
drivers/spi/spi-sh-msiof.c
drivers/spi/spi-sirf.c
drivers/spi/spi-zynqmp-gqspi.c [new file with mode: 0644]
drivers/spi/spi.c
drivers/spi/spidev.c
drivers/ssb/driver_chipcommon_pmu.c
drivers/staging/lustre/lnet/klnds/o2iblnd/o2iblnd.c
drivers/staging/lustre/lustre/include/linux/lustre_compat25.h
drivers/staging/lustre/lustre/llite/llite_internal.h
drivers/staging/lustre/lustre/llite/symlink.c
drivers/staging/ozwpan/ozhcd.c
drivers/staging/ozwpan/ozusbif.h
drivers/staging/ozwpan/ozusbsvc1.c
drivers/staging/rtl8712/rtl8712_led.c
drivers/staging/rtl8712/rtl871x_cmd.c
drivers/staging/rtl8712/rtl871x_mlme.c
drivers/staging/rtl8712/rtl871x_pwrctrl.c
drivers/staging/rtl8712/rtl871x_sta_mgt.c
drivers/staging/rts5208/rtsx.c
drivers/target/iscsi/iscsi_target.c
drivers/target/iscsi/iscsi_target_device.c
drivers/target/iscsi/iscsi_target_tmr.c
drivers/target/sbp/sbp_target.c
drivers/target/target_core_alua.c
drivers/target/target_core_device.c
drivers/target/target_core_fabric_lib.c
drivers/target/target_core_file.c
drivers/target/target_core_iblock.c
drivers/target/target_core_pr.c
drivers/target/target_core_pscsi.c
drivers/target/target_core_pscsi.h
drivers/target/target_core_rd.c
drivers/target/target_core_sbc.c
drivers/target/target_core_spc.c
drivers/target/target_core_stat.c
drivers/target/target_core_tmr.c
drivers/target/target_core_tpg.c
drivers/target/target_core_transport.c
drivers/target/target_core_ua.c
drivers/target/target_core_user.c
drivers/target/target_core_xcopy.c
drivers/target/tcm_fc/tfc_cmd.c
drivers/target/tcm_fc/tfc_conf.c
drivers/target/tcm_fc/tfc_io.c
drivers/target/tcm_fc/tfc_sess.c
drivers/tty/hvc/hvc_opal.c
drivers/tty/n_tty.c
drivers/tty/serial/8250/8250_omap.c
drivers/tty/serial/amba-pl011.c
drivers/tty/serial/imx.c
drivers/tty/serial/serial_mctrl_gpio.c
drivers/usb/dwc3/core.h
drivers/usb/gadget/function/f_fs.c
drivers/usb/gadget/function/f_midi.c
drivers/usb/gadget/function/f_uac1.c
drivers/usb/gadget/legacy/g_ffs.c
drivers/usb/gadget/legacy/tcm_usb_gadget.c
drivers/usb/gadget/legacy/tcm_usb_gadget.h
drivers/usb/gadget/udc/s3c2410_udc.c
drivers/usb/host/xhci.c
drivers/usb/host/xhci.h
drivers/usb/image/microtek.c
drivers/usb/misc/ldusb.c
drivers/usb/musb/musb_core.c
drivers/usb/phy/phy-ab8500-usb.c
drivers/usb/phy/phy-tahvo.c
drivers/usb/renesas_usbhs/fifo.c
drivers/usb/serial/Kconfig
drivers/usb/serial/cp210x.c
drivers/usb/serial/ftdi_sio.c
drivers/usb/serial/ftdi_sio_ids.h
drivers/usb/storage/scsiglue.c
drivers/usb/storage/uas.c
drivers/vfio/Kconfig
drivers/vfio/vfio_iommu_spapr_tce.c
drivers/vfio/vfio_spapr_eeh.c
drivers/vhost/scsi.c
drivers/video/console/newport_con.c
drivers/video/fbdev/Kconfig
drivers/video/fbdev/Makefile
drivers/video/fbdev/amifb.c
drivers/video/fbdev/atafb.c
drivers/video/fbdev/atmel_lcdfb.c
drivers/video/fbdev/aty/aty128fb.c
drivers/video/fbdev/aty/radeon_base.c
drivers/video/fbdev/aty/radeonfb.h
drivers/video/fbdev/core/Makefile
drivers/video/fbdev/core/fb_defio.c
drivers/video/fbdev/core/fbmon.c
drivers/video/fbdev/gbefb.c
drivers/video/fbdev/geode/gxfb_core.c
drivers/video/fbdev/hpfb.c
drivers/video/fbdev/i810/i810.h
drivers/video/fbdev/i810/i810_main.c
drivers/video/fbdev/i810/i810_main.h
drivers/video/fbdev/imxfb.c
drivers/video/fbdev/intelfb/intelfb.h
drivers/video/fbdev/intelfb/intelfbdrv.c
drivers/video/fbdev/matrox/matroxfb_base.c
drivers/video/fbdev/matrox/matroxfb_base.h
drivers/video/fbdev/msm/Makefile [deleted file]
drivers/video/fbdev/msm/mddi.c [deleted file]
drivers/video/fbdev/msm/mddi_client_dummy.c [deleted file]
drivers/video/fbdev/msm/mddi_client_nt35399.c [deleted file]
drivers/video/fbdev/msm/mddi_client_toshiba.c [deleted file]
drivers/video/fbdev/msm/mddi_hw.h [deleted file]
drivers/video/fbdev/msm/mdp.c [deleted file]
drivers/video/fbdev/msm/mdp_csc_table.h [deleted file]
drivers/video/fbdev/msm/mdp_hw.h [deleted file]
drivers/video/fbdev/msm/mdp_ppp.c [deleted file]
drivers/video/fbdev/msm/mdp_scale_tables.c [deleted file]
drivers/video/fbdev/msm/mdp_scale_tables.h [deleted file]
drivers/video/fbdev/msm/msm_fb.c [deleted file]
drivers/video/fbdev/mxsfb.c
drivers/video/fbdev/neofb.c
drivers/video/fbdev/nvidia/nv_type.h
drivers/video/fbdev/nvidia/nvidia.c
drivers/video/fbdev/omap/Kconfig
drivers/video/fbdev/omap2/displays-new/encoder-opa362.c
drivers/video/fbdev/omap2/displays-new/panel-dpi.c
drivers/video/fbdev/omap2/displays-new/panel-lgphilips-lb035q02.c
drivers/video/fbdev/omap2/displays-new/panel-sharp-ls037v7dw01.c
drivers/video/fbdev/omap2/dss/core.c
drivers/video/fbdev/omap2/dss/dispc.c
drivers/video/fbdev/omap2/dss/display-sysfs.c
drivers/video/fbdev/omap2/dss/dpi.c
drivers/video/fbdev/omap2/dss/dsi.c
drivers/video/fbdev/omap2/dss/dss.c
drivers/video/fbdev/omap2/dss/dss.h
drivers/video/fbdev/omap2/dss/hdmi4.c
drivers/video/fbdev/omap2/dss/hdmi4_core.c
drivers/video/fbdev/omap2/dss/hdmi5.c
drivers/video/fbdev/omap2/dss/hdmi5_core.c
drivers/video/fbdev/omap2/dss/hdmi_wp.c
drivers/video/fbdev/omap2/dss/rfbi.c
drivers/video/fbdev/omap2/dss/sdi.c
drivers/video/fbdev/omap2/dss/venc.c
drivers/video/fbdev/pm2fb.c
drivers/video/fbdev/pm3fb.c
drivers/video/fbdev/riva/fbdev.c
drivers/video/fbdev/riva/rivafb.h
drivers/video/fbdev/savage/savagefb.h
drivers/video/fbdev/savage/savagefb_driver.c
drivers/video/fbdev/sis/sis.h
drivers/video/fbdev/sis/sis_main.c
drivers/video/fbdev/ssd1307fb.c
drivers/video/fbdev/tdfxfb.c
drivers/video/fbdev/vesafb.c
drivers/virtio/virtio_pci_common.c
drivers/watchdog/Kconfig
drivers/watchdog/Makefile
drivers/watchdog/st_lpc_wdt.c [new file with mode: 0644]
drivers/xen/xen-acpi-cpuhotplug.c
drivers/xen/xen-scsiback.c
fs/9p/v9fs.h
fs/9p/vfs_inode.c
fs/9p/vfs_inode_dotl.c
fs/autofs4/symlink.c
fs/befs/linuxvfs.c
fs/ceph/inode.c
fs/cifs/cifsfs.h
fs/cifs/link.c
fs/configfs/symlink.c
fs/dcache.c
fs/debugfs/file.c
fs/debugfs/inode.c
fs/ecryptfs/inode.c
fs/exofs/Kbuild
fs/exofs/exofs.h
fs/exofs/inode.c
fs/exofs/namei.c
fs/exofs/symlink.c [deleted file]
fs/ext2/inode.c
fs/ext2/namei.c
fs/ext2/symlink.c
fs/ext3/inode.c
fs/ext3/namei.c
fs/ext3/symlink.c
fs/ext4/ext4.h
fs/ext4/inode.c
fs/ext4/namei.c
fs/ext4/symlink.c
fs/f2fs/namei.c
fs/fhandle.c
fs/freevxfs/vxfs_extern.h
fs/freevxfs/vxfs_immed.c
fs/freevxfs/vxfs_inode.c
fs/fuse/dir.c
fs/gfs2/inode.c
fs/hostfs/hostfs_kern.c
fs/hppfs/hppfs.c
fs/inode.c
fs/jffs2/dir.c
fs/jffs2/fs.c
fs/jffs2/readinode.c
fs/jffs2/symlink.c
fs/jfs/inode.c
fs/jfs/namei.c
fs/jfs/symlink.c
fs/kernfs/symlink.c
fs/libfs.c
fs/logfs/dir.c
fs/mount.h
fs/namei.c
fs/namespace.c
fs/nfs/symlink.c
fs/ntfs/namei.c
fs/open.c
fs/overlayfs/inode.c
fs/proc/base.c
fs/proc/inode.c
fs/proc/namespaces.c
fs/proc/self.c
fs/proc/thread_self.c
fs/select.c
fs/sysv/Makefile
fs/sysv/inode.c
fs/sysv/symlink.c [deleted file]
fs/sysv/sysv.h
fs/ubifs/dir.c
fs/ubifs/file.c
fs/ubifs/super.c
fs/ufs/inode.c
fs/ufs/namei.c
fs/ufs/symlink.c
fs/xfs/xfs_iops.c
include/acpi/acpi_bus.h
include/acpi/acpiosxf.h
include/acpi/acpixf.h
include/acpi/actbl.h
include/acpi/actbl1.h
include/acpi/actbl2.h
include/acpi/actbl3.h
include/acpi/actypes.h
include/acpi/acuuid.h [new file with mode: 0644]
include/acpi/platform/acenv.h
include/acpi/platform/acenvex.h
include/acpi/video.h
include/asm-generic/barrier.h
include/asm-generic/cmpxchg.h
include/asm-generic/gpio.h
include/asm-generic/io.h
include/asm-generic/iomap.h
include/asm-generic/pci.h
include/asm-generic/pgtable.h
include/asm-generic/qspinlock.h [new file with mode: 0644]
include/asm-generic/qspinlock_types.h [new file with mode: 0644]
include/crypto/aead.h
include/crypto/akcipher.h [new file with mode: 0644]
include/crypto/algapi.h
include/crypto/compress.h
include/crypto/cryptd.h
include/crypto/drbg.h
include/crypto/hash.h
include/crypto/internal/aead.h
include/crypto/internal/akcipher.h [new file with mode: 0644]
include/crypto/internal/geniv.h [new file with mode: 0644]
include/crypto/internal/rng.h
include/crypto/internal/rsa.h [new file with mode: 0644]
include/crypto/md5.h
include/crypto/null.h
include/crypto/rng.h
include/crypto/scatterwalk.h
include/dt-bindings/mfd/arizona.h
include/dt-bindings/mfd/st-lpc.h [new file with mode: 0644]
include/linux/acpi.h
include/linux/alarmtimer.h
include/linux/backing-dev.h
include/linux/basic_mmio_gpio.h
include/linux/clk.h
include/linux/clkdev.h
include/linux/clockchips.h
include/linux/clocksource.h
include/linux/compiler.h
include/linux/context_tracking.h
include/linux/context_tracking_state.h
include/linux/cpufreq.h
include/linux/cpuidle.h
include/linux/crc-itu-t.h
include/linux/crypto.h
include/linux/debugfs.h
include/linux/dmar.h
include/linux/efi.h
include/linux/fs.h
include/linux/gpio.h
include/linux/gpio/consumer.h
include/linux/gpio/driver.h
include/linux/hid.h
include/linux/hrtimer.h
include/linux/htirq.h
include/linux/i2c/twl.h
include/linux/intel-iommu.h
include/linux/interrupt.h
include/linux/io.h
include/linux/iommu.h
include/linux/irq.h
include/linux/irqdesc.h
include/linux/irqdomain.h
include/linux/jiffies.h
include/linux/kvm_host.h
include/linux/kvm_types.h
include/linux/lglock.h
include/linux/libata.h
include/linux/livepatch.h
include/linux/lockdep.h
include/linux/mbus.h
include/linux/mfd/arizona/core.h
include/linux/mfd/arizona/pdata.h
include/linux/mfd/arizona/registers.h
include/linux/mfd/axp20x.h
include/linux/mfd/cros_ec.h
include/linux/mfd/cros_ec_commands.h
include/linux/mfd/da9055/core.h
include/linux/mfd/max77686.h
include/linux/mlx4/device.h
include/linux/mlx5/driver.h
include/linux/mmc/card.h
include/linux/mmc/core.h
include/linux/mmc/dw_mmc.h
include/linux/mmc/host.h
include/linux/mmc/mmc.h
include/linux/mmc/sdhci-pci-data.h
include/linux/module.h
include/linux/mpi.h
include/linux/mtd/cfi.h
include/linux/mtd/nand.h
include/linux/namei.h
include/linux/netfilter_bridge/ebtables.h
include/linux/nx842.h [deleted file]
include/linux/of.h
include/linux/of_fdt.h
include/linux/osq_lock.h
include/linux/pci.h
include/linux/pci_ids.h
include/linux/perf_event.h
include/linux/platform_data/gpio-omap.h
include/linux/platform_data/irq-renesas-irqc.h [deleted file]
include/linux/platform_data/ntc_thermistor.h
include/linux/platform_data/video-msm_fb.h [deleted file]
include/linux/pm.h
include/linux/pm_clock.h
include/linux/pm_wakeirq.h [new file with mode: 0644]
include/linux/pm_wakeup.h
include/linux/power/max17042_battery.h
include/linux/power_supply.h
include/linux/property.h
include/linux/pwm.h
include/linux/pxa2xx_ssp.h
include/linux/random.h
include/linux/rculist.h
include/linux/rcupdate.h
include/linux/rcutiny.h
include/linux/rcutree.h
include/linux/regmap.h
include/linux/regulator/driver.h
include/linux/regulator/machine.h
include/linux/regulator/max8973-regulator.h
include/linux/rio.h
include/linux/scatterlist.h
include/linux/sched.h
include/linux/sched/sysctl.h
include/linux/security.h
include/linux/seqlock.h
include/linux/spinlock.h
include/linux/sw842.h [new file with mode: 0644]
include/linux/tick.h
include/linux/time64.h
include/linux/timekeeper_internal.h
include/linux/timekeeping.h
include/linux/timer.h
include/linux/timerqueue.h
include/linux/types.h
include/misc/cxl-base.h [new file with mode: 0644]
include/misc/cxl.h
include/net/xfrm.h
include/rdma/ib_addr.h
include/rdma/ib_cache.h
include/rdma/ib_mad.h
include/rdma/ib_verbs.h
include/rdma/iw_cm.h
include/rdma/opa_smi.h [new file with mode: 0644]
include/rdma/rdma_cm.h
include/scsi/scsi.h
include/scsi/scsi_common.h [new file with mode: 0644]
include/scsi/scsi_device.h
include/scsi/scsi_eh.h
include/scsi/scsi_proto.h [new file with mode: 0644]
include/scsi/srp.h
include/sound/hda_regmap.h
include/target/iscsi/iscsi_target_core.h
include/target/target_core_base.h
include/trace/events/power.h
include/trace/events/target.h
include/trace/events/timer.h
include/trace/events/writeback.h
include/uapi/drm/radeon_drm.h
include/uapi/linux/cryptouser.h [moved from include/linux/cryptouser.h with 95% similarity]
include/uapi/linux/hsi/cs-protocol.h
include/uapi/linux/kvm.h
include/uapi/linux/netfilter_bridge/ebtables.h
include/uapi/linux/perf_event.h
include/uapi/linux/vfio.h
include/uapi/misc/cxl.h
include/uapi/rdma/ib_user_verbs.h
include/video/neomagic.h
include/video/tdfx.h
init/Kconfig
init/main.c
kernel/Kconfig.locks
kernel/compat.c
kernel/context_tracking.c
kernel/cpu.c
kernel/events/core.c
kernel/events/internal.h
kernel/events/ring_buffer.c
kernel/futex.c
kernel/irq/chip.c
kernel/irq/devres.c
kernel/irq/dummychip.c
kernel/irq/generic-chip.c
kernel/irq/internals.h
kernel/irq/irqdesc.c
kernel/irq/irqdomain.c
kernel/irq/manage.c
kernel/irq/migration.c
kernel/irq/msi.c
kernel/irq/pm.c
kernel/irq/proc.c
kernel/livepatch/core.c
kernel/locking/Makefile
kernel/locking/lglock.c
kernel/locking/lockdep.c
kernel/locking/lockdep_proc.c
kernel/locking/locktorture.c
kernel/locking/mcs_spinlock.h
kernel/locking/qrwlock.c
kernel/locking/qspinlock.c [new file with mode: 0644]
kernel/locking/qspinlock_paravirt.h [new file with mode: 0644]
kernel/locking/rtmutex.c
kernel/locking/rwsem-xadd.c
kernel/power/main.c
kernel/power/suspend.c
kernel/rcu/rcutorture.c
kernel/rcu/srcu.c
kernel/rcu/tiny.c
kernel/rcu/tiny_plugin.h
kernel/rcu/tree.c
kernel/rcu/tree.h
kernel/rcu/tree_plugin.h
kernel/rcu/tree_trace.c
kernel/rcu/update.c
kernel/sched/core.c
kernel/sched/deadline.c
kernel/sched/debug.c
kernel/sched/fair.c
kernel/sched/idle.c
kernel/sched/rt.c
kernel/sched/sched.h
kernel/sched/wait.c
kernel/stop_machine.c
kernel/sys.c
kernel/sysctl.c
kernel/time/Makefile
kernel/time/alarmtimer.c
kernel/time/clockevents.c
kernel/time/clocksource.c
kernel/time/hrtimer.c
kernel/time/ntp.c
kernel/time/ntp_internal.h
kernel/time/posix-timers.c
kernel/time/tick-broadcast-hrtimer.c
kernel/time/tick-broadcast.c
kernel/time/tick-common.c
kernel/time/tick-internal.h
kernel/time/tick-oneshot.c
kernel/time/tick-sched.c
kernel/time/tick-sched.h
kernel/time/time.c
kernel/time/timeconst.bc
kernel/time/timekeeping.c
kernel/time/timekeeping.h
kernel/time/timer.c
kernel/time/timer_list.c
kernel/time/timer_stats.c
kernel/torture.c
kernel/trace/ring_buffer_benchmark.c
kernel/trace/trace_events_filter.c
lib/842/842.h [new file with mode: 0644]
lib/842/842_compress.c [new file with mode: 0644]
lib/842/842_debugfs.h [new file with mode: 0644]
lib/842/842_decompress.c [new file with mode: 0644]
lib/842/Makefile [new file with mode: 0644]
lib/Kconfig
lib/Kconfig.debug
lib/Makefile
lib/cpumask.c
lib/crc-itu-t.c
lib/mpi/longlong.h
lib/mpi/mpicoder.c
lib/mpi/mpiutil.c
lib/raid6/Makefile
lib/raid6/x86.h
lib/rhashtable.c
lib/scatterlist.c
lib/strnlen_user.c
lib/swiotlb.c
lib/timerqueue.c
mm/backing-dev.c
mm/memcontrol.c
mm/memory_hotplug.c
mm/shmem.c
mm/zsmalloc.c
net/9p/trans_rdma.c
net/bridge/br_fdb.c
net/bridge/br_multicast.c
net/bridge/netfilter/ebtables.c
net/core/dev.c
net/core/pktgen.c
net/core/skbuff.c
net/core/sock.c
net/ipv4/esp4.c
net/ipv4/udp.c
net/ipv6/addrconf_core.c
net/ipv6/esp6.c
net/key/af_key.c
net/mac80211/aes_ccm.c
net/mac80211/aes_gcm.c
net/mac80211/aes_gmac.c
net/mac802154/llsec.c
net/mpls/af_mpls.c
net/mpls/internal.h
net/openvswitch/vport-netdev.c
net/rds/af_rds.c
net/rds/ib.h
net/rds/ib_cm.c
net/rds/ib_recv.c
net/rds/ib_send.c
net/rds/iw_cm.c
net/rds/iw_send.c
net/rds/rdma_transport.c
net/rds/rds.h
net/sched/sch_api.c
net/sctp/auth.c
net/sunrpc/xprtrdma/frwr_ops.c
net/sunrpc/xprtrdma/svc_rdma_recvfrom.c
net/sunrpc/xprtrdma/svc_rdma_transport.c
net/sunrpc/xprtrdma/verbs.c
net/tipc/socket.c
net/wireless/wext-compat.c
net/xfrm/xfrm_algo.c
net/xfrm/xfrm_user.c
scripts/checkpatch.pl
scripts/checksyscalls.sh
security/capability.c
security/security.c
security/selinux/avc.c
security/selinux/hooks.c
security/selinux/include/avc.h
sound/core/hrtimer.c
sound/drivers/pcsp/pcsp.c
sound/hda/hdac_regmap.c
sound/mips/Kconfig
sound/pci/asihpi/hpioctl.c
sound/pci/hda/hda_codec.c
sound/pci/hda/hda_intel.c
sound/pci/hda/hda_local.h
sound/pci/hda/patch_realtek.c
sound/pci/hda/patch_sigmatel.c
sound/pci/hda/patch_via.c
sound/soc/omap/omap-hdmi-audio.c
sound/soc/sh/migor.c
sound/usb/mixer.c
sound/usb/mixer_maps.c
sound/usb/quirks.c
tools/Makefile
tools/arch/alpha/include/asm/barrier.h [new file with mode: 0644]
tools/arch/arm/include/asm/barrier.h [new file with mode: 0644]
tools/arch/arm64/include/asm/barrier.h [new file with mode: 0644]
tools/arch/ia64/include/asm/barrier.h [new file with mode: 0644]
tools/arch/mips/include/asm/barrier.h [new file with mode: 0644]
tools/arch/powerpc/include/asm/barrier.h [new file with mode: 0644]
tools/arch/s390/include/asm/barrier.h [new file with mode: 0644]
tools/arch/sh/include/asm/barrier.h [new file with mode: 0644]
tools/arch/sparc/include/asm/barrier.h [new file with mode: 0644]
tools/arch/sparc/include/asm/barrier_32.h [new file with mode: 0644]
tools/arch/sparc/include/asm/barrier_64.h [new file with mode: 0644]
tools/arch/tile/include/asm/barrier.h [new file with mode: 0644]
tools/arch/x86/include/asm/atomic.h [new file with mode: 0644]
tools/arch/x86/include/asm/barrier.h [new file with mode: 0644]
tools/arch/x86/include/asm/rmwcc.h [new file with mode: 0644]
tools/arch/xtensa/include/asm/barrier.h [new file with mode: 0644]
tools/build/Makefile.build
tools/build/Makefile.feature
tools/build/tests/ex/Build
tools/build/tests/ex/empty2/README [new file with mode: 0644]
tools/include/asm-generic/atomic-gcc.h [new file with mode: 0644]
tools/include/asm-generic/barrier.h [new file with mode: 0644]
tools/include/asm/atomic.h [new file with mode: 0644]
tools/include/asm/barrier.h [new file with mode: 0644]
tools/include/linux/atomic.h [new file with mode: 0644]
tools/include/linux/compiler.h
tools/include/linux/kernel.h [moved from tools/perf/util/include/linux/kernel.h with 97% similarity]
tools/include/linux/list.h [moved from tools/perf/util/include/linux/list.h with 90% similarity]
tools/include/linux/poison.h [new file with mode: 0644]
tools/include/linux/types.h
tools/lib/traceevent/.gitignore
tools/lib/traceevent/Makefile
tools/lib/traceevent/event-parse.c
tools/lib/traceevent/event-parse.h
tools/lib/traceevent/plugin_cfg80211.c
tools/perf/.gitignore
tools/perf/Documentation/callchain-overhead-calculation.txt [new file with mode: 0644]
tools/perf/Documentation/perf-bench.txt
tools/perf/Documentation/perf-inject.txt
tools/perf/Documentation/perf-kmem.txt
tools/perf/Documentation/perf-kvm.txt
tools/perf/Documentation/perf-probe.txt
tools/perf/Documentation/perf-record.txt
tools/perf/Documentation/perf-report.txt
tools/perf/Documentation/perf-script.txt
tools/perf/Documentation/perf-top.txt
tools/perf/Documentation/perf-trace.txt
tools/perf/MANIFEST
tools/perf/Makefile.perf
tools/perf/arch/arm64/Build
tools/perf/arch/arm64/include/perf_regs.h
tools/perf/arch/arm64/tests/Build [new file with mode: 0644]
tools/perf/arch/arm64/tests/dwarf-unwind.c [new file with mode: 0644]
tools/perf/arch/arm64/tests/regs_load.S [new file with mode: 0644]
tools/perf/arch/common.c
tools/perf/arch/powerpc/util/Build
tools/perf/arch/powerpc/util/sym-handling.c [new file with mode: 0644]
tools/perf/bench/Build
tools/perf/bench/bench.h
tools/perf/bench/futex-wake-parallel.c [new file with mode: 0644]
tools/perf/bench/futex-wake.c
tools/perf/bench/numa.c
tools/perf/builtin-annotate.c
tools/perf/builtin-bench.c
tools/perf/builtin-buildid-list.c
tools/perf/builtin-diff.c
tools/perf/builtin-inject.c
tools/perf/builtin-kmem.c
tools/perf/builtin-kvm.c
tools/perf/builtin-lock.c
tools/perf/builtin-mem.c
tools/perf/builtin-probe.c
tools/perf/builtin-record.c
tools/perf/builtin-report.c
tools/perf/builtin-sched.c
tools/perf/builtin-script.c
tools/perf/builtin-stat.c
tools/perf/builtin-timechart.c
tools/perf/builtin-top.c
tools/perf/builtin-trace.c
tools/perf/config/Makefile
tools/perf/config/utilities.mak
tools/perf/perf-sys.h
tools/perf/perf.h
tools/perf/tests/Build
tools/perf/tests/builtin-test.c
tools/perf/tests/code-reading.c
tools/perf/tests/dso-data.c
tools/perf/tests/dwarf-unwind.c
tools/perf/tests/evsel-roundtrip-name.c
tools/perf/tests/hists_common.c
tools/perf/tests/hists_cumulate.c
tools/perf/tests/hists_filter.c
tools/perf/tests/hists_link.c
tools/perf/tests/hists_output.c
tools/perf/tests/keep-tracking.c
tools/perf/tests/kmod-path.c
tools/perf/tests/make
tools/perf/tests/mmap-basic.c
tools/perf/tests/mmap-thread-lookup.c
tools/perf/tests/openat-syscall-all-cpus.c [moved from tools/perf/tests/open-syscall-all-cpus.c with 90% similarity]
tools/perf/tests/openat-syscall-tp-fields.c [moved from tools/perf/tests/open-syscall-tp-fields.c with 94% similarity]
tools/perf/tests/openat-syscall.c [moved from tools/perf/tests/open-syscall.c with 79% similarity]
tools/perf/tests/parse-events.c
tools/perf/tests/perf-time-to-tsc.c
tools/perf/tests/pmu.c
tools/perf/tests/switch-tracking.c
tools/perf/tests/tests.h
tools/perf/tests/thread-mg-share.c
tools/perf/tests/vmlinux-kallsyms.c
tools/perf/ui/browsers/annotate.c
tools/perf/ui/browsers/hists.c
tools/perf/ui/tui/setup.c
tools/perf/util/Build
tools/perf/util/annotate.c
tools/perf/util/annotate.h
tools/perf/util/auxtrace.c [new file with mode: 0644]
tools/perf/util/auxtrace.h [new file with mode: 0644]
tools/perf/util/build-id.c
tools/perf/util/cache.h
tools/perf/util/callchain.h
tools/perf/util/cgroup.c
tools/perf/util/cgroup.h
tools/perf/util/comm.c
tools/perf/util/data-convert-bt.c
tools/perf/util/db-export.c
tools/perf/util/dso.c
tools/perf/util/dso.h
tools/perf/util/dwarf-aux.c
tools/perf/util/dwarf-aux.h
tools/perf/util/environment.c
tools/perf/util/event.c
tools/perf/util/event.h
tools/perf/util/evlist.c
tools/perf/util/evlist.h
tools/perf/util/evsel.c
tools/perf/util/evsel.h
tools/perf/util/header.c
tools/perf/util/header.h
tools/perf/util/hist.c
tools/perf/util/hist.h
tools/perf/util/include/linux/poison.h [deleted file]
tools/perf/util/include/linux/rbtree.h
tools/perf/util/machine.c
tools/perf/util/machine.h
tools/perf/util/map.c
tools/perf/util/map.h
tools/perf/util/pager.c
tools/perf/util/parse-branch-options.c [new file with mode: 0644]
tools/perf/util/parse-branch-options.h [new file with mode: 0644]
tools/perf/util/parse-events.c
tools/perf/util/parse-events.h
tools/perf/util/parse-events.l
tools/perf/util/parse-events.y
tools/perf/util/parse-options.h
tools/perf/util/pmu.c
tools/perf/util/pmu.h
tools/perf/util/probe-event.c
tools/perf/util/probe-event.h
tools/perf/util/probe-finder.c
tools/perf/util/probe-finder.h
tools/perf/util/pstack.c
tools/perf/util/pstack.h
tools/perf/util/python-ext-sources
tools/perf/util/record.c
tools/perf/util/session.c
tools/perf/util/session.h
tools/perf/util/sort.c
tools/perf/util/sort.h
tools/perf/util/stat-shadow.c [new file with mode: 0644]
tools/perf/util/stat.c
tools/perf/util/stat.h
tools/perf/util/strfilter.c
tools/perf/util/strfilter.h
tools/perf/util/symbol-elf.c
tools/perf/util/symbol.c
tools/perf/util/symbol.h
tools/perf/util/thread-stack.c
tools/perf/util/thread-stack.h
tools/perf/util/thread.c
tools/perf/util/thread.h
tools/perf/util/thread_map.c
tools/perf/util/tool.h
tools/perf/util/trace-event-parse.c
tools/perf/util/unwind-libunwind.c
tools/perf/util/util.c
tools/perf/util/util.h
tools/perf/util/vdso.c
tools/perf/util/vdso.h
tools/perf/util/xyarray.c
tools/perf/util/xyarray.h
tools/power/cpupower/utils/idle_monitor/mperf_monitor.c
tools/power/x86/turbostat/Makefile
tools/testing/selftests/powerpc/Makefile
tools/testing/selftests/powerpc/dscr/.gitignore [new file with mode: 0644]
tools/testing/selftests/powerpc/dscr/Makefile [new file with mode: 0644]
tools/testing/selftests/powerpc/dscr/dscr.h [new file with mode: 0644]
tools/testing/selftests/powerpc/dscr/dscr_default_test.c [new file with mode: 0644]
tools/testing/selftests/powerpc/dscr/dscr_explicit_test.c [new file with mode: 0644]
tools/testing/selftests/powerpc/dscr/dscr_inherit_exec_test.c [new file with mode: 0644]
tools/testing/selftests/powerpc/dscr/dscr_inherit_test.c [new file with mode: 0644]
tools/testing/selftests/powerpc/dscr/dscr_sysfs_test.c [new file with mode: 0644]
tools/testing/selftests/powerpc/dscr/dscr_sysfs_thread_test.c [new file with mode: 0644]
tools/testing/selftests/powerpc/dscr/dscr_user_test.c [new file with mode: 0644]
tools/testing/selftests/powerpc/switch_endian/Makefile
tools/testing/selftests/powerpc/tm/Makefile
tools/testing/selftests/powerpc/tm/tm-syscall.c
tools/testing/selftests/powerpc/vphn/Makefile
tools/testing/selftests/rcutorture/bin/configinit.sh
tools/testing/selftests/rcutorture/bin/kvm-recheck.sh
tools/testing/selftests/rcutorture/bin/kvm.sh
tools/testing/selftests/rcutorture/configs/rcu/CFcommon
tools/testing/selftests/rcutorture/configs/rcu/SRCU-N
tools/testing/selftests/rcutorture/configs/rcu/SRCU-P
tools/testing/selftests/rcutorture/configs/rcu/SRCU-P.boot
tools/testing/selftests/rcutorture/configs/rcu/TASKS01
tools/testing/selftests/rcutorture/configs/rcu/TASKS02
tools/testing/selftests/rcutorture/configs/rcu/TASKS03
tools/testing/selftests/rcutorture/configs/rcu/TINY02
tools/testing/selftests/rcutorture/configs/rcu/TINY02.boot
tools/testing/selftests/rcutorture/configs/rcu/TREE01
tools/testing/selftests/rcutorture/configs/rcu/TREE02
tools/testing/selftests/rcutorture/configs/rcu/TREE02-T
tools/testing/selftests/rcutorture/configs/rcu/TREE03
tools/testing/selftests/rcutorture/configs/rcu/TREE03.boot [new file with mode: 0644]
tools/testing/selftests/rcutorture/configs/rcu/TREE04
tools/testing/selftests/rcutorture/configs/rcu/TREE05
tools/testing/selftests/rcutorture/configs/rcu/TREE06
tools/testing/selftests/rcutorture/configs/rcu/TREE06.boot
tools/testing/selftests/rcutorture/configs/rcu/TREE07
tools/testing/selftests/rcutorture/configs/rcu/TREE08
tools/testing/selftests/rcutorture/configs/rcu/TREE08-T
tools/testing/selftests/rcutorture/configs/rcu/TREE08-T.boot [new file with mode: 0644]
tools/testing/selftests/rcutorture/configs/rcu/TREE08.boot
tools/testing/selftests/rcutorture/configs/rcu/TREE09
tools/testing/selftests/rcutorture/doc/TREE_RCU-kconfig.txt
tools/testing/selftests/timers/leap-a-day.c
tools/testing/selftests/x86/Makefile
tools/testing/selftests/x86/entry_from_vm86.c [new file with mode: 0644]
tools/testing/selftests/x86/sysret_ss_attrs.c [new file with mode: 0644]
tools/testing/selftests/x86/thunks.S [new file with mode: 0644]
virt/kvm/arm/vgic-v3-emul.c
virt/kvm/arm/vgic.c
virt/kvm/async_pf.h
virt/kvm/coalesced_mmio.h
virt/kvm/irqchip.c
virt/kvm/kvm_main.c

index d46bba801aace45ed29ea559a7a1c9c1fe764fd5..acfe9df83139a935bca0bde7c3701ff24b147216 100644 (file)
@@ -6,6 +6,17 @@ Example: The real path of the attribute /sys/class/cxl/afu0.0s/irqs_max is
 
 Slave contexts (eg. /sys/class/cxl/afu0.0s):
 
+What:           /sys/class/cxl/<afu>/afu_err_buf
+Date:           September 2014
+Contact:        linuxppc-dev@lists.ozlabs.org
+Description:    read only
+                AFU Error Buffer contents. The contents of this file are
+               application specific and depends on the AFU being used.
+               Applications interacting with the AFU can use this attribute
+               to know about the current error condition and take appropriate
+               action like logging the event etc.
+
+
 What:           /sys/class/cxl/<afu>/irqs_max
 Date:           September 2014
 Contact:        linuxppc-dev@lists.ozlabs.org
@@ -15,6 +26,7 @@ Description:    read/write
                 that hardware can support (eg. 2037). Write values will limit
                 userspace applications to that many userspace interrupts. Must
                 be >= irqs_min.
+Users:         https://github.com/ibm-capi/libcxl
 
 What:           /sys/class/cxl/<afu>/irqs_min
 Date:           September 2014
@@ -24,6 +36,7 @@ Description:    read only
                 userspace must request on a CXL_START_WORK ioctl. Userspace may
                 omit the num_interrupts field in the START_WORK IOCTL to get
                 this minimum automatically.
+Users:         https://github.com/ibm-capi/libcxl
 
 What:           /sys/class/cxl/<afu>/mmio_size
 Date:           September 2014
@@ -31,6 +44,7 @@ Contact:        linuxppc-dev@lists.ozlabs.org
 Description:    read only
                 Decimal value of the size of the MMIO space that may be mmaped
                 by userspace.
+Users:         https://github.com/ibm-capi/libcxl
 
 What:           /sys/class/cxl/<afu>/modes_supported
 Date:           September 2014
@@ -38,6 +52,7 @@ Contact:        linuxppc-dev@lists.ozlabs.org
 Description:    read only
                 List of the modes this AFU supports. One per line.
                 Valid entries are: "dedicated_process" and "afu_directed"
+Users:         https://github.com/ibm-capi/libcxl
 
 What:           /sys/class/cxl/<afu>/mode
 Date:           September 2014
@@ -46,6 +61,7 @@ Description:    read/write
                 The current mode the AFU is using. Will be one of the modes
                 given in modes_supported. Writing will change the mode
                 provided that no user contexts are attached.
+Users:         https://github.com/ibm-capi/libcxl
 
 
 What:           /sys/class/cxl/<afu>/prefault_mode
@@ -59,6 +75,7 @@ Description:    read/write
                                  descriptor as an effective address and
                                  prefault what it points to.
                         all: all segments process calling START_WORK maps.
+Users:         https://github.com/ibm-capi/libcxl
 
 What:           /sys/class/cxl/<afu>/reset
 Date:           September 2014
@@ -66,12 +83,14 @@ Contact:        linuxppc-dev@lists.ozlabs.org
 Description:    write only
                 Writing 1 here will reset the AFU provided there are not
                 contexts active on the AFU.
+Users:         https://github.com/ibm-capi/libcxl
 
 What:           /sys/class/cxl/<afu>/api_version
 Date:           September 2014
 Contact:        linuxppc-dev@lists.ozlabs.org
 Description:    read only
                 Decimal value of the current version of the kernel/user API.
+Users:         https://github.com/ibm-capi/libcxl
 
 What:           /sys/class/cxl/<afu>/api_version_compatible
 Date:           September 2014
@@ -79,6 +98,7 @@ Contact:        linuxppc-dev@lists.ozlabs.org
 Description:    read only
                 Decimal value of the the lowest version of the userspace API
                 this this kernel supports.
+Users:         https://github.com/ibm-capi/libcxl
 
 
 AFU configuration records (eg. /sys/class/cxl/afu0.0/cr0):
@@ -92,6 +112,7 @@ Contact:        linuxppc-dev@lists.ozlabs.org
 Description:    read only
                Hexadecimal value of the vendor ID found in this AFU
                configuration record.
+Users:         https://github.com/ibm-capi/libcxl
 
 What:           /sys/class/cxl/<afu>/cr<config num>/device
 Date:           February 2015
@@ -99,6 +120,7 @@ Contact:        linuxppc-dev@lists.ozlabs.org
 Description:    read only
                Hexadecimal value of the device ID found in this AFU
                configuration record.
+Users:         https://github.com/ibm-capi/libcxl
 
 What:           /sys/class/cxl/<afu>/cr<config num>/class
 Date:           February 2015
@@ -106,6 +128,7 @@ Contact:        linuxppc-dev@lists.ozlabs.org
 Description:    read only
                Hexadecimal value of the class code found in this AFU
                configuration record.
+Users:         https://github.com/ibm-capi/libcxl
 
 What:           /sys/class/cxl/<afu>/cr<config num>/config
 Date:           February 2015
@@ -115,6 +138,7 @@ Description:    read only
                record. The format is expected to match the either the standard
                or extended configuration space defined by the PCIe
                specification.
+Users:         https://github.com/ibm-capi/libcxl
 
 
 
@@ -126,18 +150,21 @@ Contact:        linuxppc-dev@lists.ozlabs.org
 Description:    read only
                 Decimal value of the size of the MMIO space that may be mmaped
                 by userspace. This includes all slave contexts space also.
+Users:         https://github.com/ibm-capi/libcxl
 
 What:           /sys/class/cxl/<afu>m/pp_mmio_len
 Date:           September 2014
 Contact:        linuxppc-dev@lists.ozlabs.org
 Description:    read only
                 Decimal value of the Per Process MMIO space length.
+Users:         https://github.com/ibm-capi/libcxl
 
 What:           /sys/class/cxl/<afu>m/pp_mmio_off
 Date:           September 2014
 Contact:        linuxppc-dev@lists.ozlabs.org
 Description:    read only
                 Decimal value of the Per Process MMIO space offset.
+Users:         https://github.com/ibm-capi/libcxl
 
 
 Card info (eg. /sys/class/cxl/card0)
@@ -147,12 +174,14 @@ Date:           September 2014
 Contact:        linuxppc-dev@lists.ozlabs.org
 Description:    read only
                 Identifies the CAIA Version the card implements.
+Users:         https://github.com/ibm-capi/libcxl
 
 What:           /sys/class/cxl/<card>/psl_revision
 Date:           September 2014
 Contact:        linuxppc-dev@lists.ozlabs.org
 Description:    read only
                 Identifies the revision level of the PSL.
+Users:         https://github.com/ibm-capi/libcxl
 
 What:           /sys/class/cxl/<card>/base_image
 Date:           September 2014
@@ -162,6 +191,7 @@ Description:    read only
                 that support loadable PSLs. For FPGAs this field identifies
                 the image contained in the on-adapter flash which is loaded
                 during the initial program load.
+Users:         https://github.com/ibm-capi/libcxl
 
 What:           /sys/class/cxl/<card>/image_loaded
 Date:           September 2014
@@ -169,6 +199,7 @@ Contact:        linuxppc-dev@lists.ozlabs.org
 Description:    read only
                 Will return "user" or "factory" depending on the image loaded
                 onto the card.
+Users:         https://github.com/ibm-capi/libcxl
 
 What:           /sys/class/cxl/<card>/load_image_on_perst
 Date:           December 2014
@@ -183,6 +214,7 @@ Description:    read/write
                 user or factory image to be loaded.
                 Default is to reload on PERST whichever image the card has
                 loaded.
+Users:         https://github.com/ibm-capi/libcxl
 
 What:           /sys/class/cxl/<card>/reset
 Date:           October 2014
@@ -190,3 +222,4 @@ Contact:        linuxppc-dev@lists.ozlabs.org
 Description:    write only
                 Writing 1 will issue a PERST to card which may cause the card
                 to reload the FPGA depending on load_image_on_perst.
+Users:         https://github.com/ibm-capi/libcxl
diff --git a/Documentation/ABI/testing/sysfs-class-scsi_tape b/Documentation/ABI/testing/sysfs-class-scsi_tape
new file mode 100644 (file)
index 0000000..9be398b
--- /dev/null
@@ -0,0 +1,109 @@
+What:           /sys/class/scsi_tape/*/stats/in_flight
+Date:           Apr 2015
+KernelVersion:  4.2
+Contact:        Shane Seymour <shane.seymour@hp.com>
+Description:
+               Show the number of I/Os currently in-flight between the st
+               module and the SCSI mid-layer.
+Users:
+
+
+What:           /sys/class/scsi_tape/*/stats/io_ns
+Date:           Apr 2015
+KernelVersion:  4.2
+Contact:        Shane Seymour <shane.seymour@hp.com>
+Description:
+               Shows the total amount of time spent waiting for all I/O
+                to and from the tape drive to complete. This includes all
+                reads, writes, and other SCSI commands issued to the tape
+                drive. An example of other SCSI commands would be tape
+                movement such as a rewind when a rewind tape device is
+                closed. This item is measured in nanoseconds.
+
+                To determine the amount of time spent waiting for other I/O
+                to complete subtract read_ns and write_ns from this value.
+Users:
+
+
+What:           /sys/class/scsi_tape/*/stats/other_cnt
+Date:           Apr 2015
+KernelVersion:  4.2
+Contact:        Shane Seymour <shane.seymour@hp.com>
+Description:
+               The number of I/O requests issued to the tape drive other
+               than SCSI read/write requests.
+Users:
+
+
+What:           /sys/class/scsi_tape/*/stats/read_byte_cnt
+Date:           Apr 2015
+KernelVersion:  4.2
+Contact:        Shane Seymour <shane.seymour@hp.com>
+Description:
+               Shows the total number of bytes requested from the tape drive.
+               This value is presented in bytes because tape drives support
+               variable length block sizes.
+Users:
+
+
+What:           /sys/class/scsi_tape/*/stats/read_cnt
+Date:           Apr 2015
+KernelVersion:  4.2
+Contact:        Shane Seymour <shane.seymour@hp.com>
+Description:
+               Shows the total number of read requests issued to the tape
+               drive.
+Users:
+
+
+What:           /sys/class/scsi_tape/*/stats/read_ns
+Date:           Apr 2015
+KernelVersion:  4.2
+Contact:        Shane Seymour <shane.seymour@hp.com>
+Description:
+               Shows the total amount of time in nanoseconds waiting for
+               read I/O requests to complete.
+Users:
+
+
+What:           /sys/class/scsi_tape/*/stats/write_byte_cnt
+Date:           Apr 2015
+KernelVersion:  4.2
+Contact:        Shane Seymour <shane.seymour@hp.com>
+Description:
+               Shows the total number of bytes written to the tape drive.
+               This value is presented in bytes because tape drives support
+               variable length block sizes.
+Users:
+
+
+What:           /sys/class/scsi_tape/*/stats/write_cnt
+Date:           Apr 2015
+KernelVersion:  4.2
+Contact:        Shane Seymour <shane.seymour@hp.com>
+Description:
+               Shows the total number of write requests issued to the tape
+               drive.
+Users:
+
+
+What:           /sys/class/scsi_tape/*/stats/write_ms
+Date:           Apr 2015
+KernelVersion:  4.2
+Contact:        Shane Seymour <shane.seymour@hp.com>
+Description:
+               Shows the total amount of time in nanoseconds waiting for
+               write I/O requests to complete.
+Users:
+
+
+What:           /sys/class/scsi_tape/*/stats/resid_cnt
+Date:           Apr 2015
+KernelVersion:  4.2
+Contact:        Shane Seymour <shane.seymour@hp.com>
+Description:
+               Shows the number of times we found that a residual >0
+               was found when the SCSI midlayer indicated that there was
+               an error. For reads this may be a case of someone issuing
+               reads greater than the block size.
+Users:
index b3f6a2ac5007896b82ee09d4e96e9392d8fb6888..db197a8795809b9bc6f2e882b7519b12affbeab3 100644 (file)
@@ -1,7 +1,7 @@
-What:          /sys/module/hid_logitech/drivers/hid:logitech/<dev>/range.
+What:          /sys/bus/hid/drivers/logitech/<dev>/range
 Date:          July 2011
 KernelVersion: 3.2
-Contact:       Michal Malý <madcatxster@gmail.com>
+Contact:       Michal Malý <madcatxster@devoid-pointer.net>
 Description:   Display minimum, maximum and current range of the steering
                wheel. Writing a value within min and max boundaries sets the
                range of the wheel.
@@ -9,7 +9,7 @@ Description:    Display minimum, maximum and current range of the steering
 What:          /sys/bus/hid/drivers/logitech/<dev>/alternate_modes
 Date:          Feb 2015
 KernelVersion: 4.1
-Contact:       Michal Malý <madcatxster@gmail.com>
+Contact:       Michal Malý <madcatxster@devoid-pointer.net>
 Description:   Displays a set of alternate modes supported by a wheel. Each
                mode is listed as follows:
                  Tag: Mode Name
@@ -45,7 +45,7 @@ Description:  Displays a set of alternate modes supported by a wheel. Each
 What:          /sys/bus/hid/drivers/logitech/<dev>/real_id
 Date:          Feb 2015
 KernelVersion: 4.1
-Contact:       Michal Malý <madcatxster@gmail.com>
+Contact:       Michal Malý <madcatxster@devoid-pointer.net>
 Description:   Displays the real model of the wheel regardless of any
                alternate mode the wheel might be switched to.
                It is a read-only value.
index 05874da7ce80302d92e15781deb90c2f1522c9e4..e794eac32a90a51c4d01577affe71466cde99a1f 100644 (file)
@@ -18,3 +18,13 @@ Contact:     Dave Young <dyoung@redhat.com>
 Description:   It shows the physical address of config table entry in the EFI
                system table.
 Users:         Kexec
+
+What:          /sys/firmware/efi/systab
+Date:          April 2005
+Contact:       linux-efi@vger.kernel.org
+Description:   Displays the physical addresses of all EFI Configuration
+               Tables found via the EFI System Table. The order in
+               which the tables are printed forms an ABI and newer
+               versions are always printed first, i.e. ACPI20 comes
+               before ACPI.
+Users:         dmidecode
diff --git a/Documentation/ABI/testing/sysfs-firmware-efi-esrt b/Documentation/ABI/testing/sysfs-firmware-efi-esrt
new file mode 100644 (file)
index 0000000..6e431d1
--- /dev/null
@@ -0,0 +1,81 @@
+What:          /sys/firmware/efi/esrt/
+Date:          February 2015
+Contact:       Peter Jones <pjones@redhat.com>
+Description:   Provides userland access to read the EFI System Resource Table
+               (ESRT), a catalog of firmware for which can be updated with
+               the UEFI UpdateCapsule mechanism described in section 7.5 of
+               the UEFI Standard.
+Users:         fwupdate - https://github.com/rhinstaller/fwupdate
+
+What:          /sys/firmware/efi/esrt/fw_resource_count
+Date:          February 2015
+Contact:       Peter Jones <pjones@redhat.com>
+Description:   The number of entries in the ESRT
+
+What:          /sys/firmware/efi/esrt/fw_resource_count_max
+Date:          February 2015
+Contact:       Peter Jones <pjones@redhat.com>
+Description:   The maximum number of entries that /could/ be registered
+               in the allocation the table is currently in.  This is
+               really only useful to the system firmware itself.
+
+What:          /sys/firmware/efi/esrt/fw_resource_version
+Date:          February 2015
+Contact:       Peter Jones <pjones@redhat.com>
+Description:   The version of the ESRT structure provided by the firmware.
+
+What:          /sys/firmware/efi/esrt/entries/entry$N/
+Date:          February 2015
+Contact:       Peter Jones <pjones@redhat.com>
+Description:   Each ESRT entry is identified by a GUID, and each gets a
+               subdirectory under entries/ .
+               example: /sys/firmware/efi/esrt/entries/entry0/
+
+What:          /sys/firmware/efi/esrt/entries/entry$N/fw_type
+Date:          February 2015
+Contact:       Peter Jones <pjones@redhat.com>
+Description:   What kind of firmware entry this is:
+               0 - Unknown
+               1 - System Firmware
+               2 - Device Firmware
+               3 - UEFI Driver
+
+What:          /sys/firmware/efi/esrt/entries/entry$N/fw_class
+Date:          February 2015
+Contact:       Peter Jones <pjones@redhat.com>
+Description:   This is the entry's guid, and will match the directory name.
+
+What:          /sys/firmware/efi/esrt/entries/entry$N/fw_version
+Date:          February 2015
+Contact:       Peter Jones <pjones@redhat.com>
+Description:   The version of the firmware currently installed.  This is a
+               32-bit unsigned integer.
+
+What:          /sys/firmware/efi/esrt/entries/entry$N/lowest_supported_fw_version
+Date:          February 2015
+Contact:       Peter Jones <pjones@redhat.com>
+Description:   The lowest version of the firmware that can be installed.
+
+What:          /sys/firmware/efi/esrt/entries/entry$N/capsule_flags
+Date:          February 2015
+Contact:       Peter Jones <pjones@redhat.com>
+Description:   Flags that must be passed to UpdateCapsule()
+
+What:          /sys/firmware/efi/esrt/entries/entry$N/last_attempt_version
+Date:          February 2015
+Contact:       Peter Jones <pjones@redhat.com>
+Description:   The last firmware version for which an update was attempted.
+
+What:          /sys/firmware/efi/esrt/entries/entry$N/last_attempt_status
+Date:          February 2015
+Contact:       Peter Jones <pjones@redhat.com>
+Description:   The result of the last firmware update attempt for the
+               firmware resource entry.
+               0 - Success
+               1 - Insufficient resources
+               2 - Incorrect version
+               3 - Invalid format
+               4 - Authentication error
+               5 - AC power event
+               6 - Battery power event
+
index 0f7afb2bb442e07f9eb5304e7e8fbc34e80866c6..aef8cc5a677bdb5f57b02277aed65fda4b4c15e8 100644 (file)
@@ -25,13 +25,18 @@ physical addresses.  These are the addresses in /proc/iomem.  The physical
 address is not directly useful to a driver; it must use ioremap() to map
 the space and produce a virtual address.
 
-I/O devices use a third kind of address: a "bus address" or "DMA address".
-If a device has registers at an MMIO address, or if it performs DMA to read
-or write system memory, the addresses used by the device are bus addresses.
-In some systems, bus addresses are identical to CPU physical addresses, but
-in general they are not.  IOMMUs and host bridges can produce arbitrary
+I/O devices use a third kind of address: a "bus address".  If a device has
+registers at an MMIO address, or if it performs DMA to read or write system
+memory, the addresses used by the device are bus addresses.  In some
+systems, bus addresses are identical to CPU physical addresses, but in
+general they are not.  IOMMUs and host bridges can produce arbitrary
 mappings between physical and bus addresses.
 
+From a device's point of view, DMA uses the bus address space, but it may
+be restricted to a subset of that space.  For example, even if a system
+supports 64-bit addresses for main memory and PCI BARs, it may use an IOMMU
+so devices only need to use 32-bit DMA addresses.
+
 Here's a picture and some examples:
 
                CPU                  CPU                  Bus
@@ -72,11 +77,11 @@ can use virtual address X to access the buffer, but the device itself
 cannot because DMA doesn't go through the CPU virtual memory system.
 
 In some simple systems, the device can do DMA directly to physical address
-Y.  But in many others, there is IOMMU hardware that translates bus
+Y.  But in many others, there is IOMMU hardware that translates DMA
 addresses to physical addresses, e.g., it translates Z to Y.  This is part
 of the reason for the DMA API: the driver can give a virtual address X to
 an interface like dma_map_single(), which sets up any required IOMMU
-mapping and returns the bus address Z.  The driver then tells the device to
+mapping and returns the DMA address Z.  The driver then tells the device to
 do DMA to Z, and the IOMMU maps it to the buffer at address Y in system
 RAM.
 
@@ -98,7 +103,7 @@ First of all, you should make sure
 #include <linux/dma-mapping.h>
 
 is in your driver, which provides the definition of dma_addr_t.  This type
-can hold any valid DMA or bus address for the platform and should be used
+can hold any valid DMA address for the platform and should be used
 everywhere you hold a DMA address returned from the DMA mapping functions.
 
                         What memory is DMA'able?
@@ -316,7 +321,7 @@ There are two types of DMA mappings:
   Think of "consistent" as "synchronous" or "coherent".
 
   The current default is to return consistent memory in the low 32
-  bits of the bus space.  However, for future compatibility you should
+  bits of the DMA space.  However, for future compatibility you should
   set the consistent mask even if this default is fine for your
   driver.
 
@@ -403,7 +408,7 @@ dma_alloc_coherent() returns two values: the virtual address which you
 can use to access it from the CPU and dma_handle which you pass to the
 card.
 
-The CPU virtual address and the DMA bus address are both
+The CPU virtual address and the DMA address are both
 guaranteed to be aligned to the smallest PAGE_SIZE order which
 is greater than or equal to the requested size.  This invariant
 exists (for example) to guarantee that if you allocate a chunk
@@ -645,8 +650,8 @@ PLEASE NOTE:  The 'nents' argument to the dma_unmap_sg call must be
               dma_map_sg call.
 
 Every dma_map_{single,sg}() call should have its dma_unmap_{single,sg}()
-counterpart, because the bus address space is a shared resource and
-you could render the machine unusable by consuming all bus addresses.
+counterpart, because the DMA address space is a shared resource and
+you could render the machine unusable by consuming all DMA addresses.
 
 If you need to use the same streaming DMA region multiple times and touch
 the data in between the DMA transfers, the buffer needs to be synced
index 52088408668a80a03621e075dcbfc587565af8c6..7eba542eff7c8317d0da92ea4b638076b4f0f996 100644 (file)
@@ -18,10 +18,10 @@ Part I - dma_ API
 To get the dma_ API, you must #include <linux/dma-mapping.h>.  This
 provides dma_addr_t and the interfaces described below.
 
-A dma_addr_t can hold any valid DMA or bus address for the platform.  It
-can be given to a device to use as a DMA source or target.  A CPU cannot
-reference a dma_addr_t directly because there may be translation between
-its physical address space and the bus address space.
+A dma_addr_t can hold any valid DMA address for the platform.  It can be
+given to a device to use as a DMA source or target.  A CPU cannot reference
+a dma_addr_t directly because there may be translation between its physical
+address space and the DMA address space.
 
 Part Ia - Using large DMA-coherent buffers
 ------------------------------------------
@@ -42,7 +42,7 @@ It returns a pointer to the allocated region (in the processor's virtual
 address space) or NULL if the allocation failed.
 
 It also returns a <dma_handle> which may be cast to an unsigned integer the
-same width as the bus and given to the device as the bus address base of
+same width as the bus and given to the device as the DMA address base of
 the region.
 
 Note: consistent memory can be expensive on some platforms, and the
@@ -193,7 +193,7 @@ dma_map_single(struct device *dev, void *cpu_addr, size_t size,
                      enum dma_data_direction direction)
 
 Maps a piece of processor virtual memory so it can be accessed by the
-device and returns the bus address of the memory.
+device and returns the DMA address of the memory.
 
 The direction for both APIs may be converted freely by casting.
 However the dma_ API uses a strongly typed enumerator for its
@@ -212,20 +212,20 @@ contiguous piece of memory.  For this reason, memory to be mapped by
 this API should be obtained from sources which guarantee it to be
 physically contiguous (like kmalloc).
 
-Further, the bus address of the memory must be within the
+Further, the DMA address of the memory must be within the
 dma_mask of the device (the dma_mask is a bit mask of the
-addressable region for the device, i.e., if the bus address of
-the memory ANDed with the dma_mask is still equal to the bus
+addressable region for the device, i.e., if the DMA address of
+the memory ANDed with the dma_mask is still equal to the DMA
 address, then the device can perform DMA to the memory).  To
 ensure that the memory allocated by kmalloc is within the dma_mask,
 the driver may specify various platform-dependent flags to restrict
-the bus address range of the allocation (e.g., on x86, GFP_DMA
-guarantees to be within the first 16MB of available bus addresses,
+the DMA address range of the allocation (e.g., on x86, GFP_DMA
+guarantees to be within the first 16MB of available DMA addresses,
 as required by ISA devices).
 
 Note also that the above constraints on physical contiguity and
 dma_mask may not apply if the platform has an IOMMU (a device which
-maps an I/O bus address to a physical memory address).  However, to be
+maps an I/O DMA address to a physical memory address).  However, to be
 portable, device driver writers may *not* assume that such an IOMMU
 exists.
 
@@ -296,7 +296,7 @@ reduce current DMA mapping usage or delay and try again later).
        dma_map_sg(struct device *dev, struct scatterlist *sg,
                int nents, enum dma_data_direction direction)
 
-Returns: the number of bus address segments mapped (this may be shorter
+Returns: the number of DMA address segments mapped (this may be shorter
 than <nents> passed in if some elements of the scatter/gather list are
 physically or virtually adjacent and an IOMMU maps them with a single
 entry).
@@ -340,7 +340,7 @@ must be the same as those and passed in to the scatter/gather mapping
 API.
 
 Note: <nents> must be the number you passed in, *not* the number of
-bus address entries returned.
+DMA address entries returned.
 
 void
 dma_sync_single_for_cpu(struct device *dev, dma_addr_t dma_handle, size_t size,
@@ -507,7 +507,7 @@ it's asked for coherent memory for this device.
 phys_addr is the CPU physical address to which the memory is currently
 assigned (this will be ioremapped so the CPU can access the region).
 
-device_addr is the bus address the device needs to be programmed
+device_addr is the DMA address the device needs to be programmed
 with to actually address this memory (this will be handed out as the
 dma_addr_t in dma_alloc_coherent()).
 
index efc8d90a9a3f454541a468e3564924b9484d4b94..0992531ffefb761eb047d22003130cf45ede9584 100644 (file)
 
     <para>
      Note: The terms "transformation" and cipher algorithm are used
-     interchangably.
+     interchangeably.
     </para>
    </sect1>
 
 
      <para>
       For other use cases of AEAD ciphers, the ASCII art applies as
-      well, but the caller may not use the GIVCIPHER interface. In
-      this case, the caller must generate the IV.
+      well, but the caller may not use the AEAD cipher with a separate
+      IV generator. In this case, the caller must generate the IV.
      </para>
 
      <para>
@@ -584,8 +584,8 @@ kernel crypto API                                |   IPSEC Layer
                                                  |
 +-----------+                                    |
 |           |            (1)
-| givcipher | <-----------------------------------  esp_output
-|  (seqiv)  | ---+
+|   aead    | <-----------------------------------  esp_output
+| (seqniv)  | ---+
 +-----------+    |
                  | (2)
 +-----------+    |
@@ -620,8 +620,8 @@ kernel crypto API                                |   IPSEC Layer
      <orderedlist>
       <listitem>
        <para>
-        esp_output() invokes crypto_aead_givencrypt() to trigger an encryption
-        operation of the GIVCIPHER implementation.
+        esp_output() invokes crypto_aead_encrypt() to trigger an encryption
+        operation of the AEAD cipher with IV generator.
        </para>
 
        <para>
@@ -1563,7 +1563,7 @@ struct sockaddr_alg sa = {
 
    <sect1><title>Zero-Copy Interface</title>
     <para>
-     In addition to the send/write/read/recv system call familty, the AF_ALG
+     In addition to the send/write/read/recv system call family, the AF_ALG
      interface can be accessed with the zero-copy interface of splice/vmsplice.
      As the name indicates, the kernel tries to avoid a copy operation into
      kernel space.
@@ -1669,9 +1669,19 @@ read(opfd, out, outlen);
   </chapter>
 
   <chapter id="API"><title>Programming Interface</title>
+   <para>
+    Please note that the kernel crypto API contains the AEAD givcrypt
+    API (crypto_aead_giv* and aead_givcrypt_* function calls in
+    include/crypto/aead.h). This API is obsolete and will be removed
+    in the future. To obtain the functionality of an AEAD cipher with
+    internal IV generation, use the IV generator as a regular cipher.
+    For example, rfc4106(gcm(aes)) is the AEAD cipher with external
+    IV generation and seqniv(rfc4106(gcm(aes))) implies that the kernel
+    crypto API generates the IV. Different IV generators are available.
+   </para>
    <sect1><title>Block Cipher Context Data Structures</title>
 !Pinclude/linux/crypto.h Block Cipher Context Data Structures
-!Finclude/linux/crypto.h aead_request
+!Finclude/crypto/aead.h aead_request
    </sect1>
    <sect1><title>Block Cipher Algorithm Definitions</title>
 !Pinclude/linux/crypto.h Block Cipher Algorithm Definitions
@@ -1680,7 +1690,7 @@ read(opfd, out, outlen);
 !Finclude/linux/crypto.h aead_alg
 !Finclude/linux/crypto.h blkcipher_alg
 !Finclude/linux/crypto.h cipher_alg
-!Finclude/linux/crypto.h rng_alg
+!Finclude/crypto/rng.h rng_alg
    </sect1>
    <sect1><title>Asynchronous Block Cipher API</title>
 !Pinclude/linux/crypto.h Asynchronous Block Cipher API
@@ -1704,26 +1714,27 @@ read(opfd, out, outlen);
 !Finclude/linux/crypto.h ablkcipher_request_set_crypt
    </sect1>
    <sect1><title>Authenticated Encryption With Associated Data (AEAD) Cipher API</title>
-!Pinclude/linux/crypto.h Authenticated Encryption With Associated Data (AEAD) Cipher API
-!Finclude/linux/crypto.h crypto_alloc_aead
-!Finclude/linux/crypto.h crypto_free_aead
-!Finclude/linux/crypto.h crypto_aead_ivsize
-!Finclude/linux/crypto.h crypto_aead_authsize
-!Finclude/linux/crypto.h crypto_aead_blocksize
-!Finclude/linux/crypto.h crypto_aead_setkey
-!Finclude/linux/crypto.h crypto_aead_setauthsize
-!Finclude/linux/crypto.h crypto_aead_encrypt
-!Finclude/linux/crypto.h crypto_aead_decrypt
+!Pinclude/crypto/aead.h Authenticated Encryption With Associated Data (AEAD) Cipher API
+!Finclude/crypto/aead.h crypto_alloc_aead
+!Finclude/crypto/aead.h crypto_free_aead
+!Finclude/crypto/aead.h crypto_aead_ivsize
+!Finclude/crypto/aead.h crypto_aead_authsize
+!Finclude/crypto/aead.h crypto_aead_blocksize
+!Finclude/crypto/aead.h crypto_aead_setkey
+!Finclude/crypto/aead.h crypto_aead_setauthsize
+!Finclude/crypto/aead.h crypto_aead_encrypt
+!Finclude/crypto/aead.h crypto_aead_decrypt
    </sect1>
    <sect1><title>Asynchronous AEAD Request Handle</title>
-!Pinclude/linux/crypto.h Asynchronous AEAD Request Handle
-!Finclude/linux/crypto.h crypto_aead_reqsize
-!Finclude/linux/crypto.h aead_request_set_tfm
-!Finclude/linux/crypto.h aead_request_alloc
-!Finclude/linux/crypto.h aead_request_free
-!Finclude/linux/crypto.h aead_request_set_callback
-!Finclude/linux/crypto.h aead_request_set_crypt
-!Finclude/linux/crypto.h aead_request_set_assoc
+!Pinclude/crypto/aead.h Asynchronous AEAD Request Handle
+!Finclude/crypto/aead.h crypto_aead_reqsize
+!Finclude/crypto/aead.h aead_request_set_tfm
+!Finclude/crypto/aead.h aead_request_alloc
+!Finclude/crypto/aead.h aead_request_free
+!Finclude/crypto/aead.h aead_request_set_callback
+!Finclude/crypto/aead.h aead_request_set_crypt
+!Finclude/crypto/aead.h aead_request_set_assoc
+!Finclude/crypto/aead.h aead_request_set_ad
    </sect1>
    <sect1><title>Synchronous Block Cipher API</title>
 !Pinclude/linux/crypto.h Synchronous Block Cipher API
index 453ebe6953eefe0b305a82da60259849756bfea9..f05a9afb2c39b61efb841886e6f272adfa9da797 100644 (file)
@@ -10,7 +10,19 @@ also be used to protect arrays.  Three situations are as follows:
 
 3.  Resizeable Arrays
 
-Each of these situations are discussed below.
+Each of these three situations involves an RCU-protected pointer to an
+array that is separately indexed.  It might be tempting to consider use
+of RCU to instead protect the index into an array, however, this use
+case is -not- supported.  The problem with RCU-protected indexes into
+arrays is that compilers can play way too many optimization games with
+integers, which means that the rules governing handling of these indexes
+are far more trouble than they are worth.  If RCU-protected indexes into
+arrays prove to be particularly valuable (which they have not thus far),
+explicit cooperation from the compiler will be required to permit them
+to be safely used.
+
+That aside, each of the three RCU-protected pointer situations are
+described in the following sections.
 
 
 Situation 1: Hash Tables
@@ -36,9 +48,9 @@ Quick Quiz:  Why is it so important that updates be rare when
 Situation 3: Resizeable Arrays
 
 Use of RCU for resizeable arrays is demonstrated by the grow_ary()
-function used by the System V IPC code.  The array is used to map from
-semaphore, message-queue, and shared-memory IDs to the data structure
-that represents the corresponding IPC construct.  The grow_ary()
+function formerly used by the System V IPC code.  The array is used
+to map from semaphore, message-queue, and shared-memory IDs to the data
+structure that represents the corresponding IPC construct.  The grow_ary()
 function does not acquire any locks; instead its caller must hold the
 ids->sem semaphore.
 
index cd83d2348fef8c4a3ff3bee57fc9904976b46390..da51d306885077b050d6c3624b96cb08c75758bb 100644 (file)
@@ -47,11 +47,6 @@ checking of rcu_dereference() primitives:
                Use explicit check expression "c" along with
                srcu_read_lock_held()().  This is useful in code that
                is invoked by both SRCU readers and updaters.
-       rcu_dereference_index_check(p, c):
-               Use explicit check expression "c", but the caller
-               must supply one of the rcu_read_lock_held() functions.
-               This is useful in code that uses RCU-protected arrays
-               that is invoked by both RCU readers and updaters.
        rcu_dereference_raw(p):
                Don't check.  (Use sparingly, if at all.)
        rcu_dereference_protected(p, c):
@@ -64,11 +59,6 @@ checking of rcu_dereference() primitives:
                but retain the compiler constraints that prevent duplicating
                or coalescsing.  This is useful when when testing the
                value of the pointer itself, for example, against NULL.
-       rcu_access_index(idx):
-               Return the value of the index and omit all barriers, but
-               retain the compiler constraints that prevent duplicating
-               or coalescsing.  This is useful when when testing the
-               value of the index itself, for example, against -1.
 
 The rcu_dereference_check() check expression can be any boolean
 expression, but would normally include a lockdep expression.  However,
index ceb05da5a5acd3e8a6735543284c7a64c1c91228..1e6c0da994f544b6fc3acaa786d4f4f932551db6 100644 (file)
@@ -25,17 +25,6 @@ o    You must use one of the rcu_dereference() family of primitives
        for an example where the compiler can in fact deduce the exact
        value of the pointer, and thus cause misordering.
 
-o      Do not use single-element RCU-protected arrays.  The compiler
-       is within its right to assume that the value of an index into
-       such an array must necessarily evaluate to zero.  The compiler
-       could then substitute the constant zero for the computation, so
-       that the array index no longer depended on the value returned
-       by rcu_dereference().  If the array index no longer depends
-       on rcu_dereference(), then both the compiler and the CPU
-       are within their rights to order the array access before the
-       rcu_dereference(), which can cause the array access to return
-       garbage.
-
 o      Avoid cancellation when using the "+" and "-" infix arithmetic
        operators.  For example, for a given variable "x", avoid
        "(x-x)".  There are similar arithmetic pitfalls from other
@@ -76,14 +65,15 @@ o   Do not use the results from the boolean "&&" and "||" when
        dereferencing.  For example, the following (rather improbable)
        code is buggy:
 
-               int a[2];
-               int index;
-               int force_zero_index = 1;
+               int *p;
+               int *q;
 
                ...
 
-               r1 = rcu_dereference(i1)
-               r2 = a[r1 && force_zero_index];  /* BUGGY!!! */
+               p = rcu_dereference(gp)
+               q = &global_q;
+               q += p != &oom_p1 && p != &oom_p2;
+               r1 = *q;  /* BUGGY!!! */
 
        The reason this is buggy is that "&&" and "||" are often compiled
        using branches.  While weak-memory machines such as ARM or PowerPC
@@ -94,14 +84,15 @@ o   Do not use the results from relational operators ("==", "!=",
        ">", ">=", "<", or "<=") when dereferencing.  For example,
        the following (quite strange) code is buggy:
 
-               int a[2];
-               int index;
-               int flip_index = 0;
+               int *p;
+               int *q;
 
                ...
 
-               r1 = rcu_dereference(i1)
-               r2 = a[r1 != flip_index];  /* BUGGY!!! */
+               p = rcu_dereference(gp)
+               q = &global_q;
+               q += p > &oom_p;
+               r1 = *q;  /* BUGGY!!! */
 
        As before, the reason this is buggy is that relational operators
        are often compiled using branches.  And as before, although
@@ -193,6 +184,11 @@ o  Be very careful about comparing pointers obtained from
                pointer.  Note that the volatile cast in rcu_dereference()
                will normally prevent the compiler from knowing too much.
 
+               However, please note that if the compiler knows that the
+               pointer takes on only one of two values, a not-equal
+               comparison will provide exactly the information that the
+               compiler needs to deduce the value of the pointer.
+
 o      Disable any value-speculation optimizations that your compiler
        might provide, especially if you are making use of feedback-based
        optimizations that take data collected from prior runs.  Such
index 88dfce182f660904a609aa97e670a4ce87659985..5746b0c77f3e4c53da9d68b1213bae991f062092 100644 (file)
@@ -256,7 +256,9 @@ rcu_dereference()
        If you are going to be fetching multiple fields from the
        RCU-protected structure, using the local variable is of
        course preferred.  Repeated rcu_dereference() calls look
-       ugly and incur unnecessary overhead on Alpha CPUs.
+       ugly, do not guarantee that the same pointer will be returned
+       if an update happened while in the critical section, and incur
+       unnecessary overhead on Alpha CPUs.
 
        Note that the value returned by rcu_dereference() is valid
        only within the enclosing RCU read-side critical section.
@@ -879,9 +881,7 @@ SRCU:       Initialization/cleanup
 
 All:  lockdep-checked RCU-protected pointer access
 
-       rcu_access_index
        rcu_access_pointer
-       rcu_dereference_index_check
        rcu_dereference_raw
        rcu_lockdep_assert
        rcu_sleep_check
index 15dfce708ebf6ec3263ac0d66ff2b6f3a57471d4..b731b292e8128e30439ae54c1f0f328b56e79b72 100644 (file)
@@ -42,7 +42,7 @@ Adding ACPI support for an existing driver should be pretty
 straightforward. Here is the simplest example:
 
        #ifdef CONFIG_ACPI
-       static struct acpi_device_id mydrv_acpi_match[] = {
+       static const struct acpi_device_id mydrv_acpi_match[] = {
                /* ACPI IDs here */
                { }
        };
@@ -166,7 +166,7 @@ the platform device drivers. Below is an example where we add ACPI support
 to at25 SPI eeprom driver (this is meant for the above ACPI snippet):
 
        #ifdef CONFIG_ACPI
-       static struct acpi_device_id at25_acpi_match[] = {
+       static const struct acpi_device_id at25_acpi_match[] = {
                { "AT25", 0 },
                { },
        };
@@ -230,7 +230,7 @@ Below is an example of how to add ACPI support to the existing mpu3050
 input driver:
 
        #ifdef CONFIG_ACPI
-       static struct acpi_device_id mpu3050_acpi_match[] = {
+       static const struct acpi_device_id mpu3050_acpi_match[] = {
                { "MPU3050", 0 },
                { },
        };
@@ -359,3 +359,54 @@ the id should be set like:
 The ACPI id "XYZ0001" is then used to lookup an ACPI device directly under
 the MFD device and if found, that ACPI companion device is bound to the
 resulting child platform device.
+
+Device Tree namespace link device ID
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+The Device Tree protocol uses device indentification based on the "compatible"
+property whose value is a string or an array of strings recognized as device
+identifiers by drivers and the driver core.  The set of all those strings may be
+regarded as a device indentification namespace analogous to the ACPI/PNP device
+ID namespace.  Consequently, in principle it should not be necessary to allocate
+a new (and arguably redundant) ACPI/PNP device ID for a devices with an existing
+identification string in the Device Tree (DT) namespace, especially if that ID
+is only needed to indicate that a given device is compatible with another one,
+presumably having a matching driver in the kernel already.
+
+In ACPI, the device identification object called _CID (Compatible ID) is used to
+list the IDs of devices the given one is compatible with, but those IDs must
+belong to one of the namespaces prescribed by the ACPI specification (see
+Section 6.1.2 of ACPI 6.0 for details) and the DT namespace is not one of them.
+Moreover, the specification mandates that either a _HID or an _ADR identificaion
+object be present for all ACPI objects representing devices (Section 6.1 of ACPI
+6.0).  For non-enumerable bus types that object must be _HID and its value must
+be a device ID from one of the namespaces prescribed by the specification too.
+
+The special DT namespace link device ID, PRP0001, provides a means to use the
+existing DT-compatible device identification in ACPI and to satisfy the above
+requirements following from the ACPI specification at the same time.  Namely,
+if PRP0001 is returned by _HID, the ACPI subsystem will look for the
+"compatible" property in the device object's _DSD and will use the value of that
+property to identify the corresponding device in analogy with the original DT
+device identification algorithm.  If the "compatible" property is not present
+or its value is not valid, the device will not be enumerated by the ACPI
+subsystem.  Otherwise, it will be enumerated automatically as a platform device
+(except when an I2C or SPI link from the device to its parent is present, in
+which case the ACPI core will leave the device enumeration to the parent's
+driver) and the identification strings from the "compatible" property value will
+be used to find a driver for the device along with the device IDs listed by _CID
+(if present).
+
+Analogously, if PRP0001 is present in the list of device IDs returned by _CID,
+the identification strings listed by the "compatible" property value (if present
+and valid) will be used to look for a driver matching the device, but in that
+case their relative priority with respect to the other device IDs listed by
+_HID and _CID depends on the position of PRP0001 in the _CID return package.
+Specifically, the device IDs returned by _HID and preceding PRP0001 in the _CID
+return package will be checked first.  Also in that case the bus type the device
+will be enumerated to depends on the device ID returned by _HID.
+
+It is valid to define device objects with a _HID returning PRP0001 and without
+the "compatible" property in the _DSD or a _CID as long as one of their
+ancestors provides a _DSD with a valid "compatible" property.  Such device
+objects are then simply regarded as additional "blocks" providing hierarchical
+configuration information to the driver of the composite ancestor device.
index f3c05b5f9f08df8bf3cfe669615c29767ff2f4b5..1690350f16e77beb441efee1d797576087edd612 100644 (file)
@@ -45,11 +45,13 @@ sees fit.)
 
 Requirement: MANDATORY
 
-The device tree blob (dtb) must be placed on an 8-byte boundary within
-the first 512 megabytes from the start of the kernel image and must not
-cross a 2-megabyte boundary. This is to allow the kernel to map the
-blob using a single section mapping in the initial page tables.
+The device tree blob (dtb) must be placed on an 8-byte boundary and must
+not exceed 2 megabytes in size. Since the dtb will be mapped cacheable
+using blocks of up to 2 megabytes in size, it must not be placed within
+any 2M region which must be mapped with any specific attributes.
 
+NOTE: versions prior to v4.2 also require that the DTB be placed within
+the 512 MB region starting at text_offset bytes below the kernel Image.
 
 3. Decompress the kernel image
 ------------------------------
index ff2f28332cc43b27c7b4fa787efe4d69aed76f0a..109e97bbab7717e8b9dd721b47a992ba5773ea31 100644 (file)
@@ -196,8 +196,6 @@ affected_cpus :                     List of Online CPUs that require software
 related_cpus :                 List of Online + Offline CPUs that need software
                                coordination of frequency.
 
-scaling_driver :               Hardware driver for cpufreq.
-
 scaling_cur_freq :             Current frequency of the CPU as determined by
                                the governor and cpufreq core, in KHz. This is
                                the frequency the kernel thinks the CPU runs
diff --git a/Documentation/devicetree/bindings/arm/armv7m_systick.txt b/Documentation/devicetree/bindings/arm/armv7m_systick.txt
new file mode 100644 (file)
index 0000000..7cf4a24
--- /dev/null
@@ -0,0 +1,26 @@
+* ARMv7M System Timer
+
+ARMv7-M includes a system timer, known as SysTick. Current driver only
+implements the clocksource feature.
+
+Required properties:
+- compatible     : Should be "arm,armv7m-systick"
+- reg            : The address range of the timer
+
+Required clocking property, have to be one of:
+- clocks         : The input clock of the timer
+- clock-frequency : The rate in HZ in input of the ARM SysTick
+
+Examples:
+
+systick: timer@e000e010 {
+       compatible = "arm,armv7m-systick";
+       reg = <0xe000e010 0x10>;
+       clocks = <&clk_systick>;
+};
+
+systick: timer@e000e010 {
+       compatible = "arm,armv7m-systick";
+       reg = <0xe000e010 0x10>;
+       clock-frequency = <90000000>;
+};
index 7a4d4926f44e47b9a80077192ae9dacbd1089e7e..5ba6450693b9816dfc1dacef96570e14c22a111b 100644 (file)
@@ -248,7 +248,7 @@ Required properties for peripheral clocks:
 - #address-cells : shall be 1 (reg is used to encode clk id).
 - clocks : shall be the master clock phandle.
        e.g. clocks = <&mck>;
-- name: device tree node describing a specific system clock.
+- name: device tree node describing a specific peripheral clock.
        * #clock-cells : from common clock binding; shall be set to 0.
        * reg: peripheral id. See Atmel's datasheets to get a full
          list of peripheral ids.
index 38988ef1336bab8b73f19615387b7dba83f97651..f0d926bf9f36422ef083558ff18095381988e998 100644 (file)
@@ -1,9 +1,11 @@
-Freescale SoC SEC Security Engines versions 2.x-3.x
+Freescale SoC SEC Security Engines versions 1.x-2.x-3.x
 
 Required properties:
 
 - compatible : Should contain entries for this and backward compatible
-  SEC versions, high to low, e.g., "fsl,sec2.1", "fsl,sec2.0"
+  SEC versions, high to low, e.g., "fsl,sec2.1", "fsl,sec2.0" (SEC2/3)
+                             e.g., "fsl,sec1.2", "fsl,sec1.0" (SEC1)
+    warning: SEC1 and SEC2 are mutually exclusive
 - reg : Offset and length of the register set for the device
 - interrupts : the SEC's interrupt number
 - fsl,num-channels : An integer representing the number of channels
diff --git a/Documentation/devicetree/bindings/crypto/marvell-cesa.txt b/Documentation/devicetree/bindings/crypto/marvell-cesa.txt
new file mode 100644 (file)
index 0000000..c6c6a4a
--- /dev/null
@@ -0,0 +1,45 @@
+Marvell Cryptographic Engines And Security Accelerator
+
+Required properties:
+- compatible: should be one of the following string
+             "marvell,orion-crypto"
+             "marvell,kirkwood-crypto"
+             "marvell,dove-crypto"
+             "marvell,armada-370-crypto"
+             "marvell,armada-xp-crypto"
+             "marvell,armada-375-crypto"
+             "marvell,armada-38x-crypto"
+- reg: base physical address of the engine and length of memory mapped
+       region. Can also contain an entry for the SRAM attached to the CESA,
+       but this representation is deprecated and marvell,crypto-srams should
+       be used instead
+- reg-names: "regs". Can contain an "sram" entry, but this representation
+            is deprecated and marvell,crypto-srams should be used instead
+- interrupts: interrupt number
+- clocks: reference to the crypto engines clocks. This property is not
+         required for orion and kirkwood platforms
+- clock-names: "cesaX" and "cesazX", X should be replaced by the crypto engine
+              id.
+              This property is not required for the orion and kirkwoord
+              platforms.
+              "cesazX" clocks are not required on armada-370 platforms
+- marvell,crypto-srams: phandle to crypto SRAM definitions
+
+Optional properties:
+- marvell,crypto-sram-size: SRAM size reserved for crypto operations, if not
+                           specified the whole SRAM is used (2KB)
+
+
+Examples:
+
+       crypto@90000 {
+               compatible = "marvell,armada-xp-crypto";
+               reg = <0x90000 0x10000>;
+               reg-names = "regs";
+               interrupts = <48>, <49>;
+               clocks = <&gateclk 23>, <&gateclk 23>;
+               clock-names = "cesa0", "cesa1";
+               marvell,crypto-srams = <&crypto_sram0>, <&crypto_sram1>;
+               marvell,crypto-sram-size = <0x600>;
+               status = "okay";
+       };
index 47229b1a594b2479e62bf073128ee0b43476159b..c0c35f00335bfe0e66994bff1bfb4540db5dfea3 100644 (file)
@@ -1,20 +1,33 @@
 Marvell Cryptographic Engines And Security Accelerator
 
 Required properties:
-- compatible : should be "marvell,orion-crypto"
-- reg : base physical address of the engine and length of memory mapped
-        region, followed by base physical address of sram and its memory
-        length
-- reg-names : "regs" , "sram";
-- interrupts : interrupt number
+- compatible: should be one of the following string
+             "marvell,orion-crypto"
+             "marvell,kirkwood-crypto"
+             "marvell,dove-crypto"
+- reg: base physical address of the engine and length of memory mapped
+       region. Can also contain an entry for the SRAM attached to the CESA,
+       but this representation is deprecated and marvell,crypto-srams should
+       be used instead
+- reg-names: "regs". Can contain an "sram" entry, but this representation
+            is deprecated and marvell,crypto-srams should be used instead
+- interrupts: interrupt number
+- clocks: reference to the crypto engines clocks. This property is only
+         required for Dove platforms
+- marvell,crypto-srams: phandle to crypto SRAM definitions
+
+Optional properties:
+- marvell,crypto-sram-size: SRAM size reserved for crypto operations, if not
+                           specified the whole SRAM is used (2KB)
 
 Examples:
 
        crypto@30000 {
                compatible = "marvell,orion-crypto";
-               reg = <0x30000 0x10000>,
-                     <0x4000000 0x800>;
-               reg-names = "regs" , "sram";
+               reg = <0x30000 0x10000>;
+               reg-names = "regs";
                interrupts = <22>;
+               marvell,crypto-srams = <&crypto_sram>;
+               marvell,crypto-sram-size = <0x600>;
                status = "okay";
        };
diff --git a/Documentation/devicetree/bindings/gpio/brcm,brcmstb-gpio.txt b/Documentation/devicetree/bindings/gpio/brcm,brcmstb-gpio.txt
new file mode 100644 (file)
index 0000000..435f1bc
--- /dev/null
@@ -0,0 +1,65 @@
+Broadcom STB "UPG GIO" GPIO controller
+
+The controller's registers are organized as sets of eight 32-bit
+registers with each set controlling a bank of up to 32 pins.  A single
+interrupt is shared for all of the banks handled by the controller.
+
+Required properties:
+
+- compatible:
+    Must be "brcm,brcmstb-gpio"
+
+- reg:
+    Define the base and range of the I/O address space containing
+    the brcmstb GPIO controller registers
+
+- #gpio-cells:
+    Should be <2>.  The first cell is the pin number (within the controller's
+    pin space), and the second is used for the following:
+    bit[0]: polarity (0 for active-high, 1 for active-low)
+
+- gpio-controller:
+    Specifies that the node is a GPIO controller.
+
+- brcm,gpio-bank-widths:
+    Number of GPIO lines for each bank.  Number of elements must
+    correspond to number of banks suggested by the 'reg' property.
+
+Optional properties:
+
+- interrupts:
+    The interrupt shared by all GPIO lines for this controller.
+
+- interrupt-parent:
+    phandle of the parent interrupt controller
+
+- #interrupt-cells:
+    Should be <2>.  The first cell is the GPIO number, the second should specify
+    flags.  The following subset of flags is supported:
+    - bits[3:0] trigger type and level flags
+        1 = low-to-high edge triggered
+        2 = high-to-low edge triggered
+        4 = active high level-sensitive
+        8 = active low level-sensitive
+      Valid combinations are 1, 2, 3, 4, 8.
+    See also Documentation/devicetree/bindings/interrupt-controller/interrupts.txt
+
+- interrupt-controller:
+    Marks the device node as an interrupt controller
+
+- interrupt-names:
+    The name of the IRQ resource used by this controller
+
+Example:
+       upg_gio: gpio@f040a700 {
+               #gpio-cells = <0x2>;
+               #interrupt-cells = <0x2>;
+               compatible = "brcm,bcm7445-gpio", "brcm,brcmstb-gpio";
+               gpio-controller;
+               interrupt-controller;
+               reg = <0xf040a700 0x80>;
+               interrupt-parent = <0xf>;
+               interrupts = <0x6>;
+               interrupt-names = "upg_gio";
+               brcm,gpio-bank-widths = <0x20 0x20 0x20 0x18>;
+       };
diff --git a/Documentation/devicetree/bindings/gpio/gpio-etraxfs.txt b/Documentation/devicetree/bindings/gpio/gpio-etraxfs.txt
new file mode 100644 (file)
index 0000000..abf4db7
--- /dev/null
@@ -0,0 +1,21 @@
+Axis ETRAX FS General I/O controller bindings
+
+Required properties:
+
+- compatible:
+  - "axis,etraxfs-gio"
+- reg: Physical base address and length of the controller's registers.
+- #gpio-cells: Should be 3
+  - The first cell is the gpio offset number.
+  - The second cell is reserved and is currently unused.
+  - The third cell is the port number (hex).
+- gpio-controller: Marks the device node as a GPIO controller.
+
+Example:
+
+       gio: gpio@b001a000 {
+               compatible = "axis,etraxfs-gio";
+               reg = <0xb001a000 0x1000>;
+               gpio-controller;
+               #gpio-cells = <3>;
+       };
diff --git a/Documentation/devicetree/bindings/gpio/gpio-xlp.txt b/Documentation/devicetree/bindings/gpio/gpio-xlp.txt
new file mode 100644 (file)
index 0000000..262ee4d
--- /dev/null
@@ -0,0 +1,47 @@
+Netlogic XLP Family GPIO
+========================
+
+This GPIO driver is used for following Netlogic XLP SoCs:
+       XLP832, XLP316, XLP208, XLP980, XLP532
+
+Required properties:
+-------------------
+
+- compatible: Should be one of the following:
+  - "netlogic,xlp832-gpio": For Netlogic XLP832
+  - "netlogic,xlp316-gpio": For Netlogic XLP316
+  - "netlogic,xlp208-gpio": For Netlogic XLP208
+  - "netlogic,xlp980-gpio": For Netlogic XLP980
+  - "netlogic,xlp532-gpio": For Netlogic XLP532
+- reg: Physical base address and length of the controller's registers.
+- #gpio-cells: Should be two. The first cell is the pin number and the second
+  cell is used to specify optional parameters (currently unused).
+- gpio-controller: Marks the device node as a GPIO controller.
+- nr-gpios: Number of GPIO pins supported by the controller.
+- interrupt-cells: Should be two. The first cell is the GPIO Number. The
+  second cell is used to specify flags. The following subset of flags is
+  supported:
+  - trigger type:
+       1 = low to high edge triggered.
+       2 = high to low edge triggered.
+       4 = active high level-sensitive.
+       8 = active low level-sensitive.
+- interrupts: Interrupt number for this device.
+- interrupt-parent: phandle of the parent interrupt controller.
+- interrupt-controller: Identifies the node as an interrupt controller.
+
+Example:
+
+       gpio: xlp_gpio@34000 {
+               compatible = "netlogic,xlp316-gpio";
+               reg = <0 0x34100 0x1000
+                      0 0x35100 0x1000>;
+               #gpio-cells = <2>;
+               gpio-controller;
+               nr-gpios = <57>;
+
+               #interrupt-cells = <2>;
+               interrupt-parent = <&pic>;
+               interrupts = <39>;
+               interrupt-controller;
+       };
index 986371a4be2c8ae4eae2a09469e5916ccfb6deb1..db4c6a663c031280256d408e6a9f4bd15082a94b 100644 (file)
@@ -6,7 +6,7 @@ Required properties:
                          - First cell is the GPIO line number
                          - Second cell is used to specify optional
                            parameters (unused)
-- compatible           : Should be "xlnx,zynq-gpio-1.0"
+- compatible           : Should be "xlnx,zynq-gpio-1.0" or "xlnx,zynqmp-gpio-1.0"
 - clocks               : Clock specifier (see clock bindings for details)
 - gpio-controller      : Marks the device node as a GPIO controller.
 - interrupts           : Interrupt specifier (see interrupt bindings for
diff --git a/Documentation/devicetree/bindings/gpio/nxp,lpc1850-gpio.txt b/Documentation/devicetree/bindings/gpio/nxp,lpc1850-gpio.txt
new file mode 100644 (file)
index 0000000..eb7cdd6
--- /dev/null
@@ -0,0 +1,39 @@
+NXP LPC18xx/43xx GPIO controller Device Tree Bindings
+-----------------------------------------------------
+
+Required properties:
+- compatible           : Should be "nxp,lpc1850-gpio"
+- reg                  : Address and length of the register set for the device
+- clocks               : Clock specifier (see clock bindings for details)
+- gpio-controller      : Marks the device node as a GPIO controller.
+- #gpio-cells          : Should be two
+                         - First cell is the GPIO line number
+                         - Second cell is used to specify polarity
+
+Optional properties:
+- gpio-ranges          : Mapping between GPIO and pinctrl
+
+Example:
+#define LPC_GPIO(port, pin)    (port * 32 + pin)
+#define LPC_PIN(port, pin)     (0x##port * 32 + pin)
+
+gpio: gpio@400f4000 {
+       compatible = "nxp,lpc1850-gpio";
+       reg = <0x400f4000 0x4000>;
+       clocks = <&ccu1 CLK_CPU_GPIO>;
+       gpio-controller;
+       #gpio-cells = <2>;
+       gpio-ranges =   <&pinctrl LPC_GPIO(0,0)  LPC_PIN(0,0)  2>,
+                       ...
+                       <&pinctrl LPC_GPIO(7,19) LPC_PIN(f,5)  7>;
+};
+
+gpio_joystick {
+       compatible = "gpio-keys-polled";
+       ...
+
+       button@0 {
+               ...
+               gpios = <&gpio LPC_GPIO(4,8) GPIO_ACTIVE_LOW>;
+       };
+};
index fcca8e744f41f9c4692b3b8915d120dcbd0f0768..a04a80f9cc706030677f398736b990665490525b 100644 (file)
@@ -9,6 +9,7 @@ Requires node properties:
        "murata,ncp21wb473"
        "murata,ncp03wb473"
        "murata,ncp15wl333"
+       "murata,ncp03wf104"
 
 /* Usage of vendor name "ntc" is deprecated */
 <DEPRECATED>   "ntc,ncp15wb473"
index 4b641c7bf1c252a3465aa7e028e18042cb7ad61b..09089a6d69ed8d1c9b29115e6abce75a6d1a2fcd 100644 (file)
@@ -32,8 +32,8 @@ Example:
                touchscreen-fuzz-x = <4>;
                touchscreen-fuzz-y = <7>;
                touchscreen-fuzz-pressure = <2>;
-               touchscreen-max-x = <4096>;
-               touchscreen-max-y = <4096>;
+               touchscreen-size-x = <4096>;
+               touchscreen-size-y = <4096>;
                touchscreen-max-pressure = <2048>;
 
                ti,x-plate-ohms = <280>;
index f292917fa00d8bc3b6dce45b26d5cb8370a98289..0e9f09a6a2fe488ca11aa9be2823789f0d63baa7 100644 (file)
@@ -2,7 +2,7 @@
 
 Required properties:
 - compatible: Should be "atmel,<chip>-aic"
-  <chip> can be "at91rm9200", "sama5d3" or "sama5d4"
+  <chip> can be "at91rm9200", "sama5d2", "sama5d3" or "sama5d4"
 - interrupt-controller: Identifies the node as an interrupt controller.
 - interrupt-parent: For single AIC system, it is an empty property.
 - #interrupt-cells: The number of cells to define the interrupts. It should be 3.
index 4f7946ae8adcdc042992a9b4670e7ff10c065f9a..772c550d3b4bcfe23d05f48496f871ed5db5186e 100644 (file)
@@ -13,9 +13,12 @@ Required properties:
 - reg: Base address and length of each register bank used by the external
   IRQ pins driven by the interrupt controller hardware module. The base
   addresses, length and number of required register banks varies with soctype.
-
+- interrupt-controller: Identifies the node as an interrupt controller.
 - #interrupt-cells: has to be <2>: an interrupt index and flags, as defined in
-  interrupts.txt in this directory
+  interrupts.txt in this directory.
+- interrupts: Must contain a list of interrupt specifiers. For each interrupt
+  provided by this irqpin controller instance, there must be one entry,
+  referring to the corresponding parent interrupt.
 
 Optional properties:
 
@@ -25,3 +28,35 @@ Optional properties:
   if different from the default 4 bits
 - control-parent: disable and enable interrupts on the parent interrupt
   controller, needed for some broken implementations
+- clocks: Must contain a reference to the functional clock.  This property is
+  mandatory if the hardware implements a controllable functional clock for
+  the irqpin controller instance.
+- power-domains: Must contain a reference to the power domain. This property is
+  mandatory if the irqpin controller instance is part of a controllable power
+  domain.
+
+
+Example
+-------
+
+       irqpin1: interrupt-controller@e6900004 {
+               compatible = "renesas,intc-irqpin-r8a7740",
+                            "renesas,intc-irqpin";
+               #interrupt-cells = <2>;
+               interrupt-controller;
+               reg = <0xe6900004 4>,
+                       <0xe6900014 4>,
+                       <0xe6900024 1>,
+                       <0xe6900044 1>,
+                       <0xe6900064 1>;
+               interrupts = <0 149 IRQ_TYPE_LEVEL_HIGH
+                             0 149 IRQ_TYPE_LEVEL_HIGH
+                             0 149 IRQ_TYPE_LEVEL_HIGH
+                             0 149 IRQ_TYPE_LEVEL_HIGH
+                             0 149 IRQ_TYPE_LEVEL_HIGH
+                             0 149 IRQ_TYPE_LEVEL_HIGH
+                             0 149 IRQ_TYPE_LEVEL_HIGH
+                             0 149 IRQ_TYPE_LEVEL_HIGH>;
+               clocks = <&mstp2_clks R8A7740_CLK_INTCA>;
+               power-domains = <&pd_a4s>;
+       };
diff --git a/Documentation/devicetree/bindings/iommu/arm,smmu-v3.txt b/Documentation/devicetree/bindings/iommu/arm,smmu-v3.txt
new file mode 100644 (file)
index 0000000..c03eec1
--- /dev/null
@@ -0,0 +1,37 @@
+* ARM SMMUv3 Architecture Implementation
+
+The SMMUv3 architecture is a significant deparature from previous
+revisions, replacing the MMIO register interface with in-memory command
+and event queues and adding support for the ATS and PRI components of
+the PCIe specification.
+
+** SMMUv3 required properties:
+
+- compatible        : Should include:
+
+                      * "arm,smmu-v3" for any SMMUv3 compliant
+                        implementation. This entry should be last in the
+                        compatible list.
+
+- reg               : Base address and size of the SMMU.
+
+- interrupts        : Non-secure interrupt list describing the wired
+                      interrupt sources corresponding to entries in
+                      interrupt-names. If no wired interrupts are
+                      present then this property may be omitted.
+
+- interrupt-names   : When the interrupts property is present, should
+                      include the following:
+                      * "eventq"    - Event Queue not empty
+                      * "priq"      - PRI Queue not empty
+                      * "cmdq-sync" - CMD_SYNC complete
+                      * "gerror"    - Global Error activated
+
+** SMMUv3 optional properties:
+
+- dma-coherent      : Present if DMA operations made by the SMMU (page
+                      table walks, stream table accesses etc) are cache
+                      coherent with the CPU.
+
+                      NOTE: this only applies to the SMMU itself, not
+                      masters connected upstream of the SMMU.
index 7665aa95979f0b57d8036c51e3b4a0fa71807d1a..64fa3b2de6cdfaf1d34f1502fa3938da5b69dac0 100644 (file)
@@ -10,6 +10,9 @@ Required properties:
         "wlf,wm5110"
         "wlf,wm8280"
         "wlf,wm8997"
+        "wlf,wm8998"
+        "wlf,wm1814"
+
   - reg : I2C slave address when connected using I2C, chip select number when
     using SPI.
 
@@ -31,10 +34,10 @@ Required properties:
     as covered in Documentation/devicetree/bindings/regulator/regulator.txt
 
   - DBVDD2-supply, DBVDD3-supply : Additional databus power supplies (wm5102,
-    wm5110, wm8280)
+    wm5110, wm8280, wm8998, wm1814)
 
   - SPKVDDL-supply, SPKVDDR-supply : Speaker driver power supplies (wm5102,
-    wm5110, wm8280)
+    wm5110, wm8280, wm8998, wm1814)
 
   - SPKVDD-supply : Speaker driver power supply (wm8997)
 
@@ -53,8 +56,10 @@ Optional properties:
     of input signals. Valid values are 0 (Differential), 1 (Single-ended) and
     2 (Digital Microphone). If absent, INn_MODE registers set to 0 by default.
     If present, values must be specified less than or equal to the number of
-    input singals. If values less than the number of input signals, elements
-    that has not been specifed are set to 0 by default.
+    input signals. If values less than the number of input signals, elements
+    that have not been specified are set to 0 by default. Entries are:
+    <IN1, IN2, IN3, IN4> (wm5102, wm5110, wm8280, wm8997)
+    <IN1A, IN2A, IN1B, IN2B> (wm8998, wm1814)
 
   - wlf,dmic-ref : DMIC reference voltage source for each input, can be
     selected from either MICVDD or one of the MICBIAS's, defines
index 98685f291a720f4be59a8d1ff6070721fe1220b2..753f14f46e851553d2f5c23d03b3cb595c024d6c 100644 (file)
@@ -1,15 +1,16 @@
-AXP202/AXP209 device tree bindings
+AXP family PMIC device tree bindings
 
 The axp20x family current members :
 axp202 (X-Powers)
 axp209 (X-Powers)
+axp221 (X-Powers)
 
 Required properties:
-- compatible: "x-powers,axp202" or "x-powers,axp209"
+- compatible: "x-powers,axp202", "x-powers,axp209", "x-powers,axp221"
 - reg: The I2C slave address for the AXP chip
 - interrupt-parent: The parent interrupt controller
 - interrupts: SoC NMI / GPIO interrupt connected to the PMIC's IRQ pin
-- interrupt-controller: axp20x has its own internal IRQs
+- interrupt-controller: The PMIC has its own internal IRQs
 - #interrupt-cells: Should be set to 1
 
 Optional properties:
@@ -48,6 +49,31 @@ LDO3         : LDO           : ldo3in-supply
 LDO4           : LDO           : ldo24in-supply        : shared supply
 LDO5           : LDO           : ldo5in-supply
 
+AXP221 regulators, type, and corresponding input supply names:
+
+Regulator        Type            Supply Name             Notes
+---------        ----            -----------             -----
+DCDC1          : DC-DC buck    : vin1-supply
+DCDC2          : DC-DC buck    : vin2-supply
+DCDC3          : DC-DC buck    : vin3-supply
+DCDC4          : DC-DC buck    : vin4-supply
+DCDC5          : DC-DC buck    : vin5-supply
+DC1SW          : On/Off Switch : dcdc1-supply          : DCDC1 secondary output
+DC5LDO         : LDO           : dcdc5-supply          : input from DCDC5
+ALDO1          : LDO           : aldoin-supply         : shared supply
+ALDO2          : LDO           : aldoin-supply         : shared supply
+ALDO3          : LDO           : aldoin-supply         : shared supply
+DLDO1          : LDO           : dldoin-supply         : shared supply
+DLDO2          : LDO           : dldoin-supply         : shared supply
+DLDO3          : LDO           : dldoin-supply         : shared supply
+DLDO4          : LDO           : dldoin-supply         : shared supply
+ELDO1          : LDO           : eldoin-supply         : shared supply
+ELDO2          : LDO           : eldoin-supply         : shared supply
+ELDO3          : LDO           : eldoin-supply         : shared supply
+LDO_IO0                : LDO           : ips-supply            : GPIO 0
+LDO_IO1                : LDO           : ips-supply            : GPIO 1
+RTC_LDO                : LDO           : ips-supply            : always on
+
 Example:
 
 axp209: pmic@34 {
index 8009c3d87f333548467bbefc8cb28db9fcd36c69..1777916e9e280440f3bf6bac5066efd6eb92274e 100644 (file)
@@ -18,6 +18,10 @@ Required properties (SPI):
 - reg: SPI chip select
 
 Optional properties (SPI):
+- google,cros-ec-spi-pre-delay: Some implementations of the EC need a little
+  time to wake up from sleep before they can receive SPI transfers at a high
+  clock rate. This property specifies the delay, in usecs, between the
+  assertion of the CS to the start of the first clock pulse.
 - google,cros-ec-spi-msg-delay: Some implementations of the EC require some
   additional processing time in order to accept new transactions. If the delay
   between transactions is not long enough the EC may not be able to respond
index 42c6fa6f1c9aa961926ed6a60bc1a48f424fa90f..05b21bcb85437198e669ece2725a49c092316264 100644 (file)
@@ -5,6 +5,7 @@ DA9093 consists of a large and varied group of sub-devices (I2C Only):
 Device                   Supply Names    Description
 ------                   ------------    -----------
 da9063-regulator        :               : LDOs & BUCKs
+da9063-onkey            :               : On Key
 da9063-rtc              :               : Real-Time Clock
 da9063-watchdog         :               : Watchdog
 
@@ -51,6 +52,18 @@ Sub-nodes:
   the DA9063. There are currently no entries in this binding, however
   compatible = "dlg,da9063-rtc" should be added if a node is created.
 
+- onkey : This node defines the OnKey settings for controlling the key
+  functionality of the device. The node should contain the compatible property
+  with the value "dlg,da9063-onkey".
+
+  Optional onkey properties:
+
+  - dlg,disable-key-power : Disable power-down using a long key-press. If this
+    entry exists the OnKey driver will remove support for the KEY_POWER key
+    press. If this entry does not exist then by default the key-press
+    triggered power down is enabled and the OnKey will support both KEY_POWER
+    and KEY_SLEEP.
+
 - watchdog : This node defines settings for the Watchdog timer associated
   with the DA9063. There are currently no entries in this binding, however
   compatible = "dlg,da9063-watchdog" should be added if a node is created.
@@ -73,6 +86,11 @@ Example:
                        compatible = "dlg,da9063-watchdog";
                };
 
+               onkey {
+                       compatible = "dlg,da9063-onkey";
+                       dlg,disable-key-power;
+               };
+
                regulators {
                        DA9063_BCORE1: bcore1 {
                                regulator-name = "BCORE1";
index e39f0bc1f55e02ccd705b85ae96714fd0a7e5263..163bd81a4607c66f6dba9cd116d0d2c956e1e24f 100644 (file)
@@ -1,6 +1,6 @@
 Maxim MAX77686 multi-function device
 
-MAX77686 is a Mulitifunction device with PMIC, RTC and Charger on chip. It is
+MAX77686 is a Multifunction device with PMIC, RTC and Charger on chip. It is
 interfaced to host controller using i2c interface. PMIC and Charger submodules
 are addressed using same i2c slave address whereas RTC submodule uses
 different i2c slave address,presently for which we are statically creating i2c
index 38e64405e98d7407b5c3f145b9024942a5675609..d3425846aa5bf1ae865fbd143975459c08bf578c 100644 (file)
@@ -76,7 +76,60 @@ Optional properties:
     Valid values: 4300000, 4700000, 4800000, 4900000
     Default: 4300000
 
+- led : the LED submodule device node
+
+There are two LED outputs available - FLED1 and FLED2. Each of them can
+control a separate LED or they can be connected together to double
+the maximum current for a single connected LED. One LED is represented
+by one child node.
+
+Required properties:
+- compatible : Must be "maxim,max77693-led".
+
+Optional properties:
+- maxim,boost-mode :
+       In boost mode the device can produce up to 1.2A of total current
+       on both outputs. The maximum current on each output is reduced
+       to 625mA then. If not enabled explicitly, boost setting defaults to
+       LEDS_BOOST_FIXED in case both current sources are used.
+       Possible values:
+               LEDS_BOOST_OFF (0) - no boost,
+               LEDS_BOOST_ADAPTIVE (1) - adaptive mode,
+               LEDS_BOOST_FIXED (2) - fixed mode.
+- maxim,boost-mvout : Output voltage of the boost module in millivolts.
+       Valid values: 3300 - 5500, step by 25 (rounded down)
+       Default: 3300
+- maxim,mvsys-min : Low input voltage level in millivolts. Flash is not fired
+       if chip estimates that system voltage could drop below this level due
+       to flash power consumption.
+       Valid values: 2400 - 3400, step by 33 (rounded down)
+       Default: 2400
+
+Required properties for the LED child node:
+- led-sources : see Documentation/devicetree/bindings/leds/common.txt;
+               device current output identifiers: 0 - FLED1, 1 - FLED2
+- led-max-microamp : see Documentation/devicetree/bindings/leds/common.txt
+       Valid values for a LED connected to one FLED output:
+               15625 - 250000, step by 15625 (rounded down)
+       Valid values for a LED connected to both FLED outputs:
+               15625 - 500000, step by 15625 (rounded down)
+- flash-max-microamp : see Documentation/devicetree/bindings/leds/common.txt
+       Valid values for a single LED connected to one FLED output
+       (boost mode must be turned off):
+               15625 - 1000000, step by 15625 (rounded down)
+       Valid values for a single LED connected to both FLED outputs:
+               15625 - 1250000, step by 15625 (rounded down)
+       Valid values for two LEDs case:
+               15625 - 625000, step by 15625 (rounded down)
+- flash-max-timeout-us : see Documentation/devicetree/bindings/leds/common.txt
+       Valid values: 62500 - 1000000, step by 62500 (rounded down)
+
+Optional properties for the LED child node:
+- label : see Documentation/devicetree/bindings/leds/common.txt
+
 Example:
+#include <dt-bindings/leds/common.h>
+
        max77693@66 {
                compatible = "maxim,max77693";
                reg = <0x66>;
@@ -117,5 +170,19 @@ Example:
                        maxim,thermal-regulation-celsius = <75>;
                        maxim,battery-overcurrent-microamp = <3000000>;
                        maxim,charge-input-threshold-microvolt = <4300000>;
+
+               led {
+                       compatible = "maxim,max77693-led";
+                       maxim,boost-mode = <LEDS_BOOST_FIXED>;
+                       maxim,boost-mvout = <5000>;
+                       maxim,mvsys-min = <2400>;
+
+                       camera_flash: flash-led {
+                               label = "max77693-flash";
+                               led-sources = <0>, <1>;
+                               led-max-microamp = <500000>;
+                               flash-max-microamp = <1250000>;
+                               flash-max-timeout-us = <1000000>;
+                       };
                };
        };
index 98ee2abbe1387b16a1a8c965d1020f975da0fa99..7e9490313d5add1f0fddf1327a3901625b1d6d55 100644 (file)
@@ -8,7 +8,8 @@ Device Tree Bindings for the Arasan SDHCI Controller
   [3] Documentation/devicetree/bindings/interrupt-controller/interrupts.txt
 
 Required Properties:
-  - compatible: Compatibility string. Must be 'arasan,sdhci-8.9a'
+  - compatible: Compatibility string. Must be 'arasan,sdhci-8.9a' or
+                'arasan,sdhci-4.9a'
   - reg: From mmc bindings: Register location and length.
   - clocks: From clock bindings: Handles to clock inputs.
   - clock-names: From clock bindings: Tuple including "clk_xin" and "clk_ahb"
index 415c5575cbf7a1394439f3ba807e0f8547167231..5d0376b8f2026ed57daabd33d684590d02ff5920 100644 (file)
@@ -7,7 +7,14 @@ This file documents differences between the core properties described
 by mmc.txt and the properties used by the sdhci-esdhc-imx driver.
 
 Required properties:
-- compatible : Should be "fsl,<chip>-esdhc"
+- compatible : Should be "fsl,<chip>-esdhc", the supported chips include
+              "fsl,imx25-esdhc"
+              "fsl,imx35-esdhc"
+              "fsl,imx51-esdhc"
+              "fsl,imx53-esdhc"
+              "fsl,imx6q-usdhc"
+              "fsl,imx6sl-usdhc"
+              "fsl,imx6sx-usdhc"
 
 Optional properties:
 - fsl,cd-controller : Indicate to use controller internal card detection
index 3b3544931437accded12ada0ec38c786441d1a02..df370585cbccedc5454f547b50e2e3b1512351ca 100644 (file)
@@ -13,6 +13,10 @@ Required Properties:
 
 * compatible: should be one of the following.
   - "hisilicon,hi4511-dw-mshc": for controllers with hi4511 specific extensions.
+  - "hisilicon,hi6220-dw-mshc": for controllers with hi6220 specific extensions.
+
+Optional Properties:
+- hisilicon,peripheral-syscon: phandle of syscon used to control peripheral.
 
 Example:
 
@@ -42,3 +46,27 @@ Example:
                cap-mmc-highspeed;
                cap-sd-highspeed;
        };
+
+       /* for Hi6220 */
+
+       dwmmc_1: dwmmc1@f723e000 {
+               compatible = "hisilicon,hi6220-dw-mshc";
+               num-slots = <0x1>;
+               bus-width = <0x4>;
+               disable-wp;
+               cap-sd-highspeed;
+               sd-uhs-sdr12;
+               sd-uhs-sdr25;
+               card-detect-delay = <200>;
+               hisilicon,peripheral-syscon = <&ao_ctrl>;
+               reg = <0x0 0xf723e000 0x0 0x1000>;
+               interrupts = <0x0 0x49 0x4>;
+               clocks = <&clock_sys HI6220_MMC1_CIUCLK>, <&clock_sys HI6220_MMC1_CLK>;
+               clock-names = "ciu", "biu";
+               cd-gpios = <&gpio1 0 1>;
+               pinctrl-names = "default", "idle";
+               pinctrl-0 = <&sd_pmx_func &sd_clk_cfg_func &sd_cfg_func>;
+               pinctrl-1 = <&sd_pmx_idle &sd_clk_cfg_idle &sd_cfg_idle>;
+               vqmmc-supply = <&ldo7>;
+               vmmc-supply = <&ldo10>;
+       };
index a462c50f19a8840b991ed55e9d76f35da0ae83a3..ce0e7674967190898faa91bef1d7829ed36aeec8 100644 (file)
@@ -21,5 +21,7 @@ Example:
 
        sdhci0_pwrseq {
                compatible = "mmc-pwrseq-simple";
-               reset-gpios = <&gpio1 12 0>;
+               reset-gpios = <&gpio1 12 GPIO_ACTIVE_LOW>;
+               clocks = <&clk_32768_ck>;
+               clock-names = "ext_clock";
        }
index 438899e8829b7d5c077f81af5eff316c54329411..0384fc3f64e83f954183afca1c899fe65966a481 100644 (file)
@@ -21,6 +21,11 @@ Optional properties:
   below for the case, when a GPIO is used for the CD line
 - wp-inverted: when present, polarity on the WP line is inverted. See the note
   below for the case, when a GPIO is used for the WP line
+- disable-wp: When set no physical WP line is present. This property should
+  only be specified when the controller has a dedicated write-protect
+  detection logic. If a GPIO is always used for the write-protect detection
+  logic it is sufficient to not specify wp-gpios property in the absence of a WP
+  line.
 - max-frequency: maximum operating clock frequency
 - no-1-8-v: when present, denotes that 1.8v card voltage is not supported on
   this system, even if the controller claims it is.
diff --git a/Documentation/devicetree/bindings/mmc/mtk-sd.txt b/Documentation/devicetree/bindings/mmc/mtk-sd.txt
new file mode 100644 (file)
index 0000000..a1adfa4
--- /dev/null
@@ -0,0 +1,32 @@
+* MTK MMC controller
+
+The MTK  MSDC can act as a MMC controller
+to support MMC, SD, and SDIO types of memory cards.
+
+This file documents differences between the core properties in mmc.txt
+and the properties used by the msdc driver.
+
+Required properties:
+- compatible: Should be "mediatek,mt8173-mmc","mediatek,mt8135-mmc"
+- interrupts: Should contain MSDC interrupt number
+- clocks: MSDC source clock, HCLK
+- clock-names: "source", "hclk"
+- pinctrl-names: should be "default", "state_uhs"
+- pinctrl-0: should contain default/high speed pin ctrl
+- pinctrl-1: should contain uhs mode pin ctrl
+- vmmc-supply: power to the Core
+- vqmmc-supply: power to the IO
+
+Examples:
+mmc0: mmc@11230000 {
+       compatible = "mediatek,mt8173-mmc", "mediatek,mt8135-mmc";
+       reg = <0 0x11230000 0 0x108>;
+       interrupts = <GIC_SPI 39 IRQ_TYPE_LEVEL_LOW>;
+       vmmc-supply = <&mt6397_vemc_3v3_reg>;
+       vqmmc-supply = <&mt6397_vio18_reg>;
+       clocks = <&pericfg CLK_PERI_MSDC30_0>, <&topckgen CLK_TOP_MSDC50_0_H_SEL>;
+       clock-names = "source", "hclk";
+       pinctrl-names = "default", "state_uhs";
+       pinctrl-0 = <&mmc0_pins_default>;
+       pinctrl-1 = <&mmc0_pins_uhs>;
+};
index 299081f94abdb119bfe3ab481c8f93152c8af124..d38942f6c5ae8ef1b43272bf21ab010145a7fe21 100644 (file)
@@ -18,6 +18,8 @@ Required properties:
   dma-names property.
 - dma-names: must contain "tx" for the transmit DMA channel and "rx" for the
   receive DMA channel.
+- max-frequency: Maximum operating clock frequency, driver uses default clock
+  frequency if it is not set.
 
 
 Example: R8A7790 (R-Car H2) MMCIF0
@@ -29,4 +31,5 @@ Example: R8A7790 (R-Car H2) MMCIF0
                clocks = <&mstp3_clks R8A7790_CLK_MMCIF0>;
                dmas = <&dmac0 0xd1>, <&dmac0 0xd2>;
                dma-names = "tx", "rx";
+               max-frequency = <97500000>;
        };
diff --git a/Documentation/devicetree/bindings/mtd/brcm,brcmnand.txt b/Documentation/devicetree/bindings/mtd/brcm,brcmnand.txt
new file mode 100644 (file)
index 0000000..4ff7128
--- /dev/null
@@ -0,0 +1,150 @@
+* Broadcom STB NAND Controller
+
+The Broadcom Set-Top Box NAND controller supports low-level access to raw NAND
+flash chips. It has a memory-mapped register interface for both control
+registers and for its data input/output buffer. On some SoCs, this controller is
+paired with a custom DMA engine (inventively named "Flash DMA") which supports
+basic PROGRAM and READ functions, among other features.
+
+This controller was originally designed for STB SoCs (BCM7xxx) but is now
+available on a variety of Broadcom SoCs, including some BCM3xxx, BCM63xx, and
+iProc/Cygnus. Its history includes several similar (but not fully register
+compatible) versions.
+
+Required properties:
+- compatible       : May contain an SoC-specific compatibility string (see below)
+                     to account for any SoC-specific hardware bits that may be
+                     added on top of the base core controller.
+                     In addition, must contain compatibility information about
+                     the core NAND controller, of the following form:
+                     "brcm,brcmnand" and an appropriate version compatibility
+                     string, like "brcm,brcmnand-v7.0"
+                     Possible values:
+                         brcm,brcmnand-v4.0
+                         brcm,brcmnand-v5.0
+                         brcm,brcmnand-v6.0
+                         brcm,brcmnand-v6.1
+                         brcm,brcmnand-v7.0
+                         brcm,brcmnand-v7.1
+                         brcm,brcmnand
+- reg              : the register start and length for NAND register region.
+                     (optional) Flash DMA register range (if present)
+                     (optional) NAND flash cache range (if at non-standard offset)
+- reg-names        : a list of the names corresponding to the previous register
+                     ranges. Should contain "nand" and (optionally)
+                     "flash-dma" and/or "nand-cache".
+- interrupts       : The NAND CTLRDY interrupt and (if Flash DMA is available)
+                     FLASH_DMA_DONE
+- interrupt-names  : May be "nand_ctlrdy" or "flash_dma_done", if broken out as
+                     individual interrupts.
+                     May be "nand", if the SoC has the individual NAND
+                     interrupts multiplexed behind another custom piece of
+                     hardware
+- interrupt-parent : See standard interrupt bindings
+- #address-cells   : <1> - subnodes give the chip-select number
+- #size-cells      : <0>
+
+Optional properties:
+- brcm,nand-has-wp          : Some versions of this IP include a write-protect
+                              (WP) control bit. It is always available on >=
+                              v7.0. Use this property to describe the rare
+                              earlier versions of this core that include WP
+
+ -- Additonal SoC-specific NAND controller properties --
+
+The NAND controller is integrated differently on the variety of SoCs on which it
+is found. Part of this integration involves providing status and enable bits
+with which to control the 8 exposed NAND interrupts, as well as hardware for
+configuring the endianness of the data bus. On some SoCs, these features are
+handled via standard, modular components (e.g., their interrupts look like a
+normal IRQ chip), but on others, they are controlled in unique and interesting
+ways, sometimes with registers that lump multiple NAND-related functions
+together. The former case can be described simply by the standard interrupts
+properties in the main controller node. But for the latter exceptional cases,
+we define additional 'compatible' properties and associated register resources within the NAND controller node above.
+
+ - compatible: Can be one of several SoC-specific strings. Each SoC may have
+   different requirements for its additional properties, as described below each
+   bullet point below.
+
+   * "brcm,nand-bcm63138"
+     - reg: (required) the 'NAND_INT_BASE' register range, with separate status
+       and enable registers
+     - reg-names: (required) "nand-int-base"
+
+   * "brcm,nand-iproc"
+     - reg: (required) the "IDM" register range, for interrupt enable and APB
+       bus access endianness configuration, and the "EXT" register range,
+       for interrupt status/ack.
+     - reg-names: (required) a list of the names corresponding to the previous
+       register ranges. Should contain "iproc-idm" and "iproc-ext".
+
+
+* NAND chip-select
+
+Each controller (compatible: "brcm,brcmnand") may contain one or more subnodes
+to represent enabled chip-selects which (may) contain NAND flash chips. Their
+properties are as follows.
+
+Required properties:
+- compatible                : should contain "brcm,nandcs"
+- reg                       : a single integer representing the chip-select
+                              number (e.g., 0, 1, 2, etc.)
+- #address-cells            : see partition.txt
+- #size-cells               : see partition.txt
+- nand-ecc-strength         : see nand.txt
+- nand-ecc-step-size        : must be 512 or 1024. See nand.txt
+
+Optional properties:
+- nand-on-flash-bbt         : boolean, to enable the on-flash BBT for this
+                              chip-select. See nand.txt
+- brcm,nand-oob-sector-size : integer, to denote the spare area sector size
+                              expected for the ECC layout in use. This size, in
+                              addition to the strength and step-size,
+                              determines how the hardware BCH engine will lay
+                              out the parity bytes it stores on the flash.
+                              This property can be automatically determined by
+                              the flash geometry (particularly the NAND page
+                              and OOB size) in many cases, but when booting
+                              from NAND, the boot controller has only a limited
+                              number of available options for its default ECC
+                              layout.
+
+Each nandcs device node may optionally contain sub-nodes describing the flash
+partition mapping. See partition.txt for more detail.
+
+
+Example:
+
+nand@f0442800 {
+       compatible = "brcm,brcmnand-v7.0", "brcm,brcmnand";
+       reg = <0xF0442800 0x600>,
+             <0xF0443000 0x100>;
+       reg-names = "nand", "flash-dma";
+       interrupt-parent = <&hif_intr2_intc>;
+       interrupts = <24>, <4>;
+
+       #address-cells = <1>;
+       #size-cells = <0>;
+
+       nandcs@1 {
+               compatible = "brcm,nandcs";
+               reg = <1>; // Chip select 1
+               nand-on-flash-bbt;
+               nand-ecc-strength = <12>;
+               nand-ecc-step-size = <512>;
+
+               // Partitions
+               #address-cells = <1>;  // <2>, for 64-bit offset
+               #size-cells = <1>;     // <2>, for 64-bit length
+               flash0.rootfs@0 {
+                       reg = <0 0x10000000>;
+               };
+               flash0@0 {
+                       reg = <0 0>; // MTDPART_SIZ_FULL
+               };
+               flash0.kernel@10000000 {
+                       reg = <0x10000000 0x400000>;
+               };
+       };
+};
diff --git a/Documentation/devicetree/bindings/pci/xgene-pci-msi.txt b/Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
new file mode 100644 (file)
index 0000000..36d881c
--- /dev/null
@@ -0,0 +1,68 @@
+* AppliedMicro X-Gene v1 PCIe MSI controller
+
+Required properties:
+
+- compatible: should be "apm,xgene1-msi" to identify
+             X-Gene v1 PCIe MSI controller block.
+- msi-controller: indicates that this is X-Gene v1 PCIe MSI controller node
+- reg: physical base address (0x79000000) and length (0x900000) for controller
+       registers. These registers include the MSI termination address and data
+       registers as well as the MSI interrupt status registers.
+- reg-names: not required
+- interrupts: A list of 16 interrupt outputs of the controller, starting from
+             interrupt number 0x10 to 0x1f.
+- interrupt-names: not required
+
+Each PCIe node needs to have property msi-parent that points to msi controller node
+
+Examples:
+
+SoC DTSI:
+
+       + MSI node:
+       msi@79000000 {
+               compatible = "apm,xgene1-msi";
+               msi-controller;
+               reg = <0x00 0x79000000 0x0 0x900000>;
+               interrupts =    <0x0 0x10 0x4>
+                               <0x0 0x11 0x4>
+                               <0x0 0x12 0x4>
+                               <0x0 0x13 0x4>
+                               <0x0 0x14 0x4>
+                               <0x0 0x15 0x4>
+                               <0x0 0x16 0x4>
+                               <0x0 0x17 0x4>
+                               <0x0 0x18 0x4>
+                               <0x0 0x19 0x4>
+                               <0x0 0x1a 0x4>
+                               <0x0 0x1b 0x4>
+                               <0x0 0x1c 0x4>
+                               <0x0 0x1d 0x4>
+                               <0x0 0x1e 0x4>
+                               <0x0 0x1f 0x4>;
+       };
+
+       + PCIe controller node with msi-parent property pointing to MSI node:
+       pcie0: pcie@1f2b0000 {
+               status = "disabled";
+               device_type = "pci";
+               compatible = "apm,xgene-storm-pcie", "apm,xgene-pcie";
+               #interrupt-cells = <1>;
+               #size-cells = <2>;
+               #address-cells = <3>;
+               reg = < 0x00 0x1f2b0000 0x0 0x00010000   /* Controller registers */
+                       0xe0 0xd0000000 0x0 0x00040000>; /* PCI config space */
+               reg-names = "csr", "cfg";
+               ranges = <0x01000000 0x00 0x00000000 0xe0 0x10000000 0x00 0x00010000   /* io */
+                         0x02000000 0x00 0x80000000 0xe1 0x80000000 0x00 0x80000000>; /* mem */
+               dma-ranges = <0x42000000 0x80 0x00000000 0x80 0x00000000 0x00 0x80000000
+                             0x42000000 0x00 0x00000000 0x00 0x00000000 0x80 0x00000000>;
+               interrupt-map-mask = <0x0 0x0 0x0 0x7>;
+               interrupt-map = <0x0 0x0 0x0 0x1 &gic 0x0 0xc2 0x1
+                                0x0 0x0 0x0 0x2 &gic 0x0 0xc3 0x1
+                                0x0 0x0 0x0 0x3 &gic 0x0 0xc4 0x1
+                                0x0 0x0 0x0 0x4 &gic 0x0 0xc5 0x1>;
+               dma-coherent;
+               clocks = <&pcie0clk 0>;
+               msi-parent= <&msi>;
+       };
diff --git a/Documentation/devicetree/bindings/power/bq24257.txt b/Documentation/devicetree/bindings/power/bq24257.txt
new file mode 100644 (file)
index 0000000..5c9d394
--- /dev/null
@@ -0,0 +1,21 @@
+Binding for TI bq24257 Li-Ion Charger
+
+Required properties:
+- compatible: Should contain one of the following:
+ * "ti,bq24257"
+- reg:                    integer, i2c address of the device.
+- ti,battery-regulation-voltage: integer, maximum charging voltage in uV.
+- ti,charge-current:      integer, maximum charging current in uA.
+- ti,termination-current:  integer, charge will be terminated when current in
+                          constant-voltage phase drops below this value (in uA).
+
+Example:
+
+bq24257 {
+       compatible = "ti,bq24257";
+       reg = <0x6a>;
+
+       ti,battery-regulation-voltage = <4200000>;
+       ti,charge-current = <1000000>;
+       ti,termination-current = <50000>;
+};
diff --git a/Documentation/devicetree/bindings/power/bq25890.txt b/Documentation/devicetree/bindings/power/bq25890.txt
new file mode 100644 (file)
index 0000000..c9dd17d
--- /dev/null
@@ -0,0 +1,46 @@
+Binding for TI bq25890 Li-Ion Charger
+
+Required properties:
+- compatible: Should contain one of the following:
+    * "ti,bq25890"
+- reg: integer, i2c address of the device.
+- ti,battery-regulation-voltage: integer, maximum charging voltage (in uV);
+- ti,charge-current: integer, maximum charging current (in uA);
+- ti,termination-current: integer, charge will be terminated when current in
+    constant-voltage phase drops below this value (in uA);
+- ti,precharge-current: integer, maximum charge current during precharge
+    phase (in uA);
+- ti,minimum-sys-voltage: integer, when battery is charging and it is below
+    minimum system voltage, the system will be regulated above
+    minimum-sys-voltage setting (in uV);
+- ti,boost-voltage: integer, VBUS voltage level in boost mode (in uV);
+- ti,boost-max-current: integer, maximum allowed current draw in boost mode
+    (in uA).
+
+Optional properties:
+- ti,boost-low-freq: boolean, if present boost mode frequency will be 500kHz,
+    otherwise 1.5MHz;
+- ti,use-ilim-pin: boolean, if present the ILIM resistor will be used and the
+    input current will be the lower between the resistor setting and the IINLIM
+    register setting;
+- ti,thermal-regulation-threshold: integer, temperature above which the charge
+    current is lowered, to avoid overheating (in degrees Celsius). If omitted,
+    the default setting will be used (120 degrees);
+
+Example:
+
+bq25890 {
+        compatible = "ti,bq25890";
+        reg = <0x6a>;
+
+        ti,battery-regulation-voltage = <4200000>;
+        ti,charge-current = <1000000>;
+        ti,termination-current = <50000>;
+        ti,precharge-current = <128000>;
+        ti,minimum-sys-voltage = <3600000>;
+        ti,boost-voltage = <5000000>;
+        ti,boost-max-current = <1000000>;
+
+        ti,use-ilim-pin;
+        ti,thermal-regulation-threshold = <120>;
+};
index 74499e5033fc16dc12afd80c090481b31a25d687..0d5e7c97812161b1537e32ba6e689dcba8b1c89d 100644 (file)
@@ -1,8 +1,19 @@
-* Generic OPP Interface
+Generic OPP (Operating Performance Points) Bindings
+----------------------------------------------------
 
-SoCs have a standard set of tuples consisting of frequency and
-voltage pairs that the device will support per voltage domain. These
-are called Operating Performance Points or OPPs.
+Devices work at voltage-current-frequency combinations and some implementations
+have the liberty of choosing these. These combinations are called Operating
+Performance Points aka OPPs. This document defines bindings for these OPPs
+applicable across wide range of devices. For illustration purpose, this document
+uses CPU as a device.
+
+This document contain multiple versions of OPP binding and only one of them
+should be used per device.
+
+Binding 1: operating-points
+============================
+
+This binding only supports voltage-frequency pairs.
 
 Properties:
 - operating-points: An array of 2-tuples items, and each item consists
@@ -23,3 +34,432 @@ cpu@0 {
                198000  850000
        >;
 };
+
+
+Binding 2: operating-points-v2
+============================
+
+* Property: operating-points-v2
+
+Devices supporting OPPs must set their "operating-points-v2" property with
+phandle to a OPP table in their DT node. The OPP core will use this phandle to
+find the operating points for the device.
+
+Devices may want to choose OPP tables at runtime and so can provide a list of
+phandles here. But only *one* of them should be chosen at runtime. This must be
+accompanied by a corresponding "operating-points-names" property, to uniquely
+identify the OPP tables.
+
+If required, this can be extended for SoC vendor specfic bindings. Such bindings
+should be documented as Documentation/devicetree/bindings/power/<vendor>-opp.txt
+and should have a compatible description like: "operating-points-v2-<vendor>".
+
+Optional properties:
+- operating-points-names: Names of OPP tables (required if multiple OPP
+  tables are present), to uniquely identify them. The same list must be present
+  for all the CPUs which are sharing clock/voltage rails and hence the OPP
+  tables.
+
+* OPP Table Node
+
+This describes the OPPs belonging to a device. This node can have following
+properties:
+
+Required properties:
+- compatible: Allow OPPs to express their compatibility. It should be:
+  "operating-points-v2".
+
+- OPP nodes: One or more OPP nodes describing voltage-current-frequency
+  combinations. Their name isn't significant but their phandle can be used to
+  reference an OPP.
+
+Optional properties:
+- opp-shared: Indicates that device nodes using this OPP Table Node's phandle
+  switch their DVFS state together, i.e. they share clock/voltage/current lines.
+  Missing property means devices have independent clock/voltage/current lines,
+  but they share OPP tables.
+
+- status: Marks the OPP table enabled/disabled.
+
+
+* OPP Node
+
+This defines voltage-current-frequency combinations along with other related
+properties.
+
+Required properties:
+- opp-hz: Frequency in Hz
+
+Optional properties:
+- opp-microvolt: voltage in micro Volts.
+
+  A single regulator's voltage is specified with an array of size one or three.
+  Single entry is for target voltage and three entries are for <target min max>
+  voltages.
+
+  Entries for multiple regulators must be present in the same order as
+  regulators are specified in device's DT node.
+
+- opp-microamp: The maximum current drawn by the device in microamperes
+  considering system specific parameters (such as transients, process, aging,
+  maximum operating temperature range etc.) as necessary. This may be used to
+  set the most efficient regulator operating mode.
+
+  Should only be set if opp-microvolt is set for the OPP.
+
+  Entries for multiple regulators must be present in the same order as
+  regulators are specified in device's DT node. If this property isn't required
+  for few regulators, then this should be marked as zero for them. If it isn't
+  required for any regulator, then this property need not be present.
+
+- clock-latency-ns: Specifies the maximum possible transition latency (in
+  nanoseconds) for switching to this OPP from any other OPP.
+
+- turbo-mode: Marks the OPP to be used only for turbo modes. Turbo mode is
+  available on some platforms, where the device can run over its operating
+  frequency for a short duration of time limited by the device's power, current
+  and thermal limits.
+
+- opp-suspend: Marks the OPP to be used during device suspend. Only one OPP in
+  the table should have this.
+
+- status: Marks the node enabled/disabled.
+
+Example 1: Single cluster Dual-core ARM cortex A9, switch DVFS states together.
+
+/ {
+       cpus {
+               #address-cells = <1>;
+               #size-cells = <0>;
+
+               cpu@0 {
+                       compatible = "arm,cortex-a9";
+                       reg = <0>;
+                       next-level-cache = <&L2>;
+                       clocks = <&clk_controller 0>;
+                       clock-names = "cpu";
+                       cpu-supply = <&cpu_supply0>;
+                       operating-points-v2 = <&cpu0_opp_table>;
+               };
+
+               cpu@1 {
+                       compatible = "arm,cortex-a9";
+                       reg = <1>;
+                       next-level-cache = <&L2>;
+                       clocks = <&clk_controller 0>;
+                       clock-names = "cpu";
+                       cpu-supply = <&cpu_supply0>;
+                       operating-points-v2 = <&cpu0_opp_table>;
+               };
+       };
+
+       cpu0_opp_table: opp_table0 {
+               compatible = "operating-points-v2";
+               opp-shared;
+
+               opp00 {
+                       opp-hz = <1000000000>;
+                       opp-microvolt = <970000 975000 985000>;
+                       opp-microamp = <70000>;
+                       clock-latency-ns = <300000>;
+                       opp-suspend;
+               };
+               opp01 {
+                       opp-hz = <1100000000>;
+                       opp-microvolt = <980000 1000000 1010000>;
+                       opp-microamp = <80000>;
+                       clock-latency-ns = <310000>;
+               };
+               opp02 {
+                       opp-hz = <1200000000>;
+                       opp-microvolt = <1025000>;
+                       clock-latency-ns = <290000>;
+                       turbo-mode;
+               };
+       };
+};
+
+Example 2: Single cluster, Quad-core Qualcom-krait, switches DVFS states
+independently.
+
+/ {
+       cpus {
+               #address-cells = <1>;
+               #size-cells = <0>;
+
+               cpu@0 {
+                       compatible = "qcom,krait";
+                       reg = <0>;
+                       next-level-cache = <&L2>;
+                       clocks = <&clk_controller 0>;
+                       clock-names = "cpu";
+                       cpu-supply = <&cpu_supply0>;
+                       operating-points-v2 = <&cpu_opp_table>;
+               };
+
+               cpu@1 {
+                       compatible = "qcom,krait";
+                       reg = <1>;
+                       next-level-cache = <&L2>;
+                       clocks = <&clk_controller 1>;
+                       clock-names = "cpu";
+                       cpu-supply = <&cpu_supply1>;
+                       operating-points-v2 = <&cpu_opp_table>;
+               };
+
+               cpu@2 {
+                       compatible = "qcom,krait";
+                       reg = <2>;
+                       next-level-cache = <&L2>;
+                       clocks = <&clk_controller 2>;
+                       clock-names = "cpu";
+                       cpu-supply = <&cpu_supply2>;
+                       operating-points-v2 = <&cpu_opp_table>;
+               };
+
+               cpu@3 {
+                       compatible = "qcom,krait";
+                       reg = <3>;
+                       next-level-cache = <&L2>;
+                       clocks = <&clk_controller 3>;
+                       clock-names = "cpu";
+                       cpu-supply = <&cpu_supply3>;
+                       operating-points-v2 = <&cpu_opp_table>;
+               };
+       };
+
+       cpu_opp_table: opp_table {
+               compatible = "operating-points-v2";
+
+               /*
+                * Missing opp-shared property means CPUs switch DVFS states
+                * independently.
+                */
+
+               opp00 {
+                       opp-hz = <1000000000>;
+                       opp-microvolt = <970000 975000 985000>;
+                       opp-microamp = <70000>;
+                       clock-latency-ns = <300000>;
+                       opp-suspend;
+               };
+               opp01 {
+                       opp-hz = <1100000000>;
+                       opp-microvolt = <980000 1000000 1010000>;
+                       opp-microamp = <80000>;
+                       clock-latency-ns = <310000>;
+               };
+               opp02 {
+                       opp-hz = <1200000000>;
+                       opp-microvolt = <1025000>;
+                       opp-microamp = <90000;
+                       lock-latency-ns = <290000>;
+                       turbo-mode;
+               };
+       };
+};
+
+Example 3: Dual-cluster, Dual-core per cluster. CPUs within a cluster switch
+DVFS state together.
+
+/ {
+       cpus {
+               #address-cells = <1>;
+               #size-cells = <0>;
+
+               cpu@0 {
+                       compatible = "arm,cortex-a7";
+                       reg = <0>;
+                       next-level-cache = <&L2>;
+                       clocks = <&clk_controller 0>;
+                       clock-names = "cpu";
+                       cpu-supply = <&cpu_supply0>;
+                       operating-points-v2 = <&cluster0_opp>;
+               };
+
+               cpu@1 {
+                       compatible = "arm,cortex-a7";
+                       reg = <1>;
+                       next-level-cache = <&L2>;
+                       clocks = <&clk_controller 0>;
+                       clock-names = "cpu";
+                       cpu-supply = <&cpu_supply0>;
+                       operating-points-v2 = <&cluster0_opp>;
+               };
+
+               cpu@100 {
+                       compatible = "arm,cortex-a15";
+                       reg = <100>;
+                       next-level-cache = <&L2>;
+                       clocks = <&clk_controller 1>;
+                       clock-names = "cpu";
+                       cpu-supply = <&cpu_supply1>;
+                       operating-points-v2 = <&cluster1_opp>;
+               };
+
+               cpu@101 {
+                       compatible = "arm,cortex-a15";
+                       reg = <101>;
+                       next-level-cache = <&L2>;
+                       clocks = <&clk_controller 1>;
+                       clock-names = "cpu";
+                       cpu-supply = <&cpu_supply1>;
+                       operating-points-v2 = <&cluster1_opp>;
+               };
+       };
+
+       cluster0_opp: opp_table0 {
+               compatible = "operating-points-v2";
+               opp-shared;
+
+               opp00 {
+                       opp-hz = <1000000000>;
+                       opp-microvolt = <970000 975000 985000>;
+                       opp-microamp = <70000>;
+                       clock-latency-ns = <300000>;
+                       opp-suspend;
+               };
+               opp01 {
+                       opp-hz = <1100000000>;
+                       opp-microvolt = <980000 1000000 1010000>;
+                       opp-microamp = <80000>;
+                       clock-latency-ns = <310000>;
+               };
+               opp02 {
+                       opp-hz = <1200000000>;
+                       opp-microvolt = <1025000>;
+                       opp-microamp = <90000>;
+                       clock-latency-ns = <290000>;
+                       turbo-mode;
+               };
+       };
+
+       cluster1_opp: opp_table1 {
+               compatible = "operating-points-v2";
+               opp-shared;
+
+               opp10 {
+                       opp-hz = <1300000000>;
+                       opp-microvolt = <1045000 1050000 1055000>;
+                       opp-microamp = <95000>;
+                       clock-latency-ns = <400000>;
+                       opp-suspend;
+               };
+               opp11 {
+                       opp-hz = <1400000000>;
+                       opp-microvolt = <1075000>;
+                       opp-microamp = <100000>;
+                       clock-latency-ns = <400000>;
+               };
+               opp12 {
+                       opp-hz = <1500000000>;
+                       opp-microvolt = <1010000 1100000 1110000>;
+                       opp-microamp = <95000>;
+                       clock-latency-ns = <400000>;
+                       turbo-mode;
+               };
+       };
+};
+
+Example 4: Handling multiple regulators
+
+/ {
+       cpus {
+               cpu@0 {
+                       compatible = "arm,cortex-a7";
+                       ...
+
+                       cpu-supply = <&cpu_supply0>, <&cpu_supply1>, <&cpu_supply2>;
+                       operating-points-v2 = <&cpu0_opp_table>;
+               };
+       };
+
+       cpu0_opp_table: opp_table0 {
+               compatible = "operating-points-v2";
+               opp-shared;
+
+               opp00 {
+                       opp-hz = <1000000000>;
+                       opp-microvolt = <970000>, /* Supply 0 */
+                                       <960000>, /* Supply 1 */
+                                       <960000>; /* Supply 2 */
+                       opp-microamp =  <70000>,  /* Supply 0 */
+                                       <70000>,  /* Supply 1 */
+                                       <70000>;  /* Supply 2 */
+                       clock-latency-ns = <300000>;
+               };
+
+               /* OR */
+
+               opp00 {
+                       opp-hz = <1000000000>;
+                       opp-microvolt = <970000 975000 985000>, /* Supply 0 */
+                                       <960000 965000 975000>, /* Supply 1 */
+                                       <960000 965000 975000>; /* Supply 2 */
+                       opp-microamp =  <70000>,                /* Supply 0 */
+                                       <70000>,                /* Supply 1 */
+                                       <70000>;                /* Supply 2 */
+                       clock-latency-ns = <300000>;
+               };
+
+               /* OR */
+
+               opp00 {
+                       opp-hz = <1000000000>;
+                       opp-microvolt = <970000 975000 985000>, /* Supply 0 */
+                                       <960000 965000 975000>, /* Supply 1 */
+                                       <960000 965000 975000>; /* Supply 2 */
+                       opp-microamp =  <70000>,                /* Supply 0 */
+                                       <0>,                    /* Supply 1 doesn't need this */
+                                       <70000>;                /* Supply 2 */
+                       clock-latency-ns = <300000>;
+               };
+       };
+};
+
+Example 5: Multiple OPP tables
+
+/ {
+       cpus {
+               cpu@0 {
+                       compatible = "arm,cortex-a7";
+                       ...
+
+                       cpu-supply = <&cpu_supply>
+                       operating-points-v2 = <&cpu0_opp_table_slow>, <&cpu0_opp_table_fast>;
+                       operating-points-names = "slow", "fast";
+               };
+       };
+
+       cpu0_opp_table_slow: opp_table_slow {
+               compatible = "operating-points-v2";
+               status = "okay";
+               opp-shared;
+
+               opp00 {
+                       opp-hz = <600000000>;
+                       ...
+               };
+
+               opp01 {
+                       opp-hz = <800000000>;
+                       ...
+               };
+       };
+
+       cpu0_opp_table_fast: opp_table_fast {
+               compatible = "operating-points-v2";
+               status = "okay";
+               opp-shared;
+
+               opp10 {
+                       opp-hz = <1000000000>;
+                       ...
+               };
+
+               opp11 {
+                       opp-hz = <1100000000>;
+                       ...
+               };
+       };
+};
diff --git a/Documentation/devicetree/bindings/power/rt9455_charger.txt b/Documentation/devicetree/bindings/power/rt9455_charger.txt
new file mode 100644 (file)
index 0000000..5d9ad5c
--- /dev/null
@@ -0,0 +1,48 @@
+Binding for Richtek rt9455 battery charger
+
+Required properties:
+- compatible:                          it should contain one of the following:
+                                       "richtek,rt9455".
+- reg:                                 integer, i2c address of the device.
+- interrupt-parent:                    the phandle for the interrupt controller that
+                                       services interrupts for this device.
+- interrupts:                          interrupt mapping for GPIO IRQ, it should be
+                                       configured with IRQ_TYPE_LEVEL_LOW flag.
+- richtek,output-charge-current:       integer, output current from the charger to the
+                                       battery, in uA.
+- richtek,end-of-charge-percentage:    integer, percent of the output charge current.
+                                       When the current in constant-voltage phase drops
+                                       below output_charge_current x end-of-charge-percentage,
+                                       charge is terminated.
+- richtek,battery-regulation-voltage:  integer, maximum battery voltage in uV.
+- richtek,boost-output-voltage:                integer, maximum voltage provided to consumer
+                                       devices, when the charger is in boost mode, in uV.
+
+Optional properties:
+- richtek,min-input-voltage-regulation: integer, input voltage level in uV, used to
+                                       decrease voltage level when the over current
+                                       of the input power source occurs.
+                                       This prevents input voltage drop due to insufficient
+                                       current provided by the power source.
+                                       Default: 4500000 uV (4.5V)
+- richtek,avg-input-current-regulation: integer, input current value in uA drained by the
+                                       charger from the power source.
+                                       Default: 500000 uA (500mA)
+
+Example:
+
+rt9455@22 {
+       compatible = "richtek,rt9455";
+       reg = <0x22>;
+
+       interrupt-parent = <&gpio1>;
+       interrupts = <0 IRQ_TYPE_LEVEL_LOW>;
+
+       richtek,output-charge-current       = <500000>;
+       richtek,end-of-charge-percentage    = <10>;
+       richtek,battery-regulation-voltage  = <4200000>;
+       richtek,boost-output-voltage        = <5050000>;
+
+       richtek,min-input-voltage-regulation = <4500000>;
+       richtek,avg-input-current-regulation = <500000>;
+};
index 5bc9b685cf8aec18057a9f7ea432b99d3049fed6..3f3894aaeebcf9f02f3505f4ad675c99e0d14460 100644 (file)
@@ -9,10 +9,23 @@ Optional properties :
                          (datasheet-recommended value is 10000).
    Defining this property enables current-sense functionality.
 
+Optional threshold properties :
+ If skipped the condition won't be reported.
+ - maxim,cold-temp :      Temperature threshold to report battery
+                          as cold (in tenths of degree Celsius).
+ - maxim,over-heat-temp : Temperature threshold to report battery
+                          as over heated (in tenths of degree Celsius).
+ - maxim,dead-volt :      Voltage threshold to report battery
+                          as dead (in mV).
+ - maxim,over-volt :      Voltage threshold to report battery
+                          as over voltage (in mV).
+
 Example:
 
        battery-charger@36 {
                compatible = "maxim,max17042";
                reg = <0x36>;
                maxim,rsns-microohm = <10000>;
+               maxim,over-heat-temp = <600>;
+               maxim,over-volt = <4300>;
        };
index edda55f7400415a41d70efc9ecc9245d0cc60996..1fc5328c0651bb7d5219fa75887d8c2f630df3dc 100644 (file)
@@ -189,6 +189,19 @@ PROPERTIES
                Definition: There is one reg region describing the port
                configuration registers.
 
+- fsl,fman-10g-port
+               Usage: optional
+               Value type: boolean
+               Definition: The default port rate is 1G.
+               If this property exists, the port is s 10G port.
+
+- fsl,fman-best-effort-port
+               Usage: optional
+               Value type: boolean
+               Definition: Can be defined only if 10G-support is set.
+               This property marks a best-effort 10G port (10G port that
+               may not be capable of line rate).
+
 EXAMPLE
 
 port@a8000 {
index 7f150b5012cc33f8f261460935062359e4f1fb1c..b71b2039e112b165aff6be74c3dee3bf77fda1a1 100644 (file)
@@ -9,6 +9,11 @@ Required properties:
 
  - compatible : Should define the compatible device type for
    global-utilities.
+   Possible compatibles:
+       "fsl,qoriq-device-config-1.0"
+       "fsl,qoriq-device-config-2.0"
+       "fsl,<chip>-device-config"
+       "fsl,<chip>-guts"
  - reg : Offset and length of the register set for the device.
 
 Recommended properties:
index 4f15d8a1bfd005c09484d2acf76dc59f3a73107d..55efb24e5683c429776308e364c996b500f92134 100644 (file)
@@ -2,12 +2,30 @@
 
 Required properties:
 
-- compatible:  must be "maxim,max8973"
+- compatible:  must be one of following:
+                       "maxim,max8973"
+                       "maxim,max77621".
 - reg:         the i2c slave address of the regulator. It should be 0x1b.
 
 Any standard regulator properties can be used to configure the single max8973
 DCDC.
 
+Optional properties:
+
+-maxim,externally-enable: boolean, externally control the regulator output
+               enable/disable.
+-maxim,enable-gpio: GPIO for enable control. If the valid GPIO is provided
+               then externally enable control will be considered.
+-maxim,dvs-gpio: GPIO which is connected to DVS pin of device.
+-maxim,dvs-default-state: Default state of GPIO during initialisation.
+               1 for HIGH and 0 for LOW.
+-maxim,enable-remote-sense: boolean, enable reote sense.
+-maxim,enable-falling-slew-rate: boolean, enable falling slew rate.
+-maxim,enable-active-discharge: boolean: enable active discharge.
+-maxim,enable-frequency-shift: boolean, enable 9% frequency shift.
+-maxim,enable-bias-control: boolean, enable bias control. By enabling this
+               startup delay can be reduce to 20us from 220us.
+
 Example:
 
        max8973@1b {
diff --git a/Documentation/devicetree/bindings/regulator/qcom,spmi-regulator.txt b/Documentation/devicetree/bindings/regulator/qcom,spmi-regulator.txt
new file mode 100644 (file)
index 0000000..75b4604
--- /dev/null
@@ -0,0 +1,121 @@
+Qualcomm SPMI Regulators
+
+- compatible:
+       Usage: required
+       Value type: <string>
+       Definition: must be one of:
+                       "qcom,pm8841-regulators"
+                       "qcom,pm8916-regulators"
+                       "qcom,pm8941-regulators"
+
+- interrupts:
+       Usage: optional
+       Value type: <prop-encoded-array>
+       Definition: List of OCP interrupts.
+
+- interrupt-names:
+       Usage: required if 'interrupts' property present
+       Value type: <string-array>
+       Definition: List of strings defining the names of the
+                   interrupts in the 'interrupts' property 1-to-1.
+                   Supported values are "ocp-<regulator_name>", where
+                   <regulator_name> corresponds to a voltage switch
+                   type regulator.
+
+- vdd_s1-supply:
+- vdd_s2-supply:
+- vdd_s3-supply:
+- vdd_s4-supply:
+- vdd_s5-supply:
+- vdd_s6-supply:
+- vdd_s7-supply:
+- vdd_s8-supply:
+       Usage: optional (pm8841 only)
+       Value type: <phandle>
+       Definition: Reference to regulator supplying the input pin, as
+                   described in the data sheet.
+
+- vdd_s1-supply:
+- vdd_s2-supply:
+- vdd_s3-supply:
+- vdd_s4-supply:
+- vdd_l1_l3-supply:
+- vdd_l2-supply:
+- vdd_l4_l5_l6-supply:
+- vdd_l7-supply:
+- vdd_l8_l11_l14_l15_l16-supply:
+- vdd_l9_l10_l12_l13_l17_l18-supply:
+       Usage: optional (pm8916 only)
+       Value type: <phandle>
+       Definition: Reference to regulator supplying the input pin, as
+                   described in the data sheet.
+
+- vdd_s1-supply:
+- vdd_s2-supply:
+- vdd_s3-supply:
+- vdd_l1_l3-supply:
+- vdd_l2_lvs_1_2_3-supply:
+- vdd_l4_l11-supply:
+- vdd_l5_l7-supply:
+- vdd_l6_l12_l14_l15-supply:
+- vdd_l8_l16_l18_19-supply:
+- vdd_l9_l10_l17_l22-supply:
+- vdd_l13_l20_l23_l24-supply:
+- vdd_l21-supply:
+- vin_5vs-supply:
+       Usage: optional (pm8941 only)
+       Value type: <phandle>
+       Definition: Reference to regulator supplying the input pin, as
+                   described in the data sheet.
+
+
+The regulator node houses sub-nodes for each regulator within the device. Each
+sub-node is identified using the node's name, with valid values listed for each
+of the PMICs below.
+
+pm8841:
+       s1, s2, s3, s4, s5, s6, s7, s8
+
+pm8916:
+       s1, s2, s3, s4, l1, l2, l3, l4, l5, l6, l7, l8, l9, l10, l11, l12, l13,
+       l14, l15, l16, l17, l18
+
+pm8941:
+       s1, s2, s3, l1, l2, l3, l4, l5, l6, l7, l8, l9, l10, l11, l12, l13, l14,
+       l15, l16, l17, l18, l19, l20, l21, l22, l23, l24, lvs1, lvs2, lvs3,
+       mvs1, mvs2
+
+The content of each sub-node is defined by the standard binding for regulators -
+see regulator.txt - with additional custom properties described below:
+
+- regulator-initial-mode:
+       Usage: optional
+       Value type: <u32>
+       Descrption: 1 = Set initial mode to high power mode (HPM), also referred
+                   to as NPM.  HPM consumes more ground current than LPM, but
+                   it can source significantly higher load current. HPM is not
+                   available on boost type regulators. For voltage switch type
+                   regulators, HPM implies that over current protection and
+                   soft start are active all the time. 0 = Set initial mode to
+                   low power mode (LPM).
+
+Example:
+
+       regulators {
+               compatible = "qcom,pm8941-regulators";
+               vdd_l1_l3-supply = <&s1>;
+
+               s1: s1 {
+                       regulator-min-microvolt = <1300000>;
+                       regulator-max-microvolt = <1400000>;
+               };
+
+               ...
+
+               l1: l1 {
+                       regulator-min-microvolt = <1225000>;
+                       regulator-max-microvolt = <1300000>;
+               };
+
+               ....
+       };
index abb26b58c83eea4eb60170f56700f9fa20aa8f88..db88feb28c0313bc9914eb2bcb4fc33dde264295 100644 (file)
@@ -7,18 +7,20 @@ Optional properties:
 - regulator-microvolt-offset: Offset applied to voltages to compensate for voltage drops
 - regulator-min-microamp: smallest current consumers may set
 - regulator-max-microamp: largest current consumers may set
+- regulator-input-current-limit-microamp: maximum input current regulator allows
 - regulator-always-on: boolean, regulator should never be disabled
 - regulator-boot-on: bootloader/firmware enabled regulator
 - regulator-allow-bypass: allow the regulator to go into bypass mode
 - <name>-supply: phandle to the parent supply/regulator node
 - regulator-ramp-delay: ramp delay for regulator(in uV/uS)
   For hardware which supports disabling ramp rate, it should be explicitly
-  intialised to zero (regulator-ramp-delay = <0>) for disabling ramp delay.
+  initialised to zero (regulator-ramp-delay = <0>) for disabling ramp delay.
 - regulator-enable-ramp-delay: The time taken, in microseconds, for the supply
   rail to reach the target voltage, plus/minus whatever tolerance the board
   design requires. This property describes the total system ramp time
   required due to the combination of internal ramping of the regulator itself,
   and board design issues such as trace capacitance and load on the supply.
+- regulator-soft-start: Enable soft start so that voltage ramps slowly
 - regulator-state-mem sub-root node for Suspend-to-RAM mode
   : suspend to memory, the device goes to sleep, but all data stored in memory,
   only some external interrupt can wake the device.
@@ -37,6 +39,9 @@ Optional properties:
 - regulator-initial-mode: initial operating mode. The set of possible operating
   modes depends on the capabilities of every hardware so each device binding
   documentation explains which values the regulator supports.
+- regulator-system-load: Load in uA present on regulator that is not captured by
+  any consumer request.
+- regulator-pull-down: Enable pull down resistor when the regulator is disabled.
 
 Deprecated properties:
 - regulator-compatible: If a regulator chip contains multiple
diff --git a/Documentation/devicetree/bindings/rtc/rtc-st-lpc.txt b/Documentation/devicetree/bindings/rtc/rtc-st-lpc.txt
new file mode 100644 (file)
index 0000000..73407f5
--- /dev/null
@@ -0,0 +1,25 @@
+STMicroelectronics Low Power Controller (LPC) - RTC
+===================================================
+
+LPC currently supports Watchdog OR Real Time Clock functionality.
+
+[See: ../watchdog/st_lpc_wdt.txt for Watchdog options]
+
+Required properties
+
+- compatible   : Must be one of: "st,stih407-lpc" "st,stih416-lpc"
+                                 "st,stih415-lpc" "st,stid127-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)
+- st,lpc-mode  : The LPC can run either one of two modes ST_LPC_MODE_RTC [0] or
+                 ST_LPC_MODE_WDT [1].  One (and only one) mode must be
+                 selected.
+
+Example:
+       lpc@fde05000 {
+               compatible      = "st,stih407-lpc";
+               reg             = <0xfde05000 0x1000>;
+               clocks          = <&clk_s_d3_flexgen CLK_LPC_0>;
+               st,lpc-mode     = <ST_LPC_MODE_RTC>;
+       };
index 48c4dae5d6f944a11c8170dcaa9400b25cfd2ec5..47e46ccbc170f27f7a53de4d49b1d583587dd7f0 100644 (file)
@@ -47,7 +47,7 @@ PROPERTIES
 
        For additional details about the PAMU/LIODN binding(s) see pamu.txt
 
-- fsl,qman-channel-id
+- cell-index
        Usage:          Required
        Value type:     <u32>
        Definition:     The hardware index of the channel. This can also be
@@ -136,7 +136,7 @@ The example below shows a (P4080) QMan portals container/bus node with two porta
                        reg = <0x4000 0x4000>, <0x101000 0x1000>;
                        interrupts = <106 2 0 0>;
                        fsl,liodn = <3 4>;
-                       fsl,qman-channel-id = <1>;
+                       cell-index = <1>;
 
                        fman0 {
                                fsl,liodn = <0x22>;
diff --git a/Documentation/devicetree/bindings/spi/spi-ath79.txt b/Documentation/devicetree/bindings/spi/spi-ath79.txt
new file mode 100644 (file)
index 0000000..f1ad9c3
--- /dev/null
@@ -0,0 +1,24 @@
+Binding for Qualcomm Atheros AR7xxx/AR9xxx SPI controller
+
+Required properties:
+- compatible: has to be "qca,<soc-type>-spi", "qca,ar7100-spi" as fallback.
+- reg: Base address and size of the controllers memory area
+- clocks: phandle to the AHB clock.
+- clock-names: has to be "ahb".
+- #address-cells: <1>, as required by generic SPI binding.
+- #size-cells: <0>, also as required by generic SPI binding.
+
+Child nodes as per the generic SPI binding.
+
+Example:
+
+       spi@1F000000 {
+               compatible = "qca,ar9132-spi", "qca,ar7100-spi";
+               reg = <0x1F000000 0x10>;
+
+               clocks = <&pll 2>;
+               clock-names = "ahb";
+
+               #address-cells = <1>;
+               #size-cells = <0>;
+       };
index 70af78a9185e9b04bc8e24d30b6d9bc3bf705520..fa77f874e3210f247299660ab15208785e588a29 100644 (file)
@@ -1,7 +1,7 @@
 ARM Freescale DSPI controller
 
 Required properties:
-- compatible : "fsl,vf610-dspi"
+- compatible : "fsl,vf610-dspi", "fsl,ls1021a-v1.0-dspi", "fsl,ls2085a-dspi"
 - reg : Offset and length of the register set for the device
 - interrupts : Should contain SPI controller interrupt
 - clocks: from common clock binding: handle to dspi clock.
index 50c3a3de61c1b9c1fe1cce1d4e697875af8392d6..98bc69815eb3f72482f9d5cf6212a91e26eef34d 100644 (file)
@@ -1,7 +1,13 @@
 Marvell Orion SPI device
 
 Required properties:
-- compatible : should be "marvell,orion-spi" or "marvell,armada-370-spi".
+- compatible : should be on of the following:
+    - "marvell,orion-spi" for the Orion, mv78x00, Kirkwood and Dove SoCs
+    - "marvell,armada-370-spi", for the Armada 370 SoCs
+    - "marvell,armada-375-spi", for the Armada 375 SoCs
+    - "marvell,armada-380-spi", for the Armada 38x SoCs
+    - "marvell,armada-390-spi", for the Armada 39x SoCs
+    - "marvell,armada-xp-spi", for the Armada XP SoCs
 - reg : offset and length of the register set for the device
 - cell-index : Which of multiple SPI controllers is this.
 Optional properties:
index 4c7adb8f777ca1c683dfd475306a7ddc20648c57..ddd78ff68fae5b741b5125e7b19696b3c847fd8a 100644 (file)
@@ -1,7 +1,8 @@
 * CSR SiRFprimaII Serial Peripheral Interface
 
 Required properties:
-- compatible : Should be "sirf,prima2-spi"
+- compatible : Should be "sirf,prima2-spi", "sirf,prima2-usp"
+               or "sirf,atlas7-usp"
 - reg : Offset and length of the register set for the device
 - interrupts : Should contain SPI interrupt
 - resets: phandle to the reset controller asserting this device in
diff --git a/Documentation/devicetree/bindings/spi/spi-zynqmp-qspi.txt b/Documentation/devicetree/bindings/spi/spi-zynqmp-qspi.txt
new file mode 100644 (file)
index 0000000..c8f50e5
--- /dev/null
@@ -0,0 +1,26 @@
+Xilinx Zynq UltraScale+ MPSoC GQSPI controller Device Tree Bindings
+-------------------------------------------------------------------
+
+Required properties:
+- compatible           : Should be "xlnx,zynqmp-qspi-1.0".
+- reg                  : Physical base address and size of GQSPI registers map.
+- interrupts           : Property with a value describing the interrupt
+                         number.
+- interrupt-parent     : Must be core interrupt controller.
+- clock-names          : List of input clock names - "ref_clk", "pclk"
+                         (See clock bindings for details).
+- clocks               : Clock phandles (see clock bindings for details).
+
+Optional properties:
+- num-cs               : Number of chip selects used.
+
+Example:
+       qspi: spi@ff0f0000 {
+               compatible = "xlnx,zynqmp-qspi-1.0";
+               clock-names = "ref_clk", "pclk";
+               clocks = <&misc_clk &misc_clk>;
+               interrupts = <0 15 4>;
+               interrupt-parent = <&gic>;
+               num-cs = <1>;
+               reg = <0x0 0xff0f0000 0x1000>,<0x0 0xc0000000 0x8000000>;
+       };
index 4f8184d069cb5a472058a59578101d7b70896367..fb588b3e6a9a3de0bbe9223beef95372a18c7567 100644 (file)
@@ -4,11 +4,16 @@ Required properties:
 - compatible : should be "atmel,at91rm9200-spi".
 - reg: Address and length of the register set for the device
 - interrupts: Should contain spi interrupt
-- cs-gpios: chipselects
+- cs-gpios: chipselects (optional for SPI controller version >= 2 with the
+  Chip Select Active After Transfer feature).
 - clock-names: tuple listing input clock names.
        Required elements: "spi_clk"
 - clocks: phandles to input clocks.
 
+Optional properties:
+- atmel,fifo-size: maximum number of data the RX and TX FIFOs can store for FIFO
+  capable SPI controllers.
+
 Example:
 
 spi1: spi@fffcc000 {
@@ -20,6 +25,7 @@ spi1: spi@fffcc000 {
        clocks = <&spi1_clk>;
        clock-names = "spi_clk";
        cs-gpios = <&pioB 3 0>;
+       atmel,fifo-size = <32>;
        status = "okay";
 
        mmc-slot@0 {
index 22ed6797216d70e3ddcf6f30e40a07d7ea559f1d..4d1673ca8cf802d4b9e13fb53538baa3d5372a48 100644 (file)
@@ -4,9 +4,9 @@ Required properties:
 - compatible : "arm,pl022", "arm,primecell"
 - reg : Offset and length of the register set for the device
 - interrupts : Should contain SPI controller interrupt
+- num-cs : total number of chipselects
 
 Optional properties:
-- num-cs : total number of chipselects
 - cs-gpios : should specify GPIOs used for chipselects.
   The gpios will be referred to as reg = <index> in the SPI child nodes.
   If unspecified, a single SPI device without a chip select can be used.
diff --git a/Documentation/devicetree/bindings/timer/nxp,lpc3220-timer.txt b/Documentation/devicetree/bindings/timer/nxp,lpc3220-timer.txt
new file mode 100644 (file)
index 0000000..51b05a0
--- /dev/null
@@ -0,0 +1,26 @@
+* NXP LPC3220 timer
+
+The NXP LPC3220 timer is used on a wide range of NXP SoCs. This
+includes LPC32xx, LPC178x, LPC18xx and LPC43xx parts.
+
+Required properties:
+- compatible:
+       Should be "nxp,lpc3220-timer".
+- reg:
+       Address and length of the register set.
+- interrupts:
+       Reference to the timer interrupt
+- clocks:
+       Should contain a reference to timer clock.
+- clock-names:
+       Should contain "timerclk".
+
+Example:
+
+timer1: timer@40085000 {
+       compatible = "nxp,lpc3220-timer";
+       reg = <0x40085000 0x1000>;
+       interrupts = <13>;
+       clocks = <&ccu1 CLK_CPU_TIMER1>;
+       clock-names = "timerclk";
+};
diff --git a/Documentation/devicetree/bindings/timer/st,stm32-timer.txt b/Documentation/devicetree/bindings/timer/st,stm32-timer.txt
new file mode 100644 (file)
index 0000000..8ef28e7
--- /dev/null
@@ -0,0 +1,22 @@
+. STMicroelectronics STM32 timer
+
+The STM32 MCUs family has several general-purpose 16 and 32 bits timers.
+
+Required properties:
+- compatible : Should be "st,stm32-timer"
+- reg : Address and length of the register set
+- clocks : Reference on the timer input clock
+- interrupts : Reference to the timer interrupt
+
+Optional properties:
+- resets: Reference to a reset controller asserting the timer
+
+Example:
+
+timer5: timer@40000c00 {
+       compatible = "st,stm32-timer";
+       reg = <0x40000c00 0x400>;
+       interrupts = <50>;
+       resets = <&rrc 259>;
+       clocks = <&clk_pmtr1>;
+};
index dc2a18f0b3a10a9e1bd5814fc429fe9246b82ec7..ddbe304beb212238e859640905b83886e5164ac7 100644 (file)
@@ -15,10 +15,8 @@ Optional properties:
   - phys: phandle + phy specifier pair
   - phy-names: must be "usb"
   - dmas: Must contain a list of references to DMA specifiers.
-  - dma-names : Must contain a list of DMA names:
-   - tx0 ... tx<n>
-   - rx0 ... rx<n>
-    - This <n> means DnFIFO in USBHS module.
+  - dma-names : named "ch%d", where %d is the channel number ranging from zero
+                to the number of channels (DnFIFOs) minus one.
 
 Example:
        usbhs: usb@e6590000 {
index 80339192c93e2626f81eed30fe97ff147c86d746..53d87bad0adc0bba3e2722cc4f7a9e080401da0e 100644 (file)
@@ -161,6 +161,7 @@ ralink      Mediatek/Ralink Technology Corp.
 ramtron        Ramtron International
 realtek Realtek Semiconductor Corp.
 renesas        Renesas Electronics Corporation
+richtek        Richtek Technology Corporation
 ricoh  Ricoh Co. Ltd.
 rockchip       Fuzhou Rockchip Electronics Co., Ltd
 samsung        Samsung Semiconductor
@@ -181,6 +182,7 @@ skyworks    Skyworks Solutions, Inc.
 smsc   Standard Microsystems Corporation
 snps   Synopsys, Inc.
 solidrun       SolidRun
+solomon        Solomon Systech Limited
 sony   Sony Corporation
 spansion       Spansion Inc.
 sprd   Spreadtrum Communications Inc.
index 7a125427ff4bf88a8ef406222f6d1ce577d4fd6b..d1be78db63f5af22f0914695881c6b4d6398d8d3 100644 (file)
@@ -2,7 +2,7 @@
 
 Required properties:
   - compatible: Should be "solomon,<chip>fb-<bus>". The only supported bus for
-    now is i2c, and the supported chips are ssd1306 and ssd1307.
+    now is i2c, and the supported chips are ssd1305, ssd1306 and ssd1307.
   - reg: Should contain address of the controller on the I2C bus. Most likely
          0x3c or 0x3d
   - pwm: Should contain the pwm to use according to the OF device tree PWM
@@ -15,6 +15,16 @@ Required properties:
 
 Optional properties:
   - reset-active-low: Is the reset gpio is active on physical low?
+  - solomon,segment-no-remap: Display needs normal (non-inverted) data column
+                              to segment mapping
+  - solomon,com-seq: Display uses sequential COM pin configuration
+  - solomon,com-lrremap: Display uses left-right COM pin remap
+  - solomon,com-invdir: Display uses inverted COM pin scan direction
+  - solomon,com-offset: Number of the COM pin wired to the first display line
+  - solomon,prechargep1: Length of deselect period (phase 1) in clock cycles.
+  - solomon,prechargep2: Length of precharge period (phase 2) in clock cycles.
+                         This needs to be the higher, the higher the capacitance
+                         of the OLED's pixels is
 
 [0]: Documentation/devicetree/bindings/pwm/pwm.txt
 
@@ -26,3 +36,14 @@ ssd1307: oled@3c {
         reset-gpios = <&gpio2 7>;
         reset-active-low;
 };
+
+ssd1306: oled@3c {
+        compatible = "solomon,ssd1306fb-i2c";
+        reg = <0x3c>;
+        pwms = <&pwm 4 3000>;
+        reset-gpios = <&gpio2 7>;
+        reset-active-low;
+        solomon,com-lrremap;
+        solomon,com-invdir;
+        solomon,com-offset = <32>;
+};
diff --git a/Documentation/devicetree/bindings/watchdog/st_lpc_wdt.txt b/Documentation/devicetree/bindings/watchdog/st_lpc_wdt.txt
new file mode 100644 (file)
index 0000000..388c88a
--- /dev/null
@@ -0,0 +1,38 @@
+STMicroelectronics Low Power Controller (LPC) - Watchdog
+========================================================
+
+LPC currently supports Watchdog OR Real Time Clock functionality.
+
+[See: ../rtc/rtc-st-lpc.txt for RTC options]
+
+Required properties
+
+- compatible   : Must be one of: "st,stih407-lpc" "st,stih416-lpc"
+                                 "st,stih415-lpc" "st,stid127-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)
+- st,lpc-mode  : The LPC can run either one of two modes ST_LPC_MODE_RTC [0] or
+                 ST_LPC_MODE_WDT [1].  One (and only one) mode must be
+                 selected.
+
+Required properties [watchdog mode]
+
+- st,syscfg    : Phandle to syscfg node used to enable watchdog and configure
+                 CPU reset type.
+- timeout-sec  : Watchdog timeout in seconds
+
+Optional properties [watchdog mode]
+
+- st,warm-reset        : If present reset type will be 'warm' - if not it will be cold
+
+Example:
+       lpc@fde05000 {
+               compatible      = "st,stih407-lpc";
+               reg             = <0xfde05000 0x1000>;
+               clocks          = <&clk_s_d3_flexgen CLK_LPC_0>;
+               st,syscfg       = <&syscfg_core>;
+               timeout-sec     = <120>;
+               st,lpc-mode     = <ST_LPC_MODE_WDT>;
+               st,warm-reset;
+       };
index 0a926e2ba3ab68ffd541f5a619cd673e59826821..6a34a0f4d37ccf33248cfd81d2c917d45d8401bc 100644 (file)
@@ -50,8 +50,8 @@ prototypes:
        int (*rename2) (struct inode *, struct dentry *,
                        struct inode *, struct dentry *, unsigned int);
        int (*readlink) (struct dentry *, char __user *,int);
-       void * (*follow_link) (struct dentry *, struct nameidata *);
-       void (*put_link) (struct dentry *, struct nameidata *, void *);
+       const char *(*follow_link) (struct dentry *, void **);
+       void (*put_link) (struct inode *, void *);
        void (*truncate) (struct inode *);
        int (*permission) (struct inode *, int, unsigned int);
        int (*get_acl)(struct inode *, int);
index 7cac200e2a85dd1efebc9f05e84cb8a2b9532b37..7eb762eb31361739bac381a7453ecb449facf161 100644 (file)
@@ -1,41 +1,15 @@
-Support is available for filesystems that wish to do automounting support (such
-as kAFS which can be found in fs/afs/). This facility includes allowing
-in-kernel mounts to be performed and mountpoint degradation to be
-requested. The latter can also be requested by userspace.
+Support is available for filesystems that wish to do automounting
+support (such as kAFS which can be found in fs/afs/ and NFS in
+fs/nfs/). This facility includes allowing in-kernel mounts to be
+performed and mountpoint degradation to be requested. The latter can
+also be requested by userspace.
 
 
 ======================
 IN-KERNEL AUTOMOUNTING
 ======================
 
-A filesystem can now mount another filesystem on one of its directories by the
-following procedure:
-
- (1) Give the directory a follow_link() operation.
-
-     When the directory is accessed, the follow_link op will be called, and
-     it will be provided with the location of the mountpoint in the nameidata
-     structure (vfsmount and dentry).
-
- (2) Have the follow_link() op do the following steps:
-
-     (a) Call vfs_kern_mount() to call the appropriate filesystem to set up a
-         superblock and gain a vfsmount structure representing it.
-
-     (b) Copy the nameidata provided as an argument and substitute the dentry
-        argument into it the copy.
-
-     (c) Call do_add_mount() to install the new vfsmount into the namespace's
-        mountpoint tree, thus making it accessible to userspace. Use the
-        nameidata set up in (b) as the destination.
-
-        If the mountpoint will be automatically expired, then do_add_mount()
-        should also be given the location of an expiration list (see further
-        down).
-
-     (d) Release the path in the nameidata argument and substitute in the new
-        vfsmount and its root dentry. The ref counts on these will need
-        incrementing.
+See section "Mount Traps" of  Documentation/filesystems/autofs4.txt
 
 Then from userspace, you can just do something like:
 
@@ -61,17 +35,18 @@ AUTOMATIC MOUNTPOINT EXPIRY
 ===========================
 
 Automatic expiration of mountpoints is easy, provided you've mounted the
-mountpoint to be expired in the automounting procedure outlined above.
+mountpoint to be expired in the automounting procedure outlined separately.
 
 To do expiration, you need to follow these steps:
 
- (3) Create at least one list off which the vfsmounts to be expired can be
-     hung. Access to this list will be governed by the vfsmount_lock.
+ (1) Create at least one list off which the vfsmounts to be expired can be
+     hung.
 
- (4) In step (2c) above, the call to do_add_mount() should be provided with a
-     pointer to this list. It will hang the vfsmount off of it if it succeeds.
+ (2) When a new mountpoint is created in the ->d_automount method, add
+     the mnt to the list using mnt_set_expiry()
+             mnt_set_expiry(newmnt, &afs_vfsmounts);
 
- (5) When you want mountpoints to be expired, call mark_mounts_for_expiry()
+ (3) When you want mountpoints to be expired, call mark_mounts_for_expiry()
      with a pointer to this list. This will process the list, marking every
      vfsmount thereon for potential expiry on the next call.
 
index e69274de8d0c9c1754cc40b8d99d78a724e63e50..3eae250254d581d253dd035109d568f8ef0c7873 100644 (file)
@@ -483,3 +483,20 @@ in your dentry operations instead.
 --
 [mandatory]
        ->aio_read/->aio_write are gone.  Use ->read_iter/->write_iter.
+---
+[recommended]
+       for embedded ("fast") symlinks just set inode->i_link to wherever the
+       symlink body is and use simple_follow_link() as ->follow_link().
+--
+[mandatory]
+       calling conventions for ->follow_link() have changed.  Instead of returning
+       cookie and using nd_set_link() to store the body to traverse, we return
+       the body to traverse and store the cookie using explicit void ** argument.
+       nameidata isn't passed at all - nd_jump_link() doesn't need it and
+       nd_[gs]et_link() is gone.
+--
+[mandatory]
+       calling conventions for ->put_link() have changed.  It gets inode instead of
+       dentry,  it does not get nameidata at all and it gets called only when cookie
+       is non-NULL.  Note that link body isn't available anymore, so if you need it,
+       store it as cookie.
index 5d833b32bbcd1046de40a15fee169ed462d274fc..b403b29ef7107cd9bfad4a4d0d509cbeb22f145e 100644 (file)
@@ -350,8 +350,8 @@ struct inode_operations {
        int (*rename2) (struct inode *, struct dentry *,
                        struct inode *, struct dentry *, unsigned int);
        int (*readlink) (struct dentry *, char __user *,int);
-        void * (*follow_link) (struct dentry *, struct nameidata *);
-        void (*put_link) (struct dentry *, struct nameidata *, void *);
+       const char *(*follow_link) (struct dentry *, void **);
+       void (*put_link) (struct inode *, void *);
        int (*permission) (struct inode *, int);
        int (*get_acl)(struct inode *, int);
        int (*setattr) (struct dentry *, struct iattr *);
@@ -436,16 +436,18 @@ otherwise noted.
 
   follow_link: called by the VFS to follow a symbolic link to the
        inode it points to.  Only required if you want to support
-       symbolic links.  This method returns a void pointer cookie
-       that is passed to put_link().
+       symbolic links.  This method returns the symlink body
+       to traverse (and possibly resets the current position with
+       nd_jump_link()).  If the body won't go away until the inode
+       is gone, nothing else is needed; if it needs to be otherwise
+       pinned, the data needed to release whatever we'd grabbed
+       is to be stored in void * variable passed by address to
+       follow_link() instance.
 
   put_link: called by the VFS to release resources allocated by
-       follow_link().  The cookie returned by follow_link() is passed
-       to this method as the last parameter.  It is used by
-       filesystems such as NFS where page cache is not stable
-       (i.e. page that was installed when the symbolic link walk
-       started might not be in the page cache at the end of the
-       walk).
+       follow_link().  The cookie stored by follow_link() is passed
+       to this method as the last parameter; only called when
+       cookie isn't NULL.
 
   permission: called by the VFS to check for access rights on a POSIX-like
        filesystem.
index c21c1313f09e55bee448fbb62d5efcd93eabe848..bbc8b5888b806c74d30effc909834496ac4e72c3 100644 (file)
@@ -241,18 +241,18 @@ Set multiple GPIO outputs with a single function call
 -----------------------------------------------------
 The following functions set the output values of an array of GPIOs:
 
-       void gpiod_set_array(unsigned int array_size,
-                            struct gpio_desc **desc_array,
-                            int *value_array)
-       void gpiod_set_raw_array(unsigned int array_size,
-                                struct gpio_desc **desc_array,
-                                int *value_array)
-       void gpiod_set_array_cansleep(unsigned int array_size,
-                                     struct gpio_desc **desc_array,
-                                     int *value_array)
-       void gpiod_set_raw_array_cansleep(unsigned int array_size,
-                                         struct gpio_desc **desc_array,
-                                         int *value_array)
+       void gpiod_set_array_value(unsigned int array_size,
+                                  struct gpio_desc **desc_array,
+                                  int *value_array)
+       void gpiod_set_raw_array_value(unsigned int array_size,
+                                      struct gpio_desc **desc_array,
+                                      int *value_array)
+       void gpiod_set_array_value_cansleep(unsigned int array_size,
+                                           struct gpio_desc **desc_array,
+                                           int *value_array)
+       void gpiod_set_raw_array_value_cansleep(unsigned int array_size,
+                                               struct gpio_desc **desc_array,
+                                               int *value_array)
 
 The array can be an arbitrary set of GPIOs. The functions will try to set
 GPIOs belonging to the same bank or chip simultaneously if supported by the
@@ -271,8 +271,8 @@ matches the desired group of GPIOs, those GPIOs can be set by simply using
 the struct gpio_descs returned by gpiod_get_array():
 
        struct gpio_descs *my_gpio_descs = gpiod_get_array(...);
-       gpiod_set_array(my_gpio_descs->ndescs, my_gpio_descs->desc,
-                       my_gpio_values);
+       gpiod_set_array_value(my_gpio_descs->ndescs, my_gpio_descs->desc,
+                             my_gpio_values);
 
 It is also possible to set a completely arbitrary array of descriptors. The
 descriptors may be obtained using any combination of gpiod_get() and
index 6f83fa965b4b6e6975fda4459c72f9711ea73159..79ab5648d69b3e39a198c108c739eb1a08c20b06 100644 (file)
@@ -751,9 +751,6 @@ requested using gpio_request():
        int gpio_export_link(struct device *dev, const char *name,
                unsigned gpio)
 
-       /* change the polarity of a GPIO node in sysfs */
-       int gpio_sysfs_set_active_low(unsigned gpio, int value);
-
 After a kernel driver requests a GPIO, it may only be made available in
 the sysfs interface by gpio_export().  The driver can control whether the
 signal direction may change.  This helps drivers prevent userspace code
@@ -767,9 +764,3 @@ After the GPIO has been exported, gpio_export_link() allows creating
 symlinks from elsewhere in sysfs to the GPIO sysfs node.  Drivers can
 use this to provide the interface under their own device in sysfs with
 a descriptive name.
-
-Drivers can use gpio_sysfs_set_active_low() to hide GPIO line polarity
-differences between boards from user space.  This only affects the
-sysfs interface.  Polarity change can be done both before and after
-gpio_export(), and previously enabled poll(2) support for either
-rising or falling edge will be reconfigured to follow this setting.
index c2c3a97f8ff7c26f17a90fde416212f02882be94..535b6a8a7a7cca8ffdc37a272d8e6ad434cab7da 100644 (file)
@@ -132,9 +132,6 @@ requested using gpio_request():
        int gpiod_export_link(struct device *dev, const char *name,
                      struct gpio_desc *desc);
 
-       /* change the polarity of a GPIO node in sysfs */
-       int gpiod_sysfs_set_active_low(struct gpio_desc *desc, int value);
-
 After a kernel driver requests a GPIO, it may only be made available in
 the sysfs interface by gpiod_export(). The driver can control whether the
 signal direction may change. This helps drivers prevent userspace code
@@ -148,8 +145,3 @@ After the GPIO has been exported, gpiod_export_link() allows creating
 symlinks from elsewhere in sysfs to the GPIO sysfs node. Drivers can
 use this to provide the interface under their own device in sysfs with
 a descriptive name.
-
-Drivers can use gpiod_sysfs_set_active_low() to hide GPIO line polarity
-differences between boards from user space. Polarity change can be done both
-before and after gpiod_export(), and previously enabled poll(2) support for
-either rising or falling edge will be reconfigured to follow this setting.
index c5e05e2900a3250ed7f0884c15a82d72154bf65e..1d4cc847c6fe6a9cc53ec39b36961bc94c78ad17 100644 (file)
@@ -2,8 +2,10 @@ Kernel driver ntc_thermistor
 =================
 
 Supported thermistors from Murata:
-* Murata NTC Thermistors NCP15WB473, NCP18WB473, NCP21WB473, NCP03WB473, NCP15WL333
-  Prefixes: 'ncp15wb473', 'ncp18wb473', 'ncp21wb473', 'ncp03wb473', 'ncp15wl333'
+* Murata NTC Thermistors NCP15WB473, NCP18WB473, NCP21WB473, NCP03WB473,
+  NCP15WL333, NCP03WF104
+  Prefixes: 'ncp15wb473', 'ncp18wb473', 'ncp21wb473', 'ncp03wb473',
+  'ncp15wl333', 'ncp03wf104'
   Datasheet: Publicly available at Murata
 
 Supported thermistors from EPCOS:
diff --git a/Documentation/hwmon/tc74 b/Documentation/hwmon/tc74
new file mode 100644 (file)
index 0000000..43027aa
--- /dev/null
@@ -0,0 +1,20 @@
+Kernel driver tc74
+====================
+
+Supported chips:
+   * Microchip TC74
+     Prefix: 'tc74'
+     Datasheet: Publicly available at Microchip website.
+
+Description
+-----------
+
+Driver supports the above part.
+
+The tc74 has an 8-bit sensor, with 1 degree centigrade resolution
+and +- 2 degrees centigrade accuracy.
+
+Notes
+-----
+
+Currently entering low power standby mode is not supported.
index 389bb5d618549e5db99ed5ea8cb1b23f38ca48f4..b228ca54bcf4863cdad2a12e4d2533e4fe689a71 100644 (file)
@@ -31,10 +31,10 @@ User manual
 ===========
 
 I2C slave backends behave like standard I2C clients. So, you can instantiate
-them like described in the document 'instantiating-devices'. A quick example
-for instantiating the slave-eeprom driver from userspace:
+them as described in the document 'instantiating-devices'. A quick example for
+instantiating the slave-eeprom driver from userspace at address 0x64 on bus 1:
 
-  # echo 0-0064 > /sys/bus/i2c/drivers/i2c-slave-eeprom/bind
+  # echo slave-24c02 0x64 > /sys/bus/i2c/devices/i2c-1/new_device
 
 Each backend should come with separate documentation to describe its specific
 behaviour and setup.
index 61ab1628a057cc2c4d8b11d892d834f7e5f7773a..ae4474940fe22129492ad62afad870702e6884fc 100644 (file)
@@ -179,11 +179,6 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
 
                        See also Documentation/power/runtime_pm.txt, pci=noacpi
 
-       acpi_rsdp=      [ACPI,EFI,KEXEC]
-                       Pass the RSDP address to the kernel, mostly used
-                       on machines running EFI runtime service to boot the
-                       second kernel for kdump.
-
        acpi_apic_instance=     [ACPI, IOAPIC]
                        Format: <int>
                        2: use 2nd APIC table, if available
@@ -197,6 +192,14 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
                        (e.g. thinkpad_acpi, sony_acpi, etc.) instead
                        of the ACPI video.ko driver.
 
+       acpica_no_return_repair [HW, ACPI]
+                       Disable AML predefined validation mechanism
+                       This mechanism can repair the evaluation result to make
+                       the return objects more ACPI specification compliant.
+                       This option is useful for developers to identify the
+                       root cause of an AML interpreter issue when the issue
+                       has something to do with the repair mechanism.
+
        acpi.debug_layer=       [HW,ACPI,ACPI_DEBUG]
        acpi.debug_level=       [HW,ACPI,ACPI_DEBUG]
                        Format: <int>
@@ -225,6 +228,22 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
                        unusable.  The "log_buf_len" parameter may be useful
                        if you need to capture more output.
 
+       acpi_enforce_resources= [ACPI]
+                       { strict | lax | no }
+                       Check for resource conflicts between native drivers
+                       and ACPI OperationRegions (SystemIO and SystemMemory
+                       only). IO ports and memory declared in ACPI might be
+                       used by the ACPI subsystem in arbitrary AML code and
+                       can interfere with legacy drivers.
+                       strict (default): access to resources claimed by ACPI
+                       is denied; legacy drivers trying to access reserved
+                       resources will fail to bind to device using them.
+                       lax: access to resources claimed by ACPI is allowed;
+                       legacy drivers trying to access reserved resources
+                       will bind successfully but a warning message is logged.
+                       no: ACPI OperationRegions are not marked as reserved,
+                       no further checks are performed.
+
        acpi_force_table_verification   [HW,ACPI]
                        Enable table checksum verification during early stage.
                        By default, this is disabled due to x86 early mapping
@@ -253,6 +272,9 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
                        This feature is enabled by default.
                        This option allows to turn off the feature.
 
+       acpi_no_memhotplug [ACPI] Disable memory hotplug.  Useful for kdump
+                          kernels.
+
        acpi_no_static_ssdt     [HW,ACPI]
                        Disable installation of static SSDTs at early boot time
                        By default, SSDTs contained in the RSDT/XSDT will be
@@ -263,13 +285,10 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
                        dynamic table installation which will install SSDT
                        tables to /sys/firmware/acpi/tables/dynamic.
 
-       acpica_no_return_repair [HW, ACPI]
-                       Disable AML predefined validation mechanism
-                       This mechanism can repair the evaluation result to make
-                       the return objects more ACPI specification compliant.
-                       This option is useful for developers to identify the
-                       root cause of an AML interpreter issue when the issue
-                       has something to do with the repair mechanism.
+       acpi_rsdp=      [ACPI,EFI,KEXEC]
+                       Pass the RSDP address to the kernel, mostly used
+                       on machines running EFI runtime service to boot the
+                       second kernel for kdump.
 
        acpi_os_name=   [HW,ACPI] Tell ACPI BIOS the name of the OS
                        Format: To spoof as Windows 98: ="Microsoft Windows"
@@ -365,25 +384,6 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
                        Use timer override. For some broken Nvidia NF5 boards
                        that require a timer override, but don't have HPET
 
-       acpi_enforce_resources= [ACPI]
-                       { strict | lax | no }
-                       Check for resource conflicts between native drivers
-                       and ACPI OperationRegions (SystemIO and SystemMemory
-                       only). IO ports and memory declared in ACPI might be
-                       used by the ACPI subsystem in arbitrary AML code and
-                       can interfere with legacy drivers.
-                       strict (default): access to resources claimed by ACPI
-                       is denied; legacy drivers trying to access reserved
-                       resources will fail to bind to device using them.
-                       lax: access to resources claimed by ACPI is allowed;
-                       legacy drivers trying to access reserved resources
-                       will bind successfully but a warning message is logged.
-                       no: ACPI OperationRegions are not marked as reserved,
-                       no further checks are performed.
-
-       acpi_no_memhotplug [ACPI] Disable memory hotplug.  Useful for kdump
-                          kernels.
-
        add_efi_memmap  [EFI; X86] Include EFI memory map in
                        kernel's map of available physical RAM.
 
@@ -746,6 +746,12 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
        cpuidle.off=1   [CPU_IDLE]
                        disable the cpuidle sub-system
 
+       cpu_init_udelay=N
+                       [X86] Delay for N microsec between assert and de-assert
+                       of APIC INIT to start processors.  This delay occurs
+                       on every CPU online, such as boot, and resume from suspend.
+                       Default: 10000
+
        cpcihp_generic= [HW,PCI] Generic port I/O CompactPCI driver
                        Format:
                        <first_slot>,<last_slot>,<port>,<enum_bit>[,<debug>]
@@ -937,6 +943,10 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
                        Enable debug messages at boot time.  See
                        Documentation/dynamic-debug-howto.txt for details.
 
+       nompx           [X86] Disables Intel Memory Protection Extensions.
+                       See Documentation/x86/intel_mpx.txt for more
+                       information about the feature.
+
        eagerfpu=       [X86]
                        on      enable eager fpu restore
                        off     disable eager fpu restore
@@ -1481,6 +1491,12 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
                        By default, super page will be supported if Intel IOMMU
                        has the capability. With this option, super page will
                        not be supported.
+               ecs_off [Default Off]
+                       By default, extended context tables will be supported if
+                       the hardware advertises that it has support both for the
+                       extended tables themselves, and also PASID support. With
+                       this option set, extended tables will not be used even
+                       on hardware which claims to support them.
 
        intel_idle.max_cstate=  [KNL,HW,ACPI,X86]
                        0       disables intel_idle and fall back on acpi_idle.
@@ -2992,11 +3008,34 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
                        Set maximum number of finished RCU callbacks to
                        process in one batch.
 
+       rcutree.dump_tree=      [KNL]
+                       Dump the structure of the rcu_node combining tree
+                       out at early boot.  This is used for diagnostic
+                       purposes, to verify correct tree setup.
+
+       rcutree.gp_cleanup_delay=       [KNL]
+                       Set the number of jiffies to delay each step of
+                       RCU grace-period cleanup.  This only has effect
+                       when CONFIG_RCU_TORTURE_TEST_SLOW_CLEANUP is set.
+
        rcutree.gp_init_delay=  [KNL]
                        Set the number of jiffies to delay each step of
                        RCU grace-period initialization.  This only has
-                       effect when CONFIG_RCU_TORTURE_TEST_SLOW_INIT is
-                       set.
+                       effect when CONFIG_RCU_TORTURE_TEST_SLOW_INIT
+                       is set.
+
+       rcutree.gp_preinit_delay=       [KNL]
+                       Set the number of jiffies to delay each step of
+                       RCU grace-period pre-initialization, that is,
+                       the propagation of recent CPU-hotplug changes up
+                       the rcu_node combining tree.  This only has effect
+                       when CONFIG_RCU_TORTURE_TEST_SLOW_PREINIT is set.
+
+       rcutree.rcu_fanout_exact= [KNL]
+                       Disable autobalancing of the rcu_node combining
+                       tree.  This is used by rcutorture, and might
+                       possibly be useful for architectures having high
+                       cache-to-cache transfer latencies.
 
        rcutree.rcu_fanout_leaf= [KNL]
                        Increase the number of CPUs assigned to each
@@ -3101,7 +3140,11 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
                        test, hence the "fake".
 
        rcutorture.nreaders= [KNL]
-                       Set number of RCU readers.
+                       Set number of RCU readers.  The value -1 selects
+                       N-1, where N is the number of CPUs.  A value
+                       "n" less than -1 selects N-n-2, where N is again
+                       the number of CPUs.  For example, -2 selects N
+                       (the number of CPUs), -3 selects N+1, and so on.
 
        rcutorture.object_debug= [KNL]
                        Enable debug-object double-call_rcu() testing.
index f95746189b5ded41d9ae8bdbbcafec4e1da8820e..13feb697271f0a270334dd3807255ed14af7d7ed 100644 (file)
@@ -617,16 +617,16 @@ case what's actually required is:
 However, stores are not speculated.  This means that ordering -is- provided
 for load-store control dependencies, as in the following example:
 
-       q = ACCESS_ONCE(a);
+       q = READ_ONCE_CTRL(a);
        if (q) {
                ACCESS_ONCE(b) = p;
        }
 
-Control dependencies pair normally with other types of barriers.
-That said, please note that ACCESS_ONCE() is not optional!  Without the
-ACCESS_ONCE(), might combine the load from 'a' with other loads from
-'a', and the store to 'b' with other stores to 'b', with possible highly
-counterintuitive effects on ordering.
+Control dependencies pair normally with other types of barriers.  That
+said, please note that READ_ONCE_CTRL() is not optional!  Without the
+READ_ONCE_CTRL(), the compiler might combine the load from 'a' with
+other loads from 'a', and the store to 'b' with other stores to 'b',
+with possible highly counterintuitive effects on ordering.
 
 Worse yet, if the compiler is able to prove (say) that the value of
 variable 'a' is always non-zero, it would be well within its rights
@@ -636,12 +636,15 @@ as follows:
        q = a;
        b = p;  /* BUG: Compiler and CPU can both reorder!!! */
 
-So don't leave out the ACCESS_ONCE().
+Finally, the READ_ONCE_CTRL() includes an smp_read_barrier_depends()
+that DEC Alpha needs in order to respect control depedencies.
+
+So don't leave out the READ_ONCE_CTRL().
 
 It is tempting to try to enforce ordering on identical stores on both
 branches of the "if" statement as follows:
 
-       q = ACCESS_ONCE(a);
+       q = READ_ONCE_CTRL(a);
        if (q) {
                barrier();
                ACCESS_ONCE(b) = p;
@@ -655,7 +658,7 @@ branches of the "if" statement as follows:
 Unfortunately, current compilers will transform this as follows at high
 optimization levels:
 
-       q = ACCESS_ONCE(a);
+       q = READ_ONCE_CTRL(a);
        barrier();
        ACCESS_ONCE(b) = p;  /* BUG: No ordering vs. load from a!!! */
        if (q) {
@@ -685,7 +688,7 @@ memory barriers, for example, smp_store_release():
 In contrast, without explicit memory barriers, two-legged-if control
 ordering is guaranteed only when the stores differ, for example:
 
-       q = ACCESS_ONCE(a);
+       q = READ_ONCE_CTRL(a);
        if (q) {
                ACCESS_ONCE(b) = p;
                do_something();
@@ -694,14 +697,14 @@ ordering is guaranteed only when the stores differ, for example:
                do_something_else();
        }
 
-The initial ACCESS_ONCE() is still required to prevent the compiler from
-proving the value of 'a'.
+The initial READ_ONCE_CTRL() is still required to prevent the compiler
+from proving the value of 'a'.
 
 In addition, you need to be careful what you do with the local variable 'q',
 otherwise the compiler might be able to guess the value and again remove
 the needed conditional.  For example:
 
-       q = ACCESS_ONCE(a);
+       q = READ_ONCE_CTRL(a);
        if (q % MAX) {
                ACCESS_ONCE(b) = p;
                do_something();
@@ -714,7 +717,7 @@ If MAX is defined to be 1, then the compiler knows that (q % MAX) is
 equal to zero, in which case the compiler is within its rights to
 transform the above code into the following:
 
-       q = ACCESS_ONCE(a);
+       q = READ_ONCE_CTRL(a);
        ACCESS_ONCE(b) = p;
        do_something_else();
 
@@ -725,7 +728,7 @@ is gone, and the barrier won't bring it back.  Therefore, if you are
 relying on this ordering, you should make sure that MAX is greater than
 one, perhaps as follows:
 
-       q = ACCESS_ONCE(a);
+       q = READ_ONCE_CTRL(a);
        BUILD_BUG_ON(MAX <= 1); /* Order load from a with store to b. */
        if (q % MAX) {
                ACCESS_ONCE(b) = p;
@@ -742,14 +745,15 @@ of the 'if' statement.
 You must also be careful not to rely too much on boolean short-circuit
 evaluation.  Consider this example:
 
-       q = ACCESS_ONCE(a);
+       q = READ_ONCE_CTRL(a);
        if (a || 1 > 0)
                ACCESS_ONCE(b) = 1;
 
-Because the second condition is always true, the compiler can transform
-this example as following, defeating control dependency:
+Because the first condition cannot fault and the second condition is
+always true, the compiler can transform this example as following,
+defeating control dependency:
 
-       q = ACCESS_ONCE(a);
+       q = READ_ONCE_CTRL(a);
        ACCESS_ONCE(b) = 1;
 
 This example underscores the need to ensure that the compiler cannot
@@ -762,8 +766,8 @@ demonstrated by two related examples, with the initial values of
 x and y both being zero:
 
        CPU 0                     CPU 1
-       =====================     =====================
-       r1 = ACCESS_ONCE(x);      r2 = ACCESS_ONCE(y);
+       =======================   =======================
+       r1 = READ_ONCE_CTRL(x);   r2 = READ_ONCE_CTRL(y);
        if (r1 > 0)               if (r2 > 0)
          ACCESS_ONCE(y) = 1;       ACCESS_ONCE(x) = 1;
 
@@ -783,7 +787,8 @@ But because control dependencies do -not- provide transitivity, the above
 assertion can fail after the combined three-CPU example completes.  If you
 need the three-CPU example to provide ordering, you will need smp_mb()
 between the loads and stores in the CPU 0 and CPU 1 code fragments,
-that is, just before or just after the "if" statements.
+that is, just before or just after the "if" statements.  Furthermore,
+the original two-CPU example is very fragile and should be avoided.
 
 These two examples are the LB and WWC litmus tests from this paper:
 http://www.cl.cam.ac.uk/users/pes20/ppc-supplemental/test6.pdf and this
@@ -791,6 +796,12 @@ site: https://www.cl.cam.ac.uk/~pes20/ppcmem/index.html.
 
 In summary:
 
+  (*) Control dependencies must be headed by READ_ONCE_CTRL().
+      Or, as a much less preferable alternative, interpose
+      be headed by READ_ONCE() or an ACCESS_ONCE() read and must
+      have smp_read_barrier_depends() between this read and the
+      control-dependent write.
+
   (*) Control dependencies can order prior loads against later stores.
       However, they do -not- guarantee any other sort of ordering:
       Not prior loads against later loads, nor prior stores against
@@ -1662,7 +1673,7 @@ CPU from reordering them.
 
 There are some more advanced barrier functions:
 
- (*) set_mb(var, value)
+ (*) smp_store_mb(var, value)
 
      This assigns the value to the variable and then inserts a full memory
      barrier after it, depending on the function.  It isn't guaranteed to
@@ -1784,10 +1795,9 @@ for each construct.  These operations all imply certain barriers:
 
      Memory operations issued before the ACQUIRE may be completed after
      the ACQUIRE operation has completed.  An smp_mb__before_spinlock(),
-     combined with a following ACQUIRE, orders prior loads against
-     subsequent loads and stores and also orders prior stores against
-     subsequent stores.  Note that this is weaker than smp_mb()!  The
-     smp_mb__before_spinlock() primitive is free on many architectures.
+     combined with a following ACQUIRE, orders prior stores against
+     subsequent loads and stores. Note that this is weaker than smp_mb()!
+     The smp_mb__before_spinlock() primitive is free on many architectures.
 
  (2) RELEASE operation implication:
 
@@ -1975,7 +1985,7 @@ after it has altered the task state:
        CPU 1
        ===============================
        set_current_state();
-         set_mb();
+         smp_store_mb();
            STORE current->state
            <general barrier>
        LOAD event_indicated
@@ -2016,7 +2026,7 @@ between the STORE to indicate the event and the STORE to set TASK_RUNNING:
        CPU 1                           CPU 2
        =============================== ===============================
        set_current_state();            STORE event_indicated
-         set_mb();                     wake_up();
+         smp_store_mb();               wake_up();
            STORE current->state          <write barrier>
            <general barrier>             STORE current->state
        LOAD event_indicated
index d727a38291005f962848ed40a1ab11db4c167899..53a726855e49bfa4c313e46e15df1eec7cb610ae 100644 (file)
@@ -20,7 +20,7 @@
        files/UDP-Lite-HOWTO.txt
 
    o The Wireshark UDP-Lite WiKi (with capture files):
-       http://wiki.wireshark.org/Lightweight_User_Datagram_Protocol
+       https://wiki.wireshark.org/Lightweight_User_Datagram_Protocol
 
    o The Protocol Spec, RFC 3828, http://www.ietf.org/rfc/rfc3828.txt
 
index 44fe1d28a16327d0cb04771f05a4df4f7f175e0d..e76dc0ad4d2b7393c0f6e55d51c2eab5686d6ed1 100644 (file)
@@ -556,6 +556,12 @@ helper functions described in Section 4.  In that case, pm_runtime_resume()
 should be used.  Of course, for this purpose the device's runtime PM has to be
 enabled earlier by calling pm_runtime_enable().
 
+Note, if the device may execute pm_runtime calls during the probe (such as
+if it is registers with a subsystem that may call back in) then the
+pm_runtime_get_sync() call paired with a pm_runtime_put() call will be
+appropriate to ensure that the device is not put back to sleep during the
+probe. This can happen with systems such as the network device layer.
+
 It may be desirable to suspend the device once ->probe() has finished.
 Therefore the driver core uses the asyncronous pm_request_idle() to submit a
 request to execute the subsystem-level idle callback for the device at that
index 6fd0e8bb814097233280897b9916190a429eda22..9dc845cf7d88c77eec874cf38ee69231b92eefae 100644 (file)
@@ -30,3 +30,5 @@ ptrace.txt
        - Information on the ptrace interfaces for hardware debug registers.
 transactional_memory.txt
        - Overview of the Power8 transactional memory support.
+dscr.txt
+       - Overview DSCR (Data Stream Control Register) support.
index 2c71ecc519d953bbbd122ca05a7ea2fe5496ab66..2a230d01cd8ce30265c4135bfd461afccbdaf18d 100644 (file)
@@ -133,6 +133,9 @@ User API
     The following file operations are supported on both slave and
     master devices.
 
+    A userspace library libcxl is avaliable here:
+       https://github.com/ibm-capi/libcxl
+    This provides a C interface to this kernel API.
 
 open
 ----
@@ -366,6 +369,7 @@ Sysfs Class
     enumeration and tuning of the accelerators. Its layout is
     described in Documentation/ABI/testing/sysfs-class-cxl
 
+
 Udev rules
 ==========
 
diff --git a/Documentation/powerpc/dscr.txt b/Documentation/powerpc/dscr.txt
new file mode 100644 (file)
index 0000000..1ff4400
--- /dev/null
@@ -0,0 +1,83 @@
+                       DSCR (Data Stream Control Register)
+               ================================================
+
+DSCR register in powerpc allows user to have some control of prefetch of data
+stream in the processor. Please refer to the ISA documents or related manual
+for more detailed information regarding how to use this DSCR to attain this
+control of the pefetches . This document here provides an overview of kernel
+support for DSCR, related kernel objects, it's functionalities and exported
+user interface.
+
+(A) Data Structures:
+
+       (1) thread_struct:
+               dscr            /* Thread DSCR value */
+               dscr_inherit    /* Thread has changed default DSCR */
+
+       (2) PACA:
+               dscr_default    /* per-CPU DSCR default value */
+
+       (3) sysfs.c:
+               dscr_default    /* System DSCR default value */
+
+(B) Scheduler Changes:
+
+       Scheduler will write the per-CPU DSCR default which is stored in the
+       CPU's PACA value into the register if the thread has dscr_inherit value
+       cleared which means that it has not changed the default DSCR till now.
+       If the dscr_inherit value is set which means that it has changed the
+       default DSCR value, scheduler will write the changed value which will
+       now be contained in thread struct's dscr into the register instead of
+       the per-CPU default PACA based DSCR value.
+
+       NOTE: Please note here that the system wide global DSCR value never
+       gets used directly in the scheduler process context switch at all.
+
+(C) SYSFS Interface:
+
+       Global DSCR default:            /sys/devices/system/cpu/dscr_default
+       CPU specific DSCR default:      /sys/devices/system/cpu/cpuN/dscr
+
+       Changing the global DSCR default in the sysfs will change all the CPU
+       specific DSCR defaults immediately in their PACA structures. Again if
+       the current process has the dscr_inherit clear, it also writes the new
+       value into every CPU's DSCR register right away and updates the current
+       thread's DSCR value as well.
+
+       Changing the CPU specif DSCR default value in the sysfs does exactly
+       the same thing as above but unlike the global one above, it just changes
+       stuff for that particular CPU instead for all the CPUs on the system.
+
+(D) User Space Instructions:
+
+       The DSCR register can be accessed in the user space using any of these
+       two SPR numbers available for that purpose.
+
+       (1) Problem state SPR:          0x03    (Un-privileged, POWER8 only)
+       (2) Privileged state SPR:       0x11    (Privileged)
+
+       Accessing DSCR through privileged SPR number (0x11) from user space
+       works, as it is emulated following an illegal instruction exception
+       inside the kernel. Both mfspr and mtspr instructions are emulated.
+
+       Accessing DSCR through user level SPR (0x03) from user space will first
+       create a facility unavailable exception. Inside this exception handler
+       all mfspr isntruction based read attempts will get emulated and returned
+       where as the first mtspr instruction based write attempts will enable
+       the DSCR facility for the next time around (both for read and write) by
+       setting DSCR facility in the FSCR register.
+
+(E) Specifics about 'dscr_inherit':
+
+       The thread struct element 'dscr_inherit' represents whether the thread
+       in question has attempted and changed the DSCR itself using any of the
+       following methods. This element signifies whether the thread wants to
+       use the CPU default DSCR value or its own changed DSCR value in the
+       kernel.
+
+               (1) mtspr instruction   (SPR number 0x03)
+               (2) mtspr instruction   (SPR number 0x11)
+               (3) ptrace interface    (Explicitly set user DSCR value)
+
+       Any child of the process created after this event in the process inherits
+       this same behaviour as well.
index ded69794a5c09da762db7c4c61cc3d23e619d0fa..ba0a2a4a54ba1ffcb484786381b91f5113a62ad6 100644 (file)
@@ -74,22 +74,23 @@ Causes of transaction aborts
 Syscalls
 ========
 
-Performing syscalls from within transaction is not recommended, and can lead
-to unpredictable results.
+Syscalls made from within an active transaction will not be performed and the
+transaction will be doomed by the kernel with the failure code TM_CAUSE_SYSCALL
+| TM_CAUSE_PERSISTENT.
 
-Syscalls do not by design abort transactions, but beware: The kernel code will
-not be running in transactional state.  The effect of syscalls will always
-remain visible, but depending on the call they may abort your transaction as a
-side-effect, read soon-to-be-aborted transactional data that should not remain
-invisible, etc.  If you constantly retry a transaction that constantly aborts
-itself by calling a syscall, you'll have a livelock & make no progress.
+Syscalls made from within a suspended transaction are performed as normal and
+the transaction is not explicitly doomed by the kernel.  However, what the
+kernel does to perform the syscall may result in the transaction being doomed
+by the hardware.  The syscall is performed in suspended mode so any side
+effects will be persistent, independent of transaction success or failure.  No
+guarantees are provided by the kernel about which syscalls will affect
+transaction success.
 
-Simple syscalls (e.g. sigprocmask()) "could" be OK.  Even things like write()
-from, say, printf() should be OK as long as the kernel does not access any
-memory that was accessed transactionally.
-
-Consider any syscalls that happen to work as debug-only -- not recommended for
-production use.  Best to queue them up till after the transaction is over.
+Care must be taken when relying on syscalls to abort during active transactions
+if the calls are made via a library.  Libraries may cache values (which may
+give the appearance of success) or perform operations that cause transaction
+failure before entering the kernel (which may produce different failure codes).
+Examples are glibc's getpid() and lazy symbol resolution.
 
 
 Signals
@@ -176,8 +177,7 @@ kernel aborted a transaction:
  TM_CAUSE_RESCHED       Thread was rescheduled.
  TM_CAUSE_TLBI          Software TLB invalid.
  TM_CAUSE_FAC_UNAV      FP/VEC/VSX unavailable trap.
- TM_CAUSE_SYSCALL       Currently unused; future syscalls that must abort
-                        transactions for consistency will use this.
+ TM_CAUSE_SYSCALL       Syscall from active transaction.
  TM_CAUSE_SIGNAL        Signal delivered.
  TM_CAUSE_MISC          Currently unused.
  TM_CAUSE_ALIGNMENT     Alignment fault.
index 57883ca2498bb5ce818139a73755518e78150df9..e89ce6624af2fab481a708ad1a0e4e20d1bc0c1c 100644 (file)
@@ -48,7 +48,7 @@ preemption must be disabled around such regions.
 
 Note, some FPU functions are already explicitly preempt safe.  For example,
 kernel_fpu_begin and kernel_fpu_end will disable and enable preemption.
-However, math_state_restore must be called with preemption disabled.
+However, fpu__restore() must be called with preemption disabled.
 
 
 RULE #3: Lock acquire and release must be performed by same task
index 0d5bdb153d3b32582a7a3a6338e7c2e1d789d259..f29fa550665af92dba9248113de5be639c10c885 100644 (file)
@@ -151,6 +151,65 @@ A link named 'tape' is made from the SCSI device directory to the class
 directory corresponding to the mode 0 auto-rewind device (e.g., st0). 
 
 
+SYSFS AND STATISTICS FOR TAPE DEVICES
+
+The st driver maintains statistics for tape drives inside the sysfs filesystem.
+The following method can be used to locate the statistics that are
+available (assuming that sysfs is mounted at /sys):
+
+1. Use opendir(3) on the directory /sys/class/scsi_tape
+2. Use readdir(3) to read the directory contents
+3. Use regcomp(3)/regexec(3) to match directory entries to the extended
+        regular expression "^st[0-9]+$"
+4. Access the statistics from the /sys/class/scsi_tape/<match>/stats
+        directory (where <match> is a directory entry from /sys/class/scsi_tape
+        that matched the extended regular expression)
+
+The reason for using this approach is that all the character devices
+pointing to the same tape drive use the same statistics. That means
+that st0 would have the same statistics as nst0.
+
+The directory contains the following statistics files:
+
+1.  in_flight - The number of I/Os currently outstanding to this device.
+2.  io_ns - The amount of time spent waiting (in nanoseconds) for all I/O
+        to complete (including read and write). This includes tape movement
+        commands such as seeking between file or set marks and implicit tape
+        movement such as when rewind on close tape devices are used.
+3.  other_cnt - The number of I/Os issued to the tape drive other than read or
+        write commands. The time taken to complete these commands uses the
+        following calculation io_ms-read_ms-write_ms.
+4.  read_byte_cnt - The number of bytes read from the tape drive.
+5.  read_cnt - The number of read requests issued to the tape drive.
+6.  read_ns - The amount of time (in nanoseconds) spent waiting for read
+        requests to complete.
+7.  write_byte_cnt - The number of bytes written to the tape drive.
+8.  write_cnt - The number of write requests issued to the tape drive.
+9.  write_ns - The amount of time (in nanoseconds) spent waiting for write
+        requests to complete.
+10. resid_cnt - The number of times during a read or write we found
+       the residual amount to be non-zero. This should mean that a program
+       is issuing a read larger thean the block size on tape. For write
+       not all data made it to tape.
+
+Note: The in_flight value is incremented when an I/O starts the I/O
+itself is not added to the statistics until it completes.
+
+The total of read_cnt, write_cnt, and other_cnt may not total to the same
+value as iodone_cnt at the device level. The tape statistics only count
+I/O issued via the st module.
+
+When read the statistics may not be temporally consistent while I/O is in
+progress. The individual values are read and written to atomically however
+when reading them back via sysfs they may be in the process of being
+updated when starting an I/O or when it is completed.
+
+The value shown in in_flight is incremented before any statstics are
+updated and decremented when an I/O completes after updating statistics.
+The value of in_flight is 0 when there are no I/Os outstanding that are
+issued by the st driver. Tape statistics do not take into account any
+I/O performed via the sg device.
+
 BSD AND SYS V SEMANTICS
 
 The user can choose between these two behaviours of the tape driver by
index 2ba71cea01723cf3216ef4e95708789b6e0cc9f2..6085e1f19c9d59fa49b5a3d4d369da6426ae526e 100755 (executable)
@@ -503,11 +503,8 @@ def tcm_mod_dump_fabric_ops(proto_ident, fabric_mod_dir_var, fabric_mod_name):
        buf += "#include <linux/string.h>\n"
        buf += "#include <linux/ctype.h>\n"
        buf += "#include <asm/unaligned.h>\n"
-       buf += "#include <scsi/scsi.h>\n"
-       buf += "#include <scsi/scsi_host.h>\n"
-       buf += "#include <scsi/scsi_device.h>\n"
-       buf += "#include <scsi/scsi_cmnd.h>\n"
-       buf += "#include <scsi/libfc.h>\n\n"
+       buf += "#include <scsi/scsi_common.h>\n"
+       buf += "#include <scsi/scsi_proto.h>\n"
        buf += "#include <target/target_core_base.h>\n"
        buf += "#include <target/target_core_fabric.h>\n"
        buf += "#include <target/target_core_configfs.h>\n\n"
index 96978eced34154187acdd328fe403a14ca6200f7..1dd3fddfd3a1e536de39b69c37168dfc35553a4a 100644 (file)
@@ -289,10 +289,12 @@ PPC64 sPAPR implementation note
 
 This implementation has some specifics:
 
-1) Only one IOMMU group per container is supported as an IOMMU group
-represents the minimal entity which isolation can be guaranteed for and
-groups are allocated statically, one per a Partitionable Endpoint (PE)
+1) On older systems (POWER7 with P5IOC2/IODA1) only one IOMMU group per
+container is supported as an IOMMU table is allocated at the boot time,
+one table per a IOMMU group which is a Partitionable Endpoint (PE)
 (PE is often a PCI domain but not always).
+Newer systems (POWER8 with IODA2) have improved hardware design which allows
+to remove this limitation and have multiple IOMMU groups per a VFIO container.
 
 2) The hardware supports so called DMA windows - the PCI address range
 within which DMA transfer is allowed, any attempt to access address space
@@ -385,6 +387,18 @@ The code flow from the example above should be slightly changed:
 
        ....
 
+       /* Inject EEH error, which is expected to be caused by 32-bits
+        * config load.
+        */
+       pe_op.op = VFIO_EEH_PE_INJECT_ERR;
+       pe_op.err.type = EEH_ERR_TYPE_32;
+       pe_op.err.func = EEH_ERR_FUNC_LD_CFG_ADDR;
+       pe_op.err.addr = 0ul;
+       pe_op.err.mask = 0ul;
+       ioctl(container, VFIO_EEH_PE_OP, &pe_op);
+
+       ....
+
        /* When 0xFF's returned from reading PCI config space or IO BARs
         * of the PCI device. Check the PE's state to see if that has been
         * frozen.
@@ -427,6 +441,48 @@ The code flow from the example above should be slightly changed:
 
        ....
 
+5) There is v2 of SPAPR TCE IOMMU. It deprecates VFIO_IOMMU_ENABLE/
+VFIO_IOMMU_DISABLE and implements 2 new ioctls:
+VFIO_IOMMU_SPAPR_REGISTER_MEMORY and VFIO_IOMMU_SPAPR_UNREGISTER_MEMORY
+(which are unsupported in v1 IOMMU).
+
+PPC64 paravirtualized guests generate a lot of map/unmap requests,
+and the handling of those includes pinning/unpinning pages and updating
+mm::locked_vm counter to make sure we do not exceed the rlimit.
+The v2 IOMMU splits accounting and pinning into separate operations:
+
+- VFIO_IOMMU_SPAPR_REGISTER_MEMORY/VFIO_IOMMU_SPAPR_UNREGISTER_MEMORY ioctls
+receive a user space address and size of the block to be pinned.
+Bisecting is not supported and VFIO_IOMMU_UNREGISTER_MEMORY is expected to
+be called with the exact address and size used for registering
+the memory block. The userspace is not expected to call these often.
+The ranges are stored in a linked list in a VFIO container.
+
+- VFIO_IOMMU_MAP_DMA/VFIO_IOMMU_UNMAP_DMA ioctls only update the actual
+IOMMU table and do not do pinning; instead these check that the userspace
+address is from pre-registered range.
+
+This separation helps in optimizing DMA for guests.
+
+6) sPAPR specification allows guests to have an additional DMA window(s) on
+a PCI bus with a variable page size. Two ioctls have been added to support
+this: VFIO_IOMMU_SPAPR_TCE_CREATE and VFIO_IOMMU_SPAPR_TCE_REMOVE.
+The platform has to support the functionality or error will be returned to
+the userspace. The existing hardware supports up to 2 DMA windows, one is
+2GB long, uses 4K pages and called "default 32bit window"; the other can
+be as big as entire RAM, use different page size, it is optional - guests
+create those in run-time if the guest driver supports 64bit DMA.
+
+VFIO_IOMMU_SPAPR_TCE_CREATE receives a page shift, a DMA window size and
+a number of TCE table levels (if a TCE table is going to be big enough and
+the kernel may not be able to allocate enough of physically contiguous memory).
+It creates a new window in the available slot and returns the bus address where
+the new window starts. Due to hardware limitation, the user space cannot choose
+the location of DMA windows.
+
+VFIO_IOMMU_SPAPR_TCE_REMOVE receives the bus start address of the window
+and removes it.
+
 -------------------------------------------------------------------------------
 
 [1] VFIO was originally an acronym for "Virtual Function I/O" in its
index 9fa2bf8c3f6f13282c92c33671b5ad9a3eaa1d0a..a7926a90156f64172897ca5a9ae562f7975701a7 100644 (file)
@@ -254,6 +254,11 @@ since the last call to this ioctl.  Bit 0 is the first page in the
 memory slot.  Ensure the entire structure is cleared to avoid padding
 issues.
 
+If KVM_CAP_MULTI_ADDRESS_SPACE is available, bits 16-31 specifies
+the address space for which you want to return the dirty bitmap.
+They must be less than the value that KVM_CHECK_EXTENSION returns for
+the KVM_CAP_MULTI_ADDRESS_SPACE capability.
+
 
 4.9 KVM_SET_MEMORY_ALIAS
 
@@ -820,11 +825,21 @@ struct kvm_vcpu_events {
        } nmi;
        __u32 sipi_vector;
        __u32 flags;
+       struct {
+               __u8 smm;
+               __u8 pending;
+               __u8 smm_inside_nmi;
+               __u8 latched_init;
+       } smi;
 };
 
-KVM_VCPUEVENT_VALID_SHADOW may be set in the flags field to signal that
-interrupt.shadow contains a valid state. Otherwise, this field is undefined.
+Only two fields are defined in the flags field:
+
+- KVM_VCPUEVENT_VALID_SHADOW may be set in the flags field to signal that
+  interrupt.shadow contains a valid state.
 
+- KVM_VCPUEVENT_VALID_SMM may be set in the flags field to signal that
+  smi contains a valid state.
 
 4.32 KVM_SET_VCPU_EVENTS
 
@@ -841,17 +856,20 @@ vcpu.
 See KVM_GET_VCPU_EVENTS for the data structure.
 
 Fields that may be modified asynchronously by running VCPUs can be excluded
-from the update. These fields are nmi.pending and sipi_vector. Keep the
-corresponding bits in the flags field cleared to suppress overwriting the
-current in-kernel state. The bits are:
+from the update. These fields are nmi.pending, sipi_vector, smi.smm,
+smi.pending. Keep the corresponding bits in the flags field cleared to
+suppress overwriting the current in-kernel state. The bits are:
 
 KVM_VCPUEVENT_VALID_NMI_PENDING - transfer nmi.pending to the kernel
 KVM_VCPUEVENT_VALID_SIPI_VECTOR - transfer sipi_vector
+KVM_VCPUEVENT_VALID_SMM         - transfer the smi sub-struct.
 
 If KVM_CAP_INTR_SHADOW is available, KVM_VCPUEVENT_VALID_SHADOW can be set in
 the flags field to signal that interrupt.shadow contains a valid state and
 shall be written into the VCPU.
 
+KVM_VCPUEVENT_VALID_SMM can only be set if KVM_CAP_X86_SMM is available.
+
 
 4.33 KVM_GET_DEBUGREGS
 
@@ -911,6 +929,13 @@ slot.  When changing an existing slot, it may be moved in the guest
 physical memory space, or its flags may be modified.  It may not be
 resized.  Slots may not overlap in guest physical address space.
 
+If KVM_CAP_MULTI_ADDRESS_SPACE is available, bits 16-31 of "slot"
+specifies the address space which is being modified.  They must be
+less than the value that KVM_CHECK_EXTENSION returns for the
+KVM_CAP_MULTI_ADDRESS_SPACE capability.  Slots in separate address spaces
+are unrelated; the restriction on overlapping slots only applies within
+each address space.
+
 Memory for the region is taken starting at the address denoted by the
 field userspace_addr, which must point at user addressable memory for
 the entire memory slot size.  Any object may back this memory, including
@@ -959,7 +984,8 @@ documentation when it pops into existence).
 4.37 KVM_ENABLE_CAP
 
 Capability: KVM_CAP_ENABLE_CAP, KVM_CAP_ENABLE_CAP_VM
-Architectures: ppc, s390
+Architectures: x86 (only KVM_CAP_ENABLE_CAP_VM),
+              mips (only KVM_CAP_ENABLE_CAP), ppc, s390
 Type: vcpu ioctl, vm ioctl (with KVM_CAP_ENABLE_CAP_VM)
 Parameters: struct kvm_enable_cap (in)
 Returns: 0 on success; -1 on error
@@ -1268,7 +1294,7 @@ The flags bitmap is defined as:
    /* the host supports the ePAPR idle hcall
    #define KVM_PPC_PVINFO_FLAGS_EV_IDLE   (1<<0)
 
-4.48 KVM_ASSIGN_PCI_DEVICE
+4.48 KVM_ASSIGN_PCI_DEVICE (deprecated)
 
 Capability: none
 Architectures: x86
@@ -1318,7 +1344,7 @@ Errors:
   have their standard meanings.
 
 
-4.49 KVM_DEASSIGN_PCI_DEVICE
+4.49 KVM_DEASSIGN_PCI_DEVICE (deprecated)
 
 Capability: none
 Architectures: x86
@@ -1337,7 +1363,7 @@ Errors:
   Other error conditions may be defined by individual device types or
   have their standard meanings.
 
-4.50 KVM_ASSIGN_DEV_IRQ
+4.50 KVM_ASSIGN_DEV_IRQ (deprecated)
 
 Capability: KVM_CAP_ASSIGN_DEV_IRQ
 Architectures: x86
@@ -1377,7 +1403,7 @@ Errors:
   have their standard meanings.
 
 
-4.51 KVM_DEASSIGN_DEV_IRQ
+4.51 KVM_DEASSIGN_DEV_IRQ (deprecated)
 
 Capability: KVM_CAP_ASSIGN_DEV_IRQ
 Architectures: x86
@@ -1451,7 +1477,7 @@ struct kvm_irq_routing_s390_adapter {
 };
 
 
-4.53 KVM_ASSIGN_SET_MSIX_NR
+4.53 KVM_ASSIGN_SET_MSIX_NR (deprecated)
 
 Capability: none
 Architectures: x86
@@ -1473,7 +1499,7 @@ struct kvm_assigned_msix_nr {
 #define KVM_MAX_MSIX_PER_DEV           256
 
 
-4.54 KVM_ASSIGN_SET_MSIX_ENTRY
+4.54 KVM_ASSIGN_SET_MSIX_ENTRY (deprecated)
 
 Capability: none
 Architectures: x86
@@ -1629,7 +1655,7 @@ should skip processing the bitmap and just invalidate everything.  It must
 be set to the number of set bits in the bitmap.
 
 
-4.61 KVM_ASSIGN_SET_INTX_MASK
+4.61 KVM_ASSIGN_SET_INTX_MASK (deprecated)
 
 Capability: KVM_CAP_PCI_2_3
 Architectures: x86
@@ -2978,6 +3004,16 @@ len must be a multiple of sizeof(struct kvm_s390_irq). It must be > 0
 and it must not exceed (max_vcpus + 32) * sizeof(struct kvm_s390_irq),
 which is the maximum number of possibly pending cpu-local interrupts.
 
+4.90 KVM_SMI
+
+Capability: KVM_CAP_X86_SMM
+Architectures: x86
+Type: vcpu ioctl
+Parameters: none
+Returns: 0 on success, -1 on error
+
+Queues an SMI on the thread's vcpu.
+
 5. The kvm_run structure
 ------------------------
 
@@ -3013,7 +3049,12 @@ an interrupt can be injected now with KVM_INTERRUPT.
 The value of the current interrupt flag.  Only valid if in-kernel
 local APIC is not used.
 
-       __u8 padding2[2];
+       __u16 flags;
+
+More architecture-specific flags detailing state of the VCPU that may
+affect the device's behavior.  The only currently defined flag is
+KVM_RUN_X86_SMM, which is valid on x86 machines and is set if the
+VCPU is in system management mode.
 
        /* in (pre_kvm_run), out (post_kvm_run) */
        __u64 cr8;
index c59bd9bc41efa984cfd5f0e17ac5fd6156acfca6..3a4d681c3e981863ce2b30e8919cb16b9004d962 100644 (file)
@@ -173,6 +173,12 @@ Shadow pages contain the following information:
     Contains the value of cr4.smap && !cr0.wp for which the page is valid
     (pages for which this is true are different from other pages; see the
     treatment of cr0.wp=0 below).
+  role.smm:
+    Is 1 if the page is valid in system management mode.  This field
+    determines which of the kvm_memslots array was used to build this
+    shadow page; it is also used to go back from a struct kvm_mmu_page
+    to a memslot, through the kvm_memslots_for_spte_role macro and
+    __gfn_to_memslot.
   gfn:
     Either the guest page table containing the translations shadowed by this
     page, or the base page frame for linear translations.  See role.direct.
index 88b85899d30953a6be096a9e045fcf54be3676b4..7c1f9fad667460ff143b867ca5ce5d629560e876 100644 (file)
@@ -1124,7 +1124,6 @@ The boot loader *must* fill out the following fields in bp,
 
     o hdr.code32_start
     o hdr.cmd_line_ptr
-    o hdr.cmdline_size
     o hdr.ramdisk_image (if applicable)
     o hdr.ramdisk_size  (if applicable)
 
index 9132b86176a3899b6ad8bd7f4bd5b630dd6fa031..33884d15612599805f922f386474109ab44998ed 100644 (file)
@@ -18,10 +18,10 @@ Some of these entries are:
 
  - system_call: syscall instruction from 64-bit code.
 
- - ia32_syscall: int 0x80 from 32-bit or 64-bit code; compat syscall
+ - entry_INT80_compat: int 0x80 from 32-bit or 64-bit code; compat syscall
    either way.
 
- - ia32_syscall, ia32_sysenter: syscall and sysenter from 32-bit
+ - entry_INT80_compat, ia32_sysenter: syscall and sysenter from 32-bit
    code
 
  - interrupt: An array of entries.  Every IDT vector that doesn't
similarity index 68%
rename from Documentation/x86/x86_64/kernel-stacks
rename to Documentation/x86/kernel-stacks
index e3c8a49d1a2f5b51cee6c128c5a2aecd257409dd..0f3a6c20194326e9880bfea3c9ee78d0c8c89e65 100644 (file)
@@ -1,3 +1,6 @@
+Kernel stacks on x86-64 bit
+---------------------------
+
 Most of the text from Keith Owens, hacked by AK
 
 x86_64 page size (PAGE_SIZE) is 4K.
@@ -56,13 +59,6 @@ If that assumption is ever broken then the stacks will become corrupt.
 
 The currently assigned IST stacks are :-
 
-* STACKFAULT_STACK.  EXCEPTION_STKSZ (PAGE_SIZE).
-
-  Used for interrupt 12 - Stack Fault Exception (#SS).
-
-  This allows the CPU to recover from invalid stack segments. Rarely
-  happens.
-
 * DOUBLEFAULT_STACK.  EXCEPTION_STKSZ (PAGE_SIZE).
 
   Used for interrupt 8 - Double Fault Exception (#DF).
@@ -99,3 +95,47 @@ The currently assigned IST stacks are :-
   assumptions about the previous state of the kernel stack.
 
 For more details see the Intel IA32 or AMD AMD64 architecture manuals.
+
+
+Printing backtraces on x86
+--------------------------
+
+The question about the '?' preceding function names in an x86 stacktrace
+keeps popping up, here's an indepth explanation. It helps if the reader
+stares at print_context_stack() and the whole machinery in and around
+arch/x86/kernel/dumpstack.c.
+
+Adapted from Ingo's mail, Message-ID: <20150521101614.GA10889@gmail.com>:
+
+We always scan the full kernel stack for return addresses stored on
+the kernel stack(s) [*], from stack top to stack bottom, and print out
+anything that 'looks like' a kernel text address.
+
+If it fits into the frame pointer chain, we print it without a question
+mark, knowing that it's part of the real backtrace.
+
+If the address does not fit into our expected frame pointer chain we
+still print it, but we print a '?'. It can mean two things:
+
+ - either the address is not part of the call chain: it's just stale
+   values on the kernel stack, from earlier function calls. This is
+   the common case.
+
+ - or it is part of the call chain, but the frame pointer was not set
+   up properly within the function, so we don't recognize it.
+
+This way we will always print out the real call chain (plus a few more
+entries), regardless of whether the frame pointer was set up correctly
+or not - but in most cases we'll get the call chain right as well. The
+entries printed are strictly in stack order, so you can deduce more
+information from that as well.
+
+The most important property of this method is that we _never_ lose
+information: we always strive to print _all_ addresses on the stack(s)
+that look like kernel text addresses, so if debug information is wrong,
+we still print out the real call chain as well - just with more question
+marks than ideal.
+
+[*] For things like IRQ and IST stacks, we also scan those stacks, in
+    the right order, and try to cross from one stack into another
+    reconstructing the call chain. This works most of the time.
index cc071dc333c213676f2af659f318d7f836f2278a..860bc3adc223440df4f6a4aec7bc9fdf95e38c75 100644 (file)
@@ -1,7 +1,19 @@
 MTRR (Memory Type Range Register) control
-3 Jun 1999
-Richard Gooch
-<rgooch@atnf.csiro.au>
+
+Richard Gooch <rgooch@atnf.csiro.au> - 3 Jun 1999
+Luis R. Rodriguez <mcgrof@do-not-panic.com> - April 9, 2015
+
+===============================================================================
+Phasing out MTRR use
+
+MTRR use is replaced on modern x86 hardware with PAT. Over time the only type
+of effective MTRR that is expected to be supported will be for write-combining.
+As MTRR use is phased out device drivers should use arch_phys_wc_add() to make
+MTRR effective on non-PAT systems while a no-op on PAT enabled systems.
+
+For details refer to Documentation/x86/pat.txt.
+
+===============================================================================
 
   On Intel P6 family processors (Pentium Pro, Pentium II and later)
   the Memory Type Range Registers (MTRRs) may be used to control
index cf08c9fff3cdaa62b2217f8e4526d9a7cc88fbec..54944c71b819bd7b37aeb4d221ef02fffd201879 100644 (file)
@@ -12,7 +12,7 @@ virtual addresses.
 
 PAT allows for different types of memory attributes. The most commonly used
 ones that will be supported at this time are Write-back, Uncached,
-Write-combined and Uncached Minus.
+Write-combined, Write-through and Uncached Minus.
 
 
 PAT APIs
@@ -34,16 +34,23 @@ ioremap                |    --    |    UC-     |       UC-        |
                        |          |            |                  |
 ioremap_cache          |    --    |    WB      |       WB         |
                        |          |            |                  |
+ioremap_uc             |    --    |    UC      |       UC         |
+                       |          |            |                  |
 ioremap_nocache        |    --    |    UC-     |       UC-        |
                        |          |            |                  |
 ioremap_wc             |    --    |    --      |       WC         |
                        |          |            |                  |
+ioremap_wt             |    --    |    --      |       WT         |
+                       |          |            |                  |
 set_memory_uc          |    UC-   |    --      |       --         |
  set_memory_wb         |          |            |                  |
                        |          |            |                  |
 set_memory_wc          |    WC    |    --      |       --         |
  set_memory_wb         |          |            |                  |
                        |          |            |                  |
+set_memory_wt          |    WT    |    --      |       --         |
+ set_memory_wb         |          |            |                  |
+                       |          |            |                  |
 pci sysfs resource     |    --    |    --      |       UC-        |
                        |          |            |                  |
 pci sysfs resource_wc  |    --    |    --      |       WC         |
@@ -102,7 +109,38 @@ wants to export a RAM region, it has to do set_memory_uc() or set_memory_wc()
 as step 0 above and also track the usage of those pages and use set_memory_wb()
 before the page is freed to free pool.
 
-
+MTRR effects on PAT / non-PAT systems
+-------------------------------------
+
+The following table provides the effects of using write-combining MTRRs when
+using ioremap*() calls on x86 for both non-PAT and PAT systems. Ideally
+mtrr_add() usage will be phased out in favor of arch_phys_wc_add() which will
+be a no-op on PAT enabled systems. The region over which a arch_phys_wc_add()
+is made, should already have been ioremapped with WC attributes or PAT entries,
+this can be done by using ioremap_wc() / set_memory_wc().  Devices which
+combine areas of IO memory desired to remain uncacheable with areas where
+write-combining is desirable should consider use of ioremap_uc() followed by
+set_memory_wc() to white-list effective write-combined areas.  Such use is
+nevertheless discouraged as the effective memory type is considered
+implementation defined, yet this strategy can be used as last resort on devices
+with size-constrained regions where otherwise MTRR write-combining would
+otherwise not be effective.
+
+----------------------------------------------------------------------
+MTRR Non-PAT   PAT    Linux ioremap value        Effective memory type
+----------------------------------------------------------------------
+                                                  Non-PAT |  PAT
+     PAT
+     |PCD
+     ||PWT
+     |||
+WC   000      WB      _PAGE_CACHE_MODE_WB            WC   |   WC
+WC   001      WC      _PAGE_CACHE_MODE_WC            WC*  |   WC
+WC   010      UC-     _PAGE_CACHE_MODE_UC_MINUS      WC*  |   UC
+WC   011      UC      _PAGE_CACHE_MODE_UC            UC   |   UC
+----------------------------------------------------------------------
+
+(*) denotes implementation defined and is discouraged
 
 Notes:
 
@@ -115,8 +153,8 @@ can be more restrictive, in case of any existing aliasing for that address.
 For example: If there is an existing uncached mapping, a new ioremap_wc can
 return uncached mapping in place of write-combine requested.
 
-set_memory_[uc|wc] and set_memory_wb should be used in pairs, where driver will
-first make a region uc or wc and switch it back to wb after use.
+set_memory_[uc|wc|wt] and set_memory_wb should be used in pairs, where driver
+will first make a region uc, wc or wt and switch it back to wb after use.
 
 Over time writes to /proc/mtrr will be deprecated in favor of using PAT based
 interfaces. Users writing to /proc/mtrr are suggested to use above interfaces.
@@ -124,7 +162,7 @@ interfaces. Users writing to /proc/mtrr are suggested to use above interfaces.
 Drivers should use ioremap_[uc|wc] to access PCI BARs with [uc|wc] access
 types.
 
-Drivers should use set_memory_[uc|wc] to set access type for RAM ranges.
+Drivers should use set_memory_[uc|wc|wt] to set access type for RAM ranges.
 
 
 PAT debugging
index 5223479291a295e7b248661d28c14ea8a33b3eaa..68ed3114c363bf7332b2515b156bf0953ca88d24 100644 (file)
@@ -31,6 +31,9 @@ Machine check
                (e.g. BIOS or hardware monitoring applications), conflicting
                with OS's error handling, and you cannot deactivate the agent,
                then this option will be a help.
+   mce=no_lmce
+               Do not opt-in to Local MCE delivery. Use legacy method
+               to broadcast MCEs.
    mce=bootlog
                Enable logging of machine checks left over from booting.
                Disabled by default on AMD because some BIOS leave bogus ones.
index d5b8f01833f41365689bc78258770eecebd46f36..bce9725210659de9a7c1d3c97cb9d8374dc0d581 100644 (file)
@@ -638,9 +638,6 @@ GPIO 控制器的路径类似 /sys/class/gpio/gpiochip42/ (对于从#42 GPIO
        int gpio_export_link(struct device *dev, const char *name,
                unsigned gpio)
 
-       /* 改变 sysfs 中的一个 GPIO 节点的极性 */
-       int gpio_sysfs_set_active_low(unsigned gpio, int value);
-
 在一个内核驱动申请一个 GPIO 之后,它可以通过 gpio_export()使其在 sysfs
 接口中可见。该驱动可以控制信号方向是否可修改。这有助于防止用户空间代码无意间
 破坏重要的系统状态。
@@ -651,8 +648,3 @@ GPIO 控制器的路径类似 /sys/class/gpio/gpiochip42/ (对于从#42 GPIO
 在 GPIO 被导出之后,gpio_export_link()允许在 sysfs 文件系统的任何地方
 创建一个到这个 GPIO sysfs 节点的符号链接。这样驱动就可以通过一个描述性的
 名字,在 sysfs 中他们所拥有的设备下提供一个(到这个 GPIO sysfs 节点的)接口。
-
-驱动可以使用 gpio_sysfs_set_active_low() 来在用户空间隐藏电路板之间
-GPIO 线的极性差异。这个仅对 sysfs 接口起作用。极性的改变可以在 gpio_export()
-前后进行,且之前使能的轮询操作(poll(2))支持(上升或下降沿)将会被重新配置来遵循
-这个设置。
diff --git a/Kbuild b/Kbuild
index 6f0d82a9245d897c89a63819e857f474ab93066e..df99a5f53beb880482871e99453bf04ef2f0fb06 100644 (file)
--- a/Kbuild
+++ b/Kbuild
@@ -2,8 +2,9 @@
 # Kbuild for top-level directory of the kernel
 # This file takes care of the following:
 # 1) Generate bounds.h
-# 2) Generate asm-offsets.h (may need bounds.h)
-# 3) Check for missing system calls
+# 2) Generate timeconst.h
+# 3) Generate asm-offsets.h (may need bounds.h and timeconst.h)
+# 4) Check for missing system calls
 
 # Default sed regexp - multiline due to syntax constraints
 define sed-y
@@ -47,7 +48,26 @@ $(obj)/$(bounds-file): kernel/bounds.s FORCE
        $(call filechk,offsets,__LINUX_BOUNDS_H__)
 
 #####
-# 2) Generate asm-offsets.h
+# 2) Generate timeconst.h
+
+timeconst-file := include/generated/timeconst.h
+
+#always  += $(timeconst-file)
+targets += $(timeconst-file)
+
+quiet_cmd_gentimeconst = GEN     $@
+define cmd_gentimeconst
+       (echo $(CONFIG_HZ) | bc -q $< ) > $@
+endef
+define filechk_gentimeconst
+       (echo $(CONFIG_HZ) | bc -q $< )
+endef
+
+$(obj)/$(timeconst-file): kernel/time/timeconst.bc FORCE
+       $(call filechk,gentimeconst)
+
+#####
+# 3) Generate asm-offsets.h
 #
 
 offsets-file := include/generated/asm-offsets.h
@@ -57,7 +77,7 @@ targets += arch/$(SRCARCH)/kernel/asm-offsets.s
 
 # We use internal kbuild rules to avoid the "is up to date" message from make
 arch/$(SRCARCH)/kernel/asm-offsets.s: arch/$(SRCARCH)/kernel/asm-offsets.c \
-                                      $(obj)/$(bounds-file) FORCE
+                                      $(obj)/$(timeconst-file) $(obj)/$(bounds-file) FORCE
        $(Q)mkdir -p $(dir $@)
        $(call if_changed_dep,cc_s_c)
 
@@ -65,7 +85,7 @@ $(obj)/$(offsets-file): arch/$(SRCARCH)/kernel/asm-offsets.s FORCE
        $(call filechk,offsets,__ASM_OFFSETS_H__)
 
 #####
-# 3) Check for missing system calls
+# 4) Check for missing system calls
 #
 
 always += missing-syscalls
@@ -77,5 +97,5 @@ quiet_cmd_syscalls = CALL    $<
 missing-syscalls: scripts/checksyscalls.sh $(offsets-file) FORCE
        $(call cmd,syscalls)
 
-# Keep these two files during make clean
-no-clean-files := $(bounds-file) $(offsets-file)
+# Keep these three files during make clean
+no-clean-files := $(bounds-file) $(offsets-file) $(timeconst-file)
index e30871880fdb119f4d9ea888259f99365f6f5065..78a9174d5a487477df9d81a53ed3ec3f60db5e35 100644 (file)
@@ -51,9 +51,9 @@ trivial patch so apply some common sense.
        or does something very odd once a month document it.
 
        PLEASE remember that submissions must be made under the terms
-       of the OSDL certificate of contribution and should include a
-       Signed-off-by: line.  The current version of this "Developer's
-       Certificate of Origin" (DCO) is listed in the file
+       of the Linux Foundation certificate of contribution and should
+       include a Signed-off-by: line.  The current version of this
+       "Developer's Certificate of Origin" (DCO) is listed in the file
        Documentation/SubmittingPatches.
 
 6.     Make sure you have the right to send any changes you make. If you
@@ -445,6 +445,7 @@ F:  drivers/input/misc/adxl34x.c
 
 ADVANSYS SCSI DRIVER
 M:     Matthew Wilcox <matthew@wil.cx>
+M:     Hannes Reinecke <hare@suse.de>
 L:     linux-scsi@vger.kernel.org
 S:     Maintained
 F:     Documentation/scsi/advansys.txt
@@ -1488,10 +1489,12 @@ F:      drivers/phy/phy-stih407-usb.c
 F:     drivers/phy/phy-stih41x-usb.c
 F:     drivers/pinctrl/pinctrl-st.c
 F:     drivers/reset/sti/
+F:     drivers/rtc/rtc-st-lpc.c
 F:     drivers/tty/serial/st-asc.c
 F:     drivers/usb/dwc3/dwc3-st.c
 F:     drivers/usb/host/ehci-st.c
 F:     drivers/usb/host/ohci-st.c
+F:     drivers/watchdog/st_lpc_wdt.c
 F:     drivers/ata/ahci_st.c
 
 ARM/TECHNOLOGIC SYSTEMS TS7250 MACHINE SUPPORT
@@ -1634,11 +1637,12 @@ F:      drivers/i2c/busses/i2c-cadence.c
 F:     drivers/mmc/host/sdhci-of-arasan.c
 F:     drivers/edac/synopsys_edac.c
 
-ARM SMMU DRIVER
+ARM SMMU DRIVERS
 M:     Will Deacon <will.deacon@arm.com>
 L:     linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
 S:     Maintained
 F:     drivers/iommu/arm-smmu.c
+F:     drivers/iommu/arm-smmu-v3.c
 F:     drivers/iommu/io-pgtable-arm.c
 
 ARM64 PORT (AARCH64 ARCHITECTURE)
@@ -2250,6 +2254,13 @@ N:       bcm9583*
 N:     bcm583*
 N:     bcm113*
 
+BROADCOM BRCMSTB GPIO DRIVER
+M:     Gregory Fong <gregory.0xf0@gmail.com>
+L:     bcm-kernel-feedback-list@broadcom.com>
+S:     Supported
+F:     drivers/gpio/gpio-brcmstb.c
+F:     Documentation/devicetree/bindings/gpio/brcm,brcmstb-gpio.txt
+
 BROADCOM KONA GPIO DRIVER
 M:     Ray Jui <rjui@broadcom.com>
 L:     bcm-kernel-feedback-list@broadcom.com
@@ -2257,6 +2268,12 @@ S:       Supported
 F:     drivers/gpio/gpio-bcm-kona.c
 F:     Documentation/devicetree/bindings/gpio/gpio-bcm-kona.txt
 
+BROADCOM STB NAND FLASH DRIVER
+M:     Brian Norris <computersforpeace@gmail.com>
+L:     linux-mtd@lists.infradead.org
+S:     Maintained
+F:     drivers/mtd/nand/brcmnand/
+
 BROADCOM SPECIFIC AMBA DRIVER (BCMA)
 M:     Rafał Miłecki <zajec5@gmail.com>
 L:     linux-wireless@vger.kernel.org
@@ -2447,7 +2464,6 @@ F:        Documentation/devicetree/bindings/net/ieee802154/cc2520.txt
 CELL BROADBAND ENGINE ARCHITECTURE
 M:     Arnd Bergmann <arnd@arndb.de>
 L:     linuxppc-dev@lists.ozlabs.org
-L:     cbe-oss-dev@lists.ozlabs.org
 W:     http://www.ibm.com/developerworks/power/cell/
 S:     Supported
 F:     arch/powerpc/include/asm/cell*.h
@@ -2603,6 +2619,13 @@ L:       linux-scsi@vger.kernel.org
 S:     Supported
 F:     drivers/scsi/fnic/
 
+CISCO SCSI HBA DRIVER
+M:     Narsimhulu Musini <nmusini@cisco.com>
+M:     Sesidhar Baddela <sebaddel@cisco.com>
+L:     linux-scsi@vger.kernel.org
+S:     Supported
+F:     drivers/scsi/snic/
+
 CMPC ACPI DRIVER
 M:     Thadeu Lima de Souza Cascardo <cascardo@holoscopio.com>
 M:     Daniel Oliveira Nascimento <don@syst.com.br>
@@ -2955,7 +2978,7 @@ M:        Michael Neuling <mikey@neuling.org>
 L:     linuxppc-dev@lists.ozlabs.org
 S:     Supported
 F:     drivers/misc/cxl/
-F:     include/misc/cxl.h
+F:     include/misc/cxl*
 F:     include/uapi/misc/cxl.h
 F:     Documentation/powerpc/cxl.txt
 F:     Documentation/powerpc/cxl.txt
@@ -4643,6 +4666,18 @@ F:       drivers/hid/
 F:     include/linux/hid*
 F:     include/uapi/linux/hid*
 
+HID SENSOR HUB DRIVERS
+M:     Jiri Kosina <jkosina@suse.cz>
+M:     Jonathan Cameron <jic23@kernel.org>
+M:     Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
+L:     linux-input@vger.kernel.org
+L:     linux-iio@vger.kernel.org
+S:     Maintained
+F:     Documentation/hid/hid-sensor*
+F:     drivers/hid/hid-sensor-*
+F:     drivers/iio/*/hid-*
+F:     include/linux/hid-sensor-*
+
 HIGH-RESOLUTION TIMERS, CLOCKEVENTS, DYNTICKS
 M:     Thomas Gleixner <tglx@linutronix.de>
 L:     linux-kernel@vger.kernel.org
@@ -4879,13 +4914,23 @@ M:      Marcelo Henrique Cerri <mhcerri@linux.vnet.ibm.com>
 M:     Fionnuala Gunter <fin@linux.vnet.ibm.com>
 L:     linux-crypto@vger.kernel.org
 S:     Supported
-F:     drivers/crypto/nx/
+F:     drivers/crypto/nx/Makefile
+F:     drivers/crypto/nx/Kconfig
+F:     drivers/crypto/nx/nx-aes*
+F:     drivers/crypto/nx/nx-sha*
+F:     drivers/crypto/nx/nx.*
+F:     drivers/crypto/nx/nx_csbcpb.h
+F:     drivers/crypto/nx/nx_debugfs.h
 
 IBM Power 842 compression accelerator
 M:     Dan Streetman <ddstreet@us.ibm.com>
 S:     Supported
-F:     drivers/crypto/nx/nx-842.c
-F:     include/linux/nx842.h
+F:     drivers/crypto/nx/Makefile
+F:     drivers/crypto/nx/Kconfig
+F:     drivers/crypto/nx/nx-842*
+F:     include/linux/sw842.h
+F:     crypto/842.c
+F:     lib/842/
 
 IBM Power Linux RAID adapter
 M:     Brian King <brking@us.ibm.com>
@@ -7575,6 +7620,7 @@ F:        drivers/pci/host/pci-exynos.c
 
 PCI DRIVER FOR SYNOPSIS DESIGNWARE
 M:     Jingoo Han <jingoohan1@gmail.com>
+M:     Pratyush Anand <pratyush.anand@gmail.com>
 L:     linux-pci@vger.kernel.org
 S:     Maintained
 F:     drivers/pci/host/*designware*
@@ -7588,10 +7634,19 @@ F:      Documentation/devicetree/bindings/pci/host-generic-pci.txt
 F:     drivers/pci/host/pci-host-generic.c
 
 PCIE DRIVER FOR ST SPEAR13XX
+M:     Pratyush Anand <pratyush.anand@gmail.com>
 L:     linux-pci@vger.kernel.org
-S:     Orphan
+S:     Maintained
 F:     drivers/pci/host/*spear*
 
+PCI MSI DRIVER FOR APPLIEDMICRO XGENE
+M:     Duc Dang <dhdang@apm.com>
+L:     linux-pci@vger.kernel.org
+L:     linux-arm-kernel@lists.infradead.org
+S:     Maintained
+F:     Documentation/devicetree/bindings/pci/xgene-pci-msi.txt
+F:     drivers/pci/host/pci-xgene-msi.c
+
 PCMCIA SUBSYSTEM
 P:     Linux PCMCIA Team
 L:     linux-pcmcia@lists.infradead.org
@@ -7632,7 +7687,6 @@ F:        kernel/delayacct.c
 
 PERFORMANCE EVENTS SUBSYSTEM
 M:     Peter Zijlstra <a.p.zijlstra@chello.nl>
-M:     Paul Mackerras <paulus@samba.org>
 M:     Ingo Molnar <mingo@redhat.com>
 M:     Arnaldo Carvalho de Melo <acme@kernel.org>
 L:     linux-kernel@vger.kernel.org
@@ -7859,14 +7913,13 @@ F:      drivers/net/wireless/prism54/
 PS3 NETWORK SUPPORT
 M:     Geoff Levand <geoff@infradead.org>
 L:     netdev@vger.kernel.org
-L:     cbe-oss-dev@lists.ozlabs.org
+L:     linuxppc-dev@lists.ozlabs.org
 S:     Maintained
 F:     drivers/net/ethernet/toshiba/ps3_gelic_net.*
 
 PS3 PLATFORM SUPPORT
 M:     Geoff Levand <geoff@infradead.org>
 L:     linuxppc-dev@lists.ozlabs.org
-L:     cbe-oss-dev@lists.ozlabs.org
 S:     Maintained
 F:     arch/powerpc/boot/ps3*
 F:     arch/powerpc/include/asm/lv1call.h
@@ -7880,7 +7933,7 @@ F:        sound/ppc/snd_ps3*
 
 PS3VRAM DRIVER
 M:     Jim Paris <jim@jtan.com>
-L:     cbe-oss-dev@lists.ozlabs.org
+L:     linuxppc-dev@lists.ozlabs.org
 S:     Maintained
 F:     drivers/block/ps3vram.c
 
@@ -8535,14 +8588,20 @@ L:      linux-fbdev@vger.kernel.org
 S:     Maintained
 F:     drivers/video/fbdev/s3c-fb.c
 
-SAMSUNG MULTIFUNCTION DEVICE DRIVERS
+SAMSUNG MULTIFUNCTION PMIC DEVICE DRIVERS
 M:     Sangbeom Kim <sbkim73@samsung.com>
+M:     Krzysztof Kozlowski <k.kozlowski@samsung.com>
 L:     linux-kernel@vger.kernel.org
+L:     linux-samsung-soc@vger.kernel.org
 S:     Supported
 F:     drivers/mfd/sec*.c
 F:     drivers/regulator/s2m*.c
 F:     drivers/regulator/s5m*.c
+F:     drivers/clk/clk-s2mps11.c
+F:     drivers/rtc/rtc-s5m.c
 F:     include/linux/mfd/samsung/
+F:     Documentation/devicetree/bindings/regulator/s5m8767-regulator.txt
+F:     Documentation/devicetree/bindings/mfd/s2mp*.txt
 
 SAMSUNG S5P/EXYNOS4 SOC SERIES CAMERA SUBSYSTEM DRIVERS
 M:     Kyungmin Park <kyungmin.park@samsung.com>
@@ -9359,7 +9418,6 @@ F:        drivers/net/ethernet/toshiba/spider_net*
 SPU FILE SYSTEM
 M:     Jeremy Kerr <jk@ozlabs.org>
 L:     linuxppc-dev@lists.ozlabs.org
-L:     cbe-oss-dev@lists.ozlabs.org
 W:     http://www.ibm.com/developerworks/power/cell/
 S:     Supported
 F:     Documentation/filesystems/spufs.txt
@@ -9581,6 +9639,13 @@ F:       arch/arc/
 F:     Documentation/devicetree/bindings/arc/
 F:     drivers/tty/serial/arc_uart.c
 
+SYSTEM CONFIGURATION (SYSCON)
+M:     Lee Jones <lee.jones@linaro.org>
+M:     Arnd Bergmann <arnd@arndb.de>
+T:     git git://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd.git
+S:     Supported
+F:     drivers/mfd/syscon.c
+
 SYSV FILESYSTEM
 M:     Christoph Hellwig <hch@infradead.org>
 S:     Maintained
@@ -10015,7 +10080,7 @@ F:      include/linux/toshiba.h
 F:     include/uapi/linux/toshiba.h
 
 TMIO MMC DRIVER
-M:     Ian Molton <ian.molton@codethink.co.uk>
+M:     Ian Molton <ian@mnementh.co.uk>
 L:     linux-mmc@vger.kernel.org
 S:     Maintained
 F:     drivers/mmc/host/tmio_mmc*
@@ -10893,7 +10958,7 @@ M:      Andy Lutomirski <luto@amacapital.net>
 L:     linux-kernel@vger.kernel.org
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git x86/vdso
 S:     Maintained
-F:     arch/x86/vdso/
+F:     arch/x86/entry/vdso/
 
 XC2028/3028 TUNER DRIVER
 M:     Mauro Carvalho Chehab <mchehab@osg.samsung.com>
index aee7e5cb4c151653fd00397eb90bba764e2b7c38..6c6f14628f329d0ba10f5632fb362c818c437ff5 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
 VERSION = 4
 PATCHLEVEL = 1
 SUBLEVEL = 0
-EXTRAVERSION = -rc6
+EXTRAVERSION =
 NAME = Hurr durr I'ma sheep
 
 # *DOCUMENTATION*
@@ -215,7 +215,6 @@ VPATH               := $(srctree)$(if $(KBUILD_EXTMOD),:$(KBUILD_EXTMOD))
 
 export srctree objtree VPATH
 
-
 # SUBARCH tells the usermode build what the underlying arch is.  That is set
 # first, and if a usermode build is happening, the "ARCH=um" on the command
 # line overrides the setting of ARCH below.  If a native build is happening,
@@ -1497,11 +1496,11 @@ image_name:
 # Clear a bunch of variables before executing the submake
 tools/: FORCE
        $(Q)mkdir -p $(objtree)/tools
-       $(Q)$(MAKE) LDFLAGS= MAKEFLAGS="$(filter --j% -j,$(MAKEFLAGS))" O=$(objtree) subdir=tools -C $(src)/tools/
+       $(Q)$(MAKE) LDFLAGS= MAKEFLAGS="$(filter --j% -j,$(MAKEFLAGS))" O=$(O) subdir=tools -C $(src)/tools/
 
 tools/%: FORCE
        $(Q)mkdir -p $(objtree)/tools
-       $(Q)$(MAKE) LDFLAGS= MAKEFLAGS="$(filter --j% -j,$(MAKEFLAGS))" O=$(objtree) subdir=tools -C $(src)/tools/ $*
+       $(Q)$(MAKE) LDFLAGS= MAKEFLAGS="$(filter --j% -j,$(MAKEFLAGS))" O=$(O) subdir=tools -C $(src)/tools/ $*
 
 # Single targets
 # ---------------------------------------------------------------------------
index 429e8cd0d78e5a8f7aaeafacc3f36fae0c6a49c4..e5117766529e87ff99170fd4a53fde8d89bba4ff 100644 (file)
@@ -66,6 +66,4 @@
 #undef __ASM__MB
 #undef ____cmpxchg
 
-#define __HAVE_ARCH_CMPXCHG 1
-
 #endif /* _ALPHA_CMPXCHG_H */
index f7f680f7457dec0cc7ba0b9a5562c2041b2a5a4a..8b02afeb6319101c0080e98a06ced98e7e4f27ad 100644 (file)
@@ -71,22 +71,6 @@ extern void pcibios_set_master(struct pci_dev *dev);
 /* implement the pci_ DMA API in terms of the generic device dma_ one */
 #include <asm-generic/pci-dma-compat.h>
 
-static inline void pci_dma_burst_advice(struct pci_dev *pdev,
-                                       enum pci_dma_burst_strategy *strat,
-                                       unsigned long *strategy_parameter)
-{
-       unsigned long cacheline_size;
-       u8 byte;
-
-       pci_read_config_byte(pdev, PCI_CACHE_LINE_SIZE, &byte);
-       if (byte == 0)
-               cacheline_size = 1024;
-       else
-               cacheline_size = (int) byte * 4;
-
-       *strat = PCI_DMA_BURST_BOUNDARY;
-       *strategy_parameter = cacheline_size;
-}
 #endif
 
 /* TODO: integrate with include/asm-generic/pci.h ? */
index 00096df0f6ad05e440d1f0f4764bce34e6309816..83d0a359a1b2e79181082394f41b5fe9fc9cd441 100644 (file)
@@ -22,7 +22,6 @@
 #include <linux/bootmem.h>
 
 #include <asm/ptrace.h>
-#include <asm/pci.h>
 #include <asm/cacheflush.h>
 #include <asm/tlbflush.h>
 
index 79d69d7f63f8901dfd8f37b33069e57876d30919..15f42083bdb3501fc041c2e1634225674fe849b3 100644 (file)
@@ -22,7 +22,6 @@
 #include <asm/irq.h>
 #include <asm/mmu_context.h>
 #include <asm/io.h>
-#include <asm/pci.h>
 #include <asm/pgtable.h>
 #include <asm/core_tsunami.h>
 #include <asm/hwrpb.h>
index 700686d0486979c94ade2caec2c6938addbb63a2..2cfaa0e5c5777b2f0c49c5e0688d38cad512d840 100644 (file)
@@ -39,7 +39,6 @@
 #include <asm/irq.h>
 #include <asm/mmu_context.h>
 #include <asm/io.h>
-#include <asm/pci.h>
 #include <asm/pgtable.h>
 #include <asm/core_irongate.h>
 #include <asm/hwrpb.h>
index cabd518cb253d95e3bf532d07bfbc5c2214d19fc..7cc4ced5dbf4e4894c6b7d594a9810df74ab26d0 100644 (file)
@@ -20,6 +20,7 @@ extern void iounmap(const void __iomem *addr);
 
 #define ioremap_nocache(phy, sz)       ioremap(phy, sz)
 #define ioremap_wc(phy, sz)            ioremap(phy, sz)
+#define ioremap_wt(phy, sz)            ioremap(phy, sz)
 
 /* Change struct page to physical address */
 #define page_to_phys(page)             (page_to_pfn(page) << PAGE_SHIFT)
index c3255e0c90aa829fc792f02d1265d413f3c6e624..dbb3f4d2bf84ebf4565555949053c94619ea161d 100644 (file)
 /include/ "tps65217.dtsi"
 
 &tps {
+       /*
+        * Configure pmic to enter OFF-state instead of SLEEP-state ("RTC-only
+        * mode") at poweroff.  Most BeagleBone versions do not support RTC-only
+        * mode and risk hardware damage if this mode is entered.
+        *
+        * For details, see linux-omap mailing list May 2015 thread
+        *      [PATCH] ARM: dts: am335x-bone* enable pmic-shutdown-controller
+        * In particular, messages:
+        *      http://www.spinics.net/lists/linux-omap/msg118585.html
+        *      http://www.spinics.net/lists/linux-omap/msg118615.html
+        *
+        * You can override this later with
+        *      &tps {  /delete-property/ ti,pmic-shutdown-controller;  }
+        * if you want to use RTC-only mode and made sure you are not affected
+        * by the hardware problems. (Tip: double-check by performing a current
+        * measurement after shutdown: it should be less than 1 mA.)
+        */
+       ti,pmic-shutdown-controller;
+
        regulators {
                dcdc1_reg: regulator@0 {
                        regulator-name = "vdds_dpr";
index 518b8fde88b0c87005fe68e413cfee769befac07..18cc826e9db534714a1b4d8a3cfc497e43ffcc85 100644 (file)
@@ -12,7 +12,7 @@
                #clock-cells = <0>;
                compatible = "ti,am35xx-gate-clock";
                clocks = <&ipss_ick>;
-               reg = <0x059c>;
+               reg = <0x032c>;
                ti,bit-shift = <1>;
        };
 
@@ -20,7 +20,7 @@
                #clock-cells = <0>;
                compatible = "ti,gate-clock";
                clocks = <&rmii_ck>;
-               reg = <0x059c>;
+               reg = <0x032c>;
                ti,bit-shift = <9>;
        };
 
@@ -28,7 +28,7 @@
                #clock-cells = <0>;
                compatible = "ti,am35xx-gate-clock";
                clocks = <&ipss_ick>;
-               reg = <0x059c>;
+               reg = <0x032c>;
                ti,bit-shift = <2>;
        };
 
@@ -36,7 +36,7 @@
                #clock-cells = <0>;
                compatible = "ti,gate-clock";
                clocks = <&pclk_ck>;
-               reg = <0x059c>;
+               reg = <0x032c>;
                ti,bit-shift = <10>;
        };
 
@@ -44,7 +44,7 @@
                #clock-cells = <0>;
                compatible = "ti,am35xx-gate-clock";
                clocks = <&ipss_ick>;
-               reg = <0x059c>;
+               reg = <0x032c>;
                ti,bit-shift = <0>;
        };
 
@@ -52,7 +52,7 @@
                #clock-cells = <0>;
                compatible = "ti,gate-clock";
                clocks = <&sys_ck>;
-               reg = <0x059c>;
+               reg = <0x032c>;
                ti,bit-shift = <8>;
        };
 
@@ -60,7 +60,7 @@
                #clock-cells = <0>;
                compatible = "ti,am35xx-gate-clock";
                clocks = <&sys_ck>;
-               reg = <0x059c>;
+               reg = <0x032c>;
                ti,bit-shift = <3>;
        };
 };
index 7128fad991ac3459ac5349b0219e893fab1830c8..a42cc377a8625c35168d93cbe4fd06430ed3a6ac 100644 (file)
@@ -19,6 +19,7 @@
                rtc0 = &mcp_rtc;
                rtc1 = &tps659038_rtc;
                rtc2 = &rtc;
+               display0 = &hdmi0;
        };
 
        memory {
                pinctrl-names = "default";
                pinctrl-0 = <&extcon_usb2_pins>;
        };
+
+       hdmi0: connector {
+               compatible = "hdmi-connector";
+               label = "hdmi";
+
+               type = "a";
+
+               port {
+                       hdmi_connector_in: endpoint {
+                               remote-endpoint = <&tpd12s015_out>;
+                       };
+               };
+       };
+
+       tpd12s015: encoder {
+               compatible = "ti,tpd12s015";
+
+               pinctrl-names = "default";
+               pinctrl-0 = <&tpd12s015_pins>;
+
+               gpios = <&gpio7 10 GPIO_ACTIVE_HIGH>,   /* gpio7_10, CT CP HPD */
+                       <&gpio6 28 GPIO_ACTIVE_HIGH>,   /* gpio6_28, LS OE */
+                       <&gpio7 12 GPIO_ACTIVE_HIGH>;   /* gpio7_12/sp1_cs2, HPD */
+
+               ports {
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+
+                       port@0 {
+                               reg = <0>;
+
+                               tpd12s015_in: endpoint {
+                                       remote-endpoint = <&hdmi_out>;
+                               };
+                       };
+
+                       port@1 {
+                               reg = <1>;
+
+                               tpd12s015_out: endpoint {
+                                       remote-endpoint = <&hdmi_connector_in>;
+                               };
+                       };
+               };
+       };
 };
 
 &dra7_pmx_core {
                >;
        };
 
+       hdmi_pins: pinmux_hdmi_pins {
+               pinctrl-single,pins = <
+                       0x408 (PIN_INPUT | MUX_MODE1)   /* i2c2_sda.hdmi1_ddc_scl */
+                       0x40c (PIN_INPUT | MUX_MODE1)   /* i2c2_scl.hdmi1_ddc_sda */
+               >;
+       };
+
        i2c3_pins_default: i2c3_pins_default {
                pinctrl-single,pins = <
                        0x2a4 (PIN_INPUT| MUX_MODE10)   /* mcasp1_aclkx.i2c3_sda */
                        0x3e8 (PIN_INPUT_PULLUP | MUX_MODE14) /* uart1_ctsn.gpio7_24 */
                >;
        };
+
+       tpd12s015_pins: pinmux_tpd12s015_pins {
+               pinctrl-single,pins = <
+                       0x3b0 (PIN_OUTPUT | MUX_MODE14)         /* gpio7_10 CT_CP_HPD */
+                       0x3b8 (PIN_INPUT_PULLDOWN | MUX_MODE14) /* gpio7_12 HPD */
+                       0x370 (PIN_OUTPUT | MUX_MODE14)         /* gpio6_28 LS_OE */
+               >;
+       };
 };
 
 &i2c1 {
                };
        };
 };
+
+&dss {
+       status = "ok";
+
+       vdda_video-supply = <&ldoln_reg>;
+};
+
+&hdmi {
+       status = "ok";
+       vdda-supply = <&ldo3_reg>;
+
+       pinctrl-names = "default";
+       pinctrl-0 = <&hdmi_pins>;
+
+       port {
+               hdmi_out: endpoint {
+                       remote-endpoint = <&tpd12s015_in>;
+               };
+       };
+};
index a2cf2154dcdb68d8374c2bea4b136fccaccb7aa2..fdd187c55aa5f78b5ab61d15dc12c1ad001990d2 100644 (file)
 
                internal-regs {
 
+                       rtc@10300 {
+                               /* No crystal connected to the internal RTC */
+                               status = "disabled";
+                       };
+
                        /* J10: VCC, NC, RX, NC, TX, GND  */
                        serial@12000 {
                                status = "okay";
index de8427be830a32e24a01ace97f11303435528b7b..289806adb343806aefce22e63b6caa1d558741fb 100644 (file)
                        ti,hwmods = "usb_otg_hs";
 
                        usb0: usb@47401000 {
-                               compatible = "ti,musb-am33xx";
+                               compatible = "ti,musb-dm816";
                                reg = <0x47401400 0x400
                                       0x47401000 0x200>;
                                reg-names = "mc", "control";
                        };
 
                        usb1: usb@47401800 {
-                               compatible = "ti,musb-am33xx";
+                               compatible = "ti,musb-dm816";
                                reg = <0x47401c00 0x400
                                       0x47401800 0x200>;
                                reg-names = "mc", "control";
index f03a091cd0766b606d0f3a61a4ab03c870f3dfcf..8f1e25bcecbd76273f62671e8b9afa8f193ea261 100644 (file)
                                                        regulator-max-microvolt = <3000000>;
                                                };
                                        };
+
+                                       scm_conf_clocks: clocks {
+                                               #address-cells = <1>;
+                                               #size-cells = <0>;
+                                       };
                                };
 
                                dra7_pmx_core: pinmux@1400 {
                        clocks = <&sys_clkin1>;
                        status = "disabled";
                };
+
+               dss: dss@58000000 {
+                       compatible = "ti,dra7-dss";
+                       /* 'reg' defined in dra72x.dtsi and dra74x.dtsi */
+                       /* 'clocks' defined in dra72x.dtsi and dra74x.dtsi */
+                       status = "disabled";
+                       ti,hwmods = "dss_core";
+                       /* CTRL_CORE_DSS_PLL_CONTROL */
+                       syscon-pll-ctrl = <&scm_conf 0x538>;
+                       #address-cells = <1>;
+                       #size-cells = <1>;
+                       ranges;
+
+                       dispc@58001000 {
+                               compatible = "ti,dra7-dispc";
+                               reg = <0x58001000 0x1000>;
+                               interrupts = <GIC_SPI 20 IRQ_TYPE_LEVEL_HIGH>;
+                               ti,hwmods = "dss_dispc";
+                               clocks = <&dss_dss_clk>;
+                               clock-names = "fck";
+                               /* CTRL_CORE_SMA_SW_1 */
+                               syscon-pol = <&scm_conf 0x534>;
+                       };
+
+                       hdmi: encoder@58060000 {
+                               compatible = "ti,dra7-hdmi";
+                               reg = <0x58040000 0x200>,
+                                     <0x58040200 0x80>,
+                                     <0x58040300 0x80>,
+                                     <0x58060000 0x19000>;
+                               reg-names = "wp", "pll", "phy", "core";
+                               interrupts = <GIC_SPI 96 IRQ_TYPE_LEVEL_HIGH>;
+                               status = "disabled";
+                               ti,hwmods = "dss_hdmi";
+                               clocks = <&dss_48mhz_clk>, <&dss_hdmi_clk>;
+                               clock-names = "fck", "sys_clk";
+                       };
+               };
        };
 
        thermal_zones: thermal-zones {
index ce0390f081d92ff6966657a5a45713fa7c32c309..4e1b60581782c43fc40c9b167e45f90e64024068 100644 (file)
                reg = <0x80000000 0x40000000>; /* 1024 MB */
        };
 
+       aliases {
+               display0 = &hdmi0;
+       };
+
        evm_3v3: fixedregulator-evm_3v3 {
                compatible = "regulator-fixed";
                regulator-name = "evm_3v3";
                compatible = "linux,extcon-usb-gpio";
                id-gpio = <&pcf_gpio_21 2 GPIO_ACTIVE_HIGH>;
        };
+
+       hdmi0: connector {
+               compatible = "hdmi-connector";
+               label = "hdmi";
+
+               type = "a";
+
+               port {
+                       hdmi_connector_in: endpoint {
+                               remote-endpoint = <&tpd12s015_out>;
+                       };
+               };
+       };
+
+       tpd12s015: encoder {
+               compatible = "ti,tpd12s015";
+
+               pinctrl-names = "default";
+               pinctrl-0 = <&tpd12s015_pins>;
+
+               gpios = <&pcf_hdmi 4 GPIO_ACTIVE_HIGH>, /* P4, CT CP HPD */
+                       <&pcf_hdmi 5 GPIO_ACTIVE_HIGH>, /* P5, LS OE */
+                       <&gpio7 12 GPIO_ACTIVE_HIGH>;   /* gpio7_12/sp1_cs2, HPD */
+
+               ports {
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+
+                       port@0 {
+                               reg = <0>;
+
+                               tpd12s015_in: endpoint {
+                                       remote-endpoint = <&hdmi_out>;
+                               };
+                       };
+
+                       port@1 {
+                               reg = <1>;
+
+                               tpd12s015_out: endpoint {
+                                       remote-endpoint = <&hdmi_connector_in>;
+                               };
+                       };
+               };
+       };
 };
 
 &dra7_pmx_core {
                >;
        };
 
+       i2c5_pins: pinmux_i2c5_pins {
+               pinctrl-single,pins = <
+                       0x2b4 (PIN_INPUT | MUX_MODE10) /* mcasp1_axr0.i2c5_sda */
+                       0x2b8 (PIN_INPUT | MUX_MODE10) /* mcasp1_axr1.i2c5_scl */
+               >;
+       };
+
        nand_default: nand_default {
                pinctrl-single,pins = <
                        0x0     (PIN_INPUT  | MUX_MODE0) /* gpmc_ad0 */
                        0xb8 (PIN_OUTPUT | MUX_MODE1)   /* gpmc_cs2.qspi1_cs0 */
                >;
        };
+
+       hdmi_pins: pinmux_hdmi_pins {
+               pinctrl-single,pins = <
+                       0x408 (PIN_INPUT | MUX_MODE1) /* i2c2_sda.hdmi1_ddc_scl */
+                       0x40c (PIN_INPUT | MUX_MODE1) /* i2c2_scl.hdmi1_ddc_sda */
+               >;
+       };
+
+       tpd12s015_pins: pinmux_tpd12s015_pins {
+               pinctrl-single,pins = <
+                       0x3b8 (PIN_INPUT_PULLDOWN | MUX_MODE14) /* gpio7_12 HPD */
+               >;
+       };
 };
 
 &i2c1 {
        };
 };
 
+&i2c5 {
+       status = "okay";
+       pinctrl-names = "default";
+       pinctrl-0 = <&i2c5_pins>;
+       clock-frequency = <400000>;
+
+       pcf_hdmi: pcf8575@26 {
+               compatible = "nxp,pcf8575";
+               reg = <0x26>;
+               gpio-controller;
+               #gpio-cells = <2>;
+               /*
+                * initial state is used here to keep the mdio interface
+                * selected on RU89 through SEL_VIN4_MUX_S0, VIN2_S1 and
+                * VIN2_S0 driven high otherwise Ethernet stops working
+                * VIN6_SEL_S0 is low, thus selecting McASP3 over VIN6
+                */
+               lines-initial-states = <0x0f2b>;
+       };
+};
+
 &uart1 {
        status = "okay";
 };
                };
        };
 };
+
+&dss {
+       status = "ok";
+
+       vdda_video-supply = <&ldo5_reg>;
+};
+
+&hdmi {
+       status = "ok";
+       vdda-supply = <&ldo3_reg>;
+
+       pinctrl-names = "default";
+       pinctrl-0 = <&hdmi_pins>;
+
+       port {
+               hdmi_out: endpoint {
+                       remote-endpoint = <&tpd12s015_in>;
+               };
+       };
+};
index 03d742f8d572f10a42e426055f28e550ca8bd7a5..eaca143faa7750c24c62c4fe6bebf58eed67fa48 100644 (file)
                interrupts = <GIC_SPI 131 IRQ_TYPE_LEVEL_HIGH>;
        };
 };
+
+&dss {
+       reg = <0x58000000 0x80>,
+             <0x58004054 0x4>,
+             <0x58004300 0x20>;
+       reg-names = "dss", "pll1_clkctrl", "pll1";
+
+       clocks = <&dss_dss_clk>,
+                <&dss_video1_clk>;
+       clock-names = "fck", "video1_clk";
+};
index cc560a70926f003155474d9956652c6fe09718e7..fa995d0ca1f209a1ef71c8ace158cb4450179983 100644 (file)
                };
        };
 };
+
+&dss {
+       reg = <0x58000000 0x80>,
+             <0x58004054 0x4>,
+             <0x58004300 0x20>,
+             <0x58005054 0x4>,
+             <0x58005300 0x20>;
+       reg-names = "dss", "pll1_clkctrl", "pll1",
+                   "pll2_clkctrl", "pll2";
+
+       clocks = <&dss_dss_clk>,
+                <&dss_video1_clk>,
+                <&dss_video2_clk>;
+       clock-names = "fck", "video1_clk", "video2_clk";
+};
index 3b933f74d000cd039b3ecf441928c114dca0e124..357bedeebfac45e451a57ca736cf993bd2698c24 100644 (file)
                clocks = <&dpll_per_h12x2_ck>;
                ti,bit-shift = <8>;
                reg = <0x1120>;
+               ti,set-rate-parent;
        };
 
        dss_hdmi_clk: dss_hdmi_clk {
                clocks = <&dpll_usb_ck>;
        };
 };
+
+&scm_conf_clocks {
+       dss_deshdcp_clk: dss_deshdcp_clk {
+               #clock-cells = <0>;
+               compatible = "ti,gate-clock";
+               clocks = <&l3_iclk_div>;
+               ti,bit-shift = <0>;
+               reg = <0x558>;
+       };
+};
index b04b6b8850a71de972c5b3ce0fa6f11454740b9d..570aa339a05ec9b5670869cff3d7722899b6cafc 100644 (file)
@@ -99,6 +99,9 @@
                                        solomon,height = <32>;
                                        solomon,width = <128>;
                                        solomon,page-offset = <0>;
+                                       solomon,com-lrremap;
+                                       solomon,com-invdir;
+                                       solomon,com-offset = <32>;
                                };
                        };
 
index 5c16145920eafd9604f0571dfe60a23986313871..5f5e0f3d5b64fcb2283f72b9df923c793be7f75c 100644 (file)
                touchscreen-fuzz-x = <4>;
                touchscreen-fuzz-y = <7>;
                touchscreen-fuzz-pressure = <2>;
-               touchscreen-max-x = <4096>;
-               touchscreen-max-y = <4096>;
+               touchscreen-size-x = <4096>;
+               touchscreen-size-y = <4096>;
                touchscreen-max-pressure = <2048>;
 
                ti,x-plate-ohms = <280>;
index 5cc779c8e9c68085a288fe09986a455fc963c4cf..93ee70dbbdd390610520f497e5ce9d27830d1efb 100644 (file)
@@ -501,8 +501,8 @@ static int sa1111_setup_irq(struct sa1111 *sachip, unsigned irq_base)
         * Register SA1111 interrupt
         */
        irq_set_irq_type(sachip->irq, IRQ_TYPE_EDGE_RISING);
-       irq_set_handler_data(sachip->irq, sachip);
-       irq_set_chained_handler(sachip->irq, sa1111_irq_handler);
+       irq_set_chained_handler_and_data(sachip->irq, sa1111_irq_handler,
+                                        sachip);
 
        dev_info(sachip->dev, "Providing IRQ%u-%u\n",
                sachip->irq_base, sachip->irq_base + SA1111_IRQ_NR - 1);
@@ -836,8 +836,7 @@ static void __sa1111_remove(struct sa1111 *sachip)
        clk_unprepare(sachip->clk);
 
        if (sachip->irq != NO_IRQ) {
-               irq_set_chained_handler(sachip->irq, NULL);
-               irq_set_handler_data(sachip->irq, NULL);
+               irq_set_chained_handler_and_data(sachip->irq, NULL, NULL);
                irq_free_descs(sachip->irq_base, SA1111_IRQ_NR);
 
                release_mem_region(sachip->phys + SA1111_INTC, 512);
index 8da2207b00721b600ef340f3fdf8ceea48e0cae7..27ed1b1cd1d798d83eda3d453b96ae47fd1e284f 100644 (file)
@@ -53,20 +53,13 @@ config CRYPTO_SHA256_ARM
          SHA-256 secure hash standard (DFIPS 180-2) implemented
          using optimized ARM assembler and NEON, when available.
 
-config CRYPTO_SHA512_ARM_NEON
-       tristate "SHA384 and SHA512 digest algorithm (ARM NEON)"
-       depends on KERNEL_MODE_NEON
-       select CRYPTO_SHA512
+config CRYPTO_SHA512_ARM
+       tristate "SHA-384/512 digest algorithm (ARM-asm and NEON)"
        select CRYPTO_HASH
+       depends on !CPU_V7M
        help
          SHA-512 secure hash standard (DFIPS 180-2) implemented
-         using ARM NEON instructions, when available.
-
-         This version of SHA implements a 512 bit hash with 256 bits of
-         security against collision attacks.
-
-         This code also includes SHA-384, a 384 bit hash with 192 bits
-         of security against collision attacks.
+         using optimized ARM assembler and NEON, when available.
 
 config CRYPTO_AES_ARM
        tristate "AES cipher algorithms (ARM-asm)"
index 6ea828241fcb66ff121fc2b2cc9d892d6be05f91..fc5150702b643c2de8a8749d4e7bd178cb3b6927 100644 (file)
@@ -7,7 +7,7 @@ obj-$(CONFIG_CRYPTO_AES_ARM_BS) += aes-arm-bs.o
 obj-$(CONFIG_CRYPTO_SHA1_ARM) += sha1-arm.o
 obj-$(CONFIG_CRYPTO_SHA1_ARM_NEON) += sha1-arm-neon.o
 obj-$(CONFIG_CRYPTO_SHA256_ARM) += sha256-arm.o
-obj-$(CONFIG_CRYPTO_SHA512_ARM_NEON) += sha512-arm-neon.o
+obj-$(CONFIG_CRYPTO_SHA512_ARM) += sha512-arm.o
 
 ce-obj-$(CONFIG_CRYPTO_AES_ARM_CE) += aes-arm-ce.o
 ce-obj-$(CONFIG_CRYPTO_SHA1_ARM_CE) += sha1-arm-ce.o
@@ -30,7 +30,8 @@ sha1-arm-y    := sha1-armv4-large.o sha1_glue.o
 sha1-arm-neon-y        := sha1-armv7-neon.o sha1_neon_glue.o
 sha256-arm-neon-$(CONFIG_KERNEL_MODE_NEON) := sha256_neon_glue.o
 sha256-arm-y   := sha256-core.o sha256_glue.o $(sha256-arm-neon-y)
-sha512-arm-neon-y := sha512-armv7-neon.o sha512_neon_glue.o
+sha512-arm-neon-$(CONFIG_KERNEL_MODE_NEON) := sha512-neon-glue.o
+sha512-arm-y   := sha512-core.o sha512-glue.o $(sha512-arm-neon-y)
 sha1-arm-ce-y  := sha1-ce-core.o sha1-ce-glue.o
 sha2-arm-ce-y  := sha2-ce-core.o sha2-ce-glue.o
 aes-arm-ce-y   := aes-ce-core.o aes-ce-glue.o
@@ -45,4 +46,7 @@ $(src)/aesbs-core.S_shipped: $(src)/bsaes-armv7.pl
 $(src)/sha256-core.S_shipped: $(src)/sha256-armv4.pl
        $(call cmd,perl)
 
-.PRECIOUS: $(obj)/aesbs-core.S $(obj)/sha256-core.S
+$(src)/sha512-core.S_shipped: $(src)/sha512-armv4.pl
+       $(call cmd,perl)
+
+.PRECIOUS: $(obj)/aesbs-core.S $(obj)/sha256-core.S $(obj)/sha512-core.S
index 8cfa468ee570b0bd0f2bb800fa1f7f21b66edaf6..987aa632c9f060abff0e38e0c3c2a85eaedeeee1 100644 (file)
        \dround         q10, q11
        blo             0f                      @ AES-128: 10 rounds
        vld1.8          {q10-q11}, [ip]!
-       beq             1f                      @ AES-192: 12 rounds
        \dround         q12, q13
+       beq             1f                      @ AES-192: 12 rounds
        vld1.8          {q12-q13}, [ip]
        \dround         q10, q11
 0:     \fround         q12, q13, q14
        bx              lr
 
-1:     \dround         q12, q13
-       \fround         q10, q11, q14
+1:     \fround         q10, q11, q14
        bx              lr
        .endm
 
         *   q2        : third in/output block (_3x version only)
         *   q8        : first round key
         *   q9        : secound round key
-        *   ip        : address of 3rd round key
         *   q14       : final round key
+        *   r2        : address of round key array
         *   r3        : number of rounds
         */
        .align          6
diff --git a/arch/arm/crypto/sha512-armv4.pl b/arch/arm/crypto/sha512-armv4.pl
new file mode 100644 (file)
index 0000000..a2b11a8
--- /dev/null
@@ -0,0 +1,649 @@
+#!/usr/bin/env perl
+
+# ====================================================================
+# Written by Andy Polyakov <appro@openssl.org> for the OpenSSL
+# project. The module is, however, dual licensed under OpenSSL and
+# CRYPTOGAMS licenses depending on where you obtain it. For further
+# details see http://www.openssl.org/~appro/cryptogams/.
+#
+# Permission to use under GPL terms is granted.
+# ====================================================================
+
+# SHA512 block procedure for ARMv4. September 2007.
+
+# This code is ~4.5 (four and a half) times faster than code generated
+# by gcc 3.4 and it spends ~72 clock cycles per byte [on single-issue
+# Xscale PXA250 core].
+#
+# July 2010.
+#
+# Rescheduling for dual-issue pipeline resulted in 6% improvement on
+# Cortex A8 core and ~40 cycles per processed byte.
+
+# February 2011.
+#
+# Profiler-assisted and platform-specific optimization resulted in 7%
+# improvement on Coxtex A8 core and ~38 cycles per byte.
+
+# March 2011.
+#
+# Add NEON implementation. On Cortex A8 it was measured to process
+# one byte in 23.3 cycles or ~60% faster than integer-only code.
+
+# August 2012.
+#
+# Improve NEON performance by 12% on Snapdragon S4. In absolute
+# terms it's 22.6 cycles per byte, which is disappointing result.
+# Technical writers asserted that 3-way S4 pipeline can sustain
+# multiple NEON instructions per cycle, but dual NEON issue could
+# not be observed, see http://www.openssl.org/~appro/Snapdragon-S4.html
+# for further details. On side note Cortex-A15 processes one byte in
+# 16 cycles.
+
+# Byte order [in]dependence. =========================================
+#
+# Originally caller was expected to maintain specific *dword* order in
+# h[0-7], namely with most significant dword at *lower* address, which
+# was reflected in below two parameters as 0 and 4. Now caller is
+# expected to maintain native byte order for whole 64-bit values.
+$hi="HI";
+$lo="LO";
+# ====================================================================
+
+while (($output=shift) && ($output!~/^\w[\w\-]*\.\w+$/)) {}
+open STDOUT,">$output";
+
+$ctx="r0";     # parameter block
+$inp="r1";
+$len="r2";
+
+$Tlo="r3";
+$Thi="r4";
+$Alo="r5";
+$Ahi="r6";
+$Elo="r7";
+$Ehi="r8";
+$t0="r9";
+$t1="r10";
+$t2="r11";
+$t3="r12";
+############   r13 is stack pointer
+$Ktbl="r14";
+############   r15 is program counter
+
+$Aoff=8*0;
+$Boff=8*1;
+$Coff=8*2;
+$Doff=8*3;
+$Eoff=8*4;
+$Foff=8*5;
+$Goff=8*6;
+$Hoff=8*7;
+$Xoff=8*8;
+
+sub BODY_00_15() {
+my $magic = shift;
+$code.=<<___;
+       @ Sigma1(x)     (ROTR((x),14) ^ ROTR((x),18)  ^ ROTR((x),41))
+       @ LO            lo>>14^hi<<18 ^ lo>>18^hi<<14 ^ hi>>9^lo<<23
+       @ HI            hi>>14^lo<<18 ^ hi>>18^lo<<14 ^ lo>>9^hi<<23
+       mov     $t0,$Elo,lsr#14
+       str     $Tlo,[sp,#$Xoff+0]
+       mov     $t1,$Ehi,lsr#14
+       str     $Thi,[sp,#$Xoff+4]
+       eor     $t0,$t0,$Ehi,lsl#18
+       ldr     $t2,[sp,#$Hoff+0]       @ h.lo
+       eor     $t1,$t1,$Elo,lsl#18
+       ldr     $t3,[sp,#$Hoff+4]       @ h.hi
+       eor     $t0,$t0,$Elo,lsr#18
+       eor     $t1,$t1,$Ehi,lsr#18
+       eor     $t0,$t0,$Ehi,lsl#14
+       eor     $t1,$t1,$Elo,lsl#14
+       eor     $t0,$t0,$Ehi,lsr#9
+       eor     $t1,$t1,$Elo,lsr#9
+       eor     $t0,$t0,$Elo,lsl#23
+       eor     $t1,$t1,$Ehi,lsl#23     @ Sigma1(e)
+       adds    $Tlo,$Tlo,$t0
+       ldr     $t0,[sp,#$Foff+0]       @ f.lo
+       adc     $Thi,$Thi,$t1           @ T += Sigma1(e)
+       ldr     $t1,[sp,#$Foff+4]       @ f.hi
+       adds    $Tlo,$Tlo,$t2
+       ldr     $t2,[sp,#$Goff+0]       @ g.lo
+       adc     $Thi,$Thi,$t3           @ T += h
+       ldr     $t3,[sp,#$Goff+4]       @ g.hi
+
+       eor     $t0,$t0,$t2
+       str     $Elo,[sp,#$Eoff+0]
+       eor     $t1,$t1,$t3
+       str     $Ehi,[sp,#$Eoff+4]
+       and     $t0,$t0,$Elo
+       str     $Alo,[sp,#$Aoff+0]
+       and     $t1,$t1,$Ehi
+       str     $Ahi,[sp,#$Aoff+4]
+       eor     $t0,$t0,$t2
+       ldr     $t2,[$Ktbl,#$lo]        @ K[i].lo
+       eor     $t1,$t1,$t3             @ Ch(e,f,g)
+       ldr     $t3,[$Ktbl,#$hi]        @ K[i].hi
+
+       adds    $Tlo,$Tlo,$t0
+       ldr     $Elo,[sp,#$Doff+0]      @ d.lo
+       adc     $Thi,$Thi,$t1           @ T += Ch(e,f,g)
+       ldr     $Ehi,[sp,#$Doff+4]      @ d.hi
+       adds    $Tlo,$Tlo,$t2
+       and     $t0,$t2,#0xff
+       adc     $Thi,$Thi,$t3           @ T += K[i]
+       adds    $Elo,$Elo,$Tlo
+       ldr     $t2,[sp,#$Boff+0]       @ b.lo
+       adc     $Ehi,$Ehi,$Thi          @ d += T
+       teq     $t0,#$magic
+
+       ldr     $t3,[sp,#$Coff+0]       @ c.lo
+#if __ARM_ARCH__>=7
+       it      eq                      @ Thumb2 thing, sanity check in ARM
+#endif
+       orreq   $Ktbl,$Ktbl,#1
+       @ Sigma0(x)     (ROTR((x),28) ^ ROTR((x),34) ^ ROTR((x),39))
+       @ LO            lo>>28^hi<<4  ^ hi>>2^lo<<30 ^ hi>>7^lo<<25
+       @ HI            hi>>28^lo<<4  ^ lo>>2^hi<<30 ^ lo>>7^hi<<25
+       mov     $t0,$Alo,lsr#28
+       mov     $t1,$Ahi,lsr#28
+       eor     $t0,$t0,$Ahi,lsl#4
+       eor     $t1,$t1,$Alo,lsl#4
+       eor     $t0,$t0,$Ahi,lsr#2
+       eor     $t1,$t1,$Alo,lsr#2
+       eor     $t0,$t0,$Alo,lsl#30
+       eor     $t1,$t1,$Ahi,lsl#30
+       eor     $t0,$t0,$Ahi,lsr#7
+       eor     $t1,$t1,$Alo,lsr#7
+       eor     $t0,$t0,$Alo,lsl#25
+       eor     $t1,$t1,$Ahi,lsl#25     @ Sigma0(a)
+       adds    $Tlo,$Tlo,$t0
+       and     $t0,$Alo,$t2
+       adc     $Thi,$Thi,$t1           @ T += Sigma0(a)
+
+       ldr     $t1,[sp,#$Boff+4]       @ b.hi
+       orr     $Alo,$Alo,$t2
+       ldr     $t2,[sp,#$Coff+4]       @ c.hi
+       and     $Alo,$Alo,$t3
+       and     $t3,$Ahi,$t1
+       orr     $Ahi,$Ahi,$t1
+       orr     $Alo,$Alo,$t0           @ Maj(a,b,c).lo
+       and     $Ahi,$Ahi,$t2
+       adds    $Alo,$Alo,$Tlo
+       orr     $Ahi,$Ahi,$t3           @ Maj(a,b,c).hi
+       sub     sp,sp,#8
+       adc     $Ahi,$Ahi,$Thi          @ h += T
+       tst     $Ktbl,#1
+       add     $Ktbl,$Ktbl,#8
+___
+}
+$code=<<___;
+#ifndef __KERNEL__
+# include "arm_arch.h"
+# define VFP_ABI_PUSH  vstmdb  sp!,{d8-d15}
+# define VFP_ABI_POP   vldmia  sp!,{d8-d15}
+#else
+# define __ARM_ARCH__ __LINUX_ARM_ARCH__
+# define __ARM_MAX_ARCH__ 7
+# define VFP_ABI_PUSH
+# define VFP_ABI_POP
+#endif
+
+#ifdef __ARMEL__
+# define LO 0
+# define HI 4
+# define WORD64(hi0,lo0,hi1,lo1)       .word   lo0,hi0, lo1,hi1
+#else
+# define HI 0
+# define LO 4
+# define WORD64(hi0,lo0,hi1,lo1)       .word   hi0,lo0, hi1,lo1
+#endif
+
+.text
+#if __ARM_ARCH__<7
+.code  32
+#else
+.syntax unified
+# ifdef __thumb2__
+#  define adrl adr
+.thumb
+# else
+.code   32
+# endif
+#endif
+
+.type  K512,%object
+.align 5
+K512:
+WORD64(0x428a2f98,0xd728ae22, 0x71374491,0x23ef65cd)
+WORD64(0xb5c0fbcf,0xec4d3b2f, 0xe9b5dba5,0x8189dbbc)
+WORD64(0x3956c25b,0xf348b538, 0x59f111f1,0xb605d019)
+WORD64(0x923f82a4,0xaf194f9b, 0xab1c5ed5,0xda6d8118)
+WORD64(0xd807aa98,0xa3030242, 0x12835b01,0x45706fbe)
+WORD64(0x243185be,0x4ee4b28c, 0x550c7dc3,0xd5ffb4e2)
+WORD64(0x72be5d74,0xf27b896f, 0x80deb1fe,0x3b1696b1)
+WORD64(0x9bdc06a7,0x25c71235, 0xc19bf174,0xcf692694)
+WORD64(0xe49b69c1,0x9ef14ad2, 0xefbe4786,0x384f25e3)
+WORD64(0x0fc19dc6,0x8b8cd5b5, 0x240ca1cc,0x77ac9c65)
+WORD64(0x2de92c6f,0x592b0275, 0x4a7484aa,0x6ea6e483)
+WORD64(0x5cb0a9dc,0xbd41fbd4, 0x76f988da,0x831153b5)
+WORD64(0x983e5152,0xee66dfab, 0xa831c66d,0x2db43210)
+WORD64(0xb00327c8,0x98fb213f, 0xbf597fc7,0xbeef0ee4)
+WORD64(0xc6e00bf3,0x3da88fc2, 0xd5a79147,0x930aa725)
+WORD64(0x06ca6351,0xe003826f, 0x14292967,0x0a0e6e70)
+WORD64(0x27b70a85,0x46d22ffc, 0x2e1b2138,0x5c26c926)
+WORD64(0x4d2c6dfc,0x5ac42aed, 0x53380d13,0x9d95b3df)
+WORD64(0x650a7354,0x8baf63de, 0x766a0abb,0x3c77b2a8)
+WORD64(0x81c2c92e,0x47edaee6, 0x92722c85,0x1482353b)
+WORD64(0xa2bfe8a1,0x4cf10364, 0xa81a664b,0xbc423001)
+WORD64(0xc24b8b70,0xd0f89791, 0xc76c51a3,0x0654be30)
+WORD64(0xd192e819,0xd6ef5218, 0xd6990624,0x5565a910)
+WORD64(0xf40e3585,0x5771202a, 0x106aa070,0x32bbd1b8)
+WORD64(0x19a4c116,0xb8d2d0c8, 0x1e376c08,0x5141ab53)
+WORD64(0x2748774c,0xdf8eeb99, 0x34b0bcb5,0xe19b48a8)
+WORD64(0x391c0cb3,0xc5c95a63, 0x4ed8aa4a,0xe3418acb)
+WORD64(0x5b9cca4f,0x7763e373, 0x682e6ff3,0xd6b2b8a3)
+WORD64(0x748f82ee,0x5defb2fc, 0x78a5636f,0x43172f60)
+WORD64(0x84c87814,0xa1f0ab72, 0x8cc70208,0x1a6439ec)
+WORD64(0x90befffa,0x23631e28, 0xa4506ceb,0xde82bde9)
+WORD64(0xbef9a3f7,0xb2c67915, 0xc67178f2,0xe372532b)
+WORD64(0xca273ece,0xea26619c, 0xd186b8c7,0x21c0c207)
+WORD64(0xeada7dd6,0xcde0eb1e, 0xf57d4f7f,0xee6ed178)
+WORD64(0x06f067aa,0x72176fba, 0x0a637dc5,0xa2c898a6)
+WORD64(0x113f9804,0xbef90dae, 0x1b710b35,0x131c471b)
+WORD64(0x28db77f5,0x23047d84, 0x32caab7b,0x40c72493)
+WORD64(0x3c9ebe0a,0x15c9bebc, 0x431d67c4,0x9c100d4c)
+WORD64(0x4cc5d4be,0xcb3e42b6, 0x597f299c,0xfc657e2a)
+WORD64(0x5fcb6fab,0x3ad6faec, 0x6c44198c,0x4a475817)
+.size  K512,.-K512
+#if __ARM_MAX_ARCH__>=7 && !defined(__KERNEL__)
+.LOPENSSL_armcap:
+.word  OPENSSL_armcap_P-sha512_block_data_order
+.skip  32-4
+#else
+.skip  32
+#endif
+
+.global        sha512_block_data_order
+.type  sha512_block_data_order,%function
+sha512_block_data_order:
+#if __ARM_ARCH__<7
+       sub     r3,pc,#8                @ sha512_block_data_order
+#else
+       adr     r3,sha512_block_data_order
+#endif
+#if __ARM_MAX_ARCH__>=7 && !defined(__KERNEL__)
+       ldr     r12,.LOPENSSL_armcap
+       ldr     r12,[r3,r12]            @ OPENSSL_armcap_P
+       tst     r12,#1
+       bne     .LNEON
+#endif
+       add     $len,$inp,$len,lsl#7    @ len to point at the end of inp
+       stmdb   sp!,{r4-r12,lr}
+       sub     $Ktbl,r3,#672           @ K512
+       sub     sp,sp,#9*8
+
+       ldr     $Elo,[$ctx,#$Eoff+$lo]
+       ldr     $Ehi,[$ctx,#$Eoff+$hi]
+       ldr     $t0, [$ctx,#$Goff+$lo]
+       ldr     $t1, [$ctx,#$Goff+$hi]
+       ldr     $t2, [$ctx,#$Hoff+$lo]
+       ldr     $t3, [$ctx,#$Hoff+$hi]
+.Loop:
+       str     $t0, [sp,#$Goff+0]
+       str     $t1, [sp,#$Goff+4]
+       str     $t2, [sp,#$Hoff+0]
+       str     $t3, [sp,#$Hoff+4]
+       ldr     $Alo,[$ctx,#$Aoff+$lo]
+       ldr     $Ahi,[$ctx,#$Aoff+$hi]
+       ldr     $Tlo,[$ctx,#$Boff+$lo]
+       ldr     $Thi,[$ctx,#$Boff+$hi]
+       ldr     $t0, [$ctx,#$Coff+$lo]
+       ldr     $t1, [$ctx,#$Coff+$hi]
+       ldr     $t2, [$ctx,#$Doff+$lo]
+       ldr     $t3, [$ctx,#$Doff+$hi]
+       str     $Tlo,[sp,#$Boff+0]
+       str     $Thi,[sp,#$Boff+4]
+       str     $t0, [sp,#$Coff+0]
+       str     $t1, [sp,#$Coff+4]
+       str     $t2, [sp,#$Doff+0]
+       str     $t3, [sp,#$Doff+4]
+       ldr     $Tlo,[$ctx,#$Foff+$lo]
+       ldr     $Thi,[$ctx,#$Foff+$hi]
+       str     $Tlo,[sp,#$Foff+0]
+       str     $Thi,[sp,#$Foff+4]
+
+.L00_15:
+#if __ARM_ARCH__<7
+       ldrb    $Tlo,[$inp,#7]
+       ldrb    $t0, [$inp,#6]
+       ldrb    $t1, [$inp,#5]
+       ldrb    $t2, [$inp,#4]
+       ldrb    $Thi,[$inp,#3]
+       ldrb    $t3, [$inp,#2]
+       orr     $Tlo,$Tlo,$t0,lsl#8
+       ldrb    $t0, [$inp,#1]
+       orr     $Tlo,$Tlo,$t1,lsl#16
+       ldrb    $t1, [$inp],#8
+       orr     $Tlo,$Tlo,$t2,lsl#24
+       orr     $Thi,$Thi,$t3,lsl#8
+       orr     $Thi,$Thi,$t0,lsl#16
+       orr     $Thi,$Thi,$t1,lsl#24
+#else
+       ldr     $Tlo,[$inp,#4]
+       ldr     $Thi,[$inp],#8
+#ifdef __ARMEL__
+       rev     $Tlo,$Tlo
+       rev     $Thi,$Thi
+#endif
+#endif
+___
+       &BODY_00_15(0x94);
+$code.=<<___;
+       tst     $Ktbl,#1
+       beq     .L00_15
+       ldr     $t0,[sp,#`$Xoff+8*(16-1)`+0]
+       ldr     $t1,[sp,#`$Xoff+8*(16-1)`+4]
+       bic     $Ktbl,$Ktbl,#1
+.L16_79:
+       @ sigma0(x)     (ROTR((x),1)  ^ ROTR((x),8)  ^ ((x)>>7))
+       @ LO            lo>>1^hi<<31  ^ lo>>8^hi<<24 ^ lo>>7^hi<<25
+       @ HI            hi>>1^lo<<31  ^ hi>>8^lo<<24 ^ hi>>7
+       mov     $Tlo,$t0,lsr#1
+       ldr     $t2,[sp,#`$Xoff+8*(16-14)`+0]
+       mov     $Thi,$t1,lsr#1
+       ldr     $t3,[sp,#`$Xoff+8*(16-14)`+4]
+       eor     $Tlo,$Tlo,$t1,lsl#31
+       eor     $Thi,$Thi,$t0,lsl#31
+       eor     $Tlo,$Tlo,$t0,lsr#8
+       eor     $Thi,$Thi,$t1,lsr#8
+       eor     $Tlo,$Tlo,$t1,lsl#24
+       eor     $Thi,$Thi,$t0,lsl#24
+       eor     $Tlo,$Tlo,$t0,lsr#7
+       eor     $Thi,$Thi,$t1,lsr#7
+       eor     $Tlo,$Tlo,$t1,lsl#25
+
+       @ sigma1(x)     (ROTR((x),19) ^ ROTR((x),61) ^ ((x)>>6))
+       @ LO            lo>>19^hi<<13 ^ hi>>29^lo<<3 ^ lo>>6^hi<<26
+       @ HI            hi>>19^lo<<13 ^ lo>>29^hi<<3 ^ hi>>6
+       mov     $t0,$t2,lsr#19
+       mov     $t1,$t3,lsr#19
+       eor     $t0,$t0,$t3,lsl#13
+       eor     $t1,$t1,$t2,lsl#13
+       eor     $t0,$t0,$t3,lsr#29
+       eor     $t1,$t1,$t2,lsr#29
+       eor     $t0,$t0,$t2,lsl#3
+       eor     $t1,$t1,$t3,lsl#3
+       eor     $t0,$t0,$t2,lsr#6
+       eor     $t1,$t1,$t3,lsr#6
+       ldr     $t2,[sp,#`$Xoff+8*(16-9)`+0]
+       eor     $t0,$t0,$t3,lsl#26
+
+       ldr     $t3,[sp,#`$Xoff+8*(16-9)`+4]
+       adds    $Tlo,$Tlo,$t0
+       ldr     $t0,[sp,#`$Xoff+8*16`+0]
+       adc     $Thi,$Thi,$t1
+
+       ldr     $t1,[sp,#`$Xoff+8*16`+4]
+       adds    $Tlo,$Tlo,$t2
+       adc     $Thi,$Thi,$t3
+       adds    $Tlo,$Tlo,$t0
+       adc     $Thi,$Thi,$t1
+___
+       &BODY_00_15(0x17);
+$code.=<<___;
+#if __ARM_ARCH__>=7
+       ittt    eq                      @ Thumb2 thing, sanity check in ARM
+#endif
+       ldreq   $t0,[sp,#`$Xoff+8*(16-1)`+0]
+       ldreq   $t1,[sp,#`$Xoff+8*(16-1)`+4]
+       beq     .L16_79
+       bic     $Ktbl,$Ktbl,#1
+
+       ldr     $Tlo,[sp,#$Boff+0]
+       ldr     $Thi,[sp,#$Boff+4]
+       ldr     $t0, [$ctx,#$Aoff+$lo]
+       ldr     $t1, [$ctx,#$Aoff+$hi]
+       ldr     $t2, [$ctx,#$Boff+$lo]
+       ldr     $t3, [$ctx,#$Boff+$hi]
+       adds    $t0,$Alo,$t0
+       str     $t0, [$ctx,#$Aoff+$lo]
+       adc     $t1,$Ahi,$t1
+       str     $t1, [$ctx,#$Aoff+$hi]
+       adds    $t2,$Tlo,$t2
+       str     $t2, [$ctx,#$Boff+$lo]
+       adc     $t3,$Thi,$t3
+       str     $t3, [$ctx,#$Boff+$hi]
+
+       ldr     $Alo,[sp,#$Coff+0]
+       ldr     $Ahi,[sp,#$Coff+4]
+       ldr     $Tlo,[sp,#$Doff+0]
+       ldr     $Thi,[sp,#$Doff+4]
+       ldr     $t0, [$ctx,#$Coff+$lo]
+       ldr     $t1, [$ctx,#$Coff+$hi]
+       ldr     $t2, [$ctx,#$Doff+$lo]
+       ldr     $t3, [$ctx,#$Doff+$hi]
+       adds    $t0,$Alo,$t0
+       str     $t0, [$ctx,#$Coff+$lo]
+       adc     $t1,$Ahi,$t1
+       str     $t1, [$ctx,#$Coff+$hi]
+       adds    $t2,$Tlo,$t2
+       str     $t2, [$ctx,#$Doff+$lo]
+       adc     $t3,$Thi,$t3
+       str     $t3, [$ctx,#$Doff+$hi]
+
+       ldr     $Tlo,[sp,#$Foff+0]
+       ldr     $Thi,[sp,#$Foff+4]
+       ldr     $t0, [$ctx,#$Eoff+$lo]
+       ldr     $t1, [$ctx,#$Eoff+$hi]
+       ldr     $t2, [$ctx,#$Foff+$lo]
+       ldr     $t3, [$ctx,#$Foff+$hi]
+       adds    $Elo,$Elo,$t0
+       str     $Elo,[$ctx,#$Eoff+$lo]
+       adc     $Ehi,$Ehi,$t1
+       str     $Ehi,[$ctx,#$Eoff+$hi]
+       adds    $t2,$Tlo,$t2
+       str     $t2, [$ctx,#$Foff+$lo]
+       adc     $t3,$Thi,$t3
+       str     $t3, [$ctx,#$Foff+$hi]
+
+       ldr     $Alo,[sp,#$Goff+0]
+       ldr     $Ahi,[sp,#$Goff+4]
+       ldr     $Tlo,[sp,#$Hoff+0]
+       ldr     $Thi,[sp,#$Hoff+4]
+       ldr     $t0, [$ctx,#$Goff+$lo]
+       ldr     $t1, [$ctx,#$Goff+$hi]
+       ldr     $t2, [$ctx,#$Hoff+$lo]
+       ldr     $t3, [$ctx,#$Hoff+$hi]
+       adds    $t0,$Alo,$t0
+       str     $t0, [$ctx,#$Goff+$lo]
+       adc     $t1,$Ahi,$t1
+       str     $t1, [$ctx,#$Goff+$hi]
+       adds    $t2,$Tlo,$t2
+       str     $t2, [$ctx,#$Hoff+$lo]
+       adc     $t3,$Thi,$t3
+       str     $t3, [$ctx,#$Hoff+$hi]
+
+       add     sp,sp,#640
+       sub     $Ktbl,$Ktbl,#640
+
+       teq     $inp,$len
+       bne     .Loop
+
+       add     sp,sp,#8*9              @ destroy frame
+#if __ARM_ARCH__>=5
+       ldmia   sp!,{r4-r12,pc}
+#else
+       ldmia   sp!,{r4-r12,lr}
+       tst     lr,#1
+       moveq   pc,lr                   @ be binary compatible with V4, yet
+       bx      lr                      @ interoperable with Thumb ISA:-)
+#endif
+.size  sha512_block_data_order,.-sha512_block_data_order
+___
+
+{
+my @Sigma0=(28,34,39);
+my @Sigma1=(14,18,41);
+my @sigma0=(1, 8, 7);
+my @sigma1=(19,61,6);
+
+my $Ktbl="r3";
+my $cnt="r12"; # volatile register known as ip, intra-procedure-call scratch
+
+my @X=map("d$_",(0..15));
+my @V=($A,$B,$C,$D,$E,$F,$G,$H)=map("d$_",(16..23));
+
+sub NEON_00_15() {
+my $i=shift;
+my ($a,$b,$c,$d,$e,$f,$g,$h)=@_;
+my ($t0,$t1,$t2,$T1,$K,$Ch,$Maj)=map("d$_",(24..31));  # temps
+
+$code.=<<___ if ($i<16 || $i&1);
+       vshr.u64        $t0,$e,#@Sigma1[0]      @ $i
+#if $i<16
+       vld1.64         {@X[$i%16]},[$inp]!     @ handles unaligned
+#endif
+       vshr.u64        $t1,$e,#@Sigma1[1]
+#if $i>0
+        vadd.i64       $a,$Maj                 @ h+=Maj from the past
+#endif
+       vshr.u64        $t2,$e,#@Sigma1[2]
+___
+$code.=<<___;
+       vld1.64         {$K},[$Ktbl,:64]!       @ K[i++]
+       vsli.64         $t0,$e,#`64-@Sigma1[0]`
+       vsli.64         $t1,$e,#`64-@Sigma1[1]`
+       vmov            $Ch,$e
+       vsli.64         $t2,$e,#`64-@Sigma1[2]`
+#if $i<16 && defined(__ARMEL__)
+       vrev64.8        @X[$i],@X[$i]
+#endif
+       veor            $t1,$t0
+       vbsl            $Ch,$f,$g               @ Ch(e,f,g)
+       vshr.u64        $t0,$a,#@Sigma0[0]
+       veor            $t2,$t1                 @ Sigma1(e)
+       vadd.i64        $T1,$Ch,$h
+       vshr.u64        $t1,$a,#@Sigma0[1]
+       vsli.64         $t0,$a,#`64-@Sigma0[0]`
+       vadd.i64        $T1,$t2
+       vshr.u64        $t2,$a,#@Sigma0[2]
+       vadd.i64        $K,@X[$i%16]
+       vsli.64         $t1,$a,#`64-@Sigma0[1]`
+       veor            $Maj,$a,$b
+       vsli.64         $t2,$a,#`64-@Sigma0[2]`
+       veor            $h,$t0,$t1
+       vadd.i64        $T1,$K
+       vbsl            $Maj,$c,$b              @ Maj(a,b,c)
+       veor            $h,$t2                  @ Sigma0(a)
+       vadd.i64        $d,$T1
+       vadd.i64        $Maj,$T1
+       @ vadd.i64      $h,$Maj
+___
+}
+
+sub NEON_16_79() {
+my $i=shift;
+
+if ($i&1)      { &NEON_00_15($i,@_); return; }
+
+# 2x-vectorized, therefore runs every 2nd round
+my @X=map("q$_",(0..7));                       # view @X as 128-bit vector
+my ($t0,$t1,$s0,$s1) = map("q$_",(12..15));    # temps
+my ($d0,$d1,$d2) = map("d$_",(24..26));                # temps from NEON_00_15
+my $e=@_[4];                                   # $e from NEON_00_15
+$i /= 2;
+$code.=<<___;
+       vshr.u64        $t0,@X[($i+7)%8],#@sigma1[0]
+       vshr.u64        $t1,@X[($i+7)%8],#@sigma1[1]
+        vadd.i64       @_[0],d30                       @ h+=Maj from the past
+       vshr.u64        $s1,@X[($i+7)%8],#@sigma1[2]
+       vsli.64         $t0,@X[($i+7)%8],#`64-@sigma1[0]`
+       vext.8          $s0,@X[$i%8],@X[($i+1)%8],#8    @ X[i+1]
+       vsli.64         $t1,@X[($i+7)%8],#`64-@sigma1[1]`
+       veor            $s1,$t0
+       vshr.u64        $t0,$s0,#@sigma0[0]
+       veor            $s1,$t1                         @ sigma1(X[i+14])
+       vshr.u64        $t1,$s0,#@sigma0[1]
+       vadd.i64        @X[$i%8],$s1
+       vshr.u64        $s1,$s0,#@sigma0[2]
+       vsli.64         $t0,$s0,#`64-@sigma0[0]`
+       vsli.64         $t1,$s0,#`64-@sigma0[1]`
+       vext.8          $s0,@X[($i+4)%8],@X[($i+5)%8],#8        @ X[i+9]
+       veor            $s1,$t0
+       vshr.u64        $d0,$e,#@Sigma1[0]              @ from NEON_00_15
+       vadd.i64        @X[$i%8],$s0
+       vshr.u64        $d1,$e,#@Sigma1[1]              @ from NEON_00_15
+       veor            $s1,$t1                         @ sigma0(X[i+1])
+       vshr.u64        $d2,$e,#@Sigma1[2]              @ from NEON_00_15
+       vadd.i64        @X[$i%8],$s1
+___
+       &NEON_00_15(2*$i,@_);
+}
+
+$code.=<<___;
+#if __ARM_MAX_ARCH__>=7
+.arch  armv7-a
+.fpu   neon
+
+.global        sha512_block_data_order_neon
+.type  sha512_block_data_order_neon,%function
+.align 4
+sha512_block_data_order_neon:
+.LNEON:
+       dmb                             @ errata #451034 on early Cortex A8
+       add     $len,$inp,$len,lsl#7    @ len to point at the end of inp
+       VFP_ABI_PUSH
+       adrl    $Ktbl,K512
+       vldmia  $ctx,{$A-$H}            @ load context
+.Loop_neon:
+___
+for($i=0;$i<16;$i++)   { &NEON_00_15($i,@V); unshift(@V,pop(@V)); }
+$code.=<<___;
+       mov             $cnt,#4
+.L16_79_neon:
+       subs            $cnt,#1
+___
+for(;$i<32;$i++)       { &NEON_16_79($i,@V); unshift(@V,pop(@V)); }
+$code.=<<___;
+       bne             .L16_79_neon
+
+        vadd.i64       $A,d30          @ h+=Maj from the past
+       vldmia          $ctx,{d24-d31}  @ load context to temp
+       vadd.i64        q8,q12          @ vectorized accumulate
+       vadd.i64        q9,q13
+       vadd.i64        q10,q14
+       vadd.i64        q11,q15
+       vstmia          $ctx,{$A-$H}    @ save context
+       teq             $inp,$len
+       sub             $Ktbl,#640      @ rewind K512
+       bne             .Loop_neon
+
+       VFP_ABI_POP
+       ret                             @ bx lr
+.size  sha512_block_data_order_neon,.-sha512_block_data_order_neon
+#endif
+___
+}
+$code.=<<___;
+.asciz "SHA512 block transform for ARMv4/NEON, CRYPTOGAMS by <appro\@openssl.org>"
+.align 2
+#if __ARM_MAX_ARCH__>=7 && !defined(__KERNEL__)
+.comm  OPENSSL_armcap_P,4,4
+#endif
+___
+
+$code =~ s/\`([^\`]*)\`/eval $1/gem;
+$code =~ s/\bbx\s+lr\b/.word\t0xe12fff1e/gm;   # make it possible to compile with -march=armv4
+$code =~ s/\bret\b/bx  lr/gm;
+
+open SELF,$0;
+while(<SELF>) {
+       next if (/^#!/);
+       last if (!s/^#/@/ and !/^$/);
+       print;
+}
+close SELF;
+
+print $code;
+close STDOUT; # enforce flush
diff --git a/arch/arm/crypto/sha512-armv7-neon.S b/arch/arm/crypto/sha512-armv7-neon.S
deleted file mode 100644 (file)
index fe99472..0000000
+++ /dev/null
@@ -1,455 +0,0 @@
-/* sha512-armv7-neon.S  -  ARM/NEON assembly implementation of SHA-512 transform
- *
- * Copyright © 2013-2014 Jussi Kivilinna <jussi.kivilinna@iki.fi>
- *
- * 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/linkage.h>
-
-
-.syntax unified
-.code   32
-.fpu neon
-
-.text
-
-/* structure of SHA512_CONTEXT */
-#define hd_a 0
-#define hd_b ((hd_a) + 8)
-#define hd_c ((hd_b) + 8)
-#define hd_d ((hd_c) + 8)
-#define hd_e ((hd_d) + 8)
-#define hd_f ((hd_e) + 8)
-#define hd_g ((hd_f) + 8)
-
-/* register macros */
-#define RK %r2
-
-#define RA d0
-#define RB d1
-#define RC d2
-#define RD d3
-#define RE d4
-#define RF d5
-#define RG d6
-#define RH d7
-
-#define RT0 d8
-#define RT1 d9
-#define RT2 d10
-#define RT3 d11
-#define RT4 d12
-#define RT5 d13
-#define RT6 d14
-#define RT7 d15
-
-#define RT01q q4
-#define RT23q q5
-#define RT45q q6
-#define RT67q q7
-
-#define RW0 d16
-#define RW1 d17
-#define RW2 d18
-#define RW3 d19
-#define RW4 d20
-#define RW5 d21
-#define RW6 d22
-#define RW7 d23
-#define RW8 d24
-#define RW9 d25
-#define RW10 d26
-#define RW11 d27
-#define RW12 d28
-#define RW13 d29
-#define RW14 d30
-#define RW15 d31
-
-#define RW01q q8
-#define RW23q q9
-#define RW45q q10
-#define RW67q q11
-#define RW89q q12
-#define RW1011q q13
-#define RW1213q q14
-#define RW1415q q15
-
-/***********************************************************************
- * ARM assembly implementation of sha512 transform
- ***********************************************************************/
-#define rounds2_0_63(ra, rb, rc, rd, re, rf, rg, rh, rw0, rw1, rw01q, rw2, \
-                     rw23q, rw1415q, rw9, rw10, interleave_op, arg1) \
-       /* t1 = h + Sum1 (e) + Ch (e, f, g) + k[t] + w[t]; */ \
-       vshr.u64 RT2, re, #14; \
-       vshl.u64 RT3, re, #64 - 14; \
-       interleave_op(arg1); \
-       vshr.u64 RT4, re, #18; \
-       vshl.u64 RT5, re, #64 - 18; \
-       vld1.64 {RT0}, [RK]!; \
-       veor.64 RT23q, RT23q, RT45q; \
-       vshr.u64 RT4, re, #41; \
-       vshl.u64 RT5, re, #64 - 41; \
-       vadd.u64 RT0, RT0, rw0; \
-       veor.64 RT23q, RT23q, RT45q; \
-       vmov.64 RT7, re; \
-       veor.64 RT1, RT2, RT3; \
-       vbsl.64 RT7, rf, rg; \
-       \
-       vadd.u64 RT1, RT1, rh; \
-       vshr.u64 RT2, ra, #28; \
-       vshl.u64 RT3, ra, #64 - 28; \
-       vadd.u64 RT1, RT1, RT0; \
-       vshr.u64 RT4, ra, #34; \
-       vshl.u64 RT5, ra, #64 - 34; \
-       vadd.u64 RT1, RT1, RT7; \
-       \
-       /* h = Sum0 (a) + Maj (a, b, c); */ \
-       veor.64 RT23q, RT23q, RT45q; \
-       vshr.u64 RT4, ra, #39; \
-       vshl.u64 RT5, ra, #64 - 39; \
-       veor.64 RT0, ra, rb; \
-       veor.64 RT23q, RT23q, RT45q; \
-       vbsl.64 RT0, rc, rb; \
-       vadd.u64 rd, rd, RT1; /* d+=t1; */ \
-       veor.64 rh, RT2, RT3; \
-       \
-       /* t1 = g + Sum1 (d) + Ch (d, e, f) + k[t] + w[t]; */ \
-       vshr.u64 RT2, rd, #14; \
-       vshl.u64 RT3, rd, #64 - 14; \
-       vadd.u64 rh, rh, RT0; \
-       vshr.u64 RT4, rd, #18; \
-       vshl.u64 RT5, rd, #64 - 18; \
-       vadd.u64 rh, rh, RT1; /* h+=t1; */ \
-       vld1.64 {RT0}, [RK]!; \
-       veor.64 RT23q, RT23q, RT45q; \
-       vshr.u64 RT4, rd, #41; \
-       vshl.u64 RT5, rd, #64 - 41; \
-       vadd.u64 RT0, RT0, rw1; \
-       veor.64 RT23q, RT23q, RT45q; \
-       vmov.64 RT7, rd; \
-       veor.64 RT1, RT2, RT3; \
-       vbsl.64 RT7, re, rf; \
-       \
-       vadd.u64 RT1, RT1, rg; \
-       vshr.u64 RT2, rh, #28; \
-       vshl.u64 RT3, rh, #64 - 28; \
-       vadd.u64 RT1, RT1, RT0; \
-       vshr.u64 RT4, rh, #34; \
-       vshl.u64 RT5, rh, #64 - 34; \
-       vadd.u64 RT1, RT1, RT7; \
-       \
-       /* g = Sum0 (h) + Maj (h, a, b); */ \
-       veor.64 RT23q, RT23q, RT45q; \
-       vshr.u64 RT4, rh, #39; \
-       vshl.u64 RT5, rh, #64 - 39; \
-       veor.64 RT0, rh, ra; \
-       veor.64 RT23q, RT23q, RT45q; \
-       vbsl.64 RT0, rb, ra; \
-       vadd.u64 rc, rc, RT1; /* c+=t1; */ \
-       veor.64 rg, RT2, RT3; \
-       \
-       /* w[0] += S1 (w[14]) + w[9] + S0 (w[1]); */ \
-       /* w[1] += S1 (w[15]) + w[10] + S0 (w[2]); */ \
-       \
-       /**** S0(w[1:2]) */ \
-       \
-       /* w[0:1] += w[9:10] */ \
-       /* RT23q = rw1:rw2 */ \
-       vext.u64 RT23q, rw01q, rw23q, #1; \
-       vadd.u64 rw0, rw9; \
-       vadd.u64 rg, rg, RT0; \
-       vadd.u64 rw1, rw10;\
-       vadd.u64 rg, rg, RT1; /* g+=t1; */ \
-       \
-       vshr.u64 RT45q, RT23q, #1; \
-       vshl.u64 RT67q, RT23q, #64 - 1; \
-       vshr.u64 RT01q, RT23q, #8; \
-       veor.u64 RT45q, RT45q, RT67q; \
-       vshl.u64 RT67q, RT23q, #64 - 8; \
-       veor.u64 RT45q, RT45q, RT01q; \
-       vshr.u64 RT01q, RT23q, #7; \
-       veor.u64 RT45q, RT45q, RT67q; \
-       \
-       /**** S1(w[14:15]) */ \
-       vshr.u64 RT23q, rw1415q, #6; \
-       veor.u64 RT01q, RT01q, RT45q; \
-       vshr.u64 RT45q, rw1415q, #19; \
-       vshl.u64 RT67q, rw1415q, #64 - 19; \
-       veor.u64 RT23q, RT23q, RT45q; \
-       vshr.u64 RT45q, rw1415q, #61; \
-       veor.u64 RT23q, RT23q, RT67q; \
-       vshl.u64 RT67q, rw1415q, #64 - 61; \
-       veor.u64 RT23q, RT23q, RT45q; \
-       vadd.u64 rw01q, RT01q; /* w[0:1] += S(w[1:2]) */ \
-       veor.u64 RT01q, RT23q, RT67q;
-#define vadd_RT01q(rw01q) \
-       /* w[0:1] += S(w[14:15]) */ \
-       vadd.u64 rw01q, RT01q;
-
-#define dummy(_) /*_*/
-
-#define rounds2_64_79(ra, rb, rc, rd, re, rf, rg, rh, rw0, rw1, \
-                     interleave_op1, arg1, interleave_op2, arg2) \
-       /* t1 = h + Sum1 (e) + Ch (e, f, g) + k[t] + w[t]; */ \
-       vshr.u64 RT2, re, #14; \
-       vshl.u64 RT3, re, #64 - 14; \
-       interleave_op1(arg1); \
-       vshr.u64 RT4, re, #18; \
-       vshl.u64 RT5, re, #64 - 18; \
-       interleave_op2(arg2); \
-       vld1.64 {RT0}, [RK]!; \
-       veor.64 RT23q, RT23q, RT45q; \
-       vshr.u64 RT4, re, #41; \
-       vshl.u64 RT5, re, #64 - 41; \
-       vadd.u64 RT0, RT0, rw0; \
-       veor.64 RT23q, RT23q, RT45q; \
-       vmov.64 RT7, re; \
-       veor.64 RT1, RT2, RT3; \
-       vbsl.64 RT7, rf, rg; \
-       \
-       vadd.u64 RT1, RT1, rh; \
-       vshr.u64 RT2, ra, #28; \
-       vshl.u64 RT3, ra, #64 - 28; \
-       vadd.u64 RT1, RT1, RT0; \
-       vshr.u64 RT4, ra, #34; \
-       vshl.u64 RT5, ra, #64 - 34; \
-       vadd.u64 RT1, RT1, RT7; \
-       \
-       /* h = Sum0 (a) + Maj (a, b, c); */ \
-       veor.64 RT23q, RT23q, RT45q; \
-       vshr.u64 RT4, ra, #39; \
-       vshl.u64 RT5, ra, #64 - 39; \
-       veor.64 RT0, ra, rb; \
-       veor.64 RT23q, RT23q, RT45q; \
-       vbsl.64 RT0, rc, rb; \
-       vadd.u64 rd, rd, RT1; /* d+=t1; */ \
-       veor.64 rh, RT2, RT3; \
-       \
-       /* t1 = g + Sum1 (d) + Ch (d, e, f) + k[t] + w[t]; */ \
-       vshr.u64 RT2, rd, #14; \
-       vshl.u64 RT3, rd, #64 - 14; \
-       vadd.u64 rh, rh, RT0; \
-       vshr.u64 RT4, rd, #18; \
-       vshl.u64 RT5, rd, #64 - 18; \
-       vadd.u64 rh, rh, RT1; /* h+=t1; */ \
-       vld1.64 {RT0}, [RK]!; \
-       veor.64 RT23q, RT23q, RT45q; \
-       vshr.u64 RT4, rd, #41; \
-       vshl.u64 RT5, rd, #64 - 41; \
-       vadd.u64 RT0, RT0, rw1; \
-       veor.64 RT23q, RT23q, RT45q; \
-       vmov.64 RT7, rd; \
-       veor.64 RT1, RT2, RT3; \
-       vbsl.64 RT7, re, rf; \
-       \
-       vadd.u64 RT1, RT1, rg; \
-       vshr.u64 RT2, rh, #28; \
-       vshl.u64 RT3, rh, #64 - 28; \
-       vadd.u64 RT1, RT1, RT0; \
-       vshr.u64 RT4, rh, #34; \
-       vshl.u64 RT5, rh, #64 - 34; \
-       vadd.u64 RT1, RT1, RT7; \
-       \
-       /* g = Sum0 (h) + Maj (h, a, b); */ \
-       veor.64 RT23q, RT23q, RT45q; \
-       vshr.u64 RT4, rh, #39; \
-       vshl.u64 RT5, rh, #64 - 39; \
-       veor.64 RT0, rh, ra; \
-       veor.64 RT23q, RT23q, RT45q; \
-       vbsl.64 RT0, rb, ra; \
-       vadd.u64 rc, rc, RT1; /* c+=t1; */ \
-       veor.64 rg, RT2, RT3;
-#define vadd_rg_RT0(rg) \
-       vadd.u64 rg, rg, RT0;
-#define vadd_rg_RT1(rg) \
-       vadd.u64 rg, rg, RT1; /* g+=t1; */
-
-.align 3
-ENTRY(sha512_transform_neon)
-       /* Input:
-        *      %r0: SHA512_CONTEXT
-        *      %r1: data
-        *      %r2: u64 k[] constants
-        *      %r3: nblks
-        */
-       push {%lr};
-
-       mov %lr, #0;
-
-       /* Load context to d0-d7 */
-       vld1.64 {RA-RD}, [%r0]!;
-       vld1.64 {RE-RH}, [%r0];
-       sub %r0, #(4*8);
-
-       /* Load input to w[16], d16-d31 */
-       /* NOTE: Assumes that on ARMv7 unaligned accesses are always allowed. */
-       vld1.64 {RW0-RW3}, [%r1]!;
-       vld1.64 {RW4-RW7}, [%r1]!;
-       vld1.64 {RW8-RW11}, [%r1]!;
-       vld1.64 {RW12-RW15}, [%r1]!;
-#ifdef __ARMEL__
-       /* byteswap */
-       vrev64.8 RW01q, RW01q;
-       vrev64.8 RW23q, RW23q;
-       vrev64.8 RW45q, RW45q;
-       vrev64.8 RW67q, RW67q;
-       vrev64.8 RW89q, RW89q;
-       vrev64.8 RW1011q, RW1011q;
-       vrev64.8 RW1213q, RW1213q;
-       vrev64.8 RW1415q, RW1415q;
-#endif
-
-       /* EABI says that d8-d15 must be preserved by callee. */
-       /*vpush {RT0-RT7};*/
-
-.Loop:
-       rounds2_0_63(RA, RB, RC, RD, RE, RF, RG, RH, RW0, RW1, RW01q, RW2,
-                    RW23q, RW1415q, RW9, RW10, dummy, _);
-       b .Lenter_rounds;
-
-.Loop_rounds:
-       rounds2_0_63(RA, RB, RC, RD, RE, RF, RG, RH, RW0, RW1, RW01q, RW2,
-                    RW23q, RW1415q, RW9, RW10, vadd_RT01q, RW1415q);
-.Lenter_rounds:
-       rounds2_0_63(RG, RH, RA, RB, RC, RD, RE, RF, RW2, RW3, RW23q, RW4,
-                    RW45q, RW01q, RW11, RW12, vadd_RT01q, RW01q);
-       rounds2_0_63(RE, RF, RG, RH, RA, RB, RC, RD, RW4, RW5, RW45q, RW6,
-                    RW67q, RW23q, RW13, RW14, vadd_RT01q, RW23q);
-       rounds2_0_63(RC, RD, RE, RF, RG, RH, RA, RB, RW6, RW7, RW67q, RW8,
-                    RW89q, RW45q, RW15, RW0, vadd_RT01q, RW45q);
-       rounds2_0_63(RA, RB, RC, RD, RE, RF, RG, RH, RW8, RW9, RW89q, RW10,
-                    RW1011q, RW67q, RW1, RW2, vadd_RT01q, RW67q);
-       rounds2_0_63(RG, RH, RA, RB, RC, RD, RE, RF, RW10, RW11, RW1011q, RW12,
-                    RW1213q, RW89q, RW3, RW4, vadd_RT01q, RW89q);
-       add %lr, #16;
-       rounds2_0_63(RE, RF, RG, RH, RA, RB, RC, RD, RW12, RW13, RW1213q, RW14,
-                    RW1415q, RW1011q, RW5, RW6, vadd_RT01q, RW1011q);
-       cmp %lr, #64;
-       rounds2_0_63(RC, RD, RE, RF, RG, RH, RA, RB, RW14, RW15, RW1415q, RW0,
-                    RW01q, RW1213q, RW7, RW8, vadd_RT01q, RW1213q);
-       bne .Loop_rounds;
-
-       subs %r3, #1;
-
-       rounds2_64_79(RA, RB, RC, RD, RE, RF, RG, RH, RW0, RW1,
-                     vadd_RT01q, RW1415q, dummy, _);
-       rounds2_64_79(RG, RH, RA, RB, RC, RD, RE, RF, RW2, RW3,
-                     vadd_rg_RT0, RG, vadd_rg_RT1, RG);
-       beq .Lhandle_tail;
-       vld1.64 {RW0-RW3}, [%r1]!;
-       rounds2_64_79(RE, RF, RG, RH, RA, RB, RC, RD, RW4, RW5,
-                     vadd_rg_RT0, RE, vadd_rg_RT1, RE);
-       rounds2_64_79(RC, RD, RE, RF, RG, RH, RA, RB, RW6, RW7,
-                     vadd_rg_RT0, RC, vadd_rg_RT1, RC);
-#ifdef __ARMEL__
-       vrev64.8 RW01q, RW01q;
-       vrev64.8 RW23q, RW23q;
-#endif
-       vld1.64 {RW4-RW7}, [%r1]!;
-       rounds2_64_79(RA, RB, RC, RD, RE, RF, RG, RH, RW8, RW9,
-                     vadd_rg_RT0, RA, vadd_rg_RT1, RA);
-       rounds2_64_79(RG, RH, RA, RB, RC, RD, RE, RF, RW10, RW11,
-                     vadd_rg_RT0, RG, vadd_rg_RT1, RG);
-#ifdef __ARMEL__
-       vrev64.8 RW45q, RW45q;
-       vrev64.8 RW67q, RW67q;
-#endif
-       vld1.64 {RW8-RW11}, [%r1]!;
-       rounds2_64_79(RE, RF, RG, RH, RA, RB, RC, RD, RW12, RW13,
-                     vadd_rg_RT0, RE, vadd_rg_RT1, RE);
-       rounds2_64_79(RC, RD, RE, RF, RG, RH, RA, RB, RW14, RW15,
-                     vadd_rg_RT0, RC, vadd_rg_RT1, RC);
-#ifdef __ARMEL__
-       vrev64.8 RW89q, RW89q;
-       vrev64.8 RW1011q, RW1011q;
-#endif
-       vld1.64 {RW12-RW15}, [%r1]!;
-       vadd_rg_RT0(RA);
-       vadd_rg_RT1(RA);
-
-       /* Load context */
-       vld1.64 {RT0-RT3}, [%r0]!;
-       vld1.64 {RT4-RT7}, [%r0];
-       sub %r0, #(4*8);
-
-#ifdef __ARMEL__
-       vrev64.8 RW1213q, RW1213q;
-       vrev64.8 RW1415q, RW1415q;
-#endif
-
-       vadd.u64 RA, RT0;
-       vadd.u64 RB, RT1;
-       vadd.u64 RC, RT2;
-       vadd.u64 RD, RT3;
-       vadd.u64 RE, RT4;
-       vadd.u64 RF, RT5;
-       vadd.u64 RG, RT6;
-       vadd.u64 RH, RT7;
-
-       /* Store the first half of context */
-       vst1.64 {RA-RD}, [%r0]!;
-       sub RK, $(8*80);
-       vst1.64 {RE-RH}, [%r0]; /* Store the last half of context */
-       mov %lr, #0;
-       sub %r0, #(4*8);
-
-       b .Loop;
-
-.Lhandle_tail:
-       rounds2_64_79(RE, RF, RG, RH, RA, RB, RC, RD, RW4, RW5,
-                     vadd_rg_RT0, RE, vadd_rg_RT1, RE);
-       rounds2_64_79(RC, RD, RE, RF, RG, RH, RA, RB, RW6, RW7,
-                     vadd_rg_RT0, RC, vadd_rg_RT1, RC);
-       rounds2_64_79(RA, RB, RC, RD, RE, RF, RG, RH, RW8, RW9,
-                     vadd_rg_RT0, RA, vadd_rg_RT1, RA);
-       rounds2_64_79(RG, RH, RA, RB, RC, RD, RE, RF, RW10, RW11,
-                     vadd_rg_RT0, RG, vadd_rg_RT1, RG);
-       rounds2_64_79(RE, RF, RG, RH, RA, RB, RC, RD, RW12, RW13,
-                     vadd_rg_RT0, RE, vadd_rg_RT1, RE);
-       rounds2_64_79(RC, RD, RE, RF, RG, RH, RA, RB, RW14, RW15,
-                     vadd_rg_RT0, RC, vadd_rg_RT1, RC);
-
-       /* Load context to d16-d23 */
-       vld1.64 {RW0-RW3}, [%r0]!;
-       vadd_rg_RT0(RA);
-       vld1.64 {RW4-RW7}, [%r0];
-       vadd_rg_RT1(RA);
-       sub %r0, #(4*8);
-
-       vadd.u64 RA, RW0;
-       vadd.u64 RB, RW1;
-       vadd.u64 RC, RW2;
-       vadd.u64 RD, RW3;
-       vadd.u64 RE, RW4;
-       vadd.u64 RF, RW5;
-       vadd.u64 RG, RW6;
-       vadd.u64 RH, RW7;
-
-       /* Store the first half of context */
-       vst1.64 {RA-RD}, [%r0]!;
-
-       /* Clear used registers */
-       /* d16-d31 */
-       veor.u64 RW01q, RW01q;
-       veor.u64 RW23q, RW23q;
-       veor.u64 RW45q, RW45q;
-       veor.u64 RW67q, RW67q;
-       vst1.64 {RE-RH}, [%r0]; /* Store the last half of context */
-       veor.u64 RW89q, RW89q;
-       veor.u64 RW1011q, RW1011q;
-       veor.u64 RW1213q, RW1213q;
-       veor.u64 RW1415q, RW1415q;
-       /* d8-d15 */
-       /*vpop {RT0-RT7};*/
-       /* d0-d7 (q0-q3) */
-       veor.u64 %q0, %q0;
-       veor.u64 %q1, %q1;
-       veor.u64 %q2, %q2;
-       veor.u64 %q3, %q3;
-
-       pop {%pc};
-ENDPROC(sha512_transform_neon)
diff --git a/arch/arm/crypto/sha512-core.S_shipped b/arch/arm/crypto/sha512-core.S_shipped
new file mode 100644 (file)
index 0000000..3694c4d
--- /dev/null
@@ -0,0 +1,1861 @@
+
+@ ====================================================================
+@ Written by Andy Polyakov <appro@openssl.org> for the OpenSSL
+@ project. The module is, however, dual licensed under OpenSSL and
+@ CRYPTOGAMS licenses depending on where you obtain it. For further
+@ details see http://www.openssl.org/~appro/cryptogams/.
+@
+@ Permission to use under GPL terms is granted.
+@ ====================================================================
+
+@ SHA512 block procedure for ARMv4. September 2007.
+
+@ This code is ~4.5 (four and a half) times faster than code generated
+@ by gcc 3.4 and it spends ~72 clock cycles per byte [on single-issue
+@ Xscale PXA250 core].
+@
+@ July 2010.
+@
+@ Rescheduling for dual-issue pipeline resulted in 6% improvement on
+@ Cortex A8 core and ~40 cycles per processed byte.
+
+@ February 2011.
+@
+@ Profiler-assisted and platform-specific optimization resulted in 7%
+@ improvement on Coxtex A8 core and ~38 cycles per byte.
+
+@ March 2011.
+@
+@ Add NEON implementation. On Cortex A8 it was measured to process
+@ one byte in 23.3 cycles or ~60% faster than integer-only code.
+
+@ August 2012.
+@
+@ Improve NEON performance by 12% on Snapdragon S4. In absolute
+@ terms it's 22.6 cycles per byte, which is disappointing result.
+@ Technical writers asserted that 3-way S4 pipeline can sustain
+@ multiple NEON instructions per cycle, but dual NEON issue could
+@ not be observed, see http://www.openssl.org/~appro/Snapdragon-S4.html
+@ for further details. On side note Cortex-A15 processes one byte in
+@ 16 cycles.
+
+@ Byte order [in]dependence. =========================================
+@
+@ Originally caller was expected to maintain specific *dword* order in
+@ h[0-7], namely with most significant dword at *lower* address, which
+@ was reflected in below two parameters as 0 and 4. Now caller is
+@ expected to maintain native byte order for whole 64-bit values.
+#ifndef __KERNEL__
+# include "arm_arch.h"
+# define VFP_ABI_PUSH  vstmdb  sp!,{d8-d15}
+# define VFP_ABI_POP   vldmia  sp!,{d8-d15}
+#else
+# define __ARM_ARCH__ __LINUX_ARM_ARCH__
+# define __ARM_MAX_ARCH__ 7
+# define VFP_ABI_PUSH
+# define VFP_ABI_POP
+#endif
+
+#ifdef __ARMEL__
+# define LO 0
+# define HI 4
+# define WORD64(hi0,lo0,hi1,lo1)       .word   lo0,hi0, lo1,hi1
+#else
+# define HI 0
+# define LO 4
+# define WORD64(hi0,lo0,hi1,lo1)       .word   hi0,lo0, hi1,lo1
+#endif
+
+.text
+#if __ARM_ARCH__<7
+.code  32
+#else
+.syntax unified
+# ifdef __thumb2__
+#  define adrl adr
+.thumb
+# else
+.code   32
+# endif
+#endif
+
+.type  K512,%object
+.align 5
+K512:
+WORD64(0x428a2f98,0xd728ae22, 0x71374491,0x23ef65cd)
+WORD64(0xb5c0fbcf,0xec4d3b2f, 0xe9b5dba5,0x8189dbbc)
+WORD64(0x3956c25b,0xf348b538, 0x59f111f1,0xb605d019)
+WORD64(0x923f82a4,0xaf194f9b, 0xab1c5ed5,0xda6d8118)
+WORD64(0xd807aa98,0xa3030242, 0x12835b01,0x45706fbe)
+WORD64(0x243185be,0x4ee4b28c, 0x550c7dc3,0xd5ffb4e2)
+WORD64(0x72be5d74,0xf27b896f, 0x80deb1fe,0x3b1696b1)
+WORD64(0x9bdc06a7,0x25c71235, 0xc19bf174,0xcf692694)
+WORD64(0xe49b69c1,0x9ef14ad2, 0xefbe4786,0x384f25e3)
+WORD64(0x0fc19dc6,0x8b8cd5b5, 0x240ca1cc,0x77ac9c65)
+WORD64(0x2de92c6f,0x592b0275, 0x4a7484aa,0x6ea6e483)
+WORD64(0x5cb0a9dc,0xbd41fbd4, 0x76f988da,0x831153b5)
+WORD64(0x983e5152,0xee66dfab, 0xa831c66d,0x2db43210)
+WORD64(0xb00327c8,0x98fb213f, 0xbf597fc7,0xbeef0ee4)
+WORD64(0xc6e00bf3,0x3da88fc2, 0xd5a79147,0x930aa725)
+WORD64(0x06ca6351,0xe003826f, 0x14292967,0x0a0e6e70)
+WORD64(0x27b70a85,0x46d22ffc, 0x2e1b2138,0x5c26c926)
+WORD64(0x4d2c6dfc,0x5ac42aed, 0x53380d13,0x9d95b3df)
+WORD64(0x650a7354,0x8baf63de, 0x766a0abb,0x3c77b2a8)
+WORD64(0x81c2c92e,0x47edaee6, 0x92722c85,0x1482353b)
+WORD64(0xa2bfe8a1,0x4cf10364, 0xa81a664b,0xbc423001)
+WORD64(0xc24b8b70,0xd0f89791, 0xc76c51a3,0x0654be30)
+WORD64(0xd192e819,0xd6ef5218, 0xd6990624,0x5565a910)
+WORD64(0xf40e3585,0x5771202a, 0x106aa070,0x32bbd1b8)
+WORD64(0x19a4c116,0xb8d2d0c8, 0x1e376c08,0x5141ab53)
+WORD64(0x2748774c,0xdf8eeb99, 0x34b0bcb5,0xe19b48a8)
+WORD64(0x391c0cb3,0xc5c95a63, 0x4ed8aa4a,0xe3418acb)
+WORD64(0x5b9cca4f,0x7763e373, 0x682e6ff3,0xd6b2b8a3)
+WORD64(0x748f82ee,0x5defb2fc, 0x78a5636f,0x43172f60)
+WORD64(0x84c87814,0xa1f0ab72, 0x8cc70208,0x1a6439ec)
+WORD64(0x90befffa,0x23631e28, 0xa4506ceb,0xde82bde9)
+WORD64(0xbef9a3f7,0xb2c67915, 0xc67178f2,0xe372532b)
+WORD64(0xca273ece,0xea26619c, 0xd186b8c7,0x21c0c207)
+WORD64(0xeada7dd6,0xcde0eb1e, 0xf57d4f7f,0xee6ed178)
+WORD64(0x06f067aa,0x72176fba, 0x0a637dc5,0xa2c898a6)
+WORD64(0x113f9804,0xbef90dae, 0x1b710b35,0x131c471b)
+WORD64(0x28db77f5,0x23047d84, 0x32caab7b,0x40c72493)
+WORD64(0x3c9ebe0a,0x15c9bebc, 0x431d67c4,0x9c100d4c)
+WORD64(0x4cc5d4be,0xcb3e42b6, 0x597f299c,0xfc657e2a)
+WORD64(0x5fcb6fab,0x3ad6faec, 0x6c44198c,0x4a475817)
+.size  K512,.-K512
+#if __ARM_MAX_ARCH__>=7 && !defined(__KERNEL__)
+.LOPENSSL_armcap:
+.word  OPENSSL_armcap_P-sha512_block_data_order
+.skip  32-4
+#else
+.skip  32
+#endif
+
+.global        sha512_block_data_order
+.type  sha512_block_data_order,%function
+sha512_block_data_order:
+#if __ARM_ARCH__<7
+       sub     r3,pc,#8                @ sha512_block_data_order
+#else
+       adr     r3,sha512_block_data_order
+#endif
+#if __ARM_MAX_ARCH__>=7 && !defined(__KERNEL__)
+       ldr     r12,.LOPENSSL_armcap
+       ldr     r12,[r3,r12]            @ OPENSSL_armcap_P
+       tst     r12,#1
+       bne     .LNEON
+#endif
+       add     r2,r1,r2,lsl#7  @ len to point at the end of inp
+       stmdb   sp!,{r4-r12,lr}
+       sub     r14,r3,#672             @ K512
+       sub     sp,sp,#9*8
+
+       ldr     r7,[r0,#32+LO]
+       ldr     r8,[r0,#32+HI]
+       ldr     r9, [r0,#48+LO]
+       ldr     r10, [r0,#48+HI]
+       ldr     r11, [r0,#56+LO]
+       ldr     r12, [r0,#56+HI]
+.Loop:
+       str     r9, [sp,#48+0]
+       str     r10, [sp,#48+4]
+       str     r11, [sp,#56+0]
+       str     r12, [sp,#56+4]
+       ldr     r5,[r0,#0+LO]
+       ldr     r6,[r0,#0+HI]
+       ldr     r3,[r0,#8+LO]
+       ldr     r4,[r0,#8+HI]
+       ldr     r9, [r0,#16+LO]
+       ldr     r10, [r0,#16+HI]
+       ldr     r11, [r0,#24+LO]
+       ldr     r12, [r0,#24+HI]
+       str     r3,[sp,#8+0]
+       str     r4,[sp,#8+4]
+       str     r9, [sp,#16+0]
+       str     r10, [sp,#16+4]
+       str     r11, [sp,#24+0]
+       str     r12, [sp,#24+4]
+       ldr     r3,[r0,#40+LO]
+       ldr     r4,[r0,#40+HI]
+       str     r3,[sp,#40+0]
+       str     r4,[sp,#40+4]
+
+.L00_15:
+#if __ARM_ARCH__<7
+       ldrb    r3,[r1,#7]
+       ldrb    r9, [r1,#6]
+       ldrb    r10, [r1,#5]
+       ldrb    r11, [r1,#4]
+       ldrb    r4,[r1,#3]
+       ldrb    r12, [r1,#2]
+       orr     r3,r3,r9,lsl#8
+       ldrb    r9, [r1,#1]
+       orr     r3,r3,r10,lsl#16
+       ldrb    r10, [r1],#8
+       orr     r3,r3,r11,lsl#24
+       orr     r4,r4,r12,lsl#8
+       orr     r4,r4,r9,lsl#16
+       orr     r4,r4,r10,lsl#24
+#else
+       ldr     r3,[r1,#4]
+       ldr     r4,[r1],#8
+#ifdef __ARMEL__
+       rev     r3,r3
+       rev     r4,r4
+#endif
+#endif
+       @ Sigma1(x)     (ROTR((x),14) ^ ROTR((x),18)  ^ ROTR((x),41))
+       @ LO            lo>>14^hi<<18 ^ lo>>18^hi<<14 ^ hi>>9^lo<<23
+       @ HI            hi>>14^lo<<18 ^ hi>>18^lo<<14 ^ lo>>9^hi<<23
+       mov     r9,r7,lsr#14
+       str     r3,[sp,#64+0]
+       mov     r10,r8,lsr#14
+       str     r4,[sp,#64+4]
+       eor     r9,r9,r8,lsl#18
+       ldr     r11,[sp,#56+0]  @ h.lo
+       eor     r10,r10,r7,lsl#18
+       ldr     r12,[sp,#56+4]  @ h.hi
+       eor     r9,r9,r7,lsr#18
+       eor     r10,r10,r8,lsr#18
+       eor     r9,r9,r8,lsl#14
+       eor     r10,r10,r7,lsl#14
+       eor     r9,r9,r8,lsr#9
+       eor     r10,r10,r7,lsr#9
+       eor     r9,r9,r7,lsl#23
+       eor     r10,r10,r8,lsl#23       @ Sigma1(e)
+       adds    r3,r3,r9
+       ldr     r9,[sp,#40+0]   @ f.lo
+       adc     r4,r4,r10               @ T += Sigma1(e)
+       ldr     r10,[sp,#40+4]  @ f.hi
+       adds    r3,r3,r11
+       ldr     r11,[sp,#48+0]  @ g.lo
+       adc     r4,r4,r12               @ T += h
+       ldr     r12,[sp,#48+4]  @ g.hi
+
+       eor     r9,r9,r11
+       str     r7,[sp,#32+0]
+       eor     r10,r10,r12
+       str     r8,[sp,#32+4]
+       and     r9,r9,r7
+       str     r5,[sp,#0+0]
+       and     r10,r10,r8
+       str     r6,[sp,#0+4]
+       eor     r9,r9,r11
+       ldr     r11,[r14,#LO]   @ K[i].lo
+       eor     r10,r10,r12             @ Ch(e,f,g)
+       ldr     r12,[r14,#HI]   @ K[i].hi
+
+       adds    r3,r3,r9
+       ldr     r7,[sp,#24+0]   @ d.lo
+       adc     r4,r4,r10               @ T += Ch(e,f,g)
+       ldr     r8,[sp,#24+4]   @ d.hi
+       adds    r3,r3,r11
+       and     r9,r11,#0xff
+       adc     r4,r4,r12               @ T += K[i]
+       adds    r7,r7,r3
+       ldr     r11,[sp,#8+0]   @ b.lo
+       adc     r8,r8,r4                @ d += T
+       teq     r9,#148
+
+       ldr     r12,[sp,#16+0]  @ c.lo
+#if __ARM_ARCH__>=7
+       it      eq                      @ Thumb2 thing, sanity check in ARM
+#endif
+       orreq   r14,r14,#1
+       @ Sigma0(x)     (ROTR((x),28) ^ ROTR((x),34) ^ ROTR((x),39))
+       @ LO            lo>>28^hi<<4  ^ hi>>2^lo<<30 ^ hi>>7^lo<<25
+       @ HI            hi>>28^lo<<4  ^ lo>>2^hi<<30 ^ lo>>7^hi<<25
+       mov     r9,r5,lsr#28
+       mov     r10,r6,lsr#28
+       eor     r9,r9,r6,lsl#4
+       eor     r10,r10,r5,lsl#4
+       eor     r9,r9,r6,lsr#2
+       eor     r10,r10,r5,lsr#2
+       eor     r9,r9,r5,lsl#30
+       eor     r10,r10,r6,lsl#30
+       eor     r9,r9,r6,lsr#7
+       eor     r10,r10,r5,lsr#7
+       eor     r9,r9,r5,lsl#25
+       eor     r10,r10,r6,lsl#25       @ Sigma0(a)
+       adds    r3,r3,r9
+       and     r9,r5,r11
+       adc     r4,r4,r10               @ T += Sigma0(a)
+
+       ldr     r10,[sp,#8+4]   @ b.hi
+       orr     r5,r5,r11
+       ldr     r11,[sp,#16+4]  @ c.hi
+       and     r5,r5,r12
+       and     r12,r6,r10
+       orr     r6,r6,r10
+       orr     r5,r5,r9                @ Maj(a,b,c).lo
+       and     r6,r6,r11
+       adds    r5,r5,r3
+       orr     r6,r6,r12               @ Maj(a,b,c).hi
+       sub     sp,sp,#8
+       adc     r6,r6,r4                @ h += T
+       tst     r14,#1
+       add     r14,r14,#8
+       tst     r14,#1
+       beq     .L00_15
+       ldr     r9,[sp,#184+0]
+       ldr     r10,[sp,#184+4]
+       bic     r14,r14,#1
+.L16_79:
+       @ sigma0(x)     (ROTR((x),1)  ^ ROTR((x),8)  ^ ((x)>>7))
+       @ LO            lo>>1^hi<<31  ^ lo>>8^hi<<24 ^ lo>>7^hi<<25
+       @ HI            hi>>1^lo<<31  ^ hi>>8^lo<<24 ^ hi>>7
+       mov     r3,r9,lsr#1
+       ldr     r11,[sp,#80+0]
+       mov     r4,r10,lsr#1
+       ldr     r12,[sp,#80+4]
+       eor     r3,r3,r10,lsl#31
+       eor     r4,r4,r9,lsl#31
+       eor     r3,r3,r9,lsr#8
+       eor     r4,r4,r10,lsr#8
+       eor     r3,r3,r10,lsl#24
+       eor     r4,r4,r9,lsl#24
+       eor     r3,r3,r9,lsr#7
+       eor     r4,r4,r10,lsr#7
+       eor     r3,r3,r10,lsl#25
+
+       @ sigma1(x)     (ROTR((x),19) ^ ROTR((x),61) ^ ((x)>>6))
+       @ LO            lo>>19^hi<<13 ^ hi>>29^lo<<3 ^ lo>>6^hi<<26
+       @ HI            hi>>19^lo<<13 ^ lo>>29^hi<<3 ^ hi>>6
+       mov     r9,r11,lsr#19
+       mov     r10,r12,lsr#19
+       eor     r9,r9,r12,lsl#13
+       eor     r10,r10,r11,lsl#13
+       eor     r9,r9,r12,lsr#29
+       eor     r10,r10,r11,lsr#29
+       eor     r9,r9,r11,lsl#3
+       eor     r10,r10,r12,lsl#3
+       eor     r9,r9,r11,lsr#6
+       eor     r10,r10,r12,lsr#6
+       ldr     r11,[sp,#120+0]
+       eor     r9,r9,r12,lsl#26
+
+       ldr     r12,[sp,#120+4]
+       adds    r3,r3,r9
+       ldr     r9,[sp,#192+0]
+       adc     r4,r4,r10
+
+       ldr     r10,[sp,#192+4]
+       adds    r3,r3,r11
+       adc     r4,r4,r12
+       adds    r3,r3,r9
+       adc     r4,r4,r10
+       @ Sigma1(x)     (ROTR((x),14) ^ ROTR((x),18)  ^ ROTR((x),41))
+       @ LO            lo>>14^hi<<18 ^ lo>>18^hi<<14 ^ hi>>9^lo<<23
+       @ HI            hi>>14^lo<<18 ^ hi>>18^lo<<14 ^ lo>>9^hi<<23
+       mov     r9,r7,lsr#14
+       str     r3,[sp,#64+0]
+       mov     r10,r8,lsr#14
+       str     r4,[sp,#64+4]
+       eor     r9,r9,r8,lsl#18
+       ldr     r11,[sp,#56+0]  @ h.lo
+       eor     r10,r10,r7,lsl#18
+       ldr     r12,[sp,#56+4]  @ h.hi
+       eor     r9,r9,r7,lsr#18
+       eor     r10,r10,r8,lsr#18
+       eor     r9,r9,r8,lsl#14
+       eor     r10,r10,r7,lsl#14
+       eor     r9,r9,r8,lsr#9
+       eor     r10,r10,r7,lsr#9
+       eor     r9,r9,r7,lsl#23
+       eor     r10,r10,r8,lsl#23       @ Sigma1(e)
+       adds    r3,r3,r9
+       ldr     r9,[sp,#40+0]   @ f.lo
+       adc     r4,r4,r10               @ T += Sigma1(e)
+       ldr     r10,[sp,#40+4]  @ f.hi
+       adds    r3,r3,r11
+       ldr     r11,[sp,#48+0]  @ g.lo
+       adc     r4,r4,r12               @ T += h
+       ldr     r12,[sp,#48+4]  @ g.hi
+
+       eor     r9,r9,r11
+       str     r7,[sp,#32+0]
+       eor     r10,r10,r12
+       str     r8,[sp,#32+4]
+       and     r9,r9,r7
+       str     r5,[sp,#0+0]
+       and     r10,r10,r8
+       str     r6,[sp,#0+4]
+       eor     r9,r9,r11
+       ldr     r11,[r14,#LO]   @ K[i].lo
+       eor     r10,r10,r12             @ Ch(e,f,g)
+       ldr     r12,[r14,#HI]   @ K[i].hi
+
+       adds    r3,r3,r9
+       ldr     r7,[sp,#24+0]   @ d.lo
+       adc     r4,r4,r10               @ T += Ch(e,f,g)
+       ldr     r8,[sp,#24+4]   @ d.hi
+       adds    r3,r3,r11
+       and     r9,r11,#0xff
+       adc     r4,r4,r12               @ T += K[i]
+       adds    r7,r7,r3
+       ldr     r11,[sp,#8+0]   @ b.lo
+       adc     r8,r8,r4                @ d += T
+       teq     r9,#23
+
+       ldr     r12,[sp,#16+0]  @ c.lo
+#if __ARM_ARCH__>=7
+       it      eq                      @ Thumb2 thing, sanity check in ARM
+#endif
+       orreq   r14,r14,#1
+       @ Sigma0(x)     (ROTR((x),28) ^ ROTR((x),34) ^ ROTR((x),39))
+       @ LO            lo>>28^hi<<4  ^ hi>>2^lo<<30 ^ hi>>7^lo<<25
+       @ HI            hi>>28^lo<<4  ^ lo>>2^hi<<30 ^ lo>>7^hi<<25
+       mov     r9,r5,lsr#28
+       mov     r10,r6,lsr#28
+       eor     r9,r9,r6,lsl#4
+       eor     r10,r10,r5,lsl#4
+       eor     r9,r9,r6,lsr#2
+       eor     r10,r10,r5,lsr#2
+       eor     r9,r9,r5,lsl#30
+       eor     r10,r10,r6,lsl#30
+       eor     r9,r9,r6,lsr#7
+       eor     r10,r10,r5,lsr#7
+       eor     r9,r9,r5,lsl#25
+       eor     r10,r10,r6,lsl#25       @ Sigma0(a)
+       adds    r3,r3,r9
+       and     r9,r5,r11
+       adc     r4,r4,r10               @ T += Sigma0(a)
+
+       ldr     r10,[sp,#8+4]   @ b.hi
+       orr     r5,r5,r11
+       ldr     r11,[sp,#16+4]  @ c.hi
+       and     r5,r5,r12
+       and     r12,r6,r10
+       orr     r6,r6,r10
+       orr     r5,r5,r9                @ Maj(a,b,c).lo
+       and     r6,r6,r11
+       adds    r5,r5,r3
+       orr     r6,r6,r12               @ Maj(a,b,c).hi
+       sub     sp,sp,#8
+       adc     r6,r6,r4                @ h += T
+       tst     r14,#1
+       add     r14,r14,#8
+#if __ARM_ARCH__>=7
+       ittt    eq                      @ Thumb2 thing, sanity check in ARM
+#endif
+       ldreq   r9,[sp,#184+0]
+       ldreq   r10,[sp,#184+4]
+       beq     .L16_79
+       bic     r14,r14,#1
+
+       ldr     r3,[sp,#8+0]
+       ldr     r4,[sp,#8+4]
+       ldr     r9, [r0,#0+LO]
+       ldr     r10, [r0,#0+HI]
+       ldr     r11, [r0,#8+LO]
+       ldr     r12, [r0,#8+HI]
+       adds    r9,r5,r9
+       str     r9, [r0,#0+LO]
+       adc     r10,r6,r10
+       str     r10, [r0,#0+HI]
+       adds    r11,r3,r11
+       str     r11, [r0,#8+LO]
+       adc     r12,r4,r12
+       str     r12, [r0,#8+HI]
+
+       ldr     r5,[sp,#16+0]
+       ldr     r6,[sp,#16+4]
+       ldr     r3,[sp,#24+0]
+       ldr     r4,[sp,#24+4]
+       ldr     r9, [r0,#16+LO]
+       ldr     r10, [r0,#16+HI]
+       ldr     r11, [r0,#24+LO]
+       ldr     r12, [r0,#24+HI]
+       adds    r9,r5,r9
+       str     r9, [r0,#16+LO]
+       adc     r10,r6,r10
+       str     r10, [r0,#16+HI]
+       adds    r11,r3,r11
+       str     r11, [r0,#24+LO]
+       adc     r12,r4,r12
+       str     r12, [r0,#24+HI]
+
+       ldr     r3,[sp,#40+0]
+       ldr     r4,[sp,#40+4]
+       ldr     r9, [r0,#32+LO]
+       ldr     r10, [r0,#32+HI]
+       ldr     r11, [r0,#40+LO]
+       ldr     r12, [r0,#40+HI]
+       adds    r7,r7,r9
+       str     r7,[r0,#32+LO]
+       adc     r8,r8,r10
+       str     r8,[r0,#32+HI]
+       adds    r11,r3,r11
+       str     r11, [r0,#40+LO]
+       adc     r12,r4,r12
+       str     r12, [r0,#40+HI]
+
+       ldr     r5,[sp,#48+0]
+       ldr     r6,[sp,#48+4]
+       ldr     r3,[sp,#56+0]
+       ldr     r4,[sp,#56+4]
+       ldr     r9, [r0,#48+LO]
+       ldr     r10, [r0,#48+HI]
+       ldr     r11, [r0,#56+LO]
+       ldr     r12, [r0,#56+HI]
+       adds    r9,r5,r9
+       str     r9, [r0,#48+LO]
+       adc     r10,r6,r10
+       str     r10, [r0,#48+HI]
+       adds    r11,r3,r11
+       str     r11, [r0,#56+LO]
+       adc     r12,r4,r12
+       str     r12, [r0,#56+HI]
+
+       add     sp,sp,#640
+       sub     r14,r14,#640
+
+       teq     r1,r2
+       bne     .Loop
+
+       add     sp,sp,#8*9              @ destroy frame
+#if __ARM_ARCH__>=5
+       ldmia   sp!,{r4-r12,pc}
+#else
+       ldmia   sp!,{r4-r12,lr}
+       tst     lr,#1
+       moveq   pc,lr                   @ be binary compatible with V4, yet
+       .word   0xe12fff1e                      @ interoperable with Thumb ISA:-)
+#endif
+.size  sha512_block_data_order,.-sha512_block_data_order
+#if __ARM_MAX_ARCH__>=7
+.arch  armv7-a
+.fpu   neon
+
+.global        sha512_block_data_order_neon
+.type  sha512_block_data_order_neon,%function
+.align 4
+sha512_block_data_order_neon:
+.LNEON:
+       dmb                             @ errata #451034 on early Cortex A8
+       add     r2,r1,r2,lsl#7  @ len to point at the end of inp
+       VFP_ABI_PUSH
+       adrl    r3,K512
+       vldmia  r0,{d16-d23}            @ load context
+.Loop_neon:
+       vshr.u64        d24,d20,#14     @ 0
+#if 0<16
+       vld1.64         {d0},[r1]!      @ handles unaligned
+#endif
+       vshr.u64        d25,d20,#18
+#if 0>0
+        vadd.i64       d16,d30                 @ h+=Maj from the past
+#endif
+       vshr.u64        d26,d20,#41
+       vld1.64         {d28},[r3,:64]! @ K[i++]
+       vsli.64         d24,d20,#50
+       vsli.64         d25,d20,#46
+       vmov            d29,d20
+       vsli.64         d26,d20,#23
+#if 0<16 && defined(__ARMEL__)
+       vrev64.8        d0,d0
+#endif
+       veor            d25,d24
+       vbsl            d29,d21,d22             @ Ch(e,f,g)
+       vshr.u64        d24,d16,#28
+       veor            d26,d25                 @ Sigma1(e)
+       vadd.i64        d27,d29,d23
+       vshr.u64        d25,d16,#34
+       vsli.64         d24,d16,#36
+       vadd.i64        d27,d26
+       vshr.u64        d26,d16,#39
+       vadd.i64        d28,d0
+       vsli.64         d25,d16,#30
+       veor            d30,d16,d17
+       vsli.64         d26,d16,#25
+       veor            d23,d24,d25
+       vadd.i64        d27,d28
+       vbsl            d30,d18,d17             @ Maj(a,b,c)
+       veor            d23,d26                 @ Sigma0(a)
+       vadd.i64        d19,d27
+       vadd.i64        d30,d27
+       @ vadd.i64      d23,d30
+       vshr.u64        d24,d19,#14     @ 1
+#if 1<16
+       vld1.64         {d1},[r1]!      @ handles unaligned
+#endif
+       vshr.u64        d25,d19,#18
+#if 1>0
+        vadd.i64       d23,d30                 @ h+=Maj from the past
+#endif
+       vshr.u64        d26,d19,#41
+       vld1.64         {d28},[r3,:64]! @ K[i++]
+       vsli.64         d24,d19,#50
+       vsli.64         d25,d19,#46
+       vmov            d29,d19
+       vsli.64         d26,d19,#23
+#if 1<16 && defined(__ARMEL__)
+       vrev64.8        d1,d1
+#endif
+       veor            d25,d24
+       vbsl            d29,d20,d21             @ Ch(e,f,g)
+       vshr.u64        d24,d23,#28
+       veor            d26,d25                 @ Sigma1(e)
+       vadd.i64        d27,d29,d22
+       vshr.u64        d25,d23,#34
+       vsli.64         d24,d23,#36
+       vadd.i64        d27,d26
+       vshr.u64        d26,d23,#39
+       vadd.i64        d28,d1
+       vsli.64         d25,d23,#30
+       veor            d30,d23,d16
+       vsli.64         d26,d23,#25
+       veor            d22,d24,d25
+       vadd.i64        d27,d28
+       vbsl            d30,d17,d16             @ Maj(a,b,c)
+       veor            d22,d26                 @ Sigma0(a)
+       vadd.i64        d18,d27
+       vadd.i64        d30,d27
+       @ vadd.i64      d22,d30
+       vshr.u64        d24,d18,#14     @ 2
+#if 2<16
+       vld1.64         {d2},[r1]!      @ handles unaligned
+#endif
+       vshr.u64        d25,d18,#18
+#if 2>0
+        vadd.i64       d22,d30                 @ h+=Maj from the past
+#endif
+       vshr.u64        d26,d18,#41
+       vld1.64         {d28},[r3,:64]! @ K[i++]
+       vsli.64         d24,d18,#50
+       vsli.64         d25,d18,#46
+       vmov            d29,d18
+       vsli.64         d26,d18,#23
+#if 2<16 && defined(__ARMEL__)
+       vrev64.8        d2,d2
+#endif
+       veor            d25,d24
+       vbsl            d29,d19,d20             @ Ch(e,f,g)
+       vshr.u64        d24,d22,#28
+       veor            d26,d25                 @ Sigma1(e)
+       vadd.i64        d27,d29,d21
+       vshr.u64        d25,d22,#34
+       vsli.64         d24,d22,#36
+       vadd.i64        d27,d26
+       vshr.u64        d26,d22,#39
+       vadd.i64        d28,d2
+       vsli.64         d25,d22,#30
+       veor            d30,d22,d23
+       vsli.64         d26,d22,#25
+       veor            d21,d24,d25
+       vadd.i64        d27,d28
+       vbsl            d30,d16,d23             @ Maj(a,b,c)
+       veor            d21,d26                 @ Sigma0(a)
+       vadd.i64        d17,d27
+       vadd.i64        d30,d27
+       @ vadd.i64      d21,d30
+       vshr.u64        d24,d17,#14     @ 3
+#if 3<16
+       vld1.64         {d3},[r1]!      @ handles unaligned
+#endif
+       vshr.u64        d25,d17,#18
+#if 3>0
+        vadd.i64       d21,d30                 @ h+=Maj from the past
+#endif
+       vshr.u64        d26,d17,#41
+       vld1.64         {d28},[r3,:64]! @ K[i++]
+       vsli.64         d24,d17,#50
+       vsli.64         d25,d17,#46
+       vmov            d29,d17
+       vsli.64         d26,d17,#23
+#if 3<16 && defined(__ARMEL__)
+       vrev64.8        d3,d3
+#endif
+       veor            d25,d24
+       vbsl            d29,d18,d19             @ Ch(e,f,g)
+       vshr.u64        d24,d21,#28
+       veor            d26,d25                 @ Sigma1(e)
+       vadd.i64        d27,d29,d20
+       vshr.u64        d25,d21,#34
+       vsli.64         d24,d21,#36
+       vadd.i64        d27,d26
+       vshr.u64        d26,d21,#39
+       vadd.i64        d28,d3
+       vsli.64         d25,d21,#30
+       veor            d30,d21,d22
+       vsli.64         d26,d21,#25
+       veor            d20,d24,d25
+       vadd.i64        d27,d28
+       vbsl            d30,d23,d22             @ Maj(a,b,c)
+       veor            d20,d26                 @ Sigma0(a)
+       vadd.i64        d16,d27
+       vadd.i64        d30,d27
+       @ vadd.i64      d20,d30
+       vshr.u64        d24,d16,#14     @ 4
+#if 4<16
+       vld1.64         {d4},[r1]!      @ handles unaligned
+#endif
+       vshr.u64        d25,d16,#18
+#if 4>0
+        vadd.i64       d20,d30                 @ h+=Maj from the past
+#endif
+       vshr.u64        d26,d16,#41
+       vld1.64         {d28},[r3,:64]! @ K[i++]
+       vsli.64         d24,d16,#50
+       vsli.64         d25,d16,#46
+       vmov            d29,d16
+       vsli.64         d26,d16,#23
+#if 4<16 && defined(__ARMEL__)
+       vrev64.8        d4,d4
+#endif
+       veor            d25,d24
+       vbsl            d29,d17,d18             @ Ch(e,f,g)
+       vshr.u64        d24,d20,#28
+       veor            d26,d25                 @ Sigma1(e)
+       vadd.i64        d27,d29,d19
+       vshr.u64        d25,d20,#34
+       vsli.64         d24,d20,#36
+       vadd.i64        d27,d26
+       vshr.u64        d26,d20,#39
+       vadd.i64        d28,d4
+       vsli.64         d25,d20,#30
+       veor            d30,d20,d21
+       vsli.64         d26,d20,#25
+       veor            d19,d24,d25
+       vadd.i64        d27,d28
+       vbsl            d30,d22,d21             @ Maj(a,b,c)
+       veor            d19,d26                 @ Sigma0(a)
+       vadd.i64        d23,d27
+       vadd.i64        d30,d27
+       @ vadd.i64      d19,d30
+       vshr.u64        d24,d23,#14     @ 5
+#if 5<16
+       vld1.64         {d5},[r1]!      @ handles unaligned
+#endif
+       vshr.u64        d25,d23,#18
+#if 5>0
+        vadd.i64       d19,d30                 @ h+=Maj from the past
+#endif
+       vshr.u64        d26,d23,#41
+       vld1.64         {d28},[r3,:64]! @ K[i++]
+       vsli.64         d24,d23,#50
+       vsli.64         d25,d23,#46
+       vmov            d29,d23
+       vsli.64         d26,d23,#23
+#if 5<16 && defined(__ARMEL__)
+       vrev64.8        d5,d5
+#endif
+       veor            d25,d24
+       vbsl            d29,d16,d17             @ Ch(e,f,g)
+       vshr.u64        d24,d19,#28
+       veor            d26,d25                 @ Sigma1(e)
+       vadd.i64        d27,d29,d18
+       vshr.u64        d25,d19,#34
+       vsli.64         d24,d19,#36
+       vadd.i64        d27,d26
+       vshr.u64        d26,d19,#39
+       vadd.i64        d28,d5
+       vsli.64         d25,d19,#30
+       veor            d30,d19,d20
+       vsli.64         d26,d19,#25
+       veor            d18,d24,d25
+       vadd.i64        d27,d28
+       vbsl            d30,d21,d20             @ Maj(a,b,c)
+       veor            d18,d26                 @ Sigma0(a)
+       vadd.i64        d22,d27
+       vadd.i64        d30,d27
+       @ vadd.i64      d18,d30
+       vshr.u64        d24,d22,#14     @ 6
+#if 6<16
+       vld1.64         {d6},[r1]!      @ handles unaligned
+#endif
+       vshr.u64        d25,d22,#18
+#if 6>0
+        vadd.i64       d18,d30                 @ h+=Maj from the past
+#endif
+       vshr.u64        d26,d22,#41
+       vld1.64         {d28},[r3,:64]! @ K[i++]
+       vsli.64         d24,d22,#50
+       vsli.64         d25,d22,#46
+       vmov            d29,d22
+       vsli.64         d26,d22,#23
+#if 6<16 && defined(__ARMEL__)
+       vrev64.8        d6,d6
+#endif
+       veor            d25,d24
+       vbsl            d29,d23,d16             @ Ch(e,f,g)
+       vshr.u64        d24,d18,#28
+       veor            d26,d25                 @ Sigma1(e)
+       vadd.i64        d27,d29,d17
+       vshr.u64        d25,d18,#34
+       vsli.64         d24,d18,#36
+       vadd.i64        d27,d26
+       vshr.u64        d26,d18,#39
+       vadd.i64        d28,d6
+       vsli.64         d25,d18,#30
+       veor            d30,d18,d19
+       vsli.64         d26,d18,#25
+       veor            d17,d24,d25
+       vadd.i64        d27,d28
+       vbsl            d30,d20,d19             @ Maj(a,b,c)
+       veor            d17,d26                 @ Sigma0(a)
+       vadd.i64        d21,d27
+       vadd.i64        d30,d27
+       @ vadd.i64      d17,d30
+       vshr.u64        d24,d21,#14     @ 7
+#if 7<16
+       vld1.64         {d7},[r1]!      @ handles unaligned
+#endif
+       vshr.u64        d25,d21,#18
+#if 7>0
+        vadd.i64       d17,d30                 @ h+=Maj from the past
+#endif
+       vshr.u64        d26,d21,#41
+       vld1.64         {d28},[r3,:64]! @ K[i++]
+       vsli.64         d24,d21,#50
+       vsli.64         d25,d21,#46
+       vmov            d29,d21
+       vsli.64         d26,d21,#23
+#if 7<16 && defined(__ARMEL__)
+       vrev64.8        d7,d7
+#endif
+       veor            d25,d24
+       vbsl            d29,d22,d23             @ Ch(e,f,g)
+       vshr.u64        d24,d17,#28
+       veor            d26,d25                 @ Sigma1(e)
+       vadd.i64        d27,d29,d16
+       vshr.u64        d25,d17,#34
+       vsli.64         d24,d17,#36
+       vadd.i64        d27,d26
+       vshr.u64        d26,d17,#39
+       vadd.i64        d28,d7
+       vsli.64         d25,d17,#30
+       veor            d30,d17,d18
+       vsli.64         d26,d17,#25
+       veor            d16,d24,d25
+       vadd.i64        d27,d28
+       vbsl            d30,d19,d18             @ Maj(a,b,c)
+       veor            d16,d26                 @ Sigma0(a)
+       vadd.i64        d20,d27
+       vadd.i64        d30,d27
+       @ vadd.i64      d16,d30
+       vshr.u64        d24,d20,#14     @ 8
+#if 8<16
+       vld1.64         {d8},[r1]!      @ handles unaligned
+#endif
+       vshr.u64        d25,d20,#18
+#if 8>0
+        vadd.i64       d16,d30                 @ h+=Maj from the past
+#endif
+       vshr.u64        d26,d20,#41
+       vld1.64         {d28},[r3,:64]! @ K[i++]
+       vsli.64         d24,d20,#50
+       vsli.64         d25,d20,#46
+       vmov            d29,d20
+       vsli.64         d26,d20,#23
+#if 8<16 && defined(__ARMEL__)
+       vrev64.8        d8,d8
+#endif
+       veor            d25,d24
+       vbsl            d29,d21,d22             @ Ch(e,f,g)
+       vshr.u64        d24,d16,#28
+       veor            d26,d25                 @ Sigma1(e)
+       vadd.i64        d27,d29,d23
+       vshr.u64        d25,d16,#34
+       vsli.64         d24,d16,#36
+       vadd.i64        d27,d26
+       vshr.u64        d26,d16,#39
+       vadd.i64        d28,d8
+       vsli.64         d25,d16,#30
+       veor            d30,d16,d17
+       vsli.64         d26,d16,#25
+       veor            d23,d24,d25
+       vadd.i64        d27,d28
+       vbsl            d30,d18,d17             @ Maj(a,b,c)
+       veor            d23,d26                 @ Sigma0(a)
+       vadd.i64        d19,d27
+       vadd.i64        d30,d27
+       @ vadd.i64      d23,d30
+       vshr.u64        d24,d19,#14     @ 9
+#if 9<16
+       vld1.64         {d9},[r1]!      @ handles unaligned
+#endif
+       vshr.u64        d25,d19,#18
+#if 9>0
+        vadd.i64       d23,d30                 @ h+=Maj from the past
+#endif
+       vshr.u64        d26,d19,#41
+       vld1.64         {d28},[r3,:64]! @ K[i++]
+       vsli.64         d24,d19,#50
+       vsli.64         d25,d19,#46
+       vmov            d29,d19
+       vsli.64         d26,d19,#23
+#if 9<16 && defined(__ARMEL__)
+       vrev64.8        d9,d9
+#endif
+       veor            d25,d24
+       vbsl            d29,d20,d21             @ Ch(e,f,g)
+       vshr.u64        d24,d23,#28
+       veor            d26,d25                 @ Sigma1(e)
+       vadd.i64        d27,d29,d22
+       vshr.u64        d25,d23,#34
+       vsli.64         d24,d23,#36
+       vadd.i64        d27,d26
+       vshr.u64        d26,d23,#39
+       vadd.i64        d28,d9
+       vsli.64         d25,d23,#30
+       veor            d30,d23,d16
+       vsli.64         d26,d23,#25
+       veor            d22,d24,d25
+       vadd.i64        d27,d28
+       vbsl            d30,d17,d16             @ Maj(a,b,c)
+       veor            d22,d26                 @ Sigma0(a)
+       vadd.i64        d18,d27
+       vadd.i64        d30,d27
+       @ vadd.i64      d22,d30
+       vshr.u64        d24,d18,#14     @ 10
+#if 10<16
+       vld1.64         {d10},[r1]!     @ handles unaligned
+#endif
+       vshr.u64        d25,d18,#18
+#if 10>0
+        vadd.i64       d22,d30                 @ h+=Maj from the past
+#endif
+       vshr.u64        d26,d18,#41
+       vld1.64         {d28},[r3,:64]! @ K[i++]
+       vsli.64         d24,d18,#50
+       vsli.64         d25,d18,#46
+       vmov            d29,d18
+       vsli.64         d26,d18,#23
+#if 10<16 && defined(__ARMEL__)
+       vrev64.8        d10,d10
+#endif
+       veor            d25,d24
+       vbsl            d29,d19,d20             @ Ch(e,f,g)
+       vshr.u64        d24,d22,#28
+       veor            d26,d25                 @ Sigma1(e)
+       vadd.i64        d27,d29,d21
+       vshr.u64        d25,d22,#34
+       vsli.64         d24,d22,#36
+       vadd.i64        d27,d26
+       vshr.u64        d26,d22,#39
+       vadd.i64        d28,d10
+       vsli.64         d25,d22,#30
+       veor            d30,d22,d23
+       vsli.64         d26,d22,#25
+       veor            d21,d24,d25
+       vadd.i64        d27,d28
+       vbsl            d30,d16,d23             @ Maj(a,b,c)
+       veor            d21,d26                 @ Sigma0(a)
+       vadd.i64        d17,d27
+       vadd.i64        d30,d27
+       @ vadd.i64      d21,d30
+       vshr.u64        d24,d17,#14     @ 11
+#if 11<16
+       vld1.64         {d11},[r1]!     @ handles unaligned
+#endif
+       vshr.u64        d25,d17,#18
+#if 11>0
+        vadd.i64       d21,d30                 @ h+=Maj from the past
+#endif
+       vshr.u64        d26,d17,#41
+       vld1.64         {d28},[r3,:64]! @ K[i++]
+       vsli.64         d24,d17,#50
+       vsli.64         d25,d17,#46
+       vmov            d29,d17
+       vsli.64         d26,d17,#23
+#if 11<16 && defined(__ARMEL__)
+       vrev64.8        d11,d11
+#endif
+       veor            d25,d24
+       vbsl            d29,d18,d19             @ Ch(e,f,g)
+       vshr.u64        d24,d21,#28
+       veor            d26,d25                 @ Sigma1(e)
+       vadd.i64        d27,d29,d20
+       vshr.u64        d25,d21,#34
+       vsli.64         d24,d21,#36
+       vadd.i64        d27,d26
+       vshr.u64        d26,d21,#39
+       vadd.i64        d28,d11
+       vsli.64         d25,d21,#30
+       veor            d30,d21,d22
+       vsli.64         d26,d21,#25
+       veor            d20,d24,d25
+       vadd.i64        d27,d28
+       vbsl            d30,d23,d22             @ Maj(a,b,c)
+       veor            d20,d26                 @ Sigma0(a)
+       vadd.i64        d16,d27
+       vadd.i64        d30,d27
+       @ vadd.i64      d20,d30
+       vshr.u64        d24,d16,#14     @ 12
+#if 12<16
+       vld1.64         {d12},[r1]!     @ handles unaligned
+#endif
+       vshr.u64        d25,d16,#18
+#if 12>0
+        vadd.i64       d20,d30                 @ h+=Maj from the past
+#endif
+       vshr.u64        d26,d16,#41
+       vld1.64         {d28},[r3,:64]! @ K[i++]
+       vsli.64         d24,d16,#50
+       vsli.64         d25,d16,#46
+       vmov            d29,d16
+       vsli.64         d26,d16,#23
+#if 12<16 && defined(__ARMEL__)
+       vrev64.8        d12,d12
+#endif
+       veor            d25,d24
+       vbsl            d29,d17,d18             @ Ch(e,f,g)
+       vshr.u64        d24,d20,#28
+       veor            d26,d25                 @ Sigma1(e)
+       vadd.i64        d27,d29,d19
+       vshr.u64        d25,d20,#34
+       vsli.64         d24,d20,#36
+       vadd.i64        d27,d26
+       vshr.u64        d26,d20,#39
+       vadd.i64        d28,d12
+       vsli.64         d25,d20,#30
+       veor            d30,d20,d21
+       vsli.64         d26,d20,#25
+       veor            d19,d24,d25
+       vadd.i64        d27,d28
+       vbsl            d30,d22,d21             @ Maj(a,b,c)
+       veor            d19,d26                 @ Sigma0(a)
+       vadd.i64        d23,d27
+       vadd.i64        d30,d27
+       @ vadd.i64      d19,d30
+       vshr.u64        d24,d23,#14     @ 13
+#if 13<16
+       vld1.64         {d13},[r1]!     @ handles unaligned
+#endif
+       vshr.u64        d25,d23,#18
+#if 13>0
+        vadd.i64       d19,d30                 @ h+=Maj from the past
+#endif
+       vshr.u64        d26,d23,#41
+       vld1.64         {d28},[r3,:64]! @ K[i++]
+       vsli.64         d24,d23,#50
+       vsli.64         d25,d23,#46
+       vmov            d29,d23
+       vsli.64         d26,d23,#23
+#if 13<16 && defined(__ARMEL__)
+       vrev64.8        d13,d13
+#endif
+       veor            d25,d24
+       vbsl            d29,d16,d17             @ Ch(e,f,g)
+       vshr.u64        d24,d19,#28
+       veor            d26,d25                 @ Sigma1(e)
+       vadd.i64        d27,d29,d18
+       vshr.u64        d25,d19,#34
+       vsli.64         d24,d19,#36
+       vadd.i64        d27,d26
+       vshr.u64        d26,d19,#39
+       vadd.i64        d28,d13
+       vsli.64         d25,d19,#30
+       veor            d30,d19,d20
+       vsli.64         d26,d19,#25
+       veor            d18,d24,d25
+       vadd.i64        d27,d28
+       vbsl            d30,d21,d20             @ Maj(a,b,c)
+       veor            d18,d26                 @ Sigma0(a)
+       vadd.i64        d22,d27
+       vadd.i64        d30,d27
+       @ vadd.i64      d18,d30
+       vshr.u64        d24,d22,#14     @ 14
+#if 14<16
+       vld1.64         {d14},[r1]!     @ handles unaligned
+#endif
+       vshr.u64        d25,d22,#18
+#if 14>0
+        vadd.i64       d18,d30                 @ h+=Maj from the past
+#endif
+       vshr.u64        d26,d22,#41
+       vld1.64         {d28},[r3,:64]! @ K[i++]
+       vsli.64         d24,d22,#50
+       vsli.64         d25,d22,#46
+       vmov            d29,d22
+       vsli.64         d26,d22,#23
+#if 14<16 && defined(__ARMEL__)
+       vrev64.8        d14,d14
+#endif
+       veor            d25,d24
+       vbsl            d29,d23,d16             @ Ch(e,f,g)
+       vshr.u64        d24,d18,#28
+       veor            d26,d25                 @ Sigma1(e)
+       vadd.i64        d27,d29,d17
+       vshr.u64        d25,d18,#34
+       vsli.64         d24,d18,#36
+       vadd.i64        d27,d26
+       vshr.u64        d26,d18,#39
+       vadd.i64        d28,d14
+       vsli.64         d25,d18,#30
+       veor            d30,d18,d19
+       vsli.64         d26,d18,#25
+       veor            d17,d24,d25
+       vadd.i64        d27,d28
+       vbsl            d30,d20,d19             @ Maj(a,b,c)
+       veor            d17,d26                 @ Sigma0(a)
+       vadd.i64        d21,d27
+       vadd.i64        d30,d27
+       @ vadd.i64      d17,d30
+       vshr.u64        d24,d21,#14     @ 15
+#if 15<16
+       vld1.64         {d15},[r1]!     @ handles unaligned
+#endif
+       vshr.u64        d25,d21,#18
+#if 15>0
+        vadd.i64       d17,d30                 @ h+=Maj from the past
+#endif
+       vshr.u64        d26,d21,#41
+       vld1.64         {d28},[r3,:64]! @ K[i++]
+       vsli.64         d24,d21,#50
+       vsli.64         d25,d21,#46
+       vmov            d29,d21
+       vsli.64         d26,d21,#23
+#if 15<16 && defined(__ARMEL__)
+       vrev64.8        d15,d15
+#endif
+       veor            d25,d24
+       vbsl            d29,d22,d23             @ Ch(e,f,g)
+       vshr.u64        d24,d17,#28
+       veor            d26,d25                 @ Sigma1(e)
+       vadd.i64        d27,d29,d16
+       vshr.u64        d25,d17,#34
+       vsli.64         d24,d17,#36
+       vadd.i64        d27,d26
+       vshr.u64        d26,d17,#39
+       vadd.i64        d28,d15
+       vsli.64         d25,d17,#30
+       veor            d30,d17,d18
+       vsli.64         d26,d17,#25
+       veor            d16,d24,d25
+       vadd.i64        d27,d28
+       vbsl            d30,d19,d18             @ Maj(a,b,c)
+       veor            d16,d26                 @ Sigma0(a)
+       vadd.i64        d20,d27
+       vadd.i64        d30,d27
+       @ vadd.i64      d16,d30
+       mov             r12,#4
+.L16_79_neon:
+       subs            r12,#1
+       vshr.u64        q12,q7,#19
+       vshr.u64        q13,q7,#61
+        vadd.i64       d16,d30                 @ h+=Maj from the past
+       vshr.u64        q15,q7,#6
+       vsli.64         q12,q7,#45
+       vext.8          q14,q0,q1,#8    @ X[i+1]
+       vsli.64         q13,q7,#3
+       veor            q15,q12
+       vshr.u64        q12,q14,#1
+       veor            q15,q13                         @ sigma1(X[i+14])
+       vshr.u64        q13,q14,#8
+       vadd.i64        q0,q15
+       vshr.u64        q15,q14,#7
+       vsli.64         q12,q14,#63
+       vsli.64         q13,q14,#56
+       vext.8          q14,q4,q5,#8    @ X[i+9]
+       veor            q15,q12
+       vshr.u64        d24,d20,#14             @ from NEON_00_15
+       vadd.i64        q0,q14
+       vshr.u64        d25,d20,#18             @ from NEON_00_15
+       veor            q15,q13                         @ sigma0(X[i+1])
+       vshr.u64        d26,d20,#41             @ from NEON_00_15
+       vadd.i64        q0,q15
+       vld1.64         {d28},[r3,:64]! @ K[i++]
+       vsli.64         d24,d20,#50
+       vsli.64         d25,d20,#46
+       vmov            d29,d20
+       vsli.64         d26,d20,#23
+#if 16<16 && defined(__ARMEL__)
+       vrev64.8        ,
+#endif
+       veor            d25,d24
+       vbsl            d29,d21,d22             @ Ch(e,f,g)
+       vshr.u64        d24,d16,#28
+       veor            d26,d25                 @ Sigma1(e)
+       vadd.i64        d27,d29,d23
+       vshr.u64        d25,d16,#34
+       vsli.64         d24,d16,#36
+       vadd.i64        d27,d26
+       vshr.u64        d26,d16,#39
+       vadd.i64        d28,d0
+       vsli.64         d25,d16,#30
+       veor            d30,d16,d17
+       vsli.64         d26,d16,#25
+       veor            d23,d24,d25
+       vadd.i64        d27,d28
+       vbsl            d30,d18,d17             @ Maj(a,b,c)
+       veor            d23,d26                 @ Sigma0(a)
+       vadd.i64        d19,d27
+       vadd.i64        d30,d27
+       @ vadd.i64      d23,d30
+       vshr.u64        d24,d19,#14     @ 17
+#if 17<16
+       vld1.64         {d1},[r1]!      @ handles unaligned
+#endif
+       vshr.u64        d25,d19,#18
+#if 17>0
+        vadd.i64       d23,d30                 @ h+=Maj from the past
+#endif
+       vshr.u64        d26,d19,#41
+       vld1.64         {d28},[r3,:64]! @ K[i++]
+       vsli.64         d24,d19,#50
+       vsli.64         d25,d19,#46
+       vmov            d29,d19
+       vsli.64         d26,d19,#23
+#if 17<16 && defined(__ARMEL__)
+       vrev64.8        ,
+#endif
+       veor            d25,d24
+       vbsl            d29,d20,d21             @ Ch(e,f,g)
+       vshr.u64        d24,d23,#28
+       veor            d26,d25                 @ Sigma1(e)
+       vadd.i64        d27,d29,d22
+       vshr.u64        d25,d23,#34
+       vsli.64         d24,d23,#36
+       vadd.i64        d27,d26
+       vshr.u64        d26,d23,#39
+       vadd.i64        d28,d1
+       vsli.64         d25,d23,#30
+       veor            d30,d23,d16
+       vsli.64         d26,d23,#25
+       veor            d22,d24,d25
+       vadd.i64        d27,d28
+       vbsl            d30,d17,d16             @ Maj(a,b,c)
+       veor            d22,d26                 @ Sigma0(a)
+       vadd.i64        d18,d27
+       vadd.i64        d30,d27
+       @ vadd.i64      d22,d30
+       vshr.u64        q12,q0,#19
+       vshr.u64        q13,q0,#61
+        vadd.i64       d22,d30                 @ h+=Maj from the past
+       vshr.u64        q15,q0,#6
+       vsli.64         q12,q0,#45
+       vext.8          q14,q1,q2,#8    @ X[i+1]
+       vsli.64         q13,q0,#3
+       veor            q15,q12
+       vshr.u64        q12,q14,#1
+       veor            q15,q13                         @ sigma1(X[i+14])
+       vshr.u64        q13,q14,#8
+       vadd.i64        q1,q15
+       vshr.u64        q15,q14,#7
+       vsli.64         q12,q14,#63
+       vsli.64         q13,q14,#56
+       vext.8          q14,q5,q6,#8    @ X[i+9]
+       veor            q15,q12
+       vshr.u64        d24,d18,#14             @ from NEON_00_15
+       vadd.i64        q1,q14
+       vshr.u64        d25,d18,#18             @ from NEON_00_15
+       veor            q15,q13                         @ sigma0(X[i+1])
+       vshr.u64        d26,d18,#41             @ from NEON_00_15
+       vadd.i64        q1,q15
+       vld1.64         {d28},[r3,:64]! @ K[i++]
+       vsli.64         d24,d18,#50
+       vsli.64         d25,d18,#46
+       vmov            d29,d18
+       vsli.64         d26,d18,#23
+#if 18<16 && defined(__ARMEL__)
+       vrev64.8        ,
+#endif
+       veor            d25,d24
+       vbsl            d29,d19,d20             @ Ch(e,f,g)
+       vshr.u64        d24,d22,#28
+       veor            d26,d25                 @ Sigma1(e)
+       vadd.i64        d27,d29,d21
+       vshr.u64        d25,d22,#34
+       vsli.64         d24,d22,#36
+       vadd.i64        d27,d26
+       vshr.u64        d26,d22,#39
+       vadd.i64        d28,d2
+       vsli.64         d25,d22,#30
+       veor            d30,d22,d23
+       vsli.64         d26,d22,#25
+       veor            d21,d24,d25
+       vadd.i64        d27,d28
+       vbsl            d30,d16,d23             @ Maj(a,b,c)
+       veor            d21,d26                 @ Sigma0(a)
+       vadd.i64        d17,d27
+       vadd.i64        d30,d27
+       @ vadd.i64      d21,d30
+       vshr.u64        d24,d17,#14     @ 19
+#if 19<16
+       vld1.64         {d3},[r1]!      @ handles unaligned
+#endif
+       vshr.u64        d25,d17,#18
+#if 19>0
+        vadd.i64       d21,d30                 @ h+=Maj from the past
+#endif
+       vshr.u64        d26,d17,#41
+       vld1.64         {d28},[r3,:64]! @ K[i++]
+       vsli.64         d24,d17,#50
+       vsli.64         d25,d17,#46
+       vmov            d29,d17
+       vsli.64         d26,d17,#23
+#if 19<16 && defined(__ARMEL__)
+       vrev64.8        ,
+#endif
+       veor            d25,d24
+       vbsl            d29,d18,d19             @ Ch(e,f,g)
+       vshr.u64        d24,d21,#28
+       veor            d26,d25                 @ Sigma1(e)
+       vadd.i64        d27,d29,d20
+       vshr.u64        d25,d21,#34
+       vsli.64         d24,d21,#36
+       vadd.i64        d27,d26
+       vshr.u64        d26,d21,#39
+       vadd.i64        d28,d3
+       vsli.64         d25,d21,#30
+       veor            d30,d21,d22
+       vsli.64         d26,d21,#25
+       veor            d20,d24,d25
+       vadd.i64        d27,d28
+       vbsl            d30,d23,d22             @ Maj(a,b,c)
+       veor            d20,d26                 @ Sigma0(a)
+       vadd.i64        d16,d27
+       vadd.i64        d30,d27
+       @ vadd.i64      d20,d30
+       vshr.u64        q12,q1,#19
+       vshr.u64        q13,q1,#61
+        vadd.i64       d20,d30                 @ h+=Maj from the past
+       vshr.u64        q15,q1,#6
+       vsli.64         q12,q1,#45
+       vext.8          q14,q2,q3,#8    @ X[i+1]
+       vsli.64         q13,q1,#3
+       veor            q15,q12
+       vshr.u64        q12,q14,#1
+       veor            q15,q13                         @ sigma1(X[i+14])
+       vshr.u64        q13,q14,#8
+       vadd.i64        q2,q15
+       vshr.u64        q15,q14,#7
+       vsli.64         q12,q14,#63
+       vsli.64         q13,q14,#56
+       vext.8          q14,q6,q7,#8    @ X[i+9]
+       veor            q15,q12
+       vshr.u64        d24,d16,#14             @ from NEON_00_15
+       vadd.i64        q2,q14
+       vshr.u64        d25,d16,#18             @ from NEON_00_15
+       veor            q15,q13                         @ sigma0(X[i+1])
+       vshr.u64        d26,d16,#41             @ from NEON_00_15
+       vadd.i64        q2,q15
+       vld1.64         {d28},[r3,:64]! @ K[i++]
+       vsli.64         d24,d16,#50
+       vsli.64         d25,d16,#46
+       vmov            d29,d16
+       vsli.64         d26,d16,#23
+#if 20<16 && defined(__ARMEL__)
+       vrev64.8        ,
+#endif
+       veor            d25,d24
+       vbsl            d29,d17,d18             @ Ch(e,f,g)
+       vshr.u64        d24,d20,#28
+       veor            d26,d25                 @ Sigma1(e)
+       vadd.i64        d27,d29,d19
+       vshr.u64        d25,d20,#34
+       vsli.64         d24,d20,#36
+       vadd.i64        d27,d26
+       vshr.u64        d26,d20,#39
+       vadd.i64        d28,d4
+       vsli.64         d25,d20,#30
+       veor            d30,d20,d21
+       vsli.64         d26,d20,#25
+       veor            d19,d24,d25
+       vadd.i64        d27,d28
+       vbsl            d30,d22,d21             @ Maj(a,b,c)
+       veor            d19,d26                 @ Sigma0(a)
+       vadd.i64        d23,d27
+       vadd.i64        d30,d27
+       @ vadd.i64      d19,d30
+       vshr.u64        d24,d23,#14     @ 21
+#if 21<16
+       vld1.64         {d5},[r1]!      @ handles unaligned
+#endif
+       vshr.u64        d25,d23,#18
+#if 21>0
+        vadd.i64       d19,d30                 @ h+=Maj from the past
+#endif
+       vshr.u64        d26,d23,#41
+       vld1.64         {d28},[r3,:64]! @ K[i++]
+       vsli.64         d24,d23,#50
+       vsli.64         d25,d23,#46
+       vmov            d29,d23
+       vsli.64         d26,d23,#23
+#if 21<16 && defined(__ARMEL__)
+       vrev64.8        ,
+#endif
+       veor            d25,d24
+       vbsl            d29,d16,d17             @ Ch(e,f,g)
+       vshr.u64        d24,d19,#28
+       veor            d26,d25                 @ Sigma1(e)
+       vadd.i64        d27,d29,d18
+       vshr.u64        d25,d19,#34
+       vsli.64         d24,d19,#36
+       vadd.i64        d27,d26
+       vshr.u64        d26,d19,#39
+       vadd.i64        d28,d5
+       vsli.64         d25,d19,#30
+       veor            d30,d19,d20
+       vsli.64         d26,d19,#25
+       veor            d18,d24,d25
+       vadd.i64        d27,d28
+       vbsl            d30,d21,d20             @ Maj(a,b,c)
+       veor            d18,d26                 @ Sigma0(a)
+       vadd.i64        d22,d27
+       vadd.i64        d30,d27
+       @ vadd.i64      d18,d30
+       vshr.u64        q12,q2,#19
+       vshr.u64        q13,q2,#61
+        vadd.i64       d18,d30                 @ h+=Maj from the past
+       vshr.u64        q15,q2,#6
+       vsli.64         q12,q2,#45
+       vext.8          q14,q3,q4,#8    @ X[i+1]
+       vsli.64         q13,q2,#3
+       veor            q15,q12
+       vshr.u64        q12,q14,#1
+       veor            q15,q13                         @ sigma1(X[i+14])
+       vshr.u64        q13,q14,#8
+       vadd.i64        q3,q15
+       vshr.u64        q15,q14,#7
+       vsli.64         q12,q14,#63
+       vsli.64         q13,q14,#56
+       vext.8          q14,q7,q0,#8    @ X[i+9]
+       veor            q15,q12
+       vshr.u64        d24,d22,#14             @ from NEON_00_15
+       vadd.i64        q3,q14
+       vshr.u64        d25,d22,#18             @ from NEON_00_15
+       veor            q15,q13                         @ sigma0(X[i+1])
+       vshr.u64        d26,d22,#41             @ from NEON_00_15
+       vadd.i64        q3,q15
+       vld1.64         {d28},[r3,:64]! @ K[i++]
+       vsli.64         d24,d22,#50
+       vsli.64         d25,d22,#46
+       vmov            d29,d22
+       vsli.64         d26,d22,#23
+#if 22<16 && defined(__ARMEL__)
+       vrev64.8        ,
+#endif
+       veor            d25,d24
+       vbsl            d29,d23,d16             @ Ch(e,f,g)
+       vshr.u64        d24,d18,#28
+       veor            d26,d25                 @ Sigma1(e)
+       vadd.i64        d27,d29,d17
+       vshr.u64        d25,d18,#34
+       vsli.64         d24,d18,#36
+       vadd.i64        d27,d26
+       vshr.u64        d26,d18,#39
+       vadd.i64        d28,d6
+       vsli.64         d25,d18,#30
+       veor            d30,d18,d19
+       vsli.64         d26,d18,#25
+       veor            d17,d24,d25
+       vadd.i64        d27,d28
+       vbsl            d30,d20,d19             @ Maj(a,b,c)
+       veor            d17,d26                 @ Sigma0(a)
+       vadd.i64        d21,d27
+       vadd.i64        d30,d27
+       @ vadd.i64      d17,d30
+       vshr.u64        d24,d21,#14     @ 23
+#if 23<16
+       vld1.64         {d7},[r1]!      @ handles unaligned
+#endif
+       vshr.u64        d25,d21,#18
+#if 23>0
+        vadd.i64       d17,d30                 @ h+=Maj from the past
+#endif
+       vshr.u64        d26,d21,#41
+       vld1.64         {d28},[r3,:64]! @ K[i++]
+       vsli.64         d24,d21,#50
+       vsli.64         d25,d21,#46
+       vmov            d29,d21
+       vsli.64         d26,d21,#23
+#if 23<16 && defined(__ARMEL__)
+       vrev64.8        ,
+#endif
+       veor            d25,d24
+       vbsl            d29,d22,d23             @ Ch(e,f,g)
+       vshr.u64        d24,d17,#28
+       veor            d26,d25                 @ Sigma1(e)
+       vadd.i64        d27,d29,d16
+       vshr.u64        d25,d17,#34
+       vsli.64         d24,d17,#36
+       vadd.i64        d27,d26
+       vshr.u64        d26,d17,#39
+       vadd.i64        d28,d7
+       vsli.64         d25,d17,#30
+       veor            d30,d17,d18
+       vsli.64         d26,d17,#25
+       veor            d16,d24,d25
+       vadd.i64        d27,d28
+       vbsl            d30,d19,d18             @ Maj(a,b,c)
+       veor            d16,d26                 @ Sigma0(a)
+       vadd.i64        d20,d27
+       vadd.i64        d30,d27
+       @ vadd.i64      d16,d30
+       vshr.u64        q12,q3,#19
+       vshr.u64        q13,q3,#61
+        vadd.i64       d16,d30                 @ h+=Maj from the past
+       vshr.u64        q15,q3,#6
+       vsli.64         q12,q3,#45
+       vext.8          q14,q4,q5,#8    @ X[i+1]
+       vsli.64         q13,q3,#3
+       veor            q15,q12
+       vshr.u64        q12,q14,#1
+       veor            q15,q13                         @ sigma1(X[i+14])
+       vshr.u64        q13,q14,#8
+       vadd.i64        q4,q15
+       vshr.u64        q15,q14,#7
+       vsli.64         q12,q14,#63
+       vsli.64         q13,q14,#56
+       vext.8          q14,q0,q1,#8    @ X[i+9]
+       veor            q15,q12
+       vshr.u64        d24,d20,#14             @ from NEON_00_15
+       vadd.i64        q4,q14
+       vshr.u64        d25,d20,#18             @ from NEON_00_15
+       veor            q15,q13                         @ sigma0(X[i+1])
+       vshr.u64        d26,d20,#41             @ from NEON_00_15
+       vadd.i64        q4,q15
+       vld1.64         {d28},[r3,:64]! @ K[i++]
+       vsli.64         d24,d20,#50
+       vsli.64         d25,d20,#46
+       vmov            d29,d20
+       vsli.64         d26,d20,#23
+#if 24<16 && defined(__ARMEL__)
+       vrev64.8        ,
+#endif
+       veor            d25,d24
+       vbsl            d29,d21,d22             @ Ch(e,f,g)
+       vshr.u64        d24,d16,#28
+       veor            d26,d25                 @ Sigma1(e)
+       vadd.i64        d27,d29,d23
+       vshr.u64        d25,d16,#34
+       vsli.64         d24,d16,#36
+       vadd.i64        d27,d26
+       vshr.u64        d26,d16,#39
+       vadd.i64        d28,d8
+       vsli.64         d25,d16,#30
+       veor            d30,d16,d17
+       vsli.64         d26,d16,#25
+       veor            d23,d24,d25
+       vadd.i64        d27,d28
+       vbsl            d30,d18,d17             @ Maj(a,b,c)
+       veor            d23,d26                 @ Sigma0(a)
+       vadd.i64        d19,d27
+       vadd.i64        d30,d27
+       @ vadd.i64      d23,d30
+       vshr.u64        d24,d19,#14     @ 25
+#if 25<16
+       vld1.64         {d9},[r1]!      @ handles unaligned
+#endif
+       vshr.u64        d25,d19,#18
+#if 25>0
+        vadd.i64       d23,d30                 @ h+=Maj from the past
+#endif
+       vshr.u64        d26,d19,#41
+       vld1.64         {d28},[r3,:64]! @ K[i++]
+       vsli.64         d24,d19,#50
+       vsli.64         d25,d19,#46
+       vmov            d29,d19
+       vsli.64         d26,d19,#23
+#if 25<16 && defined(__ARMEL__)
+       vrev64.8        ,
+#endif
+       veor            d25,d24
+       vbsl            d29,d20,d21             @ Ch(e,f,g)
+       vshr.u64        d24,d23,#28
+       veor            d26,d25                 @ Sigma1(e)
+       vadd.i64        d27,d29,d22
+       vshr.u64        d25,d23,#34
+       vsli.64         d24,d23,#36
+       vadd.i64        d27,d26
+       vshr.u64        d26,d23,#39
+       vadd.i64        d28,d9
+       vsli.64         d25,d23,#30
+       veor            d30,d23,d16
+       vsli.64         d26,d23,#25
+       veor            d22,d24,d25
+       vadd.i64        d27,d28
+       vbsl            d30,d17,d16             @ Maj(a,b,c)
+       veor            d22,d26                 @ Sigma0(a)
+       vadd.i64        d18,d27
+       vadd.i64        d30,d27
+       @ vadd.i64      d22,d30
+       vshr.u64        q12,q4,#19
+       vshr.u64        q13,q4,#61
+        vadd.i64       d22,d30                 @ h+=Maj from the past
+       vshr.u64        q15,q4,#6
+       vsli.64         q12,q4,#45
+       vext.8          q14,q5,q6,#8    @ X[i+1]
+       vsli.64         q13,q4,#3
+       veor            q15,q12
+       vshr.u64        q12,q14,#1
+       veor            q15,q13                         @ sigma1(X[i+14])
+       vshr.u64        q13,q14,#8
+       vadd.i64        q5,q15
+       vshr.u64        q15,q14,#7
+       vsli.64         q12,q14,#63
+       vsli.64         q13,q14,#56
+       vext.8          q14,q1,q2,#8    @ X[i+9]
+       veor            q15,q12
+       vshr.u64        d24,d18,#14             @ from NEON_00_15
+       vadd.i64        q5,q14
+       vshr.u64        d25,d18,#18             @ from NEON_00_15
+       veor            q15,q13                         @ sigma0(X[i+1])
+       vshr.u64        d26,d18,#41             @ from NEON_00_15
+       vadd.i64        q5,q15
+       vld1.64         {d28},[r3,:64]! @ K[i++]
+       vsli.64         d24,d18,#50
+       vsli.64         d25,d18,#46
+       vmov            d29,d18
+       vsli.64         d26,d18,#23
+#if 26<16 && defined(__ARMEL__)
+       vrev64.8        ,
+#endif
+       veor            d25,d24
+       vbsl            d29,d19,d20             @ Ch(e,f,g)
+       vshr.u64        d24,d22,#28
+       veor            d26,d25                 @ Sigma1(e)
+       vadd.i64        d27,d29,d21
+       vshr.u64        d25,d22,#34
+       vsli.64         d24,d22,#36
+       vadd.i64        d27,d26
+       vshr.u64        d26,d22,#39
+       vadd.i64        d28,d10
+       vsli.64         d25,d22,#30
+       veor            d30,d22,d23
+       vsli.64         d26,d22,#25
+       veor            d21,d24,d25
+       vadd.i64        d27,d28
+       vbsl            d30,d16,d23             @ Maj(a,b,c)
+       veor            d21,d26                 @ Sigma0(a)
+       vadd.i64        d17,d27
+       vadd.i64        d30,d27
+       @ vadd.i64      d21,d30
+       vshr.u64        d24,d17,#14     @ 27
+#if 27<16
+       vld1.64         {d11},[r1]!     @ handles unaligned
+#endif
+       vshr.u64        d25,d17,#18
+#if 27>0
+        vadd.i64       d21,d30                 @ h+=Maj from the past
+#endif
+       vshr.u64        d26,d17,#41
+       vld1.64         {d28},[r3,:64]! @ K[i++]
+       vsli.64         d24,d17,#50
+       vsli.64         d25,d17,#46
+       vmov            d29,d17
+       vsli.64         d26,d17,#23
+#if 27<16 && defined(__ARMEL__)
+       vrev64.8        ,
+#endif
+       veor            d25,d24
+       vbsl            d29,d18,d19             @ Ch(e,f,g)
+       vshr.u64        d24,d21,#28
+       veor            d26,d25                 @ Sigma1(e)
+       vadd.i64        d27,d29,d20
+       vshr.u64        d25,d21,#34
+       vsli.64         d24,d21,#36
+       vadd.i64        d27,d26
+       vshr.u64        d26,d21,#39
+       vadd.i64        d28,d11
+       vsli.64         d25,d21,#30
+       veor            d30,d21,d22
+       vsli.64         d26,d21,#25
+       veor            d20,d24,d25
+       vadd.i64        d27,d28
+       vbsl            d30,d23,d22             @ Maj(a,b,c)
+       veor            d20,d26                 @ Sigma0(a)
+       vadd.i64        d16,d27
+       vadd.i64        d30,d27
+       @ vadd.i64      d20,d30
+       vshr.u64        q12,q5,#19
+       vshr.u64        q13,q5,#61
+        vadd.i64       d20,d30                 @ h+=Maj from the past
+       vshr.u64        q15,q5,#6
+       vsli.64         q12,q5,#45
+       vext.8          q14,q6,q7,#8    @ X[i+1]
+       vsli.64         q13,q5,#3
+       veor            q15,q12
+       vshr.u64        q12,q14,#1
+       veor            q15,q13                         @ sigma1(X[i+14])
+       vshr.u64        q13,q14,#8
+       vadd.i64        q6,q15
+       vshr.u64        q15,q14,#7
+       vsli.64         q12,q14,#63
+       vsli.64         q13,q14,#56
+       vext.8          q14,q2,q3,#8    @ X[i+9]
+       veor            q15,q12
+       vshr.u64        d24,d16,#14             @ from NEON_00_15
+       vadd.i64        q6,q14
+       vshr.u64        d25,d16,#18             @ from NEON_00_15
+       veor            q15,q13                         @ sigma0(X[i+1])
+       vshr.u64        d26,d16,#41             @ from NEON_00_15
+       vadd.i64        q6,q15
+       vld1.64         {d28},[r3,:64]! @ K[i++]
+       vsli.64         d24,d16,#50
+       vsli.64         d25,d16,#46
+       vmov            d29,d16
+       vsli.64         d26,d16,#23
+#if 28<16 && defined(__ARMEL__)
+       vrev64.8        ,
+#endif
+       veor            d25,d24
+       vbsl            d29,d17,d18             @ Ch(e,f,g)
+       vshr.u64        d24,d20,#28
+       veor            d26,d25                 @ Sigma1(e)
+       vadd.i64        d27,d29,d19
+       vshr.u64        d25,d20,#34
+       vsli.64         d24,d20,#36
+       vadd.i64        d27,d26
+       vshr.u64        d26,d20,#39
+       vadd.i64        d28,d12
+       vsli.64         d25,d20,#30
+       veor            d30,d20,d21
+       vsli.64         d26,d20,#25
+       veor            d19,d24,d25
+       vadd.i64        d27,d28
+       vbsl            d30,d22,d21             @ Maj(a,b,c)
+       veor            d19,d26                 @ Sigma0(a)
+       vadd.i64        d23,d27
+       vadd.i64        d30,d27
+       @ vadd.i64      d19,d30
+       vshr.u64        d24,d23,#14     @ 29
+#if 29<16
+       vld1.64         {d13},[r1]!     @ handles unaligned
+#endif
+       vshr.u64        d25,d23,#18
+#if 29>0
+        vadd.i64       d19,d30                 @ h+=Maj from the past
+#endif
+       vshr.u64        d26,d23,#41
+       vld1.64         {d28},[r3,:64]! @ K[i++]
+       vsli.64         d24,d23,#50
+       vsli.64         d25,d23,#46
+       vmov            d29,d23
+       vsli.64         d26,d23,#23
+#if 29<16 && defined(__ARMEL__)
+       vrev64.8        ,
+#endif
+       veor            d25,d24
+       vbsl            d29,d16,d17             @ Ch(e,f,g)
+       vshr.u64        d24,d19,#28
+       veor            d26,d25                 @ Sigma1(e)
+       vadd.i64        d27,d29,d18
+       vshr.u64        d25,d19,#34
+       vsli.64         d24,d19,#36
+       vadd.i64        d27,d26
+       vshr.u64        d26,d19,#39
+       vadd.i64        d28,d13
+       vsli.64         d25,d19,#30
+       veor            d30,d19,d20
+       vsli.64         d26,d19,#25
+       veor            d18,d24,d25
+       vadd.i64        d27,d28
+       vbsl            d30,d21,d20             @ Maj(a,b,c)
+       veor            d18,d26                 @ Sigma0(a)
+       vadd.i64        d22,d27
+       vadd.i64        d30,d27
+       @ vadd.i64      d18,d30
+       vshr.u64        q12,q6,#19
+       vshr.u64        q13,q6,#61
+        vadd.i64       d18,d30                 @ h+=Maj from the past
+       vshr.u64        q15,q6,#6
+       vsli.64         q12,q6,#45
+       vext.8          q14,q7,q0,#8    @ X[i+1]
+       vsli.64         q13,q6,#3
+       veor            q15,q12
+       vshr.u64        q12,q14,#1
+       veor            q15,q13                         @ sigma1(X[i+14])
+       vshr.u64        q13,q14,#8
+       vadd.i64        q7,q15
+       vshr.u64        q15,q14,#7
+       vsli.64         q12,q14,#63
+       vsli.64         q13,q14,#56
+       vext.8          q14,q3,q4,#8    @ X[i+9]
+       veor            q15,q12
+       vshr.u64        d24,d22,#14             @ from NEON_00_15
+       vadd.i64        q7,q14
+       vshr.u64        d25,d22,#18             @ from NEON_00_15
+       veor            q15,q13                         @ sigma0(X[i+1])
+       vshr.u64        d26,d22,#41             @ from NEON_00_15
+       vadd.i64        q7,q15
+       vld1.64         {d28},[r3,:64]! @ K[i++]
+       vsli.64         d24,d22,#50
+       vsli.64         d25,d22,#46
+       vmov            d29,d22
+       vsli.64         d26,d22,#23
+#if 30<16 && defined(__ARMEL__)
+       vrev64.8        ,
+#endif
+       veor            d25,d24
+       vbsl            d29,d23,d16             @ Ch(e,f,g)
+       vshr.u64        d24,d18,#28
+       veor            d26,d25                 @ Sigma1(e)
+       vadd.i64        d27,d29,d17
+       vshr.u64        d25,d18,#34
+       vsli.64         d24,d18,#36
+       vadd.i64        d27,d26
+       vshr.u64        d26,d18,#39
+       vadd.i64        d28,d14
+       vsli.64         d25,d18,#30
+       veor            d30,d18,d19
+       vsli.64         d26,d18,#25
+       veor            d17,d24,d25
+       vadd.i64        d27,d28
+       vbsl            d30,d20,d19             @ Maj(a,b,c)
+       veor            d17,d26                 @ Sigma0(a)
+       vadd.i64        d21,d27
+       vadd.i64        d30,d27
+       @ vadd.i64      d17,d30
+       vshr.u64        d24,d21,#14     @ 31
+#if 31<16
+       vld1.64         {d15},[r1]!     @ handles unaligned
+#endif
+       vshr.u64        d25,d21,#18
+#if 31>0
+        vadd.i64       d17,d30                 @ h+=Maj from the past
+#endif
+       vshr.u64        d26,d21,#41
+       vld1.64         {d28},[r3,:64]! @ K[i++]
+       vsli.64         d24,d21,#50
+       vsli.64         d25,d21,#46
+       vmov            d29,d21
+       vsli.64         d26,d21,#23
+#if 31<16 && defined(__ARMEL__)
+       vrev64.8        ,
+#endif
+       veor            d25,d24
+       vbsl            d29,d22,d23             @ Ch(e,f,g)
+       vshr.u64        d24,d17,#28
+       veor            d26,d25                 @ Sigma1(e)
+       vadd.i64        d27,d29,d16
+       vshr.u64        d25,d17,#34
+       vsli.64         d24,d17,#36
+       vadd.i64        d27,d26
+       vshr.u64        d26,d17,#39
+       vadd.i64        d28,d15
+       vsli.64         d25,d17,#30
+       veor            d30,d17,d18
+       vsli.64         d26,d17,#25
+       veor            d16,d24,d25
+       vadd.i64        d27,d28
+       vbsl            d30,d19,d18             @ Maj(a,b,c)
+       veor            d16,d26                 @ Sigma0(a)
+       vadd.i64        d20,d27
+       vadd.i64        d30,d27
+       @ vadd.i64      d16,d30
+       bne             .L16_79_neon
+
+        vadd.i64       d16,d30         @ h+=Maj from the past
+       vldmia          r0,{d24-d31}    @ load context to temp
+       vadd.i64        q8,q12          @ vectorized accumulate
+       vadd.i64        q9,q13
+       vadd.i64        q10,q14
+       vadd.i64        q11,q15
+       vstmia          r0,{d16-d23}    @ save context
+       teq             r1,r2
+       sub             r3,#640 @ rewind K512
+       bne             .Loop_neon
+
+       VFP_ABI_POP
+       bx      lr                              @ .word 0xe12fff1e
+.size  sha512_block_data_order_neon,.-sha512_block_data_order_neon
+#endif
+.asciz "SHA512 block transform for ARMv4/NEON, CRYPTOGAMS by <appro@openssl.org>"
+.align 2
+#if __ARM_MAX_ARCH__>=7 && !defined(__KERNEL__)
+.comm  OPENSSL_armcap_P,4,4
+#endif
diff --git a/arch/arm/crypto/sha512-glue.c b/arch/arm/crypto/sha512-glue.c
new file mode 100644 (file)
index 0000000..269a394
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ * sha512-glue.c - accelerated SHA-384/512 for ARM
+ *
+ * Copyright (C) 2015 Linaro Ltd <ard.biesheuvel@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <crypto/internal/hash.h>
+#include <crypto/sha.h>
+#include <crypto/sha512_base.h>
+#include <linux/crypto.h>
+#include <linux/module.h>
+
+#include <asm/hwcap.h>
+#include <asm/neon.h>
+
+#include "sha512.h"
+
+MODULE_DESCRIPTION("Accelerated SHA-384/SHA-512 secure hash for ARM");
+MODULE_AUTHOR("Ard Biesheuvel <ard.biesheuvel@linaro.org>");
+MODULE_LICENSE("GPL v2");
+
+MODULE_ALIAS_CRYPTO("sha384");
+MODULE_ALIAS_CRYPTO("sha512");
+MODULE_ALIAS_CRYPTO("sha384-arm");
+MODULE_ALIAS_CRYPTO("sha512-arm");
+
+asmlinkage void sha512_block_data_order(u64 *state, u8 const *src, int blocks);
+
+int sha512_arm_update(struct shash_desc *desc, const u8 *data,
+                     unsigned int len)
+{
+       return sha512_base_do_update(desc, data, len,
+               (sha512_block_fn *)sha512_block_data_order);
+}
+
+int sha512_arm_final(struct shash_desc *desc, u8 *out)
+{
+       sha512_base_do_finalize(desc,
+               (sha512_block_fn *)sha512_block_data_order);
+       return sha512_base_finish(desc, out);
+}
+
+int sha512_arm_finup(struct shash_desc *desc, const u8 *data,
+                    unsigned int len, u8 *out)
+{
+       sha512_base_do_update(desc, data, len,
+               (sha512_block_fn *)sha512_block_data_order);
+       return sha512_arm_final(desc, out);
+}
+
+static struct shash_alg sha512_arm_algs[] = { {
+       .init                   = sha384_base_init,
+       .update                 = sha512_arm_update,
+       .final                  = sha512_arm_final,
+       .finup                  = sha512_arm_finup,
+       .descsize               = sizeof(struct sha512_state),
+       .digestsize             = SHA384_DIGEST_SIZE,
+       .base                   = {
+               .cra_name               = "sha384",
+               .cra_driver_name        = "sha384-arm",
+               .cra_priority           = 250,
+               .cra_flags              = CRYPTO_ALG_TYPE_SHASH,
+               .cra_blocksize          = SHA512_BLOCK_SIZE,
+               .cra_module             = THIS_MODULE,
+       }
+},  {
+       .init                   = sha512_base_init,
+       .update                 = sha512_arm_update,
+       .final                  = sha512_arm_final,
+       .finup                  = sha512_arm_finup,
+       .descsize               = sizeof(struct sha512_state),
+       .digestsize             = SHA512_DIGEST_SIZE,
+       .base                   = {
+               .cra_name               = "sha512",
+               .cra_driver_name        = "sha512-arm",
+               .cra_priority           = 250,
+               .cra_flags              = CRYPTO_ALG_TYPE_SHASH,
+               .cra_blocksize          = SHA512_BLOCK_SIZE,
+               .cra_module             = THIS_MODULE,
+       }
+} };
+
+static int __init sha512_arm_mod_init(void)
+{
+       int err;
+
+       err = crypto_register_shashes(sha512_arm_algs,
+                                     ARRAY_SIZE(sha512_arm_algs));
+       if (err)
+               return err;
+
+       if (IS_ENABLED(CONFIG_KERNEL_MODE_NEON) && cpu_has_neon()) {
+               err = crypto_register_shashes(sha512_neon_algs,
+                                             ARRAY_SIZE(sha512_neon_algs));
+               if (err)
+                       goto err_unregister;
+       }
+       return 0;
+
+err_unregister:
+       crypto_unregister_shashes(sha512_arm_algs,
+                                 ARRAY_SIZE(sha512_arm_algs));
+
+       return err;
+}
+
+static void __exit sha512_arm_mod_fini(void)
+{
+       crypto_unregister_shashes(sha512_arm_algs,
+                                 ARRAY_SIZE(sha512_arm_algs));
+       if (IS_ENABLED(CONFIG_KERNEL_MODE_NEON) && cpu_has_neon())
+               crypto_unregister_shashes(sha512_neon_algs,
+                                         ARRAY_SIZE(sha512_neon_algs));
+}
+
+module_init(sha512_arm_mod_init);
+module_exit(sha512_arm_mod_fini);
diff --git a/arch/arm/crypto/sha512-neon-glue.c b/arch/arm/crypto/sha512-neon-glue.c
new file mode 100644 (file)
index 0000000..3269368
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * sha512-neon-glue.c - accelerated SHA-384/512 for ARM NEON
+ *
+ * Copyright (C) 2015 Linaro Ltd <ard.biesheuvel@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <crypto/internal/hash.h>
+#include <crypto/sha.h>
+#include <crypto/sha512_base.h>
+#include <linux/crypto.h>
+#include <linux/module.h>
+
+#include <asm/simd.h>
+#include <asm/neon.h>
+
+#include "sha512.h"
+
+MODULE_ALIAS_CRYPTO("sha384-neon");
+MODULE_ALIAS_CRYPTO("sha512-neon");
+
+asmlinkage void sha512_block_data_order_neon(u64 *state, u8 const *src,
+                                            int blocks);
+
+static int sha512_neon_update(struct shash_desc *desc, const u8 *data,
+                             unsigned int len)
+{
+       struct sha512_state *sctx = shash_desc_ctx(desc);
+
+       if (!may_use_simd() ||
+           (sctx->count[0] % SHA512_BLOCK_SIZE) + len < SHA512_BLOCK_SIZE)
+               return sha512_arm_update(desc, data, len);
+
+       kernel_neon_begin();
+       sha512_base_do_update(desc, data, len,
+               (sha512_block_fn *)sha512_block_data_order_neon);
+       kernel_neon_end();
+
+       return 0;
+}
+
+static int sha512_neon_finup(struct shash_desc *desc, const u8 *data,
+                            unsigned int len, u8 *out)
+{
+       if (!may_use_simd())
+               return sha512_arm_finup(desc, data, len, out);
+
+       kernel_neon_begin();
+       if (len)
+               sha512_base_do_update(desc, data, len,
+                       (sha512_block_fn *)sha512_block_data_order_neon);
+       sha512_base_do_finalize(desc,
+               (sha512_block_fn *)sha512_block_data_order_neon);
+       kernel_neon_end();
+
+       return sha512_base_finish(desc, out);
+}
+
+static int sha512_neon_final(struct shash_desc *desc, u8 *out)
+{
+       return sha512_neon_finup(desc, NULL, 0, out);
+}
+
+struct shash_alg sha512_neon_algs[] = { {
+       .init                   = sha384_base_init,
+       .update                 = sha512_neon_update,
+       .final                  = sha512_neon_final,
+       .finup                  = sha512_neon_finup,
+       .descsize               = sizeof(struct sha512_state),
+       .digestsize             = SHA384_DIGEST_SIZE,
+       .base                   = {
+               .cra_name               = "sha384",
+               .cra_driver_name        = "sha384-neon",
+               .cra_priority           = 300,
+               .cra_flags              = CRYPTO_ALG_TYPE_SHASH,
+               .cra_blocksize          = SHA384_BLOCK_SIZE,
+               .cra_module             = THIS_MODULE,
+
+       }
+},  {
+       .init                   = sha512_base_init,
+       .update                 = sha512_neon_update,
+       .final                  = sha512_neon_final,
+       .finup                  = sha512_neon_finup,
+       .descsize               = sizeof(struct sha512_state),
+       .digestsize             = SHA512_DIGEST_SIZE,
+       .base                   = {
+               .cra_name               = "sha512",
+               .cra_driver_name        = "sha512-neon",
+               .cra_priority           = 300,
+               .cra_flags              = CRYPTO_ALG_TYPE_SHASH,
+               .cra_blocksize          = SHA512_BLOCK_SIZE,
+               .cra_module             = THIS_MODULE,
+       }
+} };
diff --git a/arch/arm/crypto/sha512.h b/arch/arm/crypto/sha512.h
new file mode 100644 (file)
index 0000000..a75d9a8
--- /dev/null
@@ -0,0 +1,8 @@
+
+int sha512_arm_update(struct shash_desc *desc, const u8 *data,
+                     unsigned int len);
+
+int sha512_arm_finup(struct shash_desc *desc, const u8 *data,
+                    unsigned int len, u8 *out);
+
+extern struct shash_alg sha512_neon_algs[2];
diff --git a/arch/arm/crypto/sha512_neon_glue.c b/arch/arm/crypto/sha512_neon_glue.c
deleted file mode 100644 (file)
index b124dce..0000000
+++ /dev/null
@@ -1,305 +0,0 @@
-/*
- * Glue code for the SHA512 Secure Hash Algorithm assembly implementation
- * using NEON instructions.
- *
- * Copyright © 2014 Jussi Kivilinna <jussi.kivilinna@iki.fi>
- *
- * This file is based on sha512_ssse3_glue.c:
- *   Copyright (C) 2013 Intel Corporation
- *   Author: Tim Chen <tim.c.chen@linux.intel.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- */
-
-#include <crypto/internal/hash.h>
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/mm.h>
-#include <linux/cryptohash.h>
-#include <linux/types.h>
-#include <linux/string.h>
-#include <crypto/sha.h>
-#include <asm/byteorder.h>
-#include <asm/simd.h>
-#include <asm/neon.h>
-
-
-static const u64 sha512_k[] = {
-       0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL,
-       0xb5c0fbcfec4d3b2fULL, 0xe9b5dba58189dbbcULL,
-       0x3956c25bf348b538ULL, 0x59f111f1b605d019ULL,
-       0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL,
-       0xd807aa98a3030242ULL, 0x12835b0145706fbeULL,
-       0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL,
-       0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL,
-       0x9bdc06a725c71235ULL, 0xc19bf174cf692694ULL,
-       0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL,
-       0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL,
-       0x2de92c6f592b0275ULL, 0x4a7484aa6ea6e483ULL,
-       0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL,
-       0x983e5152ee66dfabULL, 0xa831c66d2db43210ULL,
-       0xb00327c898fb213fULL, 0xbf597fc7beef0ee4ULL,
-       0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL,
-       0x06ca6351e003826fULL, 0x142929670a0e6e70ULL,
-       0x27b70a8546d22ffcULL, 0x2e1b21385c26c926ULL,
-       0x4d2c6dfc5ac42aedULL, 0x53380d139d95b3dfULL,
-       0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL,
-       0x81c2c92e47edaee6ULL, 0x92722c851482353bULL,
-       0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL,
-       0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL,
-       0xd192e819d6ef5218ULL, 0xd69906245565a910ULL,
-       0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL,
-       0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL,
-       0x2748774cdf8eeb99ULL, 0x34b0bcb5e19b48a8ULL,
-       0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL,
-       0x5b9cca4f7763e373ULL, 0x682e6ff3d6b2b8a3ULL,
-       0x748f82ee5defb2fcULL, 0x78a5636f43172f60ULL,
-       0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL,
-       0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL,
-       0xbef9a3f7b2c67915ULL, 0xc67178f2e372532bULL,
-       0xca273eceea26619cULL, 0xd186b8c721c0c207ULL,
-       0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL,
-       0x06f067aa72176fbaULL, 0x0a637dc5a2c898a6ULL,
-       0x113f9804bef90daeULL, 0x1b710b35131c471bULL,
-       0x28db77f523047d84ULL, 0x32caab7b40c72493ULL,
-       0x3c9ebe0a15c9bebcULL, 0x431d67c49c100d4cULL,
-       0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL,
-       0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL
-};
-
-
-asmlinkage void sha512_transform_neon(u64 *digest, const void *data,
-                                     const u64 k[], unsigned int num_blks);
-
-
-static int sha512_neon_init(struct shash_desc *desc)
-{
-       struct sha512_state *sctx = shash_desc_ctx(desc);
-
-       sctx->state[0] = SHA512_H0;
-       sctx->state[1] = SHA512_H1;
-       sctx->state[2] = SHA512_H2;
-       sctx->state[3] = SHA512_H3;
-       sctx->state[4] = SHA512_H4;
-       sctx->state[5] = SHA512_H5;
-       sctx->state[6] = SHA512_H6;
-       sctx->state[7] = SHA512_H7;
-       sctx->count[0] = sctx->count[1] = 0;
-
-       return 0;
-}
-
-static int __sha512_neon_update(struct shash_desc *desc, const u8 *data,
-                               unsigned int len, unsigned int partial)
-{
-       struct sha512_state *sctx = shash_desc_ctx(desc);
-       unsigned int done = 0;
-
-       sctx->count[0] += len;
-       if (sctx->count[0] < len)
-               sctx->count[1]++;
-
-       if (partial) {
-               done = SHA512_BLOCK_SIZE - partial;
-               memcpy(sctx->buf + partial, data, done);
-               sha512_transform_neon(sctx->state, sctx->buf, sha512_k, 1);
-       }
-
-       if (len - done >= SHA512_BLOCK_SIZE) {
-               const unsigned int rounds = (len - done) / SHA512_BLOCK_SIZE;
-
-               sha512_transform_neon(sctx->state, data + done, sha512_k,
-                                     rounds);
-
-               done += rounds * SHA512_BLOCK_SIZE;
-       }
-
-       memcpy(sctx->buf, data + done, len - done);
-
-       return 0;
-}
-
-static int sha512_neon_update(struct shash_desc *desc, const u8 *data,
-                            unsigned int len)
-{
-       struct sha512_state *sctx = shash_desc_ctx(desc);
-       unsigned int partial = sctx->count[0] % SHA512_BLOCK_SIZE;
-       int res;
-
-       /* Handle the fast case right here */
-       if (partial + len < SHA512_BLOCK_SIZE) {
-               sctx->count[0] += len;
-               if (sctx->count[0] < len)
-                       sctx->count[1]++;
-               memcpy(sctx->buf + partial, data, len);
-
-               return 0;
-       }
-
-       if (!may_use_simd()) {
-               res = crypto_sha512_update(desc, data, len);
-       } else {
-               kernel_neon_begin();
-               res = __sha512_neon_update(desc, data, len, partial);
-               kernel_neon_end();
-       }
-
-       return res;
-}
-
-
-/* Add padding and return the message digest. */
-static int sha512_neon_final(struct shash_desc *desc, u8 *out)
-{
-       struct sha512_state *sctx = shash_desc_ctx(desc);
-       unsigned int i, index, padlen;
-       __be64 *dst = (__be64 *)out;
-       __be64 bits[2];
-       static const u8 padding[SHA512_BLOCK_SIZE] = { 0x80, };
-
-       /* save number of bits */
-       bits[1] = cpu_to_be64(sctx->count[0] << 3);
-       bits[0] = cpu_to_be64(sctx->count[1] << 3 | sctx->count[0] >> 61);
-
-       /* Pad out to 112 mod 128 and append length */
-       index = sctx->count[0] & 0x7f;
-       padlen = (index < 112) ? (112 - index) : ((128+112) - index);
-
-       if (!may_use_simd()) {
-               crypto_sha512_update(desc, padding, padlen);
-               crypto_sha512_update(desc, (const u8 *)&bits, sizeof(bits));
-       } else {
-               kernel_neon_begin();
-               /* We need to fill a whole block for __sha512_neon_update() */
-               if (padlen <= 112) {
-                       sctx->count[0] += padlen;
-                       if (sctx->count[0] < padlen)
-                               sctx->count[1]++;
-                       memcpy(sctx->buf + index, padding, padlen);
-               } else {
-                       __sha512_neon_update(desc, padding, padlen, index);
-               }
-               __sha512_neon_update(desc, (const u8 *)&bits,
-                                       sizeof(bits), 112);
-               kernel_neon_end();
-       }
-
-       /* Store state in digest */
-       for (i = 0; i < 8; i++)
-               dst[i] = cpu_to_be64(sctx->state[i]);
-
-       /* Wipe context */
-       memset(sctx, 0, sizeof(*sctx));
-
-       return 0;
-}
-
-static int sha512_neon_export(struct shash_desc *desc, void *out)
-{
-       struct sha512_state *sctx = shash_desc_ctx(desc);
-
-       memcpy(out, sctx, sizeof(*sctx));
-
-       return 0;
-}
-
-static int sha512_neon_import(struct shash_desc *desc, const void *in)
-{
-       struct sha512_state *sctx = shash_desc_ctx(desc);
-
-       memcpy(sctx, in, sizeof(*sctx));
-
-       return 0;
-}
-
-static int sha384_neon_init(struct shash_desc *desc)
-{
-       struct sha512_state *sctx = shash_desc_ctx(desc);
-
-       sctx->state[0] = SHA384_H0;
-       sctx->state[1] = SHA384_H1;
-       sctx->state[2] = SHA384_H2;
-       sctx->state[3] = SHA384_H3;
-       sctx->state[4] = SHA384_H4;
-       sctx->state[5] = SHA384_H5;
-       sctx->state[6] = SHA384_H6;
-       sctx->state[7] = SHA384_H7;
-
-       sctx->count[0] = sctx->count[1] = 0;
-
-       return 0;
-}
-
-static int sha384_neon_final(struct shash_desc *desc, u8 *hash)
-{
-       u8 D[SHA512_DIGEST_SIZE];
-
-       sha512_neon_final(desc, D);
-
-       memcpy(hash, D, SHA384_DIGEST_SIZE);
-       memzero_explicit(D, SHA512_DIGEST_SIZE);
-
-       return 0;
-}
-
-static struct shash_alg algs[] = { {
-       .digestsize     =       SHA512_DIGEST_SIZE,
-       .init           =       sha512_neon_init,
-       .update         =       sha512_neon_update,
-       .final          =       sha512_neon_final,
-       .export         =       sha512_neon_export,
-       .import         =       sha512_neon_import,
-       .descsize       =       sizeof(struct sha512_state),
-       .statesize      =       sizeof(struct sha512_state),
-       .base           =       {
-               .cra_name       =       "sha512",
-               .cra_driver_name =      "sha512-neon",
-               .cra_priority   =       250,
-               .cra_flags      =       CRYPTO_ALG_TYPE_SHASH,
-               .cra_blocksize  =       SHA512_BLOCK_SIZE,
-               .cra_module     =       THIS_MODULE,
-       }
-},  {
-       .digestsize     =       SHA384_DIGEST_SIZE,
-       .init           =       sha384_neon_init,
-       .update         =       sha512_neon_update,
-       .final          =       sha384_neon_final,
-       .export         =       sha512_neon_export,
-       .import         =       sha512_neon_import,
-       .descsize       =       sizeof(struct sha512_state),
-       .statesize      =       sizeof(struct sha512_state),
-       .base           =       {
-               .cra_name       =       "sha384",
-               .cra_driver_name =      "sha384-neon",
-               .cra_priority   =       250,
-               .cra_flags      =       CRYPTO_ALG_TYPE_SHASH,
-               .cra_blocksize  =       SHA384_BLOCK_SIZE,
-               .cra_module     =       THIS_MODULE,
-       }
-} };
-
-static int __init sha512_neon_mod_init(void)
-{
-       if (!cpu_has_neon())
-               return -ENODEV;
-
-       return crypto_register_shashes(algs, ARRAY_SIZE(algs));
-}
-
-static void __exit sha512_neon_mod_fini(void)
-{
-       crypto_unregister_shashes(algs, ARRAY_SIZE(algs));
-}
-
-module_init(sha512_neon_mod_init);
-module_exit(sha512_neon_mod_fini);
-
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("SHA512 Secure Hash Algorithm, NEON accelerated");
-
-MODULE_ALIAS_CRYPTO("sha512");
-MODULE_ALIAS_CRYPTO("sha384");
index d2f81e6b8c1cc5adb914ce38a7ab991b25801137..6c2327e1c7323d79831af30bb296c55a9409e9bd 100644 (file)
@@ -81,7 +81,7 @@ do {                                                                  \
 #define read_barrier_depends()         do { } while(0)
 #define smp_read_barrier_depends()     do { } while(0)
 
-#define set_mb(var, value)     do { var = value; smp_mb(); } while (0)
+#define smp_store_mb(var, value)       do { WRITE_ONCE(var, value); smp_mb(); } while (0)
 
 #define smp_mb__before_atomic()        smp_mb()
 #define smp_mb__after_atomic() smp_mb()
index db58deb00aa74c8176380075d06b60e6b0858009..1b7677d1e5e12063167dbbd05745d7ea82506678 100644 (file)
@@ -336,6 +336,7 @@ extern void _memset_io(volatile void __iomem *, int, size_t);
 #define ioremap_nocache(cookie,size)   __arm_ioremap((cookie), (size), MT_DEVICE)
 #define ioremap_cache(cookie,size)     __arm_ioremap((cookie), (size), MT_DEVICE_CACHED)
 #define ioremap_wc(cookie,size)                __arm_ioremap((cookie), (size), MT_DEVICE_WC)
+#define ioremap_wt(cookie,size)                __arm_ioremap((cookie), (size), MT_DEVICE)
 #define iounmap                                __arm_iounmap
 
 /*
index 25410b2d8bc1046ccf08749fca4d5ea29a6ec1c7..194c91b610ffecfd4071da89d16b923c614bf68d 100644 (file)
@@ -23,7 +23,7 @@
 #define c0_MPIDR       1       /* MultiProcessor ID Register */
 #define c0_CSSELR      2       /* Cache Size Selection Register */
 #define c1_SCTLR       3       /* System Control Register */
-#define c1_ACTLR       4       /* Auxilliary Control Register */
+#define c1_ACTLR       4       /* Auxiliary Control Register */
 #define c1_CPACR       5       /* Coprocessor Access Control */
 #define c2_TTBR0       6       /* Translation Table Base Register 0 */
 #define c2_TTBR0_high  7       /* TTBR0 top 32 bits */
index d71607c16601b6b1e1a595e32562195ccd63f5b1..e896d2c196e63b79365e6f637c7b697c56c48f71 100644 (file)
@@ -218,11 +218,6 @@ static inline int kvm_arch_dev_ioctl_check_extension(long ext)
        return 0;
 }
 
-static inline void vgic_arch_setup(const struct vgic_params *vgic)
-{
-       BUG_ON(vgic->type != VGIC_V2);
-}
-
 int kvm_perf_init(void);
 int kvm_perf_teardown(void);
 
index 585dc33a7a240bb7ae58804acf6b1e4bea1c3d90..a5635444ca410b49b5109a65535321d9c3df8d5c 100644 (file)
@@ -31,16 +31,6 @@ static inline int pci_proc_domain(struct pci_bus *bus)
  */
 #define PCI_DMA_BUS_IS_PHYS     (1)
 
-#ifdef CONFIG_PCI
-static inline void pci_dma_burst_advice(struct pci_dev *pdev,
-                                       enum pci_dma_burst_strategy *strat,
-                                       unsigned long *strategy_parameter)
-{
-       *strat = PCI_DMA_BURST_INFINITY;
-       *strategy_parameter = ~0UL;
-}
-#endif
-
 #define HAVE_PCI_MMAP
 extern int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma,
                                enum pci_mmap_state mmap_state, int write_combine);
index f1f79d1043096093a780e46e4a68cf3796a153ac..bfb915d0566566978b8e51c5ae7c93adfad04d0f 100644 (file)
@@ -28,6 +28,7 @@ config KVM
        select KVM_GENERIC_DIRTYLOG_READ_PROTECT
        select SRCU
        select MMU_NOTIFIER
+       select KVM_VFIO
        select HAVE_KVM_EVENTFD
        select HAVE_KVM_IRQFD
        depends on ARM_VIRT_EXT && ARM_LPAE && ARM_ARCH_TIMER
index 139e46c08b6ec5daff4a3692a569027cf23c0347..c5eef02c52ba76b24ac7fa996e04a988d674e751 100644 (file)
@@ -15,7 +15,7 @@ AFLAGS_init.o := -Wa,-march=armv7-a$(plus_virt)
 AFLAGS_interrupts.o := -Wa,-march=armv7-a$(plus_virt)
 
 KVM := ../../../virt/kvm
-kvm-arm-y = $(KVM)/kvm_main.o $(KVM)/coalesced_mmio.o $(KVM)/eventfd.o
+kvm-arm-y = $(KVM)/kvm_main.o $(KVM)/coalesced_mmio.o $(KVM)/eventfd.o $(KVM)/vfio.o
 
 obj-y += kvm-arm.o init.o interrupts.o
 obj-y += arm.o handle_exit.o guest.o mmu.o emulate.o reset.o
index d9631ecddd56ea09a9b9f922520e2e05576c974a..bc738d2b83929e6a05762ea8b73af6ec1ed86837 100644 (file)
@@ -171,7 +171,6 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
        int r;
        switch (ext) {
        case KVM_CAP_IRQCHIP:
-       case KVM_CAP_IRQFD:
        case KVM_CAP_IOEVENTFD:
        case KVM_CAP_DEVICE_CTRL:
        case KVM_CAP_USER_MEMORY:
@@ -532,6 +531,7 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run)
                kvm_vgic_flush_hwstate(vcpu);
                kvm_timer_flush_hwstate(vcpu);
 
+               preempt_disable();
                local_irq_disable();
 
                /*
@@ -544,6 +544,7 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run)
 
                if (ret <= 0 || need_new_vmid_gen(vcpu->kvm)) {
                        local_irq_enable();
+                       preempt_enable();
                        kvm_timer_sync_hwstate(vcpu);
                        kvm_vgic_sync_hwstate(vcpu);
                        continue;
@@ -553,14 +554,16 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run)
                 * Enter the guest
                 */
                trace_kvm_entry(*vcpu_pc(vcpu));
-               kvm_guest_enter();
+               __kvm_guest_enter();
                vcpu->mode = IN_GUEST_MODE;
 
                ret = kvm_call_hyp(__kvm_vcpu_run, vcpu);
 
                vcpu->mode = OUTSIDE_GUEST_MODE;
-               kvm_guest_exit();
-               trace_kvm_exit(kvm_vcpu_trap_get_class(vcpu), *vcpu_pc(vcpu));
+               /*
+                * Back from guest
+                *************************************************************/
+
                /*
                 * We may have taken a host interrupt in HYP mode (ie
                 * while executing the guest). This interrupt is still
@@ -574,8 +577,17 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run)
                local_irq_enable();
 
                /*
-                * Back from guest
-                *************************************************************/
+                * We do local_irq_enable() before calling kvm_guest_exit() so
+                * that if a timer interrupt hits while running the guest we
+                * account that tick as being spent in the guest.  We enable
+                * preemption after calling kvm_guest_exit() so that if we get
+                * preempted we make sure ticks after that is not counted as
+                * guest time.
+                */
+               kvm_guest_exit();
+               trace_kvm_exit(kvm_vcpu_trap_get_class(vcpu), *vcpu_pc(vcpu));
+               preempt_enable();
+
 
                kvm_timer_sync_hwstate(vcpu);
                kvm_vgic_sync_hwstate(vcpu);
index 79caf79b304a0ddc8999f437bae42b57809b9ff0..f7db3a5d80e3b0e6f3a6c02c9b4fd60351689fca 100644 (file)
@@ -170,13 +170,9 @@ __kvm_vcpu_return:
        @ Don't trap coprocessor accesses for host kernel
        set_hstr vmexit
        set_hdcr vmexit
-       set_hcptr vmexit, (HCPTR_TTA | HCPTR_TCP(10) | HCPTR_TCP(11))
+       set_hcptr vmexit, (HCPTR_TTA | HCPTR_TCP(10) | HCPTR_TCP(11)), after_vfp_restore
 
 #ifdef CONFIG_VFPv3
-       @ Save floating point registers we if let guest use them.
-       tst     r2, #(HCPTR_TCP(10) | HCPTR_TCP(11))
-       bne     after_vfp_restore
-
        @ Switch VFP/NEON hardware state to the host's
        add     r7, vcpu, #VCPU_VFP_GUEST
        store_vfp_state r7
@@ -188,6 +184,8 @@ after_vfp_restore:
        @ Restore FPEXC_EN which we clobbered on entry
        pop     {r2}
        VFPFMXR FPEXC, r2
+#else
+after_vfp_restore:
 #endif
 
        @ Reset Hyp-role
@@ -483,7 +481,7 @@ switch_to_guest_vfp:
        push    {r3-r7}
 
        @ NEON/VFP used.  Turn on VFP access.
-       set_hcptr vmexit, (HCPTR_TCP(10) | HCPTR_TCP(11))
+       set_hcptr vmtrap, (HCPTR_TCP(10) | HCPTR_TCP(11))
 
        @ Switch VFP/NEON hardware state to the guest's
        add     r7, r0, #VCPU_VFP_HOST
index 35e4a3a0c476cd9730afa52192bea5b3ff6f20fd..702740d37465c31299b24ccd4b9fed7184267689 100644 (file)
@@ -412,7 +412,6 @@ vcpu        .req    r0              @ vcpu pointer always in r0
        add     r11, vcpu, #VCPU_VGIC_CPU
 
        /* Save all interesting registers */
-       ldr     r3, [r2, #GICH_HCR]
        ldr     r4, [r2, #GICH_VMCR]
        ldr     r5, [r2, #GICH_MISR]
        ldr     r6, [r2, #GICH_EISR0]
@@ -420,7 +419,6 @@ vcpu        .req    r0              @ vcpu pointer always in r0
        ldr     r8, [r2, #GICH_ELRSR0]
        ldr     r9, [r2, #GICH_ELRSR1]
        ldr     r10, [r2, #GICH_APR]
-ARM_BE8(rev    r3, r3  )
 ARM_BE8(rev    r4, r4  )
 ARM_BE8(rev    r5, r5  )
 ARM_BE8(rev    r6, r6  )
@@ -429,7 +427,6 @@ ARM_BE8(rev r8, r8  )
 ARM_BE8(rev    r9, r9  )
 ARM_BE8(rev    r10, r10        )
 
-       str     r3, [r11, #VGIC_V2_CPU_HCR]
        str     r4, [r11, #VGIC_V2_CPU_VMCR]
        str     r5, [r11, #VGIC_V2_CPU_MISR]
 #ifdef CONFIG_CPU_ENDIAN_BE8
@@ -591,8 +588,13 @@ ARM_BE8(rev        r6, r6  )
 .endm
 
 /* Configures the HCPTR (Hyp Coprocessor Trap Register) on entry/return
- * (hardware reset value is 0). Keep previous value in r2. */
-.macro set_hcptr operation, mask
+ * (hardware reset value is 0). Keep previous value in r2.
+ * An ISB is emited on vmexit/vmtrap, but executed on vmexit only if
+ * VFP wasn't already enabled (always executed on vmtrap).
+ * If a label is specified with vmexit, it is branched to if VFP wasn't
+ * enabled.
+ */
+.macro set_hcptr operation, mask, label = none
        mrc     p15, 4, r2, c1, c1, 2
        ldr     r3, =\mask
        .if \operation == vmentry
@@ -601,6 +603,17 @@ ARM_BE8(rev        r6, r6  )
        bic     r3, r2, r3              @ Don't trap defined coproc-accesses
        .endif
        mcr     p15, 4, r3, c1, c1, 2
+       .if \operation != vmentry
+       .if \operation == vmexit
+       tst     r2, #(HCPTR_TCP(10) | HCPTR_TCP(11))
+       beq     1f
+       .endif
+       isb
+       .if \label != none
+       b       \label
+       .endif
+1:
+       .endif
 .endm
 
 /* Configures the HDCR (Hyp Debug Configuration Register) on entry/return
index 1d5accbd3dcf2ae771e7ba9475c8607846a05a0d..7b42012941872155dfc9dc0678cf7a653e9f3c64 100644 (file)
@@ -691,8 +691,8 @@ int kvm_alloc_stage2_pgd(struct kvm *kvm)
                 * work.  This is not used by the hardware and we have no
                 * alignment requirement for this allocation.
                 */
-               pgd = (pgd_t *)kmalloc(PTRS_PER_S2_PGD * sizeof(pgd_t),
-                                      GFP_KERNEL | __GFP_ZERO);
+               pgd = kmalloc(PTRS_PER_S2_PGD * sizeof(pgd_t),
+                               GFP_KERNEL | __GFP_ZERO);
 
                if (!pgd) {
                        kvm_free_hwpgd(hwpgd);
@@ -1155,7 +1155,8 @@ static void stage2_wp_range(struct kvm *kvm, phys_addr_t addr, phys_addr_t end)
  */
 void kvm_mmu_wp_memory_region(struct kvm *kvm, int slot)
 {
-       struct kvm_memory_slot *memslot = id_to_memslot(kvm->memslots, slot);
+       struct kvm_memslots *slots = kvm_memslots(kvm);
+       struct kvm_memory_slot *memslot = id_to_memslot(slots, slot);
        phys_addr_t start = memslot->base_gfn << PAGE_SHIFT;
        phys_addr_t end = (memslot->base_gfn + memslot->npages) << PAGE_SHIFT;
 
@@ -1718,8 +1719,9 @@ out:
 }
 
 void kvm_arch_commit_memory_region(struct kvm *kvm,
-                                  struct kvm_userspace_memory_region *mem,
+                                  const struct kvm_userspace_memory_region *mem,
                                   const struct kvm_memory_slot *old,
+                                  const struct kvm_memory_slot *new,
                                   enum kvm_mr_change change)
 {
        /*
@@ -1733,7 +1735,7 @@ void kvm_arch_commit_memory_region(struct kvm *kvm,
 
 int kvm_arch_prepare_memory_region(struct kvm *kvm,
                                   struct kvm_memory_slot *memslot,
-                                  struct kvm_userspace_memory_region *mem,
+                                  const struct kvm_userspace_memory_region *mem,
                                   enum kvm_mr_change change)
 {
        hva_t hva = mem->userspace_addr;
@@ -1838,7 +1840,7 @@ int kvm_arch_create_memslot(struct kvm *kvm, struct kvm_memory_slot *slot,
        return 0;
 }
 
-void kvm_arch_memslots_updated(struct kvm *kvm)
+void kvm_arch_memslots_updated(struct kvm *kvm, struct kvm_memslots *slots)
 {
 }
 
index 02fa8eff6ae1d54a93194d9e8d685017fe964d2a..4b94b513168da4ae52f517e7bb4937652308b427 100644 (file)
@@ -24,6 +24,8 @@
 #include <asm/kvm_psci.h>
 #include <asm/kvm_host.h>
 
+#include <uapi/linux/psci.h>
+
 /*
  * This is an implementation of the Power State Coordination Interface
  * as described in ARM document number ARM DEN 0022A.
@@ -230,10 +232,6 @@ static int kvm_psci_0_2_call(struct kvm_vcpu *vcpu)
        case PSCI_0_2_FN64_AFFINITY_INFO:
                val = kvm_psci_vcpu_affinity_info(vcpu);
                break;
-       case PSCI_0_2_FN_MIGRATE:
-       case PSCI_0_2_FN64_MIGRATE:
-               val = PSCI_RET_NOT_SUPPORTED;
-               break;
        case PSCI_0_2_FN_MIGRATE_INFO_TYPE:
                /*
                 * Trusted OS is MP hence does not require migration
@@ -242,10 +240,6 @@ static int kvm_psci_0_2_call(struct kvm_vcpu *vcpu)
                 */
                val = PSCI_0_2_TOS_MP;
                break;
-       case PSCI_0_2_FN_MIGRATE_INFO_UP_CPU:
-       case PSCI_0_2_FN64_MIGRATE_INFO_UP_CPU:
-               val = PSCI_RET_NOT_SUPPORTED;
-               break;
        case PSCI_0_2_FN_SYSTEM_OFF:
                kvm_psci_system_off(vcpu);
                /*
@@ -271,7 +265,8 @@ static int kvm_psci_0_2_call(struct kvm_vcpu *vcpu)
                ret = 0;
                break;
        default:
-               return -EINVAL;
+               val = PSCI_RET_NOT_SUPPORTED;
+               break;
        }
 
        *vcpu_reg(vcpu, 0) = val;
@@ -291,12 +286,9 @@ static int kvm_psci_0_1_call(struct kvm_vcpu *vcpu)
        case KVM_PSCI_FN_CPU_ON:
                val = kvm_psci_vcpu_on(vcpu);
                break;
-       case KVM_PSCI_FN_CPU_SUSPEND:
-       case KVM_PSCI_FN_MIGRATE:
+       default:
                val = PSCI_RET_NOT_SUPPORTED;
                break;
-       default:
-               return -EINVAL;
        }
 
        *vcpu_reg(vcpu, 0) = val;
index 947567ff67f922dbf33c2c8db014758ced45b2f0..af2267f6a52941eb91e46f1613891d4155427b5a 100644 (file)
@@ -167,7 +167,7 @@ Boston, MA 02111-1307, USA.  */
 
 #endif
 
-       @ Perform all needed substractions to keep only the reminder.
+       @ Perform all needed subtractions to keep only the reminder.
        @ Do comparisons in batch of 4 first.
        subs    \order, \order, #3              @ yes, 3 is intended here
        blt     2f
@@ -189,7 +189,7 @@ Boston, MA 02111-1307, USA.  */
        teqne   \dividend, #0
        beq     5f
 
-       @ Either 1, 2 or 3 comparison/substractions are left.
+       @ Either 1, 2 or 3 comparison/subtractions are left.
 2:     cmn     \order, #2
        blt     4f
        beq     3f
index 45ce065e7170f802483ebef099621f20e12772e7..3b8740c083c4818c40ad6d721b029158b24e34b2 100644 (file)
@@ -11,6 +11,7 @@
  * is licensed "as is" without any warranty of any kind, whether express
  * or implied.
  */
+#include <linux/clkdev.h>
 #include <linux/gpio.h>
 #include <linux/init.h>
 #include <linux/clk.h>
index 641edc31393850dcb134f6c3aba2dd9924787aff..78eac2c0c146bb91a7b51965e2685e3106f67eef 100644 (file)
 #include <linux/pm_clock.h>
 #include <linux/platform_device.h>
 
-#ifdef CONFIG_PM
-static int davinci_pm_runtime_suspend(struct device *dev)
-{
-       int ret;
-
-       dev_dbg(dev, "%s\n", __func__);
-
-       ret = pm_generic_runtime_suspend(dev);
-       if (ret)
-               return ret;
-
-       ret = pm_clk_suspend(dev);
-       if (ret) {
-               pm_generic_runtime_resume(dev);
-               return ret;
-       }
-
-       return 0;
-}
-
-static int davinci_pm_runtime_resume(struct device *dev)
-{
-       dev_dbg(dev, "%s\n", __func__);
-
-       pm_clk_resume(dev);
-       return pm_generic_runtime_resume(dev);
-}
-#endif
-
 static struct dev_pm_domain davinci_pm_domain = {
        .ops = {
-               SET_RUNTIME_PM_OPS(davinci_pm_runtime_suspend,
-                                  davinci_pm_runtime_resume, NULL)
+               USE_PM_CLK_RUNTIME_OPS
                USE_PLATFORM_PM_SLEEP_OPS
        },
 };
index c0b6dccbf7bd5d8d14c05172d11d6ee690bf8fef..7d23ce04cad5201919a58aefccafc88e882dd844 100644 (file)
@@ -87,8 +87,8 @@ static unsigned int exynos_pmu_spare3;
 static u32 exynos_irqwake_intmask = 0xffffffff;
 
 static const struct exynos_wkup_irq exynos3250_wkup_irq[] = {
-       { 105, BIT(1) }, /* RTC alarm */
-       { 106, BIT(2) }, /* RTC tick */
+       { 73, BIT(1) }, /* RTC alarm */
+       { 74, BIT(2) }, /* RTC tick */
        { /* sentinel */ },
 };
 
index f8cb5710d6eed09e6213a31561443081d53dbcaf..3292f2e6ed6f3fc9fd306904eaeb4551e1cd0c2e 100644 (file)
@@ -223,8 +223,8 @@ void __init gemini_gpio_init(void)
                        set_irq_flags(j, IRQF_VALID);
                }
 
-               irq_set_chained_handler(IRQ_GPIO(i), gpio_irq_handler);
-               irq_set_handler_data(IRQ_GPIO(i), (void *)i);
+               irq_set_chained_handler_and_data(IRQ_GPIO(i), gpio_irq_handler,
+                                                (void *)i);
        }
 
        BUG_ON(gpiochip_add(&gemini_gpio_chip));
index 41bebfd296dcbacac7a9d0c60747d5b1c01c73d4..edea697e8253d601ce2323a0a99c1083d1e9ff93 100644 (file)
 #include <linux/clk-provider.h>
 #include <linux/of.h>
 
-#ifdef CONFIG_PM
-static int keystone_pm_runtime_suspend(struct device *dev)
-{
-       int ret;
-
-       dev_dbg(dev, "%s\n", __func__);
-
-       ret = pm_generic_runtime_suspend(dev);
-       if (ret)
-               return ret;
-
-       ret = pm_clk_suspend(dev);
-       if (ret) {
-               pm_generic_runtime_resume(dev);
-               return ret;
-       }
-
-       return 0;
-}
-
-static int keystone_pm_runtime_resume(struct device *dev)
-{
-       dev_dbg(dev, "%s\n", __func__);
-
-       pm_clk_resume(dev);
-
-       return pm_generic_runtime_resume(dev);
-}
-#endif
-
 static struct dev_pm_domain keystone_pm_domain = {
        .ops = {
-               SET_RUNTIME_PM_OPS(keystone_pm_runtime_suspend,
-                                  keystone_pm_runtime_resume, NULL)
+               USE_PM_CLK_RUNTIME_OPS
                USE_PLATFORM_PM_SLEEP_OPS
        },
 };
index dd5d6f532e8ccec2fcfc5c1fcd0e0276aa4cf0bf..661c8f4b23102368e91d7420f50b36ccdaeab2a2 100644 (file)
@@ -1238,10 +1238,7 @@ static struct clk_lookup lookups[] = {
 
 static int __init clk_init(void)
 {
-       int i;
-
-       for (i = 0; i < ARRAY_SIZE(lookups); i++)
-               clkdev_add(&lookups[i]);
+       clkdev_add_table(lookups, ARRAY_SIZE(lookups));
 
        /*
         * Setup muxed SYSCLK for HCLK PLL base -this selects the
index 85089d821982193b2e2d79f5f5fbf050eefad464..3bc59390a943d97e33c92743c189ca6ffac43c0d 100644 (file)
@@ -7,6 +7,7 @@
  * it under the terms of the GNU General Public License version 2 as
  * published by the Free Software Foundation.
  */
+#include <linux/clkdev.h>
 #include <linux/irq.h>
 #include <linux/gpio.h>
 #include <linux/kernel.h>
@@ -14,7 +15,6 @@
 #include <linux/mutex.h>
 #include <linux/platform_device.h>
 #include <linux/input.h>
-#include <linux/clk.h>
 #include <linux/omapfb.h>
 
 #include <linux/spi/spi.h>
index c40e209de65c0fe6816a4dc31bc737335d8b71ca..667c1637ff9198b5aa539c10b6ae83352ad3afda 100644 (file)
 
 #include "soc.h"
 
-#ifdef CONFIG_PM
-static int omap1_pm_runtime_suspend(struct device *dev)
-{
-       int ret;
-
-       dev_dbg(dev, "%s\n", __func__);
-
-       ret = pm_generic_runtime_suspend(dev);
-       if (ret)
-               return ret;
-
-       ret = pm_clk_suspend(dev);
-       if (ret) {
-               pm_generic_runtime_resume(dev);
-               return ret;
-       }
-
-       return 0;
-}
-
-static int omap1_pm_runtime_resume(struct device *dev)
-{
-       dev_dbg(dev, "%s\n", __func__);
-
-       pm_clk_resume(dev);
-       return pm_generic_runtime_resume(dev);
-}
-
 static struct dev_pm_domain default_pm_domain = {
        .ops = {
-               .runtime_suspend = omap1_pm_runtime_suspend,
-               .runtime_resume = omap1_pm_runtime_resume,
+               USE_PM_CLK_RUNTIME_OPS
                USE_PLATFORM_PM_SLEEP_OPS
        },
 };
-#define OMAP1_PM_DOMAIN (&default_pm_domain)
-#else
-#define OMAP1_PM_DOMAIN NULL
-#endif /* CONFIG_PM */
 
 static struct pm_clk_notifier_block platform_bus_notifier = {
-       .pm_domain = OMAP1_PM_DOMAIN,
+       .pm_domain = &default_pm_domain,
        .con_ids = { "ick", "fck", NULL, },
 };
 
index 85e0b0c06718f0e8f1127192bfd65d5a2d4210ce..b64d717bfab6c4c3c4c11a7993b525f17789ea20 100644 (file)
@@ -232,14 +232,12 @@ void omap2xxx_clkt_vps_init(void)
        struct clk_hw_omap *hw = NULL;
        struct clk *clk;
        const char *parent_name = "mpu_ck";
-       struct clk_lookup *lookup = NULL;
 
        omap2xxx_clkt_vps_late_init();
        omap2xxx_clkt_vps_check_bootloader_rates();
 
        hw = kzalloc(sizeof(*hw), GFP_KERNEL);
-       lookup = kzalloc(sizeof(*lookup), GFP_KERNEL);
-       if (!hw || !lookup)
+       if (!hw)
                goto cleanup;
        init.name = "virt_prcm_set";
        init.ops = &virt_prcm_set_ops;
@@ -249,15 +247,9 @@ void omap2xxx_clkt_vps_init(void)
        hw->hw.init = &init;
 
        clk = clk_register(NULL, &hw->hw);
-
-       lookup->dev_id = NULL;
-       lookup->con_id = "cpufreq_ck";
-       lookup->clk = clk;
-
-       clkdev_add(lookup);
+       clkdev_create(clk, "cpufreq_ck", NULL);
        return;
 cleanup:
        kfree(hw);
-       kfree(lookup);
 }
 #endif
index f492ae147c6ac6752aaeb74b921c2861a1d8cd2a..6ab13d18c6363532d8928dee3039ed7db065461c 100644 (file)
@@ -287,6 +287,8 @@ static enum omapdss_version __init omap_display_get_version(void)
                return OMAPDSS_VER_OMAP5;
        else if (soc_is_am43xx())
                return OMAPDSS_VER_AM43xx;
+       else if (soc_is_dra7xx())
+               return OMAPDSS_VER_DRA7xx;
        else
                return OMAPDSS_VER_UNKNOWN;
 }
@@ -568,25 +570,25 @@ void __init omapdss_early_init_of(void)
 
 }
 
+static const char * const omapdss_compat_names[] __initconst = {
+       "ti,omap2-dss",
+       "ti,omap3-dss",
+       "ti,omap4-dss",
+       "ti,omap5-dss",
+       "ti,dra7-dss",
+};
+
 struct device_node * __init omapdss_find_dss_of_node(void)
 {
        struct device_node *node;
+       int i;
 
-       node = of_find_compatible_node(NULL, NULL, "ti,omap2-dss");
-       if (node)
-               return node;
-
-       node = of_find_compatible_node(NULL, NULL, "ti,omap3-dss");
-       if (node)
-               return node;
-
-       node = of_find_compatible_node(NULL, NULL, "ti,omap4-dss");
-       if (node)
-               return node;
-
-       node = of_find_compatible_node(NULL, NULL, "ti,omap5-dss");
-       if (node)
-               return node;
+       for (i = 0; i < ARRAY_SIZE(omapdss_compat_names); ++i) {
+               node = of_find_compatible_node(NULL, NULL,
+                       omapdss_compat_names[i]);
+               if (node)
+                       return node;
+       }
 
        return NULL;
 }
index 166b18f515a206ab3747d4eb51d4d1677c3879a2..e417f7fcb2bac3b7cc630529d49c01c4669b53f6 100644 (file)
@@ -47,7 +47,7 @@ static void _add_clkdev(struct omap_device *od, const char *clk_alias,
                       const char *clk_name)
 {
        struct clk *r;
-       struct clk_lookup *l;
+       int rc;
 
        if (!clk_alias || !clk_name)
                return;
@@ -62,21 +62,15 @@ static void _add_clkdev(struct omap_device *od, const char *clk_alias,
                return;
        }
 
-       r = clk_get(NULL, clk_name);
-       if (IS_ERR(r)) {
-               dev_err(&od->pdev->dev,
-                       "clk_get for %s failed\n", clk_name);
-               return;
+       rc = clk_add_alias(clk_alias, dev_name(&od->pdev->dev), clk_name, NULL);
+       if (rc) {
+               if (rc == -ENODEV || rc == -ENOMEM)
+                       dev_err(&od->pdev->dev,
+                               "clkdev_alloc for %s failed\n", clk_alias);
+               else
+                       dev_err(&od->pdev->dev,
+                               "clk_get for %s failed\n", clk_name);
        }
-
-       l = clkdev_alloc(r, clk_alias, dev_name(&od->pdev->dev));
-       if (!l) {
-               dev_err(&od->pdev->dev,
-                       "clkdev_alloc for %s failed\n", clk_alias);
-               return;
-       }
-
-       clkdev_add(l);
 }
 
 /**
@@ -688,11 +682,8 @@ struct dev_pm_domain omap_device_pm_domain = {
                SET_RUNTIME_PM_OPS(_od_runtime_suspend, _od_runtime_resume,
                                   NULL)
                USE_PLATFORM_PM_SLEEP_OPS
-               .suspend_noirq = _od_suspend_noirq,
-               .resume_noirq = _od_resume_noirq,
-               .freeze_noirq = _od_suspend_noirq,
-               .thaw_noirq = _od_resume_noirq,
-               .restore_noirq = _od_resume_noirq,
+               SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(_od_suspend_noirq,
+                                             _od_resume_noirq)
        }
 };
 
index 0e64c2fac0b5fad2c92af76a2042a1080108fc04..9961f95f52aed8df2b8d461537149aebfde1ba76 100644 (file)
  * IP blocks
  */
 
+/*
+ * 'dmm' class
+ * instance(s): dmm
+ */
+static struct omap_hwmod_class dra7xx_dmm_hwmod_class = {
+       .name   = "dmm",
+};
+
+/* dmm */
+static struct omap_hwmod dra7xx_dmm_hwmod = {
+       .name           = "dmm",
+       .class          = &dra7xx_dmm_hwmod_class,
+       .clkdm_name     = "emif_clkdm",
+       .prcm = {
+               .omap4 = {
+                       .clkctrl_offs = DRA7XX_CM_EMIF_DMM_CLKCTRL_OFFSET,
+                       .context_offs = DRA7XX_RM_EMIF_DMM_CONTEXT_OFFSET,
+               },
+       },
+};
+
 /*
  * 'l3' class
  * instance(s): l3_instr, l3_main_1, l3_main_2
@@ -438,6 +459,7 @@ static struct omap_hwmod_opt_clk dss_opt_clks[] = {
        { .role = "video2_clk", .clk = "dss_video2_clk" },
        { .role = "video1_clk", .clk = "dss_video1_clk" },
        { .role = "hdmi_clk", .clk = "dss_hdmi_clk" },
+       { .role = "hdcp_clk", .clk = "dss_deshdcp_clk" },
 };
 
 static struct omap_hwmod dra7xx_dss_hwmod = {
@@ -500,6 +522,7 @@ static struct omap_hwmod dra7xx_dss_dispc_hwmod = {
                },
        },
        .dev_attr       = &dss_dispc_dev_attr,
+       .parent_hwmod   = &dra7xx_dss_hwmod,
 };
 
 /*
@@ -541,6 +564,7 @@ static struct omap_hwmod dra7xx_dss_hdmi_hwmod = {
        },
        .opt_clks       = dss_hdmi_opt_clks,
        .opt_clks_cnt   = ARRAY_SIZE(dss_hdmi_opt_clks),
+       .parent_hwmod   = &dra7xx_dss_hwmod,
 };
 
 /*
@@ -2321,6 +2345,14 @@ static struct omap_hwmod dra7xx_wd_timer2_hwmod = {
  * Interfaces
  */
 
+/* l3_main_1 -> dmm */
+static struct omap_hwmod_ocp_if dra7xx_l3_main_1__dmm = {
+       .master         = &dra7xx_l3_main_1_hwmod,
+       .slave          = &dra7xx_dmm_hwmod,
+       .clk            = "l3_iclk_div",
+       .user           = OCP_USER_SDMA,
+};
+
 /* l3_main_2 -> l3_instr */
 static struct omap_hwmod_ocp_if dra7xx_l3_main_2__l3_instr = {
        .master         = &dra7xx_l3_main_2_hwmod,
@@ -3289,6 +3321,7 @@ static struct omap_hwmod_ocp_if dra7xx_l4_wkup__wd_timer2 = {
 };
 
 static struct omap_hwmod_ocp_if *dra7xx_hwmod_ocp_ifs[] __initdata = {
+       &dra7xx_l3_main_1__dmm,
        &dra7xx_l3_main_2__l3_instr,
        &dra7xx_l4_cfg__l3_main_1,
        &dra7xx_mpu__l3_main_1,
index d1dedc8195ed2569508e0d522301bdf535aefda8..eafd120b53f1bc15c82f2cc47dc8033e31ca566e 100644 (file)
@@ -203,23 +203,8 @@ save_context_wfi:
         */
        ldr     r1, kernel_flush
        blx     r1
-       /*
-        * The kernel doesn't interwork: v7_flush_dcache_all in particluar will
-        * always return in Thumb state when CONFIG_THUMB2_KERNEL is enabled.
-        * This sequence switches back to ARM.  Note that .align may insert a
-        * nop: bx pc needs to be word-aligned in order to work.
-        */
- THUMB(        .thumb          )
- THUMB(        .align          )
- THUMB(        bx      pc      )
- THUMB(        nop             )
-       .arm
-
        b       omap3_do_wfi
-
-/*
- * Local variables
- */
+ENDPROC(omap34xx_cpu_suspend)
 omap3_do_wfi_sram_addr:
        .word omap3_do_wfi_sram
 kernel_flush:
@@ -364,10 +349,7 @@ exit_nonoff_modes:
  * ===================================
  */
        ldmfd   sp!, {r4 - r11, pc}     @ restore regs and return
-
-/*
- * Local variables
- */
+ENDPROC(omap3_do_wfi)
 sdrc_power:
        .word   SDRC_POWER_V
 cm_idlest1_core:
index cfb864173ce33b13ea01a5cab8321c0fc9f4131f..4427bf26ea478a6cf400e9f1185467461cda1750 100644 (file)
@@ -10,6 +10,7 @@
  *
  */
 
+#include <linux/clkdev.h>
 #include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/gpio.h>
index 4ac9ab80d24bdf147c23a4f31ab85d76cb4a82c5..7518310c901530f840ff9f6fc8575e2cd0376926 100644 (file)
@@ -11,6 +11,7 @@
  *  it under the terms of the GNU General Public License version 2 as
  *  published by the Free Software Foundation.
  */
+#include <linux/clkdev.h>
 #include <linux/gpio.h>
 #include <linux/gpio/machine.h>
 #include <linux/module.h>
index 7780d1faa06f563057e68e749e7ccc7a9a8006d6..92e56d8a24d82171eb7e4cf433472e7aba6b081e 100644 (file)
@@ -12,6 +12,7 @@
  *
  */
 
+#include <linux/clkdev.h>
 #include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/platform_device.h>
index af868d258e664b5a50a4c678506bf7da7ac6dfd0..99d9a3b1bf34bedddb62e51a220d93088bab3542 100644 (file)
@@ -327,8 +327,7 @@ static int neponset_probe(struct platform_device *dev)
        irq_set_chip(d->irq_base + NEP_IRQ_SA1111, &nochip);
 
        irq_set_irq_type(irq, IRQ_TYPE_EDGE_RISING);
-       irq_set_handler_data(irq, d);
-       irq_set_chained_handler(irq, neponset_irq_handler);
+       irq_set_chained_handler_and_data(irq, neponset_irq_handler, d);
 
        /*
         * We would set IRQ_GPIO25 to be a wake-up IRQ, but unfortunately
index be92fa0f2f35b58cc46004f371c83ad443fe6862..8a63b4cdc0f27d0fa7cf69f0bb1ffa51b6289e39 100644 (file)
@@ -268,6 +268,7 @@ void __init arm_memblock_init(const struct machine_desc *mdesc)
        if (mdesc->reserve)
                mdesc->reserve();
 
+       early_init_fdt_reserve_self();
        early_init_fdt_scan_reserved_mem();
 
        /* reserve memory for DMA contiguous allocations */
index f5b00f41c4f6d848e023e08f436e0b3208e1d265..2235081a04eeac818f58b44923aa565026c0b746 100644 (file)
 void __init orion_clkdev_add(const char *con_id, const char *dev_id,
                             struct clk *clk)
 {
-       struct clk_lookup *cl;
-
-       cl = clkdev_alloc(clk, con_id, dev_id);
-       if (cl)
-               clkdev_add(cl);
+       clkdev_create(clk, con_id, "%s", dev_id);
 }
 
 /* Create clkdev entries for all orion platforms except kirkwood.
index 7796af4b1d6f65f6352ed40cd6dfcaf580cfd63d..802400f2a69edf4e09ef17f075c5e03357600fb7 100644 (file)
@@ -1,5 +1,6 @@
 config ARM64
        def_bool y
+       select ACPI_CCA_REQUIRED if ACPI
        select ACPI_GENERIC_GSI if ACPI
        select ACPI_REDUCED_HARDWARE_ONLY if ACPI
        select ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE
@@ -71,6 +72,7 @@ config ARM64
        select HAVE_RCU_TABLE_FREE
        select HAVE_SYSCALL_TRACEPOINTS
        select IRQ_DOMAIN
+       select IRQ_FORCED_THREADING
        select MODULES_USE_ELF_RELA
        select NO_BOOTMEM
        select OF
index c8d3e0e866787bcbe7ac1d72b370d20927a15a5d..d8f3a1c65ecda782dec2e6023bfac8044810b38e 100644 (file)
                        };
                };
 
+               msi: msi@79000000 {
+                       compatible = "apm,xgene1-msi";
+                       msi-controller;
+                       reg = <0x00 0x79000000 0x0 0x900000>;
+                       interrupts = <  0x0 0x10 0x4
+                                       0x0 0x11 0x4
+                                       0x0 0x12 0x4
+                                       0x0 0x13 0x4
+                                       0x0 0x14 0x4
+                                       0x0 0x15 0x4
+                                       0x0 0x16 0x4
+                                       0x0 0x17 0x4
+                                       0x0 0x18 0x4
+                                       0x0 0x19 0x4
+                                       0x0 0x1a 0x4
+                                       0x0 0x1b 0x4
+                                       0x0 0x1c 0x4
+                                       0x0 0x1d 0x4
+                                       0x0 0x1e 0x4
+                                       0x0 0x1f 0x4>;
+               };
+
                pcie0: pcie@1f2b0000 {
                        status = "disabled";
                        device_type = "pci";
                                         0x0 0x0 0x0 0x4 &gic 0x0 0xc5 0x1>;
                        dma-coherent;
                        clocks = <&pcie0clk 0>;
+                       msi-parent = <&msi>;
                };
 
                pcie1: pcie@1f2c0000 {
                                         0x0 0x0 0x0 0x4 &gic 0x0 0xcb 0x1>;
                        dma-coherent;
                        clocks = <&pcie1clk 0>;
+                       msi-parent = <&msi>;
                };
 
                pcie2: pcie@1f2d0000 {
                                         0x0 0x0 0x0 0x4 &gic 0x0 0xd1 0x1>;
                        dma-coherent;
                        clocks = <&pcie2clk 0>;
+                       msi-parent = <&msi>;
                };
 
                pcie3: pcie@1f500000 {
                                         0x0 0x0 0x0 0x4 &gic 0x0 0xd7 0x1>;
                        dma-coherent;
                        clocks = <&pcie3clk 0>;
+                       msi-parent = <&msi>;
                };
 
                pcie4: pcie@1f510000 {
                                         0x0 0x0 0x0 0x4 &gic 0x0 0xdd 0x1>;
                        dma-coherent;
                        clocks = <&pcie4clk 0>;
+                       msi-parent = <&msi>;
                };
 
                serial0: serial@1c020000 {
index 43d54017b779d4e211462b8bebe2604025bb08ea..d0ab012fa379eb97c6e43ebad83ee18185d2b598 100644 (file)
@@ -16,7 +16,8 @@
 #include "mt8173.dtsi"
 
 / {
-       model = "mediatek,mt8173-evb";
+       model = "MediaTek MT8173 evaluation board";
+       compatible = "mediatek,mt8173-evb", "mediatek,mt8173";
 
        aliases {
                serial0 = &uart0;
index 2ed7449d9273c01b314d59c7a81b12472b4f7948..daefbf0329a62b939a44f982a57951e4e0497e46 100644 (file)
@@ -180,6 +180,7 @@ CONFIG_LOCKUP_DETECTOR=y
 # CONFIG_SCHED_DEBUG is not set
 # CONFIG_DEBUG_PREEMPT is not set
 # CONFIG_FTRACE is not set
+CONFIG_MEMTEST=y
 CONFIG_SECURITY=y
 CONFIG_CRYPTO_ANSI_CPRNG=y
 CONFIG_ARM64_CRYPTO=y
index 6c348df5bf36d8d9c54b88b3e7be900e754357ab..3303e8a7b837c9fc033da5a94206af4bb0594ba5 100644 (file)
@@ -13,7 +13,7 @@
 #include <crypto/aes.h>
 #include <crypto/algapi.h>
 #include <crypto/scatterwalk.h>
-#include <linux/crypto.h>
+#include <crypto/internal/aead.h>
 #include <linux/module.h>
 
 #include "aes-ce-setkey.h"
index 59c05d8ea4a000fc9ae4d5cb47bff84ff9c4262f..39248d3adf5d8b2858a9e01aa92209e70f78772b 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/irqchip/arm-gic-acpi.h>
 
 #include <asm/cputype.h>
+#include <asm/psci.h>
 #include <asm/smp_plat.h>
 
 /* Basic configuration for ACPI */
@@ -39,18 +40,6 @@ extern int acpi_disabled;
 extern int acpi_noirq;
 extern int acpi_pci_disabled;
 
-/* 1 to indicate PSCI 0.2+ is implemented */
-static inline bool acpi_psci_present(void)
-{
-       return acpi_gbl_FADT.arm_boot_flags & ACPI_FADT_PSCI_COMPLIANT;
-}
-
-/* 1 to indicate HVC must be used instead of SMC as the PSCI conduit */
-static inline bool acpi_psci_use_hvc(void)
-{
-       return acpi_gbl_FADT.arm_boot_flags & ACPI_FADT_PSCI_USE_HVC;
-}
-
 static inline void disable_acpi(void)
 {
        acpi_disabled = 1;
@@ -88,9 +77,11 @@ static inline void arch_fix_phys_package_id(int num, u32 slot) { }
 void __init acpi_init_cpus(void);
 
 #else
-static inline bool acpi_psci_present(void) { return false; }
-static inline bool acpi_psci_use_hvc(void) { return false; }
 static inline void acpi_init_cpus(void) { }
 #endif /* CONFIG_ACPI */
 
+static inline const char *acpi_get_enable_method(int cpu)
+{
+       return acpi_psci_present() ? "psci" : NULL;
+}
 #endif /*_ASM_ACPI_H*/
diff --git a/arch/arm64/include/asm/alternative-asm.h b/arch/arm64/include/asm/alternative-asm.h
deleted file mode 100644 (file)
index 919a678..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-#ifndef __ASM_ALTERNATIVE_ASM_H
-#define __ASM_ALTERNATIVE_ASM_H
-
-#ifdef __ASSEMBLY__
-
-.macro altinstruction_entry orig_offset alt_offset feature orig_len alt_len
-       .word \orig_offset - .
-       .word \alt_offset - .
-       .hword \feature
-       .byte \orig_len
-       .byte \alt_len
-.endm
-
-.macro alternative_insn insn1 insn2 cap
-661:   \insn1
-662:   .pushsection .altinstructions, "a"
-       altinstruction_entry 661b, 663f, \cap, 662b-661b, 664f-663f
-       .popsection
-       .pushsection .altinstr_replacement, "ax"
-663:   \insn2
-664:   .popsection
-       .if ((664b-663b) != (662b-661b))
-               .error "Alternatives instruction length mismatch"
-       .endif
-.endm
-
-#endif  /*  __ASSEMBLY__  */
-
-#endif /* __ASM_ALTERNATIVE_ASM_H */
index d261f01e2baeee70c53f3ac7a6f44b7b6e21a98f..c385a0c4057f03875d6b6326009fb68e32fdfb67 100644 (file)
@@ -1,6 +1,8 @@
 #ifndef __ASM_ALTERNATIVE_H
 #define __ASM_ALTERNATIVE_H
 
+#ifndef __ASSEMBLY__
+
 #include <linux/types.h>
 #include <linux/stddef.h>
 #include <linux/stringify.h>
@@ -24,7 +26,20 @@ void free_alternatives_memory(void);
        " .byte 662b-661b\n"                            /* source len      */ \
        " .byte 664f-663f\n"                            /* replacement len */
 
-/* alternative assembly primitive: */
+/*
+ * alternative assembly primitive:
+ *
+ * If any of these .org directive fail, it means that insn1 and insn2
+ * don't have the same length. This used to be written as
+ *
+ * .if ((664b-663b) != (662b-661b))
+ *     .error "Alternatives instruction length mismatch"
+ * .endif
+ *
+ * but most assemblers die if insn1 or insn2 have a .inst. This should
+ * be fixed in a binutils release posterior to 2.25.51.0.2 (anything
+ * containing commit 4e4d08cf7399b606 or c1baaddf8861).
+ */
 #define ALTERNATIVE(oldinstr, newinstr, feature)                       \
        "661:\n\t"                                                      \
        oldinstr "\n"                                                   \
@@ -37,8 +52,31 @@ void free_alternatives_memory(void);
        newinstr "\n"                                                   \
        "664:\n\t"                                                      \
        ".popsection\n\t"                                               \
-       ".if ((664b-663b) != (662b-661b))\n\t"                          \
-       "       .error \"Alternatives instruction length mismatch\"\n\t"\
-       ".endif\n"
+       ".org   . - (664b-663b) + (662b-661b)\n\t"                      \
+       ".org   . - (662b-661b) + (664b-663b)\n"
+
+#else
+
+.macro altinstruction_entry orig_offset alt_offset feature orig_len alt_len
+       .word \orig_offset - .
+       .word \alt_offset - .
+       .hword \feature
+       .byte \orig_len
+       .byte \alt_len
+.endm
+
+.macro alternative_insn insn1 insn2 cap
+661:   \insn1
+662:   .pushsection .altinstructions, "a"
+       altinstruction_entry 661b, 663f, \cap, 662b-661b, 664f-663f
+       .popsection
+       .pushsection .altinstr_replacement, "ax"
+663:   \insn2
+664:   .popsection
+       .org    . - (664b-663b) + (662b-661b)
+       .org    . - (662b-661b) + (664b-663b)
+.endm
+
+#endif  /*  __ASSEMBLY__  */
 
 #endif /* __ASM_ALTERNATIVE_H */
index 71f19c4dc0dee8c8a74bbdea252f73432e0b1aa5..0fa47c4275cb561af6292761ee9645c9dbef0e59 100644 (file)
@@ -114,7 +114,7 @@ do {                                                                        \
 #define read_barrier_depends()         do { } while(0)
 #define smp_read_barrier_depends()     do { } while(0)
 
-#define set_mb(var, value)     do { var = value; smp_mb(); } while (0)
+#define smp_store_mb(var, value)       do { WRITE_ONCE(var, value); smp_mb(); } while (0)
 #define nop()          asm volatile("nop");
 
 #define smp_mb__before_atomic()        smp_mb()
diff --git a/arch/arm64/include/asm/boot.h b/arch/arm64/include/asm/boot.h
new file mode 100644 (file)
index 0000000..81151b6
--- /dev/null
@@ -0,0 +1,14 @@
+
+#ifndef __ASM_BOOT_H
+#define __ASM_BOOT_H
+
+#include <asm/sizes.h>
+
+/*
+ * arm64 requires the DTB to be 8 byte aligned and
+ * not exceed 2MB in size.
+ */
+#define MIN_FDT_ALIGN          8
+#define MAX_FDT_SIZE           SZ_2M
+
+#endif
index 67d309cc3b6b806ea19620142171771e55e75e93..c75b8d027eb1e657efaa2ec219e14af27c9fcfa9 100644 (file)
  *     the implementation assumes non-aliasing VIPT D-cache and (aliasing)
  *     VIPT or ASID-tagged VIVT I-cache.
  *
- *     flush_cache_all()
- *
- *             Unconditionally clean and invalidate the entire cache.
- *
  *     flush_cache_mm(mm)
  *
  *             Clean and invalidate all user space cache entries
@@ -69,7 +65,6 @@
  *             - kaddr  - page address
  *             - size   - region size
  */
-extern void flush_cache_all(void);
 extern void flush_cache_range(struct vm_area_struct *vma, unsigned long start, unsigned long end);
 extern void flush_icache_range(unsigned long start, unsigned long end);
 extern void __flush_dcache_area(void *addr, size_t len);
index 5a31d671691439b1a329f49d878bbc479f4b253c..8f03446cf89f111e3cd9c573514797c8420abd3e 100644 (file)
 #include <linux/init.h>
 #include <linux/threads.h>
 
-struct device_node;
-
 /**
  * struct cpu_operations - Callback operations for hotplugging CPUs.
  *
  * @name:      Name of the property as appears in a devicetree cpu node's
- *             enable-method property.
- * @cpu_init:  Reads any data necessary for a specific enable-method from the
- *             devicetree, for a given cpu node and proposed logical id.
+ *             enable-method property. On systems booting with ACPI, @name
+ *             identifies the struct cpu_operations entry corresponding to
+ *             the boot protocol specified in the ACPI MADT table.
+ * @cpu_init:  Reads any data necessary for a specific enable-method for a
+ *             proposed logical id.
  * @cpu_prepare: Early one-time preparation step for a cpu. If there is a
  *             mechanism for doing so, tests whether it is possible to boot
  *             the given CPU.
@@ -40,15 +40,15 @@ struct device_node;
  * @cpu_die:   Makes a cpu leave the kernel. Must not fail. Called from the
  *             cpu being killed.
  * @cpu_kill:  Ensures a cpu has left the kernel. Called from another cpu.
- * @cpu_init_idle: Reads any data necessary to initialize CPU idle states from
- *             devicetree, for a given cpu node and proposed logical id.
+ * @cpu_init_idle: Reads any data necessary to initialize CPU idle states for
+ *                a proposed logical id.
  * @cpu_suspend: Suspends a cpu and saves the required context. May fail owing
  *               to wrong parameters or error conditions. Called from the
  *               CPU being suspended. Must be called with IRQs disabled.
  */
 struct cpu_operations {
        const char      *name;
-       int             (*cpu_init)(struct device_node *, unsigned int);
+       int             (*cpu_init)(unsigned int);
        int             (*cpu_prepare)(unsigned int);
        int             (*cpu_boot)(unsigned int);
        void            (*cpu_postboot)(void);
@@ -58,14 +58,17 @@ struct cpu_operations {
        int             (*cpu_kill)(unsigned int cpu);
 #endif
 #ifdef CONFIG_CPU_IDLE
-       int             (*cpu_init_idle)(struct device_node *, unsigned int);
+       int             (*cpu_init_idle)(unsigned int);
        int             (*cpu_suspend)(unsigned long);
 #endif
 };
 
 extern const struct cpu_operations *cpu_ops[NR_CPUS];
-int __init cpu_read_ops(struct device_node *dn, int cpu);
-void __init cpu_read_bootcpu_ops(void);
-const struct cpu_operations *cpu_get_ops(const char *name);
+int __init cpu_read_ops(int cpu);
+
+static inline void __init cpu_read_bootcpu_ops(void)
+{
+       cpu_read_ops(0);
+}
 
 #endif /* ifndef __ASM_CPU_OPS_H */
index 82cb9f98ba1a3632ffbc87bff8681cb8a7eb24ff..c1044218a63a1598051d3d02b58d3241ec810cc1 100644 (file)
@@ -24,8 +24,9 @@
 #define ARM64_WORKAROUND_CLEAN_CACHE           0
 #define ARM64_WORKAROUND_DEVICE_LOAD_ACQUIRE   1
 #define ARM64_WORKAROUND_845719                        2
+#define ARM64_HAS_SYSREG_GIC_CPUIF             3
 
-#define ARM64_NCAPS                            3
+#define ARM64_NCAPS                            4
 
 #ifndef __ASSEMBLY__
 
@@ -38,6 +39,11 @@ struct arm64_cpu_capabilities {
                        u32 midr_model;
                        u32 midr_range_min, midr_range_max;
                };
+
+               struct {        /* Feature register checking */
+                       u64 register_mask;
+                       u64 register_value;
+               };
        };
 };
 
index 141b2fcabaa67e50294ba85ad5f218dfc1acac0d..0f74f05d662a76515e5938c6966c211bb5a554bb 100644 (file)
@@ -5,20 +5,16 @@
 
 #ifdef CONFIG_CPU_IDLE
 extern int arm_cpuidle_init(unsigned int cpu);
-extern int cpu_suspend(unsigned long arg);
+extern int arm_cpuidle_suspend(int index);
 #else
 static inline int arm_cpuidle_init(unsigned int cpu)
 {
        return -EOPNOTSUPP;
 }
 
-static inline int cpu_suspend(unsigned long arg)
+static inline int arm_cpuidle_suspend(int index)
 {
        return -EOPNOTSUPP;
 }
 #endif
-static inline int arm_cpuidle_suspend(int index)
-{
-       return cpu_suspend(index);
-}
 #endif
index 9437e3dc58338ea4c48b9b6afe24d62bc09a7c79..f0d6d0bfe55ceceba3339bc9044bed31a159a9cf 100644 (file)
@@ -18,6 +18,7 @@
 
 #ifdef __KERNEL__
 
+#include <linux/acpi.h>
 #include <linux/types.h>
 #include <linux/vmalloc.h>
 
 
 #define DMA_ERROR_CODE (~(dma_addr_t)0)
 extern struct dma_map_ops *dma_ops;
+extern struct dma_map_ops dummy_dma_ops;
 
 static inline struct dma_map_ops *__generic_dma_ops(struct device *dev)
 {
-       if (unlikely(!dev) || !dev->archdata.dma_ops)
+       if (unlikely(!dev))
                return dma_ops;
-       else
+       else if (dev->archdata.dma_ops)
                return dev->archdata.dma_ops;
+       else if (acpi_disabled)
+               return dma_ops;
+
+       /*
+        * When ACPI is enabled, if arch_set_dma_ops is not called,
+        * we will disable device DMA capability by setting it
+        * to dummy_dma_ops.
+        */
+       return &dummy_dma_ops;
 }
 
 static inline struct dma_map_ops *get_dma_ops(struct device *dev)
@@ -48,6 +59,9 @@ static inline struct dma_map_ops *get_dma_ops(struct device *dev)
 static inline void arch_setup_dma_ops(struct device *dev, u64 dma_base, u64 size,
                                      struct iommu_ops *iommu, bool coherent)
 {
+       if (!acpi_disabled && !dev->archdata.dma_ops)
+               dev->archdata.dma_ops = dma_ops;
+
        dev->archdata.dma_coherent = coherent;
 }
 #define arch_setup_dma_ops     arch_setup_dma_ops
index 95e6b6dcbe37ecdc191a3d37bb0c263265095f31..c0739187a920434282422ad7e6f5cb73754ae3c3 100644 (file)
@@ -17,6 +17,7 @@
 
 #ifndef __ASSEMBLY__
 #include <linux/kernel.h>
+#include <asm/boot.h>
 #include <asm/page.h>
 
 /*
  */
 enum fixed_addresses {
        FIX_HOLE,
+
+       /*
+        * Reserve a virtual window for the FDT that is 2 MB larger than the
+        * maximum supported size, and put it at the top of the fixmap region.
+        * The additional space ensures that any FDT that does not exceed
+        * MAX_FDT_SIZE can be mapped regardless of whether it crosses any
+        * 2 MB alignment boundaries.
+        *
+        * Keep this at the top so it remains 2 MB aligned.
+        */
+#define FIX_FDT_SIZE           (MAX_FDT_SIZE + SZ_2M)
+       FIX_FDT_END,
+       FIX_FDT = FIX_FDT_END + FIX_FDT_SIZE / PAGE_SIZE - 1,
+
        FIX_EARLYCON_MEM_BASE,
        FIX_TEXT_POKE0,
        __end_of_permanent_fixed_addresses,
index f81b328d9cf4034991e41d2ce3c621c1b7182c49..30e50eb54a6795aa9c504a2d22dfabd8b8db195a 100644 (file)
@@ -281,6 +281,7 @@ __AARCH64_INSN_FUNCS(ret,   0xFFFFFC1F, 0xD65F0000)
 #undef __AARCH64_INSN_FUNCS
 
 bool aarch64_insn_is_nop(u32 insn);
+bool aarch64_insn_is_branch_imm(u32 insn);
 
 int aarch64_insn_read(void *addr, u32 *insnp);
 int aarch64_insn_write(void *addr, u32 insn);
@@ -351,6 +352,8 @@ u32 aarch64_insn_gen_logical_shifted_reg(enum aarch64_insn_register dst,
                                         int shift,
                                         enum aarch64_insn_variant variant,
                                         enum aarch64_insn_logic_type type);
+s32 aarch64_get_branch_offset(u32 insn);
+u32 aarch64_set_branch_offset(u32 insn, s32 offset);
 
 bool aarch64_insn_hotpatch_safe(u32 old_insn, u32 new_insn);
 
index 540f7c0aea8250a082c718c8b97f9c1c8c151169..44be1e03ed656f2e2c5b183b7c75e1eb41800b5f 100644 (file)
@@ -117,10 +117,10 @@ static inline u64 __raw_readq(const volatile void __iomem *addr)
  * ordering rules but do not guarantee any ordering relative to Normal memory
  * accesses.
  */
-#define readb_relaxed(c)       ({ u8  __v = __raw_readb(c); __v; })
-#define readw_relaxed(c)       ({ u16 __v = le16_to_cpu((__force __le16)__raw_readw(c)); __v; })
-#define readl_relaxed(c)       ({ u32 __v = le32_to_cpu((__force __le32)__raw_readl(c)); __v; })
-#define readq_relaxed(c)       ({ u64 __v = le64_to_cpu((__force __le64)__raw_readq(c)); __v; })
+#define readb_relaxed(c)       ({ u8  __r = __raw_readb(c); __r; })
+#define readw_relaxed(c)       ({ u16 __r = le16_to_cpu((__force __le16)__raw_readw(c)); __r; })
+#define readl_relaxed(c)       ({ u32 __r = le32_to_cpu((__force __le32)__raw_readl(c)); __r; })
+#define readq_relaxed(c)       ({ u64 __r = le64_to_cpu((__force __le64)__raw_readq(c)); __r; })
 
 #define writeb_relaxed(v,c)    ((void)__raw_writeb((v),(c)))
 #define writew_relaxed(v,c)    ((void)__raw_writew((__force u16)cpu_to_le16(v),(c)))
@@ -170,6 +170,7 @@ extern void __iomem *ioremap_cache(phys_addr_t phys_addr, size_t size);
 #define ioremap(addr, size)            __ioremap((addr), (size), __pgprot(PROT_DEVICE_nGnRE))
 #define ioremap_nocache(addr, size)    __ioremap((addr), (size), __pgprot(PROT_DEVICE_nGnRE))
 #define ioremap_wc(addr, size)         __ioremap((addr), (size), __pgprot(PROT_NORMAL_NC))
+#define ioremap_wt(addr, size)         __ioremap((addr), (size), __pgprot(PROT_DEVICE_nGnRE))
 #define iounmap                                __iounmap
 
 /*
index 4f7310fa77f01acd8084b16e52f9df3fce8c4091..3c5fe685a2d69a55daf68fe40fa782a0843bb2f5 100644 (file)
@@ -27,7 +27,7 @@
 #define        MPIDR_EL1       1       /* MultiProcessor Affinity Register */
 #define        CSSELR_EL1      2       /* Cache Size Selection Register */
 #define        SCTLR_EL1       3       /* System Control Register */
-#define        ACTLR_EL1       4       /* Auxilliary Control Register */
+#define        ACTLR_EL1       4       /* Auxiliary Control Register */
 #define        CPACR_EL1       5       /* Coprocessor Access Control */
 #define        TTBR0_EL1       6       /* Translation Table Base Register 0 */
 #define        TTBR1_EL1       7       /* Translation Table Base Register 1 */
@@ -132,11 +132,6 @@ extern int __kvm_vcpu_run(struct kvm_vcpu *vcpu);
 
 extern u64 __vgic_v3_get_ich_vtr_el2(void);
 
-extern char __save_vgic_v2_state[];
-extern char __restore_vgic_v2_state[];
-extern char __save_vgic_v3_state[];
-extern char __restore_vgic_v3_state[];
-
 #endif
 
 #endif /* __ARM_KVM_ASM_H__ */
index f0f58c9beec0e87c8c7eefa7a8ba52ba518e07c3..2709db2a7eac78a49fc75c072810b9ac41cc9e0a 100644 (file)
@@ -221,29 +221,6 @@ struct vgic_sr_vectors {
        void    *restore_vgic;
 };
 
-static inline void vgic_arch_setup(const struct vgic_params *vgic)
-{
-       extern struct vgic_sr_vectors __vgic_sr_vectors;
-
-       switch(vgic->type)
-       {
-       case VGIC_V2:
-               __vgic_sr_vectors.save_vgic     = __save_vgic_v2_state;
-               __vgic_sr_vectors.restore_vgic  = __restore_vgic_v2_state;
-               break;
-
-#ifdef CONFIG_ARM_GIC_V3
-       case VGIC_V3:
-               __vgic_sr_vectors.save_vgic     = __save_vgic_v3_state;
-               __vgic_sr_vectors.restore_vgic  = __restore_vgic_v3_state;
-               break;
-#endif
-
-       default:
-               BUG();
-       }
-}
-
 static inline void kvm_arch_hardware_disable(void) {}
 static inline void kvm_arch_hardware_unsetup(void) {}
 static inline void kvm_arch_sync_events(struct kvm *kvm) {}
index 3d311761e3c27459de2457d035062baacae1903d..79fcfb048884401ef7a2082e9079324fe8396ceb 100644 (file)
@@ -34,5 +34,6 @@ extern void init_mem_pgprot(void);
 extern void create_pgd_mapping(struct mm_struct *mm, phys_addr_t phys,
                               unsigned long virt, phys_addr_t size,
                               pgprot_t prot);
+extern void *fixmap_remap_fdt(phys_addr_t dt_phys);
 
 #endif
index d26d1d53c0d706bbe14f8000558cc04bac2f2ad3..6471773db6fd8c067833ea92f647cca865e6fb4d 100644 (file)
@@ -24,4 +24,11 @@ extern unsigned long perf_misc_flags(struct pt_regs *regs);
 #define perf_misc_flags(regs)  perf_misc_flags(regs)
 #endif
 
+#define perf_arch_fetch_caller_regs(regs, __ip) { \
+       (regs)->pc = (__ip);    \
+       (regs)->regs[29] = (unsigned long) __builtin_frame_address(0); \
+       (regs)->sp = current_stack_pointer; \
+       (regs)->pstate = PSR_MODE_EL1h; \
+}
+
 #endif
index 220633b791b819b3346d36d0ff0a959c4dfbef02..14ad6e4e87d11477ca350b7dbf6ff7e6017a7cc4 100644 (file)
 struct mm_struct;
 struct cpu_suspend_ctx;
 
-extern void cpu_cache_off(void);
 extern void cpu_do_idle(void);
 extern void cpu_do_switch_mm(unsigned long pgd_phys, struct mm_struct *mm);
-extern void cpu_reset(unsigned long addr) __attribute__((noreturn));
-void cpu_soft_restart(phys_addr_t cpu_reset,
-               unsigned long addr) __attribute__((noreturn));
 extern void cpu_do_suspend(struct cpu_suspend_ctx *ptr);
 extern u64 cpu_do_resume(phys_addr_t ptr, u64 idmap_ttbr);
 
index d2c37a1df0ebaf2d610f3c5be77759c914b8d597..e4c893e54f01a98f497bda6f3118c6e262ab7a64 100644 (file)
@@ -78,13 +78,30 @@ struct cpu_context {
 
 struct thread_struct {
        struct cpu_context      cpu_context;    /* cpu context */
-       unsigned long           tp_value;
+       unsigned long           tp_value;       /* TLS register */
+#ifdef CONFIG_COMPAT
+       unsigned long           tp2_value;
+#endif
        struct fpsimd_state     fpsimd_state;
        unsigned long           fault_address;  /* fault info */
        unsigned long           fault_code;     /* ESR_EL1 value */
        struct debug_info       debug;          /* debugging */
 };
 
+#ifdef CONFIG_COMPAT
+#define task_user_tls(t)                                               \
+({                                                                     \
+       unsigned long *__tls;                                           \
+       if (is_compat_thread(task_thread_info(t)))                      \
+               __tls = &(t)->thread.tp2_value;                         \
+       else                                                            \
+               __tls = &(t)->thread.tp_value;                          \
+       __tls;                                                          \
+ })
+#else
+#define task_user_tls(t)       (&(t)->thread.tp_value)
+#endif
+
 #define INIT_THREAD  { }
 
 static inline void start_thread_common(struct pt_regs *regs, unsigned long pc)
index 2454bc59c916b20ce0c275d370d06e47b5cfaf85..49d7e1aaebdce65800b26fd184fb23c9861bd516 100644 (file)
 #ifndef __ASM_PSCI_H
 #define __ASM_PSCI_H
 
-int psci_dt_init(void);
-int psci_acpi_init(void);
+int __init psci_dt_init(void);
+
+#ifdef CONFIG_ACPI
+int __init psci_acpi_init(void);
+bool __init acpi_psci_present(void);
+bool __init acpi_psci_use_hvc(void);
+#else
+static inline int psci_acpi_init(void) { return 0; }
+static inline bool acpi_psci_present(void) { return false; }
+#endif
 
 #endif /* __ASM_PSCI_H */
index bf22650b1a782738275b63cdf8e59fc33450c4ca..db02be81b90a1cdc6f6e7504fa79980a2e16a0b6 100644 (file)
@@ -42,7 +42,7 @@ extern void handle_IPI(int ipinr, struct pt_regs *regs);
  * Discover the set of possible CPUs and determine their
  * SMP operations.
  */
-extern void of_smp_init_cpus(void);
+extern void smp_init_cpus(void);
 
 /*
  * Provide a function to raise an IPI cross call on CPUs in callmap.
index 8dcd61e32176b4500939bba9f05e63ba591e5a8b..7abf7570c00f8e03b1417f214cd06c0ae96ed378 100644 (file)
@@ -19,6 +19,8 @@
 #ifndef __ASM_SMP_PLAT_H
 #define __ASM_SMP_PLAT_H
 
+#include <linux/cpumask.h>
+
 #include <asm/types.h>
 
 struct mpidr_hash {
@@ -39,6 +41,20 @@ static inline u32 mpidr_hash_size(void)
  */
 extern u64 __cpu_logical_map[NR_CPUS];
 #define cpu_logical_map(cpu)    __cpu_logical_map[cpu]
+/*
+ * Retrieve logical cpu index corresponding to a given MPIDR.Aff*
+ *  - mpidr: MPIDR.Aff* bits to be used for the look-up
+ *
+ * Returns the cpu logical index or -EINVAL on look-up error
+ */
+static inline int get_logical_index(u64 mpidr)
+{
+       int cpu;
+       for (cpu = 0; cpu < nr_cpu_ids; cpu++)
+               if (cpu_logical_map(cpu) == mpidr)
+                       return cpu;
+       return -EINVAL;
+}
 
 void __init do_post_cpus_up_work(void);
 
index 003802f58963369e86c666ad9f5546bdee9ff100..59a5b0f1e81c3274f3c1c5c0ffb4ce4014ce35c2 100644 (file)
@@ -21,6 +21,6 @@ struct sleep_save_sp {
        phys_addr_t save_ptr_stash_phys;
 };
 
-extern int __cpu_suspend(unsigned long arg, int (*fn)(unsigned long));
+extern int cpu_suspend(unsigned long arg, int (*fn)(unsigned long));
 extern void cpu_resume(void);
 #endif
index 7a18fabbe0f6c5e6579bf4a0b4b88113d1d0adee..57f110bea6a82897520d435da21a920960fb7a2b 100644 (file)
@@ -23,6 +23,8 @@
 #include <linux/compiler.h>
 #include <linux/linkage.h>
 #include <linux/irqflags.h>
+#include <linux/signal.h>
+#include <linux/ratelimit.h>
 #include <linux/reboot.h>
 
 struct pt_regs;
@@ -41,9 +43,19 @@ struct mm_struct;
 extern void show_pte(struct mm_struct *mm, unsigned long addr);
 extern void __show_regs(struct pt_regs *);
 
-void soft_restart(unsigned long);
 extern void (*arm_pm_restart)(enum reboot_mode reboot_mode, const char *cmd);
 
+#define show_unhandled_signals_ratelimited()                           \
+({                                                                     \
+       static DEFINE_RATELIMIT_STATE(_rs,                              \
+                                     DEFAULT_RATELIMIT_INTERVAL,       \
+                                     DEFAULT_RATELIMIT_BURST);         \
+       bool __show_ratelimited = false;                                \
+       if (show_unhandled_signals && __ratelimit(&_rs))                \
+               __show_ratelimited = true;                              \
+       __show_ratelimited;                                             \
+})
+
 #define UDBG_UNDEFINED (1 << 0)
 #define UDBG_SYSCALL   (1 << 1)
 #define UDBG_BADABORT  (1 << 2)
index c3bb05b98616789a4143bb6d48333cf818653ab2..934815d45eda0f180aa3bc5d5a6d043d6832cb8d 100644 (file)
@@ -28,8 +28,6 @@
  *     TLB Management
  *     ==============
  *
- *     The arch/arm64/mm/tlb.S files implement these methods.
- *
  *     The TLB specific code is expected to perform whatever tests it needs
  *     to determine if it should invalidate the TLB for each call.  Start
  *     addresses are inclusive and end addresses are exclusive; it is safe to
index 8b839558838e5a87e391d691d70492799f122d04..19de7537e7d32f407f3f75d5f295ab5f3c7c4291 100644 (file)
@@ -36,12 +36,6 @@ EXPORT_SYMBOL(acpi_disabled);
 int acpi_pci_disabled = 1;     /* skip ACPI PCI scan and IRQ initialization */
 EXPORT_SYMBOL(acpi_pci_disabled);
 
-/* Processors with enabled flag and sane MPIDR */
-static int enabled_cpus;
-
-/* Boot CPU is valid or not in MADT */
-static bool bootcpu_valid  __initdata;
-
 static bool param_acpi_off __initdata;
 static bool param_acpi_force __initdata;
 
@@ -95,122 +89,15 @@ void __init __acpi_unmap_table(char *map, unsigned long size)
        early_memunmap(map, size);
 }
 
-/**
- * acpi_map_gic_cpu_interface - generates a logical cpu number
- * and map to MPIDR represented by GICC structure
- */
-static void __init
-acpi_map_gic_cpu_interface(struct acpi_madt_generic_interrupt *processor)
+bool __init acpi_psci_present(void)
 {
-       int i;
-       u64 mpidr = processor->arm_mpidr & MPIDR_HWID_BITMASK;
-       bool enabled = !!(processor->flags & ACPI_MADT_ENABLED);
-
-       if (mpidr == INVALID_HWID) {
-               pr_info("Skip MADT cpu entry with invalid MPIDR\n");
-               return;
-       }
-
-       total_cpus++;
-       if (!enabled)
-               return;
-
-       if (enabled_cpus >=  NR_CPUS) {
-               pr_warn("NR_CPUS limit of %d reached, Processor %d/0x%llx ignored.\n",
-                       NR_CPUS, total_cpus, mpidr);
-               return;
-       }
-
-       /* Check if GICC structure of boot CPU is available in the MADT */
-       if (cpu_logical_map(0) == mpidr) {
-               if (bootcpu_valid) {
-                       pr_err("Firmware bug, duplicate CPU MPIDR: 0x%llx in MADT\n",
-                              mpidr);
-                       return;
-               }
-
-               bootcpu_valid = true;
-       }
-
-       /*
-        * Duplicate MPIDRs are a recipe for disaster. Scan
-        * all initialized entries and check for
-        * duplicates. If any is found just ignore the CPU.
-        */
-       for (i = 1; i < enabled_cpus; i++) {
-               if (cpu_logical_map(i) == mpidr) {
-                       pr_err("Firmware bug, duplicate CPU MPIDR: 0x%llx in MADT\n",
-                              mpidr);
-                       return;
-               }
-       }
-
-       if (!acpi_psci_present())
-               return;
-
-       cpu_ops[enabled_cpus] = cpu_get_ops("psci");
-       /* CPU 0 was already initialized */
-       if (enabled_cpus) {
-               if (!cpu_ops[enabled_cpus])
-                       return;
-
-               if (cpu_ops[enabled_cpus]->cpu_init(NULL, enabled_cpus))
-                       return;
-
-               /* map the logical cpu id to cpu MPIDR */
-               cpu_logical_map(enabled_cpus) = mpidr;
-       }
-
-       enabled_cpus++;
+       return acpi_gbl_FADT.arm_boot_flags & ACPI_FADT_PSCI_COMPLIANT;
 }
 
-static int __init
-acpi_parse_gic_cpu_interface(struct acpi_subtable_header *header,
-                               const unsigned long end)
+/* Whether HVC must be used instead of SMC as the PSCI conduit */
+bool __init acpi_psci_use_hvc(void)
 {
-       struct acpi_madt_generic_interrupt *processor;
-
-       processor = (struct acpi_madt_generic_interrupt *)header;
-
-       if (BAD_MADT_ENTRY(processor, end))
-               return -EINVAL;
-
-       acpi_table_print_madt_entry(header);
-       acpi_map_gic_cpu_interface(processor);
-       return 0;
-}
-
-/* Parse GIC cpu interface entries in MADT for SMP init */
-void __init acpi_init_cpus(void)
-{
-       int count, i;
-
-       /*
-        * do a partial walk of MADT to determine how many CPUs
-        * we have including disabled CPUs, and get information
-        * we need for SMP init
-        */
-       count = acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_INTERRUPT,
-                       acpi_parse_gic_cpu_interface, 0);
-
-       if (!count) {
-               pr_err("No GIC CPU interface entries present\n");
-               return;
-       } else if (count < 0) {
-               pr_err("Error parsing GIC CPU interface entry\n");
-               return;
-       }
-
-       if (!bootcpu_valid) {
-               pr_err("MADT missing boot CPU MPIDR, not enabling secondaries\n");
-               return;
-       }
-
-       for (i = 0; i < enabled_cpus; i++)
-               set_cpu_possible(i, true);
-
-       /* Make boot-up look pretty */
-       pr_info("%d CPUs enabled, %d CPUs total\n", enabled_cpus, total_cpus);
+       return acpi_gbl_FADT.arm_boot_flags & ACPI_FADT_PSCI_USE_HVC;
 }
 
 /*
index 28f8365edc4c43edd00043f38d89f8ae8be6476f..221b98312f0c6a7fa7977ec61cef316c89a529e7 100644 (file)
 #include <asm/cacheflush.h>
 #include <asm/alternative.h>
 #include <asm/cpufeature.h>
+#include <asm/insn.h>
 #include <linux/stop_machine.h>
 
+#define __ALT_PTR(a,f)         (u32 *)((void *)&(a)->f + (a)->f)
+#define ALT_ORIG_PTR(a)                __ALT_PTR(a, orig_offset)
+#define ALT_REPL_PTR(a)                __ALT_PTR(a, alt_offset)
+
 extern struct alt_instr __alt_instructions[], __alt_instructions_end[];
 
 struct alt_region {
@@ -33,13 +38,63 @@ struct alt_region {
        struct alt_instr *end;
 };
 
+/*
+ * Check if the target PC is within an alternative block.
+ */
+static bool branch_insn_requires_update(struct alt_instr *alt, unsigned long pc)
+{
+       unsigned long replptr;
+
+       if (kernel_text_address(pc))
+               return 1;
+
+       replptr = (unsigned long)ALT_REPL_PTR(alt);
+       if (pc >= replptr && pc <= (replptr + alt->alt_len))
+               return 0;
+
+       /*
+        * Branching into *another* alternate sequence is doomed, and
+        * we're not even trying to fix it up.
+        */
+       BUG();
+}
+
+static u32 get_alt_insn(struct alt_instr *alt, u32 *insnptr, u32 *altinsnptr)
+{
+       u32 insn;
+
+       insn = le32_to_cpu(*altinsnptr);
+
+       if (aarch64_insn_is_branch_imm(insn)) {
+               s32 offset = aarch64_get_branch_offset(insn);
+               unsigned long target;
+
+               target = (unsigned long)altinsnptr + offset;
+
+               /*
+                * If we're branching inside the alternate sequence,
+                * do not rewrite the instruction, as it is already
+                * correct. Otherwise, generate the new instruction.
+                */
+               if (branch_insn_requires_update(alt, target)) {
+                       offset = target - (unsigned long)insnptr;
+                       insn = aarch64_set_branch_offset(insn, offset);
+               }
+       }
+
+       return insn;
+}
+
 static int __apply_alternatives(void *alt_region)
 {
        struct alt_instr *alt;
        struct alt_region *region = alt_region;
-       u8 *origptr, *replptr;
+       u32 *origptr, *replptr;
 
        for (alt = region->begin; alt < region->end; alt++) {
+               u32 insn;
+               int i, nr_inst;
+
                if (!cpus_have_cap(alt->cpufeature))
                        continue;
 
@@ -47,11 +102,17 @@ static int __apply_alternatives(void *alt_region)
 
                pr_info_once("patching kernel code\n");
 
-               origptr = (u8 *)&alt->orig_offset + alt->orig_offset;
-               replptr = (u8 *)&alt->alt_offset + alt->alt_offset;
-               memcpy(origptr, replptr, alt->alt_len);
+               origptr = ALT_ORIG_PTR(alt);
+               replptr = ALT_REPL_PTR(alt);
+               nr_inst = alt->alt_len / sizeof(insn);
+
+               for (i = 0; i < nr_inst; i++) {
+                       insn = get_alt_insn(alt, origptr + i, replptr + i);
+                       *(origptr + i) = cpu_to_le32(insn);
+               }
+
                flush_icache_range((uintptr_t)origptr,
-                                  (uintptr_t)(origptr + alt->alt_len));
+                                  (uintptr_t)(origptr + nr_inst));
        }
 
        return 0;
index da675cc5dfaeef4e4e0dfb954c0fe26a46238615..c99701a34d7b3c9238c80e1e4ebbaeba6e3488c3 100644 (file)
@@ -127,7 +127,6 @@ int main(void)
   DEFINE(VCPU_VGIC_CPU,                offsetof(struct kvm_vcpu, arch.vgic_cpu));
   DEFINE(VGIC_SAVE_FN,         offsetof(struct vgic_sr_vectors, save_vgic));
   DEFINE(VGIC_RESTORE_FN,      offsetof(struct vgic_sr_vectors, restore_vgic));
-  DEFINE(VGIC_SR_VECTOR_SZ,    sizeof(struct vgic_sr_vectors));
   DEFINE(VGIC_V2_CPU_HCR,      offsetof(struct vgic_cpu, vgic_v2.vgic_hcr));
   DEFINE(VGIC_V2_CPU_VMCR,     offsetof(struct vgic_cpu, vgic_v2.vgic_vmcr));
   DEFINE(VGIC_V2_CPU_MISR,     offsetof(struct vgic_cpu, vgic_v2.vgic_misr));
index fb8ff9ba467a9701cc870a451c7172f534c37f58..5ea337dd2f15d2d9dedf1507604e5899500559f5 100644 (file)
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#include <asm/cpu_ops.h>
-#include <asm/smp_plat.h>
+#include <linux/acpi.h>
 #include <linux/errno.h>
 #include <linux/of.h>
 #include <linux/string.h>
+#include <asm/acpi.h>
+#include <asm/cpu_ops.h>
+#include <asm/smp_plat.h>
 
 extern const struct cpu_operations smp_spin_table_ops;
 extern const struct cpu_operations cpu_psci_ops;
@@ -35,7 +37,7 @@ static const struct cpu_operations *supported_cpu_ops[] __initconst = {
        NULL,
 };
 
-const struct cpu_operations * __init cpu_get_ops(const char *name)
+static const struct cpu_operations * __init cpu_get_ops(const char *name)
 {
        const struct cpu_operations **ops = supported_cpu_ops;
 
@@ -49,39 +51,53 @@ const struct cpu_operations * __init cpu_get_ops(const char *name)
        return NULL;
 }
 
+static const char *__init cpu_read_enable_method(int cpu)
+{
+       const char *enable_method;
+
+       if (acpi_disabled) {
+               struct device_node *dn = of_get_cpu_node(cpu, NULL);
+
+               if (!dn) {
+                       if (!cpu)
+                               pr_err("Failed to find device node for boot cpu\n");
+                       return NULL;
+               }
+
+               enable_method = of_get_property(dn, "enable-method", NULL);
+               if (!enable_method) {
+                       /*
+                        * The boot CPU may not have an enable method (e.g.
+                        * when spin-table is used for secondaries).
+                        * Don't warn spuriously.
+                        */
+                       if (cpu != 0)
+                               pr_err("%s: missing enable-method property\n",
+                                       dn->full_name);
+               }
+       } else {
+               enable_method = acpi_get_enable_method(cpu);
+               if (!enable_method)
+                       pr_err("Unsupported ACPI enable-method\n");
+       }
+
+       return enable_method;
+}
 /*
- * Read a cpu's enable method from the device tree and record it in cpu_ops.
+ * Read a cpu's enable method and record it in cpu_ops.
  */
-int __init cpu_read_ops(struct device_node *dn, int cpu)
+int __init cpu_read_ops(int cpu)
 {
-       const char *enable_method = of_get_property(dn, "enable-method", NULL);
-       if (!enable_method) {
-               /*
-                * The boot CPU may not have an enable method (e.g. when
-                * spin-table is used for secondaries). Don't warn spuriously.
-                */
-               if (cpu != 0)
-                       pr_err("%s: missing enable-method property\n",
-                               dn->full_name);
-               return -ENOENT;
-       }
+       const char *enable_method = cpu_read_enable_method(cpu);
+
+       if (!enable_method)
+               return -ENODEV;
 
        cpu_ops[cpu] = cpu_get_ops(enable_method);
        if (!cpu_ops[cpu]) {
-               pr_warn("%s: unsupported enable-method property: %s\n",
-                       dn->full_name, enable_method);
+               pr_warn("Unsupported enable-method: %s\n", enable_method);
                return -EOPNOTSUPP;
        }
 
        return 0;
 }
-
-void __init cpu_read_bootcpu_ops(void)
-{
-       struct device_node *dn = of_get_cpu_node(0, NULL);
-       if (!dn) {
-               pr_err("Failed to find device node for boot cpu\n");
-               return;
-       }
-       cpu_read_ops(dn, 0);
-}
index 3d9967e43d896f7b2224952b007663d78a1b700b..5ad86ceac010167870ac8b3354f2711094b3864f 100644 (file)
 #include <asm/cpu.h>
 #include <asm/cpufeature.h>
 
+static bool
+has_id_aa64pfr0_feature(const struct arm64_cpu_capabilities *entry)
+{
+       u64 val;
+
+       val = read_cpuid(id_aa64pfr0_el1);
+       return (val & entry->register_mask) == entry->register_value;
+}
+
 static const struct arm64_cpu_capabilities arm64_features[] = {
+       {
+               .desc = "GIC system register CPU interface",
+               .capability = ARM64_HAS_SYSREG_GIC_CPUIF,
+               .matches = has_id_aa64pfr0_feature,
+               .register_mask = (0xf << 24),
+               .register_value = (1 << 24),
+       },
        {},
 };
 
index a78143a5c99ffb2c9a7ae69267ef3c7f3256c024..7ce589ca54a4fbb8c3b9ea3c5c5235737e1947e7 100644 (file)
 int arm_cpuidle_init(unsigned int cpu)
 {
        int ret = -EOPNOTSUPP;
-       struct device_node *cpu_node = of_cpu_device_node_get(cpu);
-
-       if (!cpu_node)
-               return -ENODEV;
 
        if (cpu_ops[cpu] && cpu_ops[cpu]->cpu_init_idle)
-               ret = cpu_ops[cpu]->cpu_init_idle(cpu_node, cpu);
+               ret = cpu_ops[cpu]->cpu_init_idle(cpu);
 
-       of_node_put(cpu_node);
        return ret;
 }
 
@@ -37,7 +32,7 @@ int arm_cpuidle_init(unsigned int cpu)
  * Return: 0 on success, -EOPNOTSUPP if CPU suspend hook not initialized, CPU
  * operations back-end error code otherwise.
  */
-int cpu_suspend(unsigned long arg)
+int arm_cpuidle_suspend(int index)
 {
        int cpu = smp_processor_id();
 
@@ -47,5 +42,5 @@ int cpu_suspend(unsigned long arg)
         */
        if (!cpu_ops[cpu] || !cpu_ops[cpu]->cpu_suspend)
                return -EOPNOTSUPP;
-       return cpu_ops[cpu]->cpu_suspend(arg);
+       return cpu_ops[cpu]->cpu_suspend(index);
 }
index 959fe8733560971e4a8d9d222f943dac192c6057..a7691a37866834375283b56501e6b11f23f49de8 100644 (file)
@@ -21,7 +21,7 @@
 #include <linux/init.h>
 #include <linux/linkage.h>
 
-#include <asm/alternative-asm.h>
+#include <asm/alternative.h>
 #include <asm/assembler.h>
 #include <asm/asm-offsets.h>
 #include <asm/cpufeature.h>
        msr     sp_el0, x23
 
 #ifdef CONFIG_ARM64_ERRATUM_845719
-       alternative_insn                                                \
-       "nop",                                                          \
-       "tbz x22, #4, 1f",                                              \
-       ARM64_WORKAROUND_845719
+
+#undef SEQUENCE_ORG
+#undef SEQUENCE_ALT
+
 #ifdef CONFIG_PID_IN_CONTEXTIDR
-       alternative_insn                                                \
-       "nop; nop",                                                     \
-       "mrs x29, contextidr_el1; msr contextidr_el1, x29; 1:",         \
-       ARM64_WORKAROUND_845719
+
+#define SEQUENCE_ORG   "nop ; nop ; nop"
+#define SEQUENCE_ALT   "tbz x22, #4, 1f ; mrs x29, contextidr_el1; msr contextidr_el1, x29; 1:"
+
 #else
-       alternative_insn                                                \
-       "nop",                                                          \
-       "msr contextidr_el1, xzr; 1:",                                  \
-       ARM64_WORKAROUND_845719
+
+#define SEQUENCE_ORG   "nop ; nop"
+#define SEQUENCE_ALT   "tbz x22, #4, 1f ; msr contextidr_el1, xzr; 1:"
+
 #endif
+
+       alternative_insn SEQUENCE_ORG, SEQUENCE_ALT, ARM64_WORKAROUND_845719
+
 #endif
        .endif
        msr     elr_el1, x21                    // set up the return data
@@ -517,6 +520,7 @@ el0_sp_pc:
        mrs     x26, far_el1
        // enable interrupts before calling the main handler
        enable_dbg_and_irq
+       ct_user_exit
        mov     x0, x26
        mov     x1, x25
        mov     x2, sp
@@ -608,11 +612,16 @@ ENDPROC(cpu_switch_to)
  */
 ret_fast_syscall:
        disable_irq                             // disable interrupts
-       ldr     x1, [tsk, #TI_FLAGS]
+       ldr     x1, [tsk, #TI_FLAGS]            // re-check for syscall tracing
+       and     x2, x1, #_TIF_SYSCALL_WORK
+       cbnz    x2, ret_fast_syscall_trace
        and     x2, x1, #_TIF_WORK_MASK
        cbnz    x2, fast_work_pending
        enable_step_tsk x1, x2
        kernel_exit 0, ret = 1
+ret_fast_syscall_trace:
+       enable_irq                              // enable interrupts
+       b       __sys_trace_return
 
 /*
  * Ok, we need to do extra processing, enter the slow path.
index 3dca15634e69c6b1b34db6e00b3564089129326b..44d6f7545505f9b6ccd72a87b87958875aa05ac3 100644 (file)
@@ -17,6 +17,7 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <linux/cpu.h>
 #include <linux/cpu_pm.h>
 #include <linux/kernel.h>
 #include <linux/init.h>
@@ -296,6 +297,35 @@ static void fpsimd_pm_init(void)
 static inline void fpsimd_pm_init(void) { }
 #endif /* CONFIG_CPU_PM */
 
+#ifdef CONFIG_HOTPLUG_CPU
+static int fpsimd_cpu_hotplug_notifier(struct notifier_block *nfb,
+                                      unsigned long action,
+                                      void *hcpu)
+{
+       unsigned int cpu = (long)hcpu;
+
+       switch (action) {
+       case CPU_DEAD:
+       case CPU_DEAD_FROZEN:
+               per_cpu(fpsimd_last_state, cpu) = NULL;
+               break;
+       }
+       return NOTIFY_OK;
+}
+
+static struct notifier_block fpsimd_cpu_hotplug_notifier_block = {
+       .notifier_call = fpsimd_cpu_hotplug_notifier,
+};
+
+static inline void fpsimd_hotplug_init(void)
+{
+       register_cpu_notifier(&fpsimd_cpu_hotplug_notifier_block);
+}
+
+#else
+static inline void fpsimd_hotplug_init(void) { }
+#endif
+
 /*
  * FP/SIMD support code initialisation.
  */
@@ -315,6 +345,7 @@ static int __init fpsimd_init(void)
                elf_hwcap |= HWCAP_ASIMD;
 
        fpsimd_pm_init();
+       fpsimd_hotplug_init();
 
        return 0;
 }
index 19f915e8f6e0f001ce061b9eaf22cdedb9506c08..c0ff3ce4299e979d11692540a76da4ec5ade5bba 100644 (file)
@@ -237,8 +237,6 @@ ENTRY(stext)
        bl      el2_setup                       // Drop to EL1, w20=cpu_boot_mode
        adrp    x24, __PHYS_OFFSET
        bl      set_cpu_boot_mode_flag
-
-       bl      __vet_fdt
        bl      __create_page_tables            // x25=TTBR0, x26=TTBR1
        /*
         * The following calls CPU setup code, see arch/arm64/mm/proc.S for
@@ -269,24 +267,6 @@ preserve_boot_args:
        b       __inval_cache_range             // tail call
 ENDPROC(preserve_boot_args)
 
-/*
- * Determine validity of the x21 FDT pointer.
- * The dtb must be 8-byte aligned and live in the first 512M of memory.
- */
-__vet_fdt:
-       tst     x21, #0x7
-       b.ne    1f
-       cmp     x21, x24
-       b.lt    1f
-       mov     x0, #(1 << 29)
-       add     x0, x0, x24
-       cmp     x21, x0
-       b.ge    1f
-       ret
-1:
-       mov     x21, #0
-       ret
-ENDPROC(__vet_fdt)
 /*
  * Macro to create a table entry to the next page.
  *
@@ -348,8 +328,7 @@ ENDPROC(__vet_fdt)
  * required to get the kernel running. The following sections are required:
  *   - identity mapping to enable the MMU (low address, TTBR0)
  *   - first few MB of the kernel linear mapping to jump to once the MMU has
- *     been enabled, including the FDT blob (TTBR1)
- *   - pgd entry for fixed mappings (TTBR1)
+ *     been enabled
  */
 __create_page_tables:
        adrp    x25, idmap_pg_dir
@@ -382,7 +361,7 @@ __create_page_tables:
         * Create the identity mapping.
         */
        mov     x0, x25                         // idmap_pg_dir
-       adrp    x3, KERNEL_START                // __pa(KERNEL_START)
+       adrp    x3, __idmap_text_start          // __pa(__idmap_text_start)
 
 #ifndef CONFIG_ARM64_VA_BITS_48
 #define EXTRA_SHIFT    (PGDIR_SHIFT + PAGE_SHIFT - 3)
@@ -405,11 +384,11 @@ __create_page_tables:
 
        /*
         * Calculate the maximum allowed value for TCR_EL1.T0SZ so that the
-        * entire kernel image can be ID mapped. As T0SZ == (64 - #bits used),
+        * entire ID map region can be mapped. As T0SZ == (64 - #bits used),
         * this number conveniently equals the number of leading zeroes in
-        * the physical address of KERNEL_END.
+        * the physical address of __idmap_text_end.
         */
-       adrp    x5, KERNEL_END
+       adrp    x5, __idmap_text_end
        clz     x5, x5
        cmp     x5, TCR_T0SZ(VA_BITS)   // default T0SZ small enough?
        b.ge    1f                      // .. then skip additional level
@@ -424,8 +403,8 @@ __create_page_tables:
 #endif
 
        create_pgd_entry x0, x3, x5, x6
-       mov     x5, x3                          // __pa(KERNEL_START)
-       adr_l   x6, KERNEL_END                  // __pa(KERNEL_END)
+       mov     x5, x3                          // __pa(__idmap_text_start)
+       adr_l   x6, __idmap_text_end            // __pa(__idmap_text_end)
        create_block_map x0, x7, x3, x5, x6
 
        /*
@@ -438,22 +417,6 @@ __create_page_tables:
        mov     x3, x24                         // phys offset
        create_block_map x0, x7, x3, x5, x6
 
-       /*
-        * Map the FDT blob (maximum 2MB; must be within 512MB of
-        * PHYS_OFFSET).
-        */
-       mov     x3, x21                         // FDT phys address
-       and     x3, x3, #~((1 << 21) - 1)       // 2MB aligned
-       mov     x6, #PAGE_OFFSET
-       sub     x5, x3, x24                     // subtract PHYS_OFFSET
-       tst     x5, #~((1 << 29) - 1)           // within 512MB?
-       csel    x21, xzr, x21, ne               // zero the FDT pointer
-       b.ne    1f
-       add     x5, x5, x6                      // __va(FDT blob)
-       add     x6, x5, #1 << 21                // 2MB for the FDT blob
-       sub     x6, x6, #1                      // inclusive range
-       create_block_map x0, x7, x3, x5, x6
-1:
        /*
         * Since the page tables have been populated with non-cacheable
         * accesses (MMU disabled), invalidate the idmap and swapper page
@@ -669,6 +632,7 @@ ENDPROC(__secondary_switched)
  *
  * other registers depend on the function called upon completion
  */
+       .section        ".idmap.text", "ax"
 __enable_mmu:
        ldr     x5, =vectors
        msr     vbar_el1, x5
index 924902083e47eca28812837c8d76583a30fdaafb..dd9671cd0bb255b2b5fa32a5e1e5a61b6decaac5 100644 (file)
@@ -77,6 +77,14 @@ bool __kprobes aarch64_insn_is_nop(u32 insn)
        }
 }
 
+bool aarch64_insn_is_branch_imm(u32 insn)
+{
+       return (aarch64_insn_is_b(insn) || aarch64_insn_is_bl(insn) ||
+               aarch64_insn_is_tbz(insn) || aarch64_insn_is_tbnz(insn) ||
+               aarch64_insn_is_cbz(insn) || aarch64_insn_is_cbnz(insn) ||
+               aarch64_insn_is_bcond(insn));
+}
+
 static DEFINE_SPINLOCK(patch_lock);
 
 static void __kprobes *patch_map(void *addr, int fixmap)
@@ -1057,6 +1065,58 @@ u32 aarch64_insn_gen_logical_shifted_reg(enum aarch64_insn_register dst,
        return aarch64_insn_encode_immediate(AARCH64_INSN_IMM_6, insn, shift);
 }
 
+/*
+ * Decode the imm field of a branch, and return the byte offset as a
+ * signed value (so it can be used when computing a new branch
+ * target).
+ */
+s32 aarch64_get_branch_offset(u32 insn)
+{
+       s32 imm;
+
+       if (aarch64_insn_is_b(insn) || aarch64_insn_is_bl(insn)) {
+               imm = aarch64_insn_decode_immediate(AARCH64_INSN_IMM_26, insn);
+               return (imm << 6) >> 4;
+       }
+
+       if (aarch64_insn_is_cbz(insn) || aarch64_insn_is_cbnz(insn) ||
+           aarch64_insn_is_bcond(insn)) {
+               imm = aarch64_insn_decode_immediate(AARCH64_INSN_IMM_19, insn);
+               return (imm << 13) >> 11;
+       }
+
+       if (aarch64_insn_is_tbz(insn) || aarch64_insn_is_tbnz(insn)) {
+               imm = aarch64_insn_decode_immediate(AARCH64_INSN_IMM_14, insn);
+               return (imm << 18) >> 16;
+       }
+
+       /* Unhandled instruction */
+       BUG();
+}
+
+/*
+ * Encode the displacement of a branch in the imm field and return the
+ * updated instruction.
+ */
+u32 aarch64_set_branch_offset(u32 insn, s32 offset)
+{
+       if (aarch64_insn_is_b(insn) || aarch64_insn_is_bl(insn))
+               return aarch64_insn_encode_immediate(AARCH64_INSN_IMM_26, insn,
+                                                    offset >> 2);
+
+       if (aarch64_insn_is_cbz(insn) || aarch64_insn_is_cbnz(insn) ||
+           aarch64_insn_is_bcond(insn))
+               return aarch64_insn_encode_immediate(AARCH64_INSN_IMM_19, insn,
+                                                    offset >> 2);
+
+       if (aarch64_insn_is_tbz(insn) || aarch64_insn_is_tbnz(insn))
+               return aarch64_insn_encode_immediate(AARCH64_INSN_IMM_14, insn,
+                                                    offset >> 2);
+
+       /* Unhandled instruction */
+       BUG();
+}
+
 bool aarch32_insn_is_wide(u32 insn)
 {
        return insn >= 0xe800;
index cce18c85d2e8edc6edcf23d13ef13e99f6cbf835..702591f6180a8d50704648977ff48e4f6811880d 100644 (file)
@@ -488,7 +488,7 @@ armpmu_reserve_hardware(struct arm_pmu *armpmu)
                        }
 
                        err = request_irq(irq, armpmu->handle_irq,
-                                       IRQF_NOBALANCING,
+                                       IRQF_NOBALANCING | IRQF_NO_THREAD,
                                        "arm-pmu", armpmu);
                        if (err) {
                                pr_err("unable to request IRQ%d for ARM PMU counters\n",
index c6b1f3b96f4581f329a11805118797afa9556e1d..223b093c9440933f46fb2aa4cbc83729be1e6f57 100644 (file)
@@ -58,14 +58,6 @@ unsigned long __stack_chk_guard __read_mostly;
 EXPORT_SYMBOL(__stack_chk_guard);
 #endif
 
-void soft_restart(unsigned long addr)
-{
-       setup_mm_for_reboot();
-       cpu_soft_restart(virt_to_phys(cpu_reset), addr);
-       /* Should never get here */
-       BUG();
-}
-
 /*
  * Function pointers to optional machine specific functions
  */
@@ -136,9 +128,7 @@ void machine_power_off(void)
 
 /*
  * Restart requires that the secondary CPUs stop performing any activity
- * while the primary CPU resets the system. Systems with a single CPU can
- * use soft_restart() as their machine descriptor's .restart hook, since that
- * will cause the only available CPU to reset. Systems with multiple CPUs must
+ * while the primary CPU resets the system. Systems with multiple CPUs must
  * provide a HW restart implementation, to ensure that all CPUs reset at once.
  * This is required so that any code running after reset on the primary CPU
  * doesn't have to co-ordinate with other CPUs to ensure they aren't still
@@ -243,7 +233,8 @@ void release_thread(struct task_struct *dead_task)
 
 int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src)
 {
-       fpsimd_preserve_current_state();
+       if (current->mm)
+               fpsimd_preserve_current_state();
        *dst = *src;
        return 0;
 }
@@ -254,35 +245,35 @@ int copy_thread(unsigned long clone_flags, unsigned long stack_start,
                unsigned long stk_sz, struct task_struct *p)
 {
        struct pt_regs *childregs = task_pt_regs(p);
-       unsigned long tls = p->thread.tp_value;
 
        memset(&p->thread.cpu_context, 0, sizeof(struct cpu_context));
 
        if (likely(!(p->flags & PF_KTHREAD))) {
                *childregs = *current_pt_regs();
                childregs->regs[0] = 0;
-               if (is_compat_thread(task_thread_info(p))) {
-                       if (stack_start)
+
+               /*
+                * Read the current TLS pointer from tpidr_el0 as it may be
+                * out-of-sync with the saved value.
+                */
+               asm("mrs %0, tpidr_el0" : "=r" (*task_user_tls(p)));
+
+               if (stack_start) {
+                       if (is_compat_thread(task_thread_info(p)))
                                childregs->compat_sp = stack_start;
-               } else {
-                       /*
-                        * Read the current TLS pointer from tpidr_el0 as it may be
-                        * out-of-sync with the saved value.
-                        */
-                       asm("mrs %0, tpidr_el0" : "=r" (tls));
-                       if (stack_start) {
-                               /* 16-byte aligned stack mandatory on AArch64 */
-                               if (stack_start & 15)
-                                       return -EINVAL;
+                       /* 16-byte aligned stack mandatory on AArch64 */
+                       else if (stack_start & 15)
+                               return -EINVAL;
+                       else
                                childregs->sp = stack_start;
-                       }
                }
+
                /*
                 * If a TLS pointer was passed to clone (4th argument), use it
                 * for the new thread.
                 */
                if (clone_flags & CLONE_SETTLS)
-                       tls = childregs->regs[3];
+                       p->thread.tp_value = childregs->regs[3];
        } else {
                memset(childregs, 0, sizeof(struct pt_regs));
                childregs->pstate = PSR_MODE_EL1h;
@@ -291,7 +282,6 @@ int copy_thread(unsigned long clone_flags, unsigned long stack_start,
        }
        p->thread.cpu_context.pc = (unsigned long)ret_from_fork;
        p->thread.cpu_context.sp = (unsigned long)childregs;
-       p->thread.tp_value = tls;
 
        ptrace_hw_copy_thread(p);
 
@@ -302,18 +292,12 @@ static void tls_thread_switch(struct task_struct *next)
 {
        unsigned long tpidr, tpidrro;
 
-       if (!is_compat_task()) {
-               asm("mrs %0, tpidr_el0" : "=r" (tpidr));
-               current->thread.tp_value = tpidr;
-       }
+       asm("mrs %0, tpidr_el0" : "=r" (tpidr));
+       *task_user_tls(current) = tpidr;
 
-       if (is_compat_thread(task_thread_info(next))) {
-               tpidr = 0;
-               tpidrro = next->thread.tp_value;
-       } else {
-               tpidr = next->thread.tp_value;
-               tpidrro = 0;
-       }
+       tpidr = *task_user_tls(next);
+       tpidrro = is_compat_thread(task_thread_info(next)) ?
+                 next->thread.tp_value : 0;
 
        asm(
        "       msr     tpidr_el0, %0\n"
index ea18cb53921e8f8ed8197c6cc7090473a5ba56af..869f202748e8eac758ce09440f0fa6e3b626b987 100644 (file)
@@ -15,7 +15,6 @@
 
 #define pr_fmt(fmt) "psci: " fmt
 
-#include <linux/acpi.h>
 #include <linux/init.h>
 #include <linux/of.h>
 #include <linux/smp.h>
@@ -25,8 +24,8 @@
 #include <linux/slab.h>
 #include <uapi/linux/psci.h>
 
-#include <asm/acpi.h>
 #include <asm/compiler.h>
+#include <asm/cputype.h>
 #include <asm/cpu_ops.h>
 #include <asm/errno.h>
 #include <asm/psci.h>
 #define PSCI_POWER_STATE_TYPE_STANDBY          0
 #define PSCI_POWER_STATE_TYPE_POWER_DOWN       1
 
-struct psci_power_state {
-       u16     id;
-       u8      type;
-       u8      affinity_level;
-};
+static bool psci_power_state_loses_context(u32 state)
+{
+       return state & PSCI_0_2_POWER_STATE_TYPE_MASK;
+}
+
+static bool psci_power_state_is_valid(u32 state)
+{
+       const u32 valid_mask = PSCI_0_2_POWER_STATE_ID_MASK |
+                              PSCI_0_2_POWER_STATE_TYPE_MASK |
+                              PSCI_0_2_POWER_STATE_AFFL_MASK;
+
+       return !(state & ~valid_mask);
+}
+
+/*
+ * The CPU any Trusted OS is resident on. The trusted OS may reject CPU_OFF
+ * calls to its resident CPU, so we must avoid issuing those. We never migrate
+ * a Trusted OS even if it claims to be capable of migration -- doing so will
+ * require cooperation with a Trusted OS driver.
+ */
+static int resident_cpu = -1;
 
 struct psci_operations {
-       int (*cpu_suspend)(struct psci_power_state state,
-                          unsigned long entry_point);
-       int (*cpu_off)(struct psci_power_state state);
+       int (*cpu_suspend)(u32 state, unsigned long entry_point);
+       int (*cpu_off)(u32 state);
        int (*cpu_on)(unsigned long cpuid, unsigned long entry_point);
        int (*migrate)(unsigned long cpuid);
        int (*affinity_info)(unsigned long target_affinity,
@@ -56,23 +70,21 @@ struct psci_operations {
 
 static struct psci_operations psci_ops;
 
-static int (*invoke_psci_fn)(u64, u64, u64, u64);
-typedef int (*psci_initcall_t)(const struct device_node *);
-
-asmlinkage int __invoke_psci_fn_hvc(u64, u64, u64, u64);
-asmlinkage int __invoke_psci_fn_smc(u64, u64, u64, u64);
+typedef unsigned long (psci_fn)(unsigned long, unsigned long,
+                               unsigned long, unsigned long);
+asmlinkage psci_fn __invoke_psci_fn_hvc;
+asmlinkage psci_fn __invoke_psci_fn_smc;
+static psci_fn *invoke_psci_fn;
 
 enum psci_function {
        PSCI_FN_CPU_SUSPEND,
        PSCI_FN_CPU_ON,
        PSCI_FN_CPU_OFF,
        PSCI_FN_MIGRATE,
-       PSCI_FN_AFFINITY_INFO,
-       PSCI_FN_MIGRATE_INFO_TYPE,
        PSCI_FN_MAX,
 };
 
-static DEFINE_PER_CPU_READ_MOSTLY(struct psci_power_state *, psci_power_state);
+static DEFINE_PER_CPU_READ_MOSTLY(u32 *, psci_power_state);
 
 static u32 psci_function_id[PSCI_FN_MAX];
 
@@ -92,56 +104,28 @@ static int psci_to_linux_errno(int errno)
        return -EINVAL;
 }
 
-static u32 psci_power_state_pack(struct psci_power_state state)
-{
-       return ((state.id << PSCI_0_2_POWER_STATE_ID_SHIFT)
-                       & PSCI_0_2_POWER_STATE_ID_MASK) |
-               ((state.type << PSCI_0_2_POWER_STATE_TYPE_SHIFT)
-                & PSCI_0_2_POWER_STATE_TYPE_MASK) |
-               ((state.affinity_level << PSCI_0_2_POWER_STATE_AFFL_SHIFT)
-                & PSCI_0_2_POWER_STATE_AFFL_MASK);
-}
-
-static void psci_power_state_unpack(u32 power_state,
-                                   struct psci_power_state *state)
-{
-       state->id = (power_state & PSCI_0_2_POWER_STATE_ID_MASK) >>
-                       PSCI_0_2_POWER_STATE_ID_SHIFT;
-       state->type = (power_state & PSCI_0_2_POWER_STATE_TYPE_MASK) >>
-                       PSCI_0_2_POWER_STATE_TYPE_SHIFT;
-       state->affinity_level =
-                       (power_state & PSCI_0_2_POWER_STATE_AFFL_MASK) >>
-                       PSCI_0_2_POWER_STATE_AFFL_SHIFT;
-}
-
-static int psci_get_version(void)
+static u32 psci_get_version(void)
 {
-       int err;
-
-       err = invoke_psci_fn(PSCI_0_2_FN_PSCI_VERSION, 0, 0, 0);
-       return err;
+       return invoke_psci_fn(PSCI_0_2_FN_PSCI_VERSION, 0, 0, 0);
 }
 
-static int psci_cpu_suspend(struct psci_power_state state,
-                           unsigned long entry_point)
+static int psci_cpu_suspend(u32 state, unsigned long entry_point)
 {
        int err;
-       u32 fn, power_state;
+       u32 fn;
 
        fn = psci_function_id[PSCI_FN_CPU_SUSPEND];
-       power_state = psci_power_state_pack(state);
-       err = invoke_psci_fn(fn, power_state, entry_point, 0);
+       err = invoke_psci_fn(fn, state, entry_point, 0);
        return psci_to_linux_errno(err);
 }
 
-static int psci_cpu_off(struct psci_power_state state)
+static int psci_cpu_off(u32 state)
 {
        int err;
-       u32 fn, power_state;
+       u32 fn;
 
        fn = psci_function_id[PSCI_FN_CPU_OFF];
-       power_state = psci_power_state_pack(state);
-       err = invoke_psci_fn(fn, power_state, 0, 0);
+       err = invoke_psci_fn(fn, state, 0, 0);
        return psci_to_linux_errno(err);
 }
 
@@ -168,30 +152,29 @@ static int psci_migrate(unsigned long cpuid)
 static int psci_affinity_info(unsigned long target_affinity,
                unsigned long lowest_affinity_level)
 {
-       int err;
-       u32 fn;
-
-       fn = psci_function_id[PSCI_FN_AFFINITY_INFO];
-       err = invoke_psci_fn(fn, target_affinity, lowest_affinity_level, 0);
-       return err;
+       return invoke_psci_fn(PSCI_0_2_FN64_AFFINITY_INFO, target_affinity,
+                             lowest_affinity_level, 0);
 }
 
 static int psci_migrate_info_type(void)
 {
-       int err;
-       u32 fn;
+       return invoke_psci_fn(PSCI_0_2_FN_MIGRATE_INFO_TYPE, 0, 0, 0);
+}
 
-       fn = psci_function_id[PSCI_FN_MIGRATE_INFO_TYPE];
-       err = invoke_psci_fn(fn, 0, 0, 0);
-       return err;
+static unsigned long psci_migrate_info_up_cpu(void)
+{
+       return invoke_psci_fn(PSCI_0_2_FN64_MIGRATE_INFO_UP_CPU, 0, 0, 0);
 }
 
-static int __maybe_unused cpu_psci_cpu_init_idle(struct device_node *cpu_node,
-                                                unsigned int cpu)
+static int __maybe_unused cpu_psci_cpu_init_idle(unsigned int cpu)
 {
        int i, ret, count = 0;
-       struct psci_power_state *psci_states;
-       struct device_node *state_node;
+       u32 *psci_states;
+       struct device_node *state_node, *cpu_node;
+
+       cpu_node = of_get_cpu_node(cpu, NULL);
+       if (!cpu_node)
+               return -ENODEV;
 
        /*
         * If the PSCI cpu_suspend function hook has not been initialized
@@ -215,13 +198,13 @@ static int __maybe_unused cpu_psci_cpu_init_idle(struct device_node *cpu_node,
                return -ENOMEM;
 
        for (i = 0; i < count; i++) {
-               u32 psci_power_state;
+               u32 state;
 
                state_node = of_parse_phandle(cpu_node, "cpu-idle-states", i);
 
                ret = of_property_read_u32(state_node,
                                           "arm,psci-suspend-param",
-                                          &psci_power_state);
+                                          &state);
                if (ret) {
                        pr_warn(" * %s missing arm,psci-suspend-param property\n",
                                state_node->full_name);
@@ -230,9 +213,13 @@ static int __maybe_unused cpu_psci_cpu_init_idle(struct device_node *cpu_node,
                }
 
                of_node_put(state_node);
-               pr_debug("psci-power-state %#x index %d\n", psci_power_state,
-                                                           i);
-               psci_power_state_unpack(psci_power_state, &psci_states[i]);
+               pr_debug("psci-power-state %#x index %d\n", state, i);
+               if (!psci_power_state_is_valid(state)) {
+                       pr_warn("Invalid PSCI power state %#x\n", state);
+                       ret = -EINVAL;
+                       goto free_mem;
+               }
+               psci_states[i] = state;
        }
        /* Idle states parsed correctly, initialize per-cpu pointer */
        per_cpu(psci_power_state, cpu) = psci_states;
@@ -275,6 +262,46 @@ static void psci_sys_poweroff(void)
        invoke_psci_fn(PSCI_0_2_FN_SYSTEM_OFF, 0, 0, 0);
 }
 
+/*
+ * Detect the presence of a resident Trusted OS which may cause CPU_OFF to
+ * return DENIED (which would be fatal).
+ */
+static void __init psci_init_migrate(void)
+{
+       unsigned long cpuid;
+       int type, cpu;
+
+       type = psci_ops.migrate_info_type();
+
+       if (type == PSCI_0_2_TOS_MP) {
+               pr_info("Trusted OS migration not required\n");
+               return;
+       }
+
+       if (type == PSCI_RET_NOT_SUPPORTED) {
+               pr_info("MIGRATE_INFO_TYPE not supported.\n");
+               return;
+       }
+
+       if (type != PSCI_0_2_TOS_UP_MIGRATE &&
+           type != PSCI_0_2_TOS_UP_NO_MIGRATE) {
+               pr_err("MIGRATE_INFO_TYPE returned unknown type (%d)\n", type);
+               return;
+       }
+
+       cpuid = psci_migrate_info_up_cpu();
+       if (cpuid & ~MPIDR_HWID_BITMASK) {
+               pr_warn("MIGRATE_INFO_UP_CPU reported invalid physical ID (0x%lx)\n",
+                       cpuid);
+               return;
+       }
+
+       cpu = get_logical_index(cpuid);
+       resident_cpu = cpu >= 0 ? cpu : -1;
+
+       pr_info("Trusted OS resident on physical CPU 0x%lx\n", cpuid);
+}
+
 static void __init psci_0_2_set_functions(void)
 {
        pr_info("Using standard PSCI v0.2 function IDs\n");
@@ -290,11 +317,8 @@ static void __init psci_0_2_set_functions(void)
        psci_function_id[PSCI_FN_MIGRATE] = PSCI_0_2_FN64_MIGRATE;
        psci_ops.migrate = psci_migrate;
 
-       psci_function_id[PSCI_FN_AFFINITY_INFO] = PSCI_0_2_FN64_AFFINITY_INFO;
        psci_ops.affinity_info = psci_affinity_info;
 
-       psci_function_id[PSCI_FN_MIGRATE_INFO_TYPE] =
-               PSCI_0_2_FN_MIGRATE_INFO_TYPE;
        psci_ops.migrate_info_type = psci_migrate_info_type;
 
        arm_pm_restart = psci_sys_reset;
@@ -307,32 +331,26 @@ static void __init psci_0_2_set_functions(void)
  */
 static int __init psci_probe(void)
 {
-       int ver = psci_get_version();
-
-       if (ver == PSCI_RET_NOT_SUPPORTED) {
-               /*
-                * PSCI versions >=0.2 mandates implementation of
-                * PSCI_VERSION.
-                */
-               pr_err("PSCI firmware does not comply with the v0.2 spec.\n");
-               return -EOPNOTSUPP;
-       } else {
-               pr_info("PSCIv%d.%d detected in firmware.\n",
-                               PSCI_VERSION_MAJOR(ver),
-                               PSCI_VERSION_MINOR(ver));
-
-               if (PSCI_VERSION_MAJOR(ver) == 0 &&
-                               PSCI_VERSION_MINOR(ver) < 2) {
-                       pr_err("Conflicting PSCI version detected.\n");
-                       return -EINVAL;
-               }
+       u32 ver = psci_get_version();
+
+       pr_info("PSCIv%d.%d detected in firmware.\n",
+                       PSCI_VERSION_MAJOR(ver),
+                       PSCI_VERSION_MINOR(ver));
+
+       if (PSCI_VERSION_MAJOR(ver) == 0 && PSCI_VERSION_MINOR(ver) < 2) {
+               pr_err("Conflicting PSCI version detected.\n");
+               return -EINVAL;
        }
 
        psci_0_2_set_functions();
 
+       psci_init_migrate();
+
        return 0;
 }
 
+typedef int (*psci_initcall_t)(const struct device_node *);
+
 /*
  * PSCI init function for PSCI versions >=0.2
  *
@@ -421,6 +439,7 @@ int __init psci_dt_init(void)
        return init_fn(np);
 }
 
+#ifdef CONFIG_ACPI
 /*
  * We use PSCI 0.2+ when ACPI is deployed on ARM64 and it's
  * explicitly clarified in SBBR
@@ -441,10 +460,11 @@ int __init psci_acpi_init(void)
 
        return psci_probe();
 }
+#endif
 
 #ifdef CONFIG_SMP
 
-static int __init cpu_psci_cpu_init(struct device_node *dn, unsigned int cpu)
+static int __init cpu_psci_cpu_init(unsigned int cpu)
 {
        return 0;
 }
@@ -469,11 +489,21 @@ static int cpu_psci_cpu_boot(unsigned int cpu)
 }
 
 #ifdef CONFIG_HOTPLUG_CPU
+static bool psci_tos_resident_on(int cpu)
+{
+       return cpu == resident_cpu;
+}
+
 static int cpu_psci_cpu_disable(unsigned int cpu)
 {
        /* Fail early if we don't have CPU_OFF support */
        if (!psci_ops.cpu_off)
                return -EOPNOTSUPP;
+
+       /* Trusted OS will deny CPU_OFF */
+       if (psci_tos_resident_on(cpu))
+               return -EPERM;
+
        return 0;
 }
 
@@ -484,9 +514,8 @@ static void cpu_psci_cpu_die(unsigned int cpu)
         * There are no known implementations of PSCI actually using the
         * power state field, pass a sensible default for now.
         */
-       struct psci_power_state state = {
-               .type = PSCI_POWER_STATE_TYPE_POWER_DOWN,
-       };
+       u32 state = PSCI_POWER_STATE_TYPE_POWER_DOWN <<
+                   PSCI_0_2_POWER_STATE_TYPE_SHIFT;
 
        ret = psci_ops.cpu_off(state);
 
@@ -498,7 +527,7 @@ static int cpu_psci_cpu_kill(unsigned int cpu)
        int err, i;
 
        if (!psci_ops.affinity_info)
-               return 1;
+               return 0;
        /*
         * cpu_kill could race with cpu_die and we can
         * potentially end up declaring this cpu undead
@@ -509,7 +538,7 @@ static int cpu_psci_cpu_kill(unsigned int cpu)
                err = psci_ops.affinity_info(cpu_logical_map(cpu), 0);
                if (err == PSCI_0_2_AFFINITY_LEVEL_OFF) {
                        pr_info("CPU%d killed.\n", cpu);
-                       return 1;
+                       return 0;
                }
 
                msleep(10);
@@ -518,15 +547,14 @@ static int cpu_psci_cpu_kill(unsigned int cpu)
 
        pr_warn("CPU%d may not have shut down cleanly (AFFINITY_INFO reports %d)\n",
                        cpu, err);
-       /* Make op_cpu_kill() fail. */
-       return 0;
+       return -ETIMEDOUT;
 }
 #endif
 #endif
 
 static int psci_suspend_finisher(unsigned long index)
 {
-       struct psci_power_state *state = __this_cpu_read(psci_power_state);
+       u32 *state = __this_cpu_read(psci_power_state);
 
        return psci_ops.cpu_suspend(state[index - 1],
                                    virt_to_phys(cpu_resume));
@@ -535,7 +563,7 @@ static int psci_suspend_finisher(unsigned long index)
 static int __maybe_unused cpu_psci_cpu_suspend(unsigned long index)
 {
        int ret;
-       struct psci_power_state *state = __this_cpu_read(psci_power_state);
+       u32 *state = __this_cpu_read(psci_power_state);
        /*
         * idle state index 0 corresponds to wfi, should never be called
         * from the cpu_suspend operations
@@ -543,10 +571,10 @@ static int __maybe_unused cpu_psci_cpu_suspend(unsigned long index)
        if (WARN_ON_ONCE(!index))
                return -EINVAL;
 
-       if (state[index - 1].type == PSCI_POWER_STATE_TYPE_STANDBY)
+       if (!psci_power_state_loses_context(state[index - 1]))
                ret = psci_ops.cpu_suspend(state[index - 1], 0);
        else
-               ret = __cpu_suspend(index, psci_suspend_finisher);
+               ret = cpu_suspend(index, psci_suspend_finisher);
 
        return ret;
 }
index 74753132c3ac8fc9bb58d6585d3d08960faed69a..ffd3970721bf6497db2f622bcd3ed31fd3007de1 100644 (file)
@@ -105,18 +105,6 @@ static struct resource mem_res[] = {
 #define kernel_code mem_res[0]
 #define kernel_data mem_res[1]
 
-void __init early_print(const char *str, ...)
-{
-       char buf[256];
-       va_list ap;
-
-       va_start(ap, str);
-       vsnprintf(buf, sizeof(buf), str, ap);
-       va_end(ap);
-
-       printk("%s", buf);
-}
-
 /*
  * The recorded values of x0 .. x3 upon kernel entry.
  */
@@ -326,12 +314,14 @@ static void __init setup_processor(void)
 
 static void __init setup_machine_fdt(phys_addr_t dt_phys)
 {
-       if (!dt_phys || !early_init_dt_scan(phys_to_virt(dt_phys))) {
-               early_print("\n"
-                       "Error: invalid device tree blob at physical address 0x%p (virtual address 0x%p)\n"
-                       "The dtb must be 8-byte aligned and passed in the first 512MB of memory\n"
-                       "\nPlease check your bootloader.\n",
-                       dt_phys, phys_to_virt(dt_phys));
+       void *dt_virt = fixmap_remap_fdt(dt_phys);
+
+       if (!dt_virt || !early_init_dt_scan(dt_virt)) {
+               pr_crit("\n"
+                       "Error: invalid device tree blob at physical address %pa (virtual address 0x%p)\n"
+                       "The dtb must be 8-byte aligned and must not exceed 2 MB in size\n"
+                       "\nPlease check your bootloader.",
+                       &dt_phys, dt_virt);
 
                while (true)
                        cpu_relax();
@@ -374,8 +364,6 @@ void __init setup_arch(char **cmdline_p)
 {
        setup_processor();
 
-       setup_machine_fdt(__fdt_pointer);
-
        init_mm.start_code = (unsigned long) _text;
        init_mm.end_code   = (unsigned long) _etext;
        init_mm.end_data   = (unsigned long) _edata;
@@ -386,6 +374,8 @@ void __init setup_arch(char **cmdline_p)
        early_fixmap_init();
        early_ioremap_init();
 
+       setup_machine_fdt(__fdt_pointer);
+
        parse_early_param();
 
        /*
@@ -408,16 +398,13 @@ void __init setup_arch(char **cmdline_p)
        if (acpi_disabled) {
                unflatten_device_tree();
                psci_dt_init();
-               cpu_read_bootcpu_ops();
-#ifdef CONFIG_SMP
-               of_smp_init_cpus();
-#endif
        } else {
                psci_acpi_init();
-               acpi_init_cpus();
        }
 
+       cpu_read_bootcpu_ops();
 #ifdef CONFIG_SMP
+       smp_init_cpus();
        smp_build_mpidr_hash();
 #endif
 
index d26fcd4cd6e6219cae5213b4b329d53d90a86a9b..1670f15ef69e34972986081deb9b1f87b0bb2bb3 100644 (file)
@@ -370,7 +370,7 @@ badframe:
        if (show_unhandled_signals)
                pr_info_ratelimited("%s[%d]: bad frame in %s: pc=%08llx sp=%08llx\n",
                                    current->comm, task_pid_nr(current), __func__,
-                                   regs->pc, regs->sp);
+                                   regs->pc, regs->compat_sp);
        force_sig(SIGSEGV, current);
        return 0;
 }
@@ -407,7 +407,7 @@ badframe:
        if (show_unhandled_signals)
                pr_info_ratelimited("%s[%d]: bad frame in %s: pc=%08llx sp=%08llx\n",
                                    current->comm, task_pid_nr(current), __func__,
-                                   regs->pc, regs->sp);
+                                   regs->pc, regs->compat_sp);
        force_sig(SIGSEGV, current);
        return 0;
 }
index ede186cdd4520169fd2ea9259d4738b8796bf45d..803cfea41962f48c3b1649bbeeaa5d88c96596d9 100644 (file)
@@ -130,12 +130,14 @@ ENDPROC(__cpu_suspend_enter)
 /*
  * x0 must contain the sctlr value retrieved from restored context
  */
+       .pushsection    ".idmap.text", "ax"
 ENTRY(cpu_resume_mmu)
        ldr     x3, =cpu_resume_after_mmu
        msr     sctlr_el1, x0           // restore sctlr_el1
        isb
        br      x3                      // global jump to virtual address
 ENDPROC(cpu_resume_mmu)
+       .popsection
 cpu_resume_after_mmu:
        mov     x0, #0                  // return zero on success
        ldp     x19, x20, [sp, #16]
@@ -162,15 +164,12 @@ ENTRY(cpu_resume)
 #else
        mov     x7, xzr
 #endif
-       adrp    x0, sleep_save_sp
-       add     x0, x0, #:lo12:sleep_save_sp
-       ldr     x0, [x0, #SLEEP_SAVE_SP_PHYS]
+       ldr_l   x0, sleep_save_sp + SLEEP_SAVE_SP_PHYS
        ldr     x0, [x0, x7, lsl #3]
        /* load sp from context */
        ldr     x2, [x0, #CPU_CTX_SP]
-       adrp    x1, sleep_idmap_phys
        /* load physical address of identity map page table in x1 */
-       ldr     x1, [x1, #:lo12:sleep_idmap_phys]
+       adrp    x1, idmap_pg_dir
        mov     sp, x2
        /*
         * cpu_do_resume expects x0 to contain context physical address
index 2cb008177252f08d876875e08e4448b5b3260dde..4b2121bd7f9cf9f6244ba4e3ff7acca7d00e80e6 100644 (file)
@@ -17,6 +17,7 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <linux/acpi.h>
 #include <linux/delay.h>
 #include <linux/init.h>
 #include <linux/spinlock.h>
@@ -248,20 +249,20 @@ static int op_cpu_kill(unsigned int cpu)
         * time and hope that it's dead, so let's skip the wait and just hope.
         */
        if (!cpu_ops[cpu]->cpu_kill)
-               return 1;
+               return 0;
 
        return cpu_ops[cpu]->cpu_kill(cpu);
 }
 
-static DECLARE_COMPLETION(cpu_died);
-
 /*
  * called on the thread which is asking for a CPU to be shutdown -
  * waits until shutdown has completed, or it is timed out.
  */
 void __cpu_die(unsigned int cpu)
 {
-       if (!wait_for_completion_timeout(&cpu_died, msecs_to_jiffies(5000))) {
+       int err;
+
+       if (!cpu_wait_death(cpu, 5)) {
                pr_crit("CPU%u: cpu didn't die\n", cpu);
                return;
        }
@@ -273,8 +274,10 @@ void __cpu_die(unsigned int cpu)
         * verify that it has really left the kernel before we consider
         * clobbering anything it might still be using.
         */
-       if (!op_cpu_kill(cpu))
-               pr_warn("CPU%d may not have shut down cleanly\n", cpu);
+       err = op_cpu_kill(cpu);
+       if (err)
+               pr_warn("CPU%d may not have shut down cleanly: %d\n",
+                       cpu, err);
 }
 
 /*
@@ -294,7 +297,7 @@ void cpu_die(void)
        local_irq_disable();
 
        /* Tell __cpu_die() that this CPU is now safe to dispose of */
-       complete(&cpu_died);
+       (void)cpu_report_death();
 
        /*
         * Actually shutdown the CPU. This must never fail. The specific hotplug
@@ -318,56 +321,157 @@ void __init smp_prepare_boot_cpu(void)
        set_my_cpu_offset(per_cpu_offset(smp_processor_id()));
 }
 
+static u64 __init of_get_cpu_mpidr(struct device_node *dn)
+{
+       const __be32 *cell;
+       u64 hwid;
+
+       /*
+        * A cpu node with missing "reg" property is
+        * considered invalid to build a cpu_logical_map
+        * entry.
+        */
+       cell = of_get_property(dn, "reg", NULL);
+       if (!cell) {
+               pr_err("%s: missing reg property\n", dn->full_name);
+               return INVALID_HWID;
+       }
+
+       hwid = of_read_number(cell, of_n_addr_cells(dn));
+       /*
+        * Non affinity bits must be set to 0 in the DT
+        */
+       if (hwid & ~MPIDR_HWID_BITMASK) {
+               pr_err("%s: invalid reg property\n", dn->full_name);
+               return INVALID_HWID;
+       }
+       return hwid;
+}
+
+/*
+ * Duplicate MPIDRs are a recipe for disaster. Scan all initialized
+ * entries and check for duplicates. If any is found just ignore the
+ * cpu. cpu_logical_map was initialized to INVALID_HWID to avoid
+ * matching valid MPIDR values.
+ */
+static bool __init is_mpidr_duplicate(unsigned int cpu, u64 hwid)
+{
+       unsigned int i;
+
+       for (i = 1; (i < cpu) && (i < NR_CPUS); i++)
+               if (cpu_logical_map(i) == hwid)
+                       return true;
+       return false;
+}
+
+/*
+ * Initialize cpu operations for a logical cpu and
+ * set it in the possible mask on success
+ */
+static int __init smp_cpu_setup(int cpu)
+{
+       if (cpu_read_ops(cpu))
+               return -ENODEV;
+
+       if (cpu_ops[cpu]->cpu_init(cpu))
+               return -ENODEV;
+
+       set_cpu_possible(cpu, true);
+
+       return 0;
+}
+
+static bool bootcpu_valid __initdata;
+static unsigned int cpu_count = 1;
+
+#ifdef CONFIG_ACPI
+/*
+ * acpi_map_gic_cpu_interface - parse processor MADT entry
+ *
+ * Carry out sanity checks on MADT processor entry and initialize
+ * cpu_logical_map on success
+ */
+static void __init
+acpi_map_gic_cpu_interface(struct acpi_madt_generic_interrupt *processor)
+{
+       u64 hwid = processor->arm_mpidr;
+
+       if (hwid & ~MPIDR_HWID_BITMASK || hwid == INVALID_HWID) {
+               pr_err("skipping CPU entry with invalid MPIDR 0x%llx\n", hwid);
+               return;
+       }
+
+       if (!(processor->flags & ACPI_MADT_ENABLED)) {
+               pr_err("skipping disabled CPU entry with 0x%llx MPIDR\n", hwid);
+               return;
+       }
+
+       if (is_mpidr_duplicate(cpu_count, hwid)) {
+               pr_err("duplicate CPU MPIDR 0x%llx in MADT\n", hwid);
+               return;
+       }
+
+       /* Check if GICC structure of boot CPU is available in the MADT */
+       if (cpu_logical_map(0) == hwid) {
+               if (bootcpu_valid) {
+                       pr_err("duplicate boot CPU MPIDR: 0x%llx in MADT\n",
+                              hwid);
+                       return;
+               }
+               bootcpu_valid = true;
+               return;
+       }
+
+       if (cpu_count >= NR_CPUS)
+               return;
+
+       /* map the logical cpu id to cpu MPIDR */
+       cpu_logical_map(cpu_count) = hwid;
+
+       cpu_count++;
+}
+
+static int __init
+acpi_parse_gic_cpu_interface(struct acpi_subtable_header *header,
+                            const unsigned long end)
+{
+       struct acpi_madt_generic_interrupt *processor;
+
+       processor = (struct acpi_madt_generic_interrupt *)header;
+       if (BAD_MADT_ENTRY(processor, end))
+               return -EINVAL;
+
+       acpi_table_print_madt_entry(header);
+
+       acpi_map_gic_cpu_interface(processor);
+
+       return 0;
+}
+#else
+#define acpi_table_parse_madt(...)     do { } while (0)
+#endif
+
 /*
  * Enumerate the possible CPU set from the device tree and build the
  * cpu logical map array containing MPIDR values related to logical
  * cpus. Assumes that cpu_logical_map(0) has already been initialized.
  */
-void __init of_smp_init_cpus(void)
+void __init of_parse_and_init_cpus(void)
 {
        struct device_node *dn = NULL;
-       unsigned int i, cpu = 1;
-       bool bootcpu_valid = false;
 
        while ((dn = of_find_node_by_type(dn, "cpu"))) {
-               const u32 *cell;
-               u64 hwid;
+               u64 hwid = of_get_cpu_mpidr(dn);
 
-               /*
-                * A cpu node with missing "reg" property is
-                * considered invalid to build a cpu_logical_map
-                * entry.
-                */
-               cell = of_get_property(dn, "reg", NULL);
-               if (!cell) {
-                       pr_err("%s: missing reg property\n", dn->full_name);
+               if (hwid == INVALID_HWID)
                        goto next;
-               }
-               hwid = of_read_number(cell, of_n_addr_cells(dn));
 
-               /*
-                * Non affinity bits must be set to 0 in the DT
-                */
-               if (hwid & ~MPIDR_HWID_BITMASK) {
-                       pr_err("%s: invalid reg property\n", dn->full_name);
+               if (is_mpidr_duplicate(cpu_count, hwid)) {
+                       pr_err("%s: duplicate cpu reg properties in the DT\n",
+                               dn->full_name);
                        goto next;
                }
 
-               /*
-                * Duplicate MPIDRs are a recipe for disaster. Scan
-                * all initialized entries and check for
-                * duplicates. If any is found just ignore the cpu.
-                * cpu_logical_map was initialized to INVALID_HWID to
-                * avoid matching valid MPIDR values.
-                */
-               for (i = 1; (i < cpu) && (i < NR_CPUS); i++) {
-                       if (cpu_logical_map(i) == hwid) {
-                               pr_err("%s: duplicate cpu reg properties in the DT\n",
-                                       dn->full_name);
-                               goto next;
-                       }
-               }
-
                /*
                 * The numbering scheme requires that the boot CPU
                 * must be assigned logical id 0. Record it so that
@@ -392,38 +496,58 @@ void __init of_smp_init_cpus(void)
                        continue;
                }
 
-               if (cpu >= NR_CPUS)
-                       goto next;
-
-               if (cpu_read_ops(dn, cpu) != 0)
-                       goto next;
-
-               if (cpu_ops[cpu]->cpu_init(dn, cpu))
+               if (cpu_count >= NR_CPUS)
                        goto next;
 
                pr_debug("cpu logical map 0x%llx\n", hwid);
-               cpu_logical_map(cpu) = hwid;
+               cpu_logical_map(cpu_count) = hwid;
 next:
-               cpu++;
+               cpu_count++;
        }
+}
+
+/*
+ * Enumerate the possible CPU set from the device tree or ACPI and build the
+ * cpu logical map array containing MPIDR values related to logical
+ * cpus. Assumes that cpu_logical_map(0) has already been initialized.
+ */
+void __init smp_init_cpus(void)
+{
+       int i;
 
-       /* sanity check */
-       if (cpu > NR_CPUS)
-               pr_warning("no. of cores (%d) greater than configured maximum of %d - clipping\n",
-                          cpu, NR_CPUS);
+       if (acpi_disabled)
+               of_parse_and_init_cpus();
+       else
+               /*
+                * do a walk of MADT to determine how many CPUs
+                * we have including disabled CPUs, and get information
+                * we need for SMP init
+                */
+               acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_INTERRUPT,
+                                     acpi_parse_gic_cpu_interface, 0);
+
+       if (cpu_count > NR_CPUS)
+               pr_warn("no. of cores (%d) greater than configured maximum of %d - clipping\n",
+                       cpu_count, NR_CPUS);
 
        if (!bootcpu_valid) {
-               pr_err("DT missing boot CPU MPIDR, not enabling secondaries\n");
+               pr_err("missing boot CPU MPIDR, not enabling secondaries\n");
                return;
        }
 
        /*
-        * All the cpus that made it to the cpu_logical_map have been
-        * validated so set them as possible cpus.
+        * We need to set the cpu_logical_map entries before enabling
+        * the cpus so that cpu processor description entries (DT cpu nodes
+        * and ACPI MADT entries) can be retrieved by matching the cpu hwid
+        * with entries in cpu_logical_map while initializing the cpus.
+        * If the cpu set-up fails, invalidate the cpu_logical_map entry.
         */
-       for (i = 0; i < NR_CPUS; i++)
-               if (cpu_logical_map(i) != INVALID_HWID)
-                       set_cpu_possible(i, true);
+       for (i = 1; i < NR_CPUS; i++) {
+               if (cpu_logical_map(i) != INVALID_HWID) {
+                       if (smp_cpu_setup(i))
+                               cpu_logical_map(i) = INVALID_HWID;
+               }
+       }
 }
 
 void __init smp_prepare_cpus(unsigned int max_cpus)
index 14944e5b28dace9ea083e74f4849d4f40eadef95..aef3605a8c47845ce2ce2d32f75e0fd59a8e4405 100644 (file)
@@ -49,8 +49,14 @@ static void write_pen_release(u64 val)
 }
 
 
-static int smp_spin_table_cpu_init(struct device_node *dn, unsigned int cpu)
+static int smp_spin_table_cpu_init(unsigned int cpu)
 {
+       struct device_node *dn;
+
+       dn = of_get_cpu_node(cpu, NULL);
+       if (!dn)
+               return -ENODEV;
+
        /*
         * Determine the address from which the CPU is polling.
         */
index d7daf45ae7a25307f8c8007c7b8240d9405352fc..8297d502217e13010bc891e7d68b5f7279ea62b9 100644 (file)
@@ -51,13 +51,13 @@ void __init cpu_suspend_set_dbg_restorer(void (*hw_bp_restore)(void *))
 }
 
 /*
- * __cpu_suspend
+ * cpu_suspend
  *
  * arg: argument to pass to the finisher function
  * fn: finisher function pointer
  *
  */
-int __cpu_suspend(unsigned long arg, int (*fn)(unsigned long))
+int cpu_suspend(unsigned long arg, int (*fn)(unsigned long))
 {
        struct mm_struct *mm = current->active_mm;
        int ret;
@@ -82,7 +82,7 @@ int __cpu_suspend(unsigned long arg, int (*fn)(unsigned long))
                 * We are resuming from reset with TTBR0_EL1 set to the
                 * idmap to enable the MMU; restore the active_mm mappings in
                 * TTBR0_EL1 unless the active_mm == &init_mm, in which case
-                * the thread entered __cpu_suspend with TTBR0_EL1 set to
+                * the thread entered cpu_suspend with TTBR0_EL1 set to
                 * reserved TTBR0 page tables and should be restored as such.
                 */
                if (mm == &init_mm)
@@ -118,7 +118,6 @@ int __cpu_suspend(unsigned long arg, int (*fn)(unsigned long))
 }
 
 struct sleep_save_sp sleep_save_sp;
-phys_addr_t sleep_idmap_phys;
 
 static int __init cpu_suspend_init(void)
 {
@@ -132,9 +131,7 @@ static int __init cpu_suspend_init(void)
 
        sleep_save_sp.save_ptr_stash = ctx_ptr;
        sleep_save_sp.save_ptr_stash_phys = virt_to_phys(ctx_ptr);
-       sleep_idmap_phys = virt_to_phys(idmap_pg_dir);
        __flush_dcache_area(&sleep_save_sp, sizeof(struct sleep_save_sp));
-       __flush_dcache_area(&sleep_idmap_phys, sizeof(sleep_idmap_phys));
 
        return 0;
 }
index 1ef2940df13c5e95e9d1e8b333b96354dc786411..a12251c074a8298435cc5d10d7361f87a0c0b128 100644 (file)
@@ -335,8 +335,7 @@ asmlinkage void __exception do_undefinstr(struct pt_regs *regs)
        if (call_undef_hook(regs) == 0)
                return;
 
-       if (show_unhandled_signals && unhandled_signal(current, SIGILL) &&
-           printk_ratelimit()) {
+       if (show_unhandled_signals_ratelimited() && unhandled_signal(current, SIGILL)) {
                pr_info("%s[%d]: undefined instruction: pc=%p\n",
                        current->comm, task_pid_nr(current), pc);
                dump_instr(KERN_INFO, regs);
@@ -363,7 +362,7 @@ asmlinkage long do_ni_syscall(struct pt_regs *regs)
        }
 #endif
 
-       if (show_unhandled_signals && printk_ratelimit()) {
+       if (show_unhandled_signals_ratelimited()) {
                pr_info("%s[%d]: syscall %d\n", current->comm,
                        task_pid_nr(current), (int)regs->syscallno);
                dump_instr("", regs);
index ff3bddea482dd84ced8711376af1084f6ae594ee..f6fe17d88da55d0b212b4d6ffe900f962e1e7bd5 100644 (file)
@@ -15,6 +15,10 @@ ccflags-y := -shared -fno-common -fno-builtin
 ccflags-y += -nostdlib -Wl,-soname=linux-vdso.so.1 \
                $(call cc-ldoption, -Wl$(comma)--hash-style=sysv)
 
+# Workaround for bare-metal (ELF) toolchains that neglect to pass -shared
+# down to collect2, resulting in silent corruption of the vDSO image.
+ccflags-y += -Wl,-shared
+
 obj-y += vdso.o
 extra-y += vdso.lds vdso-offsets.h
 CPPFLAGS_vdso.lds += -P -C -U$(ARCH)
index a2c29865c3fe54e2d3c8c7ce1b956ba5ce5c0bf7..98073332e2d05b62c12be5c9f4172ddcc3363736 100644 (file)
@@ -38,6 +38,12 @@ jiffies = jiffies_64;
        *(.hyp.text)                                    \
        VMLINUX_SYMBOL(__hyp_text_end) = .;
 
+#define IDMAP_TEXT                                     \
+       . = ALIGN(SZ_4K);                               \
+       VMLINUX_SYMBOL(__idmap_text_start) = .;         \
+       *(.idmap.text)                                  \
+       VMLINUX_SYMBOL(__idmap_text_end) = .;
+
 /*
  * The size of the PE/COFF section that covers the kernel image, which
  * runs from stext to _edata, must be a round multiple of the PE/COFF
@@ -95,6 +101,7 @@ SECTIONS
                        SCHED_TEXT
                        LOCK_TEXT
                        HYPERVISOR_TEXT
+                       IDMAP_TEXT
                        *(.fixup)
                        *(.gnu.warning)
                . = ALIGN(16);
@@ -167,11 +174,13 @@ SECTIONS
 }
 
 /*
- * The HYP init code can't be more than a page long,
+ * The HYP init code and ID map text can't be longer than a page each,
  * and should not cross a page boundary.
  */
 ASSERT(__hyp_idmap_text_end - (__hyp_idmap_text_start & ~(SZ_4K - 1)) <= SZ_4K,
        "HYP init code too big or misaligned")
+ASSERT(__idmap_text_end - (__idmap_text_start & ~(SZ_4K - 1)) <= SZ_4K,
+       "ID map text too big or misaligned")
 
 /*
  * If padding is applied before .head.text, virt<->phys conversions will fail.
index 5105e297ed5fef43509f264a001138d299fb5cba..bfffe8f4bd53ef0ac9314acb79dbad197b953e53 100644 (file)
@@ -28,6 +28,7 @@ config KVM
        select KVM_ARM_HOST
        select KVM_GENERIC_DIRTYLOG_READ_PROTECT
        select SRCU
+       select KVM_VFIO
        select HAVE_KVM_EVENTFD
        select HAVE_KVM_IRQFD
        ---help---
index d5904f876cdb535a373c6299beeec09bb5538331..f90f4aa7f88d9d60ba4e8b53645c7d81d15f3e07 100644 (file)
@@ -11,7 +11,7 @@ ARM=../../../arch/arm/kvm
 
 obj-$(CONFIG_KVM_ARM_HOST) += kvm.o
 
-kvm-$(CONFIG_KVM_ARM_HOST) += $(KVM)/kvm_main.o $(KVM)/coalesced_mmio.o $(KVM)/eventfd.o
+kvm-$(CONFIG_KVM_ARM_HOST) += $(KVM)/kvm_main.o $(KVM)/coalesced_mmio.o $(KVM)/eventfd.o $(KVM)/vfio.o
 kvm-$(CONFIG_KVM_ARM_HOST) += $(ARM)/arm.o $(ARM)/mmu.o $(ARM)/mmio.o
 kvm-$(CONFIG_KVM_ARM_HOST) += $(ARM)/psci.o $(ARM)/perf.o
 
index 5befd010e23255c151999334daf9336912b4b664..17a8fb14f428d47226edbeeb661e840ff53b8988 100644 (file)
 
 #include <linux/linkage.h>
 
+#include <asm/alternative.h>
 #include <asm/asm-offsets.h>
 #include <asm/assembler.h>
+#include <asm/cpufeature.h>
 #include <asm/debug-monitors.h>
 #include <asm/esr.h>
 #include <asm/fpsimdmacros.h>
@@ -50,8 +52,8 @@
        stp     x29, lr, [x3, #80]
 
        mrs     x19, sp_el0
-       mrs     x20, elr_el2            // EL1 PC
-       mrs     x21, spsr_el2           // EL1 pstate
+       mrs     x20, elr_el2            // pc before entering el2
+       mrs     x21, spsr_el2           // pstate before entering el2
 
        stp     x19, x20, [x3, #96]
        str     x21, [x3, #112]
@@ -82,8 +84,8 @@
        ldr     x21, [x3, #16]
 
        msr     sp_el0, x19
-       msr     elr_el2, x20                            // EL1 PC
-       msr     spsr_el2, x21                           // EL1 pstate
+       msr     elr_el2, x20            // pc on return from el2
+       msr     spsr_el2, x21           // pstate on return from el2
 
        add     x3, x2, #CPU_XREG_OFFSET(19)
        ldp     x19, x20, [x3]
  * Call into the vgic backend for state saving
  */
 .macro save_vgic_state
-       adr     x24, __vgic_sr_vectors
-       ldr     x24, [x24, VGIC_SAVE_FN]
-       kern_hyp_va     x24
-       blr     x24
+       alternative_insn "bl __save_vgic_v2_state", "bl __save_vgic_v3_state", ARM64_HAS_SYSREG_GIC_CPUIF
        mrs     x24, hcr_el2
        mov     x25, #HCR_INT_OVERRIDE
        neg     x25, x25
        orr     x24, x24, #HCR_INT_OVERRIDE
        orr     x24, x24, x25
        msr     hcr_el2, x24
-       adr     x24, __vgic_sr_vectors
-       ldr     x24, [x24, #VGIC_RESTORE_FN]
-       kern_hyp_va     x24
-       blr     x24
+       alternative_insn "bl __restore_vgic_v2_state", "bl __restore_vgic_v3_state", ARM64_HAS_SYSREG_GIC_CPUIF
 .endm
 
 .macro save_timer_state
@@ -1062,12 +1058,6 @@ ENTRY(__kvm_flush_vm_context)
        ret
 ENDPROC(__kvm_flush_vm_context)
 
-       // struct vgic_sr_vectors __vgi_sr_vectors;
-       .align 3
-ENTRY(__vgic_sr_vectors)
-       .skip   VGIC_SR_VECTOR_SZ
-ENDPROC(__vgic_sr_vectors)
-
 __kvm_hyp_panic:
        // Guess the context by looking at VTTBR:
        // If zero, then we're already a host.
index f002fe1c37002aff928e834a3c6c93008a3aa7d1..3f000712a85df9be3926a44fb1f67e9d2310c6eb 100644 (file)
@@ -47,7 +47,6 @@ __save_vgic_v2_state:
        add     x3, x0, #VCPU_VGIC_CPU
 
        /* Save all interesting registers */
-       ldr     w4, [x2, #GICH_HCR]
        ldr     w5, [x2, #GICH_VMCR]
        ldr     w6, [x2, #GICH_MISR]
        ldr     w7, [x2, #GICH_EISR0]
@@ -55,7 +54,6 @@ __save_vgic_v2_state:
        ldr     w9, [x2, #GICH_ELRSR0]
        ldr     w10, [x2, #GICH_ELRSR1]
        ldr     w11, [x2, #GICH_APR]
-CPU_BE(        rev     w4,  w4  )
 CPU_BE(        rev     w5,  w5  )
 CPU_BE(        rev     w6,  w6  )
 CPU_BE(        rev     w7,  w7  )
@@ -64,7 +62,6 @@ CPU_BE(       rev     w9,  w9  )
 CPU_BE(        rev     w10, w10 )
 CPU_BE(        rev     w11, w11 )
 
-       str     w4, [x3, #VGIC_V2_CPU_HCR]
        str     w5, [x3, #VGIC_V2_CPU_VMCR]
        str     w6, [x3, #VGIC_V2_CPU_MISR]
 CPU_LE(        str     w7, [x3, #VGIC_V2_CPU_EISR] )
index 617a012a01078393e806be0daaf068af24c230cd..3c20730ddff53c019998ff98672534ff0a45b1cc 100644 (file)
        dsb     st
 
        // Save all interesting registers
-       mrs_s   x4, ICH_HCR_EL2
        mrs_s   x5, ICH_VMCR_EL2
        mrs_s   x6, ICH_MISR_EL2
        mrs_s   x7, ICH_EISR_EL2
        mrs_s   x8, ICH_ELSR_EL2
 
-       str     w4, [x3, #VGIC_V3_CPU_HCR]
        str     w5, [x3, #VGIC_V3_CPU_VMCR]
        str     w6, [x3, #VGIC_V3_CPU_MISR]
        str     w7, [x3, #VGIC_V3_CPU_EISR]
index 773d37a14039d9bb456f0896e022c4a3e1415773..9d84feb41a16601e411976aff3a2bc87a0d7dcbe 100644 (file)
@@ -4,3 +4,5 @@ obj-y                           := dma-mapping.o extable.o fault.o init.o \
                                   context.o proc.o pageattr.o
 obj-$(CONFIG_HUGETLB_PAGE)     += hugetlbpage.o
 obj-$(CONFIG_ARM64_PTDUMP)     += dump.o
+
+CFLAGS_mmu.o                   := -I$(srctree)/scripts/dtc/libfdt/
index 2560e1e1562e764f03fe8fa9925466f18653199d..bdeb5d38c2dd72c8510c7547b5cb2170f2fc5b33 100644 (file)
 #include <linux/init.h>
 #include <asm/assembler.h>
 #include <asm/cpufeature.h>
-#include <asm/alternative-asm.h>
+#include <asm/alternative.h>
 
 #include "proc-macros.S"
 
-/*
- *     __flush_dcache_all()
- *
- *     Flush the whole D-cache.
- *
- *     Corrupted registers: x0-x7, x9-x11
- */
-__flush_dcache_all:
-       dmb     sy                              // ensure ordering with previous memory accesses
-       mrs     x0, clidr_el1                   // read clidr
-       and     x3, x0, #0x7000000              // extract loc from clidr
-       lsr     x3, x3, #23                     // left align loc bit field
-       cbz     x3, finished                    // if loc is 0, then no need to clean
-       mov     x10, #0                         // start clean at cache level 0
-loop1:
-       add     x2, x10, x10, lsr #1            // work out 3x current cache level
-       lsr     x1, x0, x2                      // extract cache type bits from clidr
-       and     x1, x1, #7                      // mask of the bits for current cache only
-       cmp     x1, #2                          // see what cache we have at this level
-       b.lt    skip                            // skip if no cache, or just i-cache
-       save_and_disable_irqs x9                // make CSSELR and CCSIDR access atomic
-       msr     csselr_el1, x10                 // select current cache level in csselr
-       isb                                     // isb to sych the new cssr&csidr
-       mrs     x1, ccsidr_el1                  // read the new ccsidr
-       restore_irqs x9
-       and     x2, x1, #7                      // extract the length of the cache lines
-       add     x2, x2, #4                      // add 4 (line length offset)
-       mov     x4, #0x3ff
-       and     x4, x4, x1, lsr #3              // find maximum number on the way size
-       clz     w5, w4                          // find bit position of way size increment
-       mov     x7, #0x7fff
-       and     x7, x7, x1, lsr #13             // extract max number of the index size
-loop2:
-       mov     x9, x4                          // create working copy of max way size
-loop3:
-       lsl     x6, x9, x5
-       orr     x11, x10, x6                    // factor way and cache number into x11
-       lsl     x6, x7, x2
-       orr     x11, x11, x6                    // factor index number into x11
-       dc      cisw, x11                       // clean & invalidate by set/way
-       subs    x9, x9, #1                      // decrement the way
-       b.ge    loop3
-       subs    x7, x7, #1                      // decrement the index
-       b.ge    loop2
-skip:
-       add     x10, x10, #2                    // increment cache number
-       cmp     x3, x10
-       b.gt    loop1
-finished:
-       mov     x10, #0                         // swith back to cache level 0
-       msr     csselr_el1, x10                 // select current cache level in csselr
-       dsb     sy
-       isb
-       ret
-ENDPROC(__flush_dcache_all)
-
-/*
- *     flush_cache_all()
- *
- *     Flush the entire cache system.  The data cache flush is now achieved
- *     using atomic clean / invalidates working outwards from L1 cache. This
- *     is done using Set/Way based cache maintainance instructions.  The
- *     instruction cache can still be invalidated back to the point of
- *     unification in a single instruction.
- */
-ENTRY(flush_cache_all)
-       mov     x12, lr
-       bl      __flush_dcache_all
-       mov     x0, #0
-       ic      ialluis                         // I+BTB cache invalidate
-       ret     x12
-ENDPROC(flush_cache_all)
-
 /*
  *     flush_icache_range(start,end)
  *
index baa758d370210ef61f36942ab98633a80a324687..76c1e6cd36fc46d42da4c39155f02897d8462da1 100644 (file)
@@ -92,6 +92,14 @@ static void reset_context(void *info)
        unsigned int cpu = smp_processor_id();
        struct mm_struct *mm = current->active_mm;
 
+       /*
+        * current->active_mm could be init_mm for the idle thread immediately
+        * after secondary CPU boot or hotplug. TTBR0_EL1 is already set to
+        * the reserved value, so no need to reset any context.
+        */
+       if (mm == &init_mm)
+               return;
+
        smp_rmb();
        asid = cpu_last_asid + cpu;
 
index b0bd4e5fd5cf9c2599ddbc509bccb233838638a4..d16a1cead23f1c3b489d320bcb0ac7d1d461621d 100644 (file)
@@ -414,6 +414,98 @@ out:
        return -ENOMEM;
 }
 
+/********************************************
+ * The following APIs are for dummy DMA ops *
+ ********************************************/
+
+static void *__dummy_alloc(struct device *dev, size_t size,
+                          dma_addr_t *dma_handle, gfp_t flags,
+                          struct dma_attrs *attrs)
+{
+       return NULL;
+}
+
+static void __dummy_free(struct device *dev, size_t size,
+                        void *vaddr, dma_addr_t dma_handle,
+                        struct dma_attrs *attrs)
+{
+}
+
+static int __dummy_mmap(struct device *dev,
+                       struct vm_area_struct *vma,
+                       void *cpu_addr, dma_addr_t dma_addr, size_t size,
+                       struct dma_attrs *attrs)
+{
+       return -ENXIO;
+}
+
+static dma_addr_t __dummy_map_page(struct device *dev, struct page *page,
+                                  unsigned long offset, size_t size,
+                                  enum dma_data_direction dir,
+                                  struct dma_attrs *attrs)
+{
+       return DMA_ERROR_CODE;
+}
+
+static void __dummy_unmap_page(struct device *dev, dma_addr_t dev_addr,
+                              size_t size, enum dma_data_direction dir,
+                              struct dma_attrs *attrs)
+{
+}
+
+static int __dummy_map_sg(struct device *dev, struct scatterlist *sgl,
+                         int nelems, enum dma_data_direction dir,
+                         struct dma_attrs *attrs)
+{
+       return 0;
+}
+
+static void __dummy_unmap_sg(struct device *dev,
+                            struct scatterlist *sgl, int nelems,
+                            enum dma_data_direction dir,
+                            struct dma_attrs *attrs)
+{
+}
+
+static void __dummy_sync_single(struct device *dev,
+                               dma_addr_t dev_addr, size_t size,
+                               enum dma_data_direction dir)
+{
+}
+
+static void __dummy_sync_sg(struct device *dev,
+                           struct scatterlist *sgl, int nelems,
+                           enum dma_data_direction dir)
+{
+}
+
+static int __dummy_mapping_error(struct device *hwdev, dma_addr_t dma_addr)
+{
+       return 1;
+}
+
+static int __dummy_dma_supported(struct device *hwdev, u64 mask)
+{
+       return 0;
+}
+
+struct dma_map_ops dummy_dma_ops = {
+       .alloc                  = __dummy_alloc,
+       .free                   = __dummy_free,
+       .mmap                   = __dummy_mmap,
+       .map_page               = __dummy_map_page,
+       .unmap_page             = __dummy_unmap_page,
+       .map_sg                 = __dummy_map_sg,
+       .unmap_sg               = __dummy_unmap_sg,
+       .sync_single_for_cpu    = __dummy_sync_single,
+       .sync_single_for_device = __dummy_sync_single,
+       .sync_sg_for_cpu        = __dummy_sync_sg,
+       .sync_sg_for_device     = __dummy_sync_sg,
+       .mapping_error          = __dummy_mapping_error,
+       .dma_supported          = __dummy_dma_supported,
+};
+EXPORT_SYMBOL(dummy_dma_ops);
+
 static int __init arm64_dma_init(void)
 {
        int ret;
index 0948d327d013651c7b9978023139ea9cd89ecaeb..b1fc69cd14994a0d75b404c810e04814ee298340 100644 (file)
@@ -115,8 +115,7 @@ static void __do_user_fault(struct task_struct *tsk, unsigned long addr,
 {
        struct siginfo si;
 
-       if (show_unhandled_signals && unhandled_signal(tsk, sig) &&
-           printk_ratelimit()) {
+       if (show_unhandled_signals_ratelimited() && unhandled_signal(tsk, sig)) {
                pr_info("%s[%d]: unhandled %s (%d) at 0x%08lx, esr 0x%03x\n",
                        tsk->comm, task_pid_nr(tsk), fault_name(esr), sig,
                        addr, esr);
@@ -478,12 +477,19 @@ asmlinkage void __exception do_sp_pc_abort(unsigned long addr,
                                           struct pt_regs *regs)
 {
        struct siginfo info;
+       struct task_struct *tsk = current;
+
+       if (show_unhandled_signals && unhandled_signal(tsk, SIGBUS))
+               pr_info_ratelimited("%s[%d]: %s exception: pc=%p sp=%p\n",
+                                   tsk->comm, task_pid_nr(tsk),
+                                   esr_get_class_string(esr), (void *)regs->pc,
+                                   (void *)regs->sp);
 
        info.si_signo = SIGBUS;
        info.si_errno = 0;
        info.si_code  = BUS_ADRALN;
        info.si_addr  = (void __user *)addr;
-       arm64_notify_die("", regs, &info, esr);
+       arm64_notify_die("Oops - SP/PC alignment exception", regs, &info, esr);
 }
 
 static struct fault_info debug_fault_info[] = {
index b6f14e8d21213ed9e91d1521c9de4d3c21c86cae..4dfa3975ce5befb8d68b716c123cc00973d419e1 100644 (file)
@@ -102,7 +102,6 @@ EXPORT_SYMBOL(flush_dcache_page);
 /*
  * Additional functions defined in assembly.
  */
-EXPORT_SYMBOL(flush_cache_all);
 EXPORT_SYMBOL(flush_icache_range);
 
 #ifdef CONFIG_TRANSPARENT_HUGEPAGE
index 597831bdddf3db2c7d8f44e4d33fbc13bcb286cb..ad87ce826cce45ec0ac710c4dc1b721d8aaa8f97 100644 (file)
@@ -262,7 +262,7 @@ static void __init free_unused_memmap(void)
                 * memmap entries are valid from the bank end aligned to
                 * MAX_ORDER_NR_PAGES.
                 */
-               prev_end = ALIGN(start + __phys_to_pfn(reg->size),
+               prev_end = ALIGN(__phys_to_pfn(reg->base + reg->size),
                                 MAX_ORDER_NR_PAGES);
        }
 
index 5b8b664422d369a00dc6f6bc78b6f28ffd32da1b..82d3435bf14ffdf91fccb2e4857a1028c22750b6 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/kernel.h>
 #include <linux/errno.h>
 #include <linux/init.h>
+#include <linux/libfdt.h>
 #include <linux/mman.h>
 #include <linux/nodemask.h>
 #include <linux/memblock.h>
@@ -643,3 +644,68 @@ void __set_fixmap(enum fixed_addresses idx,
                flush_tlb_kernel_range(addr, addr+PAGE_SIZE);
        }
 }
+
+void *__init fixmap_remap_fdt(phys_addr_t dt_phys)
+{
+       const u64 dt_virt_base = __fix_to_virt(FIX_FDT);
+       pgprot_t prot = PAGE_KERNEL | PTE_RDONLY;
+       int granularity, size, offset;
+       void *dt_virt;
+
+       /*
+        * Check whether the physical FDT address is set and meets the minimum
+        * alignment requirement. Since we are relying on MIN_FDT_ALIGN to be
+        * at least 8 bytes so that we can always access the size field of the
+        * FDT header after mapping the first chunk, double check here if that
+        * is indeed the case.
+        */
+       BUILD_BUG_ON(MIN_FDT_ALIGN < 8);
+       if (!dt_phys || dt_phys % MIN_FDT_ALIGN)
+               return NULL;
+
+       /*
+        * Make sure that the FDT region can be mapped without the need to
+        * allocate additional translation table pages, so that it is safe
+        * to call create_mapping() this early.
+        *
+        * On 64k pages, the FDT will be mapped using PTEs, so we need to
+        * be in the same PMD as the rest of the fixmap.
+        * On 4k pages, we'll use section mappings for the FDT so we only
+        * have to be in the same PUD.
+        */
+       BUILD_BUG_ON(dt_virt_base % SZ_2M);
+
+       if (IS_ENABLED(CONFIG_ARM64_64K_PAGES)) {
+               BUILD_BUG_ON(__fix_to_virt(FIX_FDT_END) >> PMD_SHIFT !=
+                            __fix_to_virt(FIX_BTMAP_BEGIN) >> PMD_SHIFT);
+
+               granularity = PAGE_SIZE;
+       } else {
+               BUILD_BUG_ON(__fix_to_virt(FIX_FDT_END) >> PUD_SHIFT !=
+                            __fix_to_virt(FIX_BTMAP_BEGIN) >> PUD_SHIFT);
+
+               granularity = PMD_SIZE;
+       }
+
+       offset = dt_phys % granularity;
+       dt_virt = (void *)dt_virt_base + offset;
+
+       /* map the first chunk so we can read the size from the header */
+       create_mapping(round_down(dt_phys, granularity), dt_virt_base,
+                      granularity, prot);
+
+       if (fdt_check_header(dt_virt) != 0)
+               return NULL;
+
+       size = fdt_totalsize(dt_virt);
+       if (size > MAX_FDT_SIZE)
+               return NULL;
+
+       if (offset + size > granularity)
+               create_mapping(round_down(dt_phys, granularity), dt_virt_base,
+                              round_up(offset + size, granularity), prot);
+
+       memblock_reserve(dt_phys, size);
+
+       return dt_virt;
+}
index cdd754e19b9b26ddafbec1c83784ea151331eb43..39139a3aa16d8164af111f241fef099743273fb0 100644 (file)
 
 #define MAIR(attr, mt) ((attr) << ((mt) * 8))
 
-/*
- *     cpu_cache_off()
- *
- *     Turn the CPU D-cache off.
- */
-ENTRY(cpu_cache_off)
-       mrs     x0, sctlr_el1
-       bic     x0, x0, #1 << 2                 // clear SCTLR.C
-       msr     sctlr_el1, x0
-       isb
-       ret
-ENDPROC(cpu_cache_off)
-
-/*
- *     cpu_reset(loc)
- *
- *     Perform a soft reset of the system.  Put the CPU into the same state
- *     as it would be if it had been reset, and branch to what would be the
- *     reset vector. It must be executed with the flat identity mapping.
- *
- *     - loc   - location to jump to for soft reset
- */
-       .align  5
-ENTRY(cpu_reset)
-       mrs     x1, sctlr_el1
-       bic     x1, x1, #1
-       msr     sctlr_el1, x1                   // disable the MMU
-       isb
-       ret     x0
-ENDPROC(cpu_reset)
-
-ENTRY(cpu_soft_restart)
-       /* Save address of cpu_reset() and reset address */
-       mov     x19, x0
-       mov     x20, x1
-
-       /* Turn D-cache off */
-       bl      cpu_cache_off
-
-       /* Push out all dirty data, and ensure cache is empty */
-       bl      flush_cache_all
-
-       mov     x0, x20
-       ret     x19
-ENDPROC(cpu_soft_restart)
-
 /*
  *     cpu_do_idle()
  *
index 962a6aeab787827462312ddf92627c2f8e7083f1..366bbeaeb405d67daa35adac5ff22a4ecfc7e125 100644 (file)
@@ -70,8 +70,6 @@ extern unsigned long __cmpxchg_u64_unsupported_on_32bit_kernels(
    if something tries to do an invalid cmpxchg().  */
 extern void __cmpxchg_called_with_bad_pointer(void);
 
-#define __HAVE_ARCH_CMPXCHG 1
-
 static inline unsigned long __cmpxchg(volatile void *ptr, unsigned long old,
                                      unsigned long new, int size)
 {
index 4f5ec2bb71727279a952843051f44e3297c7b222..e998ff5d8e1a540aa8274e07c9bc8f7443ed7633 100644 (file)
@@ -296,6 +296,7 @@ extern void __iounmap(void __iomem *addr);
        __iounmap(addr)
 
 #define ioremap_wc ioremap_nocache
+#define ioremap_wt ioremap_nocache
 
 #define cached(addr) P1SEGADDR(addr)
 #define uncached(addr) P2SEGADDR(addr)
index 4e8ad0523118d631ea24f6b9f8fd1c3ffb123194..6abebe82d4e93ed0329f271cd54e2af5c1bc38d2 100644 (file)
@@ -10,6 +10,7 @@
 #include <linux/compiler.h>
 #include <linux/types.h>
 #include <asm/byteorder.h>
+#include <asm/def_LPBlackfin.h>
 
 #define __raw_readb bfin_read8
 #define __raw_readw bfin_read16
index 0b78bc89e8402f867fcdbb31e85ff2cffa693494..a31b63ec4930547d306a94bebcb28b9bce48364f 100644 (file)
@@ -17,6 +17,8 @@
 
 #ifdef __KERNEL__
 
+#define ARCH_HAS_IOREMAP_WT
+
 #include <linux/types.h>
 #include <asm/virtconvert.h>
 #include <asm/string.h>
@@ -265,7 +267,7 @@ static inline void __iomem *ioremap_nocache(unsigned long physaddr, unsigned lon
        return __ioremap(physaddr, size, IOMAP_NOCACHE_SER);
 }
 
-static inline void __iomem *ioremap_writethrough(unsigned long physaddr, unsigned long size)
+static inline void __iomem *ioremap_wt(unsigned long physaddr, unsigned long size)
 {
        return __ioremap(physaddr, size, IOMAP_WRITETHROUGH);
 }
index 2035a4d3f9b98c36a7463741a2fd19d0e038a803..a6d4ed042c709f6c4ec75ecf0ca0a7cac53a50a9 100644 (file)
@@ -41,16 +41,6 @@ extern void pci_free_consistent(struct pci_dev *hwdev, size_t size,
 /* Return the index of the PCI controller for device PDEV. */
 #define pci_controller_num(PDEV)       (0)
 
-#ifdef CONFIG_PCI
-static inline void pci_dma_burst_advice(struct pci_dev *pdev,
-                                       enum pci_dma_burst_strategy *strat,
-                                       unsigned long *strategy_parameter)
-{
-       *strat = PCI_DMA_BURST_INFINITY;
-       *strategy_parameter = ~0UL;
-}
-#endif
-
 /*
  *     These are pretty much arbitrary with the CoMEM implementation.
  *     We have the whole address space to ourselves.
index 9e7802911a57f22b01d9f6852d3c11b2f34c1869..a6e34e2acbbaf6cc07f377db35faa7c744ba23e3 100644 (file)
@@ -64,7 +64,6 @@ static inline unsigned long __xchg(unsigned long x, volatile void *ptr,
  *  looks just like atomic_cmpxchg on our arch currently with a bunch of
  *  variable casting.
  */
-#define __HAVE_ARCH_CMPXCHG 1
 
 #define cmpxchg(ptr, old, new)                                 \
 ({                                                             \
index 76d25b2cfbbe8fd44bc1386764e9bc4279f1f74c..42a91a7aa2b08fa3a9ba4f1de06e07fdb47bfa9d 100644 (file)
@@ -137,29 +137,6 @@ config AUDIT_ARCH
        bool
        default y
 
-menuconfig PARAVIRT_GUEST
-       bool "Paravirtualized guest support"
-       depends on BROKEN
-       help
-         Say Y here to get to see options related to running Linux under
-         various hypervisors.  This option alone does not add any kernel code.
-
-         If you say N, all options in this submenu will be skipped and disabled.
-
-if PARAVIRT_GUEST
-
-config PARAVIRT
-       bool "Enable paravirtualization code"
-       depends on PARAVIRT_GUEST
-       default y
-       help
-         This changes the kernel so it can modify itself when it is run
-         under a hypervisor, potentially improving performance significantly
-         over full virtualization.  However, when run without a hypervisor
-         the kernel is theoretically slower and slightly larger.
-
-endif
-
 choice
        prompt "System type"
        default IA64_GENERIC
index f6769eb2bbf9b5ff8774017ac0eb332fcc7309d9..843ba435e43bc285f20da4b5c47e262d7714525f 100644 (file)
@@ -77,12 +77,7 @@ do {                                                                 \
        ___p1;                                                          \
 })
 
-/*
- * XXX check on this ---I suspect what Linus really wants here is
- * acquire vs release semantics but we can't discuss this stuff with
- * Linus just yet.  Grrr...
- */
-#define set_mb(var, value)     do { (var) = (value); mb(); } while (0)
+#define smp_store_mb(var, value)       do { WRITE_ONCE(var, value); mb(); } while (0)
 
 /*
  * The group barrier in front of the rsm & ssm are necessary to ensure
index 668786e84af8ae1eee5f0ac97d8926889fc2cb29..74347ebf7d68ecab3df5bc014f4a9359897dca0a 100644 (file)
 #include <asm/ptrace.h>
 #include <asm/smp.h>
 
-#ifndef CONFIG_PARAVIRT
 typedef u8 ia64_vector;
-#else
-typedef u16 ia64_vector;
-#endif
 
 /*
  * 0 special
@@ -114,15 +110,11 @@ DECLARE_PER_CPU(int[IA64_NUM_VECTORS], vector_irq);
 
 extern struct irq_chip irq_type_ia64_lsapic;   /* CPU-internal interrupt controller */
 
-#ifdef CONFIG_PARAVIRT_GUEST
-#include <asm/paravirt.h>
-#else
 #define ia64_register_ipi      ia64_native_register_ipi
 #define assign_irq_vector      ia64_native_assign_irq_vector
 #define free_irq_vector                ia64_native_free_irq_vector
 #define register_percpu_irq    ia64_native_register_percpu_irq
 #define ia64_resend_irq                ia64_native_resend_irq
-#endif
 
 extern void ia64_native_register_ipi(void);
 extern int bind_irq_vector(int irq, int vector, cpumask_t domain);
index 20477ea111ba2f2e71271e48b826fb22fc5ef041..ec970a9201329a65bcbe17d6a98cb19a3826ddd1 100644 (file)
@@ -7,19 +7,6 @@
 #ifndef _ASM_IA64_INTRINSICS_H
 #define _ASM_IA64_INTRINSICS_H
 
-#include <asm/paravirt_privop.h>
 #include <uapi/asm/intrinsics.h>
 
-#ifndef __ASSEMBLY__
-#if defined(CONFIG_PARAVIRT)
-# undef IA64_INTRINSIC_API
-# undef IA64_INTRINSIC_MACRO
-# ifdef ASM_SUPPORTED
-#  define IA64_INTRINSIC_API(name)     paravirt_ ## name
-# else
-#  define IA64_INTRINSIC_API(name)     pv_cpu_ops.name
-# endif
-#define IA64_INTRINSIC_MACRO(name)     paravirt_ ## name
-#endif
-#endif /* !__ASSEMBLY__ */
 #endif /* _ASM_IA64_INTRINSICS_H */
index 94c89a2d97fe2f59421a101c64c587d7bde0c588..4ae1fbd7f10e54b65e20dc249c71062901a634af 100644 (file)
 
 #define NR_IOSAPICS                    256
 
-#ifdef CONFIG_PARAVIRT_GUEST
-#include <asm/paravirt.h>
-#else
 #define iosapic_pcat_compat_init       ia64_native_iosapic_pcat_compat_init
 #define __iosapic_read                 __ia64_native_iosapic_read
 #define __iosapic_write                        __ia64_native_iosapic_write
 #define iosapic_get_irq_chip           ia64_native_iosapic_get_irq_chip
-#endif
 
 extern void __init ia64_native_iosapic_pcat_compat_init(void);
 extern struct irq_chip *ia64_native_iosapic_get_irq_chip(unsigned long trigger);
index e3b3556e2e1bb8d32b67a1b63e4de3e2708af0b8..a8687b1d8906912ab458b9640db72c8655115a3f 100644 (file)
@@ -1,6 +1,4 @@
 #ifndef __IA64_INTR_REMAPPING_H
 #define __IA64_INTR_REMAPPING_H
 #define irq_remapping_enabled 0
-#define dmar_alloc_hwirq       create_irq
-#define dmar_free_hwirq                destroy_irq
 #endif
index dfba22a872c31c5739c05b79019d229f13f7eab8..f31894b2a35482dc5c37db58644b0ed811bc6c6f 100644 (file)
@@ -18,12 +18,6 @@ struct mod_arch_specific {
        struct elf64_shdr *got;         /* global offset table */
        struct elf64_shdr *opd;         /* official procedure descriptors */
        struct elf64_shdr *unwind;      /* unwind-table section */
-#ifdef CONFIG_PARAVIRT
-       struct elf64_shdr *paravirt_bundles;
-                                       /* paravirt_alt_bundle_patch table */
-       struct elf64_shdr *paravirt_insts;
-                                       /* paravirt_alt_inst_patch table */
-#endif
        unsigned long gp;               /* global-pointer for module */
 
        void *core_unw_table;           /* core unwind-table cookie returned by unwinder */
index d2d46efb3e6e83888e4f53910439b15794076785..7e08f17accd573b50a32f0070971c376f616e42e 100644 (file)
 
 #define DO_SAVE_MIN            IA64_NATIVE_DO_SAVE_MIN
 
-#define __paravirt_switch_to                   ia64_native_switch_to
-#define __paravirt_leave_syscall               ia64_native_leave_syscall
-#define __paravirt_work_processed_syscall      ia64_native_work_processed_syscall
-#define __paravirt_leave_kernel                        ia64_native_leave_kernel
-#define __paravirt_pending_syscall_end         ia64_work_pending_syscall_end
-#define __paravirt_work_processed_syscall_target \
-                                               ia64_work_processed_syscall
-
-#define paravirt_fsyscall_table                        ia64_native_fsyscall_table
-#define paravirt_fsys_bubble_down              ia64_native_fsys_bubble_down
-
-#ifdef CONFIG_PARAVIRT_GUEST_ASM_CLOBBER_CHECK
-# define PARAVIRT_POISON       0xdeadbeefbaadf00d
-# define CLOBBER(clob)                         \
-       ;;                                      \
-       movl clob = PARAVIRT_POISON;            \
-       ;;
-# define CLOBBER_PRED(pred_clob)               \
-       ;;                                      \
-       cmp.eq pred_clob, p0 = r0, r0           \
-       ;;
-#else
-# define CLOBBER(clob)                 /* nothing */
-# define CLOBBER_PRED(pred_clob)       /* nothing */
-#endif
-
 #define MOV_FROM_IFA(reg)      \
        mov reg = cr.ifa
 
        mov reg = cr.iip
 
 #define MOV_FROM_IVR(reg, clob)        \
-       mov reg = cr.ivr        \
-       CLOBBER(clob)
+       mov reg = cr.ivr
 
 #define MOV_FROM_PSR(pred, reg, clob)  \
-(pred) mov reg = psr                   \
-       CLOBBER(clob)
+(pred) mov reg = psr
 
 #define MOV_FROM_ITC(pred, pred_clob, reg, clob)       \
-(pred) mov reg = ar.itc                                \
-       CLOBBER(clob)                                   \
-       CLOBBER_PRED(pred_clob)
+(pred) mov reg = ar.itc
 
 #define MOV_TO_IFA(reg, clob)  \
-       mov cr.ifa = reg        \
-       CLOBBER(clob)
+       mov cr.ifa = reg
 
 #define MOV_TO_ITIR(pred, reg, clob)   \
-(pred) mov cr.itir = reg               \
-       CLOBBER(clob)
+(pred) mov cr.itir = reg
 
 #define MOV_TO_IHA(pred, reg, clob)    \
-(pred) mov cr.iha = reg                \
-       CLOBBER(clob)
+(pred) mov cr.iha = reg
 
 #define MOV_TO_IPSR(pred, reg, clob)           \
-(pred) mov cr.ipsr = reg                       \
-       CLOBBER(clob)
+(pred) mov cr.ipsr = reg
 
 #define MOV_TO_IFS(pred, reg, clob)    \
-(pred) mov cr.ifs = reg                \
-       CLOBBER(clob)
+(pred) mov cr.ifs = reg
 
 #define MOV_TO_IIP(reg, clob)  \
-       mov cr.iip = reg        \
-       CLOBBER(clob)
+       mov cr.iip = reg
 
 #define MOV_TO_KR(kr, reg, clob0, clob1)       \
-       mov IA64_KR(kr) = reg                   \
-       CLOBBER(clob0)                          \
-       CLOBBER(clob1)
+       mov IA64_KR(kr) = reg
 
 #define ITC_I(pred, reg, clob) \
-(pred) itc.i reg               \
-       CLOBBER(clob)
+(pred) itc.i reg
 
 #define ITC_D(pred, reg, clob) \
-(pred) itc.d reg               \
-       CLOBBER(clob)
+(pred) itc.d reg
 
 #define ITC_I_AND_D(pred_i, pred_d, reg, clob) \
 (pred_i) itc.i reg;                            \
-(pred_d) itc.d reg                             \
-       CLOBBER(clob)
+(pred_d) itc.d reg
 
 #define THASH(pred, reg0, reg1, clob)          \
-(pred) thash reg0 = reg1                       \
-       CLOBBER(clob)
+(pred) thash reg0 = reg1
 
 #define SSM_PSR_IC_AND_DEFAULT_BITS_AND_SRLZ_I(clob0, clob1)           \
        ssm psr.ic | PSR_DEFAULT_BITS                                   \
-       CLOBBER(clob0)                                                  \
-       CLOBBER(clob1)                                                  \
        ;;                                                              \
        srlz.i /* guarantee that interruption collectin is on */        \
        ;;
 
 #define SSM_PSR_IC_AND_SRLZ_D(clob0, clob1)    \
        ssm psr.ic                              \
-       CLOBBER(clob0)                          \
-       CLOBBER(clob1)                          \
        ;;                                      \
        srlz.d
 
 #define RSM_PSR_IC(clob)       \
-       rsm psr.ic              \
-       CLOBBER(clob)
+       rsm psr.ic
 
 #define SSM_PSR_I(pred, pred_clob, clob)       \
-(pred) ssm psr.i                               \
-       CLOBBER(clob)                           \
-       CLOBBER_PRED(pred_clob)
+(pred) ssm psr.i
 
 #define RSM_PSR_I(pred, clob0, clob1)  \
-(pred) rsm psr.i                       \
-       CLOBBER(clob0)                  \
-       CLOBBER(clob1)
+(pred) rsm psr.i
 
 #define RSM_PSR_I_IC(clob0, clob1, clob2)      \
-       rsm psr.i | psr.ic                      \
-       CLOBBER(clob0)                          \
-       CLOBBER(clob1)                          \
-       CLOBBER(clob2)
+       rsm psr.i | psr.ic
 
 #define RSM_PSR_DT             \
        rsm psr.dt
 
 #define RSM_PSR_BE_I(clob0, clob1)     \
-       rsm psr.be | psr.i              \
-       CLOBBER(clob0)                  \
-       CLOBBER(clob1)
+       rsm psr.be | psr.i
 
 #define SSM_PSR_DT_AND_SRLZ_I  \
        ssm psr.dt              \
        srlz.i
 
 #define BSW_0(clob0, clob1, clob2)     \
-       bsw.0                           \
-       CLOBBER(clob0)                  \
-       CLOBBER(clob1)                  \
-       CLOBBER(clob2)
+       bsw.0
 
 #define BSW_1(clob0, clob1)    \
-       bsw.1                   \
-       CLOBBER(clob0)          \
-       CLOBBER(clob1)
+       bsw.1
 
 #define COVER  \
        cover
diff --git a/arch/ia64/include/asm/native/pvchk_inst.h b/arch/ia64/include/asm/native/pvchk_inst.h
deleted file mode 100644 (file)
index 8d72962..0000000
+++ /dev/null
@@ -1,271 +0,0 @@
-#ifndef _ASM_NATIVE_PVCHK_INST_H
-#define _ASM_NATIVE_PVCHK_INST_H
-
-/******************************************************************************
- * arch/ia64/include/asm/native/pvchk_inst.h
- * Checker for paravirtualizations of privileged operations.
- *
- * Copyright (C) 2005 Hewlett-Packard Co
- *      Dan Magenheimer <dan.magenheimer@hp.com>
- *
- * Copyright (c) 2008 Isaku Yamahata <yamahata at valinux co jp>
- *                    VA Linux Systems Japan K.K.
- *
- * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- *
- */
-
-/**********************************************
- * Instructions paravirtualized for correctness
- **********************************************/
-
-/* "fc" and "thash" are privilege-sensitive instructions, meaning they
- *  may have different semantics depending on whether they are executed
- *  at PL0 vs PL!=0.  When paravirtualized, these instructions mustn't
- *  be allowed to execute directly, lest incorrect semantics result.
- */
-
-#define fc     .error "fc should not be used directly."
-#define thash  .error "thash should not be used directly."
-
-/* Note that "ttag" and "cover" are also privilege-sensitive; "ttag"
- * is not currently used (though it may be in a long-format VHPT system!)
- * and the semantics of cover only change if psr.ic is off which is very
- * rare (and currently non-existent outside of assembly code
- */
-#define ttag   .error "ttag should not be used directly."
-#define cover  .error "cover should not be used directly."
-
-/* There are also privilege-sensitive registers.  These registers are
- * readable at any privilege level but only writable at PL0.
- */
-#define cpuid  .error "cpuid should not be used directly."
-#define pmd    .error "pmd should not be used directly."
-
-/*
- * mov ar.eflag =
- * mov = ar.eflag
- */
-
-/**********************************************
- * Instructions paravirtualized for performance
- **********************************************/
-/*
- * Those instructions include '.' which can't be handled by cpp.
- * or can't be handled by cpp easily.
- * They are handled by sed instead of cpp.
- */
-
-/* for .S
- * itc.i
- * itc.d
- *
- * bsw.0
- * bsw.1
- *
- * ssm psr.ic | PSR_DEFAULT_BITS
- * ssm psr.ic
- * rsm psr.ic
- * ssm psr.i
- * rsm psr.i
- * rsm psr.i | psr.ic
- * rsm psr.dt
- * ssm psr.dt
- *
- * mov = cr.ifa
- * mov = cr.itir
- * mov = cr.isr
- * mov = cr.iha
- * mov = cr.ipsr
- * mov = cr.iim
- * mov = cr.iip
- * mov = cr.ivr
- * mov = psr
- *
- * mov cr.ifa =
- * mov cr.itir =
- * mov cr.iha =
- * mov cr.ipsr =
- * mov cr.ifs =
- * mov cr.iip =
- * mov cr.kr =
- */
-
-/* for intrinsics
- * ssm psr.i
- * rsm psr.i
- * mov = psr
- * mov = ivr
- * mov = tpr
- * mov cr.itm =
- * mov eoi =
- * mov rr[] =
- * mov = rr[]
- * mov = kr
- * mov kr =
- * ptc.ga
- */
-
-/*************************************************************
- * define paravirtualized instrcution macros as nop to ingore.
- * and check whether arguments are appropriate.
- *************************************************************/
-
-/* check whether reg is a regular register */
-.macro is_rreg_in reg
-       .ifc "\reg", "r0"
-               nop 0
-               .exitm
-       .endif
-       ;;
-       mov \reg = r0
-       ;;
-.endm
-#define IS_RREG_IN(reg)        is_rreg_in reg ;
-
-#define IS_RREG_OUT(reg)                       \
-       ;;                                      \
-       mov reg = r0                            \
-       ;;
-
-#define IS_RREG_CLOB(reg)      IS_RREG_OUT(reg)
-
-/* check whether pred is a predicate register */
-#define IS_PRED_IN(pred)                       \
-       ;;                                      \
-       (pred)  nop 0                           \
-       ;;
-
-#define IS_PRED_OUT(pred)                      \
-       ;;                                      \
-       cmp.eq pred, p0 = r0, r0                \
-       ;;
-
-#define IS_PRED_CLOB(pred)     IS_PRED_OUT(pred)
-
-
-#define DO_SAVE_MIN(__COVER, SAVE_IFS, EXTRA, WORKAROUND)      \
-       nop 0
-#define MOV_FROM_IFA(reg)                      \
-       IS_RREG_OUT(reg)
-#define MOV_FROM_ITIR(reg)                     \
-       IS_RREG_OUT(reg)
-#define MOV_FROM_ISR(reg)                      \
-       IS_RREG_OUT(reg)
-#define MOV_FROM_IHA(reg)                      \
-       IS_RREG_OUT(reg)
-#define MOV_FROM_IPSR(pred, reg)               \
-       IS_PRED_IN(pred)                        \
-       IS_RREG_OUT(reg)
-#define MOV_FROM_IIM(reg)                      \
-       IS_RREG_OUT(reg)
-#define MOV_FROM_IIP(reg)                      \
-       IS_RREG_OUT(reg)
-#define MOV_FROM_IVR(reg, clob)                        \
-       IS_RREG_OUT(reg)                        \
-       IS_RREG_CLOB(clob)
-#define MOV_FROM_PSR(pred, reg, clob)          \
-       IS_PRED_IN(pred)                        \
-       IS_RREG_OUT(reg)                        \
-       IS_RREG_CLOB(clob)
-#define MOV_FROM_ITC(pred, pred_clob, reg, clob)       \
-       IS_PRED_IN(pred)                                \
-       IS_PRED_CLOB(pred_clob)                         \
-       IS_RREG_OUT(reg)                                \
-       IS_RREG_CLOB(clob)
-#define MOV_TO_IFA(reg, clob)                  \
-       IS_RREG_IN(reg)                         \
-       IS_RREG_CLOB(clob)
-#define MOV_TO_ITIR(pred, reg, clob)           \
-       IS_PRED_IN(pred)                        \
-       IS_RREG_IN(reg)                         \
-       IS_RREG_CLOB(clob)
-#define MOV_TO_IHA(pred, reg, clob)            \
-       IS_PRED_IN(pred)                        \
-       IS_RREG_IN(reg)                         \
-       IS_RREG_CLOB(clob)
-#define MOV_TO_IPSR(pred, reg, clob)           \
-       IS_PRED_IN(pred)                        \
-       IS_RREG_IN(reg)                         \
-       IS_RREG_CLOB(clob)
-#define MOV_TO_IFS(pred, reg, clob)            \
-       IS_PRED_IN(pred)                        \
-       IS_RREG_IN(reg)                         \
-       IS_RREG_CLOB(clob)
-#define MOV_TO_IIP(reg, clob)                  \
-       IS_RREG_IN(reg)                         \
-       IS_RREG_CLOB(clob)
-#define MOV_TO_KR(kr, reg, clob0, clob1)       \
-       IS_RREG_IN(reg)                         \
-       IS_RREG_CLOB(clob0)                     \
-       IS_RREG_CLOB(clob1)
-#define ITC_I(pred, reg, clob)                 \
-       IS_PRED_IN(pred)                        \
-       IS_RREG_IN(reg)                         \
-       IS_RREG_CLOB(clob)
-#define ITC_D(pred, reg, clob)                 \
-       IS_PRED_IN(pred)                        \
-       IS_RREG_IN(reg)                         \
-       IS_RREG_CLOB(clob)
-#define ITC_I_AND_D(pred_i, pred_d, reg, clob) \
-       IS_PRED_IN(pred_i)                      \
-       IS_PRED_IN(pred_d)                      \
-       IS_RREG_IN(reg)                         \
-       IS_RREG_CLOB(clob)
-#define THASH(pred, reg0, reg1, clob)          \
-       IS_PRED_IN(pred)                        \
-       IS_RREG_OUT(reg0)                       \
-       IS_RREG_IN(reg1)                        \
-       IS_RREG_CLOB(clob)
-#define SSM_PSR_IC_AND_DEFAULT_BITS_AND_SRLZ_I(clob0, clob1)   \
-       IS_RREG_CLOB(clob0)                                     \
-       IS_RREG_CLOB(clob1)
-#define SSM_PSR_IC_AND_SRLZ_D(clob0, clob1)    \
-       IS_RREG_CLOB(clob0)                     \
-       IS_RREG_CLOB(clob1)
-#define RSM_PSR_IC(clob)                       \
-       IS_RREG_CLOB(clob)
-#define SSM_PSR_I(pred, pred_clob, clob)       \
-       IS_PRED_IN(pred)                        \
-       IS_PRED_CLOB(pred_clob)                 \
-       IS_RREG_CLOB(clob)
-#define RSM_PSR_I(pred, clob0, clob1)          \
-       IS_PRED_IN(pred)                        \
-       IS_RREG_CLOB(clob0)                     \
-       IS_RREG_CLOB(clob1)
-#define RSM_PSR_I_IC(clob0, clob1, clob2)      \
-       IS_RREG_CLOB(clob0)                     \
-       IS_RREG_CLOB(clob1)                     \
-       IS_RREG_CLOB(clob2)
-#define RSM_PSR_DT                             \
-       nop 0
-#define RSM_PSR_BE_I(clob0, clob1)             \
-       IS_RREG_CLOB(clob0)                     \
-       IS_RREG_CLOB(clob1)
-#define SSM_PSR_DT_AND_SRLZ_I                  \
-       nop 0
-#define BSW_0(clob0, clob1, clob2)             \
-       IS_RREG_CLOB(clob0)                     \
-       IS_RREG_CLOB(clob1)                     \
-       IS_RREG_CLOB(clob2)
-#define BSW_1(clob0, clob1)                    \
-       IS_RREG_CLOB(clob0)                     \
-       IS_RREG_CLOB(clob1)
-#define COVER                                  \
-       nop 0
-#define RFI                                    \
-       br.ret.sptk.many rp /* defining nop causes dependency error */
-
-#endif /* _ASM_NATIVE_PVCHK_INST_H */
diff --git a/arch/ia64/include/asm/paravirt.h b/arch/ia64/include/asm/paravirt.h
deleted file mode 100644 (file)
index b53518a..0000000
+++ /dev/null
@@ -1,321 +0,0 @@
-/******************************************************************************
- * Copyright (c) 2008 Isaku Yamahata <yamahata at valinux co jp>
- *                    VA Linux Systems Japan K.K.
- *
- * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- *
- */
-
-
-#ifndef __ASM_PARAVIRT_H
-#define __ASM_PARAVIRT_H
-
-#ifndef __ASSEMBLY__
-/******************************************************************************
- * fsys related addresses
- */
-struct pv_fsys_data {
-       unsigned long *fsyscall_table;
-       void *fsys_bubble_down;
-};
-
-extern struct pv_fsys_data pv_fsys_data;
-
-unsigned long *paravirt_get_fsyscall_table(void);
-char *paravirt_get_fsys_bubble_down(void);
-
-/******************************************************************************
- * patchlist addresses for gate page
- */
-enum pv_gate_patchlist {
-       PV_GATE_START_FSYSCALL,
-       PV_GATE_END_FSYSCALL,
-
-       PV_GATE_START_BRL_FSYS_BUBBLE_DOWN,
-       PV_GATE_END_BRL_FSYS_BUBBLE_DOWN,
-
-       PV_GATE_START_VTOP,
-       PV_GATE_END_VTOP,
-
-       PV_GATE_START_MCKINLEY_E9,
-       PV_GATE_END_MCKINLEY_E9,
-};
-
-struct pv_patchdata {
-       unsigned long start_fsyscall_patchlist;
-       unsigned long end_fsyscall_patchlist;
-       unsigned long start_brl_fsys_bubble_down_patchlist;
-       unsigned long end_brl_fsys_bubble_down_patchlist;
-       unsigned long start_vtop_patchlist;
-       unsigned long end_vtop_patchlist;
-       unsigned long start_mckinley_e9_patchlist;
-       unsigned long end_mckinley_e9_patchlist;
-
-       void *gate_section;
-};
-
-extern struct pv_patchdata pv_patchdata;
-
-unsigned long paravirt_get_gate_patchlist(enum pv_gate_patchlist type);
-void *paravirt_get_gate_section(void);
-#endif
-
-#ifdef CONFIG_PARAVIRT_GUEST
-
-#define PARAVIRT_HYPERVISOR_TYPE_DEFAULT       0
-
-#ifndef __ASSEMBLY__
-
-#include <asm/hw_irq.h>
-#include <asm/meminit.h>
-
-/******************************************************************************
- * general info
- */
-struct pv_info {
-       unsigned int kernel_rpl;
-       int paravirt_enabled;
-       const char *name;
-};
-
-extern struct pv_info pv_info;
-
-static inline int paravirt_enabled(void)
-{
-       return pv_info.paravirt_enabled;
-}
-
-static inline unsigned int get_kernel_rpl(void)
-{
-       return pv_info.kernel_rpl;
-}
-
-/******************************************************************************
- * initialization hooks.
- */
-struct rsvd_region;
-
-struct pv_init_ops {
-       void (*banner)(void);
-
-       int (*reserve_memory)(struct rsvd_region *region);
-
-       void (*arch_setup_early)(void);
-       void (*arch_setup_console)(char **cmdline_p);
-       int (*arch_setup_nomca)(void);
-
-       void (*post_smp_prepare_boot_cpu)(void);
-
-#ifdef ASM_SUPPORTED
-       unsigned long (*patch_bundle)(void *sbundle, void *ebundle,
-                                     unsigned long type);
-       unsigned long (*patch_inst)(unsigned long stag, unsigned long etag,
-                                   unsigned long type);
-#endif
-       void (*patch_branch)(unsigned long tag, unsigned long type);
-};
-
-extern struct pv_init_ops pv_init_ops;
-
-static inline void paravirt_banner(void)
-{
-       if (pv_init_ops.banner)
-               pv_init_ops.banner();
-}
-
-static inline int paravirt_reserve_memory(struct rsvd_region *region)
-{
-       if (pv_init_ops.reserve_memory)
-               return pv_init_ops.reserve_memory(region);
-       return 0;
-}
-
-static inline void paravirt_arch_setup_early(void)
-{
-       if (pv_init_ops.arch_setup_early)
-               pv_init_ops.arch_setup_early();
-}
-
-static inline void paravirt_arch_setup_console(char **cmdline_p)
-{
-       if (pv_init_ops.arch_setup_console)
-               pv_init_ops.arch_setup_console(cmdline_p);
-}
-
-static inline int paravirt_arch_setup_nomca(void)
-{
-       if (pv_init_ops.arch_setup_nomca)
-               return pv_init_ops.arch_setup_nomca();
-       return 0;
-}
-
-static inline void paravirt_post_smp_prepare_boot_cpu(void)
-{
-       if (pv_init_ops.post_smp_prepare_boot_cpu)
-               pv_init_ops.post_smp_prepare_boot_cpu();
-}
-
-/******************************************************************************
- * replacement of iosapic operations.
- */
-
-struct pv_iosapic_ops {
-       void (*pcat_compat_init)(void);
-
-       struct irq_chip *(*__get_irq_chip)(unsigned long trigger);
-
-       unsigned int (*__read)(char __iomem *iosapic, unsigned int reg);
-       void (*__write)(char __iomem *iosapic, unsigned int reg, u32 val);
-};
-
-extern struct pv_iosapic_ops pv_iosapic_ops;
-
-static inline void
-iosapic_pcat_compat_init(void)
-{
-       if (pv_iosapic_ops.pcat_compat_init)
-               pv_iosapic_ops.pcat_compat_init();
-}
-
-static inline struct irq_chip*
-iosapic_get_irq_chip(unsigned long trigger)
-{
-       return pv_iosapic_ops.__get_irq_chip(trigger);
-}
-
-static inline unsigned int
-__iosapic_read(char __iomem *iosapic, unsigned int reg)
-{
-       return pv_iosapic_ops.__read(iosapic, reg);
-}
-
-static inline void
-__iosapic_write(char __iomem *iosapic, unsigned int reg, u32 val)
-{
-       return pv_iosapic_ops.__write(iosapic, reg, val);
-}
-
-/******************************************************************************
- * replacement of irq operations.
- */
-
-struct pv_irq_ops {
-       void (*register_ipi)(void);
-
-       int (*assign_irq_vector)(int irq);
-       void (*free_irq_vector)(int vector);
-
-       void (*register_percpu_irq)(ia64_vector vec,
-                                   struct irqaction *action);
-
-       void (*resend_irq)(unsigned int vector);
-};
-
-extern struct pv_irq_ops pv_irq_ops;
-
-static inline void
-ia64_register_ipi(void)
-{
-       pv_irq_ops.register_ipi();
-}
-
-static inline int
-assign_irq_vector(int irq)
-{
-       return pv_irq_ops.assign_irq_vector(irq);
-}
-
-static inline void
-free_irq_vector(int vector)
-{
-       return pv_irq_ops.free_irq_vector(vector);
-}
-
-static inline void
-register_percpu_irq(ia64_vector vec, struct irqaction *action)
-{
-       pv_irq_ops.register_percpu_irq(vec, action);
-}
-
-static inline void
-ia64_resend_irq(unsigned int vector)
-{
-       pv_irq_ops.resend_irq(vector);
-}
-
-/******************************************************************************
- * replacement of time operations.
- */
-
-extern struct itc_jitter_data_t itc_jitter_data;
-extern volatile int time_keeper_id;
-
-struct pv_time_ops {
-       void (*init_missing_ticks_accounting)(int cpu);
-       int (*do_steal_accounting)(unsigned long *new_itm);
-
-       void (*clocksource_resume)(void);
-
-       unsigned long long (*sched_clock)(void);
-};
-
-extern struct pv_time_ops pv_time_ops;
-
-static inline void
-paravirt_init_missing_ticks_accounting(int cpu)
-{
-       if (pv_time_ops.init_missing_ticks_accounting)
-               pv_time_ops.init_missing_ticks_accounting(cpu);
-}
-
-struct static_key;
-extern struct static_key paravirt_steal_enabled;
-extern struct static_key paravirt_steal_rq_enabled;
-
-static inline int
-paravirt_do_steal_accounting(unsigned long *new_itm)
-{
-       return pv_time_ops.do_steal_accounting(new_itm);
-}
-
-static inline unsigned long long paravirt_sched_clock(void)
-{
-       return pv_time_ops.sched_clock();
-}
-
-#endif /* !__ASSEMBLY__ */
-
-#else
-/* fallback for native case */
-
-#ifndef __ASSEMBLY__
-
-#define paravirt_banner()                              do { } while (0)
-#define paravirt_reserve_memory(region)                        0
-
-#define paravirt_arch_setup_early()                    do { } while (0)
-#define paravirt_arch_setup_console(cmdline_p)         do { } while (0)
-#define paravirt_arch_setup_nomca()                    0
-#define paravirt_post_smp_prepare_boot_cpu()           do { } while (0)
-
-#define paravirt_init_missing_ticks_accounting(cpu)    do { } while (0)
-#define paravirt_do_steal_accounting(new_itm)          0
-
-#endif /* __ASSEMBLY__ */
-
-
-#endif /* CONFIG_PARAVIRT_GUEST */
-
-#endif /* __ASM_PARAVIRT_H */
diff --git a/arch/ia64/include/asm/paravirt_patch.h b/arch/ia64/include/asm/paravirt_patch.h
deleted file mode 100644 (file)
index 128ff5d..0000000
+++ /dev/null
@@ -1,143 +0,0 @@
-/******************************************************************************
- * Copyright (c) 2008 Isaku Yamahata <yamahata at valinux co jp>
- *                    VA Linux Systems Japan K.K.
- *
- * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- *
- */
-
-#ifndef __ASM_PARAVIRT_PATCH_H
-#define __ASM_PARAVIRT_PATCH_H
-
-#ifdef __ASSEMBLY__
-
-       .section .paravirt_branches, "a"
-       .previous
-#define PARAVIRT_PATCH_SITE_BR(type)           \
-       {                                       \
-       [1:] ;                                  \
-       br.cond.sptk.many 2f ;                  \
-       nop.b 0 ;                               \
-       nop.b 0;; ;                             \
-       } ;                                     \
-       2:                                      \
-       .xdata8 ".paravirt_branches", 1b, type
-
-#else
-
-#include <linux/stringify.h>
-#include <asm/intrinsics.h>
-
-/* for binary patch */
-struct paravirt_patch_site_bundle {
-       void            *sbundle;
-       void            *ebundle;
-       unsigned long   type;
-};
-
-/* label means the beginning of new bundle */
-#define paravirt_alt_bundle(instr, privop)                             \
-       "\t998:\n"                                                      \
-       "\t" instr "\n"                                                 \
-       "\t999:\n"                                                      \
-       "\t.pushsection .paravirt_bundles, \"a\"\n"                     \
-       "\t.popsection\n"                                               \
-       "\t.xdata8 \".paravirt_bundles\", 998b, 999b, "                 \
-       __stringify(privop) "\n"
-
-
-struct paravirt_patch_bundle_elem {
-       const void      *sbundle;
-       const void      *ebundle;
-       unsigned long   type;
-};
-
-
-struct paravirt_patch_site_inst {
-       unsigned long   stag;
-       unsigned long   etag;
-       unsigned long   type;
-};
-
-#define paravirt_alt_inst(instr, privop)                               \
-       "\t[998:]\n"                                                    \
-       "\t" instr "\n"                                                 \
-       "\t[999:]\n"                                                    \
-       "\t.pushsection .paravirt_insts, \"a\"\n"                       \
-       "\t.popsection\n"                                               \
-       "\t.xdata8 \".paravirt_insts\", 998b, 999b, "                   \
-       __stringify(privop) "\n"
-
-struct paravirt_patch_site_branch {
-       unsigned long   tag;
-       unsigned long   type;
-};
-
-struct paravirt_patch_branch_target {
-       const void      *entry;
-       unsigned long   type;
-};
-
-void
-__paravirt_patch_apply_branch(
-       unsigned long tag, unsigned long type,
-       const struct paravirt_patch_branch_target *entries,
-       unsigned int nr_entries);
-
-void
-paravirt_patch_reloc_br(unsigned long tag, const void *target);
-
-void
-paravirt_patch_reloc_brl(unsigned long tag, const void *target);
-
-
-#if defined(ASM_SUPPORTED) && defined(CONFIG_PARAVIRT)
-unsigned long
-ia64_native_patch_bundle(void *sbundle, void *ebundle, unsigned long type);
-
-unsigned long
-__paravirt_patch_apply_bundle(void *sbundle, void *ebundle, unsigned long type,
-                             const struct paravirt_patch_bundle_elem *elems,
-                             unsigned long nelems,
-                             const struct paravirt_patch_bundle_elem **found);
-
-void
-paravirt_patch_apply_bundle(const struct paravirt_patch_site_bundle *start,
-                           const struct paravirt_patch_site_bundle *end);
-
-void
-paravirt_patch_apply_inst(const struct paravirt_patch_site_inst *start,
-                         const struct paravirt_patch_site_inst *end);
-
-void paravirt_patch_apply(void);
-#else
-#define paravirt_patch_apply_bundle(start, end)        do { } while (0)
-#define paravirt_patch_apply_inst(start, end)  do { } while (0)
-#define paravirt_patch_apply()                 do { } while (0)
-#endif
-
-#endif /* !__ASSEMBLEY__ */
-
-#endif /* __ASM_PARAVIRT_PATCH_H */
-
-/*
- * Local variables:
- * mode: C
- * c-set-style: "linux"
- * c-basic-offset: 8
- * tab-width: 8
- * indent-tabs-mode: t
- * End:
- */
diff --git a/arch/ia64/include/asm/paravirt_privop.h b/arch/ia64/include/asm/paravirt_privop.h
deleted file mode 100644 (file)
index 8f6cb11..0000000
+++ /dev/null
@@ -1,479 +0,0 @@
-/******************************************************************************
- * Copyright (c) 2008 Isaku Yamahata <yamahata at valinux co jp>
- *                    VA Linux Systems Japan K.K.
- *
- * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- *
- */
-
-#ifndef _ASM_IA64_PARAVIRT_PRIVOP_H
-#define _ASM_IA64_PARAVIRT_PRIVOP_H
-
-#ifdef CONFIG_PARAVIRT
-
-#ifndef __ASSEMBLY__
-
-#include <linux/types.h>
-#include <asm/kregs.h> /* for IA64_PSR_I */
-
-/******************************************************************************
- * replacement of intrinsics operations.
- */
-
-struct pv_cpu_ops {
-       void (*fc)(void *addr);
-       unsigned long (*thash)(unsigned long addr);
-       unsigned long (*get_cpuid)(int index);
-       unsigned long (*get_pmd)(int index);
-       unsigned long (*getreg)(int reg);
-       void (*setreg)(int reg, unsigned long val);
-       void (*ptcga)(unsigned long addr, unsigned long size);
-       unsigned long (*get_rr)(unsigned long index);
-       void (*set_rr)(unsigned long index, unsigned long val);
-       void (*set_rr0_to_rr4)(unsigned long val0, unsigned long val1,
-                              unsigned long val2, unsigned long val3,
-                              unsigned long val4);
-       void (*ssm_i)(void);
-       void (*rsm_i)(void);
-       unsigned long (*get_psr_i)(void);
-       void (*intrin_local_irq_restore)(unsigned long flags);
-};
-
-extern struct pv_cpu_ops pv_cpu_ops;
-
-extern void ia64_native_setreg_func(int regnum, unsigned long val);
-extern unsigned long ia64_native_getreg_func(int regnum);
-
-/************************************************/
-/* Instructions paravirtualized for performance */
-/************************************************/
-
-#ifndef ASM_SUPPORTED
-#define paravirt_ssm_i()       pv_cpu_ops.ssm_i()
-#define paravirt_rsm_i()       pv_cpu_ops.rsm_i()
-#define __paravirt_getreg()    pv_cpu_ops.getreg()
-#endif
-
-/* mask for ia64_native_ssm/rsm() must be constant.("i" constraing).
- * static inline function doesn't satisfy it. */
-#define paravirt_ssm(mask)                     \
-       do {                                    \
-               if ((mask) == IA64_PSR_I)       \
-                       paravirt_ssm_i();       \
-               else                            \
-                       ia64_native_ssm(mask);  \
-       } while (0)
-
-#define paravirt_rsm(mask)                     \
-       do {                                    \
-               if ((mask) == IA64_PSR_I)       \
-                       paravirt_rsm_i();       \
-               else                            \
-                       ia64_native_rsm(mask);  \
-       } while (0)
-
-/* returned ip value should be the one in the caller,
- * not in __paravirt_getreg() */
-#define paravirt_getreg(reg)                                   \
-       ({                                                      \
-               unsigned long res;                              \
-               if ((reg) == _IA64_REG_IP)                      \
-                       res = ia64_native_getreg(_IA64_REG_IP); \
-               else                                            \
-                       res = __paravirt_getreg(reg);           \
-               res;                                            \
-       })
-
-/******************************************************************************
- * replacement of hand written assembly codes.
- */
-struct pv_cpu_asm_switch {
-       unsigned long switch_to;
-       unsigned long leave_syscall;
-       unsigned long work_processed_syscall;
-       unsigned long leave_kernel;
-};
-void paravirt_cpu_asm_init(const struct pv_cpu_asm_switch *cpu_asm_switch);
-
-#endif /* __ASSEMBLY__ */
-
-#define IA64_PARAVIRT_ASM_FUNC(name)   paravirt_ ## name
-
-#else
-
-/* fallback for native case */
-#define IA64_PARAVIRT_ASM_FUNC(name)   ia64_native_ ## name
-
-#endif /* CONFIG_PARAVIRT */
-
-#if defined(CONFIG_PARAVIRT) && defined(ASM_SUPPORTED)
-#define paravirt_dv_serialize_data()   ia64_dv_serialize_data()
-#else
-#define paravirt_dv_serialize_data()   /* nothing */
-#endif
-
-/* these routines utilize privilege-sensitive or performance-sensitive
- * privileged instructions so the code must be replaced with
- * paravirtualized versions */
-#define ia64_switch_to                 IA64_PARAVIRT_ASM_FUNC(switch_to)
-#define ia64_leave_syscall             IA64_PARAVIRT_ASM_FUNC(leave_syscall)
-#define ia64_work_processed_syscall    \
-       IA64_PARAVIRT_ASM_FUNC(work_processed_syscall)
-#define ia64_leave_kernel              IA64_PARAVIRT_ASM_FUNC(leave_kernel)
-
-
-#if defined(CONFIG_PARAVIRT)
-/******************************************************************************
- * binary patching infrastructure
- */
-#define PARAVIRT_PATCH_TYPE_FC                         1
-#define PARAVIRT_PATCH_TYPE_THASH                      2
-#define PARAVIRT_PATCH_TYPE_GET_CPUID                  3
-#define PARAVIRT_PATCH_TYPE_GET_PMD                    4
-#define PARAVIRT_PATCH_TYPE_PTCGA                      5
-#define PARAVIRT_PATCH_TYPE_GET_RR                     6
-#define PARAVIRT_PATCH_TYPE_SET_RR                     7
-#define PARAVIRT_PATCH_TYPE_SET_RR0_TO_RR4             8
-#define PARAVIRT_PATCH_TYPE_SSM_I                      9
-#define PARAVIRT_PATCH_TYPE_RSM_I                      10
-#define PARAVIRT_PATCH_TYPE_GET_PSR_I                  11
-#define PARAVIRT_PATCH_TYPE_INTRIN_LOCAL_IRQ_RESTORE   12
-
-/* PARAVIRT_PATY_TYPE_[GS]ETREG + _IA64_REG_xxx */
-#define PARAVIRT_PATCH_TYPE_GETREG                     0x10000000
-#define PARAVIRT_PATCH_TYPE_SETREG                     0x20000000
-
-/*
- * struct task_struct* (*ia64_switch_to)(void* next_task);
- * void *ia64_leave_syscall;
- * void *ia64_work_processed_syscall
- * void *ia64_leave_kernel;
- */
-
-#define PARAVIRT_PATCH_TYPE_BR_START                   0x30000000
-#define PARAVIRT_PATCH_TYPE_BR_SWITCH_TO               \
-       (PARAVIRT_PATCH_TYPE_BR_START + 0)
-#define PARAVIRT_PATCH_TYPE_BR_LEAVE_SYSCALL           \
-       (PARAVIRT_PATCH_TYPE_BR_START + 1)
-#define PARAVIRT_PATCH_TYPE_BR_WORK_PROCESSED_SYSCALL  \
-       (PARAVIRT_PATCH_TYPE_BR_START + 2)
-#define PARAVIRT_PATCH_TYPE_BR_LEAVE_KERNEL            \
-       (PARAVIRT_PATCH_TYPE_BR_START + 3)
-
-#ifdef ASM_SUPPORTED
-#include <asm/paravirt_patch.h>
-
-/*
- * pv_cpu_ops calling stub.
- * normal function call convension can't be written by gcc
- * inline assembly.
- *
- * from the caller's point of view,
- * the following registers will be clobbered.
- * r2, r3
- * r8-r15
- * r16, r17
- * b6, b7
- * p6-p15
- * ar.ccv
- *
- * from the callee's point of view ,
- * the following registers can be used.
- * r2, r3: scratch
- * r8: scratch, input argument0 and return value
- * r0-r15: scratch, input argument1-5
- * b6: return pointer
- * b7: scratch
- * p6-p15: scratch
- * ar.ccv: scratch
- *
- * other registers must not be changed. especially
- * b0: rp: preserved. gcc ignores b0 in clobbered register.
- * r16: saved gp
- */
-/* 5 bundles */
-#define __PARAVIRT_BR                                                  \
-       ";;\n"                                                          \
-       "{ .mlx\n"                                                      \
-       "nop 0\n"                                                       \
-       "movl r2 = %[op_addr]\n"/* get function pointer address */      \
-       ";;\n"                                                          \
-       "}\n"                                                           \
-       "1:\n"                                                          \
-       "{ .mii\n"                                                      \
-       "ld8 r2 = [r2]\n"       /* load function descriptor address */  \
-       "mov r17 = ip\n"        /* get ip to calc return address */     \
-       "mov r16 = gp\n"        /* save gp */                           \
-       ";;\n"                                                          \
-       "}\n"                                                           \
-       "{ .mii\n"                                                      \
-       "ld8 r3 = [r2], 8\n"    /* load entry address */                \
-       "adds r17 =  1f - 1b, r17\n"    /* calculate return address */  \
-       ";;\n"                                                          \
-       "mov b7 = r3\n"         /* set entry address */                 \
-       "}\n"                                                           \
-       "{ .mib\n"                                                      \
-       "ld8 gp = [r2]\n"       /* load gp value */                     \
-       "mov b6 = r17\n"        /* set return address */                \
-       "br.cond.sptk.few b7\n" /* intrinsics are very short isns */    \
-       "}\n"                                                           \
-       "1:\n"                                                          \
-       "{ .mii\n"                                                      \
-       "mov gp = r16\n"        /* restore gp value */                  \
-       "nop 0\n"                                                       \
-       "nop 0\n"                                                       \
-       ";;\n"                                                          \
-       "}\n"
-
-#define PARAVIRT_OP(op)                                \
-       [op_addr] "i"(&pv_cpu_ops.op)
-
-#define PARAVIRT_TYPE(type)                    \
-       PARAVIRT_PATCH_TYPE_ ## type
-
-#define PARAVIRT_REG_CLOBBERS0                                 \
-       "r2", "r3", /*"r8",*/ "r9", "r10", "r11", "r14",        \
-               "r15", "r16", "r17"
-
-#define PARAVIRT_REG_CLOBBERS1                                 \
-       "r2","r3", /*"r8",*/ "r9", "r10", "r11", "r14",         \
-               "r15", "r16", "r17"
-
-#define PARAVIRT_REG_CLOBBERS2                                 \
-       "r2", "r3", /*"r8", "r9",*/ "r10", "r11", "r14",        \
-               "r15", "r16", "r17"
-
-#define PARAVIRT_REG_CLOBBERS5                                 \
-       "r2", "r3", /*"r8", "r9", "r10", "r11", "r14",*/        \
-               "r15", "r16", "r17"
-
-#define PARAVIRT_BR_CLOBBERS                   \
-       "b6", "b7"
-
-#define PARAVIRT_PR_CLOBBERS                                           \
-       "p6", "p7", "p8", "p9", "p10", "p11", "p12", "p13", "p14", "p15"
-
-#define PARAVIRT_AR_CLOBBERS                   \
-       "ar.ccv"
-
-#define PARAVIRT_CLOBBERS0                     \
-               PARAVIRT_REG_CLOBBERS0,         \
-               PARAVIRT_BR_CLOBBERS,           \
-               PARAVIRT_PR_CLOBBERS,           \
-               PARAVIRT_AR_CLOBBERS,           \
-               "memory"
-
-#define PARAVIRT_CLOBBERS1                     \
-               PARAVIRT_REG_CLOBBERS1,         \
-               PARAVIRT_BR_CLOBBERS,           \
-               PARAVIRT_PR_CLOBBERS,           \
-               PARAVIRT_AR_CLOBBERS,           \
-               "memory"
-
-#define PARAVIRT_CLOBBERS2                     \
-               PARAVIRT_REG_CLOBBERS2,         \
-               PARAVIRT_BR_CLOBBERS,           \
-               PARAVIRT_PR_CLOBBERS,           \
-               PARAVIRT_AR_CLOBBERS,           \
-               "memory"
-
-#define PARAVIRT_CLOBBERS5                     \
-               PARAVIRT_REG_CLOBBERS5,         \
-               PARAVIRT_BR_CLOBBERS,           \
-               PARAVIRT_PR_CLOBBERS,           \
-               PARAVIRT_AR_CLOBBERS,           \
-               "memory"
-
-#define PARAVIRT_BR0(op, type)                                 \
-       register unsigned long ia64_clobber asm ("r8");         \
-       asm volatile (paravirt_alt_bundle(__PARAVIRT_BR,        \
-                                         PARAVIRT_TYPE(type))  \
-                     : "=r"(ia64_clobber)                      \
-                     : PARAVIRT_OP(op)                         \
-                     : PARAVIRT_CLOBBERS0)
-
-#define PARAVIRT_BR0_RET(op, type)                             \
-       register unsigned long ia64_intri_res asm ("r8");       \
-       asm volatile (paravirt_alt_bundle(__PARAVIRT_BR,        \
-                                         PARAVIRT_TYPE(type))  \
-                     : "=r"(ia64_intri_res)                    \
-                     : PARAVIRT_OP(op)                         \
-                     : PARAVIRT_CLOBBERS0)
-
-#define PARAVIRT_BR1(op, type, arg1)                           \
-       register unsigned long __##arg1 asm ("r8") = arg1;      \
-       register unsigned long ia64_clobber asm ("r8");         \
-       asm volatile (paravirt_alt_bundle(__PARAVIRT_BR,        \
-                                         PARAVIRT_TYPE(type))  \
-                     : "=r"(ia64_clobber)                      \
-                     : PARAVIRT_OP(op), "0"(__##arg1)          \
-                     : PARAVIRT_CLOBBERS1)
-
-#define PARAVIRT_BR1_RET(op, type, arg1)                       \
-       register unsigned long ia64_intri_res asm ("r8");       \
-       register unsigned long __##arg1 asm ("r8") = arg1;      \
-       asm volatile (paravirt_alt_bundle(__PARAVIRT_BR,        \
-                                         PARAVIRT_TYPE(type))  \
-                     : "=r"(ia64_intri_res)                    \
-                     : PARAVIRT_OP(op), "0"(__##arg1)          \
-                     : PARAVIRT_CLOBBERS1)
-
-#define PARAVIRT_BR1_VOID(op, type, arg1)                      \
-       register void *__##arg1 asm ("r8") = arg1;              \
-       register unsigned long ia64_clobber asm ("r8");         \
-       asm volatile (paravirt_alt_bundle(__PARAVIRT_BR,        \
-                                         PARAVIRT_TYPE(type))  \
-                     : "=r"(ia64_clobber)                      \
-                     : PARAVIRT_OP(op), "0"(__##arg1)          \
-                     : PARAVIRT_CLOBBERS1)
-
-#define PARAVIRT_BR2(op, type, arg1, arg2)                             \
-       register unsigned long __##arg1 asm ("r8") = arg1;              \
-       register unsigned long __##arg2 asm ("r9") = arg2;              \
-       register unsigned long ia64_clobber1 asm ("r8");                \
-       register unsigned long ia64_clobber2 asm ("r9");                \
-       asm volatile (paravirt_alt_bundle(__PARAVIRT_BR,                \
-                                         PARAVIRT_TYPE(type))          \
-                     : "=r"(ia64_clobber1), "=r"(ia64_clobber2)        \
-                     : PARAVIRT_OP(op), "0"(__##arg1), "1"(__##arg2)   \
-                     : PARAVIRT_CLOBBERS2)
-
-
-#define PARAVIRT_DEFINE_CPU_OP0(op, type)              \
-       static inline void                              \
-       paravirt_ ## op (void)                          \
-       {                                               \
-               PARAVIRT_BR0(op, type);                 \
-       }
-
-#define PARAVIRT_DEFINE_CPU_OP0_RET(op, type)          \
-       static inline unsigned long                     \
-       paravirt_ ## op (void)                          \
-       {                                               \
-               PARAVIRT_BR0_RET(op, type);             \
-               return ia64_intri_res;                  \
-       }
-
-#define PARAVIRT_DEFINE_CPU_OP1_VOID(op, type)         \
-       static inline void                              \
-       paravirt_ ## op (void *arg1)                    \
-       {                                               \
-               PARAVIRT_BR1_VOID(op, type, arg1);      \
-       }
-
-#define PARAVIRT_DEFINE_CPU_OP1(op, type)              \
-       static inline void                              \
-       paravirt_ ## op (unsigned long arg1)            \
-       {                                               \
-               PARAVIRT_BR1(op, type, arg1);           \
-       }
-
-#define PARAVIRT_DEFINE_CPU_OP1_RET(op, type)          \
-       static inline unsigned long                     \
-       paravirt_ ## op (unsigned long arg1)            \
-       {                                               \
-               PARAVIRT_BR1_RET(op, type, arg1);       \
-               return ia64_intri_res;                  \
-       }
-
-#define PARAVIRT_DEFINE_CPU_OP2(op, type)              \
-       static inline void                              \
-       paravirt_ ## op (unsigned long arg1,            \
-                        unsigned long arg2)            \
-       {                                               \
-               PARAVIRT_BR2(op, type, arg1, arg2);     \
-       }
-
-
-PARAVIRT_DEFINE_CPU_OP1_VOID(fc, FC);
-PARAVIRT_DEFINE_CPU_OP1_RET(thash, THASH)
-PARAVIRT_DEFINE_CPU_OP1_RET(get_cpuid, GET_CPUID)
-PARAVIRT_DEFINE_CPU_OP1_RET(get_pmd, GET_PMD)
-PARAVIRT_DEFINE_CPU_OP2(ptcga, PTCGA)
-PARAVIRT_DEFINE_CPU_OP1_RET(get_rr, GET_RR)
-PARAVIRT_DEFINE_CPU_OP2(set_rr, SET_RR)
-PARAVIRT_DEFINE_CPU_OP0(ssm_i, SSM_I)
-PARAVIRT_DEFINE_CPU_OP0(rsm_i, RSM_I)
-PARAVIRT_DEFINE_CPU_OP0_RET(get_psr_i, GET_PSR_I)
-PARAVIRT_DEFINE_CPU_OP1(intrin_local_irq_restore, INTRIN_LOCAL_IRQ_RESTORE)
-
-static inline void
-paravirt_set_rr0_to_rr4(unsigned long val0, unsigned long val1,
-                       unsigned long val2, unsigned long val3,
-                       unsigned long val4)
-{
-       register unsigned long __val0 asm ("r8") = val0;
-       register unsigned long __val1 asm ("r9") = val1;
-       register unsigned long __val2 asm ("r10") = val2;
-       register unsigned long __val3 asm ("r11") = val3;
-       register unsigned long __val4 asm ("r14") = val4;
-
-       register unsigned long ia64_clobber0 asm ("r8");
-       register unsigned long ia64_clobber1 asm ("r9");
-       register unsigned long ia64_clobber2 asm ("r10");
-       register unsigned long ia64_clobber3 asm ("r11");
-       register unsigned long ia64_clobber4 asm ("r14");
-
-       asm volatile (paravirt_alt_bundle(__PARAVIRT_BR,
-                                         PARAVIRT_TYPE(SET_RR0_TO_RR4))
-                     : "=r"(ia64_clobber0),
-                       "=r"(ia64_clobber1),
-                       "=r"(ia64_clobber2),
-                       "=r"(ia64_clobber3),
-                       "=r"(ia64_clobber4)
-                     : PARAVIRT_OP(set_rr0_to_rr4),
-                       "0"(__val0), "1"(__val1), "2"(__val2),
-                       "3"(__val3), "4"(__val4)
-                     : PARAVIRT_CLOBBERS5);
-}
-
-/* unsigned long paravirt_getreg(int reg) */
-#define __paravirt_getreg(reg)                                         \
-       ({                                                              \
-               register unsigned long ia64_intri_res asm ("r8");       \
-               register unsigned long __reg asm ("r8") = (reg);        \
-                                                                       \
-               asm volatile (paravirt_alt_bundle(__PARAVIRT_BR,        \
-                                                 PARAVIRT_TYPE(GETREG) \
-                                                 + (reg))              \
-                             : "=r"(ia64_intri_res)                    \
-                             : PARAVIRT_OP(getreg), "0"(__reg)         \
-                             : PARAVIRT_CLOBBERS1);                    \
-                                                                       \
-               ia64_intri_res;                                         \
-       })
-
-/* void paravirt_setreg(int reg, unsigned long val) */
-#define paravirt_setreg(reg, val)                                      \
-       do {                                                            \
-               register unsigned long __val asm ("r8") = val;          \
-               register unsigned long __reg asm ("r9") = reg;          \
-               register unsigned long ia64_clobber1 asm ("r8");        \
-               register unsigned long ia64_clobber2 asm ("r9");        \
-                                                                       \
-               asm volatile (paravirt_alt_bundle(__PARAVIRT_BR,        \
-                                                 PARAVIRT_TYPE(SETREG) \
-                                                 + (reg))              \
-                             : "=r"(ia64_clobber1),                    \
-                               "=r"(ia64_clobber2)                     \
-                             : PARAVIRT_OP(setreg),                    \
-                               "1"(__reg), "0"(__val)                  \
-                             : PARAVIRT_CLOBBERS2);                    \
-       } while (0)
-
-#endif /* ASM_SUPPORTED */
-#endif /* CONFIG_PARAVIRT && ASM_SUPPOTED */
-
-#endif /* _ASM_IA64_PARAVIRT_PRIVOP_H */
index 52af5ed9f60ba98654f4657e7dd84e7b36f88390..b897fae1f0ca88d96a3b1e06ea906969ffac76d8 100644 (file)
@@ -52,25 +52,6 @@ extern unsigned long ia64_max_iommu_merge_mask;
 
 #include <asm-generic/pci-dma-compat.h>
 
-#ifdef CONFIG_PCI
-static inline void pci_dma_burst_advice(struct pci_dev *pdev,
-                                       enum pci_dma_burst_strategy *strat,
-                                       unsigned long *strategy_parameter)
-{
-       unsigned long cacheline_size;
-       u8 byte;
-
-       pci_read_config_byte(pdev, PCI_CACHE_LINE_SIZE, &byte);
-       if (byte == 0)
-               cacheline_size = 1024;
-       else
-               cacheline_size = (int) byte * 4;
-
-       *strat = PCI_DMA_BURST_MULTIPLE;
-       *strategy_parameter = cacheline_size;
-}
-#endif
-
 #define HAVE_PCI_MMAP
 extern int pci_mmap_page_range (struct pci_dev *dev, struct vm_area_struct *vma,
                                enum pci_mmap_state mmap_state, int write_combine);
@@ -108,19 +89,6 @@ static inline int pci_proc_domain(struct pci_bus *bus)
        return (pci_domain_nr(bus) != 0);
 }
 
-static inline struct resource *
-pcibios_select_root(struct pci_dev *pdev, struct resource *res)
-{
-       struct resource *root = NULL;
-
-       if (res->flags & IORESOURCE_IO)
-               root = &ioport_resource;
-       if (res->flags & IORESOURCE_MEM)
-               root = &iomem_resource;
-
-       return root;
-}
-
 #define HAVE_ARCH_PCI_GET_LEGACY_IDE_IRQ
 static inline int pci_get_legacy_ide_irq(struct pci_dev *dev, int channel)
 {
index f35109b1d9076ccd996f1308dbffbdf788cd7475..a0e3620f8f133f7ea798ae9458abf10cc168f3c3 100644 (file)
@@ -61,8 +61,6 @@ extern void ia64_xchg_called_with_bad_pointer(void);
  * indicated by comparing RETURN with OLD.
  */
 
-#define __HAVE_ARCH_CMPXCHG 1
-
 /*
  * This function doesn't exist, so you'll get a linker error
  * if something tries to do an invalid cmpxchg().
index d68b5cf81e3177c20df6af7d95bb4ce08171b43a..3686d6abafdefd3feb854656d5cf704f89be5340 100644 (file)
@@ -9,7 +9,7 @@ endif
 extra-y        := head.o init_task.o vmlinux.lds
 
 obj-y := entry.o efi.o efi_stub.o gate-data.o fsys.o ia64_ksyms.o irq.o irq_ia64.o     \
-        irq_lsapic.o ivt.o machvec.o pal.o paravirt_patchlist.o patch.o process.o perfmon.o ptrace.o sal.o             \
+        irq_lsapic.o ivt.o machvec.o pal.o patch.o process.o perfmon.o ptrace.o sal.o          \
         salinfo.o setup.o signal.o sys_ia64.o time.o traps.o unaligned.o \
         unwind.o mca.o mca_asm.o topology.o dma-mapping.o
 
@@ -35,9 +35,6 @@ mca_recovery-y                        += mca_drv.o mca_drv_asm.o
 obj-$(CONFIG_IA64_MC_ERR_INJECT)+= err_inject.o
 obj-$(CONFIG_STACKTRACE)       += stacktrace.o
 
-obj-$(CONFIG_PARAVIRT)         += paravirt.o paravirtentry.o \
-                                  paravirt_patch.o
-
 obj-$(CONFIG_IA64_ESI)         += esi.o
 ifneq ($(CONFIG_IA64_ESI),)
 obj-y                          += esi_stub.o   # must be in kernel proper
@@ -52,8 +49,6 @@ CFLAGS_traps.o  += -mfixed-range=f2-f5,f16-f31
 
 # The gate DSO image is built using a special linker script.
 include $(src)/Makefile.gate
-# tell compiled for native
-CPPFLAGS_gate.lds += -D__IA64_GATE_PARAVIRTUALIZED_NATIVE
 
 # Calculate NR_IRQ = max(IA64_NATIVE_NR_IRQS, XEN_NR_IRQS, ...) based on config
 define sed-y
@@ -84,30 +79,3 @@ arch/$(SRCARCH)/kernel/nr-irqs.s: arch/$(SRCARCH)/kernel/nr-irqs.c
 include/generated/nr-irqs.h: arch/$(SRCARCH)/kernel/nr-irqs.s
        $(Q)mkdir -p $(dir $@)
        $(call cmd,nr_irqs)
-
-#
-# native ivt.S, entry.S and fsys.S
-#
-ASM_PARAVIRT_OBJS = ivt.o entry.o fsys.o
-define paravirtualized_native
-AFLAGS_$(1) += -D__IA64_ASM_PARAVIRTUALIZED_NATIVE
-AFLAGS_pvchk-sed-$(1) += -D__IA64_ASM_PARAVIRTUALIZED_PVCHECK
-extra-y += pvchk-$(1)
-endef
-$(foreach obj,$(ASM_PARAVIRT_OBJS),$(eval $(call paravirtualized_native,$(obj))))
-
-#
-# Checker for paravirtualizations of privileged operations.
-#
-quiet_cmd_pv_check_sed = PVCHK   $@
-define cmd_pv_check_sed
-       sed -f $(srctree)/arch/$(SRCARCH)/scripts/pvcheck.sed $< > $@
-endef
-
-$(obj)/pvchk-sed-%.s: $(src)/%.S $(srctree)/arch/$(SRCARCH)/scripts/pvcheck.sed FORCE
-       $(call if_changed_dep,as_s_S)
-$(obj)/pvchk-%.s: $(obj)/pvchk-sed-%.s FORCE
-       $(call if_changed,pv_check_sed)
-$(obj)/pvchk-%.o: $(obj)/pvchk-%.s FORCE
-       $(call if_changed,as_o_S)
-.PRECIOUS: $(obj)/pvchk-sed-%.s $(obj)/pvchk-%.s $(obj)/pvchk-%.o
index c52d7540dc05f4c8ca324e0f6f9ce118f9570d71..47e962f7ed5ab2eb83149767f1104305396b9326 100644 (file)
@@ -464,7 +464,6 @@ efi_map_pal_code (void)
                 GRANULEROUNDDOWN((unsigned long) pal_vaddr),
                 pte_val(pfn_pte(__pa(pal_vaddr) >> PAGE_SHIFT, PAGE_KERNEL)),
                 IA64_GRANULE_SHIFT);
-       paravirt_dv_serialize_data();
        ia64_set_psr(psr);              /* restore psr */
 }
 
index fcf8b8cbca0be79607808c81aad2b37456bbef89..ae0de7bf55257682dc11a1cc1fa31bc6cffa3112 100644 (file)
@@ -51,7 +51,6 @@
 
 #include "minstate.h"
 
-#ifdef __IA64_ASM_PARAVIRTUALIZED_NATIVE
        /*
         * execve() is special because in case of success, we need to
         * setup a null register window frame.
@@ -161,7 +160,6 @@ GLOBAL_ENTRY(sys_clone)
        mov rp=loc0
        br.ret.sptk.many rp
 END(sys_clone)
-#endif /* __IA64_ASM_PARAVIRTUALIZED_NATIVE */
 
 /*
  * prev_task <- ia64_switch_to(struct task_struct *next)
@@ -169,7 +167,7 @@ END(sys_clone)
  *     called.  The code starting at .map relies on this.  The rest of the code
  *     doesn't care about the interrupt masking status.
  */
-GLOBAL_ENTRY(__paravirt_switch_to)
+GLOBAL_ENTRY(ia64_switch_to)
        .prologue
        alloc r16=ar.pfs,1,0,0,0
        DO_SAVE_SWITCH_STACK
@@ -221,9 +219,8 @@ GLOBAL_ENTRY(__paravirt_switch_to)
        itr.d dtr[r25]=r23              // wire in new mapping...
        SSM_PSR_IC_AND_SRLZ_D(r8, r9)   // reenable the psr.ic bit
        br.cond.sptk .done
-END(__paravirt_switch_to)
+END(ia64_switch_to)
 
-#ifdef __IA64_ASM_PARAVIRTUALIZED_NATIVE
 /*
  * Note that interrupts are enabled during save_switch_stack and load_switch_stack.  This
  * means that we may get an interrupt with "sp" pointing to the new kernel stack while
@@ -639,16 +636,8 @@ GLOBAL_ENTRY(ia64_ret_from_syscall)
        adds r2=PT(R8)+16,sp                    // r2 = &pt_regs.r8
        mov r10=r0                              // clear error indication in r10
 (p7)   br.cond.spnt handle_syscall_error       // handle potential syscall failure
-#ifdef CONFIG_PARAVIRT
-       ;;
-       br.cond.sptk.few ia64_leave_syscall
-       ;;
-#endif /* CONFIG_PARAVIRT */
 END(ia64_ret_from_syscall)
-#ifndef CONFIG_PARAVIRT
        // fall through
-#endif
-#endif /* __IA64_ASM_PARAVIRTUALIZED_NATIVE */
 
 /*
  * ia64_leave_syscall(): Same as ia64_leave_kernel, except that it doesn't
@@ -694,7 +683,7 @@ END(ia64_ret_from_syscall)
  *           ar.csd: cleared
  *           ar.ssd: cleared
  */
-GLOBAL_ENTRY(__paravirt_leave_syscall)
+GLOBAL_ENTRY(ia64_leave_syscall)
        PT_REGS_UNWIND_INFO(0)
        /*
         * work.need_resched etc. mustn't get changed by this CPU before it returns to
@@ -722,8 +711,8 @@ GLOBAL_ENTRY(__paravirt_leave_syscall)
        cmp.eq pLvSys,p0=r0,r0          // pLvSys=1: leave from syscall
 (pUStk)        cmp.eq.unc p6,p0=r0,r0          // p6 <- pUStk
 #endif
-.global __paravirt_work_processed_syscall;
-__paravirt_work_processed_syscall:
+.global ia64_work_processed_syscall;
+ia64_work_processed_syscall:
 #ifdef CONFIG_VIRT_CPU_ACCOUNTING_NATIVE
        adds r2=PT(LOADRS)+16,r12
        MOV_FROM_ITC(pUStk, p9, r22, r19)       // fetch time at leave
@@ -836,9 +825,9 @@ __paravirt_work_processed_syscall:
        mov.m ar.ssd=r0                 // M2   clear ar.ssd
        mov f11=f0                      // F    clear f11
        br.cond.sptk.many rbs_switch    // B
-END(__paravirt_leave_syscall)
+END(ia64_leave_syscall)
 
-GLOBAL_ENTRY(__paravirt_leave_kernel)
+GLOBAL_ENTRY(ia64_leave_kernel)
        PT_REGS_UNWIND_INFO(0)
        /*
         * work.need_resched etc. mustn't get changed by this CPU before it returns to
@@ -1171,26 +1160,25 @@ skip_rbs_switch:
 (p6)   br.cond.sptk.few .notify
        br.call.spnt.many rp=preempt_schedule_irq
 .ret9: cmp.eq p6,p0=r0,r0      // p6 <- 1 (re-check)
-(pLvSys)br.cond.sptk.few  __paravirt_pending_syscall_end
+(pLvSys)br.cond.sptk.few  ia64_work_pending_syscall_end
        br.cond.sptk.many .work_processed_kernel
 
 .notify:
 (pUStk)        br.call.spnt.many rp=notify_resume_user
 .ret10:        cmp.ne p6,p0=r0,r0      // p6 <- 0 (don't re-check)
-(pLvSys)br.cond.sptk.few  __paravirt_pending_syscall_end
+(pLvSys)br.cond.sptk.few  ia64_work_pending_syscall_end
        br.cond.sptk.many .work_processed_kernel
 
-.global __paravirt_pending_syscall_end;
-__paravirt_pending_syscall_end:
+.global ia64_work_pending_syscall_end;
+ia64_work_pending_syscall_end:
        adds r2=PT(R8)+16,r12
        adds r3=PT(R10)+16,r12
        ;;
        ld8 r8=[r2]
        ld8 r10=[r3]
-       br.cond.sptk.many __paravirt_work_processed_syscall_target
-END(__paravirt_leave_kernel)
+       br.cond.sptk.many ia64_work_processed_syscall
+END(ia64_leave_kernel)
 
-#ifdef __IA64_ASM_PARAVIRTUALIZED_NATIVE
 ENTRY(handle_syscall_error)
        /*
         * Some system calls (e.g., ptrace, mmap) can return arbitrary values which could
@@ -1294,7 +1282,7 @@ ENTRY(sys_rt_sigreturn)
        adds sp=16,sp
        ;;
        ld8 r9=[sp]                             // load new ar.unat
-       mov.sptk b7=r8,ia64_native_leave_kernel
+       mov.sptk b7=r8,ia64_leave_kernel
        ;;
        mov ar.unat=r9
        br.many b7
@@ -1782,4 +1770,3 @@ sys_call_table:
        data8 sys_execveat
 
        .org sys_call_table + 8*NR_syscalls     // guard against failures to increase NR_syscalls
-#endif /* __IA64_ASM_PARAVIRTUALIZED_NATIVE */
index abc6dee3799c989597d7d84cdc87e8110619affd..edbf7af958492b1624cd7c3c748c9185f9782d9f 100644 (file)
@@ -24,7 +24,7 @@
 #include <asm/unistd.h>
 
 #include "entry.h"
-#include "paravirt_inst.h"
+#include <asm/native/inst.h>
 
 /*
  * See Documentation/ia64/fsys.txt for details on fsyscalls.
@@ -402,7 +402,7 @@ ENTRY(fsys_fallback_syscall)
        mov r26=ar.pfs
 END(fsys_fallback_syscall)
        /* FALL THROUGH */
-GLOBAL_ENTRY(paravirt_fsys_bubble_down)
+GLOBAL_ENTRY(fsys_bubble_down)
        .prologue
        .altrp b6
        .body
@@ -440,7 +440,7 @@ GLOBAL_ENTRY(paravirt_fsys_bubble_down)
         *
         * PSR.BE : already is turned off in __kernel_syscall_via_epc()
         * PSR.AC : don't care (kernel normally turns PSR.AC on)
-        * PSR.I  : already turned off by the time paravirt_fsys_bubble_down gets
+        * PSR.I  : already turned off by the time fsys_bubble_down gets
         *          invoked
         * PSR.DFL: always 0 (kernel never turns it on)
         * PSR.DFH: don't care --- kernel never touches f32-f127 on its own
@@ -450,7 +450,7 @@ GLOBAL_ENTRY(paravirt_fsys_bubble_down)
         * PSR.DB : don't care --- kernel never enables kernel-level
         *          breakpoints
         * PSR.TB : must be 0 already; if it wasn't zero on entry to
-        *          __kernel_syscall_via_epc, the branch to paravirt_fsys_bubble_down
+        *          __kernel_syscall_via_epc, the branch to fsys_bubble_down
         *          will trigger a taken branch; the taken-trap-handler then
         *          converts the syscall into a break-based system-call.
         */
@@ -541,14 +541,14 @@ GLOBAL_ENTRY(paravirt_fsys_bubble_down)
        nop.m 0
 (p8)   br.call.sptk.many b6=b6                 // B    (ignore return address)
        br.cond.spnt ia64_trace_syscall         // B
-END(paravirt_fsys_bubble_down)
+END(fsys_bubble_down)
 
        .rodata
        .align 8
-       .globl paravirt_fsyscall_table
+       .globl fsyscall_table
 
-       data8 paravirt_fsys_bubble_down
-paravirt_fsyscall_table:
+       data8 fsys_bubble_down
+fsyscall_table:
        data8 fsys_ni_syscall
        data8 0                         // exit                 // 1025
        data8 0                         // read
@@ -833,4 +833,4 @@ paravirt_fsyscall_table:
 
        // fill in zeros for the remaining entries
        .zero:
-       .space paravirt_fsyscall_table + 8*NR_syscalls - .zero, 0
+       .space fsyscall_table + 8*NR_syscalls - .zero, 0
index b5f8bdd8618e6907e2548e682d2a0caf5b206c62..0bd1b3bfaf1cad6d6995bdc2087b8138d4d02bc4 100644 (file)
@@ -14,7 +14,7 @@
 #include <asm/unistd.h>
 #include <asm/kregs.h>
 #include <asm/page.h>
-#include "paravirt_inst.h"
+#include <asm/native/inst.h>
 
 /*
  * We can't easily refer to symbols inside the kernel.  To avoid full runtime relocation,
@@ -376,11 +376,4 @@ GLOBAL_ENTRY(__kernel_syscall_via_epc)
 (p9)   mov r8=ENOSYS
        FSYS_RETURN
 
-#ifdef CONFIG_PARAVIRT
-       /*
-        * padd to make the size of this symbol constant
-        * independent of paravirtualization.
-        */
-       .align PAGE_SIZE / 8
-#endif
 END(__kernel_syscall_via_epc)
index e518f7902af6a3f9fb68fca9f8a2eb8c71e25baf..3e8271e85a1e11d3755fa369a8d6ff364942dc12 100644 (file)
@@ -6,7 +6,6 @@
  */
 
 #include <asm/page.h>
-#include "paravirt_patchlist.h"
 
 SECTIONS
 {
@@ -33,21 +32,21 @@ SECTIONS
        . = GATE_ADDR + 0x600;
 
        .data..patch            : {
-               __paravirt_start_gate_mckinley_e9_patchlist = .;
+               __start_gate_mckinley_e9_patchlist = .;
                *(.data..patch.mckinley_e9)
-               __paravirt_end_gate_mckinley_e9_patchlist = .;
+               __end_gate_mckinley_e9_patchlist = .;
 
-               __paravirt_start_gate_vtop_patchlist = .;
+               __start_gate_vtop_patchlist = .;
                *(.data..patch.vtop)
-               __paravirt_end_gate_vtop_patchlist = .;
+               __end_gate_vtop_patchlist = .;
 
-               __paravirt_start_gate_fsyscall_patchlist = .;
+               __start_gate_fsyscall_patchlist = .;
                *(.data..patch.fsyscall_table)
-               __paravirt_end_gate_fsyscall_patchlist = .;
+               __end_gate_fsyscall_patchlist = .;
 
-               __paravirt_start_gate_brl_fsys_bubble_down_patchlist = .;
+               __start_gate_brl_fsys_bubble_down_patchlist = .;
                *(.data..patch.brl_fsys_bubble_down)
-               __paravirt_end_gate_brl_fsys_bubble_down_patchlist = .;
+               __end_gate_brl_fsys_bubble_down_patchlist = .;
        }                                               :readable
 
        .IA_64.unwind_info      : { *(.IA_64.unwind_info*) }
index a4acddad0c78e84aa64055ca0cff7cc650cae720..bb748c5964433165efab01ba39a33669fe76069a 100644 (file)
@@ -26,7 +26,6 @@
 #include <asm/mmu_context.h>
 #include <asm/asm-offsets.h>
 #include <asm/pal.h>
-#include <asm/paravirt.h>
 #include <asm/pgtable.h>
 #include <asm/processor.h>
 #include <asm/ptrace.h>
@@ -394,41 +393,6 @@ start_ap:
        ;;
 (isBP) st8 [r2]=r28            // save the address of the boot param area passed by the bootloader
 
-#ifdef CONFIG_PARAVIRT
-
-       movl r14=hypervisor_setup_hooks
-       movl r15=hypervisor_type
-       mov r16=num_hypervisor_hooks
-       ;;
-       ld8 r2=[r15]
-       ;;
-       cmp.ltu p7,p0=r2,r16    // array size check
-       shladd r8=r2,3,r14
-       ;;
-(p7)   ld8 r9=[r8]
-       ;;
-(p7)   mov b1=r9
-(p7)   cmp.ne.unc p7,p0=r9,r0  // no actual branch to NULL
-       ;;
-(p7)   br.call.sptk.many rp=b1
-
-       __INITDATA
-
-default_setup_hook = 0         // Currently nothing needs to be done.
-
-       .global hypervisor_type
-hypervisor_type:
-       data8           PARAVIRT_HYPERVISOR_TYPE_DEFAULT
-
-       // must have the same order with PARAVIRT_HYPERVISOR_TYPE_xxx
-
-hypervisor_setup_hooks:
-       data8           default_setup_hook
-num_hypervisor_hooks = (. - hypervisor_setup_hooks) / 8
-       .previous
-
-#endif
-
 #ifdef CONFIG_SMP
 (isAP) br.call.sptk.many rp=start_secondary
 .ret0:
@@ -1063,12 +1027,6 @@ GLOBAL_ENTRY(ia64_native_sched_clock)
        shrp r8=r9,r8,IA64_NSEC_PER_CYC_SHIFT
        br.ret.sptk.many rp
 END(ia64_native_sched_clock)
-#ifndef CONFIG_PARAVIRT
-       //unsigned long long
-       //sched_clock(void) __attribute__((alias("ia64_native_sched_clock")));
-       .global sched_clock
-sched_clock = ia64_native_sched_clock
-#endif
 
 #ifdef CONFIG_VIRT_CPU_ACCOUNTING_NATIVE
 GLOBAL_ENTRY(cycle_to_cputime)
index e42bf7a913f3ed6f74c28332de4496ff2e5579fc..b1c3cfc93e715b54f485521ed25b36ca8af46b50 100644 (file)
@@ -937,7 +937,6 @@ END(interrupt)
         *      - ar.fpsr: set to kernel settings
         *      -  b6: preserved (same as on entry)
         */
-#ifdef __IA64_ASM_PARAVIRTUALIZED_NATIVE
 GLOBAL_ENTRY(ia64_syscall_setup)
 #if PT(B6) != 0
 # error This code assumes that b6 is the first field in pt_regs.
@@ -1029,7 +1028,6 @@ GLOBAL_ENTRY(ia64_syscall_setup)
 (p10)  mov r8=-EINVAL
        br.ret.sptk.many b7
 END(ia64_syscall_setup)
-#endif /* __IA64_ASM_PARAVIRTUALIZED_NATIVE */
 
        .org ia64_ivt+0x3c00
 /////////////////////////////////////////////////////////////////////////////////////////
@@ -1043,7 +1041,7 @@ END(ia64_syscall_setup)
        DBG_FAULT(16)
        FAULT(16)
 
-#if defined(CONFIG_VIRT_CPU_ACCOUNTING_NATIVE) && defined(__IA64_ASM_PARAVIRTUALIZED_NATIVE)
+#if defined(CONFIG_VIRT_CPU_ACCOUNTING_NATIVE)
        /*
         * There is no particular reason for this code to be here, other than
         * that there happens to be space here that would go unused otherwise.
index cc82a7d744c985ce18b0a70512cd54234d71aefa..5704700fb703723b77a6e61a498f7b6a98dfa86c 100644 (file)
@@ -2,7 +2,7 @@
 #include <asm/cache.h>
 
 #include "entry.h"
-#include "paravirt_inst.h"
+#include <asm/native/inst.h>
 
 #ifdef CONFIG_VIRT_CPU_ACCOUNTING_NATIVE
 /* read ar.itc in advance, and use it before leaving bank 0 */
index 29754aae5177a94ec257021ab00c57f688a61de8..b15933c31b2ffa46aec115a8a06d564edf674588 100644 (file)
@@ -439,14 +439,6 @@ module_frob_arch_sections (Elf_Ehdr *ehdr, Elf_Shdr *sechdrs, char *secstrings,
                        mod->arch.opd = s;
                else if (strcmp(".IA_64.unwind", secstrings + s->sh_name) == 0)
                        mod->arch.unwind = s;
-#ifdef CONFIG_PARAVIRT
-               else if (strcmp(".paravirt_bundles",
-                               secstrings + s->sh_name) == 0)
-                       mod->arch.paravirt_bundles = s;
-               else if (strcmp(".paravirt_insts",
-                               secstrings + s->sh_name) == 0)
-                       mod->arch.paravirt_insts = s;
-#endif
 
        if (!mod->arch.core_plt || !mod->arch.init_plt || !mod->arch.got || !mod->arch.opd) {
                printk(KERN_ERR "%s: sections missing\n", mod->name);
@@ -914,30 +906,6 @@ module_finalize (const Elf_Ehdr *hdr, const Elf_Shdr *sechdrs, struct module *mo
        DEBUGP("%s: init: entry=%p\n", __func__, mod->init);
        if (mod->arch.unwind)
                register_unwind_table(mod);
-#ifdef CONFIG_PARAVIRT
-        if (mod->arch.paravirt_bundles) {
-                struct paravirt_patch_site_bundle *start =
-                        (struct paravirt_patch_site_bundle *)
-                        mod->arch.paravirt_bundles->sh_addr;
-                struct paravirt_patch_site_bundle *end =
-                        (struct paravirt_patch_site_bundle *)
-                        (mod->arch.paravirt_bundles->sh_addr +
-                         mod->arch.paravirt_bundles->sh_size);
-
-                paravirt_patch_apply_bundle(start, end);
-        }
-        if (mod->arch.paravirt_insts) {
-                struct paravirt_patch_site_inst *start =
-                        (struct paravirt_patch_site_inst *)
-                        mod->arch.paravirt_insts->sh_addr;
-                struct paravirt_patch_site_inst *end =
-                        (struct paravirt_patch_site_inst *)
-                        (mod->arch.paravirt_insts->sh_addr +
-                         mod->arch.paravirt_insts->sh_size);
-
-                paravirt_patch_apply_inst(start, end);
-        }
-#endif
        return 0;
 }
 
index 9dd7464f8c1742848011ce1397ef8a79f1e60d4f..d70bf15c690a53227142b6027ce7bb116475ed9d 100644 (file)
@@ -165,7 +165,7 @@ static struct irq_chip dmar_msi_type = {
        .irq_retrigger = ia64_msi_retrigger_irq,
 };
 
-static int
+static void
 msi_compose_msg(struct pci_dev *pdev, unsigned int irq, struct msi_msg *msg)
 {
        struct irq_cfg *cfg = irq_cfg + irq;
@@ -186,21 +186,29 @@ msi_compose_msg(struct pci_dev *pdev, unsigned int irq, struct msi_msg *msg)
                MSI_DATA_LEVEL_ASSERT |
                MSI_DATA_DELIVERY_FIXED |
                MSI_DATA_VECTOR(cfg->vector);
-       return 0;
 }
 
-int arch_setup_dmar_msi(unsigned int irq)
+int dmar_alloc_hwirq(int id, int node, void *arg)
 {
-       int ret;
+       int irq;
        struct msi_msg msg;
 
-       ret = msi_compose_msg(NULL, irq, &msg);
-       if (ret < 0)
-               return ret;
-       dmar_msi_write(irq, &msg);
-       irq_set_chip_and_handler_name(irq, &dmar_msi_type, handle_edge_irq,
-                                     "edge");
-       return 0;
+       irq = create_irq();
+       if (irq > 0) {
+               irq_set_handler_data(irq, arg);
+               irq_set_chip_and_handler_name(irq, &dmar_msi_type,
+                                             handle_edge_irq, "edge");
+               msi_compose_msg(NULL, irq, &msg);
+               dmar_msi_write(irq, &msg);
+       }
+
+       return irq;
+}
+
+void dmar_free_hwirq(int irq)
+{
+       irq_set_handler_data(irq, NULL);
+       destroy_irq(irq);
 }
 #endif /* CONFIG_INTEL_IOMMU */
 
diff --git a/arch/ia64/kernel/paravirt.c b/arch/ia64/kernel/paravirt.c
deleted file mode 100644 (file)
index 1b22f6d..0000000
+++ /dev/null
@@ -1,902 +0,0 @@
-/******************************************************************************
- * arch/ia64/kernel/paravirt.c
- *
- * Copyright (c) 2008 Isaku Yamahata <yamahata at valinux co jp>
- *                    VA Linux Systems Japan K.K.
- *     Yaozu (Eddie) Dong <eddie.dong@intel.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- *
- */
-
-#include <linux/init.h>
-
-#include <linux/compiler.h>
-#include <linux/io.h>
-#include <linux/irq.h>
-#include <linux/module.h>
-#include <linux/types.h>
-
-#include <asm/iosapic.h>
-#include <asm/paravirt.h>
-
-/***************************************************************************
- * general info
- */
-struct pv_info pv_info = {
-       .kernel_rpl = 0,
-       .paravirt_enabled = 0,
-       .name = "bare hardware"
-};
-
-/***************************************************************************
- * pv_init_ops
- * initialization hooks.
- */
-
-static void __init
-ia64_native_patch_branch(unsigned long tag, unsigned long type);
-
-struct pv_init_ops pv_init_ops =
-{
-#ifdef ASM_SUPPORTED
-       .patch_bundle = ia64_native_patch_bundle,
-#endif
-       .patch_branch = ia64_native_patch_branch,
-};
-
-/***************************************************************************
- * pv_cpu_ops
- * intrinsics hooks.
- */
-
-#ifndef ASM_SUPPORTED
-/* ia64_native_xxx are macros so that we have to make them real functions */
-
-#define DEFINE_VOID_FUNC1(name)                                        \
-       static void                                             \
-       ia64_native_ ## name ## _func(unsigned long arg)        \
-       {                                                       \
-               ia64_native_ ## name(arg);                      \
-       }
-
-#define DEFINE_VOID_FUNC1_VOID(name)                           \
-       static void                                             \
-       ia64_native_ ## name ## _func(void *arg)                \
-       {                                                       \
-               ia64_native_ ## name(arg);                      \
-       }
-
-#define DEFINE_VOID_FUNC2(name)                                        \
-       static void                                             \
-       ia64_native_ ## name ## _func(unsigned long arg0,       \
-                                     unsigned long arg1)       \
-       {                                                       \
-               ia64_native_ ## name(arg0, arg1);               \
-       }
-
-#define DEFINE_FUNC0(name)                     \
-       static unsigned long                    \
-       ia64_native_ ## name ## _func(void)     \
-       {                                       \
-               return ia64_native_ ## name();  \
-       }
-
-#define DEFINE_FUNC1(name, type)                       \
-       static unsigned long                            \
-       ia64_native_ ## name ## _func(type arg)         \
-       {                                               \
-               return ia64_native_ ## name(arg);       \
-       }                                               \
-
-DEFINE_VOID_FUNC1_VOID(fc);
-DEFINE_VOID_FUNC1(intrin_local_irq_restore);
-
-DEFINE_VOID_FUNC2(ptcga);
-DEFINE_VOID_FUNC2(set_rr);
-
-DEFINE_FUNC0(get_psr_i);
-
-DEFINE_FUNC1(thash, unsigned long);
-DEFINE_FUNC1(get_cpuid, int);
-DEFINE_FUNC1(get_pmd, int);
-DEFINE_FUNC1(get_rr, unsigned long);
-
-static void
-ia64_native_ssm_i_func(void)
-{
-       ia64_native_ssm(IA64_PSR_I);
-}
-
-static void
-ia64_native_rsm_i_func(void)
-{
-       ia64_native_rsm(IA64_PSR_I);
-}
-
-static void
-ia64_native_set_rr0_to_rr4_func(unsigned long val0, unsigned long val1,
-                               unsigned long val2, unsigned long val3,
-                               unsigned long val4)
-{
-       ia64_native_set_rr0_to_rr4(val0, val1, val2, val3, val4);
-}
-
-#define CASE_GET_REG(id)                               \
-       case _IA64_REG_ ## id:                          \
-       res = ia64_native_getreg(_IA64_REG_ ## id);     \
-       break;
-#define CASE_GET_AR(id) CASE_GET_REG(AR_ ## id)
-#define CASE_GET_CR(id) CASE_GET_REG(CR_ ## id)
-
-unsigned long
-ia64_native_getreg_func(int regnum)
-{
-       unsigned long res = -1;
-       switch (regnum) {
-       CASE_GET_REG(GP);
-       /*CASE_GET_REG(IP);*/ /* returned ip value shouldn't be constant */
-       CASE_GET_REG(PSR);
-       CASE_GET_REG(TP);
-       CASE_GET_REG(SP);
-
-       CASE_GET_AR(KR0);
-       CASE_GET_AR(KR1);
-       CASE_GET_AR(KR2);
-       CASE_GET_AR(KR3);
-       CASE_GET_AR(KR4);
-       CASE_GET_AR(KR5);
-       CASE_GET_AR(KR6);
-       CASE_GET_AR(KR7);
-       CASE_GET_AR(RSC);
-       CASE_GET_AR(BSP);
-       CASE_GET_AR(BSPSTORE);
-       CASE_GET_AR(RNAT);
-       CASE_GET_AR(FCR);
-       CASE_GET_AR(EFLAG);
-       CASE_GET_AR(CSD);
-       CASE_GET_AR(SSD);
-       CASE_GET_AR(CFLAG);
-       CASE_GET_AR(FSR);
-       CASE_GET_AR(FIR);
-       CASE_GET_AR(FDR);
-       CASE_GET_AR(CCV);
-       CASE_GET_AR(UNAT);
-       CASE_GET_AR(FPSR);
-       CASE_GET_AR(ITC);
-       CASE_GET_AR(PFS);
-       CASE_GET_AR(LC);
-       CASE_GET_AR(EC);
-
-       CASE_GET_CR(DCR);
-       CASE_GET_CR(ITM);
-       CASE_GET_CR(IVA);
-       CASE_GET_CR(PTA);
-       CASE_GET_CR(IPSR);
-       CASE_GET_CR(ISR);
-       CASE_GET_CR(IIP);
-       CASE_GET_CR(IFA);
-       CASE_GET_CR(ITIR);
-       CASE_GET_CR(IIPA);
-       CASE_GET_CR(IFS);
-       CASE_GET_CR(IIM);
-       CASE_GET_CR(IHA);
-       CASE_GET_CR(LID);
-       CASE_GET_CR(IVR);
-       CASE_GET_CR(TPR);
-       CASE_GET_CR(EOI);
-       CASE_GET_CR(IRR0);
-       CASE_GET_CR(IRR1);
-       CASE_GET_CR(IRR2);
-       CASE_GET_CR(IRR3);
-       CASE_GET_CR(ITV);
-       CASE_GET_CR(PMV);
-       CASE_GET_CR(CMCV);
-       CASE_GET_CR(LRR0);
-       CASE_GET_CR(LRR1);
-
-       default:
-               printk(KERN_CRIT "wrong_getreg %d\n", regnum);
-               break;
-       }
-       return res;
-}
-
-#define CASE_SET_REG(id)                               \
-       case _IA64_REG_ ## id:                          \
-       ia64_native_setreg(_IA64_REG_ ## id, val);      \
-       break;
-#define CASE_SET_AR(id) CASE_SET_REG(AR_ ## id)
-#define CASE_SET_CR(id) CASE_SET_REG(CR_ ## id)
-
-void
-ia64_native_setreg_func(int regnum, unsigned long val)
-{
-       switch (regnum) {
-       case _IA64_REG_PSR_L:
-               ia64_native_setreg(_IA64_REG_PSR_L, val);
-               ia64_dv_serialize_data();
-               break;
-       CASE_SET_REG(SP);
-       CASE_SET_REG(GP);
-
-       CASE_SET_AR(KR0);
-       CASE_SET_AR(KR1);
-       CASE_SET_AR(KR2);
-       CASE_SET_AR(KR3);
-       CASE_SET_AR(KR4);
-       CASE_SET_AR(KR5);
-       CASE_SET_AR(KR6);
-       CASE_SET_AR(KR7);
-       CASE_SET_AR(RSC);
-       CASE_SET_AR(BSP);
-       CASE_SET_AR(BSPSTORE);
-       CASE_SET_AR(RNAT);
-       CASE_SET_AR(FCR);
-       CASE_SET_AR(EFLAG);
-       CASE_SET_AR(CSD);
-       CASE_SET_AR(SSD);
-       CASE_SET_AR(CFLAG);
-       CASE_SET_AR(FSR);
-       CASE_SET_AR(FIR);
-       CASE_SET_AR(FDR);
-       CASE_SET_AR(CCV);
-       CASE_SET_AR(UNAT);
-       CASE_SET_AR(FPSR);
-       CASE_SET_AR(ITC);
-       CASE_SET_AR(PFS);
-       CASE_SET_AR(LC);
-       CASE_SET_AR(EC);
-
-       CASE_SET_CR(DCR);
-       CASE_SET_CR(ITM);
-       CASE_SET_CR(IVA);
-       CASE_SET_CR(PTA);
-       CASE_SET_CR(IPSR);
-       CASE_SET_CR(ISR);
-       CASE_SET_CR(IIP);
-       CASE_SET_CR(IFA);
-       CASE_SET_CR(ITIR);
-       CASE_SET_CR(IIPA);
-       CASE_SET_CR(IFS);
-       CASE_SET_CR(IIM);
-       CASE_SET_CR(IHA);
-       CASE_SET_CR(LID);
-       CASE_SET_CR(IVR);
-       CASE_SET_CR(TPR);
-       CASE_SET_CR(EOI);
-       CASE_SET_CR(IRR0);
-       CASE_SET_CR(IRR1);
-       CASE_SET_CR(IRR2);
-       CASE_SET_CR(IRR3);
-       CASE_SET_CR(ITV);
-       CASE_SET_CR(PMV);
-       CASE_SET_CR(CMCV);
-       CASE_SET_CR(LRR0);
-       CASE_SET_CR(LRR1);
-       default:
-               printk(KERN_CRIT "wrong setreg %d\n", regnum);
-               break;
-       }
-}
-#else
-
-#define __DEFINE_FUNC(name, code)                                      \
-       extern const char ia64_native_ ## name ## _direct_start[];      \
-       extern const char ia64_native_ ## name ## _direct_end[];        \
-       asm (".align 32\n"                                              \
-            ".proc ia64_native_" #name "_func\n"                       \
-            "ia64_native_" #name "_func:\n"                            \
-            "ia64_native_" #name "_direct_start:\n"                    \
-            code                                                       \
-            "ia64_native_" #name "_direct_end:\n"                      \
-            "br.cond.sptk.many b6\n"                                   \
-            ".endp ia64_native_" #name "_func\n")
-
-#define DEFINE_VOID_FUNC0(name, code)                          \
-       extern void                                             \
-       ia64_native_ ## name ## _func(void);                    \
-       __DEFINE_FUNC(name, code)
-
-#define DEFINE_VOID_FUNC1(name, code)                          \
-       extern void                                             \
-       ia64_native_ ## name ## _func(unsigned long arg);       \
-       __DEFINE_FUNC(name, code)
-
-#define DEFINE_VOID_FUNC1_VOID(name, code)                     \
-       extern void                                             \
-       ia64_native_ ## name ## _func(void *arg);               \
-       __DEFINE_FUNC(name, code)
-
-#define DEFINE_VOID_FUNC2(name, code)                          \
-       extern void                                             \
-       ia64_native_ ## name ## _func(unsigned long arg0,       \
-                                     unsigned long arg1);      \
-       __DEFINE_FUNC(name, code)
-
-#define DEFINE_FUNC0(name, code)               \
-       extern unsigned long                    \
-       ia64_native_ ## name ## _func(void);    \
-       __DEFINE_FUNC(name, code)
-
-#define DEFINE_FUNC1(name, type, code)                 \
-       extern unsigned long                            \
-       ia64_native_ ## name ## _func(type arg);        \
-       __DEFINE_FUNC(name, code)
-
-DEFINE_VOID_FUNC1_VOID(fc,
-                      "fc r8\n");
-DEFINE_VOID_FUNC1(intrin_local_irq_restore,
-                 ";;\n"
-                 "     cmp.ne p6, p7 = r8, r0\n"
-                 ";;\n"
-                 "(p6) ssm psr.i\n"
-                 "(p7) rsm psr.i\n"
-                 ";;\n"
-                 "(p6) srlz.d\n");
-
-DEFINE_VOID_FUNC2(ptcga,
-                 "ptc.ga r8, r9\n");
-DEFINE_VOID_FUNC2(set_rr,
-                 "mov rr[r8] = r9\n");
-
-/* ia64_native_getreg(_IA64_REG_PSR) & IA64_PSR_I */
-DEFINE_FUNC0(get_psr_i,
-            "mov r2 = " __stringify(1 << IA64_PSR_I_BIT) "\n"
-            "mov r8 = psr\n"
-            ";;\n"
-            "and r8 = r2, r8\n");
-
-DEFINE_FUNC1(thash, unsigned long,
-            "thash r8 = r8\n");
-DEFINE_FUNC1(get_cpuid, int,
-            "mov r8 = cpuid[r8]\n");
-DEFINE_FUNC1(get_pmd, int,
-            "mov r8 = pmd[r8]\n");
-DEFINE_FUNC1(get_rr, unsigned long,
-            "mov r8 = rr[r8]\n");
-
-DEFINE_VOID_FUNC0(ssm_i,
-                 "ssm psr.i\n");
-DEFINE_VOID_FUNC0(rsm_i,
-                 "rsm psr.i\n");
-
-extern void
-ia64_native_set_rr0_to_rr4_func(unsigned long val0, unsigned long val1,
-                               unsigned long val2, unsigned long val3,
-                               unsigned long val4);
-__DEFINE_FUNC(set_rr0_to_rr4,
-             "mov rr[r0] = r8\n"
-             "movl r2 = 0x2000000000000000\n"
-             ";;\n"
-             "mov rr[r2] = r9\n"
-             "shl r3 = r2, 1\n"        /* movl r3 = 0x4000000000000000 */
-             ";;\n"
-             "add r2 = r2, r3\n"       /* movl r2 = 0x6000000000000000 */
-             "mov rr[r3] = r10\n"
-             ";;\n"
-             "mov rr[r2] = r11\n"
-             "shl r3 = r3, 1\n"        /* movl r3 = 0x8000000000000000 */
-             ";;\n"
-             "mov rr[r3] = r14\n");
-
-extern unsigned long ia64_native_getreg_func(int regnum);
-asm(".global ia64_native_getreg_func\n");
-#define __DEFINE_GET_REG(id, reg)                      \
-       "mov r2 = " __stringify(_IA64_REG_ ## id) "\n"  \
-       ";;\n"                                          \
-       "cmp.eq p6, p0 = r2, r8\n"                      \
-       ";;\n"                                          \
-       "(p6) mov r8 = " #reg "\n"                      \
-       "(p6) br.cond.sptk.many b6\n"                   \
-       ";;\n"
-#define __DEFINE_GET_AR(id, reg)       __DEFINE_GET_REG(AR_ ## id, ar.reg)
-#define __DEFINE_GET_CR(id, reg)       __DEFINE_GET_REG(CR_ ## id, cr.reg)
-
-__DEFINE_FUNC(getreg,
-             __DEFINE_GET_REG(GP, gp)
-             /*__DEFINE_GET_REG(IP, ip)*/ /* returned ip value shouldn't be constant */
-             __DEFINE_GET_REG(PSR, psr)
-             __DEFINE_GET_REG(TP, tp)
-             __DEFINE_GET_REG(SP, sp)
-
-             __DEFINE_GET_REG(AR_KR0, ar0)
-             __DEFINE_GET_REG(AR_KR1, ar1)
-             __DEFINE_GET_REG(AR_KR2, ar2)
-             __DEFINE_GET_REG(AR_KR3, ar3)
-             __DEFINE_GET_REG(AR_KR4, ar4)
-             __DEFINE_GET_REG(AR_KR5, ar5)
-             __DEFINE_GET_REG(AR_KR6, ar6)
-             __DEFINE_GET_REG(AR_KR7, ar7)
-             __DEFINE_GET_AR(RSC, rsc)
-             __DEFINE_GET_AR(BSP, bsp)
-             __DEFINE_GET_AR(BSPSTORE, bspstore)
-             __DEFINE_GET_AR(RNAT, rnat)
-             __DEFINE_GET_AR(FCR, fcr)
-             __DEFINE_GET_AR(EFLAG, eflag)
-             __DEFINE_GET_AR(CSD, csd)
-             __DEFINE_GET_AR(SSD, ssd)
-             __DEFINE_GET_REG(AR_CFLAG, ar27)
-             __DEFINE_GET_AR(FSR, fsr)
-             __DEFINE_GET_AR(FIR, fir)
-             __DEFINE_GET_AR(FDR, fdr)
-             __DEFINE_GET_AR(CCV, ccv)
-             __DEFINE_GET_AR(UNAT, unat)
-             __DEFINE_GET_AR(FPSR, fpsr)
-             __DEFINE_GET_AR(ITC, itc)
-             __DEFINE_GET_AR(PFS, pfs)
-             __DEFINE_GET_AR(LC, lc)
-             __DEFINE_GET_AR(EC, ec)
-
-             __DEFINE_GET_CR(DCR, dcr)
-             __DEFINE_GET_CR(ITM, itm)
-             __DEFINE_GET_CR(IVA, iva)
-             __DEFINE_GET_CR(PTA, pta)
-             __DEFINE_GET_CR(IPSR, ipsr)
-             __DEFINE_GET_CR(ISR, isr)
-             __DEFINE_GET_CR(IIP, iip)
-             __DEFINE_GET_CR(IFA, ifa)
-             __DEFINE_GET_CR(ITIR, itir)
-             __DEFINE_GET_CR(IIPA, iipa)
-             __DEFINE_GET_CR(IFS, ifs)
-             __DEFINE_GET_CR(IIM, iim)
-             __DEFINE_GET_CR(IHA, iha)
-             __DEFINE_GET_CR(LID, lid)
-             __DEFINE_GET_CR(IVR, ivr)
-             __DEFINE_GET_CR(TPR, tpr)
-             __DEFINE_GET_CR(EOI, eoi)
-             __DEFINE_GET_CR(IRR0, irr0)
-             __DEFINE_GET_CR(IRR1, irr1)
-             __DEFINE_GET_CR(IRR2, irr2)
-             __DEFINE_GET_CR(IRR3, irr3)
-             __DEFINE_GET_CR(ITV, itv)
-             __DEFINE_GET_CR(PMV, pmv)
-             __DEFINE_GET_CR(CMCV, cmcv)
-             __DEFINE_GET_CR(LRR0, lrr0)
-             __DEFINE_GET_CR(LRR1, lrr1)
-
-             "mov r8 = -1\n"   /* unsupported case */
-       );
-
-extern void ia64_native_setreg_func(int regnum, unsigned long val);
-asm(".global ia64_native_setreg_func\n");
-#define __DEFINE_SET_REG(id, reg)                      \
-       "mov r2 = " __stringify(_IA64_REG_ ## id) "\n"  \
-       ";;\n"                                          \
-       "cmp.eq p6, p0 = r2, r9\n"                      \
-       ";;\n"                                          \
-       "(p6) mov " #reg " = r8\n"                      \
-       "(p6) br.cond.sptk.many b6\n"                   \
-       ";;\n"
-#define __DEFINE_SET_AR(id, reg)       __DEFINE_SET_REG(AR_ ## id, ar.reg)
-#define __DEFINE_SET_CR(id, reg)       __DEFINE_SET_REG(CR_ ## id, cr.reg)
-__DEFINE_FUNC(setreg,
-             "mov r2 = " __stringify(_IA64_REG_PSR_L) "\n"
-             ";;\n"
-             "cmp.eq p6, p0 = r2, r9\n"
-             ";;\n"
-             "(p6) mov psr.l = r8\n"
-#ifdef HAVE_SERIALIZE_DIRECTIVE
-             ".serialize.data\n"
-#endif
-             "(p6) br.cond.sptk.many b6\n"
-             __DEFINE_SET_REG(GP, gp)
-             __DEFINE_SET_REG(SP, sp)
-
-             __DEFINE_SET_REG(AR_KR0, ar0)
-             __DEFINE_SET_REG(AR_KR1, ar1)
-             __DEFINE_SET_REG(AR_KR2, ar2)
-             __DEFINE_SET_REG(AR_KR3, ar3)
-             __DEFINE_SET_REG(AR_KR4, ar4)
-             __DEFINE_SET_REG(AR_KR5, ar5)
-             __DEFINE_SET_REG(AR_KR6, ar6)
-             __DEFINE_SET_REG(AR_KR7, ar7)
-             __DEFINE_SET_AR(RSC, rsc)
-             __DEFINE_SET_AR(BSP, bsp)
-             __DEFINE_SET_AR(BSPSTORE, bspstore)
-             __DEFINE_SET_AR(RNAT, rnat)
-             __DEFINE_SET_AR(FCR, fcr)
-             __DEFINE_SET_AR(EFLAG, eflag)
-             __DEFINE_SET_AR(CSD, csd)
-             __DEFINE_SET_AR(SSD, ssd)
-             __DEFINE_SET_REG(AR_CFLAG, ar27)
-             __DEFINE_SET_AR(FSR, fsr)
-             __DEFINE_SET_AR(FIR, fir)
-             __DEFINE_SET_AR(FDR, fdr)
-             __DEFINE_SET_AR(CCV, ccv)
-             __DEFINE_SET_AR(UNAT, unat)
-             __DEFINE_SET_AR(FPSR, fpsr)
-             __DEFINE_SET_AR(ITC, itc)
-             __DEFINE_SET_AR(PFS, pfs)
-             __DEFINE_SET_AR(LC, lc)
-             __DEFINE_SET_AR(EC, ec)
-
-             __DEFINE_SET_CR(DCR, dcr)
-             __DEFINE_SET_CR(ITM, itm)
-             __DEFINE_SET_CR(IVA, iva)
-             __DEFINE_SET_CR(PTA, pta)
-             __DEFINE_SET_CR(IPSR, ipsr)
-             __DEFINE_SET_CR(ISR, isr)
-             __DEFINE_SET_CR(IIP, iip)
-             __DEFINE_SET_CR(IFA, ifa)
-             __DEFINE_SET_CR(ITIR, itir)
-             __DEFINE_SET_CR(IIPA, iipa)
-             __DEFINE_SET_CR(IFS, ifs)
-             __DEFINE_SET_CR(IIM, iim)
-             __DEFINE_SET_CR(IHA, iha)
-             __DEFINE_SET_CR(LID, lid)
-             __DEFINE_SET_CR(IVR, ivr)
-             __DEFINE_SET_CR(TPR, tpr)
-             __DEFINE_SET_CR(EOI, eoi)
-             __DEFINE_SET_CR(IRR0, irr0)
-             __DEFINE_SET_CR(IRR1, irr1)
-             __DEFINE_SET_CR(IRR2, irr2)
-             __DEFINE_SET_CR(IRR3, irr3)
-             __DEFINE_SET_CR(ITV, itv)
-             __DEFINE_SET_CR(PMV, pmv)
-             __DEFINE_SET_CR(CMCV, cmcv)
-             __DEFINE_SET_CR(LRR0, lrr0)
-             __DEFINE_SET_CR(LRR1, lrr1)
-       );
-#endif
-
-struct pv_cpu_ops pv_cpu_ops = {
-       .fc             = ia64_native_fc_func,
-       .thash          = ia64_native_thash_func,
-       .get_cpuid      = ia64_native_get_cpuid_func,
-       .get_pmd        = ia64_native_get_pmd_func,
-       .ptcga          = ia64_native_ptcga_func,
-       .get_rr         = ia64_native_get_rr_func,
-       .set_rr         = ia64_native_set_rr_func,
-       .set_rr0_to_rr4 = ia64_native_set_rr0_to_rr4_func,
-       .ssm_i          = ia64_native_ssm_i_func,
-       .getreg         = ia64_native_getreg_func,
-       .setreg         = ia64_native_setreg_func,
-       .rsm_i          = ia64_native_rsm_i_func,
-       .get_psr_i      = ia64_native_get_psr_i_func,
-       .intrin_local_irq_restore
-                       = ia64_native_intrin_local_irq_restore_func,
-};
-EXPORT_SYMBOL(pv_cpu_ops);
-
-/******************************************************************************
- * replacement of hand written assembly codes.
- */
-
-void
-paravirt_cpu_asm_init(const struct pv_cpu_asm_switch *cpu_asm_switch)
-{
-       extern unsigned long paravirt_switch_to_targ;
-       extern unsigned long paravirt_leave_syscall_targ;
-       extern unsigned long paravirt_work_processed_syscall_targ;
-       extern unsigned long paravirt_leave_kernel_targ;
-
-       paravirt_switch_to_targ = cpu_asm_switch->switch_to;
-       paravirt_leave_syscall_targ = cpu_asm_switch->leave_syscall;
-       paravirt_work_processed_syscall_targ =
-               cpu_asm_switch->work_processed_syscall;
-       paravirt_leave_kernel_targ = cpu_asm_switch->leave_kernel;
-}
-
-/***************************************************************************
- * pv_iosapic_ops
- * iosapic read/write hooks.
- */
-
-static unsigned int
-ia64_native_iosapic_read(char __iomem *iosapic, unsigned int reg)
-{
-       return __ia64_native_iosapic_read(iosapic, reg);
-}
-
-static void
-ia64_native_iosapic_write(char __iomem *iosapic, unsigned int reg, u32 val)
-{
-       __ia64_native_iosapic_write(iosapic, reg, val);
-}
-
-struct pv_iosapic_ops pv_iosapic_ops = {
-       .pcat_compat_init = ia64_native_iosapic_pcat_compat_init,
-       .__get_irq_chip = ia64_native_iosapic_get_irq_chip,
-
-       .__read = ia64_native_iosapic_read,
-       .__write = ia64_native_iosapic_write,
-};
-
-/***************************************************************************
- * pv_irq_ops
- * irq operations
- */
-
-struct pv_irq_ops pv_irq_ops = {
-       .register_ipi = ia64_native_register_ipi,
-
-       .assign_irq_vector = ia64_native_assign_irq_vector,
-       .free_irq_vector = ia64_native_free_irq_vector,
-       .register_percpu_irq = ia64_native_register_percpu_irq,
-
-       .resend_irq = ia64_native_resend_irq,
-};
-
-/***************************************************************************
- * pv_time_ops
- * time operations
- */
-struct static_key paravirt_steal_enabled;
-struct static_key paravirt_steal_rq_enabled;
-
-static int
-ia64_native_do_steal_accounting(unsigned long *new_itm)
-{
-       return 0;
-}
-
-struct pv_time_ops pv_time_ops = {
-       .do_steal_accounting = ia64_native_do_steal_accounting,
-       .sched_clock = ia64_native_sched_clock,
-};
-
-/***************************************************************************
- * binary pacthing
- * pv_init_ops.patch_bundle
- */
-
-#ifdef ASM_SUPPORTED
-#define IA64_NATIVE_PATCH_DEFINE_GET_REG(name, reg)    \
-       __DEFINE_FUNC(get_ ## name,                     \
-                     ";;\n"                            \
-                     "mov r8 = " #reg "\n"             \
-                     ";;\n")
-
-#define IA64_NATIVE_PATCH_DEFINE_SET_REG(name, reg)    \
-       __DEFINE_FUNC(set_ ## name,                     \
-                     ";;\n"                            \
-                     "mov " #reg " = r8\n"             \
-                     ";;\n")
-
-#define IA64_NATIVE_PATCH_DEFINE_REG(name, reg)                \
-       IA64_NATIVE_PATCH_DEFINE_GET_REG(name, reg);    \
-       IA64_NATIVE_PATCH_DEFINE_SET_REG(name, reg)     \
-
-#define IA64_NATIVE_PATCH_DEFINE_AR(name, reg)                 \
-       IA64_NATIVE_PATCH_DEFINE_REG(ar_ ## name, ar.reg)
-
-#define IA64_NATIVE_PATCH_DEFINE_CR(name, reg)                 \
-       IA64_NATIVE_PATCH_DEFINE_REG(cr_ ## name, cr.reg)
-
-
-IA64_NATIVE_PATCH_DEFINE_GET_REG(psr, psr);
-IA64_NATIVE_PATCH_DEFINE_GET_REG(tp, tp);
-
-/* IA64_NATIVE_PATCH_DEFINE_SET_REG(psr_l, psr.l); */
-__DEFINE_FUNC(set_psr_l,
-             ";;\n"
-             "mov psr.l = r8\n"
-#ifdef HAVE_SERIALIZE_DIRECTIVE
-             ".serialize.data\n"
-#endif
-             ";;\n");
-
-IA64_NATIVE_PATCH_DEFINE_REG(gp, gp);
-IA64_NATIVE_PATCH_DEFINE_REG(sp, sp);
-
-IA64_NATIVE_PATCH_DEFINE_REG(kr0, ar0);
-IA64_NATIVE_PATCH_DEFINE_REG(kr1, ar1);
-IA64_NATIVE_PATCH_DEFINE_REG(kr2, ar2);
-IA64_NATIVE_PATCH_DEFINE_REG(kr3, ar3);
-IA64_NATIVE_PATCH_DEFINE_REG(kr4, ar4);
-IA64_NATIVE_PATCH_DEFINE_REG(kr5, ar5);
-IA64_NATIVE_PATCH_DEFINE_REG(kr6, ar6);
-IA64_NATIVE_PATCH_DEFINE_REG(kr7, ar7);
-
-IA64_NATIVE_PATCH_DEFINE_AR(rsc, rsc);
-IA64_NATIVE_PATCH_DEFINE_AR(bsp, bsp);
-IA64_NATIVE_PATCH_DEFINE_AR(bspstore, bspstore);
-IA64_NATIVE_PATCH_DEFINE_AR(rnat, rnat);
-IA64_NATIVE_PATCH_DEFINE_AR(fcr, fcr);
-IA64_NATIVE_PATCH_DEFINE_AR(eflag, eflag);
-IA64_NATIVE_PATCH_DEFINE_AR(csd, csd);
-IA64_NATIVE_PATCH_DEFINE_AR(ssd, ssd);
-IA64_NATIVE_PATCH_DEFINE_REG(ar27, ar27);
-IA64_NATIVE_PATCH_DEFINE_AR(fsr, fsr);
-IA64_NATIVE_PATCH_DEFINE_AR(fir, fir);
-IA64_NATIVE_PATCH_DEFINE_AR(fdr, fdr);
-IA64_NATIVE_PATCH_DEFINE_AR(ccv, ccv);
-IA64_NATIVE_PATCH_DEFINE_AR(unat, unat);
-IA64_NATIVE_PATCH_DEFINE_AR(fpsr, fpsr);
-IA64_NATIVE_PATCH_DEFINE_AR(itc, itc);
-IA64_NATIVE_PATCH_DEFINE_AR(pfs, pfs);
-IA64_NATIVE_PATCH_DEFINE_AR(lc, lc);
-IA64_NATIVE_PATCH_DEFINE_AR(ec, ec);
-
-IA64_NATIVE_PATCH_DEFINE_CR(dcr, dcr);
-IA64_NATIVE_PATCH_DEFINE_CR(itm, itm);
-IA64_NATIVE_PATCH_DEFINE_CR(iva, iva);
-IA64_NATIVE_PATCH_DEFINE_CR(pta, pta);
-IA64_NATIVE_PATCH_DEFINE_CR(ipsr, ipsr);
-IA64_NATIVE_PATCH_DEFINE_CR(isr, isr);
-IA64_NATIVE_PATCH_DEFINE_CR(iip, iip);
-IA64_NATIVE_PATCH_DEFINE_CR(ifa, ifa);
-IA64_NATIVE_PATCH_DEFINE_CR(itir, itir);
-IA64_NATIVE_PATCH_DEFINE_CR(iipa, iipa);
-IA64_NATIVE_PATCH_DEFINE_CR(ifs, ifs);
-IA64_NATIVE_PATCH_DEFINE_CR(iim, iim);
-IA64_NATIVE_PATCH_DEFINE_CR(iha, iha);
-IA64_NATIVE_PATCH_DEFINE_CR(lid, lid);
-IA64_NATIVE_PATCH_DEFINE_CR(ivr, ivr);
-IA64_NATIVE_PATCH_DEFINE_CR(tpr, tpr);
-IA64_NATIVE_PATCH_DEFINE_CR(eoi, eoi);
-IA64_NATIVE_PATCH_DEFINE_CR(irr0, irr0);
-IA64_NATIVE_PATCH_DEFINE_CR(irr1, irr1);
-IA64_NATIVE_PATCH_DEFINE_CR(irr2, irr2);
-IA64_NATIVE_PATCH_DEFINE_CR(irr3, irr3);
-IA64_NATIVE_PATCH_DEFINE_CR(itv, itv);
-IA64_NATIVE_PATCH_DEFINE_CR(pmv, pmv);
-IA64_NATIVE_PATCH_DEFINE_CR(cmcv, cmcv);
-IA64_NATIVE_PATCH_DEFINE_CR(lrr0, lrr0);
-IA64_NATIVE_PATCH_DEFINE_CR(lrr1, lrr1);
-
-static const struct paravirt_patch_bundle_elem ia64_native_patch_bundle_elems[]
-__initdata_or_module =
-{
-#define IA64_NATIVE_PATCH_BUNDLE_ELEM(name, type)              \
-       {                                                       \
-               (void*)ia64_native_ ## name ## _direct_start,   \
-               (void*)ia64_native_ ## name ## _direct_end,     \
-               PARAVIRT_PATCH_TYPE_ ## type,                   \
-       }
-
-       IA64_NATIVE_PATCH_BUNDLE_ELEM(fc, FC),
-       IA64_NATIVE_PATCH_BUNDLE_ELEM(thash, THASH),
-       IA64_NATIVE_PATCH_BUNDLE_ELEM(get_cpuid, GET_CPUID),
-       IA64_NATIVE_PATCH_BUNDLE_ELEM(get_pmd, GET_PMD),
-       IA64_NATIVE_PATCH_BUNDLE_ELEM(ptcga, PTCGA),
-       IA64_NATIVE_PATCH_BUNDLE_ELEM(get_rr, GET_RR),
-       IA64_NATIVE_PATCH_BUNDLE_ELEM(set_rr, SET_RR),
-       IA64_NATIVE_PATCH_BUNDLE_ELEM(set_rr0_to_rr4, SET_RR0_TO_RR4),
-       IA64_NATIVE_PATCH_BUNDLE_ELEM(ssm_i, SSM_I),
-       IA64_NATIVE_PATCH_BUNDLE_ELEM(rsm_i, RSM_I),
-       IA64_NATIVE_PATCH_BUNDLE_ELEM(get_psr_i, GET_PSR_I),
-       IA64_NATIVE_PATCH_BUNDLE_ELEM(intrin_local_irq_restore,
-                                     INTRIN_LOCAL_IRQ_RESTORE),
-
-#define IA64_NATIVE_PATCH_BUNDLE_ELEM_GETREG(name, reg)                        \
-       {                                                               \
-               (void*)ia64_native_get_ ## name ## _direct_start,       \
-               (void*)ia64_native_get_ ## name ## _direct_end,         \
-               PARAVIRT_PATCH_TYPE_GETREG + _IA64_REG_ ## reg,         \
-       }
-
-#define IA64_NATIVE_PATCH_BUNDLE_ELEM_SETREG(name, reg)                        \
-       {                                                               \
-               (void*)ia64_native_set_ ## name ## _direct_start,       \
-               (void*)ia64_native_set_ ## name ## _direct_end,         \
-               PARAVIRT_PATCH_TYPE_SETREG + _IA64_REG_ ## reg,         \
-       }
-
-#define IA64_NATIVE_PATCH_BUNDLE_ELEM_REG(name, reg)           \
-       IA64_NATIVE_PATCH_BUNDLE_ELEM_GETREG(name, reg),        \
-       IA64_NATIVE_PATCH_BUNDLE_ELEM_SETREG(name, reg)         \
-
-#define IA64_NATIVE_PATCH_BUNDLE_ELEM_AR(name, reg)            \
-       IA64_NATIVE_PATCH_BUNDLE_ELEM_REG(ar_ ## name, AR_ ## reg)
-
-#define IA64_NATIVE_PATCH_BUNDLE_ELEM_CR(name, reg)            \
-       IA64_NATIVE_PATCH_BUNDLE_ELEM_REG(cr_ ## name, CR_ ## reg)
-
-       IA64_NATIVE_PATCH_BUNDLE_ELEM_GETREG(psr, PSR),
-       IA64_NATIVE_PATCH_BUNDLE_ELEM_GETREG(tp, TP),
-
-       IA64_NATIVE_PATCH_BUNDLE_ELEM_SETREG(psr_l, PSR_L),
-
-       IA64_NATIVE_PATCH_BUNDLE_ELEM_REG(gp, GP),
-       IA64_NATIVE_PATCH_BUNDLE_ELEM_REG(sp, SP),
-
-       IA64_NATIVE_PATCH_BUNDLE_ELEM_REG(kr0, AR_KR0),
-       IA64_NATIVE_PATCH_BUNDLE_ELEM_REG(kr1, AR_KR1),
-       IA64_NATIVE_PATCH_BUNDLE_ELEM_REG(kr2, AR_KR2),
-       IA64_NATIVE_PATCH_BUNDLE_ELEM_REG(kr3, AR_KR3),
-       IA64_NATIVE_PATCH_BUNDLE_ELEM_REG(kr4, AR_KR4),
-       IA64_NATIVE_PATCH_BUNDLE_ELEM_REG(kr5, AR_KR5),
-       IA64_NATIVE_PATCH_BUNDLE_ELEM_REG(kr6, AR_KR6),
-       IA64_NATIVE_PATCH_BUNDLE_ELEM_REG(kr7, AR_KR7),
-
-       IA64_NATIVE_PATCH_BUNDLE_ELEM_AR(rsc, RSC),
-       IA64_NATIVE_PATCH_BUNDLE_ELEM_AR(bsp, BSP),
-       IA64_NATIVE_PATCH_BUNDLE_ELEM_AR(bspstore, BSPSTORE),
-       IA64_NATIVE_PATCH_BUNDLE_ELEM_AR(rnat, RNAT),
-       IA64_NATIVE_PATCH_BUNDLE_ELEM_AR(fcr, FCR),
-       IA64_NATIVE_PATCH_BUNDLE_ELEM_AR(eflag, EFLAG),
-       IA64_NATIVE_PATCH_BUNDLE_ELEM_AR(csd, CSD),
-       IA64_NATIVE_PATCH_BUNDLE_ELEM_AR(ssd, SSD),
-       IA64_NATIVE_PATCH_BUNDLE_ELEM_REG(ar27, AR_CFLAG),
-       IA64_NATIVE_PATCH_BUNDLE_ELEM_AR(fsr, FSR),
-       IA64_NATIVE_PATCH_BUNDLE_ELEM_AR(fir, FIR),
-       IA64_NATIVE_PATCH_BUNDLE_ELEM_AR(fdr, FDR),
-       IA64_NATIVE_PATCH_BUNDLE_ELEM_AR(ccv, CCV),
-       IA64_NATIVE_PATCH_BUNDLE_ELEM_AR(unat, UNAT),
-       IA64_NATIVE_PATCH_BUNDLE_ELEM_AR(fpsr, FPSR),
-       IA64_NATIVE_PATCH_BUNDLE_ELEM_AR(itc, ITC),
-       IA64_NATIVE_PATCH_BUNDLE_ELEM_AR(pfs, PFS),
-       IA64_NATIVE_PATCH_BUNDLE_ELEM_AR(lc, LC),
-       IA64_NATIVE_PATCH_BUNDLE_ELEM_AR(ec, EC),
-
-       IA64_NATIVE_PATCH_BUNDLE_ELEM_CR(dcr, DCR),
-       IA64_NATIVE_PATCH_BUNDLE_ELEM_CR(itm, ITM),
-       IA64_NATIVE_PATCH_BUNDLE_ELEM_CR(iva, IVA),
-       IA64_NATIVE_PATCH_BUNDLE_ELEM_CR(pta, PTA),
-       IA64_NATIVE_PATCH_BUNDLE_ELEM_CR(ipsr, IPSR),
-       IA64_NATIVE_PATCH_BUNDLE_ELEM_CR(isr, ISR),
-       IA64_NATIVE_PATCH_BUNDLE_ELEM_CR(iip, IIP),
-       IA64_NATIVE_PATCH_BUNDLE_ELEM_CR(ifa, IFA),
-       IA64_NATIVE_PATCH_BUNDLE_ELEM_CR(itir, ITIR),
-       IA64_NATIVE_PATCH_BUNDLE_ELEM_CR(iipa, IIPA),
-       IA64_NATIVE_PATCH_BUNDLE_ELEM_CR(ifs, IFS),
-       IA64_NATIVE_PATCH_BUNDLE_ELEM_CR(iim, IIM),
-       IA64_NATIVE_PATCH_BUNDLE_ELEM_CR(iha, IHA),
-       IA64_NATIVE_PATCH_BUNDLE_ELEM_CR(lid, LID),
-       IA64_NATIVE_PATCH_BUNDLE_ELEM_CR(ivr, IVR),
-       IA64_NATIVE_PATCH_BUNDLE_ELEM_CR(tpr, TPR),
-       IA64_NATIVE_PATCH_BUNDLE_ELEM_CR(eoi, EOI),
-       IA64_NATIVE_PATCH_BUNDLE_ELEM_CR(irr0, IRR0),
-       IA64_NATIVE_PATCH_BUNDLE_ELEM_CR(irr1, IRR1),
-       IA64_NATIVE_PATCH_BUNDLE_ELEM_CR(irr2, IRR2),
-       IA64_NATIVE_PATCH_BUNDLE_ELEM_CR(irr3, IRR3),
-       IA64_NATIVE_PATCH_BUNDLE_ELEM_CR(itv, ITV),
-       IA64_NATIVE_PATCH_BUNDLE_ELEM_CR(pmv, PMV),
-       IA64_NATIVE_PATCH_BUNDLE_ELEM_CR(cmcv, CMCV),
-       IA64_NATIVE_PATCH_BUNDLE_ELEM_CR(lrr0, LRR0),
-       IA64_NATIVE_PATCH_BUNDLE_ELEM_CR(lrr1, LRR1),
-};
-
-unsigned long __init_or_module
-ia64_native_patch_bundle(void *sbundle, void *ebundle, unsigned long type)
-{
-       const unsigned long nelems = sizeof(ia64_native_patch_bundle_elems) /
-               sizeof(ia64_native_patch_bundle_elems[0]);
-
-       return __paravirt_patch_apply_bundle(sbundle, ebundle, type,
-                                             ia64_native_patch_bundle_elems,
-                                             nelems, NULL);
-}
-#endif /* ASM_SUPPOTED */
-
-extern const char ia64_native_switch_to[];
-extern const char ia64_native_leave_syscall[];
-extern const char ia64_native_work_processed_syscall[];
-extern const char ia64_native_leave_kernel[];
-
-const struct paravirt_patch_branch_target ia64_native_branch_target[]
-__initconst = {
-#define PARAVIRT_BR_TARGET(name, type)                 \
-       {                                               \
-               ia64_native_ ## name,                   \
-               PARAVIRT_PATCH_TYPE_BR_ ## type,        \
-       }
-       PARAVIRT_BR_TARGET(switch_to, SWITCH_TO),
-       PARAVIRT_BR_TARGET(leave_syscall, LEAVE_SYSCALL),
-       PARAVIRT_BR_TARGET(work_processed_syscall, WORK_PROCESSED_SYSCALL),
-       PARAVIRT_BR_TARGET(leave_kernel, LEAVE_KERNEL),
-};
-
-static void __init
-ia64_native_patch_branch(unsigned long tag, unsigned long type)
-{
-       const unsigned long nelem =
-               sizeof(ia64_native_branch_target) /
-               sizeof(ia64_native_branch_target[0]);
-       __paravirt_patch_apply_branch(tag, type,
-                                     ia64_native_branch_target, nelem);
-}
diff --git a/arch/ia64/kernel/paravirt_inst.h b/arch/ia64/kernel/paravirt_inst.h
deleted file mode 100644 (file)
index 1ad7512..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-/******************************************************************************
- * linux/arch/ia64/xen/paravirt_inst.h
- *
- * Copyright (c) 2008 Isaku Yamahata <yamahata at valinux co jp>
- *                    VA Linux Systems Japan K.K.
- *
- * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- *
- */
-
-#ifdef __IA64_ASM_PARAVIRTUALIZED_PVCHECK
-#include <asm/native/pvchk_inst.h>
-#else
-#include <asm/native/inst.h>
-#endif
-
diff --git a/arch/ia64/kernel/paravirt_patch.c b/arch/ia64/kernel/paravirt_patch.c
deleted file mode 100644 (file)
index bfdfef1..0000000
+++ /dev/null
@@ -1,514 +0,0 @@
-/******************************************************************************
- * linux/arch/ia64/xen/paravirt_patch.c
- *
- * Copyright (c) 2008 Isaku Yamahata <yamahata at valinux co jp>
- *                    VA Linux Systems Japan K.K.
- *
- * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- *
- */
-
-#include <linux/init.h>
-#include <asm/intrinsics.h>
-#include <asm/kprobes.h>
-#include <asm/paravirt.h>
-#include <asm/paravirt_patch.h>
-
-typedef union ia64_inst {
-        struct {
-               unsigned long long qp : 6;
-               unsigned long long : 31;
-               unsigned long long opcode : 4;
-               unsigned long long reserved : 23;
-        } generic;
-        unsigned long long l;
-} ia64_inst_t;
-
-/*
- * flush_icache_range() can't be used here.
- * we are here before cpu_init() which initializes
- * ia64_i_cache_stride_shift. flush_icache_range() uses it.
- */
-void __init_or_module
-paravirt_flush_i_cache_range(const void *instr, unsigned long size)
-{
-       extern void paravirt_fc_i(const void *addr);
-       unsigned long i;
-
-       for (i = 0; i < size; i += sizeof(bundle_t))
-               paravirt_fc_i(instr + i);
-}
-
-bundle_t* __init_or_module
-paravirt_get_bundle(unsigned long tag)
-{
-       return (bundle_t *)(tag & ~3UL);
-}
-
-unsigned long __init_or_module
-paravirt_get_slot(unsigned long tag)
-{
-       return tag & 3UL;
-}
-
-unsigned long __init_or_module
-paravirt_get_num_inst(unsigned long stag, unsigned long etag)
-{
-       bundle_t *sbundle = paravirt_get_bundle(stag);
-       unsigned long sslot = paravirt_get_slot(stag);
-       bundle_t *ebundle = paravirt_get_bundle(etag);
-       unsigned long eslot = paravirt_get_slot(etag);
-
-       return (ebundle - sbundle) * 3 + eslot - sslot + 1;
-}
-
-unsigned long __init_or_module
-paravirt_get_next_tag(unsigned long tag)
-{
-       unsigned long slot = paravirt_get_slot(tag);
-
-       switch (slot) {
-       case 0:
-       case 1:
-               return tag + 1;
-       case 2: {
-               bundle_t *bundle = paravirt_get_bundle(tag);
-               return (unsigned long)(bundle + 1);
-       }
-       default:
-               BUG();
-       }
-       /* NOTREACHED */
-}
-
-ia64_inst_t __init_or_module
-paravirt_read_slot0(const bundle_t *bundle)
-{
-       ia64_inst_t inst;
-       inst.l = bundle->quad0.slot0;
-       return inst;
-}
-
-ia64_inst_t __init_or_module
-paravirt_read_slot1(const bundle_t *bundle)
-{
-       ia64_inst_t inst;
-       inst.l = bundle->quad0.slot1_p0 |
-               ((unsigned long long)bundle->quad1.slot1_p1 << 18UL);
-       return inst;
-}
-
-ia64_inst_t __init_or_module
-paravirt_read_slot2(const bundle_t *bundle)
-{
-       ia64_inst_t inst;
-       inst.l = bundle->quad1.slot2;
-       return inst;
-}
-
-ia64_inst_t __init_or_module
-paravirt_read_inst(unsigned long tag)
-{
-       bundle_t *bundle = paravirt_get_bundle(tag);
-       unsigned long slot = paravirt_get_slot(tag);
-
-       switch (slot) {
-       case 0:
-               return paravirt_read_slot0(bundle);
-       case 1:
-               return paravirt_read_slot1(bundle);
-       case 2:
-               return paravirt_read_slot2(bundle);
-       default:
-               BUG();
-       }
-       /* NOTREACHED */
-}
-
-void __init_or_module
-paravirt_write_slot0(bundle_t *bundle, ia64_inst_t inst)
-{
-       bundle->quad0.slot0 = inst.l;
-}
-
-void __init_or_module
-paravirt_write_slot1(bundle_t *bundle, ia64_inst_t inst)
-{
-       bundle->quad0.slot1_p0 = inst.l;
-       bundle->quad1.slot1_p1 = inst.l >> 18UL;
-}
-
-void __init_or_module
-paravirt_write_slot2(bundle_t *bundle, ia64_inst_t inst)
-{
-       bundle->quad1.slot2 = inst.l;
-}
-
-void __init_or_module
-paravirt_write_inst(unsigned long tag, ia64_inst_t inst)
-{
-       bundle_t *bundle = paravirt_get_bundle(tag);
-       unsigned long slot = paravirt_get_slot(tag);
-
-       switch (slot) {
-       case 0:
-               paravirt_write_slot0(bundle, inst);
-               break;
-       case 1:
-               paravirt_write_slot1(bundle, inst);
-               break;
-       case 2:
-               paravirt_write_slot2(bundle, inst);
-               break;
-       default:
-               BUG();
-               break;
-       }
-       paravirt_flush_i_cache_range(bundle, sizeof(*bundle));
-}
-
-/* for debug */
-void
-paravirt_print_bundle(const bundle_t *bundle)
-{
-       const unsigned long *quad = (const unsigned long *)bundle;
-       ia64_inst_t slot0 = paravirt_read_slot0(bundle);
-       ia64_inst_t slot1 = paravirt_read_slot1(bundle);
-       ia64_inst_t slot2 = paravirt_read_slot2(bundle);
-
-       printk(KERN_DEBUG
-              "bundle 0x%p 0x%016lx 0x%016lx\n", bundle, quad[0], quad[1]);
-       printk(KERN_DEBUG
-              "bundle template 0x%x\n",
-              bundle->quad0.template);
-       printk(KERN_DEBUG
-              "slot0 0x%lx slot1_p0 0x%lx slot1_p1 0x%lx slot2 0x%lx\n",
-              (unsigned long)bundle->quad0.slot0,
-              (unsigned long)bundle->quad0.slot1_p0,
-              (unsigned long)bundle->quad1.slot1_p1,
-              (unsigned long)bundle->quad1.slot2);
-       printk(KERN_DEBUG
-              "slot0 0x%016llx slot1 0x%016llx slot2 0x%016llx\n",
-              slot0.l, slot1.l, slot2.l);
-}
-
-static int noreplace_paravirt __init_or_module = 0;
-
-static int __init setup_noreplace_paravirt(char *str)
-{
-       noreplace_paravirt = 1;
-       return 1;
-}
-__setup("noreplace-paravirt", setup_noreplace_paravirt);
-
-#ifdef ASM_SUPPORTED
-static void __init_or_module
-fill_nop_bundle(void *sbundle, void *ebundle)
-{
-       extern const char paravirt_nop_bundle[];
-       extern const unsigned long paravirt_nop_bundle_size;
-
-       void *bundle = sbundle;
-
-       BUG_ON((((unsigned long)sbundle) % sizeof(bundle_t)) != 0);
-       BUG_ON((((unsigned long)ebundle) % sizeof(bundle_t)) != 0);
-
-       while (bundle < ebundle) {
-               memcpy(bundle, paravirt_nop_bundle, paravirt_nop_bundle_size);
-
-               bundle += paravirt_nop_bundle_size;
-       }
-}
-
-/* helper function */
-unsigned long __init_or_module
-__paravirt_patch_apply_bundle(void *sbundle, void *ebundle, unsigned long type,
-                             const struct paravirt_patch_bundle_elem *elems,
-                             unsigned long nelems,
-                             const struct paravirt_patch_bundle_elem **found)
-{
-       unsigned long used = 0;
-       unsigned long i;
-
-       BUG_ON((((unsigned long)sbundle) % sizeof(bundle_t)) != 0);
-       BUG_ON((((unsigned long)ebundle) % sizeof(bundle_t)) != 0);
-
-       found = NULL;
-       for (i = 0; i < nelems; i++) {
-               const struct paravirt_patch_bundle_elem *p = &elems[i];
-               if (p->type == type) {
-                       unsigned long need = p->ebundle - p->sbundle;
-                       unsigned long room = ebundle - sbundle;
-
-                       if (found != NULL)
-                               *found = p;
-
-                       if (room < need) {
-                               /* no room to replace. skip it */
-                               printk(KERN_DEBUG
-                                      "the space is too small to put "
-                                      "bundles. type %ld need %ld room %ld\n",
-                                      type, need, room);
-                               break;
-                       }
-
-                       used = need;
-                       memcpy(sbundle, p->sbundle, used);
-                       break;
-               }
-       }
-
-       return used;
-}
-
-void __init_or_module
-paravirt_patch_apply_bundle(const struct paravirt_patch_site_bundle *start,
-                           const struct paravirt_patch_site_bundle *end)
-{
-       const struct paravirt_patch_site_bundle *p;
-
-       if (noreplace_paravirt)
-               return;
-       if (pv_init_ops.patch_bundle == NULL)
-               return;
-
-       for (p = start; p < end; p++) {
-               unsigned long used;
-
-               used = (*pv_init_ops.patch_bundle)(p->sbundle, p->ebundle,
-                                                  p->type);
-               if (used == 0)
-                       continue;
-
-               fill_nop_bundle(p->sbundle + used, p->ebundle);
-               paravirt_flush_i_cache_range(p->sbundle,
-                                            p->ebundle - p->sbundle);
-       }
-       ia64_sync_i();
-       ia64_srlz_i();
-}
-
-/*
- * nop.i, nop.m, nop.f instruction are same format.
- * but nop.b has differennt format.
- * This doesn't support nop.b for now.
- */
-static void __init_or_module
-fill_nop_inst(unsigned long stag, unsigned long etag)
-{
-       extern const bundle_t paravirt_nop_mfi_inst_bundle[];
-       unsigned long tag;
-       const ia64_inst_t nop_inst =
-               paravirt_read_slot0(paravirt_nop_mfi_inst_bundle);
-
-       for (tag = stag; tag < etag; tag = paravirt_get_next_tag(tag))
-               paravirt_write_inst(tag, nop_inst);
-}
-
-void __init_or_module
-paravirt_patch_apply_inst(const struct paravirt_patch_site_inst *start,
-                         const struct paravirt_patch_site_inst *end)
-{
-       const struct paravirt_patch_site_inst *p;
-
-       if (noreplace_paravirt)
-               return;
-       if (pv_init_ops.patch_inst == NULL)
-               return;
-
-       for (p = start; p < end; p++) {
-               unsigned long tag;
-               bundle_t *sbundle;
-               bundle_t *ebundle;
-
-               tag = (*pv_init_ops.patch_inst)(p->stag, p->etag, p->type);
-               if (tag == p->stag)
-                       continue;
-
-               fill_nop_inst(tag, p->etag);
-               sbundle = paravirt_get_bundle(p->stag);
-               ebundle = paravirt_get_bundle(p->etag) + 1;
-               paravirt_flush_i_cache_range(sbundle, (ebundle - sbundle) *
-                                            sizeof(bundle_t));
-       }
-       ia64_sync_i();
-       ia64_srlz_i();
-}
-#endif /* ASM_SUPPOTED */
-
-/* brl.cond.sptk.many <target64> X3 */
-typedef union inst_x3_op {
-       ia64_inst_t inst;
-       struct {
-               unsigned long qp: 6;
-               unsigned long btyp: 3;
-               unsigned long unused: 3;
-               unsigned long p: 1;
-               unsigned long imm20b: 20;
-               unsigned long wh: 2;
-               unsigned long d: 1;
-               unsigned long i: 1;
-               unsigned long opcode: 4;
-       };
-       unsigned long l;
-} inst_x3_op_t;
-
-typedef union inst_x3_imm {
-       ia64_inst_t inst;
-       struct {
-               unsigned long unused: 2;
-               unsigned long imm39: 39;
-       };
-       unsigned long l;
-} inst_x3_imm_t;
-
-void __init_or_module
-paravirt_patch_reloc_brl(unsigned long tag, const void *target)
-{
-       unsigned long tag_op = paravirt_get_next_tag(tag);
-       unsigned long tag_imm = tag;
-       bundle_t *bundle = paravirt_get_bundle(tag);
-
-       ia64_inst_t inst_op = paravirt_read_inst(tag_op);
-       ia64_inst_t inst_imm = paravirt_read_inst(tag_imm);
-
-       inst_x3_op_t inst_x3_op = { .l = inst_op.l };
-       inst_x3_imm_t inst_x3_imm = { .l = inst_imm.l };
-
-       unsigned long imm60 =
-               ((unsigned long)target - (unsigned long)bundle) >> 4;
-
-       BUG_ON(paravirt_get_slot(tag) != 1); /* MLX */
-       BUG_ON(((unsigned long)target & (sizeof(bundle_t) - 1)) != 0);
-
-       /* imm60[59] 1bit */
-       inst_x3_op.i = (imm60 >> 59) & 1;
-       /* imm60[19:0] 20bit */
-       inst_x3_op.imm20b = imm60 & ((1UL << 20) - 1);
-       /* imm60[58:20] 39bit */
-       inst_x3_imm.imm39 = (imm60 >> 20) & ((1UL << 39) - 1);
-
-       inst_op.l = inst_x3_op.l;
-       inst_imm.l = inst_x3_imm.l;
-
-       paravirt_write_inst(tag_op, inst_op);
-       paravirt_write_inst(tag_imm, inst_imm);
-}
-
-/* br.cond.sptk.many <target25>        B1 */
-typedef union inst_b1 {
-       ia64_inst_t inst;
-       struct {
-               unsigned long qp: 6;
-               unsigned long btype: 3;
-               unsigned long unused: 3;
-               unsigned long p: 1;
-               unsigned long imm20b: 20;
-               unsigned long wh: 2;
-               unsigned long d: 1;
-               unsigned long s: 1;
-               unsigned long opcode: 4;
-       };
-       unsigned long l;
-} inst_b1_t;
-
-void __init
-paravirt_patch_reloc_br(unsigned long tag, const void *target)
-{
-       bundle_t *bundle = paravirt_get_bundle(tag);
-       ia64_inst_t inst = paravirt_read_inst(tag);
-       unsigned long target25 = (unsigned long)target - (unsigned long)bundle;
-       inst_b1_t inst_b1;
-
-       BUG_ON(((unsigned long)target & (sizeof(bundle_t) - 1)) != 0);
-
-       inst_b1.l = inst.l;
-       if (target25 & (1UL << 63))
-               inst_b1.s = 1;
-       else
-               inst_b1.s = 0;
-
-       inst_b1.imm20b = target25 >> 4;
-       inst.l = inst_b1.l;
-
-       paravirt_write_inst(tag, inst);
-}
-
-void __init
-__paravirt_patch_apply_branch(
-       unsigned long tag, unsigned long type,
-       const struct paravirt_patch_branch_target *entries,
-       unsigned int nr_entries)
-{
-       unsigned int i;
-       for (i = 0; i < nr_entries; i++) {
-               if (entries[i].type == type) {
-                       paravirt_patch_reloc_br(tag, entries[i].entry);
-                       break;
-               }
-       }
-}
-
-static void __init
-paravirt_patch_apply_branch(const struct paravirt_patch_site_branch *start,
-                           const struct paravirt_patch_site_branch *end)
-{
-       const struct paravirt_patch_site_branch *p;
-
-       if (noreplace_paravirt)
-               return;
-       if (pv_init_ops.patch_branch == NULL)
-               return;
-
-       for (p = start; p < end; p++)
-               (*pv_init_ops.patch_branch)(p->tag, p->type);
-
-       ia64_sync_i();
-       ia64_srlz_i();
-}
-
-void __init
-paravirt_patch_apply(void)
-{
-       extern const char __start_paravirt_bundles[];
-       extern const char __stop_paravirt_bundles[];
-       extern const char __start_paravirt_insts[];
-       extern const char __stop_paravirt_insts[];
-       extern const char __start_paravirt_branches[];
-       extern const char __stop_paravirt_branches[];
-
-       paravirt_patch_apply_bundle((const struct paravirt_patch_site_bundle *)
-                                   __start_paravirt_bundles,
-                                   (const struct paravirt_patch_site_bundle *)
-                                   __stop_paravirt_bundles);
-       paravirt_patch_apply_inst((const struct paravirt_patch_site_inst *)
-                                 __start_paravirt_insts,
-                                 (const struct paravirt_patch_site_inst *)
-                                 __stop_paravirt_insts);
-       paravirt_patch_apply_branch((const struct paravirt_patch_site_branch *)
-                                   __start_paravirt_branches,
-                                   (const struct paravirt_patch_site_branch *)
-                                   __stop_paravirt_branches);
-}
-
-/*
- * Local variables:
- * mode: C
- * c-set-style: "linux"
- * c-basic-offset: 8
- * tab-width: 8
- * indent-tabs-mode: t
- * End:
- */
diff --git a/arch/ia64/kernel/paravirt_patchlist.c b/arch/ia64/kernel/paravirt_patchlist.c
deleted file mode 100644 (file)
index 0a70720..0000000
+++ /dev/null
@@ -1,81 +0,0 @@
-/******************************************************************************
- * Copyright (c) 2008 Isaku Yamahata <yamahata at valinux co jp>
- *                    VA Linux Systems Japan K.K.
- *
- * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- *
- */
-
-#include <linux/bug.h>
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <asm/paravirt.h>
-
-#define DECLARE(name)                                          \
-       extern unsigned long                                    \
-               __ia64_native_start_gate_##name##_patchlist[];  \
-       extern unsigned long                                    \
-               __ia64_native_end_gate_##name##_patchlist[]
-
-DECLARE(fsyscall);
-DECLARE(brl_fsys_bubble_down);
-DECLARE(vtop);
-DECLARE(mckinley_e9);
-
-extern unsigned long __start_gate_section[];
-
-#define ASSIGN(name)                                                       \
-       .start_##name##_patchlist =                                         \
-               (unsigned long)__ia64_native_start_gate_##name##_patchlist, \
-       .end_##name##_patchlist =                                           \
-               (unsigned long)__ia64_native_end_gate_##name##_patchlist
-
-struct pv_patchdata pv_patchdata __initdata = {
-       ASSIGN(fsyscall),
-       ASSIGN(brl_fsys_bubble_down),
-       ASSIGN(vtop),
-       ASSIGN(mckinley_e9),
-
-       .gate_section = (void*)__start_gate_section,
-};
-
-
-unsigned long __init
-paravirt_get_gate_patchlist(enum pv_gate_patchlist type)
-{
-
-#define CASE(NAME, name)                                       \
-       case PV_GATE_START_##NAME:                              \
-               return pv_patchdata.start_##name##_patchlist;   \
-       case PV_GATE_END_##NAME:                                \
-               return pv_patchdata.end_##name##_patchlist;     \
-
-       switch (type) {
-               CASE(FSYSCALL, fsyscall);
-               CASE(BRL_FSYS_BUBBLE_DOWN, brl_fsys_bubble_down);
-               CASE(VTOP, vtop);
-               CASE(MCKINLEY_E9, mckinley_e9);
-       default:
-               BUG();
-               break;
-       }
-       return 0;
-}
-
-void * __init
-paravirt_get_gate_section(void)
-{
-       return pv_patchdata.gate_section;
-}
diff --git a/arch/ia64/kernel/paravirt_patchlist.h b/arch/ia64/kernel/paravirt_patchlist.h
deleted file mode 100644 (file)
index 67cffc3..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-/******************************************************************************
- * linux/arch/ia64/xen/paravirt_patchlist.h
- *
- * Copyright (c) 2008 Isaku Yamahata <yamahata at valinux co jp>
- *                    VA Linux Systems Japan K.K.
- *
- * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- *
- */
-
-#include <asm/native/patchlist.h>
-
diff --git a/arch/ia64/kernel/paravirtentry.S b/arch/ia64/kernel/paravirtentry.S
deleted file mode 100644 (file)
index 92d880c..0000000
+++ /dev/null
@@ -1,121 +0,0 @@
-/******************************************************************************
- * linux/arch/ia64/xen/paravirtentry.S
- *
- * Copyright (c) 2008 Isaku Yamahata <yamahata at valinux co jp>
- *                    VA Linux Systems Japan K.K.
- *
- * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- *
- */
-
-#include <linux/init.h>
-#include <asm/asmmacro.h>
-#include <asm/asm-offsets.h>
-#include <asm/paravirt_privop.h>
-#include <asm/paravirt_patch.h>
-#include "entry.h"
-
-#define DATA8(sym, init_value)                 \
-       .pushsection .data..read_mostly ;       \
-       .align 8 ;                              \
-       .global sym ;                           \
-       sym: ;                                  \
-       data8 init_value ;                      \
-       .popsection
-
-#define BRANCH(targ, reg, breg, type)                                  \
-       PARAVIRT_PATCH_SITE_BR(PARAVIRT_PATCH_TYPE_BR_ ## type) ;       \
-       ;;                                                              \
-       movl reg=targ ;                                                 \
-       ;;                                                              \
-       ld8 reg=[reg] ;                                                 \
-       ;;                                                              \
-       mov breg=reg ;                                                  \
-       br.cond.sptk.many breg
-
-#define BRANCH_PROC(sym, reg, breg, type)                              \
-       DATA8(paravirt_ ## sym ## _targ, ia64_native_ ## sym) ;         \
-       GLOBAL_ENTRY(paravirt_ ## sym) ;                                \
-               BRANCH(paravirt_ ## sym ## _targ, reg, breg, type) ;    \
-       END(paravirt_ ## sym)
-
-#define BRANCH_PROC_UNWINFO(sym, reg, breg, type)                      \
-       DATA8(paravirt_ ## sym ## _targ, ia64_native_ ## sym) ;         \
-       GLOBAL_ENTRY(paravirt_ ## sym) ;                                \
-               PT_REGS_UNWIND_INFO(0) ;                                \
-               BRANCH(paravirt_ ## sym ## _targ, reg, breg, type) ;    \
-       END(paravirt_ ## sym)
-
-
-BRANCH_PROC(switch_to, r22, b7, SWITCH_TO)
-BRANCH_PROC_UNWINFO(leave_syscall, r22, b7, LEAVE_SYSCALL)
-BRANCH_PROC(work_processed_syscall, r2, b7, WORK_PROCESSED_SYSCALL)
-BRANCH_PROC_UNWINFO(leave_kernel, r22, b7, LEAVE_KERNEL)
-
-
-#ifdef CONFIG_MODULES
-#define __INIT_OR_MODULE       .text
-#define __INITDATA_OR_MODULE   .data
-#else
-#define __INIT_OR_MODULE       __INIT
-#define __INITDATA_OR_MODULE   __INITDATA
-#endif /* CONFIG_MODULES */
-
-       __INIT_OR_MODULE
-       GLOBAL_ENTRY(paravirt_fc_i)
-       fc.i r32
-       br.ret.sptk.many rp
-       END(paravirt_fc_i)
-       __FINIT
-
-       __INIT_OR_MODULE
-       .align 32
-       GLOBAL_ENTRY(paravirt_nop_b_inst_bundle)
-       {
-               nop.b 0
-               nop.b 0
-               nop.b 0
-       }
-       END(paravirt_nop_b_inst_bundle)
-       __FINIT
-
-       /* NOTE: nop.[mfi] has same format */
-       __INIT_OR_MODULE
-       GLOBAL_ENTRY(paravirt_nop_mfi_inst_bundle)
-       {
-               nop.m 0
-               nop.f 0
-               nop.i 0
-       }
-       END(paravirt_nop_mfi_inst_bundle)
-       __FINIT
-
-       __INIT_OR_MODULE
-       GLOBAL_ENTRY(paravirt_nop_bundle)
-paravirt_nop_bundle_start:
-       {
-               nop 0
-               nop 0
-               nop 0
-       }
-paravirt_nop_bundle_end:
-       END(paravirt_nop_bundle)
-       __FINIT
-
-       __INITDATA_OR_MODULE
-       .align 8
-       .global paravirt_nop_bundle_size
-paravirt_nop_bundle_size:
-       data8   paravirt_nop_bundle_end - paravirt_nop_bundle_start
index 1cf091793714dde033b74a3d696eceb8b1b737b3..944a8e2438a641aac50bb2c7f00b69c71cad8c0f 100644 (file)
@@ -7,7 +7,6 @@
 #include <linux/init.h>
 #include <linux/string.h>
 
-#include <asm/paravirt.h>
 #include <asm/patch.h>
 #include <asm/processor.h>
 #include <asm/sections.h>
@@ -169,35 +168,16 @@ ia64_patch_mckinley_e9 (unsigned long start, unsigned long end)
        ia64_srlz_i();
 }
 
-extern unsigned long ia64_native_fsyscall_table[NR_syscalls];
-extern char ia64_native_fsys_bubble_down[];
-struct pv_fsys_data pv_fsys_data __initdata = {
-       .fsyscall_table = (unsigned long *)ia64_native_fsyscall_table,
-       .fsys_bubble_down = (void *)ia64_native_fsys_bubble_down,
-};
-
-unsigned long * __init
-paravirt_get_fsyscall_table(void)
-{
-       return pv_fsys_data.fsyscall_table;
-}
-
-char * __init
-paravirt_get_fsys_bubble_down(void)
-{
-       return pv_fsys_data.fsys_bubble_down;
-}
-
 static void __init
 patch_fsyscall_table (unsigned long start, unsigned long end)
 {
-       u64 fsyscall_table = (u64)paravirt_get_fsyscall_table();
+       extern unsigned long fsyscall_table[NR_syscalls];
        s32 *offp = (s32 *) start;
        u64 ip;
 
        while (offp < (s32 *) end) {
                ip = (u64) ia64_imva((char *) offp + *offp);
-               ia64_patch_imm64(ip, fsyscall_table);
+               ia64_patch_imm64(ip, (u64) fsyscall_table);
                ia64_fc((void *) ip);
                ++offp;
        }
@@ -208,7 +188,7 @@ patch_fsyscall_table (unsigned long start, unsigned long end)
 static void __init
 patch_brl_fsys_bubble_down (unsigned long start, unsigned long end)
 {
-       u64 fsys_bubble_down = (u64)paravirt_get_fsys_bubble_down();
+       extern char fsys_bubble_down[];
        s32 *offp = (s32 *) start;
        u64 ip;
 
@@ -226,13 +206,13 @@ patch_brl_fsys_bubble_down (unsigned long start, unsigned long end)
 void __init
 ia64_patch_gate (void)
 {
-#      define START(name)      paravirt_get_gate_patchlist(PV_GATE_START_##name)
-#      define END(name)        paravirt_get_gate_patchlist(PV_GATE_END_##name)
+#      define START(name)      ((unsigned long) __start_gate_##name##_patchlist)
+#      define END(name)        ((unsigned long)__end_gate_##name##_patchlist)
 
-       patch_fsyscall_table(START(FSYSCALL), END(FSYSCALL));
-       patch_brl_fsys_bubble_down(START(BRL_FSYS_BUBBLE_DOWN), END(BRL_FSYS_BUBBLE_DOWN));
-       ia64_patch_vtop(START(VTOP), END(VTOP));
-       ia64_patch_mckinley_e9(START(MCKINLEY_E9), END(MCKINLEY_E9));
+       patch_fsyscall_table(START(fsyscall), END(fsyscall));
+       patch_brl_fsys_bubble_down(START(brl_fsys_bubble_down), END(brl_fsys_bubble_down));
+       ia64_patch_vtop(START(vtop), END(vtop));
+       ia64_patch_mckinley_e9(START(mckinley_e9), END(mckinley_e9));
 }
 
 void ia64_patch_phys_stack_reg(unsigned long val)
index b9761389cb8d4fcd41c4a44ff608f4d28ebadb5a..4f118b0d30915f4f5927719522414a66efe17f08 100644 (file)
@@ -50,8 +50,6 @@
 #include <asm/mca.h>
 #include <asm/meminit.h>
 #include <asm/page.h>
-#include <asm/paravirt.h>
-#include <asm/paravirt_patch.h>
 #include <asm/patch.h>
 #include <asm/pgtable.h>
 #include <asm/processor.h>
@@ -360,8 +358,6 @@ reserve_memory (void)
        rsvd_region[n].end   = (unsigned long) ia64_imva(_end);
        n++;
 
-       n += paravirt_reserve_memory(&rsvd_region[n]);
-
 #ifdef CONFIG_BLK_DEV_INITRD
        if (ia64_boot_param->initrd_start) {
                rsvd_region[n].start = (unsigned long)__va(ia64_boot_param->initrd_start);
@@ -528,10 +524,7 @@ setup_arch (char **cmdline_p)
 {
        unw_init();
 
-       paravirt_arch_setup_early();
-
        ia64_patch_vtop((u64) __start___vtop_patchlist, (u64) __end___vtop_patchlist);
-       paravirt_patch_apply();
 
        *cmdline_p = __va(ia64_boot_param->command_line);
        strlcpy(boot_command_line, *cmdline_p, COMMAND_LINE_SIZE);
@@ -594,9 +587,6 @@ setup_arch (char **cmdline_p)
        cpu_init();     /* initialize the bootstrap CPU */
        mmu_context_init();     /* initialize context_id bitmap */
 
-       paravirt_banner();
-       paravirt_arch_setup_console(cmdline_p);
-
 #ifdef CONFIG_VT
        if (!conswitchp) {
 # if defined(CONFIG_DUMMY_CONSOLE)
@@ -616,8 +606,6 @@ setup_arch (char **cmdline_p)
 #endif
 
        /* enable IA-64 Machine Check Abort Handling unless disabled */
-       if (paravirt_arch_setup_nomca())
-               nomca = 1;
        if (!nomca)
                ia64_mca_init();
 
index 15051e9c2c6f98f3f2e8743739f10b63f795be3a..0e76fad27975db388776f61e0cf8afd4241f1112 100644 (file)
@@ -49,7 +49,6 @@
 #include <asm/machvec.h>
 #include <asm/mca.h>
 #include <asm/page.h>
-#include <asm/paravirt.h>
 #include <asm/pgalloc.h>
 #include <asm/pgtable.h>
 #include <asm/processor.h>
@@ -127,7 +126,7 @@ int smp_num_siblings = 1;
 volatile int ia64_cpu_to_sapicid[NR_CPUS];
 EXPORT_SYMBOL(ia64_cpu_to_sapicid);
 
-static volatile cpumask_t cpu_callin_map;
+static cpumask_t cpu_callin_map;
 
 struct smp_boot_data smp_boot_data __initdata;
 
@@ -477,6 +476,7 @@ do_boot_cpu (int sapicid, int cpu, struct task_struct *idle)
        for (timeout = 0; timeout < 100000; timeout++) {
                if (cpumask_test_cpu(cpu, &cpu_callin_map))
                        break;  /* It has booted */
+               barrier(); /* Make sure we re-read cpu_callin_map */
                udelay(100);
        }
        Dprintk("\n");
@@ -568,7 +568,6 @@ void smp_prepare_boot_cpu(void)
        cpumask_set_cpu(smp_processor_id(), &cpu_callin_map);
        set_numa_node(cpu_to_node_map[smp_processor_id()]);
        per_cpu(cpu_state, smp_processor_id()) = CPU_ONLINE;
-       paravirt_post_smp_prepare_boot_cpu();
 }
 
 #ifdef CONFIG_HOTPLUG_CPU
index 9a0104a38cd37ef3e1a4a1537cfe95c785d45dbf..c8dbe2acd735bb6fabe29c41b21483f3fd46009d 100644 (file)
@@ -25,7 +25,6 @@
 #include <asm/machvec.h>
 #include <asm/delay.h>
 #include <asm/hw_irq.h>
-#include <asm/paravirt.h>
 #include <asm/ptrace.h>
 #include <asm/sal.h>
 #include <asm/sections.h>
@@ -47,33 +46,12 @@ EXPORT_SYMBOL(last_cli_ip);
 
 #endif
 
-#ifdef CONFIG_PARAVIRT
-/* We need to define a real function for sched_clock, to override the
-   weak default version */
-unsigned long long sched_clock(void)
-{
-        return paravirt_sched_clock();
-}
-#endif
-
-#ifdef CONFIG_PARAVIRT
-static void
-paravirt_clocksource_resume(struct clocksource *cs)
-{
-       if (pv_time_ops.clocksource_resume)
-               pv_time_ops.clocksource_resume();
-}
-#endif
-
 static struct clocksource clocksource_itc = {
        .name           = "itc",
        .rating         = 350,
        .read           = itc_get_cycles,
        .mask           = CLOCKSOURCE_MASK(64),
        .flags          = CLOCK_SOURCE_IS_CONTINUOUS,
-#ifdef CONFIG_PARAVIRT
-       .resume         = paravirt_clocksource_resume,
-#endif
 };
 static struct clocksource *itc_clocksource;
 
@@ -164,9 +142,6 @@ timer_interrupt (int irq, void *dev_id)
 
        profile_tick(CPU_PROFILING);
 
-       if (paravirt_do_steal_accounting(&new_itm))
-               goto skip_process_time_accounting;
-
        while (1) {
                update_process_times(user_mode(get_irq_regs()));
 
@@ -187,8 +162,6 @@ timer_interrupt (int irq, void *dev_id)
                local_irq_disable();
        }
 
-skip_process_time_accounting:
-
        do {
                /*
                 * If we're too close to the next clock tick for
@@ -337,8 +310,6 @@ void ia64_init_itm(void)
                 */
                clocksource_itc.rating = 50;
 
-       paravirt_init_missing_ticks_accounting(smp_processor_id());
-
        /* avoid softlock up message when cpu is unplug and plugged again. */
        touch_softlockup_watchdog();
 
index 84f8a52ac5ae2bdb65004691813aed4ed008ea9f..dc506b05ffbdbf21f5eece65a723ecb178c4564c 100644 (file)
@@ -136,27 +136,6 @@ SECTIONS {
                __end___mckinley_e9_bundles = .;
        }
 
-#if defined(CONFIG_PARAVIRT)
-       . = ALIGN(16);
-       .paravirt_bundles : AT(ADDR(.paravirt_bundles) - LOAD_OFFSET) {
-               __start_paravirt_bundles = .;
-               *(.paravirt_bundles)
-               __stop_paravirt_bundles = .;
-       }
-       . = ALIGN(16);
-       .paravirt_insts : AT(ADDR(.paravirt_insts) - LOAD_OFFSET) {
-               __start_paravirt_insts = .;
-               *(.paravirt_insts)
-               __stop_paravirt_insts = .;
-       }
-       . = ALIGN(16);
-       .paravirt_branches : AT(ADDR(.paravirt_branches) - LOAD_OFFSET) {
-               __start_paravirt_branches = .;
-               *(.paravirt_branches)
-               __stop_paravirt_branches = .;
-       }
-#endif
-
 #if defined(CONFIG_IA64_GENERIC)
        /* Machine Vector */
        . = ALIGN(16);
index a9b65cf7b34a74b120f842a8344a2ce763ca963a..7f3028965064b1af467bfe93028e1277006fabc1 100644 (file)
@@ -34,7 +34,6 @@
 #include <asm/uaccess.h>
 #include <asm/unistd.h>
 #include <asm/mca.h>
-#include <asm/paravirt.h>
 
 extern void ia64_tlb_init (void);
 
@@ -244,7 +243,6 @@ put_kernel_page (struct page *page, unsigned long address, pgprot_t pgprot)
 static void __init
 setup_gate (void)
 {
-       void *gate_section;
        struct page *page;
 
        /*
@@ -252,11 +250,10 @@ setup_gate (void)
         * headers etc. and once execute-only page to enable
         * privilege-promotion via "epc":
         */
-       gate_section = paravirt_get_gate_section();
-       page = virt_to_page(ia64_imva(gate_section));
+       page = virt_to_page(ia64_imva(__start_gate_section));
        put_kernel_page(page, GATE_ADDR, PAGE_READONLY);
 #ifdef HAVE_BUGGY_SEGREL
-       page = virt_to_page(ia64_imva(gate_section + PAGE_SIZE));
+       page = virt_to_page(ia64_imva(__start_gate_section + PAGE_SIZE));
        put_kernel_page(page, GATE_ADDR + PAGE_SIZE, PAGE_GATE);
 #else
        put_kernel_page(page, GATE_ADDR + PERCPU_PAGE_SIZE, PAGE_GATE);
@@ -642,8 +639,8 @@ mem_init (void)
         * code can tell them apart.
         */
        for (i = 0; i < NR_syscalls; ++i) {
+               extern unsigned long fsyscall_table[NR_syscalls];
                extern unsigned long sys_call_table[NR_syscalls];
-               unsigned long *fsyscall_table = paravirt_get_fsyscall_table();
 
                if (!fsyscall_table[i] || nolwsys)
                        fsyscall_table[i] = sys_call_table[i] | 1;
diff --git a/arch/ia64/scripts/pvcheck.sed b/arch/ia64/scripts/pvcheck.sed
deleted file mode 100644 (file)
index e59809a..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-#
-# Checker for paravirtualizations of privileged operations.
-#
-s/ssm.*psr\.ic.*/.warning \"ssm psr.ic should not be used directly\"/g
-s/rsm.*psr\.ic.*/.warning \"rsm psr.ic should not be used directly\"/g
-s/ssm.*psr\.i.*/.warning \"ssm psr.i should not be used directly\"/g
-s/rsm.*psr\.i.*/.warning \"rsm psr.i should not be used directly\"/g
-s/ssm.*psr\.dt.*/.warning \"ssm psr.dt should not be used directly\"/g
-s/rsm.*psr\.dt.*/.warning \"rsm psr.dt should not be used directly\"/g
-s/mov.*=.*cr\.ifa/.warning \"cr.ifa should not used directly\"/g
-s/mov.*=.*cr\.itir/.warning \"cr.itir should not used directly\"/g
-s/mov.*=.*cr\.isr/.warning \"cr.isr should not used directly\"/g
-s/mov.*=.*cr\.iha/.warning \"cr.iha should not used directly\"/g
-s/mov.*=.*cr\.ipsr/.warning \"cr.ipsr should not used directly\"/g
-s/mov.*=.*cr\.iim/.warning \"cr.iim should not used directly\"/g
-s/mov.*=.*cr\.iip/.warning \"cr.iip should not used directly\"/g
-s/mov.*=.*cr\.ivr/.warning \"cr.ivr should not used directly\"/g
-s/mov.*=[^\.]*psr/.warning \"psr should not used directly\"/g  # avoid ar.fpsr
-s/mov.*=.*ar\.eflags/.warning \"ar.eflags should not used directly\"/g
-s/mov.*=.*ar\.itc.*/.warning \"ar.itc should not used directly\"/g
-s/mov.*cr\.ifa.*=.*/.warning \"cr.ifa should not used directly\"/g
-s/mov.*cr\.itir.*=.*/.warning \"cr.itir should not used directly\"/g
-s/mov.*cr\.iha.*=.*/.warning \"cr.iha should not used directly\"/g
-s/mov.*cr\.ipsr.*=.*/.warning \"cr.ipsr should not used directly\"/g
-s/mov.*cr\.ifs.*=.*/.warning \"cr.ifs should not used directly\"/g
-s/mov.*cr\.iip.*=.*/.warning \"cr.iip should not used directly\"/g
-s/mov.*cr\.kr.*=.*/.warning \"cr.kr should not used directly\"/g
-s/mov.*ar\.eflags.*=.*/.warning \"ar.eflags should not used directly\"/g
-s/itc\.i.*/.warning \"itc.i should not be used directly.\"/g
-s/itc\.d.*/.warning \"itc.d should not be used directly.\"/g
-s/bsw\.0/.warning \"bsw.0 should not be used directly.\"/g
-s/bsw\.1/.warning \"bsw.1 should not be used directly.\"/g
-s/ptc\.ga.*/.warning \"ptc.ga should not be used directly.\"/g
index de651db20b43938ca7289404dfa1eb4b7940e0c8..14bf9b739dd2c8eb643a805263dc88741fd3b1de 100644 (file)
@@ -107,8 +107,6 @@ __xchg_local(unsigned long x, volatile void *ptr, int size)
        ((__typeof__(*(ptr)))__xchg_local((unsigned long)(x), (ptr),    \
                        sizeof(*(ptr))))
 
-#define __HAVE_ARCH_CMPXCHG    1
-
 static inline unsigned long
 __cmpxchg_u32(volatile unsigned int *p, unsigned int old, unsigned int new)
 {
index 9cc00dbd59cef4096b186fd1ffe5e4b51766f430..0c3f25ee3381d9fad606ea36bb09682d362a87d8 100644 (file)
@@ -68,6 +68,7 @@ static inline void __iomem *ioremap(unsigned long offset, unsigned long size)
 extern void iounmap(volatile void __iomem *addr);
 #define ioremap_nocache(off,size) ioremap(off,size)
 #define ioremap_wc ioremap_nocache
+#define ioremap_wt ioremap_nocache
 
 /*
  * IO bus memory addresses are also 1:1 with the physical address
index ed1643b4c67893b627be4c23a3c9bc0c25b718f0..753a6237f99a30a81ac842d7540be5c9ecdc9819 100644 (file)
@@ -177,9 +177,9 @@ CONFIG_IP_SET_HASH_NETPORT=m
 CONFIG_IP_SET_HASH_NETIFACE=m
 CONFIG_IP_SET_LIST_SET=m
 CONFIG_NF_CONNTRACK_IPV4=m
-CONFIG_NF_LOG_ARP=m
 CONFIG_NFT_CHAIN_ROUTE_IPV4=m
 CONFIG_NF_TABLES_ARP=m
+CONFIG_NF_LOG_ARP=m
 CONFIG_NFT_CHAIN_NAT_IPV4=m
 CONFIG_NFT_MASQ_IPV4=m
 CONFIG_NFT_REDIR_IPV4=m
@@ -267,7 +267,9 @@ CONFIG_BATMAN_ADV_DAT=y
 CONFIG_BATMAN_ADV_NC=y
 CONFIG_BATMAN_ADV_MCAST=y
 CONFIG_NETLINK_DIAG=m
+CONFIG_MPLS=y
 CONFIG_NET_MPLS_GSO=m
+CONFIG_MPLS_ROUTING=m
 # CONFIG_WIRELESS is not set
 # CONFIG_UEVENT_HELPER is not set
 CONFIG_DEVTMPFS=y
@@ -285,6 +287,7 @@ CONFIG_BLK_DEV_CRYPTOLOOP=m
 CONFIG_BLK_DEV_DRBD=m
 CONFIG_BLK_DEV_NBD=m
 CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_PMEM=m
 CONFIG_CDROM_PKTCDVD=m
 CONFIG_ATA_OVER_ETH=m
 CONFIG_DUMMY_IRQ=m
@@ -324,6 +327,7 @@ CONFIG_DM_RAID=m
 CONFIG_DM_ZERO=m
 CONFIG_DM_MULTIPATH=m
 CONFIG_DM_UEVENT=y
+CONFIG_DM_LOG_WRITES=m
 CONFIG_TARGET_CORE=m
 CONFIG_TCM_IBLOCK=m
 CONFIG_TCM_FILEIO=m
@@ -348,6 +352,7 @@ CONFIG_VETH=m
 CONFIG_A2065=y
 CONFIG_ARIADNE=y
 # CONFIG_NET_VENDOR_ARC is not set
+# CONFIG_NET_CADENCE is not set
 # CONFIG_NET_VENDOR_BROADCOM is not set
 # CONFIG_NET_VENDOR_CIRRUS is not set
 # CONFIG_NET_VENDOR_HP is not set
@@ -414,7 +419,6 @@ CONFIG_HID=m
 CONFIG_HIDRAW=y
 CONFIG_UHID=m
 # CONFIG_HID_GENERIC is not set
-# CONFIG_HID_PLANTRONICS is not set
 # CONFIG_USB_SUPPORT is not set
 CONFIG_RTC_CLASS=y
 CONFIG_RTC_DRV_MSM6242=m
index d38822b1847ee272325793172d32804f3f8f8c00..1f93dcaf02e514719093af817e017037705979fa 100644 (file)
@@ -175,9 +175,9 @@ CONFIG_IP_SET_HASH_NETPORT=m
 CONFIG_IP_SET_HASH_NETIFACE=m
 CONFIG_IP_SET_LIST_SET=m
 CONFIG_NF_CONNTRACK_IPV4=m
-CONFIG_NF_LOG_ARP=m
 CONFIG_NFT_CHAIN_ROUTE_IPV4=m
 CONFIG_NF_TABLES_ARP=m
+CONFIG_NF_LOG_ARP=m
 CONFIG_NFT_CHAIN_NAT_IPV4=m
 CONFIG_NFT_MASQ_IPV4=m
 CONFIG_NFT_REDIR_IPV4=m
@@ -265,7 +265,9 @@ CONFIG_BATMAN_ADV_DAT=y
 CONFIG_BATMAN_ADV_NC=y
 CONFIG_BATMAN_ADV_MCAST=y
 CONFIG_NETLINK_DIAG=m
+CONFIG_MPLS=y
 CONFIG_NET_MPLS_GSO=m
+CONFIG_MPLS_ROUTING=m
 # CONFIG_WIRELESS is not set
 # CONFIG_UEVENT_HELPER is not set
 CONFIG_DEVTMPFS=y
@@ -277,6 +279,7 @@ CONFIG_BLK_DEV_CRYPTOLOOP=m
 CONFIG_BLK_DEV_DRBD=m
 CONFIG_BLK_DEV_NBD=m
 CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_PMEM=m
 CONFIG_CDROM_PKTCDVD=m
 CONFIG_ATA_OVER_ETH=m
 CONFIG_DUMMY_IRQ=m
@@ -306,6 +309,7 @@ CONFIG_DM_RAID=m
 CONFIG_DM_ZERO=m
 CONFIG_DM_MULTIPATH=m
 CONFIG_DM_UEVENT=y
+CONFIG_DM_LOG_WRITES=m
 CONFIG_TARGET_CORE=m
 CONFIG_TCM_IBLOCK=m
 CONFIG_TCM_FILEIO=m
@@ -327,6 +331,7 @@ CONFIG_NETCONSOLE=m
 CONFIG_NETCONSOLE_DYNAMIC=y
 CONFIG_VETH=m
 # CONFIG_NET_VENDOR_ARC is not set
+# CONFIG_NET_CADENCE is not set
 # CONFIG_NET_VENDOR_BROADCOM is not set
 # CONFIG_NET_VENDOR_INTEL is not set
 # CONFIG_NET_VENDOR_MARVELL is not set
@@ -375,7 +380,6 @@ CONFIG_HID=m
 CONFIG_HIDRAW=y
 CONFIG_UHID=m
 # CONFIG_HID_GENERIC is not set
-# CONFIG_HID_PLANTRONICS is not set
 # CONFIG_USB_SUPPORT is not set
 CONFIG_RTC_CLASS=y
 CONFIG_RTC_DRV_GENERIC=m
index c429199cf4a9332a9e43432c6bcfed6032863ba0..831b8b8b92ad3619b0e50d83ed6da092627c3305 100644 (file)
@@ -175,9 +175,9 @@ CONFIG_IP_SET_HASH_NETPORT=m
 CONFIG_IP_SET_HASH_NETIFACE=m
 CONFIG_IP_SET_LIST_SET=m
 CONFIG_NF_CONNTRACK_IPV4=m
-CONFIG_NF_LOG_ARP=m
 CONFIG_NFT_CHAIN_ROUTE_IPV4=m
 CONFIG_NF_TABLES_ARP=m
+CONFIG_NF_LOG_ARP=m
 CONFIG_NFT_CHAIN_NAT_IPV4=m
 CONFIG_NFT_MASQ_IPV4=m
 CONFIG_NFT_REDIR_IPV4=m
@@ -265,7 +265,9 @@ CONFIG_BATMAN_ADV_DAT=y
 CONFIG_BATMAN_ADV_NC=y
 CONFIG_BATMAN_ADV_MCAST=y
 CONFIG_NETLINK_DIAG=m
+CONFIG_MPLS=y
 CONFIG_NET_MPLS_GSO=m
+CONFIG_MPLS_ROUTING=m
 # CONFIG_WIRELESS is not set
 # CONFIG_UEVENT_HELPER is not set
 CONFIG_DEVTMPFS=y
@@ -281,6 +283,7 @@ CONFIG_BLK_DEV_CRYPTOLOOP=m
 CONFIG_BLK_DEV_DRBD=m
 CONFIG_BLK_DEV_NBD=m
 CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_PMEM=m
 CONFIG_CDROM_PKTCDVD=m
 CONFIG_ATA_OVER_ETH=m
 CONFIG_DUMMY_IRQ=m
@@ -315,6 +318,7 @@ CONFIG_DM_RAID=m
 CONFIG_DM_ZERO=m
 CONFIG_DM_MULTIPATH=m
 CONFIG_DM_UEVENT=y
+CONFIG_DM_LOG_WRITES=m
 CONFIG_TARGET_CORE=m
 CONFIG_TCM_IBLOCK=m
 CONFIG_TCM_FILEIO=m
@@ -337,6 +341,7 @@ CONFIG_NETCONSOLE_DYNAMIC=y
 CONFIG_VETH=m
 CONFIG_ATARILANCE=y
 # CONFIG_NET_VENDOR_ARC is not set
+# CONFIG_NET_CADENCE is not set
 # CONFIG_NET_VENDOR_BROADCOM is not set
 # CONFIG_NET_VENDOR_INTEL is not set
 # CONFIG_NET_VENDOR_MARVELL is not set
@@ -391,7 +396,6 @@ CONFIG_DMASOUND_ATARI=m
 CONFIG_HID=m
 CONFIG_HIDRAW=y
 CONFIG_UHID=m
-# CONFIG_HID_PLANTRONICS is not set
 CONFIG_RTC_CLASS=y
 CONFIG_RTC_DRV_GENERIC=m
 # CONFIG_IOMMU_SUPPORT is not set
index 9b880371d6421ec9be77870c3fd352589eb571ee..91fd187c16d59f6e8685aa55e8accf8f1a14a39c 100644 (file)
@@ -173,9 +173,9 @@ CONFIG_IP_SET_HASH_NETPORT=m
 CONFIG_IP_SET_HASH_NETIFACE=m
 CONFIG_IP_SET_LIST_SET=m
 CONFIG_NF_CONNTRACK_IPV4=m
-CONFIG_NF_LOG_ARP=m
 CONFIG_NFT_CHAIN_ROUTE_IPV4=m
 CONFIG_NF_TABLES_ARP=m
+CONFIG_NF_LOG_ARP=m
 CONFIG_NFT_CHAIN_NAT_IPV4=m
 CONFIG_NFT_MASQ_IPV4=m
 CONFIG_NFT_REDIR_IPV4=m
@@ -263,7 +263,9 @@ CONFIG_BATMAN_ADV_DAT=y
 CONFIG_BATMAN_ADV_NC=y
 CONFIG_BATMAN_ADV_MCAST=y
 CONFIG_NETLINK_DIAG=m
+CONFIG_MPLS=y
 CONFIG_NET_MPLS_GSO=m
+CONFIG_MPLS_ROUTING=m
 # CONFIG_WIRELESS is not set
 # CONFIG_UEVENT_HELPER is not set
 CONFIG_DEVTMPFS=y
@@ -275,6 +277,7 @@ CONFIG_BLK_DEV_CRYPTOLOOP=m
 CONFIG_BLK_DEV_DRBD=m
 CONFIG_BLK_DEV_NBD=m
 CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_PMEM=m
 CONFIG_CDROM_PKTCDVD=m
 CONFIG_ATA_OVER_ETH=m
 CONFIG_DUMMY_IRQ=m
@@ -305,6 +308,7 @@ CONFIG_DM_RAID=m
 CONFIG_DM_ZERO=m
 CONFIG_DM_MULTIPATH=m
 CONFIG_DM_UEVENT=y
+CONFIG_DM_LOG_WRITES=m
 CONFIG_TARGET_CORE=m
 CONFIG_TCM_IBLOCK=m
 CONFIG_TCM_FILEIO=m
@@ -326,6 +330,7 @@ CONFIG_NETCONSOLE=m
 CONFIG_NETCONSOLE_DYNAMIC=y
 CONFIG_VETH=m
 # CONFIG_NET_VENDOR_ARC is not set
+# CONFIG_NET_CADENCE is not set
 # CONFIG_NET_VENDOR_BROADCOM is not set
 CONFIG_BVME6000_NET=y
 # CONFIG_NET_VENDOR_MARVELL is not set
@@ -369,7 +374,6 @@ CONFIG_HID=m
 CONFIG_HIDRAW=y
 CONFIG_UHID=m
 # CONFIG_HID_GENERIC is not set
-# CONFIG_HID_PLANTRONICS is not set
 # CONFIG_USB_SUPPORT is not set
 CONFIG_RTC_CLASS=y
 CONFIG_RTC_DRV_GENERIC=m
index 49ae3376e993a50f462a61dcd790ca4383b8ff40..9d4934f1d2c3b8c995339eb5f9766594db367d25 100644 (file)
@@ -175,9 +175,9 @@ CONFIG_IP_SET_HASH_NETPORT=m
 CONFIG_IP_SET_HASH_NETIFACE=m
 CONFIG_IP_SET_LIST_SET=m
 CONFIG_NF_CONNTRACK_IPV4=m
-CONFIG_NF_LOG_ARP=m
 CONFIG_NFT_CHAIN_ROUTE_IPV4=m
 CONFIG_NF_TABLES_ARP=m
+CONFIG_NF_LOG_ARP=m
 CONFIG_NFT_CHAIN_NAT_IPV4=m
 CONFIG_NFT_MASQ_IPV4=m
 CONFIG_NFT_REDIR_IPV4=m
@@ -265,7 +265,9 @@ CONFIG_BATMAN_ADV_DAT=y
 CONFIG_BATMAN_ADV_NC=y
 CONFIG_BATMAN_ADV_MCAST=y
 CONFIG_NETLINK_DIAG=m
+CONFIG_MPLS=y
 CONFIG_NET_MPLS_GSO=m
+CONFIG_MPLS_ROUTING=m
 # CONFIG_WIRELESS is not set
 # CONFIG_UEVENT_HELPER is not set
 CONFIG_DEVTMPFS=y
@@ -277,6 +279,7 @@ CONFIG_BLK_DEV_CRYPTOLOOP=m
 CONFIG_BLK_DEV_DRBD=m
 CONFIG_BLK_DEV_NBD=m
 CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_PMEM=m
 CONFIG_CDROM_PKTCDVD=m
 CONFIG_ATA_OVER_ETH=m
 CONFIG_DUMMY_IRQ=m
@@ -306,6 +309,7 @@ CONFIG_DM_RAID=m
 CONFIG_DM_ZERO=m
 CONFIG_DM_MULTIPATH=m
 CONFIG_DM_UEVENT=y
+CONFIG_DM_LOG_WRITES=m
 CONFIG_TARGET_CORE=m
 CONFIG_TCM_IBLOCK=m
 CONFIG_TCM_FILEIO=m
@@ -328,6 +332,7 @@ CONFIG_NETCONSOLE_DYNAMIC=y
 CONFIG_VETH=m
 CONFIG_HPLANCE=y
 # CONFIG_NET_VENDOR_ARC is not set
+# CONFIG_NET_CADENCE is not set
 # CONFIG_NET_VENDOR_BROADCOM is not set
 # CONFIG_NET_VENDOR_INTEL is not set
 # CONFIG_NET_VENDOR_MARVELL is not set
@@ -378,7 +383,6 @@ CONFIG_HID=m
 CONFIG_HIDRAW=y
 CONFIG_UHID=m
 # CONFIG_HID_GENERIC is not set
-# CONFIG_HID_PLANTRONICS is not set
 # CONFIG_USB_SUPPORT is not set
 CONFIG_RTC_CLASS=y
 CONFIG_RTC_DRV_GENERIC=m
index ee143a57058cb93ed3fd116154ef19a9932e2532..72bc187ca995ad5910a82b0f4b0f8c5f847b21ee 100644 (file)
@@ -174,9 +174,9 @@ CONFIG_IP_SET_HASH_NETPORT=m
 CONFIG_IP_SET_HASH_NETIFACE=m
 CONFIG_IP_SET_LIST_SET=m
 CONFIG_NF_CONNTRACK_IPV4=m
-CONFIG_NF_LOG_ARP=m
 CONFIG_NFT_CHAIN_ROUTE_IPV4=m
 CONFIG_NF_TABLES_ARP=m
+CONFIG_NF_LOG_ARP=m
 CONFIG_NFT_CHAIN_NAT_IPV4=m
 CONFIG_NFT_MASQ_IPV4=m
 CONFIG_NFT_REDIR_IPV4=m
@@ -267,7 +267,9 @@ CONFIG_BATMAN_ADV_DAT=y
 CONFIG_BATMAN_ADV_NC=y
 CONFIG_BATMAN_ADV_MCAST=y
 CONFIG_NETLINK_DIAG=m
+CONFIG_MPLS=y
 CONFIG_NET_MPLS_GSO=m
+CONFIG_MPLS_ROUTING=m
 # CONFIG_WIRELESS is not set
 # CONFIG_UEVENT_HELPER is not set
 CONFIG_DEVTMPFS=y
@@ -280,6 +282,7 @@ CONFIG_BLK_DEV_CRYPTOLOOP=m
 CONFIG_BLK_DEV_DRBD=m
 CONFIG_BLK_DEV_NBD=m
 CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_PMEM=m
 CONFIG_CDROM_PKTCDVD=m
 CONFIG_ATA_OVER_ETH=m
 CONFIG_DUMMY_IRQ=m
@@ -315,6 +318,7 @@ CONFIG_DM_RAID=m
 CONFIG_DM_ZERO=m
 CONFIG_DM_MULTIPATH=m
 CONFIG_DM_UEVENT=y
+CONFIG_DM_LOG_WRITES=m
 CONFIG_TARGET_CORE=m
 CONFIG_TCM_IBLOCK=m
 CONFIG_TCM_FILEIO=m
@@ -344,6 +348,7 @@ CONFIG_NETCONSOLE_DYNAMIC=y
 CONFIG_VETH=m
 CONFIG_MACMACE=y
 # CONFIG_NET_VENDOR_ARC is not set
+# CONFIG_NET_CADENCE is not set
 # CONFIG_NET_VENDOR_BROADCOM is not set
 CONFIG_MAC89x0=y
 # CONFIG_NET_VENDOR_INTEL is not set
@@ -400,7 +405,6 @@ CONFIG_HID=m
 CONFIG_HIDRAW=y
 CONFIG_UHID=m
 # CONFIG_HID_GENERIC is not set
-# CONFIG_HID_PLANTRONICS is not set
 # CONFIG_USB_SUPPORT is not set
 CONFIG_RTC_CLASS=y
 CONFIG_RTC_DRV_GENERIC=m
index c777aa05048f0bad1a2b33d46e7d5617e3752c42..8fb65535597fd13f3bec6769e37493311d4f99fd 100644 (file)
@@ -184,9 +184,9 @@ CONFIG_IP_SET_HASH_NETPORT=m
 CONFIG_IP_SET_HASH_NETIFACE=m
 CONFIG_IP_SET_LIST_SET=m
 CONFIG_NF_CONNTRACK_IPV4=m
-CONFIG_NF_LOG_ARP=m
 CONFIG_NFT_CHAIN_ROUTE_IPV4=m
 CONFIG_NF_TABLES_ARP=m
+CONFIG_NF_LOG_ARP=m
 CONFIG_NFT_CHAIN_NAT_IPV4=m
 CONFIG_NFT_MASQ_IPV4=m
 CONFIG_NFT_REDIR_IPV4=m
@@ -277,7 +277,9 @@ CONFIG_BATMAN_ADV_DAT=y
 CONFIG_BATMAN_ADV_NC=y
 CONFIG_BATMAN_ADV_MCAST=y
 CONFIG_NETLINK_DIAG=m
+CONFIG_MPLS=y
 CONFIG_NET_MPLS_GSO=m
+CONFIG_MPLS_ROUTING=m
 # CONFIG_WIRELESS is not set
 # CONFIG_UEVENT_HELPER is not set
 CONFIG_DEVTMPFS=y
@@ -299,6 +301,7 @@ CONFIG_BLK_DEV_CRYPTOLOOP=m
 CONFIG_BLK_DEV_DRBD=m
 CONFIG_BLK_DEV_NBD=m
 CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_PMEM=m
 CONFIG_CDROM_PKTCDVD=m
 CONFIG_ATA_OVER_ETH=m
 CONFIG_DUMMY_IRQ=m
@@ -348,6 +351,7 @@ CONFIG_DM_RAID=m
 CONFIG_DM_ZERO=m
 CONFIG_DM_MULTIPATH=m
 CONFIG_DM_UEVENT=y
+CONFIG_DM_LOG_WRITES=m
 CONFIG_TARGET_CORE=m
 CONFIG_TCM_IBLOCK=m
 CONFIG_TCM_FILEIO=m
@@ -384,6 +388,7 @@ CONFIG_MVME147_NET=y
 CONFIG_SUN3LANCE=y
 CONFIG_MACMACE=y
 # CONFIG_NET_VENDOR_ARC is not set
+# CONFIG_NET_CADENCE is not set
 # CONFIG_NET_VENDOR_BROADCOM is not set
 CONFIG_MAC89x0=y
 # CONFIG_NET_VENDOR_HP is not set
@@ -468,7 +473,6 @@ CONFIG_HID=m
 CONFIG_HIDRAW=y
 CONFIG_UHID=m
 # CONFIG_HID_GENERIC is not set
-# CONFIG_HID_PLANTRONICS is not set
 # CONFIG_USB_SUPPORT is not set
 CONFIG_RTC_CLASS=y
 CONFIG_RTC_DRV_MSM6242=m
index a7628a85e260ddabb6e99f6ff5c71ee4fd565b8f..f34491ec01269ceb35d2deae8d72dc71280ba566 100644 (file)
@@ -172,9 +172,9 @@ CONFIG_IP_SET_HASH_NETPORT=m
 CONFIG_IP_SET_HASH_NETIFACE=m
 CONFIG_IP_SET_LIST_SET=m
 CONFIG_NF_CONNTRACK_IPV4=m
-CONFIG_NF_LOG_ARP=m
 CONFIG_NFT_CHAIN_ROUTE_IPV4=m
 CONFIG_NF_TABLES_ARP=m
+CONFIG_NF_LOG_ARP=m
 CONFIG_NFT_CHAIN_NAT_IPV4=m
 CONFIG_NFT_MASQ_IPV4=m
 CONFIG_NFT_REDIR_IPV4=m
@@ -262,7 +262,9 @@ CONFIG_BATMAN_ADV_DAT=y
 CONFIG_BATMAN_ADV_NC=y
 CONFIG_BATMAN_ADV_MCAST=y
 CONFIG_NETLINK_DIAG=m
+CONFIG_MPLS=y
 CONFIG_NET_MPLS_GSO=m
+CONFIG_MPLS_ROUTING=m
 # CONFIG_WIRELESS is not set
 # CONFIG_UEVENT_HELPER is not set
 CONFIG_DEVTMPFS=y
@@ -274,6 +276,7 @@ CONFIG_BLK_DEV_CRYPTOLOOP=m
 CONFIG_BLK_DEV_DRBD=m
 CONFIG_BLK_DEV_NBD=m
 CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_PMEM=m
 CONFIG_CDROM_PKTCDVD=m
 CONFIG_ATA_OVER_ETH=m
 CONFIG_DUMMY_IRQ=m
@@ -304,6 +307,7 @@ CONFIG_DM_RAID=m
 CONFIG_DM_ZERO=m
 CONFIG_DM_MULTIPATH=m
 CONFIG_DM_UEVENT=y
+CONFIG_DM_LOG_WRITES=m
 CONFIG_TARGET_CORE=m
 CONFIG_TCM_IBLOCK=m
 CONFIG_TCM_FILEIO=m
@@ -326,6 +330,7 @@ CONFIG_NETCONSOLE_DYNAMIC=y
 CONFIG_VETH=m
 CONFIG_MVME147_NET=y
 # CONFIG_NET_VENDOR_ARC is not set
+# CONFIG_NET_CADENCE is not set
 # CONFIG_NET_VENDOR_BROADCOM is not set
 # CONFIG_NET_VENDOR_INTEL is not set
 # CONFIG_NET_VENDOR_MARVELL is not set
@@ -369,7 +374,6 @@ CONFIG_HID=m
 CONFIG_HIDRAW=y
 CONFIG_UHID=m
 # CONFIG_HID_GENERIC is not set
-# CONFIG_HID_PLANTRONICS is not set
 # CONFIG_USB_SUPPORT is not set
 CONFIG_RTC_CLASS=y
 CONFIG_RTC_DRV_GENERIC=m
index ebaa68268a4a5f3513b03f8e7fa842cf78427f60..3d3614d1b041d047fc01a24225ba722f01410000 100644 (file)
@@ -173,9 +173,9 @@ CONFIG_IP_SET_HASH_NETPORT=m
 CONFIG_IP_SET_HASH_NETIFACE=m
 CONFIG_IP_SET_LIST_SET=m
 CONFIG_NF_CONNTRACK_IPV4=m
-CONFIG_NF_LOG_ARP=m
 CONFIG_NFT_CHAIN_ROUTE_IPV4=m
 CONFIG_NF_TABLES_ARP=m
+CONFIG_NF_LOG_ARP=m
 CONFIG_NFT_CHAIN_NAT_IPV4=m
 CONFIG_NFT_MASQ_IPV4=m
 CONFIG_NFT_REDIR_IPV4=m
@@ -263,7 +263,9 @@ CONFIG_BATMAN_ADV_DAT=y
 CONFIG_BATMAN_ADV_NC=y
 CONFIG_BATMAN_ADV_MCAST=y
 CONFIG_NETLINK_DIAG=m
+CONFIG_MPLS=y
 CONFIG_NET_MPLS_GSO=m
+CONFIG_MPLS_ROUTING=m
 # CONFIG_WIRELESS is not set
 # CONFIG_UEVENT_HELPER is not set
 CONFIG_DEVTMPFS=y
@@ -275,6 +277,7 @@ CONFIG_BLK_DEV_CRYPTOLOOP=m
 CONFIG_BLK_DEV_DRBD=m
 CONFIG_BLK_DEV_NBD=m
 CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_PMEM=m
 CONFIG_CDROM_PKTCDVD=m
 CONFIG_ATA_OVER_ETH=m
 CONFIG_DUMMY_IRQ=m
@@ -305,6 +308,7 @@ CONFIG_DM_RAID=m
 CONFIG_DM_ZERO=m
 CONFIG_DM_MULTIPATH=m
 CONFIG_DM_UEVENT=y
+CONFIG_DM_LOG_WRITES=m
 CONFIG_TARGET_CORE=m
 CONFIG_TCM_IBLOCK=m
 CONFIG_TCM_FILEIO=m
@@ -326,6 +330,7 @@ CONFIG_NETCONSOLE=m
 CONFIG_NETCONSOLE_DYNAMIC=y
 CONFIG_VETH=m
 # CONFIG_NET_VENDOR_ARC is not set
+# CONFIG_NET_CADENCE is not set
 # CONFIG_NET_VENDOR_BROADCOM is not set
 CONFIG_MVME16x_NET=y
 # CONFIG_NET_VENDOR_MARVELL is not set
@@ -369,7 +374,6 @@ CONFIG_HID=m
 CONFIG_HIDRAW=y
 CONFIG_UHID=m
 # CONFIG_HID_GENERIC is not set
-# CONFIG_HID_PLANTRONICS is not set
 # CONFIG_USB_SUPPORT is not set
 CONFIG_RTC_CLASS=y
 CONFIG_RTC_DRV_GENERIC=m
index 2c16853aedd350b8270623bf10e4e1e6cff9619b..643e9c93bea72697f944fdbe5ce2e3a5e0dd22d5 100644 (file)
@@ -173,9 +173,9 @@ CONFIG_IP_SET_HASH_NETPORT=m
 CONFIG_IP_SET_HASH_NETIFACE=m
 CONFIG_IP_SET_LIST_SET=m
 CONFIG_NF_CONNTRACK_IPV4=m
-CONFIG_NF_LOG_ARP=m
 CONFIG_NFT_CHAIN_ROUTE_IPV4=m
 CONFIG_NF_TABLES_ARP=m
+CONFIG_NF_LOG_ARP=m
 CONFIG_NFT_CHAIN_NAT_IPV4=m
 CONFIG_NFT_MASQ_IPV4=m
 CONFIG_NFT_REDIR_IPV4=m
@@ -263,7 +263,9 @@ CONFIG_BATMAN_ADV_DAT=y
 CONFIG_BATMAN_ADV_NC=y
 CONFIG_BATMAN_ADV_MCAST=y
 CONFIG_NETLINK_DIAG=m
+CONFIG_MPLS=y
 CONFIG_NET_MPLS_GSO=m
+CONFIG_MPLS_ROUTING=m
 # CONFIG_WIRELESS is not set
 # CONFIG_UEVENT_HELPER is not set
 CONFIG_DEVTMPFS=y
@@ -278,6 +280,7 @@ CONFIG_BLK_DEV_CRYPTOLOOP=m
 CONFIG_BLK_DEV_DRBD=m
 CONFIG_BLK_DEV_NBD=m
 CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_PMEM=m
 CONFIG_CDROM_PKTCDVD=m
 CONFIG_ATA_OVER_ETH=m
 CONFIG_DUMMY_IRQ=m
@@ -311,6 +314,7 @@ CONFIG_DM_RAID=m
 CONFIG_DM_ZERO=m
 CONFIG_DM_MULTIPATH=m
 CONFIG_DM_UEVENT=y
+CONFIG_DM_LOG_WRITES=m
 CONFIG_TARGET_CORE=m
 CONFIG_TCM_IBLOCK=m
 CONFIG_TCM_FILEIO=m
@@ -334,6 +338,7 @@ CONFIG_VETH=m
 # CONFIG_NET_VENDOR_3COM is not set
 # CONFIG_NET_VENDOR_AMD is not set
 # CONFIG_NET_VENDOR_ARC is not set
+# CONFIG_NET_CADENCE is not set
 # CONFIG_NET_VENDOR_BROADCOM is not set
 # CONFIG_NET_VENDOR_CIRRUS is not set
 # CONFIG_NET_VENDOR_HP is not set
@@ -390,7 +395,6 @@ CONFIG_HID=m
 CONFIG_HIDRAW=y
 CONFIG_UHID=m
 # CONFIG_HID_GENERIC is not set
-# CONFIG_HID_PLANTRONICS is not set
 # CONFIG_USB_SUPPORT is not set
 CONFIG_RTC_CLASS=y
 CONFIG_RTC_DRV_GENERIC=m
index e3056bf0f65bdea73d9b3eeefba282be8fc371a0..8fecc5aa166c3ea77ecdb18ed1edf02f23aa9e58 100644 (file)
@@ -170,9 +170,9 @@ CONFIG_IP_SET_HASH_NETPORT=m
 CONFIG_IP_SET_HASH_NETIFACE=m
 CONFIG_IP_SET_LIST_SET=m
 CONFIG_NF_CONNTRACK_IPV4=m
-CONFIG_NF_LOG_ARP=m
 CONFIG_NFT_CHAIN_ROUTE_IPV4=m
 CONFIG_NF_TABLES_ARP=m
+CONFIG_NF_LOG_ARP=m
 CONFIG_NFT_CHAIN_NAT_IPV4=m
 CONFIG_NFT_MASQ_IPV4=m
 CONFIG_NFT_REDIR_IPV4=m
@@ -260,7 +260,9 @@ CONFIG_BATMAN_ADV_DAT=y
 CONFIG_BATMAN_ADV_NC=y
 CONFIG_BATMAN_ADV_MCAST=y
 CONFIG_NETLINK_DIAG=m
+CONFIG_MPLS=y
 CONFIG_NET_MPLS_GSO=m
+CONFIG_MPLS_ROUTING=m
 # CONFIG_WIRELESS is not set
 # CONFIG_UEVENT_HELPER is not set
 CONFIG_DEVTMPFS=y
@@ -272,6 +274,7 @@ CONFIG_BLK_DEV_CRYPTOLOOP=m
 CONFIG_BLK_DEV_DRBD=m
 CONFIG_BLK_DEV_NBD=m
 CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_PMEM=m
 CONFIG_CDROM_PKTCDVD=m
 CONFIG_ATA_OVER_ETH=m
 CONFIG_DUMMY_IRQ=m
@@ -302,6 +305,7 @@ CONFIG_DM_RAID=m
 CONFIG_DM_ZERO=m
 CONFIG_DM_MULTIPATH=m
 CONFIG_DM_UEVENT=y
+CONFIG_DM_LOG_WRITES=m
 CONFIG_TARGET_CORE=m
 CONFIG_TCM_IBLOCK=m
 CONFIG_TCM_FILEIO=m
@@ -324,6 +328,7 @@ CONFIG_NETCONSOLE_DYNAMIC=y
 CONFIG_VETH=m
 CONFIG_SUN3LANCE=y
 # CONFIG_NET_VENDOR_ARC is not set
+# CONFIG_NET_CADENCE is not set
 CONFIG_SUN3_82586=y
 # CONFIG_NET_VENDOR_MARVELL is not set
 # CONFIG_NET_VENDOR_MICREL is not set
@@ -370,7 +375,6 @@ CONFIG_HID=m
 CONFIG_HIDRAW=y
 CONFIG_UHID=m
 # CONFIG_HID_GENERIC is not set
-# CONFIG_HID_PLANTRONICS is not set
 # CONFIG_USB_SUPPORT is not set
 CONFIG_RTC_CLASS=y
 CONFIG_RTC_DRV_GENERIC=m
index 73c36b7a000978cf3d8f9a6f39271435cf0d2e21..9902c5bfbdc87685f9e815c72a6d4ec40006dfc8 100644 (file)
@@ -170,9 +170,9 @@ CONFIG_IP_SET_HASH_NETPORT=m
 CONFIG_IP_SET_HASH_NETIFACE=m
 CONFIG_IP_SET_LIST_SET=m
 CONFIG_NF_CONNTRACK_IPV4=m
-CONFIG_NF_LOG_ARP=m
 CONFIG_NFT_CHAIN_ROUTE_IPV4=m
 CONFIG_NF_TABLES_ARP=m
+CONFIG_NF_LOG_ARP=m
 CONFIG_NFT_CHAIN_NAT_IPV4=m
 CONFIG_NFT_MASQ_IPV4=m
 CONFIG_NFT_REDIR_IPV4=m
@@ -260,7 +260,9 @@ CONFIG_BATMAN_ADV_DAT=y
 CONFIG_BATMAN_ADV_NC=y
 CONFIG_BATMAN_ADV_MCAST=y
 CONFIG_NETLINK_DIAG=m
+CONFIG_MPLS=y
 CONFIG_NET_MPLS_GSO=m
+CONFIG_MPLS_ROUTING=m
 # CONFIG_WIRELESS is not set
 # CONFIG_UEVENT_HELPER is not set
 CONFIG_DEVTMPFS=y
@@ -272,6 +274,7 @@ CONFIG_BLK_DEV_CRYPTOLOOP=m
 CONFIG_BLK_DEV_DRBD=m
 CONFIG_BLK_DEV_NBD=m
 CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_PMEM=m
 CONFIG_CDROM_PKTCDVD=m
 CONFIG_ATA_OVER_ETH=m
 CONFIG_DUMMY_IRQ=m
@@ -302,6 +305,7 @@ CONFIG_DM_RAID=m
 CONFIG_DM_ZERO=m
 CONFIG_DM_MULTIPATH=m
 CONFIG_DM_UEVENT=y
+CONFIG_DM_LOG_WRITES=m
 CONFIG_TARGET_CORE=m
 CONFIG_TCM_IBLOCK=m
 CONFIG_TCM_FILEIO=m
@@ -324,6 +328,7 @@ CONFIG_NETCONSOLE_DYNAMIC=y
 CONFIG_VETH=m
 CONFIG_SUN3LANCE=y
 # CONFIG_NET_VENDOR_ARC is not set
+# CONFIG_NET_CADENCE is not set
 # CONFIG_NET_VENDOR_BROADCOM is not set
 # CONFIG_NET_VENDOR_INTEL is not set
 # CONFIG_NET_VENDOR_MARVELL is not set
@@ -370,7 +375,6 @@ CONFIG_HID=m
 CONFIG_HIDRAW=y
 CONFIG_UHID=m
 # CONFIG_HID_GENERIC is not set
-# CONFIG_HID_PLANTRONICS is not set
 # CONFIG_USB_SUPPORT is not set
 CONFIG_RTC_CLASS=y
 CONFIG_RTC_DRV_GENERIC=m
index bc755bc620ad0985d6b2a08e57561354a67a301c..83b1df80f0ac0e69c9cf0662981a24358f29a166 100644 (file)
@@ -90,7 +90,6 @@ extern unsigned long __invalid_cmpxchg_size(volatile void *,
  * indicated by comparing RETURN with OLD.
  */
 #ifdef CONFIG_RMW_INSNS
-#define __HAVE_ARCH_CMPXCHG    1
 
 static inline unsigned long __cmpxchg(volatile void *p, unsigned long old,
                                      unsigned long new, int size)
index 8955b40a5dc4304a37c6ff88b328a333f1df13cd..618c85d3c786713c8787043f176eac5d74e13e5e 100644 (file)
@@ -20,6 +20,8 @@
 
 #ifdef __KERNEL__
 
+#define ARCH_HAS_IOREMAP_WT
+
 #include <linux/compiler.h>
 #include <asm/raw_io.h>
 #include <asm/virtconvert.h>
@@ -465,7 +467,7 @@ static inline void __iomem *ioremap_nocache(unsigned long physaddr, unsigned lon
 {
        return __ioremap(physaddr, size, IOMAP_NOCACHE_SER);
 }
-static inline void __iomem *ioremap_writethrough(unsigned long physaddr,
+static inline void __iomem *ioremap_wt(unsigned long physaddr,
                                         unsigned long size)
 {
        return __ioremap(physaddr, size, IOMAP_WRITETHROUGH);
index a93c8cde4d382fc171798affe7090cc59a60f46d..ad7bd40e67428fe420a71ed7e35018a2bd1236e4 100644 (file)
@@ -3,6 +3,8 @@
 
 #ifdef __KERNEL__
 
+#define ARCH_HAS_IOREMAP_WT
+
 #include <asm/virtconvert.h>
 #include <asm-generic/iomap.h>
 
@@ -153,7 +155,7 @@ static inline void *ioremap_nocache(unsigned long physaddr, unsigned long size)
 {
        return __ioremap(physaddr, size, IOMAP_NOCACHE_SER);
 }
-static inline void *ioremap_writethrough(unsigned long physaddr, unsigned long size)
+static inline void *ioremap_wt(unsigned long physaddr, unsigned long size)
 {
        return __ioremap(physaddr, size, IOMAP_WRITETHROUGH);
 }
index e546a5534dd48581c8a1e1a9fc7cf9cc9edb5e21..564665f9af30c9e1da180b20f5176a2b1c0e9731 100644 (file)
@@ -120,13 +120,16 @@ void dma_sync_single_for_device(struct device *dev, dma_addr_t handle,
 }
 EXPORT_SYMBOL(dma_sync_single_for_device);
 
-void dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg, int nents,
-                           enum dma_data_direction dir)
+void dma_sync_sg_for_device(struct device *dev, struct scatterlist *sglist,
+                           int nents, enum dma_data_direction dir)
 {
        int i;
+       struct scatterlist *sg;
 
-       for (i = 0; i < nents; sg++, i++)
-               dma_sync_single_for_device(dev, sg->dma_address, sg->length, dir);
+       for_each_sg(sglist, sg, nents, i) {
+               dma_sync_single_for_device(dev, sg->dma_address, sg->length,
+                                          dir);
+       }
 }
 EXPORT_SYMBOL(dma_sync_sg_for_device);
 
@@ -151,14 +154,16 @@ dma_addr_t dma_map_page(struct device *dev, struct page *page,
 }
 EXPORT_SYMBOL(dma_map_page);
 
-int dma_map_sg(struct device *dev, struct scatterlist *sg, int nents,
+int dma_map_sg(struct device *dev, struct scatterlist *sglist, int nents,
               enum dma_data_direction dir)
 {
        int i;
+       struct scatterlist *sg;
 
-       for (i = 0; i < nents; sg++, i++) {
+       for_each_sg(sglist, sg, nents, i) {
                sg->dma_address = sg_phys(sg);
-               dma_sync_single_for_device(dev, sg->dma_address, sg->length, dir);
+               dma_sync_single_for_device(dev, sg->dma_address, sg->length,
+                                          dir);
        }
        return nents;
 }
index d703d8e26a656c1560202d3c1af6a39ed95771e5..5a696e50793034bfdecefec781f95a402196c99a 100644 (file)
@@ -84,7 +84,7 @@ static inline void fence(void)
 #define read_barrier_depends()         do { } while (0)
 #define smp_read_barrier_depends()     do { } while (0)
 
-#define set_mb(var, value) do { var = value; smp_mb(); } while (0)
+#define smp_store_mb(var, value) do { WRITE_ONCE(var, value); smp_mb(); } while (0)
 
 #define smp_store_release(p, v)                                                \
 do {                                                                   \
index b1bc1be8540f581ab570963f099116507ea751db..be29e3e44321a1aa2d3adcf069522bd5152cf4ff 100644 (file)
@@ -51,8 +51,6 @@ static inline unsigned long __cmpxchg(volatile void *ptr, unsigned long old,
        return old;
 }
 
-#define __HAVE_ARCH_CMPXCHG 1
-
 #define cmpxchg(ptr, o, n)                                             \
        ({                                                              \
                __typeof__(*(ptr)) _o_ = (o);                           \
index d5779b0ec5730a01963a4a86940d1f5eb500084e..9890f21eadbe867ef4c48f5141ce4ceadb54965f 100644 (file)
@@ -160,6 +160,9 @@ extern void __iounmap(void __iomem *addr);
 #define ioremap_wc(offset, size)                \
        __ioremap((offset), (size), _PAGE_WR_COMBINE)
 
+#define ioremap_wt(offset, size)                \
+       __ioremap((offset), (size), 0)
+
 #define iounmap(addr)                           \
        __iounmap(addr)
 
index 940f5fc1d1da13f3988fe99fd69a7d02e0825c8d..39b6315db82ee74bc3aa652460b50c6cdde605a7 100644 (file)
@@ -39,10 +39,10 @@ extern resource_size_t isa_mem_base;
 extern void iounmap(void __iomem *addr);
 
 extern void __iomem *ioremap(phys_addr_t address, unsigned long size);
-#define ioremap_writethrough(addr, size)       ioremap((addr), (size))
 #define ioremap_nocache(addr, size)            ioremap((addr), (size))
 #define ioremap_fullcache(addr, size)          ioremap((addr), (size))
 #define ioremap_wc(addr, size)                 ioremap((addr), (size))
+#define ioremap_wt(addr, size)                 ioremap((addr), (size))
 
 #endif /* CONFIG_MMU */
 
index 468aca8cec0d32033d7859726c4aeedee0ec3423..b42ed684b94594bcfd7b8382e1161a65433ab22a 100644 (file)
@@ -27,8 +27,6 @@
 #define PCIBIOS_MIN_IO         0x1000
 #define PCIBIOS_MIN_MEM                0x10000000
 
-struct pci_dev;
-
 /* Values for the `which' argument to sys_pciconfig_iobase syscall.  */
 #define IOBASE_BRIDGE_NUMBER   0
 #define IOBASE_MEMORY          1
@@ -44,16 +42,6 @@ struct pci_dev;
  */
 #define pcibios_assign_all_busses()    0
 
-#ifdef CONFIG_PCI
-static inline void pci_dma_burst_advice(struct pci_dev *pdev,
-                                       enum pci_dma_burst_strategy *strat,
-                                       unsigned long *strategy_parameter)
-{
-       *strat = PCI_DMA_BURST_INFINITY;
-       *strategy_parameter = ~0UL;
-}
-#endif
-
 extern int pci_domain_nr(struct pci_bus *bus);
 
 /* Decide whether to display the domain number in /proc */
@@ -83,40 +71,12 @@ extern int pci_mmap_legacy_page_range(struct pci_bus *bus,
  */
 #define PCI_DMA_BUS_IS_PHYS     (1)
 
-static inline struct resource *pcibios_select_root(struct pci_dev *pdev,
-                       struct resource *res)
-{
-       struct resource *root = NULL;
-
-       if (res->flags & IORESOURCE_IO)
-               root = &ioport_resource;
-       if (res->flags & IORESOURCE_MEM)
-               root = &iomem_resource;
-
-       return root;
-}
-
 extern void pcibios_claim_one_bus(struct pci_bus *b);
 
 extern void pcibios_finish_adding_to_bus(struct pci_bus *bus);
 
 extern void pcibios_resource_survey(void);
 
-extern struct pci_controller *init_phb_dynamic(struct device_node *dn);
-extern int remove_phb_dynamic(struct pci_controller *phb);
-
-extern struct pci_dev *of_create_pci_dev(struct device_node *node,
-                                       struct pci_bus *bus, int devfn);
-
-extern void of_scan_pci_bridge(struct device_node *node,
-                               struct pci_dev *dev);
-
-extern void of_scan_bus(struct device_node *node, struct pci_bus *bus);
-extern void of_rescan_bus(struct device_node *node, struct pci_bus *bus);
-
-extern int pci_bus_find_capability(struct pci_bus *bus,
-                                               unsigned int devfn, int cap);
-
 struct file;
 extern pgprot_t        pci_phys_mem_access_prot(struct file *file,
                                         unsigned long pfn,
index d1dd6e83d59b15520896822d673ad2d4a80ecd3d..b70bb538f00165df2d46ec87c27217577ec4b95a 100644 (file)
@@ -47,6 +47,8 @@ const struct cpu_ver_key cpu_ver_lookup[] = {
        {"9.1", 0x1d},
        {"9.2", 0x1f},
        {"9.3", 0x20},
+       {"9.4", 0x21},
+       {"9.5", 0x22},
        {NULL, 0},
 };
 
index ed7ba8a118227440cd53c2670cc3afd7ac4bf360..bf4dec229437a836ee1829504be7bf603ce73b37 100644 (file)
@@ -154,6 +154,7 @@ dma_direct_sync_sg_for_device(struct device *dev,
                        __dma_sync(sg->dma_address, sg->length, direction);
 }
 
+static
 int dma_direct_mmap_coherent(struct device *dev, struct vm_area_struct *vma,
                             void *cpu_addr, dma_addr_t handle, size_t size,
                             struct dma_attrs *attrs)
index 8736af5806ae49cc16c11a81c643ed23c61f781a..6366f69d118ecd1af77d8910023ed92afaf7329b 100644 (file)
@@ -32,7 +32,7 @@
 #define GDB_RTLBHI     56
 
 /* keep pvr separately because it is unchangeble */
-struct pvr_s pvr;
+static struct pvr_s pvr;
 
 void pt_regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *regs)
 {
index a73c93c3d44a1069149945cf2732aeed26c918bd..7fc8397d16f21d713ad3e073308f69c75dd88692 100644 (file)
@@ -225,7 +225,7 @@ void __init plat_time_init(void)
        ddr_clk_rate = ath79_get_sys_clk_rate("ddr");
        ref_clk_rate = ath79_get_sys_clk_rate("ref");
 
-       pr_info("Clocks: CPU:%lu.%03luMHz, DDR:%lu.%03luMHz, AHB:%lu.%03luMHz, Ref:%lu.%03luMHz",
+       pr_info("Clocks: CPU:%lu.%03luMHz, DDR:%lu.%03luMHz, AHB:%lu.%03luMHz, Ref:%lu.%03luMHz\n",
                cpu_clk_rate / 1000000, (cpu_clk_rate / 1000) % 1000,
                ddr_clk_rate / 1000000, (ddr_clk_rate / 1000) % 1000,
                ahb_clk_rate / 1000000, (ahb_clk_rate / 1000) % 1000,
index 12dccdb38286092e304224419d2c36ee18b2f88b..af4c712f7afc435c3f2ff7b2092ebc0a6977b955 100644 (file)
@@ -69,10 +69,10 @@ static int octeon_md5_init(struct shash_desc *desc)
 {
        struct md5_state *mctx = shash_desc_ctx(desc);
 
-       mctx->hash[0] = cpu_to_le32(0x67452301);
-       mctx->hash[1] = cpu_to_le32(0xefcdab89);
-       mctx->hash[2] = cpu_to_le32(0x98badcfe);
-       mctx->hash[3] = cpu_to_le32(0x10325476);
+       mctx->hash[0] = cpu_to_le32(MD5_H0);
+       mctx->hash[1] = cpu_to_le32(MD5_H1);
+       mctx->hash[2] = cpu_to_le32(MD5_H2);
+       mctx->hash[3] = cpu_to_le32(MD5_H3);
        mctx->byte_count = 0;
 
        return 0;
index 558e94977942033dc8247bcc510ebb705aa9698a..68f0c5871adcdf51f40380ffbba09b1e5e52202c 100644 (file)
@@ -2,7 +2,6 @@
 # Makefile for the Cobalt micro systems family specific parts of the kernel
 #
 
-obj-y := buttons.o irq.o lcd.o led.o reset.o rtc.o serial.o setup.o time.o
+obj-y := buttons.o irq.o lcd.o led.o mtd.o reset.o rtc.o serial.o setup.o time.o
 
 obj-$(CONFIG_PCI)              += pci.o
-obj-$(CONFIG_MTD_PHYSMAP)      += mtd.o
index 2b8bbbcb9be0e9f8c5c6f7dbc1a2c6f692185443..7ecba84656d4951a4196622344338c842997fb0f 100644 (file)
 #define __WEAK_LLSC_MB         "               \n"
 #endif
 
-#define set_mb(var, value) \
-       do { var = value; smp_mb(); } while (0)
+#define smp_store_mb(var, value) \
+       do { WRITE_ONCE(var, value); smp_mb(); } while (0)
 
 #define smp_llsc_mb()  __asm__ __volatile__(__WEAK_LLSC_MB : : :"memory")
 
index 412f945f1f5ec26e78ab228aa2ee5b36cc2362fe..b71ab4a5fd508ea0ee59d9a27b9d15eee0d7180b 100644 (file)
@@ -138,8 +138,6 @@ static inline unsigned long __xchg(unsigned long x, volatile void * ptr, int siz
                __xchg((unsigned long)(x), (ptr), sizeof(*(ptr))));     \
 })
 
-#define __HAVE_ARCH_CMPXCHG 1
-
 #define __cmpxchg_asm(ld, st, m, old, new)                             \
 ({                                                                     \
        __typeof(*(m)) __ret;                                           \
index 4c25823563fe16dfe8f4008351c111eb0dd5c4ad..e8c8d9d0c45fe7c3600ba39a44e7090ba0827a04 100644 (file)
@@ -839,7 +839,7 @@ static inline void kvm_arch_hardware_unsetup(void) {}
 static inline void kvm_arch_sync_events(struct kvm *kvm) {}
 static inline void kvm_arch_free_memslot(struct kvm *kvm,
                struct kvm_memory_slot *free, struct kvm_memory_slot *dont) {}
-static inline void kvm_arch_memslots_updated(struct kvm *kvm) {}
+static inline void kvm_arch_memslots_updated(struct kvm *kvm, struct kvm_memslots *slots) {}
 static inline void kvm_arch_flush_shadow_all(struct kvm *kvm) {}
 static inline void kvm_arch_flush_shadow_memslot(struct kvm *kvm,
                struct kvm_memory_slot *slot) {}
index aa2283e602fc3ae79a236fa3f17c9aa4434bcd84..aa71216edf99f96e2cf023a233be5fe2502fd6cb 100644 (file)
@@ -16,8 +16,4 @@ struct ath79_spi_platform_data {
        unsigned        num_chipselect;
 };
 
-struct ath79_spi_controller_data {
-       unsigned        gpio;
-};
-
 #endif /* _ATH79_SPI_PLATFORM_H */
index d9692993fc83373057d9594ad945b753b62723e3..70dcc5498128b5e918d8c57496eafa09e8c1071f 100644 (file)
@@ -113,16 +113,6 @@ struct pci_dev;
  */
 extern unsigned int PCI_DMA_BUS_IS_PHYS;
 
-#ifdef CONFIG_PCI
-static inline void pci_dma_burst_advice(struct pci_dev *pdev,
-                                       enum pci_dma_burst_strategy *strat,
-                                       unsigned long *strategy_parameter)
-{
-       *strat = PCI_DMA_BURST_INFINITY;
-       *strategy_parameter = ~0UL;
-}
-#endif
-
 #ifdef CONFIG_PCI_DOMAINS
 #define pci_domain_nr(bus) ((struct pci_controller *)(bus)->sysdata)->index
 
index 18ae5ddef118c071e1240486e90f08be3e4b0871..c28a8499aec7f4fa18c5bd4c71922812d2ebf143 100644 (file)
 #define _PAGE_PRESENT_SHIFT    0
 #define _PAGE_PRESENT          (1 << _PAGE_PRESENT_SHIFT)
 /* R2 or later cores check for RI/XI support to determine _PAGE_READ */
-#ifdef CONFIG_CPU_MIPSR2
+#if defined(CONFIG_CPU_MIPSR2) || defined(CONFIG_CPU_MIPSR6)
 #define _PAGE_WRITE_SHIFT      (_PAGE_PRESENT_SHIFT + 1)
 #define _PAGE_WRITE            (1 << _PAGE_WRITE_SHIFT)
 #else
 #define _PAGE_SPLITTING                (1 << _PAGE_SPLITTING_SHIFT)
 
 /* Only R2 or newer cores have the XI bit */
-#ifdef CONFIG_CPU_MIPSR2
+#if defined(CONFIG_CPU_MIPSR2) || defined(CONFIG_CPU_MIPSR6)
 #define _PAGE_NO_EXEC_SHIFT    (_PAGE_SPLITTING_SHIFT + 1)
 #else
 #define _PAGE_GLOBAL_SHIFT     (_PAGE_SPLITTING_SHIFT + 1)
 #define _PAGE_GLOBAL           (1 << _PAGE_GLOBAL_SHIFT)
-#endif /* CONFIG_CPU_MIPSR2 */
+#endif /* CONFIG_CPU_MIPSR2 || CONFIG_CPU_MIPSR6 */
 
 #endif /* CONFIG_64BIT && CONFIG_MIPS_HUGE_TLB_SUPPORT */
 
-#ifdef CONFIG_CPU_MIPSR2
+#if defined(CONFIG_CPU_MIPSR2) || defined(CONFIG_CPU_MIPSR6)
 /* XI - page cannot be executed */
 #ifndef _PAGE_NO_EXEC_SHIFT
 #define _PAGE_NO_EXEC_SHIFT    (_PAGE_MODIFIED_SHIFT + 1)
 #define _PAGE_GLOBAL_SHIFT     (_PAGE_NO_READ_SHIFT + 1)
 #define _PAGE_GLOBAL           (1 << _PAGE_GLOBAL_SHIFT)
 
-#else  /* !CONFIG_CPU_MIPSR2 */
+#else  /* !CONFIG_CPU_MIPSR2 && !CONFIG_CPU_MIPSR6 */
 #define _PAGE_GLOBAL_SHIFT     (_PAGE_MODIFIED_SHIFT + 1)
 #define _PAGE_GLOBAL           (1 << _PAGE_GLOBAL_SHIFT)
-#endif /* CONFIG_CPU_MIPSR2 */
+#endif /* CONFIG_CPU_MIPSR2 || CONFIG_CPU_MIPSR6 */
 
 #define _PAGE_VALID_SHIFT      (_PAGE_GLOBAL_SHIFT + 1)
 #define _PAGE_VALID            (1 << _PAGE_VALID_SHIFT)
  */
 static inline uint64_t pte_to_entrylo(unsigned long pte_val)
 {
-#ifdef CONFIG_CPU_MIPSR2
+#if defined(CONFIG_CPU_MIPSR2) || defined(CONFIG_CPU_MIPSR6)
        if (cpu_has_rixi) {
                int sa;
 #ifdef CONFIG_32BIT
index e92d6c4b5ed192305b0b1f1605481f745cfadb10..7163cd7fdd69a622892e4be83acbe0450e8f2af0 100644 (file)
@@ -104,7 +104,6 @@ do {                                                                        \
        if (test_and_clear_tsk_thread_flag(prev, TIF_USEDMSA))          \
                __fpsave = FP_SAVE_VECTOR;                              \
        (last) = resume(prev, next, task_thread_info(next), __fpsave);  \
-       disable_msa();                                                  \
 } while (0)
 
 #define finish_arch_switch(prev)                                       \
@@ -122,6 +121,7 @@ do {                                                                        \
        if (cpu_has_userlocal)                                          \
                write_c0_userlocal(current_thread_info()->tp_value);    \
        __restore_watch();                                              \
+       disable_msa();                                                  \
 } while (0)
 
 #endif /* _ASM_SWITCH_TO_H */
index e36515dcd3b29efcdb0014c7dfd4541805eb4e4c..209e5b76c1bce56f02ceeb1fdeffeccc6fe46bd8 100644 (file)
@@ -74,13 +74,12 @@ static inline void cpu_set_fpu_fcsr_mask(struct cpuinfo_mips *c)
 {
        unsigned long sr, mask, fcsr, fcsr0, fcsr1;
 
+       fcsr = c->fpu_csr31;
        mask = FPU_CSR_ALL_X | FPU_CSR_ALL_E | FPU_CSR_ALL_S | FPU_CSR_RM;
 
        sr = read_c0_status();
        __enable_fpu(FPU_AS_IS);
 
-       fcsr = read_32bit_cp1_register(CP1_STATUS);
-
        fcsr0 = fcsr & mask;
        write_32bit_cp1_register(CP1_STATUS, fcsr0);
        fcsr0 = read_32bit_cp1_register(CP1_STATUS);
index 51f57d8416625a26cc73d38a615c750fd4dd2f51..3c8a18a00a65fee62e7cc11068d3866b18b04fd1 100644 (file)
@@ -109,7 +109,7 @@ void __init init_IRQ(void)
 #endif
 }
 
-#ifdef DEBUG_STACKOVERFLOW
+#ifdef CONFIG_DEBUG_STACKOVERFLOW
 static inline void check_stack_overflow(void)
 {
        unsigned long sp;
index 4b50c5787e25bdb4bdb28eb7736296e1d5d3812c..d5fa3eaf39a106546f52d82ec3e5391302ef8dec 100644 (file)
@@ -2409,7 +2409,7 @@ enum emulation_result kvm_mips_complete_mmio_load(struct kvm_vcpu *vcpu,
                if (vcpu->mmio_needed == 2)
                        *gpr = *(int16_t *) run->mmio.data;
                else
-                       *gpr = *(int16_t *) run->mmio.data;
+                       *gpr = *(uint16_t *)run->mmio.data;
 
                break;
        case 1:
index bb68e8d520e83b5a30b74b22ae3292b1dd1469e1..cd4c129ce7434d60fc6af74bb6b764b536de4dda 100644 (file)
@@ -198,15 +198,16 @@ int kvm_arch_create_memslot(struct kvm *kvm, struct kvm_memory_slot *slot,
 
 int kvm_arch_prepare_memory_region(struct kvm *kvm,
                                   struct kvm_memory_slot *memslot,
-                                  struct kvm_userspace_memory_region *mem,
+                                  const struct kvm_userspace_memory_region *mem,
                                   enum kvm_mr_change change)
 {
        return 0;
 }
 
 void kvm_arch_commit_memory_region(struct kvm *kvm,
-                                  struct kvm_userspace_memory_region *mem,
+                                  const struct kvm_userspace_memory_region *mem,
                                   const struct kvm_memory_slot *old,
+                                  const struct kvm_memory_slot *new,
                                   enum kvm_mr_change change)
 {
        unsigned long npages = 0;
@@ -393,7 +394,7 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run)
        kvm_mips_deliver_interrupts(vcpu,
                                    kvm_read_c0_guest_cause(vcpu->arch.cop0));
 
-       kvm_guest_enter();
+       __kvm_guest_enter();
 
        /* Disable hardware page table walking while in guest */
        htw_stop();
@@ -403,7 +404,7 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run)
        /* Re-enable HTW before enabling interrupts */
        htw_start();
 
-       kvm_guest_exit();
+       __kvm_guest_exit();
        local_irq_enable();
 
        if (vcpu->sigset_active)
@@ -968,6 +969,7 @@ out:
 /* Get (and clear) the dirty memory log for a memory slot. */
 int kvm_vm_ioctl_get_dirty_log(struct kvm *kvm, struct kvm_dirty_log *log)
 {
+       struct kvm_memslots *slots;
        struct kvm_memory_slot *memslot;
        unsigned long ga, ga_end;
        int is_dirty = 0;
@@ -982,7 +984,8 @@ int kvm_vm_ioctl_get_dirty_log(struct kvm *kvm, struct kvm_dirty_log *log)
 
        /* If nothing is dirty, don't bother messing with page tables. */
        if (is_dirty) {
-               memslot = &kvm->memslots->memslots[log->slot];
+               slots = kvm_memslots(kvm);
+               memslot = id_to_memslot(slots, log->slot);
 
                ga = memslot->base_gfn << PAGE_SHIFT;
                ga_end = ga + (memslot->npages << PAGE_SHIFT);
index e70c33fdb88153ac6bfdf12a4f632d3b3a26ccb9..f2e8153e44f536213e196002f005bb86da9ef72f 100644 (file)
@@ -3,15 +3,13 @@
 #
 
 obj-y += setup.o init.o cmdline.o env.o time.o reset.o irq.o \
-    bonito-irq.o mem.o machtype.o platform.o
+    bonito-irq.o mem.o machtype.o platform.o serial.o
 obj-$(CONFIG_PCI) += pci.o
 
 #
 # Serial port support
 #
 obj-$(CONFIG_EARLY_PRINTK) += early_printk.o
-loongson-serial-$(CONFIG_SERIAL_8250) := serial.o
-obj-y += $(loongson-serial-m) $(loongson-serial-y)
 obj-$(CONFIG_LOONGSON_UART_BASE) += uart_base.o
 obj-$(CONFIG_LOONGSON_MC146818) += rtc.o
 
index e3c68b5da18da4012de0aaed6363d5a5484d5e41..509877c6e9d908d7bac6110982c7208ab69204af 100644 (file)
@@ -272,7 +272,7 @@ void loongson3_ipi_interrupt(struct pt_regs *regs)
        if (action & SMP_ASK_C0COUNT) {
                BUG_ON(cpu != 0);
                c0count = read_c0_count();
-               for (i = 1; i < loongson_sysconf.nr_cpus; i++)
+               for (i = 1; i < num_possible_cpus(); i++)
                        per_cpu(core0_c0count, i) = c0count;
        }
 }
index 0dbb65a51ce5b1c2913cfec00571710e3a0ecb10..2e03ab1735911d202ce82c97b4911b5c1002ed70 100644 (file)
@@ -1372,7 +1372,7 @@ static int probe_scache(void)
        scache_size = addr;
        c->scache.linesz = 16 << ((config & R4K_CONF_SB) >> 22);
        c->scache.ways = 1;
-       c->dcache.waybit = 0;           /* does not matter */
+       c->scache.waybit = 0;           /* does not matter */
 
        return 1;
 }
index 5d6139390bf830adf503d67d004a5322d8eb7ad4..e23fdf2a9c80d2f0dbbb498343efb859c08f3b4e 100644 (file)
@@ -681,11 +681,7 @@ static unsigned int get_stack_depth(struct jit_ctx *ctx)
                sp_off += config_enabled(CONFIG_64BIT) ?
                        (ARGS_USED_BY_JIT + 1) * RSIZE : RSIZE;
 
-       /*
-        * Subtract the bytes for the last registers since we only care about
-        * the location on the stack pointer.
-        */
-       return sp_off - RSIZE;
+       return sp_off;
 }
 
 static void build_prologue(struct jit_ctx *ctx)
index a138e8ee5cfcf7bb8c2025202ea3e728c4240476..b3ab59318d91a7a2817e6615efba1ef3fc0a2c37 100644 (file)
@@ -13,7 +13,6 @@
 #include <linux/kernel.h>
 #include <linux/init.h>
 
-#include <asm/pci.h>
 #include <asm/io.h>
 #include <asm/gt64120.h>
 
index 6b5821febc38fbd3abcf787e35748db7a709ebf3..951d8070fb4868993847ad8a35385ec791fbecf8 100644 (file)
@@ -8,7 +8,6 @@
 #include <linux/kernel.h>
 #include <linux/pci.h>
 #include <linux/types.h>
-#include <asm/pci.h>
 #include <asm/ip32/mace.h>
 
 #if 0
index 8b117e63830659d12d4669258663c3976df9f16e..c5347d99cf3a2b2583f78b86c3429caab8434d11 100644 (file)
@@ -20,7 +20,6 @@
 #include <linux/of_irq.h>
 #include <linux/of_pci.h>
 
-#include <asm/pci.h>
 #include <asm/gpio.h>
 #include <asm/addrspace.h>
 
index e20b02e3ae28be201789dd260ab79a382be944f8..e10d10b9e82a98bf5e53382d88cbc98b769ef53c 100644 (file)
@@ -41,7 +41,7 @@ static irqreturn_t ill_acc_irq_handler(int irq, void *_priv)
                addr, (type >> ILL_ACC_OFF_S) & ILL_ACC_OFF_M,
                type & ILL_ACC_LEN_M);
 
-       rt_memc_w32(REG_ILL_ACC_TYPE, REG_ILL_ACC_TYPE);
+       rt_memc_w32(ILL_INT_STATUS, REG_ILL_ACC_TYPE);
 
        return IRQ_HANDLED;
 }
index cc4a2ba9e228998c7ccbd5af24d5e5d9c5c42fb6..07c5b4a3903ba61ede14417e63f0d9b7a630f7db 100644 (file)
@@ -282,6 +282,7 @@ static inline void __iomem *ioremap_nocache(unsigned long offset, unsigned long
 }
 
 #define ioremap_wc ioremap_nocache
+#define ioremap_wt ioremap_nocache
 
 static inline void iounmap(void __iomem *addr)
 {
index 5f70af25c7d02a54315ed2b2da2e6fd12652e7d8..c222d1792d5b57681b37f3188d287237a90b74fd 100644 (file)
@@ -83,19 +83,6 @@ extern int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma,
 /* implement the pci_ DMA API in terms of the generic device dma_ one */
 #include <asm-generic/pci-dma-compat.h>
 
-static inline struct resource *
-pcibios_select_root(struct pci_dev *pdev, struct resource *res)
-{
-       struct resource *root = NULL;
-
-       if (res->flags & IORESOURCE_IO)
-               root = &ioport_resource;
-       if (res->flags & IORESOURCE_MEM)
-               root = &iomem_resource;
-
-       return root;
-}
-
 static inline int pci_get_legacy_ide_irq(struct pci_dev *dev, int channel)
 {
        return channel ? 15 : 14;
index 6e24d7cceb0c1407db80f7354e662f11b5f7d339..c5a62da22cd2eb40b5e0bc16588f23f0cc7ecdd4 100644 (file)
@@ -46,6 +46,7 @@ static inline void iounmap(void __iomem *addr)
 }
 
 #define ioremap_wc ioremap_nocache
+#define ioremap_wt ioremap_nocache
 
 /* Pages to physical address... */
 #define page_to_phys(page)     virt_to_phys(page_to_virt(page))
index 7f4547418ee1c8cb5ae5a2fa8398a39da068f406..be186a75f6225f46cb0f18e3bd191257aa20e460 100644 (file)
@@ -8,6 +8,7 @@
  * for more details.
  */
 
+#include <linux/export.h>
 #include <linux/interrupt.h>
 #include <linux/clockchips.h>
 #include <linux/clocksource.h>
@@ -106,6 +107,7 @@ cycles_t get_cycles(void)
 {
        return nios2_timer_read(&nios2_cs.cs);
 }
+EXPORT_SYMBOL(get_cycles);
 
 static void nios2_timer_start(struct nios2_timer *timer)
 {
index dbd13354ec414b70a9abb814f62fc94035ccdf08..0a90b965cccbefe172be5dde879b5035b4589685 100644 (file)
@@ -46,8 +46,6 @@ __xchg(unsigned long x, __volatile__ void *ptr, int size)
 #define xchg(ptr, x) \
        ((__typeof__(*(ptr)))__xchg((unsigned long)(x), (ptr), sizeof(*(ptr))))
 
-#define __HAVE_ARCH_CMPXCHG    1
-
 /* bug catcher for when unsupported size is used - won't link */
 extern void __cmpxchg_called_with_bad_pointer(void);
 
index 20df2b04fc09f469ed517df9acc240bddff386e7..bf5e044281d693a53a2f008ae7654750fd5b1060 100644 (file)
@@ -196,25 +196,6 @@ static inline void pcibios_register_hba(struct pci_hba_data *x)
 /* export the pci_ DMA API in terms of the dma_ one */
 #include <asm-generic/pci-dma-compat.h>
 
-#ifdef CONFIG_PCI
-static inline void pci_dma_burst_advice(struct pci_dev *pdev,
-                                       enum pci_dma_burst_strategy *strat,
-                                       unsigned long *strategy_parameter)
-{
-       unsigned long cacheline_size;
-       u8 byte;
-
-       pci_read_config_byte(pdev, PCI_CACHE_LINE_SIZE, &byte);
-       if (byte == 0)
-               cacheline_size = 1024;
-       else
-               cacheline_size = (int) byte * 4;
-
-       *strat = PCI_DMA_BURST_MULTIPLE;
-       *strategy_parameter = cacheline_size;
-}
-#endif
-
 static inline int pci_get_legacy_ide_irq(struct pci_dev *dev, int channel)
 {
        return channel ? 15 : 14;
index 0efa8f90a8f1055f040c9a62360c97d4aa4253e0..3a510f4a6b68cfe56f711ed81d46fcf1b2e0ee72 100644 (file)
@@ -19,6 +19,14 @@ config PPC_WERROR
        depends on !PPC_DISABLE_WERROR
        default y
 
+config STRICT_MM_TYPECHECKS
+       bool "Do extra type checking on mm types"
+       default n
+       help
+         This option turns on extra type checking for some mm related types.
+
+         If you don't know what this means, say N.
+
 config PRINT_STACK_DEPTH
        int "Stack depth to print" if DEBUG_KERNEL
        default 64
index 07a480861f785295f51a28879c9ecfa9dad6955f..05f464eb69527e1d59dff1a13fdd449d6b7aa25f 100644 (file)
@@ -66,7 +66,10 @@ endif
 UTS_MACHINE := $(OLDARCH)
 
 ifeq ($(CONFIG_CPU_LITTLE_ENDIAN),y)
-override CC    += -mlittle-endian -mno-strict-align
+override CC    += -mlittle-endian
+ifneq ($(COMPILER),clang)
+override CC    += -mno-strict-align
+endif
 override AS    += -mlittle-endian
 override LD    += -EL
 override CROSS32CC += -mlittle-endian
@@ -113,14 +116,14 @@ else
 endif
 endif
 
-CFLAGS-$(CONFIG_PPC64) := -mtraceback=no
+CFLAGS-$(CONFIG_PPC64) := $(call cc-option,-mtraceback=no)
 ifeq ($(CONFIG_CPU_LITTLE_ENDIAN),y)
-CFLAGS-$(CONFIG_PPC64) += $(call cc-option,-mabi=elfv2,-mcall-aixdesc)
+CFLAGS-$(CONFIG_PPC64) += $(call cc-option,-mabi=elfv2,$(call cc-option,-mcall-aixdesc))
 AFLAGS-$(CONFIG_PPC64) += $(call cc-option,-mabi=elfv2)
 else
-CFLAGS-$(CONFIG_PPC64) += -mcall-aixdesc
+CFLAGS-$(CONFIG_PPC64) += $(call cc-option,-mcall-aixdesc)
 endif
-CFLAGS-$(CONFIG_PPC64) += $(call cc-option,-mcmodel=medium,-mminimal-toc)
+CFLAGS-$(CONFIG_PPC64) += $(call cc-option,-mcmodel=medium,$(call cc-option,-mminimal-toc))
 CFLAGS-$(CONFIG_PPC64) += $(call cc-option,-mno-pointers-to-nested-functions)
 CFLAGS-$(CONFIG_PPC32) := -ffixed-r2 $(MULTIPLEWORD)
 
@@ -160,7 +163,8 @@ asinstr := $(call as-instr,lis 9$(comma)foo@high,-DHAVE_AS_ATHIGH=1)
 
 KBUILD_CPPFLAGS        += -Iarch/$(ARCH) $(asinstr)
 KBUILD_AFLAGS  += -Iarch/$(ARCH) $(AFLAGS-y)
-KBUILD_CFLAGS  += -msoft-float -pipe -Iarch/$(ARCH) $(CFLAGS-y)
+KBUILD_CFLAGS  += $(call cc-option,-msoft-float)
+KBUILD_CFLAGS  += -pipe -Iarch/$(ARCH) $(CFLAGS-y)
 CPP            = $(CC) -E $(KBUILD_CFLAGS)
 
 CHECKFLAGS     += -m$(CONFIG_WORD_SIZE) -D__powerpc__ -D__powerpc$(CONFIG_WORD_SIZE)__
@@ -192,7 +196,7 @@ KBUILD_CFLAGS       += $(call cc-option,-fno-dwarf2-cfi-asm)
 
 # Never use string load/store instructions as they are
 # often slow when they are implemented at all
-KBUILD_CFLAGS          += -mno-string
+KBUILD_CFLAGS          += $(call cc-option,-mno-string)
 
 ifeq ($(CONFIG_6xx),y)
 KBUILD_CFLAGS          += -mcpu=powerpc
@@ -269,6 +273,21 @@ bootwrapper_install:
 %.dtb: scripts
        $(Q)$(MAKE) ARCH=ppc64 $(build)=$(boot) $(patsubst %,$(boot)/%,$@)
 
+# Used to create 'merged defconfigs'
+# To use it $(call) it with the first argument as the base defconfig
+# and the second argument as a space separated list of .config files to merge,
+# without the .config suffix.
+define merge_into_defconfig
+       $(Q)$(CONFIG_SHELL) $(srctree)/scripts/kconfig/merge_config.sh \
+               -m -O $(objtree) $(srctree)/arch/$(ARCH)/configs/$(1) \
+               $(foreach config,$(2),$(srctree)/arch/$(ARCH)/configs/$(config).config)
+       +$(Q)$(MAKE) -f $(srctree)/Makefile olddefconfig
+endef
+
+PHONY += pseries_le_defconfig
+pseries_le_defconfig:
+       $(call merge_into_defconfig,pseries_defconfig,le)
+
 define archhelp
   @echo '* zImage          - Build default images selected by kernel config'
   @echo '  zImage.*        - Compressed kernel image (arch/$(ARCH)/boot/zImage.*)'
@@ -314,7 +333,8 @@ TOUT        := .tmp_gas_check
 # - Require gcc 4.0 or above on 64-bit
 # - gcc-4.2.0 has issues compiling modules on 64-bit
 checkbin:
-       @if test "$(cc-version)" = "0304" ; then \
+       @if test "${COMPILER}" != "clang" \
+           && test "$(cc-version)" = "0304" ; then \
                if ! /bin/echo mftb 5 | $(AS) -v -mppc -many -o $(TOUT) >/dev/null 2>&1 ; then \
                        echo -n '*** ${VERSION}.${PATCHLEVEL} kernels no longer build '; \
                        echo 'correctly with gcc-3.4 and your version of binutils.'; \
@@ -322,13 +342,15 @@ checkbin:
                        false; \
                fi ; \
        fi
-       @if test "$(cc-version)" -lt "0400" \
+       @if test "${COMPILER}" != "clang" \
+           && test "$(cc-version)" -lt "0400" \
            && test "x${CONFIG_PPC64}" = "xy" ; then \
                 echo -n "Sorry, GCC v4.0 or above is required to build " ; \
                 echo "the 64-bit powerpc kernel." ; \
                 false ; \
         fi
-       @if test "$(cc-fullversion)" = "040200" \
+       @if test "${COMPILER}" != "clang" \
+           && test "$(cc-fullversion)" = "040200" \
            && test "x${CONFIG_MODULES}${CONFIG_PPC64}" = "xyy" ; then \
                echo -n '*** GCC-4.2.0 cannot compile the 64-bit powerpc ' ; \
                echo 'kernel with modules enabled.' ; \
@@ -336,6 +358,14 @@ checkbin:
                echo 'disable kernel modules' ; \
                false ; \
        fi
+       @if test "x${CONFIG_CPU_LITTLE_ENDIAN}" = "xy" \
+           && $(LD) --version | head -1 | grep ' 2\.24$$' >/dev/null ; then \
+               echo -n '*** binutils 2.24 miscompiles weak symbols ' ; \
+               echo 'in some circumstances.' ; \
+               echo -n '*** Please use a different binutils version.' ; \
+               false ; \
+       fi
+
 
 CLEAN_FILES += $(TOUT)
 
index 24ed80dc2120d5df44092d0b613100289f978fed..559d00657fb5e0422747f34a190cf341047538f3 100644 (file)
                        size = <0 0x1000000>;
                        alignment = <0 0x1000000>;
                };
+               qman_fqd: qman-fqd {
+                       size = <0 0x400000>;
+                       alignment = <0 0x400000>;
+               };
+               qman_pfdr: qman-pfdr {
+                       size = <0 0x2000000>;
+                       alignment = <0 0x2000000>;
+               };
        };
 
        dcsr: dcsr@f00000000 {
                ranges = <0x0 0xf 0xf4000000 0x2000000>;
        };
 
+       qportals: qman-portals@ff6000000 {
+               ranges = <0x0 0xf 0xf6000000 0x2000000>;
+       };
+
        soc: soc@ffe000000 {
                ranges = <0x00000000 0xf 0xfe000000 0x1000000>;
                reg = <0xf 0xfe000000 0 0x00001000>;
index 86161ae6c966e091ef32c053e8662f44b804d927..1ea8602e4345f4a3bde9bdd57f959cdd0c8c51ef 100644 (file)
                compatible = "fsl,b4420-device-config", "fsl,qoriq-device-config-2.0";
        };
 
-/include/ "qoriq-clockgen2.dtsi"
        global-utilities@e1000 {
-               compatible = "fsl,b4420-clockgen", "fsl,qoriq-clockgen-2.0";
-
-               mux0: mux0@0 {
-                       #clock-cells = <0>;
-                       reg = <0x0 0x4>;
-                       compatible = "fsl,qoriq-core-mux-2.0";
-                       clocks = <&pll0 0>, <&pll0 1>, <&pll0 2>,
-                               <&pll1 0>, <&pll1 1>, <&pll1 2>;
-                       clock-names = "pll0", "pll0-div2", "pll0-div4",
-                               "pll1", "pll1-div2", "pll1-div4";
-                       clock-output-names = "cmux0";
-               };
+               compatible = "fsl,b4420-clockgen", "fsl,b4-clockgen",
+                             "fsl,qoriq-clockgen-2.0";
        };
 
        rcpm: global-utilities@e2000 {
index f35e9e0a54455793d306a2ede44141f0636a2f16..9ba904be39ee185cdd5d70ff0d9431d1a5bfcdad 100644 (file)
        };
 };
 
+&qportals {
+       qportal14: qman-portal@38000 {
+               compatible = "fsl,qman-portal";
+               reg = <0x38000 0x4000>, <0x100e000 0x1000>;
+               interrupts = <132 0x2 0 0>;
+               cell-index = <0xe>;
+       };
+       qportal15: qman-portal@3c000 {
+               compatible = "fsl,qman-portal";
+               reg = <0x3c000 0x4000>, <0x100f000 0x1000>;
+               interrupts = <134 0x2 0 0>;
+               cell-index = <0xf>;
+       };
+       qportal16: qman-portal@40000 {
+               compatible = "fsl,qman-portal";
+               reg = <0x40000 0x4000>, <0x1010000 0x1000>;
+               interrupts = <136 0x2 0 0>;
+               cell-index = <0x10>;
+       };
+       qportal17: qman-portal@44000 {
+               compatible = "fsl,qman-portal";
+               reg = <0x44000 0x4000>, <0x1011000 0x1000>;
+               interrupts = <138 0x2 0 0>;
+               cell-index = <0x11>;
+       };
+       qportal18: qman-portal@48000 {
+               compatible = "fsl,qman-portal";
+               reg = <0x48000 0x4000>, <0x1012000 0x1000>;
+               interrupts = <140 0x2 0 0>;
+               cell-index = <0x12>;
+       };
+       qportal19: qman-portal@4c000 {
+               compatible = "fsl,qman-portal";
+               reg = <0x4c000 0x4000>, <0x1013000 0x1000>;
+               interrupts = <142 0x2 0 0>;
+               cell-index = <0x13>;
+       };
+       qportal20: qman-portal@50000 {
+               compatible = "fsl,qman-portal";
+               reg = <0x50000 0x4000>, <0x1014000 0x1000>;
+               interrupts = <144 0x2 0 0>;
+               cell-index = <0x14>;
+       };
+       qportal21: qman-portal@54000 {
+               compatible = "fsl,qman-portal";
+               reg = <0x54000 0x4000>, <0x1015000 0x1000>;
+               interrupts = <146 0x2 0 0>;
+               cell-index = <0x15>;
+       };
+       qportal22: qman-portal@58000 {
+               compatible = "fsl,qman-portal";
+               reg = <0x58000 0x4000>, <0x1016000 0x1000>;
+               interrupts = <148 0x2 0 0>;
+               cell-index = <0x16>;
+       };
+       qportal23: qman-portal@5c000 {
+               compatible = "fsl,qman-portal";
+               reg = <0x5c000 0x4000>, <0x1017000 0x1000>;
+               interrupts = <150 0x2 0 0>;
+               cell-index = <0x17>;
+       };
+       qportal24: qman-portal@60000 {
+               compatible = "fsl,qman-portal";
+               reg = <0x60000 0x4000>, <0x1018000 0x1000>;
+               interrupts = <152 0x2 0 0>;
+               cell-index = <0x18>;
+       };
+};
+
 &soc {
        ddr2: memory-controller@9000 {
                compatible = "fsl,qoriq-memory-controller-v4.5", "fsl,qoriq-memory-controller";
                compatible = "fsl,b4860-device-config", "fsl,qoriq-device-config-2.0";
        };
 
-/include/ "qoriq-clockgen2.dtsi"
        global-utilities@e1000 {
-               compatible = "fsl,b4860-clockgen", "fsl,qoriq-clockgen-2.0";
-
-               mux0: mux0@0 {
-                       #clock-cells = <0>;
-                       reg = <0x0 0x4>;
-                       compatible = "fsl,qoriq-core-mux-2.0";
-                       clocks = <&pll0 0>, <&pll0 1>, <&pll0 2>,
-                               <&pll1 0>, <&pll1 1>, <&pll1 2>;
-                       clock-names = "pll0", "pll0-div2", "pll0-div4",
-                               "pll1", "pll1-div2", "pll1-div4";
-                       clock-output-names = "cmux0";
-               };
+               compatible = "fsl,b4860-clockgen", "fsl,b4-clockgen",
+                             "fsl,qoriq-clockgen-2.0";
        };
 
        rcpm: global-utilities@e2000 {
index 73136c0029d223f1b19d372dd58b0891ed61c0b4..603910ac1db0e4d89ba5684eecae4fd8f8d2014f 100644 (file)
        alloc-ranges = <0 0 0x10000 0>;
 };
 
+&qman_fqd {
+       compatible = "fsl,qman-fqd";
+       alloc-ranges = <0 0 0x10000 0>;
+};
+
+&qman_pfdr {
+       compatible = "fsl,qman-pfdr";
+       alloc-ranges = <0 0 0x10000 0>;
+};
+
 &ifc {
        #address-cells = <2>;
        #size-cells = <1>;
        };
 };
 
+&qportals {
+       #address-cells = <0x1>;
+       #size-cells = <0x1>;
+       compatible = "simple-bus";
+
+       qportal0: qman-portal@0 {
+               compatible = "fsl,qman-portal";
+               reg = <0x0 0x4000>, <0x1000000 0x1000>;
+               interrupts = <104 0x2 0 0>;
+               cell-index = <0x0>;
+       };
+       qportal1: qman-portal@4000 {
+               compatible = "fsl,qman-portal";
+               reg = <0x4000 0x4000>, <0x1001000 0x1000>;
+               interrupts = <106 0x2 0 0>;
+               cell-index = <0x1>;
+       };
+       qportal2: qman-portal@8000 {
+               compatible = "fsl,qman-portal";
+               reg = <0x8000 0x4000>, <0x1002000 0x1000>;
+               interrupts = <108 0x2 0 0>;
+               cell-index = <0x2>;
+       };
+       qportal3: qman-portal@c000 {
+               compatible = "fsl,qman-portal";
+               reg = <0xc000 0x4000>, <0x1003000 0x1000>;
+               interrupts = <110 0x2 0 0>;
+               cell-index = <0x3>;
+       };
+       qportal4: qman-portal@10000 {
+               compatible = "fsl,qman-portal";
+               reg = <0x10000 0x4000>, <0x1004000 0x1000>;
+               interrupts = <112 0x2 0 0>;
+               cell-index = <0x4>;
+       };
+       qportal5: qman-portal@14000 {
+               compatible = "fsl,qman-portal";
+               reg = <0x14000 0x4000>, <0x1005000 0x1000>;
+               interrupts = <114 0x2 0 0>;
+               cell-index = <0x5>;
+       };
+       qportal6: qman-portal@18000 {
+               compatible = "fsl,qman-portal";
+               reg = <0x18000 0x4000>, <0x1006000 0x1000>;
+               interrupts = <116 0x2 0 0>;
+               cell-index = <0x6>;
+       };
+       qportal7: qman-portal@1c000 {
+               compatible = "fsl,qman-portal";
+               reg = <0x1c000 0x4000>, <0x1007000 0x1000>;
+               interrupts = <118 0x2 0 0>;
+               cell-index = <0x7>;
+       };
+       qportal8: qman-portal@20000 {
+               compatible = "fsl,qman-portal";
+               reg = <0x20000 0x4000>, <0x1008000 0x1000>;
+               interrupts = <120 0x2 0 0>;
+               cell-index = <0x8>;
+       };
+       qportal9: qman-portal@24000 {
+               compatible = "fsl,qman-portal";
+               reg = <0x24000 0x4000>, <0x1009000 0x1000>;
+               interrupts = <122 0x2 0 0>;
+               cell-index = <0x9>;
+       };
+       qportal10: qman-portal@28000 {
+               compatible = "fsl,qman-portal";
+               reg = <0x28000 0x4000>, <0x100a000 0x1000>;
+               interrupts = <124 0x2 0 0>;
+               cell-index = <0xa>;
+       };
+       qportal11: qman-portal@2c000 {
+               compatible = "fsl,qman-portal";
+               reg = <0x2c000 0x4000>, <0x100b000 0x1000>;
+               interrupts = <126 0x2 0 0>;
+               cell-index = <0xb>;
+       };
+       qportal12: qman-portal@30000 {
+               compatible = "fsl,qman-portal";
+               reg = <0x30000 0x4000>, <0x100c000 0x1000>;
+               interrupts = <128 0x2 0 0>;
+               cell-index = <0xc>;
+       };
+       qportal13: qman-portal@34000 {
+               compatible = "fsl,qman-portal";
+               reg = <0x34000 0x4000>, <0x100d000 0x1000>;
+               interrupts = <130 0x2 0 0>;
+               cell-index = <0xd>;
+       };
+};
+
 &soc {
        #address-cells = <1>;
        #size-cells = <1>;
                fsl,liodn-bits = <12>;
        };
 
+/include/ "qoriq-clockgen2.dtsi"
        clockgen: global-utilities@e1000 {
                compatible = "fsl,b4-clockgen", "fsl,qoriq-clockgen-2.0";
                reg = <0xe1000 0x1000>;
+
+               mux0: mux0@0 {
+                       #clock-cells = <0>;
+                       reg = <0x0 0x4>;
+                       compatible = "fsl,qoriq-core-mux-2.0";
+                       clocks = <&pll0 0>, <&pll0 1>, <&pll0 2>,
+                               <&pll1 0>, <&pll1 1>, <&pll1 2>;
+                       clock-names = "pll0", "pll0-div2", "pll0-div4",
+                               "pll1", "pll1-div2", "pll1-div4";
+                       clock-output-names = "cmux0";
+               };
        };
 
        rcpm: global-utilities@e2000 {
 /include/ "qoriq-duart-1.dtsi"
 /include/ "qoriq-sec5.3-0.dtsi"
 
+/include/ "qoriq-qman3.dtsi"
+       qman: qman@318000 {
+               interrupts = <16 2 1 28>;
+       };
+
 /include/ "qoriq-bman1.dtsi"
        bman: bman@31a000 {
                interrupts = <16 2 1 29>;
index 7780f21430cba57b61f4f332e20f57cbc5ff553a..da6d3fc6ba41501f31a42389464659d33e469e14 100644 (file)
        alloc-ranges = <0 0 0x10 0>;
 };
 
+&qman_fqd {
+       compatible = "fsl,qman-fqd";
+       alloc-ranges = <0 0 0x10 0>;
+};
+
+&qman_pfdr {
+       compatible = "fsl,qman-pfdr";
+       alloc-ranges = <0 0 0x10 0>;
+};
+
 &lbc {
        #address-cells = <2>;
        #size-cells = <1>;
        };
 };
 
+&qportals {
+       #address-cells = <1>;
+       #size-cells = <1>;
+       compatible = "simple-bus";
+
+       qportal0: qman-portal@0 {
+               compatible = "fsl,qman-portal";
+               reg = <0x0 0x4000>, <0x100000 0x1000>;
+               interrupts = <29 2 0 0>;
+               cell-index = <0>;
+       };
+       qportal1: qman-portal@4000 {
+               compatible = "fsl,qman-portal";
+               reg = <0x4000 0x4000>, <0x101000 0x1000>;
+               interrupts = <31 2 0 0>;
+               cell-index = <1>;
+       };
+       qportal2: qman-portal@8000 {
+               compatible = "fsl,qman-portal";
+               reg = <0x8000 0x4000>, <0x102000 0x1000>;
+               interrupts = <33 2 0 0>;
+               cell-index = <2>;
+       };
+};
+
 &bportals {
        #address-cells = <1>;
        #size-cells = <1>;
 /include/ "pq3-mpic.dtsi"
 /include/ "pq3-mpic-timer-B.dtsi"
 
+       qman: qman@88000 {
+               compatible = "fsl,qman";
+               reg = <0x88000 0x1000>;
+               interrupts = <16 2 0 0>;
+               fsl,qman-portals = <&qportals>;
+               memory-region = <&qman_fqd &qman_pfdr>;
+       };
+
        bman: bman@8a000 {
                compatible = "fsl,bman";
                reg = <0x8a000 0x1000>;
index f2feacfd9a2536749d2a41f2fa76820554915a12..04ad177b6a12fd609c3789536b923c24532aaea6 100644 (file)
        alloc-ranges = <0 0 0x10 0>;
 };
 
+&qman_fqd {
+       compatible = "fsl,qman-fqd";
+       alloc-ranges = <0 0 0x10 0>;
+};
+
+&qman_pfdr {
+       compatible = "fsl,qman-pfdr";
+       alloc-ranges = <0 0 0x10 0>;
+};
+
 &lbc {
        compatible = "fsl,p2041-elbc", "fsl,elbc", "simple-bus";
        interrupts = <25 2 0 0>;
 
 /include/ "qoriq-bman1-portals.dtsi"
 
+/include/ "qoriq-qman1-portals.dtsi"
+
 &soc {
        #address-cells = <1>;
        #size-cells = <1>;
 
 /include/ "qoriq-esdhc-0.dtsi"
        sdhc@114000 {
+               compatible = "fsl,p2041-esdhc", "fsl,esdhc";
                fsl,iommu-parent = <&pamu1>;
                fsl,liodn-reg = <&guts 0x530>; /* eSDHCLIODNR */
                sdhci,auto-cmd12;
@@ -415,5 +428,6 @@ crypto: crypto@300000 {
                fsl,iommu-parent = <&pamu1>;
        };
 
+/include/ "qoriq-qman1.dtsi"
 /include/ "qoriq-bman1.dtsi"
 };
index d6fea37395ad60185a34e7297791cd7ea8af3a24..2cab18af6df2a24ed4ae523a080ee9e36dce8cc1 100644 (file)
        alloc-ranges = <0 0 0x10 0>;
 };
 
+&qman_fqd {
+       compatible = "fsl,qman-fqd";
+       alloc-ranges = <0 0 0x10 0>;
+};
+
+&qman_pfdr {
+       compatible = "fsl,qman-pfdr";
+       alloc-ranges = <0 0 0x10 0>;
+};
+
 &lbc {
        compatible = "fsl,p3041-elbc", "fsl,elbc", "simple-bus";
        interrupts = <25 2 0 0>;
 
 /include/ "qoriq-bman1-portals.dtsi"
 
+/include/ "qoriq-qman1-portals.dtsi"
+
 &soc {
        #address-cells = <1>;
        #size-cells = <1>;
 
 /include/ "qoriq-esdhc-0.dtsi"
        sdhc@114000 {
+               compatible = "fsl,p3041-esdhc", "fsl,esdhc";
                fsl,iommu-parent = <&pamu1>;
                fsl,liodn-reg = <&guts 0x530>; /* eSDHCLIODNR */
                sdhci,auto-cmd12;
@@ -442,5 +455,6 @@ crypto: crypto@300000 {
                fsl,iommu-parent = <&pamu1>;
        };
 
+/include/ "qoriq-qman1.dtsi"
 /include/ "qoriq-bman1.dtsi"
 };
index 89482c9b2301b5fdd0ffcfc4aa0c08efd2f5a29a..dfc76bc41cb26cd2e0a2ecc68589fefaea579208 100644 (file)
        alloc-ranges = <0 0 0x10 0>;
 };
 
+&qman_fqd {
+       compatible = "fsl,qman-fqd";
+       alloc-ranges = <0 0 0x10 0>;
+};
+
+&qman_pfdr {
+       compatible = "fsl,qman-pfdr";
+       alloc-ranges = <0 0 0x10 0>;
+};
+
 &lbc {
        compatible = "fsl,p4080-elbc", "fsl,elbc", "simple-bus";
        interrupts = <25 2 0 0>;
 
 /include/ "qoriq-bman1-portals.dtsi"
 
+/include/ "qoriq-qman1-portals.dtsi"
+
 &soc {
        #address-cells = <1>;
        #size-cells = <1>;
 
 /include/ "qoriq-esdhc-0.dtsi"
        sdhc@114000 {
+               compatible = "fsl,p4080-esdhc", "fsl,esdhc";
                fsl,iommu-parent = <&pamu1>;
                fsl,liodn-reg = <&guts 0x530>; /* eSDHCLIODNR */
                voltage-ranges = <3300 3300>;
@@ -498,5 +511,6 @@ crypto: crypto@300000 {
                fsl,iommu-parent = <&pamu1>;
        };
 
+/include/ "qoriq-qman1.dtsi"
 /include/ "qoriq-bman1.dtsi"
 };
index 6e04851e2fc936a0c44b8e0f4d9b223ad139eefb..b77923ad72cf5682177b721ae75fe85cfa04152d 100644 (file)
        alloc-ranges = <0 0 0x10000 0>;
 };
 
+&qman_fqd {
+       compatible = "fsl,qman-fqd";
+       alloc-ranges = <0 0 0x10000 0>;
+};
+
+&qman_pfdr {
+       compatible = "fsl,qman-pfdr";
+       alloc-ranges = <0 0 0x10000 0>;
+};
+
 &lbc {
        compatible = "fsl,p5020-elbc", "fsl,elbc", "simple-bus";
        interrupts = <25 2 0 0>;
 
 /include/ "qoriq-bman1-portals.dtsi"
 
+/include/ "qoriq-qman1-portals.dtsi"
+
 &soc {
        #address-cells = <1>;
        #size-cells = <1>;
 
 /include/ "qoriq-esdhc-0.dtsi"
        sdhc@114000 {
+               compatible = "fsl,p5020-esdhc", "fsl,esdhc";
                fsl,iommu-parent = <&pamu1>;
                fsl,liodn-reg = <&guts 0x530>; /* eSDHCLIODNR */
                sdhci,auto-cmd12;
                fsl,iommu-parent = <&pamu1>;
        };
 
+/include/ "qoriq-qman1.dtsi"
 /include/ "qoriq-bman1.dtsi"
 
 /include/ "qoriq-raid1.0-0.dtsi"
index 5e44dfa1e1a58fef4e895784c3ebc7a025673f8a..6d214526b81ba56d7e2f6f24b8ffa520e519b281 100644 (file)
        alloc-ranges = <0 0 0x10000 0>;
 };
 
+&qman_fqd {
+       compatible = "fsl,qman-fqd";
+       alloc-ranges = <0 0 0x10000 0>;
+};
+
+&qman_pfdr {
+       compatible = "fsl,qman-pfdr";
+       alloc-ranges = <0 0 0x10000 0>;
+};
+
 &lbc {
        compatible = "fsl,p5040-elbc", "fsl,elbc", "simple-bus";
        interrupts = <25 2 0 0>;
 
 /include/ "qoriq-bman1-portals.dtsi"
 
+/include/ "qoriq-qman1-portals.dtsi"
+
 &soc {
        #address-cells = <1>;
        #size-cells = <1>;
 
 /include/ "qoriq-esdhc-0.dtsi"
        sdhc@114000 {
+               compatible = "fsl,p5040-esdhc", "fsl,esdhc";
                fsl,iommu-parent = <&pamu2>;
                fsl,liodn-reg = <&guts 0x530>; /* eSDHCLIODNR */
                sdhci,auto-cmd12;
                fsl,iommu-parent = <&pamu4>;
        };
 
+/include/ "qoriq-qman1.dtsi"
 /include/ "qoriq-bman1.dtsi"
 };
index 05d51acafa67a960caa975e997ebaad9d9b3ac0a..e77e4b4ed53b73911fd41a24012f313798c86307 100644 (file)
                compatible = "fsl,qman-portal";
                reg = <0x0 0x4000>, <0x100000 0x1000>;
                interrupts = <104 2 0 0>;
-               fsl,qman-channel-id = <0x0>;
+               cell-index = <0x0>;
        };
        qportal1: qman-portal@4000 {
                compatible = "fsl,qman-portal";
                reg = <0x4000 0x4000>, <0x101000 0x1000>;
                interrupts = <106 2 0 0>;
-               fsl,qman-channel-id = <1>;
+               cell-index = <1>;
        };
        qportal2: qman-portal@8000 {
                compatible = "fsl,qman-portal";
                reg = <0x8000 0x4000>, <0x102000 0x1000>;
                interrupts = <108 2 0 0>;
-               fsl,qman-channel-id = <2>;
+               cell-index = <2>;
        };
        qportal3: qman-portal@c000 {
                compatible = "fsl,qman-portal";
                reg = <0xc000 0x4000>, <0x103000 0x1000>;
                interrupts = <110 2 0 0>;
-               fsl,qman-channel-id = <3>;
+               cell-index = <3>;
        };
        qportal4: qman-portal@10000 {
                compatible = "fsl,qman-portal";
                reg = <0x10000 0x4000>, <0x104000 0x1000>;
                interrupts = <112 2 0 0>;
-               fsl,qman-channel-id = <4>;
+               cell-index = <4>;
        };
        qportal5: qman-portal@14000 {
                compatible = "fsl,qman-portal";
                reg = <0x14000 0x4000>, <0x105000 0x1000>;
                interrupts = <114 2 0 0>;
-               fsl,qman-channel-id = <5>;
+               cell-index = <5>;
        };
        qportal6: qman-portal@18000 {
                compatible = "fsl,qman-portal";
                reg = <0x18000 0x4000>, <0x106000 0x1000>;
                interrupts = <116 2 0 0>;
-               fsl,qman-channel-id = <6>;
+               cell-index = <6>;
        };
 
        qportal7: qman-portal@1c000 {
                compatible = "fsl,qman-portal";
                reg = <0x1c000 0x4000>, <0x107000 0x1000>;
                interrupts = <118 2 0 0>;
-               fsl,qman-channel-id = <7>;
+               cell-index = <7>;
        };
        qportal8: qman-portal@20000 {
                compatible = "fsl,qman-portal";
                reg = <0x20000 0x4000>, <0x108000 0x1000>;
                interrupts = <120 2 0 0>;
-               fsl,qman-channel-id = <8>;
+               cell-index = <8>;
        };
        qportal9: qman-portal@24000 {
                compatible = "fsl,qman-portal";
                reg = <0x24000 0x4000>, <0x109000 0x1000>;
                interrupts = <122 2 0 0>;
-               fsl,qman-channel-id = <9>;
+               cell-index = <9>;
        };
 };
diff --git a/arch/powerpc/boot/dts/fsl/t1023si-post.dtsi b/arch/powerpc/boot/dts/fsl/t1023si-post.dtsi
new file mode 100644 (file)
index 0000000..df1f068
--- /dev/null
@@ -0,0 +1,330 @@
+/*
+ * T1023 Silicon/SoC Device Tree Source (post include)
+ *
+ * Copyright 2014 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.
+ */
+
+&ifc {
+       #address-cells = <2>;
+       #size-cells = <1>;
+       compatible = "fsl,ifc", "simple-bus";
+       interrupts = <25 2 0 0>;
+};
+
+&pci0 {
+       compatible = "fsl,t1023-pcie", "fsl,qoriq-pcie-v2.4", "fsl,qoriq-pcie";
+       device_type = "pci";
+       #size-cells = <2>;
+       #address-cells = <3>;
+       bus-range = <0x0 0xff>;
+       interrupts = <20 2 0 0>;
+       fsl,iommu-parent = <&pamu0>;
+       pcie@0 {
+               reg = <0 0 0 0 0>;
+               #interrupt-cells = <1>;
+               #size-cells = <2>;
+               #address-cells = <3>;
+               device_type = "pci";
+               interrupts = <20 2 0 0>;
+               interrupt-map-mask = <0xf800 0 0 7>;
+               interrupt-map = <
+                       /* IDSEL 0x0 */
+                       0000 0 0 1 &mpic 40 1 0 0
+                       0000 0 0 2 &mpic 1 1 0 0
+                       0000 0 0 3 &mpic 2 1 0 0
+                       0000 0 0 4 &mpic 3 1 0 0
+                       >;
+       };
+};
+
+&pci1 {
+       compatible = "fsl,t1023-pcie", "fsl,qoriq-pcie-v2.4", "fsl,qoriq-pcie";
+       device_type = "pci";
+       #size-cells = <2>;
+       #address-cells = <3>;
+       bus-range = <0 0xff>;
+       interrupts = <21 2 0 0>;
+       fsl,iommu-parent = <&pamu0>;
+       pcie@0 {
+               reg = <0 0 0 0 0>;
+               #interrupt-cells = <1>;
+               #size-cells = <2>;
+               #address-cells = <3>;
+               device_type = "pci";
+               interrupts = <21 2 0 0>;
+               interrupt-map-mask = <0xf800 0 0 7>;
+               interrupt-map = <
+                       /* IDSEL 0x0 */
+                       0000 0 0 1 &mpic 41 1 0 0
+                       0000 0 0 2 &mpic 5 1 0 0
+                       0000 0 0 3 &mpic 6 1 0 0
+                       0000 0 0 4 &mpic 7 1 0 0
+                       >;
+       };
+};
+
+&pci2 {
+       compatible = "fsl,t1023-pcie", "fsl,qoriq-pcie-v2.4", "fsl,qoriq-pcie";
+       device_type = "pci";
+       #size-cells = <2>;
+       #address-cells = <3>;
+       bus-range = <0x0 0xff>;
+       interrupts = <22 2 0 0>;
+       fsl,iommu-parent = <&pamu0>;
+       pcie@0 {
+               reg = <0 0 0 0 0>;
+               #interrupt-cells = <1>;
+               #size-cells = <2>;
+               #address-cells = <3>;
+               device_type = "pci";
+               interrupts = <22 2 0 0>;
+               interrupt-map-mask = <0xf800 0 0 7>;
+               interrupt-map = <
+                       /* IDSEL 0x0 */
+                       0000 0 0 1 &mpic 42 1 0 0
+                       0000 0 0 2 &mpic 9 1 0 0
+                       0000 0 0 3 &mpic 10 1 0 0
+                       0000 0 0 4 &mpic 11 1 0 0
+                       >;
+       };
+};
+
+&dcsr {
+       #address-cells = <1>;
+       #size-cells = <1>;
+       compatible = "fsl,dcsr", "simple-bus";
+
+       dcsr-epu@0 {
+               compatible = "fsl,t1023-dcsr-epu", "fsl,dcsr-epu";
+               interrupts = <52 2 0 0
+                             84 2 0 0
+                             85 2 0 0>;
+               reg = <0x0 0x1000>;
+       };
+       dcsr-npc {
+               compatible = "fsl,t1023-dcsr-cnpc", "fsl,dcsr-cnpc";
+               reg = <0x1000 0x1000 0x1002000 0x10000>;
+       };
+       dcsr-nxc@2000 {
+               compatible = "fsl,dcsr-nxc";
+               reg = <0x2000 0x1000>;
+       };
+       dcsr-corenet {
+               compatible = "fsl,dcsr-corenet";
+               reg = <0x8000 0x1000 0x1A000 0x1000>;
+       };
+       dcsr-ocn@11000 {
+               compatible = "fsl,t1023-dcsr-ocn", "fsl,dcsr-ocn";
+               reg = <0x11000 0x1000>;
+       };
+       dcsr-ddr@12000 {
+               compatible = "fsl,dcsr-ddr";
+               dev-handle = <&ddr1>;
+               reg = <0x12000 0x1000>;
+       };
+       dcsr-nal@18000 {
+               compatible = "fsl,t1023-dcsr-nal", "fsl,dcsr-nal";
+               reg = <0x18000 0x1000>;
+       };
+       dcsr-rcpm@22000 {
+               compatible = "fsl,t1023-dcsr-rcpm", "fsl,dcsr-rcpm";
+               reg = <0x22000 0x1000>;
+       };
+       dcsr-snpc@30000 {
+               compatible = "fsl,t1023-dcsr-snpc", "fsl,dcsr-snpc";
+               reg = <0x30000 0x1000 0x1022000 0x10000>;
+       };
+       dcsr-snpc@31000 {
+               compatible = "fsl,t1023-dcsr-snpc", "fsl,dcsr-snpc";
+               reg = <0x31000 0x1000 0x1042000 0x10000>;
+       };
+       dcsr-cpu-sb-proxy@100000 {
+               compatible = "fsl,dcsr-e5500-sb-proxy", "fsl,dcsr-cpu-sb-proxy";
+               cpu-handle = <&cpu0>;
+               reg = <0x100000 0x1000 0x101000 0x1000>;
+       };
+       dcsr-cpu-sb-proxy@108000 {
+               compatible = "fsl,dcsr-e5500-sb-proxy", "fsl,dcsr-cpu-sb-proxy";
+               cpu-handle = <&cpu1>;
+               reg = <0x108000 0x1000 0x109000 0x1000>;
+       };
+};
+
+&soc {
+       #address-cells = <1>;
+       #size-cells = <1>;
+       device_type = "soc";
+       compatible = "simple-bus";
+
+       soc-sram-error {
+               compatible = "fsl,soc-sram-error";
+               interrupts = <16 2 1 29>;
+       };
+
+       corenet-law@0 {
+               compatible = "fsl,corenet-law";
+               reg = <0x0 0x1000>;
+               fsl,num-laws = <16>;
+       };
+
+       ddr1: memory-controller@8000 {
+               compatible = "fsl,qoriq-memory-controller-v5.0",
+                               "fsl,qoriq-memory-controller";
+               reg = <0x8000 0x1000>;
+               interrupts = <16 2 1 23>;
+       };
+
+       cpc: l3-cache-controller@10000 {
+               compatible = "fsl,t1023-l3-cache-controller", "cache";
+               reg = <0x10000 0x1000>;
+               interrupts = <16 2 1 27>;
+       };
+
+       corenet-cf@18000 {
+               compatible = "fsl,corenet2-cf";
+               reg = <0x18000 0x1000>;
+               interrupts = <16 2 1 31>;
+       };
+
+       iommu@20000 {
+               compatible = "fsl,pamu-v1.0", "fsl,pamu";
+               reg = <0x20000 0x1000>;
+               ranges = <0 0x20000 0x1000>;
+               #address-cells = <1>;
+               #size-cells = <1>;
+               interrupts = <
+                       24 2 0 0
+                       16 2 1 30>;
+               pamu0: pamu@0 {
+                       reg = <0 0x1000>;
+                       fsl,primary-cache-geometry = <128 1>;
+                       fsl,secondary-cache-geometry = <32 2>;
+               };
+       };
+
+/include/ "qoriq-mpic.dtsi"
+
+       guts: global-utilities@e0000 {
+               compatible = "fsl,t1023-device-config", "fsl,qoriq-device-config-2.0";
+               reg = <0xe0000 0xe00>;
+               fsl,has-rstcr;
+               fsl,liodn-bits = <12>;
+       };
+
+/include/ "qoriq-clockgen2.dtsi"
+       global-utilities@e1000 {
+               compatible = "fsl,t1023-clockgen", "fsl,qoriq-clockgen-2.0";
+               mux0: mux0@0 {
+                       #clock-cells = <0>;
+                       reg = <0x0 4>;
+                       compatible = "fsl,core-mux-clock";
+                       clocks = <&pll0 0>, <&pll0 1>;
+                       clock-names = "pll0_0", "pll0_1";
+                       clock-output-names = "cmux0";
+               };
+               mux1: mux1@20 {
+                       #clock-cells = <0>;
+                       reg = <0x20 4>;
+                       compatible = "fsl,core-mux-clock";
+                       clocks = <&pll0 0>, <&pll0 1>;
+                       clock-names = "pll0_0", "pll0_1";
+                       clock-output-names = "cmux1";
+               };
+       };
+
+       rcpm: global-utilities@e2000 {
+               compatible = "fsl,t1023-rcpm", "fsl,qoriq-rcpm-2.0";
+               reg = <0xe2000 0x1000>;
+       };
+
+       sfp: sfp@e8000 {
+               compatible = "fsl,t1023-sfp";
+               reg = <0xe8000 0x1000>;
+       };
+
+       serdes: serdes@ea000 {
+               compatible = "fsl,t1023-serdes";
+               reg = <0xea000 0x4000>;
+       };
+
+       scfg: global-utilities@fc000 {
+               compatible = "fsl,t1023-scfg";
+               reg = <0xfc000 0x1000>;
+       };
+
+/include/ "elo3-dma-0.dtsi"
+/include/ "elo3-dma-1.dtsi"
+
+/include/ "qoriq-espi-0.dtsi"
+       spi@110000 {
+               fsl,espi-num-chipselects = <4>;
+       };
+
+/include/ "qoriq-esdhc-0.dtsi"
+       sdhc@114000 {
+               compatible = "fsl,t1023-esdhc", "fsl,esdhc";
+               fsl,iommu-parent = <&pamu0>;
+               fsl,liodn-reg = <&guts 0x530>; /* eSDHCLIODNR */
+               sdhci,auto-cmd12;
+               no-1-8-v;
+       };
+/include/ "qoriq-i2c-0.dtsi"
+/include/ "qoriq-i2c-1.dtsi"
+/include/ "qoriq-duart-0.dtsi"
+/include/ "qoriq-duart-1.dtsi"
+/include/ "qoriq-gpio-0.dtsi"
+/include/ "qoriq-gpio-1.dtsi"
+/include/ "qoriq-gpio-2.dtsi"
+/include/ "qoriq-gpio-3.dtsi"
+/include/ "qoriq-usb2-mph-0.dtsi"
+       usb0: usb@210000 {
+               compatible = "fsl-usb2-mph-v2.5", "fsl-usb2-mph";
+               fsl,iommu-parent = <&pamu0>;
+               fsl,liodn-reg = <&guts 0x520>; /* USB1LIODNR */
+               phy_type = "utmi";
+               port0;
+       };
+/include/ "qoriq-usb2-dr-0.dtsi"
+       usb1: usb@211000 {
+               compatible = "fsl-usb2-dr-v2.5", "fsl-usb2-dr";
+               fsl,iommu-parent = <&pamu0>;
+               fsl,liodn-reg = <&guts 0x524>; /* USB2LIODNR */
+               dr_mode = "host";
+               phy_type = "utmi";
+       };
+/include/ "qoriq-sata2-0.dtsi"
+       sata@220000 {
+               fsl,iommu-parent = <&pamu0>;
+               fsl,liodn-reg = <&guts 0x550>; /* SATA1LIODNR */
+       };
+
+/include/ "qoriq-sec5.0-0.dtsi"
+};
diff --git a/arch/powerpc/boot/dts/fsl/t1024si-post.dtsi b/arch/powerpc/boot/dts/fsl/t1024si-post.dtsi
new file mode 100644 (file)
index 0000000..95e3af8
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * T1024 Silicon/SoC Device Tree Source (post include)
+ *
+ * Copyright 2014 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/ "t1023si-post.dtsi"
+
+/ {
+       aliases {
+               vga = &display;
+               display = &display;
+       };
+
+       qe:qe@ffe140000 {
+               #address-cells = <1>;
+               #size-cells = <1>;
+               device_type = "qe";
+               compatible = "fsl,qe";
+               ranges = <0x0 0xf 0xfe140000 0x40000>;
+               reg = <0xf 0xfe140000 0 0x480>;
+               fsl,qe-num-riscs = <1>;
+               fsl,qe-num-snums = <28>;
+               brg-frequency = <0>;
+               bus-frequency = <0>;
+       };
+};
+
+&soc {
+       display:display@180000 {
+               compatible = "fsl,t1024-diu", "fsl,diu";
+               reg = <0x180000 1000>;
+               interrupts = <74 2 0 0>;
+       };
+};
+
+&qe {
+       qeic: interrupt-controller@80 {
+               interrupt-controller;
+               compatible = "fsl,qe-ic";
+               #address-cells = <0>;
+               #interrupt-cells = <1>;
+               reg = <0x80 0x80>;
+               interrupts = <95 2 0 0  94 2 0 0>; //high:79 low:78
+       };
+
+       ucc@2000 {
+               cell-index = <1>;
+               reg = <0x2000 0x200>;
+               interrupts = <32>;
+               interrupt-parent = <&qeic>;
+       };
+
+       ucc@2200 {
+               cell-index = <3>;
+               reg = <0x2200 0x200>;
+               interrupts = <34>;
+               interrupt-parent = <&qeic>;
+       };
+
+       muram@10000 {
+               #address-cells = <1>;
+               #size-cells = <1>;
+               compatible = "fsl,qe-muram", "fsl,cpm-muram";
+               ranges = <0x0 0x10000 0x6000>;
+
+               data-only@0 {
+                       compatible = "fsl,qe-muram-data", "fsl,cpm-muram-data";
+                       reg = <0x0 0x6000>;
+               };
+       };
+};
diff --git a/arch/powerpc/boot/dts/fsl/t102xsi-pre.dtsi b/arch/powerpc/boot/dts/fsl/t102xsi-pre.dtsi
new file mode 100644 (file)
index 0000000..1f1a9f8
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * T1024/T1023 Silicon/SoC Device Tree Source (pre include)
+ *
+ * Copyright 2014 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.
+ */
+
+/dts-v1/;
+
+/include/ "e5500_power_isa.dtsi"
+
+/ {
+       #address-cells = <2>;
+       #size-cells = <2>;
+       interrupt-parent = <&mpic>;
+
+       aliases {
+               ccsr = &soc;
+               dcsr = &dcsr;
+
+               dma0 = &dma0;
+               dma1 = &dma1;
+               serial0 = &serial0;
+               serial1 = &serial1;
+               serial2 = &serial2;
+               serial3 = &serial3;
+               pci0 = &pci0;
+               pci1 = &pci1;
+               pci2 = &pci2;
+               usb0 = &usb0;
+               usb1 = &usb1;
+               sdhc = &sdhc;
+
+               crypto = &crypto;
+       };
+
+       cpus {
+               #address-cells = <1>;
+               #size-cells = <0>;
+
+               cpu0: PowerPC,e5500@0 {
+                       device_type = "cpu";
+                       reg = <0>;
+                       clocks = <&mux0>;
+                       next-level-cache = <&L2_1>;
+                       L2_1: l2-cache {
+                               next-level-cache = <&cpc>;
+                       };
+               };
+               cpu1: PowerPC,e5500@1 {
+                       device_type = "cpu";
+                       reg = <1>;
+                       clocks = <&mux1>;
+                       next-level-cache = <&L2_2>;
+                       L2_2: l2-cache {
+                               next-level-cache = <&cpc>;
+                       };
+               };
+       };
+};
index 5cc01be5b1527cadb8b6a385b893fe7a5cfd1223..9e9f7e201d43a2234a8483f36f12b7e8ac0764fd 100644 (file)
        alloc-ranges = <0 0 0x10000 0>;
 };
 
+&qman_fqd {
+       compatible = "fsl,qman-fqd";
+       alloc-ranges = <0 0 0x10000 0>;
+};
+
+&qman_pfdr {
+       compatible = "fsl,qman-pfdr";
+       alloc-ranges = <0 0 0x10000 0>;
+};
+
 &ifc {
        #address-cells = <2>;
        #size-cells = <1>;
        };
 };
 
+&qportals {
+       #address-cells = <0x1>;
+       #size-cells = <0x1>;
+       compatible = "simple-bus";
+
+       qportal0: qman-portal@0 {
+               compatible = "fsl,qman-portal";
+               reg = <0x0 0x4000>, <0x1000000 0x1000>;
+               interrupts = <104 0x2 0 0>;
+               cell-index = <0x0>;
+       };
+       qportal1: qman-portal@4000 {
+               compatible = "fsl,qman-portal";
+               reg = <0x4000 0x4000>, <0x1001000 0x1000>;
+               interrupts = <106 0x2 0 0>;
+               cell-index = <0x1>;
+       };
+       qportal2: qman-portal@8000 {
+               compatible = "fsl,qman-portal";
+               reg = <0x8000 0x4000>, <0x1002000 0x1000>;
+               interrupts = <108 0x2 0 0>;
+               cell-index = <0x2>;
+       };
+       qportal3: qman-portal@c000 {
+               compatible = "fsl,qman-portal";
+               reg = <0xc000 0x4000>, <0x1003000 0x1000>;
+               interrupts = <110 0x2 0 0>;
+               cell-index = <0x3>;
+       };
+       qportal4: qman-portal@10000 {
+               compatible = "fsl,qman-portal";
+               reg = <0x10000 0x4000>, <0x1004000 0x1000>;
+               interrupts = <112 0x2 0 0>;
+               cell-index = <0x4>;
+       };
+       qportal5: qman-portal@14000 {
+               compatible = "fsl,qman-portal";
+               reg = <0x14000 0x4000>, <0x1005000 0x1000>;
+               interrupts = <114 0x2 0 0>;
+               cell-index = <0x5>;
+       };
+       qportal6: qman-portal@18000 {
+               compatible = "fsl,qman-portal";
+               reg = <0x18000 0x4000>, <0x1006000 0x1000>;
+               interrupts = <116 0x2 0 0>;
+               cell-index = <0x6>;
+       };
+       qportal7: qman-portal@1c000 {
+               compatible = "fsl,qman-portal";
+               reg = <0x1c000 0x4000>, <0x1007000 0x1000>;
+               interrupts = <118 0x2 0 0>;
+               cell-index = <0x7>;
+       };
+       qportal8: qman-portal@20000 {
+               compatible = "fsl,qman-portal";
+               reg = <0x20000 0x4000>, <0x1008000 0x1000>;
+               interrupts = <120 0x2 0 0>;
+               cell-index = <0x8>;
+       };
+       qportal9: qman-portal@24000 {
+               compatible = "fsl,qman-portal";
+               reg = <0x24000 0x4000>, <0x1009000 0x1000>;
+               interrupts = <122 0x2 0 0>;
+               cell-index = <0x9>;
+       };
+};
+
 &soc {
        #address-cells = <1>;
        #size-cells = <1>;
                fsl,liodn-reg = <&guts 0x554>; /* SATA2LIODNR */
        };
 /include/ "qoriq-sec5.0-0.dtsi"
+/include/ "qoriq-qman3.dtsi"
 /include/ "qoriq-bman1.dtsi"
 };
index 86bdaf6cbd141c0309524166e97e874e4e69647d..32c790ae7fde70f1762f72df2c0c5ad136919522 100644 (file)
        alloc-ranges = <0 0 0x10000 0>;
 };
 
+&qman_fqd {
+       compatible = "fsl,qman-fqd";
+       alloc-ranges = <0 0 0x10000 0>;
+};
+
+&qman_pfdr {
+       compatible = "fsl,qman-pfdr";
+       alloc-ranges = <0 0 0x10000 0>;
+};
+
 &ifc {
        #address-cells = <2>;
        #size-cells = <1>;
        };
 };
 
+&qportals {
+       #address-cells = <0x1>;
+       #size-cells = <0x1>;
+       compatible = "simple-bus";
+
+       qportal0: qman-portal@0 {
+               compatible = "fsl,qman-portal";
+               reg = <0x0 0x4000>, <0x1000000 0x1000>;
+               interrupts = <104 0x2 0 0>;
+               cell-index = <0x0>;
+       };
+       qportal1: qman-portal@4000 {
+               compatible = "fsl,qman-portal";
+               reg = <0x4000 0x4000>, <0x1001000 0x1000>;
+               interrupts = <106 0x2 0 0>;
+               cell-index = <0x1>;
+       };
+       qportal2: qman-portal@8000 {
+               compatible = "fsl,qman-portal";
+               reg = <0x8000 0x4000>, <0x1002000 0x1000>;
+               interrupts = <108 0x2 0 0>;
+               cell-index = <0x2>;
+       };
+       qportal3: qman-portal@c000 {
+               compatible = "fsl,qman-portal";
+               reg = <0xc000 0x4000>, <0x1003000 0x1000>;
+               interrupts = <110 0x2 0 0>;
+               cell-index = <0x3>;
+       };
+       qportal4: qman-portal@10000 {
+               compatible = "fsl,qman-portal";
+               reg = <0x10000 0x4000>, <0x1004000 0x1000>;
+               interrupts = <112 0x2 0 0>;
+               cell-index = <0x4>;
+       };
+       qportal5: qman-portal@14000 {
+               compatible = "fsl,qman-portal";
+               reg = <0x14000 0x4000>, <0x1005000 0x1000>;
+               interrupts = <114 0x2 0 0>;
+               cell-index = <0x5>;
+       };
+       qportal6: qman-portal@18000 {
+               compatible = "fsl,qman-portal";
+               reg = <0x18000 0x4000>, <0x1006000 0x1000>;
+               interrupts = <116 0x2 0 0>;
+               cell-index = <0x6>;
+       };
+       qportal7: qman-portal@1c000 {
+               compatible = "fsl,qman-portal";
+               reg = <0x1c000 0x4000>, <0x1007000 0x1000>;
+               interrupts = <118 0x2 0 0>;
+               cell-index = <0x7>;
+       };
+       qportal8: qman-portal@20000 {
+               compatible = "fsl,qman-portal";
+               reg = <0x20000 0x4000>, <0x1008000 0x1000>;
+               interrupts = <120 0x2 0 0>;
+               cell-index = <0x8>;
+       };
+       qportal9: qman-portal@24000 {
+               compatible = "fsl,qman-portal";
+               reg = <0x24000 0x4000>, <0x1009000 0x1000>;
+               interrupts = <122 0x2 0 0>;
+               cell-index = <0x9>;
+       };
+       qportal10: qman-portal@28000 {
+               compatible = "fsl,qman-portal";
+               reg = <0x28000 0x4000>, <0x100a000 0x1000>;
+               interrupts = <124 0x2 0 0>;
+               cell-index = <0xa>;
+       };
+       qportal11: qman-portal@2c000 {
+               compatible = "fsl,qman-portal";
+               reg = <0x2c000 0x4000>, <0x100b000 0x1000>;
+               interrupts = <126 0x2 0 0>;
+               cell-index = <0xb>;
+       };
+       qportal12: qman-portal@30000 {
+               compatible = "fsl,qman-portal";
+               reg = <0x30000 0x4000>, <0x100c000 0x1000>;
+               interrupts = <128 0x2 0 0>;
+               cell-index = <0xc>;
+       };
+       qportal13: qman-portal@34000 {
+               compatible = "fsl,qman-portal";
+               reg = <0x34000 0x4000>, <0x100d000 0x1000>;
+               interrupts = <130 0x2 0 0>;
+               cell-index = <0xd>;
+       };
+       qportal14: qman-portal@38000 {
+               compatible = "fsl,qman-portal";
+               reg = <0x38000 0x4000>, <0x100e000 0x1000>;
+               interrupts = <132 0x2 0 0>;
+               cell-index = <0xe>;
+       };
+       qportal15: qman-portal@3c000 {
+               compatible = "fsl,qman-portal";
+               reg = <0x3c000 0x4000>, <0x100f000 0x1000>;
+               interrupts = <134 0x2 0 0>;
+               cell-index = <0xf>;
+       };
+       qportal16: qman-portal@40000 {
+               compatible = "fsl,qman-portal";
+               reg = <0x40000 0x4000>, <0x1010000 0x1000>;
+               interrupts = <136 0x2 0 0>;
+               cell-index = <0x10>;
+       };
+       qportal17: qman-portal@44000 {
+               compatible = "fsl,qman-portal";
+               reg = <0x44000 0x4000>, <0x1011000 0x1000>;
+               interrupts = <138 0x2 0 0>;
+               cell-index = <0x11>;
+       };
+};
+
 &soc {
        #address-cells = <1>;
        #size-cells = <1>;
                        compatible = "fsl,qoriq-core-mux-2.0";
                        clocks = <&pll0 0>, <&pll0 1>, <&pll0 2>,
                                 <&pll1 0>, <&pll1 1>, <&pll1 2>;
-                       clock-names = "pll0", "pll0-div2", "pll1-div4",
+                       clock-names = "pll0", "pll0-div2", "pll0-div4",
                                "pll1", "pll1-div2", "pll1-div4";
                        clock-output-names = "cmux0";
                };
                        compatible = "fsl,qoriq-core-mux-2.0";
                        clocks = <&pll0 0>, <&pll0 1>, <&pll0 2>,
                                 <&pll1 0>, <&pll1 1>, <&pll1 2>;
-                       clock-names = "pll0", "pll0-div2", "pll1-div4",
+                       clock-names = "pll0", "pll0-div2", "pll0-div4",
                                "pll1", "pll1-div2", "pll1-div4";
                        clock-output-names = "cmux1";
                };
                phy_type = "utmi";
        };
 /include/ "qoriq-sec5.2-0.dtsi"
+/include/ "qoriq-qman3.dtsi"
 /include/ "qoriq-bman1.dtsi"
 
        L2_1: l2-cache-controller@c20000 {
index 4d4f25895d8c4a9f62d471e2189143892d6d7287..d806360d0f64b4907d4790b995f222b2ae355575 100644 (file)
        alloc-ranges = <0 0 0x10000 0>;
 };
 
+&qman_fqd {
+       compatible = "fsl,qman-fqd";
+       alloc-ranges = <0 0 0x10000 0>;
+};
+
+&qman_pfdr {
+       compatible = "fsl,qman-pfdr";
+       alloc-ranges = <0 0 0x10000 0>;
+};
+
 &ifc {
        #address-cells = <2>;
        #size-cells = <1>;
        };
 };
 
+&qportals {
+       #address-cells = <0x1>;
+       #size-cells = <0x1>;
+       compatible = "simple-bus";
+
+       qportal0: qman-portal@0 {
+               compatible = "fsl,qman-portal";
+               reg = <0x0 0x4000>, <0x1000000 0x1000>;
+               interrupts = <104 0x2 0 0>;
+               cell-index = <0x0>;
+       };
+       qportal1: qman-portal@4000 {
+               compatible = "fsl,qman-portal";
+               reg = <0x4000 0x4000>, <0x1001000 0x1000>;
+               interrupts = <106 0x2 0 0>;
+               cell-index = <0x1>;
+       };
+       qportal2: qman-portal@8000 {
+               compatible = "fsl,qman-portal";
+               reg = <0x8000 0x4000>, <0x1002000 0x1000>;
+               interrupts = <108 0x2 0 0>;
+               cell-index = <0x2>;
+       };
+       qportal3: qman-portal@c000 {
+               compatible = "fsl,qman-portal";
+               reg = <0xc000 0x4000>, <0x1003000 0x1000>;
+               interrupts = <110 0x2 0 0>;
+               cell-index = <0x3>;
+       };
+       qportal4: qman-portal@10000 {
+               compatible = "fsl,qman-portal";
+               reg = <0x10000 0x4000>, <0x1004000 0x1000>;
+               interrupts = <112 0x2 0 0>;
+               cell-index = <0x4>;
+       };
+       qportal5: qman-portal@14000 {
+               compatible = "fsl,qman-portal";
+               reg = <0x14000 0x4000>, <0x1005000 0x1000>;
+               interrupts = <114 0x2 0 0>;
+               cell-index = <0x5>;
+       };
+       qportal6: qman-portal@18000 {
+               compatible = "fsl,qman-portal";
+               reg = <0x18000 0x4000>, <0x1006000 0x1000>;
+               interrupts = <116 0x2 0 0>;
+               cell-index = <0x6>;
+       };
+       qportal7: qman-portal@1c000 {
+               compatible = "fsl,qman-portal";
+               reg = <0x1c000 0x4000>, <0x1007000 0x1000>;
+               interrupts = <118 0x2 0 0>;
+               cell-index = <0x7>;
+       };
+       qportal8: qman-portal@20000 {
+               compatible = "fsl,qman-portal";
+               reg = <0x20000 0x4000>, <0x1008000 0x1000>;
+               interrupts = <120 0x2 0 0>;
+               cell-index = <0x8>;
+       };
+       qportal9: qman-portal@24000 {
+               compatible = "fsl,qman-portal";
+               reg = <0x24000 0x4000>, <0x1009000 0x1000>;
+               interrupts = <122 0x2 0 0>;
+               cell-index = <0x9>;
+       };
+       qportal10: qman-portal@28000 {
+               compatible = "fsl,qman-portal";
+               reg = <0x28000 0x4000>, <0x100a000 0x1000>;
+               interrupts = <124 0x2 0 0>;
+               cell-index = <0xa>;
+       };
+       qportal11: qman-portal@2c000 {
+               compatible = "fsl,qman-portal";
+               reg = <0x2c000 0x4000>, <0x100b000 0x1000>;
+               interrupts = <126 0x2 0 0>;
+               cell-index = <0xb>;
+       };
+       qportal12: qman-portal@30000 {
+               compatible = "fsl,qman-portal";
+               reg = <0x30000 0x4000>, <0x100c000 0x1000>;
+               interrupts = <128 0x2 0 0>;
+               cell-index = <0xc>;
+       };
+       qportal13: qman-portal@34000 {
+               compatible = "fsl,qman-portal";
+               reg = <0x34000 0x4000>, <0x100d000 0x1000>;
+               interrupts = <130 0x2 0 0>;
+               cell-index = <0xd>;
+       };
+       qportal14: qman-portal@38000 {
+               compatible = "fsl,qman-portal";
+               reg = <0x38000 0x4000>, <0x100e000 0x1000>;
+               interrupts = <132 0x2 0 0>;
+               cell-index = <0xe>;
+       };
+       qportal15: qman-portal@3c000 {
+               compatible = "fsl,qman-portal";
+               reg = <0x3c000 0x4000>, <0x100f000 0x1000>;
+               interrupts = <134 0x2 0 0>;
+               cell-index = <0xf>;
+       };
+       qportal16: qman-portal@40000 {
+               compatible = "fsl,qman-portal";
+               reg = <0x40000 0x4000>, <0x1010000 0x1000>;
+               interrupts = <136 0x2 0 0>;
+               cell-index = <0x10>;
+       };
+       qportal17: qman-portal@44000 {
+               compatible = "fsl,qman-portal";
+               reg = <0x44000 0x4000>, <0x1011000 0x1000>;
+               interrupts = <138 0x2 0 0>;
+               cell-index = <0x11>;
+       };
+       qportal18: qman-portal@48000 {
+               compatible = "fsl,qman-portal";
+               reg = <0x48000 0x4000>, <0x1012000 0x1000>;
+               interrupts = <140 0x2 0 0>;
+               cell-index = <0x12>;
+       };
+       qportal19: qman-portal@4c000 {
+               compatible = "fsl,qman-portal";
+               reg = <0x4c000 0x4000>, <0x1013000 0x1000>;
+               interrupts = <142 0x2 0 0>;
+               cell-index = <0x13>;
+       };
+       qportal20: qman-portal@50000 {
+               compatible = "fsl,qman-portal";
+               reg = <0x50000 0x4000>, <0x1014000 0x1000>;
+               interrupts = <144 0x2 0 0>;
+               cell-index = <0x14>;
+       };
+       qportal21: qman-portal@54000 {
+               compatible = "fsl,qman-portal";
+               reg = <0x54000 0x4000>, <0x1015000 0x1000>;
+               interrupts = <146 0x2 0 0>;
+               cell-index = <0x15>;
+       };
+       qportal22: qman-portal@58000 {
+               compatible = "fsl,qman-portal";
+               reg = <0x58000 0x4000>, <0x1016000 0x1000>;
+               interrupts = <148 0x2 0 0>;
+               cell-index = <0x16>;
+       };
+       qportal23: qman-portal@5c000 {
+               compatible = "fsl,qman-portal";
+               reg = <0x5c000 0x4000>, <0x1017000 0x1000>;
+               interrupts = <150 0x2 0 0>;
+               cell-index = <0x17>;
+       };
+       qportal24: qman-portal@60000 {
+               compatible = "fsl,qman-portal";
+               reg = <0x60000 0x4000>, <0x1018000 0x1000>;
+               interrupts = <152 0x2 0 0>;
+               cell-index = <0x18>;
+       };
+       qportal25: qman-portal@64000 {
+               compatible = "fsl,qman-portal";
+               reg = <0x64000 0x4000>, <0x1019000 0x1000>;
+               interrupts = <154 0x2 0 0>;
+               cell-index = <0x19>;
+       };
+       qportal26: qman-portal@68000 {
+               compatible = "fsl,qman-portal";
+               reg = <0x68000 0x4000>, <0x101a000 0x1000>;
+               interrupts = <156 0x2 0 0>;
+               cell-index = <0x1a>;
+       };
+       qportal27: qman-portal@6c000 {
+               compatible = "fsl,qman-portal";
+               reg = <0x6c000 0x4000>, <0x101b000 0x1000>;
+               interrupts = <158 0x2 0 0>;
+               cell-index = <0x1b>;
+       };
+       qportal28: qman-portal@70000 {
+               compatible = "fsl,qman-portal";
+               reg = <0x70000 0x4000>, <0x101c000 0x1000>;
+               interrupts = <160 0x2 0 0>;
+               cell-index = <0x1c>;
+       };
+       qportal29: qman-portal@74000 {
+               compatible = "fsl,qman-portal";
+               reg = <0x74000 0x4000>, <0x101d000 0x1000>;
+               interrupts = <162 0x2 0 0>;
+               cell-index = <0x1d>;
+       };
+       qportal30: qman-portal@78000 {
+               compatible = "fsl,qman-portal";
+               reg = <0x78000 0x4000>, <0x101e000 0x1000>;
+               interrupts = <164 0x2 0 0>;
+               cell-index = <0x1e>;
+       };
+       qportal31: qman-portal@7c000 {
+               compatible = "fsl,qman-portal";
+               reg = <0x7c000 0x4000>, <0x101f000 0x1000>;
+               interrupts = <166 0x2 0 0>;
+               cell-index = <0x1f>;
+       };
+       qportal32: qman-portal@80000 {
+               compatible = "fsl,qman-portal";
+               reg = <0x80000 0x4000>, <0x1020000 0x1000>;
+               interrupts = <168 0x2 0 0>;
+               cell-index = <0x20>;
+       };
+       qportal33: qman-portal@84000 {
+               compatible = "fsl,qman-portal";
+               reg = <0x84000 0x4000>, <0x1021000 0x1000>;
+               interrupts = <170 0x2 0 0>;
+               cell-index = <0x21>;
+       };
+       qportal34: qman-portal@88000 {
+               compatible = "fsl,qman-portal";
+               reg = <0x88000 0x4000>, <0x1022000 0x1000>;
+               interrupts = <172 0x2 0 0>;
+               cell-index = <0x22>;
+       };
+       qportal35: qman-portal@8c000 {
+               compatible = "fsl,qman-portal";
+               reg = <0x8c000 0x4000>, <0x1023000 0x1000>;
+               interrupts = <174 0x2 0 0>;
+               cell-index = <0x23>;
+       };
+       qportal36: qman-portal@90000 {
+               compatible = "fsl,qman-portal";
+               reg = <0x90000 0x4000>, <0x1024000 0x1000>;
+               interrupts = <384 0x2 0 0>;
+               cell-index = <0x24>;
+       };
+       qportal37: qman-portal@94000 {
+               compatible = "fsl,qman-portal";
+               reg = <0x94000 0x4000>, <0x1025000 0x1000>;
+               interrupts = <386 0x2 0 0>;
+               cell-index = <0x25>;
+       };
+       qportal38: qman-portal@98000 {
+               compatible = "fsl,qman-portal";
+               reg = <0x98000 0x4000>, <0x1026000 0x1000>;
+               interrupts = <388 0x2 0 0>;
+               cell-index = <0x26>;
+       };
+       qportal39: qman-portal@9c000 {
+               compatible = "fsl,qman-portal";
+               reg = <0x9c000 0x4000>, <0x1027000 0x1000>;
+               interrupts = <390 0x2 0 0>;
+               cell-index = <0x27>;
+       };
+       qportal40: qman-portal@a0000 {
+               compatible = "fsl,qman-portal";
+               reg = <0xa0000 0x4000>, <0x1028000 0x1000>;
+               interrupts = <392 0x2 0 0>;
+               cell-index = <0x28>;
+       };
+       qportal41: qman-portal@a4000 {
+               compatible = "fsl,qman-portal";
+               reg = <0xa4000 0x4000>, <0x1029000 0x1000>;
+               interrupts = <394 0x2 0 0>;
+               cell-index = <0x29>;
+       };
+       qportal42: qman-portal@a8000 {
+               compatible = "fsl,qman-portal";
+               reg = <0xa8000 0x4000>, <0x102a000 0x1000>;
+               interrupts = <396 0x2 0 0>;
+               cell-index = <0x2a>;
+       };
+       qportal43: qman-portal@ac000 {
+               compatible = "fsl,qman-portal";
+               reg = <0xac000 0x4000>, <0x102b000 0x1000>;
+               interrupts = <398 0x2 0 0>;
+               cell-index = <0x2b>;
+       };
+       qportal44: qman-portal@b0000 {
+               compatible = "fsl,qman-portal";
+               reg = <0xb0000 0x4000>, <0x102c000 0x1000>;
+               interrupts = <400 0x2 0 0>;
+               cell-index = <0x2c>;
+       };
+       qportal45: qman-portal@b4000 {
+               compatible = "fsl,qman-portal";
+               reg = <0xb4000 0x4000>, <0x102d000 0x1000>;
+               interrupts = <402 0x2 0 0>;
+               cell-index = <0x2d>;
+       };
+       qportal46: qman-portal@b8000 {
+               compatible = "fsl,qman-portal";
+               reg = <0xb8000 0x4000>, <0x102e000 0x1000>;
+               interrupts = <404 0x2 0 0>;
+               cell-index = <0x2e>;
+       };
+       qportal47: qman-portal@bc000 {
+               compatible = "fsl,qman-portal";
+               reg = <0xbc000 0x4000>, <0x102f000 0x1000>;
+               interrupts = <406 0x2 0 0>;
+               cell-index = <0x2f>;
+       };
+       qportal48: qman-portal@c0000 {
+               compatible = "fsl,qman-portal";
+               reg = <0xc0000 0x4000>, <0x1030000 0x1000>;
+               interrupts = <408 0x2 0 0>;
+               cell-index = <0x30>;
+       };
+       qportal49: qman-portal@c4000 {
+               compatible = "fsl,qman-portal";
+               reg = <0xc4000 0x4000>, <0x1031000 0x1000>;
+               interrupts = <410 0x2 0 0>;
+               cell-index = <0x31>;
+       };
+};
+
 &soc {
        #address-cells = <1>;
        #size-cells = <1>;
 /include/ "qoriq-sata2-0.dtsi"
 /include/ "qoriq-sata2-1.dtsi"
 /include/ "qoriq-sec5.0-0.dtsi"
+/include/ "qoriq-qman3.dtsi"
 /include/ "qoriq-bman1.dtsi"
 
        L2_1: l2-cache-controller@c20000 {
index 97e6d11d1e6ddda49a4342090e8bad936df0045a..48dab6a50437b46bc61648b0e0aa597fd78a4f7e 100644 (file)
                        size = <0 0x1000000>;
                        alignment = <0 0x1000000>;
                };
+               qman_fqd: qman-fqd {
+                       size = <0 0x400000>;
+                       alignment = <0 0x400000>;
+               };
+               qman_pfdr: qman-pfdr {
+                       size = <0 0x2000000>;
+                       alignment = <0 0x2000000>;
+               };
        };
 
        dcsr: dcsr@f00000000 {
                ranges = <0x0 0xf 0xf4000000 0x200000>;
        };
 
+       qportals: qman-portals@ff4200000 {
+               ranges = <0x0 0xf 0xf4200000 0x200000>;
+       };
+
        soc: soc@ffe000000 {
                ranges = <0x00000000 0xf 0xfe000000 0x1000000>;
                reg = <0xf 0xfe000000 0 0x00001000>;
index eb76caae11d9848220c0ca426864576b2c15fee5..42796c5b05619df744b54b2fdc13b36706b97f3a 100644 (file)
                        size = <0 0x1000000>;
                        alignment = <0 0x1000000>;
                };
+               qman_fqd: qman-fqd {
+                       size = <0 0x400000>;
+                       alignment = <0 0x400000>;
+               };
+               qman_pfdr: qman-pfdr {
+                       size = <0 0x2000000>;
+                       alignment = <0 0x2000000>;
+               };
        };
 
        dcsr: dcsr@f00000000 {
                ranges = <0x0 0xf 0xf4000000 0x200000>;
        };
 
+       qportals: qman-portals@ff4200000 {
+               ranges = <0x0 0xf 0xf4200000 0x200000>;
+       };
+
        soc: soc@ffe000000 {
                ranges = <0x00000000 0xf 0xfe000000 0x1000000>;
                reg = <0xf 0xfe000000 0 0x00001000>;
index 9236e3742a2376f6c4ac43095bc0a3446cde84b4..05a00a4d28612ea8dbc139d14cbe8394ee55b92b 100644 (file)
                        size = <0 0x1000000>;
                        alignment = <0 0x1000000>;
                };
+               qman_fqd: qman-fqd {
+                       size = <0 0x400000>;
+                       alignment = <0 0x400000>;
+               };
+               qman_pfdr: qman-pfdr {
+                       size = <0 0x2000000>;
+                       alignment = <0 0x2000000>;
+               };
+       };
+
+       qportals: qman-portals@ff000000 {
+               ranges = <0x0 0xf 0xff000000 0x200000>;
        };
 
        bportals: bman-portals@ff200000 {
index c1e69dc7188ecbebf419332a9615b278fdb1c7d0..d2bb0765bd5a644eb30c2d5721ffccfa398a816c 100644 (file)
                        size = <0 0x1000000>;
                        alignment = <0 0x1000000>;
                };
+               qman_fqd: qman-fqd {
+                       size = <0 0x400000>;
+                       alignment = <0 0x400000>;
+               };
+               qman_pfdr: qman-pfdr {
+                       size = <0 0x2000000>;
+                       alignment = <0 0x2000000>;
+               };
        };
 
        dcsr: dcsr@f00000000 {
                ranges = <0x0 0xf 0xf4000000 0x200000>;
        };
 
+       qportals: qman-portals@ff4200000 {
+               ranges = <0x0 0xf 0xf4200000 0x200000>;
+       };
+
        soc: soc@ffe000000 {
                ranges = <0x00000000 0xf 0xfe000000 0x1000000>;
                reg = <0xf 0xfe000000 0 0x00001000>;
index 2192fe94866d5ac42528f3543826ef5a5bc24fd5..eca6c697cfd78e6132c6abcdbd6b4e40613e895c 100644 (file)
                        size = <0 0x1000000>;
                        alignment = <0 0x1000000>;
                };
+               qman_fqd: qman-fqd {
+                       size = <0 0x400000>;
+                       alignment = <0 0x400000>;
+               };
+               qman_pfdr: qman-pfdr {
+                       size = <0 0x2000000>;
+                       alignment = <0 0x2000000>;
+               };
        };
 
        dcsr: dcsr@f00000000 {
                ranges = <0x0 0xf 0xf4000000 0x200000>;
        };
 
+       qportals: qman-portals@ff4200000 {
+               ranges = <0x0 0xf 0xf4200000 0x200000>;
+       };
+
        soc: soc@ffe000000 {
                ranges = <0x00000000 0xf 0xfe000000 0x1000000>;
                reg = <0xf 0xfe000000 0 0x00001000>;
index fad4416546423374507dfdb59cef032e9a2977a0..4f80c9d02c27f12210c920ba57e1a3af14bcc8f8 100644 (file)
                        size = <0 0x1000000>;
                        alignment = <0 0x1000000>;
                };
+               qman_fqd: qman-fqd {
+                       size = <0 0x400000>;
+                       alignment = <0 0x400000>;
+               };
+               qman_pfdr: qman-pfdr {
+                       size = <0 0x2000000>;
+                       alignment = <0 0x2000000>;
+               };
        };
 
        dcsr: dcsr@f00000000 {
                ranges = <0x0 0xf 0xf4000000 0x200000>;
        };
 
+       qportals: qman-portals@ff4200000 {
+               ranges = <0x0 0xf 0xf4200000 0x200000>;
+       };
+
        soc: soc@ffe000000 {
                ranges = <0x00000000 0xf 0xfe000000 0x1000000>;
                reg = <0xf 0xfe000000 0 0x00001000>;
index 7382636dc560af9a10071a670235adb26cde4344..d0309a8b974997e37fc4e9afb58610453f40af94 100644 (file)
                        size = <0 0x1000000>;
                        alignment = <0 0x1000000>;
                };
+               qman_fqd: qman-fqd {
+                       size = <0 0x400000>;
+                       alignment = <0 0x400000>;
+               };
+               qman_pfdr: qman-pfdr {
+                       size = <0 0x2000000>;
+                       alignment = <0 0x2000000>;
+               };
        };
 
        dcsr: dcsr@f00000000 {
                ranges = <0x0 0xf 0xf4000000 0x200000>;
        };
 
+       qportals: qman-portals@ff4200000 {
+               ranges = <0x0 0xf 0xf4200000 0x200000>;
+       };
+
        soc: soc@ffe000000 {
                ranges = <0x00000000 0xf 0xfe000000 0x1000000>;
                reg = <0xf 0xfe000000 0 0x00001000>;
index 35dabf5b60980614d151f8f461304f4608cb580c..05168236d3ab47d8db82ae6c0c4e4530a2e17f6e 100644 (file)
                        size = <0 0x1000000>;
                        alignment = <0 0x1000000>;
                };
+               qman_fqd: qman-fqd {
+                       size = <0 0x400000>;
+                       alignment = <0 0x400000>;
+               };
+               qman_pfdr: qman-pfdr {
+                       size = <0 0x2000000>;
+                       alignment = <0 0x2000000>;
+               };
        };
 
        dcsr: dcsr@f00000000 {
                ranges = <0x0 0xf 0xf4000000 0x200000>;
        };
 
+       qportals: qman-portals@ff4200000 {
+               ranges = <0x0 0xf 0xf4200000 0x200000>;
+       };
+
        soc: soc@ffe000000 {
                ranges = <0x00000000 0xf 0xfe000000 0x1000000>;
                reg = <0xf 0xfe000000 0 0x00001000>;
diff --git a/arch/powerpc/boot/dts/t1023rdb.dts b/arch/powerpc/boot/dts/t1023rdb.dts
new file mode 100644 (file)
index 0000000..06b090a
--- /dev/null
@@ -0,0 +1,151 @@
+/*
+ * T1023 RDB Device Tree Source
+ *
+ * Copyright 2014 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/ "fsl/t102xsi-pre.dtsi"
+
+/ {
+       model = "fsl,T1023RDB";
+       compatible = "fsl,T1023RDB";
+       #address-cells = <2>;
+       #size-cells = <2>;
+       interrupt-parent = <&mpic>;
+
+       ifc: localbus@ffe124000 {
+               reg = <0xf 0xfe124000 0 0x2000>;
+               ranges = <0 0 0xf 0xe8000000 0x08000000
+                         1 0 0xf 0xff800000 0x00010000>;
+
+               nor@0,0 {
+                       #address-cells = <1>;
+                       #size-cells = <1>;
+                       status = "disabled";
+                       compatible = "cfi-flash";
+                       reg = <0x0 0x0 0x8000000>;
+                       bank-width = <2>;
+                       device-width = <1>;
+               };
+
+               nand@1,0 {
+                       #address-cells = <1>;
+                       #size-cells = <1>;
+                       compatible = "fsl,ifc-nand";
+                       reg = <0x2 0x0 0x10000>;
+               };
+       };
+
+       memory {
+               device_type = "memory";
+       };
+
+       dcsr: dcsr@f00000000 {
+               ranges = <0x00000000 0xf 0x00000000 0x01072000>;
+       };
+
+       soc: soc@ffe000000 {
+               ranges = <0x00000000 0xf 0xfe000000 0x1000000>;
+               reg = <0xf 0xfe000000 0 0x00001000>;
+               spi@110000 {
+                       flash@0 {
+                               #address-cells = <1>;
+                               #size-cells = <1>;
+                               compatible = "spansion,s25fl512s";
+                               reg = <0>;
+                               spi-max-frequency = <10000000>; /* input clk */
+                       };
+               };
+
+               i2c@118000 {
+                       eeprom@50 {
+                               compatible = "st,m24256";
+                               reg = <0x50>;
+                       };
+
+                       rtc@68 {
+                               compatible = "dallas,ds1339";
+                               reg = <0x68>;
+                               interrupts = <0x5 0x1 0 0>;
+                       };
+               };
+
+               i2c@118100 {
+               };
+       };
+
+       pci0: pcie@ffe240000 {
+               reg = <0xf 0xfe240000 0 0x10000>;
+               ranges = <0x02000000 0 0xe0000000 0xc 0x00000000 0 0x10000000
+                         0x01000000 0 0x00000000 0xf 0xf8000000 0 0x00010000>;
+               pcie@0 {
+                       ranges = <0x02000000 0 0xe0000000
+                                 0x02000000 0 0xe0000000
+                                 0 0x10000000
+
+                                 0x01000000 0 0x00000000
+                                 0x01000000 0 0x00000000
+                                 0 0x00010000>;
+               };
+       };
+
+       pci1: pcie@ffe250000 {
+               reg = <0xf 0xfe250000 0 0x10000>;
+               ranges = <0x02000000 0 0xe0000000 0xc 0x10000000 0 0x10000000
+                         0x01000000 0 0x00000000 0xf 0xf8010000 0 0x00010000>;
+               pcie@0 {
+                       ranges = <0x02000000 0 0xe0000000
+                                 0x02000000 0 0xe0000000
+                                 0 0x10000000
+
+                                 0x01000000 0 0x00000000
+                                 0x01000000 0 0x00000000
+                                 0 0x00010000>;
+               };
+       };
+
+       pci2: pcie@ffe260000 {
+               reg = <0xf 0xfe260000 0 0x10000>;
+               ranges = <0x02000000 0 0xe0000000 0xc 0x20000000 0 0x10000000
+                         0x01000000 0 0x00000000 0xf 0xf8020000 0 0x00010000>;
+               pcie@0 {
+                       ranges = <0x02000000 0 0xe0000000
+                                 0x02000000 0 0xe0000000
+                                 0 0x10000000
+
+                                 0x01000000 0 0x00000000
+                                 0x01000000 0 0x00000000
+                                 0 0x00010000>;
+               };
+       };
+};
+
+/include/ "fsl/t1023si-post.dtsi"
diff --git a/arch/powerpc/boot/dts/t1024qds.dts b/arch/powerpc/boot/dts/t1024qds.dts
new file mode 100644 (file)
index 0000000..f31fabb
--- /dev/null
@@ -0,0 +1,251 @@
+/*
+ * T1024 QDS Device Tree Source
+ *
+ * Copyright 2014 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/ "fsl/t102xsi-pre.dtsi"
+
+/ {
+       model = "fsl,T1024QDS";
+       compatible = "fsl,T1024QDS";
+       #address-cells = <2>;
+       #size-cells = <2>;
+       interrupt-parent = <&mpic>;
+
+       ifc: localbus@ffe124000 {
+               reg = <0xf 0xfe124000 0 0x2000>;
+               ranges = <0 0 0xf 0xe8000000 0x08000000
+                         2 0 0xf 0xff800000 0x00010000
+                         3 0 0xf 0xffdf0000 0x00008000>;
+
+               nor@0,0 {
+                       #address-cells = <1>;
+                       #size-cells = <1>;
+                       compatible = "cfi-flash";
+                       reg = <0x0 0x0 0x8000000>;
+                       bank-width = <2>;
+                       device-width = <1>;
+               };
+
+               nand@2,0 {
+                       #address-cells = <1>;
+                       #size-cells = <1>;
+                       compatible = "fsl,ifc-nand";
+                       reg = <0x2 0x0 0x10000>;
+               };
+
+               board-control@3,0 {
+                       #address-cells = <1>;
+                       #size-cells = <1>;
+                       compatible = "fsl,tetra-fpga", "fsl,fpga-qixis";
+                       reg = <3 0 0x300>;
+                       ranges = <0 3 0 0x300>;
+               };
+       };
+
+       memory {
+               device_type = "memory";
+       };
+
+       dcsr: dcsr@f00000000 {
+               ranges = <0x00000000 0xf 0x00000000 0x01072000>;
+       };
+
+       soc: soc@ffe000000 {
+               ranges = <0x00000000 0xf 0xfe000000 0x1000000>;
+               reg = <0xf 0xfe000000 0 0x00001000>;
+               spi@110000 {
+                       flash@0 {
+                               #address-cells = <1>;
+                               #size-cells = <1>;
+                               compatible = "micron,n25q128a11";  /* 16MB */
+                               reg = <0>;
+                               spi-max-frequency = <10000000>;
+                       };
+
+                       flash@1 {
+                               #address-cells = <1>;
+                               #size-cells = <1>;
+                               compatible = "sst,sst25wf040";  /* 512KB */
+                               reg = <1>;
+                               spi-max-frequency = <10000000>;
+                       };
+
+                       flash@2 {
+                               #address-cells = <1>;
+                               #size-cells = <1>;
+                               compatible = "eon,en25s64";   /* 8MB */
+                               reg = <2>;
+                               spi-max-frequency = <10000000>;
+                       };
+
+                       slic@2 {
+                               compatible = "maxim,ds26522";
+                               reg = <2>;
+                               spi-max-frequency = <2000000>;
+                       };
+
+                       slic@3 {
+                               compatible = "maxim,ds26522";
+                               reg = <3>;
+                               spi-max-frequency = <2000000>;
+                       };
+               };
+
+               i2c@118000 {
+                       pca9547@77 {
+                               compatible = "nxp,pca9547";
+                               reg = <0x77>;
+                               #address-cells = <1>;
+                               #size-cells = <0>;
+
+                               i2c@0 {
+                                       #address-cells = <1>;
+                                       #size-cells = <0>;
+                                       reg = <0x0>;
+
+                                       eeprom@50 {
+                                               compatible = "atmel,24c512";
+                                               reg = <0x50>;
+                                       };
+
+                                       eeprom@51 {
+                                               compatible = "atmel,24c02";
+                                               reg = <0x51>;
+                                       };
+
+                                       eeprom@57 {
+                                               compatible = "atmel,24c02";
+                                               reg = <0x57>;
+                                       };
+                               };
+
+                               i2c@2 {
+                                       #address-cells = <1>;
+                                       #size-cells = <0>;
+                                       reg = <0x2>;
+
+                                       ina220@40 {
+                                               compatible = "ti,ina220";
+                                               reg = <0x40>;
+                                               shunt-resistor = <1000>;
+                                       };
+
+                                       ina220@41 {
+                                               compatible = "ti,ina220";
+                                               reg = <0x41>;
+                                               shunt-resistor = <1000>;
+                                       };
+                               };
+
+                               i2c@3 {
+                                       #address-cells = <1>;
+                                       #size-cells = <0>;
+                                       reg = <0x3>;
+
+                                       adt7461@4c {
+                                               /* Thermal Monitor */
+                                               compatible = "adi,adt7461";
+                                               reg = <0x4c>;
+                                       };
+
+                                       eeprom@55 {
+                                               compatible = "atmel,24c02";
+                                               reg = <0x55>;
+                                       };
+
+                                       eeprom@56 {
+                                               compatible = "atmel,24c512";
+                                               reg = <0x56>;
+                                       };
+
+                                       eeprom@57 {
+                                               compatible = "atmel,24c512";
+                                               reg = <0x57>;
+                                       };
+                               };
+                       };
+                       rtc@68 {
+                               compatible = "dallas,ds3232";
+                               reg = <0x68>;
+                               interrupts = <0x5 0x1 0 0>;
+                       };
+               };
+       };
+
+       pci0: pcie@ffe240000 {
+               reg = <0xf 0xfe240000 0 0x10000>;
+               ranges = <0x02000000 0 0xe0000000 0xc 0x00000000 0 0x10000000
+                         0x01000000 0 0x00000000 0xf 0xf8000000 0 0x00010000>;
+               pcie@0 {
+                       ranges = <0x02000000 0 0xe0000000
+                                 0x02000000 0 0xe0000000
+                                 0 0x10000000
+
+                                 0x01000000 0 0x00000000
+                                 0x01000000 0 0x00000000
+                                 0 0x00010000>;
+               };
+       };
+
+       pci1: pcie@ffe250000 {
+               reg = <0xf 0xfe250000 0 0x10000>;
+               ranges = <0x02000000 0 0xe0000000 0xc 0x10000000 0 0x10000000
+                         0x01000000 0 0x00000000 0xf 0xf8010000 0 0x00010000>;
+               pcie@0 {
+                       ranges = <0x02000000 0 0xe0000000
+                                 0x02000000 0 0xe0000000
+                                 0 0x10000000
+
+                                 0x01000000 0 0x00000000
+                                 0x01000000 0 0x00000000
+                                 0 0x00010000>;
+               };
+       };
+
+       pci2: pcie@ffe260000 {
+               reg = <0xf 0xfe260000 0 0x10000>;
+               ranges = <0x02000000 0 0xe0000000 0xc 0x20000000 0 0x10000000
+                         0x01000000 0 0x00000000 0xf 0xf8020000 0 0x00010000>;
+               pcie@0 {
+                       ranges = <0x02000000 0 0xe0000000
+                                 0x02000000 0 0xe0000000
+                                 0 0x10000000
+
+                                 0x01000000 0 0x00000000
+                                 0x01000000 0 0x00000000
+                                 0 0x00010000>;
+               };
+       };
+};
+
+/include/ "fsl/t1024si-post.dtsi"
diff --git a/arch/powerpc/boot/dts/t1024rdb.dts b/arch/powerpc/boot/dts/t1024rdb.dts
new file mode 100644 (file)
index 0000000..733e723
--- /dev/null
@@ -0,0 +1,185 @@
+/*
+ * T1024 RDB Device Tree Source
+ *
+ * Copyright 2014 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/ "fsl/t102xsi-pre.dtsi"
+
+/ {
+       model = "fsl,T1024RDB";
+       compatible = "fsl,T1024RDB";
+       #address-cells = <2>;
+       #size-cells = <2>;
+       interrupt-parent = <&mpic>;
+
+       ifc: localbus@ffe124000 {
+               reg = <0xf 0xfe124000 0 0x2000>;
+               ranges = <0 0 0xf 0xe8000000 0x08000000
+                         2 0 0xf 0xff800000 0x00010000
+                         3 0 0xf 0xffdf0000 0x00008000>;
+
+               nor@0,0 {
+                       #address-cells = <1>;
+                       #size-cells = <1>;
+                       compatible = "cfi-flash";
+                       reg = <0x0 0x0 0x8000000>;
+                       bank-width = <2>;
+                       device-width = <1>;
+               };
+
+               nand@1,0 {
+                       #address-cells = <1>;
+                       #size-cells = <1>;
+                       compatible = "fsl,ifc-nand";
+                       reg = <0x2 0x0 0x10000>;
+               };
+
+               board-control@2,0 {
+                       #address-cells = <1>;
+                       #size-cells = <1>;
+                       compatible = "fsl,t1024-cpld";
+                       reg = <3 0 0x300>;
+                       ranges = <0 3 0 0x300>;
+                       bank-width = <1>;
+                       device-width = <1>;
+               };
+       };
+
+       memory {
+               device_type = "memory";
+       };
+
+       dcsr: dcsr@f00000000 {
+               ranges = <0x00000000 0xf 0x00000000 0x01072000>;
+       };
+
+       soc: soc@ffe000000 {
+               ranges = <0x00000000 0xf 0xfe000000 0x1000000>;
+               reg = <0xf 0xfe000000 0 0x00001000>;
+               spi@110000 {
+                       flash@0 {
+                               #address-cells = <1>;
+                               #size-cells = <1>;
+                               compatible = "micron,n25q512ax3";
+                               reg = <0>;
+                               spi-max-frequency = <10000000>; /* input clk */
+                       };
+
+                       slic@1 {
+                               compatible = "maxim,ds26522";
+                               reg = <1>;
+                               spi-max-frequency = <2000000>;
+                       };
+
+                       slic@2 {
+                               compatible = "maxim,ds26522";
+                               reg = <2>;
+                               spi-max-frequency = <2000000>;
+                       };
+               };
+
+               i2c@118000 {
+                       adt7461@4c {
+                               /* Thermal Monitor */
+                               compatible = "adi,adt7461";
+                               reg = <0x4c>;
+                       };
+
+                       eeprom@50 {
+                               compatible = "atmel,24c256";
+                               reg = <0x50>;
+                       };
+
+                       rtc@68 {
+                               compatible = "dallas,ds1339";
+                               reg = <0x68>;
+                               interrupts = <0x1 0x1 0 0>;
+                       };
+               };
+
+               i2c@118100 {
+                       pca9546@77 {
+                               compatible = "nxp,pca9546";
+                               reg = <0x77>;
+                               #address-cells = <1>;
+                               #size-cells = <0>;
+                       };
+               };
+       };
+
+       pci0: pcie@ffe240000 {
+               reg = <0xf 0xfe240000 0 0x10000>;
+               ranges = <0x02000000 0 0xe0000000 0xc 0x00000000 0 0x10000000
+                         0x01000000 0 0x00000000 0xf 0xf8000000 0 0x00010000>;
+               pcie@0 {
+                       ranges = <0x02000000 0 0xe0000000
+                                 0x02000000 0 0xe0000000
+                                 0 0x10000000
+
+                                 0x01000000 0 0x00000000
+                                 0x01000000 0 0x00000000
+                                 0 0x00010000>;
+               };
+       };
+
+       pci1: pcie@ffe250000 {
+               reg = <0xf 0xfe250000 0 0x10000>;
+               ranges = <0x02000000 0 0xe0000000 0xc 0x10000000 0 0x10000000
+                         0x01000000 0 0x00000000 0xf 0xf8010000 0 0x00010000>;
+               pcie@0 {
+                       ranges = <0x02000000 0 0xe0000000
+                                 0x02000000 0 0xe0000000
+                                 0 0x10000000
+
+                                 0x01000000 0 0x00000000
+                                 0x01000000 0 0x00000000
+                                 0 0x00010000>;
+               };
+       };
+
+       pci2: pcie@ffe260000 {
+               reg = <0xf 0xfe260000 0 0x10000>;
+               ranges = <0x02000000 0 0xe0000000 0xc 0x20000000 0 0x10000000
+                         0x01000000 0 0x00000000 0xf 0xf8020000 0 0x00010000>;
+               pcie@0 {
+                       ranges = <0x02000000 0 0xe0000000
+                                 0x02000000 0 0xe0000000
+                                 0 0x10000000
+
+                                 0x01000000 0 0x00000000
+                                 0x01000000 0 0x00000000
+                                 0 0x00010000>;
+               };
+       };
+};
+
+/include/ "fsl/t1024si-post.dtsi"
index f7e9bfbeefc7b1b288229b6a99172db10582dd45..1498d1e4aecf6338244e71b0b4f9c35b05d9883f 100644 (file)
                        size = <0 0x1000000>;
                        alignment = <0 0x1000000>;
                };
+               qman_fqd: qman-fqd {
+                       size = <0 0x400000>;
+                       alignment = <0 0x400000>;
+               };
+               qman_pfdr: qman-pfdr {
+                       size = <0 0x2000000>;
+                       alignment = <0 0x2000000>;
+               };
        };
 
        ifc: localbus@ffe124000 {
                ranges = <0x0 0xf 0xf4000000 0x2000000>;
        };
 
+       qportals: qman-portals@ff6000000 {
+               ranges = <0x0 0xf 0xf6000000 0x2000000>;
+       };
+
        soc: soc@ffe000000 {
                ranges = <0x00000000 0xf 0xfe000000 0x1000000>;
                reg = <0xf 0xfe000000 0 0x00001000>;
index 76e07a3f2ca8b993e72b99ea0233678387d51e05..830ea484295b846e5b07f1b05998f07760f7fa80 100644 (file)
                        size = <0 0x1000000>;
                        alignment = <0 0x1000000>;
                };
+               qman_fqd: qman-fqd {
+                       size = <0 0x400000>;
+                       alignment = <0 0x400000>;
+               };
+               qman_pfdr: qman-pfdr {
+                       size = <0 0x2000000>;
+                       alignment = <0 0x2000000>;
+               };
        };
 
        ifc: localbus@ffe124000 {
                ranges = <0x0 0xf 0xf4000000 0x2000000>;
        };
 
+       qportals: qman-portals@ff6000000 {
+               ranges = <0x0 0xf 0xf6000000 0x2000000>;
+       };
+
        soc: soc@ffe000000 {
                ranges = <0x00000000 0xf 0xfe000000 0x1000000>;
                reg = <0xf 0xfe000000 0 0x00001000>;
index c42e07f4f648d8db7cb4180f150377d06fa5e995..869f9159b4d14ff9ed775c41f8dd2f17b8df0259 100644 (file)
                        size = <0 0x1000000>;
                        alignment = <0 0x1000000>;
                };
+               qman_fqd: qman-fqd {
+                       size = <0 0x400000>;
+                       alignment = <0 0x400000>;
+               };
+               qman_pfdr: qman-pfdr {
+                       size = <0 0x2000000>;
+                       alignment = <0 0x2000000>;
+               };
        };
 
        ifc: localbus@ffe124000 {
                ranges = <0x0 0xf 0xf4000000 0x2000000>;
        };
 
+       qportals: qman-portals@ff6000000 {
+               ranges = <0x0 0xf 0xf6000000 0x2000000>;
+       };
+
        soc: soc@ffe000000 {
                ranges = <0x00000000 0xf 0xfe000000 0x1000000>;
                reg = <0xf 0xfe000000 0 0x00001000>;
index e1463b165d0ecf3282c67ccfd5aad8fdd90ce1c6..693d2a8fa01cde3b78e2717af34bca06a737fa72 100644 (file)
                        size = <0 0x1000000>;
                        alignment = <0 0x1000000>;
                };
+               qman_fqd: qman-fqd {
+                       size = <0 0x400000>;
+                       alignment = <0 0x400000>;
+               };
+               qman_pfdr: qman-pfdr {
+                       size = <0 0x2000000>;
+                       alignment = <0 0x2000000>;
+               };
        };
 
        ifc: localbus@ffe124000 {
                ranges = <0x0 0xf 0xf4000000 0x2000000>;
        };
 
+       qportals: qman-portals@ff6000000 {
+               ranges = <0x0 0xf 0xf6000000 0x2000000>;
+       };
+
        soc: soc@ffe000000 {
                ranges = <0x00000000 0xf 0xfe000000 0x1000000>;
                reg = <0xf 0xfe000000 0 0x00001000>;
index 6df77766410b13591fb5fc4c658675f7105630f0..93722da10e16899da336c4f6f5a0095e03c1f81e 100644 (file)
                        size = <0 0x1000000>;
                        alignment = <0 0x1000000>;
                };
+               qman_fqd: qman-fqd {
+                       size = <0 0x400000>;
+                       alignment = <0 0x400000>;
+               };
+               qman_pfdr: qman-pfdr {
+                       size = <0 0x2000000>;
+                       alignment = <0 0x2000000>;
+               };
        };
 
        dcsr: dcsr@f00000000 {
                ranges = <0x0 0xf 0xf4000000 0x2000000>;
        };
 
+       qportals: qman-portals@ff6000000 {
+               ranges = <0x0 0xf 0xf6000000 0x2000000>;
+       };
+
        soc: soc@ffe000000 {
                ranges = <0x00000000 0xf 0xfe000000 0x1000000>;
                reg = <0xf 0xfe000000 0 0x00001000>;
index 46049cf37f022f3f0f791cbf99218bd964e0e4ae..993eb4b8a487543663c8530f44397d350648c468 100644 (file)
                        size = <0 0x1000000>;
                        alignment = <0 0x1000000>;
                };
+               qman_fqd: qman-fqd {
+                       size = <0 0x400000>;
+                       alignment = <0 0x400000>;
+               };
+               qman_pfdr: qman-pfdr {
+                       size = <0 0x2000000>;
+                       alignment = <0 0x2000000>;
+               };
        };
 
        dcsr: dcsr@f00000000 {
                ranges = <0x0 0xf 0xf4000000 0x2000000>;
        };
 
+       qportals: qman-portals@ff6000000 {
+               ranges = <0x0 0xf 0xf6000000 0x2000000>;
+       };
+
        soc: soc@ffe000000 {
                ranges = <0x00000000 0xf 0xfe000000 0x1000000>;
                reg = <0xf 0xfe000000 0 0x00001000>;
index 34f3ea1729e0b052773766094ff30cca3c912805..858b539d004bbfd22793bc71106d07ae0579f393 100644 (file)
@@ -108,7 +108,7 @@ CONFIG_SENSORS_LM90=y
 CONFIG_WATCHDOG=y
 CONFIG_USB=y
 CONFIG_USB_MON=y
-CONFIG_USB_ISP1760_HCD=y
+CONFIG_USB_ISP1760=y
 CONFIG_USB_STORAGE=y
 CONFIG_NEW_LEDS=y
 CONFIG_LEDS_CLASS=y
diff --git a/arch/powerpc/configs/le.config b/arch/powerpc/configs/le.config
new file mode 100644 (file)
index 0000000..ee43fdb
--- /dev/null
@@ -0,0 +1 @@
+CONFIG_CPU_LITTLE_ENDIAN=y
index aad501ae38344f4f16b220845b40d6ce0305a2f8..a97efc2146fdfe949bf0f569c549497d4dc5c6cf 100644 (file)
@@ -155,6 +155,7 @@ CONFIG_ACENIC=m
 CONFIG_ACENIC_OMIT_TIGON_I=y
 CONFIG_PCNET32=y
 CONFIG_TIGON3=y
+CONFIG_BNX2X=m
 CONFIG_CHELSIO_T1=m
 CONFIG_BE2NET=m
 CONFIG_S2IO=m
index c2e39f66b182a6b4a8d1e9558eef6824a2134d0a..0d9efcedaf3473c4573548995069b0e5cec5c765 100644 (file)
@@ -154,6 +154,7 @@ CONFIG_ACENIC=m
 CONFIG_ACENIC_OMIT_TIGON_I=y
 CONFIG_PCNET32=y
 CONFIG_TIGON3=y
+CONFIG_BNX2X=m
 CONFIG_CHELSIO_T1=m
 CONFIG_BE2NET=m
 CONFIG_S2IO=m
@@ -297,7 +298,6 @@ CONFIG_CODE_PATCHING_SELFTEST=y
 CONFIG_FTR_FIXUP_SELFTEST=y
 CONFIG_MSI_BITMAP_SELFTEST=y
 CONFIG_XMON=y
-CONFIG_XMON_DEFAULT=y
 CONFIG_CRYPTO_TEST=m
 CONFIG_CRYPTO_PCBC=m
 CONFIG_CRYPTO_HMAC=y
diff --git a/arch/powerpc/configs/pseries_le_defconfig b/arch/powerpc/configs/pseries_le_defconfig
deleted file mode 100644 (file)
index 09bc96e..0000000
+++ /dev/null
@@ -1,319 +0,0 @@
-CONFIG_PPC64=y
-CONFIG_SMP=y
-CONFIG_NR_CPUS=2048
-CONFIG_CPU_LITTLE_ENDIAN=y
-CONFIG_SYSVIPC=y
-CONFIG_POSIX_MQUEUE=y
-CONFIG_FHANDLE=y
-CONFIG_AUDIT=y
-CONFIG_AUDITSYSCALL=y
-CONFIG_IRQ_DOMAIN_DEBUG=y
-CONFIG_NO_HZ=y
-CONFIG_HIGH_RES_TIMERS=y
-CONFIG_TASKSTATS=y
-CONFIG_TASK_DELAY_ACCT=y
-CONFIG_TASK_XACCT=y
-CONFIG_TASK_IO_ACCOUNTING=y
-CONFIG_IKCONFIG=y
-CONFIG_IKCONFIG_PROC=y
-CONFIG_NUMA_BALANCING=y
-CONFIG_NUMA_BALANCING_DEFAULT_ENABLED=y
-CONFIG_CGROUPS=y
-CONFIG_CGROUP_FREEZER=y
-CONFIG_CGROUP_DEVICE=y
-CONFIG_CPUSETS=y
-CONFIG_CGROUP_CPUACCT=y
-CONFIG_MEMCG=y
-CONFIG_MEMCG_SWAP=y
-CONFIG_CGROUP_PERF=y
-CONFIG_CGROUP_SCHED=y
-CONFIG_USER_NS=y
-CONFIG_BLK_DEV_INITRD=y
-# CONFIG_COMPAT_BRK is not set
-CONFIG_PROFILING=y
-CONFIG_OPROFILE=y
-CONFIG_KPROBES=y
-CONFIG_JUMP_LABEL=y
-CONFIG_MODULES=y
-CONFIG_MODULE_UNLOAD=y
-CONFIG_MODVERSIONS=y
-CONFIG_MODULE_SRCVERSION_ALL=y
-CONFIG_PARTITION_ADVANCED=y
-CONFIG_PPC_SPLPAR=y
-CONFIG_SCANLOG=m
-CONFIG_PPC_SMLPAR=y
-CONFIG_DTL=y
-# CONFIG_PPC_PMAC is not set
-CONFIG_RTAS_FLASH=m
-CONFIG_IBMEBUS=y
-CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y
-CONFIG_HZ_100=y
-CONFIG_BINFMT_MISC=m
-CONFIG_PPC_TRANSACTIONAL_MEM=y
-CONFIG_KEXEC=y
-CONFIG_IRQ_ALL_CPUS=y
-CONFIG_MEMORY_HOTPLUG=y
-CONFIG_MEMORY_HOTREMOVE=y
-CONFIG_KSM=y
-CONFIG_TRANSPARENT_HUGEPAGE=y
-CONFIG_PPC_64K_PAGES=y
-CONFIG_PPC_SUBPAGE_PROT=y
-CONFIG_SCHED_SMT=y
-CONFIG_HOTPLUG_PCI=y
-CONFIG_HOTPLUG_PCI_RPA=m
-CONFIG_HOTPLUG_PCI_RPA_DLPAR=m
-CONFIG_NET=y
-CONFIG_PACKET=y
-CONFIG_UNIX=y
-CONFIG_XFRM_USER=m
-CONFIG_NET_KEY=m
-CONFIG_INET=y
-CONFIG_IP_MULTICAST=y
-CONFIG_NET_IPIP=y
-CONFIG_SYN_COOKIES=y
-CONFIG_INET_AH=m
-CONFIG_INET_ESP=m
-CONFIG_INET_IPCOMP=m
-# CONFIG_IPV6 is not set
-CONFIG_NETFILTER=y
-# CONFIG_NETFILTER_ADVANCED is not set
-CONFIG_BRIDGE=m
-CONFIG_VLAN_8021Q=m
-CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
-CONFIG_DEVTMPFS=y
-CONFIG_DEVTMPFS_MOUNT=y
-CONFIG_PARPORT=m
-CONFIG_PARPORT_PC=m
-CONFIG_BLK_DEV_FD=m
-CONFIG_BLK_DEV_LOOP=y
-CONFIG_BLK_DEV_NBD=m
-CONFIG_BLK_DEV_RAM=y
-CONFIG_BLK_DEV_RAM_SIZE=65536
-CONFIG_VIRTIO_BLK=m
-CONFIG_IDE=y
-CONFIG_BLK_DEV_IDECD=y
-CONFIG_BLK_DEV_GENERIC=y
-CONFIG_BLK_DEV_AMD74XX=y
-CONFIG_BLK_DEV_SD=y
-CONFIG_CHR_DEV_ST=y
-CONFIG_BLK_DEV_SR=y
-CONFIG_BLK_DEV_SR_VENDOR=y
-CONFIG_CHR_DEV_SG=y
-CONFIG_SCSI_CONSTANTS=y
-CONFIG_SCSI_FC_ATTRS=y
-CONFIG_SCSI_CXGB3_ISCSI=m
-CONFIG_SCSI_CXGB4_ISCSI=m
-CONFIG_SCSI_BNX2_ISCSI=m
-CONFIG_BE2ISCSI=m
-CONFIG_SCSI_MPT2SAS=m
-CONFIG_SCSI_IBMVSCSI=y
-CONFIG_SCSI_IBMVFC=m
-CONFIG_SCSI_SYM53C8XX_2=y
-CONFIG_SCSI_SYM53C8XX_DMA_ADDRESSING_MODE=0
-CONFIG_SCSI_IPR=y
-CONFIG_SCSI_QLA_FC=m
-CONFIG_SCSI_QLA_ISCSI=m
-CONFIG_SCSI_LPFC=m
-CONFIG_SCSI_VIRTIO=m
-CONFIG_SCSI_DH=m
-CONFIG_SCSI_DH_RDAC=m
-CONFIG_SCSI_DH_ALUA=m
-CONFIG_ATA=y
-CONFIG_SATA_AHCI=y
-# CONFIG_ATA_SFF is not set
-CONFIG_MD=y
-CONFIG_BLK_DEV_MD=y
-CONFIG_MD_LINEAR=y
-CONFIG_MD_RAID0=y
-CONFIG_MD_RAID1=y
-CONFIG_MD_RAID10=m
-CONFIG_MD_RAID456=m
-CONFIG_MD_MULTIPATH=m
-CONFIG_MD_FAULTY=m
-CONFIG_BLK_DEV_DM=y
-CONFIG_DM_CRYPT=m
-CONFIG_DM_SNAPSHOT=m
-CONFIG_DM_THIN_PROVISIONING=m
-CONFIG_DM_MIRROR=m
-CONFIG_DM_ZERO=m
-CONFIG_DM_MULTIPATH=m
-CONFIG_DM_MULTIPATH_QL=m
-CONFIG_DM_MULTIPATH_ST=m
-CONFIG_DM_UEVENT=y
-CONFIG_BONDING=m
-CONFIG_DUMMY=m
-CONFIG_MACVLAN=m
-CONFIG_MACVTAP=m
-CONFIG_VXLAN=m
-CONFIG_NETCONSOLE=y
-CONFIG_TUN=m
-CONFIG_VETH=m
-CONFIG_VIRTIO_NET=m
-CONFIG_VHOST_NET=m
-CONFIG_VORTEX=y
-CONFIG_ACENIC=m
-CONFIG_ACENIC_OMIT_TIGON_I=y
-CONFIG_PCNET32=y
-CONFIG_TIGON3=y
-CONFIG_CHELSIO_T1=m
-CONFIG_BE2NET=m
-CONFIG_S2IO=m
-CONFIG_IBMVETH=y
-CONFIG_EHEA=y
-CONFIG_E100=y
-CONFIG_E1000=y
-CONFIG_E1000E=y
-CONFIG_IXGB=m
-CONFIG_IXGBE=m
-CONFIG_MLX4_EN=m
-CONFIG_MYRI10GE=m
-CONFIG_QLGE=m
-CONFIG_NETXEN_NIC=m
-CONFIG_PPP=m
-CONFIG_PPP_BSDCOMP=m
-CONFIG_PPP_DEFLATE=m
-CONFIG_PPPOE=m
-CONFIG_PPP_ASYNC=m
-CONFIG_PPP_SYNC_TTY=m
-# CONFIG_INPUT_MOUSEDEV_PSAUX is not set
-CONFIG_INPUT_EVDEV=m
-CONFIG_INPUT_MISC=y
-CONFIG_INPUT_PCSPKR=m
-# CONFIG_SERIO_SERPORT is not set
-CONFIG_DEVPTS_MULTIPLE_INSTANCES=y
-CONFIG_SERIAL_8250=y
-CONFIG_SERIAL_8250_CONSOLE=y
-CONFIG_SERIAL_ICOM=m
-CONFIG_SERIAL_JSM=m
-CONFIG_HVC_CONSOLE=y
-CONFIG_HVC_RTAS=y
-CONFIG_HVCS=m
-CONFIG_VIRTIO_CONSOLE=m
-CONFIG_IBM_BSR=m
-CONFIG_GEN_RTC=y
-CONFIG_RAW_DRIVER=y
-CONFIG_MAX_RAW_DEVS=1024
-CONFIG_FB=y
-CONFIG_FIRMWARE_EDID=y
-CONFIG_FB_OF=y
-CONFIG_FB_MATROX=y
-CONFIG_FB_MATROX_MILLENIUM=y
-CONFIG_FB_MATROX_MYSTIQUE=y
-CONFIG_FB_MATROX_G=y
-CONFIG_FB_RADEON=y
-CONFIG_FB_IBM_GXT4500=y
-CONFIG_LCD_PLATFORM=m
-# CONFIG_VGA_CONSOLE is not set
-CONFIG_FRAMEBUFFER_CONSOLE=y
-CONFIG_LOGO=y
-CONFIG_HID_GYRATION=y
-CONFIG_HID_PANTHERLORD=y
-CONFIG_HID_PETALYNX=y
-CONFIG_HID_SAMSUNG=y
-CONFIG_HID_SUNPLUS=y
-CONFIG_USB_HIDDEV=y
-CONFIG_USB=y
-CONFIG_USB_MON=m
-CONFIG_USB_EHCI_HCD=y
-# CONFIG_USB_EHCI_HCD_PPC_OF is not set
-CONFIG_USB_OHCI_HCD=y
-CONFIG_USB_STORAGE=m
-CONFIG_INFINIBAND=m
-CONFIG_INFINIBAND_USER_MAD=m
-CONFIG_INFINIBAND_USER_ACCESS=m
-CONFIG_INFINIBAND_MTHCA=m
-CONFIG_INFINIBAND_EHCA=m
-CONFIG_INFINIBAND_CXGB3=m
-CONFIG_INFINIBAND_CXGB4=m
-CONFIG_MLX4_INFINIBAND=m
-CONFIG_INFINIBAND_IPOIB=m
-CONFIG_INFINIBAND_IPOIB_CM=y
-CONFIG_INFINIBAND_SRP=m
-CONFIG_INFINIBAND_ISER=m
-CONFIG_VIRTIO_PCI=m
-CONFIG_VIRTIO_BALLOON=m
-CONFIG_EXT2_FS=y
-CONFIG_EXT2_FS_XATTR=y
-CONFIG_EXT2_FS_POSIX_ACL=y
-CONFIG_EXT2_FS_SECURITY=y
-CONFIG_EXT2_FS_XIP=y
-CONFIG_EXT3_FS=y
-CONFIG_EXT3_FS_POSIX_ACL=y
-CONFIG_EXT3_FS_SECURITY=y
-CONFIG_EXT4_FS=y
-CONFIG_EXT4_FS_POSIX_ACL=y
-CONFIG_EXT4_FS_SECURITY=y
-CONFIG_REISERFS_FS=y
-CONFIG_REISERFS_FS_XATTR=y
-CONFIG_REISERFS_FS_POSIX_ACL=y
-CONFIG_REISERFS_FS_SECURITY=y
-CONFIG_JFS_FS=m
-CONFIG_JFS_POSIX_ACL=y
-CONFIG_JFS_SECURITY=y
-CONFIG_XFS_FS=m
-CONFIG_XFS_POSIX_ACL=y
-CONFIG_BTRFS_FS=m
-CONFIG_BTRFS_FS_POSIX_ACL=y
-CONFIG_NILFS2_FS=m
-CONFIG_AUTOFS4_FS=m
-CONFIG_FUSE_FS=m
-CONFIG_OVERLAY_FS=m
-CONFIG_ISO9660_FS=y
-CONFIG_UDF_FS=m
-CONFIG_MSDOS_FS=y
-CONFIG_VFAT_FS=y
-CONFIG_PROC_KCORE=y
-CONFIG_TMPFS=y
-CONFIG_TMPFS_POSIX_ACL=y
-CONFIG_HUGETLBFS=y
-CONFIG_CRAMFS=m
-CONFIG_SQUASHFS=m
-CONFIG_SQUASHFS_XATTR=y
-CONFIG_SQUASHFS_LZO=y
-CONFIG_SQUASHFS_XZ=y
-CONFIG_PSTORE=y
-CONFIG_NFS_FS=y
-CONFIG_NFS_V3_ACL=y
-CONFIG_NFS_V4=y
-CONFIG_NFSD=m
-CONFIG_NFSD_V3_ACL=y
-CONFIG_NFSD_V4=y
-CONFIG_CIFS=m
-CONFIG_CIFS_XATTR=y
-CONFIG_CIFS_POSIX=y
-CONFIG_NLS_DEFAULT="utf8"
-CONFIG_NLS_CODEPAGE_437=y
-CONFIG_NLS_ASCII=y
-CONFIG_NLS_ISO8859_1=y
-CONFIG_NLS_UTF8=y
-CONFIG_MAGIC_SYSRQ=y
-CONFIG_DEBUG_KERNEL=y
-CONFIG_DEBUG_STACK_USAGE=y
-CONFIG_DEBUG_STACKOVERFLOW=y
-CONFIG_LOCKUP_DETECTOR=y
-CONFIG_LATENCYTOP=y
-CONFIG_SCHED_TRACER=y
-CONFIG_BLK_DEV_IO_TRACE=y
-CONFIG_CODE_PATCHING_SELFTEST=y
-CONFIG_FTR_FIXUP_SELFTEST=y
-CONFIG_MSI_BITMAP_SELFTEST=y
-CONFIG_XMON=y
-CONFIG_CRYPTO_TEST=m
-CONFIG_CRYPTO_PCBC=m
-CONFIG_CRYPTO_HMAC=y
-CONFIG_CRYPTO_MICHAEL_MIC=m
-CONFIG_CRYPTO_TGR192=m
-CONFIG_CRYPTO_WP512=m
-CONFIG_CRYPTO_ANUBIS=m
-CONFIG_CRYPTO_BLOWFISH=m
-CONFIG_CRYPTO_CAST6=m
-CONFIG_CRYPTO_KHAZAD=m
-CONFIG_CRYPTO_SALSA20=m
-CONFIG_CRYPTO_SERPENT=m
-CONFIG_CRYPTO_TEA=m
-CONFIG_CRYPTO_TWOFISH=m
-CONFIG_CRYPTO_LZO=m
-# CONFIG_CRYPTO_ANSI_CPRNG is not set
-CONFIG_VIRTUALIZATION=y
-CONFIG_KVM_BOOK3S_64=m
-CONFIG_KVM_BOOK3S_64_HV=m
index 452fb4dc575f3ab2615cd1bec5708cff45fc49f5..92289679b4c47112d29b3753de84bc7cd1cb52b4 100644 (file)
@@ -37,10 +37,10 @@ static int ppc_md5_init(struct shash_desc *desc)
 {
        struct md5_state *sctx = shash_desc_ctx(desc);
 
-       sctx->hash[0] = 0x67452301;
-       sctx->hash[1] = 0xefcdab89;
-       sctx->hash[2] = 0x98badcfe;
-       sctx->hash[3] = 0x10325476;
+       sctx->hash[0] = MD5_H0;
+       sctx->hash[1] = MD5_H1;
+       sctx->hash[2] = MD5_H2;
+       sctx->hash[3] = MD5_H3;
        sctx->byte_count = 0;
 
        return 0;
index a3bf5be111ff1d073eb329476ce773c07ce32ed5..51ccc7232042e9f415c26580cca3a83d02175625 100644 (file)
@@ -34,7 +34,7 @@
 #define rmb()  __asm__ __volatile__ ("sync" : : : "memory")
 #define wmb()  __asm__ __volatile__ ("sync" : : : "memory")
 
-#define set_mb(var, value)     do { var = value; mb(); } while (0)
+#define smp_store_mb(var, value)       do { WRITE_ONCE(var, value); mb(); } while (0)
 
 #ifdef __SUBARCH_HAS_LWSYNC
 #    define SMPWMB      LWSYNC
@@ -89,5 +89,6 @@ do {                                                                  \
 
 #define smp_mb__before_atomic()     smp_mb()
 #define smp_mb__after_atomic()      smp_mb()
+#define smp_mb__before_spinlock()   smp_mb()
 
 #endif /* _ASM_POWERPC_BARRIER_H */
index d463c68fe7f05fa798a151ea179a5f88a842ddee..ad6263cffb0fd0b3e09b192748f57f9049ce3cae 100644 (file)
@@ -144,7 +144,6 @@ __xchg_local(volatile void *ptr, unsigned long x, unsigned int size)
  * Compare and exchange - if *p == old, set it to new,
  * and return the old value of *p.
  */
-#define __HAVE_ARCH_CMPXCHG    1
 
 static __always_inline unsigned long
 __cmpxchg_u32(volatile unsigned int *p, unsigned long old, unsigned long new)
index 6367b8347dad9b0bceb3507573450b2e7c672400..b118072670fb15ceac83fce37967f1b07e4cffec 100644 (file)
@@ -242,11 +242,13 @@ enum {
 
 /* We only set the TM feature if the kernel was compiled with TM supprt */
 #ifdef CONFIG_PPC_TRANSACTIONAL_MEM
-#define CPU_FTR_TM_COMP                CPU_FTR_TM
-#define PPC_FEATURE2_HTM_COMP  PPC_FEATURE2_HTM
+#define CPU_FTR_TM_COMP                        CPU_FTR_TM
+#define PPC_FEATURE2_HTM_COMP          PPC_FEATURE2_HTM
+#define PPC_FEATURE2_HTM_NOSC_COMP     PPC_FEATURE2_HTM_NOSC
 #else
-#define CPU_FTR_TM_COMP                0
-#define PPC_FEATURE2_HTM_COMP  0
+#define CPU_FTR_TM_COMP                        0
+#define PPC_FEATURE2_HTM_COMP          0
+#define PPC_FEATURE2_HTM_NOSC_COMP     0
 #endif
 
 /* We need to mark all pages as being coherent if we're SMP or we have a
@@ -366,7 +368,7 @@ enum {
            CPU_FTR_USE_TB | CPU_FTR_MAYBE_CAN_NAP | \
            CPU_FTR_COMMON | CPU_FTR_FPU_UNAVAILABLE)
 #define CPU_FTRS_CLASSIC32     (CPU_FTR_COMMON | CPU_FTR_USE_TB)
-#define CPU_FTRS_8XX   (CPU_FTR_USE_TB)
+#define CPU_FTRS_8XX   (CPU_FTR_USE_TB | CPU_FTR_NOEXECUTE)
 #define CPU_FTRS_40X   (CPU_FTR_USE_TB | CPU_FTR_NODSISRALIGN | CPU_FTR_NOEXECUTE)
 #define CPU_FTRS_44X   (CPU_FTR_USE_TB | CPU_FTR_NODSISRALIGN | CPU_FTR_NOEXECUTE)
 #define CPU_FTRS_440x6 (CPU_FTR_USE_TB | CPU_FTR_NODSISRALIGN | CPU_FTR_NOEXECUTE | \
index 5be6c4753667ef66b5c0151979802a5d2c627262..ba42e46ea58eeed448d3d7abb5c67f5570e96bca 100644 (file)
@@ -31,9 +31,9 @@ extern cpumask_t threads_core_mask;
 /* cpu_thread_mask_to_cores - Return a cpumask of one per cores
  *                            hit by the argument
  *
- * @threads:   a cpumask of threads
+ * @threads:   a cpumask of online threads
  *
- * This function returns a cpumask which will have one "cpu" (or thread)
+ * This function returns a cpumask which will have one online cpu's
  * bit set for each core that has at least one thread set in the argument.
  *
  * This can typically be used for things like IPI for tlb invalidations
@@ -42,13 +42,16 @@ extern cpumask_t threads_core_mask;
 static inline cpumask_t cpu_thread_mask_to_cores(const struct cpumask *threads)
 {
        cpumask_t       tmp, res;
-       int             i;
+       int             i, cpu;
 
        cpumask_clear(&res);
        for (i = 0; i < NR_CPUS; i += threads_per_core) {
                cpumask_shift_left(&tmp, &threads_core_mask, i);
-               if (cpumask_intersects(threads, &tmp))
-                       cpumask_set_cpu(i, &res);
+               if (cpumask_intersects(threads, &tmp)) {
+                       cpu = cpumask_next_and(-1, &tmp, cpu_online_mask);
+                       if (cpu < nr_cpu_ids)
+                               cpumask_set_cpu(cpu, &res);
+               }
        }
        return res;
 }
index 9f1371bab5fc2141a2345ce5ad1fac286465e868..e9bdda88f1fbb1e38af6b46abcafc330f7f2cdbd 100644 (file)
@@ -46,6 +46,9 @@ struct dev_archdata {
 #ifdef CONFIG_FAIL_IOMMU
        int fail_iommu;
 #endif
+#ifdef CONFIG_CXL_BASE
+       struct cxl_context      *cxl_ctx;
+#endif
 };
 
 struct pdev_archdata {
index a52db28ecc1e1f45287d24806f554a46ec87c6f6..c5eb86f3d452fbe66d44ae1cce9bbfff91a8b14d 100644 (file)
@@ -27,6 +27,8 @@
 #include <linux/time.h>
 #include <linux/atomic.h>
 
+#include <uapi/asm/eeh.h>
+
 struct pci_dev;
 struct pci_bus;
 struct pci_dn;
@@ -185,11 +187,6 @@ enum {
 #define EEH_STATE_DMA_ACTIVE   (1 << 4)        /* Active DMA           */
 #define EEH_STATE_MMIO_ENABLED (1 << 5)        /* MMIO enabled         */
 #define EEH_STATE_DMA_ENABLED  (1 << 6)        /* DMA enabled          */
-#define EEH_PE_STATE_NORMAL            0       /* Normal state         */
-#define EEH_PE_STATE_RESET             1       /* PE reset asserted    */
-#define EEH_PE_STATE_STOPPED_IO_DMA    2       /* Frozen PE            */
-#define EEH_PE_STATE_STOPPED_DMA       4       /* Stopped DMA, Enabled IO */
-#define EEH_PE_STATE_UNAVAIL           5       /* Unavailable          */
 #define EEH_RESET_DEACTIVATE   0       /* Deactivate the PE reset      */
 #define EEH_RESET_HOT          1       /* Hot reset                    */
 #define EEH_RESET_FUNDAMENTAL  3       /* Fundamental reset            */
@@ -294,6 +291,8 @@ int eeh_pe_set_option(struct eeh_pe *pe, int option);
 int eeh_pe_get_state(struct eeh_pe *pe);
 int eeh_pe_reset(struct eeh_pe *pe, int option);
 int eeh_pe_configure(struct eeh_pe *pe);
+int eeh_pe_inject_err(struct eeh_pe *pe, int type, int func,
+                     unsigned long addr, unsigned long mask);
 
 /**
  * EEH_POSSIBLE_ERROR() -- test for possible MMIO failure.
diff --git a/arch/powerpc/include/asm/icswx.h b/arch/powerpc/include/asm/icswx.h
new file mode 100644 (file)
index 0000000..9f8402b
--- /dev/null
@@ -0,0 +1,184 @@
+/*
+ * ICSWX api
+ *
+ * Copyright (C) 2015 IBM Corp.
+ *
+ * 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 provides the Initiate Coprocessor Store Word Indexed (ICSWX)
+ * instruction.  This instruction is used to communicate with PowerPC
+ * coprocessors.  This also provides definitions of the structures used
+ * to communicate with the coprocessor.
+ *
+ * The RFC02130: Coprocessor Architecture document is the reference for
+ * everything in this file unless otherwise noted.
+ */
+#ifndef _ARCH_POWERPC_INCLUDE_ASM_ICSWX_H_
+#define _ARCH_POWERPC_INCLUDE_ASM_ICSWX_H_
+
+#include <asm/ppc-opcode.h> /* for PPC_ICSWX */
+
+/* Chapter 6.5.8 Coprocessor-Completion Block (CCB) */
+
+#define CCB_VALUE              (0x3fffffffffffffff)
+#define CCB_ADDRESS            (0xfffffffffffffff8)
+#define CCB_CM                 (0x0000000000000007)
+#define CCB_CM0                        (0x0000000000000004)
+#define CCB_CM12               (0x0000000000000003)
+
+#define CCB_CM0_ALL_COMPLETIONS        (0x0)
+#define CCB_CM0_LAST_IN_CHAIN  (0x4)
+#define CCB_CM12_STORE         (0x0)
+#define CCB_CM12_INTERRUPT     (0x1)
+
+#define CCB_SIZE               (0x10)
+#define CCB_ALIGN              CCB_SIZE
+
+struct coprocessor_completion_block {
+       __be64 value;
+       __be64 address;
+} __packed __aligned(CCB_ALIGN);
+
+
+/* Chapter 6.5.7 Coprocessor-Status Block (CSB) */
+
+#define CSB_V                  (0x80)
+#define CSB_F                  (0x04)
+#define CSB_CH                 (0x03)
+#define CSB_CE_INCOMPLETE      (0x80)
+#define CSB_CE_TERMINATION     (0x40)
+#define CSB_CE_TPBC            (0x20)
+
+#define CSB_CC_SUCCESS         (0)
+#define CSB_CC_INVALID_ALIGN   (1)
+#define CSB_CC_OPERAND_OVERLAP (2)
+#define CSB_CC_DATA_LENGTH     (3)
+#define CSB_CC_TRANSLATION     (5)
+#define CSB_CC_PROTECTION      (6)
+#define CSB_CC_RD_EXTERNAL     (7)
+#define CSB_CC_INVALID_OPERAND (8)
+#define CSB_CC_PRIVILEGE       (9)
+#define CSB_CC_INTERNAL                (10)
+#define CSB_CC_WR_EXTERNAL     (12)
+#define CSB_CC_NOSPC           (13)
+#define CSB_CC_EXCESSIVE_DDE   (14)
+#define CSB_CC_WR_TRANSLATION  (15)
+#define CSB_CC_WR_PROTECTION   (16)
+#define CSB_CC_UNKNOWN_CODE    (17)
+#define CSB_CC_ABORT           (18)
+#define CSB_CC_TRANSPORT       (20)
+#define CSB_CC_SEGMENTED_DDL   (31)
+#define CSB_CC_PROGRESS_POINT  (32)
+#define CSB_CC_DDE_OVERFLOW    (33)
+#define CSB_CC_SESSION         (34)
+#define CSB_CC_PROVISION       (36)
+#define CSB_CC_CHAIN           (37)
+#define CSB_CC_SEQUENCE                (38)
+#define CSB_CC_HW              (39)
+
+#define CSB_SIZE               (0x10)
+#define CSB_ALIGN              CSB_SIZE
+
+struct coprocessor_status_block {
+       u8 flags;
+       u8 cs;
+       u8 cc;
+       u8 ce;
+       __be32 count;
+       __be64 address;
+} __packed __aligned(CSB_ALIGN);
+
+
+/* Chapter 6.5.10 Data-Descriptor List (DDL)
+ * each list contains one or more Data-Descriptor Entries (DDE)
+ */
+
+#define DDE_P                  (0x8000)
+
+#define DDE_SIZE               (0x10)
+#define DDE_ALIGN              DDE_SIZE
+
+struct data_descriptor_entry {
+       __be16 flags;
+       u8 count;
+       u8 index;
+       __be32 length;
+       __be64 address;
+} __packed __aligned(DDE_ALIGN);
+
+
+/* Chapter 6.5.2 Coprocessor-Request Block (CRB) */
+
+#define CRB_SIZE               (0x80)
+#define CRB_ALIGN              (0x100) /* Errata: requires 256 alignment */
+
+/* Coprocessor Status Block field
+ *   ADDRESS   address of CSB
+ *   C         CCB is valid
+ *   AT                0 = addrs are virtual, 1 = addrs are phys
+ *   M         enable perf monitor
+ */
+#define CRB_CSB_ADDRESS                (0xfffffffffffffff0)
+#define CRB_CSB_C              (0x0000000000000008)
+#define CRB_CSB_AT             (0x0000000000000002)
+#define CRB_CSB_M              (0x0000000000000001)
+
+struct coprocessor_request_block {
+       __be32 ccw;
+       __be32 flags;
+       __be64 csb_addr;
+
+       struct data_descriptor_entry source;
+       struct data_descriptor_entry target;
+
+       struct coprocessor_completion_block ccb;
+
+       u8 reserved[48];
+
+       struct coprocessor_status_block csb;
+} __packed __aligned(CRB_ALIGN);
+
+
+/* RFC02167 Initiate Coprocessor Instructions document
+ * Chapter 8.2.1.1.1 RS
+ * Chapter 8.2.3 Coprocessor Directive
+ * Chapter 8.2.4 Execution
+ *
+ * The CCW must be converted to BE before passing to icswx()
+ */
+
+#define CCW_PS                 (0xff000000)
+#define CCW_CT                 (0x00ff0000)
+#define CCW_CD                 (0x0000ffff)
+#define CCW_CL                 (0x0000c000)
+
+
+/* RFC02167 Initiate Coprocessor Instructions document
+ * Chapter 8.2.1 Initiate Coprocessor Store Word Indexed (ICSWX)
+ * Chapter 8.2.4.1 Condition Register 0
+ */
+
+#define ICSWX_INITIATED                (0x8)
+#define ICSWX_BUSY             (0x4)
+#define ICSWX_REJECTED         (0x2)
+
+static inline int icswx(__be32 ccw, struct coprocessor_request_block *crb)
+{
+       __be64 ccw_reg = ccw;
+       u32 cr;
+
+       __asm__ __volatile__(
+       PPC_ICSWX(%1,0,%2) "\n"
+       "mfcr %0\n"
+       : "=r" (cr)
+       : "r" (ccw_reg), "r" (crb)
+       : "cr0", "memory");
+
+       return (int)((cr >> 28) & 0xf);
+}
+
+
+#endif /* _ARCH_POWERPC_INCLUDE_ASM_ICSWX_H_ */
index 1e27d63385655b9c275937b1ee886b33b8c50ed9..ca18cff909006b66017587c30777665feffac913 100644 (file)
 extern int iommu_is_off;
 extern int iommu_force_on;
 
+struct iommu_table_ops {
+       /*
+        * When called with direction==DMA_NONE, it is equal to clear().
+        * uaddr is a linear map address.
+        */
+       int (*set)(struct iommu_table *tbl,
+                       long index, long npages,
+                       unsigned long uaddr,
+                       enum dma_data_direction direction,
+                       struct dma_attrs *attrs);
+#ifdef CONFIG_IOMMU_API
+       /*
+        * Exchanges existing TCE with new TCE plus direction bits;
+        * returns old TCE and DMA direction mask.
+        * @tce is a physical address.
+        */
+       int (*exchange)(struct iommu_table *tbl,
+                       long index,
+                       unsigned long *hpa,
+                       enum dma_data_direction *direction);
+#endif
+       void (*clear)(struct iommu_table *tbl,
+                       long index, long npages);
+       /* get() returns a physical address */
+       unsigned long (*get)(struct iommu_table *tbl, long index);
+       void (*flush)(struct iommu_table *tbl);
+       void (*free)(struct iommu_table *tbl);
+};
+
+/* These are used by VIO */
+extern struct iommu_table_ops iommu_table_lpar_multi_ops;
+extern struct iommu_table_ops iommu_table_pseries_ops;
+
 /*
  * IOMAP_MAX_ORDER defines the largest contiguous block
  * of dma space we can get.  IOMAP_MAX_ORDER = 13
@@ -64,6 +97,9 @@ struct iommu_pool {
 struct iommu_table {
        unsigned long  it_busno;     /* Bus number this table belongs to */
        unsigned long  it_size;      /* Size of iommu table in entries */
+       unsigned long  it_indirect_levels;
+       unsigned long  it_level_size;
+       unsigned long  it_allocated_size;
        unsigned long  it_offset;    /* Offset into global table */
        unsigned long  it_base;      /* mapped address of tce table */
        unsigned long  it_index;     /* which iommu table this is */
@@ -75,15 +111,16 @@ struct iommu_table {
        struct iommu_pool pools[IOMMU_NR_POOLS];
        unsigned long *it_map;       /* A simple allocation bitmap for now */
        unsigned long  it_page_shift;/* table iommu page size */
-#ifdef CONFIG_IOMMU_API
-       struct iommu_group *it_group;
-#endif
-       void (*set_bypass)(struct iommu_table *tbl, bool enable);
-#ifdef CONFIG_PPC_POWERNV
-       void           *data;
-#endif
+       struct list_head it_group_list;/* List of iommu_table_group_link */
+       unsigned long *it_userspace; /* userspace view of the table */
+       struct iommu_table_ops *it_ops;
 };
 
+#define IOMMU_TABLE_USERSPACE_ENTRY(tbl, entry) \
+               ((tbl)->it_userspace ? \
+                       &((tbl)->it_userspace[(entry) - (tbl)->it_offset]) : \
+                       NULL)
+
 /* Pure 2^n version of get_order */
 static inline __attribute_const__
 int get_iommu_order(unsigned long size, struct iommu_table *tbl)
@@ -112,14 +149,62 @@ extern void iommu_free_table(struct iommu_table *tbl, const char *node_name);
  */
 extern struct iommu_table *iommu_init_table(struct iommu_table * tbl,
                                            int nid);
+#define IOMMU_TABLE_GROUP_MAX_TABLES   2
+
+struct iommu_table_group;
+
+struct iommu_table_group_ops {
+       unsigned long (*get_table_size)(
+                       __u32 page_shift,
+                       __u64 window_size,
+                       __u32 levels);
+       long (*create_table)(struct iommu_table_group *table_group,
+                       int num,
+                       __u32 page_shift,
+                       __u64 window_size,
+                       __u32 levels,
+                       struct iommu_table **ptbl);
+       long (*set_window)(struct iommu_table_group *table_group,
+                       int num,
+                       struct iommu_table *tblnew);
+       long (*unset_window)(struct iommu_table_group *table_group,
+                       int num);
+       /* Switch ownership from platform code to external user (e.g. VFIO) */
+       void (*take_ownership)(struct iommu_table_group *table_group);
+       /* Switch ownership from external user (e.g. VFIO) back to core */
+       void (*release_ownership)(struct iommu_table_group *table_group);
+};
+
+struct iommu_table_group_link {
+       struct list_head next;
+       struct rcu_head rcu;
+       struct iommu_table_group *table_group;
+};
+
+struct iommu_table_group {
+       /* IOMMU properties */
+       __u32 tce32_start;
+       __u32 tce32_size;
+       __u64 pgsizes; /* Bitmap of supported page sizes */
+       __u32 max_dynamic_windows_supported;
+       __u32 max_levels;
+
+       struct iommu_group *group;
+       struct iommu_table *tables[IOMMU_TABLE_GROUP_MAX_TABLES];
+       struct iommu_table_group_ops *ops;
+};
+
 #ifdef CONFIG_IOMMU_API
-extern void iommu_register_group(struct iommu_table *tbl,
+
+extern void iommu_register_group(struct iommu_table_group *table_group,
                                 int pci_domain_number, unsigned long pe_num);
 extern int iommu_add_device(struct device *dev);
 extern void iommu_del_device(struct device *dev);
 extern int __init tce_iommu_bus_notifier_init(void);
+extern long iommu_tce_xchg(struct iommu_table *tbl, unsigned long entry,
+               unsigned long *hpa, enum dma_data_direction *direction);
 #else
-static inline void iommu_register_group(struct iommu_table *tbl,
+static inline void iommu_register_group(struct iommu_table_group *table_group,
                                        int pci_domain_number,
                                        unsigned long pe_num)
 {
@@ -140,13 +225,6 @@ static inline int __init tce_iommu_bus_notifier_init(void)
 }
 #endif /* !CONFIG_IOMMU_API */
 
-static inline void set_iommu_table_base_and_group(struct device *dev,
-                                                 void *base)
-{
-       set_iommu_table_base(dev, base);
-       iommu_add_device(dev);
-}
-
 extern int ppc_iommu_map_sg(struct device *dev, struct iommu_table *tbl,
                            struct scatterlist *sglist, int nelems,
                            unsigned long mask,
@@ -197,20 +275,13 @@ extern int iommu_tce_clear_param_check(struct iommu_table *tbl,
                unsigned long npages);
 extern int iommu_tce_put_param_check(struct iommu_table *tbl,
                unsigned long ioba, unsigned long tce);
-extern int iommu_tce_build(struct iommu_table *tbl, unsigned long entry,
-               unsigned long hwaddr, enum dma_data_direction direction);
-extern unsigned long iommu_clear_tce(struct iommu_table *tbl,
-               unsigned long entry);
-extern int iommu_clear_tces_and_put_pages(struct iommu_table *tbl,
-               unsigned long entry, unsigned long pages);
-extern int iommu_put_tce_user_mode(struct iommu_table *tbl,
-               unsigned long entry, unsigned long tce);
 
 extern void iommu_flush_tce(struct iommu_table *tbl);
 extern int iommu_take_ownership(struct iommu_table *tbl);
 extern void iommu_release_ownership(struct iommu_table *tbl);
 
 extern enum dma_data_direction iommu_tce_direction(unsigned long tce);
+extern unsigned long iommu_direction_to_tce_perm(enum dma_data_direction dir);
 
 #endif /* __KERNEL__ */
 #endif /* _ASM_IOMMU_H */
index 3536d12eb79831811670e6212a97ecb4d3e76178..2aa79c864e91520ed563efd312c657f89696adc1 100644 (file)
@@ -430,7 +430,7 @@ static inline void note_hpte_modification(struct kvm *kvm,
  */
 static inline struct kvm_memslots *kvm_memslots_raw(struct kvm *kvm)
 {
-       return rcu_dereference_raw_notrace(kvm->memslots);
+       return rcu_dereference_raw_notrace(kvm->memslots[0]);
 }
 
 extern void kvmppc_mmu_debugfs_init(struct kvm *kvm);
index a193a13cf08bf1dd40a51e393aeeab4456ce42b0..d91f65b28e322808d2d05c260ffa13614c01333b 100644 (file)
@@ -698,7 +698,7 @@ struct kvm_vcpu_arch {
 static inline void kvm_arch_hardware_disable(void) {}
 static inline void kvm_arch_hardware_unsetup(void) {}
 static inline void kvm_arch_sync_events(struct kvm *kvm) {}
-static inline void kvm_arch_memslots_updated(struct kvm *kvm) {}
+static inline void kvm_arch_memslots_updated(struct kvm *kvm, struct kvm_memslots *slots) {}
 static inline void kvm_arch_flush_shadow_all(struct kvm *kvm) {}
 static inline void kvm_arch_sched_in(struct kvm_vcpu *vcpu, int cpu) {}
 static inline void kvm_arch_exit(void) {}
index b8475daad88471006369ad99c25d6c51efbe7501..c6ef05bd0765dd17fe77af03b1a736a671ffc2c9 100644 (file)
@@ -182,10 +182,11 @@ extern int kvmppc_core_create_memslot(struct kvm *kvm,
                                      unsigned long npages);
 extern int kvmppc_core_prepare_memory_region(struct kvm *kvm,
                                struct kvm_memory_slot *memslot,
-                               struct kvm_userspace_memory_region *mem);
+                               const struct kvm_userspace_memory_region *mem);
 extern void kvmppc_core_commit_memory_region(struct kvm *kvm,
-                               struct kvm_userspace_memory_region *mem,
-                               const struct kvm_memory_slot *old);
+                               const struct kvm_userspace_memory_region *mem,
+                               const struct kvm_memory_slot *old,
+                               const struct kvm_memory_slot *new);
 extern int kvm_vm_ioctl_get_smmu_info(struct kvm *kvm,
                                      struct kvm_ppc_smmu_info *info);
 extern void kvmppc_core_flush_memslot(struct kvm *kvm,
@@ -243,10 +244,11 @@ struct kvmppc_ops {
        void (*flush_memslot)(struct kvm *kvm, struct kvm_memory_slot *memslot);
        int (*prepare_memory_region)(struct kvm *kvm,
                                     struct kvm_memory_slot *memslot,
-                                    struct kvm_userspace_memory_region *mem);
+                                    const struct kvm_userspace_memory_region *mem);
        void (*commit_memory_region)(struct kvm *kvm,
-                                    struct kvm_userspace_memory_region *mem,
-                                    const struct kvm_memory_slot *old);
+                                    const struct kvm_userspace_memory_region *mem,
+                                    const struct kvm_memory_slot *old,
+                                    const struct kvm_memory_slot *new);
        int (*unmap_hva)(struct kvm *kvm, unsigned long hva);
        int (*unmap_hva_range)(struct kvm *kvm, unsigned long start,
                           unsigned long end);
index ef8899432ae72dd0f91359cfba7e74d3c162b620..952579f5e79a93ecad26afeda4a5c4ef2d133250 100644 (file)
@@ -65,31 +65,6 @@ struct machdep_calls {
         * destroyed as well */
        void            (*hpte_clear_all)(void);
 
-       int             (*tce_build)(struct iommu_table *tbl,
-                                    long index,
-                                    long npages,
-                                    unsigned long uaddr,
-                                    enum dma_data_direction direction,
-                                    struct dma_attrs *attrs);
-       void            (*tce_free)(struct iommu_table *tbl,
-                                   long index,
-                                   long npages);
-       unsigned long   (*tce_get)(struct iommu_table *tbl,
-                                   long index);
-       void            (*tce_flush)(struct iommu_table *tbl);
-
-       /* _rm versions are for real mode use only */
-       int             (*tce_build_rm)(struct iommu_table *tbl,
-                                    long index,
-                                    long npages,
-                                    unsigned long uaddr,
-                                    enum dma_data_direction direction,
-                                    struct dma_attrs *attrs);
-       void            (*tce_free_rm)(struct iommu_table *tbl,
-                                   long index,
-                                   long npages);
-       void            (*tce_flush_rm)(struct iommu_table *tbl);
-
        void __iomem *  (*ioremap)(phys_addr_t addr, unsigned long size,
                                   unsigned long flags, void *caller);
        void            (*iounmap)(volatile void __iomem *token);
@@ -131,12 +106,6 @@ struct machdep_calls {
        /* To setup PHBs when using automatic OF platform driver for PCI */
        int             (*pci_setup_phb)(struct pci_controller *host);
 
-#ifdef CONFIG_PCI_MSI
-       int             (*setup_msi_irqs)(struct pci_dev *dev,
-                                         int nvec, int type);
-       void            (*teardown_msi_irqs)(struct pci_dev *dev);
-#endif
-
        void            (*restart)(char *cmd);
        void            (*halt)(void);
        void            (*panic)(char *str);
index 986b9e1e1044f62a9b66454ae71d9a69a3bbd366..f05500a29a60d6f23f4e23de99821995511ccae0 100644 (file)
 #define MI_Ks          0x80000000      /* Should not be set */
 #define MI_Kp          0x40000000      /* Should always be set */
 
+/*
+ * All pages' PP exec bits are set to 000, which means Execute for Supervisor
+ * and no Execute for User.
+ * Then we use the APG to say whether accesses are according to Page rules,
+ * "all Supervisor" rules (Exec for all) and "all User" rules (Exec for noone)
+ * Therefore, we define 4 APG groups. msb is _PAGE_EXEC, lsb is _PAGE_USER
+ * 0 (00) => Not User, no exec => 11 (all accesses performed as user)
+ * 1 (01) => User but no exec => 11 (all accesses performed as user)
+ * 2 (10) => Not User, exec => 01 (rights according to page definition)
+ * 3 (11) => User, exec => 00 (all accesses performed as supervisor)
+ */
+#define MI_APG_INIT    0xf4ffffff
+
 /* The effective page number register.  When read, contains the information
  * about the last instruction TLB miss.  When MI_RPN is written, bits in
  * this register are used to create the TLB entry.
 #define MD_Ks          0x80000000      /* Should not be set */
 #define MD_Kp          0x40000000      /* Should always be set */
 
+/*
+ * All pages' PP data bits are set to either 000 or 011, which means
+ * respectively RW for Supervisor and no access for User, or RO for
+ * Supervisor and no access for user.
+ * Then we use the APG to say whether accesses are according to Page rules or
+ * "all Supervisor" rules (Access to all)
+ * Therefore, we define 2 APG groups. lsb is _PAGE_USER
+ * 0 => No user => 01 (all accesses performed according to page definition)
+ * 1 => User => 00 (all accesses performed as supervisor
+ *                                 according to page definition)
+ */
+#define MD_APG_INIT    0x4fffffff
+
 /* The effective page number register.  When read, contains the information
  * about the last instruction TLB miss.  When MD_RPN is written, bits in
  * this register are used to create the TLB entry.
@@ -145,7 +171,14 @@ typedef struct {
 } mm_context_t;
 #endif /* !__ASSEMBLY__ */
 
+#if (PAGE_SHIFT == 12)
 #define mmu_virtual_psize      MMU_PAGE_4K
+#elif (PAGE_SHIFT == 14)
+#define mmu_virtual_psize      MMU_PAGE_16K
+#else
+#error "Unsupported PAGE_SIZE"
+#endif
+
 #define mmu_linear_psize       MMU_PAGE_8M
 
 #endif /* _ASM_POWERPC_MMU_8XX_H_ */
index 1da6a81ce541fad8f083f23e3509dda137928d0a..a82f5347540ae2c875733253a8639a089399fa3f 100644 (file)
@@ -536,6 +536,9 @@ typedef struct {
        /* for 4K PTE fragment support */
        void *pte_frag;
 #endif
+#ifdef CONFIG_SPAPR_TCE_IOMMU
+       struct list_head iommu_group_mem_list;
+#endif
 } mm_context_t;
 
 
index 73382eba02dccf2d8373bc860e4ebbe7fe7cac0a..3e5184210d9b984fca5aa57cfe3c70e6539d74bb 100644 (file)
  */
 extern int init_new_context(struct task_struct *tsk, struct mm_struct *mm);
 extern void destroy_context(struct mm_struct *mm);
+#ifdef CONFIG_SPAPR_TCE_IOMMU
+struct mm_iommu_table_group_mem_t;
+
+extern bool mm_iommu_preregistered(void);
+extern long mm_iommu_get(unsigned long ua, unsigned long entries,
+               struct mm_iommu_table_group_mem_t **pmem);
+extern long mm_iommu_put(struct mm_iommu_table_group_mem_t *mem);
+extern void mm_iommu_init(mm_context_t *ctx);
+extern void mm_iommu_cleanup(mm_context_t *ctx);
+extern struct mm_iommu_table_group_mem_t *mm_iommu_lookup(unsigned long ua,
+               unsigned long size);
+extern struct mm_iommu_table_group_mem_t *mm_iommu_find(unsigned long ua,
+               unsigned long entries);
+extern long mm_iommu_ua_to_hpa(struct mm_iommu_table_group_mem_t *mem,
+               unsigned long ua, unsigned long *hpa);
+extern long mm_iommu_mapped_inc(struct mm_iommu_table_group_mem_t *mem);
+extern void mm_iommu_mapped_dec(struct mm_iommu_table_group_mem_t *mem);
+#endif
 
 extern void switch_mmu_context(struct mm_struct *prev, struct mm_struct *next);
 extern void switch_slb(struct task_struct *tsk, struct mm_struct *mm);
index 0321a909e663bf1899e81154bfcff65b0febd7b1..e9e4c52f368543324acbbd992b9fc5cb14fd0f26 100644 (file)
 #define OPAL_FLASH_READ                                110
 #define OPAL_FLASH_WRITE                       111
 #define OPAL_FLASH_ERASE                       112
-#define OPAL_LAST                              112
+#define OPAL_PRD_MSG                           113
+#define OPAL_LAST                              113
 
 /* Device tree flags */
 
 #define OPAL_PM_WINKLE_ENABLED         0x00040000
 #define OPAL_PM_SLEEP_ENABLED_ER1      0x00080000 /* with workaround */
 
+/*
+ * OPAL_CONFIG_CPU_IDLE_STATE parameters
+ */
+#define OPAL_CONFIG_IDLE_FASTSLEEP     1
+#define OPAL_CONFIG_IDLE_UNDO          0
+#define OPAL_CONFIG_IDLE_APPLY         1
+
 #ifndef __ASSEMBLY__
 
 /* Other enums */
@@ -352,6 +360,7 @@ enum opal_msg_type {
        OPAL_MSG_SHUTDOWN,              /* params[0] = 1 reboot, 0 shutdown */
        OPAL_MSG_HMI_EVT,
        OPAL_MSG_DPO,
+       OPAL_MSG_PRD,
        OPAL_MSG_TYPE_MAX,
 };
 
@@ -674,6 +683,23 @@ typedef struct oppanel_line {
        __be64 line_len;
 } oppanel_line_t;
 
+enum opal_prd_msg_type {
+       OPAL_PRD_MSG_TYPE_INIT = 0,     /* HBRT --> OPAL */
+       OPAL_PRD_MSG_TYPE_FINI,         /* HBRT/kernel --> OPAL */
+       OPAL_PRD_MSG_TYPE_ATTN,         /* HBRT <-- OPAL */
+       OPAL_PRD_MSG_TYPE_ATTN_ACK,     /* HBRT --> OPAL */
+       OPAL_PRD_MSG_TYPE_OCC_ERROR,    /* HBRT <-- OPAL */
+       OPAL_PRD_MSG_TYPE_OCC_RESET,    /* HBRT <-- OPAL */
+};
+
+struct opal_prd_msg_header {
+       uint8_t         type;
+       uint8_t         pad[1];
+       __be16          size;
+};
+
+struct opal_prd_msg;
+
 /*
  * SG entries
  *
index 042af1abfc4dd02a5f0902bd41b7e67237bfdfbd..958e941c0cda886bb340fc670bec7e172a94d36b 100644 (file)
@@ -186,6 +186,7 @@ int64_t opal_handle_hmi(void);
 int64_t opal_register_dump_region(uint32_t id, uint64_t start, uint64_t end);
 int64_t opal_unregister_dump_region(uint32_t id);
 int64_t opal_slw_set_reg(uint64_t cpu_pir, uint64_t sprn, uint64_t val);
+int64_t opal_config_cpu_idle_state(uint64_t state, uint64_t flag);
 int64_t opal_pci_set_phb_cxl_mode(uint64_t phb_id, uint64_t mode, uint64_t pe_number);
 int64_t opal_ipmi_send(uint64_t interface, struct opal_ipmi_msg *msg,
                uint64_t msg_len);
@@ -193,6 +194,7 @@ int64_t opal_ipmi_recv(uint64_t interface, struct opal_ipmi_msg *msg,
                uint64_t *msg_len);
 int64_t opal_i2c_request(uint64_t async_token, uint32_t bus_id,
                         struct opal_i2c_request *oreq);
+int64_t opal_prd_msg(struct opal_prd_msg *msg);
 
 int64_t opal_flash_read(uint64_t id, uint64_t offset, uint64_t buf,
                uint64_t size, uint64_t token);
@@ -239,6 +241,10 @@ extern int opal_elog_init(void);
 extern void opal_platform_dump_init(void);
 extern void opal_sys_param_init(void);
 extern void opal_msglog_init(void);
+extern int opal_async_comp_init(void);
+extern int opal_sensor_init(void);
+extern int opal_hmi_handler_init(void);
+extern int opal_event_init(void);
 
 extern int opal_machine_check(struct pt_regs *regs);
 extern bool opal_mce_check_early_recovery(struct pt_regs *regs);
@@ -250,6 +256,8 @@ extern int opal_resync_timebase(void);
 
 extern void opal_lpc_init(void);
 
+extern int opal_event_request(unsigned int opal_event_nr);
+
 struct opal_sg_list *opal_vmalloc_to_sg_list(void *vmalloc_addr,
                                             unsigned long vmalloc_size);
 void opal_free_sg_list(struct opal_sg_list *sg);
index 69c059887a2c0def2a13f566c385c3eca97c0a60..71294a6e976e9c338a81ac69981d3d3bd9a62144 100644 (file)
@@ -278,9 +278,7 @@ extern long long virt_phys_offset;
 
 #ifndef __ASSEMBLY__
 
-#undef STRICT_MM_TYPECHECKS
-
-#ifdef STRICT_MM_TYPECHECKS
+#ifdef CONFIG_STRICT_MM_TYPECHECKS
 /* These are used to make use of C type-checking. */
 
 /* PTE level */
index 1811c44bf34bcb6564036a76fb5b7a63563edf83..712add5904454362ff145aa9391ffd8674dfe604 100644 (file)
@@ -27,9 +27,23 @@ struct pci_controller_ops {
         * allow assignment/enabling of the device. */
        bool            (*enable_device_hook)(struct pci_dev *);
 
+       void            (*disable_device)(struct pci_dev *);
+
+       void            (*release_device)(struct pci_dev *);
+
        /* Called during PCI resource reassignment */
        resource_size_t (*window_alignment)(struct pci_bus *, unsigned long type);
        void            (*reset_secondary_bus)(struct pci_dev *dev);
+
+#ifdef CONFIG_PCI_MSI
+       int             (*setup_msi_irqs)(struct pci_dev *dev,
+                                         int nvec, int type);
+       void            (*teardown_msi_irqs)(struct pci_dev *dev);
+#endif
+
+       int             (*dma_set_mask)(struct pci_dev *dev, u64 dma_mask);
+
+       void            (*shutdown)(struct pci_controller *);
 };
 
 /*
@@ -185,7 +199,7 @@ struct pci_dn {
 
        struct  pci_dn *parent;
        struct  pci_controller *phb;    /* for pci devices */
-       struct  iommu_table *iommu_table;       /* for phb's or bridges */
+       struct  iommu_table_group *table_group; /* for phb's or bridges */
        struct  device_node *node;      /* back-pointer to the device_node */
 
        int     pci_ext_config_space;   /* for pci devices */
index 4aef8d6609997f48b9d3fcf4a488b698b11d9e3b..99dc432b256ae1899a7b8238602fc1996af63a6d 100644 (file)
@@ -71,36 +71,6 @@ extern struct dma_map_ops *get_pci_dma_ops(void);
  */
 #define PCI_DISABLE_MWI
 
-#ifdef CONFIG_PCI
-static inline void pci_dma_burst_advice(struct pci_dev *pdev,
-                                       enum pci_dma_burst_strategy *strat,
-                                       unsigned long *strategy_parameter)
-{
-       unsigned long cacheline_size;
-       u8 byte;
-
-       pci_read_config_byte(pdev, PCI_CACHE_LINE_SIZE, &byte);
-       if (byte == 0)
-               cacheline_size = 1024;
-       else
-               cacheline_size = (int) byte * 4;
-
-       *strat = PCI_DMA_BURST_MULTIPLE;
-       *strategy_parameter = cacheline_size;
-}
-#endif
-
-#else /* 32-bit */
-
-#ifdef CONFIG_PCI
-static inline void pci_dma_burst_advice(struct pci_dev *pdev,
-                                       enum pci_dma_burst_strategy *strat,
-                                       unsigned long *strategy_parameter)
-{
-       *strat = PCI_DMA_BURST_INFINITY;
-       *strategy_parameter = ~0UL;
-}
-#endif
 #endif /* CONFIG_PPC64 */
 
 extern int pci_domain_nr(struct pci_bus *bus);
index 64b52b1cf5425dcaad0a9cc06ea77037675aa88f..9c326565d498fb1ba8d85e00f0bd20a2350c5a43 100644 (file)
@@ -170,24 +170,6 @@ static inline unsigned long pte_update(pte_t *p,
 #ifdef PTE_ATOMIC_UPDATES
        unsigned long old, tmp;
 
-#ifdef CONFIG_PPC_8xx
-       unsigned long tmp2;
-
-       __asm__ __volatile__("\
-1:     lwarx   %0,0,%4\n\
-       andc    %1,%0,%5\n\
-       or      %1,%1,%6\n\
-       /* 0x200 == Extended encoding, bit 22 */ \
-       /* Bit 22 has to be 1 when _PAGE_USER is unset and _PAGE_RO is set */ \
-       rlwimi  %1,%1,32-1,0x200\n /* get _PAGE_RO */ \
-       rlwinm  %3,%1,32-2,0x200\n /* get _PAGE_USER */ \
-       andc    %1,%1,%3\n\
-       stwcx.  %1,0,%4\n\
-       bne-    1b"
-       : "=&r" (old), "=&r" (tmp), "=m" (*p), "=&r" (tmp2)
-       : "r" (p), "r" (clr), "r" (set), "m" (*p)
-       : "cc" );
-#else /* CONFIG_PPC_8xx */
        __asm__ __volatile__("\
 1:     lwarx   %0,0,%3\n\
        andc    %1,%0,%4\n\
@@ -198,7 +180,6 @@ static inline unsigned long pte_update(pte_t *p,
        : "=&r" (old), "=&r" (tmp), "=m" (*p)
        : "r" (p), "r" (clr), "r" (set), "m" (*p)
        : "cc" );
-#endif /* CONFIG_PPC_8xx */
 #else /* PTE_ATOMIC_UPDATES */
        unsigned long old = pte_val(*p);
        *p = __pte((old & ~clr) | set);
index 43e6ad424c7fc30503db061360fbd1565811b17d..f890f7ce159323d8a3f35fca2f95a594ed0fa9e9 100644 (file)
  */
 #ifndef __real_pte
 
-#ifdef STRICT_MM_TYPECHECKS
+#ifdef CONFIG_STRICT_MM_TYPECHECKS
 #define __real_pte(e,p)                ((real_pte_t){(e)})
 #define __rpte_to_pte(r)       ((r).pte)
 #else
@@ -347,11 +347,27 @@ static inline void __ptep_set_access_flags(pte_t *ptep, pte_t entry)
        pr_err("%s:%d: bad pgd %08lx.\n", __FILE__, __LINE__, pgd_val(e))
 
 /* Encode and de-code a swap entry */
-#define __swp_type(entry)      (((entry).val >> 1) & 0x3f)
-#define __swp_offset(entry)    ((entry).val >> 8)
-#define __swp_entry(type, offset) ((swp_entry_t){((type)<< 1)|((offset)<<8)})
-#define __pte_to_swp_entry(pte)        ((swp_entry_t){pte_val(pte) >> PTE_RPN_SHIFT})
-#define __swp_entry_to_pte(x)  ((pte_t) { (x).val << PTE_RPN_SHIFT })
+#define MAX_SWAPFILES_CHECK() do { \
+       BUILD_BUG_ON(MAX_SWAPFILES_SHIFT > SWP_TYPE_BITS); \
+       /*                                                      \
+        * Don't have overlapping bits with _PAGE_HPTEFLAGS     \
+        * We filter HPTEFLAGS on set_pte.                      \
+        */                                                     \
+       BUILD_BUG_ON(_PAGE_HPTEFLAGS & (0x1f << _PAGE_BIT_SWAP_TYPE)); \
+       } while (0)
+/*
+ * on pte we don't need handle RADIX_TREE_EXCEPTIONAL_SHIFT;
+ */
+#define SWP_TYPE_BITS 5
+#define __swp_type(x)          (((x).val >> _PAGE_BIT_SWAP_TYPE) \
+                               & ((1UL << SWP_TYPE_BITS) - 1))
+#define __swp_offset(x)                ((x).val >> PTE_RPN_SHIFT)
+#define __swp_entry(type, offset)      ((swp_entry_t) { \
+                                       ((type) << _PAGE_BIT_SWAP_TYPE) \
+                                       | ((offset) << PTE_RPN_SHIFT) })
+
+#define __pte_to_swp_entry(pte)                ((swp_entry_t) { pte_val((pte)) })
+#define __swp_entry_to_pte(x)          __pte((x).val)
 
 void pgtable_cache_add(unsigned shift, void (*ctor)(void *));
 void pgtable_cache_init(void);
index f9b498292a5c1cde5eeec7db06ae53759fd6921c..6f77f71ee96445792a7263f1bf370aa73c2095cf 100644 (file)
@@ -11,7 +11,7 @@
 #define _ASM_PNV_PCI_H
 
 #include <linux/pci.h>
-#include <misc/cxl.h>
+#include <misc/cxl-base.h>
 
 int pnv_phb_to_cxl_mode(struct pci_dev *dev, uint64_t mode);
 int pnv_cxl_ioda_msi_setup(struct pci_dev *dev, unsigned int hwirq,
index 5c93f691b4955ce91a287aabf98ed45a7aafe870..8452335661a5d5fd896eba78c4e43b40a4532291 100644 (file)
 #define PPC_INST_DCBAL                 0x7c2005ec
 #define PPC_INST_DCBZL                 0x7c2007ec
 #define PPC_INST_ICBT                  0x7c00002c
+#define PPC_INST_ICSWX                 0x7c00032d
+#define PPC_INST_ICSWEPX               0x7c00076d
 #define PPC_INST_ISEL                  0x7c00001e
 #define PPC_INST_ISEL_MASK             0xfc00003e
 #define PPC_INST_LDARX                 0x7c0000a8
 #define MFTMR(tmr, r)          stringify_in_c(.long PPC_INST_MFTMR | \
                                               TMRN(tmr) | ___PPC_RT(r))
 
+/* Coprocessor instructions */
+#define PPC_ICSWX(s, a, b)     stringify_in_c(.long PPC_INST_ICSWX |   \
+                                              ___PPC_RS(s) |           \
+                                              ___PPC_RA(a) |           \
+                                              ___PPC_RB(b))
+#define PPC_ICSWEPX(s, a, b)   stringify_in_c(.long PPC_INST_ICSWEPX | \
+                                              ___PPC_RS(s) |           \
+                                              ___PPC_RA(a) |           \
+                                              ___PPC_RB(b))
+
+
 #endif /* _ASM_POWERPC_PPC_OPCODE_H */
index bf117d8fb45fe773bb6154578961dd031c921f28..28ded5d9b57961b7be7b9265cf1f334a5a79395e 100644 (file)
@@ -295,6 +295,15 @@ struct thread_struct {
 #endif
 #ifdef CONFIG_PPC64
        unsigned long   dscr;
+       /*
+        * This member element dscr_inherit indicates that the process
+        * has explicitly attempted and changed the DSCR register value
+        * for itself. Hence kernel wont use the default CPU DSCR value
+        * contained in the PACA structure anymore during process context
+        * switch. Once this variable is set, this behaviour will also be
+        * inherited to all the children of this process from that point
+        * onwards.
+        */
        int             dscr_inherit;
        unsigned long   ppr;    /* used to save/restore SMT priority */
 #endif
index 97bae64afdaabd70ee30768f3dc785410cba1aa0..a0e2ba9609760e4108ce9aca0a4fd255fa15b1d1 100644 (file)
 #define _PAGE_SPECIAL  0x0008  /* SW entry, forced to 0 by the TLB miss */
 #define _PAGE_DIRTY    0x0100  /* C: page changed */
 
-/* These 4 software bits must be masked out when the entry is loaded
- * into the TLB, 1 SW bit left(0x0080).
+/* These 4 software bits must be masked out when the L2 entry is loaded
+ * into the TLB.
  */
-#define _PAGE_GUARDED  0x0010  /* software: guarded access */
-#define _PAGE_ACCESSED 0x0020  /* software: page referenced */
-#define _PAGE_WRITETHRU        0x0040  /* software: caching is write through */
+#define _PAGE_GUARDED  0x0010  /* Copied to L1 G entry in DTLB */
+#define _PAGE_USER     0x0020  /* Copied to L1 APG lsb */
+#define _PAGE_EXEC     0x0040  /* Copied to L1 APG */
+#define _PAGE_WRITETHRU        0x0080  /* software: caching is write through */
+#define _PAGE_ACCESSED 0x0800  /* software: page referenced */
 
-/* Setting any bits in the nibble with the follow two controls will
- * require a TLB exception handler change.  It is assumed unused bits
- * are always zero.
- */
-#define _PAGE_RO       0x0400  /* lsb PP bits */
-#define _PAGE_USER     0x0800  /* msb PP bits */
-/* set when _PAGE_USER is unset and _PAGE_RO is set */
-#define _PAGE_KNLRO    0x0200
+#define _PAGE_RO       0x0600  /* Supervisor RO, User no access */
 
 #define _PMD_PRESENT   0x0001
 #define _PMD_BAD       0x0ff0
 #define _PMD_PAGE_MASK 0x000c
 #define _PMD_PAGE_8M   0x000c
 
-#define _PTE_NONE_MASK _PAGE_KNLRO
-
 /* Until my rework is finished, 8xx still needs atomic PTE updates */
 #define PTE_ATOMIC_UPDATES     1
 
 /* We need to add _PAGE_SHARED to kernel pages */
-#define _PAGE_KERNEL_RO        (_PAGE_SHARED | _PAGE_RO | _PAGE_KNLRO)
-#define _PAGE_KERNEL_ROX       (_PAGE_EXEC | _PAGE_RO | _PAGE_KNLRO)
+#define _PAGE_KERNEL_RO                (_PAGE_SHARED | _PAGE_RO)
+#define _PAGE_KERNEL_ROX       (_PAGE_SHARED | _PAGE_RO | _PAGE_EXEC)
+#define _PAGE_KERNEL_RW                (_PAGE_SHARED | _PAGE_DIRTY | _PAGE_RW | \
+                                _PAGE_HWWRITE)
+#define _PAGE_KERNEL_RWX       (_PAGE_SHARED | _PAGE_DIRTY | _PAGE_RW | \
+                                _PAGE_HWWRITE | _PAGE_EXEC)
 
 #endif /* __KERNEL__ */
 #endif /*  _ASM_POWERPC_PTE_8xx_H */
index 91a704952ca1a96234b9088c4493b6371bbbefcf..8d8473278d91c37e1bf5b742bbe20f8938a9ed9a 100644 (file)
@@ -11,6 +11,7 @@
 /* Architected bits */
 #define _PAGE_PRESENT  0x000001 /* software: pte contains a translation */
 #define _PAGE_SW1      0x000002
+#define _PAGE_BIT_SWAP_TYPE    2
 #define _PAGE_BAP_SR   0x000004
 #define _PAGE_BAP_UR   0x000008
 #define _PAGE_BAP_SW   0x000010
index c5a755ef7011ad18389ea9fca4db9236824e7343..b7c8d079c121e8fe6bea35eaad0eeeee206d4404 100644 (file)
@@ -85,10 +85,8 @@ extern unsigned long bad_call_to_PMD_PAGE_SIZE(void);
  * 64-bit PTEs
  */
 #if defined(CONFIG_PPC32) && defined(CONFIG_PTE_64BIT)
-#define PTE_RPN_MAX    (1ULL << (64 - PTE_RPN_SHIFT))
 #define PTE_RPN_MASK   (~((1ULL<<PTE_RPN_SHIFT)-1))
 #else
-#define PTE_RPN_MAX    (1UL << (32 - PTE_RPN_SHIFT))
 #define PTE_RPN_MASK   (~((1UL<<PTE_RPN_SHIFT)-1))
 #endif
 
index fc852f7e7b3a63f86e94f0cb43b62eb2b11fc3a3..ef612c160da7c8fb8d4b11ed8a6f67c270d2dcce 100644 (file)
@@ -16,6 +16,7 @@
  */
 #define _PAGE_PRESENT          0x0001 /* software: pte contains a translation */
 #define _PAGE_USER             0x0002 /* matches one of the PP bits */
+#define _PAGE_BIT_SWAP_TYPE    2
 #define _PAGE_EXEC             0x0004 /* No execute on POWER4 and newer (we invert) */
 #define _PAGE_GUARDED          0x0008
 /* We can derive Memory coherence from _PAGE_NO_CACHE */
index f1863a138b4a496d726bbfe791b2c4f034ea5973..71f2b3f02cf8848425b26d92e6bd6f650ce726f8 100644 (file)
@@ -358,7 +358,7 @@ SYSCALL_SPU(setns)
 COMPAT_SYS(process_vm_readv)
 COMPAT_SYS(process_vm_writev)
 SYSCALL(finit_module)
-SYSCALL(ni_syscall) /* sys_kcmp */
+SYSCALL(kcmp) /* sys_kcmp */
 SYSCALL_SPU(sched_setattr)
 SYSCALL_SPU(sched_getattr)
 SYSCALL_SPU(renameat2)
index c15da6073cb8b8e177c715fe81eb76081432d62e..8e86b48d03699047dda0f493a3955c8c05e34909 100644 (file)
@@ -144,6 +144,26 @@ TRACE_EVENT_FN(opal_exit,
 );
 #endif
 
+TRACE_EVENT(hash_fault,
+
+           TP_PROTO(unsigned long addr, unsigned long access, unsigned long trap),
+           TP_ARGS(addr, access, trap),
+           TP_STRUCT__entry(
+                   __field(unsigned long, addr)
+                   __field(unsigned long, access)
+                   __field(unsigned long, trap)
+                   ),
+
+           TP_fast_assign(
+                   __entry->addr = addr;
+                   __entry->access = access;
+                   __entry->trap = trap;
+                   ),
+
+           TP_printk("hash fault with addr 0x%lx and access = 0x%lx trap = 0x%lx",
+                     __entry->addr, __entry->access, __entry->trap)
+);
+
 #endif /* _TRACE_POWERPC_H */
 
 #undef TRACE_INCLUDE_PATH
index a0c071d24e0e5e744969bc2eeea473b210bde785..2a8ebae0936beb0f6b3ec46eafaf979f0d8ddedd 100644 (file)
@@ -265,7 +265,7 @@ do {                                                                \
 ({                                                             \
        long __gu_err;                                          \
        unsigned long __gu_val;                                 \
-       const __typeof__(*(ptr)) __user *__gu_addr = (ptr);     \
+       __typeof__(*(ptr)) __user *__gu_addr = (ptr);   \
        __chk_user_ptr(ptr);                                    \
        if (!is_kernel_addr((unsigned long)__gu_addr))          \
                might_fault();                                  \
@@ -279,7 +279,7 @@ do {                                                                \
 ({                                                             \
        long __gu_err;                                          \
        long long __gu_val;                                     \
-       const __typeof__(*(ptr)) __user *__gu_addr = (ptr);     \
+       __typeof__(*(ptr)) __user *__gu_addr = (ptr);   \
        __chk_user_ptr(ptr);                                    \
        if (!is_kernel_addr((unsigned long)__gu_addr))          \
                might_fault();                                  \
@@ -293,7 +293,7 @@ do {                                                                \
 ({                                                                     \
        long __gu_err = -EFAULT;                                        \
        unsigned long  __gu_val = 0;                                    \
-       const __typeof__(*(ptr)) __user *__gu_addr = (ptr);             \
+       __typeof__(*(ptr)) __user *__gu_addr = (ptr);           \
        might_fault();                                                  \
        if (access_ok(VERIFY_READ, __gu_addr, (size)))                  \
                __get_user_size(__gu_val, __gu_addr, (size), __gu_err); \
@@ -305,7 +305,7 @@ do {                                                                \
 ({                                                             \
        long __gu_err;                                          \
        unsigned long __gu_val;                                 \
-       const __typeof__(*(ptr)) __user *__gu_addr = (ptr);     \
+       __typeof__(*(ptr)) __user *__gu_addr = (ptr);   \
        __chk_user_ptr(ptr);                                    \
        __get_user_size(__gu_val, __gu_addr, (size), __gu_err); \
        (x) = (__force __typeof__(*(ptr)))__gu_val;                     \
index 79c4068be278fb38ce0033cf9065c5a596beb28b..f44a027818afe549d231b72cdffb9b4598f1443e 100644 (file)
@@ -18,6 +18,7 @@ header-y += kvm_para.h
 header-y += mman.h
 header-y += msgbuf.h
 header-y += nvram.h
+header-y += opal-prd.h
 header-y += param.h
 header-y += perf_event.h
 header-y += poll.h
index de2c0e4ee1aab1c13d0ac60d0d2300849665c48c..43686043e29734b47b183882417e309e617d1039 100644 (file)
@@ -42,5 +42,6 @@
 #define PPC_FEATURE2_ISEL              0x08000000
 #define PPC_FEATURE2_TAR               0x04000000
 #define PPC_FEATURE2_VEC_CRYPTO                0x02000000
+#define PPC_FEATURE2_HTM_NOSC          0x01000000
 
 #endif /* _UAPI__ASM_POWERPC_CPUTABLE_H */
diff --git a/arch/powerpc/include/uapi/asm/eeh.h b/arch/powerpc/include/uapi/asm/eeh.h
new file mode 100644 (file)
index 0000000..291b7d1
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ * Copyright IBM Corp. 2015
+ *
+ * Authors: Gavin Shan <gwshan@linux.vnet.ibm.com>
+ */
+
+#ifndef _ASM_POWERPC_EEH_H
+#define _ASM_POWERPC_EEH_H
+
+/* PE states */
+#define EEH_PE_STATE_NORMAL            0       /* Normal state         */
+#define EEH_PE_STATE_RESET             1       /* PE reset asserted    */
+#define EEH_PE_STATE_STOPPED_IO_DMA    2       /* Frozen PE            */
+#define EEH_PE_STATE_STOPPED_DMA       4       /* Stopped DMA only     */
+#define EEH_PE_STATE_UNAVAIL           5       /* Unavailable          */
+
+/* EEH error types and functions */
+#define EEH_ERR_TYPE_32                        0       /* 32-bits error        */
+#define EEH_ERR_TYPE_64                        1       /* 64-bits error        */
+#define EEH_ERR_FUNC_MIN               0
+#define EEH_ERR_FUNC_LD_MEM_ADDR       0       /* Memory load  */
+#define EEH_ERR_FUNC_LD_MEM_DATA       1
+#define EEH_ERR_FUNC_LD_IO_ADDR                2       /* IO load      */
+#define EEH_ERR_FUNC_LD_IO_DATA                3
+#define EEH_ERR_FUNC_LD_CFG_ADDR       4       /* Config load  */
+#define EEH_ERR_FUNC_LD_CFG_DATA       5
+#define EEH_ERR_FUNC_ST_MEM_ADDR       6       /* Memory store */
+#define EEH_ERR_FUNC_ST_MEM_DATA       7
+#define EEH_ERR_FUNC_ST_IO_ADDR                8       /* IO store     */
+#define EEH_ERR_FUNC_ST_IO_DATA                9
+#define EEH_ERR_FUNC_ST_CFG_ADDR       10      /* Config store */
+#define EEH_ERR_FUNC_ST_CFG_DATA       11
+#define EEH_ERR_FUNC_DMA_RD_ADDR       12      /* DMA read     */
+#define EEH_ERR_FUNC_DMA_RD_DATA       13
+#define EEH_ERR_FUNC_DMA_RD_MASTER     14
+#define EEH_ERR_FUNC_DMA_RD_TARGET     15
+#define EEH_ERR_FUNC_DMA_WR_ADDR       16      /* DMA write    */
+#define EEH_ERR_FUNC_DMA_WR_DATA       17
+#define EEH_ERR_FUNC_DMA_WR_MASTER     18
+#define EEH_ERR_FUNC_DMA_WR_TARGET     19
+#define EEH_ERR_FUNC_MAX               19
+
+#endif /* _ASM_POWERPC_EEH_H */
diff --git a/arch/powerpc/include/uapi/asm/opal-prd.h b/arch/powerpc/include/uapi/asm/opal-prd.h
new file mode 100644 (file)
index 0000000..319ff4a
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * OPAL Runtime Diagnostics interface driver
+ * Supported on POWERNV platform
+ *
+ * (C) Copyright IBM 2015
+ *
+ * Author: Vaidyanathan Srinivasan <svaidy at linux.vnet.ibm.com>
+ * Author: Jeremy Kerr <jk@ozlabs.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
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _UAPI_ASM_POWERPC_OPAL_PRD_H_
+#define _UAPI_ASM_POWERPC_OPAL_PRD_H_
+
+#include <linux/types.h>
+
+/**
+ * The version of the kernel interface of the PRD system. This describes the
+ * interface available for the /dev/opal-prd device. The actual PRD message
+ * layout and content is private to the firmware <--> userspace interface, so
+ * is not covered by this versioning.
+ *
+ * Future interface versions are backwards-compatible; if a later kernel
+ * version is encountered, functionality provided in earlier versions
+ * will work.
+ */
+#define OPAL_PRD_KERNEL_VERSION                1
+
+#define OPAL_PRD_GET_INFO              _IOR('o', 0x01, struct opal_prd_info)
+#define OPAL_PRD_SCOM_READ             _IOR('o', 0x02, struct opal_prd_scom)
+#define OPAL_PRD_SCOM_WRITE            _IOW('o', 0x03, struct opal_prd_scom)
+
+#ifndef __ASSEMBLY__
+
+struct opal_prd_info {
+       __u64   version;
+       __u64   reserved[3];
+};
+
+struct opal_prd_scom {
+       __u64   chip;
+       __u64   addr;
+       __u64   data;
+       __s64   rc;
+};
+
+#endif /* __ASSEMBLY__ */
+
+#endif /* _UAPI_ASM_POWERPC_OPAL_PRD_H */
index 5d836b7c1176242ae2c943c0bcc81cf6bcb031cf..5047659815a54ffd69037b245eebd9f6c64a679d 100644 (file)
@@ -11,7 +11,7 @@
 #define TM_CAUSE_RESCHED       0xde
 #define TM_CAUSE_TLBI          0xdc
 #define TM_CAUSE_FAC_UNAV      0xda
-#define TM_CAUSE_SYSCALL       0xd8  /* future use */
+#define TM_CAUSE_SYSCALL       0xd8
 #define TM_CAUSE_MISC          0xd6  /* future use */
 #define TM_CAUSE_SIGNAL                0xd4
 #define TM_CAUSE_ALIGNMENT     0xd2
index c1ebbdaac28fb85e37cb1fdf34c96e2e1175a977..87c7d1473488a95fc5956fa2f80bcef543b74b69 100644 (file)
@@ -33,11 +33,12 @@ obj-y                               := cputable.o ptrace.o syscalls.o \
                                   signal.o sysfs.o cacheinfo.o time.o \
                                   prom.o traps.o setup-common.o \
                                   udbg.o misc.o io.o dma.o \
-                                  misc_$(CONFIG_WORD_SIZE).o vdso32/ \
+                                  misc_$(CONFIG_WORD_SIZE).o \
                                   of_platform.o prom_parse.o
 obj-$(CONFIG_PPC64)            += setup_64.o sys_ppc32.o \
                                   signal_64.o ptrace32.o \
                                   paca.o nvram_64.o firmware.o
+obj-$(CONFIG_VDSO32)           += vdso32/
 obj-$(CONFIG_HAVE_HW_BREAKPOINT)       += hw_breakpoint.o
 obj-$(CONFIG_PPC_BOOK3S_64)    += cpu_setup_ppc970.o cpu_setup_pa6t.o
 obj-$(CONFIG_PPC_BOOK3S_64)    += cpu_setup_power.o
index 0034b6b3556a4f6b571ec60fdd55c02535c6ea97..98230579d99c74267c9dc2fd1ef0df887b52dd2d 100644 (file)
@@ -247,7 +247,7 @@ int main(void)
 #endif
        DEFINE(PACAHWCPUID, offsetof(struct paca_struct, hw_cpu_id));
        DEFINE(PACAKEXECSTATE, offsetof(struct paca_struct, kexec_state));
-       DEFINE(PACA_DSCR, offsetof(struct paca_struct, dscr_default));
+       DEFINE(PACA_DSCR_DEFAULT, offsetof(struct paca_struct, dscr_default));
        DEFINE(PACA_STARTTIME, offsetof(struct paca_struct, starttime));
        DEFINE(PACA_STARTTIME_USER, offsetof(struct paca_struct, starttime_user));
        DEFINE(PACA_USER_TIME, offsetof(struct paca_struct, user_time));
index 60262fdf35babd4909508b9a4d9a214f215ac03a..7d80bfdfb15eef4fb4abf8390d0724b688e8f6f8 100644 (file)
@@ -108,7 +108,9 @@ extern void __restore_cpu_e6500(void);
                                 PPC_FEATURE_TRUE_LE | \
                                 PPC_FEATURE_PSERIES_PERFMON_COMPAT)
 #define COMMON_USER2_POWER8    (PPC_FEATURE2_ARCH_2_07 | \
-                                PPC_FEATURE2_HTM_COMP | PPC_FEATURE2_DSCR | \
+                                PPC_FEATURE2_HTM_COMP | \
+                                PPC_FEATURE2_HTM_NOSC_COMP | \
+                                PPC_FEATURE2_DSCR | \
                                 PPC_FEATURE2_ISEL | PPC_FEATURE2_TAR | \
                                 PPC_FEATURE2_VEC_CRYPTO)
 #define COMMON_USER_PA6T       (COMMON_USER_PPC64 | PPC_FEATURE_PA6T |\
index 484b2d4462c10cd954aad0a5e9cb7776022b1db1..35e4dcc5dce362eb941fc18eabe39f797c90fba1 100644 (file)
@@ -248,6 +248,14 @@ int dma_set_mask(struct device *dev, u64 dma_mask)
 {
        if (ppc_md.dma_set_mask)
                return ppc_md.dma_set_mask(dev, dma_mask);
+
+       if (dev_is_pci(dev)) {
+               struct pci_dev *pdev = to_pci_dev(dev);
+               struct pci_controller *phb = pci_bus_to_host(pdev->bus);
+               if (phb->controller_ops.dma_set_mask)
+                       return phb->controller_ops.dma_set_mask(pdev, dma_mask);
+       }
+
        return __dma_set_mask(dev, dma_mask);
 }
 EXPORT_SYMBOL(dma_set_mask);
index 9ee61d15653d6ec46546b93ea72601072176e4b7..af9b597b10af65192368dbf5881a5fa7929cab4a 100644 (file)
@@ -144,8 +144,6 @@ struct eeh_stats {
 
 static struct eeh_stats eeh_stats;
 
-#define IS_BRIDGE(class_code) (((class_code)<<16) == PCI_BASE_CLASS_BRIDGE)
-
 static int __init eeh_setup(char *str)
 {
        if (!strcmp(str, "off"))
@@ -719,7 +717,7 @@ static void *eeh_restore_dev_state(void *data, void *userdata)
 
        /* The caller should restore state for the specified device */
        if (pdev != dev)
-               pci_save_state(pdev);
+               pci_restore_state(pdev);
 
        return NULL;
 }
@@ -1412,13 +1410,11 @@ static int dev_has_iommu_table(struct device *dev, void *data)
 {
        struct pci_dev *pdev = to_pci_dev(dev);
        struct pci_dev **ppdev = data;
-       struct iommu_table *tbl;
 
        if (!dev)
                return 0;
 
-       tbl = get_iommu_table_base(dev);
-       if (tbl && tbl->it_group) {
+       if (dev->iommu_group) {
                *ppdev = pdev;
                return 1;
        }
@@ -1647,6 +1643,41 @@ int eeh_pe_configure(struct eeh_pe *pe)
 }
 EXPORT_SYMBOL_GPL(eeh_pe_configure);
 
+/**
+ * eeh_pe_inject_err - Injecting the specified PCI error to the indicated PE
+ * @pe: the indicated PE
+ * @type: error type
+ * @function: error function
+ * @addr: address
+ * @mask: address mask
+ *
+ * The routine is called to inject the specified PCI error, which
+ * is determined by @type and @function, to the indicated PE for
+ * testing purpose.
+ */
+int eeh_pe_inject_err(struct eeh_pe *pe, int type, int func,
+                     unsigned long addr, unsigned long mask)
+{
+       /* Invalid PE ? */
+       if (!pe)
+               return -ENODEV;
+
+       /* Unsupported operation ? */
+       if (!eeh_ops || !eeh_ops->err_inject)
+               return -ENOENT;
+
+       /* Check on PCI error type */
+       if (type != EEH_ERR_TYPE_32 && type != EEH_ERR_TYPE_64)
+               return -EINVAL;
+
+       /* Check on PCI error function */
+       if (func < EEH_ERR_FUNC_MIN || func > EEH_ERR_FUNC_MAX)
+               return -EINVAL;
+
+       return eeh_ops->err_inject(pe, type, func, addr, mask);
+}
+EXPORT_SYMBOL_GPL(eeh_pe_inject_err);
+
 static int proc_eeh_show(struct seq_file *m, void *v)
 {
        if (!eeh_enabled()) {
index eeabeabea49c41cec4da2532b290db2fb1c0679b..a1e86e172e3cca3b24cf74ecc503248ddcd5e3d2 100644 (file)
  */
 struct pci_io_addr_range {
        struct rb_node rb_node;
-       unsigned long addr_lo;
-       unsigned long addr_hi;
+       resource_size_t addr_lo;
+       resource_size_t addr_hi;
        struct eeh_dev *edev;
        struct pci_dev *pcidev;
-       unsigned int flags;
+       unsigned long flags;
 };
 
 static struct pci_io_addr_cache {
@@ -125,8 +125,8 @@ static void eeh_addr_cache_print(struct pci_io_addr_cache *cache)
 
 /* Insert address range into the rb tree. */
 static struct pci_io_addr_range *
-eeh_addr_cache_insert(struct pci_dev *dev, unsigned long alo,
-                     unsigned long ahi, unsigned int flags)
+eeh_addr_cache_insert(struct pci_dev *dev, resource_size_t alo,
+                     resource_size_t ahi, unsigned long flags)
 {
        struct rb_node **p = &pci_io_addr_cache_root.rb_root.rb_node;
        struct rb_node *parent = NULL;
@@ -197,9 +197,9 @@ static void __eeh_addr_cache_insert_dev(struct pci_dev *dev)
 
        /* Walk resources on this device, poke them into the tree */
        for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) {
-               unsigned long start = pci_resource_start(dev,i);
-               unsigned long end = pci_resource_end(dev,i);
-               unsigned int flags = pci_resource_flags(dev,i);
+               resource_size_t start = pci_resource_start(dev,i);
+               resource_size_t end = pci_resource_end(dev,i);
+               unsigned long flags = pci_resource_flags(dev,i);
 
                /* We are interested only bus addresses, not dma or other stuff */
                if (0 == (flags & (IORESOURCE_IO | IORESOURCE_MEM)))
index 24768ff3cb7308345248ada7a70f60421c7aea46..89eb4bc34d3a8934a0a15c4d2c428f373e5eb0ba 100644 (file)
@@ -660,7 +660,7 @@ static void eeh_handle_normal_event(struct eeh_pe *pe)
        eeh_pe_dev_traverse(pe, eeh_report_error, &result);
 
        /* Get the current PCI slot state. This can take a long time,
-        * sometimes over 3 seconds for certain systems.
+        * sometimes over 300 seconds for certain systems.
         */
        rc = eeh_ops->wait_state(pe, MAX_WAIT_FOR_RECOVERY*1000);
        if (rc < 0 || rc == EEH_STATE_NOT_SUPPORT) {
index afbc20019c2efba2b81b7cd6298941753d9776b5..579e0f9a2d5700dd0cbbc3034f1dce09b2e49e70 100644 (file)
@@ -34,6 +34,7 @@
 #include <asm/ftrace.h>
 #include <asm/hw_irq.h>
 #include <asm/context_tracking.h>
+#include <asm/tm.h>
 
 /*
  * System calls.
@@ -51,6 +52,12 @@ exception_marker:
 
        .globl system_call_common
 system_call_common:
+#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
+BEGIN_FTR_SECTION
+       extrdi. r10, r12, 1, (63-MSR_TS_T_LG) /* transaction active? */
+       bne     tabort_syscall
+END_FTR_SECTION_IFSET(CPU_FTR_TM)
+#endif
        andi.   r10,r12,MSR_PR
        mr      r10,r1
        addi    r1,r1,-INT_FRAME_SIZE
@@ -311,6 +318,34 @@ syscall_exit_work:
        bl      do_syscall_trace_leave
        b       ret_from_except
 
+#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
+tabort_syscall:
+       /* Firstly we need to enable TM in the kernel */
+       mfmsr   r10
+       li      r13, 1
+       rldimi  r10, r13, MSR_TM_LG, 63-MSR_TM_LG
+       mtmsrd  r10, 0
+
+       /* tabort, this dooms the transaction, nothing else */
+       li      r13, (TM_CAUSE_SYSCALL|TM_CAUSE_PERSISTENT)
+       TABORT(R13)
+
+       /*
+        * Return directly to userspace. We have corrupted user register state,
+        * but userspace will never see that register state. Execution will
+        * resume after the tbegin of the aborted transaction with the
+        * checkpointed register state.
+        */
+       li      r13, MSR_RI
+       andc    r10, r10, r13
+       mtmsrd  r10, 1
+       mtspr   SPRN_SRR0, r11
+       mtspr   SPRN_SRR1, r12
+
+       rfid
+       b       .       /* prevent speculative execution */
+#endif
+
 /* Save non-volatile GPRs, if not already saved. */
 _GLOBAL(save_nvgprs)
        ld      r11,_TRAP(r1)
@@ -556,7 +591,7 @@ BEGIN_FTR_SECTION
        ld      r0,THREAD_DSCR(r4)
        cmpwi   r6,0
        bne     1f
-       ld      r0,PACA_DSCR(r13)
+       ld      r0,PACA_DSCR_DEFAULT(r13)
 1:
 BEGIN_FTR_SECTION_NESTED(70)
        mfspr   r8, SPRN_FSCR
index 9519e6bdc6d75c324334bf4f0f52dd6da9d9bbcc..0a0399c2af119c1c63efe094b1404f3250045769 100644 (file)
@@ -59,14 +59,13 @@ END_FTR_SECTION_IFSET(CPU_FTR_REAL_LE)                              \
 
 #if defined(CONFIG_RELOCATABLE)
        /*
-        * We can't branch directly; in the direct case we use LR
-        * and system_call_entry restores LR.  (We thus need to move
-        * LR to r10 in the RFID case too.)
+        * We can't branch directly so we do it via the CTR which
+        * is volatile across system calls.
         */
 #define SYSCALL_PSERIES_2_DIRECT                               \
        mflr    r10 ;                                           \
        ld      r12,PACAKBASE(r13) ;                            \
-       LOAD_HANDLER(r12, system_call_entry_direct) ;           \
+       LOAD_HANDLER(r12, system_call_entry) ;                  \
        mtctr   r12 ;                                           \
        mfspr   r12,SPRN_SRR1 ;                                 \
        /* Re-use of r13... No spare regs to do this */ \
@@ -80,7 +79,7 @@ END_FTR_SECTION_IFSET(CPU_FTR_REAL_LE)                                \
        mfspr   r12,SPRN_SRR1 ;                                 \
        li      r10,MSR_RI ;                                    \
        mtmsrd  r10,1 ;                 /* Set RI (EE=0) */     \
-       b       system_call_entry_direct ;
+       b       system_call_common ;
 #endif
 
 /*
@@ -969,13 +968,6 @@ hv_facility_unavailable_relon_trampoline:
 __end_interrupts:
 
        .align  7
-system_call_entry_direct:
-#if defined(CONFIG_RELOCATABLE)
-       /* The first level prologue may have used LR to get here, saving
-        * orig in r10.  To save hacking/ifdeffing common code, restore here.
-        */
-       mtlr    r10
-#endif
 system_call_entry:
        b       system_call_common
 
index 9b53fe139bf6f53c4227d713d1f969f3f0c718b8..78c1eba4c04a432ea233983e81d9da7a87071834 100644 (file)
        mtspr   spr, reg
 #endif
 
+/* Macro to test if an address is a kernel address */
+#if CONFIG_TASK_SIZE <= 0x80000000 && CONFIG_PAGE_OFFSET >= 0x80000000
+#define IS_KERNEL(tmp, addr)           \
+       andis.  tmp, addr, 0x8000       /* Address >= 0x80000000 */
+#define BRANCH_UNLESS_KERNEL(label)    beq     label
+#else
+#define IS_KERNEL(tmp, addr)           \
+       rlwinm  tmp, addr, 16, 16, 31;  \
+       cmpli   cr0, tmp, PAGE_OFFSET >> 16
+#define BRANCH_UNLESS_KERNEL(label)    blt     label
+#endif
+
+
 /*
  * Value for the bits that have fixed value in RPN entries.
  * Also used for tagging DAR for DTLBerror.
@@ -116,13 +129,13 @@ turn_on_mmu:
  */
 #define EXCEPTION_PROLOG       \
        EXCEPTION_PROLOG_0;     \
+       mfcr    r10;            \
        EXCEPTION_PROLOG_1;     \
        EXCEPTION_PROLOG_2
 
 #define EXCEPTION_PROLOG_0     \
        mtspr   SPRN_SPRG_SCRATCH0,r10; \
-       mtspr   SPRN_SPRG_SCRATCH1,r11; \
-       mfcr    r10
+       mtspr   SPRN_SPRG_SCRATCH1,r11
 
 #define EXCEPTION_PROLOG_1     \
        mfspr   r11,SPRN_SRR1;          /* check whether user or kernel */ \
@@ -162,7 +175,6 @@ turn_on_mmu:
  * Exception exit code.
  */
 #define EXCEPTION_EPILOG_0     \
-       mtcr    r10;            \
        mfspr   r10,SPRN_SPRG_SCRATCH0; \
        mfspr   r11,SPRN_SPRG_SCRATCH1
 
@@ -297,19 +309,22 @@ SystemCall:
  * We have to use the MD_xxx registers for the tablewalk because the
  * equivalent MI_xxx registers only perform the attribute functions.
  */
+
+#ifdef CONFIG_8xx_CPU15
+#define INVALIDATE_ADJACENT_PAGES_CPU15(tmp, addr)     \
+       addi    tmp, addr, PAGE_SIZE;   \
+       tlbie   tmp;                    \
+       addi    tmp, addr, -PAGE_SIZE;  \
+       tlbie   tmp
+#else
+#define INVALIDATE_ADJACENT_PAGES_CPU15(tmp, addr)
+#endif
+
 InstructionTLBMiss:
 #ifdef CONFIG_8xx_CPU6
-       mtspr   SPRN_DAR, r3
+       mtspr   SPRN_SPRG_SCRATCH2, r3
 #endif
        EXCEPTION_PROLOG_0
-       mtspr   SPRN_SPRG_SCRATCH2, r10
-       mfspr   r10, SPRN_SRR0  /* Get effective address of fault */
-#ifdef CONFIG_8xx_CPU15
-       addi    r11, r10, PAGE_SIZE
-       tlbie   r11
-       addi    r11, r10, -PAGE_SIZE
-       tlbie   r11
-#endif
 
        /* If we are faulting a kernel address, we have to use the
         * kernel page tables.
@@ -317,24 +332,34 @@ InstructionTLBMiss:
 #ifdef CONFIG_MODULES
        /* Only modules will cause ITLB Misses as we always
         * pin the first 8MB of kernel memory */
-       andis.  r11, r10, 0x8000        /* Address >= 0x80000000 */
-#endif
+       mfspr   r11, SPRN_SRR0  /* Get effective address of fault */
+       INVALIDATE_ADJACENT_PAGES_CPU15(r10, r11)
+       mfcr    r10
+       IS_KERNEL(r11, r11)
        mfspr   r11, SPRN_M_TW  /* Get level 1 table */
-#ifdef CONFIG_MODULES
-       beq     3f
+       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 */
 #endif
        /* Insert level 1 index */
        rlwimi  r11, r10, 32 - ((PAGE_SHIFT - 2) << 1), (PAGE_SHIFT - 2) << 1, 29
        lwz     r11, (swapper_pg_dir-PAGE_OFFSET)@l(r11)        /* Get the level 1 entry */
 
-       /* Load the MI_TWC with the attributes for this "segment." */
-       MTSPR_CPU6(SPRN_MI_TWC, r11, r3)        /* Set segment attributes */
-       rlwinm  r11, r11,0,0,19 /* Extract page descriptor page address */
        /* Extract level 2 index */
        rlwinm  r10, r10, 32 - (PAGE_SHIFT - 2), 32 - PAGE_SHIFT, 29
-       lwzx    r10, r10, r11   /* Get the pte */
+       rlwimi  r10, r11, 0, 0, 32 - PAGE_SHIFT - 1     /* Add level 2 base */
+       lwz     r10, 0(r10)     /* Get the pte */
+
+       /* Insert the APG into the TWC from the Linux PTE. */
+       rlwimi  r11, r10, 0, 25, 26
+       /* Load the MI_TWC with the attributes for this "segment." */
+       MTSPR_CPU6(SPRN_MI_TWC, r11, r3)        /* Set segment attributes */
 
 #ifdef CONFIG_SWAP
        rlwinm  r11, r10, 32-5, _PAGE_PRESENT
@@ -343,40 +368,41 @@ InstructionTLBMiss:
 #endif
        li      r11, RPN_PATTERN
        /* The Linux PTE won't go exactly into the MMU TLB.
-        * Software indicator bits 21 and 28 must be clear.
+        * Software indicator bits 20-23 and 28 must be clear.
         * Software indicator bits 24, 25, 26, and 27 must be
         * set.  All other Linux PTE bits control the behavior
         * of the MMU.
         */
-       rlwimi  r10, r11, 0, 0x07f8     /* Set 24-27, clear 21-23,28 */
+       rlwimi  r10, r11, 0, 0x0ff8     /* Set 24-27, clear 20-23,28 */
        MTSPR_CPU6(SPRN_MI_RPN, r10, r3)        /* Update TLB entry */
 
        /* Restore registers */
 #ifdef CONFIG_8xx_CPU6
-       mfspr   r3, SPRN_DAR
-       mtspr   SPRN_DAR, r11   /* Tag DAR */
+       mfspr   r3, SPRN_SPRG_SCRATCH2
 #endif
-       mfspr   r10, SPRN_SPRG_SCRATCH2
        EXCEPTION_EPILOG_0
        rfi
 
        . = 0x1200
 DataStoreTLBMiss:
 #ifdef CONFIG_8xx_CPU6
-       mtspr   SPRN_DAR, r3
+       mtspr   SPRN_SPRG_SCRATCH2, r3
 #endif
        EXCEPTION_PROLOG_0
-       mtspr   SPRN_SPRG_SCRATCH2, r10
-       mfspr   r10, SPRN_MD_EPN
+       mfcr    r10
 
        /* If we are faulting a kernel address, we have to use the
         * kernel page tables.
         */
-       andis.  r11, r10, 0x8000
+       mfspr   r11, SPRN_MD_EPN
+       IS_KERNEL(r11, r11)
        mfspr   r11, SPRN_M_TW  /* Get level 1 table */
-       beq     3f
+       BRANCH_UNLESS_KERNEL(3f)
        lis     r11, (swapper_pg_dir-PAGE_OFFSET)@ha
 3:
+       mtcr    r10
+       mfspr   r10, SPRN_MD_EPN
+
        /* Insert level 1 index */
        rlwimi  r11, r10, 32 - ((PAGE_SHIFT - 2) << 1), (PAGE_SHIFT - 2) << 1, 29
        lwz     r11, (swapper_pg_dir-PAGE_OFFSET)@l(r11)        /* Get the level 1 entry */
@@ -388,13 +414,13 @@ DataStoreTLBMiss:
        rlwimi  r10, r11, 0, 0, 32 - PAGE_SHIFT - 1     /* Add level 2 base */
        lwz     r10, 0(r10)     /* Get the pte */
 
-       /* Insert the Guarded flag into the TWC from the Linux PTE.
-        * It is bit 27 of both the Linux PTE and the TWC (at least
+       /* Insert the Guarded flag and APG into the TWC from the Linux PTE.
+        * It is bit 26-27 of both the Linux PTE and the TWC (at least
         * I got that right :-).  It will be better when we can put
         * this into the Linux pgd/pmd and load it in the operation
         * above.
         */
-       rlwimi  r11, r10, 0, 27, 27
+       rlwimi  r11, r10, 0, 26, 27
        /* Insert the WriteThru flag into the TWC from the Linux PTE.
         * It is bit 25 in the Linux PTE and bit 30 in the TWC
         */
@@ -423,14 +449,14 @@ DataStoreTLBMiss:
         */
        li      r11, RPN_PATTERN
        rlwimi  r10, r11, 0, 24, 28     /* Set 24-27, clear 28 */
+       rlwimi  r10, r11, 0, 20, 20     /* clear 20 */
        MTSPR_CPU6(SPRN_MD_RPN, r10, r3)        /* Update TLB entry */
 
        /* Restore registers */
 #ifdef CONFIG_8xx_CPU6
-       mfspr   r3, SPRN_DAR
+       mfspr   r3, SPRN_SPRG_SCRATCH2
 #endif
        mtspr   SPRN_DAR, r11   /* Tag DAR */
-       mfspr   r10, SPRN_SPRG_SCRATCH2
        EXCEPTION_EPILOG_0
        rfi
 
@@ -456,6 +482,7 @@ InstructionTLBError:
        . = 0x1400
 DataTLBError:
        EXCEPTION_PROLOG_0
+       mfcr    r10
 
        mfspr   r11, SPRN_DAR
        cmpwi   cr0, r11, RPN_PATTERN
@@ -503,9 +530,9 @@ FixupDAR:/* Entry point for dcbx workaround. */
        mtspr   SPRN_SPRG_SCRATCH2, r10
        /* fetch instruction from memory. */
        mfspr   r10, SPRN_SRR0
-       andis.  r11, r10, 0x8000        /* Address >= 0x80000000 */
+       IS_KERNEL(r11, r10)
        mfspr   r11, SPRN_M_TW  /* Get level 1 table */
-       beq     3f
+       BRANCH_UNLESS_KERNEL(3f)
        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
@@ -743,15 +770,20 @@ initial_mmu:
        ori     r8, r8, MI_EVALID       /* Mark it valid */
        mtspr   SPRN_MI_EPN, r8
        mtspr   SPRN_MD_EPN, r8
-       li      r8, MI_PS8MEG           /* Set 8M byte page */
+       li      r8, MI_PS8MEG | (2 << 5)        /* Set 8M byte page, APG 2 */
        ori     r8, r8, MI_SVALID       /* Make it valid */
        mtspr   SPRN_MI_TWC, r8
+       li      r8, MI_PS8MEG           /* Set 8M byte page, APG 0 */
+       ori     r8, r8, MI_SVALID       /* Make it valid */
        mtspr   SPRN_MD_TWC, r8
        li      r8, MI_BOOTINIT         /* Create RPN for address 0 */
        mtspr   SPRN_MI_RPN, r8         /* Store TLB entry */
        mtspr   SPRN_MD_RPN, r8
-       lis     r8, MI_Kp@h             /* Set the protection mode */
+       lis     r8, MI_APG_INIT@h       /* Set protection modes */
+       ori     r8, r8, MI_APG_INIT@l
        mtspr   SPRN_MI_AP, r8
+       lis     r8, MD_APG_INIT@h
+       ori     r8, r8, MD_APG_INIT@l
        mtspr   SPRN_MD_AP, r8
 
        /* Map another 8 MByte at the IMMR to get the processor
index 15448668988dd85a902b830fba8ef020c2557cb0..b9b6ef510be1ec366576a718423b6c319e8e4c06 100644 (file)
@@ -58,15 +58,6 @@ BEGIN_FTR_SECTION
        mtlr    r0
        lis     r3,HID0_NAP@h
 END_FTR_SECTION_IFSET(CPU_FTR_CAN_NAP)
-BEGIN_FTR_SECTION
-       msync
-       li      r7,L2CSR0_L2FL@l
-       mtspr   SPRN_L2CSR0,r7
-2:
-       mfspr   r7,SPRN_L2CSR0
-       andi.   r4,r7,L2CSR0_L2FL@l
-       bne     2b
-END_FTR_SECTION_IFSET(CPU_FTR_L2CSR|CPU_FTR_CAN_NAP)
 1:
        /* Go to NAP or DOZE now */
        mfspr   r4,SPRN_HID0
index b054f33ab1fbcdad3bff7fda332c9e55dcda1d2f..a8e3490b54e3b828e7821ab3152d03192cef150d 100644 (file)
@@ -322,11 +322,11 @@ static dma_addr_t iommu_alloc(struct device *dev, struct iommu_table *tbl,
        ret = entry << tbl->it_page_shift;      /* Set the return dma address */
 
        /* Put the TCEs in the HW table */
-       build_fail = ppc_md.tce_build(tbl, entry, npages,
+       build_fail = tbl->it_ops->set(tbl, entry, npages,
                                      (unsigned long)page &
                                      IOMMU_PAGE_MASK(tbl), direction, attrs);
 
-       /* ppc_md.tce_build() only returns non-zero for transient errors.
+       /* tbl->it_ops->set() only returns non-zero for transient errors.
         * Clean up the table bitmap in this case and return
         * DMA_ERROR_CODE. For all other errors the functionality is
         * not altered.
@@ -337,8 +337,8 @@ static dma_addr_t iommu_alloc(struct device *dev, struct iommu_table *tbl,
        }
 
        /* Flush/invalidate TLB caches if necessary */
-       if (ppc_md.tce_flush)
-               ppc_md.tce_flush(tbl);
+       if (tbl->it_ops->flush)
+               tbl->it_ops->flush(tbl);
 
        /* Make sure updates are seen by hardware */
        mb();
@@ -408,7 +408,7 @@ static void __iommu_free(struct iommu_table *tbl, dma_addr_t dma_addr,
        if (!iommu_free_check(tbl, dma_addr, npages))
                return;
 
-       ppc_md.tce_free(tbl, entry, npages);
+       tbl->it_ops->clear(tbl, entry, npages);
 
        spin_lock_irqsave(&(pool->lock), flags);
        bitmap_clear(tbl->it_map, free_entry, npages);
@@ -424,8 +424,8 @@ static void iommu_free(struct iommu_table *tbl, dma_addr_t dma_addr,
         * not do an mb() here on purpose, it is not needed on any of
         * the current platforms.
         */
-       if (ppc_md.tce_flush)
-               ppc_md.tce_flush(tbl);
+       if (tbl->it_ops->flush)
+               tbl->it_ops->flush(tbl);
 }
 
 int ppc_iommu_map_sg(struct device *dev, struct iommu_table *tbl,
@@ -495,7 +495,7 @@ int ppc_iommu_map_sg(struct device *dev, struct iommu_table *tbl,
                            npages, entry, dma_addr);
 
                /* Insert into HW table */
-               build_fail = ppc_md.tce_build(tbl, entry, npages,
+               build_fail = tbl->it_ops->set(tbl, entry, npages,
                                              vaddr & IOMMU_PAGE_MASK(tbl),
                                              direction, attrs);
                if(unlikely(build_fail))
@@ -534,8 +534,8 @@ int ppc_iommu_map_sg(struct device *dev, struct iommu_table *tbl,
        }
 
        /* Flush/invalidate TLB caches if necessary */
-       if (ppc_md.tce_flush)
-               ppc_md.tce_flush(tbl);
+       if (tbl->it_ops->flush)
+               tbl->it_ops->flush(tbl);
 
        DBG("mapped %d elements:\n", outcount);
 
@@ -600,8 +600,8 @@ void ppc_iommu_unmap_sg(struct iommu_table *tbl, struct scatterlist *sglist,
         * do not do an mb() here, the affected platforms do not need it
         * when freeing.
         */
-       if (ppc_md.tce_flush)
-               ppc_md.tce_flush(tbl);
+       if (tbl->it_ops->flush)
+               tbl->it_ops->flush(tbl);
 }
 
 static void iommu_table_clear(struct iommu_table *tbl)
@@ -613,17 +613,17 @@ static void iommu_table_clear(struct iommu_table *tbl)
         */
        if (!is_kdump_kernel() || is_fadump_active()) {
                /* Clear the table in case firmware left allocations in it */
-               ppc_md.tce_free(tbl, tbl->it_offset, tbl->it_size);
+               tbl->it_ops->clear(tbl, tbl->it_offset, tbl->it_size);
                return;
        }
 
 #ifdef CONFIG_CRASH_DUMP
-       if (ppc_md.tce_get) {
+       if (tbl->it_ops->get) {
                unsigned long index, tceval, tcecount = 0;
 
                /* Reserve the existing mappings left by the first kernel. */
                for (index = 0; index < tbl->it_size; index++) {
-                       tceval = ppc_md.tce_get(tbl, index + tbl->it_offset);
+                       tceval = tbl->it_ops->get(tbl, index + tbl->it_offset);
                        /*
                         * Freed TCE entry contains 0x7fffffffffffffff on JS20
                         */
@@ -657,6 +657,8 @@ struct iommu_table *iommu_init_table(struct iommu_table *tbl, int nid)
        unsigned int i;
        struct iommu_pool *p;
 
+       BUG_ON(!tbl->it_ops);
+
        /* number of bytes needed for the bitmap */
        sz = BITS_TO_LONGS(tbl->it_size) * sizeof(unsigned long);
 
@@ -713,9 +715,11 @@ void iommu_free_table(struct iommu_table *tbl, const char *node_name)
        unsigned long bitmap_sz;
        unsigned int order;
 
-       if (!tbl || !tbl->it_map) {
-               printk(KERN_ERR "%s: expected TCE map for %s\n", __func__,
-                               node_name);
+       if (!tbl)
+               return;
+
+       if (!tbl->it_map) {
+               kfree(tbl);
                return;
        }
 
@@ -726,13 +730,6 @@ void iommu_free_table(struct iommu_table *tbl, const char *node_name)
        if (tbl->it_offset == 0)
                clear_bit(0, tbl->it_map);
 
-#ifdef CONFIG_IOMMU_API
-       if (tbl->it_group) {
-               iommu_group_put(tbl->it_group);
-               BUG_ON(tbl->it_group);
-       }
-#endif
-
        /* verify that table contains no entries */
        if (!bitmap_empty(tbl->it_map, tbl->it_size))
                pr_warn("%s: Unexpected TCEs for %s\n", __func__, node_name);
@@ -871,17 +868,33 @@ void iommu_free_coherent(struct iommu_table *tbl, size_t size,
        }
 }
 
+unsigned long iommu_direction_to_tce_perm(enum dma_data_direction dir)
+{
+       switch (dir) {
+       case DMA_BIDIRECTIONAL:
+               return TCE_PCI_READ | TCE_PCI_WRITE;
+       case DMA_FROM_DEVICE:
+               return TCE_PCI_WRITE;
+       case DMA_TO_DEVICE:
+               return TCE_PCI_READ;
+       default:
+               return 0;
+       }
+}
+EXPORT_SYMBOL_GPL(iommu_direction_to_tce_perm);
+
 #ifdef CONFIG_IOMMU_API
 /*
  * SPAPR TCE API
  */
 static void group_release(void *iommu_data)
 {
-       struct iommu_table *tbl = iommu_data;
-       tbl->it_group = NULL;
+       struct iommu_table_group *table_group = iommu_data;
+
+       table_group->group = NULL;
 }
 
-void iommu_register_group(struct iommu_table *tbl,
+void iommu_register_group(struct iommu_table_group *table_group,
                int pci_domain_number, unsigned long pe_num)
 {
        struct iommu_group *grp;
@@ -893,8 +906,8 @@ void iommu_register_group(struct iommu_table *tbl,
                                PTR_ERR(grp));
                return;
        }
-       tbl->it_group = grp;
-       iommu_group_set_iommudata(grp, tbl, group_release);
+       table_group->group = grp;
+       iommu_group_set_iommudata(grp, table_group, group_release);
        name = kasprintf(GFP_KERNEL, "domain%d-pe%lx",
                        pci_domain_number, pe_num);
        if (!name)
@@ -919,8 +932,8 @@ EXPORT_SYMBOL_GPL(iommu_tce_direction);
 void iommu_flush_tce(struct iommu_table *tbl)
 {
        /* Flush/invalidate TLB caches if necessary */
-       if (ppc_md.tce_flush)
-               ppc_md.tce_flush(tbl);
+       if (tbl->it_ops->flush)
+               tbl->it_ops->flush(tbl);
 
        /* Make sure updates are seen by hardware */
        mb();
@@ -931,7 +944,7 @@ int iommu_tce_clear_param_check(struct iommu_table *tbl,
                unsigned long ioba, unsigned long tce_value,
                unsigned long npages)
 {
-       /* ppc_md.tce_free() does not support any value but 0 */
+       /* tbl->it_ops->clear() does not support any value but 0 */
        if (tce_value)
                return -EINVAL;
 
@@ -952,10 +965,7 @@ EXPORT_SYMBOL_GPL(iommu_tce_clear_param_check);
 int iommu_tce_put_param_check(struct iommu_table *tbl,
                unsigned long ioba, unsigned long tce)
 {
-       if (!(tce & (TCE_PCI_WRITE | TCE_PCI_READ)))
-               return -EINVAL;
-
-       if (tce & ~(IOMMU_PAGE_MASK(tbl) | TCE_PCI_WRITE | TCE_PCI_READ))
+       if (tce & ~IOMMU_PAGE_MASK(tbl))
                return -EINVAL;
 
        if (ioba & ~IOMMU_PAGE_MASK(tbl))
@@ -972,68 +982,16 @@ int iommu_tce_put_param_check(struct iommu_table *tbl,
 }
 EXPORT_SYMBOL_GPL(iommu_tce_put_param_check);
 
-unsigned long iommu_clear_tce(struct iommu_table *tbl, unsigned long entry)
-{
-       unsigned long oldtce;
-       struct iommu_pool *pool = get_pool(tbl, entry);
-
-       spin_lock(&(pool->lock));
-
-       oldtce = ppc_md.tce_get(tbl, entry);
-       if (oldtce & (TCE_PCI_WRITE | TCE_PCI_READ))
-               ppc_md.tce_free(tbl, entry, 1);
-       else
-               oldtce = 0;
-
-       spin_unlock(&(pool->lock));
-
-       return oldtce;
-}
-EXPORT_SYMBOL_GPL(iommu_clear_tce);
-
-int iommu_clear_tces_and_put_pages(struct iommu_table *tbl,
-               unsigned long entry, unsigned long pages)
-{
-       unsigned long oldtce;
-       struct page *page;
-
-       for ( ; pages; --pages, ++entry) {
-               oldtce = iommu_clear_tce(tbl, entry);
-               if (!oldtce)
-                       continue;
-
-               page = pfn_to_page(oldtce >> PAGE_SHIFT);
-               WARN_ON(!page);
-               if (page) {
-                       if (oldtce & TCE_PCI_WRITE)
-                               SetPageDirty(page);
-                       put_page(page);
-               }
-       }
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(iommu_clear_tces_and_put_pages);
-
-/*
- * hwaddr is a kernel virtual address here (0xc... bazillion),
- * tce_build converts it to a physical address.
- */
-int iommu_tce_build(struct iommu_table *tbl, unsigned long entry,
-               unsigned long hwaddr, enum dma_data_direction direction)
+long iommu_tce_xchg(struct iommu_table *tbl, unsigned long entry,
+               unsigned long *hpa, enum dma_data_direction *direction)
 {
-       int ret = -EBUSY;
-       unsigned long oldtce;
-       struct iommu_pool *pool = get_pool(tbl, entry);
-
-       spin_lock(&(pool->lock));
+       long ret;
 
-       oldtce = ppc_md.tce_get(tbl, entry);
-       /* Add new entry if it is not busy */
-       if (!(oldtce & (TCE_PCI_WRITE | TCE_PCI_READ)))
-               ret = ppc_md.tce_build(tbl, entry, 1, hwaddr, direction, NULL);
+       ret = tbl->it_ops->exchange(tbl, entry, hpa, direction);
 
-       spin_unlock(&(pool->lock));
+       if (!ret && ((*direction == DMA_FROM_DEVICE) ||
+                       (*direction == DMA_BIDIRECTIONAL)))
+               SetPageDirty(pfn_to_page(*hpa >> PAGE_SHIFT));
 
        /* if (unlikely(ret))
                pr_err("iommu_tce: %s failed on hwaddr=%lx ioba=%lx kva=%lx ret=%d\n",
@@ -1042,84 +1000,72 @@ int iommu_tce_build(struct iommu_table *tbl, unsigned long entry,
 
        return ret;
 }
-EXPORT_SYMBOL_GPL(iommu_tce_build);
+EXPORT_SYMBOL_GPL(iommu_tce_xchg);
 
-int iommu_put_tce_user_mode(struct iommu_table *tbl, unsigned long entry,
-               unsigned long tce)
+int iommu_take_ownership(struct iommu_table *tbl)
 {
-       int ret;
-       struct page *page = NULL;
-       unsigned long hwaddr, offset = tce & IOMMU_PAGE_MASK(tbl) & ~PAGE_MASK;
-       enum dma_data_direction direction = iommu_tce_direction(tce);
-
-       ret = get_user_pages_fast(tce & PAGE_MASK, 1,
-                       direction != DMA_TO_DEVICE, &page);
-       if (unlikely(ret != 1)) {
-               /* pr_err("iommu_tce: get_user_pages_fast failed tce=%lx ioba=%lx ret=%d\n",
-                               tce, entry << tbl->it_page_shift, ret); */
-               return -EFAULT;
-       }
-       hwaddr = (unsigned long) page_address(page) + offset;
-
-       ret = iommu_tce_build(tbl, entry, hwaddr, direction);
-       if (ret)
-               put_page(page);
-
-       if (ret < 0)
-               pr_err("iommu_tce: %s failed ioba=%lx, tce=%lx, ret=%d\n",
-                       __func__, entry << tbl->it_page_shift, tce, ret);
+       unsigned long flags, i, sz = (tbl->it_size + 7) >> 3;
+       int ret = 0;
 
-       return ret;
-}
-EXPORT_SYMBOL_GPL(iommu_put_tce_user_mode);
+       /*
+        * VFIO does not control TCE entries allocation and the guest
+        * can write new TCEs on top of existing ones so iommu_tce_build()
+        * must be able to release old pages. This functionality
+        * requires exchange() callback defined so if it is not
+        * implemented, we disallow taking ownership over the table.
+        */
+       if (!tbl->it_ops->exchange)
+               return -EINVAL;
 
-int iommu_take_ownership(struct iommu_table *tbl)
-{
-       unsigned long sz = (tbl->it_size + 7) >> 3;
+       spin_lock_irqsave(&tbl->large_pool.lock, flags);
+       for (i = 0; i < tbl->nr_pools; i++)
+               spin_lock(&tbl->pools[i].lock);
 
        if (tbl->it_offset == 0)
                clear_bit(0, tbl->it_map);
 
        if (!bitmap_empty(tbl->it_map, tbl->it_size)) {
                pr_err("iommu_tce: it_map is not empty");
-               return -EBUSY;
+               ret = -EBUSY;
+               /* Restore bit#0 set by iommu_init_table() */
+               if (tbl->it_offset == 0)
+                       set_bit(0, tbl->it_map);
+       } else {
+               memset(tbl->it_map, 0xff, sz);
        }
 
-       memset(tbl->it_map, 0xff, sz);
-       iommu_clear_tces_and_put_pages(tbl, tbl->it_offset, tbl->it_size);
+       for (i = 0; i < tbl->nr_pools; i++)
+               spin_unlock(&tbl->pools[i].lock);
+       spin_unlock_irqrestore(&tbl->large_pool.lock, flags);
 
-       /*
-        * Disable iommu bypass, otherwise the user can DMA to all of
-        * our physical memory via the bypass window instead of just
-        * the pages that has been explicitly mapped into the iommu
-        */
-       if (tbl->set_bypass)
-               tbl->set_bypass(tbl, false);
-
-       return 0;
+       return ret;
 }
 EXPORT_SYMBOL_GPL(iommu_take_ownership);
 
 void iommu_release_ownership(struct iommu_table *tbl)
 {
-       unsigned long sz = (tbl->it_size + 7) >> 3;
+       unsigned long flags, i, sz = (tbl->it_size + 7) >> 3;
+
+       spin_lock_irqsave(&tbl->large_pool.lock, flags);
+       for (i = 0; i < tbl->nr_pools; i++)
+               spin_lock(&tbl->pools[i].lock);
 
-       iommu_clear_tces_and_put_pages(tbl, tbl->it_offset, tbl->it_size);
        memset(tbl->it_map, 0, sz);
 
        /* Restore bit#0 set by iommu_init_table() */
        if (tbl->it_offset == 0)
                set_bit(0, tbl->it_map);
 
-       /* The kernel owns the device now, we can restore the iommu bypass */
-       if (tbl->set_bypass)
-               tbl->set_bypass(tbl, true);
+       for (i = 0; i < tbl->nr_pools; i++)
+               spin_unlock(&tbl->pools[i].lock);
+       spin_unlock_irqrestore(&tbl->large_pool.lock, flags);
 }
 EXPORT_SYMBOL_GPL(iommu_release_ownership);
 
 int iommu_add_device(struct device *dev)
 {
        struct iommu_table *tbl;
+       struct iommu_table_group_link *tgl;
 
        /*
         * The sysfs entries should be populated before
@@ -1137,15 +1083,22 @@ int iommu_add_device(struct device *dev)
        }
 
        tbl = get_iommu_table_base(dev);
-       if (!tbl || !tbl->it_group) {
+       if (!tbl) {
                pr_debug("%s: Skipping device %s with no tbl\n",
                         __func__, dev_name(dev));
                return 0;
        }
 
+       tgl = list_first_entry_or_null(&tbl->it_group_list,
+                       struct iommu_table_group_link, next);
+       if (!tgl) {
+               pr_debug("%s: Skipping device %s with no group\n",
+                        __func__, dev_name(dev));
+               return 0;
+       }
        pr_debug("%s: Adding %s to iommu group %d\n",
                 __func__, dev_name(dev),
-                iommu_group_id(tbl->it_group));
+                iommu_group_id(tgl->table_group->group));
 
        if (PAGE_SIZE < IOMMU_PAGE_SIZE(tbl)) {
                pr_err("%s: Invalid IOMMU page size %lx (%lx) on %s\n",
@@ -1154,7 +1107,7 @@ int iommu_add_device(struct device *dev)
                return -EINVAL;
        }
 
-       return iommu_group_add_device(tbl->it_group, dev);
+       return iommu_group_add_device(tgl->table_group->group, dev);
 }
 EXPORT_SYMBOL_GPL(iommu_add_device);
 
index 71bd161640cfc8d101e03117582229823d365676..dab616a33b8dbe283aa46c05b5492af69190650f 100644 (file)
 
 int arch_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
 {
-       if (!ppc_md.setup_msi_irqs || !ppc_md.teardown_msi_irqs) {
+       struct pci_controller *phb = pci_bus_to_host(dev->bus);
+
+       if (!phb->controller_ops.setup_msi_irqs ||
+           !phb->controller_ops.teardown_msi_irqs) {
                pr_debug("msi: Platform doesn't provide MSI callbacks.\n");
                return -ENOSYS;
        }
@@ -24,10 +27,12 @@ int arch_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
        if (type == PCI_CAP_ID_MSI && nvec > 1)
                return 1;
 
-       return ppc_md.setup_msi_irqs(dev, nvec, type);
+       return phb->controller_ops.setup_msi_irqs(dev, nvec, type);
 }
 
 void arch_teardown_msi_irqs(struct pci_dev *dev)
 {
-       ppc_md.teardown_msi_irqs(dev);
+       struct pci_controller *phb = pci_bus_to_host(dev->bus);
+
+       phb->controller_ops.teardown_msi_irqs(dev);
 }
index 0d054068a21d5849bc949320bfcc5835ac92dfdb..b9de34d44fcb877c388bcb54c98cc82bfb2ac3f2 100644 (file)
@@ -89,6 +89,7 @@ struct pci_controller *pcibios_alloc_controller(struct device_node *dev)
 #endif
        return phb;
 }
+EXPORT_SYMBOL_GPL(pcibios_alloc_controller);
 
 void pcibios_free_controller(struct pci_controller *phb)
 {
@@ -1447,6 +1448,7 @@ void pcibios_claim_one_bus(struct pci_bus *bus)
        list_for_each_entry(child_bus, &bus->children, node)
                pcibios_claim_one_bus(child_bus);
 }
+EXPORT_SYMBOL_GPL(pcibios_claim_one_bus);
 
 
 /* pcibios_finish_adding_to_bus
@@ -1488,6 +1490,14 @@ int pcibios_enable_device(struct pci_dev *dev, int mask)
        return pci_enable_resources(dev, mask);
 }
 
+void pcibios_disable_device(struct pci_dev *dev)
+{
+       struct pci_controller *phb = pci_bus_to_host(dev->bus);
+
+       if (phb->controller_ops.disable_device)
+               phb->controller_ops.disable_device(dev);
+}
+
 resource_size_t pcibios_io_space_offset(struct pci_controller *hose)
 {
        return (unsigned long) hose->io_base_virt - _IO_BASE;
@@ -1680,6 +1690,7 @@ void pcibios_scan_phb(struct pci_controller *hose)
                        pcie_bus_configure_settings(child);
        }
 }
+EXPORT_SYMBOL_GPL(pcibios_scan_phb);
 
 static void fixup_hide_host_resource_fsl(struct pci_dev *dev)
 {
index 7ed85a69a9c29b956d232596ba089cf610e9e1d2..7f9ed0c1f6b93d88dc4b3d8e815b7e1c099b1f82 100644 (file)
  */
 void pcibios_release_device(struct pci_dev *dev)
 {
+       struct pci_controller *phb = pci_bus_to_host(dev->bus);
+
        eeh_remove_device(dev);
+
+       if (phb->controller_ops.release_device)
+               phb->controller_ops.release_device(dev);
 }
 
 /**
index febb50dd53285d8cbee941e5895bcbcb3bc77b8f..8005e18d1b40381f6b815890b0d58c99a382b4e8 100644 (file)
@@ -1112,7 +1112,6 @@ static void setup_ksp_vsid(struct task_struct *p, unsigned long sp)
 /*
  * Copy a thread..
  */
-extern unsigned long dscr_default; /* defined in arch/powerpc/kernel/sysfs.c */
 
 /*
  * Copy architecture-specific thread state
index 308c5e15676b9160141ddcd86939c82f91029f3f..8b888b12a4752a907bfbd5f34afbf0b80f870a98 100644 (file)
@@ -46,7 +46,6 @@
 #include <asm/mmu.h>
 #include <asm/paca.h>
 #include <asm/pgtable.h>
-#include <asm/pci.h>
 #include <asm/iommu.h>
 #include <asm/btext.h>
 #include <asm/sections.h>
@@ -573,6 +572,7 @@ static void __init early_reserve_mem_dt(void)
        int len;
        const __be32 *prop;
 
+       early_init_fdt_reserve_self();
        early_init_fdt_scan_reserved_mem();
 
        dt_root = of_get_flat_dt_root();
@@ -800,6 +800,7 @@ int of_get_ibm_chip_id(struct device_node *np)
        }
        return -1;
 }
+EXPORT_SYMBOL(of_get_ibm_chip_id);
 
 /**
  * cpu_to_chip_id - Return the cpus chip-id
index fd1fe4c3759908ca0a06e17223cf444f0ca12f86..fcca8077e6a29c995b226d64183c176a1f76a78d 100644 (file)
@@ -37,7 +37,6 @@
 #include <asm/smp.h>
 #include <asm/mmu.h>
 #include <asm/pgtable.h>
-#include <asm/pci.h>
 #include <asm/iommu.h>
 #include <asm/btext.h>
 #include <asm/sections.h>
index c69671c03c3b5b7dc9b3e1f70726abba12c5c4b1..bdcbb716f4d66845e56b619e958e85c3bde318fd 100644 (file)
@@ -523,7 +523,8 @@ void __init setup_system(void)
        smp_release_cpus();
 #endif
 
-       pr_info("Starting Linux PPC64 %s\n", init_utsname()->version);
+       pr_info("Starting Linux %s %s\n", init_utsname()->machine,
+                init_utsname()->version);
 
        pr_info("-----------------------------------------------------\n");
        pr_info("ppc64_pft_size    = 0x%llx\n", ppc64_pft_size);
@@ -685,6 +686,9 @@ void __init setup_arch(char **cmdline_p)
        init_mm.brk = klimit;
 #ifdef CONFIG_PPC_64K_PAGES
        init_mm.context.pte_frag = NULL;
+#endif
+#ifdef CONFIG_SPAPR_TCE_IOMMU
+       mm_iommu_init(&init_mm.context);
 #endif
        irqstack_early_init();
        exc_lvl_early_init();
index fa1fd8a0c867f25611dfb6726e64cfd16ab39447..692873bff3341510730de0ec7e7d8c432ded989e 100644 (file)
@@ -496,13 +496,34 @@ static DEVICE_ATTR(spurr, 0400, show_spurr, NULL);
 static DEVICE_ATTR(purr, 0400, show_purr, store_purr);
 static DEVICE_ATTR(pir, 0400, show_pir, NULL);
 
+/*
+ * This is the system wide DSCR register default value. Any
+ * change to this default value through the sysfs interface
+ * will update all per cpu DSCR default values across the
+ * system stored in their respective PACA structures.
+ */
 static unsigned long dscr_default;
 
+/**
+ * read_dscr() - Fetch the cpu specific DSCR default
+ * @val:       Returned cpu specific DSCR default value
+ *
+ * This function returns the per cpu DSCR default value
+ * for any cpu which is contained in it's PACA structure.
+ */
 static void read_dscr(void *val)
 {
        *(unsigned long *)val = get_paca()->dscr_default;
 }
 
+
+/**
+ * write_dscr() - Update the cpu specific DSCR default
+ * @val:       New cpu specific DSCR default value to update
+ *
+ * This function updates the per cpu DSCR default value
+ * for any cpu which is contained in it's PACA structure.
+ */
 static void write_dscr(void *val)
 {
        get_paca()->dscr_default = *(unsigned long *)val;
@@ -520,12 +541,29 @@ static void add_write_permission_dev_attr(struct device_attribute *attr)
        attr->attr.mode |= 0200;
 }
 
+/**
+ * show_dscr_default() - Fetch the system wide DSCR default
+ * @dev:       Device structure
+ * @attr:      Device attribute structure
+ * @buf:       Interface buffer
+ *
+ * This function returns the system wide DSCR default value.
+ */
 static ssize_t show_dscr_default(struct device *dev,
                struct device_attribute *attr, char *buf)
 {
        return sprintf(buf, "%lx\n", dscr_default);
 }
 
+/**
+ * store_dscr_default() - Update the system wide DSCR default
+ * @dev:       Device structure
+ * @attr:      Device attribute structure
+ * @buf:       Interface buffer
+ * @count:     Size of the update
+ *
+ * This function updates the system wide DSCR default value.
+ */
 static ssize_t __used store_dscr_default(struct device *dev,
                struct device_attribute *attr, const char *buf,
                size_t count)
index 5754b226da7e5a2691ef27dc1d31b3cb0e519f72..bf8f34a5867088d1a2346d17b89e2daaabe365ab 100644 (file)
@@ -293,7 +293,7 @@ dont_backup_fp:
        ld      r2, STK_GOT(r1)
 
        /* Load CPU's default DSCR */
-       ld      r0, PACA_DSCR(r13)
+       ld      r0, PACA_DSCR_DEFAULT(r13)
        mtspr   SPRN_DSCR, r0
 
        blr
@@ -473,7 +473,7 @@ restore_gprs:
        ld      r2, STK_GOT(r1)
 
        /* Load CPU's default DSCR */
-       ld      r0, PACA_DSCR(r13)
+       ld      r0, PACA_DSCR_DEFAULT(r13)
        mtspr   SPRN_DSCR, r0
 
        blr
index 19e4744b6eba2a10dbc41d118e2de83021e3aed6..6530f1b8874dac16dc7f71480b5d748d0ccc268d 100644 (file)
@@ -1377,6 +1377,7 @@ void facility_unavailable_exception(struct pt_regs *regs)
        };
        char *facility = "unknown";
        u64 value;
+       u32 instword, rd;
        u8 status;
        bool hv;
 
@@ -1388,12 +1389,46 @@ void facility_unavailable_exception(struct pt_regs *regs)
 
        status = value >> 56;
        if (status == FSCR_DSCR_LG) {
-               /* User is acessing the DSCR.  Set the inherit bit and allow
-                * the user to set it directly in future by setting via the
-                * FSCR DSCR bit.  We always leave HFSCR DSCR set.
+               /*
+                * User is accessing the DSCR register using the problem
+                * state only SPR number (0x03) either through a mfspr or
+                * a mtspr instruction. If it is a write attempt through
+                * a mtspr, then we set the inherit bit. This also allows
+                * the user to write or read the register directly in the
+                * future by setting via the FSCR DSCR bit. But in case it
+                * is a read DSCR attempt through a mfspr instruction, we
+                * just emulate the instruction instead. This code path will
+                * always emulate all the mfspr instructions till the user
+                * has attempted atleast one mtspr instruction. This way it
+                * preserves the same behaviour when the user is accessing
+                * the DSCR through privilege level only SPR number (0x11)
+                * which is emulated through illegal instruction exception.
+                * We always leave HFSCR DSCR set.
                 */
-               current->thread.dscr_inherit = 1;
-               mtspr(SPRN_FSCR, value | FSCR_DSCR);
+               if (get_user(instword, (u32 __user *)(regs->nip))) {
+                       pr_err("Failed to fetch the user instruction\n");
+                       return;
+               }
+
+               /* Write into DSCR (mtspr 0x03, RS) */
+               if ((instword & PPC_INST_MTSPR_DSCR_USER_MASK)
+                               == PPC_INST_MTSPR_DSCR_USER) {
+                       rd = (instword >> 21) & 0x1f;
+                       current->thread.dscr = regs->gpr[rd];
+                       current->thread.dscr_inherit = 1;
+                       mtspr(SPRN_FSCR, value | FSCR_DSCR);
+               }
+
+               /* Read from DSCR (mfspr RT, 0x03) */
+               if ((instword & PPC_INST_MFSPR_DSCR_USER_MASK)
+                               == PPC_INST_MFSPR_DSCR_USER) {
+                       if (emulate_instruction(regs)) {
+                               pr_err("DSCR based mfspr emulation failed\n");
+                               return;
+                       }
+                       regs->nip += 4;
+                       emulate_single_step(regs);
+               }
                return;
        }
 
index 305eb0d9b76882d44e9f72805c951e1fa8c68b05..b457bfa2843603f9c920236372effe6304da3bed 100644 (file)
 /* The alignment of the vDSO */
 #define VDSO_ALIGNMENT (1 << 16)
 
-extern char vdso32_start, vdso32_end;
-static void *vdso32_kbase = &vdso32_start;
 static unsigned int vdso32_pages;
+static void *vdso32_kbase;
 static struct page **vdso32_pagelist;
 unsigned long vdso32_sigtramp;
 unsigned long vdso32_rt_sigtramp;
 
+#ifdef CONFIG_VDSO32
+extern char vdso32_start, vdso32_end;
+#endif
+
 #ifdef CONFIG_PPC64
 extern char vdso64_start, vdso64_end;
 static void *vdso64_kbase = &vdso64_start;
@@ -140,50 +143,6 @@ struct lib64_elfinfo
 };
 
 
-#ifdef __DEBUG
-static void dump_one_vdso_page(struct page *pg, struct page *upg)
-{
-       printk("kpg: %p (c:%d,f:%08lx)", __va(page_to_pfn(pg) << PAGE_SHIFT),
-              page_count(pg),
-              pg->flags);
-       if (upg && !IS_ERR(upg) /* && pg != upg*/) {
-               printk(" upg: %p (c:%d,f:%08lx)", __va(page_to_pfn(upg)
-                                                      << PAGE_SHIFT),
-                      page_count(upg),
-                      upg->flags);
-       }
-       printk("\n");
-}
-
-static void dump_vdso_pages(struct vm_area_struct * vma)
-{
-       int i;
-
-       if (!vma || is_32bit_task()) {
-               printk("vDSO32 @ %016lx:\n", (unsigned long)vdso32_kbase);
-               for (i=0; i<vdso32_pages; i++) {
-                       struct page *pg = virt_to_page(vdso32_kbase +
-                                                      i*PAGE_SIZE);
-                       struct page *upg = (vma && vma->vm_mm) ?
-                               follow_page(vma, vma->vm_start + i*PAGE_SIZE, 0)
-                               : NULL;
-                       dump_one_vdso_page(pg, upg);
-               }
-       }
-       if (!vma || !is_32bit_task()) {
-               printk("vDSO64 @ %016lx:\n", (unsigned long)vdso64_kbase);
-               for (i=0; i<vdso64_pages; i++) {
-                       struct page *pg = virt_to_page(vdso64_kbase +
-                                                      i*PAGE_SIZE);
-                       struct page *upg = (vma && vma->vm_mm) ?
-                               follow_page(vma, vma->vm_start + i*PAGE_SIZE, 0)
-                               : NULL;
-                       dump_one_vdso_page(pg, upg);
-               }
-       }
-}
-#endif /* DEBUG */
-
 /*
  * This is called from binfmt_elf, we create the special vma for the
  * vDSO and insert it into the mm struct tree
@@ -292,6 +251,7 @@ const char *arch_vma_name(struct vm_area_struct *vma)
 
 
 
+#ifdef CONFIG_VDSO32
 static void * __init find_section32(Elf32_Ehdr *ehdr, const char *secname,
                                  unsigned long *size)
 {
@@ -379,6 +339,20 @@ static int __init vdso_do_func_patch32(struct lib32_elfinfo *v32,
 
        return 0;
 }
+#else /* !CONFIG_VDSO32 */
+static unsigned long __init find_function32(struct lib32_elfinfo *lib,
+                                           const char *symname)
+{
+       return 0;
+}
+
+static int __init vdso_do_func_patch32(struct lib32_elfinfo *v32,
+                                      struct lib64_elfinfo *v64,
+                                      const char *orig, const char *fix)
+{
+       return 0;
+}
+#endif /* CONFIG_VDSO32 */
 
 
 #ifdef CONFIG_PPC64
@@ -489,6 +463,7 @@ static __init int vdso_do_find_sections(struct lib32_elfinfo *v32,
         * Locate symbol tables & text section
         */
 
+#ifdef CONFIG_VDSO32
        v32->dynsym = find_section32(v32->hdr, ".dynsym", &v32->dynsymsize);
        v32->dynstr = find_section32(v32->hdr, ".dynstr", NULL);
        if (v32->dynsym == NULL || v32->dynstr == NULL) {
@@ -501,6 +476,7 @@ static __init int vdso_do_find_sections(struct lib32_elfinfo *v32,
                return -1;
        }
        v32->text = sect - vdso32_kbase;
+#endif
 
 #ifdef CONFIG_PPC64
        v64->dynsym = find_section64(v64->hdr, ".dynsym", &v64->dynsymsize);
@@ -537,7 +513,9 @@ static __init void vdso_setup_trampolines(struct lib32_elfinfo *v32,
 static __init int vdso_fixup_datapage(struct lib32_elfinfo *v32,
                                       struct lib64_elfinfo *v64)
 {
+#ifdef CONFIG_VDSO32
        Elf32_Sym *sym32;
+#endif
 #ifdef CONFIG_PPC64
        Elf64_Sym *sym64;
 
@@ -552,6 +530,7 @@ static __init int vdso_fixup_datapage(struct lib32_elfinfo *v32,
                (sym64->st_value - VDSO64_LBASE);
 #endif /* CONFIG_PPC64 */
 
+#ifdef CONFIG_VDSO32
        sym32 = find_symbol32(v32, "__kernel_datapage_offset");
        if (sym32 == NULL) {
                printk(KERN_ERR "vDSO32: Can't find symbol "
@@ -561,6 +540,7 @@ static __init int vdso_fixup_datapage(struct lib32_elfinfo *v32,
        *((int *)(vdso32_kbase + (sym32->st_value - VDSO32_LBASE))) =
                (vdso32_pages << PAGE_SHIFT) -
                (sym32->st_value - VDSO32_LBASE);
+#endif
 
        return 0;
 }
@@ -569,55 +549,54 @@ static __init int vdso_fixup_datapage(struct lib32_elfinfo *v32,
 static __init int vdso_fixup_features(struct lib32_elfinfo *v32,
                                      struct lib64_elfinfo *v64)
 {
-       void *start32;
-       unsigned long size32;
+       unsigned long size;
+       void *start;
 
 #ifdef CONFIG_PPC64
-       void *start64;
-       unsigned long size64;
-
-       start64 = find_section64(v64->hdr, "__ftr_fixup", &size64);
-       if (start64)
+       start = find_section64(v64->hdr, "__ftr_fixup", &size);
+       if (start)
                do_feature_fixups(cur_cpu_spec->cpu_features,
-                                 start64, start64 + size64);
+                                 start, start + size);
 
-       start64 = find_section64(v64->hdr, "__mmu_ftr_fixup", &size64);
-       if (start64)
+       start = find_section64(v64->hdr, "__mmu_ftr_fixup", &size);
+       if (start)
                do_feature_fixups(cur_cpu_spec->mmu_features,
-                                 start64, start64 + size64);
+                                 start, start + size);
 
-       start64 = find_section64(v64->hdr, "__fw_ftr_fixup", &size64);
-       if (start64)
+       start = find_section64(v64->hdr, "__fw_ftr_fixup", &size);
+       if (start)
                do_feature_fixups(powerpc_firmware_features,
-                                 start64, start64 + size64);
+                                 start, start + size);
 
-       start64 = find_section64(v64->hdr, "__lwsync_fixup", &size64);
-       if (start64)
+       start = find_section64(v64->hdr, "__lwsync_fixup", &size);
+       if (start)
                do_lwsync_fixups(cur_cpu_spec->cpu_features,
-                                start64, start64 + size64);
+                                start, start + size);
 #endif /* CONFIG_PPC64 */
 
-       start32 = find_section32(v32->hdr, "__ftr_fixup", &size32);
-       if (start32)
+#ifdef CONFIG_VDSO32
+       start = find_section32(v32->hdr, "__ftr_fixup", &size);
+       if (start)
                do_feature_fixups(cur_cpu_spec->cpu_features,
-                                 start32, start32 + size32);
+                                 start, start + size);
 
-       start32 = find_section32(v32->hdr, "__mmu_ftr_fixup", &size32);
-       if (start32)
+       start = find_section32(v32->hdr, "__mmu_ftr_fixup", &size);
+       if (start)
                do_feature_fixups(cur_cpu_spec->mmu_features,
-                                 start32, start32 + size32);
+                                 start, start + size);
 
 #ifdef CONFIG_PPC64
-       start32 = find_section32(v32->hdr, "__fw_ftr_fixup", &size32);
-       if (start32)
+       start = find_section32(v32->hdr, "__fw_ftr_fixup", &size);
+       if (start)
                do_feature_fixups(powerpc_firmware_features,
-                                 start32, start32 + size32);
+                                 start, start + size);
 #endif /* CONFIG_PPC64 */
 
-       start32 = find_section32(v32->hdr, "__lwsync_fixup", &size32);
-       if (start32)
+       start = find_section32(v32->hdr, "__lwsync_fixup", &size);
+       if (start)
                do_lwsync_fixups(cur_cpu_spec->cpu_features,
-                                start32, start32 + size32);
+                                start, start + size);
+#endif
 
        return 0;
 }
@@ -779,11 +758,15 @@ static int __init vdso_init(void)
 #endif /* CONFIG_PPC64 */
 
 
+#ifdef CONFIG_VDSO32
+       vdso32_kbase = &vdso32_start;
+
        /*
         * Calculate the size of the 32 bits vDSO
         */
        vdso32_pages = (&vdso32_end - &vdso32_start) >> PAGE_SHIFT;
        DBG("vdso32_kbase: %p, 0x%x pages\n", vdso32_kbase, vdso32_pages);
+#endif
 
 
        /*
@@ -804,6 +787,7 @@ static int __init vdso_init(void)
                return 0;
        }
 
+#ifdef CONFIG_VDSO32
        /* Make sure pages are in the correct state */
        vdso32_pagelist = kzalloc(sizeof(struct page *) * (vdso32_pages + 2),
                                  GFP_KERNEL);
@@ -816,6 +800,7 @@ static int __init vdso_init(void)
        }
        vdso32_pagelist[i++] = virt_to_page(vdso_data);
        vdso32_pagelist[i] = NULL;
+#endif
 
 #ifdef CONFIG_PPC64
        vdso64_pagelist = kzalloc(sizeof(struct page *) * (vdso64_pages + 2),
index 5bfdab9047be2577443a77034e718ae56253546e..b41426c60ef62ea0f9757a62578ff86d143af3bf 100644 (file)
@@ -1196,6 +1196,11 @@ static struct iommu_table *vio_build_iommu_table(struct vio_dev *dev)
        tbl->it_type = TCE_VB;
        tbl->it_blocksize = 16;
 
+       if (firmware_has_feature(FW_FEATURE_LPAR))
+               tbl->it_ops = &iommu_table_lpar_multi_ops;
+       else
+               tbl->it_ops = &iommu_table_pseries_ops;
+
        return iommu_init_table(tbl, -1);
 }
 
index 453a8a47a4676c72333643305ef0770f188a555e..05ea8fc7f82976f5944391c8454ea11b189e572d 100644 (file)
@@ -757,16 +757,17 @@ void kvmppc_core_flush_memslot(struct kvm *kvm, struct kvm_memory_slot *memslot)
 
 int kvmppc_core_prepare_memory_region(struct kvm *kvm,
                                struct kvm_memory_slot *memslot,
-                               struct kvm_userspace_memory_region *mem)
+                               const struct kvm_userspace_memory_region *mem)
 {
        return kvm->arch.kvm_ops->prepare_memory_region(kvm, memslot, mem);
 }
 
 void kvmppc_core_commit_memory_region(struct kvm *kvm,
-                               struct kvm_userspace_memory_region *mem,
-                               const struct kvm_memory_slot *old)
+                               const struct kvm_userspace_memory_region *mem,
+                               const struct kvm_memory_slot *old,
+                               const struct kvm_memory_slot *new)
 {
-       kvm->arch.kvm_ops->commit_memory_region(kvm, mem, old);
+       kvm->arch.kvm_ops->commit_memory_region(kvm, mem, old, new);
 }
 
 int kvm_unmap_hva(struct kvm *kvm, unsigned long hva)
index 1a4acf8bf4f4b7a790c16dc0286c4c8bc9380edf..dab68b7af3f280225288c68f8d09eee753d85824 100644 (file)
@@ -650,7 +650,7 @@ static void kvmppc_rmap_reset(struct kvm *kvm)
        int srcu_idx;
 
        srcu_idx = srcu_read_lock(&kvm->srcu);
-       slots = kvm->memslots;
+       slots = kvm_memslots(kvm);
        kvm_for_each_memslot(memslot, slots) {
                /*
                 * This assumes it is acceptable to lose reference and
index df81caab738339c5b8dfe7c2def731bb9ba640d1..68d067ad4222fd4ece16bd049bdadf6be57402d5 100644 (file)
@@ -2321,6 +2321,7 @@ static int kvm_vm_ioctl_get_smmu_info_hv(struct kvm *kvm,
 static int kvm_vm_ioctl_get_dirty_log_hv(struct kvm *kvm,
                                         struct kvm_dirty_log *log)
 {
+       struct kvm_memslots *slots;
        struct kvm_memory_slot *memslot;
        int r;
        unsigned long n;
@@ -2331,7 +2332,8 @@ static int kvm_vm_ioctl_get_dirty_log_hv(struct kvm *kvm,
        if (log->slot >= KVM_USER_MEM_SLOTS)
                goto out;
 
-       memslot = id_to_memslot(kvm->memslots, log->slot);
+       slots = kvm_memslots(kvm);
+       memslot = id_to_memslot(slots, log->slot);
        r = -ENOENT;
        if (!memslot->dirty_bitmap)
                goto out;
@@ -2374,16 +2376,18 @@ static int kvmppc_core_create_memslot_hv(struct kvm_memory_slot *slot,
 
 static int kvmppc_core_prepare_memory_region_hv(struct kvm *kvm,
                                        struct kvm_memory_slot *memslot,
-                                       struct kvm_userspace_memory_region *mem)
+                                       const struct kvm_userspace_memory_region *mem)
 {
        return 0;
 }
 
 static void kvmppc_core_commit_memory_region_hv(struct kvm *kvm,
-                               struct kvm_userspace_memory_region *mem,
-                               const struct kvm_memory_slot *old)
+                               const struct kvm_userspace_memory_region *mem,
+                               const struct kvm_memory_slot *old,
+                               const struct kvm_memory_slot *new)
 {
        unsigned long npages = mem->memory_size >> PAGE_SHIFT;
+       struct kvm_memslots *slots;
        struct kvm_memory_slot *memslot;
 
        if (npages && old->npages) {
@@ -2393,7 +2397,8 @@ static void kvmppc_core_commit_memory_region_hv(struct kvm *kvm,
                 * since the rmap array starts out as all zeroes,
                 * i.e. no pages are dirty.
                 */
-               memslot = id_to_memslot(kvm->memslots, mem->slot);
+               slots = kvm_memslots(kvm);
+               memslot = id_to_memslot(slots, mem->slot);
                kvmppc_hv_get_dirty_log(kvm, memslot, NULL);
        }
 }
index 4d70df26c402c7f59f4bc8468f93afec162c1f8d..faa86e9c05510973001b7d3c64557e8a9cbd212d 100644 (file)
@@ -324,7 +324,7 @@ kvm_start_guest:
 kvm_secondary_got_guest:
 
        /* Set HSTATE_DSCR(r13) to something sensible */
-       ld      r6, PACA_DSCR(r13)
+       ld      r6, PACA_DSCR_DEFAULT(r13)
        std     r6, HSTATE_DSCR(r13)
 
        /* Order load of vcore, ptid etc. after load of vcpu */
index f57383941d0368e64ab99cc200e70e1ee4e92f53..64891b081ad54f57bbaa603d3315bf32b48368c8 100644 (file)
@@ -1530,6 +1530,7 @@ out:
 static int kvm_vm_ioctl_get_dirty_log_pr(struct kvm *kvm,
                                         struct kvm_dirty_log *log)
 {
+       struct kvm_memslots *slots;
        struct kvm_memory_slot *memslot;
        struct kvm_vcpu *vcpu;
        ulong ga, ga_end;
@@ -1545,7 +1546,8 @@ static int kvm_vm_ioctl_get_dirty_log_pr(struct kvm *kvm,
 
        /* If nothing is dirty, don't bother messing with page tables. */
        if (is_dirty) {
-               memslot = id_to_memslot(kvm->memslots, log->slot);
+               slots = kvm_memslots(kvm);
+               memslot = id_to_memslot(slots, log->slot);
 
                ga = memslot->base_gfn << PAGE_SHIFT;
                ga_end = ga + (memslot->npages << PAGE_SHIFT);
@@ -1571,14 +1573,15 @@ static void kvmppc_core_flush_memslot_pr(struct kvm *kvm,
 
 static int kvmppc_core_prepare_memory_region_pr(struct kvm *kvm,
                                        struct kvm_memory_slot *memslot,
-                                       struct kvm_userspace_memory_region *mem)
+                                       const struct kvm_userspace_memory_region *mem)
 {
        return 0;
 }
 
 static void kvmppc_core_commit_memory_region_pr(struct kvm *kvm,
-                               struct kvm_userspace_memory_region *mem,
-                               const struct kvm_memory_slot *old)
+                               const struct kvm_userspace_memory_region *mem,
+                               const struct kvm_memory_slot *old,
+                               const struct kvm_memory_slot *new)
 {
        return;
 }
index 6c1316a15a2729a67ed61bcb6e2ce8b90af6bdbe..cc5842657161580262d10fb49e0bdfde4de497fd 100644 (file)
@@ -1004,10 +1004,10 @@ int kvmppc_handle_exit(struct kvm_run *run, struct kvm_vcpu *vcpu,
                break;
        }
 
-       local_irq_enable();
-
        trace_kvm_exit(exit_nr, vcpu);
-       kvm_guest_exit();
+       __kvm_guest_exit();
+
+       local_irq_enable();
 
        run->exit_reason = KVM_EXIT_UNKNOWN;
        run->ready_for_interrupt_injection = 1;
@@ -1784,14 +1784,15 @@ int kvmppc_core_create_memslot(struct kvm *kvm, struct kvm_memory_slot *slot,
 
 int kvmppc_core_prepare_memory_region(struct kvm *kvm,
                                      struct kvm_memory_slot *memslot,
-                                     struct kvm_userspace_memory_region *mem)
+                                     const struct kvm_userspace_memory_region *mem)
 {
        return 0;
 }
 
 void kvmppc_core_commit_memory_region(struct kvm *kvm,
-                               struct kvm_userspace_memory_region *mem,
-                               const struct kvm_memory_slot *old)
+                               const struct kvm_userspace_memory_region *mem,
+                               const struct kvm_memory_slot *old,
+                               const struct kvm_memory_slot *new)
 {
 }
 
index ac3ddf115f3d852db205c72e79f2b592c6ef9e99..e5dde32fe71fc1856cb5ae515150aa7130de3c96 100644 (file)
@@ -115,7 +115,7 @@ int kvmppc_prepare_to_enter(struct kvm_vcpu *vcpu)
                        continue;
                }
 
-               kvm_guest_enter();
+               __kvm_guest_enter();
                return 1;
        }
 
@@ -595,18 +595,19 @@ int kvm_arch_create_memslot(struct kvm *kvm, struct kvm_memory_slot *slot,
 
 int kvm_arch_prepare_memory_region(struct kvm *kvm,
                                   struct kvm_memory_slot *memslot,
-                                  struct kvm_userspace_memory_region *mem,
+                                  const struct kvm_userspace_memory_region *mem,
                                   enum kvm_mr_change change)
 {
        return kvmppc_core_prepare_memory_region(kvm, memslot, mem);
 }
 
 void kvm_arch_commit_memory_region(struct kvm *kvm,
-                                  struct kvm_userspace_memory_region *mem,
+                                  const struct kvm_userspace_memory_region *mem,
                                   const struct kvm_memory_slot *old,
+                                  const struct kvm_memory_slot *new,
                                   enum kvm_mr_change change)
 {
-       kvmppc_core_commit_memory_region(kvm, mem, old);
+       kvmppc_core_commit_memory_region(kvm, mem, old, new);
 }
 
 void kvm_arch_flush_shadow_memslot(struct kvm *kvm,
index 7902802a19a56a7ee27d3f1e5ba8e4eacf3f5f6f..a47e14277fd8a91d1d4a255592db73a979a94f1b 100644 (file)
@@ -33,6 +33,6 @@ obj-$(CONFIG_PPC_LIB_RHEAP) += rheap.o
 obj-$(CONFIG_FTR_FIXUP_SELFTEST) += feature-fixups-test.o
 
 obj-$(CONFIG_ALTIVEC)  += xor_vmx.o
-CFLAGS_xor_vmx.o += -maltivec -mabi=altivec
+CFLAGS_xor_vmx.o += -maltivec $(call cc-option,-mabi=altivec)
 
 obj-$(CONFIG_PPC64) += $(obj64-y)
index 9c8770b5f96f20212a3995559e4a4c7c9eb28786..3eb73a38220de34379de3bad092b06d40f051f53 100644 (file)
@@ -36,3 +36,4 @@ obj-$(CONFIG_PPC_SUBPAGE_PROT)        += subpage-prot.o
 obj-$(CONFIG_NOT_COHERENT_CACHE) += dma-noncoherent.o
 obj-$(CONFIG_HIGHMEM)          += highmem.o
 obj-$(CONFIG_PPC_COPRO_BASE)   += copro_fault.o
+obj-$(CONFIG_SPAPR_TCE_IOMMU)  += mmu_context_iommu.o
index f031a47d7701e60d349f08b46365e2912844066b..6527882ce05ede3a0a45f74d3a11c4c375da6514 100644 (file)
@@ -26,7 +26,7 @@
 #include <asm/reg.h>
 #include <asm/copro.h>
 #include <asm/spu.h>
-#include <misc/cxl.h>
+#include <misc/cxl-base.h>
 
 /*
  * This ought to be kept in sync with the powerpc specific do_page_fault
@@ -100,7 +100,7 @@ EXPORT_SYMBOL_GPL(copro_handle_mm_fault);
 
 int copro_calculate_slb(struct mm_struct *mm, u64 ea, struct copro_slb *slb)
 {
-       u64 vsid;
+       u64 vsid, vsidkey;
        int psize, ssize;
 
        switch (REGION_ID(ea)) {
@@ -109,6 +109,7 @@ int copro_calculate_slb(struct mm_struct *mm, u64 ea, struct copro_slb *slb)
                psize = get_slice_psize(mm, ea);
                ssize = user_segment_size(ea);
                vsid = get_vsid(mm->context.id, ea, ssize);
+               vsidkey = SLB_VSID_USER;
                break;
        case VMALLOC_REGION_ID:
                pr_devel("%s: 0x%llx -- VMALLOC_REGION_ID\n", __func__, ea);
@@ -118,19 +119,21 @@ int copro_calculate_slb(struct mm_struct *mm, u64 ea, struct copro_slb *slb)
                        psize = mmu_io_psize;
                ssize = mmu_kernel_ssize;
                vsid = get_kernel_vsid(ea, mmu_kernel_ssize);
+               vsidkey = SLB_VSID_KERNEL;
                break;
        case KERNEL_REGION_ID:
                pr_devel("%s: 0x%llx -- KERNEL_REGION_ID\n", __func__, ea);
                psize = mmu_linear_psize;
                ssize = mmu_kernel_ssize;
                vsid = get_kernel_vsid(ea, mmu_kernel_ssize);
+               vsidkey = SLB_VSID_KERNEL;
                break;
        default:
                pr_debug("%s: invalid region access at %016llx\n", __func__, ea);
                return 1;
        }
 
-       vsid = (vsid << slb_vsid_shift(ssize)) | SLB_VSID_USER;
+       vsid = (vsid << slb_vsid_shift(ssize)) | vsidkey;
 
        vsid |= mmu_psize_defs[psize].sllp |
                ((ssize == MMU_SEGSIZE_1T) ? SLB_VSID_B_1T : 0);
index 9c4880ddecd63f06e0f4b2b45aded0b57b47e940..13befa35d8a8ecdd31611aadb42c6be206ba743e 100644 (file)
@@ -29,7 +29,7 @@
 #include <asm/kexec.h>
 #include <asm/ppc-opcode.h>
 
-#include <misc/cxl.h>
+#include <misc/cxl-base.h>
 
 #ifdef DEBUG_LOW
 #define DBG_LOW(fmt...) udbg_printf(fmt)
index fda236f908eb3a8239d172d5e3d35b21fc67e3ac..5ec987f65b2c95c4328fdb7369fd4c2d597284ba 100644 (file)
@@ -57,6 +57,7 @@
 #include <asm/fadump.h>
 #include <asm/firmware.h>
 #include <asm/tm.h>
+#include <asm/trace.h>
 
 #ifdef DEBUG
 #define DBG(fmt...) udbg_printf(fmt)
@@ -1004,6 +1005,7 @@ int hash_page_mm(struct mm_struct *mm, unsigned long ea,
 
        DBG_LOW("hash_page(ea=%016lx, access=%lx, trap=%lx\n",
                ea, access, trap);
+       trace_hash_fault(ea, access, trap);
 
        /* Get region & vsid */
        switch (REGION_ID(ea)) {
@@ -1475,7 +1477,7 @@ static void kernel_map_linear_page(unsigned long vaddr, unsigned long lmi)
        unsigned long hash;
        unsigned long vsid = get_kernel_vsid(vaddr, mmu_kernel_ssize);
        unsigned long vpn = hpt_vpn(vaddr, vsid, mmu_kernel_ssize);
-       unsigned long mode = htab_convert_pte_flags(PAGE_KERNEL);
+       unsigned long mode = htab_convert_pte_flags(pgprot_val(PAGE_KERNEL));
        long ret;
 
        hash = hpt_hash(vpn, PAGE_SHIFT, mmu_kernel_ssize);
index 45fda71feb27465fcd4079744d0a19cd9f168ea9..0f11819d8f1dc07441f6e13362a4dca53f55fb87 100644 (file)
@@ -560,7 +560,7 @@ subsys_initcall(add_system_ram_resources);
  */
 int devmem_is_allowed(unsigned long pfn)
 {
-       if (iomem_is_exclusive(pfn << PAGE_SHIFT))
+       if (iomem_is_exclusive(PFN_PHYS(pfn)))
                return 0;
        if (!page_is_ram(pfn))
                return 1;
index 178876aef40f8d6dd5d69b650e986225e0ddfb70..4e4efbc2658e3500c1a2f0eb3fb2a036a5d284cc 100644 (file)
@@ -88,6 +88,9 @@ int init_new_context(struct task_struct *tsk, struct mm_struct *mm)
 
 #ifdef CONFIG_PPC_64K_PAGES
        mm->context.pte_frag = NULL;
+#endif
+#ifdef CONFIG_SPAPR_TCE_IOMMU
+       mm_iommu_init(&mm->context);
 #endif
        return 0;
 }
@@ -132,6 +135,9 @@ static inline void destroy_pagetable_page(struct mm_struct *mm)
 
 void destroy_context(struct mm_struct *mm)
 {
+#ifdef CONFIG_SPAPR_TCE_IOMMU
+       mm_iommu_cleanup(&mm->context);
+#endif
 
 #ifdef CONFIG_PPC_ICSWX
        drop_cop(mm->context.acop, mm);
diff --git a/arch/powerpc/mm/mmu_context_iommu.c b/arch/powerpc/mm/mmu_context_iommu.c
new file mode 100644 (file)
index 0000000..da6a216
--- /dev/null
@@ -0,0 +1,316 @@
+/*
+ *  IOMMU helpers in MMU context.
+ *
+ *  Copyright (C) 2015 IBM Corp. <aik@ozlabs.ru>
+ *
+ *  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/sched.h>
+#include <linux/slab.h>
+#include <linux/rculist.h>
+#include <linux/vmalloc.h>
+#include <linux/mutex.h>
+#include <asm/mmu_context.h>
+
+static DEFINE_MUTEX(mem_list_mutex);
+
+struct mm_iommu_table_group_mem_t {
+       struct list_head next;
+       struct rcu_head rcu;
+       unsigned long used;
+       atomic64_t mapped;
+       u64 ua;                 /* userspace address */
+       u64 entries;            /* number of entries in hpas[] */
+       u64 *hpas;              /* vmalloc'ed */
+};
+
+static long mm_iommu_adjust_locked_vm(struct mm_struct *mm,
+               unsigned long npages, bool incr)
+{
+       long ret = 0, locked, lock_limit;
+
+       if (!npages)
+               return 0;
+
+       down_write(&mm->mmap_sem);
+
+       if (incr) {
+               locked = mm->locked_vm + npages;
+               lock_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT;
+               if (locked > lock_limit && !capable(CAP_IPC_LOCK))
+                       ret = -ENOMEM;
+               else
+                       mm->locked_vm += npages;
+       } else {
+               if (WARN_ON_ONCE(npages > mm->locked_vm))
+                       npages = mm->locked_vm;
+               mm->locked_vm -= npages;
+       }
+
+       pr_debug("[%d] RLIMIT_MEMLOCK HASH64 %c%ld %ld/%ld\n",
+                       current->pid,
+                       incr ? '+' : '-',
+                       npages << PAGE_SHIFT,
+                       mm->locked_vm << PAGE_SHIFT,
+                       rlimit(RLIMIT_MEMLOCK));
+       up_write(&mm->mmap_sem);
+
+       return ret;
+}
+
+bool mm_iommu_preregistered(void)
+{
+       if (!current || !current->mm)
+               return false;
+
+       return !list_empty(&current->mm->context.iommu_group_mem_list);
+}
+EXPORT_SYMBOL_GPL(mm_iommu_preregistered);
+
+long mm_iommu_get(unsigned long ua, unsigned long entries,
+               struct mm_iommu_table_group_mem_t **pmem)
+{
+       struct mm_iommu_table_group_mem_t *mem;
+       long i, j, ret = 0, locked_entries = 0;
+       struct page *page = NULL;
+
+       if (!current || !current->mm)
+               return -ESRCH; /* process exited */
+
+       mutex_lock(&mem_list_mutex);
+
+       list_for_each_entry_rcu(mem, &current->mm->context.iommu_group_mem_list,
+                       next) {
+               if ((mem->ua == ua) && (mem->entries == entries)) {
+                       ++mem->used;
+                       *pmem = mem;
+                       goto unlock_exit;
+               }
+
+               /* Overlap? */
+               if ((mem->ua < (ua + (entries << PAGE_SHIFT))) &&
+                               (ua < (mem->ua +
+                                      (mem->entries << PAGE_SHIFT)))) {
+                       ret = -EINVAL;
+                       goto unlock_exit;
+               }
+
+       }
+
+       ret = mm_iommu_adjust_locked_vm(current->mm, entries, true);
+       if (ret)
+               goto unlock_exit;
+
+       locked_entries = entries;
+
+       mem = kzalloc(sizeof(*mem), GFP_KERNEL);
+       if (!mem) {
+               ret = -ENOMEM;
+               goto unlock_exit;
+       }
+
+       mem->hpas = vzalloc(entries * sizeof(mem->hpas[0]));
+       if (!mem->hpas) {
+               kfree(mem);
+               ret = -ENOMEM;
+               goto unlock_exit;
+       }
+
+       for (i = 0; i < entries; ++i) {
+               if (1 != get_user_pages_fast(ua + (i << PAGE_SHIFT),
+                                       1/* pages */, 1/* iswrite */, &page)) {
+                       for (j = 0; j < i; ++j)
+                               put_page(pfn_to_page(
+                                               mem->hpas[j] >> PAGE_SHIFT));
+                       vfree(mem->hpas);
+                       kfree(mem);
+                       ret = -EFAULT;
+                       goto unlock_exit;
+               }
+
+               mem->hpas[i] = page_to_pfn(page) << PAGE_SHIFT;
+       }
+
+       atomic64_set(&mem->mapped, 1);
+       mem->used = 1;
+       mem->ua = ua;
+       mem->entries = entries;
+       *pmem = mem;
+
+       list_add_rcu(&mem->next, &current->mm->context.iommu_group_mem_list);
+
+unlock_exit:
+       if (locked_entries && ret)
+               mm_iommu_adjust_locked_vm(current->mm, locked_entries, false);
+
+       mutex_unlock(&mem_list_mutex);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(mm_iommu_get);
+
+static void mm_iommu_unpin(struct mm_iommu_table_group_mem_t *mem)
+{
+       long i;
+       struct page *page = NULL;
+
+       for (i = 0; i < mem->entries; ++i) {
+               if (!mem->hpas[i])
+                       continue;
+
+               page = pfn_to_page(mem->hpas[i] >> PAGE_SHIFT);
+               if (!page)
+                       continue;
+
+               put_page(page);
+               mem->hpas[i] = 0;
+       }
+}
+
+static void mm_iommu_do_free(struct mm_iommu_table_group_mem_t *mem)
+{
+
+       mm_iommu_unpin(mem);
+       vfree(mem->hpas);
+       kfree(mem);
+}
+
+static void mm_iommu_free(struct rcu_head *head)
+{
+       struct mm_iommu_table_group_mem_t *mem = container_of(head,
+                       struct mm_iommu_table_group_mem_t, rcu);
+
+       mm_iommu_do_free(mem);
+}
+
+static void mm_iommu_release(struct mm_iommu_table_group_mem_t *mem)
+{
+       list_del_rcu(&mem->next);
+       mm_iommu_adjust_locked_vm(current->mm, mem->entries, false);
+       call_rcu(&mem->rcu, mm_iommu_free);
+}
+
+long mm_iommu_put(struct mm_iommu_table_group_mem_t *mem)
+{
+       long ret = 0;
+
+       if (!current || !current->mm)
+               return -ESRCH; /* process exited */
+
+       mutex_lock(&mem_list_mutex);
+
+       if (mem->used == 0) {
+               ret = -ENOENT;
+               goto unlock_exit;
+       }
+
+       --mem->used;
+       /* There are still users, exit */
+       if (mem->used)
+               goto unlock_exit;
+
+       /* Are there still mappings? */
+       if (atomic_cmpxchg(&mem->mapped, 1, 0) != 1) {
+               ++mem->used;
+               ret = -EBUSY;
+               goto unlock_exit;
+       }
+
+       /* @mapped became 0 so now mappings are disabled, release the region */
+       mm_iommu_release(mem);
+
+unlock_exit:
+       mutex_unlock(&mem_list_mutex);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(mm_iommu_put);
+
+struct mm_iommu_table_group_mem_t *mm_iommu_lookup(unsigned long ua,
+               unsigned long size)
+{
+       struct mm_iommu_table_group_mem_t *mem, *ret = NULL;
+
+       list_for_each_entry_rcu(mem,
+                       &current->mm->context.iommu_group_mem_list,
+                       next) {
+               if ((mem->ua <= ua) &&
+                               (ua + size <= mem->ua +
+                                (mem->entries << PAGE_SHIFT))) {
+                       ret = mem;
+                       break;
+               }
+       }
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(mm_iommu_lookup);
+
+struct mm_iommu_table_group_mem_t *mm_iommu_find(unsigned long ua,
+               unsigned long entries)
+{
+       struct mm_iommu_table_group_mem_t *mem, *ret = NULL;
+
+       list_for_each_entry_rcu(mem,
+                       &current->mm->context.iommu_group_mem_list,
+                       next) {
+               if ((mem->ua == ua) && (mem->entries == entries)) {
+                       ret = mem;
+                       break;
+               }
+       }
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(mm_iommu_find);
+
+long mm_iommu_ua_to_hpa(struct mm_iommu_table_group_mem_t *mem,
+               unsigned long ua, unsigned long *hpa)
+{
+       const long entry = (ua - mem->ua) >> PAGE_SHIFT;
+       u64 *va = &mem->hpas[entry];
+
+       if (entry >= mem->entries)
+               return -EFAULT;
+
+       *hpa = *va | (ua & ~PAGE_MASK);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(mm_iommu_ua_to_hpa);
+
+long mm_iommu_mapped_inc(struct mm_iommu_table_group_mem_t *mem)
+{
+       if (atomic64_inc_not_zero(&mem->mapped))
+               return 0;
+
+       /* Last mm_iommu_put() has been called, no more mappings allowed() */
+       return -ENXIO;
+}
+EXPORT_SYMBOL_GPL(mm_iommu_mapped_inc);
+
+void mm_iommu_mapped_dec(struct mm_iommu_table_group_mem_t *mem)
+{
+       atomic64_add_unless(&mem->mapped, -1, 1);
+}
+EXPORT_SYMBOL_GPL(mm_iommu_mapped_dec);
+
+void mm_iommu_init(mm_context_t *ctx)
+{
+       INIT_LIST_HEAD_RCU(&ctx->iommu_group_mem_list);
+}
+
+void mm_iommu_cleanup(mm_context_t *ctx)
+{
+       struct mm_iommu_table_group_mem_t *mem, *tmp;
+
+       list_for_each_entry_safe(mem, tmp, &ctx->iommu_group_mem_list, next) {
+               list_del_rcu(&mem->next);
+               mm_iommu_do_free(mem);
+       }
+}
index 89bf95bd63b1f6bac752956bbea522f31093487d..765b419883f22edebd839f16b7842cd833e2681a 100644 (file)
@@ -398,18 +398,18 @@ ALT_FTR_SECTION_END_IFSET(CPU_FTR_SMT)
        rldicl  r15,r16,64-PUD_SHIFT+3,64-PUD_INDEX_SIZE-3
        clrrdi  r15,r15,3
        cmpdi   cr0,r14,0
-       bge     tlb_miss_fault_e6500    /* Bad pgd entry or hugepage; bail */
+       bge     tlb_miss_huge_e6500     /* Bad pgd entry or hugepage; bail */
        ldx     r14,r14,r15             /* grab pud entry */
 
        rldicl  r15,r16,64-PMD_SHIFT+3,64-PMD_INDEX_SIZE-3
        clrrdi  r15,r15,3
        cmpdi   cr0,r14,0
-       bge     tlb_miss_fault_e6500
+       bge     tlb_miss_huge_e6500
        ldx     r14,r14,r15             /* Grab pmd entry */
 
        mfspr   r10,SPRN_MAS0
        cmpdi   cr0,r14,0
-       bge     tlb_miss_fault_e6500
+       bge     tlb_miss_huge_e6500
 
        /* Now we build the MAS for a 2M indirect page:
         *
@@ -428,6 +428,7 @@ ALT_FTR_SECTION_END_IFSET(CPU_FTR_SMT)
        clrrdi  r15,r16,21              /* make EA 2M-aligned */
        mtspr   SPRN_MAS2,r15
 
+tlb_miss_huge_done_e6500:
        lbz     r15,TCD_ESEL_NEXT(r11)
        lbz     r16,TCD_ESEL_MAX(r11)
        lbz     r14,TCD_ESEL_FIRST(r11)
@@ -456,6 +457,50 @@ END_FTR_SECTION_IFSET(CPU_FTR_SMT)
        tlb_epilog_bolted
        rfi
 
+tlb_miss_huge_e6500:
+       beq     tlb_miss_fault_e6500
+       li      r10,1
+       andi.   r15,r14,HUGEPD_SHIFT_MASK@l /* r15 = psize */
+       rldimi  r14,r10,63,0            /* Set PD_HUGE */
+       xor     r14,r14,r15             /* Clear size bits */
+       ldx     r14,0,r14
+
+       /*
+        * Now we build the MAS for a huge page.
+        *
+        * MAS 0   :    ESEL needs to be filled by software round-robin
+        *               - can be handled by indirect code
+        * MAS 1   :    Need to clear IND and set TSIZE
+        * MAS 2,3+7:   Needs to be redone similar to non-tablewalk handler
+        */
+
+       subi    r15,r15,10              /* Convert psize to tsize */
+       mfspr   r10,SPRN_MAS1
+       rlwinm  r10,r10,0,~MAS1_IND
+       rlwimi  r10,r15,MAS1_TSIZE_SHIFT,MAS1_TSIZE_MASK
+       mtspr   SPRN_MAS1,r10
+
+       li      r10,-0x400
+       sld     r15,r10,r15             /* Generate mask based on size */
+       and     r10,r16,r15
+       rldicr  r15,r14,64-(PTE_RPN_SHIFT-PAGE_SHIFT),63-PAGE_SHIFT
+       rlwimi  r10,r14,32-19,27,31     /* Insert WIMGE */
+       clrldi  r15,r15,PAGE_SHIFT      /* Clear crap at the top */
+       rlwimi  r15,r14,32-8,22,25      /* Move in U bits */
+       mtspr   SPRN_MAS2,r10
+       andi.   r10,r14,_PAGE_DIRTY
+       rlwimi  r15,r14,32-2,26,31      /* Move in BAP bits */
+
+       /* Mask out SW and UW if !DIRTY (XXX optimize this !) */
+       bne     1f
+       li      r10,MAS3_SW|MAS3_UW
+       andc    r15,r15,r10
+1:
+       mtspr   SPRN_MAS7_MAS3,r15
+
+       mfspr   r10,SPRN_MAS0
+       b       tlb_miss_huge_done_e6500
+
 tlb_miss_kernel_e6500:
        ld      r14,PACA_KERNELPGD(r13)
        cmpldi  cr1,r15,8               /* Check for vmalloc region */
index 12b638425bb9b543f4761246bbb7709e64d379cb..d90893b76e7ceb51b5741d50d2d06fe873ee6af4 100644 (file)
@@ -131,7 +131,16 @@ static void pmao_restore_workaround(bool ebb) { }
 
 static bool regs_use_siar(struct pt_regs *regs)
 {
-       return !!regs->result;
+       /*
+        * When we take a performance monitor exception the regs are setup
+        * using perf_read_regs() which overloads some fields, in particular
+        * regs->result to tell us whether to use SIAR.
+        *
+        * However if the regs are from another exception, eg. a syscall, then
+        * they have not been setup using perf_read_regs() and so regs->result
+        * is something random.
+        */
+       return ((TRAP(regs) == 0xf00) && regs->result);
 }
 
 /*
index c949ca055712be9c6bbe5ff4ed808d2d640a434a..63016621aff8af4807b37ed7af2a7ee078755e4b 100644 (file)
@@ -193,7 +193,7 @@ static struct irq_chip mpc52xx_gpt_irq_chip = {
 
 void mpc52xx_gpt_irq_cascade(unsigned int virq, struct irq_desc *desc)
 {
-       struct mpc52xx_gpt_priv *gpt = irq_get_handler_data(virq);
+       struct mpc52xx_gpt_priv *gpt = irq_desc_get_handler_data(desc);
        int sub_virq;
        u32 status;
 
index e2d401ad8fbbed71bf6594f840dc63ab2dc367e0..6eb3b2abae9088a653705010741cff60b90261a2 100644 (file)
@@ -12,7 +12,7 @@
 
 #undef DEBUG
 
-#include <asm/pci.h>
+#include <linux/pci.h>
 #include <asm/mpc52xx.h>
 #include <asm/delay.h>
 #include <asm/machdep.h>
index 2fb4b24368a6e6ae7851ade3bcae379d2245229b..97915feffd42d35be230f3d4b58883b938b4eaae 100644 (file)
@@ -282,7 +282,7 @@ config CORENET_GENERIC
          For 64bit kernel, the following boards are supported:
            T208x QDS/RDB, T4240 QDS/RDB and B4 QDS
          The following boards are supported for both 32bit and 64bit kernel:
-           P5020 DS, P5040 DS and T104xQDS/RDB
+           P5020 DS, P5040 DS, T102x QDS/RDB, T104x QDS/RDB
 
 endif # FSL_SOC_BOOKE
 
index 9824d2cf79bd6d618dba10e1bb4e38f4b946b408..bd839dc287fe61f3ef507a1d2d0070d2ad7b31c5 100644 (file)
@@ -150,6 +150,9 @@ static const char * const boards[] __initconst = {
        "fsl,B4860QDS",
        "fsl,B4420QDS",
        "fsl,B4220QDS",
+       "fsl,T1023RDB",
+       "fsl,T1024QDS",
+       "fsl,T1024RDB",
        "fsl,T1040QDS",
        "fsl,T1042QDS",
        "fsl,T1040RDB",
index 8631ac5f0e579c35ffa4ac2153e237be908a8817..b8b8216979104c22dc29fc06dd433aeedb409e33 100644 (file)
@@ -345,6 +345,7 @@ void mpc85xx_smp_kexec_cpu_down(int crash_shutdown, int secondary)
        local_irq_disable();
 
        if (secondary) {
+               __flush_disable_L1();
                atomic_inc(&kexec_down_cpus);
                /* loop forever */
                while (1);
@@ -357,61 +358,11 @@ static void mpc85xx_smp_kexec_down(void *arg)
                ppc_md.kexec_cpu_down(0,1);
 }
 
-static void map_and_flush(unsigned long paddr)
-{
-       struct page *page = pfn_to_page(paddr >> PAGE_SHIFT);
-       unsigned long kaddr  = (unsigned long)kmap_atomic(page);
-
-       flush_dcache_range(kaddr, kaddr + PAGE_SIZE);
-       kunmap_atomic((void *)kaddr);
-}
-
-/**
- * Before we reset the other cores, we need to flush relevant cache
- * out to memory so we don't get anything corrupted, some of these flushes
- * are performed out of an overabundance of caution as interrupts are not
- * disabled yet and we can switch cores
- */
-static void mpc85xx_smp_flush_dcache_kexec(struct kimage *image)
-{
-       kimage_entry_t *ptr, entry;
-       unsigned long paddr;
-       int i;
-
-       if (image->type == KEXEC_TYPE_DEFAULT) {
-               /* normal kexec images are stored in temporary pages */
-               for (ptr = &image->head; (entry = *ptr) && !(entry & IND_DONE);
-                    ptr = (entry & IND_INDIRECTION) ?
-                               phys_to_virt(entry & PAGE_MASK) : ptr + 1) {
-                       if (!(entry & IND_DESTINATION)) {
-                               map_and_flush(entry);
-                       }
-               }
-               /* flush out last IND_DONE page */
-               map_and_flush(entry);
-       } else {
-               /* crash type kexec images are copied to the crash region */
-               for (i = 0; i < image->nr_segments; i++) {
-                       struct kexec_segment *seg = &image->segment[i];
-                       for (paddr = seg->mem; paddr < seg->mem + seg->memsz;
-                            paddr += PAGE_SIZE) {
-                               map_and_flush(paddr);
-                       }
-               }
-       }
-
-       /* also flush the kimage struct to be passed in as well */
-       flush_dcache_range((unsigned long)image,
-                          (unsigned long)image + sizeof(*image));
-}
-
 static void mpc85xx_smp_machine_kexec(struct kimage *image)
 {
        int timeout = INT_MAX;
        int i, num_cpus = num_present_cpus();
 
-       mpc85xx_smp_flush_dcache_kexec(image);
-
        if (image->type == KEXEC_TYPE_DEFAULT)
                smp_call_function(mpc85xx_smp_kexec_down, NULL, 0);
 
index 1eadb6d0dc6406acbe24a5b946fb4a62b104b83e..30e002f4648c81a1b7f1d4a6d5a0d7c08d4ea2fb 100644 (file)
@@ -79,7 +79,7 @@ static void __init twr_p1025_setup_arch(void)
        mpc85xx_qe_init();
        mpc85xx_qe_par_io_init();
 
-#if defined(CONFIG_UCC_GETH) || defined(CONFIG_SERIAL_QE)
+#if IS_ENABLED(CONFIG_UCC_GETH) || IS_ENABLED(CONFIG_SERIAL_QE)
        if (machine_is(twr_p1025)) {
                struct ccsr_guts __iomem *guts;
 
@@ -101,7 +101,7 @@ static void __init twr_p1025_setup_arch(void)
                                        MPC85xx_PMUXCR_QE(12));
                        iounmap(guts);
 
-#if defined(CONFIG_SERIAL_QE)
+#if IS_ENABLED(CONFIG_SERIAL_QE)
                        /* On P1025TWR board, the UCC7 acted as UART port.
                         * However, The UCC7's CTS pin is low level in default,
                         * it will impact the transmission in full duplex
index 7264e91190be928e125d01fe22be89a2b544c597..c140e94c7c72b466483fbf05be8eb82e084d346b 100644 (file)
@@ -405,6 +405,16 @@ config PPC_DOORBELL
 
 endmenu
 
+config VDSO32
+       def_bool y
+       depends on PPC32 || CPU_BIG_ENDIAN
+       help
+         This symbol controls whether we build the 32-bit VDSO. We obviously
+         want to do that if we're building a 32-bit kernel. If we're building
+         a 64-bit kernel then we only want a 32-bit VDSO if we're building for
+         big endian. That is because the only little endian configuration we
+         support is ppc64le which is 64-bit only.
+
 choice
        prompt "Endianness selection"
        default CPU_BIG_ENDIAN
@@ -421,6 +431,7 @@ config CPU_BIG_ENDIAN
 
 config CPU_LITTLE_ENDIAN
        bool "Build little endian kernel"
+       depends on PPC_BOOK3S_64
        select PPC64_BOOT_WRAPPER
        help
          Build a little endian kernel.
index 623bd961465ad501eeb1294fae6095105e081d4a..fe51de4fcf135a1d1b355efcd8c20915c3a1b80e 100644 (file)
@@ -22,6 +22,7 @@
 #include <asm/machdep.h>
 #include <asm/prom.h>
 
+#include "cell.h"
 
 /*
  * MSIC registers, specified as offsets from dcr_base
@@ -95,7 +96,7 @@ static void msic_dcr_write(struct axon_msic *msic, unsigned int dcr_n, u32 val)
 static void axon_msi_cascade(unsigned int irq, struct irq_desc *desc)
 {
        struct irq_chip *chip = irq_desc_get_chip(desc);
-       struct axon_msic *msic = irq_get_handler_data(irq);
+       struct axon_msic *msic = irq_desc_get_handler_data(desc);
        u32 write_offset, msi;
        int idx;
        int retry = 0;
@@ -406,8 +407,8 @@ static int axon_msi_probe(struct platform_device *device)
 
        dev_set_drvdata(&device->dev, msic);
 
-       ppc_md.setup_msi_irqs = axon_msi_setup_msi_irqs;
-       ppc_md.teardown_msi_irqs = axon_msi_teardown_msi_irqs;
+       cell_pci_controller_ops.setup_msi_irqs = axon_msi_setup_msi_irqs;
+       cell_pci_controller_ops.teardown_msi_irqs = axon_msi_teardown_msi_irqs;
 
        axon_msi_debug_setup(dn, msic);
 
index 21b502398bf379063a000012c1a7d2df4cf2c2b9..14a582b2127458c31a4865753bf6acc97755a4f5 100644 (file)
@@ -466,6 +466,11 @@ static inline u32 cell_iommu_get_ioid(struct device_node *np)
        return *ioid;
 }
 
+static struct iommu_table_ops cell_iommu_ops = {
+       .set = tce_build_cell,
+       .clear = tce_free_cell
+};
+
 static struct iommu_window * __init
 cell_iommu_setup_window(struct cbe_iommu *iommu, struct device_node *np,
                        unsigned long offset, unsigned long size,
@@ -492,6 +497,7 @@ cell_iommu_setup_window(struct cbe_iommu *iommu, struct device_node *np,
        window->table.it_offset =
                (offset >> window->table.it_page_shift) + pte_offset;
        window->table.it_size = size >> window->table.it_page_shift;
+       window->table.it_ops = &cell_iommu_ops;
 
        iommu_init_table(&window->table, iommu->nid);
 
@@ -1201,8 +1207,6 @@ static int __init cell_iommu_init(void)
        /* Setup various callbacks */
        cell_pci_controller_ops.dma_dev_setup = cell_pci_dma_dev_setup;
        ppc_md.dma_get_required_mask = cell_dma_get_required_mask;
-       ppc_md.tce_build = tce_build_cell;
-       ppc_md.tce_free = tce_free_cell;
 
        if (!iommu_fixed_disabled && cell_iommu_fixed_mapping_init() == 0)
                goto bail;
index c269caee58f9486f6e260cf67367cb501820642c..9dd154d6f89a9205aa33eff33930e2cb0efc3397 100644 (file)
@@ -124,7 +124,7 @@ static void hlwd_pic_irq_cascade(unsigned int cascade_virq,
                                      struct irq_desc *desc)
 {
        struct irq_chip *chip = irq_desc_get_chip(desc);
-       struct irq_domain *irq_domain = irq_get_handler_data(cascade_virq);
+       struct irq_domain *irq_domain = irq_desc_get_handler_data(desc);
        unsigned int virq;
 
        raw_spin_lock(&desc->lock);
index 8e8d4cae5ebe731ff5b1c45723ec11ba4c13c68f..60b4e0fd9808aed53e9284bee352b862b053e770 100644 (file)
@@ -1,2 +1,3 @@
 obj-y  += setup.o pci.o time.o idle.o powersave.o iommu.o dma_lib.o misc.o
 obj-$(CONFIG_PPC_PASEMI_MDIO)  += gpio_mdio.o
+obj-$(CONFIG_PCI_MSI)          += msi.o
index b8f567b2ea1921eddef2f5687714904b83e75080..c929644e74a6dd1ca90430d54c4097dc95f382cf 100644 (file)
@@ -134,6 +134,10 @@ static void iobmap_free(struct iommu_table *tbl, long index,
        }
 }
 
+static struct iommu_table_ops iommu_table_iobmap_ops = {
+       .set = iobmap_build,
+       .clear  = iobmap_free
+};
 
 static void iommu_table_iobmap_setup(void)
 {
@@ -153,6 +157,7 @@ static void iommu_table_iobmap_setup(void)
         * Should probably be 8 (64 bytes)
         */
        iommu_table_iobmap.it_blocksize = 4;
+       iommu_table_iobmap.it_ops = &iommu_table_iobmap_ops;
        iommu_init_table(&iommu_table_iobmap, 0);
        pr_debug(" <- %s\n", __func__);
 }
@@ -252,8 +257,6 @@ void __init iommu_init_early_pasemi(void)
 
        pasemi_pci_controller_ops.dma_dev_setup = pci_dma_dev_setup_pasemi;
        pasemi_pci_controller_ops.dma_bus_setup = pci_dma_bus_setup_pasemi;
-       ppc_md.tce_build = iobmap_build;
-       ppc_md.tce_free  = iobmap_free;
        set_pci_dma_ops(&dma_iommu_ops);
 }
 
similarity index 93%
rename from arch/powerpc/sysdev/mpic_pasemi_msi.c
rename to arch/powerpc/platforms/pasemi/msi.c
index a3f660eed6deae7d95e121bee79aa42fae463887..27f2b187a91b5a17a15f4630defab5216eaf3e2d 100644 (file)
@@ -13,8 +13,6 @@
  *
  */
 
-#undef DEBUG
-
 #include <linux/irq.h>
 #include <linux/msi.h>
 #include <asm/mpic.h>
@@ -23,7 +21,7 @@
 #include <asm/ppc-pci.h>
 #include <asm/msi_bitmap.h>
 
-#include "mpic.h"
+#include <sysdev/mpic.h>
 
 /* Allocate 16 interrupts per device, to give an alignment of 16,
  * since that's the size of the grouping w.r.t. affinity. If someone
@@ -144,6 +142,7 @@ static int pasemi_msi_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type)
 int mpic_pasemi_msi_init(struct mpic *mpic)
 {
        int rc;
+       struct pci_controller *phb;
 
        if (!mpic->irqhost->of_node ||
            !of_device_is_compatible(mpic->irqhost->of_node,
@@ -159,9 +158,11 @@ int mpic_pasemi_msi_init(struct mpic *mpic)
        pr_debug("pasemi_msi: Registering PA Semi MPIC MSI callbacks\n");
 
        msi_mpic = mpic;
-       WARN_ON(ppc_md.setup_msi_irqs);
-       ppc_md.setup_msi_irqs = pasemi_msi_setup_msi_irqs;
-       ppc_md.teardown_msi_irqs = pasemi_msi_teardown_msi_irqs;
+       list_for_each_entry(phb, &hose_list, list_node) {
+               WARN_ON(phb->controller_ops.setup_msi_irqs);
+               phb->controller_ops.setup_msi_irqs = pasemi_msi_setup_msi_irqs;
+               phb->controller_ops.teardown_msi_irqs = pasemi_msi_teardown_msi_irqs;
+       }
 
        return 0;
 }
index 4b044d8cb49a36fefcdc335a88bf5fa35743bd28..604190cab5227c6fd8957baa00aa3ae6ca87a790 100644 (file)
@@ -19,3 +19,10 @@ config PPC_POWERNV
        select CPU_FREQ_GOV_CONSERVATIVE
        select PPC_DOORBELL
        default y
+
+config OPAL_PRD
+       tristate 'OPAL PRD driver'
+       depends on PPC_POWERNV
+       help
+         This enables the opal-prd driver, a facility to run processor
+         recovery diagnostics on OpenPower machines
index 33e44f37212f9fe785db5c554d274e051cb025d4..1c8cdb6250e7c15fb0b26620e0ba43756008cfbc 100644 (file)
@@ -1,7 +1,7 @@
-obj-y                  += setup.o opal-wrappers.o opal.o opal-async.o
+obj-y                  += setup.o opal-wrappers.o opal.o opal-async.o idle.o
 obj-y                  += opal-rtc.o opal-nvram.o opal-lpc.o opal-flash.o
 obj-y                  += rng.o opal-elog.o opal-dump.o opal-sysparam.o opal-sensor.o
-obj-y                  += opal-msglog.o opal-hmi.o opal-power.o
+obj-y                  += opal-msglog.o opal-hmi.o opal-power.o opal-irqchip.o
 
 obj-$(CONFIG_SMP)      += smp.o subcore.o subcore-asm.o
 obj-$(CONFIG_PCI)      += pci.o pci-p5ioc2.o pci-ioda.o
@@ -9,3 +9,4 @@ obj-$(CONFIG_EEH)       += eeh-powernv.o
 obj-$(CONFIG_PPC_SCOM) += opal-xscom.o
 obj-$(CONFIG_MEMORY_FAILURE)   += opal-memory-errors.o
 obj-$(CONFIG_TRACEPOINTS)      += opal-tracepoints.o
+obj-$(CONFIG_OPAL_PRD) += opal-prd.o
index ce738ab3d5a9f6b93dbcc4b10201f68624fce15e..5cf5e6ea213baaeee1b6e017636eae95aba6b0c6 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/delay.h>
 #include <linux/export.h>
 #include <linux/init.h>
+#include <linux/interrupt.h>
 #include <linux/list.h>
 #include <linux/msi.h>
 #include <linux/of.h>
@@ -40,6 +41,7 @@
 #include "pci.h"
 
 static bool pnv_eeh_nb_init = false;
+static int eeh_event_irq = -EINVAL;
 
 /**
  * pnv_eeh_init - EEH platform dependent initialization
@@ -88,34 +90,22 @@ static int pnv_eeh_init(void)
        return 0;
 }
 
-static int pnv_eeh_event(struct notifier_block *nb,
-                        unsigned long events, void *change)
+static irqreturn_t pnv_eeh_event(int irq, void *data)
 {
-       uint64_t changed_evts = (uint64_t)change;
-
        /*
-        * We simply send special EEH event if EEH has
-        * been enabled, or clear pending events in
-        * case that we enable EEH soon
+        * We simply send a special EEH event if EEH has been
+        * enabled. We don't care about EEH events until we've
+        * finished processing the outstanding ones. Event processing
+        * gets unmasked in next_error() if EEH is enabled.
         */
-       if (!(changed_evts & OPAL_EVENT_PCI_ERROR) ||
-           !(events & OPAL_EVENT_PCI_ERROR))
-               return 0;
+       disable_irq_nosync(irq);
 
        if (eeh_enabled())
                eeh_send_failure_event(NULL);
-       else
-               opal_notifier_update_evt(OPAL_EVENT_PCI_ERROR, 0x0ul);
 
-       return 0;
+       return IRQ_HANDLED;
 }
 
-static struct notifier_block pnv_eeh_nb = {
-       .notifier_call  = pnv_eeh_event,
-       .next           = NULL,
-       .priority       = 0
-};
-
 #ifdef CONFIG_DEBUG_FS
 static ssize_t pnv_eeh_ei_write(struct file *filp,
                                const char __user *user_buf,
@@ -237,16 +227,28 @@ static int pnv_eeh_post_init(void)
 
        /* Register OPAL event notifier */
        if (!pnv_eeh_nb_init) {
-               ret = opal_notifier_register(&pnv_eeh_nb);
-               if (ret) {
-                       pr_warn("%s: Can't register OPAL event notifier (%d)\n",
-                               __func__, ret);
+               eeh_event_irq = opal_event_request(ilog2(OPAL_EVENT_PCI_ERROR));
+               if (eeh_event_irq < 0) {
+                       pr_err("%s: Can't register OPAL event interrupt (%d)\n",
+                              __func__, eeh_event_irq);
+                       return eeh_event_irq;
+               }
+
+               ret = request_irq(eeh_event_irq, pnv_eeh_event,
+                               IRQ_TYPE_LEVEL_HIGH, "opal-eeh", NULL);
+               if (ret < 0) {
+                       irq_dispose_mapping(eeh_event_irq);
+                       pr_err("%s: Can't request OPAL event interrupt (%d)\n",
+                              __func__, eeh_event_irq);
                        return ret;
                }
 
                pnv_eeh_nb_init = true;
        }
 
+       if (!eeh_enabled())
+               disable_irq(eeh_event_irq);
+
        list_for_each_entry(hose, &hose_list, list_node) {
                phb = hose->private_data;
 
@@ -979,7 +981,7 @@ static int pnv_eeh_reset(struct eeh_pe *pe, int option)
 /**
  * pnv_eeh_wait_state - Wait for PE state
  * @pe: EEH PE
- * @max_wait: maximal period in microsecond
+ * @max_wait: maximal period in millisecond
  *
  * Wait for the state of associated PE. It might take some time
  * to retrieve the PE's state.
@@ -1000,13 +1002,13 @@ static int pnv_eeh_wait_state(struct eeh_pe *pe, int max_wait)
                if (ret != EEH_STATE_UNAVAILABLE)
                        return ret;
 
-               max_wait -= mwait;
                if (max_wait <= 0) {
                        pr_warn("%s: Timeout getting PE#%x's state (%d)\n",
                                __func__, pe->addr, max_wait);
                        return EEH_STATE_NOT_SUPPORT;
                }
 
+               max_wait -= mwait;
                msleep(mwait);
        }
 
@@ -1303,12 +1305,10 @@ static int pnv_eeh_next_error(struct eeh_pe **pe)
        int state, ret = EEH_NEXT_ERR_NONE;
 
        /*
-        * While running here, it's safe to purge the event queue.
-        * And we should keep the cached OPAL notifier event sychronized
-        * between the kernel and firmware.
+        * While running here, it's safe to purge the event queue. The
+        * event should still be masked.
         */
        eeh_remove_event(NULL, false);
-       opal_notifier_update_evt(OPAL_EVENT_PCI_ERROR, 0x0ul);
 
        list_for_each_entry(hose, &hose_list, list_node) {
                /*
@@ -1477,6 +1477,10 @@ static int pnv_eeh_next_error(struct eeh_pe **pe)
                        break;
        }
 
+       /* Unmask the event */
+       if (eeh_enabled())
+               enable_irq(eeh_event_irq);
+
        return ret;
 }
 
diff --git a/arch/powerpc/platforms/powernv/idle.c b/arch/powerpc/platforms/powernv/idle.c
new file mode 100644 (file)
index 0000000..59d735d
--- /dev/null
@@ -0,0 +1,293 @@
+/*
+ * PowerNV cpuidle code
+ *
+ * Copyright 2015 IBM Corp.
+ *
+ * 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/types.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/device.h>
+#include <linux/cpu.h>
+
+#include <asm/firmware.h>
+#include <asm/machdep.h>
+#include <asm/opal.h>
+#include <asm/cputhreads.h>
+#include <asm/cpuidle.h>
+#include <asm/code-patching.h>
+#include <asm/smp.h>
+
+#include "powernv.h"
+#include "subcore.h"
+
+static u32 supported_cpuidle_states;
+
+int pnv_save_sprs_for_winkle(void)
+{
+       int cpu;
+       int rc;
+
+       /*
+        * hid0, hid1, hid4, hid5, hmeer and lpcr values are symmetric accross
+        * all cpus at boot. Get these reg values of current cpu and use the
+        * same accross all cpus.
+        */
+       uint64_t lpcr_val = mfspr(SPRN_LPCR) & ~(u64)LPCR_PECE1;
+       uint64_t hid0_val = mfspr(SPRN_HID0);
+       uint64_t hid1_val = mfspr(SPRN_HID1);
+       uint64_t hid4_val = mfspr(SPRN_HID4);
+       uint64_t hid5_val = mfspr(SPRN_HID5);
+       uint64_t hmeer_val = mfspr(SPRN_HMEER);
+
+       for_each_possible_cpu(cpu) {
+               uint64_t pir = get_hard_smp_processor_id(cpu);
+               uint64_t hsprg0_val = (uint64_t)&paca[cpu];
+
+               /*
+                * HSPRG0 is used to store the cpu's pointer to paca. Hence last
+                * 3 bits are guaranteed to be 0. Program slw to restore HSPRG0
+                * with 63rd bit set, so that when a thread wakes up at 0x100 we
+                * can use this bit to distinguish between fastsleep and
+                * deep winkle.
+                */
+               hsprg0_val |= 1;
+
+               rc = opal_slw_set_reg(pir, SPRN_HSPRG0, hsprg0_val);
+               if (rc != 0)
+                       return rc;
+
+               rc = opal_slw_set_reg(pir, SPRN_LPCR, lpcr_val);
+               if (rc != 0)
+                       return rc;
+
+               /* HIDs are per core registers */
+               if (cpu_thread_in_core(cpu) == 0) {
+
+                       rc = opal_slw_set_reg(pir, SPRN_HMEER, hmeer_val);
+                       if (rc != 0)
+                               return rc;
+
+                       rc = opal_slw_set_reg(pir, SPRN_HID0, hid0_val);
+                       if (rc != 0)
+                               return rc;
+
+                       rc = opal_slw_set_reg(pir, SPRN_HID1, hid1_val);
+                       if (rc != 0)
+                               return rc;
+
+                       rc = opal_slw_set_reg(pir, SPRN_HID4, hid4_val);
+                       if (rc != 0)
+                               return rc;
+
+                       rc = opal_slw_set_reg(pir, SPRN_HID5, hid5_val);
+                       if (rc != 0)
+                               return rc;
+               }
+       }
+
+       return 0;
+}
+
+static void pnv_alloc_idle_core_states(void)
+{
+       int i, j;
+       int nr_cores = cpu_nr_cores();
+       u32 *core_idle_state;
+
+       /*
+        * core_idle_state - First 8 bits track the idle state of each thread
+        * of the core. The 8th bit is the lock bit. Initially all thread bits
+        * are set. They are cleared when the thread enters deep idle state
+        * like sleep and winkle. Initially the lock bit is cleared.
+        * The lock bit has 2 purposes
+        * a. While the first thread is restoring core state, it prevents
+        * other threads in the core from switching to process context.
+        * b. While the last thread in the core is saving the core state, it
+        * prevents a different thread from waking up.
+        */
+       for (i = 0; i < nr_cores; i++) {
+               int first_cpu = i * threads_per_core;
+               int node = cpu_to_node(first_cpu);
+
+               core_idle_state = kmalloc_node(sizeof(u32), GFP_KERNEL, node);
+               *core_idle_state = PNV_CORE_IDLE_THREAD_BITS;
+
+               for (j = 0; j < threads_per_core; j++) {
+                       int cpu = first_cpu + j;
+
+                       paca[cpu].core_idle_state_ptr = core_idle_state;
+                       paca[cpu].thread_idle_state = PNV_THREAD_RUNNING;
+                       paca[cpu].thread_mask = 1 << j;
+               }
+       }
+
+       update_subcore_sibling_mask();
+
+       if (supported_cpuidle_states & OPAL_PM_WINKLE_ENABLED)
+               pnv_save_sprs_for_winkle();
+}
+
+u32 pnv_get_supported_cpuidle_states(void)
+{
+       return supported_cpuidle_states;
+}
+EXPORT_SYMBOL_GPL(pnv_get_supported_cpuidle_states);
+
+
+static void pnv_fastsleep_workaround_apply(void *info)
+
+{
+       int rc;
+       int *err = info;
+
+       rc = opal_config_cpu_idle_state(OPAL_CONFIG_IDLE_FASTSLEEP,
+                                       OPAL_CONFIG_IDLE_APPLY);
+       if (rc)
+               *err = 1;
+}
+
+/*
+ * Used to store fastsleep workaround state
+ * 0 - Workaround applied/undone at fastsleep entry/exit path (Default)
+ * 1 - Workaround applied once, never undone.
+ */
+static u8 fastsleep_workaround_applyonce;
+
+static ssize_t show_fastsleep_workaround_applyonce(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       return sprintf(buf, "%u\n", fastsleep_workaround_applyonce);
+}
+
+static ssize_t store_fastsleep_workaround_applyonce(struct device *dev,
+               struct device_attribute *attr, const char *buf,
+               size_t count)
+{
+       cpumask_t primary_thread_mask;
+       int err;
+       u8 val;
+
+       if (kstrtou8(buf, 0, &val) || val != 1)
+               return -EINVAL;
+
+       if (fastsleep_workaround_applyonce == 1)
+               return count;
+
+       /*
+        * fastsleep_workaround_applyonce = 1 implies
+        * fastsleep workaround needs to be left in 'applied' state on all
+        * the cores. Do this by-
+        * 1. Patching out the call to 'undo' workaround in fastsleep exit path
+        * 2. Sending ipi to all the cores which have atleast one online thread
+        * 3. Patching out the call to 'apply' workaround in fastsleep entry
+        * path
+        * There is no need to send ipi to cores which have all threads
+        * offlined, as last thread of the core entering fastsleep or deeper
+        * state would have applied workaround.
+        */
+       err = patch_instruction(
+               (unsigned int *)pnv_fastsleep_workaround_at_exit,
+               PPC_INST_NOP);
+       if (err) {
+               pr_err("fastsleep_workaround_applyonce change failed while patching pnv_fastsleep_workaround_at_exit");
+               goto fail;
+       }
+
+       get_online_cpus();
+       primary_thread_mask = cpu_online_cores_map();
+       on_each_cpu_mask(&primary_thread_mask,
+                               pnv_fastsleep_workaround_apply,
+                               &err, 1);
+       put_online_cpus();
+       if (err) {
+               pr_err("fastsleep_workaround_applyonce change failed while running pnv_fastsleep_workaround_apply");
+               goto fail;
+       }
+
+       err = patch_instruction(
+               (unsigned int *)pnv_fastsleep_workaround_at_entry,
+               PPC_INST_NOP);
+       if (err) {
+               pr_err("fastsleep_workaround_applyonce change failed while patching pnv_fastsleep_workaround_at_entry");
+               goto fail;
+       }
+
+       fastsleep_workaround_applyonce = 1;
+
+       return count;
+fail:
+       return -EIO;
+}
+
+static DEVICE_ATTR(fastsleep_workaround_applyonce, 0600,
+                       show_fastsleep_workaround_applyonce,
+                       store_fastsleep_workaround_applyonce);
+
+static int __init pnv_init_idle_states(void)
+{
+       struct device_node *power_mgt;
+       int dt_idle_states;
+       u32 *flags;
+       int i;
+
+       supported_cpuidle_states = 0;
+
+       if (cpuidle_disable != IDLE_NO_OVERRIDE)
+               goto out;
+
+       if (!firmware_has_feature(FW_FEATURE_OPALv3))
+               goto out;
+
+       power_mgt = of_find_node_by_path("/ibm,opal/power-mgt");
+       if (!power_mgt) {
+               pr_warn("opal: PowerMgmt Node not found\n");
+               goto out;
+       }
+       dt_idle_states = of_property_count_u32_elems(power_mgt,
+                       "ibm,cpu-idle-state-flags");
+       if (dt_idle_states < 0) {
+               pr_warn("cpuidle-powernv: no idle states found in the DT\n");
+               goto out;
+       }
+
+       flags = kzalloc(sizeof(*flags) * dt_idle_states, GFP_KERNEL);
+       if (of_property_read_u32_array(power_mgt,
+                       "ibm,cpu-idle-state-flags", flags, dt_idle_states)) {
+               pr_warn("cpuidle-powernv: missing ibm,cpu-idle-state-flags in DT\n");
+               goto out_free;
+       }
+
+       for (i = 0; i < dt_idle_states; i++)
+               supported_cpuidle_states |= flags[i];
+
+       if (!(supported_cpuidle_states & OPAL_PM_SLEEP_ENABLED_ER1)) {
+               patch_instruction(
+                       (unsigned int *)pnv_fastsleep_workaround_at_entry,
+                       PPC_INST_NOP);
+               patch_instruction(
+                       (unsigned int *)pnv_fastsleep_workaround_at_exit,
+                       PPC_INST_NOP);
+       } else {
+               /*
+                * OPAL_PM_SLEEP_ENABLED_ER1 is set. It indicates that
+                * workaround is needed to use fastsleep. Provide sysfs
+                * control to choose how this workaround has to be applied.
+                */
+               device_create_file(cpu_subsys.dev_root,
+                               &dev_attr_fastsleep_workaround_applyonce);
+       }
+
+       pnv_alloc_idle_core_states();
+out_free:
+       kfree(flags);
+out:
+       return 0;
+}
+machine_subsys_initcall(powernv, pnv_init_idle_states);
index 693b6cdac691b63057890e6b1152f87de5bf4b13..bdc8c0c71d156483619f17db2f81e50722c0be7c 100644 (file)
@@ -151,7 +151,7 @@ static struct notifier_block opal_async_comp_nb = {
                .priority       = 0,
 };
 
-static int __init opal_async_comp_init(void)
+int __init opal_async_comp_init(void)
 {
        struct device_node *opal_node;
        const __be32 *async;
@@ -205,4 +205,3 @@ out_opal_node:
 out:
        return err;
 }
-machine_subsys_initcall(powernv, opal_async_comp_init);
index 5aa9c1ce4de3eabd8476211d7323622dd7c60254..2ee96431f7360e1d8d63ece45520416860c62428 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/vmalloc.h>
 #include <linux/pagemap.h>
 #include <linux/delay.h>
+#include <linux/interrupt.h>
 
 #include <asm/opal.h>
 
@@ -60,7 +61,7 @@ static ssize_t dump_type_show(struct dump_obj *dump_obj,
                              struct dump_attribute *attr,
                              char *buf)
 {
-       
+
        return sprintf(buf, "0x%x %s\n", dump_obj->type,
                       dump_type_to_string(dump_obj->type));
 }
@@ -363,7 +364,7 @@ static struct dump_obj *create_dump_obj(uint32_t id, size_t size,
        return dump;
 }
 
-static int process_dump(void)
+static irqreturn_t process_dump(int irq, void *data)
 {
        int rc;
        uint32_t dump_id, dump_size, dump_type;
@@ -387,45 +388,13 @@ static int process_dump(void)
        if (!dump)
                return -1;
 
-       return 0;
-}
-
-static void dump_work_fn(struct work_struct *work)
-{
-       process_dump();
+       return IRQ_HANDLED;
 }
 
-static DECLARE_WORK(dump_work, dump_work_fn);
-
-static void schedule_process_dump(void)
-{
-       schedule_work(&dump_work);
-}
-
-/*
- * New dump available notification
- *
- * Once we get notification, we add sysfs entries for it.
- * We only fetch the dump on demand, and create sysfs asynchronously.
- */
-static int dump_event(struct notifier_block *nb,
-                     unsigned long events, void *change)
-{
-       if (events & OPAL_EVENT_DUMP_AVAIL)
-               schedule_process_dump();
-
-       return 0;
-}
-
-static struct notifier_block dump_nb = {
-       .notifier_call  = dump_event,
-       .next           = NULL,
-       .priority       = 0
-};
-
 void __init opal_platform_dump_init(void)
 {
        int rc;
+       int dump_irq;
 
        /* ELOG not supported by firmware */
        if (!opal_check_token(OPAL_DUMP_READ))
@@ -445,10 +414,19 @@ void __init opal_platform_dump_init(void)
                return;
        }
 
-       rc = opal_notifier_register(&dump_nb);
+       dump_irq = opal_event_request(ilog2(OPAL_EVENT_DUMP_AVAIL));
+       if (!dump_irq) {
+               pr_err("%s: Can't register OPAL event irq (%d)\n",
+                      __func__, dump_irq);
+               return;
+       }
+
+       rc = request_threaded_irq(dump_irq, NULL, process_dump,
+                               IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
+                               "opal-dump", NULL);
        if (rc) {
-               pr_warn("%s: Can't register OPAL event notifier (%d)\n",
-                       __func__, rc);
+               pr_err("%s: Can't request OPAL event irq (%d)\n",
+                      __func__, rc);
                return;
        }
 
index 38ce757e5e2af59354ace706976e292b62d3d1d9..4949ef0d94004e0f315f90e37cc2a50ce3582584 100644 (file)
@@ -10,6 +10,7 @@
  */
 #include <linux/kernel.h>
 #include <linux/init.h>
+#include <linux/interrupt.h>
 #include <linux/of.h>
 #include <linux/slab.h>
 #include <linux/sysfs.h>
@@ -276,24 +277,15 @@ static void elog_work_fn(struct work_struct *work)
 
 static DECLARE_WORK(elog_work, elog_work_fn);
 
-static int elog_event(struct notifier_block *nb,
-                               unsigned long events, void *change)
+static irqreturn_t elog_event(int irq, void *data)
 {
-       /* check for error log event */
-       if (events & OPAL_EVENT_ERROR_LOG_AVAIL)
-               schedule_work(&elog_work);
-       return 0;
+       schedule_work(&elog_work);
+       return IRQ_HANDLED;
 }
 
-static struct notifier_block elog_nb = {
-       .notifier_call  = elog_event,
-       .next           = NULL,
-       .priority       = 0
-};
-
 int __init opal_elog_init(void)
 {
-       int rc = 0;
+       int rc = 0, irq;
 
        /* ELOG not supported by firmware */
        if (!opal_check_token(OPAL_ELOG_READ))
@@ -305,10 +297,18 @@ int __init opal_elog_init(void)
                return -1;
        }
 
-       rc = opal_notifier_register(&elog_nb);
+       irq = opal_event_request(ilog2(OPAL_EVENT_ERROR_LOG_AVAIL));
+       if (!irq) {
+               pr_err("%s: Can't register OPAL event irq (%d)\n",
+                      __func__, irq);
+               return irq;
+       }
+
+       rc = request_irq(irq, elog_event,
+                       IRQ_TYPE_LEVEL_HIGH, "opal-elog", NULL);
        if (rc) {
-               pr_err("%s: Can't register OPAL event notifier (%d)\n",
-               __func__, rc);
+               pr_err("%s: Can't request OPAL event irq (%d)\n",
+                      __func__, rc);
                return rc;
        }
 
index b322bfb51343f65fdfe76d265cdcb76928011d21..a8f49d380449bf172a9f9361389b9beae52bc2e2 100644 (file)
@@ -170,7 +170,7 @@ static struct notifier_block opal_hmi_handler_nb = {
        .priority       = 0,
 };
 
-static int __init opal_hmi_handler_init(void)
+int __init opal_hmi_handler_init(void)
 {
        int ret;
 
@@ -186,4 +186,3 @@ static int __init opal_hmi_handler_init(void)
        }
        return 0;
 }
-machine_subsys_initcall(powernv, opal_hmi_handler_init);
diff --git a/arch/powerpc/platforms/powernv/opal-irqchip.c b/arch/powerpc/platforms/powernv/opal-irqchip.c
new file mode 100644 (file)
index 0000000..e2e7d75
--- /dev/null
@@ -0,0 +1,253 @@
+/*
+ * This file implements an irqchip for OPAL events. Whenever there is
+ * an interrupt that is handled by OPAL we get passed a list of events
+ * that Linux needs to do something about. These basically look like
+ * interrupts to Linux so we implement an irqchip to handle them.
+ *
+ * Copyright Alistair Popple, IBM Corporation 2014.
+ *
+ * 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/bitops.h>
+#include <linux/irq.h>
+#include <linux/irqchip.h>
+#include <linux/irqdomain.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/kthread.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/irq_work.h>
+
+#include <asm/machdep.h>
+#include <asm/opal.h>
+
+#include "powernv.h"
+
+/* Maximum number of events supported by OPAL firmware */
+#define MAX_NUM_EVENTS 64
+
+struct opal_event_irqchip {
+       struct irq_chip irqchip;
+       struct irq_domain *domain;
+       unsigned long mask;
+};
+static struct opal_event_irqchip opal_event_irqchip;
+
+static unsigned int opal_irq_count;
+static unsigned int *opal_irqs;
+
+static void opal_handle_irq_work(struct irq_work *work);
+static __be64 last_outstanding_events;
+static struct irq_work opal_event_irq_work = {
+       .func = opal_handle_irq_work,
+};
+
+static void opal_event_mask(struct irq_data *d)
+{
+       clear_bit(d->hwirq, &opal_event_irqchip.mask);
+}
+
+static void opal_event_unmask(struct irq_data *d)
+{
+       set_bit(d->hwirq, &opal_event_irqchip.mask);
+
+       opal_poll_events(&last_outstanding_events);
+       if (last_outstanding_events & opal_event_irqchip.mask)
+               /* Need to retrigger the interrupt */
+               irq_work_queue(&opal_event_irq_work);
+}
+
+static int opal_event_set_type(struct irq_data *d, unsigned int flow_type)
+{
+       /*
+        * For now we only support level triggered events. The irq
+        * handler will be called continuously until the event has
+        * been cleared in OPAL.
+        */
+       if (flow_type != IRQ_TYPE_LEVEL_HIGH)
+               return -EINVAL;
+
+       return 0;
+}
+
+static struct opal_event_irqchip opal_event_irqchip = {
+       .irqchip = {
+               .name = "OPAL EVT",
+               .irq_mask = opal_event_mask,
+               .irq_unmask = opal_event_unmask,
+               .irq_set_type = opal_event_set_type,
+       },
+       .mask = 0,
+};
+
+static int opal_event_map(struct irq_domain *d, unsigned int irq,
+                       irq_hw_number_t hwirq)
+{
+       irq_set_chip_data(irq, &opal_event_irqchip);
+       irq_set_chip_and_handler(irq, &opal_event_irqchip.irqchip,
+                               handle_level_irq);
+
+       return 0;
+}
+
+void opal_handle_events(uint64_t events)
+{
+       int virq, hwirq = 0;
+       u64 mask = opal_event_irqchip.mask;
+
+       if (!in_irq() && (events & mask)) {
+               last_outstanding_events = events;
+               irq_work_queue(&opal_event_irq_work);
+               return;
+       }
+
+       while (events & mask) {
+               hwirq = fls64(events) - 1;
+               if (BIT_ULL(hwirq) & mask) {
+                       virq = irq_find_mapping(opal_event_irqchip.domain,
+                                               hwirq);
+                       if (virq)
+                               generic_handle_irq(virq);
+               }
+               events &= ~BIT_ULL(hwirq);
+       }
+}
+
+static irqreturn_t opal_interrupt(int irq, void *data)
+{
+       __be64 events;
+
+       opal_handle_interrupt(virq_to_hw(irq), &events);
+       opal_handle_events(be64_to_cpu(events));
+
+       return IRQ_HANDLED;
+}
+
+static void opal_handle_irq_work(struct irq_work *work)
+{
+       opal_handle_events(be64_to_cpu(last_outstanding_events));
+}
+
+static int opal_event_match(struct irq_domain *h, struct device_node *node)
+{
+       return h->of_node == node;
+}
+
+static int opal_event_xlate(struct irq_domain *h, struct device_node *np,
+                          const u32 *intspec, unsigned int intsize,
+                          irq_hw_number_t *out_hwirq, unsigned int *out_flags)
+{
+       *out_hwirq = intspec[0];
+       *out_flags = IRQ_TYPE_LEVEL_HIGH;
+
+       return 0;
+}
+
+static const struct irq_domain_ops opal_event_domain_ops = {
+       .match  = opal_event_match,
+       .map    = opal_event_map,
+       .xlate  = opal_event_xlate,
+};
+
+void opal_event_shutdown(void)
+{
+       unsigned int i;
+
+       /* First free interrupts, which will also mask them */
+       for (i = 0; i < opal_irq_count; i++) {
+               if (opal_irqs[i])
+                       free_irq(opal_irqs[i], NULL);
+               opal_irqs[i] = 0;
+       }
+}
+
+int __init opal_event_init(void)
+{
+       struct device_node *dn, *opal_node;
+       const __be32 *irqs;
+       int i, irqlen, rc = 0;
+
+       opal_node = of_find_node_by_path("/ibm,opal");
+       if (!opal_node) {
+               pr_warn("opal: Node not found\n");
+               return -ENODEV;
+       }
+
+       /* If dn is NULL it means the domain won't be linked to a DT
+        * node so therefore irq_of_parse_and_map(...) wont work. But
+        * that shouldn't be problem because if we're running a
+        * version of skiboot that doesn't have the dn then the
+        * devices won't have the correct properties and will have to
+        * fall back to the legacy method (opal_event_request(...))
+        * anyway. */
+       dn = of_find_compatible_node(NULL, NULL, "ibm,opal-event");
+       opal_event_irqchip.domain = irq_domain_add_linear(dn, MAX_NUM_EVENTS,
+                               &opal_event_domain_ops, &opal_event_irqchip);
+       of_node_put(dn);
+       if (!opal_event_irqchip.domain) {
+               pr_warn("opal: Unable to create irq domain\n");
+               rc = -ENOMEM;
+               goto out;
+       }
+
+       /* Get interrupt property */
+       irqs = of_get_property(opal_node, "opal-interrupts", &irqlen);
+       opal_irq_count = irqs ? (irqlen / 4) : 0;
+       pr_debug("Found %d interrupts reserved for OPAL\n", opal_irq_count);
+
+       /* Install interrupt handlers */
+       opal_irqs = kcalloc(opal_irq_count, sizeof(*opal_irqs), GFP_KERNEL);
+       for (i = 0; irqs && i < opal_irq_count; i++, irqs++) {
+               unsigned int irq, virq;
+
+               /* Get hardware and virtual IRQ */
+               irq = be32_to_cpup(irqs);
+               virq = irq_create_mapping(NULL, irq);
+               if (virq == NO_IRQ) {
+                       pr_warn("Failed to map irq 0x%x\n", irq);
+                       continue;
+               }
+
+               /* Install interrupt handler */
+               rc = request_irq(virq, opal_interrupt, 0, "opal", NULL);
+               if (rc) {
+                       irq_dispose_mapping(virq);
+                       pr_warn("Error %d requesting irq %d (0x%x)\n",
+                                rc, virq, irq);
+                       continue;
+               }
+
+               /* Cache IRQ */
+               opal_irqs[i] = virq;
+       }
+
+out:
+       of_node_put(opal_node);
+       return rc;
+}
+machine_arch_initcall(powernv, opal_event_init);
+
+/**
+ * opal_event_request(unsigned int opal_event_nr) - Request an event
+ * @opal_event_nr: the opal event number to request
+ *
+ * This routine can be used to find the linux virq number which can
+ * then be passed to request_irq to assign a handler for a particular
+ * opal event. This should only be used by legacy devices which don't
+ * have proper device tree bindings. Most devices should use
+ * irq_of_parse_and_map() instead.
+ */
+int opal_event_request(unsigned int opal_event_nr)
+{
+       if (WARN_ON_ONCE(!opal_event_irqchip.domain))
+               return NO_IRQ;
+
+       return irq_create_mapping(opal_event_irqchip.domain, opal_event_nr);
+}
+EXPORT_SYMBOL(opal_event_request);
index 43db2136dbff8f67101d085b8e5ecef7f75dacdb..00a29432be39fb60f6209a625c98ee8b54504bb0 100644 (file)
@@ -144,4 +144,4 @@ static int __init opal_mem_err_init(void)
        }
        return 0;
 }
-machine_subsys_initcall(powernv, opal_mem_err_init);
+machine_device_initcall(powernv, opal_mem_err_init);
diff --git a/arch/powerpc/platforms/powernv/opal-prd.c b/arch/powerpc/platforms/powernv/opal-prd.c
new file mode 100644 (file)
index 0000000..46cb3fe
--- /dev/null
@@ -0,0 +1,449 @@
+/*
+ * OPAL Runtime Diagnostics interface driver
+ * Supported on POWERNV platform
+ *
+ * Copyright IBM Corporation 2015
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "opal-prd: " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/miscdevice.h>
+#include <linux/fs.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/poll.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <asm/opal-prd.h>
+#include <asm/opal.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+
+
+/**
+ * The msg member must be at the end of the struct, as it's followed by the
+ * message data.
+ */
+struct opal_prd_msg_queue_item {
+       struct list_head                list;
+       struct opal_prd_msg_header      msg;
+};
+
+static struct device_node *prd_node;
+static LIST_HEAD(opal_prd_msg_queue);
+static DEFINE_SPINLOCK(opal_prd_msg_queue_lock);
+static DECLARE_WAIT_QUEUE_HEAD(opal_prd_msg_wait);
+static atomic_t prd_usage;
+
+static bool opal_prd_range_is_valid(uint64_t addr, uint64_t size)
+{
+       struct device_node *parent, *node;
+       bool found;
+
+       if (addr + size < addr)
+               return false;
+
+       parent = of_find_node_by_path("/reserved-memory");
+       if (!parent)
+               return false;
+
+       found = false;
+
+       for_each_child_of_node(parent, node) {
+               uint64_t range_addr, range_size, range_end;
+               const __be32 *addrp;
+               const char *label;
+
+               addrp = of_get_address(node, 0, &range_size, NULL);
+
+               range_addr = of_read_number(addrp, 2);
+               range_end = range_addr + range_size;
+
+               label = of_get_property(node, "ibm,prd-label", NULL);
+
+               /* PRD ranges need a label */
+               if (!label)
+                       continue;
+
+               if (range_end <= range_addr)
+                       continue;
+
+               if (addr >= range_addr && addr + size <= range_end) {
+                       found = true;
+                       of_node_put(node);
+                       break;
+               }
+       }
+
+       of_node_put(parent);
+       return found;
+}
+
+static int opal_prd_open(struct inode *inode, struct file *file)
+{
+       /*
+        * Prevent multiple (separate) processes from concurrent interactions
+        * with the FW PRD channel
+        */
+       if (atomic_xchg(&prd_usage, 1) == 1)
+               return -EBUSY;
+
+       return 0;
+}
+
+/*
+ * opal_prd_mmap - maps firmware-provided ranges into userspace
+ * @file: file structure for the device
+ * @vma: VMA to map the registers into
+ */
+
+static int opal_prd_mmap(struct file *file, struct vm_area_struct *vma)
+{
+       size_t addr, size;
+       int rc;
+
+       pr_devel("opal_prd_mmap(0x%016lx, 0x%016lx, 0x%lx, 0x%lx)\n",
+                       vma->vm_start, vma->vm_end, vma->vm_pgoff,
+                       vma->vm_flags);
+
+       addr = vma->vm_pgoff << PAGE_SHIFT;
+       size = vma->vm_end - vma->vm_start;
+
+       /* ensure we're mapping within one of the allowable ranges */
+       if (!opal_prd_range_is_valid(addr, size))
+               return -EINVAL;
+
+       vma->vm_page_prot = __pgprot(pgprot_val(phys_mem_access_prot(file,
+                                               vma->vm_pgoff,
+                                                size, vma->vm_page_prot))
+                                       | _PAGE_SPECIAL);
+
+       rc = remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, size,
+                       vma->vm_page_prot);
+
+       return rc;
+}
+
+static bool opal_msg_queue_empty(void)
+{
+       unsigned long flags;
+       bool ret;
+
+       spin_lock_irqsave(&opal_prd_msg_queue_lock, flags);
+       ret = list_empty(&opal_prd_msg_queue);
+       spin_unlock_irqrestore(&opal_prd_msg_queue_lock, flags);
+
+       return ret;
+}
+
+static unsigned int opal_prd_poll(struct file *file,
+               struct poll_table_struct *wait)
+{
+       poll_wait(file, &opal_prd_msg_wait, wait);
+
+       if (!opal_msg_queue_empty())
+               return POLLIN | POLLRDNORM;
+
+       return 0;
+}
+
+static ssize_t opal_prd_read(struct file *file, char __user *buf,
+               size_t count, loff_t *ppos)
+{
+       struct opal_prd_msg_queue_item *item;
+       unsigned long flags;
+       ssize_t size, err;
+       int rc;
+
+       /* we need at least a header's worth of data */
+       if (count < sizeof(item->msg))
+               return -EINVAL;
+
+       if (*ppos)
+               return -ESPIPE;
+
+       item = NULL;
+
+       for (;;) {
+
+               spin_lock_irqsave(&opal_prd_msg_queue_lock, flags);
+               if (!list_empty(&opal_prd_msg_queue)) {
+                       item = list_first_entry(&opal_prd_msg_queue,
+                                       struct opal_prd_msg_queue_item, list);
+                       list_del(&item->list);
+               }
+               spin_unlock_irqrestore(&opal_prd_msg_queue_lock, flags);
+
+               if (item)
+                       break;
+
+               if (file->f_flags & O_NONBLOCK)
+                       return -EAGAIN;
+
+               rc = wait_event_interruptible(opal_prd_msg_wait,
+                               !opal_msg_queue_empty());
+               if (rc)
+                       return -EINTR;
+       }
+
+       size = be16_to_cpu(item->msg.size);
+       if (size > count) {
+               err = -EINVAL;
+               goto err_requeue;
+       }
+
+       rc = copy_to_user(buf, &item->msg, size);
+       if (rc) {
+               err = -EFAULT;
+               goto err_requeue;
+       }
+
+       kfree(item);
+
+       return size;
+
+err_requeue:
+       /* eep! re-queue at the head of the list */
+       spin_lock_irqsave(&opal_prd_msg_queue_lock, flags);
+       list_add(&item->list, &opal_prd_msg_queue);
+       spin_unlock_irqrestore(&opal_prd_msg_queue_lock, flags);
+       return err;
+}
+
+static ssize_t opal_prd_write(struct file *file, const char __user *buf,
+               size_t count, loff_t *ppos)
+{
+       struct opal_prd_msg_header hdr;
+       ssize_t size;
+       void *msg;
+       int rc;
+
+       size = sizeof(hdr);
+
+       if (count < size)
+               return -EINVAL;
+
+       /* grab the header */
+       rc = copy_from_user(&hdr, buf, sizeof(hdr));
+       if (rc)
+               return -EFAULT;
+
+       size = be16_to_cpu(hdr.size);
+
+       msg = kmalloc(size, GFP_KERNEL);
+       if (!msg)
+               return -ENOMEM;
+
+       rc = copy_from_user(msg, buf, size);
+       if (rc) {
+               size = -EFAULT;
+               goto out_free;
+       }
+
+       rc = opal_prd_msg(msg);
+       if (rc) {
+               pr_warn("write: opal_prd_msg returned %d\n", rc);
+               size = -EIO;
+       }
+
+out_free:
+       kfree(msg);
+
+       return size;
+}
+
+static int opal_prd_release(struct inode *inode, struct file *file)
+{
+       struct opal_prd_msg_header msg;
+
+       msg.size = cpu_to_be16(sizeof(msg));
+       msg.type = OPAL_PRD_MSG_TYPE_FINI;
+
+       opal_prd_msg((struct opal_prd_msg *)&msg);
+
+       atomic_xchg(&prd_usage, 0);
+
+       return 0;
+}
+
+static long opal_prd_ioctl(struct file *file, unsigned int cmd,
+               unsigned long param)
+{
+       struct opal_prd_info info;
+       struct opal_prd_scom scom;
+       int rc = 0;
+
+       switch (cmd) {
+       case OPAL_PRD_GET_INFO:
+               memset(&info, 0, sizeof(info));
+               info.version = OPAL_PRD_KERNEL_VERSION;
+               rc = copy_to_user((void __user *)param, &info, sizeof(info));
+               if (rc)
+                       return -EFAULT;
+               break;
+
+       case OPAL_PRD_SCOM_READ:
+               rc = copy_from_user(&scom, (void __user *)param, sizeof(scom));
+               if (rc)
+                       return -EFAULT;
+
+               scom.rc = opal_xscom_read(scom.chip, scom.addr,
+                               (__be64 *)&scom.data);
+               scom.data = be64_to_cpu(scom.data);
+               pr_devel("ioctl SCOM_READ: chip %llx addr %016llx data %016llx rc %lld\n",
+                               scom.chip, scom.addr, scom.data, scom.rc);
+
+               rc = copy_to_user((void __user *)param, &scom, sizeof(scom));
+               if (rc)
+                       return -EFAULT;
+               break;
+
+       case OPAL_PRD_SCOM_WRITE:
+               rc = copy_from_user(&scom, (void __user *)param, sizeof(scom));
+               if (rc)
+                       return -EFAULT;
+
+               scom.rc = opal_xscom_write(scom.chip, scom.addr, scom.data);
+               pr_devel("ioctl SCOM_WRITE: chip %llx addr %016llx data %016llx rc %lld\n",
+                               scom.chip, scom.addr, scom.data, scom.rc);
+
+               rc = copy_to_user((void __user *)param, &scom, sizeof(scom));
+               if (rc)
+                       return -EFAULT;
+               break;
+
+       default:
+               rc = -EINVAL;
+       }
+
+       return rc;
+}
+
+static const struct file_operations opal_prd_fops = {
+       .open           = opal_prd_open,
+       .mmap           = opal_prd_mmap,
+       .poll           = opal_prd_poll,
+       .read           = opal_prd_read,
+       .write          = opal_prd_write,
+       .unlocked_ioctl = opal_prd_ioctl,
+       .release        = opal_prd_release,
+       .owner          = THIS_MODULE,
+};
+
+static struct miscdevice opal_prd_dev = {
+       .minor          = MISC_DYNAMIC_MINOR,
+       .name           = "opal-prd",
+       .fops           = &opal_prd_fops,
+};
+
+/* opal interface */
+static int opal_prd_msg_notifier(struct notifier_block *nb,
+               unsigned long msg_type, void *_msg)
+{
+       struct opal_prd_msg_queue_item *item;
+       struct opal_prd_msg_header *hdr;
+       struct opal_msg *msg = _msg;
+       int msg_size, item_size;
+       unsigned long flags;
+
+       if (msg_type != OPAL_MSG_PRD)
+               return 0;
+
+       /* Calculate total size of the message and item we need to store. The
+        * 'size' field in the header includes the header itself. */
+       hdr = (void *)msg->params;
+       msg_size = be16_to_cpu(hdr->size);
+       item_size = msg_size + sizeof(*item) - sizeof(item->msg);
+
+       item = kzalloc(item_size, GFP_ATOMIC);
+       if (!item)
+               return -ENOMEM;
+
+       memcpy(&item->msg, msg->params, msg_size);
+
+       spin_lock_irqsave(&opal_prd_msg_queue_lock, flags);
+       list_add_tail(&item->list, &opal_prd_msg_queue);
+       spin_unlock_irqrestore(&opal_prd_msg_queue_lock, flags);
+
+       wake_up_interruptible(&opal_prd_msg_wait);
+
+       return 0;
+}
+
+static struct notifier_block opal_prd_event_nb = {
+       .notifier_call  = opal_prd_msg_notifier,
+       .next           = NULL,
+       .priority       = 0,
+};
+
+static int opal_prd_probe(struct platform_device *pdev)
+{
+       int rc;
+
+       if (!pdev || !pdev->dev.of_node)
+               return -ENODEV;
+
+       /* We should only have one prd driver instance per machine; ensure
+        * that we only get a valid probe on a single OF node.
+        */
+       if (prd_node)
+               return -EBUSY;
+
+       prd_node = pdev->dev.of_node;
+
+       rc = opal_message_notifier_register(OPAL_MSG_PRD, &opal_prd_event_nb);
+       if (rc) {
+               pr_err("Couldn't register event notifier\n");
+               return rc;
+       }
+
+       rc = misc_register(&opal_prd_dev);
+       if (rc) {
+               pr_err("failed to register miscdev\n");
+               opal_message_notifier_unregister(OPAL_MSG_PRD,
+                               &opal_prd_event_nb);
+               return rc;
+       }
+
+       return 0;
+}
+
+static int opal_prd_remove(struct platform_device *pdev)
+{
+       misc_deregister(&opal_prd_dev);
+       opal_message_notifier_unregister(OPAL_MSG_PRD, &opal_prd_event_nb);
+       return 0;
+}
+
+static const struct of_device_id opal_prd_match[] = {
+       { .compatible = "ibm,opal-prd" },
+       { },
+};
+
+static struct platform_driver opal_prd_driver = {
+       .driver = {
+               .name           = "opal-prd",
+               .owner          = THIS_MODULE,
+               .of_match_table = opal_prd_match,
+       },
+       .probe  = opal_prd_probe,
+       .remove = opal_prd_remove,
+};
+
+module_platform_driver(opal_prd_driver);
+
+MODULE_DEVICE_TABLE(of, opal_prd_match);
+MODULE_DESCRIPTION("PowerNV OPAL runtime diagnostic driver");
+MODULE_LICENSE("GPL");
index 655250499d18cd84e59a8217d5f102ffd15ddab0..a06059df9239202d660a5f7579034b715f9ee18a 100644 (file)
@@ -77,7 +77,7 @@ out:
 }
 EXPORT_SYMBOL_GPL(opal_get_sensor_data);
 
-static __init int opal_sensor_init(void)
+int __init opal_sensor_init(void)
 {
        struct platform_device *pdev;
        struct device_node *sensor;
@@ -93,4 +93,3 @@ static __init int opal_sensor_init(void)
 
        return PTR_ERR_OR_ZERO(pdev);
 }
-machine_subsys_initcall(powernv, opal_sensor_init);
index 9d1acf22a099dfc1bf474f6244af11d1153742f6..afe66c576a385f4204860766fa69b9d6fd3e590f 100644 (file)
@@ -55,8 +55,10 @@ static ssize_t opal_get_sys_param(u32 param_id, u32 length, void *buffer)
        }
 
        ret = opal_get_param(token, param_id, (u64)buffer, length);
-       if (ret != OPAL_ASYNC_COMPLETION)
+       if (ret != OPAL_ASYNC_COMPLETION) {
+               ret = opal_error_code(ret);
                goto out_token;
+       }
 
        ret = opal_async_wait_response(token, &msg);
        if (ret) {
@@ -65,7 +67,7 @@ static ssize_t opal_get_sys_param(u32 param_id, u32 length, void *buffer)
                goto out_token;
        }
 
-       ret = be64_to_cpu(msg.params[1]);
+       ret = opal_error_code(be64_to_cpu(msg.params[1]));
 
 out_token:
        opal_async_release_token(token);
@@ -89,8 +91,10 @@ static int opal_set_sys_param(u32 param_id, u32 length, void *buffer)
 
        ret = opal_set_param(token, param_id, (u64)buffer, length);
 
-       if (ret != OPAL_ASYNC_COMPLETION)
+       if (ret != OPAL_ASYNC_COMPLETION) {
+               ret = opal_error_code(ret);
                goto out_token;
+       }
 
        ret = opal_async_wait_response(token, &msg);
        if (ret) {
@@ -99,7 +103,7 @@ static int opal_set_sys_param(u32 param_id, u32 length, void *buffer)
                goto out_token;
        }
 
-       ret = be64_to_cpu(msg.params[1]);
+       ret = opal_error_code(be64_to_cpu(msg.params[1]));
 
 out_token:
        opal_async_release_token(token);
@@ -162,10 +166,20 @@ void __init opal_sys_param_init(void)
                goto out;
        }
 
+       /* Some systems do not use sysparams; this is not an error */
+       sysparam = of_find_node_by_path("/ibm,opal/sysparams");
+       if (!sysparam)
+               goto out;
+
+       if (!of_device_is_compatible(sysparam, "ibm,opal-sysparams")) {
+               pr_err("SYSPARAM: Opal sysparam node not compatible\n");
+               goto out_node_put;
+       }
+
        sysparam_kobj = kobject_create_and_add("sysparams", opal_kobj);
        if (!sysparam_kobj) {
                pr_err("SYSPARAM: Failed to create sysparam kobject\n");
-               goto out;
+               goto out_node_put;
        }
 
        /* Allocate big enough buffer for any get/set transactions */
@@ -176,30 +190,19 @@ void __init opal_sys_param_init(void)
                goto out_kobj_put;
        }
 
-       sysparam = of_find_node_by_path("/ibm,opal/sysparams");
-       if (!sysparam) {
-               pr_err("SYSPARAM: Opal sysparam node not found\n");
-               goto out_param_buf;
-       }
-
-       if (!of_device_is_compatible(sysparam, "ibm,opal-sysparams")) {
-               pr_err("SYSPARAM: Opal sysparam node not compatible\n");
-               goto out_node_put;
-       }
-
        /* Number of parameters exposed through DT */
        count = of_property_count_strings(sysparam, "param-name");
        if (count < 0) {
                pr_err("SYSPARAM: No string found of property param-name in "
                                "the node %s\n", sysparam->name);
-               goto out_node_put;
+               goto out_param_buf;
        }
 
        id = kzalloc(sizeof(*id) * count, GFP_KERNEL);
        if (!id) {
                pr_err("SYSPARAM: Failed to allocate memory to read parameter "
                                "id\n");
-               goto out_node_put;
+               goto out_param_buf;
        }
 
        size = kzalloc(sizeof(*size) * count, GFP_KERNEL);
@@ -293,12 +296,12 @@ out_free_size:
        kfree(size);
 out_free_id:
        kfree(id);
-out_node_put:
-       of_node_put(sysparam);
 out_param_buf:
        kfree(param_data_buf);
 out_kobj_put:
        kobject_put(sysparam_kobj);
+out_node_put:
+       of_node_put(sysparam);
 out:
        return;
 }
index a7ade94cdf87bb75be61616f8a6279bd94bb2377..d6a7b8252e4da205edb33d8de53d90eb329f9a94 100644 (file)
@@ -283,6 +283,7 @@ OPAL_CALL(opal_sensor_read,                 OPAL_SENSOR_READ);
 OPAL_CALL(opal_get_param,                      OPAL_GET_PARAM);
 OPAL_CALL(opal_set_param,                      OPAL_SET_PARAM);
 OPAL_CALL(opal_handle_hmi,                     OPAL_HANDLE_HMI);
+OPAL_CALL(opal_config_cpu_idle_state,          OPAL_CONFIG_CPU_IDLE_STATE);
 OPAL_CALL(opal_slw_set_reg,                    OPAL_SLW_SET_REG);
 OPAL_CALL(opal_register_dump_region,           OPAL_REGISTER_DUMP_REGION);
 OPAL_CALL(opal_unregister_dump_region,         OPAL_UNREGISTER_DUMP_REGION);
@@ -295,3 +296,4 @@ OPAL_CALL(opal_i2c_request,                 OPAL_I2C_REQUEST);
 OPAL_CALL(opal_flash_read,                     OPAL_FLASH_READ);
 OPAL_CALL(opal_flash_write,                    OPAL_FLASH_WRITE);
 OPAL_CALL(opal_flash_erase,                    OPAL_FLASH_ERASE);
+OPAL_CALL(opal_prd_msg,                                OPAL_PRD_MSG);
index 2241565b0739ff3bc6dc84f9bc6c63c70edf8b8c..f084afa0e3baeb776ec3c653f093000509da1ee2 100644 (file)
@@ -53,13 +53,7 @@ static int mc_recoverable_range_len;
 
 struct device_node *opal_node;
 static DEFINE_SPINLOCK(opal_write_lock);
-static unsigned int *opal_irqs;
-static unsigned int opal_irq_count;
-static ATOMIC_NOTIFIER_HEAD(opal_notifier_head);
 static struct atomic_notifier_head opal_msg_notifier_head[OPAL_MSG_TYPE_MAX];
-static DEFINE_SPINLOCK(opal_notifier_lock);
-static uint64_t last_notified_mask = 0x0ul;
-static atomic_t opal_notifier_hold = ATOMIC_INIT(0);
 static uint32_t opal_heartbeat;
 
 static void opal_reinit_cores(void)
@@ -225,82 +219,6 @@ static int __init opal_register_exception_handlers(void)
 }
 machine_early_initcall(powernv, opal_register_exception_handlers);
 
-int opal_notifier_register(struct notifier_block *nb)
-{
-       if (!nb) {
-               pr_warning("%s: Invalid argument (%p)\n",
-                          __func__, nb);
-               return -EINVAL;
-       }
-
-       atomic_notifier_chain_register(&opal_notifier_head, nb);
-       return 0;
-}
-EXPORT_SYMBOL_GPL(opal_notifier_register);
-
-int opal_notifier_unregister(struct notifier_block *nb)
-{
-       if (!nb) {
-               pr_warning("%s: Invalid argument (%p)\n",
-                          __func__, nb);
-               return -EINVAL;
-       }
-
-       atomic_notifier_chain_unregister(&opal_notifier_head, nb);
-       return 0;
-}
-EXPORT_SYMBOL_GPL(opal_notifier_unregister);
-
-static void opal_do_notifier(uint64_t events)
-{
-       unsigned long flags;
-       uint64_t changed_mask;
-
-       if (atomic_read(&opal_notifier_hold))
-               return;
-
-       spin_lock_irqsave(&opal_notifier_lock, flags);
-       changed_mask = last_notified_mask ^ events;
-       last_notified_mask = events;
-       spin_unlock_irqrestore(&opal_notifier_lock, flags);
-
-       /*
-        * We feed with the event bits and changed bits for
-        * enough information to the callback.
-        */
-       atomic_notifier_call_chain(&opal_notifier_head,
-                                  events, (void *)changed_mask);
-}
-
-void opal_notifier_update_evt(uint64_t evt_mask,
-                             uint64_t evt_val)
-{
-       unsigned long flags;
-
-       spin_lock_irqsave(&opal_notifier_lock, flags);
-       last_notified_mask &= ~evt_mask;
-       last_notified_mask |= evt_val;
-       spin_unlock_irqrestore(&opal_notifier_lock, flags);
-}
-
-void opal_notifier_enable(void)
-{
-       int64_t rc;
-       __be64 evt = 0;
-
-       atomic_set(&opal_notifier_hold, 0);
-
-       /* Process pending events */
-       rc = opal_poll_events(&evt);
-       if (rc == OPAL_SUCCESS && evt)
-               opal_do_notifier(be64_to_cpu(evt));
-}
-
-void opal_notifier_disable(void)
-{
-       atomic_set(&opal_notifier_hold, 1);
-}
-
 /*
  * Opal message notifier based on message type. Allow subscribers to get
  * notified for specific messgae type.
@@ -317,6 +235,7 @@ int opal_message_notifier_register(enum opal_msg_type msg_type,
        return atomic_notifier_chain_register(
                                &opal_msg_notifier_head[msg_type], nb);
 }
+EXPORT_SYMBOL_GPL(opal_message_notifier_register);
 
 int opal_message_notifier_unregister(enum opal_msg_type msg_type,
                                     struct notifier_block *nb)
@@ -324,6 +243,7 @@ int opal_message_notifier_unregister(enum opal_msg_type msg_type,
        return atomic_notifier_chain_unregister(
                        &opal_msg_notifier_head[msg_type], nb);
 }
+EXPORT_SYMBOL_GPL(opal_message_notifier_unregister);
 
 static void opal_message_do_notify(uint32_t msg_type, void *msg)
 {
@@ -364,36 +284,36 @@ static void opal_handle_message(void)
        opal_message_do_notify(type, (void *)&msg);
 }
 
-static int opal_message_notify(struct notifier_block *nb,
-                         unsigned long events, void *change)
+static irqreturn_t opal_message_notify(int irq, void *data)
 {
-       if (events & OPAL_EVENT_MSG_PENDING)
-               opal_handle_message();
-       return 0;
+       opal_handle_message();
+       return IRQ_HANDLED;
 }
 
-static struct notifier_block opal_message_nb = {
-       .notifier_call  = opal_message_notify,
-       .next           = NULL,
-       .priority       = 0,
-};
-
 static int __init opal_message_init(void)
 {
-       int ret, i;
+       int ret, i, irq;
 
        for (i = 0; i < OPAL_MSG_TYPE_MAX; i++)
                ATOMIC_INIT_NOTIFIER_HEAD(&opal_msg_notifier_head[i]);
 
-       ret = opal_notifier_register(&opal_message_nb);
+       irq = opal_event_request(ilog2(OPAL_EVENT_MSG_PENDING));
+       if (!irq) {
+               pr_err("%s: Can't register OPAL event irq (%d)\n",
+                      __func__, irq);
+               return irq;
+       }
+
+       ret = request_irq(irq, opal_message_notify,
+                       IRQ_TYPE_LEVEL_HIGH, "opal-msg", NULL);
        if (ret) {
-               pr_err("%s: Can't register OPAL event notifier (%d)\n",
+               pr_err("%s: Can't request OPAL event irq (%d)\n",
                       __func__, ret);
                return ret;
        }
+
        return 0;
 }
-machine_early_initcall(powernv, opal_message_init);
 
 int opal_get_chars(uint32_t vtermno, char *buf, int count)
 {
@@ -573,7 +493,7 @@ int opal_handle_hmi_exception(struct pt_regs *regs)
        local_paca->hmi_event_available = 0;
        rc = opal_poll_events(&evt);
        if (rc == OPAL_SUCCESS && evt)
-               opal_do_notifier(be64_to_cpu(evt));
+               opal_handle_events(be64_to_cpu(evt));
 
        return 1;
 }
@@ -610,17 +530,6 @@ out:
        return !!recover_addr;
 }
 
-static irqreturn_t opal_interrupt(int irq, void *data)
-{
-       __be64 events;
-
-       opal_handle_interrupt(virq_to_hw(irq), &events);
-
-       opal_do_notifier(be64_to_cpu(events));
-
-       return IRQ_HANDLED;
-}
-
 static int opal_sysfs_init(void)
 {
        opal_kobj = kobject_create_and_add("opal", firmware_kobj);
@@ -693,21 +602,13 @@ static void __init opal_dump_region_init(void)
                        "rc = %d\n", rc);
 }
 
-static void opal_flash_init(struct device_node *opal_node)
-{
-       struct device_node *np;
-
-       for_each_child_of_node(opal_node, np)
-               if (of_device_is_compatible(np, "ibm,opal-flash"))
-                       of_platform_device_create(np, NULL, NULL);
-}
-
-static void opal_ipmi_init(struct device_node *opal_node)
+static void opal_pdev_init(struct device_node *opal_node,
+               const char *compatible)
 {
        struct device_node *np;
 
        for_each_child_of_node(opal_node, np)
-               if (of_device_is_compatible(np, "ibm,opal-ipmi"))
+               if (of_device_is_compatible(np, compatible))
                        of_platform_device_create(np, NULL, NULL);
 }
 
@@ -719,52 +620,15 @@ static void opal_i2c_create_devs(void)
                of_platform_device_create(np, NULL, NULL);
 }
 
-static void __init opal_irq_init(struct device_node *dn)
-{
-       const __be32 *irqs;
-       int i, irqlen;
-
-       /* Get interrupt property */
-       irqs = of_get_property(opal_node, "opal-interrupts", &irqlen);
-       opal_irq_count = irqs ? (irqlen / 4) : 0;
-       pr_debug("Found %d interrupts reserved for OPAL\n", opal_irq_count);
-       if (!opal_irq_count)
-               return;
-
-       /* Install interrupt handlers */
-       opal_irqs = kzalloc(opal_irq_count * sizeof(unsigned int), GFP_KERNEL);
-       for (i = 0; irqs && i < opal_irq_count; i++, irqs++) {
-               unsigned int irq, virq;
-               int rc;
-
-               /* Get hardware and virtual IRQ */
-               irq = be32_to_cpup(irqs);
-               virq = irq_create_mapping(NULL, irq);
-               if (virq == NO_IRQ) {
-                       pr_warn("Failed to map irq 0x%x\n", irq);
-                       continue;
-               }
-
-               /* Install interrupt handler */
-               rc = request_irq(virq, opal_interrupt, 0, "opal", NULL);
-               if (rc) {
-                       irq_dispose_mapping(virq);
-                       pr_warn("Error %d requesting irq %d (0x%x)\n",
-                                rc, virq, irq);
-                       continue;
-               }
-
-               /* Cache IRQ */
-               opal_irqs[i] = virq;
-       }
-}
-
 static int kopald(void *unused)
 {
+       __be64 events;
+
        set_freezable();
        do {
                try_to_freeze();
-               opal_poll_events(NULL);
+               opal_poll_events(&events);
+               opal_handle_events(be64_to_cpu(events));
                msleep_interruptible(opal_heartbeat);
        } while (!kthread_should_stop());
 
@@ -807,15 +671,24 @@ static int __init opal_init(void)
                of_node_put(consoles);
        }
 
+       /* Initialise OPAL messaging system */
+       opal_message_init();
+
+       /* Initialise OPAL asynchronous completion interface */
+       opal_async_comp_init();
+
+       /* Initialise OPAL sensor interface */
+       opal_sensor_init();
+
+       /* Initialise OPAL hypervisor maintainence interrupt handling */
+       opal_hmi_handler_init();
+
        /* Create i2c platform devices */
        opal_i2c_create_devs();
 
        /* Setup a heatbeat thread if requested by OPAL */
        opal_init_heartbeat();
 
-       /* Find all OPAL interrupts and request them */
-       opal_irq_init(opal_node);
-
        /* Create "opal" kobject under /sys/firmware */
        rc = opal_sysfs_init();
        if (rc == 0) {
@@ -835,10 +708,10 @@ static int __init opal_init(void)
                opal_msglog_init();
        }
 
-       /* Initialize OPAL IPMI backend */
-       opal_ipmi_init(opal_node);
-
-       opal_flash_init(opal_node);
+       /* Initialize platform devices: IPMI backend, PRD & flash interface */
+       opal_pdev_init(opal_node, "ibm,opal-ipmi");
+       opal_pdev_init(opal_node, "ibm,opal-flash");
+       opal_pdev_init(opal_node, "ibm,opal-prd");
 
        return 0;
 }
@@ -846,15 +719,9 @@ machine_subsys_initcall(powernv, opal_init);
 
 void opal_shutdown(void)
 {
-       unsigned int i;
        long rc = OPAL_BUSY;
 
-       /* First free interrupts, which will also mask them */
-       for (i = 0; i < opal_irq_count; i++) {
-               if (opal_irqs[i])
-                       free_irq(opal_irqs[i], NULL);
-               opal_irqs[i] = 0;
-       }
+       opal_event_shutdown();
 
        /*
         * Then sync with OPAL which ensure anything that can
@@ -876,11 +743,14 @@ void opal_shutdown(void)
 
 /* Export this so that test modules can use it */
 EXPORT_SYMBOL_GPL(opal_invalid_call);
+EXPORT_SYMBOL_GPL(opal_xscom_read);
+EXPORT_SYMBOL_GPL(opal_xscom_write);
 EXPORT_SYMBOL_GPL(opal_ipmi_send);
 EXPORT_SYMBOL_GPL(opal_ipmi_recv);
 EXPORT_SYMBOL_GPL(opal_flash_read);
 EXPORT_SYMBOL_GPL(opal_flash_write);
 EXPORT_SYMBOL_GPL(opal_flash_erase);
+EXPORT_SYMBOL_GPL(opal_prd_msg);
 
 /* Convert a region of vmalloc memory to an opal sg list */
 struct opal_sg_list *opal_vmalloc_to_sg_list(void *vmalloc_addr,
@@ -954,6 +824,7 @@ int opal_error_code(int rc)
        case OPAL_ASYNC_COMPLETION:     return -EINPROGRESS;
        case OPAL_BUSY_EVENT:           return -EBUSY;
        case OPAL_NO_MEM:               return -ENOMEM;
+       case OPAL_PERMISSION:           return -EPERM;
 
        case OPAL_UNSUPPORTED:          return -EIO;
        case OPAL_HARDWARE:             return -EIO;
index f8bc950efcae39a63f213290b65d36fec1667b60..5738d315248b202b4a26aff084b07e819a80855c 100644 (file)
@@ -23,6 +23,9 @@
 #include <linux/io.h>
 #include <linux/msi.h>
 #include <linux/memblock.h>
+#include <linux/iommu.h>
+#include <linux/rculist.h>
+#include <linux/sizes.h>
 
 #include <asm/sections.h>
 #include <asm/io.h>
@@ -38,8 +41,9 @@
 #include <asm/debug.h>
 #include <asm/firmware.h>
 #include <asm/pnv-pci.h>
+#include <asm/mmzone.h>
 
-#include <misc/cxl.h>
+#include <misc/cxl-base.h>
 
 #include "powernv.h"
 #include "pci.h"
 /* 256M DMA window, 4K TCE pages, 8 bytes TCE */
 #define TCE32_TABLE_SIZE       ((0x10000000 / 0x1000) * 8)
 
+#define POWERNV_IOMMU_DEFAULT_LEVELS   1
+#define POWERNV_IOMMU_MAX_LEVELS       5
+
+static void pnv_pci_ioda2_table_free_pages(struct iommu_table *tbl);
+
 static void pe_level_printk(const struct pnv_ioda_pe *pe, const char *level,
                            const char *fmt, ...)
 {
@@ -1086,10 +1095,6 @@ static void pnv_ioda_setup_bus_PE(struct pci_bus *bus, int all)
                return;
        }
 
-       pe->tce32_table = kzalloc_node(sizeof(struct iommu_table),
-                       GFP_KERNEL, hose->node);
-       pe->tce32_table->data = pe;
-
        /* Associate it with all child devices */
        pnv_ioda_setup_same_PE(bus, pe);
 
@@ -1283,36 +1288,27 @@ m64_failed:
        return -EBUSY;
 }
 
+static long pnv_pci_ioda2_unset_window(struct iommu_table_group *table_group,
+               int num);
+static void pnv_pci_ioda2_set_bypass(struct pnv_ioda_pe *pe, bool enable);
+
 static void pnv_pci_ioda2_release_dma_pe(struct pci_dev *dev, struct pnv_ioda_pe *pe)
 {
-       struct pci_bus        *bus;
-       struct pci_controller *hose;
-       struct pnv_phb        *phb;
        struct iommu_table    *tbl;
-       unsigned long         addr;
        int64_t               rc;
 
-       bus = dev->bus;
-       hose = pci_bus_to_host(bus);
-       phb = hose->private_data;
-       tbl = pe->tce32_table;
-       addr = tbl->it_base;
-
-       opal_pci_map_pe_dma_window(phb->opal_id, pe->pe_number,
-                                  pe->pe_number << 1, 1, __pa(addr),
-                                  0, 0x1000);
-
-       rc = opal_pci_map_pe_dma_window_real(pe->phb->opal_id,
-                                       pe->pe_number,
-                                       (pe->pe_number << 1) + 1,
-                                       pe->tce_bypass_base,
-                                       0);
+       tbl = pe->table_group.tables[0];
+       rc = pnv_pci_ioda2_unset_window(&pe->table_group, 0);
        if (rc)
                pe_warn(pe, "OPAL error %ld release DMA window\n", rc);
 
+       pnv_pci_ioda2_set_bypass(pe, false);
+       if (pe->table_group.group) {
+               iommu_group_put(pe->table_group.group);
+               BUG_ON(pe->table_group.group);
+       }
+       pnv_pci_ioda2_table_free_pages(tbl);
        iommu_free_table(tbl, of_node_full_name(dev->dev.of_node));
-       free_pages(addr, get_order(TCE32_TABLE_SIZE));
-       pe->tce32_table = NULL;
 }
 
 static void pnv_ioda_release_vf_PE(struct pci_dev *pdev, u16 num_vfs)
@@ -1460,10 +1456,6 @@ static void pnv_ioda_setup_vf_PE(struct pci_dev *pdev, u16 num_vfs)
                        continue;
                }
 
-               pe->tce32_table = kzalloc_node(sizeof(struct iommu_table),
-                               GFP_KERNEL, hose->node);
-               pe->tce32_table->data = pe;
-
                /* Put PE to the list */
                mutex_lock(&phb->ioda.pe_list_mutex);
                list_add_tail(&pe->list, &phb->ioda.pe_list);
@@ -1598,12 +1590,19 @@ static void pnv_pci_ioda_dma_dev_setup(struct pnv_phb *phb, struct pci_dev *pdev
 
        pe = &phb->ioda.pe_array[pdn->pe_number];
        WARN_ON(get_dma_ops(&pdev->dev) != &dma_iommu_ops);
-       set_iommu_table_base_and_group(&pdev->dev, pe->tce32_table);
+       set_iommu_table_base(&pdev->dev, pe->table_group.tables[0]);
+       /*
+        * Note: iommu_add_device() will fail here as
+        * for physical PE: the device is already added by now;
+        * for virtual PE: sysfs entries are not ready yet and
+        * tce_iommu_bus_notifier will add the device to a group later.
+        */
 }
 
-static int pnv_pci_ioda_dma_set_mask(struct pnv_phb *phb,
-                                    struct pci_dev *pdev, u64 dma_mask)
+static int pnv_pci_ioda_dma_set_mask(struct pci_dev *pdev, u64 dma_mask)
 {
+       struct pci_controller *hose = pci_bus_to_host(pdev->bus);
+       struct pnv_phb *phb = hose->private_data;
        struct pci_dn *pdn = pci_get_pdn(pdev);
        struct pnv_ioda_pe *pe;
        uint64_t top;
@@ -1625,7 +1624,7 @@ static int pnv_pci_ioda_dma_set_mask(struct pnv_phb *phb,
        } else {
                dev_info(&pdev->dev, "Using 32-bit DMA via iommu\n");
                set_dma_ops(&pdev->dev, &dma_iommu_ops);
-               set_iommu_table_base(&pdev->dev, pe->tce32_table);
+               set_iommu_table_base(&pdev->dev, pe->table_group.tables[0]);
        }
        *pdev->dev.dma_mask = dma_mask;
        return 0;
@@ -1654,36 +1653,36 @@ static u64 pnv_pci_ioda_dma_get_required_mask(struct pnv_phb *phb,
 }
 
 static void pnv_ioda_setup_bus_dma(struct pnv_ioda_pe *pe,
-                                  struct pci_bus *bus,
-                                  bool add_to_iommu_group)
+                                  struct pci_bus *bus)
 {
        struct pci_dev *dev;
 
        list_for_each_entry(dev, &bus->devices, bus_list) {
-               if (add_to_iommu_group)
-                       set_iommu_table_base_and_group(&dev->dev,
-                                                      pe->tce32_table);
-               else
-                       set_iommu_table_base(&dev->dev, pe->tce32_table);
+               set_iommu_table_base(&dev->dev, pe->table_group.tables[0]);
+               iommu_add_device(&dev->dev);
 
-               if (dev->subordinate)
-                       pnv_ioda_setup_bus_dma(pe, dev->subordinate,
-                                              add_to_iommu_group);
+               if ((pe->flags & PNV_IODA_PE_BUS_ALL) && dev->subordinate)
+                       pnv_ioda_setup_bus_dma(pe, dev->subordinate);
        }
 }
 
-static void pnv_pci_ioda1_tce_invalidate(struct pnv_ioda_pe *pe,
-                                        struct iommu_table *tbl,
-                                        __be64 *startp, __be64 *endp, bool rm)
+static void pnv_pci_ioda1_tce_invalidate(struct iommu_table *tbl,
+               unsigned long index, unsigned long npages, bool rm)
 {
+       struct iommu_table_group_link *tgl = list_first_entry_or_null(
+                       &tbl->it_group_list, struct iommu_table_group_link,
+                       next);
+       struct pnv_ioda_pe *pe = container_of(tgl->table_group,
+                       struct pnv_ioda_pe, table_group);
        __be64 __iomem *invalidate = rm ?
-               (__be64 __iomem *)pe->tce_inval_reg_phys :
-               (__be64 __iomem *)tbl->it_index;
+               (__be64 __iomem *)pe->phb->ioda.tce_inval_reg_phys :
+               pe->phb->ioda.tce_inval_reg;
        unsigned long start, end, inc;
        const unsigned shift = tbl->it_page_shift;
 
-       start = __pa(startp);
-       end = __pa(endp);
+       start = __pa(((__be64 *)tbl->it_base) + index - tbl->it_offset);
+       end = __pa(((__be64 *)tbl->it_base) + index - tbl->it_offset +
+                       npages - 1);
 
        /* BML uses this case for p6/p7/galaxy2: Shift addr and put in node */
        if (tbl->it_busno) {
@@ -1719,26 +1718,79 @@ static void pnv_pci_ioda1_tce_invalidate(struct pnv_ioda_pe *pe,
         */
 }
 
-static void pnv_pci_ioda2_tce_invalidate(struct pnv_ioda_pe *pe,
-                                        struct iommu_table *tbl,
-                                        __be64 *startp, __be64 *endp, bool rm)
+static int pnv_ioda1_tce_build(struct iommu_table *tbl, long index,
+               long npages, unsigned long uaddr,
+               enum dma_data_direction direction,
+               struct dma_attrs *attrs)
+{
+       int ret = pnv_tce_build(tbl, index, npages, uaddr, direction,
+                       attrs);
+
+       if (!ret && (tbl->it_type & TCE_PCI_SWINV_CREATE))
+               pnv_pci_ioda1_tce_invalidate(tbl, index, npages, false);
+
+       return ret;
+}
+
+#ifdef CONFIG_IOMMU_API
+static int pnv_ioda1_tce_xchg(struct iommu_table *tbl, long index,
+               unsigned long *hpa, enum dma_data_direction *direction)
+{
+       long ret = pnv_tce_xchg(tbl, index, hpa, direction);
+
+       if (!ret && (tbl->it_type &
+                       (TCE_PCI_SWINV_CREATE | TCE_PCI_SWINV_FREE)))
+               pnv_pci_ioda1_tce_invalidate(tbl, index, 1, false);
+
+       return ret;
+}
+#endif
+
+static void pnv_ioda1_tce_free(struct iommu_table *tbl, long index,
+               long npages)
+{
+       pnv_tce_free(tbl, index, npages);
+
+       if (tbl->it_type & TCE_PCI_SWINV_FREE)
+               pnv_pci_ioda1_tce_invalidate(tbl, index, npages, false);
+}
+
+static struct iommu_table_ops pnv_ioda1_iommu_ops = {
+       .set = pnv_ioda1_tce_build,
+#ifdef CONFIG_IOMMU_API
+       .exchange = pnv_ioda1_tce_xchg,
+#endif
+       .clear = pnv_ioda1_tce_free,
+       .get = pnv_tce_get,
+};
+
+static inline void pnv_pci_ioda2_tce_invalidate_entire(struct pnv_ioda_pe *pe)
+{
+       /* 01xb - invalidate TCEs that match the specified PE# */
+       unsigned long val = (0x4ull << 60) | (pe->pe_number & 0xFF);
+       struct pnv_phb *phb = pe->phb;
+
+       if (!phb->ioda.tce_inval_reg)
+               return;
+
+       mb(); /* Ensure above stores are visible */
+       __raw_writeq(cpu_to_be64(val), phb->ioda.tce_inval_reg);
+}
+
+static void pnv_pci_ioda2_do_tce_invalidate(unsigned pe_number, bool rm,
+               __be64 __iomem *invalidate, unsigned shift,
+               unsigned long index, unsigned long npages)
 {
        unsigned long start, end, inc;
-       __be64 __iomem *invalidate = rm ?
-               (__be64 __iomem *)pe->tce_inval_reg_phys :
-               (__be64 __iomem *)tbl->it_index;
-       const unsigned shift = tbl->it_page_shift;
 
        /* We'll invalidate DMA address in PE scope */
        start = 0x2ull << 60;
-       start |= (pe->pe_number & 0xFF);
+       start |= (pe_number & 0xFF);
        end = start;
 
        /* Figure out the start, end and step */
-       inc = tbl->it_offset + (((u64)startp - tbl->it_base) / sizeof(u64));
-       start |= (inc << shift);
-       inc = tbl->it_offset + (((u64)endp - tbl->it_base) / sizeof(u64));
-       end |= (inc << shift);
+       start |= (index << shift);
+       end |= ((index + npages - 1) << shift);
        inc = (0x1ull << shift);
        mb();
 
@@ -1751,25 +1803,83 @@ static void pnv_pci_ioda2_tce_invalidate(struct pnv_ioda_pe *pe,
        }
 }
 
-void pnv_pci_ioda_tce_invalidate(struct iommu_table *tbl,
-                                __be64 *startp, __be64 *endp, bool rm)
+static void pnv_pci_ioda2_tce_invalidate(struct iommu_table *tbl,
+               unsigned long index, unsigned long npages, bool rm)
 {
-       struct pnv_ioda_pe *pe = tbl->data;
-       struct pnv_phb *phb = pe->phb;
+       struct iommu_table_group_link *tgl;
 
-       if (phb->type == PNV_PHB_IODA1)
-               pnv_pci_ioda1_tce_invalidate(pe, tbl, startp, endp, rm);
-       else
-               pnv_pci_ioda2_tce_invalidate(pe, tbl, startp, endp, rm);
+       list_for_each_entry_rcu(tgl, &tbl->it_group_list, next) {
+               struct pnv_ioda_pe *pe = container_of(tgl->table_group,
+                               struct pnv_ioda_pe, table_group);
+               __be64 __iomem *invalidate = rm ?
+                       (__be64 __iomem *)pe->phb->ioda.tce_inval_reg_phys :
+                       pe->phb->ioda.tce_inval_reg;
+
+               pnv_pci_ioda2_do_tce_invalidate(pe->pe_number, rm,
+                       invalidate, tbl->it_page_shift,
+                       index, npages);
+       }
+}
+
+static int pnv_ioda2_tce_build(struct iommu_table *tbl, long index,
+               long npages, unsigned long uaddr,
+               enum dma_data_direction direction,
+               struct dma_attrs *attrs)
+{
+       int ret = pnv_tce_build(tbl, index, npages, uaddr, direction,
+                       attrs);
+
+       if (!ret && (tbl->it_type & TCE_PCI_SWINV_CREATE))
+               pnv_pci_ioda2_tce_invalidate(tbl, index, npages, false);
+
+       return ret;
+}
+
+#ifdef CONFIG_IOMMU_API
+static int pnv_ioda2_tce_xchg(struct iommu_table *tbl, long index,
+               unsigned long *hpa, enum dma_data_direction *direction)
+{
+       long ret = pnv_tce_xchg(tbl, index, hpa, direction);
+
+       if (!ret && (tbl->it_type &
+                       (TCE_PCI_SWINV_CREATE | TCE_PCI_SWINV_FREE)))
+               pnv_pci_ioda2_tce_invalidate(tbl, index, 1, false);
+
+       return ret;
+}
+#endif
+
+static void pnv_ioda2_tce_free(struct iommu_table *tbl, long index,
+               long npages)
+{
+       pnv_tce_free(tbl, index, npages);
+
+       if (tbl->it_type & TCE_PCI_SWINV_FREE)
+               pnv_pci_ioda2_tce_invalidate(tbl, index, npages, false);
+}
+
+static void pnv_ioda2_table_free(struct iommu_table *tbl)
+{
+       pnv_pci_ioda2_table_free_pages(tbl);
+       iommu_free_table(tbl, "pnv");
 }
 
+static struct iommu_table_ops pnv_ioda2_iommu_ops = {
+       .set = pnv_ioda2_tce_build,
+#ifdef CONFIG_IOMMU_API
+       .exchange = pnv_ioda2_tce_xchg,
+#endif
+       .clear = pnv_ioda2_tce_free,
+       .get = pnv_tce_get,
+       .free = pnv_ioda2_table_free,
+};
+
 static void pnv_pci_ioda_setup_dma_pe(struct pnv_phb *phb,
                                      struct pnv_ioda_pe *pe, unsigned int base,
                                      unsigned int segs)
 {
 
        struct page *tce_mem = NULL;
-       const __be64 *swinvp;
        struct iommu_table *tbl;
        unsigned int i;
        int64_t rc;
@@ -1783,6 +1893,11 @@ static void pnv_pci_ioda_setup_dma_pe(struct pnv_phb *phb,
        if (WARN_ON(pe->tce32_seg >= 0))
                return;
 
+       tbl = pnv_pci_table_alloc(phb->hose->node);
+       iommu_register_group(&pe->table_group, phb->hose->global_number,
+                       pe->pe_number);
+       pnv_pci_link_table_and_group(phb->hose->node, 0, tbl, &pe->table_group);
+
        /* Grab a 32-bit TCE table */
        pe->tce32_seg = base;
        pe_info(pe, " Setting up 32-bit TCE table at %08x..%08x\n",
@@ -1817,39 +1932,30 @@ static void pnv_pci_ioda_setup_dma_pe(struct pnv_phb *phb,
        }
 
        /* Setup linux iommu table */
-       tbl = pe->tce32_table;
        pnv_pci_setup_iommu_table(tbl, addr, TCE32_TABLE_SIZE * segs,
                                  base << 28, IOMMU_PAGE_SHIFT_4K);
 
        /* OPAL variant of P7IOC SW invalidated TCEs */
-       swinvp = of_get_property(phb->hose->dn, "ibm,opal-tce-kill", NULL);
-       if (swinvp) {
-               /* We need a couple more fields -- an address and a data
-                * to or.  Since the bus is only printed out on table free
-                * errors, and on the first pass the data will be a relative
-                * bus number, print that out instead.
-                */
-               pe->tce_inval_reg_phys = be64_to_cpup(swinvp);
-               tbl->it_index = (unsigned long)ioremap(pe->tce_inval_reg_phys,
-                               8);
+       if (phb->ioda.tce_inval_reg)
                tbl->it_type |= (TCE_PCI_SWINV_CREATE |
                                 TCE_PCI_SWINV_FREE   |
                                 TCE_PCI_SWINV_PAIR);
-       }
+
+       tbl->it_ops = &pnv_ioda1_iommu_ops;
+       pe->table_group.tce32_start = tbl->it_offset << tbl->it_page_shift;
+       pe->table_group.tce32_size = tbl->it_size << tbl->it_page_shift;
        iommu_init_table(tbl, phb->hose->node);
 
        if (pe->flags & PNV_IODA_PE_DEV) {
-               iommu_register_group(tbl, phb->hose->global_number,
-                                    pe->pe_number);
-               set_iommu_table_base_and_group(&pe->pdev->dev, tbl);
-       } else if (pe->flags & (PNV_IODA_PE_BUS | PNV_IODA_PE_BUS_ALL)) {
-               iommu_register_group(tbl, phb->hose->global_number,
-                                    pe->pe_number);
-               pnv_ioda_setup_bus_dma(pe, pe->pbus, true);
-       } else if (pe->flags & PNV_IODA_PE_VF) {
-               iommu_register_group(tbl, phb->hose->global_number,
-                                    pe->pe_number);
-       }
+               /*
+                * Setting table base here only for carrying iommu_group
+                * further down to let iommu_add_device() do the job.
+                * pnv_pci_ioda_dma_dev_setup will override it later anyway.
+                */
+               set_iommu_table_base(&pe->pdev->dev, tbl);
+               iommu_add_device(&pe->pdev->dev);
+       } else if (pe->flags & (PNV_IODA_PE_BUS | PNV_IODA_PE_BUS_ALL))
+               pnv_ioda_setup_bus_dma(pe, pe->pbus);
 
        return;
  fail:
@@ -1858,11 +1964,53 @@ static void pnv_pci_ioda_setup_dma_pe(struct pnv_phb *phb,
                pe->tce32_seg = -1;
        if (tce_mem)
                __free_pages(tce_mem, get_order(TCE32_TABLE_SIZE * segs));
+       if (tbl) {
+               pnv_pci_unlink_table_and_group(tbl, &pe->table_group);
+               iommu_free_table(tbl, "pnv");
+       }
 }
 
-static void pnv_pci_ioda2_set_bypass(struct iommu_table *tbl, bool enable)
+static long pnv_pci_ioda2_set_window(struct iommu_table_group *table_group,
+               int num, struct iommu_table *tbl)
+{
+       struct pnv_ioda_pe *pe = container_of(table_group, struct pnv_ioda_pe,
+                       table_group);
+       struct pnv_phb *phb = pe->phb;
+       int64_t rc;
+       const unsigned long size = tbl->it_indirect_levels ?
+                       tbl->it_level_size : tbl->it_size;
+       const __u64 start_addr = tbl->it_offset << tbl->it_page_shift;
+       const __u64 win_size = tbl->it_size << tbl->it_page_shift;
+
+       pe_info(pe, "Setting up window#%d %llx..%llx pg=%x\n", num,
+                       start_addr, start_addr + win_size - 1,
+                       IOMMU_PAGE_SIZE(tbl));
+
+       /*
+        * Map TCE table through TVT. The TVE index is the PE number
+        * shifted by 1 bit for 32-bits DMA space.
+        */
+       rc = opal_pci_map_pe_dma_window(phb->opal_id,
+                       pe->pe_number,
+                       (pe->pe_number << 1) + num,
+                       tbl->it_indirect_levels + 1,
+                       __pa(tbl->it_base),
+                       size << 3,
+                       IOMMU_PAGE_SIZE(tbl));
+       if (rc) {
+               pe_err(pe, "Failed to configure TCE table, err %ld\n", rc);
+               return rc;
+       }
+
+       pnv_pci_link_table_and_group(phb->hose->node, num,
+                       tbl, &pe->table_group);
+       pnv_pci_ioda2_tce_invalidate_entire(pe);
+
+       return 0;
+}
+
+static void pnv_pci_ioda2_set_bypass(struct pnv_ioda_pe *pe, bool enable)
 {
-       struct pnv_ioda_pe *pe = tbl->data;
        uint16_t window_id = (pe->pe_number << 1 ) + 1;
        int64_t rc;
 
@@ -1882,17 +2030,6 @@ static void pnv_pci_ioda2_set_bypass(struct iommu_table *tbl, bool enable)
                                                     window_id,
                                                     pe->tce_bypass_base,
                                                     0);
-
-               /*
-                * EEH needs the mapping between IOMMU table and group
-                * of those VFIO/KVM pass-through devices. We can postpone
-                * resetting DMA ops until the DMA mask is configured in
-                * host side.
-                */
-               if (pe->pdev)
-                       set_iommu_table_base(&pe->pdev->dev, tbl);
-               else
-                       pnv_ioda_setup_bus_dma(pe, pe->pbus, false);
        }
        if (rc)
                pe_err(pe, "OPAL error %lld configuring bypass window\n", rc);
@@ -1900,106 +2037,363 @@ static void pnv_pci_ioda2_set_bypass(struct iommu_table *tbl, bool enable)
                pe->tce_bypass_enabled = enable;
 }
 
-static void pnv_pci_ioda2_setup_bypass_pe(struct pnv_phb *phb,
-                                         struct pnv_ioda_pe *pe)
+static long pnv_pci_ioda2_table_alloc_pages(int nid, __u64 bus_offset,
+               __u32 page_shift, __u64 window_size, __u32 levels,
+               struct iommu_table *tbl);
+
+static long pnv_pci_ioda2_create_table(struct iommu_table_group *table_group,
+               int num, __u32 page_shift, __u64 window_size, __u32 levels,
+               struct iommu_table **ptbl)
 {
-       /* TVE #1 is selected by PCI address bit 59 */
-       pe->tce_bypass_base = 1ull << 59;
+       struct pnv_ioda_pe *pe = container_of(table_group, struct pnv_ioda_pe,
+                       table_group);
+       int nid = pe->phb->hose->node;
+       __u64 bus_offset = num ? pe->tce_bypass_base : table_group->tce32_start;
+       long ret;
+       struct iommu_table *tbl;
 
-       /* Install set_bypass callback for VFIO */
-       pe->tce32_table->set_bypass = pnv_pci_ioda2_set_bypass;
+       tbl = pnv_pci_table_alloc(nid);
+       if (!tbl)
+               return -ENOMEM;
 
-       /* Enable bypass by default */
-       pnv_pci_ioda2_set_bypass(pe->tce32_table, true);
+       ret = pnv_pci_ioda2_table_alloc_pages(nid,
+                       bus_offset, page_shift, window_size,
+                       levels, tbl);
+       if (ret) {
+               iommu_free_table(tbl, "pnv");
+               return ret;
+       }
+
+       tbl->it_ops = &pnv_ioda2_iommu_ops;
+       if (pe->phb->ioda.tce_inval_reg)
+               tbl->it_type |= (TCE_PCI_SWINV_CREATE | TCE_PCI_SWINV_FREE);
+
+       *ptbl = tbl;
+
+       return 0;
 }
 
-static void pnv_pci_ioda2_setup_dma_pe(struct pnv_phb *phb,
-                                      struct pnv_ioda_pe *pe)
+static long pnv_pci_ioda2_setup_default_config(struct pnv_ioda_pe *pe)
+{
+       struct iommu_table *tbl = NULL;
+       long rc;
+
+       rc = pnv_pci_ioda2_create_table(&pe->table_group, 0,
+                       IOMMU_PAGE_SHIFT_4K,
+                       pe->table_group.tce32_size,
+                       POWERNV_IOMMU_DEFAULT_LEVELS, &tbl);
+       if (rc) {
+               pe_err(pe, "Failed to create 32-bit TCE table, err %ld",
+                               rc);
+               return rc;
+       }
+
+       iommu_init_table(tbl, pe->phb->hose->node);
+
+       rc = pnv_pci_ioda2_set_window(&pe->table_group, 0, tbl);
+       if (rc) {
+               pe_err(pe, "Failed to configure 32-bit TCE table, err %ld\n",
+                               rc);
+               pnv_ioda2_table_free(tbl);
+               return rc;
+       }
+
+       if (!pnv_iommu_bypass_disabled)
+               pnv_pci_ioda2_set_bypass(pe, true);
+
+       /* OPAL variant of PHB3 invalidated TCEs */
+       if (pe->phb->ioda.tce_inval_reg)
+               tbl->it_type |= (TCE_PCI_SWINV_CREATE | TCE_PCI_SWINV_FREE);
+
+       /*
+        * Setting table base here only for carrying iommu_group
+        * further down to let iommu_add_device() do the job.
+        * pnv_pci_ioda_dma_dev_setup will override it later anyway.
+        */
+       if (pe->flags & PNV_IODA_PE_DEV)
+               set_iommu_table_base(&pe->pdev->dev, tbl);
+
+       return 0;
+}
+
+#if defined(CONFIG_IOMMU_API) || defined(CONFIG_PCI_IOV)
+static long pnv_pci_ioda2_unset_window(struct iommu_table_group *table_group,
+               int num)
+{
+       struct pnv_ioda_pe *pe = container_of(table_group, struct pnv_ioda_pe,
+                       table_group);
+       struct pnv_phb *phb = pe->phb;
+       long ret;
+
+       pe_info(pe, "Removing DMA window #%d\n", num);
+
+       ret = opal_pci_map_pe_dma_window(phb->opal_id, pe->pe_number,
+                       (pe->pe_number << 1) + num,
+                       0/* levels */, 0/* table address */,
+                       0/* table size */, 0/* page size */);
+       if (ret)
+               pe_warn(pe, "Unmapping failed, ret = %ld\n", ret);
+       else
+               pnv_pci_ioda2_tce_invalidate_entire(pe);
+
+       pnv_pci_unlink_table_and_group(table_group->tables[num], table_group);
+
+       return ret;
+}
+#endif
+
+#ifdef CONFIG_IOMMU_API
+static unsigned long pnv_pci_ioda2_get_table_size(__u32 page_shift,
+               __u64 window_size, __u32 levels)
+{
+       unsigned long bytes = 0;
+       const unsigned window_shift = ilog2(window_size);
+       unsigned entries_shift = window_shift - page_shift;
+       unsigned table_shift = entries_shift + 3;
+       unsigned long tce_table_size = max(0x1000UL, 1UL << table_shift);
+       unsigned long direct_table_size;
+
+       if (!levels || (levels > POWERNV_IOMMU_MAX_LEVELS) ||
+                       (window_size > memory_hotplug_max()) ||
+                       !is_power_of_2(window_size))
+               return 0;
+
+       /* Calculate a direct table size from window_size and levels */
+       entries_shift = (entries_shift + levels - 1) / levels;
+       table_shift = entries_shift + 3;
+       table_shift = max_t(unsigned, table_shift, PAGE_SHIFT);
+       direct_table_size =  1UL << table_shift;
+
+       for ( ; levels; --levels) {
+               bytes += _ALIGN_UP(tce_table_size, direct_table_size);
+
+               tce_table_size /= direct_table_size;
+               tce_table_size <<= 3;
+               tce_table_size = _ALIGN_UP(tce_table_size, direct_table_size);
+       }
+
+       return bytes;
+}
+
+static void pnv_ioda2_take_ownership(struct iommu_table_group *table_group)
+{
+       struct pnv_ioda_pe *pe = container_of(table_group, struct pnv_ioda_pe,
+                                               table_group);
+       /* Store @tbl as pnv_pci_ioda2_unset_window() resets it */
+       struct iommu_table *tbl = pe->table_group.tables[0];
+
+       pnv_pci_ioda2_set_bypass(pe, false);
+       pnv_pci_ioda2_unset_window(&pe->table_group, 0);
+       pnv_ioda2_table_free(tbl);
+}
+
+static void pnv_ioda2_release_ownership(struct iommu_table_group *table_group)
+{
+       struct pnv_ioda_pe *pe = container_of(table_group, struct pnv_ioda_pe,
+                                               table_group);
+
+       pnv_pci_ioda2_setup_default_config(pe);
+}
+
+static struct iommu_table_group_ops pnv_pci_ioda2_ops = {
+       .get_table_size = pnv_pci_ioda2_get_table_size,
+       .create_table = pnv_pci_ioda2_create_table,
+       .set_window = pnv_pci_ioda2_set_window,
+       .unset_window = pnv_pci_ioda2_unset_window,
+       .take_ownership = pnv_ioda2_take_ownership,
+       .release_ownership = pnv_ioda2_release_ownership,
+};
+#endif
+
+static void pnv_pci_ioda_setup_opal_tce_kill(struct pnv_phb *phb)
 {
-       struct page *tce_mem = NULL;
-       void *addr;
        const __be64 *swinvp;
-       struct iommu_table *tbl;
-       unsigned int tce_table_size, end;
-       int64_t rc;
 
-       /* We shouldn't already have a 32-bit DMA associated */
-       if (WARN_ON(pe->tce32_seg >= 0))
+       /* OPAL variant of PHB3 invalidated TCEs */
+       swinvp = of_get_property(phb->hose->dn, "ibm,opal-tce-kill", NULL);
+       if (!swinvp)
                return;
 
-       /* The PE will reserve all possible 32-bits space */
-       pe->tce32_seg = 0;
-       end = (1 << ilog2(phb->ioda.m32_pci_base));
-       tce_table_size = (end / 0x1000) * 8;
-       pe_info(pe, "Setting up 32-bit TCE table at 0..%08x\n",
-               end);
+       phb->ioda.tce_inval_reg_phys = be64_to_cpup(swinvp);
+       phb->ioda.tce_inval_reg = ioremap(phb->ioda.tce_inval_reg_phys, 8);
+}
 
-       /* Allocate TCE table */
-       tce_mem = alloc_pages_node(phb->hose->node, GFP_KERNEL,
-                                  get_order(tce_table_size));
+static __be64 *pnv_pci_ioda2_table_do_alloc_pages(int nid, unsigned shift,
+               unsigned levels, unsigned long limit,
+               unsigned long *current_offset)
+{
+       struct page *tce_mem = NULL;
+       __be64 *addr, *tmp;
+       unsigned order = max_t(unsigned, shift, PAGE_SHIFT) - PAGE_SHIFT;
+       unsigned long allocated = 1UL << (order + PAGE_SHIFT);
+       unsigned entries = 1UL << (shift - 3);
+       long i;
+
+       tce_mem = alloc_pages_node(nid, GFP_KERNEL, order);
        if (!tce_mem) {
-               pe_err(pe, "Failed to allocate a 32-bit TCE memory\n");
-               goto fail;
+               pr_err("Failed to allocate a TCE memory, order=%d\n", order);
+               return NULL;
        }
        addr = page_address(tce_mem);
-       memset(addr, 0, tce_table_size);
+       memset(addr, 0, allocated);
+
+       --levels;
+       if (!levels) {
+               *current_offset += allocated;
+               return addr;
+       }
+
+       for (i = 0; i < entries; ++i) {
+               tmp = pnv_pci_ioda2_table_do_alloc_pages(nid, shift,
+                               levels, limit, current_offset);
+               if (!tmp)
+                       break;
+
+               addr[i] = cpu_to_be64(__pa(tmp) |
+                               TCE_PCI_READ | TCE_PCI_WRITE);
+
+               if (*current_offset >= limit)
+                       break;
+       }
+
+       return addr;
+}
+
+static void pnv_pci_ioda2_table_do_free_pages(__be64 *addr,
+               unsigned long size, unsigned level);
+
+static long pnv_pci_ioda2_table_alloc_pages(int nid, __u64 bus_offset,
+               __u32 page_shift, __u64 window_size, __u32 levels,
+               struct iommu_table *tbl)
+{
+       void *addr;
+       unsigned long offset = 0, level_shift;
+       const unsigned window_shift = ilog2(window_size);
+       unsigned entries_shift = window_shift - page_shift;
+       unsigned table_shift = max_t(unsigned, entries_shift + 3, PAGE_SHIFT);
+       const unsigned long tce_table_size = 1UL << table_shift;
+
+       if (!levels || (levels > POWERNV_IOMMU_MAX_LEVELS))
+               return -EINVAL;
+
+       if ((window_size > memory_hotplug_max()) || !is_power_of_2(window_size))
+               return -EINVAL;
+
+       /* Adjust direct table size from window_size and levels */
+       entries_shift = (entries_shift + levels - 1) / levels;
+       level_shift = entries_shift + 3;
+       level_shift = max_t(unsigned, level_shift, PAGE_SHIFT);
+
+       /* Allocate TCE table */
+       addr = pnv_pci_ioda2_table_do_alloc_pages(nid, level_shift,
+                       levels, tce_table_size, &offset);
+
+       /* addr==NULL means that the first level allocation failed */
+       if (!addr)
+               return -ENOMEM;
 
        /*
-        * Map TCE table through TVT. The TVE index is the PE number
-        * shifted by 1 bit for 32-bits DMA space.
+        * First level was allocated but some lower level failed as
+        * we did not allocate as much as we wanted,
+        * release partially allocated table.
         */
-       rc = opal_pci_map_pe_dma_window(phb->opal_id, pe->pe_number,
-                                       pe->pe_number << 1, 1, __pa(addr),
-                                       tce_table_size, 0x1000);
-       if (rc) {
-               pe_err(pe, "Failed to configure 32-bit TCE table,"
-                      " err %ld\n", rc);
-               goto fail;
+       if (offset < tce_table_size) {
+               pnv_pci_ioda2_table_do_free_pages(addr,
+                               1ULL << (level_shift - 3), levels - 1);
+               return -ENOMEM;
        }
 
        /* Setup linux iommu table */
-       tbl = pe->tce32_table;
-       pnv_pci_setup_iommu_table(tbl, addr, tce_table_size, 0,
-                       IOMMU_PAGE_SHIFT_4K);
+       pnv_pci_setup_iommu_table(tbl, addr, tce_table_size, bus_offset,
+                       page_shift);
+       tbl->it_level_size = 1ULL << (level_shift - 3);
+       tbl->it_indirect_levels = levels - 1;
+       tbl->it_allocated_size = offset;
 
-       /* OPAL variant of PHB3 invalidated TCEs */
-       swinvp = of_get_property(phb->hose->dn, "ibm,opal-tce-kill", NULL);
-       if (swinvp) {
-               /* We need a couple more fields -- an address and a data
-                * to or.  Since the bus is only printed out on table free
-                * errors, and on the first pass the data will be a relative
-                * bus number, print that out instead.
-                */
-               pe->tce_inval_reg_phys = be64_to_cpup(swinvp);
-               tbl->it_index = (unsigned long)ioremap(pe->tce_inval_reg_phys,
-                               8);
-               tbl->it_type |= (TCE_PCI_SWINV_CREATE | TCE_PCI_SWINV_FREE);
+       pr_devel("Created TCE table: ws=%08llx ts=%lx @%08llx\n",
+                       window_size, tce_table_size, bus_offset);
+
+       return 0;
+}
+
+static void pnv_pci_ioda2_table_do_free_pages(__be64 *addr,
+               unsigned long size, unsigned level)
+{
+       const unsigned long addr_ul = (unsigned long) addr &
+                       ~(TCE_PCI_READ | TCE_PCI_WRITE);
+
+       if (level) {
+               long i;
+               u64 *tmp = (u64 *) addr_ul;
+
+               for (i = 0; i < size; ++i) {
+                       unsigned long hpa = be64_to_cpu(tmp[i]);
+
+                       if (!(hpa & (TCE_PCI_READ | TCE_PCI_WRITE)))
+                               continue;
+
+                       pnv_pci_ioda2_table_do_free_pages(__va(hpa), size,
+                                       level - 1);
+               }
        }
-       iommu_init_table(tbl, phb->hose->node);
 
-       if (pe->flags & PNV_IODA_PE_DEV) {
-               iommu_register_group(tbl, phb->hose->global_number,
-                                    pe->pe_number);
-               set_iommu_table_base_and_group(&pe->pdev->dev, tbl);
-       } else if (pe->flags & (PNV_IODA_PE_BUS | PNV_IODA_PE_BUS_ALL)) {
-               iommu_register_group(tbl, phb->hose->global_number,
-                                    pe->pe_number);
-               pnv_ioda_setup_bus_dma(pe, pe->pbus, true);
-       } else if (pe->flags & PNV_IODA_PE_VF) {
-               iommu_register_group(tbl, phb->hose->global_number,
-                                    pe->pe_number);
-       }
-
-       /* Also create a bypass window */
-       if (!pnv_iommu_bypass_disabled)
-               pnv_pci_ioda2_setup_bypass_pe(phb, pe);
+       free_pages(addr_ul, get_order(size << 3));
+}
 
-       return;
-fail:
-       if (pe->tce32_seg >= 0)
-               pe->tce32_seg = -1;
-       if (tce_mem)
-               __free_pages(tce_mem, get_order(tce_table_size));
+static void pnv_pci_ioda2_table_free_pages(struct iommu_table *tbl)
+{
+       const unsigned long size = tbl->it_indirect_levels ?
+                       tbl->it_level_size : tbl->it_size;
+
+       if (!tbl->it_size)
+               return;
+
+       pnv_pci_ioda2_table_do_free_pages((__be64 *)tbl->it_base, size,
+                       tbl->it_indirect_levels);
+}
+
+static void pnv_pci_ioda2_setup_dma_pe(struct pnv_phb *phb,
+                                      struct pnv_ioda_pe *pe)
+{
+       int64_t rc;
+
+       /* We shouldn't already have a 32-bit DMA associated */
+       if (WARN_ON(pe->tce32_seg >= 0))
+               return;
+
+       /* TVE #1 is selected by PCI address bit 59 */
+       pe->tce_bypass_base = 1ull << 59;
+
+       iommu_register_group(&pe->table_group, phb->hose->global_number,
+                       pe->pe_number);
+
+       /* The PE will reserve all possible 32-bits space */
+       pe->tce32_seg = 0;
+       pe_info(pe, "Setting up 32-bit TCE table at 0..%08x\n",
+               phb->ioda.m32_pci_base);
+
+       /* Setup linux iommu table */
+       pe->table_group.tce32_start = 0;
+       pe->table_group.tce32_size = phb->ioda.m32_pci_base;
+       pe->table_group.max_dynamic_windows_supported =
+                       IOMMU_TABLE_GROUP_MAX_TABLES;
+       pe->table_group.max_levels = POWERNV_IOMMU_MAX_LEVELS;
+       pe->table_group.pgsizes = SZ_4K | SZ_64K | SZ_16M;
+#ifdef CONFIG_IOMMU_API
+       pe->table_group.ops = &pnv_pci_ioda2_ops;
+#endif
+
+       rc = pnv_pci_ioda2_setup_default_config(pe);
+       if (rc) {
+               if (pe->tce32_seg >= 0)
+                       pe->tce32_seg = -1;
+               return;
+       }
+
+       if (pe->flags & PNV_IODA_PE_DEV)
+               iommu_add_device(&pe->pdev->dev);
+       else if (pe->flags & (PNV_IODA_PE_BUS | PNV_IODA_PE_BUS_ALL))
+               pnv_ioda_setup_bus_dma(pe, pe->pbus);
 }
 
 static void pnv_ioda_setup_dma(struct pnv_phb *phb)
@@ -2024,6 +2418,8 @@ static void pnv_ioda_setup_dma(struct pnv_phb *phb)
        pr_info("PCI: %d PE# for a total weight of %d\n",
                phb->ioda.dma_pe_count, phb->ioda.dma_weight);
 
+       pnv_pci_ioda_setup_opal_tce_kill(phb);
+
        /* Walk our PE list and configure their DMA segments, hand them
         * out one base segment plus any residual segments based on
         * weight
@@ -2642,12 +3038,27 @@ static u32 pnv_ioda_bdfn_to_pe(struct pnv_phb *phb, struct pci_bus *bus,
        return phb->ioda.pe_rmap[(bus->number << 8) | devfn];
 }
 
-static void pnv_pci_ioda_shutdown(struct pnv_phb *phb)
+static void pnv_pci_ioda_shutdown(struct pci_controller *hose)
 {
+       struct pnv_phb *phb = hose->private_data;
+
        opal_pci_reset(phb->opal_id, OPAL_RESET_PCI_IODA_TABLE,
                       OPAL_ASSERT_RESET);
 }
 
+static const struct pci_controller_ops pnv_pci_ioda_controller_ops = {
+       .dma_dev_setup = pnv_pci_dma_dev_setup,
+#ifdef CONFIG_PCI_MSI
+       .setup_msi_irqs = pnv_setup_msi_irqs,
+       .teardown_msi_irqs = pnv_teardown_msi_irqs,
+#endif
+       .enable_device_hook = pnv_pci_enable_device_hook,
+       .window_alignment = pnv_pci_window_alignment,
+       .reset_secondary_bus = pnv_pci_reset_secondary_bus,
+       .dma_set_mask = pnv_pci_ioda_dma_set_mask,
+       .shutdown = pnv_pci_ioda_shutdown,
+};
+
 static void __init pnv_pci_init_ioda_phb(struct device_node *np,
                                         u64 hub_id, int ioda_type)
 {
@@ -2791,12 +3202,8 @@ static void __init pnv_pci_init_ioda_phb(struct device_node *np,
 
        /* Setup TCEs */
        phb->dma_dev_setup = pnv_pci_ioda_dma_dev_setup;
-       phb->dma_set_mask = pnv_pci_ioda_dma_set_mask;
        phb->dma_get_required_mask = pnv_pci_ioda_dma_get_required_mask;
 
-       /* Setup shutdown function for kexec */
-       phb->shutdown = pnv_pci_ioda_shutdown;
-
        /* Setup MSI support */
        pnv_pci_init_ioda_msis(phb);
 
@@ -2808,10 +3215,7 @@ static void __init pnv_pci_init_ioda_phb(struct device_node *np,
         * the child P2P bridges) can form individual PE.
         */
        ppc_md.pcibios_fixup = pnv_pci_ioda_fixup;
-       pnv_pci_controller_ops.enable_device_hook = pnv_pci_enable_device_hook;
-       pnv_pci_controller_ops.window_alignment = pnv_pci_window_alignment;
-       pnv_pci_controller_ops.reset_secondary_bus = pnv_pci_reset_secondary_bus;
-       hose->controller_ops = pnv_pci_controller_ops;
+       hose->controller_ops = pnv_pci_ioda_controller_ops;
 
 #ifdef CONFIG_PCI_IOV
        ppc_md.pcibios_fixup_sriov = pnv_pci_ioda_fixup_iov_resources;
index 4729ca793813cfe908e0d5818766ac130cfdafd6..f2bdfea3b68d067cb28a91d259e84ec126783001 100644 (file)
@@ -83,18 +83,42 @@ static void pnv_pci_init_p5ioc2_msis(struct pnv_phb *phb)
 static void pnv_pci_init_p5ioc2_msis(struct pnv_phb *phb) { }
 #endif /* CONFIG_PCI_MSI */
 
+static struct iommu_table_ops pnv_p5ioc2_iommu_ops = {
+       .set = pnv_tce_build,
+#ifdef CONFIG_IOMMU_API
+       .exchange = pnv_tce_xchg,
+#endif
+       .clear = pnv_tce_free,
+       .get = pnv_tce_get,
+};
+
 static void pnv_pci_p5ioc2_dma_dev_setup(struct pnv_phb *phb,
                                         struct pci_dev *pdev)
 {
-       if (phb->p5ioc2.iommu_table.it_map == NULL) {
-               iommu_init_table(&phb->p5ioc2.iommu_table, phb->hose->node);
-               iommu_register_group(&phb->p5ioc2.iommu_table,
+       struct iommu_table *tbl = phb->p5ioc2.table_group.tables[0];
+
+       if (!tbl->it_map) {
+               tbl->it_ops = &pnv_p5ioc2_iommu_ops;
+               iommu_init_table(tbl, phb->hose->node);
+               iommu_register_group(&phb->p5ioc2.table_group,
                                pci_domain_nr(phb->hose->bus), phb->opal_id);
+               INIT_LIST_HEAD_RCU(&tbl->it_group_list);
+               pnv_pci_link_table_and_group(phb->hose->node, 0,
+                               tbl, &phb->p5ioc2.table_group);
        }
 
-       set_iommu_table_base_and_group(&pdev->dev, &phb->p5ioc2.iommu_table);
+       set_iommu_table_base(&pdev->dev, tbl);
+       iommu_add_device(&pdev->dev);
 }
 
+static const struct pci_controller_ops pnv_pci_p5ioc2_controller_ops = {
+       .dma_dev_setup = pnv_pci_dma_dev_setup,
+#ifdef CONFIG_PCI_MSI
+       .setup_msi_irqs = pnv_setup_msi_irqs,
+       .teardown_msi_irqs = pnv_teardown_msi_irqs,
+#endif
+};
+
 static void __init pnv_pci_init_p5ioc2_phb(struct device_node *np, u64 hub_id,
                                           void *tce_mem, u64 tce_size)
 {
@@ -103,6 +127,8 @@ static void __init pnv_pci_init_p5ioc2_phb(struct device_node *np, u64 hub_id,
        u64 phb_id;
        int64_t rc;
        static int primary = 1;
+       struct iommu_table_group *table_group;
+       struct iommu_table *tbl;
 
        pr_info(" Initializing p5ioc2 PHB %s\n", np->full_name);
 
@@ -133,7 +159,7 @@ static void __init pnv_pci_init_p5ioc2_phb(struct device_node *np, u64 hub_id,
        phb->hose->first_busno = 0;
        phb->hose->last_busno = 0xff;
        phb->hose->private_data = phb;
-       phb->hose->controller_ops = pnv_pci_controller_ops;
+       phb->hose->controller_ops = pnv_pci_p5ioc2_controller_ops;
        phb->hub_id = hub_id;
        phb->opal_id = phb_id;
        phb->type = PNV_PHB_P5IOC2;
@@ -172,6 +198,15 @@ static void __init pnv_pci_init_p5ioc2_phb(struct device_node *np, u64 hub_id,
        pnv_pci_setup_iommu_table(&phb->p5ioc2.iommu_table,
                                  tce_mem, tce_size, 0,
                                  IOMMU_PAGE_SHIFT_4K);
+       /*
+        * We do not allocate iommu_table as we do not support
+        * hotplug or SRIOV on P5IOC2 and therefore iommu_free_table()
+        * should not be called for phb->p5ioc2.table_group.tables[0] ever.
+        */
+       tbl = phb->p5ioc2.table_group.tables[0] = &phb->p5ioc2.iommu_table;
+       table_group = &phb->p5ioc2.table_group;
+       table_group->tce32_start = tbl->it_offset << tbl->it_page_shift;
+       table_group->tce32_size = tbl->it_size << tbl->it_page_shift;
 }
 
 void __init pnv_pci_init_p5ioc2_hub(struct device_node *np)
index bca2aeb6e4b6a4f179622c4786d79e7f65062069..765d8ed558d0e16a6dddc16dc7d67d6b6b44b2ce 100644 (file)
@@ -45,7 +45,7 @@
 //#define cfg_dbg(fmt...)      printk(fmt)
 
 #ifdef CONFIG_PCI_MSI
-static int pnv_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type)
+int pnv_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type)
 {
        struct pci_controller *hose = pci_bus_to_host(pdev->bus);
        struct pnv_phb *phb = hose->private_data;
@@ -94,7 +94,7 @@ static int pnv_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type)
        return 0;
 }
 
-static void pnv_teardown_msi_irqs(struct pci_dev *pdev)
+void pnv_teardown_msi_irqs(struct pci_dev *pdev)
 {
        struct pci_controller *hose = pci_bus_to_host(pdev->bus);
        struct pnv_phb *phb = hose->private_data;
@@ -572,80 +572,152 @@ struct pci_ops pnv_pci_ops = {
        .write = pnv_pci_write_config,
 };
 
-static int pnv_tce_build(struct iommu_table *tbl, long index, long npages,
-                        unsigned long uaddr, enum dma_data_direction direction,
-                        struct dma_attrs *attrs, bool rm)
+static __be64 *pnv_tce(struct iommu_table *tbl, long idx)
 {
-       u64 proto_tce;
-       __be64 *tcep, *tces;
-       u64 rpn;
-
-       proto_tce = TCE_PCI_READ; // Read allowed
+       __be64 *tmp = ((__be64 *)tbl->it_base);
+       int  level = tbl->it_indirect_levels;
+       const long shift = ilog2(tbl->it_level_size);
+       unsigned long mask = (tbl->it_level_size - 1) << (level * shift);
+
+       while (level) {
+               int n = (idx & mask) >> (level * shift);
+               unsigned long tce = be64_to_cpu(tmp[n]);
+
+               tmp = __va(tce & ~(TCE_PCI_READ | TCE_PCI_WRITE));
+               idx &= ~mask;
+               mask >>= shift;
+               --level;
+       }
 
-       if (direction != DMA_TO_DEVICE)
-               proto_tce |= TCE_PCI_WRITE;
+       return tmp + idx;
+}
 
-       tces = tcep = ((__be64 *)tbl->it_base) + index - tbl->it_offset;
-       rpn = __pa(uaddr) >> tbl->it_page_shift;
+int pnv_tce_build(struct iommu_table *tbl, long index, long npages,
+               unsigned long uaddr, enum dma_data_direction direction,
+               struct dma_attrs *attrs)
+{
+       u64 proto_tce = iommu_direction_to_tce_perm(direction);
+       u64 rpn = __pa(uaddr) >> tbl->it_page_shift;
+       long i;
 
-       while (npages--)
-               *(tcep++) = cpu_to_be64(proto_tce |
-                               (rpn++ << tbl->it_page_shift));
+       for (i = 0; i < npages; i++) {
+               unsigned long newtce = proto_tce |
+                       ((rpn + i) << tbl->it_page_shift);
+               unsigned long idx = index - tbl->it_offset + i;
 
-       /* Some implementations won't cache invalid TCEs and thus may not
-        * need that flush. We'll probably turn it_type into a bit mask
-        * of flags if that becomes the case
-        */
-       if (tbl->it_type & TCE_PCI_SWINV_CREATE)
-               pnv_pci_ioda_tce_invalidate(tbl, tces, tcep - 1, rm);
+               *(pnv_tce(tbl, idx)) = cpu_to_be64(newtce);
+       }
 
        return 0;
 }
 
-static int pnv_tce_build_vm(struct iommu_table *tbl, long index, long npages,
-                           unsigned long uaddr,
-                           enum dma_data_direction direction,
-                           struct dma_attrs *attrs)
+#ifdef CONFIG_IOMMU_API
+int pnv_tce_xchg(struct iommu_table *tbl, long index,
+               unsigned long *hpa, enum dma_data_direction *direction)
 {
-       return pnv_tce_build(tbl, index, npages, uaddr, direction, attrs,
-                       false);
+       u64 proto_tce = iommu_direction_to_tce_perm(*direction);
+       unsigned long newtce = *hpa | proto_tce, oldtce;
+       unsigned long idx = index - tbl->it_offset;
+
+       BUG_ON(*hpa & ~IOMMU_PAGE_MASK(tbl));
+
+       oldtce = xchg(pnv_tce(tbl, idx), cpu_to_be64(newtce));
+       *hpa = be64_to_cpu(oldtce) & ~(TCE_PCI_READ | TCE_PCI_WRITE);
+       *direction = iommu_tce_direction(oldtce);
+
+       return 0;
 }
+#endif
 
-static void pnv_tce_free(struct iommu_table *tbl, long index, long npages,
-               bool rm)
+void pnv_tce_free(struct iommu_table *tbl, long index, long npages)
 {
-       __be64 *tcep, *tces;
+       long i;
 
-       tces = tcep = ((__be64 *)tbl->it_base) + index - tbl->it_offset;
+       for (i = 0; i < npages; i++) {
+               unsigned long idx = index - tbl->it_offset + i;
 
-       while (npages--)
-               *(tcep++) = cpu_to_be64(0);
+               *(pnv_tce(tbl, idx)) = cpu_to_be64(0);
+       }
+}
 
-       if (tbl->it_type & TCE_PCI_SWINV_FREE)
-               pnv_pci_ioda_tce_invalidate(tbl, tces, tcep - 1, rm);
+unsigned long pnv_tce_get(struct iommu_table *tbl, long index)
+{
+       return *(pnv_tce(tbl, index - tbl->it_offset));
 }
 
-static void pnv_tce_free_vm(struct iommu_table *tbl, long index, long npages)
+struct iommu_table *pnv_pci_table_alloc(int nid)
 {
-       pnv_tce_free(tbl, index, npages, false);
+       struct iommu_table *tbl;
+
+       tbl = kzalloc_node(sizeof(struct iommu_table), GFP_KERNEL, nid);
+       INIT_LIST_HEAD_RCU(&tbl->it_group_list);
+
+       return tbl;
 }
 
-static unsigned long pnv_tce_get(struct iommu_table *tbl, long index)
+long pnv_pci_link_table_and_group(int node, int num,
+               struct iommu_table *tbl,
+               struct iommu_table_group *table_group)
 {
-       return ((u64 *)tbl->it_base)[index - tbl->it_offset];
+       struct iommu_table_group_link *tgl = NULL;
+
+       if (WARN_ON(!tbl || !table_group))
+               return -EINVAL;
+
+       tgl = kzalloc_node(sizeof(struct iommu_table_group_link), GFP_KERNEL,
+                       node);
+       if (!tgl)
+               return -ENOMEM;
+
+       tgl->table_group = table_group;
+       list_add_rcu(&tgl->next, &tbl->it_group_list);
+
+       table_group->tables[num] = tbl;
+
+       return 0;
 }
 
-static int pnv_tce_build_rm(struct iommu_table *tbl, long index, long npages,
-                           unsigned long uaddr,
-                           enum dma_data_direction direction,
-                           struct dma_attrs *attrs)
+static void pnv_iommu_table_group_link_free(struct rcu_head *head)
 {
-       return pnv_tce_build(tbl, index, npages, uaddr, direction, attrs, true);
+       struct iommu_table_group_link *tgl = container_of(head,
+                       struct iommu_table_group_link, rcu);
+
+       kfree(tgl);
 }
 
-static void pnv_tce_free_rm(struct iommu_table *tbl, long index, long npages)
+void pnv_pci_unlink_table_and_group(struct iommu_table *tbl,
+               struct iommu_table_group *table_group)
 {
-       pnv_tce_free(tbl, index, npages, true);
+       long i;
+       bool found;
+       struct iommu_table_group_link *tgl;
+
+       if (!tbl || !table_group)
+               return;
+
+       /* Remove link to a group from table's list of attached groups */
+       found = false;
+       list_for_each_entry_rcu(tgl, &tbl->it_group_list, next) {
+               if (tgl->table_group == table_group) {
+                       list_del_rcu(&tgl->next);
+                       call_rcu(&tgl->rcu, pnv_iommu_table_group_link_free);
+                       found = true;
+                       break;
+               }
+       }
+       if (WARN_ON(!found))
+               return;
+
+       /* Clean a pointer to iommu_table in iommu_table_group::tables[] */
+       found = false;
+       for (i = 0; i < IOMMU_TABLE_GROUP_MAX_TABLES; ++i) {
+               if (table_group->tables[i] == tbl) {
+                       table_group->tables[i] = NULL;
+                       found = true;
+                       break;
+               }
+       }
+       WARN_ON(!found);
 }
 
 void pnv_pci_setup_iommu_table(struct iommu_table *tbl,
@@ -662,7 +734,7 @@ void pnv_pci_setup_iommu_table(struct iommu_table *tbl,
        tbl->it_type = TCE_PCI;
 }
 
-static void pnv_pci_dma_dev_setup(struct pci_dev *pdev)
+void pnv_pci_dma_dev_setup(struct pci_dev *pdev)
 {
        struct pci_controller *hose = pci_bus_to_host(pdev->bus);
        struct pnv_phb *phb = hose->private_data;
@@ -689,16 +761,6 @@ static void pnv_pci_dma_dev_setup(struct pci_dev *pdev)
                phb->dma_dev_setup(phb, pdev);
 }
 
-int pnv_pci_dma_set_mask(struct pci_dev *pdev, u64 dma_mask)
-{
-       struct pci_controller *hose = pci_bus_to_host(pdev->bus);
-       struct pnv_phb *phb = hose->private_data;
-
-       if (phb && phb->dma_set_mask)
-               return phb->dma_set_mask(phb, pdev, dma_mask);
-       return __dma_set_mask(&pdev->dev, dma_mask);
-}
-
 u64 pnv_pci_dma_get_required_mask(struct pci_dev *pdev)
 {
        struct pci_controller *hose = pci_bus_to_host(pdev->bus);
@@ -714,12 +776,9 @@ void pnv_pci_shutdown(void)
 {
        struct pci_controller *hose;
 
-       list_for_each_entry(hose, &hose_list, list_node) {
-               struct pnv_phb *phb = hose->private_data;
-
-               if (phb && phb->shutdown)
-                       phb->shutdown(phb);
-       }
+       list_for_each_entry(hose, &hose_list, list_node)
+               if (hose->controller_ops.shutdown)
+                       hose->controller_ops.shutdown(hose);
 }
 
 /* Fixup wrong class code in p7ioc and p8 root complex */
@@ -762,22 +821,7 @@ void __init pnv_pci_init(void)
        pci_devs_phb_init();
 
        /* Configure IOMMU DMA hooks */
-       ppc_md.tce_build = pnv_tce_build_vm;
-       ppc_md.tce_free = pnv_tce_free_vm;
-       ppc_md.tce_build_rm = pnv_tce_build_rm;
-       ppc_md.tce_free_rm = pnv_tce_free_rm;
-       ppc_md.tce_get = pnv_tce_get;
        set_pci_dma_ops(&dma_iommu_ops);
-
-       /* Configure MSIs */
-#ifdef CONFIG_PCI_MSI
-       ppc_md.setup_msi_irqs = pnv_setup_msi_irqs;
-       ppc_md.teardown_msi_irqs = pnv_teardown_msi_irqs;
-#endif
 }
 
 machine_subsys_initcall_sync(powernv, tce_iommu_bus_notifier_init);
-
-struct pci_controller_ops pnv_pci_controller_ops = {
-       .dma_dev_setup = pnv_pci_dma_dev_setup,
-};
index 070ee888fc95cef223337e3bd945726ffc7ac7cc..8ef2d28aded0f6ebda3a0d9f767726927817dac5 100644 (file)
@@ -57,8 +57,7 @@ struct pnv_ioda_pe {
        /* "Base" iommu table, ie, 4K TCEs, 32-bit DMA */
        int                     tce32_seg;
        int                     tce32_segcount;
-       struct iommu_table      *tce32_table;
-       phys_addr_t             tce_inval_reg_phys;
+       struct iommu_table_group table_group;
 
        /* 64-bit TCE bypass region */
        bool                    tce_bypass_enabled;
@@ -106,13 +105,10 @@ struct pnv_phb {
                         unsigned int hwirq, unsigned int virq,
                         unsigned int is_64, struct msi_msg *msg);
        void (*dma_dev_setup)(struct pnv_phb *phb, struct pci_dev *pdev);
-       int (*dma_set_mask)(struct pnv_phb *phb, struct pci_dev *pdev,
-                           u64 dma_mask);
        u64 (*dma_get_required_mask)(struct pnv_phb *phb,
                                     struct pci_dev *pdev);
        void (*fixup_phb)(struct pci_controller *hose);
        u32 (*bdfn_to_pe)(struct pnv_phb *phb, struct pci_bus *bus, u32 devfn);
-       void (*shutdown)(struct pnv_phb *phb);
        int (*init_m64)(struct pnv_phb *phb);
        void (*reserve_m64_pe)(struct pnv_phb *phb);
        int (*pick_m64_pe)(struct pnv_phb *phb, struct pci_bus *bus, int all);
@@ -123,6 +119,7 @@ struct pnv_phb {
        union {
                struct {
                        struct iommu_table iommu_table;
+                       struct iommu_table_group table_group;
                } p5ioc2;
 
                struct {
@@ -186,6 +183,12 @@ struct pnv_phb {
                         * boot for resource allocation purposes
                         */
                        struct list_head        pe_dma_list;
+
+                       /* TCE cache invalidate registers (physical and
+                        * remapped)
+                        */
+                       phys_addr_t             tce_inval_reg_phys;
+                       __be64 __iomem          *tce_inval_reg;
                } ioda;
        };
 
@@ -200,6 +203,13 @@ struct pnv_phb {
 };
 
 extern struct pci_ops pnv_pci_ops;
+extern int pnv_tce_build(struct iommu_table *tbl, long index, long npages,
+               unsigned long uaddr, enum dma_data_direction direction,
+               struct dma_attrs *attrs);
+extern void pnv_tce_free(struct iommu_table *tbl, long index, long npages);
+extern int pnv_tce_xchg(struct iommu_table *tbl, long index,
+               unsigned long *hpa, enum dma_data_direction *direction);
+extern unsigned long pnv_tce_get(struct iommu_table *tbl, long index);
 
 void pnv_pci_dump_phb_diag_data(struct pci_controller *hose,
                                unsigned char *log_buff);
@@ -207,6 +217,13 @@ int pnv_pci_cfg_read(struct pci_dn *pdn,
                     int where, int size, u32 *val);
 int pnv_pci_cfg_write(struct pci_dn *pdn,
                      int where, int size, u32 val);
+extern struct iommu_table *pnv_pci_table_alloc(int nid);
+
+extern long pnv_pci_link_table_and_group(int node, int num,
+               struct iommu_table *tbl,
+               struct iommu_table_group *table_group);
+extern void pnv_pci_unlink_table_and_group(struct iommu_table *tbl,
+               struct iommu_table_group *table_group);
 extern void pnv_pci_setup_iommu_table(struct iommu_table *tbl,
                                      void *tce_mem, u64 tce_size,
                                      u64 dma_offset, unsigned page_shift);
@@ -218,4 +235,8 @@ extern void pnv_pci_ioda_tce_invalidate(struct iommu_table *tbl,
 extern void pnv_pci_reset_secondary_bus(struct pci_dev *dev);
 extern int pnv_eeh_phb_reset(struct pci_controller *hose, int option);
 
+extern void pnv_pci_dma_dev_setup(struct pci_dev *pdev);
+extern int pnv_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type);
+extern void pnv_teardown_msi_irqs(struct pci_dev *pdev);
+
 #endif /* __POWERNV_PCI_H */
index 826d2c9bea5693864c44143f2e3fb7e5a66cb609..9269e30e4ca0e75efcf476910809c9508a5b0698 100644 (file)
@@ -12,29 +12,24 @@ struct pci_dev;
 #ifdef CONFIG_PCI
 extern void pnv_pci_init(void);
 extern void pnv_pci_shutdown(void);
-extern int pnv_pci_dma_set_mask(struct pci_dev *pdev, u64 dma_mask);
 extern u64 pnv_pci_dma_get_required_mask(struct pci_dev *pdev);
 #else
 static inline void pnv_pci_init(void) { }
 static inline void pnv_pci_shutdown(void) { }
 
-static inline int pnv_pci_dma_set_mask(struct pci_dev *pdev, u64 dma_mask)
-{
-       return -ENODEV;
-}
-
 static inline u64 pnv_pci_dma_get_required_mask(struct pci_dev *pdev)
 {
        return 0;
 }
 #endif
 
-extern struct pci_controller_ops pnv_pci_controller_ops;
-
 extern u32 pnv_get_supported_cpuidle_states(void);
 
 extern void pnv_lpc_init(void);
 
+extern void opal_handle_events(uint64_t events);
+extern void opal_event_shutdown(void);
+
 bool cpu_core_split_required(void);
 
 #endif /* _POWERNV_H */
index 16fdcb23f4c3ab12f4d392b6324e5fd79ddb3b14..53737e019ae362dba4e1c52f30c55279dd4623fe 100644 (file)
 #include <asm/opal.h>
 #include <asm/kexec.h>
 #include <asm/smp.h>
-#include <asm/cputhreads.h>
-#include <asm/cpuidle.h>
-#include <asm/code-patching.h>
 
 #include "powernv.h"
-#include "subcore.h"
 
 static void __init pnv_setup_arch(void)
 {
@@ -111,7 +107,7 @@ static void pnv_prepare_going_down(void)
         * Disable all notifiers from OPAL, we can't
         * service interrupts anymore anyway
         */
-       opal_notifier_disable();
+       opal_event_shutdown();
 
        /* Soft disable interrupts */
        local_irq_disable();
@@ -169,13 +165,6 @@ static void pnv_progress(char *s, unsigned short hex)
 {
 }
 
-static int pnv_dma_set_mask(struct device *dev, u64 dma_mask)
-{
-       if (dev_is_pci(dev))
-               return pnv_pci_dma_set_mask(to_pci_dev(dev), dma_mask);
-       return __dma_set_mask(dev, dma_mask);
-}
-
 static u64 pnv_dma_get_required_mask(struct device *dev)
 {
        if (dev_is_pci(dev))
@@ -277,173 +266,6 @@ static void __init pnv_setup_machdep_opal(void)
        ppc_md.handle_hmi_exception = opal_handle_hmi_exception;
 }
 
-static u32 supported_cpuidle_states;
-
-int pnv_save_sprs_for_winkle(void)
-{
-       int cpu;
-       int rc;
-
-       /*
-        * hid0, hid1, hid4, hid5, hmeer and lpcr values are symmetric accross
-        * all cpus at boot. Get these reg values of current cpu and use the
-        * same accross all cpus.
-        */
-       uint64_t lpcr_val = mfspr(SPRN_LPCR) & ~(u64)LPCR_PECE1;
-       uint64_t hid0_val = mfspr(SPRN_HID0);
-       uint64_t hid1_val = mfspr(SPRN_HID1);
-       uint64_t hid4_val = mfspr(SPRN_HID4);
-       uint64_t hid5_val = mfspr(SPRN_HID5);
-       uint64_t hmeer_val = mfspr(SPRN_HMEER);
-
-       for_each_possible_cpu(cpu) {
-               uint64_t pir = get_hard_smp_processor_id(cpu);
-               uint64_t hsprg0_val = (uint64_t)&paca[cpu];
-
-               /*
-                * HSPRG0 is used to store the cpu's pointer to paca. Hence last
-                * 3 bits are guaranteed to be 0. Program slw to restore HSPRG0
-                * with 63rd bit set, so that when a thread wakes up at 0x100 we
-                * can use this bit to distinguish between fastsleep and
-                * deep winkle.
-                */
-               hsprg0_val |= 1;
-
-               rc = opal_slw_set_reg(pir, SPRN_HSPRG0, hsprg0_val);
-               if (rc != 0)
-                       return rc;
-
-               rc = opal_slw_set_reg(pir, SPRN_LPCR, lpcr_val);
-               if (rc != 0)
-                       return rc;
-
-               /* HIDs are per core registers */
-               if (cpu_thread_in_core(cpu) == 0) {
-
-                       rc = opal_slw_set_reg(pir, SPRN_HMEER, hmeer_val);
-                       if (rc != 0)
-                               return rc;
-
-                       rc = opal_slw_set_reg(pir, SPRN_HID0, hid0_val);
-                       if (rc != 0)
-                               return rc;
-
-                       rc = opal_slw_set_reg(pir, SPRN_HID1, hid1_val);
-                       if (rc != 0)
-                               return rc;
-
-                       rc = opal_slw_set_reg(pir, SPRN_HID4, hid4_val);
-                       if (rc != 0)
-                               return rc;
-
-                       rc = opal_slw_set_reg(pir, SPRN_HID5, hid5_val);
-                       if (rc != 0)
-                               return rc;
-               }
-       }
-
-       return 0;
-}
-
-static void pnv_alloc_idle_core_states(void)
-{
-       int i, j;
-       int nr_cores = cpu_nr_cores();
-       u32 *core_idle_state;
-
-       /*
-        * core_idle_state - First 8 bits track the idle state of each thread
-        * of the core. The 8th bit is the lock bit. Initially all thread bits
-        * are set. They are cleared when the thread enters deep idle state
-        * like sleep and winkle. Initially the lock bit is cleared.
-        * The lock bit has 2 purposes
-        * a. While the first thread is restoring core state, it prevents
-        * other threads in the core from switching to process context.
-        * b. While the last thread in the core is saving the core state, it
-        * prevents a different thread from waking up.
-        */
-       for (i = 0; i < nr_cores; i++) {
-               int first_cpu = i * threads_per_core;
-               int node = cpu_to_node(first_cpu);
-
-               core_idle_state = kmalloc_node(sizeof(u32), GFP_KERNEL, node);
-               *core_idle_state = PNV_CORE_IDLE_THREAD_BITS;
-
-               for (j = 0; j < threads_per_core; j++) {
-                       int cpu = first_cpu + j;
-
-                       paca[cpu].core_idle_state_ptr = core_idle_state;
-                       paca[cpu].thread_idle_state = PNV_THREAD_RUNNING;
-                       paca[cpu].thread_mask = 1 << j;
-               }
-       }
-
-       update_subcore_sibling_mask();
-
-       if (supported_cpuidle_states & OPAL_PM_WINKLE_ENABLED)
-               pnv_save_sprs_for_winkle();
-}
-
-u32 pnv_get_supported_cpuidle_states(void)
-{
-       return supported_cpuidle_states;
-}
-EXPORT_SYMBOL_GPL(pnv_get_supported_cpuidle_states);
-
-static int __init pnv_init_idle_states(void)
-{
-       struct device_node *power_mgt;
-       int dt_idle_states;
-       u32 *flags;
-       int i;
-
-       supported_cpuidle_states = 0;
-
-       if (cpuidle_disable != IDLE_NO_OVERRIDE)
-               goto out;
-
-       if (!firmware_has_feature(FW_FEATURE_OPALv3))
-               goto out;
-
-       power_mgt = of_find_node_by_path("/ibm,opal/power-mgt");
-       if (!power_mgt) {
-               pr_warn("opal: PowerMgmt Node not found\n");
-               goto out;
-       }
-       dt_idle_states = of_property_count_u32_elems(power_mgt,
-                       "ibm,cpu-idle-state-flags");
-       if (dt_idle_states < 0) {
-               pr_warn("cpuidle-powernv: no idle states found in the DT\n");
-               goto out;
-       }
-
-       flags = kzalloc(sizeof(*flags) * dt_idle_states, GFP_KERNEL);
-       if (of_property_read_u32_array(power_mgt,
-                       "ibm,cpu-idle-state-flags", flags, dt_idle_states)) {
-               pr_warn("cpuidle-powernv: missing ibm,cpu-idle-state-flags in DT\n");
-               goto out_free;
-       }
-
-       for (i = 0; i < dt_idle_states; i++)
-               supported_cpuidle_states |= flags[i];
-
-       if (!(supported_cpuidle_states & OPAL_PM_SLEEP_ENABLED_ER1)) {
-               patch_instruction(
-                       (unsigned int *)pnv_fastsleep_workaround_at_entry,
-                       PPC_INST_NOP);
-               patch_instruction(
-                       (unsigned int *)pnv_fastsleep_workaround_at_exit,
-                       PPC_INST_NOP);
-       }
-       pnv_alloc_idle_core_states();
-out_free:
-       kfree(flags);
-out:
-       return 0;
-}
-
-subsys_initcall(pnv_init_idle_states);
-
 static int __init pnv_probe(void)
 {
        unsigned long root = of_get_flat_dt_root();
@@ -492,7 +314,6 @@ define_machine(powernv) {
        .machine_shutdown       = pnv_shutdown,
        .power_save             = power7_idle,
        .calibrate_decr         = generic_calibrate_decr,
-       .dma_set_mask           = pnv_dma_set_mask,
        .dma_get_required_mask  = pnv_dma_get_required_mask,
 #ifdef CONFIG_KEXEC
        .kexec_cpu_down         = pnv_kexec_cpu_down,
index 019d34aaf054bc843d6a2d5ac531e9207e6c42ab..47d9cebe7159edb2d5d72a3167a89d928dbaa5dc 100644 (file)
@@ -421,11 +421,10 @@ static ssize_t dlpar_cpu_probe(const char *buf, size_t count)
                return -ENODEV;
 
        dn = dlpar_configure_connector(cpu_to_be32(drc_index), parent);
+       of_node_put(parent);
        if (!dn)
                return -EINVAL;
 
-       of_node_put(parent);
-
        rc = dlpar_attach_node(dn);
        if (rc) {
                dlpar_release_drc(drc_index);
index 2039397cc75d5f6230dc91c945fc7a79dc0ecc68..1ba55d0bb449ddd79cbe6a644a265d09c730a1a5 100644 (file)
@@ -519,7 +519,7 @@ static int pseries_eeh_reset(struct eeh_pe *pe, int option)
 /**
  * pseries_eeh_wait_state - Wait for PE state
  * @pe: EEH PE
- * @max_wait: maximal period in microsecond
+ * @max_wait: maximal period in millisecond
  *
  * Wait for the state of associated PE. It might take some time
  * to retrieve the PE's state.
index 61d5a17f45c0b5dff8265dc8b1606ef7c08b2ce5..10510dea16b31a30be6382a0b9a8c5c9c4e1affd 100644 (file)
@@ -36,6 +36,8 @@
 #include <linux/crash_dump.h>
 #include <linux/memory.h>
 #include <linux/of.h>
+#include <linux/iommu.h>
+#include <linux/rculist.h>
 #include <asm/io.h>
 #include <asm/prom.h>
 #include <asm/rtas.h>
 
 #include "pseries.h"
 
+static struct iommu_table_group *iommu_pseries_alloc_group(int node)
+{
+       struct iommu_table_group *table_group = NULL;
+       struct iommu_table *tbl = NULL;
+       struct iommu_table_group_link *tgl = NULL;
+
+       table_group = kzalloc_node(sizeof(struct iommu_table_group), GFP_KERNEL,
+                          node);
+       if (!table_group)
+               goto fail_exit;
+
+       tbl = kzalloc_node(sizeof(struct iommu_table), GFP_KERNEL, node);
+       if (!tbl)
+               goto fail_exit;
+
+       tgl = kzalloc_node(sizeof(struct iommu_table_group_link), GFP_KERNEL,
+                       node);
+       if (!tgl)
+               goto fail_exit;
+
+       INIT_LIST_HEAD_RCU(&tbl->it_group_list);
+       tgl->table_group = table_group;
+       list_add_rcu(&tgl->next, &tbl->it_group_list);
+
+       table_group->tables[0] = tbl;
+
+       return table_group;
+
+fail_exit:
+       kfree(tgl);
+       kfree(table_group);
+       kfree(tbl);
+
+       return NULL;
+}
+
+static void iommu_pseries_free_group(struct iommu_table_group *table_group,
+               const char *node_name)
+{
+       struct iommu_table *tbl;
+#ifdef CONFIG_IOMMU_API
+       struct iommu_table_group_link *tgl;
+#endif
+
+       if (!table_group)
+               return;
+
+       tbl = table_group->tables[0];
+#ifdef CONFIG_IOMMU_API
+       tgl = list_first_entry_or_null(&tbl->it_group_list,
+                       struct iommu_table_group_link, next);
+
+       WARN_ON_ONCE(!tgl);
+       if (tgl) {
+               list_del_rcu(&tgl->next);
+               kfree(tgl);
+       }
+       if (table_group->group) {
+               iommu_group_put(table_group->group);
+               BUG_ON(table_group->group);
+       }
+#endif
+       iommu_free_table(tbl, node_name);
+
+       kfree(table_group);
+}
+
 static void tce_invalidate_pSeries_sw(struct iommu_table *tbl,
                                      __be64 *startp, __be64 *endp)
 {
@@ -193,7 +262,7 @@ static int tce_buildmulti_pSeriesLP(struct iommu_table *tbl, long tcenum,
        int ret = 0;
        unsigned long flags;
 
-       if (npages == 1) {
+       if ((npages == 1) || !firmware_has_feature(FW_FEATURE_MULTITCE)) {
                return tce_build_pSeriesLP(tbl, tcenum, npages, uaddr,
                                           direction, attrs);
        }
@@ -285,6 +354,9 @@ static void tce_freemulti_pSeriesLP(struct iommu_table *tbl, long tcenum, long n
 {
        u64 rc;
 
+       if (!firmware_has_feature(FW_FEATURE_MULTITCE))
+               return tce_free_pSeriesLP(tbl, tcenum, npages);
+
        rc = plpar_tce_stuff((u64)tbl->it_index, (u64)tcenum << 12, 0, npages);
 
        if (rc && printk_ratelimit()) {
@@ -460,7 +532,6 @@ static int tce_setrange_multi_pSeriesLP_walk(unsigned long start_pfn,
        return tce_setrange_multi_pSeriesLP(start_pfn, num_pfn, arg);
 }
 
-
 #ifdef CONFIG_PCI
 static void iommu_table_setparms(struct pci_controller *phb,
                                 struct device_node *dn,
@@ -546,6 +617,12 @@ static void iommu_table_setparms_lpar(struct pci_controller *phb,
        tbl->it_size = size >> tbl->it_page_shift;
 }
 
+struct iommu_table_ops iommu_table_pseries_ops = {
+       .set = tce_build_pSeries,
+       .clear = tce_free_pSeries,
+       .get = tce_get_pseries
+};
+
 static void pci_dma_bus_setup_pSeries(struct pci_bus *bus)
 {
        struct device_node *dn;
@@ -610,12 +687,13 @@ static void pci_dma_bus_setup_pSeries(struct pci_bus *bus)
        pci->phb->dma_window_size = 0x8000000ul;
        pci->phb->dma_window_base_cur = 0x8000000ul;
 
-       tbl = kzalloc_node(sizeof(struct iommu_table), GFP_KERNEL,
-                          pci->phb->node);
+       pci->table_group = iommu_pseries_alloc_group(pci->phb->node);
+       tbl = pci->table_group->tables[0];
 
        iommu_table_setparms(pci->phb, dn, tbl);
-       pci->iommu_table = iommu_init_table(tbl, pci->phb->node);
-       iommu_register_group(tbl, pci_domain_nr(bus), 0);
+       tbl->it_ops = &iommu_table_pseries_ops;
+       iommu_init_table(tbl, pci->phb->node);
+       iommu_register_group(pci->table_group, pci_domain_nr(bus), 0);
 
        /* Divide the rest (1.75GB) among the children */
        pci->phb->dma_window_size = 0x80000000ul;
@@ -625,6 +703,11 @@ static void pci_dma_bus_setup_pSeries(struct pci_bus *bus)
        pr_debug("ISA/IDE, window size is 0x%llx\n", pci->phb->dma_window_size);
 }
 
+struct iommu_table_ops iommu_table_lpar_multi_ops = {
+       .set = tce_buildmulti_pSeriesLP,
+       .clear = tce_freemulti_pSeriesLP,
+       .get = tce_get_pSeriesLP
+};
 
 static void pci_dma_bus_setup_pSeriesLP(struct pci_bus *bus)
 {
@@ -653,15 +736,17 @@ static void pci_dma_bus_setup_pSeriesLP(struct pci_bus *bus)
        ppci = PCI_DN(pdn);
 
        pr_debug("  parent is %s, iommu_table: 0x%p\n",
-                pdn->full_name, ppci->iommu_table);
+                pdn->full_name, ppci->table_group);
 
-       if (!ppci->iommu_table) {
-               tbl = kzalloc_node(sizeof(struct iommu_table), GFP_KERNEL,
-                                  ppci->phb->node);
+       if (!ppci->table_group) {
+               ppci->table_group = iommu_pseries_alloc_group(ppci->phb->node);
+               tbl = ppci->table_group->tables[0];
                iommu_table_setparms_lpar(ppci->phb, pdn, tbl, dma_window);
-               ppci->iommu_table = iommu_init_table(tbl, ppci->phb->node);
-               iommu_register_group(tbl, pci_domain_nr(bus), 0);
-               pr_debug("  created table: %p\n", ppci->iommu_table);
+               tbl->it_ops = &iommu_table_lpar_multi_ops;
+               iommu_init_table(tbl, ppci->phb->node);
+               iommu_register_group(ppci->table_group,
+                               pci_domain_nr(bus), 0);
+               pr_debug("  created table: %p\n", ppci->table_group);
        }
 }
 
@@ -683,13 +768,15 @@ static void pci_dma_dev_setup_pSeries(struct pci_dev *dev)
                struct pci_controller *phb = PCI_DN(dn)->phb;
 
                pr_debug(" --> first child, no bridge. Allocating iommu table.\n");
-               tbl = kzalloc_node(sizeof(struct iommu_table), GFP_KERNEL,
-                                  phb->node);
+               PCI_DN(dn)->table_group = iommu_pseries_alloc_group(phb->node);
+               tbl = PCI_DN(dn)->table_group->tables[0];
                iommu_table_setparms(phb, dn, tbl);
-               PCI_DN(dn)->iommu_table = iommu_init_table(tbl, phb->node);
-               iommu_register_group(tbl, pci_domain_nr(phb->bus), 0);
-               set_iommu_table_base_and_group(&dev->dev,
-                                              PCI_DN(dn)->iommu_table);
+               tbl->it_ops = &iommu_table_pseries_ops;
+               iommu_init_table(tbl, phb->node);
+               iommu_register_group(PCI_DN(dn)->table_group,
+                               pci_domain_nr(phb->bus), 0);
+               set_iommu_table_base(&dev->dev, tbl);
+               iommu_add_device(&dev->dev);
                return;
        }
 
@@ -697,13 +784,14 @@ static void pci_dma_dev_setup_pSeries(struct pci_dev *dev)
         * an already allocated iommu table is found and use that.
         */
 
-       while (dn && PCI_DN(dn) && PCI_DN(dn)->iommu_table == NULL)
+       while (dn && PCI_DN(dn) && PCI_DN(dn)->table_group == NULL)
                dn = dn->parent;
 
-       if (dn && PCI_DN(dn))
-               set_iommu_table_base_and_group(&dev->dev,
-                                              PCI_DN(dn)->iommu_table);
-       else
+       if (dn && PCI_DN(dn)) {
+               set_iommu_table_base(&dev->dev,
+                               PCI_DN(dn)->table_group->tables[0]);
+               iommu_add_device(&dev->dev);
+       } else
                printk(KERN_WARNING "iommu: Device %s has no iommu table\n",
                       pci_name(dev));
 }
@@ -1088,7 +1176,7 @@ static void pci_dma_dev_setup_pSeriesLP(struct pci_dev *dev)
        dn = pci_device_to_OF_node(dev);
        pr_debug("  node is %s\n", dn->full_name);
 
-       for (pdn = dn; pdn && PCI_DN(pdn) && !PCI_DN(pdn)->iommu_table;
+       for (pdn = dn; pdn && PCI_DN(pdn) && !PCI_DN(pdn)->table_group;
             pdn = pdn->parent) {
                dma_window = of_get_property(pdn, "ibm,dma-window", NULL);
                if (dma_window)
@@ -1104,18 +1192,21 @@ static void pci_dma_dev_setup_pSeriesLP(struct pci_dev *dev)
        pr_debug("  parent is %s\n", pdn->full_name);
 
        pci = PCI_DN(pdn);
-       if (!pci->iommu_table) {
-               tbl = kzalloc_node(sizeof(struct iommu_table), GFP_KERNEL,
-                                  pci->phb->node);
+       if (!pci->table_group) {
+               pci->table_group = iommu_pseries_alloc_group(pci->phb->node);
+               tbl = pci->table_group->tables[0];
                iommu_table_setparms_lpar(pci->phb, pdn, tbl, dma_window);
-               pci->iommu_table = iommu_init_table(tbl, pci->phb->node);
-               iommu_register_group(tbl, pci_domain_nr(pci->phb->bus), 0);
-               pr_debug("  created table: %p\n", pci->iommu_table);
+               tbl->it_ops = &iommu_table_lpar_multi_ops;
+               iommu_init_table(tbl, pci->phb->node);
+               iommu_register_group(pci->table_group,
+                               pci_domain_nr(pci->phb->bus), 0);
+               pr_debug("  created table: %p\n", pci->table_group);
        } else {
-               pr_debug("  found DMA window, table: %p\n", pci->iommu_table);
+               pr_debug("  found DMA window, table: %p\n", pci->table_group);
        }
 
-       set_iommu_table_base_and_group(&dev->dev, pci->iommu_table);
+       set_iommu_table_base(&dev->dev, pci->table_group->tables[0]);
+       iommu_add_device(&dev->dev);
 }
 
 static int dma_set_mask_pSeriesLP(struct device *dev, u64 dma_mask)
@@ -1145,7 +1236,7 @@ static int dma_set_mask_pSeriesLP(struct device *dev, u64 dma_mask)
                 * search upwards in the tree until we either hit a dma-window
                 * property, OR find a parent with a table already allocated.
                 */
-               for (pdn = dn; pdn && PCI_DN(pdn) && !PCI_DN(pdn)->iommu_table;
+               for (pdn = dn; pdn && PCI_DN(pdn) && !PCI_DN(pdn)->table_group;
                                pdn = pdn->parent) {
                        dma_window = of_get_property(pdn, "ibm,dma-window", NULL);
                        if (dma_window)
@@ -1189,7 +1280,7 @@ static u64 dma_get_required_mask_pSeriesLP(struct device *dev)
                dn = pci_device_to_OF_node(pdev);
 
                /* search upwards for ibm,dma-window */
-               for (; dn && PCI_DN(dn) && !PCI_DN(dn)->iommu_table;
+               for (; dn && PCI_DN(dn) && !PCI_DN(dn)->table_group;
                                dn = dn->parent)
                        if (of_get_property(dn, "ibm,dma-window", NULL))
                                break;
@@ -1269,8 +1360,9 @@ static int iommu_reconfig_notifier(struct notifier_block *nb, unsigned long acti
                 * the device node.
                 */
                remove_ddw(np, false);
-               if (pci && pci->iommu_table)
-                       iommu_free_table(pci->iommu_table, np->full_name);
+               if (pci && pci->table_group)
+                       iommu_pseries_free_group(pci->table_group,
+                                       np->full_name);
 
                spin_lock(&direct_window_list_lock);
                list_for_each_entry(window, &direct_window_list, list) {
@@ -1300,22 +1392,11 @@ void iommu_init_early_pSeries(void)
                return;
 
        if (firmware_has_feature(FW_FEATURE_LPAR)) {
-               if (firmware_has_feature(FW_FEATURE_MULTITCE)) {
-                       ppc_md.tce_build = tce_buildmulti_pSeriesLP;
-                       ppc_md.tce_free  = tce_freemulti_pSeriesLP;
-               } else {
-                       ppc_md.tce_build = tce_build_pSeriesLP;
-                       ppc_md.tce_free  = tce_free_pSeriesLP;
-               }
-               ppc_md.tce_get   = tce_get_pSeriesLP;
                pseries_pci_controller_ops.dma_bus_setup = pci_dma_bus_setup_pSeriesLP;
                pseries_pci_controller_ops.dma_dev_setup = pci_dma_dev_setup_pSeriesLP;
                ppc_md.dma_set_mask = dma_set_mask_pSeriesLP;
                ppc_md.dma_get_required_mask = dma_get_required_mask_pSeriesLP;
        } else {
-               ppc_md.tce_build = tce_build_pSeries;
-               ppc_md.tce_free  = tce_free_pSeries;
-               ppc_md.tce_get   = tce_get_pseries;
                pseries_pci_controller_ops.dma_bus_setup = pci_dma_bus_setup_pSeries;
                pseries_pci_controller_ops.dma_dev_setup = pci_dma_dev_setup_pSeries;
        }
@@ -1333,8 +1414,6 @@ static int __init disable_multitce(char *str)
            firmware_has_feature(FW_FEATURE_LPAR) &&
            firmware_has_feature(FW_FEATURE_MULTITCE)) {
                printk(KERN_INFO "Disabling MULTITCE firmware feature\n");
-               ppc_md.tce_build = tce_build_pSeriesLP;
-               ppc_md.tce_free  = tce_free_pSeriesLP;
                powerpc_firmware_features &= ~FW_FEATURE_MULTITCE;
        }
        return 1;
index c8d24f9a69481d007fe4bcccb65bad5b2f032e5c..c22bb647cce678cdc9eac5121d1efddeaf00e37e 100644 (file)
@@ -18,6 +18,8 @@
 #include <asm/ppc-pci.h>
 #include <asm/machdep.h>
 
+#include "pseries.h"
+
 static int query_token, change_token;
 
 #define RTAS_QUERY_FN          0
@@ -505,6 +507,8 @@ static void rtas_msi_pci_irq_fixup(struct pci_dev *pdev)
 
 static int rtas_msi_init(void)
 {
+       struct pci_controller *phb;
+
        query_token  = rtas_token("ibm,query-interrupt-source-number");
        change_token = rtas_token("ibm,change-msi");
 
@@ -516,9 +520,15 @@ static int rtas_msi_init(void)
 
        pr_debug("rtas_msi: Registering RTAS MSI callbacks.\n");
 
-       WARN_ON(ppc_md.setup_msi_irqs);
-       ppc_md.setup_msi_irqs = rtas_setup_msi_irqs;
-       ppc_md.teardown_msi_irqs = rtas_teardown_msi_irqs;
+       WARN_ON(pseries_pci_controller_ops.setup_msi_irqs);
+       pseries_pci_controller_ops.setup_msi_irqs = rtas_setup_msi_irqs;
+       pseries_pci_controller_ops.teardown_msi_irqs = rtas_teardown_msi_irqs;
+
+       list_for_each_entry(phb, &hose_list, list_node) {
+               WARN_ON(phb->controller_ops.setup_msi_irqs);
+               phb->controller_ops.setup_msi_irqs = rtas_setup_msi_irqs;
+               phb->controller_ops.teardown_msi_irqs = rtas_teardown_msi_irqs;
+       }
 
        WARN_ON(ppc_md.pci_irq_fixup);
        ppc_md.pci_irq_fixup = rtas_msi_pci_irq_fixup;
index f7cb2a1b01fa053ddbd7df7aca8a423fc84af56f..5b492a6438ffa8723ca9e78feaa17d92bff57da2 100644 (file)
@@ -2,7 +2,7 @@ subdir-ccflags-$(CONFIG_PPC_WERROR) := -Werror
 
 ccflags-$(CONFIG_PPC64)                := $(NO_MINIMAL_TOC)
 
-mpic-msi-obj-$(CONFIG_PCI_MSI) += mpic_msi.o mpic_u3msi.o mpic_pasemi_msi.o
+mpic-msi-obj-$(CONFIG_PCI_MSI) += mpic_msi.o mpic_u3msi.o
 obj-$(CONFIG_MPIC)             += mpic.o $(mpic-msi-obj-y)
 obj-$(CONFIG_MPIC_TIMER)        += mpic_timer.o
 obj-$(CONFIG_FSL_MPIC_TIMER_WAKEUP)    += fsl_mpic_timer_wakeup.o
index d00a5663e312d33c6e0e01c6871ba9247a59cfc6..90bcdfeedf4829fd9b8658e340f1694f97d68102 100644 (file)
@@ -286,6 +286,12 @@ static int __init dart_init(struct device_node *dart_node)
        return 0;
 }
 
+static struct iommu_table_ops iommu_dart_ops = {
+       .set = dart_build,
+       .clear = dart_free,
+       .flush = dart_flush,
+};
+
 static void iommu_table_dart_setup(void)
 {
        iommu_table_dart.it_busno = 0;
@@ -298,6 +304,7 @@ static void iommu_table_dart_setup(void)
        iommu_table_dart.it_base = (unsigned long)dart_vbase;
        iommu_table_dart.it_index = 0;
        iommu_table_dart.it_blocksize = 1;
+       iommu_table_dart.it_ops = &iommu_dart_ops;
        iommu_init_table(&iommu_table_dart, -1);
 
        /* Reserve the last page of the DART to avoid possible prefetch
@@ -386,11 +393,6 @@ void __init iommu_init_early_dart(struct pci_controller_ops *controller_ops)
        if (dart_init(dn) != 0)
                goto bail;
 
-       /* Setup low level TCE operations for the core IOMMU code */
-       ppc_md.tce_build = dart_build;
-       ppc_md.tce_free  = dart_free;
-       ppc_md.tce_flush = dart_flush;
-
        /* Setup bypass if supported */
        if (dart_is_u4)
                ppc_md.dma_set_mask = dart_dma_set_mask;
index f086c6f22dc963dcc008033d01dd4f10247be7b1..5236e5427c38c2c922a2266a16ea4d172db3ef21 100644 (file)
@@ -405,6 +405,7 @@ static int fsl_of_msi_probe(struct platform_device *dev)
        const struct fsl_msi_feature *features;
        int len;
        u32 offset;
+       struct pci_controller *phb;
 
        match = of_match_device(fsl_of_msi_ids, &dev->dev);
        if (!match)
@@ -541,14 +542,20 @@ static int fsl_of_msi_probe(struct platform_device *dev)
 
        list_add_tail(&msi->list, &msi_head);
 
-       /* The multiple setting ppc_md.setup_msi_irqs will not harm things */
-       if (!ppc_md.setup_msi_irqs) {
-               ppc_md.setup_msi_irqs = fsl_setup_msi_irqs;
-               ppc_md.teardown_msi_irqs = fsl_teardown_msi_irqs;
-       } else if (ppc_md.setup_msi_irqs != fsl_setup_msi_irqs) {
-               dev_err(&dev->dev, "Different MSI driver already installed!\n");
-               err = -ENODEV;
-               goto error_out;
+       /*
+        * Apply the MSI ops to all the controllers.
+        * It doesn't hurt to reassign the same ops,
+        * but bail out if we find another MSI driver.
+        */
+       list_for_each_entry(phb, &hose_list, list_node) {
+               if (!phb->controller_ops.setup_msi_irqs) {
+                       phb->controller_ops.setup_msi_irqs = fsl_setup_msi_irqs;
+                       phb->controller_ops.teardown_msi_irqs = fsl_teardown_msi_irqs;
+               } else if (phb->controller_ops.setup_msi_irqs != fsl_setup_msi_irqs) {
+                       dev_err(&dev->dev, "Different MSI driver already installed!\n");
+                       err = -ENODEV;
+                       goto error_out;
+               }
        }
        return 0;
 error_out:
index 45598da0b3214221feb8b8d66200fe8146b3ef2f..31c33475c7b7042e2224a1094c9070c558096990 100644 (file)
@@ -204,7 +204,7 @@ static int i8259_host_xlate(struct irq_domain *h, struct device_node *ct,
        return 0;
 }
 
-static struct irq_domain_ops i8259_host_ops = {
+static const struct irq_domain_ops i8259_host_ops = {
        .match = i8259_host_match,
        .map = i8259_host_map,
        .xlate = i8259_host_xlate,
index b28733727ed3cc705b2fa2361aed4e38f1b61962..d78f1364b639d8ab0f208d37b0ef8662b3d7568e 100644 (file)
@@ -691,7 +691,7 @@ static int ipic_host_map(struct irq_domain *h, unsigned int virq,
        return 0;
 }
 
-static struct irq_domain_ops ipic_host_ops = {
+static const struct irq_domain_ops ipic_host_ops = {
        .match  = ipic_host_match,
        .map    = ipic_host_map,
        .xlate  = irq_domain_xlate_onetwocell,
index c4828c0be5bd861e734577dc982071a305e8fbea..d93a78be43469cfe5fe7f481bdf5ceb99114e11b 100644 (file)
@@ -120,7 +120,7 @@ static int mpc8xx_pic_host_xlate(struct irq_domain *h, struct device_node *ct,
 }
 
 
-static struct irq_domain_ops mpc8xx_pic_host_ops = {
+static const struct irq_domain_ops mpc8xx_pic_host_ops = {
        .map = mpc8xx_pic_host_map,
        .xlate = mpc8xx_pic_host_xlate,
 };
index b2b8447a227a34a73d7f3746a3d9e74a5e5d2616..c8e73332eaad5ab1d08ef3036be34b4b484d8ef6 100644 (file)
@@ -1195,7 +1195,7 @@ static void mpic_cascade(unsigned int irq, struct irq_desc *desc)
        chip->irq_eoi(&desc->irq_data);
 }
 
-static struct irq_domain_ops mpic_host_ops = {
+static const struct irq_domain_ops mpic_host_ops = {
        .match = mpic_host_match,
        .map = mpic_host_map,
        .xlate = mpic_host_xlate,
index 24bf07a63924e3c28745b6ab3eb264dbf93975bd..32971a41853ba4316338dff84993ebcce742250a 100644 (file)
@@ -15,7 +15,6 @@
 extern void mpic_msi_reserve_hwirq(struct mpic *mpic, irq_hw_number_t hwirq);
 extern int mpic_msi_init_allocator(struct mpic *mpic);
 extern int mpic_u3msi_init(struct mpic *mpic);
-extern int mpic_pasemi_msi_init(struct mpic *mpic);
 #else
 static inline void mpic_msi_reserve_hwirq(struct mpic *mpic,
                                          irq_hw_number_t hwirq)
@@ -27,11 +26,12 @@ static inline int mpic_u3msi_init(struct mpic *mpic)
 {
        return -1;
 }
+#endif
 
-static inline int mpic_pasemi_msi_init(struct mpic *mpic)
-{
-       return -1;
-}
+#if defined(CONFIG_PCI_MSI) && defined(CONFIG_PPC_PASEMI)
+int mpic_pasemi_msi_init(struct mpic *mpic);
+#else
+static inline int mpic_pasemi_msi_init(struct mpic *mpic) { return -1; }
 #endif
 
 extern int mpic_set_irq_type(struct irq_data *d, unsigned int flow_type);
index b2cef18093893c9323dbea08f9aaf1ed0dab328a..fc46ef3b816eb3ee6b0796775c4eb77b4e9bcbd5 100644 (file)
@@ -181,6 +181,7 @@ static int u3msi_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type)
 int mpic_u3msi_init(struct mpic *mpic)
 {
        int rc;
+       struct pci_controller *phb;
 
        rc = mpic_msi_init_allocator(mpic);
        if (rc) {
@@ -193,9 +194,11 @@ int mpic_u3msi_init(struct mpic *mpic)
        BUG_ON(msi_mpic);
        msi_mpic = mpic;
 
-       WARN_ON(ppc_md.setup_msi_irqs);
-       ppc_md.setup_msi_irqs = u3msi_setup_msi_irqs;
-       ppc_md.teardown_msi_irqs = u3msi_teardown_msi_irqs;
+       list_for_each_entry(phb, &hose_list, list_node) {
+               WARN_ON(phb->controller_ops.setup_msi_irqs);
+               phb->controller_ops.setup_msi_irqs = u3msi_setup_msi_irqs;
+               phb->controller_ops.teardown_msi_irqs = u3msi_teardown_msi_irqs;
+       }
 
        return 0;
 }
index 8848e99a83f21a27779c0ea56e94876bfdfda400..0f842dd16bcdd3c9463c7de27a31a40635ce7880 100644 (file)
@@ -223,7 +223,7 @@ static int mv64x60_host_map(struct irq_domain *h, unsigned int virq,
        return 0;
 }
 
-static struct irq_domain_ops mv64x60_host_ops = {
+static const struct irq_domain_ops mv64x60_host_ops = {
        .map   = mv64x60_host_map,
 };
 
index f366d2d4c0790653fd3a7eec3fcca86e864e2451..2bc33674ebfc11d2525700191552389fafc1a23d 100644 (file)
@@ -128,6 +128,7 @@ static int hsta_msi_probe(struct platform_device *pdev)
        struct device *dev = &pdev->dev;
        struct resource *mem;
        int irq, ret, irq_count;
+       struct pci_controller *phb;
 
        mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        if (IS_ERR(mem)) {
@@ -171,8 +172,10 @@ static int hsta_msi_probe(struct platform_device *pdev)
                }
        }
 
-       ppc_md.setup_msi_irqs = hsta_setup_msi_irqs;
-       ppc_md.teardown_msi_irqs = hsta_teardown_msi_irqs;
+       list_for_each_entry(phb, &hose_list, list_node) {
+               phb->controller_ops.setup_msi_irqs = hsta_setup_msi_irqs;
+               phb->controller_ops.teardown_msi_irqs = hsta_teardown_msi_irqs;
+       }
        return 0;
 
 out2:
index 6e2e6aa378bbe36f6d38a7ff6b3c8c6e21c4d063..6eb21f2ea5857295d33bc5f9e8da87b7f0d9071f 100644 (file)
@@ -218,6 +218,7 @@ static int ppc4xx_msi_probe(struct platform_device *dev)
        struct ppc4xx_msi *msi;
        struct resource res;
        int err = 0;
+       struct pci_controller *phb;
 
        dev_dbg(&dev->dev, "PCIE-MSI: Setting up MSI support...\n");
 
@@ -250,8 +251,10 @@ static int ppc4xx_msi_probe(struct platform_device *dev)
        }
        ppc4xx_msi = *msi;
 
-       ppc_md.setup_msi_irqs = ppc4xx_setup_msi_irqs;
-       ppc_md.teardown_msi_irqs = ppc4xx_teardown_msi_irqs;
+       list_for_each_entry(phb, &hose_list, list_node) {
+               phb->controller_ops.setup_msi_irqs = ppc4xx_setup_msi_irqs;
+               phb->controller_ops.teardown_msi_irqs = ppc4xx_teardown_msi_irqs;
+       }
        return err;
 
 error_out:
index 543765e1ef14e6f61c6b97b1cf0fdc9eabba99ec..6512cd8caa517d92496110e8119af46ae067d242 100644 (file)
@@ -271,7 +271,7 @@ static int qe_ic_host_map(struct irq_domain *h, unsigned int virq,
        return 0;
 }
 
-static struct irq_domain_ops qe_ic_host_ops = {
+static const struct irq_domain_ops qe_ic_host_ops = {
        .match = qe_ic_host_match,
        .map = qe_ic_host_map,
        .xlate = irq_domain_xlate_onetwocell,
index 188012c58f7f4e49806d1b3bc5888cf4839daaa1..57b54476e74721eabccf3f15f130c6e2f40c81eb 100644 (file)
@@ -397,7 +397,7 @@ static int pci_irq_host_map(struct irq_domain *h, unsigned int virq,
        return 0;
 }
 
-static struct irq_domain_ops pci_irq_domain_ops = {
+static const struct irq_domain_ops pci_irq_domain_ops = {
        .map = pci_irq_host_map,
        .xlate = pci_irq_host_xlate,
 };
index 7c37157d4c24c5bfe234fb1eeda24924e65a1e8d..d77345338671c1d63b5a98d3258885d9d3aa2b98 100644 (file)
@@ -189,7 +189,7 @@ static int uic_host_map(struct irq_domain *h, unsigned int virq,
        return 0;
 }
 
-static struct irq_domain_ops uic_host_ops = {
+static const struct irq_domain_ops uic_host_ops = {
        .map    = uic_host_map,
        .xlate  = irq_domain_xlate_twocell,
 };
@@ -198,7 +198,7 @@ void uic_irq_cascade(unsigned int virq, struct irq_desc *desc)
 {
        struct irq_chip *chip = irq_desc_get_chip(desc);
        struct irq_data *idata = irq_desc_get_irq_data(desc);
-       struct uic *uic = irq_get_handler_data(virq);
+       struct uic *uic = irq_desc_get_handler_data(desc);
        u32 msr;
        int src;
        int subvirq;
index 2fc4cf1b75575f9916e26fcf330cfce4959f39a5..eae32654bdf225c5a271b713bb70520395f43a56 100644 (file)
@@ -147,12 +147,16 @@ static void icp_native_cause_ipi(int cpu, unsigned long data)
 {
        kvmppc_set_host_ipi(cpu, 1);
 #ifdef CONFIG_PPC_DOORBELL
-       if (cpu_has_feature(CPU_FTR_DBELL) &&
-           (cpumask_test_cpu(cpu, cpu_sibling_mask(smp_processor_id()))))
-               doorbell_cause_ipi(cpu, data);
-       else
+       if (cpu_has_feature(CPU_FTR_DBELL)) {
+               if (cpumask_test_cpu(cpu, cpu_sibling_mask(get_cpu()))) {
+                       doorbell_cause_ipi(cpu, data);
+                       put_cpu();
+                       return;
+               }
+               put_cpu();
+       }
 #endif
-               icp_native_set_qirr(cpu, IPI_PRIORITY);
+       icp_native_set_qirr(cpu, IPI_PRIORITY);
 }
 
 /*
index 878a54036a25cbff2f49c6036b3651a8173e348b..08c248eb491bad53e82afe28a73b510d4aa87f86 100644 (file)
@@ -227,7 +227,7 @@ void xics_migrate_irqs_away(void)
 
                /* Locate interrupt server */
                server = -1;
-               ics = irq_get_chip_data(virq);
+               ics = irq_desc_get_chip_data(desc);
                if (ics)
                        server = ics->get_server(ics, irq);
                if (server < 0) {
@@ -360,7 +360,7 @@ static int xics_host_xlate(struct irq_domain *h, struct device_node *ct,
        return 0;
 }
 
-static struct irq_domain_ops xics_host_ops = {
+static const struct irq_domain_ops xics_host_ops = {
        .match = xics_host_match,
        .map = xics_host_map,
        .xlate = xics_host_xlate,
index 56f0524e47a6f84f723d26a3bf90a2c688b4d051..43b8b275bc5c50e778e79f9695f9ffb85a266328 100644 (file)
@@ -179,7 +179,7 @@ static int xilinx_intc_map(struct irq_domain *h, unsigned int virq,
        return 0;
 }
 
-static struct irq_domain_ops xilinx_intc_ops = {
+static const struct irq_domain_ops xilinx_intc_ops = {
        .map = xilinx_intc_map,
        .xlate = xilinx_intc_xlate,
 };
index f043c3c7e73ccdb5706ac68019921d77df8392f0..dd42a26d049d8ab89f660362144a127019b0ab23 100644 (file)
@@ -128,14 +128,14 @@ static struct hypfs_dbfs_file hypfs_sprp_file = {
 
 int hypfs_sprp_init(void)
 {
-       if (!sclp_has_sprp())
+       if (!sclp.has_sprp)
                return 0;
        return hypfs_dbfs_create_file(&hypfs_sprp_file);
 }
 
 void hypfs_sprp_exit(void)
 {
-       if (!sclp_has_sprp())
+       if (!sclp.has_sprp)
                return;
        hypfs_dbfs_remove_file(&hypfs_sprp_file);
 }
index 8d724718ec21c8d0331e1b8fbc14c96cb5892649..e6f8615a11eb5c72ed84576de97924f5481031ae 100644 (file)
@@ -36,7 +36,7 @@
 #define smp_mb__before_atomic()                smp_mb()
 #define smp_mb__after_atomic()         smp_mb()
 
-#define set_mb(var, value)             do { var = value; mb(); } while (0)
+#define smp_store_mb(var, value)               do { WRITE_ONCE(var, value); mb(); } while (0)
 
 #define smp_store_release(p, v)                                                \
 do {                                                                   \
index 4eadec466b8ca3ecc5ba29881856fef9f85d79f3..411464f4c97a57fd49d87debc7ef041767342699 100644 (file)
@@ -32,8 +32,6 @@
        __old;                                                          \
 })
 
-#define __HAVE_ARCH_CMPXCHG
-
 #define __cmpxchg_double_op(p1, p2, o1, o2, n1, n2, insn)              \
 ({                                                                     \
        register __typeof__(*(p1)) __old1 asm("2") = (o1);              \
index 30fd5c84680e84f60fe438f6e9c0bc5b45f58ba4..cb5fdf3a78fc8205929fa995676961afe3d90798 100644 (file)
@@ -29,6 +29,7 @@ void unxlate_dev_mem_ptr(phys_addr_t phys, void *addr);
 
 #define ioremap_nocache(addr, size)    ioremap(addr, size)
 #define ioremap_wc                     ioremap_nocache
+#define ioremap_wt                     ioremap_nocache
 
 static inline void __iomem *ioremap(unsigned long offset, unsigned long size)
 {
index d01fc588b5c378fddc46eba49e28b4de4be1f1a9..3024acbe1f9d63c935b8c74780227f07e429037c 100644 (file)
@@ -80,6 +80,7 @@ struct sca_block {
 #define CPUSTAT_MCDS       0x00000100
 #define CPUSTAT_SM         0x00000080
 #define CPUSTAT_IBS        0x00000040
+#define CPUSTAT_GED2       0x00000010
 #define CPUSTAT_G          0x00000008
 #define CPUSTAT_GED        0x00000004
 #define CPUSTAT_J          0x00000002
@@ -95,7 +96,8 @@ struct kvm_s390_sie_block {
 #define PROG_IN_SIE (1<<0)
        __u32   prog0c;                 /* 0x000c */
        __u8    reserved10[16];         /* 0x0010 */
-#define PROG_BLOCK_SIE 0x00000001
+#define PROG_BLOCK_SIE (1<<0)
+#define PROG_REQUEST   (1<<1)
        atomic_t prog20;                /* 0x0020 */
        __u8    reserved24[4];          /* 0x0024 */
        __u64   cputm;                  /* 0x0028 */
@@ -634,7 +636,7 @@ static inline void kvm_arch_vcpu_uninit(struct kvm_vcpu *vcpu) {}
 static inline void kvm_arch_sched_in(struct kvm_vcpu *vcpu, int cpu) {}
 static inline void kvm_arch_free_memslot(struct kvm *kvm,
                struct kvm_memory_slot *free, struct kvm_memory_slot *dont) {}
-static inline void kvm_arch_memslots_updated(struct kvm *kvm) {}
+static inline void kvm_arch_memslots_updated(struct kvm *kvm, struct kvm_memslots *slots) {}
 static inline void kvm_arch_flush_shadow_all(struct kvm *kvm) {}
 static inline void kvm_arch_flush_shadow_memslot(struct kvm *kvm,
                struct kvm_memory_slot *slot) {}
index ef24a212eeb727b8d8df3326115ef17dbeb0a272..0bb2da79adf351dd2935077cd2ca6fd82b7d4e8a 100644 (file)
@@ -1565,9 +1565,9 @@ static inline int has_transparent_hugepage(void)
 /*
  * 64 bit swap entry format:
  * A page-table entry has some bits we have to treat in a special way.
- * Bits 52 and bit 55 have to be zero, otherwise an specification
+ * Bits 52 and bit 55 have to be zero, otherwise a specification
  * exception will occur instead of a page translation exception. The
- * specifiation exception has the bad habit not to store necessary
+ * specification exception has the bad habit not to store necessary
  * information in the lowcore.
  * Bits 54 and 63 are used to indicate the page type.
  * A swap pte is indicated by bit pattern (pte & 0x201) == 0x200
index f1096bab5199aef439c33c75395f86e3f38a6e08..c891f41b27532f5fdfd646cffbd3f8f15171311a 100644 (file)
@@ -46,33 +46,39 @@ struct sclp_cpu_info {
        struct sclp_cpu_entry cpu[MAX_CPU_ADDRESS + 1];
 };
 
+struct sclp_info {
+       unsigned char has_linemode : 1;
+       unsigned char has_vt220 : 1;
+       unsigned char has_siif : 1;
+       unsigned char has_sigpif : 1;
+       unsigned char has_cpu_type : 1;
+       unsigned char has_sprp : 1;
+       unsigned int ibc;
+       unsigned int mtid;
+       unsigned int mtid_cp;
+       unsigned int mtid_prev;
+       unsigned long long rzm;
+       unsigned long long rnmax;
+       unsigned long long hamax;
+       unsigned int max_cpu;
+       unsigned long hsa_size;
+       unsigned long long facilities;
+};
+extern struct sclp_info sclp;
+
 int sclp_get_cpu_info(struct sclp_cpu_info *info);
 int sclp_cpu_configure(u8 cpu);
 int sclp_cpu_deconfigure(u8 cpu);
-unsigned long long sclp_get_rnmax(void);
-unsigned long long sclp_get_rzm(void);
-unsigned int sclp_get_max_cpu(void);
-unsigned int sclp_get_mtid(u8 cpu_type);
-unsigned int sclp_get_mtid_max(void);
-unsigned int sclp_get_mtid_prev(void);
 int sclp_sdias_blk_count(void);
 int sclp_sdias_copy(void *dest, int blk_num, int nr_blks);
 int sclp_chp_configure(struct chp_id chpid);
 int sclp_chp_deconfigure(struct chp_id chpid);
 int sclp_chp_read_info(struct sclp_chp_info *info);
 void sclp_get_ipl_info(struct sclp_ipl_info *info);
-bool __init sclp_has_linemode(void);
-bool __init sclp_has_vt220(void);
-bool sclp_has_sprp(void);
 int sclp_pci_configure(u32 fid);
 int sclp_pci_deconfigure(u32 fid);
 int memcpy_hsa(void *dest, unsigned long src, size_t count, int mode);
-unsigned long sclp_get_hsa_size(void);
 void sclp_early_detect(void);
-int sclp_has_siif(void);
-int sclp_has_sigpif(void);
-unsigned int sclp_get_ibc(void);
-
 long _sclp_print_early(const char *);
 
 #endif /* _ASM_S390_SCLP_H */
index 98eb2a5792234d9d12c303bdb1301f869f706b60..dcb6312a0b918089118c7e7835610addefbe2309 100644 (file)
@@ -10,6 +10,7 @@
 #define _ASM_S390_TIMEX_H
 
 #include <asm/lowcore.h>
+#include <linux/time64.h>
 
 /* The value of the TOD clock for 1.1.1970. */
 #define TOD_UNIX_EPOCH 0x7d91048bca000000ULL
@@ -108,10 +109,10 @@ int get_sync_clock(unsigned long long *clock);
 void init_cpu_timer(void);
 unsigned long long monotonic_clock(void);
 
-void tod_to_timeval(__u64, struct timespec *);
+void tod_to_timeval(__u64 todval, struct timespec64 *xt);
 
 static inline
-void stck_to_timespec(unsigned long long stck, struct timespec *ts)
+void stck_to_timespec64(unsigned long long stck, struct timespec64 *ts)
 {
        tod_to_timeval(stck - TOD_UNIX_EPOCH, ts);
 }
index 9f73c805902291c1619335cb38c4fe0f8492b29a..d9f0dcfcae5eaed275f30bb2fb91cf2ef7c1dd01 100644 (file)
@@ -122,7 +122,7 @@ static ssize_t copy_oldmem_page_zfcpdump(char *buf, size_t csize,
 {
        int rc;
 
-       if (src < sclp_get_hsa_size()) {
+       if (src < sclp.hsa_size) {
                rc = memcpy_hsa(buf, src, csize, userbuf);
        } else {
                if (userbuf)
@@ -215,7 +215,7 @@ static int remap_oldmem_pfn_range_zfcpdump(struct vm_area_struct *vma,
                                           unsigned long pfn,
                                           unsigned long size, pgprot_t prot)
 {
-       unsigned long hsa_end = sclp_get_hsa_size();
+       unsigned long hsa_end = sclp.hsa_size;
        unsigned long size_hsa;
 
        if (pfn < hsa_end >> PAGE_SHIFT) {
@@ -258,7 +258,7 @@ int copy_from_oldmem(void *dest, void *src, size_t count)
                                return rc;
                }
        } else {
-               unsigned long hsa_end = sclp_get_hsa_size();
+               unsigned long hsa_end = sclp.hsa_size;
                if ((unsigned long) src < hsa_end) {
                        copied = min(count, hsa_end - (unsigned long) src);
                        rc = memcpy_hsa(dest, (unsigned long) src, copied, 0);
@@ -609,7 +609,7 @@ int elfcorehdr_alloc(unsigned long long *addr, unsigned long long *size)
        if (elfcorehdr_addr != ELFCORE_ADDR_MAX)
                return 0;
        /* If we cannot get HSA size for zfcpdump return error */
-       if (ipl_info.type == IPL_TYPE_FCP_DUMP && !sclp_get_hsa_size())
+       if (ipl_info.type == IPL_TYPE_FCP_DUMP && !sclp.hsa_size)
                return -ENODEV;
 
        /* For kdump, exclude previous crashkernel memory */
index c1f21aca76e7fe0b1f7200f82c97646c51b08c00..6fca0e46464e01842f613584e4c82e5ca1fe4270 100644 (file)
@@ -1457,23 +1457,24 @@ int
 debug_dflt_header_fn(debug_info_t * id, struct debug_view *view,
                         int area, debug_entry_t * entry, char *out_buf)
 {
-       struct timespec time_spec;
+       struct timespec64 time_spec;
        char *except_str;
        unsigned long caller;
        int rc = 0;
        unsigned int level;
 
        level = entry->id.fields.level;
-       stck_to_timespec(entry->id.stck, &time_spec);
+       stck_to_timespec64(entry->id.stck, &time_spec);
 
        if (entry->id.fields.exception)
                except_str = "*";
        else
                except_str = "-";
        caller = ((unsigned long) entry->caller) & PSW_ADDR_INSN;
-       rc += sprintf(out_buf, "%02i %011lu:%06lu %1u %1s %02i %p  ",
-                     area, time_spec.tv_sec, time_spec.tv_nsec / 1000, level,
-                     except_str, entry->id.fields.cpuid, (void *) caller);
+       rc += sprintf(out_buf, "%02i %011lld:%06lu %1u %1s %02i %p  ",
+                     area, (long long)time_spec.tv_sec,
+                     time_spec.tv_nsec / 1000, level, except_str,
+                     entry->id.fields.cpuid, (void *)caller);
        return rc;
 }
 EXPORT_SYMBOL(debug_dflt_header_fn);
index 99b44acbfcc726b3035abc5046aaa4bb0f58fa1a..3238893c9d4ff66d492ce2a04421b85fa0cc0c34 100644 (file)
@@ -1005,7 +1005,7 @@ ENTRY(sie64a)
 .Lsie_gmap:
        lg      %r14,__SF_EMPTY(%r15)           # get control block pointer
        oi      __SIE_PROG0C+3(%r14),1          # we are going into SIE now
-       tm      __SIE_PROG20+3(%r14),1          # last exit...
+       tm      __SIE_PROG20+3(%r14),3          # last exit...
        jnz     .Lsie_done
        LPP     __SF_EMPTY(%r15)                # set guest id
        sie     0(%r14)
index 7262fe438c9903c58be86f31f0fa93b406c9ce6c..af4f41d52cde58b0d30f1857989477091a7429c7 100644 (file)
@@ -128,9 +128,9 @@ __setup("condev=", condev_setup);
 static void __init set_preferred_console(void)
 {
        if (MACHINE_IS_KVM) {
-               if (sclp_has_vt220())
+               if (sclp.has_vt220)
                        add_preferred_console("ttyS", 1, NULL);
-               else if (sclp_has_linemode())
+               else if (sclp.has_linemode)
                        add_preferred_console("ttyS", 0, NULL);
                else
                        add_preferred_console("hvc", 0, NULL);
@@ -510,8 +510,8 @@ static void reserve_memory_end(void)
 {
 #ifdef CONFIG_CRASH_DUMP
        if (ipl_info.type == IPL_TYPE_FCP_DUMP &&
-           !OLDMEM_BASE && sclp_get_hsa_size()) {
-               memory_end = sclp_get_hsa_size();
+           !OLDMEM_BASE && sclp.hsa_size) {
+               memory_end = sclp.hsa_size;
                memory_end &= PAGE_MASK;
                memory_end_set = 1;
        }
@@ -576,7 +576,7 @@ static void __init reserve_crashkernel(void)
                crash_base = low;
        } else {
                /* Find suitable area in free memory */
-               low = max_t(unsigned long, crash_size, sclp_get_hsa_size());
+               low = max_t(unsigned long, crash_size, sclp.hsa_size);
                high = crash_base ? crash_base + crash_size : ULONG_MAX;
 
                if (crash_base && crash_base < low) {
@@ -640,19 +640,24 @@ static void __init check_initrd(void)
 }
 
 /*
- * Reserve all kernel text
+ * Reserve memory used for lowcore/command line/kernel image.
  */
 static void __init reserve_kernel(void)
 {
-       unsigned long start_pfn;
-       start_pfn = PFN_UP(__pa(&_end));
+       unsigned long start_pfn = PFN_UP(__pa(&_end));
 
+#ifdef CONFIG_DMA_API_DEBUG
        /*
-        * Reserve memory used for lowcore/command line/kernel image.
+        * DMA_API_DEBUG code stumbles over addresses from the
+        * range [_ehead, _stext]. Mark the memory as reserved
+        * so it is not used for CONFIG_DMA_API_DEBUG=y.
         */
+       memblock_reserve(0, PFN_PHYS(start_pfn));
+#else
        memblock_reserve(0, (unsigned long)_ehead);
        memblock_reserve((unsigned long)_stext, PFN_PHYS(start_pfn)
                         - (unsigned long)_stext);
+#endif
 }
 
 static void __init reserve_elfcorehdr(void)
index efd2c1968000afadc669d1f23e740317a5b809be..0d9d59d4710e9878d99edd3d59bdfbbd67b93022 100644 (file)
@@ -601,7 +601,7 @@ static void __init smp_store_cpu_states(struct sclp_cpu_info *info)
                /* No previous system present, normal boot. */
                return;
        /* Set multi-threading state to the previous system. */
-       pcpu_set_smt(sclp_get_mtid_prev());
+       pcpu_set_smt(sclp.mtid_prev);
        /* Collect CPU states. */
        cpu = 0;
        for (i = 0; i < info->configured; i++) {
@@ -740,7 +740,7 @@ static void __init smp_detect_cpus(void)
 #endif
 
        /* Set multi-threading state for the current system */
-       mtid = sclp_get_mtid(boot_cpu_type);
+       mtid = boot_cpu_type ? sclp.mtid : sclp.mtid_cp;
        mtid = (mtid < smp_max_threads) ? mtid : smp_max_threads - 1;
        pcpu_set_smt(mtid);
 
@@ -880,12 +880,13 @@ void __noreturn cpu_die(void)
 
 void __init smp_fill_possible_mask(void)
 {
-       unsigned int possible, sclp, cpu;
+       unsigned int possible, sclp_max, cpu;
 
-       sclp = min(smp_max_threads, sclp_get_mtid_max() + 1);
-       sclp = sclp_get_max_cpu()*sclp ?: nr_cpu_ids;
+       sclp_max = max(sclp.mtid, sclp.mtid_cp) + 1;
+       sclp_max = min(smp_max_threads, sclp_max);
+       sclp_max = sclp.max_cpu * sclp_max ?: nr_cpu_ids;
        possible = setup_possible_cpus ?: nr_cpu_ids;
-       possible = min(possible, sclp);
+       possible = min(possible, sclp_max);
        for (cpu = 0; cpu < possible && cpu < nr_cpu_ids; cpu++)
                set_cpu_possible(cpu, true);
 }
index d3236c9e226b87b98323add039a9901df23bcaeb..39e2f41b6cf0cf93c80744ca46687800e2b188c7 100644 (file)
@@ -9,10 +9,10 @@
 #include <linux/pfn.h>
 #include <linux/suspend.h>
 #include <linux/mm.h>
+#include <linux/pci.h>
 #include <asm/ctl_reg.h>
 #include <asm/ipl.h>
 #include <asm/cio.h>
-#include <asm/pci.h>
 #include <asm/sections.h>
 #include "entry.h"
 
index 170ddd2018b31667df8619b471df42b7fb562705..9e733d965e08886611ef40535f09505c5b7b9878 100644 (file)
@@ -76,7 +76,7 @@ unsigned long long monotonic_clock(void)
 }
 EXPORT_SYMBOL(monotonic_clock);
 
-void tod_to_timeval(__u64 todval, struct timespec *xt)
+void tod_to_timeval(__u64 todval, struct timespec64 *xt)
 {
        unsigned long long sec;
 
@@ -181,12 +181,12 @@ static void timing_alert_interrupt(struct ext_code ext_code,
 static void etr_reset(void);
 static void stp_reset(void);
 
-void read_persistent_clock(struct timespec *ts)
+void read_persistent_clock64(struct timespec64 *ts)
 {
        tod_to_timeval(get_tod_clock() - TOD_UNIX_EPOCH, ts);
 }
 
-void read_boot_clock(struct timespec *ts)
+void read_boot_clock64(struct timespec64 *ts)
 {
        tod_to_timeval(sched_clock_base_cc - TOD_UNIX_EPOCH, ts);
 }
index 9e3779e3e496314a4e3f15823c152867c3a3cadd..7365e8a4603216828813d80f197cfb59c0865149 100644 (file)
@@ -241,21 +241,6 @@ static int handle_prog(struct kvm_vcpu *vcpu)
        return kvm_s390_inject_prog_irq(vcpu, &pgm_info);
 }
 
-static int handle_instruction_and_prog(struct kvm_vcpu *vcpu)
-{
-       int rc, rc2;
-
-       vcpu->stat.exit_instr_and_program++;
-       rc = handle_instruction(vcpu);
-       rc2 = handle_prog(vcpu);
-
-       if (rc == -EOPNOTSUPP)
-               vcpu->arch.sie_block->icptcode = 0x04;
-       if (rc)
-               return rc;
-       return rc2;
-}
-
 /**
  * handle_external_interrupt - used for external interruption interceptions
  *
@@ -355,7 +340,6 @@ static const intercept_handler_t intercept_funcs[] = {
        [0x00 >> 2] = handle_noop,
        [0x04 >> 2] = handle_instruction,
        [0x08 >> 2] = handle_prog,
-       [0x0C >> 2] = handle_instruction_and_prog,
        [0x10 >> 2] = handle_noop,
        [0x14 >> 2] = handle_external_interrupt,
        [0x18 >> 2] = handle_noop,
index 9de47265ef73da07ffd7ef37337bf2e44e59bd46..c98d89708e99fb19ee2217c826f75f2bd0e3a41a 100644 (file)
@@ -134,6 +134,8 @@ static unsigned long deliverable_irqs(struct kvm_vcpu *vcpu)
 
        active_mask = pending_local_irqs(vcpu);
        active_mask |= pending_floating_irqs(vcpu);
+       if (!active_mask)
+               return 0;
 
        if (psw_extint_disabled(vcpu))
                active_mask &= ~IRQ_PEND_EXT_MASK;
@@ -799,7 +801,7 @@ int kvm_s390_ext_call_pending(struct kvm_vcpu *vcpu)
        struct kvm_s390_local_interrupt *li = &vcpu->arch.local_int;
        uint8_t sigp_ctrl = vcpu->kvm->arch.sca->cpu[vcpu->vcpu_id].sigp_ctrl;
 
-       if (!sclp_has_sigpif())
+       if (!sclp.has_sigpif)
                return test_bit(IRQ_PEND_EXT_EXTERNAL, &li->pending_irqs);
 
        return (sigp_ctrl & SIGP_CTRL_C) &&
@@ -941,12 +943,9 @@ int __must_check kvm_s390_deliver_pending_interrupts(struct kvm_vcpu *vcpu)
        if (cpu_timer_irq_pending(vcpu))
                set_bit(IRQ_PEND_EXT_CPU_TIMER, &li->pending_irqs);
 
-       do {
-               irqs = deliverable_irqs(vcpu);
+       while ((irqs = deliverable_irqs(vcpu)) && !rc) {
                /* bits are in the order of interrupt priority */
                irq_type = find_first_bit(&irqs, IRQ_PEND_COUNT);
-               if (irq_type == IRQ_PEND_COUNT)
-                       break;
                if (is_ioirq(irq_type)) {
                        rc = __deliver_io(vcpu, irq_type);
                } else {
@@ -958,9 +957,7 @@ int __must_check kvm_s390_deliver_pending_interrupts(struct kvm_vcpu *vcpu)
                        }
                        rc = func(vcpu);
                }
-               if (rc)
-                       break;
-       } while (!rc);
+       }
 
        set_intercept_indicators(vcpu);
 
@@ -1058,10 +1055,10 @@ static int __inject_extcall(struct kvm_vcpu *vcpu, struct kvm_s390_irq *irq)
            kvm_get_vcpu(vcpu->kvm, src_id) == NULL)
                return -EINVAL;
 
-       if (sclp_has_sigpif())
+       if (sclp.has_sigpif)
                return __inject_extcall_sigpif(vcpu, src_id);
 
-       if (!test_and_set_bit(IRQ_PEND_EXT_EXTERNAL, &li->pending_irqs))
+       if (test_and_set_bit(IRQ_PEND_EXT_EXTERNAL, &li->pending_irqs))
                return -EBUSY;
        *extcall = irq->u.extcall;
        atomic_set_mask(CPUSTAT_EXT_INT, li->cpuflags);
@@ -1340,12 +1337,54 @@ static int __inject_io(struct kvm *kvm, struct kvm_s390_interrupt_info *inti)
        return 0;
 }
 
-static int __inject_vm(struct kvm *kvm, struct kvm_s390_interrupt_info *inti)
+/*
+ * Find a destination VCPU for a floating irq and kick it.
+ */
+static void __floating_irq_kick(struct kvm *kvm, u64 type)
 {
+       struct kvm_s390_float_interrupt *fi = &kvm->arch.float_int;
        struct kvm_s390_local_interrupt *li;
+       struct kvm_vcpu *dst_vcpu;
+       int sigcpu, online_vcpus, nr_tries = 0;
+
+       online_vcpus = atomic_read(&kvm->online_vcpus);
+       if (!online_vcpus)
+               return;
+
+       /* find idle VCPUs first, then round robin */
+       sigcpu = find_first_bit(fi->idle_mask, online_vcpus);
+       if (sigcpu == online_vcpus) {
+               do {
+                       sigcpu = fi->next_rr_cpu;
+                       fi->next_rr_cpu = (fi->next_rr_cpu + 1) % online_vcpus;
+                       /* avoid endless loops if all vcpus are stopped */
+                       if (nr_tries++ >= online_vcpus)
+                               return;
+               } while (is_vcpu_stopped(kvm_get_vcpu(kvm, sigcpu)));
+       }
+       dst_vcpu = kvm_get_vcpu(kvm, sigcpu);
+
+       /* make the VCPU drop out of the SIE, or wake it up if sleeping */
+       li = &dst_vcpu->arch.local_int;
+       spin_lock(&li->lock);
+       switch (type) {
+       case KVM_S390_MCHK:
+               atomic_set_mask(CPUSTAT_STOP_INT, li->cpuflags);
+               break;
+       case KVM_S390_INT_IO_MIN...KVM_S390_INT_IO_MAX:
+               atomic_set_mask(CPUSTAT_IO_INT, li->cpuflags);
+               break;
+       default:
+               atomic_set_mask(CPUSTAT_EXT_INT, li->cpuflags);
+               break;
+       }
+       spin_unlock(&li->lock);
+       kvm_s390_vcpu_wakeup(dst_vcpu);
+}
+
+static int __inject_vm(struct kvm *kvm, struct kvm_s390_interrupt_info *inti)
+{
        struct kvm_s390_float_interrupt *fi;
-       struct kvm_vcpu *dst_vcpu = NULL;
-       int sigcpu;
        u64 type = READ_ONCE(inti->type);
        int rc;
 
@@ -1373,32 +1412,8 @@ static int __inject_vm(struct kvm *kvm, struct kvm_s390_interrupt_info *inti)
        if (rc)
                return rc;
 
-       sigcpu = find_first_bit(fi->idle_mask, KVM_MAX_VCPUS);
-       if (sigcpu == KVM_MAX_VCPUS) {
-               do {
-                       sigcpu = fi->next_rr_cpu++;
-                       if (sigcpu == KVM_MAX_VCPUS)
-                               sigcpu = fi->next_rr_cpu = 0;
-               } while (kvm_get_vcpu(kvm, sigcpu) == NULL);
-       }
-       dst_vcpu = kvm_get_vcpu(kvm, sigcpu);
-       li = &dst_vcpu->arch.local_int;
-       spin_lock(&li->lock);
-       switch (type) {
-       case KVM_S390_MCHK:
-               atomic_set_mask(CPUSTAT_STOP_INT, li->cpuflags);
-               break;
-       case KVM_S390_INT_IO_MIN...KVM_S390_INT_IO_MAX:
-               atomic_set_mask(CPUSTAT_IO_INT, li->cpuflags);
-               break;
-       default:
-               atomic_set_mask(CPUSTAT_EXT_INT, li->cpuflags);
-               break;
-       }
-       spin_unlock(&li->lock);
-       kvm_s390_vcpu_wakeup(kvm_get_vcpu(kvm, sigcpu));
+       __floating_irq_kick(kvm, type);
        return 0;
-
 }
 
 int kvm_s390_inject_vm(struct kvm *kvm,
@@ -1606,6 +1621,9 @@ void kvm_s390_clear_float_irqs(struct kvm *kvm)
        int i;
 
        spin_lock(&fi->lock);
+       fi->pending_irqs = 0;
+       memset(&fi->srv_signal, 0, sizeof(fi->srv_signal));
+       memset(&fi->mchk, 0, sizeof(fi->mchk));
        for (i = 0; i < FIRQ_LIST_COUNT; i++)
                clear_irq_list(&fi->lists[i]);
        for (i = 0; i < FIRQ_MAX_COUNT; i++)
index 8cd8e7b288c5db84bf358785bb9aee4510c69050..2078f92d15ac90adcfec617b46df750e073b76d0 100644 (file)
 #include "kvm-s390.h"
 #include "gaccess.h"
 
+#define KMSG_COMPONENT "kvm-s390"
+#undef pr_fmt
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
 #define CREATE_TRACE_POINTS
 #include "trace.h"
 #include "trace-s390.h"
@@ -110,7 +114,7 @@ struct kvm_stats_debugfs_item debugfs_entries[] = {
 /* upper facilities limit for kvm */
 unsigned long kvm_s390_fac_list_mask[] = {
        0xffe6fffbfcfdfc40UL,
-       0x005c800000000000UL,
+       0x005e800000000000UL,
 };
 
 unsigned long kvm_s390_fac_list_mask_size(void)
@@ -236,6 +240,7 @@ int kvm_vm_ioctl_get_dirty_log(struct kvm *kvm,
 {
        int r;
        unsigned long n;
+       struct kvm_memslots *slots;
        struct kvm_memory_slot *memslot;
        int is_dirty = 0;
 
@@ -245,7 +250,8 @@ int kvm_vm_ioctl_get_dirty_log(struct kvm *kvm,
        if (log->slot >= KVM_USER_MEM_SLOTS)
                goto out;
 
-       memslot = id_to_memslot(kvm->memslots, log->slot);
+       slots = kvm_memslots(kvm);
+       memslot = id_to_memslot(slots, log->slot);
        r = -ENOENT;
        if (!memslot->dirty_bitmap)
                goto out;
@@ -454,10 +460,10 @@ static int kvm_s390_set_tod_low(struct kvm *kvm, struct kvm_device_attr *attr)
 
        mutex_lock(&kvm->lock);
        kvm->arch.epoch = gtod - host_tod;
-       kvm_for_each_vcpu(vcpu_idx, cur_vcpu, kvm) {
+       kvm_s390_vcpu_block_all(kvm);
+       kvm_for_each_vcpu(vcpu_idx, cur_vcpu, kvm)
                cur_vcpu->arch.sie_block->epoch = kvm->arch.epoch;
-               exit_sie(cur_vcpu);
-       }
+       kvm_s390_vcpu_unblock_all(kvm);
        mutex_unlock(&kvm->lock);
        return 0;
 }
@@ -604,7 +610,7 @@ static int kvm_s390_get_machine(struct kvm *kvm, struct kvm_device_attr *attr)
                goto out;
        }
        get_cpu_id((struct cpuid *) &mach->cpuid);
-       mach->ibc = sclp_get_ibc();
+       mach->ibc = sclp.ibc;
        memcpy(&mach->fac_mask, kvm->arch.model.fac->mask,
               S390_ARCH_FAC_LIST_SIZE_BYTE);
        memcpy((unsigned long *)&mach->fac_list, S390_lowcore.stfle_fac_list,
@@ -1068,7 +1074,7 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
               S390_ARCH_FAC_LIST_SIZE_BYTE);
 
        kvm_s390_get_cpu_id(&kvm->arch.model.cpu_id);
-       kvm->arch.model.ibc = sclp_get_ibc() & 0x0fff;
+       kvm->arch.model.ibc = sclp.ibc & 0x0fff;
 
        if (kvm_s390_crypto_init(kvm) < 0)
                goto out_err;
@@ -1311,8 +1317,13 @@ int kvm_arch_vcpu_setup(struct kvm_vcpu *vcpu)
 
        atomic_set(&vcpu->arch.sie_block->cpuflags, CPUSTAT_ZARCH |
                                                    CPUSTAT_SM |
-                                                   CPUSTAT_STOPPED |
-                                                   CPUSTAT_GED);
+                                                   CPUSTAT_STOPPED);
+
+       if (test_kvm_facility(vcpu->kvm, 78))
+               atomic_set_mask(CPUSTAT_GED2, &vcpu->arch.sie_block->cpuflags);
+       else if (test_kvm_facility(vcpu->kvm, 8))
+               atomic_set_mask(CPUSTAT_GED, &vcpu->arch.sie_block->cpuflags);
+
        kvm_s390_vcpu_setup_model(vcpu);
 
        vcpu->arch.sie_block->ecb   = 6;
@@ -1321,9 +1332,9 @@ int kvm_arch_vcpu_setup(struct kvm_vcpu *vcpu)
 
        vcpu->arch.sie_block->ecb2  = 8;
        vcpu->arch.sie_block->eca   = 0xC1002000U;
-       if (sclp_has_siif())
+       if (sclp.has_siif)
                vcpu->arch.sie_block->eca |= 1;
-       if (sclp_has_sigpif())
+       if (sclp.has_sigpif)
                vcpu->arch.sie_block->eca |= 0x10000000U;
        if (test_kvm_facility(vcpu->kvm, 129)) {
                vcpu->arch.sie_block->eca |= 0x00020000;
@@ -1409,16 +1420,28 @@ int kvm_arch_vcpu_runnable(struct kvm_vcpu *vcpu)
        return kvm_s390_vcpu_has_irq(vcpu, 0);
 }
 
-void s390_vcpu_block(struct kvm_vcpu *vcpu)
+void kvm_s390_vcpu_block(struct kvm_vcpu *vcpu)
 {
        atomic_set_mask(PROG_BLOCK_SIE, &vcpu->arch.sie_block->prog20);
+       exit_sie(vcpu);
 }
 
-void s390_vcpu_unblock(struct kvm_vcpu *vcpu)
+void kvm_s390_vcpu_unblock(struct kvm_vcpu *vcpu)
 {
        atomic_clear_mask(PROG_BLOCK_SIE, &vcpu->arch.sie_block->prog20);
 }
 
+static void kvm_s390_vcpu_request(struct kvm_vcpu *vcpu)
+{
+       atomic_set_mask(PROG_REQUEST, &vcpu->arch.sie_block->prog20);
+       exit_sie(vcpu);
+}
+
+static void kvm_s390_vcpu_request_handled(struct kvm_vcpu *vcpu)
+{
+       atomic_clear_mask(PROG_REQUEST, &vcpu->arch.sie_block->prog20);
+}
+
 /*
  * Kick a guest cpu out of SIE and wait until SIE is not running.
  * If the CPU is not running (e.g. waiting as idle) the function will
@@ -1430,11 +1453,11 @@ void exit_sie(struct kvm_vcpu *vcpu)
                cpu_relax();
 }
 
-/* Kick a guest cpu out of SIE and prevent SIE-reentry */
-void exit_sie_sync(struct kvm_vcpu *vcpu)
+/* Kick a guest cpu out of SIE to process a request synchronously */
+void kvm_s390_sync_request(int req, struct kvm_vcpu *vcpu)
 {
-       s390_vcpu_block(vcpu);
-       exit_sie(vcpu);
+       kvm_make_request(req, vcpu);
+       kvm_s390_vcpu_request(vcpu);
 }
 
 static void kvm_gmap_notifier(struct gmap *gmap, unsigned long address)
@@ -1447,8 +1470,7 @@ static void kvm_gmap_notifier(struct gmap *gmap, unsigned long address)
                /* match against both prefix pages */
                if (kvm_s390_get_prefix(vcpu) == (address & ~0x1000UL)) {
                        VCPU_EVENT(vcpu, 2, "gmap notifier for %lx", address);
-                       kvm_make_request(KVM_REQ_MMU_RELOAD, vcpu);
-                       exit_sie_sync(vcpu);
+                       kvm_s390_sync_request(KVM_REQ_MMU_RELOAD, vcpu);
                }
        }
 }
@@ -1720,8 +1742,10 @@ static bool ibs_enabled(struct kvm_vcpu *vcpu)
 
 static int kvm_s390_handle_requests(struct kvm_vcpu *vcpu)
 {
+       if (!vcpu->requests)
+               return 0;
 retry:
-       s390_vcpu_unblock(vcpu);
+       kvm_s390_vcpu_request_handled(vcpu);
        /*
         * We use MMU_RELOAD just to re-arm the ipte notifier for the
         * guest prefix page. gmap_ipte_notify will wait on the ptl lock.
@@ -1993,12 +2017,14 @@ static int __vcpu_run(struct kvm_vcpu *vcpu)
                 * As PF_VCPU will be used in fault handler, between
                 * guest_enter and guest_exit should be no uaccess.
                 */
-               preempt_disable();
-               kvm_guest_enter();
-               preempt_enable();
+               local_irq_disable();
+               __kvm_guest_enter();
+               local_irq_enable();
                exit_reason = sie64a(vcpu->arch.sie_block,
                                     vcpu->run->s.regs.gprs);
-               kvm_guest_exit();
+               local_irq_disable();
+               __kvm_guest_exit();
+               local_irq_enable();
                vcpu->srcu_idx = srcu_read_lock(&vcpu->kvm->srcu);
 
                rc = vcpu_post_run(vcpu, exit_reason);
@@ -2068,7 +2094,7 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
        if (!kvm_s390_user_cpu_state_ctrl(vcpu->kvm)) {
                kvm_s390_vcpu_start(vcpu);
        } else if (is_vcpu_stopped(vcpu)) {
-               pr_err_ratelimited("kvm-s390: can't run stopped vcpu %d\n",
+               pr_err_ratelimited("can't run stopped vcpu %d\n",
                                   vcpu->vcpu_id);
                return -EINVAL;
        }
@@ -2206,8 +2232,7 @@ int kvm_s390_vcpu_store_adtl_status(struct kvm_vcpu *vcpu, unsigned long addr)
 static void __disable_ibs_on_vcpu(struct kvm_vcpu *vcpu)
 {
        kvm_check_request(KVM_REQ_ENABLE_IBS, vcpu);
-       kvm_make_request(KVM_REQ_DISABLE_IBS, vcpu);
-       exit_sie_sync(vcpu);
+       kvm_s390_sync_request(KVM_REQ_DISABLE_IBS, vcpu);
 }
 
 static void __disable_ibs_on_all_vcpus(struct kvm *kvm)
@@ -2223,8 +2248,7 @@ static void __disable_ibs_on_all_vcpus(struct kvm *kvm)
 static void __enable_ibs_on_vcpu(struct kvm_vcpu *vcpu)
 {
        kvm_check_request(KVM_REQ_DISABLE_IBS, vcpu);
-       kvm_make_request(KVM_REQ_ENABLE_IBS, vcpu);
-       exit_sie_sync(vcpu);
+       kvm_s390_sync_request(KVM_REQ_ENABLE_IBS, vcpu);
 }
 
 void kvm_s390_vcpu_start(struct kvm_vcpu *vcpu)
@@ -2563,7 +2587,7 @@ int kvm_arch_create_memslot(struct kvm *kvm, struct kvm_memory_slot *slot,
 /* Section: memory related */
 int kvm_arch_prepare_memory_region(struct kvm *kvm,
                                   struct kvm_memory_slot *memslot,
-                                  struct kvm_userspace_memory_region *mem,
+                                  const struct kvm_userspace_memory_region *mem,
                                   enum kvm_mr_change change)
 {
        /* A few sanity checks. We can have memory slots which have to be
@@ -2581,8 +2605,9 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm,
 }
 
 void kvm_arch_commit_memory_region(struct kvm *kvm,
-                               struct kvm_userspace_memory_region *mem,
+                               const struct kvm_userspace_memory_region *mem,
                                const struct kvm_memory_slot *old,
+                               const struct kvm_memory_slot *new,
                                enum kvm_mr_change change)
 {
        int rc;
@@ -2601,7 +2626,7 @@ void kvm_arch_commit_memory_region(struct kvm *kvm,
        rc = gmap_map_segment(kvm->arch.gmap, mem->userspace_addr,
                mem->guest_phys_addr, mem->memory_size);
        if (rc)
-               printk(KERN_WARNING "kvm-s390: failed to commit memory region\n");
+               pr_warn("failed to commit memory region\n");
        return;
 }
 
index ca108b90ae5613a15e0d82a68ef0288a93d62792..c5704786e4731d8099f04df7aa9dd7afd75e886c 100644 (file)
@@ -211,10 +211,10 @@ int kvm_s390_vcpu_store_status(struct kvm_vcpu *vcpu, unsigned long addr);
 int kvm_s390_vcpu_store_adtl_status(struct kvm_vcpu *vcpu, unsigned long addr);
 void kvm_s390_vcpu_start(struct kvm_vcpu *vcpu);
 void kvm_s390_vcpu_stop(struct kvm_vcpu *vcpu);
-void s390_vcpu_block(struct kvm_vcpu *vcpu);
-void s390_vcpu_unblock(struct kvm_vcpu *vcpu);
+void kvm_s390_vcpu_block(struct kvm_vcpu *vcpu);
+void kvm_s390_vcpu_unblock(struct kvm_vcpu *vcpu);
 void exit_sie(struct kvm_vcpu *vcpu);
-void exit_sie_sync(struct kvm_vcpu *vcpu);
+void kvm_s390_sync_request(int req, struct kvm_vcpu *vcpu);
 int kvm_s390_vcpu_setup_cmma(struct kvm_vcpu *vcpu);
 void kvm_s390_vcpu_unsetup_cmma(struct kvm_vcpu *vcpu);
 /* is cmma enabled */
@@ -228,6 +228,25 @@ int kvm_s390_handle_diag(struct kvm_vcpu *vcpu);
 int kvm_s390_inject_prog_irq(struct kvm_vcpu *vcpu,
                             struct kvm_s390_pgm_info *pgm_info);
 
+static inline void kvm_s390_vcpu_block_all(struct kvm *kvm)
+{
+       int i;
+       struct kvm_vcpu *vcpu;
+
+       WARN_ON(!mutex_is_locked(&kvm->lock));
+       kvm_for_each_vcpu(i, vcpu, kvm)
+               kvm_s390_vcpu_block(vcpu);
+}
+
+static inline void kvm_s390_vcpu_unblock_all(struct kvm *kvm)
+{
+       int i;
+       struct kvm_vcpu *vcpu;
+
+       kvm_for_each_vcpu(i, vcpu, kvm)
+               kvm_s390_vcpu_unblock(vcpu);
+}
+
 /**
  * kvm_s390_inject_prog_cond - conditionally inject a program check
  * @vcpu: virtual cpu
index d22d8ee1ff9d9c6404d653f5c4f8a04b8ddc70da..ad42422457713721427a5f0e20f9887b8ed5e86e 100644 (file)
@@ -698,10 +698,14 @@ static int handle_pfmf(struct kvm_vcpu *vcpu)
        case 0x00001000:
                end = (start + (1UL << 20)) & ~((1UL << 20) - 1);
                break;
-       /* We dont support EDAT2
        case 0x00002000:
+               /* only support 2G frame size if EDAT2 is available and we are
+                  not in 24-bit addressing mode */
+               if (!test_kvm_facility(vcpu->kvm, 78) ||
+                   psw_bits(vcpu->arch.sie_block->gpsw).eaba == PSW_AMODE_24BIT)
+                       return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
                end = (start + (1UL << 31)) & ~((1UL << 31) - 1);
-               break;*/
+               break;
        default:
                return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
        }
index 80875c43a4a4b16c11e8449d5cbf1f4db1b66d75..76e873748b56e91579ad06fd2fece4e2c346a51f 100644 (file)
@@ -213,7 +213,7 @@ unsigned long memory_block_size_bytes(void)
         * Make sure the memory block size is always greater
         * or equal than the memory increment size.
         */
-       return max_t(unsigned long, MIN_MEMORY_BLOCK_SIZE, sclp_get_rzm());
+       return max_t(unsigned long, MIN_MEMORY_BLOCK_SIZE, sclp.rzm);
 }
 
 #ifdef CONFIG_MEMORY_HOTREMOVE
index 0f36043958053f72f190ed338b7592c91f9cf58e..e00f0d5d296d0ce3dd042afcd1cd6d00b14b1f9f 100644 (file)
@@ -31,8 +31,8 @@ void __init detect_memory_memblock(void)
        unsigned long addr, size;
        int type;
 
-       rzm = sclp_get_rzm();
-       rnmax = sclp_get_rnmax();
+       rzm = sclp.rzm;
+       rnmax = sclp.rnmax;
        memsize = rzm * rnmax;
        if (!rzm)
                rzm = 1ULL << 17;
index ba8593a515baaa274d968aa64e6f54125238c032..de156ba3bd71c0d4db274a619c7c9fd6038c119c 100644 (file)
@@ -48,7 +48,9 @@ extern u8 sk_load_word[], sk_load_half[], sk_load_byte[];
  * We get 160 bytes stack space from calling function, but only use
  * 11 * 8 byte (old backchain + r15 - r6) for storing registers.
  */
-#define STK_OFF (MAX_BPF_STACK + 8 + 4 + 4 + (160 - 11 * 8))
+#define STK_SPACE      (MAX_BPF_STACK + 8 + 4 + 4 + 160)
+#define STK_160_UNUSED (160 - 11 * 8)
+#define STK_OFF                (STK_SPACE - STK_160_UNUSED)
 #define STK_OFF_TMP    160     /* Offset of tmp buffer on stack */
 #define STK_OFF_HLEN   168     /* Offset of SKB header length on stack */
 
index 20c146d1251ae2cd6c07279bf371adae6b2e3a1e..55423d8be580113d045d30edbf86d26fb74340ff 100644 (file)
@@ -384,13 +384,16 @@ static void bpf_jit_prologue(struct bpf_jit *jit)
        }
        /* Setup stack and backchain */
        if (jit->seen & SEEN_STACK) {
-               /* lgr %bfp,%r15 (BPF frame pointer) */
-               EMIT4(0xb9040000, BPF_REG_FP, REG_15);
+               if (jit->seen & SEEN_FUNC)
+                       /* lgr %w1,%r15 (backchain) */
+                       EMIT4(0xb9040000, REG_W1, REG_15);
+               /* la %bfp,STK_160_UNUSED(%r15) (BPF frame pointer) */
+               EMIT4_DISP(0x41000000, BPF_REG_FP, REG_15, STK_160_UNUSED);
                /* aghi %r15,-STK_OFF */
                EMIT4_IMM(0xa70b0000, REG_15, -STK_OFF);
                if (jit->seen & SEEN_FUNC)
-                       /* stg %bfp,152(%r15) (backchain) */
-                       EMIT6_DISP_LH(0xe3000000, 0x0024, BPF_REG_FP, REG_0,
+                       /* stg %w1,152(%r15) (backchain) */
+                       EMIT6_DISP_LH(0xe3000000, 0x0024, REG_W1, REG_0,
                                      REG_15, 152);
        }
        /*
index 460fdb21cf61da215eba972cd75359d0be5d4b4e..ed2394dd14e92faf57ac34b63ef49731f08ade68 100644 (file)
@@ -75,7 +75,13 @@ static void __zpci_event_availability(struct zpci_ccdf_avail *ccdf)
        zpci_err_hex(ccdf, sizeof(*ccdf));
 
        switch (ccdf->pec) {
-       case 0x0301: /* Standby -> Configured */
+       case 0x0301: /* Reserved|Standby -> Configured */
+               if (!zdev) {
+                       ret = clp_add_pci_device(ccdf->fid, ccdf->fh, 0);
+                       if (ret)
+                               break;
+                       zdev = get_zdev_by_fid(ccdf->fid);
+               }
                if (!zdev || zdev->state != ZPCI_FN_STATE_STANDBY)
                        break;
                zdev->state = ZPCI_FN_STATE_CONFIGURED;
index f384839c3ee53e2608209217372423025e21e911..cc3f6420b71c1d6438067761c3a905f6033dc36a 100644 (file)
@@ -42,8 +42,6 @@ static inline unsigned long __cmpxchg(volatile unsigned long *m,
                                        (unsigned long)(o),     \
                                        (unsigned long)(n)))
 
-#define __HAVE_ARCH_CMPXCHG    1
-
 #include <asm-generic/cmpxchg-local.h>
 
 #endif /* _ASM_SCORE_CMPXCHG_H */
index 00b7d3a2fc60681253eb2e1c1b874e48bbd02a4a..16efa3ad037f7cffbdbb4a5ffcf57a5d25325648 100644 (file)
@@ -175,10 +175,10 @@ ENTRY(__clear_user)
        br      r3
 
        .section .fixup, "ax"
+99:
        br      r3
        .previous
        .section __ex_table, "a"
        .align  2
-99:
        .word   0b, 99b
        .previous
index 4ce95a001b807059e2362e1e4088ab125b74d22d..45361946460fedd7fa2873a32a828542655aadb8 100644 (file)
@@ -18,7 +18,6 @@
 #include <linux/delay.h>
 #include <linux/types.h>
 #include <linux/irq.h>
-#include <asm/pci.h>
 #include <asm/io.h>
 #include "pci-sh5.h"
 
index 16c1e721bf5434ebcafaf578422b71bb85ed36cf..8229114c6a58cfa82c821d67570b0a93626a523d 100644 (file)
@@ -20,7 +20,6 @@
 #include <linux/types.h>
 #include <linux/irq.h>
 #include <cpu/irq.h>
-#include <asm/pci.h>
 #include <asm/io.h>
 #include "pci-sh5.h"
 
index 43715308b06874c115d1a5477b00beea79d68a7c..bf91037db4e01f66dcf6a2e84536279dcbf028c1 100644 (file)
@@ -32,7 +32,7 @@
 #define ctrl_barrier() __asm__ __volatile__ ("nop;nop;nop;nop;nop;nop;nop;nop")
 #endif
 
-#define set_mb(var, value) do { (void)xchg(&var, value); } while (0)
+#define smp_store_mb(var, value) do { (void)xchg(&var, value); } while (0)
 
 #include <asm-generic/barrier.h>
 
index f6bd1406b897fc927510724d8abd99e807b85ad8..85c97b188d71647683df8bebc6fd7fbbb3d55442 100644 (file)
@@ -46,8 +46,6 @@ extern void __xchg_called_with_bad_pointer(void);
  * if something tries to do an invalid cmpxchg(). */
 extern void __cmpxchg_called_with_bad_pointer(void);
 
-#define __HAVE_ARCH_CMPXCHG 1
-
 static inline unsigned long __cmpxchg(volatile void * ptr, unsigned long old,
                unsigned long new, int size)
 {
index 5b4511552998d9207e7be0cd7d801ccb99242290..e343dbd02e416d3ea1891374242b75781f546f73 100644 (file)
@@ -86,24 +86,6 @@ extern void pcibios_set_master(struct pci_dev *dev);
  * direct memory write.
  */
 #define PCI_DISABLE_MWI
-
-static inline void pci_dma_burst_advice(struct pci_dev *pdev,
-                                       enum pci_dma_burst_strategy *strat,
-                                       unsigned long *strategy_parameter)
-{
-       unsigned long cacheline_size;
-       u8 byte;
-
-       pci_read_config_byte(pdev, PCI_CACHE_LINE_SIZE, &byte);
-
-       if (byte == 0)
-               cacheline_size = L1_CACHE_BYTES;
-       else
-               cacheline_size = byte << 2;
-
-       *strat = PCI_DMA_BURST_MULTIPLE;
-       *strategy_parameter = cacheline_size;
-}
 #endif
 
 /* Board-specific fixup routines. */
index 1fdf1ee672de130f6c36ee73a3959cae5084ae3e..7f54bf2f453d7a7b31c93d4f1e356071c94bf94e 100644 (file)
@@ -246,8 +246,7 @@ int __init arch_clk_init(void)
        for (i = 0; i < ARRAY_SIZE(main_clks); i++)
                ret |= clk_register(main_clks[i]);
 
-       for (i = 0; i < ARRAY_SIZE(lookups); i++)
-               clkdev_add(&lookups[i]);
+       clkdev_add_table(lookups, ARRAY_SIZE(lookups));
 
        if (!ret)
                ret = sh_clk_div4_register(div4_clks, ARRAY_SIZE(div4_clks),
index 9a28fdb36387363f8d42ce67ad3c0bd140cb4088..e40ec2c97ad199c9f32f2b47837fb64e948b0971 100644 (file)
@@ -141,8 +141,8 @@ int __init arch_clk_init(void)
 
        for (i = 0; i < ARRAY_SIZE(clks); i++)
                ret |= clk_register(clks[i]);
-       for (i = 0; i < ARRAY_SIZE(lookups); i++)
-               clkdev_add(&lookups[i]);
+
+       clkdev_add_table(lookups, ARRAY_SIZE(lookups));
 
        if (!ret)
                ret = sh_clk_div4_register(div4_clks, ARRAY_SIZE(div4_clks),
index 17d0ea55a5a2f24ac54178e88308114395807498..8eb6e62340c950bdfe1610325b1f3e12bd25c8f8 100644 (file)
@@ -164,8 +164,8 @@ int __init arch_clk_init(void)
 
        for (i = 0; i < ARRAY_SIZE(clks); i++)
                ret |= clk_register(clks[i]);
-       for (i = 0; i < ARRAY_SIZE(lookups); i++)
-               clkdev_add(&lookups[i]);
+
+       clkdev_add_table(lookups, ARRAY_SIZE(lookups));
 
        if (!ret)
                ret = sh_clk_div4_register(div4_clks, ARRAY_SIZE(div4_clks),
index bec2a83f1ba5dc224c49db9dac0e20d22e15a5ff..5e50e7ebeff089b9cd82cc9774363e03f3a58054 100644 (file)
@@ -179,8 +179,8 @@ int __init arch_clk_init(void)
 
        for (i = 0; i < ARRAY_SIZE(clks); i++)
                ret |= clk_register(clks[i]);
-       for (i = 0; i < ARRAY_SIZE(lookups); i++)
-               clkdev_add(&lookups[i]);
+
+       clkdev_add_table(lookups, ARRAY_SIZE(lookups));
 
        if (!ret)
                ret = sh_clk_div4_register(div4_clks, ARRAY_SIZE(div4_clks),
index 9a49a44f6f94c85d0d13e991cab8eab836083fb4..605221d1448a19a7634c349d34139fc4744517dc 100644 (file)
@@ -138,8 +138,8 @@ int __init arch_clk_init(void)
 
        for (i = 0; i < ARRAY_SIZE(clks); i++)
                ret |= clk_register(clks[i]);
-       for (i = 0; i < ARRAY_SIZE(lookups); i++)
-               clkdev_add(&lookups[i]);
+
+       clkdev_add_table(lookups, ARRAY_SIZE(lookups));
 
        if (!ret)
                ret = sh_clk_div4_register(div4_clks, ARRAY_SIZE(div4_clks),
index b688731d7ede6d4529f81e0fed5523b9541f9dad..c9d2b922734beefc7522b454fb30427ea5f789d7 100644 (file)
@@ -33,10 +33,10 @@ static int md5_sparc64_init(struct shash_desc *desc)
 {
        struct md5_state *mctx = shash_desc_ctx(desc);
 
-       mctx->hash[0] = cpu_to_le32(0x67452301);
-       mctx->hash[1] = cpu_to_le32(0xefcdab89);
-       mctx->hash[2] = cpu_to_le32(0x98badcfe);
-       mctx->hash[3] = cpu_to_le32(0x10325476);
+       mctx->hash[0] = cpu_to_le32(MD5_H0);
+       mctx->hash[1] = cpu_to_le32(MD5_H1);
+       mctx->hash[2] = cpu_to_le32(MD5_H2);
+       mctx->hash[3] = cpu_to_le32(MD5_H3);
        mctx->byte_count = 0;
 
        return 0;
index 76648941fea71b4327e058d6e995a3331587541c..809941e33e1217489b1c88caa4c000cf55a08b62 100644 (file)
@@ -40,8 +40,8 @@ do {  __asm__ __volatile__("ba,pt     %%xcc, 1f\n\t" \
 #define dma_rmb()      rmb()
 #define dma_wmb()      wmb()
 
-#define set_mb(__var, __value) \
-       do { __var = __value; membar_safe("#StoreLoad"); } while(0)
+#define smp_store_mb(__var, __value) \
+       do { WRITE_ONCE(__var, __value); membar_safe("#StoreLoad"); } while(0)
 
 #ifdef CONFIG_SMP
 #define smp_mb()       mb()
index d38b52dca216273a147328cc2d0288c20c2eeba6..83ffb83c5397f3e4bede59570856a11c41d9fc0e 100644 (file)
@@ -34,7 +34,6 @@ static inline unsigned long __xchg(unsigned long x, __volatile__ void * ptr, int
  *
  * Cribbed from <asm-parisc/atomic.h>
  */
-#define __HAVE_ARCH_CMPXCHG    1
 
 /* bug catcher for when unsupported size is used - won't link */
 void __cmpxchg_called_with_bad_pointer(void);
index 0e1ed6cfbf68faa50792369faa69b4f917db1fd1..faa2f61058c271abf9ce799a02c9a55678464533 100644 (file)
@@ -65,8 +65,6 @@ static inline unsigned long __xchg(unsigned long x, __volatile__ void * ptr,
 
 #include <asm-generic/cmpxchg-local.h>
 
-#define __HAVE_ARCH_CMPXCHG 1
-
 static inline unsigned long
 __cmpxchg_u32(volatile int *m, int old, int new)
 {
index 407ac14295f43ec01e93141e1673794d5e3f024c..57f26c398dc9d21961e3d811ac1e11c8ea4ccf71 100644 (file)
@@ -129,6 +129,7 @@ static inline void sbus_memcpy_toio(volatile void __iomem *dst,
 void __iomem *ioremap(unsigned long offset, unsigned long size);
 #define ioremap_nocache(X,Y)   ioremap((X),(Y))
 #define ioremap_wc(X,Y)                ioremap((X),(Y))
+#define ioremap_wt(X,Y)                ioremap((X),(Y))
 void iounmap(volatile void __iomem *addr);
 
 /* Create a virtual mapping cookie for an IO port range */
index 50d4840d9aebbfa036c7a110e9f866fa77007647..c32fa3f752c8ea4954d2d2faeb7bc3f1c2015453 100644 (file)
@@ -402,6 +402,7 @@ static inline void __iomem *ioremap(unsigned long offset, unsigned long size)
 
 #define ioremap_nocache(X,Y)           ioremap((X),(Y))
 #define ioremap_wc(X,Y)                        ioremap((X),(Y))
+#define ioremap_wt(X,Y)                        ioremap((X),(Y))
 
 static inline void iounmap(volatile void __iomem *addr)
 {
index 53e9b4987db0b9212116945274f6d53d9c8bc448..b7c092df31340cf146c4301e4f8de1aa39d2a764 100644 (file)
 
 struct pci_dev;
 
-#ifdef CONFIG_PCI
-static inline void pci_dma_burst_advice(struct pci_dev *pdev,
-                                       enum pci_dma_burst_strategy *strat,
-                                       unsigned long *strategy_parameter)
-{
-       *strat = PCI_DMA_BURST_INFINITY;
-       *strategy_parameter = ~0UL;
-}
-#endif
-
 #endif /* __KERNEL__ */
 
 #ifndef CONFIG_LEON_PCI
index bd00a62261696098c220ed885fba73296598dab5..022d16008a00d8a3c82de515f0e0ca8c6f7001c0 100644 (file)
 #define PCI64_REQUIRED_MASK    (~(u64)0)
 #define PCI64_ADDR_BASE                0xfffc000000000000UL
 
-#ifdef CONFIG_PCI
-static inline void pci_dma_burst_advice(struct pci_dev *pdev,
-                                       enum pci_dma_burst_strategy *strat,
-                                       unsigned long *strategy_parameter)
-{
-       unsigned long cacheline_size;
-       u8 byte;
-
-       pci_read_config_byte(pdev, PCI_CACHE_LINE_SIZE, &byte);
-       if (byte == 0)
-               cacheline_size = 1024;
-       else
-               cacheline_size = (int) byte * 4;
-
-       *strat = PCI_DMA_BURST_BOUNDARY;
-       *strategy_parameter = cacheline_size;
-}
-#endif
-
 /* Return the index of the PCI controller for device PDEV. */
 
 int pci_domain_nr(struct pci_bus *bus);
index 7b11c5fadd4220f5e8694b6e2a2f906ccf4a50d3..0496970cef8296237edcf1f4f688dffa8af5ecfc 100644 (file)
@@ -105,9 +105,6 @@ static inline long atomic64_add_unless(atomic64_t *v, long a, long u)
 
 #define atomic64_inc_not_zero(v)       atomic64_add_unless((v), 1, 0)
 
-/* Define this to indicate that cmpxchg is an efficient operation. */
-#define __HAVE_ARCH_CMPXCHG
-
 #endif /* !__ASSEMBLY__ */
 
 #endif /* _ASM_TILE_ATOMIC_64_H */
index 6ef4ecab1df29bb86b5dbca9c0f900f0ae7133f7..dc61de15c1f936d4990d5b8d06b1e0d4ab0eb913 100644 (file)
@@ -54,7 +54,7 @@ extern void iounmap(volatile void __iomem *addr);
 
 #define ioremap_nocache(physaddr, size)                ioremap(physaddr, size)
 #define ioremap_wc(physaddr, size)             ioremap(physaddr, size)
-#define ioremap_writethrough(physaddr, size)   ioremap(physaddr, size)
+#define ioremap_wt(physaddr, size)             ioremap(physaddr, size)
 #define ioremap_fullcache(physaddr, size)      ioremap(physaddr, size)
 
 #define mmiowb()
index 654407e98619d2aa247c9f23d65504aad60dfcf1..38b3f3785c3c82c6c55a98192d67bc60bd1f45a9 100644 (file)
 #include <asm-generic/pci.h>
 #include <mach/hardware.h> /* for PCIBIOS_MIN_* */
 
-#ifdef CONFIG_PCI
-static inline void pci_dma_burst_advice(struct pci_dev *pdev,
-                                       enum pci_dma_burst_strategy *strat,
-                                       unsigned long *strategy_parameter)
-{
-       *strat = PCI_DMA_BURST_INFINITY;
-       *strategy_parameter = ~0UL;
-}
-#endif
-
 #define HAVE_PCI_MMAP
 extern int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma,
        enum pci_mmap_state mmap_state, int write_combine);
index 3942f74c92d7d338ee0c8a858ad2e5a81774686d..1538562cc720e78132d0eb31c38116b029d56ce0 100644 (file)
@@ -1,3 +1,6 @@
+
+obj-y += entry/
+
 obj-$(CONFIG_KVM) += kvm/
 
 # Xen paravirtualization support
@@ -11,7 +14,7 @@ obj-y += kernel/
 obj-y += mm/
 
 obj-y += crypto/
-obj-y += vdso/
+
 obj-$(CONFIG_IA32_EMULATION) += ia32/
 
 obj-y += platform/
index 226d5696e1d1dd5fe715124710b0f2cd04f8c495..7e39f9b227056d80a828faa1b2683d9cceb14d98 100644 (file)
@@ -9,140 +9,141 @@ config 64BIT
 config X86_32
        def_bool y
        depends on !64BIT
-       select CLKSRC_I8253
-       select HAVE_UID16
 
 config X86_64
        def_bool y
        depends on 64BIT
-       select X86_DEV_DMA_OPS
-       select ARCH_USE_CMPXCHG_LOCKREF
-       select HAVE_LIVEPATCH
 
 ### Arch settings
 config X86
        def_bool y
-       select ACPI_SYSTEM_POWER_STATES_SUPPORT if ACPI
-       select ARCH_MIGHT_HAVE_ACPI_PDC if ACPI
+       select ACPI_LEGACY_TABLES_LOOKUP        if ACPI
+       select ACPI_SYSTEM_POWER_STATES_SUPPORT if ACPI
+       select ANON_INODES
+       select ARCH_CLOCKSOURCE_DATA
+       select ARCH_DISCARD_MEMBLOCK
+       select ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE
        select ARCH_HAS_DEBUG_STRICT_USER_COPY_CHECKS
+       select ARCH_HAS_ELF_RANDOMIZE
        select ARCH_HAS_FAST_MULTIPLIER
        select ARCH_HAS_GCOV_PROFILE_ALL
+       select ARCH_HAS_SG_CHAIN
+       select ARCH_HAVE_NMI_SAFE_CMPXCHG
+       select ARCH_MIGHT_HAVE_ACPI_PDC         if ACPI
        select ARCH_MIGHT_HAVE_PC_PARPORT
        select ARCH_MIGHT_HAVE_PC_SERIO
-       select HAVE_AOUT if X86_32
-       select HAVE_UNSTABLE_SCHED_CLOCK
-       select ARCH_SUPPORTS_NUMA_BALANCING if X86_64
-       select ARCH_SUPPORTS_INT128 if X86_64
-       select HAVE_IDE
-       select HAVE_OPROFILE
-       select HAVE_PCSPKR_PLATFORM
-       select HAVE_PERF_EVENTS
-       select HAVE_IOREMAP_PROT
-       select HAVE_KPROBES
-       select HAVE_MEMBLOCK
-       select HAVE_MEMBLOCK_NODE_MAP
-       select ARCH_DISCARD_MEMBLOCK
-       select ARCH_WANT_OPTIONAL_GPIOLIB
+       select ARCH_SUPPORTS_ATOMIC_RMW
+       select ARCH_SUPPORTS_INT128             if X86_64
+       select ARCH_SUPPORTS_NUMA_BALANCING     if X86_64
+       select ARCH_USE_BUILTIN_BSWAP
+       select ARCH_USE_CMPXCHG_LOCKREF         if X86_64
+       select ARCH_USE_QUEUED_RWLOCKS
+       select ARCH_USE_QUEUED_SPINLOCKS
        select ARCH_WANT_FRAME_POINTERS
-       select HAVE_DMA_ATTRS
-       select HAVE_DMA_CONTIGUOUS
-       select HAVE_KRETPROBES
+       select ARCH_WANT_IPC_PARSE_VERSION      if X86_32
+       select ARCH_WANT_OPTIONAL_GPIOLIB
+       select BUILDTIME_EXTABLE_SORT
+       select CLKEVT_I8253
+       select CLKSRC_I8253                     if X86_32
+       select CLOCKSOURCE_VALIDATE_LAST_CYCLE
+       select CLOCKSOURCE_WATCHDOG
+       select CLONE_BACKWARDS                  if X86_32
+       select COMPAT_OLD_SIGACTION             if IA32_EMULATION
+       select DCACHE_WORD_ACCESS
+       select GENERIC_CLOCKEVENTS
+       select GENERIC_CLOCKEVENTS_BROADCAST    if X86_64 || (X86_32 && X86_LOCAL_APIC)
+       select GENERIC_CLOCKEVENTS_MIN_ADJUST
+       select GENERIC_CMOS_UPDATE
+       select GENERIC_CPU_AUTOPROBE
        select GENERIC_EARLY_IOREMAP
-       select HAVE_OPTPROBES
-       select HAVE_KPROBES_ON_FTRACE
-       select HAVE_FTRACE_MCOUNT_RECORD
-       select HAVE_FENTRY if X86_64
+       select GENERIC_FIND_FIRST_BIT
+       select GENERIC_IOMAP
+       select GENERIC_IRQ_PROBE
+       select GENERIC_IRQ_SHOW
+       select GENERIC_PENDING_IRQ              if SMP
+       select GENERIC_SMP_IDLE_THREAD
+       select GENERIC_STRNCPY_FROM_USER
+       select GENERIC_STRNLEN_USER
+       select GENERIC_TIME_VSYSCALL
+       select HAVE_ACPI_APEI                   if ACPI
+       select HAVE_ACPI_APEI_NMI               if ACPI
+       select HAVE_ALIGNED_STRUCT_PAGE         if SLUB
+       select HAVE_AOUT                        if X86_32
+       select HAVE_ARCH_AUDITSYSCALL
+       select HAVE_ARCH_HUGE_VMAP              if X86_64 || X86_PAE
+       select HAVE_ARCH_JUMP_LABEL
+       select HAVE_ARCH_KASAN                  if X86_64 && SPARSEMEM_VMEMMAP
+       select HAVE_ARCH_KGDB
+       select HAVE_ARCH_KMEMCHECK
+       select HAVE_ARCH_SECCOMP_FILTER
+       select HAVE_ARCH_SOFT_DIRTY             if X86_64
+       select HAVE_ARCH_TRACEHOOK
+       select HAVE_ARCH_TRANSPARENT_HUGEPAGE
+       select HAVE_BPF_JIT                     if X86_64
+       select HAVE_CC_STACKPROTECTOR
+       select HAVE_CMPXCHG_DOUBLE
+       select HAVE_CMPXCHG_LOCAL
+       select HAVE_CONTEXT_TRACKING            if X86_64
        select HAVE_C_RECORDMCOUNT
+       select HAVE_DEBUG_KMEMLEAK
+       select HAVE_DEBUG_STACKOVERFLOW
+       select HAVE_DMA_API_DEBUG
+       select HAVE_DMA_ATTRS
+       select HAVE_DMA_CONTIGUOUS
        select HAVE_DYNAMIC_FTRACE
        select HAVE_DYNAMIC_FTRACE_WITH_REGS
-       select HAVE_FUNCTION_TRACER
-       select HAVE_FUNCTION_GRAPH_TRACER
-       select HAVE_FUNCTION_GRAPH_FP_TEST
-       select HAVE_SYSCALL_TRACEPOINTS
-       select SYSCTL_EXCEPTION_TRACE
-       select HAVE_KVM
-       select HAVE_ARCH_KGDB
-       select HAVE_ARCH_TRACEHOOK
-       select HAVE_GENERIC_DMA_COHERENT if X86_32
        select HAVE_EFFICIENT_UNALIGNED_ACCESS
-       select USER_STACKTRACE_SUPPORT
-       select HAVE_REGS_AND_STACK_ACCESS_API
-       select HAVE_DMA_API_DEBUG
-       select HAVE_KERNEL_GZIP
+       select HAVE_FENTRY                      if X86_64
+       select HAVE_FTRACE_MCOUNT_RECORD
+       select HAVE_FUNCTION_GRAPH_FP_TEST
+       select HAVE_FUNCTION_GRAPH_TRACER
+       select HAVE_FUNCTION_TRACER
+       select HAVE_GENERIC_DMA_COHERENT        if X86_32
+       select HAVE_HW_BREAKPOINT
+       select HAVE_IDE
+       select HAVE_IOREMAP_PROT
+       select HAVE_IRQ_EXIT_ON_IRQ_STACK       if X86_64
+       select HAVE_IRQ_TIME_ACCOUNTING
        select HAVE_KERNEL_BZIP2
+       select HAVE_KERNEL_GZIP
+       select HAVE_KERNEL_LZ4
        select HAVE_KERNEL_LZMA
-       select HAVE_KERNEL_XZ
        select HAVE_KERNEL_LZO
-       select HAVE_KERNEL_LZ4
-       select HAVE_HW_BREAKPOINT
+       select HAVE_KERNEL_XZ
+       select HAVE_KPROBES
+       select HAVE_KPROBES_ON_FTRACE
+       select HAVE_KRETPROBES
+       select HAVE_KVM
+       select HAVE_LIVEPATCH                   if X86_64
+       select HAVE_MEMBLOCK
+       select HAVE_MEMBLOCK_NODE_MAP
        select HAVE_MIXED_BREAKPOINTS_REGS
-       select PERF_EVENTS
+       select HAVE_OPROFILE
+       select HAVE_OPTPROBES
+       select HAVE_PCSPKR_PLATFORM
+       select HAVE_PERF_EVENTS
        select HAVE_PERF_EVENTS_NMI
        select HAVE_PERF_REGS
        select HAVE_PERF_USER_STACK_DUMP
-       select HAVE_DEBUG_KMEMLEAK
-       select ANON_INODES
-       select HAVE_ALIGNED_STRUCT_PAGE if SLUB
-       select HAVE_CMPXCHG_LOCAL
-       select HAVE_CMPXCHG_DOUBLE
-       select HAVE_ARCH_KMEMCHECK
-       select HAVE_ARCH_KASAN if X86_64 && SPARSEMEM_VMEMMAP
+       select HAVE_REGS_AND_STACK_ACCESS_API
+       select HAVE_SYSCALL_TRACEPOINTS
+       select HAVE_UID16                       if X86_32
+       select HAVE_UNSTABLE_SCHED_CLOCK
        select HAVE_USER_RETURN_NOTIFIER
-       select ARCH_HAS_ELF_RANDOMIZE
-       select HAVE_ARCH_JUMP_LABEL
-       select ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE
-       select SPARSE_IRQ
-       select GENERIC_FIND_FIRST_BIT
-       select GENERIC_IRQ_PROBE
-       select GENERIC_PENDING_IRQ if SMP
-       select GENERIC_IRQ_SHOW
-       select GENERIC_CLOCKEVENTS_MIN_ADJUST
        select IRQ_FORCED_THREADING
-       select HAVE_BPF_JIT if X86_64
-       select HAVE_ARCH_TRANSPARENT_HUGEPAGE
-       select HAVE_ARCH_HUGE_VMAP if X86_64 || (X86_32 && X86_PAE)
-       select ARCH_HAS_SG_CHAIN
-       select CLKEVT_I8253
-       select ARCH_HAVE_NMI_SAFE_CMPXCHG
-       select GENERIC_IOMAP
-       select DCACHE_WORD_ACCESS
-       select GENERIC_SMP_IDLE_THREAD
-       select ARCH_WANT_IPC_PARSE_VERSION if X86_32
-       select HAVE_ARCH_SECCOMP_FILTER
-       select BUILDTIME_EXTABLE_SORT
-       select GENERIC_CMOS_UPDATE
-       select HAVE_ARCH_SOFT_DIRTY if X86_64
-       select CLOCKSOURCE_WATCHDOG
-       select GENERIC_CLOCKEVENTS
-       select ARCH_CLOCKSOURCE_DATA
-       select CLOCKSOURCE_VALIDATE_LAST_CYCLE
-       select GENERIC_CLOCKEVENTS_BROADCAST if X86_64 || (X86_32 && X86_LOCAL_APIC)
-       select GENERIC_TIME_VSYSCALL
-       select GENERIC_STRNCPY_FROM_USER
-       select GENERIC_STRNLEN_USER
-       select HAVE_CONTEXT_TRACKING if X86_64
-       select HAVE_IRQ_TIME_ACCOUNTING
-       select VIRT_TO_BUS
-       select MODULES_USE_ELF_REL if X86_32
-       select MODULES_USE_ELF_RELA if X86_64
-       select CLONE_BACKWARDS if X86_32
-       select ARCH_USE_BUILTIN_BSWAP
-       select ARCH_USE_QUEUE_RWLOCK
-       select OLD_SIGSUSPEND3 if X86_32 || IA32_EMULATION
-       select OLD_SIGACTION if X86_32
-       select COMPAT_OLD_SIGACTION if IA32_EMULATION
+       select MODULES_USE_ELF_RELA             if X86_64
+       select MODULES_USE_ELF_REL              if X86_32
+       select OLD_SIGACTION                    if X86_32
+       select OLD_SIGSUSPEND3                  if X86_32 || IA32_EMULATION
+       select PERF_EVENTS
        select RTC_LIB
-       select HAVE_DEBUG_STACKOVERFLOW
-       select HAVE_IRQ_EXIT_ON_IRQ_STACK if X86_64
-       select HAVE_CC_STACKPROTECTOR
-       select GENERIC_CPU_AUTOPROBE
-       select HAVE_ARCH_AUDITSYSCALL
-       select ARCH_SUPPORTS_ATOMIC_RMW
-       select HAVE_ACPI_APEI if ACPI
-       select HAVE_ACPI_APEI_NMI if ACPI
-       select ACPI_LEGACY_TABLES_LOOKUP if ACPI
-       select X86_FEATURE_NAMES if PROC_FS
+       select SPARSE_IRQ
        select SRCU
+       select SYSCTL_EXCEPTION_TRACE
+       select USER_STACKTRACE_SUPPORT
+       select VIRT_TO_BUS
+       select X86_DEV_DMA_OPS                  if X86_64
+       select X86_FEATURE_NAMES                if PROC_FS
 
 config INSTRUCTION_DECODER
        def_bool y
@@ -260,10 +261,6 @@ config X86_64_SMP
        def_bool y
        depends on X86_64 && SMP
 
-config X86_HT
-       def_bool y
-       depends on SMP
-
 config X86_32_LAZY_GS
        def_bool y
        depends on X86_32 && !CC_STACKPROTECTOR
@@ -341,7 +338,7 @@ config X86_FEATURE_NAMES
 
 config X86_X2APIC
        bool "Support x2apic"
-       depends on X86_LOCAL_APIC && X86_64 && IRQ_REMAP
+       depends on X86_LOCAL_APIC && X86_64 && (IRQ_REMAP || HYPERVISOR_GUEST)
        ---help---
          This enables x2apic support on CPUs that have this feature.
 
@@ -441,6 +438,7 @@ config X86_UV
        depends on X86_EXTENDED_PLATFORM
        depends on NUMA
        depends on X86_X2APIC
+       depends on PCI
        ---help---
          This option is needed in order to support SGI Ultraviolet systems.
          If you don't have one of these, you should say N here.
@@ -466,7 +464,6 @@ config X86_INTEL_CE
        select X86_REBOOTFIXUPS
        select OF
        select OF_EARLY_FLATTREE
-       select IRQ_DOMAIN
        ---help---
          Select for the Intel CE media processor (CE4100) SOC.
          This option compiles in support for the CE4100 SOC for settop
@@ -666,7 +663,7 @@ config PARAVIRT_DEBUG
 config PARAVIRT_SPINLOCKS
        bool "Paravirtualization layer for spinlocks"
        depends on PARAVIRT && SMP
-       select UNINLINE_SPIN_UNLOCK
+       select UNINLINE_SPIN_UNLOCK if !QUEUED_SPINLOCKS
        ---help---
          Paravirtualized spinlocks allow a pvops backend to replace the
          spinlock implementation with something virtualization-friendly
@@ -851,11 +848,12 @@ config NR_CPUS
        default "1" if !SMP
        default "8192" if MAXSMP
        default "32" if SMP && X86_BIGSMP
-       default "8" if SMP
+       default "8" if SMP && X86_32
+       default "64" if SMP
        ---help---
          This allows you to specify the maximum number of CPUs which this
          kernel will support.  If CPUMASK_OFFSTACK is enabled, the maximum
-         supported value is 4096, otherwise the maximum value is 512.  The
+         supported value is 8192, otherwise the maximum value is 512.  The
          minimum value which makes sense is 2.
 
          This is purely to save memory - each supported CPU adds
@@ -863,7 +861,7 @@ config NR_CPUS
 
 config SCHED_SMT
        bool "SMT (Hyperthreading) scheduler support"
-       depends on X86_HT
+       depends on SMP
        ---help---
          SMT scheduler support improves the CPU scheduler's decision making
          when dealing with Intel Pentium 4 chips with HyperThreading at a
@@ -873,7 +871,7 @@ config SCHED_SMT
 config SCHED_MC
        def_bool y
        prompt "Multi-core scheduler support"
-       depends on X86_HT
+       depends on SMP
        ---help---
          Multi-core scheduler support improves the CPU scheduler's decision
          making when dealing with multi-core CPU chips at a cost of slightly
@@ -914,12 +912,12 @@ config X86_UP_IOAPIC
 config X86_LOCAL_APIC
        def_bool y
        depends on X86_64 || SMP || X86_32_NON_STANDARD || X86_UP_APIC || PCI_MSI
-       select GENERIC_IRQ_LEGACY_ALLOC_HWIRQ
+       select IRQ_DOMAIN_HIERARCHY
+       select PCI_MSI_IRQ_DOMAIN if PCI_MSI
 
 config X86_IO_APIC
        def_bool y
        depends on X86_LOCAL_APIC || X86_UP_IOAPIC
-       select IRQ_DOMAIN
 
 config X86_REROUTE_FOR_BROKEN_BOOT_IRQS
        bool "Reroute for broken boot IRQs"
index 72484a645f056d1d7a8aa4182b07fc908c3b4162..a15893d17c55988b542ad362f8c0074958130587 100644 (file)
@@ -332,4 +332,27 @@ config X86_DEBUG_STATIC_CPU_HAS
 
          If unsure, say N.
 
+config X86_DEBUG_FPU
+       bool "Debug the x86 FPU code"
+       depends on DEBUG_KERNEL
+       default y
+       ---help---
+         If this option is enabled then there will be extra sanity
+         checks and (boot time) debug printouts added to the kernel.
+         This debugging adds some small amount of runtime overhead
+         to the kernel.
+
+         If unsure, say N.
+
+config PUNIT_ATOM_DEBUG
+       tristate "ATOM Punit debug driver"
+       select DEBUG_FS
+       select IOSF_MBI
+       ---help---
+         This is a debug driver, which gets the power states
+         of all Punit North Complex devices. The power states of
+         each device is exposed as part of the debugfs interface.
+         The current power state can be read from
+         /sys/kernel/debug/punit_atom/dev_power_state
+
 endmenu
index 2fda005bb33443c1684546dff0620f8db48e7c5c..118e6debc483ed6f6d6357209619216843ee670c 100644 (file)
@@ -77,6 +77,12 @@ else
         KBUILD_AFLAGS += -m64
         KBUILD_CFLAGS += -m64
 
+        # Align jump targets to 1 byte, not the default 16 bytes:
+        KBUILD_CFLAGS += -falign-jumps=1
+
+        # Pack loops tightly as well:
+        KBUILD_CFLAGS += -falign-loops=1
+
         # Don't autogenerate traditional x87 instructions
         KBUILD_CFLAGS += $(call cc-option,-mno-80387)
         KBUILD_CFLAGS += $(call cc-option,-mno-fp-ret-in-387)
@@ -84,6 +90,9 @@ else
        # Use -mpreferred-stack-boundary=3 if supported.
        KBUILD_CFLAGS += $(call cc-option,-mpreferred-stack-boundary=3)
 
+       # Use -mskip-rax-setup if supported.
+       KBUILD_CFLAGS += $(call cc-option,-mskip-rax-setup)
+
         # FIXME - should be integrated in Makefile.cpu (Makefile_32.cpu)
         cflags-$(CONFIG_MK8) += $(call cc-option,-march=k8)
         cflags-$(CONFIG_MPSC) += $(call cc-option,-march=nocona)
@@ -140,12 +149,6 @@ endif
 sp-$(CONFIG_X86_32) := esp
 sp-$(CONFIG_X86_64) := rsp
 
-# do binutils support CFI?
-cfi := $(call as-instr,.cfi_startproc\n.cfi_rel_offset $(sp-y)$(comma)0\n.cfi_endproc,-DCONFIG_AS_CFI=1)
-# is .cfi_signal_frame supported too?
-cfi-sigframe := $(call as-instr,.cfi_startproc\n.cfi_signal_frame\n.cfi_endproc,-DCONFIG_AS_CFI_SIGNAL_FRAME=1)
-cfi-sections := $(call as-instr,.cfi_sections .debug_frame,-DCONFIG_AS_CFI_SECTIONS=1)
-
 # does binutils support specific instructions?
 asinstr := $(call as-instr,fxsaveq (%rax),-DCONFIG_AS_FXSAVEQ=1)
 asinstr += $(call as-instr,pshufb %xmm0$(comma)%xmm0,-DCONFIG_AS_SSSE3=1)
@@ -153,8 +156,8 @@ asinstr += $(call as-instr,crc32l %eax$(comma)%eax,-DCONFIG_AS_CRC32=1)
 avx_instr := $(call as-instr,vxorps %ymm0$(comma)%ymm1$(comma)%ymm2,-DCONFIG_AS_AVX=1)
 avx2_instr :=$(call as-instr,vpbroadcastb %xmm0$(comma)%ymm1,-DCONFIG_AS_AVX2=1)
 
-KBUILD_AFLAGS += $(cfi) $(cfi-sigframe) $(cfi-sections) $(asinstr) $(avx_instr) $(avx2_instr)
-KBUILD_CFLAGS += $(cfi) $(cfi-sigframe) $(cfi-sections) $(asinstr) $(avx_instr) $(avx2_instr)
+KBUILD_AFLAGS += $(asinstr) $(avx_instr) $(avx2_instr)
+KBUILD_CFLAGS += $(asinstr) $(avx_instr) $(avx2_instr)
 
 LDFLAGS := -m elf_$(UTS_MACHINE)
 
@@ -178,7 +181,7 @@ archscripts: scripts_basic
 # Syscall table generation
 
 archheaders:
-       $(Q)$(MAKE) $(build)=arch/x86/syscalls all
+       $(Q)$(MAKE) $(build)=arch/x86/entry/syscalls all
 
 archprepare:
 ifeq ($(CONFIG_KEXEC_FILE),y)
@@ -241,7 +244,7 @@ install:
 
 PHONY += vdso_install
 vdso_install:
-       $(Q)$(MAKE) $(build)=arch/x86/vdso $@
+       $(Q)$(MAKE) $(build)=arch/x86/entry/vdso $@
 
 archclean:
        $(Q)rm -rf $(objtree)/arch/i386
index 89dd0d78013aaff6c889340e0e3caceb4c8f8c88..805d25ca5f1db1602498c7047025b973ac788b3c 100644 (file)
@@ -2,15 +2,14 @@
 #define BOOT_COMPRESSED_MISC_H
 
 /*
- * we have to be careful, because no indirections are allowed here, and
- * paravirt_ops is a kind of one. As it will only run in baremetal anyway,
- * we just keep it from happening
+ * Special hack: we have to be careful, because no indirections are allowed here,
+ * and paravirt_ops is a kind of one. As it will only run in baremetal anyway,
+ * we just keep it from happening. (This list needs to be extended when new
+ * paravirt and debugging variants are added.)
  */
 #undef CONFIG_PARAVIRT
+#undef CONFIG_PARAVIRT_SPINLOCKS
 #undef CONFIG_KASAN
-#ifdef CONFIG_X86_32
-#define _ASM_X86_DESC_H 1
-#endif
 
 #include <linux/linkage.h>
 #include <linux/screen_info.h>
index 112cefacf2af07b216cb598232bcc586a76fa919..2bfc8a7c88c11e1d4f20d4d3c58e62063c76328f 100644 (file)
@@ -32,7 +32,7 @@
 #include <crypto/lrw.h>
 #include <crypto/xts.h>
 #include <asm/cpu_device_id.h>
-#include <asm/i387.h>
+#include <asm/fpu/api.h>
 #include <asm/crypto/aes.h>
 #include <crypto/ablk_helper.h>
 #include <crypto/scatterwalk.h>
 #endif
 
 
+#define AESNI_ALIGN    16
+#define AES_BLOCK_MASK (~(AES_BLOCK_SIZE - 1))
+#define RFC4106_HASH_SUBKEY_SIZE 16
+
 /* This data is stored at the end of the crypto_tfm struct.
  * It's a type of per "session" data storage location.
  * This needs to be 16 byte aligned.
  */
 struct aesni_rfc4106_gcm_ctx {
-       u8 hash_subkey[16];
-       struct crypto_aes_ctx aes_key_expanded;
+       u8 hash_subkey[16] __attribute__ ((__aligned__(AESNI_ALIGN)));
+       struct crypto_aes_ctx aes_key_expanded
+               __attribute__ ((__aligned__(AESNI_ALIGN)));
        u8 nonce[4];
-       struct cryptd_aead *cryptd_tfm;
 };
 
 struct aesni_gcm_set_hash_subkey_result {
@@ -66,10 +70,6 @@ struct aesni_hash_subkey_req_data {
        struct scatterlist sg;
 };
 
-#define AESNI_ALIGN    (16)
-#define AES_BLOCK_MASK (~(AES_BLOCK_SIZE-1))
-#define RFC4106_HASH_SUBKEY_SIZE 16
-
 struct aesni_lrw_ctx {
        struct lrw_table_ctx lrw_table;
        u8 raw_aes_ctx[sizeof(struct crypto_aes_ctx) + AESNI_ALIGN - 1];
@@ -283,10 +283,11 @@ static void (*aesni_gcm_dec_tfm)(void *ctx, u8 *out,
 static inline struct
 aesni_rfc4106_gcm_ctx *aesni_rfc4106_gcm_ctx_get(struct crypto_aead *tfm)
 {
-       return
-               (struct aesni_rfc4106_gcm_ctx *)
-               PTR_ALIGN((u8 *)
-               crypto_tfm_ctx(crypto_aead_tfm(tfm)), AESNI_ALIGN);
+       unsigned long align = AESNI_ALIGN;
+
+       if (align <= crypto_tfm_ctx_alignment())
+               align = 1;
+       return PTR_ALIGN(crypto_aead_ctx(tfm), align);
 }
 #endif
 
@@ -790,36 +791,30 @@ static int xts_decrypt(struct blkcipher_desc *desc, struct scatterlist *dst,
 #endif
 
 #ifdef CONFIG_X86_64
-static int rfc4106_init(struct crypto_tfm *tfm)
+static int rfc4106_init(struct crypto_aead *aead)
 {
        struct cryptd_aead *cryptd_tfm;
-       struct aesni_rfc4106_gcm_ctx *ctx = (struct aesni_rfc4106_gcm_ctx *)
-               PTR_ALIGN((u8 *)crypto_tfm_ctx(tfm), AESNI_ALIGN);
-       struct crypto_aead *cryptd_child;
-       struct aesni_rfc4106_gcm_ctx *child_ctx;
+       struct cryptd_aead **ctx = crypto_aead_ctx(aead);
+
        cryptd_tfm = cryptd_alloc_aead("__driver-gcm-aes-aesni",
                                       CRYPTO_ALG_INTERNAL,
                                       CRYPTO_ALG_INTERNAL);
        if (IS_ERR(cryptd_tfm))
                return PTR_ERR(cryptd_tfm);
 
-       cryptd_child = cryptd_aead_child(cryptd_tfm);
-       child_ctx = aesni_rfc4106_gcm_ctx_get(cryptd_child);
-       memcpy(child_ctx, ctx, sizeof(*ctx));
-       ctx->cryptd_tfm = cryptd_tfm;
-       tfm->crt_aead.reqsize = sizeof(struct aead_request)
-               + crypto_aead_reqsize(&cryptd_tfm->base);
+       *ctx = cryptd_tfm;
+       crypto_aead_set_reqsize(
+               aead,
+               sizeof(struct aead_request) +
+               crypto_aead_reqsize(&cryptd_tfm->base));
        return 0;
 }
 
-static void rfc4106_exit(struct crypto_tfm *tfm)
+static void rfc4106_exit(struct crypto_aead *aead)
 {
-       struct aesni_rfc4106_gcm_ctx *ctx =
-               (struct aesni_rfc4106_gcm_ctx *)
-               PTR_ALIGN((u8 *)crypto_tfm_ctx(tfm), AESNI_ALIGN);
-       if (!IS_ERR(ctx->cryptd_tfm))
-               cryptd_free_aead(ctx->cryptd_tfm);
-       return;
+       struct cryptd_aead **ctx = crypto_aead_ctx(aead);
+
+       cryptd_free_aead(*ctx);
 }
 
 static void
@@ -845,8 +840,6 @@ rfc4106_set_hash_subkey(u8 *hash_subkey, const u8 *key, unsigned int key_len)
        if (IS_ERR(ctr_tfm))
                return PTR_ERR(ctr_tfm);
 
-       crypto_ablkcipher_clear_flags(ctr_tfm, ~0);
-
        ret = crypto_ablkcipher_setkey(ctr_tfm, key, key_len);
        if (ret)
                goto out_free_ablkcipher;
@@ -895,73 +888,29 @@ out_free_ablkcipher:
 static int common_rfc4106_set_key(struct crypto_aead *aead, const u8 *key,
                                  unsigned int key_len)
 {
-       int ret = 0;
-       struct crypto_tfm *tfm = crypto_aead_tfm(aead);
        struct aesni_rfc4106_gcm_ctx *ctx = aesni_rfc4106_gcm_ctx_get(aead);
-       u8 *new_key_align, *new_key_mem = NULL;
 
        if (key_len < 4) {
-               crypto_tfm_set_flags(tfm, CRYPTO_TFM_RES_BAD_KEY_LEN);
+               crypto_aead_set_flags(aead, CRYPTO_TFM_RES_BAD_KEY_LEN);
                return -EINVAL;
        }
        /*Account for 4 byte nonce at the end.*/
        key_len -= 4;
-       if (key_len != AES_KEYSIZE_128 && key_len != AES_KEYSIZE_192 &&
-           key_len != AES_KEYSIZE_256) {
-               crypto_tfm_set_flags(tfm, CRYPTO_TFM_RES_BAD_KEY_LEN);
-               return -EINVAL;
-       }
 
        memcpy(ctx->nonce, key + key_len, sizeof(ctx->nonce));
-       /*This must be on a 16 byte boundary!*/
-       if ((unsigned long)(&(ctx->aes_key_expanded.key_enc[0])) % AESNI_ALIGN)
-               return -EINVAL;
-
-       if ((unsigned long)key % AESNI_ALIGN) {
-               /*key is not aligned: use an auxuliar aligned pointer*/
-               new_key_mem = kmalloc(key_len+AESNI_ALIGN, GFP_KERNEL);
-               if (!new_key_mem)
-                       return -ENOMEM;
-
-               new_key_align = PTR_ALIGN(new_key_mem, AESNI_ALIGN);
-               memcpy(new_key_align, key, key_len);
-               key = new_key_align;
-       }
 
-       if (!irq_fpu_usable())
-               ret = crypto_aes_expand_key(&(ctx->aes_key_expanded),
-               key, key_len);
-       else {
-               kernel_fpu_begin();
-               ret = aesni_set_key(&(ctx->aes_key_expanded), key, key_len);
-               kernel_fpu_end();
-       }
-       /*This must be on a 16 byte boundary!*/
-       if ((unsigned long)(&(ctx->hash_subkey[0])) % AESNI_ALIGN) {
-               ret = -EINVAL;
-               goto exit;
-       }
-       ret = rfc4106_set_hash_subkey(ctx->hash_subkey, key, key_len);
-exit:
-       kfree(new_key_mem);
-       return ret;
+       return aes_set_key_common(crypto_aead_tfm(aead),
+                                 &ctx->aes_key_expanded, key, key_len) ?:
+              rfc4106_set_hash_subkey(ctx->hash_subkey, key, key_len);
 }
 
 static int rfc4106_set_key(struct crypto_aead *parent, const u8 *key,
                           unsigned int key_len)
 {
-       struct aesni_rfc4106_gcm_ctx *ctx = aesni_rfc4106_gcm_ctx_get(parent);
-       struct crypto_aead *child = cryptd_aead_child(ctx->cryptd_tfm);
-       struct aesni_rfc4106_gcm_ctx *c_ctx = aesni_rfc4106_gcm_ctx_get(child);
-       struct cryptd_aead *cryptd_tfm = ctx->cryptd_tfm;
-       int ret;
+       struct cryptd_aead **ctx = crypto_aead_ctx(parent);
+       struct cryptd_aead *cryptd_tfm = *ctx;
 
-       ret = crypto_aead_setkey(child, key, key_len);
-       if (!ret) {
-               memcpy(ctx, c_ctx, sizeof(*ctx));
-               ctx->cryptd_tfm = cryptd_tfm;
-       }
-       return ret;
+       return crypto_aead_setkey(&cryptd_tfm->base, key, key_len);
 }
 
 static int common_rfc4106_set_authsize(struct crypto_aead *aead,
@@ -975,7 +924,7 @@ static int common_rfc4106_set_authsize(struct crypto_aead *aead,
        default:
                return -EINVAL;
        }
-       crypto_aead_crt(aead)->authsize = authsize;
+
        return 0;
 }
 
@@ -984,30 +933,23 @@ static int common_rfc4106_set_authsize(struct crypto_aead *aead,
 static int rfc4106_set_authsize(struct crypto_aead *parent,
                                unsigned int authsize)
 {
-       struct aesni_rfc4106_gcm_ctx *ctx = aesni_rfc4106_gcm_ctx_get(parent);
-       struct crypto_aead *child = cryptd_aead_child(ctx->cryptd_tfm);
-       int ret;
+       struct cryptd_aead **ctx = crypto_aead_ctx(parent);
+       struct cryptd_aead *cryptd_tfm = *ctx;
 
-       ret = crypto_aead_setauthsize(child, authsize);
-       if (!ret)
-               crypto_aead_crt(parent)->authsize = authsize;
-       return ret;
+       return crypto_aead_setauthsize(&cryptd_tfm->base, authsize);
 }
 
-static int __driver_rfc4106_encrypt(struct aead_request *req)
+static int helper_rfc4106_encrypt(struct aead_request *req)
 {
        u8 one_entry_in_sg = 0;
        u8 *src, *dst, *assoc;
        __be32 counter = cpu_to_be32(1);
        struct crypto_aead *tfm = crypto_aead_reqtfm(req);
        struct aesni_rfc4106_gcm_ctx *ctx = aesni_rfc4106_gcm_ctx_get(tfm);
-       u32 key_len = ctx->aes_key_expanded.key_length;
        void *aes_ctx = &(ctx->aes_key_expanded);
        unsigned long auth_tag_len = crypto_aead_authsize(tfm);
-       u8 iv_tab[16+AESNI_ALIGN];
-       u8* iv = (u8 *) PTR_ALIGN((u8 *)iv_tab, AESNI_ALIGN);
+       u8 iv[16] __attribute__ ((__aligned__(AESNI_ALIGN)));
        struct scatter_walk src_sg_walk;
-       struct scatter_walk assoc_sg_walk;
        struct scatter_walk dst_sg_walk;
        unsigned int i;
 
@@ -1016,12 +958,6 @@ static int __driver_rfc4106_encrypt(struct aead_request *req)
        /* to 8 or 12 bytes */
        if (unlikely(req->assoclen != 8 && req->assoclen != 12))
                return -EINVAL;
-       if (unlikely(auth_tag_len != 8 && auth_tag_len != 12 && auth_tag_len != 16))
-               return -EINVAL;
-       if (unlikely(key_len != AES_KEYSIZE_128 &&
-                    key_len != AES_KEYSIZE_192 &&
-                    key_len != AES_KEYSIZE_256))
-               return -EINVAL;
 
        /* IV below built */
        for (i = 0; i < 4; i++)
@@ -1030,55 +966,57 @@ static int __driver_rfc4106_encrypt(struct aead_request *req)
                *(iv+4+i) = req->iv[i];
        *((__be32 *)(iv+12)) = counter;
 
-       if ((sg_is_last(req->src)) && (sg_is_last(req->assoc))) {
+       if (sg_is_last(req->src) &&
+           req->src->offset + req->src->length <= PAGE_SIZE &&
+           sg_is_last(req->dst) &&
+           req->dst->offset + req->dst->length <= PAGE_SIZE) {
                one_entry_in_sg = 1;
                scatterwalk_start(&src_sg_walk, req->src);
-               scatterwalk_start(&assoc_sg_walk, req->assoc);
-               src = scatterwalk_map(&src_sg_walk);
-               assoc = scatterwalk_map(&assoc_sg_walk);
+               assoc = scatterwalk_map(&src_sg_walk);
+               src = assoc + req->assoclen;
                dst = src;
                if (unlikely(req->src != req->dst)) {
                        scatterwalk_start(&dst_sg_walk, req->dst);
-                       dst = scatterwalk_map(&dst_sg_walk);
+                       dst = scatterwalk_map(&dst_sg_walk) + req->assoclen;
                }
-
        } else {
                /* Allocate memory for src, dst, assoc */
-               src = kmalloc(req->cryptlen + auth_tag_len + req->assoclen,
+               assoc = kmalloc(req->cryptlen + auth_tag_len + req->assoclen,
                        GFP_ATOMIC);
-               if (unlikely(!src))
+               if (unlikely(!assoc))
                        return -ENOMEM;
-               assoc = (src + req->cryptlen + auth_tag_len);
-               scatterwalk_map_and_copy(src, req->src, 0, req->cryptlen, 0);
-               scatterwalk_map_and_copy(assoc, req->assoc, 0,
-                                       req->assoclen, 0);
+               scatterwalk_map_and_copy(assoc, req->src, 0,
+                                        req->assoclen + req->cryptlen, 0);
+               src = assoc + req->assoclen;
                dst = src;
        }
 
+       kernel_fpu_begin();
        aesni_gcm_enc_tfm(aes_ctx, dst, src, (unsigned long)req->cryptlen, iv,
                ctx->hash_subkey, assoc, (unsigned long)req->assoclen, dst
                + ((unsigned long)req->cryptlen), auth_tag_len);
+       kernel_fpu_end();
 
        /* The authTag (aka the Integrity Check Value) needs to be written
         * back to the packet. */
        if (one_entry_in_sg) {
                if (unlikely(req->src != req->dst)) {
-                       scatterwalk_unmap(dst);
-                       scatterwalk_done(&dst_sg_walk, 0, 0);
+                       scatterwalk_unmap(dst - req->assoclen);
+                       scatterwalk_advance(&dst_sg_walk, req->dst->length);
+                       scatterwalk_done(&dst_sg_walk, 1, 0);
                }
-               scatterwalk_unmap(src);
                scatterwalk_unmap(assoc);
-               scatterwalk_done(&src_sg_walk, 0, 0);
-               scatterwalk_done(&assoc_sg_walk, 0, 0);
+               scatterwalk_advance(&src_sg_walk, req->src->length);
+               scatterwalk_done(&src_sg_walk, req->src == req->dst, 0);
        } else {
-               scatterwalk_map_and_copy(dst, req->dst, 0,
-                       req->cryptlen + auth_tag_len, 1);
-               kfree(src);
+               scatterwalk_map_and_copy(dst, req->dst, req->assoclen,
+                                        req->cryptlen + auth_tag_len, 1);
+               kfree(assoc);
        }
        return 0;
 }
 
-static int __driver_rfc4106_decrypt(struct aead_request *req)
+static int helper_rfc4106_decrypt(struct aead_request *req)
 {
        u8 one_entry_in_sg = 0;
        u8 *src, *dst, *assoc;
@@ -1087,26 +1025,16 @@ static int __driver_rfc4106_decrypt(struct aead_request *req)
        int retval = 0;
        struct crypto_aead *tfm = crypto_aead_reqtfm(req);
        struct aesni_rfc4106_gcm_ctx *ctx = aesni_rfc4106_gcm_ctx_get(tfm);
-       u32 key_len = ctx->aes_key_expanded.key_length;
        void *aes_ctx = &(ctx->aes_key_expanded);
        unsigned long auth_tag_len = crypto_aead_authsize(tfm);
-       u8 iv_and_authTag[32+AESNI_ALIGN];
-       u8 *iv = (u8 *) PTR_ALIGN((u8 *)iv_and_authTag, AESNI_ALIGN);
-       u8 *authTag = iv + 16;
+       u8 iv[16] __attribute__ ((__aligned__(AESNI_ALIGN)));
+       u8 authTag[16];
        struct scatter_walk src_sg_walk;
-       struct scatter_walk assoc_sg_walk;
        struct scatter_walk dst_sg_walk;
        unsigned int i;
 
-       if (unlikely((req->cryptlen < auth_tag_len) ||
-               (req->assoclen != 8 && req->assoclen != 12)))
+       if (unlikely(req->assoclen != 8 && req->assoclen != 12))
                return -EINVAL;
-       if (unlikely(auth_tag_len != 8 && auth_tag_len != 12 && auth_tag_len != 16))
-               return -EINVAL;
-       if (unlikely(key_len != AES_KEYSIZE_128 &&
-                    key_len != AES_KEYSIZE_192 &&
-                    key_len != AES_KEYSIZE_256))
-               return -EINVAL;
 
        /* Assuming we are supporting rfc4106 64-bit extended */
        /* sequence numbers We need to have the AAD length */
@@ -1120,33 +1048,36 @@ static int __driver_rfc4106_decrypt(struct aead_request *req)
                *(iv+4+i) = req->iv[i];
        *((__be32 *)(iv+12)) = counter;
 
-       if ((sg_is_last(req->src)) && (sg_is_last(req->assoc))) {
+       if (sg_is_last(req->src) &&
+           req->src->offset + req->src->length <= PAGE_SIZE &&
+           sg_is_last(req->dst) &&
+           req->dst->offset + req->dst->length <= PAGE_SIZE) {
                one_entry_in_sg = 1;
                scatterwalk_start(&src_sg_walk, req->src);
-               scatterwalk_start(&assoc_sg_walk, req->assoc);
-               src = scatterwalk_map(&src_sg_walk);
-               assoc = scatterwalk_map(&assoc_sg_walk);
+               assoc = scatterwalk_map(&src_sg_walk);
+               src = assoc + req->assoclen;
                dst = src;
                if (unlikely(req->src != req->dst)) {
                        scatterwalk_start(&dst_sg_walk, req->dst);
-                       dst = scatterwalk_map(&dst_sg_walk);
+                       dst = scatterwalk_map(&dst_sg_walk) + req->assoclen;
                }
 
        } else {
                /* Allocate memory for src, dst, assoc */
-               src = kmalloc(req->cryptlen + req->assoclen, GFP_ATOMIC);
-               if (!src)
+               assoc = kmalloc(req->cryptlen + req->assoclen, GFP_ATOMIC);
+               if (!assoc)
                        return -ENOMEM;
-               assoc = (src + req->cryptlen);
-               scatterwalk_map_and_copy(src, req->src, 0, req->cryptlen, 0);
-               scatterwalk_map_and_copy(assoc, req->assoc, 0,
-                       req->assoclen, 0);
+               scatterwalk_map_and_copy(assoc, req->src, 0,
+                                        req->assoclen + req->cryptlen, 0);
+               src = assoc + req->assoclen;
                dst = src;
        }
 
+       kernel_fpu_begin();
        aesni_gcm_dec_tfm(aes_ctx, dst, src, tempCipherLen, iv,
                ctx->hash_subkey, assoc, (unsigned long)req->assoclen,
                authTag, auth_tag_len);
+       kernel_fpu_end();
 
        /* Compare generated tag with passed in tag. */
        retval = crypto_memneq(src + tempCipherLen, authTag, auth_tag_len) ?
@@ -1154,90 +1085,59 @@ static int __driver_rfc4106_decrypt(struct aead_request *req)
 
        if (one_entry_in_sg) {
                if (unlikely(req->src != req->dst)) {
-                       scatterwalk_unmap(dst);
-                       scatterwalk_done(&dst_sg_walk, 0, 0);
+                       scatterwalk_unmap(dst - req->assoclen);
+                       scatterwalk_advance(&dst_sg_walk, req->dst->length);
+                       scatterwalk_done(&dst_sg_walk, 1, 0);
                }
-               scatterwalk_unmap(src);
                scatterwalk_unmap(assoc);
-               scatterwalk_done(&src_sg_walk, 0, 0);
-               scatterwalk_done(&assoc_sg_walk, 0, 0);
+               scatterwalk_advance(&src_sg_walk, req->src->length);
+               scatterwalk_done(&src_sg_walk, req->src == req->dst, 0);
        } else {
-               scatterwalk_map_and_copy(dst, req->dst, 0, tempCipherLen, 1);
-               kfree(src);
+               scatterwalk_map_and_copy(dst, req->dst, req->assoclen,
+                                        tempCipherLen, 1);
+               kfree(assoc);
        }
        return retval;
 }
 
 static int rfc4106_encrypt(struct aead_request *req)
 {
-       int ret;
        struct crypto_aead *tfm = crypto_aead_reqtfm(req);
-       struct aesni_rfc4106_gcm_ctx *ctx = aesni_rfc4106_gcm_ctx_get(tfm);
+       struct cryptd_aead **ctx = crypto_aead_ctx(tfm);
+       struct cryptd_aead *cryptd_tfm = *ctx;
+       struct aead_request *subreq = aead_request_ctx(req);
 
-       if (!irq_fpu_usable()) {
-               struct aead_request *cryptd_req =
-                       (struct aead_request *) aead_request_ctx(req);
+       aead_request_set_tfm(subreq, irq_fpu_usable() ?
+                                    cryptd_aead_child(cryptd_tfm) :
+                                    &cryptd_tfm->base);
 
-               memcpy(cryptd_req, req, sizeof(*req));
-               aead_request_set_tfm(cryptd_req, &ctx->cryptd_tfm->base);
-               ret = crypto_aead_encrypt(cryptd_req);
-       } else {
-               kernel_fpu_begin();
-               ret = __driver_rfc4106_encrypt(req);
-               kernel_fpu_end();
-       }
-       return ret;
+       aead_request_set_callback(subreq, req->base.flags,
+                                 req->base.complete, req->base.data);
+       aead_request_set_crypt(subreq, req->src, req->dst,
+                              req->cryptlen, req->iv);
+       aead_request_set_ad(subreq, req->assoclen);
+
+       return crypto_aead_encrypt(subreq);
 }
 
 static int rfc4106_decrypt(struct aead_request *req)
 {
-       int ret;
        struct crypto_aead *tfm = crypto_aead_reqtfm(req);
-       struct aesni_rfc4106_gcm_ctx *ctx = aesni_rfc4106_gcm_ctx_get(tfm);
+       struct cryptd_aead **ctx = crypto_aead_ctx(tfm);
+       struct cryptd_aead *cryptd_tfm = *ctx;
+       struct aead_request *subreq = aead_request_ctx(req);
 
-       if (!irq_fpu_usable()) {
-               struct aead_request *cryptd_req =
-                       (struct aead_request *) aead_request_ctx(req);
+       aead_request_set_tfm(subreq, irq_fpu_usable() ?
+                                    cryptd_aead_child(cryptd_tfm) :
+                                    &cryptd_tfm->base);
 
-               memcpy(cryptd_req, req, sizeof(*req));
-               aead_request_set_tfm(cryptd_req, &ctx->cryptd_tfm->base);
-               ret = crypto_aead_decrypt(cryptd_req);
-       } else {
-               kernel_fpu_begin();
-               ret = __driver_rfc4106_decrypt(req);
-               kernel_fpu_end();
-       }
-       return ret;
-}
-
-static int helper_rfc4106_encrypt(struct aead_request *req)
-{
-       int ret;
-
-       if (unlikely(!irq_fpu_usable())) {
-               WARN_ONCE(1, "__gcm-aes-aesni alg used in invalid context");
-               ret = -EINVAL;
-       } else {
-               kernel_fpu_begin();
-               ret = __driver_rfc4106_encrypt(req);
-               kernel_fpu_end();
-       }
-       return ret;
-}
-
-static int helper_rfc4106_decrypt(struct aead_request *req)
-{
-       int ret;
+       aead_request_set_callback(subreq, req->base.flags,
+                                 req->base.complete, req->base.data);
+       aead_request_set_crypt(subreq, req->src, req->dst,
+                              req->cryptlen, req->iv);
+       aead_request_set_ad(subreq, req->assoclen);
 
-       if (unlikely(!irq_fpu_usable())) {
-               WARN_ONCE(1, "__gcm-aes-aesni alg used in invalid context");
-               ret = -EINVAL;
-       } else {
-               kernel_fpu_begin();
-               ret = __driver_rfc4106_decrypt(req);
-               kernel_fpu_end();
-       }
-       return ret;
+       return crypto_aead_decrypt(subreq);
 }
 #endif
 
@@ -1410,51 +1310,6 @@ static struct crypto_alg aesni_algs[] = { {
                        .geniv          = "chainiv",
                },
        },
-}, {
-       .cra_name               = "__gcm-aes-aesni",
-       .cra_driver_name        = "__driver-gcm-aes-aesni",
-       .cra_priority           = 0,
-       .cra_flags              = CRYPTO_ALG_TYPE_AEAD | CRYPTO_ALG_INTERNAL,
-       .cra_blocksize          = 1,
-       .cra_ctxsize            = sizeof(struct aesni_rfc4106_gcm_ctx) +
-                                 AESNI_ALIGN,
-       .cra_alignmask          = 0,
-       .cra_type               = &crypto_aead_type,
-       .cra_module             = THIS_MODULE,
-       .cra_u = {
-               .aead = {
-                       .setkey         = common_rfc4106_set_key,
-                       .setauthsize    = common_rfc4106_set_authsize,
-                       .encrypt        = helper_rfc4106_encrypt,
-                       .decrypt        = helper_rfc4106_decrypt,
-                       .ivsize         = 8,
-                       .maxauthsize    = 16,
-               },
-       },
-}, {
-       .cra_name               = "rfc4106(gcm(aes))",
-       .cra_driver_name        = "rfc4106-gcm-aesni",
-       .cra_priority           = 400,
-       .cra_flags              = CRYPTO_ALG_TYPE_AEAD | CRYPTO_ALG_ASYNC,
-       .cra_blocksize          = 1,
-       .cra_ctxsize            = sizeof(struct aesni_rfc4106_gcm_ctx) +
-                                 AESNI_ALIGN,
-       .cra_alignmask          = 0,
-       .cra_type               = &crypto_nivaead_type,
-       .cra_module             = THIS_MODULE,
-       .cra_init               = rfc4106_init,
-       .cra_exit               = rfc4106_exit,
-       .cra_u = {
-               .aead = {
-                       .setkey         = rfc4106_set_key,
-                       .setauthsize    = rfc4106_set_authsize,
-                       .encrypt        = rfc4106_encrypt,
-                       .decrypt        = rfc4106_decrypt,
-                       .geniv          = "seqiv",
-                       .ivsize         = 8,
-                       .maxauthsize    = 16,
-               },
-       },
 #endif
 #if IS_ENABLED(CONFIG_CRYPTO_PCBC)
 }, {
@@ -1569,6 +1424,46 @@ static struct crypto_alg aesni_algs[] = { {
        },
 } };
 
+#ifdef CONFIG_X86_64
+static struct aead_alg aesni_aead_algs[] = { {
+       .setkey                 = common_rfc4106_set_key,
+       .setauthsize            = common_rfc4106_set_authsize,
+       .encrypt                = helper_rfc4106_encrypt,
+       .decrypt                = helper_rfc4106_decrypt,
+       .ivsize                 = 8,
+       .maxauthsize            = 16,
+       .base = {
+               .cra_name               = "__gcm-aes-aesni",
+               .cra_driver_name        = "__driver-gcm-aes-aesni",
+               .cra_flags              = CRYPTO_ALG_INTERNAL,
+               .cra_blocksize          = 1,
+               .cra_ctxsize            = sizeof(struct aesni_rfc4106_gcm_ctx),
+               .cra_alignmask          = AESNI_ALIGN - 1,
+               .cra_module             = THIS_MODULE,
+       },
+}, {
+       .init                   = rfc4106_init,
+       .exit                   = rfc4106_exit,
+       .setkey                 = rfc4106_set_key,
+       .setauthsize            = rfc4106_set_authsize,
+       .encrypt                = rfc4106_encrypt,
+       .decrypt                = rfc4106_decrypt,
+       .ivsize                 = 8,
+       .maxauthsize            = 16,
+       .base = {
+               .cra_name               = "rfc4106(gcm(aes))",
+               .cra_driver_name        = "rfc4106-gcm-aesni",
+               .cra_priority           = 400,
+               .cra_flags              = CRYPTO_ALG_ASYNC,
+               .cra_blocksize          = 1,
+               .cra_ctxsize            = sizeof(struct cryptd_aead *),
+               .cra_module             = THIS_MODULE,
+       },
+} };
+#else
+static struct aead_alg aesni_aead_algs[0];
+#endif
+
 
 static const struct x86_cpu_id aesni_cpu_id[] = {
        X86_FEATURE_MATCH(X86_FEATURE_AES),
@@ -1616,11 +1511,27 @@ static int __init aesni_init(void)
        if (err)
                return err;
 
-       return crypto_register_algs(aesni_algs, ARRAY_SIZE(aesni_algs));
+       err = crypto_register_algs(aesni_algs, ARRAY_SIZE(aesni_algs));
+       if (err)
+               goto fpu_exit;
+
+       err = crypto_register_aeads(aesni_aead_algs,
+                                   ARRAY_SIZE(aesni_aead_algs));
+       if (err)
+               goto unregister_algs;
+
+       return err;
+
+unregister_algs:
+       crypto_unregister_algs(aesni_algs, ARRAY_SIZE(aesni_algs));
+fpu_exit:
+       crypto_fpu_exit();
+       return err;
 }
 
 static void __exit aesni_exit(void)
 {
+       crypto_unregister_aeads(aesni_aead_algs, ARRAY_SIZE(aesni_aead_algs));
        crypto_unregister_algs(aesni_algs, ARRAY_SIZE(aesni_algs));
 
        crypto_fpu_exit();
index baf0ac21ace5664835ed9435207ddabc3dc0cc7a..4c65c70e628bb2776c17c106973b2f2b668890fb 100644 (file)
@@ -19,8 +19,7 @@
 #include <crypto/ctr.h>
 #include <crypto/lrw.h>
 #include <crypto/xts.h>
-#include <asm/xcr.h>
-#include <asm/xsave.h>
+#include <asm/fpu/api.h>
 #include <asm/crypto/camellia.h>
 #include <asm/crypto/glue_helper.h>
 
@@ -561,16 +560,15 @@ static struct crypto_alg cmll_algs[10] = { {
 
 static int __init camellia_aesni_init(void)
 {
-       u64 xcr0;
+       const char *feature_name;
 
        if (!cpu_has_avx2 || !cpu_has_avx || !cpu_has_aes || !cpu_has_osxsave) {
                pr_info("AVX2 or AES-NI instructions are not detected.\n");
                return -ENODEV;
        }
 
-       xcr0 = xgetbv(XCR_XFEATURE_ENABLED_MASK);
-       if ((xcr0 & (XSTATE_SSE | XSTATE_YMM)) != (XSTATE_SSE | XSTATE_YMM)) {
-               pr_info("AVX2 detected but unusable.\n");
+       if (!cpu_has_xfeatures(XSTATE_SSE | XSTATE_YMM, &feature_name)) {
+               pr_info("CPU feature '%s' is not supported.\n", feature_name);
                return -ENODEV;
        }
 
index 78818a1e73e3f62b0e7be68123fcef148e978f77..80a0e4389c9ad3f5e6e1f6d8bc5292e391801ff2 100644 (file)
@@ -19,8 +19,7 @@
 #include <crypto/ctr.h>
 #include <crypto/lrw.h>
 #include <crypto/xts.h>
-#include <asm/xcr.h>
-#include <asm/xsave.h>
+#include <asm/fpu/api.h>
 #include <asm/crypto/camellia.h>
 #include <asm/crypto/glue_helper.h>
 
@@ -553,16 +552,10 @@ static struct crypto_alg cmll_algs[10] = { {
 
 static int __init camellia_aesni_init(void)
 {
-       u64 xcr0;
+       const char *feature_name;
 
-       if (!cpu_has_avx || !cpu_has_aes || !cpu_has_osxsave) {
-               pr_info("AVX or AES-NI instructions are not detected.\n");
-               return -ENODEV;
-       }
-
-       xcr0 = xgetbv(XCR_XFEATURE_ENABLED_MASK);
-       if ((xcr0 & (XSTATE_SSE | XSTATE_YMM)) != (XSTATE_SSE | XSTATE_YMM)) {
-               pr_info("AVX detected but unusable.\n");
+       if (!cpu_has_xfeatures(XSTATE_SSE | XSTATE_YMM, &feature_name)) {
+               pr_info("CPU feature '%s' is not supported.\n", feature_name);
                return -ENODEV;
        }
 
index 236c80974457b97575a585911aabbdf72b23c4f8..be00aa48b2b5e3044ea8a397b0df37428e409447 100644 (file)
@@ -31,8 +31,7 @@
 #include <crypto/cast5.h>
 #include <crypto/cryptd.h>
 #include <crypto/ctr.h>
-#include <asm/xcr.h>
-#include <asm/xsave.h>
+#include <asm/fpu/api.h>
 #include <asm/crypto/glue_helper.h>
 
 #define CAST5_PARALLEL_BLOCKS 16
@@ -468,16 +467,10 @@ static struct crypto_alg cast5_algs[6] = { {
 
 static int __init cast5_init(void)
 {
-       u64 xcr0;
+       const char *feature_name;
 
-       if (!cpu_has_avx || !cpu_has_osxsave) {
-               pr_info("AVX instructions are not detected.\n");
-               return -ENODEV;
-       }
-
-       xcr0 = xgetbv(XCR_XFEATURE_ENABLED_MASK);
-       if ((xcr0 & (XSTATE_SSE | XSTATE_YMM)) != (XSTATE_SSE | XSTATE_YMM)) {
-               pr_info("AVX detected but unusable.\n");
+       if (!cpu_has_xfeatures(XSTATE_SSE | XSTATE_YMM, &feature_name)) {
+               pr_info("CPU feature '%s' is not supported.\n", feature_name);
                return -ENODEV;
        }
 
index f448810ca4ac1dbac428d9eadee47ecaf546c6a3..5dbba72242217541a92056068378abd86e67c378 100644 (file)
@@ -36,8 +36,7 @@
 #include <crypto/ctr.h>
 #include <crypto/lrw.h>
 #include <crypto/xts.h>
-#include <asm/xcr.h>
-#include <asm/xsave.h>
+#include <asm/fpu/api.h>
 #include <asm/crypto/glue_helper.h>
 
 #define CAST6_PARALLEL_BLOCKS 8
@@ -590,16 +589,10 @@ static struct crypto_alg cast6_algs[10] = { {
 
 static int __init cast6_init(void)
 {
-       u64 xcr0;
+       const char *feature_name;
 
-       if (!cpu_has_avx || !cpu_has_osxsave) {
-               pr_info("AVX instructions are not detected.\n");
-               return -ENODEV;
-       }
-
-       xcr0 = xgetbv(XCR_XFEATURE_ENABLED_MASK);
-       if ((xcr0 & (XSTATE_SSE | XSTATE_YMM)) != (XSTATE_SSE | XSTATE_YMM)) {
-               pr_info("AVX detected but unusable.\n");
+       if (!cpu_has_xfeatures(XSTATE_SSE | XSTATE_YMM, &feature_name)) {
+               pr_info("CPU feature '%s' is not supported.\n", feature_name);
                return -ENODEV;
        }
 
index 1937fc1d876338aa0aa9bb5fddea9e0aa3541707..07d2c6c86a5483216684970489fcba3b4478007d 100644 (file)
@@ -35,7 +35,7 @@
 
 #include <asm/cpufeature.h>
 #include <asm/cpu_device_id.h>
-#include <asm/i387.h>
+#include <asm/fpu/api.h>
 
 #define CHKSUM_BLOCK_SIZE      1
 #define CHKSUM_DIGEST_SIZE     4
index 28640c3d6af7f6172a8fe39d4553c98019614e24..81a595d75cf5959bbcae8c2096ebdf0f538bf7f9 100644 (file)
@@ -32,8 +32,7 @@
 
 #include <asm/cpufeature.h>
 #include <asm/cpu_device_id.h>
-#include <asm/i387.h>
-#include <asm/fpu-internal.h>
+#include <asm/fpu/internal.h>
 
 #define CHKSUM_BLOCK_SIZE      1
 #define CHKSUM_DIGEST_SIZE     4
index b6c67bf30fdf6704f6d83b093ee73ae7d9b77fcf..a3fcfc97a311d5b660e0dcda3be489038f231dce 100644 (file)
@@ -29,7 +29,7 @@
 #include <linux/init.h>
 #include <linux/string.h>
 #include <linux/kernel.h>
-#include <asm/i387.h>
+#include <asm/fpu/api.h>
 #include <asm/cpufeature.h>
 #include <asm/cpu_device_id.h>
 
index f368ba261739fa09be28bc02fe34cf3112099fa8..e7d679e2a0187d702bad44258a7e1dbf0def9418 100644 (file)
@@ -18,7 +18,7 @@
 #include <linux/module.h>
 #include <linux/slab.h>
 #include <linux/crypto.h>
-#include <asm/i387.h>
+#include <asm/fpu/api.h>
 
 struct crypto_fpu_ctx {
        struct crypto_blkcipher *child;
@@ -156,7 +156,7 @@ int __init crypto_fpu_init(void)
        return crypto_register_template(&crypto_fpu_tmpl);
 }
 
-void __exit crypto_fpu_exit(void)
+void crypto_fpu_exit(void)
 {
        crypto_unregister_template(&crypto_fpu_tmpl);
 }
index 2079baf06bdd3b64e7f61ec3fecf088c4bd8bc24..64d7cf1b50e112ab370ac63e59e9ff89d4d677e0 100644 (file)
@@ -19,7 +19,7 @@
 #include <crypto/cryptd.h>
 #include <crypto/gf128mul.h>
 #include <crypto/internal/hash.h>
-#include <asm/i387.h>
+#include <asm/fpu/api.h>
 #include <asm/cpu_device_id.h>
 
 #define GHASH_BLOCK_SIZE       16
index 2f63dc89e7a9ed0fa55b1ede20e7bd0ba2c060ca..7d838dc4d888f30010dfba7c21561a16bfe4e3d0 100644 (file)
@@ -20,8 +20,7 @@
 #include <crypto/lrw.h>
 #include <crypto/xts.h>
 #include <crypto/serpent.h>
-#include <asm/xcr.h>
-#include <asm/xsave.h>
+#include <asm/fpu/api.h>
 #include <asm/crypto/serpent-avx.h>
 #include <asm/crypto/glue_helper.h>
 
@@ -537,16 +536,14 @@ static struct crypto_alg srp_algs[10] = { {
 
 static int __init init(void)
 {
-       u64 xcr0;
+       const char *feature_name;
 
        if (!cpu_has_avx2 || !cpu_has_osxsave) {
                pr_info("AVX2 instructions are not detected.\n");
                return -ENODEV;
        }
-
-       xcr0 = xgetbv(XCR_XFEATURE_ENABLED_MASK);
-       if ((xcr0 & (XSTATE_SSE | XSTATE_YMM)) != (XSTATE_SSE | XSTATE_YMM)) {
-               pr_info("AVX detected but unusable.\n");
+       if (!cpu_has_xfeatures(XSTATE_SSE | XSTATE_YMM, &feature_name)) {
+               pr_info("CPU feature '%s' is not supported.\n", feature_name);
                return -ENODEV;
        }
 
index c8d478af84563e72a4ca21b9b5fd725b4c8aa248..da7dafc9b16d5cca124e4f6d01d4462f0cb92b15 100644 (file)
@@ -36,8 +36,7 @@
 #include <crypto/ctr.h>
 #include <crypto/lrw.h>
 #include <crypto/xts.h>
-#include <asm/xcr.h>
-#include <asm/xsave.h>
+#include <asm/fpu/api.h>
 #include <asm/crypto/serpent-avx.h>
 #include <asm/crypto/glue_helper.h>
 
@@ -596,16 +595,10 @@ static struct crypto_alg serpent_algs[10] = { {
 
 static int __init serpent_init(void)
 {
-       u64 xcr0;
+       const char *feature_name;
 
-       if (!cpu_has_avx || !cpu_has_osxsave) {
-               printk(KERN_INFO "AVX instructions are not detected.\n");
-               return -ENODEV;
-       }
-
-       xcr0 = xgetbv(XCR_XFEATURE_ENABLED_MASK);
-       if ((xcr0 & (XSTATE_SSE | XSTATE_YMM)) != (XSTATE_SSE | XSTATE_YMM)) {
-               printk(KERN_INFO "AVX detected but unusable.\n");
+       if (!cpu_has_xfeatures(XSTATE_SSE | XSTATE_YMM, &feature_name)) {
+               pr_info("CPU feature '%s' is not supported.\n", feature_name);
                return -ENODEV;
        }
 
index e510b1c5d690a5115231c608a51b6d4a37932d76..a841e9765bd614b17befbcf647319e2020412d88 100644 (file)
 #include <crypto/mcryptd.h>
 #include <crypto/crypto_wq.h>
 #include <asm/byteorder.h>
-#include <asm/i387.h>
-#include <asm/xcr.h>
-#include <asm/xsave.h>
 #include <linux/hardirq.h>
-#include <asm/fpu-internal.h>
+#include <asm/fpu/api.h>
 #include "sha_mb_ctx.h"
 
 #define FLUSH_INTERVAL 1000 /* in usec */
@@ -885,7 +882,8 @@ static int __init sha1_mb_mod_init(void)
                INIT_DELAYED_WORK(&cpu_state->flush, mcryptd_flusher);
                cpu_state->cpu = cpu;
                cpu_state->alg_state = &sha1_mb_alg_state;
-               cpu_state->mgr = (struct sha1_ctx_mgr *) kzalloc(sizeof(struct sha1_ctx_mgr), GFP_KERNEL);
+               cpu_state->mgr = kzalloc(sizeof(struct sha1_ctx_mgr),
+                                       GFP_KERNEL);
                if (!cpu_state->mgr)
                        goto err2;
                sha1_ctx_mgr_init(cpu_state->mgr);
index 33d1b9dc14cc751203ba72af1585f6685558b7fc..7c48e8b20848e5c890825f25db91c63616dce65d 100644 (file)
@@ -29,9 +29,7 @@
 #include <linux/types.h>
 #include <crypto/sha.h>
 #include <crypto/sha1_base.h>
-#include <asm/i387.h>
-#include <asm/xcr.h>
-#include <asm/xsave.h>
+#include <asm/fpu/api.h>
 
 
 asmlinkage void sha1_transform_ssse3(u32 *digest, const char *data,
@@ -123,15 +121,9 @@ static struct shash_alg alg = {
 #ifdef CONFIG_AS_AVX
 static bool __init avx_usable(void)
 {
-       u64 xcr0;
-
-       if (!cpu_has_avx || !cpu_has_osxsave)
-               return false;
-
-       xcr0 = xgetbv(XCR_XFEATURE_ENABLED_MASK);
-       if ((xcr0 & (XSTATE_SSE | XSTATE_YMM)) != (XSTATE_SSE | XSTATE_YMM)) {
-               pr_info("AVX detected but unusable.\n");
-
+       if (!cpu_has_xfeatures(XSTATE_SSE | XSTATE_YMM, NULL)) {
+               if (cpu_has_avx)
+                       pr_info("AVX detected but unusable.\n");
                return false;
        }
 
index ccc338881ee81886b6de43963df23d179d510b22..f8097fc0d1d1b56eb6d48389ac2778145897cfb2 100644 (file)
@@ -37,9 +37,7 @@
 #include <linux/types.h>
 #include <crypto/sha.h>
 #include <crypto/sha256_base.h>
-#include <asm/i387.h>
-#include <asm/xcr.h>
-#include <asm/xsave.h>
+#include <asm/fpu/api.h>
 #include <linux/string.h>
 
 asmlinkage void sha256_transform_ssse3(u32 *digest, const char *data,
@@ -132,15 +130,9 @@ static struct shash_alg algs[] = { {
 #ifdef CONFIG_AS_AVX
 static bool __init avx_usable(void)
 {
-       u64 xcr0;
-
-       if (!cpu_has_avx || !cpu_has_osxsave)
-               return false;
-
-       xcr0 = xgetbv(XCR_XFEATURE_ENABLED_MASK);
-       if ((xcr0 & (XSTATE_SSE | XSTATE_YMM)) != (XSTATE_SSE | XSTATE_YMM)) {
-               pr_info("AVX detected but unusable.\n");
-
+       if (!cpu_has_xfeatures(XSTATE_SSE | XSTATE_YMM, NULL)) {
+               if (cpu_has_avx)
+                       pr_info("AVX detected but unusable.\n");
                return false;
        }
 
index d9fa4c1e063ff631cc09e168c6c6747527edf334..2edad7b81870154055e86694142a66de7fbc48ff 100644 (file)
@@ -35,9 +35,7 @@
 #include <linux/types.h>
 #include <crypto/sha.h>
 #include <crypto/sha512_base.h>
-#include <asm/i387.h>
-#include <asm/xcr.h>
-#include <asm/xsave.h>
+#include <asm/fpu/api.h>
 
 #include <linux/string.h>
 
@@ -131,15 +129,9 @@ static struct shash_alg algs[] = { {
 #ifdef CONFIG_AS_AVX
 static bool __init avx_usable(void)
 {
-       u64 xcr0;
-
-       if (!cpu_has_avx || !cpu_has_osxsave)
-               return false;
-
-       xcr0 = xgetbv(XCR_XFEATURE_ENABLED_MASK);
-       if ((xcr0 & (XSTATE_SSE | XSTATE_YMM)) != (XSTATE_SSE | XSTATE_YMM)) {
-               pr_info("AVX detected but unusable.\n");
-
+       if (!cpu_has_xfeatures(XSTATE_SSE | XSTATE_YMM, NULL)) {
+               if (cpu_has_avx)
+                       pr_info("AVX detected but unusable.\n");
                return false;
        }
 
index b5e2d56518517010e6869d592aa505450965e36f..c2bd0ce718eee505272249386dffef3cc417dad8 100644 (file)
@@ -36,9 +36,7 @@
 #include <crypto/ctr.h>
 #include <crypto/lrw.h>
 #include <crypto/xts.h>
-#include <asm/i387.h>
-#include <asm/xcr.h>
-#include <asm/xsave.h>
+#include <asm/fpu/api.h>
 #include <asm/crypto/twofish.h>
 #include <asm/crypto/glue_helper.h>
 #include <crypto/scatterwalk.h>
@@ -558,16 +556,10 @@ static struct crypto_alg twofish_algs[10] = { {
 
 static int __init twofish_init(void)
 {
-       u64 xcr0;
+       const char *feature_name;
 
-       if (!cpu_has_avx || !cpu_has_osxsave) {
-               printk(KERN_INFO "AVX instructions are not detected.\n");
-               return -ENODEV;
-       }
-
-       xcr0 = xgetbv(XCR_XFEATURE_ENABLED_MASK);
-       if ((xcr0 & (XSTATE_SSE | XSTATE_YMM)) != (XSTATE_SSE | XSTATE_YMM)) {
-               printk(KERN_INFO "AVX detected but unusable.\n");
+       if (!cpu_has_xfeatures(XSTATE_SSE | XSTATE_YMM, &feature_name)) {
+               pr_info("CPU feature '%s' is not supported.\n", feature_name);
                return -ENODEV;
        }
 
diff --git a/arch/x86/entry/Makefile b/arch/x86/entry/Makefile
new file mode 100644 (file)
index 0000000..7a14497
--- /dev/null
@@ -0,0 +1,10 @@
+#
+# Makefile for the x86 low level entry code
+#
+obj-y                          := entry_$(BITS).o thunk_$(BITS).o syscall_$(BITS).o
+
+obj-y                          += vdso/
+obj-y                          += vsyscall/
+
+obj-$(CONFIG_IA32_EMULATION)   += entry_64_compat.o syscall_32.o
+
similarity index 80%
rename from arch/x86/include/asm/calling.h
rename to arch/x86/entry/calling.h
index 1c8b50edb2db3cac8f54c0c300d393b18bf24342..f4e6308c4200bcb7bb2b588aafd2b46d4ab288f9 100644 (file)
@@ -46,8 +46,6 @@ For 32-bit we have the following conventions - kernel is built with
 
 */
 
-#include <asm/dwarf2.h>
-
 #ifdef CONFIG_X86_64
 
 /*
@@ -91,28 +89,27 @@ For 32-bit we have the following conventions - kernel is built with
 #define SIZEOF_PTREGS  21*8
 
        .macro ALLOC_PT_GPREGS_ON_STACK addskip=0
-       subq    $15*8+\addskip, %rsp
-       CFI_ADJUST_CFA_OFFSET 15*8+\addskip
+       addq    $-(15*8+\addskip), %rsp
        .endm
 
        .macro SAVE_C_REGS_HELPER offset=0 rax=1 rcx=1 r8910=1 r11=1
        .if \r11
-       movq_cfi r11, 6*8+\offset
+       movq %r11, 6*8+\offset(%rsp)
        .endif
        .if \r8910
-       movq_cfi r10, 7*8+\offset
-       movq_cfi r9,  8*8+\offset
-       movq_cfi r8,  9*8+\offset
+       movq %r10, 7*8+\offset(%rsp)
+       movq %r9,  8*8+\offset(%rsp)
+       movq %r8,  9*8+\offset(%rsp)
        .endif
        .if \rax
-       movq_cfi rax, 10*8+\offset
+       movq %rax, 10*8+\offset(%rsp)
        .endif
        .if \rcx
-       movq_cfi rcx, 11*8+\offset
+       movq %rcx, 11*8+\offset(%rsp)
        .endif
-       movq_cfi rdx, 12*8+\offset
-       movq_cfi rsi, 13*8+\offset
-       movq_cfi rdi, 14*8+\offset
+       movq %rdx, 12*8+\offset(%rsp)
+       movq %rsi, 13*8+\offset(%rsp)
+       movq %rdi, 14*8+\offset(%rsp)
        .endm
        .macro SAVE_C_REGS offset=0
        SAVE_C_REGS_HELPER \offset, 1, 1, 1, 1
@@ -131,24 +128,24 @@ For 32-bit we have the following conventions - kernel is built with
        .endm
 
        .macro SAVE_EXTRA_REGS offset=0
-       movq_cfi r15, 0*8+\offset
-       movq_cfi r14, 1*8+\offset
-       movq_cfi r13, 2*8+\offset
-       movq_cfi r12, 3*8+\offset
-       movq_cfi rbp, 4*8+\offset
-       movq_cfi rbx, 5*8+\offset
+       movq %r15, 0*8+\offset(%rsp)
+       movq %r14, 1*8+\offset(%rsp)
+       movq %r13, 2*8+\offset(%rsp)
+       movq %r12, 3*8+\offset(%rsp)
+       movq %rbp, 4*8+\offset(%rsp)
+       movq %rbx, 5*8+\offset(%rsp)
        .endm
        .macro SAVE_EXTRA_REGS_RBP offset=0
-       movq_cfi rbp, 4*8+\offset
+       movq %rbp, 4*8+\offset(%rsp)
        .endm
 
        .macro RESTORE_EXTRA_REGS offset=0
-       movq_cfi_restore 0*8+\offset, r15
-       movq_cfi_restore 1*8+\offset, r14
-       movq_cfi_restore 2*8+\offset, r13
-       movq_cfi_restore 3*8+\offset, r12
-       movq_cfi_restore 4*8+\offset, rbp
-       movq_cfi_restore 5*8+\offset, rbx
+       movq 0*8+\offset(%rsp), %r15
+       movq 1*8+\offset(%rsp), %r14
+       movq 2*8+\offset(%rsp), %r13
+       movq 3*8+\offset(%rsp), %r12
+       movq 4*8+\offset(%rsp), %rbp
+       movq 5*8+\offset(%rsp), %rbx
        .endm
 
        .macro ZERO_EXTRA_REGS
@@ -162,24 +159,24 @@ For 32-bit we have the following conventions - kernel is built with
 
        .macro RESTORE_C_REGS_HELPER rstor_rax=1, rstor_rcx=1, rstor_r11=1, rstor_r8910=1, rstor_rdx=1
        .if \rstor_r11
-       movq_cfi_restore 6*8, r11
+       movq 6*8(%rsp), %r11
        .endif
        .if \rstor_r8910
-       movq_cfi_restore 7*8, r10
-       movq_cfi_restore 8*8, r9
-       movq_cfi_restore 9*8, r8
+       movq 7*8(%rsp), %r10
+       movq 8*8(%rsp), %r9
+       movq 9*8(%rsp), %r8
        .endif
        .if \rstor_rax
-       movq_cfi_restore 10*8, rax
+       movq 10*8(%rsp), %rax
        .endif
        .if \rstor_rcx
-       movq_cfi_restore 11*8, rcx
+       movq 11*8(%rsp), %rcx
        .endif
        .if \rstor_rdx
-       movq_cfi_restore 12*8, rdx
+       movq 12*8(%rsp), %rdx
        .endif
-       movq_cfi_restore 13*8, rsi
-       movq_cfi_restore 14*8, rdi
+       movq 13*8(%rsp), %rsi
+       movq 14*8(%rsp), %rdi
        .endm
        .macro RESTORE_C_REGS
        RESTORE_C_REGS_HELPER 1,1,1,1,1
@@ -204,8 +201,7 @@ For 32-bit we have the following conventions - kernel is built with
        .endm
 
        .macro REMOVE_PT_GPREGS_FROM_STACK addskip=0
-       addq $15*8+\addskip, %rsp
-       CFI_ADJUST_CFA_OFFSET -(15*8+\addskip)
+       subq $-(15*8+\addskip), %rsp
        .endm
 
        .macro icebp
@@ -224,23 +220,23 @@ For 32-bit we have the following conventions - kernel is built with
  */
 
        .macro SAVE_ALL
-       pushl_cfi_reg eax
-       pushl_cfi_reg ebp
-       pushl_cfi_reg edi
-       pushl_cfi_reg esi
-       pushl_cfi_reg edx
-       pushl_cfi_reg ecx
-       pushl_cfi_reg ebx
+       pushl %eax
+       pushl %ebp
+       pushl %edi
+       pushl %esi
+       pushl %edx
+       pushl %ecx
+       pushl %ebx
        .endm
 
        .macro RESTORE_ALL
-       popl_cfi_reg ebx
-       popl_cfi_reg ecx
-       popl_cfi_reg edx
-       popl_cfi_reg esi
-       popl_cfi_reg edi
-       popl_cfi_reg ebp
-       popl_cfi_reg eax
+       popl %ebx
+       popl %ecx
+       popl %edx
+       popl %esi
+       popl %edi
+       popl %ebp
+       popl %eax
        .endm
 
 #endif /* CONFIG_X86_64 */
diff --git a/arch/x86/entry/entry_32.S b/arch/x86/entry/entry_32.S
new file mode 100644 (file)
index 0000000..21dc60a
--- /dev/null
@@ -0,0 +1,1248 @@
+/*
+ *  Copyright (C) 1991,1992  Linus Torvalds
+ *
+ * entry_32.S contains the system-call and low-level fault and trap handling routines.
+ *
+ * Stack layout in 'syscall_exit':
+ *     ptrace needs to have all registers on the stack.
+ *     If the order here is changed, it needs to be
+ *     updated in fork.c:copy_process(), signal.c:do_signal(),
+ *     ptrace.c and ptrace.h
+ *
+ *      0(%esp) - %ebx
+ *      4(%esp) - %ecx
+ *      8(%esp) - %edx
+ *      C(%esp) - %esi
+ *     10(%esp) - %edi
+ *     14(%esp) - %ebp
+ *     18(%esp) - %eax
+ *     1C(%esp) - %ds
+ *     20(%esp) - %es
+ *     24(%esp) - %fs
+ *     28(%esp) - %gs          saved iff !CONFIG_X86_32_LAZY_GS
+ *     2C(%esp) - orig_eax
+ *     30(%esp) - %eip
+ *     34(%esp) - %cs
+ *     38(%esp) - %eflags
+ *     3C(%esp) - %oldesp
+ *     40(%esp) - %oldss
+ */
+
+#include <linux/linkage.h>
+#include <linux/err.h>
+#include <asm/thread_info.h>
+#include <asm/irqflags.h>
+#include <asm/errno.h>
+#include <asm/segment.h>
+#include <asm/smp.h>
+#include <asm/page_types.h>
+#include <asm/percpu.h>
+#include <asm/processor-flags.h>
+#include <asm/ftrace.h>
+#include <asm/irq_vectors.h>
+#include <asm/cpufeature.h>
+#include <asm/alternative-asm.h>
+#include <asm/asm.h>
+#include <asm/smap.h>
+
+/* Avoid __ASSEMBLER__'ifying <linux/audit.h> just for this.  */
+#include <linux/elf-em.h>
+#define AUDIT_ARCH_I386                (EM_386|__AUDIT_ARCH_LE)
+#define __AUDIT_ARCH_LE                0x40000000
+
+#ifndef CONFIG_AUDITSYSCALL
+# define sysenter_audit                syscall_trace_entry
+# define sysexit_audit         syscall_exit_work
+#endif
+
+       .section .entry.text, "ax"
+
+/*
+ * We use macros for low-level operations which need to be overridden
+ * for paravirtualization.  The following will never clobber any registers:
+ *   INTERRUPT_RETURN (aka. "iret")
+ *   GET_CR0_INTO_EAX (aka. "movl %cr0, %eax")
+ *   ENABLE_INTERRUPTS_SYSEXIT (aka "sti; sysexit").
+ *
+ * For DISABLE_INTERRUPTS/ENABLE_INTERRUPTS (aka "cli"/"sti"), you must
+ * specify what registers can be overwritten (CLBR_NONE, CLBR_EAX/EDX/ECX/ANY).
+ * Allowing a register to be clobbered can shrink the paravirt replacement
+ * enough to patch inline, increasing performance.
+ */
+
+#ifdef CONFIG_PREEMPT
+# define preempt_stop(clobbers)        DISABLE_INTERRUPTS(clobbers); TRACE_IRQS_OFF
+#else
+# define preempt_stop(clobbers)
+# define resume_kernel         restore_all
+#endif
+
+.macro TRACE_IRQS_IRET
+#ifdef CONFIG_TRACE_IRQFLAGS
+       testl   $X86_EFLAGS_IF, PT_EFLAGS(%esp)     # interrupts off?
+       jz      1f
+       TRACE_IRQS_ON
+1:
+#endif
+.endm
+
+/*
+ * User gs save/restore
+ *
+ * %gs is used for userland TLS and kernel only uses it for stack
+ * canary which is required to be at %gs:20 by gcc.  Read the comment
+ * at the top of stackprotector.h for more info.
+ *
+ * Local labels 98 and 99 are used.
+ */
+#ifdef CONFIG_X86_32_LAZY_GS
+
+ /* unfortunately push/pop can't be no-op */
+.macro PUSH_GS
+       pushl   $0
+.endm
+.macro POP_GS pop=0
+       addl    $(4 + \pop), %esp
+.endm
+.macro POP_GS_EX
+.endm
+
+ /* all the rest are no-op */
+.macro PTGS_TO_GS
+.endm
+.macro PTGS_TO_GS_EX
+.endm
+.macro GS_TO_REG reg
+.endm
+.macro REG_TO_PTGS reg
+.endm
+.macro SET_KERNEL_GS reg
+.endm
+
+#else  /* CONFIG_X86_32_LAZY_GS */
+
+.macro PUSH_GS
+       pushl   %gs
+.endm
+
+.macro POP_GS pop=0
+98:    popl    %gs
+  .if \pop <> 0
+       add     $\pop, %esp
+  .endif
+.endm
+.macro POP_GS_EX
+.pushsection .fixup, "ax"
+99:    movl    $0, (%esp)
+       jmp     98b
+.popsection
+       _ASM_EXTABLE(98b, 99b)
+.endm
+
+.macro PTGS_TO_GS
+98:    mov     PT_GS(%esp), %gs
+.endm
+.macro PTGS_TO_GS_EX
+.pushsection .fixup, "ax"
+99:    movl    $0, PT_GS(%esp)
+       jmp     98b
+.popsection
+       _ASM_EXTABLE(98b, 99b)
+.endm
+
+.macro GS_TO_REG reg
+       movl    %gs, \reg
+.endm
+.macro REG_TO_PTGS reg
+       movl    \reg, PT_GS(%esp)
+.endm
+.macro SET_KERNEL_GS reg
+       movl    $(__KERNEL_STACK_CANARY), \reg
+       movl    \reg, %gs
+.endm
+
+#endif /* CONFIG_X86_32_LAZY_GS */
+
+.macro SAVE_ALL
+       cld
+       PUSH_GS
+       pushl   %fs
+       pushl   %es
+       pushl   %ds
+       pushl   %eax
+       pushl   %ebp
+       pushl   %edi
+       pushl   %esi
+       pushl   %edx
+       pushl   %ecx
+       pushl   %ebx
+       movl    $(__USER_DS), %edx
+       movl    %edx, %ds
+       movl    %edx, %es
+       movl    $(__KERNEL_PERCPU), %edx
+       movl    %edx, %fs
+       SET_KERNEL_GS %edx
+.endm
+
+.macro RESTORE_INT_REGS
+       popl    %ebx
+       popl    %ecx
+       popl    %edx
+       popl    %esi
+       popl    %edi
+       popl    %ebp
+       popl    %eax
+.endm
+
+.macro RESTORE_REGS pop=0
+       RESTORE_INT_REGS
+1:     popl    %ds
+2:     popl    %es
+3:     popl    %fs
+       POP_GS \pop
+.pushsection .fixup, "ax"
+4:     movl    $0, (%esp)
+       jmp     1b
+5:     movl    $0, (%esp)
+       jmp     2b
+6:     movl    $0, (%esp)
+       jmp     3b
+.popsection
+       _ASM_EXTABLE(1b, 4b)
+       _ASM_EXTABLE(2b, 5b)
+       _ASM_EXTABLE(3b, 6b)
+       POP_GS_EX
+.endm
+
+ENTRY(ret_from_fork)
+       pushl   %eax
+       call    schedule_tail
+       GET_THREAD_INFO(%ebp)
+       popl    %eax
+       pushl   $0x0202                         # Reset kernel eflags
+       popfl
+       jmp     syscall_exit
+END(ret_from_fork)
+
+ENTRY(ret_from_kernel_thread)
+       pushl   %eax
+       call    schedule_tail
+       GET_THREAD_INFO(%ebp)
+       popl    %eax
+       pushl   $0x0202                         # Reset kernel eflags
+       popfl
+       movl    PT_EBP(%esp), %eax
+       call    *PT_EBX(%esp)
+       movl    $0, PT_EAX(%esp)
+       jmp     syscall_exit
+ENDPROC(ret_from_kernel_thread)
+
+/*
+ * Return to user mode is not as complex as all this looks,
+ * but we want the default path for a system call return to
+ * go as quickly as possible which is why some of this is
+ * less clear than it otherwise should be.
+ */
+
+       # userspace resumption stub bypassing syscall exit tracing
+       ALIGN
+ret_from_exception:
+       preempt_stop(CLBR_ANY)
+ret_from_intr:
+       GET_THREAD_INFO(%ebp)
+#ifdef CONFIG_VM86
+       movl    PT_EFLAGS(%esp), %eax           # mix EFLAGS and CS
+       movb    PT_CS(%esp), %al
+       andl    $(X86_EFLAGS_VM | SEGMENT_RPL_MASK), %eax
+#else
+       /*
+        * We can be coming here from child spawned by kernel_thread().
+        */
+       movl    PT_CS(%esp), %eax
+       andl    $SEGMENT_RPL_MASK, %eax
+#endif
+       cmpl    $USER_RPL, %eax
+       jb      resume_kernel                   # not returning to v8086 or userspace
+
+ENTRY(resume_userspace)
+       LOCKDEP_SYS_EXIT
+       DISABLE_INTERRUPTS(CLBR_ANY)            # make sure we don't miss an interrupt
+                                               # setting need_resched or sigpending
+                                               # between sampling and the iret
+       TRACE_IRQS_OFF
+       movl    TI_flags(%ebp), %ecx
+       andl    $_TIF_WORK_MASK, %ecx           # is there any work to be done on
+                                               # int/exception return?
+       jne     work_pending
+       jmp     restore_all
+END(ret_from_exception)
+
+#ifdef CONFIG_PREEMPT
+ENTRY(resume_kernel)
+       DISABLE_INTERRUPTS(CLBR_ANY)
+need_resched:
+       cmpl    $0, PER_CPU_VAR(__preempt_count)
+       jnz     restore_all
+       testl   $X86_EFLAGS_IF, PT_EFLAGS(%esp) # interrupts off (exception path) ?
+       jz      restore_all
+       call    preempt_schedule_irq
+       jmp     need_resched
+END(resume_kernel)
+#endif
+
+/*
+ * SYSENTER_RETURN points to after the SYSENTER instruction
+ * in the vsyscall page.  See vsyscall-sysentry.S, which defines
+ * the symbol.
+ */
+
+       # SYSENTER  call handler stub
+ENTRY(entry_SYSENTER_32)
+       movl    TSS_sysenter_sp0(%esp), %esp
+sysenter_past_esp:
+       /*
+        * Interrupts are disabled here, but we can't trace it until
+        * enough kernel state to call TRACE_IRQS_OFF can be called - but
+        * we immediately enable interrupts at that point anyway.
+        */
+       pushl   $__USER_DS
+       pushl   %ebp
+       pushfl
+       orl     $X86_EFLAGS_IF, (%esp)
+       pushl   $__USER_CS
+       /*
+        * Push current_thread_info()->sysenter_return to the stack.
+        * A tiny bit of offset fixup is necessary: TI_sysenter_return
+        * is relative to thread_info, which is at the bottom of the
+        * kernel stack page.  4*4 means the 4 words pushed above;
+        * TOP_OF_KERNEL_STACK_PADDING takes us to the top of the stack;
+        * and THREAD_SIZE takes us to the bottom.
+        */
+       pushl   ((TI_sysenter_return) - THREAD_SIZE + TOP_OF_KERNEL_STACK_PADDING + 4*4)(%esp)
+
+       pushl   %eax
+       SAVE_ALL
+       ENABLE_INTERRUPTS(CLBR_NONE)
+
+/*
+ * Load the potential sixth argument from user stack.
+ * Careful about security.
+ */
+       cmpl    $__PAGE_OFFSET-3, %ebp
+       jae     syscall_fault
+       ASM_STAC
+1:     movl    (%ebp), %ebp
+       ASM_CLAC
+       movl    %ebp, PT_EBP(%esp)
+       _ASM_EXTABLE(1b, syscall_fault)
+
+       GET_THREAD_INFO(%ebp)
+
+       testl   $_TIF_WORK_SYSCALL_ENTRY, TI_flags(%ebp)
+       jnz     sysenter_audit
+sysenter_do_call:
+       cmpl    $(NR_syscalls), %eax
+       jae     sysenter_badsys
+       call    *sys_call_table(, %eax, 4)
+sysenter_after_call:
+       movl    %eax, PT_EAX(%esp)
+       LOCKDEP_SYS_EXIT
+       DISABLE_INTERRUPTS(CLBR_ANY)
+       TRACE_IRQS_OFF
+       movl    TI_flags(%ebp), %ecx
+       testl   $_TIF_ALLWORK_MASK, %ecx
+       jnz     sysexit_audit
+sysenter_exit:
+/* if something modifies registers it must also disable sysexit */
+       movl    PT_EIP(%esp), %edx
+       movl    PT_OLDESP(%esp), %ecx
+       xorl    %ebp, %ebp
+       TRACE_IRQS_ON
+1:     mov     PT_FS(%esp), %fs
+       PTGS_TO_GS
+       ENABLE_INTERRUPTS_SYSEXIT
+
+#ifdef CONFIG_AUDITSYSCALL
+sysenter_audit:
+       testl   $(_TIF_WORK_SYSCALL_ENTRY & ~_TIF_SYSCALL_AUDIT), TI_flags(%ebp)
+       jnz     syscall_trace_entry
+       /* movl PT_EAX(%esp), %eax already set, syscall number: 1st arg to audit */
+       movl    PT_EBX(%esp), %edx              /* ebx/a0: 2nd arg to audit */
+       /* movl PT_ECX(%esp), %ecx already set, a1: 3nd arg to audit */
+       pushl   PT_ESI(%esp)                    /* a3: 5th arg */
+       pushl   PT_EDX+4(%esp)                  /* a2: 4th arg */
+       call    __audit_syscall_entry
+       popl    %ecx                            /* get that remapped edx off the stack */
+       popl    %ecx                            /* get that remapped esi off the stack */
+       movl    PT_EAX(%esp), %eax              /* reload syscall number */
+       jmp     sysenter_do_call
+
+sysexit_audit:
+       testl   $(_TIF_ALLWORK_MASK & ~_TIF_SYSCALL_AUDIT), %ecx
+       jnz     syscall_exit_work
+       TRACE_IRQS_ON
+       ENABLE_INTERRUPTS(CLBR_ANY)
+       movl    %eax, %edx                      /* second arg, syscall return value */
+       cmpl    $-MAX_ERRNO, %eax               /* is it an error ? */
+       setbe %al                               /* 1 if so, 0 if not */
+       movzbl %al, %eax                        /* zero-extend that */
+       call    __audit_syscall_exit
+       DISABLE_INTERRUPTS(CLBR_ANY)
+       TRACE_IRQS_OFF
+       movl    TI_flags(%ebp), %ecx
+       testl   $(_TIF_ALLWORK_MASK & ~_TIF_SYSCALL_AUDIT), %ecx
+       jnz     syscall_exit_work
+       movl    PT_EAX(%esp), %eax              /* reload syscall return value */
+       jmp     sysenter_exit
+#endif
+
+.pushsection .fixup, "ax"
+2:     movl    $0, PT_FS(%esp)
+       jmp     1b
+.popsection
+       _ASM_EXTABLE(1b, 2b)
+       PTGS_TO_GS_EX
+ENDPROC(entry_SYSENTER_32)
+
+       # system call handler stub
+ENTRY(entry_INT80_32)
+       ASM_CLAC
+       pushl   %eax                            # save orig_eax
+       SAVE_ALL
+       GET_THREAD_INFO(%ebp)
+                                               # system call tracing in operation / emulation
+       testl   $_TIF_WORK_SYSCALL_ENTRY, TI_flags(%ebp)
+       jnz     syscall_trace_entry
+       cmpl    $(NR_syscalls), %eax
+       jae     syscall_badsys
+syscall_call:
+       call    *sys_call_table(, %eax, 4)
+syscall_after_call:
+       movl    %eax, PT_EAX(%esp)              # store the return value
+syscall_exit:
+       LOCKDEP_SYS_EXIT
+       DISABLE_INTERRUPTS(CLBR_ANY)            # make sure we don't miss an interrupt
+                                               # setting need_resched or sigpending
+                                               # between sampling and the iret
+       TRACE_IRQS_OFF
+       movl    TI_flags(%ebp), %ecx
+       testl   $_TIF_ALLWORK_MASK, %ecx        # current->work
+       jnz     syscall_exit_work
+
+restore_all:
+       TRACE_IRQS_IRET
+restore_all_notrace:
+#ifdef CONFIG_X86_ESPFIX32
+       movl    PT_EFLAGS(%esp), %eax           # mix EFLAGS, SS and CS
+       /*
+        * Warning: PT_OLDSS(%esp) contains the wrong/random values if we
+        * are returning to the kernel.
+        * See comments in process.c:copy_thread() for details.
+        */
+       movb    PT_OLDSS(%esp), %ah
+       movb    PT_CS(%esp), %al
+       andl    $(X86_EFLAGS_VM | (SEGMENT_TI_MASK << 8) | SEGMENT_RPL_MASK), %eax
+       cmpl    $((SEGMENT_LDT << 8) | USER_RPL), %eax
+       je ldt_ss                               # returning to user-space with LDT SS
+#endif
+restore_nocheck:
+       RESTORE_REGS 4                          # skip orig_eax/error_code
+irq_return:
+       INTERRUPT_RETURN
+.section .fixup, "ax"
+ENTRY(iret_exc )
+       pushl   $0                              # no error code
+       pushl   $do_iret_error
+       jmp     error_code
+.previous
+       _ASM_EXTABLE(irq_return, iret_exc)
+
+#ifdef CONFIG_X86_ESPFIX32
+ldt_ss:
+#ifdef CONFIG_PARAVIRT
+       /*
+        * The kernel can't run on a non-flat stack if paravirt mode
+        * is active.  Rather than try to fixup the high bits of
+        * ESP, bypass this code entirely.  This may break DOSemu
+        * and/or Wine support in a paravirt VM, although the option
+        * is still available to implement the setting of the high
+        * 16-bits in the INTERRUPT_RETURN paravirt-op.
+        */
+       cmpl    $0, pv_info+PARAVIRT_enabled
+       jne     restore_nocheck
+#endif
+
+/*
+ * Setup and switch to ESPFIX stack
+ *
+ * We're returning to userspace with a 16 bit stack. The CPU will not
+ * restore the high word of ESP for us on executing iret... This is an
+ * "official" bug of all the x86-compatible CPUs, which we can work
+ * around to make dosemu and wine happy. We do this by preloading the
+ * high word of ESP with the high word of the userspace ESP while
+ * compensating for the offset by changing to the ESPFIX segment with
+ * a base address that matches for the difference.
+ */
+#define GDT_ESPFIX_SS PER_CPU_VAR(gdt_page) + (GDT_ENTRY_ESPFIX_SS * 8)
+       mov     %esp, %edx                      /* load kernel esp */
+       mov     PT_OLDESP(%esp), %eax           /* load userspace esp */
+       mov     %dx, %ax                        /* eax: new kernel esp */
+       sub     %eax, %edx                      /* offset (low word is 0) */
+       shr     $16, %edx
+       mov     %dl, GDT_ESPFIX_SS + 4          /* bits 16..23 */
+       mov     %dh, GDT_ESPFIX_SS + 7          /* bits 24..31 */
+       pushl   $__ESPFIX_SS
+       pushl   %eax                            /* new kernel esp */
+       /*
+        * Disable interrupts, but do not irqtrace this section: we
+        * will soon execute iret and the tracer was already set to
+        * the irqstate after the IRET:
+        */
+       DISABLE_INTERRUPTS(CLBR_EAX)
+       lss     (%esp), %esp                    /* switch to espfix segment */
+       jmp     restore_nocheck
+#endif
+ENDPROC(entry_INT80_32)
+
+       # perform work that needs to be done immediately before resumption
+       ALIGN
+work_pending:
+       testb   $_TIF_NEED_RESCHED, %cl
+       jz      work_notifysig
+work_resched:
+       call    schedule
+       LOCKDEP_SYS_EXIT
+       DISABLE_INTERRUPTS(CLBR_ANY)            # make sure we don't miss an interrupt
+                                               # setting need_resched or sigpending
+                                               # between sampling and the iret
+       TRACE_IRQS_OFF
+       movl    TI_flags(%ebp), %ecx
+       andl    $_TIF_WORK_MASK, %ecx           # is there any work to be done other
+                                               # than syscall tracing?
+       jz      restore_all
+       testb   $_TIF_NEED_RESCHED, %cl
+       jnz     work_resched
+
+work_notifysig:                                        # deal with pending signals and
+                                               # notify-resume requests
+#ifdef CONFIG_VM86
+       testl   $X86_EFLAGS_VM, PT_EFLAGS(%esp)
+       movl    %esp, %eax
+       jnz     work_notifysig_v86              # returning to kernel-space or
+                                               # vm86-space
+1:
+#else
+       movl    %esp, %eax
+#endif
+       TRACE_IRQS_ON
+       ENABLE_INTERRUPTS(CLBR_NONE)
+       movb    PT_CS(%esp), %bl
+       andb    $SEGMENT_RPL_MASK, %bl
+       cmpb    $USER_RPL, %bl
+       jb      resume_kernel
+       xorl    %edx, %edx
+       call    do_notify_resume
+       jmp     resume_userspace
+
+#ifdef CONFIG_VM86
+       ALIGN
+work_notifysig_v86:
+       pushl   %ecx                            # save ti_flags for do_notify_resume
+       call    save_v86_state                  # %eax contains pt_regs pointer
+       popl    %ecx
+       movl    %eax, %esp
+       jmp     1b
+#endif
+END(work_pending)
+
+       # perform syscall exit tracing
+       ALIGN
+syscall_trace_entry:
+       movl    $-ENOSYS, PT_EAX(%esp)
+       movl    %esp, %eax
+       call    syscall_trace_enter
+       /* What it returned is what we'll actually use.  */
+       cmpl    $(NR_syscalls), %eax
+       jnae    syscall_call
+       jmp     syscall_exit
+END(syscall_trace_entry)
+
+       # perform syscall exit tracing
+       ALIGN
+syscall_exit_work:
+       testl   $_TIF_WORK_SYSCALL_EXIT, %ecx
+       jz      work_pending
+       TRACE_IRQS_ON
+       ENABLE_INTERRUPTS(CLBR_ANY)             # could let syscall_trace_leave() call
+                                               # schedule() instead
+       movl    %esp, %eax
+       call    syscall_trace_leave
+       jmp     resume_userspace
+END(syscall_exit_work)
+
+syscall_fault:
+       ASM_CLAC
+       GET_THREAD_INFO(%ebp)
+       movl    $-EFAULT, PT_EAX(%esp)
+       jmp     resume_userspace
+END(syscall_fault)
+
+syscall_badsys:
+       movl    $-ENOSYS, %eax
+       jmp     syscall_after_call
+END(syscall_badsys)
+
+sysenter_badsys:
+       movl    $-ENOSYS, %eax
+       jmp     sysenter_after_call
+END(sysenter_badsys)
+
+.macro FIXUP_ESPFIX_STACK
+/*
+ * Switch back for ESPFIX stack to the normal zerobased stack
+ *
+ * We can't call C functions using the ESPFIX stack. This code reads
+ * the high word of the segment base from the GDT and swiches to the
+ * normal stack and adjusts ESP with the matching offset.
+ */
+#ifdef CONFIG_X86_ESPFIX32
+       /* fixup the stack */
+       mov     GDT_ESPFIX_SS + 4, %al /* bits 16..23 */
+       mov     GDT_ESPFIX_SS + 7, %ah /* bits 24..31 */
+       shl     $16, %eax
+       addl    %esp, %eax                      /* the adjusted stack pointer */
+       pushl   $__KERNEL_DS
+       pushl   %eax
+       lss     (%esp), %esp                    /* switch to the normal stack segment */
+#endif
+.endm
+.macro UNWIND_ESPFIX_STACK
+#ifdef CONFIG_X86_ESPFIX32
+       movl    %ss, %eax
+       /* see if on espfix stack */
+       cmpw    $__ESPFIX_SS, %ax
+       jne     27f
+       movl    $__KERNEL_DS, %eax
+       movl    %eax, %ds
+       movl    %eax, %es
+       /* switch to normal stack */
+       FIXUP_ESPFIX_STACK
+27:
+#endif
+.endm
+
+/*
+ * Build the entry stubs with some assembler magic.
+ * We pack 1 stub into every 8-byte block.
+ */
+       .align 8
+ENTRY(irq_entries_start)
+    vector=FIRST_EXTERNAL_VECTOR
+    .rept (FIRST_SYSTEM_VECTOR - FIRST_EXTERNAL_VECTOR)
+       pushl   $(~vector+0x80)                 /* Note: always in signed byte range */
+    vector=vector+1
+       jmp     common_interrupt
+       .align  8
+    .endr
+END(irq_entries_start)
+
+/*
+ * the CPU automatically disables interrupts when executing an IRQ vector,
+ * so IRQ-flags tracing has to follow that:
+ */
+       .p2align CONFIG_X86_L1_CACHE_SHIFT
+common_interrupt:
+       ASM_CLAC
+       addl    $-0x80, (%esp)                  /* Adjust vector into the [-256, -1] range */
+       SAVE_ALL
+       TRACE_IRQS_OFF
+       movl    %esp, %eax
+       call    do_IRQ
+       jmp     ret_from_intr
+ENDPROC(common_interrupt)
+
+#define BUILD_INTERRUPT3(name, nr, fn) \
+ENTRY(name)                            \
+       ASM_CLAC;                       \
+       pushl   $~(nr);                 \
+       SAVE_ALL;                       \
+       TRACE_IRQS_OFF                  \
+       movl    %esp, %eax;             \
+       call    fn;                     \
+       jmp     ret_from_intr;          \
+ENDPROC(name)
+
+
+#ifdef CONFIG_TRACING
+# define TRACE_BUILD_INTERRUPT(name, nr)       BUILD_INTERRUPT3(trace_##name, nr, smp_trace_##name)
+#else
+# define TRACE_BUILD_INTERRUPT(name, nr)
+#endif
+
+#define BUILD_INTERRUPT(name, nr)              \
+       BUILD_INTERRUPT3(name, nr, smp_##name); \
+       TRACE_BUILD_INTERRUPT(name, nr)
+
+/* The include is where all of the SMP etc. interrupts come from */
+#include <asm/entry_arch.h>
+
+ENTRY(coprocessor_error)
+       ASM_CLAC
+       pushl   $0
+       pushl   $do_coprocessor_error
+       jmp     error_code
+END(coprocessor_error)
+
+ENTRY(simd_coprocessor_error)
+       ASM_CLAC
+       pushl   $0
+#ifdef CONFIG_X86_INVD_BUG
+       /* AMD 486 bug: invd from userspace calls exception 19 instead of #GP */
+       ALTERNATIVE "pushl      $do_general_protection",        \
+                   "pushl      $do_simd_coprocessor_error",    \
+                   X86_FEATURE_XMM
+#else
+       pushl   $do_simd_coprocessor_error
+#endif
+       jmp     error_code
+END(simd_coprocessor_error)
+
+ENTRY(device_not_available)
+       ASM_CLAC
+       pushl   $-1                             # mark this as an int
+       pushl   $do_device_not_available
+       jmp     error_code
+END(device_not_available)
+
+#ifdef CONFIG_PARAVIRT
+ENTRY(native_iret)
+       iret
+       _ASM_EXTABLE(native_iret, iret_exc)
+END(native_iret)
+
+ENTRY(native_irq_enable_sysexit)
+       sti
+       sysexit
+END(native_irq_enable_sysexit)
+#endif
+
+ENTRY(overflow)
+       ASM_CLAC
+       pushl   $0
+       pushl   $do_overflow
+       jmp     error_code
+END(overflow)
+
+ENTRY(bounds)
+       ASM_CLAC
+       pushl   $0
+       pushl   $do_bounds
+       jmp     error_code
+END(bounds)
+
+ENTRY(invalid_op)
+       ASM_CLAC
+       pushl   $0
+       pushl   $do_invalid_op
+       jmp     error_code
+END(invalid_op)
+
+ENTRY(coprocessor_segment_overrun)
+       ASM_CLAC
+       pushl   $0
+       pushl   $do_coprocessor_segment_overrun
+       jmp     error_code
+END(coprocessor_segment_overrun)
+
+ENTRY(invalid_TSS)
+       ASM_CLAC
+       pushl   $do_invalid_TSS
+       jmp     error_code
+END(invalid_TSS)
+
+ENTRY(segment_not_present)
+       ASM_CLAC
+       pushl   $do_segment_not_present
+       jmp     error_code
+END(segment_not_present)
+
+ENTRY(stack_segment)
+       ASM_CLAC
+       pushl   $do_stack_segment
+       jmp     error_code
+END(stack_segment)
+
+ENTRY(alignment_check)
+       ASM_CLAC
+       pushl   $do_alignment_check
+       jmp     error_code
+END(alignment_check)
+
+ENTRY(divide_error)
+       ASM_CLAC
+       pushl   $0                              # no error code
+       pushl   $do_divide_error
+       jmp     error_code
+END(divide_error)
+
+#ifdef CONFIG_X86_MCE
+ENTRY(machine_check)
+       ASM_CLAC
+       pushl   $0
+       pushl   machine_check_vector
+       jmp     error_code
+END(machine_check)
+#endif
+
+ENTRY(spurious_interrupt_bug)
+       ASM_CLAC
+       pushl   $0
+       pushl   $do_spurious_interrupt_bug
+       jmp     error_code
+END(spurious_interrupt_bug)
+
+#ifdef CONFIG_XEN
+/*
+ * Xen doesn't set %esp to be precisely what the normal SYSENTER
+ * entry point expects, so fix it up before using the normal path.
+ */
+ENTRY(xen_sysenter_target)
+       addl    $5*4, %esp                      /* remove xen-provided frame */
+       jmp     sysenter_past_esp
+
+ENTRY(xen_hypervisor_callback)
+       pushl   $-1                             /* orig_ax = -1 => not a system call */
+       SAVE_ALL
+       TRACE_IRQS_OFF
+
+       /*
+        * Check to see if we got the event in the critical
+        * region in xen_iret_direct, after we've reenabled
+        * events and checked for pending events.  This simulates
+        * iret instruction's behaviour where it delivers a
+        * pending interrupt when enabling interrupts:
+        */
+       movl    PT_EIP(%esp), %eax
+       cmpl    $xen_iret_start_crit, %eax
+       jb      1f
+       cmpl    $xen_iret_end_crit, %eax
+       jae     1f
+
+       jmp     xen_iret_crit_fixup
+
+ENTRY(xen_do_upcall)
+1:     mov     %esp, %eax
+       call    xen_evtchn_do_upcall
+#ifndef CONFIG_PREEMPT
+       call    xen_maybe_preempt_hcall
+#endif
+       jmp     ret_from_intr
+ENDPROC(xen_hypervisor_callback)
+
+/*
+ * Hypervisor uses this for application faults while it executes.
+ * We get here for two reasons:
+ *  1. Fault while reloading DS, ES, FS or GS
+ *  2. Fault while executing IRET
+ * Category 1 we fix up by reattempting the load, and zeroing the segment
+ * register if the load fails.
+ * Category 2 we fix up by jumping to do_iret_error. We cannot use the
+ * normal Linux return path in this case because if we use the IRET hypercall
+ * to pop the stack frame we end up in an infinite loop of failsafe callbacks.
+ * We distinguish between categories by maintaining a status value in EAX.
+ */
+ENTRY(xen_failsafe_callback)
+       pushl   %eax
+       movl    $1, %eax
+1:     mov     4(%esp), %ds
+2:     mov     8(%esp), %es
+3:     mov     12(%esp), %fs
+4:     mov     16(%esp), %gs
+       /* EAX == 0 => Category 1 (Bad segment)
+          EAX != 0 => Category 2 (Bad IRET) */
+       testl   %eax, %eax
+       popl    %eax
+       lea     16(%esp), %esp
+       jz      5f
+       jmp     iret_exc
+5:     pushl   $-1                             /* orig_ax = -1 => not a system call */
+       SAVE_ALL
+       jmp     ret_from_exception
+
+.section .fixup, "ax"
+6:     xorl    %eax, %eax
+       movl    %eax, 4(%esp)
+       jmp     1b
+7:     xorl    %eax, %eax
+       movl    %eax, 8(%esp)
+       jmp     2b
+8:     xorl    %eax, %eax
+       movl    %eax, 12(%esp)
+       jmp     3b
+9:     xorl    %eax, %eax
+       movl    %eax, 16(%esp)
+       jmp     4b
+.previous
+       _ASM_EXTABLE(1b, 6b)
+       _ASM_EXTABLE(2b, 7b)
+       _ASM_EXTABLE(3b, 8b)
+       _ASM_EXTABLE(4b, 9b)
+ENDPROC(xen_failsafe_callback)
+
+BUILD_INTERRUPT3(xen_hvm_callback_vector, HYPERVISOR_CALLBACK_VECTOR,
+               xen_evtchn_do_upcall)
+
+#endif /* CONFIG_XEN */
+
+#if IS_ENABLED(CONFIG_HYPERV)
+
+BUILD_INTERRUPT3(hyperv_callback_vector, HYPERVISOR_CALLBACK_VECTOR,
+       hyperv_vector_handler)
+
+#endif /* CONFIG_HYPERV */
+
+#ifdef CONFIG_FUNCTION_TRACER
+#ifdef CONFIG_DYNAMIC_FTRACE
+
+ENTRY(mcount)
+       ret
+END(mcount)
+
+ENTRY(ftrace_caller)
+       pushl   %eax
+       pushl   %ecx
+       pushl   %edx
+       pushl   $0                              /* Pass NULL as regs pointer */
+       movl    4*4(%esp), %eax
+       movl    0x4(%ebp), %edx
+       movl    function_trace_op, %ecx
+       subl    $MCOUNT_INSN_SIZE, %eax
+
+.globl ftrace_call
+ftrace_call:
+       call    ftrace_stub
+
+       addl    $4, %esp                        /* skip NULL pointer */
+       popl    %edx
+       popl    %ecx
+       popl    %eax
+ftrace_ret:
+#ifdef CONFIG_FUNCTION_GRAPH_TRACER
+.globl ftrace_graph_call
+ftrace_graph_call:
+       jmp     ftrace_stub
+#endif
+
+.globl ftrace_stub
+ftrace_stub:
+       ret
+END(ftrace_caller)
+
+ENTRY(ftrace_regs_caller)
+       pushf   /* push flags before compare (in cs location) */
+
+       /*
+        * i386 does not save SS and ESP when coming from kernel.
+        * Instead, to get sp, &regs->sp is used (see ptrace.h).
+        * Unfortunately, that means eflags must be at the same location
+        * as the current return ip is. We move the return ip into the
+        * ip location, and move flags into the return ip location.
+        */
+       pushl   4(%esp)                         /* save return ip into ip slot */
+
+       pushl   $0                              /* Load 0 into orig_ax */
+       pushl   %gs
+       pushl   %fs
+       pushl   %es
+       pushl   %ds
+       pushl   %eax
+       pushl   %ebp
+       pushl   %edi
+       pushl   %esi
+       pushl   %edx
+       pushl   %ecx
+       pushl   %ebx
+
+       movl    13*4(%esp), %eax                /* Get the saved flags */
+       movl    %eax, 14*4(%esp)                /* Move saved flags into regs->flags location */
+                                               /* clobbering return ip */
+       movl    $__KERNEL_CS, 13*4(%esp)
+
+       movl    12*4(%esp), %eax                /* Load ip (1st parameter) */
+       subl    $MCOUNT_INSN_SIZE, %eax         /* Adjust ip */
+       movl    0x4(%ebp), %edx                 /* Load parent ip (2nd parameter) */
+       movl    function_trace_op, %ecx         /* Save ftrace_pos in 3rd parameter */
+       pushl   %esp                            /* Save pt_regs as 4th parameter */
+
+GLOBAL(ftrace_regs_call)
+       call    ftrace_stub
+
+       addl    $4, %esp                        /* Skip pt_regs */
+       movl    14*4(%esp), %eax                /* Move flags back into cs */
+       movl    %eax, 13*4(%esp)                /* Needed to keep addl  from modifying flags */
+       movl    12*4(%esp), %eax                /* Get return ip from regs->ip */
+       movl    %eax, 14*4(%esp)                /* Put return ip back for ret */
+
+       popl    %ebx
+       popl    %ecx
+       popl    %edx
+       popl    %esi
+       popl    %edi
+       popl    %ebp
+       popl    %eax
+       popl    %ds
+       popl    %es
+       popl    %fs
+       popl    %gs
+       addl    $8, %esp                        /* Skip orig_ax and ip */
+       popf                                    /* Pop flags at end (no addl to corrupt flags) */
+       jmp     ftrace_ret
+
+       popf
+       jmp     ftrace_stub
+#else /* ! CONFIG_DYNAMIC_FTRACE */
+
+ENTRY(mcount)
+       cmpl    $__PAGE_OFFSET, %esp
+       jb      ftrace_stub                     /* Paging not enabled yet? */
+
+       cmpl    $ftrace_stub, ftrace_trace_function
+       jnz     trace
+#ifdef CONFIG_FUNCTION_GRAPH_TRACER
+       cmpl    $ftrace_stub, ftrace_graph_return
+       jnz     ftrace_graph_caller
+
+       cmpl    $ftrace_graph_entry_stub, ftrace_graph_entry
+       jnz     ftrace_graph_caller
+#endif
+.globl ftrace_stub
+ftrace_stub:
+       ret
+
+       /* taken from glibc */
+trace:
+       pushl   %eax
+       pushl   %ecx
+       pushl   %edx
+       movl    0xc(%esp), %eax
+       movl    0x4(%ebp), %edx
+       subl    $MCOUNT_INSN_SIZE, %eax
+
+       call    *ftrace_trace_function
+
+       popl    %edx
+       popl    %ecx
+       popl    %eax
+       jmp     ftrace_stub
+END(mcount)
+#endif /* CONFIG_DYNAMIC_FTRACE */
+#endif /* CONFIG_FUNCTION_TRACER */
+
+#ifdef CONFIG_FUNCTION_GRAPH_TRACER
+ENTRY(ftrace_graph_caller)
+       pushl   %eax
+       pushl   %ecx
+       pushl   %edx
+       movl    0xc(%esp), %eax
+       lea     0x4(%ebp), %edx
+       movl    (%ebp), %ecx
+       subl    $MCOUNT_INSN_SIZE, %eax
+       call    prepare_ftrace_return
+       popl    %edx
+       popl    %ecx
+       popl    %eax
+       ret
+END(ftrace_graph_caller)
+
+.globl return_to_handler
+return_to_handler:
+       pushl   %eax
+       pushl   %edx
+       movl    %ebp, %eax
+       call    ftrace_return_to_handler
+       movl    %eax, %ecx
+       popl    %edx
+       popl    %eax
+       jmp     *%ecx
+#endif
+
+#ifdef CONFIG_TRACING
+ENTRY(trace_page_fault)
+       ASM_CLAC
+       pushl   $trace_do_page_fault
+       jmp     error_code
+END(trace_page_fault)
+#endif
+
+ENTRY(page_fault)
+       ASM_CLAC
+       pushl   $do_page_fault
+       ALIGN
+error_code:
+       /* the function address is in %gs's slot on the stack */
+       pushl   %fs
+       pushl   %es
+       pushl   %ds
+       pushl   %eax
+       pushl   %ebp
+       pushl   %edi
+       pushl   %esi
+       pushl   %edx
+       pushl   %ecx
+       pushl   %ebx
+       cld
+       movl    $(__KERNEL_PERCPU), %ecx
+       movl    %ecx, %fs
+       UNWIND_ESPFIX_STACK
+       GS_TO_REG %ecx
+       movl    PT_GS(%esp), %edi               # get the function address
+       movl    PT_ORIG_EAX(%esp), %edx         # get the error code
+       movl    $-1, PT_ORIG_EAX(%esp)          # no syscall to restart
+       REG_TO_PTGS %ecx
+       SET_KERNEL_GS %ecx
+       movl    $(__USER_DS), %ecx
+       movl    %ecx, %ds
+       movl    %ecx, %es
+       TRACE_IRQS_OFF
+       movl    %esp, %eax                      # pt_regs pointer
+       call    *%edi
+       jmp     ret_from_exception
+END(page_fault)
+
+/*
+ * Debug traps and NMI can happen at the one SYSENTER instruction
+ * that sets up the real kernel stack. Check here, since we can't
+ * allow the wrong stack to be used.
+ *
+ * "TSS_sysenter_sp0+12" is because the NMI/debug handler will have
+ * already pushed 3 words if it hits on the sysenter instruction:
+ * eflags, cs and eip.
+ *
+ * We just load the right stack, and push the three (known) values
+ * by hand onto the new stack - while updating the return eip past
+ * the instruction that would have done it for sysenter.
+ */
+.macro FIX_STACK offset ok label
+       cmpw    $__KERNEL_CS, 4(%esp)
+       jne     \ok
+\label:
+       movl    TSS_sysenter_sp0 + \offset(%esp), %esp
+       pushfl
+       pushl   $__KERNEL_CS
+       pushl   $sysenter_past_esp
+.endm
+
+ENTRY(debug)
+       ASM_CLAC
+       cmpl    $entry_SYSENTER_32, (%esp)
+       jne     debug_stack_correct
+       FIX_STACK 12, debug_stack_correct, debug_esp_fix_insn
+debug_stack_correct:
+       pushl   $-1                             # mark this as an int
+       SAVE_ALL
+       TRACE_IRQS_OFF
+       xorl    %edx, %edx                      # error code 0
+       movl    %esp, %eax                      # pt_regs pointer
+       call    do_debug
+       jmp     ret_from_exception
+END(debug)
+
+/*
+ * NMI is doubly nasty. It can happen _while_ we're handling
+ * a debug fault, and the debug fault hasn't yet been able to
+ * clear up the stack. So we first check whether we got  an
+ * NMI on the sysenter entry path, but after that we need to
+ * check whether we got an NMI on the debug path where the debug
+ * fault happened on the sysenter path.
+ */
+ENTRY(nmi)
+       ASM_CLAC
+#ifdef CONFIG_X86_ESPFIX32
+       pushl   %eax
+       movl    %ss, %eax
+       cmpw    $__ESPFIX_SS, %ax
+       popl    %eax
+       je      nmi_espfix_stack
+#endif
+       cmpl    $entry_SYSENTER_32, (%esp)
+       je      nmi_stack_fixup
+       pushl   %eax
+       movl    %esp, %eax
+       /*
+        * Do not access memory above the end of our stack page,
+        * it might not exist.
+        */
+       andl    $(THREAD_SIZE-1), %eax
+       cmpl    $(THREAD_SIZE-20), %eax
+       popl    %eax
+       jae     nmi_stack_correct
+       cmpl    $entry_SYSENTER_32, 12(%esp)
+       je      nmi_debug_stack_check
+nmi_stack_correct:
+       pushl   %eax
+       SAVE_ALL
+       xorl    %edx, %edx                      # zero error code
+       movl    %esp, %eax                      # pt_regs pointer
+       call    do_nmi
+       jmp     restore_all_notrace
+
+nmi_stack_fixup:
+       FIX_STACK 12, nmi_stack_correct, 1
+       jmp     nmi_stack_correct
+
+nmi_debug_stack_check:
+       cmpw    $__KERNEL_CS, 16(%esp)
+       jne     nmi_stack_correct
+       cmpl    $debug, (%esp)
+       jb      nmi_stack_correct
+       cmpl    $debug_esp_fix_insn, (%esp)
+       ja      nmi_stack_correct
+       FIX_STACK 24, nmi_stack_correct, 1
+       jmp     nmi_stack_correct
+
+#ifdef CONFIG_X86_ESPFIX32
+nmi_espfix_stack:
+       /*
+        * create the pointer to lss back
+        */
+       pushl   %ss
+       pushl   %esp
+       addl    $4, (%esp)
+       /* copy the iret frame of 12 bytes */
+       .rept 3
+       pushl   16(%esp)
+       .endr
+       pushl   %eax
+       SAVE_ALL
+       FIXUP_ESPFIX_STACK                      # %eax == %esp
+       xorl    %edx, %edx                      # zero error code
+       call    do_nmi
+       RESTORE_REGS
+       lss     12+4(%esp), %esp                # back to espfix stack
+       jmp     irq_return
+#endif
+END(nmi)
+
+ENTRY(int3)
+       ASM_CLAC
+       pushl   $-1                             # mark this as an int
+       SAVE_ALL
+       TRACE_IRQS_OFF
+       xorl    %edx, %edx                      # zero error code
+       movl    %esp, %eax                      # pt_regs pointer
+       call    do_int3
+       jmp     ret_from_exception
+END(int3)
+
+ENTRY(general_protection)
+       pushl   $do_general_protection
+       jmp     error_code
+END(general_protection)
+
+#ifdef CONFIG_KVM_GUEST
+ENTRY(async_page_fault)
+       ASM_CLAC
+       pushl   $do_async_page_fault
+       jmp     error_code
+END(async_page_fault)
+#endif
similarity index 58%
rename from arch/x86/kernel/entry_64.S
rename to arch/x86/entry/entry_64.S
index 02c2eff7478da6b5180eaf639010f93112e333c1..3bb2c4302df1f8ba18e21b997fd5b51e8ec55c54 100644 (file)
@@ -4,34 +4,25 @@
  *  Copyright (C) 1991, 1992  Linus Torvalds
  *  Copyright (C) 2000, 2001, 2002  Andi Kleen SuSE Labs
  *  Copyright (C) 2000  Pavel Machek <pavel@suse.cz>
- */
-
-/*
+ *
  * entry.S contains the system-call and fault low-level handling routines.
  *
  * Some of this is documented in Documentation/x86/entry_64.txt
  *
- * NOTE: This code handles signal-recognition, which happens every time
- * after an interrupt and after each system call.
- *
  * A note on terminology:
- * - iret frame: Architecture defined interrupt frame from SS to RIP
- * at the top of the kernel process stack.
+ * - iret frame:       Architecture defined interrupt frame from SS to RIP
+ *                     at the top of the kernel process stack.
  *
  * Some macro usage:
- * - CFI macros are used to generate dwarf2 unwind information for better
- * backtraces. They don't change any code.
- * - ENTRY/END Define functions in the symbol table.
- * - TRACE_IRQ_* - Trace hard interrupt state for lock debugging.
- * - idtentry - Define exception entry points.
+ * - ENTRY/END:                Define functions in the symbol table.
+ * - TRACE_IRQ_*:      Trace hardirq state for lock debugging.
+ * - idtentry:         Define exception entry points.
  */
-
 #include <linux/linkage.h>
 #include <asm/segment.h>
 #include <asm/cache.h>
 #include <asm/errno.h>
-#include <asm/dwarf2.h>
-#include <asm/calling.h>
+#include "calling.h"
 #include <asm/asm-offsets.h>
 #include <asm/msr.h>
 #include <asm/unistd.h>
 
 /* Avoid __ASSEMBLER__'ifying <linux/audit.h> just for this.  */
 #include <linux/elf-em.h>
-#define AUDIT_ARCH_X86_64      (EM_X86_64|__AUDIT_ARCH_64BIT|__AUDIT_ARCH_LE)
-#define __AUDIT_ARCH_64BIT 0x80000000
-#define __AUDIT_ARCH_LE           0x40000000
-
-       .code64
-       .section .entry.text, "ax"
+#define AUDIT_ARCH_X86_64                      (EM_X86_64|__AUDIT_ARCH_64BIT|__AUDIT_ARCH_LE)
+#define __AUDIT_ARCH_64BIT                     0x80000000
+#define __AUDIT_ARCH_LE                                0x40000000
 
+.code64
+.section .entry.text, "ax"
 
 #ifdef CONFIG_PARAVIRT
 ENTRY(native_usergs_sysret64)
@@ -64,11 +54,10 @@ ENTRY(native_usergs_sysret64)
 ENDPROC(native_usergs_sysret64)
 #endif /* CONFIG_PARAVIRT */
 
-
 .macro TRACE_IRQS_IRETQ
 #ifdef CONFIG_TRACE_IRQFLAGS
-       bt   $9,EFLAGS(%rsp)    /* interrupts off? */
-       jnc  1f
+       bt      $9, EFLAGS(%rsp)                /* interrupts off? */
+       jnc     1f
        TRACE_IRQS_ON
 1:
 #endif
@@ -88,89 +77,34 @@ ENDPROC(native_usergs_sysret64)
 #if defined(CONFIG_DYNAMIC_FTRACE) && defined(CONFIG_TRACE_IRQFLAGS)
 
 .macro TRACE_IRQS_OFF_DEBUG
-       call debug_stack_set_zero
+       call    debug_stack_set_zero
        TRACE_IRQS_OFF
-       call debug_stack_reset
+       call    debug_stack_reset
 .endm
 
 .macro TRACE_IRQS_ON_DEBUG
-       call debug_stack_set_zero
+       call    debug_stack_set_zero
        TRACE_IRQS_ON
-       call debug_stack_reset
+       call    debug_stack_reset
 .endm
 
 .macro TRACE_IRQS_IRETQ_DEBUG
-       bt   $9,EFLAGS(%rsp)    /* interrupts off? */
-       jnc  1f
+       bt      $9, EFLAGS(%rsp)                /* interrupts off? */
+       jnc     1f
        TRACE_IRQS_ON_DEBUG
 1:
 .endm
 
 #else
-# define TRACE_IRQS_OFF_DEBUG          TRACE_IRQS_OFF
-# define TRACE_IRQS_ON_DEBUG           TRACE_IRQS_ON
-# define TRACE_IRQS_IRETQ_DEBUG                TRACE_IRQS_IRETQ
+# define TRACE_IRQS_OFF_DEBUG                  TRACE_IRQS_OFF
+# define TRACE_IRQS_ON_DEBUG                   TRACE_IRQS_ON
+# define TRACE_IRQS_IRETQ_DEBUG                        TRACE_IRQS_IRETQ
 #endif
 
 /*
- * empty frame
- */
-       .macro EMPTY_FRAME start=1 offset=0
-       .if \start
-       CFI_STARTPROC simple
-       CFI_SIGNAL_FRAME
-       CFI_DEF_CFA rsp,8+\offset
-       .else
-       CFI_DEF_CFA_OFFSET 8+\offset
-       .endif
-       .endm
-
-/*
- * initial frame state for interrupts (and exceptions without error code)
- */
-       .macro INTR_FRAME start=1 offset=0
-       EMPTY_FRAME \start, 5*8+\offset
-       /*CFI_REL_OFFSET ss, 4*8+\offset*/
-       CFI_REL_OFFSET rsp, 3*8+\offset
-       /*CFI_REL_OFFSET rflags, 2*8+\offset*/
-       /*CFI_REL_OFFSET cs, 1*8+\offset*/
-       CFI_REL_OFFSET rip, 0*8+\offset
-       .endm
-
-/*
- * initial frame state for exceptions with error code (and interrupts
- * with vector already pushed)
- */
-       .macro XCPT_FRAME start=1 offset=0
-       INTR_FRAME \start, 1*8+\offset
-       .endm
-
-/*
- * frame that enables passing a complete pt_regs to a C function.
- */
-       .macro DEFAULT_FRAME start=1 offset=0
-       XCPT_FRAME \start, ORIG_RAX+\offset
-       CFI_REL_OFFSET rdi, RDI+\offset
-       CFI_REL_OFFSET rsi, RSI+\offset
-       CFI_REL_OFFSET rdx, RDX+\offset
-       CFI_REL_OFFSET rcx, RCX+\offset
-       CFI_REL_OFFSET rax, RAX+\offset
-       CFI_REL_OFFSET r8, R8+\offset
-       CFI_REL_OFFSET r9, R9+\offset
-       CFI_REL_OFFSET r10, R10+\offset
-       CFI_REL_OFFSET r11, R11+\offset
-       CFI_REL_OFFSET rbx, RBX+\offset
-       CFI_REL_OFFSET rbp, RBP+\offset
-       CFI_REL_OFFSET r12, R12+\offset
-       CFI_REL_OFFSET r13, R13+\offset
-       CFI_REL_OFFSET r14, R14+\offset
-       CFI_REL_OFFSET r15, R15+\offset
-       .endm
-
-/*
- * 64bit SYSCALL instruction entry. Up to 6 arguments in registers.
+ * 64-bit SYSCALL instruction entry. Up to 6 arguments in registers.
  *
- * 64bit SYSCALL saves rip to rcx, clears rflags.RF, then saves rflags to r11,
+ * 64-bit SYSCALL saves rip to rcx, clears rflags.RF, then saves rflags to r11,
  * then loads new ss, cs, and rip from previously programmed MSRs.
  * rflags gets masked by a value from another MSR (so CLD and CLAC
  * are not needed). SYSCALL does not save anything on the stack
@@ -186,7 +120,7 @@ ENDPROC(native_usergs_sysret64)
  * r10  arg3 (needs to be moved to rcx to conform to C ABI)
  * r8   arg4
  * r9   arg5
- * (note: r12-r15,rbp,rbx are callee-preserved in C ABI)
+ * (note: r12-r15, rbp, rbx are callee-preserved in C ABI)
  *
  * Only called from user space.
  *
@@ -195,13 +129,7 @@ ENDPROC(native_usergs_sysret64)
  * with them due to bugs in both AMD and Intel CPUs.
  */
 
-ENTRY(system_call)
-       CFI_STARTPROC   simple
-       CFI_SIGNAL_FRAME
-       CFI_DEF_CFA     rsp,0
-       CFI_REGISTER    rip,rcx
-       /*CFI_REGISTER  rflags,r11*/
-
+ENTRY(entry_SYSCALL_64)
        /*
         * Interrupts are off on entry.
         * We do not frame this tiny irq-off block with TRACE_IRQS_OFF/ON,
@@ -213,14 +141,14 @@ ENTRY(system_call)
         * after the swapgs, so that it can do the swapgs
         * for the guest and jump here on syscall.
         */
-GLOBAL(system_call_after_swapgs)
+GLOBAL(entry_SYSCALL_64_after_swapgs)
 
-       movq    %rsp,PER_CPU_VAR(rsp_scratch)
-       movq    PER_CPU_VAR(kernel_stack),%rsp
+       movq    %rsp, PER_CPU_VAR(rsp_scratch)
+       movq    PER_CPU_VAR(cpu_current_top_of_stack), %rsp
 
        /* Construct struct pt_regs on stack */
-       pushq_cfi $__USER_DS                    /* pt_regs->ss */
-       pushq_cfi PER_CPU_VAR(rsp_scratch)      /* pt_regs->sp */
+       pushq   $__USER_DS                      /* pt_regs->ss */
+       pushq   PER_CPU_VAR(rsp_scratch)        /* pt_regs->sp */
        /*
         * Re-enable interrupts.
         * We use 'rsp_scratch' as a scratch space, hence irq-off block above
@@ -229,36 +157,34 @@ GLOBAL(system_call_after_swapgs)
         * with using rsp_scratch:
         */
        ENABLE_INTERRUPTS(CLBR_NONE)
-       pushq_cfi       %r11                    /* pt_regs->flags */
-       pushq_cfi       $__USER_CS              /* pt_regs->cs */
-       pushq_cfi       %rcx                    /* pt_regs->ip */
-       CFI_REL_OFFSET rip,0
-       pushq_cfi_reg   rax                     /* pt_regs->orig_ax */
-       pushq_cfi_reg   rdi                     /* pt_regs->di */
-       pushq_cfi_reg   rsi                     /* pt_regs->si */
-       pushq_cfi_reg   rdx                     /* pt_regs->dx */
-       pushq_cfi_reg   rcx                     /* pt_regs->cx */
-       pushq_cfi       $-ENOSYS                /* pt_regs->ax */
-       pushq_cfi_reg   r8                      /* pt_regs->r8 */
-       pushq_cfi_reg   r9                      /* pt_regs->r9 */
-       pushq_cfi_reg   r10                     /* pt_regs->r10 */
-       pushq_cfi_reg   r11                     /* pt_regs->r11 */
-       sub     $(6*8),%rsp /* pt_regs->bp,bx,r12-15 not saved */
-       CFI_ADJUST_CFA_OFFSET 6*8
-
-       testl $_TIF_WORK_SYSCALL_ENTRY, ASM_THREAD_INFO(TI_flags, %rsp, SIZEOF_PTREGS)
-       jnz tracesys
-system_call_fastpath:
+       pushq   %r11                            /* pt_regs->flags */
+       pushq   $__USER_CS                      /* pt_regs->cs */
+       pushq   %rcx                            /* pt_regs->ip */
+       pushq   %rax                            /* pt_regs->orig_ax */
+       pushq   %rdi                            /* pt_regs->di */
+       pushq   %rsi                            /* pt_regs->si */
+       pushq   %rdx                            /* pt_regs->dx */
+       pushq   %rcx                            /* pt_regs->cx */
+       pushq   $-ENOSYS                        /* pt_regs->ax */
+       pushq   %r8                             /* pt_regs->r8 */
+       pushq   %r9                             /* pt_regs->r9 */
+       pushq   %r10                            /* pt_regs->r10 */
+       pushq   %r11                            /* pt_regs->r11 */
+       sub     $(6*8), %rsp                    /* pt_regs->bp, bx, r12-15 not saved */
+
+       testl   $_TIF_WORK_SYSCALL_ENTRY, ASM_THREAD_INFO(TI_flags, %rsp, SIZEOF_PTREGS)
+       jnz     tracesys
+entry_SYSCALL_64_fastpath:
 #if __SYSCALL_MASK == ~0
-       cmpq $__NR_syscall_max,%rax
+       cmpq    $__NR_syscall_max, %rax
 #else
-       andl $__SYSCALL_MASK,%eax
-       cmpl $__NR_syscall_max,%eax
+       andl    $__SYSCALL_MASK, %eax
+       cmpl    $__NR_syscall_max, %eax
 #endif
-       ja      1f      /* return -ENOSYS (already in pt_regs->ax) */
-       movq %r10,%rcx
-       call *sys_call_table(,%rax,8)
-       movq %rax,RAX(%rsp)
+       ja      1f                              /* return -ENOSYS (already in pt_regs->ax) */
+       movq    %r10, %rcx
+       call    *sys_call_table(, %rax, 8)
+       movq    %rax, RAX(%rsp)
 1:
 /*
  * Syscall return path ending with SYSRET (fast path).
@@ -279,19 +205,15 @@ system_call_fastpath:
         * flags (TIF_NOTIFY_RESUME, TIF_USER_RETURN_NOTIFY, etc) set is
         * very bad.
         */
-       testl $_TIF_ALLWORK_MASK, ASM_THREAD_INFO(TI_flags, %rsp, SIZEOF_PTREGS)
-       jnz int_ret_from_sys_call_irqs_off      /* Go to the slow path */
-
-       CFI_REMEMBER_STATE
+       testl   $_TIF_ALLWORK_MASK, ASM_THREAD_INFO(TI_flags, %rsp, SIZEOF_PTREGS)
+       jnz     int_ret_from_sys_call_irqs_off  /* Go to the slow path */
 
        RESTORE_C_REGS_EXCEPT_RCX_R11
-       movq    RIP(%rsp),%rcx
-       CFI_REGISTER    rip,rcx
-       movq    EFLAGS(%rsp),%r11
-       /*CFI_REGISTER  rflags,r11*/
-       movq    RSP(%rsp),%rsp
+       movq    RIP(%rsp), %rcx
+       movq    EFLAGS(%rsp), %r11
+       movq    RSP(%rsp), %rsp
        /*
-        * 64bit SYSRET restores rip from rcx,
+        * 64-bit SYSRET restores rip from rcx,
         * rflags from r11 (but RF and VM bits are forced to 0),
         * cs and ss are loaded from MSRs.
         * Restoration of rflags re-enables interrupts.
@@ -307,25 +229,23 @@ system_call_fastpath:
         */
        USERGS_SYSRET64
 
-       CFI_RESTORE_STATE
-
        /* Do syscall entry tracing */
 tracesys:
-       movq %rsp, %rdi
-       movl $AUDIT_ARCH_X86_64, %esi
-       call syscall_trace_enter_phase1
-       test %rax, %rax
-       jnz tracesys_phase2             /* if needed, run the slow path */
-       RESTORE_C_REGS_EXCEPT_RAX       /* else restore clobbered regs */
-       movq ORIG_RAX(%rsp), %rax
-       jmp system_call_fastpath        /*      and return to the fast path */
+       movq    %rsp, %rdi
+       movl    $AUDIT_ARCH_X86_64, %esi
+       call    syscall_trace_enter_phase1
+       test    %rax, %rax
+       jnz     tracesys_phase2                 /* if needed, run the slow path */
+       RESTORE_C_REGS_EXCEPT_RAX               /* else restore clobbered regs */
+       movq    ORIG_RAX(%rsp), %rax
+       jmp     entry_SYSCALL_64_fastpath       /* and return to the fast path */
 
 tracesys_phase2:
        SAVE_EXTRA_REGS
-       movq %rsp, %rdi
-       movl $AUDIT_ARCH_X86_64, %esi
-       movq %rax,%rdx
-       call syscall_trace_enter_phase2
+       movq    %rsp, %rdi
+       movl    $AUDIT_ARCH_X86_64, %esi
+       movq    %rax, %rdx
+       call    syscall_trace_enter_phase2
 
        /*
         * Reload registers from stack in case ptrace changed them.
@@ -335,15 +255,15 @@ tracesys_phase2:
        RESTORE_C_REGS_EXCEPT_RAX
        RESTORE_EXTRA_REGS
 #if __SYSCALL_MASK == ~0
-       cmpq $__NR_syscall_max,%rax
+       cmpq    $__NR_syscall_max, %rax
 #else
-       andl $__SYSCALL_MASK,%eax
-       cmpl $__NR_syscall_max,%eax
+       andl    $__SYSCALL_MASK, %eax
+       cmpl    $__NR_syscall_max, %eax
 #endif
-       ja      1f      /* return -ENOSYS (already in pt_regs->ax) */
-       movq %r10,%rcx  /* fixup for C */
-       call *sys_call_table(,%rax,8)
-       movq %rax,RAX(%rsp)
+       ja      1f                              /* return -ENOSYS (already in pt_regs->ax) */
+       movq    %r10, %rcx                      /* fixup for C */
+       call    *sys_call_table(, %rax, 8)
+       movq    %rax, RAX(%rsp)
 1:
        /* Use IRET because user could have changed pt_regs->foo */
 
@@ -355,31 +275,33 @@ GLOBAL(int_ret_from_sys_call)
        DISABLE_INTERRUPTS(CLBR_NONE)
 int_ret_from_sys_call_irqs_off: /* jumps come here from the irqs-off SYSRET path */
        TRACE_IRQS_OFF
-       movl $_TIF_ALLWORK_MASK,%edi
+       movl    $_TIF_ALLWORK_MASK, %edi
        /* edi: mask to check */
 GLOBAL(int_with_check)
        LOCKDEP_SYS_EXIT_IRQ
        GET_THREAD_INFO(%rcx)
-       movl TI_flags(%rcx),%edx
-       andl %edi,%edx
-       jnz   int_careful
-       andl    $~TS_COMPAT,TI_status(%rcx)
+       movl    TI_flags(%rcx), %edx
+       andl    %edi, %edx
+       jnz     int_careful
+       andl    $~TS_COMPAT, TI_status(%rcx)
        jmp     syscall_return
 
-       /* Either reschedule or signal or syscall exit tracking needed. */
-       /* First do a reschedule test. */
-       /* edx: work, edi: workmask */
+       /*
+        * Either reschedule or signal or syscall exit tracking needed.
+        * First do a reschedule test.
+        * edx: work, edi: workmask
+        */
 int_careful:
-       bt $TIF_NEED_RESCHED,%edx
-       jnc  int_very_careful
+       bt      $TIF_NEED_RESCHED, %edx
+       jnc     int_very_careful
        TRACE_IRQS_ON
        ENABLE_INTERRUPTS(CLBR_NONE)
-       pushq_cfi %rdi
+       pushq   %rdi
        SCHEDULE_USER
-       popq_cfi %rdi
+       popq    %rdi
        DISABLE_INTERRUPTS(CLBR_NONE)
        TRACE_IRQS_OFF
-       jmp int_with_check
+       jmp     int_with_check
 
        /* handle signals and tracing -- both require a full pt_regs */
 int_very_careful:
@@ -387,27 +309,27 @@ int_very_careful:
        ENABLE_INTERRUPTS(CLBR_NONE)
        SAVE_EXTRA_REGS
        /* Check for syscall exit trace */
-       testl $_TIF_WORK_SYSCALL_EXIT,%edx
-       jz int_signal
-       pushq_cfi %rdi
-       leaq 8(%rsp),%rdi       # &ptregs -> arg1
-       call syscall_trace_leave
-       popq_cfi %rdi
-       andl $~(_TIF_WORK_SYSCALL_EXIT|_TIF_SYSCALL_EMU),%edi
-       jmp int_restore_rest
+       testl   $_TIF_WORK_SYSCALL_EXIT, %edx
+       jz      int_signal
+       pushq   %rdi
+       leaq    8(%rsp), %rdi                   /* &ptregs -> arg1 */
+       call    syscall_trace_leave
+       popq    %rdi
+       andl    $~(_TIF_WORK_SYSCALL_EXIT|_TIF_SYSCALL_EMU), %edi
+       jmp     int_restore_rest
 
 int_signal:
-       testl $_TIF_DO_NOTIFY_MASK,%edx
-       jz 1f
-       movq %rsp,%rdi          # &ptregs -> arg1
-       xorl %esi,%esi          # oldset -> arg2
-       call do_notify_resume
-1:     movl $_TIF_WORK_MASK,%edi
+       testl   $_TIF_DO_NOTIFY_MASK, %edx
+       jz      1f
+       movq    %rsp, %rdi                      /* &ptregs -> arg1 */
+       xorl    %esi, %esi                      /* oldset -> arg2 */
+       call    do_notify_resume
+1:     movl    $_TIF_WORK_MASK, %edi
 int_restore_rest:
        RESTORE_EXTRA_REGS
        DISABLE_INTERRUPTS(CLBR_NONE)
        TRACE_IRQS_OFF
-       jmp int_with_check
+       jmp     int_with_check
 
 syscall_return:
        /* The IRETQ could re-enable interrupts: */
@@ -418,34 +340,37 @@ syscall_return:
         * Try to use SYSRET instead of IRET if we're returning to
         * a completely clean 64-bit userspace context.
         */
-       movq RCX(%rsp),%rcx
-       cmpq %rcx,RIP(%rsp)             /* RCX == RIP */
-       jne opportunistic_sysret_failed
+       movq    RCX(%rsp), %rcx
+       movq    RIP(%rsp), %r11
+       cmpq    %rcx, %r11                      /* RCX == RIP */
+       jne     opportunistic_sysret_failed
 
        /*
         * On Intel CPUs, SYSRET with non-canonical RCX/RIP will #GP
         * in kernel space.  This essentially lets the user take over
-        * the kernel, since userspace controls RSP.  It's not worth
-        * testing for canonicalness exactly -- this check detects any
-        * of the 17 high bits set, which is true for non-canonical
-        * or kernel addresses.  (This will pessimize vsyscall=native.
-        * Big deal.)
+        * the kernel, since userspace controls RSP.
         *
-        * If virtual addresses ever become wider, this will need
+        * If width of "canonical tail" ever becomes variable, this will need
         * to be updated to remain correct on both old and new CPUs.
         */
        .ifne __VIRTUAL_MASK_SHIFT - 47
        .error "virtual address width changed -- SYSRET checks need update"
        .endif
-       shr $__VIRTUAL_MASK_SHIFT, %rcx
-       jnz opportunistic_sysret_failed
 
-       cmpq $__USER_CS,CS(%rsp)        /* CS must match SYSRET */
-       jne opportunistic_sysret_failed
+       /* Change top 16 bits to be the sign-extension of 47th bit */
+       shl     $(64 - (__VIRTUAL_MASK_SHIFT+1)), %rcx
+       sar     $(64 - (__VIRTUAL_MASK_SHIFT+1)), %rcx
 
-       movq R11(%rsp),%r11
-       cmpq %r11,EFLAGS(%rsp)          /* R11 == RFLAGS */
-       jne opportunistic_sysret_failed
+       /* If this changed %rcx, it was not canonical */
+       cmpq    %rcx, %r11
+       jne     opportunistic_sysret_failed
+
+       cmpq    $__USER_CS, CS(%rsp)            /* CS must match SYSRET */
+       jne     opportunistic_sysret_failed
+
+       movq    R11(%rsp), %r11
+       cmpq    %r11, EFLAGS(%rsp)              /* R11 == RFLAGS */
+       jne     opportunistic_sysret_failed
 
        /*
         * SYSRET can't restore RF.  SYSRET can restore TF, but unlike IRET,
@@ -454,47 +379,41 @@ syscall_return:
         * with register state that satisfies the opportunistic SYSRET
         * conditions.  For example, single-stepping this user code:
         *
-        *           movq $stuck_here,%rcx
+        *           movq       $stuck_here, %rcx
         *           pushfq
         *           popq %r11
         *   stuck_here:
         *
         * would never get past 'stuck_here'.
         */
-       testq $(X86_EFLAGS_RF|X86_EFLAGS_TF), %r11
-       jnz opportunistic_sysret_failed
+       testq   $(X86_EFLAGS_RF|X86_EFLAGS_TF), %r11
+       jnz     opportunistic_sysret_failed
 
        /* nothing to check for RSP */
 
-       cmpq $__USER_DS,SS(%rsp)        /* SS must match SYSRET */
-       jne opportunistic_sysret_failed
+       cmpq    $__USER_DS, SS(%rsp)            /* SS must match SYSRET */
+       jne     opportunistic_sysret_failed
 
        /*
-        * We win!  This label is here just for ease of understanding
-        * perf profiles.  Nothing jumps here.
+        * We win! This label is here just for ease of understanding
+        * perf profiles. Nothing jumps here.
         */
 syscall_return_via_sysret:
-       CFI_REMEMBER_STATE
-       /* r11 is already restored (see code above) */
-       RESTORE_C_REGS_EXCEPT_R11
-       movq RSP(%rsp),%rsp
+       /* rcx and r11 are already restored (see code above) */
+       RESTORE_C_REGS_EXCEPT_RCX_R11
+       movq    RSP(%rsp), %rsp
        USERGS_SYSRET64
-       CFI_RESTORE_STATE
 
 opportunistic_sysret_failed:
        SWAPGS
        jmp     restore_c_regs_and_iret
-       CFI_ENDPROC
-END(system_call)
+END(entry_SYSCALL_64)
 
 
        .macro FORK_LIKE func
 ENTRY(stub_\func)
-       CFI_STARTPROC
-       DEFAULT_FRAME 0, 8              /* offset 8: return address */
        SAVE_EXTRA_REGS 8
-       jmp sys_\func
-       CFI_ENDPROC
+       jmp     sys_\func
 END(stub_\func)
        .endm
 
@@ -503,8 +422,6 @@ END(stub_\func)
        FORK_LIKE  vfork
 
 ENTRY(stub_execve)
-       CFI_STARTPROC
-       DEFAULT_FRAME 0, 8
        call    sys_execve
 return_from_execve:
        testl   %eax, %eax
@@ -514,11 +431,9 @@ return_from_execve:
 1:
        /* must use IRET code path (pt_regs->cs may have changed) */
        addq    $8, %rsp
-       CFI_ADJUST_CFA_OFFSET -8
        ZERO_EXTRA_REGS
-       movq    %rax,RAX(%rsp)
+       movq    %rax, RAX(%rsp)
        jmp     int_ret_from_sys_call
-       CFI_ENDPROC
 END(stub_execve)
 /*
  * Remaining execve stubs are only 7 bytes long.
@@ -526,47 +441,25 @@ END(stub_execve)
  */
        .align  8
 GLOBAL(stub_execveat)
-       CFI_STARTPROC
-       DEFAULT_FRAME 0, 8
        call    sys_execveat
        jmp     return_from_execve
-       CFI_ENDPROC
 END(stub_execveat)
 
-#ifdef CONFIG_X86_X32_ABI
+#if defined(CONFIG_X86_X32_ABI) || defined(CONFIG_IA32_EMULATION)
        .align  8
 GLOBAL(stub_x32_execve)
-       CFI_STARTPROC
-       DEFAULT_FRAME 0, 8
-       call    compat_sys_execve
-       jmp     return_from_execve
-       CFI_ENDPROC
-END(stub_x32_execve)
-       .align  8
-GLOBAL(stub_x32_execveat)
-       CFI_STARTPROC
-       DEFAULT_FRAME 0, 8
-       call    compat_sys_execveat
-       jmp     return_from_execve
-       CFI_ENDPROC
-END(stub_x32_execveat)
-#endif
-
-#ifdef CONFIG_IA32_EMULATION
-       .align  8
 GLOBAL(stub32_execve)
-       CFI_STARTPROC
        call    compat_sys_execve
        jmp     return_from_execve
-       CFI_ENDPROC
 END(stub32_execve)
+END(stub_x32_execve)
        .align  8
+GLOBAL(stub_x32_execveat)
 GLOBAL(stub32_execveat)
-       CFI_STARTPROC
        call    compat_sys_execveat
        jmp     return_from_execve
-       CFI_ENDPROC
 END(stub32_execveat)
+END(stub_x32_execveat)
 #endif
 
 /*
@@ -574,8 +467,6 @@ END(stub32_execveat)
  * This cannot be done with SYSRET, so use the IRET return path instead.
  */
 ENTRY(stub_rt_sigreturn)
-       CFI_STARTPROC
-       DEFAULT_FRAME 0, 8
        /*
         * SAVE_EXTRA_REGS result is not normally needed:
         * sigreturn overwrites all pt_regs->GPREGS.
@@ -584,24 +475,19 @@ ENTRY(stub_rt_sigreturn)
         * we SAVE_EXTRA_REGS here.
         */
        SAVE_EXTRA_REGS 8
-       call sys_rt_sigreturn
+       call    sys_rt_sigreturn
 return_from_stub:
        addq    $8, %rsp
-       CFI_ADJUST_CFA_OFFSET -8
        RESTORE_EXTRA_REGS
-       movq %rax,RAX(%rsp)
-       jmp int_ret_from_sys_call
-       CFI_ENDPROC
+       movq    %rax, RAX(%rsp)
+       jmp     int_ret_from_sys_call
 END(stub_rt_sigreturn)
 
 #ifdef CONFIG_X86_X32_ABI
 ENTRY(stub_x32_rt_sigreturn)
-       CFI_STARTPROC
-       DEFAULT_FRAME 0, 8
        SAVE_EXTRA_REGS 8
-       call sys32_x32_rt_sigreturn
-       jmp  return_from_stub
-       CFI_ENDPROC
+       call    sys32_x32_rt_sigreturn
+       jmp     return_from_stub
 END(stub_x32_rt_sigreturn)
 #endif
 
@@ -611,36 +497,36 @@ END(stub_x32_rt_sigreturn)
  * rdi: prev task we switched from
  */
 ENTRY(ret_from_fork)
-       DEFAULT_FRAME
 
-       LOCK ; btr $TIF_FORK,TI_flags(%r8)
+       LOCK ; btr $TIF_FORK, TI_flags(%r8)
 
-       pushq_cfi $0x0002
-       popfq_cfi                               # reset kernel eflags
+       pushq   $0x0002
+       popfq                                   /* reset kernel eflags */
 
-       call schedule_tail                      # rdi: 'prev' task parameter
+       call    schedule_tail                   /* rdi: 'prev' task parameter */
 
        RESTORE_EXTRA_REGS
 
-       testl $3,CS(%rsp)                       # from kernel_thread?
+       testb   $3, CS(%rsp)                    /* from kernel_thread? */
 
        /*
         * By the time we get here, we have no idea whether our pt_regs,
         * ti flags, and ti status came from the 64-bit SYSCALL fast path,
-        * the slow path, or one of the ia32entry paths.
+        * the slow path, or one of the 32-bit compat paths.
         * Use IRET code path to return, since it can safely handle
         * all of the above.
         */
        jnz     int_ret_from_sys_call
 
-       /* We came from kernel_thread */
-       /* nb: we depend on RESTORE_EXTRA_REGS above */
-       movq %rbp, %rdi
-       call *%rbx
-       movl $0, RAX(%rsp)
+       /*
+        * We came from kernel_thread
+        * nb: we depend on RESTORE_EXTRA_REGS above
+        */
+       movq    %rbp, %rdi
+       call    *%rbx
+       movl    $0, RAX(%rsp)
        RESTORE_EXTRA_REGS
-       jmp int_ret_from_sys_call
-       CFI_ENDPROC
+       jmp     int_ret_from_sys_call
 END(ret_from_fork)
 
 /*
@@ -649,16 +535,13 @@ END(ret_from_fork)
  */
        .align 8
 ENTRY(irq_entries_start)
-       INTR_FRAME
     vector=FIRST_EXTERNAL_VECTOR
     .rept (FIRST_SYSTEM_VECTOR - FIRST_EXTERNAL_VECTOR)
-       pushq_cfi $(~vector+0x80)       /* Note: always in signed byte range */
+       pushq   $(~vector+0x80)                 /* Note: always in signed byte range */
     vector=vector+1
        jmp     common_interrupt
-       CFI_ADJUST_CFA_OFFSET -8
        .align  8
     .endr
-       CFI_ENDPROC
 END(irq_entries_start)
 
 /*
@@ -684,10 +567,10 @@ END(irq_entries_start)
        /* this goes to 0(%rsp) for unwinder, not for saving the value: */
        SAVE_EXTRA_REGS_RBP -RBP
 
-       leaq -RBP(%rsp),%rdi    /* arg1 for \func (pointer to pt_regs) */
+       leaq    -RBP(%rsp), %rdi                /* arg1 for \func (pointer to pt_regs) */
 
-       testl $3, CS-RBP(%rsp)
-       je 1f
+       testb   $3, CS-RBP(%rsp)
+       jz      1f
        SWAPGS
 1:
        /*
@@ -697,24 +580,14 @@ END(irq_entries_start)
         * a little cheaper to use a separate counter in the PDA (short of
         * moving irq_enter into assembly, which would be too much work)
         */
-       movq %rsp, %rsi
-       incl PER_CPU_VAR(irq_count)
-       cmovzq PER_CPU_VAR(irq_stack_ptr),%rsp
-       CFI_DEF_CFA_REGISTER    rsi
-       pushq %rsi
-       /*
-        * For debugger:
-        * "CFA (Current Frame Address) is the value on stack + offset"
-        */
-       CFI_ESCAPE      0x0f /* DW_CFA_def_cfa_expression */, 6, \
-                       0x77 /* DW_OP_breg7 (rsp) */, 0, \
-                       0x06 /* DW_OP_deref */, \
-                       0x08 /* DW_OP_const1u */, SIZEOF_PTREGS-RBP, \
-                       0x22 /* DW_OP_plus */
+       movq    %rsp, %rsi
+       incl    PER_CPU_VAR(irq_count)
+       cmovzq  PER_CPU_VAR(irq_stack_ptr), %rsp
+       pushq   %rsi
        /* We entered an interrupt context - irqs are off: */
        TRACE_IRQS_OFF
 
-       call \func
+       call    \func
        .endm
 
        /*
@@ -723,42 +596,36 @@ END(irq_entries_start)
         */
        .p2align CONFIG_X86_L1_CACHE_SHIFT
 common_interrupt:
-       XCPT_FRAME
        ASM_CLAC
-       addq $-0x80,(%rsp)              /* Adjust vector to [-256,-1] range */
+       addq    $-0x80, (%rsp)                  /* Adjust vector to [-256, -1] range */
        interrupt do_IRQ
        /* 0(%rsp): old RSP */
 ret_from_intr:
        DISABLE_INTERRUPTS(CLBR_NONE)
        TRACE_IRQS_OFF
-       decl PER_CPU_VAR(irq_count)
+       decl    PER_CPU_VAR(irq_count)
 
        /* Restore saved previous stack */
-       popq %rsi
-       CFI_DEF_CFA rsi,SIZEOF_PTREGS-RBP /* reg/off reset after def_cfa_expr */
+       popq    %rsi
        /* return code expects complete pt_regs - adjust rsp accordingly: */
-       leaq -RBP(%rsi),%rsp
-       CFI_DEF_CFA_REGISTER    rsp
-       CFI_ADJUST_CFA_OFFSET   RBP
+       leaq    -RBP(%rsi), %rsp
 
-       testl $3,CS(%rsp)
-       je retint_kernel
+       testb   $3, CS(%rsp)
+       jz      retint_kernel
        /* Interrupt came from user space */
-
+retint_user:
        GET_THREAD_INFO(%rcx)
-       /*
-        * %rcx: thread info. Interrupts off.
-        */
+
+       /* %rcx: thread info. Interrupts are off. */
 retint_with_reschedule:
-       movl $_TIF_WORK_MASK,%edi
+       movl    $_TIF_WORK_MASK, %edi
 retint_check:
        LOCKDEP_SYS_EXIT_IRQ
-       movl TI_flags(%rcx),%edx
-       andl %edi,%edx
-       CFI_REMEMBER_STATE
-       jnz  retint_careful
+       movl    TI_flags(%rcx), %edx
+       andl    %edi, %edx
+       jnz     retint_careful
 
-retint_swapgs:         /* return to user-space */
+retint_swapgs:                                 /* return to user-space */
        /*
         * The iretq could re-enable interrupts:
         */
@@ -773,9 +640,9 @@ retint_kernel:
 #ifdef CONFIG_PREEMPT
        /* Interrupts are off */
        /* Check if we need preemption */
-       bt      $9,EFLAGS(%rsp) /* interrupts were off? */
+       bt      $9, EFLAGS(%rsp)                /* were interrupts off? */
        jnc     1f
-0:     cmpl    $0,PER_CPU_VAR(__preempt_count)
+0:     cmpl    $0, PER_CPU_VAR(__preempt_count)
        jnz     1f
        call    preempt_schedule_irq
        jmp     0b
@@ -793,8 +660,6 @@ retint_kernel:
 restore_c_regs_and_iret:
        RESTORE_C_REGS
        REMOVE_PT_GPREGS_FROM_STACK 8
-
-irq_return:
        INTERRUPT_RETURN
 
 ENTRY(native_iret)
@@ -803,8 +668,8 @@ ENTRY(native_iret)
         * 64-bit mode SS:RSP on the exception stack is always valid.
         */
 #ifdef CONFIG_X86_ESPFIX64
-       testb $4,(SS-RIP)(%rsp)
-       jnz native_irq_return_ldt
+       testb   $4, (SS-RIP)(%rsp)
+       jnz     native_irq_return_ldt
 #endif
 
 .global native_irq_return_iret
@@ -819,62 +684,60 @@ native_irq_return_iret:
 
 #ifdef CONFIG_X86_ESPFIX64
 native_irq_return_ldt:
-       pushq_cfi %rax
-       pushq_cfi %rdi
+       pushq   %rax
+       pushq   %rdi
        SWAPGS
-       movq PER_CPU_VAR(espfix_waddr),%rdi
-       movq %rax,(0*8)(%rdi)   /* RAX */
-       movq (2*8)(%rsp),%rax   /* RIP */
-       movq %rax,(1*8)(%rdi)
-       movq (3*8)(%rsp),%rax   /* CS */
-       movq %rax,(2*8)(%rdi)
-       movq (4*8)(%rsp),%rax   /* RFLAGS */
-       movq %rax,(3*8)(%rdi)
-       movq (6*8)(%rsp),%rax   /* SS */
-       movq %rax,(5*8)(%rdi)
-       movq (5*8)(%rsp),%rax   /* RSP */
-       movq %rax,(4*8)(%rdi)
-       andl $0xffff0000,%eax
-       popq_cfi %rdi
-       orq PER_CPU_VAR(espfix_stack),%rax
+       movq    PER_CPU_VAR(espfix_waddr), %rdi
+       movq    %rax, (0*8)(%rdi)               /* RAX */
+       movq    (2*8)(%rsp), %rax               /* RIP */
+       movq    %rax, (1*8)(%rdi)
+       movq    (3*8)(%rsp), %rax               /* CS */
+       movq    %rax, (2*8)(%rdi)
+       movq    (4*8)(%rsp), %rax               /* RFLAGS */
+       movq    %rax, (3*8)(%rdi)
+       movq    (6*8)(%rsp), %rax               /* SS */
+       movq    %rax, (5*8)(%rdi)
+       movq    (5*8)(%rsp), %rax               /* RSP */
+       movq    %rax, (4*8)(%rdi)
+       andl    $0xffff0000, %eax
+       popq    %rdi
+       orq     PER_CPU_VAR(espfix_stack), %rax
        SWAPGS
-       movq %rax,%rsp
-       popq_cfi %rax
-       jmp native_irq_return_iret
+       movq    %rax, %rsp
+       popq    %rax
+       jmp     native_irq_return_iret
 #endif
 
        /* edi: workmask, edx: work */
 retint_careful:
-       CFI_RESTORE_STATE
-       bt    $TIF_NEED_RESCHED,%edx
-       jnc   retint_signal
+       bt      $TIF_NEED_RESCHED, %edx
+       jnc     retint_signal
        TRACE_IRQS_ON
        ENABLE_INTERRUPTS(CLBR_NONE)
-       pushq_cfi %rdi
+       pushq   %rdi
        SCHEDULE_USER
-       popq_cfi %rdi
+       popq    %rdi
        GET_THREAD_INFO(%rcx)
        DISABLE_INTERRUPTS(CLBR_NONE)
        TRACE_IRQS_OFF
-       jmp retint_check
+       jmp     retint_check
 
 retint_signal:
-       testl $_TIF_DO_NOTIFY_MASK,%edx
-       jz    retint_swapgs
+       testl   $_TIF_DO_NOTIFY_MASK, %edx
+       jz      retint_swapgs
        TRACE_IRQS_ON
        ENABLE_INTERRUPTS(CLBR_NONE)
        SAVE_EXTRA_REGS
-       movq $-1,ORIG_RAX(%rsp)
-       xorl %esi,%esi          # oldset
-       movq %rsp,%rdi          # &pt_regs
-       call do_notify_resume
+       movq    $-1, ORIG_RAX(%rsp)
+       xorl    %esi, %esi                      /* oldset */
+       movq    %rsp, %rdi                      /* &pt_regs */
+       call    do_notify_resume
        RESTORE_EXTRA_REGS
        DISABLE_INTERRUPTS(CLBR_NONE)
        TRACE_IRQS_OFF
        GET_THREAD_INFO(%rcx)
-       jmp retint_with_reschedule
+       jmp     retint_with_reschedule
 
-       CFI_ENDPROC
 END(common_interrupt)
 
 /*
@@ -882,13 +745,11 @@ END(common_interrupt)
  */
 .macro apicinterrupt3 num sym do_sym
 ENTRY(\sym)
-       INTR_FRAME
        ASM_CLAC
-       pushq_cfi $~(\num)
+       pushq   $~(\num)
 .Lcommon_\sym:
        interrupt \do_sym
-       jmp ret_from_intr
-       CFI_ENDPROC
+       jmp     ret_from_intr
 END(\sym)
 .endm
 
@@ -910,53 +771,45 @@ trace_apicinterrupt \num \sym
 .endm
 
 #ifdef CONFIG_SMP
-apicinterrupt3 IRQ_MOVE_CLEANUP_VECTOR \
-       irq_move_cleanup_interrupt smp_irq_move_cleanup_interrupt
-apicinterrupt3 REBOOT_VECTOR \
-       reboot_interrupt smp_reboot_interrupt
+apicinterrupt3 IRQ_MOVE_CLEANUP_VECTOR         irq_move_cleanup_interrupt      smp_irq_move_cleanup_interrupt
+apicinterrupt3 REBOOT_VECTOR                   reboot_interrupt                smp_reboot_interrupt
 #endif
 
 #ifdef CONFIG_X86_UV
-apicinterrupt3 UV_BAU_MESSAGE \
-       uv_bau_message_intr1 uv_bau_message_interrupt
+apicinterrupt3 UV_BAU_MESSAGE                  uv_bau_message_intr1            uv_bau_message_interrupt
 #endif
-apicinterrupt LOCAL_TIMER_VECTOR \
-       apic_timer_interrupt smp_apic_timer_interrupt
-apicinterrupt X86_PLATFORM_IPI_VECTOR \
-       x86_platform_ipi smp_x86_platform_ipi
+
+apicinterrupt LOCAL_TIMER_VECTOR               apic_timer_interrupt            smp_apic_timer_interrupt
+apicinterrupt X86_PLATFORM_IPI_VECTOR          x86_platform_ipi                smp_x86_platform_ipi
 
 #ifdef CONFIG_HAVE_KVM
-apicinterrupt3 POSTED_INTR_VECTOR \
-       kvm_posted_intr_ipi smp_kvm_posted_intr_ipi
+apicinterrupt3 POSTED_INTR_VECTOR              kvm_posted_intr_ipi             smp_kvm_posted_intr_ipi
+apicinterrupt3 POSTED_INTR_WAKEUP_VECTOR       kvm_posted_intr_wakeup_ipi      smp_kvm_posted_intr_wakeup_ipi
 #endif
 
 #ifdef CONFIG_X86_MCE_THRESHOLD
-apicinterrupt THRESHOLD_APIC_VECTOR \
-       threshold_interrupt smp_threshold_interrupt
+apicinterrupt THRESHOLD_APIC_VECTOR            threshold_interrupt             smp_threshold_interrupt
+#endif
+
+#ifdef CONFIG_X86_MCE_AMD
+apicinterrupt DEFERRED_ERROR_VECTOR            deferred_error_interrupt        smp_deferred_error_interrupt
 #endif
 
 #ifdef CONFIG_X86_THERMAL_VECTOR
-apicinterrupt THERMAL_APIC_VECTOR \
-       thermal_interrupt smp_thermal_interrupt
+apicinterrupt THERMAL_APIC_VECTOR              thermal_interrupt               smp_thermal_interrupt
 #endif
 
 #ifdef CONFIG_SMP
-apicinterrupt CALL_FUNCTION_SINGLE_VECTOR \
-       call_function_single_interrupt smp_call_function_single_interrupt
-apicinterrupt CALL_FUNCTION_VECTOR \
-       call_function_interrupt smp_call_function_interrupt
-apicinterrupt RESCHEDULE_VECTOR \
-       reschedule_interrupt smp_reschedule_interrupt
+apicinterrupt CALL_FUNCTION_SINGLE_VECTOR      call_function_single_interrupt  smp_call_function_single_interrupt
+apicinterrupt CALL_FUNCTION_VECTOR             call_function_interrupt         smp_call_function_interrupt
+apicinterrupt RESCHEDULE_VECTOR                        reschedule_interrupt            smp_reschedule_interrupt
 #endif
 
-apicinterrupt ERROR_APIC_VECTOR \
-       error_interrupt smp_error_interrupt
-apicinterrupt SPURIOUS_APIC_VECTOR \
-       spurious_interrupt smp_spurious_interrupt
+apicinterrupt ERROR_APIC_VECTOR                        error_interrupt                 smp_error_interrupt
+apicinterrupt SPURIOUS_APIC_VECTOR             spurious_interrupt              smp_spurious_interrupt
 
 #ifdef CONFIG_IRQ_WORK
-apicinterrupt IRQ_WORK_VECTOR \
-       irq_work_interrupt smp_irq_work_interrupt
+apicinterrupt IRQ_WORK_VECTOR                  irq_work_interrupt              smp_irq_work_interrupt
 #endif
 
 /*
@@ -971,100 +824,87 @@ ENTRY(\sym)
        .error "using shift_ist requires paranoid=1"
        .endif
 
-       .if \has_error_code
-       XCPT_FRAME
-       .else
-       INTR_FRAME
-       .endif
-
        ASM_CLAC
        PARAVIRT_ADJUST_EXCEPTION_FRAME
 
        .ifeq \has_error_code
-       pushq_cfi $-1                   /* ORIG_RAX: no syscall to restart */
+       pushq   $-1                             /* ORIG_RAX: no syscall to restart */
        .endif
 
        ALLOC_PT_GPREGS_ON_STACK
 
        .if \paranoid
        .if \paranoid == 1
-       CFI_REMEMBER_STATE
-       testl $3, CS(%rsp)              /* If coming from userspace, switch */
-       jnz 1f                          /* stacks. */
+       testb   $3, CS(%rsp)                    /* If coming from userspace, switch stacks */
+       jnz     1f
        .endif
-       call paranoid_entry
+       call    paranoid_entry
        .else
-       call error_entry
+       call    error_entry
        .endif
        /* returned flag: ebx=0: need swapgs on exit, ebx=1: don't need it */
 
-       DEFAULT_FRAME 0
-
        .if \paranoid
        .if \shift_ist != -1
-       TRACE_IRQS_OFF_DEBUG            /* reload IDT in case of recursion */
+       TRACE_IRQS_OFF_DEBUG                    /* reload IDT in case of recursion */
        .else
        TRACE_IRQS_OFF
        .endif
        .endif
 
-       movq %rsp,%rdi                  /* pt_regs pointer */
+       movq    %rsp, %rdi                      /* pt_regs pointer */
 
        .if \has_error_code
-       movq ORIG_RAX(%rsp),%rsi        /* get error code */
-       movq $-1,ORIG_RAX(%rsp)         /* no syscall to restart */
+       movq    ORIG_RAX(%rsp), %rsi            /* get error code */
+       movq    $-1, ORIG_RAX(%rsp)             /* no syscall to restart */
        .else
-       xorl %esi,%esi                  /* no error code */
+       xorl    %esi, %esi                      /* no error code */
        .endif
 
        .if \shift_ist != -1
-       subq $EXCEPTION_STKSZ, CPU_TSS_IST(\shift_ist)
+       subq    $EXCEPTION_STKSZ, CPU_TSS_IST(\shift_ist)
        .endif
 
-       call \do_sym
+       call    \do_sym
 
        .if \shift_ist != -1
-       addq $EXCEPTION_STKSZ, CPU_TSS_IST(\shift_ist)
+       addq    $EXCEPTION_STKSZ, CPU_TSS_IST(\shift_ist)
        .endif
 
        /* these procedures expect "no swapgs" flag in ebx */
        .if \paranoid
-       jmp paranoid_exit
+       jmp     paranoid_exit
        .else
-       jmp error_exit
+       jmp     error_exit
        .endif
 
        .if \paranoid == 1
-       CFI_RESTORE_STATE
        /*
         * Paranoid entry from userspace.  Switch stacks and treat it
         * as a normal entry.  This means that paranoid handlers
         * run in real process context if user_mode(regs).
         */
 1:
-       call error_entry
+       call    error_entry
 
-       DEFAULT_FRAME 0
 
-       movq %rsp,%rdi                  /* pt_regs pointer */
-       call sync_regs
-       movq %rax,%rsp                  /* switch stack */
+       movq    %rsp, %rdi                      /* pt_regs pointer */
+       call    sync_regs
+       movq    %rax, %rsp                      /* switch stack */
 
-       movq %rsp,%rdi                  /* pt_regs pointer */
+       movq    %rsp, %rdi                      /* pt_regs pointer */
 
        .if \has_error_code
-       movq ORIG_RAX(%rsp),%rsi        /* get error code */
-       movq $-1,ORIG_RAX(%rsp)         /* no syscall to restart */
+       movq    ORIG_RAX(%rsp), %rsi            /* get error code */
+       movq    $-1, ORIG_RAX(%rsp)             /* no syscall to restart */
        .else
-       xorl %esi,%esi                  /* no error code */
+       xorl    %esi, %esi                      /* no error code */
        .endif
 
-       call \do_sym
+       call    \do_sym
 
-       jmp error_exit                  /* %ebx: no swapgs flag */
+       jmp     error_exit                      /* %ebx: no swapgs flag */
        .endif
-
-       CFI_ENDPROC
 END(\sym)
 .endm
 
@@ -1079,65 +919,58 @@ idtentry \sym \do_sym has_error_code=\has_error_code
 .endm
 #endif
 
-idtentry divide_error do_divide_error has_error_code=0
-idtentry overflow do_overflow has_error_code=0
-idtentry bounds do_bounds has_error_code=0
-idtentry invalid_op do_invalid_op has_error_code=0
-idtentry device_not_available do_device_not_available has_error_code=0
-idtentry double_fault do_double_fault has_error_code=1 paranoid=2
-idtentry coprocessor_segment_overrun do_coprocessor_segment_overrun has_error_code=0
-idtentry invalid_TSS do_invalid_TSS has_error_code=1
-idtentry segment_not_present do_segment_not_present has_error_code=1
-idtentry spurious_interrupt_bug do_spurious_interrupt_bug has_error_code=0
-idtentry coprocessor_error do_coprocessor_error has_error_code=0
-idtentry alignment_check do_alignment_check has_error_code=1
-idtentry simd_coprocessor_error do_simd_coprocessor_error has_error_code=0
-
-
-       /* Reload gs selector with exception handling */
-       /* edi:  new selector */
+idtentry divide_error                  do_divide_error                 has_error_code=0
+idtentry overflow                      do_overflow                     has_error_code=0
+idtentry bounds                                do_bounds                       has_error_code=0
+idtentry invalid_op                    do_invalid_op                   has_error_code=0
+idtentry device_not_available          do_device_not_available         has_error_code=0
+idtentry double_fault                  do_double_fault                 has_error_code=1 paranoid=2
+idtentry coprocessor_segment_overrun   do_coprocessor_segment_overrun  has_error_code=0
+idtentry invalid_TSS                   do_invalid_TSS                  has_error_code=1
+idtentry segment_not_present           do_segment_not_present          has_error_code=1
+idtentry spurious_interrupt_bug                do_spurious_interrupt_bug       has_error_code=0
+idtentry coprocessor_error             do_coprocessor_error            has_error_code=0
+idtentry alignment_check               do_alignment_check              has_error_code=1
+idtentry simd_coprocessor_error                do_simd_coprocessor_error       has_error_code=0
+
+
+       /*
+        * Reload gs selector with exception handling
+        * edi:  new selector
+        */
 ENTRY(native_load_gs_index)
-       CFI_STARTPROC
-       pushfq_cfi
+       pushfq
        DISABLE_INTERRUPTS(CLBR_ANY & ~CLBR_RDI)
        SWAPGS
 gs_change:
-       movl %edi,%gs
-2:     mfence          /* workaround */
+       movl    %edi, %gs
+2:     mfence                                  /* workaround */
        SWAPGS
-       popfq_cfi
+       popfq
        ret
-       CFI_ENDPROC
 END(native_load_gs_index)
 
-       _ASM_EXTABLE(gs_change,bad_gs)
-       .section .fixup,"ax"
+       _ASM_EXTABLE(gs_change, bad_gs)
+       .section .fixup, "ax"
        /* running with kernelgs */
 bad_gs:
-       SWAPGS                  /* switch back to user gs */
-       xorl %eax,%eax
-       movl %eax,%gs
-       jmp  2b
+       SWAPGS                                  /* switch back to user gs */
+       xorl    %eax, %eax
+       movl    %eax, %gs
+       jmp     2b
        .previous
 
 /* Call softirq on interrupt stack. Interrupts are off. */
 ENTRY(do_softirq_own_stack)
-       CFI_STARTPROC
-       pushq_cfi %rbp
-       CFI_REL_OFFSET rbp,0
-       mov  %rsp,%rbp
-       CFI_DEF_CFA_REGISTER rbp
-       incl PER_CPU_VAR(irq_count)
-       cmove PER_CPU_VAR(irq_stack_ptr),%rsp
-       push  %rbp                      # backlink for old unwinder
-       call __do_softirq
+       pushq   %rbp
+       mov     %rsp, %rbp
+       incl    PER_CPU_VAR(irq_count)
+       cmove   PER_CPU_VAR(irq_stack_ptr), %rsp
+       push    %rbp                            /* frame pointer backlink */
+       call    __do_softirq
        leaveq
-       CFI_RESTORE             rbp
-       CFI_DEF_CFA_REGISTER    rsp
-       CFI_ADJUST_CFA_OFFSET   -8
-       decl PER_CPU_VAR(irq_count)
+       decl    PER_CPU_VAR(irq_count)
        ret
-       CFI_ENDPROC
 END(do_softirq_own_stack)
 
 #ifdef CONFIG_XEN
@@ -1156,29 +989,24 @@ idtentry xen_hypervisor_callback xen_do_hypervisor_callback has_error_code=0
  * existing activation in its critical region -- if so, we pop the current
  * activation and restart the handler using the previous one.
  */
-ENTRY(xen_do_hypervisor_callback)   # do_hypervisor_callback(struct *pt_regs)
-       CFI_STARTPROC
+ENTRY(xen_do_hypervisor_callback)              /* do_hypervisor_callback(struct *pt_regs) */
+
 /*
  * Since we don't modify %rdi, evtchn_do_upall(struct *pt_regs) will
  * see the correct pointer to the pt_regs
  */
-       movq %rdi, %rsp            # we don't return, adjust the stack frame
-       CFI_ENDPROC
-       DEFAULT_FRAME
-11:    incl PER_CPU_VAR(irq_count)
-       movq %rsp,%rbp
-       CFI_DEF_CFA_REGISTER rbp
-       cmovzq PER_CPU_VAR(irq_stack_ptr),%rsp
-       pushq %rbp                      # backlink for old unwinder
-       call xen_evtchn_do_upcall
-       popq %rsp
-       CFI_DEF_CFA_REGISTER rsp
-       decl PER_CPU_VAR(irq_count)
+       movq    %rdi, %rsp                      /* we don't return, adjust the stack frame */
+11:    incl    PER_CPU_VAR(irq_count)
+       movq    %rsp, %rbp
+       cmovzq  PER_CPU_VAR(irq_stack_ptr), %rsp
+       pushq   %rbp                            /* frame pointer backlink */
+       call    xen_evtchn_do_upcall
+       popq    %rsp
+       decl    PER_CPU_VAR(irq_count)
 #ifndef CONFIG_PREEMPT
-       call xen_maybe_preempt_hcall
+       call    xen_maybe_preempt_hcall
 #endif
-       jmp  error_exit
-       CFI_ENDPROC
+       jmp     error_exit
 END(xen_do_hypervisor_callback)
 
 /*
@@ -1195,51 +1023,35 @@ END(xen_do_hypervisor_callback)
  * with its current contents: any discrepancy means we in category 1.
  */
 ENTRY(xen_failsafe_callback)
-       INTR_FRAME 1 (6*8)
-       /*CFI_REL_OFFSET gs,GS*/
-       /*CFI_REL_OFFSET fs,FS*/
-       /*CFI_REL_OFFSET es,ES*/
-       /*CFI_REL_OFFSET ds,DS*/
-       CFI_REL_OFFSET r11,8
-       CFI_REL_OFFSET rcx,0
-       movw %ds,%cx
-       cmpw %cx,0x10(%rsp)
-       CFI_REMEMBER_STATE
-       jne 1f
-       movw %es,%cx
-       cmpw %cx,0x18(%rsp)
-       jne 1f
-       movw %fs,%cx
-       cmpw %cx,0x20(%rsp)
-       jne 1f
-       movw %gs,%cx
-       cmpw %cx,0x28(%rsp)
-       jne 1f
+       movl    %ds, %ecx
+       cmpw    %cx, 0x10(%rsp)
+       jne     1f
+       movl    %es, %ecx
+       cmpw    %cx, 0x18(%rsp)
+       jne     1f
+       movl    %fs, %ecx
+       cmpw    %cx, 0x20(%rsp)
+       jne     1f
+       movl    %gs, %ecx
+       cmpw    %cx, 0x28(%rsp)
+       jne     1f
        /* All segments match their saved values => Category 2 (Bad IRET). */
-       movq (%rsp),%rcx
-       CFI_RESTORE rcx
-       movq 8(%rsp),%r11
-       CFI_RESTORE r11
-       addq $0x30,%rsp
-       CFI_ADJUST_CFA_OFFSET -0x30
-       pushq_cfi $0    /* RIP */
-       pushq_cfi %r11
-       pushq_cfi %rcx
-       jmp general_protection
-       CFI_RESTORE_STATE
+       movq    (%rsp), %rcx
+       movq    8(%rsp), %r11
+       addq    $0x30, %rsp
+       pushq   $0                              /* RIP */
+       pushq   %r11
+       pushq   %rcx
+       jmp     general_protection
 1:     /* Segment mismatch => Category 1 (Bad segment). Retry the IRET. */
-       movq (%rsp),%rcx
-       CFI_RESTORE rcx
-       movq 8(%rsp),%r11
-       CFI_RESTORE r11
-       addq $0x30,%rsp
-       CFI_ADJUST_CFA_OFFSET -0x30
-       pushq_cfi $-1 /* orig_ax = -1 => not a system call */
+       movq    (%rsp), %rcx
+       movq    8(%rsp), %r11
+       addq    $0x30, %rsp
+       pushq   $-1 /* orig_ax = -1 => not a system call */
        ALLOC_PT_GPREGS_ON_STACK
        SAVE_C_REGS
        SAVE_EXTRA_REGS
-       jmp error_exit
-       CFI_ENDPROC
+       jmp     error_exit
 END(xen_failsafe_callback)
 
 apicinterrupt3 HYPERVISOR_CALLBACK_VECTOR \
@@ -1252,21 +1064,25 @@ apicinterrupt3 HYPERVISOR_CALLBACK_VECTOR \
        hyperv_callback_vector hyperv_vector_handler
 #endif /* CONFIG_HYPERV */
 
-idtentry debug do_debug has_error_code=0 paranoid=1 shift_ist=DEBUG_STACK
-idtentry int3 do_int3 has_error_code=0 paranoid=1 shift_ist=DEBUG_STACK
-idtentry stack_segment do_stack_segment has_error_code=1
+idtentry debug                 do_debug                has_error_code=0        paranoid=1 shift_ist=DEBUG_STACK
+idtentry int3                  do_int3                 has_error_code=0        paranoid=1 shift_ist=DEBUG_STACK
+idtentry stack_segment         do_stack_segment        has_error_code=1
+
 #ifdef CONFIG_XEN
-idtentry xen_debug do_debug has_error_code=0
-idtentry xen_int3 do_int3 has_error_code=0
-idtentry xen_stack_segment do_stack_segment has_error_code=1
+idtentry xen_debug             do_debug                has_error_code=0
+idtentry xen_int3              do_int3                 has_error_code=0
+idtentry xen_stack_segment     do_stack_segment        has_error_code=1
 #endif
-idtentry general_protection do_general_protection has_error_code=1
-trace_idtentry page_fault do_page_fault has_error_code=1
+
+idtentry general_protection    do_general_protection   has_error_code=1
+trace_idtentry page_fault      do_page_fault           has_error_code=1
+
 #ifdef CONFIG_KVM_GUEST
-idtentry async_page_fault do_async_page_fault has_error_code=1
+idtentry async_page_fault      do_async_page_fault     has_error_code=1
 #endif
+
 #ifdef CONFIG_X86_MCE
-idtentry machine_check has_error_code=0 paranoid=1 do_sym=*machine_check_vector(%rip)
+idtentry machine_check                                 has_error_code=0        paranoid=1 do_sym=*machine_check_vector(%rip)
 #endif
 
 /*
@@ -1275,19 +1091,17 @@ idtentry machine_check has_error_code=0 paranoid=1 do_sym=*machine_check_vector(
  * Return: ebx=0: need swapgs on exit, ebx=1: otherwise
  */
 ENTRY(paranoid_entry)
-       XCPT_FRAME 1 15*8
        cld
        SAVE_C_REGS 8
        SAVE_EXTRA_REGS 8
-       movl $1,%ebx
-       movl $MSR_GS_BASE,%ecx
+       movl    $1, %ebx
+       movl    $MSR_GS_BASE, %ecx
        rdmsr
-       testl %edx,%edx
-       js 1f   /* negative -> in kernel */
+       testl   %edx, %edx
+       js      1f                              /* negative -> in kernel */
        SWAPGS
-       xorl %ebx,%ebx
+       xorl    %ebx, %ebx
 1:     ret
-       CFI_ENDPROC
 END(paranoid_entry)
 
 /*
@@ -1299,17 +1113,17 @@ END(paranoid_entry)
  * in syscall entry), so checking for preemption here would
  * be complicated.  Fortunately, we there's no good reason
  * to try to handle preemption here.
+ *
+ * On entry, ebx is "no swapgs" flag (1: don't need swapgs, 0: need it)
  */
-/* On entry, ebx is "no swapgs" flag (1: don't need swapgs, 0: need it) */
 ENTRY(paranoid_exit)
-       DEFAULT_FRAME
        DISABLE_INTERRUPTS(CLBR_NONE)
        TRACE_IRQS_OFF_DEBUG
-       testl %ebx,%ebx                         /* swapgs needed? */
-       jnz paranoid_exit_no_swapgs
+       testl   %ebx, %ebx                      /* swapgs needed? */
+       jnz     paranoid_exit_no_swapgs
        TRACE_IRQS_IRETQ
        SWAPGS_UNSAFE_STACK
-       jmp paranoid_exit_restore
+       jmp     paranoid_exit_restore
 paranoid_exit_no_swapgs:
        TRACE_IRQS_IRETQ_DEBUG
 paranoid_exit_restore:
@@ -1317,24 +1131,24 @@ paranoid_exit_restore:
        RESTORE_C_REGS
        REMOVE_PT_GPREGS_FROM_STACK 8
        INTERRUPT_RETURN
-       CFI_ENDPROC
 END(paranoid_exit)
 
 /*
  * Save all registers in pt_regs, and switch gs if needed.
- * Return: ebx=0: need swapgs on exit, ebx=1: otherwise
+ * Return: EBX=0: came from user mode; EBX=1: otherwise
  */
 ENTRY(error_entry)
-       XCPT_FRAME 1 15*8
        cld
        SAVE_C_REGS 8
        SAVE_EXTRA_REGS 8
-       xorl %ebx,%ebx
-       testl $3,CS+8(%rsp)
-       je error_kernelspace
-error_swapgs:
+       xorl    %ebx, %ebx
+       testb   $3, CS+8(%rsp)
+       jz      error_kernelspace
+
+       /* We entered from user mode */
        SWAPGS
-error_sti:
+
+error_entry_done:
        TRACE_IRQS_OFF
        ret
 
@@ -1345,56 +1159,66 @@ error_sti:
         * for these here too.
         */
 error_kernelspace:
-       CFI_REL_OFFSET rcx, RCX+8
-       incl %ebx
-       leaq native_irq_return_iret(%rip),%rcx
-       cmpq %rcx,RIP+8(%rsp)
-       je error_bad_iret
-       movl %ecx,%eax  /* zero extend */
-       cmpq %rax,RIP+8(%rsp)
-       je bstep_iret
-       cmpq $gs_change,RIP+8(%rsp)
-       je error_swapgs
-       jmp error_sti
+       incl    %ebx
+       leaq    native_irq_return_iret(%rip), %rcx
+       cmpq    %rcx, RIP+8(%rsp)
+       je      error_bad_iret
+       movl    %ecx, %eax                      /* zero extend */
+       cmpq    %rax, RIP+8(%rsp)
+       je      bstep_iret
+       cmpq    $gs_change, RIP+8(%rsp)
+       jne     error_entry_done
+
+       /*
+        * hack: gs_change can fail with user gsbase.  If this happens, fix up
+        * gsbase and proceed.  We'll fix up the exception and land in
+        * gs_change's error handler with kernel gsbase.
+        */
+       SWAPGS
+       jmp     error_entry_done
 
 bstep_iret:
        /* Fix truncated RIP */
-       movq %rcx,RIP+8(%rsp)
+       movq    %rcx, RIP+8(%rsp)
        /* fall through */
 
 error_bad_iret:
+       /*
+        * We came from an IRET to user mode, so we have user gsbase.
+        * Switch to kernel gsbase:
+        */
        SWAPGS
-       mov %rsp,%rdi
-       call fixup_bad_iret
-       mov %rax,%rsp
-       decl %ebx       /* Return to usergs */
-       jmp error_sti
-       CFI_ENDPROC
+
+       /*
+        * Pretend that the exception came from user mode: set up pt_regs
+        * as if we faulted immediately after IRET and clear EBX so that
+        * error_exit knows that we will be returning to user mode.
+        */
+       mov     %rsp, %rdi
+       call    fixup_bad_iret
+       mov     %rax, %rsp
+       decl    %ebx
+       jmp     error_entry_done
 END(error_entry)
 
 
-/* On entry, ebx is "no swapgs" flag (1: don't need swapgs, 0: need it) */
+/*
+ * On entry, EBS is a "return to kernel mode" flag:
+ *   1: already in kernel mode, don't need SWAPGS
+ *   0: user gsbase is loaded, we need SWAPGS and standard preparation for return to usermode
+ */
 ENTRY(error_exit)
-       DEFAULT_FRAME
-       movl %ebx,%eax
+       movl    %ebx, %eax
        RESTORE_EXTRA_REGS
        DISABLE_INTERRUPTS(CLBR_NONE)
        TRACE_IRQS_OFF
-       GET_THREAD_INFO(%rcx)
-       testl %eax,%eax
-       jne retint_kernel
-       LOCKDEP_SYS_EXIT_IRQ
-       movl TI_flags(%rcx),%edx
-       movl $_TIF_WORK_MASK,%edi
-       andl %edi,%edx
-       jnz retint_careful
-       jmp retint_swapgs
-       CFI_ENDPROC
+       testl   %eax, %eax
+       jnz     retint_kernel
+       jmp     retint_user
 END(error_exit)
 
 /* Runs on exception stack */
 ENTRY(nmi)
-       INTR_FRAME
        PARAVIRT_ADJUST_EXCEPTION_FRAME
        /*
         * We allow breakpoints in NMIs. If a breakpoint occurs, then
@@ -1429,22 +1253,21 @@ ENTRY(nmi)
         */
 
        /* Use %rdx as our temp variable throughout */
-       pushq_cfi %rdx
-       CFI_REL_OFFSET rdx, 0
+       pushq   %rdx
 
        /*
         * If %cs was not the kernel segment, then the NMI triggered in user
         * space, which means it is definitely not nested.
         */
-       cmpl $__KERNEL_CS, 16(%rsp)
-       jne first_nmi
+       cmpl    $__KERNEL_CS, 16(%rsp)
+       jne     first_nmi
 
        /*
         * Check the special variable on the stack to see if NMIs are
         * executing.
         */
-       cmpl $1, -8(%rsp)
-       je nested_nmi
+       cmpl    $1, -8(%rsp)
+       je      nested_nmi
 
        /*
         * Now test if the previous stack was an NMI stack.
@@ -1458,51 +1281,46 @@ ENTRY(nmi)
        cmpq    %rdx, 4*8(%rsp)
        /* If the stack pointer is above the NMI stack, this is a normal NMI */
        ja      first_nmi
+
        subq    $EXCEPTION_STKSZ, %rdx
        cmpq    %rdx, 4*8(%rsp)
        /* If it is below the NMI stack, it is a normal NMI */
        jb      first_nmi
        /* Ah, it is within the NMI stack, treat it as nested */
 
-       CFI_REMEMBER_STATE
-
 nested_nmi:
        /*
         * Do nothing if we interrupted the fixup in repeat_nmi.
         * It's about to repeat the NMI handler, so we are fine
         * with ignoring this one.
         */
-       movq $repeat_nmi, %rdx
-       cmpq 8(%rsp), %rdx
-       ja 1f
-       movq $end_repeat_nmi, %rdx
-       cmpq 8(%rsp), %rdx
-       ja nested_nmi_out
+       movq    $repeat_nmi, %rdx
+       cmpq    8(%rsp), %rdx
+       ja      1f
+       movq    $end_repeat_nmi, %rdx
+       cmpq    8(%rsp), %rdx
+       ja      nested_nmi_out
 
 1:
        /* Set up the interrupted NMIs stack to jump to repeat_nmi */
-       leaq -1*8(%rsp), %rdx
-       movq %rdx, %rsp
-       CFI_ADJUST_CFA_OFFSET 1*8
-       leaq -10*8(%rsp), %rdx
-       pushq_cfi $__KERNEL_DS
-       pushq_cfi %rdx
-       pushfq_cfi
-       pushq_cfi $__KERNEL_CS
-       pushq_cfi $repeat_nmi
+       leaq    -1*8(%rsp), %rdx
+       movq    %rdx, %rsp
+       leaq    -10*8(%rsp), %rdx
+       pushq   $__KERNEL_DS
+       pushq   %rdx
+       pushfq
+       pushq   $__KERNEL_CS
+       pushq   $repeat_nmi
 
        /* Put stack back */
-       addq $(6*8), %rsp
-       CFI_ADJUST_CFA_OFFSET -6*8
+       addq    $(6*8), %rsp
 
 nested_nmi_out:
-       popq_cfi %rdx
-       CFI_RESTORE rdx
+       popq    %rdx
 
        /* No need to check faults here */
        INTERRUPT_RETURN
 
-       CFI_RESTORE_STATE
 first_nmi:
        /*
         * Because nested NMIs will use the pushed location that we
@@ -1540,23 +1358,18 @@ first_nmi:
         * is also used by nested NMIs and can not be trusted on exit.
         */
        /* Do not pop rdx, nested NMIs will corrupt that part of the stack */
-       movq (%rsp), %rdx
-       CFI_RESTORE rdx
+       movq    (%rsp), %rdx
 
        /* Set the NMI executing variable on the stack. */
-       pushq_cfi $1
+       pushq   $1
 
-       /*
-        * Leave room for the "copied" frame
-        */
-       subq $(5*8), %rsp
-       CFI_ADJUST_CFA_OFFSET 5*8
+       /* Leave room for the "copied" frame */
+       subq    $(5*8), %rsp
 
        /* Copy the stack frame to the Saved frame */
        .rept 5
-       pushq_cfi 11*8(%rsp)
+       pushq   11*8(%rsp)
        .endr
-       CFI_DEF_CFA_OFFSET 5*8
 
        /* Everything up to here is safe from nested NMIs */
 
@@ -1575,16 +1388,14 @@ repeat_nmi:
         * is benign for the non-repeat case, where 1 was pushed just above
         * to this very stack slot).
         */
-       movq $1, 10*8(%rsp)
+       movq    $1, 10*8(%rsp)
 
        /* Make another copy, this one may be modified by nested NMIs */
-       addq $(10*8), %rsp
-       CFI_ADJUST_CFA_OFFSET -10*8
+       addq    $(10*8), %rsp
        .rept 5
-       pushq_cfi -6*8(%rsp)
+       pushq   -6*8(%rsp)
        .endr
-       subq $(5*8), %rsp
-       CFI_DEF_CFA_OFFSET 5*8
+       subq    $(5*8), %rsp
 end_repeat_nmi:
 
        /*
@@ -1592,7 +1403,7 @@ end_repeat_nmi:
         * NMI if the first NMI took an exception and reset our iret stack
         * so that we repeat another NMI.
         */
-       pushq_cfi $-1           /* ORIG_RAX: no syscall to restart */
+       pushq   $-1                             /* ORIG_RAX: no syscall to restart */
        ALLOC_PT_GPREGS_ON_STACK
 
        /*
@@ -1602,8 +1413,7 @@ end_repeat_nmi:
         * setting NEED_RESCHED or anything that normal interrupts and
         * exceptions might do.
         */
-       call paranoid_entry
-       DEFAULT_FRAME 0
+       call    paranoid_entry
 
        /*
         * Save off the CR2 register. If we take a page fault in the NMI then
@@ -1614,22 +1424,21 @@ end_repeat_nmi:
         * origin fault. Save it off and restore it if it changes.
         * Use the r12 callee-saved register.
         */
-       movq %cr2, %r12
+       movq    %cr2, %r12
 
        /* paranoidentry do_nmi, 0; without TRACE_IRQS_OFF */
-       movq %rsp,%rdi
-       movq $-1,%rsi
-       call do_nmi
+       movq    %rsp, %rdi
+       movq    $-1, %rsi
+       call    do_nmi
 
        /* Did the NMI take a page fault? Restore cr2 if it did */
-       movq %cr2, %rcx
-       cmpq %rcx, %r12
-       je 1f
-       movq %r12, %cr2
+       movq    %cr2, %rcx
+       cmpq    %rcx, %r12
+       je      1f
+       movq    %r12, %cr2
 1:
-       
-       testl %ebx,%ebx                         /* swapgs needed? */
-       jnz nmi_restore
+       testl   %ebx, %ebx                      /* swapgs needed? */
+       jnz     nmi_restore
 nmi_swapgs:
        SWAPGS_UNSAFE_STACK
 nmi_restore:
@@ -1639,15 +1448,11 @@ nmi_restore:
        REMOVE_PT_GPREGS_FROM_STACK 6*8
 
        /* Clear the NMI executing stack variable */
-       movq $0, 5*8(%rsp)
-       jmp irq_return
-       CFI_ENDPROC
+       movq    $0, 5*8(%rsp)
+       INTERRUPT_RETURN
 END(nmi)
 
 ENTRY(ignore_sysret)
-       CFI_STARTPROC
-       mov $-ENOSYS,%eax
+       mov     $-ENOSYS, %eax
        sysret
-       CFI_ENDPROC
 END(ignore_sysret)
-
diff --git a/arch/x86/entry/entry_64_compat.S b/arch/x86/entry/entry_64_compat.S
new file mode 100644 (file)
index 0000000..bb187a6
--- /dev/null
@@ -0,0 +1,556 @@
+/*
+ * Compatibility mode system call entry point for x86-64.
+ *
+ * Copyright 2000-2002 Andi Kleen, SuSE Labs.
+ */
+#include "calling.h"
+#include <asm/asm-offsets.h>
+#include <asm/current.h>
+#include <asm/errno.h>
+#include <asm/ia32_unistd.h>
+#include <asm/thread_info.h>
+#include <asm/segment.h>
+#include <asm/irqflags.h>
+#include <asm/asm.h>
+#include <asm/smap.h>
+#include <linux/linkage.h>
+#include <linux/err.h>
+
+/* Avoid __ASSEMBLER__'ifying <linux/audit.h> just for this.  */
+#include <linux/elf-em.h>
+#define AUDIT_ARCH_I386                (EM_386|__AUDIT_ARCH_LE)
+#define __AUDIT_ARCH_LE                0x40000000
+
+#ifndef CONFIG_AUDITSYSCALL
+# define sysexit_audit         ia32_ret_from_sys_call
+# define sysretl_audit         ia32_ret_from_sys_call
+#endif
+
+       .section .entry.text, "ax"
+
+#ifdef CONFIG_PARAVIRT
+ENTRY(native_usergs_sysret32)
+       swapgs
+       sysretl
+ENDPROC(native_usergs_sysret32)
+#endif
+
+/*
+ * 32-bit SYSENTER instruction entry.
+ *
+ * SYSENTER loads ss, rsp, cs, and rip from previously programmed MSRs.
+ * IF and VM in rflags are cleared (IOW: interrupts are off).
+ * SYSENTER does not save anything on the stack,
+ * and does not save old rip (!!!) and rflags.
+ *
+ * Arguments:
+ * eax  system call number
+ * ebx  arg1
+ * ecx  arg2
+ * edx  arg3
+ * esi  arg4
+ * edi  arg5
+ * ebp  user stack
+ * 0(%ebp) arg6
+ *
+ * This is purely a fast path. For anything complicated we use the int 0x80
+ * path below. We set up a complete hardware stack frame to share code
+ * with the int 0x80 path.
+ */
+ENTRY(entry_SYSENTER_compat)
+       /*
+        * Interrupts are off on entry.
+        * We do not frame this tiny irq-off block with TRACE_IRQS_OFF/ON,
+        * it is too small to ever cause noticeable irq latency.
+        */
+       SWAPGS_UNSAFE_STACK
+       movq    PER_CPU_VAR(cpu_current_top_of_stack), %rsp
+       ENABLE_INTERRUPTS(CLBR_NONE)
+
+       /* Zero-extending 32-bit regs, do not remove */
+       movl    %ebp, %ebp
+       movl    %eax, %eax
+
+       movl    ASM_THREAD_INFO(TI_sysenter_return, %rsp, 0), %r10d
+
+       /* Construct struct pt_regs on stack */
+       pushq   $__USER32_DS            /* pt_regs->ss */
+       pushq   %rbp                    /* pt_regs->sp */
+       pushfq                          /* pt_regs->flags */
+       pushq   $__USER32_CS            /* pt_regs->cs */
+       pushq   %r10                    /* pt_regs->ip = thread_info->sysenter_return */
+       pushq   %rax                    /* pt_regs->orig_ax */
+       pushq   %rdi                    /* pt_regs->di */
+       pushq   %rsi                    /* pt_regs->si */
+       pushq   %rdx                    /* pt_regs->dx */
+       pushq   %rcx                    /* pt_regs->cx */
+       pushq   $-ENOSYS                /* pt_regs->ax */
+       cld
+       sub     $(10*8), %rsp /* pt_regs->r8-11, bp, bx, r12-15 not saved */
+
+       /*
+        * no need to do an access_ok check here because rbp has been
+        * 32-bit zero extended
+        */
+       ASM_STAC
+1:     movl    (%rbp), %ebp
+       _ASM_EXTABLE(1b, ia32_badarg)
+       ASM_CLAC
+
+       /*
+        * Sysenter doesn't filter flags, so we need to clear NT
+        * ourselves.  To save a few cycles, we can check whether
+        * NT was set instead of doing an unconditional popfq.
+        */
+       testl   $X86_EFLAGS_NT, EFLAGS(%rsp)
+       jnz     sysenter_fix_flags
+sysenter_flags_fixed:
+
+       orl     $TS_COMPAT, ASM_THREAD_INFO(TI_status, %rsp, SIZEOF_PTREGS)
+       testl   $_TIF_WORK_SYSCALL_ENTRY, ASM_THREAD_INFO(TI_flags, %rsp, SIZEOF_PTREGS)
+       jnz     sysenter_tracesys
+
+sysenter_do_call:
+       /* 32-bit syscall -> 64-bit C ABI argument conversion */
+       movl    %edi, %r8d              /* arg5 */
+       movl    %ebp, %r9d              /* arg6 */
+       xchg    %ecx, %esi              /* rsi:arg2, rcx:arg4 */
+       movl    %ebx, %edi              /* arg1 */
+       movl    %edx, %edx              /* arg3 (zero extension) */
+sysenter_dispatch:
+       cmpq    $(IA32_NR_syscalls-1), %rax
+       ja      1f
+       call    *ia32_sys_call_table(, %rax, 8)
+       movq    %rax, RAX(%rsp)
+1:
+       DISABLE_INTERRUPTS(CLBR_NONE)
+       TRACE_IRQS_OFF
+       testl   $_TIF_ALLWORK_MASK, ASM_THREAD_INFO(TI_flags, %rsp, SIZEOF_PTREGS)
+       jnz     sysexit_audit
+sysexit_from_sys_call:
+       /*
+        * NB: SYSEXIT is not obviously safe for 64-bit kernels -- an
+        * NMI between STI and SYSEXIT has poorly specified behavior,
+        * and and NMI followed by an IRQ with usergs is fatal.  So
+        * we just pretend we're using SYSEXIT but we really use
+        * SYSRETL instead.
+        *
+        * This code path is still called 'sysexit' because it pairs
+        * with 'sysenter' and it uses the SYSENTER calling convention.
+        */
+       andl    $~TS_COMPAT, ASM_THREAD_INFO(TI_status, %rsp, SIZEOF_PTREGS)
+       movl    RIP(%rsp), %ecx         /* User %eip */
+       RESTORE_RSI_RDI
+       xorl    %edx, %edx              /* Do not leak kernel information */
+       xorq    %r8, %r8
+       xorq    %r9, %r9
+       xorq    %r10, %r10
+       movl    EFLAGS(%rsp), %r11d     /* User eflags */
+       TRACE_IRQS_ON
+
+       /*
+        * SYSRETL works even on Intel CPUs.  Use it in preference to SYSEXIT,
+        * since it avoids a dicey window with interrupts enabled.
+        */
+       movl    RSP(%rsp), %esp
+
+       /*
+        * USERGS_SYSRET32 does:
+        *  gsbase = user's gs base
+        *  eip = ecx
+        *  rflags = r11
+        *  cs = __USER32_CS
+        *  ss = __USER_DS
+        *
+        * The prologue set RIP(%rsp) to VDSO32_SYSENTER_RETURN, which does:
+        *
+        *  pop %ebp
+        *  pop %edx
+        *  pop %ecx
+        *
+        * Therefore, we invoke SYSRETL with EDX and R8-R10 zeroed to
+        * avoid info leaks.  R11 ends up with VDSO32_SYSENTER_RETURN's
+        * address (already known to user code), and R12-R15 are
+        * callee-saved and therefore don't contain any interesting
+        * kernel data.
+        */
+       USERGS_SYSRET32
+
+#ifdef CONFIG_AUDITSYSCALL
+       .macro auditsys_entry_common
+       /*
+        * At this point, registers hold syscall args in the 32-bit syscall ABI:
+        * EAX is syscall number, the 6 args are in EBX,ECX,EDX,ESI,EDI,EBP.
+        *
+        * We want to pass them to __audit_syscall_entry(), which is a 64-bit
+        * C function with 5 parameters, so shuffle them to match what
+        * the function expects: RDI,RSI,RDX,RCX,R8.
+        */
+       movl    %esi, %r8d              /* arg5 (R8 ) <= 4th syscall arg (ESI) */
+       xchg    %ecx, %edx              /* arg4 (RCX) <= 3rd syscall arg (EDX) */
+                                       /* arg3 (RDX) <= 2nd syscall arg (ECX) */
+       movl    %ebx, %esi              /* arg2 (RSI) <= 1st syscall arg (EBX) */
+       movl    %eax, %edi              /* arg1 (RDI) <= syscall number  (EAX) */
+       call    __audit_syscall_entry
+
+       /*
+        * We are going to jump back to the syscall dispatch code.
+        * Prepare syscall args as required by the 64-bit C ABI.
+        * Registers clobbered by __audit_syscall_entry() are
+        * loaded from pt_regs on stack:
+        */
+       movl    ORIG_RAX(%rsp), %eax    /* syscall number */
+       movl    %ebx, %edi              /* arg1 */
+       movl    RCX(%rsp), %esi         /* arg2 */
+       movl    RDX(%rsp), %edx         /* arg3 */
+       movl    RSI(%rsp), %ecx         /* arg4 */
+       movl    RDI(%rsp), %r8d         /* arg5 */
+       movl    %ebp, %r9d              /* arg6 */
+       .endm
+
+       .macro auditsys_exit exit
+       testl   $(_TIF_ALLWORK_MASK & ~_TIF_SYSCALL_AUDIT), ASM_THREAD_INFO(TI_flags, %rsp, SIZEOF_PTREGS)
+       jnz     ia32_ret_from_sys_call
+       TRACE_IRQS_ON
+       ENABLE_INTERRUPTS(CLBR_NONE)
+       movl    %eax, %esi              /* second arg, syscall return value */
+       cmpl    $-MAX_ERRNO, %eax       /* is it an error ? */
+       jbe     1f
+       movslq  %eax, %rsi              /* if error sign extend to 64 bits */
+1:     setbe   %al                     /* 1 if error, 0 if not */
+       movzbl  %al, %edi               /* zero-extend that into %edi */
+       call    __audit_syscall_exit
+       movq    RAX(%rsp), %rax         /* reload syscall return value */
+       movl    $(_TIF_ALLWORK_MASK & ~_TIF_SYSCALL_AUDIT), %edi
+       DISABLE_INTERRUPTS(CLBR_NONE)
+       TRACE_IRQS_OFF
+       testl   %edi, ASM_THREAD_INFO(TI_flags, %rsp, SIZEOF_PTREGS)
+       jz      \exit
+       xorl    %eax, %eax              /* Do not leak kernel information */
+       movq    %rax, R11(%rsp)
+       movq    %rax, R10(%rsp)
+       movq    %rax, R9(%rsp)
+       movq    %rax, R8(%rsp)
+       jmp     int_with_check
+       .endm
+
+sysenter_auditsys:
+       auditsys_entry_common
+       jmp     sysenter_dispatch
+
+sysexit_audit:
+       auditsys_exit sysexit_from_sys_call
+#endif
+
+sysenter_fix_flags:
+       pushq   $(X86_EFLAGS_IF|X86_EFLAGS_FIXED)
+       popfq
+       jmp     sysenter_flags_fixed
+
+sysenter_tracesys:
+#ifdef CONFIG_AUDITSYSCALL
+       testl   $(_TIF_WORK_SYSCALL_ENTRY & ~_TIF_SYSCALL_AUDIT), ASM_THREAD_INFO(TI_flags, %rsp, SIZEOF_PTREGS)
+       jz      sysenter_auditsys
+#endif
+       SAVE_EXTRA_REGS
+       xorl    %eax, %eax              /* Do not leak kernel information */
+       movq    %rax, R11(%rsp)
+       movq    %rax, R10(%rsp)
+       movq    %rax, R9(%rsp)
+       movq    %rax, R8(%rsp)
+       movq    %rsp, %rdi              /* &pt_regs -> arg1 */
+       call    syscall_trace_enter
+
+       /* Reload arg registers from stack. (see sysenter_tracesys) */
+       movl    RCX(%rsp), %ecx
+       movl    RDX(%rsp), %edx
+       movl    RSI(%rsp), %esi
+       movl    RDI(%rsp), %edi
+       movl    %eax, %eax              /* zero extension */
+
+       RESTORE_EXTRA_REGS
+       jmp     sysenter_do_call
+ENDPROC(entry_SYSENTER_compat)
+
+/*
+ * 32-bit SYSCALL instruction entry.
+ *
+ * 32-bit SYSCALL saves rip to rcx, clears rflags.RF, then saves rflags to r11,
+ * then loads new ss, cs, and rip from previously programmed MSRs.
+ * rflags gets masked by a value from another MSR (so CLD and CLAC
+ * are not needed). SYSCALL does not save anything on the stack
+ * and does not change rsp.
+ *
+ * Note: rflags saving+masking-with-MSR happens only in Long mode
+ * (in legacy 32-bit mode, IF, RF and VM bits are cleared and that's it).
+ * Don't get confused: rflags saving+masking depends on Long Mode Active bit
+ * (EFER.LMA=1), NOT on bitness of userspace where SYSCALL executes
+ * or target CS descriptor's L bit (SYSCALL does not read segment descriptors).
+ *
+ * Arguments:
+ * eax  system call number
+ * ecx  return address
+ * ebx  arg1
+ * ebp  arg2   (note: not saved in the stack frame, should not be touched)
+ * edx  arg3
+ * esi  arg4
+ * edi  arg5
+ * esp  user stack
+ * 0(%esp) arg6
+ *
+ * This is purely a fast path. For anything complicated we use the int 0x80
+ * path below. We set up a complete hardware stack frame to share code
+ * with the int 0x80 path.
+ */
+ENTRY(entry_SYSCALL_compat)
+       /*
+        * Interrupts are off on entry.
+        * We do not frame this tiny irq-off block with TRACE_IRQS_OFF/ON,
+        * it is too small to ever cause noticeable irq latency.
+        */
+       SWAPGS_UNSAFE_STACK
+       movl    %esp, %r8d
+       movq    PER_CPU_VAR(cpu_current_top_of_stack), %rsp
+       ENABLE_INTERRUPTS(CLBR_NONE)
+
+       /* Zero-extending 32-bit regs, do not remove */
+       movl    %eax, %eax
+
+       /* Construct struct pt_regs on stack */
+       pushq   $__USER32_DS            /* pt_regs->ss */
+       pushq   %r8                     /* pt_regs->sp */
+       pushq   %r11                    /* pt_regs->flags */
+       pushq   $__USER32_CS            /* pt_regs->cs */
+       pushq   %rcx                    /* pt_regs->ip */
+       pushq   %rax                    /* pt_regs->orig_ax */
+       pushq   %rdi                    /* pt_regs->di */
+       pushq   %rsi                    /* pt_regs->si */
+       pushq   %rdx                    /* pt_regs->dx */
+       pushq   %rbp                    /* pt_regs->cx */
+       movl    %ebp, %ecx
+       pushq   $-ENOSYS                /* pt_regs->ax */
+       sub     $(10*8), %rsp           /* pt_regs->r8-11, bp, bx, r12-15 not saved */
+
+       /*
+        * No need to do an access_ok check here because r8 has been
+        * 32-bit zero extended:
+        */
+       ASM_STAC
+1:     movl    (%r8), %ebp
+       _ASM_EXTABLE(1b, ia32_badarg)
+       ASM_CLAC
+       orl     $TS_COMPAT, ASM_THREAD_INFO(TI_status, %rsp, SIZEOF_PTREGS)
+       testl   $_TIF_WORK_SYSCALL_ENTRY, ASM_THREAD_INFO(TI_flags, %rsp, SIZEOF_PTREGS)
+       jnz     cstar_tracesys
+
+cstar_do_call:
+       /* 32-bit syscall -> 64-bit C ABI argument conversion */
+       movl    %edi, %r8d              /* arg5 */
+       movl    %ebp, %r9d              /* arg6 */
+       xchg    %ecx, %esi              /* rsi:arg2, rcx:arg4 */
+       movl    %ebx, %edi              /* arg1 */
+       movl    %edx, %edx              /* arg3 (zero extension) */
+
+cstar_dispatch:
+       cmpq    $(IA32_NR_syscalls-1), %rax
+       ja      1f
+
+       call    *ia32_sys_call_table(, %rax, 8)
+       movq    %rax, RAX(%rsp)
+1:
+       movl    RCX(%rsp), %ebp
+       DISABLE_INTERRUPTS(CLBR_NONE)
+       TRACE_IRQS_OFF
+       testl   $_TIF_ALLWORK_MASK, ASM_THREAD_INFO(TI_flags, %rsp, SIZEOF_PTREGS)
+       jnz     sysretl_audit
+
+sysretl_from_sys_call:
+       andl    $~TS_COMPAT, ASM_THREAD_INFO(TI_status, %rsp, SIZEOF_PTREGS)
+       RESTORE_RSI_RDI_RDX
+       movl    RIP(%rsp), %ecx
+       movl    EFLAGS(%rsp), %r11d
+       xorq    %r10, %r10
+       xorq    %r9, %r9
+       xorq    %r8, %r8
+       TRACE_IRQS_ON
+       movl    RSP(%rsp), %esp
+       /*
+        * 64-bit->32-bit SYSRET restores eip from ecx,
+        * eflags from r11 (but RF and VM bits are forced to 0),
+        * cs and ss are loaded from MSRs.
+        * (Note: 32-bit->32-bit SYSRET is different: since r11
+        * does not exist, it merely sets eflags.IF=1).
+        *
+        * NB: On AMD CPUs with the X86_BUG_SYSRET_SS_ATTRS bug, the ss
+        * descriptor is not reinitialized.  This means that we must
+        * avoid SYSRET with SS == NULL, which could happen if we schedule,
+        * exit the kernel, and re-enter using an interrupt vector.  (All
+        * interrupt entries on x86_64 set SS to NULL.)  We prevent that
+        * from happening by reloading SS in __switch_to.
+        */
+       USERGS_SYSRET32
+
+#ifdef CONFIG_AUDITSYSCALL
+cstar_auditsys:
+       auditsys_entry_common
+       jmp     cstar_dispatch
+
+sysretl_audit:
+       auditsys_exit sysretl_from_sys_call
+#endif
+
+cstar_tracesys:
+#ifdef CONFIG_AUDITSYSCALL
+       testl   $(_TIF_WORK_SYSCALL_ENTRY & ~_TIF_SYSCALL_AUDIT), ASM_THREAD_INFO(TI_flags, %rsp, SIZEOF_PTREGS)
+       jz      cstar_auditsys
+#endif
+       SAVE_EXTRA_REGS
+       xorl    %eax, %eax              /* Do not leak kernel information */
+       movq    %rax, R11(%rsp)
+       movq    %rax, R10(%rsp)
+       movq    %rax, R9(%rsp)
+       movq    %rax, R8(%rsp)
+       movq    %rsp, %rdi              /* &pt_regs -> arg1 */
+       call    syscall_trace_enter
+
+       /* Reload arg registers from stack. (see sysenter_tracesys) */
+       movl    RCX(%rsp), %ecx
+       movl    RDX(%rsp), %edx
+       movl    RSI(%rsp), %esi
+       movl    RDI(%rsp), %edi
+       movl    %eax, %eax              /* zero extension */
+
+       RESTORE_EXTRA_REGS
+       jmp     cstar_do_call
+END(entry_SYSCALL_compat)
+
+ia32_badarg:
+       ASM_CLAC
+       movq    $-EFAULT, RAX(%rsp)
+ia32_ret_from_sys_call:
+       xorl    %eax, %eax              /* Do not leak kernel information */
+       movq    %rax, R11(%rsp)
+       movq    %rax, R10(%rsp)
+       movq    %rax, R9(%rsp)
+       movq    %rax, R8(%rsp)
+       jmp     int_ret_from_sys_call
+
+/*
+ * Emulated IA32 system calls via int 0x80.
+ *
+ * Arguments:
+ * eax  system call number
+ * ebx  arg1
+ * ecx  arg2
+ * edx  arg3
+ * esi  arg4
+ * edi  arg5
+ * ebp  arg6   (note: not saved in the stack frame, should not be touched)
+ *
+ * Notes:
+ * Uses the same stack frame as the x86-64 version.
+ * All registers except eax must be saved (but ptrace may violate that).
+ * Arguments are zero extended. For system calls that want sign extension and
+ * take long arguments a wrapper is needed. Most calls can just be called
+ * directly.
+ * Assumes it is only called from user space and entered with interrupts off.
+ */
+
+ENTRY(entry_INT80_compat)
+       /*
+        * Interrupts are off on entry.
+        * We do not frame this tiny irq-off block with TRACE_IRQS_OFF/ON,
+        * it is too small to ever cause noticeable irq latency.
+        */
+       PARAVIRT_ADJUST_EXCEPTION_FRAME
+       SWAPGS
+       ENABLE_INTERRUPTS(CLBR_NONE)
+
+       /* Zero-extending 32-bit regs, do not remove */
+       movl    %eax, %eax
+
+       /* Construct struct pt_regs on stack (iret frame is already on stack) */
+       pushq   %rax                    /* pt_regs->orig_ax */
+       pushq   %rdi                    /* pt_regs->di */
+       pushq   %rsi                    /* pt_regs->si */
+       pushq   %rdx                    /* pt_regs->dx */
+       pushq   %rcx                    /* pt_regs->cx */
+       pushq   $-ENOSYS                /* pt_regs->ax */
+       pushq   $0                      /* pt_regs->r8 */
+       pushq   $0                      /* pt_regs->r9 */
+       pushq   $0                      /* pt_regs->r10 */
+       pushq   $0                      /* pt_regs->r11 */
+       cld
+       sub     $(6*8), %rsp /* pt_regs->bp, bx, r12-15 not saved */
+
+       orl     $TS_COMPAT, ASM_THREAD_INFO(TI_status, %rsp, SIZEOF_PTREGS)
+       testl   $_TIF_WORK_SYSCALL_ENTRY, ASM_THREAD_INFO(TI_flags, %rsp, SIZEOF_PTREGS)
+       jnz     ia32_tracesys
+
+ia32_do_call:
+       /* 32-bit syscall -> 64-bit C ABI argument conversion */
+       movl    %edi, %r8d              /* arg5 */
+       movl    %ebp, %r9d              /* arg6 */
+       xchg    %ecx, %esi              /* rsi:arg2, rcx:arg4 */
+       movl    %ebx, %edi              /* arg1 */
+       movl    %edx, %edx              /* arg3 (zero extension) */
+       cmpq    $(IA32_NR_syscalls-1), %rax
+       ja      1f
+
+       call    *ia32_sys_call_table(, %rax, 8)
+       movq    %rax, RAX(%rsp)
+1:
+       jmp     int_ret_from_sys_call
+
+ia32_tracesys:
+       SAVE_EXTRA_REGS
+       movq    %rsp, %rdi                      /* &pt_regs -> arg1 */
+       call    syscall_trace_enter
+       /*
+        * Reload arg registers from stack in case ptrace changed them.
+        * Don't reload %eax because syscall_trace_enter() returned
+        * the %rax value we should see.  But do truncate it to 32 bits.
+        * If it's -1 to make us punt the syscall, then (u32)-1 is still
+        * an appropriately invalid value.
+        */
+       movl    RCX(%rsp), %ecx
+       movl    RDX(%rsp), %edx
+       movl    RSI(%rsp), %esi
+       movl    RDI(%rsp), %edi
+       movl    %eax, %eax              /* zero extension */
+       RESTORE_EXTRA_REGS
+       jmp     ia32_do_call
+END(entry_INT80_compat)
+
+       .macro PTREGSCALL label, func
+       ALIGN
+GLOBAL(\label)
+       leaq    \func(%rip), %rax
+       jmp     ia32_ptregs_common
+       .endm
+
+       PTREGSCALL stub32_rt_sigreturn, sys32_rt_sigreturn
+       PTREGSCALL stub32_sigreturn,    sys32_sigreturn
+       PTREGSCALL stub32_fork,         sys_fork
+       PTREGSCALL stub32_vfork,        sys_vfork
+
+       ALIGN
+GLOBAL(stub32_clone)
+       leaq    sys_clone(%rip), %rax
+       /*
+        * The 32-bit clone ABI is: clone(..., int tls_val, int *child_tidptr).
+        * The 64-bit clone ABI is: clone(..., int *child_tidptr, int tls_val).
+        *
+        * The native 64-bit kernel's sys_clone() implements the latter,
+        * so we need to swap arguments here before calling it:
+        */
+       xchg    %r8, %rcx
+       jmp     ia32_ptregs_common
+
+       ALIGN
+ia32_ptregs_common:
+       SAVE_EXTRA_REGS 8
+       call    *%rax
+       RESTORE_EXTRA_REGS 8
+       ret
+END(ia32_ptregs_common)
similarity index 79%
rename from arch/x86/kernel/syscall_32.c
rename to arch/x86/entry/syscall_32.c
index 3777189c4a19f04d1b7118ce2fae228e86356592..8ea34f94e973c41322625743f92fe16bc0ed35fa 100644 (file)
@@ -10,7 +10,7 @@
 #else
 #define SYM(sym, compat) sym
 #define ia32_sys_call_table sys_call_table
-#define __NR_ia32_syscall_max __NR_syscall_max
+#define __NR_syscall_compat_max __NR_syscall_max
 #endif
 
 #define __SYSCALL_I386(nr, sym, compat) extern asmlinkage void SYM(sym, compat)(void) ;
@@ -23,11 +23,11 @@ typedef asmlinkage void (*sys_call_ptr_t)(void);
 
 extern asmlinkage void sys_ni_syscall(void);
 
-__visible const sys_call_ptr_t ia32_sys_call_table[__NR_ia32_syscall_max+1] = {
+__visible const sys_call_ptr_t ia32_sys_call_table[__NR_syscall_compat_max+1] = {
        /*
         * Smells like a compiler bug -- it doesn't work
         * when the & below is removed.
         */
-       [0 ... __NR_ia32_syscall_max] = &sys_ni_syscall,
+       [0 ... __NR_syscall_compat_max] = &sys_ni_syscall,
 #include <asm/syscalls_32.h>
 };
similarity index 95%
rename from arch/x86/syscalls/Makefile
rename to arch/x86/entry/syscalls/Makefile
index a55abb9f6c5e2b944d1e4c4b1e2460bd7990654d..57aa59fd140c0079d8b6a50add515c3df9ab7ace 100644 (file)
@@ -1,5 +1,5 @@
-out := $(obj)/../include/generated/asm
-uapi := $(obj)/../include/generated/uapi/asm
+out := $(obj)/../../include/generated/asm
+uapi := $(obj)/../../include/generated/uapi/asm
 
 # Create output directory if not already present
 _dummy := $(shell [ -d '$(out)' ] || mkdir -p '$(out)') \
similarity index 83%
rename from arch/x86/lib/thunk_32.S
rename to arch/x86/entry/thunk_32.S
index e407941d04883dadc2cfdb9ac56dea0354aa1d3d..e5a17114a8c4f917b56819bc6f7ff80ff0ac4798 100644 (file)
@@ -6,16 +6,14 @@
  */
        #include <linux/linkage.h>
        #include <asm/asm.h>
-       #include <asm/dwarf2.h>
 
        /* put return address in eax (arg1) */
        .macro THUNK name, func, put_ret_addr_in_eax=0
        .globl \name
 \name:
-       CFI_STARTPROC
-       pushl_cfi_reg eax
-       pushl_cfi_reg ecx
-       pushl_cfi_reg edx
+       pushl %eax
+       pushl %ecx
+       pushl %edx
 
        .if \put_ret_addr_in_eax
        /* Place EIP in the arg1 */
        .endif
 
        call \func
-       popl_cfi_reg edx
-       popl_cfi_reg ecx
-       popl_cfi_reg eax
+       popl %edx
+       popl %ecx
+       popl %eax
        ret
-       CFI_ENDPROC
        _ASM_NOKPROBE(\name)
        .endm
 
similarity index 71%
rename from arch/x86/lib/thunk_64.S
rename to arch/x86/entry/thunk_64.S
index 2198902329b5e893f2f685b1edfce5a60889c66d..efb2b932b7483419ec6921ac800506012e9030ae 100644 (file)
@@ -6,35 +6,32 @@
  * Subject to the GNU public license, v.2. No warranty of any kind.
  */
 #include <linux/linkage.h>
-#include <asm/dwarf2.h>
-#include <asm/calling.h>
+#include "calling.h"
 #include <asm/asm.h>
 
        /* rdi: arg1 ... normal C conventions. rax is saved/restored. */
        .macro THUNK name, func, put_ret_addr_in_rdi=0
        .globl \name
 \name:
-       CFI_STARTPROC
 
        /* this one pushes 9 elems, the next one would be %rIP */
-       pushq_cfi_reg rdi
-       pushq_cfi_reg rsi
-       pushq_cfi_reg rdx
-       pushq_cfi_reg rcx
-       pushq_cfi_reg rax
-       pushq_cfi_reg r8
-       pushq_cfi_reg r9
-       pushq_cfi_reg r10
-       pushq_cfi_reg r11
+       pushq %rdi
+       pushq %rsi
+       pushq %rdx
+       pushq %rcx
+       pushq %rax
+       pushq %r8
+       pushq %r9
+       pushq %r10
+       pushq %r11
 
        .if \put_ret_addr_in_rdi
        /* 9*8(%rsp) is return addr on stack */
-       movq_cfi_restore 9*8, rdi
+       movq 9*8(%rsp), %rdi
        .endif
 
        call \func
        jmp  restore
-       CFI_ENDPROC
        _ASM_NOKPROBE(\name)
        .endm
 
 #if defined(CONFIG_TRACE_IRQFLAGS) \
  || defined(CONFIG_DEBUG_LOCK_ALLOC) \
  || defined(CONFIG_PREEMPT)
-       CFI_STARTPROC
-       CFI_ADJUST_CFA_OFFSET 9*8
 restore:
-       popq_cfi_reg r11
-       popq_cfi_reg r10
-       popq_cfi_reg r9
-       popq_cfi_reg r8
-       popq_cfi_reg rax
-       popq_cfi_reg rcx
-       popq_cfi_reg rdx
-       popq_cfi_reg rsi
-       popq_cfi_reg rdi
+       popq %r11
+       popq %r10
+       popq %r9
+       popq %r8
+       popq %rax
+       popq %rcx
+       popq %rdx
+       popq %rsi
+       popq %rdi
        ret
-       CFI_ENDPROC
        _ASM_NOKPROBE(restore)
 #endif
diff --git a/arch/x86/entry/vsyscall/Makefile b/arch/x86/entry/vsyscall/Makefile
new file mode 100644 (file)
index 0000000..a9f4856
--- /dev/null
@@ -0,0 +1,7 @@
+#
+# Makefile for the x86 low level vsyscall code
+#
+obj-y                                  := vsyscall_gtod.o
+
+obj-$(CONFIG_X86_VSYSCALL_EMULATION)   += vsyscall_64.o vsyscall_emu_64.o
+
similarity index 89%
rename from arch/x86/kernel/vsyscall_trace.h
rename to arch/x86/entry/vsyscall/vsyscall_trace.h
index a8b2edec54fef066bb2fcbbe2192e41fb5c56367..9dd7359a38a847352c1c60292c29710ec1ea99d0 100644 (file)
@@ -24,6 +24,6 @@ TRACE_EVENT(emulate_vsyscall,
 #endif
 
 #undef TRACE_INCLUDE_PATH
-#define TRACE_INCLUDE_PATH ../../arch/x86/kernel
+#define TRACE_INCLUDE_PATH ../../arch/x86/entry/vsyscall/
 #define TRACE_INCLUDE_FILE vsyscall_trace
 #include <trace/define_trace.h>
index bb635c6418692f4fea1d02fce3b1c170f1a38b0f..cd4339bae066032a69c02cfb1917f210a7f963e8 100644 (file)
@@ -2,7 +2,7 @@
 # Makefile for the ia32 kernel emulation subsystem.
 #
 
-obj-$(CONFIG_IA32_EMULATION) := ia32entry.o sys_ia32.o ia32_signal.o
+obj-$(CONFIG_IA32_EMULATION) := sys_ia32.o ia32_signal.o
 
 obj-$(CONFIG_IA32_AOUT) += ia32_aout.o
 
index c81d35e6c7f1d91c22734793c006c0f5f33c0c10..ae3a29ae875b5508d62b4d91c79db3f1dc26581d 100644 (file)
@@ -21,8 +21,8 @@
 #include <linux/binfmts.h>
 #include <asm/ucontext.h>
 #include <asm/uaccess.h>
-#include <asm/i387.h>
-#include <asm/fpu-internal.h>
+#include <asm/fpu/internal.h>
+#include <asm/fpu/signal.h>
 #include <asm/ptrace.h>
 #include <asm/ia32_unistd.h>
 #include <asm/user32.h>
@@ -198,7 +198,7 @@ static int ia32_restore_sigcontext(struct pt_regs *regs,
                buf = compat_ptr(tmp);
        } get_user_catch(err);
 
-       err |= restore_xstate_sig(buf, 1);
+       err |= fpu__restore_sig(buf, 1);
 
        force_iret();
 
@@ -308,6 +308,7 @@ static void __user *get_sigframe(struct ksignal *ksig, struct pt_regs *regs,
                                 size_t frame_size,
                                 void __user **fpstate)
 {
+       struct fpu *fpu = &current->thread.fpu;
        unsigned long sp;
 
        /* Default to using normal stack */
@@ -322,12 +323,12 @@ static void __user *get_sigframe(struct ksignal *ksig, struct pt_regs *regs,
                 ksig->ka.sa.sa_restorer)
                sp = (unsigned long) ksig->ka.sa.sa_restorer;
 
-       if (used_math()) {
+       if (fpu->fpstate_active) {
                unsigned long fx_aligned, math_size;
 
-               sp = alloc_mathframe(sp, 1, &fx_aligned, &math_size);
+               sp = fpu__alloc_mathframe(sp, 1, &fx_aligned, &math_size);
                *fpstate = (struct _fpstate_ia32 __user *) sp;
-               if (save_xstate_sig(*fpstate, (void __user *)fx_aligned,
+               if (copy_fpstate_to_sigframe(*fpstate, (void __user *)fx_aligned,
                                    math_size) < 0)
                        return (void __user *) -1L;
        }
diff --git a/arch/x86/ia32/ia32entry.S b/arch/x86/ia32/ia32entry.S
deleted file mode 100644 (file)
index 72bf268..0000000
+++ /dev/null
@@ -1,611 +0,0 @@
-/*
- * Compatibility mode system call entry point for x86-64. 
- *             
- * Copyright 2000-2002 Andi Kleen, SuSE Labs.
- */             
-
-#include <asm/dwarf2.h>
-#include <asm/calling.h>
-#include <asm/asm-offsets.h>
-#include <asm/current.h>
-#include <asm/errno.h>
-#include <asm/ia32_unistd.h>   
-#include <asm/thread_info.h>   
-#include <asm/segment.h>
-#include <asm/irqflags.h>
-#include <asm/asm.h>
-#include <asm/smap.h>
-#include <linux/linkage.h>
-#include <linux/err.h>
-
-/* Avoid __ASSEMBLER__'ifying <linux/audit.h> just for this.  */
-#include <linux/elf-em.h>
-#define AUDIT_ARCH_I386                (EM_386|__AUDIT_ARCH_LE)
-#define __AUDIT_ARCH_LE           0x40000000
-
-#ifndef CONFIG_AUDITSYSCALL
-#define sysexit_audit ia32_ret_from_sys_call
-#define sysretl_audit ia32_ret_from_sys_call
-#endif
-
-       .section .entry.text, "ax"
-
-       /* clobbers %rax */
-       .macro  CLEAR_RREGS _r9=rax
-       xorl    %eax,%eax
-       movq    %rax,R11(%rsp)
-       movq    %rax,R10(%rsp)
-       movq    %\_r9,R9(%rsp)
-       movq    %rax,R8(%rsp)
-       .endm
-
-       /*
-        * Reload arg registers from stack in case ptrace changed them.
-        * We don't reload %eax because syscall_trace_enter() returned
-        * the %rax value we should see.  Instead, we just truncate that
-        * value to 32 bits again as we did on entry from user mode.
-        * If it's a new value set by user_regset during entry tracing,
-        * this matches the normal truncation of the user-mode value.
-        * If it's -1 to make us punt the syscall, then (u32)-1 is still
-        * an appropriately invalid value.
-        */
-       .macro LOAD_ARGS32 _r9=0
-       .if \_r9
-       movl R9(%rsp),%r9d
-       .endif
-       movl RCX(%rsp),%ecx
-       movl RDX(%rsp),%edx
-       movl RSI(%rsp),%esi
-       movl RDI(%rsp),%edi
-       movl %eax,%eax                  /* zero extension */
-       .endm
-       
-       .macro CFI_STARTPROC32 simple
-       CFI_STARTPROC   \simple
-       CFI_UNDEFINED   r8
-       CFI_UNDEFINED   r9
-       CFI_UNDEFINED   r10
-       CFI_UNDEFINED   r11
-       CFI_UNDEFINED   r12
-       CFI_UNDEFINED   r13
-       CFI_UNDEFINED   r14
-       CFI_UNDEFINED   r15
-       .endm
-
-#ifdef CONFIG_PARAVIRT
-ENTRY(native_usergs_sysret32)
-       swapgs
-       sysretl
-ENDPROC(native_usergs_sysret32)
-
-ENTRY(native_irq_enable_sysexit)
-       swapgs
-       sti
-       sysexit
-ENDPROC(native_irq_enable_sysexit)
-#endif
-
-/*
- * 32bit SYSENTER instruction entry.
- *
- * SYSENTER loads ss, rsp, cs, and rip from previously programmed MSRs.
- * IF and VM in rflags are cleared (IOW: interrupts are off).
- * SYSENTER does not save anything on the stack,
- * and does not save old rip (!!!) and rflags.
- *
- * Arguments:
- * eax  system call number
- * ebx  arg1
- * ecx  arg2
- * edx  arg3
- * esi  arg4
- * edi  arg5
- * ebp  user stack
- * 0(%ebp) arg6
- *
- * This is purely a fast path. For anything complicated we use the int 0x80
- * path below. We set up a complete hardware stack frame to share code
- * with the int 0x80 path.
- */
-ENTRY(ia32_sysenter_target)
-       CFI_STARTPROC32 simple
-       CFI_SIGNAL_FRAME
-       CFI_DEF_CFA     rsp,0
-       CFI_REGISTER    rsp,rbp
-
-       /*
-        * Interrupts are off on entry.
-        * We do not frame this tiny irq-off block with TRACE_IRQS_OFF/ON,
-        * it is too small to ever cause noticeable irq latency.
-        */
-       SWAPGS_UNSAFE_STACK
-       movq    PER_CPU_VAR(cpu_tss + TSS_sp0), %rsp
-       ENABLE_INTERRUPTS(CLBR_NONE)
-
-       /* Zero-extending 32-bit regs, do not remove */
-       movl    %ebp, %ebp
-       movl    %eax, %eax
-
-       movl    ASM_THREAD_INFO(TI_sysenter_return, %rsp, 0), %r10d
-       CFI_REGISTER rip,r10
-
-       /* Construct struct pt_regs on stack */
-       pushq_cfi       $__USER32_DS            /* pt_regs->ss */
-       pushq_cfi       %rbp                    /* pt_regs->sp */
-       CFI_REL_OFFSET  rsp,0
-       pushfq_cfi                              /* pt_regs->flags */
-       pushq_cfi       $__USER32_CS            /* pt_regs->cs */
-       pushq_cfi       %r10 /* pt_regs->ip = thread_info->sysenter_return */
-       CFI_REL_OFFSET  rip,0
-       pushq_cfi_reg   rax                     /* pt_regs->orig_ax */
-       pushq_cfi_reg   rdi                     /* pt_regs->di */
-       pushq_cfi_reg   rsi                     /* pt_regs->si */
-       pushq_cfi_reg   rdx                     /* pt_regs->dx */
-       pushq_cfi_reg   rcx                     /* pt_regs->cx */
-       pushq_cfi_reg   rax                     /* pt_regs->ax */
-       cld
-       sub     $(10*8),%rsp /* pt_regs->r8-11,bp,bx,r12-15 not saved */
-       CFI_ADJUST_CFA_OFFSET 10*8
-
-       /*
-        * no need to do an access_ok check here because rbp has been
-        * 32bit zero extended
-        */
-       ASM_STAC
-1:     movl    (%rbp),%ebp
-       _ASM_EXTABLE(1b,ia32_badarg)
-       ASM_CLAC
-
-       /*
-        * Sysenter doesn't filter flags, so we need to clear NT
-        * ourselves.  To save a few cycles, we can check whether
-        * NT was set instead of doing an unconditional popfq.
-        */
-       testl $X86_EFLAGS_NT,EFLAGS(%rsp)
-       jnz sysenter_fix_flags
-sysenter_flags_fixed:
-
-       orl     $TS_COMPAT, ASM_THREAD_INFO(TI_status, %rsp, SIZEOF_PTREGS)
-       testl   $_TIF_WORK_SYSCALL_ENTRY, ASM_THREAD_INFO(TI_flags, %rsp, SIZEOF_PTREGS)
-       CFI_REMEMBER_STATE
-       jnz  sysenter_tracesys
-       cmpq    $(IA32_NR_syscalls-1),%rax
-       ja      ia32_badsys
-sysenter_do_call:
-       /* 32bit syscall -> 64bit C ABI argument conversion */
-       movl    %edi,%r8d       /* arg5 */
-       movl    %ebp,%r9d       /* arg6 */
-       xchg    %ecx,%esi       /* rsi:arg2, rcx:arg4 */
-       movl    %ebx,%edi       /* arg1 */
-       movl    %edx,%edx       /* arg3 (zero extension) */
-sysenter_dispatch:
-       call    *ia32_sys_call_table(,%rax,8)
-       movq    %rax,RAX(%rsp)
-       DISABLE_INTERRUPTS(CLBR_NONE)
-       TRACE_IRQS_OFF
-       testl   $_TIF_ALLWORK_MASK, ASM_THREAD_INFO(TI_flags, %rsp, SIZEOF_PTREGS)
-       jnz     sysexit_audit
-sysexit_from_sys_call:
-       /*
-        * NB: SYSEXIT is not obviously safe for 64-bit kernels -- an
-        * NMI between STI and SYSEXIT has poorly specified behavior,
-        * and and NMI followed by an IRQ with usergs is fatal.  So
-        * we just pretend we're using SYSEXIT but we really use
-        * SYSRETL instead.
-        *
-        * This code path is still called 'sysexit' because it pairs
-        * with 'sysenter' and it uses the SYSENTER calling convention.
-        */
-       andl    $~TS_COMPAT,ASM_THREAD_INFO(TI_status, %rsp, SIZEOF_PTREGS)
-       movl    RIP(%rsp),%ecx          /* User %eip */
-       CFI_REGISTER rip,rcx
-       RESTORE_RSI_RDI
-       xorl    %edx,%edx               /* avoid info leaks */
-       xorq    %r8,%r8
-       xorq    %r9,%r9
-       xorq    %r10,%r10
-       movl    EFLAGS(%rsp),%r11d      /* User eflags */
-       /*CFI_RESTORE rflags*/
-       TRACE_IRQS_ON
-
-       /*
-        * SYSRETL works even on Intel CPUs.  Use it in preference to SYSEXIT,
-        * since it avoids a dicey window with interrupts enabled.
-        */
-       movl    RSP(%rsp),%esp
-
-       /*
-        * USERGS_SYSRET32 does:
-        *  gsbase = user's gs base
-        *  eip = ecx
-        *  rflags = r11
-        *  cs = __USER32_CS
-        *  ss = __USER_DS
-        *
-        * The prologue set RIP(%rsp) to VDSO32_SYSENTER_RETURN, which does:
-        *
-        *  pop %ebp
-        *  pop %edx
-        *  pop %ecx
-        *
-        * Therefore, we invoke SYSRETL with EDX and R8-R10 zeroed to
-        * avoid info leaks.  R11 ends up with VDSO32_SYSENTER_RETURN's
-        * address (already known to user code), and R12-R15 are
-        * callee-saved and therefore don't contain any interesting
-        * kernel data.
-        */
-       USERGS_SYSRET32
-
-       CFI_RESTORE_STATE
-
-#ifdef CONFIG_AUDITSYSCALL
-       .macro auditsys_entry_common
-       movl %esi,%r8d                  /* 5th arg: 4th syscall arg */
-       movl %ecx,%r9d                  /*swap with edx*/
-       movl %edx,%ecx                  /* 4th arg: 3rd syscall arg */
-       movl %r9d,%edx                  /* 3rd arg: 2nd syscall arg */
-       movl %ebx,%esi                  /* 2nd arg: 1st syscall arg */
-       movl %eax,%edi                  /* 1st arg: syscall number */
-       call __audit_syscall_entry
-       movl RAX(%rsp),%eax     /* reload syscall number */
-       cmpq $(IA32_NR_syscalls-1),%rax
-       ja ia32_badsys
-       movl %ebx,%edi                  /* reload 1st syscall arg */
-       movl RCX(%rsp),%esi     /* reload 2nd syscall arg */
-       movl RDX(%rsp),%edx     /* reload 3rd syscall arg */
-       movl RSI(%rsp),%ecx     /* reload 4th syscall arg */
-       movl RDI(%rsp),%r8d     /* reload 5th syscall arg */
-       .endm
-
-       .macro auditsys_exit exit
-       testl $(_TIF_ALLWORK_MASK & ~_TIF_SYSCALL_AUDIT), ASM_THREAD_INFO(TI_flags, %rsp, SIZEOF_PTREGS)
-       jnz ia32_ret_from_sys_call
-       TRACE_IRQS_ON
-       ENABLE_INTERRUPTS(CLBR_NONE)
-       movl %eax,%esi          /* second arg, syscall return value */
-       cmpl $-MAX_ERRNO,%eax   /* is it an error ? */
-       jbe 1f
-       movslq %eax, %rsi       /* if error sign extend to 64 bits */
-1:     setbe %al               /* 1 if error, 0 if not */
-       movzbl %al,%edi         /* zero-extend that into %edi */
-       call __audit_syscall_exit
-       movq RAX(%rsp),%rax     /* reload syscall return value */
-       movl $(_TIF_ALLWORK_MASK & ~_TIF_SYSCALL_AUDIT),%edi
-       DISABLE_INTERRUPTS(CLBR_NONE)
-       TRACE_IRQS_OFF
-       testl %edi, ASM_THREAD_INFO(TI_flags, %rsp, SIZEOF_PTREGS)
-       jz \exit
-       CLEAR_RREGS
-       jmp int_with_check
-       .endm
-
-sysenter_auditsys:
-       auditsys_entry_common
-       movl %ebp,%r9d                  /* reload 6th syscall arg */
-       jmp sysenter_dispatch
-
-sysexit_audit:
-       auditsys_exit sysexit_from_sys_call
-#endif
-
-sysenter_fix_flags:
-       pushq_cfi $(X86_EFLAGS_IF|X86_EFLAGS_FIXED)
-       popfq_cfi
-       jmp sysenter_flags_fixed
-
-sysenter_tracesys:
-#ifdef CONFIG_AUDITSYSCALL
-       testl   $(_TIF_WORK_SYSCALL_ENTRY & ~_TIF_SYSCALL_AUDIT), ASM_THREAD_INFO(TI_flags, %rsp, SIZEOF_PTREGS)
-       jz      sysenter_auditsys
-#endif
-       SAVE_EXTRA_REGS
-       CLEAR_RREGS
-       movq    $-ENOSYS,RAX(%rsp)/* ptrace can change this for a bad syscall */
-       movq    %rsp,%rdi        /* &pt_regs -> arg1 */
-       call    syscall_trace_enter
-       LOAD_ARGS32  /* reload args from stack in case ptrace changed it */
-       RESTORE_EXTRA_REGS
-       cmpq    $(IA32_NR_syscalls-1),%rax
-       ja      int_ret_from_sys_call /* sysenter_tracesys has set RAX(%rsp) */
-       jmp     sysenter_do_call
-       CFI_ENDPROC
-ENDPROC(ia32_sysenter_target)
-
-/*
- * 32bit SYSCALL instruction entry.
- *
- * 32bit SYSCALL saves rip to rcx, clears rflags.RF, then saves rflags to r11,
- * then loads new ss, cs, and rip from previously programmed MSRs.
- * rflags gets masked by a value from another MSR (so CLD and CLAC
- * are not needed). SYSCALL does not save anything on the stack
- * and does not change rsp.
- *
- * Note: rflags saving+masking-with-MSR happens only in Long mode
- * (in legacy 32bit mode, IF, RF and VM bits are cleared and that's it).
- * Don't get confused: rflags saving+masking depends on Long Mode Active bit
- * (EFER.LMA=1), NOT on bitness of userspace where SYSCALL executes
- * or target CS descriptor's L bit (SYSCALL does not read segment descriptors).
- *
- * Arguments:
- * eax  system call number
- * ecx  return address
- * ebx  arg1
- * ebp  arg2   (note: not saved in the stack frame, should not be touched)
- * edx  arg3
- * esi  arg4
- * edi  arg5
- * esp  user stack
- * 0(%esp) arg6
- *
- * This is purely a fast path. For anything complicated we use the int 0x80
- * path below. We set up a complete hardware stack frame to share code
- * with the int 0x80 path.
- */
-ENTRY(ia32_cstar_target)
-       CFI_STARTPROC32 simple
-       CFI_SIGNAL_FRAME
-       CFI_DEF_CFA     rsp,0
-       CFI_REGISTER    rip,rcx
-       /*CFI_REGISTER  rflags,r11*/
-
-       /*
-        * Interrupts are off on entry.
-        * We do not frame this tiny irq-off block with TRACE_IRQS_OFF/ON,
-        * it is too small to ever cause noticeable irq latency.
-        */
-       SWAPGS_UNSAFE_STACK
-       movl    %esp,%r8d
-       CFI_REGISTER    rsp,r8
-       movq    PER_CPU_VAR(kernel_stack),%rsp
-       ENABLE_INTERRUPTS(CLBR_NONE)
-
-       /* Zero-extending 32-bit regs, do not remove */
-       movl    %eax,%eax
-
-       /* Construct struct pt_regs on stack */
-       pushq_cfi       $__USER32_DS            /* pt_regs->ss */
-       pushq_cfi       %r8                     /* pt_regs->sp */
-       CFI_REL_OFFSET rsp,0
-       pushq_cfi       %r11                    /* pt_regs->flags */
-       pushq_cfi       $__USER32_CS            /* pt_regs->cs */
-       pushq_cfi       %rcx                    /* pt_regs->ip */
-       CFI_REL_OFFSET rip,0
-       pushq_cfi_reg   rax                     /* pt_regs->orig_ax */
-       pushq_cfi_reg   rdi                     /* pt_regs->di */
-       pushq_cfi_reg   rsi                     /* pt_regs->si */
-       pushq_cfi_reg   rdx                     /* pt_regs->dx */
-       pushq_cfi_reg   rbp                     /* pt_regs->cx */
-       movl    %ebp,%ecx
-       pushq_cfi_reg   rax                     /* pt_regs->ax */
-       sub     $(10*8),%rsp /* pt_regs->r8-11,bp,bx,r12-15 not saved */
-       CFI_ADJUST_CFA_OFFSET 10*8
-
-       /*
-        * no need to do an access_ok check here because r8 has been
-        * 32bit zero extended
-        */
-       ASM_STAC
-1:     movl    (%r8),%r9d
-       _ASM_EXTABLE(1b,ia32_badarg)
-       ASM_CLAC
-       orl     $TS_COMPAT, ASM_THREAD_INFO(TI_status, %rsp, SIZEOF_PTREGS)
-       testl   $_TIF_WORK_SYSCALL_ENTRY, ASM_THREAD_INFO(TI_flags, %rsp, SIZEOF_PTREGS)
-       CFI_REMEMBER_STATE
-       jnz   cstar_tracesys
-       cmpq $IA32_NR_syscalls-1,%rax
-       ja  ia32_badsys
-cstar_do_call:
-       /* 32bit syscall -> 64bit C ABI argument conversion */
-       movl    %edi,%r8d       /* arg5 */
-       /* r9 already loaded */ /* arg6 */
-       xchg    %ecx,%esi       /* rsi:arg2, rcx:arg4 */
-       movl    %ebx,%edi       /* arg1 */
-       movl    %edx,%edx       /* arg3 (zero extension) */
-cstar_dispatch:
-       call *ia32_sys_call_table(,%rax,8)
-       movq %rax,RAX(%rsp)
-       DISABLE_INTERRUPTS(CLBR_NONE)
-       TRACE_IRQS_OFF
-       testl $_TIF_ALLWORK_MASK, ASM_THREAD_INFO(TI_flags, %rsp, SIZEOF_PTREGS)
-       jnz sysretl_audit
-sysretl_from_sys_call:
-       andl $~TS_COMPAT, ASM_THREAD_INFO(TI_status, %rsp, SIZEOF_PTREGS)
-       RESTORE_RSI_RDI_RDX
-       movl RIP(%rsp),%ecx
-       CFI_REGISTER rip,rcx
-       movl EFLAGS(%rsp),%r11d
-       /*CFI_REGISTER rflags,r11*/
-       xorq    %r10,%r10
-       xorq    %r9,%r9
-       xorq    %r8,%r8
-       TRACE_IRQS_ON
-       movl RSP(%rsp),%esp
-       CFI_RESTORE rsp
-       /*
-        * 64bit->32bit SYSRET restores eip from ecx,
-        * eflags from r11 (but RF and VM bits are forced to 0),
-        * cs and ss are loaded from MSRs.
-        * (Note: 32bit->32bit SYSRET is different: since r11
-        * does not exist, it merely sets eflags.IF=1).
-        *
-        * NB: On AMD CPUs with the X86_BUG_SYSRET_SS_ATTRS bug, the ss
-        * descriptor is not reinitialized.  This means that we must
-        * avoid SYSRET with SS == NULL, which could happen if we schedule,
-        * exit the kernel, and re-enter using an interrupt vector.  (All
-        * interrupt entries on x86_64 set SS to NULL.)  We prevent that
-        * from happening by reloading SS in __switch_to.
-        */
-       USERGS_SYSRET32
-
-#ifdef CONFIG_AUDITSYSCALL
-cstar_auditsys:
-       CFI_RESTORE_STATE
-       movl %r9d,R9(%rsp)      /* register to be clobbered by call */
-       auditsys_entry_common
-       movl R9(%rsp),%r9d      /* reload 6th syscall arg */
-       jmp cstar_dispatch
-
-sysretl_audit:
-       auditsys_exit sysretl_from_sys_call
-#endif
-
-cstar_tracesys:
-#ifdef CONFIG_AUDITSYSCALL
-       testl $(_TIF_WORK_SYSCALL_ENTRY & ~_TIF_SYSCALL_AUDIT), ASM_THREAD_INFO(TI_flags, %rsp, SIZEOF_PTREGS)
-       jz cstar_auditsys
-#endif
-       xchgl %r9d,%ebp
-       SAVE_EXTRA_REGS
-       CLEAR_RREGS r9
-       movq $-ENOSYS,RAX(%rsp) /* ptrace can change this for a bad syscall */
-       movq %rsp,%rdi        /* &pt_regs -> arg1 */
-       call syscall_trace_enter
-       LOAD_ARGS32 1   /* reload args from stack in case ptrace changed it */
-       RESTORE_EXTRA_REGS
-       xchgl %ebp,%r9d
-       cmpq $(IA32_NR_syscalls-1),%rax
-       ja int_ret_from_sys_call /* cstar_tracesys has set RAX(%rsp) */
-       jmp cstar_do_call
-END(ia32_cstar_target)
-                               
-ia32_badarg:
-       ASM_CLAC
-       movq $-EFAULT,%rax
-       jmp ia32_sysret
-       CFI_ENDPROC
-
-/*
- * Emulated IA32 system calls via int 0x80.
- *
- * Arguments:
- * eax  system call number
- * ebx  arg1
- * ecx  arg2
- * edx  arg3
- * esi  arg4
- * edi  arg5
- * ebp  arg6   (note: not saved in the stack frame, should not be touched)
- *
- * Notes:
- * Uses the same stack frame as the x86-64 version.
- * All registers except eax must be saved (but ptrace may violate that).
- * Arguments are zero extended. For system calls that want sign extension and
- * take long arguments a wrapper is needed. Most calls can just be called
- * directly.
- * Assumes it is only called from user space and entered with interrupts off.
- */
-
-ENTRY(ia32_syscall)
-       CFI_STARTPROC32 simple
-       CFI_SIGNAL_FRAME
-       CFI_DEF_CFA     rsp,5*8
-       /*CFI_REL_OFFSET        ss,4*8 */
-       CFI_REL_OFFSET  rsp,3*8
-       /*CFI_REL_OFFSET        rflags,2*8 */
-       /*CFI_REL_OFFSET        cs,1*8 */
-       CFI_REL_OFFSET  rip,0*8
-
-       /*
-        * Interrupts are off on entry.
-        * We do not frame this tiny irq-off block with TRACE_IRQS_OFF/ON,
-        * it is too small to ever cause noticeable irq latency.
-        */
-       PARAVIRT_ADJUST_EXCEPTION_FRAME
-       SWAPGS
-       ENABLE_INTERRUPTS(CLBR_NONE)
-
-       /* Zero-extending 32-bit regs, do not remove */
-       movl    %eax,%eax
-
-       /* Construct struct pt_regs on stack (iret frame is already on stack) */
-       pushq_cfi_reg   rax                     /* pt_regs->orig_ax */
-       pushq_cfi_reg   rdi                     /* pt_regs->di */
-       pushq_cfi_reg   rsi                     /* pt_regs->si */
-       pushq_cfi_reg   rdx                     /* pt_regs->dx */
-       pushq_cfi_reg   rcx                     /* pt_regs->cx */
-       pushq_cfi_reg   rax                     /* pt_regs->ax */
-       cld
-       sub     $(10*8),%rsp /* pt_regs->r8-11,bp,bx,r12-15 not saved */
-       CFI_ADJUST_CFA_OFFSET 10*8
-
-       orl $TS_COMPAT, ASM_THREAD_INFO(TI_status, %rsp, SIZEOF_PTREGS)
-       testl $_TIF_WORK_SYSCALL_ENTRY, ASM_THREAD_INFO(TI_flags, %rsp, SIZEOF_PTREGS)
-       jnz ia32_tracesys
-       cmpq $(IA32_NR_syscalls-1),%rax
-       ja ia32_badsys
-ia32_do_call:
-       /* 32bit syscall -> 64bit C ABI argument conversion */
-       movl %edi,%r8d  /* arg5 */
-       movl %ebp,%r9d  /* arg6 */
-       xchg %ecx,%esi  /* rsi:arg2, rcx:arg4 */
-       movl %ebx,%edi  /* arg1 */
-       movl %edx,%edx  /* arg3 (zero extension) */
-       call *ia32_sys_call_table(,%rax,8) # xxx: rip relative
-ia32_sysret:
-       movq %rax,RAX(%rsp)
-ia32_ret_from_sys_call:
-       CLEAR_RREGS
-       jmp int_ret_from_sys_call
-
-ia32_tracesys:
-       SAVE_EXTRA_REGS
-       CLEAR_RREGS
-       movq $-ENOSYS,RAX(%rsp) /* ptrace can change this for a bad syscall */
-       movq %rsp,%rdi        /* &pt_regs -> arg1 */
-       call syscall_trace_enter
-       LOAD_ARGS32     /* reload args from stack in case ptrace changed it */
-       RESTORE_EXTRA_REGS
-       cmpq $(IA32_NR_syscalls-1),%rax
-       ja  int_ret_from_sys_call       /* ia32_tracesys has set RAX(%rsp) */
-       jmp ia32_do_call
-END(ia32_syscall)
-
-ia32_badsys:
-       movq $0,ORIG_RAX(%rsp)
-       movq $-ENOSYS,%rax
-       jmp ia32_sysret
-
-       CFI_ENDPROC
-       
-       .macro PTREGSCALL label, func
-       ALIGN
-GLOBAL(\label)
-       leaq \func(%rip),%rax
-       jmp  ia32_ptregs_common 
-       .endm
-
-       CFI_STARTPROC32
-
-       PTREGSCALL stub32_rt_sigreturn, sys32_rt_sigreturn
-       PTREGSCALL stub32_sigreturn, sys32_sigreturn
-       PTREGSCALL stub32_fork, sys_fork
-       PTREGSCALL stub32_vfork, sys_vfork
-
-       ALIGN
-GLOBAL(stub32_clone)
-       leaq sys_clone(%rip),%rax
-       mov     %r8, %rcx
-       jmp  ia32_ptregs_common 
-
-       ALIGN
-ia32_ptregs_common:
-       CFI_ENDPROC
-       CFI_STARTPROC32 simple
-       CFI_SIGNAL_FRAME
-       CFI_DEF_CFA     rsp,SIZEOF_PTREGS
-       CFI_REL_OFFSET  rax,RAX
-       CFI_REL_OFFSET  rcx,RCX
-       CFI_REL_OFFSET  rdx,RDX
-       CFI_REL_OFFSET  rsi,RSI
-       CFI_REL_OFFSET  rdi,RDI
-       CFI_REL_OFFSET  rip,RIP
-/*     CFI_REL_OFFSET  cs,CS*/
-/*     CFI_REL_OFFSET  rflags,EFLAGS*/
-       CFI_REL_OFFSET  rsp,RSP
-/*     CFI_REL_OFFSET  ss,SS*/
-       SAVE_EXTRA_REGS 8
-       call *%rax
-       RESTORE_EXTRA_REGS 8
-       ret
-       CFI_ENDPROC
-END(ia32_ptregs_common)
index bdf02eeee76519582b0fe9c35b631852b1b417d9..e7636bac7372d41d4b8077f4d7df7d81de32ee32 100644 (file)
        .endm
 #endif
 
+/*
+ * Issue one struct alt_instr descriptor entry (need to put it into
+ * the section .altinstructions, see below). This entry contains
+ * enough information for the alternatives patching code to patch an
+ * instruction. See apply_alternatives().
+ */
 .macro altinstruction_entry orig alt feature orig_len alt_len pad_len
        .long \orig - .
        .long \alt - .
        .byte \pad_len
 .endm
 
+/*
+ * Define an alternative between two instructions. If @feature is
+ * present, early code in apply_alternatives() replaces @oldinstr with
+ * @newinstr. ".skip" directive takes care of proper instruction padding
+ * in case @newinstr is longer than @oldinstr.
+ */
 .macro ALTERNATIVE oldinstr, newinstr, feature
 140:
        \oldinstr
  */
 #define alt_max_short(a, b)    ((a) ^ (((a) ^ (b)) & -(-((a) < (b)))))
 
+
+/*
+ * Same as ALTERNATIVE macro above but for two alternatives. If CPU
+ * has @feature1, it replaces @oldinstr with @newinstr1. If CPU has
+ * @feature2, it replaces @oldinstr with @feature2.
+ */
 .macro ALTERNATIVE_2 oldinstr, newinstr1, feature1, newinstr2, feature2
 140:
        \oldinstr
index ba32af062f61d69164a792630e3257c8cdc6deb5..7bfc85bbb8ffc0578011ceac2c08548bd140ade3 100644 (file)
@@ -52,6 +52,12 @@ struct alt_instr {
        u8  padlen;             /* length of build-time padding */
 } __packed;
 
+/*
+ * Debug flag that can be tested to see whether alternative
+ * instructions were patched in already:
+ */
+extern int alternatives_patched;
+
 extern void alternative_instructions(void);
 extern void apply_alternatives(struct alt_instr *start, struct alt_instr *end);
 
index aaac3b2fb746d3e61019f9e0804d7bf2913f8de2..1a5da2e63aeeebc062bd0f5c08b36e4bda32707d 100644 (file)
@@ -98,11 +98,22 @@ static inline u16 amd_get_node_id(struct pci_dev *pdev)
        return 0;
 }
 
+static inline bool amd_gart_present(void)
+{
+       /* GART present only on Fam15h, upto model 0fh */
+       if (boot_cpu_data.x86 == 0xf || boot_cpu_data.x86 == 0x10 ||
+           (boot_cpu_data.x86 == 0x15 && boot_cpu_data.x86_model < 0x10))
+               return true;
+
+       return false;
+}
+
 #else
 
 #define amd_nb_num(x)          0
 #define amd_nb_has_feature(x)  false
 #define node_to_amd_nb(x)      NULL
+#define amd_gart_present(x)    false
 
 #endif
 
index 976b86a325e55cedfd28c029455ddecb2c06b6be..c8393634ca0c50ff4f4acb490e3e5c57ffc5ef19 100644 (file)
@@ -644,6 +644,12 @@ static inline void entering_ack_irq(void)
        entering_irq();
 }
 
+static inline void ipi_entering_ack_irq(void)
+{
+       ack_APIC_irq();
+       irq_enter();
+}
+
 static inline void exiting_irq(void)
 {
        irq_exit();
index 7730c1c5c83aa7aaf6859170f812ad1f410c1a0b..189679aba703537393b87d699a4e11cbfe94e23c 100644 (file)
        _ASM_ALIGN ;                                            \
        _ASM_PTR (entry);                                       \
        .popsection
+
+.macro ALIGN_DESTINATION
+       /* check for bad alignment of destination */
+       movl %edi,%ecx
+       andl $7,%ecx
+       jz 102f                         /* already aligned */
+       subl $8,%ecx
+       negl %ecx
+       subl %ecx,%edx
+100:   movb (%rsi),%al
+101:   movb %al,(%rdi)
+       incq %rsi
+       incq %rdi
+       decl %ecx
+       jnz 100b
+102:
+       .section .fixup,"ax"
+103:   addl %ecx,%edx                  /* ecx is zerorest also */
+       jmp copy_user_handle_tail
+       .previous
+
+       _ASM_EXTABLE(100b,103b)
+       _ASM_EXTABLE(101b,103b)
+       .endm
+
 #else
 # define _ASM_EXTABLE(from,to)                                 \
        " .pushsection \"__ex_table\",\"a\"\n"                  \
index 5e5cd123fdfbc2b0fe90cabc5d27948d3ded267a..e9168955c42f4ee8b18e726e28ecacf39b75a7f2 100644 (file)
@@ -22,7 +22,7 @@
  *
  * Atomically reads the value of @v.
  */
-static inline int atomic_read(const atomic_t *v)
+static __always_inline int atomic_read(const atomic_t *v)
 {
        return ACCESS_ONCE((v)->counter);
 }
@@ -34,7 +34,7 @@ static inline int atomic_read(const atomic_t *v)
  *
  * Atomically sets the value of @v to @i.
  */
-static inline void atomic_set(atomic_t *v, int i)
+static __always_inline void atomic_set(atomic_t *v, int i)
 {
        v->counter = i;
 }
@@ -46,7 +46,7 @@ static inline void atomic_set(atomic_t *v, int i)
  *
  * Atomically adds @i to @v.
  */
-static inline void atomic_add(int i, atomic_t *v)
+static __always_inline void atomic_add(int i, atomic_t *v)
 {
        asm volatile(LOCK_PREFIX "addl %1,%0"
                     : "+m" (v->counter)
@@ -60,7 +60,7 @@ static inline void atomic_add(int i, atomic_t *v)
  *
  * Atomically subtracts @i from @v.
  */
-static inline void atomic_sub(int i, atomic_t *v)
+static __always_inline void atomic_sub(int i, atomic_t *v)
 {
        asm volatile(LOCK_PREFIX "subl %1,%0"
                     : "+m" (v->counter)
@@ -76,7 +76,7 @@ static inline void atomic_sub(int i, atomic_t *v)
  * true if the result is zero, or false for all
  * other cases.
  */
-static inline int atomic_sub_and_test(int i, atomic_t *v)
+static __always_inline int atomic_sub_and_test(int i, atomic_t *v)
 {
        GEN_BINARY_RMWcc(LOCK_PREFIX "subl", v->counter, "er", i, "%0", "e");
 }
@@ -87,7 +87,7 @@ static inline int atomic_sub_and_test(int i, atomic_t *v)
  *
  * Atomically increments @v by 1.
  */
-static inline void atomic_inc(atomic_t *v)
+static __always_inline void atomic_inc(atomic_t *v)
 {
        asm volatile(LOCK_PREFIX "incl %0"
                     : "+m" (v->counter));
@@ -99,7 +99,7 @@ static inline void atomic_inc(atomic_t *v)
  *
  * Atomically decrements @v by 1.
  */
-static inline void atomic_dec(atomic_t *v)
+static __always_inline void atomic_dec(atomic_t *v)
 {
        asm volatile(LOCK_PREFIX "decl %0"
                     : "+m" (v->counter));
@@ -113,7 +113,7 @@ static inline void atomic_dec(atomic_t *v)
  * returns true if the result is 0, or false for all other
  * cases.
  */
-static inline int atomic_dec_and_test(atomic_t *v)
+static __always_inline int atomic_dec_and_test(atomic_t *v)
 {
        GEN_UNARY_RMWcc(LOCK_PREFIX "decl", v->counter, "%0", "e");
 }
@@ -126,7 +126,7 @@ static inline int atomic_dec_and_test(atomic_t *v)
  * and returns true if the result is zero, or false for all
  * other cases.
  */
-static inline int atomic_inc_and_test(atomic_t *v)
+static __always_inline int atomic_inc_and_test(atomic_t *v)
 {
        GEN_UNARY_RMWcc(LOCK_PREFIX "incl", v->counter, "%0", "e");
 }
@@ -140,7 +140,7 @@ static inline int atomic_inc_and_test(atomic_t *v)
  * if the result is negative, or false when
  * result is greater than or equal to zero.
  */
-static inline int atomic_add_negative(int i, atomic_t *v)
+static __always_inline int atomic_add_negative(int i, atomic_t *v)
 {
        GEN_BINARY_RMWcc(LOCK_PREFIX "addl", v->counter, "er", i, "%0", "s");
 }
@@ -152,7 +152,7 @@ static inline int atomic_add_negative(int i, atomic_t *v)
  *
  * Atomically adds @i to @v and returns @i + @v
  */
-static inline int atomic_add_return(int i, atomic_t *v)
+static __always_inline int atomic_add_return(int i, atomic_t *v)
 {
        return i + xadd(&v->counter, i);
 }
@@ -164,7 +164,7 @@ static inline int atomic_add_return(int i, atomic_t *v)
  *
  * Atomically subtracts @i from @v and returns @v - @i
  */
-static inline int atomic_sub_return(int i, atomic_t *v)
+static __always_inline int atomic_sub_return(int i, atomic_t *v)
 {
        return atomic_add_return(-i, v);
 }
@@ -172,7 +172,7 @@ static inline int atomic_sub_return(int i, atomic_t *v)
 #define atomic_inc_return(v)  (atomic_add_return(1, v))
 #define atomic_dec_return(v)  (atomic_sub_return(1, v))
 
-static inline int atomic_cmpxchg(atomic_t *v, int old, int new)
+static __always_inline int atomic_cmpxchg(atomic_t *v, int old, int new)
 {
        return cmpxchg(&v->counter, old, new);
 }
@@ -191,7 +191,7 @@ static inline int atomic_xchg(atomic_t *v, int new)
  * Atomically adds @a to @v, so long as @v was not already @u.
  * Returns the old value of @v.
  */
-static inline int __atomic_add_unless(atomic_t *v, int a, int u)
+static __always_inline int __atomic_add_unless(atomic_t *v, int a, int u)
 {
        int c, old;
        c = atomic_read(v);
@@ -213,7 +213,7 @@ static inline int __atomic_add_unless(atomic_t *v, int a, int u)
  * Atomically adds 1 to @v
  * Returns the new value of @u
  */
-static inline short int atomic_inc_short(short int *v)
+static __always_inline short int atomic_inc_short(short int *v)
 {
        asm(LOCK_PREFIX "addw $1, %0" : "+m" (*v));
        return *v;
index f8d273e18516dedf885bbafb16224c189913e14f..b965f9e03f2a04b2291fc0b62d537b6d9416bdaf 100644 (file)
@@ -40,7 +40,7 @@ static inline void atomic64_set(atomic64_t *v, long i)
  *
  * Atomically adds @i to @v.
  */
-static inline void atomic64_add(long i, atomic64_t *v)
+static __always_inline void atomic64_add(long i, atomic64_t *v)
 {
        asm volatile(LOCK_PREFIX "addq %1,%0"
                     : "=m" (v->counter)
@@ -81,7 +81,7 @@ static inline int atomic64_sub_and_test(long i, atomic64_t *v)
  *
  * Atomically increments @v by 1.
  */
-static inline void atomic64_inc(atomic64_t *v)
+static __always_inline void atomic64_inc(atomic64_t *v)
 {
        asm volatile(LOCK_PREFIX "incq %0"
                     : "=m" (v->counter)
@@ -94,7 +94,7 @@ static inline void atomic64_inc(atomic64_t *v)
  *
  * Atomically decrements @v by 1.
  */
-static inline void atomic64_dec(atomic64_t *v)
+static __always_inline void atomic64_dec(atomic64_t *v)
 {
        asm volatile(LOCK_PREFIX "decq %0"
                     : "=m" (v->counter)
@@ -148,7 +148,7 @@ static inline int atomic64_add_negative(long i, atomic64_t *v)
  *
  * Atomically adds @i to @v and returns @i + @v
  */
-static inline long atomic64_add_return(long i, atomic64_t *v)
+static __always_inline long atomic64_add_return(long i, atomic64_t *v)
 {
        return i + xadd(&v->counter, i);
 }
index 959e45b81fe29192b0f1c97a65e028e7314f603d..e51a8f803f55e30df69a6a02a20a86f9ed7ebdad 100644 (file)
 #define smp_mb()       mb()
 #define smp_rmb()      dma_rmb()
 #define smp_wmb()      barrier()
-#define set_mb(var, value) do { (void)xchg(&var, value); } while (0)
+#define smp_store_mb(var, value) do { (void)xchg(&var, value); } while (0)
 #else /* !SMP */
 #define smp_mb()       barrier()
 #define smp_rmb()      barrier()
 #define smp_wmb()      barrier()
-#define set_mb(var, value) do { var = value; barrier(); } while (0)
+#define smp_store_mb(var, value) do { WRITE_ONCE(var, value); barrier(); } while (0)
 #endif /* SMP */
 
 #define read_barrier_depends()         do { } while (0)
index 47c8e32f621a0efd0ee3d9dd3cfb8c8c4f440292..b6f7457d12e41f338832ad2fbf8e0ad83c55fa5e 100644 (file)
@@ -8,7 +8,7 @@
 /*
  * The set_memory_* API can be used to change various attributes of a virtual
  * address range. The attributes include:
- * Cachability   : UnCached, WriteCombining, WriteBack
+ * Cachability   : UnCached, WriteCombining, WriteThrough, WriteBack
  * Executability : eXeutable, NoteXecutable
  * Read/Write    : ReadOnly, ReadWrite
  * Presence      : NotPresent
 
 int _set_memory_uc(unsigned long addr, int numpages);
 int _set_memory_wc(unsigned long addr, int numpages);
+int _set_memory_wt(unsigned long addr, int numpages);
 int _set_memory_wb(unsigned long addr, int numpages);
 int set_memory_uc(unsigned long addr, int numpages);
 int set_memory_wc(unsigned long addr, int numpages);
+int set_memory_wt(unsigned long addr, int numpages);
 int set_memory_wb(unsigned long addr, int numpages);
 int set_memory_x(unsigned long addr, int numpages);
 int set_memory_nx(unsigned long addr, int numpages);
@@ -48,10 +50,12 @@ int set_memory_4k(unsigned long addr, int numpages);
 
 int set_memory_array_uc(unsigned long *addr, int addrinarray);
 int set_memory_array_wc(unsigned long *addr, int addrinarray);
+int set_memory_array_wt(unsigned long *addr, int addrinarray);
 int set_memory_array_wb(unsigned long *addr, int addrinarray);
 
 int set_pages_array_uc(struct page **pages, int addrinarray);
 int set_pages_array_wc(struct page **pages, int addrinarray);
+int set_pages_array_wt(struct page **pages, int addrinarray);
 int set_pages_array_wb(struct page **pages, int addrinarray);
 
 /*
index 99c105d78b7e123eec41d9bb5b2c308e5205d514..ad19841eddfe142fec6b2b27dc329059d8fb7296 100644 (file)
@@ -4,8 +4,6 @@
 #include <linux/compiler.h>
 #include <asm/alternative.h> /* Provides LOCK_PREFIX */
 
-#define __HAVE_ARCH_CMPXCHG 1
-
 /*
  * Non-existant functions to indicate usage errors at link time
  * (or compile-time if the compiler implements __compiletime_error().
index 1eef55596e82cade6ecc0e910488a6fcc9e0b74a..03bb1065c3352826843a305b15399751a5c860ae 100644 (file)
@@ -7,7 +7,7 @@
 
 #include <linux/kernel.h>
 #include <linux/crypto.h>
-#include <asm/i387.h>
+#include <asm/fpu/api.h>
 #include <crypto/b128ops.h>
 
 typedef void (*common_glue_func_t)(void *ctx, u8 *dst, const u8 *src);
index 808dae63eeea6f73eb312d4f5b7f77cf4e869e6c..1f5b7287d1ad8df92f789003018fec3913b03e1c 100644 (file)
@@ -127,50 +127,14 @@ static inline gfp_t dma_alloc_coherent_gfp_flags(struct device *dev, gfp_t gfp)
 
 #define dma_alloc_coherent(d,s,h,f)    dma_alloc_attrs(d,s,h,f,NULL)
 
-static inline void *
+void *
 dma_alloc_attrs(struct device *dev, size_t size, dma_addr_t *dma_handle,
-               gfp_t gfp, struct dma_attrs *attrs)
-{
-       struct dma_map_ops *ops = get_dma_ops(dev);
-       void *memory;
-
-       gfp &= ~(__GFP_DMA | __GFP_HIGHMEM | __GFP_DMA32);
-
-       if (dma_alloc_from_coherent(dev, size, dma_handle, &memory))
-               return memory;
-
-       if (!dev)
-               dev = &x86_dma_fallback_dev;
-
-       if (!is_device_dma_capable(dev))
-               return NULL;
-
-       if (!ops->alloc)
-               return NULL;
-
-       memory = ops->alloc(dev, size, dma_handle,
-                           dma_alloc_coherent_gfp_flags(dev, gfp), attrs);
-       debug_dma_alloc_coherent(dev, size, *dma_handle, memory);
-
-       return memory;
-}
+               gfp_t gfp, struct dma_attrs *attrs);
 
 #define dma_free_coherent(d,s,c,h) dma_free_attrs(d,s,c,h,NULL)
 
-static inline void dma_free_attrs(struct device *dev, size_t size,
-                                 void *vaddr, dma_addr_t bus,
-                                 struct dma_attrs *attrs)
-{
-       struct dma_map_ops *ops = get_dma_ops(dev);
-
-       WARN_ON(irqs_disabled());       /* for portability */
-
-       if (dma_release_from_coherent(dev, get_order(size), vaddr))
-               return;
-
-       debug_dma_free_coherent(dev, size, vaddr, bus);
-       if (ops->free)
-               ops->free(dev, size, vaddr, bus, attrs);
-}
+void dma_free_attrs(struct device *dev, size_t size,
+                   void *vaddr, dma_addr_t bus,
+                   struct dma_attrs *attrs);
 
 #endif
diff --git a/arch/x86/include/asm/dwarf2.h b/arch/x86/include/asm/dwarf2.h
deleted file mode 100644 (file)
index de1cdaf..0000000
+++ /dev/null
@@ -1,170 +0,0 @@
-#ifndef _ASM_X86_DWARF2_H
-#define _ASM_X86_DWARF2_H
-
-#ifndef __ASSEMBLY__
-#warning "asm/dwarf2.h should be only included in pure assembly files"
-#endif
-
-/*
- * Macros for dwarf2 CFI unwind table entries.
- * See "as.info" for details on these pseudo ops. Unfortunately
- * they are only supported in very new binutils, so define them
- * away for older version.
- */
-
-#ifdef CONFIG_AS_CFI
-
-#define CFI_STARTPROC          .cfi_startproc
-#define CFI_ENDPROC            .cfi_endproc
-#define CFI_DEF_CFA            .cfi_def_cfa
-#define CFI_DEF_CFA_REGISTER   .cfi_def_cfa_register
-#define CFI_DEF_CFA_OFFSET     .cfi_def_cfa_offset
-#define CFI_ADJUST_CFA_OFFSET  .cfi_adjust_cfa_offset
-#define CFI_OFFSET             .cfi_offset
-#define CFI_REL_OFFSET         .cfi_rel_offset
-#define CFI_REGISTER           .cfi_register
-#define CFI_RESTORE            .cfi_restore
-#define CFI_REMEMBER_STATE     .cfi_remember_state
-#define CFI_RESTORE_STATE      .cfi_restore_state
-#define CFI_UNDEFINED          .cfi_undefined
-#define CFI_ESCAPE             .cfi_escape
-
-#ifdef CONFIG_AS_CFI_SIGNAL_FRAME
-#define CFI_SIGNAL_FRAME       .cfi_signal_frame
-#else
-#define CFI_SIGNAL_FRAME
-#endif
-
-#if defined(CONFIG_AS_CFI_SECTIONS) && defined(__ASSEMBLY__)
-       /*
-        * Emit CFI data in .debug_frame sections, not .eh_frame sections.
-        * The latter we currently just discard since we don't do DWARF
-        * unwinding at runtime.  So only the offline DWARF information is
-        * useful to anyone.  Note we should not use this directive if this
-        * file is used in the vDSO assembly, or if vmlinux.lds.S gets
-        * changed so it doesn't discard .eh_frame.
-        */
-       .cfi_sections .debug_frame
-#endif
-
-#else
-
-/*
- * Due to the structure of pre-exisiting code, don't use assembler line
- * comment character # to ignore the arguments. Instead, use a dummy macro.
- */
-.macro cfi_ignore a=0, b=0, c=0, d=0
-.endm
-
-#define CFI_STARTPROC          cfi_ignore
-#define CFI_ENDPROC            cfi_ignore
-#define CFI_DEF_CFA            cfi_ignore
-#define CFI_DEF_CFA_REGISTER   cfi_ignore
-#define CFI_DEF_CFA_OFFSET     cfi_ignore
-#define CFI_ADJUST_CFA_OFFSET  cfi_ignore
-#define CFI_OFFSET             cfi_ignore
-#define CFI_REL_OFFSET         cfi_ignore
-#define CFI_REGISTER           cfi_ignore
-#define CFI_RESTORE            cfi_ignore
-#define CFI_REMEMBER_STATE     cfi_ignore
-#define CFI_RESTORE_STATE      cfi_ignore
-#define CFI_UNDEFINED          cfi_ignore
-#define CFI_ESCAPE             cfi_ignore
-#define CFI_SIGNAL_FRAME       cfi_ignore
-
-#endif
-
-/*
- * An attempt to make CFI annotations more or less
- * correct and shorter. It is implied that you know
- * what you're doing if you use them.
- */
-#ifdef __ASSEMBLY__
-#ifdef CONFIG_X86_64
-       .macro pushq_cfi reg
-       pushq \reg
-       CFI_ADJUST_CFA_OFFSET 8
-       .endm
-
-       .macro pushq_cfi_reg reg
-       pushq %\reg
-       CFI_ADJUST_CFA_OFFSET 8
-       CFI_REL_OFFSET \reg, 0
-       .endm
-
-       .macro popq_cfi reg
-       popq \reg
-       CFI_ADJUST_CFA_OFFSET -8
-       .endm
-
-       .macro popq_cfi_reg reg
-       popq %\reg
-       CFI_ADJUST_CFA_OFFSET -8
-       CFI_RESTORE \reg
-       .endm
-
-       .macro pushfq_cfi
-       pushfq
-       CFI_ADJUST_CFA_OFFSET 8
-       .endm
-
-       .macro popfq_cfi
-       popfq
-       CFI_ADJUST_CFA_OFFSET -8
-       .endm
-
-       .macro movq_cfi reg offset=0
-       movq %\reg, \offset(%rsp)
-       CFI_REL_OFFSET \reg, \offset
-       .endm
-
-       .macro movq_cfi_restore offset reg
-       movq \offset(%rsp), %\reg
-       CFI_RESTORE \reg
-       .endm
-#else /*!CONFIG_X86_64*/
-       .macro pushl_cfi reg
-       pushl \reg
-       CFI_ADJUST_CFA_OFFSET 4
-       .endm
-
-       .macro pushl_cfi_reg reg
-       pushl %\reg
-       CFI_ADJUST_CFA_OFFSET 4
-       CFI_REL_OFFSET \reg, 0
-       .endm
-
-       .macro popl_cfi reg
-       popl \reg
-       CFI_ADJUST_CFA_OFFSET -4
-       .endm
-
-       .macro popl_cfi_reg reg
-       popl %\reg
-       CFI_ADJUST_CFA_OFFSET -4
-       CFI_RESTORE \reg
-       .endm
-
-       .macro pushfl_cfi
-       pushfl
-       CFI_ADJUST_CFA_OFFSET 4
-       .endm
-
-       .macro popfl_cfi
-       popfl
-       CFI_ADJUST_CFA_OFFSET -4
-       .endm
-
-       .macro movl_cfi reg offset=0
-       movl %\reg, \offset(%esp)
-       CFI_REL_OFFSET \reg, \offset
-       .endm
-
-       .macro movl_cfi_restore offset reg
-       movl \offset(%esp), %\reg
-       CFI_RESTORE \reg
-       .endm
-#endif /*!CONFIG_X86_64*/
-#endif /*__ASSEMBLY__*/
-
-#endif /* _ASM_X86_DWARF2_H */
index 3738b138b843d46467c75a910d916cc79ebad25f..155162ea0e00292b619cc2a02ff8b2f31fafd02b 100644 (file)
@@ -1,7 +1,7 @@
 #ifndef _ASM_X86_EFI_H
 #define _ASM_X86_EFI_H
 
-#include <asm/i387.h>
+#include <asm/fpu/api.h>
 #include <asm/pgtable.h>
 
 /*
index dc5fa661465f9a3fda9788c2f6caf9adc185a741..df002992d8fd3dffa9d9d0913e823ae52f9e2256 100644 (file)
@@ -23,6 +23,8 @@ BUILD_INTERRUPT(x86_platform_ipi, X86_PLATFORM_IPI_VECTOR)
 #ifdef CONFIG_HAVE_KVM
 BUILD_INTERRUPT3(kvm_posted_intr_ipi, POSTED_INTR_VECTOR,
                 smp_kvm_posted_intr_ipi)
+BUILD_INTERRUPT3(kvm_posted_intr_wakeup_ipi, POSTED_INTR_WAKEUP_VECTOR,
+                smp_kvm_posted_intr_wakeup_ipi)
 #endif
 
 /*
@@ -50,4 +52,7 @@ BUILD_INTERRUPT(thermal_interrupt,THERMAL_APIC_VECTOR)
 BUILD_INTERRUPT(threshold_interrupt,THRESHOLD_APIC_VECTOR)
 #endif
 
+#ifdef CONFIG_X86_MCE_AMD
+BUILD_INTERRUPT(deferred_error_interrupt, DEFERRED_ERROR_VECTOR)
+#endif
 #endif
diff --git a/arch/x86/include/asm/fpu-internal.h b/arch/x86/include/asm/fpu-internal.h
deleted file mode 100644 (file)
index da5e967..0000000
+++ /dev/null
@@ -1,626 +0,0 @@
-/*
- * Copyright (C) 1994 Linus Torvalds
- *
- * Pentium III FXSR, SSE support
- * General FPU state handling cleanups
- *     Gareth Hughes <gareth@valinux.com>, May 2000
- * x86-64 work by Andi Kleen 2002
- */
-
-#ifndef _FPU_INTERNAL_H
-#define _FPU_INTERNAL_H
-
-#include <linux/kernel_stat.h>
-#include <linux/regset.h>
-#include <linux/compat.h>
-#include <linux/slab.h>
-#include <asm/asm.h>
-#include <asm/cpufeature.h>
-#include <asm/processor.h>
-#include <asm/sigcontext.h>
-#include <asm/user.h>
-#include <asm/uaccess.h>
-#include <asm/xsave.h>
-#include <asm/smap.h>
-
-#ifdef CONFIG_X86_64
-# include <asm/sigcontext32.h>
-# include <asm/user32.h>
-struct ksignal;
-int ia32_setup_rt_frame(int sig, struct ksignal *ksig,
-                       compat_sigset_t *set, struct pt_regs *regs);
-int ia32_setup_frame(int sig, struct ksignal *ksig,
-                    compat_sigset_t *set, struct pt_regs *regs);
-#else
-# define user_i387_ia32_struct user_i387_struct
-# define user32_fxsr_struct    user_fxsr_struct
-# define ia32_setup_frame      __setup_frame
-# define ia32_setup_rt_frame   __setup_rt_frame
-#endif
-
-extern unsigned int mxcsr_feature_mask;
-extern void fpu_init(void);
-extern void eager_fpu_init(void);
-
-DECLARE_PER_CPU(struct task_struct *, fpu_owner_task);
-
-extern void convert_from_fxsr(struct user_i387_ia32_struct *env,
-                             struct task_struct *tsk);
-extern void convert_to_fxsr(struct task_struct *tsk,
-                           const struct user_i387_ia32_struct *env);
-
-extern user_regset_active_fn fpregs_active, xfpregs_active;
-extern user_regset_get_fn fpregs_get, xfpregs_get, fpregs_soft_get,
-                               xstateregs_get;
-extern user_regset_set_fn fpregs_set, xfpregs_set, fpregs_soft_set,
-                                xstateregs_set;
-
-/*
- * xstateregs_active == fpregs_active. Please refer to the comment
- * at the definition of fpregs_active.
- */
-#define xstateregs_active      fpregs_active
-
-#ifdef CONFIG_MATH_EMULATION
-extern void finit_soft_fpu(struct i387_soft_struct *soft);
-#else
-static inline void finit_soft_fpu(struct i387_soft_struct *soft) {}
-#endif
-
-/*
- * Must be run with preemption disabled: this clears the fpu_owner_task,
- * on this CPU.
- *
- * This will disable any lazy FPU state restore of the current FPU state,
- * but if the current thread owns the FPU, it will still be saved by.
- */
-static inline void __cpu_disable_lazy_restore(unsigned int cpu)
-{
-       per_cpu(fpu_owner_task, cpu) = NULL;
-}
-
-/*
- * Used to indicate that the FPU state in memory is newer than the FPU
- * state in registers, and the FPU state should be reloaded next time the
- * task is run. Only safe on the current task, or non-running tasks.
- */
-static inline void task_disable_lazy_fpu_restore(struct task_struct *tsk)
-{
-       tsk->thread.fpu.last_cpu = ~0;
-}
-
-static inline int fpu_lazy_restore(struct task_struct *new, unsigned int cpu)
-{
-       return new == this_cpu_read_stable(fpu_owner_task) &&
-               cpu == new->thread.fpu.last_cpu;
-}
-
-static inline int is_ia32_compat_frame(void)
-{
-       return config_enabled(CONFIG_IA32_EMULATION) &&
-              test_thread_flag(TIF_IA32);
-}
-
-static inline int is_ia32_frame(void)
-{
-       return config_enabled(CONFIG_X86_32) || is_ia32_compat_frame();
-}
-
-static inline int is_x32_frame(void)
-{
-       return config_enabled(CONFIG_X86_X32_ABI) && test_thread_flag(TIF_X32);
-}
-
-#define X87_FSW_ES (1 << 7)    /* Exception Summary */
-
-static __always_inline __pure bool use_eager_fpu(void)
-{
-       return static_cpu_has_safe(X86_FEATURE_EAGER_FPU);
-}
-
-static __always_inline __pure bool use_xsaveopt(void)
-{
-       return static_cpu_has_safe(X86_FEATURE_XSAVEOPT);
-}
-
-static __always_inline __pure bool use_xsave(void)
-{
-       return static_cpu_has_safe(X86_FEATURE_XSAVE);
-}
-
-static __always_inline __pure bool use_fxsr(void)
-{
-       return static_cpu_has_safe(X86_FEATURE_FXSR);
-}
-
-static inline void fx_finit(struct i387_fxsave_struct *fx)
-{
-       fx->cwd = 0x37f;
-       fx->mxcsr = MXCSR_DEFAULT;
-}
-
-extern void __sanitize_i387_state(struct task_struct *);
-
-static inline void sanitize_i387_state(struct task_struct *tsk)
-{
-       if (!use_xsaveopt())
-               return;
-       __sanitize_i387_state(tsk);
-}
-
-#define user_insn(insn, output, input...)                              \
-({                                                                     \
-       int err;                                                        \
-       asm volatile(ASM_STAC "\n"                                      \
-                    "1:" #insn "\n\t"                                  \
-                    "2: " ASM_CLAC "\n"                                \
-                    ".section .fixup,\"ax\"\n"                         \
-                    "3:  movl $-1,%[err]\n"                            \
-                    "    jmp  2b\n"                                    \
-                    ".previous\n"                                      \
-                    _ASM_EXTABLE(1b, 3b)                               \
-                    : [err] "=r" (err), output                         \
-                    : "0"(0), input);                                  \
-       err;                                                            \
-})
-
-#define check_insn(insn, output, input...)                             \
-({                                                                     \
-       int err;                                                        \
-       asm volatile("1:" #insn "\n\t"                                  \
-                    "2:\n"                                             \
-                    ".section .fixup,\"ax\"\n"                         \
-                    "3:  movl $-1,%[err]\n"                            \
-                    "    jmp  2b\n"                                    \
-                    ".previous\n"                                      \
-                    _ASM_EXTABLE(1b, 3b)                               \
-                    : [err] "=r" (err), output                         \
-                    : "0"(0), input);                                  \
-       err;                                                            \
-})
-
-static inline int fsave_user(struct i387_fsave_struct __user *fx)
-{
-       return user_insn(fnsave %[fx]; fwait,  [fx] "=m" (*fx), "m" (*fx));
-}
-
-static inline int fxsave_user(struct i387_fxsave_struct __user *fx)
-{
-       if (config_enabled(CONFIG_X86_32))
-               return user_insn(fxsave %[fx], [fx] "=m" (*fx), "m" (*fx));
-       else if (config_enabled(CONFIG_AS_FXSAVEQ))
-               return user_insn(fxsaveq %[fx], [fx] "=m" (*fx), "m" (*fx));
-
-       /* See comment in fpu_fxsave() below. */
-       return user_insn(rex64/fxsave (%[fx]), "=m" (*fx), [fx] "R" (fx));
-}
-
-static inline int fxrstor_checking(struct i387_fxsave_struct *fx)
-{
-       if (config_enabled(CONFIG_X86_32))
-               return check_insn(fxrstor %[fx], "=m" (*fx), [fx] "m" (*fx));
-       else if (config_enabled(CONFIG_AS_FXSAVEQ))
-               return check_insn(fxrstorq %[fx], "=m" (*fx), [fx] "m" (*fx));
-
-       /* See comment in fpu_fxsave() below. */
-       return check_insn(rex64/fxrstor (%[fx]), "=m" (*fx), [fx] "R" (fx),
-                         "m" (*fx));
-}
-
-static inline int fxrstor_user(struct i387_fxsave_struct __user *fx)
-{
-       if (config_enabled(CONFIG_X86_32))
-               return user_insn(fxrstor %[fx], "=m" (*fx), [fx] "m" (*fx));
-       else if (config_enabled(CONFIG_AS_FXSAVEQ))
-               return user_insn(fxrstorq %[fx], "=m" (*fx), [fx] "m" (*fx));
-
-       /* See comment in fpu_fxsave() below. */
-       return user_insn(rex64/fxrstor (%[fx]), "=m" (*fx), [fx] "R" (fx),
-                         "m" (*fx));
-}
-
-static inline int frstor_checking(struct i387_fsave_struct *fx)
-{
-       return check_insn(frstor %[fx], "=m" (*fx), [fx] "m" (*fx));
-}
-
-static inline int frstor_user(struct i387_fsave_struct __user *fx)
-{
-       return user_insn(frstor %[fx], "=m" (*fx), [fx] "m" (*fx));
-}
-
-static inline void fpu_fxsave(struct fpu *fpu)
-{
-       if (config_enabled(CONFIG_X86_32))
-               asm volatile( "fxsave %[fx]" : [fx] "=m" (fpu->state->fxsave));
-       else if (config_enabled(CONFIG_AS_FXSAVEQ))
-               asm volatile("fxsaveq %[fx]" : [fx] "=m" (fpu->state->fxsave));
-       else {
-               /* Using "rex64; fxsave %0" is broken because, if the memory
-                * operand uses any extended registers for addressing, a second
-                * REX prefix will be generated (to the assembler, rex64
-                * followed by semicolon is a separate instruction), and hence
-                * the 64-bitness is lost.
-                *
-                * Using "fxsaveq %0" would be the ideal choice, but is only
-                * supported starting with gas 2.16.
-                *
-                * Using, as a workaround, the properly prefixed form below
-                * isn't accepted by any binutils version so far released,
-                * complaining that the same type of prefix is used twice if
-                * an extended register is needed for addressing (fix submitted
-                * to mainline 2005-11-21).
-                *
-                *  asm volatile("rex64/fxsave %0" : "=m" (fpu->state->fxsave));
-                *
-                * This, however, we can work around by forcing the compiler to
-                * select an addressing mode that doesn't require extended
-                * registers.
-                */
-               asm volatile( "rex64/fxsave (%[fx])"
-                            : "=m" (fpu->state->fxsave)
-                            : [fx] "R" (&fpu->state->fxsave));
-       }
-}
-
-/*
- * These must be called with preempt disabled. Returns
- * 'true' if the FPU state is still intact.
- */
-static inline int fpu_save_init(struct fpu *fpu)
-{
-       if (use_xsave()) {
-               fpu_xsave(fpu);
-
-               /*
-                * xsave header may indicate the init state of the FP.
-                */
-               if (!(fpu->state->xsave.xsave_hdr.xstate_bv & XSTATE_FP))
-                       return 1;
-       } else if (use_fxsr()) {
-               fpu_fxsave(fpu);
-       } else {
-               asm volatile("fnsave %[fx]; fwait"
-                            : [fx] "=m" (fpu->state->fsave));
-               return 0;
-       }
-
-       /*
-        * If exceptions are pending, we need to clear them so
-        * that we don't randomly get exceptions later.
-        *
-        * FIXME! Is this perhaps only true for the old-style
-        * irq13 case? Maybe we could leave the x87 state
-        * intact otherwise?
-        */
-       if (unlikely(fpu->state->fxsave.swd & X87_FSW_ES)) {
-               asm volatile("fnclex");
-               return 0;
-       }
-       return 1;
-}
-
-static inline int __save_init_fpu(struct task_struct *tsk)
-{
-       return fpu_save_init(&tsk->thread.fpu);
-}
-
-static inline int fpu_restore_checking(struct fpu *fpu)
-{
-       if (use_xsave())
-               return fpu_xrstor_checking(&fpu->state->xsave);
-       else if (use_fxsr())
-               return fxrstor_checking(&fpu->state->fxsave);
-       else
-               return frstor_checking(&fpu->state->fsave);
-}
-
-static inline int restore_fpu_checking(struct task_struct *tsk)
-{
-       /*
-        * AMD K7/K8 CPUs don't save/restore FDP/FIP/FOP unless an exception is
-        * pending. Clear the x87 state here by setting it to fixed values.
-        * "m" is a random variable that should be in L1.
-        */
-       if (unlikely(static_cpu_has_bug_safe(X86_BUG_FXSAVE_LEAK))) {
-               asm volatile(
-                       "fnclex\n\t"
-                       "emms\n\t"
-                       "fildl %P[addr]"        /* set F?P to defined value */
-                       : : [addr] "m" (tsk->thread.fpu.has_fpu));
-       }
-
-       return fpu_restore_checking(&tsk->thread.fpu);
-}
-
-/*
- * Software FPU state helpers. Careful: these need to
- * be preemption protection *and* they need to be
- * properly paired with the CR0.TS changes!
- */
-static inline int __thread_has_fpu(struct task_struct *tsk)
-{
-       return tsk->thread.fpu.has_fpu;
-}
-
-/* Must be paired with an 'stts' after! */
-static inline void __thread_clear_has_fpu(struct task_struct *tsk)
-{
-       tsk->thread.fpu.has_fpu = 0;
-       this_cpu_write(fpu_owner_task, NULL);
-}
-
-/* Must be paired with a 'clts' before! */
-static inline void __thread_set_has_fpu(struct task_struct *tsk)
-{
-       tsk->thread.fpu.has_fpu = 1;
-       this_cpu_write(fpu_owner_task, tsk);
-}
-
-/*
- * Encapsulate the CR0.TS handling together with the
- * software flag.
- *
- * These generally need preemption protection to work,
- * do try to avoid using these on their own.
- */
-static inline void __thread_fpu_end(struct task_struct *tsk)
-{
-       __thread_clear_has_fpu(tsk);
-       if (!use_eager_fpu())
-               stts();
-}
-
-static inline void __thread_fpu_begin(struct task_struct *tsk)
-{
-       if (!use_eager_fpu())
-               clts();
-       __thread_set_has_fpu(tsk);
-}
-
-static inline void drop_fpu(struct task_struct *tsk)
-{
-       /*
-        * Forget coprocessor state..
-        */
-       preempt_disable();
-       tsk->thread.fpu_counter = 0;
-
-       if (__thread_has_fpu(tsk)) {
-               /* Ignore delayed exceptions from user space */
-               asm volatile("1: fwait\n"
-                            "2:\n"
-                            _ASM_EXTABLE(1b, 2b));
-               __thread_fpu_end(tsk);
-       }
-
-       clear_stopped_child_used_math(tsk);
-       preempt_enable();
-}
-
-static inline void restore_init_xstate(void)
-{
-       if (use_xsave())
-               xrstor_state(init_xstate_buf, -1);
-       else
-               fxrstor_checking(&init_xstate_buf->i387);
-}
-
-/*
- * Reset the FPU state in the eager case and drop it in the lazy case (later use
- * will reinit it).
- */
-static inline void fpu_reset_state(struct task_struct *tsk)
-{
-       if (!use_eager_fpu())
-               drop_fpu(tsk);
-       else
-               restore_init_xstate();
-}
-
-/*
- * FPU state switching for scheduling.
- *
- * This is a two-stage process:
- *
- *  - switch_fpu_prepare() saves the old state and
- *    sets the new state of the CR0.TS bit. This is
- *    done within the context of the old process.
- *
- *  - switch_fpu_finish() restores the new state as
- *    necessary.
- */
-typedef struct { int preload; } fpu_switch_t;
-
-static inline fpu_switch_t switch_fpu_prepare(struct task_struct *old, struct task_struct *new, int cpu)
-{
-       fpu_switch_t fpu;
-
-       /*
-        * If the task has used the math, pre-load the FPU on xsave processors
-        * or if the past 5 consecutive context-switches used math.
-        */
-       fpu.preload = tsk_used_math(new) &&
-                     (use_eager_fpu() || new->thread.fpu_counter > 5);
-
-       if (__thread_has_fpu(old)) {
-               if (!__save_init_fpu(old))
-                       task_disable_lazy_fpu_restore(old);
-               else
-                       old->thread.fpu.last_cpu = cpu;
-
-               /* But leave fpu_owner_task! */
-               old->thread.fpu.has_fpu = 0;
-
-               /* Don't change CR0.TS if we just switch! */
-               if (fpu.preload) {
-                       new->thread.fpu_counter++;
-                       __thread_set_has_fpu(new);
-                       prefetch(new->thread.fpu.state);
-               } else if (!use_eager_fpu())
-                       stts();
-       } else {
-               old->thread.fpu_counter = 0;
-               task_disable_lazy_fpu_restore(old);
-               if (fpu.preload) {
-                       new->thread.fpu_counter++;
-                       if (fpu_lazy_restore(new, cpu))
-                               fpu.preload = 0;
-                       else
-                               prefetch(new->thread.fpu.state);
-                       __thread_fpu_begin(new);
-               }
-       }
-       return fpu;
-}
-
-/*
- * By the time this gets called, we've already cleared CR0.TS and
- * given the process the FPU if we are going to preload the FPU
- * state - all we need to do is to conditionally restore the register
- * state itself.
- */
-static inline void switch_fpu_finish(struct task_struct *new, fpu_switch_t fpu)
-{
-       if (fpu.preload) {
-               if (unlikely(restore_fpu_checking(new)))
-                       fpu_reset_state(new);
-       }
-}
-
-/*
- * Signal frame handlers...
- */
-extern int save_xstate_sig(void __user *buf, void __user *fx, int size);
-extern int __restore_xstate_sig(void __user *buf, void __user *fx, int size);
-
-static inline int xstate_sigframe_size(void)
-{
-       return use_xsave() ? xstate_size + FP_XSTATE_MAGIC2_SIZE : xstate_size;
-}
-
-static inline int restore_xstate_sig(void __user *buf, int ia32_frame)
-{
-       void __user *buf_fx = buf;
-       int size = xstate_sigframe_size();
-
-       if (ia32_frame && use_fxsr()) {
-               buf_fx = buf + sizeof(struct i387_fsave_struct);
-               size += sizeof(struct i387_fsave_struct);
-       }
-
-       return __restore_xstate_sig(buf, buf_fx, size);
-}
-
-/*
- * Needs to be preemption-safe.
- *
- * NOTE! user_fpu_begin() must be used only immediately before restoring
- * the save state. It does not do any saving/restoring on its own. In
- * lazy FPU mode, it is just an optimization to avoid a #NM exception,
- * the task can lose the FPU right after preempt_enable().
- */
-static inline void user_fpu_begin(void)
-{
-       preempt_disable();
-       if (!user_has_fpu())
-               __thread_fpu_begin(current);
-       preempt_enable();
-}
-
-static inline void __save_fpu(struct task_struct *tsk)
-{
-       if (use_xsave()) {
-               if (unlikely(system_state == SYSTEM_BOOTING))
-                       xsave_state_booting(&tsk->thread.fpu.state->xsave, -1);
-               else
-                       xsave_state(&tsk->thread.fpu.state->xsave, -1);
-       } else
-               fpu_fxsave(&tsk->thread.fpu);
-}
-
-/*
- * i387 state interaction
- */
-static inline unsigned short get_fpu_cwd(struct task_struct *tsk)
-{
-       if (cpu_has_fxsr) {
-               return tsk->thread.fpu.state->fxsave.cwd;
-       } else {
-               return (unsigned short)tsk->thread.fpu.state->fsave.cwd;
-       }
-}
-
-static inline unsigned short get_fpu_swd(struct task_struct *tsk)
-{
-       if (cpu_has_fxsr) {
-               return tsk->thread.fpu.state->fxsave.swd;
-       } else {
-               return (unsigned short)tsk->thread.fpu.state->fsave.swd;
-       }
-}
-
-static inline unsigned short get_fpu_mxcsr(struct task_struct *tsk)
-{
-       if (cpu_has_xmm) {
-               return tsk->thread.fpu.state->fxsave.mxcsr;
-       } else {
-               return MXCSR_DEFAULT;
-       }
-}
-
-static bool fpu_allocated(struct fpu *fpu)
-{
-       return fpu->state != NULL;
-}
-
-static inline int fpu_alloc(struct fpu *fpu)
-{
-       if (fpu_allocated(fpu))
-               return 0;
-       fpu->state = kmem_cache_alloc(task_xstate_cachep, GFP_KERNEL);
-       if (!fpu->state)
-               return -ENOMEM;
-       WARN_ON((unsigned long)fpu->state & 15);
-       return 0;
-}
-
-static inline void fpu_free(struct fpu *fpu)
-{
-       if (fpu->state) {
-               kmem_cache_free(task_xstate_cachep, fpu->state);
-               fpu->state = NULL;
-       }
-}
-
-static inline void fpu_copy(struct task_struct *dst, struct task_struct *src)
-{
-       if (use_eager_fpu()) {
-               memset(&dst->thread.fpu.state->xsave, 0, xstate_size);
-               __save_fpu(dst);
-       } else {
-               struct fpu *dfpu = &dst->thread.fpu;
-               struct fpu *sfpu = &src->thread.fpu;
-
-               unlazy_fpu(src);
-               memcpy(dfpu->state, sfpu->state, xstate_size);
-       }
-}
-
-static inline unsigned long
-alloc_mathframe(unsigned long sp, int ia32_frame, unsigned long *buf_fx,
-               unsigned long *size)
-{
-       unsigned long frame_size = xstate_sigframe_size();
-
-       *buf_fx = sp = round_down(sp - frame_size, 64);
-       if (ia32_frame && use_fxsr()) {
-               frame_size += sizeof(struct i387_fsave_struct);
-               sp -= sizeof(struct i387_fsave_struct);
-       }
-
-       *size = frame_size;
-       return sp;
-}
-
-#endif
diff --git a/arch/x86/include/asm/fpu/api.h b/arch/x86/include/asm/fpu/api.h
new file mode 100644 (file)
index 0000000..1429a7c
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 1994 Linus Torvalds
+ *
+ * Pentium III FXSR, SSE support
+ * General FPU state handling cleanups
+ *     Gareth Hughes <gareth@valinux.com>, May 2000
+ * x86-64 work by Andi Kleen 2002
+ */
+
+#ifndef _ASM_X86_FPU_API_H
+#define _ASM_X86_FPU_API_H
+
+/*
+ * Careful: __kernel_fpu_begin/end() must be called with preempt disabled
+ * and they don't touch the preempt state on their own.
+ * If you enable preemption after __kernel_fpu_begin(), preempt notifier
+ * should call the __kernel_fpu_end() to prevent the kernel/user FPU
+ * state from getting corrupted. KVM for example uses this model.
+ *
+ * All other cases use kernel_fpu_begin/end() which disable preemption
+ * during kernel FPU usage.
+ */
+extern void __kernel_fpu_begin(void);
+extern void __kernel_fpu_end(void);
+extern void kernel_fpu_begin(void);
+extern void kernel_fpu_end(void);
+extern bool irq_fpu_usable(void);
+
+/*
+ * Some instructions like VIA's padlock instructions generate a spurious
+ * DNA fault but don't modify SSE registers. And these instructions
+ * get used from interrupt context as well. To prevent these kernel instructions
+ * in interrupt context interacting wrongly with other user/kernel fpu usage, we
+ * should use them only in the context of irq_ts_save/restore()
+ */
+extern int  irq_ts_save(void);
+extern void irq_ts_restore(int TS_state);
+
+/*
+ * Query the presence of one or more xfeatures. Works on any legacy CPU as well.
+ *
+ * If 'feature_name' is set then put a human-readable description of
+ * the feature there as well - this can be used to print error (or success)
+ * messages.
+ */
+extern int cpu_has_xfeatures(u64 xfeatures_mask, const char **feature_name);
+
+#endif /* _ASM_X86_FPU_API_H */
diff --git a/arch/x86/include/asm/fpu/internal.h b/arch/x86/include/asm/fpu/internal.h
new file mode 100644 (file)
index 0000000..3c3550c
--- /dev/null
@@ -0,0 +1,694 @@
+/*
+ * Copyright (C) 1994 Linus Torvalds
+ *
+ * Pentium III FXSR, SSE support
+ * General FPU state handling cleanups
+ *     Gareth Hughes <gareth@valinux.com>, May 2000
+ * x86-64 work by Andi Kleen 2002
+ */
+
+#ifndef _ASM_X86_FPU_INTERNAL_H
+#define _ASM_X86_FPU_INTERNAL_H
+
+#include <linux/compat.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+
+#include <asm/user.h>
+#include <asm/fpu/api.h>
+#include <asm/fpu/xstate.h>
+
+/*
+ * High level FPU state handling functions:
+ */
+extern void fpu__activate_curr(struct fpu *fpu);
+extern void fpu__activate_fpstate_read(struct fpu *fpu);
+extern void fpu__activate_fpstate_write(struct fpu *fpu);
+extern void fpu__save(struct fpu *fpu);
+extern void fpu__restore(struct fpu *fpu);
+extern int  fpu__restore_sig(void __user *buf, int ia32_frame);
+extern void fpu__drop(struct fpu *fpu);
+extern int  fpu__copy(struct fpu *dst_fpu, struct fpu *src_fpu);
+extern void fpu__clear(struct fpu *fpu);
+extern int  fpu__exception_code(struct fpu *fpu, int trap_nr);
+extern int  dump_fpu(struct pt_regs *ptregs, struct user_i387_struct *fpstate);
+
+/*
+ * Boot time FPU initialization functions:
+ */
+extern void fpu__init_cpu(void);
+extern void fpu__init_system_xstate(void);
+extern void fpu__init_cpu_xstate(void);
+extern void fpu__init_system(struct cpuinfo_x86 *c);
+extern void fpu__init_check_bugs(void);
+extern void fpu__resume_cpu(void);
+
+/*
+ * Debugging facility:
+ */
+#ifdef CONFIG_X86_DEBUG_FPU
+# define WARN_ON_FPU(x) WARN_ON_ONCE(x)
+#else
+# define WARN_ON_FPU(x) ({ (void)(x); 0; })
+#endif
+
+/*
+ * FPU related CPU feature flag helper routines:
+ */
+static __always_inline __pure bool use_eager_fpu(void)
+{
+       return static_cpu_has_safe(X86_FEATURE_EAGER_FPU);
+}
+
+static __always_inline __pure bool use_xsaveopt(void)
+{
+       return static_cpu_has_safe(X86_FEATURE_XSAVEOPT);
+}
+
+static __always_inline __pure bool use_xsave(void)
+{
+       return static_cpu_has_safe(X86_FEATURE_XSAVE);
+}
+
+static __always_inline __pure bool use_fxsr(void)
+{
+       return static_cpu_has_safe(X86_FEATURE_FXSR);
+}
+
+/*
+ * fpstate handling functions:
+ */
+
+extern union fpregs_state init_fpstate;
+
+extern void fpstate_init(union fpregs_state *state);
+#ifdef CONFIG_MATH_EMULATION
+extern void fpstate_init_soft(struct swregs_state *soft);
+#else
+static inline void fpstate_init_soft(struct swregs_state *soft) {}
+#endif
+static inline void fpstate_init_fxstate(struct fxregs_state *fx)
+{
+       fx->cwd = 0x37f;
+       fx->mxcsr = MXCSR_DEFAULT;
+}
+extern void fpstate_sanitize_xstate(struct fpu *fpu);
+
+#define user_insn(insn, output, input...)                              \
+({                                                                     \
+       int err;                                                        \
+       asm volatile(ASM_STAC "\n"                                      \
+                    "1:" #insn "\n\t"                                  \
+                    "2: " ASM_CLAC "\n"                                \
+                    ".section .fixup,\"ax\"\n"                         \
+                    "3:  movl $-1,%[err]\n"                            \
+                    "    jmp  2b\n"                                    \
+                    ".previous\n"                                      \
+                    _ASM_EXTABLE(1b, 3b)                               \
+                    : [err] "=r" (err), output                         \
+                    : "0"(0), input);                                  \
+       err;                                                            \
+})
+
+#define check_insn(insn, output, input...)                             \
+({                                                                     \
+       int err;                                                        \
+       asm volatile("1:" #insn "\n\t"                                  \
+                    "2:\n"                                             \
+                    ".section .fixup,\"ax\"\n"                         \
+                    "3:  movl $-1,%[err]\n"                            \
+                    "    jmp  2b\n"                                    \
+                    ".previous\n"                                      \
+                    _ASM_EXTABLE(1b, 3b)                               \
+                    : [err] "=r" (err), output                         \
+                    : "0"(0), input);                                  \
+       err;                                                            \
+})
+
+static inline int copy_fregs_to_user(struct fregs_state __user *fx)
+{
+       return user_insn(fnsave %[fx]; fwait,  [fx] "=m" (*fx), "m" (*fx));
+}
+
+static inline int copy_fxregs_to_user(struct fxregs_state __user *fx)
+{
+       if (config_enabled(CONFIG_X86_32))
+               return user_insn(fxsave %[fx], [fx] "=m" (*fx), "m" (*fx));
+       else if (config_enabled(CONFIG_AS_FXSAVEQ))
+               return user_insn(fxsaveq %[fx], [fx] "=m" (*fx), "m" (*fx));
+
+       /* See comment in copy_fxregs_to_kernel() below. */
+       return user_insn(rex64/fxsave (%[fx]), "=m" (*fx), [fx] "R" (fx));
+}
+
+static inline void copy_kernel_to_fxregs(struct fxregs_state *fx)
+{
+       int err;
+
+       if (config_enabled(CONFIG_X86_32)) {
+               err = check_insn(fxrstor %[fx], "=m" (*fx), [fx] "m" (*fx));
+       } else {
+               if (config_enabled(CONFIG_AS_FXSAVEQ)) {
+                       err = check_insn(fxrstorq %[fx], "=m" (*fx), [fx] "m" (*fx));
+               } else {
+                       /* See comment in copy_fxregs_to_kernel() below. */
+                       err = check_insn(rex64/fxrstor (%[fx]), "=m" (*fx), [fx] "R" (fx), "m" (*fx));
+               }
+       }
+       /* Copying from a kernel buffer to FPU registers should never fail: */
+       WARN_ON_FPU(err);
+}
+
+static inline int copy_user_to_fxregs(struct fxregs_state __user *fx)
+{
+       if (config_enabled(CONFIG_X86_32))
+               return user_insn(fxrstor %[fx], "=m" (*fx), [fx] "m" (*fx));
+       else if (config_enabled(CONFIG_AS_FXSAVEQ))
+               return user_insn(fxrstorq %[fx], "=m" (*fx), [fx] "m" (*fx));
+
+       /* See comment in copy_fxregs_to_kernel() below. */
+       return user_insn(rex64/fxrstor (%[fx]), "=m" (*fx), [fx] "R" (fx),
+                         "m" (*fx));
+}
+
+static inline void copy_kernel_to_fregs(struct fregs_state *fx)
+{
+       int err = check_insn(frstor %[fx], "=m" (*fx), [fx] "m" (*fx));
+
+       WARN_ON_FPU(err);
+}
+
+static inline int copy_user_to_fregs(struct fregs_state __user *fx)
+{
+       return user_insn(frstor %[fx], "=m" (*fx), [fx] "m" (*fx));
+}
+
+static inline void copy_fxregs_to_kernel(struct fpu *fpu)
+{
+       if (config_enabled(CONFIG_X86_32))
+               asm volatile( "fxsave %[fx]" : [fx] "=m" (fpu->state.fxsave));
+       else if (config_enabled(CONFIG_AS_FXSAVEQ))
+               asm volatile("fxsaveq %[fx]" : [fx] "=m" (fpu->state.fxsave));
+       else {
+               /* Using "rex64; fxsave %0" is broken because, if the memory
+                * operand uses any extended registers for addressing, a second
+                * REX prefix will be generated (to the assembler, rex64
+                * followed by semicolon is a separate instruction), and hence
+                * the 64-bitness is lost.
+                *
+                * Using "fxsaveq %0" would be the ideal choice, but is only
+                * supported starting with gas 2.16.
+                *
+                * Using, as a workaround, the properly prefixed form below
+                * isn't accepted by any binutils version so far released,
+                * complaining that the same type of prefix is used twice if
+                * an extended register is needed for addressing (fix submitted
+                * to mainline 2005-11-21).
+                *
+                *  asm volatile("rex64/fxsave %0" : "=m" (fpu->state.fxsave));
+                *
+                * This, however, we can work around by forcing the compiler to
+                * select an addressing mode that doesn't require extended
+                * registers.
+                */
+               asm volatile( "rex64/fxsave (%[fx])"
+                            : "=m" (fpu->state.fxsave)
+                            : [fx] "R" (&fpu->state.fxsave));
+       }
+}
+
+/* These macros all use (%edi)/(%rdi) as the single memory argument. */
+#define XSAVE          ".byte " REX_PREFIX "0x0f,0xae,0x27"
+#define XSAVEOPT       ".byte " REX_PREFIX "0x0f,0xae,0x37"
+#define XSAVES         ".byte " REX_PREFIX "0x0f,0xc7,0x2f"
+#define XRSTOR         ".byte " REX_PREFIX "0x0f,0xae,0x2f"
+#define XRSTORS                ".byte " REX_PREFIX "0x0f,0xc7,0x1f"
+
+/* xstate instruction fault handler: */
+#define xstate_fault(__err)            \
+                                       \
+       ".section .fixup,\"ax\"\n"      \
+                                       \
+       "3:  movl $-2,%[_err]\n"        \
+       "    jmp  2b\n"                 \
+                                       \
+       ".previous\n"                   \
+                                       \
+       _ASM_EXTABLE(1b, 3b)            \
+       : [_err] "=r" (__err)
+
+/*
+ * This function is called only during boot time when x86 caps are not set
+ * up and alternative can not be used yet.
+ */
+static inline void copy_xregs_to_kernel_booting(struct xregs_state *xstate)
+{
+       u64 mask = -1;
+       u32 lmask = mask;
+       u32 hmask = mask >> 32;
+       int err = 0;
+
+       WARN_ON(system_state != SYSTEM_BOOTING);
+
+       if (boot_cpu_has(X86_FEATURE_XSAVES))
+               asm volatile("1:"XSAVES"\n\t"
+                       "2:\n\t"
+                            xstate_fault(err)
+                       : "D" (xstate), "m" (*xstate), "a" (lmask), "d" (hmask), "0" (err)
+                       : "memory");
+       else
+               asm volatile("1:"XSAVE"\n\t"
+                       "2:\n\t"
+                            xstate_fault(err)
+                       : "D" (xstate), "m" (*xstate), "a" (lmask), "d" (hmask), "0" (err)
+                       : "memory");
+
+       /* We should never fault when copying to a kernel buffer: */
+       WARN_ON_FPU(err);
+}
+
+/*
+ * This function is called only during boot time when x86 caps are not set
+ * up and alternative can not be used yet.
+ */
+static inline void copy_kernel_to_xregs_booting(struct xregs_state *xstate)
+{
+       u64 mask = -1;
+       u32 lmask = mask;
+       u32 hmask = mask >> 32;
+       int err = 0;
+
+       WARN_ON(system_state != SYSTEM_BOOTING);
+
+       if (boot_cpu_has(X86_FEATURE_XSAVES))
+               asm volatile("1:"XRSTORS"\n\t"
+                       "2:\n\t"
+                            xstate_fault(err)
+                       : "D" (xstate), "m" (*xstate), "a" (lmask), "d" (hmask), "0" (err)
+                       : "memory");
+       else
+               asm volatile("1:"XRSTOR"\n\t"
+                       "2:\n\t"
+                            xstate_fault(err)
+                       : "D" (xstate), "m" (*xstate), "a" (lmask), "d" (hmask), "0" (err)
+                       : "memory");
+
+       /* We should never fault when copying from a kernel buffer: */
+       WARN_ON_FPU(err);
+}
+
+/*
+ * Save processor xstate to xsave area.
+ */
+static inline void copy_xregs_to_kernel(struct xregs_state *xstate)
+{
+       u64 mask = -1;
+       u32 lmask = mask;
+       u32 hmask = mask >> 32;
+       int err = 0;
+
+       WARN_ON(!alternatives_patched);
+
+       /*
+        * If xsaves is enabled, xsaves replaces xsaveopt because
+        * it supports compact format and supervisor states in addition to
+        * modified optimization in xsaveopt.
+        *
+        * Otherwise, if xsaveopt is enabled, xsaveopt replaces xsave
+        * because xsaveopt supports modified optimization which is not
+        * supported by xsave.
+        *
+        * If none of xsaves and xsaveopt is enabled, use xsave.
+        */
+       alternative_input_2(
+               "1:"XSAVE,
+               XSAVEOPT,
+               X86_FEATURE_XSAVEOPT,
+               XSAVES,
+               X86_FEATURE_XSAVES,
+               [xstate] "D" (xstate), "a" (lmask), "d" (hmask) :
+               "memory");
+       asm volatile("2:\n\t"
+                    xstate_fault(err)
+                    : "0" (err)
+                    : "memory");
+
+       /* We should never fault when copying to a kernel buffer: */
+       WARN_ON_FPU(err);
+}
+
+/*
+ * Restore processor xstate from xsave area.
+ */
+static inline void copy_kernel_to_xregs(struct xregs_state *xstate, u64 mask)
+{
+       u32 lmask = mask;
+       u32 hmask = mask >> 32;
+       int err = 0;
+
+       /*
+        * Use xrstors to restore context if it is enabled. xrstors supports
+        * compacted format of xsave area which is not supported by xrstor.
+        */
+       alternative_input(
+               "1: " XRSTOR,
+               XRSTORS,
+               X86_FEATURE_XSAVES,
+               "D" (xstate), "m" (*xstate), "a" (lmask), "d" (hmask)
+               : "memory");
+
+       asm volatile("2:\n"
+                    xstate_fault(err)
+                    : "0" (err)
+                    : "memory");
+
+       /* We should never fault when copying from a kernel buffer: */
+       WARN_ON_FPU(err);
+}
+
+/*
+ * Save xstate to user space xsave area.
+ *
+ * We don't use modified optimization because xrstor/xrstors might track
+ * a different application.
+ *
+ * We don't use compacted format xsave area for
+ * backward compatibility for old applications which don't understand
+ * compacted format of xsave area.
+ */
+static inline int copy_xregs_to_user(struct xregs_state __user *buf)
+{
+       int err;
+
+       /*
+        * Clear the xsave header first, so that reserved fields are
+        * initialized to zero.
+        */
+       err = __clear_user(&buf->header, sizeof(buf->header));
+       if (unlikely(err))
+               return -EFAULT;
+
+       __asm__ __volatile__(ASM_STAC "\n"
+                            "1:"XSAVE"\n"
+                            "2: " ASM_CLAC "\n"
+                            xstate_fault(err)
+                            : "D" (buf), "a" (-1), "d" (-1), "0" (err)
+                            : "memory");
+       return err;
+}
+
+/*
+ * Restore xstate from user space xsave area.
+ */
+static inline int copy_user_to_xregs(struct xregs_state __user *buf, u64 mask)
+{
+       struct xregs_state *xstate = ((__force struct xregs_state *)buf);
+       u32 lmask = mask;
+       u32 hmask = mask >> 32;
+       int err = 0;
+
+       __asm__ __volatile__(ASM_STAC "\n"
+                            "1:"XRSTOR"\n"
+                            "2: " ASM_CLAC "\n"
+                            xstate_fault(err)
+                            : "D" (xstate), "a" (lmask), "d" (hmask), "0" (err)
+                            : "memory");       /* memory required? */
+       return err;
+}
+
+/*
+ * These must be called with preempt disabled. Returns
+ * 'true' if the FPU state is still intact and we can
+ * keep registers active.
+ *
+ * The legacy FNSAVE instruction cleared all FPU state
+ * unconditionally, so registers are essentially destroyed.
+ * Modern FPU state can be kept in registers, if there are
+ * no pending FP exceptions.
+ */
+static inline int copy_fpregs_to_fpstate(struct fpu *fpu)
+{
+       if (likely(use_xsave())) {
+               copy_xregs_to_kernel(&fpu->state.xsave);
+               return 1;
+       }
+
+       if (likely(use_fxsr())) {
+               copy_fxregs_to_kernel(fpu);
+               return 1;
+       }
+
+       /*
+        * Legacy FPU register saving, FNSAVE always clears FPU registers,
+        * so we have to mark them inactive:
+        */
+       asm volatile("fnsave %[fp]; fwait" : [fp] "=m" (fpu->state.fsave));
+
+       return 0;
+}
+
+static inline void __copy_kernel_to_fpregs(union fpregs_state *fpstate)
+{
+       if (use_xsave()) {
+               copy_kernel_to_xregs(&fpstate->xsave, -1);
+       } else {
+               if (use_fxsr())
+                       copy_kernel_to_fxregs(&fpstate->fxsave);
+               else
+                       copy_kernel_to_fregs(&fpstate->fsave);
+       }
+}
+
+static inline void copy_kernel_to_fpregs(union fpregs_state *fpstate)
+{
+       /*
+        * AMD K7/K8 CPUs don't save/restore FDP/FIP/FOP unless an exception is
+        * pending. Clear the x87 state here by setting it to fixed values.
+        * "m" is a random variable that should be in L1.
+        */
+       if (unlikely(static_cpu_has_bug_safe(X86_BUG_FXSAVE_LEAK))) {
+               asm volatile(
+                       "fnclex\n\t"
+                       "emms\n\t"
+                       "fildl %P[addr]"        /* set F?P to defined value */
+                       : : [addr] "m" (fpstate));
+       }
+
+       __copy_kernel_to_fpregs(fpstate);
+}
+
+extern int copy_fpstate_to_sigframe(void __user *buf, void __user *fp, int size);
+
+/*
+ * FPU context switch related helper methods:
+ */
+
+DECLARE_PER_CPU(struct fpu *, fpu_fpregs_owner_ctx);
+
+/*
+ * Must be run with preemption disabled: this clears the fpu_fpregs_owner_ctx,
+ * on this CPU.
+ *
+ * This will disable any lazy FPU state restore of the current FPU state,
+ * but if the current thread owns the FPU, it will still be saved by.
+ */
+static inline void __cpu_disable_lazy_restore(unsigned int cpu)
+{
+       per_cpu(fpu_fpregs_owner_ctx, cpu) = NULL;
+}
+
+static inline int fpu_want_lazy_restore(struct fpu *fpu, unsigned int cpu)
+{
+       return fpu == this_cpu_read_stable(fpu_fpregs_owner_ctx) && cpu == fpu->last_cpu;
+}
+
+
+/*
+ * Wrap lazy FPU TS handling in a 'hw fpregs activation/deactivation'
+ * idiom, which is then paired with the sw-flag (fpregs_active) later on:
+ */
+
+static inline void __fpregs_activate_hw(void)
+{
+       if (!use_eager_fpu())
+               clts();
+}
+
+static inline void __fpregs_deactivate_hw(void)
+{
+       if (!use_eager_fpu())
+               stts();
+}
+
+/* Must be paired with an 'stts' (fpregs_deactivate_hw()) after! */
+static inline void __fpregs_deactivate(struct fpu *fpu)
+{
+       WARN_ON_FPU(!fpu->fpregs_active);
+
+       fpu->fpregs_active = 0;
+       this_cpu_write(fpu_fpregs_owner_ctx, NULL);
+}
+
+/* Must be paired with a 'clts' (fpregs_activate_hw()) before! */
+static inline void __fpregs_activate(struct fpu *fpu)
+{
+       WARN_ON_FPU(fpu->fpregs_active);
+
+       fpu->fpregs_active = 1;
+       this_cpu_write(fpu_fpregs_owner_ctx, fpu);
+}
+
+/*
+ * The question "does this thread have fpu access?"
+ * is slightly racy, since preemption could come in
+ * and revoke it immediately after the test.
+ *
+ * However, even in that very unlikely scenario,
+ * we can just assume we have FPU access - typically
+ * to save the FP state - we'll just take a #NM
+ * fault and get the FPU access back.
+ */
+static inline int fpregs_active(void)
+{
+       return current->thread.fpu.fpregs_active;
+}
+
+/*
+ * Encapsulate the CR0.TS handling together with the
+ * software flag.
+ *
+ * These generally need preemption protection to work,
+ * do try to avoid using these on their own.
+ */
+static inline void fpregs_activate(struct fpu *fpu)
+{
+       __fpregs_activate_hw();
+       __fpregs_activate(fpu);
+}
+
+static inline void fpregs_deactivate(struct fpu *fpu)
+{
+       __fpregs_deactivate(fpu);
+       __fpregs_deactivate_hw();
+}
+
+/*
+ * FPU state switching for scheduling.
+ *
+ * This is a two-stage process:
+ *
+ *  - switch_fpu_prepare() saves the old state and
+ *    sets the new state of the CR0.TS bit. This is
+ *    done within the context of the old process.
+ *
+ *  - switch_fpu_finish() restores the new state as
+ *    necessary.
+ */
+typedef struct { int preload; } fpu_switch_t;
+
+static inline fpu_switch_t
+switch_fpu_prepare(struct fpu *old_fpu, struct fpu *new_fpu, int cpu)
+{
+       fpu_switch_t fpu;
+
+       /*
+        * If the task has used the math, pre-load the FPU on xsave processors
+        * or if the past 5 consecutive context-switches used math.
+        */
+       fpu.preload = new_fpu->fpstate_active &&
+                     (use_eager_fpu() || new_fpu->counter > 5);
+
+       if (old_fpu->fpregs_active) {
+               if (!copy_fpregs_to_fpstate(old_fpu))
+                       old_fpu->last_cpu = -1;
+               else
+                       old_fpu->last_cpu = cpu;
+
+               /* But leave fpu_fpregs_owner_ctx! */
+               old_fpu->fpregs_active = 0;
+
+               /* Don't change CR0.TS if we just switch! */
+               if (fpu.preload) {
+                       new_fpu->counter++;
+                       __fpregs_activate(new_fpu);
+                       prefetch(&new_fpu->state);
+               } else {
+                       __fpregs_deactivate_hw();
+               }
+       } else {
+               old_fpu->counter = 0;
+               old_fpu->last_cpu = -1;
+               if (fpu.preload) {
+                       new_fpu->counter++;
+                       if (fpu_want_lazy_restore(new_fpu, cpu))
+                               fpu.preload = 0;
+                       else
+                               prefetch(&new_fpu->state);
+                       fpregs_activate(new_fpu);
+               }
+       }
+       return fpu;
+}
+
+/*
+ * Misc helper functions:
+ */
+
+/*
+ * By the time this gets called, we've already cleared CR0.TS and
+ * given the process the FPU if we are going to preload the FPU
+ * state - all we need to do is to conditionally restore the register
+ * state itself.
+ */
+static inline void switch_fpu_finish(struct fpu *new_fpu, fpu_switch_t fpu_switch)
+{
+       if (fpu_switch.preload)
+               copy_kernel_to_fpregs(&new_fpu->state);
+}
+
+/*
+ * Needs to be preemption-safe.
+ *
+ * NOTE! user_fpu_begin() must be used only immediately before restoring
+ * the save state. It does not do any saving/restoring on its own. In
+ * lazy FPU mode, it is just an optimization to avoid a #NM exception,
+ * the task can lose the FPU right after preempt_enable().
+ */
+static inline void user_fpu_begin(void)
+{
+       struct fpu *fpu = &current->thread.fpu;
+
+       preempt_disable();
+       if (!fpregs_active())
+               fpregs_activate(fpu);
+       preempt_enable();
+}
+
+/*
+ * MXCSR and XCR definitions:
+ */
+
+extern unsigned int mxcsr_feature_mask;
+
+#define XCR_XFEATURE_ENABLED_MASK      0x00000000
+
+static inline u64 xgetbv(u32 index)
+{
+       u32 eax, edx;
+
+       asm volatile(".byte 0x0f,0x01,0xd0" /* xgetbv */
+                    : "=a" (eax), "=d" (edx)
+                    : "c" (index));
+       return eax + ((u64)edx << 32);
+}
+
+static inline void xsetbv(u32 index, u64 value)
+{
+       u32 eax = value;
+       u32 edx = value >> 32;
+
+       asm volatile(".byte 0x0f,0x01,0xd1" /* xsetbv */
+                    : : "a" (eax), "d" (edx), "c" (index));
+}
+
+#endif /* _ASM_X86_FPU_INTERNAL_H */
diff --git a/arch/x86/include/asm/fpu/regset.h b/arch/x86/include/asm/fpu/regset.h
new file mode 100644 (file)
index 0000000..39d3107
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * FPU regset handling methods:
+ */
+#ifndef _ASM_X86_FPU_REGSET_H
+#define _ASM_X86_FPU_REGSET_H
+
+#include <linux/regset.h>
+
+extern user_regset_active_fn regset_fpregs_active, regset_xregset_fpregs_active;
+extern user_regset_get_fn fpregs_get, xfpregs_get, fpregs_soft_get,
+                               xstateregs_get;
+extern user_regset_set_fn fpregs_set, xfpregs_set, fpregs_soft_set,
+                                xstateregs_set;
+
+/*
+ * xstateregs_active == regset_fpregs_active. Please refer to the comment
+ * at the definition of regset_fpregs_active.
+ */
+#define xstateregs_active      regset_fpregs_active
+
+#endif /* _ASM_X86_FPU_REGSET_H */
diff --git a/arch/x86/include/asm/fpu/signal.h b/arch/x86/include/asm/fpu/signal.h
new file mode 100644 (file)
index 0000000..7358e9d
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * x86 FPU signal frame handling methods:
+ */
+#ifndef _ASM_X86_FPU_SIGNAL_H
+#define _ASM_X86_FPU_SIGNAL_H
+
+#ifdef CONFIG_X86_64
+# include <asm/sigcontext32.h>
+# include <asm/user32.h>
+struct ksignal;
+int ia32_setup_rt_frame(int sig, struct ksignal *ksig,
+                       compat_sigset_t *set, struct pt_regs *regs);
+int ia32_setup_frame(int sig, struct ksignal *ksig,
+                    compat_sigset_t *set, struct pt_regs *regs);
+#else
+# define user_i387_ia32_struct user_i387_struct
+# define user32_fxsr_struct    user_fxsr_struct
+# define ia32_setup_frame      __setup_frame
+# define ia32_setup_rt_frame   __setup_rt_frame
+#endif
+
+extern void convert_from_fxsr(struct user_i387_ia32_struct *env,
+                             struct task_struct *tsk);
+extern void convert_to_fxsr(struct task_struct *tsk,
+                           const struct user_i387_ia32_struct *env);
+
+unsigned long
+fpu__alloc_mathframe(unsigned long sp, int ia32_frame,
+                    unsigned long *buf_fx, unsigned long *size);
+
+extern void fpu__init_prepare_fx_sw_frame(void);
+
+#endif /* _ASM_X86_FPU_SIGNAL_H */
diff --git a/arch/x86/include/asm/fpu/types.h b/arch/x86/include/asm/fpu/types.h
new file mode 100644 (file)
index 0000000..0637826
--- /dev/null
@@ -0,0 +1,293 @@
+/*
+ * FPU data structures:
+ */
+#ifndef _ASM_X86_FPU_H
+#define _ASM_X86_FPU_H
+
+/*
+ * The legacy x87 FPU state format, as saved by FSAVE and
+ * restored by the FRSTOR instructions:
+ */
+struct fregs_state {
+       u32                     cwd;    /* FPU Control Word             */
+       u32                     swd;    /* FPU Status Word              */
+       u32                     twd;    /* FPU Tag Word                 */
+       u32                     fip;    /* FPU IP Offset                */
+       u32                     fcs;    /* FPU IP Selector              */
+       u32                     foo;    /* FPU Operand Pointer Offset   */
+       u32                     fos;    /* FPU Operand Pointer Selector */
+
+       /* 8*10 bytes for each FP-reg = 80 bytes:                       */
+       u32                     st_space[20];
+
+       /* Software status information [not touched by FSAVE]:          */
+       u32                     status;
+};
+
+/*
+ * The legacy fx SSE/MMX FPU state format, as saved by FXSAVE and
+ * restored by the FXRSTOR instructions. It's similar to the FSAVE
+ * format, but differs in some areas, plus has extensions at
+ * the end for the XMM registers.
+ */
+struct fxregs_state {
+       u16                     cwd; /* Control Word                    */
+       u16                     swd; /* Status Word                     */
+       u16                     twd; /* Tag Word                        */
+       u16                     fop; /* Last Instruction Opcode         */
+       union {
+               struct {
+                       u64     rip; /* Instruction Pointer             */
+                       u64     rdp; /* Data Pointer                    */
+               };
+               struct {
+                       u32     fip; /* FPU IP Offset                   */
+                       u32     fcs; /* FPU IP Selector                 */
+                       u32     foo; /* FPU Operand Offset              */
+                       u32     fos; /* FPU Operand Selector            */
+               };
+       };
+       u32                     mxcsr;          /* MXCSR Register State */
+       u32                     mxcsr_mask;     /* MXCSR Mask           */
+
+       /* 8*16 bytes for each FP-reg = 128 bytes:                      */
+       u32                     st_space[32];
+
+       /* 16*16 bytes for each XMM-reg = 256 bytes:                    */
+       u32                     xmm_space[64];
+
+       u32                     padding[12];
+
+       union {
+               u32             padding1[12];
+               u32             sw_reserved[12];
+       };
+
+} __attribute__((aligned(16)));
+
+/* Default value for fxregs_state.mxcsr: */
+#define MXCSR_DEFAULT          0x1f80
+
+/*
+ * Software based FPU emulation state. This is arbitrary really,
+ * it matches the x87 format to make it easier to understand:
+ */
+struct swregs_state {
+       u32                     cwd;
+       u32                     swd;
+       u32                     twd;
+       u32                     fip;
+       u32                     fcs;
+       u32                     foo;
+       u32                     fos;
+       /* 8*10 bytes for each FP-reg = 80 bytes: */
+       u32                     st_space[20];
+       u8                      ftop;
+       u8                      changed;
+       u8                      lookahead;
+       u8                      no_update;
+       u8                      rm;
+       u8                      alimit;
+       struct math_emu_info    *info;
+       u32                     entry_eip;
+};
+
+/*
+ * List of XSAVE features Linux knows about:
+ */
+enum xfeature_bit {
+       XSTATE_BIT_FP,
+       XSTATE_BIT_SSE,
+       XSTATE_BIT_YMM,
+       XSTATE_BIT_BNDREGS,
+       XSTATE_BIT_BNDCSR,
+       XSTATE_BIT_OPMASK,
+       XSTATE_BIT_ZMM_Hi256,
+       XSTATE_BIT_Hi16_ZMM,
+
+       XFEATURES_NR_MAX,
+};
+
+#define XSTATE_FP              (1 << XSTATE_BIT_FP)
+#define XSTATE_SSE             (1 << XSTATE_BIT_SSE)
+#define XSTATE_YMM             (1 << XSTATE_BIT_YMM)
+#define XSTATE_BNDREGS         (1 << XSTATE_BIT_BNDREGS)
+#define XSTATE_BNDCSR          (1 << XSTATE_BIT_BNDCSR)
+#define XSTATE_OPMASK          (1 << XSTATE_BIT_OPMASK)
+#define XSTATE_ZMM_Hi256       (1 << XSTATE_BIT_ZMM_Hi256)
+#define XSTATE_Hi16_ZMM                (1 << XSTATE_BIT_Hi16_ZMM)
+
+#define XSTATE_FPSSE           (XSTATE_FP | XSTATE_SSE)
+#define XSTATE_AVX512          (XSTATE_OPMASK | XSTATE_ZMM_Hi256 | XSTATE_Hi16_ZMM)
+
+/*
+ * There are 16x 256-bit AVX registers named YMM0-YMM15.
+ * The low 128 bits are aliased to the 16 SSE registers (XMM0-XMM15)
+ * and are stored in 'struct fxregs_state::xmm_space[]'.
+ *
+ * The high 128 bits are stored here:
+ *    16x 128 bits == 256 bytes.
+ */
+struct ymmh_struct {
+       u8                              ymmh_space[256];
+};
+
+/* We don't support LWP yet: */
+struct lwp_struct {
+       u8                              reserved[128];
+};
+
+/* Intel MPX support: */
+struct bndreg {
+       u64                             lower_bound;
+       u64                             upper_bound;
+} __packed;
+
+struct bndcsr {
+       u64                             bndcfgu;
+       u64                             bndstatus;
+} __packed;
+
+struct mpx_struct {
+       struct bndreg                   bndreg[4];
+       struct bndcsr                   bndcsr;
+};
+
+struct xstate_header {
+       u64                             xfeatures;
+       u64                             xcomp_bv;
+       u64                             reserved[6];
+} __attribute__((packed));
+
+/* New processor state extensions should be added here: */
+#define XSTATE_RESERVE                 (sizeof(struct ymmh_struct) + \
+                                        sizeof(struct lwp_struct)  + \
+                                        sizeof(struct mpx_struct)  )
+/*
+ * This is our most modern FPU state format, as saved by the XSAVE
+ * and restored by the XRSTOR instructions.
+ *
+ * It consists of a legacy fxregs portion, an xstate header and
+ * subsequent fixed size areas as defined by the xstate header.
+ * Not all CPUs support all the extensions.
+ */
+struct xregs_state {
+       struct fxregs_state             i387;
+       struct xstate_header            header;
+       u8                              __reserved[XSTATE_RESERVE];
+} __attribute__ ((packed, aligned (64)));
+
+/*
+ * This is a union of all the possible FPU state formats
+ * put together, so that we can pick the right one runtime.
+ *
+ * The size of the structure is determined by the largest
+ * member - which is the xsave area:
+ */
+union fpregs_state {
+       struct fregs_state              fsave;
+       struct fxregs_state             fxsave;
+       struct swregs_state             soft;
+       struct xregs_state              xsave;
+};
+
+/*
+ * Highest level per task FPU state data structure that
+ * contains the FPU register state plus various FPU
+ * state fields:
+ */
+struct fpu {
+       /*
+        * @state:
+        *
+        * In-memory copy of all FPU registers that we save/restore
+        * over context switches. If the task is using the FPU then
+        * the registers in the FPU are more recent than this state
+        * copy. If the task context-switches away then they get
+        * saved here and represent the FPU state.
+        *
+        * After context switches there may be a (short) time period
+        * during which the in-FPU hardware registers are unchanged
+        * and still perfectly match this state, if the tasks
+        * scheduled afterwards are not using the FPU.
+        *
+        * This is the 'lazy restore' window of optimization, which
+        * we track though 'fpu_fpregs_owner_ctx' and 'fpu->last_cpu'.
+        *
+        * We detect whether a subsequent task uses the FPU via setting
+        * CR0::TS to 1, which causes any FPU use to raise a #NM fault.
+        *
+        * During this window, if the task gets scheduled again, we
+        * might be able to skip having to do a restore from this
+        * memory buffer to the hardware registers - at the cost of
+        * incurring the overhead of #NM fault traps.
+        *
+        * Note that on modern CPUs that support the XSAVEOPT (or other
+        * optimized XSAVE instructions), we don't use #NM traps anymore,
+        * as the hardware can track whether FPU registers need saving
+        * or not. On such CPUs we activate the non-lazy ('eagerfpu')
+        * logic, which unconditionally saves/restores all FPU state
+        * across context switches. (if FPU state exists.)
+        */
+       union fpregs_state              state;
+
+       /*
+        * @last_cpu:
+        *
+        * Records the last CPU on which this context was loaded into
+        * FPU registers. (In the lazy-restore case we might be
+        * able to reuse FPU registers across multiple context switches
+        * this way, if no intermediate task used the FPU.)
+        *
+        * A value of -1 is used to indicate that the FPU state in context
+        * memory is newer than the FPU state in registers, and that the
+        * FPU state should be reloaded next time the task is run.
+        */
+       unsigned int                    last_cpu;
+
+       /*
+        * @fpstate_active:
+        *
+        * This flag indicates whether this context is active: if the task
+        * is not running then we can restore from this context, if the task
+        * is running then we should save into this context.
+        */
+       unsigned char                   fpstate_active;
+
+       /*
+        * @fpregs_active:
+        *
+        * This flag determines whether a given context is actively
+        * loaded into the FPU's registers and that those registers
+        * represent the task's current FPU state.
+        *
+        * Note the interaction with fpstate_active:
+        *
+        *   # task does not use the FPU:
+        *   fpstate_active == 0
+        *
+        *   # task uses the FPU and regs are active:
+        *   fpstate_active == 1 && fpregs_active == 1
+        *
+        *   # the regs are inactive but still match fpstate:
+        *   fpstate_active == 1 && fpregs_active == 0 && fpregs_owner == fpu
+        *
+        * The third state is what we use for the lazy restore optimization
+        * on lazy-switching CPUs.
+        */
+       unsigned char                   fpregs_active;
+
+       /*
+        * @counter:
+        *
+        * This counter contains the number of consecutive context switches
+        * during which the FPU stays used. If this is over a threshold, the
+        * lazy FPU restore logic becomes eager, to save the trap overhead.
+        * This is an unsigned char so that after 256 iterations the counter
+        * wraps and the context switch behavior turns lazy again; this is to
+        * deal with bursty apps that only use the FPU for a short time:
+        */
+       unsigned char                   counter;
+};
+
+#endif /* _ASM_X86_FPU_H */
diff --git a/arch/x86/include/asm/fpu/xstate.h b/arch/x86/include/asm/fpu/xstate.h
new file mode 100644 (file)
index 0000000..4656b25
--- /dev/null
@@ -0,0 +1,46 @@
+#ifndef __ASM_X86_XSAVE_H
+#define __ASM_X86_XSAVE_H
+
+#include <linux/types.h>
+#include <asm/processor.h>
+#include <linux/uaccess.h>
+
+/* Bit 63 of XCR0 is reserved for future expansion */
+#define XSTATE_EXTEND_MASK     (~(XSTATE_FPSSE | (1ULL << 63)))
+
+#define XSTATE_CPUID           0x0000000d
+
+#define FXSAVE_SIZE    512
+
+#define XSAVE_HDR_SIZE     64
+#define XSAVE_HDR_OFFSET    FXSAVE_SIZE
+
+#define XSAVE_YMM_SIZE     256
+#define XSAVE_YMM_OFFSET    (XSAVE_HDR_SIZE + XSAVE_HDR_OFFSET)
+
+/* Supported features which support lazy state saving */
+#define XSTATE_LAZY    (XSTATE_FP | XSTATE_SSE | XSTATE_YMM                  \
+                       | XSTATE_OPMASK | XSTATE_ZMM_Hi256 | XSTATE_Hi16_ZMM)
+
+/* Supported features which require eager state saving */
+#define XSTATE_EAGER   (XSTATE_BNDREGS | XSTATE_BNDCSR)
+
+/* All currently supported features */
+#define XCNTXT_MASK    (XSTATE_LAZY | XSTATE_EAGER)
+
+#ifdef CONFIG_X86_64
+#define REX_PREFIX     "0x48, "
+#else
+#define REX_PREFIX
+#endif
+
+extern unsigned int xstate_size;
+extern u64 xfeatures_mask;
+extern u64 xstate_fx_sw_bytes[USER_XSTATE_FX_SW_WORDS];
+
+extern void update_regset_xstate_info(unsigned int size, u64 xstate_mask);
+
+void *get_xsave_addr(struct xregs_state *xsave, int xstate);
+const void *get_xsave_field_ptr(int xstate_field);
+
+#endif
index 3b629f47eb65b4a51f6399ad153d44f9b5c24b0d..793179cf8e21aa89636f869fc3a9e2fe0b4a29e0 100644 (file)
@@ -1,20 +1,17 @@
 #ifdef __ASSEMBLY__
 
 #include <asm/asm.h>
-#include <asm/dwarf2.h>
 
 /* The annotation hides the frame from the unwinder and makes it look
    like a ordinary ebp save/restore. This avoids some special cases for
    frame pointer later */
 #ifdef CONFIG_FRAME_POINTER
        .macro FRAME
-       __ASM_SIZE(push,_cfi)   %__ASM_REG(bp)
-       CFI_REL_OFFSET          __ASM_REG(bp), 0
+       __ASM_SIZE(push,)       %__ASM_REG(bp)
        __ASM_SIZE(mov)         %__ASM_REG(sp), %__ASM_REG(bp)
        .endm
        .macro ENDFRAME
-       __ASM_SIZE(pop,_cfi)    %__ASM_REG(bp)
-       CFI_RESTORE             __ASM_REG(bp)
+       __ASM_SIZE(pop,)        %__ASM_REG(bp)
        .endm
 #else
        .macro FRAME
index 0f5fb6b6567e9c7e2856e86678189a82af0d853e..7178043b0e1dd69d20a6ff5ddaa37ee6c32841f8 100644 (file)
@@ -14,6 +14,7 @@ typedef struct {
 #endif
 #ifdef CONFIG_HAVE_KVM
        unsigned int kvm_posted_intr_ipis;
+       unsigned int kvm_posted_intr_wakeup_ipis;
 #endif
        unsigned int x86_platform_ipis; /* arch dependent */
        unsigned int apic_perf_irqs;
@@ -33,6 +34,9 @@ typedef struct {
 #ifdef CONFIG_X86_MCE_THRESHOLD
        unsigned int irq_threshold_count;
 #endif
+#ifdef CONFIG_X86_MCE_AMD
+       unsigned int irq_deferred_error_count;
+#endif
 #if IS_ENABLED(CONFIG_HYPERV) || defined(CONFIG_XEN)
        unsigned int irq_hv_callback_count;
 #endif
index 36f7125945e3e241cdf2ac825124fd8d7883e0ba..5fa9fb0f8809902a8e15f6f8fa1d245d379a6a16 100644 (file)
@@ -74,20 +74,16 @@ extern unsigned int hpet_readl(unsigned int a);
 extern void force_hpet_resume(void);
 
 struct irq_data;
+struct hpet_dev;
+struct irq_domain;
+
 extern void hpet_msi_unmask(struct irq_data *data);
 extern void hpet_msi_mask(struct irq_data *data);
-struct hpet_dev;
 extern void hpet_msi_write(struct hpet_dev *hdev, struct msi_msg *msg);
 extern void hpet_msi_read(struct hpet_dev *hdev, struct msi_msg *msg);
-
-#ifdef CONFIG_PCI_MSI
-extern int default_setup_hpet_msi(unsigned int irq, unsigned int id);
-#else
-static inline int default_setup_hpet_msi(unsigned int irq, unsigned int id)
-{
-       return -EINVAL;
-}
-#endif
+extern struct irq_domain *hpet_create_irq_domain(int hpet_id);
+extern int hpet_assign_irq(struct irq_domain *domain,
+                          struct hpet_dev *dev, int dev_num);
 
 #ifdef CONFIG_HPET_EMULATE_RTC
 
index e9571ddabc4feb821ae04d47c9d6c3b509178344..6615032e19c80b79f3cd023ee291c790bcb07ae2 100644 (file)
@@ -29,6 +29,7 @@
 extern asmlinkage void apic_timer_interrupt(void);
 extern asmlinkage void x86_platform_ipi(void);
 extern asmlinkage void kvm_posted_intr_ipi(void);
+extern asmlinkage void kvm_posted_intr_wakeup_ipi(void);
 extern asmlinkage void error_interrupt(void);
 extern asmlinkage void irq_work_interrupt(void);
 
@@ -36,43 +37,10 @@ extern asmlinkage void spurious_interrupt(void);
 extern asmlinkage void thermal_interrupt(void);
 extern asmlinkage void reschedule_interrupt(void);
 
-extern asmlinkage void invalidate_interrupt(void);
-extern asmlinkage void invalidate_interrupt0(void);
-extern asmlinkage void invalidate_interrupt1(void);
-extern asmlinkage void invalidate_interrupt2(void);
-extern asmlinkage void invalidate_interrupt3(void);
-extern asmlinkage void invalidate_interrupt4(void);
-extern asmlinkage void invalidate_interrupt5(void);
-extern asmlinkage void invalidate_interrupt6(void);
-extern asmlinkage void invalidate_interrupt7(void);
-extern asmlinkage void invalidate_interrupt8(void);
-extern asmlinkage void invalidate_interrupt9(void);
-extern asmlinkage void invalidate_interrupt10(void);
-extern asmlinkage void invalidate_interrupt11(void);
-extern asmlinkage void invalidate_interrupt12(void);
-extern asmlinkage void invalidate_interrupt13(void);
-extern asmlinkage void invalidate_interrupt14(void);
-extern asmlinkage void invalidate_interrupt15(void);
-extern asmlinkage void invalidate_interrupt16(void);
-extern asmlinkage void invalidate_interrupt17(void);
-extern asmlinkage void invalidate_interrupt18(void);
-extern asmlinkage void invalidate_interrupt19(void);
-extern asmlinkage void invalidate_interrupt20(void);
-extern asmlinkage void invalidate_interrupt21(void);
-extern asmlinkage void invalidate_interrupt22(void);
-extern asmlinkage void invalidate_interrupt23(void);
-extern asmlinkage void invalidate_interrupt24(void);
-extern asmlinkage void invalidate_interrupt25(void);
-extern asmlinkage void invalidate_interrupt26(void);
-extern asmlinkage void invalidate_interrupt27(void);
-extern asmlinkage void invalidate_interrupt28(void);
-extern asmlinkage void invalidate_interrupt29(void);
-extern asmlinkage void invalidate_interrupt30(void);
-extern asmlinkage void invalidate_interrupt31(void);
-
 extern asmlinkage void irq_move_cleanup_interrupt(void);
 extern asmlinkage void reboot_interrupt(void);
 extern asmlinkage void threshold_interrupt(void);
+extern asmlinkage void deferred_error_interrupt(void);
 
 extern asmlinkage void call_function_interrupt(void);
 extern asmlinkage void call_function_single_interrupt(void);
@@ -87,60 +55,93 @@ extern void trace_spurious_interrupt(void);
 extern void trace_thermal_interrupt(void);
 extern void trace_reschedule_interrupt(void);
 extern void trace_threshold_interrupt(void);
+extern void trace_deferred_error_interrupt(void);
 extern void trace_call_function_interrupt(void);
 extern void trace_call_function_single_interrupt(void);
 #define trace_irq_move_cleanup_interrupt  irq_move_cleanup_interrupt
 #define trace_reboot_interrupt  reboot_interrupt
 #define trace_kvm_posted_intr_ipi kvm_posted_intr_ipi
+#define trace_kvm_posted_intr_wakeup_ipi kvm_posted_intr_wakeup_ipi
 #endif /* CONFIG_TRACING */
 
-#ifdef CONFIG_IRQ_REMAP
-/* Intel specific interrupt remapping information */
-struct irq_2_iommu {
-       struct intel_iommu *iommu;
-       u16 irte_index;
-       u16 sub_handle;
-       u8  irte_mask;
-};
-
-/* AMD specific interrupt remapping information */
-struct irq_2_irte {
-       u16 devid; /* Device ID for IRTE table */
-       u16 index; /* Index into IRTE table*/
-};
-#endif /* CONFIG_IRQ_REMAP */
-
 #ifdef CONFIG_X86_LOCAL_APIC
 struct irq_data;
+struct pci_dev;
+struct msi_desc;
+
+enum irq_alloc_type {
+       X86_IRQ_ALLOC_TYPE_IOAPIC = 1,
+       X86_IRQ_ALLOC_TYPE_HPET,
+       X86_IRQ_ALLOC_TYPE_MSI,
+       X86_IRQ_ALLOC_TYPE_MSIX,
+       X86_IRQ_ALLOC_TYPE_DMAR,
+       X86_IRQ_ALLOC_TYPE_UV,
+};
 
-struct irq_cfg {
-       cpumask_var_t           domain;
-       cpumask_var_t           old_domain;
-       u8                      vector;
-       u8                      move_in_progress : 1;
-#ifdef CONFIG_IRQ_REMAP
-       u8                      remapped : 1;
+struct irq_alloc_info {
+       enum irq_alloc_type     type;
+       u32                     flags;
+       const struct cpumask    *mask;  /* CPU mask for vector allocation */
        union {
-               struct irq_2_iommu irq_2_iommu;
-               struct irq_2_irte  irq_2_irte;
-       };
+               int             unused;
+#ifdef CONFIG_HPET_TIMER
+               struct {
+                       int             hpet_id;
+                       int             hpet_index;
+                       void            *hpet_data;
+               };
 #endif
-       union {
-#ifdef CONFIG_X86_IO_APIC
+#ifdef CONFIG_PCI_MSI
                struct {
-                       struct list_head        irq_2_pin;
+                       struct pci_dev  *msi_dev;
+                       irq_hw_number_t msi_hwirq;
+               };
+#endif
+#ifdef CONFIG_X86_IO_APIC
+               struct {
+                       int             ioapic_id;
+                       int             ioapic_pin;
+                       int             ioapic_node;
+                       u32             ioapic_trigger : 1;
+                       u32             ioapic_polarity : 1;
+                       u32             ioapic_valid : 1;
+                       struct IO_APIC_route_entry *ioapic_entry;
+               };
+#endif
+#ifdef CONFIG_DMAR_TABLE
+               struct {
+                       int             dmar_id;
+                       void            *dmar_data;
+               };
+#endif
+#ifdef CONFIG_HT_IRQ
+               struct {
+                       int             ht_pos;
+                       int             ht_idx;
+                       struct pci_dev  *ht_dev;
+                       void            *ht_update;
+               };
+#endif
+#ifdef CONFIG_X86_UV
+               struct {
+                       int             uv_limit;
+                       int             uv_blade;
+                       unsigned long   uv_offset;
+                       char            *uv_name;
                };
 #endif
        };
 };
 
+struct irq_cfg {
+       unsigned int            dest_apicid;
+       u8                      vector;
+};
+
 extern struct irq_cfg *irq_cfg(unsigned int irq);
 extern struct irq_cfg *irqd_cfg(struct irq_data *irq_data);
-extern struct irq_cfg *alloc_irq_and_cfg_at(unsigned int at, int node);
 extern void lock_vector_lock(void);
 extern void unlock_vector_lock(void);
-extern int assign_irq_vector(int, struct irq_cfg *, const struct cpumask *);
-extern void clear_irq_vector(int irq, struct irq_cfg *cfg);
 extern void setup_vector_irq(int cpu);
 #ifdef CONFIG_SMP
 extern void send_cleanup_vector(struct irq_cfg *);
@@ -150,10 +151,7 @@ static inline void send_cleanup_vector(struct irq_cfg *c) { }
 static inline void irq_complete_move(struct irq_cfg *c) { }
 #endif
 
-extern int apic_retrigger_irq(struct irq_data *data);
 extern void apic_ack_edge(struct irq_data *data);
-extern int apic_set_affinity(struct irq_data *data, const struct cpumask *mask,
-                            unsigned int *dest_id);
 #else  /*  CONFIG_X86_LOCAL_APIC */
 static inline void lock_vector_lock(void) {}
 static inline void unlock_vector_lock(void) {}
@@ -163,8 +161,7 @@ static inline void unlock_vector_lock(void) {}
 extern atomic_t irq_err_count;
 extern atomic_t irq_mis_count;
 
-/* EISA */
-extern void eisa_set_level_irq(unsigned int irq);
+extern void elcr_set_level_irq(unsigned int irq);
 
 /* SMP */
 extern __visible void smp_apic_timer_interrupt(struct pt_regs *);
@@ -178,7 +175,6 @@ extern asmlinkage void smp_irq_move_cleanup_interrupt(void);
 extern __visible void smp_reschedule_interrupt(struct pt_regs *);
 extern __visible void smp_call_function_interrupt(struct pt_regs *);
 extern __visible void smp_call_function_single_interrupt(struct pt_regs *);
-extern __visible void smp_invalidate_interrupt(struct pt_regs *);
 #endif
 
 extern char irq_entries_start[];
diff --git a/arch/x86/include/asm/i387.h b/arch/x86/include/asm/i387.h
deleted file mode 100644 (file)
index 6eb6fcb..0000000
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * Copyright (C) 1994 Linus Torvalds
- *
- * Pentium III FXSR, SSE support
- * General FPU state handling cleanups
- *     Gareth Hughes <gareth@valinux.com>, May 2000
- * x86-64 work by Andi Kleen 2002
- */
-
-#ifndef _ASM_X86_I387_H
-#define _ASM_X86_I387_H
-
-#ifndef __ASSEMBLY__
-
-#include <linux/sched.h>
-#include <linux/hardirq.h>
-
-struct pt_regs;
-struct user_i387_struct;
-
-extern int init_fpu(struct task_struct *child);
-extern void fpu_finit(struct fpu *fpu);
-extern int dump_fpu(struct pt_regs *, struct user_i387_struct *);
-extern void math_state_restore(void);
-
-extern bool irq_fpu_usable(void);
-
-/*
- * Careful: __kernel_fpu_begin/end() must be called with preempt disabled
- * and they don't touch the preempt state on their own.
- * If you enable preemption after __kernel_fpu_begin(), preempt notifier
- * should call the __kernel_fpu_end() to prevent the kernel/user FPU
- * state from getting corrupted. KVM for example uses this model.
- *
- * All other cases use kernel_fpu_begin/end() which disable preemption
- * during kernel FPU usage.
- */
-extern void __kernel_fpu_begin(void);
-extern void __kernel_fpu_end(void);
-
-static inline void kernel_fpu_begin(void)
-{
-       preempt_disable();
-       WARN_ON_ONCE(!irq_fpu_usable());
-       __kernel_fpu_begin();
-}
-
-static inline void kernel_fpu_end(void)
-{
-       __kernel_fpu_end();
-       preempt_enable();
-}
-
-/* Must be called with preempt disabled */
-extern void kernel_fpu_disable(void);
-extern void kernel_fpu_enable(void);
-
-/*
- * Some instructions like VIA's padlock instructions generate a spurious
- * DNA fault but don't modify SSE registers. And these instructions
- * get used from interrupt context as well. To prevent these kernel instructions
- * in interrupt context interacting wrongly with other user/kernel fpu usage, we
- * should use them only in the context of irq_ts_save/restore()
- */
-static inline int irq_ts_save(void)
-{
-       /*
-        * If in process context and not atomic, we can take a spurious DNA fault.
-        * Otherwise, doing clts() in process context requires disabling preemption
-        * or some heavy lifting like kernel_fpu_begin()
-        */
-       if (!in_atomic())
-               return 0;
-
-       if (read_cr0() & X86_CR0_TS) {
-               clts();
-               return 1;
-       }
-
-       return 0;
-}
-
-static inline void irq_ts_restore(int TS_state)
-{
-       if (TS_state)
-               stts();
-}
-
-/*
- * The question "does this thread have fpu access?"
- * is slightly racy, since preemption could come in
- * and revoke it immediately after the test.
- *
- * However, even in that very unlikely scenario,
- * we can just assume we have FPU access - typically
- * to save the FP state - we'll just take a #NM
- * fault and get the FPU access back.
- */
-static inline int user_has_fpu(void)
-{
-       return current->thread.fpu.has_fpu;
-}
-
-extern void unlazy_fpu(struct task_struct *tsk);
-
-#endif /* __ASSEMBLY__ */
-
-#endif /* _ASM_X86_I387_H */
index 34a5b93704d3ecb1d98190f6a12437a9acab7204..83ec9b1d77cc17eecb4281118290bbb278ecc273 100644 (file)
   */
 
 #define ARCH_HAS_IOREMAP_WC
+#define ARCH_HAS_IOREMAP_WT
 
 #include <linux/string.h>
 #include <linux/compiler.h>
 #include <asm/page.h>
 #include <asm/early_ioremap.h>
+#include <asm/pgtable_types.h>
 
 #define build_mmio_read(name, size, type, reg, barrier) \
 static inline type name(const volatile void __iomem *addr) \
@@ -177,6 +179,7 @@ static inline unsigned int isa_virt_to_bus(volatile void *address)
  * look at pci_iomap().
  */
 extern void __iomem *ioremap_nocache(resource_size_t offset, unsigned long size);
+extern void __iomem *ioremap_uc(resource_size_t offset, unsigned long size);
 extern void __iomem *ioremap_cache(resource_size_t offset, unsigned long size);
 extern void __iomem *ioremap_prot(resource_size_t offset, unsigned long size,
                                unsigned long prot_val);
@@ -197,8 +200,6 @@ extern void set_iounmap_nonlazy(void);
 
 #include <asm-generic/iomap.h>
 
-#include <linux/vmalloc.h>
-
 /*
  * Convert a virtual cached pointer to an uncached pointer
  */
@@ -320,6 +321,7 @@ extern void unxlate_dev_mem_ptr(phys_addr_t phys, void *addr);
 extern int ioremap_change_attr(unsigned long vaddr, unsigned long size,
                                enum page_cache_mode pcm);
 extern void __iomem *ioremap_wc(resource_size_t offset, unsigned long size);
+extern void __iomem *ioremap_wt(resource_size_t offset, unsigned long size);
 
 extern bool is_early_ioremap_ptep(pte_t *ptep);
 
@@ -338,6 +340,9 @@ extern bool xen_biovec_phys_mergeable(const struct bio_vec *vec1,
 #define IO_SPACE_LIMIT 0xffff
 
 #ifdef CONFIG_MTRR
+extern int __must_check arch_phys_wc_index(int handle);
+#define arch_phys_wc_index arch_phys_wc_index
+
 extern int __must_check arch_phys_wc_add(unsigned long base,
                                         unsigned long size);
 extern void arch_phys_wc_del(int handle);
index 2f91685fe1cdb51d937eb20d29c46952d54f298f..6cbf2cfb3f8a02481d1c5c63b99eafb0c29eba35 100644 (file)
@@ -95,9 +95,22 @@ struct IR_IO_APIC_route_entry {
                index           : 15;
 } __attribute__ ((packed));
 
-#define IOAPIC_AUTO     -1
-#define IOAPIC_EDGE     0
-#define IOAPIC_LEVEL    1
+struct irq_alloc_info;
+struct ioapic_domain_cfg;
+
+#define IOAPIC_AUTO                    -1
+#define IOAPIC_EDGE                    0
+#define IOAPIC_LEVEL                   1
+
+#define IOAPIC_MASKED                  1
+#define IOAPIC_UNMASKED                        0
+
+#define IOAPIC_POL_HIGH                        0
+#define IOAPIC_POL_LOW                 1
+
+#define IOAPIC_DEST_MODE_PHYSICAL      0
+#define IOAPIC_DEST_MODE_LOGICAL       1
+
 #define        IOAPIC_MAP_ALLOC                0x1
 #define        IOAPIC_MAP_CHECK                0x2
 
@@ -110,9 +123,6 @@ extern int nr_ioapics;
 
 extern int mpc_ioapic_id(int ioapic);
 extern unsigned int mpc_ioapic_addr(int ioapic);
-extern struct mp_ioapic_gsi *mp_ioapic_gsi_routing(int ioapic);
-
-#define MP_MAX_IOAPIC_PIN 127
 
 /* # of MP IRQ source entries */
 extern int mp_irq_entries;
@@ -120,9 +130,6 @@ extern int mp_irq_entries;
 /* MP IRQ source entries */
 extern struct mpc_intsrc mp_irqs[MAX_IRQ_SOURCES];
 
-/* Older SiS APIC requires we rewrite the index register */
-extern int sis_apic_bug;
-
 /* 1 if "noapic" boot option passed */
 extern int skip_ioapic_setup;
 
@@ -132,6 +139,8 @@ extern int noioapicquirk;
 /* -1 if "noapic" boot option passed */
 extern int noioapicreroute;
 
+extern u32 gsi_top;
+
 extern unsigned long io_apic_irqs;
 
 #define IO_APIC_IRQ(x) (((x) >= NR_IRQS_LEGACY) || ((1 << (x)) & io_apic_irqs))
@@ -147,13 +156,6 @@ struct irq_cfg;
 extern void ioapic_insert_resources(void);
 extern int arch_early_ioapic_init(void);
 
-extern int native_setup_ioapic_entry(int, struct IO_APIC_route_entry *,
-                                    unsigned int, int,
-                                    struct io_apic_irq_attr *);
-extern void eoi_ioapic_irq(unsigned int irq, struct irq_cfg *cfg);
-
-extern void native_eoi_ioapic_pin(int apic, int pin, int vector);
-
 extern int save_ioapic_entries(void);
 extern void mask_ioapic_entries(void);
 extern int restore_ioapic_entries(void);
@@ -161,82 +163,32 @@ extern int restore_ioapic_entries(void);
 extern void setup_ioapic_ids_from_mpc(void);
 extern void setup_ioapic_ids_from_mpc_nocheck(void);
 
-struct io_apic_irq_attr {
-       int ioapic;
-       int ioapic_pin;
-       int trigger;
-       int polarity;
-};
-
-enum ioapic_domain_type {
-       IOAPIC_DOMAIN_INVALID,
-       IOAPIC_DOMAIN_LEGACY,
-       IOAPIC_DOMAIN_STRICT,
-       IOAPIC_DOMAIN_DYNAMIC,
-};
-
-struct device_node;
-struct irq_domain;
-struct irq_domain_ops;
-
-struct ioapic_domain_cfg {
-       enum ioapic_domain_type         type;
-       const struct irq_domain_ops     *ops;
-       struct device_node              *dev;
-};
-
-struct mp_ioapic_gsi{
-       u32 gsi_base;
-       u32 gsi_end;
-};
-extern u32 gsi_top;
-
 extern int mp_find_ioapic(u32 gsi);
 extern int mp_find_ioapic_pin(int ioapic, u32 gsi);
-extern u32 mp_pin_to_gsi(int ioapic, int pin);
-extern int mp_map_gsi_to_irq(u32 gsi, unsigned int flags);
+extern int mp_map_gsi_to_irq(u32 gsi, unsigned int flags,
+                            struct irq_alloc_info *info);
 extern void mp_unmap_irq(int irq);
 extern int mp_register_ioapic(int id, u32 address, u32 gsi_base,
                              struct ioapic_domain_cfg *cfg);
 extern int mp_unregister_ioapic(u32 gsi_base);
 extern int mp_ioapic_registered(u32 gsi_base);
-extern int mp_irqdomain_map(struct irq_domain *domain, unsigned int virq,
-                           irq_hw_number_t hwirq);
-extern void mp_irqdomain_unmap(struct irq_domain *domain, unsigned int virq);
-extern int mp_set_gsi_attr(u32 gsi, int trigger, int polarity, int node);
-extern void __init pre_init_apic_IRQ0(void);
+
+extern void ioapic_set_alloc_attr(struct irq_alloc_info *info,
+                                 int node, int trigger, int polarity);
 
 extern void mp_save_irq(struct mpc_intsrc *m);
 
 extern void disable_ioapic_support(void);
 
-extern void __init native_io_apic_init_mappings(void);
+extern void __init io_apic_init_mappings(void);
 extern unsigned int native_io_apic_read(unsigned int apic, unsigned int reg);
-extern void native_io_apic_write(unsigned int apic, unsigned int reg, unsigned int val);
-extern void native_io_apic_modify(unsigned int apic, unsigned int reg, unsigned int val);
 extern void native_disable_io_apic(void);
-extern void native_io_apic_print_entries(unsigned int apic, unsigned int nr_entries);
-extern void intel_ir_io_apic_print_entries(unsigned int apic, unsigned int nr_entries);
-extern int native_ioapic_set_affinity(struct irq_data *,
-                                     const struct cpumask *,
-                                     bool);
 
 static inline unsigned int io_apic_read(unsigned int apic, unsigned int reg)
 {
        return x86_io_apic_ops.read(apic, reg);
 }
 
-static inline void io_apic_write(unsigned int apic, unsigned int reg, unsigned int value)
-{
-       x86_io_apic_ops.write(apic, reg, value);
-}
-static inline void io_apic_modify(unsigned int apic, unsigned int reg, unsigned int value)
-{
-       x86_io_apic_ops.modify(apic, reg, value);
-}
-
-extern void io_apic_eoi(unsigned int apic, unsigned int vector);
-
 extern void setup_IO_APIC(void);
 extern void enable_IO_APIC(void);
 extern void disable_IO_APIC(void);
@@ -253,8 +205,12 @@ static inline int arch_early_ioapic_init(void) { return 0; }
 static inline void print_IO_APICs(void) {}
 #define gsi_top (NR_IRQS_LEGACY)
 static inline int mp_find_ioapic(u32 gsi) { return 0; }
-static inline u32 mp_pin_to_gsi(int ioapic, int pin) { return UINT_MAX; }
-static inline int mp_map_gsi_to_irq(u32 gsi, unsigned int flags) { return gsi; }
+static inline int mp_map_gsi_to_irq(u32 gsi, unsigned int flags,
+                                   struct irq_alloc_info *info)
+{
+       return gsi;
+}
+
 static inline void mp_unmap_irq(int irq) { }
 
 static inline int save_ioapic_entries(void)
@@ -268,17 +224,11 @@ static inline int restore_ioapic_entries(void)
        return -ENOMEM;
 }
 
-static inline void mp_save_irq(struct mpc_intsrc *m) { };
+static inline void mp_save_irq(struct mpc_intsrc *m) { }
 static inline void disable_ioapic_support(void) { }
-#define native_io_apic_init_mappings   NULL
+static inline void io_apic_init_mappings(void) { }
 #define native_io_apic_read            NULL
-#define native_io_apic_write           NULL
-#define native_io_apic_modify          NULL
 #define native_disable_io_apic         NULL
-#define native_io_apic_print_entries   NULL
-#define native_ioapic_set_affinity     NULL
-#define native_setup_ioapic_entry      NULL
-#define native_eoi_ioapic_pin          NULL
 
 static inline void setup_IO_APIC(void) { }
 static inline void enable_IO_APIC(void) { }
index a80cbb88ea911e0ab855e804aaca7eac41ad0a85..8008d06581c7f4d3e6a5680b45b8b841ad078788 100644 (file)
@@ -30,6 +30,10 @@ extern void fixup_irqs(void);
 extern void irq_force_complete_move(int);
 #endif
 
+#ifdef CONFIG_HAVE_KVM
+extern void kvm_set_posted_intr_wakeup_handler(void (*handler)(void));
+#endif
+
 extern void (*x86_platform_ipi_callback)(void);
 extern void native_init_IRQ(void);
 extern bool handle_irq(unsigned irq, struct pt_regs *regs);
index 6224d316c405c444553877845385e2d7b151c161..046c7fb1ca4332ef19044f5a482c1d0a3e3fc632 100644 (file)
 #ifndef __X86_IRQ_REMAPPING_H
 #define __X86_IRQ_REMAPPING_H
 
+#include <asm/irqdomain.h>
+#include <asm/hw_irq.h>
 #include <asm/io_apic.h>
 
-struct IO_APIC_route_entry;
-struct io_apic_irq_attr;
-struct irq_chip;
 struct msi_msg;
-struct pci_dev;
-struct irq_cfg;
+struct irq_alloc_info;
+
+enum irq_remap_cap {
+       IRQ_POSTING_CAP = 0,
+};
 
 #ifdef CONFIG_IRQ_REMAP
 
+extern bool irq_remapping_cap(enum irq_remap_cap cap);
 extern void set_irq_remapping_broken(void);
 extern int irq_remapping_prepare(void);
 extern int irq_remapping_enable(void);
 extern void irq_remapping_disable(void);
 extern int irq_remapping_reenable(int);
 extern int irq_remap_enable_fault_handling(void);
-extern int setup_ioapic_remapped_entry(int irq,
-                                      struct IO_APIC_route_entry *entry,
-                                      unsigned int destination,
-                                      int vector,
-                                      struct io_apic_irq_attr *attr);
-extern void free_remapped_irq(int irq);
-extern void compose_remapped_msi_msg(struct pci_dev *pdev,
-                                    unsigned int irq, unsigned int dest,
-                                    struct msi_msg *msg, u8 hpet_id);
-extern int setup_hpet_msi_remapped(unsigned int irq, unsigned int id);
 extern void panic_if_irq_remap(const char *msg);
-extern bool setup_remapped_irq(int irq,
-                              struct irq_cfg *cfg,
-                              struct irq_chip *chip);
 
-void irq_remap_modify_chip_defaults(struct irq_chip *chip);
+extern struct irq_domain *
+irq_remapping_get_ir_irq_domain(struct irq_alloc_info *info);
+extern struct irq_domain *
+irq_remapping_get_irq_domain(struct irq_alloc_info *info);
+
+/* Create PCI MSI/MSIx irqdomain, use @parent as the parent irqdomain. */
+extern struct irq_domain *arch_create_msi_irq_domain(struct irq_domain *parent);
+
+/* Get parent irqdomain for interrupt remapping irqdomain */
+static inline struct irq_domain *arch_get_ir_parent_domain(void)
+{
+       return x86_vector_domain;
+}
+
+struct vcpu_data {
+       u64 pi_desc_addr;       /* Physical address of PI Descriptor */
+       u32 vector;             /* Guest vector of the interrupt */
+};
 
 #else  /* CONFIG_IRQ_REMAP */
 
+static inline bool irq_remapping_cap(enum irq_remap_cap cap) { return 0; }
 static inline void set_irq_remapping_broken(void) { }
 static inline int irq_remapping_prepare(void) { return -ENODEV; }
 static inline int irq_remapping_enable(void) { return -ENODEV; }
 static inline void irq_remapping_disable(void) { }
 static inline int irq_remapping_reenable(int eim) { return -ENODEV; }
 static inline int irq_remap_enable_fault_handling(void) { return -ENODEV; }
-static inline int setup_ioapic_remapped_entry(int irq,
-                                             struct IO_APIC_route_entry *entry,
-                                             unsigned int destination,
-                                             int vector,
-                                             struct io_apic_irq_attr *attr)
-{
-       return -ENODEV;
-}
-static inline void free_remapped_irq(int irq) { }
-static inline void compose_remapped_msi_msg(struct pci_dev *pdev,
-                                           unsigned int irq, unsigned int dest,
-                                           struct msi_msg *msg, u8 hpet_id)
-{
-}
-static inline int setup_hpet_msi_remapped(unsigned int irq, unsigned int id)
-{
-       return -ENODEV;
-}
 
 static inline void panic_if_irq_remap(const char *msg)
 {
 }
 
-static inline void irq_remap_modify_chip_defaults(struct irq_chip *chip)
+static inline struct irq_domain *
+irq_remapping_get_ir_irq_domain(struct irq_alloc_info *info)
 {
+       return NULL;
 }
 
-static inline bool setup_remapped_irq(int irq,
-                                     struct irq_cfg *cfg,
-                                     struct irq_chip *chip)
+static inline struct irq_domain *
+irq_remapping_get_irq_domain(struct irq_alloc_info *info)
 {
-       return false;
+       return NULL;
 }
-#endif /* CONFIG_IRQ_REMAP */
-
-#define dmar_alloc_hwirq()     irq_alloc_hwirq(-1)
-#define dmar_free_hwirq                irq_free_hwirq
 
+#endif /* CONFIG_IRQ_REMAP */
 #endif /* __X86_IRQ_REMAPPING_H */
index 666c89ec4bd7298c1114e7e220a3fee3ebe77bc8..4c2d2eb2060a0b4b74d71bae834e49e51e777cff 100644 (file)
 #define IRQ_MOVE_CLEANUP_VECTOR                FIRST_EXTERNAL_VECTOR
 
 #define IA32_SYSCALL_VECTOR            0x80
-#ifdef CONFIG_X86_32
-# define SYSCALL_VECTOR                        0x80
-#endif
 
 /*
  * Vectors 0x30-0x3f are used for ISA interrupts.
  *   round up to the next 16-vector boundary
  */
-#define IRQ0_VECTOR                    ((FIRST_EXTERNAL_VECTOR + 16) & ~15)
-
-#define IRQ1_VECTOR                    (IRQ0_VECTOR +  1)
-#define IRQ2_VECTOR                    (IRQ0_VECTOR +  2)
-#define IRQ3_VECTOR                    (IRQ0_VECTOR +  3)
-#define IRQ4_VECTOR                    (IRQ0_VECTOR +  4)
-#define IRQ5_VECTOR                    (IRQ0_VECTOR +  5)
-#define IRQ6_VECTOR                    (IRQ0_VECTOR +  6)
-#define IRQ7_VECTOR                    (IRQ0_VECTOR +  7)
-#define IRQ8_VECTOR                    (IRQ0_VECTOR +  8)
-#define IRQ9_VECTOR                    (IRQ0_VECTOR +  9)
-#define IRQ10_VECTOR                   (IRQ0_VECTOR + 10)
-#define IRQ11_VECTOR                   (IRQ0_VECTOR + 11)
-#define IRQ12_VECTOR                   (IRQ0_VECTOR + 12)
-#define IRQ13_VECTOR                   (IRQ0_VECTOR + 13)
-#define IRQ14_VECTOR                   (IRQ0_VECTOR + 14)
-#define IRQ15_VECTOR                   (IRQ0_VECTOR + 15)
+#define ISA_IRQ_VECTOR(irq)            (((FIRST_EXTERNAL_VECTOR + 16) & ~15) + irq)
 
 /*
  * Special IRQ vectors used by the SMP architecture, 0xf0-0xff
  */
 #define X86_PLATFORM_IPI_VECTOR                0xf7
 
-/* Vector for KVM to deliver posted interrupt IPI */
-#ifdef CONFIG_HAVE_KVM
-#define POSTED_INTR_VECTOR             0xf2
-#endif
-
+#define POSTED_INTR_WAKEUP_VECTOR      0xf1
 /*
  * IRQ work vector:
  */
 #define IRQ_WORK_VECTOR                        0xf6
 
 #define UV_BAU_MESSAGE                 0xf5
+#define DEFERRED_ERROR_VECTOR          0xf4
 
 /* Vector on which hypervisor callbacks will be delivered */
 #define HYPERVISOR_CALLBACK_VECTOR     0xf3
 
+/* Vector for KVM to deliver posted interrupt IPI */
+#ifdef CONFIG_HAVE_KVM
+#define POSTED_INTR_VECTOR             0xf2
+#endif
+
 /*
  * Local APIC timer IRQ vector is on a different priority level,
  * to work around the 'lost local interrupt if more than 2 IRQ
@@ -155,18 +138,22 @@ static inline int invalid_vm86_irq(int irq)
  * static arrays.
  */
 
-#define NR_IRQS_LEGACY                   16
+#define NR_IRQS_LEGACY                 16
 
-#define IO_APIC_VECTOR_LIMIT           ( 32 * MAX_IO_APICS )
+#define CPU_VECTOR_LIMIT               (64 * NR_CPUS)
+#define IO_APIC_VECTOR_LIMIT           (32 * MAX_IO_APICS)
 
-#ifdef CONFIG_X86_IO_APIC
-# define CPU_VECTOR_LIMIT              (64 * NR_CPUS)
-# define NR_IRQS                                       \
+#if defined(CONFIG_X86_IO_APIC) && defined(CONFIG_PCI_MSI)
+#define NR_IRQS                                                \
        (CPU_VECTOR_LIMIT > IO_APIC_VECTOR_LIMIT ?      \
                (NR_VECTORS + CPU_VECTOR_LIMIT)  :      \
                (NR_VECTORS + IO_APIC_VECTOR_LIMIT))
-#else /* !CONFIG_X86_IO_APIC: */
-# define NR_IRQS                       NR_IRQS_LEGACY
+#elif defined(CONFIG_X86_IO_APIC)
+#define        NR_IRQS                         (NR_VECTORS + IO_APIC_VECTOR_LIMIT)
+#elif defined(CONFIG_PCI_MSI)
+#define NR_IRQS                                (NR_VECTORS + CPU_VECTOR_LIMIT)
+#else
+#define NR_IRQS                                NR_IRQS_LEGACY
 #endif
 
 #endif /* _ASM_X86_IRQ_VECTORS_H */
diff --git a/arch/x86/include/asm/irqdomain.h b/arch/x86/include/asm/irqdomain.h
new file mode 100644 (file)
index 0000000..d26075b
--- /dev/null
@@ -0,0 +1,63 @@
+#ifndef _ASM_IRQDOMAIN_H
+#define _ASM_IRQDOMAIN_H
+
+#include <linux/irqdomain.h>
+#include <asm/hw_irq.h>
+
+#ifdef CONFIG_X86_LOCAL_APIC
+enum {
+       /* Allocate contiguous CPU vectors */
+       X86_IRQ_ALLOC_CONTIGUOUS_VECTORS                = 0x1,
+};
+
+extern struct irq_domain *x86_vector_domain;
+
+extern void init_irq_alloc_info(struct irq_alloc_info *info,
+                               const struct cpumask *mask);
+extern void copy_irq_alloc_info(struct irq_alloc_info *dst,
+                               struct irq_alloc_info *src);
+#endif /* CONFIG_X86_LOCAL_APIC */
+
+#ifdef CONFIG_X86_IO_APIC
+struct device_node;
+struct irq_data;
+
+enum ioapic_domain_type {
+       IOAPIC_DOMAIN_INVALID,
+       IOAPIC_DOMAIN_LEGACY,
+       IOAPIC_DOMAIN_STRICT,
+       IOAPIC_DOMAIN_DYNAMIC,
+};
+
+struct ioapic_domain_cfg {
+       enum ioapic_domain_type         type;
+       const struct irq_domain_ops     *ops;
+       struct device_node              *dev;
+};
+
+extern const struct irq_domain_ops mp_ioapic_irqdomain_ops;
+
+extern int mp_irqdomain_alloc(struct irq_domain *domain, unsigned int virq,
+                             unsigned int nr_irqs, void *arg);
+extern void mp_irqdomain_free(struct irq_domain *domain, unsigned int virq,
+                             unsigned int nr_irqs);
+extern void mp_irqdomain_activate(struct irq_domain *domain,
+                                 struct irq_data *irq_data);
+extern void mp_irqdomain_deactivate(struct irq_domain *domain,
+                                   struct irq_data *irq_data);
+extern int mp_irqdomain_ioapic_idx(struct irq_domain *domain);
+#endif /* CONFIG_X86_IO_APIC */
+
+#ifdef CONFIG_PCI_MSI
+extern void arch_init_msi_domain(struct irq_domain *domain);
+#else
+static inline void arch_init_msi_domain(struct irq_domain *domain) { }
+#endif
+
+#ifdef CONFIG_HT_IRQ
+extern void arch_init_htirq_domain(struct irq_domain *domain);
+#else
+static inline void arch_init_htirq_domain(struct irq_domain *domain) { }
+#endif
+
+#endif
index 57a9d94fe160342036307f75e2f1f7b767b3cbf6..e16466ec473cbffe77f0d1d49ae50f509506938e 100644 (file)
@@ -193,6 +193,8 @@ struct x86_emulate_ops {
        int (*cpl)(struct x86_emulate_ctxt *ctxt);
        int (*get_dr)(struct x86_emulate_ctxt *ctxt, int dr, ulong *dest);
        int (*set_dr)(struct x86_emulate_ctxt *ctxt, int dr, ulong value);
+       u64 (*get_smbase)(struct x86_emulate_ctxt *ctxt);
+       void (*set_smbase)(struct x86_emulate_ctxt *ctxt, u64 smbase);
        int (*set_msr)(struct x86_emulate_ctxt *ctxt, u32 msr_index, u64 data);
        int (*get_msr)(struct x86_emulate_ctxt *ctxt, u32 msr_index, u64 *pdata);
        int (*check_pmc)(struct x86_emulate_ctxt *ctxt, u32 pmc);
@@ -262,6 +264,11 @@ enum x86emul_mode {
        X86EMUL_MODE_PROT64,    /* 64-bit (long) mode.    */
 };
 
+/* These match some of the HF_* flags defined in kvm_host.h  */
+#define X86EMUL_GUEST_MASK           (1 << 5) /* VCPU is in guest-mode */
+#define X86EMUL_SMM_MASK             (1 << 6)
+#define X86EMUL_SMM_INSIDE_NMI_MASK  (1 << 7)
+
 struct x86_emulate_ctxt {
        const struct x86_emulate_ops *ops;
 
@@ -273,8 +280,8 @@ struct x86_emulate_ctxt {
 
        /* interruptibility state, as a result of execution of STI or MOV SS */
        int interruptibility;
+       int emul_flags;
 
-       bool guest_mode; /* guest running a nested guest */
        bool perm_ok; /* do not check permissions if true */
        bool ud;        /* inject an #UD if host doesn't support insn */
 
index f4a555beef1908b78c9ad6992d4727d59d9c81ee..c7fa57b529d2972c6e968a05625f65a4fa3ca14b 100644 (file)
@@ -184,23 +184,12 @@ struct kvm_mmu_memory_cache {
        void *objects[KVM_NR_MEM_OBJS];
 };
 
-/*
- * kvm_mmu_page_role, below, is defined as:
- *
- *   bits 0:3 - total guest paging levels (2-4, or zero for real mode)
- *   bits 4:7 - page table level for this shadow (1-4)
- *   bits 8:9 - page table quadrant for 2-level guests
- *   bit   16 - direct mapping of virtual to physical mapping at gfn
- *              used for real mode and two-dimensional paging
- *   bits 17:19 - common access permissions for all ptes in this shadow page
- */
 union kvm_mmu_page_role {
        unsigned word;
        struct {
                unsigned level:4;
                unsigned cr4_pae:1;
                unsigned quadrant:2;
-               unsigned pad_for_nice_hex_output:6;
                unsigned direct:1;
                unsigned access:3;
                unsigned invalid:1;
@@ -208,6 +197,15 @@ union kvm_mmu_page_role {
                unsigned cr0_wp:1;
                unsigned smep_andnot_wp:1;
                unsigned smap_andnot_wp:1;
+               unsigned :8;
+
+               /*
+                * This is left at the top of the word so that
+                * kvm_memslots_for_spte_role can extract it with a
+                * simple shift.  While there is room, give it a whole
+                * byte so it is also faster to load it from memory.
+                */
+               unsigned smm:8;
        };
 };
 
@@ -338,12 +336,28 @@ struct kvm_pmu {
        u64 reprogram_pmi;
 };
 
+struct kvm_pmu_ops;
+
 enum {
        KVM_DEBUGREG_BP_ENABLED = 1,
        KVM_DEBUGREG_WONT_EXIT = 2,
        KVM_DEBUGREG_RELOAD = 4,
 };
 
+struct kvm_mtrr_range {
+       u64 base;
+       u64 mask;
+       struct list_head node;
+};
+
+struct kvm_mtrr {
+       struct kvm_mtrr_range var_ranges[KVM_NR_VAR_MTRR];
+       mtrr_type fixed_ranges[KVM_NR_FIXED_MTRR_REGION];
+       u64 deftype;
+
+       struct list_head head;
+};
+
 struct kvm_vcpu_arch {
        /*
         * rip and regs accesses must go through
@@ -368,6 +382,7 @@ struct kvm_vcpu_arch {
        int32_t apic_arb_prio;
        int mp_state;
        u64 ia32_misc_enable_msr;
+       u64 smbase;
        bool tpr_access_reporting;
        u64 ia32_xss;
 
@@ -471,8 +486,9 @@ struct kvm_vcpu_arch {
        atomic_t nmi_queued;  /* unprocessed asynchronous NMIs */
        unsigned nmi_pending; /* NMI queued after currently running handler */
        bool nmi_injected;    /* Trying to inject an NMI this entry */
+       bool smi_pending;    /* SMI queued after currently running handler */
 
-       struct mtrr_state_type mtrr_state;
+       struct kvm_mtrr mtrr_state;
        u64 pat;
 
        unsigned switch_db_regs;
@@ -637,6 +653,8 @@ struct kvm_arch {
        #endif
 
        bool boot_vcpu_runs_old_kvmclock;
+
+       u64 disabled_quirks;
 };
 
 struct kvm_vm_stat {
@@ -689,12 +707,13 @@ struct msr_data {
 
 struct kvm_lapic_irq {
        u32 vector;
-       u32 delivery_mode;
-       u32 dest_mode;
-       u32 level;
-       u32 trig_mode;
+       u16 delivery_mode;
+       u16 dest_mode;
+       bool level;
+       u16 trig_mode;
        u32 shorthand;
        u32 dest_id;
+       bool msi_redir_hint;
 };
 
 struct kvm_x86_ops {
@@ -706,19 +725,20 @@ struct kvm_x86_ops {
        int (*hardware_setup)(void);               /* __init */
        void (*hardware_unsetup)(void);            /* __exit */
        bool (*cpu_has_accelerated_tpr)(void);
+       bool (*cpu_has_high_real_mode_segbase)(void);
        void (*cpuid_update)(struct kvm_vcpu *vcpu);
 
        /* Create, but do not attach this VCPU */
        struct kvm_vcpu *(*vcpu_create)(struct kvm *kvm, unsigned id);
        void (*vcpu_free)(struct kvm_vcpu *vcpu);
-       void (*vcpu_reset)(struct kvm_vcpu *vcpu);
+       void (*vcpu_reset)(struct kvm_vcpu *vcpu, bool init_event);
 
        void (*prepare_guest_switch)(struct kvm_vcpu *vcpu);
        void (*vcpu_load)(struct kvm_vcpu *vcpu, int cpu);
        void (*vcpu_put)(struct kvm_vcpu *vcpu);
 
        void (*update_db_bp_intercept)(struct kvm_vcpu *vcpu);
-       int (*get_msr)(struct kvm_vcpu *vcpu, u32 msr_index, u64 *pdata);
+       int (*get_msr)(struct kvm_vcpu *vcpu, struct msr_data *msr);
        int (*set_msr)(struct kvm_vcpu *vcpu, struct msr_data *msr);
        u64 (*get_segment_base)(struct kvm_vcpu *vcpu, int seg);
        void (*get_segment)(struct kvm_vcpu *vcpu,
@@ -836,6 +856,8 @@ struct kvm_x86_ops {
        void (*enable_log_dirty_pt_masked)(struct kvm *kvm,
                                           struct kvm_memory_slot *slot,
                                           gfn_t offset, unsigned long mask);
+       /* pmu operations of sub-arch */
+       const struct kvm_pmu_ops *pmu_ops;
 };
 
 struct kvm_arch_async_pf {
@@ -871,7 +893,7 @@ void kvm_mmu_reset_context(struct kvm_vcpu *vcpu);
 void kvm_mmu_slot_remove_write_access(struct kvm *kvm,
                                      struct kvm_memory_slot *memslot);
 void kvm_mmu_zap_collapsible_sptes(struct kvm *kvm,
-                                       struct kvm_memory_slot *memslot);
+                                  const struct kvm_memory_slot *memslot);
 void kvm_mmu_slot_leaf_clear_dirty(struct kvm *kvm,
                                   struct kvm_memory_slot *memslot);
 void kvm_mmu_slot_largepage_remove_write_access(struct kvm *kvm,
@@ -882,7 +904,7 @@ void kvm_mmu_clear_dirty_pt_masked(struct kvm *kvm,
                                   struct kvm_memory_slot *slot,
                                   gfn_t gfn_offset, unsigned long mask);
 void kvm_mmu_zap_all(struct kvm *kvm);
-void kvm_mmu_invalidate_mmio_sptes(struct kvm *kvm);
+void kvm_mmu_invalidate_mmio_sptes(struct kvm *kvm, struct kvm_memslots *slots);
 unsigned int kvm_mmu_calculate_mmu_pages(struct kvm *kvm);
 void kvm_mmu_change_mmu_pages(struct kvm *kvm, unsigned int kvm_nr_mmu_pages);
 
@@ -890,7 +912,6 @@ int load_pdptrs(struct kvm_vcpu *vcpu, struct kvm_mmu *mmu, unsigned long cr3);
 
 int emulator_write_phys(struct kvm_vcpu *vcpu, gpa_t gpa,
                          const void *val, int bytes);
-u8 kvm_get_guest_memory_type(struct kvm_vcpu *vcpu, gfn_t gfn);
 
 struct kvm_irq_mask_notifier {
        void (*func)(struct kvm_irq_mask_notifier *kimn, bool masked);
@@ -938,7 +959,7 @@ static inline int emulate_instruction(struct kvm_vcpu *vcpu,
 
 void kvm_enable_efer_bits(u64);
 bool kvm_valid_efer(struct kvm_vcpu *vcpu, u64 efer);
-int kvm_get_msr(struct kvm_vcpu *vcpu, u32 msr_index, u64 *data);
+int kvm_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr);
 int kvm_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr);
 
 struct x86_emulate_ctxt;
@@ -967,7 +988,7 @@ void kvm_lmsw(struct kvm_vcpu *vcpu, unsigned long msw);
 void kvm_get_cs_db_l_bits(struct kvm_vcpu *vcpu, int *db, int *l);
 int kvm_set_xcr(struct kvm_vcpu *vcpu, u32 index, u64 xcr);
 
-int kvm_get_msr_common(struct kvm_vcpu *vcpu, u32 msr, u64 *pdata);
+int kvm_get_msr_common(struct kvm_vcpu *vcpu, struct msr_data *msr);
 int kvm_set_msr_common(struct kvm_vcpu *vcpu, struct msr_data *msr);
 
 unsigned long kvm_get_rflags(struct kvm_vcpu *vcpu);
@@ -1002,8 +1023,6 @@ void kvm_pic_clear_all(struct kvm_pic *pic, int irq_source_id);
 
 void kvm_inject_nmi(struct kvm_vcpu *vcpu);
 
-int fx_init(struct kvm_vcpu *vcpu);
-
 void kvm_mmu_pte_write(struct kvm_vcpu *vcpu, gpa_t gpa,
                       const u8 *new, int bytes);
 int kvm_mmu_unprotect_page(struct kvm *kvm, gfn_t gfn);
@@ -1112,6 +1131,14 @@ enum {
 #define HF_NMI_MASK            (1 << 3)
 #define HF_IRET_MASK           (1 << 4)
 #define HF_GUEST_MASK          (1 << 5) /* VCPU is in guest-mode */
+#define HF_SMM_MASK            (1 << 6)
+#define HF_SMM_INSIDE_NMI_MASK (1 << 7)
+
+#define __KVM_VCPU_MULTIPLE_ADDRESS_SPACE
+#define KVM_ADDRESS_SPACE_NUM 2
+
+#define kvm_arch_vcpu_memslots_id(vcpu) ((vcpu)->arch.hflags & HF_SMM_MASK ? 1 : 0)
+#define kvm_memslots_for_spte_role(kvm, role) __kvm_memslots(kvm, (role).smm)
 
 /*
  * Hardware virtualization extension instructions may fault if a
@@ -1146,7 +1173,7 @@ int kvm_cpu_has_injectable_intr(struct kvm_vcpu *v);
 int kvm_cpu_has_interrupt(struct kvm_vcpu *vcpu);
 int kvm_arch_interrupt_allowed(struct kvm_vcpu *vcpu);
 int kvm_cpu_get_interrupt(struct kvm_vcpu *v);
-void kvm_vcpu_reset(struct kvm_vcpu *vcpu);
+void kvm_vcpu_reset(struct kvm_vcpu *vcpu, bool init_event);
 void kvm_vcpu_reload_apic_access_page(struct kvm_vcpu *vcpu);
 void kvm_arch_mmu_notifier_invalidate_page(struct kvm *kvm,
                                           unsigned long address);
@@ -1170,16 +1197,9 @@ void kvm_complete_insn_gp(struct kvm_vcpu *vcpu, int err);
 
 int kvm_is_in_guest(void);
 
-void kvm_pmu_init(struct kvm_vcpu *vcpu);
-void kvm_pmu_destroy(struct kvm_vcpu *vcpu);
-void kvm_pmu_reset(struct kvm_vcpu *vcpu);
-void kvm_pmu_cpuid_update(struct kvm_vcpu *vcpu);
-bool kvm_pmu_msr(struct kvm_vcpu *vcpu, u32 msr);
-int kvm_pmu_get_msr(struct kvm_vcpu *vcpu, u32 msr, u64 *data);
-int kvm_pmu_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info);
-int kvm_pmu_check_pmc(struct kvm_vcpu *vcpu, unsigned pmc);
-int kvm_pmu_read_pmc(struct kvm_vcpu *vcpu, unsigned pmc, u64 *data);
-void kvm_handle_pmu_event(struct kvm_vcpu *vcpu);
-void kvm_deliver_pmi(struct kvm_vcpu *vcpu);
+int __x86_set_memory_region(struct kvm *kvm,
+                           const struct kvm_userspace_memory_region *mem);
+int x86_set_memory_region(struct kvm *kvm,
+                         const struct kvm_userspace_memory_region *mem);
 
 #endif /* _ASM_X86_KVM_HOST_H */
index 2d29197bd2fbfb7da88b1b3f9c9c932a78e177a5..19c099afa8613ead6565917f5787deb3df12c9e7 100644 (file)
@@ -21,6 +21,7 @@
 #ifndef _ASM_X86_LIVEPATCH_H
 #define _ASM_X86_LIVEPATCH_H
 
+#include <asm/setup.h>
 #include <linux/module.h>
 #include <linux/ftrace.h>
 
index 1f5a86d518db379ea65c100158df0c60988bb810..982dfc3679ad3d9cd741d75f8799bc1b8b8c5ef7 100644 (file)
 #define MCG_EXT_CNT(c)         (((c) & MCG_EXT_CNT_MASK) >> MCG_EXT_CNT_SHIFT)
 #define MCG_SER_P              (1ULL<<24)   /* MCA recovery/new status bits */
 #define MCG_ELOG_P             (1ULL<<26)   /* Extended error log supported */
+#define MCG_LMCE_P             (1ULL<<27)   /* Local machine check supported */
 
 /* MCG_STATUS register defines */
 #define MCG_STATUS_RIPV  (1ULL<<0)   /* restart ip valid */
 #define MCG_STATUS_EIPV  (1ULL<<1)   /* ip points to correct instruction */
 #define MCG_STATUS_MCIP  (1ULL<<2)   /* machine check in progress */
+#define MCG_STATUS_LMCES (1ULL<<3)   /* LMCE signaled */
+
+/* MCG_EXT_CTL register defines */
+#define MCG_EXT_CTL_LMCE_EN (1ULL<<0) /* Enable LMCE */
 
 /* MCi_STATUS register defines */
 #define MCI_STATUS_VAL   (1ULL<<63)  /* valid error */
@@ -104,6 +109,7 @@ struct mce_log {
 struct mca_config {
        bool dont_log_ce;
        bool cmci_disabled;
+       bool lmce_disabled;
        bool ignore_ce;
        bool disabled;
        bool ser;
@@ -117,8 +123,19 @@ struct mca_config {
 };
 
 struct mce_vendor_flags {
-       __u64           overflow_recov  : 1, /* cpuid_ebx(80000007) */
-                       __reserved_0    : 63;
+                       /*
+                        * overflow recovery cpuid bit indicates that overflow
+                        * conditions are not fatal
+                        */
+       __u64           overflow_recov  : 1,
+
+                       /*
+                        * SUCCOR stands for S/W UnCorrectable error COntainment
+                        * and Recovery. It indicates support for data poisoning
+                        * in HW and deferred error interrupts.
+                        */
+                       succor          : 1,
+                       __reserved_0    : 62;
 };
 extern struct mce_vendor_flags mce_flags;
 
@@ -168,12 +185,16 @@ void cmci_clear(void);
 void cmci_reenable(void);
 void cmci_rediscover(void);
 void cmci_recheck(void);
+void lmce_clear(void);
+void lmce_enable(void);
 #else
 static inline void mce_intel_feature_init(struct cpuinfo_x86 *c) { }
 static inline void cmci_clear(void) {}
 static inline void cmci_reenable(void) {}
 static inline void cmci_rediscover(void) {}
 static inline void cmci_recheck(void) {}
+static inline void lmce_clear(void) {}
+static inline void lmce_enable(void) {}
 #endif
 
 #ifdef CONFIG_X86_MCE_AMD
@@ -223,6 +244,9 @@ void do_machine_check(struct pt_regs *, long);
 extern void (*mce_threshold_vector)(void);
 extern void (*threshold_cpu_callback)(unsigned long action, unsigned int cpu);
 
+/* Deferred error interrupt handler */
+extern void (*deferred_error_int_vector)(void);
+
 /*
  * Thermal handler
  */
index 2fb20d6f7e23b0ccace549901dacf89b51e9c381..9e6278c7140eac3cac2e5841002300c3b903e457 100644 (file)
@@ -1,6 +1,8 @@
 #ifndef _ASM_X86_MICROCODE_H
 #define _ASM_X86_MICROCODE_H
 
+#include <linux/earlycpio.h>
+
 #define native_rdmsr(msr, val1, val2)                  \
 do {                                                   \
        u64 __val = native_read_msr((msr));             \
@@ -152,6 +154,7 @@ extern void __init load_ucode_bsp(void);
 extern void load_ucode_ap(void);
 extern int __init save_microcode_in_initrd(void);
 void reload_early_microcode(void);
+extern bool get_builtin_firmware(struct cpio_data *cd, const char *name);
 #else
 static inline void __init load_ucode_bsp(void) {}
 static inline void load_ucode_ap(void) {}
@@ -160,6 +163,9 @@ static inline int __init save_microcode_in_initrd(void)
        return 0;
 }
 static inline void reload_early_microcode(void) {}
+static inline bool get_builtin_firmware(struct cpio_data *cd, const char *name)
+{
+       return false;
+}
 #endif
-
 #endif /* _ASM_X86_MICROCODE_H */
index af935397e053e4a504daed8c14cfc06686a08794..ac6d328977a67e4fd0ae5674e2d7e8a30539ff92 100644 (file)
@@ -65,12 +65,12 @@ extern enum ucode_state load_microcode_amd(int cpu, u8 family, const u8 *data, s
 extern u8 amd_ucode_patch[PATCH_MAX_SIZE];
 
 #ifdef CONFIG_MICROCODE_AMD_EARLY
-extern void __init load_ucode_amd_bsp(void);
+extern void __init load_ucode_amd_bsp(unsigned int family);
 extern void load_ucode_amd_ap(void);
 extern int __init save_microcode_in_initrd_amd(void);
 void reload_ucode_amd(void);
 #else
-static inline void __init load_ucode_amd_bsp(void) {}
+static inline void __init load_ucode_amd_bsp(unsigned int family) {}
 static inline void load_ucode_amd_ap(void) {}
 static inline int __init save_microcode_in_initrd_amd(void) { return -EINVAL; }
 void reload_ucode_amd(void) {}
index 2b9209c46ca939991abed04a1c5d4ef786b0c698..7991c606125d01b137a6f0e1ed16f1e8c1d86015 100644 (file)
@@ -51,20 +51,11 @@ struct extended_sigtable {
        (((struct microcode_intel *)mc)->hdr.datasize ? \
         ((struct microcode_intel *)mc)->hdr.datasize : DEFAULT_UCODE_DATASIZE)
 
-#define sigmatch(s1, s2, p1, p2) \
-       (((s1) == (s2)) && (((p1) & (p2)) || (((p1) == 0) && ((p2) == 0))))
-
 #define exttable_size(et) ((et)->count * EXT_SIGNATURE_SIZE + EXT_HEADER_SIZE)
 
-extern int get_matching_microcode(unsigned int csig, int cpf, int rev, void *mc);
+extern int has_newer_microcode(void *mc, unsigned int csig, int cpf, int rev);
 extern int microcode_sanity_check(void *mc, int print_err);
-extern int get_matching_sig(unsigned int csig, int cpf, int rev, void *mc);
-
-static inline int
-revision_is_newer(struct microcode_header_intel *mc_header, int rev)
-{
-       return (mc_header->rev <= rev) ? 0 : 1;
-}
+extern int find_matching_signature(void *mc, unsigned int csig, int cpf);
 
 #ifdef CONFIG_MICROCODE_INTEL_EARLY
 extern void __init load_ucode_intel_bsp(void);
index 883f6b933fa4b6501af7a050fc161eafdb8f8d91..5e8daee7c5c94be6fc48bc7f32e593fb46948557 100644 (file)
@@ -142,6 +142,19 @@ static inline void arch_exit_mmap(struct mm_struct *mm)
        paravirt_arch_exit_mmap(mm);
 }
 
+#ifdef CONFIG_X86_64
+static inline bool is_64bit_mm(struct mm_struct *mm)
+{
+       return  !config_enabled(CONFIG_IA32_EMULATION) ||
+               !(mm->context.ia32_compat == TIF_IA32);
+}
+#else
+static inline bool is_64bit_mm(struct mm_struct *mm)
+{
+       return false;
+}
+#endif
+
 static inline void arch_bprm_mm_init(struct mm_struct *mm,
                struct vm_area_struct *vma)
 {
index a952a13d59a71bb354699cacd918d5583984693c..7a35495275a9b7b1150bde2935757a212defde59 100644 (file)
 #define MPX_BNDCFG_ENABLE_FLAG 0x1
 #define MPX_BD_ENTRY_VALID_FLAG        0x1
 
-#ifdef CONFIG_X86_64
-
-/* upper 28 bits [47:20] of the virtual address in 64-bit used to
- * index into bounds directory (BD).
- */
-#define MPX_BD_ENTRY_OFFSET    28
-#define MPX_BD_ENTRY_SHIFT     3
-/* bits [19:3] of the virtual address in 64-bit used to index into
- * bounds table (BT).
+/*
+ * The upper 28 bits [47:20] of the virtual address in 64-bit
+ * are used to index into bounds directory (BD).
+ *
+ * The directory is 2G (2^31) in size, and with 8-byte entries
+ * it has 2^28 entries.
  */
-#define MPX_BT_ENTRY_OFFSET    17
-#define MPX_BT_ENTRY_SHIFT     5
-#define MPX_IGN_BITS           3
-#define MPX_BD_ENTRY_TAIL      3
+#define MPX_BD_SIZE_BYTES_64   (1UL<<31)
+#define MPX_BD_ENTRY_BYTES_64  8
+#define MPX_BD_NR_ENTRIES_64   (MPX_BD_SIZE_BYTES_64/MPX_BD_ENTRY_BYTES_64)
 
-#else
-
-#define MPX_BD_ENTRY_OFFSET    20
-#define MPX_BD_ENTRY_SHIFT     2
-#define MPX_BT_ENTRY_OFFSET    10
-#define MPX_BT_ENTRY_SHIFT     4
-#define MPX_IGN_BITS           2
-#define MPX_BD_ENTRY_TAIL      2
+/*
+ * The 32-bit directory is 4MB (2^22) in size, and with 4-byte
+ * entries it has 2^20 entries.
+ */
+#define MPX_BD_SIZE_BYTES_32   (1UL<<22)
+#define MPX_BD_ENTRY_BYTES_32  4
+#define MPX_BD_NR_ENTRIES_32   (MPX_BD_SIZE_BYTES_32/MPX_BD_ENTRY_BYTES_32)
 
-#endif
+/*
+ * A 64-bit table is 4MB total in size, and an entry is
+ * 4 64-bit pointers in size.
+ */
+#define MPX_BT_SIZE_BYTES_64   (1UL<<22)
+#define MPX_BT_ENTRY_BYTES_64  32
+#define MPX_BT_NR_ENTRIES_64   (MPX_BT_SIZE_BYTES_64/MPX_BT_ENTRY_BYTES_64)
 
-#define MPX_BD_SIZE_BYTES (1UL<<(MPX_BD_ENTRY_OFFSET+MPX_BD_ENTRY_SHIFT))
-#define MPX_BT_SIZE_BYTES (1UL<<(MPX_BT_ENTRY_OFFSET+MPX_BT_ENTRY_SHIFT))
+/*
+ * A 32-bit table is 16kB total in size, and an entry is
+ * 4 32-bit pointers in size.
+ */
+#define MPX_BT_SIZE_BYTES_32   (1UL<<14)
+#define MPX_BT_ENTRY_BYTES_32  16
+#define MPX_BT_NR_ENTRIES_32   (MPX_BT_SIZE_BYTES_32/MPX_BT_ENTRY_BYTES_32)
 
 #define MPX_BNDSTA_TAIL                2
 #define MPX_BNDCFG_TAIL                12
 #define MPX_BNDSTA_ADDR_MASK   (~((1UL<<MPX_BNDSTA_TAIL)-1))
-#define MPX_BNDCFG_ADDR_MASK   (~((1UL<<MPX_BNDCFG_TAIL)-1))
-#define MPX_BT_ADDR_MASK       (~((1UL<<MPX_BD_ENTRY_TAIL)-1))
-
 #define MPX_BNDCFG_ADDR_MASK   (~((1UL<<MPX_BNDCFG_TAIL)-1))
 #define MPX_BNDSTA_ERROR_CODE  0x3
 
-#define MPX_BD_ENTRY_MASK      ((1<<MPX_BD_ENTRY_OFFSET)-1)
-#define MPX_BT_ENTRY_MASK      ((1<<MPX_BT_ENTRY_OFFSET)-1)
-#define MPX_GET_BD_ENTRY_OFFSET(addr)  ((((addr)>>(MPX_BT_ENTRY_OFFSET+ \
-               MPX_IGN_BITS)) & MPX_BD_ENTRY_MASK) << MPX_BD_ENTRY_SHIFT)
-#define MPX_GET_BT_ENTRY_OFFSET(addr)  ((((addr)>>MPX_IGN_BITS) & \
-               MPX_BT_ENTRY_MASK) << MPX_BT_ENTRY_SHIFT)
-
 #ifdef CONFIG_X86_INTEL_MPX
-siginfo_t *mpx_generate_siginfo(struct pt_regs *regs,
-                               struct xsave_struct *xsave_buf);
-int mpx_handle_bd_fault(struct xsave_struct *xsave_buf);
+siginfo_t *mpx_generate_siginfo(struct pt_regs *regs);
+int mpx_handle_bd_fault(void);
 static inline int kernel_managing_mpx_tables(struct mm_struct *mm)
 {
        return (mm->bd_addr != MPX_INVALID_BOUNDS_DIR);
@@ -77,12 +72,11 @@ static inline void mpx_mm_init(struct mm_struct *mm)
 void mpx_notify_unmap(struct mm_struct *mm, struct vm_area_struct *vma,
                      unsigned long start, unsigned long end);
 #else
-static inline siginfo_t *mpx_generate_siginfo(struct pt_regs *regs,
-                                             struct xsave_struct *xsave_buf)
+static inline siginfo_t *mpx_generate_siginfo(struct pt_regs *regs)
 {
        return NULL;
 }
-static inline int mpx_handle_bd_fault(struct xsave_struct *xsave_buf)
+static inline int mpx_handle_bd_fault(void)
 {
        return -EINVAL;
 }
diff --git a/arch/x86/include/asm/msi.h b/arch/x86/include/asm/msi.h
new file mode 100644 (file)
index 0000000..93724cc
--- /dev/null
@@ -0,0 +1,7 @@
+#ifndef _ASM_X86_MSI_H
+#define _ASM_X86_MSI_H
+#include <asm/hw_irq.h>
+
+typedef struct irq_alloc_info msi_alloc_info_t;
+
+#endif /* _ASM_X86_MSI_H */
similarity index 99%
rename from arch/x86/include/uapi/asm/msr-index.h
rename to arch/x86/include/asm/msr-index.h
index 3c6bb342a48f1ad123ba82261c517c18266227b6..9ebc3d0093736bcc2e62646a89fbac1f75c878d7 100644 (file)
@@ -56,6 +56,7 @@
 #define MSR_IA32_MCG_CAP               0x00000179
 #define MSR_IA32_MCG_STATUS            0x0000017a
 #define MSR_IA32_MCG_CTL               0x0000017b
+#define MSR_IA32_MCG_EXT_CTL           0x000004d0
 
 #define MSR_OFFCORE_RSP_0              0x000001a6
 #define MSR_OFFCORE_RSP_1              0x000001a7
 #define FEATURE_CONTROL_LOCKED                         (1<<0)
 #define FEATURE_CONTROL_VMXON_ENABLED_INSIDE_SMX       (1<<1)
 #define FEATURE_CONTROL_VMXON_ENABLED_OUTSIDE_SMX      (1<<2)
+#define FEATURE_CONTROL_LMCE                           (1<<20)
 
 #define MSR_IA32_APICBASE              0x0000001b
 #define MSR_IA32_APICBASE_BSP          (1<<8)
index de36f22eb0b9e79db05711cd46196c0c072bb023..e6a707eb508167dea3eda93a440cee3ba15f39db 100644 (file)
@@ -1,13 +1,14 @@
 #ifndef _ASM_X86_MSR_H
 #define _ASM_X86_MSR_H
 
-#include <uapi/asm/msr.h>
+#include "msr-index.h"
 
 #ifndef __ASSEMBLY__
 
 #include <asm/asm.h>
 #include <asm/errno.h>
 #include <asm/cpumask.h>
+#include <uapi/asm/msr.h>
 
 struct msr {
        union {
@@ -205,8 +206,13 @@ do {                                                            \
 
 #endif /* !CONFIG_PARAVIRT */
 
-#define wrmsrl_safe(msr, val) wrmsr_safe((msr), (u32)(val),            \
-                                            (u32)((val) >> 32))
+/*
+ * 64-bit version of wrmsr_safe():
+ */
+static inline int wrmsrl_safe(u32 msr, u64 val)
+{
+       return wrmsr_safe(msr, (u32)val,  (u32)(val >> 32));
+}
 
 #define write_tsc(low, high) wrmsr(MSR_IA32_TSC, (low), (high))
 
index f768f62984194a13da806c0fab7691ec8b4167d2..b94f6f64e23d0cf7e630c190fe48518b47e819ed 100644 (file)
@@ -31,7 +31,7 @@
  * arch_phys_wc_add and arch_phys_wc_del.
  */
 # ifdef CONFIG_MTRR
-extern u8 mtrr_type_lookup(u64 addr, u64 end);
+extern u8 mtrr_type_lookup(u64 addr, u64 end, u8 *uniform);
 extern void mtrr_save_fixed_ranges(void *);
 extern void mtrr_save_state(void);
 extern int mtrr_add(unsigned long base, unsigned long size,
@@ -48,14 +48,13 @@ extern void mtrr_aps_init(void);
 extern void mtrr_bp_restore(void);
 extern int mtrr_trim_uncached_memory(unsigned long end_pfn);
 extern int amd_special_default_mtrr(void);
-extern int phys_wc_to_mtrr_index(int handle);
 #  else
-static inline u8 mtrr_type_lookup(u64 addr, u64 end)
+static inline u8 mtrr_type_lookup(u64 addr, u64 end, u8 *uniform)
 {
        /*
         * Return no-MTRRs:
         */
-       return 0xff;
+       return MTRR_TYPE_INVALID;
 }
 #define mtrr_save_fixed_ranges(arg) do {} while (0)
 #define mtrr_save_state() do {} while (0)
@@ -84,10 +83,6 @@ static inline int mtrr_trim_uncached_memory(unsigned long end_pfn)
 static inline void mtrr_centaur_report_mcr(int mcr, u32 lo, u32 hi)
 {
 }
-static inline int phys_wc_to_mtrr_index(int handle)
-{
-       return -1;
-}
 
 #define mtrr_ap_init() do {} while (0)
 #define mtrr_bp_init() do {} while (0)
@@ -127,4 +122,8 @@ struct mtrr_gentry32 {
                                 _IOW(MTRR_IOCTL_BASE,  9, struct mtrr_sentry32)
 #endif /* CONFIG_COMPAT */
 
+/* Bit fields for enabled in struct mtrr_state_type */
+#define MTRR_STATE_MTRR_FIXED_ENABLED  0x01
+#define MTRR_STATE_MTRR_ENABLED                0x02
+
 #endif /* _ASM_X86_MTRR_H */
index 8957810ad7d1e348dd6315242c3411a863745be6..d143bfad45d70f98e541c14f0e4f94a312d7b2ad 100644 (file)
@@ -712,6 +712,31 @@ static inline void __set_fixmap(unsigned /* enum fixed_addresses */ idx,
 
 #if defined(CONFIG_SMP) && defined(CONFIG_PARAVIRT_SPINLOCKS)
 
+#ifdef CONFIG_QUEUED_SPINLOCKS
+
+static __always_inline void pv_queued_spin_lock_slowpath(struct qspinlock *lock,
+                                                       u32 val)
+{
+       PVOP_VCALL2(pv_lock_ops.queued_spin_lock_slowpath, lock, val);
+}
+
+static __always_inline void pv_queued_spin_unlock(struct qspinlock *lock)
+{
+       PVOP_VCALLEE1(pv_lock_ops.queued_spin_unlock, lock);
+}
+
+static __always_inline void pv_wait(u8 *ptr, u8 val)
+{
+       PVOP_VCALL2(pv_lock_ops.wait, ptr, val);
+}
+
+static __always_inline void pv_kick(int cpu)
+{
+       PVOP_VCALL1(pv_lock_ops.kick, cpu);
+}
+
+#else /* !CONFIG_QUEUED_SPINLOCKS */
+
 static __always_inline void __ticket_lock_spinning(struct arch_spinlock *lock,
                                                        __ticket_t ticket)
 {
@@ -724,7 +749,9 @@ static __always_inline void __ticket_unlock_kick(struct arch_spinlock *lock,
        PVOP_VCALL2(pv_lock_ops.unlock_kick, lock, ticket);
 }
 
-#endif
+#endif /* CONFIG_QUEUED_SPINLOCKS */
+
+#endif /* SMP && PARAVIRT_SPINLOCKS */
 
 #ifdef CONFIG_X86_32
 #define PV_SAVE_REGS "pushl %ecx; pushl %edx;"
index f7b0b5c112f28cc89c524231e9022b4560158b48..a6b8f9fadb06853862d9e8f2fe9e51d7698ec647 100644 (file)
@@ -160,13 +160,14 @@ struct pv_cpu_ops {
        u64 (*read_pmc)(int counter);
        unsigned long long (*read_tscp)(unsigned int *aux);
 
+#ifdef CONFIG_X86_32
        /*
         * Atomically enable interrupts and return to userspace.  This
-        * is only ever used to return to 32-bit processes; in a
-        * 64-bit kernel, it's used for 32-on-64 compat processes, but
-        * never native 64-bit processes.  (Jump, not call.)
+        * is only used in 32-bit kernels.  64-bit kernels use
+        * usergs_sysret32 instead.
         */
        void (*irq_enable_sysexit)(void);
+#endif
 
        /*
         * Switch to usermode gs and return to 64-bit usermode using
@@ -333,9 +334,19 @@ struct arch_spinlock;
 typedef u16 __ticket_t;
 #endif
 
+struct qspinlock;
+
 struct pv_lock_ops {
+#ifdef CONFIG_QUEUED_SPINLOCKS
+       void (*queued_spin_lock_slowpath)(struct qspinlock *lock, u32 val);
+       struct paravirt_callee_save queued_spin_unlock;
+
+       void (*wait)(u8 *ptr, u8 val);
+       void (*kick)(int cpu);
+#else /* !CONFIG_QUEUED_SPINLOCKS */
        struct paravirt_callee_save lock_spinning;
        void (*unlock_kick)(struct arch_spinlock *lock, __ticket_t ticket);
+#endif /* !CONFIG_QUEUED_SPINLOCKS */
 };
 
 /* This contains all the paravirt structures: we get a convenient
index 91bc4ba95f919e90c3a398469f2dd52ca40e3cdd..ca6c228d5e62837be88984b652bb436949295d03 100644 (file)
@@ -4,14 +4,9 @@
 #include <linux/types.h>
 #include <asm/pgtable_types.h>
 
-#ifdef CONFIG_X86_PAT
-extern int pat_enabled;
-#else
-static const int pat_enabled;
-#endif
-
+bool pat_enabled(void);
 extern void pat_init(void);
-void pat_init_cache_modes(void);
+void pat_init_cache_modes(u64);
 
 extern int reserve_memtype(u64 start, u64 end,
                enum page_cache_mode req_pcm, enum page_cache_mode *ret_pcm);
index 4e370a5d81170e4fb4c6fa5d1abaf451c87cf502..b962e0fe565852d4c8214d6ed891664bce0ebb5a 100644 (file)
@@ -80,13 +80,6 @@ extern int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma,
 
 #ifdef CONFIG_PCI
 extern void early_quirks(void);
-static inline void pci_dma_burst_advice(struct pci_dev *pdev,
-                                       enum pci_dma_burst_strategy *strat,
-                                       unsigned long *strategy_parameter)
-{
-       *strat = PCI_DMA_BURST_INFINITY;
-       *strategy_parameter = ~0UL;
-}
 #else
 static inline void early_quirks(void) { }
 #endif
@@ -96,15 +89,10 @@ extern void pci_iommu_alloc(void);
 #ifdef CONFIG_PCI_MSI
 /* implemented in arch/x86/kernel/apic/io_apic. */
 struct msi_desc;
-void native_compose_msi_msg(struct pci_dev *pdev, unsigned int irq,
-                           unsigned int dest, struct msi_msg *msg, u8 hpet_id);
 int native_setup_msi_irqs(struct pci_dev *dev, int nvec, int type);
 void native_teardown_msi_irq(unsigned int irq);
 void native_restore_msi_irqs(struct pci_dev *dev);
-int setup_msi_irq(struct pci_dev *dev, struct msi_desc *msidesc,
-                 unsigned int irq_base, unsigned int irq_offset);
 #else
-#define native_compose_msi_msg         NULL
 #define native_setup_msi_irqs          NULL
 #define native_teardown_msi_irq                NULL
 #endif
index fe57e7a98839801ce76a6a33c0f3a7552e5d292a..2562e303405b9d3c314c46a1ce180d4d097d7a11 100644 (file)
@@ -398,11 +398,17 @@ static inline int is_new_memtype_allowed(u64 paddr, unsigned long size,
         * requested memtype:
         * - request is uncached, return cannot be write-back
         * - request is write-combine, return cannot be write-back
+        * - request is write-through, return cannot be write-back
+        * - request is write-through, return cannot be write-combine
         */
        if ((pcm == _PAGE_CACHE_MODE_UC_MINUS &&
             new_pcm == _PAGE_CACHE_MODE_WB) ||
            (pcm == _PAGE_CACHE_MODE_WC &&
-            new_pcm == _PAGE_CACHE_MODE_WB)) {
+            new_pcm == _PAGE_CACHE_MODE_WB) ||
+           (pcm == _PAGE_CACHE_MODE_WT &&
+            new_pcm == _PAGE_CACHE_MODE_WB) ||
+           (pcm == _PAGE_CACHE_MODE_WT &&
+            new_pcm == _PAGE_CACHE_MODE_WC)) {
                return 0;
        }
 
index 78f0c8cbe316f9114746877420cfc400604bd486..13f310bfc09a754bfa3c69305476421e7dd67706 100644 (file)
@@ -367,6 +367,9 @@ extern int nx_enabled;
 #define pgprot_writecombine    pgprot_writecombine
 extern pgprot_t pgprot_writecombine(pgprot_t prot);
 
+#define pgprot_writethrough    pgprot_writethrough
+extern pgprot_t pgprot_writethrough(pgprot_t prot);
+
 /* Indicate that x86 has its own track and untrack pfn vma functions */
 #define __HAVE_PFNMAP_TRACKING
 
index 23ba6765b718c790dbf698edbb2758a2ca6f9102..43e6519df0d507429a9533b51c7a28f2c0f3b90b 100644 (file)
@@ -21,6 +21,7 @@ struct mm_struct;
 #include <asm/desc_defs.h>
 #include <asm/nops.h>
 #include <asm/special_insns.h>
+#include <asm/fpu/types.h>
 
 #include <linux/personality.h>
 #include <linux/cpumask.h>
@@ -52,11 +53,16 @@ static inline void *current_text_addr(void)
        return pc;
 }
 
+/*
+ * These alignment constraints are for performance in the vSMP case,
+ * but in the task_struct case we must also meet hardware imposed
+ * alignment requirements of the FPU state:
+ */
 #ifdef CONFIG_X86_VSMP
 # define ARCH_MIN_TASKALIGN            (1 << INTERNODE_CACHE_SHIFT)
 # define ARCH_MIN_MMSTRUCT_ALIGN       (1 << INTERNODE_CACHE_SHIFT)
 #else
-# define ARCH_MIN_TASKALIGN            16
+# define ARCH_MIN_TASKALIGN            __alignof__(union fpregs_state)
 # define ARCH_MIN_MMSTRUCT_ALIGN       0
 #endif
 
@@ -166,7 +172,6 @@ extern const struct seq_operations cpuinfo_op;
 #define cache_line_size()      (boot_cpu_data.x86_cache_alignment)
 
 extern void cpu_detect(struct cpuinfo_x86 *c);
-extern void fpu_detect(struct cpuinfo_x86 *c);
 
 extern void early_cpu_init(void);
 extern void identify_boot_cpu(void);
@@ -313,128 +318,6 @@ struct orig_ist {
        unsigned long           ist[7];
 };
 
-#define        MXCSR_DEFAULT           0x1f80
-
-struct i387_fsave_struct {
-       u32                     cwd;    /* FPU Control Word             */
-       u32                     swd;    /* FPU Status Word              */
-       u32                     twd;    /* FPU Tag Word                 */
-       u32                     fip;    /* FPU IP Offset                */
-       u32                     fcs;    /* FPU IP Selector              */
-       u32                     foo;    /* FPU Operand Pointer Offset   */
-       u32                     fos;    /* FPU Operand Pointer Selector */
-
-       /* 8*10 bytes for each FP-reg = 80 bytes:                       */
-       u32                     st_space[20];
-
-       /* Software status information [not touched by FSAVE ]:         */
-       u32                     status;
-};
-
-struct i387_fxsave_struct {
-       u16                     cwd; /* Control Word                    */
-       u16                     swd; /* Status Word                     */
-       u16                     twd; /* Tag Word                        */
-       u16                     fop; /* Last Instruction Opcode         */
-       union {
-               struct {
-                       u64     rip; /* Instruction Pointer             */
-                       u64     rdp; /* Data Pointer                    */
-               };
-               struct {
-                       u32     fip; /* FPU IP Offset                   */
-                       u32     fcs; /* FPU IP Selector                 */
-                       u32     foo; /* FPU Operand Offset              */
-                       u32     fos; /* FPU Operand Selector            */
-               };
-       };
-       u32                     mxcsr;          /* MXCSR Register State */
-       u32                     mxcsr_mask;     /* MXCSR Mask           */
-
-       /* 8*16 bytes for each FP-reg = 128 bytes:                      */
-       u32                     st_space[32];
-
-       /* 16*16 bytes for each XMM-reg = 256 bytes:                    */
-       u32                     xmm_space[64];
-
-       u32                     padding[12];
-
-       union {
-               u32             padding1[12];
-               u32             sw_reserved[12];
-       };
-
-} __attribute__((aligned(16)));
-
-struct i387_soft_struct {
-       u32                     cwd;
-       u32                     swd;
-       u32                     twd;
-       u32                     fip;
-       u32                     fcs;
-       u32                     foo;
-       u32                     fos;
-       /* 8*10 bytes for each FP-reg = 80 bytes: */
-       u32                     st_space[20];
-       u8                      ftop;
-       u8                      changed;
-       u8                      lookahead;
-       u8                      no_update;
-       u8                      rm;
-       u8                      alimit;
-       struct math_emu_info    *info;
-       u32                     entry_eip;
-};
-
-struct ymmh_struct {
-       /* 16 * 16 bytes for each YMMH-reg = 256 bytes */
-       u32 ymmh_space[64];
-};
-
-/* We don't support LWP yet: */
-struct lwp_struct {
-       u8 reserved[128];
-};
-
-struct bndreg {
-       u64 lower_bound;
-       u64 upper_bound;
-} __packed;
-
-struct bndcsr {
-       u64 bndcfgu;
-       u64 bndstatus;
-} __packed;
-
-struct xsave_hdr_struct {
-       u64 xstate_bv;
-       u64 xcomp_bv;
-       u64 reserved[6];
-} __attribute__((packed));
-
-struct xsave_struct {
-       struct i387_fxsave_struct i387;
-       struct xsave_hdr_struct xsave_hdr;
-       struct ymmh_struct ymmh;
-       struct lwp_struct lwp;
-       struct bndreg bndreg[4];
-       struct bndcsr bndcsr;
-       /* new processor state extensions will go here */
-} __attribute__ ((packed, aligned (64)));
-
-union thread_xstate {
-       struct i387_fsave_struct        fsave;
-       struct i387_fxsave_struct       fxsave;
-       struct i387_soft_struct         soft;
-       struct xsave_struct             xsave;
-};
-
-struct fpu {
-       unsigned int last_cpu;
-       unsigned int has_fpu;
-       union thread_xstate *state;
-};
-
 #ifdef CONFIG_X86_64
 DECLARE_PER_CPU(struct orig_ist, orig_ist);
 
@@ -483,8 +366,6 @@ DECLARE_PER_CPU(struct irq_stack *, softirq_stack);
 #endif /* X86_64 */
 
 extern unsigned int xstate_size;
-extern void free_thread_xstate(struct task_struct *);
-extern struct kmem_cache *task_xstate_cachep;
 
 struct perf_event;
 
@@ -508,6 +389,10 @@ struct thread_struct {
        unsigned long           fs;
 #endif
        unsigned long           gs;
+
+       /* Floating point and extended processor state */
+       struct fpu              fpu;
+
        /* Save middle states of ptrace breakpoints */
        struct perf_event       *ptrace_bps[HBP_NUM];
        /* Debug status used for traps, single steps, etc... */
@@ -518,8 +403,6 @@ struct thread_struct {
        unsigned long           cr2;
        unsigned long           trap_nr;
        unsigned long           error_code;
-       /* floating point and extended processor state */
-       struct fpu              fpu;
 #ifdef CONFIG_X86_32
        /* Virtual 86 mode info */
        struct vm86_struct __user *vm86_info;
@@ -535,15 +418,6 @@ struct thread_struct {
        unsigned long           iopl;
        /* Max allowed port in the bitmap, in bytes: */
        unsigned                io_bitmap_max;
-       /*
-        * fpu_counter contains the number of consecutive context switches
-        * that the FPU is used. If this is over a threshold, the lazy fpu
-        * saving becomes unlazy to save the trap. This is an unsigned char
-        * so that after 256 times the counter wraps and the behavior turns
-        * lazy again; this to deal with bursty apps that only use FPU for
-        * a short time
-        */
-       unsigned char fpu_counter;
 };
 
 /*
@@ -928,24 +802,25 @@ extern int get_tsc_mode(unsigned long adr);
 extern int set_tsc_mode(unsigned int val);
 
 /* Register/unregister a process' MPX related resource */
-#define MPX_ENABLE_MANAGEMENT(tsk)     mpx_enable_management((tsk))
-#define MPX_DISABLE_MANAGEMENT(tsk)    mpx_disable_management((tsk))
+#define MPX_ENABLE_MANAGEMENT()        mpx_enable_management()
+#define MPX_DISABLE_MANAGEMENT()       mpx_disable_management()
 
 #ifdef CONFIG_X86_INTEL_MPX
-extern int mpx_enable_management(struct task_struct *tsk);
-extern int mpx_disable_management(struct task_struct *tsk);
+extern int mpx_enable_management(void);
+extern int mpx_disable_management(void);
 #else
-static inline int mpx_enable_management(struct task_struct *tsk)
+static inline int mpx_enable_management(void)
 {
        return -EINVAL;
 }
-static inline int mpx_disable_management(struct task_struct *tsk)
+static inline int mpx_disable_management(void)
 {
        return -EINVAL;
 }
 #endif /* CONFIG_X86_INTEL_MPX */
 
 extern u16 amd_get_nb_id(int cpu);
+extern u32 amd_get_nodes_per_socket(void);
 
 static inline uint32_t hypervisor_cpuid_base(const char *sig, uint32_t leaves)
 {
index a90f8972dad507240ae946b61fd8a5f217d4be52..a4a77286cb1ddf68b466e934e9f69cb1785f3062 100644 (file)
@@ -5,12 +5,14 @@
 
 /* misc architecture specific prototypes */
 
-void system_call(void);
 void syscall_init(void);
 
-void ia32_syscall(void);
-void ia32_cstar_target(void);
-void ia32_sysenter_target(void);
+void entry_SYSCALL_64(void);
+void entry_SYSCALL_compat(void);
+void entry_INT80_32(void);
+void entry_INT80_compat(void);
+void entry_SYSENTER_32(void);
+void entry_SYSENTER_compat(void);
 
 void x86_configure_nx(void);
 void x86_report_nx(void);
index 19507ffa5d28e9ce3ddece3856dd9cde4446f7f8..5fabf1362942c65e5fc4327511e51487a14bd5d7 100644 (file)
@@ -107,7 +107,7 @@ static inline unsigned long regs_return_value(struct pt_regs *regs)
 static inline int user_mode(struct pt_regs *regs)
 {
 #ifdef CONFIG_X86_32
-       return (regs->cs & SEGMENT_RPL_MASK) == USER_RPL;
+       return ((regs->cs & SEGMENT_RPL_MASK) | (regs->flags & X86_VM_MASK)) >= USER_RPL;
 #else
        return !!(regs->cs & 3);
 #endif
index 6167fd7981886228dc075f58901380be63ed87a7..655e07a48f6cfa9c09114108f7d8b4cb466fc705 100644 (file)
@@ -41,5 +41,6 @@ struct pvclock_wall_clock {
 
 #define PVCLOCK_TSC_STABLE_BIT (1 << 0)
 #define PVCLOCK_GUEST_STOPPED  (1 << 1)
+#define PVCLOCK_COUNTS_FROM_ZERO (1 << 2)
 #endif /* __ASSEMBLY__ */
 #endif /* _ASM_X86_PVCLOCK_ABI_H */
index d6b078e9fa28a3f4588237cb9a122f5b5ce53162..628954ceede1159fa7df62944d7b140df73d629b 100644 (file)
@@ -86,7 +86,6 @@ unsigned __pvclock_read_cycles(const struct pvclock_vcpu_time_info *src,
        offset = pvclock_get_nsec_offset(src);
        ret = src->system_time + offset;
        ret_flags = src->flags;
-       rdtsc_barrier();
 
        *cycles = ret;
        *flags = ret_flags;
diff --git a/arch/x86/include/asm/qspinlock.h b/arch/x86/include/asm/qspinlock.h
new file mode 100644 (file)
index 0000000..9d51fae
--- /dev/null
@@ -0,0 +1,57 @@
+#ifndef _ASM_X86_QSPINLOCK_H
+#define _ASM_X86_QSPINLOCK_H
+
+#include <asm/cpufeature.h>
+#include <asm-generic/qspinlock_types.h>
+#include <asm/paravirt.h>
+
+#define        queued_spin_unlock queued_spin_unlock
+/**
+ * queued_spin_unlock - release a queued spinlock
+ * @lock : Pointer to queued spinlock structure
+ *
+ * A smp_store_release() on the least-significant byte.
+ */
+static inline void native_queued_spin_unlock(struct qspinlock *lock)
+{
+       smp_store_release((u8 *)lock, 0);
+}
+
+#ifdef CONFIG_PARAVIRT_SPINLOCKS
+extern void native_queued_spin_lock_slowpath(struct qspinlock *lock, u32 val);
+extern void __pv_init_lock_hash(void);
+extern void __pv_queued_spin_lock_slowpath(struct qspinlock *lock, u32 val);
+extern void __raw_callee_save___pv_queued_spin_unlock(struct qspinlock *lock);
+
+static inline void queued_spin_lock_slowpath(struct qspinlock *lock, u32 val)
+{
+       pv_queued_spin_lock_slowpath(lock, val);
+}
+
+static inline void queued_spin_unlock(struct qspinlock *lock)
+{
+       pv_queued_spin_unlock(lock);
+}
+#else
+static inline void queued_spin_unlock(struct qspinlock *lock)
+{
+       native_queued_spin_unlock(lock);
+}
+#endif
+
+#define virt_queued_spin_lock virt_queued_spin_lock
+
+static inline bool virt_queued_spin_lock(struct qspinlock *lock)
+{
+       if (!static_cpu_has(X86_FEATURE_HYPERVISOR))
+               return false;
+
+       while (atomic_cmpxchg(&lock->val, 0, _Q_LOCKED_VAL) != 0)
+               cpu_relax();
+
+       return true;
+}
+
+#include <asm-generic/qspinlock.h>
+
+#endif /* _ASM_X86_QSPINLOCK_H */
diff --git a/arch/x86/include/asm/qspinlock_paravirt.h b/arch/x86/include/asm/qspinlock_paravirt.h
new file mode 100644 (file)
index 0000000..b002e71
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef __ASM_QSPINLOCK_PARAVIRT_H
+#define __ASM_QSPINLOCK_PARAVIRT_H
+
+PV_CALLEE_SAVE_REGS_THUNK(__pv_queued_spin_unlock);
+
+#endif
index 5a9856eb12bad7edb0f9a333870e331f5677d588..7d5a1929d76b31bba69295e533e460ed50904cfd 100644 (file)
 #define TLS_SIZE                       (GDT_ENTRY_TLS_ENTRIES* 8)
 
 #ifdef __KERNEL__
+
+/*
+ * early_idt_handler_array is an array of entry points referenced in the
+ * early IDT.  For simplicity, it's a real array with one entry point
+ * every nine bytes.  That leaves room for an optional 'push $0' if the
+ * vector has no error code (two bytes), a 'push $vector_number' (two
+ * bytes), and a jump to the common entry code (up to five bytes).
+ */
+#define EARLY_IDT_HANDLER_SIZE 9
+
 #ifndef __ASSEMBLY__
 
-extern const char early_idt_handlers[NUM_EXCEPTION_VECTORS][2+2+5];
+extern const char early_idt_handler_array[NUM_EXCEPTION_VECTORS][EARLY_IDT_HANDLER_SIZE];
 #ifdef CONFIG_TRACING
-# define trace_early_idt_handlers early_idt_handlers
+# define trace_early_idt_handler_array early_idt_handler_array
 #endif
 
 /*
index f69e06b283fb9ee03e8704847558aa577aecf221..11af24e09c8a667911bf9f0b4a3e17b7c75acf16 100644 (file)
@@ -60,17 +60,24 @@ static inline void x86_ce4100_early_setup(void) { }
 #ifndef _SETUP
 
 #include <asm/espfix.h>
+#include <linux/kernel.h>
 
 /*
  * This is set up by the setup-routine at boot-time
  */
 extern struct boot_params boot_params;
+extern char _text[];
 
 static inline bool kaslr_enabled(void)
 {
        return !!(boot_params.hdr.loadflags & KASLR_FLAG);
 }
 
+static inline unsigned long kaslr_offset(void)
+{
+       return (unsigned long)&_text - __START_KERNEL;
+}
+
 /*
  * Do NOT EVER look at the BIOS memory size location.
  * It does not work on many machines.
index ee80b92f00962a392a824221dbdbeb781b6312bf..6c8a7ed13365ae5dd675fd4b158c8034ba213a29 100644 (file)
@@ -1,5 +1,5 @@
 
-#include <asm/i387.h>
+#include <asm/fpu/api.h>
 
 /*
  * may_use_simd - whether it is allowable at this time to issue SIMD
index aeb4666e0c0a770a7fbb8432b7d133b2dd9e764d..2270e41b32fd856e226840d4f76dddaa7852d13d 100644 (file)
@@ -215,6 +215,44 @@ static inline void clwb(volatile void *__p)
                : [pax] "a" (p));
 }
 
+/**
+ * pcommit_sfence() - persistent commit and fence
+ *
+ * The PCOMMIT instruction ensures that data that has been flushed from the
+ * processor's cache hierarchy with CLWB, CLFLUSHOPT or CLFLUSH is accepted to
+ * memory and is durable on the DIMM.  The primary use case for this is
+ * persistent memory.
+ *
+ * This function shows how to properly use CLWB/CLFLUSHOPT/CLFLUSH and PCOMMIT
+ * with appropriate fencing.
+ *
+ * Example:
+ * void flush_and_commit_buffer(void *vaddr, unsigned int size)
+ * {
+ *         unsigned long clflush_mask = boot_cpu_data.x86_clflush_size - 1;
+ *         void *vend = vaddr + size;
+ *         void *p;
+ *
+ *         for (p = (void *)((unsigned long)vaddr & ~clflush_mask);
+ *              p < vend; p += boot_cpu_data.x86_clflush_size)
+ *                 clwb(p);
+ *
+ *         // SFENCE to order CLWB/CLFLUSHOPT/CLFLUSH cache flushes
+ *         // MFENCE via mb() also works
+ *         wmb();
+ *
+ *         // PCOMMIT and the required SFENCE for ordering
+ *         pcommit_sfence();
+ * }
+ *
+ * After this function completes the data pointed to by 'vaddr' has been
+ * accepted to memory and will be durable if the 'vaddr' points to persistent
+ * memory.
+ *
+ * PCOMMIT must always be ordered by an MFENCE or SFENCE, so to help simplify
+ * things we include both the PCOMMIT and the required SFENCE in the
+ * alternatives generated by pcommit_sfence().
+ */
 static inline void pcommit_sfence(void)
 {
        alternative(ASM_NOP7,
index 64b611782ef0856f1744611936f76d6e8de1bb57..be0a05913b9105b62122afbbbef99c6146b2dbf6 100644 (file)
 extern struct static_key paravirt_ticketlocks_enabled;
 static __always_inline bool static_key_false(struct static_key *key);
 
+#ifdef CONFIG_QUEUED_SPINLOCKS
+#include <asm/qspinlock.h>
+#else
+
 #ifdef CONFIG_PARAVIRT_SPINLOCKS
 
 static inline void __ticket_enter_slowpath(arch_spinlock_t *lock)
@@ -196,6 +200,7 @@ static inline void arch_spin_unlock_wait(arch_spinlock_t *lock)
                cpu_relax();
        }
 }
+#endif /* CONFIG_QUEUED_SPINLOCKS */
 
 /*
  * Read-write spinlocks, allowing multiple readers
index 5f9d7572d82b190a2b2d2219bb205a1e543d5acd..65c3e37f879aced6501861eff95d56e08735236a 100644 (file)
@@ -23,6 +23,9 @@ typedef u32 __ticketpair_t;
 
 #define TICKET_SHIFT   (sizeof(__ticket_t) * 8)
 
+#ifdef CONFIG_QUEUED_SPINLOCKS
+#include <asm-generic/qspinlock_types.h>
+#else
 typedef struct arch_spinlock {
        union {
                __ticketpair_t head_tail;
@@ -33,6 +36,7 @@ typedef struct arch_spinlock {
 } arch_spinlock_t;
 
 #define __ARCH_SPIN_LOCK_UNLOCKED      { { 0 } }
+#endif /* CONFIG_QUEUED_SPINLOCKS */
 
 #include <asm-generic/qrwlock_types.h>
 
index 6a998598f172424f198bf7e034676a8c47b2eb9b..c2e00bb2a1365cef17911e262d28d0fad75e6828 100644 (file)
@@ -39,7 +39,9 @@
 #include <asm/processor.h>
 #include <asm/percpu.h>
 #include <asm/desc.h>
+
 #include <linux/random.h>
+#include <linux/sched.h>
 
 /*
  * 24 byte read-only segment initializer for stack canary.  Linker
index 552d6c90a6d43dbebbae0756ba10061bc0969e8d..d1793f06854d28f22481c40f03cd2171d7b13495 100644 (file)
@@ -7,7 +7,7 @@
 #define _ASM_X86_SUSPEND_32_H
 
 #include <asm/desc.h>
-#include <asm/i387.h>
+#include <asm/fpu/api.h>
 
 /* image of the saved processor state */
 struct saved_context {
index bc6232834babf33973c8a4e6da1ce63cd159dce4..7ebf0ebe4e687f3704cac47b3de3dd1b906d27e8 100644 (file)
@@ -7,7 +7,7 @@
 #define _ASM_X86_SUSPEND_64_H
 
 #include <asm/desc.h>
-#include <asm/i387.h>
+#include <asm/fpu/api.h>
 
 /*
  * Image of the saved processor state, used by the low level ACPI suspend to
index b4bdec3e9523e03b7ece3154302c8d95ce2a9ac8..225ee545e1a05e36bfa25eb6a214102b8be8c0bf 100644 (file)
@@ -177,8 +177,6 @@ struct thread_info {
  */
 #ifndef __ASSEMBLY__
 
-DECLARE_PER_CPU(unsigned long, kernel_stack);
-
 static inline struct thread_info *current_thread_info(void)
 {
        return (struct thread_info *)(current_top_of_stack() - THREAD_SIZE);
@@ -197,9 +195,13 @@ static inline unsigned long current_stack_pointer(void)
 
 #else /* !__ASSEMBLY__ */
 
+#ifdef CONFIG_X86_64
+# define cpu_current_top_of_stack (cpu_tss + TSS_sp0)
+#endif
+
 /* Load thread_info address into "reg" */
 #define GET_THREAD_INFO(reg) \
-       _ASM_MOV PER_CPU_VAR(kernel_stack),reg ; \
+       _ASM_MOV PER_CPU_VAR(cpu_current_top_of_stack),reg ; \
        _ASM_SUB $(THREAD_SIZE),reg ;
 
 /*
index 5a77593fdaced1134dc0971cf62bb8b07aea6070..0fb46482dfde160b9dcfad6ef57841e07c3830e2 100644 (file)
@@ -26,7 +26,7 @@
 #define _ASM_X86_TOPOLOGY_H
 
 #ifdef CONFIG_X86_32
-# ifdef CONFIG_X86_HT
+# ifdef CONFIG_SMP
 #  define ENABLE_TOPO_DEFINES
 # endif
 #else
index 4cab890007a7267ecc7ce148eaa1fdfbee4a49df..38a09a13a9bcdab24f0e2f08fd67f10cc25236dd 100644 (file)
@@ -100,6 +100,12 @@ DEFINE_IRQ_VECTOR_EVENT(call_function_single);
  */
 DEFINE_IRQ_VECTOR_EVENT(threshold_apic);
 
+/*
+ * deferred_error_apic - called when entering/exiting a deferred apic interrupt
+ * vector handler
+ */
+DEFINE_IRQ_VECTOR_EVENT(deferred_error_apic);
+
 /*
  * thermal_apic - called when entering/exiting a thermal apic interrupt
  * vector handler
diff --git a/arch/x86/include/asm/trace/mpx.h b/arch/x86/include/asm/trace/mpx.h
new file mode 100644 (file)
index 0000000..173dd3b
--- /dev/null
@@ -0,0 +1,132 @@
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM mpx
+
+#if !defined(_TRACE_MPX_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_MPX_H
+
+#include <linux/tracepoint.h>
+
+#ifdef CONFIG_X86_INTEL_MPX
+
+TRACE_EVENT(mpx_bounds_register_exception,
+
+       TP_PROTO(void *addr_referenced,
+                const struct bndreg *bndreg),
+       TP_ARGS(addr_referenced, bndreg),
+
+       TP_STRUCT__entry(
+               __field(void *, addr_referenced)
+               __field(u64, lower_bound)
+               __field(u64, upper_bound)
+       ),
+
+       TP_fast_assign(
+               __entry->addr_referenced = addr_referenced;
+               __entry->lower_bound = bndreg->lower_bound;
+               __entry->upper_bound = bndreg->upper_bound;
+       ),
+       /*
+        * Note that we are printing out the '~' of the upper
+        * bounds register here.  It is actually stored in its
+        * one's complement form so that its 'init' state
+        * corresponds to all 0's.  But, that looks like
+        * gibberish when printed out, so print out the 1's
+        * complement instead of the actual value here.  Note
+        * though that you still need to specify filters for the
+        * actual value, not the displayed one.
+        */
+       TP_printk("address referenced: 0x%p bounds: lower: 0x%llx ~upper: 0x%llx",
+               __entry->addr_referenced,
+               __entry->lower_bound,
+               ~__entry->upper_bound
+       )
+);
+
+TRACE_EVENT(bounds_exception_mpx,
+
+       TP_PROTO(const struct bndcsr *bndcsr),
+       TP_ARGS(bndcsr),
+
+       TP_STRUCT__entry(
+               __field(u64, bndcfgu)
+               __field(u64, bndstatus)
+       ),
+
+       TP_fast_assign(
+               /* need to get rid of the 'const' on bndcsr */
+               __entry->bndcfgu   = (u64)bndcsr->bndcfgu;
+               __entry->bndstatus = (u64)bndcsr->bndstatus;
+       ),
+
+       TP_printk("bndcfgu:0x%llx bndstatus:0x%llx",
+               __entry->bndcfgu,
+               __entry->bndstatus)
+);
+
+DECLARE_EVENT_CLASS(mpx_range_trace,
+
+       TP_PROTO(unsigned long start,
+                unsigned long end),
+       TP_ARGS(start, end),
+
+       TP_STRUCT__entry(
+               __field(unsigned long, start)
+               __field(unsigned long, end)
+       ),
+
+       TP_fast_assign(
+               __entry->start = start;
+               __entry->end   = end;
+       ),
+
+       TP_printk("[0x%p:0x%p]",
+               (void *)__entry->start,
+               (void *)__entry->end
+       )
+);
+
+DEFINE_EVENT(mpx_range_trace, mpx_unmap_zap,
+       TP_PROTO(unsigned long start, unsigned long end),
+       TP_ARGS(start, end)
+);
+
+DEFINE_EVENT(mpx_range_trace, mpx_unmap_search,
+       TP_PROTO(unsigned long start, unsigned long end),
+       TP_ARGS(start, end)
+);
+
+TRACE_EVENT(mpx_new_bounds_table,
+
+       TP_PROTO(unsigned long table_vaddr),
+       TP_ARGS(table_vaddr),
+
+       TP_STRUCT__entry(
+               __field(unsigned long, table_vaddr)
+       ),
+
+       TP_fast_assign(
+               __entry->table_vaddr = table_vaddr;
+       ),
+
+       TP_printk("table vaddr:%p", (void *)__entry->table_vaddr)
+);
+
+#else
+
+/*
+ * This gets used outside of MPX-specific code, so we need a stub.
+ */
+static inline void trace_bounds_exception_mpx(const struct bndcsr *bndcsr)
+{
+}
+
+#endif /* CONFIG_X86_INTEL_MPX */
+
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH asm/trace/
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_FILE mpx
+#endif /* _TRACE_MPX_H */
+
+/* This part must be outside protection */
+#include <trace/define_trace.h>
index 4e49d7dff78e5f30ffb6353c37277eaf6ab264aa..c5380bea2a36aaa533fd8aa28c58ff2ee68a7dec 100644 (file)
@@ -108,7 +108,8 @@ extern int panic_on_unrecovered_nmi;
 void math_emulate(struct math_emu_info *);
 #ifndef CONFIG_X86_32
 asmlinkage void smp_thermal_interrupt(void);
-asmlinkage void mce_threshold_interrupt(void);
+asmlinkage void smp_threshold_interrupt(void);
+asmlinkage void smp_deferred_error_interrupt(void);
 #endif
 
 extern enum ctx_state ist_enter(struct pt_regs *regs);
index 7c8ad3451988811787a7f7b26a5e1822288a00f9..f5dcb5204dcd5b27e8b8e9a1b87612a28cda10c6 100644 (file)
@@ -59,6 +59,10 @@ __copy_to_user_inatomic(void __user *to, const void *from, unsigned long n)
                        __put_user_size(*(u32 *)from, (u32 __user *)to,
                                        4, ret, 4);
                        return ret;
+               case 8:
+                       __put_user_size(*(u64 *)from, (u64 __user *)to,
+                                       8, ret, 8);
+                       return ret;
                }
        }
        return __copy_to_user_ll(to, from, n);
index ccab4af1646d440584bf981c3a08784590b961dc..59a54e869f1598f05a38310e955d360f59bd87ad 100644 (file)
@@ -14,8 +14,8 @@ struct user_ymmh_regs {
        __u32 ymmh_space[64];
 };
 
-struct user_xsave_hdr {
-       __u64 xstate_bv;
+struct user_xstate_header {
+       __u64 xfeatures;
        __u64 reserved1[2];
        __u64 reserved2[5];
 };
@@ -41,11 +41,11 @@ struct user_xsave_hdr {
  * particular process/thread.
  *
  * Also when the user modifies certain state FP/SSE/etc through the
- * ptrace interface, they must ensure that the xsave_hdr.xstate_bv
+ * ptrace interface, they must ensure that the header.xfeatures
  * bytes[512..519] of the memory layout are updated correspondingly.
  * i.e., for example when FP state is modified to a non-init state,
- * xsave_hdr.xstate_bv's bit 0 must be set to '1', when SSE is modified to
- * non-init state, xsave_hdr.xstate_bv's bit 1 must to be set to '1', etc.
+ * header.xfeatures's bit 0 must be set to '1', when SSE is modified to
+ * non-init state, header.xfeatures's bit 1 must to be set to '1', etc.
  */
 #define USER_XSTATE_FX_SW_WORDS 6
 #define USER_XSTATE_XCR0_WORD  0
@@ -55,7 +55,7 @@ struct user_xstateregs {
                __u64 fpx_space[58];
                __u64 xstate_fx_sw[USER_XSTATE_FX_SW_WORDS];
        } i387;
-       struct user_xsave_hdr xsave_hdr;
+       struct user_xstate_header header;
        struct user_ymmh_regs ymmh;
        /* further processor state extensions go here */
 };
index f58a9c7a3c86658d6094be935fff23b50f785cc5..48d34d28f5a60543bc72471e2a1931fcf13dc04e 100644 (file)
@@ -171,38 +171,17 @@ struct x86_platform_ops {
 };
 
 struct pci_dev;
-struct msi_msg;
 
 struct x86_msi_ops {
        int (*setup_msi_irqs)(struct pci_dev *dev, int nvec, int type);
-       void (*compose_msi_msg)(struct pci_dev *dev, unsigned int irq,
-                               unsigned int dest, struct msi_msg *msg,
-                              u8 hpet_id);
        void (*teardown_msi_irq)(unsigned int irq);
        void (*teardown_msi_irqs)(struct pci_dev *dev);
        void (*restore_msi_irqs)(struct pci_dev *dev);
-       int  (*setup_hpet_msi)(unsigned int irq, unsigned int id);
 };
 
-struct IO_APIC_route_entry;
-struct io_apic_irq_attr;
-struct irq_data;
-struct cpumask;
-
 struct x86_io_apic_ops {
-       void            (*init)   (void);
        unsigned int    (*read)   (unsigned int apic, unsigned int reg);
-       void            (*write)  (unsigned int apic, unsigned int reg, unsigned int value);
-       void            (*modify) (unsigned int apic, unsigned int reg, unsigned int value);
        void            (*disable)(void);
-       void            (*print_entries)(unsigned int apic, unsigned int nr_entries);
-       int             (*set_affinity)(struct irq_data *data,
-                                       const struct cpumask *mask,
-                                       bool force);
-       int             (*setup_entry)(int irq, struct IO_APIC_route_entry *entry,
-                                      unsigned int destination, int vector,
-                                      struct io_apic_irq_attr *attr);
-       void            (*eoi_ioapic_pin)(int apic, int pin, int vector);
 };
 
 extern struct x86_init_ops x86_init;
diff --git a/arch/x86/include/asm/xcr.h b/arch/x86/include/asm/xcr.h
deleted file mode 100644 (file)
index f2cba4e..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-/* -*- linux-c -*- ------------------------------------------------------- *
- *
- *   Copyright 2008 rPath, Inc. - All Rights Reserved
- *
- *   This file is part of the Linux kernel, and is made available under
- *   the terms of the GNU General Public License version 2 or (at your
- *   option) any later version; incorporated herein by reference.
- *
- * ----------------------------------------------------------------------- */
-
-/*
- * asm-x86/xcr.h
- *
- * Definitions for the eXtended Control Register instructions
- */
-
-#ifndef _ASM_X86_XCR_H
-#define _ASM_X86_XCR_H
-
-#define XCR_XFEATURE_ENABLED_MASK      0x00000000
-
-#ifdef __KERNEL__
-# ifndef __ASSEMBLY__
-
-#include <linux/types.h>
-
-static inline u64 xgetbv(u32 index)
-{
-       u32 eax, edx;
-
-       asm volatile(".byte 0x0f,0x01,0xd0" /* xgetbv */
-                    : "=a" (eax), "=d" (edx)
-                    : "c" (index));
-       return eax + ((u64)edx << 32);
-}
-
-static inline void xsetbv(u32 index, u64 value)
-{
-       u32 eax = value;
-       u32 edx = value >> 32;
-
-       asm volatile(".byte 0x0f,0x01,0xd1" /* xsetbv */
-                    : : "a" (eax), "d" (edx), "c" (index));
-}
-
-# endif /* __ASSEMBLY__ */
-#endif /* __KERNEL__ */
-
-#endif /* _ASM_X86_XCR_H */
index d8829751b3f895e9fd19fa6bd597758650f417b8..1f5c5161ead682664dc30fc5dda802de2de0bc4b 100644 (file)
@@ -36,7 +36,7 @@
  * no advantages to be gotten from x86-64 here anyways.
  */
 
-#include <asm/i387.h>
+#include <asm/fpu/api.h>
 
 #ifdef CONFIG_X86_32
 /* reduce register pressure */
index ce05722e3c68bce4d72a1bcdc9e798b5014581cf..5a08bc8bff33934e10b4b9afe8e3236ac8c5ce93 100644 (file)
@@ -26,7 +26,7 @@
 #define XO3(x, y)      "       pxor   8*("#x")(%4), %%mm"#y"   ;\n"
 #define XO4(x, y)      "       pxor   8*("#x")(%5), %%mm"#y"   ;\n"
 
-#include <asm/i387.h>
+#include <asm/fpu/api.h>
 
 static void
 xor_pII_mmx_2(unsigned long bytes, unsigned long *p1, unsigned long *p2)
index 492b29802f571b0363a22fe686f708c8235c317a..7c0a517ec7511a667166c216df8357087ff3e7b0 100644 (file)
@@ -18,7 +18,7 @@
 #ifdef CONFIG_AS_AVX
 
 #include <linux/compiler.h>
-#include <asm/i387.h>
+#include <asm/fpu/api.h>
 
 #define BLOCK4(i) \
                BLOCK(32 * i, 0) \
diff --git a/arch/x86/include/asm/xsave.h b/arch/x86/include/asm/xsave.h
deleted file mode 100644 (file)
index c9a6d68..0000000
+++ /dev/null
@@ -1,257 +0,0 @@
-#ifndef __ASM_X86_XSAVE_H
-#define __ASM_X86_XSAVE_H
-
-#include <linux/types.h>
-#include <asm/processor.h>
-
-#define XSTATE_CPUID           0x0000000d
-
-#define XSTATE_FP              0x1
-#define XSTATE_SSE             0x2
-#define XSTATE_YMM             0x4
-#define XSTATE_BNDREGS         0x8
-#define XSTATE_BNDCSR          0x10
-#define XSTATE_OPMASK          0x20
-#define XSTATE_ZMM_Hi256       0x40
-#define XSTATE_Hi16_ZMM                0x80
-
-#define XSTATE_FPSSE   (XSTATE_FP | XSTATE_SSE)
-#define XSTATE_AVX512  (XSTATE_OPMASK | XSTATE_ZMM_Hi256 | XSTATE_Hi16_ZMM)
-/* Bit 63 of XCR0 is reserved for future expansion */
-#define XSTATE_EXTEND_MASK     (~(XSTATE_FPSSE | (1ULL << 63)))
-
-#define FXSAVE_SIZE    512
-
-#define XSAVE_HDR_SIZE     64
-#define XSAVE_HDR_OFFSET    FXSAVE_SIZE
-
-#define XSAVE_YMM_SIZE     256
-#define XSAVE_YMM_OFFSET    (XSAVE_HDR_SIZE + XSAVE_HDR_OFFSET)
-
-/* Supported features which support lazy state saving */
-#define XSTATE_LAZY    (XSTATE_FP | XSTATE_SSE | XSTATE_YMM                  \
-                       | XSTATE_OPMASK | XSTATE_ZMM_Hi256 | XSTATE_Hi16_ZMM)
-
-/* Supported features which require eager state saving */
-#define XSTATE_EAGER   (XSTATE_BNDREGS | XSTATE_BNDCSR)
-
-/* All currently supported features */
-#define XCNTXT_MASK    (XSTATE_LAZY | XSTATE_EAGER)
-
-#ifdef CONFIG_X86_64
-#define REX_PREFIX     "0x48, "
-#else
-#define REX_PREFIX
-#endif
-
-extern unsigned int xstate_size;
-extern u64 pcntxt_mask;
-extern u64 xstate_fx_sw_bytes[USER_XSTATE_FX_SW_WORDS];
-extern struct xsave_struct *init_xstate_buf;
-
-extern void xsave_init(void);
-extern void update_regset_xstate_info(unsigned int size, u64 xstate_mask);
-extern int init_fpu(struct task_struct *child);
-
-/* These macros all use (%edi)/(%rdi) as the single memory argument. */
-#define XSAVE          ".byte " REX_PREFIX "0x0f,0xae,0x27"
-#define XSAVEOPT       ".byte " REX_PREFIX "0x0f,0xae,0x37"
-#define XSAVES         ".byte " REX_PREFIX "0x0f,0xc7,0x2f"
-#define XRSTOR         ".byte " REX_PREFIX "0x0f,0xae,0x2f"
-#define XRSTORS                ".byte " REX_PREFIX "0x0f,0xc7,0x1f"
-
-#define xstate_fault   ".section .fixup,\"ax\"\n"      \
-                       "3:  movl $-1,%[err]\n"         \
-                       "    jmp  2b\n"                 \
-                       ".previous\n"                   \
-                       _ASM_EXTABLE(1b, 3b)            \
-                       : [err] "=r" (err)
-
-/*
- * This function is called only during boot time when x86 caps are not set
- * up and alternative can not be used yet.
- */
-static inline int xsave_state_booting(struct xsave_struct *fx, u64 mask)
-{
-       u32 lmask = mask;
-       u32 hmask = mask >> 32;
-       int err = 0;
-
-       WARN_ON(system_state != SYSTEM_BOOTING);
-
-       if (boot_cpu_has(X86_FEATURE_XSAVES))
-               asm volatile("1:"XSAVES"\n\t"
-                       "2:\n\t"
-                            xstate_fault
-                       : "D" (fx), "m" (*fx), "a" (lmask), "d" (hmask)
-                       :   "memory");
-       else
-               asm volatile("1:"XSAVE"\n\t"
-                       "2:\n\t"
-                            xstate_fault
-                       : "D" (fx), "m" (*fx), "a" (lmask), "d" (hmask)
-                       :   "memory");
-       return err;
-}
-
-/*
- * This function is called only during boot time when x86 caps are not set
- * up and alternative can not be used yet.
- */
-static inline int xrstor_state_booting(struct xsave_struct *fx, u64 mask)
-{
-       u32 lmask = mask;
-       u32 hmask = mask >> 32;
-       int err = 0;
-
-       WARN_ON(system_state != SYSTEM_BOOTING);
-
-       if (boot_cpu_has(X86_FEATURE_XSAVES))
-               asm volatile("1:"XRSTORS"\n\t"
-                       "2:\n\t"
-                            xstate_fault
-                       : "D" (fx), "m" (*fx), "a" (lmask), "d" (hmask)
-                       :   "memory");
-       else
-               asm volatile("1:"XRSTOR"\n\t"
-                       "2:\n\t"
-                            xstate_fault
-                       : "D" (fx), "m" (*fx), "a" (lmask), "d" (hmask)
-                       :   "memory");
-       return err;
-}
-
-/*
- * Save processor xstate to xsave area.
- */
-static inline int xsave_state(struct xsave_struct *fx, u64 mask)
-{
-       u32 lmask = mask;
-       u32 hmask = mask >> 32;
-       int err = 0;
-
-       /*
-        * If xsaves is enabled, xsaves replaces xsaveopt because
-        * it supports compact format and supervisor states in addition to
-        * modified optimization in xsaveopt.
-        *
-        * Otherwise, if xsaveopt is enabled, xsaveopt replaces xsave
-        * because xsaveopt supports modified optimization which is not
-        * supported by xsave.
-        *
-        * If none of xsaves and xsaveopt is enabled, use xsave.
-        */
-       alternative_input_2(
-               "1:"XSAVE,
-               XSAVEOPT,
-               X86_FEATURE_XSAVEOPT,
-               XSAVES,
-               X86_FEATURE_XSAVES,
-               [fx] "D" (fx), "a" (lmask), "d" (hmask) :
-               "memory");
-       asm volatile("2:\n\t"
-                    xstate_fault
-                    : "0" (0)
-                    : "memory");
-
-       return err;
-}
-
-/*
- * Restore processor xstate from xsave area.
- */
-static inline int xrstor_state(struct xsave_struct *fx, u64 mask)
-{
-       int err = 0;
-       u32 lmask = mask;
-       u32 hmask = mask >> 32;
-
-       /*
-        * Use xrstors to restore context if it is enabled. xrstors supports
-        * compacted format of xsave area which is not supported by xrstor.
-        */
-       alternative_input(
-               "1: " XRSTOR,
-               XRSTORS,
-               X86_FEATURE_XSAVES,
-               "D" (fx), "m" (*fx), "a" (lmask), "d" (hmask)
-               : "memory");
-
-       asm volatile("2:\n"
-                    xstate_fault
-                    : "0" (0)
-                    : "memory");
-
-       return err;
-}
-
-/*
- * Save xstate context for old process during context switch.
- */
-static inline void fpu_xsave(struct fpu *fpu)
-{
-       xsave_state(&fpu->state->xsave, -1);
-}
-
-/*
- * Restore xstate context for new process during context switch.
- */
-static inline int fpu_xrstor_checking(struct xsave_struct *fx)
-{
-       return xrstor_state(fx, -1);
-}
-
-/*
- * Save xstate to user space xsave area.
- *
- * We don't use modified optimization because xrstor/xrstors might track
- * a different application.
- *
- * We don't use compacted format xsave area for
- * backward compatibility for old applications which don't understand
- * compacted format of xsave area.
- */
-static inline int xsave_user(struct xsave_struct __user *buf)
-{
-       int err;
-
-       /*
-        * Clear the xsave header first, so that reserved fields are
-        * initialized to zero.
-        */
-       err = __clear_user(&buf->xsave_hdr, sizeof(buf->xsave_hdr));
-       if (unlikely(err))
-               return -EFAULT;
-
-       __asm__ __volatile__(ASM_STAC "\n"
-                            "1:"XSAVE"\n"
-                            "2: " ASM_CLAC "\n"
-                            xstate_fault
-                            : "D" (buf), "a" (-1), "d" (-1), "0" (0)
-                            : "memory");
-       return err;
-}
-
-/*
- * Restore xstate from user space xsave area.
- */
-static inline int xrestore_user(struct xsave_struct __user *buf, u64 mask)
-{
-       int err = 0;
-       struct xsave_struct *xstate = ((__force struct xsave_struct *)buf);
-       u32 lmask = mask;
-       u32 hmask = mask >> 32;
-
-       __asm__ __volatile__(ASM_STAC "\n"
-                            "1:"XRSTOR"\n"
-                            "2: " ASM_CLAC "\n"
-                            xstate_fault
-                            : "D" (xstate), "a" (lmask), "d" (hmask), "0" (0)
-                            : "memory");       /* memory required? */
-       return err;
-}
-
-void *get_xsave_addr(struct xsave_struct *xsave, int xstate);
-void setup_xstate_comp(void);
-
-#endif
index d7dcef58aefa9c736efedcd6292bd0d539ac24a4..a4ae82eb82aa8485146aee83376ba26fb9306afe 100644 (file)
@@ -106,6 +106,8 @@ struct kvm_ioapic_state {
 #define KVM_IRQCHIP_IOAPIC       2
 #define KVM_NR_IRQCHIPS          3
 
+#define KVM_RUN_X86_SMM                 (1 << 0)
+
 /* for KVM_GET_REGS and KVM_SET_REGS */
 struct kvm_regs {
        /* out (KVM_GET_REGS) / in (KVM_SET_REGS) */
@@ -281,6 +283,7 @@ struct kvm_reinject_control {
 #define KVM_VCPUEVENT_VALID_NMI_PENDING        0x00000001
 #define KVM_VCPUEVENT_VALID_SIPI_VECTOR        0x00000002
 #define KVM_VCPUEVENT_VALID_SHADOW     0x00000004
+#define KVM_VCPUEVENT_VALID_SMM                0x00000008
 
 /* Interrupt shadow states */
 #define KVM_X86_SHADOW_INT_MOV_SS      0x01
@@ -309,7 +312,13 @@ struct kvm_vcpu_events {
        } nmi;
        __u32 sipi_vector;
        __u32 flags;
-       __u32 reserved[10];
+       struct {
+               __u8 smm;
+               __u8 pending;
+               __u8 smm_inside_nmi;
+               __u8 latched_init;
+       } smi;
+       __u32 reserved[9];
 };
 
 /* for KVM_GET/SET_DEBUGREGS */
@@ -345,4 +354,7 @@ struct kvm_xcrs {
 struct kvm_sync_regs {
 };
 
+#define KVM_QUIRK_LINT0_REENABLED      (1 << 0)
+#define KVM_QUIRK_CD_NW_CLEARED                (1 << 1)
+
 #endif /* _ASM_X86_KVM_H */
index 155e51048fa4067581ba8fbf8bafa108e193f8ec..c41f4fe25483ae7eed13eb9ee3e65a7aa9f6edbe 100644 (file)
@@ -1,8 +1,6 @@
 #ifndef _UAPI_ASM_X86_MSR_H
 #define _UAPI_ASM_X86_MSR_H
 
-#include <asm/msr-index.h>
-
 #ifndef __ASSEMBLY__
 
 #include <linux/types.h>
index d0acb658c8f43950807dbf39a57e84c73bf34f06..7528dcf59691652571283d186a52f50abe4270a6 100644 (file)
@@ -103,7 +103,7 @@ struct mtrr_state_type {
 #define MTRRIOC_GET_PAGE_ENTRY   _IOWR(MTRR_IOCTL_BASE, 8, struct mtrr_gentry)
 #define MTRRIOC_KILL_PAGE_ENTRY  _IOW(MTRR_IOCTL_BASE,  9, struct mtrr_sentry)
 
-/*  These are the region types  */
+/* MTRR memory types, which are defined in SDM */
 #define MTRR_TYPE_UNCACHABLE 0
 #define MTRR_TYPE_WRCOMB     1
 /*#define MTRR_TYPE_         2*/
@@ -113,5 +113,11 @@ struct mtrr_state_type {
 #define MTRR_TYPE_WRBACK     6
 #define MTRR_NUM_TYPES       7
 
+/*
+ * Invalid MTRR memory type.  mtrr_type_lookup() returns this value when
+ * MTRRs are disabled.  Note, this value is allocated from the reserved
+ * values (0x7-0xff) of the MTRR memory types.
+ */
+#define MTRR_TYPE_INVALID    0xff
 
 #endif /* _UAPI_ASM_X86_MTRR_H */
index 16dc4e8a2cd34845042445915f9e9d74d90546c6..0e8a973de9ee8aec0c555a5e9e8b23348e2cc10b 100644 (file)
@@ -25,7 +25,7 @@ struct _fpx_sw_bytes {
        __u32 extended_size;    /* total size of the layout referred by
                                 * fpstate pointer in the sigcontext.
                                 */
-       __u64 xstate_bv;
+       __u64 xfeatures;
                                /* feature bit mask (including fp/sse/extended
                                 * state) that is present in the memory
                                 * layout.
@@ -209,8 +209,8 @@ struct sigcontext {
 
 #endif /* !__i386__ */
 
-struct _xsave_hdr {
-       __u64 xstate_bv;
+struct _header {
+       __u64 xfeatures;
        __u64 reserved1[2];
        __u64 reserved2[5];
 };
@@ -228,7 +228,7 @@ struct _ymmh_state {
  */
 struct _xstate {
        struct _fpstate fpstate;
-       struct _xsave_hdr xstate_hdr;
+       struct _header xstate_hdr;
        struct _ymmh_state ymmh;
        /* new processor state extensions go here */
 };
index 9bcd0b56ca1775aa82a9dee3a47614461bb7a881..0f15af41bd80b764c80f90f0153a96e136a2106e 100644 (file)
@@ -22,7 +22,7 @@ KASAN_SANITIZE_dumpstack_$(BITS).o := n
 
 CFLAGS_irq.o := -I$(src)/../include/asm/trace
 
-obj-y                  := process_$(BITS).o signal.o entry_$(BITS).o
+obj-y                  := process_$(BITS).o signal.o
 obj-y                  += traps.o irq.o irq_$(BITS).o dumpstack_$(BITS).o
 obj-y                  += time.o ioport.o ldt.o dumpstack.o nmi.o
 obj-y                  += setup.o x86_init.o i8259.o irqinit.o jump_label.o
@@ -31,9 +31,6 @@ 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-y                  += syscall_$(BITS).o vsyscall_gtod.o
-obj-$(CONFIG_IA32_EMULATION)   += syscall_32.o
-obj-$(CONFIG_X86_VSYSCALL_EMULATION)   += vsyscall_64.o vsyscall_emu_64.o
 obj-$(CONFIG_X86_ESPFIX64)     += espfix_64.o
 obj-$(CONFIG_SYSFS)    += ksysfs.o
 obj-y                  += bootflag.o e820.o
@@ -44,7 +41,7 @@ obj-y                 += pci-iommu_table.o
 obj-y                  += resource.o
 
 obj-y                          += process.o
-obj-y                          += i387.o xsave.o
+obj-y                          += fpu/
 obj-y                          += ptrace.o
 obj-$(CONFIG_X86_32)           += tls.o
 obj-$(CONFIG_IA32_EMULATION)   += tls.o
index dbe76a14c3c9909e50ad51ff14e2eefd31cdf101..e49ee24da85e17b247b8decc355dd15393e8f1ad 100644 (file)
 #include <linux/module.h>
 #include <linux/dmi.h>
 #include <linux/irq.h>
-#include <linux/irqdomain.h>
 #include <linux/slab.h>
 #include <linux/bootmem.h>
 #include <linux/ioport.h>
 #include <linux/pci.h>
 
+#include <asm/irqdomain.h>
 #include <asm/pci_x86.h>
 #include <asm/pgtable.h>
 #include <asm/io_apic.h>
@@ -400,57 +400,13 @@ static int mp_config_acpi_gsi(struct device *dev, u32 gsi, int trigger,
        return 0;
 }
 
-static int mp_register_gsi(struct device *dev, u32 gsi, int trigger,
-                          int polarity)
-{
-       int irq, node;
-
-       if (acpi_irq_model != ACPI_IRQ_MODEL_IOAPIC)
-               return gsi;
-
-       trigger = trigger == ACPI_EDGE_SENSITIVE ? 0 : 1;
-       polarity = polarity == ACPI_ACTIVE_HIGH ? 0 : 1;
-       node = dev ? dev_to_node(dev) : NUMA_NO_NODE;
-       if (mp_set_gsi_attr(gsi, trigger, polarity, node)) {
-               pr_warn("Failed to set pin attr for GSI%d\n", gsi);
-               return -1;
-       }
-
-       irq = mp_map_gsi_to_irq(gsi, IOAPIC_MAP_ALLOC);
-       if (irq < 0)
-               return irq;
-
-       /* Don't set up the ACPI SCI because it's already set up */
-       if (enable_update_mptable && acpi_gbl_FADT.sci_interrupt != gsi)
-               mp_config_acpi_gsi(dev, gsi, trigger, polarity);
-
-       return irq;
-}
-
-static void mp_unregister_gsi(u32 gsi)
-{
-       int irq;
-
-       if (acpi_irq_model != ACPI_IRQ_MODEL_IOAPIC)
-               return;
-
-       irq = mp_map_gsi_to_irq(gsi, 0);
-       if (irq > 0)
-               mp_unmap_irq(irq);
-}
-
-static struct irq_domain_ops acpi_irqdomain_ops = {
-       .map = mp_irqdomain_map,
-       .unmap = mp_irqdomain_unmap,
-};
-
 static int __init
 acpi_parse_ioapic(struct acpi_subtable_header * header, const unsigned long end)
 {
        struct acpi_madt_io_apic *ioapic = NULL;
        struct ioapic_domain_cfg cfg = {
                .type = IOAPIC_DOMAIN_DYNAMIC,
-               .ops = &acpi_irqdomain_ops,
+               .ops = &mp_ioapic_irqdomain_ops,
        };
 
        ioapic = (struct acpi_madt_io_apic *)header;
@@ -652,7 +608,7 @@ static int acpi_register_gsi_pic(struct device *dev, u32 gsi,
         * Make sure all (legacy) PCI IRQs are set as level-triggered.
         */
        if (trigger == ACPI_LEVEL_SENSITIVE)
-               eisa_set_level_irq(gsi);
+               elcr_set_level_irq(gsi);
 #endif
 
        return gsi;
@@ -663,10 +619,21 @@ static int acpi_register_gsi_ioapic(struct device *dev, u32 gsi,
                                    int trigger, int polarity)
 {
        int irq = gsi;
-
 #ifdef CONFIG_X86_IO_APIC
+       int node;
+       struct irq_alloc_info info;
+
+       node = dev ? dev_to_node(dev) : NUMA_NO_NODE;
+       trigger = trigger == ACPI_EDGE_SENSITIVE ? 0 : 1;
+       polarity = polarity == ACPI_ACTIVE_HIGH ? 0 : 1;
+       ioapic_set_alloc_attr(&info, node, trigger, polarity);
+
        mutex_lock(&acpi_ioapic_lock);
-       irq = mp_register_gsi(dev, gsi, trigger, polarity);
+       irq = mp_map_gsi_to_irq(gsi, IOAPIC_MAP_ALLOC, &info);
+       /* Don't set up the ACPI SCI because it's already set up */
+       if (irq >= 0 && enable_update_mptable &&
+           acpi_gbl_FADT.sci_interrupt != gsi)
+               mp_config_acpi_gsi(dev, gsi, trigger, polarity);
        mutex_unlock(&acpi_ioapic_lock);
 #endif
 
@@ -676,8 +643,12 @@ static int acpi_register_gsi_ioapic(struct device *dev, u32 gsi,
 static void acpi_unregister_gsi_ioapic(u32 gsi)
 {
 #ifdef CONFIG_X86_IO_APIC
+       int irq;
+
        mutex_lock(&acpi_ioapic_lock);
-       mp_unregister_gsi(gsi);
+       irq = mp_map_gsi_to_irq(gsi, 0, NULL);
+       if (irq > 0)
+               mp_unmap_irq(irq);
        mutex_unlock(&acpi_ioapic_lock);
 #endif
 }
@@ -786,7 +757,7 @@ int acpi_register_ioapic(acpi_handle handle, u64 phys_addr, u32 gsi_base)
        u64 addr;
        struct ioapic_domain_cfg cfg = {
                .type = IOAPIC_DOMAIN_DYNAMIC,
-               .ops = &acpi_irqdomain_ops,
+               .ops = &mp_ioapic_irqdomain_ops,
        };
 
        ioapic_id = acpi_get_ioapic_id(handle, gsi_base, &addr);
index 665c6b7d2ea93c25740a4b18043d5602d33d9711..0c26b1b44e51aeffb72a6daa151049880f52892f 100644 (file)
@@ -12,11 +12,13 @@ ENTRY(wakeup_pmode_return)
 wakeup_pmode_return:
        movw    $__KERNEL_DS, %ax
        movw    %ax, %ss
-       movw    %ax, %ds
-       movw    %ax, %es
        movw    %ax, %fs
        movw    %ax, %gs
 
+       movw    $__USER_DS, %ax
+       movw    %ax, %ds
+       movw    %ax, %es
+
        # reload the gdt, as we need the full 32 bit address
        lidt    saved_idt
        lldt    saved_ldt
index ae693b51ed8ed589660570a77cfb5654af924388..8c35df4681041eee92f14020de8f795a8b7d710c 100644 (file)
@@ -62,7 +62,7 @@ ENTRY(do_suspend_lowlevel)
        pushfq
        popq    pt_regs_flags(%rax)
 
-       movq    $resume_point, saved_rip(%rip)
+       movq    $.Lresume_point, saved_rip(%rip)
 
        movq    %rsp, saved_rsp
        movq    %rbp, saved_rbp
@@ -75,10 +75,10 @@ ENTRY(do_suspend_lowlevel)
        xorl    %eax, %eax
        call    x86_acpi_enter_sleep_state
        /* in case something went wrong, restore the machine status and go on */
-       jmp     resume_point
+       jmp     .Lresume_point
 
        .align 4
-resume_point:
+.Lresume_point:
        /* We don't restore %rax, it must be 0 anyway */
        movq    $saved_context, %rax
        movq    saved_context_cr4(%rax), %rbx
index aef65319316065eab845f35141682c3550f18a22..c42827eb86cf0c52c36389d0c26dc937776b03bf 100644 (file)
 #include <asm/io.h>
 #include <asm/fixmap.h>
 
+int __read_mostly alternatives_patched;
+
+EXPORT_SYMBOL_GPL(alternatives_patched);
+
 #define MAX_PATCH_LEN (255-1)
 
 static int __initdata_or_module debug_alternative;
@@ -227,6 +231,15 @@ void __init arch_init_ideal_nops(void)
 #endif
                }
                break;
+
+       case X86_VENDOR_AMD:
+               if (boot_cpu_data.x86 > 0xf) {
+                       ideal_nops = p6_nops;
+                       return;
+               }
+
+               /* fall through */
+
        default:
 #ifdef CONFIG_X86_64
                ideal_nops = k8_nops;
@@ -627,6 +640,7 @@ void __init alternative_instructions(void)
        apply_paravirt(__parainstructions, __parainstructions_end);
 
        restart_nmi();
+       alternatives_patched = 1;
 }
 
 /**
index 5caed1dd7ccf89e6595fc4a7db7d1d5b7f92f0a6..29fa475ec51823e61a9e94f34142e37341a1208b 100644 (file)
@@ -89,9 +89,7 @@ int amd_cache_northbridges(void)
                        next_northbridge(link, amd_nb_link_ids);
        }
 
-       /* GART present only on Fam15h upto model 0fh */
-       if (boot_cpu_data.x86 == 0xf || boot_cpu_data.x86 == 0x10 ||
-           (boot_cpu_data.x86 == 0x15 && boot_cpu_data.x86_model < 0x10))
+       if (amd_gart_present())
                amd_northbridges.flags |= AMD_NB_GART;
 
        /*
index 6a7c23ff21d3de8ccc906b41bceec57caaea414e..ede92c3364d3277fc3ea378695030eea47ca9fc8 100644 (file)
@@ -171,10 +171,6 @@ static int __init apbt_clockevent_register(void)
 
 static void apbt_setup_irq(struct apbt_dev *adev)
 {
-       /* timer0 irq has been setup early */
-       if (adev->irq == 0)
-               return;
-
        irq_modify_status(adev->irq, 0, IRQ_MOVE_PCNTXT);
        irq_set_affinity(adev->irq, cpumask_of(adev->cpu));
 }
index 76164e173a24f7251e3e684e66aa8afd7792f1e1..6e85f713641dda77bd2d9dfeb99fa10cdbe0cb0d 100644 (file)
@@ -262,6 +262,9 @@ void __init early_gart_iommu_check(void)
        u64 aper_base = 0, last_aper_base = 0;
        int aper_enabled = 0, last_aper_enabled = 0, last_valid = 0;
 
+       if (!amd_gart_present())
+               return;
+
        if (!early_pci_allowed())
                return;
 
@@ -355,6 +358,9 @@ int __init gart_iommu_hole_init(void)
        int fix, slot, valid_agp = 0;
        int i, node;
 
+       if (!amd_gart_present())
+               return -ENODEV;
+
        if (gart_iommu_aperture_disabled || !fix_aperture ||
            !early_pci_allowed())
                return -ENODEV;
@@ -452,7 +458,7 @@ out:
                   force_iommu ||
                   valid_agp ||
                   fallback_aper_force) {
-               pr_info("Your BIOS doesn't leave a aperture memory hole\n");
+               pr_info("Your BIOS doesn't leave an aperture memory hole\n");
                pr_info("Please enable the IOMMU option in the BIOS setup\n");
                pr_info("This costs you %dMB of RAM\n",
                        32 << fallback_aper_order);
index 816f36e979ad03c6b052b1ec5c1ca34b6dc566e4..ae50d3454d7874e98f1e71e3402fb786dacb67c7 100644 (file)
@@ -3,6 +3,8 @@
  *
  * Copyright (C) 1997, 1998, 1999, 2000, 2009 Ingo Molnar, Hajnalka Szabo
  *     Moved from arch/x86/kernel/apic/io_apic.c.
+ * Jiang Liu <jiang.liu@linux.intel.com>
+ *     Add support of hierarchical irqdomain
  *
  * 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
 #include <linux/device.h>
 #include <linux/pci.h>
 #include <linux/htirq.h>
+#include <asm/irqdomain.h>
 #include <asm/hw_irq.h>
 #include <asm/apic.h>
 #include <asm/hypertransport.h>
 
+static struct irq_domain *htirq_domain;
+
 /*
  * Hypertransport interrupt support
  */
-static void target_ht_irq(unsigned int irq, unsigned int dest, u8 vector)
-{
-       struct ht_irq_msg msg;
-
-       fetch_ht_irq_msg(irq, &msg);
-
-       msg.address_lo &= ~(HT_IRQ_LOW_VECTOR_MASK | HT_IRQ_LOW_DEST_ID_MASK);
-       msg.address_hi &= ~(HT_IRQ_HIGH_DEST_ID_MASK);
-
-       msg.address_lo |= HT_IRQ_LOW_VECTOR(vector) | HT_IRQ_LOW_DEST_ID(dest);
-       msg.address_hi |= HT_IRQ_HIGH_DEST_ID(dest);
-
-       write_ht_irq_msg(irq, &msg);
-}
-
 static int
 ht_set_affinity(struct irq_data *data, const struct cpumask *mask, bool force)
 {
-       struct irq_cfg *cfg = irqd_cfg(data);
-       unsigned int dest;
+       struct irq_data *parent = data->parent_data;
        int ret;
 
-       ret = apic_set_affinity(data, mask, &dest);
-       if (ret)
-               return ret;
-
-       target_ht_irq(data->irq, dest, cfg->vector);
-       return IRQ_SET_MASK_OK_NOCOPY;
+       ret = parent->chip->irq_set_affinity(parent, mask, force);
+       if (ret >= 0) {
+               struct ht_irq_msg msg;
+               struct irq_cfg *cfg = irqd_cfg(data);
+
+               fetch_ht_irq_msg(data->irq, &msg);
+               msg.address_lo &= ~(HT_IRQ_LOW_VECTOR_MASK |
+                                   HT_IRQ_LOW_DEST_ID_MASK);
+               msg.address_lo |= HT_IRQ_LOW_VECTOR(cfg->vector) |
+                                 HT_IRQ_LOW_DEST_ID(cfg->dest_apicid);
+               msg.address_hi &= ~(HT_IRQ_HIGH_DEST_ID_MASK);
+               msg.address_hi |= HT_IRQ_HIGH_DEST_ID(cfg->dest_apicid);
+               write_ht_irq_msg(data->irq, &msg);
+       }
+
+       return ret;
 }
 
 static struct irq_chip ht_irq_chip = {
        .name                   = "PCI-HT",
        .irq_mask               = mask_ht_irq,
        .irq_unmask             = unmask_ht_irq,
-       .irq_ack                = apic_ack_edge,
+       .irq_ack                = irq_chip_ack_parent,
        .irq_set_affinity       = ht_set_affinity,
-       .irq_retrigger          = apic_retrigger_irq,
+       .irq_retrigger          = irq_chip_retrigger_hierarchy,
        .flags                  = IRQCHIP_SKIP_SET_WAKE,
 };
 
-int arch_setup_ht_irq(unsigned int irq, struct pci_dev *dev)
+static int htirq_domain_alloc(struct irq_domain *domain, unsigned int virq,
+                             unsigned int nr_irqs, void *arg)
 {
-       struct irq_cfg *cfg;
-       struct ht_irq_msg msg;
-       unsigned dest;
-       int err;
+       struct ht_irq_cfg *ht_cfg;
+       struct irq_alloc_info *info = arg;
+       struct pci_dev *dev;
+       irq_hw_number_t hwirq;
+       int ret;
 
-       if (disable_apic)
-               return -ENXIO;
+       if (nr_irqs > 1 || !info)
+               return -EINVAL;
 
-       cfg = irq_cfg(irq);
-       err = assign_irq_vector(irq, cfg, apic->target_cpus());
-       if (err)
-               return err;
+       dev = info->ht_dev;
+       hwirq = (info->ht_idx & 0xFF) |
+               PCI_DEVID(dev->bus->number, dev->devfn) << 8 |
+               (pci_domain_nr(dev->bus) & 0xFFFFFFFF) << 24;
+       if (irq_find_mapping(domain, hwirq) > 0)
+               return -EEXIST;
 
-       err = apic->cpu_mask_to_apicid_and(cfg->domain,
-                                          apic->target_cpus(), &dest);
-       if (err)
-               return err;
+       ht_cfg = kmalloc(sizeof(*ht_cfg), GFP_KERNEL);
+       if (!ht_cfg)
+               return -ENOMEM;
 
-       msg.address_hi = HT_IRQ_HIGH_DEST_ID(dest);
+       ret = irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, info);
+       if (ret < 0) {
+               kfree(ht_cfg);
+               return ret;
+       }
+
+       /* Initialize msg to a value that will never match the first write. */
+       ht_cfg->msg.address_lo = 0xffffffff;
+       ht_cfg->msg.address_hi = 0xffffffff;
+       ht_cfg->dev = info->ht_dev;
+       ht_cfg->update = info->ht_update;
+       ht_cfg->pos = info->ht_pos;
+       ht_cfg->idx = 0x10 + (info->ht_idx * 2);
+       irq_domain_set_info(domain, virq, hwirq, &ht_irq_chip, ht_cfg,
+                           handle_edge_irq, ht_cfg, "edge");
+
+       return 0;
+}
+
+static void htirq_domain_free(struct irq_domain *domain, unsigned int virq,
+                             unsigned int nr_irqs)
+{
+       struct irq_data *irq_data = irq_domain_get_irq_data(domain, virq);
+
+       BUG_ON(nr_irqs != 1);
+       kfree(irq_data->chip_data);
+       irq_domain_free_irqs_top(domain, virq, nr_irqs);
+}
 
+static void htirq_domain_activate(struct irq_domain *domain,
+                                 struct irq_data *irq_data)
+{
+       struct ht_irq_msg msg;
+       struct irq_cfg *cfg = irqd_cfg(irq_data);
+
+       msg.address_hi = HT_IRQ_HIGH_DEST_ID(cfg->dest_apicid);
        msg.address_lo =
                HT_IRQ_LOW_BASE |
-               HT_IRQ_LOW_DEST_ID(dest) |
+               HT_IRQ_LOW_DEST_ID(cfg->dest_apicid) |
                HT_IRQ_LOW_VECTOR(cfg->vector) |
                ((apic->irq_dest_mode == 0) ?
                        HT_IRQ_LOW_DM_PHYSICAL :
@@ -95,13 +131,56 @@ int arch_setup_ht_irq(unsigned int irq, struct pci_dev *dev)
                        HT_IRQ_LOW_MT_FIXED :
                        HT_IRQ_LOW_MT_ARBITRATED) |
                HT_IRQ_LOW_IRQ_MASKED;
+       write_ht_irq_msg(irq_data->irq, &msg);
+}
 
-       write_ht_irq_msg(irq, &msg);
+static void htirq_domain_deactivate(struct irq_domain *domain,
+                                   struct irq_data *irq_data)
+{
+       struct ht_irq_msg msg;
 
-       irq_set_chip_and_handler_name(irq, &ht_irq_chip,
-                                     handle_edge_irq, "edge");
+       memset(&msg, 0, sizeof(msg));
+       write_ht_irq_msg(irq_data->irq, &msg);
+}
 
-       dev_dbg(&dev->dev, "irq %d for HT\n", irq);
+static const struct irq_domain_ops htirq_domain_ops = {
+       .alloc          = htirq_domain_alloc,
+       .free           = htirq_domain_free,
+       .activate       = htirq_domain_activate,
+       .deactivate     = htirq_domain_deactivate,
+};
 
-       return 0;
+void arch_init_htirq_domain(struct irq_domain *parent)
+{
+       if (disable_apic)
+               return;
+
+       htirq_domain = irq_domain_add_tree(NULL, &htirq_domain_ops, NULL);
+       if (!htirq_domain)
+               pr_warn("failed to initialize irqdomain for HTIRQ.\n");
+       else
+               htirq_domain->parent = parent;
+}
+
+int arch_setup_ht_irq(int idx, int pos, struct pci_dev *dev,
+                     ht_irq_update_t *update)
+{
+       struct irq_alloc_info info;
+
+       if (!htirq_domain)
+               return -ENOSYS;
+
+       init_irq_alloc_info(&info, NULL);
+       info.ht_idx = idx;
+       info.ht_pos = pos;
+       info.ht_dev = dev;
+       info.ht_update = update;
+
+       return irq_domain_alloc_irqs(htirq_domain, 1, dev_to_node(&dev->dev),
+                                    &info);
+}
+
+void arch_teardown_ht_irq(unsigned int irq)
+{
+       irq_domain_free_irqs(irq, 1);
 }
index f4dc2462a1ac410803cd94ff4944ebf23c636fa9..845dc0df2002472275a39e421502cdb0768c54a1 100644 (file)
  *                                     and Rolf G. Tews
  *                                     for testing these extensively
  *     Paul Diefenbaugh        :       Added full ACPI support
+ *
+ * Historical information which is worth to be preserved:
+ *
+ * - SiS APIC rmw bug:
+ *
+ *     We used to have a workaround for a bug in SiS chips which
+ *     required to rewrite the index register for a read-modify-write
+ *     operation as the chip lost the index information which was
+ *     setup for the read already. We cache the data now, so that
+ *     workaround has been removed.
  */
 
 #include <linux/mm.h>
 #include <linux/acpi.h>
 #include <linux/module.h>
 #include <linux/syscore_ops.h>
-#include <linux/irqdomain.h>
 #include <linux/freezer.h>
 #include <linux/kthread.h>
 #include <linux/jiffies.h>     /* time_after() */
 #include <linux/slab.h>
 #include <linux/bootmem.h>
 
+#include <asm/irqdomain.h>
 #include <asm/idle.h>
 #include <asm/io.h>
 #include <asm/smp.h>
 #define        for_each_ioapic_pin(idx, pin)   \
        for_each_ioapic((idx))          \
                for_each_pin((idx), (pin))
-
 #define for_each_irq_pin(entry, head) \
        list_for_each_entry(entry, &head, list)
 
-/*
- *      Is the SiS APIC rmw bug present ?
- *      -1 = don't know, 0 = no, 1 = yes
- */
-int sis_apic_bug = -1;
-
 static DEFINE_RAW_SPINLOCK(ioapic_lock);
 static DEFINE_MUTEX(ioapic_mutex);
 static unsigned int ioapic_dynirq_base;
 static int ioapic_initialized;
 
-struct mp_pin_info {
+struct irq_pin_list {
+       struct list_head list;
+       int apic, pin;
+};
+
+struct mp_chip_data {
+       struct list_head irq_2_pin;
+       struct IO_APIC_route_entry entry;
        int trigger;
        int polarity;
-       int node;
-       int set;
        u32 count;
+       bool isa_irq;
+};
+
+struct mp_ioapic_gsi {
+       u32 gsi_base;
+       u32 gsi_end;
 };
 
 static struct ioapic {
@@ -101,7 +115,6 @@ static struct ioapic {
        struct mp_ioapic_gsi  gsi_config;
        struct ioapic_domain_cfg irqdomain_cfg;
        struct irq_domain *irqdomain;
-       struct mp_pin_info *pin_info;
        struct resource *iomem_res;
 } ioapics[MAX_IO_APICS];
 
@@ -117,7 +130,7 @@ unsigned int mpc_ioapic_addr(int ioapic_idx)
        return ioapics[ioapic_idx].mp_config.apicaddr;
 }
 
-struct mp_ioapic_gsi *mp_ioapic_gsi_routing(int ioapic_idx)
+static inline struct mp_ioapic_gsi *mp_ioapic_gsi_routing(int ioapic_idx)
 {
        return &ioapics[ioapic_idx].gsi_config;
 }
@@ -129,11 +142,16 @@ static inline int mp_ioapic_pin_count(int ioapic)
        return gsi_cfg->gsi_end - gsi_cfg->gsi_base + 1;
 }
 
-u32 mp_pin_to_gsi(int ioapic, int pin)
+static inline u32 mp_pin_to_gsi(int ioapic, int pin)
 {
        return mp_ioapic_gsi_routing(ioapic)->gsi_base + pin;
 }
 
+static inline bool mp_is_legacy_irq(int irq)
+{
+       return irq >= 0 && irq < nr_legacy_irqs();
+}
+
 /*
  * Initialize all legacy IRQs and all pins on the first IOAPIC
  * if we have legacy interrupt controller. Kernel boot option "pirq="
@@ -144,12 +162,7 @@ static inline int mp_init_irq_at_boot(int ioapic, int irq)
        if (!nr_legacy_irqs())
                return 0;
 
-       return ioapic == 0 || (irq >= 0 && irq < nr_legacy_irqs());
-}
-
-static inline struct mp_pin_info *mp_pin_info(int ioapic_idx, int pin)
-{
-       return ioapics[ioapic_idx].pin_info + pin;
+       return ioapic == 0 || mp_is_legacy_irq(irq);
 }
 
 static inline struct irq_domain *mp_ioapic_irqdomain(int ioapic)
@@ -216,16 +229,6 @@ void mp_save_irq(struct mpc_intsrc *m)
                panic("Max # of irq sources exceeded!!\n");
 }
 
-struct irq_pin_list {
-       struct list_head list;
-       int apic, pin;
-};
-
-static struct irq_pin_list *alloc_irq_pin_list(int node)
-{
-       return kzalloc_node(sizeof(struct irq_pin_list), GFP_KERNEL, node);
-}
-
 static void alloc_ioapic_saved_registers(int idx)
 {
        size_t size;
@@ -247,8 +250,7 @@ static void free_ioapic_saved_registers(int idx)
 
 int __init arch_early_ioapic_init(void)
 {
-       struct irq_cfg *cfg;
-       int i, node = cpu_to_node(0);
+       int i;
 
        if (!nr_legacy_irqs())
                io_apic_irqs = ~0UL;
@@ -256,16 +258,6 @@ int __init arch_early_ioapic_init(void)
        for_each_ioapic(i)
                alloc_ioapic_saved_registers(i);
 
-       /*
-        * For legacy IRQ's, start with assigning irq0 to irq15 to
-        * IRQ0_VECTOR to IRQ15_VECTOR for all cpu's.
-        */
-       for (i = 0; i < nr_legacy_irqs(); i++) {
-               cfg = alloc_irq_and_cfg_at(i, node);
-               cfg->vector = IRQ0_VECTOR + i;
-               cpumask_setall(cfg->domain);
-       }
-
        return 0;
 }
 
@@ -283,7 +275,7 @@ static __attribute_const__ struct io_apic __iomem *io_apic_base(int idx)
                + (mpc_ioapic_addr(idx) & ~PAGE_MASK);
 }
 
-void io_apic_eoi(unsigned int apic, unsigned int vector)
+static inline void io_apic_eoi(unsigned int apic, unsigned int vector)
 {
        struct io_apic __iomem *io_apic = io_apic_base(apic);
        writel(vector, &io_apic->eoi);
@@ -296,7 +288,8 @@ unsigned int native_io_apic_read(unsigned int apic, unsigned int reg)
        return readl(&io_apic->data);
 }
 
-void native_io_apic_write(unsigned int apic, unsigned int reg, unsigned int value)
+static void io_apic_write(unsigned int apic, unsigned int reg,
+                         unsigned int value)
 {
        struct io_apic __iomem *io_apic = io_apic_base(apic);
 
@@ -304,21 +297,6 @@ void native_io_apic_write(unsigned int apic, unsigned int reg, unsigned int valu
        writel(value, &io_apic->data);
 }
 
-/*
- * Re-write a value: to be used for read-modify-write
- * cycles where the read already set up the index register.
- *
- * Older SiS APIC requires we rewrite the index register
- */
-void native_io_apic_modify(unsigned int apic, unsigned int reg, unsigned int value)
-{
-       struct io_apic __iomem *io_apic = io_apic_base(apic);
-
-       if (sis_apic_bug)
-               writel(reg, &io_apic->index);
-       writel(value, &io_apic->data);
-}
-
 union entry_union {
        struct { u32 w1, w2; };
        struct IO_APIC_route_entry entry;
@@ -378,7 +356,7 @@ static void ioapic_write_entry(int apic, int pin, struct IO_APIC_route_entry e)
 static void ioapic_mask_entry(int apic, int pin)
 {
        unsigned long flags;
-       union entry_union eu = { .entry.mask = 1 };
+       union entry_union eu = { .entry.mask = IOAPIC_MASKED };
 
        raw_spin_lock_irqsave(&ioapic_lock, flags);
        io_apic_write(apic, 0x10 + 2*pin, eu.w1);
@@ -391,16 +369,17 @@ static void ioapic_mask_entry(int apic, int pin)
  * shared ISA-space IRQs, so we have to support them. We are super
  * fast in the common case, and fast for shared ISA-space IRQs.
  */
-static int __add_pin_to_irq_node(struct irq_cfg *cfg, int node, int apic, int pin)
+static int __add_pin_to_irq_node(struct mp_chip_data *data,
+                                int node, int apic, int pin)
 {
        struct irq_pin_list *entry;
 
        /* don't allow duplicates */
-       for_each_irq_pin(entry, cfg->irq_2_pin)
+       for_each_irq_pin(entry, data->irq_2_pin)
                if (entry->apic == apic && entry->pin == pin)
                        return 0;
 
-       entry = alloc_irq_pin_list(node);
+       entry = kzalloc_node(sizeof(struct irq_pin_list), GFP_ATOMIC, node);
        if (!entry) {
                pr_err("can not alloc irq_pin_list (%d,%d,%d)\n",
                       node, apic, pin);
@@ -408,16 +387,16 @@ static int __add_pin_to_irq_node(struct irq_cfg *cfg, int node, int apic, int pi
        }
        entry->apic = apic;
        entry->pin = pin;
+       list_add_tail(&entry->list, &data->irq_2_pin);
 
-       list_add_tail(&entry->list, &cfg->irq_2_pin);
        return 0;
 }
 
-static void __remove_pin_from_irq(struct irq_cfg *cfg, int apic, int pin)
+static void __remove_pin_from_irq(struct mp_chip_data *data, int apic, int pin)
 {
        struct irq_pin_list *tmp, *entry;
 
-       list_for_each_entry_safe(entry, tmp, &cfg->irq_2_pin, list)
+       list_for_each_entry_safe(entry, tmp, &data->irq_2_pin, list)
                if (entry->apic == apic && entry->pin == pin) {
                        list_del(&entry->list);
                        kfree(entry);
@@ -425,22 +404,23 @@ static void __remove_pin_from_irq(struct irq_cfg *cfg, int apic, int pin)
                }
 }
 
-static void add_pin_to_irq_node(struct irq_cfg *cfg, int node, int apic, int pin)
+static void add_pin_to_irq_node(struct mp_chip_data *data,
+                               int node, int apic, int pin)
 {
-       if (__add_pin_to_irq_node(cfg, node, apic, pin))
+       if (__add_pin_to_irq_node(data, node, apic, pin))
                panic("IO-APIC: failed to add irq-pin. Can not proceed\n");
 }
 
 /*
  * Reroute an IRQ to a different pin.
  */
-static void __init replace_pin_at_irq_node(struct irq_cfg *cfg, int node,
+static void __init replace_pin_at_irq_node(struct mp_chip_data *data, int node,
                                           int oldapic, int oldpin,
                                           int newapic, int newpin)
 {
        struct irq_pin_list *entry;
 
-       for_each_irq_pin(entry, cfg->irq_2_pin) {
+       for_each_irq_pin(entry, data->irq_2_pin) {
                if (entry->apic == oldapic && entry->pin == oldpin) {
                        entry->apic = newapic;
                        entry->pin = newpin;
@@ -450,32 +430,26 @@ static void __init replace_pin_at_irq_node(struct irq_cfg *cfg, int node,
        }
 
        /* old apic/pin didn't exist, so just add new ones */
-       add_pin_to_irq_node(cfg, node, newapic, newpin);
-}
-
-static void __io_apic_modify_irq(struct irq_pin_list *entry,
-                                int mask_and, int mask_or,
-                                void (*final)(struct irq_pin_list *entry))
-{
-       unsigned int reg, pin;
-
-       pin = entry->pin;
-       reg = io_apic_read(entry->apic, 0x10 + pin * 2);
-       reg &= mask_and;
-       reg |= mask_or;
-       io_apic_modify(entry->apic, 0x10 + pin * 2, reg);
-       if (final)
-               final(entry);
+       add_pin_to_irq_node(data, node, newapic, newpin);
 }
 
-static void io_apic_modify_irq(struct irq_cfg *cfg,
+static void io_apic_modify_irq(struct mp_chip_data *data,
                               int mask_and, int mask_or,
                               void (*final)(struct irq_pin_list *entry))
 {
+       union entry_union eu;
        struct irq_pin_list *entry;
 
-       for_each_irq_pin(entry, cfg->irq_2_pin)
-               __io_apic_modify_irq(entry, mask_and, mask_or, final);
+       eu.entry = data->entry;
+       eu.w1 &= mask_and;
+       eu.w1 |= mask_or;
+       data->entry = eu.entry;
+
+       for_each_irq_pin(entry, data->irq_2_pin) {
+               io_apic_write(entry->apic, 0x10 + 2 * entry->pin, eu.w1);
+               if (final)
+                       final(entry);
+       }
 }
 
 static void io_apic_sync(struct irq_pin_list *entry)
@@ -490,39 +464,31 @@ static void io_apic_sync(struct irq_pin_list *entry)
        readl(&io_apic->data);
 }
 
-static void mask_ioapic(struct irq_cfg *cfg)
+static void mask_ioapic_irq(struct irq_data *irq_data)
 {
+       struct mp_chip_data *data = irq_data->chip_data;
        unsigned long flags;
 
        raw_spin_lock_irqsave(&ioapic_lock, flags);
-       io_apic_modify_irq(cfg, ~0, IO_APIC_REDIR_MASKED, &io_apic_sync);
+       io_apic_modify_irq(data, ~0, IO_APIC_REDIR_MASKED, &io_apic_sync);
        raw_spin_unlock_irqrestore(&ioapic_lock, flags);
 }
 
-static void mask_ioapic_irq(struct irq_data *data)
+static void __unmask_ioapic(struct mp_chip_data *data)
 {
-       mask_ioapic(irqd_cfg(data));
+       io_apic_modify_irq(data, ~IO_APIC_REDIR_MASKED, 0, NULL);
 }
 
-static void __unmask_ioapic(struct irq_cfg *cfg)
-{
-       io_apic_modify_irq(cfg, ~IO_APIC_REDIR_MASKED, 0, NULL);
-}
-
-static void unmask_ioapic(struct irq_cfg *cfg)
+static void unmask_ioapic_irq(struct irq_data *irq_data)
 {
+       struct mp_chip_data *data = irq_data->chip_data;
        unsigned long flags;
 
        raw_spin_lock_irqsave(&ioapic_lock, flags);
-       __unmask_ioapic(cfg);
+       __unmask_ioapic(data);
        raw_spin_unlock_irqrestore(&ioapic_lock, flags);
 }
 
-static void unmask_ioapic_irq(struct irq_data *data)
-{
-       unmask_ioapic(irqd_cfg(data));
-}
-
 /*
  * IO-APIC versions below 0x20 don't support EOI register.
  * For the record, here is the information about various versions:
@@ -539,7 +505,7 @@ static void unmask_ioapic_irq(struct irq_data *data)
  * Otherwise, we simulate the EOI message manually by changing the trigger
  * mode to edge and then back to level, with RTE being masked during this.
  */
-void native_eoi_ioapic_pin(int apic, int pin, int vector)
+static void __eoi_ioapic_pin(int apic, int pin, int vector)
 {
        if (mpc_ioapic_ver(apic) >= 0x20) {
                io_apic_eoi(apic, vector);
@@ -551,7 +517,7 @@ void native_eoi_ioapic_pin(int apic, int pin, int vector)
                /*
                 * Mask the entry and change the trigger mode to edge.
                 */
-               entry1.mask = 1;
+               entry1.mask = IOAPIC_MASKED;
                entry1.trigger = IOAPIC_EDGE;
 
                __ioapic_write_entry(apic, pin, entry1);
@@ -563,15 +529,14 @@ void native_eoi_ioapic_pin(int apic, int pin, int vector)
        }
 }
 
-void eoi_ioapic_irq(unsigned int irq, struct irq_cfg *cfg)
+void eoi_ioapic_pin(int vector, struct mp_chip_data *data)
 {
-       struct irq_pin_list *entry;
        unsigned long flags;
+       struct irq_pin_list *entry;
 
        raw_spin_lock_irqsave(&ioapic_lock, flags);
-       for_each_irq_pin(entry, cfg->irq_2_pin)
-               x86_io_apic_ops.eoi_ioapic_pin(entry->apic, entry->pin,
-                                              cfg->vector);
+       for_each_irq_pin(entry, data->irq_2_pin)
+               __eoi_ioapic_pin(entry->apic, entry->pin, vector);
        raw_spin_unlock_irqrestore(&ioapic_lock, flags);
 }
 
@@ -588,8 +553,8 @@ static void clear_IO_APIC_pin(unsigned int apic, unsigned int pin)
         * Make sure the entry is masked and re-read the contents to check
         * if it is a level triggered pin and if the remote-IRR is set.
         */
-       if (!entry.mask) {
-               entry.mask = 1;
+       if (entry.mask == IOAPIC_UNMASKED) {
+               entry.mask = IOAPIC_MASKED;
                ioapic_write_entry(apic, pin, entry);
                entry = ioapic_read_entry(apic, pin);
        }
@@ -602,13 +567,12 @@ static void clear_IO_APIC_pin(unsigned int apic, unsigned int pin)
                 * doesn't clear the remote-IRR if the trigger mode is not
                 * set to level.
                 */
-               if (!entry.trigger) {
+               if (entry.trigger == IOAPIC_EDGE) {
                        entry.trigger = IOAPIC_LEVEL;
                        ioapic_write_entry(apic, pin, entry);
                }
-
                raw_spin_lock_irqsave(&ioapic_lock, flags);
-               x86_io_apic_ops.eoi_ioapic_pin(apic, pin, entry.vector);
+               __eoi_ioapic_pin(apic, pin, entry.vector);
                raw_spin_unlock_irqrestore(&ioapic_lock, flags);
        }
 
@@ -706,8 +670,8 @@ void mask_ioapic_entries(void)
                        struct IO_APIC_route_entry entry;
 
                        entry = ioapics[apic].saved_registers[pin];
-                       if (!entry.mask) {
-                               entry.mask = 1;
+                       if (entry.mask == IOAPIC_UNMASKED) {
+                               entry.mask = IOAPIC_MASKED;
                                ioapic_write_entry(apic, pin, entry);
                        }
                }
@@ -809,11 +773,11 @@ static int EISA_ELCR(unsigned int irq)
 
 #endif
 
-/* ISA interrupts are always polarity zero edge triggered,
+/* ISA interrupts are always active high edge triggered,
  * when listed as conforming in the MP table. */
 
-#define default_ISA_trigger(idx)       (0)
-#define default_ISA_polarity(idx)      (0)
+#define default_ISA_trigger(idx)       (IOAPIC_EDGE)
+#define default_ISA_polarity(idx)      (IOAPIC_POL_HIGH)
 
 /* EISA interrupts are always polarity zero and can be edge or level
  * trigger depending on the ELCR value.  If an interrupt is listed as
@@ -823,53 +787,55 @@ static int EISA_ELCR(unsigned int irq)
 #define default_EISA_trigger(idx)      (EISA_ELCR(mp_irqs[idx].srcbusirq))
 #define default_EISA_polarity(idx)     default_ISA_polarity(idx)
 
-/* PCI interrupts are always polarity one level triggered,
+/* PCI interrupts are always active low level triggered,
  * when listed as conforming in the MP table. */
 
-#define default_PCI_trigger(idx)       (1)
-#define default_PCI_polarity(idx)      (1)
+#define default_PCI_trigger(idx)       (IOAPIC_LEVEL)
+#define default_PCI_polarity(idx)      (IOAPIC_POL_LOW)
 
 static int irq_polarity(int idx)
 {
        int bus = mp_irqs[idx].srcbus;
-       int polarity;
 
        /*
         * Determine IRQ line polarity (high active or low active):
         */
-       switch (mp_irqs[idx].irqflag & 3)
-       {
-               case 0: /* conforms, ie. bus-type dependent polarity */
-                       if (test_bit(bus, mp_bus_not_pci))
-                               polarity = default_ISA_polarity(idx);
-                       else
-                               polarity = default_PCI_polarity(idx);
-                       break;
-               case 1: /* high active */
-               {
-                       polarity = 0;
-                       break;
-               }
-               case 2: /* reserved */
-               {
-                       pr_warn("broken BIOS!!\n");
-                       polarity = 1;
-                       break;
-               }
-               case 3: /* low active */
-               {
-                       polarity = 1;
-                       break;
-               }
-               default: /* invalid */
-               {
-                       pr_warn("broken BIOS!!\n");
-                       polarity = 1;
-                       break;
-               }
+       switch (mp_irqs[idx].irqflag & 0x03) {
+       case 0:
+               /* conforms to spec, ie. bus-type dependent polarity */
+               if (test_bit(bus, mp_bus_not_pci))
+                       return default_ISA_polarity(idx);
+               else
+                       return default_PCI_polarity(idx);
+       case 1:
+               return IOAPIC_POL_HIGH;
+       case 2:
+               pr_warn("IOAPIC: Invalid polarity: 2, defaulting to low\n");
+       case 3:
+       default: /* Pointless default required due to do gcc stupidity */
+               return IOAPIC_POL_LOW;
+       }
+}
+
+#ifdef CONFIG_EISA
+static int eisa_irq_trigger(int idx, int bus, int trigger)
+{
+       switch (mp_bus_id_to_type[bus]) {
+       case MP_BUS_PCI:
+       case MP_BUS_ISA:
+               return trigger;
+       case MP_BUS_EISA:
+               return default_EISA_trigger(idx);
        }
-       return polarity;
+       pr_warn("IOAPIC: Invalid srcbus: %d defaulting to level\n", bus);
+       return IOAPIC_LEVEL;
 }
+#else
+static inline int eisa_irq_trigger(int idx, int bus, int trigger)
+{
+       return trigger;
+}
+#endif
 
 static int irq_trigger(int idx)
 {
@@ -879,153 +845,227 @@ static int irq_trigger(int idx)
        /*
         * Determine IRQ trigger mode (edge or level sensitive):
         */
-       switch ((mp_irqs[idx].irqflag>>2) & 3)
-       {
-               case 0: /* conforms, ie. bus-type dependent */
-                       if (test_bit(bus, mp_bus_not_pci))
-                               trigger = default_ISA_trigger(idx);
-                       else
-                               trigger = default_PCI_trigger(idx);
-#ifdef CONFIG_EISA
-                       switch (mp_bus_id_to_type[bus]) {
-                               case MP_BUS_ISA: /* ISA pin */
-                               {
-                                       /* set before the switch */
-                                       break;
-                               }
-                               case MP_BUS_EISA: /* EISA pin */
-                               {
-                                       trigger = default_EISA_trigger(idx);
-                                       break;
-                               }
-                               case MP_BUS_PCI: /* PCI pin */
-                               {
-                                       /* set before the switch */
-                                       break;
-                               }
-                               default:
-                               {
-                                       pr_warn("broken BIOS!!\n");
-                                       trigger = 1;
-                                       break;
-                               }
-                       }
+       switch ((mp_irqs[idx].irqflag >> 2) & 0x03) {
+       case 0:
+               /* conforms to spec, ie. bus-type dependent trigger mode */
+               if (test_bit(bus, mp_bus_not_pci))
+                       trigger = default_ISA_trigger(idx);
+               else
+                       trigger = default_PCI_trigger(idx);
+               /* Take EISA into account */
+               return eisa_irq_trigger(idx, bus, trigger);
+       case 1:
+               return IOAPIC_EDGE;
+       case 2:
+               pr_warn("IOAPIC: Invalid trigger mode 2 defaulting to level\n");
+       case 3:
+       default: /* Pointless default required due to do gcc stupidity */
+               return IOAPIC_LEVEL;
+       }
+}
+
+void ioapic_set_alloc_attr(struct irq_alloc_info *info, int node,
+                          int trigger, int polarity)
+{
+       init_irq_alloc_info(info, NULL);
+       info->type = X86_IRQ_ALLOC_TYPE_IOAPIC;
+       info->ioapic_node = node;
+       info->ioapic_trigger = trigger;
+       info->ioapic_polarity = polarity;
+       info->ioapic_valid = 1;
+}
+
+#ifndef CONFIG_ACPI
+int acpi_get_override_irq(u32 gsi, int *trigger, int *polarity);
 #endif
-                       break;
-               case 1: /* edge */
-               {
-                       trigger = 0;
-                       break;
-               }
-               case 2: /* reserved */
-               {
-                       pr_warn("broken BIOS!!\n");
-                       trigger = 1;
-                       break;
-               }
-               case 3: /* level */
-               {
-                       trigger = 1;
-                       break;
-               }
-               default: /* invalid */
-               {
-                       pr_warn("broken BIOS!!\n");
-                       trigger = 0;
-                       break;
+
+static void ioapic_copy_alloc_attr(struct irq_alloc_info *dst,
+                                  struct irq_alloc_info *src,
+                                  u32 gsi, int ioapic_idx, int pin)
+{
+       int trigger, polarity;
+
+       copy_irq_alloc_info(dst, src);
+       dst->type = X86_IRQ_ALLOC_TYPE_IOAPIC;
+       dst->ioapic_id = mpc_ioapic_id(ioapic_idx);
+       dst->ioapic_pin = pin;
+       dst->ioapic_valid = 1;
+       if (src && src->ioapic_valid) {
+               dst->ioapic_node = src->ioapic_node;
+               dst->ioapic_trigger = src->ioapic_trigger;
+               dst->ioapic_polarity = src->ioapic_polarity;
+       } else {
+               dst->ioapic_node = NUMA_NO_NODE;
+               if (acpi_get_override_irq(gsi, &trigger, &polarity) >= 0) {
+                       dst->ioapic_trigger = trigger;
+                       dst->ioapic_polarity = polarity;
+               } else {
+                       /*
+                        * PCI interrupts are always active low level
+                        * triggered.
+                        */
+                       dst->ioapic_trigger = IOAPIC_LEVEL;
+                       dst->ioapic_polarity = IOAPIC_POL_LOW;
                }
        }
-       return trigger;
 }
 
-static int alloc_irq_from_domain(struct irq_domain *domain, u32 gsi, int pin)
+static int ioapic_alloc_attr_node(struct irq_alloc_info *info)
+{
+       return (info && info->ioapic_valid) ? info->ioapic_node : NUMA_NO_NODE;
+}
+
+static void mp_register_handler(unsigned int irq, unsigned long trigger)
+{
+       irq_flow_handler_t hdl;
+       bool fasteoi;
+
+       if (trigger) {
+               irq_set_status_flags(irq, IRQ_LEVEL);
+               fasteoi = true;
+       } else {
+               irq_clear_status_flags(irq, IRQ_LEVEL);
+               fasteoi = false;
+       }
+
+       hdl = fasteoi ? handle_fasteoi_irq : handle_edge_irq;
+       __irq_set_handler(irq, hdl, 0, fasteoi ? "fasteoi" : "edge");
+}
+
+static bool mp_check_pin_attr(int irq, struct irq_alloc_info *info)
 {
+       struct mp_chip_data *data = irq_get_chip_data(irq);
+
+       /*
+        * setup_IO_APIC_irqs() programs all legacy IRQs with default trigger
+        * and polarity attirbutes. So allow the first user to reprogram the
+        * pin with real trigger and polarity attributes.
+        */
+       if (irq < nr_legacy_irqs() && data->count == 1) {
+               if (info->ioapic_trigger != data->trigger)
+                       mp_register_handler(irq, data->trigger);
+               data->entry.trigger = data->trigger = info->ioapic_trigger;
+               data->entry.polarity = data->polarity = info->ioapic_polarity;
+       }
+
+       return data->trigger == info->ioapic_trigger &&
+              data->polarity == info->ioapic_polarity;
+}
+
+static int alloc_irq_from_domain(struct irq_domain *domain, int ioapic, u32 gsi,
+                                struct irq_alloc_info *info)
+{
+       bool legacy = false;
        int irq = -1;
-       int ioapic = (int)(long)domain->host_data;
        int type = ioapics[ioapic].irqdomain_cfg.type;
 
        switch (type) {
        case IOAPIC_DOMAIN_LEGACY:
                /*
-                * Dynamically allocate IRQ number for non-ISA IRQs in the first 16
-                * GSIs on some weird platforms.
+                * Dynamically allocate IRQ number for non-ISA IRQs in the first
+                * 16 GSIs on some weird platforms.
                 */
-               if (gsi < nr_legacy_irqs())
-                       irq = irq_create_mapping(domain, pin);
-               else if (irq_create_strict_mappings(domain, gsi, pin, 1) == 0)
+               if (!ioapic_initialized || gsi >= nr_legacy_irqs())
                        irq = gsi;
+               legacy = mp_is_legacy_irq(irq);
                break;
        case IOAPIC_DOMAIN_STRICT:
-               if (irq_create_strict_mappings(domain, gsi, pin, 1) == 0)
-                       irq = gsi;
+               irq = gsi;
                break;
        case IOAPIC_DOMAIN_DYNAMIC:
-               irq = irq_create_mapping(domain, pin);
                break;
        default:
                WARN(1, "ioapic: unknown irqdomain type %d\n", type);
-               break;
+               return -1;
+       }
+
+       return __irq_domain_alloc_irqs(domain, irq, 1,
+                                      ioapic_alloc_attr_node(info),
+                                      info, legacy);
+}
+
+/*
+ * Need special handling for ISA IRQs because there may be multiple IOAPIC pins
+ * sharing the same ISA IRQ number and irqdomain only supports 1:1 mapping
+ * between IOAPIC pin and IRQ number. A typical IOAPIC has 24 pins, pin 0-15 are
+ * used for legacy IRQs and pin 16-23 are used for PCI IRQs (PIRQ A-H).
+ * When ACPI is disabled, only legacy IRQ numbers (IRQ0-15) are available, and
+ * some BIOSes may use MP Interrupt Source records to override IRQ numbers for
+ * PIRQs instead of reprogramming the interrupt routing logic. Thus there may be
+ * multiple pins sharing the same legacy IRQ number when ACPI is disabled.
+ */
+static int alloc_isa_irq_from_domain(struct irq_domain *domain,
+                                    int irq, int ioapic, int pin,
+                                    struct irq_alloc_info *info)
+{
+       struct mp_chip_data *data;
+       struct irq_data *irq_data = irq_get_irq_data(irq);
+       int node = ioapic_alloc_attr_node(info);
+
+       /*
+        * Legacy ISA IRQ has already been allocated, just add pin to
+        * the pin list assoicated with this IRQ and program the IOAPIC
+        * entry. The IOAPIC entry
+        */
+       if (irq_data && irq_data->parent_data) {
+               if (!mp_check_pin_attr(irq, info))
+                       return -EBUSY;
+               if (__add_pin_to_irq_node(irq_data->chip_data, node, ioapic,
+                                         info->ioapic_pin))
+                       return -ENOMEM;
+       } else {
+               irq = __irq_domain_alloc_irqs(domain, irq, 1, node, info, true);
+               if (irq >= 0) {
+                       irq_data = irq_domain_get_irq_data(domain, irq);
+                       data = irq_data->chip_data;
+                       data->isa_irq = true;
+               }
        }
 
-       return irq > 0 ? irq : -1;
+       return irq;
 }
 
 static int mp_map_pin_to_irq(u32 gsi, int idx, int ioapic, int pin,
-                            unsigned int flags)
+                            unsigned int flags, struct irq_alloc_info *info)
 {
        int irq;
+       bool legacy = false;
+       struct irq_alloc_info tmp;
+       struct mp_chip_data *data;
        struct irq_domain *domain = mp_ioapic_irqdomain(ioapic);
-       struct mp_pin_info *info = mp_pin_info(ioapic, pin);
 
        if (!domain)
-               return -1;
+               return -ENOSYS;
 
-       mutex_lock(&ioapic_mutex);
-
-       /*
-        * Don't use irqdomain to manage ISA IRQs because there may be
-        * multiple IOAPIC pins sharing the same ISA IRQ number and
-        * irqdomain only supports 1:1 mapping between IOAPIC pin and
-        * IRQ number. A typical IOAPIC has 24 pins, pin 0-15 are used
-        * for legacy IRQs and pin 16-23 are used for PCI IRQs (PIRQ A-H).
-        * When ACPI is disabled, only legacy IRQ numbers (IRQ0-15) are
-        * available, and some BIOSes may use MP Interrupt Source records
-        * to override IRQ numbers for PIRQs instead of reprogramming
-        * the interrupt routing logic. Thus there may be multiple pins
-        * sharing the same legacy IRQ number when ACPI is disabled.
-        */
        if (idx >= 0 && test_bit(mp_irqs[idx].srcbus, mp_bus_not_pci)) {
                irq = mp_irqs[idx].srcbusirq;
-               if (flags & IOAPIC_MAP_ALLOC) {
-                       if (info->count == 0 &&
-                           mp_irqdomain_map(domain, irq, pin) != 0)
-                               irq = -1;
+               legacy = mp_is_legacy_irq(irq);
+       }
 
-                       /* special handling for timer IRQ0 */
+       mutex_lock(&ioapic_mutex);
+       if (!(flags & IOAPIC_MAP_ALLOC)) {
+               if (!legacy) {
+                       irq = irq_find_mapping(domain, pin);
                        if (irq == 0)
-                               info->count++;
+                               irq = -ENOENT;
                }
        } else {
-               irq = irq_find_mapping(domain, pin);
-               if (irq <= 0 && (flags & IOAPIC_MAP_ALLOC))
-                       irq = alloc_irq_from_domain(domain, gsi, pin);
-       }
-
-       if (flags & IOAPIC_MAP_ALLOC) {
-               /* special handling for legacy IRQs */
-               if (irq < nr_legacy_irqs() && info->count == 1 &&
-                   mp_irqdomain_map(domain, irq, pin) != 0)
-                       irq = -1;
-
-               if (irq > 0)
-                       info->count++;
-               else if (info->count == 0)
-                       info->set = 0;
+               ioapic_copy_alloc_attr(&tmp, info, gsi, ioapic, pin);
+               if (legacy)
+                       irq = alloc_isa_irq_from_domain(domain, irq,
+                                                       ioapic, pin, &tmp);
+               else if ((irq = irq_find_mapping(domain, pin)) == 0)
+                       irq = alloc_irq_from_domain(domain, ioapic, gsi, &tmp);
+               else if (!mp_check_pin_attr(irq, &tmp))
+                       irq = -EBUSY;
+               if (irq >= 0) {
+                       data = irq_get_chip_data(irq);
+                       data->count++;
+               }
        }
-
        mutex_unlock(&ioapic_mutex);
 
-       return irq > 0 ? irq : -1;
+       return irq;
 }
 
 static int pin_2_irq(int idx, int ioapic, int pin, unsigned int flags)
@@ -1058,10 +1098,10 @@ static int pin_2_irq(int idx, int ioapic, int pin, unsigned int flags)
        }
 #endif
 
-       return  mp_map_pin_to_irq(gsi, idx, ioapic, pin, flags);
+       return  mp_map_pin_to_irq(gsi, idx, ioapic, pin, flags, NULL);
 }
 
-int mp_map_gsi_to_irq(u32 gsi, unsigned int flags)
+int mp_map_gsi_to_irq(u32 gsi, unsigned int flags, struct irq_alloc_info *info)
 {
        int ioapic, pin, idx;
 
@@ -1074,31 +1114,24 @@ int mp_map_gsi_to_irq(u32 gsi, unsigned int flags)
        if ((flags & IOAPIC_MAP_CHECK) && idx < 0)
                return -1;
 
-       return mp_map_pin_to_irq(gsi, idx, ioapic, pin, flags);
+       return mp_map_pin_to_irq(gsi, idx, ioapic, pin, flags, info);
 }
 
 void mp_unmap_irq(int irq)
 {
-       struct irq_data *data = irq_get_irq_data(irq);
-       struct mp_pin_info *info;
-       int ioapic, pin;
+       struct irq_data *irq_data = irq_get_irq_data(irq);
+       struct mp_chip_data *data;
 
-       if (!data || !data->domain)
+       if (!irq_data || !irq_data->domain)
                return;
 
-       ioapic = (int)(long)data->domain->host_data;
-       pin = (int)data->hwirq;
-       info = mp_pin_info(ioapic, pin);
+       data = irq_data->chip_data;
+       if (!data || data->isa_irq)
+               return;
 
        mutex_lock(&ioapic_mutex);
-       if (--info->count == 0) {
-               info->set = 0;
-               if (irq < nr_legacy_irqs() &&
-                   ioapics[ioapic].irqdomain_cfg.type == IOAPIC_DOMAIN_LEGACY)
-                       mp_irqdomain_unmap(data->domain, irq);
-               else
-                       irq_dispose_mapping(irq);
-       }
+       if (--data->count == 0)
+               irq_domain_free_irqs(irq, 1);
        mutex_unlock(&ioapic_mutex);
 }
 
@@ -1165,7 +1198,7 @@ out:
 }
 EXPORT_SYMBOL(IO_APIC_get_PCI_irq_vector);
 
-static struct irq_chip ioapic_chip;
+static struct irq_chip ioapic_chip, ioapic_ir_chip;
 
 #ifdef CONFIG_X86_32
 static inline int IO_APIC_irq_trigger(int irq)
@@ -1189,96 +1222,6 @@ static inline int IO_APIC_irq_trigger(int irq)
 }
 #endif
 
-static void ioapic_register_intr(unsigned int irq, struct irq_cfg *cfg,
-                                unsigned long trigger)
-{
-       struct irq_chip *chip = &ioapic_chip;
-       irq_flow_handler_t hdl;
-       bool fasteoi;
-
-       if ((trigger == IOAPIC_AUTO && IO_APIC_irq_trigger(irq)) ||
-           trigger == IOAPIC_LEVEL) {
-               irq_set_status_flags(irq, IRQ_LEVEL);
-               fasteoi = true;
-       } else {
-               irq_clear_status_flags(irq, IRQ_LEVEL);
-               fasteoi = false;
-       }
-
-       if (setup_remapped_irq(irq, cfg, chip))
-               fasteoi = trigger != 0;
-
-       hdl = fasteoi ? handle_fasteoi_irq : handle_edge_irq;
-       irq_set_chip_and_handler_name(irq, chip, hdl,
-                                     fasteoi ? "fasteoi" : "edge");
-}
-
-int native_setup_ioapic_entry(int irq, struct IO_APIC_route_entry *entry,
-                             unsigned int destination, int vector,
-                             struct io_apic_irq_attr *attr)
-{
-       memset(entry, 0, sizeof(*entry));
-
-       entry->delivery_mode = apic->irq_delivery_mode;
-       entry->dest_mode     = apic->irq_dest_mode;
-       entry->dest          = destination;
-       entry->vector        = vector;
-       entry->mask          = 0;                       /* enable IRQ */
-       entry->trigger       = attr->trigger;
-       entry->polarity      = attr->polarity;
-
-       /*
-        * Mask level triggered irqs.
-        * Use IRQ_DELAYED_DISABLE for edge triggered irqs.
-        */
-       if (attr->trigger)
-               entry->mask = 1;
-
-       return 0;
-}
-
-static void setup_ioapic_irq(unsigned int irq, struct irq_cfg *cfg,
-                               struct io_apic_irq_attr *attr)
-{
-       struct IO_APIC_route_entry entry;
-       unsigned int dest;
-
-       if (!IO_APIC_IRQ(irq))
-               return;
-
-       if (assign_irq_vector(irq, cfg, apic->target_cpus()))
-               return;
-
-       if (apic->cpu_mask_to_apicid_and(cfg->domain, apic->target_cpus(),
-                                        &dest)) {
-               pr_warn("Failed to obtain apicid for ioapic %d, pin %d\n",
-                       mpc_ioapic_id(attr->ioapic), attr->ioapic_pin);
-               clear_irq_vector(irq, cfg);
-
-               return;
-       }
-
-       apic_printk(APIC_VERBOSE,KERN_DEBUG
-                   "IOAPIC[%d]: Set routing entry (%d-%d -> 0x%x -> "
-                   "IRQ %d Mode:%i Active:%i Dest:%d)\n",
-                   attr->ioapic, mpc_ioapic_id(attr->ioapic), attr->ioapic_pin,
-                   cfg->vector, irq, attr->trigger, attr->polarity, dest);
-
-       if (x86_io_apic_ops.setup_entry(irq, &entry, dest, cfg->vector, attr)) {
-               pr_warn("Failed to setup ioapic entry for ioapic  %d, pin %d\n",
-                       mpc_ioapic_id(attr->ioapic), attr->ioapic_pin);
-               clear_irq_vector(irq, cfg);
-
-               return;
-       }
-
-       ioapic_register_intr(irq, cfg, attr->trigger);
-       if (irq < nr_legacy_irqs())
-               legacy_pic->mask(irq);
-
-       ioapic_write_entry(attr->ioapic, attr->ioapic_pin, entry);
-}
-
 static void __init setup_IO_APIC_irqs(void)
 {
        unsigned int ioapic, pin;
@@ -1298,106 +1241,41 @@ static void __init setup_IO_APIC_irqs(void)
        }
 }
 
-/*
- * Set up the timer pin, possibly with the 8259A-master behind.
- */
-static void __init setup_timer_IRQ0_pin(unsigned int ioapic_idx,
-                                       unsigned int pin, int vector)
-{
-       struct IO_APIC_route_entry entry;
-       unsigned int dest;
-
-       memset(&entry, 0, sizeof(entry));
-
-       /*
-        * We use logical delivery to get the timer IRQ
-        * to the first CPU.
-        */
-       if (unlikely(apic->cpu_mask_to_apicid_and(apic->target_cpus(),
-                                                 apic->target_cpus(), &dest)))
-               dest = BAD_APICID;
-
-       entry.dest_mode = apic->irq_dest_mode;
-       entry.mask = 0;                 /* don't mask IRQ for edge */
-       entry.dest = dest;
-       entry.delivery_mode = apic->irq_delivery_mode;
-       entry.polarity = 0;
-       entry.trigger = 0;
-       entry.vector = vector;
-
-       /*
-        * The timer IRQ doesn't have to know that behind the
-        * scene we may have a 8259A-master in AEOI mode ...
-        */
-       irq_set_chip_and_handler_name(0, &ioapic_chip, handle_edge_irq,
-                                     "edge");
-
-       /*
-        * Add it to the IO-APIC irq-routing table:
-        */
-       ioapic_write_entry(ioapic_idx, pin, entry);
-}
-
-void native_io_apic_print_entries(unsigned int apic, unsigned int nr_entries)
+void ioapic_zap_locks(void)
 {
-       int i;
-
-       pr_debug(" NR Dst Mask Trig IRR Pol Stat Dmod Deli Vect:\n");
-
-       for (i = 0; i <= nr_entries; i++) {
-               struct IO_APIC_route_entry entry;
-
-               entry = ioapic_read_entry(apic, i);
-
-               pr_debug(" %02x %02X  ", i, entry.dest);
-               pr_cont("%1d    %1d    %1d   %1d   %1d    "
-                       "%1d    %1d    %02X\n",
-                       entry.mask,
-                       entry.trigger,
-                       entry.irr,
-                       entry.polarity,
-                       entry.delivery_status,
-                       entry.dest_mode,
-                       entry.delivery_mode,
-                       entry.vector);
-       }
+       raw_spin_lock_init(&ioapic_lock);
 }
 
-void intel_ir_io_apic_print_entries(unsigned int apic,
-                                   unsigned int nr_entries)
+static void io_apic_print_entries(unsigned int apic, unsigned int nr_entries)
 {
        int i;
+       char buf[256];
+       struct IO_APIC_route_entry entry;
+       struct IR_IO_APIC_route_entry *ir_entry = (void *)&entry;
 
-       pr_debug(" NR Indx Fmt Mask Trig IRR Pol Stat Indx2 Zero Vect:\n");
-
+       printk(KERN_DEBUG "IOAPIC %d:\n", apic);
        for (i = 0; i <= nr_entries; i++) {
-               struct IR_IO_APIC_route_entry *ir_entry;
-               struct IO_APIC_route_entry entry;
-
                entry = ioapic_read_entry(apic, i);
-
-               ir_entry = (struct IR_IO_APIC_route_entry *)&entry;
-
-               pr_debug(" %02x %04X ", i, ir_entry->index);
-               pr_cont("%1d   %1d    %1d    %1d   %1d   "
-                       "%1d    %1d     %X    %02X\n",
-                       ir_entry->format,
-                       ir_entry->mask,
-                       ir_entry->trigger,
-                       ir_entry->irr,
-                       ir_entry->polarity,
-                       ir_entry->delivery_status,
-                       ir_entry->index2,
-                       ir_entry->zero,
-                       ir_entry->vector);
+               snprintf(buf, sizeof(buf),
+                        " pin%02x, %s, %s, %s, V(%02X), IRR(%1d), S(%1d)",
+                        i,
+                        entry.mask == IOAPIC_MASKED ? "disabled" : "enabled ",
+                        entry.trigger == IOAPIC_LEVEL ? "level" : "edge ",
+                        entry.polarity == IOAPIC_POL_LOW ? "low " : "high",
+                        entry.vector, entry.irr, entry.delivery_status);
+               if (ir_entry->format)
+                       printk(KERN_DEBUG "%s, remapped, I(%04X),  Z(%X)\n",
+                              buf, (ir_entry->index << 15) | ir_entry->index,
+                              ir_entry->zero);
+               else
+                       printk(KERN_DEBUG "%s, %s, D(%02X), M(%1d)\n",
+                              buf,
+                              entry.dest_mode == IOAPIC_DEST_MODE_LOGICAL ?
+                              "logical " : "physical",
+                              entry.dest, entry.delivery_mode);
        }
 }
 
-void ioapic_zap_locks(void)
-{
-       raw_spin_lock_init(&ioapic_lock);
-}
-
 static void __init print_IO_APIC(int ioapic_idx)
 {
        union IO_APIC_reg_00 reg_00;
@@ -1451,16 +1329,13 @@ static void __init print_IO_APIC(int ioapic_idx)
        }
 
        printk(KERN_DEBUG ".... IRQ redirection table:\n");
-
-       x86_io_apic_ops.print_entries(ioapic_idx, reg_01.bits.entries);
+       io_apic_print_entries(ioapic_idx, reg_01.bits.entries);
 }
 
 void __init print_IO_APICs(void)
 {
        int ioapic_idx;
-       struct irq_cfg *cfg;
        unsigned int irq;
-       struct irq_chip *chip;
 
        printk(KERN_DEBUG "number of MP IRQ sources: %d.\n", mp_irq_entries);
        for_each_ioapic(ioapic_idx)
@@ -1480,18 +1355,20 @@ void __init print_IO_APICs(void)
        printk(KERN_DEBUG "IRQ to pin mappings:\n");
        for_each_active_irq(irq) {
                struct irq_pin_list *entry;
+               struct irq_chip *chip;
+               struct mp_chip_data *data;
 
                chip = irq_get_chip(irq);
-               if (chip != &ioapic_chip)
+               if (chip != &ioapic_chip && chip != &ioapic_ir_chip)
                        continue;
-
-               cfg = irq_cfg(irq);
-               if (!cfg)
+               data = irq_get_chip_data(irq);
+               if (!data)
                        continue;
-               if (list_empty(&cfg->irq_2_pin))
+               if (list_empty(&data->irq_2_pin))
                        continue;
+
                printk(KERN_DEBUG "IRQ%d ", irq);
-               for_each_irq_pin(entry, cfg->irq_2_pin)
+               for_each_irq_pin(entry, data->irq_2_pin)
                        pr_cont("-> %d:%d", entry->apic, entry->pin);
                pr_cont("\n");
        }
@@ -1564,15 +1441,12 @@ void native_disable_io_apic(void)
                struct IO_APIC_route_entry entry;
 
                memset(&entry, 0, sizeof(entry));
-               entry.mask            = 0; /* Enabled */
-               entry.trigger         = 0; /* Edge */
-               entry.irr             = 0;
-               entry.polarity        = 0; /* High */
-               entry.delivery_status = 0;
-               entry.dest_mode       = 0; /* Physical */
-               entry.delivery_mode   = dest_ExtINT; /* ExtInt */
-               entry.vector          = 0;
-               entry.dest            = read_apic_id();
+               entry.mask              = IOAPIC_UNMASKED;
+               entry.trigger           = IOAPIC_EDGE;
+               entry.polarity          = IOAPIC_POL_HIGH;
+               entry.dest_mode         = IOAPIC_DEST_MODE_PHYSICAL;
+               entry.delivery_mode     = dest_ExtINT;
+               entry.dest              = read_apic_id();
 
                /*
                 * Add it to the IO-APIC irq-routing table:
@@ -1582,7 +1456,6 @@ void native_disable_io_apic(void)
 
        if (cpu_has_apic || apic_from_smp_config())
                disconnect_bsp_APIC(ioapic_i8259.pin != -1);
-
 }
 
 /*
@@ -1792,7 +1665,6 @@ static int __init timer_irq_works(void)
  * This is not complete - we should be able to fake
  * an edge even if it isn't on the 8259A...
  */
-
 static unsigned int startup_ioapic_irq(struct irq_data *data)
 {
        int was_pending = 0, irq = data->irq;
@@ -1804,74 +1676,22 @@ static unsigned int startup_ioapic_irq(struct irq_data *data)
                if (legacy_pic->irq_pending(irq))
                        was_pending = 1;
        }
-       __unmask_ioapic(irqd_cfg(data));
+       __unmask_ioapic(data->chip_data);
        raw_spin_unlock_irqrestore(&ioapic_lock, flags);
 
        return was_pending;
 }
 
-/*
- * Level and edge triggered IO-APIC interrupts need different handling,
- * so we use two separate IRQ descriptors. Edge triggered IRQs can be
- * handled with the level-triggered descriptor, but that one has slightly
- * more overhead. Level-triggered interrupts cannot be handled with the
- * edge-triggered handler, without risking IRQ storms and other ugly
- * races.
- */
-
-static void __target_IO_APIC_irq(unsigned int irq, unsigned int dest, struct irq_cfg *cfg)
-{
-       int apic, pin;
-       struct irq_pin_list *entry;
-       u8 vector = cfg->vector;
-
-       for_each_irq_pin(entry, cfg->irq_2_pin) {
-               unsigned int reg;
-
-               apic = entry->apic;
-               pin = entry->pin;
-
-               io_apic_write(apic, 0x11 + pin*2, dest);
-               reg = io_apic_read(apic, 0x10 + pin*2);
-               reg &= ~IO_APIC_REDIR_VECTOR_MASK;
-               reg |= vector;
-               io_apic_modify(apic, 0x10 + pin*2, reg);
-       }
-}
-
-int native_ioapic_set_affinity(struct irq_data *data,
-                              const struct cpumask *mask,
-                              bool force)
-{
-       unsigned int dest, irq = data->irq;
-       unsigned long flags;
-       int ret;
-
-       if (!config_enabled(CONFIG_SMP))
-               return -EPERM;
-
-       raw_spin_lock_irqsave(&ioapic_lock, flags);
-       ret = apic_set_affinity(data, mask, &dest);
-       if (!ret) {
-               /* Only the high 8 bits are valid. */
-               dest = SET_APIC_LOGICAL_ID(dest);
-               __target_IO_APIC_irq(irq, dest, irqd_cfg(data));
-               ret = IRQ_SET_MASK_OK_NOCOPY;
-       }
-       raw_spin_unlock_irqrestore(&ioapic_lock, flags);
-       return ret;
-}
-
 atomic_t irq_mis_count;
 
 #ifdef CONFIG_GENERIC_PENDING_IRQ
-static bool io_apic_level_ack_pending(struct irq_cfg *cfg)
+static bool io_apic_level_ack_pending(struct mp_chip_data *data)
 {
        struct irq_pin_list *entry;
        unsigned long flags;
 
        raw_spin_lock_irqsave(&ioapic_lock, flags);
-       for_each_irq_pin(entry, cfg->irq_2_pin) {
+       for_each_irq_pin(entry, data->irq_2_pin) {
                unsigned int reg;
                int pin;
 
@@ -1888,18 +1708,17 @@ static bool io_apic_level_ack_pending(struct irq_cfg *cfg)
        return false;
 }
 
-static inline bool ioapic_irqd_mask(struct irq_data *data, struct irq_cfg *cfg)
+static inline bool ioapic_irqd_mask(struct irq_data *data)
 {
        /* If we are moving the irq we need to mask it */
        if (unlikely(irqd_is_setaffinity_pending(data))) {
-               mask_ioapic(cfg);
+               mask_ioapic_irq(data);
                return true;
        }
        return false;
 }
 
-static inline void ioapic_irqd_unmask(struct irq_data *data,
-                                     struct irq_cfg *cfg, bool masked)
+static inline void ioapic_irqd_unmask(struct irq_data *data, bool masked)
 {
        if (unlikely(masked)) {
                /* Only migrate the irq if the ack has been received.
@@ -1928,31 +1747,30 @@ static inline void ioapic_irqd_unmask(struct irq_data *data,
                 * accurate and is causing problems then it is a hardware bug
                 * and you can go talk to the chipset vendor about it.
                 */
-               if (!io_apic_level_ack_pending(cfg))
+               if (!io_apic_level_ack_pending(data->chip_data))
                        irq_move_masked_irq(data);
-               unmask_ioapic(cfg);
+               unmask_ioapic_irq(data);
        }
 }
 #else
-static inline bool ioapic_irqd_mask(struct irq_data *data, struct irq_cfg *cfg)
+static inline bool ioapic_irqd_mask(struct irq_data *data)
 {
        return false;
 }
-static inline void ioapic_irqd_unmask(struct irq_data *data,
-                                     struct irq_cfg *cfg, bool masked)
+static inline void ioapic_irqd_unmask(struct irq_data *data, bool masked)
 {
 }
 #endif
 
-static void ack_ioapic_level(struct irq_data *data)
+static void ioapic_ack_level(struct irq_data *irq_data)
 {
-       struct irq_cfg *cfg = irqd_cfg(data);
-       int i, irq = data->irq;
+       struct irq_cfg *cfg = irqd_cfg(irq_data);
        unsigned long v;
        bool masked;
+       int i;
 
        irq_complete_move(cfg);
-       masked = ioapic_irqd_mask(data, cfg);
+       masked = ioapic_irqd_mask(irq_data);
 
        /*
         * It appears there is an erratum which affects at least version 0x11
@@ -2004,11 +1822,49 @@ static void ack_ioapic_level(struct irq_data *data)
         */
        if (!(v & (1 << (i & 0x1f)))) {
                atomic_inc(&irq_mis_count);
+               eoi_ioapic_pin(cfg->vector, irq_data->chip_data);
+       }
+
+       ioapic_irqd_unmask(irq_data, masked);
+}
+
+static void ioapic_ir_ack_level(struct irq_data *irq_data)
+{
+       struct mp_chip_data *data = irq_data->chip_data;
+
+       /*
+        * Intr-remapping uses pin number as the virtual vector
+        * in the RTE. Actual vector is programmed in
+        * intr-remapping table entry. Hence for the io-apic
+        * EOI we use the pin number.
+        */
+       ack_APIC_irq();
+       eoi_ioapic_pin(data->entry.vector, data);
+}
 
-               eoi_ioapic_irq(irq, cfg);
+static int ioapic_set_affinity(struct irq_data *irq_data,
+                              const struct cpumask *mask, bool force)
+{
+       struct irq_data *parent = irq_data->parent_data;
+       struct mp_chip_data *data = irq_data->chip_data;
+       struct irq_pin_list *entry;
+       struct irq_cfg *cfg;
+       unsigned long flags;
+       int ret;
+
+       ret = parent->chip->irq_set_affinity(parent, mask, force);
+       raw_spin_lock_irqsave(&ioapic_lock, flags);
+       if (ret >= 0 && ret != IRQ_SET_MASK_OK_DONE) {
+               cfg = irqd_cfg(irq_data);
+               data->entry.dest = cfg->dest_apicid;
+               data->entry.vector = cfg->vector;
+               for_each_irq_pin(entry, data->irq_2_pin)
+                       __ioapic_write_entry(entry->apic, entry->pin,
+                                            data->entry);
        }
+       raw_spin_unlock_irqrestore(&ioapic_lock, flags);
 
-       ioapic_irqd_unmask(data, cfg, masked);
+       return ret;
 }
 
 static struct irq_chip ioapic_chip __read_mostly = {
@@ -2016,10 +1872,20 @@ static struct irq_chip ioapic_chip __read_mostly = {
        .irq_startup            = startup_ioapic_irq,
        .irq_mask               = mask_ioapic_irq,
        .irq_unmask             = unmask_ioapic_irq,
-       .irq_ack                = apic_ack_edge,
-       .irq_eoi                = ack_ioapic_level,
-       .irq_set_affinity       = native_ioapic_set_affinity,
-       .irq_retrigger          = apic_retrigger_irq,
+       .irq_ack                = irq_chip_ack_parent,
+       .irq_eoi                = ioapic_ack_level,
+       .irq_set_affinity       = ioapic_set_affinity,
+       .flags                  = IRQCHIP_SKIP_SET_WAKE,
+};
+
+static struct irq_chip ioapic_ir_chip __read_mostly = {
+       .name                   = "IR-IO-APIC",
+       .irq_startup            = startup_ioapic_irq,
+       .irq_mask               = mask_ioapic_irq,
+       .irq_unmask             = unmask_ioapic_irq,
+       .irq_ack                = irq_chip_ack_parent,
+       .irq_eoi                = ioapic_ir_ack_level,
+       .irq_set_affinity       = ioapic_set_affinity,
        .flags                  = IRQCHIP_SKIP_SET_WAKE,
 };
 
@@ -2113,12 +1979,12 @@ static inline void __init unlock_ExtINT_logic(void)
 
        memset(&entry1, 0, sizeof(entry1));
 
-       entry1.dest_mode = 0;                   /* physical delivery */
-       entry1.mask = 0;                        /* unmask IRQ now */
+       entry1.dest_mode = IOAPIC_DEST_MODE_PHYSICAL;
+       entry1.mask = IOAPIC_UNMASKED;
        entry1.dest = hard_smp_processor_id();
        entry1.delivery_mode = dest_ExtINT;
        entry1.polarity = entry0.polarity;
-       entry1.trigger = 0;
+       entry1.trigger = IOAPIC_EDGE;
        entry1.vector = 0;
 
        ioapic_write_entry(apic, pin, entry1);
@@ -2152,6 +2018,25 @@ static int __init disable_timer_pin_setup(char *arg)
 }
 early_param("disable_timer_pin_1", disable_timer_pin_setup);
 
+static int mp_alloc_timer_irq(int ioapic, int pin)
+{
+       int irq = -1;
+       struct irq_domain *domain = mp_ioapic_irqdomain(ioapic);
+
+       if (domain) {
+               struct irq_alloc_info info;
+
+               ioapic_set_alloc_attr(&info, NUMA_NO_NODE, 0, 0);
+               info.ioapic_id = mpc_ioapic_id(ioapic);
+               info.ioapic_pin = pin;
+               mutex_lock(&ioapic_mutex);
+               irq = alloc_isa_irq_from_domain(domain, 0, ioapic, pin, &info);
+               mutex_unlock(&ioapic_mutex);
+       }
+
+       return irq;
+}
+
 /*
  * This code may look a bit paranoid, but it's supposed to cooperate with
  * a wide range of boards and BIOS bugs.  Fortunately only the timer IRQ
@@ -2162,7 +2047,9 @@ early_param("disable_timer_pin_1", disable_timer_pin_setup);
  */
 static inline void __init check_timer(void)
 {
-       struct irq_cfg *cfg = irq_cfg(0);
+       struct irq_data *irq_data = irq_get_irq_data(0);
+       struct mp_chip_data *data = irq_data->chip_data;
+       struct irq_cfg *cfg = irqd_cfg(irq_data);
        int node = cpu_to_node(0);
        int apic1, pin1, apic2, pin2;
        unsigned long flags;
@@ -2174,7 +2061,6 @@ static inline void __init check_timer(void)
         * get/set the timer IRQ vector:
         */
        legacy_pic->mask(0);
-       assign_irq_vector(0, cfg, apic->target_cpus());
 
        /*
         * As IRQ0 is to be enabled in the 8259A, the virtual
@@ -2215,23 +2101,21 @@ static inline void __init check_timer(void)
        }
 
        if (pin1 != -1) {
-               /*
-                * Ok, does IRQ0 through the IOAPIC work?
-                */
+               /* Ok, does IRQ0 through the IOAPIC work? */
                if (no_pin1) {
-                       add_pin_to_irq_node(cfg, node, apic1, pin1);
-                       setup_timer_IRQ0_pin(apic1, pin1, cfg->vector);
+                       mp_alloc_timer_irq(apic1, pin1);
                } else {
-                       /* for edge trigger, setup_ioapic_irq already
-                        * leave it unmasked.
+                       /*
+                        * for edge trigger, it's already unmasked,
                         * so only need to unmask if it is level-trigger
                         * do we really have level trigger timer?
                         */
                        int idx;
                        idx = find_irq_entry(apic1, pin1, mp_INT);
                        if (idx != -1 && irq_trigger(idx))
-                               unmask_ioapic(cfg);
+                               unmask_ioapic_irq(irq_get_chip_data(0));
                }
+               irq_domain_activate_irq(irq_data);
                if (timer_irq_works()) {
                        if (disable_timer_pin_1 > 0)
                                clear_IO_APIC_pin(0, pin1);
@@ -2251,8 +2135,8 @@ static inline void __init check_timer(void)
                /*
                 * legacy devices should be connected to IO APIC #0
                 */
-               replace_pin_at_irq_node(cfg, node, apic1, pin1, apic2, pin2);
-               setup_timer_IRQ0_pin(apic2, pin2, cfg->vector);
+               replace_pin_at_irq_node(data, node, apic1, pin1, apic2, pin2);
+               irq_domain_activate_irq(irq_data);
                legacy_pic->unmask(0);
                if (timer_irq_works()) {
                        apic_printk(APIC_QUIET, KERN_INFO "....... works.\n");
@@ -2329,36 +2213,35 @@ out:
 
 static int mp_irqdomain_create(int ioapic)
 {
-       size_t size;
+       struct irq_alloc_info info;
+       struct irq_domain *parent;
        int hwirqs = mp_ioapic_pin_count(ioapic);
        struct ioapic *ip = &ioapics[ioapic];
        struct ioapic_domain_cfg *cfg = &ip->irqdomain_cfg;
        struct mp_ioapic_gsi *gsi_cfg = mp_ioapic_gsi_routing(ioapic);
 
-       size = sizeof(struct mp_pin_info) * mp_ioapic_pin_count(ioapic);
-       ip->pin_info = kzalloc(size, GFP_KERNEL);
-       if (!ip->pin_info)
-               return -ENOMEM;
-
        if (cfg->type == IOAPIC_DOMAIN_INVALID)
                return 0;
 
+       init_irq_alloc_info(&info, NULL);
+       info.type = X86_IRQ_ALLOC_TYPE_IOAPIC;
+       info.ioapic_id = mpc_ioapic_id(ioapic);
+       parent = irq_remapping_get_ir_irq_domain(&info);
+       if (!parent)
+               parent = x86_vector_domain;
+
        ip->irqdomain = irq_domain_add_linear(cfg->dev, hwirqs, cfg->ops,
                                              (void *)(long)ioapic);
-       if(!ip->irqdomain) {
-               kfree(ip->pin_info);
-               ip->pin_info = NULL;
+       if (!ip->irqdomain)
                return -ENOMEM;
-       }
+
+       ip->irqdomain->parent = parent;
 
        if (cfg->type == IOAPIC_DOMAIN_LEGACY ||
            cfg->type == IOAPIC_DOMAIN_STRICT)
                ioapic_dynirq_base = max(ioapic_dynirq_base,
                                         gsi_cfg->gsi_end + 1);
 
-       if (gsi_cfg->gsi_base == 0)
-               irq_set_default_host(ip->irqdomain);
-
        return 0;
 }
 
@@ -2368,8 +2251,6 @@ static void ioapic_destroy_irqdomain(int idx)
                irq_domain_remove(ioapics[idx].irqdomain);
                ioapics[idx].irqdomain = NULL;
        }
-       kfree(ioapics[idx].pin_info);
-       ioapics[idx].pin_info = NULL;
 }
 
 void __init setup_IO_APIC(void)
@@ -2399,20 +2280,6 @@ void __init setup_IO_APIC(void)
        ioapic_initialized = 1;
 }
 
-/*
- *      Called after all the initialization is done. If we didn't find any
- *      APIC bugs then we can allow the modify fast path
- */
-
-static int __init io_apic_bug_finalize(void)
-{
-       if (sis_apic_bug == -1)
-               sis_apic_bug = 0;
-       return 0;
-}
-
-late_initcall(io_apic_bug_finalize);
-
 static void resume_ioapic_id(int ioapic_idx)
 {
        unsigned long flags;
@@ -2451,20 +2318,6 @@ static int __init ioapic_init_ops(void)
 
 device_initcall(ioapic_init_ops);
 
-static int
-io_apic_setup_irq_pin(unsigned int irq, int node, struct io_apic_irq_attr *attr)
-{
-       struct irq_cfg *cfg = alloc_irq_and_cfg_at(irq, node);
-       int ret;
-
-       if (!cfg)
-               return -EINVAL;
-       ret = __add_pin_to_irq_node(cfg, node, attr->ioapic, attr->ioapic_pin);
-       if (!ret)
-               setup_ioapic_irq(irq, cfg, attr);
-       return ret;
-}
-
 static int io_apic_get_redir_entries(int ioapic)
 {
        union IO_APIC_reg_01    reg_01;
@@ -2692,7 +2545,7 @@ void __init setup_ioapic_dest(void)
                else
                        mask = apic->target_cpus();
 
-               x86_io_apic_ops.set_affinity(idata, mask, false);
+               irq_set_affinity(irq, mask);
        }
 
 }
@@ -2737,7 +2590,7 @@ static struct resource * __init ioapic_setup_resources(void)
        return res;
 }
 
-void __init native_io_apic_init_mappings(void)
+void __init io_apic_init_mappings(void)
 {
        unsigned long ioapic_phys, idx = FIX_IO_APIC_BASE_0;
        struct resource *ioapic_res;
@@ -2962,7 +2815,6 @@ int mp_unregister_ioapic(u32 gsi_base)
 {
        int ioapic, pin;
        int found = 0;
-       struct mp_pin_info *pin_info;
 
        for_each_ioapic(ioapic)
                if (ioapics[ioapic].gsi_config.gsi_base == gsi_base) {
@@ -2975,11 +2827,17 @@ int mp_unregister_ioapic(u32 gsi_base)
        }
 
        for_each_pin(ioapic, pin) {
-               pin_info = mp_pin_info(ioapic, pin);
-               if (pin_info->count) {
-                       pr_warn("pin%d on IOAPIC%d is still in use.\n",
-                               pin, ioapic);
-                       return -EBUSY;
+               u32 gsi = mp_pin_to_gsi(ioapic, pin);
+               int irq = mp_map_gsi_to_irq(gsi, 0, NULL);
+               struct mp_chip_data *data;
+
+               if (irq >= 0) {
+                       data = irq_get_chip_data(irq);
+                       if (data && data->count) {
+                               pr_warn("pin%d on IOAPIC%d is still in use.\n",
+                                       pin, ioapic);
+                               return -EBUSY;
+                       }
                }
        }
 
@@ -3006,108 +2864,141 @@ int mp_ioapic_registered(u32 gsi_base)
        return 0;
 }
 
-static inline void set_io_apic_irq_attr(struct io_apic_irq_attr *irq_attr,
-                                       int ioapic, int ioapic_pin,
-                                       int trigger, int polarity)
+static void mp_irqdomain_get_attr(u32 gsi, struct mp_chip_data *data,
+                                 struct irq_alloc_info *info)
 {
-       irq_attr->ioapic        = ioapic;
-       irq_attr->ioapic_pin    = ioapic_pin;
-       irq_attr->trigger       = trigger;
-       irq_attr->polarity      = polarity;
+       if (info && info->ioapic_valid) {
+               data->trigger = info->ioapic_trigger;
+               data->polarity = info->ioapic_polarity;
+       } else if (acpi_get_override_irq(gsi, &data->trigger,
+                                        &data->polarity) < 0) {
+               /* PCI interrupts are always active low level triggered. */
+               data->trigger = IOAPIC_LEVEL;
+               data->polarity = IOAPIC_POL_LOW;
+       }
 }
 
-int mp_irqdomain_map(struct irq_domain *domain, unsigned int virq,
-                    irq_hw_number_t hwirq)
+static void mp_setup_entry(struct irq_cfg *cfg, struct mp_chip_data *data,
+                          struct IO_APIC_route_entry *entry)
 {
-       int ioapic = (int)(long)domain->host_data;
-       struct mp_pin_info *info = mp_pin_info(ioapic, hwirq);
-       struct io_apic_irq_attr attr;
+       memset(entry, 0, sizeof(*entry));
+       entry->delivery_mode = apic->irq_delivery_mode;
+       entry->dest_mode     = apic->irq_dest_mode;
+       entry->dest          = cfg->dest_apicid;
+       entry->vector        = cfg->vector;
+       entry->trigger       = data->trigger;
+       entry->polarity      = data->polarity;
+       /*
+        * Mask level triggered irqs. Edge triggered irqs are masked
+        * by the irq core code in case they fire.
+        */
+       if (data->trigger == IOAPIC_LEVEL)
+               entry->mask = IOAPIC_MASKED;
+       else
+               entry->mask = IOAPIC_UNMASKED;
+}
 
-       /* Get default attribute if not set by caller yet */
-       if (!info->set) {
-               u32 gsi = mp_pin_to_gsi(ioapic, hwirq);
+int mp_irqdomain_alloc(struct irq_domain *domain, unsigned int virq,
+                      unsigned int nr_irqs, void *arg)
+{
+       int ret, ioapic, pin;
+       struct irq_cfg *cfg;
+       struct irq_data *irq_data;
+       struct mp_chip_data *data;
+       struct irq_alloc_info *info = arg;
 
-               if (acpi_get_override_irq(gsi, &info->trigger,
-                                         &info->polarity) < 0) {
-                       /*
-                        * PCI interrupts are always polarity one level
-                        * triggered.
-                        */
-                       info->trigger = 1;
-                       info->polarity = 1;
-               }
-               info->node = NUMA_NO_NODE;
+       if (!info || nr_irqs > 1)
+               return -EINVAL;
+       irq_data = irq_domain_get_irq_data(domain, virq);
+       if (!irq_data)
+               return -EINVAL;
 
-               /*
-                * setup_IO_APIC_irqs() programs all legacy IRQs with default
-                * trigger and polarity attributes. Don't set the flag for that
-                * case so the first legacy IRQ user could reprogram the pin
-                * with real trigger and polarity attributes.
-                */
-               if (virq >= nr_legacy_irqs() || info->count)
-                       info->set = 1;
-       }
-       set_io_apic_irq_attr(&attr, ioapic, hwirq, info->trigger,
-                            info->polarity);
+       ioapic = mp_irqdomain_ioapic_idx(domain);
+       pin = info->ioapic_pin;
+       if (irq_find_mapping(domain, (irq_hw_number_t)pin) > 0)
+               return -EEXIST;
 
-       return io_apic_setup_irq_pin(virq, info->node, &attr);
-}
+       data = kzalloc(sizeof(*data), GFP_KERNEL);
+       if (!data)
+               return -ENOMEM;
 
-void mp_irqdomain_unmap(struct irq_domain *domain, unsigned int virq)
-{
-       struct irq_data *data = irq_get_irq_data(virq);
-       struct irq_cfg *cfg = irq_cfg(virq);
-       int ioapic = (int)(long)domain->host_data;
-       int pin = (int)data->hwirq;
+       info->ioapic_entry = &data->entry;
+       ret = irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, info);
+       if (ret < 0) {
+               kfree(data);
+               return ret;
+       }
+
+       INIT_LIST_HEAD(&data->irq_2_pin);
+       irq_data->hwirq = info->ioapic_pin;
+       irq_data->chip = (domain->parent == x86_vector_domain) ?
+                         &ioapic_chip : &ioapic_ir_chip;
+       irq_data->chip_data = data;
+       mp_irqdomain_get_attr(mp_pin_to_gsi(ioapic, pin), data, info);
+
+       cfg = irqd_cfg(irq_data);
+       add_pin_to_irq_node(data, ioapic_alloc_attr_node(info), ioapic, pin);
+       if (info->ioapic_entry)
+               mp_setup_entry(cfg, data, info->ioapic_entry);
+       mp_register_handler(virq, data->trigger);
+       if (virq < nr_legacy_irqs())
+               legacy_pic->mask(virq);
+
+       apic_printk(APIC_VERBOSE, KERN_DEBUG
+                   "IOAPIC[%d]: Set routing entry (%d-%d -> 0x%x -> IRQ %d Mode:%i Active:%i Dest:%d)\n",
+                   ioapic, mpc_ioapic_id(ioapic), pin, cfg->vector,
+                   virq, data->trigger, data->polarity, cfg->dest_apicid);
 
-       ioapic_mask_entry(ioapic, pin);
-       __remove_pin_from_irq(cfg, ioapic, pin);
-       WARN_ON(!list_empty(&cfg->irq_2_pin));
-       arch_teardown_hwirq(virq);
+       return 0;
 }
 
-int mp_set_gsi_attr(u32 gsi, int trigger, int polarity, int node)
+void mp_irqdomain_free(struct irq_domain *domain, unsigned int virq,
+                      unsigned int nr_irqs)
 {
-       int ret = 0;
-       int ioapic, pin;
-       struct mp_pin_info *info;
+       struct irq_data *irq_data;
+       struct mp_chip_data *data;
 
-       ioapic = mp_find_ioapic(gsi);
-       if (ioapic < 0)
-               return -ENODEV;
-
-       pin = mp_find_ioapic_pin(ioapic, gsi);
-       info = mp_pin_info(ioapic, pin);
-       trigger = trigger ? 1 : 0;
-       polarity = polarity ? 1 : 0;
-
-       mutex_lock(&ioapic_mutex);
-       if (!info->set) {
-               info->trigger = trigger;
-               info->polarity = polarity;
-               info->node = node;
-               info->set = 1;
-       } else if (info->trigger != trigger || info->polarity != polarity) {
-               ret = -EBUSY;
+       BUG_ON(nr_irqs != 1);
+       irq_data = irq_domain_get_irq_data(domain, virq);
+       if (irq_data && irq_data->chip_data) {
+               data = irq_data->chip_data;
+               __remove_pin_from_irq(data, mp_irqdomain_ioapic_idx(domain),
+                                     (int)irq_data->hwirq);
+               WARN_ON(!list_empty(&data->irq_2_pin));
+               kfree(irq_data->chip_data);
        }
-       mutex_unlock(&ioapic_mutex);
-
-       return ret;
+       irq_domain_free_irqs_top(domain, virq, nr_irqs);
 }
 
-/* Enable IOAPIC early just for system timer */
-void __init pre_init_apic_IRQ0(void)
+void mp_irqdomain_activate(struct irq_domain *domain,
+                          struct irq_data *irq_data)
 {
-       struct io_apic_irq_attr attr = { 0, 0, 0, 0 };
+       unsigned long flags;
+       struct irq_pin_list *entry;
+       struct mp_chip_data *data = irq_data->chip_data;
 
-       printk(KERN_INFO "Early APIC setup for system timer0\n");
-#ifndef CONFIG_SMP
-       physid_set_mask_of_physid(boot_cpu_physical_apicid,
-                                        &phys_cpu_present_map);
-#endif
-       setup_local_APIC();
+       raw_spin_lock_irqsave(&ioapic_lock, flags);
+       for_each_irq_pin(entry, data->irq_2_pin)
+               __ioapic_write_entry(entry->apic, entry->pin, data->entry);
+       raw_spin_unlock_irqrestore(&ioapic_lock, flags);
+}
 
-       io_apic_setup_irq_pin(0, 0, &attr);
-       irq_set_chip_and_handler_name(0, &ioapic_chip, handle_edge_irq,
-                                     "edge");
+void mp_irqdomain_deactivate(struct irq_domain *domain,
+                            struct irq_data *irq_data)
+{
+       /* It won't be called for IRQ with multiple IOAPIC pins associated */
+       ioapic_mask_entry(mp_irqdomain_ioapic_idx(domain),
+                         (int)irq_data->hwirq);
+}
+
+int mp_irqdomain_ioapic_idx(struct irq_domain *domain)
+{
+       return (int)(long)domain->host_data;
 }
+
+const struct irq_domain_ops mp_ioapic_irqdomain_ops = {
+       .alloc          = mp_irqdomain_alloc,
+       .free           = mp_irqdomain_free,
+       .activate       = mp_irqdomain_activate,
+       .deactivate     = mp_irqdomain_deactivate,
+};
index d6ba2d660dc52f59945825ef80a66821d5a971a5..1a9d735e09c62f508d3226faafda2406eb75c843 100644 (file)
@@ -3,6 +3,8 @@
  *
  * Copyright (C) 1997, 1998, 1999, 2000, 2009 Ingo Molnar, Hajnalka Szabo
  *     Moved from arch/x86/kernel/apic/io_apic.c.
+ * Jiang Liu <jiang.liu@linux.intel.com>
+ *     Convert to hierarchical irqdomain
  *
  * 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
 #include <linux/dmar.h>
 #include <linux/hpet.h>
 #include <linux/msi.h>
+#include <asm/irqdomain.h>
 #include <asm/msidef.h>
 #include <asm/hpet.h>
 #include <asm/hw_irq.h>
 #include <asm/apic.h>
 #include <asm/irq_remapping.h>
 
-void native_compose_msi_msg(struct pci_dev *pdev,
-                           unsigned int irq, unsigned int dest,
-                           struct msi_msg *msg, u8 hpet_id)
+static struct irq_domain *msi_default_domain;
+
+static void irq_msi_compose_msg(struct irq_data *data, struct msi_msg *msg)
 {
-       struct irq_cfg *cfg = irq_cfg(irq);
+       struct irq_cfg *cfg = irqd_cfg(data);
 
        msg->address_hi = MSI_ADDR_BASE_HI;
 
        if (x2apic_enabled())
-               msg->address_hi |= MSI_ADDR_EXT_DEST_ID(dest);
+               msg->address_hi |= MSI_ADDR_EXT_DEST_ID(cfg->dest_apicid);
 
        msg->address_lo =
                MSI_ADDR_BASE_LO |
@@ -39,7 +42,7 @@ void native_compose_msi_msg(struct pci_dev *pdev,
                ((apic->irq_delivery_mode != dest_LowestPrio) ?
                        MSI_ADDR_REDIRECTION_CPU :
                        MSI_ADDR_REDIRECTION_LOWPRI) |
-               MSI_ADDR_DEST_ID(dest);
+               MSI_ADDR_DEST_ID(cfg->dest_apicid);
 
        msg->data =
                MSI_DATA_TRIGGER_EDGE |
@@ -50,180 +53,201 @@ void native_compose_msi_msg(struct pci_dev *pdev,
                MSI_DATA_VECTOR(cfg->vector);
 }
 
-static int msi_compose_msg(struct pci_dev *pdev, unsigned int irq,
-                          struct msi_msg *msg, u8 hpet_id)
+/*
+ * IRQ Chip for MSI PCI/PCI-X/PCI-Express Devices,
+ * which implement the MSI or MSI-X Capability Structure.
+ */
+static struct irq_chip pci_msi_controller = {
+       .name                   = "PCI-MSI",
+       .irq_unmask             = pci_msi_unmask_irq,
+       .irq_mask               = pci_msi_mask_irq,
+       .irq_ack                = irq_chip_ack_parent,
+       .irq_retrigger          = irq_chip_retrigger_hierarchy,
+       .irq_compose_msi_msg    = irq_msi_compose_msg,
+       .flags                  = IRQCHIP_SKIP_SET_WAKE,
+};
+
+int native_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
 {
-       struct irq_cfg *cfg;
-       int err;
-       unsigned dest;
+       struct irq_domain *domain;
+       struct irq_alloc_info info;
 
-       if (disable_apic)
-               return -ENXIO;
+       init_irq_alloc_info(&info, NULL);
+       info.type = X86_IRQ_ALLOC_TYPE_MSI;
+       info.msi_dev = dev;
 
-       cfg = irq_cfg(irq);
-       err = assign_irq_vector(irq, cfg, apic->target_cpus());
-       if (err)
-               return err;
+       domain = irq_remapping_get_irq_domain(&info);
+       if (domain == NULL)
+               domain = msi_default_domain;
+       if (domain == NULL)
+               return -ENOSYS;
 
-       err = apic->cpu_mask_to_apicid_and(cfg->domain,
-                                          apic->target_cpus(), &dest);
-       if (err)
-               return err;
+       return pci_msi_domain_alloc_irqs(domain, dev, nvec, type);
+}
 
-       x86_msi.compose_msi_msg(pdev, irq, dest, msg, hpet_id);
+void native_teardown_msi_irq(unsigned int irq)
+{
+       irq_domain_free_irqs(irq, 1);
+}
 
-       return 0;
+static irq_hw_number_t pci_msi_get_hwirq(struct msi_domain_info *info,
+                                        msi_alloc_info_t *arg)
+{
+       return arg->msi_hwirq;
 }
 
-static int
-msi_set_affinity(struct irq_data *data, const struct cpumask *mask, bool force)
+static int pci_msi_prepare(struct irq_domain *domain, struct device *dev,
+                          int nvec, msi_alloc_info_t *arg)
 {
-       struct irq_cfg *cfg = irqd_cfg(data);
-       struct msi_msg msg;
-       unsigned int dest;
-       int ret;
+       struct pci_dev *pdev = to_pci_dev(dev);
+       struct msi_desc *desc = first_pci_msi_entry(pdev);
+
+       init_irq_alloc_info(arg, NULL);
+       arg->msi_dev = pdev;
+       if (desc->msi_attrib.is_msix) {
+               arg->type = X86_IRQ_ALLOC_TYPE_MSIX;
+       } else {
+               arg->type = X86_IRQ_ALLOC_TYPE_MSI;
+               arg->flags |= X86_IRQ_ALLOC_CONTIGUOUS_VECTORS;
+       }
 
-       ret = apic_set_affinity(data, mask, &dest);
-       if (ret)
-               return ret;
+       return 0;
+}
 
-       __get_cached_msi_msg(data->msi_desc, &msg);
+static void pci_msi_set_desc(msi_alloc_info_t *arg, struct msi_desc *desc)
+{
+       arg->msi_hwirq = pci_msi_domain_calc_hwirq(arg->msi_dev, desc);
+}
+
+static struct msi_domain_ops pci_msi_domain_ops = {
+       .get_hwirq      = pci_msi_get_hwirq,
+       .msi_prepare    = pci_msi_prepare,
+       .set_desc       = pci_msi_set_desc,
+};
 
-       msg.data &= ~MSI_DATA_VECTOR_MASK;
-       msg.data |= MSI_DATA_VECTOR(cfg->vector);
-       msg.address_lo &= ~MSI_ADDR_DEST_ID_MASK;
-       msg.address_lo |= MSI_ADDR_DEST_ID(dest);
+static struct msi_domain_info pci_msi_domain_info = {
+       .flags          = MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
+                         MSI_FLAG_PCI_MSIX,
+       .ops            = &pci_msi_domain_ops,
+       .chip           = &pci_msi_controller,
+       .handler        = handle_edge_irq,
+       .handler_name   = "edge",
+};
 
-       __pci_write_msi_msg(data->msi_desc, &msg);
+void arch_init_msi_domain(struct irq_domain *parent)
+{
+       if (disable_apic)
+               return;
 
-       return IRQ_SET_MASK_OK_NOCOPY;
+       msi_default_domain = pci_msi_create_irq_domain(NULL,
+                                       &pci_msi_domain_info, parent);
+       if (!msi_default_domain)
+               pr_warn("failed to initialize irqdomain for MSI/MSI-x.\n");
 }
 
-/*
- * IRQ Chip for MSI PCI/PCI-X/PCI-Express Devices,
- * which implement the MSI or MSI-X Capability Structure.
- */
-static struct irq_chip msi_chip = {
-       .name                   = "PCI-MSI",
+#ifdef CONFIG_IRQ_REMAP
+static struct irq_chip pci_msi_ir_controller = {
+       .name                   = "IR-PCI-MSI",
        .irq_unmask             = pci_msi_unmask_irq,
        .irq_mask               = pci_msi_mask_irq,
-       .irq_ack                = apic_ack_edge,
-       .irq_set_affinity       = msi_set_affinity,
-       .irq_retrigger          = apic_retrigger_irq,
+       .irq_ack                = irq_chip_ack_parent,
+       .irq_retrigger          = irq_chip_retrigger_hierarchy,
+       .irq_set_vcpu_affinity  = irq_chip_set_vcpu_affinity_parent,
        .flags                  = IRQCHIP_SKIP_SET_WAKE,
 };
 
-int setup_msi_irq(struct pci_dev *dev, struct msi_desc *msidesc,
-                 unsigned int irq_base, unsigned int irq_offset)
-{
-       struct irq_chip *chip = &msi_chip;
-       struct msi_msg msg;
-       unsigned int irq = irq_base + irq_offset;
-       int ret;
-
-       ret = msi_compose_msg(dev, irq, &msg, -1);
-       if (ret < 0)
-               return ret;
-
-       irq_set_msi_desc_off(irq_base, irq_offset, msidesc);
-
-       /*
-        * MSI-X message is written per-IRQ, the offset is always 0.
-        * MSI message denotes a contiguous group of IRQs, written for 0th IRQ.
-        */
-       if (!irq_offset)
-               pci_write_msi_msg(irq, &msg);
+static struct msi_domain_info pci_msi_ir_domain_info = {
+       .flags          = MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
+                         MSI_FLAG_MULTI_PCI_MSI | MSI_FLAG_PCI_MSIX,
+       .ops            = &pci_msi_domain_ops,
+       .chip           = &pci_msi_ir_controller,
+       .handler        = handle_edge_irq,
+       .handler_name   = "edge",
+};
 
-       setup_remapped_irq(irq, irq_cfg(irq), chip);
+struct irq_domain *arch_create_msi_irq_domain(struct irq_domain *parent)
+{
+       return pci_msi_create_irq_domain(NULL, &pci_msi_ir_domain_info, parent);
+}
+#endif
 
-       irq_set_chip_and_handler_name(irq, chip, handle_edge_irq, "edge");
+#ifdef CONFIG_DMAR_TABLE
+static void dmar_msi_write_msg(struct irq_data *data, struct msi_msg *msg)
+{
+       dmar_msi_write(data->irq, msg);
+}
 
-       dev_dbg(&dev->dev, "irq %d for MSI/MSI-X\n", irq);
+static struct irq_chip dmar_msi_controller = {
+       .name                   = "DMAR-MSI",
+       .irq_unmask             = dmar_msi_unmask,
+       .irq_mask               = dmar_msi_mask,
+       .irq_ack                = irq_chip_ack_parent,
+       .irq_set_affinity       = msi_domain_set_affinity,
+       .irq_retrigger          = irq_chip_retrigger_hierarchy,
+       .irq_compose_msi_msg    = irq_msi_compose_msg,
+       .irq_write_msi_msg      = dmar_msi_write_msg,
+       .flags                  = IRQCHIP_SKIP_SET_WAKE,
+};
 
-       return 0;
+static irq_hw_number_t dmar_msi_get_hwirq(struct msi_domain_info *info,
+                                         msi_alloc_info_t *arg)
+{
+       return arg->dmar_id;
 }
 
-int native_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
+static int dmar_msi_init(struct irq_domain *domain,
+                        struct msi_domain_info *info, unsigned int virq,
+                        irq_hw_number_t hwirq, msi_alloc_info_t *arg)
 {
-       struct msi_desc *msidesc;
-       unsigned int irq;
-       int node, ret;
+       irq_domain_set_info(domain, virq, arg->dmar_id, info->chip, NULL,
+                           handle_edge_irq, arg->dmar_data, "edge");
 
-       /* Multiple MSI vectors only supported with interrupt remapping */
-       if (type == PCI_CAP_ID_MSI && nvec > 1)
-               return 1;
+       return 0;
+}
 
-       node = dev_to_node(&dev->dev);
+static struct msi_domain_ops dmar_msi_domain_ops = {
+       .get_hwirq      = dmar_msi_get_hwirq,
+       .msi_init       = dmar_msi_init,
+};
 
-       list_for_each_entry(msidesc, &dev->msi_list, list) {
-               irq = irq_alloc_hwirq(node);
-               if (!irq)
-                       return -ENOSPC;
+static struct msi_domain_info dmar_msi_domain_info = {
+       .ops            = &dmar_msi_domain_ops,
+       .chip           = &dmar_msi_controller,
+};
 
-               ret = setup_msi_irq(dev, msidesc, irq, 0);
-               if (ret < 0) {
-                       irq_free_hwirq(irq);
-                       return ret;
-               }
+static struct irq_domain *dmar_get_irq_domain(void)
+{
+       static struct irq_domain *dmar_domain;
+       static DEFINE_MUTEX(dmar_lock);
 
-       }
-       return 0;
-}
+       mutex_lock(&dmar_lock);
+       if (dmar_domain == NULL)
+               dmar_domain = msi_create_irq_domain(NULL, &dmar_msi_domain_info,
+                                                   x86_vector_domain);
+       mutex_unlock(&dmar_lock);
 
-void native_teardown_msi_irq(unsigned int irq)
-{
-       irq_free_hwirq(irq);
+       return dmar_domain;
 }
 
-#ifdef CONFIG_DMAR_TABLE
-static int
-dmar_msi_set_affinity(struct irq_data *data, const struct cpumask *mask,
-                     bool force)
+int dmar_alloc_hwirq(int id, int node, void *arg)
 {
-       struct irq_cfg *cfg = irqd_cfg(data);
-       unsigned int dest, irq = data->irq;
-       struct msi_msg msg;
-       int ret;
-
-       ret = apic_set_affinity(data, mask, &dest);
-       if (ret)
-               return ret;
+       struct irq_domain *domain = dmar_get_irq_domain();
+       struct irq_alloc_info info;
 
-       dmar_msi_read(irq, &msg);
+       if (!domain)
+               return -1;
 
-       msg.data &= ~MSI_DATA_VECTOR_MASK;
-       msg.data |= MSI_DATA_VECTOR(cfg->vector);
-       msg.address_lo &= ~MSI_ADDR_DEST_ID_MASK;
-       msg.address_lo |= MSI_ADDR_DEST_ID(dest);
-       msg.address_hi = MSI_ADDR_BASE_HI | MSI_ADDR_EXT_DEST_ID(dest);
+       init_irq_alloc_info(&info, NULL);
+       info.type = X86_IRQ_ALLOC_TYPE_DMAR;
+       info.dmar_id = id;
+       info.dmar_data = arg;
 
-       dmar_msi_write(irq, &msg);
-
-       return IRQ_SET_MASK_OK_NOCOPY;
+       return irq_domain_alloc_irqs(domain, 1, node, &info);
 }
 
-static struct irq_chip dmar_msi_type = {
-       .name                   = "DMAR_MSI",
-       .irq_unmask             = dmar_msi_unmask,
-       .irq_mask               = dmar_msi_mask,
-       .irq_ack                = apic_ack_edge,
-       .irq_set_affinity       = dmar_msi_set_affinity,
-       .irq_retrigger          = apic_retrigger_irq,
-       .flags                  = IRQCHIP_SKIP_SET_WAKE,
-};
-
-int arch_setup_dmar_msi(unsigned int irq)
+void dmar_free_hwirq(int irq)
 {
-       int ret;
-       struct msi_msg msg;
-
-       ret = msi_compose_msg(NULL, irq, &msg, -1);
-       if (ret < 0)
-               return ret;
-       dmar_msi_write(irq, &msg);
-       irq_set_chip_and_handler_name(irq, &dmar_msi_type, handle_edge_irq,
-                                     "edge");
-       return 0;
+       irq_domain_free_irqs(irq, 1);
 }
 #endif
 
@@ -231,56 +255,103 @@ int arch_setup_dmar_msi(unsigned int irq)
  * MSI message composition
  */
 #ifdef CONFIG_HPET_TIMER
+static inline int hpet_dev_id(struct irq_domain *domain)
+{
+       struct msi_domain_info *info = msi_get_domain_info(domain);
+
+       return (int)(long)info->data;
+}
 
-static int hpet_msi_set_affinity(struct irq_data *data,
-                                const struct cpumask *mask, bool force)
+static void hpet_msi_write_msg(struct irq_data *data, struct msi_msg *msg)
 {
-       struct irq_cfg *cfg = irqd_cfg(data);
-       struct msi_msg msg;
-       unsigned int dest;
-       int ret;
+       hpet_msi_write(data->handler_data, msg);
+}
 
-       ret = apic_set_affinity(data, mask, &dest);
-       if (ret)
-               return ret;
+static struct irq_chip hpet_msi_controller = {
+       .name = "HPET-MSI",
+       .irq_unmask = hpet_msi_unmask,
+       .irq_mask = hpet_msi_mask,
+       .irq_ack = irq_chip_ack_parent,
+       .irq_set_affinity = msi_domain_set_affinity,
+       .irq_retrigger = irq_chip_retrigger_hierarchy,
+       .irq_compose_msi_msg = irq_msi_compose_msg,
+       .irq_write_msi_msg = hpet_msi_write_msg,
+       .flags = IRQCHIP_SKIP_SET_WAKE,
+};
 
-       hpet_msi_read(data->handler_data, &msg);
+static irq_hw_number_t hpet_msi_get_hwirq(struct msi_domain_info *info,
+                                         msi_alloc_info_t *arg)
+{
+       return arg->hpet_index;
+}
 
-       msg.data &= ~MSI_DATA_VECTOR_MASK;
-       msg.data |= MSI_DATA_VECTOR(cfg->vector);
-       msg.address_lo &= ~MSI_ADDR_DEST_ID_MASK;
-       msg.address_lo |= MSI_ADDR_DEST_ID(dest);
+static int hpet_msi_init(struct irq_domain *domain,
+                        struct msi_domain_info *info, unsigned int virq,
+                        irq_hw_number_t hwirq, msi_alloc_info_t *arg)
+{
+       irq_set_status_flags(virq, IRQ_MOVE_PCNTXT);
+       irq_domain_set_info(domain, virq, arg->hpet_index, info->chip, NULL,
+                           handle_edge_irq, arg->hpet_data, "edge");
 
-       hpet_msi_write(data->handler_data, &msg);
+       return 0;
+}
 
-       return IRQ_SET_MASK_OK_NOCOPY;
+static void hpet_msi_free(struct irq_domain *domain,
+                         struct msi_domain_info *info, unsigned int virq)
+{
+       irq_clear_status_flags(virq, IRQ_MOVE_PCNTXT);
 }
 
-static struct irq_chip hpet_msi_type = {
-       .name = "HPET_MSI",
-       .irq_unmask = hpet_msi_unmask,
-       .irq_mask = hpet_msi_mask,
-       .irq_ack = apic_ack_edge,
-       .irq_set_affinity = hpet_msi_set_affinity,
-       .irq_retrigger = apic_retrigger_irq,
-       .flags = IRQCHIP_SKIP_SET_WAKE,
+static struct msi_domain_ops hpet_msi_domain_ops = {
+       .get_hwirq      = hpet_msi_get_hwirq,
+       .msi_init       = hpet_msi_init,
+       .msi_free       = hpet_msi_free,
+};
+
+static struct msi_domain_info hpet_msi_domain_info = {
+       .ops            = &hpet_msi_domain_ops,
+       .chip           = &hpet_msi_controller,
 };
 
-int default_setup_hpet_msi(unsigned int irq, unsigned int id)
+struct irq_domain *hpet_create_irq_domain(int hpet_id)
 {
-       struct irq_chip *chip = &hpet_msi_type;
-       struct msi_msg msg;
-       int ret;
+       struct irq_domain *parent;
+       struct irq_alloc_info info;
+       struct msi_domain_info *domain_info;
+
+       if (x86_vector_domain == NULL)
+               return NULL;
+
+       domain_info = kzalloc(sizeof(*domain_info), GFP_KERNEL);
+       if (!domain_info)
+               return NULL;
+
+       *domain_info = hpet_msi_domain_info;
+       domain_info->data = (void *)(long)hpet_id;
+
+       init_irq_alloc_info(&info, NULL);
+       info.type = X86_IRQ_ALLOC_TYPE_HPET;
+       info.hpet_id = hpet_id;
+       parent = irq_remapping_get_ir_irq_domain(&info);
+       if (parent == NULL)
+               parent = x86_vector_domain;
+       else
+               hpet_msi_controller.name = "IR-HPET-MSI";
+
+       return msi_create_irq_domain(NULL, domain_info, parent);
+}
 
-       ret = msi_compose_msg(NULL, irq, &msg, id);
-       if (ret < 0)
-               return ret;
+int hpet_assign_irq(struct irq_domain *domain, struct hpet_dev *dev,
+                   int dev_num)
+{
+       struct irq_alloc_info info;
 
-       hpet_msi_write(irq_get_handler_data(irq), &msg);
-       irq_set_status_flags(irq, IRQ_MOVE_PCNTXT);
-       setup_remapped_irq(irq, irq_cfg(irq), chip);
+       init_irq_alloc_info(&info, NULL);
+       info.type = X86_IRQ_ALLOC_TYPE_HPET;
+       info.hpet_data = dev;
+       info.hpet_id = hpet_dev_id(domain);
+       info.hpet_index = dev_num;
 
-       irq_set_chip_and_handler_name(irq, chip, handle_edge_irq, "edge");
-       return 0;
+       return irq_domain_alloc_irqs(domain, 1, NUMA_NO_NODE, &info);
 }
 #endif
index 6cedd79145813cc792c891573ac03b3e18d9e529..28eba2d38b1570c77f3b82543d6ee84686333027 100644 (file)
@@ -3,6 +3,8 @@
  *
  * Copyright (C) 1997, 1998, 1999, 2000, 2009 Ingo Molnar, Hajnalka Szabo
  *     Moved from arch/x86/kernel/apic/io_apic.c.
+ * Jiang Liu <jiang.liu@linux.intel.com>
+ *     Enable support of hierarchical irqdomains
  *
  * 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
 #include <linux/interrupt.h>
 #include <linux/init.h>
 #include <linux/compiler.h>
-#include <linux/irqdomain.h>
 #include <linux/slab.h>
+#include <asm/irqdomain.h>
 #include <asm/hw_irq.h>
 #include <asm/apic.h>
 #include <asm/i8259.h>
 #include <asm/desc.h>
 #include <asm/irq_remapping.h>
 
+struct apic_chip_data {
+       struct irq_cfg          cfg;
+       cpumask_var_t           domain;
+       cpumask_var_t           old_domain;
+       u8                      move_in_progress : 1;
+};
+
+struct irq_domain *x86_vector_domain;
 static DEFINE_RAW_SPINLOCK(vector_lock);
+static cpumask_var_t vector_cpumask;
+static struct irq_chip lapic_controller;
+#ifdef CONFIG_X86_IO_APIC
+static struct apic_chip_data *legacy_irq_data[NR_IRQS_LEGACY];
+#endif
 
 void lock_vector_lock(void)
 {
@@ -34,71 +49,59 @@ void unlock_vector_lock(void)
        raw_spin_unlock(&vector_lock);
 }
 
-struct irq_cfg *irq_cfg(unsigned int irq)
+static struct apic_chip_data *apic_chip_data(struct irq_data *irq_data)
 {
-       return irq_get_chip_data(irq);
+       if (!irq_data)
+               return NULL;
+
+       while (irq_data->parent_data)
+               irq_data = irq_data->parent_data;
+
+       return irq_data->chip_data;
 }
 
 struct irq_cfg *irqd_cfg(struct irq_data *irq_data)
 {
-       return irq_data->chip_data;
+       struct apic_chip_data *data = apic_chip_data(irq_data);
+
+       return data ? &data->cfg : NULL;
 }
 
-static struct irq_cfg *alloc_irq_cfg(unsigned int irq, int node)
+struct irq_cfg *irq_cfg(unsigned int irq)
 {
-       struct irq_cfg *cfg;
+       return irqd_cfg(irq_get_irq_data(irq));
+}
 
-       cfg = kzalloc_node(sizeof(*cfg), GFP_KERNEL, node);
-       if (!cfg)
+static struct apic_chip_data *alloc_apic_chip_data(int node)
+{
+       struct apic_chip_data *data;
+
+       data = kzalloc_node(sizeof(*data), GFP_KERNEL, node);
+       if (!data)
                return NULL;
-       if (!zalloc_cpumask_var_node(&cfg->domain, GFP_KERNEL, node))
-               goto out_cfg;
-       if (!zalloc_cpumask_var_node(&cfg->old_domain, GFP_KERNEL, node))
+       if (!zalloc_cpumask_var_node(&data->domain, GFP_KERNEL, node))
+               goto out_data;
+       if (!zalloc_cpumask_var_node(&data->old_domain, GFP_KERNEL, node))
                goto out_domain;
-#ifdef CONFIG_X86_IO_APIC
-       INIT_LIST_HEAD(&cfg->irq_2_pin);
-#endif
-       return cfg;
+       return data;
 out_domain:
-       free_cpumask_var(cfg->domain);
-out_cfg:
-       kfree(cfg);
+       free_cpumask_var(data->domain);
+out_data:
+       kfree(data);
        return NULL;
 }
 
-struct irq_cfg *alloc_irq_and_cfg_at(unsigned int at, int node)
+static void free_apic_chip_data(struct apic_chip_data *data)
 {
-       int res = irq_alloc_desc_at(at, node);
-       struct irq_cfg *cfg;
-
-       if (res < 0) {
-               if (res != -EEXIST)
-                       return NULL;
-               cfg = irq_cfg(at);
-               if (cfg)
-                       return cfg;
+       if (data) {
+               free_cpumask_var(data->domain);
+               free_cpumask_var(data->old_domain);
+               kfree(data);
        }
-
-       cfg = alloc_irq_cfg(at, node);
-       if (cfg)
-               irq_set_chip_data(at, cfg);
-       else
-               irq_free_desc(at);
-       return cfg;
-}
-
-static void free_irq_cfg(unsigned int at, struct irq_cfg *cfg)
-{
-       if (!cfg)
-               return;
-       irq_set_chip_data(at, NULL);
-       free_cpumask_var(cfg->domain);
-       free_cpumask_var(cfg->old_domain);
-       kfree(cfg);
 }
 
-static int
-__assign_irq_vector(int irq, struct irq_cfg *cfg, const struct cpumask *mask)
+static int __assign_irq_vector(int irq, struct apic_chip_data *d,
+                              const struct cpumask *mask)
 {
        /*
         * NOTE! The local APIC isn't very good at handling
@@ -114,36 +117,33 @@ __assign_irq_vector(int irq, struct irq_cfg *cfg, const struct cpumask *mask)
        static int current_vector = FIRST_EXTERNAL_VECTOR + VECTOR_OFFSET_START;
        static int current_offset = VECTOR_OFFSET_START % 16;
        int cpu, err;
-       cpumask_var_t tmp_mask;
 
-       if (cfg->move_in_progress)
+       if (d->move_in_progress)
                return -EBUSY;
 
-       if (!alloc_cpumask_var(&tmp_mask, GFP_ATOMIC))
-               return -ENOMEM;
-
        /* Only try and allocate irqs on cpus that are present */
        err = -ENOSPC;
-       cpumask_clear(cfg->old_domain);
+       cpumask_clear(d->old_domain);
        cpu = cpumask_first_and(mask, cpu_online_mask);
        while (cpu < nr_cpu_ids) {
                int new_cpu, vector, offset;
 
-               apic->vector_allocation_domain(cpu, tmp_mask, mask);
+               apic->vector_allocation_domain(cpu, vector_cpumask, mask);
 
-               if (cpumask_subset(tmp_mask, cfg->domain)) {
+               if (cpumask_subset(vector_cpumask, d->domain)) {
                        err = 0;
-                       if (cpumask_equal(tmp_mask, cfg->domain))
+                       if (cpumask_equal(vector_cpumask, d->domain))
                                break;
                        /*
                         * New cpumask using the vector is a proper subset of
                         * the current in use mask. So cleanup the vector
                         * allocation for the members that are not used anymore.
                         */
-                       cpumask_andnot(cfg->old_domain, cfg->domain, tmp_mask);
-                       cfg->move_in_progress =
-                          cpumask_intersects(cfg->old_domain, cpu_online_mask);
-                       cpumask_and(cfg->domain, cfg->domain, tmp_mask);
+                       cpumask_andnot(d->old_domain, d->domain,
+                                      vector_cpumask);
+                       d->move_in_progress =
+                          cpumask_intersects(d->old_domain, cpu_online_mask);
+                       cpumask_and(d->domain, d->domain, vector_cpumask);
                        break;
                }
 
@@ -157,16 +157,18 @@ next:
                }
 
                if (unlikely(current_vector == vector)) {
-                       cpumask_or(cfg->old_domain, cfg->old_domain, tmp_mask);
-                       cpumask_andnot(tmp_mask, mask, cfg->old_domain);
-                       cpu = cpumask_first_and(tmp_mask, cpu_online_mask);
+                       cpumask_or(d->old_domain, d->old_domain,
+                                  vector_cpumask);
+                       cpumask_andnot(vector_cpumask, mask, d->old_domain);
+                       cpu = cpumask_first_and(vector_cpumask,
+                                               cpu_online_mask);
                        continue;
                }
 
                if (test_bit(vector, used_vectors))
                        goto next;
 
-               for_each_cpu_and(new_cpu, tmp_mask, cpu_online_mask) {
+               for_each_cpu_and(new_cpu, vector_cpumask, cpu_online_mask) {
                        if (per_cpu(vector_irq, new_cpu)[vector] >
                            VECTOR_UNDEFINED)
                                goto next;
@@ -174,55 +176,73 @@ next:
                /* Found one! */
                current_vector = vector;
                current_offset = offset;
-               if (cfg->vector) {
-                       cpumask_copy(cfg->old_domain, cfg->domain);
-                       cfg->move_in_progress =
-                          cpumask_intersects(cfg->old_domain, cpu_online_mask);
+               if (d->cfg.vector) {
+                       cpumask_copy(d->old_domain, d->domain);
+                       d->move_in_progress =
+                          cpumask_intersects(d->old_domain, cpu_online_mask);
                }
-               for_each_cpu_and(new_cpu, tmp_mask, cpu_online_mask)
+               for_each_cpu_and(new_cpu, vector_cpumask, cpu_online_mask)
                        per_cpu(vector_irq, new_cpu)[vector] = irq;
-               cfg->vector = vector;
-               cpumask_copy(cfg->domain, tmp_mask);
+               d->cfg.vector = vector;
+               cpumask_copy(d->domain, vector_cpumask);
                err = 0;
                break;
        }
-       free_cpumask_var(tmp_mask);
+
+       if (!err) {
+               /* cache destination APIC IDs into cfg->dest_apicid */
+               err = apic->cpu_mask_to_apicid_and(mask, d->domain,
+                                                  &d->cfg.dest_apicid);
+       }
 
        return err;
 }
 
-int assign_irq_vector(int irq, struct irq_cfg *cfg, const struct cpumask *mask)
+static int assign_irq_vector(int irq, struct apic_chip_data *data,
+                            const struct cpumask *mask)
 {
        int err;
        unsigned long flags;
 
        raw_spin_lock_irqsave(&vector_lock, flags);
-       err = __assign_irq_vector(irq, cfg, mask);
+       err = __assign_irq_vector(irq, data, mask);
        raw_spin_unlock_irqrestore(&vector_lock, flags);
        return err;
 }
 
-void clear_irq_vector(int irq, struct irq_cfg *cfg)
+static int assign_irq_vector_policy(int irq, int node,
+                                   struct apic_chip_data *data,
+                                   struct irq_alloc_info *info)
+{
+       if (info && info->mask)
+               return assign_irq_vector(irq, data, info->mask);
+       if (node != NUMA_NO_NODE &&
+           assign_irq_vector(irq, data, cpumask_of_node(node)) == 0)
+               return 0;
+       return assign_irq_vector(irq, data, apic->target_cpus());
+}
+
+static void clear_irq_vector(int irq, struct apic_chip_data *data)
 {
        int cpu, vector;
        unsigned long flags;
 
        raw_spin_lock_irqsave(&vector_lock, flags);
-       BUG_ON(!cfg->vector);
+       BUG_ON(!data->cfg.vector);
 
-       vector = cfg->vector;
-       for_each_cpu_and(cpu, cfg->domain, cpu_online_mask)
+       vector = data->cfg.vector;
+       for_each_cpu_and(cpu, data->domain, cpu_online_mask)
                per_cpu(vector_irq, cpu)[vector] = VECTOR_UNDEFINED;
 
-       cfg->vector = 0;
-       cpumask_clear(cfg->domain);
+       data->cfg.vector = 0;
+       cpumask_clear(data->domain);
 
-       if (likely(!cfg->move_in_progress)) {
+       if (likely(!data->move_in_progress)) {
                raw_spin_unlock_irqrestore(&vector_lock, flags);
                return;
        }
 
-       for_each_cpu_and(cpu, cfg->old_domain, cpu_online_mask) {
+       for_each_cpu_and(cpu, data->old_domain, cpu_online_mask) {
                for (vector = FIRST_EXTERNAL_VECTOR; vector < NR_VECTORS;
                     vector++) {
                        if (per_cpu(vector_irq, cpu)[vector] != irq)
@@ -231,10 +251,95 @@ void clear_irq_vector(int irq, struct irq_cfg *cfg)
                        break;
                }
        }
-       cfg->move_in_progress = 0;
+       data->move_in_progress = 0;
        raw_spin_unlock_irqrestore(&vector_lock, flags);
 }
 
+void init_irq_alloc_info(struct irq_alloc_info *info,
+                        const struct cpumask *mask)
+{
+       memset(info, 0, sizeof(*info));
+       info->mask = mask;
+}
+
+void copy_irq_alloc_info(struct irq_alloc_info *dst, struct irq_alloc_info *src)
+{
+       if (src)
+               *dst = *src;
+       else
+               memset(dst, 0, sizeof(*dst));
+}
+
+static void x86_vector_free_irqs(struct irq_domain *domain,
+                                unsigned int virq, unsigned int nr_irqs)
+{
+       struct irq_data *irq_data;
+       int i;
+
+       for (i = 0; i < nr_irqs; i++) {
+               irq_data = irq_domain_get_irq_data(x86_vector_domain, virq + i);
+               if (irq_data && irq_data->chip_data) {
+                       clear_irq_vector(virq + i, irq_data->chip_data);
+                       free_apic_chip_data(irq_data->chip_data);
+#ifdef CONFIG_X86_IO_APIC
+                       if (virq + i < nr_legacy_irqs())
+                               legacy_irq_data[virq + i] = NULL;
+#endif
+                       irq_domain_reset_irq_data(irq_data);
+               }
+       }
+}
+
+static int x86_vector_alloc_irqs(struct irq_domain *domain, unsigned int virq,
+                                unsigned int nr_irqs, void *arg)
+{
+       struct irq_alloc_info *info = arg;
+       struct apic_chip_data *data;
+       struct irq_data *irq_data;
+       int i, err;
+
+       if (disable_apic)
+               return -ENXIO;
+
+       /* Currently vector allocator can't guarantee contiguous allocations */
+       if ((info->flags & X86_IRQ_ALLOC_CONTIGUOUS_VECTORS) && nr_irqs > 1)
+               return -ENOSYS;
+
+       for (i = 0; i < nr_irqs; i++) {
+               irq_data = irq_domain_get_irq_data(domain, virq + i);
+               BUG_ON(!irq_data);
+#ifdef CONFIG_X86_IO_APIC
+               if (virq + i < nr_legacy_irqs() && legacy_irq_data[virq + i])
+                       data = legacy_irq_data[virq + i];
+               else
+#endif
+                       data = alloc_apic_chip_data(irq_data->node);
+               if (!data) {
+                       err = -ENOMEM;
+                       goto error;
+               }
+
+               irq_data->chip = &lapic_controller;
+               irq_data->chip_data = data;
+               irq_data->hwirq = virq + i;
+               err = assign_irq_vector_policy(virq, irq_data->node, data,
+                                              info);
+               if (err)
+                       goto error;
+       }
+
+       return 0;
+
+error:
+       x86_vector_free_irqs(domain, virq, i + 1);
+       return err;
+}
+
+static const struct irq_domain_ops x86_vector_domain_ops = {
+       .alloc  = x86_vector_alloc_irqs,
+       .free   = x86_vector_free_irqs,
+};
+
 int __init arch_probe_nr_irqs(void)
 {
        int nr;
@@ -258,8 +363,43 @@ int __init arch_probe_nr_irqs(void)
        return nr_legacy_irqs();
 }
 
+#ifdef CONFIG_X86_IO_APIC
+static void init_legacy_irqs(void)
+{
+       int i, node = cpu_to_node(0);
+       struct apic_chip_data *data;
+
+       /*
+        * For legacy IRQ's, start with assigning irq0 to irq15 to
+        * ISA_IRQ_VECTOR(i) for all cpu's.
+        */
+       for (i = 0; i < nr_legacy_irqs(); i++) {
+               data = legacy_irq_data[i] = alloc_apic_chip_data(node);
+               BUG_ON(!data);
+
+               data->cfg.vector = ISA_IRQ_VECTOR(i);
+               cpumask_setall(data->domain);
+               irq_set_chip_data(i, data);
+       }
+}
+#else
+static void init_legacy_irqs(void) { }
+#endif
+
 int __init arch_early_irq_init(void)
 {
+       init_legacy_irqs();
+
+       x86_vector_domain = irq_domain_add_tree(NULL, &x86_vector_domain_ops,
+                                               NULL);
+       BUG_ON(x86_vector_domain == NULL);
+       irq_set_default_host(x86_vector_domain);
+
+       arch_init_msi_domain(x86_vector_domain);
+       arch_init_htirq_domain(x86_vector_domain);
+
+       BUG_ON(!alloc_cpumask_var(&vector_cpumask, GFP_KERNEL));
+
        return arch_early_ioapic_init();
 }
 
@@ -267,7 +407,7 @@ static void __setup_vector_irq(int cpu)
 {
        /* Initialize vector_irq on a new cpu */
        int irq, vector;
-       struct irq_cfg *cfg;
+       struct apic_chip_data *data;
 
        /*
         * vector_lock will make sure that we don't run into irq vector
@@ -277,13 +417,13 @@ static void __setup_vector_irq(int cpu)
        raw_spin_lock(&vector_lock);
        /* Mark the inuse vectors */
        for_each_active_irq(irq) {
-               cfg = irq_cfg(irq);
-               if (!cfg)
+               data = apic_chip_data(irq_get_irq_data(irq));
+               if (!data)
                        continue;
 
-               if (!cpumask_test_cpu(cpu, cfg->domain))
+               if (!cpumask_test_cpu(cpu, data->domain))
                        continue;
-               vector = cfg->vector;
+               vector = data->cfg.vector;
                per_cpu(vector_irq, cpu)[vector] = irq;
        }
        /* Mark the free vectors */
@@ -292,8 +432,8 @@ static void __setup_vector_irq(int cpu)
                if (irq <= VECTOR_UNDEFINED)
                        continue;
 
-               cfg = irq_cfg(irq);
-               if (!cpumask_test_cpu(cpu, cfg->domain))
+               data = apic_chip_data(irq_get_irq_data(irq));
+               if (!cpumask_test_cpu(cpu, data->domain))
                        per_cpu(vector_irq, cpu)[vector] = VECTOR_UNDEFINED;
        }
        raw_spin_unlock(&vector_lock);
@@ -314,20 +454,20 @@ void setup_vector_irq(int cpu)
         * legacy vector to irq mapping:
         */
        for (irq = 0; irq < nr_legacy_irqs(); irq++)
-               per_cpu(vector_irq, cpu)[IRQ0_VECTOR + irq] = irq;
+               per_cpu(vector_irq, cpu)[ISA_IRQ_VECTOR(irq)] = irq;
 
        __setup_vector_irq(cpu);
 }
 
-int apic_retrigger_irq(struct irq_data *data)
+static int apic_retrigger_irq(struct irq_data *irq_data)
 {
-       struct irq_cfg *cfg = irqd_cfg(data);
+       struct apic_chip_data *data = apic_chip_data(irq_data);
        unsigned long flags;
        int cpu;
 
        raw_spin_lock_irqsave(&vector_lock, flags);
-       cpu = cpumask_first_and(cfg->domain, cpu_online_mask);
-       apic->send_IPI_mask(cpumask_of(cpu), cfg->vector);
+       cpu = cpumask_first_and(data->domain, cpu_online_mask);
+       apic->send_IPI_mask(cpumask_of(cpu), data->cfg.vector);
        raw_spin_unlock_irqrestore(&vector_lock, flags);
 
        return 1;
@@ -340,73 +480,76 @@ void apic_ack_edge(struct irq_data *data)
        ack_APIC_irq();
 }
 
-/*
- * Either sets data->affinity to a valid value, and returns
- * ->cpu_mask_to_apicid of that in dest_id, or returns -1 and
- * leaves data->affinity untouched.
- */
-int apic_set_affinity(struct irq_data *data, const struct cpumask *mask,
-                     unsigned int *dest_id)
+static int apic_set_affinity(struct irq_data *irq_data,
+                            const struct cpumask *dest, bool force)
 {
-       struct irq_cfg *cfg = irqd_cfg(data);
-       unsigned int irq = data->irq;
-       int err;
+       struct apic_chip_data *data = irq_data->chip_data;
+       int err, irq = irq_data->irq;
 
        if (!config_enabled(CONFIG_SMP))
                return -EPERM;
 
-       if (!cpumask_intersects(mask, cpu_online_mask))
+       if (!cpumask_intersects(dest, cpu_online_mask))
                return -EINVAL;
 
-       err = assign_irq_vector(irq, cfg, mask);
-       if (err)
-               return err;
-
-       err = apic->cpu_mask_to_apicid_and(mask, cfg->domain, dest_id);
+       err = assign_irq_vector(irq, data, dest);
        if (err) {
-               if (assign_irq_vector(irq, cfg, data->affinity))
+               struct irq_data *top = irq_get_irq_data(irq);
+
+               if (assign_irq_vector(irq, data, top->affinity))
                        pr_err("Failed to recover vector for irq %d\n", irq);
                return err;
        }
 
-       cpumask_copy(data->affinity, mask);
-
-       return 0;
+       return IRQ_SET_MASK_OK;
 }
 
+static struct irq_chip lapic_controller = {
+       .irq_ack                = apic_ack_edge,
+       .irq_set_affinity       = apic_set_affinity,
+       .irq_retrigger          = apic_retrigger_irq,
+};
+
 #ifdef CONFIG_SMP
-void send_cleanup_vector(struct irq_cfg *cfg)
+static void __send_cleanup_vector(struct apic_chip_data *data)
 {
        cpumask_var_t cleanup_mask;
 
        if (unlikely(!alloc_cpumask_var(&cleanup_mask, GFP_ATOMIC))) {
                unsigned int i;
 
-               for_each_cpu_and(i, cfg->old_domain, cpu_online_mask)
+               for_each_cpu_and(i, data->old_domain, cpu_online_mask)
                        apic->send_IPI_mask(cpumask_of(i),
                                            IRQ_MOVE_CLEANUP_VECTOR);
        } else {
-               cpumask_and(cleanup_mask, cfg->old_domain, cpu_online_mask);
+               cpumask_and(cleanup_mask, data->old_domain, cpu_online_mask);
                apic->send_IPI_mask(cleanup_mask, IRQ_MOVE_CLEANUP_VECTOR);
                free_cpumask_var(cleanup_mask);
        }
-       cfg->move_in_progress = 0;
+       data->move_in_progress = 0;
+}
+
+void send_cleanup_vector(struct irq_cfg *cfg)
+{
+       struct apic_chip_data *data;
+
+       data = container_of(cfg, struct apic_chip_data, cfg);
+       if (data->move_in_progress)
+               __send_cleanup_vector(data);
 }
 
 asmlinkage __visible void smp_irq_move_cleanup_interrupt(void)
 {
        unsigned vector, me;
 
-       ack_APIC_irq();
-       irq_enter();
-       exit_idle();
+       entering_ack_irq();
 
        me = smp_processor_id();
        for (vector = FIRST_EXTERNAL_VECTOR; vector < NR_VECTORS; vector++) {
                int irq;
                unsigned int irr;
                struct irq_desc *desc;
-               struct irq_cfg *cfg;
+               struct apic_chip_data *data;
 
                irq = __this_cpu_read(vector_irq[vector]);
 
@@ -417,8 +560,8 @@ asmlinkage __visible void smp_irq_move_cleanup_interrupt(void)
                if (!desc)
                        continue;
 
-               cfg = irq_cfg(irq);
-               if (!cfg)
+               data = apic_chip_data(&desc->irq_data);
+               if (!data)
                        continue;
 
                raw_spin_lock(&desc->lock);
@@ -427,10 +570,11 @@ asmlinkage __visible void smp_irq_move_cleanup_interrupt(void)
                 * Check if the irq migration is in progress. If so, we
                 * haven't received the cleanup request yet for this irq.
                 */
-               if (cfg->move_in_progress)
+               if (data->move_in_progress)
                        goto unlock;
 
-               if (vector == cfg->vector && cpumask_test_cpu(me, cfg->domain))
+               if (vector == data->cfg.vector &&
+                   cpumask_test_cpu(me, data->domain))
                        goto unlock;
 
                irr = apic_read(APIC_IRR + (vector / 32 * 0x10));
@@ -450,20 +594,21 @@ unlock:
                raw_spin_unlock(&desc->lock);
        }
 
-       irq_exit();
+       exiting_irq();
 }
 
 static void __irq_complete_move(struct irq_cfg *cfg, unsigned vector)
 {
        unsigned me;
+       struct apic_chip_data *data;
 
-       if (likely(!cfg->move_in_progress))
+       data = container_of(cfg, struct apic_chip_data, cfg);
+       if (likely(!data->move_in_progress))
                return;
 
        me = smp_processor_id();
-
-       if (vector == cfg->vector && cpumask_test_cpu(me, cfg->domain))
-               send_cleanup_vector(cfg);
+       if (vector == data->cfg.vector && cpumask_test_cpu(me, data->domain))
+               __send_cleanup_vector(data);
 }
 
 void irq_complete_move(struct irq_cfg *cfg)
@@ -475,46 +620,11 @@ void irq_force_complete_move(int irq)
 {
        struct irq_cfg *cfg = irq_cfg(irq);
 
-       if (!cfg)
-               return;
-
-       __irq_complete_move(cfg, cfg->vector);
+       if (cfg)
+               __irq_complete_move(cfg, cfg->vector);
 }
 #endif
 
-/*
- * Dynamic irq allocate and deallocation. Should be replaced by irq domains!
- */
-int arch_setup_hwirq(unsigned int irq, int node)
-{
-       struct irq_cfg *cfg;
-       unsigned long flags;
-       int ret;
-
-       cfg = alloc_irq_cfg(irq, node);
-       if (!cfg)
-               return -ENOMEM;
-
-       raw_spin_lock_irqsave(&vector_lock, flags);
-       ret = __assign_irq_vector(irq, cfg, apic->target_cpus());
-       raw_spin_unlock_irqrestore(&vector_lock, flags);
-
-       if (!ret)
-               irq_set_chip_data(irq, cfg);
-       else
-               free_irq_cfg(irq, cfg);
-       return ret;
-}
-
-void arch_teardown_hwirq(unsigned int irq)
-{
-       struct irq_cfg *cfg = irq_cfg(irq);
-
-       free_remapped_irq(irq);
-       clear_irq_vector(irq, cfg);
-       free_irq_cfg(irq, cfg);
-}
-
 static void __init print_APIC_field(int base)
 {
        int i;
index 6fae733e9194893dc761ccd06839df81232c7427..3ffd925655e0d4a8338d401d2793a61a5cb3fd6f 100644 (file)
@@ -21,11 +21,13 @@ early_param("x2apic_phys", set_x2apic_phys_mode);
 
 static bool x2apic_fadt_phys(void)
 {
+#ifdef CONFIG_ACPI
        if ((acpi_gbl_FADT.header.revision >= FADT2_REVISION_ID) &&
                (acpi_gbl_FADT.flags & ACPI_FADT_APIC_PHYSICAL)) {
                printk(KERN_DEBUG "System requires x2apic physical mode\n");
                return true;
        }
+#endif
        return false;
 }
 
index 9f6b9341950f7895b247b1c320cc0f9cd75cda9d..8e3d22a1af94094b749abca95187b073403d628c 100644 (file)
@@ -41,6 +41,25 @@ void common(void) {
        OFFSET(pbe_orig_address, pbe, orig_address);
        OFFSET(pbe_next, pbe, next);
 
+#if defined(CONFIG_X86_32) || defined(CONFIG_IA32_EMULATION)
+       BLANK();
+       OFFSET(IA32_SIGCONTEXT_ax, sigcontext_ia32, ax);
+       OFFSET(IA32_SIGCONTEXT_bx, sigcontext_ia32, bx);
+       OFFSET(IA32_SIGCONTEXT_cx, sigcontext_ia32, cx);
+       OFFSET(IA32_SIGCONTEXT_dx, sigcontext_ia32, dx);
+       OFFSET(IA32_SIGCONTEXT_si, sigcontext_ia32, si);
+       OFFSET(IA32_SIGCONTEXT_di, sigcontext_ia32, di);
+       OFFSET(IA32_SIGCONTEXT_bp, sigcontext_ia32, bp);
+       OFFSET(IA32_SIGCONTEXT_sp, sigcontext_ia32, sp);
+       OFFSET(IA32_SIGCONTEXT_ip, sigcontext_ia32, ip);
+
+       BLANK();
+       OFFSET(TI_sysenter_return, thread_info, sysenter_return);
+
+       BLANK();
+       OFFSET(IA32_RT_SIGFRAME_sigcontext, rt_sigframe_ia32, uc.uc_mcontext);
+#endif
+
 #ifdef CONFIG_PARAVIRT
        BLANK();
        OFFSET(PARAVIRT_enabled, pv_info, paravirt_enabled);
@@ -49,7 +68,9 @@ void common(void) {
        OFFSET(PV_IRQ_irq_disable, pv_irq_ops, irq_disable);
        OFFSET(PV_IRQ_irq_enable, pv_irq_ops, irq_enable);
        OFFSET(PV_CPU_iret, pv_cpu_ops, iret);
+#ifdef CONFIG_X86_32
        OFFSET(PV_CPU_irq_enable_sysexit, pv_cpu_ops, irq_enable_sysexit);
+#endif
        OFFSET(PV_CPU_read_cr0, pv_cpu_ops, read_cr0);
        OFFSET(PV_MMU_read_cr2, pv_mmu_ops, read_cr2);
 #endif
index 47703aed74cfb7d013b2d577488d68c7280bf8c6..6ce39025f467fb060ce68c8b8ebfaf8f87258d09 100644 (file)
@@ -17,17 +17,6 @@ void foo(void);
 
 void foo(void)
 {
-       OFFSET(IA32_SIGCONTEXT_ax, sigcontext, ax);
-       OFFSET(IA32_SIGCONTEXT_bx, sigcontext, bx);
-       OFFSET(IA32_SIGCONTEXT_cx, sigcontext, cx);
-       OFFSET(IA32_SIGCONTEXT_dx, sigcontext, dx);
-       OFFSET(IA32_SIGCONTEXT_si, sigcontext, si);
-       OFFSET(IA32_SIGCONTEXT_di, sigcontext, di);
-       OFFSET(IA32_SIGCONTEXT_bp, sigcontext, bp);
-       OFFSET(IA32_SIGCONTEXT_sp, sigcontext, sp);
-       OFFSET(IA32_SIGCONTEXT_ip, sigcontext, ip);
-       BLANK();
-
        OFFSET(CPUINFO_x86, cpuinfo_x86, x86);
        OFFSET(CPUINFO_x86_vendor, cpuinfo_x86, x86_vendor);
        OFFSET(CPUINFO_x86_model, cpuinfo_x86, x86_model);
@@ -37,10 +26,6 @@ void foo(void)
        OFFSET(CPUINFO_x86_vendor_id, cpuinfo_x86, x86_vendor_id);
        BLANK();
 
-       OFFSET(TI_sysenter_return, thread_info, sysenter_return);
-       OFFSET(TI_cpu, thread_info, cpu);
-       BLANK();
-
        OFFSET(PT_EBX, pt_regs, bx);
        OFFSET(PT_ECX, pt_regs, cx);
        OFFSET(PT_EDX, pt_regs, dx);
@@ -60,9 +45,6 @@ void foo(void)
        OFFSET(PT_OLDSS,  pt_regs, ss);
        BLANK();
 
-       OFFSET(IA32_RT_SIGFRAME_sigcontext, rt_sigframe, uc.uc_mcontext);
-       BLANK();
-
        OFFSET(saved_context_gdt_desc, saved_context, gdt_desc);
        BLANK();
 
index 5ce6f2da87639c7d373879e43120c9035ba590b9..d8f42f902a0f6a6f8d3d5fbeebad623fc729a906 100644 (file)
@@ -29,27 +29,6 @@ int main(void)
        BLANK();
 #endif
 
-#ifdef CONFIG_IA32_EMULATION
-       OFFSET(TI_sysenter_return, thread_info, sysenter_return);
-       BLANK();
-
-#define ENTRY(entry) OFFSET(IA32_SIGCONTEXT_ ## entry, sigcontext_ia32, entry)
-       ENTRY(ax);
-       ENTRY(bx);
-       ENTRY(cx);
-       ENTRY(dx);
-       ENTRY(si);
-       ENTRY(di);
-       ENTRY(bp);
-       ENTRY(sp);
-       ENTRY(ip);
-       BLANK();
-#undef ENTRY
-
-       OFFSET(IA32_RT_SIGFRAME_sigcontext, rt_sigframe_ia32, uc.uc_mcontext);
-       BLANK();
-#endif
-
 #define ENTRY(entry) OFFSET(pt_regs_ ## entry, pt_regs, entry)
        ENTRY(bx);
        ENTRY(cx);
@@ -87,7 +66,7 @@ int main(void)
        DEFINE(__NR_syscall_max, sizeof(syscalls_64) - 1);
        DEFINE(NR_syscalls, sizeof(syscalls_64));
 
-       DEFINE(__NR_ia32_syscall_max, sizeof(syscalls_ia32) - 1);
+       DEFINE(__NR_syscall_compat_max, sizeof(syscalls_ia32) - 1);
        DEFINE(IA32_NR_syscalls, sizeof(syscalls_ia32));
 
        return 0;
index e4cf63301ff439ed390e50a81a72fef9ea50e23a..dd3a4baffe50cca6595a57755e17c7d284ee999c 100644 (file)
 
 #include "cpu.h"
 
+/*
+ * nodes_per_socket: Stores the number of nodes per socket.
+ * Refer to Fam15h Models 00-0fh BKDG - CPUID Fn8000_001E_ECX
+ * Node Identifiers[10:8]
+ */
+static u32 nodes_per_socket = 1;
+
 static inline int rdmsrl_amd_safe(unsigned msr, unsigned long long *p)
 {
        u32 gprs[8] = { 0 };
@@ -288,10 +295,10 @@ static int nearby_node(int apicid)
  *     Assumption: Number of cores in each internal node is the same.
  * (2) AMD processors supporting compute units
  */
-#ifdef CONFIG_X86_HT
+#ifdef CONFIG_SMP
 static void amd_get_topology(struct cpuinfo_x86 *c)
 {
-       u32 nodes, cores_per_cu = 1;
+       u32 cores_per_cu = 1;
        u8 node_id;
        int cpu = smp_processor_id();
 
@@ -300,7 +307,7 @@ static void amd_get_topology(struct cpuinfo_x86 *c)
                u32 eax, ebx, ecx, edx;
 
                cpuid(0x8000001e, &eax, &ebx, &ecx, &edx);
-               nodes = ((ecx >> 8) & 7) + 1;
+               nodes_per_socket = ((ecx >> 8) & 7) + 1;
                node_id = ecx & 7;
 
                /* get compute unit information */
@@ -311,18 +318,18 @@ static void amd_get_topology(struct cpuinfo_x86 *c)
                u64 value;
 
                rdmsrl(MSR_FAM10H_NODE_ID, value);
-               nodes = ((value >> 3) & 7) + 1;
+               nodes_per_socket = ((value >> 3) & 7) + 1;
                node_id = value & 7;
        } else
                return;
 
        /* fixup multi-node processor information */
-       if (nodes > 1) {
+       if (nodes_per_socket > 1) {
                u32 cores_per_node;
                u32 cus_per_node;
 
                set_cpu_cap(c, X86_FEATURE_AMD_DCM);
-               cores_per_node = c->x86_max_cores / nodes;
+               cores_per_node = c->x86_max_cores / nodes_per_socket;
                cus_per_node = cores_per_node / cores_per_cu;
 
                /* store NodeID, use llc_shared_map to store sibling info */
@@ -341,7 +348,7 @@ static void amd_get_topology(struct cpuinfo_x86 *c)
  */
 static void amd_detect_cmp(struct cpuinfo_x86 *c)
 {
-#ifdef CONFIG_X86_HT
+#ifdef CONFIG_SMP
        unsigned bits;
        int cpu = smp_processor_id();
 
@@ -366,6 +373,12 @@ u16 amd_get_nb_id(int cpu)
 }
 EXPORT_SYMBOL_GPL(amd_get_nb_id);
 
+u32 amd_get_nodes_per_socket(void)
+{
+       return nodes_per_socket;
+}
+EXPORT_SYMBOL_GPL(amd_get_nodes_per_socket);
+
 static void srat_detect_node(struct cpuinfo_x86 *c)
 {
 #ifdef CONFIG_NUMA
@@ -420,7 +433,7 @@ static void srat_detect_node(struct cpuinfo_x86 *c)
 
 static void early_init_amd_mc(struct cpuinfo_x86 *c)
 {
-#ifdef CONFIG_X86_HT
+#ifdef CONFIG_SMP
        unsigned bits, ecx;
 
        /* Multi core CPU? */
@@ -520,8 +533,16 @@ static void early_init_amd(struct cpuinfo_x86 *c)
                        set_cpu_cap(c, X86_FEATURE_K6_MTRR);
 #endif
 #if defined(CONFIG_X86_LOCAL_APIC) && defined(CONFIG_PCI)
-       /* check CPU config space for extended APIC ID */
-       if (cpu_has_apic && c->x86 >= 0xf) {
+       /*
+        * ApicID can always be treated as an 8-bit value for AMD APIC versions
+        * >= 0x10, but even old K8s came out of reset with version 0x10. So, we
+        * can safely set X86_FEATURE_EXTD_APICID unconditionally for families
+        * after 16h.
+        */
+       if (cpu_has_apic && c->x86 > 0x16) {
+               set_cpu_cap(c, X86_FEATURE_EXTD_APICID);
+       } else if (cpu_has_apic && c->x86 >= 0xf) {
+               /* check CPU config space for extended APIC ID */
                unsigned int val;
                val = read_pci_config(0, 24, 0, 0x68);
                if ((val & ((1 << 17) | (1 << 18))) == ((1 << 17) | (1 << 18)))
index 03445346ee0aae247f31ebf2aef6a48be0dfce8a..bd17db15a2c1ef07412c73f064f282a3f55c9313 100644 (file)
 #include <asm/bugs.h>
 #include <asm/processor.h>
 #include <asm/processor-flags.h>
-#include <asm/i387.h>
+#include <asm/fpu/internal.h>
 #include <asm/msr.h>
 #include <asm/paravirt.h>
 #include <asm/alternative.h>
 
-static double __initdata x = 4195835.0;
-static double __initdata y = 3145727.0;
-
-/*
- * This used to check for exceptions..
- * However, it turns out that to support that,
- * the XMM trap handlers basically had to
- * be buggy. So let's have a correct XMM trap
- * handler, and forget about printing out
- * some status at boot.
- *
- * We should really only care about bugs here
- * anyway. Not features.
- */
-static void __init check_fpu(void)
-{
-       s32 fdiv_bug;
-
-       kernel_fpu_begin();
-
-       /*
-        * trap_init() enabled FXSR and company _before_ testing for FP
-        * problems here.
-        *
-        * Test for the divl bug: http://en.wikipedia.org/wiki/Fdiv_bug
-        */
-       __asm__("fninit\n\t"
-               "fldl %1\n\t"
-               "fdivl %2\n\t"
-               "fmull %2\n\t"
-               "fldl %1\n\t"
-               "fsubp %%st,%%st(1)\n\t"
-               "fistpl %0\n\t"
-               "fwait\n\t"
-               "fninit"
-               : "=m" (*&fdiv_bug)
-               : "m" (*&x), "m" (*&y));
-
-       kernel_fpu_end();
-
-       if (fdiv_bug) {
-               set_cpu_bug(&boot_cpu_data, X86_BUG_FDIV);
-               pr_warn("Hmm, FPU with FDIV bug\n");
-       }
-}
-
 void __init check_bugs(void)
 {
        identify_boot_cpu();
@@ -85,10 +39,5 @@ void __init check_bugs(void)
                '0' + (boot_cpu_data.x86 > 6 ? 6 : boot_cpu_data.x86);
        alternative_instructions();
 
-       /*
-        * kernel_fpu_begin/end() in check_fpu() relies on the patched
-        * alternative instructions.
-        */
-       if (cpu_has_fpu)
-               check_fpu();
+       fpu__init_check_bugs();
 }
index a62cf04dac8ae99d1310b02b827bca528311d490..9fc5e3d9d9c8390f4c9bb177449979061f20bb14 100644 (file)
@@ -5,6 +5,7 @@
 #include <linux/module.h>
 #include <linux/percpu.h>
 #include <linux/string.h>
+#include <linux/ctype.h>
 #include <linux/delay.h>
 #include <linux/sched.h>
 #include <linux/init.h>
@@ -31,8 +32,7 @@
 #include <asm/setup.h>
 #include <asm/apic.h>
 #include <asm/desc.h>
-#include <asm/i387.h>
-#include <asm/fpu-internal.h>
+#include <asm/fpu/internal.h>
 #include <asm/mtrr.h>
 #include <linux/numa.h>
 #include <asm/asm.h>
@@ -145,32 +145,21 @@ DEFINE_PER_CPU_PAGE_ALIGNED(struct gdt_page, gdt_page) = { .gdt = {
 } };
 EXPORT_PER_CPU_SYMBOL_GPL(gdt_page);
 
-static int __init x86_xsave_setup(char *s)
+static int __init x86_mpx_setup(char *s)
 {
+       /* require an exact match without trailing characters */
        if (strlen(s))
                return 0;
-       setup_clear_cpu_cap(X86_FEATURE_XSAVE);
-       setup_clear_cpu_cap(X86_FEATURE_XSAVEOPT);
-       setup_clear_cpu_cap(X86_FEATURE_XSAVES);
-       setup_clear_cpu_cap(X86_FEATURE_AVX);
-       setup_clear_cpu_cap(X86_FEATURE_AVX2);
-       return 1;
-}
-__setup("noxsave", x86_xsave_setup);
 
-static int __init x86_xsaveopt_setup(char *s)
-{
-       setup_clear_cpu_cap(X86_FEATURE_XSAVEOPT);
-       return 1;
-}
-__setup("noxsaveopt", x86_xsaveopt_setup);
+       /* do not emit a message if the feature is not present */
+       if (!boot_cpu_has(X86_FEATURE_MPX))
+               return 1;
 
-static int __init x86_xsaves_setup(char *s)
-{
-       setup_clear_cpu_cap(X86_FEATURE_XSAVES);
+       setup_clear_cpu_cap(X86_FEATURE_MPX);
+       pr_info("nompx: Intel Memory Protection Extensions (MPX) disabled\n");
        return 1;
 }
-__setup("noxsaves", x86_xsaves_setup);
+__setup("nompx", x86_mpx_setup);
 
 #ifdef CONFIG_X86_32
 static int cachesize_override = -1;
@@ -183,14 +172,6 @@ static int __init cachesize_setup(char *str)
 }
 __setup("cachesize=", cachesize_setup);
 
-static int __init x86_fxsr_setup(char *s)
-{
-       setup_clear_cpu_cap(X86_FEATURE_FXSR);
-       setup_clear_cpu_cap(X86_FEATURE_XMM);
-       return 1;
-}
-__setup("nofxsr", x86_fxsr_setup);
-
 static int __init x86_sep_setup(char *s)
 {
        setup_clear_cpu_cap(X86_FEATURE_SEP);
@@ -419,7 +400,7 @@ static const struct cpu_dev *cpu_devs[X86_VENDOR_NUM] = {};
 static void get_model_name(struct cpuinfo_x86 *c)
 {
        unsigned int *v;
-       char *p, *q;
+       char *p, *q, *s;
 
        if (c->extended_cpuid_level < 0x80000004)
                return;
@@ -430,19 +411,21 @@ static void get_model_name(struct cpuinfo_x86 *c)
        cpuid(0x80000004, &v[8], &v[9], &v[10], &v[11]);
        c->x86_model_id[48] = 0;
 
-       /*
-        * Intel chips right-justify this string for some dumb reason;
-        * undo that brain damage:
-        */
-       p = q = &c->x86_model_id[0];
+       /* Trim whitespace */
+       p = q = s = &c->x86_model_id[0];
+
        while (*p == ' ')
                p++;
-       if (p != q) {
-               while (*p)
-                       *q++ = *p++;
-               while (q <= &c->x86_model_id[48])
-                       *q++ = '\0';    /* Zero-pad the rest */
+
+       while (*p) {
+               /* Note the last non-whitespace index */
+               if (!isspace(*p))
+                       s = q;
+
+               *q++ = *p++;
        }
+
+       *(s + 1) = '\0';
 }
 
 void cpu_detect_cache_sizes(struct cpuinfo_x86 *c)
@@ -508,7 +491,7 @@ static void cpu_detect_tlb(struct cpuinfo_x86 *c)
 
 void detect_ht(struct cpuinfo_x86 *c)
 {
-#ifdef CONFIG_X86_HT
+#ifdef CONFIG_SMP
        u32 eax, ebx, ecx, edx;
        int index_msb, core_bits;
        static bool printed;
@@ -759,7 +742,7 @@ static void __init early_identify_cpu(struct cpuinfo_x86 *c)
        cpu_detect(c);
        get_cpu_vendor(c);
        get_cpu_cap(c);
-       fpu_detect(c);
+       fpu__init_system(c);
 
        if (this_cpu->c_early_init)
                this_cpu->c_early_init(c);
@@ -844,7 +827,7 @@ static void generic_identify(struct cpuinfo_x86 *c)
        if (c->cpuid_level >= 0x00000001) {
                c->initial_apicid = (cpuid_ebx(1) >> 24) & 0xFF;
 #ifdef CONFIG_X86_32
-# ifdef CONFIG_X86_HT
+# ifdef CONFIG_SMP
                c->apicid = apic->phys_pkg_id(c->initial_apicid, 0);
 # else
                c->apicid = c->initial_apicid;
@@ -1026,7 +1009,7 @@ void enable_sep_cpu(void)
              (unsigned long)tss + offsetofend(struct tss_struct, SYSENTER_stack),
              0);
 
-       wrmsr(MSR_IA32_SYSENTER_EIP, (unsigned long)ia32_sysenter_target, 0);
+       wrmsr(MSR_IA32_SYSENTER_EIP, (unsigned long)entry_SYSENTER_32, 0);
 
 out:
        put_cpu();
@@ -1122,7 +1105,7 @@ void print_cpu_info(struct cpuinfo_x86 *c)
                printk(KERN_CONT "%s ", vendor);
 
        if (c->x86_model_id[0])
-               printk(KERN_CONT "%s", strim(c->x86_model_id));
+               printk(KERN_CONT "%s", c->x86_model_id);
        else
                printk(KERN_CONT "%d86", c->x86);
 
@@ -1155,10 +1138,6 @@ static __init int setup_disablecpuid(char *arg)
 }
 __setup("clearcpuid=", setup_disablecpuid);
 
-DEFINE_PER_CPU(unsigned long, kernel_stack) =
-       (unsigned long)&init_thread_union + THREAD_SIZE;
-EXPORT_PER_CPU_SYMBOL(kernel_stack);
-
 #ifdef CONFIG_X86_64
 struct desc_ptr idt_descr = { NR_VECTORS * 16 - 1, (unsigned long) idt_table };
 struct desc_ptr debug_idt_descr = { NR_VECTORS * 16 - 1,
@@ -1183,8 +1162,6 @@ DEFINE_PER_CPU(unsigned int, irq_count) __visible = -1;
 DEFINE_PER_CPU(int, __preempt_count) = INIT_PREEMPT_COUNT;
 EXPORT_PER_CPU_SYMBOL(__preempt_count);
 
-DEFINE_PER_CPU(struct task_struct *, fpu_owner_task);
-
 /*
  * Special IST stacks which the CPU switches to when it calls
  * an IST-marked descriptor entry. Up to 7 stacks (hardware
@@ -1208,10 +1185,10 @@ void syscall_init(void)
         * set CS/DS but only a 32bit target. LSTAR sets the 64bit rip.
         */
        wrmsrl(MSR_STAR,  ((u64)__USER32_CS)<<48  | ((u64)__KERNEL_CS)<<32);
-       wrmsrl(MSR_LSTAR, system_call);
+       wrmsrl(MSR_LSTAR, entry_SYSCALL_64);
 
 #ifdef CONFIG_IA32_EMULATION
-       wrmsrl(MSR_CSTAR, ia32_cstar_target);
+       wrmsrl(MSR_CSTAR, entry_SYSCALL_compat);
        /*
         * This only works on Intel CPUs.
         * On AMD CPUs these MSRs are 32-bit, CPU truncates MSR_IA32_SYSENTER_EIP.
@@ -1220,7 +1197,7 @@ void syscall_init(void)
         */
        wrmsrl_safe(MSR_IA32_SYSENTER_CS, (u64)__KERNEL_CS);
        wrmsrl_safe(MSR_IA32_SYSENTER_ESP, 0ULL);
-       wrmsrl_safe(MSR_IA32_SYSENTER_EIP, (u64)ia32_sysenter_target);
+       wrmsrl_safe(MSR_IA32_SYSENTER_EIP, (u64)entry_SYSENTER_compat);
 #else
        wrmsrl(MSR_CSTAR, ignore_sysret);
        wrmsrl_safe(MSR_IA32_SYSENTER_CS, (u64)GDT_ENTRY_INVALID_SEG);
@@ -1275,7 +1252,6 @@ DEFINE_PER_CPU(struct task_struct *, current_task) = &init_task;
 EXPORT_PER_CPU_SYMBOL(current_task);
 DEFINE_PER_CPU(int, __preempt_count) = INIT_PREEMPT_COUNT;
 EXPORT_PER_CPU_SYMBOL(__preempt_count);
-DEFINE_PER_CPU(struct task_struct *, fpu_owner_task);
 
 /*
  * On x86_32, vm86 modifies tss.sp0, so sp0 isn't a reliable way to find
@@ -1439,7 +1415,7 @@ void cpu_init(void)
        clear_all_debug_regs();
        dbg_restore_debug_regs();
 
-       fpu_init();
+       fpu__init_cpu();
 
        if (is_uv_system())
                uv_cpu_init();
@@ -1495,7 +1471,7 @@ void cpu_init(void)
        clear_all_debug_regs();
        dbg_restore_debug_regs();
 
-       fpu_init();
+       fpu__init_cpu();
 }
 #endif
 
index edcb0e28c336d085d0ee1011793c98ba6f5a13ae..be4febc58b9443372e4df5037e357789de5184fe 100644 (file)
@@ -654,7 +654,7 @@ unsigned int init_intel_cacheinfo(struct cpuinfo_x86 *c)
        unsigned int new_l1d = 0, new_l1i = 0; /* Cache sizes from cpuid(4) */
        unsigned int new_l2 = 0, new_l3 = 0, i; /* Cache sizes from cpuid(4) */
        unsigned int l2_id = 0, l3_id = 0, num_threads_sharing, index_msb;
-#ifdef CONFIG_X86_HT
+#ifdef CONFIG_SMP
        unsigned int cpu = c->cpu_index;
 #endif
 
@@ -773,19 +773,19 @@ unsigned int init_intel_cacheinfo(struct cpuinfo_x86 *c)
 
        if (new_l2) {
                l2 = new_l2;
-#ifdef CONFIG_X86_HT
+#ifdef CONFIG_SMP
                per_cpu(cpu_llc_id, cpu) = l2_id;
 #endif
        }
 
        if (new_l3) {
                l3 = new_l3;
-#ifdef CONFIG_X86_HT
+#ifdef CONFIG_SMP
                per_cpu(cpu_llc_id, cpu) = l3_id;
 #endif
        }
 
-#ifdef CONFIG_X86_HT
+#ifdef CONFIG_SMP
        /*
         * If cpu_llc_id is not yet set, this means cpuid_level < 4 which in
         * turns means that the only possibility is SMT (as indicated in
index 20190bdac9d58ecabddd3cbe6692d0f52bb687ad..df919ff103c3ae845e727388765ebc1842df0cbc 100644 (file)
 static DEFINE_MUTEX(mce_chrdev_read_mutex);
 
 #define rcu_dereference_check_mce(p) \
-       rcu_dereference_index_check((p), \
-                             rcu_read_lock_sched_held() || \
-                             lockdep_is_held(&mce_chrdev_read_mutex))
+({ \
+       rcu_lockdep_assert(rcu_read_lock_sched_held() || \
+                          lockdep_is_held(&mce_chrdev_read_mutex), \
+                          "suspicious rcu_dereference_check_mce() usage"); \
+       smp_load_acquire(&(p)); \
+})
 
 #define CREATE_TRACE_POINTS
 #include <trace/events/mce.h>
@@ -1050,6 +1053,7 @@ void do_machine_check(struct pt_regs *regs, long error_code)
        char *msg = "Unknown";
        u64 recover_paddr = ~0ull;
        int flags = MF_ACTION_REQUIRED;
+       int lmce = 0;
 
        prev_state = ist_enter(regs);
 
@@ -1077,11 +1081,20 @@ void do_machine_check(struct pt_regs *regs, long error_code)
                kill_it = 1;
 
        /*
-        * Go through all the banks in exclusion of the other CPUs.
-        * This way we don't report duplicated events on shared banks
-        * because the first one to see it will clear it.
+        * Check if this MCE is signaled to only this logical processor
         */
-       order = mce_start(&no_way_out);
+       if (m.mcgstatus & MCG_STATUS_LMCES)
+               lmce = 1;
+       else {
+               /*
+                * Go through all the banks in exclusion of the other CPUs.
+                * This way we don't report duplicated events on shared banks
+                * because the first one to see it will clear it.
+                * If this is a Local MCE, then no need to perform rendezvous.
+                */
+               order = mce_start(&no_way_out);
+       }
+
        for (i = 0; i < cfg->banks; i++) {
                __clear_bit(i, toclear);
                if (!test_bit(i, valid_banks))
@@ -1158,8 +1171,18 @@ void do_machine_check(struct pt_regs *regs, long error_code)
         * Do most of the synchronization with other CPUs.
         * When there's any problem use only local no_way_out state.
         */
-       if (mce_end(order) < 0)
-               no_way_out = worst >= MCE_PANIC_SEVERITY;
+       if (!lmce) {
+               if (mce_end(order) < 0)
+                       no_way_out = worst >= MCE_PANIC_SEVERITY;
+       } else {
+               /*
+                * Local MCE skipped calling mce_reign()
+                * If we found a fatal error, we need to panic here.
+                */
+                if (worst >= MCE_PANIC_SEVERITY && mca_cfg.tolerant < 3)
+                       mce_panic("Machine check from unknown source",
+                               NULL, NULL);
+       }
 
        /*
         * At insane "tolerant" levels we take no action. Otherwise
@@ -1640,10 +1663,16 @@ static void __mcheck_cpu_init_vendor(struct cpuinfo_x86 *c)
                mce_intel_feature_init(c);
                mce_adjust_timer = cmci_intel_adjust_timer;
                break;
-       case X86_VENDOR_AMD:
+
+       case X86_VENDOR_AMD: {
+               u32 ebx = cpuid_ebx(0x80000007);
+
                mce_amd_feature_init(c);
-               mce_flags.overflow_recov = cpuid_ebx(0x80000007) & 0x1;
+               mce_flags.overflow_recov = !!(ebx & BIT(0));
+               mce_flags.succor         = !!(ebx & BIT(1));
                break;
+               }
+
        default:
                break;
        }
@@ -1887,7 +1916,7 @@ out:
 static unsigned int mce_chrdev_poll(struct file *file, poll_table *wait)
 {
        poll_wait(file, &mce_chrdev_wait, wait);
-       if (rcu_access_index(mcelog.next))
+       if (READ_ONCE(mcelog.next))
                return POLLIN | POLLRDNORM;
        if (!mce_apei_read_done && apei_check_mce())
                return POLLIN | POLLRDNORM;
@@ -1932,8 +1961,8 @@ void register_mce_write_callback(ssize_t (*fn)(struct file *filp,
 }
 EXPORT_SYMBOL_GPL(register_mce_write_callback);
 
-ssize_t mce_chrdev_write(struct file *filp, const char __user *ubuf,
-                        size_t usize, loff_t *off)
+static ssize_t mce_chrdev_write(struct file *filp, const char __user *ubuf,
+                               size_t usize, loff_t *off)
 {
        if (mce_write)
                return mce_write(filp, ubuf, usize, off);
@@ -1979,6 +2008,7 @@ void mce_disable_bank(int bank)
 /*
  * mce=off Disables machine check
  * mce=no_cmci Disables CMCI
+ * mce=no_lmce Disables LMCE
  * mce=dont_log_ce Clears corrected events silently, no log created for CEs.
  * mce=ignore_ce Disables polling and CMCI, corrected events are not cleared.
  * mce=TOLERANCELEVEL[,monarchtimeout] (number, see above)
@@ -2002,6 +2032,8 @@ static int __init mcheck_enable(char *str)
                cfg->disabled = true;
        else if (!strcmp(str, "no_cmci"))
                cfg->cmci_disabled = true;
+       else if (!strcmp(str, "no_lmce"))
+               cfg->lmce_disabled = true;
        else if (!strcmp(str, "dont_log_ce"))
                cfg->dont_log_ce = true;
        else if (!strcmp(str, "ignore_ce"))
@@ -2011,11 +2043,8 @@ static int __init mcheck_enable(char *str)
        else if (!strcmp(str, "bios_cmci_threshold"))
                cfg->bios_cmci_threshold = true;
        else if (isdigit(str[0])) {
-               get_option(&str, &(cfg->tolerant));
-               if (*str == ',') {
-                       ++str;
+               if (get_option(&str, &cfg->tolerant) == 2)
                        get_option(&str, &(cfg->monarch_timeout));
-               }
        } else {
                pr_info("mce argument %s ignored. Please use /sys\n", str);
                return 0;
index 55ad9b37cae853ce0d50f193dc7eb82a49207fee..e99b15077e9464b9c9f337873ae58101285e3215 100644 (file)
@@ -1,19 +1,13 @@
 /*
- *  (c) 2005-2012 Advanced Micro Devices, Inc.
+ *  (c) 2005-2015 Advanced Micro Devices, Inc.
  *  Your use of this code is subject to the terms and conditions of the
  *  GNU general public license version 2. See "COPYING" or
  *  http://www.gnu.org/licenses/gpl.html
  *
  *  Written by Jacob Shin - AMD, Inc.
- *
  *  Maintained by: Borislav Petkov <bp@alien8.de>
  *
- *  April 2006
- *     - added support for AMD Family 0x10 processors
- *  May 2012
- *     - major scrubbing
- *
- *  All MC4_MISCi registers are shared between multi-cores
+ *  All MC4_MISCi registers are shared between cores on a node.
  */
 #include <linux/interrupt.h>
 #include <linux/notifier.h>
@@ -32,6 +26,7 @@
 #include <asm/idle.h>
 #include <asm/mce.h>
 #include <asm/msr.h>
+#include <asm/trace/irq_vectors.h>
 
 #define NR_BLOCKS         9
 #define THRESHOLD_MAX     0xFFF
 #define MASK_BLKPTR_LO    0xFF000000
 #define MCG_XBLK_ADDR     0xC0000400
 
+/* Deferred error settings */
+#define MSR_CU_DEF_ERR         0xC0000410
+#define MASK_DEF_LVTOFF                0x000000F0
+#define MASK_DEF_INT_TYPE      0x00000006
+#define DEF_LVT_OFF            0x2
+#define DEF_INT_TYPE_APIC      0x2
+
 static const char * const th_names[] = {
        "load_store",
        "insn_fetch",
@@ -60,6 +62,13 @@ static DEFINE_PER_CPU(struct threshold_bank **, threshold_banks);
 static DEFINE_PER_CPU(unsigned char, bank_map);        /* see which banks are on */
 
 static void amd_threshold_interrupt(void);
+static void amd_deferred_error_interrupt(void);
+
+static void default_deferred_error_interrupt(void)
+{
+       pr_err("Unexpected deferred interrupt at vector %x\n", DEFERRED_ERROR_VECTOR);
+}
+void (*deferred_error_int_vector)(void) = default_deferred_error_interrupt;
 
 /*
  * CPU Initialization
@@ -196,7 +205,7 @@ static void mce_threshold_block_init(struct threshold_block *b, int offset)
        threshold_restart_bank(&tr);
 };
 
-static int setup_APIC_mce(int reserved, int new)
+static int setup_APIC_mce_threshold(int reserved, int new)
 {
        if (reserved < 0 && !setup_APIC_eilvt(new, THRESHOLD_APIC_VECTOR,
                                              APIC_EILVT_MSG_FIX, 0))
@@ -205,6 +214,39 @@ static int setup_APIC_mce(int reserved, int new)
        return reserved;
 }
 
+static int setup_APIC_deferred_error(int reserved, int new)
+{
+       if (reserved < 0 && !setup_APIC_eilvt(new, DEFERRED_ERROR_VECTOR,
+                                             APIC_EILVT_MSG_FIX, 0))
+               return new;
+
+       return reserved;
+}
+
+static void deferred_error_interrupt_enable(struct cpuinfo_x86 *c)
+{
+       u32 low = 0, high = 0;
+       int def_offset = -1, def_new;
+
+       if (rdmsr_safe(MSR_CU_DEF_ERR, &low, &high))
+               return;
+
+       def_new = (low & MASK_DEF_LVTOFF) >> 4;
+       if (!(low & MASK_DEF_LVTOFF)) {
+               pr_err(FW_BUG "Your BIOS is not setting up LVT offset 0x2 for deferred error IRQs correctly.\n");
+               def_new = DEF_LVT_OFF;
+               low = (low & ~MASK_DEF_LVTOFF) | (DEF_LVT_OFF << 4);
+       }
+
+       def_offset = setup_APIC_deferred_error(def_offset, def_new);
+       if ((def_offset == def_new) &&
+           (deferred_error_int_vector != amd_deferred_error_interrupt))
+               deferred_error_int_vector = amd_deferred_error_interrupt;
+
+       low = (low & ~MASK_DEF_INT_TYPE) | DEF_INT_TYPE_APIC;
+       wrmsr(MSR_CU_DEF_ERR, low, high);
+}
+
 /* cpu init entry point, called from mce.c with preempt off */
 void mce_amd_feature_init(struct cpuinfo_x86 *c)
 {
@@ -252,7 +294,7 @@ void mce_amd_feature_init(struct cpuinfo_x86 *c)
 
                        b.interrupt_enable = 1;
                        new     = (high & MASK_LVTOFF_HI) >> 20;
-                       offset  = setup_APIC_mce(offset, new);
+                       offset  = setup_APIC_mce_threshold(offset, new);
 
                        if ((offset == new) &&
                            (mce_threshold_vector != amd_threshold_interrupt))
@@ -262,6 +304,73 @@ init:
                        mce_threshold_block_init(&b, offset);
                }
        }
+
+       if (mce_flags.succor)
+               deferred_error_interrupt_enable(c);
+}
+
+static void __log_error(unsigned int bank, bool threshold_err, u64 misc)
+{
+       struct mce m;
+       u64 status;
+
+       rdmsrl(MSR_IA32_MCx_STATUS(bank), status);
+       if (!(status & MCI_STATUS_VAL))
+               return;
+
+       mce_setup(&m);
+
+       m.status = status;
+       m.bank = bank;
+
+       if (threshold_err)
+               m.misc = misc;
+
+       if (m.status & MCI_STATUS_ADDRV)
+               rdmsrl(MSR_IA32_MCx_ADDR(bank), m.addr);
+
+       mce_log(&m);
+       wrmsrl(MSR_IA32_MCx_STATUS(bank), 0);
+}
+
+static inline void __smp_deferred_error_interrupt(void)
+{
+       inc_irq_stat(irq_deferred_error_count);
+       deferred_error_int_vector();
+}
+
+asmlinkage __visible void smp_deferred_error_interrupt(void)
+{
+       entering_irq();
+       __smp_deferred_error_interrupt();
+       exiting_ack_irq();
+}
+
+asmlinkage __visible void smp_trace_deferred_error_interrupt(void)
+{
+       entering_irq();
+       trace_deferred_error_apic_entry(DEFERRED_ERROR_VECTOR);
+       __smp_deferred_error_interrupt();
+       trace_deferred_error_apic_exit(DEFERRED_ERROR_VECTOR);
+       exiting_ack_irq();
+}
+
+/* APIC interrupt handler for deferred errors */
+static void amd_deferred_error_interrupt(void)
+{
+       u64 status;
+       unsigned int bank;
+
+       for (bank = 0; bank < mca_cfg.banks; ++bank) {
+               rdmsrl(MSR_IA32_MCx_STATUS(bank), status);
+
+               if (!(status & MCI_STATUS_VAL) ||
+                   !(status & MCI_STATUS_DEFERRED))
+                       continue;
+
+               __log_error(bank, false, 0);
+               break;
+       }
 }
 
 /*
@@ -273,12 +382,12 @@ init:
  * the interrupt goes off when error_count reaches threshold_limit.
  * the handler will simply log mcelog w/ software defined bank number.
  */
+
 static void amd_threshold_interrupt(void)
 {
        u32 low = 0, high = 0, address = 0;
        int cpu = smp_processor_id();
        unsigned int bank, block;
-       struct mce m;
 
        /* assume first bank caused it */
        for (bank = 0; bank < mca_cfg.banks; ++bank) {
@@ -321,15 +430,7 @@ static void amd_threshold_interrupt(void)
        return;
 
 log:
-       mce_setup(&m);
-       rdmsrl(MSR_IA32_MCx_STATUS(bank), m.status);
-       if (!(m.status & MCI_STATUS_VAL))
-               return;
-       m.misc = ((u64)high << 32) | low;
-       m.bank = bank;
-       mce_log(&m);
-
-       wrmsrl(MSR_IA32_MCx_STATUS(bank), 0);
+       __log_error(bank, true, ((u64)high << 32) | low);
 }
 
 /*
index b4a41cf030edab7dbfae7dc67560ba63eabc2309..844f56c5616d242dc41631957bf94f687f039b0d 100644 (file)
@@ -91,6 +91,36 @@ static int cmci_supported(int *banks)
        return !!(cap & MCG_CMCI_P);
 }
 
+static bool lmce_supported(void)
+{
+       u64 tmp;
+
+       if (mca_cfg.lmce_disabled)
+               return false;
+
+       rdmsrl(MSR_IA32_MCG_CAP, tmp);
+
+       /*
+        * LMCE depends on recovery support in the processor. Hence both
+        * MCG_SER_P and MCG_LMCE_P should be present in MCG_CAP.
+        */
+       if ((tmp & (MCG_SER_P | MCG_LMCE_P)) !=
+                  (MCG_SER_P | MCG_LMCE_P))
+               return false;
+
+       /*
+        * BIOS should indicate support for LMCE by setting bit 20 in
+        * IA32_FEATURE_CONTROL without which touching MCG_EXT_CTL will
+        * generate a #GP fault.
+        */
+       rdmsrl(MSR_IA32_FEATURE_CONTROL, tmp);
+       if ((tmp & (FEATURE_CONTROL_LOCKED | FEATURE_CONTROL_LMCE)) ==
+                  (FEATURE_CONTROL_LOCKED | FEATURE_CONTROL_LMCE))
+               return true;
+
+       return false;
+}
+
 bool mce_intel_cmci_poll(void)
 {
        if (__this_cpu_read(cmci_storm_state) == CMCI_STORM_NONE)
@@ -405,8 +435,22 @@ static void intel_init_cmci(void)
        cmci_recheck();
 }
 
+void intel_init_lmce(void)
+{
+       u64 val;
+
+       if (!lmce_supported())
+               return;
+
+       rdmsrl(MSR_IA32_MCG_EXT_CTL, val);
+
+       if (!(val & MCG_EXT_CTL_LMCE_EN))
+               wrmsrl(MSR_IA32_MCG_EXT_CTL, val | MCG_EXT_CTL_LMCE_EN);
+}
+
 void mce_intel_feature_init(struct cpuinfo_x86 *c)
 {
        intel_init_thermal(c);
        intel_init_cmci();
+       intel_init_lmce();
 }
index 737737edbd1ef5bbc478cf2b6f898f25c231ea81..e8a215a9a34557542380e4a9e7fcf64a1ed63d91 100644 (file)
@@ -228,7 +228,23 @@ static void apply_ucode_in_initrd(void *ucode, size_t size, bool save_patch)
        }
 }
 
-void __init load_ucode_amd_bsp(void)
+static bool __init load_builtin_amd_microcode(struct cpio_data *cp,
+                                             unsigned int family)
+{
+#ifdef CONFIG_X86_64
+       char fw_name[36] = "amd-ucode/microcode_amd.bin";
+
+       if (family >= 0x15)
+               snprintf(fw_name, sizeof(fw_name),
+                        "amd-ucode/microcode_amd_fam%.2xh.bin", family);
+
+       return get_builtin_firmware(cp, fw_name);
+#else
+       return false;
+#endif
+}
+
+void __init load_ucode_amd_bsp(unsigned int family)
 {
        struct cpio_data cp;
        void **data;
@@ -243,8 +259,10 @@ void __init load_ucode_amd_bsp(void)
 #endif
 
        cp = find_ucode_in_initrd();
-       if (!cp.data)
-               return;
+       if (!cp.data) {
+               if (!load_builtin_amd_microcode(&cp, family))
+                       return;
+       }
 
        *data = cp.data;
        *size = cp.size;
index 36a83617eb21cc19245794a89c986eba45179d3a..6236a54a63f449ce2ea824be13a3bcca2f57e4b9 100644 (file)
@@ -1,74 +1,16 @@
 /*
- *     Intel CPU Microcode Update Driver for Linux
+ * CPU Microcode Update Driver for Linux
  *
- *     Copyright (C) 2000-2006 Tigran Aivazian <tigran@aivazian.fsnet.co.uk>
- *                   2006      Shaohua Li <shaohua.li@intel.com>
+ * Copyright (C) 2000-2006 Tigran Aivazian <tigran@aivazian.fsnet.co.uk>
+ *           2006      Shaohua Li <shaohua.li@intel.com>
+ *           2013-2015 Borislav Petkov <bp@alien8.de>
  *
- *     This driver allows to upgrade microcode on Intel processors
- *     belonging to IA-32 family - PentiumPro, Pentium II,
- *     Pentium III, Xeon, Pentium 4, etc.
+ * This driver allows to upgrade microcode on x86 processors.
  *
- *     Reference: Section 8.11 of Volume 3a, IA-32 Intel? Architecture
- *     Software Developer's Manual
- *     Order Number 253668 or free download from:
- *
- *     http://developer.intel.com/Assets/PDF/manual/253668.pdf 
- *
- *     For more information, go to http://www.urbanmyth.org/microcode
- *
- *     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.
- *
- *     1.0     16 Feb 2000, Tigran Aivazian <tigran@sco.com>
- *             Initial release.
- *     1.01    18 Feb 2000, Tigran Aivazian <tigran@sco.com>
- *             Added read() support + cleanups.
- *     1.02    21 Feb 2000, Tigran Aivazian <tigran@sco.com>
- *             Added 'device trimming' support. open(O_WRONLY) zeroes
- *             and frees the saved copy of applied microcode.
- *     1.03    29 Feb 2000, Tigran Aivazian <tigran@sco.com>
- *             Made to use devfs (/dev/cpu/microcode) + cleanups.
- *     1.04    06 Jun 2000, Simon Trimmer <simon@veritas.com>
- *             Added misc device support (now uses both devfs and misc).
- *             Added MICROCODE_IOCFREE ioctl to clear memory.
- *     1.05    09 Jun 2000, Simon Trimmer <simon@veritas.com>
- *             Messages for error cases (non Intel & no suitable microcode).
- *     1.06    03 Aug 2000, Tigran Aivazian <tigran@veritas.com>
- *             Removed ->release(). Removed exclusive open and status bitmap.
- *             Added microcode_rwsem to serialize read()/write()/ioctl().
- *             Removed global kernel lock usage.
- *     1.07    07 Sep 2000, Tigran Aivazian <tigran@veritas.com>
- *             Write 0 to 0x8B msr and then cpuid before reading revision,
- *             so that it works even if there were no update done by the
- *             BIOS. Otherwise, reading from 0x8B gives junk (which happened
- *             to be 0 on my machine which is why it worked even when I
- *             disabled update by the BIOS)
- *             Thanks to Eric W. Biederman <ebiederman@lnxi.com> for the fix.
- *     1.08    11 Dec 2000, Richard Schaal <richard.schaal@intel.com> and
- *                          Tigran Aivazian <tigran@veritas.com>
- *             Intel Pentium 4 processor support and bugfixes.
- *     1.09    30 Oct 2001, Tigran Aivazian <tigran@veritas.com>
- *             Bugfix for HT (Hyper-Threading) enabled processors
- *             whereby processor resources are shared by all logical processors
- *             in a single CPU package.
- *     1.10    28 Feb 2002 Asit K Mallick <asit.k.mallick@intel.com> and
- *             Tigran Aivazian <tigran@veritas.com>,
- *             Serialize updates as required on HT processors due to
- *             speculative nature of implementation.
- *     1.11    22 Mar 2002 Tigran Aivazian <tigran@veritas.com>
- *             Fix the panic when writing zero-length microcode chunk.
- *     1.12    29 Sep 2003 Nitin Kamble <nitin.a.kamble@intel.com>,
- *             Jun Nakajima <jun.nakajima@intel.com>
- *             Support for the microcode updates in the new format.
- *     1.13    10 Oct 2003 Tigran Aivazian <tigran@veritas.com>
- *             Removed ->read() method and obsoleted MICROCODE_IOCFREE ioctl
- *             because we no longer hold a copy of applied microcode
- *             in kernel memory.
- *     1.14    25 Jun 2004 Tigran Aivazian <tigran@veritas.com>
- *             Fix sigmatch() macro to handle old CPUs with pf == 0.
- *             Thanks to Stuart Swales for pointing out this bug.
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
  */
 
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
index a413a69cbd744f2e2873434ee20b66e86fb466dd..8ebc421d62996ae8d90b4828fd52a4fae198cae1 100644 (file)
@@ -3,6 +3,7 @@
  *
  *     Copyright (C) 2012 Fenghua Yu <fenghua.yu@intel.com>
  *                        H Peter Anvin" <hpa@zytor.com>
+ *               (C) 2015 Borislav Petkov <bp@alien8.de>
  *
  *     This driver allows to early upgrade microcode on Intel processors
  *     belonging to IA-32 family - PentiumPro, Pentium II,
@@ -17,6 +18,7 @@
  *     2 of the License, or (at your option) any later version.
  */
 #include <linux/module.h>
+#include <linux/firmware.h>
 #include <asm/microcode.h>
 #include <asm/microcode_intel.h>
 #include <asm/microcode_amd.h>
@@ -43,9 +45,29 @@ static bool __init check_loader_disabled_bsp(void)
        return *res;
 }
 
+extern struct builtin_fw __start_builtin_fw[];
+extern struct builtin_fw __end_builtin_fw[];
+
+bool get_builtin_firmware(struct cpio_data *cd, const char *name)
+{
+#ifdef CONFIG_FW_LOADER
+       struct builtin_fw *b_fw;
+
+       for (b_fw = __start_builtin_fw; b_fw != __end_builtin_fw; b_fw++) {
+               if (!strcmp(name, b_fw->name)) {
+                       cd->size = b_fw->size;
+                       cd->data = b_fw->data;
+                       return true;
+               }
+       }
+#endif
+       return false;
+}
+
 void __init load_ucode_bsp(void)
 {
-       int vendor, family;
+       int vendor;
+       unsigned int family;
 
        if (check_loader_disabled_bsp())
                return;
@@ -63,7 +85,7 @@ void __init load_ucode_bsp(void)
                break;
        case X86_VENDOR_AMD:
                if (family >= 0x10)
-                       load_ucode_amd_bsp();
+                       load_ucode_amd_bsp(family);
                break;
        default:
                break;
index a41beadb3db9a396e5b74795e62a49648b367870..969dc17eb1b4b86775d5496bb6ebe9ba67110b4c 100644 (file)
@@ -1,74 +1,13 @@
 /*
- *     Intel CPU Microcode Update Driver for Linux
+ * Intel CPU Microcode Update Driver for Linux
  *
- *     Copyright (C) 2000-2006 Tigran Aivazian <tigran@aivazian.fsnet.co.uk>
- *                   2006      Shaohua Li <shaohua.li@intel.com>
+ * Copyright (C) 2000-2006 Tigran Aivazian <tigran@aivazian.fsnet.co.uk>
+ *              2006 Shaohua Li <shaohua.li@intel.com>
  *
- *     This driver allows to upgrade microcode on Intel processors
- *     belonging to IA-32 family - PentiumPro, Pentium II,
- *     Pentium III, Xeon, Pentium 4, etc.
- *
- *     Reference: Section 8.11 of Volume 3a, IA-32 Intel? Architecture
- *     Software Developer's Manual
- *     Order Number 253668 or free download from:
- *
- *     http://developer.intel.com/Assets/PDF/manual/253668.pdf 
- *
- *     For more information, go to http://www.urbanmyth.org/microcode
- *
- *     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.
- *
- *     1.0     16 Feb 2000, Tigran Aivazian <tigran@sco.com>
- *             Initial release.
- *     1.01    18 Feb 2000, Tigran Aivazian <tigran@sco.com>
- *             Added read() support + cleanups.
- *     1.02    21 Feb 2000, Tigran Aivazian <tigran@sco.com>
- *             Added 'device trimming' support. open(O_WRONLY) zeroes
- *             and frees the saved copy of applied microcode.
- *     1.03    29 Feb 2000, Tigran Aivazian <tigran@sco.com>
- *             Made to use devfs (/dev/cpu/microcode) + cleanups.
- *     1.04    06 Jun 2000, Simon Trimmer <simon@veritas.com>
- *             Added misc device support (now uses both devfs and misc).
- *             Added MICROCODE_IOCFREE ioctl to clear memory.
- *     1.05    09 Jun 2000, Simon Trimmer <simon@veritas.com>
- *             Messages for error cases (non Intel & no suitable microcode).
- *     1.06    03 Aug 2000, Tigran Aivazian <tigran@veritas.com>
- *             Removed ->release(). Removed exclusive open and status bitmap.
- *             Added microcode_rwsem to serialize read()/write()/ioctl().
- *             Removed global kernel lock usage.
- *     1.07    07 Sep 2000, Tigran Aivazian <tigran@veritas.com>
- *             Write 0 to 0x8B msr and then cpuid before reading revision,
- *             so that it works even if there were no update done by the
- *             BIOS. Otherwise, reading from 0x8B gives junk (which happened
- *             to be 0 on my machine which is why it worked even when I
- *             disabled update by the BIOS)
- *             Thanks to Eric W. Biederman <ebiederman@lnxi.com> for the fix.
- *     1.08    11 Dec 2000, Richard Schaal <richard.schaal@intel.com> and
- *                          Tigran Aivazian <tigran@veritas.com>
- *             Intel Pentium 4 processor support and bugfixes.
- *     1.09    30 Oct 2001, Tigran Aivazian <tigran@veritas.com>
- *             Bugfix for HT (Hyper-Threading) enabled processors
- *             whereby processor resources are shared by all logical processors
- *             in a single CPU package.
- *     1.10    28 Feb 2002 Asit K Mallick <asit.k.mallick@intel.com> and
- *             Tigran Aivazian <tigran@veritas.com>,
- *             Serialize updates as required on HT processors due to
- *             speculative nature of implementation.
- *     1.11    22 Mar 2002 Tigran Aivazian <tigran@veritas.com>
- *             Fix the panic when writing zero-length microcode chunk.
- *     1.12    29 Sep 2003 Nitin Kamble <nitin.a.kamble@intel.com>,
- *             Jun Nakajima <jun.nakajima@intel.com>
- *             Support for the microcode updates in the new format.
- *     1.13    10 Oct 2003 Tigran Aivazian <tigran@veritas.com>
- *             Removed ->read() method and obsoleted MICROCODE_IOCFREE ioctl
- *             because we no longer hold a copy of applied microcode
- *             in kernel memory.
- *     1.14    25 Jun 2004 Tigran Aivazian <tigran@veritas.com>
- *             Fix sigmatch() macro to handle old CPUs with pf == 0.
- *             Thanks to Stuart Swales for pointing out this bug.
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
  */
 
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
@@ -124,7 +63,7 @@ static int get_matching_mc(struct microcode_intel *mc_intel, int cpu)
        cpf = cpu_sig.pf;
        crev = cpu_sig.rev;
 
-       return get_matching_microcode(csig, cpf, crev, mc_intel);
+       return has_newer_microcode(mc_intel, csig, cpf, crev);
 }
 
 static int apply_microcode_intel(int cpu)
@@ -226,7 +165,7 @@ static enum ucode_state generic_load_microcode(int cpu, void *data, size_t size,
 
                csig = uci->cpu_sig.sig;
                cpf = uci->cpu_sig.pf;
-               if (get_matching_microcode(csig, cpf, new_rev, mc)) {
+               if (has_newer_microcode(mc, csig, cpf, new_rev)) {
                        vfree(new_mc);
                        new_rev = mc_header.rev;
                        new_mc  = mc;
index 2f49ab4ac0ae137d7ab0b851cf4b9e751d58922c..8187b7247d1c3e97b9a7321a1d0e12ccda1e0962 100644 (file)
@@ -59,10 +59,10 @@ load_microcode_early(struct microcode_intel **saved,
                ucode_ptr = saved[i];
                mc_hdr    = (struct microcode_header_intel *)ucode_ptr;
 
-               ret = get_matching_microcode(uci->cpu_sig.sig,
-                                            uci->cpu_sig.pf,
-                                            new_rev,
-                                            ucode_ptr);
+               ret = has_newer_microcode(ucode_ptr,
+                                         uci->cpu_sig.sig,
+                                         uci->cpu_sig.pf,
+                                         new_rev);
                if (!ret)
                        continue;
 
@@ -246,7 +246,7 @@ static unsigned int _save_mc(struct microcode_intel **mc_saved,
                             u8 *ucode_ptr, unsigned int num_saved)
 {
        struct microcode_header_intel *mc_hdr, *mc_saved_hdr;
-       unsigned int sig, pf, new_rev;
+       unsigned int sig, pf;
        int found = 0, i;
 
        mc_hdr = (struct microcode_header_intel *)ucode_ptr;
@@ -255,14 +255,13 @@ static unsigned int _save_mc(struct microcode_intel **mc_saved,
                mc_saved_hdr = (struct microcode_header_intel *)mc_saved[i];
                sig          = mc_saved_hdr->sig;
                pf           = mc_saved_hdr->pf;
-               new_rev      = mc_hdr->rev;
 
-               if (!get_matching_sig(sig, pf, new_rev, ucode_ptr))
+               if (!find_matching_signature(ucode_ptr, sig, pf))
                        continue;
 
                found = 1;
 
-               if (!revision_is_newer(mc_hdr, new_rev))
+               if (mc_hdr->rev <= mc_saved_hdr->rev)
                        continue;
 
                /*
@@ -522,6 +521,27 @@ out:
 EXPORT_SYMBOL_GPL(save_mc_for_early);
 #endif
 
+static bool __init load_builtin_intel_microcode(struct cpio_data *cp)
+{
+#ifdef CONFIG_X86_64
+       unsigned int eax = 0x00000001, ebx, ecx = 0, edx;
+       unsigned int family, model, stepping;
+       char name[30];
+
+       native_cpuid(&eax, &ebx, &ecx, &edx);
+
+       family   = __x86_family(eax);
+       model    = x86_model(eax);
+       stepping = eax & 0xf;
+
+       sprintf(name, "intel-ucode/%02x-%02x-%02x", family, model, stepping);
+
+       return get_builtin_firmware(cp, name);
+#else
+       return false;
+#endif
+}
+
 static __initdata char ucode_name[] = "kernel/x86/microcode/GenuineIntel.bin";
 static __init enum ucode_state
 scan_microcode(struct mc_saved_data *mc_saved_data, unsigned long *initrd,
@@ -540,8 +560,10 @@ scan_microcode(struct mc_saved_data *mc_saved_data, unsigned long *initrd,
        cd.size = 0;
 
        cd = find_cpio_data(p, (void *)start, size, &offset);
-       if (!cd.data)
-               return UCODE_ERROR;
+       if (!cd.data) {
+               if (!load_builtin_intel_microcode(&cd))
+                       return UCODE_ERROR;
+       }
 
        return get_matching_model_microcode(0, start, cd.data, cd.size,
                                            mc_saved_data, initrd, uci);
index cd47a510a3f174233300d8763705b6f200faf9f4..1883d252ff7d60ce7707a8108283144de64ce14d 100644 (file)
 #include <asm/processor.h>
 #include <asm/msr.h>
 
-static inline int
-update_match_cpu(unsigned int csig, unsigned int cpf,
-                unsigned int sig, unsigned int pf)
+static inline bool cpu_signatures_match(unsigned int s1, unsigned int p1,
+                                       unsigned int s2, unsigned int p2)
 {
-       return (!sigmatch(sig, csig, pf, cpf)) ? 0 : 1;
+       if (s1 != s2)
+               return false;
+
+       /* Processor flags are either both 0 ... */
+       if (!p1 && !p2)
+               return true;
+
+       /* ... or they intersect. */
+       return p1 & p2;
 }
 
 int microcode_sanity_check(void *mc, int print_err)
@@ -124,27 +131,25 @@ EXPORT_SYMBOL_GPL(microcode_sanity_check);
 /*
  * Returns 1 if update has been found, 0 otherwise.
  */
-int get_matching_sig(unsigned int csig, int cpf, int rev, void *mc)
+int find_matching_signature(void *mc, unsigned int csig, int cpf)
 {
-       struct microcode_header_intel *mc_header = mc;
-       struct extended_sigtable *ext_header;
-       unsigned long total_size = get_totalsize(mc_header);
-       int ext_sigcount, i;
+       struct microcode_header_intel *mc_hdr = mc;
+       struct extended_sigtable *ext_hdr;
        struct extended_signature *ext_sig;
+       int i;
 
-       if (update_match_cpu(csig, cpf, mc_header->sig, mc_header->pf))
+       if (cpu_signatures_match(csig, cpf, mc_hdr->sig, mc_hdr->pf))
                return 1;
 
        /* Look for ext. headers: */
-       if (total_size <= get_datasize(mc_header) + MC_HEADER_SIZE)
+       if (get_totalsize(mc_hdr) <= get_datasize(mc_hdr) + MC_HEADER_SIZE)
                return 0;
 
-       ext_header = mc + get_datasize(mc_header) + MC_HEADER_SIZE;
-       ext_sigcount = ext_header->count;
-       ext_sig = (void *)ext_header + EXT_HEADER_SIZE;
+       ext_hdr = mc + get_datasize(mc_hdr) + MC_HEADER_SIZE;
+       ext_sig = (void *)ext_hdr + EXT_HEADER_SIZE;
 
-       for (i = 0; i < ext_sigcount; i++) {
-               if (update_match_cpu(csig, cpf, ext_sig->sig, ext_sig->pf))
+       for (i = 0; i < ext_hdr->count; i++) {
+               if (cpu_signatures_match(csig, cpf, ext_sig->sig, ext_sig->pf))
                        return 1;
                ext_sig++;
        }
@@ -154,13 +159,13 @@ int get_matching_sig(unsigned int csig, int cpf, int rev, void *mc)
 /*
  * Returns 1 if update has been found, 0 otherwise.
  */
-int get_matching_microcode(unsigned int csig, int cpf, int rev, void *mc)
+int has_newer_microcode(void *mc, unsigned int csig, int cpf, int new_rev)
 {
        struct microcode_header_intel *mc_hdr = mc;
 
-       if (!revision_is_newer(mc_hdr, rev))
+       if (mc_hdr->rev <= new_rev)
                return 0;
 
-       return get_matching_sig(csig, cpf, rev, mc);
+       return find_matching_signature(mc, csig, cpf);
 }
-EXPORT_SYMBOL_GPL(get_matching_microcode);
+EXPORT_SYMBOL_GPL(has_newer_microcode);
index 939155ffdecec60628a06b2937604dd2f2f98813..aad4bd84b475ec4c762e72f62bbd9fb07f7ac2c4 100644 (file)
@@ -39,14 +39,12 @@ void hyperv_vector_handler(struct pt_regs *regs)
 {
        struct pt_regs *old_regs = set_irq_regs(regs);
 
-       irq_enter();
-       exit_idle();
-
+       entering_irq();
        inc_irq_stat(irq_hv_callback_count);
        if (vmbus_handler)
                vmbus_handler();
 
-       irq_exit();
+       exiting_irq();
        set_irq_regs(old_regs);
 }
 
index 5f90b85ff22e584be8d1d7eb1615acde7584b397..70d7c93f455083e8703f6a37e94dc636432d7b64 100644 (file)
@@ -98,7 +98,8 @@ x86_get_mtrr_mem_range(struct range *range, int nr_range,
                        continue;
                base = range_state[i].base_pfn;
                if (base < (1<<(20-PAGE_SHIFT)) && mtrr_state.have_fixed &&
-                   (mtrr_state.enabled & 1)) {
+                   (mtrr_state.enabled & MTRR_STATE_MTRR_ENABLED) &&
+                   (mtrr_state.enabled & MTRR_STATE_MTRR_FIXED_ENABLED)) {
                        /* Var MTRR contains UC entry below 1M? Skip it: */
                        printk(BIOS_BUG_MSG, i);
                        if (base + size <= (1<<(20-PAGE_SHIFT)))
index 7d74f7b3c6ba49ee5f3ef9baa93e9eb03a906992..3b533cf37c745c9ecfc81fc5fde94bc46f84e1b5 100644 (file)
@@ -102,59 +102,76 @@ static int check_type_overlap(u8 *prev, u8 *curr)
        return 0;
 }
 
-/*
- * Error/Semi-error returns:
- * 0xFF - when MTRR is not enabled
- * *repeat == 1 implies [start:end] spanned across MTRR range and type returned
- *             corresponds only to [start:*partial_end].
- *             Caller has to lookup again for [*partial_end:end].
+/**
+ * mtrr_type_lookup_fixed - look up memory type in MTRR fixed entries
+ *
+ * Return the MTRR fixed memory type of 'start'.
+ *
+ * MTRR fixed entries are divided into the following ways:
+ *  0x00000 - 0x7FFFF : This range is divided into eight 64KB sub-ranges
+ *  0x80000 - 0xBFFFF : This range is divided into sixteen 16KB sub-ranges
+ *  0xC0000 - 0xFFFFF : This range is divided into sixty-four 4KB sub-ranges
+ *
+ * Return Values:
+ * MTRR_TYPE_(type)  - Matched memory type
+ * MTRR_TYPE_INVALID - Unmatched
+ */
+static u8 mtrr_type_lookup_fixed(u64 start, u64 end)
+{
+       int idx;
+
+       if (start >= 0x100000)
+               return MTRR_TYPE_INVALID;
+
+       /* 0x0 - 0x7FFFF */
+       if (start < 0x80000) {
+               idx = 0;
+               idx += (start >> 16);
+               return mtrr_state.fixed_ranges[idx];
+       /* 0x80000 - 0xBFFFF */
+       } else if (start < 0xC0000) {
+               idx = 1 * 8;
+               idx += ((start - 0x80000) >> 14);
+               return mtrr_state.fixed_ranges[idx];
+       }
+
+       /* 0xC0000 - 0xFFFFF */
+       idx = 3 * 8;
+       idx += ((start - 0xC0000) >> 12);
+       return mtrr_state.fixed_ranges[idx];
+}
+
+/**
+ * mtrr_type_lookup_variable - look up memory type in MTRR variable entries
+ *
+ * Return Value:
+ * MTRR_TYPE_(type) - Matched memory type or default memory type (unmatched)
+ *
+ * Output Arguments:
+ * repeat - Set to 1 when [start:end] spanned across MTRR range and type
+ *         returned corresponds only to [start:*partial_end].  Caller has
+ *         to lookup again for [*partial_end:end].
+ *
+ * uniform - Set to 1 when an MTRR covers the region uniformly, i.e. the
+ *          region is fully covered by a single MTRR entry or the default
+ *          type.
  */
-static u8 __mtrr_type_lookup(u64 start, u64 end, u64 *partial_end, int *repeat)
+static u8 mtrr_type_lookup_variable(u64 start, u64 end, u64 *partial_end,
+                                   int *repeat, u8 *uniform)
 {
        int i;
        u64 base, mask;
        u8 prev_match, curr_match;
 
        *repeat = 0;
-       if (!mtrr_state_set)
-               return 0xFF;
-
-       if (!mtrr_state.enabled)
-               return 0xFF;
+       *uniform = 1;
 
-       /* Make end inclusive end, instead of exclusive */
+       /* Make end inclusive instead of exclusive */
        end--;
 
-       /* Look in fixed ranges. Just return the type as per start */
-       if (mtrr_state.have_fixed && (start < 0x100000)) {
-               int idx;
-
-               if (start < 0x80000) {
-                       idx = 0;
-                       idx += (start >> 16);
-                       return mtrr_state.fixed_ranges[idx];
-               } else if (start < 0xC0000) {
-                       idx = 1 * 8;
-                       idx += ((start - 0x80000) >> 14);
-                       return mtrr_state.fixed_ranges[idx];
-               } else if (start < 0x1000000) {
-                       idx = 3 * 8;
-                       idx += ((start - 0xC0000) >> 12);
-                       return mtrr_state.fixed_ranges[idx];
-               }
-       }
-
-       /*
-        * Look in variable ranges
-        * Look of multiple ranges matching this address and pick type
-        * as per MTRR precedence
-        */
-       if (!(mtrr_state.enabled & 2))
-               return mtrr_state.def_type;
-
-       prev_match = 0xFF;
+       prev_match = MTRR_TYPE_INVALID;
        for (i = 0; i < num_var_ranges; ++i) {
-               unsigned short start_state, end_state;
+               unsigned short start_state, end_state, inclusive;
 
                if (!(mtrr_state.var_ranges[i].mask_lo & (1 << 11)))
                        continue;
@@ -166,20 +183,29 @@ static u8 __mtrr_type_lookup(u64 start, u64 end, u64 *partial_end, int *repeat)
 
                start_state = ((start & mask) == (base & mask));
                end_state = ((end & mask) == (base & mask));
+               inclusive = ((start < base) && (end > base));
 
-               if (start_state != end_state) {
+               if ((start_state != end_state) || inclusive) {
                        /*
                         * We have start:end spanning across an MTRR.
-                        * We split the region into
-                        * either
-                        * (start:mtrr_end) (mtrr_end:end)
-                        * or
-                        * (start:mtrr_start) (mtrr_start:end)
+                        * We split the region into either
+                        *
+                        * - start_state:1
+                        * (start:mtrr_end)(mtrr_end:end)
+                        * - end_state:1
+                        * (start:mtrr_start)(mtrr_start:end)
+                        * - inclusive:1
+                        * (start:mtrr_start)(mtrr_start:mtrr_end)(mtrr_end:end)
+                        *
                         * depending on kind of overlap.
-                        * Return the type for first region and a pointer to
-                        * the start of second region so that caller will
-                        * lookup again on the second region.
-                        * Note: This way we handle multiple overlaps as well.
+                        *
+                        * Return the type of the first region and a pointer
+                        * to the start of next region so that caller will be
+                        * advised to lookup again after having adjusted start
+                        * and end.
+                        *
+                        * Note: This way we handle overlaps with multiple
+                        * entries and the default type properly.
                         */
                        if (start_state)
                                *partial_end = base + get_mtrr_size(mask);
@@ -193,59 +219,94 @@ static u8 __mtrr_type_lookup(u64 start, u64 end, u64 *partial_end, int *repeat)
 
                        end = *partial_end - 1; /* end is inclusive */
                        *repeat = 1;
+                       *uniform = 0;
                }
 
                if ((start & mask) != (base & mask))
                        continue;
 
                curr_match = mtrr_state.var_ranges[i].base_lo & 0xff;
-               if (prev_match == 0xFF) {
+               if (prev_match == MTRR_TYPE_INVALID) {
                        prev_match = curr_match;
                        continue;
                }
 
+               *uniform = 0;
                if (check_type_overlap(&prev_match, &curr_match))
                        return curr_match;
        }
 
-       if (mtrr_tom2) {
-               if (start >= (1ULL<<32) && (end < mtrr_tom2))
-                       return MTRR_TYPE_WRBACK;
-       }
-
-       if (prev_match != 0xFF)
+       if (prev_match != MTRR_TYPE_INVALID)
                return prev_match;
 
        return mtrr_state.def_type;
 }
 
-/*
- * Returns the effective MTRR type for the region
- * Error return:
- * 0xFF - when MTRR is not enabled
+/**
+ * mtrr_type_lookup - look up memory type in MTRR
+ *
+ * Return Values:
+ * MTRR_TYPE_(type)  - The effective MTRR type for the region
+ * MTRR_TYPE_INVALID - MTRR is disabled
+ *
+ * Output Argument:
+ * uniform - Set to 1 when an MTRR covers the region uniformly, i.e. the
+ *          region is fully covered by a single MTRR entry or the default
+ *          type.
  */
-u8 mtrr_type_lookup(u64 start, u64 end)
+u8 mtrr_type_lookup(u64 start, u64 end, u8 *uniform)
 {
-       u8 type, prev_type;
+       u8 type, prev_type, is_uniform = 1, dummy;
        int repeat;
        u64 partial_end;
 
-       type = __mtrr_type_lookup(start, end, &partial_end, &repeat);
+       if (!mtrr_state_set)
+               return MTRR_TYPE_INVALID;
+
+       if (!(mtrr_state.enabled & MTRR_STATE_MTRR_ENABLED))
+               return MTRR_TYPE_INVALID;
+
+       /*
+        * Look up the fixed ranges first, which take priority over
+        * the variable ranges.
+        */
+       if ((start < 0x100000) &&
+           (mtrr_state.have_fixed) &&
+           (mtrr_state.enabled & MTRR_STATE_MTRR_FIXED_ENABLED)) {
+               is_uniform = 0;
+               type = mtrr_type_lookup_fixed(start, end);
+               goto out;
+       }
+
+       /*
+        * Look up the variable ranges.  Look of multiple ranges matching
+        * this address and pick type as per MTRR precedence.
+        */
+       type = mtrr_type_lookup_variable(start, end, &partial_end,
+                                        &repeat, &is_uniform);
 
        /*
         * Common path is with repeat = 0.
         * However, we can have cases where [start:end] spans across some
-        * MTRR range. Do repeated lookups for that case here.
+        * MTRR ranges and/or the default type.  Do repeated lookups for
+        * that case here.
         */
        while (repeat) {
                prev_type = type;
                start = partial_end;
-               type = __mtrr_type_lookup(start, end, &partial_end, &repeat);
+               is_uniform = 0;
+               type = mtrr_type_lookup_variable(start, end, &partial_end,
+                                                &repeat, &dummy);
 
                if (check_type_overlap(&prev_type, &type))
-                       return type;
+                       goto out;
        }
 
+       if (mtrr_tom2 && (start >= (1ULL<<32)) && (end < mtrr_tom2))
+               type = MTRR_TYPE_WRBACK;
+
+out:
+       *uniform = is_uniform;
        return type;
 }
 
@@ -347,7 +408,9 @@ static void __init print_mtrr_state(void)
                 mtrr_attrib_to_str(mtrr_state.def_type));
        if (mtrr_state.have_fixed) {
                pr_debug("MTRR fixed ranges %sabled:\n",
-                        mtrr_state.enabled & 1 ? "en" : "dis");
+                       ((mtrr_state.enabled & MTRR_STATE_MTRR_ENABLED) &&
+                        (mtrr_state.enabled & MTRR_STATE_MTRR_FIXED_ENABLED)) ?
+                        "en" : "dis");
                print_fixed(0x00000, 0x10000, mtrr_state.fixed_ranges + 0);
                for (i = 0; i < 2; ++i)
                        print_fixed(0x80000 + i * 0x20000, 0x04000,
@@ -360,7 +423,7 @@ static void __init print_mtrr_state(void)
                print_fixed_last();
        }
        pr_debug("MTRR variable ranges %sabled:\n",
-                mtrr_state.enabled & 2 ? "en" : "dis");
+                mtrr_state.enabled & MTRR_STATE_MTRR_ENABLED ? "en" : "dis");
        high_width = (__ffs64(size_or_mask) - (32 - PAGE_SHIFT) + 3) / 4;
 
        for (i = 0; i < num_var_ranges; ++i) {
@@ -382,7 +445,7 @@ static void __init print_mtrr_state(void)
 }
 
 /* Grab all of the MTRR state for this CPU into *state */
-void __init get_mtrr_state(void)
+bool __init get_mtrr_state(void)
 {
        struct mtrr_var_range *vrs;
        unsigned long flags;
@@ -426,6 +489,8 @@ void __init get_mtrr_state(void)
 
        post_set();
        local_irq_restore(flags);
+
+       return !!(mtrr_state.enabled & MTRR_STATE_MTRR_ENABLED);
 }
 
 /* Some BIOS's are messed up and don't set all MTRRs the same! */
index ea5f363a194866303395358353a658750dcff3d4..e7ed0d8ebacb158833ea248c2d3efb6d2d6379de 100644 (file)
 #define MTRR_TO_PHYS_WC_OFFSET 1000
 
 u32 num_var_ranges;
+static bool __mtrr_enabled;
+
+static bool mtrr_enabled(void)
+{
+       return __mtrr_enabled;
+}
 
 unsigned int mtrr_usage_table[MTRR_MAX_VAR_RANGES];
 static DEFINE_MUTEX(mtrr_mutex);
@@ -286,7 +292,7 @@ int mtrr_add_page(unsigned long base, unsigned long size,
        int i, replace, error;
        mtrr_type ltype;
 
-       if (!mtrr_if)
+       if (!mtrr_enabled())
                return -ENXIO;
 
        error = mtrr_if->validate_add_page(base, size, type);
@@ -435,6 +441,8 @@ static int mtrr_check(unsigned long base, unsigned long size)
 int mtrr_add(unsigned long base, unsigned long size, unsigned int type,
             bool increment)
 {
+       if (!mtrr_enabled())
+               return -ENODEV;
        if (mtrr_check(base, size))
                return -EINVAL;
        return mtrr_add_page(base >> PAGE_SHIFT, size >> PAGE_SHIFT, type,
@@ -463,8 +471,8 @@ int mtrr_del_page(int reg, unsigned long base, unsigned long size)
        unsigned long lbase, lsize;
        int error = -EINVAL;
 
-       if (!mtrr_if)
-               return -ENXIO;
+       if (!mtrr_enabled())
+               return -ENODEV;
 
        max = num_var_ranges;
        /* No CPU hotplug when we change MTRR entries */
@@ -523,6 +531,8 @@ int mtrr_del_page(int reg, unsigned long base, unsigned long size)
  */
 int mtrr_del(int reg, unsigned long base, unsigned long size)
 {
+       if (!mtrr_enabled())
+               return -ENODEV;
        if (mtrr_check(base, size))
                return -EINVAL;
        return mtrr_del_page(reg, base >> PAGE_SHIFT, size >> PAGE_SHIFT);
@@ -538,6 +548,9 @@ EXPORT_SYMBOL(mtrr_del);
  * attempts to add a WC MTRR covering size bytes starting at base and
  * logs an error if this fails.
  *
+ * The called should provide a power of two size on an equivalent
+ * power of two boundary.
+ *
  * Drivers must store the return value to pass to mtrr_del_wc_if_needed,
  * but drivers should not try to interpret that return value.
  */
@@ -545,7 +558,7 @@ int arch_phys_wc_add(unsigned long base, unsigned long size)
 {
        int ret;
 
-       if (pat_enabled)
+       if (pat_enabled() || !mtrr_enabled())
                return 0;  /* Success!  (We don't need to do anything.) */
 
        ret = mtrr_add(base, size, MTRR_TYPE_WRCOMB, true);
@@ -577,7 +590,7 @@ void arch_phys_wc_del(int handle)
 EXPORT_SYMBOL(arch_phys_wc_del);
 
 /*
- * phys_wc_to_mtrr_index - translates arch_phys_wc_add's return value
+ * arch_phys_wc_index - translates arch_phys_wc_add's return value
  * @handle: Return value from arch_phys_wc_add
  *
  * This will turn the return value from arch_phys_wc_add into an mtrr
@@ -587,14 +600,14 @@ EXPORT_SYMBOL(arch_phys_wc_del);
  * in printk line.  Alas there is an illegitimate use in some ancient
  * drm ioctls.
  */
-int phys_wc_to_mtrr_index(int handle)
+int arch_phys_wc_index(int handle)
 {
        if (handle < MTRR_TO_PHYS_WC_OFFSET)
                return -1;
        else
                return handle - MTRR_TO_PHYS_WC_OFFSET;
 }
-EXPORT_SYMBOL_GPL(phys_wc_to_mtrr_index);
+EXPORT_SYMBOL_GPL(arch_phys_wc_index);
 
 /*
  * HACK ALERT!
@@ -734,10 +747,12 @@ void __init mtrr_bp_init(void)
        }
 
        if (mtrr_if) {
+               __mtrr_enabled = true;
                set_num_var_ranges();
                init_table();
                if (use_intel()) {
-                       get_mtrr_state();
+                       /* BIOS may override */
+                       __mtrr_enabled = get_mtrr_state();
 
                        if (mtrr_cleanup(phys_addr)) {
                                changed_by_mtrr_cleanup = 1;
@@ -745,10 +760,16 @@ void __init mtrr_bp_init(void)
                        }
                }
        }
+
+       if (!mtrr_enabled())
+               pr_info("MTRR: Disabled\n");
 }
 
 void mtrr_ap_init(void)
 {
+       if (!mtrr_enabled())
+               return;
+
        if (!use_intel() || mtrr_aps_delayed_init)
                return;
        /*
@@ -774,6 +795,9 @@ void mtrr_save_state(void)
 {
        int first_cpu;
 
+       if (!mtrr_enabled())
+               return;
+
        get_online_cpus();
        first_cpu = cpumask_first(cpu_online_mask);
        smp_call_function_single(first_cpu, mtrr_save_fixed_ranges, NULL, 1);
@@ -782,6 +806,8 @@ void mtrr_save_state(void)
 
 void set_mtrr_aps_delayed_init(void)
 {
+       if (!mtrr_enabled())
+               return;
        if (!use_intel())
                return;
 
@@ -793,7 +819,7 @@ void set_mtrr_aps_delayed_init(void)
  */
 void mtrr_aps_init(void)
 {
-       if (!use_intel())
+       if (!use_intel() || !mtrr_enabled())
                return;
 
        /*
@@ -810,7 +836,7 @@ void mtrr_aps_init(void)
 
 void mtrr_bp_restore(void)
 {
-       if (!use_intel())
+       if (!use_intel() || !mtrr_enabled())
                return;
 
        mtrr_if->set_all();
@@ -818,7 +844,7 @@ void mtrr_bp_restore(void)
 
 static int __init mtrr_init_finialize(void)
 {
-       if (!mtrr_if)
+       if (!mtrr_enabled())
                return 0;
 
        if (use_intel()) {
index df5e41f31a27e71cd44aadd35cf43f311b437008..951884dcc43354573c2bd234aed3fd3adb067a84 100644 (file)
@@ -51,7 +51,7 @@ void set_mtrr_prepare_save(struct set_mtrr_context *ctxt);
 
 void fill_mtrr_var_range(unsigned int index,
                u32 base_lo, u32 base_hi, u32 mask_lo, u32 mask_hi);
-void get_mtrr_state(void);
+bool get_mtrr_state(void);
 
 extern void set_mtrr_ops(const struct mtrr_ops *ops);
 
index 87848ebe2bb79a56625908c5a6af1b78055d70c9..5801a14f7524315a7318fe5a0f60509704fdb756 100644 (file)
@@ -135,6 +135,7 @@ static int x86_pmu_extra_regs(u64 config, struct perf_event *event)
 }
 
 static atomic_t active_events;
+static atomic_t pmc_refcount;
 static DEFINE_MUTEX(pmc_reserve_mutex);
 
 #ifdef CONFIG_X86_LOCAL_APIC
@@ -190,6 +191,7 @@ static bool check_hw_exists(void)
        u64 val, val_fail, val_new= ~0;
        int i, reg, reg_fail, ret = 0;
        int bios_fail = 0;
+       int reg_safe = -1;
 
        /*
         * Check to see if the BIOS enabled any of the counters, if so
@@ -204,6 +206,8 @@ static bool check_hw_exists(void)
                        bios_fail = 1;
                        val_fail = val;
                        reg_fail = reg;
+               } else {
+                       reg_safe = i;
                }
        }
 
@@ -221,12 +225,23 @@ static bool check_hw_exists(void)
                }
        }
 
+       /*
+        * If all the counters are enabled, the below test will always
+        * fail.  The tools will also become useless in this scenario.
+        * Just fail and disable the hardware counters.
+        */
+
+       if (reg_safe == -1) {
+               reg = reg_safe;
+               goto msr_fail;
+       }
+
        /*
         * Read the current value, change it and read it back to see if it
         * matches, this is needed to detect certain hardware emulators
         * (qemu/kvm) that don't trap on the MSR access and always return 0s.
         */
-       reg = x86_pmu_event_addr(0);
+       reg = x86_pmu_event_addr(reg_safe);
        if (rdmsrl_safe(reg, &val))
                goto msr_fail;
        val ^= 0xffffUL;
@@ -256,11 +271,8 @@ msr_fail:
 
 static void hw_perf_event_destroy(struct perf_event *event)
 {
-       if (atomic_dec_and_mutex_lock(&active_events, &pmc_reserve_mutex)) {
-               release_pmc_hardware();
-               release_ds_buffers();
-               mutex_unlock(&pmc_reserve_mutex);
-       }
+       x86_release_hardware();
+       atomic_dec(&active_events);
 }
 
 void hw_perf_lbr_event_destroy(struct perf_event *event)
@@ -310,6 +322,35 @@ set_ext_hw_attr(struct hw_perf_event *hwc, struct perf_event *event)
        return x86_pmu_extra_regs(val, event);
 }
 
+int x86_reserve_hardware(void)
+{
+       int err = 0;
+
+       if (!atomic_inc_not_zero(&pmc_refcount)) {
+               mutex_lock(&pmc_reserve_mutex);
+               if (atomic_read(&pmc_refcount) == 0) {
+                       if (!reserve_pmc_hardware())
+                               err = -EBUSY;
+                       else
+                               reserve_ds_buffers();
+               }
+               if (!err)
+                       atomic_inc(&pmc_refcount);
+               mutex_unlock(&pmc_reserve_mutex);
+       }
+
+       return err;
+}
+
+void x86_release_hardware(void)
+{
+       if (atomic_dec_and_mutex_lock(&pmc_refcount, &pmc_reserve_mutex)) {
+               release_pmc_hardware();
+               release_ds_buffers();
+               mutex_unlock(&pmc_reserve_mutex);
+       }
+}
+
 /*
  * Check if we can create event of a certain type (that no conflicting events
  * are present).
@@ -322,21 +363,34 @@ int x86_add_exclusive(unsigned int what)
                return 0;
 
        mutex_lock(&pmc_reserve_mutex);
-       for (i = 0; i < ARRAY_SIZE(x86_pmu.lbr_exclusive); i++)
+       for (i = 0; i < ARRAY_SIZE(x86_pmu.lbr_exclusive); i++) {
                if (i != what && atomic_read(&x86_pmu.lbr_exclusive[i]))
                        goto out;
+       }
 
        atomic_inc(&x86_pmu.lbr_exclusive[what]);
        ret = 0;
 
 out:
        mutex_unlock(&pmc_reserve_mutex);
+
+       /*
+        * Assuming that all exclusive events will share the PMI handler
+        * (which checks active_events for whether there is work to do),
+        * we can bump active_events counter right here, except for
+        * x86_lbr_exclusive_lbr events that go through x86_pmu_event_init()
+        * path, which already bumps active_events for them.
+        */
+       if (!ret && what != x86_lbr_exclusive_lbr)
+               atomic_inc(&active_events);
+
        return ret;
 }
 
 void x86_del_exclusive(unsigned int what)
 {
        atomic_dec(&x86_pmu.lbr_exclusive[what]);
+       atomic_dec(&active_events);
 }
 
 int x86_setup_perfctr(struct perf_event *event)
@@ -513,22 +567,11 @@ static int __x86_pmu_event_init(struct perf_event *event)
        if (!x86_pmu_initialized())
                return -ENODEV;
 
-       err = 0;
-       if (!atomic_inc_not_zero(&active_events)) {
-               mutex_lock(&pmc_reserve_mutex);
-               if (atomic_read(&active_events) == 0) {
-                       if (!reserve_pmc_hardware())
-                               err = -EBUSY;
-                       else
-                               reserve_ds_buffers();
-               }
-               if (!err)
-                       atomic_inc(&active_events);
-               mutex_unlock(&pmc_reserve_mutex);
-       }
+       err = x86_reserve_hardware();
        if (err)
                return err;
 
+       atomic_inc(&active_events);
        event->destroy = hw_perf_event_destroy;
 
        event->hw.idx = -1;
@@ -611,6 +654,7 @@ struct sched_state {
        int     event;          /* event index */
        int     counter;        /* counter index */
        int     unassigned;     /* number of events to be assigned left */
+       int     nr_gp;          /* number of GP counters used */
        unsigned long used[BITS_TO_LONGS(X86_PMC_IDX_MAX)];
 };
 
@@ -620,27 +664,29 @@ struct sched_state {
 struct perf_sched {
        int                     max_weight;
        int                     max_events;
-       struct perf_event       **events;
-       struct sched_state      state;
+       int                     max_gp;
        int                     saved_states;
+       struct event_constraint **constraints;
+       struct sched_state      state;
        struct sched_state      saved[SCHED_STATES_MAX];
 };
 
 /*
  * Initialize interator that runs through all events and counters.
  */
-static void perf_sched_init(struct perf_sched *sched, struct perf_event **events,
-                           int num, int wmin, int wmax)
+static void perf_sched_init(struct perf_sched *sched, struct event_constraint **constraints,
+                           int num, int wmin, int wmax, int gpmax)
 {
        int idx;
 
        memset(sched, 0, sizeof(*sched));
        sched->max_events       = num;
        sched->max_weight       = wmax;
-       sched->events           = events;
+       sched->max_gp           = gpmax;
+       sched->constraints      = constraints;
 
        for (idx = 0; idx < num; idx++) {
-               if (events[idx]->hw.constraint->weight == wmin)
+               if (constraints[idx]->weight == wmin)
                        break;
        }
 
@@ -687,7 +733,7 @@ static bool __perf_sched_find_counter(struct perf_sched *sched)
        if (sched->state.event >= sched->max_events)
                return false;
 
-       c = sched->events[sched->state.event]->hw.constraint;
+       c = sched->constraints[sched->state.event];
        /* Prefer fixed purpose counters */
        if (c->idxmsk64 & (~0ULL << INTEL_PMC_IDX_FIXED)) {
                idx = INTEL_PMC_IDX_FIXED;
@@ -696,11 +742,16 @@ static bool __perf_sched_find_counter(struct perf_sched *sched)
                                goto done;
                }
        }
+
        /* Grab the first unused counter starting with idx */
        idx = sched->state.counter;
        for_each_set_bit_from(idx, c->idxmsk, INTEL_PMC_IDX_FIXED) {
-               if (!__test_and_set_bit(idx, sched->state.used))
+               if (!__test_and_set_bit(idx, sched->state.used)) {
+                       if (sched->state.nr_gp++ >= sched->max_gp)
+                               return false;
+
                        goto done;
+               }
        }
 
        return false;
@@ -745,7 +796,7 @@ static bool perf_sched_next_event(struct perf_sched *sched)
                        if (sched->state.weight > sched->max_weight)
                                return false;
                }
-               c = sched->events[sched->state.event]->hw.constraint;
+               c = sched->constraints[sched->state.event];
        } while (c->weight != sched->state.weight);
 
        sched->state.counter = 0;       /* start with first counter */
@@ -756,12 +807,12 @@ static bool perf_sched_next_event(struct perf_sched *sched)
 /*
  * Assign a counter for each event.
  */
-int perf_assign_events(struct perf_event **events, int n,
-                       int wmin, int wmax, int *assign)
+int perf_assign_events(struct event_constraint **constraints, int n,
+                       int wmin, int wmax, int gpmax, int *assign)
 {
        struct perf_sched sched;
 
-       perf_sched_init(&sched, events, n, wmin, wmax);
+       perf_sched_init(&sched, constraints, n, wmin, wmax, gpmax);
 
        do {
                if (!perf_sched_find_counter(&sched))
@@ -788,9 +839,9 @@ int x86_schedule_events(struct cpu_hw_events *cpuc, int n, int *assign)
                x86_pmu.start_scheduling(cpuc);
 
        for (i = 0, wmin = X86_PMC_IDX_MAX, wmax = 0; i < n; i++) {
-               hwc = &cpuc->event_list[i]->hw;
+               cpuc->event_constraint[i] = NULL;
                c = x86_pmu.get_event_constraints(cpuc, i, cpuc->event_list[i]);
-               hwc->constraint = c;
+               cpuc->event_constraint[i] = c;
 
                wmin = min(wmin, c->weight);
                wmax = max(wmax, c->weight);
@@ -801,7 +852,7 @@ int x86_schedule_events(struct cpu_hw_events *cpuc, int n, int *assign)
         */
        for (i = 0; i < n; i++) {
                hwc = &cpuc->event_list[i]->hw;
-               c = hwc->constraint;
+               c = cpuc->event_constraint[i];
 
                /* never assigned */
                if (hwc->idx == -1)
@@ -821,9 +872,26 @@ int x86_schedule_events(struct cpu_hw_events *cpuc, int n, int *assign)
        }
 
        /* slow path */
-       if (i != n)
-               unsched = perf_assign_events(cpuc->event_list, n, wmin,
-                                            wmax, assign);
+       if (i != n) {
+               int gpmax = x86_pmu.num_counters;
+
+               /*
+                * Do not allow scheduling of more than half the available
+                * generic counters.
+                *
+                * This helps avoid counter starvation of sibling thread by
+                * ensuring at most half the counters cannot be in exclusive
+                * mode. There is no designated counters for the limits. Any
+                * N/2 counters can be used. This helps with events with
+                * specific counter constraints.
+                */
+               if (is_ht_workaround_enabled() && !cpuc->is_fake &&
+                   READ_ONCE(cpuc->excl_cntrs->exclusive_present))
+                       gpmax /= 2;
+
+               unsched = perf_assign_events(cpuc->event_constraint, n, wmin,
+                                            wmax, gpmax, assign);
+       }
 
        /*
         * In case of success (unsched = 0), mark events as committed,
@@ -840,12 +908,9 @@ int x86_schedule_events(struct cpu_hw_events *cpuc, int n, int *assign)
                        e = cpuc->event_list[i];
                        e->hw.flags |= PERF_X86_EVENT_COMMITTED;
                        if (x86_pmu.commit_scheduling)
-                               x86_pmu.commit_scheduling(cpuc, e, assign[i]);
+                               x86_pmu.commit_scheduling(cpuc, i, assign[i]);
                }
-       }
-
-       if (!assign || unsched) {
-
+       } else {
                for (i = 0; i < n; i++) {
                        e = cpuc->event_list[i];
                        /*
@@ -1058,13 +1123,16 @@ int x86_perf_event_set_period(struct perf_event *event)
 
        per_cpu(pmc_prev_left[idx], smp_processor_id()) = left;
 
-       /*
-        * The hw event starts counting from this event offset,
-        * mark it to be able to extra future deltas:
-        */
-       local64_set(&hwc->prev_count, (u64)-left);
+       if (!(hwc->flags & PERF_X86_EVENT_AUTO_RELOAD) ||
+           local64_read(&hwc->prev_count) != (u64)-left) {
+               /*
+                * The hw event starts counting from this event offset,
+                * mark it to be able to extra future deltas:
+                */
+               local64_set(&hwc->prev_count, (u64)-left);
 
-       wrmsrl(hwc->event_base, (u64)(-left) & x86_pmu.cntval_mask);
+               wrmsrl(hwc->event_base, (u64)(-left) & x86_pmu.cntval_mask);
+       }
 
        /*
         * Due to erratum on certan cpu we need
@@ -1292,8 +1360,10 @@ static void x86_pmu_del(struct perf_event *event, int flags)
                x86_pmu.put_event_constraints(cpuc, event);
 
        /* Delete the array entry. */
-       while (++i < cpuc->n_events)
+       while (++i < cpuc->n_events) {
                cpuc->event_list[i-1] = cpuc->event_list[i];
+               cpuc->event_constraint[i-1] = cpuc->event_constraint[i];
+       }
        --cpuc->n_events;
 
        perf_event_update_userpage(event);
@@ -1374,6 +1444,10 @@ perf_event_nmi_handler(unsigned int cmd, struct pt_regs *regs)
        u64 finish_clock;
        int ret;
 
+       /*
+        * All PMUs/events that share this PMI handler should make sure to
+        * increment active_events for their events.
+        */
        if (!atomic_read(&active_events))
                return NMI_DONE;
 
index 6ac5cb7a9e14839dcd0b622a91f0f0939133c81e..3e7fd27dfe201718860185be3fe5eeb3c028ed5c 100644 (file)
@@ -74,6 +74,9 @@ struct event_constraint {
 #define PERF_X86_EVENT_EXCL            0x0040 /* HT exclusivity on counter */
 #define PERF_X86_EVENT_DYNAMIC         0x0080 /* dynamic alloc'd constraint */
 #define PERF_X86_EVENT_RDPMC_ALLOWED   0x0100 /* grant rdpmc permission */
+#define PERF_X86_EVENT_EXCL_ACCT       0x0200 /* accounted EXCL event */
+#define PERF_X86_EVENT_AUTO_RELOAD     0x0400 /* use PEBS auto-reload */
+#define PERF_X86_EVENT_FREERUNNING     0x0800 /* use freerunning PEBS */
 
 
 struct amd_nb {
@@ -86,6 +89,18 @@ struct amd_nb {
 /* The maximal number of PEBS events: */
 #define MAX_PEBS_EVENTS                8
 
+/*
+ * Flags PEBS can handle without an PMI.
+ *
+ * TID can only be handled by flushing at context switch.
+ *
+ */
+#define PEBS_FREERUNNING_FLAGS \
+       (PERF_SAMPLE_IP | PERF_SAMPLE_TID | PERF_SAMPLE_ADDR | \
+       PERF_SAMPLE_ID | PERF_SAMPLE_CPU | PERF_SAMPLE_STREAM_ID | \
+       PERF_SAMPLE_DATA_SRC | PERF_SAMPLE_IDENTIFIER | \
+       PERF_SAMPLE_TRANSACTION)
+
 /*
  * A debug store configuration.
  *
@@ -132,10 +147,7 @@ enum intel_excl_state_type {
 };
 
 struct intel_excl_states {
-       enum intel_excl_state_type init_state[X86_PMC_IDX_MAX];
        enum intel_excl_state_type state[X86_PMC_IDX_MAX];
-       int  num_alloc_cntrs;/* #counters allocated */
-       int  max_alloc_cntrs;/* max #counters allowed */
        bool sched_started; /* true if scheduling has started */
 };
 
@@ -144,6 +156,11 @@ struct intel_excl_cntrs {
 
        struct intel_excl_states states[2];
 
+       union {
+               u16     has_exclusive[2];
+               u32     exclusive_present;
+       };
+
        int             refcnt;         /* per-core: #HT threads */
        unsigned        core_id;        /* per-core: core id */
 };
@@ -172,7 +189,11 @@ struct cpu_hw_events {
                                             added in the current transaction */
        int                     assign[X86_PMC_IDX_MAX]; /* event to counter assignment */
        u64                     tags[X86_PMC_IDX_MAX];
+
        struct perf_event       *event_list[X86_PMC_IDX_MAX]; /* in enabled order */
+       struct event_constraint *event_constraint[X86_PMC_IDX_MAX];
+
+       int                     n_excl; /* the number of exclusive events */
 
        unsigned int            group_flag;
        int                     is_fake;
@@ -519,12 +540,10 @@ struct x86_pmu {
        void            (*put_event_constraints)(struct cpu_hw_events *cpuc,
                                                 struct perf_event *event);
 
-       void            (*commit_scheduling)(struct cpu_hw_events *cpuc,
-                                            struct perf_event *event,
-                                            int cntr);
-
        void            (*start_scheduling)(struct cpu_hw_events *cpuc);
 
+       void            (*commit_scheduling)(struct cpu_hw_events *cpuc, int idx, int cntr);
+
        void            (*stop_scheduling)(struct cpu_hw_events *cpuc);
 
        struct event_constraint *event_constraints;
@@ -697,6 +716,10 @@ int x86_add_exclusive(unsigned int what);
 
 void x86_del_exclusive(unsigned int what);
 
+int x86_reserve_hardware(void);
+
+void x86_release_hardware(void);
+
 void hw_perf_lbr_event_destroy(struct perf_event *event);
 
 int x86_setup_perfctr(struct perf_event *event);
@@ -717,8 +740,8 @@ static inline void __x86_pmu_enable_event(struct hw_perf_event *hwc,
 
 void x86_pmu_enable_all(int added);
 
-int perf_assign_events(struct perf_event **events, int n,
-                       int wmin, int wmax, int *assign);
+int perf_assign_events(struct event_constraint **constraints, int n,
+                       int wmin, int wmax, int gpmax, int *assign);
 int x86_schedule_events(struct cpu_hw_events *cpuc, int n, int *assign);
 
 void x86_pmu_stop(struct perf_event *event, int flags);
@@ -860,6 +883,8 @@ void intel_pmu_pebs_enable_all(void);
 
 void intel_pmu_pebs_disable_all(void);
 
+void intel_pmu_pebs_sched_task(struct perf_event_context *ctx, bool sched_in);
+
 void intel_ds_init(void);
 
 void intel_pmu_lbr_sched_task(struct perf_event_context *ctx, bool sched_in);
@@ -929,4 +954,8 @@ static inline struct intel_shared_regs *allocate_shared_regs(int cpu)
        return NULL;
 }
 
+static inline int is_ht_workaround_enabled(void)
+{
+       return 0;
+}
 #endif /* CONFIG_CPU_SUP_INTEL */
index 32481773577129f2ef4d026a4d8d89c7e40ba6a1..b9826a981fb20fa45a7c1255e277e9ad1cd5d150 100644 (file)
@@ -1903,9 +1903,8 @@ static void
 intel_start_scheduling(struct cpu_hw_events *cpuc)
 {
        struct intel_excl_cntrs *excl_cntrs = cpuc->excl_cntrs;
-       struct intel_excl_states *xl, *xlo;
+       struct intel_excl_states *xl;
        int tid = cpuc->excl_thread_id;
-       int o_tid = 1 - tid; /* sibling thread */
 
        /*
         * nothing needed if in group validation mode
@@ -1916,35 +1915,52 @@ intel_start_scheduling(struct cpu_hw_events *cpuc)
        /*
         * no exclusion needed
         */
-       if (!excl_cntrs)
+       if (WARN_ON_ONCE(!excl_cntrs))
                return;
 
-       xlo = &excl_cntrs->states[o_tid];
        xl = &excl_cntrs->states[tid];
 
        xl->sched_started = true;
-       xl->num_alloc_cntrs = 0;
        /*
         * lock shared state until we are done scheduling
         * in stop_event_scheduling()
         * makes scheduling appear as a transaction
         */
-       WARN_ON_ONCE(!irqs_disabled());
        raw_spin_lock(&excl_cntrs->lock);
+}
 
-       /*
-        * save initial state of sibling thread
-        */
-       memcpy(xlo->init_state, xlo->state, sizeof(xlo->init_state));
+static void intel_commit_scheduling(struct cpu_hw_events *cpuc, int idx, int cntr)
+{
+       struct intel_excl_cntrs *excl_cntrs = cpuc->excl_cntrs;
+       struct event_constraint *c = cpuc->event_constraint[idx];
+       struct intel_excl_states *xl;
+       int tid = cpuc->excl_thread_id;
+
+       if (cpuc->is_fake || !is_ht_workaround_enabled())
+               return;
+
+       if (WARN_ON_ONCE(!excl_cntrs))
+               return;
+
+       if (!(c->flags & PERF_X86_EVENT_DYNAMIC))
+               return;
+
+       xl = &excl_cntrs->states[tid];
+
+       lockdep_assert_held(&excl_cntrs->lock);
+
+       if (c->flags & PERF_X86_EVENT_EXCL)
+               xl->state[cntr] = INTEL_EXCL_EXCLUSIVE;
+       else
+               xl->state[cntr] = INTEL_EXCL_SHARED;
 }
 
 static void
 intel_stop_scheduling(struct cpu_hw_events *cpuc)
 {
        struct intel_excl_cntrs *excl_cntrs = cpuc->excl_cntrs;
-       struct intel_excl_states *xl, *xlo;
+       struct intel_excl_states *xl;
        int tid = cpuc->excl_thread_id;
-       int o_tid = 1 - tid; /* sibling thread */
 
        /*
         * nothing needed if in group validation mode
@@ -1954,17 +1970,11 @@ intel_stop_scheduling(struct cpu_hw_events *cpuc)
        /*
         * no exclusion needed
         */
-       if (!excl_cntrs)
+       if (WARN_ON_ONCE(!excl_cntrs))
                return;
 
-       xlo = &excl_cntrs->states[o_tid];
        xl = &excl_cntrs->states[tid];
 
-       /*
-        * make new sibling thread state visible
-        */
-       memcpy(xlo->state, xlo->init_state, sizeof(xlo->state));
-
        xl->sched_started = false;
        /*
         * release shared state lock (acquired in intel_start_scheduling())
@@ -1976,12 +1986,10 @@ static struct event_constraint *
 intel_get_excl_constraints(struct cpu_hw_events *cpuc, struct perf_event *event,
                           int idx, struct event_constraint *c)
 {
-       struct event_constraint *cx;
        struct intel_excl_cntrs *excl_cntrs = cpuc->excl_cntrs;
-       struct intel_excl_states *xl, *xlo;
-       int is_excl, i;
+       struct intel_excl_states *xlo;
        int tid = cpuc->excl_thread_id;
-       int o_tid = 1 - tid; /* alternate */
+       int is_excl, i;
 
        /*
         * validating a group does not require
@@ -1993,34 +2001,8 @@ intel_get_excl_constraints(struct cpu_hw_events *cpuc, struct perf_event *event,
        /*
         * no exclusion needed
         */
-       if (!excl_cntrs)
+       if (WARN_ON_ONCE(!excl_cntrs))
                return c;
-       /*
-        * event requires exclusive counter access
-        * across HT threads
-        */
-       is_excl = c->flags & PERF_X86_EVENT_EXCL;
-
-       /*
-        * xl = state of current HT
-        * xlo = state of sibling HT
-        */
-       xl = &excl_cntrs->states[tid];
-       xlo = &excl_cntrs->states[o_tid];
-
-       /*
-        * do not allow scheduling of more than max_alloc_cntrs
-        * which is set to half the available generic counters.
-        * this helps avoid counter starvation of sibling thread
-        * by ensuring at most half the counters cannot be in
-        * exclusive mode. There is not designated counters for the
-        * limits. Any N/2 counters can be used. This helps with
-        * events with specifix counter constraints
-        */
-       if (xl->num_alloc_cntrs++ == xl->max_alloc_cntrs)
-               return &emptyconstraint;
-
-       cx = c;
 
        /*
         * because we modify the constraint, we need
@@ -2031,10 +2013,7 @@ intel_get_excl_constraints(struct cpu_hw_events *cpuc, struct perf_event *event,
         * been cloned (marked dynamic)
         */
        if (!(c->flags & PERF_X86_EVENT_DYNAMIC)) {
-
-               /* sanity check */
-               if (idx < 0)
-                       return &emptyconstraint;
+               struct event_constraint *cx;
 
                /*
                 * grab pre-allocated constraint entry
@@ -2045,13 +2024,14 @@ intel_get_excl_constraints(struct cpu_hw_events *cpuc, struct perf_event *event,
                 * initialize dynamic constraint
                 * with static constraint
                 */
-               memcpy(cx, c, sizeof(*cx));
+               *cx = *c;
 
                /*
                 * mark constraint as dynamic, so we
                 * can free it later on
                 */
                cx->flags |= PERF_X86_EVENT_DYNAMIC;
+               c = cx;
        }
 
        /*
@@ -2061,6 +2041,22 @@ intel_get_excl_constraints(struct cpu_hw_events *cpuc, struct perf_event *event,
         * of this function
         */
 
+       /*
+        * state of sibling HT
+        */
+       xlo = &excl_cntrs->states[tid ^ 1];
+
+       /*
+        * event requires exclusive counter access
+        * across HT threads
+        */
+       is_excl = c->flags & PERF_X86_EVENT_EXCL;
+       if (is_excl && !(event->hw.flags & PERF_X86_EVENT_EXCL_ACCT)) {
+               event->hw.flags |= PERF_X86_EVENT_EXCL_ACCT;
+               if (!cpuc->n_excl++)
+                       WRITE_ONCE(excl_cntrs->has_exclusive[tid], 1);
+       }
+
        /*
         * Modify static constraint with current dynamic
         * state of thread
@@ -2069,44 +2065,44 @@ intel_get_excl_constraints(struct cpu_hw_events *cpuc, struct perf_event *event,
         * SHARED   : sibling counter measuring non-exclusive event
         * UNUSED   : sibling counter unused
         */
-       for_each_set_bit(i, cx->idxmsk, X86_PMC_IDX_MAX) {
+       for_each_set_bit(i, c->idxmsk, X86_PMC_IDX_MAX) {
                /*
                 * exclusive event in sibling counter
                 * our corresponding counter cannot be used
                 * regardless of our event
                 */
-               if (xl->state[i] == INTEL_EXCL_EXCLUSIVE)
-                       __clear_bit(i, cx->idxmsk);
+               if (xlo->state[i] == INTEL_EXCL_EXCLUSIVE)
+                       __clear_bit(i, c->idxmsk);
                /*
                 * if measuring an exclusive event, sibling
                 * measuring non-exclusive, then counter cannot
                 * be used
                 */
-               if (is_excl && xl->state[i] == INTEL_EXCL_SHARED)
-                       __clear_bit(i, cx->idxmsk);
+               if (is_excl && xlo->state[i] == INTEL_EXCL_SHARED)
+                       __clear_bit(i, c->idxmsk);
        }
 
        /*
         * recompute actual bit weight for scheduling algorithm
         */
-       cx->weight = hweight64(cx->idxmsk64);
+       c->weight = hweight64(c->idxmsk64);
 
        /*
         * if we return an empty mask, then switch
         * back to static empty constraint to avoid
         * the cost of freeing later on
         */
-       if (cx->weight == 0)
-               cx = &emptyconstraint;
+       if (c->weight == 0)
+               c = &emptyconstraint;
 
-       return cx;
+       return c;
 }
 
 static struct event_constraint *
 intel_get_event_constraints(struct cpu_hw_events *cpuc, int idx,
                            struct perf_event *event)
 {
-       struct event_constraint *c1 = event->hw.constraint;
+       struct event_constraint *c1 = cpuc->event_constraint[idx];
        struct event_constraint *c2;
 
        /*
@@ -2132,10 +2128,8 @@ static void intel_put_excl_constraints(struct cpu_hw_events *cpuc,
 {
        struct hw_perf_event *hwc = &event->hw;
        struct intel_excl_cntrs *excl_cntrs = cpuc->excl_cntrs;
-       struct intel_excl_states *xlo, *xl;
-       unsigned long flags = 0; /* keep compiler happy */
        int tid = cpuc->excl_thread_id;
-       int o_tid = 1 - tid;
+       struct intel_excl_states *xl;
 
        /*
         * nothing needed if in group validation mode
@@ -2143,31 +2137,35 @@ static void intel_put_excl_constraints(struct cpu_hw_events *cpuc,
        if (cpuc->is_fake)
                return;
 
-       WARN_ON_ONCE(!excl_cntrs);
-
-       if (!excl_cntrs)
+       if (WARN_ON_ONCE(!excl_cntrs))
                return;
 
-       xl = &excl_cntrs->states[tid];
-       xlo = &excl_cntrs->states[o_tid];
+       if (hwc->flags & PERF_X86_EVENT_EXCL_ACCT) {
+               hwc->flags &= ~PERF_X86_EVENT_EXCL_ACCT;
+               if (!--cpuc->n_excl)
+                       WRITE_ONCE(excl_cntrs->has_exclusive[tid], 0);
+       }
 
        /*
-        * put_constraint may be called from x86_schedule_events()
-        * which already has the lock held so here make locking
-        * conditional
+        * If event was actually assigned, then mark the counter state as
+        * unused now.
         */
-       if (!xl->sched_started)
-               raw_spin_lock_irqsave(&excl_cntrs->lock, flags);
+       if (hwc->idx >= 0) {
+               xl = &excl_cntrs->states[tid];
 
-       /*
-        * if event was actually assigned, then mark the
-        * counter state as unused now
-        */
-       if (hwc->idx >= 0)
-               xlo->state[hwc->idx] = INTEL_EXCL_UNUSED;
+               /*
+                * put_constraint may be called from x86_schedule_events()
+                * which already has the lock held so here make locking
+                * conditional.
+                */
+               if (!xl->sched_started)
+                       raw_spin_lock(&excl_cntrs->lock);
 
-       if (!xl->sched_started)
-               raw_spin_unlock_irqrestore(&excl_cntrs->lock, flags);
+               xl->state[hwc->idx] = INTEL_EXCL_UNUSED;
+
+               if (!xl->sched_started)
+                       raw_spin_unlock(&excl_cntrs->lock);
+       }
 }
 
 static void
@@ -2188,8 +2186,6 @@ intel_put_shared_regs_event_constraints(struct cpu_hw_events *cpuc,
 static void intel_put_event_constraints(struct cpu_hw_events *cpuc,
                                        struct perf_event *event)
 {
-       struct event_constraint *c = event->hw.constraint;
-
        intel_put_shared_regs_event_constraints(cpuc, event);
 
        /*
@@ -2197,48 +2193,8 @@ static void intel_put_event_constraints(struct cpu_hw_events *cpuc,
         * all events are subject to and must call the
         * put_excl_constraints() routine
         */
-       if (c && cpuc->excl_cntrs)
+       if (cpuc->excl_cntrs)
                intel_put_excl_constraints(cpuc, event);
-
-       /* cleanup dynamic constraint */
-       if (c && (c->flags & PERF_X86_EVENT_DYNAMIC))
-               event->hw.constraint = NULL;
-}
-
-static void intel_commit_scheduling(struct cpu_hw_events *cpuc,
-                                   struct perf_event *event, int cntr)
-{
-       struct intel_excl_cntrs *excl_cntrs = cpuc->excl_cntrs;
-       struct event_constraint *c = event->hw.constraint;
-       struct intel_excl_states *xlo, *xl;
-       int tid = cpuc->excl_thread_id;
-       int o_tid = 1 - tid;
-       int is_excl;
-
-       if (cpuc->is_fake || !c)
-               return;
-
-       is_excl = c->flags & PERF_X86_EVENT_EXCL;
-
-       if (!(c->flags & PERF_X86_EVENT_DYNAMIC))
-               return;
-
-       WARN_ON_ONCE(!excl_cntrs);
-
-       if (!excl_cntrs)
-               return;
-
-       xl = &excl_cntrs->states[tid];
-       xlo = &excl_cntrs->states[o_tid];
-
-       WARN_ON_ONCE(!raw_spin_is_locked(&excl_cntrs->lock));
-
-       if (cntr >= 0) {
-               if (is_excl)
-                       xlo->init_state[cntr] = INTEL_EXCL_EXCLUSIVE;
-               else
-                       xlo->init_state[cntr] = INTEL_EXCL_SHARED;
-       }
 }
 
 static void intel_pebs_aliases_core2(struct perf_event *event)
@@ -2304,8 +2260,15 @@ static int intel_pmu_hw_config(struct perf_event *event)
        if (ret)
                return ret;
 
-       if (event->attr.precise_ip && x86_pmu.pebs_aliases)
-               x86_pmu.pebs_aliases(event);
+       if (event->attr.precise_ip) {
+               if (!event->attr.freq) {
+                       event->hw.flags |= PERF_X86_EVENT_AUTO_RELOAD;
+                       if (!(event->attr.sample_type & ~PEBS_FREERUNNING_FLAGS))
+                               event->hw.flags |= PERF_X86_EVENT_FREERUNNING;
+               }
+               if (x86_pmu.pebs_aliases)
+                       x86_pmu.pebs_aliases(event);
+       }
 
        if (needs_branch_stack(event)) {
                ret = intel_pmu_setup_lbr_filter(event);
@@ -2554,19 +2517,11 @@ struct intel_shared_regs *allocate_shared_regs(int cpu)
 static struct intel_excl_cntrs *allocate_excl_cntrs(int cpu)
 {
        struct intel_excl_cntrs *c;
-       int i;
 
        c = kzalloc_node(sizeof(struct intel_excl_cntrs),
                         GFP_KERNEL, cpu_to_node(cpu));
        if (c) {
                raw_spin_lock_init(&c->lock);
-               for (i = 0; i < X86_PMC_IDX_MAX; i++) {
-                       c->states[0].state[i] = INTEL_EXCL_UNUSED;
-                       c->states[0].init_state[i] = INTEL_EXCL_UNUSED;
-
-                       c->states[1].state[i] = INTEL_EXCL_UNUSED;
-                       c->states[1].init_state[i] = INTEL_EXCL_UNUSED;
-               }
                c->core_id = -1;
        }
        return c;
@@ -2639,8 +2594,6 @@ static void intel_pmu_cpu_starting(int cpu)
                cpuc->lbr_sel = &cpuc->shared_regs->regs[EXTRA_REG_LBR];
 
        if (x86_pmu.flags & PMU_FL_EXCL_CNTRS) {
-               int h = x86_pmu.num_counters >> 1;
-
                for_each_cpu(i, topology_sibling_cpumask(cpu)) {
                        struct intel_excl_cntrs *c;
 
@@ -2654,11 +2607,6 @@ static void intel_pmu_cpu_starting(int cpu)
                }
                cpuc->excl_cntrs->core_id = core_id;
                cpuc->excl_cntrs->refcnt++;
-               /*
-                * set hard limit to half the number of generic counters
-                */
-               cpuc->excl_cntrs->states[0].max_alloc_cntrs = h;
-               cpuc->excl_cntrs->states[1].max_alloc_cntrs = h;
        }
 }
 
@@ -2694,6 +2642,15 @@ static void intel_pmu_cpu_dying(int cpu)
        fini_debug_store_on_cpu(cpu);
 }
 
+static void intel_pmu_sched_task(struct perf_event_context *ctx,
+                                bool sched_in)
+{
+       if (x86_pmu.pebs_active)
+               intel_pmu_pebs_sched_task(ctx, sched_in);
+       if (x86_pmu.lbr_nr)
+               intel_pmu_lbr_sched_task(ctx, sched_in);
+}
+
 PMU_FORMAT_ATTR(offcore_rsp, "config1:0-63");
 
 PMU_FORMAT_ATTR(ldlat, "config1:0-15");
@@ -2783,7 +2740,7 @@ static __initconst const struct x86_pmu intel_pmu = {
        .cpu_starting           = intel_pmu_cpu_starting,
        .cpu_dying              = intel_pmu_cpu_dying,
        .guest_get_msrs         = intel_guest_get_msrs,
-       .sched_task             = intel_pmu_lbr_sched_task,
+       .sched_task             = intel_pmu_sched_task,
 };
 
 static __init void intel_clovertown_quirk(void)
@@ -2956,8 +2913,8 @@ static __init void intel_ht_bug(void)
 {
        x86_pmu.flags |= PMU_FL_EXCL_CNTRS | PMU_FL_EXCL_ENABLED;
 
-       x86_pmu.commit_scheduling = intel_commit_scheduling;
        x86_pmu.start_scheduling = intel_start_scheduling;
+       x86_pmu.commit_scheduling = intel_commit_scheduling;
        x86_pmu.stop_scheduling = intel_stop_scheduling;
 }
 
@@ -3270,6 +3227,8 @@ __init int intel_pmu_init(void)
 
        case 61: /* 14nm Broadwell Core-M */
        case 86: /* 14nm Broadwell Xeon D */
+       case 71: /* 14nm Broadwell + GT3e (Intel Iris Pro graphics) */
+       case 79: /* 14nm Broadwell Server */
                x86_pmu.late_ack = true;
                memcpy(hw_cache_event_ids, hsw_hw_cache_event_ids, sizeof(hw_cache_event_ids));
                memcpy(hw_cache_extra_regs, hsw_hw_cache_extra_regs, sizeof(hw_cache_extra_regs));
@@ -3339,13 +3298,13 @@ __init int intel_pmu_init(void)
                 * counter, so do not extend mask to generic counters
                 */
                for_each_event_constraint(c, x86_pmu.event_constraints) {
-                       if (c->cmask != FIXED_EVENT_FLAGS
-                           || c->idxmsk64 == INTEL_PMC_MSK_FIXED_REF_CYCLES) {
-                               continue;
+                       if (c->cmask == FIXED_EVENT_FLAGS
+                           && c->idxmsk64 != INTEL_PMC_MSK_FIXED_REF_CYCLES) {
+                               c->idxmsk64 |= (1ULL << x86_pmu.num_counters) - 1;
                        }
-
-                       c->idxmsk64 |= (1ULL << x86_pmu.num_counters) - 1;
-                       c->weight += x86_pmu.num_counters;
+                       c->idxmsk64 &=
+                               ~(~0UL << (INTEL_PMC_IDX_FIXED + x86_pmu.num_counters_fixed));
+                       c->weight = hweight64(c->idxmsk64);
                }
        }
 
@@ -3413,8 +3372,8 @@ static __init int fixup_ht_bug(void)
 
        x86_pmu.flags &= ~(PMU_FL_EXCL_CNTRS | PMU_FL_EXCL_ENABLED);
 
-       x86_pmu.commit_scheduling = NULL;
        x86_pmu.start_scheduling = NULL;
+       x86_pmu.commit_scheduling = NULL;
        x86_pmu.stop_scheduling = NULL;
 
        watchdog_nmi_enable_all();
index ac1f0c55f3796e17bdaddc5f946a41509890e446..7795f3f8b1d57198469ded20ac9a1244035428e8 100644 (file)
@@ -483,17 +483,26 @@ static int bts_event_add(struct perf_event *event, int mode)
 
 static void bts_event_destroy(struct perf_event *event)
 {
+       x86_release_hardware();
        x86_del_exclusive(x86_lbr_exclusive_bts);
 }
 
 static int bts_event_init(struct perf_event *event)
 {
+       int ret;
+
        if (event->attr.type != bts_pmu.type)
                return -ENOENT;
 
        if (x86_add_exclusive(x86_lbr_exclusive_bts))
                return -EBUSY;
 
+       ret = x86_reserve_hardware();
+       if (ret) {
+               x86_del_exclusive(x86_lbr_exclusive_bts);
+               return ret;
+       }
+
        event->destroy = bts_event_destroy;
 
        return 0;
index e4d1b8b738fa8a9fc350030c0b41dd75f5309530..188076161c1be51afe135d6289b8ce0e96952bf3 100644 (file)
 #define MSR_IA32_QM_CTR                0x0c8e
 #define MSR_IA32_QM_EVTSEL     0x0c8d
 
-static unsigned int cqm_max_rmid = -1;
+static u32 cqm_max_rmid = -1;
 static unsigned int cqm_l3_scale; /* supposedly cacheline size */
 
-struct intel_cqm_state {
-       raw_spinlock_t          lock;
-       int                     rmid;
-       int                     cnt;
+/**
+ * struct intel_pqr_state - State cache for the PQR MSR
+ * @rmid:              The cached Resource Monitoring ID
+ * @closid:            The cached Class Of Service ID
+ * @rmid_usecnt:       The usage counter for rmid
+ *
+ * The upper 32 bits of MSR_IA32_PQR_ASSOC contain closid and the
+ * lower 10 bits rmid. The update to MSR_IA32_PQR_ASSOC always
+ * contains both parts, so we need to cache them.
+ *
+ * The cache also helps to avoid pointless updates if the value does
+ * not change.
+ */
+struct intel_pqr_state {
+       u32                     rmid;
+       u32                     closid;
+       int                     rmid_usecnt;
 };
 
-static DEFINE_PER_CPU(struct intel_cqm_state, cqm_state);
+/*
+ * The cached intel_pqr_state is strictly per CPU and can never be
+ * updated from a remote CPU. Both functions which modify the state
+ * (intel_cqm_event_start and intel_cqm_event_stop) are called with
+ * interrupts disabled, which is sufficient for the protection.
+ */
+static DEFINE_PER_CPU(struct intel_pqr_state, pqr_state);
 
 /*
  * Protects cache_cgroups and cqm_rmid_free_lru and cqm_rmid_limbo_lru.
@@ -57,7 +76,7 @@ static cpumask_t cqm_cpumask;
  * near-zero occupancy value, i.e. no cachelines are tagged with this
  * RMID, once __intel_cqm_rmid_rotate() returns.
  */
-static unsigned int intel_cqm_rotation_rmid;
+static u32 intel_cqm_rotation_rmid;
 
 #define INVALID_RMID           (-1)
 
@@ -69,7 +88,7 @@ static unsigned int intel_cqm_rotation_rmid;
  * Likewise, an rmid value of -1 is used to indicate "no rmid currently
  * assigned" and is used as part of the rotation code.
  */
-static inline bool __rmid_valid(unsigned int rmid)
+static inline bool __rmid_valid(u32 rmid)
 {
        if (!rmid || rmid == INVALID_RMID)
                return false;
@@ -77,7 +96,7 @@ static inline bool __rmid_valid(unsigned int rmid)
        return true;
 }
 
-static u64 __rmid_read(unsigned int rmid)
+static u64 __rmid_read(u32 rmid)
 {
        u64 val;
 
@@ -102,7 +121,7 @@ enum rmid_recycle_state {
 };
 
 struct cqm_rmid_entry {
-       unsigned int rmid;
+       u32 rmid;
        enum rmid_recycle_state state;
        struct list_head list;
        unsigned long queue_time;
@@ -147,7 +166,7 @@ static LIST_HEAD(cqm_rmid_limbo_lru);
  */
 static struct cqm_rmid_entry **cqm_rmid_ptrs;
 
-static inline struct cqm_rmid_entry *__rmid_entry(int rmid)
+static inline struct cqm_rmid_entry *__rmid_entry(u32 rmid)
 {
        struct cqm_rmid_entry *entry;
 
@@ -162,7 +181,7 @@ static inline struct cqm_rmid_entry *__rmid_entry(int rmid)
  *
  * We expect to be called with cache_mutex held.
  */
-static int __get_rmid(void)
+static u32 __get_rmid(void)
 {
        struct cqm_rmid_entry *entry;
 
@@ -177,7 +196,7 @@ static int __get_rmid(void)
        return entry->rmid;
 }
 
-static void __put_rmid(unsigned int rmid)
+static void __put_rmid(u32 rmid)
 {
        struct cqm_rmid_entry *entry;
 
@@ -372,7 +391,7 @@ static bool __conflict_event(struct perf_event *a, struct perf_event *b)
 }
 
 struct rmid_read {
-       unsigned int rmid;
+       u32 rmid;
        atomic64_t value;
 };
 
@@ -381,12 +400,11 @@ static void __intel_cqm_event_count(void *info);
 /*
  * Exchange the RMID of a group of events.
  */
-static unsigned int
-intel_cqm_xchg_rmid(struct perf_event *group, unsigned int rmid)
+static u32 intel_cqm_xchg_rmid(struct perf_event *group, u32 rmid)
 {
        struct perf_event *event;
-       unsigned int old_rmid = group->hw.cqm_rmid;
        struct list_head *head = &group->hw.cqm_group_entry;
+       u32 old_rmid = group->hw.cqm_rmid;
 
        lockdep_assert_held(&cache_mutex);
 
@@ -451,7 +469,7 @@ static void intel_cqm_stable(void *arg)
  * If we have group events waiting for an RMID that don't conflict with
  * events already running, assign @rmid.
  */
-static bool intel_cqm_sched_in_event(unsigned int rmid)
+static bool intel_cqm_sched_in_event(u32 rmid)
 {
        struct perf_event *leader, *event;
 
@@ -598,7 +616,7 @@ static bool intel_cqm_rmid_stabilize(unsigned int *available)
 static void __intel_cqm_pick_and_rotate(struct perf_event *next)
 {
        struct perf_event *rotor;
-       unsigned int rmid;
+       u32 rmid;
 
        lockdep_assert_held(&cache_mutex);
 
@@ -626,7 +644,7 @@ static void __intel_cqm_pick_and_rotate(struct perf_event *next)
 static void intel_cqm_sched_out_conflicting_events(struct perf_event *event)
 {
        struct perf_event *group, *g;
-       unsigned int rmid;
+       u32 rmid;
 
        lockdep_assert_held(&cache_mutex);
 
@@ -828,8 +846,8 @@ static void intel_cqm_setup_event(struct perf_event *event,
                                  struct perf_event **group)
 {
        struct perf_event *iter;
-       unsigned int rmid;
        bool conflict = false;
+       u32 rmid;
 
        list_for_each_entry(iter, &cache_groups, hw.cqm_groups_entry) {
                rmid = iter->hw.cqm_rmid;
@@ -860,7 +878,7 @@ static void intel_cqm_setup_event(struct perf_event *event,
 static void intel_cqm_event_read(struct perf_event *event)
 {
        unsigned long flags;
-       unsigned int rmid;
+       u32 rmid;
        u64 val;
 
        /*
@@ -961,55 +979,48 @@ out:
 
 static void intel_cqm_event_start(struct perf_event *event, int mode)
 {
-       struct intel_cqm_state *state = this_cpu_ptr(&cqm_state);
-       unsigned int rmid = event->hw.cqm_rmid;
-       unsigned long flags;
+       struct intel_pqr_state *state = this_cpu_ptr(&pqr_state);
+       u32 rmid = event->hw.cqm_rmid;
 
        if (!(event->hw.cqm_state & PERF_HES_STOPPED))
                return;
 
        event->hw.cqm_state &= ~PERF_HES_STOPPED;
 
-       raw_spin_lock_irqsave(&state->lock, flags);
-
-       if (state->cnt++)
-               WARN_ON_ONCE(state->rmid != rmid);
-       else
+       if (state->rmid_usecnt++) {
+               if (!WARN_ON_ONCE(state->rmid != rmid))
+                       return;
+       } else {
                WARN_ON_ONCE(state->rmid);
+       }
 
        state->rmid = rmid;
-       wrmsrl(MSR_IA32_PQR_ASSOC, state->rmid);
-
-       raw_spin_unlock_irqrestore(&state->lock, flags);
+       wrmsr(MSR_IA32_PQR_ASSOC, rmid, state->closid);
 }
 
 static void intel_cqm_event_stop(struct perf_event *event, int mode)
 {
-       struct intel_cqm_state *state = this_cpu_ptr(&cqm_state);
-       unsigned long flags;
+       struct intel_pqr_state *state = this_cpu_ptr(&pqr_state);
 
        if (event->hw.cqm_state & PERF_HES_STOPPED)
                return;
 
        event->hw.cqm_state |= PERF_HES_STOPPED;
 
-       raw_spin_lock_irqsave(&state->lock, flags);
        intel_cqm_event_read(event);
 
-       if (!--state->cnt) {
+       if (!--state->rmid_usecnt) {
                state->rmid = 0;
-               wrmsrl(MSR_IA32_PQR_ASSOC, 0);
+               wrmsr(MSR_IA32_PQR_ASSOC, 0, state->closid);
        } else {
                WARN_ON_ONCE(!state->rmid);
        }
-
-       raw_spin_unlock_irqrestore(&state->lock, flags);
 }
 
 static int intel_cqm_event_add(struct perf_event *event, int mode)
 {
        unsigned long flags;
-       unsigned int rmid;
+       u32 rmid;
 
        raw_spin_lock_irqsave(&cache_lock, flags);
 
@@ -1024,11 +1035,6 @@ static int intel_cqm_event_add(struct perf_event *event, int mode)
        return 0;
 }
 
-static void intel_cqm_event_del(struct perf_event *event, int mode)
-{
-       intel_cqm_event_stop(event, mode);
-}
-
 static void intel_cqm_event_destroy(struct perf_event *event)
 {
        struct perf_event *group_other = NULL;
@@ -1057,7 +1063,7 @@ static void intel_cqm_event_destroy(struct perf_event *event)
                        list_replace(&event->hw.cqm_groups_entry,
                                     &group_other->hw.cqm_groups_entry);
                } else {
-                       unsigned int rmid = event->hw.cqm_rmid;
+                       u32 rmid = event->hw.cqm_rmid;
 
                        if (__rmid_valid(rmid))
                                __put_rmid(rmid);
@@ -1221,7 +1227,7 @@ static struct pmu intel_cqm_pmu = {
        .task_ctx_nr         = perf_sw_context,
        .event_init          = intel_cqm_event_init,
        .add                 = intel_cqm_event_add,
-       .del                 = intel_cqm_event_del,
+       .del                 = intel_cqm_event_stop,
        .start               = intel_cqm_event_start,
        .stop                = intel_cqm_event_stop,
        .read                = intel_cqm_event_read,
@@ -1243,12 +1249,12 @@ static inline void cqm_pick_event_reader(int cpu)
 
 static void intel_cqm_cpu_prepare(unsigned int cpu)
 {
-       struct intel_cqm_state *state = &per_cpu(cqm_state, cpu);
+       struct intel_pqr_state *state = &per_cpu(pqr_state, cpu);
        struct cpuinfo_x86 *c = &cpu_data(cpu);
 
-       raw_spin_lock_init(&state->lock);
        state->rmid = 0;
-       state->cnt  = 0;
+       state->closid = 0;
+       state->rmid_usecnt = 0;
 
        WARN_ON(c->x86_cache_max_rmid != cqm_max_rmid);
        WARN_ON(c->x86_cache_occ_scale != cqm_l3_scale);
index 813f75d71175e3a117f13ec53efe6856a0508bec..71fc40238843bb0e80ad1be14e38e7f7b3787eb5 100644 (file)
@@ -11,7 +11,7 @@
 #define BTS_RECORD_SIZE                24
 
 #define BTS_BUFFER_SIZE                (PAGE_SIZE << 4)
-#define PEBS_BUFFER_SIZE       PAGE_SIZE
+#define PEBS_BUFFER_SIZE       (PAGE_SIZE << 4)
 #define PEBS_FIXUP_SIZE                PAGE_SIZE
 
 /*
@@ -250,7 +250,7 @@ static int alloc_pebs_buffer(int cpu)
 {
        struct debug_store *ds = per_cpu(cpu_hw_events, cpu).ds;
        int node = cpu_to_node(cpu);
-       int max, thresh = 1; /* always use a single PEBS record */
+       int max;
        void *buffer, *ibuffer;
 
        if (!x86_pmu.pebs)
@@ -280,9 +280,6 @@ static int alloc_pebs_buffer(int cpu)
        ds->pebs_absolute_maximum = ds->pebs_buffer_base +
                max * x86_pmu.pebs_record_size;
 
-       ds->pebs_interrupt_threshold = ds->pebs_buffer_base +
-               thresh * x86_pmu.pebs_record_size;
-
        return 0;
 }
 
@@ -549,6 +546,19 @@ int intel_pmu_drain_bts_buffer(void)
        return 1;
 }
 
+static inline void intel_pmu_drain_pebs_buffer(void)
+{
+       struct pt_regs regs;
+
+       x86_pmu.drain_pebs(&regs);
+}
+
+void intel_pmu_pebs_sched_task(struct perf_event_context *ctx, bool sched_in)
+{
+       if (!sched_in)
+               intel_pmu_drain_pebs_buffer();
+}
+
 /*
  * PEBS
  */
@@ -684,33 +694,81 @@ struct event_constraint *intel_pebs_constraints(struct perf_event *event)
        return &emptyconstraint;
 }
 
+static inline bool pebs_is_enabled(struct cpu_hw_events *cpuc)
+{
+       return (cpuc->pebs_enabled & ((1ULL << MAX_PEBS_EVENTS) - 1));
+}
+
 void intel_pmu_pebs_enable(struct perf_event *event)
 {
        struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
        struct hw_perf_event *hwc = &event->hw;
+       struct debug_store *ds = cpuc->ds;
+       bool first_pebs;
+       u64 threshold;
 
        hwc->config &= ~ARCH_PERFMON_EVENTSEL_INT;
 
+       first_pebs = !pebs_is_enabled(cpuc);
        cpuc->pebs_enabled |= 1ULL << hwc->idx;
 
        if (event->hw.flags & PERF_X86_EVENT_PEBS_LDLAT)
                cpuc->pebs_enabled |= 1ULL << (hwc->idx + 32);
        else if (event->hw.flags & PERF_X86_EVENT_PEBS_ST)
                cpuc->pebs_enabled |= 1ULL << 63;
+
+       /*
+        * When the event is constrained enough we can use a larger
+        * threshold and run the event with less frequent PMI.
+        */
+       if (hwc->flags & PERF_X86_EVENT_FREERUNNING) {
+               threshold = ds->pebs_absolute_maximum -
+                       x86_pmu.max_pebs_events * x86_pmu.pebs_record_size;
+
+               if (first_pebs)
+                       perf_sched_cb_inc(event->ctx->pmu);
+       } else {
+               threshold = ds->pebs_buffer_base + x86_pmu.pebs_record_size;
+
+               /*
+                * If not all events can use larger buffer,
+                * roll back to threshold = 1
+                */
+               if (!first_pebs &&
+                   (ds->pebs_interrupt_threshold > threshold))
+                       perf_sched_cb_dec(event->ctx->pmu);
+       }
+
+       /* Use auto-reload if possible to save a MSR write in the PMI */
+       if (hwc->flags & PERF_X86_EVENT_AUTO_RELOAD) {
+               ds->pebs_event_reset[hwc->idx] =
+                       (u64)(-hwc->sample_period) & x86_pmu.cntval_mask;
+       }
+
+       if (first_pebs || ds->pebs_interrupt_threshold > threshold)
+               ds->pebs_interrupt_threshold = threshold;
 }
 
 void intel_pmu_pebs_disable(struct perf_event *event)
 {
        struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
        struct hw_perf_event *hwc = &event->hw;
+       struct debug_store *ds = cpuc->ds;
 
        cpuc->pebs_enabled &= ~(1ULL << hwc->idx);
 
-       if (event->hw.constraint->flags & PERF_X86_EVENT_PEBS_LDLAT)
+       if (event->hw.flags & PERF_X86_EVENT_PEBS_LDLAT)
                cpuc->pebs_enabled &= ~(1ULL << (hwc->idx + 32));
-       else if (event->hw.constraint->flags & PERF_X86_EVENT_PEBS_ST)
+       else if (event->hw.flags & PERF_X86_EVENT_PEBS_ST)
                cpuc->pebs_enabled &= ~(1ULL << 63);
 
+       if (ds->pebs_interrupt_threshold >
+           ds->pebs_buffer_base + x86_pmu.pebs_record_size) {
+               intel_pmu_drain_pebs_buffer();
+               if (!pebs_is_enabled(cpuc))
+                       perf_sched_cb_dec(event->ctx->pmu);
+       }
+
        if (cpuc->enabled)
                wrmsrl(MSR_IA32_PEBS_ENABLE, cpuc->pebs_enabled);
 
@@ -846,8 +904,10 @@ static inline u64 intel_hsw_transaction(struct pebs_record_hsw *pebs)
        return txn;
 }
 
-static void __intel_pmu_pebs_event(struct perf_event *event,
-                                  struct pt_regs *iregs, void *__pebs)
+static void setup_pebs_sample_data(struct perf_event *event,
+                                  struct pt_regs *iregs, void *__pebs,
+                                  struct perf_sample_data *data,
+                                  struct pt_regs *regs)
 {
 #define PERF_X86_EVENT_PEBS_HSW_PREC \
                (PERF_X86_EVENT_PEBS_ST_HSW | \
@@ -859,13 +919,11 @@ static void __intel_pmu_pebs_event(struct perf_event *event,
         */
        struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
        struct pebs_record_hsw *pebs = __pebs;
-       struct perf_sample_data data;
-       struct pt_regs regs;
        u64 sample_type;
        int fll, fst, dsrc;
        int fl = event->hw.flags;
 
-       if (!intel_pmu_save_and_restart(event))
+       if (pebs == NULL)
                return;
 
        sample_type = event->attr.sample_type;
@@ -874,15 +932,15 @@ static void __intel_pmu_pebs_event(struct perf_event *event,
        fll = fl & PERF_X86_EVENT_PEBS_LDLAT;
        fst = fl & (PERF_X86_EVENT_PEBS_ST | PERF_X86_EVENT_PEBS_HSW_PREC);
 
-       perf_sample_data_init(&data, 0, event->hw.last_period);
+       perf_sample_data_init(data, 0, event->hw.last_period);
 
-       data.period = event->hw.last_period;
+       data->period = event->hw.last_period;
 
        /*
         * Use latency for weight (only avail with PEBS-LL)
         */
        if (fll && (sample_type & PERF_SAMPLE_WEIGHT))
-               data.weight = pebs->lat;
+               data->weight = pebs->lat;
 
        /*
         * data.data_src encodes the data source
@@ -895,7 +953,7 @@ static void __intel_pmu_pebs_event(struct perf_event *event,
                        val = precise_datala_hsw(event, pebs->dse);
                else if (fst)
                        val = precise_store_data(pebs->dse);
-               data.data_src.val = val;
+               data->data_src.val = val;
        }
 
        /*
@@ -908,61 +966,123 @@ static void __intel_pmu_pebs_event(struct perf_event *event,
         * PERF_SAMPLE_IP and PERF_SAMPLE_CALLCHAIN to function properly.
         * A possible PERF_SAMPLE_REGS will have to transfer all regs.
         */
-       regs = *iregs;
-       regs.flags = pebs->flags;
-       set_linear_ip(&regs, pebs->ip);
-       regs.bp = pebs->bp;
-       regs.sp = pebs->sp;
+       *regs = *iregs;
+       regs->flags = pebs->flags;
+       set_linear_ip(regs, pebs->ip);
+       regs->bp = pebs->bp;
+       regs->sp = pebs->sp;
 
        if (sample_type & PERF_SAMPLE_REGS_INTR) {
-               regs.ax = pebs->ax;
-               regs.bx = pebs->bx;
-               regs.cx = pebs->cx;
-               regs.dx = pebs->dx;
-               regs.si = pebs->si;
-               regs.di = pebs->di;
-               regs.bp = pebs->bp;
-               regs.sp = pebs->sp;
-
-               regs.flags = pebs->flags;
+               regs->ax = pebs->ax;
+               regs->bx = pebs->bx;
+               regs->cx = pebs->cx;
+               regs->dx = pebs->dx;
+               regs->si = pebs->si;
+               regs->di = pebs->di;
+               regs->bp = pebs->bp;
+               regs->sp = pebs->sp;
+
+               regs->flags = pebs->flags;
 #ifndef CONFIG_X86_32
-               regs.r8 = pebs->r8;
-               regs.r9 = pebs->r9;
-               regs.r10 = pebs->r10;
-               regs.r11 = pebs->r11;
-               regs.r12 = pebs->r12;
-               regs.r13 = pebs->r13;
-               regs.r14 = pebs->r14;
-               regs.r15 = pebs->r15;
+               regs->r8 = pebs->r8;
+               regs->r9 = pebs->r9;
+               regs->r10 = pebs->r10;
+               regs->r11 = pebs->r11;
+               regs->r12 = pebs->r12;
+               regs->r13 = pebs->r13;
+               regs->r14 = pebs->r14;
+               regs->r15 = pebs->r15;
 #endif
        }
 
        if (event->attr.precise_ip > 1 && x86_pmu.intel_cap.pebs_format >= 2) {
-               regs.ip = pebs->real_ip;
-               regs.flags |= PERF_EFLAGS_EXACT;
-       } else if (event->attr.precise_ip > 1 && intel_pmu_pebs_fixup_ip(&regs))
-               regs.flags |= PERF_EFLAGS_EXACT;
+               regs->ip = pebs->real_ip;
+               regs->flags |= PERF_EFLAGS_EXACT;
+       } else if (event->attr.precise_ip > 1 && intel_pmu_pebs_fixup_ip(regs))
+               regs->flags |= PERF_EFLAGS_EXACT;
        else
-               regs.flags &= ~PERF_EFLAGS_EXACT;
+               regs->flags &= ~PERF_EFLAGS_EXACT;
 
        if ((sample_type & PERF_SAMPLE_ADDR) &&
            x86_pmu.intel_cap.pebs_format >= 1)
-               data.addr = pebs->dla;
+               data->addr = pebs->dla;
 
        if (x86_pmu.intel_cap.pebs_format >= 2) {
                /* Only set the TSX weight when no memory weight. */
                if ((sample_type & PERF_SAMPLE_WEIGHT) && !fll)
-                       data.weight = intel_hsw_weight(pebs);
+                       data->weight = intel_hsw_weight(pebs);
 
                if (sample_type & PERF_SAMPLE_TRANSACTION)
-                       data.txn = intel_hsw_transaction(pebs);
+                       data->txn = intel_hsw_transaction(pebs);
        }
 
        if (has_branch_stack(event))
-               data.br_stack = &cpuc->lbr_stack;
+               data->br_stack = &cpuc->lbr_stack;
+}
+
+static inline void *
+get_next_pebs_record_by_bit(void *base, void *top, int bit)
+{
+       struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
+       void *at;
+       u64 pebs_status;
+
+       if (base == NULL)
+               return NULL;
+
+       for (at = base; at < top; at += x86_pmu.pebs_record_size) {
+               struct pebs_record_nhm *p = at;
 
-       if (perf_event_overflow(event, &data, &regs))
+               if (test_bit(bit, (unsigned long *)&p->status)) {
+                       /* PEBS v3 has accurate status bits */
+                       if (x86_pmu.intel_cap.pebs_format >= 3)
+                               return at;
+
+                       if (p->status == (1 << bit))
+                               return at;
+
+                       /* clear non-PEBS bit and re-check */
+                       pebs_status = p->status & cpuc->pebs_enabled;
+                       pebs_status &= (1ULL << MAX_PEBS_EVENTS) - 1;
+                       if (pebs_status == (1 << bit))
+                               return at;
+               }
+       }
+       return NULL;
+}
+
+static void __intel_pmu_pebs_event(struct perf_event *event,
+                                  struct pt_regs *iregs,
+                                  void *base, void *top,
+                                  int bit, int count)
+{
+       struct perf_sample_data data;
+       struct pt_regs regs;
+       void *at = get_next_pebs_record_by_bit(base, top, bit);
+
+       if (!intel_pmu_save_and_restart(event) &&
+           !(event->hw.flags & PERF_X86_EVENT_AUTO_RELOAD))
+               return;
+
+       while (count > 1) {
+               setup_pebs_sample_data(event, iregs, at, &data, &regs);
+               perf_event_output(event, &data, &regs);
+               at += x86_pmu.pebs_record_size;
+               at = get_next_pebs_record_by_bit(at, top, bit);
+               count--;
+       }
+
+       setup_pebs_sample_data(event, iregs, at, &data, &regs);
+
+       /*
+        * All but the last records are processed.
+        * The last one is left to be able to call the overflow handler.
+        */
+       if (perf_event_overflow(event, &data, &regs)) {
                x86_pmu_stop(event, 0);
+               return;
+       }
+
 }
 
 static void intel_pmu_drain_pebs_core(struct pt_regs *iregs)
@@ -992,72 +1112,99 @@ static void intel_pmu_drain_pebs_core(struct pt_regs *iregs)
        if (!event->attr.precise_ip)
                return;
 
-       n = top - at;
+       n = (top - at) / x86_pmu.pebs_record_size;
        if (n <= 0)
                return;
 
-       /*
-        * Should not happen, we program the threshold at 1 and do not
-        * set a reset value.
-        */
-       WARN_ONCE(n > 1, "bad leftover pebs %d\n", n);
-       at += n - 1;
-
-       __intel_pmu_pebs_event(event, iregs, at);
+       __intel_pmu_pebs_event(event, iregs, at, top, 0, n);
 }
 
 static void intel_pmu_drain_pebs_nhm(struct pt_regs *iregs)
 {
        struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
        struct debug_store *ds = cpuc->ds;
-       struct perf_event *event = NULL;
-       void *at, *top;
-       u64 status = 0;
-       int bit;
+       struct perf_event *event;
+       void *base, *at, *top;
+       short counts[MAX_PEBS_EVENTS] = {};
+       short error[MAX_PEBS_EVENTS] = {};
+       int bit, i;
 
        if (!x86_pmu.pebs_active)
                return;
 
-       at  = (struct pebs_record_nhm *)(unsigned long)ds->pebs_buffer_base;
+       base = (struct pebs_record_nhm *)(unsigned long)ds->pebs_buffer_base;
        top = (struct pebs_record_nhm *)(unsigned long)ds->pebs_index;
 
        ds->pebs_index = ds->pebs_buffer_base;
 
-       if (unlikely(at > top))
+       if (unlikely(base >= top))
                return;
 
-       /*
-        * Should not happen, we program the threshold at 1 and do not
-        * set a reset value.
-        */
-       WARN_ONCE(top - at > x86_pmu.max_pebs_events * x86_pmu.pebs_record_size,
-                 "Unexpected number of pebs records %ld\n",
-                 (long)(top - at) / x86_pmu.pebs_record_size);
-
-       for (; at < top; at += x86_pmu.pebs_record_size) {
+       for (at = base; at < top; at += x86_pmu.pebs_record_size) {
                struct pebs_record_nhm *p = at;
 
-               for_each_set_bit(bit, (unsigned long *)&p->status,
-                                x86_pmu.max_pebs_events) {
-                       event = cpuc->events[bit];
-                       if (!test_bit(bit, cpuc->active_mask))
-                               continue;
-
-                       WARN_ON_ONCE(!event);
+               /* PEBS v3 has accurate status bits */
+               if (x86_pmu.intel_cap.pebs_format >= 3) {
+                       for_each_set_bit(bit, (unsigned long *)&p->status,
+                                        MAX_PEBS_EVENTS)
+                               counts[bit]++;
 
-                       if (!event->attr.precise_ip)
-                               continue;
+                       continue;
+               }
 
-                       if (__test_and_set_bit(bit, (unsigned long *)&status))
+               bit = find_first_bit((unsigned long *)&p->status,
+                                       x86_pmu.max_pebs_events);
+               if (bit >= x86_pmu.max_pebs_events)
+                       continue;
+               if (!test_bit(bit, cpuc->active_mask))
+                       continue;
+               /*
+                * The PEBS hardware does not deal well with the situation
+                * when events happen near to each other and multiple bits
+                * are set. But it should happen rarely.
+                *
+                * If these events include one PEBS and multiple non-PEBS
+                * events, it doesn't impact PEBS record. The record will
+                * be handled normally. (slow path)
+                *
+                * If these events include two or more PEBS events, the
+                * records for the events can be collapsed into a single
+                * one, and it's not possible to reconstruct all events
+                * that caused the PEBS record. It's called collision.
+                * If collision happened, the record will be dropped.
+                *
+                */
+               if (p->status != (1 << bit)) {
+                       u64 pebs_status;
+
+                       /* slow path */
+                       pebs_status = p->status & cpuc->pebs_enabled;
+                       pebs_status &= (1ULL << MAX_PEBS_EVENTS) - 1;
+                       if (pebs_status != (1 << bit)) {
+                               for_each_set_bit(i, (unsigned long *)&pebs_status,
+                                                MAX_PEBS_EVENTS)
+                                       error[i]++;
                                continue;
-
-                       break;
+                       }
                }
+               counts[bit]++;
+       }
 
-               if (!event || bit >= x86_pmu.max_pebs_events)
+       for (bit = 0; bit < x86_pmu.max_pebs_events; bit++) {
+               if ((counts[bit] == 0) && (error[bit] == 0))
                        continue;
+               event = cpuc->events[bit];
+               WARN_ON_ONCE(!event);
+               WARN_ON_ONCE(!event->attr.precise_ip);
 
-               __intel_pmu_pebs_event(event, iregs, at);
+               /* log dropped samples number */
+               if (error[bit])
+                       perf_log_lost_samples(event, error[bit]);
+
+               if (counts[bit]) {
+                       __intel_pmu_pebs_event(event, iregs, base,
+                                              top, bit, counts[bit]);
+               }
        }
 }
 
index 94e5b506caa6d13206956095e646bfacbf558fb1..452a7bd2dedb6b72e98fad8da0cb8f2e0d2f32c3 100644 (file)
@@ -96,6 +96,7 @@ enum {
        X86_BR_NO_TX            = 1 << 14,/* not in transaction */
        X86_BR_ZERO_CALL        = 1 << 15,/* zero length call */
        X86_BR_CALL_STACK       = 1 << 16,/* call stack */
+       X86_BR_IND_JMP          = 1 << 17,/* indirect jump */
 };
 
 #define X86_BR_PLM (X86_BR_USER | X86_BR_KERNEL)
@@ -113,6 +114,7 @@ enum {
         X86_BR_IRQ      |\
         X86_BR_ABORT    |\
         X86_BR_IND_CALL |\
+        X86_BR_IND_JMP  |\
         X86_BR_ZERO_CALL)
 
 #define X86_BR_ALL (X86_BR_PLM | X86_BR_ANY)
@@ -262,9 +264,6 @@ void intel_pmu_lbr_sched_task(struct perf_event_context *ctx, bool sched_in)
        struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
        struct x86_perf_task_context *task_ctx;
 
-       if (!x86_pmu.lbr_nr)
-               return;
-
        /*
         * If LBR callstack feature is enabled and the stack was saved when
         * the task was scheduled out, restore the stack. Otherwise flush
@@ -523,6 +522,9 @@ static int intel_pmu_setup_sw_lbr_filter(struct perf_event *event)
                        X86_BR_CALL_STACK;
        }
 
+       if (br_type & PERF_SAMPLE_BRANCH_IND_JUMP)
+               mask |= X86_BR_IND_JMP;
+
        /*
         * stash actual user request into reg, it may
         * be used by fixup code for some CPU
@@ -736,7 +738,7 @@ static int branch_type(unsigned long from, unsigned long to, int abort)
                        break;
                case 4:
                case 5:
-                       ret = X86_BR_JMP;
+                       ret = X86_BR_IND_JMP;
                        break;
                }
                break;
@@ -844,6 +846,7 @@ static const int nhm_lbr_sel_map[PERF_SAMPLE_BRANCH_MAX_SHIFT] = {
         */
        [PERF_SAMPLE_BRANCH_IND_CALL_SHIFT] = LBR_IND_CALL | LBR_IND_JMP,
        [PERF_SAMPLE_BRANCH_COND_SHIFT]     = LBR_JCC,
+       [PERF_SAMPLE_BRANCH_IND_JUMP_SHIFT] = LBR_IND_JMP,
 };
 
 static const int snb_lbr_sel_map[PERF_SAMPLE_BRANCH_MAX_SHIFT] = {
@@ -856,6 +859,7 @@ static const int snb_lbr_sel_map[PERF_SAMPLE_BRANCH_MAX_SHIFT] = {
                                                | LBR_FAR,
        [PERF_SAMPLE_BRANCH_IND_CALL_SHIFT]     = LBR_IND_CALL,
        [PERF_SAMPLE_BRANCH_COND_SHIFT]         = LBR_JCC,
+       [PERF_SAMPLE_BRANCH_IND_JUMP_SHIFT]     = LBR_IND_JMP,
 };
 
 static const int hsw_lbr_sel_map[PERF_SAMPLE_BRANCH_MAX_SHIFT] = {
@@ -870,6 +874,7 @@ static const int hsw_lbr_sel_map[PERF_SAMPLE_BRANCH_MAX_SHIFT] = {
        [PERF_SAMPLE_BRANCH_COND_SHIFT]         = LBR_JCC,
        [PERF_SAMPLE_BRANCH_CALL_STACK_SHIFT]   = LBR_REL_CALL | LBR_IND_CALL
                                                | LBR_RETURN | LBR_CALL_STACK,
+       [PERF_SAMPLE_BRANCH_IND_JUMP_SHIFT]     = LBR_IND_JMP,
 };
 
 /* core */
index ffe666c2c6b58657b5895948a2e7d69f95223521..159887c3a89d66a4aaad415ddc069b25904b5676 100644 (file)
@@ -151,7 +151,7 @@ static int __init pt_pmu_hw_init(void)
 
                de_attr->attr.attr.name = pt_caps[i].name;
 
-               sysfs_attr_init(&de_attrs->attr.attr);
+               sysfs_attr_init(&de_attr->attr.attr);
 
                de_attr->attr.attr.mode         = S_IRUGO;
                de_attr->attr.show              = pt_cap_show;
@@ -187,15 +187,6 @@ static bool pt_event_valid(struct perf_event *event)
  * These all are cpu affine and operate on a local PT
  */
 
-static bool pt_is_running(void)
-{
-       u64 ctl;
-
-       rdmsrl(MSR_IA32_RTIT_CTL, ctl);
-
-       return !!(ctl & RTIT_CTL_TRACEEN);
-}
-
 static void pt_config(struct perf_event *event)
 {
        u64 reg;
@@ -609,16 +600,19 @@ static unsigned int pt_topa_next_entry(struct pt_buffer *buf, unsigned int pg)
  * @handle:    Current output handle.
  *
  * Place INT and STOP marks to prevent overwriting old data that the consumer
- * hasn't yet collected.
+ * hasn't yet collected and waking up the consumer after a certain fraction of
+ * the buffer has filled up. Only needed and sensible for non-snapshot counters.
+ *
+ * This obviously relies on buf::head to figure out buffer markers, so it has
+ * to be called after pt_buffer_reset_offsets() and before the hardware tracing
+ * is enabled.
  */
 static int pt_buffer_reset_markers(struct pt_buffer *buf,
                                   struct perf_output_handle *handle)
 
 {
-       unsigned long idx, npages, end;
-
-       if (buf->snapshot)
-               return 0;
+       unsigned long head = local64_read(&buf->head);
+       unsigned long idx, npages, wakeup;
 
        /* can't stop in the middle of an output region */
        if (buf->output_off + handle->size + 1 <
@@ -634,17 +628,26 @@ static int pt_buffer_reset_markers(struct pt_buffer *buf,
        buf->topa_index[buf->stop_pos]->stop = 0;
        buf->topa_index[buf->intr_pos]->intr = 0;
 
-       if (pt_cap_get(PT_CAP_topa_multiple_entries)) {
-               npages = (handle->size + 1) >> PAGE_SHIFT;
-               end = (local64_read(&buf->head) >> PAGE_SHIFT) + npages;
-               /*if (end > handle->wakeup >> PAGE_SHIFT)
-                 end = handle->wakeup >> PAGE_SHIFT;*/
-               idx = end & (buf->nr_pages - 1);
-               buf->stop_pos = idx;
-               idx = (local64_read(&buf->head) >> PAGE_SHIFT) + npages - 1;
-               idx &= buf->nr_pages - 1;
-               buf->intr_pos = idx;
-       }
+       /* how many pages till the STOP marker */
+       npages = handle->size >> PAGE_SHIFT;
+
+       /* if it's on a page boundary, fill up one more page */
+       if (!offset_in_page(head + handle->size + 1))
+               npages++;
+
+       idx = (head >> PAGE_SHIFT) + npages;
+       idx &= buf->nr_pages - 1;
+       buf->stop_pos = idx;
+
+       wakeup = handle->wakeup >> PAGE_SHIFT;
+
+       /* in the worst case, wake up the consumer one page before hard stop */
+       idx = (head >> PAGE_SHIFT) + npages - 1;
+       if (idx > wakeup)
+               idx = wakeup;
+
+       idx &= buf->nr_pages - 1;
+       buf->intr_pos = idx;
 
        buf->topa_index[buf->stop_pos]->stop = 1;
        buf->topa_index[buf->intr_pos]->intr = 1;
@@ -664,7 +667,7 @@ static void pt_buffer_setup_topa_index(struct pt_buffer *buf)
        struct topa *cur = buf->first, *prev = buf->last;
        struct topa_entry *te_cur = TOPA_ENTRY(cur, 0),
                *te_prev = TOPA_ENTRY(prev, prev->last - 1);
-       int pg = 0, idx = 0, ntopa = 0;
+       int pg = 0, idx = 0;
 
        while (pg < buf->nr_pages) {
                int tidx;
@@ -679,9 +682,9 @@ static void pt_buffer_setup_topa_index(struct pt_buffer *buf)
                        /* advance to next topa table */
                        idx = 0;
                        cur = list_entry(cur->list.next, struct topa, list);
-                       ntopa++;
-               } else
+               } else {
                        idx++;
+               }
                te_cur = TOPA_ENTRY(cur, idx);
        }
 
@@ -693,7 +696,14 @@ static void pt_buffer_setup_topa_index(struct pt_buffer *buf)
  * @head:      Write pointer (aux_head) from AUX buffer.
  *
  * Find the ToPA table and entry corresponding to given @head and set buffer's
- * "current" pointers accordingly.
+ * "current" pointers accordingly. This is done after we have obtained the
+ * current aux_head position from a successful call to perf_aux_output_begin()
+ * to make sure the hardware is writing to the right place.
+ *
+ * This function modifies buf::{cur,cur_idx,output_off} that will be programmed
+ * into PT msrs when the tracing is enabled and buf::head and buf::data_size,
+ * which are used to determine INT and STOP markers' locations by a subsequent
+ * call to pt_buffer_reset_markers().
  */
 static void pt_buffer_reset_offsets(struct pt_buffer *buf, unsigned long head)
 {
@@ -891,6 +901,7 @@ void intel_pt_interrupt(void)
                }
 
                pt_buffer_reset_offsets(buf, pt->handle.head);
+               /* snapshot counters don't use PMI, so it's safe */
                ret = pt_buffer_reset_markers(buf, &pt->handle);
                if (ret) {
                        perf_aux_output_end(&pt->handle, 0, true);
@@ -913,7 +924,7 @@ static void pt_event_start(struct perf_event *event, int mode)
        struct pt *pt = this_cpu_ptr(&pt_ctx);
        struct pt_buffer *buf = perf_get_aux(&pt->handle);
 
-       if (pt_is_running() || !buf || pt_buffer_is_full(buf, pt)) {
+       if (!buf || pt_buffer_is_full(buf, pt)) {
                event->hw.state = PERF_HES_STOPPED;
                return;
        }
@@ -944,7 +955,6 @@ static void pt_event_stop(struct perf_event *event, int mode)
        event->hw.state = PERF_HES_STOPPED;
 
        if (mode & PERF_EF_UPDATE) {
-               struct pt *pt = this_cpu_ptr(&pt_ctx);
                struct pt_buffer *buf = perf_get_aux(&pt->handle);
 
                if (!buf)
index 358c54ad20d4084db807a05ae49def561aa4dd32..5cbd4e64feb582d927c6751914757a6784d68a60 100644 (file)
@@ -204,9 +204,8 @@ again:
 
 static void rapl_start_hrtimer(struct rapl_pmu *pmu)
 {
-       __hrtimer_start_range_ns(&pmu->hrtimer,
-                       pmu->timer_interval, 0,
-                       HRTIMER_MODE_REL_PINNED, 0);
+       hrtimer_start(&pmu->hrtimer, pmu->timer_interval,
+                    HRTIMER_MODE_REL_PINNED);
 }
 
 static void rapl_stop_hrtimer(struct rapl_pmu *pmu)
index c635b8b49e931e7926efc3dc96475a8c577958e0..21b5e38c921b7a78102a2adbabf06328b56dbf9b 100644 (file)
@@ -233,9 +233,8 @@ static enum hrtimer_restart uncore_pmu_hrtimer(struct hrtimer *hrtimer)
 
 void uncore_pmu_start_hrtimer(struct intel_uncore_box *box)
 {
-       __hrtimer_start_range_ns(&box->hrtimer,
-                       ns_to_ktime(box->hrtimer_duration), 0,
-                       HRTIMER_MODE_REL_PINNED, 0);
+       hrtimer_start(&box->hrtimer, ns_to_ktime(box->hrtimer_duration),
+                     HRTIMER_MODE_REL_PINNED);
 }
 
 void uncore_pmu_cancel_hrtimer(struct intel_uncore_box *box)
@@ -365,9 +364,8 @@ static int uncore_assign_events(struct intel_uncore_box *box, int assign[], int
        bitmap_zero(used_mask, UNCORE_PMC_IDX_MAX);
 
        for (i = 0, wmin = UNCORE_PMC_IDX_MAX, wmax = 0; i < n; i++) {
-               hwc = &box->event_list[i]->hw;
                c = uncore_get_event_constraint(box, box->event_list[i]);
-               hwc->constraint = c;
+               box->event_constraint[i] = c;
                wmin = min(wmin, c->weight);
                wmax = max(wmax, c->weight);
        }
@@ -375,7 +373,7 @@ static int uncore_assign_events(struct intel_uncore_box *box, int assign[], int
        /* fastpath, try to reuse previous register */
        for (i = 0; i < n; i++) {
                hwc = &box->event_list[i]->hw;
-               c = hwc->constraint;
+               c = box->event_constraint[i];
 
                /* never assigned */
                if (hwc->idx == -1)
@@ -395,8 +393,8 @@ static int uncore_assign_events(struct intel_uncore_box *box, int assign[], int
        }
        /* slow path */
        if (i != n)
-               ret = perf_assign_events(box->event_list, n,
-                                        wmin, wmax, assign);
+               ret = perf_assign_events(box->event_constraint, n,
+                                        wmin, wmax, n, assign);
 
        if (!assign || ret) {
                for (i = 0; i < n; i++)
@@ -840,6 +838,7 @@ static int uncore_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id
        box->phys_id = phys_id;
        box->pci_dev = pdev;
        box->pmu = pmu;
+       uncore_box_init(box);
        pci_set_drvdata(pdev, box);
 
        raw_spin_lock(&uncore_box_lock);
@@ -922,6 +921,9 @@ static int __init uncore_pci_init(void)
        case 69: /* Haswell Celeron */
                ret = hsw_uncore_pci_init();
                break;
+       case 61: /* Broadwell */
+               ret = bdw_uncore_pci_init();
+               break;
        default:
                return 0;
        }
@@ -1003,8 +1005,10 @@ static int uncore_cpu_starting(int cpu)
                        pmu = &type->pmus[j];
                        box = *per_cpu_ptr(pmu->box, cpu);
                        /* called by uncore_cpu_init? */
-                       if (box && box->phys_id >= 0)
+                       if (box && box->phys_id >= 0) {
+                               uncore_box_init(box);
                                continue;
+                       }
 
                        for_each_online_cpu(k) {
                                exist = *per_cpu_ptr(pmu->box, k);
@@ -1020,8 +1024,10 @@ static int uncore_cpu_starting(int cpu)
                                }
                        }
 
-                       if (box)
+                       if (box) {
                                box->phys_id = phys_id;
+                               uncore_box_init(box);
+                       }
                }
        }
        return 0;
index 6c8c1e7e69d85d3ad217eada0f0e55573c3daaf0..0f77f0a196e488b7e617ed2cabd0246cdab2611d 100644 (file)
@@ -97,6 +97,7 @@ struct intel_uncore_box {
        atomic_t refcnt;
        struct perf_event *events[UNCORE_PMC_IDX_MAX];
        struct perf_event *event_list[UNCORE_PMC_IDX_MAX];
+       struct event_constraint *event_constraint[UNCORE_PMC_IDX_MAX];
        unsigned long active_mask[BITS_TO_LONGS(UNCORE_PMC_IDX_MAX)];
        u64 tags[UNCORE_PMC_IDX_MAX];
        struct pci_dev *pci_dev;
@@ -257,14 +258,6 @@ static inline int uncore_num_counters(struct intel_uncore_box *box)
        return box->pmu->type->num_counters;
 }
 
-static inline void uncore_box_init(struct intel_uncore_box *box)
-{
-       if (!test_and_set_bit(UNCORE_BOX_FLAG_INITIATED, &box->flags)) {
-               if (box->pmu->type->ops->init_box)
-                       box->pmu->type->ops->init_box(box);
-       }
-}
-
 static inline void uncore_disable_box(struct intel_uncore_box *box)
 {
        if (box->pmu->type->ops->disable_box)
@@ -273,8 +266,6 @@ static inline void uncore_disable_box(struct intel_uncore_box *box)
 
 static inline void uncore_enable_box(struct intel_uncore_box *box)
 {
-       uncore_box_init(box);
-
        if (box->pmu->type->ops->enable_box)
                box->pmu->type->ops->enable_box(box);
 }
@@ -297,6 +288,14 @@ static inline u64 uncore_read_counter(struct intel_uncore_box *box,
        return box->pmu->type->ops->read_counter(box, event);
 }
 
+static inline void uncore_box_init(struct intel_uncore_box *box)
+{
+       if (!test_and_set_bit(UNCORE_BOX_FLAG_INITIATED, &box->flags)) {
+               if (box->pmu->type->ops->init_box)
+                       box->pmu->type->ops->init_box(box);
+       }
+}
+
 static inline bool uncore_box_is_fake(struct intel_uncore_box *box)
 {
        return (box->phys_id < 0);
@@ -326,6 +325,7 @@ extern struct event_constraint uncore_constraint_empty;
 int snb_uncore_pci_init(void);
 int ivb_uncore_pci_init(void);
 int hsw_uncore_pci_init(void);
+int bdw_uncore_pci_init(void);
 void snb_uncore_cpu_init(void);
 void nhm_uncore_cpu_init(void);
 
index 4562e9e22c60600a89f706c3b8c3cfb269636060..b005a78c701286e3c460b1f70fde54d8116fd91e 100644 (file)
@@ -7,6 +7,7 @@
 #define PCI_DEVICE_ID_INTEL_IVB_E3_IMC 0x0150
 #define PCI_DEVICE_ID_INTEL_HSW_IMC    0x0c00
 #define PCI_DEVICE_ID_INTEL_HSW_U_IMC  0x0a04
+#define PCI_DEVICE_ID_INTEL_BDW_IMC    0x1604
 
 /* SNB event control */
 #define SNB_UNC_CTL_EV_SEL_MASK                        0x000000ff
@@ -486,6 +487,14 @@ static const struct pci_device_id hsw_uncore_pci_ids[] = {
        { /* end: all zeroes */ },
 };
 
+static const struct pci_device_id bdw_uncore_pci_ids[] = {
+       { /* IMC */
+               PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BDW_IMC),
+               .driver_data = UNCORE_PCI_DEV_DATA(SNB_PCI_UNCORE_IMC, 0),
+       },
+       { /* end: all zeroes */ },
+};
+
 static struct pci_driver snb_uncore_pci_driver = {
        .name           = "snb_uncore",
        .id_table       = snb_uncore_pci_ids,
@@ -501,6 +510,11 @@ static struct pci_driver hsw_uncore_pci_driver = {
        .id_table       = hsw_uncore_pci_ids,
 };
 
+static struct pci_driver bdw_uncore_pci_driver = {
+       .name           = "bdw_uncore",
+       .id_table       = bdw_uncore_pci_ids,
+};
+
 struct imc_uncore_pci_dev {
        __u32 pci_id;
        struct pci_driver *driver;
@@ -514,6 +528,7 @@ static const struct imc_uncore_pci_dev desktop_imc_pci_ids[] = {
        IMC_DEV(IVB_E3_IMC, &ivb_uncore_pci_driver), /* Xeon E3-1200 v2/3rd Gen Core processor */
        IMC_DEV(HSW_IMC, &hsw_uncore_pci_driver),    /* 4th Gen Core Processor */
        IMC_DEV(HSW_U_IMC, &hsw_uncore_pci_driver),  /* 4th Gen Core ULT Mobile Processor */
+       IMC_DEV(BDW_IMC, &bdw_uncore_pci_driver),    /* 5th Gen Core U */
        {  /* end marker */ }
 };
 
@@ -561,6 +576,11 @@ int hsw_uncore_pci_init(void)
        return imc_uncore_pci_init();
 }
 
+int bdw_uncore_pci_init(void)
+{
+       return imc_uncore_pci_init();
+}
+
 /* end of Sandy Bridge uncore support */
 
 /* Nehalem uncore support */
index 12d9548457e7195a8a36b458e374cab9cabe5e07..6d6e85dd5849878e9caa379ef20eaab10b97559f 100644 (file)
                                ((1ULL << (n)) - 1)))
 
 /* Haswell-EP Ubox */
-#define HSWEP_U_MSR_PMON_CTR0                  0x705
-#define HSWEP_U_MSR_PMON_CTL0                  0x709
+#define HSWEP_U_MSR_PMON_CTR0                  0x709
+#define HSWEP_U_MSR_PMON_CTL0                  0x705
 #define HSWEP_U_MSR_PMON_FILTER                        0x707
 
 #define HSWEP_U_MSR_PMON_UCLK_FIXED_CTL                0x703
@@ -1914,7 +1914,7 @@ static struct intel_uncore_type hswep_uncore_cbox = {
        .name                   = "cbox",
        .num_counters           = 4,
        .num_boxes              = 18,
-       .perf_ctr_bits          = 44,
+       .perf_ctr_bits          = 48,
        .event_ctl              = HSWEP_C0_MSR_PMON_CTL0,
        .perf_ctr               = HSWEP_C0_MSR_PMON_CTR0,
        .event_mask             = SNBEP_CBO_MSR_PMON_RAW_EVENT_MASK,
index c76d3e37c6e1dc99a7083f05a2f79b7cd82968e1..e068d6683dba6bab6bd4c9f9804a621385e3baee 100644 (file)
@@ -22,6 +22,7 @@
 #include <linux/elfcore.h>
 #include <linux/module.h>
 #include <linux/slab.h>
+#include <linux/vmalloc.h>
 
 #include <asm/processor.h>
 #include <asm/hardirq.h>
index 6367a780cc8ca891b9513d2e4688717c8d5d3207..5ee771859b6f6e144090efe8f315e0c7707978b3 100644 (file)
@@ -4,7 +4,6 @@
 #include <linux/bootmem.h>
 #include <linux/export.h>
 #include <linux/io.h>
-#include <linux/irqdomain.h>
 #include <linux/interrupt.h>
 #include <linux/list.h>
 #include <linux/of.h>
@@ -17,6 +16,7 @@
 #include <linux/of_pci.h>
 #include <linux/initrd.h>
 
+#include <asm/irqdomain.h>
 #include <asm/hpet.h>
 #include <asm/apic.h>
 #include <asm/pci_x86.h>
@@ -196,38 +196,31 @@ static struct of_ioapic_type of_ioapic_type[] =
        },
 };
 
-static int ioapic_xlate(struct irq_domain *domain,
-                       struct device_node *controller,
-                       const u32 *intspec, u32 intsize,
-                       irq_hw_number_t *out_hwirq, u32 *out_type)
+static int dt_irqdomain_alloc(struct irq_domain *domain, unsigned int virq,
+                             unsigned int nr_irqs, void *arg)
 {
+       struct of_phandle_args *irq_data = (void *)arg;
        struct of_ioapic_type *it;
-       u32 line, idx, gsi;
+       struct irq_alloc_info tmp;
 
-       if (WARN_ON(intsize < 2))
+       if (WARN_ON(irq_data->args_count < 2))
                return -EINVAL;
-
-       line = intspec[0];
-
-       if (intspec[1] >= ARRAY_SIZE(of_ioapic_type))
+       if (irq_data->args[1] >= ARRAY_SIZE(of_ioapic_type))
                return -EINVAL;
 
-       it = &of_ioapic_type[intspec[1]];
+       it = &of_ioapic_type[irq_data->args[1]];
+       ioapic_set_alloc_attr(&tmp, NUMA_NO_NODE, it->trigger, it->polarity);
+       tmp.ioapic_id = mpc_ioapic_id(mp_irqdomain_ioapic_idx(domain));
+       tmp.ioapic_pin = irq_data->args[0];
 
-       idx = (u32)(long)domain->host_data;
-       gsi = mp_pin_to_gsi(idx, line);
-       if (mp_set_gsi_attr(gsi, it->trigger, it->polarity, cpu_to_node(0)))
-               return -EBUSY;
-
-       *out_hwirq = line;
-       *out_type = it->out_type;
-       return 0;
+       return mp_irqdomain_alloc(domain, virq, nr_irqs, &tmp);
 }
 
-const struct irq_domain_ops ioapic_irq_domain_ops = {
-       .map = mp_irqdomain_map,
-       .unmap = mp_irqdomain_unmap,
-       .xlate = ioapic_xlate,
+static const struct irq_domain_ops ioapic_irq_domain_ops = {
+       .alloc          = dt_irqdomain_alloc,
+       .free           = mp_irqdomain_free,
+       .activate       = mp_irqdomain_activate,
+       .deactivate     = mp_irqdomain_deactivate,
 };
 
 static void __init dtb_add_ioapic(struct device_node *dn)
index fe9f0b79a18b321bbc80711b7e71dde49b7436e5..5cb9a4d6f62387bd4925244ec11d529ba677ae0b 100644 (file)
@@ -627,8 +627,12 @@ static struct chipset early_qrk[] __initdata = {
        { PCI_VENDOR_ID_INTEL, PCI_ANY_ID, PCI_CLASS_DISPLAY_VGA, PCI_ANY_ID,
          QFLAG_APPLY_ONCE, intel_graphics_stolen },
        /*
-        * HPET on current version of Baytrail platform has accuracy
-        * problems, disable it for now:
+        * HPET on the current version of the Baytrail platform has accuracy
+        * problems: it will halt in deep idle state - so we disable it.
+        *
+        * More details can be found in section 18.10.1.3 of the datasheet:
+        *
+        *    http://www.intel.com/content/dam/www/public/us/en/documents/datasheets/atom-z8000-datasheet-vol-1.pdf
         */
        { PCI_VENDOR_ID_INTEL, 0x0f00,
                PCI_CLASS_BRIDGE_HOST, PCI_ANY_ID, 0, force_disable_hpet},
diff --git a/arch/x86/kernel/entry_32.S b/arch/x86/kernel/entry_32.S
deleted file mode 100644 (file)
index 1c30976..0000000
+++ /dev/null
@@ -1,1401 +0,0 @@
-/*
- *
- *  Copyright (C) 1991, 1992  Linus Torvalds
- */
-
-/*
- * entry.S contains the system-call and fault low-level handling routines.
- * This also contains the timer-interrupt handler, as well as all interrupts
- * and faults that can result in a task-switch.
- *
- * NOTE: This code handles signal-recognition, which happens every time
- * after a timer-interrupt and after each system call.
- *
- * I changed all the .align's to 4 (16 byte alignment), as that's faster
- * on a 486.
- *
- * Stack layout in 'syscall_exit':
- *     ptrace needs to have all regs on the stack.
- *     if the order here is changed, it needs to be
- *     updated in fork.c:copy_process, signal.c:do_signal,
- *     ptrace.c and ptrace.h
- *
- *      0(%esp) - %ebx
- *      4(%esp) - %ecx
- *      8(%esp) - %edx
- *       C(%esp) - %esi
- *     10(%esp) - %edi
- *     14(%esp) - %ebp
- *     18(%esp) - %eax
- *     1C(%esp) - %ds
- *     20(%esp) - %es
- *     24(%esp) - %fs
- *     28(%esp) - %gs          saved iff !CONFIG_X86_32_LAZY_GS
- *     2C(%esp) - orig_eax
- *     30(%esp) - %eip
- *     34(%esp) - %cs
- *     38(%esp) - %eflags
- *     3C(%esp) - %oldesp
- *     40(%esp) - %oldss
- *
- * "current" is in register %ebx during any slow entries.
- */
-
-#include <linux/linkage.h>
-#include <linux/err.h>
-#include <asm/thread_info.h>
-#include <asm/irqflags.h>
-#include <asm/errno.h>
-#include <asm/segment.h>
-#include <asm/smp.h>
-#include <asm/page_types.h>
-#include <asm/percpu.h>
-#include <asm/dwarf2.h>
-#include <asm/processor-flags.h>
-#include <asm/ftrace.h>
-#include <asm/irq_vectors.h>
-#include <asm/cpufeature.h>
-#include <asm/alternative-asm.h>
-#include <asm/asm.h>
-#include <asm/smap.h>
-
-/* Avoid __ASSEMBLER__'ifying <linux/audit.h> just for this.  */
-#include <linux/elf-em.h>
-#define AUDIT_ARCH_I386                (EM_386|__AUDIT_ARCH_LE)
-#define __AUDIT_ARCH_LE           0x40000000
-
-#ifndef CONFIG_AUDITSYSCALL
-#define sysenter_audit syscall_trace_entry
-#define sysexit_audit  syscall_exit_work
-#endif
-
-       .section .entry.text, "ax"
-
-/*
- * We use macros for low-level operations which need to be overridden
- * for paravirtualization.  The following will never clobber any registers:
- *   INTERRUPT_RETURN (aka. "iret")
- *   GET_CR0_INTO_EAX (aka. "movl %cr0, %eax")
- *   ENABLE_INTERRUPTS_SYSEXIT (aka "sti; sysexit").
- *
- * For DISABLE_INTERRUPTS/ENABLE_INTERRUPTS (aka "cli"/"sti"), you must
- * specify what registers can be overwritten (CLBR_NONE, CLBR_EAX/EDX/ECX/ANY).
- * Allowing a register to be clobbered can shrink the paravirt replacement
- * enough to patch inline, increasing performance.
- */
-
-#ifdef CONFIG_PREEMPT
-#define preempt_stop(clobbers) DISABLE_INTERRUPTS(clobbers); TRACE_IRQS_OFF
-#else
-#define preempt_stop(clobbers)
-#define resume_kernel          restore_all
-#endif
-
-.macro TRACE_IRQS_IRET
-#ifdef CONFIG_TRACE_IRQFLAGS
-       testl $X86_EFLAGS_IF,PT_EFLAGS(%esp)     # interrupts off?
-       jz 1f
-       TRACE_IRQS_ON
-1:
-#endif
-.endm
-
-/*
- * User gs save/restore
- *
- * %gs is used for userland TLS and kernel only uses it for stack
- * canary which is required to be at %gs:20 by gcc.  Read the comment
- * at the top of stackprotector.h for more info.
- *
- * Local labels 98 and 99 are used.
- */
-#ifdef CONFIG_X86_32_LAZY_GS
-
- /* unfortunately push/pop can't be no-op */
-.macro PUSH_GS
-       pushl_cfi $0
-.endm
-.macro POP_GS pop=0
-       addl $(4 + \pop), %esp
-       CFI_ADJUST_CFA_OFFSET -(4 + \pop)
-.endm
-.macro POP_GS_EX
-.endm
-
- /* all the rest are no-op */
-.macro PTGS_TO_GS
-.endm
-.macro PTGS_TO_GS_EX
-.endm
-.macro GS_TO_REG reg
-.endm
-.macro REG_TO_PTGS reg
-.endm
-.macro SET_KERNEL_GS reg
-.endm
-
-#else  /* CONFIG_X86_32_LAZY_GS */
-
-.macro PUSH_GS
-       pushl_cfi %gs
-       /*CFI_REL_OFFSET gs, 0*/
-.endm
-
-.macro POP_GS pop=0
-98:    popl_cfi %gs
-       /*CFI_RESTORE gs*/
-  .if \pop <> 0
-       add $\pop, %esp
-       CFI_ADJUST_CFA_OFFSET -\pop
-  .endif
-.endm
-.macro POP_GS_EX
-.pushsection .fixup, "ax"
-99:    movl $0, (%esp)
-       jmp 98b
-.popsection
-       _ASM_EXTABLE(98b,99b)
-.endm
-
-.macro PTGS_TO_GS
-98:    mov PT_GS(%esp), %gs
-.endm
-.macro PTGS_TO_GS_EX
-.pushsection .fixup, "ax"
-99:    movl $0, PT_GS(%esp)
-       jmp 98b
-.popsection
-       _ASM_EXTABLE(98b,99b)
-.endm
-
-.macro GS_TO_REG reg
-       movl %gs, \reg
-       /*CFI_REGISTER gs, \reg*/
-.endm
-.macro REG_TO_PTGS reg
-       movl \reg, PT_GS(%esp)
-       /*CFI_REL_OFFSET gs, PT_GS*/
-.endm
-.macro SET_KERNEL_GS reg
-       movl $(__KERNEL_STACK_CANARY), \reg
-       movl \reg, %gs
-.endm
-
-#endif /* CONFIG_X86_32_LAZY_GS */
-
-.macro SAVE_ALL
-       cld
-       PUSH_GS
-       pushl_cfi %fs
-       /*CFI_REL_OFFSET fs, 0;*/
-       pushl_cfi %es
-       /*CFI_REL_OFFSET es, 0;*/
-       pushl_cfi %ds
-       /*CFI_REL_OFFSET ds, 0;*/
-       pushl_cfi %eax
-       CFI_REL_OFFSET eax, 0
-       pushl_cfi %ebp
-       CFI_REL_OFFSET ebp, 0
-       pushl_cfi %edi
-       CFI_REL_OFFSET edi, 0
-       pushl_cfi %esi
-       CFI_REL_OFFSET esi, 0
-       pushl_cfi %edx
-       CFI_REL_OFFSET edx, 0
-       pushl_cfi %ecx
-       CFI_REL_OFFSET ecx, 0
-       pushl_cfi %ebx
-       CFI_REL_OFFSET ebx, 0
-       movl $(__USER_DS), %edx
-       movl %edx, %ds
-       movl %edx, %es
-       movl $(__KERNEL_PERCPU), %edx
-       movl %edx, %fs
-       SET_KERNEL_GS %edx
-.endm
-
-.macro RESTORE_INT_REGS
-       popl_cfi %ebx
-       CFI_RESTORE ebx
-       popl_cfi %ecx
-       CFI_RESTORE ecx
-       popl_cfi %edx
-       CFI_RESTORE edx
-       popl_cfi %esi
-       CFI_RESTORE esi
-       popl_cfi %edi
-       CFI_RESTORE edi
-       popl_cfi %ebp
-       CFI_RESTORE ebp
-       popl_cfi %eax
-       CFI_RESTORE eax
-.endm
-
-.macro RESTORE_REGS pop=0
-       RESTORE_INT_REGS
-1:     popl_cfi %ds
-       /*CFI_RESTORE ds;*/
-2:     popl_cfi %es
-       /*CFI_RESTORE es;*/
-3:     popl_cfi %fs
-       /*CFI_RESTORE fs;*/
-       POP_GS \pop
-.pushsection .fixup, "ax"
-4:     movl $0, (%esp)
-       jmp 1b
-5:     movl $0, (%esp)
-       jmp 2b
-6:     movl $0, (%esp)
-       jmp 3b
-.popsection
-       _ASM_EXTABLE(1b,4b)
-       _ASM_EXTABLE(2b,5b)
-       _ASM_EXTABLE(3b,6b)
-       POP_GS_EX
-.endm
-
-.macro RING0_INT_FRAME
-       CFI_STARTPROC simple
-       CFI_SIGNAL_FRAME
-       CFI_DEF_CFA esp, 3*4
-       /*CFI_OFFSET cs, -2*4;*/
-       CFI_OFFSET eip, -3*4
-.endm
-
-.macro RING0_EC_FRAME
-       CFI_STARTPROC simple
-       CFI_SIGNAL_FRAME
-       CFI_DEF_CFA esp, 4*4
-       /*CFI_OFFSET cs, -2*4;*/
-       CFI_OFFSET eip, -3*4
-.endm
-
-.macro RING0_PTREGS_FRAME
-       CFI_STARTPROC simple
-       CFI_SIGNAL_FRAME
-       CFI_DEF_CFA esp, PT_OLDESP-PT_EBX
-       /*CFI_OFFSET cs, PT_CS-PT_OLDESP;*/
-       CFI_OFFSET eip, PT_EIP-PT_OLDESP
-       /*CFI_OFFSET es, PT_ES-PT_OLDESP;*/
-       /*CFI_OFFSET ds, PT_DS-PT_OLDESP;*/
-       CFI_OFFSET eax, PT_EAX-PT_OLDESP
-       CFI_OFFSET ebp, PT_EBP-PT_OLDESP
-       CFI_OFFSET edi, PT_EDI-PT_OLDESP
-       CFI_OFFSET esi, PT_ESI-PT_OLDESP
-       CFI_OFFSET edx, PT_EDX-PT_OLDESP
-       CFI_OFFSET ecx, PT_ECX-PT_OLDESP
-       CFI_OFFSET ebx, PT_EBX-PT_OLDESP
-.endm
-
-ENTRY(ret_from_fork)
-       CFI_STARTPROC
-       pushl_cfi %eax
-       call schedule_tail
-       GET_THREAD_INFO(%ebp)
-       popl_cfi %eax
-       pushl_cfi $0x0202               # Reset kernel eflags
-       popfl_cfi
-       jmp syscall_exit
-       CFI_ENDPROC
-END(ret_from_fork)
-
-ENTRY(ret_from_kernel_thread)
-       CFI_STARTPROC
-       pushl_cfi %eax
-       call schedule_tail
-       GET_THREAD_INFO(%ebp)
-       popl_cfi %eax
-       pushl_cfi $0x0202               # Reset kernel eflags
-       popfl_cfi
-       movl PT_EBP(%esp),%eax
-       call *PT_EBX(%esp)
-       movl $0,PT_EAX(%esp)
-       jmp syscall_exit
-       CFI_ENDPROC
-ENDPROC(ret_from_kernel_thread)
-
-/*
- * Return to user mode is not as complex as all this looks,
- * but we want the default path for a system call return to
- * go as quickly as possible which is why some of this is
- * less clear than it otherwise should be.
- */
-
-       # userspace resumption stub bypassing syscall exit tracing
-       ALIGN
-       RING0_PTREGS_FRAME
-ret_from_exception:
-       preempt_stop(CLBR_ANY)
-ret_from_intr:
-       GET_THREAD_INFO(%ebp)
-#ifdef CONFIG_VM86
-       movl PT_EFLAGS(%esp), %eax      # mix EFLAGS and CS
-       movb PT_CS(%esp), %al
-       andl $(X86_EFLAGS_VM | SEGMENT_RPL_MASK), %eax
-#else
-       /*
-        * We can be coming here from child spawned by kernel_thread().
-        */
-       movl PT_CS(%esp), %eax
-       andl $SEGMENT_RPL_MASK, %eax
-#endif
-       cmpl $USER_RPL, %eax
-       jb resume_kernel                # not returning to v8086 or userspace
-
-ENTRY(resume_userspace)
-       LOCKDEP_SYS_EXIT
-       DISABLE_INTERRUPTS(CLBR_ANY)    # make sure we don't miss an interrupt
-                                       # setting need_resched or sigpending
-                                       # between sampling and the iret
-       TRACE_IRQS_OFF
-       movl TI_flags(%ebp), %ecx
-       andl $_TIF_WORK_MASK, %ecx      # is there any work to be done on
-                                       # int/exception return?
-       jne work_pending
-       jmp restore_all
-END(ret_from_exception)
-
-#ifdef CONFIG_PREEMPT
-ENTRY(resume_kernel)
-       DISABLE_INTERRUPTS(CLBR_ANY)
-need_resched:
-       cmpl $0,PER_CPU_VAR(__preempt_count)
-       jnz restore_all
-       testl $X86_EFLAGS_IF,PT_EFLAGS(%esp)    # interrupts off (exception path) ?
-       jz restore_all
-       call preempt_schedule_irq
-       jmp need_resched
-END(resume_kernel)
-#endif
-       CFI_ENDPROC
-
-/* SYSENTER_RETURN points to after the "sysenter" instruction in
-   the vsyscall page.  See vsyscall-sysentry.S, which defines the symbol.  */
-
-       # sysenter call handler stub
-ENTRY(ia32_sysenter_target)
-       CFI_STARTPROC simple
-       CFI_SIGNAL_FRAME
-       CFI_DEF_CFA esp, 0
-       CFI_REGISTER esp, ebp
-       movl TSS_sysenter_sp0(%esp),%esp
-sysenter_past_esp:
-       /*
-        * Interrupts are disabled here, but we can't trace it until
-        * enough kernel state to call TRACE_IRQS_OFF can be called - but
-        * we immediately enable interrupts at that point anyway.
-        */
-       pushl_cfi $__USER_DS
-       /*CFI_REL_OFFSET ss, 0*/
-       pushl_cfi %ebp
-       CFI_REL_OFFSET esp, 0
-       pushfl_cfi
-       orl $X86_EFLAGS_IF, (%esp)
-       pushl_cfi $__USER_CS
-       /*CFI_REL_OFFSET cs, 0*/
-       /*
-        * Push current_thread_info()->sysenter_return to the stack.
-        * A tiny bit of offset fixup is necessary: TI_sysenter_return
-        * is relative to thread_info, which is at the bottom of the
-        * kernel stack page.  4*4 means the 4 words pushed above;
-        * TOP_OF_KERNEL_STACK_PADDING takes us to the top of the stack;
-        * and THREAD_SIZE takes us to the bottom.
-        */
-       pushl_cfi ((TI_sysenter_return) - THREAD_SIZE + TOP_OF_KERNEL_STACK_PADDING + 4*4)(%esp)
-       CFI_REL_OFFSET eip, 0
-
-       pushl_cfi %eax
-       SAVE_ALL
-       ENABLE_INTERRUPTS(CLBR_NONE)
-
-/*
- * Load the potential sixth argument from user stack.
- * Careful about security.
- */
-       cmpl $__PAGE_OFFSET-3,%ebp
-       jae syscall_fault
-       ASM_STAC
-1:     movl (%ebp),%ebp
-       ASM_CLAC
-       movl %ebp,PT_EBP(%esp)
-       _ASM_EXTABLE(1b,syscall_fault)
-
-       GET_THREAD_INFO(%ebp)
-
-       testl $_TIF_WORK_SYSCALL_ENTRY,TI_flags(%ebp)
-       jnz sysenter_audit
-sysenter_do_call:
-       cmpl $(NR_syscalls), %eax
-       jae sysenter_badsys
-       call *sys_call_table(,%eax,4)
-sysenter_after_call:
-       movl %eax,PT_EAX(%esp)
-       LOCKDEP_SYS_EXIT
-       DISABLE_INTERRUPTS(CLBR_ANY)
-       TRACE_IRQS_OFF
-       movl TI_flags(%ebp), %ecx
-       testl $_TIF_ALLWORK_MASK, %ecx
-       jnz sysexit_audit
-sysenter_exit:
-/* if something modifies registers it must also disable sysexit */
-       movl PT_EIP(%esp), %edx
-       movl PT_OLDESP(%esp), %ecx
-       xorl %ebp,%ebp
-       TRACE_IRQS_ON
-1:     mov  PT_FS(%esp), %fs
-       PTGS_TO_GS
-       ENABLE_INTERRUPTS_SYSEXIT
-
-#ifdef CONFIG_AUDITSYSCALL
-sysenter_audit:
-       testl $(_TIF_WORK_SYSCALL_ENTRY & ~_TIF_SYSCALL_AUDIT),TI_flags(%ebp)
-       jnz syscall_trace_entry
-       /* movl PT_EAX(%esp), %eax      already set, syscall number: 1st arg to audit */
-       movl PT_EBX(%esp), %edx         /* ebx/a0: 2nd arg to audit */
-       /* movl PT_ECX(%esp), %ecx      already set, a1: 3nd arg to audit */
-       pushl_cfi PT_ESI(%esp)          /* a3: 5th arg */
-       pushl_cfi PT_EDX+4(%esp)        /* a2: 4th arg */
-       call __audit_syscall_entry
-       popl_cfi %ecx /* get that remapped edx off the stack */
-       popl_cfi %ecx /* get that remapped esi off the stack */
-       movl PT_EAX(%esp),%eax          /* reload syscall number */
-       jmp sysenter_do_call
-
-sysexit_audit:
-       testl $(_TIF_ALLWORK_MASK & ~_TIF_SYSCALL_AUDIT), %ecx
-       jnz syscall_exit_work
-       TRACE_IRQS_ON
-       ENABLE_INTERRUPTS(CLBR_ANY)
-       movl %eax,%edx          /* second arg, syscall return value */
-       cmpl $-MAX_ERRNO,%eax   /* is it an error ? */
-       setbe %al               /* 1 if so, 0 if not */
-       movzbl %al,%eax         /* zero-extend that */
-       call __audit_syscall_exit
-       DISABLE_INTERRUPTS(CLBR_ANY)
-       TRACE_IRQS_OFF
-       movl TI_flags(%ebp), %ecx
-       testl $(_TIF_ALLWORK_MASK & ~_TIF_SYSCALL_AUDIT), %ecx
-       jnz syscall_exit_work
-       movl PT_EAX(%esp),%eax  /* reload syscall return value */
-       jmp sysenter_exit
-#endif
-
-       CFI_ENDPROC
-.pushsection .fixup,"ax"
-2:     movl $0,PT_FS(%esp)
-       jmp 1b
-.popsection
-       _ASM_EXTABLE(1b,2b)
-       PTGS_TO_GS_EX
-ENDPROC(ia32_sysenter_target)
-
-       # system call handler stub
-ENTRY(system_call)
-       RING0_INT_FRAME                 # can't unwind into user space anyway
-       ASM_CLAC
-       pushl_cfi %eax                  # save orig_eax
-       SAVE_ALL
-       GET_THREAD_INFO(%ebp)
-                                       # system call tracing in operation / emulation
-       testl $_TIF_WORK_SYSCALL_ENTRY,TI_flags(%ebp)
-       jnz syscall_trace_entry
-       cmpl $(NR_syscalls), %eax
-       jae syscall_badsys
-syscall_call:
-       call *sys_call_table(,%eax,4)
-syscall_after_call:
-       movl %eax,PT_EAX(%esp)          # store the return value
-syscall_exit:
-       LOCKDEP_SYS_EXIT
-       DISABLE_INTERRUPTS(CLBR_ANY)    # make sure we don't miss an interrupt
-                                       # setting need_resched or sigpending
-                                       # between sampling and the iret
-       TRACE_IRQS_OFF
-       movl TI_flags(%ebp), %ecx
-       testl $_TIF_ALLWORK_MASK, %ecx  # current->work
-       jnz syscall_exit_work
-
-restore_all:
-       TRACE_IRQS_IRET
-restore_all_notrace:
-#ifdef CONFIG_X86_ESPFIX32
-       movl PT_EFLAGS(%esp), %eax      # mix EFLAGS, SS and CS
-       # Warning: PT_OLDSS(%esp) contains the wrong/random values if we
-       # are returning to the kernel.
-       # See comments in process.c:copy_thread() for details.
-       movb PT_OLDSS(%esp), %ah
-       movb PT_CS(%esp), %al
-       andl $(X86_EFLAGS_VM | (SEGMENT_TI_MASK << 8) | SEGMENT_RPL_MASK), %eax
-       cmpl $((SEGMENT_LDT << 8) | USER_RPL), %eax
-       CFI_REMEMBER_STATE
-       je ldt_ss                       # returning to user-space with LDT SS
-#endif
-restore_nocheck:
-       RESTORE_REGS 4                  # skip orig_eax/error_code
-irq_return:
-       INTERRUPT_RETURN
-.section .fixup,"ax"
-ENTRY(iret_exc)
-       pushl $0                        # no error code
-       pushl $do_iret_error
-       jmp error_code
-.previous
-       _ASM_EXTABLE(irq_return,iret_exc)
-
-#ifdef CONFIG_X86_ESPFIX32
-       CFI_RESTORE_STATE
-ldt_ss:
-#ifdef CONFIG_PARAVIRT
-       /*
-        * The kernel can't run on a non-flat stack if paravirt mode
-        * is active.  Rather than try to fixup the high bits of
-        * ESP, bypass this code entirely.  This may break DOSemu
-        * and/or Wine support in a paravirt VM, although the option
-        * is still available to implement the setting of the high
-        * 16-bits in the INTERRUPT_RETURN paravirt-op.
-        */
-       cmpl $0, pv_info+PARAVIRT_enabled
-       jne restore_nocheck
-#endif
-
-/*
- * Setup and switch to ESPFIX stack
- *
- * We're returning to userspace with a 16 bit stack. The CPU will not
- * restore the high word of ESP for us on executing iret... This is an
- * "official" bug of all the x86-compatible CPUs, which we can work
- * around to make dosemu and wine happy. We do this by preloading the
- * high word of ESP with the high word of the userspace ESP while
- * compensating for the offset by changing to the ESPFIX segment with
- * a base address that matches for the difference.
- */
-#define GDT_ESPFIX_SS PER_CPU_VAR(gdt_page) + (GDT_ENTRY_ESPFIX_SS * 8)
-       mov %esp, %edx                  /* load kernel esp */
-       mov PT_OLDESP(%esp), %eax       /* load userspace esp */
-       mov %dx, %ax                    /* eax: new kernel esp */
-       sub %eax, %edx                  /* offset (low word is 0) */
-       shr $16, %edx
-       mov %dl, GDT_ESPFIX_SS + 4 /* bits 16..23 */
-       mov %dh, GDT_ESPFIX_SS + 7 /* bits 24..31 */
-       pushl_cfi $__ESPFIX_SS
-       pushl_cfi %eax                  /* new kernel esp */
-       /* Disable interrupts, but do not irqtrace this section: we
-        * will soon execute iret and the tracer was already set to
-        * the irqstate after the iret */
-       DISABLE_INTERRUPTS(CLBR_EAX)
-       lss (%esp), %esp                /* switch to espfix segment */
-       CFI_ADJUST_CFA_OFFSET -8
-       jmp restore_nocheck
-#endif
-       CFI_ENDPROC
-ENDPROC(system_call)
-
-       # perform work that needs to be done immediately before resumption
-       ALIGN
-       RING0_PTREGS_FRAME              # can't unwind into user space anyway
-work_pending:
-       testb $_TIF_NEED_RESCHED, %cl
-       jz work_notifysig
-work_resched:
-       call schedule
-       LOCKDEP_SYS_EXIT
-       DISABLE_INTERRUPTS(CLBR_ANY)    # make sure we don't miss an interrupt
-                                       # setting need_resched or sigpending
-                                       # between sampling and the iret
-       TRACE_IRQS_OFF
-       movl TI_flags(%ebp), %ecx
-       andl $_TIF_WORK_MASK, %ecx      # is there any work to be done other
-                                       # than syscall tracing?
-       jz restore_all
-       testb $_TIF_NEED_RESCHED, %cl
-       jnz work_resched
-
-work_notifysig:                                # deal with pending signals and
-                                       # notify-resume requests
-#ifdef CONFIG_VM86
-       testl $X86_EFLAGS_VM, PT_EFLAGS(%esp)
-       movl %esp, %eax
-       jnz work_notifysig_v86          # returning to kernel-space or
-                                       # vm86-space
-1:
-#else
-       movl %esp, %eax
-#endif
-       TRACE_IRQS_ON
-       ENABLE_INTERRUPTS(CLBR_NONE)
-       movb PT_CS(%esp), %bl
-       andb $SEGMENT_RPL_MASK, %bl
-       cmpb $USER_RPL, %bl
-       jb resume_kernel
-       xorl %edx, %edx
-       call do_notify_resume
-       jmp resume_userspace
-
-#ifdef CONFIG_VM86
-       ALIGN
-work_notifysig_v86:
-       pushl_cfi %ecx                  # save ti_flags for do_notify_resume
-       call save_v86_state             # %eax contains pt_regs pointer
-       popl_cfi %ecx
-       movl %eax, %esp
-       jmp 1b
-#endif
-END(work_pending)
-
-       # perform syscall exit tracing
-       ALIGN
-syscall_trace_entry:
-       movl $-ENOSYS,PT_EAX(%esp)
-       movl %esp, %eax
-       call syscall_trace_enter
-       /* What it returned is what we'll actually use.  */
-       cmpl $(NR_syscalls), %eax
-       jnae syscall_call
-       jmp syscall_exit
-END(syscall_trace_entry)
-
-       # perform syscall exit tracing
-       ALIGN
-syscall_exit_work:
-       testl $_TIF_WORK_SYSCALL_EXIT, %ecx
-       jz work_pending
-       TRACE_IRQS_ON
-       ENABLE_INTERRUPTS(CLBR_ANY)     # could let syscall_trace_leave() call
-                                       # schedule() instead
-       movl %esp, %eax
-       call syscall_trace_leave
-       jmp resume_userspace
-END(syscall_exit_work)
-       CFI_ENDPROC
-
-       RING0_INT_FRAME                 # can't unwind into user space anyway
-syscall_fault:
-       ASM_CLAC
-       GET_THREAD_INFO(%ebp)
-       movl $-EFAULT,PT_EAX(%esp)
-       jmp resume_userspace
-END(syscall_fault)
-
-syscall_badsys:
-       movl $-ENOSYS,%eax
-       jmp syscall_after_call
-END(syscall_badsys)
-
-sysenter_badsys:
-       movl $-ENOSYS,%eax
-       jmp sysenter_after_call
-END(sysenter_badsys)
-       CFI_ENDPROC
-
-.macro FIXUP_ESPFIX_STACK
-/*
- * Switch back for ESPFIX stack to the normal zerobased stack
- *
- * We can't call C functions using the ESPFIX stack. This code reads
- * the high word of the segment base from the GDT and swiches to the
- * normal stack and adjusts ESP with the matching offset.
- */
-#ifdef CONFIG_X86_ESPFIX32
-       /* fixup the stack */
-       mov GDT_ESPFIX_SS + 4, %al /* bits 16..23 */
-       mov GDT_ESPFIX_SS + 7, %ah /* bits 24..31 */
-       shl $16, %eax
-       addl %esp, %eax                 /* the adjusted stack pointer */
-       pushl_cfi $__KERNEL_DS
-       pushl_cfi %eax
-       lss (%esp), %esp                /* switch to the normal stack segment */
-       CFI_ADJUST_CFA_OFFSET -8
-#endif
-.endm
-.macro UNWIND_ESPFIX_STACK
-#ifdef CONFIG_X86_ESPFIX32
-       movl %ss, %eax
-       /* see if on espfix stack */
-       cmpw $__ESPFIX_SS, %ax
-       jne 27f
-       movl $__KERNEL_DS, %eax
-       movl %eax, %ds
-       movl %eax, %es
-       /* switch to normal stack */
-       FIXUP_ESPFIX_STACK
-27:
-#endif
-.endm
-
-/*
- * Build the entry stubs with some assembler magic.
- * We pack 1 stub into every 8-byte block.
- */
-       .align 8
-ENTRY(irq_entries_start)
-       RING0_INT_FRAME
-    vector=FIRST_EXTERNAL_VECTOR
-    .rept (FIRST_SYSTEM_VECTOR - FIRST_EXTERNAL_VECTOR)
-       pushl_cfi $(~vector+0x80)       /* Note: always in signed byte range */
-    vector=vector+1
-       jmp     common_interrupt
-       CFI_ADJUST_CFA_OFFSET -4
-       .align  8
-    .endr
-END(irq_entries_start)
-
-/*
- * the CPU automatically disables interrupts when executing an IRQ vector,
- * so IRQ-flags tracing has to follow that:
- */
-       .p2align CONFIG_X86_L1_CACHE_SHIFT
-common_interrupt:
-       ASM_CLAC
-       addl $-0x80,(%esp)      /* Adjust vector into the [-256,-1] range */
-       SAVE_ALL
-       TRACE_IRQS_OFF
-       movl %esp,%eax
-       call do_IRQ
-       jmp ret_from_intr
-ENDPROC(common_interrupt)
-       CFI_ENDPROC
-
-#define BUILD_INTERRUPT3(name, nr, fn) \
-ENTRY(name)                            \
-       RING0_INT_FRAME;                \
-       ASM_CLAC;                       \
-       pushl_cfi $~(nr);               \
-       SAVE_ALL;                       \
-       TRACE_IRQS_OFF                  \
-       movl %esp,%eax;                 \
-       call fn;                        \
-       jmp ret_from_intr;              \
-       CFI_ENDPROC;                    \
-ENDPROC(name)
-
-
-#ifdef CONFIG_TRACING
-#define TRACE_BUILD_INTERRUPT(name, nr)                \
-       BUILD_INTERRUPT3(trace_##name, nr, smp_trace_##name)
-#else
-#define TRACE_BUILD_INTERRUPT(name, nr)
-#endif
-
-#define BUILD_INTERRUPT(name, nr) \
-       BUILD_INTERRUPT3(name, nr, smp_##name); \
-       TRACE_BUILD_INTERRUPT(name, nr)
-
-/* The include is where all of the SMP etc. interrupts come from */
-#include <asm/entry_arch.h>
-
-ENTRY(coprocessor_error)
-       RING0_INT_FRAME
-       ASM_CLAC
-       pushl_cfi $0
-       pushl_cfi $do_coprocessor_error
-       jmp error_code
-       CFI_ENDPROC
-END(coprocessor_error)
-
-ENTRY(simd_coprocessor_error)
-       RING0_INT_FRAME
-       ASM_CLAC
-       pushl_cfi $0
-#ifdef CONFIG_X86_INVD_BUG
-       /* AMD 486 bug: invd from userspace calls exception 19 instead of #GP */
-       ALTERNATIVE "pushl_cfi $do_general_protection", \
-                   "pushl $do_simd_coprocessor_error", \
-                   X86_FEATURE_XMM
-#else
-       pushl_cfi $do_simd_coprocessor_error
-#endif
-       jmp error_code
-       CFI_ENDPROC
-END(simd_coprocessor_error)
-
-ENTRY(device_not_available)
-       RING0_INT_FRAME
-       ASM_CLAC
-       pushl_cfi $-1                   # mark this as an int
-       pushl_cfi $do_device_not_available
-       jmp error_code
-       CFI_ENDPROC
-END(device_not_available)
-
-#ifdef CONFIG_PARAVIRT
-ENTRY(native_iret)
-       iret
-       _ASM_EXTABLE(native_iret, iret_exc)
-END(native_iret)
-
-ENTRY(native_irq_enable_sysexit)
-       sti
-       sysexit
-END(native_irq_enable_sysexit)
-#endif
-
-ENTRY(overflow)
-       RING0_INT_FRAME
-       ASM_CLAC
-       pushl_cfi $0
-       pushl_cfi $do_overflow
-       jmp error_code
-       CFI_ENDPROC
-END(overflow)
-
-ENTRY(bounds)
-       RING0_INT_FRAME
-       ASM_CLAC
-       pushl_cfi $0
-       pushl_cfi $do_bounds
-       jmp error_code
-       CFI_ENDPROC
-END(bounds)
-
-ENTRY(invalid_op)
-       RING0_INT_FRAME
-       ASM_CLAC
-       pushl_cfi $0
-       pushl_cfi $do_invalid_op
-       jmp error_code
-       CFI_ENDPROC
-END(invalid_op)
-
-ENTRY(coprocessor_segment_overrun)
-       RING0_INT_FRAME
-       ASM_CLAC
-       pushl_cfi $0
-       pushl_cfi $do_coprocessor_segment_overrun
-       jmp error_code
-       CFI_ENDPROC
-END(coprocessor_segment_overrun)
-
-ENTRY(invalid_TSS)
-       RING0_EC_FRAME
-       ASM_CLAC
-       pushl_cfi $do_invalid_TSS
-       jmp error_code
-       CFI_ENDPROC
-END(invalid_TSS)
-
-ENTRY(segment_not_present)
-       RING0_EC_FRAME
-       ASM_CLAC
-       pushl_cfi $do_segment_not_present
-       jmp error_code
-       CFI_ENDPROC
-END(segment_not_present)
-
-ENTRY(stack_segment)
-       RING0_EC_FRAME
-       ASM_CLAC
-       pushl_cfi $do_stack_segment
-       jmp error_code
-       CFI_ENDPROC
-END(stack_segment)
-
-ENTRY(alignment_check)
-       RING0_EC_FRAME
-       ASM_CLAC
-       pushl_cfi $do_alignment_check
-       jmp error_code
-       CFI_ENDPROC
-END(alignment_check)
-
-ENTRY(divide_error)
-       RING0_INT_FRAME
-       ASM_CLAC
-       pushl_cfi $0                    # no error code
-       pushl_cfi $do_divide_error
-       jmp error_code
-       CFI_ENDPROC
-END(divide_error)
-
-#ifdef CONFIG_X86_MCE
-ENTRY(machine_check)
-       RING0_INT_FRAME
-       ASM_CLAC
-       pushl_cfi $0
-       pushl_cfi machine_check_vector
-       jmp error_code
-       CFI_ENDPROC
-END(machine_check)
-#endif
-
-ENTRY(spurious_interrupt_bug)
-       RING0_INT_FRAME
-       ASM_CLAC
-       pushl_cfi $0
-       pushl_cfi $do_spurious_interrupt_bug
-       jmp error_code
-       CFI_ENDPROC
-END(spurious_interrupt_bug)
-
-#ifdef CONFIG_XEN
-/* Xen doesn't set %esp to be precisely what the normal sysenter
-   entrypoint expects, so fix it up before using the normal path. */
-ENTRY(xen_sysenter_target)
-       RING0_INT_FRAME
-       addl $5*4, %esp         /* remove xen-provided frame */
-       CFI_ADJUST_CFA_OFFSET -5*4
-       jmp sysenter_past_esp
-       CFI_ENDPROC
-
-ENTRY(xen_hypervisor_callback)
-       CFI_STARTPROC
-       pushl_cfi $-1 /* orig_ax = -1 => not a system call */
-       SAVE_ALL
-       TRACE_IRQS_OFF
-
-       /* Check to see if we got the event in the critical
-          region in xen_iret_direct, after we've reenabled
-          events and checked for pending events.  This simulates
-          iret instruction's behaviour where it delivers a
-          pending interrupt when enabling interrupts. */
-       movl PT_EIP(%esp),%eax
-       cmpl $xen_iret_start_crit,%eax
-       jb   1f
-       cmpl $xen_iret_end_crit,%eax
-       jae  1f
-
-       jmp  xen_iret_crit_fixup
-
-ENTRY(xen_do_upcall)
-1:     mov %esp, %eax
-       call xen_evtchn_do_upcall
-#ifndef CONFIG_PREEMPT
-       call xen_maybe_preempt_hcall
-#endif
-       jmp  ret_from_intr
-       CFI_ENDPROC
-ENDPROC(xen_hypervisor_callback)
-
-# Hypervisor uses this for application faults while it executes.
-# We get here for two reasons:
-#  1. Fault while reloading DS, ES, FS or GS
-#  2. Fault while executing IRET
-# Category 1 we fix up by reattempting the load, and zeroing the segment
-# register if the load fails.
-# Category 2 we fix up by jumping to do_iret_error. We cannot use the
-# normal Linux return path in this case because if we use the IRET hypercall
-# to pop the stack frame we end up in an infinite loop of failsafe callbacks.
-# We distinguish between categories by maintaining a status value in EAX.
-ENTRY(xen_failsafe_callback)
-       CFI_STARTPROC
-       pushl_cfi %eax
-       movl $1,%eax
-1:     mov 4(%esp),%ds
-2:     mov 8(%esp),%es
-3:     mov 12(%esp),%fs
-4:     mov 16(%esp),%gs
-       /* EAX == 0 => Category 1 (Bad segment)
-          EAX != 0 => Category 2 (Bad IRET) */
-       testl %eax,%eax
-       popl_cfi %eax
-       lea 16(%esp),%esp
-       CFI_ADJUST_CFA_OFFSET -16
-       jz 5f
-       jmp iret_exc
-5:     pushl_cfi $-1 /* orig_ax = -1 => not a system call */
-       SAVE_ALL
-       jmp ret_from_exception
-       CFI_ENDPROC
-
-.section .fixup,"ax"
-6:     xorl %eax,%eax
-       movl %eax,4(%esp)
-       jmp 1b
-7:     xorl %eax,%eax
-       movl %eax,8(%esp)
-       jmp 2b
-8:     xorl %eax,%eax
-       movl %eax,12(%esp)
-       jmp 3b
-9:     xorl %eax,%eax
-       movl %eax,16(%esp)
-       jmp 4b
-.previous
-       _ASM_EXTABLE(1b,6b)
-       _ASM_EXTABLE(2b,7b)
-       _ASM_EXTABLE(3b,8b)
-       _ASM_EXTABLE(4b,9b)
-ENDPROC(xen_failsafe_callback)
-
-BUILD_INTERRUPT3(xen_hvm_callback_vector, HYPERVISOR_CALLBACK_VECTOR,
-               xen_evtchn_do_upcall)
-
-#endif /* CONFIG_XEN */
-
-#if IS_ENABLED(CONFIG_HYPERV)
-
-BUILD_INTERRUPT3(hyperv_callback_vector, HYPERVISOR_CALLBACK_VECTOR,
-       hyperv_vector_handler)
-
-#endif /* CONFIG_HYPERV */
-
-#ifdef CONFIG_FUNCTION_TRACER
-#ifdef CONFIG_DYNAMIC_FTRACE
-
-ENTRY(mcount)
-       ret
-END(mcount)
-
-ENTRY(ftrace_caller)
-       pushl %eax
-       pushl %ecx
-       pushl %edx
-       pushl $0        /* Pass NULL as regs pointer */
-       movl 4*4(%esp), %eax
-       movl 0x4(%ebp), %edx
-       movl function_trace_op, %ecx
-       subl $MCOUNT_INSN_SIZE, %eax
-
-.globl ftrace_call
-ftrace_call:
-       call ftrace_stub
-
-       addl $4,%esp    /* skip NULL pointer */
-       popl %edx
-       popl %ecx
-       popl %eax
-ftrace_ret:
-#ifdef CONFIG_FUNCTION_GRAPH_TRACER
-.globl ftrace_graph_call
-ftrace_graph_call:
-       jmp ftrace_stub
-#endif
-
-.globl ftrace_stub
-ftrace_stub:
-       ret
-END(ftrace_caller)
-
-ENTRY(ftrace_regs_caller)
-       pushf   /* push flags before compare (in cs location) */
-
-       /*
-        * i386 does not save SS and ESP when coming from kernel.
-        * Instead, to get sp, &regs->sp is used (see ptrace.h).
-        * Unfortunately, that means eflags must be at the same location
-        * as the current return ip is. We move the return ip into the
-        * ip location, and move flags into the return ip location.
-        */
-       pushl 4(%esp)   /* save return ip into ip slot */
-
-       pushl $0        /* Load 0 into orig_ax */
-       pushl %gs
-       pushl %fs
-       pushl %es
-       pushl %ds
-       pushl %eax
-       pushl %ebp
-       pushl %edi
-       pushl %esi
-       pushl %edx
-       pushl %ecx
-       pushl %ebx
-
-       movl 13*4(%esp), %eax   /* Get the saved flags */
-       movl %eax, 14*4(%esp)   /* Move saved flags into regs->flags location */
-                               /* clobbering return ip */
-       movl $__KERNEL_CS,13*4(%esp)
-
-       movl 12*4(%esp), %eax   /* Load ip (1st parameter) */
-       subl $MCOUNT_INSN_SIZE, %eax    /* Adjust ip */
-       movl 0x4(%ebp), %edx    /* Load parent ip (2nd parameter) */
-       movl function_trace_op, %ecx /* Save ftrace_pos in 3rd parameter */
-       pushl %esp              /* Save pt_regs as 4th parameter */
-
-GLOBAL(ftrace_regs_call)
-       call ftrace_stub
-
-       addl $4, %esp           /* Skip pt_regs */
-       movl 14*4(%esp), %eax   /* Move flags back into cs */
-       movl %eax, 13*4(%esp)   /* Needed to keep addl from modifying flags */
-       movl 12*4(%esp), %eax   /* Get return ip from regs->ip */
-       movl %eax, 14*4(%esp)   /* Put return ip back for ret */
-
-       popl %ebx
-       popl %ecx
-       popl %edx
-       popl %esi
-       popl %edi
-       popl %ebp
-       popl %eax
-       popl %ds
-       popl %es
-       popl %fs
-       popl %gs
-       addl $8, %esp           /* Skip orig_ax and ip */
-       popf                    /* Pop flags at end (no addl to corrupt flags) */
-       jmp ftrace_ret
-
-       popf
-       jmp  ftrace_stub
-#else /* ! CONFIG_DYNAMIC_FTRACE */
-
-ENTRY(mcount)
-       cmpl $__PAGE_OFFSET, %esp
-       jb ftrace_stub          /* Paging not enabled yet? */
-
-       cmpl $ftrace_stub, ftrace_trace_function
-       jnz trace
-#ifdef CONFIG_FUNCTION_GRAPH_TRACER
-       cmpl $ftrace_stub, ftrace_graph_return
-       jnz ftrace_graph_caller
-
-       cmpl $ftrace_graph_entry_stub, ftrace_graph_entry
-       jnz ftrace_graph_caller
-#endif
-.globl ftrace_stub
-ftrace_stub:
-       ret
-
-       /* taken from glibc */
-trace:
-       pushl %eax
-       pushl %ecx
-       pushl %edx
-       movl 0xc(%esp), %eax
-       movl 0x4(%ebp), %edx
-       subl $MCOUNT_INSN_SIZE, %eax
-
-       call *ftrace_trace_function
-
-       popl %edx
-       popl %ecx
-       popl %eax
-       jmp ftrace_stub
-END(mcount)
-#endif /* CONFIG_DYNAMIC_FTRACE */
-#endif /* CONFIG_FUNCTION_TRACER */
-
-#ifdef CONFIG_FUNCTION_GRAPH_TRACER
-ENTRY(ftrace_graph_caller)
-       pushl %eax
-       pushl %ecx
-       pushl %edx
-       movl 0xc(%esp), %eax
-       lea 0x4(%ebp), %edx
-       movl (%ebp), %ecx
-       subl $MCOUNT_INSN_SIZE, %eax
-       call prepare_ftrace_return
-       popl %edx
-       popl %ecx
-       popl %eax
-       ret
-END(ftrace_graph_caller)
-
-.globl return_to_handler
-return_to_handler:
-       pushl %eax
-       pushl %edx
-       movl %ebp, %eax
-       call ftrace_return_to_handler
-       movl %eax, %ecx
-       popl %edx
-       popl %eax
-       jmp *%ecx
-#endif
-
-#ifdef CONFIG_TRACING
-ENTRY(trace_page_fault)
-       RING0_EC_FRAME
-       ASM_CLAC
-       pushl_cfi $trace_do_page_fault
-       jmp error_code
-       CFI_ENDPROC
-END(trace_page_fault)
-#endif
-
-ENTRY(page_fault)
-       RING0_EC_FRAME
-       ASM_CLAC
-       pushl_cfi $do_page_fault
-       ALIGN
-error_code:
-       /* the function address is in %gs's slot on the stack */
-       pushl_cfi %fs
-       /*CFI_REL_OFFSET fs, 0*/
-       pushl_cfi %es
-       /*CFI_REL_OFFSET es, 0*/
-       pushl_cfi %ds
-       /*CFI_REL_OFFSET ds, 0*/
-       pushl_cfi_reg eax
-       pushl_cfi_reg ebp
-       pushl_cfi_reg edi
-       pushl_cfi_reg esi
-       pushl_cfi_reg edx
-       pushl_cfi_reg ecx
-       pushl_cfi_reg ebx
-       cld
-       movl $(__KERNEL_PERCPU), %ecx
-       movl %ecx, %fs
-       UNWIND_ESPFIX_STACK
-       GS_TO_REG %ecx
-       movl PT_GS(%esp), %edi          # get the function address
-       movl PT_ORIG_EAX(%esp), %edx    # get the error code
-       movl $-1, PT_ORIG_EAX(%esp)     # no syscall to restart
-       REG_TO_PTGS %ecx
-       SET_KERNEL_GS %ecx
-       movl $(__USER_DS), %ecx
-       movl %ecx, %ds
-       movl %ecx, %es
-       TRACE_IRQS_OFF
-       movl %esp,%eax                  # pt_regs pointer
-       call *%edi
-       jmp ret_from_exception
-       CFI_ENDPROC
-END(page_fault)
-
-/*
- * Debug traps and NMI can happen at the one SYSENTER instruction
- * that sets up the real kernel stack. Check here, since we can't
- * allow the wrong stack to be used.
- *
- * "TSS_sysenter_sp0+12" is because the NMI/debug handler will have
- * already pushed 3 words if it hits on the sysenter instruction:
- * eflags, cs and eip.
- *
- * We just load the right stack, and push the three (known) values
- * by hand onto the new stack - while updating the return eip past
- * the instruction that would have done it for sysenter.
- */
-.macro FIX_STACK offset ok label
-       cmpw $__KERNEL_CS, 4(%esp)
-       jne \ok
-\label:
-       movl TSS_sysenter_sp0 + \offset(%esp), %esp
-       CFI_DEF_CFA esp, 0
-       CFI_UNDEFINED eip
-       pushfl_cfi
-       pushl_cfi $__KERNEL_CS
-       pushl_cfi $sysenter_past_esp
-       CFI_REL_OFFSET eip, 0
-.endm
-
-ENTRY(debug)
-       RING0_INT_FRAME
-       ASM_CLAC
-       cmpl $ia32_sysenter_target,(%esp)
-       jne debug_stack_correct
-       FIX_STACK 12, debug_stack_correct, debug_esp_fix_insn
-debug_stack_correct:
-       pushl_cfi $-1                   # mark this as an int
-       SAVE_ALL
-       TRACE_IRQS_OFF
-       xorl %edx,%edx                  # error code 0
-       movl %esp,%eax                  # pt_regs pointer
-       call do_debug
-       jmp ret_from_exception
-       CFI_ENDPROC
-END(debug)
-
-/*
- * NMI is doubly nasty. It can happen _while_ we're handling
- * a debug fault, and the debug fault hasn't yet been able to
- * clear up the stack. So we first check whether we got  an
- * NMI on the sysenter entry path, but after that we need to
- * check whether we got an NMI on the debug path where the debug
- * fault happened on the sysenter path.
- */
-ENTRY(nmi)
-       RING0_INT_FRAME
-       ASM_CLAC
-#ifdef CONFIG_X86_ESPFIX32
-       pushl_cfi %eax
-       movl %ss, %eax
-       cmpw $__ESPFIX_SS, %ax
-       popl_cfi %eax
-       je nmi_espfix_stack
-#endif
-       cmpl $ia32_sysenter_target,(%esp)
-       je nmi_stack_fixup
-       pushl_cfi %eax
-       movl %esp,%eax
-       /* Do not access memory above the end of our stack page,
-        * it might not exist.
-        */
-       andl $(THREAD_SIZE-1),%eax
-       cmpl $(THREAD_SIZE-20),%eax
-       popl_cfi %eax
-       jae nmi_stack_correct
-       cmpl $ia32_sysenter_target,12(%esp)
-       je nmi_debug_stack_check
-nmi_stack_correct:
-       /* We have a RING0_INT_FRAME here */
-       pushl_cfi %eax
-       SAVE_ALL
-       xorl %edx,%edx          # zero error code
-       movl %esp,%eax          # pt_regs pointer
-       call do_nmi
-       jmp restore_all_notrace
-       CFI_ENDPROC
-
-nmi_stack_fixup:
-       RING0_INT_FRAME
-       FIX_STACK 12, nmi_stack_correct, 1
-       jmp nmi_stack_correct
-
-nmi_debug_stack_check:
-       /* We have a RING0_INT_FRAME here */
-       cmpw $__KERNEL_CS,16(%esp)
-       jne nmi_stack_correct
-       cmpl $debug,(%esp)
-       jb nmi_stack_correct
-       cmpl $debug_esp_fix_insn,(%esp)
-       ja nmi_stack_correct
-       FIX_STACK 24, nmi_stack_correct, 1
-       jmp nmi_stack_correct
-
-#ifdef CONFIG_X86_ESPFIX32
-nmi_espfix_stack:
-       /* We have a RING0_INT_FRAME here.
-        *
-        * create the pointer to lss back
-        */
-       pushl_cfi %ss
-       pushl_cfi %esp
-       addl $4, (%esp)
-       /* copy the iret frame of 12 bytes */
-       .rept 3
-       pushl_cfi 16(%esp)
-       .endr
-       pushl_cfi %eax
-       SAVE_ALL
-       FIXUP_ESPFIX_STACK              # %eax == %esp
-       xorl %edx,%edx                  # zero error code
-       call do_nmi
-       RESTORE_REGS
-       lss 12+4(%esp), %esp            # back to espfix stack
-       CFI_ADJUST_CFA_OFFSET -24
-       jmp irq_return
-#endif
-       CFI_ENDPROC
-END(nmi)
-
-ENTRY(int3)
-       RING0_INT_FRAME
-       ASM_CLAC
-       pushl_cfi $-1                   # mark this as an int
-       SAVE_ALL
-       TRACE_IRQS_OFF
-       xorl %edx,%edx          # zero error code
-       movl %esp,%eax          # pt_regs pointer
-       call do_int3
-       jmp ret_from_exception
-       CFI_ENDPROC
-END(int3)
-
-ENTRY(general_protection)
-       RING0_EC_FRAME
-       pushl_cfi $do_general_protection
-       jmp error_code
-       CFI_ENDPROC
-END(general_protection)
-
-#ifdef CONFIG_KVM_GUEST
-ENTRY(async_page_fault)
-       RING0_EC_FRAME
-       ASM_CLAC
-       pushl_cfi $do_async_page_fault
-       jmp error_code
-       CFI_ENDPROC
-END(async_page_fault)
-#endif
-
diff --git a/arch/x86/kernel/fpu/Makefile b/arch/x86/kernel/fpu/Makefile
new file mode 100644 (file)
index 0000000..68279ef
--- /dev/null
@@ -0,0 +1,5 @@
+#
+# Build rules for the FPU support code:
+#
+
+obj-y                          += init.o bugs.o core.o regset.o signal.o xstate.o
diff --git a/arch/x86/kernel/fpu/bugs.c b/arch/x86/kernel/fpu/bugs.c
new file mode 100644 (file)
index 0000000..dd9ca9b
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * x86 FPU bug checks:
+ */
+#include <asm/fpu/internal.h>
+
+/*
+ * Boot time CPU/FPU FDIV bug detection code:
+ */
+
+static double __initdata x = 4195835.0;
+static double __initdata y = 3145727.0;
+
+/*
+ * This used to check for exceptions..
+ * However, it turns out that to support that,
+ * the XMM trap handlers basically had to
+ * be buggy. So let's have a correct XMM trap
+ * handler, and forget about printing out
+ * some status at boot.
+ *
+ * We should really only care about bugs here
+ * anyway. Not features.
+ */
+static void __init check_fpu(void)
+{
+       u32 cr0_saved;
+       s32 fdiv_bug;
+
+       /* We might have CR0::TS set already, clear it: */
+       cr0_saved = read_cr0();
+       write_cr0(cr0_saved & ~X86_CR0_TS);
+
+       kernel_fpu_begin();
+
+       /*
+        * trap_init() enabled FXSR and company _before_ testing for FP
+        * problems here.
+        *
+        * Test for the divl bug: http://en.wikipedia.org/wiki/Fdiv_bug
+        */
+       __asm__("fninit\n\t"
+               "fldl %1\n\t"
+               "fdivl %2\n\t"
+               "fmull %2\n\t"
+               "fldl %1\n\t"
+               "fsubp %%st,%%st(1)\n\t"
+               "fistpl %0\n\t"
+               "fwait\n\t"
+               "fninit"
+               : "=m" (*&fdiv_bug)
+               : "m" (*&x), "m" (*&y));
+
+       kernel_fpu_end();
+
+       write_cr0(cr0_saved);
+
+       if (fdiv_bug) {
+               set_cpu_bug(&boot_cpu_data, X86_BUG_FDIV);
+               pr_warn("Hmm, FPU with FDIV bug\n");
+       }
+}
+
+void __init fpu__init_check_bugs(void)
+{
+       /*
+        * kernel_fpu_begin/end() in check_fpu() relies on the patched
+        * alternative instructions.
+        */
+       if (cpu_has_fpu)
+               check_fpu();
+}
diff --git a/arch/x86/kernel/fpu/core.c b/arch/x86/kernel/fpu/core.c
new file mode 100644 (file)
index 0000000..79de954
--- /dev/null
@@ -0,0 +1,523 @@
+/*
+ *  Copyright (C) 1994 Linus Torvalds
+ *
+ *  Pentium III FXSR, SSE support
+ *  General FPU state handling cleanups
+ *     Gareth Hughes <gareth@valinux.com>, May 2000
+ */
+#include <asm/fpu/internal.h>
+#include <asm/fpu/regset.h>
+#include <asm/fpu/signal.h>
+#include <asm/traps.h>
+
+#include <linux/hardirq.h>
+
+/*
+ * Represents the initial FPU state. It's mostly (but not completely) zeroes,
+ * depending on the FPU hardware format:
+ */
+union fpregs_state init_fpstate __read_mostly;
+
+/*
+ * Track whether the kernel is using the FPU state
+ * currently.
+ *
+ * This flag is used:
+ *
+ *   - by IRQ context code to potentially use the FPU
+ *     if it's unused.
+ *
+ *   - to debug kernel_fpu_begin()/end() correctness
+ */
+static DEFINE_PER_CPU(bool, in_kernel_fpu);
+
+/*
+ * Track which context is using the FPU on the CPU:
+ */
+DEFINE_PER_CPU(struct fpu *, fpu_fpregs_owner_ctx);
+
+static void kernel_fpu_disable(void)
+{
+       WARN_ON_FPU(this_cpu_read(in_kernel_fpu));
+       this_cpu_write(in_kernel_fpu, true);
+}
+
+static void kernel_fpu_enable(void)
+{
+       WARN_ON_FPU(!this_cpu_read(in_kernel_fpu));
+       this_cpu_write(in_kernel_fpu, false);
+}
+
+static bool kernel_fpu_disabled(void)
+{
+       return this_cpu_read(in_kernel_fpu);
+}
+
+/*
+ * Were we in an interrupt that interrupted kernel mode?
+ *
+ * On others, we can do a kernel_fpu_begin/end() pair *ONLY* if that
+ * pair does nothing at all: the thread must not have fpu (so
+ * that we don't try to save the FPU state), and TS must
+ * be set (so that the clts/stts pair does nothing that is
+ * visible in the interrupted kernel thread).
+ *
+ * Except for the eagerfpu case when we return true; in the likely case
+ * the thread has FPU but we are not going to set/clear TS.
+ */
+static bool interrupted_kernel_fpu_idle(void)
+{
+       if (kernel_fpu_disabled())
+               return false;
+
+       if (use_eager_fpu())
+               return true;
+
+       return !current->thread.fpu.fpregs_active && (read_cr0() & X86_CR0_TS);
+}
+
+/*
+ * Were we in user mode (or vm86 mode) when we were
+ * interrupted?
+ *
+ * Doing kernel_fpu_begin/end() is ok if we are running
+ * in an interrupt context from user mode - we'll just
+ * save the FPU state as required.
+ */
+static bool interrupted_user_mode(void)
+{
+       struct pt_regs *regs = get_irq_regs();
+       return regs && user_mode(regs);
+}
+
+/*
+ * Can we use the FPU in kernel mode with the
+ * whole "kernel_fpu_begin/end()" sequence?
+ *
+ * It's always ok in process context (ie "not interrupt")
+ * but it is sometimes ok even from an irq.
+ */
+bool irq_fpu_usable(void)
+{
+       return !in_interrupt() ||
+               interrupted_user_mode() ||
+               interrupted_kernel_fpu_idle();
+}
+EXPORT_SYMBOL(irq_fpu_usable);
+
+void __kernel_fpu_begin(void)
+{
+       struct fpu *fpu = &current->thread.fpu;
+
+       WARN_ON_FPU(!irq_fpu_usable());
+
+       kernel_fpu_disable();
+
+       if (fpu->fpregs_active) {
+               copy_fpregs_to_fpstate(fpu);
+       } else {
+               this_cpu_write(fpu_fpregs_owner_ctx, NULL);
+               __fpregs_activate_hw();
+       }
+}
+EXPORT_SYMBOL(__kernel_fpu_begin);
+
+void __kernel_fpu_end(void)
+{
+       struct fpu *fpu = &current->thread.fpu;
+
+       if (fpu->fpregs_active)
+               copy_kernel_to_fpregs(&fpu->state);
+       else
+               __fpregs_deactivate_hw();
+
+       kernel_fpu_enable();
+}
+EXPORT_SYMBOL(__kernel_fpu_end);
+
+void kernel_fpu_begin(void)
+{
+       preempt_disable();
+       __kernel_fpu_begin();
+}
+EXPORT_SYMBOL_GPL(kernel_fpu_begin);
+
+void kernel_fpu_end(void)
+{
+       __kernel_fpu_end();
+       preempt_enable();
+}
+EXPORT_SYMBOL_GPL(kernel_fpu_end);
+
+/*
+ * CR0::TS save/restore functions:
+ */
+int irq_ts_save(void)
+{
+       /*
+        * If in process context and not atomic, we can take a spurious DNA fault.
+        * Otherwise, doing clts() in process context requires disabling preemption
+        * or some heavy lifting like kernel_fpu_begin()
+        */
+       if (!in_atomic())
+               return 0;
+
+       if (read_cr0() & X86_CR0_TS) {
+               clts();
+               return 1;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(irq_ts_save);
+
+void irq_ts_restore(int TS_state)
+{
+       if (TS_state)
+               stts();
+}
+EXPORT_SYMBOL_GPL(irq_ts_restore);
+
+/*
+ * Save the FPU state (mark it for reload if necessary):
+ *
+ * This only ever gets called for the current task.
+ */
+void fpu__save(struct fpu *fpu)
+{
+       WARN_ON_FPU(fpu != &current->thread.fpu);
+
+       preempt_disable();
+       if (fpu->fpregs_active) {
+               if (!copy_fpregs_to_fpstate(fpu))
+                       fpregs_deactivate(fpu);
+       }
+       preempt_enable();
+}
+EXPORT_SYMBOL_GPL(fpu__save);
+
+/*
+ * Legacy x87 fpstate state init:
+ */
+static inline void fpstate_init_fstate(struct fregs_state *fp)
+{
+       fp->cwd = 0xffff037fu;
+       fp->swd = 0xffff0000u;
+       fp->twd = 0xffffffffu;
+       fp->fos = 0xffff0000u;
+}
+
+void fpstate_init(union fpregs_state *state)
+{
+       if (!cpu_has_fpu) {
+               fpstate_init_soft(&state->soft);
+               return;
+       }
+
+       memset(state, 0, xstate_size);
+
+       if (cpu_has_fxsr)
+               fpstate_init_fxstate(&state->fxsave);
+       else
+               fpstate_init_fstate(&state->fsave);
+}
+EXPORT_SYMBOL_GPL(fpstate_init);
+
+/*
+ * Copy the current task's FPU state to a new task's FPU context.
+ *
+ * In both the 'eager' and the 'lazy' case we save hardware registers
+ * directly to the destination buffer.
+ */
+static void fpu_copy(struct fpu *dst_fpu, struct fpu *src_fpu)
+{
+       WARN_ON_FPU(src_fpu != &current->thread.fpu);
+
+       /*
+        * Don't let 'init optimized' areas of the XSAVE area
+        * leak into the child task:
+        */
+       if (use_eager_fpu())
+               memset(&dst_fpu->state.xsave, 0, xstate_size);
+
+       /*
+        * Save current FPU registers directly into the child
+        * FPU context, without any memory-to-memory copying.
+        *
+        * If the FPU context got destroyed in the process (FNSAVE
+        * done on old CPUs) then copy it back into the source
+        * context and mark the current task for lazy restore.
+        *
+        * We have to do all this with preemption disabled,
+        * mostly because of the FNSAVE case, because in that
+        * case we must not allow preemption in the window
+        * between the FNSAVE and us marking the context lazy.
+        *
+        * It shouldn't be an issue as even FNSAVE is plenty
+        * fast in terms of critical section length.
+        */
+       preempt_disable();
+       if (!copy_fpregs_to_fpstate(dst_fpu)) {
+               memcpy(&src_fpu->state, &dst_fpu->state, xstate_size);
+               fpregs_deactivate(src_fpu);
+       }
+       preempt_enable();
+}
+
+int fpu__copy(struct fpu *dst_fpu, struct fpu *src_fpu)
+{
+       dst_fpu->counter = 0;
+       dst_fpu->fpregs_active = 0;
+       dst_fpu->last_cpu = -1;
+
+       if (src_fpu->fpstate_active)
+               fpu_copy(dst_fpu, src_fpu);
+
+       return 0;
+}
+
+/*
+ * Activate the current task's in-memory FPU context,
+ * if it has not been used before:
+ */
+void fpu__activate_curr(struct fpu *fpu)
+{
+       WARN_ON_FPU(fpu != &current->thread.fpu);
+
+       if (!fpu->fpstate_active) {
+               fpstate_init(&fpu->state);
+
+               /* Safe to do for the current task: */
+               fpu->fpstate_active = 1;
+       }
+}
+EXPORT_SYMBOL_GPL(fpu__activate_curr);
+
+/*
+ * This function must be called before we read a task's fpstate.
+ *
+ * If the task has not used the FPU before then initialize its
+ * fpstate.
+ *
+ * If the task has used the FPU before then save it.
+ */
+void fpu__activate_fpstate_read(struct fpu *fpu)
+{
+       /*
+        * If fpregs are active (in the current CPU), then
+        * copy them to the fpstate:
+        */
+       if (fpu->fpregs_active) {
+               fpu__save(fpu);
+       } else {
+               if (!fpu->fpstate_active) {
+                       fpstate_init(&fpu->state);
+
+                       /* Safe to do for current and for stopped child tasks: */
+                       fpu->fpstate_active = 1;
+               }
+       }
+}
+
+/*
+ * This function must be called before we write a task's fpstate.
+ *
+ * If the task has used the FPU before then unlazy it.
+ * If the task has not used the FPU before then initialize its fpstate.
+ *
+ * After this function call, after registers in the fpstate are
+ * modified and the child task has woken up, the child task will
+ * restore the modified FPU state from the modified context. If we
+ * didn't clear its lazy status here then the lazy in-registers
+ * state pending on its former CPU could be restored, corrupting
+ * the modifications.
+ */
+void fpu__activate_fpstate_write(struct fpu *fpu)
+{
+       /*
+        * Only stopped child tasks can be used to modify the FPU
+        * state in the fpstate buffer:
+        */
+       WARN_ON_FPU(fpu == &current->thread.fpu);
+
+       if (fpu->fpstate_active) {
+               /* Invalidate any lazy state: */
+               fpu->last_cpu = -1;
+       } else {
+               fpstate_init(&fpu->state);
+
+               /* Safe to do for stopped child tasks: */
+               fpu->fpstate_active = 1;
+       }
+}
+
+/*
+ * 'fpu__restore()' is called to copy FPU registers from
+ * the FPU fpstate to the live hw registers and to activate
+ * access to the hardware registers, so that FPU instructions
+ * can be used afterwards.
+ *
+ * Must be called with kernel preemption disabled (for example
+ * with local interrupts disabled, as it is in the case of
+ * do_device_not_available()).
+ */
+void fpu__restore(struct fpu *fpu)
+{
+       fpu__activate_curr(fpu);
+
+       /* Avoid __kernel_fpu_begin() right after fpregs_activate() */
+       kernel_fpu_disable();
+       fpregs_activate(fpu);
+       copy_kernel_to_fpregs(&fpu->state);
+       fpu->counter++;
+       kernel_fpu_enable();
+}
+EXPORT_SYMBOL_GPL(fpu__restore);
+
+/*
+ * Drops current FPU state: deactivates the fpregs and
+ * the fpstate. NOTE: it still leaves previous contents
+ * in the fpregs in the eager-FPU case.
+ *
+ * This function can be used in cases where we know that
+ * a state-restore is coming: either an explicit one,
+ * or a reschedule.
+ */
+void fpu__drop(struct fpu *fpu)
+{
+       preempt_disable();
+       fpu->counter = 0;
+
+       if (fpu->fpregs_active) {
+               /* Ignore delayed exceptions from user space */
+               asm volatile("1: fwait\n"
+                            "2:\n"
+                            _ASM_EXTABLE(1b, 2b));
+               fpregs_deactivate(fpu);
+       }
+
+       fpu->fpstate_active = 0;
+
+       preempt_enable();
+}
+
+/*
+ * Clear FPU registers by setting them up from
+ * the init fpstate:
+ */
+static inline void copy_init_fpstate_to_fpregs(void)
+{
+       if (use_xsave())
+               copy_kernel_to_xregs(&init_fpstate.xsave, -1);
+       else
+               copy_kernel_to_fxregs(&init_fpstate.fxsave);
+}
+
+/*
+ * Clear the FPU state back to init state.
+ *
+ * Called by sys_execve(), by the signal handler code and by various
+ * error paths.
+ */
+void fpu__clear(struct fpu *fpu)
+{
+       WARN_ON_FPU(fpu != &current->thread.fpu); /* Almost certainly an anomaly */
+
+       if (!use_eager_fpu()) {
+               /* FPU state will be reallocated lazily at the first use. */
+               fpu__drop(fpu);
+       } else {
+               if (!fpu->fpstate_active) {
+                       fpu__activate_curr(fpu);
+                       user_fpu_begin();
+               }
+               copy_init_fpstate_to_fpregs();
+       }
+}
+
+/*
+ * x87 math exception handling:
+ */
+
+static inline unsigned short get_fpu_cwd(struct fpu *fpu)
+{
+       if (cpu_has_fxsr) {
+               return fpu->state.fxsave.cwd;
+       } else {
+               return (unsigned short)fpu->state.fsave.cwd;
+       }
+}
+
+static inline unsigned short get_fpu_swd(struct fpu *fpu)
+{
+       if (cpu_has_fxsr) {
+               return fpu->state.fxsave.swd;
+       } else {
+               return (unsigned short)fpu->state.fsave.swd;
+       }
+}
+
+static inline unsigned short get_fpu_mxcsr(struct fpu *fpu)
+{
+       if (cpu_has_xmm) {
+               return fpu->state.fxsave.mxcsr;
+       } else {
+               return MXCSR_DEFAULT;
+       }
+}
+
+int fpu__exception_code(struct fpu *fpu, int trap_nr)
+{
+       int err;
+
+       if (trap_nr == X86_TRAP_MF) {
+               unsigned short cwd, swd;
+               /*
+                * (~cwd & swd) will mask out exceptions that are not set to unmasked
+                * status.  0x3f is the exception bits in these regs, 0x200 is the
+                * C1 reg you need in case of a stack fault, 0x040 is the stack
+                * fault bit.  We should only be taking one exception at a time,
+                * so if this combination doesn't produce any single exception,
+                * then we have a bad program that isn't synchronizing its FPU usage
+                * and it will suffer the consequences since we won't be able to
+                * fully reproduce the context of the exception
+                */
+               cwd = get_fpu_cwd(fpu);
+               swd = get_fpu_swd(fpu);
+
+               err = swd & ~cwd;
+       } else {
+               /*
+                * The SIMD FPU exceptions are handled a little differently, as there
+                * is only a single status/control register.  Thus, to determine which
+                * unmasked exception was caught we must mask the exception mask bits
+                * at 0x1f80, and then use these to mask the exception bits at 0x3f.
+                */
+               unsigned short mxcsr = get_fpu_mxcsr(fpu);
+               err = ~(mxcsr >> 7) & mxcsr;
+       }
+
+       if (err & 0x001) {      /* Invalid op */
+               /*
+                * swd & 0x240 == 0x040: Stack Underflow
+                * swd & 0x240 == 0x240: Stack Overflow
+                * User must clear the SF bit (0x40) if set
+                */
+               return FPE_FLTINV;
+       } else if (err & 0x004) { /* Divide by Zero */
+               return FPE_FLTDIV;
+       } else if (err & 0x008) { /* Overflow */
+               return FPE_FLTOVF;
+       } else if (err & 0x012) { /* Denormal, Underflow */
+               return FPE_FLTUND;
+       } else if (err & 0x020) { /* Precision */
+               return FPE_FLTRES;
+       }
+
+       /*
+        * If we're using IRQ 13, or supposedly even some trap
+        * X86_TRAP_MF implementations, it's possible
+        * we get a spurious trap, which is not an error.
+        */
+       return 0;
+}
diff --git a/arch/x86/kernel/fpu/init.c b/arch/x86/kernel/fpu/init.c
new file mode 100644 (file)
index 0000000..fc878fe
--- /dev/null
@@ -0,0 +1,354 @@
+/*
+ * x86 FPU boot time init code:
+ */
+#include <asm/fpu/internal.h>
+#include <asm/tlbflush.h>
+
+/*
+ * Initialize the TS bit in CR0 according to the style of context-switches
+ * we are using:
+ */
+static void fpu__init_cpu_ctx_switch(void)
+{
+       if (!cpu_has_eager_fpu)
+               stts();
+       else
+               clts();
+}
+
+/*
+ * Initialize the registers found in all CPUs, CR0 and CR4:
+ */
+static void fpu__init_cpu_generic(void)
+{
+       unsigned long cr0;
+       unsigned long cr4_mask = 0;
+
+       if (cpu_has_fxsr)
+               cr4_mask |= X86_CR4_OSFXSR;
+       if (cpu_has_xmm)
+               cr4_mask |= X86_CR4_OSXMMEXCPT;
+       if (cr4_mask)
+               cr4_set_bits(cr4_mask);
+
+       cr0 = read_cr0();
+       cr0 &= ~(X86_CR0_TS|X86_CR0_EM); /* clear TS and EM */
+       if (!cpu_has_fpu)
+               cr0 |= X86_CR0_EM;
+       write_cr0(cr0);
+
+       /* Flush out any pending x87 state: */
+       asm volatile ("fninit");
+}
+
+/*
+ * Enable all supported FPU features. Called when a CPU is brought online:
+ */
+void fpu__init_cpu(void)
+{
+       fpu__init_cpu_generic();
+       fpu__init_cpu_xstate();
+       fpu__init_cpu_ctx_switch();
+}
+
+/*
+ * The earliest FPU detection code.
+ *
+ * Set the X86_FEATURE_FPU CPU-capability bit based on
+ * trying to execute an actual sequence of FPU instructions:
+ */
+static void fpu__init_system_early_generic(struct cpuinfo_x86 *c)
+{
+       unsigned long cr0;
+       u16 fsw, fcw;
+
+       fsw = fcw = 0xffff;
+
+       cr0 = read_cr0();
+       cr0 &= ~(X86_CR0_TS | X86_CR0_EM);
+       write_cr0(cr0);
+
+       asm volatile("fninit ; fnstsw %0 ; fnstcw %1"
+                    : "+m" (fsw), "+m" (fcw));
+
+       if (fsw == 0 && (fcw & 0x103f) == 0x003f)
+               set_cpu_cap(c, X86_FEATURE_FPU);
+       else
+               clear_cpu_cap(c, X86_FEATURE_FPU);
+
+#ifndef CONFIG_MATH_EMULATION
+       if (!cpu_has_fpu) {
+               pr_emerg("x86/fpu: Giving up, no FPU found and no math emulation present\n");
+               for (;;)
+                       asm volatile("hlt");
+       }
+#endif
+}
+
+/*
+ * Boot time FPU feature detection code:
+ */
+unsigned int mxcsr_feature_mask __read_mostly = 0xffffffffu;
+
+static void __init fpu__init_system_mxcsr(void)
+{
+       unsigned int mask = 0;
+
+       if (cpu_has_fxsr) {
+               struct fxregs_state fx_tmp __aligned(32) = { };
+
+               asm volatile("fxsave %0" : "+m" (fx_tmp));
+
+               mask = fx_tmp.mxcsr_mask;
+
+               /*
+                * If zero then use the default features mask,
+                * which has all features set, except the
+                * denormals-are-zero feature bit:
+                */
+               if (mask == 0)
+                       mask = 0x0000ffbf;
+       }
+       mxcsr_feature_mask &= mask;
+}
+
+/*
+ * Once per bootup FPU initialization sequences that will run on most x86 CPUs:
+ */
+static void __init fpu__init_system_generic(void)
+{
+       /*
+        * Set up the legacy init FPU context. (xstate init might overwrite this
+        * with a more modern format, if the CPU supports it.)
+        */
+       fpstate_init_fxstate(&init_fpstate.fxsave);
+
+       fpu__init_system_mxcsr();
+}
+
+/*
+ * Size of the FPU context state. All tasks in the system use the
+ * same context size, regardless of what portion they use.
+ * This is inherent to the XSAVE architecture which puts all state
+ * components into a single, continuous memory block:
+ */
+unsigned int xstate_size;
+EXPORT_SYMBOL_GPL(xstate_size);
+
+/*
+ * Set up the xstate_size based on the legacy FPU context size.
+ *
+ * We set this up first, and later it will be overwritten by
+ * fpu__init_system_xstate() if the CPU knows about xstates.
+ */
+static void __init fpu__init_system_xstate_size_legacy(void)
+{
+       static int on_boot_cpu = 1;
+
+       WARN_ON_FPU(!on_boot_cpu);
+       on_boot_cpu = 0;
+
+       /*
+        * Note that xstate_size might be overwriten later during
+        * fpu__init_system_xstate().
+        */
+
+       if (!cpu_has_fpu) {
+               /*
+                * Disable xsave as we do not support it if i387
+                * emulation is enabled.
+                */
+               setup_clear_cpu_cap(X86_FEATURE_XSAVE);
+               setup_clear_cpu_cap(X86_FEATURE_XSAVEOPT);
+               xstate_size = sizeof(struct swregs_state);
+       } else {
+               if (cpu_has_fxsr)
+                       xstate_size = sizeof(struct fxregs_state);
+               else
+                       xstate_size = sizeof(struct fregs_state);
+       }
+       /*
+        * Quirk: we don't yet handle the XSAVES* instructions
+        * correctly, as we don't correctly convert between
+        * standard and compacted format when interfacing
+        * with user-space - so disable it for now.
+        *
+        * The difference is small: with recent CPUs the
+        * compacted format is only marginally smaller than
+        * the standard FPU state format.
+        *
+        * ( This is easy to backport while we are fixing
+        *   XSAVES* support. )
+        */
+       setup_clear_cpu_cap(X86_FEATURE_XSAVES);
+}
+
+/*
+ * FPU context switching strategies:
+ *
+ * Against popular belief, we don't do lazy FPU saves, due to the
+ * task migration complications it brings on SMP - we only do
+ * lazy FPU restores.
+ *
+ * 'lazy' is the traditional strategy, which is based on setting
+ * CR0::TS to 1 during context-switch (instead of doing a full
+ * restore of the FPU state), which causes the first FPU instruction
+ * after the context switch (whenever it is executed) to fault - at
+ * which point we lazily restore the FPU state into FPU registers.
+ *
+ * Tasks are of course under no obligation to execute FPU instructions,
+ * so it can easily happen that another context-switch occurs without
+ * a single FPU instruction being executed. If we eventually switch
+ * back to the original task (that still owns the FPU) then we have
+ * not only saved the restores along the way, but we also have the
+ * FPU ready to be used for the original task.
+ *
+ * 'eager' switching is used on modern CPUs, there we switch the FPU
+ * state during every context switch, regardless of whether the task
+ * has used FPU instructions in that time slice or not. This is done
+ * because modern FPU context saving instructions are able to optimize
+ * state saving and restoration in hardware: they can detect both
+ * unused and untouched FPU state and optimize accordingly.
+ *
+ * [ Note that even in 'lazy' mode we might optimize context switches
+ *   to use 'eager' restores, if we detect that a task is using the FPU
+ *   frequently. See the fpu->counter logic in fpu/internal.h for that. ]
+ */
+static enum { AUTO, ENABLE, DISABLE } eagerfpu = AUTO;
+
+static int __init eager_fpu_setup(char *s)
+{
+       if (!strcmp(s, "on"))
+               eagerfpu = ENABLE;
+       else if (!strcmp(s, "off"))
+               eagerfpu = DISABLE;
+       else if (!strcmp(s, "auto"))
+               eagerfpu = AUTO;
+       return 1;
+}
+__setup("eagerfpu=", eager_fpu_setup);
+
+/*
+ * Pick the FPU context switching strategy:
+ */
+static void __init fpu__init_system_ctx_switch(void)
+{
+       static bool on_boot_cpu = 1;
+
+       WARN_ON_FPU(!on_boot_cpu);
+       on_boot_cpu = 0;
+
+       WARN_ON_FPU(current->thread.fpu.fpstate_active);
+       current_thread_info()->status = 0;
+
+       /* Auto enable eagerfpu for xsaveopt */
+       if (cpu_has_xsaveopt && eagerfpu != DISABLE)
+               eagerfpu = ENABLE;
+
+       if (xfeatures_mask & XSTATE_EAGER) {
+               if (eagerfpu == DISABLE) {
+                       pr_err("x86/fpu: eagerfpu switching disabled, disabling the following xstate features: 0x%llx.\n",
+                              xfeatures_mask & XSTATE_EAGER);
+                       xfeatures_mask &= ~XSTATE_EAGER;
+               } else {
+                       eagerfpu = ENABLE;
+               }
+       }
+
+       if (eagerfpu == ENABLE)
+               setup_force_cpu_cap(X86_FEATURE_EAGER_FPU);
+
+       printk(KERN_INFO "x86/fpu: Using '%s' FPU context switches.\n", eagerfpu == ENABLE ? "eager" : "lazy");
+}
+
+/*
+ * Called on the boot CPU once per system bootup, to set up the initial
+ * FPU state that is later cloned into all processes:
+ */
+void __init fpu__init_system(struct cpuinfo_x86 *c)
+{
+       fpu__init_system_early_generic(c);
+
+       /*
+        * The FPU has to be operational for some of the
+        * later FPU init activities:
+        */
+       fpu__init_cpu();
+
+       /*
+        * But don't leave CR0::TS set yet, as some of the FPU setup
+        * methods depend on being able to execute FPU instructions
+        * that will fault on a set TS, such as the FXSAVE in
+        * fpu__init_system_mxcsr().
+        */
+       clts();
+
+       fpu__init_system_generic();
+       fpu__init_system_xstate_size_legacy();
+       fpu__init_system_xstate();
+
+       fpu__init_system_ctx_switch();
+}
+
+/*
+ * Boot parameter to turn off FPU support and fall back to math-emu:
+ */
+static int __init no_387(char *s)
+{
+       setup_clear_cpu_cap(X86_FEATURE_FPU);
+       return 1;
+}
+__setup("no387", no_387);
+
+/*
+ * Disable all xstate CPU features:
+ */
+static int __init x86_noxsave_setup(char *s)
+{
+       if (strlen(s))
+               return 0;
+
+       setup_clear_cpu_cap(X86_FEATURE_XSAVE);
+       setup_clear_cpu_cap(X86_FEATURE_XSAVEOPT);
+       setup_clear_cpu_cap(X86_FEATURE_XSAVES);
+       setup_clear_cpu_cap(X86_FEATURE_AVX);
+       setup_clear_cpu_cap(X86_FEATURE_AVX2);
+
+       return 1;
+}
+__setup("noxsave", x86_noxsave_setup);
+
+/*
+ * Disable the XSAVEOPT instruction specifically:
+ */
+static int __init x86_noxsaveopt_setup(char *s)
+{
+       setup_clear_cpu_cap(X86_FEATURE_XSAVEOPT);
+
+       return 1;
+}
+__setup("noxsaveopt", x86_noxsaveopt_setup);
+
+/*
+ * Disable the XSAVES instruction:
+ */
+static int __init x86_noxsaves_setup(char *s)
+{
+       setup_clear_cpu_cap(X86_FEATURE_XSAVES);
+
+       return 1;
+}
+__setup("noxsaves", x86_noxsaves_setup);
+
+/*
+ * Disable FX save/restore and SSE support:
+ */
+static int __init x86_nofxsr_setup(char *s)
+{
+       setup_clear_cpu_cap(X86_FEATURE_FXSR);
+       setup_clear_cpu_cap(X86_FEATURE_FXSR_OPT);
+       setup_clear_cpu_cap(X86_FEATURE_XMM);
+
+       return 1;
+}
+__setup("nofxsr", x86_nofxsr_setup);
diff --git a/arch/x86/kernel/fpu/regset.c b/arch/x86/kernel/fpu/regset.c
new file mode 100644 (file)
index 0000000..dc60810
--- /dev/null
@@ -0,0 +1,356 @@
+/*
+ * FPU register's regset abstraction, for ptrace, core dumps, etc.
+ */
+#include <asm/fpu/internal.h>
+#include <asm/fpu/signal.h>
+#include <asm/fpu/regset.h>
+
+/*
+ * The xstateregs_active() routine is the same as the regset_fpregs_active() routine,
+ * as the "regset->n" for the xstate regset will be updated based on the feature
+ * capabilites supported by the xsave.
+ */
+int regset_fpregs_active(struct task_struct *target, const struct user_regset *regset)
+{
+       struct fpu *target_fpu = &target->thread.fpu;
+
+       return target_fpu->fpstate_active ? regset->n : 0;
+}
+
+int regset_xregset_fpregs_active(struct task_struct *target, const struct user_regset *regset)
+{
+       struct fpu *target_fpu = &target->thread.fpu;
+
+       return (cpu_has_fxsr && target_fpu->fpstate_active) ? regset->n : 0;
+}
+
+int xfpregs_get(struct task_struct *target, const struct user_regset *regset,
+               unsigned int pos, unsigned int count,
+               void *kbuf, void __user *ubuf)
+{
+       struct fpu *fpu = &target->thread.fpu;
+
+       if (!cpu_has_fxsr)
+               return -ENODEV;
+
+       fpu__activate_fpstate_read(fpu);
+       fpstate_sanitize_xstate(fpu);
+
+       return user_regset_copyout(&pos, &count, &kbuf, &ubuf,
+                                  &fpu->state.fxsave, 0, -1);
+}
+
+int xfpregs_set(struct task_struct *target, const struct user_regset *regset,
+               unsigned int pos, unsigned int count,
+               const void *kbuf, const void __user *ubuf)
+{
+       struct fpu *fpu = &target->thread.fpu;
+       int ret;
+
+       if (!cpu_has_fxsr)
+               return -ENODEV;
+
+       fpu__activate_fpstate_write(fpu);
+       fpstate_sanitize_xstate(fpu);
+
+       ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
+                                &fpu->state.fxsave, 0, -1);
+
+       /*
+        * mxcsr reserved bits must be masked to zero for security reasons.
+        */
+       fpu->state.fxsave.mxcsr &= mxcsr_feature_mask;
+
+       /*
+        * update the header bits in the xsave header, indicating the
+        * presence of FP and SSE state.
+        */
+       if (cpu_has_xsave)
+               fpu->state.xsave.header.xfeatures |= XSTATE_FPSSE;
+
+       return ret;
+}
+
+int xstateregs_get(struct task_struct *target, const struct user_regset *regset,
+               unsigned int pos, unsigned int count,
+               void *kbuf, void __user *ubuf)
+{
+       struct fpu *fpu = &target->thread.fpu;
+       struct xregs_state *xsave;
+       int ret;
+
+       if (!cpu_has_xsave)
+               return -ENODEV;
+
+       fpu__activate_fpstate_read(fpu);
+
+       xsave = &fpu->state.xsave;
+
+       /*
+        * Copy the 48bytes defined by the software first into the xstate
+        * memory layout in the thread struct, so that we can copy the entire
+        * xstateregs to the user using one user_regset_copyout().
+        */
+       memcpy(&xsave->i387.sw_reserved,
+               xstate_fx_sw_bytes, sizeof(xstate_fx_sw_bytes));
+       /*
+        * Copy the xstate memory layout.
+        */
+       ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, xsave, 0, -1);
+       return ret;
+}
+
+int xstateregs_set(struct task_struct *target, const struct user_regset *regset,
+                 unsigned int pos, unsigned int count,
+                 const void *kbuf, const void __user *ubuf)
+{
+       struct fpu *fpu = &target->thread.fpu;
+       struct xregs_state *xsave;
+       int ret;
+
+       if (!cpu_has_xsave)
+               return -ENODEV;
+
+       fpu__activate_fpstate_write(fpu);
+
+       xsave = &fpu->state.xsave;
+
+       ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, xsave, 0, -1);
+       /*
+        * mxcsr reserved bits must be masked to zero for security reasons.
+        */
+       xsave->i387.mxcsr &= mxcsr_feature_mask;
+       xsave->header.xfeatures &= xfeatures_mask;
+       /*
+        * These bits must be zero.
+        */
+       memset(&xsave->header.reserved, 0, 48);
+
+       return ret;
+}
+
+#if defined CONFIG_X86_32 || defined CONFIG_IA32_EMULATION
+
+/*
+ * FPU tag word conversions.
+ */
+
+static inline unsigned short twd_i387_to_fxsr(unsigned short twd)
+{
+       unsigned int tmp; /* to avoid 16 bit prefixes in the code */
+
+       /* Transform each pair of bits into 01 (valid) or 00 (empty) */
+       tmp = ~twd;
+       tmp = (tmp | (tmp>>1)) & 0x5555; /* 0V0V0V0V0V0V0V0V */
+       /* and move the valid bits to the lower byte. */
+       tmp = (tmp | (tmp >> 1)) & 0x3333; /* 00VV00VV00VV00VV */
+       tmp = (tmp | (tmp >> 2)) & 0x0f0f; /* 0000VVVV0000VVVV */
+       tmp = (tmp | (tmp >> 4)) & 0x00ff; /* 00000000VVVVVVVV */
+
+       return tmp;
+}
+
+#define FPREG_ADDR(f, n)       ((void *)&(f)->st_space + (n) * 16)
+#define FP_EXP_TAG_VALID       0
+#define FP_EXP_TAG_ZERO                1
+#define FP_EXP_TAG_SPECIAL     2
+#define FP_EXP_TAG_EMPTY       3
+
+static inline u32 twd_fxsr_to_i387(struct fxregs_state *fxsave)
+{
+       struct _fpxreg *st;
+       u32 tos = (fxsave->swd >> 11) & 7;
+       u32 twd = (unsigned long) fxsave->twd;
+       u32 tag;
+       u32 ret = 0xffff0000u;
+       int i;
+
+       for (i = 0; i < 8; i++, twd >>= 1) {
+               if (twd & 0x1) {
+                       st = FPREG_ADDR(fxsave, (i - tos) & 7);
+
+                       switch (st->exponent & 0x7fff) {
+                       case 0x7fff:
+                               tag = FP_EXP_TAG_SPECIAL;
+                               break;
+                       case 0x0000:
+                               if (!st->significand[0] &&
+                                   !st->significand[1] &&
+                                   !st->significand[2] &&
+                                   !st->significand[3])
+                                       tag = FP_EXP_TAG_ZERO;
+                               else
+                                       tag = FP_EXP_TAG_SPECIAL;
+                               break;
+                       default:
+                               if (st->significand[3] & 0x8000)
+                                       tag = FP_EXP_TAG_VALID;
+                               else
+                                       tag = FP_EXP_TAG_SPECIAL;
+                               break;
+                       }
+               } else {
+                       tag = FP_EXP_TAG_EMPTY;
+               }
+               ret |= tag << (2 * i);
+       }
+       return ret;
+}
+
+/*
+ * FXSR floating point environment conversions.
+ */
+
+void
+convert_from_fxsr(struct user_i387_ia32_struct *env, struct task_struct *tsk)
+{
+       struct fxregs_state *fxsave = &tsk->thread.fpu.state.fxsave;
+       struct _fpreg *to = (struct _fpreg *) &env->st_space[0];
+       struct _fpxreg *from = (struct _fpxreg *) &fxsave->st_space[0];
+       int i;
+
+       env->cwd = fxsave->cwd | 0xffff0000u;
+       env->swd = fxsave->swd | 0xffff0000u;
+       env->twd = twd_fxsr_to_i387(fxsave);
+
+#ifdef CONFIG_X86_64
+       env->fip = fxsave->rip;
+       env->foo = fxsave->rdp;
+       /*
+        * should be actually ds/cs at fpu exception time, but
+        * that information is not available in 64bit mode.
+        */
+       env->fcs = task_pt_regs(tsk)->cs;
+       if (tsk == current) {
+               savesegment(ds, env->fos);
+       } else {
+               env->fos = tsk->thread.ds;
+       }
+       env->fos |= 0xffff0000;
+#else
+       env->fip = fxsave->fip;
+       env->fcs = (u16) fxsave->fcs | ((u32) fxsave->fop << 16);
+       env->foo = fxsave->foo;
+       env->fos = fxsave->fos;
+#endif
+
+       for (i = 0; i < 8; ++i)
+               memcpy(&to[i], &from[i], sizeof(to[0]));
+}
+
+void convert_to_fxsr(struct task_struct *tsk,
+                    const struct user_i387_ia32_struct *env)
+
+{
+       struct fxregs_state *fxsave = &tsk->thread.fpu.state.fxsave;
+       struct _fpreg *from = (struct _fpreg *) &env->st_space[0];
+       struct _fpxreg *to = (struct _fpxreg *) &fxsave->st_space[0];
+       int i;
+
+       fxsave->cwd = env->cwd;
+       fxsave->swd = env->swd;
+       fxsave->twd = twd_i387_to_fxsr(env->twd);
+       fxsave->fop = (u16) ((u32) env->fcs >> 16);
+#ifdef CONFIG_X86_64
+       fxsave->rip = env->fip;
+       fxsave->rdp = env->foo;
+       /* cs and ds ignored */
+#else
+       fxsave->fip = env->fip;
+       fxsave->fcs = (env->fcs & 0xffff);
+       fxsave->foo = env->foo;
+       fxsave->fos = env->fos;
+#endif
+
+       for (i = 0; i < 8; ++i)
+               memcpy(&to[i], &from[i], sizeof(from[0]));
+}
+
+int fpregs_get(struct task_struct *target, const struct user_regset *regset,
+              unsigned int pos, unsigned int count,
+              void *kbuf, void __user *ubuf)
+{
+       struct fpu *fpu = &target->thread.fpu;
+       struct user_i387_ia32_struct env;
+
+       fpu__activate_fpstate_read(fpu);
+
+       if (!static_cpu_has(X86_FEATURE_FPU))
+               return fpregs_soft_get(target, regset, pos, count, kbuf, ubuf);
+
+       if (!cpu_has_fxsr)
+               return user_regset_copyout(&pos, &count, &kbuf, &ubuf,
+                                          &fpu->state.fsave, 0,
+                                          -1);
+
+       fpstate_sanitize_xstate(fpu);
+
+       if (kbuf && pos == 0 && count == sizeof(env)) {
+               convert_from_fxsr(kbuf, target);
+               return 0;
+       }
+
+       convert_from_fxsr(&env, target);
+
+       return user_regset_copyout(&pos, &count, &kbuf, &ubuf, &env, 0, -1);
+}
+
+int fpregs_set(struct task_struct *target, const struct user_regset *regset,
+              unsigned int pos, unsigned int count,
+              const void *kbuf, const void __user *ubuf)
+{
+       struct fpu *fpu = &target->thread.fpu;
+       struct user_i387_ia32_struct env;
+       int ret;
+
+       fpu__activate_fpstate_write(fpu);
+       fpstate_sanitize_xstate(fpu);
+
+       if (!static_cpu_has(X86_FEATURE_FPU))
+               return fpregs_soft_set(target, regset, pos, count, kbuf, ubuf);
+
+       if (!cpu_has_fxsr)
+               return user_regset_copyin(&pos, &count, &kbuf, &ubuf,
+                                         &fpu->state.fsave, 0,
+                                         -1);
+
+       if (pos > 0 || count < sizeof(env))
+               convert_from_fxsr(&env, target);
+
+       ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &env, 0, -1);
+       if (!ret)
+               convert_to_fxsr(target, &env);
+
+       /*
+        * update the header bit in the xsave header, indicating the
+        * presence of FP.
+        */
+       if (cpu_has_xsave)
+               fpu->state.xsave.header.xfeatures |= XSTATE_FP;
+       return ret;
+}
+
+/*
+ * FPU state for core dumps.
+ * This is only used for a.out dumps now.
+ * It is declared generically using elf_fpregset_t (which is
+ * struct user_i387_struct) but is in fact only used for 32-bit
+ * dumps, so on 64-bit it is really struct user_i387_ia32_struct.
+ */
+int dump_fpu(struct pt_regs *regs, struct user_i387_struct *ufpu)
+{
+       struct task_struct *tsk = current;
+       struct fpu *fpu = &tsk->thread.fpu;
+       int fpvalid;
+
+       fpvalid = fpu->fpstate_active;
+       if (fpvalid)
+               fpvalid = !fpregs_get(tsk, NULL,
+                                     0, sizeof(struct user_i387_ia32_struct),
+                                     ufpu, NULL);
+
+       return fpvalid;
+}
+EXPORT_SYMBOL(dump_fpu);
+
+#endif /* CONFIG_X86_32 || CONFIG_IA32_EMULATION */
diff --git a/arch/x86/kernel/fpu/signal.c b/arch/x86/kernel/fpu/signal.c
new file mode 100644 (file)
index 0000000..50ec9af
--- /dev/null
@@ -0,0 +1,404 @@
+/*
+ * FPU signal frame handling routines.
+ */
+
+#include <linux/compat.h>
+#include <linux/cpu.h>
+
+#include <asm/fpu/internal.h>
+#include <asm/fpu/signal.h>
+#include <asm/fpu/regset.h>
+
+#include <asm/sigframe.h>
+
+static struct _fpx_sw_bytes fx_sw_reserved, fx_sw_reserved_ia32;
+
+/*
+ * Check for the presence of extended state information in the
+ * user fpstate pointer in the sigcontext.
+ */
+static inline int check_for_xstate(struct fxregs_state __user *buf,
+                                  void __user *fpstate,
+                                  struct _fpx_sw_bytes *fx_sw)
+{
+       int min_xstate_size = sizeof(struct fxregs_state) +
+                             sizeof(struct xstate_header);
+       unsigned int magic2;
+
+       if (__copy_from_user(fx_sw, &buf->sw_reserved[0], sizeof(*fx_sw)))
+               return -1;
+
+       /* Check for the first magic field and other error scenarios. */
+       if (fx_sw->magic1 != FP_XSTATE_MAGIC1 ||
+           fx_sw->xstate_size < min_xstate_size ||
+           fx_sw->xstate_size > xstate_size ||
+           fx_sw->xstate_size > fx_sw->extended_size)
+               return -1;
+
+       /*
+        * Check for the presence of second magic word at the end of memory
+        * layout. This detects the case where the user just copied the legacy
+        * fpstate layout with out copying the extended state information
+        * in the memory layout.
+        */
+       if (__get_user(magic2, (__u32 __user *)(fpstate + fx_sw->xstate_size))
+           || magic2 != FP_XSTATE_MAGIC2)
+               return -1;
+
+       return 0;
+}
+
+/*
+ * Signal frame handlers.
+ */
+static inline int save_fsave_header(struct task_struct *tsk, void __user *buf)
+{
+       if (use_fxsr()) {
+               struct xregs_state *xsave = &tsk->thread.fpu.state.xsave;
+               struct user_i387_ia32_struct env;
+               struct _fpstate_ia32 __user *fp = buf;
+
+               convert_from_fxsr(&env, tsk);
+
+               if (__copy_to_user(buf, &env, sizeof(env)) ||
+                   __put_user(xsave->i387.swd, &fp->status) ||
+                   __put_user(X86_FXSR_MAGIC, &fp->magic))
+                       return -1;
+       } else {
+               struct fregs_state __user *fp = buf;
+               u32 swd;
+               if (__get_user(swd, &fp->swd) || __put_user(swd, &fp->status))
+                       return -1;
+       }
+
+       return 0;
+}
+
+static inline int save_xstate_epilog(void __user *buf, int ia32_frame)
+{
+       struct xregs_state __user *x = buf;
+       struct _fpx_sw_bytes *sw_bytes;
+       u32 xfeatures;
+       int err;
+
+       /* Setup the bytes not touched by the [f]xsave and reserved for SW. */
+       sw_bytes = ia32_frame ? &fx_sw_reserved_ia32 : &fx_sw_reserved;
+       err = __copy_to_user(&x->i387.sw_reserved, sw_bytes, sizeof(*sw_bytes));
+
+       if (!use_xsave())
+               return err;
+
+       err |= __put_user(FP_XSTATE_MAGIC2, (__u32 *)(buf + xstate_size));
+
+       /*
+        * Read the xfeatures which we copied (directly from the cpu or
+        * from the state in task struct) to the user buffers.
+        */
+       err |= __get_user(xfeatures, (__u32 *)&x->header.xfeatures);
+
+       /*
+        * For legacy compatible, we always set FP/SSE bits in the bit
+        * vector while saving the state to the user context. This will
+        * enable us capturing any changes(during sigreturn) to
+        * the FP/SSE bits by the legacy applications which don't touch
+        * xfeatures in the xsave header.
+        *
+        * xsave aware apps can change the xfeatures in the xsave
+        * header as well as change any contents in the memory layout.
+        * xrestore as part of sigreturn will capture all the changes.
+        */
+       xfeatures |= XSTATE_FPSSE;
+
+       err |= __put_user(xfeatures, (__u32 *)&x->header.xfeatures);
+
+       return err;
+}
+
+static inline int copy_fpregs_to_sigframe(struct xregs_state __user *buf)
+{
+       int err;
+
+       if (use_xsave())
+               err = copy_xregs_to_user(buf);
+       else if (use_fxsr())
+               err = copy_fxregs_to_user((struct fxregs_state __user *) buf);
+       else
+               err = copy_fregs_to_user((struct fregs_state __user *) buf);
+
+       if (unlikely(err) && __clear_user(buf, xstate_size))
+               err = -EFAULT;
+       return err;
+}
+
+/*
+ * Save the fpu, extended register state to the user signal frame.
+ *
+ * 'buf_fx' is the 64-byte aligned pointer at which the [f|fx|x]save
+ *  state is copied.
+ *  'buf' points to the 'buf_fx' or to the fsave header followed by 'buf_fx'.
+ *
+ *     buf == buf_fx for 64-bit frames and 32-bit fsave frame.
+ *     buf != buf_fx for 32-bit frames with fxstate.
+ *
+ * If the fpu, extended register state is live, save the state directly
+ * to the user frame pointed by the aligned pointer 'buf_fx'. Otherwise,
+ * copy the thread's fpu state to the user frame starting at 'buf_fx'.
+ *
+ * If this is a 32-bit frame with fxstate, put a fsave header before
+ * the aligned state at 'buf_fx'.
+ *
+ * For [f]xsave state, update the SW reserved fields in the [f]xsave frame
+ * indicating the absence/presence of the extended state to the user.
+ */
+int copy_fpstate_to_sigframe(void __user *buf, void __user *buf_fx, int size)
+{
+       struct xregs_state *xsave = &current->thread.fpu.state.xsave;
+       struct task_struct *tsk = current;
+       int ia32_fxstate = (buf != buf_fx);
+
+       ia32_fxstate &= (config_enabled(CONFIG_X86_32) ||
+                        config_enabled(CONFIG_IA32_EMULATION));
+
+       if (!access_ok(VERIFY_WRITE, buf, size))
+               return -EACCES;
+
+       if (!static_cpu_has(X86_FEATURE_FPU))
+               return fpregs_soft_get(current, NULL, 0,
+                       sizeof(struct user_i387_ia32_struct), NULL,
+                       (struct _fpstate_ia32 __user *) buf) ? -1 : 1;
+
+       if (fpregs_active()) {
+               /* Save the live register state to the user directly. */
+               if (copy_fpregs_to_sigframe(buf_fx))
+                       return -1;
+               /* Update the thread's fxstate to save the fsave header. */
+               if (ia32_fxstate)
+                       copy_fxregs_to_kernel(&tsk->thread.fpu);
+       } else {
+               fpstate_sanitize_xstate(&tsk->thread.fpu);
+               if (__copy_to_user(buf_fx, xsave, xstate_size))
+                       return -1;
+       }
+
+       /* Save the fsave header for the 32-bit frames. */
+       if ((ia32_fxstate || !use_fxsr()) && save_fsave_header(tsk, buf))
+               return -1;
+
+       if (use_fxsr() && save_xstate_epilog(buf_fx, ia32_fxstate))
+               return -1;
+
+       return 0;
+}
+
+static inline void
+sanitize_restored_xstate(struct task_struct *tsk,
+                        struct user_i387_ia32_struct *ia32_env,
+                        u64 xfeatures, int fx_only)
+{
+       struct xregs_state *xsave = &tsk->thread.fpu.state.xsave;
+       struct xstate_header *header = &xsave->header;
+
+       if (use_xsave()) {
+               /* These bits must be zero. */
+               memset(header->reserved, 0, 48);
+
+               /*
+                * Init the state that is not present in the memory
+                * layout and not enabled by the OS.
+                */
+               if (fx_only)
+                       header->xfeatures = XSTATE_FPSSE;
+               else
+                       header->xfeatures &= (xfeatures_mask & xfeatures);
+       }
+
+       if (use_fxsr()) {
+               /*
+                * mscsr reserved bits must be masked to zero for security
+                * reasons.
+                */
+               xsave->i387.mxcsr &= mxcsr_feature_mask;
+
+               convert_to_fxsr(tsk, ia32_env);
+       }
+}
+
+/*
+ * Restore the extended state if present. Otherwise, restore the FP/SSE state.
+ */
+static inline int copy_user_to_fpregs_zeroing(void __user *buf, u64 xbv, int fx_only)
+{
+       if (use_xsave()) {
+               if ((unsigned long)buf % 64 || fx_only) {
+                       u64 init_bv = xfeatures_mask & ~XSTATE_FPSSE;
+                       copy_kernel_to_xregs(&init_fpstate.xsave, init_bv);
+                       return copy_user_to_fxregs(buf);
+               } else {
+                       u64 init_bv = xfeatures_mask & ~xbv;
+                       if (unlikely(init_bv))
+                               copy_kernel_to_xregs(&init_fpstate.xsave, init_bv);
+                       return copy_user_to_xregs(buf, xbv);
+               }
+       } else if (use_fxsr()) {
+               return copy_user_to_fxregs(buf);
+       } else
+               return copy_user_to_fregs(buf);
+}
+
+static int __fpu__restore_sig(void __user *buf, void __user *buf_fx, int size)
+{
+       int ia32_fxstate = (buf != buf_fx);
+       struct task_struct *tsk = current;
+       struct fpu *fpu = &tsk->thread.fpu;
+       int state_size = xstate_size;
+       u64 xfeatures = 0;
+       int fx_only = 0;
+
+       ia32_fxstate &= (config_enabled(CONFIG_X86_32) ||
+                        config_enabled(CONFIG_IA32_EMULATION));
+
+       if (!buf) {
+               fpu__clear(fpu);
+               return 0;
+       }
+
+       if (!access_ok(VERIFY_READ, buf, size))
+               return -EACCES;
+
+       fpu__activate_curr(fpu);
+
+       if (!static_cpu_has(X86_FEATURE_FPU))
+               return fpregs_soft_set(current, NULL,
+                                      0, sizeof(struct user_i387_ia32_struct),
+                                      NULL, buf) != 0;
+
+       if (use_xsave()) {
+               struct _fpx_sw_bytes fx_sw_user;
+               if (unlikely(check_for_xstate(buf_fx, buf_fx, &fx_sw_user))) {
+                       /*
+                        * Couldn't find the extended state information in the
+                        * memory layout. Restore just the FP/SSE and init all
+                        * the other extended state.
+                        */
+                       state_size = sizeof(struct fxregs_state);
+                       fx_only = 1;
+               } else {
+                       state_size = fx_sw_user.xstate_size;
+                       xfeatures = fx_sw_user.xfeatures;
+               }
+       }
+
+       if (ia32_fxstate) {
+               /*
+                * For 32-bit frames with fxstate, copy the user state to the
+                * thread's fpu state, reconstruct fxstate from the fsave
+                * header. Sanitize the copied state etc.
+                */
+               struct fpu *fpu = &tsk->thread.fpu;
+               struct user_i387_ia32_struct env;
+               int err = 0;
+
+               /*
+                * Drop the current fpu which clears fpu->fpstate_active. This ensures
+                * that any context-switch during the copy of the new state,
+                * avoids the intermediate state from getting restored/saved.
+                * Thus avoiding the new restored state from getting corrupted.
+                * We will be ready to restore/save the state only after
+                * fpu->fpstate_active is again set.
+                */
+               fpu__drop(fpu);
+
+               if (__copy_from_user(&fpu->state.xsave, buf_fx, state_size) ||
+                   __copy_from_user(&env, buf, sizeof(env))) {
+                       fpstate_init(&fpu->state);
+                       err = -1;
+               } else {
+                       sanitize_restored_xstate(tsk, &env, xfeatures, fx_only);
+               }
+
+               fpu->fpstate_active = 1;
+               if (use_eager_fpu()) {
+                       preempt_disable();
+                       fpu__restore(fpu);
+                       preempt_enable();
+               }
+
+               return err;
+       } else {
+               /*
+                * For 64-bit frames and 32-bit fsave frames, restore the user
+                * state to the registers directly (with exceptions handled).
+                */
+               user_fpu_begin();
+               if (copy_user_to_fpregs_zeroing(buf_fx, xfeatures, fx_only)) {
+                       fpu__clear(fpu);
+                       return -1;
+               }
+       }
+
+       return 0;
+}
+
+static inline int xstate_sigframe_size(void)
+{
+       return use_xsave() ? xstate_size + FP_XSTATE_MAGIC2_SIZE : xstate_size;
+}
+
+/*
+ * Restore FPU state from a sigframe:
+ */
+int fpu__restore_sig(void __user *buf, int ia32_frame)
+{
+       void __user *buf_fx = buf;
+       int size = xstate_sigframe_size();
+
+       if (ia32_frame && use_fxsr()) {
+               buf_fx = buf + sizeof(struct fregs_state);
+               size += sizeof(struct fregs_state);
+       }
+
+       return __fpu__restore_sig(buf, buf_fx, size);
+}
+
+unsigned long
+fpu__alloc_mathframe(unsigned long sp, int ia32_frame,
+                    unsigned long *buf_fx, unsigned long *size)
+{
+       unsigned long frame_size = xstate_sigframe_size();
+
+       *buf_fx = sp = round_down(sp - frame_size, 64);
+       if (ia32_frame && use_fxsr()) {
+               frame_size += sizeof(struct fregs_state);
+               sp -= sizeof(struct fregs_state);
+       }
+
+       *size = frame_size;
+
+       return sp;
+}
+/*
+ * Prepare the SW reserved portion of the fxsave memory layout, indicating
+ * the presence of the extended state information in the memory layout
+ * pointed by the fpstate pointer in the sigcontext.
+ * This will be saved when ever the FP and extended state context is
+ * saved on the user stack during the signal handler delivery to the user.
+ */
+void fpu__init_prepare_fx_sw_frame(void)
+{
+       int fsave_header_size = sizeof(struct fregs_state);
+       int size = xstate_size + FP_XSTATE_MAGIC2_SIZE;
+
+       if (config_enabled(CONFIG_X86_32))
+               size += fsave_header_size;
+
+       fx_sw_reserved.magic1 = FP_XSTATE_MAGIC1;
+       fx_sw_reserved.extended_size = size;
+       fx_sw_reserved.xfeatures = xfeatures_mask;
+       fx_sw_reserved.xstate_size = xstate_size;
+
+       if (config_enabled(CONFIG_IA32_EMULATION)) {
+               fx_sw_reserved_ia32 = fx_sw_reserved;
+               fx_sw_reserved_ia32.extended_size += fsave_header_size;
+       }
+}
+
diff --git a/arch/x86/kernel/fpu/xstate.c b/arch/x86/kernel/fpu/xstate.c
new file mode 100644 (file)
index 0000000..62fc001
--- /dev/null
@@ -0,0 +1,461 @@
+/*
+ * xsave/xrstor support.
+ *
+ * Author: Suresh Siddha <suresh.b.siddha@intel.com>
+ */
+#include <linux/compat.h>
+#include <linux/cpu.h>
+
+#include <asm/fpu/api.h>
+#include <asm/fpu/internal.h>
+#include <asm/fpu/signal.h>
+#include <asm/fpu/regset.h>
+
+#include <asm/tlbflush.h>
+
+static const char *xfeature_names[] =
+{
+       "x87 floating point registers"  ,
+       "SSE registers"                 ,
+       "AVX registers"                 ,
+       "MPX bounds registers"          ,
+       "MPX CSR"                       ,
+       "AVX-512 opmask"                ,
+       "AVX-512 Hi256"                 ,
+       "AVX-512 ZMM_Hi256"             ,
+       "unknown xstate feature"        ,
+};
+
+/*
+ * Mask of xstate features supported by the CPU and the kernel:
+ */
+u64 xfeatures_mask __read_mostly;
+
+static unsigned int xstate_offsets[XFEATURES_NR_MAX] = { [ 0 ... XFEATURES_NR_MAX - 1] = -1};
+static unsigned int xstate_sizes[XFEATURES_NR_MAX]   = { [ 0 ... XFEATURES_NR_MAX - 1] = -1};
+static unsigned int xstate_comp_offsets[sizeof(xfeatures_mask)*8];
+
+/* The number of supported xfeatures in xfeatures_mask: */
+static unsigned int xfeatures_nr;
+
+/*
+ * Return whether the system supports a given xfeature.
+ *
+ * Also return the name of the (most advanced) feature that the caller requested:
+ */
+int cpu_has_xfeatures(u64 xfeatures_needed, const char **feature_name)
+{
+       u64 xfeatures_missing = xfeatures_needed & ~xfeatures_mask;
+
+       if (unlikely(feature_name)) {
+               long xfeature_idx, max_idx;
+               u64 xfeatures_print;
+               /*
+                * So we use FLS here to be able to print the most advanced
+                * feature that was requested but is missing. So if a driver
+                * asks about "XSTATE_SSE | XSTATE_YMM" we'll print the
+                * missing AVX feature - this is the most informative message
+                * to users:
+                */
+               if (xfeatures_missing)
+                       xfeatures_print = xfeatures_missing;
+               else
+                       xfeatures_print = xfeatures_needed;
+
+               xfeature_idx = fls64(xfeatures_print)-1;
+               max_idx = ARRAY_SIZE(xfeature_names)-1;
+               xfeature_idx = min(xfeature_idx, max_idx);
+
+               *feature_name = xfeature_names[xfeature_idx];
+       }
+
+       if (xfeatures_missing)
+               return 0;
+
+       return 1;
+}
+EXPORT_SYMBOL_GPL(cpu_has_xfeatures);
+
+/*
+ * When executing XSAVEOPT (or other optimized XSAVE instructions), if
+ * a processor implementation detects that an FPU state component is still
+ * (or is again) in its initialized state, it may clear the corresponding
+ * bit in the header.xfeatures field, and can skip the writeout of registers
+ * to the corresponding memory layout.
+ *
+ * This means that when the bit is zero, the state component might still contain
+ * some previous - non-initialized register state.
+ *
+ * Before writing xstate information to user-space we sanitize those components,
+ * to always ensure that the memory layout of a feature will be in the init state
+ * if the corresponding header bit is zero. This is to ensure that user-space doesn't
+ * see some stale state in the memory layout during signal handling, debugging etc.
+ */
+void fpstate_sanitize_xstate(struct fpu *fpu)
+{
+       struct fxregs_state *fx = &fpu->state.fxsave;
+       int feature_bit;
+       u64 xfeatures;
+
+       if (!use_xsaveopt())
+               return;
+
+       xfeatures = fpu->state.xsave.header.xfeatures;
+
+       /*
+        * None of the feature bits are in init state. So nothing else
+        * to do for us, as the memory layout is up to date.
+        */
+       if ((xfeatures & xfeatures_mask) == xfeatures_mask)
+               return;
+
+       /*
+        * FP is in init state
+        */
+       if (!(xfeatures & XSTATE_FP)) {
+               fx->cwd = 0x37f;
+               fx->swd = 0;
+               fx->twd = 0;
+               fx->fop = 0;
+               fx->rip = 0;
+               fx->rdp = 0;
+               memset(&fx->st_space[0], 0, 128);
+       }
+
+       /*
+        * SSE is in init state
+        */
+       if (!(xfeatures & XSTATE_SSE))
+               memset(&fx->xmm_space[0], 0, 256);
+
+       /*
+        * First two features are FPU and SSE, which above we handled
+        * in a special way already:
+        */
+       feature_bit = 0x2;
+       xfeatures = (xfeatures_mask & ~xfeatures) >> 2;
+
+       /*
+        * Update all the remaining memory layouts according to their
+        * standard xstate layout, if their header bit is in the init
+        * state:
+        */
+       while (xfeatures) {
+               if (xfeatures & 0x1) {
+                       int offset = xstate_offsets[feature_bit];
+                       int size = xstate_sizes[feature_bit];
+
+                       memcpy((void *)fx + offset,
+                              (void *)&init_fpstate.xsave + offset,
+                              size);
+               }
+
+               xfeatures >>= 1;
+               feature_bit++;
+       }
+}
+
+/*
+ * Enable the extended processor state save/restore feature.
+ * Called once per CPU onlining.
+ */
+void fpu__init_cpu_xstate(void)
+{
+       if (!cpu_has_xsave || !xfeatures_mask)
+               return;
+
+       cr4_set_bits(X86_CR4_OSXSAVE);
+       xsetbv(XCR_XFEATURE_ENABLED_MASK, xfeatures_mask);
+}
+
+/*
+ * Record the offsets and sizes of various xstates contained
+ * in the XSAVE state memory layout.
+ *
+ * ( Note that certain features might be non-present, for them
+ *   we'll have 0 offset and 0 size. )
+ */
+static void __init setup_xstate_features(void)
+{
+       u32 eax, ebx, ecx, edx, leaf;
+
+       xfeatures_nr = fls64(xfeatures_mask);
+
+       for (leaf = 2; leaf < xfeatures_nr; leaf++) {
+               cpuid_count(XSTATE_CPUID, leaf, &eax, &ebx, &ecx, &edx);
+
+               xstate_offsets[leaf] = ebx;
+               xstate_sizes[leaf] = eax;
+
+               printk(KERN_INFO "x86/fpu: xstate_offset[%d]: %04x, xstate_sizes[%d]: %04x\n", leaf, ebx, leaf, eax);
+       }
+}
+
+static void __init print_xstate_feature(u64 xstate_mask)
+{
+       const char *feature_name;
+
+       if (cpu_has_xfeatures(xstate_mask, &feature_name))
+               pr_info("x86/fpu: Supporting XSAVE feature 0x%02Lx: '%s'\n", xstate_mask, feature_name);
+}
+
+/*
+ * Print out all the supported xstate features:
+ */
+static void __init print_xstate_features(void)
+{
+       print_xstate_feature(XSTATE_FP);
+       print_xstate_feature(XSTATE_SSE);
+       print_xstate_feature(XSTATE_YMM);
+       print_xstate_feature(XSTATE_BNDREGS);
+       print_xstate_feature(XSTATE_BNDCSR);
+       print_xstate_feature(XSTATE_OPMASK);
+       print_xstate_feature(XSTATE_ZMM_Hi256);
+       print_xstate_feature(XSTATE_Hi16_ZMM);
+}
+
+/*
+ * This function sets up offsets and sizes of all extended states in
+ * xsave area. This supports both standard format and compacted format
+ * of the xsave aread.
+ */
+static void __init setup_xstate_comp(void)
+{
+       unsigned int xstate_comp_sizes[sizeof(xfeatures_mask)*8];
+       int i;
+
+       /*
+        * The FP xstates and SSE xstates are legacy states. They are always
+        * in the fixed offsets in the xsave area in either compacted form
+        * or standard form.
+        */
+       xstate_comp_offsets[0] = 0;
+       xstate_comp_offsets[1] = offsetof(struct fxregs_state, xmm_space);
+
+       if (!cpu_has_xsaves) {
+               for (i = 2; i < xfeatures_nr; i++) {
+                       if (test_bit(i, (unsigned long *)&xfeatures_mask)) {
+                               xstate_comp_offsets[i] = xstate_offsets[i];
+                               xstate_comp_sizes[i] = xstate_sizes[i];
+                       }
+               }
+               return;
+       }
+
+       xstate_comp_offsets[2] = FXSAVE_SIZE + XSAVE_HDR_SIZE;
+
+       for (i = 2; i < xfeatures_nr; i++) {
+               if (test_bit(i, (unsigned long *)&xfeatures_mask))
+                       xstate_comp_sizes[i] = xstate_sizes[i];
+               else
+                       xstate_comp_sizes[i] = 0;
+
+               if (i > 2)
+                       xstate_comp_offsets[i] = xstate_comp_offsets[i-1]
+                                       + xstate_comp_sizes[i-1];
+
+       }
+}
+
+/*
+ * setup the xstate image representing the init state
+ */
+static void __init setup_init_fpu_buf(void)
+{
+       static int on_boot_cpu = 1;
+
+       WARN_ON_FPU(!on_boot_cpu);
+       on_boot_cpu = 0;
+
+       if (!cpu_has_xsave)
+               return;
+
+       setup_xstate_features();
+       print_xstate_features();
+
+       if (cpu_has_xsaves) {
+               init_fpstate.xsave.header.xcomp_bv = (u64)1 << 63 | xfeatures_mask;
+               init_fpstate.xsave.header.xfeatures = xfeatures_mask;
+       }
+
+       /*
+        * Init all the features state with header_bv being 0x0
+        */
+       copy_kernel_to_xregs_booting(&init_fpstate.xsave);
+
+       /*
+        * Dump the init state again. This is to identify the init state
+        * of any feature which is not represented by all zero's.
+        */
+       copy_xregs_to_kernel_booting(&init_fpstate.xsave);
+}
+
+/*
+ * Calculate total size of enabled xstates in XCR0/xfeatures_mask.
+ */
+static void __init init_xstate_size(void)
+{
+       unsigned int eax, ebx, ecx, edx;
+       int i;
+
+       if (!cpu_has_xsaves) {
+               cpuid_count(XSTATE_CPUID, 0, &eax, &ebx, &ecx, &edx);
+               xstate_size = ebx;
+               return;
+       }
+
+       xstate_size = FXSAVE_SIZE + XSAVE_HDR_SIZE;
+       for (i = 2; i < 64; i++) {
+               if (test_bit(i, (unsigned long *)&xfeatures_mask)) {
+                       cpuid_count(XSTATE_CPUID, i, &eax, &ebx, &ecx, &edx);
+                       xstate_size += eax;
+               }
+       }
+}
+
+/*
+ * Enable and initialize the xsave feature.
+ * Called once per system bootup.
+ */
+void __init fpu__init_system_xstate(void)
+{
+       unsigned int eax, ebx, ecx, edx;
+       static int on_boot_cpu = 1;
+
+       WARN_ON_FPU(!on_boot_cpu);
+       on_boot_cpu = 0;
+
+       if (!cpu_has_xsave) {
+               pr_info("x86/fpu: Legacy x87 FPU detected.\n");
+               return;
+       }
+
+       if (boot_cpu_data.cpuid_level < XSTATE_CPUID) {
+               WARN_ON_FPU(1);
+               return;
+       }
+
+       cpuid_count(XSTATE_CPUID, 0, &eax, &ebx, &ecx, &edx);
+       xfeatures_mask = eax + ((u64)edx << 32);
+
+       if ((xfeatures_mask & XSTATE_FPSSE) != XSTATE_FPSSE) {
+               pr_err("x86/fpu: FP/SSE not present amongst the CPU's xstate features: 0x%llx.\n", xfeatures_mask);
+               BUG();
+       }
+
+       /* Support only the state known to the OS: */
+       xfeatures_mask = xfeatures_mask & XCNTXT_MASK;
+
+       /* Enable xstate instructions to be able to continue with initialization: */
+       fpu__init_cpu_xstate();
+
+       /* Recompute the context size for enabled features: */
+       init_xstate_size();
+
+       update_regset_xstate_info(xstate_size, xfeatures_mask);
+       fpu__init_prepare_fx_sw_frame();
+       setup_init_fpu_buf();
+       setup_xstate_comp();
+
+       pr_info("x86/fpu: Enabled xstate features 0x%llx, context size is 0x%x bytes, using '%s' format.\n",
+               xfeatures_mask,
+               xstate_size,
+               cpu_has_xsaves ? "compacted" : "standard");
+}
+
+/*
+ * Restore minimal FPU state after suspend:
+ */
+void fpu__resume_cpu(void)
+{
+       /*
+        * Restore XCR0 on xsave capable CPUs:
+        */
+       if (cpu_has_xsave)
+               xsetbv(XCR_XFEATURE_ENABLED_MASK, xfeatures_mask);
+}
+
+/*
+ * Given the xsave area and a state inside, this function returns the
+ * address of the state.
+ *
+ * This is the API that is called to get xstate address in either
+ * standard format or compacted format of xsave area.
+ *
+ * Note that if there is no data for the field in the xsave buffer
+ * this will return NULL.
+ *
+ * Inputs:
+ *     xstate: the thread's storage area for all FPU data
+ *     xstate_feature: state which is defined in xsave.h (e.g.
+ *     XSTATE_FP, XSTATE_SSE, etc...)
+ * Output:
+ *     address of the state in the xsave area, or NULL if the
+ *     field is not present in the xsave buffer.
+ */
+void *get_xsave_addr(struct xregs_state *xsave, int xstate_feature)
+{
+       int feature_nr = fls64(xstate_feature) - 1;
+       /*
+        * Do we even *have* xsave state?
+        */
+       if (!boot_cpu_has(X86_FEATURE_XSAVE))
+               return NULL;
+
+       xsave = &current->thread.fpu.state.xsave;
+       /*
+        * We should not ever be requesting features that we
+        * have not enabled.  Remember that pcntxt_mask is
+        * what we write to the XCR0 register.
+        */
+       WARN_ONCE(!(xfeatures_mask & xstate_feature),
+                 "get of unsupported state");
+       /*
+        * This assumes the last 'xsave*' instruction to
+        * have requested that 'xstate_feature' be saved.
+        * If it did not, we might be seeing and old value
+        * of the field in the buffer.
+        *
+        * This can happen because the last 'xsave' did not
+        * request that this feature be saved (unlikely)
+        * or because the "init optimization" caused it
+        * to not be saved.
+        */
+       if (!(xsave->header.xfeatures & xstate_feature))
+               return NULL;
+
+       return (void *)xsave + xstate_comp_offsets[feature_nr];
+}
+EXPORT_SYMBOL_GPL(get_xsave_addr);
+
+/*
+ * This wraps up the common operations that need to occur when retrieving
+ * data from xsave state.  It first ensures that the current task was
+ * using the FPU and retrieves the data in to a buffer.  It then calculates
+ * the offset of the requested field in the buffer.
+ *
+ * This function is safe to call whether the FPU is in use or not.
+ *
+ * Note that this only works on the current task.
+ *
+ * Inputs:
+ *     @xsave_state: state which is defined in xsave.h (e.g. XSTATE_FP,
+ *     XSTATE_SSE, etc...)
+ * Output:
+ *     address of the state in the xsave area or NULL if the state
+ *     is not present or is in its 'init state'.
+ */
+const void *get_xsave_field_ptr(int xsave_state)
+{
+       struct fpu *fpu = &current->thread.fpu;
+
+       if (!fpu->fpstate_active)
+               return NULL;
+       /*
+        * fpu__save() takes the CPU's xstate registers
+        * and saves them off to the 'fpu memory buffer.
+        */
+       fpu__save(fpu);
+
+       return get_xsave_addr(&fpu->state.xsave, xsave_state);
+}
index 2b55ee6db053c79fbe91a6119e613075be54111b..5a4668136e9892b6b8695d1d82edf86afbdea0a0 100644 (file)
@@ -167,7 +167,7 @@ asmlinkage __visible void __init x86_64_start_kernel(char * real_mode_data)
        clear_bss();
 
        for (i = 0; i < NUM_EXCEPTION_VECTORS; i++)
-               set_intr_gate(i, early_idt_handlers[i]);
+               set_intr_gate(i, early_idt_handler_array[i]);
        load_idt((const struct desc_ptr *)&idt_descr);
 
        copy_bootdata(__va(real_mode_data));
index d031bad9e07eadf3a80bc69a449cd13a44ed8080..0e2d96ffd158d0e5f4c1d355040cd9b285ef84d6 100644 (file)
 #define PAGE_TABLE_SIZE(pages) ((pages) / PTRS_PER_PGD)
 #endif
 
-/* Number of possible pages in the lowmem region */
-LOWMEM_PAGES = (((1<<32) - __PAGE_OFFSET) >> PAGE_SHIFT)
-       
+/*
+ * Number of possible pages in the lowmem region.
+ *
+ * We shift 2 by 31 instead of 1 by 32 to the left in order to avoid a
+ * gas warning about overflowing shift count when gas has been compiled
+ * with only a host target support using a 32-bit type for internal
+ * representation.
+ */
+LOWMEM_PAGES = (((2<<31) - __PAGE_OFFSET) >> PAGE_SHIFT)
+
 /* Enough space to fit pagetables for the low memory linear map */
 MAPPING_BEYOND_END = PAGE_TABLE_SIZE(LOWMEM_PAGES) << PAGE_SHIFT
 
@@ -478,21 +485,22 @@ is486:
 __INIT
 setup_once:
        /*
-        * Set up a idt with 256 entries pointing to ignore_int,
-        * interrupt gates. It doesn't actually load idt - that needs
-        * to be done on each CPU. Interrupts are enabled elsewhere,
-        * when we can be relatively sure everything is ok.
+        * Set up a idt with 256 interrupt gates that push zero if there
+        * is no error code and then jump to early_idt_handler_common.
+        * It doesn't actually load the idt - that needs to be done on
+        * each CPU. Interrupts are enabled elsewhere, when we can be
+        * relatively sure everything is ok.
         */
 
        movl $idt_table,%edi
-       movl $early_idt_handlers,%eax
+       movl $early_idt_handler_array,%eax
        movl $NUM_EXCEPTION_VECTORS,%ecx
 1:
        movl %eax,(%edi)
        movl %eax,4(%edi)
        /* interrupt gate, dpl=0, present */
        movl $(0x8E000000 + __KERNEL_CS),2(%edi)
-       addl $9,%eax
+       addl $EARLY_IDT_HANDLER_SIZE,%eax
        addl $8,%edi
        loop 1b
 
@@ -524,30 +532,32 @@ setup_once:
        andl $0,setup_once_ref  /* Once is enough, thanks */
        ret
 
-ENTRY(early_idt_handlers)
+ENTRY(early_idt_handler_array)
        # 36(%esp) %eflags
        # 32(%esp) %cs
        # 28(%esp) %eip
        # 24(%rsp) error code
        i = 0
        .rept NUM_EXCEPTION_VECTORS
-       .if (EXCEPTION_ERRCODE_MASK >> i) & 1
-       ASM_NOP2
-       .else
+       .ifeq (EXCEPTION_ERRCODE_MASK >> i) & 1
        pushl $0                # Dummy error code, to make stack frame uniform
        .endif
        pushl $i                # 20(%esp) Vector number
-       jmp early_idt_handler
+       jmp early_idt_handler_common
        i = i + 1
+       .fill early_idt_handler_array + i*EARLY_IDT_HANDLER_SIZE - ., 1, 0xcc
        .endr
-ENDPROC(early_idt_handlers)
+ENDPROC(early_idt_handler_array)
        
-       /* This is global to keep gas from relaxing the jumps */
-ENTRY(early_idt_handler)
+early_idt_handler_common:
+       /*
+        * The stack is the hardware frame, an error code or zero, and the
+        * vector number.
+        */
        cld
 
        cmpl $2,(%esp)          # X86_TRAP_NMI
-       je is_nmi               # Ignore NMI
+       je .Lis_nmi             # Ignore NMI
 
        cmpl $2,%ss:early_recursion_flag
        je hlt_loop
@@ -600,10 +610,10 @@ ex_entry:
        pop %ecx
        pop %eax
        decl %ss:early_recursion_flag
-is_nmi:
+.Lis_nmi:
        addl $8,%esp            /* drop vector number and error code */
        iret
-ENDPROC(early_idt_handler)
+ENDPROC(early_idt_handler_common)
 
 /* This is the default interrupt "handler" :-) */
        ALIGN
index ae6588b301c248b3c281a1e072802e6764e9ac44..e5c27f729a3840b2755533cb597fbf702de07621 100644 (file)
@@ -321,30 +321,32 @@ bad_address:
        jmp bad_address
 
        __INIT
-       .globl early_idt_handlers
-early_idt_handlers:
+ENTRY(early_idt_handler_array)
        # 104(%rsp) %rflags
        #  96(%rsp) %cs
        #  88(%rsp) %rip
        #  80(%rsp) error code
        i = 0
        .rept NUM_EXCEPTION_VECTORS
-       .if (EXCEPTION_ERRCODE_MASK >> i) & 1
-       ASM_NOP2
-       .else
+       .ifeq (EXCEPTION_ERRCODE_MASK >> i) & 1
        pushq $0                # Dummy error code, to make stack frame uniform
        .endif
        pushq $i                # 72(%rsp) Vector number
-       jmp early_idt_handler
+       jmp early_idt_handler_common
        i = i + 1
+       .fill early_idt_handler_array + i*EARLY_IDT_HANDLER_SIZE - ., 1, 0xcc
        .endr
+ENDPROC(early_idt_handler_array)
 
-/* This is global to keep gas from relaxing the jumps */
-ENTRY(early_idt_handler)
+early_idt_handler_common:
+       /*
+        * The stack is the hardware frame, an error code or zero, and the
+        * vector number.
+        */
        cld
 
        cmpl $2,(%rsp)          # X86_TRAP_NMI
-       je is_nmi               # Ignore NMI
+       je .Lis_nmi             # Ignore NMI
 
        cmpl $2,early_recursion_flag(%rip)
        jz  1f
@@ -409,10 +411,10 @@ ENTRY(early_idt_handler)
        popq %rcx
        popq %rax
        decl early_recursion_flag(%rip)
-is_nmi:
+.Lis_nmi:
        addq $16,%rsp           # drop vector number and error code
        INTERRUPT_RETURN
-ENDPROC(early_idt_handler)
+ENDPROC(early_idt_handler_common)
 
        __INITDATA
 
index 3acbff4716b088ded2e1d237106872d5d1d1a7f0..10757d0a3fcf438e43ffbfc634e7daa8dc110a2e 100644 (file)
@@ -12,6 +12,7 @@
 #include <linux/pm.h>
 #include <linux/io.h>
 
+#include <asm/irqdomain.h>
 #include <asm/fixmap.h>
 #include <asm/hpet.h>
 #include <asm/time.h>
@@ -305,8 +306,6 @@ static void hpet_legacy_clockevent_register(void)
        printk(KERN_DEBUG "hpet clockevent registered\n");
 }
 
-static int hpet_setup_msi_irq(unsigned int irq);
-
 static void hpet_set_mode(enum clock_event_mode mode,
                          struct clock_event_device *evt, int timer)
 {
@@ -357,7 +356,7 @@ static void hpet_set_mode(enum clock_event_mode mode,
                        hpet_enable_legacy_int();
                } else {
                        struct hpet_dev *hdev = EVT_TO_HPET_DEV(evt);
-                       hpet_setup_msi_irq(hdev->irq);
+                       irq_domain_activate_irq(irq_get_irq_data(hdev->irq));
                        disable_irq(hdev->irq);
                        irq_set_affinity(hdev->irq, cpumask_of(hdev->cpu));
                        enable_irq(hdev->irq);
@@ -423,6 +422,7 @@ static int hpet_legacy_next_event(unsigned long delta,
 
 static DEFINE_PER_CPU(struct hpet_dev *, cpu_hpet_dev);
 static struct hpet_dev *hpet_devs;
+static struct irq_domain *hpet_domain;
 
 void hpet_msi_unmask(struct irq_data *data)
 {
@@ -473,31 +473,6 @@ static int hpet_msi_next_event(unsigned long delta,
        return hpet_next_event(delta, evt, hdev->num);
 }
 
-static int hpet_setup_msi_irq(unsigned int irq)
-{
-       if (x86_msi.setup_hpet_msi(irq, hpet_blockid)) {
-               irq_free_hwirq(irq);
-               return -EINVAL;
-       }
-       return 0;
-}
-
-static int hpet_assign_irq(struct hpet_dev *dev)
-{
-       unsigned int irq = irq_alloc_hwirq(-1);
-
-       if (!irq)
-               return -EINVAL;
-
-       irq_set_handler_data(irq, dev);
-
-       if (hpet_setup_msi_irq(irq))
-               return -EINVAL;
-
-       dev->irq = irq;
-       return 0;
-}
-
 static irqreturn_t hpet_interrupt_handler(int irq, void *data)
 {
        struct hpet_dev *dev = (struct hpet_dev *)data;
@@ -540,9 +515,6 @@ static void init_one_hpet_msi_clockevent(struct hpet_dev *hdev, int cpu)
        if (!(hdev->flags & HPET_DEV_VALID))
                return;
 
-       if (hpet_setup_msi_irq(hdev->irq))
-               return;
-
        hdev->cpu = cpu;
        per_cpu(cpu_hpet_dev, cpu) = hdev;
        evt->name = hdev->name;
@@ -574,7 +546,7 @@ static void hpet_msi_capability_lookup(unsigned int start_timer)
        unsigned int id;
        unsigned int num_timers;
        unsigned int num_timers_used = 0;
-       int i;
+       int i, irq;
 
        if (hpet_msi_disable)
                return;
@@ -587,6 +559,10 @@ static void hpet_msi_capability_lookup(unsigned int start_timer)
        num_timers++; /* Value read out starts from 0 */
        hpet_print_config();
 
+       hpet_domain = hpet_create_irq_domain(hpet_blockid);
+       if (!hpet_domain)
+               return;
+
        hpet_devs = kzalloc(sizeof(struct hpet_dev) * num_timers, GFP_KERNEL);
        if (!hpet_devs)
                return;
@@ -604,12 +580,14 @@ static void hpet_msi_capability_lookup(unsigned int start_timer)
                hdev->flags = 0;
                if (cfg & HPET_TN_PERIODIC_CAP)
                        hdev->flags |= HPET_DEV_PERI_CAP;
+               sprintf(hdev->name, "hpet%d", i);
                hdev->num = i;
 
-               sprintf(hdev->name, "hpet%d", i);
-               if (hpet_assign_irq(hdev))
+               irq = hpet_assign_irq(hpet_domain, hdev, hdev->num);
+               if (irq <= 0)
                        continue;
 
+               hdev->irq = irq;
                hdev->flags |= HPET_DEV_FSB_CAP;
                hdev->flags |= HPET_DEV_VALID;
                num_timers_used++;
@@ -709,10 +687,6 @@ static int hpet_cpuhp_notify(struct notifier_block *n,
 }
 #else
 
-static int hpet_setup_msi_irq(unsigned int irq)
-{
-       return 0;
-}
 static void hpet_msi_capability_lookup(unsigned int start_timer)
 {
        return;
diff --git a/arch/x86/kernel/i387.c b/arch/x86/kernel/i387.c
deleted file mode 100644 (file)
index 6185d31..0000000
+++ /dev/null
@@ -1,671 +0,0 @@
-/*
- *  Copyright (C) 1994 Linus Torvalds
- *
- *  Pentium III FXSR, SSE support
- *  General FPU state handling cleanups
- *     Gareth Hughes <gareth@valinux.com>, May 2000
- */
-#include <linux/module.h>
-#include <linux/regset.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
-
-#include <asm/sigcontext.h>
-#include <asm/processor.h>
-#include <asm/math_emu.h>
-#include <asm/tlbflush.h>
-#include <asm/uaccess.h>
-#include <asm/ptrace.h>
-#include <asm/i387.h>
-#include <asm/fpu-internal.h>
-#include <asm/user.h>
-
-static DEFINE_PER_CPU(bool, in_kernel_fpu);
-
-void kernel_fpu_disable(void)
-{
-       WARN_ON(this_cpu_read(in_kernel_fpu));
-       this_cpu_write(in_kernel_fpu, true);
-}
-
-void kernel_fpu_enable(void)
-{
-       this_cpu_write(in_kernel_fpu, false);
-}
-
-/*
- * Were we in an interrupt that interrupted kernel mode?
- *
- * On others, we can do a kernel_fpu_begin/end() pair *ONLY* if that
- * pair does nothing at all: the thread must not have fpu (so
- * that we don't try to save the FPU state), and TS must
- * be set (so that the clts/stts pair does nothing that is
- * visible in the interrupted kernel thread).
- *
- * Except for the eagerfpu case when we return true; in the likely case
- * the thread has FPU but we are not going to set/clear TS.
- */
-static inline bool interrupted_kernel_fpu_idle(void)
-{
-       if (this_cpu_read(in_kernel_fpu))
-               return false;
-
-       if (use_eager_fpu())
-               return true;
-
-       return !__thread_has_fpu(current) &&
-               (read_cr0() & X86_CR0_TS);
-}
-
-/*
- * Were we in user mode (or vm86 mode) when we were
- * interrupted?
- *
- * Doing kernel_fpu_begin/end() is ok if we are running
- * in an interrupt context from user mode - we'll just
- * save the FPU state as required.
- */
-static inline bool interrupted_user_mode(void)
-{
-       struct pt_regs *regs = get_irq_regs();
-       return regs && user_mode(regs);
-}
-
-/*
- * Can we use the FPU in kernel mode with the
- * whole "kernel_fpu_begin/end()" sequence?
- *
- * It's always ok in process context (ie "not interrupt")
- * but it is sometimes ok even from an irq.
- */
-bool irq_fpu_usable(void)
-{
-       return !in_interrupt() ||
-               interrupted_user_mode() ||
-               interrupted_kernel_fpu_idle();
-}
-EXPORT_SYMBOL(irq_fpu_usable);
-
-void __kernel_fpu_begin(void)
-{
-       struct task_struct *me = current;
-
-       this_cpu_write(in_kernel_fpu, true);
-
-       if (__thread_has_fpu(me)) {
-               __save_init_fpu(me);
-       } else {
-               this_cpu_write(fpu_owner_task, NULL);
-               if (!use_eager_fpu())
-                       clts();
-       }
-}
-EXPORT_SYMBOL(__kernel_fpu_begin);
-
-void __kernel_fpu_end(void)
-{
-       struct task_struct *me = current;
-
-       if (__thread_has_fpu(me)) {
-               if (WARN_ON(restore_fpu_checking(me)))
-                       fpu_reset_state(me);
-       } else if (!use_eager_fpu()) {
-               stts();
-       }
-
-       this_cpu_write(in_kernel_fpu, false);
-}
-EXPORT_SYMBOL(__kernel_fpu_end);
-
-void unlazy_fpu(struct task_struct *tsk)
-{
-       preempt_disable();
-       if (__thread_has_fpu(tsk)) {
-               if (use_eager_fpu()) {
-                       __save_fpu(tsk);
-               } else {
-                       __save_init_fpu(tsk);
-                       __thread_fpu_end(tsk);
-               }
-       }
-       preempt_enable();
-}
-EXPORT_SYMBOL(unlazy_fpu);
-
-unsigned int mxcsr_feature_mask __read_mostly = 0xffffffffu;
-unsigned int xstate_size;
-EXPORT_SYMBOL_GPL(xstate_size);
-static struct i387_fxsave_struct fx_scratch;
-
-static void mxcsr_feature_mask_init(void)
-{
-       unsigned long mask = 0;
-
-       if (cpu_has_fxsr) {
-               memset(&fx_scratch, 0, sizeof(struct i387_fxsave_struct));
-               asm volatile("fxsave %0" : "+m" (fx_scratch));
-               mask = fx_scratch.mxcsr_mask;
-               if (mask == 0)
-                       mask = 0x0000ffbf;
-       }
-       mxcsr_feature_mask &= mask;
-}
-
-static void init_thread_xstate(void)
-{
-       /*
-        * Note that xstate_size might be overwriten later during
-        * xsave_init().
-        */
-
-       if (!cpu_has_fpu) {
-               /*
-                * Disable xsave as we do not support it if i387
-                * emulation is enabled.
-                */
-               setup_clear_cpu_cap(X86_FEATURE_XSAVE);
-               setup_clear_cpu_cap(X86_FEATURE_XSAVEOPT);
-               xstate_size = sizeof(struct i387_soft_struct);
-               return;
-       }
-
-       if (cpu_has_fxsr)
-               xstate_size = sizeof(struct i387_fxsave_struct);
-       else
-               xstate_size = sizeof(struct i387_fsave_struct);
-
-       /*
-        * Quirk: we don't yet handle the XSAVES* instructions
-        * correctly, as we don't correctly convert between
-        * standard and compacted format when interfacing
-        * with user-space - so disable it for now.
-        *
-        * The difference is small: with recent CPUs the
-        * compacted format is only marginally smaller than
-        * the standard FPU state format.
-        *
-        * ( This is easy to backport while we are fixing
-        *   XSAVES* support. )
-        */
-       setup_clear_cpu_cap(X86_FEATURE_XSAVES);
-}
-
-/*
- * Called at bootup to set up the initial FPU state that is later cloned
- * into all processes.
- */
-
-void fpu_init(void)
-{
-       unsigned long cr0;
-       unsigned long cr4_mask = 0;
-
-#ifndef CONFIG_MATH_EMULATION
-       if (!cpu_has_fpu) {
-               pr_emerg("No FPU found and no math emulation present\n");
-               pr_emerg("Giving up\n");
-               for (;;)
-                       asm volatile("hlt");
-       }
-#endif
-       if (cpu_has_fxsr)
-               cr4_mask |= X86_CR4_OSFXSR;
-       if (cpu_has_xmm)
-               cr4_mask |= X86_CR4_OSXMMEXCPT;
-       if (cr4_mask)
-               cr4_set_bits(cr4_mask);
-
-       cr0 = read_cr0();
-       cr0 &= ~(X86_CR0_TS|X86_CR0_EM); /* clear TS and EM */
-       if (!cpu_has_fpu)
-               cr0 |= X86_CR0_EM;
-       write_cr0(cr0);
-
-       /*
-        * init_thread_xstate is only called once to avoid overriding
-        * xstate_size during boot time or during CPU hotplug.
-        */
-       if (xstate_size == 0)
-               init_thread_xstate();
-
-       mxcsr_feature_mask_init();
-       xsave_init();
-       eager_fpu_init();
-}
-
-void fpu_finit(struct fpu *fpu)
-{
-       if (!cpu_has_fpu) {
-               finit_soft_fpu(&fpu->state->soft);
-               return;
-       }
-
-       memset(fpu->state, 0, xstate_size);
-
-       if (cpu_has_fxsr) {
-               fx_finit(&fpu->state->fxsave);
-       } else {
-               struct i387_fsave_struct *fp = &fpu->state->fsave;
-               fp->cwd = 0xffff037fu;
-               fp->swd = 0xffff0000u;
-               fp->twd = 0xffffffffu;
-               fp->fos = 0xffff0000u;
-       }
-}
-EXPORT_SYMBOL_GPL(fpu_finit);
-
-/*
- * The _current_ task is using the FPU for the first time
- * so initialize it and set the mxcsr to its default
- * value at reset if we support XMM instructions and then
- * remember the current task has used the FPU.
- */
-int init_fpu(struct task_struct *tsk)
-{
-       int ret;
-
-       if (tsk_used_math(tsk)) {
-               if (cpu_has_fpu && tsk == current)
-                       unlazy_fpu(tsk);
-               task_disable_lazy_fpu_restore(tsk);
-               return 0;
-       }
-
-       /*
-        * Memory allocation at the first usage of the FPU and other state.
-        */
-       ret = fpu_alloc(&tsk->thread.fpu);
-       if (ret)
-               return ret;
-
-       fpu_finit(&tsk->thread.fpu);
-
-       set_stopped_child_used_math(tsk);
-       return 0;
-}
-EXPORT_SYMBOL_GPL(init_fpu);
-
-/*
- * The xstateregs_active() routine is the same as the fpregs_active() routine,
- * as the "regset->n" for the xstate regset will be updated based on the feature
- * capabilites supported by the xsave.
- */
-int fpregs_active(struct task_struct *target, const struct user_regset *regset)
-{
-       return tsk_used_math(target) ? regset->n : 0;
-}
-
-int xfpregs_active(struct task_struct *target, const struct user_regset *regset)
-{
-       return (cpu_has_fxsr && tsk_used_math(target)) ? regset->n : 0;
-}
-
-int xfpregs_get(struct task_struct *target, const struct user_regset *regset,
-               unsigned int pos, unsigned int count,
-               void *kbuf, void __user *ubuf)
-{
-       int ret;
-
-       if (!cpu_has_fxsr)
-               return -ENODEV;
-
-       ret = init_fpu(target);
-       if (ret)
-               return ret;
-
-       sanitize_i387_state(target);
-
-       return user_regset_copyout(&pos, &count, &kbuf, &ubuf,
-                                  &target->thread.fpu.state->fxsave, 0, -1);
-}
-
-int xfpregs_set(struct task_struct *target, const struct user_regset *regset,
-               unsigned int pos, unsigned int count,
-               const void *kbuf, const void __user *ubuf)
-{
-       int ret;
-
-       if (!cpu_has_fxsr)
-               return -ENODEV;
-
-       ret = init_fpu(target);
-       if (ret)
-               return ret;
-
-       sanitize_i387_state(target);
-
-       ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
-                                &target->thread.fpu.state->fxsave, 0, -1);
-
-       /*
-        * mxcsr reserved bits must be masked to zero for security reasons.
-        */
-       target->thread.fpu.state->fxsave.mxcsr &= mxcsr_feature_mask;
-
-       /*
-        * update the header bits in the xsave header, indicating the
-        * presence of FP and SSE state.
-        */
-       if (cpu_has_xsave)
-               target->thread.fpu.state->xsave.xsave_hdr.xstate_bv |= XSTATE_FPSSE;
-
-       return ret;
-}
-
-int xstateregs_get(struct task_struct *target, const struct user_regset *regset,
-               unsigned int pos, unsigned int count,
-               void *kbuf, void __user *ubuf)
-{
-       struct xsave_struct *xsave;
-       int ret;
-
-       if (!cpu_has_xsave)
-               return -ENODEV;
-
-       ret = init_fpu(target);
-       if (ret)
-               return ret;
-
-       xsave = &target->thread.fpu.state->xsave;
-
-       /*
-        * Copy the 48bytes defined by the software first into the xstate
-        * memory layout in the thread struct, so that we can copy the entire
-        * xstateregs to the user using one user_regset_copyout().
-        */
-       memcpy(&xsave->i387.sw_reserved,
-               xstate_fx_sw_bytes, sizeof(xstate_fx_sw_bytes));
-       /*
-        * Copy the xstate memory layout.
-        */
-       ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, xsave, 0, -1);
-       return ret;
-}
-
-int xstateregs_set(struct task_struct *target, const struct user_regset *regset,
-                 unsigned int pos, unsigned int count,
-                 const void *kbuf, const void __user *ubuf)
-{
-       struct xsave_struct *xsave;
-       int ret;
-
-       if (!cpu_has_xsave)
-               return -ENODEV;
-
-       ret = init_fpu(target);
-       if (ret)
-               return ret;
-
-       xsave = &target->thread.fpu.state->xsave;
-
-       ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, xsave, 0, -1);
-       /*
-        * mxcsr reserved bits must be masked to zero for security reasons.
-        */
-       xsave->i387.mxcsr &= mxcsr_feature_mask;
-       xsave->xsave_hdr.xstate_bv &= pcntxt_mask;
-       /*
-        * These bits must be zero.
-        */
-       memset(&xsave->xsave_hdr.reserved, 0, 48);
-       return ret;
-}
-
-#if defined CONFIG_X86_32 || defined CONFIG_IA32_EMULATION
-
-/*
- * FPU tag word conversions.
- */
-
-static inline unsigned short twd_i387_to_fxsr(unsigned short twd)
-{
-       unsigned int tmp; /* to avoid 16 bit prefixes in the code */
-
-       /* Transform each pair of bits into 01 (valid) or 00 (empty) */
-       tmp = ~twd;
-       tmp = (tmp | (tmp>>1)) & 0x5555; /* 0V0V0V0V0V0V0V0V */
-       /* and move the valid bits to the lower byte. */
-       tmp = (tmp | (tmp >> 1)) & 0x3333; /* 00VV00VV00VV00VV */
-       tmp = (tmp | (tmp >> 2)) & 0x0f0f; /* 0000VVVV0000VVVV */
-       tmp = (tmp | (tmp >> 4)) & 0x00ff; /* 00000000VVVVVVVV */
-
-       return tmp;
-}
-
-#define FPREG_ADDR(f, n)       ((void *)&(f)->st_space + (n) * 16)
-#define FP_EXP_TAG_VALID       0
-#define FP_EXP_TAG_ZERO                1
-#define FP_EXP_TAG_SPECIAL     2
-#define FP_EXP_TAG_EMPTY       3
-
-static inline u32 twd_fxsr_to_i387(struct i387_fxsave_struct *fxsave)
-{
-       struct _fpxreg *st;
-       u32 tos = (fxsave->swd >> 11) & 7;
-       u32 twd = (unsigned long) fxsave->twd;
-       u32 tag;
-       u32 ret = 0xffff0000u;
-       int i;
-
-       for (i = 0; i < 8; i++, twd >>= 1) {
-               if (twd & 0x1) {
-                       st = FPREG_ADDR(fxsave, (i - tos) & 7);
-
-                       switch (st->exponent & 0x7fff) {
-                       case 0x7fff:
-                               tag = FP_EXP_TAG_SPECIAL;
-                               break;
-                       case 0x0000:
-                               if (!st->significand[0] &&
-                                   !st->significand[1] &&
-                                   !st->significand[2] &&
-                                   !st->significand[3])
-                                       tag = FP_EXP_TAG_ZERO;
-                               else
-                                       tag = FP_EXP_TAG_SPECIAL;
-                               break;
-                       default:
-                               if (st->significand[3] & 0x8000)
-                                       tag = FP_EXP_TAG_VALID;
-                               else
-                                       tag = FP_EXP_TAG_SPECIAL;
-                               break;
-                       }
-               } else {
-                       tag = FP_EXP_TAG_EMPTY;
-               }
-               ret |= tag << (2 * i);
-       }
-       return ret;
-}
-
-/*
- * FXSR floating point environment conversions.
- */
-
-void
-convert_from_fxsr(struct user_i387_ia32_struct *env, struct task_struct *tsk)
-{
-       struct i387_fxsave_struct *fxsave = &tsk->thread.fpu.state->fxsave;
-       struct _fpreg *to = (struct _fpreg *) &env->st_space[0];
-       struct _fpxreg *from = (struct _fpxreg *) &fxsave->st_space[0];
-       int i;
-
-       env->cwd = fxsave->cwd | 0xffff0000u;
-       env->swd = fxsave->swd | 0xffff0000u;
-       env->twd = twd_fxsr_to_i387(fxsave);
-
-#ifdef CONFIG_X86_64
-       env->fip = fxsave->rip;
-       env->foo = fxsave->rdp;
-       /*
-        * should be actually ds/cs at fpu exception time, but
-        * that information is not available in 64bit mode.
-        */
-       env->fcs = task_pt_regs(tsk)->cs;
-       if (tsk == current) {
-               savesegment(ds, env->fos);
-       } else {
-               env->fos = tsk->thread.ds;
-       }
-       env->fos |= 0xffff0000;
-#else
-       env->fip = fxsave->fip;
-       env->fcs = (u16) fxsave->fcs | ((u32) fxsave->fop << 16);
-       env->foo = fxsave->foo;
-       env->fos = fxsave->fos;
-#endif
-
-       for (i = 0; i < 8; ++i)
-               memcpy(&to[i], &from[i], sizeof(to[0]));
-}
-
-void convert_to_fxsr(struct task_struct *tsk,
-                    const struct user_i387_ia32_struct *env)
-
-{
-       struct i387_fxsave_struct *fxsave = &tsk->thread.fpu.state->fxsave;
-       struct _fpreg *from = (struct _fpreg *) &env->st_space[0];
-       struct _fpxreg *to = (struct _fpxreg *) &fxsave->st_space[0];
-       int i;
-
-       fxsave->cwd = env->cwd;
-       fxsave->swd = env->swd;
-       fxsave->twd = twd_i387_to_fxsr(env->twd);
-       fxsave->fop = (u16) ((u32) env->fcs >> 16);
-#ifdef CONFIG_X86_64
-       fxsave->rip = env->fip;
-       fxsave->rdp = env->foo;
-       /* cs and ds ignored */
-#else
-       fxsave->fip = env->fip;
-       fxsave->fcs = (env->fcs & 0xffff);
-       fxsave->foo = env->foo;
-       fxsave->fos = env->fos;
-#endif
-
-       for (i = 0; i < 8; ++i)
-               memcpy(&to[i], &from[i], sizeof(from[0]));
-}
-
-int fpregs_get(struct task_struct *target, const struct user_regset *regset,
-              unsigned int pos, unsigned int count,
-              void *kbuf, void __user *ubuf)
-{
-       struct user_i387_ia32_struct env;
-       int ret;
-
-       ret = init_fpu(target);
-       if (ret)
-               return ret;
-
-       if (!static_cpu_has(X86_FEATURE_FPU))
-               return fpregs_soft_get(target, regset, pos, count, kbuf, ubuf);
-
-       if (!cpu_has_fxsr)
-               return user_regset_copyout(&pos, &count, &kbuf, &ubuf,
-                                          &target->thread.fpu.state->fsave, 0,
-                                          -1);
-
-       sanitize_i387_state(target);
-
-       if (kbuf && pos == 0 && count == sizeof(env)) {
-               convert_from_fxsr(kbuf, target);
-               return 0;
-       }
-
-       convert_from_fxsr(&env, target);
-
-       return user_regset_copyout(&pos, &count, &kbuf, &ubuf, &env, 0, -1);
-}
-
-int fpregs_set(struct task_struct *target, const struct user_regset *regset,
-              unsigned int pos, unsigned int count,
-              const void *kbuf, const void __user *ubuf)
-{
-       struct user_i387_ia32_struct env;
-       int ret;
-
-       ret = init_fpu(target);
-       if (ret)
-               return ret;
-
-       sanitize_i387_state(target);
-
-       if (!static_cpu_has(X86_FEATURE_FPU))
-               return fpregs_soft_set(target, regset, pos, count, kbuf, ubuf);
-
-       if (!cpu_has_fxsr)
-               return user_regset_copyin(&pos, &count, &kbuf, &ubuf,
-                                         &target->thread.fpu.state->fsave, 0,
-                                         -1);
-
-       if (pos > 0 || count < sizeof(env))
-               convert_from_fxsr(&env, target);
-
-       ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &env, 0, -1);
-       if (!ret)
-               convert_to_fxsr(target, &env);
-
-       /*
-        * update the header bit in the xsave header, indicating the
-        * presence of FP.
-        */
-       if (cpu_has_xsave)
-               target->thread.fpu.state->xsave.xsave_hdr.xstate_bv |= XSTATE_FP;
-       return ret;
-}
-
-/*
- * FPU state for core dumps.
- * This is only used for a.out dumps now.
- * It is declared generically using elf_fpregset_t (which is
- * struct user_i387_struct) but is in fact only used for 32-bit
- * dumps, so on 64-bit it is really struct user_i387_ia32_struct.
- */
-int dump_fpu(struct pt_regs *regs, struct user_i387_struct *fpu)
-{
-       struct task_struct *tsk = current;
-       int fpvalid;
-
-       fpvalid = !!used_math();
-       if (fpvalid)
-               fpvalid = !fpregs_get(tsk, NULL,
-                                     0, sizeof(struct user_i387_ia32_struct),
-                                     fpu, NULL);
-
-       return fpvalid;
-}
-EXPORT_SYMBOL(dump_fpu);
-
-#endif /* CONFIG_X86_32 || CONFIG_IA32_EMULATION */
-
-static int __init no_387(char *s)
-{
-       setup_clear_cpu_cap(X86_FEATURE_FPU);
-       return 1;
-}
-
-__setup("no387", no_387);
-
-void fpu_detect(struct cpuinfo_x86 *c)
-{
-       unsigned long cr0;
-       u16 fsw, fcw;
-
-       fsw = fcw = 0xffff;
-
-       cr0 = read_cr0();
-       cr0 &= ~(X86_CR0_TS | X86_CR0_EM);
-       write_cr0(cr0);
-
-       asm volatile("fninit ; fnstsw %0 ; fnstcw %1"
-                    : "+m" (fsw), "+m" (fcw));
-
-       if (fsw == 0 && (fcw & 0x103f) == 0x003f)
-               set_cpu_cap(c, X86_FEATURE_FPU);
-       else
-               clear_cpu_cap(c, X86_FEATURE_FPU);
-
-       /* The final cr0 value is set in fpu_init() */
-}
index e7cc5370cd2fcade87dc1cecae2ab85184f62d27..16cb827a5b27745d1f571d49a5b45443babb2c8c 100644 (file)
@@ -329,8 +329,8 @@ static void init_8259A(int auto_eoi)
         */
        outb_pic(0x11, PIC_MASTER_CMD); /* ICW1: select 8259A-1 init */
 
-       /* ICW2: 8259A-1 IR0-7 mapped to 0x30-0x37 */
-       outb_pic(IRQ0_VECTOR, PIC_MASTER_IMR);
+       /* ICW2: 8259A-1 IR0-7 mapped to ISA_IRQ_VECTOR(0) */
+       outb_pic(ISA_IRQ_VECTOR(0), PIC_MASTER_IMR);
 
        /* 8259A-1 (the master) has a slave on IR2 */
        outb_pic(1U << PIC_CASCADE_IR, PIC_MASTER_IMR);
@@ -342,8 +342,8 @@ static void init_8259A(int auto_eoi)
 
        outb_pic(0x11, PIC_SLAVE_CMD);  /* ICW1: select 8259A-2 init */
 
-       /* ICW2: 8259A-2 IR0-7 mapped to IRQ8_VECTOR */
-       outb_pic(IRQ8_VECTOR, PIC_SLAVE_IMR);
+       /* ICW2: 8259A-2 IR0-7 mapped to ISA_IRQ_VECTOR(8) */
+       outb_pic(ISA_IRQ_VECTOR(8), PIC_SLAVE_IMR);
        /* 8259A-2 is a slave on master's IR2 */
        outb_pic(PIC_CASCADE_IR, PIC_SLAVE_IMR);
        /* (slave's support for AEOI in flat mode is to be investigated) */
index e5952c22553241e2ceea5d5fd6f1f7b758cc960e..88b366487b0e44b613e83d412febfad74381ca92 100644 (file)
 #define CREATE_TRACE_POINTS
 #include <asm/trace/irq_vectors.h>
 
+DEFINE_PER_CPU_SHARED_ALIGNED(irq_cpustat_t, irq_stat);
+EXPORT_PER_CPU_SYMBOL(irq_stat);
+
+DEFINE_PER_CPU(struct pt_regs *, irq_regs);
+EXPORT_PER_CPU_SYMBOL(irq_regs);
+
 atomic_t irq_err_count;
 
 /* Function pointer for generic interrupt vector handling */
@@ -116,6 +122,12 @@ int arch_show_interrupts(struct seq_file *p, int prec)
                seq_printf(p, "%10u ", irq_stats(j)->irq_threshold_count);
        seq_puts(p, "  Threshold APIC interrupts\n");
 #endif
+#ifdef CONFIG_X86_MCE_AMD
+       seq_printf(p, "%*s: ", prec, "DFR");
+       for_each_online_cpu(j)
+               seq_printf(p, "%10u ", irq_stats(j)->irq_deferred_error_count);
+       seq_puts(p, "  Deferred Error APIC interrupts\n");
+#endif
 #ifdef CONFIG_X86_MCE
        seq_printf(p, "%*s: ", prec, "MCE");
        for_each_online_cpu(j)
@@ -135,6 +147,18 @@ int arch_show_interrupts(struct seq_file *p, int prec)
        seq_printf(p, "%*s: %10u\n", prec, "ERR", atomic_read(&irq_err_count));
 #if defined(CONFIG_X86_IO_APIC)
        seq_printf(p, "%*s: %10u\n", prec, "MIS", atomic_read(&irq_mis_count));
+#endif
+#ifdef CONFIG_HAVE_KVM
+       seq_printf(p, "%*s: ", prec, "PIN");
+       for_each_online_cpu(j)
+               seq_printf(p, "%10u ", irq_stats(j)->kvm_posted_intr_ipis);
+       seq_puts(p, "  Posted-interrupt notification event\n");
+
+       seq_printf(p, "%*s: ", prec, "PIW");
+       for_each_online_cpu(j)
+               seq_printf(p, "%10u ",
+                          irq_stats(j)->kvm_posted_intr_wakeup_ipis);
+       seq_puts(p, "  Posted-interrupt wakeup event\n");
 #endif
        return 0;
 }
@@ -192,8 +216,7 @@ __visible unsigned int __irq_entry do_IRQ(struct pt_regs *regs)
        unsigned vector = ~regs->orig_ax;
        unsigned irq;
 
-       irq_enter();
-       exit_idle();
+       entering_irq();
 
        irq = __this_cpu_read(vector_irq[vector]);
 
@@ -209,7 +232,7 @@ __visible unsigned int __irq_entry do_IRQ(struct pt_regs *regs)
                }
        }
 
-       irq_exit();
+       exiting_irq();
 
        set_irq_regs(old_regs);
        return 1;
@@ -237,6 +260,18 @@ __visible void smp_x86_platform_ipi(struct pt_regs *regs)
 }
 
 #ifdef CONFIG_HAVE_KVM
+static void dummy_handler(void) {}
+static void (*kvm_posted_intr_wakeup_handler)(void) = dummy_handler;
+
+void kvm_set_posted_intr_wakeup_handler(void (*handler)(void))
+{
+       if (handler)
+               kvm_posted_intr_wakeup_handler = handler;
+       else
+               kvm_posted_intr_wakeup_handler = dummy_handler;
+}
+EXPORT_SYMBOL_GPL(kvm_set_posted_intr_wakeup_handler);
+
 /*
  * Handler for POSTED_INTERRUPT_VECTOR.
  */
@@ -244,16 +279,23 @@ __visible void smp_kvm_posted_intr_ipi(struct pt_regs *regs)
 {
        struct pt_regs *old_regs = set_irq_regs(regs);
 
-       ack_APIC_irq();
-
-       irq_enter();
-
-       exit_idle();
-
+       entering_ack_irq();
        inc_irq_stat(kvm_posted_intr_ipis);
+       exiting_irq();
+       set_irq_regs(old_regs);
+}
 
-       irq_exit();
+/*
+ * Handler for POSTED_INTERRUPT_WAKEUP_VECTOR.
+ */
+__visible void smp_kvm_posted_intr_wakeup_ipi(struct pt_regs *regs)
+{
+       struct pt_regs *old_regs = set_irq_regs(regs);
 
+       entering_ack_irq();
+       inc_irq_stat(kvm_posted_intr_wakeup_ipis);
+       kvm_posted_intr_wakeup_handler();
+       exiting_irq();
        set_irq_regs(old_regs);
 }
 #endif
index f9fd86a7fcc7d1bc8c2cc920fc5b5037f5859336..cd74f5978ab97a4c7d2ee072d23d764ab49ca6dd 100644 (file)
 
 #include <asm/apic.h>
 
-DEFINE_PER_CPU_SHARED_ALIGNED(irq_cpustat_t, irq_stat);
-EXPORT_PER_CPU_SYMBOL(irq_stat);
-
-DEFINE_PER_CPU(struct pt_regs *, irq_regs);
-EXPORT_PER_CPU_SYMBOL(irq_regs);
-
 #ifdef CONFIG_DEBUG_STACKOVERFLOW
 
 int sysctl_panic_on_stackoverflow __read_mostly;
index 394e643d7830fc01d4da516bd79cab1dc0aa6962..bc4604e500a3284ab3b2f5903b4fc1d506c0b367 100644 (file)
 #include <asm/idle.h>
 #include <asm/apic.h>
 
-DEFINE_PER_CPU_SHARED_ALIGNED(irq_cpustat_t, irq_stat);
-EXPORT_PER_CPU_SYMBOL(irq_stat);
-
-DEFINE_PER_CPU(struct pt_regs *, irq_regs);
-EXPORT_PER_CPU_SYMBOL(irq_regs);
-
 int sysctl_panic_on_stackoverflow;
 
 /*
index 15d741ddfeeb7c4497e28052e1ad7ea08e68c252..dc5fa6a1e8d640aa8fc407ee3035feb0a1778451 100644 (file)
 #include <asm/apic.h>
 #include <asm/trace/irq_vectors.h>
 
-static inline void irq_work_entering_irq(void)
-{
-       irq_enter();
-       ack_APIC_irq();
-}
-
 static inline void __smp_irq_work_interrupt(void)
 {
        inc_irq_stat(apic_irq_work_irqs);
@@ -24,14 +18,14 @@ static inline void __smp_irq_work_interrupt(void)
 
 __visible void smp_irq_work_interrupt(struct pt_regs *regs)
 {
-       irq_work_entering_irq();
+       ipi_entering_ack_irq();
        __smp_irq_work_interrupt();
        exiting_irq();
 }
 
 __visible void smp_trace_irq_work_interrupt(struct pt_regs *regs)
 {
-       irq_work_entering_irq();
+       ipi_entering_ack_irq();
        trace_irq_work_entry(IRQ_WORK_VECTOR);
        __smp_irq_work_interrupt();
        trace_irq_work_exit(IRQ_WORK_VECTOR);
index cd10a64372647c3579ba6717db49c6cd63c6353a..a3a5e158ed69553eaa623678d3812e008a4e24c7 100644 (file)
@@ -86,7 +86,7 @@ void __init init_IRQ(void)
        int i;
 
        /*
-        * On cpu 0, Assign IRQ0_VECTOR..IRQ15_VECTOR's to IRQ 0..15.
+        * On cpu 0, Assign ISA_IRQ_VECTOR(irq) to IRQ 0..15.
         * If these IRQ's are handled by legacy interrupt-controllers like PIC,
         * then this configuration will likely be static after the boot. If
         * these IRQ's are handled by more mordern controllers like IO-APIC,
@@ -94,7 +94,7 @@ void __init init_IRQ(void)
         * irq's migrate etc.
         */
        for (i = 0; i < nr_legacy_irqs(); i++)
-               per_cpu(vector_irq, 0)[IRQ0_VECTOR + i] = i;
+               per_cpu(vector_irq, 0)[ISA_IRQ_VECTOR(i)] = i;
 
        x86_init.irqs.intr_init();
 }
@@ -135,6 +135,10 @@ static void __init apic_intr_init(void)
        alloc_intr_gate(THRESHOLD_APIC_VECTOR, threshold_interrupt);
 #endif
 
+#ifdef CONFIG_X86_MCE_AMD
+       alloc_intr_gate(DEFERRED_ERROR_VECTOR, deferred_error_interrupt);
+#endif
+
 #ifdef CONFIG_X86_LOCAL_APIC
        /* self generated IPI for local APIC timer */
        alloc_intr_gate(LOCAL_TIMER_VECTOR, apic_timer_interrupt);
@@ -144,6 +148,8 @@ static void __init apic_intr_init(void)
 #ifdef CONFIG_HAVE_KVM
        /* IPI for KVM to deliver posted interrupt */
        alloc_intr_gate(POSTED_INTR_VECTOR, kvm_posted_intr_ipi);
+       /* IPI for KVM to deliver interrupt to wake up tasks */
+       alloc_intr_gate(POSTED_INTR_WAKEUP_VECTOR, kvm_posted_intr_wakeup_ipi);
 #endif
 
        /* IPI vectors for APIC spurious and error interrupts */
index 9435620062df30e549d1510baaf9c4c72ab90290..47190bd399e7ef5680172859fa78d9ae1ba0e595 100644 (file)
@@ -331,7 +331,7 @@ static void kvm_guest_apic_eoi_write(u32 reg, u32 val)
        apic_write(APIC_EOI, APIC_EOI_ACK);
 }
 
-void kvm_guest_cpu_init(void)
+static void kvm_guest_cpu_init(void)
 {
        if (!kvm_para_available())
                return;
@@ -584,6 +584,39 @@ static void kvm_kick_cpu(int cpu)
        kvm_hypercall2(KVM_HC_KICK_CPU, flags, apicid);
 }
 
+
+#ifdef CONFIG_QUEUED_SPINLOCKS
+
+#include <asm/qspinlock.h>
+
+static void kvm_wait(u8 *ptr, u8 val)
+{
+       unsigned long flags;
+
+       if (in_nmi())
+               return;
+
+       local_irq_save(flags);
+
+       if (READ_ONCE(*ptr) != val)
+               goto out;
+
+       /*
+        * halt until it's our turn and kicked. Note that we do safe halt
+        * for irq enabled case to avoid hang when lock info is overwritten
+        * in irq spinlock slowpath and no spurious interrupt occur to save us.
+        */
+       if (arch_irqs_disabled_flags(flags))
+               halt();
+       else
+               safe_halt();
+
+out:
+       local_irq_restore(flags);
+}
+
+#else /* !CONFIG_QUEUED_SPINLOCKS */
+
 enum kvm_contention_stat {
        TAKEN_SLOW,
        TAKEN_SLOW_PICKUP,
@@ -655,7 +688,7 @@ static inline void spin_time_accum_blocked(u64 start)
 static struct dentry *d_spin_debug;
 static struct dentry *d_kvm_debug;
 
-struct dentry *kvm_init_debugfs(void)
+static struct dentry *kvm_init_debugfs(void)
 {
        d_kvm_debug = debugfs_create_dir("kvm-guest", NULL);
        if (!d_kvm_debug)
@@ -817,6 +850,8 @@ static void kvm_unlock_kick(struct arch_spinlock *lock, __ticket_t ticket)
        }
 }
 
+#endif /* !CONFIG_QUEUED_SPINLOCKS */
+
 /*
  * Setup pv_lock_ops to exploit KVM_FEATURE_PV_UNHALT if present.
  */
@@ -828,8 +863,16 @@ void __init kvm_spinlock_init(void)
        if (!kvm_para_has_feature(KVM_FEATURE_PV_UNHALT))
                return;
 
+#ifdef CONFIG_QUEUED_SPINLOCKS
+       __pv_init_lock_hash();
+       pv_lock_ops.queued_spin_lock_slowpath = __pv_queued_spin_lock_slowpath;
+       pv_lock_ops.queued_spin_unlock = PV_CALLEE_SAVE(__pv_queued_spin_unlock);
+       pv_lock_ops.wait = kvm_wait;
+       pv_lock_ops.kick = kvm_kick_cpu;
+#else /* !CONFIG_QUEUED_SPINLOCKS */
        pv_lock_ops.lock_spinning = PV_CALLEE_SAVE(kvm_lock_spinning);
        pv_lock_ops.unlock_kick = kvm_unlock_kick;
+#endif
 }
 
 static __init int kvm_spinlock_init_jump(void)
index 42caaef897c86987a75796c1b6edea79d7f164cb..49487b4880616a225427c99d8eb7c498da36bae3 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/percpu.h>
 #include <linux/hardirq.h>
 #include <linux/memblock.h>
+#include <linux/sched.h>
 
 #include <asm/x86_init.h>
 #include <asm/reboot.h>
@@ -217,8 +218,10 @@ static void kvm_shutdown(void)
 
 void __init kvmclock_init(void)
 {
+       struct pvclock_vcpu_time_info *vcpu_time;
        unsigned long mem;
-       int size;
+       int size, cpu;
+       u8 flags;
 
        size = PAGE_ALIGN(sizeof(struct pvclock_vsyscall_time_info)*NR_CPUS);
 
@@ -264,7 +267,14 @@ void __init kvmclock_init(void)
        pv_info.name = "KVM";
 
        if (kvm_para_has_feature(KVM_FEATURE_CLOCKSOURCE_STABLE_BIT))
-               pvclock_set_flags(PVCLOCK_TSC_STABLE_BIT);
+               pvclock_set_flags(~0);
+
+       cpu = get_cpu();
+       vcpu_time = &hv_clock[cpu].pvti;
+       flags = pvclock_read_flags(vcpu_time);
+       if (flags & PVCLOCK_COUNTS_FROM_ZERO)
+               set_sched_clock_stable();
+       put_cpu();
 }
 
 int __init kvm_setup_vsyscall_timeinfo(void)
index 415480d3ea848bcf95e7ae26b56a5261add1cc8c..819ab3f9c9c7cb1476007619b5e6998c528be30f 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/ftrace.h>
 #include <linux/io.h>
 #include <linux/suspend.h>
+#include <linux/vmalloc.h>
 
 #include <asm/init.h>
 #include <asm/pgtable.h>
@@ -25,6 +26,7 @@
 #include <asm/io_apic.h>
 #include <asm/debugreg.h>
 #include <asm/kexec-bzimage64.h>
+#include <asm/setup.h>
 
 #ifdef CONFIG_KEXEC_FILE
 static struct kexec_file_ops *kexec_file_loaders[] = {
@@ -334,7 +336,7 @@ void arch_crash_save_vmcoreinfo(void)
        VMCOREINFO_LENGTH(node_data, MAX_NUMNODES);
 #endif
        vmcoreinfo_append_str("KERNELOFFSET=%lx\n",
-                             (unsigned long)&_text - __START_KERNEL);
+                             kaslr_offset());
 }
 
 /* arch-dependent functionality related to kexec file-based syscall */
index 2d2a237f2c73698a4dd2819800edd6179239904b..30ca7607cbbbbcae4793aa5c14d8f73bbd784d71 100644 (file)
@@ -19,8 +19,8 @@
 #include <linux/module.h>
 #include <linux/smp.h>
 #include <linux/pci.h>
-#include <linux/irqdomain.h>
 
+#include <asm/irqdomain.h>
 #include <asm/mtrr.h>
 #include <asm/mpspec.h>
 #include <asm/pgalloc.h>
@@ -113,11 +113,6 @@ static void __init MP_bus_info(struct mpc_bus *m)
                pr_warn("Unknown bustype %s - ignoring\n", str);
 }
 
-static struct irq_domain_ops mp_ioapic_irqdomain_ops = {
-       .map = mp_irqdomain_map,
-       .unmap = mp_irqdomain_unmap,
-};
-
 static void __init MP_ioapic_info(struct mpc_ioapic *m)
 {
        struct ioapic_domain_cfg cfg = {
index bbb6c7316341f806dc3cb3a5bdce52cbb28de2a3..33ee3e0efd65bccc9ca049b07a97e81428ead282 100644 (file)
@@ -8,11 +8,33 @@
 
 #include <asm/paravirt.h>
 
+#ifdef CONFIG_QUEUED_SPINLOCKS
+__visible void __native_queued_spin_unlock(struct qspinlock *lock)
+{
+       native_queued_spin_unlock(lock);
+}
+
+PV_CALLEE_SAVE_REGS_THUNK(__native_queued_spin_unlock);
+
+bool pv_is_native_spin_unlock(void)
+{
+       return pv_lock_ops.queued_spin_unlock.func ==
+               __raw_callee_save___native_queued_spin_unlock;
+}
+#endif
+
 struct pv_lock_ops pv_lock_ops = {
 #ifdef CONFIG_SMP
+#ifdef CONFIG_QUEUED_SPINLOCKS
+       .queued_spin_lock_slowpath = native_queued_spin_lock_slowpath,
+       .queued_spin_unlock = PV_CALLEE_SAVE(__native_queued_spin_unlock),
+       .wait = paravirt_nop,
+       .kick = paravirt_nop,
+#else /* !CONFIG_QUEUED_SPINLOCKS */
        .lock_spinning = __PV_IS_CALLEE_SAVE(paravirt_nop),
        .unlock_kick = paravirt_nop,
-#endif
+#endif /* !CONFIG_QUEUED_SPINLOCKS */
+#endif /* SMP */
 };
 EXPORT_SYMBOL(pv_lock_ops);
 
index c614dd492f5f720058346a33883f5a67d4689b94..58bcfb67c01f1f1f9b60a6e87bf5fd11b80bd281 100644 (file)
@@ -154,7 +154,9 @@ unsigned paravirt_patch_default(u8 type, u16 clobbers, void *insnbuf,
                ret = paravirt_patch_ident_64(insnbuf, len);
 
        else if (type == PARAVIRT_PATCH(pv_cpu_ops.iret) ||
+#ifdef CONFIG_X86_32
                 type == PARAVIRT_PATCH(pv_cpu_ops.irq_enable_sysexit) ||
+#endif
                 type == PARAVIRT_PATCH(pv_cpu_ops.usergs_sysret32) ||
                 type == PARAVIRT_PATCH(pv_cpu_ops.usergs_sysret64))
                /* If operation requires a jmp, then jmp */
@@ -371,7 +373,7 @@ __visible struct pv_cpu_ops pv_cpu_ops = {
 
        .load_sp0 = native_load_sp0,
 
-#if defined(CONFIG_X86_32) || defined(CONFIG_IA32_EMULATION)
+#if defined(CONFIG_X86_32)
        .irq_enable_sysexit = native_irq_enable_sysexit,
 #endif
 #ifdef CONFIG_X86_64
index d9f32e6d6ab65476be60c34d06c522215b83a26b..e1b013696dde586ba1a80f23535ac07b1bee3e81 100644 (file)
@@ -12,6 +12,10 @@ DEF_NATIVE(pv_mmu_ops, read_cr3, "mov %cr3, %eax");
 DEF_NATIVE(pv_cpu_ops, clts, "clts");
 DEF_NATIVE(pv_cpu_ops, read_tsc, "rdtsc");
 
+#if defined(CONFIG_PARAVIRT_SPINLOCKS) && defined(CONFIG_QUEUED_SPINLOCKS)
+DEF_NATIVE(pv_lock_ops, queued_spin_unlock, "movb $0, (%eax)");
+#endif
+
 unsigned paravirt_patch_ident_32(void *insnbuf, unsigned len)
 {
        /* arg in %eax, return in %eax */
@@ -24,6 +28,8 @@ unsigned paravirt_patch_ident_64(void *insnbuf, unsigned len)
        return 0;
 }
 
+extern bool pv_is_native_spin_unlock(void);
+
 unsigned native_patch(u8 type, u16 clobbers, void *ibuf,
                      unsigned long addr, unsigned len)
 {
@@ -47,14 +53,22 @@ unsigned native_patch(u8 type, u16 clobbers, void *ibuf,
                PATCH_SITE(pv_mmu_ops, write_cr3);
                PATCH_SITE(pv_cpu_ops, clts);
                PATCH_SITE(pv_cpu_ops, read_tsc);
-
-       patch_site:
-               ret = paravirt_patch_insns(ibuf, len, start, end);
-               break;
+#if defined(CONFIG_PARAVIRT_SPINLOCKS) && defined(CONFIG_QUEUED_SPINLOCKS)
+               case PARAVIRT_PATCH(pv_lock_ops.queued_spin_unlock):
+                       if (pv_is_native_spin_unlock()) {
+                               start = start_pv_lock_ops_queued_spin_unlock;
+                               end   = end_pv_lock_ops_queued_spin_unlock;
+                               goto patch_site;
+                       }
+#endif
 
        default:
                ret = paravirt_patch_default(type, clobbers, ibuf, addr, len);
                break;
+
+patch_site:
+               ret = paravirt_patch_insns(ibuf, len, start, end);
+               break;
        }
 #undef PATCH_SITE
        return ret;
index a1da6737ba5b80c4ee636204d49d4813348ef903..8aa05583bc42dec102b3fffb63d2fa6a828eee4c 100644 (file)
@@ -21,6 +21,10 @@ DEF_NATIVE(pv_cpu_ops, swapgs, "swapgs");
 DEF_NATIVE(, mov32, "mov %edi, %eax");
 DEF_NATIVE(, mov64, "mov %rdi, %rax");
 
+#if defined(CONFIG_PARAVIRT_SPINLOCKS) && defined(CONFIG_QUEUED_SPINLOCKS)
+DEF_NATIVE(pv_lock_ops, queued_spin_unlock, "movb $0, (%rdi)");
+#endif
+
 unsigned paravirt_patch_ident_32(void *insnbuf, unsigned len)
 {
        return paravirt_patch_insns(insnbuf, len,
@@ -33,6 +37,8 @@ unsigned paravirt_patch_ident_64(void *insnbuf, unsigned len)
                                    start__mov64, end__mov64);
 }
 
+extern bool pv_is_native_spin_unlock(void);
+
 unsigned native_patch(u8 type, u16 clobbers, void *ibuf,
                      unsigned long addr, unsigned len)
 {
@@ -49,7 +55,6 @@ unsigned native_patch(u8 type, u16 clobbers, void *ibuf,
                PATCH_SITE(pv_irq_ops, save_fl);
                PATCH_SITE(pv_irq_ops, irq_enable);
                PATCH_SITE(pv_irq_ops, irq_disable);
-               PATCH_SITE(pv_cpu_ops, irq_enable_sysexit);
                PATCH_SITE(pv_cpu_ops, usergs_sysret32);
                PATCH_SITE(pv_cpu_ops, usergs_sysret64);
                PATCH_SITE(pv_cpu_ops, swapgs);
@@ -59,14 +64,22 @@ unsigned native_patch(u8 type, u16 clobbers, void *ibuf,
                PATCH_SITE(pv_cpu_ops, clts);
                PATCH_SITE(pv_mmu_ops, flush_tlb_single);
                PATCH_SITE(pv_cpu_ops, wbinvd);
-
-       patch_site:
-               ret = paravirt_patch_insns(ibuf, len, start, end);
-               break;
+#if defined(CONFIG_PARAVIRT_SPINLOCKS) && defined(CONFIG_QUEUED_SPINLOCKS)
+               case PARAVIRT_PATCH(pv_lock_ops.queued_spin_unlock):
+                       if (pv_is_native_spin_unlock()) {
+                               start = start_pv_lock_ops_queued_spin_unlock;
+                               end   = end_pv_lock_ops_queued_spin_unlock;
+                               goto patch_site;
+                       }
+#endif
 
        default:
                ret = paravirt_patch_default(type, clobbers, ibuf, addr, len);
                break;
+
+patch_site:
+               ret = paravirt_patch_insns(ibuf, len, start, end);
+               break;
        }
 #undef PATCH_SITE
        return ret;
index a25e202bb319caf87ce147831f6a1a47ed03e254..353972c1946cd35f378054439a05bed8200f92c9 100644 (file)
@@ -140,6 +140,51 @@ void dma_generic_free_coherent(struct device *dev, size_t size, void *vaddr,
                free_pages((unsigned long)vaddr, get_order(size));
 }
 
+void *dma_alloc_attrs(struct device *dev, size_t size, dma_addr_t *dma_handle,
+                     gfp_t gfp, struct dma_attrs *attrs)
+{
+       struct dma_map_ops *ops = get_dma_ops(dev);
+       void *memory;
+
+       gfp &= ~(__GFP_DMA | __GFP_HIGHMEM | __GFP_DMA32);
+
+       if (dma_alloc_from_coherent(dev, size, dma_handle, &memory))
+               return memory;
+
+       if (!dev)
+               dev = &x86_dma_fallback_dev;
+
+       if (!is_device_dma_capable(dev))
+               return NULL;
+
+       if (!ops->alloc)
+               return NULL;
+
+       memory = ops->alloc(dev, size, dma_handle,
+                           dma_alloc_coherent_gfp_flags(dev, gfp), attrs);
+       debug_dma_alloc_coherent(dev, size, *dma_handle, memory);
+
+       return memory;
+}
+EXPORT_SYMBOL(dma_alloc_attrs);
+
+void dma_free_attrs(struct device *dev, size_t size,
+                   void *vaddr, dma_addr_t bus,
+                   struct dma_attrs *attrs)
+{
+       struct dma_map_ops *ops = get_dma_ops(dev);
+
+       WARN_ON(irqs_disabled());       /* for portability */
+
+       if (dma_release_from_coherent(dev, get_order(size), vaddr))
+               return;
+
+       debug_dma_free_coherent(dev, size, vaddr, bus);
+       if (ops->free)
+               ops->free(dev, size, vaddr, bus, attrs);
+}
+EXPORT_SYMBOL(dma_free_attrs);
+
 /*
  * See <Documentation/x86/x86_64/boot-options.txt> for the iommu kernel
  * parameter documentation.
index 77dd0ad58be4a6c9c8af113189c9805c1a535633..adf0392d549aa465e8f3e0ac336ea6f0e5967f1c 100644 (file)
@@ -20,6 +20,13 @@ void *x86_swiotlb_alloc_coherent(struct device *hwdev, size_t size,
 {
        void *vaddr;
 
+       /*
+        * Don't print a warning when the first allocation attempt fails.
+        * swiotlb_alloc_coherent() will print a warning when the DMA
+        * memory allocation ultimately failed.
+        */
+       flags |= __GFP_NOWARN;
+
        vaddr = dma_generic_alloc_coherent(hwdev, size, dma_handle, flags,
                                           attrs);
        if (vaddr)
index c648139d68d756469ddd8b72b1c0d4e2a4cea72b..9cad694ed7c4d6a755b34af705e0a055cb0c04aa 100644 (file)
@@ -25,8 +25,7 @@
 #include <asm/idle.h>
 #include <asm/uaccess.h>
 #include <asm/mwait.h>
-#include <asm/i387.h>
-#include <asm/fpu-internal.h>
+#include <asm/fpu/internal.h>
 #include <asm/debugreg.h>
 #include <asm/nmi.h>
 #include <asm/tlbflush.h>
@@ -76,9 +75,6 @@ void idle_notifier_unregister(struct notifier_block *n)
 EXPORT_SYMBOL_GPL(idle_notifier_unregister);
 #endif
 
-struct kmem_cache *task_xstate_cachep;
-EXPORT_SYMBOL_GPL(task_xstate_cachep);
-
 /*
  * this gets called so that we can store lazy state into memory and copy the
  * current task into the new thread.
@@ -87,36 +83,7 @@ int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src)
 {
        *dst = *src;
 
-       dst->thread.fpu_counter = 0;
-       dst->thread.fpu.has_fpu = 0;
-       dst->thread.fpu.state = NULL;
-       task_disable_lazy_fpu_restore(dst);
-       if (tsk_used_math(src)) {
-               int err = fpu_alloc(&dst->thread.fpu);
-               if (err)
-                       return err;
-               fpu_copy(dst, src);
-       }
-       return 0;
-}
-
-void free_thread_xstate(struct task_struct *tsk)
-{
-       fpu_free(&tsk->thread.fpu);
-}
-
-void arch_release_task_struct(struct task_struct *tsk)
-{
-       free_thread_xstate(tsk);
-}
-
-void arch_task_cache_init(void)
-{
-        task_xstate_cachep =
-               kmem_cache_create("task_xstate", xstate_size,
-                                 __alignof__(union thread_xstate),
-                                 SLAB_PANIC | SLAB_NOTRACK, NULL);
-       setup_xstate_comp();
+       return fpu__copy(&dst->thread.fpu, &src->thread.fpu);
 }
 
 /*
@@ -127,6 +94,7 @@ void exit_thread(void)
        struct task_struct *me = current;
        struct thread_struct *t = &me->thread;
        unsigned long *bp = t->io_bitmap_ptr;
+       struct fpu *fpu = &t->fpu;
 
        if (bp) {
                struct tss_struct *tss = &per_cpu(cpu_tss, get_cpu());
@@ -142,7 +110,7 @@ void exit_thread(void)
                kfree(bp);
        }
 
-       drop_fpu(me);
+       fpu__drop(fpu);
 }
 
 void flush_thread(void)
@@ -152,19 +120,7 @@ void flush_thread(void)
        flush_ptrace_hw_breakpoint(tsk);
        memset(tsk->thread.tls_array, 0, sizeof(tsk->thread.tls_array));
 
-       if (!use_eager_fpu()) {
-               /* FPU state will be reallocated lazily at the first use. */
-               drop_fpu(tsk);
-               free_thread_xstate(tsk);
-       } else {
-               if (!tsk_used_math(tsk)) {
-                       /* kthread execs. TODO: cleanup this horror. */
-                       if (WARN_ON(init_fpu(tsk)))
-                               force_sig(SIGKILL, tsk);
-                       user_fpu_begin();
-               }
-               restore_init_xstate();
-       }
+       fpu__clear(&tsk->thread.fpu);
 }
 
 static void hard_disable_TSC(void)
index 8ed2106b06da63e0a8e0dcf561aad7a1fc112e40..c09c99ccf3e33fc5afff500b6310e2e6f9150526 100644 (file)
@@ -39,8 +39,7 @@
 #include <asm/pgtable.h>
 #include <asm/ldt.h>
 #include <asm/processor.h>
-#include <asm/i387.h>
-#include <asm/fpu-internal.h>
+#include <asm/fpu/internal.h>
 #include <asm/desc.h>
 #ifdef CONFIG_MATH_EMULATION
 #include <asm/math_emu.h>
@@ -242,14 +241,16 @@ __visible __notrace_funcgraph struct task_struct *
 __switch_to(struct task_struct *prev_p, struct task_struct *next_p)
 {
        struct thread_struct *prev = &prev_p->thread,
-                                *next = &next_p->thread;
+                            *next = &next_p->thread;
+       struct fpu *prev_fpu = &prev->fpu;
+       struct fpu *next_fpu = &next->fpu;
        int cpu = smp_processor_id();
        struct tss_struct *tss = &per_cpu(cpu_tss, cpu);
-       fpu_switch_t fpu;
+       fpu_switch_t fpu_switch;
 
        /* never put a printk in __switch_to... printk() calls wake_up*() indirectly */
 
-       fpu = switch_fpu_prepare(prev_p, next_p, cpu);
+       fpu_switch = switch_fpu_prepare(prev_fpu, next_fpu, cpu);
 
        /*
         * Save away %gs. No need to save %fs, as it was saved on the
@@ -296,19 +297,16 @@ __switch_to(struct task_struct *prev_p, struct task_struct *next_p)
         * Leave lazy mode, flushing any hypercalls made here.
         * This must be done before restoring TLS segments so
         * the GDT and LDT are properly updated, and must be
-        * done before math_state_restore, so the TS bit is up
+        * done before fpu__restore(), so the TS bit is up
         * to date.
         */
        arch_end_context_switch(next_p);
 
        /*
-        * Reload esp0, kernel_stack, and current_top_of_stack.  This changes
+        * Reload esp0 and cpu_current_top_of_stack.  This changes
         * current_thread_info().
         */
        load_sp0(tss, next);
-       this_cpu_write(kernel_stack,
-                      (unsigned long)task_stack_page(next_p) +
-                      THREAD_SIZE);
        this_cpu_write(cpu_current_top_of_stack,
                       (unsigned long)task_stack_page(next_p) +
                       THREAD_SIZE);
@@ -319,7 +317,7 @@ __switch_to(struct task_struct *prev_p, struct task_struct *next_p)
        if (prev->gs | next->gs)
                lazy_load_gs(next->gs);
 
-       switch_fpu_finish(next_p, fpu);
+       switch_fpu_finish(next_fpu, fpu_switch);
 
        this_cpu_write(current_task, next_p);
 
index ddfdbf74f1744c235fb80ee2ee0718152882b2ce..843f92e4c7110cd621fb94dfca8cb980044fa32d 100644 (file)
@@ -38,8 +38,7 @@
 
 #include <asm/pgtable.h>
 #include <asm/processor.h>
-#include <asm/i387.h>
-#include <asm/fpu-internal.h>
+#include <asm/fpu/internal.h>
 #include <asm/mmu_context.h>
 #include <asm/prctl.h>
 #include <asm/desc.h>
@@ -274,12 +273,14 @@ __switch_to(struct task_struct *prev_p, struct task_struct *next_p)
 {
        struct thread_struct *prev = &prev_p->thread;
        struct thread_struct *next = &next_p->thread;
+       struct fpu *prev_fpu = &prev->fpu;
+       struct fpu *next_fpu = &next->fpu;
        int cpu = smp_processor_id();
        struct tss_struct *tss = &per_cpu(cpu_tss, cpu);
        unsigned fsindex, gsindex;
-       fpu_switch_t fpu;
+       fpu_switch_t fpu_switch;
 
-       fpu = switch_fpu_prepare(prev_p, next_p, cpu);
+       fpu_switch = switch_fpu_prepare(prev_fpu, next_fpu, cpu);
 
        /* We must save %fs and %gs before load_TLS() because
         * %fs and %gs may be cleared by load_TLS().
@@ -299,7 +300,7 @@ __switch_to(struct task_struct *prev_p, struct task_struct *next_p)
         * Leave lazy mode, flushing any hypercalls made here.  This
         * must be done after loading TLS entries in the GDT but before
         * loading segments that might reference them, and and it must
-        * be done before math_state_restore, so the TS bit is up to
+        * be done before fpu__restore(), so the TS bit is up to
         * date.
         */
        arch_end_context_switch(next_p);
@@ -391,7 +392,7 @@ __switch_to(struct task_struct *prev_p, struct task_struct *next_p)
                wrmsrl(MSR_KERNEL_GS_BASE, next->gs);
        prev->gsindex = gsindex;
 
-       switch_fpu_finish(next_p, fpu);
+       switch_fpu_finish(next_fpu, fpu_switch);
 
        /*
         * Switch the PDA and FPU contexts.
@@ -409,9 +410,6 @@ __switch_to(struct task_struct *prev_p, struct task_struct *next_p)
        /* Reload esp0 and ss1.  This changes current_thread_info(). */
        load_sp0(tss, next);
 
-       this_cpu_write(kernel_stack,
-               (unsigned long)task_stack_page(next_p) + THREAD_SIZE);
-
        /*
         * Now maybe reload the debug registers and handle I/O bitmaps
         */
index a7bc794807195af79b6c15054b1941867d373198..9be72bc3613f80a9b5a5ae1bbf01adefdcfc29ce 100644 (file)
@@ -11,7 +11,6 @@
 #include <linux/errno.h>
 #include <linux/slab.h>
 #include <linux/ptrace.h>
-#include <linux/regset.h>
 #include <linux/tracehook.h>
 #include <linux/user.h>
 #include <linux/elf.h>
@@ -28,8 +27,9 @@
 #include <asm/uaccess.h>
 #include <asm/pgtable.h>
 #include <asm/processor.h>
-#include <asm/i387.h>
-#include <asm/fpu-internal.h>
+#include <asm/fpu/internal.h>
+#include <asm/fpu/signal.h>
+#include <asm/fpu/regset.h>
 #include <asm/debugreg.h>
 #include <asm/ldt.h>
 #include <asm/desc.h>
@@ -1297,7 +1297,7 @@ static struct user_regset x86_64_regsets[] __read_mostly = {
                .core_note_type = NT_PRFPREG,
                .n = sizeof(struct user_i387_struct) / sizeof(long),
                .size = sizeof(long), .align = sizeof(long),
-               .active = xfpregs_active, .get = xfpregs_get, .set = xfpregs_set
+               .active = regset_xregset_fpregs_active, .get = xfpregs_get, .set = xfpregs_set
        },
        [REGSET_XSTATE] = {
                .core_note_type = NT_X86_XSTATE,
@@ -1338,13 +1338,13 @@ static struct user_regset x86_32_regsets[] __read_mostly = {
                .core_note_type = NT_PRFPREG,
                .n = sizeof(struct user_i387_ia32_struct) / sizeof(u32),
                .size = sizeof(u32), .align = sizeof(u32),
-               .active = fpregs_active, .get = fpregs_get, .set = fpregs_set
+               .active = regset_fpregs_active, .get = fpregs_get, .set = fpregs_set
        },
        [REGSET_XFP] = {
                .core_note_type = NT_PRXFPREG,
                .n = sizeof(struct user32_fxsr_struct) / sizeof(u32),
                .size = sizeof(u32), .align = sizeof(u32),
-               .active = xfpregs_active, .get = xfpregs_get, .set = xfpregs_set
+               .active = regset_xregset_fpregs_active, .get = xfpregs_get, .set = xfpregs_set
        },
        [REGSET_XSTATE] = {
                .core_note_type = NT_X86_XSTATE,
index d74ac33290ae3eeef46b923c4556d644b72f0d5a..39ca113676fe54a73ffe3d01276e89cbc2252b62 100644 (file)
@@ -531,12 +531,14 @@ static void __init reserve_crashkernel_low(void)
        if (ret != 0) {
                /*
                 * two parts from lib/swiotlb.c:
-                *      swiotlb size: user specified with swiotlb= or default.
-                *      swiotlb overflow buffer: now is hardcoded to 32k.
-                *              We round it to 8M for other buffers that
-                *              may need to stay low too.
+                * -swiotlb size: user-specified with swiotlb= or default.
+                *
+                * -swiotlb overflow buffer: now hardcoded to 32k. We round it
+                * to 8M for other buffers that may need to stay low too. Also
+                * make sure we allocate enough extra low memory so that we
+                * don't run out of DMA buffers for 32-bit devices.
                 */
-               low_size = swiotlb_size_or_default() + (8UL<<20);
+               low_size = max(swiotlb_size_or_default() + (8UL<<20), 256UL<<20);
                auto_set = true;
        } else {
                /* passed with crashkernel=0,low ? */
@@ -834,7 +836,7 @@ dump_kernel_offset(struct notifier_block *self, unsigned long v, void *p)
 {
        if (kaslr_enabled()) {
                pr_emerg("Kernel Offset: 0x%lx from 0x%lx (relocation range: 0x%lx-0x%lx)\n",
-                        (unsigned long)&_text - __START_KERNEL,
+                        kaslr_offset(),
                         __START_KERNEL,
                         __START_KERNEL_map,
                         MODULES_VADDR-1);
@@ -1222,8 +1224,7 @@ void __init setup_arch(char **cmdline_p)
        init_cpu_to_node();
 
        init_apic_mappings();
-       if (x86_io_apic_ops.init)
-               x86_io_apic_ops.init();
+       io_apic_init_mappings();
 
        kvm_guest_init();
 
index 1ea14fd53933bae96dc5f8e3fec99575a3c3c454..206996c1669db344aba7ff072f734552723e7938 100644 (file)
@@ -26,8 +26,8 @@
 
 #include <asm/processor.h>
 #include <asm/ucontext.h>
-#include <asm/i387.h>
-#include <asm/fpu-internal.h>
+#include <asm/fpu/internal.h>
+#include <asm/fpu/signal.h>
 #include <asm/vdso.h>
 #include <asm/mce.h>
 #include <asm/sighandling.h>
@@ -103,7 +103,7 @@ int restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc)
                get_user_ex(buf, &sc->fpstate);
        } get_user_catch(err);
 
-       err |= restore_xstate_sig(buf, config_enabled(CONFIG_X86_32));
+       err |= fpu__restore_sig(buf, config_enabled(CONFIG_X86_32));
 
        force_iret();
 
@@ -199,6 +199,7 @@ get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, size_t frame_size,
        unsigned long sp = regs->sp;
        unsigned long buf_fx = 0;
        int onsigstack = on_sig_stack(sp);
+       struct fpu *fpu = &current->thread.fpu;
 
        /* redzone */
        if (config_enabled(CONFIG_X86_64))
@@ -218,9 +219,9 @@ get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, size_t frame_size,
                }
        }
 
-       if (used_math()) {
-               sp = alloc_mathframe(sp, config_enabled(CONFIG_X86_32),
-                                    &buf_fx, &math_size);
+       if (fpu->fpstate_active) {
+               sp = fpu__alloc_mathframe(sp, config_enabled(CONFIG_X86_32),
+                                         &buf_fx, &math_size);
                *fpstate = (void __user *)sp;
        }
 
@@ -234,8 +235,8 @@ get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, size_t frame_size,
                return (void __user *)-1L;
 
        /* save i387 and extended state */
-       if (used_math() &&
-           save_xstate_sig(*fpstate, (void __user *)buf_fx, math_size) < 0)
+       if (fpu->fpstate_active &&
+           copy_fpstate_to_sigframe(*fpstate, (void __user *)buf_fx, math_size) < 0)
                return (void __user *)-1L;
 
        return (void __user *)sp;
@@ -593,6 +594,22 @@ badframe:
        return 0;
 }
 
+static inline int is_ia32_compat_frame(void)
+{
+       return config_enabled(CONFIG_IA32_EMULATION) &&
+              test_thread_flag(TIF_IA32);
+}
+
+static inline int is_ia32_frame(void)
+{
+       return config_enabled(CONFIG_X86_32) || is_ia32_compat_frame();
+}
+
+static inline int is_x32_frame(void)
+{
+       return config_enabled(CONFIG_X86_X32_ABI) && test_thread_flag(TIF_X32);
+}
+
 static int
 setup_rt_frame(struct ksignal *ksig, struct pt_regs *regs)
 {
@@ -617,6 +634,7 @@ static void
 handle_signal(struct ksignal *ksig, struct pt_regs *regs)
 {
        bool stepping, failed;
+       struct fpu *fpu = &current->thread.fpu;
 
        /* Are we from a system call? */
        if (syscall_get_nr(current, regs) >= 0) {
@@ -665,8 +683,8 @@ handle_signal(struct ksignal *ksig, struct pt_regs *regs)
                /*
                 * Ensure the signal handler starts with the new fpu state.
                 */
-               if (used_math())
-                       fpu_reset_state(current);
+               if (fpu->fpstate_active)
+                       fpu__clear(fpu);
        }
        signal_setup_done(failed, ksig, stepping);
 }
index be8e1bde07aa47ff373f0245e0f4b7d6d2edcfd5..15aaa69bbb5eff9596e49000b38b90abce636052 100644 (file)
@@ -170,8 +170,7 @@ static int smp_stop_nmi_callback(unsigned int val, struct pt_regs *regs)
 
 asmlinkage __visible void smp_reboot_interrupt(void)
 {
-       ack_APIC_irq();
-       irq_enter();
+       ipi_entering_ack_irq();
        stop_this_cpu(NULL);
        irq_exit();
 }
@@ -265,12 +264,6 @@ __visible void smp_reschedule_interrupt(struct pt_regs *regs)
         */
 }
 
-static inline void smp_entering_irq(void)
-{
-       ack_APIC_irq();
-       irq_enter();
-}
-
 __visible void smp_trace_reschedule_interrupt(struct pt_regs *regs)
 {
        /*
@@ -279,7 +272,7 @@ __visible void smp_trace_reschedule_interrupt(struct pt_regs *regs)
         * scheduler_ipi(). This is OK, since those functions are allowed
         * to nest.
         */
-       smp_entering_irq();
+       ipi_entering_ack_irq();
        trace_reschedule_entry(RESCHEDULE_VECTOR);
        __smp_reschedule_interrupt();
        trace_reschedule_exit(RESCHEDULE_VECTOR);
@@ -297,14 +290,14 @@ static inline void __smp_call_function_interrupt(void)
 
 __visible void smp_call_function_interrupt(struct pt_regs *regs)
 {
-       smp_entering_irq();
+       ipi_entering_ack_irq();
        __smp_call_function_interrupt();
        exiting_irq();
 }
 
 __visible void smp_trace_call_function_interrupt(struct pt_regs *regs)
 {
-       smp_entering_irq();
+       ipi_entering_ack_irq();
        trace_call_function_entry(CALL_FUNCTION_VECTOR);
        __smp_call_function_interrupt();
        trace_call_function_exit(CALL_FUNCTION_VECTOR);
@@ -319,14 +312,14 @@ static inline void __smp_call_function_single_interrupt(void)
 
 __visible void smp_call_function_single_interrupt(struct pt_regs *regs)
 {
-       smp_entering_irq();
+       ipi_entering_ack_irq();
        __smp_call_function_single_interrupt();
        exiting_irq();
 }
 
 __visible void smp_trace_call_function_single_interrupt(struct pt_regs *regs)
 {
-       smp_entering_irq();
+       ipi_entering_ack_irq();
        trace_call_function_single_entry(CALL_FUNCTION_SINGLE_VECTOR);
        __smp_call_function_single_interrupt();
        trace_call_function_single_exit(CALL_FUNCTION_SINGLE_VECTOR);
index 0e8209619455f50d8aa1743eac105564a88c065b..8add66b22f333cbc5e8936b41e64525b7f49e1bc 100644 (file)
@@ -68,8 +68,7 @@
 #include <asm/mwait.h>
 #include <asm/apic.h>
 #include <asm/io_apic.h>
-#include <asm/i387.h>
-#include <asm/fpu-internal.h>
+#include <asm/fpu/internal.h>
 #include <asm/setup.h>
 #include <asm/uv/uv.h>
 #include <linux/mc146818rtc.h>
@@ -515,6 +514,40 @@ void __inquire_remote_apic(int apicid)
        }
 }
 
+/*
+ * The Multiprocessor Specification 1.4 (1997) example code suggests
+ * that there should be a 10ms delay between the BSP asserting INIT
+ * and de-asserting INIT, when starting a remote processor.
+ * But that slows boot and resume on modern processors, which include
+ * many cores and don't require that delay.
+ *
+ * Cmdline "init_cpu_udelay=" is available to over-ride this delay.
+ * Modern processor families are quirked to remove the delay entirely.
+ */
+#define UDELAY_10MS_DEFAULT 10000
+
+static unsigned int init_udelay = UDELAY_10MS_DEFAULT;
+
+static int __init cpu_init_udelay(char *str)
+{
+       get_option(&str, &init_udelay);
+
+       return 0;
+}
+early_param("cpu_init_udelay", cpu_init_udelay);
+
+static void __init smp_quirk_init_udelay(void)
+{
+       /* if cmdline changed it from default, leave it alone */
+       if (init_udelay != UDELAY_10MS_DEFAULT)
+               return;
+
+       /* if modern processor, use no delay */
+       if (((boot_cpu_data.x86_vendor == X86_VENDOR_INTEL) && (boot_cpu_data.x86 == 6)) ||
+           ((boot_cpu_data.x86_vendor == X86_VENDOR_AMD) && (boot_cpu_data.x86 >= 0xF)))
+               init_udelay = 0;
+}
+
 /*
  * Poke the other CPU in the eye via NMI to wake it up. Remember that the normal
  * INIT, INIT, STARTUP sequence will reset the chip hard for us, and this
@@ -557,7 +590,7 @@ wakeup_secondary_cpu_via_nmi(int apicid, unsigned long start_eip)
 static int
 wakeup_secondary_cpu_via_init(int phys_apicid, unsigned long start_eip)
 {
-       unsigned long send_status, accept_status = 0;
+       unsigned long send_status = 0, accept_status = 0;
        int maxlvt, num_starts, j;
 
        maxlvt = lapic_get_maxlvt();
@@ -585,7 +618,7 @@ wakeup_secondary_cpu_via_init(int phys_apicid, unsigned long start_eip)
        pr_debug("Waiting for send to finish...\n");
        send_status = safe_apic_wait_icr_idle();
 
-       mdelay(10);
+       udelay(init_udelay);
 
        pr_debug("Deasserting INIT\n");
 
@@ -653,6 +686,7 @@ wakeup_secondary_cpu_via_init(int phys_apicid, unsigned long start_eip)
                 * Give the other CPU some time to accept the IPI.
                 */
                udelay(200);
+
                if (maxlvt > 3)         /* Due to the Pentium erratum 3AP.  */
                        apic_write(APIC_ESR, 0);
                accept_status = (apic_read(APIC_ESR) & 0xEF);
@@ -794,8 +828,6 @@ void common_cpu_up(unsigned int cpu, struct task_struct *idle)
        clear_tsk_thread_flag(idle, TIF_FORK);
        initial_gs = per_cpu_offset(cpu);
 #endif
-       per_cpu(kernel_stack, cpu) =
-               (unsigned long)task_stack_page(idle) + THREAD_SIZE;
 }
 
 /*
@@ -1178,6 +1210,8 @@ void __init native_smp_prepare_cpus(unsigned int max_cpus)
                uv_system_init();
 
        set_mtrr_aps_delayed_init();
+
+       smp_quirk_init_udelay();
 }
 
 void arch_enable_nonboot_cpus_begin(void)
index 324ab524768756b1987efbee11f06ce3ba24562b..f5791927aa644493354dd487a35a1111fdf676d6 100644 (file)
 #include <asm/ftrace.h>
 #include <asm/traps.h>
 #include <asm/desc.h>
-#include <asm/i387.h>
-#include <asm/fpu-internal.h>
+#include <asm/fpu/internal.h>
 #include <asm/mce.h>
 #include <asm/fixmap.h>
 #include <asm/mach_traps.h>
 #include <asm/alternative.h>
+#include <asm/fpu/xstate.h>
+#include <asm/trace/mpx.h>
 #include <asm/mpx.h>
 
 #ifdef CONFIG_X86_64
@@ -72,8 +73,7 @@ gate_desc debug_idt_table[NR_VECTORS] __page_aligned_bss;
 #else
 #include <asm/processor-flags.h>
 #include <asm/setup.h>
-
-asmlinkage int system_call(void);
+#include <asm/proto.h>
 #endif
 
 /* Must be page-aligned because the real IDT is used in a fixmap. */
@@ -371,10 +371,8 @@ dotraplinkage void do_double_fault(struct pt_regs *regs, long error_code)
 
 dotraplinkage void do_bounds(struct pt_regs *regs, long error_code)
 {
-       struct task_struct *tsk = current;
-       struct xsave_struct *xsave_buf;
        enum ctx_state prev_state;
-       struct bndcsr *bndcsr;
+       const struct bndcsr *bndcsr;
        siginfo_t *info;
 
        prev_state = exception_enter();
@@ -393,15 +391,15 @@ dotraplinkage void do_bounds(struct pt_regs *regs, long error_code)
 
        /*
         * We need to look at BNDSTATUS to resolve this exception.
-        * It is not directly accessible, though, so we need to
-        * do an xsave and then pull it out of the xsave buffer.
+        * A NULL here might mean that it is in its 'init state',
+        * which is all zeros which indicates MPX was not
+        * responsible for the exception.
         */
-       fpu_save_init(&tsk->thread.fpu);
-       xsave_buf = &(tsk->thread.fpu.state->xsave);
-       bndcsr = get_xsave_addr(xsave_buf, XSTATE_BNDCSR);
+       bndcsr = get_xsave_field_ptr(XSTATE_BNDCSR);
        if (!bndcsr)
                goto exit_trap;
 
+       trace_bounds_exception_mpx(bndcsr);
        /*
         * The error code field of the BNDSTATUS register communicates status
         * information of a bound range exception #BR or operation involving
@@ -409,11 +407,11 @@ dotraplinkage void do_bounds(struct pt_regs *regs, long error_code)
         */
        switch (bndcsr->bndstatus & MPX_BNDSTA_ERROR_CODE) {
        case 2: /* Bound directory has invalid entry. */
-               if (mpx_handle_bd_fault(xsave_buf))
+               if (mpx_handle_bd_fault())
                        goto exit_trap;
                break; /* Success, it was handled */
        case 1: /* Bound violation. */
-               info = mpx_generate_siginfo(regs, xsave_buf);
+               info = mpx_generate_siginfo(regs);
                if (IS_ERR(info)) {
                        /*
                         * We failed to decode the MPX instruction.  Act as if
@@ -709,8 +707,8 @@ NOKPROBE_SYMBOL(do_debug);
 static void math_error(struct pt_regs *regs, int error_code, int trapnr)
 {
        struct task_struct *task = current;
+       struct fpu *fpu = &task->thread.fpu;
        siginfo_t info;
-       unsigned short err;
        char *str = (trapnr == X86_TRAP_MF) ? "fpu exception" :
                                                "simd exception";
 
@@ -718,8 +716,7 @@ static void math_error(struct pt_regs *regs, int error_code, int trapnr)
                return;
        conditional_sti(regs);
 
-       if (!user_mode(regs))
-       {
+       if (!user_mode(regs)) {
                if (!fixup_exception(regs)) {
                        task->thread.error_code = error_code;
                        task->thread.trap_nr = trapnr;
@@ -731,62 +728,20 @@ static void math_error(struct pt_regs *regs, int error_code, int trapnr)
        /*
         * Save the info for the exception handler and clear the error.
         */
-       unlazy_fpu(task);
-       task->thread.trap_nr = trapnr;
+       fpu__save(fpu);
+
+       task->thread.trap_nr    = trapnr;
        task->thread.error_code = error_code;
-       info.si_signo = SIGFPE;
-       info.si_errno = 0;
-       info.si_addr = (void __user *)uprobe_get_trap_addr(regs);
-       if (trapnr == X86_TRAP_MF) {
-               unsigned short cwd, swd;
-               /*
-                * (~cwd & swd) will mask out exceptions that are not set to unmasked
-                * status.  0x3f is the exception bits in these regs, 0x200 is the
-                * C1 reg you need in case of a stack fault, 0x040 is the stack
-                * fault bit.  We should only be taking one exception at a time,
-                * so if this combination doesn't produce any single exception,
-                * then we have a bad program that isn't synchronizing its FPU usage
-                * and it will suffer the consequences since we won't be able to
-                * fully reproduce the context of the exception
-                */
-               cwd = get_fpu_cwd(task);
-               swd = get_fpu_swd(task);
+       info.si_signo           = SIGFPE;
+       info.si_errno           = 0;
+       info.si_addr            = (void __user *)uprobe_get_trap_addr(regs);
 
-               err = swd & ~cwd;
-       } else {
-               /*
-                * The SIMD FPU exceptions are handled a little differently, as there
-                * is only a single status/control register.  Thus, to determine which
-                * unmasked exception was caught we must mask the exception mask bits
-                * at 0x1f80, and then use these to mask the exception bits at 0x3f.
-                */
-               unsigned short mxcsr = get_fpu_mxcsr(task);
-               err = ~(mxcsr >> 7) & mxcsr;
-       }
+       info.si_code = fpu__exception_code(fpu, trapnr);
 
-       if (err & 0x001) {      /* Invalid op */
-               /*
-                * swd & 0x240 == 0x040: Stack Underflow
-                * swd & 0x240 == 0x240: Stack Overflow
-                * User must clear the SF bit (0x40) if set
-                */
-               info.si_code = FPE_FLTINV;
-       } else if (err & 0x004) { /* Divide by Zero */
-               info.si_code = FPE_FLTDIV;
-       } else if (err & 0x008) { /* Overflow */
-               info.si_code = FPE_FLTOVF;
-       } else if (err & 0x012) { /* Denormal, Underflow */
-               info.si_code = FPE_FLTUND;
-       } else if (err & 0x020) { /* Precision */
-               info.si_code = FPE_FLTRES;
-       } else {
-               /*
-                * If we're using IRQ 13, or supposedly even some trap
-                * X86_TRAP_MF implementations, it's possible
-                * we get a spurious trap, which is not an error.
-                */
+       /* Retry when we get spurious exceptions: */
+       if (!info.si_code)
                return;
-       }
+
        force_sig_info(SIGFPE, &info, task);
 }
 
@@ -813,62 +768,8 @@ dotraplinkage void
 do_spurious_interrupt_bug(struct pt_regs *regs, long error_code)
 {
        conditional_sti(regs);
-#if 0
-       /* No need to warn about this any longer. */
-       pr_info("Ignoring P6 Local APIC Spurious Interrupt Bug...\n");
-#endif
-}
-
-asmlinkage __visible void __attribute__((weak)) smp_thermal_interrupt(void)
-{
 }
 
-asmlinkage __visible void __attribute__((weak)) smp_threshold_interrupt(void)
-{
-}
-
-/*
- * 'math_state_restore()' saves the current math information in the
- * old math state array, and gets the new ones from the current task
- *
- * Careful.. There are problems with IBM-designed IRQ13 behaviour.
- * Don't touch unless you *really* know how it works.
- *
- * Must be called with kernel preemption disabled (eg with local
- * local interrupts as in the case of do_device_not_available).
- */
-void math_state_restore(void)
-{
-       struct task_struct *tsk = current;
-
-       if (!tsk_used_math(tsk)) {
-               local_irq_enable();
-               /*
-                * does a slab alloc which can sleep
-                */
-               if (init_fpu(tsk)) {
-                       /*
-                        * ran out of memory!
-                        */
-                       do_group_exit(SIGKILL);
-                       return;
-               }
-               local_irq_disable();
-       }
-
-       /* Avoid __kernel_fpu_begin() right after __thread_fpu_begin() */
-       kernel_fpu_disable();
-       __thread_fpu_begin(tsk);
-       if (unlikely(restore_fpu_checking(tsk))) {
-               fpu_reset_state(tsk);
-               force_sig_info(SIGSEGV, SEND_SIG_PRIV, tsk);
-       } else {
-               tsk->thread.fpu_counter++;
-       }
-       kernel_fpu_enable();
-}
-EXPORT_SYMBOL_GPL(math_state_restore);
-
 dotraplinkage void
 do_device_not_available(struct pt_regs *regs, long error_code)
 {
@@ -889,7 +790,7 @@ do_device_not_available(struct pt_regs *regs, long error_code)
                return;
        }
 #endif
-       math_state_restore(); /* interrupts still off */
+       fpu__restore(&current->thread.fpu); /* interrupts still off */
 #ifdef CONFIG_X86_32
        conditional_sti(regs);
 #endif
@@ -992,13 +893,13 @@ void __init trap_init(void)
                set_bit(i, used_vectors);
 
 #ifdef CONFIG_IA32_EMULATION
-       set_system_intr_gate(IA32_SYSCALL_VECTOR, ia32_syscall);
+       set_system_intr_gate(IA32_SYSCALL_VECTOR, entry_INT80_compat);
        set_bit(IA32_SYSCALL_VECTOR, used_vectors);
 #endif
 
 #ifdef CONFIG_X86_32
-       set_system_trap_gate(SYSCALL_VECTOR, &system_call);
-       set_bit(SYSCALL_VECTOR, used_vectors);
+       set_system_trap_gate(IA32_SYSCALL_VECTOR, entry_INT80_32);
+       set_bit(IA32_SYSCALL_VECTOR, used_vectors);
 #endif
 
        /*
index 0b81ad67da07fa36e57577de5f7165ab320a55a1..66476244731ef8fba8fafbe1bb6cbd17f1a18b9c 100644 (file)
@@ -29,6 +29,7 @@
 #include <linux/kdebug.h>
 #include <asm/processor.h>
 #include <asm/insn.h>
+#include <asm/mmu_context.h>
 
 /* Post-execution fixups. */
 
@@ -312,11 +313,6 @@ static int uprobe_init_insn(struct arch_uprobe *auprobe, struct insn *insn, bool
 }
 
 #ifdef CONFIG_X86_64
-static inline bool is_64bit_mm(struct mm_struct *mm)
-{
-       return  !config_enabled(CONFIG_IA32_EMULATION) ||
-               !(mm->context.ia32_compat == TIF_IA32);
-}
 /*
  * If arch_uprobe->insn doesn't use rip-relative addressing, return
  * immediately.  Otherwise, rewrite the instruction so that it accesses
@@ -497,10 +493,6 @@ static void riprel_post_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
        }
 }
 #else /* 32-bit: */
-static inline bool is_64bit_mm(struct mm_struct *mm)
-{
-       return false;
-}
 /*
  * No RIP-relative addressing on 32-bit
  */
index 234b0722de53522d6d20bd060478ea3e97f8cbb9..3839628d962e4f1baad89c124724413d8f17d8b2 100644 (file)
@@ -11,7 +11,6 @@
 #include <asm/bios_ebda.h>
 #include <asm/paravirt.h>
 #include <asm/pci_x86.h>
-#include <asm/pci.h>
 #include <asm/mpspec.h>
 #include <asm/setup.h>
 #include <asm/apic.h>
@@ -111,11 +110,9 @@ EXPORT_SYMBOL_GPL(x86_platform);
 #if defined(CONFIG_PCI_MSI)
 struct x86_msi_ops x86_msi = {
        .setup_msi_irqs         = native_setup_msi_irqs,
-       .compose_msi_msg        = native_compose_msi_msg,
        .teardown_msi_irq       = native_teardown_msi_irq,
        .teardown_msi_irqs      = default_teardown_msi_irqs,
        .restore_msi_irqs       = default_restore_msi_irqs,
-       .setup_hpet_msi         = default_setup_hpet_msi,
 };
 
 /* MSI arch specific hooks */
@@ -141,13 +138,6 @@ void arch_restore_msi_irqs(struct pci_dev *dev)
 #endif
 
 struct x86_io_apic_ops x86_io_apic_ops = {
-       .init                   = native_io_apic_init_mappings,
        .read                   = native_io_apic_read,
-       .write                  = native_io_apic_write,
-       .modify                 = native_io_apic_modify,
        .disable                = native_disable_io_apic,
-       .print_entries          = native_io_apic_print_entries,
-       .set_affinity           = native_ioapic_set_affinity,
-       .setup_entry            = native_setup_ioapic_entry,
-       .eoi_ioapic_pin         = native_eoi_ioapic_pin,
 };
diff --git a/arch/x86/kernel/xsave.c b/arch/x86/kernel/xsave.c
deleted file mode 100644 (file)
index 87a815b..0000000
+++ /dev/null
@@ -1,724 +0,0 @@
-/*
- * xsave/xrstor support.
- *
- * Author: Suresh Siddha <suresh.b.siddha@intel.com>
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/bootmem.h>
-#include <linux/compat.h>
-#include <linux/cpu.h>
-#include <asm/i387.h>
-#include <asm/fpu-internal.h>
-#include <asm/sigframe.h>
-#include <asm/tlbflush.h>
-#include <asm/xcr.h>
-
-/*
- * Supported feature mask by the CPU and the kernel.
- */
-u64 pcntxt_mask;
-
-/*
- * Represents init state for the supported extended state.
- */
-struct xsave_struct *init_xstate_buf;
-
-static struct _fpx_sw_bytes fx_sw_reserved, fx_sw_reserved_ia32;
-static unsigned int *xstate_offsets, *xstate_sizes;
-static unsigned int xstate_comp_offsets[sizeof(pcntxt_mask)*8];
-static unsigned int xstate_features;
-
-/*
- * If a processor implementation discern that a processor state component is
- * in its initialized state it may modify the corresponding bit in the
- * xsave_hdr.xstate_bv as '0', with out modifying the corresponding memory
- * layout in the case of xsaveopt. While presenting the xstate information to
- * the user, we always ensure that the memory layout of a feature will be in
- * the init state if the corresponding header bit is zero. This is to ensure
- * that the user doesn't see some stale state in the memory layout during
- * signal handling, debugging etc.
- */
-void __sanitize_i387_state(struct task_struct *tsk)
-{
-       struct i387_fxsave_struct *fx = &tsk->thread.fpu.state->fxsave;
-       int feature_bit = 0x2;
-       u64 xstate_bv;
-
-       if (!fx)
-               return;
-
-       xstate_bv = tsk->thread.fpu.state->xsave.xsave_hdr.xstate_bv;
-
-       /*
-        * None of the feature bits are in init state. So nothing else
-        * to do for us, as the memory layout is up to date.
-        */
-       if ((xstate_bv & pcntxt_mask) == pcntxt_mask)
-               return;
-
-       /*
-        * FP is in init state
-        */
-       if (!(xstate_bv & XSTATE_FP)) {
-               fx->cwd = 0x37f;
-               fx->swd = 0;
-               fx->twd = 0;
-               fx->fop = 0;
-               fx->rip = 0;
-               fx->rdp = 0;
-               memset(&fx->st_space[0], 0, 128);
-       }
-
-       /*
-        * SSE is in init state
-        */
-       if (!(xstate_bv & XSTATE_SSE))
-               memset(&fx->xmm_space[0], 0, 256);
-
-       xstate_bv = (pcntxt_mask & ~xstate_bv) >> 2;
-
-       /*
-        * Update all the other memory layouts for which the corresponding
-        * header bit is in the init state.
-        */
-       while (xstate_bv) {
-               if (xstate_bv & 0x1) {
-                       int offset = xstate_offsets[feature_bit];
-                       int size = xstate_sizes[feature_bit];
-
-                       memcpy(((void *) fx) + offset,
-                              ((void *) init_xstate_buf) + offset,
-                              size);
-               }
-
-               xstate_bv >>= 1;
-               feature_bit++;
-       }
-}
-
-/*
- * Check for the presence of extended state information in the
- * user fpstate pointer in the sigcontext.
- */
-static inline int check_for_xstate(struct i387_fxsave_struct __user *buf,
-                                  void __user *fpstate,
-                                  struct _fpx_sw_bytes *fx_sw)
-{
-       int min_xstate_size = sizeof(struct i387_fxsave_struct) +
-                             sizeof(struct xsave_hdr_struct);
-       unsigned int magic2;
-
-       if (__copy_from_user(fx_sw, &buf->sw_reserved[0], sizeof(*fx_sw)))
-               return -1;
-
-       /* Check for the first magic field and other error scenarios. */
-       if (fx_sw->magic1 != FP_XSTATE_MAGIC1 ||
-           fx_sw->xstate_size < min_xstate_size ||
-           fx_sw->xstate_size > xstate_size ||
-           fx_sw->xstate_size > fx_sw->extended_size)
-               return -1;
-
-       /*
-        * Check for the presence of second magic word at the end of memory
-        * layout. This detects the case where the user just copied the legacy
-        * fpstate layout with out copying the extended state information
-        * in the memory layout.
-        */
-       if (__get_user(magic2, (__u32 __user *)(fpstate + fx_sw->xstate_size))
-           || magic2 != FP_XSTATE_MAGIC2)
-               return -1;
-
-       return 0;
-}
-
-/*
- * Signal frame handlers.
- */
-static inline int save_fsave_header(struct task_struct *tsk, void __user *buf)
-{
-       if (use_fxsr()) {
-               struct xsave_struct *xsave = &tsk->thread.fpu.state->xsave;
-               struct user_i387_ia32_struct env;
-               struct _fpstate_ia32 __user *fp = buf;
-
-               convert_from_fxsr(&env, tsk);
-
-               if (__copy_to_user(buf, &env, sizeof(env)) ||
-                   __put_user(xsave->i387.swd, &fp->status) ||
-                   __put_user(X86_FXSR_MAGIC, &fp->magic))
-                       return -1;
-       } else {
-               struct i387_fsave_struct __user *fp = buf;
-               u32 swd;
-               if (__get_user(swd, &fp->swd) || __put_user(swd, &fp->status))
-                       return -1;
-       }
-
-       return 0;
-}
-
-static inline int save_xstate_epilog(void __user *buf, int ia32_frame)
-{
-       struct xsave_struct __user *x = buf;
-       struct _fpx_sw_bytes *sw_bytes;
-       u32 xstate_bv;
-       int err;
-
-       /* Setup the bytes not touched by the [f]xsave and reserved for SW. */
-       sw_bytes = ia32_frame ? &fx_sw_reserved_ia32 : &fx_sw_reserved;
-       err = __copy_to_user(&x->i387.sw_reserved, sw_bytes, sizeof(*sw_bytes));
-
-       if (!use_xsave())
-               return err;
-
-       err |= __put_user(FP_XSTATE_MAGIC2, (__u32 *)(buf + xstate_size));
-
-       /*
-        * Read the xstate_bv which we copied (directly from the cpu or
-        * from the state in task struct) to the user buffers.
-        */
-       err |= __get_user(xstate_bv, (__u32 *)&x->xsave_hdr.xstate_bv);
-
-       /*
-        * For legacy compatible, we always set FP/SSE bits in the bit
-        * vector while saving the state to the user context. This will
-        * enable us capturing any changes(during sigreturn) to
-        * the FP/SSE bits by the legacy applications which don't touch
-        * xstate_bv in the xsave header.
-        *
-        * xsave aware apps can change the xstate_bv in the xsave
-        * header as well as change any contents in the memory layout.
-        * xrestore as part of sigreturn will capture all the changes.
-        */
-       xstate_bv |= XSTATE_FPSSE;
-
-       err |= __put_user(xstate_bv, (__u32 *)&x->xsave_hdr.xstate_bv);
-
-       return err;
-}
-
-static inline int save_user_xstate(struct xsave_struct __user *buf)
-{
-       int err;
-
-       if (use_xsave())
-               err = xsave_user(buf);
-       else if (use_fxsr())
-               err = fxsave_user((struct i387_fxsave_struct __user *) buf);
-       else
-               err = fsave_user((struct i387_fsave_struct __user *) buf);
-
-       if (unlikely(err) && __clear_user(buf, xstate_size))
-               err = -EFAULT;
-       return err;
-}
-
-/*
- * Save the fpu, extended register state to the user signal frame.
- *
- * 'buf_fx' is the 64-byte aligned pointer at which the [f|fx|x]save
- *  state is copied.
- *  'buf' points to the 'buf_fx' or to the fsave header followed by 'buf_fx'.
- *
- *     buf == buf_fx for 64-bit frames and 32-bit fsave frame.
- *     buf != buf_fx for 32-bit frames with fxstate.
- *
- * If the fpu, extended register state is live, save the state directly
- * to the user frame pointed by the aligned pointer 'buf_fx'. Otherwise,
- * copy the thread's fpu state to the user frame starting at 'buf_fx'.
- *
- * If this is a 32-bit frame with fxstate, put a fsave header before
- * the aligned state at 'buf_fx'.
- *
- * For [f]xsave state, update the SW reserved fields in the [f]xsave frame
- * indicating the absence/presence of the extended state to the user.
- */
-int save_xstate_sig(void __user *buf, void __user *buf_fx, int size)
-{
-       struct xsave_struct *xsave = &current->thread.fpu.state->xsave;
-       struct task_struct *tsk = current;
-       int ia32_fxstate = (buf != buf_fx);
-
-       ia32_fxstate &= (config_enabled(CONFIG_X86_32) ||
-                        config_enabled(CONFIG_IA32_EMULATION));
-
-       if (!access_ok(VERIFY_WRITE, buf, size))
-               return -EACCES;
-
-       if (!static_cpu_has(X86_FEATURE_FPU))
-               return fpregs_soft_get(current, NULL, 0,
-                       sizeof(struct user_i387_ia32_struct), NULL,
-                       (struct _fpstate_ia32 __user *) buf) ? -1 : 1;
-
-       if (user_has_fpu()) {
-               /* Save the live register state to the user directly. */
-               if (save_user_xstate(buf_fx))
-                       return -1;
-               /* Update the thread's fxstate to save the fsave header. */
-               if (ia32_fxstate)
-                       fpu_fxsave(&tsk->thread.fpu);
-       } else {
-               sanitize_i387_state(tsk);
-               if (__copy_to_user(buf_fx, xsave, xstate_size))
-                       return -1;
-       }
-
-       /* Save the fsave header for the 32-bit frames. */
-       if ((ia32_fxstate || !use_fxsr()) && save_fsave_header(tsk, buf))
-               return -1;
-
-       if (use_fxsr() && save_xstate_epilog(buf_fx, ia32_fxstate))
-               return -1;
-
-       return 0;
-}
-
-static inline void
-sanitize_restored_xstate(struct task_struct *tsk,
-                        struct user_i387_ia32_struct *ia32_env,
-                        u64 xstate_bv, int fx_only)
-{
-       struct xsave_struct *xsave = &tsk->thread.fpu.state->xsave;
-       struct xsave_hdr_struct *xsave_hdr = &xsave->xsave_hdr;
-
-       if (use_xsave()) {
-               /* These bits must be zero. */
-               memset(xsave_hdr->reserved, 0, 48);
-
-               /*
-                * Init the state that is not present in the memory
-                * layout and not enabled by the OS.
-                */
-               if (fx_only)
-                       xsave_hdr->xstate_bv = XSTATE_FPSSE;
-               else
-                       xsave_hdr->xstate_bv &= (pcntxt_mask & xstate_bv);
-       }
-
-       if (use_fxsr()) {
-               /*
-                * mscsr reserved bits must be masked to zero for security
-                * reasons.
-                */
-               xsave->i387.mxcsr &= mxcsr_feature_mask;
-
-               convert_to_fxsr(tsk, ia32_env);
-       }
-}
-
-/*
- * Restore the extended state if present. Otherwise, restore the FP/SSE state.
- */
-static inline int restore_user_xstate(void __user *buf, u64 xbv, int fx_only)
-{
-       if (use_xsave()) {
-               if ((unsigned long)buf % 64 || fx_only) {
-                       u64 init_bv = pcntxt_mask & ~XSTATE_FPSSE;
-                       xrstor_state(init_xstate_buf, init_bv);
-                       return fxrstor_user(buf);
-               } else {
-                       u64 init_bv = pcntxt_mask & ~xbv;
-                       if (unlikely(init_bv))
-                               xrstor_state(init_xstate_buf, init_bv);
-                       return xrestore_user(buf, xbv);
-               }
-       } else if (use_fxsr()) {
-               return fxrstor_user(buf);
-       } else
-               return frstor_user(buf);
-}
-
-int __restore_xstate_sig(void __user *buf, void __user *buf_fx, int size)
-{
-       int ia32_fxstate = (buf != buf_fx);
-       struct task_struct *tsk = current;
-       int state_size = xstate_size;
-       u64 xstate_bv = 0;
-       int fx_only = 0;
-
-       ia32_fxstate &= (config_enabled(CONFIG_X86_32) ||
-                        config_enabled(CONFIG_IA32_EMULATION));
-
-       if (!buf) {
-               fpu_reset_state(tsk);
-               return 0;
-       }
-
-       if (!access_ok(VERIFY_READ, buf, size))
-               return -EACCES;
-
-       if (!used_math() && init_fpu(tsk))
-               return -1;
-
-       if (!static_cpu_has(X86_FEATURE_FPU))
-               return fpregs_soft_set(current, NULL,
-                                      0, sizeof(struct user_i387_ia32_struct),
-                                      NULL, buf) != 0;
-
-       if (use_xsave()) {
-               struct _fpx_sw_bytes fx_sw_user;
-               if (unlikely(check_for_xstate(buf_fx, buf_fx, &fx_sw_user))) {
-                       /*
-                        * Couldn't find the extended state information in the
-                        * memory layout. Restore just the FP/SSE and init all
-                        * the other extended state.
-                        */
-                       state_size = sizeof(struct i387_fxsave_struct);
-                       fx_only = 1;
-               } else {
-                       state_size = fx_sw_user.xstate_size;
-                       xstate_bv = fx_sw_user.xstate_bv;
-               }
-       }
-
-       if (ia32_fxstate) {
-               /*
-                * For 32-bit frames with fxstate, copy the user state to the
-                * thread's fpu state, reconstruct fxstate from the fsave
-                * header. Sanitize the copied state etc.
-                */
-               struct fpu *fpu = &tsk->thread.fpu;
-               struct user_i387_ia32_struct env;
-               int err = 0;
-
-               /*
-                * Drop the current fpu which clears used_math(). This ensures
-                * that any context-switch during the copy of the new state,
-                * avoids the intermediate state from getting restored/saved.
-                * Thus avoiding the new restored state from getting corrupted.
-                * We will be ready to restore/save the state only after
-                * set_used_math() is again set.
-                */
-               drop_fpu(tsk);
-
-               if (__copy_from_user(&fpu->state->xsave, buf_fx, state_size) ||
-                   __copy_from_user(&env, buf, sizeof(env))) {
-                       fpu_finit(fpu);
-                       err = -1;
-               } else {
-                       sanitize_restored_xstate(tsk, &env, xstate_bv, fx_only);
-               }
-
-               set_used_math();
-               if (use_eager_fpu()) {
-                       preempt_disable();
-                       math_state_restore();
-                       preempt_enable();
-               }
-
-               return err;
-       } else {
-               /*
-                * For 64-bit frames and 32-bit fsave frames, restore the user
-                * state to the registers directly (with exceptions handled).
-                */
-               user_fpu_begin();
-               if (restore_user_xstate(buf_fx, xstate_bv, fx_only)) {
-                       fpu_reset_state(tsk);
-                       return -1;
-               }
-       }
-
-       return 0;
-}
-
-/*
- * Prepare the SW reserved portion of the fxsave memory layout, indicating
- * the presence of the extended state information in the memory layout
- * pointed by the fpstate pointer in the sigcontext.
- * This will be saved when ever the FP and extended state context is
- * saved on the user stack during the signal handler delivery to the user.
- */
-static void prepare_fx_sw_frame(void)
-{
-       int fsave_header_size = sizeof(struct i387_fsave_struct);
-       int size = xstate_size + FP_XSTATE_MAGIC2_SIZE;
-
-       if (config_enabled(CONFIG_X86_32))
-               size += fsave_header_size;
-
-       fx_sw_reserved.magic1 = FP_XSTATE_MAGIC1;
-       fx_sw_reserved.extended_size = size;
-       fx_sw_reserved.xstate_bv = pcntxt_mask;
-       fx_sw_reserved.xstate_size = xstate_size;
-
-       if (config_enabled(CONFIG_IA32_EMULATION)) {
-               fx_sw_reserved_ia32 = fx_sw_reserved;
-               fx_sw_reserved_ia32.extended_size += fsave_header_size;
-       }
-}
-
-/*
- * Enable the extended processor state save/restore feature
- */
-static inline void xstate_enable(void)
-{
-       cr4_set_bits(X86_CR4_OSXSAVE);
-       xsetbv(XCR_XFEATURE_ENABLED_MASK, pcntxt_mask);
-}
-
-/*
- * Record the offsets and sizes of different state managed by the xsave
- * memory layout.
- */
-static void __init setup_xstate_features(void)
-{
-       int eax, ebx, ecx, edx, leaf = 0x2;
-
-       xstate_features = fls64(pcntxt_mask);
-       xstate_offsets = alloc_bootmem(xstate_features * sizeof(int));
-       xstate_sizes = alloc_bootmem(xstate_features * sizeof(int));
-
-       do {
-               cpuid_count(XSTATE_CPUID, leaf, &eax, &ebx, &ecx, &edx);
-
-               if (eax == 0)
-                       break;
-
-               xstate_offsets[leaf] = ebx;
-               xstate_sizes[leaf] = eax;
-
-               leaf++;
-       } while (1);
-}
-
-/*
- * This function sets up offsets and sizes of all extended states in
- * xsave area. This supports both standard format and compacted format
- * of the xsave aread.
- *
- * Input: void
- * Output: void
- */
-void setup_xstate_comp(void)
-{
-       unsigned int xstate_comp_sizes[sizeof(pcntxt_mask)*8];
-       int i;
-
-       /*
-        * The FP xstates and SSE xstates are legacy states. They are always
-        * in the fixed offsets in the xsave area in either compacted form
-        * or standard form.
-        */
-       xstate_comp_offsets[0] = 0;
-       xstate_comp_offsets[1] = offsetof(struct i387_fxsave_struct, xmm_space);
-
-       if (!cpu_has_xsaves) {
-               for (i = 2; i < xstate_features; i++) {
-                       if (test_bit(i, (unsigned long *)&pcntxt_mask)) {
-                               xstate_comp_offsets[i] = xstate_offsets[i];
-                               xstate_comp_sizes[i] = xstate_sizes[i];
-                       }
-               }
-               return;
-       }
-
-       xstate_comp_offsets[2] = FXSAVE_SIZE + XSAVE_HDR_SIZE;
-
-       for (i = 2; i < xstate_features; i++) {
-               if (test_bit(i, (unsigned long *)&pcntxt_mask))
-                       xstate_comp_sizes[i] = xstate_sizes[i];
-               else
-                       xstate_comp_sizes[i] = 0;
-
-               if (i > 2)
-                       xstate_comp_offsets[i] = xstate_comp_offsets[i-1]
-                                       + xstate_comp_sizes[i-1];
-
-       }
-}
-
-/*
- * setup the xstate image representing the init state
- */
-static void __init setup_init_fpu_buf(void)
-{
-       /*
-        * Setup init_xstate_buf to represent the init state of
-        * all the features managed by the xsave
-        */
-       init_xstate_buf = alloc_bootmem_align(xstate_size,
-                                             __alignof__(struct xsave_struct));
-       fx_finit(&init_xstate_buf->i387);
-
-       if (!cpu_has_xsave)
-               return;
-
-       setup_xstate_features();
-
-       if (cpu_has_xsaves) {
-               init_xstate_buf->xsave_hdr.xcomp_bv =
-                                               (u64)1 << 63 | pcntxt_mask;
-               init_xstate_buf->xsave_hdr.xstate_bv = pcntxt_mask;
-       }
-
-       /*
-        * Init all the features state with header_bv being 0x0
-        */
-       xrstor_state_booting(init_xstate_buf, -1);
-       /*
-        * Dump the init state again. This is to identify the init state
-        * of any feature which is not represented by all zero's.
-        */
-       xsave_state_booting(init_xstate_buf, -1);
-}
-
-static enum { AUTO, ENABLE, DISABLE } eagerfpu = AUTO;
-static int __init eager_fpu_setup(char *s)
-{
-       if (!strcmp(s, "on"))
-               eagerfpu = ENABLE;
-       else if (!strcmp(s, "off"))
-               eagerfpu = DISABLE;
-       else if (!strcmp(s, "auto"))
-               eagerfpu = AUTO;
-       return 1;
-}
-__setup("eagerfpu=", eager_fpu_setup);
-
-
-/*
- * Calculate total size of enabled xstates in XCR0/pcntxt_mask.
- */
-static void __init init_xstate_size(void)
-{
-       unsigned int eax, ebx, ecx, edx;
-       int i;
-
-       if (!cpu_has_xsaves) {
-               cpuid_count(XSTATE_CPUID, 0, &eax, &ebx, &ecx, &edx);
-               xstate_size = ebx;
-               return;
-       }
-
-       xstate_size = FXSAVE_SIZE + XSAVE_HDR_SIZE;
-       for (i = 2; i < 64; i++) {
-               if (test_bit(i, (unsigned long *)&pcntxt_mask)) {
-                       cpuid_count(XSTATE_CPUID, i, &eax, &ebx, &ecx, &edx);
-                       xstate_size += eax;
-               }
-       }
-}
-
-/*
- * Enable and initialize the xsave feature.
- */
-static void __init xstate_enable_boot_cpu(void)
-{
-       unsigned int eax, ebx, ecx, edx;
-
-       if (boot_cpu_data.cpuid_level < XSTATE_CPUID) {
-               WARN(1, KERN_ERR "XSTATE_CPUID missing\n");
-               return;
-       }
-
-       cpuid_count(XSTATE_CPUID, 0, &eax, &ebx, &ecx, &edx);
-       pcntxt_mask = eax + ((u64)edx << 32);
-
-       if ((pcntxt_mask & XSTATE_FPSSE) != XSTATE_FPSSE) {
-               pr_err("FP/SSE not shown under xsave features 0x%llx\n",
-                      pcntxt_mask);
-               BUG();
-       }
-
-       /*
-        * Support only the state known to OS.
-        */
-       pcntxt_mask = pcntxt_mask & XCNTXT_MASK;
-
-       xstate_enable();
-
-       /*
-        * Recompute the context size for enabled features
-        */
-       init_xstate_size();
-
-       update_regset_xstate_info(xstate_size, pcntxt_mask);
-       prepare_fx_sw_frame();
-       setup_init_fpu_buf();
-
-       /* Auto enable eagerfpu for xsaveopt */
-       if (cpu_has_xsaveopt && eagerfpu != DISABLE)
-               eagerfpu = ENABLE;
-
-       if (pcntxt_mask & XSTATE_EAGER) {
-               if (eagerfpu == DISABLE) {
-                       pr_err("eagerfpu not present, disabling some xstate features: 0x%llx\n",
-                                       pcntxt_mask & XSTATE_EAGER);
-                       pcntxt_mask &= ~XSTATE_EAGER;
-               } else {
-                       eagerfpu = ENABLE;
-               }
-       }
-
-       pr_info("enabled xstate_bv 0x%llx, cntxt size 0x%x using %s\n",
-               pcntxt_mask, xstate_size,
-               cpu_has_xsaves ? "compacted form" : "standard form");
-}
-
-/*
- * For the very first instance, this calls xstate_enable_boot_cpu();
- * for all subsequent instances, this calls xstate_enable().
- *
- * This is somewhat obfuscated due to the lack of powerful enough
- * overrides for the section checks.
- */
-void xsave_init(void)
-{
-       static __refdata void (*next_func)(void) = xstate_enable_boot_cpu;
-       void (*this_func)(void);
-
-       if (!cpu_has_xsave)
-               return;
-
-       this_func = next_func;
-       next_func = xstate_enable;
-       this_func();
-}
-
-/*
- * setup_init_fpu_buf() is __init and it is OK to call it here because
- * init_xstate_buf will be unset only once during boot.
- */
-void __init_refok eager_fpu_init(void)
-{
-       WARN_ON(used_math());
-       current_thread_info()->status = 0;
-
-       if (eagerfpu == ENABLE)
-               setup_force_cpu_cap(X86_FEATURE_EAGER_FPU);
-
-       if (!cpu_has_eager_fpu) {
-               stts();
-               return;
-       }
-
-       if (!init_xstate_buf)
-               setup_init_fpu_buf();
-}
-
-/*
- * Given the xsave area and a state inside, this function returns the
- * address of the state.
- *
- * This is the API that is called to get xstate address in either
- * standard format or compacted format of xsave area.
- *
- * Inputs:
- *     xsave: base address of the xsave area;
- *     xstate: state which is defined in xsave.h (e.g. XSTATE_FP, XSTATE_SSE,
- *     etc.)
- * Output:
- *     address of the state in the xsave area.
- */
-void *get_xsave_addr(struct xsave_struct *xsave, int xstate)
-{
-       int feature = fls64(xstate) - 1;
-       if (!test_bit(feature, (unsigned long *)&pcntxt_mask))
-               return NULL;
-
-       return (void *)xsave + xstate_comp_offsets[feature];
-}
-EXPORT_SYMBOL_GPL(get_xsave_addr);
index 413a7bf9efbb93286aa6d3de429f4efcef5c3887..d8a1d56276e1245bda5a4b84c990eafd18efbca1 100644 (file)
@@ -86,15 +86,16 @@ config KVM_MMU_AUDIT
         auditing of KVM MMU events at runtime.
 
 config KVM_DEVICE_ASSIGNMENT
-       bool "KVM legacy PCI device assignment support"
+       bool "KVM legacy PCI device assignment support (DEPRECATED)"
        depends on KVM && PCI && IOMMU_API
-       default y
+       default n
        ---help---
          Provide support for legacy PCI device assignment through KVM.  The
          kernel now also supports a full featured userspace device driver
-         framework through VFIO, which supersedes much of this support.
+         framework through VFIO, which supersedes this support and provides
+         better security.
 
-         If unsure, say Y.
+         If unsure, say N.
 
 # OK, it's a little counter-intuitive to do this, but it puts it neatly under
 # the virtualization menu.
index 16e8f962eaadf9d8f0d1cb4942288ebcd3f12a94..67d215cb8953274c602923bfa2682d417cd6909d 100644 (file)
@@ -12,10 +12,10 @@ kvm-y                       += $(KVM)/kvm_main.o $(KVM)/coalesced_mmio.o \
 kvm-$(CONFIG_KVM_ASYNC_PF)     += $(KVM)/async_pf.o
 
 kvm-y                  += x86.o mmu.o emulate.o i8259.o irq.o lapic.o \
-                          i8254.o ioapic.o irq_comm.o cpuid.o pmu.o
+                          i8254.o ioapic.o irq_comm.o cpuid.o pmu.o mtrr.o
 kvm-$(CONFIG_KVM_DEVICE_ASSIGNMENT)    += assigned-dev.o iommu.o
-kvm-intel-y            += vmx.o
-kvm-amd-y              += svm.o
+kvm-intel-y            += vmx.o pmu_intel.o
+kvm-amd-y              += svm.o pmu_amd.o
 
 obj-$(CONFIG_KVM)      += kvm.o
 obj-$(CONFIG_KVM_INTEL)        += kvm-intel.o
index 1d08ad3582d07fd61cd03302eba547e3a997058b..64dd467930997adbfa6aecd25641ef2004c5488c 100644 (file)
 #include <linux/module.h>
 #include <linux/vmalloc.h>
 #include <linux/uaccess.h>
-#include <asm/i387.h> /* For use_eager_fpu.  Ugh! */
-#include <asm/fpu-internal.h> /* For use_eager_fpu.  Ugh! */
+#include <asm/fpu/internal.h> /* For use_eager_fpu.  Ugh! */
 #include <asm/user.h>
-#include <asm/xsave.h>
+#include <asm/fpu/xstate.h>
 #include "cpuid.h"
 #include "lapic.h"
 #include "mmu.h"
 #include "trace.h"
+#include "pmu.h"
 
 static u32 xstate_required_size(u64 xstate_bv, bool compacted)
 {
@@ -97,7 +97,7 @@ int kvm_update_cpuid(struct kvm_vcpu *vcpu)
        if (best && (best->eax & (F(XSAVES) | F(XSAVEC))))
                best->ebx = xstate_required_size(vcpu->arch.xcr0, true);
 
-       vcpu->arch.eager_fpu = guest_cpuid_has_mpx(vcpu);
+       vcpu->arch.eager_fpu = use_eager_fpu() || guest_cpuid_has_mpx(vcpu);
 
        /*
         * The existing code assumes virtual address is 48-bit in the canonical
@@ -111,7 +111,7 @@ int kvm_update_cpuid(struct kvm_vcpu *vcpu)
        /* Update physical-address width */
        vcpu->arch.maxphyaddr = cpuid_query_maxphyaddr(vcpu);
 
-       kvm_pmu_cpuid_update(vcpu);
+       kvm_pmu_refresh(vcpu);
        return 0;
 }
 
@@ -415,6 +415,12 @@ static inline int __do_cpuid_ent(struct kvm_cpuid_entry2 *entry, u32 function,
                }
                break;
        }
+       case 6: /* Thermal management */
+               entry->eax = 0x4; /* allow ARAT */
+               entry->ebx = 0;
+               entry->ecx = 0;
+               entry->edx = 0;
+               break;
        case 7: {
                entry->flags |= KVM_CPUID_FLAG_SIGNIFCANT_INDEX;
                /* Mask ebx against host capability word 9 */
@@ -591,7 +597,6 @@ static inline int __do_cpuid_ent(struct kvm_cpuid_entry2 *entry, u32 function,
                break;
        case 3: /* Processor serial number */
        case 5: /* MONITOR/MWAIT */
-       case 6: /* Thermal management */
        case 0xC0000002:
        case 0xC0000003:
        case 0xC0000004:
index 496b3695d3d3c96fd2687b2b6bc013d9ee8d96e5..dd05b9cef6ae332d42ebee6c2cb41554e8eef632 100644 (file)
@@ -70,6 +70,14 @@ static inline bool guest_cpuid_has_fsgsbase(struct kvm_vcpu *vcpu)
        return best && (best->ebx & bit(X86_FEATURE_FSGSBASE));
 }
 
+static inline bool guest_cpuid_has_longmode(struct kvm_vcpu *vcpu)
+{
+       struct kvm_cpuid_entry2 *best;
+
+       best = kvm_find_cpuid_entry(vcpu, 0x80000001, 0);
+       return best && (best->edx & bit(X86_FEATURE_LM));
+}
+
 static inline bool guest_cpuid_has_osvw(struct kvm_vcpu *vcpu)
 {
        struct kvm_cpuid_entry2 *best;
index 630bcb0d7a045b4930213eac1c1bbee3ef7d0ebe..e7a4fde5d631031908b6f336d9ecc40e7a3a413d 100644 (file)
@@ -25,6 +25,7 @@
 #include <linux/module.h>
 #include <asm/kvm_emulate.h>
 #include <linux/stringify.h>
+#include <asm/debugreg.h>
 
 #include "x86.h"
 #include "tss.h"
@@ -523,13 +524,9 @@ static void masked_increment(ulong *reg, ulong mask, int inc)
 static inline void
 register_address_increment(struct x86_emulate_ctxt *ctxt, int reg, int inc)
 {
-       ulong mask;
+       ulong *preg = reg_rmw(ctxt, reg);
 
-       if (ctxt->ad_bytes == sizeof(unsigned long))
-               mask = ~0UL;
-       else
-               mask = ad_mask(ctxt);
-       masked_increment(reg_rmw(ctxt, reg), mask, inc);
+       assign_register(preg, *preg + inc, ctxt->ad_bytes);
 }
 
 static void rsp_increment(struct x86_emulate_ctxt *ctxt, int inc)
@@ -2262,6 +2259,260 @@ static int em_lseg(struct x86_emulate_ctxt *ctxt)
        return rc;
 }
 
+static int emulator_has_longmode(struct x86_emulate_ctxt *ctxt)
+{
+       u32 eax, ebx, ecx, edx;
+
+       eax = 0x80000001;
+       ecx = 0;
+       ctxt->ops->get_cpuid(ctxt, &eax, &ebx, &ecx, &edx);
+       return edx & bit(X86_FEATURE_LM);
+}
+
+#define GET_SMSTATE(type, smbase, offset)                                \
+       ({                                                                \
+        type __val;                                                      \
+        int r = ctxt->ops->read_std(ctxt, smbase + offset, &__val,       \
+                                    sizeof(__val), NULL);                \
+        if (r != X86EMUL_CONTINUE)                                       \
+                return X86EMUL_UNHANDLEABLE;                             \
+        __val;                                                           \
+       })
+
+static void rsm_set_desc_flags(struct desc_struct *desc, u32 flags)
+{
+       desc->g    = (flags >> 23) & 1;
+       desc->d    = (flags >> 22) & 1;
+       desc->l    = (flags >> 21) & 1;
+       desc->avl  = (flags >> 20) & 1;
+       desc->p    = (flags >> 15) & 1;
+       desc->dpl  = (flags >> 13) & 3;
+       desc->s    = (flags >> 12) & 1;
+       desc->type = (flags >>  8) & 15;
+}
+
+static int rsm_load_seg_32(struct x86_emulate_ctxt *ctxt, u64 smbase, int n)
+{
+       struct desc_struct desc;
+       int offset;
+       u16 selector;
+
+       selector = GET_SMSTATE(u32, smbase, 0x7fa8 + n * 4);
+
+       if (n < 3)
+               offset = 0x7f84 + n * 12;
+       else
+               offset = 0x7f2c + (n - 3) * 12;
+
+       set_desc_base(&desc,      GET_SMSTATE(u32, smbase, offset + 8));
+       set_desc_limit(&desc,     GET_SMSTATE(u32, smbase, offset + 4));
+       rsm_set_desc_flags(&desc, GET_SMSTATE(u32, smbase, offset));
+       ctxt->ops->set_segment(ctxt, selector, &desc, 0, n);
+       return X86EMUL_CONTINUE;
+}
+
+static int rsm_load_seg_64(struct x86_emulate_ctxt *ctxt, u64 smbase, int n)
+{
+       struct desc_struct desc;
+       int offset;
+       u16 selector;
+       u32 base3;
+
+       offset = 0x7e00 + n * 16;
+
+       selector =                GET_SMSTATE(u16, smbase, offset);
+       rsm_set_desc_flags(&desc, GET_SMSTATE(u16, smbase, offset + 2) << 8);
+       set_desc_limit(&desc,     GET_SMSTATE(u32, smbase, offset + 4));
+       set_desc_base(&desc,      GET_SMSTATE(u32, smbase, offset + 8));
+       base3 =                   GET_SMSTATE(u32, smbase, offset + 12);
+
+       ctxt->ops->set_segment(ctxt, selector, &desc, base3, n);
+       return X86EMUL_CONTINUE;
+}
+
+static int rsm_enter_protected_mode(struct x86_emulate_ctxt *ctxt,
+                                    u64 cr0, u64 cr4)
+{
+       int bad;
+
+       /*
+        * First enable PAE, long mode needs it before CR0.PG = 1 is set.
+        * Then enable protected mode.  However, PCID cannot be enabled
+        * if EFER.LMA=0, so set it separately.
+        */
+       bad = ctxt->ops->set_cr(ctxt, 4, cr4 & ~X86_CR4_PCIDE);
+       if (bad)
+               return X86EMUL_UNHANDLEABLE;
+
+       bad = ctxt->ops->set_cr(ctxt, 0, cr0);
+       if (bad)
+               return X86EMUL_UNHANDLEABLE;
+
+       if (cr4 & X86_CR4_PCIDE) {
+               bad = ctxt->ops->set_cr(ctxt, 4, cr4);
+               if (bad)
+                       return X86EMUL_UNHANDLEABLE;
+       }
+
+       return X86EMUL_CONTINUE;
+}
+
+static int rsm_load_state_32(struct x86_emulate_ctxt *ctxt, u64 smbase)
+{
+       struct desc_struct desc;
+       struct desc_ptr dt;
+       u16 selector;
+       u32 val, cr0, cr4;
+       int i;
+
+       cr0 =                      GET_SMSTATE(u32, smbase, 0x7ffc);
+       ctxt->ops->set_cr(ctxt, 3, GET_SMSTATE(u32, smbase, 0x7ff8));
+       ctxt->eflags =             GET_SMSTATE(u32, smbase, 0x7ff4) | X86_EFLAGS_FIXED;
+       ctxt->_eip =               GET_SMSTATE(u32, smbase, 0x7ff0);
+
+       for (i = 0; i < 8; i++)
+               *reg_write(ctxt, i) = GET_SMSTATE(u32, smbase, 0x7fd0 + i * 4);
+
+       val = GET_SMSTATE(u32, smbase, 0x7fcc);
+       ctxt->ops->set_dr(ctxt, 6, (val & DR6_VOLATILE) | DR6_FIXED_1);
+       val = GET_SMSTATE(u32, smbase, 0x7fc8);
+       ctxt->ops->set_dr(ctxt, 7, (val & DR7_VOLATILE) | DR7_FIXED_1);
+
+       selector =                 GET_SMSTATE(u32, smbase, 0x7fc4);
+       set_desc_base(&desc,       GET_SMSTATE(u32, smbase, 0x7f64));
+       set_desc_limit(&desc,      GET_SMSTATE(u32, smbase, 0x7f60));
+       rsm_set_desc_flags(&desc,  GET_SMSTATE(u32, smbase, 0x7f5c));
+       ctxt->ops->set_segment(ctxt, selector, &desc, 0, VCPU_SREG_TR);
+
+       selector =                 GET_SMSTATE(u32, smbase, 0x7fc0);
+       set_desc_base(&desc,       GET_SMSTATE(u32, smbase, 0x7f80));
+       set_desc_limit(&desc,      GET_SMSTATE(u32, smbase, 0x7f7c));
+       rsm_set_desc_flags(&desc,  GET_SMSTATE(u32, smbase, 0x7f78));
+       ctxt->ops->set_segment(ctxt, selector, &desc, 0, VCPU_SREG_LDTR);
+
+       dt.address =               GET_SMSTATE(u32, smbase, 0x7f74);
+       dt.size =                  GET_SMSTATE(u32, smbase, 0x7f70);
+       ctxt->ops->set_gdt(ctxt, &dt);
+
+       dt.address =               GET_SMSTATE(u32, smbase, 0x7f58);
+       dt.size =                  GET_SMSTATE(u32, smbase, 0x7f54);
+       ctxt->ops->set_idt(ctxt, &dt);
+
+       for (i = 0; i < 6; i++) {
+               int r = rsm_load_seg_32(ctxt, smbase, i);
+               if (r != X86EMUL_CONTINUE)
+                       return r;
+       }
+
+       cr4 = GET_SMSTATE(u32, smbase, 0x7f14);
+
+       ctxt->ops->set_smbase(ctxt, GET_SMSTATE(u32, smbase, 0x7ef8));
+
+       return rsm_enter_protected_mode(ctxt, cr0, cr4);
+}
+
+static int rsm_load_state_64(struct x86_emulate_ctxt *ctxt, u64 smbase)
+{
+       struct desc_struct desc;
+       struct desc_ptr dt;
+       u64 val, cr0, cr4;
+       u32 base3;
+       u16 selector;
+       int i;
+
+       for (i = 0; i < 16; i++)
+               *reg_write(ctxt, i) = GET_SMSTATE(u64, smbase, 0x7ff8 - i * 8);
+
+       ctxt->_eip   = GET_SMSTATE(u64, smbase, 0x7f78);
+       ctxt->eflags = GET_SMSTATE(u32, smbase, 0x7f70) | X86_EFLAGS_FIXED;
+
+       val = GET_SMSTATE(u32, smbase, 0x7f68);
+       ctxt->ops->set_dr(ctxt, 6, (val & DR6_VOLATILE) | DR6_FIXED_1);
+       val = GET_SMSTATE(u32, smbase, 0x7f60);
+       ctxt->ops->set_dr(ctxt, 7, (val & DR7_VOLATILE) | DR7_FIXED_1);
+
+       cr0 =                       GET_SMSTATE(u64, smbase, 0x7f58);
+       ctxt->ops->set_cr(ctxt, 3,  GET_SMSTATE(u64, smbase, 0x7f50));
+       cr4 =                       GET_SMSTATE(u64, smbase, 0x7f48);
+       ctxt->ops->set_smbase(ctxt, GET_SMSTATE(u32, smbase, 0x7f00));
+       val =                       GET_SMSTATE(u64, smbase, 0x7ed0);
+       ctxt->ops->set_msr(ctxt, MSR_EFER, val & ~EFER_LMA);
+
+       selector =                  GET_SMSTATE(u32, smbase, 0x7e90);
+       rsm_set_desc_flags(&desc,   GET_SMSTATE(u32, smbase, 0x7e92) << 8);
+       set_desc_limit(&desc,       GET_SMSTATE(u32, smbase, 0x7e94));
+       set_desc_base(&desc,        GET_SMSTATE(u32, smbase, 0x7e98));
+       base3 =                     GET_SMSTATE(u32, smbase, 0x7e9c);
+       ctxt->ops->set_segment(ctxt, selector, &desc, base3, VCPU_SREG_TR);
+
+       dt.size =                   GET_SMSTATE(u32, smbase, 0x7e84);
+       dt.address =                GET_SMSTATE(u64, smbase, 0x7e88);
+       ctxt->ops->set_idt(ctxt, &dt);
+
+       selector =                  GET_SMSTATE(u32, smbase, 0x7e70);
+       rsm_set_desc_flags(&desc,   GET_SMSTATE(u32, smbase, 0x7e72) << 8);
+       set_desc_limit(&desc,       GET_SMSTATE(u32, smbase, 0x7e74));
+       set_desc_base(&desc,        GET_SMSTATE(u32, smbase, 0x7e78));
+       base3 =                     GET_SMSTATE(u32, smbase, 0x7e7c);
+       ctxt->ops->set_segment(ctxt, selector, &desc, base3, VCPU_SREG_LDTR);
+
+       dt.size =                   GET_SMSTATE(u32, smbase, 0x7e64);
+       dt.address =                GET_SMSTATE(u64, smbase, 0x7e68);
+       ctxt->ops->set_gdt(ctxt, &dt);
+
+       for (i = 0; i < 6; i++) {
+               int r = rsm_load_seg_64(ctxt, smbase, i);
+               if (r != X86EMUL_CONTINUE)
+                       return r;
+       }
+
+       return rsm_enter_protected_mode(ctxt, cr0, cr4);
+}
+
+static int em_rsm(struct x86_emulate_ctxt *ctxt)
+{
+       unsigned long cr0, cr4, efer;
+       u64 smbase;
+       int ret;
+
+       if ((ctxt->emul_flags & X86EMUL_SMM_MASK) == 0)
+               return emulate_ud(ctxt);
+
+       /*
+        * Get back to real mode, to prepare a safe state in which to load
+        * CR0/CR3/CR4/EFER.  Also this will ensure that addresses passed
+        * to read_std/write_std are not virtual.
+        *
+        * CR4.PCIDE must be zero, because it is a 64-bit mode only feature.
+        */
+       cr0 = ctxt->ops->get_cr(ctxt, 0);
+       if (cr0 & X86_CR0_PE)
+               ctxt->ops->set_cr(ctxt, 0, cr0 & ~(X86_CR0_PG | X86_CR0_PE));
+       cr4 = ctxt->ops->get_cr(ctxt, 4);
+       if (cr4 & X86_CR4_PAE)
+               ctxt->ops->set_cr(ctxt, 4, cr4 & ~X86_CR4_PAE);
+       efer = 0;
+       ctxt->ops->set_msr(ctxt, MSR_EFER, efer);
+
+       smbase = ctxt->ops->get_smbase(ctxt);
+       if (emulator_has_longmode(ctxt))
+               ret = rsm_load_state_64(ctxt, smbase + 0x8000);
+       else
+               ret = rsm_load_state_32(ctxt, smbase + 0x8000);
+
+       if (ret != X86EMUL_CONTINUE) {
+               /* FIXME: should triple fault */
+               return X86EMUL_UNHANDLEABLE;
+       }
+
+       if ((ctxt->emul_flags & X86EMUL_SMM_INSIDE_NMI_MASK) == 0)
+               ctxt->ops->set_nmi_mask(ctxt, false);
+
+       ctxt->emul_flags &= ~X86EMUL_SMM_INSIDE_NMI_MASK;
+       ctxt->emul_flags &= ~X86EMUL_SMM_MASK;
+       return X86EMUL_CONTINUE;
+}
+
 static void
 setup_syscalls_segments(struct x86_emulate_ctxt *ctxt,
                        struct desc_struct *cs, struct desc_struct *ss)
@@ -2573,6 +2824,30 @@ static bool emulator_io_permited(struct x86_emulate_ctxt *ctxt,
        return true;
 }
 
+static void string_registers_quirk(struct x86_emulate_ctxt *ctxt)
+{
+       /*
+        * Intel CPUs mask the counter and pointers in quite strange
+        * manner when ECX is zero due to REP-string optimizations.
+        */
+#ifdef CONFIG_X86_64
+       if (ctxt->ad_bytes != 4 || !vendor_intel(ctxt))
+               return;
+
+       *reg_write(ctxt, VCPU_REGS_RCX) = 0;
+
+       switch (ctxt->b) {
+       case 0xa4:      /* movsb */
+       case 0xa5:      /* movsd/w */
+               *reg_rmw(ctxt, VCPU_REGS_RSI) &= (u32)-1;
+               /* fall through */
+       case 0xaa:      /* stosb */
+       case 0xab:      /* stosd/w */
+               *reg_rmw(ctxt, VCPU_REGS_RDI) &= (u32)-1;
+       }
+#endif
+}
+
 static void save_state_to_tss16(struct x86_emulate_ctxt *ctxt,
                                struct tss_segment_16 *tss)
 {
@@ -2849,7 +3124,7 @@ static int emulator_do_task_switch(struct x86_emulate_ctxt *ctxt,
        ulong old_tss_base =
                ops->get_cached_segment_base(ctxt, VCPU_SREG_TR);
        u32 desc_limit;
-       ulong desc_addr;
+       ulong desc_addr, dr7;
 
        /* FIXME: old_tss_base == ~0 ? */
 
@@ -2934,6 +3209,9 @@ static int emulator_do_task_switch(struct x86_emulate_ctxt *ctxt,
                ret = em_push(ctxt);
        }
 
+       ops->get_dr(ctxt, 7, &dr7);
+       ops->set_dr(ctxt, 7, dr7 & ~(DR_LOCAL_ENABLE_MASK | DR_LOCAL_SLOWDOWN));
+
        return ret;
 }
 
@@ -3840,7 +4118,7 @@ static const struct opcode group5[] = {
        F(DstMem | SrcNone | Lock,              em_inc),
        F(DstMem | SrcNone | Lock,              em_dec),
        I(SrcMem | NearBranch,                  em_call_near_abs),
-       I(SrcMemFAddr | ImplicitOps | Stack,    em_call_far),
+       I(SrcMemFAddr | ImplicitOps,            em_call_far),
        I(SrcMem | NearBranch,                  em_jmp_abs),
        I(SrcMemFAddr | ImplicitOps,            em_jmp_far),
        I(SrcMem | Stack,                       em_push), D(Undefined),
@@ -4173,7 +4451,7 @@ static const struct opcode twobyte_table[256] = {
        F(DstMem | SrcReg | Src2CL | ModRM, em_shld), N, N,
        /* 0xA8 - 0xAF */
        I(Stack | Src2GS, em_push_sreg), I(Stack | Src2GS, em_pop_sreg),
-       DI(ImplicitOps, rsm),
+       II(No64 | EmulateOnUD | ImplicitOps, em_rsm, rsm),
        F(DstMem | SrcReg | ModRM | BitOp | Lock | PageTable, em_bts),
        F(DstMem | SrcReg | Src2ImmByte | ModRM, em_shrd),
        F(DstMem | SrcReg | Src2CL | ModRM, em_shrd),
@@ -4871,7 +5149,7 @@ int x86_emulate_insn(struct x86_emulate_ctxt *ctxt)
                                fetch_possible_mmx_operand(ctxt, &ctxt->dst);
                }
 
-               if (unlikely(ctxt->guest_mode) && (ctxt->d & Intercept)) {
+               if (unlikely(ctxt->emul_flags & X86EMUL_GUEST_MASK) && ctxt->intercept) {
                        rc = emulator_check_intercept(ctxt, ctxt->intercept,
                                                      X86_ICPT_PRE_EXCEPT);
                        if (rc != X86EMUL_CONTINUE)
@@ -4900,7 +5178,7 @@ int x86_emulate_insn(struct x86_emulate_ctxt *ctxt)
                                goto done;
                }
 
-               if (unlikely(ctxt->guest_mode) && (ctxt->d & Intercept)) {
+               if (unlikely(ctxt->emul_flags & X86EMUL_GUEST_MASK) && (ctxt->d & Intercept)) {
                        rc = emulator_check_intercept(ctxt, ctxt->intercept,
                                                      X86_ICPT_POST_EXCEPT);
                        if (rc != X86EMUL_CONTINUE)
@@ -4910,6 +5188,7 @@ int x86_emulate_insn(struct x86_emulate_ctxt *ctxt)
                if (ctxt->rep_prefix && (ctxt->d & String)) {
                        /* All REP prefixes have the same first termination condition */
                        if (address_mask(ctxt, reg_read(ctxt, VCPU_REGS_RCX)) == 0) {
+                               string_registers_quirk(ctxt);
                                ctxt->eip = ctxt->_eip;
                                ctxt->eflags &= ~X86_EFLAGS_RF;
                                goto done;
@@ -4953,7 +5232,7 @@ int x86_emulate_insn(struct x86_emulate_ctxt *ctxt)
 
 special_insn:
 
-       if (unlikely(ctxt->guest_mode) && (ctxt->d & Intercept)) {
+       if (unlikely(ctxt->emul_flags & X86EMUL_GUEST_MASK) && (ctxt->d & Intercept)) {
                rc = emulator_check_intercept(ctxt, ctxt->intercept,
                                              X86_ICPT_POST_MEMACCESS);
                if (rc != X86EMUL_CONTINUE)
index 28146f03c51421ce12f728d69613ded0a65699fd..856f79105bb59fcb14650de8f2d35814115f646f 100644 (file)
@@ -349,6 +349,7 @@ static int ioapic_service(struct kvm_ioapic *ioapic, int irq, bool line_status)
        irqe.delivery_mode = entry->fields.delivery_mode << 8;
        irqe.level = 1;
        irqe.shorthand = 0;
+       irqe.msi_redir_hint = false;
 
        if (irqe.trig_mode == IOAPIC_EDGE_TRIG)
                ioapic->irr_delivered |= 1 << irq;
@@ -637,11 +638,9 @@ void kvm_ioapic_destroy(struct kvm *kvm)
        struct kvm_ioapic *ioapic = kvm->arch.vioapic;
 
        cancel_delayed_work_sync(&ioapic->eoi_inject);
-       if (ioapic) {
-               kvm_io_bus_unregister_dev(kvm, KVM_MMIO_BUS, &ioapic->dev);
-               kvm->arch.vioapic = NULL;
-               kfree(ioapic);
-       }
+       kvm_io_bus_unregister_dev(kvm, KVM_MMIO_BUS, &ioapic->dev);
+       kvm->arch.vioapic = NULL;
+       kfree(ioapic);
 }
 
 int kvm_get_ioapic(struct kvm *kvm, struct kvm_ioapic_state *state)
index 72298b3ac025a8dc820f4593e5c7f2278f3bb35a..9efff9e5b58c6cc5b3831b95709acd08c69e388a 100644 (file)
@@ -31,6 +31,8 @@
 
 #include "ioapic.h"
 
+#include "lapic.h"
+
 static int kvm_set_pic_irq(struct kvm_kernel_irq_routing_entry *e,
                           struct kvm *kvm, int irq_source_id, int level,
                           bool line_status)
@@ -48,11 +50,6 @@ static int kvm_set_ioapic_irq(struct kvm_kernel_irq_routing_entry *e,
                                line_status);
 }
 
-inline static bool kvm_is_dm_lowest_prio(struct kvm_lapic_irq *irq)
-{
-       return irq->delivery_mode == APIC_DM_LOWEST;
-}
-
 int kvm_irq_delivery_to_apic(struct kvm *kvm, struct kvm_lapic *src,
                struct kvm_lapic_irq *irq, unsigned long *dest_map)
 {
@@ -60,7 +57,7 @@ int kvm_irq_delivery_to_apic(struct kvm *kvm, struct kvm_lapic *src,
        struct kvm_vcpu *vcpu, *lowest = NULL;
 
        if (irq->dest_mode == 0 && irq->dest_id == 0xff &&
-                       kvm_is_dm_lowest_prio(irq)) {
+                       kvm_lowest_prio_delivery(irq)) {
                printk(KERN_INFO "kvm: apic: phys broadcast and lowest prio\n");
                irq->delivery_mode = APIC_DM_FIXED;
        }
@@ -76,7 +73,7 @@ int kvm_irq_delivery_to_apic(struct kvm *kvm, struct kvm_lapic *src,
                                        irq->dest_id, irq->dest_mode))
                        continue;
 
-               if (!kvm_is_dm_lowest_prio(irq)) {
+               if (!kvm_lowest_prio_delivery(irq)) {
                        if (r < 0)
                                r = 0;
                        r += kvm_apic_set_irq(vcpu, irq, dest_map);
@@ -106,9 +103,10 @@ static inline void kvm_set_msi_irq(struct kvm_kernel_irq_routing_entry *e,
        irq->dest_mode = (1 << MSI_ADDR_DEST_MODE_SHIFT) & e->msi.address_lo;
        irq->trig_mode = (1 << MSI_DATA_TRIGGER_SHIFT) & e->msi.data;
        irq->delivery_mode = e->msi.data & 0x700;
+       irq->msi_redir_hint = ((e->msi.address_lo
+               & MSI_ADDR_REDIRECTION_LOWPRI) > 0);
        irq->level = 1;
        irq->shorthand = 0;
-       /* TODO Deal with RH bit of MSI message address */
 }
 
 int kvm_set_msi(struct kvm_kernel_irq_routing_entry *e,
index 544076c4f44bc330ba28b74b07ad3966e990168f..e1e89ee4af750dc51f78cfbf8aa71e22d77b4cd1 100644 (file)
@@ -99,4 +99,9 @@ static inline bool is_guest_mode(struct kvm_vcpu *vcpu)
        return vcpu->arch.hflags & HF_GUEST_MASK;
 }
 
+static inline bool is_smm(struct kvm_vcpu *vcpu)
+{
+       return vcpu->arch.hflags & HF_SMM_MASK;
+}
+
 #endif
index 629af0f1c5c4d0953010adc88233132bcdff4cb7..36e9de1b4127c5576ceb292c6ef320eae830df24 100644 (file)
@@ -240,6 +240,15 @@ static inline void kvm_apic_set_ldr(struct kvm_lapic *apic, u32 id)
        recalculate_apic_map(apic->vcpu->kvm);
 }
 
+static inline void kvm_apic_set_x2apic_id(struct kvm_lapic *apic, u8 id)
+{
+       u32 ldr = ((id >> 4) << 16) | (1 << (id & 0xf));
+
+       apic_set_reg(apic, APIC_ID, id << 24);
+       apic_set_reg(apic, APIC_LDR, ldr);
+       recalculate_apic_map(apic->vcpu->kvm);
+}
+
 static inline int apic_lvt_enabled(struct kvm_lapic *apic, int lvt_type)
 {
        return !(kvm_apic_get_reg(apic, lvt_type) & APIC_LVT_MASKED);
@@ -728,7 +737,7 @@ bool kvm_irq_delivery_to_apic_fast(struct kvm *kvm, struct kvm_lapic *src,
 
                dst = map->logical_map[cid];
 
-               if (irq->delivery_mode == APIC_DM_LOWEST) {
+               if (kvm_lowest_prio_delivery(irq)) {
                        int l = -1;
                        for_each_set_bit(i, &bitmap, 16) {
                                if (!dst[i])
@@ -799,7 +808,9 @@ static int __apic_accept_irq(struct kvm_lapic *apic, int delivery_mode,
                break;
 
        case APIC_DM_SMI:
-               apic_debug("Ignoring guest SMI\n");
+               result = 1;
+               kvm_make_request(KVM_REQ_SMI, vcpu);
+               kvm_vcpu_kick(vcpu);
                break;
 
        case APIC_DM_NMI:
@@ -914,9 +925,10 @@ static void apic_send_ipi(struct kvm_lapic *apic)
        irq.vector = icr_low & APIC_VECTOR_MASK;
        irq.delivery_mode = icr_low & APIC_MODE_MASK;
        irq.dest_mode = icr_low & APIC_DEST_MASK;
-       irq.level = icr_low & APIC_INT_ASSERT;
+       irq.level = (icr_low & APIC_INT_ASSERT) != 0;
        irq.trig_mode = icr_low & APIC_INT_LEVELTRIG;
        irq.shorthand = icr_low & APIC_SHORT_MASK;
+       irq.msi_redir_hint = false;
        if (apic_x2apic_mode(apic))
                irq.dest_id = icr_high;
        else
@@ -926,10 +938,11 @@ static void apic_send_ipi(struct kvm_lapic *apic)
 
        apic_debug("icr_high 0x%x, icr_low 0x%x, "
                   "short_hand 0x%x, dest 0x%x, trig_mode 0x%x, level 0x%x, "
-                  "dest_mode 0x%x, delivery_mode 0x%x, vector 0x%x\n",
+                  "dest_mode 0x%x, delivery_mode 0x%x, vector 0x%x, "
+                  "msi_redir_hint 0x%x\n",
                   icr_high, icr_low, irq.shorthand, irq.dest_id,
                   irq.trig_mode, irq.level, irq.dest_mode, irq.delivery_mode,
-                  irq.vector);
+                  irq.vector, irq.msi_redir_hint);
 
        kvm_irq_delivery_to_apic(apic->vcpu->kvm, apic, &irq, NULL);
 }
@@ -1090,6 +1103,17 @@ static void update_divide_count(struct kvm_lapic *apic)
                                   apic->divide_count);
 }
 
+static void apic_update_lvtt(struct kvm_lapic *apic)
+{
+       u32 timer_mode = kvm_apic_get_reg(apic, APIC_LVTT) &
+                       apic->lapic_timer.timer_mode_mask;
+
+       if (apic->lapic_timer.timer_mode != timer_mode) {
+               apic->lapic_timer.timer_mode = timer_mode;
+               hrtimer_cancel(&apic->lapic_timer.timer);
+       }
+}
+
 static void apic_timer_expired(struct kvm_lapic *apic)
 {
        struct kvm_vcpu *vcpu = apic->vcpu;
@@ -1298,6 +1322,7 @@ static int apic_reg_write(struct kvm_lapic *apic, u32 reg, u32 val)
                                apic_set_reg(apic, APIC_LVTT + 0x10 * i,
                                             lvt_val | APIC_LVT_MASKED);
                        }
+                       apic_update_lvtt(apic);
                        atomic_set(&apic->lapic_timer.pending, 0);
 
                }
@@ -1330,20 +1355,13 @@ static int apic_reg_write(struct kvm_lapic *apic, u32 reg, u32 val)
 
                break;
 
-       case APIC_LVTT: {
-               u32 timer_mode = val & apic->lapic_timer.timer_mode_mask;
-
-               if (apic->lapic_timer.timer_mode != timer_mode) {
-                       apic->lapic_timer.timer_mode = timer_mode;
-                       hrtimer_cancel(&apic->lapic_timer.timer);
-               }
-
+       case APIC_LVTT:
                if (!kvm_apic_sw_enabled(apic))
                        val |= APIC_LVT_MASKED;
                val &= (apic_lvt_mask[0] | apic->lapic_timer.timer_mode_mask);
                apic_set_reg(apic, APIC_LVTT, val);
+               apic_update_lvtt(apic);
                break;
-       }
 
        case APIC_TMICT:
                if (apic_lvtt_tscdeadline(apic))
@@ -1536,9 +1554,7 @@ void kvm_lapic_set_base(struct kvm_vcpu *vcpu, u64 value)
 
        if ((old_value ^ value) & X2APIC_ENABLE) {
                if (value & X2APIC_ENABLE) {
-                       u32 id = kvm_apic_id(apic);
-                       u32 ldr = ((id >> 4) << 16) | (1 << (id & 0xf));
-                       kvm_apic_set_ldr(apic, ldr);
+                       kvm_apic_set_x2apic_id(apic, vcpu->vcpu_id);
                        kvm_x86_ops->set_virtual_x2apic_mode(vcpu, true);
                } else
                        kvm_x86_ops->set_virtual_x2apic_mode(vcpu, false);
@@ -1557,7 +1573,7 @@ void kvm_lapic_set_base(struct kvm_vcpu *vcpu, u64 value)
 
 }
 
-void kvm_lapic_reset(struct kvm_vcpu *vcpu)
+void kvm_lapic_reset(struct kvm_vcpu *vcpu, bool init_event)
 {
        struct kvm_lapic *apic;
        int i;
@@ -1571,19 +1587,22 @@ void kvm_lapic_reset(struct kvm_vcpu *vcpu)
        /* Stop the timer in case it's a reset to an active apic */
        hrtimer_cancel(&apic->lapic_timer.timer);
 
-       kvm_apic_set_id(apic, vcpu->vcpu_id);
+       if (!init_event)
+               kvm_apic_set_id(apic, vcpu->vcpu_id);
        kvm_apic_set_version(apic->vcpu);
 
        for (i = 0; i < APIC_LVT_NUM; i++)
                apic_set_reg(apic, APIC_LVTT + 0x10 * i, APIC_LVT_MASKED);
-       apic->lapic_timer.timer_mode = 0;
-       apic_set_reg(apic, APIC_LVT0,
-                    SET_APIC_DELIVERY_MODE(0, APIC_MODE_EXTINT));
+       apic_update_lvtt(apic);
+       if (!(vcpu->kvm->arch.disabled_quirks & KVM_QUIRK_LINT0_REENABLED))
+               apic_set_reg(apic, APIC_LVT0,
+                            SET_APIC_DELIVERY_MODE(0, APIC_MODE_EXTINT));
 
        apic_set_reg(apic, APIC_DFR, 0xffffffffU);
        apic_set_spiv(apic, 0xff);
        apic_set_reg(apic, APIC_TASKPRI, 0);
-       kvm_apic_set_ldr(apic, 0);
+       if (!apic_x2apic_mode(apic))
+               kvm_apic_set_ldr(apic, 0);
        apic_set_reg(apic, APIC_ESR, 0);
        apic_set_reg(apic, APIC_ICR, 0);
        apic_set_reg(apic, APIC_ICR2, 0);
@@ -1712,7 +1731,7 @@ int kvm_create_lapic(struct kvm_vcpu *vcpu)
                        APIC_DEFAULT_PHYS_BASE | MSR_IA32_APICBASE_ENABLE);
 
        static_key_slow_inc(&apic_sw_disabled.key); /* sw disabled at reset */
-       kvm_lapic_reset(vcpu);
+       kvm_lapic_reset(vcpu, false);
        kvm_iodevice_init(&apic->dev, &apic_mmio_ops);
 
        return 0;
@@ -1802,6 +1821,7 @@ void kvm_apic_post_state_restore(struct kvm_vcpu *vcpu,
 
        apic_update_ppr(apic);
        hrtimer_cancel(&apic->lapic_timer.timer);
+       apic_update_lvtt(apic);
        update_divide_count(apic);
        start_apic_timer(apic);
        apic->irr_pending = true;
@@ -2043,11 +2063,22 @@ void kvm_apic_accept_events(struct kvm_vcpu *vcpu)
        if (!kvm_vcpu_has_lapic(vcpu) || !apic->pending_events)
                return;
 
-       pe = xchg(&apic->pending_events, 0);
+       /*
+        * INITs are latched while in SMM.  Because an SMM CPU cannot
+        * be in KVM_MP_STATE_INIT_RECEIVED state, just eat SIPIs
+        * and delay processing of INIT until the next RSM.
+        */
+       if (is_smm(vcpu)) {
+               WARN_ON_ONCE(vcpu->arch.mp_state == KVM_MP_STATE_INIT_RECEIVED);
+               if (test_bit(KVM_APIC_SIPI, &apic->pending_events))
+                       clear_bit(KVM_APIC_SIPI, &apic->pending_events);
+               return;
+       }
 
+       pe = xchg(&apic->pending_events, 0);
        if (test_bit(KVM_APIC_INIT, &pe)) {
-               kvm_lapic_reset(vcpu);
-               kvm_vcpu_reset(vcpu);
+               kvm_lapic_reset(vcpu, true);
+               kvm_vcpu_reset(vcpu, true);
                if (kvm_vcpu_is_bsp(apic->vcpu))
                        vcpu->arch.mp_state = KVM_MP_STATE_RUNNABLE;
                else
index 9d28383fc1e70cc3437ae2eb062ff9433c659f96..f2f4e10ab7724640fb02ea3b6ea31ec08cfe95a4 100644 (file)
@@ -48,7 +48,7 @@ int kvm_apic_has_interrupt(struct kvm_vcpu *vcpu);
 int kvm_apic_accept_pic_intr(struct kvm_vcpu *vcpu);
 int kvm_get_apic_interrupt(struct kvm_vcpu *vcpu);
 void kvm_apic_accept_events(struct kvm_vcpu *vcpu);
-void kvm_lapic_reset(struct kvm_vcpu *vcpu);
+void kvm_lapic_reset(struct kvm_vcpu *vcpu, bool init_event);
 u64 kvm_lapic_get_cr8(struct kvm_vcpu *vcpu);
 void kvm_lapic_set_tpr(struct kvm_vcpu *vcpu, unsigned long cr8);
 void kvm_lapic_set_eoi(struct kvm_vcpu *vcpu);
@@ -150,7 +150,18 @@ static inline bool kvm_apic_vid_enabled(struct kvm *kvm)
 
 static inline bool kvm_apic_has_events(struct kvm_vcpu *vcpu)
 {
-       return vcpu->arch.apic->pending_events;
+       return kvm_vcpu_has_lapic(vcpu) && vcpu->arch.apic->pending_events;
+}
+
+static inline bool kvm_lowest_prio_delivery(struct kvm_lapic_irq *irq)
+{
+       return (irq->delivery_mode == APIC_DM_LOWEST ||
+                       irq->msi_redir_hint);
+}
+
+static inline int kvm_lapic_latched_init(struct kvm_vcpu *vcpu)
+{
+       return kvm_vcpu_has_lapic(vcpu) && test_bit(KVM_APIC_INIT, &vcpu->arch.apic->pending_events);
 }
 
 bool kvm_apic_pending_eoi(struct kvm_vcpu *vcpu, int vector);
index 44a7d25154973437e0ce4233e142d01c43a948b9..f807496b62c2cc76e82a60cd58ee187f0cdc77c2 100644 (file)
@@ -223,15 +223,15 @@ static unsigned int get_mmio_spte_generation(u64 spte)
        return gen;
 }
 
-static unsigned int kvm_current_mmio_generation(struct kvm *kvm)
+static unsigned int kvm_current_mmio_generation(struct kvm_vcpu *vcpu)
 {
-       return kvm_memslots(kvm)->generation & MMIO_GEN_MASK;
+       return kvm_vcpu_memslots(vcpu)->generation & MMIO_GEN_MASK;
 }
 
-static void mark_mmio_spte(struct kvm *kvm, u64 *sptep, u64 gfn,
+static void mark_mmio_spte(struct kvm_vcpu *vcpu, u64 *sptep, u64 gfn,
                           unsigned access)
 {
-       unsigned int gen = kvm_current_mmio_generation(kvm);
+       unsigned int gen = kvm_current_mmio_generation(vcpu);
        u64 mask = generation_mmio_spte_mask(gen);
 
        access &= ACC_WRITE_MASK | ACC_USER_MASK;
@@ -258,22 +258,22 @@ static unsigned get_mmio_spte_access(u64 spte)
        return (spte & ~mask) & ~PAGE_MASK;
 }
 
-static bool set_mmio_spte(struct kvm *kvm, u64 *sptep, gfn_t gfn,
+static bool set_mmio_spte(struct kvm_vcpu *vcpu, u64 *sptep, gfn_t gfn,
                          pfn_t pfn, unsigned access)
 {
        if (unlikely(is_noslot_pfn(pfn))) {
-               mark_mmio_spte(kvm, sptep, gfn, access);
+               mark_mmio_spte(vcpu, sptep, gfn, access);
                return true;
        }
 
        return false;
 }
 
-static bool check_mmio_spte(struct kvm *kvm, u64 spte)
+static bool check_mmio_spte(struct kvm_vcpu *vcpu, u64 spte)
 {
        unsigned int kvm_gen, spte_gen;
 
-       kvm_gen = kvm_current_mmio_generation(kvm);
+       kvm_gen = kvm_current_mmio_generation(vcpu);
        spte_gen = get_mmio_spte_generation(spte);
 
        trace_check_mmio_spte(spte, kvm_gen, spte_gen);
@@ -804,30 +804,36 @@ static struct kvm_lpage_info *lpage_info_slot(gfn_t gfn,
        return &slot->arch.lpage_info[level - 2][idx];
 }
 
-static void account_shadowed(struct kvm *kvm, gfn_t gfn)
+static void account_shadowed(struct kvm *kvm, struct kvm_mmu_page *sp)
 {
+       struct kvm_memslots *slots;
        struct kvm_memory_slot *slot;
        struct kvm_lpage_info *linfo;
+       gfn_t gfn;
        int i;
 
-       slot = gfn_to_memslot(kvm, gfn);
-       for (i = PT_DIRECTORY_LEVEL;
-            i < PT_PAGE_TABLE_LEVEL + KVM_NR_PAGE_SIZES; ++i) {
+       gfn = sp->gfn;
+       slots = kvm_memslots_for_spte_role(kvm, sp->role);
+       slot = __gfn_to_memslot(slots, gfn);
+       for (i = PT_DIRECTORY_LEVEL; i <= PT_MAX_HUGEPAGE_LEVEL; ++i) {
                linfo = lpage_info_slot(gfn, slot, i);
                linfo->write_count += 1;
        }
        kvm->arch.indirect_shadow_pages++;
 }
 
-static void unaccount_shadowed(struct kvm *kvm, gfn_t gfn)
+static void unaccount_shadowed(struct kvm *kvm, struct kvm_mmu_page *sp)
 {
+       struct kvm_memslots *slots;
        struct kvm_memory_slot *slot;
        struct kvm_lpage_info *linfo;
+       gfn_t gfn;
        int i;
 
-       slot = gfn_to_memslot(kvm, gfn);
-       for (i = PT_DIRECTORY_LEVEL;
-            i < PT_PAGE_TABLE_LEVEL + KVM_NR_PAGE_SIZES; ++i) {
+       gfn = sp->gfn;
+       slots = kvm_memslots_for_spte_role(kvm, sp->role);
+       slot = __gfn_to_memslot(slots, gfn);
+       for (i = PT_DIRECTORY_LEVEL; i <= PT_MAX_HUGEPAGE_LEVEL; ++i) {
                linfo = lpage_info_slot(gfn, slot, i);
                linfo->write_count -= 1;
                WARN_ON(linfo->write_count < 0);
@@ -835,14 +841,14 @@ static void unaccount_shadowed(struct kvm *kvm, gfn_t gfn)
        kvm->arch.indirect_shadow_pages--;
 }
 
-static int has_wrprotected_page(struct kvm *kvm,
+static int has_wrprotected_page(struct kvm_vcpu *vcpu,
                                gfn_t gfn,
                                int level)
 {
        struct kvm_memory_slot *slot;
        struct kvm_lpage_info *linfo;
 
-       slot = gfn_to_memslot(kvm, gfn);
+       slot = kvm_vcpu_gfn_to_memslot(vcpu, gfn);
        if (slot) {
                linfo = lpage_info_slot(gfn, slot, level);
                return linfo->write_count;
@@ -858,8 +864,7 @@ static int host_mapping_level(struct kvm *kvm, gfn_t gfn)
 
        page_size = kvm_host_page_size(kvm, gfn);
 
-       for (i = PT_PAGE_TABLE_LEVEL;
-            i < (PT_PAGE_TABLE_LEVEL + KVM_NR_PAGE_SIZES); ++i) {
+       for (i = PT_PAGE_TABLE_LEVEL; i <= PT_MAX_HUGEPAGE_LEVEL; ++i) {
                if (page_size >= KVM_HPAGE_SIZE(i))
                        ret = i;
                else
@@ -875,7 +880,7 @@ gfn_to_memslot_dirty_bitmap(struct kvm_vcpu *vcpu, gfn_t gfn,
 {
        struct kvm_memory_slot *slot;
 
-       slot = gfn_to_memslot(vcpu->kvm, gfn);
+       slot = kvm_vcpu_gfn_to_memslot(vcpu, gfn);
        if (!slot || slot->flags & KVM_MEMSLOT_INVALID ||
              (no_dirty_log && slot->dirty_bitmap))
                slot = NULL;
@@ -900,7 +905,7 @@ static int mapping_level(struct kvm_vcpu *vcpu, gfn_t large_gfn)
        max_level = min(kvm_x86_ops->get_lpage_level(), host_level);
 
        for (level = PT_DIRECTORY_LEVEL; level <= max_level; ++level)
-               if (has_wrprotected_page(vcpu->kvm, large_gfn, level))
+               if (has_wrprotected_page(vcpu, large_gfn, level))
                        break;
 
        return level - 1;
@@ -1042,12 +1047,14 @@ static unsigned long *__gfn_to_rmap(gfn_t gfn, int level,
 /*
  * Take gfn and return the reverse mapping to it.
  */
-static unsigned long *gfn_to_rmap(struct kvm *kvm, gfn_t gfn, int level)
+static unsigned long *gfn_to_rmap(struct kvm *kvm, gfn_t gfn, struct kvm_mmu_page *sp)
 {
+       struct kvm_memslots *slots;
        struct kvm_memory_slot *slot;
 
-       slot = gfn_to_memslot(kvm, gfn);
-       return __gfn_to_rmap(gfn, level, slot);
+       slots = kvm_memslots_for_spte_role(kvm, sp->role);
+       slot = __gfn_to_memslot(slots, gfn);
+       return __gfn_to_rmap(gfn, sp->role.level, slot);
 }
 
 static bool rmap_can_add(struct kvm_vcpu *vcpu)
@@ -1065,7 +1072,7 @@ static int rmap_add(struct kvm_vcpu *vcpu, u64 *spte, gfn_t gfn)
 
        sp = page_header(__pa(spte));
        kvm_mmu_page_set_gfn(sp, spte - sp->spt, gfn);
-       rmapp = gfn_to_rmap(vcpu->kvm, gfn, sp->role.level);
+       rmapp = gfn_to_rmap(vcpu->kvm, gfn, sp);
        return pte_list_add(vcpu, spte, rmapp);
 }
 
@@ -1077,7 +1084,7 @@ static void rmap_remove(struct kvm *kvm, u64 *spte)
 
        sp = page_header(__pa(spte));
        gfn = kvm_mmu_page_get_gfn(sp, spte - sp->spt);
-       rmapp = gfn_to_rmap(kvm, gfn, sp->role.level);
+       rmapp = gfn_to_rmap(kvm, gfn, sp);
        pte_list_remove(spte, rmapp);
 }
 
@@ -1142,6 +1149,11 @@ static u64 *rmap_get_next(struct rmap_iterator *iter)
        return NULL;
 }
 
+#define for_each_rmap_spte(_rmap_, _iter_, _spte_)                         \
+          for (_spte_ = rmap_get_first(*_rmap_, _iter_);                   \
+               _spte_ && ({BUG_ON(!is_shadow_present_pte(*_spte_)); 1;});  \
+                       _spte_ = rmap_get_next(_iter_))
+
 static void drop_spte(struct kvm *kvm, u64 *sptep)
 {
        if (mmu_spte_clear_track_bits(sptep))
@@ -1205,12 +1217,8 @@ static bool __rmap_write_protect(struct kvm *kvm, unsigned long *rmapp,
        struct rmap_iterator iter;
        bool flush = false;
 
-       for (sptep = rmap_get_first(*rmapp, &iter); sptep;) {
-               BUG_ON(!(*sptep & PT_PRESENT_MASK));
-
+       for_each_rmap_spte(rmapp, &iter, sptep)
                flush |= spte_write_protect(kvm, sptep, pt_protect);
-               sptep = rmap_get_next(&iter);
-       }
 
        return flush;
 }
@@ -1232,12 +1240,8 @@ static bool __rmap_clear_dirty(struct kvm *kvm, unsigned long *rmapp)
        struct rmap_iterator iter;
        bool flush = false;
 
-       for (sptep = rmap_get_first(*rmapp, &iter); sptep;) {
-               BUG_ON(!(*sptep & PT_PRESENT_MASK));
-
+       for_each_rmap_spte(rmapp, &iter, sptep)
                flush |= spte_clear_dirty(kvm, sptep);
-               sptep = rmap_get_next(&iter);
-       }
 
        return flush;
 }
@@ -1259,12 +1263,8 @@ static bool __rmap_set_dirty(struct kvm *kvm, unsigned long *rmapp)
        struct rmap_iterator iter;
        bool flush = false;
 
-       for (sptep = rmap_get_first(*rmapp, &iter); sptep;) {
-               BUG_ON(!(*sptep & PT_PRESENT_MASK));
-
+       for_each_rmap_spte(rmapp, &iter, sptep)
                flush |= spte_set_dirty(kvm, sptep);
-               sptep = rmap_get_next(&iter);
-       }
 
        return flush;
 }
@@ -1342,42 +1342,45 @@ void kvm_arch_mmu_enable_log_dirty_pt_masked(struct kvm *kvm,
                kvm_mmu_write_protect_pt_masked(kvm, slot, gfn_offset, mask);
 }
 
-static bool rmap_write_protect(struct kvm *kvm, u64 gfn)
+static bool rmap_write_protect(struct kvm_vcpu *vcpu, u64 gfn)
 {
        struct kvm_memory_slot *slot;
        unsigned long *rmapp;
        int i;
        bool write_protected = false;
 
-       slot = gfn_to_memslot(kvm, gfn);
+       slot = kvm_vcpu_gfn_to_memslot(vcpu, gfn);
 
-       for (i = PT_PAGE_TABLE_LEVEL;
-            i < PT_PAGE_TABLE_LEVEL + KVM_NR_PAGE_SIZES; ++i) {
+       for (i = PT_PAGE_TABLE_LEVEL; i <= PT_MAX_HUGEPAGE_LEVEL; ++i) {
                rmapp = __gfn_to_rmap(gfn, i, slot);
-               write_protected |= __rmap_write_protect(kvm, rmapp, true);
+               write_protected |= __rmap_write_protect(vcpu->kvm, rmapp, true);
        }
 
        return write_protected;
 }
 
-static int kvm_unmap_rmapp(struct kvm *kvm, unsigned long *rmapp,
-                          struct kvm_memory_slot *slot, gfn_t gfn, int level,
-                          unsigned long data)
+static bool kvm_zap_rmapp(struct kvm *kvm, unsigned long *rmapp)
 {
        u64 *sptep;
        struct rmap_iterator iter;
-       int need_tlb_flush = 0;
+       bool flush = false;
 
        while ((sptep = rmap_get_first(*rmapp, &iter))) {
                BUG_ON(!(*sptep & PT_PRESENT_MASK));
-               rmap_printk("kvm_rmap_unmap_hva: spte %p %llx gfn %llx (%d)\n",
-                            sptep, *sptep, gfn, level);
+               rmap_printk("%s: spte %p %llx.\n", __func__, sptep, *sptep);
 
                drop_spte(kvm, sptep);
-               need_tlb_flush = 1;
+               flush = true;
        }
 
-       return need_tlb_flush;
+       return flush;
+}
+
+static int kvm_unmap_rmapp(struct kvm *kvm, unsigned long *rmapp,
+                          struct kvm_memory_slot *slot, gfn_t gfn, int level,
+                          unsigned long data)
+{
+       return kvm_zap_rmapp(kvm, rmapp);
 }
 
 static int kvm_set_pte_rmapp(struct kvm *kvm, unsigned long *rmapp,
@@ -1394,8 +1397,8 @@ static int kvm_set_pte_rmapp(struct kvm *kvm, unsigned long *rmapp,
        WARN_ON(pte_huge(*ptep));
        new_pfn = pte_pfn(*ptep);
 
-       for (sptep = rmap_get_first(*rmapp, &iter); sptep;) {
-               BUG_ON(!is_shadow_present_pte(*sptep));
+restart:
+       for_each_rmap_spte(rmapp, &iter, sptep) {
                rmap_printk("kvm_set_pte_rmapp: spte %p %llx gfn %llx (%d)\n",
                             sptep, *sptep, gfn, level);
 
@@ -1403,7 +1406,7 @@ static int kvm_set_pte_rmapp(struct kvm *kvm, unsigned long *rmapp,
 
                if (pte_write(*ptep)) {
                        drop_spte(kvm, sptep);
-                       sptep = rmap_get_first(*rmapp, &iter);
+                       goto restart;
                } else {
                        new_spte = *sptep & ~PT64_BASE_ADDR_MASK;
                        new_spte |= (u64)new_pfn << PAGE_SHIFT;
@@ -1414,7 +1417,6 @@ static int kvm_set_pte_rmapp(struct kvm *kvm, unsigned long *rmapp,
 
                        mmu_spte_clear_track_bits(sptep);
                        mmu_spte_set(sptep, new_spte);
-                       sptep = rmap_get_next(&iter);
                }
        }
 
@@ -1424,6 +1426,74 @@ static int kvm_set_pte_rmapp(struct kvm *kvm, unsigned long *rmapp,
        return 0;
 }
 
+struct slot_rmap_walk_iterator {
+       /* input fields. */
+       struct kvm_memory_slot *slot;
+       gfn_t start_gfn;
+       gfn_t end_gfn;
+       int start_level;
+       int end_level;
+
+       /* output fields. */
+       gfn_t gfn;
+       unsigned long *rmap;
+       int level;
+
+       /* private field. */
+       unsigned long *end_rmap;
+};
+
+static void
+rmap_walk_init_level(struct slot_rmap_walk_iterator *iterator, int level)
+{
+       iterator->level = level;
+       iterator->gfn = iterator->start_gfn;
+       iterator->rmap = __gfn_to_rmap(iterator->gfn, level, iterator->slot);
+       iterator->end_rmap = __gfn_to_rmap(iterator->end_gfn, level,
+                                          iterator->slot);
+}
+
+static void
+slot_rmap_walk_init(struct slot_rmap_walk_iterator *iterator,
+                   struct kvm_memory_slot *slot, int start_level,
+                   int end_level, gfn_t start_gfn, gfn_t end_gfn)
+{
+       iterator->slot = slot;
+       iterator->start_level = start_level;
+       iterator->end_level = end_level;
+       iterator->start_gfn = start_gfn;
+       iterator->end_gfn = end_gfn;
+
+       rmap_walk_init_level(iterator, iterator->start_level);
+}
+
+static bool slot_rmap_walk_okay(struct slot_rmap_walk_iterator *iterator)
+{
+       return !!iterator->rmap;
+}
+
+static void slot_rmap_walk_next(struct slot_rmap_walk_iterator *iterator)
+{
+       if (++iterator->rmap <= iterator->end_rmap) {
+               iterator->gfn += (1UL << KVM_HPAGE_GFN_SHIFT(iterator->level));
+               return;
+       }
+
+       if (++iterator->level > iterator->end_level) {
+               iterator->rmap = NULL;
+               return;
+       }
+
+       rmap_walk_init_level(iterator, iterator->level);
+}
+
+#define for_each_slot_rmap_range(_slot_, _start_level_, _end_level_,   \
+          _start_gfn, _end_gfn, _iter_)                                \
+       for (slot_rmap_walk_init(_iter_, _slot_, _start_level_,         \
+                                _end_level_, _start_gfn, _end_gfn);    \
+            slot_rmap_walk_okay(_iter_);                               \
+            slot_rmap_walk_next(_iter_))
+
 static int kvm_handle_hva_range(struct kvm *kvm,
                                unsigned long start,
                                unsigned long end,
@@ -1435,48 +1505,36 @@ static int kvm_handle_hva_range(struct kvm *kvm,
                                               int level,
                                               unsigned long data))
 {
-       int j;
-       int ret = 0;
        struct kvm_memslots *slots;
        struct kvm_memory_slot *memslot;
+       struct slot_rmap_walk_iterator iterator;
+       int ret = 0;
+       int i;
 
-       slots = kvm_memslots(kvm);
-
-       kvm_for_each_memslot(memslot, slots) {
-               unsigned long hva_start, hva_end;
-               gfn_t gfn_start, gfn_end;
-
-               hva_start = max(start, memslot->userspace_addr);
-               hva_end = min(end, memslot->userspace_addr +
-                                       (memslot->npages << PAGE_SHIFT));
-               if (hva_start >= hva_end)
-                       continue;
-               /*
-                * {gfn(page) | page intersects with [hva_start, hva_end)} =
-                * {gfn_start, gfn_start+1, ..., gfn_end-1}.
-                */
-               gfn_start = hva_to_gfn_memslot(hva_start, memslot);
-               gfn_end = hva_to_gfn_memslot(hva_end + PAGE_SIZE - 1, memslot);
-
-               for (j = PT_PAGE_TABLE_LEVEL;
-                    j < PT_PAGE_TABLE_LEVEL + KVM_NR_PAGE_SIZES; ++j) {
-                       unsigned long idx, idx_end;
-                       unsigned long *rmapp;
-                       gfn_t gfn = gfn_start;
+       for (i = 0; i < KVM_ADDRESS_SPACE_NUM; i++) {
+               slots = __kvm_memslots(kvm, i);
+               kvm_for_each_memslot(memslot, slots) {
+                       unsigned long hva_start, hva_end;
+                       gfn_t gfn_start, gfn_end;
 
+                       hva_start = max(start, memslot->userspace_addr);
+                       hva_end = min(end, memslot->userspace_addr +
+                                     (memslot->npages << PAGE_SHIFT));
+                       if (hva_start >= hva_end)
+                               continue;
                        /*
-                        * {idx(page_j) | page_j intersects with
-                        *  [hva_start, hva_end)} = {idx, idx+1, ..., idx_end}.
+                        * {gfn(page) | page intersects with [hva_start, hva_end)} =
+                        * {gfn_start, gfn_start+1, ..., gfn_end-1}.
                         */
-                       idx = gfn_to_index(gfn_start, memslot->base_gfn, j);
-                       idx_end = gfn_to_index(gfn_end - 1, memslot->base_gfn, j);
-
-                       rmapp = __gfn_to_rmap(gfn_start, j, memslot);
-
-                       for (; idx <= idx_end;
-                              ++idx, gfn += (1UL << KVM_HPAGE_GFN_SHIFT(j)))
-                               ret |= handler(kvm, rmapp++, memslot,
-                                              gfn, j, data);
+                       gfn_start = hva_to_gfn_memslot(hva_start, memslot);
+                       gfn_end = hva_to_gfn_memslot(hva_end + PAGE_SIZE - 1, memslot);
+
+                       for_each_slot_rmap_range(memslot, PT_PAGE_TABLE_LEVEL,
+                                                PT_MAX_HUGEPAGE_LEVEL,
+                                                gfn_start, gfn_end - 1,
+                                                &iterator)
+                               ret |= handler(kvm, iterator.rmap, memslot,
+                                              iterator.gfn, iterator.level, data);
                }
        }
 
@@ -1518,16 +1576,13 @@ static int kvm_age_rmapp(struct kvm *kvm, unsigned long *rmapp,
 
        BUG_ON(!shadow_accessed_mask);
 
-       for (sptep = rmap_get_first(*rmapp, &iter); sptep;
-            sptep = rmap_get_next(&iter)) {
-               BUG_ON(!is_shadow_present_pte(*sptep));
-
+       for_each_rmap_spte(rmapp, &iter, sptep)
                if (*sptep & shadow_accessed_mask) {
                        young = 1;
                        clear_bit((ffs(shadow_accessed_mask) - 1),
                                 (unsigned long *)sptep);
                }
-       }
+
        trace_kvm_age_page(gfn, level, slot, young);
        return young;
 }
@@ -1548,15 +1603,11 @@ static int kvm_test_age_rmapp(struct kvm *kvm, unsigned long *rmapp,
        if (!shadow_accessed_mask)
                goto out;
 
-       for (sptep = rmap_get_first(*rmapp, &iter); sptep;
-            sptep = rmap_get_next(&iter)) {
-               BUG_ON(!is_shadow_present_pte(*sptep));
-
+       for_each_rmap_spte(rmapp, &iter, sptep)
                if (*sptep & shadow_accessed_mask) {
                        young = 1;
                        break;
                }
-       }
 out:
        return young;
 }
@@ -1570,7 +1621,7 @@ static void rmap_recycle(struct kvm_vcpu *vcpu, u64 *spte, gfn_t gfn)
 
        sp = page_header(__pa(spte));
 
-       rmapp = gfn_to_rmap(vcpu->kvm, gfn, sp->role.level);
+       rmapp = gfn_to_rmap(vcpu->kvm, gfn, sp);
 
        kvm_unmap_rmapp(vcpu->kvm, rmapp, NULL, gfn, sp->role.level, 0);
        kvm_flush_remote_tlbs(vcpu->kvm);
@@ -1990,7 +2041,7 @@ static void mmu_sync_children(struct kvm_vcpu *vcpu,
                bool protected = false;
 
                for_each_sp(pages, sp, parents, i)
-                       protected |= rmap_write_protect(vcpu->kvm, sp->gfn);
+                       protected |= rmap_write_protect(vcpu, sp->gfn);
 
                if (protected)
                        kvm_flush_remote_tlbs(vcpu->kvm);
@@ -2088,12 +2139,12 @@ static struct kvm_mmu_page *kvm_mmu_get_page(struct kvm_vcpu *vcpu,
        hlist_add_head(&sp->hash_link,
                &vcpu->kvm->arch.mmu_page_hash[kvm_page_table_hashfn(gfn)]);
        if (!direct) {
-               if (rmap_write_protect(vcpu->kvm, gfn))
+               if (rmap_write_protect(vcpu, gfn))
                        kvm_flush_remote_tlbs(vcpu->kvm);
                if (level > PT_PAGE_TABLE_LEVEL && need_sync)
                        kvm_sync_pages(vcpu, gfn);
 
-               account_shadowed(vcpu->kvm, gfn);
+               account_shadowed(vcpu->kvm, sp);
        }
        sp->mmu_valid_gen = vcpu->kvm->arch.mmu_valid_gen;
        init_shadow_page_table(sp);
@@ -2274,7 +2325,7 @@ static int kvm_mmu_prepare_zap_page(struct kvm *kvm, struct kvm_mmu_page *sp,
        kvm_mmu_unlink_parents(kvm, sp);
 
        if (!sp->role.invalid && !sp->role.direct)
-               unaccount_shadowed(kvm, sp->gfn);
+               unaccount_shadowed(kvm, sp);
 
        if (sp->unsync)
                kvm_unlink_unsync_page(kvm, sp);
@@ -2386,111 +2437,6 @@ int kvm_mmu_unprotect_page(struct kvm *kvm, gfn_t gfn)
 }
 EXPORT_SYMBOL_GPL(kvm_mmu_unprotect_page);
 
-/*
- * The function is based on mtrr_type_lookup() in
- * arch/x86/kernel/cpu/mtrr/generic.c
- */
-static int get_mtrr_type(struct mtrr_state_type *mtrr_state,
-                        u64 start, u64 end)
-{
-       int i;
-       u64 base, mask;
-       u8 prev_match, curr_match;
-       int num_var_ranges = KVM_NR_VAR_MTRR;
-
-       if (!mtrr_state->enabled)
-               return 0xFF;
-
-       /* Make end inclusive end, instead of exclusive */
-       end--;
-
-       /* Look in fixed ranges. Just return the type as per start */
-       if (mtrr_state->have_fixed && (start < 0x100000)) {
-               int idx;
-
-               if (start < 0x80000) {
-                       idx = 0;
-                       idx += (start >> 16);
-                       return mtrr_state->fixed_ranges[idx];
-               } else if (start < 0xC0000) {
-                       idx = 1 * 8;
-                       idx += ((start - 0x80000) >> 14);
-                       return mtrr_state->fixed_ranges[idx];
-               } else if (start < 0x1000000) {
-                       idx = 3 * 8;
-                       idx += ((start - 0xC0000) >> 12);
-                       return mtrr_state->fixed_ranges[idx];
-               }
-       }
-
-       /*
-        * Look in variable ranges
-        * Look of multiple ranges matching this address and pick type
-        * as per MTRR precedence
-        */
-       if (!(mtrr_state->enabled & 2))
-               return mtrr_state->def_type;
-
-       prev_match = 0xFF;
-       for (i = 0; i < num_var_ranges; ++i) {
-               unsigned short start_state, end_state;
-
-               if (!(mtrr_state->var_ranges[i].mask_lo & (1 << 11)))
-                       continue;
-
-               base = (((u64)mtrr_state->var_ranges[i].base_hi) << 32) +
-                      (mtrr_state->var_ranges[i].base_lo & PAGE_MASK);
-               mask = (((u64)mtrr_state->var_ranges[i].mask_hi) << 32) +
-                      (mtrr_state->var_ranges[i].mask_lo & PAGE_MASK);
-
-               start_state = ((start & mask) == (base & mask));
-               end_state = ((end & mask) == (base & mask));
-               if (start_state != end_state)
-                       return 0xFE;
-
-               if ((start & mask) != (base & mask))
-                       continue;
-
-               curr_match = mtrr_state->var_ranges[i].base_lo & 0xff;
-               if (prev_match == 0xFF) {
-                       prev_match = curr_match;
-                       continue;
-               }
-
-               if (prev_match == MTRR_TYPE_UNCACHABLE ||
-                   curr_match == MTRR_TYPE_UNCACHABLE)
-                       return MTRR_TYPE_UNCACHABLE;
-
-               if ((prev_match == MTRR_TYPE_WRBACK &&
-                    curr_match == MTRR_TYPE_WRTHROUGH) ||
-                   (prev_match == MTRR_TYPE_WRTHROUGH &&
-                    curr_match == MTRR_TYPE_WRBACK)) {
-                       prev_match = MTRR_TYPE_WRTHROUGH;
-                       curr_match = MTRR_TYPE_WRTHROUGH;
-               }
-
-               if (prev_match != curr_match)
-                       return MTRR_TYPE_UNCACHABLE;
-       }
-
-       if (prev_match != 0xFF)
-               return prev_match;
-
-       return mtrr_state->def_type;
-}
-
-u8 kvm_get_guest_memory_type(struct kvm_vcpu *vcpu, gfn_t gfn)
-{
-       u8 mtrr;
-
-       mtrr = get_mtrr_type(&vcpu->arch.mtrr_state, gfn << PAGE_SHIFT,
-                            (gfn << PAGE_SHIFT) + PAGE_SIZE);
-       if (mtrr == 0xfe || mtrr == 0xff)
-               mtrr = MTRR_TYPE_WRBACK;
-       return mtrr;
-}
-EXPORT_SYMBOL_GPL(kvm_get_guest_memory_type);
-
 static void __kvm_unsync_page(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp)
 {
        trace_kvm_mmu_unsync_page(sp);
@@ -2541,7 +2487,7 @@ static int set_spte(struct kvm_vcpu *vcpu, u64 *sptep,
        u64 spte;
        int ret = 0;
 
-       if (set_mmio_spte(vcpu->kvm, sptep, gfn, pfn, pte_access))
+       if (set_mmio_spte(vcpu, sptep, gfn, pfn, pte_access))
                return 0;
 
        spte = PT_PRESENT_MASK;
@@ -2578,7 +2524,7 @@ static int set_spte(struct kvm_vcpu *vcpu, u64 *sptep,
                 * be fixed if guest refault.
                 */
                if (level > PT_PAGE_TABLE_LEVEL &&
-                   has_wrprotected_page(vcpu->kvm, gfn, level))
+                   has_wrprotected_page(vcpu, gfn, level))
                        goto done;
 
                spte |= PT_WRITABLE_MASK | SPTE_MMU_WRITEABLE;
@@ -2602,7 +2548,7 @@ static int set_spte(struct kvm_vcpu *vcpu, u64 *sptep,
        }
 
        if (pte_access & ACC_WRITE_MASK) {
-               mark_page_dirty(vcpu->kvm, gfn);
+               kvm_vcpu_mark_page_dirty(vcpu, gfn);
                spte |= shadow_dirty_mask;
        }
 
@@ -2692,15 +2638,17 @@ static int direct_pte_prefetch_many(struct kvm_vcpu *vcpu,
                                    u64 *start, u64 *end)
 {
        struct page *pages[PTE_PREFETCH_NUM];
+       struct kvm_memory_slot *slot;
        unsigned access = sp->role.access;
        int i, ret;
        gfn_t gfn;
 
        gfn = kvm_mmu_page_get_gfn(sp, start - sp->spt);
-       if (!gfn_to_memslot_dirty_bitmap(vcpu, gfn, access & ACC_WRITE_MASK))
+       slot = gfn_to_memslot_dirty_bitmap(vcpu, gfn, access & ACC_WRITE_MASK);
+       if (!slot)
                return -1;
 
-       ret = gfn_to_page_many_atomic(vcpu->kvm, gfn, pages, end - start);
+       ret = gfn_to_page_many_atomic(slot, gfn, pages, end - start);
        if (ret <= 0)
                return -1;
 
@@ -2818,7 +2766,7 @@ static int kvm_handle_bad_page(struct kvm_vcpu *vcpu, gfn_t gfn, pfn_t pfn)
                return 1;
 
        if (pfn == KVM_PFN_ERR_HWPOISON) {
-               kvm_send_hwpoison_signal(gfn_to_hva(vcpu->kvm, gfn), current);
+               kvm_send_hwpoison_signal(kvm_vcpu_gfn_to_hva(vcpu, gfn), current);
                return 0;
        }
 
@@ -2841,7 +2789,7 @@ static void transparent_hugepage_adjust(struct kvm_vcpu *vcpu,
        if (!is_error_noslot_pfn(pfn) && !kvm_is_reserved_pfn(pfn) &&
            level == PT_PAGE_TABLE_LEVEL &&
            PageTransCompound(pfn_to_page(pfn)) &&
-           !has_wrprotected_page(vcpu->kvm, gfn, PT_DIRECTORY_LEVEL)) {
+           !has_wrprotected_page(vcpu, gfn, PT_DIRECTORY_LEVEL)) {
                unsigned long mask;
                /*
                 * mmu_notifier_retry was successful and we hold the
@@ -2933,7 +2881,7 @@ fast_pf_fix_direct_spte(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp,
         * Compare with set_spte where instead shadow_dirty_mask is set.
         */
        if (cmpxchg64(sptep, spte, spte | PT_WRITABLE_MASK) == spte)
-               mark_page_dirty(vcpu->kvm, gfn);
+               kvm_vcpu_mark_page_dirty(vcpu, gfn);
 
        return true;
 }
@@ -3388,7 +3336,7 @@ int handle_mmio_page_fault_common(struct kvm_vcpu *vcpu, u64 addr, bool direct)
                gfn_t gfn = get_mmio_spte_gfn(spte);
                unsigned access = get_mmio_spte_access(spte);
 
-               if (!check_mmio_spte(vcpu->kvm, spte))
+               if (!check_mmio_spte(vcpu, spte))
                        return RET_MMIO_PF_INVALID;
 
                if (direct)
@@ -3460,7 +3408,7 @@ static int kvm_arch_setup_async_pf(struct kvm_vcpu *vcpu, gva_t gva, gfn_t gfn)
        arch.direct_map = vcpu->arch.mmu.direct_map;
        arch.cr3 = vcpu->arch.mmu.get_cr3(vcpu);
 
-       return kvm_setup_async_pf(vcpu, gva, gfn_to_hva(vcpu->kvm, gfn), &arch);
+       return kvm_setup_async_pf(vcpu, gva, kvm_vcpu_gfn_to_hva(vcpu, gfn), &arch);
 }
 
 static bool can_do_async_pf(struct kvm_vcpu *vcpu)
@@ -3475,10 +3423,12 @@ static bool can_do_async_pf(struct kvm_vcpu *vcpu)
 static bool try_async_pf(struct kvm_vcpu *vcpu, bool prefault, gfn_t gfn,
                         gva_t gva, pfn_t *pfn, bool write, bool *writable)
 {
+       struct kvm_memory_slot *slot;
        bool async;
 
-       *pfn = gfn_to_pfn_async(vcpu->kvm, gfn, &async, write, writable);
-
+       slot = kvm_vcpu_gfn_to_memslot(vcpu, gfn);
+       async = false;
+       *pfn = __gfn_to_pfn_memslot(slot, gfn, false, &async, write, writable);
        if (!async)
                return false; /* *pfn has correct page already */
 
@@ -3492,11 +3442,20 @@ static bool try_async_pf(struct kvm_vcpu *vcpu, bool prefault, gfn_t gfn,
                        return true;
        }
 
-       *pfn = gfn_to_pfn_prot(vcpu->kvm, gfn, write, writable);
-
+       *pfn = __gfn_to_pfn_memslot(slot, gfn, false, NULL, write, writable);
        return false;
 }
 
+static bool
+check_hugepage_cache_consistency(struct kvm_vcpu *vcpu, gfn_t gfn, int level)
+{
+       int page_num = KVM_PAGES_PER_HPAGE(level);
+
+       gfn &= ~(page_num - 1);
+
+       return kvm_mtrr_check_gfn_range_consistency(vcpu, gfn, page_num);
+}
+
 static int tdp_page_fault(struct kvm_vcpu *vcpu, gva_t gpa, u32 error_code,
                          bool prefault)
 {
@@ -3522,9 +3481,17 @@ static int tdp_page_fault(struct kvm_vcpu *vcpu, gva_t gpa, u32 error_code,
        if (r)
                return r;
 
-       force_pt_level = mapping_level_dirty_bitmap(vcpu, gfn);
+       if (mapping_level_dirty_bitmap(vcpu, gfn) ||
+           !check_hugepage_cache_consistency(vcpu, gfn, PT_DIRECTORY_LEVEL))
+               force_pt_level = 1;
+       else
+               force_pt_level = 0;
+
        if (likely(!force_pt_level)) {
                level = mapping_level(vcpu, gfn);
+               if (level > PT_DIRECTORY_LEVEL &&
+                   !check_hugepage_cache_consistency(vcpu, gfn, level))
+                       level = PT_DIRECTORY_LEVEL;
                gfn &= ~(KVM_PAGES_PER_HPAGE(level) - 1);
        } else
                level = PT_PAGE_TABLE_LEVEL;
@@ -3590,7 +3557,7 @@ static void inject_page_fault(struct kvm_vcpu *vcpu,
        vcpu->arch.mmu.inject_page_fault(vcpu, fault);
 }
 
-static bool sync_mmio_spte(struct kvm *kvm, u64 *sptep, gfn_t gfn,
+static bool sync_mmio_spte(struct kvm_vcpu *vcpu, u64 *sptep, gfn_t gfn,
                           unsigned access, int *nr_present)
 {
        if (unlikely(is_mmio_spte(*sptep))) {
@@ -3600,7 +3567,7 @@ static bool sync_mmio_spte(struct kvm *kvm, u64 *sptep, gfn_t gfn,
                }
 
                (*nr_present)++;
-               mark_mmio_spte(kvm, sptep, gfn, access);
+               mark_mmio_spte(vcpu, sptep, gfn, access);
                return true;
        }
 
@@ -3878,6 +3845,7 @@ static void init_kvm_tdp_mmu(struct kvm_vcpu *vcpu)
        struct kvm_mmu *context = &vcpu->arch.mmu;
 
        context->base_role.word = 0;
+       context->base_role.smm = is_smm(vcpu);
        context->page_fault = tdp_page_fault;
        context->sync_page = nonpaging_sync_page;
        context->invlpg = nonpaging_invlpg;
@@ -3939,6 +3907,7 @@ void kvm_init_shadow_mmu(struct kvm_vcpu *vcpu)
                = smep && !is_write_protection(vcpu);
        context->base_role.smap_andnot_wp
                = smap && !is_write_protection(vcpu);
+       context->base_role.smm = is_smm(vcpu);
 }
 EXPORT_SYMBOL_GPL(kvm_init_shadow_mmu);
 
@@ -4110,7 +4079,7 @@ static u64 mmu_pte_write_fetch_gpte(struct kvm_vcpu *vcpu, gpa_t *gpa,
                /* Handle a 32-bit guest writing two halves of a 64-bit gpte */
                *gpa &= ~(gpa_t)7;
                *bytes = 8;
-               r = kvm_read_guest(vcpu->kvm, *gpa, &gentry, 8);
+               r = kvm_vcpu_read_guest(vcpu, *gpa, &gentry, 8);
                if (r)
                        gentry = 0;
                new = (const u8 *)&gentry;
@@ -4215,13 +4184,14 @@ void kvm_mmu_pte_write(struct kvm_vcpu *vcpu, gpa_t gpa,
        u64 entry, gentry, *spte;
        int npte;
        bool remote_flush, local_flush, zap_page;
-       union kvm_mmu_page_role mask = (union kvm_mmu_page_role) {
-               .cr0_wp = 1,
-               .cr4_pae = 1,
-               .nxe = 1,
-               .smep_andnot_wp = 1,
-               .smap_andnot_wp = 1,
-       };
+       union kvm_mmu_page_role mask = { };
+
+       mask.cr0_wp = 1;
+       mask.cr4_pae = 1;
+       mask.nxe = 1;
+       mask.smep_andnot_wp = 1;
+       mask.smap_andnot_wp = 1;
+       mask.smm = 1;
 
        /*
         * If we don't have indirect shadow pages, it means no page is
@@ -4420,36 +4390,115 @@ void kvm_mmu_setup(struct kvm_vcpu *vcpu)
        init_kvm_mmu(vcpu);
 }
 
-void kvm_mmu_slot_remove_write_access(struct kvm *kvm,
-                                     struct kvm_memory_slot *memslot)
+/* The return value indicates if tlb flush on all vcpus is needed. */
+typedef bool (*slot_level_handler) (struct kvm *kvm, unsigned long *rmap);
+
+/* The caller should hold mmu-lock before calling this function. */
+static bool
+slot_handle_level_range(struct kvm *kvm, struct kvm_memory_slot *memslot,
+                       slot_level_handler fn, int start_level, int end_level,
+                       gfn_t start_gfn, gfn_t end_gfn, bool lock_flush_tlb)
 {
-       gfn_t last_gfn;
-       int i;
+       struct slot_rmap_walk_iterator iterator;
        bool flush = false;
 
-       last_gfn = memslot->base_gfn + memslot->npages - 1;
+       for_each_slot_rmap_range(memslot, start_level, end_level, start_gfn,
+                       end_gfn, &iterator) {
+               if (iterator.rmap)
+                       flush |= fn(kvm, iterator.rmap);
 
-       spin_lock(&kvm->mmu_lock);
+               if (need_resched() || spin_needbreak(&kvm->mmu_lock)) {
+                       if (flush && lock_flush_tlb) {
+                               kvm_flush_remote_tlbs(kvm);
+                               flush = false;
+                       }
+                       cond_resched_lock(&kvm->mmu_lock);
+               }
+       }
+
+       if (flush && lock_flush_tlb) {
+               kvm_flush_remote_tlbs(kvm);
+               flush = false;
+       }
 
-       for (i = PT_PAGE_TABLE_LEVEL;
-            i < PT_PAGE_TABLE_LEVEL + KVM_NR_PAGE_SIZES; ++i) {
-               unsigned long *rmapp;
-               unsigned long last_index, index;
+       return flush;
+}
+
+static bool
+slot_handle_level(struct kvm *kvm, struct kvm_memory_slot *memslot,
+                 slot_level_handler fn, int start_level, int end_level,
+                 bool lock_flush_tlb)
+{
+       return slot_handle_level_range(kvm, memslot, fn, start_level,
+                       end_level, memslot->base_gfn,
+                       memslot->base_gfn + memslot->npages - 1,
+                       lock_flush_tlb);
+}
+
+static bool
+slot_handle_all_level(struct kvm *kvm, struct kvm_memory_slot *memslot,
+                     slot_level_handler fn, bool lock_flush_tlb)
+{
+       return slot_handle_level(kvm, memslot, fn, PT_PAGE_TABLE_LEVEL,
+                                PT_MAX_HUGEPAGE_LEVEL, lock_flush_tlb);
+}
+
+static bool
+slot_handle_large_level(struct kvm *kvm, struct kvm_memory_slot *memslot,
+                       slot_level_handler fn, bool lock_flush_tlb)
+{
+       return slot_handle_level(kvm, memslot, fn, PT_PAGE_TABLE_LEVEL + 1,
+                                PT_MAX_HUGEPAGE_LEVEL, lock_flush_tlb);
+}
+
+static bool
+slot_handle_leaf(struct kvm *kvm, struct kvm_memory_slot *memslot,
+                slot_level_handler fn, bool lock_flush_tlb)
+{
+       return slot_handle_level(kvm, memslot, fn, PT_PAGE_TABLE_LEVEL,
+                                PT_PAGE_TABLE_LEVEL, lock_flush_tlb);
+}
 
-               rmapp = memslot->arch.rmap[i - PT_PAGE_TABLE_LEVEL];
-               last_index = gfn_to_index(last_gfn, memslot->base_gfn, i);
+void kvm_zap_gfn_range(struct kvm *kvm, gfn_t gfn_start, gfn_t gfn_end)
+{
+       struct kvm_memslots *slots;
+       struct kvm_memory_slot *memslot;
+       int i;
 
-               for (index = 0; index <= last_index; ++index, ++rmapp) {
-                       if (*rmapp)
-                               flush |= __rmap_write_protect(kvm, rmapp,
-                                               false);
+       spin_lock(&kvm->mmu_lock);
+       for (i = 0; i < KVM_ADDRESS_SPACE_NUM; i++) {
+               slots = __kvm_memslots(kvm, i);
+               kvm_for_each_memslot(memslot, slots) {
+                       gfn_t start, end;
+
+                       start = max(gfn_start, memslot->base_gfn);
+                       end = min(gfn_end, memslot->base_gfn + memslot->npages);
+                       if (start >= end)
+                               continue;
 
-                       if (need_resched() || spin_needbreak(&kvm->mmu_lock))
-                               cond_resched_lock(&kvm->mmu_lock);
+                       slot_handle_level_range(kvm, memslot, kvm_zap_rmapp,
+                                               PT_PAGE_TABLE_LEVEL, PT_MAX_HUGEPAGE_LEVEL,
+                                               start, end - 1, true);
                }
        }
 
        spin_unlock(&kvm->mmu_lock);
+}
+
+static bool slot_rmap_write_protect(struct kvm *kvm, unsigned long *rmapp)
+{
+       return __rmap_write_protect(kvm, rmapp, false);
+}
+
+void kvm_mmu_slot_remove_write_access(struct kvm *kvm,
+                                     struct kvm_memory_slot *memslot)
+{
+       bool flush;
+
+       spin_lock(&kvm->mmu_lock);
+       flush = slot_handle_all_level(kvm, memslot, slot_rmap_write_protect,
+                                     false);
+       spin_unlock(&kvm->mmu_lock);
 
        /*
         * kvm_mmu_slot_remove_write_access() and kvm_vm_ioctl_get_dirty_log()
@@ -4482,9 +4531,8 @@ static bool kvm_mmu_zap_collapsible_spte(struct kvm *kvm,
        pfn_t pfn;
        struct kvm_mmu_page *sp;
 
-       for (sptep = rmap_get_first(*rmapp, &iter); sptep;) {
-               BUG_ON(!(*sptep & PT_PRESENT_MASK));
-
+restart:
+       for_each_rmap_spte(rmapp, &iter, sptep) {
                sp = page_header(__pa(sptep));
                pfn = spte_to_pfn(*sptep);
 
@@ -4499,71 +4547,31 @@ static bool kvm_mmu_zap_collapsible_spte(struct kvm *kvm,
                        !kvm_is_reserved_pfn(pfn) &&
                        PageTransCompound(pfn_to_page(pfn))) {
                        drop_spte(kvm, sptep);
-                       sptep = rmap_get_first(*rmapp, &iter);
                        need_tlb_flush = 1;
-               } else
-                       sptep = rmap_get_next(&iter);
+                       goto restart;
+               }
        }
 
        return need_tlb_flush;
 }
 
 void kvm_mmu_zap_collapsible_sptes(struct kvm *kvm,
-                       struct kvm_memory_slot *memslot)
+                                  const struct kvm_memory_slot *memslot)
 {
-       bool flush = false;
-       unsigned long *rmapp;
-       unsigned long last_index, index;
-
+       /* FIXME: const-ify all uses of struct kvm_memory_slot.  */
        spin_lock(&kvm->mmu_lock);
-
-       rmapp = memslot->arch.rmap[0];
-       last_index = gfn_to_index(memslot->base_gfn + memslot->npages - 1,
-                               memslot->base_gfn, PT_PAGE_TABLE_LEVEL);
-
-       for (index = 0; index <= last_index; ++index, ++rmapp) {
-               if (*rmapp)
-                       flush |= kvm_mmu_zap_collapsible_spte(kvm, rmapp);
-
-               if (need_resched() || spin_needbreak(&kvm->mmu_lock)) {
-                       if (flush) {
-                               kvm_flush_remote_tlbs(kvm);
-                               flush = false;
-                       }
-                       cond_resched_lock(&kvm->mmu_lock);
-               }
-       }
-
-       if (flush)
-               kvm_flush_remote_tlbs(kvm);
-
+       slot_handle_leaf(kvm, (struct kvm_memory_slot *)memslot,
+                        kvm_mmu_zap_collapsible_spte, true);
        spin_unlock(&kvm->mmu_lock);
 }
 
 void kvm_mmu_slot_leaf_clear_dirty(struct kvm *kvm,
                                   struct kvm_memory_slot *memslot)
 {
-       gfn_t last_gfn;
-       unsigned long *rmapp;
-       unsigned long last_index, index;
-       bool flush = false;
-
-       last_gfn = memslot->base_gfn + memslot->npages - 1;
+       bool flush;
 
        spin_lock(&kvm->mmu_lock);
-
-       rmapp = memslot->arch.rmap[PT_PAGE_TABLE_LEVEL - 1];
-       last_index = gfn_to_index(last_gfn, memslot->base_gfn,
-                       PT_PAGE_TABLE_LEVEL);
-
-       for (index = 0; index <= last_index; ++index, ++rmapp) {
-               if (*rmapp)
-                       flush |= __rmap_clear_dirty(kvm, rmapp);
-
-               if (need_resched() || spin_needbreak(&kvm->mmu_lock))
-                       cond_resched_lock(&kvm->mmu_lock);
-       }
-
+       flush = slot_handle_leaf(kvm, memslot, __rmap_clear_dirty, false);
        spin_unlock(&kvm->mmu_lock);
 
        lockdep_assert_held(&kvm->slots_lock);
@@ -4582,31 +4590,11 @@ EXPORT_SYMBOL_GPL(kvm_mmu_slot_leaf_clear_dirty);
 void kvm_mmu_slot_largepage_remove_write_access(struct kvm *kvm,
                                        struct kvm_memory_slot *memslot)
 {
-       gfn_t last_gfn;
-       int i;
-       bool flush = false;
-
-       last_gfn = memslot->base_gfn + memslot->npages - 1;
+       bool flush;
 
        spin_lock(&kvm->mmu_lock);
-
-       for (i = PT_PAGE_TABLE_LEVEL + 1; /* skip rmap for 4K page */
-            i < PT_PAGE_TABLE_LEVEL + KVM_NR_PAGE_SIZES; ++i) {
-               unsigned long *rmapp;
-               unsigned long last_index, index;
-
-               rmapp = memslot->arch.rmap[i - PT_PAGE_TABLE_LEVEL];
-               last_index = gfn_to_index(last_gfn, memslot->base_gfn, i);
-
-               for (index = 0; index <= last_index; ++index, ++rmapp) {
-                       if (*rmapp)
-                               flush |= __rmap_write_protect(kvm, rmapp,
-                                               false);
-
-                       if (need_resched() || spin_needbreak(&kvm->mmu_lock))
-                               cond_resched_lock(&kvm->mmu_lock);
-               }
-       }
+       flush = slot_handle_large_level(kvm, memslot, slot_rmap_write_protect,
+                                       false);
        spin_unlock(&kvm->mmu_lock);
 
        /* see kvm_mmu_slot_remove_write_access */
@@ -4620,31 +4608,10 @@ EXPORT_SYMBOL_GPL(kvm_mmu_slot_largepage_remove_write_access);
 void kvm_mmu_slot_set_dirty(struct kvm *kvm,
                            struct kvm_memory_slot *memslot)
 {
-       gfn_t last_gfn;
-       int i;
-       bool flush = false;
-
-       last_gfn = memslot->base_gfn + memslot->npages - 1;
+       bool flush;
 
        spin_lock(&kvm->mmu_lock);
-
-       for (i = PT_PAGE_TABLE_LEVEL;
-            i < PT_PAGE_TABLE_LEVEL + KVM_NR_PAGE_SIZES; ++i) {
-               unsigned long *rmapp;
-               unsigned long last_index, index;
-
-               rmapp = memslot->arch.rmap[i - PT_PAGE_TABLE_LEVEL];
-               last_index = gfn_to_index(last_gfn, memslot->base_gfn, i);
-
-               for (index = 0; index <= last_index; ++index, ++rmapp) {
-                       if (*rmapp)
-                               flush |= __rmap_set_dirty(kvm, rmapp);
-
-                       if (need_resched() || spin_needbreak(&kvm->mmu_lock))
-                               cond_resched_lock(&kvm->mmu_lock);
-               }
-       }
-
+       flush = slot_handle_all_level(kvm, memslot, __rmap_set_dirty, false);
        spin_unlock(&kvm->mmu_lock);
 
        lockdep_assert_held(&kvm->slots_lock);
@@ -4741,13 +4708,13 @@ static bool kvm_has_zapped_obsolete_pages(struct kvm *kvm)
        return unlikely(!list_empty_careful(&kvm->arch.zapped_obsolete_pages));
 }
 
-void kvm_mmu_invalidate_mmio_sptes(struct kvm *kvm)
+void kvm_mmu_invalidate_mmio_sptes(struct kvm *kvm, struct kvm_memslots *slots)
 {
        /*
         * The very rare case: if the generation-number is round,
         * zap all shadow pages.
         */
-       if (unlikely(kvm_current_mmio_generation(kvm) == 0)) {
+       if (unlikely((slots->generation & MMIO_GEN_MASK) == 0)) {
                printk_ratelimited(KERN_DEBUG "kvm: zapping shadow pages for mmio generation wraparound\n");
                kvm_mmu_invalidate_zap_all_pages(kvm);
        }
@@ -4869,15 +4836,18 @@ unsigned int kvm_mmu_calculate_mmu_pages(struct kvm *kvm)
        unsigned int  nr_pages = 0;
        struct kvm_memslots *slots;
        struct kvm_memory_slot *memslot;
+       int i;
 
-       slots = kvm_memslots(kvm);
+       for (i = 0; i < KVM_ADDRESS_SPACE_NUM; i++) {
+               slots = __kvm_memslots(kvm, i);
 
-       kvm_for_each_memslot(memslot, slots)
-               nr_pages += memslot->npages;
+               kvm_for_each_memslot(memslot, slots)
+                       nr_pages += memslot->npages;
+       }
 
        nr_mmu_pages = nr_pages * KVM_PERMILLE_MMU_PAGES / 1000;
        nr_mmu_pages = max(nr_mmu_pages,
-                       (unsigned int) KVM_MIN_ALLOC_MMU_PAGES);
+                          (unsigned int) KVM_MIN_ALLOC_MMU_PAGES);
 
        return nr_mmu_pages;
 }
index 0ada65ecddcf27ca619269d92435012c3f19790a..398d21c0f6dd05273fea48598bef016e1c26b51c 100644 (file)
@@ -43,6 +43,7 @@
 #define PT_PDPE_LEVEL 3
 #define PT_DIRECTORY_LEVEL 2
 #define PT_PAGE_TABLE_LEVEL 1
+#define PT_MAX_HUGEPAGE_LEVEL (PT_PAGE_TABLE_LEVEL + KVM_NR_PAGE_SIZES - 1)
 
 static inline u64 rsvd_bits(int s, int e)
 {
@@ -170,4 +171,5 @@ static inline bool permission_fault(struct kvm_vcpu *vcpu, struct kvm_mmu *mmu,
 }
 
 void kvm_mmu_invalidate_zap_all_pages(struct kvm *kvm);
+void kvm_zap_gfn_range(struct kvm *kvm, gfn_t gfn_start, gfn_t gfn_end);
 #endif
index 9ade5cfb5a4c26cf9d63f6f59519d50e94b5e2ae..a4f62e6f2db2fe74cb78337ebeab9307b873c738 100644 (file)
@@ -114,7 +114,7 @@ static void audit_mappings(struct kvm_vcpu *vcpu, u64 *sptep, int level)
                return;
 
        gfn = kvm_mmu_page_get_gfn(sp, sptep - sp->spt);
-       pfn = gfn_to_pfn_atomic(vcpu->kvm, gfn);
+       pfn = kvm_vcpu_gfn_to_pfn_atomic(vcpu, gfn);
 
        if (is_error_pfn(pfn))
                return;
@@ -131,12 +131,16 @@ static void inspect_spte_has_rmap(struct kvm *kvm, u64 *sptep)
        static DEFINE_RATELIMIT_STATE(ratelimit_state, 5 * HZ, 10);
        unsigned long *rmapp;
        struct kvm_mmu_page *rev_sp;
+       struct kvm_memslots *slots;
+       struct kvm_memory_slot *slot;
        gfn_t gfn;
 
        rev_sp = page_header(__pa(sptep));
        gfn = kvm_mmu_page_get_gfn(rev_sp, sptep - rev_sp->spt);
 
-       if (!gfn_to_memslot(kvm, gfn)) {
+       slots = kvm_memslots_for_spte_role(kvm, rev_sp->role);
+       slot = __gfn_to_memslot(slots, gfn);
+       if (!slot) {
                if (!__ratelimit(&ratelimit_state))
                        return;
                audit_printk(kvm, "no memslot for gfn %llx\n", gfn);
@@ -146,7 +150,7 @@ static void inspect_spte_has_rmap(struct kvm *kvm, u64 *sptep)
                return;
        }
 
-       rmapp = gfn_to_rmap(kvm, gfn, rev_sp->role.level);
+       rmapp = __gfn_to_rmap(gfn, rev_sp->role.level, slot);
        if (!*rmapp) {
                if (!__ratelimit(&ratelimit_state))
                        return;
@@ -191,19 +195,21 @@ static void audit_write_protection(struct kvm *kvm, struct kvm_mmu_page *sp)
        unsigned long *rmapp;
        u64 *sptep;
        struct rmap_iterator iter;
+       struct kvm_memslots *slots;
+       struct kvm_memory_slot *slot;
 
        if (sp->role.direct || sp->unsync || sp->role.invalid)
                return;
 
-       rmapp = gfn_to_rmap(kvm, sp->gfn, PT_PAGE_TABLE_LEVEL);
+       slots = kvm_memslots_for_spte_role(kvm, sp->role);
+       slot = __gfn_to_memslot(slots, sp->gfn);
+       rmapp = __gfn_to_rmap(sp->gfn, PT_PAGE_TABLE_LEVEL, slot);
 
-       for (sptep = rmap_get_first(*rmapp, &iter); sptep;
-            sptep = rmap_get_next(&iter)) {
+       for_each_rmap_spte(rmapp, &iter, sptep)
                if (is_writable_pte(*sptep))
                        audit_printk(kvm, "shadow page has writable "
                                     "mappings: gfn %llx role %x\n",
                                     sp->gfn, sp->role.word);
-       }
 }
 
 static void audit_sp(struct kvm *kvm, struct kvm_mmu_page *sp)
diff --git a/arch/x86/kvm/mtrr.c b/arch/x86/kvm/mtrr.c
new file mode 100644 (file)
index 0000000..de1d2d8
--- /dev/null
@@ -0,0 +1,699 @@
+/*
+ * vMTRR implementation
+ *
+ * Copyright (C) 2006 Qumranet, Inc.
+ * Copyright 2010 Red Hat, Inc. and/or its affiliates.
+ * Copyright(C) 2015 Intel Corporation.
+ *
+ * Authors:
+ *   Yaniv Kamay  <yaniv@qumranet.com>
+ *   Avi Kivity   <avi@qumranet.com>
+ *   Marcelo Tosatti <mtosatti@redhat.com>
+ *   Paolo Bonzini <pbonzini@redhat.com>
+ *   Xiao Guangrong <guangrong.xiao@linux.intel.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2.  See
+ * the COPYING file in the top-level directory.
+ */
+
+#include <linux/kvm_host.h>
+#include <asm/mtrr.h>
+
+#include "cpuid.h"
+#include "mmu.h"
+
+#define IA32_MTRR_DEF_TYPE_E           (1ULL << 11)
+#define IA32_MTRR_DEF_TYPE_FE          (1ULL << 10)
+#define IA32_MTRR_DEF_TYPE_TYPE_MASK   (0xff)
+
+static bool msr_mtrr_valid(unsigned msr)
+{
+       switch (msr) {
+       case 0x200 ... 0x200 + 2 * KVM_NR_VAR_MTRR - 1:
+       case MSR_MTRRfix64K_00000:
+       case MSR_MTRRfix16K_80000:
+       case MSR_MTRRfix16K_A0000:
+       case MSR_MTRRfix4K_C0000:
+       case MSR_MTRRfix4K_C8000:
+       case MSR_MTRRfix4K_D0000:
+       case MSR_MTRRfix4K_D8000:
+       case MSR_MTRRfix4K_E0000:
+       case MSR_MTRRfix4K_E8000:
+       case MSR_MTRRfix4K_F0000:
+       case MSR_MTRRfix4K_F8000:
+       case MSR_MTRRdefType:
+       case MSR_IA32_CR_PAT:
+               return true;
+       case 0x2f8:
+               return true;
+       }
+       return false;
+}
+
+static bool valid_pat_type(unsigned t)
+{
+       return t < 8 && (1 << t) & 0xf3; /* 0, 1, 4, 5, 6, 7 */
+}
+
+static bool valid_mtrr_type(unsigned t)
+{
+       return t < 8 && (1 << t) & 0x73; /* 0, 1, 4, 5, 6 */
+}
+
+bool kvm_mtrr_valid(struct kvm_vcpu *vcpu, u32 msr, u64 data)
+{
+       int i;
+       u64 mask;
+
+       if (!msr_mtrr_valid(msr))
+               return false;
+
+       if (msr == MSR_IA32_CR_PAT) {
+               for (i = 0; i < 8; i++)
+                       if (!valid_pat_type((data >> (i * 8)) & 0xff))
+                               return false;
+               return true;
+       } else if (msr == MSR_MTRRdefType) {
+               if (data & ~0xcff)
+                       return false;
+               return valid_mtrr_type(data & 0xff);
+       } else if (msr >= MSR_MTRRfix64K_00000 && msr <= MSR_MTRRfix4K_F8000) {
+               for (i = 0; i < 8 ; i++)
+                       if (!valid_mtrr_type((data >> (i * 8)) & 0xff))
+                               return false;
+               return true;
+       }
+
+       /* variable MTRRs */
+       WARN_ON(!(msr >= 0x200 && msr < 0x200 + 2 * KVM_NR_VAR_MTRR));
+
+       mask = (~0ULL) << cpuid_maxphyaddr(vcpu);
+       if ((msr & 1) == 0) {
+               /* MTRR base */
+               if (!valid_mtrr_type(data & 0xff))
+                       return false;
+               mask |= 0xf00;
+       } else
+               /* MTRR mask */
+               mask |= 0x7ff;
+       if (data & mask) {
+               kvm_inject_gp(vcpu, 0);
+               return false;
+       }
+
+       return true;
+}
+EXPORT_SYMBOL_GPL(kvm_mtrr_valid);
+
+static bool mtrr_is_enabled(struct kvm_mtrr *mtrr_state)
+{
+       return !!(mtrr_state->deftype & IA32_MTRR_DEF_TYPE_E);
+}
+
+static bool fixed_mtrr_is_enabled(struct kvm_mtrr *mtrr_state)
+{
+       return !!(mtrr_state->deftype & IA32_MTRR_DEF_TYPE_FE);
+}
+
+static u8 mtrr_default_type(struct kvm_mtrr *mtrr_state)
+{
+       return mtrr_state->deftype & IA32_MTRR_DEF_TYPE_TYPE_MASK;
+}
+
+/*
+* Three terms are used in the following code:
+* - segment, it indicates the address segments covered by fixed MTRRs.
+* - unit, it corresponds to the MSR entry in the segment.
+* - range, a range is covered in one memory cache type.
+*/
+struct fixed_mtrr_segment {
+       u64 start;
+       u64 end;
+
+       int range_shift;
+
+       /* the start position in kvm_mtrr.fixed_ranges[]. */
+       int range_start;
+};
+
+static struct fixed_mtrr_segment fixed_seg_table[] = {
+       /* MSR_MTRRfix64K_00000, 1 unit. 64K fixed mtrr. */
+       {
+               .start = 0x0,
+               .end = 0x80000,
+               .range_shift = 16, /* 64K */
+               .range_start = 0,
+       },
+
+       /*
+        * MSR_MTRRfix16K_80000 ... MSR_MTRRfix16K_A0000, 2 units,
+        * 16K fixed mtrr.
+        */
+       {
+               .start = 0x80000,
+               .end = 0xc0000,
+               .range_shift = 14, /* 16K */
+               .range_start = 8,
+       },
+
+       /*
+        * MSR_MTRRfix4K_C0000 ... MSR_MTRRfix4K_F8000, 8 units,
+        * 4K fixed mtrr.
+        */
+       {
+               .start = 0xc0000,
+               .end = 0x100000,
+               .range_shift = 12, /* 12K */
+               .range_start = 24,
+       }
+};
+
+/*
+ * The size of unit is covered in one MSR, one MSR entry contains
+ * 8 ranges so that unit size is always 8 * 2^range_shift.
+ */
+static u64 fixed_mtrr_seg_unit_size(int seg)
+{
+       return 8 << fixed_seg_table[seg].range_shift;
+}
+
+static bool fixed_msr_to_seg_unit(u32 msr, int *seg, int *unit)
+{
+       switch (msr) {
+       case MSR_MTRRfix64K_00000:
+               *seg = 0;
+               *unit = 0;
+               break;
+       case MSR_MTRRfix16K_80000 ... MSR_MTRRfix16K_A0000:
+               *seg = 1;
+               *unit = msr - MSR_MTRRfix16K_80000;
+               break;
+       case MSR_MTRRfix4K_C0000 ... MSR_MTRRfix4K_F8000:
+               *seg = 2;
+               *unit = msr - MSR_MTRRfix4K_C0000;
+               break;
+       default:
+               return false;
+       }
+
+       return true;
+}
+
+static void fixed_mtrr_seg_unit_range(int seg, int unit, u64 *start, u64 *end)
+{
+       struct fixed_mtrr_segment *mtrr_seg = &fixed_seg_table[seg];
+       u64 unit_size = fixed_mtrr_seg_unit_size(seg);
+
+       *start = mtrr_seg->start + unit * unit_size;
+       *end = *start + unit_size;
+       WARN_ON(*end > mtrr_seg->end);
+}
+
+static int fixed_mtrr_seg_unit_range_index(int seg, int unit)
+{
+       struct fixed_mtrr_segment *mtrr_seg = &fixed_seg_table[seg];
+
+       WARN_ON(mtrr_seg->start + unit * fixed_mtrr_seg_unit_size(seg)
+               > mtrr_seg->end);
+
+       /* each unit has 8 ranges. */
+       return mtrr_seg->range_start + 8 * unit;
+}
+
+static int fixed_mtrr_seg_end_range_index(int seg)
+{
+       struct fixed_mtrr_segment *mtrr_seg = &fixed_seg_table[seg];
+       int n;
+
+       n = (mtrr_seg->end - mtrr_seg->start) >> mtrr_seg->range_shift;
+       return mtrr_seg->range_start + n - 1;
+}
+
+static bool fixed_msr_to_range(u32 msr, u64 *start, u64 *end)
+{
+       int seg, unit;
+
+       if (!fixed_msr_to_seg_unit(msr, &seg, &unit))
+               return false;
+
+       fixed_mtrr_seg_unit_range(seg, unit, start, end);
+       return true;
+}
+
+static int fixed_msr_to_range_index(u32 msr)
+{
+       int seg, unit;
+
+       if (!fixed_msr_to_seg_unit(msr, &seg, &unit))
+               return -1;
+
+       return fixed_mtrr_seg_unit_range_index(seg, unit);
+}
+
+static int fixed_mtrr_addr_to_seg(u64 addr)
+{
+       struct fixed_mtrr_segment *mtrr_seg;
+       int seg, seg_num = ARRAY_SIZE(fixed_seg_table);
+
+       for (seg = 0; seg < seg_num; seg++) {
+               mtrr_seg = &fixed_seg_table[seg];
+               if (mtrr_seg->start >= addr && addr < mtrr_seg->end)
+                       return seg;
+       }
+
+       return -1;
+}
+
+static int fixed_mtrr_addr_seg_to_range_index(u64 addr, int seg)
+{
+       struct fixed_mtrr_segment *mtrr_seg;
+       int index;
+
+       mtrr_seg = &fixed_seg_table[seg];
+       index = mtrr_seg->range_start;
+       index += (addr - mtrr_seg->start) >> mtrr_seg->range_shift;
+       return index;
+}
+
+static u64 fixed_mtrr_range_end_addr(int seg, int index)
+{
+       struct fixed_mtrr_segment *mtrr_seg = &fixed_seg_table[seg];
+       int pos = index - mtrr_seg->range_start;
+
+       return mtrr_seg->start + ((pos + 1) << mtrr_seg->range_shift);
+}
+
+static void var_mtrr_range(struct kvm_mtrr_range *range, u64 *start, u64 *end)
+{
+       u64 mask;
+
+       *start = range->base & PAGE_MASK;
+
+       mask = range->mask & PAGE_MASK;
+       mask |= ~0ULL << boot_cpu_data.x86_phys_bits;
+
+       /* This cannot overflow because writing to the reserved bits of
+        * variable MTRRs causes a #GP.
+        */
+       *end = (*start | ~mask) + 1;
+}
+
+static void update_mtrr(struct kvm_vcpu *vcpu, u32 msr)
+{
+       struct kvm_mtrr *mtrr_state = &vcpu->arch.mtrr_state;
+       gfn_t start, end;
+       int index;
+
+       if (msr == MSR_IA32_CR_PAT || !tdp_enabled ||
+             !kvm_arch_has_noncoherent_dma(vcpu->kvm))
+               return;
+
+       if (!mtrr_is_enabled(mtrr_state) && msr != MSR_MTRRdefType)
+               return;
+
+       /* fixed MTRRs. */
+       if (fixed_msr_to_range(msr, &start, &end)) {
+               if (!fixed_mtrr_is_enabled(mtrr_state))
+                       return;
+       } else if (msr == MSR_MTRRdefType) {
+               start = 0x0;
+               end = ~0ULL;
+       } else {
+               /* variable range MTRRs. */
+               index = (msr - 0x200) / 2;
+               var_mtrr_range(&mtrr_state->var_ranges[index], &start, &end);
+       }
+
+       kvm_zap_gfn_range(vcpu->kvm, gpa_to_gfn(start), gpa_to_gfn(end));
+}
+
+static bool var_mtrr_range_is_valid(struct kvm_mtrr_range *range)
+{
+       return (range->mask & (1 << 11)) != 0;
+}
+
+static void set_var_mtrr_msr(struct kvm_vcpu *vcpu, u32 msr, u64 data)
+{
+       struct kvm_mtrr *mtrr_state = &vcpu->arch.mtrr_state;
+       struct kvm_mtrr_range *tmp, *cur;
+       int index, is_mtrr_mask;
+
+       index = (msr - 0x200) / 2;
+       is_mtrr_mask = msr - 0x200 - 2 * index;
+       cur = &mtrr_state->var_ranges[index];
+
+       /* remove the entry if it's in the list. */
+       if (var_mtrr_range_is_valid(cur))
+               list_del(&mtrr_state->var_ranges[index].node);
+
+       if (!is_mtrr_mask)
+               cur->base = data;
+       else
+               cur->mask = data;
+
+       /* add it to the list if it's enabled. */
+       if (var_mtrr_range_is_valid(cur)) {
+               list_for_each_entry(tmp, &mtrr_state->head, node)
+                       if (cur->base >= tmp->base)
+                               break;
+               list_add_tail(&cur->node, &tmp->node);
+       }
+}
+
+int kvm_mtrr_set_msr(struct kvm_vcpu *vcpu, u32 msr, u64 data)
+{
+       int index;
+
+       if (!kvm_mtrr_valid(vcpu, msr, data))
+               return 1;
+
+       index = fixed_msr_to_range_index(msr);
+       if (index >= 0)
+               *(u64 *)&vcpu->arch.mtrr_state.fixed_ranges[index] = data;
+       else if (msr == MSR_MTRRdefType)
+               vcpu->arch.mtrr_state.deftype = data;
+       else if (msr == MSR_IA32_CR_PAT)
+               vcpu->arch.pat = data;
+       else
+               set_var_mtrr_msr(vcpu, msr, data);
+
+       update_mtrr(vcpu, msr);
+       return 0;
+}
+
+int kvm_mtrr_get_msr(struct kvm_vcpu *vcpu, u32 msr, u64 *pdata)
+{
+       int index;
+
+       /* MSR_MTRRcap is a readonly MSR. */
+       if (msr == MSR_MTRRcap) {
+               /*
+                * SMRR = 0
+                * WC = 1
+                * FIX = 1
+                * VCNT = KVM_NR_VAR_MTRR
+                */
+               *pdata = 0x500 | KVM_NR_VAR_MTRR;
+               return 0;
+       }
+
+       if (!msr_mtrr_valid(msr))
+               return 1;
+
+       index = fixed_msr_to_range_index(msr);
+       if (index >= 0)
+               *pdata = *(u64 *)&vcpu->arch.mtrr_state.fixed_ranges[index];
+       else if (msr == MSR_MTRRdefType)
+               *pdata = vcpu->arch.mtrr_state.deftype;
+       else if (msr == MSR_IA32_CR_PAT)
+               *pdata = vcpu->arch.pat;
+       else {  /* Variable MTRRs */
+               int is_mtrr_mask;
+
+               index = (msr - 0x200) / 2;
+               is_mtrr_mask = msr - 0x200 - 2 * index;
+               if (!is_mtrr_mask)
+                       *pdata = vcpu->arch.mtrr_state.var_ranges[index].base;
+               else
+                       *pdata = vcpu->arch.mtrr_state.var_ranges[index].mask;
+       }
+
+       return 0;
+}
+
+void kvm_vcpu_mtrr_init(struct kvm_vcpu *vcpu)
+{
+       INIT_LIST_HEAD(&vcpu->arch.mtrr_state.head);
+}
+
+struct mtrr_iter {
+       /* input fields. */
+       struct kvm_mtrr *mtrr_state;
+       u64 start;
+       u64 end;
+
+       /* output fields. */
+       int mem_type;
+       /* [start, end) is not fully covered in MTRRs? */
+       bool partial_map;
+
+       /* private fields. */
+       union {
+               /* used for fixed MTRRs. */
+               struct {
+                       int index;
+                       int seg;
+               };
+
+               /* used for var MTRRs. */
+               struct {
+                       struct kvm_mtrr_range *range;
+                       /* max address has been covered in var MTRRs. */
+                       u64 start_max;
+               };
+       };
+
+       bool fixed;
+};
+
+static bool mtrr_lookup_fixed_start(struct mtrr_iter *iter)
+{
+       int seg, index;
+
+       if (!fixed_mtrr_is_enabled(iter->mtrr_state))
+               return false;
+
+       seg = fixed_mtrr_addr_to_seg(iter->start);
+       if (seg < 0)
+               return false;
+
+       iter->fixed = true;
+       index = fixed_mtrr_addr_seg_to_range_index(iter->start, seg);
+       iter->index = index;
+       iter->seg = seg;
+       return true;
+}
+
+static bool match_var_range(struct mtrr_iter *iter,
+                           struct kvm_mtrr_range *range)
+{
+       u64 start, end;
+
+       var_mtrr_range(range, &start, &end);
+       if (!(start >= iter->end || end <= iter->start)) {
+               iter->range = range;
+
+               /*
+                * the function is called when we do kvm_mtrr.head walking.
+                * Range has the minimum base address which interleaves
+                * [looker->start_max, looker->end).
+                */
+               iter->partial_map |= iter->start_max < start;
+
+               /* update the max address has been covered. */
+               iter->start_max = max(iter->start_max, end);
+               return true;
+       }
+
+       return false;
+}
+
+static void __mtrr_lookup_var_next(struct mtrr_iter *iter)
+{
+       struct kvm_mtrr *mtrr_state = iter->mtrr_state;
+
+       list_for_each_entry_continue(iter->range, &mtrr_state->head, node)
+               if (match_var_range(iter, iter->range))
+                       return;
+
+       iter->range = NULL;
+       iter->partial_map |= iter->start_max < iter->end;
+}
+
+static void mtrr_lookup_var_start(struct mtrr_iter *iter)
+{
+       struct kvm_mtrr *mtrr_state = iter->mtrr_state;
+
+       iter->fixed = false;
+       iter->start_max = iter->start;
+       iter->range = list_prepare_entry(iter->range, &mtrr_state->head, node);
+
+       __mtrr_lookup_var_next(iter);
+}
+
+static void mtrr_lookup_fixed_next(struct mtrr_iter *iter)
+{
+       /* terminate the lookup. */
+       if (fixed_mtrr_range_end_addr(iter->seg, iter->index) >= iter->end) {
+               iter->fixed = false;
+               iter->range = NULL;
+               return;
+       }
+
+       iter->index++;
+
+       /* have looked up for all fixed MTRRs. */
+       if (iter->index >= ARRAY_SIZE(iter->mtrr_state->fixed_ranges))
+               return mtrr_lookup_var_start(iter);
+
+       /* switch to next segment. */
+       if (iter->index > fixed_mtrr_seg_end_range_index(iter->seg))
+               iter->seg++;
+}
+
+static void mtrr_lookup_var_next(struct mtrr_iter *iter)
+{
+       __mtrr_lookup_var_next(iter);
+}
+
+static void mtrr_lookup_start(struct mtrr_iter *iter)
+{
+       if (!mtrr_is_enabled(iter->mtrr_state)) {
+               iter->partial_map = true;
+               return;
+       }
+
+       if (!mtrr_lookup_fixed_start(iter))
+               mtrr_lookup_var_start(iter);
+}
+
+static void mtrr_lookup_init(struct mtrr_iter *iter,
+                            struct kvm_mtrr *mtrr_state, u64 start, u64 end)
+{
+       iter->mtrr_state = mtrr_state;
+       iter->start = start;
+       iter->end = end;
+       iter->partial_map = false;
+       iter->fixed = false;
+       iter->range = NULL;
+
+       mtrr_lookup_start(iter);
+}
+
+static bool mtrr_lookup_okay(struct mtrr_iter *iter)
+{
+       if (iter->fixed) {
+               iter->mem_type = iter->mtrr_state->fixed_ranges[iter->index];
+               return true;
+       }
+
+       if (iter->range) {
+               iter->mem_type = iter->range->base & 0xff;
+               return true;
+       }
+
+       return false;
+}
+
+static void mtrr_lookup_next(struct mtrr_iter *iter)
+{
+       if (iter->fixed)
+               mtrr_lookup_fixed_next(iter);
+       else
+               mtrr_lookup_var_next(iter);
+}
+
+#define mtrr_for_each_mem_type(_iter_, _mtrr_, _gpa_start_, _gpa_end_) \
+       for (mtrr_lookup_init(_iter_, _mtrr_, _gpa_start_, _gpa_end_); \
+            mtrr_lookup_okay(_iter_); mtrr_lookup_next(_iter_))
+
+u8 kvm_mtrr_get_guest_memory_type(struct kvm_vcpu *vcpu, gfn_t gfn)
+{
+       struct kvm_mtrr *mtrr_state = &vcpu->arch.mtrr_state;
+       struct mtrr_iter iter;
+       u64 start, end;
+       int type = -1;
+       const int wt_wb_mask = (1 << MTRR_TYPE_WRBACK)
+                              | (1 << MTRR_TYPE_WRTHROUGH);
+
+       start = gfn_to_gpa(gfn);
+       end = start + PAGE_SIZE;
+
+       mtrr_for_each_mem_type(&iter, mtrr_state, start, end) {
+               int curr_type = iter.mem_type;
+
+               /*
+                * Please refer to Intel SDM Volume 3: 11.11.4.1 MTRR
+                * Precedences.
+                */
+
+               if (type == -1) {
+                       type = curr_type;
+                       continue;
+               }
+
+               /*
+                * If two or more variable memory ranges match and the
+                * memory types are identical, then that memory type is
+                * used.
+                */
+               if (type == curr_type)
+                       continue;
+
+               /*
+                * If two or more variable memory ranges match and one of
+                * the memory types is UC, the UC memory type used.
+                */
+               if (curr_type == MTRR_TYPE_UNCACHABLE)
+                       return MTRR_TYPE_UNCACHABLE;
+
+               /*
+                * If two or more variable memory ranges match and the
+                * memory types are WT and WB, the WT memory type is used.
+                */
+               if (((1 << type) & wt_wb_mask) &&
+                     ((1 << curr_type) & wt_wb_mask)) {
+                       type = MTRR_TYPE_WRTHROUGH;
+                       continue;
+               }
+
+               /*
+                * For overlaps not defined by the above rules, processor
+                * behavior is undefined.
+                */
+
+               /* We use WB for this undefined behavior. :( */
+               return MTRR_TYPE_WRBACK;
+       }
+
+       /* It is not covered by MTRRs. */
+       if (iter.partial_map) {
+               /*
+                * We just check one page, partially covered by MTRRs is
+                * impossible.
+                */
+               WARN_ON(type != -1);
+               type = mtrr_default_type(mtrr_state);
+       }
+       return type;
+}
+EXPORT_SYMBOL_GPL(kvm_mtrr_get_guest_memory_type);
+
+bool kvm_mtrr_check_gfn_range_consistency(struct kvm_vcpu *vcpu, gfn_t gfn,
+                                         int page_num)
+{
+       struct kvm_mtrr *mtrr_state = &vcpu->arch.mtrr_state;
+       struct mtrr_iter iter;
+       u64 start, end;
+       int type = -1;
+
+       start = gfn_to_gpa(gfn);
+       end = gfn_to_gpa(gfn + page_num);
+       mtrr_for_each_mem_type(&iter, mtrr_state, start, end) {
+               if (type == -1) {
+                       type = iter.mem_type;
+                       continue;
+               }
+
+               if (type != iter.mem_type)
+                       return false;
+       }
+
+       if (!iter.partial_map)
+               return true;
+
+       if (type == -1)
+               return true;
+
+       return type == mtrr_default_type(mtrr_state);
+}
index 6e6d115fe9b542607c946cf57d1cc37ca948c923..0f67d7e2480074f9d313e0f6af43bd9b64c5a555 100644 (file)
@@ -256,7 +256,7 @@ static int FNAME(update_accessed_dirty_bits)(struct kvm_vcpu *vcpu,
                if (ret)
                        return ret;
 
-               mark_page_dirty(vcpu->kvm, table_gfn);
+               kvm_vcpu_mark_page_dirty(vcpu, table_gfn);
                walker->ptes[level] = pte;
        }
        return 0;
@@ -338,7 +338,7 @@ retry_walk:
 
                real_gfn = gpa_to_gfn(real_gfn);
 
-               host_addr = gfn_to_hva_prot(vcpu->kvm, real_gfn,
+               host_addr = kvm_vcpu_gfn_to_hva_prot(vcpu, real_gfn,
                                            &walker->pte_writable[walker->level - 1]);
                if (unlikely(kvm_is_error_hva(host_addr)))
                        goto error;
@@ -511,11 +511,11 @@ static bool FNAME(gpte_changed)(struct kvm_vcpu *vcpu,
                base_gpa = pte_gpa & ~mask;
                index = (pte_gpa - base_gpa) / sizeof(pt_element_t);
 
-               r = kvm_read_guest_atomic(vcpu->kvm, base_gpa,
+               r = kvm_vcpu_read_guest_atomic(vcpu, base_gpa,
                                gw->prefetch_ptes, sizeof(gw->prefetch_ptes));
                curr_pte = gw->prefetch_ptes[index];
        } else
-               r = kvm_read_guest_atomic(vcpu->kvm, pte_gpa,
+               r = kvm_vcpu_read_guest_atomic(vcpu, pte_gpa,
                                  &curr_pte, sizeof(curr_pte));
 
        return r || curr_pte != gw->ptes[level - 1];
@@ -869,8 +869,8 @@ static void FNAME(invlpg)(struct kvm_vcpu *vcpu, gva_t gva)
                        if (!rmap_can_add(vcpu))
                                break;
 
-                       if (kvm_read_guest_atomic(vcpu->kvm, pte_gpa, &gpte,
-                                                 sizeof(pt_element_t)))
+                       if (kvm_vcpu_read_guest_atomic(vcpu, pte_gpa, &gpte,
+                                                      sizeof(pt_element_t)))
                                break;
 
                        FNAME(update_pte)(vcpu, sp, sptep, &gpte);
@@ -956,8 +956,8 @@ static int FNAME(sync_page)(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp)
 
                pte_gpa = first_pte_gpa + i * sizeof(pt_element_t);
 
-               if (kvm_read_guest_atomic(vcpu->kvm, pte_gpa, &gpte,
-                                         sizeof(pt_element_t)))
+               if (kvm_vcpu_read_guest_atomic(vcpu, pte_gpa, &gpte,
+                                              sizeof(pt_element_t)))
                        return -EINVAL;
 
                if (FNAME(prefetch_invalid_gpte)(vcpu, sp, &sp->spt[i], gpte)) {
@@ -970,7 +970,7 @@ static int FNAME(sync_page)(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp)
                pte_access &= FNAME(gpte_access)(vcpu, gpte);
                FNAME(protect_clean_gpte)(&pte_access, gpte);
 
-               if (sync_mmio_spte(vcpu->kvm, &sp->spt[i], gfn, pte_access,
+               if (sync_mmio_spte(vcpu, &sp->spt[i], gfn, pte_access,
                      &nr_present))
                        continue;
 
index 29fbf9dfdc549f47f1e189f58c5d5adfeb7a5fd1..31aa2c85dc9761ec104a9b8f36015a120eb41250 100644 (file)
@@ -1,11 +1,12 @@
 /*
  * Kernel-based Virtual Machine -- Performance Monitoring Unit support
  *
- * Copyright 2011 Red Hat, Inc. and/or its affiliates.
+ * Copyright 2015 Red Hat, Inc. and/or its affiliates.
  *
  * Authors:
  *   Avi Kivity   <avi@redhat.com>
  *   Gleb Natapov <gleb@redhat.com>
+ *   Wei Huang    <wei@redhat.com>
  *
  * This work is licensed under the terms of the GNU GPL, version 2.  See
  * the COPYING file in the top-level directory.
 #include "x86.h"
 #include "cpuid.h"
 #include "lapic.h"
+#include "pmu.h"
+
+/* NOTE:
+ * - Each perf counter is defined as "struct kvm_pmc";
+ * - There are two types of perf counters: general purpose (gp) and fixed.
+ *   gp counters are stored in gp_counters[] and fixed counters are stored
+ *   in fixed_counters[] respectively. Both of them are part of "struct
+ *   kvm_pmu";
+ * - pmu.c understands the difference between gp counters and fixed counters.
+ *   However AMD doesn't support fixed-counters;
+ * - There are three types of index to access perf counters (PMC):
+ *     1. MSR (named msr): For example Intel has MSR_IA32_PERFCTRn and AMD
+ *        has MSR_K7_PERFCTRn.
+ *     2. MSR Index (named idx): This normally is used by RDPMC instruction.
+ *        For instance AMD RDPMC instruction uses 0000_0003h in ECX to access
+ *        C001_0007h (MSR_K7_PERCTR3). Intel has a similar mechanism, except
+ *        that it also supports fixed counters. idx can be used to as index to
+ *        gp and fixed counters.
+ *     3. Global PMC Index (named pmc): pmc is an index specific to PMU
+ *        code. Each pmc, stored in kvm_pmc.idx field, is unique across
+ *        all perf counters (both gp and fixed). The mapping relationship
+ *        between pmc and perf counters is as the following:
+ *        * Intel: [0 .. INTEL_PMC_MAX_GENERIC-1] <=> gp counters
+ *                 [INTEL_PMC_IDX_FIXED .. INTEL_PMC_IDX_FIXED + 2] <=> fixed
+ *        * AMD:   [0 .. AMD64_NUM_COUNTERS-1] <=> gp counters
+ */
 
-static struct kvm_arch_event_perf_mapping {
-       u8 eventsel;
-       u8 unit_mask;
-       unsigned event_type;
-       bool inexact;
-} arch_events[] = {
-       /* Index must match CPUID 0x0A.EBX bit vector */
-       [0] = { 0x3c, 0x00, PERF_COUNT_HW_CPU_CYCLES },
-       [1] = { 0xc0, 0x00, PERF_COUNT_HW_INSTRUCTIONS },
-       [2] = { 0x3c, 0x01, PERF_COUNT_HW_BUS_CYCLES  },
-       [3] = { 0x2e, 0x4f, PERF_COUNT_HW_CACHE_REFERENCES },
-       [4] = { 0x2e, 0x41, PERF_COUNT_HW_CACHE_MISSES },
-       [5] = { 0xc4, 0x00, PERF_COUNT_HW_BRANCH_INSTRUCTIONS },
-       [6] = { 0xc5, 0x00, PERF_COUNT_HW_BRANCH_MISSES },
-       [7] = { 0x00, 0x30, PERF_COUNT_HW_REF_CPU_CYCLES },
-};
-
-/* mapping between fixed pmc index and arch_events array */
-static int fixed_pmc_events[] = {1, 0, 7};
-
-static bool pmc_is_gp(struct kvm_pmc *pmc)
-{
-       return pmc->type == KVM_PMC_GP;
-}
-
-static inline u64 pmc_bitmask(struct kvm_pmc *pmc)
-{
-       struct kvm_pmu *pmu = &pmc->vcpu->arch.pmu;
-
-       return pmu->counter_bitmask[pmc->type];
-}
-
-static inline bool pmc_enabled(struct kvm_pmc *pmc)
-{
-       struct kvm_pmu *pmu = &pmc->vcpu->arch.pmu;
-       return test_bit(pmc->idx, (unsigned long *)&pmu->global_ctrl);
-}
-
-static inline struct kvm_pmc *get_gp_pmc(struct kvm_pmu *pmu, u32 msr,
-                                        u32 base)
-{
-       if (msr >= base && msr < base + pmu->nr_arch_gp_counters)
-               return &pmu->gp_counters[msr - base];
-       return NULL;
-}
-
-static inline struct kvm_pmc *get_fixed_pmc(struct kvm_pmu *pmu, u32 msr)
-{
-       int base = MSR_CORE_PERF_FIXED_CTR0;
-       if (msr >= base && msr < base + pmu->nr_arch_fixed_counters)
-               return &pmu->fixed_counters[msr - base];
-       return NULL;
-}
-
-static inline struct kvm_pmc *get_fixed_pmc_idx(struct kvm_pmu *pmu, int idx)
-{
-       return get_fixed_pmc(pmu, MSR_CORE_PERF_FIXED_CTR0 + idx);
-}
-
-static struct kvm_pmc *global_idx_to_pmc(struct kvm_pmu *pmu, int idx)
-{
-       if (idx < INTEL_PMC_IDX_FIXED)
-               return get_gp_pmc(pmu, MSR_P6_EVNTSEL0 + idx, MSR_P6_EVNTSEL0);
-       else
-               return get_fixed_pmc_idx(pmu, idx - INTEL_PMC_IDX_FIXED);
-}
-
-void kvm_deliver_pmi(struct kvm_vcpu *vcpu)
-{
-       if (vcpu->arch.apic)
-               kvm_apic_local_deliver(vcpu->arch.apic, APIC_LVTPC);
-}
-
-static void trigger_pmi(struct irq_work *irq_work)
+static void kvm_pmi_trigger_fn(struct irq_work *irq_work)
 {
-       struct kvm_pmu *pmu = container_of(irq_work, struct kvm_pmu,
-                       irq_work);
-       struct kvm_vcpu *vcpu = container_of(pmu, struct kvm_vcpu,
-                       arch.pmu);
+       struct kvm_pmu *pmu = container_of(irq_work, struct kvm_pmu, irq_work);
+       struct kvm_vcpu *vcpu = pmu_to_vcpu(pmu);
 
-       kvm_deliver_pmi(vcpu);
+       kvm_pmu_deliver_pmi(vcpu);
 }
 
 static void kvm_perf_overflow(struct perf_event *perf_event,
@@ -108,63 +60,46 @@ static void kvm_perf_overflow(struct perf_event *perf_event,
                              struct pt_regs *regs)
 {
        struct kvm_pmc *pmc = perf_event->overflow_handler_context;
-       struct kvm_pmu *pmu = &pmc->vcpu->arch.pmu;
-       if (!test_and_set_bit(pmc->idx, (unsigned long *)&pmu->reprogram_pmi)) {
+       struct kvm_pmu *pmu = pmc_to_pmu(pmc);
+
+       if (!test_and_set_bit(pmc->idx,
+                             (unsigned long *)&pmu->reprogram_pmi)) {
                __set_bit(pmc->idx, (unsigned long *)&pmu->global_status);
                kvm_make_request(KVM_REQ_PMU, pmc->vcpu);
        }
 }
 
 static void kvm_perf_overflow_intr(struct perf_event *perf_event,
-               struct perf_sample_data *data, struct pt_regs *regs)
+                                  struct perf_sample_data *data,
+                                  struct pt_regs *regs)
 {
        struct kvm_pmc *pmc = perf_event->overflow_handler_context;
-       struct kvm_pmu *pmu = &pmc->vcpu->arch.pmu;
-       if (!test_and_set_bit(pmc->idx, (unsigned long *)&pmu->reprogram_pmi)) {
+       struct kvm_pmu *pmu = pmc_to_pmu(pmc);
+
+       if (!test_and_set_bit(pmc->idx,
+                             (unsigned long *)&pmu->reprogram_pmi)) {
                __set_bit(pmc->idx, (unsigned long *)&pmu->global_status);
                kvm_make_request(KVM_REQ_PMU, pmc->vcpu);
+
                /*
                 * Inject PMI. If vcpu was in a guest mode during NMI PMI
                 * can be ejected on a guest mode re-entry. Otherwise we can't
                 * be sure that vcpu wasn't executing hlt instruction at the
-                * time of vmexit and is not going to re-enter guest mode until,
+                * time of vmexit and is not going to re-enter guest mode until
                 * woken up. So we should wake it, but this is impossible from
                 * NMI context. Do it from irq work instead.
                 */
                if (!kvm_is_in_guest())
-                       irq_work_queue(&pmc->vcpu->arch.pmu.irq_work);
+                       irq_work_queue(&pmc_to_pmu(pmc)->irq_work);
                else
                        kvm_make_request(KVM_REQ_PMI, pmc->vcpu);
        }
 }
 
-static u64 read_pmc(struct kvm_pmc *pmc)
-{
-       u64 counter, enabled, running;
-
-       counter = pmc->counter;
-
-       if (pmc->perf_event)
-               counter += perf_event_read_value(pmc->perf_event,
-                                                &enabled, &running);
-
-       /* FIXME: Scaling needed? */
-
-       return counter & pmc_bitmask(pmc);
-}
-
-static void stop_counter(struct kvm_pmc *pmc)
-{
-       if (pmc->perf_event) {
-               pmc->counter = read_pmc(pmc);
-               perf_event_release_kernel(pmc->perf_event);
-               pmc->perf_event = NULL;
-       }
-}
-
-static void reprogram_counter(struct kvm_pmc *pmc, u32 type,
-               unsigned config, bool exclude_user, bool exclude_kernel,
-               bool intr, bool in_tx, bool in_tx_cp)
+static void pmc_reprogram_counter(struct kvm_pmc *pmc, u32 type,
+                                 unsigned config, bool exclude_user,
+                                 bool exclude_kernel, bool intr,
+                                 bool in_tx, bool in_tx_cp)
 {
        struct perf_event *event;
        struct perf_event_attr attr = {
@@ -177,6 +112,7 @@ static void reprogram_counter(struct kvm_pmc *pmc, u32 type,
                .exclude_kernel = exclude_kernel,
                .config = config,
        };
+
        if (in_tx)
                attr.config |= HSW_IN_TX;
        if (in_tx_cp)
@@ -188,33 +124,16 @@ static void reprogram_counter(struct kvm_pmc *pmc, u32 type,
                                                 intr ? kvm_perf_overflow_intr :
                                                 kvm_perf_overflow, pmc);
        if (IS_ERR(event)) {
-               printk_once("kvm: pmu event creation failed %ld\n",
-                               PTR_ERR(event));
+               printk_once("kvm_pmu: event creation failed %ld\n",
+                           PTR_ERR(event));
                return;
        }
 
        pmc->perf_event = event;
-       clear_bit(pmc->idx, (unsigned long*)&pmc->vcpu->arch.pmu.reprogram_pmi);
-}
-
-static unsigned find_arch_event(struct kvm_pmu *pmu, u8 event_select,
-               u8 unit_mask)
-{
-       int i;
-
-       for (i = 0; i < ARRAY_SIZE(arch_events); i++)
-               if (arch_events[i].eventsel == event_select
-                               && arch_events[i].unit_mask == unit_mask
-                               && (pmu->available_event_types & (1 << i)))
-                       break;
-
-       if (i == ARRAY_SIZE(arch_events))
-               return PERF_COUNT_HW_MAX;
-
-       return arch_events[i].event_type;
+       clear_bit(pmc->idx, (unsigned long*)&pmc_to_pmu(pmc)->reprogram_pmi);
 }
 
-static void reprogram_gp_counter(struct kvm_pmc *pmc, u64 eventsel)
+void reprogram_gp_counter(struct kvm_pmc *pmc, u64 eventsel)
 {
        unsigned config, type = PERF_TYPE_RAW;
        u8 event_select, unit_mask;
@@ -224,21 +143,22 @@ static void reprogram_gp_counter(struct kvm_pmc *pmc, u64 eventsel)
 
        pmc->eventsel = eventsel;
 
-       stop_counter(pmc);
+       pmc_stop_counter(pmc);
 
-       if (!(eventsel & ARCH_PERFMON_EVENTSEL_ENABLE) || !pmc_enabled(pmc))
+       if (!(eventsel & ARCH_PERFMON_EVENTSEL_ENABLE) || !pmc_is_enabled(pmc))
                return;
 
        event_select = eventsel & ARCH_PERFMON_EVENTSEL_EVENT;
        unit_mask = (eventsel & ARCH_PERFMON_EVENTSEL_UMASK) >> 8;
 
        if (!(eventsel & (ARCH_PERFMON_EVENTSEL_EDGE |
-                               ARCH_PERFMON_EVENTSEL_INV |
-                               ARCH_PERFMON_EVENTSEL_CMASK |
-                               HSW_IN_TX |
-                               HSW_IN_TX_CHECKPOINTED))) {
-               config = find_arch_event(&pmc->vcpu->arch.pmu, event_select,
-                               unit_mask);
+                         ARCH_PERFMON_EVENTSEL_INV |
+                         ARCH_PERFMON_EVENTSEL_CMASK |
+                         HSW_IN_TX |
+                         HSW_IN_TX_CHECKPOINTED))) {
+               config = kvm_x86_ops->pmu_ops->find_arch_event(pmc_to_pmu(pmc),
+                                                     event_select,
+                                                     unit_mask);
                if (config != PERF_COUNT_HW_MAX)
                        type = PERF_TYPE_HARDWARE;
        }
@@ -246,56 +166,36 @@ static void reprogram_gp_counter(struct kvm_pmc *pmc, u64 eventsel)
        if (type == PERF_TYPE_RAW)
                config = eventsel & X86_RAW_EVENT_MASK;
 
-       reprogram_counter(pmc, type, config,
-                       !(eventsel & ARCH_PERFMON_EVENTSEL_USR),
-                       !(eventsel & ARCH_PERFMON_EVENTSEL_OS),
-                       eventsel & ARCH_PERFMON_EVENTSEL_INT,
-                       (eventsel & HSW_IN_TX),
-                       (eventsel & HSW_IN_TX_CHECKPOINTED));
+       pmc_reprogram_counter(pmc, type, config,
+                             !(eventsel & ARCH_PERFMON_EVENTSEL_USR),
+                             !(eventsel & ARCH_PERFMON_EVENTSEL_OS),
+                             eventsel & ARCH_PERFMON_EVENTSEL_INT,
+                             (eventsel & HSW_IN_TX),
+                             (eventsel & HSW_IN_TX_CHECKPOINTED));
 }
+EXPORT_SYMBOL_GPL(reprogram_gp_counter);
 
-static void reprogram_fixed_counter(struct kvm_pmc *pmc, u8 en_pmi, int idx)
+void reprogram_fixed_counter(struct kvm_pmc *pmc, u8 ctrl, int idx)
 {
-       unsigned en = en_pmi & 0x3;
-       bool pmi = en_pmi & 0x8;
+       unsigned en_field = ctrl & 0x3;
+       bool pmi = ctrl & 0x8;
 
-       stop_counter(pmc);
+       pmc_stop_counter(pmc);
 
-       if (!en || !pmc_enabled(pmc))
+       if (!en_field || !pmc_is_enabled(pmc))
                return;
 
-       reprogram_counter(pmc, PERF_TYPE_HARDWARE,
-                       arch_events[fixed_pmc_events[idx]].event_type,
-                       !(en & 0x2), /* exclude user */
-                       !(en & 0x1), /* exclude kernel */
-                       pmi, false, false);
+       pmc_reprogram_counter(pmc, PERF_TYPE_HARDWARE,
+                             kvm_x86_ops->pmu_ops->find_fixed_event(idx),
+                             !(en_field & 0x2), /* exclude user */
+                             !(en_field & 0x1), /* exclude kernel */
+                             pmi, false, false);
 }
+EXPORT_SYMBOL_GPL(reprogram_fixed_counter);
 
-static inline u8 fixed_en_pmi(u64 ctrl, int idx)
+void reprogram_counter(struct kvm_pmu *pmu, int pmc_idx)
 {
-       return (ctrl >> (idx * 4)) & 0xf;
-}
-
-static void reprogram_fixed_counters(struct kvm_pmu *pmu, u64 data)
-{
-       int i;
-
-       for (i = 0; i < pmu->nr_arch_fixed_counters; i++) {
-               u8 en_pmi = fixed_en_pmi(data, i);
-               struct kvm_pmc *pmc = get_fixed_pmc_idx(pmu, i);
-
-               if (fixed_en_pmi(pmu->fixed_ctr_ctrl, i) == en_pmi)
-                       continue;
-
-               reprogram_fixed_counter(pmc, en_pmi, i);
-       }
-
-       pmu->fixed_ctr_ctrl = data;
-}
-
-static void reprogram_idx(struct kvm_pmu *pmu, int idx)
-{
-       struct kvm_pmc *pmc = global_idx_to_pmc(pmu, idx);
+       struct kvm_pmc *pmc = kvm_x86_ops->pmu_ops->pmc_idx_to_pmc(pmu, pmc_idx);
 
        if (!pmc)
                return;
@@ -303,274 +203,107 @@ static void reprogram_idx(struct kvm_pmu *pmu, int idx)
        if (pmc_is_gp(pmc))
                reprogram_gp_counter(pmc, pmc->eventsel);
        else {
-               int fidx = idx - INTEL_PMC_IDX_FIXED;
-               reprogram_fixed_counter(pmc,
-                               fixed_en_pmi(pmu->fixed_ctr_ctrl, fidx), fidx);
+               int idx = pmc_idx - INTEL_PMC_IDX_FIXED;
+               u8 ctrl = fixed_ctrl_field(pmu->fixed_ctr_ctrl, idx);
+
+               reprogram_fixed_counter(pmc, ctrl, idx);
        }
 }
+EXPORT_SYMBOL_GPL(reprogram_counter);
 
-static void global_ctrl_changed(struct kvm_pmu *pmu, u64 data)
+void kvm_pmu_handle_event(struct kvm_vcpu *vcpu)
 {
+       struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
+       u64 bitmask;
        int bit;
-       u64 diff = pmu->global_ctrl ^ data;
-
-       pmu->global_ctrl = data;
-
-       for_each_set_bit(bit, (unsigned long *)&diff, X86_PMC_IDX_MAX)
-               reprogram_idx(pmu, bit);
-}
 
-bool kvm_pmu_msr(struct kvm_vcpu *vcpu, u32 msr)
-{
-       struct kvm_pmu *pmu = &vcpu->arch.pmu;
-       int ret;
-
-       switch (msr) {
-       case MSR_CORE_PERF_FIXED_CTR_CTRL:
-       case MSR_CORE_PERF_GLOBAL_STATUS:
-       case MSR_CORE_PERF_GLOBAL_CTRL:
-       case MSR_CORE_PERF_GLOBAL_OVF_CTRL:
-               ret = pmu->version > 1;
-               break;
-       default:
-               ret = get_gp_pmc(pmu, msr, MSR_IA32_PERFCTR0)
-                       || get_gp_pmc(pmu, msr, MSR_P6_EVNTSEL0)
-                       || get_fixed_pmc(pmu, msr);
-               break;
-       }
-       return ret;
-}
+       bitmask = pmu->reprogram_pmi;
 
-int kvm_pmu_get_msr(struct kvm_vcpu *vcpu, u32 index, u64 *data)
-{
-       struct kvm_pmu *pmu = &vcpu->arch.pmu;
-       struct kvm_pmc *pmc;
+       for_each_set_bit(bit, (unsigned long *)&bitmask, X86_PMC_IDX_MAX) {
+               struct kvm_pmc *pmc = kvm_x86_ops->pmu_ops->pmc_idx_to_pmc(pmu, bit);
 
-       switch (index) {
-       case MSR_CORE_PERF_FIXED_CTR_CTRL:
-               *data = pmu->fixed_ctr_ctrl;
-               return 0;
-       case MSR_CORE_PERF_GLOBAL_STATUS:
-               *data = pmu->global_status;
-               return 0;
-       case MSR_CORE_PERF_GLOBAL_CTRL:
-               *data = pmu->global_ctrl;
-               return 0;
-       case MSR_CORE_PERF_GLOBAL_OVF_CTRL:
-               *data = pmu->global_ovf_ctrl;
-               return 0;
-       default:
-               if ((pmc = get_gp_pmc(pmu, index, MSR_IA32_PERFCTR0)) ||
-                               (pmc = get_fixed_pmc(pmu, index))) {
-                       *data = read_pmc(pmc);
-                       return 0;
-               } else if ((pmc = get_gp_pmc(pmu, index, MSR_P6_EVNTSEL0))) {
-                       *data = pmc->eventsel;
-                       return 0;
+               if (unlikely(!pmc || !pmc->perf_event)) {
+                       clear_bit(bit, (unsigned long *)&pmu->reprogram_pmi);
+                       continue;
                }
-       }
-       return 1;
-}
 
-int kvm_pmu_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
-{
-       struct kvm_pmu *pmu = &vcpu->arch.pmu;
-       struct kvm_pmc *pmc;
-       u32 index = msr_info->index;
-       u64 data = msr_info->data;
-
-       switch (index) {
-       case MSR_CORE_PERF_FIXED_CTR_CTRL:
-               if (pmu->fixed_ctr_ctrl == data)
-                       return 0;
-               if (!(data & 0xfffffffffffff444ull)) {
-                       reprogram_fixed_counters(pmu, data);
-                       return 0;
-               }
-               break;
-       case MSR_CORE_PERF_GLOBAL_STATUS:
-               if (msr_info->host_initiated) {
-                       pmu->global_status = data;
-                       return 0;
-               }
-               break; /* RO MSR */
-       case MSR_CORE_PERF_GLOBAL_CTRL:
-               if (pmu->global_ctrl == data)
-                       return 0;
-               if (!(data & pmu->global_ctrl_mask)) {
-                       global_ctrl_changed(pmu, data);
-                       return 0;
-               }
-               break;
-       case MSR_CORE_PERF_GLOBAL_OVF_CTRL:
-               if (!(data & (pmu->global_ctrl_mask & ~(3ull<<62)))) {
-                       if (!msr_info->host_initiated)
-                               pmu->global_status &= ~data;
-                       pmu->global_ovf_ctrl = data;
-                       return 0;
-               }
-               break;
-       default:
-               if ((pmc = get_gp_pmc(pmu, index, MSR_IA32_PERFCTR0)) ||
-                               (pmc = get_fixed_pmc(pmu, index))) {
-                       if (!msr_info->host_initiated)
-                               data = (s64)(s32)data;
-                       pmc->counter += data - read_pmc(pmc);
-                       return 0;
-               } else if ((pmc = get_gp_pmc(pmu, index, MSR_P6_EVNTSEL0))) {
-                       if (data == pmc->eventsel)
-                               return 0;
-                       if (!(data & pmu->reserved_bits)) {
-                               reprogram_gp_counter(pmc, data);
-                               return 0;
-                       }
-               }
+               reprogram_counter(pmu, bit);
        }
-       return 1;
 }
 
-int kvm_pmu_check_pmc(struct kvm_vcpu *vcpu, unsigned pmc)
+/* check if idx is a valid index to access PMU */
+int kvm_pmu_is_valid_msr_idx(struct kvm_vcpu *vcpu, unsigned idx)
 {
-       struct kvm_pmu *pmu = &vcpu->arch.pmu;
-       bool fixed = pmc & (1u << 30);
-       pmc &= ~(3u << 30);
-       return (!fixed && pmc >= pmu->nr_arch_gp_counters) ||
-               (fixed && pmc >= pmu->nr_arch_fixed_counters);
+       return kvm_x86_ops->pmu_ops->is_valid_msr_idx(vcpu, idx);
 }
 
-int kvm_pmu_read_pmc(struct kvm_vcpu *vcpu, unsigned pmc, u64 *data)
+int kvm_pmu_rdpmc(struct kvm_vcpu *vcpu, unsigned idx, u64 *data)
 {
-       struct kvm_pmu *pmu = &vcpu->arch.pmu;
-       bool fast_mode = pmc & (1u << 31);
-       bool fixed = pmc & (1u << 30);
-       struct kvm_pmc *counters;
-       u64 ctr;
-
-       pmc &= ~(3u << 30);
-       if (!fixed && pmc >= pmu->nr_arch_gp_counters)
-               return 1;
-       if (fixed && pmc >= pmu->nr_arch_fixed_counters)
+       bool fast_mode = idx & (1u << 31);
+       struct kvm_pmc *pmc;
+       u64 ctr_val;
+
+       pmc = kvm_x86_ops->pmu_ops->msr_idx_to_pmc(vcpu, idx);
+       if (!pmc)
                return 1;
-       counters = fixed ? pmu->fixed_counters : pmu->gp_counters;
-       ctr = read_pmc(&counters[pmc]);
+
+       ctr_val = pmc_read_counter(pmc);
        if (fast_mode)
-               ctr = (u32)ctr;
-       *data = ctr;
+               ctr_val = (u32)ctr_val;
 
+       *data = ctr_val;
        return 0;
 }
 
-void kvm_pmu_cpuid_update(struct kvm_vcpu *vcpu)
+void kvm_pmu_deliver_pmi(struct kvm_vcpu *vcpu)
 {
-       struct kvm_pmu *pmu = &vcpu->arch.pmu;
-       struct kvm_cpuid_entry2 *entry;
-       union cpuid10_eax eax;
-       union cpuid10_edx edx;
-
-       pmu->nr_arch_gp_counters = 0;
-       pmu->nr_arch_fixed_counters = 0;
-       pmu->counter_bitmask[KVM_PMC_GP] = 0;
-       pmu->counter_bitmask[KVM_PMC_FIXED] = 0;
-       pmu->version = 0;
-       pmu->reserved_bits = 0xffffffff00200000ull;
-
-       entry = kvm_find_cpuid_entry(vcpu, 0xa, 0);
-       if (!entry)
-               return;
-       eax.full = entry->eax;
-       edx.full = entry->edx;
-
-       pmu->version = eax.split.version_id;
-       if (!pmu->version)
-               return;
-
-       pmu->nr_arch_gp_counters = min_t(int, eax.split.num_counters,
-                                       INTEL_PMC_MAX_GENERIC);
-       pmu->counter_bitmask[KVM_PMC_GP] = ((u64)1 << eax.split.bit_width) - 1;
-       pmu->available_event_types = ~entry->ebx &
-                                       ((1ull << eax.split.mask_length) - 1);
-
-       if (pmu->version == 1) {
-               pmu->nr_arch_fixed_counters = 0;
-       } else {
-               pmu->nr_arch_fixed_counters =
-                       min_t(int, edx.split.num_counters_fixed,
-                               INTEL_PMC_MAX_FIXED);
-               pmu->counter_bitmask[KVM_PMC_FIXED] =
-                       ((u64)1 << edx.split.bit_width_fixed) - 1;
-       }
+       if (vcpu->arch.apic)
+               kvm_apic_local_deliver(vcpu->arch.apic, APIC_LVTPC);
+}
 
-       pmu->global_ctrl = ((1 << pmu->nr_arch_gp_counters) - 1) |
-               (((1ull << pmu->nr_arch_fixed_counters) - 1) << INTEL_PMC_IDX_FIXED);
-       pmu->global_ctrl_mask = ~pmu->global_ctrl;
+bool kvm_pmu_is_valid_msr(struct kvm_vcpu *vcpu, u32 msr)
+{
+       return kvm_x86_ops->pmu_ops->is_valid_msr(vcpu, msr);
+}
 
-       entry = kvm_find_cpuid_entry(vcpu, 7, 0);
-       if (entry &&
-           (boot_cpu_has(X86_FEATURE_HLE) || boot_cpu_has(X86_FEATURE_RTM)) &&
-           (entry->ebx & (X86_FEATURE_HLE|X86_FEATURE_RTM)))
-               pmu->reserved_bits ^= HSW_IN_TX|HSW_IN_TX_CHECKPOINTED;
+int kvm_pmu_get_msr(struct kvm_vcpu *vcpu, u32 msr, u64 *data)
+{
+       return kvm_x86_ops->pmu_ops->get_msr(vcpu, msr, data);
 }
 
-void kvm_pmu_init(struct kvm_vcpu *vcpu)
+int kvm_pmu_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
 {
-       int i;
-       struct kvm_pmu *pmu = &vcpu->arch.pmu;
+       return kvm_x86_ops->pmu_ops->set_msr(vcpu, msr_info);
+}
 
-       memset(pmu, 0, sizeof(*pmu));
-       for (i = 0; i < INTEL_PMC_MAX_GENERIC; i++) {
-               pmu->gp_counters[i].type = KVM_PMC_GP;
-               pmu->gp_counters[i].vcpu = vcpu;
-               pmu->gp_counters[i].idx = i;
-       }
-       for (i = 0; i < INTEL_PMC_MAX_FIXED; i++) {
-               pmu->fixed_counters[i].type = KVM_PMC_FIXED;
-               pmu->fixed_counters[i].vcpu = vcpu;
-               pmu->fixed_counters[i].idx = i + INTEL_PMC_IDX_FIXED;
-       }
-       init_irq_work(&pmu->irq_work, trigger_pmi);
-       kvm_pmu_cpuid_update(vcpu);
+/* refresh PMU settings. This function generally is called when underlying
+ * settings are changed (such as changes of PMU CPUID by guest VMs), which
+ * should rarely happen.
+ */
+void kvm_pmu_refresh(struct kvm_vcpu *vcpu)
+{
+       kvm_x86_ops->pmu_ops->refresh(vcpu);
 }
 
 void kvm_pmu_reset(struct kvm_vcpu *vcpu)
 {
-       struct kvm_pmu *pmu = &vcpu->arch.pmu;
-       int i;
+       struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
 
        irq_work_sync(&pmu->irq_work);
-       for (i = 0; i < INTEL_PMC_MAX_GENERIC; i++) {
-               struct kvm_pmc *pmc = &pmu->gp_counters[i];
-               stop_counter(pmc);
-               pmc->counter = pmc->eventsel = 0;
-       }
+       kvm_x86_ops->pmu_ops->reset(vcpu);
+}
 
-       for (i = 0; i < INTEL_PMC_MAX_FIXED; i++)
-               stop_counter(&pmu->fixed_counters[i]);
+void kvm_pmu_init(struct kvm_vcpu *vcpu)
+{
+       struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
 
-       pmu->fixed_ctr_ctrl = pmu->global_ctrl = pmu->global_status =
-               pmu->global_ovf_ctrl = 0;
+       memset(pmu, 0, sizeof(*pmu));
+       kvm_x86_ops->pmu_ops->init(vcpu);
+       init_irq_work(&pmu->irq_work, kvm_pmi_trigger_fn);
+       kvm_pmu_refresh(vcpu);
 }
 
 void kvm_pmu_destroy(struct kvm_vcpu *vcpu)
 {
        kvm_pmu_reset(vcpu);
 }
-
-void kvm_handle_pmu_event(struct kvm_vcpu *vcpu)
-{
-       struct kvm_pmu *pmu = &vcpu->arch.pmu;
-       u64 bitmask;
-       int bit;
-
-       bitmask = pmu->reprogram_pmi;
-
-       for_each_set_bit(bit, (unsigned long *)&bitmask, X86_PMC_IDX_MAX) {
-               struct kvm_pmc *pmc = global_idx_to_pmc(pmu, bit);
-
-               if (unlikely(!pmc || !pmc->perf_event)) {
-                       clear_bit(bit, (unsigned long *)&pmu->reprogram_pmi);
-                       continue;
-               }
-
-               reprogram_idx(pmu, bit);
-       }
-}
diff --git a/arch/x86/kvm/pmu.h b/arch/x86/kvm/pmu.h
new file mode 100644 (file)
index 0000000..f96e1f9
--- /dev/null
@@ -0,0 +1,118 @@
+#ifndef __KVM_X86_PMU_H
+#define __KVM_X86_PMU_H
+
+#define vcpu_to_pmu(vcpu) (&(vcpu)->arch.pmu)
+#define pmu_to_vcpu(pmu)  (container_of((pmu), struct kvm_vcpu, arch.pmu))
+#define pmc_to_pmu(pmc)   (&(pmc)->vcpu->arch.pmu)
+
+/* retrieve the 4 bits for EN and PMI out of IA32_FIXED_CTR_CTRL */
+#define fixed_ctrl_field(ctrl_reg, idx) (((ctrl_reg) >> ((idx)*4)) & 0xf)
+
+struct kvm_event_hw_type_mapping {
+       u8 eventsel;
+       u8 unit_mask;
+       unsigned event_type;
+};
+
+struct kvm_pmu_ops {
+       unsigned (*find_arch_event)(struct kvm_pmu *pmu, u8 event_select,
+                                   u8 unit_mask);
+       unsigned (*find_fixed_event)(int idx);
+       bool (*pmc_is_enabled)(struct kvm_pmc *pmc);
+       struct kvm_pmc *(*pmc_idx_to_pmc)(struct kvm_pmu *pmu, int pmc_idx);
+       struct kvm_pmc *(*msr_idx_to_pmc)(struct kvm_vcpu *vcpu, unsigned idx);
+       int (*is_valid_msr_idx)(struct kvm_vcpu *vcpu, unsigned idx);
+       bool (*is_valid_msr)(struct kvm_vcpu *vcpu, u32 msr);
+       int (*get_msr)(struct kvm_vcpu *vcpu, u32 msr, u64 *data);
+       int (*set_msr)(struct kvm_vcpu *vcpu, struct msr_data *msr_info);
+       void (*refresh)(struct kvm_vcpu *vcpu);
+       void (*init)(struct kvm_vcpu *vcpu);
+       void (*reset)(struct kvm_vcpu *vcpu);
+};
+
+static inline u64 pmc_bitmask(struct kvm_pmc *pmc)
+{
+       struct kvm_pmu *pmu = pmc_to_pmu(pmc);
+
+       return pmu->counter_bitmask[pmc->type];
+}
+
+static inline u64 pmc_read_counter(struct kvm_pmc *pmc)
+{
+       u64 counter, enabled, running;
+
+       counter = pmc->counter;
+       if (pmc->perf_event)
+               counter += perf_event_read_value(pmc->perf_event,
+                                                &enabled, &running);
+       /* FIXME: Scaling needed? */
+       return counter & pmc_bitmask(pmc);
+}
+
+static inline void pmc_stop_counter(struct kvm_pmc *pmc)
+{
+       if (pmc->perf_event) {
+               pmc->counter = pmc_read_counter(pmc);
+               perf_event_release_kernel(pmc->perf_event);
+               pmc->perf_event = NULL;
+       }
+}
+
+static inline bool pmc_is_gp(struct kvm_pmc *pmc)
+{
+       return pmc->type == KVM_PMC_GP;
+}
+
+static inline bool pmc_is_fixed(struct kvm_pmc *pmc)
+{
+       return pmc->type == KVM_PMC_FIXED;
+}
+
+static inline bool pmc_is_enabled(struct kvm_pmc *pmc)
+{
+       return kvm_x86_ops->pmu_ops->pmc_is_enabled(pmc);
+}
+
+/* returns general purpose PMC with the specified MSR. Note that it can be
+ * used for both PERFCTRn and EVNTSELn; that is why it accepts base as a
+ * paramenter to tell them apart.
+ */
+static inline struct kvm_pmc *get_gp_pmc(struct kvm_pmu *pmu, u32 msr,
+                                        u32 base)
+{
+       if (msr >= base && msr < base + pmu->nr_arch_gp_counters)
+               return &pmu->gp_counters[msr - base];
+
+       return NULL;
+}
+
+/* returns fixed PMC with the specified MSR */
+static inline struct kvm_pmc *get_fixed_pmc(struct kvm_pmu *pmu, u32 msr)
+{
+       int base = MSR_CORE_PERF_FIXED_CTR0;
+
+       if (msr >= base && msr < base + pmu->nr_arch_fixed_counters)
+               return &pmu->fixed_counters[msr - base];
+
+       return NULL;
+}
+
+void reprogram_gp_counter(struct kvm_pmc *pmc, u64 eventsel);
+void reprogram_fixed_counter(struct kvm_pmc *pmc, u8 ctrl, int fixed_idx);
+void reprogram_counter(struct kvm_pmu *pmu, int pmc_idx);
+
+void kvm_pmu_deliver_pmi(struct kvm_vcpu *vcpu);
+void kvm_pmu_handle_event(struct kvm_vcpu *vcpu);
+int kvm_pmu_rdpmc(struct kvm_vcpu *vcpu, unsigned pmc, u64 *data);
+int kvm_pmu_is_valid_msr_idx(struct kvm_vcpu *vcpu, unsigned idx);
+bool kvm_pmu_is_valid_msr(struct kvm_vcpu *vcpu, u32 msr);
+int kvm_pmu_get_msr(struct kvm_vcpu *vcpu, u32 msr, u64 *data);
+int kvm_pmu_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info);
+void kvm_pmu_refresh(struct kvm_vcpu *vcpu);
+void kvm_pmu_reset(struct kvm_vcpu *vcpu);
+void kvm_pmu_init(struct kvm_vcpu *vcpu);
+void kvm_pmu_destroy(struct kvm_vcpu *vcpu);
+
+extern struct kvm_pmu_ops intel_pmu_ops;
+extern struct kvm_pmu_ops amd_pmu_ops;
+#endif /* __KVM_X86_PMU_H */
diff --git a/arch/x86/kvm/pmu_amd.c b/arch/x86/kvm/pmu_amd.c
new file mode 100644 (file)
index 0000000..886aa25
--- /dev/null
@@ -0,0 +1,207 @@
+/*
+ * KVM PMU support for AMD
+ *
+ * Copyright 2015, Red Hat, Inc. and/or its affiliates.
+ *
+ * Author:
+ *   Wei Huang <wei@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2.  See
+ * the COPYING file in the top-level directory.
+ *
+ * Implementation is based on pmu_intel.c file
+ */
+#include <linux/types.h>
+#include <linux/kvm_host.h>
+#include <linux/perf_event.h>
+#include "x86.h"
+#include "cpuid.h"
+#include "lapic.h"
+#include "pmu.h"
+
+/* duplicated from amd_perfmon_event_map, K7 and above should work. */
+static struct kvm_event_hw_type_mapping amd_event_mapping[] = {
+       [0] = { 0x76, 0x00, PERF_COUNT_HW_CPU_CYCLES },
+       [1] = { 0xc0, 0x00, PERF_COUNT_HW_INSTRUCTIONS },
+       [2] = { 0x80, 0x00, PERF_COUNT_HW_CACHE_REFERENCES },
+       [3] = { 0x81, 0x00, PERF_COUNT_HW_CACHE_MISSES },
+       [4] = { 0xc2, 0x00, PERF_COUNT_HW_BRANCH_INSTRUCTIONS },
+       [5] = { 0xc3, 0x00, PERF_COUNT_HW_BRANCH_MISSES },
+       [6] = { 0xd0, 0x00, PERF_COUNT_HW_STALLED_CYCLES_FRONTEND },
+       [7] = { 0xd1, 0x00, PERF_COUNT_HW_STALLED_CYCLES_BACKEND },
+};
+
+static unsigned amd_find_arch_event(struct kvm_pmu *pmu,
+                                   u8 event_select,
+                                   u8 unit_mask)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(amd_event_mapping); i++)
+               if (amd_event_mapping[i].eventsel == event_select
+                   && amd_event_mapping[i].unit_mask == unit_mask)
+                       break;
+
+       if (i == ARRAY_SIZE(amd_event_mapping))
+               return PERF_COUNT_HW_MAX;
+
+       return amd_event_mapping[i].event_type;
+}
+
+/* return PERF_COUNT_HW_MAX as AMD doesn't have fixed events */
+static unsigned amd_find_fixed_event(int idx)
+{
+       return PERF_COUNT_HW_MAX;
+}
+
+/* check if a PMC is enabled by comparing it against global_ctrl bits. Because
+ * AMD CPU doesn't have global_ctrl MSR, all PMCs are enabled (return TRUE).
+ */
+static bool amd_pmc_is_enabled(struct kvm_pmc *pmc)
+{
+       return true;
+}
+
+static struct kvm_pmc *amd_pmc_idx_to_pmc(struct kvm_pmu *pmu, int pmc_idx)
+{
+       return get_gp_pmc(pmu, MSR_K7_EVNTSEL0 + pmc_idx, MSR_K7_EVNTSEL0);
+}
+
+/* returns 0 if idx's corresponding MSR exists; otherwise returns 1. */
+static int amd_is_valid_msr_idx(struct kvm_vcpu *vcpu, unsigned idx)
+{
+       struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
+
+       idx &= ~(3u << 30);
+
+       return (idx >= pmu->nr_arch_gp_counters);
+}
+
+/* idx is the ECX register of RDPMC instruction */
+static struct kvm_pmc *amd_msr_idx_to_pmc(struct kvm_vcpu *vcpu, unsigned idx)
+{
+       struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
+       struct kvm_pmc *counters;
+
+       idx &= ~(3u << 30);
+       if (idx >= pmu->nr_arch_gp_counters)
+               return NULL;
+       counters = pmu->gp_counters;
+
+       return &counters[idx];
+}
+
+static bool amd_is_valid_msr(struct kvm_vcpu *vcpu, u32 msr)
+{
+       struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
+       int ret = false;
+
+       ret = get_gp_pmc(pmu, msr, MSR_K7_PERFCTR0) ||
+               get_gp_pmc(pmu, msr, MSR_K7_EVNTSEL0);
+
+       return ret;
+}
+
+static int amd_pmu_get_msr(struct kvm_vcpu *vcpu, u32 msr, u64 *data)
+{
+       struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
+       struct kvm_pmc *pmc;
+
+       /* MSR_K7_PERFCTRn */
+       pmc = get_gp_pmc(pmu, msr, MSR_K7_PERFCTR0);
+       if (pmc) {
+               *data = pmc_read_counter(pmc);
+               return 0;
+       }
+       /* MSR_K7_EVNTSELn */
+       pmc = get_gp_pmc(pmu, msr, MSR_K7_EVNTSEL0);
+       if (pmc) {
+               *data = pmc->eventsel;
+               return 0;
+       }
+
+       return 1;
+}
+
+static int amd_pmu_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
+{
+       struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
+       struct kvm_pmc *pmc;
+       u32 msr = msr_info->index;
+       u64 data = msr_info->data;
+
+       /* MSR_K7_PERFCTRn */
+       pmc = get_gp_pmc(pmu, msr, MSR_K7_PERFCTR0);
+       if (pmc) {
+               if (!msr_info->host_initiated)
+                       data = (s64)data;
+               pmc->counter += data - pmc_read_counter(pmc);
+               return 0;
+       }
+       /* MSR_K7_EVNTSELn */
+       pmc = get_gp_pmc(pmu, msr, MSR_K7_EVNTSEL0);
+       if (pmc) {
+               if (data == pmc->eventsel)
+                       return 0;
+               if (!(data & pmu->reserved_bits)) {
+                       reprogram_gp_counter(pmc, data);
+                       return 0;
+               }
+       }
+
+       return 1;
+}
+
+static void amd_pmu_refresh(struct kvm_vcpu *vcpu)
+{
+       struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
+
+       pmu->nr_arch_gp_counters = AMD64_NUM_COUNTERS;
+       pmu->counter_bitmask[KVM_PMC_GP] = ((u64)1 << 48) - 1;
+       pmu->reserved_bits = 0xffffffff00200000ull;
+       /* not applicable to AMD; but clean them to prevent any fall out */
+       pmu->counter_bitmask[KVM_PMC_FIXED] = 0;
+       pmu->nr_arch_fixed_counters = 0;
+       pmu->version = 0;
+       pmu->global_status = 0;
+}
+
+static void amd_pmu_init(struct kvm_vcpu *vcpu)
+{
+       struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
+       int i;
+
+       for (i = 0; i < AMD64_NUM_COUNTERS ; i++) {
+               pmu->gp_counters[i].type = KVM_PMC_GP;
+               pmu->gp_counters[i].vcpu = vcpu;
+               pmu->gp_counters[i].idx = i;
+       }
+}
+
+static void amd_pmu_reset(struct kvm_vcpu *vcpu)
+{
+       struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
+       int i;
+
+       for (i = 0; i < AMD64_NUM_COUNTERS; i++) {
+               struct kvm_pmc *pmc = &pmu->gp_counters[i];
+
+               pmc_stop_counter(pmc);
+               pmc->counter = pmc->eventsel = 0;
+       }
+}
+
+struct kvm_pmu_ops amd_pmu_ops = {
+       .find_arch_event = amd_find_arch_event,
+       .find_fixed_event = amd_find_fixed_event,
+       .pmc_is_enabled = amd_pmc_is_enabled,
+       .pmc_idx_to_pmc = amd_pmc_idx_to_pmc,
+       .msr_idx_to_pmc = amd_msr_idx_to_pmc,
+       .is_valid_msr_idx = amd_is_valid_msr_idx,
+       .is_valid_msr = amd_is_valid_msr,
+       .get_msr = amd_pmu_get_msr,
+       .set_msr = amd_pmu_set_msr,
+       .refresh = amd_pmu_refresh,
+       .init = amd_pmu_init,
+       .reset = amd_pmu_reset,
+};
diff --git a/arch/x86/kvm/pmu_intel.c b/arch/x86/kvm/pmu_intel.c
new file mode 100644 (file)
index 0000000..ab38af4
--- /dev/null
@@ -0,0 +1,358 @@
+/*
+ * KVM PMU support for Intel CPUs
+ *
+ * Copyright 2011 Red Hat, Inc. and/or its affiliates.
+ *
+ * Authors:
+ *   Avi Kivity   <avi@redhat.com>
+ *   Gleb Natapov <gleb@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2.  See
+ * the COPYING file in the top-level directory.
+ *
+ */
+#include <linux/types.h>
+#include <linux/kvm_host.h>
+#include <linux/perf_event.h>
+#include <asm/perf_event.h>
+#include "x86.h"
+#include "cpuid.h"
+#include "lapic.h"
+#include "pmu.h"
+
+static struct kvm_event_hw_type_mapping intel_arch_events[] = {
+       /* Index must match CPUID 0x0A.EBX bit vector */
+       [0] = { 0x3c, 0x00, PERF_COUNT_HW_CPU_CYCLES },
+       [1] = { 0xc0, 0x00, PERF_COUNT_HW_INSTRUCTIONS },
+       [2] = { 0x3c, 0x01, PERF_COUNT_HW_BUS_CYCLES  },
+       [3] = { 0x2e, 0x4f, PERF_COUNT_HW_CACHE_REFERENCES },
+       [4] = { 0x2e, 0x41, PERF_COUNT_HW_CACHE_MISSES },
+       [5] = { 0xc4, 0x00, PERF_COUNT_HW_BRANCH_INSTRUCTIONS },
+       [6] = { 0xc5, 0x00, PERF_COUNT_HW_BRANCH_MISSES },
+       [7] = { 0x00, 0x30, PERF_COUNT_HW_REF_CPU_CYCLES },
+};
+
+/* mapping between fixed pmc index and intel_arch_events array */
+static int fixed_pmc_events[] = {1, 0, 7};
+
+static void reprogram_fixed_counters(struct kvm_pmu *pmu, u64 data)
+{
+       int i;
+
+       for (i = 0; i < pmu->nr_arch_fixed_counters; i++) {
+               u8 new_ctrl = fixed_ctrl_field(data, i);
+               u8 old_ctrl = fixed_ctrl_field(pmu->fixed_ctr_ctrl, i);
+               struct kvm_pmc *pmc;
+
+               pmc = get_fixed_pmc(pmu, MSR_CORE_PERF_FIXED_CTR0 + i);
+
+               if (old_ctrl == new_ctrl)
+                       continue;
+
+               reprogram_fixed_counter(pmc, new_ctrl, i);
+       }
+
+       pmu->fixed_ctr_ctrl = data;
+}
+
+/* function is called when global control register has been updated. */
+static void global_ctrl_changed(struct kvm_pmu *pmu, u64 data)
+{
+       int bit;
+       u64 diff = pmu->global_ctrl ^ data;
+
+       pmu->global_ctrl = data;
+
+       for_each_set_bit(bit, (unsigned long *)&diff, X86_PMC_IDX_MAX)
+               reprogram_counter(pmu, bit);
+}
+
+static unsigned intel_find_arch_event(struct kvm_pmu *pmu,
+                                     u8 event_select,
+                                     u8 unit_mask)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(intel_arch_events); i++)
+               if (intel_arch_events[i].eventsel == event_select
+                   && intel_arch_events[i].unit_mask == unit_mask
+                   && (pmu->available_event_types & (1 << i)))
+                       break;
+
+       if (i == ARRAY_SIZE(intel_arch_events))
+               return PERF_COUNT_HW_MAX;
+
+       return intel_arch_events[i].event_type;
+}
+
+static unsigned intel_find_fixed_event(int idx)
+{
+       if (idx >= ARRAY_SIZE(fixed_pmc_events))
+               return PERF_COUNT_HW_MAX;
+
+       return intel_arch_events[fixed_pmc_events[idx]].event_type;
+}
+
+/* check if a PMC is enabled by comparising it with globl_ctrl bits. */
+static bool intel_pmc_is_enabled(struct kvm_pmc *pmc)
+{
+       struct kvm_pmu *pmu = pmc_to_pmu(pmc);
+
+       return test_bit(pmc->idx, (unsigned long *)&pmu->global_ctrl);
+}
+
+static struct kvm_pmc *intel_pmc_idx_to_pmc(struct kvm_pmu *pmu, int pmc_idx)
+{
+       if (pmc_idx < INTEL_PMC_IDX_FIXED)
+               return get_gp_pmc(pmu, MSR_P6_EVNTSEL0 + pmc_idx,
+                                 MSR_P6_EVNTSEL0);
+       else {
+               u32 idx = pmc_idx - INTEL_PMC_IDX_FIXED;
+
+               return get_fixed_pmc(pmu, idx + MSR_CORE_PERF_FIXED_CTR0);
+       }
+}
+
+/* returns 0 if idx's corresponding MSR exists; otherwise returns 1. */
+static int intel_is_valid_msr_idx(struct kvm_vcpu *vcpu, unsigned idx)
+{
+       struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
+       bool fixed = idx & (1u << 30);
+
+       idx &= ~(3u << 30);
+
+       return (!fixed && idx >= pmu->nr_arch_gp_counters) ||
+               (fixed && idx >= pmu->nr_arch_fixed_counters);
+}
+
+static struct kvm_pmc *intel_msr_idx_to_pmc(struct kvm_vcpu *vcpu,
+                                           unsigned idx)
+{
+       struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
+       bool fixed = idx & (1u << 30);
+       struct kvm_pmc *counters;
+
+       idx &= ~(3u << 30);
+       if (!fixed && idx >= pmu->nr_arch_gp_counters)
+               return NULL;
+       if (fixed && idx >= pmu->nr_arch_fixed_counters)
+               return NULL;
+       counters = fixed ? pmu->fixed_counters : pmu->gp_counters;
+
+       return &counters[idx];
+}
+
+static bool intel_is_valid_msr(struct kvm_vcpu *vcpu, u32 msr)
+{
+       struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
+       int ret;
+
+       switch (msr) {
+       case MSR_CORE_PERF_FIXED_CTR_CTRL:
+       case MSR_CORE_PERF_GLOBAL_STATUS:
+       case MSR_CORE_PERF_GLOBAL_CTRL:
+       case MSR_CORE_PERF_GLOBAL_OVF_CTRL:
+               ret = pmu->version > 1;
+               break;
+       default:
+               ret = get_gp_pmc(pmu, msr, MSR_IA32_PERFCTR0) ||
+                       get_gp_pmc(pmu, msr, MSR_P6_EVNTSEL0) ||
+                       get_fixed_pmc(pmu, msr);
+               break;
+       }
+
+       return ret;
+}
+
+static int intel_pmu_get_msr(struct kvm_vcpu *vcpu, u32 msr, u64 *data)
+{
+       struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
+       struct kvm_pmc *pmc;
+
+       switch (msr) {
+       case MSR_CORE_PERF_FIXED_CTR_CTRL:
+               *data = pmu->fixed_ctr_ctrl;
+               return 0;
+       case MSR_CORE_PERF_GLOBAL_STATUS:
+               *data = pmu->global_status;
+               return 0;
+       case MSR_CORE_PERF_GLOBAL_CTRL:
+               *data = pmu->global_ctrl;
+               return 0;
+       case MSR_CORE_PERF_GLOBAL_OVF_CTRL:
+               *data = pmu->global_ovf_ctrl;
+               return 0;
+       default:
+               if ((pmc = get_gp_pmc(pmu, msr, MSR_IA32_PERFCTR0)) ||
+                   (pmc = get_fixed_pmc(pmu, msr))) {
+                       *data = pmc_read_counter(pmc);
+                       return 0;
+               } else if ((pmc = get_gp_pmc(pmu, msr, MSR_P6_EVNTSEL0))) {
+                       *data = pmc->eventsel;
+                       return 0;
+               }
+       }
+
+       return 1;
+}
+
+static int intel_pmu_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
+{
+       struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
+       struct kvm_pmc *pmc;
+       u32 msr = msr_info->index;
+       u64 data = msr_info->data;
+
+       switch (msr) {
+       case MSR_CORE_PERF_FIXED_CTR_CTRL:
+               if (pmu->fixed_ctr_ctrl == data)
+                       return 0;
+               if (!(data & 0xfffffffffffff444ull)) {
+                       reprogram_fixed_counters(pmu, data);
+                       return 0;
+               }
+               break;
+       case MSR_CORE_PERF_GLOBAL_STATUS:
+               if (msr_info->host_initiated) {
+                       pmu->global_status = data;
+                       return 0;
+               }
+               break; /* RO MSR */
+       case MSR_CORE_PERF_GLOBAL_CTRL:
+               if (pmu->global_ctrl == data)
+                       return 0;
+               if (!(data & pmu->global_ctrl_mask)) {
+                       global_ctrl_changed(pmu, data);
+                       return 0;
+               }
+               break;
+       case MSR_CORE_PERF_GLOBAL_OVF_CTRL:
+               if (!(data & (pmu->global_ctrl_mask & ~(3ull<<62)))) {
+                       if (!msr_info->host_initiated)
+                               pmu->global_status &= ~data;
+                       pmu->global_ovf_ctrl = data;
+                       return 0;
+               }
+               break;
+       default:
+               if ((pmc = get_gp_pmc(pmu, msr, MSR_IA32_PERFCTR0)) ||
+                   (pmc = get_fixed_pmc(pmu, msr))) {
+                       if (!msr_info->host_initiated)
+                               data = (s64)(s32)data;
+                       pmc->counter += data - pmc_read_counter(pmc);
+                       return 0;
+               } else if ((pmc = get_gp_pmc(pmu, msr, MSR_P6_EVNTSEL0))) {
+                       if (data == pmc->eventsel)
+                               return 0;
+                       if (!(data & pmu->reserved_bits)) {
+                               reprogram_gp_counter(pmc, data);
+                               return 0;
+                       }
+               }
+       }
+
+       return 1;
+}
+
+static void intel_pmu_refresh(struct kvm_vcpu *vcpu)
+{
+       struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
+       struct kvm_cpuid_entry2 *entry;
+       union cpuid10_eax eax;
+       union cpuid10_edx edx;
+
+       pmu->nr_arch_gp_counters = 0;
+       pmu->nr_arch_fixed_counters = 0;
+       pmu->counter_bitmask[KVM_PMC_GP] = 0;
+       pmu->counter_bitmask[KVM_PMC_FIXED] = 0;
+       pmu->version = 0;
+       pmu->reserved_bits = 0xffffffff00200000ull;
+
+       entry = kvm_find_cpuid_entry(vcpu, 0xa, 0);
+       if (!entry)
+               return;
+       eax.full = entry->eax;
+       edx.full = entry->edx;
+
+       pmu->version = eax.split.version_id;
+       if (!pmu->version)
+               return;
+
+       pmu->nr_arch_gp_counters = min_t(int, eax.split.num_counters,
+                                       INTEL_PMC_MAX_GENERIC);
+       pmu->counter_bitmask[KVM_PMC_GP] = ((u64)1 << eax.split.bit_width) - 1;
+       pmu->available_event_types = ~entry->ebx &
+                                       ((1ull << eax.split.mask_length) - 1);
+
+       if (pmu->version == 1) {
+               pmu->nr_arch_fixed_counters = 0;
+       } else {
+               pmu->nr_arch_fixed_counters =
+                       min_t(int, edx.split.num_counters_fixed,
+                               INTEL_PMC_MAX_FIXED);
+               pmu->counter_bitmask[KVM_PMC_FIXED] =
+                       ((u64)1 << edx.split.bit_width_fixed) - 1;
+       }
+
+       pmu->global_ctrl = ((1 << pmu->nr_arch_gp_counters) - 1) |
+               (((1ull << pmu->nr_arch_fixed_counters) - 1) << INTEL_PMC_IDX_FIXED);
+       pmu->global_ctrl_mask = ~pmu->global_ctrl;
+
+       entry = kvm_find_cpuid_entry(vcpu, 7, 0);
+       if (entry &&
+           (boot_cpu_has(X86_FEATURE_HLE) || boot_cpu_has(X86_FEATURE_RTM)) &&
+           (entry->ebx & (X86_FEATURE_HLE|X86_FEATURE_RTM)))
+               pmu->reserved_bits ^= HSW_IN_TX|HSW_IN_TX_CHECKPOINTED;
+}
+
+static void intel_pmu_init(struct kvm_vcpu *vcpu)
+{
+       int i;
+       struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
+
+       for (i = 0; i < INTEL_PMC_MAX_GENERIC; i++) {
+               pmu->gp_counters[i].type = KVM_PMC_GP;
+               pmu->gp_counters[i].vcpu = vcpu;
+               pmu->gp_counters[i].idx = i;
+       }
+
+       for (i = 0; i < INTEL_PMC_MAX_FIXED; i++) {
+               pmu->fixed_counters[i].type = KVM_PMC_FIXED;
+               pmu->fixed_counters[i].vcpu = vcpu;
+               pmu->fixed_counters[i].idx = i + INTEL_PMC_IDX_FIXED;
+       }
+}
+
+static void intel_pmu_reset(struct kvm_vcpu *vcpu)
+{
+       struct kvm_pmu *pmu = vcpu_to_pmu(vcpu);
+       int i;
+
+       for (i = 0; i < INTEL_PMC_MAX_GENERIC; i++) {
+               struct kvm_pmc *pmc = &pmu->gp_counters[i];
+
+               pmc_stop_counter(pmc);
+               pmc->counter = pmc->eventsel = 0;
+       }
+
+       for (i = 0; i < INTEL_PMC_MAX_FIXED; i++)
+               pmc_stop_counter(&pmu->fixed_counters[i]);
+
+       pmu->fixed_ctr_ctrl = pmu->global_ctrl = pmu->global_status =
+               pmu->global_ovf_ctrl = 0;
+}
+
+struct kvm_pmu_ops intel_pmu_ops = {
+       .find_arch_event = intel_find_arch_event,
+       .find_fixed_event = intel_find_fixed_event,
+       .pmc_is_enabled = intel_pmc_is_enabled,
+       .pmc_idx_to_pmc = intel_pmc_idx_to_pmc,
+       .msr_idx_to_pmc = intel_msr_idx_to_pmc,
+       .is_valid_msr_idx = intel_is_valid_msr_idx,
+       .is_valid_msr = intel_is_valid_msr,
+       .get_msr = intel_pmu_get_msr,
+       .set_msr = intel_pmu_set_msr,
+       .refresh = intel_pmu_refresh,
+       .init = intel_pmu_init,
+       .reset = intel_pmu_reset,
+};
index 9afa233b5482f6a68addba2494916e9624e52566..851a9a1c6dfcc55a25e649c96abc811d8c866692 100644 (file)
@@ -21,6 +21,7 @@
 #include "kvm_cache_regs.h"
 #include "x86.h"
 #include "cpuid.h"
+#include "pmu.h"
 
 #include <linux/module.h>
 #include <linux/mod_devicetable.h>
@@ -511,8 +512,10 @@ static void skip_emulated_instruction(struct kvm_vcpu *vcpu)
 {
        struct vcpu_svm *svm = to_svm(vcpu);
 
-       if (svm->vmcb->control.next_rip != 0)
+       if (svm->vmcb->control.next_rip != 0) {
+               WARN_ON(!static_cpu_has(X86_FEATURE_NRIPS));
                svm->next_rip = svm->vmcb->control.next_rip;
+       }
 
        if (!svm->next_rip) {
                if (emulate_instruction(vcpu, EMULTYPE_SKIP) !=
@@ -1082,7 +1085,7 @@ static u64 svm_compute_tsc_offset(struct kvm_vcpu *vcpu, u64 target_tsc)
        return target_tsc - tsc;
 }
 
-static void init_vmcb(struct vcpu_svm *svm)
+static void init_vmcb(struct vcpu_svm *svm, bool init_event)
 {
        struct vmcb_control_area *control = &svm->vmcb->control;
        struct vmcb_save_area *save = &svm->vmcb->save;
@@ -1153,17 +1156,17 @@ static void init_vmcb(struct vcpu_svm *svm)
        init_sys_seg(&save->ldtr, SEG_TYPE_LDT);
        init_sys_seg(&save->tr, SEG_TYPE_BUSY_TSS16);
 
-       svm_set_efer(&svm->vcpu, 0);
+       if (!init_event)
+               svm_set_efer(&svm->vcpu, 0);
        save->dr6 = 0xffff0ff0;
        kvm_set_rflags(&svm->vcpu, 2);
        save->rip = 0x0000fff0;
        svm->vcpu.arch.regs[VCPU_REGS_RIP] = save->rip;
 
        /*
-        * This is the guest-visible cr0 value.
         * svm_set_cr0() sets PG and WP and clears NW and CD on save->cr0.
+        * It also updates the guest-visible cr0 value.
         */
-       svm->vcpu.arch.cr0 = 0;
        (void)kvm_set_cr0(&svm->vcpu, X86_CR0_NW | X86_CR0_CD | X86_CR0_ET);
 
        save->cr4 = X86_CR4_PAE;
@@ -1176,7 +1179,7 @@ static void init_vmcb(struct vcpu_svm *svm)
                clr_exception_intercept(svm, PF_VECTOR);
                clr_cr_intercept(svm, INTERCEPT_CR3_READ);
                clr_cr_intercept(svm, INTERCEPT_CR3_WRITE);
-               save->g_pat = 0x0007040600070406ULL;
+               save->g_pat = svm->vcpu.arch.pat;
                save->cr3 = 0;
                save->cr4 = 0;
        }
@@ -1195,13 +1198,19 @@ static void init_vmcb(struct vcpu_svm *svm)
        enable_gif(svm);
 }
 
-static void svm_vcpu_reset(struct kvm_vcpu *vcpu)
+static void svm_vcpu_reset(struct kvm_vcpu *vcpu, bool init_event)
 {
        struct vcpu_svm *svm = to_svm(vcpu);
        u32 dummy;
        u32 eax = 1;
 
-       init_vmcb(svm);
+       if (!init_event) {
+               svm->vcpu.arch.apic_base = APIC_DEFAULT_PHYS_BASE |
+                                          MSR_IA32_APICBASE_ENABLE;
+               if (kvm_vcpu_is_reset_bsp(&svm->vcpu))
+                       svm->vcpu.arch.apic_base |= MSR_IA32_APICBASE_BSP;
+       }
+       init_vmcb(svm, init_event);
 
        kvm_cpuid(vcpu, &eax, &dummy, &dummy, &dummy);
        kvm_register_write(vcpu, VCPU_REGS_RDX, eax);
@@ -1257,12 +1266,7 @@ static struct kvm_vcpu *svm_create_vcpu(struct kvm *kvm, unsigned int id)
        clear_page(svm->vmcb);
        svm->vmcb_pa = page_to_pfn(page) << PAGE_SHIFT;
        svm->asid_generation = 0;
-       init_vmcb(svm);
-
-       svm->vcpu.arch.apic_base = APIC_DEFAULT_PHYS_BASE |
-                                  MSR_IA32_APICBASE_ENABLE;
-       if (kvm_vcpu_is_reset_bsp(&svm->vcpu))
-               svm->vcpu.arch.apic_base |= MSR_IA32_APICBASE_BSP;
+       init_vmcb(svm, false);
 
        svm_init_osvw(&svm->vcpu);
 
@@ -1575,7 +1579,8 @@ static void svm_set_cr0(struct kvm_vcpu *vcpu, unsigned long cr0)
         * does not do it - this results in some delay at
         * reboot
         */
-       cr0 &= ~(X86_CR0_CD | X86_CR0_NW);
+       if (!(vcpu->kvm->arch.disabled_quirks & KVM_QUIRK_CD_NW_CLEARED))
+               cr0 &= ~(X86_CR0_CD | X86_CR0_NW);
        svm->vmcb->save.cr0 = cr0;
        mark_dirty(svm->vmcb, VMCB_CR);
        update_cr0_intercept(svm);
@@ -1883,7 +1888,7 @@ static int shutdown_interception(struct vcpu_svm *svm)
         * so reinitialize it.
         */
        clear_page(svm->vmcb);
-       init_vmcb(svm);
+       init_vmcb(svm, false);
 
        kvm_run->exit_reason = KVM_EXIT_SHUTDOWN;
        return 0;
@@ -1953,8 +1958,8 @@ static u64 nested_svm_get_tdp_pdptr(struct kvm_vcpu *vcpu, int index)
        u64 pdpte;
        int ret;
 
-       ret = kvm_read_guest_page(vcpu->kvm, gpa_to_gfn(cr3), &pdpte,
-                                 offset_in_page(cr3) + index * 8, 8);
+       ret = kvm_vcpu_read_guest_page(vcpu, gpa_to_gfn(cr3), &pdpte,
+                                      offset_in_page(cr3) + index * 8, 8);
        if (ret)
                return 0;
        return pdpte;
@@ -2112,7 +2117,7 @@ static void *nested_svm_map(struct vcpu_svm *svm, u64 gpa, struct page **_page)
 
        might_sleep();
 
-       page = gfn_to_page(svm->vcpu.kvm, gpa >> PAGE_SHIFT);
+       page = kvm_vcpu_gfn_to_page(&svm->vcpu, gpa >> PAGE_SHIFT);
        if (is_error_page(page))
                goto error;
 
@@ -2151,7 +2156,7 @@ static int nested_svm_intercept_ioio(struct vcpu_svm *svm)
        mask = (0xf >> (4 - size)) << start_bit;
        val = 0;
 
-       if (kvm_read_guest(svm->vcpu.kvm, gpa, &val, iopm_len))
+       if (kvm_vcpu_read_guest(&svm->vcpu, gpa, &val, iopm_len))
                return NESTED_EXIT_DONE;
 
        return (val & mask) ? NESTED_EXIT_DONE : NESTED_EXIT_HOST;
@@ -2176,7 +2181,7 @@ static int nested_svm_exit_handled_msr(struct vcpu_svm *svm)
        /* Offset is in 32 bit units but need in 8 bit units */
        offset *= 4;
 
-       if (kvm_read_guest(svm->vcpu.kvm, svm->nested.vmcb_msrpm + offset, &value, 4))
+       if (kvm_vcpu_read_guest(&svm->vcpu, svm->nested.vmcb_msrpm + offset, &value, 4))
                return NESTED_EXIT_DONE;
 
        return (value & mask) ? NESTED_EXIT_DONE : NESTED_EXIT_HOST;
@@ -2447,7 +2452,7 @@ static bool nested_svm_vmrun_msrpm(struct vcpu_svm *svm)
                p      = msrpm_offsets[i];
                offset = svm->nested.vmcb_msrpm + (p * 4);
 
-               if (kvm_read_guest(svm->vcpu.kvm, offset, &value, 4))
+               if (kvm_vcpu_read_guest(&svm->vcpu, offset, &value, 4))
                        return false;
 
                svm->nested.msrpm[p] = svm->msrpm[p] | value;
@@ -3067,42 +3072,42 @@ static u64 svm_read_l1_tsc(struct kvm_vcpu *vcpu, u64 host_tsc)
                svm_scale_tsc(vcpu, host_tsc);
 }
 
-static int svm_get_msr(struct kvm_vcpu *vcpu, unsigned ecx, u64 *data)
+static int svm_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
 {
        struct vcpu_svm *svm = to_svm(vcpu);
 
-       switch (ecx) {
+       switch (msr_info->index) {
        case MSR_IA32_TSC: {
-               *data = svm->vmcb->control.tsc_offset +
+               msr_info->data = svm->vmcb->control.tsc_offset +
                        svm_scale_tsc(vcpu, native_read_tsc());
 
                break;
        }
        case MSR_STAR:
-               *data = svm->vmcb->save.star;
+               msr_info->data = svm->vmcb->save.star;
                break;
 #ifdef CONFIG_X86_64
        case MSR_LSTAR:
-               *data = svm->vmcb->save.lstar;
+               msr_info->data = svm->vmcb->save.lstar;
                break;
        case MSR_CSTAR:
-               *data = svm->vmcb->save.cstar;
+               msr_info->data = svm->vmcb->save.cstar;
                break;
        case MSR_KERNEL_GS_BASE:
-               *data = svm->vmcb->save.kernel_gs_base;
+               msr_info->data = svm->vmcb->save.kernel_gs_base;
                break;
        case MSR_SYSCALL_MASK:
-               *data = svm->vmcb->save.sfmask;
+               msr_info->data = svm->vmcb->save.sfmask;
                break;
 #endif
        case MSR_IA32_SYSENTER_CS:
-               *data = svm->vmcb->save.sysenter_cs;
+               msr_info->data = svm->vmcb->save.sysenter_cs;
                break;
        case MSR_IA32_SYSENTER_EIP:
-               *data = svm->sysenter_eip;
+               msr_info->data = svm->sysenter_eip;
                break;
        case MSR_IA32_SYSENTER_ESP:
-               *data = svm->sysenter_esp;
+               msr_info->data = svm->sysenter_esp;
                break;
        /*
         * Nobody will change the following 5 values in the VMCB so we can
@@ -3110,31 +3115,31 @@ static int svm_get_msr(struct kvm_vcpu *vcpu, unsigned ecx, u64 *data)
         * implemented.
         */
        case MSR_IA32_DEBUGCTLMSR:
-               *data = svm->vmcb->save.dbgctl;
+               msr_info->data = svm->vmcb->save.dbgctl;
                break;
        case MSR_IA32_LASTBRANCHFROMIP:
-               *data = svm->vmcb->save.br_from;
+               msr_info->data = svm->vmcb->save.br_from;
                break;
        case MSR_IA32_LASTBRANCHTOIP:
-               *data = svm->vmcb->save.br_to;
+               msr_info->data = svm->vmcb->save.br_to;
                break;
        case MSR_IA32_LASTINTFROMIP:
-               *data = svm->vmcb->save.last_excp_from;
+               msr_info->data = svm->vmcb->save.last_excp_from;
                break;
        case MSR_IA32_LASTINTTOIP:
-               *data = svm->vmcb->save.last_excp_to;
+               msr_info->data = svm->vmcb->save.last_excp_to;
                break;
        case MSR_VM_HSAVE_PA:
-               *data = svm->nested.hsave_msr;
+               msr_info->data = svm->nested.hsave_msr;
                break;
        case MSR_VM_CR:
-               *data = svm->nested.vm_cr_msr;
+               msr_info->data = svm->nested.vm_cr_msr;
                break;
        case MSR_IA32_UCODE_REV:
-               *data = 0x01000065;
+               msr_info->data = 0x01000065;
                break;
        default:
-               return kvm_get_msr_common(vcpu, ecx, data);
+               return kvm_get_msr_common(vcpu, msr_info);
        }
        return 0;
 }
@@ -3142,16 +3147,20 @@ static int svm_get_msr(struct kvm_vcpu *vcpu, unsigned ecx, u64 *data)
 static int rdmsr_interception(struct vcpu_svm *svm)
 {
        u32 ecx = kvm_register_read(&svm->vcpu, VCPU_REGS_RCX);
-       u64 data;
+       struct msr_data msr_info;
 
-       if (svm_get_msr(&svm->vcpu, ecx, &data)) {
+       msr_info.index = ecx;
+       msr_info.host_initiated = false;
+       if (svm_get_msr(&svm->vcpu, &msr_info)) {
                trace_kvm_msr_read_ex(ecx);
                kvm_inject_gp(&svm->vcpu, 0);
        } else {
-               trace_kvm_msr_read(ecx, data);
+               trace_kvm_msr_read(ecx, msr_info.data);
 
-               kvm_register_write(&svm->vcpu, VCPU_REGS_RAX, data & 0xffffffff);
-               kvm_register_write(&svm->vcpu, VCPU_REGS_RDX, data >> 32);
+               kvm_register_write(&svm->vcpu, VCPU_REGS_RAX,
+                                  msr_info.data & 0xffffffff);
+               kvm_register_write(&svm->vcpu, VCPU_REGS_RDX,
+                                  msr_info.data >> 32);
                svm->next_rip = kvm_rip_read(&svm->vcpu) + 2;
                skip_emulated_instruction(&svm->vcpu);
        }
@@ -3388,6 +3397,7 @@ static int (*const svm_exit_handlers[])(struct vcpu_svm *svm) = {
        [SVM_EXIT_MWAIT]                        = mwait_interception,
        [SVM_EXIT_XSETBV]                       = xsetbv_interception,
        [SVM_EXIT_NPF]                          = pf_interception,
+       [SVM_EXIT_RSM]                          = emulate_on_interception,
 };
 
 static void dump_vmcb(struct kvm_vcpu *vcpu)
@@ -4073,6 +4083,11 @@ static bool svm_cpu_has_accelerated_tpr(void)
        return false;
 }
 
+static bool svm_has_high_real_mode_segbase(void)
+{
+       return true;
+}
+
 static u64 svm_get_mt_mask(struct kvm_vcpu *vcpu, gfn_t gfn, bool is_mmio)
 {
        return 0;
@@ -4317,7 +4332,9 @@ static int svm_check_intercept(struct kvm_vcpu *vcpu,
                break;
        }
 
-       vmcb->control.next_rip  = info->next_rip;
+       /* TODO: Advertise NRIPS to guest hypervisor unconditionally */
+       if (static_cpu_has(X86_FEATURE_NRIPS))
+               vmcb->control.next_rip  = info->next_rip;
        vmcb->control.exit_code = icpt_info.exit_code;
        vmexit = nested_svm_exit_handled(svm);
 
@@ -4346,6 +4363,7 @@ static struct kvm_x86_ops svm_x86_ops = {
        .hardware_enable = svm_hardware_enable,
        .hardware_disable = svm_hardware_disable,
        .cpu_has_accelerated_tpr = svm_cpu_has_accelerated_tpr,
+       .cpu_has_high_real_mode_segbase = svm_has_high_real_mode_segbase,
 
        .vcpu_create = svm_create_vcpu,
        .vcpu_free = svm_free_vcpu,
@@ -4440,6 +4458,8 @@ static struct kvm_x86_ops svm_x86_ops = {
        .handle_external_intr = svm_handle_external_intr,
 
        .sched_in = svm_sched_in,
+
+       .pmu_ops = &amd_pmu_ops,
 };
 
 static int __init svm_init(void)
index 7c7bc8bef21fd125e13c6ddc4a1f27f36c6b6b25..4eae7c35ddf56464ca8f2063a7bd36bf83eead53 100644 (file)
@@ -952,6 +952,28 @@ TRACE_EVENT(kvm_wait_lapic_expire,
                  __entry->delta < 0 ? "early" : "late")
 );
 
+TRACE_EVENT(kvm_enter_smm,
+       TP_PROTO(unsigned int vcpu_id, u64 smbase, bool entering),
+       TP_ARGS(vcpu_id, smbase, entering),
+
+       TP_STRUCT__entry(
+               __field(        unsigned int,   vcpu_id         )
+               __field(        u64,            smbase          )
+               __field(        bool,           entering        )
+       ),
+
+       TP_fast_assign(
+               __entry->vcpu_id        = vcpu_id;
+               __entry->smbase         = smbase;
+               __entry->entering       = entering;
+       ),
+
+       TP_printk("vcpu %u: %s SMM, smbase 0x%llx",
+                 __entry->vcpu_id,
+                 __entry->entering ? "entering" : "leaving",
+                 __entry->smbase)
+);
+
 #endif /* _TRACE_KVM_H */
 
 #undef TRACE_INCLUDE_PATH
index 2d73807f0d317f3c46a7aa42234a67d6894b20a2..ab53d80b0f64450ff7e2f77879bcd26bfec1196b 100644 (file)
 #include <asm/vmx.h>
 #include <asm/virtext.h>
 #include <asm/mce.h>
-#include <asm/i387.h>
-#include <asm/xcr.h>
+#include <asm/fpu/internal.h>
 #include <asm/perf_event.h>
 #include <asm/debugreg.h>
 #include <asm/kexec.h>
 #include <asm/apic.h>
 
 #include "trace.h"
+#include "pmu.h"
 
 #define __ex(x) __kvm_handle_fault_on_reboot(x)
 #define __ex_clear(x, reg) \
@@ -786,7 +786,7 @@ static inline struct vmcs12 *get_vmcs12(struct kvm_vcpu *vcpu)
 
 static struct page *nested_get_page(struct kvm_vcpu *vcpu, gpa_t addr)
 {
-       struct page *page = gfn_to_page(vcpu->kvm, addr >> PAGE_SHIFT);
+       struct page *page = kvm_vcpu_gfn_to_page(vcpu, addr >> PAGE_SHIFT);
        if (is_error_page(page))
                return NULL;
 
@@ -1883,7 +1883,7 @@ static void __vmx_load_host_state(struct vcpu_vmx *vmx)
         * If the FPU is not active (through the host task or
         * the guest vcpu), then restore the cr0.TS bit.
         */
-       if (!user_has_fpu() && !vmx->vcpu.guest_fpu_loaded)
+       if (!fpregs_active() && !vmx->vcpu.guest_fpu_loaded)
                stts();
        load_gdt(this_cpu_ptr(&host_gdt));
 }
@@ -2170,8 +2170,7 @@ static void vmx_set_msr_bitmap(struct kvm_vcpu *vcpu)
 
        if (is_guest_mode(vcpu))
                msr_bitmap = vmx_msr_bitmap_nested;
-       else if (irqchip_in_kernel(vcpu->kvm) &&
-               apic_x2apic_mode(vcpu->arch.apic)) {
+       else if (vcpu->arch.apic_base & X2APIC_ENABLE) {
                if (is_long_mode(vcpu))
                        msr_bitmap = vmx_msr_bitmap_longmode_x2apic;
                else
@@ -2623,76 +2622,69 @@ static int vmx_get_vmx_msr(struct kvm_vcpu *vcpu, u32 msr_index, u64 *pdata)
  * Returns 0 on success, non-0 otherwise.
  * Assumes vcpu_load() was already called.
  */
-static int vmx_get_msr(struct kvm_vcpu *vcpu, u32 msr_index, u64 *pdata)
+static int vmx_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
 {
-       u64 data;
        struct shared_msr_entry *msr;
 
-       if (!pdata) {
-               printk(KERN_ERR "BUG: get_msr called with NULL pdata\n");
-               return -EINVAL;
-       }
-
-       switch (msr_index) {
+       switch (msr_info->index) {
 #ifdef CONFIG_X86_64
        case MSR_FS_BASE:
-               data = vmcs_readl(GUEST_FS_BASE);
+               msr_info->data = vmcs_readl(GUEST_FS_BASE);
                break;
        case MSR_GS_BASE:
-               data = vmcs_readl(GUEST_GS_BASE);
+               msr_info->data = vmcs_readl(GUEST_GS_BASE);
                break;
        case MSR_KERNEL_GS_BASE:
                vmx_load_host_state(to_vmx(vcpu));
-               data = to_vmx(vcpu)->msr_guest_kernel_gs_base;
+               msr_info->data = to_vmx(vcpu)->msr_guest_kernel_gs_base;
                break;
 #endif
        case MSR_EFER:
-               return kvm_get_msr_common(vcpu, msr_index, pdata);
+               return kvm_get_msr_common(vcpu, msr_info);
        case MSR_IA32_TSC:
-               data = guest_read_tsc();
+               msr_info->data = guest_read_tsc();
                break;
        case MSR_IA32_SYSENTER_CS:
-               data = vmcs_read32(GUEST_SYSENTER_CS);
+               msr_info->data = vmcs_read32(GUEST_SYSENTER_CS);
                break;
        case MSR_IA32_SYSENTER_EIP:
-               data = vmcs_readl(GUEST_SYSENTER_EIP);
+               msr_info->data = vmcs_readl(GUEST_SYSENTER_EIP);
                break;
        case MSR_IA32_SYSENTER_ESP:
-               data = vmcs_readl(GUEST_SYSENTER_ESP);
+               msr_info->data = vmcs_readl(GUEST_SYSENTER_ESP);
                break;
        case MSR_IA32_BNDCFGS:
                if (!vmx_mpx_supported())
                        return 1;
-               data = vmcs_read64(GUEST_BNDCFGS);
+               msr_info->data = vmcs_read64(GUEST_BNDCFGS);
                break;
        case MSR_IA32_FEATURE_CONTROL:
                if (!nested_vmx_allowed(vcpu))
                        return 1;
-               data = to_vmx(vcpu)->nested.msr_ia32_feature_control;
+               msr_info->data = to_vmx(vcpu)->nested.msr_ia32_feature_control;
                break;
        case MSR_IA32_VMX_BASIC ... MSR_IA32_VMX_VMFUNC:
                if (!nested_vmx_allowed(vcpu))
                        return 1;
-               return vmx_get_vmx_msr(vcpu, msr_index, pdata);
+               return vmx_get_vmx_msr(vcpu, msr_info->index, &msr_info->data);
        case MSR_IA32_XSS:
                if (!vmx_xsaves_supported())
                        return 1;
-               data = vcpu->arch.ia32_xss;
+               msr_info->data = vcpu->arch.ia32_xss;
                break;
        case MSR_TSC_AUX:
                if (!to_vmx(vcpu)->rdtscp_enabled)
                        return 1;
                /* Otherwise falls through */
        default:
-               msr = find_msr_entry(to_vmx(vcpu), msr_index);
+               msr = find_msr_entry(to_vmx(vcpu), msr_info->index);
                if (msr) {
-                       data = msr->data;
+                       msr_info->data = msr->data;
                        break;
                }
-               return kvm_get_msr_common(vcpu, msr_index, pdata);
+               return kvm_get_msr_common(vcpu, msr_info);
        }
 
-       *pdata = data;
        return 0;
 }
 
@@ -4123,7 +4115,7 @@ static int alloc_apic_access_page(struct kvm *kvm)
        kvm_userspace_mem.flags = 0;
        kvm_userspace_mem.guest_phys_addr = APIC_DEFAULT_PHYS_BASE;
        kvm_userspace_mem.memory_size = PAGE_SIZE;
-       r = __kvm_set_memory_region(kvm, &kvm_userspace_mem);
+       r = __x86_set_memory_region(kvm, &kvm_userspace_mem);
        if (r)
                goto out;
 
@@ -4158,7 +4150,7 @@ static int alloc_identity_pagetable(struct kvm *kvm)
        kvm_userspace_mem.guest_phys_addr =
                kvm->arch.ept_identity_map_addr;
        kvm_userspace_mem.memory_size = PAGE_SIZE;
-       r = __kvm_set_memory_region(kvm, &kvm_userspace_mem);
+       r = __x86_set_memory_region(kvm, &kvm_userspace_mem);
 
        return r;
 }
@@ -4667,16 +4659,8 @@ static int vmx_vcpu_setup(struct vcpu_vmx *vmx)
        vmcs_write32(VM_ENTRY_MSR_LOAD_COUNT, 0);
        vmcs_write64(VM_ENTRY_MSR_LOAD_ADDR, __pa(vmx->msr_autoload.guest));
 
-       if (vmcs_config.vmentry_ctrl & VM_ENTRY_LOAD_IA32_PAT) {
-               u32 msr_low, msr_high;
-               u64 host_pat;
-               rdmsr(MSR_IA32_CR_PAT, msr_low, msr_high);
-               host_pat = msr_low | ((u64) msr_high << 32);
-               /* Write the default value follow host pat */
-               vmcs_write64(GUEST_IA32_PAT, host_pat);
-               /* Keep arch.pat sync with GUEST_IA32_PAT */
-               vmx->vcpu.arch.pat = host_pat;
-       }
+       if (vmcs_config.vmentry_ctrl & VM_ENTRY_LOAD_IA32_PAT)
+               vmcs_write64(GUEST_IA32_PAT, vmx->vcpu.arch.pat);
 
        for (i = 0; i < ARRAY_SIZE(vmx_msr_index); ++i) {
                u32 index = vmx_msr_index[i];
@@ -4708,22 +4692,27 @@ static int vmx_vcpu_setup(struct vcpu_vmx *vmx)
        return 0;
 }
 
-static void vmx_vcpu_reset(struct kvm_vcpu *vcpu)
+static void vmx_vcpu_reset(struct kvm_vcpu *vcpu, bool init_event)
 {
        struct vcpu_vmx *vmx = to_vmx(vcpu);
        struct msr_data apic_base_msr;
+       u64 cr0;
 
        vmx->rmode.vm86_active = 0;
 
        vmx->soft_vnmi_blocked = 0;
 
        vmx->vcpu.arch.regs[VCPU_REGS_RDX] = get_rdx_init_val();
-       kvm_set_cr8(&vmx->vcpu, 0);
-       apic_base_msr.data = APIC_DEFAULT_PHYS_BASE | MSR_IA32_APICBASE_ENABLE;
-       if (kvm_vcpu_is_reset_bsp(&vmx->vcpu))
-               apic_base_msr.data |= MSR_IA32_APICBASE_BSP;
-       apic_base_msr.host_initiated = true;
-       kvm_set_apic_base(&vmx->vcpu, &apic_base_msr);
+       kvm_set_cr8(vcpu, 0);
+
+       if (!init_event) {
+               apic_base_msr.data = APIC_DEFAULT_PHYS_BASE |
+                                    MSR_IA32_APICBASE_ENABLE;
+               if (kvm_vcpu_is_reset_bsp(vcpu))
+                       apic_base_msr.data |= MSR_IA32_APICBASE_BSP;
+               apic_base_msr.host_initiated = true;
+               kvm_set_apic_base(vcpu, &apic_base_msr);
+       }
 
        vmx_segment_cache_clear(vmx);
 
@@ -4747,9 +4736,12 @@ static void vmx_vcpu_reset(struct kvm_vcpu *vcpu)
        vmcs_write32(GUEST_LDTR_LIMIT, 0xffff);
        vmcs_write32(GUEST_LDTR_AR_BYTES, 0x00082);
 
-       vmcs_write32(GUEST_SYSENTER_CS, 0);
-       vmcs_writel(GUEST_SYSENTER_ESP, 0);
-       vmcs_writel(GUEST_SYSENTER_EIP, 0);
+       if (!init_event) {
+               vmcs_write32(GUEST_SYSENTER_CS, 0);
+               vmcs_writel(GUEST_SYSENTER_ESP, 0);
+               vmcs_writel(GUEST_SYSENTER_EIP, 0);
+               vmcs_write64(GUEST_IA32_DEBUGCTL, 0);
+       }
 
        vmcs_writel(GUEST_RFLAGS, 0x02);
        kvm_rip_write(vcpu, 0xfff0);
@@ -4764,18 +4756,15 @@ static void vmx_vcpu_reset(struct kvm_vcpu *vcpu)
        vmcs_write32(GUEST_INTERRUPTIBILITY_INFO, 0);
        vmcs_write32(GUEST_PENDING_DBG_EXCEPTIONS, 0);
 
-       /* Special registers */
-       vmcs_write64(GUEST_IA32_DEBUGCTL, 0);
-
        setup_msrs(vmx);
 
        vmcs_write32(VM_ENTRY_INTR_INFO_FIELD, 0);  /* 22.2.1 */
 
-       if (cpu_has_vmx_tpr_shadow()) {
+       if (cpu_has_vmx_tpr_shadow() && !init_event) {
                vmcs_write64(VIRTUAL_APIC_PAGE_ADDR, 0);
-               if (vm_need_tpr_shadow(vmx->vcpu.kvm))
+               if (vm_need_tpr_shadow(vcpu->kvm))
                        vmcs_write64(VIRTUAL_APIC_PAGE_ADDR,
-                                    __pa(vmx->vcpu.arch.apic->regs));
+                                    __pa(vcpu->arch.apic->regs));
                vmcs_write32(TPR_THRESHOLD, 0);
        }
 
@@ -4787,12 +4776,14 @@ static void vmx_vcpu_reset(struct kvm_vcpu *vcpu)
        if (vmx->vpid != 0)
                vmcs_write16(VIRTUAL_PROCESSOR_ID, vmx->vpid);
 
-       vmx->vcpu.arch.cr0 = X86_CR0_NW | X86_CR0_CD | X86_CR0_ET;
-       vmx_set_cr0(&vmx->vcpu, kvm_read_cr0(vcpu)); /* enter rmode */
-       vmx_set_cr4(&vmx->vcpu, 0);
-       vmx_set_efer(&vmx->vcpu, 0);
-       vmx_fpu_activate(&vmx->vcpu);
-       update_exception_bitmap(&vmx->vcpu);
+       cr0 = X86_CR0_NW | X86_CR0_CD | X86_CR0_ET;
+       vmx_set_cr0(vcpu, cr0); /* enter rmode */
+       vmx->vcpu.arch.cr0 = cr0;
+       vmx_set_cr4(vcpu, 0);
+       if (!init_event)
+               vmx_set_efer(vcpu, 0);
+       vmx_fpu_activate(vcpu);
+       update_exception_bitmap(vcpu);
 
        vpid_sync_context(vmx);
 }
@@ -4965,7 +4956,7 @@ static int vmx_set_tss_addr(struct kvm *kvm, unsigned int addr)
                .flags = 0,
        };
 
-       ret = kvm_set_memory_region(kvm, &tss_mem);
+       ret = x86_set_memory_region(kvm, &tss_mem);
        if (ret)
                return ret;
        kvm->arch.tss_addr = addr;
@@ -5475,19 +5466,21 @@ static int handle_cpuid(struct kvm_vcpu *vcpu)
 static int handle_rdmsr(struct kvm_vcpu *vcpu)
 {
        u32 ecx = vcpu->arch.regs[VCPU_REGS_RCX];
-       u64 data;
+       struct msr_data msr_info;
 
-       if (vmx_get_msr(vcpu, ecx, &data)) {
+       msr_info.index = ecx;
+       msr_info.host_initiated = false;
+       if (vmx_get_msr(vcpu, &msr_info)) {
                trace_kvm_msr_read_ex(ecx);
                kvm_inject_gp(vcpu, 0);
                return 1;
        }
 
-       trace_kvm_msr_read(ecx, data);
+       trace_kvm_msr_read(ecx, msr_info.data);
 
        /* FIXME: handling of bits 32:63 of rax, rdx */
-       vcpu->arch.regs[VCPU_REGS_RAX] = data & -1u;
-       vcpu->arch.regs[VCPU_REGS_RDX] = (data >> 32) & -1u;
+       vcpu->arch.regs[VCPU_REGS_RAX] = msr_info.data & -1u;
+       vcpu->arch.regs[VCPU_REGS_RDX] = (msr_info.data >> 32) & -1u;
        skip_emulated_instruction(vcpu);
        return 1;
 }
@@ -5710,9 +5703,6 @@ static int handle_task_switch(struct kvm_vcpu *vcpu)
                return 0;
        }
 
-       /* clear all local breakpoint enable flags */
-       vmcs_writel(GUEST_DR7, vmcs_readl(GUEST_DR7) & ~0x155);
-
        /*
         * TODO: What about debug traps on tss switch?
         *       Are we supposed to inject them and update dr6?
@@ -7333,7 +7323,7 @@ static bool nested_vmx_exit_handled_io(struct kvm_vcpu *vcpu,
                bitmap += (port & 0x7fff) / 8;
 
                if (last_bitmap != bitmap)
-                       if (kvm_read_guest(vcpu->kvm, bitmap, &b, 1))
+                       if (kvm_vcpu_read_guest(vcpu, bitmap, &b, 1))
                                return true;
                if (b & (1 << (port & 7)))
                        return true;
@@ -7377,7 +7367,7 @@ static bool nested_vmx_exit_handled_msr(struct kvm_vcpu *vcpu,
        /* Then read the msr_index'th bit from this bitmap: */
        if (msr_index < 1024*8) {
                unsigned char b;
-               if (kvm_read_guest(vcpu->kvm, bitmap + msr_index/8, &b, 1))
+               if (kvm_vcpu_read_guest(vcpu, bitmap + msr_index/8, &b, 1))
                        return true;
                return 1 & (b >> (msr_index & 7));
        } else
@@ -7642,9 +7632,9 @@ static void vmx_disable_pml(struct vcpu_vmx *vmx)
        vmcs_write32(SECONDARY_VM_EXEC_CONTROL, exec_control);
 }
 
-static void vmx_flush_pml_buffer(struct vcpu_vmx *vmx)
+static void vmx_flush_pml_buffer(struct kvm_vcpu *vcpu)
 {
-       struct kvm *kvm = vmx->vcpu.kvm;
+       struct vcpu_vmx *vmx = to_vmx(vcpu);
        u64 *pml_buf;
        u16 pml_idx;
 
@@ -7666,7 +7656,7 @@ static void vmx_flush_pml_buffer(struct vcpu_vmx *vmx)
 
                gpa = pml_buf[pml_idx];
                WARN_ON(gpa & (PAGE_SIZE - 1));
-               mark_page_dirty(kvm, gpa >> PAGE_SHIFT);
+               kvm_vcpu_mark_page_dirty(vcpu, gpa >> PAGE_SHIFT);
        }
 
        /* reset PML index */
@@ -7691,6 +7681,158 @@ static void kvm_flush_pml_buffers(struct kvm *kvm)
                kvm_vcpu_kick(vcpu);
 }
 
+static void vmx_dump_sel(char *name, uint32_t sel)
+{
+       pr_err("%s sel=0x%04x, attr=0x%05x, limit=0x%08x, base=0x%016lx\n",
+              name, vmcs_read32(sel),
+              vmcs_read32(sel + GUEST_ES_AR_BYTES - GUEST_ES_SELECTOR),
+              vmcs_read32(sel + GUEST_ES_LIMIT - GUEST_ES_SELECTOR),
+              vmcs_readl(sel + GUEST_ES_BASE - GUEST_ES_SELECTOR));
+}
+
+static void vmx_dump_dtsel(char *name, uint32_t limit)
+{
+       pr_err("%s                           limit=0x%08x, base=0x%016lx\n",
+              name, vmcs_read32(limit),
+              vmcs_readl(limit + GUEST_GDTR_BASE - GUEST_GDTR_LIMIT));
+}
+
+static void dump_vmcs(void)
+{
+       u32 vmentry_ctl = vmcs_read32(VM_ENTRY_CONTROLS);
+       u32 vmexit_ctl = vmcs_read32(VM_EXIT_CONTROLS);
+       u32 cpu_based_exec_ctrl = vmcs_read32(CPU_BASED_VM_EXEC_CONTROL);
+       u32 pin_based_exec_ctrl = vmcs_read32(PIN_BASED_VM_EXEC_CONTROL);
+       u32 secondary_exec_control = 0;
+       unsigned long cr4 = vmcs_readl(GUEST_CR4);
+       u64 efer = vmcs_readl(GUEST_IA32_EFER);
+       int i, n;
+
+       if (cpu_has_secondary_exec_ctrls())
+               secondary_exec_control = vmcs_read32(SECONDARY_VM_EXEC_CONTROL);
+
+       pr_err("*** Guest State ***\n");
+       pr_err("CR0: actual=0x%016lx, shadow=0x%016lx, gh_mask=%016lx\n",
+              vmcs_readl(GUEST_CR0), vmcs_readl(CR0_READ_SHADOW),
+              vmcs_readl(CR0_GUEST_HOST_MASK));
+       pr_err("CR4: actual=0x%016lx, shadow=0x%016lx, gh_mask=%016lx\n",
+              cr4, vmcs_readl(CR4_READ_SHADOW), vmcs_readl(CR4_GUEST_HOST_MASK));
+       pr_err("CR3 = 0x%016lx\n", vmcs_readl(GUEST_CR3));
+       if ((secondary_exec_control & SECONDARY_EXEC_ENABLE_EPT) &&
+           (cr4 & X86_CR4_PAE) && !(efer & EFER_LMA))
+       {
+               pr_err("PDPTR0 = 0x%016lx  PDPTR1 = 0x%016lx\n",
+                      vmcs_readl(GUEST_PDPTR0), vmcs_readl(GUEST_PDPTR1));
+               pr_err("PDPTR2 = 0x%016lx  PDPTR3 = 0x%016lx\n",
+                      vmcs_readl(GUEST_PDPTR2), vmcs_readl(GUEST_PDPTR3));
+       }
+       pr_err("RSP = 0x%016lx  RIP = 0x%016lx\n",
+              vmcs_readl(GUEST_RSP), vmcs_readl(GUEST_RIP));
+       pr_err("RFLAGS=0x%08lx         DR7 = 0x%016lx\n",
+              vmcs_readl(GUEST_RFLAGS), vmcs_readl(GUEST_DR7));
+       pr_err("Sysenter RSP=%016lx CS:RIP=%04x:%016lx\n",
+              vmcs_readl(GUEST_SYSENTER_ESP),
+              vmcs_read32(GUEST_SYSENTER_CS), vmcs_readl(GUEST_SYSENTER_EIP));
+       vmx_dump_sel("CS:  ", GUEST_CS_SELECTOR);
+       vmx_dump_sel("DS:  ", GUEST_DS_SELECTOR);
+       vmx_dump_sel("SS:  ", GUEST_SS_SELECTOR);
+       vmx_dump_sel("ES:  ", GUEST_ES_SELECTOR);
+       vmx_dump_sel("FS:  ", GUEST_FS_SELECTOR);
+       vmx_dump_sel("GS:  ", GUEST_GS_SELECTOR);
+       vmx_dump_dtsel("GDTR:", GUEST_GDTR_LIMIT);
+       vmx_dump_sel("LDTR:", GUEST_LDTR_SELECTOR);
+       vmx_dump_dtsel("IDTR:", GUEST_IDTR_LIMIT);
+       vmx_dump_sel("TR:  ", GUEST_TR_SELECTOR);
+       if ((vmexit_ctl & (VM_EXIT_SAVE_IA32_PAT | VM_EXIT_SAVE_IA32_EFER)) ||
+           (vmentry_ctl & (VM_ENTRY_LOAD_IA32_PAT | VM_ENTRY_LOAD_IA32_EFER)))
+               pr_err("EFER =     0x%016llx  PAT = 0x%016lx\n",
+                      efer, vmcs_readl(GUEST_IA32_PAT));
+       pr_err("DebugCtl = 0x%016lx  DebugExceptions = 0x%016lx\n",
+              vmcs_readl(GUEST_IA32_DEBUGCTL),
+              vmcs_readl(GUEST_PENDING_DBG_EXCEPTIONS));
+       if (vmentry_ctl & VM_ENTRY_LOAD_IA32_PERF_GLOBAL_CTRL)
+               pr_err("PerfGlobCtl = 0x%016lx\n",
+                      vmcs_readl(GUEST_IA32_PERF_GLOBAL_CTRL));
+       if (vmentry_ctl & VM_ENTRY_LOAD_BNDCFGS)
+               pr_err("BndCfgS = 0x%016lx\n", vmcs_readl(GUEST_BNDCFGS));
+       pr_err("Interruptibility = %08x  ActivityState = %08x\n",
+              vmcs_read32(GUEST_INTERRUPTIBILITY_INFO),
+              vmcs_read32(GUEST_ACTIVITY_STATE));
+       if (secondary_exec_control & SECONDARY_EXEC_VIRTUAL_INTR_DELIVERY)
+               pr_err("InterruptStatus = %04x\n",
+                      vmcs_read16(GUEST_INTR_STATUS));
+
+       pr_err("*** Host State ***\n");
+       pr_err("RIP = 0x%016lx  RSP = 0x%016lx\n",
+              vmcs_readl(HOST_RIP), vmcs_readl(HOST_RSP));
+       pr_err("CS=%04x SS=%04x DS=%04x ES=%04x FS=%04x GS=%04x TR=%04x\n",
+              vmcs_read16(HOST_CS_SELECTOR), vmcs_read16(HOST_SS_SELECTOR),
+              vmcs_read16(HOST_DS_SELECTOR), vmcs_read16(HOST_ES_SELECTOR),
+              vmcs_read16(HOST_FS_SELECTOR), vmcs_read16(HOST_GS_SELECTOR),
+              vmcs_read16(HOST_TR_SELECTOR));
+       pr_err("FSBase=%016lx GSBase=%016lx TRBase=%016lx\n",
+              vmcs_readl(HOST_FS_BASE), vmcs_readl(HOST_GS_BASE),
+              vmcs_readl(HOST_TR_BASE));
+       pr_err("GDTBase=%016lx IDTBase=%016lx\n",
+              vmcs_readl(HOST_GDTR_BASE), vmcs_readl(HOST_IDTR_BASE));
+       pr_err("CR0=%016lx CR3=%016lx CR4=%016lx\n",
+              vmcs_readl(HOST_CR0), vmcs_readl(HOST_CR3),
+              vmcs_readl(HOST_CR4));
+       pr_err("Sysenter RSP=%016lx CS:RIP=%04x:%016lx\n",
+              vmcs_readl(HOST_IA32_SYSENTER_ESP),
+              vmcs_read32(HOST_IA32_SYSENTER_CS),
+              vmcs_readl(HOST_IA32_SYSENTER_EIP));
+       if (vmexit_ctl & (VM_EXIT_LOAD_IA32_PAT | VM_EXIT_LOAD_IA32_EFER))
+               pr_err("EFER = 0x%016lx  PAT = 0x%016lx\n",
+                      vmcs_readl(HOST_IA32_EFER), vmcs_readl(HOST_IA32_PAT));
+       if (vmexit_ctl & VM_EXIT_LOAD_IA32_PERF_GLOBAL_CTRL)
+               pr_err("PerfGlobCtl = 0x%016lx\n",
+                      vmcs_readl(HOST_IA32_PERF_GLOBAL_CTRL));
+
+       pr_err("*** Control State ***\n");
+       pr_err("PinBased=%08x CPUBased=%08x SecondaryExec=%08x\n",
+              pin_based_exec_ctrl, cpu_based_exec_ctrl, secondary_exec_control);
+       pr_err("EntryControls=%08x ExitControls=%08x\n", vmentry_ctl, vmexit_ctl);
+       pr_err("ExceptionBitmap=%08x PFECmask=%08x PFECmatch=%08x\n",
+              vmcs_read32(EXCEPTION_BITMAP),
+              vmcs_read32(PAGE_FAULT_ERROR_CODE_MASK),
+              vmcs_read32(PAGE_FAULT_ERROR_CODE_MATCH));
+       pr_err("VMEntry: intr_info=%08x errcode=%08x ilen=%08x\n",
+              vmcs_read32(VM_ENTRY_INTR_INFO_FIELD),
+              vmcs_read32(VM_ENTRY_EXCEPTION_ERROR_CODE),
+              vmcs_read32(VM_ENTRY_INSTRUCTION_LEN));
+       pr_err("VMExit: intr_info=%08x errcode=%08x ilen=%08x\n",
+              vmcs_read32(VM_EXIT_INTR_INFO),
+              vmcs_read32(VM_EXIT_INTR_ERROR_CODE),
+              vmcs_read32(VM_EXIT_INSTRUCTION_LEN));
+       pr_err("        reason=%08x qualification=%016lx\n",
+              vmcs_read32(VM_EXIT_REASON), vmcs_readl(EXIT_QUALIFICATION));
+       pr_err("IDTVectoring: info=%08x errcode=%08x\n",
+              vmcs_read32(IDT_VECTORING_INFO_FIELD),
+              vmcs_read32(IDT_VECTORING_ERROR_CODE));
+       pr_err("TSC Offset = 0x%016lx\n", vmcs_readl(TSC_OFFSET));
+       if (cpu_based_exec_ctrl & CPU_BASED_TPR_SHADOW)
+               pr_err("TPR Threshold = 0x%02x\n", vmcs_read32(TPR_THRESHOLD));
+       if (pin_based_exec_ctrl & PIN_BASED_POSTED_INTR)
+               pr_err("PostedIntrVec = 0x%02x\n", vmcs_read16(POSTED_INTR_NV));
+       if ((secondary_exec_control & SECONDARY_EXEC_ENABLE_EPT))
+               pr_err("EPT pointer = 0x%016lx\n", vmcs_readl(EPT_POINTER));
+       n = vmcs_read32(CR3_TARGET_COUNT);
+       for (i = 0; i + 1 < n; i += 4)
+               pr_err("CR3 target%u=%016lx target%u=%016lx\n",
+                      i, vmcs_readl(CR3_TARGET_VALUE0 + i * 2),
+                      i + 1, vmcs_readl(CR3_TARGET_VALUE0 + i * 2 + 2));
+       if (i < n)
+               pr_err("CR3 target%u=%016lx\n",
+                      i, vmcs_readl(CR3_TARGET_VALUE0 + i * 2));
+       if (secondary_exec_control & SECONDARY_EXEC_PAUSE_LOOP_EXITING)
+               pr_err("PLE Gap=%08x Window=%08x\n",
+                      vmcs_read32(PLE_GAP), vmcs_read32(PLE_WINDOW));
+       if (secondary_exec_control & SECONDARY_EXEC_ENABLE_VPID)
+               pr_err("Virtual processor ID = 0x%04x\n",
+                      vmcs_read16(VIRTUAL_PROCESSOR_ID));
+}
+
 /*
  * The guest has exited.  See if we can fix it or if we need userspace
  * assistance.
@@ -7709,7 +7851,7 @@ static int vmx_handle_exit(struct kvm_vcpu *vcpu)
         * flushed already.
         */
        if (enable_pml)
-               vmx_flush_pml_buffer(vmx);
+               vmx_flush_pml_buffer(vcpu);
 
        /* If guest state is invalid, start emulating */
        if (vmx->emulation_required)
@@ -7723,6 +7865,7 @@ static int vmx_handle_exit(struct kvm_vcpu *vcpu)
        }
 
        if (exit_reason & VMX_EXIT_REASONS_FAILED_VMENTRY) {
+               dump_vmcs();
                vcpu->run->exit_reason = KVM_EXIT_FAIL_ENTRY;
                vcpu->run->fail_entry.hardware_entry_failure_reason
                        = exit_reason;
@@ -7996,6 +8139,11 @@ static void vmx_handle_external_intr(struct kvm_vcpu *vcpu)
                local_irq_enable();
 }
 
+static bool vmx_has_high_real_mode_segbase(void)
+{
+       return enable_unrestricted_guest || emulate_invalid_guest_state;
+}
+
 static bool vmx_mpx_supported(void)
 {
        return (vmcs_config.vmexit_ctrl & VM_EXIT_CLEAR_BNDCFGS) &&
@@ -8480,7 +8628,8 @@ static int get_ept_level(void)
 
 static u64 vmx_get_mt_mask(struct kvm_vcpu *vcpu, gfn_t gfn, bool is_mmio)
 {
-       u64 ret;
+       u8 cache;
+       u64 ipat = 0;
 
        /* For VT-d and EPT combination
         * 1. MMIO: always map as UC
@@ -8493,16 +8642,27 @@ static u64 vmx_get_mt_mask(struct kvm_vcpu *vcpu, gfn_t gfn, bool is_mmio)
         * 3. EPT without VT-d: always map as WB and set IPAT=1 to keep
         *    consistent with host MTRR
         */
-       if (is_mmio)
-               ret = MTRR_TYPE_UNCACHABLE << VMX_EPT_MT_EPTE_SHIFT;
-       else if (kvm_arch_has_noncoherent_dma(vcpu->kvm))
-               ret = kvm_get_guest_memory_type(vcpu, gfn) <<
-                     VMX_EPT_MT_EPTE_SHIFT;
-       else
-               ret = (MTRR_TYPE_WRBACK << VMX_EPT_MT_EPTE_SHIFT)
-                       | VMX_EPT_IPAT_BIT;
+       if (is_mmio) {
+               cache = MTRR_TYPE_UNCACHABLE;
+               goto exit;
+       }
 
-       return ret;
+       if (!kvm_arch_has_noncoherent_dma(vcpu->kvm)) {
+               ipat = VMX_EPT_IPAT_BIT;
+               cache = MTRR_TYPE_WRBACK;
+               goto exit;
+       }
+
+       if (kvm_read_cr0(vcpu) & X86_CR0_CD) {
+               ipat = VMX_EPT_IPAT_BIT;
+               cache = MTRR_TYPE_UNCACHABLE;
+               goto exit;
+       }
+
+       cache = kvm_mtrr_get_guest_memory_type(vcpu, gfn);
+
+exit:
+       return (cache << VMX_EPT_MT_EPTE_SHIFT) | ipat;
 }
 
 static int vmx_get_lpage_level(void)
@@ -8924,7 +9084,7 @@ static int nested_vmx_msr_check_common(struct kvm_vcpu *vcpu,
                                       struct vmx_msr_entry *e)
 {
        /* x2APIC MSR accesses are not allowed */
-       if (apic_x2apic_mode(vcpu->arch.apic) && e->index >> 8 == 0x8)
+       if (vcpu->arch.apic_base & X2APIC_ENABLE && e->index >> 8 == 0x8)
                return -EINVAL;
        if (e->index == MSR_IA32_UCODE_WRITE || /* SDM Table 35-2 */
            e->index == MSR_IA32_UCODE_REV)
@@ -8966,8 +9126,8 @@ static u32 nested_vmx_load_msr(struct kvm_vcpu *vcpu, u64 gpa, u32 count)
 
        msr.host_initiated = false;
        for (i = 0; i < count; i++) {
-               if (kvm_read_guest(vcpu->kvm, gpa + i * sizeof(e),
-                                  &e, sizeof(e))) {
+               if (kvm_vcpu_read_guest(vcpu, gpa + i * sizeof(e),
+                                       &e, sizeof(e))) {
                        pr_warn_ratelimited(
                                "%s cannot read MSR entry (%u, 0x%08llx)\n",
                                __func__, i, gpa + i * sizeof(e));
@@ -8999,9 +9159,10 @@ static int nested_vmx_store_msr(struct kvm_vcpu *vcpu, u64 gpa, u32 count)
        struct vmx_msr_entry e;
 
        for (i = 0; i < count; i++) {
-               if (kvm_read_guest(vcpu->kvm,
-                                  gpa + i * sizeof(e),
-                                  &e, 2 * sizeof(u32))) {
+               struct msr_data msr_info;
+               if (kvm_vcpu_read_guest(vcpu,
+                                       gpa + i * sizeof(e),
+                                       &e, 2 * sizeof(u32))) {
                        pr_warn_ratelimited(
                                "%s cannot read MSR entry (%u, 0x%08llx)\n",
                                __func__, i, gpa + i * sizeof(e));
@@ -9013,19 +9174,21 @@ static int nested_vmx_store_msr(struct kvm_vcpu *vcpu, u64 gpa, u32 count)
                                __func__, i, e.index, e.reserved);
                        return -EINVAL;
                }
-               if (kvm_get_msr(vcpu, e.index, &e.value)) {
+               msr_info.host_initiated = false;
+               msr_info.index = e.index;
+               if (kvm_get_msr(vcpu, &msr_info)) {
                        pr_warn_ratelimited(
                                "%s cannot read MSR (%u, 0x%x)\n",
                                __func__, i, e.index);
                        return -EINVAL;
                }
-               if (kvm_write_guest(vcpu->kvm,
-                                   gpa + i * sizeof(e) +
-                                       offsetof(struct vmx_msr_entry, value),
-                                   &e.value, sizeof(e.value))) {
+               if (kvm_vcpu_write_guest(vcpu,
+                                        gpa + i * sizeof(e) +
+                                            offsetof(struct vmx_msr_entry, value),
+                                        &msr_info.data, sizeof(msr_info.data))) {
                        pr_warn_ratelimited(
                                "%s cannot write MSR (%u, 0x%x, 0x%llx)\n",
-                               __func__, i, e.index, e.value);
+                               __func__, i, e.index, msr_info.data);
                        return -EINVAL;
                }
        }
@@ -10150,6 +10313,7 @@ static struct kvm_x86_ops vmx_x86_ops = {
        .hardware_enable = hardware_enable,
        .hardware_disable = hardware_disable,
        .cpu_has_accelerated_tpr = report_flexpriority,
+       .cpu_has_high_real_mode_segbase = vmx_has_high_real_mode_segbase,
 
        .vcpu_create = vmx_create_vcpu,
        .vcpu_free = vmx_free_vcpu,
@@ -10255,6 +10419,8 @@ static struct kvm_x86_ops vmx_x86_ops = {
        .slot_disable_log_dirty = vmx_slot_disable_log_dirty,
        .flush_log_dirty = vmx_flush_log_dirty,
        .enable_log_dirty_pt_masked = vmx_enable_log_dirty_pt_masked,
+
+       .pmu_ops = &intel_pmu_ops,
 };
 
 static int __init vmx_init(void)
index ea306adbbc13603591d46d3f062cc20cf1bc37d1..ac165c2fb8e54a307cb3f70a5e990635f95978b2 100644 (file)
@@ -28,6 +28,7 @@
 #include "x86.h"
 #include "cpuid.h"
 #include "assigned-dev.h"
+#include "pmu.h"
 
 #include <linux/clocksource.h>
 #include <linux/interrupt.h>
 #include <asm/debugreg.h>
 #include <asm/msr.h>
 #include <asm/desc.h>
-#include <asm/mtrr.h>
 #include <asm/mce.h>
-#include <asm/i387.h>
-#include <asm/fpu-internal.h> /* Ugh! */
-#include <asm/xcr.h>
+#include <linux/kernel_stat.h>
+#include <asm/fpu/internal.h> /* Ugh! */
 #include <asm/pvclock.h>
 #include <asm/div64.h>
 
@@ -99,6 +98,9 @@ module_param(ignore_msrs, bool, S_IRUGO | S_IWUSR);
 unsigned int min_timer_period_us = 500;
 module_param(min_timer_period_us, uint, S_IRUGO | S_IWUSR);
 
+static bool __read_mostly kvmclock_periodic_sync = true;
+module_param(kvmclock_periodic_sync, bool, S_IRUGO);
+
 bool kvm_has_tsc_control;
 EXPORT_SYMBOL_GPL(kvm_has_tsc_control);
 u32  kvm_max_guest_tsc_khz;
@@ -475,7 +477,7 @@ EXPORT_SYMBOL_GPL(kvm_require_dr);
 
 /*
  * This function will be used to read from the physical memory of the currently
- * running guest. The difference to kvm_read_guest_page is that this function
+ * running guest. The difference to kvm_vcpu_read_guest_page is that this function
  * can read from guest physical or from the guest's guest physical memory.
  */
 int kvm_read_guest_page_mmu(struct kvm_vcpu *vcpu, struct kvm_mmu *mmu,
@@ -493,7 +495,7 @@ int kvm_read_guest_page_mmu(struct kvm_vcpu *vcpu, struct kvm_mmu *mmu,
 
        real_gfn = gpa_to_gfn(real_gfn);
 
-       return kvm_read_guest_page(vcpu->kvm, real_gfn, data, offset, len);
+       return kvm_vcpu_read_guest_page(vcpu, real_gfn, data, offset, len);
 }
 EXPORT_SYMBOL_GPL(kvm_read_guest_page_mmu);
 
@@ -572,8 +574,7 @@ out:
 int kvm_set_cr0(struct kvm_vcpu *vcpu, unsigned long cr0)
 {
        unsigned long old_cr0 = kvm_read_cr0(vcpu);
-       unsigned long update_bits = X86_CR0_PG | X86_CR0_WP |
-                                   X86_CR0_CD | X86_CR0_NW;
+       unsigned long update_bits = X86_CR0_PG | X86_CR0_WP;
 
        cr0 |= X86_CR0_ET;
 
@@ -619,6 +620,10 @@ int kvm_set_cr0(struct kvm_vcpu *vcpu, unsigned long cr0)
 
        if ((cr0 ^ old_cr0) & update_bits)
                kvm_mmu_reset_context(vcpu);
+
+       if ((cr0 ^ old_cr0) & X86_CR0_CD)
+               kvm_zap_gfn_range(vcpu->kvm, 0, ~0ULL);
+
        return 0;
 }
 EXPORT_SYMBOL_GPL(kvm_set_cr0);
@@ -908,7 +913,7 @@ bool kvm_rdpmc(struct kvm_vcpu *vcpu)
        u64 data;
        int err;
 
-       err = kvm_pmu_read_pmc(vcpu, ecx, &data);
+       err = kvm_pmu_rdpmc(vcpu, ecx, &data);
        if (err)
                return err;
        kvm_register_write(vcpu, VCPU_REGS_RAX, (u32)data);
@@ -923,17 +928,11 @@ EXPORT_SYMBOL_GPL(kvm_rdpmc);
  *
  * This list is modified at module load time to reflect the
  * capabilities of the host cpu. This capabilities test skips MSRs that are
- * kvm-specific. Those are put in the beginning of the list.
+ * kvm-specific. Those are put in emulated_msrs; filtering of emulated_msrs
+ * may depend on host virtualization features rather than host cpu features.
  */
 
-#define KVM_SAVE_MSRS_BEGIN    12
 static u32 msrs_to_save[] = {
-       MSR_KVM_SYSTEM_TIME, MSR_KVM_WALL_CLOCK,
-       MSR_KVM_SYSTEM_TIME_NEW, MSR_KVM_WALL_CLOCK_NEW,
-       HV_X64_MSR_GUEST_OS_ID, HV_X64_MSR_HYPERCALL,
-       HV_X64_MSR_TIME_REF_COUNT, HV_X64_MSR_REFERENCE_TSC,
-       HV_X64_MSR_APIC_ASSIST_PAGE, MSR_KVM_ASYNC_PF_EN, MSR_KVM_STEAL_TIME,
-       MSR_KVM_PV_EOI_EN,
        MSR_IA32_SYSENTER_CS, MSR_IA32_SYSENTER_ESP, MSR_IA32_SYSENTER_EIP,
        MSR_STAR,
 #ifdef CONFIG_X86_64
@@ -945,14 +944,24 @@ static u32 msrs_to_save[] = {
 
 static unsigned num_msrs_to_save;
 
-static const u32 emulated_msrs[] = {
+static u32 emulated_msrs[] = {
+       MSR_KVM_SYSTEM_TIME, MSR_KVM_WALL_CLOCK,
+       MSR_KVM_SYSTEM_TIME_NEW, MSR_KVM_WALL_CLOCK_NEW,
+       HV_X64_MSR_GUEST_OS_ID, HV_X64_MSR_HYPERCALL,
+       HV_X64_MSR_TIME_REF_COUNT, HV_X64_MSR_REFERENCE_TSC,
+       HV_X64_MSR_APIC_ASSIST_PAGE, MSR_KVM_ASYNC_PF_EN, MSR_KVM_STEAL_TIME,
+       MSR_KVM_PV_EOI_EN,
+
        MSR_IA32_TSC_ADJUST,
        MSR_IA32_TSCDEADLINE,
        MSR_IA32_MISC_ENABLE,
        MSR_IA32_MCG_STATUS,
        MSR_IA32_MCG_CTL,
+       MSR_IA32_SMBASE,
 };
 
+static unsigned num_emulated_msrs;
+
 bool kvm_valid_efer(struct kvm_vcpu *vcpu, u64 efer)
 {
        if (efer & efer_reserved_bits)
@@ -1046,6 +1055,21 @@ EXPORT_SYMBOL_GPL(kvm_set_msr);
 /*
  * Adapt set_msr() to msr_io()'s calling convention
  */
+static int do_get_msr(struct kvm_vcpu *vcpu, unsigned index, u64 *data)
+{
+       struct msr_data msr;
+       int r;
+
+       msr.index = index;
+       msr.host_initiated = true;
+       r = kvm_get_msr(vcpu, &msr);
+       if (r)
+               return r;
+
+       *data = msr.data;
+       return 0;
+}
+
 static int do_set_msr(struct kvm_vcpu *vcpu, unsigned index, u64 *data)
 {
        struct msr_data msr;
@@ -1698,6 +1722,8 @@ static int kvm_guest_time_update(struct kvm_vcpu *v)
                vcpu->pvclock_set_guest_stopped_request = false;
        }
 
+       pvclock_flags |= PVCLOCK_COUNTS_FROM_ZERO;
+
        /* If the host uses TSC clocksource, then it is stable */
        if (use_master_clock)
                pvclock_flags |= PVCLOCK_TSC_STABLE_BIT;
@@ -1768,127 +1794,14 @@ static void kvmclock_sync_fn(struct work_struct *work)
                                           kvmclock_sync_work);
        struct kvm *kvm = container_of(ka, struct kvm, arch);
 
+       if (!kvmclock_periodic_sync)
+               return;
+
        schedule_delayed_work(&kvm->arch.kvmclock_update_work, 0);
        schedule_delayed_work(&kvm->arch.kvmclock_sync_work,
                                        KVMCLOCK_SYNC_PERIOD);
 }
 
-static bool msr_mtrr_valid(unsigned msr)
-{
-       switch (msr) {
-       case 0x200 ... 0x200 + 2 * KVM_NR_VAR_MTRR - 1:
-       case MSR_MTRRfix64K_00000:
-       case MSR_MTRRfix16K_80000:
-       case MSR_MTRRfix16K_A0000:
-       case MSR_MTRRfix4K_C0000:
-       case MSR_MTRRfix4K_C8000:
-       case MSR_MTRRfix4K_D0000:
-       case MSR_MTRRfix4K_D8000:
-       case MSR_MTRRfix4K_E0000:
-       case MSR_MTRRfix4K_E8000:
-       case MSR_MTRRfix4K_F0000:
-       case MSR_MTRRfix4K_F8000:
-       case MSR_MTRRdefType:
-       case MSR_IA32_CR_PAT:
-               return true;
-       case 0x2f8:
-               return true;
-       }
-       return false;
-}
-
-static bool valid_pat_type(unsigned t)
-{
-       return t < 8 && (1 << t) & 0xf3; /* 0, 1, 4, 5, 6, 7 */
-}
-
-static bool valid_mtrr_type(unsigned t)
-{
-       return t < 8 && (1 << t) & 0x73; /* 0, 1, 4, 5, 6 */
-}
-
-bool kvm_mtrr_valid(struct kvm_vcpu *vcpu, u32 msr, u64 data)
-{
-       int i;
-       u64 mask;
-
-       if (!msr_mtrr_valid(msr))
-               return false;
-
-       if (msr == MSR_IA32_CR_PAT) {
-               for (i = 0; i < 8; i++)
-                       if (!valid_pat_type((data >> (i * 8)) & 0xff))
-                               return false;
-               return true;
-       } else if (msr == MSR_MTRRdefType) {
-               if (data & ~0xcff)
-                       return false;
-               return valid_mtrr_type(data & 0xff);
-       } else if (msr >= MSR_MTRRfix64K_00000 && msr <= MSR_MTRRfix4K_F8000) {
-               for (i = 0; i < 8 ; i++)
-                       if (!valid_mtrr_type((data >> (i * 8)) & 0xff))
-                               return false;
-               return true;
-       }
-
-       /* variable MTRRs */
-       WARN_ON(!(msr >= 0x200 && msr < 0x200 + 2 * KVM_NR_VAR_MTRR));
-
-       mask = (~0ULL) << cpuid_maxphyaddr(vcpu);
-       if ((msr & 1) == 0) {
-               /* MTRR base */
-               if (!valid_mtrr_type(data & 0xff))
-                       return false;
-               mask |= 0xf00;
-       } else
-               /* MTRR mask */
-               mask |= 0x7ff;
-       if (data & mask) {
-               kvm_inject_gp(vcpu, 0);
-               return false;
-       }
-
-       return true;
-}
-EXPORT_SYMBOL_GPL(kvm_mtrr_valid);
-
-static int set_msr_mtrr(struct kvm_vcpu *vcpu, u32 msr, u64 data)
-{
-       u64 *p = (u64 *)&vcpu->arch.mtrr_state.fixed_ranges;
-
-       if (!kvm_mtrr_valid(vcpu, msr, data))
-               return 1;
-
-       if (msr == MSR_MTRRdefType) {
-               vcpu->arch.mtrr_state.def_type = data;
-               vcpu->arch.mtrr_state.enabled = (data & 0xc00) >> 10;
-       } else if (msr == MSR_MTRRfix64K_00000)
-               p[0] = data;
-       else if (msr == MSR_MTRRfix16K_80000 || msr == MSR_MTRRfix16K_A0000)
-               p[1 + msr - MSR_MTRRfix16K_80000] = data;
-       else if (msr >= MSR_MTRRfix4K_C0000 && msr <= MSR_MTRRfix4K_F8000)
-               p[3 + msr - MSR_MTRRfix4K_C0000] = data;
-       else if (msr == MSR_IA32_CR_PAT)
-               vcpu->arch.pat = data;
-       else {  /* Variable MTRRs */
-               int idx, is_mtrr_mask;
-               u64 *pt;
-
-               idx = (msr - 0x200) / 2;
-               is_mtrr_mask = msr - 0x200 - 2 * idx;
-               if (!is_mtrr_mask)
-                       pt =
-                         (u64 *)&vcpu->arch.mtrr_state.var_ranges[idx].base_lo;
-               else
-                       pt =
-                         (u64 *)&vcpu->arch.mtrr_state.var_ranges[idx].mask_lo;
-               *pt = data;
-       }
-
-       kvm_mmu_reset_context(vcpu);
-       return 0;
-}
-
 static int set_msr_mce(struct kvm_vcpu *vcpu, u32 msr, u64 data)
 {
        u64 mcg_cap = vcpu->arch.mcg_cap;
@@ -1947,7 +1860,7 @@ static int xen_hvm_config(struct kvm_vcpu *vcpu, u64 data)
                r = PTR_ERR(page);
                goto out;
        }
-       if (kvm_write_guest(kvm, page_addr, page, PAGE_SIZE))
+       if (kvm_vcpu_write_guest(vcpu, page_addr, page, PAGE_SIZE))
                goto out_free;
        r = 0;
 out_free:
@@ -2047,13 +1960,13 @@ static int set_msr_hyperv(struct kvm_vcpu *vcpu, u32 msr, u64 data)
                        break;
                }
                gfn = data >> HV_X64_MSR_APIC_ASSIST_PAGE_ADDRESS_SHIFT;
-               addr = gfn_to_hva(vcpu->kvm, gfn);
+               addr = kvm_vcpu_gfn_to_hva(vcpu, gfn);
                if (kvm_is_error_hva(addr))
                        return 1;
                if (__clear_user((void __user *)addr, PAGE_SIZE))
                        return 1;
                vcpu->arch.hv_vapic = data;
-               mark_page_dirty(vcpu->kvm, gfn);
+               kvm_vcpu_mark_page_dirty(vcpu, gfn);
                if (kvm_lapic_enable_pv_eoi(vcpu, gfn_to_gpa(gfn) | KVM_MSR_ENABLED))
                        return 1;
                break;
@@ -2180,7 +2093,7 @@ int kvm_set_msr_common(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
                            __func__, data);
                break;
        case 0x200 ... 0x2ff:
-               return set_msr_mtrr(vcpu, msr, data);
+               return kvm_mtrr_set_msr(vcpu, msr, data);
        case MSR_IA32_APICBASE:
                return kvm_set_apic_base(vcpu, msr_info);
        case APIC_BASE_MSR ... APIC_BASE_MSR + 0x3ff:
@@ -2200,6 +2113,11 @@ int kvm_set_msr_common(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
        case MSR_IA32_MISC_ENABLE:
                vcpu->arch.ia32_misc_enable_msr = data;
                break;
+       case MSR_IA32_SMBASE:
+               if (!msr_info->host_initiated)
+                       return 1;
+               vcpu->arch.smbase = data;
+               break;
        case MSR_KVM_WALL_CLOCK_NEW:
        case MSR_KVM_WALL_CLOCK:
                vcpu->kvm->arch.wall_clock = data;
@@ -2220,6 +2138,8 @@ int kvm_set_msr_common(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
                                        &vcpu->requests);
 
                        ka->boot_vcpu_runs_old_kvmclock = tmp;
+
+                       ka->kvmclock_offset = -get_kernel_ns();
                }
 
                vcpu->arch.time = data;
@@ -2281,37 +2201,12 @@ int kvm_set_msr_common(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
        case MSR_IA32_MC0_CTL ... MSR_IA32_MCx_CTL(KVM_MAX_MCE_BANKS) - 1:
                return set_msr_mce(vcpu, msr, data);
 
-       /* Performance counters are not protected by a CPUID bit,
-        * so we should check all of them in the generic path for the sake of
-        * cross vendor migration.
-        * Writing a zero into the event select MSRs disables them,
-        * which we perfectly emulate ;-). Any other value should be at least
-        * reported, some guests depend on them.
-        */
-       case MSR_K7_EVNTSEL0:
-       case MSR_K7_EVNTSEL1:
-       case MSR_K7_EVNTSEL2:
-       case MSR_K7_EVNTSEL3:
-               if (data != 0)
-                       vcpu_unimpl(vcpu, "unimplemented perfctr wrmsr: "
-                                   "0x%x data 0x%llx\n", msr, data);
-               break;
-       /* at least RHEL 4 unconditionally writes to the perfctr registers,
-        * so we ignore writes to make it happy.
-        */
-       case MSR_K7_PERFCTR0:
-       case MSR_K7_PERFCTR1:
-       case MSR_K7_PERFCTR2:
-       case MSR_K7_PERFCTR3:
-               vcpu_unimpl(vcpu, "unimplemented perfctr wrmsr: "
-                           "0x%x data 0x%llx\n", msr, data);
-               break;
-       case MSR_P6_PERFCTR0:
-       case MSR_P6_PERFCTR1:
-               pr = true;
-       case MSR_P6_EVNTSEL0:
-       case MSR_P6_EVNTSEL1:
-               if (kvm_pmu_msr(vcpu, msr))
+       case MSR_K7_PERFCTR0 ... MSR_K7_PERFCTR3:
+       case MSR_P6_PERFCTR0 ... MSR_P6_PERFCTR1:
+               pr = true; /* fall through */
+       case MSR_K7_EVNTSEL0 ... MSR_K7_EVNTSEL3:
+       case MSR_P6_EVNTSEL0 ... MSR_P6_EVNTSEL1:
+               if (kvm_pmu_is_valid_msr(vcpu, msr))
                        return kvm_pmu_set_msr(vcpu, msr_info);
 
                if (pr || data != 0)
@@ -2357,7 +2252,7 @@ int kvm_set_msr_common(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
        default:
                if (msr && (msr == vcpu->kvm->arch.xen_hvm_config.msr))
                        return xen_hvm_config(vcpu, data);
-               if (kvm_pmu_msr(vcpu, msr))
+               if (kvm_pmu_is_valid_msr(vcpu, msr))
                        return kvm_pmu_set_msr(vcpu, msr_info);
                if (!ignore_msrs) {
                        vcpu_unimpl(vcpu, "unhandled wrmsr: 0x%x data %llx\n",
@@ -2379,48 +2274,12 @@ EXPORT_SYMBOL_GPL(kvm_set_msr_common);
  * Returns 0 on success, non-0 otherwise.
  * Assumes vcpu_load() was already called.
  */
-int kvm_get_msr(struct kvm_vcpu *vcpu, u32 msr_index, u64 *pdata)
+int kvm_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr)
 {
-       return kvm_x86_ops->get_msr(vcpu, msr_index, pdata);
+       return kvm_x86_ops->get_msr(vcpu, msr);
 }
 EXPORT_SYMBOL_GPL(kvm_get_msr);
 
-static int get_msr_mtrr(struct kvm_vcpu *vcpu, u32 msr, u64 *pdata)
-{
-       u64 *p = (u64 *)&vcpu->arch.mtrr_state.fixed_ranges;
-
-       if (!msr_mtrr_valid(msr))
-               return 1;
-
-       if (msr == MSR_MTRRdefType)
-               *pdata = vcpu->arch.mtrr_state.def_type +
-                        (vcpu->arch.mtrr_state.enabled << 10);
-       else if (msr == MSR_MTRRfix64K_00000)
-               *pdata = p[0];
-       else if (msr == MSR_MTRRfix16K_80000 || msr == MSR_MTRRfix16K_A0000)
-               *pdata = p[1 + msr - MSR_MTRRfix16K_80000];
-       else if (msr >= MSR_MTRRfix4K_C0000 && msr <= MSR_MTRRfix4K_F8000)
-               *pdata = p[3 + msr - MSR_MTRRfix4K_C0000];
-       else if (msr == MSR_IA32_CR_PAT)
-               *pdata = vcpu->arch.pat;
-       else {  /* Variable MTRRs */
-               int idx, is_mtrr_mask;
-               u64 *pt;
-
-               idx = (msr - 0x200) / 2;
-               is_mtrr_mask = msr - 0x200 - 2 * idx;
-               if (!is_mtrr_mask)
-                       pt =
-                         (u64 *)&vcpu->arch.mtrr_state.var_ranges[idx].base_lo;
-               else
-                       pt =
-                         (u64 *)&vcpu->arch.mtrr_state.var_ranges[idx].mask_lo;
-               *pdata = *pt;
-       }
-
-       return 0;
-}
-
 static int get_msr_mce(struct kvm_vcpu *vcpu, u32 msr, u64 *pdata)
 {
        u64 data;
@@ -2518,11 +2377,11 @@ static int get_msr_hyperv(struct kvm_vcpu *vcpu, u32 msr, u64 *pdata)
        return 0;
 }
 
-int kvm_get_msr_common(struct kvm_vcpu *vcpu, u32 msr, u64 *pdata)
+int kvm_get_msr_common(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
 {
        u64 data;
 
-       switch (msr) {
+       switch (msr_info->index) {
        case MSR_IA32_PLATFORM_ID:
        case MSR_IA32_EBL_CR_POWERON:
        case MSR_IA32_DEBUGCTLMSR:
@@ -2533,38 +2392,28 @@ int kvm_get_msr_common(struct kvm_vcpu *vcpu, u32 msr, u64 *pdata)
        case MSR_K8_SYSCFG:
        case MSR_K7_HWCR:
        case MSR_VM_HSAVE_PA:
-       case MSR_K7_EVNTSEL0:
-       case MSR_K7_EVNTSEL1:
-       case MSR_K7_EVNTSEL2:
-       case MSR_K7_EVNTSEL3:
-       case MSR_K7_PERFCTR0:
-       case MSR_K7_PERFCTR1:
-       case MSR_K7_PERFCTR2:
-       case MSR_K7_PERFCTR3:
        case MSR_K8_INT_PENDING_MSG:
        case MSR_AMD64_NB_CFG:
        case MSR_FAM10H_MMIO_CONF_BASE:
        case MSR_AMD64_BU_CFG2:
-               data = 0;
+               msr_info->data = 0;
                break;
-       case MSR_P6_PERFCTR0:
-       case MSR_P6_PERFCTR1:
-       case MSR_P6_EVNTSEL0:
-       case MSR_P6_EVNTSEL1:
-               if (kvm_pmu_msr(vcpu, msr))
-                       return kvm_pmu_get_msr(vcpu, msr, pdata);
-               data = 0;
+       case MSR_K7_EVNTSEL0 ... MSR_K7_EVNTSEL3:
+       case MSR_K7_PERFCTR0 ... MSR_K7_PERFCTR3:
+       case MSR_P6_PERFCTR0 ... MSR_P6_PERFCTR1:
+       case MSR_P6_EVNTSEL0 ... MSR_P6_EVNTSEL1:
+               if (kvm_pmu_is_valid_msr(vcpu, msr_info->index))
+                       return kvm_pmu_get_msr(vcpu, msr_info->index, &msr_info->data);
+               msr_info->data = 0;
                break;
        case MSR_IA32_UCODE_REV:
-               data = 0x100000000ULL;
+               msr_info->data = 0x100000000ULL;
                break;
        case MSR_MTRRcap:
-               data = 0x500 | KVM_NR_VAR_MTRR;
-               break;
        case 0x200 ... 0x2ff:
-               return get_msr_mtrr(vcpu, msr, pdata);
+               return kvm_mtrr_get_msr(vcpu, msr_info->index, &msr_info->data);
        case 0xcd: /* fsb frequency */
-               data = 3;
+               msr_info->data = 3;
                break;
                /*
                 * MSR_EBC_FREQUENCY_ID
@@ -2578,48 +2427,53 @@ int kvm_get_msr_common(struct kvm_vcpu *vcpu, u32 msr, u64 *pdata)
                 * multiplying by zero otherwise.
                 */
        case MSR_EBC_FREQUENCY_ID:
-               data = 1 << 24;
+               msr_info->data = 1 << 24;
                break;
        case MSR_IA32_APICBASE:
-               data = kvm_get_apic_base(vcpu);
+               msr_info->data = kvm_get_apic_base(vcpu);
                break;
        case APIC_BASE_MSR ... APIC_BASE_MSR + 0x3ff:
-               return kvm_x2apic_msr_read(vcpu, msr, pdata);
+               return kvm_x2apic_msr_read(vcpu, msr_info->index, &msr_info->data);
                break;
        case MSR_IA32_TSCDEADLINE:
-               data = kvm_get_lapic_tscdeadline_msr(vcpu);
+               msr_info->data = kvm_get_lapic_tscdeadline_msr(vcpu);
                break;
        case MSR_IA32_TSC_ADJUST:
-               data = (u64)vcpu->arch.ia32_tsc_adjust_msr;
+               msr_info->data = (u64)vcpu->arch.ia32_tsc_adjust_msr;
                break;
        case MSR_IA32_MISC_ENABLE:
-               data = vcpu->arch.ia32_misc_enable_msr;
+               msr_info->data = vcpu->arch.ia32_misc_enable_msr;
+               break;
+       case MSR_IA32_SMBASE:
+               if (!msr_info->host_initiated)
+                       return 1;
+               msr_info->data = vcpu->arch.smbase;
                break;
        case MSR_IA32_PERF_STATUS:
                /* TSC increment by tick */
-               data = 1000ULL;
+               msr_info->data = 1000ULL;
                /* CPU multiplier */
                data |= (((uint64_t)4ULL) << 40);
                break;
        case MSR_EFER:
-               data = vcpu->arch.efer;
+               msr_info->data = vcpu->arch.efer;
                break;
        case MSR_KVM_WALL_CLOCK:
        case MSR_KVM_WALL_CLOCK_NEW:
-               data = vcpu->kvm->arch.wall_clock;
+               msr_info->data = vcpu->kvm->arch.wall_clock;
                break;
        case MSR_KVM_SYSTEM_TIME:
        case MSR_KVM_SYSTEM_TIME_NEW:
-               data = vcpu->arch.time;
+               msr_info->data = vcpu->arch.time;
                break;
        case MSR_KVM_ASYNC_PF_EN:
-               data = vcpu->arch.apf.msr_val;
+               msr_info->data = vcpu->arch.apf.msr_val;
                break;
        case MSR_KVM_STEAL_TIME:
-               data = vcpu->arch.st.msr_val;
+               msr_info->data = vcpu->arch.st.msr_val;
                break;
        case MSR_KVM_PV_EOI_EN:
-               data = vcpu->arch.pv_eoi.msr_val;
+               msr_info->data = vcpu->arch.pv_eoi.msr_val;
                break;
        case MSR_IA32_P5_MC_ADDR:
        case MSR_IA32_P5_MC_TYPE:
@@ -2627,7 +2481,7 @@ int kvm_get_msr_common(struct kvm_vcpu *vcpu, u32 msr, u64 *pdata)
        case MSR_IA32_MCG_CTL:
        case MSR_IA32_MCG_STATUS:
        case MSR_IA32_MC0_CTL ... MSR_IA32_MCx_CTL(KVM_MAX_MCE_BANKS) - 1:
-               return get_msr_mce(vcpu, msr, pdata);
+               return get_msr_mce(vcpu, msr_info->index, &msr_info->data);
        case MSR_K7_CLK_CTL:
                /*
                 * Provide expected ramp-up count for K7. All other
@@ -2638,17 +2492,17 @@ int kvm_get_msr_common(struct kvm_vcpu *vcpu, u32 msr, u64 *pdata)
                 * type 6, model 8 and higher from exploding due to
                 * the rdmsr failing.
                 */
-               data = 0x20000000;
+               msr_info->data = 0x20000000;
                break;
        case HV_X64_MSR_GUEST_OS_ID ... HV_X64_MSR_SINT15:
-               if (kvm_hv_msr_partition_wide(msr)) {
+               if (kvm_hv_msr_partition_wide(msr_info->index)) {
                        int r;
                        mutex_lock(&vcpu->kvm->lock);
-                       r = get_msr_hyperv_pw(vcpu, msr, pdata);
+                       r = get_msr_hyperv_pw(vcpu, msr_info->index, &msr_info->data);
                        mutex_unlock(&vcpu->kvm->lock);
                        return r;
                } else
-                       return get_msr_hyperv(vcpu, msr, pdata);
+                       return get_msr_hyperv(vcpu, msr_info->index, &msr_info->data);
                break;
        case MSR_IA32_BBL_CR_CTL3:
                /* This legacy MSR exists but isn't fully documented in current
@@ -2661,31 +2515,30 @@ int kvm_get_msr_common(struct kvm_vcpu *vcpu, u32 msr, u64 *pdata)
                 * L2 cache control register 3: 64GB range, 256KB size,
                 * enabled, latency 0x1, configured
                 */
-               data = 0xbe702111;
+               msr_info->data = 0xbe702111;
                break;
        case MSR_AMD64_OSVW_ID_LENGTH:
                if (!guest_cpuid_has_osvw(vcpu))
                        return 1;
-               data = vcpu->arch.osvw.length;
+               msr_info->data = vcpu->arch.osvw.length;
                break;
        case MSR_AMD64_OSVW_STATUS:
                if (!guest_cpuid_has_osvw(vcpu))
                        return 1;
-               data = vcpu->arch.osvw.status;
+               msr_info->data = vcpu->arch.osvw.status;
                break;
        default:
-               if (kvm_pmu_msr(vcpu, msr))
-                       return kvm_pmu_get_msr(vcpu, msr, pdata);
+               if (kvm_pmu_is_valid_msr(vcpu, msr_info->index))
+                       return kvm_pmu_get_msr(vcpu, msr_info->index, &msr_info->data);
                if (!ignore_msrs) {
-                       vcpu_unimpl(vcpu, "unhandled rdmsr: 0x%x\n", msr);
+                       vcpu_unimpl(vcpu, "unhandled rdmsr: 0x%x\n", msr_info->index);
                        return 1;
                } else {
-                       vcpu_unimpl(vcpu, "ignored rdmsr: 0x%x\n", msr);
-                       data = 0;
+                       vcpu_unimpl(vcpu, "ignored rdmsr: 0x%x\n", msr_info->index);
+                       msr_info->data = 0;
                }
                break;
        }
-       *pdata = data;
        return 0;
 }
 EXPORT_SYMBOL_GPL(kvm_get_msr_common);
@@ -2798,12 +2651,25 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
        case KVM_CAP_HYPERV_TIME:
        case KVM_CAP_IOAPIC_POLARITY_IGNORED:
        case KVM_CAP_TSC_DEADLINE_TIMER:
+       case KVM_CAP_ENABLE_CAP_VM:
+       case KVM_CAP_DISABLE_QUIRKS:
 #ifdef CONFIG_KVM_DEVICE_ASSIGNMENT
        case KVM_CAP_ASSIGN_DEV_IRQ:
        case KVM_CAP_PCI_2_3:
 #endif
                r = 1;
                break;
+       case KVM_CAP_X86_SMM:
+               /* SMBASE is usually relocated above 1M on modern chipsets,
+                * and SMM handlers might indeed rely on 4G segment limits,
+                * so do not report SMM to be available if real mode is
+                * emulated via vm86 mode.  Still, do not go to great lengths
+                * to avoid userspace's usage of the feature, because it is a
+                * fringe case that is not enabled except via specific settings
+                * of the module parameters.
+                */
+               r = kvm_x86_ops->cpu_has_high_real_mode_segbase();
+               break;
        case KVM_CAP_COALESCED_MMIO:
                r = KVM_COALESCED_MMIO_PAGE_OFFSET;
                break;
@@ -2860,7 +2726,7 @@ long kvm_arch_dev_ioctl(struct file *filp,
                if (copy_from_user(&msr_list, user_msr_list, sizeof msr_list))
                        goto out;
                n = msr_list.nmsrs;
-               msr_list.nmsrs = num_msrs_to_save + ARRAY_SIZE(emulated_msrs);
+               msr_list.nmsrs = num_msrs_to_save + num_emulated_msrs;
                if (copy_to_user(user_msr_list, &msr_list, sizeof msr_list))
                        goto out;
                r = -E2BIG;
@@ -2872,7 +2738,7 @@ long kvm_arch_dev_ioctl(struct file *filp,
                        goto out;
                if (copy_to_user(user_msr_list->indices + num_msrs_to_save,
                                 &emulated_msrs,
-                                ARRAY_SIZE(emulated_msrs) * sizeof(u32)))
+                                num_emulated_msrs * sizeof(u32)))
                        goto out;
                r = 0;
                break;
@@ -3016,6 +2882,13 @@ static int kvm_vcpu_ioctl_nmi(struct kvm_vcpu *vcpu)
        return 0;
 }
 
+static int kvm_vcpu_ioctl_smi(struct kvm_vcpu *vcpu)
+{
+       kvm_make_request(KVM_REQ_SMI, vcpu);
+
+       return 0;
+}
+
 static int vcpu_ioctl_tpr_access_reporting(struct kvm_vcpu *vcpu,
                                           struct kvm_tpr_access_ctl *tac)
 {
@@ -3121,8 +2994,15 @@ static void kvm_vcpu_ioctl_x86_get_vcpu_events(struct kvm_vcpu *vcpu,
 
        events->sipi_vector = 0; /* never valid when reporting to user space */
 
+       events->smi.smm = is_smm(vcpu);
+       events->smi.pending = vcpu->arch.smi_pending;
+       events->smi.smm_inside_nmi =
+               !!(vcpu->arch.hflags & HF_SMM_INSIDE_NMI_MASK);
+       events->smi.latched_init = kvm_lapic_latched_init(vcpu);
+
        events->flags = (KVM_VCPUEVENT_VALID_NMI_PENDING
-                        | KVM_VCPUEVENT_VALID_SHADOW);
+                        | KVM_VCPUEVENT_VALID_SHADOW
+                        | KVM_VCPUEVENT_VALID_SMM);
        memset(&events->reserved, 0, sizeof(events->reserved));
 }
 
@@ -3131,7 +3011,8 @@ static int kvm_vcpu_ioctl_x86_set_vcpu_events(struct kvm_vcpu *vcpu,
 {
        if (events->flags & ~(KVM_VCPUEVENT_VALID_NMI_PENDING
                              | KVM_VCPUEVENT_VALID_SIPI_VECTOR
-                             | KVM_VCPUEVENT_VALID_SHADOW))
+                             | KVM_VCPUEVENT_VALID_SHADOW
+                             | KVM_VCPUEVENT_VALID_SMM))
                return -EINVAL;
 
        process_nmi(vcpu);
@@ -3156,6 +3037,24 @@ static int kvm_vcpu_ioctl_x86_set_vcpu_events(struct kvm_vcpu *vcpu,
            kvm_vcpu_has_lapic(vcpu))
                vcpu->arch.apic->sipi_vector = events->sipi_vector;
 
+       if (events->flags & KVM_VCPUEVENT_VALID_SMM) {
+               if (events->smi.smm)
+                       vcpu->arch.hflags |= HF_SMM_MASK;
+               else
+                       vcpu->arch.hflags &= ~HF_SMM_MASK;
+               vcpu->arch.smi_pending = events->smi.pending;
+               if (events->smi.smm_inside_nmi)
+                       vcpu->arch.hflags |= HF_SMM_INSIDE_NMI_MASK;
+               else
+                       vcpu->arch.hflags &= ~HF_SMM_INSIDE_NMI_MASK;
+               if (kvm_vcpu_has_lapic(vcpu)) {
+                       if (events->smi.latched_init)
+                               set_bit(KVM_APIC_INIT, &vcpu->arch.apic->pending_events);
+                       else
+                               clear_bit(KVM_APIC_INIT, &vcpu->arch.apic->pending_events);
+               }
+       }
+
        kvm_make_request(KVM_REQ_EVENT, vcpu);
 
        return 0;
@@ -3194,8 +3093,8 @@ static int kvm_vcpu_ioctl_x86_set_debugregs(struct kvm_vcpu *vcpu,
 
 static void fill_xsave(u8 *dest, struct kvm_vcpu *vcpu)
 {
-       struct xsave_struct *xsave = &vcpu->arch.guest_fpu.state->xsave;
-       u64 xstate_bv = xsave->xsave_hdr.xstate_bv;
+       struct xregs_state *xsave = &vcpu->arch.guest_fpu.state.xsave;
+       u64 xstate_bv = xsave->header.xfeatures;
        u64 valid;
 
        /*
@@ -3230,7 +3129,7 @@ static void fill_xsave(u8 *dest, struct kvm_vcpu *vcpu)
 
 static void load_xsave(struct kvm_vcpu *vcpu, u8 *src)
 {
-       struct xsave_struct *xsave = &vcpu->arch.guest_fpu.state->xsave;
+       struct xregs_state *xsave = &vcpu->arch.guest_fpu.state.xsave;
        u64 xstate_bv = *(u64 *)(src + XSAVE_HDR_OFFSET);
        u64 valid;
 
@@ -3241,9 +3140,9 @@ static void load_xsave(struct kvm_vcpu *vcpu, u8 *src)
        memcpy(xsave, src, XSAVE_HDR_OFFSET);
 
        /* Set XSTATE_BV and possibly XCOMP_BV.  */
-       xsave->xsave_hdr.xstate_bv = xstate_bv;
+       xsave->header.xfeatures = xstate_bv;
        if (cpu_has_xsaves)
-               xsave->xsave_hdr.xcomp_bv = host_xcr0 | XSTATE_COMPACTION_ENABLED;
+               xsave->header.xcomp_bv = host_xcr0 | XSTATE_COMPACTION_ENABLED;
 
        /*
         * Copy each region from the non-compacted offset to the
@@ -3275,8 +3174,8 @@ static void kvm_vcpu_ioctl_x86_get_xsave(struct kvm_vcpu *vcpu,
                fill_xsave((u8 *) guest_xsave->region, vcpu);
        } else {
                memcpy(guest_xsave->region,
-                       &vcpu->arch.guest_fpu.state->fxsave,
-                       sizeof(struct i387_fxsave_struct));
+                       &vcpu->arch.guest_fpu.state.fxsave,
+                       sizeof(struct fxregs_state));
                *(u64 *)&guest_xsave->region[XSAVE_HDR_OFFSET / sizeof(u32)] =
                        XSTATE_FPSSE;
        }
@@ -3300,8 +3199,8 @@ static int kvm_vcpu_ioctl_x86_set_xsave(struct kvm_vcpu *vcpu,
        } else {
                if (xstate_bv & ~XSTATE_FPSSE)
                        return -EINVAL;
-               memcpy(&vcpu->arch.guest_fpu.state->fxsave,
-                       guest_xsave->region, sizeof(struct i387_fxsave_struct));
+               memcpy(&vcpu->arch.guest_fpu.state.fxsave,
+                       guest_xsave->region, sizeof(struct fxregs_state));
        }
        return 0;
 }
@@ -3415,6 +3314,10 @@ long kvm_arch_vcpu_ioctl(struct file *filp,
                r = kvm_vcpu_ioctl_nmi(vcpu);
                break;
        }
+       case KVM_SMI: {
+               r = kvm_vcpu_ioctl_smi(vcpu);
+               break;
+       }
        case KVM_SET_CPUID: {
                struct kvm_cpuid __user *cpuid_arg = argp;
                struct kvm_cpuid cpuid;
@@ -3454,7 +3357,7 @@ long kvm_arch_vcpu_ioctl(struct file *filp,
                break;
        }
        case KVM_GET_MSRS:
-               r = msr_io(vcpu, argp, kvm_get_msr, 1);
+               r = msr_io(vcpu, argp, do_get_msr, 1);
                break;
        case KVM_SET_MSRS:
                r = msr_io(vcpu, argp, do_set_msr, 0);
@@ -3845,6 +3748,26 @@ int kvm_vm_ioctl_irq_line(struct kvm *kvm, struct kvm_irq_level *irq_event,
        return 0;
 }
 
+static int kvm_vm_ioctl_enable_cap(struct kvm *kvm,
+                                  struct kvm_enable_cap *cap)
+{
+       int r;
+
+       if (cap->flags)
+               return -EINVAL;
+
+       switch (cap->cap) {
+       case KVM_CAP_DISABLE_QUIRKS:
+               kvm->arch.disabled_quirks = cap->args[0];
+               r = 0;
+               break;
+       default:
+               r = -EINVAL;
+               break;
+       }
+       return r;
+}
+
 long kvm_arch_vm_ioctl(struct file *filp,
                       unsigned int ioctl, unsigned long arg)
 {
@@ -4097,7 +4020,15 @@ long kvm_arch_vm_ioctl(struct file *filp,
                r = 0;
                break;
        }
+       case KVM_ENABLE_CAP: {
+               struct kvm_enable_cap cap;
 
+               r = -EFAULT;
+               if (copy_from_user(&cap, argp, sizeof(cap)))
+                       goto out;
+               r = kvm_vm_ioctl_enable_cap(kvm, &cap);
+               break;
+       }
        default:
                r = kvm_vm_ioctl_assigned_device(kvm, ioctl, arg);
        }
@@ -4110,8 +4041,7 @@ static void kvm_init_msr_list(void)
        u32 dummy[2];
        unsigned i, j;
 
-       /* skip the first msrs in the list. KVM-specific */
-       for (i = j = KVM_SAVE_MSRS_BEGIN; i < ARRAY_SIZE(msrs_to_save); i++) {
+       for (i = j = 0; i < ARRAY_SIZE(msrs_to_save); i++) {
                if (rdmsr_safe(msrs_to_save[i], &dummy[0], &dummy[1]) < 0)
                        continue;
 
@@ -4136,6 +4066,22 @@ static void kvm_init_msr_list(void)
                j++;
        }
        num_msrs_to_save = j;
+
+       for (i = j = 0; i < ARRAY_SIZE(emulated_msrs); i++) {
+               switch (emulated_msrs[i]) {
+               case MSR_IA32_SMBASE:
+                       if (!kvm_x86_ops->cpu_has_high_real_mode_segbase())
+                               continue;
+                       break;
+               default:
+                       break;
+               }
+
+               if (j < i)
+                       emulated_msrs[j] = emulated_msrs[i];
+               j++;
+       }
+       num_emulated_msrs = j;
 }
 
 static int vcpu_mmio_write(struct kvm_vcpu *vcpu, gpa_t addr, int len,
@@ -4253,8 +4199,8 @@ static int kvm_read_guest_virt_helper(gva_t addr, void *val, unsigned int bytes,
 
                if (gpa == UNMAPPED_GVA)
                        return X86EMUL_PROPAGATE_FAULT;
-               ret = kvm_read_guest_page(vcpu->kvm, gpa >> PAGE_SHIFT, data,
-                                         offset, toread);
+               ret = kvm_vcpu_read_guest_page(vcpu, gpa >> PAGE_SHIFT, data,
+                                              offset, toread);
                if (ret < 0) {
                        r = X86EMUL_IO_NEEDED;
                        goto out;
@@ -4287,8 +4233,8 @@ static int kvm_fetch_guest_virt(struct x86_emulate_ctxt *ctxt,
        offset = addr & (PAGE_SIZE-1);
        if (WARN_ON(offset + bytes > PAGE_SIZE))
                bytes = (unsigned)PAGE_SIZE - offset;
-       ret = kvm_read_guest_page(vcpu->kvm, gpa >> PAGE_SHIFT, val,
-                                 offset, bytes);
+       ret = kvm_vcpu_read_guest_page(vcpu, gpa >> PAGE_SHIFT, val,
+                                      offset, bytes);
        if (unlikely(ret < 0))
                return X86EMUL_IO_NEEDED;
 
@@ -4334,7 +4280,7 @@ int kvm_write_guest_virt_system(struct x86_emulate_ctxt *ctxt,
 
                if (gpa == UNMAPPED_GVA)
                        return X86EMUL_PROPAGATE_FAULT;
-               ret = kvm_write_guest(vcpu->kvm, gpa, data, towrite);
+               ret = kvm_vcpu_write_guest(vcpu, gpa, data, towrite);
                if (ret < 0) {
                        r = X86EMUL_IO_NEEDED;
                        goto out;
@@ -4387,7 +4333,7 @@ int emulator_write_phys(struct kvm_vcpu *vcpu, gpa_t gpa,
 {
        int ret;
 
-       ret = kvm_write_guest(vcpu->kvm, gpa, val, bytes);
+       ret = kvm_vcpu_write_guest(vcpu, gpa, val, bytes);
        if (ret < 0)
                return 0;
        kvm_mmu_pte_write(vcpu, gpa, val, bytes);
@@ -4421,7 +4367,7 @@ static int read_prepare(struct kvm_vcpu *vcpu, void *val, int bytes)
 static int read_emulate(struct kvm_vcpu *vcpu, gpa_t gpa,
                        void *val, int bytes)
 {
-       return !kvm_read_guest(vcpu->kvm, gpa, val, bytes);
+       return !kvm_vcpu_read_guest(vcpu, gpa, val, bytes);
 }
 
 static int write_emulate(struct kvm_vcpu *vcpu, gpa_t gpa,
@@ -4619,7 +4565,7 @@ static int emulator_cmpxchg_emulated(struct x86_emulate_ctxt *ctxt,
        if (((gpa + bytes - 1) & PAGE_MASK) != (gpa & PAGE_MASK))
                goto emul_write;
 
-       page = gfn_to_page(vcpu->kvm, gpa >> PAGE_SHIFT);
+       page = kvm_vcpu_gfn_to_page(vcpu, gpa >> PAGE_SHIFT);
        if (is_error_page(page))
                goto emul_write;
 
@@ -4647,7 +4593,7 @@ static int emulator_cmpxchg_emulated(struct x86_emulate_ctxt *ctxt,
        if (!exchanged)
                return X86EMUL_CMPXCHG_FAILED;
 
-       mark_page_dirty(vcpu->kvm, gpa >> PAGE_SHIFT);
+       kvm_vcpu_mark_page_dirty(vcpu, gpa >> PAGE_SHIFT);
        kvm_mmu_pte_write(vcpu, gpa, new, bytes);
 
        return X86EMUL_CONTINUE;
@@ -4946,7 +4892,17 @@ static void emulator_set_segment(struct x86_emulate_ctxt *ctxt, u16 selector,
 static int emulator_get_msr(struct x86_emulate_ctxt *ctxt,
                            u32 msr_index, u64 *pdata)
 {
-       return kvm_get_msr(emul_to_vcpu(ctxt), msr_index, pdata);
+       struct msr_data msr;
+       int r;
+
+       msr.index = msr_index;
+       msr.host_initiated = false;
+       r = kvm_get_msr(emul_to_vcpu(ctxt), &msr);
+       if (r)
+               return r;
+
+       *pdata = msr.data;
+       return 0;
 }
 
 static int emulator_set_msr(struct x86_emulate_ctxt *ctxt,
@@ -4960,16 +4916,30 @@ static int emulator_set_msr(struct x86_emulate_ctxt *ctxt,
        return kvm_set_msr(emul_to_vcpu(ctxt), &msr);
 }
 
+static u64 emulator_get_smbase(struct x86_emulate_ctxt *ctxt)
+{
+       struct kvm_vcpu *vcpu = emul_to_vcpu(ctxt);
+
+       return vcpu->arch.smbase;
+}
+
+static void emulator_set_smbase(struct x86_emulate_ctxt *ctxt, u64 smbase)
+{
+       struct kvm_vcpu *vcpu = emul_to_vcpu(ctxt);
+
+       vcpu->arch.smbase = smbase;
+}
+
 static int emulator_check_pmc(struct x86_emulate_ctxt *ctxt,
                              u32 pmc)
 {
-       return kvm_pmu_check_pmc(emul_to_vcpu(ctxt), pmc);
+       return kvm_pmu_is_valid_msr_idx(emul_to_vcpu(ctxt), pmc);
 }
 
 static int emulator_read_pmc(struct x86_emulate_ctxt *ctxt,
                             u32 pmc, u64 *pdata)
 {
-       return kvm_pmu_read_pmc(emul_to_vcpu(ctxt), pmc, pdata);
+       return kvm_pmu_rdpmc(emul_to_vcpu(ctxt), pmc, pdata);
 }
 
 static void emulator_halt(struct x86_emulate_ctxt *ctxt)
@@ -5045,6 +5015,8 @@ static const struct x86_emulate_ops emulate_ops = {
        .cpl                 = emulator_get_cpl,
        .get_dr              = emulator_get_dr,
        .set_dr              = emulator_set_dr,
+       .get_smbase          = emulator_get_smbase,
+       .set_smbase          = emulator_set_smbase,
        .set_msr             = emulator_set_msr,
        .get_msr             = emulator_get_msr,
        .check_pmc           = emulator_check_pmc,
@@ -5106,7 +5078,10 @@ static void init_emulate_ctxt(struct kvm_vcpu *vcpu)
                     (cs_l && is_long_mode(vcpu))       ? X86EMUL_MODE_PROT64 :
                     cs_db                              ? X86EMUL_MODE_PROT32 :
                                                          X86EMUL_MODE_PROT16;
-       ctxt->guest_mode = is_guest_mode(vcpu);
+       BUILD_BUG_ON(HF_GUEST_MASK != X86EMUL_GUEST_MASK);
+       BUILD_BUG_ON(HF_SMM_MASK != X86EMUL_SMM_MASK);
+       BUILD_BUG_ON(HF_SMM_INSIDE_NMI_MASK != X86EMUL_SMM_INSIDE_NMI_MASK);
+       ctxt->emul_flags = vcpu->arch.hflags;
 
        init_decode_cache(ctxt);
        vcpu->arch.emulate_regs_need_sync_from_vcpu = false;
@@ -5275,6 +5250,34 @@ static bool retry_instruction(struct x86_emulate_ctxt *ctxt,
 static int complete_emulated_mmio(struct kvm_vcpu *vcpu);
 static int complete_emulated_pio(struct kvm_vcpu *vcpu);
 
+static void kvm_smm_changed(struct kvm_vcpu *vcpu)
+{
+       if (!(vcpu->arch.hflags & HF_SMM_MASK)) {
+               /* This is a good place to trace that we are exiting SMM.  */
+               trace_kvm_enter_smm(vcpu->vcpu_id, vcpu->arch.smbase, false);
+
+               if (unlikely(vcpu->arch.smi_pending)) {
+                       kvm_make_request(KVM_REQ_SMI, vcpu);
+                       vcpu->arch.smi_pending = 0;
+               } else {
+                       /* Process a latched INIT, if any.  */
+                       kvm_make_request(KVM_REQ_EVENT, vcpu);
+               }
+       }
+
+       kvm_mmu_reset_context(vcpu);
+}
+
+static void kvm_set_hflags(struct kvm_vcpu *vcpu, unsigned emul_flags)
+{
+       unsigned changed = vcpu->arch.hflags ^ emul_flags;
+
+       vcpu->arch.hflags = emul_flags;
+
+       if (changed & HF_SMM_MASK)
+               kvm_smm_changed(vcpu);
+}
+
 static int kvm_vcpu_check_hw_bp(unsigned long addr, u32 type, u32 dr7,
                                unsigned long *db)
 {
@@ -5474,6 +5477,8 @@ restart:
                unsigned long rflags = kvm_x86_ops->get_rflags(vcpu);
                toggle_interruptibility(vcpu, ctxt->interruptibility);
                vcpu->arch.emulate_regs_need_sync_to_vcpu = false;
+               if (vcpu->arch.hflags != ctxt->emul_flags)
+                       kvm_set_hflags(vcpu, ctxt->emul_flags);
                kvm_rip_write(vcpu, ctxt->eip);
                if (r == EMULATE_DONE)
                        kvm_vcpu_check_singlestep(vcpu, rflags, &r);
@@ -5952,6 +5957,7 @@ static void kvm_pv_kick_cpu_op(struct kvm *kvm, unsigned long flags, int apicid)
        lapic_irq.shorthand = 0;
        lapic_irq.dest_mode = 0;
        lapic_irq.dest_id = apicid;
+       lapic_irq.msi_redir_hint = false;
 
        lapic_irq.delivery_mode = APIC_DM_REMRD;
        kvm_irq_delivery_to_apic(kvm, NULL, &lapic_irq, NULL);
@@ -6039,6 +6045,7 @@ static void post_kvm_run_save(struct kvm_vcpu *vcpu)
        struct kvm_run *kvm_run = vcpu->run;
 
        kvm_run->if_flag = (kvm_get_rflags(vcpu) & X86_EFLAGS_IF) != 0;
+       kvm_run->flags = is_smm(vcpu) ? KVM_RUN_X86_SMM : 0;
        kvm_run->cr8 = kvm_get_cr8(vcpu);
        kvm_run->apic_base = kvm_get_apic_base(vcpu);
        if (irqchip_in_kernel(vcpu->kvm))
@@ -6162,6 +6169,233 @@ static void process_nmi(struct kvm_vcpu *vcpu)
        kvm_make_request(KVM_REQ_EVENT, vcpu);
 }
 
+#define put_smstate(type, buf, offset, val)                      \
+       *(type *)((buf) + (offset) - 0x7e00) = val
+
+static u32 process_smi_get_segment_flags(struct kvm_segment *seg)
+{
+       u32 flags = 0;
+       flags |= seg->g       << 23;
+       flags |= seg->db      << 22;
+       flags |= seg->l       << 21;
+       flags |= seg->avl     << 20;
+       flags |= seg->present << 15;
+       flags |= seg->dpl     << 13;
+       flags |= seg->s       << 12;
+       flags |= seg->type    << 8;
+       return flags;
+}
+
+static void process_smi_save_seg_32(struct kvm_vcpu *vcpu, char *buf, int n)
+{
+       struct kvm_segment seg;
+       int offset;
+
+       kvm_get_segment(vcpu, &seg, n);
+       put_smstate(u32, buf, 0x7fa8 + n * 4, seg.selector);
+
+       if (n < 3)
+               offset = 0x7f84 + n * 12;
+       else
+               offset = 0x7f2c + (n - 3) * 12;
+
+       put_smstate(u32, buf, offset + 8, seg.base);
+       put_smstate(u32, buf, offset + 4, seg.limit);
+       put_smstate(u32, buf, offset, process_smi_get_segment_flags(&seg));
+}
+
+static void process_smi_save_seg_64(struct kvm_vcpu *vcpu, char *buf, int n)
+{
+       struct kvm_segment seg;
+       int offset;
+       u16 flags;
+
+       kvm_get_segment(vcpu, &seg, n);
+       offset = 0x7e00 + n * 16;
+
+       flags = process_smi_get_segment_flags(&seg) >> 8;
+       put_smstate(u16, buf, offset, seg.selector);
+       put_smstate(u16, buf, offset + 2, flags);
+       put_smstate(u32, buf, offset + 4, seg.limit);
+       put_smstate(u64, buf, offset + 8, seg.base);
+}
+
+static void process_smi_save_state_32(struct kvm_vcpu *vcpu, char *buf)
+{
+       struct desc_ptr dt;
+       struct kvm_segment seg;
+       unsigned long val;
+       int i;
+
+       put_smstate(u32, buf, 0x7ffc, kvm_read_cr0(vcpu));
+       put_smstate(u32, buf, 0x7ff8, kvm_read_cr3(vcpu));
+       put_smstate(u32, buf, 0x7ff4, kvm_get_rflags(vcpu));
+       put_smstate(u32, buf, 0x7ff0, kvm_rip_read(vcpu));
+
+       for (i = 0; i < 8; i++)
+               put_smstate(u32, buf, 0x7fd0 + i * 4, kvm_register_read(vcpu, i));
+
+       kvm_get_dr(vcpu, 6, &val);
+       put_smstate(u32, buf, 0x7fcc, (u32)val);
+       kvm_get_dr(vcpu, 7, &val);
+       put_smstate(u32, buf, 0x7fc8, (u32)val);
+
+       kvm_get_segment(vcpu, &seg, VCPU_SREG_TR);
+       put_smstate(u32, buf, 0x7fc4, seg.selector);
+       put_smstate(u32, buf, 0x7f64, seg.base);
+       put_smstate(u32, buf, 0x7f60, seg.limit);
+       put_smstate(u32, buf, 0x7f5c, process_smi_get_segment_flags(&seg));
+
+       kvm_get_segment(vcpu, &seg, VCPU_SREG_LDTR);
+       put_smstate(u32, buf, 0x7fc0, seg.selector);
+       put_smstate(u32, buf, 0x7f80, seg.base);
+       put_smstate(u32, buf, 0x7f7c, seg.limit);
+       put_smstate(u32, buf, 0x7f78, process_smi_get_segment_flags(&seg));
+
+       kvm_x86_ops->get_gdt(vcpu, &dt);
+       put_smstate(u32, buf, 0x7f74, dt.address);
+       put_smstate(u32, buf, 0x7f70, dt.size);
+
+       kvm_x86_ops->get_idt(vcpu, &dt);
+       put_smstate(u32, buf, 0x7f58, dt.address);
+       put_smstate(u32, buf, 0x7f54, dt.size);
+
+       for (i = 0; i < 6; i++)
+               process_smi_save_seg_32(vcpu, buf, i);
+
+       put_smstate(u32, buf, 0x7f14, kvm_read_cr4(vcpu));
+
+       /* revision id */
+       put_smstate(u32, buf, 0x7efc, 0x00020000);
+       put_smstate(u32, buf, 0x7ef8, vcpu->arch.smbase);
+}
+
+static void process_smi_save_state_64(struct kvm_vcpu *vcpu, char *buf)
+{
+#ifdef CONFIG_X86_64
+       struct desc_ptr dt;
+       struct kvm_segment seg;
+       unsigned long val;
+       int i;
+
+       for (i = 0; i < 16; i++)
+               put_smstate(u64, buf, 0x7ff8 - i * 8, kvm_register_read(vcpu, i));
+
+       put_smstate(u64, buf, 0x7f78, kvm_rip_read(vcpu));
+       put_smstate(u32, buf, 0x7f70, kvm_get_rflags(vcpu));
+
+       kvm_get_dr(vcpu, 6, &val);
+       put_smstate(u64, buf, 0x7f68, val);
+       kvm_get_dr(vcpu, 7, &val);
+       put_smstate(u64, buf, 0x7f60, val);
+
+       put_smstate(u64, buf, 0x7f58, kvm_read_cr0(vcpu));
+       put_smstate(u64, buf, 0x7f50, kvm_read_cr3(vcpu));
+       put_smstate(u64, buf, 0x7f48, kvm_read_cr4(vcpu));
+
+       put_smstate(u32, buf, 0x7f00, vcpu->arch.smbase);
+
+       /* revision id */
+       put_smstate(u32, buf, 0x7efc, 0x00020064);
+
+       put_smstate(u64, buf, 0x7ed0, vcpu->arch.efer);
+
+       kvm_get_segment(vcpu, &seg, VCPU_SREG_TR);
+       put_smstate(u16, buf, 0x7e90, seg.selector);
+       put_smstate(u16, buf, 0x7e92, process_smi_get_segment_flags(&seg) >> 8);
+       put_smstate(u32, buf, 0x7e94, seg.limit);
+       put_smstate(u64, buf, 0x7e98, seg.base);
+
+       kvm_x86_ops->get_idt(vcpu, &dt);
+       put_smstate(u32, buf, 0x7e84, dt.size);
+       put_smstate(u64, buf, 0x7e88, dt.address);
+
+       kvm_get_segment(vcpu, &seg, VCPU_SREG_LDTR);
+       put_smstate(u16, buf, 0x7e70, seg.selector);
+       put_smstate(u16, buf, 0x7e72, process_smi_get_segment_flags(&seg) >> 8);
+       put_smstate(u32, buf, 0x7e74, seg.limit);
+       put_smstate(u64, buf, 0x7e78, seg.base);
+
+       kvm_x86_ops->get_gdt(vcpu, &dt);
+       put_smstate(u32, buf, 0x7e64, dt.size);
+       put_smstate(u64, buf, 0x7e68, dt.address);
+
+       for (i = 0; i < 6; i++)
+               process_smi_save_seg_64(vcpu, buf, i);
+#else
+       WARN_ON_ONCE(1);
+#endif
+}
+
+static void process_smi(struct kvm_vcpu *vcpu)
+{
+       struct kvm_segment cs, ds;
+       char buf[512];
+       u32 cr0;
+
+       if (is_smm(vcpu)) {
+               vcpu->arch.smi_pending = true;
+               return;
+       }
+
+       trace_kvm_enter_smm(vcpu->vcpu_id, vcpu->arch.smbase, true);
+       vcpu->arch.hflags |= HF_SMM_MASK;
+       memset(buf, 0, 512);
+       if (guest_cpuid_has_longmode(vcpu))
+               process_smi_save_state_64(vcpu, buf);
+       else
+               process_smi_save_state_32(vcpu, buf);
+
+       kvm_vcpu_write_guest(vcpu, vcpu->arch.smbase + 0xfe00, buf, sizeof(buf));
+
+       if (kvm_x86_ops->get_nmi_mask(vcpu))
+               vcpu->arch.hflags |= HF_SMM_INSIDE_NMI_MASK;
+       else
+               kvm_x86_ops->set_nmi_mask(vcpu, true);
+
+       kvm_set_rflags(vcpu, X86_EFLAGS_FIXED);
+       kvm_rip_write(vcpu, 0x8000);
+
+       cr0 = vcpu->arch.cr0 & ~(X86_CR0_PE | X86_CR0_EM | X86_CR0_TS | X86_CR0_PG);
+       kvm_x86_ops->set_cr0(vcpu, cr0);
+       vcpu->arch.cr0 = cr0;
+
+       kvm_x86_ops->set_cr4(vcpu, 0);
+
+       __kvm_set_dr(vcpu, 7, DR7_FIXED_1);
+
+       cs.selector = (vcpu->arch.smbase >> 4) & 0xffff;
+       cs.base = vcpu->arch.smbase;
+
+       ds.selector = 0;
+       ds.base = 0;
+
+       cs.limit    = ds.limit = 0xffffffff;
+       cs.type     = ds.type = 0x3;
+       cs.dpl      = ds.dpl = 0;
+       cs.db       = ds.db = 0;
+       cs.s        = ds.s = 1;
+       cs.l        = ds.l = 0;
+       cs.g        = ds.g = 1;
+       cs.avl      = ds.avl = 0;
+       cs.present  = ds.present = 1;
+       cs.unusable = ds.unusable = 0;
+       cs.padding  = ds.padding = 0;
+
+       kvm_set_segment(vcpu, &cs, VCPU_SREG_CS);
+       kvm_set_segment(vcpu, &ds, VCPU_SREG_DS);
+       kvm_set_segment(vcpu, &ds, VCPU_SREG_ES);
+       kvm_set_segment(vcpu, &ds, VCPU_SREG_FS);
+       kvm_set_segment(vcpu, &ds, VCPU_SREG_GS);
+       kvm_set_segment(vcpu, &ds, VCPU_SREG_SS);
+
+       if (guest_cpuid_has_longmode(vcpu))
+               kvm_x86_ops->set_efer(vcpu, 0);
+
+       kvm_update_cpuid(vcpu);
+       kvm_mmu_reset_context(vcpu);
+}
+
 static void vcpu_scan_ioapic(struct kvm_vcpu *vcpu)
 {
        u64 eoi_exit_bitmap[4];
@@ -6270,12 +6504,14 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu)
                }
                if (kvm_check_request(KVM_REQ_STEAL_UPDATE, vcpu))
                        record_steal_time(vcpu);
+               if (kvm_check_request(KVM_REQ_SMI, vcpu))
+                       process_smi(vcpu);
                if (kvm_check_request(KVM_REQ_NMI, vcpu))
                        process_nmi(vcpu);
                if (kvm_check_request(KVM_REQ_PMU, vcpu))
-                       kvm_handle_pmu_event(vcpu);
+                       kvm_pmu_handle_event(vcpu);
                if (kvm_check_request(KVM_REQ_PMI, vcpu))
-                       kvm_deliver_pmi(vcpu);
+                       kvm_pmu_deliver_pmi(vcpu);
                if (kvm_check_request(KVM_REQ_SCAN_IOAPIC, vcpu))
                        vcpu_scan_ioapic(vcpu);
                if (kvm_check_request(KVM_REQ_APIC_PAGE_RELOAD, vcpu))
@@ -6347,7 +6583,7 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu)
        if (req_immediate_exit)
                smp_send_reschedule(vcpu->cpu);
 
-       kvm_guest_enter();
+       __kvm_guest_enter();
 
        if (unlikely(vcpu->arch.switch_db_regs)) {
                set_debugreg(0, 7);
@@ -6597,11 +6833,11 @@ static int complete_emulated_mmio(struct kvm_vcpu *vcpu)
 
 int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
 {
+       struct fpu *fpu = &current->thread.fpu;
        int r;
        sigset_t sigsaved;
 
-       if (!tsk_used_math(current) && init_fpu(current))
-               return -ENOMEM;
+       fpu__activate_curr(fpu);
 
        if (vcpu->sigset_active)
                sigprocmask(SIG_SETMASK, &vcpu->sigset, &sigsaved);
@@ -6971,8 +7207,8 @@ int kvm_arch_vcpu_ioctl_translate(struct kvm_vcpu *vcpu,
 
 int kvm_arch_vcpu_ioctl_get_fpu(struct kvm_vcpu *vcpu, struct kvm_fpu *fpu)
 {
-       struct i387_fxsave_struct *fxsave =
-                       &vcpu->arch.guest_fpu.state->fxsave;
+       struct fxregs_state *fxsave =
+                       &vcpu->arch.guest_fpu.state.fxsave;
 
        memcpy(fpu->fpr, fxsave->st_space, 128);
        fpu->fcw = fxsave->cwd;
@@ -6988,8 +7224,8 @@ int kvm_arch_vcpu_ioctl_get_fpu(struct kvm_vcpu *vcpu, struct kvm_fpu *fpu)
 
 int kvm_arch_vcpu_ioctl_set_fpu(struct kvm_vcpu *vcpu, struct kvm_fpu *fpu)
 {
-       struct i387_fxsave_struct *fxsave =
-                       &vcpu->arch.guest_fpu.state->fxsave;
+       struct fxregs_state *fxsave =
+                       &vcpu->arch.guest_fpu.state.fxsave;
 
        memcpy(fxsave->st_space, fpu->fpr, 128);
        fxsave->cwd = fpu->fcw;
@@ -7003,17 +7239,11 @@ int kvm_arch_vcpu_ioctl_set_fpu(struct kvm_vcpu *vcpu, struct kvm_fpu *fpu)
        return 0;
 }
 
-int fx_init(struct kvm_vcpu *vcpu)
+static void fx_init(struct kvm_vcpu *vcpu)
 {
-       int err;
-
-       err = fpu_alloc(&vcpu->arch.guest_fpu);
-       if (err)
-               return err;
-
-       fpu_finit(&vcpu->arch.guest_fpu);
+       fpstate_init(&vcpu->arch.guest_fpu.state);
        if (cpu_has_xsaves)
-               vcpu->arch.guest_fpu.state->xsave.xsave_hdr.xcomp_bv =
+               vcpu->arch.guest_fpu.state.xsave.header.xcomp_bv =
                        host_xcr0 | XSTATE_COMPACTION_ENABLED;
 
        /*
@@ -7022,14 +7252,6 @@ int fx_init(struct kvm_vcpu *vcpu)
        vcpu->arch.xcr0 = XSTATE_FP;
 
        vcpu->arch.cr0 |= X86_CR0_ET;
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(fx_init);
-
-static void fx_free(struct kvm_vcpu *vcpu)
-{
-       fpu_free(&vcpu->arch.guest_fpu);
 }
 
 void kvm_load_guest_fpu(struct kvm_vcpu *vcpu)
@@ -7045,7 +7267,7 @@ void kvm_load_guest_fpu(struct kvm_vcpu *vcpu)
        kvm_put_guest_xcr0(vcpu);
        vcpu->guest_fpu_loaded = 1;
        __kernel_fpu_begin();
-       fpu_restore_checking(&vcpu->arch.guest_fpu);
+       __copy_kernel_to_fpregs(&vcpu->arch.guest_fpu.state);
        trace_kvm_fpu(1);
 }
 
@@ -7053,16 +7275,25 @@ void kvm_put_guest_fpu(struct kvm_vcpu *vcpu)
 {
        kvm_put_guest_xcr0(vcpu);
 
-       if (!vcpu->guest_fpu_loaded)
+       if (!vcpu->guest_fpu_loaded) {
+               vcpu->fpu_counter = 0;
                return;
+       }
 
        vcpu->guest_fpu_loaded = 0;
-       fpu_save_init(&vcpu->arch.guest_fpu);
+       copy_fpregs_to_fpstate(&vcpu->arch.guest_fpu);
        __kernel_fpu_end();
        ++vcpu->stat.fpu_reload;
-       if (!vcpu->arch.eager_fpu)
-               kvm_make_request(KVM_REQ_DEACTIVATE_FPU, vcpu);
-
+       /*
+        * If using eager FPU mode, or if the guest is a frequent user
+        * of the FPU, just leave the FPU active for next time.
+        * Every 255 times fpu_counter rolls over to 0; a guest that uses
+        * the FPU in bursts will revert to loading it on demand.
+        */
+       if (!vcpu->arch.eager_fpu) {
+               if (++vcpu->fpu_counter < 5)
+                       kvm_make_request(KVM_REQ_DEACTIVATE_FPU, vcpu);
+       }
        trace_kvm_fpu(0);
 }
 
@@ -7071,7 +7302,6 @@ void kvm_arch_vcpu_free(struct kvm_vcpu *vcpu)
        kvmclock_reset(vcpu);
 
        free_cpumask_var(vcpu->arch.wbinvd_dirty_mask);
-       fx_free(vcpu);
        kvm_x86_ops->vcpu_free(vcpu);
 }
 
@@ -7099,14 +7329,13 @@ int kvm_arch_vcpu_setup(struct kvm_vcpu *vcpu)
 {
        int r;
 
-       vcpu->arch.mtrr_state.have_fixed = 1;
+       kvm_vcpu_mtrr_init(vcpu);
        r = vcpu_load(vcpu);
        if (r)
                return r;
-       kvm_vcpu_reset(vcpu);
+       kvm_vcpu_reset(vcpu, false);
        kvm_mmu_setup(vcpu);
        vcpu_put(vcpu);
-
        return r;
 }
 
@@ -7123,6 +7352,9 @@ void kvm_arch_vcpu_postcreate(struct kvm_vcpu *vcpu)
        kvm_write_tsc(vcpu, &msr);
        vcpu_put(vcpu);
 
+       if (!kvmclock_periodic_sync)
+               return;
+
        schedule_delayed_work(&kvm->arch.kvmclock_sync_work,
                                        KVMCLOCK_SYNC_PERIOD);
 }
@@ -7137,12 +7369,13 @@ void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu)
        kvm_mmu_unload(vcpu);
        vcpu_put(vcpu);
 
-       fx_free(vcpu);
        kvm_x86_ops->vcpu_free(vcpu);
 }
 
-void kvm_vcpu_reset(struct kvm_vcpu *vcpu)
+void kvm_vcpu_reset(struct kvm_vcpu *vcpu, bool init_event)
 {
+       vcpu->arch.hflags = 0;
+
        atomic_set(&vcpu->arch.nmi_queued, 0);
        vcpu->arch.nmi_pending = 0;
        vcpu->arch.nmi_injected = false;
@@ -7168,13 +7401,16 @@ void kvm_vcpu_reset(struct kvm_vcpu *vcpu)
        kvm_async_pf_hash_reset(vcpu);
        vcpu->arch.apf.halted = false;
 
-       kvm_pmu_reset(vcpu);
+       if (!init_event) {
+               kvm_pmu_reset(vcpu);
+               vcpu->arch.smbase = 0x30000;
+       }
 
        memset(vcpu->arch.regs, 0, sizeof(vcpu->arch.regs));
        vcpu->arch.regs_avail = ~0;
        vcpu->arch.regs_dirty = ~0;
 
-       kvm_x86_ops->vcpu_reset(vcpu);
+       kvm_x86_ops->vcpu_reset(vcpu, init_event);
 }
 
 void kvm_vcpu_deliver_sipi_vector(struct kvm_vcpu *vcpu, u8 vector)
@@ -7363,9 +7599,7 @@ int kvm_arch_vcpu_init(struct kvm_vcpu *vcpu)
                goto fail_free_mce_banks;
        }
 
-       r = fx_init(vcpu);
-       if (r)
-               goto fail_free_wbinvd_dirty_mask;
+       fx_init(vcpu);
 
        vcpu->arch.ia32_tsc_adjust_msr = 0x0;
        vcpu->arch.pv_time_enabled = false;
@@ -7375,12 +7609,13 @@ int kvm_arch_vcpu_init(struct kvm_vcpu *vcpu)
 
        vcpu->arch.maxphyaddr = cpuid_query_maxphyaddr(vcpu);
 
+       vcpu->arch.pat = MSR_IA32_CR_PAT_DEFAULT;
+
        kvm_async_pf_hash_reset(vcpu);
        kvm_pmu_init(vcpu);
 
        return 0;
-fail_free_wbinvd_dirty_mask:
-       free_cpumask_var(vcpu->arch.wbinvd_dirty_mask);
+
 fail_free_mce_banks:
        kfree(vcpu->arch.mce_banks);
 fail_free_lapic:
@@ -7482,6 +7717,40 @@ void kvm_arch_sync_events(struct kvm *kvm)
        kvm_free_pit(kvm);
 }
 
+int __x86_set_memory_region(struct kvm *kvm,
+                           const struct kvm_userspace_memory_region *mem)
+{
+       int i, r;
+
+       /* Called with kvm->slots_lock held.  */
+       BUG_ON(mem->slot >= KVM_MEM_SLOTS_NUM);
+
+       for (i = 0; i < KVM_ADDRESS_SPACE_NUM; i++) {
+               struct kvm_userspace_memory_region m = *mem;
+
+               m.slot |= i << 16;
+               r = __kvm_set_memory_region(kvm, &m);
+               if (r < 0)
+                       return r;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(__x86_set_memory_region);
+
+int x86_set_memory_region(struct kvm *kvm,
+                         const struct kvm_userspace_memory_region *mem)
+{
+       int r;
+
+       mutex_lock(&kvm->slots_lock);
+       r = __x86_set_memory_region(kvm, mem);
+       mutex_unlock(&kvm->slots_lock);
+
+       return r;
+}
+EXPORT_SYMBOL_GPL(x86_set_memory_region);
+
 void kvm_arch_destroy_vm(struct kvm *kvm)
 {
        if (current->mm == kvm->mm) {
@@ -7493,13 +7762,13 @@ void kvm_arch_destroy_vm(struct kvm *kvm)
                struct kvm_userspace_memory_region mem;
                memset(&mem, 0, sizeof(mem));
                mem.slot = APIC_ACCESS_PAGE_PRIVATE_MEMSLOT;
-               kvm_set_memory_region(kvm, &mem);
+               x86_set_memory_region(kvm, &mem);
 
                mem.slot = IDENTITY_PAGETABLE_PRIVATE_MEMSLOT;
-               kvm_set_memory_region(kvm, &mem);
+               x86_set_memory_region(kvm, &mem);
 
                mem.slot = TSS_PRIVATE_MEMSLOT;
-               kvm_set_memory_region(kvm, &mem);
+               x86_set_memory_region(kvm, &mem);
        }
        kvm_iommu_unmap_guest(kvm);
        kfree(kvm->arch.vpic);
@@ -7588,18 +7857,18 @@ out_free:
        return -ENOMEM;
 }
 
-void kvm_arch_memslots_updated(struct kvm *kvm)
+void kvm_arch_memslots_updated(struct kvm *kvm, struct kvm_memslots *slots)
 {
        /*
         * memslots->generation has been incremented.
         * mmio generation may have reached its maximum value.
         */
-       kvm_mmu_invalidate_mmio_sptes(kvm);
+       kvm_mmu_invalidate_mmio_sptes(kvm, slots);
 }
 
 int kvm_arch_prepare_memory_region(struct kvm *kvm,
                                struct kvm_memory_slot *memslot,
-                               struct kvm_userspace_memory_region *mem,
+                               const struct kvm_userspace_memory_region *mem,
                                enum kvm_mr_change change)
 {
        /*
@@ -7677,14 +7946,14 @@ static void kvm_mmu_slot_apply_flags(struct kvm *kvm,
 }
 
 void kvm_arch_commit_memory_region(struct kvm *kvm,
-                               struct kvm_userspace_memory_region *mem,
+                               const struct kvm_userspace_memory_region *mem,
                                const struct kvm_memory_slot *old,
+                               const struct kvm_memory_slot *new,
                                enum kvm_mr_change change)
 {
-       struct kvm_memory_slot *new;
        int nr_mmu_pages = 0;
 
-       if ((mem->slot >= KVM_USER_MEM_SLOTS) && (change == KVM_MR_DELETE)) {
+       if (change == KVM_MR_DELETE && old->id >= KVM_USER_MEM_SLOTS) {
                int ret;
 
                ret = vm_munmap(old->userspace_addr,
@@ -7701,9 +7970,6 @@ void kvm_arch_commit_memory_region(struct kvm *kvm,
        if (nr_mmu_pages)
                kvm_mmu_change_mmu_pages(kvm, nr_mmu_pages);
 
-       /* It's OK to get 'new' slot here as it has already been installed */
-       new = id_to_memslot(kvm->memslots, mem->slot);
-
        /*
         * Dirty logging tracks sptes in 4k granularity, meaning that large
         * sptes have to be split.  If live migration is successful, the guest
@@ -7728,9 +7994,11 @@ void kvm_arch_commit_memory_region(struct kvm *kvm,
         * been zapped so no dirty logging staff is needed for old slot. For
         * KVM_MR_FLAGS_ONLY, the old slot is essentially the same one as the
         * new and it's also covered when dealing with the new slot.
+        *
+        * FIXME: const-ify all uses of struct kvm_memory_slot.
         */
        if (change != KVM_MR_DELETE)
-               kvm_mmu_slot_apply_flags(kvm, new);
+               kvm_mmu_slot_apply_flags(kvm, (struct kvm_memory_slot *) new);
 }
 
 void kvm_arch_flush_shadow_all(struct kvm *kvm)
index f5fef18680964164263c5fb111bf55e68bd3655c..edc8cdcd786b00627a1029c8bb3b2aedcb291d09 100644 (file)
@@ -4,6 +4,8 @@
 #include <linux/kvm_host.h>
 #include "kvm_cache_regs.h"
 
+#define MSR_IA32_CR_PAT_DEFAULT  0x0007040600070406ULL
+
 static inline void kvm_clear_exception_queue(struct kvm_vcpu *vcpu)
 {
        vcpu->arch.exception.pending = false;
@@ -160,7 +162,13 @@ int kvm_write_guest_virt_system(struct x86_emulate_ctxt *ctxt,
        gva_t addr, void *val, unsigned int bytes,
        struct x86_exception *exception);
 
+void kvm_vcpu_mtrr_init(struct kvm_vcpu *vcpu);
+u8 kvm_mtrr_get_guest_memory_type(struct kvm_vcpu *vcpu, gfn_t gfn);
 bool kvm_mtrr_valid(struct kvm_vcpu *vcpu, u32 msr, u64 data);
+int kvm_mtrr_set_msr(struct kvm_vcpu *vcpu, u32 msr, u64 data);
+int kvm_mtrr_get_msr(struct kvm_vcpu *vcpu, u32 msr, u64 *pdata);
+bool kvm_mtrr_check_gfn_range_consistency(struct kvm_vcpu *vcpu, gfn_t gfn,
+                                         int page_num);
 
 #define KVM_SUPPORTED_XCR0     (XSTATE_FP | XSTATE_SSE | XSTATE_YMM \
                                | XSTATE_BNDREGS | XSTATE_BNDCSR \
index 8f9a133cc09934c2de2fa77a1eecb14ce5f94c06..f2dc08c003eb0b4c6c9f691a1c4b9e8f556eaaac 100644 (file)
@@ -70,7 +70,7 @@
 #include <asm/e820.h>
 #include <asm/mce.h>
 #include <asm/io.h>
-#include <asm/i387.h>
+#include <asm/fpu/api.h>
 #include <asm/stackprotector.h>
 #include <asm/reboot.h>                /* for struct machine_ops */
 #include <asm/kvm_para.h>
@@ -90,7 +90,7 @@ struct lguest_data lguest_data = {
        .noirq_iret = (u32)lguest_noirq_iret,
        .kernel_address = PAGE_OFFSET,
        .blocked_interrupts = { 1 }, /* Block timer interrupts */
-       .syscall_vec = SYSCALL_VECTOR,
+       .syscall_vec = IA32_SYSCALL_VECTOR,
 };
 
 /*G:037
@@ -866,7 +866,7 @@ static void __init lguest_init_IRQ(void)
        for (i = FIRST_EXTERNAL_VECTOR; i < FIRST_SYSTEM_VECTOR; i++) {
                /* Some systems map "vectors" to interrupts weirdly.  Not us! */
                __this_cpu_write(vector_irq[i], i - FIRST_EXTERNAL_VECTOR);
-               if (i != SYSCALL_VECTOR)
+               if (i != IA32_SYSCALL_VECTOR)
                        set_intr_gate(i, irq_entries_start +
                                        8 * (i - FIRST_EXTERNAL_VECTOR));
        }
index 1530afb07c85443aac9c6e1949efd4d58e8fb51a..f2587888d987f7ce4f370fd70b4b18461581b19e 100644 (file)
@@ -17,7 +17,6 @@ clean-files := inat-tables.c
 obj-$(CONFIG_SMP) += msr-smp.o cache-smp.o
 
 lib-y := delay.o misc.o cmdline.o
-lib-y += thunk_$(BITS).o
 lib-y += usercopy_$(BITS).o usercopy.o getuser.o putuser.o
 lib-y += memcpy_$(BITS).o
 lib-$(CONFIG_RWSEM_XCHGADD_ALGORITHM) += rwsem.o
@@ -40,6 +39,6 @@ else
         lib-y += csum-partial_64.o csum-copy_64.o csum-wrappers_64.o
         lib-y += clear_page_64.o copy_page_64.o
         lib-y += memmove_64.o memset_64.o
-        lib-y += copy_user_64.o copy_user_nocache_64.o
+        lib-y += copy_user_64.o
        lib-y += cmpxchg16b_emu.o
 endif
index 00933d5e992f7a8df394ee347717365925c0f58c..9b0ca8fe80fce949d0a6b6825aec99d96946d8de 100644 (file)
 
 #include <linux/linkage.h>
 #include <asm/alternative-asm.h>
-#include <asm/dwarf2.h>
 
 /* if you want SMP support, implement these with real spinlocks */
 .macro LOCK reg
-       pushfl_cfi
+       pushfl
        cli
 .endm
 
 .macro UNLOCK reg
-       popfl_cfi
+       popfl
 .endm
 
 #define BEGIN(op) \
 .macro endp; \
-       CFI_ENDPROC; \
 ENDPROC(atomic64_##op##_386); \
 .purgem endp; \
 .endm; \
 ENTRY(atomic64_##op##_386); \
-       CFI_STARTPROC; \
        LOCK v;
 
 #define ENDP endp
index 082a85167a5b68feff28d749b0e04425a0f4adf2..db3ae85440ff7925df7b59fe07972068ae6d8b7a 100644 (file)
@@ -11,7 +11,6 @@
 
 #include <linux/linkage.h>
 #include <asm/alternative-asm.h>
-#include <asm/dwarf2.h>
 
 .macro read64 reg
        movl %ebx, %eax
 .endm
 
 ENTRY(atomic64_read_cx8)
-       CFI_STARTPROC
-
        read64 %ecx
        ret
-       CFI_ENDPROC
 ENDPROC(atomic64_read_cx8)
 
 ENTRY(atomic64_set_cx8)
-       CFI_STARTPROC
-
 1:
 /* we don't need LOCK_PREFIX since aligned 64-bit writes
  * are atomic on 586 and newer */
@@ -39,28 +33,23 @@ ENTRY(atomic64_set_cx8)
        jne 1b
 
        ret
-       CFI_ENDPROC
 ENDPROC(atomic64_set_cx8)
 
 ENTRY(atomic64_xchg_cx8)
-       CFI_STARTPROC
-
 1:
        LOCK_PREFIX
        cmpxchg8b (%esi)
        jne 1b
 
        ret
-       CFI_ENDPROC
 ENDPROC(atomic64_xchg_cx8)
 
 .macro addsub_return func ins insc
 ENTRY(atomic64_\func\()_return_cx8)
-       CFI_STARTPROC
-       pushl_cfi_reg ebp
-       pushl_cfi_reg ebx
-       pushl_cfi_reg esi
-       pushl_cfi_reg edi
+       pushl %ebp
+       pushl %ebx
+       pushl %esi
+       pushl %edi
 
        movl %eax, %esi
        movl %edx, %edi
@@ -79,12 +68,11 @@ ENTRY(atomic64_\func\()_return_cx8)
 10:
        movl %ebx, %eax
        movl %ecx, %edx
-       popl_cfi_reg edi
-       popl_cfi_reg esi
-       popl_cfi_reg ebx
-       popl_cfi_reg ebp
+       popl %edi
+       popl %esi
+       popl %ebx
+       popl %ebp
        ret
-       CFI_ENDPROC
 ENDPROC(atomic64_\func\()_return_cx8)
 .endm
 
@@ -93,8 +81,7 @@ addsub_return sub sub sbb
 
 .macro incdec_return func ins insc
 ENTRY(atomic64_\func\()_return_cx8)
-       CFI_STARTPROC
-       pushl_cfi_reg ebx
+       pushl %ebx
 
        read64 %esi
 1:
@@ -109,9 +96,8 @@ ENTRY(atomic64_\func\()_return_cx8)
 10:
        movl %ebx, %eax
        movl %ecx, %edx
-       popl_cfi_reg ebx
+       popl %ebx
        ret
-       CFI_ENDPROC
 ENDPROC(atomic64_\func\()_return_cx8)
 .endm
 
@@ -119,8 +105,7 @@ incdec_return inc add adc
 incdec_return dec sub sbb
 
 ENTRY(atomic64_dec_if_positive_cx8)
-       CFI_STARTPROC
-       pushl_cfi_reg ebx
+       pushl %ebx
 
        read64 %esi
 1:
@@ -136,18 +121,16 @@ ENTRY(atomic64_dec_if_positive_cx8)
 2:
        movl %ebx, %eax
        movl %ecx, %edx
-       popl_cfi_reg ebx
+       popl %ebx
        ret
-       CFI_ENDPROC
 ENDPROC(atomic64_dec_if_positive_cx8)
 
 ENTRY(atomic64_add_unless_cx8)
-       CFI_STARTPROC
-       pushl_cfi_reg ebp
-       pushl_cfi_reg ebx
+       pushl %ebp
+       pushl %ebx
 /* these just push these two parameters on the stack */
-       pushl_cfi_reg edi
-       pushl_cfi_reg ecx
+       pushl %edi
+       pushl %ecx
 
        movl %eax, %ebp
        movl %edx, %edi
@@ -168,21 +151,18 @@ ENTRY(atomic64_add_unless_cx8)
        movl $1, %eax
 3:
        addl $8, %esp
-       CFI_ADJUST_CFA_OFFSET -8
-       popl_cfi_reg ebx
-       popl_cfi_reg ebp
+       popl %ebx
+       popl %ebp
        ret
 4:
        cmpl %edx, 4(%esp)
        jne 2b
        xorl %eax, %eax
        jmp 3b
-       CFI_ENDPROC
 ENDPROC(atomic64_add_unless_cx8)
 
 ENTRY(atomic64_inc_not_zero_cx8)
-       CFI_STARTPROC
-       pushl_cfi_reg ebx
+       pushl %ebx
 
        read64 %esi
 1:
@@ -199,7 +179,6 @@ ENTRY(atomic64_inc_not_zero_cx8)
 
        movl $1, %eax
 3:
-       popl_cfi_reg ebx
+       popl %ebx
        ret
-       CFI_ENDPROC
 ENDPROC(atomic64_inc_not_zero_cx8)
index 9bc944a9127481ead40689a73054d80e50f0bc10..c1e6232098531f0d8ce14d492215041994443b81 100644 (file)
@@ -26,7 +26,6 @@
  */
 
 #include <linux/linkage.h>
-#include <asm/dwarf2.h>
 #include <asm/errno.h>
 #include <asm/asm.h>
                                
@@ -50,9 +49,8 @@ unsigned int csum_partial(const unsigned char * buff, int len, unsigned int sum)
           * alignment for the unrolled loop.
           */           
 ENTRY(csum_partial)
-       CFI_STARTPROC
-       pushl_cfi_reg esi
-       pushl_cfi_reg ebx
+       pushl %esi
+       pushl %ebx
        movl 20(%esp),%eax      # Function arg: unsigned int sum
        movl 16(%esp),%ecx      # Function arg: int len
        movl 12(%esp),%esi      # Function arg: unsigned char *buff
@@ -129,10 +127,9 @@ ENTRY(csum_partial)
        jz 8f
        roll $8, %eax
 8:
-       popl_cfi_reg ebx
-       popl_cfi_reg esi
+       popl %ebx
+       popl %esi
        ret
-       CFI_ENDPROC
 ENDPROC(csum_partial)
 
 #else
@@ -140,9 +137,8 @@ ENDPROC(csum_partial)
 /* Version for PentiumII/PPro */
 
 ENTRY(csum_partial)
-       CFI_STARTPROC
-       pushl_cfi_reg esi
-       pushl_cfi_reg ebx
+       pushl %esi
+       pushl %ebx
        movl 20(%esp),%eax      # Function arg: unsigned int sum
        movl 16(%esp),%ecx      # Function arg: int len
        movl 12(%esp),%esi      # Function arg: const unsigned char *buf
@@ -249,10 +245,9 @@ ENTRY(csum_partial)
        jz 90f
        roll $8, %eax
 90: 
-       popl_cfi_reg ebx
-       popl_cfi_reg esi
+       popl %ebx
+       popl %esi
        ret
-       CFI_ENDPROC
 ENDPROC(csum_partial)
                                
 #endif
@@ -287,12 +282,10 @@ unsigned int csum_partial_copy_generic (const char *src, char *dst,
 #define FP             12
                
 ENTRY(csum_partial_copy_generic)
-       CFI_STARTPROC
        subl  $4,%esp   
-       CFI_ADJUST_CFA_OFFSET 4
-       pushl_cfi_reg edi
-       pushl_cfi_reg esi
-       pushl_cfi_reg ebx
+       pushl %edi
+       pushl %esi
+       pushl %ebx
        movl ARGBASE+16(%esp),%eax      # sum
        movl ARGBASE+12(%esp),%ecx      # len
        movl ARGBASE+4(%esp),%esi       # src
@@ -401,12 +394,11 @@ DST(      movb %cl, (%edi)        )
 
 .previous
 
-       popl_cfi_reg ebx
-       popl_cfi_reg esi
-       popl_cfi_reg edi
-       popl_cfi %ecx                   # equivalent to addl $4,%esp
+       popl %ebx
+       popl %esi
+       popl %edi
+       popl %ecx                       # equivalent to addl $4,%esp
        ret     
-       CFI_ENDPROC
 ENDPROC(csum_partial_copy_generic)
 
 #else
@@ -426,10 +418,9 @@ ENDPROC(csum_partial_copy_generic)
 #define ARGBASE 12
                
 ENTRY(csum_partial_copy_generic)
-       CFI_STARTPROC
-       pushl_cfi_reg ebx
-       pushl_cfi_reg edi
-       pushl_cfi_reg esi
+       pushl %ebx
+       pushl %edi
+       pushl %esi
        movl ARGBASE+4(%esp),%esi       #src
        movl ARGBASE+8(%esp),%edi       #dst    
        movl ARGBASE+12(%esp),%ecx      #len
@@ -489,11 +480,10 @@ DST(      movb %dl, (%edi)         )
        jmp  7b                 
 .previous                              
 
-       popl_cfi_reg esi
-       popl_cfi_reg edi
-       popl_cfi_reg ebx
+       popl %esi
+       popl %edi
+       popl %ebx
        ret
-       CFI_ENDPROC
 ENDPROC(csum_partial_copy_generic)
                                
 #undef ROUND
index e67e579c93bdf7f3d0737565ea106edeeefb3b6d..a2fe51b00ccefc660850e1a6810dd4adee611d2e 100644 (file)
@@ -1,5 +1,4 @@
 #include <linux/linkage.h>
-#include <asm/dwarf2.h>
 #include <asm/cpufeature.h>
 #include <asm/alternative-asm.h>
 
@@ -15,7 +14,6 @@
  * %rdi        - page
  */
 ENTRY(clear_page)
-       CFI_STARTPROC
 
        ALTERNATIVE_2 "jmp clear_page_orig", "", X86_FEATURE_REP_GOOD, \
                      "jmp clear_page_c_e", X86_FEATURE_ERMS
@@ -24,11 +22,9 @@ ENTRY(clear_page)
        xorl %eax,%eax
        rep stosq
        ret
-       CFI_ENDPROC
 ENDPROC(clear_page)
 
 ENTRY(clear_page_orig)
-       CFI_STARTPROC
 
        xorl   %eax,%eax
        movl   $4096/64,%ecx
@@ -48,14 +44,11 @@ ENTRY(clear_page_orig)
        jnz     .Lloop
        nop
        ret
-       CFI_ENDPROC
 ENDPROC(clear_page_orig)
 
 ENTRY(clear_page_c_e)
-       CFI_STARTPROC
        movl $4096,%ecx
        xorl %eax,%eax
        rep stosb
        ret
-       CFI_ENDPROC
 ENDPROC(clear_page_c_e)
index 40a172541ee2cb2c2342a0a9934948769e87337f..9b330242e7408125d6865e8f753c61ab9ae64802 100644 (file)
@@ -6,7 +6,6 @@
  *
  */
 #include <linux/linkage.h>
-#include <asm/dwarf2.h>
 #include <asm/percpu.h>
 
 .text
@@ -21,7 +20,6 @@
  * %al  : Operation successful
  */
 ENTRY(this_cpu_cmpxchg16b_emu)
-CFI_STARTPROC
 
 #
 # Emulate 'cmpxchg16b %gs:(%rsi)' except we return the result in %al not
@@ -32,7 +30,7 @@ CFI_STARTPROC
 # *atomic* on a single cpu (as provided by the this_cpu_xx class of
 # macros).
 #
-       pushfq_cfi
+       pushfq
        cli
 
        cmpq PER_CPU_VAR((%rsi)), %rax
@@ -43,17 +41,13 @@ CFI_STARTPROC
        movq %rbx, PER_CPU_VAR((%rsi))
        movq %rcx, PER_CPU_VAR(8(%rsi))
 
-       CFI_REMEMBER_STATE
-       popfq_cfi
+       popfq
        mov $1, %al
        ret
 
-       CFI_RESTORE_STATE
 .Lnot_same:
-       popfq_cfi
+       popfq
        xor %al,%al
        ret
 
-CFI_ENDPROC
-
 ENDPROC(this_cpu_cmpxchg16b_emu)
index b4807fce517760ac4a72dceae939ae322b0bda6b..ad53497784904b2c1f420fd41576c9ffeea8ce0b 100644 (file)
@@ -7,7 +7,6 @@
  */
 
 #include <linux/linkage.h>
-#include <asm/dwarf2.h>
 
 .text
 
  * %ecx : high 32 bits of new value
  */
 ENTRY(cmpxchg8b_emu)
-CFI_STARTPROC
 
 #
 # Emulate 'cmpxchg8b (%esi)' on UP except we don't
 # set the whole ZF thing (caller will just compare
 # eax:edx with the expected value)
 #
-       pushfl_cfi
+       pushfl
        cli
 
        cmpl  (%esi), %eax
@@ -38,18 +36,15 @@ CFI_STARTPROC
        movl %ebx,  (%esi)
        movl %ecx, 4(%esi)
 
-       CFI_REMEMBER_STATE
-       popfl_cfi
+       popfl
        ret
 
-       CFI_RESTORE_STATE
 .Lnot_same:
        movl  (%esi), %eax
 .Lhalf_same:
        movl 4(%esi), %edx
 
-       popfl_cfi
+       popfl
        ret
 
-CFI_ENDPROC
 ENDPROC(cmpxchg8b_emu)
index 8239dbcbf98455a99a125953392114f07a09aa74..009f98216b7eb316c12847e42f50ac77d4f4b8a3 100644 (file)
@@ -1,7 +1,6 @@
 /* Written 2003 by Andi Kleen, based on a kernel by Evandro Menezes */
 
 #include <linux/linkage.h>
-#include <asm/dwarf2.h>
 #include <asm/cpufeature.h>
 #include <asm/alternative-asm.h>
 
  */
        ALIGN
 ENTRY(copy_page)
-       CFI_STARTPROC
        ALTERNATIVE "jmp copy_page_regs", "", X86_FEATURE_REP_GOOD
        movl    $4096/8, %ecx
        rep     movsq
        ret
-       CFI_ENDPROC
 ENDPROC(copy_page)
 
 ENTRY(copy_page_regs)
-       CFI_STARTPROC
        subq    $2*8,   %rsp
-       CFI_ADJUST_CFA_OFFSET 2*8
        movq    %rbx,   (%rsp)
-       CFI_REL_OFFSET rbx, 0
        movq    %r12,   1*8(%rsp)
-       CFI_REL_OFFSET r12, 1*8
 
        movl    $(4096/64)-5,   %ecx
        .p2align 4
@@ -87,11 +80,7 @@ ENTRY(copy_page_regs)
        jnz     .Loop2
 
        movq    (%rsp), %rbx
-       CFI_RESTORE rbx
        movq    1*8(%rsp), %r12
-       CFI_RESTORE r12
        addq    $2*8, %rsp
-       CFI_ADJUST_CFA_OFFSET -2*8
        ret
-       CFI_ENDPROC
 ENDPROC(copy_page_regs)
index fa997dfaef242fa9abdb28c20658a939caf72697..982ce34f4a9bf66011fc2652b45466d9c2b276f9 100644 (file)
@@ -7,7 +7,6 @@
  */
 
 #include <linux/linkage.h>
-#include <asm/dwarf2.h>
 #include <asm/current.h>
 #include <asm/asm-offsets.h>
 #include <asm/thread_info.h>
 #include <asm/asm.h>
 #include <asm/smap.h>
 
-       .macro ALIGN_DESTINATION
-       /* check for bad alignment of destination */
-       movl %edi,%ecx
-       andl $7,%ecx
-       jz 102f                         /* already aligned */
-       subl $8,%ecx
-       negl %ecx
-       subl %ecx,%edx
-100:   movb (%rsi),%al
-101:   movb %al,(%rdi)
-       incq %rsi
-       incq %rdi
-       decl %ecx
-       jnz 100b
-102:
-       .section .fixup,"ax"
-103:   addl %ecx,%edx                  /* ecx is zerorest also */
-       jmp copy_user_handle_tail
-       .previous
-
-       _ASM_EXTABLE(100b,103b)
-       _ASM_EXTABLE(101b,103b)
-       .endm
-
 /* Standard copy_to_user with segment limit checking */
 ENTRY(_copy_to_user)
-       CFI_STARTPROC
        GET_THREAD_INFO(%rax)
        movq %rdi,%rcx
        addq %rdx,%rcx
@@ -54,12 +28,10 @@ ENTRY(_copy_to_user)
                      X86_FEATURE_REP_GOOD,                     \
                      "jmp copy_user_enhanced_fast_string",     \
                      X86_FEATURE_ERMS
-       CFI_ENDPROC
 ENDPROC(_copy_to_user)
 
 /* Standard copy_from_user with segment limit checking */
 ENTRY(_copy_from_user)
-       CFI_STARTPROC
        GET_THREAD_INFO(%rax)
        movq %rsi,%rcx
        addq %rdx,%rcx
@@ -71,14 +43,12 @@ ENTRY(_copy_from_user)
                      X86_FEATURE_REP_GOOD,                     \
                      "jmp copy_user_enhanced_fast_string",     \
                      X86_FEATURE_ERMS
-       CFI_ENDPROC
 ENDPROC(_copy_from_user)
 
        .section .fixup,"ax"
        /* must zero dest */
 ENTRY(bad_from_user)
 bad_from_user:
-       CFI_STARTPROC
        movl %edx,%ecx
        xorl %eax,%eax
        rep
@@ -86,7 +56,6 @@ bad_from_user:
 bad_to_user:
        movl %edx,%eax
        ret
-       CFI_ENDPROC
 ENDPROC(bad_from_user)
        .previous
 
@@ -104,7 +73,6 @@ ENDPROC(bad_from_user)
  * eax uncopied bytes or 0 if successful.
  */
 ENTRY(copy_user_generic_unrolled)
-       CFI_STARTPROC
        ASM_STAC
        cmpl $8,%edx
        jb 20f          /* less then 8 bytes, go to byte copy loop */
@@ -186,7 +154,6 @@ ENTRY(copy_user_generic_unrolled)
        _ASM_EXTABLE(19b,40b)
        _ASM_EXTABLE(21b,50b)
        _ASM_EXTABLE(22b,50b)
-       CFI_ENDPROC
 ENDPROC(copy_user_generic_unrolled)
 
 /* Some CPUs run faster using the string copy instructions.
@@ -208,7 +175,6 @@ ENDPROC(copy_user_generic_unrolled)
  * eax uncopied bytes or 0 if successful.
  */
 ENTRY(copy_user_generic_string)
-       CFI_STARTPROC
        ASM_STAC
        cmpl $8,%edx
        jb 2f           /* less than 8 bytes, go to byte copy loop */
@@ -233,7 +199,6 @@ ENTRY(copy_user_generic_string)
 
        _ASM_EXTABLE(1b,11b)
        _ASM_EXTABLE(3b,12b)
-       CFI_ENDPROC
 ENDPROC(copy_user_generic_string)
 
 /*
@@ -249,7 +214,6 @@ ENDPROC(copy_user_generic_string)
  * eax uncopied bytes or 0 if successful.
  */
 ENTRY(copy_user_enhanced_fast_string)
-       CFI_STARTPROC
        ASM_STAC
        movl %edx,%ecx
 1:     rep
@@ -264,5 +228,94 @@ ENTRY(copy_user_enhanced_fast_string)
        .previous
 
        _ASM_EXTABLE(1b,12b)
-       CFI_ENDPROC
 ENDPROC(copy_user_enhanced_fast_string)
+
+/*
+ * copy_user_nocache - Uncached memory copy with exception handling
+ * This will force destination/source out of cache for more performance.
+ */
+ENTRY(__copy_user_nocache)
+       ASM_STAC
+       cmpl $8,%edx
+       jb 20f          /* less then 8 bytes, go to byte copy loop */
+       ALIGN_DESTINATION
+       movl %edx,%ecx
+       andl $63,%edx
+       shrl $6,%ecx
+       jz 17f
+1:     movq (%rsi),%r8
+2:     movq 1*8(%rsi),%r9
+3:     movq 2*8(%rsi),%r10
+4:     movq 3*8(%rsi),%r11
+5:     movnti %r8,(%rdi)
+6:     movnti %r9,1*8(%rdi)
+7:     movnti %r10,2*8(%rdi)
+8:     movnti %r11,3*8(%rdi)
+9:     movq 4*8(%rsi),%r8
+10:    movq 5*8(%rsi),%r9
+11:    movq 6*8(%rsi),%r10
+12:    movq 7*8(%rsi),%r11
+13:    movnti %r8,4*8(%rdi)
+14:    movnti %r9,5*8(%rdi)
+15:    movnti %r10,6*8(%rdi)
+16:    movnti %r11,7*8(%rdi)
+       leaq 64(%rsi),%rsi
+       leaq 64(%rdi),%rdi
+       decl %ecx
+       jnz 1b
+17:    movl %edx,%ecx
+       andl $7,%edx
+       shrl $3,%ecx
+       jz 20f
+18:    movq (%rsi),%r8
+19:    movnti %r8,(%rdi)
+       leaq 8(%rsi),%rsi
+       leaq 8(%rdi),%rdi
+       decl %ecx
+       jnz 18b
+20:    andl %edx,%edx
+       jz 23f
+       movl %edx,%ecx
+21:    movb (%rsi),%al
+22:    movb %al,(%rdi)
+       incq %rsi
+       incq %rdi
+       decl %ecx
+       jnz 21b
+23:    xorl %eax,%eax
+       ASM_CLAC
+       sfence
+       ret
+
+       .section .fixup,"ax"
+30:    shll $6,%ecx
+       addl %ecx,%edx
+       jmp 60f
+40:    lea (%rdx,%rcx,8),%rdx
+       jmp 60f
+50:    movl %ecx,%edx
+60:    sfence
+       jmp copy_user_handle_tail
+       .previous
+
+       _ASM_EXTABLE(1b,30b)
+       _ASM_EXTABLE(2b,30b)
+       _ASM_EXTABLE(3b,30b)
+       _ASM_EXTABLE(4b,30b)
+       _ASM_EXTABLE(5b,30b)
+       _ASM_EXTABLE(6b,30b)
+       _ASM_EXTABLE(7b,30b)
+       _ASM_EXTABLE(8b,30b)
+       _ASM_EXTABLE(9b,30b)
+       _ASM_EXTABLE(10b,30b)
+       _ASM_EXTABLE(11b,30b)
+       _ASM_EXTABLE(12b,30b)
+       _ASM_EXTABLE(13b,30b)
+       _ASM_EXTABLE(14b,30b)
+       _ASM_EXTABLE(15b,30b)
+       _ASM_EXTABLE(16b,30b)
+       _ASM_EXTABLE(18b,40b)
+       _ASM_EXTABLE(19b,40b)
+       _ASM_EXTABLE(21b,50b)
+       _ASM_EXTABLE(22b,50b)
+ENDPROC(__copy_user_nocache)
diff --git a/arch/x86/lib/copy_user_nocache_64.S b/arch/x86/lib/copy_user_nocache_64.S
deleted file mode 100644 (file)
index 6a4f43c..0000000
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * Copyright 2008 Vitaly Mayatskikh <vmayatsk@redhat.com>
- * Copyright 2002 Andi Kleen, SuSE Labs.
- * Subject to the GNU Public License v2.
- *
- * Functions to copy from and to user space.
- */
-
-#include <linux/linkage.h>
-#include <asm/dwarf2.h>
-
-#define FIX_ALIGNMENT 1
-
-#include <asm/current.h>
-#include <asm/asm-offsets.h>
-#include <asm/thread_info.h>
-#include <asm/asm.h>
-#include <asm/smap.h>
-
-       .macro ALIGN_DESTINATION
-#ifdef FIX_ALIGNMENT
-       /* check for bad alignment of destination */
-       movl %edi,%ecx
-       andl $7,%ecx
-       jz 102f                         /* already aligned */
-       subl $8,%ecx
-       negl %ecx
-       subl %ecx,%edx
-100:   movb (%rsi),%al
-101:   movb %al,(%rdi)
-       incq %rsi
-       incq %rdi
-       decl %ecx
-       jnz 100b
-102:
-       .section .fixup,"ax"
-103:   addl %ecx,%edx                  /* ecx is zerorest also */
-       jmp copy_user_handle_tail
-       .previous
-
-       _ASM_EXTABLE(100b,103b)
-       _ASM_EXTABLE(101b,103b)
-#endif
-       .endm
-
-/*
- * copy_user_nocache - Uncached memory copy with exception handling
- * This will force destination/source out of cache for more performance.
- */
-ENTRY(__copy_user_nocache)
-       CFI_STARTPROC
-       ASM_STAC
-       cmpl $8,%edx
-       jb 20f          /* less then 8 bytes, go to byte copy loop */
-       ALIGN_DESTINATION
-       movl %edx,%ecx
-       andl $63,%edx
-       shrl $6,%ecx
-       jz 17f
-1:     movq (%rsi),%r8
-2:     movq 1*8(%rsi),%r9
-3:     movq 2*8(%rsi),%r10
-4:     movq 3*8(%rsi),%r11
-5:     movnti %r8,(%rdi)
-6:     movnti %r9,1*8(%rdi)
-7:     movnti %r10,2*8(%rdi)
-8:     movnti %r11,3*8(%rdi)
-9:     movq 4*8(%rsi),%r8
-10:    movq 5*8(%rsi),%r9
-11:    movq 6*8(%rsi),%r10
-12:    movq 7*8(%rsi),%r11
-13:    movnti %r8,4*8(%rdi)
-14:    movnti %r9,5*8(%rdi)
-15:    movnti %r10,6*8(%rdi)
-16:    movnti %r11,7*8(%rdi)
-       leaq 64(%rsi),%rsi
-       leaq 64(%rdi),%rdi
-       decl %ecx
-       jnz 1b
-17:    movl %edx,%ecx
-       andl $7,%edx
-       shrl $3,%ecx
-       jz 20f
-18:    movq (%rsi),%r8
-19:    movnti %r8,(%rdi)
-       leaq 8(%rsi),%rsi
-       leaq 8(%rdi),%rdi
-       decl %ecx
-       jnz 18b
-20:    andl %edx,%edx
-       jz 23f
-       movl %edx,%ecx
-21:    movb (%rsi),%al
-22:    movb %al,(%rdi)
-       incq %rsi
-       incq %rdi
-       decl %ecx
-       jnz 21b
-23:    xorl %eax,%eax
-       ASM_CLAC
-       sfence
-       ret
-
-       .section .fixup,"ax"
-30:    shll $6,%ecx
-       addl %ecx,%edx
-       jmp 60f
-40:    lea (%rdx,%rcx,8),%rdx
-       jmp 60f
-50:    movl %ecx,%edx
-60:    sfence
-       jmp copy_user_handle_tail
-       .previous
-
-       _ASM_EXTABLE(1b,30b)
-       _ASM_EXTABLE(2b,30b)
-       _ASM_EXTABLE(3b,30b)
-       _ASM_EXTABLE(4b,30b)
-       _ASM_EXTABLE(5b,30b)
-       _ASM_EXTABLE(6b,30b)
-       _ASM_EXTABLE(7b,30b)
-       _ASM_EXTABLE(8b,30b)
-       _ASM_EXTABLE(9b,30b)
-       _ASM_EXTABLE(10b,30b)
-       _ASM_EXTABLE(11b,30b)
-       _ASM_EXTABLE(12b,30b)
-       _ASM_EXTABLE(13b,30b)
-       _ASM_EXTABLE(14b,30b)
-       _ASM_EXTABLE(15b,30b)
-       _ASM_EXTABLE(16b,30b)
-       _ASM_EXTABLE(18b,40b)
-       _ASM_EXTABLE(19b,40b)
-       _ASM_EXTABLE(21b,50b)
-       _ASM_EXTABLE(22b,50b)
-       CFI_ENDPROC
-ENDPROC(__copy_user_nocache)
index 9734182966f3be925a38c0762aa71e3148156148..7e48807b2fa198c4e2b8e7022df2218308a41b7c 100644 (file)
@@ -6,7 +6,6 @@
  * for more details. No warranty for anything given at all.
  */
 #include <linux/linkage.h>
-#include <asm/dwarf2.h>
 #include <asm/errno.h>
 #include <asm/asm.h>
 
 
 
 ENTRY(csum_partial_copy_generic)
-       CFI_STARTPROC
        cmpl    $3*64, %edx
        jle     .Lignore
 
 .Lignore:
        subq  $7*8, %rsp
-       CFI_ADJUST_CFA_OFFSET 7*8
        movq  %rbx, 2*8(%rsp)
-       CFI_REL_OFFSET rbx, 2*8
        movq  %r12, 3*8(%rsp)
-       CFI_REL_OFFSET r12, 3*8
        movq  %r14, 4*8(%rsp)
-       CFI_REL_OFFSET r14, 4*8
        movq  %r13, 5*8(%rsp)
-       CFI_REL_OFFSET r13, 5*8
        movq  %rbp, 6*8(%rsp)
-       CFI_REL_OFFSET rbp, 6*8
 
        movq  %r8, (%rsp)
        movq  %r9, 1*8(%rsp)
@@ -206,22 +198,14 @@ ENTRY(csum_partial_copy_generic)
        addl %ebx, %eax
        adcl %r9d, %eax         /* carry */
 
-       CFI_REMEMBER_STATE
 .Lende:
        movq 2*8(%rsp), %rbx
-       CFI_RESTORE rbx
        movq 3*8(%rsp), %r12
-       CFI_RESTORE r12
        movq 4*8(%rsp), %r14
-       CFI_RESTORE r14
        movq 5*8(%rsp), %r13
-       CFI_RESTORE r13
        movq 6*8(%rsp), %rbp
-       CFI_RESTORE rbp
        addq $7*8, %rsp
-       CFI_ADJUST_CFA_OFFSET -7*8
        ret
-       CFI_RESTORE_STATE
 
        /* Exception handlers. Very simple, zeroing is done in the wrappers */
 .Lbad_source:
@@ -237,5 +221,4 @@ ENTRY(csum_partial_copy_generic)
        jz   .Lende
        movl $-EFAULT, (%rax)
        jmp .Lende
-       CFI_ENDPROC
 ENDPROC(csum_partial_copy_generic)
index a4512359656aea8fcda1d2c19d45f3bb4936bee3..46668cda4ffdfd5af38abe5ca973ca15aa19d8d8 100644 (file)
@@ -26,7 +26,6 @@
  */
 
 #include <linux/linkage.h>
-#include <asm/dwarf2.h>
 #include <asm/page_types.h>
 #include <asm/errno.h>
 #include <asm/asm-offsets.h>
@@ -36,7 +35,6 @@
 
        .text
 ENTRY(__get_user_1)
-       CFI_STARTPROC
        GET_THREAD_INFO(%_ASM_DX)
        cmp TI_addr_limit(%_ASM_DX),%_ASM_AX
        jae bad_get_user
@@ -45,11 +43,9 @@ ENTRY(__get_user_1)
        xor %eax,%eax
        ASM_CLAC
        ret
-       CFI_ENDPROC
 ENDPROC(__get_user_1)
 
 ENTRY(__get_user_2)
-       CFI_STARTPROC
        add $1,%_ASM_AX
        jc bad_get_user
        GET_THREAD_INFO(%_ASM_DX)
@@ -60,11 +56,9 @@ ENTRY(__get_user_2)
        xor %eax,%eax
        ASM_CLAC
        ret
-       CFI_ENDPROC
 ENDPROC(__get_user_2)
 
 ENTRY(__get_user_4)
-       CFI_STARTPROC
        add $3,%_ASM_AX
        jc bad_get_user
        GET_THREAD_INFO(%_ASM_DX)
@@ -75,11 +69,9 @@ ENTRY(__get_user_4)
        xor %eax,%eax
        ASM_CLAC
        ret
-       CFI_ENDPROC
 ENDPROC(__get_user_4)
 
 ENTRY(__get_user_8)
-       CFI_STARTPROC
 #ifdef CONFIG_X86_64
        add $7,%_ASM_AX
        jc bad_get_user
@@ -104,28 +96,23 @@ ENTRY(__get_user_8)
        ASM_CLAC
        ret
 #endif
-       CFI_ENDPROC
 ENDPROC(__get_user_8)
 
 
 bad_get_user:
-       CFI_STARTPROC
        xor %edx,%edx
        mov $(-EFAULT),%_ASM_AX
        ASM_CLAC
        ret
-       CFI_ENDPROC
 END(bad_get_user)
 
 #ifdef CONFIG_X86_32
 bad_get_user_8:
-       CFI_STARTPROC
        xor %edx,%edx
        xor %ecx,%ecx
        mov $(-EFAULT),%_ASM_AX
        ASM_CLAC
        ret
-       CFI_ENDPROC
 END(bad_get_user_8)
 #endif
 
index 05a95e713da885e8686bc2bba1fdf544c03d2d74..33147fef3452ce5bad3a6540ad5234ada774d96a 100644 (file)
  */
 
 #include <linux/linkage.h>
-#include <asm/dwarf2.h>
 
 /*
  * override generic version in lib/iomap_copy.c
  */
 ENTRY(__iowrite32_copy)
-       CFI_STARTPROC
        movl %edx,%ecx
        rep movsd
        ret
-       CFI_ENDPROC
 ENDPROC(__iowrite32_copy)
index b046664f5a1ccf37f3c94b2ed7d8d5b3298e89bf..16698bba87deb9ff593d934e8a8fb1447be9b214 100644 (file)
@@ -2,7 +2,6 @@
 
 #include <linux/linkage.h>
 #include <asm/cpufeature.h>
-#include <asm/dwarf2.h>
 #include <asm/alternative-asm.h>
 
 /*
@@ -53,7 +52,6 @@ ENTRY(memcpy_erms)
 ENDPROC(memcpy_erms)
 
 ENTRY(memcpy_orig)
-       CFI_STARTPROC
        movq %rdi, %rax
 
        cmpq $0x20, %rdx
@@ -178,5 +176,4 @@ ENTRY(memcpy_orig)
 
 .Lend:
        retq
-       CFI_ENDPROC
 ENDPROC(memcpy_orig)
index 0f8a0d0331b91715238f01e8f54ce74525fb0cc4..ca2afdd6d98ed2be90da6f9ea1624beb102f0fc3 100644 (file)
@@ -6,7 +6,6 @@
  *     - Copyright 2011 Fenghua Yu <fenghua.yu@intel.com>
  */
 #include <linux/linkage.h>
-#include <asm/dwarf2.h>
 #include <asm/cpufeature.h>
 #include <asm/alternative-asm.h>
 
@@ -27,7 +26,6 @@
 
 ENTRY(memmove)
 ENTRY(__memmove)
-       CFI_STARTPROC
 
        /* Handle more 32 bytes in loop */
        mov %rdi, %rax
@@ -207,6 +205,5 @@ ENTRY(__memmove)
        movb %r11b, (%rdi)
 13:
        retq
-       CFI_ENDPROC
 ENDPROC(__memmove)
 ENDPROC(memmove)
index 93118fb239762ba78efd754d20a756aed0221411..2661fad0582716f780af9904dc5b7c62199a5c58 100644 (file)
@@ -1,7 +1,6 @@
 /* Copyright 2002 Andi Kleen, SuSE Labs */
 
 #include <linux/linkage.h>
-#include <asm/dwarf2.h>
 #include <asm/cpufeature.h>
 #include <asm/alternative-asm.h>
 
@@ -66,7 +65,6 @@ ENTRY(memset_erms)
 ENDPROC(memset_erms)
 
 ENTRY(memset_orig)
-       CFI_STARTPROC
        movq %rdi,%r10
 
        /* expand byte value  */
@@ -78,7 +76,6 @@ ENTRY(memset_orig)
        movl  %edi,%r9d
        andl  $7,%r9d
        jnz  .Lbad_alignment
-       CFI_REMEMBER_STATE
 .Lafter_bad_alignment:
 
        movq  %rdx,%rcx
@@ -128,7 +125,6 @@ ENTRY(memset_orig)
        movq    %r10,%rax
        ret
 
-       CFI_RESTORE_STATE
 .Lbad_alignment:
        cmpq $7,%rdx
        jbe     .Lhandle_7
@@ -139,5 +135,4 @@ ENTRY(memset_orig)
        subq %r8,%rdx
        jmp .Lafter_bad_alignment
 .Lfinal:
-       CFI_ENDPROC
 ENDPROC(memset_orig)
index c9f2d9ba8dd8c2da54b0bbd07c37be1d38aa49c4..e5e3ed8dc0798bd007e8573ddbf57dc4e2312049 100644 (file)
@@ -22,7 +22,7 @@
 #include <linux/sched.h>
 #include <linux/types.h>
 
-#include <asm/i387.h>
+#include <asm/fpu/api.h>
 #include <asm/asm.h>
 
 void *_mmx_memcpy(void *to, const void *from, size_t len)
index 3ca5218fbece0c568ed6079ee2370ff1cedf71f8..c81556409bbb87cfbfb5b2b8b2cdd26e8e916730 100644 (file)
@@ -1,6 +1,5 @@
 #include <linux/linkage.h>
 #include <linux/errno.h>
-#include <asm/dwarf2.h>
 #include <asm/asm.h>
 #include <asm/msr.h>
 
@@ -13,9 +12,8 @@
  */
 .macro op_safe_regs op
 ENTRY(\op\()_safe_regs)
-       CFI_STARTPROC
-       pushq_cfi_reg rbx
-       pushq_cfi_reg rbp
+       pushq %rbx
+       pushq %rbp
        movq    %rdi, %r10      /* Save pointer */
        xorl    %r11d, %r11d    /* Return value */
        movl    (%rdi), %eax
@@ -25,7 +23,6 @@ ENTRY(\op\()_safe_regs)
        movl    20(%rdi), %ebp
        movl    24(%rdi), %esi
        movl    28(%rdi), %edi
-       CFI_REMEMBER_STATE
 1:     \op
 2:     movl    %eax, (%r10)
        movl    %r11d, %eax     /* Return value */
@@ -35,16 +32,14 @@ ENTRY(\op\()_safe_regs)
        movl    %ebp, 20(%r10)
        movl    %esi, 24(%r10)
        movl    %edi, 28(%r10)
-       popq_cfi_reg rbp
-       popq_cfi_reg rbx
+       popq %rbp
+       popq %rbx
        ret
 3:
-       CFI_RESTORE_STATE
        movl    $-EIO, %r11d
        jmp     2b
 
        _ASM_EXTABLE(1b, 3b)
-       CFI_ENDPROC
 ENDPROC(\op\()_safe_regs)
 .endm
 
@@ -52,13 +47,12 @@ ENDPROC(\op\()_safe_regs)
 
 .macro op_safe_regs op
 ENTRY(\op\()_safe_regs)
-       CFI_STARTPROC
-       pushl_cfi_reg ebx
-       pushl_cfi_reg ebp
-       pushl_cfi_reg esi
-       pushl_cfi_reg edi
-       pushl_cfi $0              /* Return value */
-       pushl_cfi %eax
+       pushl %ebx
+       pushl %ebp
+       pushl %esi
+       pushl %edi
+       pushl $0              /* Return value */
+       pushl %eax
        movl    4(%eax), %ecx
        movl    8(%eax), %edx
        movl    12(%eax), %ebx
@@ -66,32 +60,28 @@ ENTRY(\op\()_safe_regs)
        movl    24(%eax), %esi
        movl    28(%eax), %edi
        movl    (%eax), %eax
-       CFI_REMEMBER_STATE
 1:     \op
-2:     pushl_cfi %eax
+2:     pushl %eax
        movl    4(%esp), %eax
-       popl_cfi (%eax)
+       popl (%eax)
        addl    $4, %esp
-       CFI_ADJUST_CFA_OFFSET -4
        movl    %ecx, 4(%eax)
        movl    %edx, 8(%eax)
        movl    %ebx, 12(%eax)
        movl    %ebp, 20(%eax)
        movl    %esi, 24(%eax)
        movl    %edi, 28(%eax)
-       popl_cfi %eax
-       popl_cfi_reg edi
-       popl_cfi_reg esi
-       popl_cfi_reg ebp
-       popl_cfi_reg ebx
+       popl %eax
+       popl %edi
+       popl %esi
+       popl %ebp
+       popl %ebx
        ret
 3:
-       CFI_RESTORE_STATE
        movl    $-EIO, 4(%esp)
        jmp     2b
 
        _ASM_EXTABLE(1b, 3b)
-       CFI_ENDPROC
 ENDPROC(\op\()_safe_regs)
 .endm
 
index fc6ba17a7eec2a957fc2022d8a370bb1b9e5b15f..e0817a12d32362b4e69687c7b09e424580616e5d 100644 (file)
@@ -11,7 +11,6 @@
  * return value.
  */
 #include <linux/linkage.h>
-#include <asm/dwarf2.h>
 #include <asm/thread_info.h>
 #include <asm/errno.h>
 #include <asm/asm.h>
  * as they get called from within inline assembly.
  */
 
-#define ENTER  CFI_STARTPROC ; \
-               GET_THREAD_INFO(%_ASM_BX)
+#define ENTER  GET_THREAD_INFO(%_ASM_BX)
 #define EXIT   ASM_CLAC ;      \
-               ret ;           \
-               CFI_ENDPROC
+               ret
 
 .text
 ENTRY(__put_user_1)
@@ -87,7 +84,6 @@ ENTRY(__put_user_8)
 ENDPROC(__put_user_8)
 
 bad_put_user:
-       CFI_STARTPROC
        movl $-EFAULT,%eax
        EXIT
 END(bad_put_user)
index 2322abe4da3b014aef389c03e177c37eec31b802..40027db991405ba123a508a7d9523afe04fd744b 100644 (file)
@@ -15,7 +15,6 @@
 
 #include <linux/linkage.h>
 #include <asm/alternative-asm.h>
-#include <asm/dwarf2.h>
 
 #define __ASM_HALF_REG(reg)    __ASM_SEL(reg, e##reg)
 #define __ASM_HALF_SIZE(inst)  __ASM_SEL(inst##w, inst##l)
  */
 
 #define save_common_regs \
-       pushl_cfi_reg ecx
+       pushl %ecx
 
 #define restore_common_regs \
-       popl_cfi_reg ecx
+       popl %ecx
 
        /* Avoid uglifying the argument copying x86-64 needs to do. */
        .macro movq src, dst
  */
 
 #define save_common_regs \
-       pushq_cfi_reg rdi; \
-       pushq_cfi_reg rsi; \
-       pushq_cfi_reg rcx; \
-       pushq_cfi_reg r8;  \
-       pushq_cfi_reg r9;  \
-       pushq_cfi_reg r10; \
-       pushq_cfi_reg r11
+       pushq %rdi; \
+       pushq %rsi; \
+       pushq %rcx; \
+       pushq %r8;  \
+       pushq %r9;  \
+       pushq %r10; \
+       pushq %r11
 
 #define restore_common_regs \
-       popq_cfi_reg r11; \
-       popq_cfi_reg r10; \
-       popq_cfi_reg r9; \
-       popq_cfi_reg r8; \
-       popq_cfi_reg rcx; \
-       popq_cfi_reg rsi; \
-       popq_cfi_reg rdi
+       popq %r11; \
+       popq %r10; \
+       popq %r9; \
+       popq %r8; \
+       popq %rcx; \
+       popq %rsi; \
+       popq %rdi
 
 #endif
 
 /* Fix up special calling conventions */
 ENTRY(call_rwsem_down_read_failed)
-       CFI_STARTPROC
        save_common_regs
-       __ASM_SIZE(push,_cfi_reg) __ASM_REG(dx)
+       __ASM_SIZE(push,) %__ASM_REG(dx)
        movq %rax,%rdi
        call rwsem_down_read_failed
-       __ASM_SIZE(pop,_cfi_reg) __ASM_REG(dx)
+       __ASM_SIZE(pop,) %__ASM_REG(dx)
        restore_common_regs
        ret
-       CFI_ENDPROC
 ENDPROC(call_rwsem_down_read_failed)
 
 ENTRY(call_rwsem_down_write_failed)
-       CFI_STARTPROC
        save_common_regs
        movq %rax,%rdi
        call rwsem_down_write_failed
        restore_common_regs
        ret
-       CFI_ENDPROC
 ENDPROC(call_rwsem_down_write_failed)
 
 ENTRY(call_rwsem_wake)
-       CFI_STARTPROC
        /* do nothing if still outstanding active readers */
        __ASM_HALF_SIZE(dec) %__ASM_HALF_REG(dx)
        jnz 1f
@@ -116,17 +110,14 @@ ENTRY(call_rwsem_wake)
        call rwsem_wake
        restore_common_regs
 1:     ret
-       CFI_ENDPROC
 ENDPROC(call_rwsem_wake)
 
 ENTRY(call_rwsem_downgrade_wake)
-       CFI_STARTPROC
        save_common_regs
-       __ASM_SIZE(push,_cfi_reg) __ASM_REG(dx)
+       __ASM_SIZE(push,) %__ASM_REG(dx)
        movq %rax,%rdi
        call rwsem_downgrade_wake
-       __ASM_SIZE(pop,_cfi_reg) __ASM_REG(dx)
+       __ASM_SIZE(pop,) %__ASM_REG(dx)
        restore_common_regs
        ret
-       CFI_ENDPROC
 ENDPROC(call_rwsem_downgrade_wake)
index dc8adad10a2f3881cdbca82b9a624dc17b88c4f8..dd76a05729b0106c95ae2bd925ca21a647214fd6 100644 (file)
@@ -30,7 +30,7 @@ static void fclex(void)
 }
 
 /* Needs to be externally visible */
-void finit_soft_fpu(struct i387_soft_struct *soft)
+void fpstate_init_soft(struct swregs_state *soft)
 {
        struct address *oaddr, *iaddr;
        memset(soft, 0, sizeof(*soft));
@@ -52,7 +52,7 @@ void finit_soft_fpu(struct i387_soft_struct *soft)
 
 void finit(void)
 {
-       finit_soft_fpu(&current->thread.fpu.state->soft);
+       fpstate_init_soft(&current->thread.fpu.state.soft);
 }
 
 /*
index 9b868124128d79699d6325dc579908bc575c3f4a..f37e84ab49f38e335bde57880a6cbe8640fb2c4b 100644 (file)
@@ -31,7 +31,7 @@
 #include <asm/traps.h>
 #include <asm/desc.h>
 #include <asm/user.h>
-#include <asm/i387.h>
+#include <asm/fpu/internal.h>
 
 #include "fpu_system.h"
 #include "fpu_emu.h"
@@ -147,13 +147,9 @@ void math_emulate(struct math_emu_info *info)
        unsigned long code_base = 0;
        unsigned long code_limit = 0;   /* Initialized to stop compiler warnings */
        struct desc_struct code_descriptor;
+       struct fpu *fpu = &current->thread.fpu;
 
-       if (!used_math()) {
-               if (init_fpu(current)) {
-                       do_group_exit(SIGKILL);
-                       return;
-               }
-       }
+       fpu__activate_curr(fpu);
 
 #ifdef RE_ENTRANT_CHECKING
        if (emulating) {
@@ -673,7 +669,7 @@ void math_abort(struct math_emu_info *info, unsigned int signal)
 #endif /* PARANOID */
 }
 
-#define S387 ((struct i387_soft_struct *)s387)
+#define S387 ((struct swregs_state *)s387)
 #define sstatus_word() \
   ((S387->swd & ~SW_Top & 0xffff) | ((S387->ftop << SW_Top_Shift) & SW_Top))
 
@@ -682,14 +678,14 @@ int fpregs_soft_set(struct task_struct *target,
                    unsigned int pos, unsigned int count,
                    const void *kbuf, const void __user *ubuf)
 {
-       struct i387_soft_struct *s387 = &target->thread.fpu.state->soft;
+       struct swregs_state *s387 = &target->thread.fpu.state.soft;
        void *space = s387->st_space;
        int ret;
        int offset, other, i, tags, regnr, tag, newtop;
 
        RE_ENTRANT_CHECK_OFF;
        ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, s387, 0,
-                                offsetof(struct i387_soft_struct, st_space));
+                                offsetof(struct swregs_state, st_space));
        RE_ENTRANT_CHECK_ON;
 
        if (ret)
@@ -734,7 +730,7 @@ int fpregs_soft_get(struct task_struct *target,
                    unsigned int pos, unsigned int count,
                    void *kbuf, void __user *ubuf)
 {
-       struct i387_soft_struct *s387 = &target->thread.fpu.state->soft;
+       struct swregs_state *s387 = &target->thread.fpu.state.soft;
        const void *space = s387->st_space;
        int ret;
        int offset = (S387->ftop & 7) * 10, other = 80 - offset;
@@ -752,7 +748,7 @@ int fpregs_soft_get(struct task_struct *target,
 #endif /* PECULIAR_486 */
 
        ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, s387, 0,
-                                 offsetof(struct i387_soft_struct, st_space));
+                                 offsetof(struct swregs_state, st_space));
 
        /* Copy all registers in stack order. */
        if (!ret)
index 2c614410a5f3978d646f87d2814edaf2ec383396..9ccecb61a4fa129a82028b27edc18b91a2f99042 100644 (file)
@@ -31,7 +31,7 @@
 #define SEG_EXPAND_DOWN(s)     (((s).b & ((1 << 11) | (1 << 10))) \
                                 == (1 << 10))
 
-#define I387                   (current->thread.fpu.state)
+#define I387                   (&current->thread.fpu.state)
 #define FPU_info               (I387->soft.info)
 
 #define FPU_CS                 (*(unsigned short *) &(FPU_info->regs->cs))
index 1d553186c4345c02be5c0152764d988cfe365ae1..8533b46e6bee565e242f8ea339d892b65206c97d 100644 (file)
@@ -40,7 +40,7 @@
  */
 uint16_t __cachemode2pte_tbl[_PAGE_CACHE_MODE_NUM] = {
        [_PAGE_CACHE_MODE_WB      ]     = 0         | 0        ,
-       [_PAGE_CACHE_MODE_WC      ]     = _PAGE_PWT | 0        ,
+       [_PAGE_CACHE_MODE_WC      ]     = 0         | _PAGE_PCD,
        [_PAGE_CACHE_MODE_UC_MINUS]     = 0         | _PAGE_PCD,
        [_PAGE_CACHE_MODE_UC      ]     = _PAGE_PWT | _PAGE_PCD,
        [_PAGE_CACHE_MODE_WT      ]     = 0         | _PAGE_PCD,
@@ -50,11 +50,11 @@ EXPORT_SYMBOL(__cachemode2pte_tbl);
 
 uint8_t __pte2cachemode_tbl[8] = {
        [__pte2cm_idx( 0        | 0         | 0        )] = _PAGE_CACHE_MODE_WB,
-       [__pte2cm_idx(_PAGE_PWT | 0         | 0        )] = _PAGE_CACHE_MODE_WC,
+       [__pte2cm_idx(_PAGE_PWT | 0         | 0        )] = _PAGE_CACHE_MODE_UC_MINUS,
        [__pte2cm_idx( 0        | _PAGE_PCD | 0        )] = _PAGE_CACHE_MODE_UC_MINUS,
        [__pte2cm_idx(_PAGE_PWT | _PAGE_PCD | 0        )] = _PAGE_CACHE_MODE_UC,
        [__pte2cm_idx( 0        | 0         | _PAGE_PAT)] = _PAGE_CACHE_MODE_WB,
-       [__pte2cm_idx(_PAGE_PWT | 0         | _PAGE_PAT)] = _PAGE_CACHE_MODE_WC,
+       [__pte2cm_idx(_PAGE_PWT | 0         | _PAGE_PAT)] = _PAGE_CACHE_MODE_UC_MINUS,
        [__pte2cm_idx(0         | _PAGE_PCD | _PAGE_PAT)] = _PAGE_CACHE_MODE_UC_MINUS,
        [__pte2cm_idx(_PAGE_PWT | _PAGE_PCD | _PAGE_PAT)] = _PAGE_CACHE_MODE_UC,
 };
index 2b7ece0e103aec9e501c6018f00aa6113e6999f5..9c0ff045fdd4dec98832a5c6de9353174c9695f0 100644 (file)
@@ -78,13 +78,13 @@ void __iomem *
 iomap_atomic_prot_pfn(unsigned long pfn, pgprot_t prot)
 {
        /*
-        * For non-PAT systems, promote PAGE_KERNEL_WC to PAGE_KERNEL_UC_MINUS.
-        * PAGE_KERNEL_WC maps to PWT, which translates to uncached if the
-        * MTRR is UC or WC.  UC_MINUS gets the real intention, of the
-        * user, which is "WC if the MTRR is WC, UC if you can't do that."
+        * For non-PAT systems, translate non-WB request to UC- just in
+        * case the caller set the PWT bit to prot directly without using
+        * pgprot_writecombine(). UC- translates to uncached if the MTRR
+        * is UC or WC. UC- gets the real intention, of the user, which is
+        * "WC if the MTRR is WC, UC if you can't do that."
         */
-       if (!pat_enabled && pgprot_val(prot) ==
-           (__PAGE_KERNEL | cachemode2protval(_PAGE_CACHE_MODE_WC)))
+       if (!pat_enabled() && pgprot2cachemode(prot) != _PAGE_CACHE_MODE_WB)
                prot = __pgprot(__PAGE_KERNEL |
                                cachemode2protval(_PAGE_CACHE_MODE_UC_MINUS));
 
index 70e7444c68351641828b834fd9a9a4fc9cea3b65..cc5ccc415cc01ef8ea9e58b3f81a281c9ab412bf 100644 (file)
@@ -42,6 +42,9 @@ int ioremap_change_attr(unsigned long vaddr, unsigned long size,
        case _PAGE_CACHE_MODE_WC:
                err = _set_memory_wc(vaddr, nrpages);
                break;
+       case _PAGE_CACHE_MODE_WT:
+               err = _set_memory_wt(vaddr, nrpages);
+               break;
        case _PAGE_CACHE_MODE_WB:
                err = _set_memory_wb(vaddr, nrpages);
                break;
@@ -172,6 +175,10 @@ static void __iomem *__ioremap_caller(resource_size_t phys_addr,
                prot = __pgprot(pgprot_val(prot) |
                                cachemode2protval(_PAGE_CACHE_MODE_WC));
                break;
+       case _PAGE_CACHE_MODE_WT:
+               prot = __pgprot(pgprot_val(prot) |
+                               cachemode2protval(_PAGE_CACHE_MODE_WT));
+               break;
        case _PAGE_CACHE_MODE_WB:
                break;
        }
@@ -234,10 +241,11 @@ void __iomem *ioremap_nocache(resource_size_t phys_addr, unsigned long size)
 {
        /*
         * Ideally, this should be:
-        *      pat_enabled ? _PAGE_CACHE_MODE_UC : _PAGE_CACHE_MODE_UC_MINUS;
+        *      pat_enabled() ? _PAGE_CACHE_MODE_UC : _PAGE_CACHE_MODE_UC_MINUS;
         *
         * Till we fix all X drivers to use ioremap_wc(), we will use
-        * UC MINUS.
+        * UC MINUS. Drivers that are certain they need or can already
+        * be converted over to strong UC can use ioremap_uc().
         */
        enum page_cache_mode pcm = _PAGE_CACHE_MODE_UC_MINUS;
 
@@ -246,6 +254,39 @@ void __iomem *ioremap_nocache(resource_size_t phys_addr, unsigned long size)
 }
 EXPORT_SYMBOL(ioremap_nocache);
 
+/**
+ * ioremap_uc     -   map bus memory into CPU space as strongly uncachable
+ * @phys_addr:    bus address of the memory
+ * @size:      size of the resource to map
+ *
+ * ioremap_uc performs a platform specific sequence of operations to
+ * make bus memory CPU accessible via the readb/readw/readl/writeb/
+ * writew/writel functions and the other mmio helpers. The returned
+ * address is not guaranteed to be usable directly as a virtual
+ * address.
+ *
+ * This version of ioremap ensures that the memory is marked with a strong
+ * preference as completely uncachable on the CPU when possible. For non-PAT
+ * systems this ends up setting page-attribute flags PCD=1, PWT=1. For PAT
+ * systems this will set the PAT entry for the pages as strong UC.  This call
+ * will honor existing caching rules from things like the PCI bus. Note that
+ * there are other caches and buffers on many busses. In particular driver
+ * authors should read up on PCI writes.
+ *
+ * It's useful if some control registers are in such an area and
+ * write combining or read caching is not desirable:
+ *
+ * Must be freed with iounmap.
+ */
+void __iomem *ioremap_uc(resource_size_t phys_addr, unsigned long size)
+{
+       enum page_cache_mode pcm = _PAGE_CACHE_MODE_UC;
+
+       return __ioremap_caller(phys_addr, size, pcm,
+                               __builtin_return_address(0));
+}
+EXPORT_SYMBOL_GPL(ioremap_uc);
+
 /**
  * ioremap_wc  -       map memory into CPU space write combined
  * @phys_addr: bus address of the memory
@@ -258,14 +299,28 @@ EXPORT_SYMBOL(ioremap_nocache);
  */
 void __iomem *ioremap_wc(resource_size_t phys_addr, unsigned long size)
 {
-       if (pat_enabled)
-               return __ioremap_caller(phys_addr, size, _PAGE_CACHE_MODE_WC,
+       return __ioremap_caller(phys_addr, size, _PAGE_CACHE_MODE_WC,
                                        __builtin_return_address(0));
-       else
-               return ioremap_nocache(phys_addr, size);
 }
 EXPORT_SYMBOL(ioremap_wc);
 
+/**
+ * ioremap_wt  -       map memory into CPU space write through
+ * @phys_addr: bus address of the memory
+ * @size:      size of the resource to map
+ *
+ * This version of ioremap ensures that the memory is marked write through.
+ * Write through stores data into memory while keeping the cache up-to-date.
+ *
+ * Must be freed with iounmap.
+ */
+void __iomem *ioremap_wt(resource_size_t phys_addr, unsigned long size)
+{
+       return __ioremap_caller(phys_addr, size, _PAGE_CACHE_MODE_WT,
+                                       __builtin_return_address(0));
+}
+EXPORT_SYMBOL(ioremap_wt);
+
 void __iomem *ioremap_cache(resource_size_t phys_addr, unsigned long size)
 {
        return __ioremap_caller(phys_addr, size, _PAGE_CACHE_MODE_WB,
@@ -331,7 +386,7 @@ void iounmap(volatile void __iomem *addr)
 }
 EXPORT_SYMBOL(iounmap);
 
-int arch_ioremap_pud_supported(void)
+int __init arch_ioremap_pud_supported(void)
 {
 #ifdef CONFIG_X86_64
        return cpu_has_gbpages;
@@ -340,7 +395,7 @@ int arch_ioremap_pud_supported(void)
 #endif
 }
 
-int arch_ioremap_pmd_supported(void)
+int __init arch_ioremap_pmd_supported(void)
 {
        return cpu_has_pse;
 }
@@ -353,18 +408,18 @@ void *xlate_dev_mem_ptr(phys_addr_t phys)
 {
        unsigned long start  = phys &  PAGE_MASK;
        unsigned long offset = phys & ~PAGE_MASK;
-       unsigned long vaddr;
+       void *vaddr;
 
        /* If page is RAM, we can use __va. Otherwise ioremap and unmap. */
        if (page_is_ram(start >> PAGE_SHIFT))
                return __va(phys);
 
-       vaddr = (unsigned long)ioremap_cache(start, PAGE_SIZE);
+       vaddr = ioremap_cache(start, PAGE_SIZE);
        /* Only add the offset on success and return NULL if the ioremap() failed: */
        if (vaddr)
                vaddr += offset;
 
-       return (void *)vaddr;
+       return vaddr;
 }
 
 void unxlate_dev_mem_ptr(phys_addr_t phys, void *addr)
@@ -373,7 +428,6 @@ void unxlate_dev_mem_ptr(phys_addr_t phys, void *addr)
                return;
 
        iounmap((void __iomem *)((unsigned long)addr & PAGE_MASK));
-       return;
 }
 
 static pte_t bm_pte[PAGE_SIZE/sizeof(pte_t)] __page_aligned_bss;
index c439ec47821601c5b594bc1eec5abc529c5fd012..7a657f58bbea152057262a61e325c169f78bc516 100644 (file)
 #include <linux/syscalls.h>
 #include <linux/sched/sysctl.h>
 
-#include <asm/i387.h>
 #include <asm/insn.h>
 #include <asm/mman.h>
 #include <asm/mmu_context.h>
 #include <asm/mpx.h>
 #include <asm/processor.h>
-#include <asm/fpu-internal.h>
+#include <asm/fpu/internal.h>
+
+#define CREATE_TRACE_POINTS
+#include <asm/trace/mpx.h>
 
 static const char *mpx_mapping_name(struct vm_area_struct *vma)
 {
@@ -32,6 +34,22 @@ static int is_mpx_vma(struct vm_area_struct *vma)
        return (vma->vm_ops == &mpx_vma_ops);
 }
 
+static inline unsigned long mpx_bd_size_bytes(struct mm_struct *mm)
+{
+       if (is_64bit_mm(mm))
+               return MPX_BD_SIZE_BYTES_64;
+       else
+               return MPX_BD_SIZE_BYTES_32;
+}
+
+static inline unsigned long mpx_bt_size_bytes(struct mm_struct *mm)
+{
+       if (is_64bit_mm(mm))
+               return MPX_BT_SIZE_BYTES_64;
+       else
+               return MPX_BT_SIZE_BYTES_32;
+}
+
 /*
  * This is really a simplified "vm_mmap". it only handles MPX
  * bounds tables (the bounds directory is user-allocated).
@@ -47,8 +65,8 @@ static unsigned long mpx_mmap(unsigned long len)
        vm_flags_t vm_flags;
        struct vm_area_struct *vma;
 
-       /* Only bounds table and bounds directory can be allocated here */
-       if (len != MPX_BD_SIZE_BYTES && len != MPX_BT_SIZE_BYTES)
+       /* Only bounds table can be allocated here */
+       if (len != mpx_bt_size_bytes(mm))
                return -EINVAL;
 
        down_write(&mm->mmap_sem);
@@ -272,10 +290,9 @@ bad_opcode:
  *
  * The caller is expected to kfree() the returned siginfo_t.
  */
-siginfo_t *mpx_generate_siginfo(struct pt_regs *regs,
-                               struct xsave_struct *xsave_buf)
+siginfo_t *mpx_generate_siginfo(struct pt_regs *regs)
 {
-       struct bndreg *bndregs, *bndreg;
+       const struct bndreg *bndregs, *bndreg;
        siginfo_t *info = NULL;
        struct insn insn;
        uint8_t bndregno;
@@ -295,8 +312,8 @@ siginfo_t *mpx_generate_siginfo(struct pt_regs *regs,
                err = -EINVAL;
                goto err_out;
        }
-       /* get the bndregs _area_ of the xsave structure */
-       bndregs = get_xsave_addr(xsave_buf, XSTATE_BNDREGS);
+       /* get bndregs field from current task's xsave area */
+       bndregs = get_xsave_field_ptr(XSTATE_BNDREGS);
        if (!bndregs) {
                err = -EINVAL;
                goto err_out;
@@ -334,6 +351,7 @@ siginfo_t *mpx_generate_siginfo(struct pt_regs *regs,
                err = -EINVAL;
                goto err_out;
        }
+       trace_mpx_bounds_register_exception(info->si_addr, bndreg);
        return info;
 err_out:
        /* info might be NULL, but kfree() handles that */
@@ -341,25 +359,18 @@ err_out:
        return ERR_PTR(err);
 }
 
-static __user void *task_get_bounds_dir(struct task_struct *tsk)
+static __user void *mpx_get_bounds_dir(void)
 {
-       struct bndcsr *bndcsr;
+       const struct bndcsr *bndcsr;
 
        if (!cpu_feature_enabled(X86_FEATURE_MPX))
                return MPX_INVALID_BOUNDS_DIR;
 
-       /*
-        * 32-bit binaries on 64-bit kernels are currently
-        * unsupported.
-        */
-       if (IS_ENABLED(CONFIG_X86_64) && test_thread_flag(TIF_IA32))
-               return MPX_INVALID_BOUNDS_DIR;
        /*
         * The bounds directory pointer is stored in a register
         * only accessible if we first do an xsave.
         */
-       fpu_save_init(&tsk->thread.fpu);
-       bndcsr = get_xsave_addr(&tsk->thread.fpu.state->xsave, XSTATE_BNDCSR);
+       bndcsr = get_xsave_field_ptr(XSTATE_BNDCSR);
        if (!bndcsr)
                return MPX_INVALID_BOUNDS_DIR;
 
@@ -378,10 +389,10 @@ static __user void *task_get_bounds_dir(struct task_struct *tsk)
                (bndcsr->bndcfgu & MPX_BNDCFG_ADDR_MASK);
 }
 
-int mpx_enable_management(struct task_struct *tsk)
+int mpx_enable_management(void)
 {
        void __user *bd_base = MPX_INVALID_BOUNDS_DIR;
-       struct mm_struct *mm = tsk->mm;
+       struct mm_struct *mm = current->mm;
        int ret = 0;
 
        /*
@@ -390,11 +401,12 @@ int mpx_enable_management(struct task_struct *tsk)
         * directory into XSAVE/XRSTOR Save Area and enable MPX through
         * XRSTOR instruction.
         *
-        * fpu_xsave() is expected to be very expensive. Storing the bounds
-        * directory here means that we do not have to do xsave in the unmap
-        * path; we can just use mm->bd_addr instead.
+        * The copy_xregs_to_kernel() beneath get_xsave_field_ptr() is
+        * expected to be relatively expensive. Storing the bounds
+        * directory here means that we do not have to do xsave in the
+        * unmap path; we can just use mm->bd_addr instead.
         */
-       bd_base = task_get_bounds_dir(tsk);
+       bd_base = mpx_get_bounds_dir();
        down_write(&mm->mmap_sem);
        mm->bd_addr = bd_base;
        if (mm->bd_addr == MPX_INVALID_BOUNDS_DIR)
@@ -404,7 +416,7 @@ int mpx_enable_management(struct task_struct *tsk)
        return ret;
 }
 
-int mpx_disable_management(struct task_struct *tsk)
+int mpx_disable_management(void)
 {
        struct mm_struct *mm = current->mm;
 
@@ -417,29 +429,59 @@ int mpx_disable_management(struct task_struct *tsk)
        return 0;
 }
 
+static int mpx_cmpxchg_bd_entry(struct mm_struct *mm,
+               unsigned long *curval,
+               unsigned long __user *addr,
+               unsigned long old_val, unsigned long new_val)
+{
+       int ret;
+       /*
+        * user_atomic_cmpxchg_inatomic() actually uses sizeof()
+        * the pointer that we pass to it to figure out how much
+        * data to cmpxchg.  We have to be careful here not to
+        * pass a pointer to a 64-bit data type when we only want
+        * a 32-bit copy.
+        */
+       if (is_64bit_mm(mm)) {
+               ret = user_atomic_cmpxchg_inatomic(curval,
+                               addr, old_val, new_val);
+       } else {
+               u32 uninitialized_var(curval_32);
+               u32 old_val_32 = old_val;
+               u32 new_val_32 = new_val;
+               u32 __user *addr_32 = (u32 __user *)addr;
+
+               ret = user_atomic_cmpxchg_inatomic(&curval_32,
+                               addr_32, old_val_32, new_val_32);
+               *curval = curval_32;
+       }
+       return ret;
+}
+
 /*
- * With 32-bit mode, MPX_BT_SIZE_BYTES is 4MB, and the size of each
- * bounds table is 16KB. With 64-bit mode, MPX_BT_SIZE_BYTES is 2GB,
+ * With 32-bit mode, a bounds directory is 4MB, and the size of each
+ * bounds table is 16KB. With 64-bit mode, a bounds directory is 2GB,
  * and the size of each bounds table is 4MB.
  */
-static int allocate_bt(long __user *bd_entry)
+static int allocate_bt(struct mm_struct *mm, long __user *bd_entry)
 {
        unsigned long expected_old_val = 0;
        unsigned long actual_old_val = 0;
        unsigned long bt_addr;
+       unsigned long bd_new_entry;
        int ret = 0;
 
        /*
         * Carve the virtual space out of userspace for the new
         * bounds table:
         */
-       bt_addr = mpx_mmap(MPX_BT_SIZE_BYTES);
+       bt_addr = mpx_mmap(mpx_bt_size_bytes(mm));
        if (IS_ERR((void *)bt_addr))
                return PTR_ERR((void *)bt_addr);
        /*
         * Set the valid flag (kinda like _PAGE_PRESENT in a pte)
         */
-       bt_addr = bt_addr | MPX_BD_ENTRY_VALID_FLAG;
+       bd_new_entry = bt_addr | MPX_BD_ENTRY_VALID_FLAG;
 
        /*
         * Go poke the address of the new bounds table in to the
@@ -452,8 +494,8 @@ static int allocate_bt(long __user *bd_entry)
         * mmap_sem at this point, unlike some of the other part
         * of the MPX code that have to pagefault_disable().
         */
-       ret = user_atomic_cmpxchg_inatomic(&actual_old_val, bd_entry,
-                                          expected_old_val, bt_addr);
+       ret = mpx_cmpxchg_bd_entry(mm, &actual_old_val, bd_entry,
+                                  expected_old_val, bd_new_entry);
        if (ret)
                goto out_unmap;
 
@@ -481,9 +523,10 @@ static int allocate_bt(long __user *bd_entry)
                ret = -EINVAL;
                goto out_unmap;
        }
+       trace_mpx_new_bounds_table(bt_addr);
        return 0;
 out_unmap:
-       vm_munmap(bt_addr & MPX_BT_ADDR_MASK, MPX_BT_SIZE_BYTES);
+       vm_munmap(bt_addr, mpx_bt_size_bytes(mm));
        return ret;
 }
 
@@ -498,12 +541,13 @@ out_unmap:
  * bound table is 16KB. With 64-bit mode, the size of BD is 2GB,
  * and the size of each bound table is 4MB.
  */
-static int do_mpx_bt_fault(struct xsave_struct *xsave_buf)
+static int do_mpx_bt_fault(void)
 {
        unsigned long bd_entry, bd_base;
-       struct bndcsr *bndcsr;
+       const struct bndcsr *bndcsr;
+       struct mm_struct *mm = current->mm;
 
-       bndcsr = get_xsave_addr(xsave_buf, XSTATE_BNDCSR);
+       bndcsr = get_xsave_field_ptr(XSTATE_BNDCSR);
        if (!bndcsr)
                return -EINVAL;
        /*
@@ -520,13 +564,13 @@ static int do_mpx_bt_fault(struct xsave_struct *xsave_buf)
         * the directory is.
         */
        if ((bd_entry < bd_base) ||
-           (bd_entry >= bd_base + MPX_BD_SIZE_BYTES))
+           (bd_entry >= bd_base + mpx_bd_size_bytes(mm)))
                return -EINVAL;
 
-       return allocate_bt((long __user *)bd_entry);
+       return allocate_bt(mm, (long __user *)bd_entry);
 }
 
-int mpx_handle_bd_fault(struct xsave_struct *xsave_buf)
+int mpx_handle_bd_fault(void)
 {
        /*
         * Userspace never asked us to manage the bounds tables,
@@ -535,7 +579,7 @@ int mpx_handle_bd_fault(struct xsave_struct *xsave_buf)
        if (!kernel_managing_mpx_tables(current->mm))
                return -EINVAL;
 
-       if (do_mpx_bt_fault(xsave_buf)) {
+       if (do_mpx_bt_fault()) {
                force_sig(SIGSEGV, current);
                /*
                 * The force_sig() is essentially "handling" this
@@ -572,29 +616,55 @@ static int mpx_resolve_fault(long __user *addr, int write)
        return 0;
 }
 
+static unsigned long mpx_bd_entry_to_bt_addr(struct mm_struct *mm,
+                                            unsigned long bd_entry)
+{
+       unsigned long bt_addr = bd_entry;
+       int align_to_bytes;
+       /*
+        * Bit 0 in a bt_entry is always the valid bit.
+        */
+       bt_addr &= ~MPX_BD_ENTRY_VALID_FLAG;
+       /*
+        * Tables are naturally aligned at 8-byte boundaries
+        * on 64-bit and 4-byte boundaries on 32-bit.  The
+        * documentation makes it appear that the low bits
+        * are ignored by the hardware, so we do the same.
+        */
+       if (is_64bit_mm(mm))
+               align_to_bytes = 8;
+       else
+               align_to_bytes = 4;
+       bt_addr &= ~(align_to_bytes-1);
+       return bt_addr;
+}
+
 /*
  * Get the base of bounds tables pointed by specific bounds
  * directory entry.
  */
 static int get_bt_addr(struct mm_struct *mm,
-                       long __user *bd_entry, unsigned long *bt_addr)
+                       long __user *bd_entry_ptr,
+                       unsigned long *bt_addr_result)
 {
        int ret;
        int valid_bit;
+       unsigned long bd_entry;
+       unsigned long bt_addr;
 
-       if (!access_ok(VERIFY_READ, (bd_entry), sizeof(*bd_entry)))
+       if (!access_ok(VERIFY_READ, (bd_entry_ptr), sizeof(*bd_entry_ptr)))
                return -EFAULT;
 
        while (1) {
                int need_write = 0;
 
                pagefault_disable();
-               ret = get_user(*bt_addr, bd_entry);
+               ret = get_user(bd_entry, bd_entry_ptr);
                pagefault_enable();
                if (!ret)
                        break;
                if (ret == -EFAULT)
-                       ret = mpx_resolve_fault(bd_entry, need_write);
+                       ret = mpx_resolve_fault(bd_entry_ptr, need_write);
                /*
                 * If we could not resolve the fault, consider it
                 * userspace's fault and error out.
@@ -603,8 +673,8 @@ static int get_bt_addr(struct mm_struct *mm,
                        return ret;
        }
 
-       valid_bit = *bt_addr & MPX_BD_ENTRY_VALID_FLAG;
-       *bt_addr &= MPX_BT_ADDR_MASK;
+       valid_bit = bd_entry & MPX_BD_ENTRY_VALID_FLAG;
+       bt_addr = mpx_bd_entry_to_bt_addr(mm, bd_entry);
 
        /*
         * When the kernel is managing bounds tables, a bounds directory
@@ -613,7 +683,7 @@ static int get_bt_addr(struct mm_struct *mm,
         * data in the address field, we know something is wrong. This
         * -EINVAL return will cause a SIGSEGV.
         */
-       if (!valid_bit && *bt_addr)
+       if (!valid_bit && bt_addr)
                return -EINVAL;
        /*
         * Do we have an completely zeroed bt entry?  That is OK.  It
@@ -624,19 +694,100 @@ static int get_bt_addr(struct mm_struct *mm,
        if (!valid_bit)
                return -ENOENT;
 
+       *bt_addr_result = bt_addr;
        return 0;
 }
 
+static inline int bt_entry_size_bytes(struct mm_struct *mm)
+{
+       if (is_64bit_mm(mm))
+               return MPX_BT_ENTRY_BYTES_64;
+       else
+               return MPX_BT_ENTRY_BYTES_32;
+}
+
+/*
+ * Take a virtual address and turns it in to the offset in bytes
+ * inside of the bounds table where the bounds table entry
+ * controlling 'addr' can be found.
+ */
+static unsigned long mpx_get_bt_entry_offset_bytes(struct mm_struct *mm,
+               unsigned long addr)
+{
+       unsigned long bt_table_nr_entries;
+       unsigned long offset = addr;
+
+       if (is_64bit_mm(mm)) {
+               /* Bottom 3 bits are ignored on 64-bit */
+               offset >>= 3;
+               bt_table_nr_entries = MPX_BT_NR_ENTRIES_64;
+       } else {
+               /* Bottom 2 bits are ignored on 32-bit */
+               offset >>= 2;
+               bt_table_nr_entries = MPX_BT_NR_ENTRIES_32;
+       }
+       /*
+        * We know the size of the table in to which we are
+        * indexing, and we have eliminated all the low bits
+        * which are ignored for indexing.
+        *
+        * Mask out all the high bits which we do not need
+        * to index in to the table.  Note that the tables
+        * are always powers of two so this gives us a proper
+        * mask.
+        */
+       offset &= (bt_table_nr_entries-1);
+       /*
+        * We now have an entry offset in terms of *entries* in
+        * the table.  We need to scale it back up to bytes.
+        */
+       offset *= bt_entry_size_bytes(mm);
+       return offset;
+}
+
+/*
+ * How much virtual address space does a single bounds
+ * directory entry cover?
+ *
+ * Note, we need a long long because 4GB doesn't fit in
+ * to a long on 32-bit.
+ */
+static inline unsigned long bd_entry_virt_space(struct mm_struct *mm)
+{
+       unsigned long long virt_space = (1ULL << boot_cpu_data.x86_virt_bits);
+       if (is_64bit_mm(mm))
+               return virt_space / MPX_BD_NR_ENTRIES_64;
+       else
+               return virt_space / MPX_BD_NR_ENTRIES_32;
+}
+
 /*
  * Free the backing physical pages of bounds table 'bt_addr'.
  * Assume start...end is within that bounds table.
  */
-static int zap_bt_entries(struct mm_struct *mm,
+static noinline int zap_bt_entries_mapping(struct mm_struct *mm,
                unsigned long bt_addr,
-               unsigned long start, unsigned long end)
+               unsigned long start_mapping, unsigned long end_mapping)
 {
        struct vm_area_struct *vma;
        unsigned long addr, len;
+       unsigned long start;
+       unsigned long end;
+
+       /*
+        * if we 'end' on a boundary, the offset will be 0 which
+        * is not what we want.  Back it up a byte to get the
+        * last bt entry.  Then once we have the entry itself,
+        * move 'end' back up by the table entry size.
+        */
+       start = bt_addr + mpx_get_bt_entry_offset_bytes(mm, start_mapping);
+       end   = bt_addr + mpx_get_bt_entry_offset_bytes(mm, end_mapping - 1);
+       /*
+        * Move end back up by one entry.  Among other things
+        * this ensures that it remains page-aligned and does
+        * not screw up zap_page_range()
+        */
+       end += bt_entry_size_bytes(mm);
 
        /*
         * Find the first overlapping vma. If vma->vm_start > start, there
@@ -648,7 +799,7 @@ static int zap_bt_entries(struct mm_struct *mm,
                return -EINVAL;
 
        /*
-        * A NUMA policy on a VM_MPX VMA could cause this bouds table to
+        * A NUMA policy on a VM_MPX VMA could cause this bounds table to
         * be split. So we need to look across the entire 'start -> end'
         * range of this bounds table, find all of the VM_MPX VMAs, and
         * zap only those.
@@ -666,27 +817,65 @@ static int zap_bt_entries(struct mm_struct *mm,
 
                len = min(vma->vm_end, end) - addr;
                zap_page_range(vma, addr, len, NULL);
+               trace_mpx_unmap_zap(addr, addr+len);
 
                vma = vma->vm_next;
                addr = vma->vm_start;
        }
-
        return 0;
 }
 
-static int unmap_single_bt(struct mm_struct *mm,
+static unsigned long mpx_get_bd_entry_offset(struct mm_struct *mm,
+               unsigned long addr)
+{
+       /*
+        * There are several ways to derive the bd offsets.  We
+        * use the following approach here:
+        * 1. We know the size of the virtual address space
+        * 2. We know the number of entries in a bounds table
+        * 3. We know that each entry covers a fixed amount of
+        *    virtual address space.
+        * So, we can just divide the virtual address by the
+        * virtual space used by one entry to determine which
+        * entry "controls" the given virtual address.
+        */
+       if (is_64bit_mm(mm)) {
+               int bd_entry_size = 8; /* 64-bit pointer */
+               /*
+                * Take the 64-bit addressing hole in to account.
+                */
+               addr &= ((1UL << boot_cpu_data.x86_virt_bits) - 1);
+               return (addr / bd_entry_virt_space(mm)) * bd_entry_size;
+       } else {
+               int bd_entry_size = 4; /* 32-bit pointer */
+               /*
+                * 32-bit has no hole so this case needs no mask
+                */
+               return (addr / bd_entry_virt_space(mm)) * bd_entry_size;
+       }
+       /*
+        * The two return calls above are exact copies.  If we
+        * pull out a single copy and put it in here, gcc won't
+        * realize that we're doing a power-of-2 divide and use
+        * shifts.  It uses a real divide.  If we put them up
+        * there, it manages to figure it out (gcc 4.8.3).
+        */
+}
+
+static int unmap_entire_bt(struct mm_struct *mm,
                long __user *bd_entry, unsigned long bt_addr)
 {
        unsigned long expected_old_val = bt_addr | MPX_BD_ENTRY_VALID_FLAG;
-       unsigned long actual_old_val = 0;
+       unsigned long uninitialized_var(actual_old_val);
        int ret;
 
        while (1) {
                int need_write = 1;
+               unsigned long cleared_bd_entry = 0;
 
                pagefault_disable();
-               ret = user_atomic_cmpxchg_inatomic(&actual_old_val, bd_entry,
-                                                  expected_old_val, 0);
+               ret = mpx_cmpxchg_bd_entry(mm, &actual_old_val,
+                               bd_entry, expected_old_val, cleared_bd_entry);
                pagefault_enable();
                if (!ret)
                        break;
@@ -705,9 +894,8 @@ static int unmap_single_bt(struct mm_struct *mm,
        if (actual_old_val != expected_old_val) {
                /*
                 * Someone else raced with us to unmap the table.
-                * There was no bounds table pointed to by the
-                * directory, so declare success.  Somebody freed
-                * it.
+                * That is OK, since we were both trying to do
+                * the same thing.  Declare success.
                 */
                if (!actual_old_val)
                        return 0;
@@ -720,176 +908,113 @@ static int unmap_single_bt(struct mm_struct *mm,
                 */
                return -EINVAL;
        }
-
        /*
         * Note, we are likely being called under do_munmap() already. To
         * avoid recursion, do_munmap() will check whether it comes
         * from one bounds table through VM_MPX flag.
         */
-       return do_munmap(mm, bt_addr, MPX_BT_SIZE_BYTES);
+       return do_munmap(mm, bt_addr, mpx_bt_size_bytes(mm));
 }
 
-/*
- * If the bounds table pointed by bounds directory 'bd_entry' is
- * not shared, unmap this whole bounds table. Otherwise, only free
- * those backing physical pages of bounds table entries covered
- * in this virtual address region start...end.
- */
-static int unmap_shared_bt(struct mm_struct *mm,
-               long __user *bd_entry, unsigned long start,
-               unsigned long end, bool prev_shared, bool next_shared)
+static int try_unmap_single_bt(struct mm_struct *mm,
+              unsigned long start, unsigned long end)
 {
-       unsigned long bt_addr;
-       int ret;
-
-       ret = get_bt_addr(mm, bd_entry, &bt_addr);
+       struct vm_area_struct *next;
+       struct vm_area_struct *prev;
        /*
-        * We could see an "error" ret for not-present bounds
-        * tables (not really an error), or actual errors, but
-        * stop unmapping either way.
+        * "bta" == Bounds Table Area: the area controlled by the
+        * bounds table that we are unmapping.
         */
-       if (ret)
-               return ret;
-
-       if (prev_shared && next_shared)
-               ret = zap_bt_entries(mm, bt_addr,
-                               bt_addr+MPX_GET_BT_ENTRY_OFFSET(start),
-                               bt_addr+MPX_GET_BT_ENTRY_OFFSET(end));
-       else if (prev_shared)
-               ret = zap_bt_entries(mm, bt_addr,
-                               bt_addr+MPX_GET_BT_ENTRY_OFFSET(start),
-                               bt_addr+MPX_BT_SIZE_BYTES);
-       else if (next_shared)
-               ret = zap_bt_entries(mm, bt_addr, bt_addr,
-                               bt_addr+MPX_GET_BT_ENTRY_OFFSET(end));
-       else
-               ret = unmap_single_bt(mm, bd_entry, bt_addr);
-
-       return ret;
-}
-
-/*
- * A virtual address region being munmap()ed might share bounds table
- * with adjacent VMAs. We only need to free the backing physical
- * memory of these shared bounds tables entries covered in this virtual
- * address region.
- */
-static int unmap_edge_bts(struct mm_struct *mm,
-               unsigned long start, unsigned long end)
-{
+       unsigned long bta_start_vaddr = start & ~(bd_entry_virt_space(mm)-1);
+       unsigned long bta_end_vaddr = bta_start_vaddr + bd_entry_virt_space(mm);
+       unsigned long uninitialized_var(bt_addr);
+       void __user *bde_vaddr;
        int ret;
-       long __user *bde_start, *bde_end;
-       struct vm_area_struct *prev, *next;
-       bool prev_shared = false, next_shared = false;
-
-       bde_start = mm->bd_addr + MPX_GET_BD_ENTRY_OFFSET(start);
-       bde_end = mm->bd_addr + MPX_GET_BD_ENTRY_OFFSET(end-1);
-
        /*
-        * Check whether bde_start and bde_end are shared with adjacent
-        * VMAs.
-        *
-        * We already unliked the VMAs from the mm's rbtree so 'start'
+        * We already unlinked the VMAs from the mm's rbtree so 'start'
         * is guaranteed to be in a hole. This gets us the first VMA
         * before the hole in to 'prev' and the next VMA after the hole
         * in to 'next'.
         */
        next = find_vma_prev(mm, start, &prev);
-       if (prev && (mm->bd_addr + MPX_GET_BD_ENTRY_OFFSET(prev->vm_end-1))
-                       == bde_start)
-               prev_shared = true;
-       if (next && (mm->bd_addr + MPX_GET_BD_ENTRY_OFFSET(next->vm_start))
-                       == bde_end)
-               next_shared = true;
-
        /*
-        * This virtual address region being munmap()ed is only
-        * covered by one bounds table.
-        *
-        * In this case, if this table is also shared with adjacent
-        * VMAs, only part of the backing physical memory of the bounds
-        * table need be freeed. Otherwise the whole bounds table need
-        * be unmapped.
-        */
-       if (bde_start == bde_end) {
-               return unmap_shared_bt(mm, bde_start, start, end,
-                               prev_shared, next_shared);
+        * Do not count other MPX bounds table VMAs as neighbors.
+        * Although theoretically possible, we do not allow bounds
+        * tables for bounds tables so our heads do not explode.
+        * If we count them as neighbors here, we may end up with
+        * lots of tables even though we have no actual table
+        * entries in use.
+        */
+       while (next && is_mpx_vma(next))
+               next = next->vm_next;
+       while (prev && is_mpx_vma(prev))
+               prev = prev->vm_prev;
+       /*
+        * We know 'start' and 'end' lie within an area controlled
+        * by a single bounds table.  See if there are any other
+        * VMAs controlled by that bounds table.  If there are not
+        * then we can "expand" the are we are unmapping to possibly
+        * cover the entire table.
+        */
+       next = find_vma_prev(mm, start, &prev);
+       if ((!prev || prev->vm_end <= bta_start_vaddr) &&
+           (!next || next->vm_start >= bta_end_vaddr)) {
+               /*
+                * No neighbor VMAs controlled by same bounds
+                * table.  Try to unmap the whole thing
+                */
+               start = bta_start_vaddr;
+               end = bta_end_vaddr;
        }
 
+       bde_vaddr = mm->bd_addr + mpx_get_bd_entry_offset(mm, start);
+       ret = get_bt_addr(mm, bde_vaddr, &bt_addr);
        /*
-        * If more than one bounds tables are covered in this virtual
-        * address region being munmap()ed, we need to separately check
-        * whether bde_start and bde_end are shared with adjacent VMAs.
+        * No bounds table there, so nothing to unmap.
         */
-       ret = unmap_shared_bt(mm, bde_start, start, end, prev_shared, false);
-       if (ret)
-               return ret;
-       ret = unmap_shared_bt(mm, bde_end, start, end, false, next_shared);
+       if (ret == -ENOENT) {
+               ret = 0;
+               return 0;
+       }
        if (ret)
                return ret;
-
-       return 0;
+       /*
+        * We are unmapping an entire table.  Either because the
+        * unmap that started this whole process was large enough
+        * to cover an entire table, or that the unmap was small
+        * but was the area covered by a bounds table.
+        */
+       if ((start == bta_start_vaddr) &&
+           (end == bta_end_vaddr))
+               return unmap_entire_bt(mm, bde_vaddr, bt_addr);
+       return zap_bt_entries_mapping(mm, bt_addr, start, end);
 }
 
 static int mpx_unmap_tables(struct mm_struct *mm,
                unsigned long start, unsigned long end)
 {
-       int ret;
-       long __user *bd_entry, *bde_start, *bde_end;
-       unsigned long bt_addr;
-
-       /*
-        * "Edge" bounds tables are those which are being used by the region
-        * (start -> end), but that may be shared with adjacent areas.  If they
-        * turn out to be completely unshared, they will be freed.  If they are
-        * shared, we will free the backing store (like an MADV_DONTNEED) for
-        * areas used by this region.
-        */
-       ret = unmap_edge_bts(mm, start, end);
-       switch (ret) {
-               /* non-present tables are OK */
-               case 0:
-               case -ENOENT:
-                       /* Success, or no tables to unmap */
-                       break;
-               case -EINVAL:
-               case -EFAULT:
-               default:
-                       return ret;
-       }
-
-       /*
-        * Only unmap the bounds table that are
-        *   1. fully covered
-        *   2. not at the edges of the mapping, even if full aligned
-        */
-       bde_start = mm->bd_addr + MPX_GET_BD_ENTRY_OFFSET(start);
-       bde_end = mm->bd_addr + MPX_GET_BD_ENTRY_OFFSET(end-1);
-       for (bd_entry = bde_start + 1; bd_entry < bde_end; bd_entry++) {
-               ret = get_bt_addr(mm, bd_entry, &bt_addr);
-               switch (ret) {
-                       case 0:
-                               break;
-                       case -ENOENT:
-                               /* No table here, try the next one */
-                               continue;
-                       case -EINVAL:
-                       case -EFAULT:
-                       default:
-                               /*
-                                * Note: we are being strict here.
-                                * Any time we run in to an issue
-                                * unmapping tables, we stop and
-                                * SIGSEGV.
-                                */
-                               return ret;
-               }
-
-               ret = unmap_single_bt(mm, bd_entry, bt_addr);
+       unsigned long one_unmap_start;
+       trace_mpx_unmap_search(start, end);
+
+       one_unmap_start = start;
+       while (one_unmap_start < end) {
+               int ret;
+               unsigned long next_unmap_start = ALIGN(one_unmap_start+1,
+                                                      bd_entry_virt_space(mm));
+               unsigned long one_unmap_end = end;
+               /*
+                * if the end is beyond the current bounds table,
+                * move it back so we only deal with a single one
+                * at a time
+                */
+               if (one_unmap_end > next_unmap_start)
+                       one_unmap_end = next_unmap_start;
+               ret = try_unmap_single_bt(mm, one_unmap_start, one_unmap_end);
                if (ret)
                        return ret;
-       }
 
+               one_unmap_start = next_unmap_start;
+       }
        return 0;
 }
 
index 6629f397b4675a3c258a7e54fde7cb8367d2c185..8ff686aa7e8c23d8faec2bd0ff27491114897854 100644 (file)
@@ -9,6 +9,7 @@
 #include <linux/random.h>
 #include <linux/kernel.h>
 #include <linux/mm.h>
+#include <linux/vmalloc.h>
 
 #include <asm/cacheflush.h>
 #include <asm/pgtable.h>
index 89af288ec6740cfd793a4c4804e939bb023e9453..727158cb3b3c91111f7bfe63a3b2fd9744972c1a 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/percpu.h>
 #include <linux/gfp.h>
 #include <linux/pci.h>
+#include <linux/vmalloc.h>
 
 #include <asm/e820.h>
 #include <asm/processor.h>
@@ -129,16 +130,15 @@ within(unsigned long addr, unsigned long start, unsigned long end)
  */
 void clflush_cache_range(void *vaddr, unsigned int size)
 {
-       void *vend = vaddr + size - 1;
+       unsigned long clflush_mask = boot_cpu_data.x86_clflush_size - 1;
+       void *vend = vaddr + size;
+       void *p;
 
        mb();
 
-       for (; vaddr < vend; vaddr += boot_cpu_data.x86_clflush_size)
-               clflushopt(vaddr);
-       /*
-        * Flush any possible final partial cacheline:
-        */
-       clflushopt(vend);
+       for (p = (void *)((unsigned long)vaddr & ~clflush_mask);
+            p < vend; p += boot_cpu_data.x86_clflush_size)
+               clflushopt(p);
 
        mb();
 }
@@ -418,13 +418,11 @@ phys_addr_t slow_virt_to_phys(void *__virt_addr)
        phys_addr_t phys_addr;
        unsigned long offset;
        enum pg_level level;
-       unsigned long psize;
        unsigned long pmask;
        pte_t *pte;
 
        pte = lookup_address(virt_addr, &level);
        BUG_ON(!pte);
-       psize = page_level_size(level);
        pmask = page_level_mask(level);
        offset = virt_addr & ~pmask;
        phys_addr = (phys_addr_t)pte_pfn(*pte) << PAGE_SHIFT;
@@ -1468,6 +1466,9 @@ int _set_memory_uc(unsigned long addr, int numpages)
 {
        /*
         * for now UC MINUS. see comments in ioremap_nocache()
+        * If you really need strong UC use ioremap_uc(), but note
+        * that you cannot override IO areas with set_memory_*() as
+        * these helpers cannot work with IO memory.
         */
        return change_page_attr_set(&addr, numpages,
                                    cachemode2pgprot(_PAGE_CACHE_MODE_UC_MINUS),
@@ -1502,12 +1503,10 @@ EXPORT_SYMBOL(set_memory_uc);
 static int _set_memory_array(unsigned long *addr, int addrinarray,
                enum page_cache_mode new_type)
 {
+       enum page_cache_mode set_type;
        int i, j;
        int ret;
 
-       /*
-        * for now UC MINUS. see comments in ioremap_nocache()
-        */
        for (i = 0; i < addrinarray; i++) {
                ret = reserve_memtype(__pa(addr[i]), __pa(addr[i]) + PAGE_SIZE,
                                        new_type, NULL);
@@ -1515,9 +1514,12 @@ static int _set_memory_array(unsigned long *addr, int addrinarray,
                        goto out_free;
        }
 
+       /* If WC, set to UC- first and then WC */
+       set_type = (new_type == _PAGE_CACHE_MODE_WC) ?
+                               _PAGE_CACHE_MODE_UC_MINUS : new_type;
+
        ret = change_page_attr_set(addr, addrinarray,
-                                  cachemode2pgprot(_PAGE_CACHE_MODE_UC_MINUS),
-                                  1);
+                                  cachemode2pgprot(set_type), 1);
 
        if (!ret && new_type == _PAGE_CACHE_MODE_WC)
                ret = change_page_attr_set_clr(addr, addrinarray,
@@ -1549,6 +1551,12 @@ int set_memory_array_wc(unsigned long *addr, int addrinarray)
 }
 EXPORT_SYMBOL(set_memory_array_wc);
 
+int set_memory_array_wt(unsigned long *addr, int addrinarray)
+{
+       return _set_memory_array(addr, addrinarray, _PAGE_CACHE_MODE_WT);
+}
+EXPORT_SYMBOL_GPL(set_memory_array_wt);
+
 int _set_memory_wc(unsigned long addr, int numpages)
 {
        int ret;
@@ -1571,27 +1579,42 @@ int set_memory_wc(unsigned long addr, int numpages)
 {
        int ret;
 
-       if (!pat_enabled)
-               return set_memory_uc(addr, numpages);
-
        ret = reserve_memtype(__pa(addr), __pa(addr) + numpages * PAGE_SIZE,
                _PAGE_CACHE_MODE_WC, NULL);
        if (ret)
-               goto out_err;
+               return ret;
 
        ret = _set_memory_wc(addr, numpages);
        if (ret)
-               goto out_free;
-
-       return 0;
+               free_memtype(__pa(addr), __pa(addr) + numpages * PAGE_SIZE);
 
-out_free:
-       free_memtype(__pa(addr), __pa(addr) + numpages * PAGE_SIZE);
-out_err:
        return ret;
 }
 EXPORT_SYMBOL(set_memory_wc);
 
+int _set_memory_wt(unsigned long addr, int numpages)
+{
+       return change_page_attr_set(&addr, numpages,
+                                   cachemode2pgprot(_PAGE_CACHE_MODE_WT), 0);
+}
+
+int set_memory_wt(unsigned long addr, int numpages)
+{
+       int ret;
+
+       ret = reserve_memtype(__pa(addr), __pa(addr) + numpages * PAGE_SIZE,
+                             _PAGE_CACHE_MODE_WT, NULL);
+       if (ret)
+               return ret;
+
+       ret = _set_memory_wt(addr, numpages);
+       if (ret)
+               free_memtype(__pa(addr), __pa(addr) + numpages * PAGE_SIZE);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(set_memory_wt);
+
 int _set_memory_wb(unsigned long addr, int numpages)
 {
        /* WB cache mode is hard wired to all cache attribute bits being 0 */
@@ -1682,6 +1705,7 @@ static int _set_pages_array(struct page **pages, int addrinarray,
 {
        unsigned long start;
        unsigned long end;
+       enum page_cache_mode set_type;
        int i;
        int free_idx;
        int ret;
@@ -1695,8 +1719,12 @@ static int _set_pages_array(struct page **pages, int addrinarray,
                        goto err_out;
        }
 
+       /* If WC, set to UC- first and then WC */
+       set_type = (new_type == _PAGE_CACHE_MODE_WC) ?
+                               _PAGE_CACHE_MODE_UC_MINUS : new_type;
+
        ret = cpa_set_pages_array(pages, addrinarray,
-                       cachemode2pgprot(_PAGE_CACHE_MODE_UC_MINUS));
+                                 cachemode2pgprot(set_type));
        if (!ret && new_type == _PAGE_CACHE_MODE_WC)
                ret = change_page_attr_set_clr(NULL, addrinarray,
                                               cachemode2pgprot(
@@ -1730,6 +1758,12 @@ int set_pages_array_wc(struct page **pages, int addrinarray)
 }
 EXPORT_SYMBOL(set_pages_array_wc);
 
+int set_pages_array_wt(struct page **pages, int addrinarray)
+{
+       return _set_pages_array(pages, addrinarray, _PAGE_CACHE_MODE_WT);
+}
+EXPORT_SYMBOL_GPL(set_pages_array_wt);
+
 int set_pages_wb(struct page *page, int numpages)
 {
        unsigned long addr = (unsigned long)page_address(page);
index 35af6771a95ad6c126cca1f352b896998116876e..188e3e07eeeba7c0eb6555c138a16e97c3e5d787 100644 (file)
 #include "pat_internal.h"
 #include "mm_internal.h"
 
-#ifdef CONFIG_X86_PAT
-int __read_mostly pat_enabled = 1;
+#undef pr_fmt
+#define pr_fmt(fmt) "" fmt
+
+static bool boot_cpu_done;
+
+static int __read_mostly __pat_enabled = IS_ENABLED(CONFIG_X86_PAT);
 
 static inline void pat_disable(const char *reason)
 {
-       pat_enabled = 0;
-       printk(KERN_INFO "%s\n", reason);
+       __pat_enabled = 0;
+       pr_info("x86/PAT: %s\n", reason);
 }
 
 static int __init nopat(char *str)
@@ -48,13 +52,12 @@ static int __init nopat(char *str)
        return 0;
 }
 early_param("nopat", nopat);
-#else
-static inline void pat_disable(const char *reason)
+
+bool pat_enabled(void)
 {
-       (void)reason;
+       return !!__pat_enabled;
 }
-#endif
-
+EXPORT_SYMBOL_GPL(pat_enabled);
 
 int pat_debug_enable;
 
@@ -65,22 +68,24 @@ static int __init pat_debug_setup(char *str)
 }
 __setup("debugpat", pat_debug_setup);
 
-static u64 __read_mostly boot_pat_state;
-
 #ifdef CONFIG_X86_PAT
 /*
- * X86 PAT uses page flags WC and Uncached together to keep track of
- * memory type of pages that have backing page struct. X86 PAT supports 3
- * different memory types, _PAGE_CACHE_MODE_WB, _PAGE_CACHE_MODE_WC and
- * _PAGE_CACHE_MODE_UC_MINUS and fourth state where page's memory type has not
- * been changed from its default (value of -1 used to denote this).
- * Note we do not support _PAGE_CACHE_MODE_UC here.
+ * X86 PAT uses page flags arch_1 and uncached together to keep track of
+ * memory type of pages that have backing page struct.
+ *
+ * X86 PAT supports 4 different memory types:
+ *  - _PAGE_CACHE_MODE_WB
+ *  - _PAGE_CACHE_MODE_WC
+ *  - _PAGE_CACHE_MODE_UC_MINUS
+ *  - _PAGE_CACHE_MODE_WT
+ *
+ * _PAGE_CACHE_MODE_WB is the default type.
  */
 
-#define _PGMT_DEFAULT          0
+#define _PGMT_WB               0
 #define _PGMT_WC               (1UL << PG_arch_1)
 #define _PGMT_UC_MINUS         (1UL << PG_uncached)
-#define _PGMT_WB               (1UL << PG_uncached | 1UL << PG_arch_1)
+#define _PGMT_WT               (1UL << PG_uncached | 1UL << PG_arch_1)
 #define _PGMT_MASK             (1UL << PG_uncached | 1UL << PG_arch_1)
 #define _PGMT_CLEAR_MASK       (~_PGMT_MASK)
 
@@ -88,14 +93,14 @@ static inline enum page_cache_mode get_page_memtype(struct page *pg)
 {
        unsigned long pg_flags = pg->flags & _PGMT_MASK;
 
-       if (pg_flags == _PGMT_DEFAULT)
-               return -1;
+       if (pg_flags == _PGMT_WB)
+               return _PAGE_CACHE_MODE_WB;
        else if (pg_flags == _PGMT_WC)
                return _PAGE_CACHE_MODE_WC;
        else if (pg_flags == _PGMT_UC_MINUS)
                return _PAGE_CACHE_MODE_UC_MINUS;
        else
-               return _PAGE_CACHE_MODE_WB;
+               return _PAGE_CACHE_MODE_WT;
 }
 
 static inline void set_page_memtype(struct page *pg,
@@ -112,11 +117,12 @@ static inline void set_page_memtype(struct page *pg,
        case _PAGE_CACHE_MODE_UC_MINUS:
                memtype_flags = _PGMT_UC_MINUS;
                break;
-       case _PAGE_CACHE_MODE_WB:
-               memtype_flags = _PGMT_WB;
+       case _PAGE_CACHE_MODE_WT:
+               memtype_flags = _PGMT_WT;
                break;
+       case _PAGE_CACHE_MODE_WB:
        default:
-               memtype_flags = _PGMT_DEFAULT;
+               memtype_flags = _PGMT_WB;
                break;
        }
 
@@ -174,78 +180,154 @@ static enum page_cache_mode pat_get_cache_mode(unsigned pat_val, char *msg)
  * configuration.
  * Using lower indices is preferred, so we start with highest index.
  */
-void pat_init_cache_modes(void)
+void pat_init_cache_modes(u64 pat)
 {
-       int i;
        enum page_cache_mode cache;
        char pat_msg[33];
-       u64 pat;
+       int i;
 
-       rdmsrl(MSR_IA32_CR_PAT, pat);
        pat_msg[32] = 0;
        for (i = 7; i >= 0; i--) {
                cache = pat_get_cache_mode((pat >> (i * 8)) & 7,
                                           pat_msg + 4 * i);
                update_cache_mode_entry(i, cache);
        }
-       pr_info("PAT configuration [0-7]: %s\n", pat_msg);
+       pr_info("x86/PAT: Configuration [0-7]: %s\n", pat_msg);
 }
 
 #define PAT(x, y)      ((u64)PAT_ ## y << ((x)*8))
 
-void pat_init(void)
+static void pat_bsp_init(u64 pat)
 {
-       u64 pat;
-       bool boot_cpu = !boot_pat_state;
+       u64 tmp_pat;
 
-       if (!pat_enabled)
+       if (!cpu_has_pat) {
+               pat_disable("PAT not supported by CPU.");
                return;
+       }
 
-       if (!cpu_has_pat) {
-               if (!boot_pat_state) {
-                       pat_disable("PAT not supported by CPU.");
-                       return;
-               } else {
-                       /*
-                        * If this happens we are on a secondary CPU, but
-                        * switched to PAT on the boot CPU. We have no way to
-                        * undo PAT.
-                        */
-                       printk(KERN_ERR "PAT enabled, "
-                              "but not supported by secondary CPU\n");
-                       BUG();
-               }
+       if (!pat_enabled())
+               goto done;
+
+       rdmsrl(MSR_IA32_CR_PAT, tmp_pat);
+       if (!tmp_pat) {
+               pat_disable("PAT MSR is 0, disabled.");
+               return;
        }
 
-       /* Set PWT to Write-Combining. All other bits stay the same */
-       /*
-        * PTE encoding used in Linux:
-        *      PAT
-        *      |PCD
-        *      ||PWT
-        *      |||
-        *      000 WB          _PAGE_CACHE_WB
-        *      001 WC          _PAGE_CACHE_WC
-        *      010 UC-         _PAGE_CACHE_UC_MINUS
-        *      011 UC          _PAGE_CACHE_UC
-        * PAT bit unused
-        */
-       pat = PAT(0, WB) | PAT(1, WC) | PAT(2, UC_MINUS) | PAT(3, UC) |
-             PAT(4, WB) | PAT(5, WC) | PAT(6, UC_MINUS) | PAT(7, UC);
-
-       /* Boot CPU check */
-       if (!boot_pat_state) {
-               rdmsrl(MSR_IA32_CR_PAT, boot_pat_state);
-               if (!boot_pat_state) {
-                       pat_disable("PAT read returns always zero, disabled.");
-                       return;
-               }
+       wrmsrl(MSR_IA32_CR_PAT, pat);
+
+done:
+       pat_init_cache_modes(pat);
+}
+
+static void pat_ap_init(u64 pat)
+{
+       if (!pat_enabled())
+               return;
+
+       if (!cpu_has_pat) {
+               /*
+                * If this happens we are on a secondary CPU, but switched to
+                * PAT on the boot CPU. We have no way to undo PAT.
+                */
+               panic("x86/PAT: PAT enabled, but not supported by secondary CPU\n");
        }
 
        wrmsrl(MSR_IA32_CR_PAT, pat);
+}
+
+void pat_init(void)
+{
+       u64 pat;
+       struct cpuinfo_x86 *c = &boot_cpu_data;
+
+       if (!pat_enabled()) {
+               /*
+                * No PAT. Emulate the PAT table that corresponds to the two
+                * cache bits, PWT (Write Through) and PCD (Cache Disable). This
+                * setup is the same as the BIOS default setup when the system
+                * has PAT but the "nopat" boot option has been specified. This
+                * emulated PAT table is used when MSR_IA32_CR_PAT returns 0.
+                *
+                * PTE encoding:
+                *
+                *       PCD
+                *       |PWT  PAT
+                *       ||    slot
+                *       00    0    WB : _PAGE_CACHE_MODE_WB
+                *       01    1    WT : _PAGE_CACHE_MODE_WT
+                *       10    2    UC-: _PAGE_CACHE_MODE_UC_MINUS
+                *       11    3    UC : _PAGE_CACHE_MODE_UC
+                *
+                * NOTE: When WC or WP is used, it is redirected to UC- per
+                * the default setup in __cachemode2pte_tbl[].
+                */
+               pat = PAT(0, WB) | PAT(1, WT) | PAT(2, UC_MINUS) | PAT(3, UC) |
+                     PAT(4, WB) | PAT(5, WT) | PAT(6, UC_MINUS) | PAT(7, UC);
 
-       if (boot_cpu)
-               pat_init_cache_modes();
+       } else if ((c->x86_vendor == X86_VENDOR_INTEL) &&
+                  (((c->x86 == 0x6) && (c->x86_model <= 0xd)) ||
+                   ((c->x86 == 0xf) && (c->x86_model <= 0x6)))) {
+               /*
+                * PAT support with the lower four entries. Intel Pentium 2,
+                * 3, M, and 4 are affected by PAT errata, which makes the
+                * upper four entries unusable. To be on the safe side, we don't
+                * use those.
+                *
+                *  PTE encoding:
+                *      PAT
+                *      |PCD
+                *      ||PWT  PAT
+                *      |||    slot
+                *      000    0    WB : _PAGE_CACHE_MODE_WB
+                *      001    1    WC : _PAGE_CACHE_MODE_WC
+                *      010    2    UC-: _PAGE_CACHE_MODE_UC_MINUS
+                *      011    3    UC : _PAGE_CACHE_MODE_UC
+                * PAT bit unused
+                *
+                * NOTE: When WT or WP is used, it is redirected to UC- per
+                * the default setup in __cachemode2pte_tbl[].
+                */
+               pat = PAT(0, WB) | PAT(1, WC) | PAT(2, UC_MINUS) | PAT(3, UC) |
+                     PAT(4, WB) | PAT(5, WC) | PAT(6, UC_MINUS) | PAT(7, UC);
+       } else {
+               /*
+                * Full PAT support.  We put WT in slot 7 to improve
+                * robustness in the presence of errata that might cause
+                * the high PAT bit to be ignored.  This way, a buggy slot 7
+                * access will hit slot 3, and slot 3 is UC, so at worst
+                * we lose performance without causing a correctness issue.
+                * Pentium 4 erratum N46 is an example for such an erratum,
+                * although we try not to use PAT at all on affected CPUs.
+                *
+                *  PTE encoding:
+                *      PAT
+                *      |PCD
+                *      ||PWT  PAT
+                *      |||    slot
+                *      000    0    WB : _PAGE_CACHE_MODE_WB
+                *      001    1    WC : _PAGE_CACHE_MODE_WC
+                *      010    2    UC-: _PAGE_CACHE_MODE_UC_MINUS
+                *      011    3    UC : _PAGE_CACHE_MODE_UC
+                *      100    4    WB : Reserved
+                *      101    5    WC : Reserved
+                *      110    6    UC-: Reserved
+                *      111    7    WT : _PAGE_CACHE_MODE_WT
+                *
+                * The reserved slots are unused, but mapped to their
+                * corresponding types in the presence of PAT errata.
+                */
+               pat = PAT(0, WB) | PAT(1, WC) | PAT(2, UC_MINUS) | PAT(3, UC) |
+                     PAT(4, WB) | PAT(5, WC) | PAT(6, UC_MINUS) | PAT(7, WT);
+       }
+
+       if (!boot_cpu_done) {
+               pat_bsp_init(pat);
+               boot_cpu_done = true;
+       } else {
+               pat_ap_init(pat);
+       }
 }
 
 #undef PAT
@@ -267,9 +349,9 @@ static unsigned long pat_x_mtrr_type(u64 start, u64 end,
         * request is for WB.
         */
        if (req_type == _PAGE_CACHE_MODE_WB) {
-               u8 mtrr_type;
+               u8 mtrr_type, uniform;
 
-               mtrr_type = mtrr_type_lookup(start, end);
+               mtrr_type = mtrr_type_lookup(start, end, &uniform);
                if (mtrr_type != MTRR_TYPE_WRBACK)
                        return _PAGE_CACHE_MODE_UC_MINUS;
 
@@ -324,9 +406,14 @@ static int pat_pagerange_is_ram(resource_size_t start, resource_size_t end)
 
 /*
  * For RAM pages, we use page flags to mark the pages with appropriate type.
- * Here we do two pass:
- * - Find the memtype of all the pages in the range, look for any conflicts
- * - In case of no conflicts, set the new memtype for pages in the range
+ * The page flags are limited to four types, WB (default), WC, WT and UC-.
+ * WP request fails with -EINVAL, and UC gets redirected to UC-.  Setting
+ * a new memory type is only allowed for a page mapped with the default WB
+ * type.
+ *
+ * Here we do two passes:
+ * - Find the memtype of all the pages in the range, look for any conflicts.
+ * - In case of no conflicts, set the new memtype for pages in the range.
  */
 static int reserve_ram_pages_type(u64 start, u64 end,
                                  enum page_cache_mode req_type,
@@ -335,6 +422,12 @@ static int reserve_ram_pages_type(u64 start, u64 end,
        struct page *page;
        u64 pfn;
 
+       if (req_type == _PAGE_CACHE_MODE_WP) {
+               if (new_type)
+                       *new_type = _PAGE_CACHE_MODE_UC_MINUS;
+               return -EINVAL;
+       }
+
        if (req_type == _PAGE_CACHE_MODE_UC) {
                /* We do not support strong UC */
                WARN_ON_ONCE(1);
@@ -346,8 +439,8 @@ static int reserve_ram_pages_type(u64 start, u64 end,
 
                page = pfn_to_page(pfn);
                type = get_page_memtype(page);
-               if (type != -1) {
-                       pr_info("reserve_ram_pages_type failed [mem %#010Lx-%#010Lx], track 0x%x, req 0x%x\n",
+               if (type != _PAGE_CACHE_MODE_WB) {
+                       pr_info("x86/PAT: reserve_ram_pages_type failed [mem %#010Lx-%#010Lx], track 0x%x, req 0x%x\n",
                                start, end - 1, type, req_type);
                        if (new_type)
                                *new_type = type;
@@ -373,7 +466,7 @@ static int free_ram_pages_type(u64 start, u64 end)
 
        for (pfn = (start >> PAGE_SHIFT); pfn < (end >> PAGE_SHIFT); ++pfn) {
                page = pfn_to_page(pfn);
-               set_page_memtype(page, -1);
+               set_page_memtype(page, _PAGE_CACHE_MODE_WB);
        }
        return 0;
 }
@@ -384,6 +477,7 @@ static int free_ram_pages_type(u64 start, u64 end)
  * - _PAGE_CACHE_MODE_WC
  * - _PAGE_CACHE_MODE_UC_MINUS
  * - _PAGE_CACHE_MODE_UC
+ * - _PAGE_CACHE_MODE_WT
  *
  * If new_type is NULL, function will return an error if it cannot reserve the
  * region with req_type. If new_type is non-NULL, function will return
@@ -400,14 +494,10 @@ int reserve_memtype(u64 start, u64 end, enum page_cache_mode req_type,
 
        BUG_ON(start >= end); /* end is exclusive */
 
-       if (!pat_enabled) {
+       if (!pat_enabled()) {
                /* This is identical to page table setting without PAT */
-               if (new_type) {
-                       if (req_type == _PAGE_CACHE_MODE_WC)
-                               *new_type = _PAGE_CACHE_MODE_UC_MINUS;
-                       else
-                               *new_type = req_type;
-               }
+               if (new_type)
+                       *new_type = req_type;
                return 0;
        }
 
@@ -451,9 +541,9 @@ int reserve_memtype(u64 start, u64 end, enum page_cache_mode req_type,
 
        err = rbt_memtype_check_insert(new, new_type);
        if (err) {
-               printk(KERN_INFO "reserve_memtype failed [mem %#010Lx-%#010Lx], track %s, req %s\n",
-                      start, end - 1,
-                      cattr_name(new->type), cattr_name(req_type));
+               pr_info("x86/PAT: reserve_memtype failed [mem %#010Lx-%#010Lx], track %s, req %s\n",
+                       start, end - 1,
+                       cattr_name(new->type), cattr_name(req_type));
                kfree(new);
                spin_unlock(&memtype_lock);
 
@@ -475,7 +565,7 @@ int free_memtype(u64 start, u64 end)
        int is_range_ram;
        struct memtype *entry;
 
-       if (!pat_enabled)
+       if (!pat_enabled())
                return 0;
 
        /* Low ISA region is always mapped WB. No need to track */
@@ -497,8 +587,8 @@ int free_memtype(u64 start, u64 end)
        spin_unlock(&memtype_lock);
 
        if (!entry) {
-               printk(KERN_INFO "%s:%d freeing invalid memtype [mem %#010Lx-%#010Lx]\n",
-                      current->comm, current->pid, start, end - 1);
+               pr_info("x86/PAT: %s:%d freeing invalid memtype [mem %#010Lx-%#010Lx]\n",
+                       current->comm, current->pid, start, end - 1);
                return -EINVAL;
        }
 
@@ -517,7 +607,7 @@ int free_memtype(u64 start, u64 end)
  * Only to be called when PAT is enabled
  *
  * Returns _PAGE_CACHE_MODE_WB, _PAGE_CACHE_MODE_WC, _PAGE_CACHE_MODE_UC_MINUS
- * or _PAGE_CACHE_MODE_UC
+ * or _PAGE_CACHE_MODE_WT.
  */
 static enum page_cache_mode lookup_memtype(u64 paddr)
 {
@@ -529,16 +619,9 @@ static enum page_cache_mode lookup_memtype(u64 paddr)
 
        if (pat_pagerange_is_ram(paddr, paddr + PAGE_SIZE)) {
                struct page *page;
-               page = pfn_to_page(paddr >> PAGE_SHIFT);
-               rettype = get_page_memtype(page);
-               /*
-                * -1 from get_page_memtype() implies RAM page is in its
-                * default state and not reserved, and hence of type WB
-                */
-               if (rettype == -1)
-                       rettype = _PAGE_CACHE_MODE_WB;
 
-               return rettype;
+               page = pfn_to_page(paddr >> PAGE_SHIFT);
+               return get_page_memtype(page);
        }
 
        spin_lock(&memtype_lock);
@@ -623,13 +706,13 @@ static inline int range_is_allowed(unsigned long pfn, unsigned long size)
        u64 to = from + size;
        u64 cursor = from;
 
-       if (!pat_enabled)
+       if (!pat_enabled())
                return 1;
 
        while (cursor < to) {
                if (!devmem_is_allowed(pfn)) {
-                       printk(KERN_INFO "Program %s tried to access /dev/mem between [mem %#010Lx-%#010Lx], PAT prevents it\n",
-                              current->comm, from, to - 1);
+                       pr_info("x86/PAT: Program %s tried to access /dev/mem between [mem %#010Lx-%#010Lx], PAT prevents it\n",
+                               current->comm, from, to - 1);
                        return 0;
                }
                cursor += PAGE_SIZE;
@@ -659,7 +742,7 @@ int phys_mem_access_prot_allowed(struct file *file, unsigned long pfn,
         * caching for the high addresses through the KEN pin, but
         * we maintain the tradition of paranoia in this code.
         */
-       if (!pat_enabled &&
+       if (!pat_enabled() &&
            !(boot_cpu_has(X86_FEATURE_MTRR) ||
              boot_cpu_has(X86_FEATURE_K6_MTRR) ||
              boot_cpu_has(X86_FEATURE_CYRIX_ARR) ||
@@ -698,8 +781,7 @@ int kernel_map_sync_memtype(u64 base, unsigned long size,
                                size;
 
        if (ioremap_change_attr((unsigned long)__va(base), id_sz, pcm) < 0) {
-               printk(KERN_INFO "%s:%d ioremap_change_attr failed %s "
-                       "for [mem %#010Lx-%#010Lx]\n",
+               pr_info("x86/PAT: %s:%d ioremap_change_attr failed %s for [mem %#010Lx-%#010Lx]\n",
                        current->comm, current->pid,
                        cattr_name(pcm),
                        base, (unsigned long long)(base + size-1));
@@ -729,12 +811,12 @@ static int reserve_pfn_range(u64 paddr, unsigned long size, pgprot_t *vma_prot,
         * the type requested matches the type of first page in the range.
         */
        if (is_ram) {
-               if (!pat_enabled)
+               if (!pat_enabled())
                        return 0;
 
                pcm = lookup_memtype(paddr);
                if (want_pcm != pcm) {
-                       printk(KERN_WARNING "%s:%d map pfn RAM range req %s for [mem %#010Lx-%#010Lx], got %s\n",
+                       pr_warn("x86/PAT: %s:%d map pfn RAM range req %s for [mem %#010Lx-%#010Lx], got %s\n",
                                current->comm, current->pid,
                                cattr_name(want_pcm),
                                (unsigned long long)paddr,
@@ -755,13 +837,12 @@ static int reserve_pfn_range(u64 paddr, unsigned long size, pgprot_t *vma_prot,
                if (strict_prot ||
                    !is_new_memtype_allowed(paddr, size, want_pcm, pcm)) {
                        free_memtype(paddr, paddr + size);
-                       printk(KERN_ERR "%s:%d map pfn expected mapping type %s"
-                               " for [mem %#010Lx-%#010Lx], got %s\n",
-                               current->comm, current->pid,
-                               cattr_name(want_pcm),
-                               (unsigned long long)paddr,
-                               (unsigned long long)(paddr + size - 1),
-                               cattr_name(pcm));
+                       pr_err("x86/PAT: %s:%d map pfn expected mapping type %s for [mem %#010Lx-%#010Lx], got %s\n",
+                              current->comm, current->pid,
+                              cattr_name(want_pcm),
+                              (unsigned long long)paddr,
+                              (unsigned long long)(paddr + size - 1),
+                              cattr_name(pcm));
                        return -EINVAL;
                }
                /*
@@ -844,7 +925,7 @@ int track_pfn_remap(struct vm_area_struct *vma, pgprot_t *prot,
                return ret;
        }
 
-       if (!pat_enabled)
+       if (!pat_enabled())
                return 0;
 
        /*
@@ -872,7 +953,7 @@ int track_pfn_insert(struct vm_area_struct *vma, pgprot_t *prot,
 {
        enum page_cache_mode pcm;
 
-       if (!pat_enabled)
+       if (!pat_enabled())
                return 0;
 
        /* Set prot based on lookup */
@@ -913,14 +994,18 @@ void untrack_pfn(struct vm_area_struct *vma, unsigned long pfn,
 
 pgprot_t pgprot_writecombine(pgprot_t prot)
 {
-       if (pat_enabled)
-               return __pgprot(pgprot_val(prot) |
+       return __pgprot(pgprot_val(prot) |
                                cachemode2protval(_PAGE_CACHE_MODE_WC));
-       else
-               return pgprot_noncached(prot);
 }
 EXPORT_SYMBOL_GPL(pgprot_writecombine);
 
+pgprot_t pgprot_writethrough(pgprot_t prot)
+{
+       return __pgprot(pgprot_val(prot) |
+                               cachemode2protval(_PAGE_CACHE_MODE_WT));
+}
+EXPORT_SYMBOL_GPL(pgprot_writethrough);
+
 #if defined(CONFIG_DEBUG_FS) && defined(CONFIG_X86_PAT)
 
 static struct memtype *memtype_get_idx(loff_t pos)
@@ -996,7 +1081,7 @@ static const struct file_operations memtype_fops = {
 
 static int __init pat_memtype_list_init(void)
 {
-       if (pat_enabled) {
+       if (pat_enabled()) {
                debugfs_create_file("pat_memtype_list", S_IRUSR,
                                    arch_debugfs_dir, NULL, &memtype_fops);
        }
index f6411620305d89f89ba7d3430e252355c29fea6b..a739bfc40690b77537ca774b74092fe1beefdcce 100644 (file)
@@ -4,7 +4,7 @@
 extern int pat_debug_enable;
 
 #define dprintk(fmt, arg...) \
-       do { if (pat_debug_enable) printk(KERN_INFO fmt, ##arg); } while (0)
+       do { if (pat_debug_enable) pr_info("x86/PAT: " fmt, ##arg); } while (0)
 
 struct memtype {
        u64                     start;
index 6582adcc8bd935b96df0c428308255fa262710ea..63931080366aaae1626c48b8ec780acd78d09569 100644 (file)
@@ -160,9 +160,9 @@ success:
        return 0;
 
 failure:
-       printk(KERN_INFO "%s:%d conflicting memory types "
-               "%Lx-%Lx %s<->%s\n", current->comm, current->pid, start,
-               end, cattr_name(found_type), cattr_name(match->type));
+       pr_info("x86/PAT: %s:%d conflicting memory types %Lx-%Lx %s<->%s\n",
+               current->comm, current->pid, start, end,
+               cattr_name(found_type), cattr_name(match->type));
        return -EBUSY;
 }
 
index 0b97d2c75df3d5fc05b1caafdadce2972660543d..fb0a9dd1d6e46fc6e6921bf29df9f71e830fdcb8 100644 (file)
@@ -563,16 +563,31 @@ void native_set_fixmap(enum fixed_addresses idx, phys_addr_t phys,
 }
 
 #ifdef CONFIG_HAVE_ARCH_HUGE_VMAP
+/**
+ * pud_set_huge - setup kernel PUD mapping
+ *
+ * MTRRs can override PAT memory types with 4KiB granularity. Therefore, this
+ * function sets up a huge page only if any of the following conditions are met:
+ *
+ * - MTRRs are disabled, or
+ *
+ * - MTRRs are enabled and the range is completely covered by a single MTRR, or
+ *
+ * - MTRRs are enabled and the corresponding MTRR memory type is WB, which
+ *   has no effect on the requested PAT memory type.
+ *
+ * Callers should try to decrease page size (1GB -> 2MB -> 4K) if the bigger
+ * page mapping attempt fails.
+ *
+ * Returns 1 on success and 0 on failure.
+ */
 int pud_set_huge(pud_t *pud, phys_addr_t addr, pgprot_t prot)
 {
-       u8 mtrr;
+       u8 mtrr, uniform;
 
-       /*
-        * Do not use a huge page when the range is covered by non-WB type
-        * of MTRRs.
-        */
-       mtrr = mtrr_type_lookup(addr, addr + PUD_SIZE);
-       if ((mtrr != MTRR_TYPE_WRBACK) && (mtrr != 0xFF))
+       mtrr = mtrr_type_lookup(addr, addr + PUD_SIZE, &uniform);
+       if ((mtrr != MTRR_TYPE_INVALID) && (!uniform) &&
+           (mtrr != MTRR_TYPE_WRBACK))
                return 0;
 
        prot = pgprot_4k_2_large(prot);
@@ -584,17 +599,24 @@ int pud_set_huge(pud_t *pud, phys_addr_t addr, pgprot_t prot)
        return 1;
 }
 
+/**
+ * pmd_set_huge - setup kernel PMD mapping
+ *
+ * See text over pud_set_huge() above.
+ *
+ * Returns 1 on success and 0 on failure.
+ */
 int pmd_set_huge(pmd_t *pmd, phys_addr_t addr, pgprot_t prot)
 {
-       u8 mtrr;
+       u8 mtrr, uniform;
 
-       /*
-        * Do not use a huge page when the range is covered by non-WB type
-        * of MTRRs.
-        */
-       mtrr = mtrr_type_lookup(addr, addr + PMD_SIZE);
-       if ((mtrr != MTRR_TYPE_WRBACK) && (mtrr != 0xFF))
+       mtrr = mtrr_type_lookup(addr, addr + PMD_SIZE, &uniform);
+       if ((mtrr != MTRR_TYPE_INVALID) && (!uniform) &&
+           (mtrr != MTRR_TYPE_WRBACK)) {
+               pr_warn_once("%s: Cannot satisfy [mem %#010llx-%#010llx] with a huge-page mapping due to MTRR override.\n",
+                            __func__, addr, addr + PMD_SIZE);
                return 0;
+       }
 
        prot = pgprot_4k_2_large(prot);
 
@@ -605,6 +627,11 @@ int pmd_set_huge(pmd_t *pmd, phys_addr_t addr, pgprot_t prot)
        return 1;
 }
 
+/**
+ * pud_clear_huge - clear kernel PUD mapping when it is set
+ *
+ * Returns 1 on success and 0 on failure (no PUD map is found).
+ */
 int pud_clear_huge(pud_t *pud)
 {
        if (pud_large(*pud)) {
@@ -615,6 +642,11 @@ int pud_clear_huge(pud_t *pud)
        return 0;
 }
 
+/**
+ * pmd_clear_huge - clear kernel PMD mapping when it is set
+ *
+ * Returns 1 on success and 0 on failure (no PMD map is found).
+ */
 int pmd_clear_huge(pmd_t *pmd)
 {
        if (pmd_large(*pmd)) {
index 6440221ced0d4925d3fee4a0c11b424a6b696f2d..4093216b3791311c995bfb2f644deed3845ef708 100644 (file)
@@ -8,7 +8,6 @@
  * of the License.
  */
 #include <linux/linkage.h>
-#include <asm/dwarf2.h>
 
 /*
  * Calling convention :
index 14a63ed6fe092cd3f512d155046942a28c3744e8..ff9911707160ac7691e119aad62f32d7f61c143a 100644 (file)
@@ -81,6 +81,17 @@ static const struct dmi_system_id pci_crs_quirks[] __initconst = {
                        DMI_MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies, LTD"),
                },
        },
+       /* https://bugs.launchpad.net/ubuntu/+source/alsa-driver/+bug/931368 */
+       /* https://bugs.launchpad.net/ubuntu/+source/alsa-driver/+bug/1033299 */
+       {
+               .callback = set_use_crs,
+               .ident = "Foxconn K8M890-8237A",
+               .matches = {
+                       DMI_MATCH(DMI_BOARD_VENDOR, "Foxconn"),
+                       DMI_MATCH(DMI_BOARD_NAME, "K8M890-8237A"),
+                       DMI_MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies, LTD"),
+               },
+       },
 
        /* Now for the blacklist.. */
 
@@ -121,8 +132,10 @@ void __init pci_acpi_crs_quirks(void)
 {
        int year;
 
-       if (dmi_get_date(DMI_BIOS_DATE, &year, NULL, NULL) && year < 2008)
-               pci_use_crs = false;
+       if (dmi_get_date(DMI_BIOS_DATE, &year, NULL, NULL) && year < 2008) {
+               if (iomem_resource.end <= 0xffffffff)
+                       pci_use_crs = false;
+       }
 
        dmi_check_system(pci_crs_quirks);
 
index 349c0d32cc0b140222141cfec5fa29a8c6ddbace..0a9f2caf358ff7230e6afa569219234127490c1a 100644 (file)
@@ -429,12 +429,12 @@ int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma,
         * Caller can followup with UC MINUS request and add a WC mtrr if there
         * is a free mtrr slot.
         */
-       if (!pat_enabled && write_combine)
+       if (!pat_enabled() && write_combine)
                return -EINVAL;
 
-       if (pat_enabled && write_combine)
+       if (pat_enabled() && write_combine)
                prot |= cachemode2protval(_PAGE_CACHE_MODE_WC);
-       else if (pat_enabled || boot_cpu_data.x86 > 3)
+       else if (pat_enabled() || boot_cpu_data.x86 > 3)
                /*
                 * ioremap() and ioremap_nocache() defaults to UC MINUS for now.
                 * To avoid attribute conflicts, request UC MINUS here
index 852aa4c92da027cb07fb64c77c855aaf0877a1da..27062303c88135b5d614455ed372861516903e53 100644 (file)
@@ -208,6 +208,7 @@ static int pci_write(struct pci_bus *bus, unsigned int devfn, int where,
 
 static int intel_mid_pci_irq_enable(struct pci_dev *dev)
 {
+       struct irq_alloc_info info;
        int polarity;
 
        if (dev->irq_managed && dev->irq > 0)
@@ -217,14 +218,13 @@ static int intel_mid_pci_irq_enable(struct pci_dev *dev)
                polarity = 0; /* active high */
        else
                polarity = 1; /* active low */
+       ioapic_set_alloc_attr(&info, dev_to_node(&dev->dev), 1, polarity);
 
        /*
         * MRST only have IOAPIC, the PCI irq lines are 1:1 mapped to
         * IOAPIC RTE entries, so we just enable RTE for the device.
         */
-       if (mp_set_gsi_attr(dev->irq, 1, polarity, dev_to_node(&dev->dev)))
-               return -EBUSY;
-       if (mp_map_gsi_to_irq(dev->irq, IOAPIC_MAP_ALLOC) < 0)
+       if (mp_map_gsi_to_irq(dev->irq, IOAPIC_MAP_ALLOC, &info) < 0)
                return -EBUSY;
 
        dev->irq_managed = 1;
index 5dc6ca5e174131d2c7208ea1ed86739ef4532d22..9bd115484745703791c6515b289938546d2478ab 100644 (file)
@@ -146,19 +146,20 @@ static void __init pirq_peer_trick(void)
 
 /*
  *  Code for querying and setting of IRQ routes on various interrupt routers.
+ *  PIC Edge/Level Control Registers (ELCR) 0x4d0 & 0x4d1.
  */
 
-void eisa_set_level_irq(unsigned int irq)
+void elcr_set_level_irq(unsigned int irq)
 {
        unsigned char mask = 1 << (irq & 7);
        unsigned int port = 0x4d0 + (irq >> 3);
        unsigned char val;
-       static u16 eisa_irq_mask;
+       static u16 elcr_irq_mask;
 
-       if (irq >= 16 || (1 << irq) & eisa_irq_mask)
+       if (irq >= 16 || (1 << irq) & elcr_irq_mask)
                return;
 
-       eisa_irq_mask |= (1 << irq);
+       elcr_irq_mask |= (1 << irq);
        printk(KERN_DEBUG "PCI: setting IRQ %u as level-triggered\n", irq);
        val = inb(port);
        if (!(val & mask)) {
@@ -965,11 +966,11 @@ static int pcibios_lookup_irq(struct pci_dev *dev, int assign)
        } else if (r->get && (irq = r->get(pirq_router_dev, dev, pirq)) && \
        ((!(pci_probe & PCI_USE_PIRQ_MASK)) || ((1 << irq) & mask))) {
                msg = "found";
-               eisa_set_level_irq(irq);
+               elcr_set_level_irq(irq);
        } else if (newirq && r->set &&
                (dev->class >> 8) != PCI_CLASS_DISPLAY_VGA) {
                if (r->set(pirq_router_dev, dev, pirq, newirq)) {
-                       eisa_set_level_irq(newirq);
+                       elcr_set_level_irq(newirq);
                        msg = "assigned";
                        irq = newirq;
                }
index a62e0be3a2f1b4f563ab5f6fda0acb59612ea523..f1a6c8e86ddd927e0b5f8d947b8f4e725a5b4933 100644 (file)
@@ -1,4 +1,5 @@
 # Platform specific code goes here
+obj-y  += atom/
 obj-y  += ce4100/
 obj-y  += efi/
 obj-y  += geode/
diff --git a/arch/x86/platform/atom/Makefile b/arch/x86/platform/atom/Makefile
new file mode 100644 (file)
index 0000000..0a3a40c
--- /dev/null
@@ -0,0 +1 @@
+obj-$(CONFIG_PUNIT_ATOM_DEBUG) += punit_atom_debug.o
diff --git a/arch/x86/platform/atom/punit_atom_debug.c b/arch/x86/platform/atom/punit_atom_debug.c
new file mode 100644 (file)
index 0000000..5ca8ead
--- /dev/null
@@ -0,0 +1,183 @@
+/*
+ * Intel SOC Punit device state debug driver
+ * Punit controls power management for North Complex devices (Graphics
+ * blocks, Image Signal Processing, video processing, display, DSP etc.)
+ *
+ * Copyright (c) 2015, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/io.h>
+#include <asm/cpu_device_id.h>
+#include <asm/iosf_mbi.h>
+
+/* Side band Interface port */
+#define PUNIT_PORT             0x04
+/* Power gate status reg */
+#define PWRGT_STATUS           0x61
+/* Subsystem config/status Video processor */
+#define VED_SS_PM0             0x32
+/* Subsystem config/status ISP (Image Signal Processor) */
+#define ISP_SS_PM0             0x39
+/* Subsystem config/status Input/output controller */
+#define MIO_SS_PM              0x3B
+/* Shift bits for getting status for video, isp and i/o */
+#define SSS_SHIFT              24
+/* Shift bits for getting status for graphics rendering */
+#define RENDER_POS             0
+/* Shift bits for getting status for media control */
+#define MEDIA_POS              2
+/* Shift bits for getting status for Valley View/Baytrail display */
+#define VLV_DISPLAY_POS                6
+/* Subsystem config/status display for Cherry Trail SOC */
+#define CHT_DSP_SSS            0x36
+/* Shift bits for getting status for display */
+#define CHT_DSP_SSS_POS                16
+
+struct punit_device {
+       char *name;
+       int reg;
+       int sss_pos;
+};
+
+static const struct punit_device punit_device_byt[] = {
+       { "GFX RENDER", PWRGT_STATUS,   RENDER_POS },
+       { "GFX MEDIA",  PWRGT_STATUS,   MEDIA_POS },
+       { "DISPLAY",    PWRGT_STATUS,   VLV_DISPLAY_POS },
+       { "VED",        VED_SS_PM0,     SSS_SHIFT },
+       { "ISP",        ISP_SS_PM0,     SSS_SHIFT },
+       { "MIO",        MIO_SS_PM,      SSS_SHIFT },
+       { NULL }
+};
+
+static const struct punit_device punit_device_cht[] = {
+       { "GFX RENDER", PWRGT_STATUS,   RENDER_POS },
+       { "GFX MEDIA",  PWRGT_STATUS,   MEDIA_POS },
+       { "DISPLAY",    CHT_DSP_SSS,    CHT_DSP_SSS_POS },
+       { "VED",        VED_SS_PM0,     SSS_SHIFT },
+       { "ISP",        ISP_SS_PM0,     SSS_SHIFT },
+       { "MIO",        MIO_SS_PM,      SSS_SHIFT },
+       { NULL }
+};
+
+static const char * const dstates[] = {"D0", "D0i1", "D0i2", "D0i3"};
+
+static int punit_dev_state_show(struct seq_file *seq_file, void *unused)
+{
+       u32 punit_pwr_status;
+       struct punit_device *punit_devp = seq_file->private;
+       int index;
+       int status;
+
+       seq_puts(seq_file, "\n\nPUNIT NORTH COMPLEX DEVICES :\n");
+       while (punit_devp->name) {
+               status = iosf_mbi_read(PUNIT_PORT, BT_MBI_PMC_READ,
+                                      punit_devp->reg,
+                                      &punit_pwr_status);
+               if (status) {
+                       seq_printf(seq_file, "%9s : Read Failed\n",
+                                  punit_devp->name);
+               } else  {
+                       index = (punit_pwr_status >> punit_devp->sss_pos) & 3;
+                       seq_printf(seq_file, "%9s : %s\n", punit_devp->name,
+                                  dstates[index]);
+               }
+               punit_devp++;
+       }
+
+       return 0;
+}
+
+static int punit_dev_state_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, punit_dev_state_show, inode->i_private);
+}
+
+static const struct file_operations punit_dev_state_ops = {
+       .open           = punit_dev_state_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static struct dentry *punit_dbg_file;
+
+static int punit_dbgfs_register(struct punit_device *punit_device)
+{
+       static struct dentry *dev_state;
+
+       punit_dbg_file = debugfs_create_dir("punit_atom", NULL);
+       if (!punit_dbg_file)
+               return -ENXIO;
+
+       dev_state = debugfs_create_file("dev_power_state", S_IFREG | S_IRUGO,
+                                       punit_dbg_file, punit_device,
+                                       &punit_dev_state_ops);
+       if (!dev_state) {
+               pr_err("punit_dev_state register failed\n");
+               debugfs_remove(punit_dbg_file);
+               return -ENXIO;
+       }
+
+       return 0;
+}
+
+static void punit_dbgfs_unregister(void)
+{
+       debugfs_remove_recursive(punit_dbg_file);
+}
+
+#define ICPU(model, drv_data) \
+       { X86_VENDOR_INTEL, 6, model, X86_FEATURE_MWAIT,\
+         (kernel_ulong_t)&drv_data }
+
+static const struct x86_cpu_id intel_punit_cpu_ids[] = {
+       ICPU(55, punit_device_byt), /* Valleyview, Bay Trail */
+       ICPU(76, punit_device_cht), /* Braswell, Cherry Trail */
+       {}
+};
+
+MODULE_DEVICE_TABLE(x86cpu, intel_punit_cpu_ids);
+
+static int __init punit_atom_debug_init(void)
+{
+       const struct x86_cpu_id *id;
+       int ret;
+
+       id = x86_match_cpu(intel_punit_cpu_ids);
+       if (!id)
+               return -ENODEV;
+
+       ret = punit_dbgfs_register((struct punit_device *)id->driver_data);
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+
+static void __exit punit_atom_debug_exit(void)
+{
+       punit_dbgfs_unregister();
+}
+
+module_init(punit_atom_debug_init);
+module_exit(punit_atom_debug_exit);
+
+MODULE_AUTHOR("Kumar P, Mahesh <mahesh.kumar.p@intel.com>");
+MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>");
+MODULE_DESCRIPTION("Driver for Punit devices states debugging");
+MODULE_LICENSE("GPL v2");
index 02744df576d52588a35308998ecc1a138435012e..3b984c3aa1b0b5ba6e7b5e5321edb4fbc7013e5d 100644 (file)
@@ -501,6 +501,8 @@ void __init efi_init(void)
 
        if (efi_enabled(EFI_DBG))
                print_efi_memmap();
+
+       efi_esrt_init();
 }
 
 void __init efi_late_init(void)
index 0b283d4d0ad770d945f407c4921d09ecacb34db4..de734134bc8d2e1733fb831a32cdbfd8fcf80ed5 100644 (file)
@@ -27,6 +27,7 @@ static struct platform_device wdt_dev = {
 static int tangier_probe(struct platform_device *pdev)
 {
        int gsi;
+       struct irq_alloc_info info;
        struct intel_mid_wdt_pdata *pdata = pdev->dev.platform_data;
 
        if (!pdata)
@@ -34,8 +35,8 @@ static int tangier_probe(struct platform_device *pdev)
 
        /* IOAPIC builds identity mapping between GSI and IRQ on MID */
        gsi = pdata->irq;
-       if (mp_set_gsi_attr(gsi, 1, 0, cpu_to_node(0)) ||
-           mp_map_gsi_to_irq(gsi, IOAPIC_MAP_ALLOC) <= 0) {
+       ioapic_set_alloc_attr(&info, cpu_to_node(0), 1, 0);
+       if (mp_map_gsi_to_irq(gsi, IOAPIC_MAP_ALLOC, &info) <= 0) {
                dev_warn(&pdev->dev, "cannot find interrupt %d in ioapic\n",
                         gsi);
                return -EINVAL;
index 3005f0c89f2ecfbcc817c7e379c97586d5524e7f..01d54ea766c16539d001da113320c56ee8eac1f8 100644 (file)
@@ -81,26 +81,34 @@ static unsigned long __init intel_mid_calibrate_tsc(void)
        return 0;
 }
 
+static void __init intel_mid_setup_bp_timer(void)
+{
+       apbt_time_init();
+       setup_boot_APIC_clock();
+}
+
 static void __init intel_mid_time_init(void)
 {
        sfi_table_parse(SFI_SIG_MTMR, NULL, NULL, sfi_parse_mtmr);
+
        switch (intel_mid_timer_options) {
        case INTEL_MID_TIMER_APBT_ONLY:
                break;
        case INTEL_MID_TIMER_LAPIC_APBT:
-               x86_init.timers.setup_percpu_clockev = setup_boot_APIC_clock;
+               /* Use apbt and local apic */
+               x86_init.timers.setup_percpu_clockev = intel_mid_setup_bp_timer;
                x86_cpuinit.setup_percpu_clockev = setup_secondary_APIC_clock;
-               break;
+               return;
        default:
                if (!boot_cpu_has(X86_FEATURE_ARAT))
                        break;
+               /* Lapic only, no apbt */
                x86_init.timers.setup_percpu_clockev = setup_boot_APIC_clock;
                x86_cpuinit.setup_percpu_clockev = setup_secondary_APIC_clock;
                return;
        }
-       /* we need at least one APB timer */
-       pre_init_apic_IRQ0();
-       apbt_time_init();
+
+       x86_init.timers.setup_percpu_clockev = apbt_time_init;
 }
 
 static void intel_mid_arch_setup(void)
index c14ad34776c466f3cb18fe4b0a50fe40db8cd3ec..ce992e8cc06526c4e61f1efb1b852e0f41df9ccf 100644 (file)
@@ -95,18 +95,16 @@ int __init sfi_parse_mtmr(struct sfi_table_header *table)
                pr_debug("timer[%d]: paddr = 0x%08x, freq = %dHz, irq = %d\n",
                        totallen, (u32)pentry->phys_addr,
                        pentry->freq_hz, pentry->irq);
-                       if (!pentry->irq)
-                               continue;
-                       mp_irq.type = MP_INTSRC;
-                       mp_irq.irqtype = mp_INT;
-/* triggering mode edge bit 2-3, active high polarity bit 0-1 */
-                       mp_irq.irqflag = 5;
-                       mp_irq.srcbus = MP_BUS_ISA;
-                       mp_irq.srcbusirq = pentry->irq; /* IRQ */
-                       mp_irq.dstapic = MP_APIC_ALL;
-                       mp_irq.dstirq = pentry->irq;
-                       mp_save_irq(&mp_irq);
-                       mp_map_gsi_to_irq(pentry->irq, IOAPIC_MAP_ALLOC);
+               mp_irq.type = MP_INTSRC;
+               mp_irq.irqtype = mp_INT;
+               /* triggering mode edge bit 2-3, active high polarity bit 0-1 */
+               mp_irq.irqflag = 5;
+               mp_irq.srcbus = MP_BUS_ISA;
+               mp_irq.srcbusirq = pentry->irq; /* IRQ */
+               mp_irq.dstapic = MP_APIC_ALL;
+               mp_irq.dstirq = pentry->irq;
+               mp_save_irq(&mp_irq);
+               mp_map_gsi_to_irq(pentry->irq, IOAPIC_MAP_ALLOC, NULL);
        }
 
        return 0;
@@ -177,7 +175,7 @@ int __init sfi_parse_mrtc(struct sfi_table_header *table)
                mp_irq.dstapic = MP_APIC_ALL;
                mp_irq.dstirq = pentry->irq;
                mp_save_irq(&mp_irq);
-               mp_map_gsi_to_irq(pentry->irq, IOAPIC_MAP_ALLOC);
+               mp_map_gsi_to_irq(pentry->irq, IOAPIC_MAP_ALLOC, NULL);
        }
        return 0;
 }
@@ -436,6 +434,7 @@ static int __init sfi_parse_devs(struct sfi_table_header *table)
        struct devs_id *dev = NULL;
        int num, i, ret;
        int polarity;
+       struct irq_alloc_info info;
 
        sb = (struct sfi_table_simple *)table;
        num = SFI_GET_NUM_ENTRIES(sb, struct sfi_device_table_entry);
@@ -469,9 +468,8 @@ static int __init sfi_parse_devs(struct sfi_table_header *table)
                                polarity = 1;
                        }
 
-                       ret = mp_set_gsi_attr(irq, 1, polarity, NUMA_NO_NODE);
-                       if (ret == 0)
-                               ret = mp_map_gsi_to_irq(irq, IOAPIC_MAP_ALLOC);
+                       ioapic_set_alloc_attr(&info, NUMA_NO_NODE, 1, polarity);
+                       ret = mp_map_gsi_to_irq(irq, IOAPIC_MAP_ALLOC, &info);
                        WARN_ON(ret < 0);
                }
 
index 2a8a74f3bd76c1338e2378c26bc231cc5adf9bea..6c7111bbd1e92faf169b9b046726f704c260e8d2 100644 (file)
@@ -25,8 +25,8 @@
 #include <linux/init.h>
 #include <linux/sfi.h>
 #include <linux/io.h>
-#include <linux/irqdomain.h>
 
+#include <asm/irqdomain.h>
 #include <asm/io_apic.h>
 #include <asm/mpspec.h>
 #include <asm/setup.h>
@@ -71,9 +71,6 @@ static int __init sfi_parse_cpus(struct sfi_table_header *table)
 #endif /* CONFIG_X86_LOCAL_APIC */
 
 #ifdef CONFIG_X86_IO_APIC
-static struct irq_domain_ops sfi_ioapic_irqdomain_ops = {
-       .map = mp_irqdomain_map,
-};
 
 static int __init sfi_parse_ioapic(struct sfi_table_header *table)
 {
@@ -82,7 +79,7 @@ static int __init sfi_parse_ioapic(struct sfi_table_header *table)
        int i, num;
        struct ioapic_domain_cfg cfg = {
                .type = IOAPIC_DOMAIN_STRICT,
-               .ops = &sfi_ioapic_irqdomain_ops,
+               .ops = &mp_ioapic_irqdomain_ops,
        };
 
        sb = (struct sfi_table_simple *)table;
index 0ce67364543242a21fd3d6e2b40f8864623ce668..8570abe68be1feb0b12bb1828a7126d07b94307d 100644 (file)
 #include <linux/slab.h>
 #include <linux/irq.h>
 
+#include <asm/irqdomain.h>
 #include <asm/apic.h>
 #include <asm/uv/uv_irq.h>
 #include <asm/uv/uv_hub.h>
 
 /* MMR offset and pnode of hub sourcing interrupts for a given irq */
-struct uv_irq_2_mmr_pnode{
-       struct rb_node          list;
+struct uv_irq_2_mmr_pnode {
        unsigned long           offset;
        int                     pnode;
-       int                     irq;
 };
 
-static DEFINE_SPINLOCK(uv_irq_lock);
-static struct rb_root          uv_irq_root;
+static void uv_program_mmr(struct irq_cfg *cfg, struct uv_irq_2_mmr_pnode *info)
+{
+       unsigned long mmr_value;
+       struct uv_IO_APIC_route_entry *entry;
+
+       BUILD_BUG_ON(sizeof(struct uv_IO_APIC_route_entry) !=
+                    sizeof(unsigned long));
+
+       mmr_value = 0;
+       entry = (struct uv_IO_APIC_route_entry *)&mmr_value;
+       entry->vector           = cfg->vector;
+       entry->delivery_mode    = apic->irq_delivery_mode;
+       entry->dest_mode        = apic->irq_dest_mode;
+       entry->polarity         = 0;
+       entry->trigger          = 0;
+       entry->mask             = 0;
+       entry->dest             = cfg->dest_apicid;
 
-static int uv_set_irq_affinity(struct irq_data *, const struct cpumask *, bool);
+       uv_write_global_mmr64(info->pnode, info->offset, mmr_value);
+}
 
 static void uv_noop(struct irq_data *data) { }
 
@@ -37,6 +52,23 @@ static void uv_ack_apic(struct irq_data *data)
        ack_APIC_irq();
 }
 
+static int
+uv_set_irq_affinity(struct irq_data *data, const struct cpumask *mask,
+                   bool force)
+{
+       struct irq_data *parent = data->parent_data;
+       struct irq_cfg *cfg = irqd_cfg(data);
+       int ret;
+
+       ret = parent->chip->irq_set_affinity(parent, mask, force);
+       if (ret >= 0) {
+               uv_program_mmr(cfg, data->chip_data);
+               send_cleanup_vector(cfg);
+       }
+
+       return ret;
+}
+
 static struct irq_chip uv_irq_chip = {
        .name                   = "UV-CORE",
        .irq_mask               = uv_noop,
@@ -45,189 +77,99 @@ static struct irq_chip uv_irq_chip = {
        .irq_set_affinity       = uv_set_irq_affinity,
 };
 
-/*
- * Add offset and pnode information of the hub sourcing interrupts to the
- * rb tree for a specific irq.
- */
-static int uv_set_irq_2_mmr_info(int irq, unsigned long offset, unsigned blade)
+static int uv_domain_alloc(struct irq_domain *domain, unsigned int virq,
+                          unsigned int nr_irqs, void *arg)
 {
-       struct rb_node **link = &uv_irq_root.rb_node;
-       struct rb_node *parent = NULL;
-       struct uv_irq_2_mmr_pnode *n;
-       struct uv_irq_2_mmr_pnode *e;
-       unsigned long irqflags;
-
-       n = kmalloc_node(sizeof(struct uv_irq_2_mmr_pnode), GFP_KERNEL,
-                               uv_blade_to_memory_nid(blade));
-       if (!n)
+       struct uv_irq_2_mmr_pnode *chip_data;
+       struct irq_alloc_info *info = arg;
+       struct irq_data *irq_data = irq_domain_get_irq_data(domain, virq);
+       int ret;
+
+       if (nr_irqs > 1 || !info || info->type != X86_IRQ_ALLOC_TYPE_UV)
+               return -EINVAL;
+
+       chip_data = kmalloc_node(sizeof(*chip_data), GFP_KERNEL,
+                                irq_data->node);
+       if (!chip_data)
                return -ENOMEM;
 
-       n->irq = irq;
-       n->offset = offset;
-       n->pnode = uv_blade_to_pnode(blade);
-       spin_lock_irqsave(&uv_irq_lock, irqflags);
-       /* Find the right place in the rbtree: */
-       while (*link) {
-               parent = *link;
-               e = rb_entry(parent, struct uv_irq_2_mmr_pnode, list);
-
-               if (unlikely(irq == e->irq)) {
-                       /* irq entry exists */
-                       e->pnode = uv_blade_to_pnode(blade);
-                       e->offset = offset;
-                       spin_unlock_irqrestore(&uv_irq_lock, irqflags);
-                       kfree(n);
-                       return 0;
-               }
-
-               if (irq < e->irq)
-                       link = &(*link)->rb_left;
+       ret = irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, arg);
+       if (ret >= 0) {
+               if (info->uv_limit == UV_AFFINITY_CPU)
+                       irq_set_status_flags(virq, IRQ_NO_BALANCING);
                else
-                       link = &(*link)->rb_right;
+                       irq_set_status_flags(virq, IRQ_MOVE_PCNTXT);
+
+               chip_data->pnode = uv_blade_to_pnode(info->uv_blade);
+               chip_data->offset = info->uv_offset;
+               irq_domain_set_info(domain, virq, virq, &uv_irq_chip, chip_data,
+                                   handle_percpu_irq, NULL, info->uv_name);
+       } else {
+               kfree(chip_data);
        }
 
-       /* Insert the node into the rbtree. */
-       rb_link_node(&n->list, parent, link);
-       rb_insert_color(&n->list, &uv_irq_root);
-
-       spin_unlock_irqrestore(&uv_irq_lock, irqflags);
-       return 0;
+       return ret;
 }
 
-/* Retrieve offset and pnode information from the rb tree for a specific irq */
-int uv_irq_2_mmr_info(int irq, unsigned long *offset, int *pnode)
+static void uv_domain_free(struct irq_domain *domain, unsigned int virq,
+                          unsigned int nr_irqs)
 {
-       struct uv_irq_2_mmr_pnode *e;
-       struct rb_node *n;
-       unsigned long irqflags;
-
-       spin_lock_irqsave(&uv_irq_lock, irqflags);
-       n = uv_irq_root.rb_node;
-       while (n) {
-               e = rb_entry(n, struct uv_irq_2_mmr_pnode, list);
-
-               if (e->irq == irq) {
-                       *offset = e->offset;
-                       *pnode = e->pnode;
-                       spin_unlock_irqrestore(&uv_irq_lock, irqflags);
-                       return 0;
-               }
-
-               if (irq < e->irq)
-                       n = n->rb_left;
-               else
-                       n = n->rb_right;
-       }
-       spin_unlock_irqrestore(&uv_irq_lock, irqflags);
-       return -1;
+       struct irq_data *irq_data = irq_domain_get_irq_data(domain, virq);
+
+       BUG_ON(nr_irqs != 1);
+       kfree(irq_data->chip_data);
+       irq_clear_status_flags(virq, IRQ_MOVE_PCNTXT);
+       irq_clear_status_flags(virq, IRQ_NO_BALANCING);
+       irq_domain_free_irqs_top(domain, virq, nr_irqs);
 }
 
 /*
  * Re-target the irq to the specified CPU and enable the specified MMR located
  * on the specified blade to allow the sending of MSIs to the specified CPU.
  */
-static int
-arch_enable_uv_irq(char *irq_name, unsigned int irq, int cpu, int mmr_blade,
-                      unsigned long mmr_offset, int limit)
+static void uv_domain_activate(struct irq_domain *domain,
+                              struct irq_data *irq_data)
 {
-       const struct cpumask *eligible_cpu = cpumask_of(cpu);
-       struct irq_cfg *cfg = irq_cfg(irq);
-       unsigned long mmr_value;
-       struct uv_IO_APIC_route_entry *entry;
-       int mmr_pnode, err;
-       unsigned int dest;
-
-       BUILD_BUG_ON(sizeof(struct uv_IO_APIC_route_entry) !=
-                       sizeof(unsigned long));
-
-       err = assign_irq_vector(irq, cfg, eligible_cpu);
-       if (err != 0)
-               return err;
-
-       err = apic->cpu_mask_to_apicid_and(eligible_cpu, eligible_cpu, &dest);
-       if (err != 0)
-               return err;
-
-       if (limit == UV_AFFINITY_CPU)
-               irq_set_status_flags(irq, IRQ_NO_BALANCING);
-       else
-               irq_set_status_flags(irq, IRQ_MOVE_PCNTXT);
-
-       irq_set_chip_and_handler_name(irq, &uv_irq_chip, handle_percpu_irq,
-                                     irq_name);
-
-       mmr_value = 0;
-       entry = (struct uv_IO_APIC_route_entry *)&mmr_value;
-       entry->vector           = cfg->vector;
-       entry->delivery_mode    = apic->irq_delivery_mode;
-       entry->dest_mode        = apic->irq_dest_mode;
-       entry->polarity         = 0;
-       entry->trigger          = 0;
-       entry->mask             = 0;
-       entry->dest             = dest;
-
-       mmr_pnode = uv_blade_to_pnode(mmr_blade);
-       uv_write_global_mmr64(mmr_pnode, mmr_offset, mmr_value);
-
-       if (cfg->move_in_progress)
-               send_cleanup_vector(cfg);
-
-       return irq;
+       uv_program_mmr(irqd_cfg(irq_data), irq_data->chip_data);
 }
 
 /*
  * Disable the specified MMR located on the specified blade so that MSIs are
  * longer allowed to be sent.
  */
-static void arch_disable_uv_irq(int mmr_pnode, unsigned long mmr_offset)
+static void uv_domain_deactivate(struct irq_domain *domain,
+                                struct irq_data *irq_data)
 {
        unsigned long mmr_value;
        struct uv_IO_APIC_route_entry *entry;
 
-       BUILD_BUG_ON(sizeof(struct uv_IO_APIC_route_entry) !=
-                       sizeof(unsigned long));
-
        mmr_value = 0;
        entry = (struct uv_IO_APIC_route_entry *)&mmr_value;
        entry->mask = 1;
-
-       uv_write_global_mmr64(mmr_pnode, mmr_offset, mmr_value);
+       uv_program_mmr(irqd_cfg(irq_data), irq_data->chip_data);
 }
 
-static int
-uv_set_irq_affinity(struct irq_data *data, const struct cpumask *mask,
-                   bool force)
-{
-       struct irq_cfg *cfg = irqd_cfg(data);
-       unsigned int dest;
-       unsigned long mmr_value, mmr_offset;
-       struct uv_IO_APIC_route_entry *entry;
-       int mmr_pnode;
-
-       if (apic_set_affinity(data, mask, &dest))
-               return -1;
-
-       mmr_value = 0;
-       entry = (struct uv_IO_APIC_route_entry *)&mmr_value;
-
-       entry->vector           = cfg->vector;
-       entry->delivery_mode    = apic->irq_delivery_mode;
-       entry->dest_mode        = apic->irq_dest_mode;
-       entry->polarity         = 0;
-       entry->trigger          = 0;
-       entry->mask             = 0;
-       entry->dest             = dest;
-
-       /* Get previously stored MMR and pnode of hub sourcing interrupts */
-       if (uv_irq_2_mmr_info(data->irq, &mmr_offset, &mmr_pnode))
-               return -1;
-
-       uv_write_global_mmr64(mmr_pnode, mmr_offset, mmr_value);
+static const struct irq_domain_ops uv_domain_ops = {
+       .alloc          = uv_domain_alloc,
+       .free           = uv_domain_free,
+       .activate       = uv_domain_activate,
+       .deactivate     = uv_domain_deactivate,
+};
 
-       if (cfg->move_in_progress)
-               send_cleanup_vector(cfg);
+static struct irq_domain *uv_get_irq_domain(void)
+{
+       static struct irq_domain *uv_domain;
+       static DEFINE_MUTEX(uv_lock);
+
+       mutex_lock(&uv_lock);
+       if (uv_domain == NULL) {
+               uv_domain = irq_domain_add_tree(NULL, &uv_domain_ops, NULL);
+               if (uv_domain)
+                       uv_domain->parent = x86_vector_domain;
+       }
+       mutex_unlock(&uv_lock);
 
-       return IRQ_SET_MASK_OK_NOCOPY;
+       return uv_domain;
 }
 
 /*
@@ -238,19 +180,21 @@ uv_set_irq_affinity(struct irq_data *data, const struct cpumask *mask,
 int uv_setup_irq(char *irq_name, int cpu, int mmr_blade,
                 unsigned long mmr_offset, int limit)
 {
-       int ret, irq = irq_alloc_hwirq(uv_blade_to_memory_nid(mmr_blade));
+       struct irq_alloc_info info;
+       struct irq_domain *domain = uv_get_irq_domain();
 
-       if (!irq)
-               return -EBUSY;
+       if (!domain)
+               return -ENOMEM;
 
-       ret = arch_enable_uv_irq(irq_name, irq, cpu, mmr_blade, mmr_offset,
-               limit);
-       if (ret == irq)
-               uv_set_irq_2_mmr_info(irq, mmr_offset, mmr_blade);
-       else
-               irq_free_hwirq(irq);
+       init_irq_alloc_info(&info, cpumask_of(cpu));
+       info.type = X86_IRQ_ALLOC_TYPE_UV;
+       info.uv_limit = limit;
+       info.uv_blade = mmr_blade;
+       info.uv_offset = mmr_offset;
+       info.uv_name = irq_name;
 
-       return ret;
+       return irq_domain_alloc_irqs(domain, 1,
+                                    uv_blade_to_memory_nid(mmr_blade), &info);
 }
 EXPORT_SYMBOL_GPL(uv_setup_irq);
 
@@ -263,26 +207,6 @@ EXPORT_SYMBOL_GPL(uv_setup_irq);
  */
 void uv_teardown_irq(unsigned int irq)
 {
-       struct uv_irq_2_mmr_pnode *e;
-       struct rb_node *n;
-       unsigned long irqflags;
-
-       spin_lock_irqsave(&uv_irq_lock, irqflags);
-       n = uv_irq_root.rb_node;
-       while (n) {
-               e = rb_entry(n, struct uv_irq_2_mmr_pnode, list);
-               if (e->irq == irq) {
-                       arch_disable_uv_irq(e->pnode, e->offset);
-                       rb_erase(n, &uv_irq_root);
-                       kfree(e);
-                       break;
-               }
-               if (irq < e->irq)
-                       n = n->rb_left;
-               else
-                       n = n->rb_right;
-       }
-       spin_unlock_irqrestore(&uv_irq_lock, irqflags);
-       irq_free_hwirq(irq);
+       irq_domain_free_irqs(irq, 1);
 }
 EXPORT_SYMBOL_GPL(uv_teardown_irq);
index 757678fb26e1a06277687c1c90f86e75377de03a..0d7dd1f5ac36fa6814c18522dd28561566c570eb 100644 (file)
 #include <asm/mtrr.h>
 #include <asm/page.h>
 #include <asm/mce.h>
-#include <asm/xcr.h>
 #include <asm/suspend.h>
+#include <asm/fpu/internal.h>
 #include <asm/debugreg.h>
-#include <asm/fpu-internal.h> /* pcntxt_mask */
 #include <asm/cpu.h>
 
 #ifdef CONFIG_X86_32
@@ -155,6 +154,8 @@ static void fix_processor_context(void)
 #endif
        load_TR_desc();                         /* This does ltr */
        load_LDT(&current->active_mm->context); /* This does lldt */
+
+       fpu__resume_cpu();
 }
 
 /**
@@ -221,12 +222,6 @@ static void notrace __restore_processor_state(struct saved_context *ctxt)
        wrmsrl(MSR_KERNEL_GS_BASE, ctxt->gs_kernel_base);
 #endif
 
-       /*
-        * restore XCR0 for xsave capable cpu's.
-        */
-       if (cpu_has_xsave)
-               xsetbv(XCR_XFEATURE_ENABLED_MASK, pcntxt_mask);
-
        fix_processor_context();
 
        do_fpu_end();
index 3c4469a7a929c7fa3488707a25829cdc6fcc6411..e2386cb4e0c315f32fae03afd13045c4047eb902 100644 (file)
@@ -78,9 +78,9 @@ ENTRY(restore_image)
 
        /* code below has been relocated to a safe page */
 ENTRY(core_restore_code)
-loop:
+.Lloop:
        testq   %rdx, %rdx
-       jz      done
+       jz      .Ldone
 
        /* get addresses from the pbe and copy the page */
        movq    pbe_address(%rdx), %rsi
@@ -91,8 +91,8 @@ loop:
 
        /* progress to the next pbe */
        movq    pbe_next(%rdx), %rdx
-       jmp     loop
-done:
+       jmp     .Lloop
+.Ldone:
        /* jump to the restore_registers address from the image header */
        jmpq    *%rax
        /*
index acb384d246694e974d5d722edd96856e2774fc1b..a8fecc226946dc1fb65960f84b26f53fde82f57e 100644 (file)
@@ -26,7 +26,7 @@ else
 
 obj-y += syscalls_64.o vdso/
 
-subarch-y = ../lib/csum-partial_64.o ../lib/memcpy_64.o ../lib/thunk_64.o \
+subarch-y = ../lib/csum-partial_64.o ../lib/memcpy_64.o ../entry/thunk_64.o \
                ../lib/rwsem.o
 
 endif
index 7e8a1a6504356159bd1b604b53de4ed194ddd19c..b9531d34313491b5180878f64ca18f49fb69c94f 100644 (file)
@@ -39,7 +39,8 @@
 #define smp_mb()       barrier()
 #define smp_rmb()      barrier()
 #define smp_wmb()      barrier()
-#define set_mb(var, value) do { var = value; barrier(); } while (0)
+
+#define smp_store_mb(var, value) do { WRITE_ONCE(var, value); barrier(); } while (0)
 
 #define read_barrier_depends()         do { } while (0)
 #define smp_read_barrier_depends()     do { } while (0)
index 46957ead3060eecb5e76b6f6daf3b498a6b6a5e9..0b95c9b8283fe2afe885d9a8ae98393c14ecc498 100644 (file)
@@ -1181,10 +1181,11 @@ static const struct pv_cpu_ops xen_cpu_ops __initconst = {
        .read_tscp = native_read_tscp,
 
        .iret = xen_iret,
-       .irq_enable_sysexit = xen_sysexit,
 #ifdef CONFIG_X86_64
        .usergs_sysret32 = xen_sysret32,
        .usergs_sysret64 = xen_sysret64,
+#else
+       .irq_enable_sysexit = xen_sysexit,
 #endif
 
        .load_tr_desc = paravirt_nop,
@@ -1423,7 +1424,7 @@ static void xen_pvh_set_cr_flags(int cpu)
                return;
        /*
         * For BSP, PSE PGE are set in probe_page_size_mask(), for APs
-        * set them here. For all, OSFXSR OSXMMEXCPT are set in fpu_init.
+        * set them here. For all, OSFXSR OSXMMEXCPT are set in fpu__init_cpu().
        */
        if (cpu_has_pse)
                cr4_set_bits_and_update_boot(X86_CR4_PSE);
@@ -1467,6 +1468,7 @@ asmlinkage __visible void __init xen_start_kernel(void)
 {
        struct physdev_set_iopl set_iopl;
        unsigned long initrd_start = 0;
+       u64 pat;
        int rc;
 
        if (!xen_start_info)
@@ -1574,8 +1576,8 @@ asmlinkage __visible void __init xen_start_kernel(void)
         * Modify the cache mode translation tables to match Xen's PAT
         * configuration.
         */
-
-       pat_init_cache_modes();
+       rdmsrl(MSR_IA32_CR_PAT, pat);
+       pat_init_cache_modes(pat);
 
        /* keep using Xen gdt for now; no urgent need to change it */
 
index b47124d4cd67e29199fae1c48f9a94ecd93b00f7..8b7f18e200aa4a453d8ae60d02b85b4141abc677 100644 (file)
@@ -67,6 +67,7 @@
 #include <linux/seq_file.h>
 #include <linux/bootmem.h>
 #include <linux/slab.h>
+#include <linux/vmalloc.h>
 
 #include <asm/cache.h>
 #include <asm/setup.h>
index 956374c1edbc31e4c1eb50c3fb29cb8828ad44b5..9e2ba5c6e1dd7be4a0b10a70b315cf5f0f20c081 100644 (file)
 #include "xen-ops.h"
 #include "debugfs.h"
 
+static DEFINE_PER_CPU(int, lock_kicker_irq) = -1;
+static DEFINE_PER_CPU(char *, irq_name);
+static bool xen_pvspin = true;
+
+#ifdef CONFIG_QUEUED_SPINLOCKS
+
+#include <asm/qspinlock.h>
+
+static void xen_qlock_kick(int cpu)
+{
+       xen_send_IPI_one(cpu, XEN_SPIN_UNLOCK_VECTOR);
+}
+
+/*
+ * Halt the current CPU & release it back to the host
+ */
+static void xen_qlock_wait(u8 *byte, u8 val)
+{
+       int irq = __this_cpu_read(lock_kicker_irq);
+
+       /* If kicker interrupts not initialized yet, just spin */
+       if (irq == -1)
+               return;
+
+       /* clear pending */
+       xen_clear_irq_pending(irq);
+       barrier();
+
+       /*
+        * We check the byte value after clearing pending IRQ to make sure
+        * that we won't miss a wakeup event because of the clearing.
+        *
+        * The sync_clear_bit() call in xen_clear_irq_pending() is atomic.
+        * So it is effectively a memory barrier for x86.
+        */
+       if (READ_ONCE(*byte) != val)
+               return;
+
+       /*
+        * If an interrupt happens here, it will leave the wakeup irq
+        * pending, which will cause xen_poll_irq() to return
+        * immediately.
+        */
+
+       /* Block until irq becomes pending (or perhaps a spurious wakeup) */
+       xen_poll_irq(irq);
+}
+
+#else /* CONFIG_QUEUED_SPINLOCKS */
+
 enum xen_contention_stat {
        TAKEN_SLOW,
        TAKEN_SLOW_PICKUP,
@@ -100,12 +150,9 @@ struct xen_lock_waiting {
        __ticket_t want;
 };
 
-static DEFINE_PER_CPU(int, lock_kicker_irq) = -1;
-static DEFINE_PER_CPU(char *, irq_name);
 static DEFINE_PER_CPU(struct xen_lock_waiting, lock_waiting);
 static cpumask_t waiting_cpus;
 
-static bool xen_pvspin = true;
 __visible void xen_lock_spinning(struct arch_spinlock *lock, __ticket_t want)
 {
        int irq = __this_cpu_read(lock_kicker_irq);
@@ -217,6 +264,7 @@ static void xen_unlock_kick(struct arch_spinlock *lock, __ticket_t next)
                }
        }
 }
+#endif /* CONFIG_QUEUED_SPINLOCKS */
 
 static irqreturn_t dummy_handler(int irq, void *dev_id)
 {
@@ -280,8 +328,16 @@ void __init xen_init_spinlocks(void)
                return;
        }
        printk(KERN_DEBUG "xen: PV spinlocks enabled\n");
+#ifdef CONFIG_QUEUED_SPINLOCKS
+       __pv_init_lock_hash();
+       pv_lock_ops.queued_spin_lock_slowpath = __pv_queued_spin_lock_slowpath;
+       pv_lock_ops.queued_spin_unlock = PV_CALLEE_SAVE(__pv_queued_spin_unlock);
+       pv_lock_ops.wait = xen_qlock_wait;
+       pv_lock_ops.kick = xen_qlock_kick;
+#else
        pv_lock_ops.lock_spinning = PV_CALLEE_SAVE(xen_lock_spinning);
        pv_lock_ops.unlock_kick = xen_unlock_kick;
+#endif
 }
 
 /*
@@ -310,7 +366,7 @@ static __init int xen_parse_nopvspin(char *arg)
 }
 early_param("xen_nopvspin", xen_parse_nopvspin);
 
-#ifdef CONFIG_XEN_DEBUG_FS
+#if defined(CONFIG_XEN_DEBUG_FS) && !defined(CONFIG_QUEUED_SPINLOCKS)
 
 static struct dentry *d_spin_debug;
 
index 985fc3ee0973c85f916c67cd9a40fc9c2c73d340..f22667abf7b9d54d475edd08ffcdac8ba79b4cf3 100644 (file)
@@ -15,6 +15,8 @@
 #include <asm/percpu.h>
 #include <asm/processor-flags.h>
 #include <asm/segment.h>
+#include <asm/asm-offsets.h>
+#include <asm/thread_info.h>
 
 #include <xen/interface/xen.h>
 
@@ -47,29 +49,13 @@ ENTRY(xen_iret)
 ENDPATCH(xen_iret)
 RELOC(xen_iret, 1b+1)
 
-/*
- * sysexit is not used for 64-bit processes, so it's only ever used to
- * return to 32-bit compat userspace.
- */
-ENTRY(xen_sysexit)
-       pushq $__USER32_DS
-       pushq %rcx
-       pushq $X86_EFLAGS_IF
-       pushq $__USER32_CS
-       pushq %rdx
-
-       pushq $0
-1:     jmp hypercall_iret
-ENDPATCH(xen_sysexit)
-RELOC(xen_sysexit, 1b+1)
-
 ENTRY(xen_sysret64)
        /*
         * We're already on the usermode stack at this point, but
         * still with the kernel gs, so we can easily switch back
         */
        movq %rsp, PER_CPU_VAR(rsp_scratch)
-       movq PER_CPU_VAR(kernel_stack), %rsp
+       movq PER_CPU_VAR(cpu_current_top_of_stack), %rsp
 
        pushq $__USER_DS
        pushq PER_CPU_VAR(rsp_scratch)
@@ -88,7 +74,7 @@ ENTRY(xen_sysret32)
         * still with the kernel gs, so we can easily switch back
         */
        movq %rsp, PER_CPU_VAR(rsp_scratch)
-       movq PER_CPU_VAR(kernel_stack), %rsp
+       movq PER_CPU_VAR(cpu_current_top_of_stack), %rsp
 
        pushq $__USER32_DS
        pushq PER_CPU_VAR(rsp_scratch)
@@ -128,7 +114,7 @@ RELOC(xen_sysret32, 1b+1)
 /* Normal 64-bit system call target */
 ENTRY(xen_syscall_target)
        undo_xen_syscall
-       jmp system_call_after_swapgs
+       jmp entry_SYSCALL_64_after_swapgs
 ENDPROC(xen_syscall_target)
 
 #ifdef CONFIG_IA32_EMULATION
@@ -136,13 +122,13 @@ ENDPROC(xen_syscall_target)
 /* 32-bit compat syscall target */
 ENTRY(xen_syscall32_target)
        undo_xen_syscall
-       jmp ia32_cstar_target
+       jmp entry_SYSCALL_compat
 ENDPROC(xen_syscall32_target)
 
 /* 32-bit compat sysenter target */
 ENTRY(xen_sysenter_target)
        undo_xen_syscall
-       jmp ia32_sysenter_target
+       jmp entry_SYSENTER_compat
 ENDPROC(xen_sysenter_target)
 
 #else /* !CONFIG_IA32_EMULATION */
index 9e195c683549dc138d77e98687c34f62b40eda76..c20fe29e65f48b4706789e0ad59bb33b1a3acc18 100644 (file)
@@ -134,7 +134,9 @@ DECL_ASM(void, xen_restore_fl_direct, unsigned long);
 
 /* These are not functions, and cannot be called normally */
 __visible void xen_iret(void);
+#ifdef CONFIG_X86_32
 __visible void xen_sysexit(void);
+#endif
 __visible void xen_sysret32(void);
 __visible void xen_sysret64(void);
 __visible void xen_adjust_exception_frame(void);
index 87be10e8b57aa00830172c33fc4eb8acacb65233..e5b872ba24843ee84ee65346dd87ab79abb6141b 100644 (file)
@@ -151,7 +151,7 @@ config HAVE_SMP
          the CPU core definition and currently needs to be selected manually.
 
          Multiprocessor support in implemented with external cache and
-         interrupt controlers.
+         interrupt controllers.
 
          The MX interrupt distributer adds Interprocessor Interrupts
          and causes the IRQ numbers to be increased by 4 for devices
index fe1600a094384e8c18a988e91862b531031984f3..c39bb6e61911e3577274bc4794a08084d4035aaa 100644 (file)
@@ -59,6 +59,7 @@ static inline void __iomem *ioremap_cache(unsigned long offset,
 }
 
 #define ioremap_wc ioremap_nocache
+#define ioremap_wt ioremap_nocache
 
 static inline void __iomem *ioremap(unsigned long offset, unsigned long size)
 {
index e68b71b85a7eaf0e3097debe8bf4dc4078e7a038..594eea04266e6d05f7256255552a1c4c72c664f3 100644 (file)
@@ -1600,6 +1600,7 @@ static int blk_mq_hctx_notify(void *data, unsigned long action,
        return NOTIFY_OK;
 }
 
+/* hctx->ctxs will be freed in queue's release handler */
 static void blk_mq_exit_hctx(struct request_queue *q,
                struct blk_mq_tag_set *set,
                struct blk_mq_hw_ctx *hctx, unsigned int hctx_idx)
@@ -1618,7 +1619,6 @@ static void blk_mq_exit_hctx(struct request_queue *q,
 
        blk_mq_unregister_cpu_notifier(&hctx->cpu_notifier);
        blk_free_flush_queue(hctx->fq);
-       kfree(hctx->ctxs);
        blk_mq_free_bitmap(&hctx->ctx_map);
 }
 
@@ -1891,8 +1891,12 @@ void blk_mq_release(struct request_queue *q)
        unsigned int i;
 
        /* hctx kobj stays in hctx */
-       queue_for_each_hw_ctx(q, hctx, i)
+       queue_for_each_hw_ctx(q, hctx, i) {
+               if (!hctx)
+                       continue;
+               kfree(hctx->ctxs);
                kfree(hctx);
+       }
 
        kfree(q->queue_hw_ctx);
 
index 0a536dc05f3b559d6d04c1e819d65290f96f7c35..ea982eadaf6380b974d6b1d39a7197085217ac91 100644 (file)
@@ -422,9 +422,9 @@ int blk_alloc_devt(struct hd_struct *part, dev_t *devt)
        /* allocate ext devt */
        idr_preload(GFP_KERNEL);
 
-       spin_lock(&ext_devt_lock);
+       spin_lock_bh(&ext_devt_lock);
        idx = idr_alloc(&ext_devt_idr, part, 0, NR_EXT_DEVT, GFP_NOWAIT);
-       spin_unlock(&ext_devt_lock);
+       spin_unlock_bh(&ext_devt_lock);
 
        idr_preload_end();
        if (idx < 0)
@@ -449,9 +449,9 @@ void blk_free_devt(dev_t devt)
                return;
 
        if (MAJOR(devt) == BLOCK_EXT_MAJOR) {
-               spin_lock(&ext_devt_lock);
+               spin_lock_bh(&ext_devt_lock);
                idr_remove(&ext_devt_idr, blk_mangle_minor(MINOR(devt)));
-               spin_unlock(&ext_devt_lock);
+               spin_unlock_bh(&ext_devt_lock);
        }
 }
 
@@ -653,7 +653,6 @@ void del_gendisk(struct gendisk *disk)
        disk->flags &= ~GENHD_FL_UP;
 
        sysfs_remove_link(&disk_to_dev(disk)->kobj, "bdi");
-       bdi_unregister(&disk->queue->backing_dev_info);
        blk_unregister_queue(disk);
        blk_unregister_region(disk_devt(disk), disk->minors);
 
@@ -691,13 +690,13 @@ struct gendisk *get_gendisk(dev_t devt, int *partno)
        } else {
                struct hd_struct *part;
 
-               spin_lock(&ext_devt_lock);
+               spin_lock_bh(&ext_devt_lock);
                part = idr_find(&ext_devt_idr, blk_mangle_minor(MINOR(devt)));
                if (part && get_disk(part_to_disk(part))) {
                        *partno = part->partno;
                        disk = part_to_disk(part);
                }
-               spin_unlock(&ext_devt_lock);
+               spin_unlock_bh(&ext_devt_lock);
        }
 
        return disk;
index b48f4f108c474104d484ceb316099ef78b20f5d3..98e387efb8c88fded52a9fb85027d949175052d4 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Cryptographic API for the 842 compression algorithm.
+ * Cryptographic API for the 842 software compression algorithm.
  *
  * 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
  * 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, 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ * Copyright (C) IBM Corporation, 2011-2015
  *
- * Copyright (C) IBM Corporation, 2011
+ * Original Authors: Robert Jennings <rcj@linux.vnet.ibm.com>
+ *                   Seth Jennings <sjenning@linux.vnet.ibm.com>
  *
- * Authors: Robert Jennings <rcj@linux.vnet.ibm.com>
- *          Seth Jennings <sjenning@linux.vnet.ibm.com>
+ * Rewrite: Dan Streetman <ddstreet@ieee.org>
+ *
+ * This is the software implementation of compression and decompression using
+ * the 842 format.  This uses the software 842 library at lib/842/ which is
+ * only a reference implementation, and is very, very slow as compared to other
+ * software compressors.  You probably do not want to use this software
+ * compression.  If you have access to the PowerPC 842 compression hardware, you
+ * want to use the 842 hardware compression interface, which is at:
+ * drivers/crypto/nx/nx-842-crypto.c
  */
 
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/crypto.h>
-#include <linux/vmalloc.h>
-#include <linux/nx842.h>
-#include <linux/lzo.h>
-#include <linux/timer.h>
-
-static int nx842_uselzo;
-
-struct nx842_ctx {
-       void *nx842_wmem; /* working memory for 842/lzo */
-};
+#include <linux/sw842.h>
 
-enum nx842_crypto_type {
-       NX842_CRYPTO_TYPE_842,
-       NX842_CRYPTO_TYPE_LZO
+struct crypto842_ctx {
+       char wmem[SW842_MEM_COMPRESS];  /* working memory for compress */
 };
 
-#define NX842_SENTINEL 0xdeadbeef
-
-struct nx842_crypto_header {
-       unsigned int sentinel; /* debug */
-       enum nx842_crypto_type type;
-};
-
-static int nx842_init(struct crypto_tfm *tfm)
-{
-       struct nx842_ctx *ctx = crypto_tfm_ctx(tfm);
-       int wmemsize;
-
-       wmemsize = max_t(int, nx842_get_workmem_size(), LZO1X_MEM_COMPRESS);
-       ctx->nx842_wmem = kmalloc(wmemsize, GFP_NOFS);
-       if (!ctx->nx842_wmem)
-               return -ENOMEM;
-
-       return 0;
-}
-
-static void nx842_exit(struct crypto_tfm *tfm)
-{
-       struct nx842_ctx *ctx = crypto_tfm_ctx(tfm);
-
-       kfree(ctx->nx842_wmem);
-}
-
-static void nx842_reset_uselzo(unsigned long data)
+static int crypto842_compress(struct crypto_tfm *tfm,
+                             const u8 *src, unsigned int slen,
+                             u8 *dst, unsigned int *dlen)
 {
-       nx842_uselzo = 0;
-}
-
-static DEFINE_TIMER(failover_timer, nx842_reset_uselzo, 0, 0);
-
-static int nx842_crypto_compress(struct crypto_tfm *tfm, const u8 *src,
-                           unsigned int slen, u8 *dst, unsigned int *dlen)
-{
-       struct nx842_ctx *ctx = crypto_tfm_ctx(tfm);
-       struct nx842_crypto_header *hdr;
-       unsigned int tmp_len = *dlen;
-       size_t lzodlen; /* needed for lzo */
-       int err;
-
-       *dlen = 0;
-       hdr = (struct nx842_crypto_header *)dst;
-       hdr->sentinel = NX842_SENTINEL; /* debug */
-       dst += sizeof(struct nx842_crypto_header);
-       tmp_len -= sizeof(struct nx842_crypto_header);
-       lzodlen = tmp_len;
-
-       if (likely(!nx842_uselzo)) {
-               err = nx842_compress(src, slen, dst, &tmp_len, ctx->nx842_wmem);
-
-               if (likely(!err)) {
-                       hdr->type = NX842_CRYPTO_TYPE_842;
-                       *dlen = tmp_len + sizeof(struct nx842_crypto_header);
-                       return 0;
-               }
-
-               /* hardware failed */
-               nx842_uselzo = 1;
+       struct crypto842_ctx *ctx = crypto_tfm_ctx(tfm);
 
-               /* set timer to check for hardware again in 1 second */
-               mod_timer(&failover_timer, jiffies + msecs_to_jiffies(1000));
-       }
-
-       /* no hardware, use lzo */
-       err = lzo1x_1_compress(src, slen, dst, &lzodlen, ctx->nx842_wmem);
-       if (err != LZO_E_OK)
-               return -EINVAL;
-
-       hdr->type = NX842_CRYPTO_TYPE_LZO;
-       *dlen = lzodlen + sizeof(struct nx842_crypto_header);
-       return 0;
+       return sw842_compress(src, slen, dst, dlen, ctx->wmem);
 }
 
-static int nx842_crypto_decompress(struct crypto_tfm *tfm, const u8 *src,
-                             unsigned int slen, u8 *dst, unsigned int *dlen)
+static int crypto842_decompress(struct crypto_tfm *tfm,
+                               const u8 *src, unsigned int slen,
+                               u8 *dst, unsigned int *dlen)
 {
-       struct nx842_ctx *ctx = crypto_tfm_ctx(tfm);
-       struct nx842_crypto_header *hdr;
-       unsigned int tmp_len = *dlen;
-       size_t lzodlen; /* needed for lzo */
-       int err;
-
-       *dlen = 0;
-       hdr = (struct nx842_crypto_header *)src;
-
-       if (unlikely(hdr->sentinel != NX842_SENTINEL))
-               return -EINVAL;
-
-       src += sizeof(struct nx842_crypto_header);
-       slen -= sizeof(struct nx842_crypto_header);
-
-       if (likely(hdr->type == NX842_CRYPTO_TYPE_842)) {
-               err = nx842_decompress(src, slen, dst, &tmp_len,
-                       ctx->nx842_wmem);
-               if (err)
-                       return -EINVAL;
-               *dlen = tmp_len;
-       } else if (hdr->type == NX842_CRYPTO_TYPE_LZO) {
-               lzodlen = tmp_len;
-               err = lzo1x_decompress_safe(src, slen, dst, &lzodlen);
-               if (err != LZO_E_OK)
-                       return -EINVAL;
-               *dlen = lzodlen;
-       } else
-               return -EINVAL;
-
-       return 0;
+       return sw842_decompress(src, slen, dst, dlen);
 }
 
 static struct crypto_alg alg = {
        .cra_name               = "842",
+       .cra_driver_name        = "842-generic",
+       .cra_priority           = 100,
        .cra_flags              = CRYPTO_ALG_TYPE_COMPRESS,
-       .cra_ctxsize            = sizeof(struct nx842_ctx),
+       .cra_ctxsize            = sizeof(struct crypto842_ctx),
        .cra_module             = THIS_MODULE,
-       .cra_init               = nx842_init,
-       .cra_exit               = nx842_exit,
        .cra_u                  = { .compress = {
-       .coa_compress           = nx842_crypto_compress,
-       .coa_decompress         = nx842_crypto_decompress } }
+       .coa_compress           = crypto842_compress,
+       .coa_decompress         = crypto842_decompress } }
 };
 
-static int __init nx842_mod_init(void)
+static int __init crypto842_mod_init(void)
 {
-       del_timer(&failover_timer);
        return crypto_register_alg(&alg);
 }
+module_init(crypto842_mod_init);
 
-static void __exit nx842_mod_exit(void)
+static void __exit crypto842_mod_exit(void)
 {
        crypto_unregister_alg(&alg);
 }
-
-module_init(nx842_mod_init);
-module_exit(nx842_mod_exit);
+module_exit(crypto842_mod_exit);
 
 MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("842 Compression Algorithm");
+MODULE_DESCRIPTION("842 Software Compression Algorithm");
 MODULE_ALIAS_CRYPTO("842");
+MODULE_ALIAS_CRYPTO("842-generic");
+MODULE_AUTHOR("Dan Streetman <ddstreet@ieee.org>");
index 362905e7c841ff55b204891e9e93748f2cfea96e..b4cfc5754033b9ddfa17d8ad395baf0e751891bd 100644 (file)
@@ -78,6 +78,10 @@ config CRYPTO_RNG2
        tristate
        select CRYPTO_ALGAPI2
 
+config CRYPTO_RNG_DEFAULT
+       tristate
+       select CRYPTO_DRBG_MENU
+
 config CRYPTO_PCOMP
        tristate
        select CRYPTO_PCOMP2
@@ -87,6 +91,23 @@ config CRYPTO_PCOMP2
        tristate
        select CRYPTO_ALGAPI2
 
+config CRYPTO_AKCIPHER2
+       tristate
+       select CRYPTO_ALGAPI2
+
+config CRYPTO_AKCIPHER
+       tristate
+       select CRYPTO_AKCIPHER2
+       select CRYPTO_ALGAPI
+
+config CRYPTO_RSA
+       tristate "RSA algorithm"
+       select CRYPTO_AKCIPHER
+       select MPILIB
+       select ASN1
+       help
+         Generic implementation of the RSA public key algorithm.
+
 config CRYPTO_MANAGER
        tristate "Cryptographic algorithm manager"
        select CRYPTO_MANAGER2
@@ -100,6 +121,7 @@ config CRYPTO_MANAGER2
        select CRYPTO_HASH2
        select CRYPTO_BLKCIPHER2
        select CRYPTO_PCOMP2
+       select CRYPTO_AKCIPHER2
 
 config CRYPTO_USER
        tristate "Userspace cryptographic algorithm configuration"
@@ -217,15 +239,39 @@ config CRYPTO_GCM
          Support for Galois/Counter Mode (GCM) and Galois Message
          Authentication Code (GMAC). Required for IPSec.
 
+config CRYPTO_CHACHA20POLY1305
+       tristate "ChaCha20-Poly1305 AEAD support"
+       select CRYPTO_CHACHA20
+       select CRYPTO_POLY1305
+       select CRYPTO_AEAD
+       help
+         ChaCha20-Poly1305 AEAD support, RFC7539.
+
+         Support for the AEAD wrapper using the ChaCha20 stream cipher combined
+         with the Poly1305 authenticator. It is defined in RFC7539 for use in
+         IETF protocols.
+
 config CRYPTO_SEQIV
        tristate "Sequence Number IV Generator"
        select CRYPTO_AEAD
        select CRYPTO_BLKCIPHER
-       select CRYPTO_RNG
+       select CRYPTO_NULL
+       select CRYPTO_RNG_DEFAULT
        help
          This IV generator generates an IV based on a sequence number by
          xoring it with a salt.  This algorithm is mainly useful for CTR
 
+config CRYPTO_ECHAINIV
+       tristate "Encrypted Chain IV Generator"
+       select CRYPTO_AEAD
+       select CRYPTO_NULL
+       select CRYPTO_RNG_DEFAULT
+       default m
+       help
+         This IV generator generates an IV based on the encryption of
+         a sequence number xored with a salt.  This is the default
+         algorithm for CBC.
+
 comment "Block modes"
 
 config CRYPTO_CBC
@@ -415,6 +461,15 @@ config CRYPTO_GHASH
        help
          GHASH is message digest algorithm for GCM (Galois/Counter Mode).
 
+config CRYPTO_POLY1305
+       tristate "Poly1305 authenticator algorithm"
+       help
+         Poly1305 authenticator algorithm, RFC7539.
+
+         Poly1305 is an authenticator algorithm designed by Daniel J. Bernstein.
+         It is used for the ChaCha20-Poly1305 AEAD, specified in RFC7539 for use
+         in IETF protocols. This is the portable C implementation of Poly1305.
+
 config CRYPTO_MD4
        tristate "MD4 digest algorithm"
        select CRYPTO_HASH
@@ -1145,6 +1200,19 @@ config CRYPTO_SALSA20_X86_64
          The Salsa20 stream cipher algorithm is designed by Daniel J.
          Bernstein <djb@cr.yp.to>. See <http://cr.yp.to/snuffle.html>
 
+config CRYPTO_CHACHA20
+       tristate "ChaCha20 cipher algorithm"
+       select CRYPTO_BLKCIPHER
+       help
+         ChaCha20 cipher algorithm, RFC7539.
+
+         ChaCha20 is a 256-bit high-speed stream cipher designed by Daniel J.
+         Bernstein and further specified in RFC7539 for use in IETF protocols.
+         This is the portable C implementation of ChaCha20.
+
+         See also:
+         <http://cr.yp.to/chacha/chacha-20080128.pdf>
+
 config CRYPTO_SEED
        tristate "SEED cipher algorithm"
        select CRYPTO_ALGAPI
@@ -1412,10 +1480,9 @@ config CRYPTO_LZO
 
 config CRYPTO_842
        tristate "842 compression algorithm"
-       depends on CRYPTO_DEV_NX_COMPRESS
-       # 842 uses lzo if the hardware becomes unavailable
-       select LZO_COMPRESS
-       select LZO_DECOMPRESS
+       select CRYPTO_ALGAPI
+       select 842_COMPRESS
+       select 842_DECOMPRESS
        help
          This is the 842 algorithm.
 
@@ -1439,7 +1506,6 @@ comment "Random Number Generation"
 
 config CRYPTO_ANSI_CPRNG
        tristate "Pseudo Random Number Generation for Cryptographic modules"
-       default m
        select CRYPTO_AES
        select CRYPTO_RNG
        help
@@ -1457,15 +1523,14 @@ menuconfig CRYPTO_DRBG_MENU
 if CRYPTO_DRBG_MENU
 
 config CRYPTO_DRBG_HMAC
-       bool "Enable HMAC DRBG"
+       bool
        default y
        select CRYPTO_HMAC
-       help
-         Enable the HMAC DRBG variant as defined in NIST SP800-90A.
+       select CRYPTO_SHA256
 
 config CRYPTO_DRBG_HASH
        bool "Enable Hash DRBG"
-       select CRYPTO_HASH
+       select CRYPTO_SHA256
        help
          Enable the Hash DRBG variant as defined in NIST SP800-90A.
 
@@ -1477,11 +1542,21 @@ config CRYPTO_DRBG_CTR
 
 config CRYPTO_DRBG
        tristate
-       default CRYPTO_DRBG_MENU if (CRYPTO_DRBG_HMAC || CRYPTO_DRBG_HASH || CRYPTO_DRBG_CTR)
+       default CRYPTO_DRBG_MENU
        select CRYPTO_RNG
+       select CRYPTO_JITTERENTROPY
 
 endif  # if CRYPTO_DRBG_MENU
 
+config CRYPTO_JITTERENTROPY
+       tristate "Jitterentropy Non-Deterministic Random Number Generator"
+       help
+         The Jitterentropy RNG is a noise that is intended
+         to provide seed to another RNG. The RNG does not
+         perform any cryptographic whitening of the generated
+         random numbers. This Jitterentropy RNG registers with
+         the kernel crypto API and can be used by any caller.
+
 config CRYPTO_USER_API
        tristate
 
@@ -1512,6 +1587,15 @@ config CRYPTO_USER_API_RNG
          This option enables the user-spaces interface for random
          number generator algorithms.
 
+config CRYPTO_USER_API_AEAD
+       tristate "User-space interface for AEAD cipher algorithms"
+       depends on NET
+       select CRYPTO_AEAD
+       select CRYPTO_USER_API
+       help
+         This option enables the user-spaces interface for AEAD
+         cipher algorithms.
+
 config CRYPTO_HASH_INFO
        bool
 
index 97b7d3ac87e759908d22447784ea841abdff3e8c..0077476f50247554eae7db976d50879962dc7097 100644 (file)
@@ -21,12 +21,22 @@ obj-$(CONFIG_CRYPTO_BLKCIPHER2) += crypto_blkcipher.o
 obj-$(CONFIG_CRYPTO_BLKCIPHER2) += chainiv.o
 obj-$(CONFIG_CRYPTO_BLKCIPHER2) += eseqiv.o
 obj-$(CONFIG_CRYPTO_SEQIV) += seqiv.o
+obj-$(CONFIG_CRYPTO_ECHAINIV) += echainiv.o
 
 crypto_hash-y += ahash.o
 crypto_hash-y += shash.o
 obj-$(CONFIG_CRYPTO_HASH2) += crypto_hash.o
 
 obj-$(CONFIG_CRYPTO_PCOMP2) += pcompress.o
+obj-$(CONFIG_CRYPTO_AKCIPHER2) += akcipher.o
+
+$(obj)/rsakey-asn1.o: $(obj)/rsakey-asn1.c $(obj)/rsakey-asn1.h
+clean-files += rsakey-asn1.c rsakey-asn1.h
+
+rsa_generic-y := rsakey-asn1.o
+rsa_generic-y += rsa.o
+rsa_generic-y += rsa_helper.o
+obj-$(CONFIG_CRYPTO_RSA) += rsa_generic.o
 
 cryptomgr-y := algboss.o testmgr.o
 
@@ -58,6 +68,7 @@ obj-$(CONFIG_CRYPTO_XTS) += xts.o
 obj-$(CONFIG_CRYPTO_CTR) += ctr.o
 obj-$(CONFIG_CRYPTO_GCM) += gcm.o
 obj-$(CONFIG_CRYPTO_CCM) += ccm.o
+obj-$(CONFIG_CRYPTO_CHACHA20POLY1305) += chacha20poly1305.o
 obj-$(CONFIG_CRYPTO_PCRYPT) += pcrypt.o
 obj-$(CONFIG_CRYPTO_CRYPTD) += cryptd.o
 obj-$(CONFIG_CRYPTO_MCRYPTD) += mcryptd.o
@@ -79,6 +90,8 @@ obj-$(CONFIG_CRYPTO_KHAZAD) += khazad.o
 obj-$(CONFIG_CRYPTO_ANUBIS) += anubis.o
 obj-$(CONFIG_CRYPTO_SEED) += seed.o
 obj-$(CONFIG_CRYPTO_SALSA20) += salsa20_generic.o
+obj-$(CONFIG_CRYPTO_CHACHA20) += chacha20_generic.o
+obj-$(CONFIG_CRYPTO_POLY1305) += poly1305_generic.o
 obj-$(CONFIG_CRYPTO_DEFLATE) += deflate.o
 obj-$(CONFIG_CRYPTO_ZLIB) += zlib.o
 obj-$(CONFIG_CRYPTO_MICHAEL_MIC) += michael_mic.o
@@ -91,9 +104,9 @@ obj-$(CONFIG_CRYPTO_LZ4) += lz4.o
 obj-$(CONFIG_CRYPTO_LZ4HC) += lz4hc.o
 obj-$(CONFIG_CRYPTO_842) += 842.o
 obj-$(CONFIG_CRYPTO_RNG2) += rng.o
-obj-$(CONFIG_CRYPTO_RNG2) += krng.o
 obj-$(CONFIG_CRYPTO_ANSI_CPRNG) += ansi_cprng.o
 obj-$(CONFIG_CRYPTO_DRBG) += drbg.o
+obj-$(CONFIG_CRYPTO_JITTERENTROPY) += jitterentropy.o
 obj-$(CONFIG_CRYPTO_TEST) += tcrypt.o
 obj-$(CONFIG_CRYPTO_GHASH) += ghash-generic.o
 obj-$(CONFIG_CRYPTO_USER_API) += af_alg.o
index db201bca1581934f4b43bdffda642cec27ff816a..b788f169cc9880d0d1ac2ce4930145d9862b306d 100644 (file)
@@ -454,7 +454,7 @@ static int crypto_init_givcipher_ops(struct crypto_tfm *tfm, u32 type,
                      alg->setkey : setkey;
        crt->encrypt = alg->encrypt;
        crt->decrypt = alg->decrypt;
-       crt->givencrypt = alg->givencrypt;
+       crt->givencrypt = alg->givencrypt ?: no_givdecrypt;
        crt->givdecrypt = alg->givdecrypt ?: no_givdecrypt;
        crt->base = __crypto_ablkcipher_cast(tfm);
        crt->ivsize = alg->ivsize;
@@ -586,6 +586,13 @@ static int crypto_givcipher_default(struct crypto_alg *alg, u32 type, u32 mask)
        if (!tmpl)
                goto kill_larval;
 
+       if (tmpl->create) {
+               err = tmpl->create(tmpl, tb);
+               if (err)
+                       goto put_tmpl;
+               goto ok;
+       }
+
        inst = tmpl->alloc(tb);
        err = PTR_ERR(inst);
        if (IS_ERR(inst))
@@ -597,6 +604,7 @@ static int crypto_givcipher_default(struct crypto_alg *alg, u32 type, u32 mask)
                goto put_tmpl;
        }
 
+ok:
        /* Redo the lookup to use the instance we just registered. */
        err = -EAGAIN;
 
@@ -636,7 +644,7 @@ struct crypto_alg *crypto_lookup_skcipher(const char *name, u32 type, u32 mask)
 
        if ((alg->cra_flags & CRYPTO_ALG_TYPE_MASK) ==
            CRYPTO_ALG_TYPE_GIVCIPHER) {
-               if ((alg->cra_flags ^ type ^ ~mask) & CRYPTO_ALG_TESTED) {
+               if (~alg->cra_flags & (type ^ ~mask) & CRYPTO_ALG_TESTED) {
                        crypto_mod_put(alg);
                        alg = ERR_PTR(-ENOENT);
                }
index 222271070b49189bf2671acb1aee5a9ecc92ecc8..07bf99773548bf9f088b6ff8380edb4caf7cda4a 100644 (file)
@@ -12,7 +12,8 @@
  *
  */
 
-#include <crypto/internal/aead.h>
+#include <crypto/internal/geniv.h>
+#include <crypto/scatterwalk.h>
 #include <linux/err.h>
 #include <linux/init.h>
 #include <linux/kernel.h>
 
 #include "internal.h"
 
+struct compat_request_ctx {
+       struct scatterlist src[2];
+       struct scatterlist dst[2];
+       struct scatterlist ivbuf[2];
+       struct scatterlist *ivsg;
+       struct aead_givcrypt_request subreq;
+};
+
+static int aead_null_givencrypt(struct aead_givcrypt_request *req);
+static int aead_null_givdecrypt(struct aead_givcrypt_request *req);
+
 static int setkey_unaligned(struct crypto_aead *tfm, const u8 *key,
                            unsigned int keylen)
 {
-       struct aead_alg *aead = crypto_aead_alg(tfm);
        unsigned long alignmask = crypto_aead_alignmask(tfm);
        int ret;
        u8 *buffer, *alignbuffer;
@@ -42,47 +53,95 @@ static int setkey_unaligned(struct crypto_aead *tfm, const u8 *key,
 
        alignbuffer = (u8 *)ALIGN((unsigned long)buffer, alignmask + 1);
        memcpy(alignbuffer, key, keylen);
-       ret = aead->setkey(tfm, alignbuffer, keylen);
+       ret = tfm->setkey(tfm, alignbuffer, keylen);
        memset(alignbuffer, 0, keylen);
        kfree(buffer);
        return ret;
 }
 
-static int setkey(struct crypto_aead *tfm, const u8 *key, unsigned int keylen)
+int crypto_aead_setkey(struct crypto_aead *tfm,
+                      const u8 *key, unsigned int keylen)
 {
-       struct aead_alg *aead = crypto_aead_alg(tfm);
        unsigned long alignmask = crypto_aead_alignmask(tfm);
 
+       tfm = tfm->child;
+
        if ((unsigned long)key & alignmask)
                return setkey_unaligned(tfm, key, keylen);
 
-       return aead->setkey(tfm, key, keylen);
+       return tfm->setkey(tfm, key, keylen);
 }
+EXPORT_SYMBOL_GPL(crypto_aead_setkey);
 
 int crypto_aead_setauthsize(struct crypto_aead *tfm, unsigned int authsize)
 {
-       struct aead_tfm *crt = crypto_aead_crt(tfm);
        int err;
 
-       if (authsize > crypto_aead_alg(tfm)->maxauthsize)
+       if (authsize > crypto_aead_maxauthsize(tfm))
                return -EINVAL;
 
-       if (crypto_aead_alg(tfm)->setauthsize) {
-               err = crypto_aead_alg(tfm)->setauthsize(crt->base, authsize);
+       if (tfm->setauthsize) {
+               err = tfm->setauthsize(tfm->child, authsize);
                if (err)
                        return err;
        }
 
-       crypto_aead_crt(crt->base)->authsize = authsize;
-       crt->authsize = authsize;
+       tfm->child->authsize = authsize;
+       tfm->authsize = authsize;
        return 0;
 }
 EXPORT_SYMBOL_GPL(crypto_aead_setauthsize);
 
-static unsigned int crypto_aead_ctxsize(struct crypto_alg *alg, u32 type,
-                                       u32 mask)
+struct aead_old_request {
+       struct scatterlist srcbuf[2];
+       struct scatterlist dstbuf[2];
+       struct aead_request subreq;
+};
+
+unsigned int crypto_aead_reqsize(struct crypto_aead *tfm)
 {
-       return alg->cra_ctxsize;
+       return tfm->reqsize + sizeof(struct aead_old_request);
+}
+EXPORT_SYMBOL_GPL(crypto_aead_reqsize);
+
+static int old_crypt(struct aead_request *req,
+                    int (*crypt)(struct aead_request *req))
+{
+       struct aead_old_request *nreq = aead_request_ctx(req);
+       struct crypto_aead *aead = crypto_aead_reqtfm(req);
+       struct scatterlist *src, *dst;
+
+       if (req->old)
+               return crypt(req);
+
+       src = scatterwalk_ffwd(nreq->srcbuf, req->src, req->assoclen);
+       dst = req->src == req->dst ?
+             src : scatterwalk_ffwd(nreq->dstbuf, req->dst, req->assoclen);
+
+       aead_request_set_tfm(&nreq->subreq, aead);
+       aead_request_set_callback(&nreq->subreq, aead_request_flags(req),
+                                 req->base.complete, req->base.data);
+       aead_request_set_crypt(&nreq->subreq, src, dst, req->cryptlen,
+                              req->iv);
+       aead_request_set_assoc(&nreq->subreq, req->src, req->assoclen);
+
+       return crypt(&nreq->subreq);
+}
+
+static int old_encrypt(struct aead_request *req)
+{
+       struct crypto_aead *aead = crypto_aead_reqtfm(req);
+       struct old_aead_alg *alg = crypto_old_aead_alg(aead);
+
+       return old_crypt(req, alg->encrypt);
+}
+
+static int old_decrypt(struct aead_request *req)
+{
+       struct crypto_aead *aead = crypto_aead_reqtfm(req);
+       struct old_aead_alg *alg = crypto_old_aead_alg(aead);
+
+       return old_crypt(req, alg->decrypt);
 }
 
 static int no_givcrypt(struct aead_givcrypt_request *req)
@@ -90,32 +149,68 @@ static int no_givcrypt(struct aead_givcrypt_request *req)
        return -ENOSYS;
 }
 
-static int crypto_init_aead_ops(struct crypto_tfm *tfm, u32 type, u32 mask)
+static int crypto_old_aead_init_tfm(struct crypto_tfm *tfm)
 {
-       struct aead_alg *alg = &tfm->__crt_alg->cra_aead;
-       struct aead_tfm *crt = &tfm->crt_aead;
+       struct old_aead_alg *alg = &tfm->__crt_alg->cra_aead;
+       struct crypto_aead *crt = __crypto_aead_cast(tfm);
 
        if (max(alg->maxauthsize, alg->ivsize) > PAGE_SIZE / 8)
                return -EINVAL;
 
-       crt->setkey = tfm->__crt_alg->cra_flags & CRYPTO_ALG_GENIV ?
-                     alg->setkey : setkey;
-       crt->encrypt = alg->encrypt;
-       crt->decrypt = alg->decrypt;
-       crt->givencrypt = alg->givencrypt ?: no_givcrypt;
-       crt->givdecrypt = alg->givdecrypt ?: no_givcrypt;
-       crt->base = __crypto_aead_cast(tfm);
-       crt->ivsize = alg->ivsize;
+       crt->setkey = alg->setkey;
+       crt->setauthsize = alg->setauthsize;
+       crt->encrypt = old_encrypt;
+       crt->decrypt = old_decrypt;
+       if (alg->ivsize) {
+               crt->givencrypt = alg->givencrypt ?: no_givcrypt;
+               crt->givdecrypt = alg->givdecrypt ?: no_givcrypt;
+       } else {
+               crt->givencrypt = aead_null_givencrypt;
+               crt->givdecrypt = aead_null_givdecrypt;
+       }
+       crt->child = __crypto_aead_cast(tfm);
        crt->authsize = alg->maxauthsize;
 
        return 0;
 }
 
+static void crypto_aead_exit_tfm(struct crypto_tfm *tfm)
+{
+       struct crypto_aead *aead = __crypto_aead_cast(tfm);
+       struct aead_alg *alg = crypto_aead_alg(aead);
+
+       alg->exit(aead);
+}
+
+static int crypto_aead_init_tfm(struct crypto_tfm *tfm)
+{
+       struct crypto_aead *aead = __crypto_aead_cast(tfm);
+       struct aead_alg *alg = crypto_aead_alg(aead);
+
+       if (crypto_old_aead_alg(aead)->encrypt)
+               return crypto_old_aead_init_tfm(tfm);
+
+       aead->setkey = alg->setkey;
+       aead->setauthsize = alg->setauthsize;
+       aead->encrypt = alg->encrypt;
+       aead->decrypt = alg->decrypt;
+       aead->child = __crypto_aead_cast(tfm);
+       aead->authsize = alg->maxauthsize;
+
+       if (alg->exit)
+               aead->base.exit = crypto_aead_exit_tfm;
+
+       if (alg->init)
+               return alg->init(aead);
+
+       return 0;
+}
+
 #ifdef CONFIG_NET
-static int crypto_aead_report(struct sk_buff *skb, struct crypto_alg *alg)
+static int crypto_old_aead_report(struct sk_buff *skb, struct crypto_alg *alg)
 {
        struct crypto_report_aead raead;
-       struct aead_alg *aead = &alg->cra_aead;
+       struct old_aead_alg *aead = &alg->cra_aead;
 
        strncpy(raead.type, "aead", sizeof(raead.type));
        strncpy(raead.geniv, aead->geniv ?: "<built-in>", sizeof(raead.geniv));
@@ -129,6 +224,64 @@ static int crypto_aead_report(struct sk_buff *skb, struct crypto_alg *alg)
                goto nla_put_failure;
        return 0;
 
+nla_put_failure:
+       return -EMSGSIZE;
+}
+#else
+static int crypto_old_aead_report(struct sk_buff *skb, struct crypto_alg *alg)
+{
+       return -ENOSYS;
+}
+#endif
+
+static void crypto_old_aead_show(struct seq_file *m, struct crypto_alg *alg)
+       __attribute__ ((unused));
+static void crypto_old_aead_show(struct seq_file *m, struct crypto_alg *alg)
+{
+       struct old_aead_alg *aead = &alg->cra_aead;
+
+       seq_printf(m, "type         : aead\n");
+       seq_printf(m, "async        : %s\n", alg->cra_flags & CRYPTO_ALG_ASYNC ?
+                                            "yes" : "no");
+       seq_printf(m, "blocksize    : %u\n", alg->cra_blocksize);
+       seq_printf(m, "ivsize       : %u\n", aead->ivsize);
+       seq_printf(m, "maxauthsize  : %u\n", aead->maxauthsize);
+       seq_printf(m, "geniv        : %s\n", aead->geniv ?: "<built-in>");
+}
+
+const struct crypto_type crypto_aead_type = {
+       .extsize = crypto_alg_extsize,
+       .init_tfm = crypto_aead_init_tfm,
+#ifdef CONFIG_PROC_FS
+       .show = crypto_old_aead_show,
+#endif
+       .report = crypto_old_aead_report,
+       .lookup = crypto_lookup_aead,
+       .maskclear = ~(CRYPTO_ALG_TYPE_MASK | CRYPTO_ALG_GENIV),
+       .maskset = CRYPTO_ALG_TYPE_MASK,
+       .type = CRYPTO_ALG_TYPE_AEAD,
+       .tfmsize = offsetof(struct crypto_aead, base),
+};
+EXPORT_SYMBOL_GPL(crypto_aead_type);
+
+#ifdef CONFIG_NET
+static int crypto_aead_report(struct sk_buff *skb, struct crypto_alg *alg)
+{
+       struct crypto_report_aead raead;
+       struct aead_alg *aead = container_of(alg, struct aead_alg, base);
+
+       strncpy(raead.type, "aead", sizeof(raead.type));
+       strncpy(raead.geniv, "<none>", sizeof(raead.geniv));
+
+       raead.blocksize = alg->cra_blocksize;
+       raead.maxauthsize = aead->maxauthsize;
+       raead.ivsize = aead->ivsize;
+
+       if (nla_put(skb, CRYPTOCFGA_REPORT_AEAD,
+                   sizeof(struct crypto_report_aead), &raead))
+               goto nla_put_failure;
+       return 0;
+
 nla_put_failure:
        return -EMSGSIZE;
 }
@@ -143,7 +296,7 @@ static void crypto_aead_show(struct seq_file *m, struct crypto_alg *alg)
        __attribute__ ((unused));
 static void crypto_aead_show(struct seq_file *m, struct crypto_alg *alg)
 {
-       struct aead_alg *aead = &alg->cra_aead;
+       struct aead_alg *aead = container_of(alg, struct aead_alg, base);
 
        seq_printf(m, "type         : aead\n");
        seq_printf(m, "async        : %s\n", alg->cra_flags & CRYPTO_ALG_ASYNC ?
@@ -151,18 +304,21 @@ static void crypto_aead_show(struct seq_file *m, struct crypto_alg *alg)
        seq_printf(m, "blocksize    : %u\n", alg->cra_blocksize);
        seq_printf(m, "ivsize       : %u\n", aead->ivsize);
        seq_printf(m, "maxauthsize  : %u\n", aead->maxauthsize);
-       seq_printf(m, "geniv        : %s\n", aead->geniv ?: "<built-in>");
+       seq_printf(m, "geniv        : <none>\n");
 }
 
-const struct crypto_type crypto_aead_type = {
-       .ctxsize = crypto_aead_ctxsize,
-       .init = crypto_init_aead_ops,
+static const struct crypto_type crypto_new_aead_type = {
+       .extsize = crypto_alg_extsize,
+       .init_tfm = crypto_aead_init_tfm,
 #ifdef CONFIG_PROC_FS
        .show = crypto_aead_show,
 #endif
        .report = crypto_aead_report,
+       .maskclear = ~CRYPTO_ALG_TYPE_MASK,
+       .maskset = CRYPTO_ALG_TYPE_MASK,
+       .type = CRYPTO_ALG_TYPE_AEAD,
+       .tfmsize = offsetof(struct crypto_aead, base),
 };
-EXPORT_SYMBOL_GPL(crypto_aead_type);
 
 static int aead_null_givencrypt(struct aead_givcrypt_request *req)
 {
@@ -174,33 +330,11 @@ static int aead_null_givdecrypt(struct aead_givcrypt_request *req)
        return crypto_aead_decrypt(&req->areq);
 }
 
-static int crypto_init_nivaead_ops(struct crypto_tfm *tfm, u32 type, u32 mask)
-{
-       struct aead_alg *alg = &tfm->__crt_alg->cra_aead;
-       struct aead_tfm *crt = &tfm->crt_aead;
-
-       if (max(alg->maxauthsize, alg->ivsize) > PAGE_SIZE / 8)
-               return -EINVAL;
-
-       crt->setkey = setkey;
-       crt->encrypt = alg->encrypt;
-       crt->decrypt = alg->decrypt;
-       if (!alg->ivsize) {
-               crt->givencrypt = aead_null_givencrypt;
-               crt->givdecrypt = aead_null_givdecrypt;
-       }
-       crt->base = __crypto_aead_cast(tfm);
-       crt->ivsize = alg->ivsize;
-       crt->authsize = alg->maxauthsize;
-
-       return 0;
-}
-
 #ifdef CONFIG_NET
 static int crypto_nivaead_report(struct sk_buff *skb, struct crypto_alg *alg)
 {
        struct crypto_report_aead raead;
-       struct aead_alg *aead = &alg->cra_aead;
+       struct old_aead_alg *aead = &alg->cra_aead;
 
        strncpy(raead.type, "nivaead", sizeof(raead.type));
        strncpy(raead.geniv, aead->geniv, sizeof(raead.geniv));
@@ -229,7 +363,7 @@ static void crypto_nivaead_show(struct seq_file *m, struct crypto_alg *alg)
        __attribute__ ((unused));
 static void crypto_nivaead_show(struct seq_file *m, struct crypto_alg *alg)
 {
-       struct aead_alg *aead = &alg->cra_aead;
+       struct old_aead_alg *aead = &alg->cra_aead;
 
        seq_printf(m, "type         : nivaead\n");
        seq_printf(m, "async        : %s\n", alg->cra_flags & CRYPTO_ALG_ASYNC ?
@@ -241,43 +375,215 @@ static void crypto_nivaead_show(struct seq_file *m, struct crypto_alg *alg)
 }
 
 const struct crypto_type crypto_nivaead_type = {
-       .ctxsize = crypto_aead_ctxsize,
-       .init = crypto_init_nivaead_ops,
+       .extsize = crypto_alg_extsize,
+       .init_tfm = crypto_aead_init_tfm,
 #ifdef CONFIG_PROC_FS
        .show = crypto_nivaead_show,
 #endif
        .report = crypto_nivaead_report,
+       .maskclear = ~(CRYPTO_ALG_TYPE_MASK | CRYPTO_ALG_GENIV),
+       .maskset = CRYPTO_ALG_TYPE_MASK | CRYPTO_ALG_GENIV,
+       .type = CRYPTO_ALG_TYPE_AEAD,
+       .tfmsize = offsetof(struct crypto_aead, base),
 };
 EXPORT_SYMBOL_GPL(crypto_nivaead_type);
 
 static int crypto_grab_nivaead(struct crypto_aead_spawn *spawn,
                               const char *name, u32 type, u32 mask)
 {
-       struct crypto_alg *alg;
+       spawn->base.frontend = &crypto_nivaead_type;
+       return crypto_grab_spawn(&spawn->base, name, type, mask);
+}
+
+static int aead_geniv_setkey(struct crypto_aead *tfm,
+                            const u8 *key, unsigned int keylen)
+{
+       struct aead_geniv_ctx *ctx = crypto_aead_ctx(tfm);
+
+       return crypto_aead_setkey(ctx->child, key, keylen);
+}
+
+static int aead_geniv_setauthsize(struct crypto_aead *tfm,
+                                 unsigned int authsize)
+{
+       struct aead_geniv_ctx *ctx = crypto_aead_ctx(tfm);
+
+       return crypto_aead_setauthsize(ctx->child, authsize);
+}
+
+static void compat_encrypt_complete2(struct aead_request *req, int err)
+{
+       struct compat_request_ctx *rctx = aead_request_ctx(req);
+       struct aead_givcrypt_request *subreq = &rctx->subreq;
+       struct crypto_aead *geniv;
+
+       if (err == -EINPROGRESS)
+               return;
+
+       if (err)
+               goto out;
+
+       geniv = crypto_aead_reqtfm(req);
+       scatterwalk_map_and_copy(subreq->giv, rctx->ivsg, 0,
+                                crypto_aead_ivsize(geniv), 1);
+
+out:
+       kzfree(subreq->giv);
+}
+
+static void compat_encrypt_complete(struct crypto_async_request *base, int err)
+{
+       struct aead_request *req = base->data;
+
+       compat_encrypt_complete2(req, err);
+       aead_request_complete(req, err);
+}
+
+static int compat_encrypt(struct aead_request *req)
+{
+       struct crypto_aead *geniv = crypto_aead_reqtfm(req);
+       struct aead_geniv_ctx *ctx = crypto_aead_ctx(geniv);
+       struct compat_request_ctx *rctx = aead_request_ctx(req);
+       struct aead_givcrypt_request *subreq = &rctx->subreq;
+       unsigned int ivsize = crypto_aead_ivsize(geniv);
+       struct scatterlist *src, *dst;
+       crypto_completion_t compl;
+       void *data;
+       u8 *info;
+       __be64 seq;
        int err;
 
-       type &= ~(CRYPTO_ALG_TYPE_MASK | CRYPTO_ALG_GENIV);
-       type |= CRYPTO_ALG_TYPE_AEAD;
-       mask |= CRYPTO_ALG_TYPE_MASK | CRYPTO_ALG_GENIV;
+       if (req->cryptlen < ivsize)
+               return -EINVAL;
 
-       alg = crypto_alg_mod_lookup(name, type, mask);
-       if (IS_ERR(alg))
-               return PTR_ERR(alg);
+       compl = req->base.complete;
+       data = req->base.data;
+
+       rctx->ivsg = scatterwalk_ffwd(rctx->ivbuf, req->dst, req->assoclen);
+       info = PageHighMem(sg_page(rctx->ivsg)) ? NULL : sg_virt(rctx->ivsg);
+
+       if (!info) {
+               info = kmalloc(ivsize, req->base.flags &
+                                      CRYPTO_TFM_REQ_MAY_SLEEP ? GFP_KERNEL:
+                                                                 GFP_ATOMIC);
+               if (!info)
+                       return -ENOMEM;
+
+               compl = compat_encrypt_complete;
+               data = req;
+       }
+
+       memcpy(&seq, req->iv + ivsize - sizeof(seq), sizeof(seq));
+
+       src = scatterwalk_ffwd(rctx->src, req->src, req->assoclen + ivsize);
+       dst = req->src == req->dst ?
+             src : scatterwalk_ffwd(rctx->dst, rctx->ivsg, ivsize);
+
+       aead_givcrypt_set_tfm(subreq, ctx->child);
+       aead_givcrypt_set_callback(subreq, req->base.flags,
+                                  req->base.complete, req->base.data);
+       aead_givcrypt_set_crypt(subreq, src, dst,
+                               req->cryptlen - ivsize, req->iv);
+       aead_givcrypt_set_assoc(subreq, req->src, req->assoclen);
+       aead_givcrypt_set_giv(subreq, info, be64_to_cpu(seq));
+
+       err = crypto_aead_givencrypt(subreq);
+       if (unlikely(PageHighMem(sg_page(rctx->ivsg))))
+               compat_encrypt_complete2(req, err);
+       return err;
+}
+
+static int compat_decrypt(struct aead_request *req)
+{
+       struct crypto_aead *geniv = crypto_aead_reqtfm(req);
+       struct aead_geniv_ctx *ctx = crypto_aead_ctx(geniv);
+       struct compat_request_ctx *rctx = aead_request_ctx(req);
+       struct aead_request *subreq = &rctx->subreq.areq;
+       unsigned int ivsize = crypto_aead_ivsize(geniv);
+       struct scatterlist *src, *dst;
+       crypto_completion_t compl;
+       void *data;
+
+       if (req->cryptlen < ivsize)
+               return -EINVAL;
+
+       aead_request_set_tfm(subreq, ctx->child);
+
+       compl = req->base.complete;
+       data = req->base.data;
+
+       src = scatterwalk_ffwd(rctx->src, req->src, req->assoclen + ivsize);
+       dst = req->src == req->dst ?
+             src : scatterwalk_ffwd(rctx->dst, req->dst,
+                                    req->assoclen + ivsize);
+
+       aead_request_set_callback(subreq, req->base.flags, compl, data);
+       aead_request_set_crypt(subreq, src, dst,
+                              req->cryptlen - ivsize, req->iv);
+       aead_request_set_assoc(subreq, req->src, req->assoclen);
+
+       scatterwalk_map_and_copy(req->iv, req->src, req->assoclen, ivsize, 0);
+
+       return crypto_aead_decrypt(subreq);
+}
+
+static int compat_encrypt_first(struct aead_request *req)
+{
+       struct crypto_aead *geniv = crypto_aead_reqtfm(req);
+       struct aead_geniv_ctx *ctx = crypto_aead_ctx(geniv);
+       int err = 0;
+
+       spin_lock_bh(&ctx->lock);
+       if (geniv->encrypt != compat_encrypt_first)
+               goto unlock;
+
+       geniv->encrypt = compat_encrypt;
+
+unlock:
+       spin_unlock_bh(&ctx->lock);
+
+       if (err)
+               return err;
+
+       return compat_encrypt(req);
+}
+
+static int aead_geniv_init_compat(struct crypto_tfm *tfm)
+{
+       struct crypto_aead *geniv = __crypto_aead_cast(tfm);
+       struct aead_geniv_ctx *ctx = crypto_aead_ctx(geniv);
+       int err;
+
+       spin_lock_init(&ctx->lock);
+
+       crypto_aead_set_reqsize(geniv, sizeof(struct compat_request_ctx));
+
+       err = aead_geniv_init(tfm);
+
+       ctx->child = geniv->child;
+       geniv->child = geniv;
 
-       err = crypto_init_spawn(&spawn->base, alg, spawn->base.inst, mask);
-       crypto_mod_put(alg);
        return err;
 }
 
-struct crypto_instance *aead_geniv_alloc(struct crypto_template *tmpl,
-                                        struct rtattr **tb, u32 type,
-                                        u32 mask)
+static void aead_geniv_exit_compat(struct crypto_tfm *tfm)
+{
+       struct crypto_aead *geniv = __crypto_aead_cast(tfm);
+       struct aead_geniv_ctx *ctx = crypto_aead_ctx(geniv);
+
+       crypto_free_aead(ctx->child);
+}
+
+struct aead_instance *aead_geniv_alloc(struct crypto_template *tmpl,
+                                      struct rtattr **tb, u32 type, u32 mask)
 {
        const char *name;
        struct crypto_aead_spawn *spawn;
        struct crypto_attr_type *algt;
-       struct crypto_instance *inst;
-       struct crypto_alg *alg;
+       struct aead_instance *inst;
+       struct aead_alg *alg;
+       unsigned int ivsize;
+       unsigned int maxauthsize;
        int err;
 
        algt = crypto_get_attr_type(tb);
@@ -296,20 +602,25 @@ struct crypto_instance *aead_geniv_alloc(struct crypto_template *tmpl,
        if (!inst)
                return ERR_PTR(-ENOMEM);
 
-       spawn = crypto_instance_ctx(inst);
+       spawn = aead_instance_ctx(inst);
 
        /* Ignore async algorithms if necessary. */
        mask |= crypto_requires_sync(algt->type, algt->mask);
 
-       crypto_set_aead_spawn(spawn, inst);
-       err = crypto_grab_nivaead(spawn, name, type, mask);
+       crypto_set_aead_spawn(spawn, aead_crypto_instance(inst));
+       err = (algt->mask & CRYPTO_ALG_GENIV) ?
+             crypto_grab_nivaead(spawn, name, type, mask) :
+             crypto_grab_aead(spawn, name, type, mask);
        if (err)
                goto err_free_inst;
 
-       alg = crypto_aead_spawn_alg(spawn);
+       alg = crypto_spawn_aead_alg(spawn);
+
+       ivsize = crypto_aead_alg_ivsize(alg);
+       maxauthsize = crypto_aead_alg_maxauthsize(alg);
 
        err = -EINVAL;
-       if (!alg->cra_aead.ivsize)
+       if (ivsize < sizeof(u64))
                goto err_drop_alg;
 
        /*
@@ -318,39 +629,64 @@ struct crypto_instance *aead_geniv_alloc(struct crypto_template *tmpl,
         * template name and double-check the IV generator.
         */
        if (algt->mask & CRYPTO_ALG_GENIV) {
-               if (strcmp(tmpl->name, alg->cra_aead.geniv))
+               if (!alg->base.cra_aead.encrypt)
+                       goto err_drop_alg;
+               if (strcmp(tmpl->name, alg->base.cra_aead.geniv))
                        goto err_drop_alg;
 
-               memcpy(inst->alg.cra_name, alg->cra_name, CRYPTO_MAX_ALG_NAME);
-               memcpy(inst->alg.cra_driver_name, alg->cra_driver_name,
+               memcpy(inst->alg.base.cra_name, alg->base.cra_name,
                       CRYPTO_MAX_ALG_NAME);
-       } else {
-               err = -ENAMETOOLONG;
-               if (snprintf(inst->alg.cra_name, CRYPTO_MAX_ALG_NAME,
-                            "%s(%s)", tmpl->name, alg->cra_name) >=
-                   CRYPTO_MAX_ALG_NAME)
-                       goto err_drop_alg;
-               if (snprintf(inst->alg.cra_driver_name, CRYPTO_MAX_ALG_NAME,
-                            "%s(%s)", tmpl->name, alg->cra_driver_name) >=
-                   CRYPTO_MAX_ALG_NAME)
-                       goto err_drop_alg;
+               memcpy(inst->alg.base.cra_driver_name,
+                      alg->base.cra_driver_name, CRYPTO_MAX_ALG_NAME);
+
+               inst->alg.base.cra_flags = CRYPTO_ALG_TYPE_AEAD |
+                                          CRYPTO_ALG_GENIV;
+               inst->alg.base.cra_flags |= alg->base.cra_flags &
+                                           CRYPTO_ALG_ASYNC;
+               inst->alg.base.cra_priority = alg->base.cra_priority;
+               inst->alg.base.cra_blocksize = alg->base.cra_blocksize;
+               inst->alg.base.cra_alignmask = alg->base.cra_alignmask;
+               inst->alg.base.cra_type = &crypto_aead_type;
+
+               inst->alg.base.cra_aead.ivsize = ivsize;
+               inst->alg.base.cra_aead.maxauthsize = maxauthsize;
+
+               inst->alg.base.cra_aead.setkey = alg->base.cra_aead.setkey;
+               inst->alg.base.cra_aead.setauthsize =
+                       alg->base.cra_aead.setauthsize;
+               inst->alg.base.cra_aead.encrypt = alg->base.cra_aead.encrypt;
+               inst->alg.base.cra_aead.decrypt = alg->base.cra_aead.decrypt;
+
+               goto out;
        }
 
-       inst->alg.cra_flags = CRYPTO_ALG_TYPE_AEAD | CRYPTO_ALG_GENIV;
-       inst->alg.cra_flags |= alg->cra_flags & CRYPTO_ALG_ASYNC;
-       inst->alg.cra_priority = alg->cra_priority;
-       inst->alg.cra_blocksize = alg->cra_blocksize;
-       inst->alg.cra_alignmask = alg->cra_alignmask;
-       inst->alg.cra_type = &crypto_aead_type;
+       err = -ENAMETOOLONG;
+       if (snprintf(inst->alg.base.cra_name, CRYPTO_MAX_ALG_NAME,
+                    "%s(%s)", tmpl->name, alg->base.cra_name) >=
+           CRYPTO_MAX_ALG_NAME)
+               goto err_drop_alg;
+       if (snprintf(inst->alg.base.cra_driver_name, CRYPTO_MAX_ALG_NAME,
+                    "%s(%s)", tmpl->name, alg->base.cra_driver_name) >=
+           CRYPTO_MAX_ALG_NAME)
+               goto err_drop_alg;
+
+       inst->alg.base.cra_flags = alg->base.cra_flags & CRYPTO_ALG_ASYNC;
+       inst->alg.base.cra_priority = alg->base.cra_priority;
+       inst->alg.base.cra_blocksize = alg->base.cra_blocksize;
+       inst->alg.base.cra_alignmask = alg->base.cra_alignmask;
+       inst->alg.base.cra_ctxsize = sizeof(struct aead_geniv_ctx);
+
+       inst->alg.setkey = aead_geniv_setkey;
+       inst->alg.setauthsize = aead_geniv_setauthsize;
 
-       inst->alg.cra_aead.ivsize = alg->cra_aead.ivsize;
-       inst->alg.cra_aead.maxauthsize = alg->cra_aead.maxauthsize;
-       inst->alg.cra_aead.geniv = alg->cra_aead.geniv;
+       inst->alg.ivsize = ivsize;
+       inst->alg.maxauthsize = maxauthsize;
 
-       inst->alg.cra_aead.setkey = alg->cra_aead.setkey;
-       inst->alg.cra_aead.setauthsize = alg->cra_aead.setauthsize;
-       inst->alg.cra_aead.encrypt = alg->cra_aead.encrypt;
-       inst->alg.cra_aead.decrypt = alg->cra_aead.decrypt;
+       inst->alg.encrypt = compat_encrypt_first;
+       inst->alg.decrypt = compat_decrypt;
+
+       inst->alg.base.cra_init = aead_geniv_init_compat;
+       inst->alg.base.cra_exit = aead_geniv_exit_compat;
 
 out:
        return inst;
@@ -364,9 +700,9 @@ err_free_inst:
 }
 EXPORT_SYMBOL_GPL(aead_geniv_alloc);
 
-void aead_geniv_free(struct crypto_instance *inst)
+void aead_geniv_free(struct aead_instance *inst)
 {
-       crypto_drop_aead(crypto_instance_ctx(inst));
+       crypto_drop_aead(aead_instance_ctx(inst));
        kfree(inst);
 }
 EXPORT_SYMBOL_GPL(aead_geniv_free);
@@ -374,14 +710,17 @@ EXPORT_SYMBOL_GPL(aead_geniv_free);
 int aead_geniv_init(struct crypto_tfm *tfm)
 {
        struct crypto_instance *inst = (void *)tfm->__crt_alg;
+       struct crypto_aead *child;
        struct crypto_aead *aead;
 
-       aead = crypto_spawn_aead(crypto_instance_ctx(inst));
-       if (IS_ERR(aead))
-               return PTR_ERR(aead);
+       aead = __crypto_aead_cast(tfm);
+
+       child = crypto_spawn_aead(crypto_instance_ctx(inst));
+       if (IS_ERR(child))
+               return PTR_ERR(child);
 
-       tfm->crt_aead.base = aead;
-       tfm->crt_aead.reqsize += crypto_aead_reqsize(aead);
+       aead->child = child;
+       aead->reqsize += crypto_aead_reqsize(child);
 
        return 0;
 }
@@ -389,7 +728,7 @@ EXPORT_SYMBOL_GPL(aead_geniv_init);
 
 void aead_geniv_exit(struct crypto_tfm *tfm)
 {
-       crypto_free_aead(tfm->crt_aead.base);
+       crypto_free_aead(__crypto_aead_cast(tfm)->child);
 }
 EXPORT_SYMBOL_GPL(aead_geniv_exit);
 
@@ -443,6 +782,13 @@ static int crypto_nivaead_default(struct crypto_alg *alg, u32 type, u32 mask)
        if (!tmpl)
                goto kill_larval;
 
+       if (tmpl->create) {
+               err = tmpl->create(tmpl, tb);
+               if (err)
+                       goto put_tmpl;
+               goto ok;
+       }
+
        inst = tmpl->alloc(tb);
        err = PTR_ERR(inst);
        if (IS_ERR(inst))
@@ -454,6 +800,7 @@ static int crypto_nivaead_default(struct crypto_alg *alg, u32 type, u32 mask)
                goto put_tmpl;
        }
 
+ok:
        /* Redo the lookup to use the instance we just registered. */
        err = -EAGAIN;
 
@@ -489,7 +836,7 @@ struct crypto_alg *crypto_lookup_aead(const char *name, u32 type, u32 mask)
                return alg;
 
        if (alg->cra_type == &crypto_aead_type) {
-               if ((alg->cra_flags ^ type ^ ~mask) & CRYPTO_ALG_TESTED) {
+               if (~alg->cra_flags & (type ^ ~mask) & CRYPTO_ALG_TESTED) {
                        crypto_mod_put(alg);
                        alg = ERR_PTR(-ENOENT);
                }
@@ -505,62 +852,91 @@ EXPORT_SYMBOL_GPL(crypto_lookup_aead);
 int crypto_grab_aead(struct crypto_aead_spawn *spawn, const char *name,
                     u32 type, u32 mask)
 {
-       struct crypto_alg *alg;
-       int err;
+       spawn->base.frontend = &crypto_aead_type;
+       return crypto_grab_spawn(&spawn->base, name, type, mask);
+}
+EXPORT_SYMBOL_GPL(crypto_grab_aead);
 
-       type &= ~(CRYPTO_ALG_TYPE_MASK | CRYPTO_ALG_GENIV);
-       type |= CRYPTO_ALG_TYPE_AEAD;
-       mask &= ~(CRYPTO_ALG_TYPE_MASK | CRYPTO_ALG_GENIV);
-       mask |= CRYPTO_ALG_TYPE_MASK;
+struct crypto_aead *crypto_alloc_aead(const char *alg_name, u32 type, u32 mask)
+{
+       return crypto_alloc_tfm(alg_name, &crypto_aead_type, type, mask);
+}
+EXPORT_SYMBOL_GPL(crypto_alloc_aead);
 
-       alg = crypto_lookup_aead(name, type, mask);
-       if (IS_ERR(alg))
-               return PTR_ERR(alg);
+static int aead_prepare_alg(struct aead_alg *alg)
+{
+       struct crypto_alg *base = &alg->base;
 
-       err = crypto_init_spawn(&spawn->base, alg, spawn->base.inst, mask);
-       crypto_mod_put(alg);
-       return err;
+       if (max(alg->maxauthsize, alg->ivsize) > PAGE_SIZE / 8)
+               return -EINVAL;
+
+       base->cra_type = &crypto_new_aead_type;
+       base->cra_flags &= ~CRYPTO_ALG_TYPE_MASK;
+       base->cra_flags |= CRYPTO_ALG_TYPE_AEAD;
+
+       return 0;
 }
-EXPORT_SYMBOL_GPL(crypto_grab_aead);
 
-struct crypto_aead *crypto_alloc_aead(const char *alg_name, u32 type, u32 mask)
+int crypto_register_aead(struct aead_alg *alg)
 {
-       struct crypto_tfm *tfm;
+       struct crypto_alg *base = &alg->base;
        int err;
 
-       type &= ~(CRYPTO_ALG_TYPE_MASK | CRYPTO_ALG_GENIV);
-       type |= CRYPTO_ALG_TYPE_AEAD;
-       mask &= ~(CRYPTO_ALG_TYPE_MASK | CRYPTO_ALG_GENIV);
-       mask |= CRYPTO_ALG_TYPE_MASK;
+       err = aead_prepare_alg(alg);
+       if (err)
+               return err;
 
-       for (;;) {
-               struct crypto_alg *alg;
+       return crypto_register_alg(base);
+}
+EXPORT_SYMBOL_GPL(crypto_register_aead);
 
-               alg = crypto_lookup_aead(alg_name, type, mask);
-               if (IS_ERR(alg)) {
-                       err = PTR_ERR(alg);
-                       goto err;
-               }
+void crypto_unregister_aead(struct aead_alg *alg)
+{
+       crypto_unregister_alg(&alg->base);
+}
+EXPORT_SYMBOL_GPL(crypto_unregister_aead);
 
-               tfm = __crypto_alloc_tfm(alg, type, mask);
-               if (!IS_ERR(tfm))
-                       return __crypto_aead_cast(tfm);
+int crypto_register_aeads(struct aead_alg *algs, int count)
+{
+       int i, ret;
 
-               crypto_mod_put(alg);
-               err = PTR_ERR(tfm);
+       for (i = 0; i < count; i++) {
+               ret = crypto_register_aead(&algs[i]);
+               if (ret)
+                       goto err;
+       }
+
+       return 0;
 
 err:
-               if (err != -EAGAIN)
-                       break;
-               if (signal_pending(current)) {
-                       err = -EINTR;
-                       break;
-               }
-       }
+       for (--i; i >= 0; --i)
+               crypto_unregister_aead(&algs[i]);
 
-       return ERR_PTR(err);
+       return ret;
 }
-EXPORT_SYMBOL_GPL(crypto_alloc_aead);
+EXPORT_SYMBOL_GPL(crypto_register_aeads);
+
+void crypto_unregister_aeads(struct aead_alg *algs, int count)
+{
+       int i;
+
+       for (i = count - 1; i >= 0; --i)
+               crypto_unregister_aead(&algs[i]);
+}
+EXPORT_SYMBOL_GPL(crypto_unregister_aeads);
+
+int aead_register_instance(struct crypto_template *tmpl,
+                          struct aead_instance *inst)
+{
+       int err;
+
+       err = aead_prepare_alg(&inst->alg);
+       if (err)
+               return err;
+
+       return crypto_register_instance(tmpl, aead_crypto_instance(inst));
+}
+EXPORT_SYMBOL_GPL(aead_register_instance);
 
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("Authenticated Encryption with Associated Data (AEAD)");
index f22cc56fd1b383f7ef37472808070e8e31120a11..2bc180e02115b9d18cc08ef7df87d35af4b363c9 100644 (file)
@@ -127,6 +127,7 @@ EXPORT_SYMBOL_GPL(af_alg_release);
 
 static int alg_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
 {
+       const u32 forbidden = CRYPTO_ALG_INTERNAL;
        struct sock *sk = sock->sk;
        struct alg_sock *ask = alg_sk(sk);
        struct sockaddr_alg *sa = (void *)uaddr;
@@ -151,7 +152,9 @@ static int alg_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
        if (IS_ERR(type))
                return PTR_ERR(type);
 
-       private = type->bind(sa->salg_name, sa->salg_feat, sa->salg_mask);
+       private = type->bind(sa->salg_name,
+                            sa->salg_feat & ~forbidden,
+                            sa->salg_mask & ~forbidden);
        if (IS_ERR(private)) {
                module_put(type->owner);
                return PTR_ERR(private);
diff --git a/crypto/akcipher.c b/crypto/akcipher.c
new file mode 100644 (file)
index 0000000..d798641
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ * Public Key Encryption
+ *
+ * Copyright (c) 2015, Intel Corporation
+ * Authors: Tadeusz Struk <tadeusz.struk@intel.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/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/crypto.h>
+#include <crypto/algapi.h>
+#include <linux/cryptouser.h>
+#include <net/netlink.h>
+#include <crypto/akcipher.h>
+#include <crypto/public_key.h>
+#include "internal.h"
+
+#ifdef CONFIG_NET
+static int crypto_akcipher_report(struct sk_buff *skb, struct crypto_alg *alg)
+{
+       struct crypto_report_akcipher rakcipher;
+
+       strncpy(rakcipher.type, "akcipher", sizeof(rakcipher.type));
+
+       if (nla_put(skb, CRYPTOCFGA_REPORT_AKCIPHER,
+                   sizeof(struct crypto_report_akcipher), &rakcipher))
+               goto nla_put_failure;
+       return 0;
+
+nla_put_failure:
+       return -EMSGSIZE;
+}
+#else
+static int crypto_akcipher_report(struct sk_buff *skb, struct crypto_alg *alg)
+{
+       return -ENOSYS;
+}
+#endif
+
+static void crypto_akcipher_show(struct seq_file *m, struct crypto_alg *alg)
+       __attribute__ ((unused));
+
+static void crypto_akcipher_show(struct seq_file *m, struct crypto_alg *alg)
+{
+       seq_puts(m, "type         : akcipher\n");
+}
+
+static void crypto_akcipher_exit_tfm(struct crypto_tfm *tfm)
+{
+       struct crypto_akcipher *akcipher = __crypto_akcipher_tfm(tfm);
+       struct akcipher_alg *alg = crypto_akcipher_alg(akcipher);
+
+       alg->exit(akcipher);
+}
+
+static int crypto_akcipher_init_tfm(struct crypto_tfm *tfm)
+{
+       struct crypto_akcipher *akcipher = __crypto_akcipher_tfm(tfm);
+       struct akcipher_alg *alg = crypto_akcipher_alg(akcipher);
+
+       if (alg->exit)
+               akcipher->base.exit = crypto_akcipher_exit_tfm;
+
+       if (alg->init)
+               return alg->init(akcipher);
+
+       return 0;
+}
+
+static const struct crypto_type crypto_akcipher_type = {
+       .extsize = crypto_alg_extsize,
+       .init_tfm = crypto_akcipher_init_tfm,
+#ifdef CONFIG_PROC_FS
+       .show = crypto_akcipher_show,
+#endif
+       .report = crypto_akcipher_report,
+       .maskclear = ~CRYPTO_ALG_TYPE_MASK,
+       .maskset = CRYPTO_ALG_TYPE_MASK,
+       .type = CRYPTO_ALG_TYPE_AKCIPHER,
+       .tfmsize = offsetof(struct crypto_akcipher, base),
+};
+
+struct crypto_akcipher *crypto_alloc_akcipher(const char *alg_name, u32 type,
+                                             u32 mask)
+{
+       return crypto_alloc_tfm(alg_name, &crypto_akcipher_type, type, mask);
+}
+EXPORT_SYMBOL_GPL(crypto_alloc_akcipher);
+
+int crypto_register_akcipher(struct akcipher_alg *alg)
+{
+       struct crypto_alg *base = &alg->base;
+
+       base->cra_type = &crypto_akcipher_type;
+       base->cra_flags &= ~CRYPTO_ALG_TYPE_MASK;
+       base->cra_flags |= CRYPTO_ALG_TYPE_AKCIPHER;
+       return crypto_register_alg(base);
+}
+EXPORT_SYMBOL_GPL(crypto_register_akcipher);
+
+void crypto_unregister_akcipher(struct akcipher_alg *alg)
+{
+       crypto_unregister_alg(&alg->base);
+}
+EXPORT_SYMBOL_GPL(crypto_unregister_akcipher);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Generic public key cihper type");
index d2627a3d4ed8b15cf53127b11daaad412a950711..3c079b7f23f6bada906f9d444a50cd15b0831d65 100644 (file)
@@ -12,6 +12,7 @@
 
 #include <linux/err.h>
 #include <linux/errno.h>
+#include <linux/fips.h>
 #include <linux/init.h>
 #include <linux/kernel.h>
 #include <linux/list.h>
@@ -43,12 +44,9 @@ static inline int crypto_set_driver_name(struct crypto_alg *alg)
 
 static inline void crypto_check_module_sig(struct module *mod)
 {
-#ifdef CONFIG_CRYPTO_FIPS
-       if (fips_enabled && mod && !mod->sig_ok)
+       if (fips_enabled && mod && !module_sig_ok(mod))
                panic("Module %s signature verification failed in FIPS mode\n",
-                     mod->name);
-#endif
-       return;
+                     module_name(mod));
 }
 
 static int crypto_check_alg(struct crypto_alg *alg)
@@ -614,6 +612,22 @@ out:
 }
 EXPORT_SYMBOL_GPL(crypto_init_spawn2);
 
+int crypto_grab_spawn(struct crypto_spawn *spawn, const char *name,
+                     u32 type, u32 mask)
+{
+       struct crypto_alg *alg;
+       int err;
+
+       alg = crypto_find_alg(name, spawn->frontend, type, mask);
+       if (IS_ERR(alg))
+               return PTR_ERR(alg);
+
+       err = crypto_init_spawn(spawn, alg, spawn->inst, mask);
+       crypto_mod_put(alg);
+       return err;
+}
+EXPORT_SYMBOL_GPL(crypto_grab_spawn);
+
 void crypto_drop_spawn(struct crypto_spawn *spawn)
 {
        if (!spawn->alg)
@@ -964,6 +978,13 @@ void crypto_xor(u8 *dst, const u8 *src, unsigned int size)
 }
 EXPORT_SYMBOL_GPL(crypto_xor);
 
+unsigned int crypto_alg_extsize(struct crypto_alg *alg)
+{
+       return alg->cra_ctxsize +
+              (alg->cra_alignmask & ~(crypto_tfm_ctx_alignment() - 1));
+}
+EXPORT_SYMBOL_GPL(crypto_alg_extsize);
+
 static int __init crypto_algapi_init(void)
 {
        crypto_init_proc();
index 69abada22373f54b5dd434ba514e9c6c80c180e5..e0408a480d2f4eb8f818c9590ec9a3d7d166e25d 100644 (file)
@@ -13,6 +13,7 @@
  * any later version.
  */
 
+#include <crypto/aead.h>
 #include <crypto/scatterwalk.h>
 #include <crypto/if_alg.h>
 #include <linux/init.h>
@@ -71,7 +72,7 @@ static inline bool aead_sufficient_data(struct aead_ctx *ctx)
 {
        unsigned as = crypto_aead_authsize(crypto_aead_reqtfm(&ctx->aead_req));
 
-       return (ctx->used >= (ctx->aead_assoclen + (ctx->enc ? 0 : as)));
+       return ctx->used >= ctx->aead_assoclen + as;
 }
 
 static void aead_put_sgl(struct sock *sk)
@@ -352,12 +353,8 @@ static int aead_recvmsg(struct socket *sock, struct msghdr *msg, size_t ignored,
        struct sock *sk = sock->sk;
        struct alg_sock *ask = alg_sk(sk);
        struct aead_ctx *ctx = ask->private;
-       unsigned bs = crypto_aead_blocksize(crypto_aead_reqtfm(&ctx->aead_req));
        unsigned as = crypto_aead_authsize(crypto_aead_reqtfm(&ctx->aead_req));
        struct aead_sg_list *sgl = &ctx->tsgl;
-       struct scatterlist *sg = NULL;
-       struct scatterlist assoc[ALG_MAX_PAGES];
-       size_t assoclen = 0;
        unsigned int i = 0;
        int err = -EINVAL;
        unsigned long used = 0;
@@ -406,23 +403,13 @@ static int aead_recvmsg(struct socket *sock, struct msghdr *msg, size_t ignored,
        if (!aead_sufficient_data(ctx))
                goto unlock;
 
+       outlen = used;
+
        /*
         * The cipher operation input data is reduced by the associated data
         * length as this data is processed separately later on.
         */
-       used -= ctx->aead_assoclen;
-
-       if (ctx->enc) {
-               /* round up output buffer to multiple of block size */
-               outlen = ((used + bs - 1) / bs * bs);
-               /* add the size needed for the auth tag to be created */
-               outlen += as;
-       } else {
-               /* output data size is input without the authentication tag */
-               outlen = used - as;
-               /* round up output buffer to multiple of block size */
-               outlen = ((outlen + bs - 1) / bs * bs);
-       }
+       used -= ctx->aead_assoclen + (ctx->enc ? as : 0);
 
        /* convert iovecs of output buffers into scatterlists */
        while (iov_iter_count(&msg->msg_iter)) {
@@ -451,47 +438,11 @@ static int aead_recvmsg(struct socket *sock, struct msghdr *msg, size_t ignored,
        if (usedpages < outlen)
                goto unlock;
 
-       sg_init_table(assoc, ALG_MAX_PAGES);
-       assoclen = ctx->aead_assoclen;
-       /*
-        * Split scatterlist into two: first part becomes AD, second part
-        * is plaintext / ciphertext. The first part is assigned to assoc
-        * scatterlist. When this loop finishes, sg points to the start of the
-        * plaintext / ciphertext.
-        */
-       for (i = 0; i < ctx->tsgl.cur; i++) {
-               sg = sgl->sg + i;
-               if (sg->length <= assoclen) {
-                       /* AD is larger than one page */
-                       sg_set_page(assoc + i, sg_page(sg),
-                                   sg->length, sg->offset);
-                       assoclen -= sg->length;
-                       if (i >= ctx->tsgl.cur)
-                               goto unlock;
-               } else if (!assoclen) {
-                       /* current page is to start of plaintext / ciphertext */
-                       if (i)
-                               /* AD terminates at page boundary */
-                               sg_mark_end(assoc + i - 1);
-                       else
-                               /* AD size is zero */
-                               sg_mark_end(assoc);
-                       break;
-               } else {
-                       /* AD does not terminate at page boundary */
-                       sg_set_page(assoc + i, sg_page(sg),
-                                   assoclen, sg->offset);
-                       sg_mark_end(assoc + i);
-                       /* plaintext / ciphertext starts after AD */
-                       sg->length -= assoclen;
-                       sg->offset += assoclen;
-                       break;
-               }
-       }
+       sg_mark_end(sgl->sg + sgl->cur - 1);
 
-       aead_request_set_assoc(&ctx->aead_req, assoc, ctx->aead_assoclen);
-       aead_request_set_crypt(&ctx->aead_req, sg, ctx->rsgl[0].sg, used,
-                              ctx->iv);
+       aead_request_set_crypt(&ctx->aead_req, sgl->sg, ctx->rsgl[0].sg,
+                              used, ctx->iv);
+       aead_request_set_ad(&ctx->aead_req, ctx->aead_assoclen);
 
        err = af_alg_wait_for_completion(ctx->enc ?
                                         crypto_aead_encrypt(&ctx->aead_req) :
@@ -563,7 +514,8 @@ static struct proto_ops algif_aead_ops = {
 
 static void *aead_bind(const char *name, u32 type, u32 mask)
 {
-       return crypto_alloc_aead(name, type, mask);
+       return crypto_alloc_aead(name, type | CRYPTO_ALG_AEAD_NEW,
+                                mask | CRYPTO_ALG_AEAD_NEW);
 }
 
 static void aead_release(void *private)
index 8109aaad2726170ed1e5b9238dbd278220d68079..150c2b6480ed2eefd53deb4bc56ba6b45c355aa3 100644 (file)
@@ -164,7 +164,7 @@ static int rng_setkey(void *private, const u8 *seed, unsigned int seedlen)
         * Check whether seedlen is of sufficient size is done in RNG
         * implementations.
         */
-       return crypto_rng_reset(private, (u8 *)seed, seedlen);
+       return crypto_rng_reset(private, seed, seedlen);
 }
 
 static const struct af_alg_type algif_type_rng = {
index 765fe76093482df7dbf16965c3516b5934d789b0..eff337ce9003701290d0ebaef5ade526a6bf0029 100644 (file)
@@ -20,8 +20,6 @@
 #include <linux/moduleparam.h>
 #include <linux/string.h>
 
-#include "internal.h"
-
 #define DEFAULT_PRNG_KEY "0123456789abcdef"
 #define DEFAULT_PRNG_KSZ 16
 #define DEFAULT_BLK_SZ 16
@@ -281,11 +279,11 @@ static void free_prng_context(struct prng_context *ctx)
 }
 
 static int reset_prng_context(struct prng_context *ctx,
-                             unsigned char *key, size_t klen,
-                             unsigned char *V, unsigned char *DT)
+                             const unsigned char *key, size_t klen,
+                             const unsigned char *V, const unsigned char *DT)
 {
        int ret;
-       unsigned char *prng_key;
+       const unsigned char *prng_key;
 
        spin_lock_bh(&ctx->prng_lock);
        ctx->flags |= PRNG_NEED_RESET;
@@ -353,8 +351,9 @@ static void cprng_exit(struct crypto_tfm *tfm)
        free_prng_context(crypto_tfm_ctx(tfm));
 }
 
-static int cprng_get_random(struct crypto_rng *tfm, u8 *rdata,
-                           unsigned int dlen)
+static int cprng_get_random(struct crypto_rng *tfm,
+                           const u8 *src, unsigned int slen,
+                           u8 *rdata, unsigned int dlen)
 {
        struct prng_context *prng = crypto_rng_ctx(tfm);
 
@@ -367,11 +366,12 @@ static int cprng_get_random(struct crypto_rng *tfm, u8 *rdata,
  *  V and KEY are required during reset, and DT is optional, detected
  *  as being present by testing the length of the seed
  */
-static int cprng_reset(struct crypto_rng *tfm, u8 *seed, unsigned int slen)
+static int cprng_reset(struct crypto_rng *tfm,
+                      const u8 *seed, unsigned int slen)
 {
        struct prng_context *prng = crypto_rng_ctx(tfm);
-       u8 *key = seed + DEFAULT_BLK_SZ;
-       u8 *dt = NULL;
+       const u8 *key = seed + DEFAULT_BLK_SZ;
+       const u8 *dt = NULL;
 
        if (slen < DEFAULT_PRNG_KSZ + DEFAULT_BLK_SZ)
                return -EINVAL;
@@ -387,18 +387,20 @@ static int cprng_reset(struct crypto_rng *tfm, u8 *seed, unsigned int slen)
 }
 
 #ifdef CONFIG_CRYPTO_FIPS
-static int fips_cprng_get_random(struct crypto_rng *tfm, u8 *rdata,
-                           unsigned int dlen)
+static int fips_cprng_get_random(struct crypto_rng *tfm,
+                                const u8 *src, unsigned int slen,
+                                u8 *rdata, unsigned int dlen)
 {
        struct prng_context *prng = crypto_rng_ctx(tfm);
 
        return get_prng_bytes(rdata, dlen, prng, 1);
 }
 
-static int fips_cprng_reset(struct crypto_rng *tfm, u8 *seed, unsigned int slen)
+static int fips_cprng_reset(struct crypto_rng *tfm,
+                           const u8 *seed, unsigned int slen)
 {
        u8 rdata[DEFAULT_BLK_SZ];
-       u8 *key = seed + DEFAULT_BLK_SZ;
+       const u8 *key = seed + DEFAULT_BLK_SZ;
        int rc;
 
        struct prng_context *prng = crypto_rng_ctx(tfm);
@@ -424,40 +426,32 @@ out:
 }
 #endif
 
-static struct crypto_alg rng_algs[] = { {
-       .cra_name               = "stdrng",
-       .cra_driver_name        = "ansi_cprng",
-       .cra_priority           = 100,
-       .cra_flags              = CRYPTO_ALG_TYPE_RNG,
-       .cra_ctxsize            = sizeof(struct prng_context),
-       .cra_type               = &crypto_rng_type,
-       .cra_module             = THIS_MODULE,
-       .cra_init               = cprng_init,
-       .cra_exit               = cprng_exit,
-       .cra_u                  = {
-               .rng = {
-                       .rng_make_random        = cprng_get_random,
-                       .rng_reset              = cprng_reset,
-                       .seedsize = DEFAULT_PRNG_KSZ + 2*DEFAULT_BLK_SZ,
-               }
+static struct rng_alg rng_algs[] = { {
+       .generate               = cprng_get_random,
+       .seed                   = cprng_reset,
+       .seedsize               = DEFAULT_PRNG_KSZ + 2 * DEFAULT_BLK_SZ,
+       .base                   =       {
+               .cra_name               = "stdrng",
+               .cra_driver_name        = "ansi_cprng",
+               .cra_priority           = 100,
+               .cra_ctxsize            = sizeof(struct prng_context),
+               .cra_module             = THIS_MODULE,
+               .cra_init               = cprng_init,
+               .cra_exit               = cprng_exit,
        }
 #ifdef CONFIG_CRYPTO_FIPS
 }, {
-       .cra_name               = "fips(ansi_cprng)",
-       .cra_driver_name        = "fips_ansi_cprng",
-       .cra_priority           = 300,
-       .cra_flags              = CRYPTO_ALG_TYPE_RNG,
-       .cra_ctxsize            = sizeof(struct prng_context),
-       .cra_type               = &crypto_rng_type,
-       .cra_module             = THIS_MODULE,
-       .cra_init               = cprng_init,
-       .cra_exit               = cprng_exit,
-       .cra_u                  = {
-               .rng = {
-                       .rng_make_random        = fips_cprng_get_random,
-                       .rng_reset              = fips_cprng_reset,
-                       .seedsize = DEFAULT_PRNG_KSZ + 2*DEFAULT_BLK_SZ,
-               }
+       .generate               = fips_cprng_get_random,
+       .seed                   = fips_cprng_reset,
+       .seedsize               = DEFAULT_PRNG_KSZ + 2 * DEFAULT_BLK_SZ,
+       .base                   =       {
+               .cra_name               = "fips(ansi_cprng)",
+               .cra_driver_name        = "fips_ansi_cprng",
+               .cra_priority           = 300,
+               .cra_ctxsize            = sizeof(struct prng_context),
+               .cra_module             = THIS_MODULE,
+               .cra_init               = cprng_init,
+               .cra_exit               = cprng_exit,
        }
 #endif
 } };
@@ -465,12 +459,12 @@ static struct crypto_alg rng_algs[] = { {
 /* Module initalization */
 static int __init prng_mod_init(void)
 {
-       return crypto_register_algs(rng_algs, ARRAY_SIZE(rng_algs));
+       return crypto_register_rngs(rng_algs, ARRAY_SIZE(rng_algs));
 }
 
 static void __exit prng_mod_fini(void)
 {
-       crypto_unregister_algs(rng_algs, ARRAY_SIZE(rng_algs));
+       crypto_unregister_rngs(rng_algs, ARRAY_SIZE(rng_algs));
 }
 
 MODULE_LICENSE("GPL");
index 78fb16cab13f9660553a0705941608ec3a59d88a..3e852299afb434de793afedc5bc6d570150d8ae5 100644 (file)
@@ -10,7 +10,7 @@
  *
  */
 
-#include <crypto/aead.h>
+#include <crypto/internal/aead.h>
 #include <crypto/internal/hash.h>
 #include <crypto/internal/skcipher.h>
 #include <crypto/authenc.h>
@@ -570,13 +570,14 @@ static int crypto_authenc_init_tfm(struct crypto_tfm *tfm)
                            crypto_ahash_alignmask(auth) + 1) +
                      crypto_ablkcipher_ivsize(enc);
 
-       tfm->crt_aead.reqsize = sizeof(struct authenc_request_ctx) +
-                               ctx->reqoff +
-                               max_t(unsigned int,
-                               crypto_ahash_reqsize(auth) +
-                               sizeof(struct ahash_request),
-                               sizeof(struct skcipher_givcrypt_request) +
-                               crypto_ablkcipher_reqsize(enc));
+       crypto_aead_set_reqsize(__crypto_aead_cast(tfm),
+               sizeof(struct authenc_request_ctx) +
+               ctx->reqoff +
+               max_t(unsigned int,
+                       crypto_ahash_reqsize(auth) +
+                       sizeof(struct ahash_request),
+                       sizeof(struct skcipher_givcrypt_request) +
+                       crypto_ablkcipher_reqsize(enc)));
 
        return 0;
 
index 024bff2344fcff9d0ae3af5c04b4a75280dd2d4a..a3da6770bc9ed2bf66d59e8e74461829eeb4fe4e 100644 (file)
@@ -12,7 +12,7 @@
  *
  */
 
-#include <crypto/aead.h>
+#include <crypto/internal/aead.h>
 #include <crypto/internal/hash.h>
 #include <crypto/internal/skcipher.h>
 #include <crypto/authenc.h>
@@ -662,13 +662,14 @@ static int crypto_authenc_esn_init_tfm(struct crypto_tfm *tfm)
                            crypto_ahash_alignmask(auth) + 1) +
                      crypto_ablkcipher_ivsize(enc);
 
-       tfm->crt_aead.reqsize = sizeof(struct authenc_esn_request_ctx) +
-                               ctx->reqoff +
-                               max_t(unsigned int,
-                               crypto_ahash_reqsize(auth) +
-                               sizeof(struct ahash_request),
-                               sizeof(struct skcipher_givcrypt_request) +
-                               crypto_ablkcipher_reqsize(enc));
+       crypto_aead_set_reqsize(__crypto_aead_cast(tfm),
+               sizeof(struct authenc_esn_request_ctx) +
+               ctx->reqoff +
+               max_t(unsigned int,
+                       crypto_ahash_reqsize(auth) +
+                       sizeof(struct ahash_request),
+                       sizeof(struct skcipher_givcrypt_request) +
+                       crypto_ablkcipher_reqsize(enc)));
 
        return 0;
 
index 0122bec38564d6c5de533d0e80b03d23e95f673b..11b981492031361f7cdf336b58ecbb98b8951196 100644 (file)
@@ -14,6 +14,7 @@
  *
  */
 
+#include <crypto/aead.h>
 #include <crypto/internal/skcipher.h>
 #include <crypto/scatterwalk.h>
 #include <linux/errno.h>
index 003bbbd21a2ba9a24f4207f20037d5ee75593726..a4d1a5eda18b4e9758ec899fdf48c6f1359a8c14 100644 (file)
@@ -453,9 +453,9 @@ static int crypto_ccm_init_tfm(struct crypto_tfm *tfm)
 
        align = crypto_tfm_alg_alignmask(tfm);
        align &= ~(crypto_tfm_ctx_alignment() - 1);
-       tfm->crt_aead.reqsize = align +
-                               sizeof(struct crypto_ccm_req_priv_ctx) +
-                               crypto_ablkcipher_reqsize(ctr);
+       crypto_aead_set_reqsize(__crypto_aead_cast(tfm),
+               align + sizeof(struct crypto_ccm_req_priv_ctx) +
+               crypto_ablkcipher_reqsize(ctr));
 
        return 0;
 
@@ -729,10 +729,10 @@ static int crypto_rfc4309_init_tfm(struct crypto_tfm *tfm)
 
        align = crypto_aead_alignmask(aead);
        align &= ~(crypto_tfm_ctx_alignment() - 1);
-       tfm->crt_aead.reqsize = sizeof(struct aead_request) +
-                               ALIGN(crypto_aead_reqsize(aead),
-                                     crypto_tfm_ctx_alignment()) +
-                               align + 16;
+       crypto_aead_set_reqsize(__crypto_aead_cast(tfm),
+               sizeof(struct aead_request) +
+               ALIGN(crypto_aead_reqsize(aead), crypto_tfm_ctx_alignment()) +
+               align + 16);
 
        return 0;
 }
diff --git a/crypto/chacha20_generic.c b/crypto/chacha20_generic.c
new file mode 100644 (file)
index 0000000..fa42e70
--- /dev/null
@@ -0,0 +1,216 @@
+/*
+ * ChaCha20 256-bit cipher algorithm, RFC7539
+ *
+ * Copyright (C) 2015 Martin Willi
+ *
+ * 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 <crypto/algapi.h>
+#include <linux/crypto.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+#define CHACHA20_NONCE_SIZE 16
+#define CHACHA20_KEY_SIZE   32
+#define CHACHA20_BLOCK_SIZE 64
+
+struct chacha20_ctx {
+       u32 key[8];
+};
+
+static inline u32 rotl32(u32 v, u8 n)
+{
+       return (v << n) | (v >> (sizeof(v) * 8 - n));
+}
+
+static inline u32 le32_to_cpuvp(const void *p)
+{
+       return le32_to_cpup(p);
+}
+
+static void chacha20_block(u32 *state, void *stream)
+{
+       u32 x[16], *out = stream;
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(x); i++)
+               x[i] = state[i];
+
+       for (i = 0; i < 20; i += 2) {
+               x[0]  += x[4];    x[12] = rotl32(x[12] ^ x[0],  16);
+               x[1]  += x[5];    x[13] = rotl32(x[13] ^ x[1],  16);
+               x[2]  += x[6];    x[14] = rotl32(x[14] ^ x[2],  16);
+               x[3]  += x[7];    x[15] = rotl32(x[15] ^ x[3],  16);
+
+               x[8]  += x[12];   x[4]  = rotl32(x[4]  ^ x[8],  12);
+               x[9]  += x[13];   x[5]  = rotl32(x[5]  ^ x[9],  12);
+               x[10] += x[14];   x[6]  = rotl32(x[6]  ^ x[10], 12);
+               x[11] += x[15];   x[7]  = rotl32(x[7]  ^ x[11], 12);
+
+               x[0]  += x[4];    x[12] = rotl32(x[12] ^ x[0],   8);
+               x[1]  += x[5];    x[13] = rotl32(x[13] ^ x[1],   8);
+               x[2]  += x[6];    x[14] = rotl32(x[14] ^ x[2],   8);
+               x[3]  += x[7];    x[15] = rotl32(x[15] ^ x[3],   8);
+
+               x[8]  += x[12];   x[4]  = rotl32(x[4]  ^ x[8],   7);
+               x[9]  += x[13];   x[5]  = rotl32(x[5]  ^ x[9],   7);
+               x[10] += x[14];   x[6]  = rotl32(x[6]  ^ x[10],  7);
+               x[11] += x[15];   x[7]  = rotl32(x[7]  ^ x[11],  7);
+
+               x[0]  += x[5];    x[15] = rotl32(x[15] ^ x[0],  16);
+               x[1]  += x[6];    x[12] = rotl32(x[12] ^ x[1],  16);
+               x[2]  += x[7];    x[13] = rotl32(x[13] ^ x[2],  16);
+               x[3]  += x[4];    x[14] = rotl32(x[14] ^ x[3],  16);
+
+               x[10] += x[15];   x[5]  = rotl32(x[5]  ^ x[10], 12);
+               x[11] += x[12];   x[6]  = rotl32(x[6]  ^ x[11], 12);
+               x[8]  += x[13];   x[7]  = rotl32(x[7]  ^ x[8],  12);
+               x[9]  += x[14];   x[4]  = rotl32(x[4]  ^ x[9],  12);
+
+               x[0]  += x[5];    x[15] = rotl32(x[15] ^ x[0],   8);
+               x[1]  += x[6];    x[12] = rotl32(x[12] ^ x[1],   8);
+               x[2]  += x[7];    x[13] = rotl32(x[13] ^ x[2],   8);
+               x[3]  += x[4];    x[14] = rotl32(x[14] ^ x[3],   8);
+
+               x[10] += x[15];   x[5]  = rotl32(x[5]  ^ x[10],  7);
+               x[11] += x[12];   x[6]  = rotl32(x[6]  ^ x[11],  7);
+               x[8]  += x[13];   x[7]  = rotl32(x[7]  ^ x[8],   7);
+               x[9]  += x[14];   x[4]  = rotl32(x[4]  ^ x[9],   7);
+       }
+
+       for (i = 0; i < ARRAY_SIZE(x); i++)
+               out[i] = cpu_to_le32(x[i] + state[i]);
+
+       state[12]++;
+}
+
+static void chacha20_docrypt(u32 *state, u8 *dst, const u8 *src,
+                            unsigned int bytes)
+{
+       u8 stream[CHACHA20_BLOCK_SIZE];
+
+       if (dst != src)
+               memcpy(dst, src, bytes);
+
+       while (bytes >= CHACHA20_BLOCK_SIZE) {
+               chacha20_block(state, stream);
+               crypto_xor(dst, stream, CHACHA20_BLOCK_SIZE);
+               bytes -= CHACHA20_BLOCK_SIZE;
+               dst += CHACHA20_BLOCK_SIZE;
+       }
+       if (bytes) {
+               chacha20_block(state, stream);
+               crypto_xor(dst, stream, bytes);
+       }
+}
+
+static void chacha20_init(u32 *state, struct chacha20_ctx *ctx, u8 *iv)
+{
+       static const char constant[16] = "expand 32-byte k";
+
+       state[0]  = le32_to_cpuvp(constant +  0);
+       state[1]  = le32_to_cpuvp(constant +  4);
+       state[2]  = le32_to_cpuvp(constant +  8);
+       state[3]  = le32_to_cpuvp(constant + 12);
+       state[4]  = ctx->key[0];
+       state[5]  = ctx->key[1];
+       state[6]  = ctx->key[2];
+       state[7]  = ctx->key[3];
+       state[8]  = ctx->key[4];
+       state[9]  = ctx->key[5];
+       state[10] = ctx->key[6];
+       state[11] = ctx->key[7];
+       state[12] = le32_to_cpuvp(iv +  0);
+       state[13] = le32_to_cpuvp(iv +  4);
+       state[14] = le32_to_cpuvp(iv +  8);
+       state[15] = le32_to_cpuvp(iv + 12);
+}
+
+static int chacha20_setkey(struct crypto_tfm *tfm, const u8 *key,
+                          unsigned int keysize)
+{
+       struct chacha20_ctx *ctx = crypto_tfm_ctx(tfm);
+       int i;
+
+       if (keysize != CHACHA20_KEY_SIZE)
+               return -EINVAL;
+
+       for (i = 0; i < ARRAY_SIZE(ctx->key); i++)
+               ctx->key[i] = le32_to_cpuvp(key + i * sizeof(u32));
+
+       return 0;
+}
+
+static int chacha20_crypt(struct blkcipher_desc *desc, struct scatterlist *dst,
+                         struct scatterlist *src, unsigned int nbytes)
+{
+       struct blkcipher_walk walk;
+       u32 state[16];
+       int err;
+
+       blkcipher_walk_init(&walk, dst, src, nbytes);
+       err = blkcipher_walk_virt_block(desc, &walk, CHACHA20_BLOCK_SIZE);
+
+       chacha20_init(state, crypto_blkcipher_ctx(desc->tfm), walk.iv);
+
+       while (walk.nbytes >= CHACHA20_BLOCK_SIZE) {
+               chacha20_docrypt(state, walk.dst.virt.addr, walk.src.virt.addr,
+                                rounddown(walk.nbytes, CHACHA20_BLOCK_SIZE));
+               err = blkcipher_walk_done(desc, &walk,
+                                         walk.nbytes % CHACHA20_BLOCK_SIZE);
+       }
+
+       if (walk.nbytes) {
+               chacha20_docrypt(state, walk.dst.virt.addr, walk.src.virt.addr,
+                                walk.nbytes);
+               err = blkcipher_walk_done(desc, &walk, 0);
+       }
+
+       return err;
+}
+
+static struct crypto_alg alg = {
+       .cra_name               = "chacha20",
+       .cra_driver_name        = "chacha20-generic",
+       .cra_priority           = 100,
+       .cra_flags              = CRYPTO_ALG_TYPE_BLKCIPHER,
+       .cra_blocksize          = 1,
+       .cra_type               = &crypto_blkcipher_type,
+       .cra_ctxsize            = sizeof(struct chacha20_ctx),
+       .cra_alignmask          = sizeof(u32) - 1,
+       .cra_module             = THIS_MODULE,
+       .cra_u                  = {
+               .blkcipher = {
+                       .min_keysize    = CHACHA20_KEY_SIZE,
+                       .max_keysize    = CHACHA20_KEY_SIZE,
+                       .ivsize         = CHACHA20_NONCE_SIZE,
+                       .geniv          = "seqiv",
+                       .setkey         = chacha20_setkey,
+                       .encrypt        = chacha20_crypt,
+                       .decrypt        = chacha20_crypt,
+               },
+       },
+};
+
+static int __init chacha20_generic_mod_init(void)
+{
+       return crypto_register_alg(&alg);
+}
+
+static void __exit chacha20_generic_mod_fini(void)
+{
+       crypto_unregister_alg(&alg);
+}
+
+module_init(chacha20_generic_mod_init);
+module_exit(chacha20_generic_mod_fini);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Martin Willi <martin@strongswan.org>");
+MODULE_DESCRIPTION("chacha20 cipher algorithm");
+MODULE_ALIAS_CRYPTO("chacha20");
+MODULE_ALIAS_CRYPTO("chacha20-generic");
diff --git a/crypto/chacha20poly1305.c b/crypto/chacha20poly1305.c
new file mode 100644 (file)
index 0000000..7b46ed7
--- /dev/null
@@ -0,0 +1,695 @@
+/*
+ * ChaCha20-Poly1305 AEAD, RFC7539
+ *
+ * Copyright (C) 2015 Martin Willi
+ *
+ * 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 <crypto/internal/aead.h>
+#include <crypto/internal/hash.h>
+#include <crypto/internal/skcipher.h>
+#include <crypto/scatterwalk.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+#include "internal.h"
+
+#define POLY1305_BLOCK_SIZE    16
+#define POLY1305_DIGEST_SIZE   16
+#define POLY1305_KEY_SIZE      32
+#define CHACHA20_KEY_SIZE      32
+#define CHACHA20_IV_SIZE       16
+#define CHACHAPOLY_IV_SIZE     12
+
+struct chachapoly_instance_ctx {
+       struct crypto_skcipher_spawn chacha;
+       struct crypto_ahash_spawn poly;
+       unsigned int saltlen;
+};
+
+struct chachapoly_ctx {
+       struct crypto_ablkcipher *chacha;
+       struct crypto_ahash *poly;
+       /* key bytes we use for the ChaCha20 IV */
+       unsigned int saltlen;
+       u8 salt[];
+};
+
+struct poly_req {
+       /* zero byte padding for AD/ciphertext, as needed */
+       u8 pad[POLY1305_BLOCK_SIZE];
+       /* tail data with AD/ciphertext lengths */
+       struct {
+               __le64 assoclen;
+               __le64 cryptlen;
+       } tail;
+       struct scatterlist src[1];
+       struct ahash_request req; /* must be last member */
+};
+
+struct chacha_req {
+       u8 iv[CHACHA20_IV_SIZE];
+       struct scatterlist src[1];
+       struct ablkcipher_request req; /* must be last member */
+};
+
+struct chachapoly_req_ctx {
+       /* the key we generate for Poly1305 using Chacha20 */
+       u8 key[POLY1305_KEY_SIZE];
+       /* calculated Poly1305 tag */
+       u8 tag[POLY1305_DIGEST_SIZE];
+       /* length of data to en/decrypt, without ICV */
+       unsigned int cryptlen;
+       union {
+               struct poly_req poly;
+               struct chacha_req chacha;
+       } u;
+};
+
+static inline void async_done_continue(struct aead_request *req, int err,
+                                      int (*cont)(struct aead_request *))
+{
+       if (!err)
+               err = cont(req);
+
+       if (err != -EINPROGRESS && err != -EBUSY)
+               aead_request_complete(req, err);
+}
+
+static void chacha_iv(u8 *iv, struct aead_request *req, u32 icb)
+{
+       struct chachapoly_ctx *ctx = crypto_aead_ctx(crypto_aead_reqtfm(req));
+       __le32 leicb = cpu_to_le32(icb);
+
+       memcpy(iv, &leicb, sizeof(leicb));
+       memcpy(iv + sizeof(leicb), ctx->salt, ctx->saltlen);
+       memcpy(iv + sizeof(leicb) + ctx->saltlen, req->iv,
+              CHACHA20_IV_SIZE - sizeof(leicb) - ctx->saltlen);
+}
+
+static int poly_verify_tag(struct aead_request *req)
+{
+       struct chachapoly_req_ctx *rctx = aead_request_ctx(req);
+       u8 tag[sizeof(rctx->tag)];
+
+       scatterwalk_map_and_copy(tag, req->src, rctx->cryptlen, sizeof(tag), 0);
+       if (crypto_memneq(tag, rctx->tag, sizeof(tag)))
+               return -EBADMSG;
+       return 0;
+}
+
+static int poly_copy_tag(struct aead_request *req)
+{
+       struct chachapoly_req_ctx *rctx = aead_request_ctx(req);
+
+       scatterwalk_map_and_copy(rctx->tag, req->dst, rctx->cryptlen,
+                                sizeof(rctx->tag), 1);
+       return 0;
+}
+
+static void chacha_decrypt_done(struct crypto_async_request *areq, int err)
+{
+       async_done_continue(areq->data, err, poly_verify_tag);
+}
+
+static int chacha_decrypt(struct aead_request *req)
+{
+       struct chachapoly_ctx *ctx = crypto_aead_ctx(crypto_aead_reqtfm(req));
+       struct chachapoly_req_ctx *rctx = aead_request_ctx(req);
+       struct chacha_req *creq = &rctx->u.chacha;
+       int err;
+
+       chacha_iv(creq->iv, req, 1);
+
+       ablkcipher_request_set_callback(&creq->req, aead_request_flags(req),
+                                       chacha_decrypt_done, req);
+       ablkcipher_request_set_tfm(&creq->req, ctx->chacha);
+       ablkcipher_request_set_crypt(&creq->req, req->src, req->dst,
+                                    rctx->cryptlen, creq->iv);
+       err = crypto_ablkcipher_decrypt(&creq->req);
+       if (err)
+               return err;
+
+       return poly_verify_tag(req);
+}
+
+static int poly_tail_continue(struct aead_request *req)
+{
+       struct chachapoly_req_ctx *rctx = aead_request_ctx(req);
+
+       if (rctx->cryptlen == req->cryptlen) /* encrypting */
+               return poly_copy_tag(req);
+
+       return chacha_decrypt(req);
+}
+
+static void poly_tail_done(struct crypto_async_request *areq, int err)
+{
+       async_done_continue(areq->data, err, poly_tail_continue);
+}
+
+static int poly_tail(struct aead_request *req)
+{
+       struct chachapoly_ctx *ctx = crypto_aead_ctx(crypto_aead_reqtfm(req));
+       struct chachapoly_req_ctx *rctx = aead_request_ctx(req);
+       struct poly_req *preq = &rctx->u.poly;
+       __le64 len;
+       int err;
+
+       sg_init_table(preq->src, 1);
+       len = cpu_to_le64(req->assoclen);
+       memcpy(&preq->tail.assoclen, &len, sizeof(len));
+       len = cpu_to_le64(rctx->cryptlen);
+       memcpy(&preq->tail.cryptlen, &len, sizeof(len));
+       sg_set_buf(preq->src, &preq->tail, sizeof(preq->tail));
+
+       ahash_request_set_callback(&preq->req, aead_request_flags(req),
+                                  poly_tail_done, req);
+       ahash_request_set_tfm(&preq->req, ctx->poly);
+       ahash_request_set_crypt(&preq->req, preq->src,
+                               rctx->tag, sizeof(preq->tail));
+
+       err = crypto_ahash_finup(&preq->req);
+       if (err)
+               return err;
+
+       return poly_tail_continue(req);
+}
+
+static void poly_cipherpad_done(struct crypto_async_request *areq, int err)
+{
+       async_done_continue(areq->data, err, poly_tail);
+}
+
+static int poly_cipherpad(struct aead_request *req)
+{
+       struct chachapoly_ctx *ctx = crypto_aead_ctx(crypto_aead_reqtfm(req));
+       struct chachapoly_req_ctx *rctx = aead_request_ctx(req);
+       struct poly_req *preq = &rctx->u.poly;
+       unsigned int padlen, bs = POLY1305_BLOCK_SIZE;
+       int err;
+
+       padlen = (bs - (rctx->cryptlen % bs)) % bs;
+       memset(preq->pad, 0, sizeof(preq->pad));
+       sg_init_table(preq->src, 1);
+       sg_set_buf(preq->src, &preq->pad, padlen);
+
+       ahash_request_set_callback(&preq->req, aead_request_flags(req),
+                                  poly_cipherpad_done, req);
+       ahash_request_set_tfm(&preq->req, ctx->poly);
+       ahash_request_set_crypt(&preq->req, preq->src, NULL, padlen);
+
+       err = crypto_ahash_update(&preq->req);
+       if (err)
+               return err;
+
+       return poly_tail(req);
+}
+
+static void poly_cipher_done(struct crypto_async_request *areq, int err)
+{
+       async_done_continue(areq->data, err, poly_cipherpad);
+}
+
+static int poly_cipher(struct aead_request *req)
+{
+       struct chachapoly_ctx *ctx = crypto_aead_ctx(crypto_aead_reqtfm(req));
+       struct chachapoly_req_ctx *rctx = aead_request_ctx(req);
+       struct poly_req *preq = &rctx->u.poly;
+       struct scatterlist *crypt = req->src;
+       int err;
+
+       if (rctx->cryptlen == req->cryptlen) /* encrypting */
+               crypt = req->dst;
+
+       ahash_request_set_callback(&preq->req, aead_request_flags(req),
+                                  poly_cipher_done, req);
+       ahash_request_set_tfm(&preq->req, ctx->poly);
+       ahash_request_set_crypt(&preq->req, crypt, NULL, rctx->cryptlen);
+
+       err = crypto_ahash_update(&preq->req);
+       if (err)
+               return err;
+
+       return poly_cipherpad(req);
+}
+
+static void poly_adpad_done(struct crypto_async_request *areq, int err)
+{
+       async_done_continue(areq->data, err, poly_cipher);
+}
+
+static int poly_adpad(struct aead_request *req)
+{
+       struct chachapoly_ctx *ctx = crypto_aead_ctx(crypto_aead_reqtfm(req));
+       struct chachapoly_req_ctx *rctx = aead_request_ctx(req);
+       struct poly_req *preq = &rctx->u.poly;
+       unsigned int padlen, bs = POLY1305_BLOCK_SIZE;
+       int err;
+
+       padlen = (bs - (req->assoclen % bs)) % bs;
+       memset(preq->pad, 0, sizeof(preq->pad));
+       sg_init_table(preq->src, 1);
+       sg_set_buf(preq->src, preq->pad, padlen);
+
+       ahash_request_set_callback(&preq->req, aead_request_flags(req),
+                                  poly_adpad_done, req);
+       ahash_request_set_tfm(&preq->req, ctx->poly);
+       ahash_request_set_crypt(&preq->req, preq->src, NULL, padlen);
+
+       err = crypto_ahash_update(&preq->req);
+       if (err)
+               return err;
+
+       return poly_cipher(req);
+}
+
+static void poly_ad_done(struct crypto_async_request *areq, int err)
+{
+       async_done_continue(areq->data, err, poly_adpad);
+}
+
+static int poly_ad(struct aead_request *req)
+{
+       struct chachapoly_ctx *ctx = crypto_aead_ctx(crypto_aead_reqtfm(req));
+       struct chachapoly_req_ctx *rctx = aead_request_ctx(req);
+       struct poly_req *preq = &rctx->u.poly;
+       int err;
+
+       ahash_request_set_callback(&preq->req, aead_request_flags(req),
+                                  poly_ad_done, req);
+       ahash_request_set_tfm(&preq->req, ctx->poly);
+       ahash_request_set_crypt(&preq->req, req->assoc, NULL, req->assoclen);
+
+       err = crypto_ahash_update(&preq->req);
+       if (err)
+               return err;
+
+       return poly_adpad(req);
+}
+
+static void poly_setkey_done(struct crypto_async_request *areq, int err)
+{
+       async_done_continue(areq->data, err, poly_ad);
+}
+
+static int poly_setkey(struct aead_request *req)
+{
+       struct chachapoly_ctx *ctx = crypto_aead_ctx(crypto_aead_reqtfm(req));
+       struct chachapoly_req_ctx *rctx = aead_request_ctx(req);
+       struct poly_req *preq = &rctx->u.poly;
+       int err;
+
+       sg_init_table(preq->src, 1);
+       sg_set_buf(preq->src, rctx->key, sizeof(rctx->key));
+
+       ahash_request_set_callback(&preq->req, aead_request_flags(req),
+                                  poly_setkey_done, req);
+       ahash_request_set_tfm(&preq->req, ctx->poly);
+       ahash_request_set_crypt(&preq->req, preq->src, NULL, sizeof(rctx->key));
+
+       err = crypto_ahash_update(&preq->req);
+       if (err)
+               return err;
+
+       return poly_ad(req);
+}
+
+static void poly_init_done(struct crypto_async_request *areq, int err)
+{
+       async_done_continue(areq->data, err, poly_setkey);
+}
+
+static int poly_init(struct aead_request *req)
+{
+       struct chachapoly_ctx *ctx = crypto_aead_ctx(crypto_aead_reqtfm(req));
+       struct chachapoly_req_ctx *rctx = aead_request_ctx(req);
+       struct poly_req *preq = &rctx->u.poly;
+       int err;
+
+       ahash_request_set_callback(&preq->req, aead_request_flags(req),
+                                  poly_init_done, req);
+       ahash_request_set_tfm(&preq->req, ctx->poly);
+
+       err = crypto_ahash_init(&preq->req);
+       if (err)
+               return err;
+
+       return poly_setkey(req);
+}
+
+static void poly_genkey_done(struct crypto_async_request *areq, int err)
+{
+       async_done_continue(areq->data, err, poly_init);
+}
+
+static int poly_genkey(struct aead_request *req)
+{
+       struct chachapoly_ctx *ctx = crypto_aead_ctx(crypto_aead_reqtfm(req));
+       struct chachapoly_req_ctx *rctx = aead_request_ctx(req);
+       struct chacha_req *creq = &rctx->u.chacha;
+       int err;
+
+       sg_init_table(creq->src, 1);
+       memset(rctx->key, 0, sizeof(rctx->key));
+       sg_set_buf(creq->src, rctx->key, sizeof(rctx->key));
+
+       chacha_iv(creq->iv, req, 0);
+
+       ablkcipher_request_set_callback(&creq->req, aead_request_flags(req),
+                                       poly_genkey_done, req);
+       ablkcipher_request_set_tfm(&creq->req, ctx->chacha);
+       ablkcipher_request_set_crypt(&creq->req, creq->src, creq->src,
+                                    POLY1305_KEY_SIZE, creq->iv);
+
+       err = crypto_ablkcipher_decrypt(&creq->req);
+       if (err)
+               return err;
+
+       return poly_init(req);
+}
+
+static void chacha_encrypt_done(struct crypto_async_request *areq, int err)
+{
+       async_done_continue(areq->data, err, poly_genkey);
+}
+
+static int chacha_encrypt(struct aead_request *req)
+{
+       struct chachapoly_ctx *ctx = crypto_aead_ctx(crypto_aead_reqtfm(req));
+       struct chachapoly_req_ctx *rctx = aead_request_ctx(req);
+       struct chacha_req *creq = &rctx->u.chacha;
+       int err;
+
+       chacha_iv(creq->iv, req, 1);
+
+       ablkcipher_request_set_callback(&creq->req, aead_request_flags(req),
+                                       chacha_encrypt_done, req);
+       ablkcipher_request_set_tfm(&creq->req, ctx->chacha);
+       ablkcipher_request_set_crypt(&creq->req, req->src, req->dst,
+                                    req->cryptlen, creq->iv);
+       err = crypto_ablkcipher_encrypt(&creq->req);
+       if (err)
+               return err;
+
+       return poly_genkey(req);
+}
+
+static int chachapoly_encrypt(struct aead_request *req)
+{
+       struct chachapoly_req_ctx *rctx = aead_request_ctx(req);
+
+       rctx->cryptlen = req->cryptlen;
+
+       /* encrypt call chain:
+        * - chacha_encrypt/done()
+        * - poly_genkey/done()
+        * - poly_init/done()
+        * - poly_setkey/done()
+        * - poly_ad/done()
+        * - poly_adpad/done()
+        * - poly_cipher/done()
+        * - poly_cipherpad/done()
+        * - poly_tail/done/continue()
+        * - poly_copy_tag()
+        */
+       return chacha_encrypt(req);
+}
+
+static int chachapoly_decrypt(struct aead_request *req)
+{
+       struct chachapoly_req_ctx *rctx = aead_request_ctx(req);
+
+       if (req->cryptlen < POLY1305_DIGEST_SIZE)
+               return -EINVAL;
+       rctx->cryptlen = req->cryptlen - POLY1305_DIGEST_SIZE;
+
+       /* decrypt call chain:
+        * - poly_genkey/done()
+        * - poly_init/done()
+        * - poly_setkey/done()
+        * - poly_ad/done()
+        * - poly_adpad/done()
+        * - poly_cipher/done()
+        * - poly_cipherpad/done()
+        * - poly_tail/done/continue()
+        * - chacha_decrypt/done()
+        * - poly_verify_tag()
+        */
+       return poly_genkey(req);
+}
+
+static int chachapoly_setkey(struct crypto_aead *aead, const u8 *key,
+                            unsigned int keylen)
+{
+       struct chachapoly_ctx *ctx = crypto_aead_ctx(aead);
+       int err;
+
+       if (keylen != ctx->saltlen + CHACHA20_KEY_SIZE)
+               return -EINVAL;
+
+       keylen -= ctx->saltlen;
+       memcpy(ctx->salt, key + keylen, ctx->saltlen);
+
+       crypto_ablkcipher_clear_flags(ctx->chacha, CRYPTO_TFM_REQ_MASK);
+       crypto_ablkcipher_set_flags(ctx->chacha, crypto_aead_get_flags(aead) &
+                                   CRYPTO_TFM_REQ_MASK);
+
+       err = crypto_ablkcipher_setkey(ctx->chacha, key, keylen);
+       crypto_aead_set_flags(aead, crypto_ablkcipher_get_flags(ctx->chacha) &
+                             CRYPTO_TFM_RES_MASK);
+       return err;
+}
+
+static int chachapoly_setauthsize(struct crypto_aead *tfm,
+                                 unsigned int authsize)
+{
+       if (authsize != POLY1305_DIGEST_SIZE)
+               return -EINVAL;
+
+       return 0;
+}
+
+static int chachapoly_init(struct crypto_tfm *tfm)
+{
+       struct crypto_instance *inst = (void *)tfm->__crt_alg;
+       struct chachapoly_instance_ctx *ictx = crypto_instance_ctx(inst);
+       struct chachapoly_ctx *ctx = crypto_tfm_ctx(tfm);
+       struct crypto_ablkcipher *chacha;
+       struct crypto_ahash *poly;
+       unsigned long align;
+
+       poly = crypto_spawn_ahash(&ictx->poly);
+       if (IS_ERR(poly))
+               return PTR_ERR(poly);
+
+       chacha = crypto_spawn_skcipher(&ictx->chacha);
+       if (IS_ERR(chacha)) {
+               crypto_free_ahash(poly);
+               return PTR_ERR(chacha);
+       }
+
+       ctx->chacha = chacha;
+       ctx->poly = poly;
+       ctx->saltlen = ictx->saltlen;
+
+       align = crypto_tfm_alg_alignmask(tfm);
+       align &= ~(crypto_tfm_ctx_alignment() - 1);
+       crypto_aead_set_reqsize(__crypto_aead_cast(tfm),
+                               align + offsetof(struct chachapoly_req_ctx, u) +
+                               max(offsetof(struct chacha_req, req) +
+                                   sizeof(struct ablkcipher_request) +
+                                   crypto_ablkcipher_reqsize(chacha),
+                                   offsetof(struct poly_req, req) +
+                                   sizeof(struct ahash_request) +
+                                   crypto_ahash_reqsize(poly)));
+
+       return 0;
+}
+
+static void chachapoly_exit(struct crypto_tfm *tfm)
+{
+       struct chachapoly_ctx *ctx = crypto_tfm_ctx(tfm);
+
+       crypto_free_ahash(ctx->poly);
+       crypto_free_ablkcipher(ctx->chacha);
+}
+
+static struct crypto_instance *chachapoly_alloc(struct rtattr **tb,
+                                               const char *name,
+                                               unsigned int ivsize)
+{
+       struct crypto_attr_type *algt;
+       struct crypto_instance *inst;
+       struct crypto_alg *chacha;
+       struct crypto_alg *poly;
+       struct ahash_alg *poly_ahash;
+       struct chachapoly_instance_ctx *ctx;
+       const char *chacha_name, *poly_name;
+       int err;
+
+       if (ivsize > CHACHAPOLY_IV_SIZE)
+               return ERR_PTR(-EINVAL);
+
+       algt = crypto_get_attr_type(tb);
+       if (IS_ERR(algt))
+               return ERR_CAST(algt);
+
+       if ((algt->type ^ CRYPTO_ALG_TYPE_AEAD) & algt->mask)
+               return ERR_PTR(-EINVAL);
+
+       chacha_name = crypto_attr_alg_name(tb[1]);
+       if (IS_ERR(chacha_name))
+               return ERR_CAST(chacha_name);
+       poly_name = crypto_attr_alg_name(tb[2]);
+       if (IS_ERR(poly_name))
+               return ERR_CAST(poly_name);
+
+       poly = crypto_find_alg(poly_name, &crypto_ahash_type,
+                              CRYPTO_ALG_TYPE_HASH,
+                              CRYPTO_ALG_TYPE_AHASH_MASK);
+       if (IS_ERR(poly))
+               return ERR_CAST(poly);
+
+       err = -ENOMEM;
+       inst = kzalloc(sizeof(*inst) + sizeof(*ctx), GFP_KERNEL);
+       if (!inst)
+               goto out_put_poly;
+
+       ctx = crypto_instance_ctx(inst);
+       ctx->saltlen = CHACHAPOLY_IV_SIZE - ivsize;
+       poly_ahash = container_of(poly, struct ahash_alg, halg.base);
+       err = crypto_init_ahash_spawn(&ctx->poly, &poly_ahash->halg, inst);
+       if (err)
+               goto err_free_inst;
+
+       crypto_set_skcipher_spawn(&ctx->chacha, inst);
+       err = crypto_grab_skcipher(&ctx->chacha, chacha_name, 0,
+                                  crypto_requires_sync(algt->type,
+                                                       algt->mask));
+       if (err)
+               goto err_drop_poly;
+
+       chacha = crypto_skcipher_spawn_alg(&ctx->chacha);
+
+       err = -EINVAL;
+       /* Need 16-byte IV size, including Initial Block Counter value */
+       if (chacha->cra_ablkcipher.ivsize != CHACHA20_IV_SIZE)
+               goto out_drop_chacha;
+       /* Not a stream cipher? */
+       if (chacha->cra_blocksize != 1)
+               goto out_drop_chacha;
+
+       err = -ENAMETOOLONG;
+       if (snprintf(inst->alg.cra_name, CRYPTO_MAX_ALG_NAME,
+                    "%s(%s,%s)", name, chacha_name,
+                    poly_name) >= CRYPTO_MAX_ALG_NAME)
+               goto out_drop_chacha;
+       if (snprintf(inst->alg.cra_driver_name, CRYPTO_MAX_ALG_NAME,
+                    "%s(%s,%s)", name, chacha->cra_driver_name,
+                    poly->cra_driver_name) >= CRYPTO_MAX_ALG_NAME)
+               goto out_drop_chacha;
+
+       inst->alg.cra_flags = CRYPTO_ALG_TYPE_AEAD;
+       inst->alg.cra_flags |= (chacha->cra_flags |
+                               poly->cra_flags) & CRYPTO_ALG_ASYNC;
+       inst->alg.cra_priority = (chacha->cra_priority +
+                                 poly->cra_priority) / 2;
+       inst->alg.cra_blocksize = 1;
+       inst->alg.cra_alignmask = chacha->cra_alignmask | poly->cra_alignmask;
+       inst->alg.cra_type = &crypto_nivaead_type;
+       inst->alg.cra_aead.ivsize = ivsize;
+       inst->alg.cra_aead.maxauthsize = POLY1305_DIGEST_SIZE;
+       inst->alg.cra_ctxsize = sizeof(struct chachapoly_ctx) + ctx->saltlen;
+       inst->alg.cra_init = chachapoly_init;
+       inst->alg.cra_exit = chachapoly_exit;
+       inst->alg.cra_aead.encrypt = chachapoly_encrypt;
+       inst->alg.cra_aead.decrypt = chachapoly_decrypt;
+       inst->alg.cra_aead.setkey = chachapoly_setkey;
+       inst->alg.cra_aead.setauthsize = chachapoly_setauthsize;
+       inst->alg.cra_aead.geniv = "seqiv";
+
+out:
+       crypto_mod_put(poly);
+       return inst;
+
+out_drop_chacha:
+       crypto_drop_skcipher(&ctx->chacha);
+err_drop_poly:
+       crypto_drop_ahash(&ctx->poly);
+err_free_inst:
+       kfree(inst);
+out_put_poly:
+       inst = ERR_PTR(err);
+       goto out;
+}
+
+static struct crypto_instance *rfc7539_alloc(struct rtattr **tb)
+{
+       return chachapoly_alloc(tb, "rfc7539", 12);
+}
+
+static struct crypto_instance *rfc7539esp_alloc(struct rtattr **tb)
+{
+       return chachapoly_alloc(tb, "rfc7539esp", 8);
+}
+
+static void chachapoly_free(struct crypto_instance *inst)
+{
+       struct chachapoly_instance_ctx *ctx = crypto_instance_ctx(inst);
+
+       crypto_drop_skcipher(&ctx->chacha);
+       crypto_drop_ahash(&ctx->poly);
+       kfree(inst);
+}
+
+static struct crypto_template rfc7539_tmpl = {
+       .name = "rfc7539",
+       .alloc = rfc7539_alloc,
+       .free = chachapoly_free,
+       .module = THIS_MODULE,
+};
+
+static struct crypto_template rfc7539esp_tmpl = {
+       .name = "rfc7539esp",
+       .alloc = rfc7539esp_alloc,
+       .free = chachapoly_free,
+       .module = THIS_MODULE,
+};
+
+static int __init chacha20poly1305_module_init(void)
+{
+       int err;
+
+       err = crypto_register_template(&rfc7539_tmpl);
+       if (err)
+               return err;
+
+       err = crypto_register_template(&rfc7539esp_tmpl);
+       if (err)
+               crypto_unregister_template(&rfc7539_tmpl);
+
+       return err;
+}
+
+static void __exit chacha20poly1305_module_exit(void)
+{
+       crypto_unregister_template(&rfc7539esp_tmpl);
+       crypto_unregister_template(&rfc7539_tmpl);
+}
+
+module_init(chacha20poly1305_module_init);
+module_exit(chacha20poly1305_module_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Martin Willi <martin@strongswan.org>");
+MODULE_DESCRIPTION("ChaCha20-Poly1305 AEAD");
+MODULE_ALIAS_CRYPTO("chacha20poly1305");
+MODULE_ALIAS_CRYPTO("rfc7539");
+MODULE_ALIAS_CRYPTO("rfc7539esp");
index 63c17d5992f79b5a5ed40a526fa58a532eb3e5dc..b4340018c8d47527068a65142876ac86e8728701 100644 (file)
@@ -80,44 +80,37 @@ unlock:
        return err;
 }
 
-static int chainiv_givencrypt_first(struct skcipher_givcrypt_request *req)
+static int chainiv_init_common(struct crypto_tfm *tfm, char iv[])
 {
-       struct crypto_ablkcipher *geniv = skcipher_givcrypt_reqtfm(req);
-       struct chainiv_ctx *ctx = crypto_ablkcipher_ctx(geniv);
+       struct crypto_ablkcipher *geniv = __crypto_ablkcipher_cast(tfm);
        int err = 0;
 
-       spin_lock_bh(&ctx->lock);
-       if (crypto_ablkcipher_crt(geniv)->givencrypt !=
-           chainiv_givencrypt_first)
-               goto unlock;
-
-       crypto_ablkcipher_crt(geniv)->givencrypt = chainiv_givencrypt;
-       err = crypto_rng_get_bytes(crypto_default_rng, ctx->iv,
-                                  crypto_ablkcipher_ivsize(geniv));
-
-unlock:
-       spin_unlock_bh(&ctx->lock);
-
-       if (err)
-               return err;
-
-       return chainiv_givencrypt(req);
-}
-
-static int chainiv_init_common(struct crypto_tfm *tfm)
-{
        tfm->crt_ablkcipher.reqsize = sizeof(struct ablkcipher_request);
 
-       return skcipher_geniv_init(tfm);
+       if (iv) {
+               err = crypto_rng_get_bytes(crypto_default_rng, iv,
+                                          crypto_ablkcipher_ivsize(geniv));
+               crypto_put_default_rng();
+       }
+
+       return err ?: skcipher_geniv_init(tfm);
 }
 
 static int chainiv_init(struct crypto_tfm *tfm)
 {
+       struct crypto_ablkcipher *geniv = __crypto_ablkcipher_cast(tfm);
        struct chainiv_ctx *ctx = crypto_tfm_ctx(tfm);
+       char *iv;
 
        spin_lock_init(&ctx->lock);
 
-       return chainiv_init_common(tfm);
+       iv = NULL;
+       if (!crypto_get_default_rng()) {
+               crypto_ablkcipher_crt(geniv)->givencrypt = chainiv_givencrypt;
+               iv = ctx->iv;
+       }
+
+       return chainiv_init_common(tfm, iv);
 }
 
 static int async_chainiv_schedule_work(struct async_chainiv_ctx *ctx)
@@ -205,33 +198,6 @@ postpone:
        return async_chainiv_postpone_request(req);
 }
 
-static int async_chainiv_givencrypt_first(struct skcipher_givcrypt_request *req)
-{
-       struct crypto_ablkcipher *geniv = skcipher_givcrypt_reqtfm(req);
-       struct async_chainiv_ctx *ctx = crypto_ablkcipher_ctx(geniv);
-       int err = 0;
-
-       if (test_and_set_bit(CHAINIV_STATE_INUSE, &ctx->state))
-               goto out;
-
-       if (crypto_ablkcipher_crt(geniv)->givencrypt !=
-           async_chainiv_givencrypt_first)
-               goto unlock;
-
-       crypto_ablkcipher_crt(geniv)->givencrypt = async_chainiv_givencrypt;
-       err = crypto_rng_get_bytes(crypto_default_rng, ctx->iv,
-                                  crypto_ablkcipher_ivsize(geniv));
-
-unlock:
-       clear_bit(CHAINIV_STATE_INUSE, &ctx->state);
-
-       if (err)
-               return err;
-
-out:
-       return async_chainiv_givencrypt(req);
-}
-
 static void async_chainiv_do_postponed(struct work_struct *work)
 {
        struct async_chainiv_ctx *ctx = container_of(work,
@@ -263,14 +229,23 @@ static void async_chainiv_do_postponed(struct work_struct *work)
 
 static int async_chainiv_init(struct crypto_tfm *tfm)
 {
+       struct crypto_ablkcipher *geniv = __crypto_ablkcipher_cast(tfm);
        struct async_chainiv_ctx *ctx = crypto_tfm_ctx(tfm);
+       char *iv;
 
        spin_lock_init(&ctx->lock);
 
        crypto_init_queue(&ctx->queue, 100);
        INIT_WORK(&ctx->postponed, async_chainiv_do_postponed);
 
-       return chainiv_init_common(tfm);
+       iv = NULL;
+       if (!crypto_get_default_rng()) {
+               crypto_ablkcipher_crt(geniv)->givencrypt =
+                       async_chainiv_givencrypt;
+               iv = ctx->iv;
+       }
+
+       return chainiv_init_common(tfm, iv);
 }
 
 static void async_chainiv_exit(struct crypto_tfm *tfm)
@@ -288,21 +263,14 @@ static struct crypto_instance *chainiv_alloc(struct rtattr **tb)
 {
        struct crypto_attr_type *algt;
        struct crypto_instance *inst;
-       int err;
 
        algt = crypto_get_attr_type(tb);
        if (IS_ERR(algt))
                return ERR_CAST(algt);
 
-       err = crypto_get_default_rng();
-       if (err)
-               return ERR_PTR(err);
-
        inst = skcipher_geniv_alloc(&chainiv_tmpl, tb, 0, 0);
        if (IS_ERR(inst))
-               goto put_rng;
-
-       inst->alg.cra_ablkcipher.givencrypt = chainiv_givencrypt_first;
+               goto out;
 
        inst->alg.cra_init = chainiv_init;
        inst->alg.cra_exit = skcipher_geniv_exit;
@@ -312,9 +280,6 @@ static struct crypto_instance *chainiv_alloc(struct rtattr **tb)
        if (!crypto_requires_sync(algt->type, algt->mask)) {
                inst->alg.cra_flags |= CRYPTO_ALG_ASYNC;
 
-               inst->alg.cra_ablkcipher.givencrypt =
-                       async_chainiv_givencrypt_first;
-
                inst->alg.cra_init = async_chainiv_init;
                inst->alg.cra_exit = async_chainiv_exit;
 
@@ -325,22 +290,12 @@ static struct crypto_instance *chainiv_alloc(struct rtattr **tb)
 
 out:
        return inst;
-
-put_rng:
-       crypto_put_default_rng();
-       goto out;
-}
-
-static void chainiv_free(struct crypto_instance *inst)
-{
-       skcipher_geniv_free(inst);
-       crypto_put_default_rng();
 }
 
 static struct crypto_template chainiv_tmpl = {
        .name = "chainiv",
        .alloc = chainiv_alloc,
-       .free = chainiv_free,
+       .free = skcipher_geniv_free,
        .module = THIS_MODULE,
 };
 
index b0602ba03111230f42b34ca7c6d169a54d7c24ef..22ba81f76764aff2edd614ad8fad2914df33a8a9 100644 (file)
@@ -295,6 +295,23 @@ static void cryptd_blkcipher_exit_tfm(struct crypto_tfm *tfm)
        crypto_free_blkcipher(ctx->child);
 }
 
+static int cryptd_init_instance(struct crypto_instance *inst,
+                               struct crypto_alg *alg)
+{
+       if (snprintf(inst->alg.cra_driver_name, CRYPTO_MAX_ALG_NAME,
+                    "cryptd(%s)",
+                    alg->cra_driver_name) >= CRYPTO_MAX_ALG_NAME)
+               return -ENAMETOOLONG;
+
+       memcpy(inst->alg.cra_name, alg->cra_name, CRYPTO_MAX_ALG_NAME);
+
+       inst->alg.cra_priority = alg->cra_priority + 50;
+       inst->alg.cra_blocksize = alg->cra_blocksize;
+       inst->alg.cra_alignmask = alg->cra_alignmask;
+
+       return 0;
+}
+
 static void *cryptd_alloc_instance(struct crypto_alg *alg, unsigned int head,
                                   unsigned int tail)
 {
@@ -308,17 +325,10 @@ static void *cryptd_alloc_instance(struct crypto_alg *alg, unsigned int head,
 
        inst = (void *)(p + head);
 
-       err = -ENAMETOOLONG;
-       if (snprintf(inst->alg.cra_driver_name, CRYPTO_MAX_ALG_NAME,
-                    "cryptd(%s)", alg->cra_driver_name) >= CRYPTO_MAX_ALG_NAME)
+       err = cryptd_init_instance(inst, alg);
+       if (err)
                goto out_free_inst;
 
-       memcpy(inst->alg.cra_name, alg->cra_name, CRYPTO_MAX_ALG_NAME);
-
-       inst->alg.cra_priority = alg->cra_priority + 50;
-       inst->alg.cra_blocksize = alg->cra_blocksize;
-       inst->alg.cra_alignmask = alg->cra_alignmask;
-
 out:
        return p;
 
@@ -654,6 +664,24 @@ out_put_alg:
        return err;
 }
 
+static int cryptd_aead_setkey(struct crypto_aead *parent,
+                             const u8 *key, unsigned int keylen)
+{
+       struct cryptd_aead_ctx *ctx = crypto_aead_ctx(parent);
+       struct crypto_aead *child = ctx->child;
+
+       return crypto_aead_setkey(child, key, keylen);
+}
+
+static int cryptd_aead_setauthsize(struct crypto_aead *parent,
+                                  unsigned int authsize)
+{
+       struct cryptd_aead_ctx *ctx = crypto_aead_ctx(parent);
+       struct crypto_aead *child = ctx->child;
+
+       return crypto_aead_setauthsize(child, authsize);
+}
+
 static void cryptd_aead_crypt(struct aead_request *req,
                        struct crypto_aead *child,
                        int err,
@@ -715,27 +743,26 @@ static int cryptd_aead_decrypt_enqueue(struct aead_request *req)
        return cryptd_aead_enqueue(req, cryptd_aead_decrypt );
 }
 
-static int cryptd_aead_init_tfm(struct crypto_tfm *tfm)
+static int cryptd_aead_init_tfm(struct crypto_aead *tfm)
 {
-       struct crypto_instance *inst = crypto_tfm_alg_instance(tfm);
-       struct aead_instance_ctx *ictx = crypto_instance_ctx(inst);
+       struct aead_instance *inst = aead_alg_instance(tfm);
+       struct aead_instance_ctx *ictx = aead_instance_ctx(inst);
        struct crypto_aead_spawn *spawn = &ictx->aead_spawn;
-       struct cryptd_aead_ctx *ctx = crypto_tfm_ctx(tfm);
+       struct cryptd_aead_ctx *ctx = crypto_aead_ctx(tfm);
        struct crypto_aead *cipher;
 
        cipher = crypto_spawn_aead(spawn);
        if (IS_ERR(cipher))
                return PTR_ERR(cipher);
 
-       crypto_aead_set_flags(cipher, CRYPTO_TFM_REQ_MAY_SLEEP);
        ctx->child = cipher;
-       tfm->crt_aead.reqsize = sizeof(struct cryptd_aead_request_ctx);
+       crypto_aead_set_reqsize(tfm, sizeof(struct cryptd_aead_request_ctx));
        return 0;
 }
 
-static void cryptd_aead_exit_tfm(struct crypto_tfm *tfm)
+static void cryptd_aead_exit_tfm(struct crypto_aead *tfm)
 {
-       struct cryptd_aead_ctx *ctx = crypto_tfm_ctx(tfm);
+       struct cryptd_aead_ctx *ctx = crypto_aead_ctx(tfm);
        crypto_free_aead(ctx->child);
 }
 
@@ -744,57 +771,57 @@ static int cryptd_create_aead(struct crypto_template *tmpl,
                              struct cryptd_queue *queue)
 {
        struct aead_instance_ctx *ctx;
-       struct crypto_instance *inst;
-       struct crypto_alg *alg;
-       u32 type = CRYPTO_ALG_TYPE_AEAD;
-       u32 mask = CRYPTO_ALG_TYPE_MASK;
+       struct aead_instance *inst;
+       struct aead_alg *alg;
+       const char *name;
+       u32 type = 0;
+       u32 mask = 0;
        int err;
 
        cryptd_check_internal(tb, &type, &mask);
 
-       alg = crypto_get_attr_alg(tb, type, mask);
-        if (IS_ERR(alg))
-               return PTR_ERR(alg);
+       name = crypto_attr_alg_name(tb[1]);
+       if (IS_ERR(name))
+               return PTR_ERR(name);
 
-       inst = cryptd_alloc_instance(alg, 0, sizeof(*ctx));
-       err = PTR_ERR(inst);
-       if (IS_ERR(inst))
-               goto out_put_alg;
+       inst = kzalloc(sizeof(*inst) + sizeof(*ctx), GFP_KERNEL);
+       if (!inst)
+               return -ENOMEM;
 
-       ctx = crypto_instance_ctx(inst);
+       ctx = aead_instance_ctx(inst);
        ctx->queue = queue;
 
-       err = crypto_init_spawn(&ctx->aead_spawn.base, alg, inst,
-                       CRYPTO_ALG_TYPE_MASK | CRYPTO_ALG_ASYNC);
+       crypto_set_aead_spawn(&ctx->aead_spawn, aead_crypto_instance(inst));
+       err = crypto_grab_aead(&ctx->aead_spawn, name, type, mask);
        if (err)
                goto out_free_inst;
 
-       type = CRYPTO_ALG_TYPE_AEAD | CRYPTO_ALG_ASYNC;
-       if (alg->cra_flags & CRYPTO_ALG_INTERNAL)
-               type |= CRYPTO_ALG_INTERNAL;
-       inst->alg.cra_flags = type;
-       inst->alg.cra_type = alg->cra_type;
-       inst->alg.cra_ctxsize = sizeof(struct cryptd_aead_ctx);
-       inst->alg.cra_init = cryptd_aead_init_tfm;
-       inst->alg.cra_exit = cryptd_aead_exit_tfm;
-       inst->alg.cra_aead.setkey      = alg->cra_aead.setkey;
-       inst->alg.cra_aead.setauthsize = alg->cra_aead.setauthsize;
-       inst->alg.cra_aead.geniv       = alg->cra_aead.geniv;
-       inst->alg.cra_aead.ivsize      = alg->cra_aead.ivsize;
-       inst->alg.cra_aead.maxauthsize = alg->cra_aead.maxauthsize;
-       inst->alg.cra_aead.encrypt     = cryptd_aead_encrypt_enqueue;
-       inst->alg.cra_aead.decrypt     = cryptd_aead_decrypt_enqueue;
-       inst->alg.cra_aead.givencrypt  = alg->cra_aead.givencrypt;
-       inst->alg.cra_aead.givdecrypt  = alg->cra_aead.givdecrypt;
+       alg = crypto_spawn_aead_alg(&ctx->aead_spawn);
+       err = cryptd_init_instance(aead_crypto_instance(inst), &alg->base);
+       if (err)
+               goto out_drop_aead;
 
-       err = crypto_register_instance(tmpl, inst);
+       inst->alg.base.cra_flags = CRYPTO_ALG_ASYNC |
+                                  (alg->base.cra_flags & CRYPTO_ALG_INTERNAL);
+       inst->alg.base.cra_ctxsize = sizeof(struct cryptd_aead_ctx);
+
+       inst->alg.ivsize = crypto_aead_alg_ivsize(alg);
+       inst->alg.maxauthsize = crypto_aead_alg_maxauthsize(alg);
+
+       inst->alg.init = cryptd_aead_init_tfm;
+       inst->alg.exit = cryptd_aead_exit_tfm;
+       inst->alg.setkey = cryptd_aead_setkey;
+       inst->alg.setauthsize = cryptd_aead_setauthsize;
+       inst->alg.encrypt = cryptd_aead_encrypt_enqueue;
+       inst->alg.decrypt = cryptd_aead_decrypt_enqueue;
+
+       err = aead_register_instance(tmpl, inst);
        if (err) {
-               crypto_drop_spawn(&ctx->aead_spawn.base);
+out_drop_aead:
+               crypto_drop_aead(&ctx->aead_spawn);
 out_free_inst:
                kfree(inst);
        }
-out_put_alg:
-       crypto_mod_put(alg);
        return err;
 }
 
@@ -832,8 +859,8 @@ static void cryptd_free(struct crypto_instance *inst)
                kfree(ahash_instance(inst));
                return;
        case CRYPTO_ALG_TYPE_AEAD:
-               crypto_drop_spawn(&aead_ctx->aead_spawn.base);
-               kfree(inst);
+               crypto_drop_aead(&aead_ctx->aead_spawn);
+               kfree(aead_instance(inst));
                return;
        default:
                crypto_drop_spawn(&ctx->spawn);
index a20319132e338e7a8e606f9f8d04b3d310741857..941c9a434d50f9480e0a8dc456b87e80c3a80ff7 100644 (file)
 #include <linux/mm.h>
 #include <linux/string.h>
 
+static DEFINE_MUTEX(crypto_default_null_skcipher_lock);
+static struct crypto_blkcipher *crypto_default_null_skcipher;
+static int crypto_default_null_skcipher_refcnt;
+
 static int null_compress(struct crypto_tfm *tfm, const u8 *src,
                         unsigned int slen, u8 *dst, unsigned int *dlen)
 {
@@ -149,6 +153,41 @@ MODULE_ALIAS_CRYPTO("compress_null");
 MODULE_ALIAS_CRYPTO("digest_null");
 MODULE_ALIAS_CRYPTO("cipher_null");
 
+struct crypto_blkcipher *crypto_get_default_null_skcipher(void)
+{
+       struct crypto_blkcipher *tfm;
+
+       mutex_lock(&crypto_default_null_skcipher_lock);
+       tfm = crypto_default_null_skcipher;
+
+       if (!tfm) {
+               tfm = crypto_alloc_blkcipher("ecb(cipher_null)", 0, 0);
+               if (IS_ERR(tfm))
+                       goto unlock;
+
+               crypto_default_null_skcipher = tfm;
+       }
+
+       crypto_default_null_skcipher_refcnt++;
+
+unlock:
+       mutex_unlock(&crypto_default_null_skcipher_lock);
+
+       return tfm;
+}
+EXPORT_SYMBOL_GPL(crypto_get_default_null_skcipher);
+
+void crypto_put_default_null_skcipher(void)
+{
+       mutex_lock(&crypto_default_null_skcipher_lock);
+       if (!--crypto_default_null_skcipher_refcnt) {
+               crypto_free_blkcipher(crypto_default_null_skcipher);
+               crypto_default_null_skcipher = NULL;
+       }
+       mutex_unlock(&crypto_default_null_skcipher_lock);
+}
+EXPORT_SYMBOL_GPL(crypto_put_default_null_skcipher);
+
 static int __init crypto_null_mod_init(void)
 {
        int ret = 0;
index 41dfe762b7fbabd670522b8a408619a6c14cdb53..08ea2867fc8a370d9eb0f1cd0365783f5a5e4f2c 100644 (file)
@@ -27,6 +27,8 @@
 #include <net/net_namespace.h>
 #include <crypto/internal/aead.h>
 #include <crypto/internal/skcipher.h>
+#include <crypto/internal/rng.h>
+#include <crypto/akcipher.h>
 
 #include "internal.h"
 
@@ -110,6 +112,21 @@ nla_put_failure:
        return -EMSGSIZE;
 }
 
+static int crypto_report_akcipher(struct sk_buff *skb, struct crypto_alg *alg)
+{
+       struct crypto_report_akcipher rakcipher;
+
+       strncpy(rakcipher.type, "akcipher", sizeof(rakcipher.type));
+
+       if (nla_put(skb, CRYPTOCFGA_REPORT_AKCIPHER,
+                   sizeof(struct crypto_report_akcipher), &rakcipher))
+               goto nla_put_failure;
+       return 0;
+
+nla_put_failure:
+       return -EMSGSIZE;
+}
+
 static int crypto_report_one(struct crypto_alg *alg,
                             struct crypto_user_alg *ualg, struct sk_buff *skb)
 {
@@ -154,6 +171,12 @@ static int crypto_report_one(struct crypto_alg *alg,
                        goto nla_put_failure;
 
                break;
+
+       case CRYPTO_ALG_TYPE_AKCIPHER:
+               if (crypto_report_akcipher(skb, alg))
+                       goto nla_put_failure;
+
+               break;
        }
 
 out:
@@ -450,13 +473,21 @@ static int crypto_add_alg(struct sk_buff *skb, struct nlmsghdr *nlh,
        return 0;
 }
 
+static int crypto_del_rng(struct sk_buff *skb, struct nlmsghdr *nlh,
+                         struct nlattr **attrs)
+{
+       if (!netlink_capable(skb, CAP_NET_ADMIN))
+               return -EPERM;
+       return crypto_del_default_rng();
+}
+
 #define MSGSIZE(type) sizeof(struct type)
 
 static const int crypto_msg_min[CRYPTO_NR_MSGTYPES] = {
        [CRYPTO_MSG_NEWALG      - CRYPTO_MSG_BASE] = MSGSIZE(crypto_user_alg),
        [CRYPTO_MSG_DELALG      - CRYPTO_MSG_BASE] = MSGSIZE(crypto_user_alg),
        [CRYPTO_MSG_UPDATEALG   - CRYPTO_MSG_BASE] = MSGSIZE(crypto_user_alg),
-       [CRYPTO_MSG_GETALG      - CRYPTO_MSG_BASE] = MSGSIZE(crypto_user_alg),
+       [CRYPTO_MSG_DELRNG      - CRYPTO_MSG_BASE] = 0,
 };
 
 static const struct nla_policy crypto_policy[CRYPTOCFGA_MAX+1] = {
@@ -476,6 +507,7 @@ static const struct crypto_link {
        [CRYPTO_MSG_GETALG      - CRYPTO_MSG_BASE] = { .doit = crypto_report,
                                                       .dump = crypto_dump_report,
                                                       .done = crypto_dump_report_done},
+       [CRYPTO_MSG_DELRNG      - CRYPTO_MSG_BASE] = { .doit = crypto_del_rng },
 };
 
 static int crypto_user_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
index b69409cb7e6ac70b6d9ae4c19c469ad7abb684e6..a7c23146b87fe43bb4d84438e240e20caae27f17 100644 (file)
@@ -98,6 +98,7 @@
  */
 
 #include <crypto/drbg.h>
+#include <linux/kernel.h>
 
 /***************************************************************
  * Backend cipher definitions available to DRBG
@@ -190,6 +191,8 @@ static const struct drbg_core drbg_cores[] = {
 #endif /* CONFIG_CRYPTO_DRBG_HMAC */
 };
 
+static int drbg_uninstantiate(struct drbg_state *drbg);
+
 /******************************************************************
  * Generic helper functions
  ******************************************************************/
@@ -235,7 +238,7 @@ static bool drbg_fips_continuous_test(struct drbg_state *drbg,
 #ifdef CONFIG_CRYPTO_FIPS
        int ret = 0;
        /* skip test if we test the overall system */
-       if (drbg->test_data)
+       if (list_empty(&drbg->test_data.list))
                return true;
        /* only perform test in FIPS mode */
        if (0 == fips_enabled)
@@ -487,7 +490,7 @@ static int drbg_ctr_df(struct drbg_state *drbg,
 
 out:
        memset(iv, 0, drbg_blocklen(drbg));
-       memset(temp, 0, drbg_statelen(drbg));
+       memset(temp, 0, drbg_statelen(drbg) + drbg_blocklen(drbg));
        memset(pad, 0, drbg_blocklen(drbg));
        return ret;
 }
@@ -1041,6 +1044,58 @@ static struct drbg_state_ops drbg_hash_ops = {
  * Functions common for DRBG implementations
  ******************************************************************/
 
+static inline int __drbg_seed(struct drbg_state *drbg, struct list_head *seed,
+                             int reseed)
+{
+       int ret = drbg->d_ops->update(drbg, seed, reseed);
+
+       if (ret)
+               return ret;
+
+       drbg->seeded = true;
+       /* 10.1.1.2 / 10.1.1.3 step 5 */
+       drbg->reseed_ctr = 1;
+
+       return ret;
+}
+
+static void drbg_async_seed(struct work_struct *work)
+{
+       struct drbg_string data;
+       LIST_HEAD(seedlist);
+       struct drbg_state *drbg = container_of(work, struct drbg_state,
+                                              seed_work);
+       unsigned int entropylen = drbg_sec_strength(drbg->core->flags);
+       unsigned char entropy[32];
+
+       BUG_ON(!entropylen);
+       BUG_ON(entropylen > sizeof(entropy));
+       get_random_bytes(entropy, entropylen);
+
+       drbg_string_fill(&data, entropy, entropylen);
+       list_add_tail(&data.list, &seedlist);
+
+       mutex_lock(&drbg->drbg_mutex);
+
+       /* If nonblocking pool is initialized, deactivate Jitter RNG */
+       crypto_free_rng(drbg->jent);
+       drbg->jent = NULL;
+
+       /* Set seeded to false so that if __drbg_seed fails the
+        * next generate call will trigger a reseed.
+        */
+       drbg->seeded = false;
+
+       __drbg_seed(drbg, &seedlist, true);
+
+       if (drbg->seeded)
+               drbg->reseed_threshold = drbg_max_requests(drbg);
+
+       mutex_unlock(&drbg->drbg_mutex);
+
+       memzero_explicit(entropy, entropylen);
+}
+
 /*
  * Seeding or reseeding of the DRBG
  *
@@ -1055,9 +1110,9 @@ static struct drbg_state_ops drbg_hash_ops = {
 static int drbg_seed(struct drbg_state *drbg, struct drbg_string *pers,
                     bool reseed)
 {
-       int ret = 0;
-       unsigned char *entropy = NULL;
-       size_t entropylen = 0;
+       int ret;
+       unsigned char entropy[((32 + 16) * 2)];
+       unsigned int entropylen = drbg_sec_strength(drbg->core->flags);
        struct drbg_string data1;
        LIST_HEAD(seedlist);
 
@@ -1068,31 +1123,45 @@ static int drbg_seed(struct drbg_state *drbg, struct drbg_string *pers,
                return -EINVAL;
        }
 
-       if (drbg->test_data && drbg->test_data->testentropy) {
-               drbg_string_fill(&data1, drbg->test_data->testentropy->buf,
-                                drbg->test_data->testentropy->len);
+       if (list_empty(&drbg->test_data.list)) {
+               drbg_string_fill(&data1, drbg->test_data.buf,
+                                drbg->test_data.len);
                pr_devel("DRBG: using test entropy\n");
        } else {
                /*
                 * Gather entropy equal to the security strength of the DRBG.
                 * With a derivation function, a nonce is required in addition
                 * to the entropy. A nonce must be at least 1/2 of the security
-                * strength of the DRBG in size. Thus, entropy * nonce is 3/2
+                * strength of the DRBG in size. Thus, entropy + nonce is 3/2
                 * of the strength. The consideration of a nonce is only
                 * applicable during initial seeding.
                 */
-               entropylen = drbg_sec_strength(drbg->core->flags);
-               if (!entropylen)
-                       return -EFAULT;
+               BUG_ON(!entropylen);
                if (!reseed)
                        entropylen = ((entropylen + 1) / 2) * 3;
-               pr_devel("DRBG: (re)seeding with %zu bytes of entropy\n",
-                        entropylen);
-               entropy = kzalloc(entropylen, GFP_KERNEL);
-               if (!entropy)
-                       return -ENOMEM;
+               BUG_ON((entropylen * 2) > sizeof(entropy));
+
+               /* Get seed from in-kernel /dev/urandom */
                get_random_bytes(entropy, entropylen);
-               drbg_string_fill(&data1, entropy, entropylen);
+
+               if (!drbg->jent) {
+                       drbg_string_fill(&data1, entropy, entropylen);
+                       pr_devel("DRBG: (re)seeding with %u bytes of entropy\n",
+                                entropylen);
+               } else {
+                       /* Get seed from Jitter RNG */
+                       ret = crypto_rng_get_bytes(drbg->jent,
+                                                  entropy + entropylen,
+                                                  entropylen);
+                       if (ret) {
+                               pr_devel("DRBG: jent failed with %d\n", ret);
+                               return ret;
+                       }
+
+                       drbg_string_fill(&data1, entropy, entropylen * 2);
+                       pr_devel("DRBG: (re)seeding with %u bytes of entropy\n",
+                                entropylen * 2);
+               }
        }
        list_add_tail(&data1.list, &seedlist);
 
@@ -1111,16 +1180,10 @@ static int drbg_seed(struct drbg_state *drbg, struct drbg_string *pers,
                memset(drbg->C, 0, drbg_statelen(drbg));
        }
 
-       ret = drbg->d_ops->update(drbg, &seedlist, reseed);
-       if (ret)
-               goto out;
+       ret = __drbg_seed(drbg, &seedlist, reseed);
 
-       drbg->seeded = true;
-       /* 10.1.1.2 / 10.1.1.3 step 5 */
-       drbg->reseed_ctr = 1;
+       memzero_explicit(entropy, entropylen * 2);
 
-out:
-       kzfree(entropy);
        return ret;
 }
 
@@ -1136,6 +1199,8 @@ static inline void drbg_dealloc_state(struct drbg_state *drbg)
        kzfree(drbg->scratchpad);
        drbg->scratchpad = NULL;
        drbg->reseed_ctr = 0;
+       drbg->d_ops = NULL;
+       drbg->core = NULL;
 #ifdef CONFIG_CRYPTO_FIPS
        kzfree(drbg->prev);
        drbg->prev = NULL;
@@ -1152,6 +1217,27 @@ static inline int drbg_alloc_state(struct drbg_state *drbg)
        int ret = -ENOMEM;
        unsigned int sb_size = 0;
 
+       switch (drbg->core->flags & DRBG_TYPE_MASK) {
+#ifdef CONFIG_CRYPTO_DRBG_HMAC
+       case DRBG_HMAC:
+               drbg->d_ops = &drbg_hmac_ops;
+               break;
+#endif /* CONFIG_CRYPTO_DRBG_HMAC */
+#ifdef CONFIG_CRYPTO_DRBG_HASH
+       case DRBG_HASH:
+               drbg->d_ops = &drbg_hash_ops;
+               break;
+#endif /* CONFIG_CRYPTO_DRBG_HASH */
+#ifdef CONFIG_CRYPTO_DRBG_CTR
+       case DRBG_CTR:
+               drbg->d_ops = &drbg_ctr_ops;
+               break;
+#endif /* CONFIG_CRYPTO_DRBG_CTR */
+       default:
+               ret = -EOPNOTSUPP;
+               goto err;
+       }
+
        drbg->V = kmalloc(drbg_statelen(drbg), GFP_KERNEL);
        if (!drbg->V)
                goto err;
@@ -1181,87 +1267,14 @@ static inline int drbg_alloc_state(struct drbg_state *drbg)
                if (!drbg->scratchpad)
                        goto err;
        }
-       spin_lock_init(&drbg->drbg_lock);
-       return 0;
-
-err:
-       drbg_dealloc_state(drbg);
-       return ret;
-}
-
-/*
- * Strategy to avoid holding long term locks: generate a shadow copy of DRBG
- * and perform all operations on this shadow copy. After finishing, restore
- * the updated state of the shadow copy into original drbg state. This way,
- * only the read and write operations of the original drbg state must be
- * locked
- */
-static inline void drbg_copy_drbg(struct drbg_state *src,
-                                 struct drbg_state *dst)
-{
-       if (!src || !dst)
-               return;
-       memcpy(dst->V, src->V, drbg_statelen(src));
-       memcpy(dst->C, src->C, drbg_statelen(src));
-       dst->reseed_ctr = src->reseed_ctr;
-       dst->seeded = src->seeded;
-       dst->pr = src->pr;
-#ifdef CONFIG_CRYPTO_FIPS
-       dst->fips_primed = src->fips_primed;
-       memcpy(dst->prev, src->prev, drbg_blocklen(src));
-#endif
-       /*
-        * Not copied:
-        * scratchpad is initialized drbg_alloc_state;
-        * priv_data is initialized with call to crypto_init;
-        * d_ops and core are set outside, as these parameters are const;
-        * test_data is set outside to prevent it being copied back.
-        */
-}
-
-static int drbg_make_shadow(struct drbg_state *drbg, struct drbg_state **shadow)
-{
-       int ret = -ENOMEM;
-       struct drbg_state *tmp = NULL;
-
-       tmp = kzalloc(sizeof(struct drbg_state), GFP_KERNEL);
-       if (!tmp)
-               return -ENOMEM;
-
-       /* read-only data as they are defined as const, no lock needed */
-       tmp->core = drbg->core;
-       tmp->d_ops = drbg->d_ops;
 
-       ret = drbg_alloc_state(tmp);
-       if (ret)
-               goto err;
-
-       spin_lock_bh(&drbg->drbg_lock);
-       drbg_copy_drbg(drbg, tmp);
-       /* only make a link to the test buffer, as we only read that data */
-       tmp->test_data = drbg->test_data;
-       spin_unlock_bh(&drbg->drbg_lock);
-       *shadow = tmp;
        return 0;
 
 err:
-       kzfree(tmp);
+       drbg_dealloc_state(drbg);
        return ret;
 }
 
-static void drbg_restore_shadow(struct drbg_state *drbg,
-                               struct drbg_state **shadow)
-{
-       struct drbg_state *tmp = *shadow;
-
-       spin_lock_bh(&drbg->drbg_lock);
-       drbg_copy_drbg(tmp, drbg);
-       spin_unlock_bh(&drbg->drbg_lock);
-       drbg_dealloc_state(tmp);
-       kzfree(tmp);
-       *shadow = NULL;
-}
-
 /*************************************************************************
  * DRBG interface functions
  *************************************************************************/
@@ -1287,14 +1300,12 @@ static int drbg_generate(struct drbg_state *drbg,
                         struct drbg_string *addtl)
 {
        int len = 0;
-       struct drbg_state *shadow = NULL;
        LIST_HEAD(addtllist);
-       struct drbg_string timestamp;
-       union {
-               cycles_t cycles;
-               unsigned char char_cycles[sizeof(cycles_t)];
-       } now;
 
+       if (!drbg->core) {
+               pr_devel("DRBG: not yet seeded\n");
+               return -EINVAL;
+       }
        if (0 == buflen || !buf) {
                pr_devel("DRBG: no output buffer provided\n");
                return -EINVAL;
@@ -1304,15 +1315,9 @@ static int drbg_generate(struct drbg_state *drbg,
                return -EINVAL;
        }
 
-       len = drbg_make_shadow(drbg, &shadow);
-       if (len) {
-               pr_devel("DRBG: shadow copy cannot be generated\n");
-               return len;
-       }
-
        /* 9.3.1 step 2 */
        len = -EINVAL;
-       if (buflen > (drbg_max_request_bytes(shadow))) {
+       if (buflen > (drbg_max_request_bytes(drbg))) {
                pr_devel("DRBG: requested random numbers too large %u\n",
                         buflen);
                goto err;
@@ -1321,7 +1326,7 @@ static int drbg_generate(struct drbg_state *drbg,
        /* 9.3.1 step 3 is implicit with the chosen DRBG */
 
        /* 9.3.1 step 4 */
-       if (addtl && addtl->len > (drbg_max_addtl(shadow))) {
+       if (addtl && addtl->len > (drbg_max_addtl(drbg))) {
                pr_devel("DRBG: additional information string too long %zu\n",
                         addtl->len);
                goto err;
@@ -1332,46 +1337,29 @@ static int drbg_generate(struct drbg_state *drbg,
         * 9.3.1 step 6 and 9 supplemented by 9.3.2 step c is implemented
         * here. The spec is a bit convoluted here, we make it simpler.
         */
-       if ((drbg_max_requests(shadow)) < shadow->reseed_ctr)
-               shadow->seeded = false;
+       if (drbg->reseed_threshold < drbg->reseed_ctr)
+               drbg->seeded = false;
 
-       /* allocate cipher handle */
-       len = shadow->d_ops->crypto_init(shadow);
-       if (len)
-               goto err;
-
-       if (shadow->pr || !shadow->seeded) {
+       if (drbg->pr || !drbg->seeded) {
                pr_devel("DRBG: reseeding before generation (prediction "
                         "resistance: %s, state %s)\n",
                         drbg->pr ? "true" : "false",
                         drbg->seeded ? "seeded" : "unseeded");
                /* 9.3.1 steps 7.1 through 7.3 */
-               len = drbg_seed(shadow, addtl, true);
+               len = drbg_seed(drbg, addtl, true);
                if (len)
                        goto err;
                /* 9.3.1 step 7.4 */
                addtl = NULL;
        }
 
-       /*
-        * Mix the time stamp into the DRBG state if the DRBG is not in
-        * test mode. If there are two callers invoking the DRBG at the same
-        * time, i.e. before the first caller merges its shadow state back,
-        * both callers would obtain the same random number stream without
-        * changing the state here.
-        */
-       if (!drbg->test_data) {
-               now.cycles = random_get_entropy();
-               drbg_string_fill(&timestamp, now.char_cycles, sizeof(cycles_t));
-               list_add_tail(&timestamp.list, &addtllist);
-       }
        if (addtl && 0 < addtl->len)
                list_add_tail(&addtl->list, &addtllist);
        /* 9.3.1 step 8 and 10 */
-       len = shadow->d_ops->generate(shadow, buf, buflen, &addtllist);
+       len = drbg->d_ops->generate(drbg, buf, buflen, &addtllist);
 
        /* 10.1.1.4 step 6, 10.1.2.5 step 7, 10.2.1.5.2 step 7 */
-       shadow->reseed_ctr++;
+       drbg->reseed_ctr++;
        if (0 >= len)
                goto err;
 
@@ -1391,7 +1379,7 @@ static int drbg_generate(struct drbg_state *drbg,
         * case somebody has a need to implement the test of 11.3.3.
         */
 #if 0
-       if (shadow->reseed_ctr && !(shadow->reseed_ctr % 4096)) {
+       if (drbg->reseed_ctr && !(drbg->reseed_ctr % 4096)) {
                int err = 0;
                pr_devel("DRBG: start to perform self test\n");
                if (drbg->core->flags & DRBG_HMAC)
@@ -1410,8 +1398,6 @@ static int drbg_generate(struct drbg_state *drbg,
                         * are returned when reusing this DRBG cipher handle
                         */
                        drbg_uninstantiate(drbg);
-                       drbg_dealloc_state(shadow);
-                       kzfree(shadow);
                        return 0;
                } else {
                        pr_devel("DRBG: self test successful\n");
@@ -1425,8 +1411,6 @@ static int drbg_generate(struct drbg_state *drbg,
         */
        len = 0;
 err:
-       shadow->d_ops->crypto_fini(shadow);
-       drbg_restore_shadow(drbg, &shadow);
        return len;
 }
 
@@ -1442,19 +1426,68 @@ static int drbg_generate_long(struct drbg_state *drbg,
                              unsigned char *buf, unsigned int buflen,
                              struct drbg_string *addtl)
 {
-       int len = 0;
+       unsigned int len = 0;
        unsigned int slice = 0;
        do {
-               int tmplen = 0;
+               int err = 0;
                unsigned int chunk = 0;
                slice = ((buflen - len) / drbg_max_request_bytes(drbg));
                chunk = slice ? drbg_max_request_bytes(drbg) : (buflen - len);
-               tmplen = drbg_generate(drbg, buf + len, chunk, addtl);
-               if (0 >= tmplen)
-                       return tmplen;
-               len += tmplen;
+               mutex_lock(&drbg->drbg_mutex);
+               err = drbg_generate(drbg, buf + len, chunk, addtl);
+               mutex_unlock(&drbg->drbg_mutex);
+               if (0 > err)
+                       return err;
+               len += chunk;
        } while (slice > 0 && (len < buflen));
-       return len;
+       return 0;
+}
+
+static void drbg_schedule_async_seed(struct random_ready_callback *rdy)
+{
+       struct drbg_state *drbg = container_of(rdy, struct drbg_state,
+                                              random_ready);
+
+       schedule_work(&drbg->seed_work);
+}
+
+static int drbg_prepare_hrng(struct drbg_state *drbg)
+{
+       int err;
+
+       /* We do not need an HRNG in test mode. */
+       if (list_empty(&drbg->test_data.list))
+               return 0;
+
+       INIT_WORK(&drbg->seed_work, drbg_async_seed);
+
+       drbg->random_ready.owner = THIS_MODULE;
+       drbg->random_ready.func = drbg_schedule_async_seed;
+
+       err = add_random_ready_callback(&drbg->random_ready);
+
+       switch (err) {
+       case 0:
+               break;
+
+       case -EALREADY:
+               err = 0;
+               /* fall through */
+
+       default:
+               drbg->random_ready.func = NULL;
+               return err;
+       }
+
+       drbg->jent = crypto_alloc_rng("jitterentropy_rng", 0, 0);
+
+       /*
+        * Require frequent reseeds until the seed source is fully
+        * initialized.
+        */
+       drbg->reseed_threshold = 50;
+
+       return err;
 }
 
 /*
@@ -1477,32 +1510,12 @@ static int drbg_generate_long(struct drbg_state *drbg,
 static int drbg_instantiate(struct drbg_state *drbg, struct drbg_string *pers,
                            int coreref, bool pr)
 {
-       int ret = -ENOMEM;
+       int ret;
+       bool reseed = true;
 
        pr_devel("DRBG: Initializing DRBG core %d with prediction resistance "
                 "%s\n", coreref, pr ? "enabled" : "disabled");
-       drbg->core = &drbg_cores[coreref];
-       drbg->pr = pr;
-       drbg->seeded = false;
-       switch (drbg->core->flags & DRBG_TYPE_MASK) {
-#ifdef CONFIG_CRYPTO_DRBG_HMAC
-       case DRBG_HMAC:
-               drbg->d_ops = &drbg_hmac_ops;
-               break;
-#endif /* CONFIG_CRYPTO_DRBG_HMAC */
-#ifdef CONFIG_CRYPTO_DRBG_HASH
-       case DRBG_HASH:
-               drbg->d_ops = &drbg_hash_ops;
-               break;
-#endif /* CONFIG_CRYPTO_DRBG_HASH */
-#ifdef CONFIG_CRYPTO_DRBG_CTR
-       case DRBG_CTR:
-               drbg->d_ops = &drbg_ctr_ops;
-               break;
-#endif /* CONFIG_CRYPTO_DRBG_CTR */
-       default:
-               return -EOPNOTSUPP;
-       }
+       mutex_lock(&drbg->drbg_mutex);
 
        /* 9.1 step 1 is implicit with the selected DRBG type */
 
@@ -1514,22 +1527,52 @@ static int drbg_instantiate(struct drbg_state *drbg, struct drbg_string *pers,
 
        /* 9.1 step 4 is implicit in  drbg_sec_strength */
 
-       ret = drbg_alloc_state(drbg);
-       if (ret)
-               return ret;
+       if (!drbg->core) {
+               drbg->core = &drbg_cores[coreref];
+               drbg->pr = pr;
+               drbg->seeded = false;
+               drbg->reseed_threshold = drbg_max_requests(drbg);
 
-       ret = -EFAULT;
-       if (drbg->d_ops->crypto_init(drbg))
-               goto err;
-       ret = drbg_seed(drbg, pers, false);
-       drbg->d_ops->crypto_fini(drbg);
-       if (ret)
-               goto err;
+               ret = drbg_alloc_state(drbg);
+               if (ret)
+                       goto unlock;
 
-       return 0;
+               ret = -EFAULT;
+               if (drbg->d_ops->crypto_init(drbg))
+                       goto err;
+
+               ret = drbg_prepare_hrng(drbg);
+               if (ret)
+                       goto free_everything;
+
+               if (IS_ERR(drbg->jent)) {
+                       ret = PTR_ERR(drbg->jent);
+                       drbg->jent = NULL;
+                       if (fips_enabled || ret != -ENOENT)
+                               goto free_everything;
+                       pr_info("DRBG: Continuing without Jitter RNG\n");
+               }
+
+               reseed = false;
+       }
+
+       ret = drbg_seed(drbg, pers, reseed);
+
+       if (ret && !reseed)
+               goto free_everything;
+
+       mutex_unlock(&drbg->drbg_mutex);
+       return ret;
 
 err:
        drbg_dealloc_state(drbg);
+unlock:
+       mutex_unlock(&drbg->drbg_mutex);
+       return ret;
+
+free_everything:
+       mutex_unlock(&drbg->drbg_mutex);
+       drbg_uninstantiate(drbg);
        return ret;
 }
 
@@ -1544,10 +1587,17 @@ err:
  */
 static int drbg_uninstantiate(struct drbg_state *drbg)
 {
-       spin_lock_bh(&drbg->drbg_lock);
+       if (drbg->random_ready.func) {
+               del_random_ready_callback(&drbg->random_ready);
+               cancel_work_sync(&drbg->seed_work);
+               crypto_free_rng(drbg->jent);
+               drbg->jent = NULL;
+       }
+
+       if (drbg->d_ops)
+               drbg->d_ops->crypto_fini(drbg);
        drbg_dealloc_state(drbg);
        /* no scrubbing of test_data -- this shall survive an uninstantiate */
-       spin_unlock_bh(&drbg->drbg_lock);
        return 0;
 }
 
@@ -1555,16 +1605,17 @@ static int drbg_uninstantiate(struct drbg_state *drbg)
  * Helper function for setting the test data in the DRBG
  *
  * @drbg DRBG state handle
- * @test_data test data to sets
+ * @data test data
+ * @len test data length
  */
-static inline void drbg_set_testdata(struct drbg_state *drbg,
-                                    struct drbg_test_data *test_data)
+static void drbg_kcapi_set_entropy(struct crypto_rng *tfm,
+                                  const u8 *data, unsigned int len)
 {
-       if (!test_data || !test_data->testentropy)
-               return;
-       spin_lock_bh(&drbg->drbg_lock);
-       drbg->test_data = test_data;
-       spin_unlock_bh(&drbg->drbg_lock);
+       struct drbg_state *drbg = crypto_rng_ctx(tfm);
+
+       mutex_lock(&drbg->drbg_mutex);
+       drbg_string_fill(&drbg->test_data, data, len);
+       mutex_unlock(&drbg->drbg_mutex);
 }
 
 /***************************************************************
@@ -1584,7 +1635,8 @@ static int drbg_init_hash_kernel(struct drbg_state *drbg)
 
        tfm = crypto_alloc_shash(drbg->core->backend_cra_name, 0, 0);
        if (IS_ERR(tfm)) {
-               pr_info("DRBG: could not allocate digest TFM handle\n");
+               pr_info("DRBG: could not allocate digest TFM handle: %s\n",
+                               drbg->core->backend_cra_name);
                return PTR_ERR(tfm);
        }
        BUG_ON(drbg_blocklen(drbg) != crypto_shash_digestsize(tfm));
@@ -1635,7 +1687,8 @@ static int drbg_init_sym_kernel(struct drbg_state *drbg)
 
        tfm = crypto_alloc_cipher(drbg->core->backend_cra_name, 0, 0);
        if (IS_ERR(tfm)) {
-               pr_info("DRBG: could not allocate cipher TFM handle\n");
+               pr_info("DRBG: could not allocate cipher TFM handle: %s\n",
+                               drbg->core->backend_cra_name);
                return PTR_ERR(tfm);
        }
        BUG_ON(drbg_blocklen(drbg) != crypto_cipher_blocksize(tfm));
@@ -1714,15 +1767,10 @@ static inline void drbg_convert_tfm_core(const char *cra_driver_name,
 static int drbg_kcapi_init(struct crypto_tfm *tfm)
 {
        struct drbg_state *drbg = crypto_tfm_ctx(tfm);
-       bool pr = false;
-       int coreref = 0;
 
-       drbg_convert_tfm_core(crypto_tfm_alg_driver_name(tfm), &coreref, &pr);
-       /*
-        * when personalization string is needed, the caller must call reset
-        * and provide the personalization string as seed information
-        */
-       return drbg_instantiate(drbg, NULL, coreref, pr);
+       mutex_init(&drbg->drbg_mutex);
+
+       return 0;
 }
 
 static void drbg_kcapi_cleanup(struct crypto_tfm *tfm)
@@ -1734,65 +1782,49 @@ static void drbg_kcapi_cleanup(struct crypto_tfm *tfm)
  * Generate random numbers invoked by the kernel crypto API:
  * The API of the kernel crypto API is extended as follows:
  *
- * If dlen is larger than zero, rdata is interpreted as the output buffer
- * where random data is to be stored.
- *
- * If dlen is zero, rdata is interpreted as a pointer to a struct drbg_gen
- * which holds the additional information string that is used for the
- * DRBG generation process. The output buffer that is to be used to store
- * data is also pointed to by struct drbg_gen.
+ * src is additional input supplied to the RNG.
+ * slen is the length of src.
+ * dst is the output buffer where random data is to be stored.
+ * dlen is the length of dst.
  */
-static int drbg_kcapi_random(struct crypto_rng *tfm, u8 *rdata,
-                            unsigned int dlen)
+static int drbg_kcapi_random(struct crypto_rng *tfm,
+                            const u8 *src, unsigned int slen,
+                            u8 *dst, unsigned int dlen)
 {
        struct drbg_state *drbg = crypto_rng_ctx(tfm);
-       if (0 < dlen) {
-               return drbg_generate_long(drbg, rdata, dlen, NULL);
-       } else {
-               struct drbg_gen *data = (struct drbg_gen *)rdata;
-               struct drbg_string addtl;
-               /* catch NULL pointer */
-               if (!data)
-                       return 0;
-               drbg_set_testdata(drbg, data->test_data);
+       struct drbg_string *addtl = NULL;
+       struct drbg_string string;
+
+       if (slen) {
                /* linked list variable is now local to allow modification */
-               drbg_string_fill(&addtl, data->addtl->buf, data->addtl->len);
-               return drbg_generate_long(drbg, data->outbuf, data->outlen,
-                                         &addtl);
+               drbg_string_fill(&string, src, slen);
+               addtl = &string;
        }
+
+       return drbg_generate_long(drbg, dst, dlen, addtl);
 }
 
 /*
- * Reset the DRBG invoked by the kernel crypto API
- * The reset implies a full re-initialization of the DRBG. Similar to the
- * generate function of drbg_kcapi_random, this function extends the
- * kernel crypto API interface with struct drbg_gen
+ * Seed the DRBG invoked by the kernel crypto API
  */
-static int drbg_kcapi_reset(struct crypto_rng *tfm, u8 *seed, unsigned int slen)
+static int drbg_kcapi_seed(struct crypto_rng *tfm,
+                          const u8 *seed, unsigned int slen)
 {
        struct drbg_state *drbg = crypto_rng_ctx(tfm);
        struct crypto_tfm *tfm_base = crypto_rng_tfm(tfm);
        bool pr = false;
-       struct drbg_string seed_string;
+       struct drbg_string string;
+       struct drbg_string *seed_string = NULL;
        int coreref = 0;
 
-       drbg_uninstantiate(drbg);
        drbg_convert_tfm_core(crypto_tfm_alg_driver_name(tfm_base), &coreref,
                              &pr);
        if (0 < slen) {
-               drbg_string_fill(&seed_string, seed, slen);
-               return drbg_instantiate(drbg, &seed_string, coreref, pr);
-       } else {
-               struct drbg_gen *data = (struct drbg_gen *)seed;
-               /* allow invocation of API call with NULL, 0 */
-               if (!data)
-                       return drbg_instantiate(drbg, NULL, coreref, pr);
-               drbg_set_testdata(drbg, data->test_data);
-               /* linked list variable is now local to allow modification */
-               drbg_string_fill(&seed_string, data->addtl->buf,
-                                data->addtl->len);
-               return drbg_instantiate(drbg, &seed_string, coreref, pr);
+               drbg_string_fill(&string, seed, slen);
+               seed_string = &string;
        }
+
+       return drbg_instantiate(drbg, seed_string, coreref, pr);
 }
 
 /***************************************************************
@@ -1811,7 +1843,6 @@ static int drbg_kcapi_reset(struct crypto_rng *tfm, u8 *seed, unsigned int slen)
  */
 static inline int __init drbg_healthcheck_sanity(void)
 {
-#ifdef CONFIG_CRYPTO_FIPS
        int len = 0;
 #define OUTBUFLEN 16
        unsigned char buf[OUTBUFLEN];
@@ -1839,6 +1870,8 @@ static inline int __init drbg_healthcheck_sanity(void)
        if (!drbg)
                return -ENOMEM;
 
+       mutex_init(&drbg->drbg_mutex);
+
        /*
         * if the following tests fail, it is likely that there is a buffer
         * overflow as buf is much smaller than the requested or provided
@@ -1877,37 +1910,33 @@ static inline int __init drbg_healthcheck_sanity(void)
 outbuf:
        kzfree(drbg);
        return rc;
-#else /* CONFIG_CRYPTO_FIPS */
-       return 0;
-#endif /* CONFIG_CRYPTO_FIPS */
 }
 
-static struct crypto_alg drbg_algs[22];
+static struct rng_alg drbg_algs[22];
 
 /*
  * Fill the array drbg_algs used to register the different DRBGs
  * with the kernel crypto API. To fill the array, the information
  * from drbg_cores[] is used.
  */
-static inline void __init drbg_fill_array(struct crypto_alg *alg,
+static inline void __init drbg_fill_array(struct rng_alg *alg,
                                          const struct drbg_core *core, int pr)
 {
        int pos = 0;
-       static int priority = 100;
+       static int priority = 200;
 
-       memset(alg, 0, sizeof(struct crypto_alg));
-       memcpy(alg->cra_name, "stdrng", 6);
+       memcpy(alg->base.cra_name, "stdrng", 6);
        if (pr) {
-               memcpy(alg->cra_driver_name, "drbg_pr_", 8);
+               memcpy(alg->base.cra_driver_name, "drbg_pr_", 8);
                pos = 8;
        } else {
-               memcpy(alg->cra_driver_name, "drbg_nopr_", 10);
+               memcpy(alg->base.cra_driver_name, "drbg_nopr_", 10);
                pos = 10;
        }
-       memcpy(alg->cra_driver_name + pos, core->cra_name,
+       memcpy(alg->base.cra_driver_name + pos, core->cra_name,
               strlen(core->cra_name));
 
-       alg->cra_priority = priority;
+       alg->base.cra_priority = priority;
        priority++;
        /*
         * If FIPS mode enabled, the selected DRBG shall have the
@@ -1915,17 +1944,16 @@ static inline void __init drbg_fill_array(struct crypto_alg *alg,
         * it is selected.
         */
        if (fips_enabled)
-               alg->cra_priority += 200;
-
-       alg->cra_flags          = CRYPTO_ALG_TYPE_RNG;
-       alg->cra_ctxsize        = sizeof(struct drbg_state);
-       alg->cra_type           = &crypto_rng_type;
-       alg->cra_module         = THIS_MODULE;
-       alg->cra_init           = drbg_kcapi_init;
-       alg->cra_exit           = drbg_kcapi_cleanup;
-       alg->cra_u.rng.rng_make_random  = drbg_kcapi_random;
-       alg->cra_u.rng.rng_reset        = drbg_kcapi_reset;
-       alg->cra_u.rng.seedsize = 0;
+               alg->base.cra_priority += 200;
+
+       alg->base.cra_ctxsize   = sizeof(struct drbg_state);
+       alg->base.cra_module    = THIS_MODULE;
+       alg->base.cra_init      = drbg_kcapi_init;
+       alg->base.cra_exit      = drbg_kcapi_cleanup;
+       alg->generate           = drbg_kcapi_random;
+       alg->seed               = drbg_kcapi_seed;
+       alg->set_ent            = drbg_kcapi_set_entropy;
+       alg->seedsize           = 0;
 }
 
 static int __init drbg_init(void)
@@ -1958,12 +1986,12 @@ static int __init drbg_init(void)
                drbg_fill_array(&drbg_algs[i], &drbg_cores[j], 1);
        for (j = 0; ARRAY_SIZE(drbg_cores) > j; j++, i++)
                drbg_fill_array(&drbg_algs[i], &drbg_cores[j], 0);
-       return crypto_register_algs(drbg_algs, (ARRAY_SIZE(drbg_cores) * 2));
+       return crypto_register_rngs(drbg_algs, (ARRAY_SIZE(drbg_cores) * 2));
 }
 
 static void __exit drbg_exit(void)
 {
-       crypto_unregister_algs(drbg_algs, (ARRAY_SIZE(drbg_cores) * 2));
+       crypto_unregister_rngs(drbg_algs, (ARRAY_SIZE(drbg_cores) * 2));
 }
 
 module_init(drbg_init);
@@ -1984,3 +2012,4 @@ MODULE_DESCRIPTION("NIST SP800-90A Deterministic Random Bit Generator (DRBG) "
                   CRYPTO_DRBG_HASH_STRING
                   CRYPTO_DRBG_HMAC_STRING
                   CRYPTO_DRBG_CTR_STRING);
+MODULE_ALIAS_CRYPTO("stdrng");
diff --git a/crypto/echainiv.c b/crypto/echainiv.c
new file mode 100644 (file)
index 0000000..b6e43dc
--- /dev/null
@@ -0,0 +1,312 @@
+/*
+ * echainiv: Encrypted Chain IV Generator
+ *
+ * This generator generates an IV based on a sequence number by xoring it
+ * with a salt and then encrypting it with the same key as used to encrypt
+ * the plain text.  This algorithm requires that the block size be equal
+ * to the IV size.  It is mainly useful for CBC.
+ *
+ * This generator can only be used by algorithms where authentication
+ * is performed after encryption (i.e., authenc).
+ *
+ * Copyright (c) 2015 Herbert Xu <herbert@gondor.apana.org.au>
+ *
+ * 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 <crypto/internal/geniv.h>
+#include <crypto/null.h>
+#include <crypto/rng.h>
+#include <crypto/scatterwalk.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/percpu.h>
+#include <linux/spinlock.h>
+#include <linux/string.h>
+
+#define MAX_IV_SIZE 16
+
+struct echainiv_ctx {
+       /* aead_geniv_ctx must be first the element */
+       struct aead_geniv_ctx geniv;
+       struct crypto_blkcipher *null;
+       u8 salt[] __attribute__ ((aligned(__alignof__(u32))));
+};
+
+static DEFINE_PER_CPU(u32 [MAX_IV_SIZE / sizeof(u32)], echainiv_iv);
+
+/* We don't care if we get preempted and read/write IVs from the next CPU. */
+static void echainiv_read_iv(u8 *dst, unsigned size)
+{
+       u32 *a = (u32 *)dst;
+       u32 __percpu *b = echainiv_iv;
+
+       for (; size >= 4; size -= 4) {
+               *a++ = this_cpu_read(*b);
+               b++;
+       }
+}
+
+static void echainiv_write_iv(const u8 *src, unsigned size)
+{
+       const u32 *a = (const u32 *)src;
+       u32 __percpu *b = echainiv_iv;
+
+       for (; size >= 4; size -= 4) {
+               this_cpu_write(*b, *a);
+               a++;
+               b++;
+       }
+}
+
+static void echainiv_encrypt_complete2(struct aead_request *req, int err)
+{
+       struct aead_request *subreq = aead_request_ctx(req);
+       struct crypto_aead *geniv;
+       unsigned int ivsize;
+
+       if (err == -EINPROGRESS)
+               return;
+
+       if (err)
+               goto out;
+
+       geniv = crypto_aead_reqtfm(req);
+       ivsize = crypto_aead_ivsize(geniv);
+
+       echainiv_write_iv(subreq->iv, ivsize);
+
+       if (req->iv != subreq->iv)
+               memcpy(req->iv, subreq->iv, ivsize);
+
+out:
+       if (req->iv != subreq->iv)
+               kzfree(subreq->iv);
+}
+
+static void echainiv_encrypt_complete(struct crypto_async_request *base,
+                                        int err)
+{
+       struct aead_request *req = base->data;
+
+       echainiv_encrypt_complete2(req, err);
+       aead_request_complete(req, err);
+}
+
+static int echainiv_encrypt(struct aead_request *req)
+{
+       struct crypto_aead *geniv = crypto_aead_reqtfm(req);
+       struct echainiv_ctx *ctx = crypto_aead_ctx(geniv);
+       struct aead_request *subreq = aead_request_ctx(req);
+       crypto_completion_t compl;
+       void *data;
+       u8 *info;
+       unsigned int ivsize = crypto_aead_ivsize(geniv);
+       int err;
+
+       if (req->cryptlen < ivsize)
+               return -EINVAL;
+
+       aead_request_set_tfm(subreq, ctx->geniv.child);
+
+       compl = echainiv_encrypt_complete;
+       data = req;
+       info = req->iv;
+
+       if (req->src != req->dst) {
+               struct blkcipher_desc desc = {
+                       .tfm = ctx->null,
+               };
+
+               err = crypto_blkcipher_encrypt(
+                       &desc, req->dst, req->src,
+                       req->assoclen + req->cryptlen);
+               if (err)
+                       return err;
+       }
+
+       if (unlikely(!IS_ALIGNED((unsigned long)info,
+                                crypto_aead_alignmask(geniv) + 1))) {
+               info = kmalloc(ivsize, req->base.flags &
+                                      CRYPTO_TFM_REQ_MAY_SLEEP ? GFP_KERNEL:
+                                                                 GFP_ATOMIC);
+               if (!info)
+                       return -ENOMEM;
+
+               memcpy(info, req->iv, ivsize);
+       }
+
+       aead_request_set_callback(subreq, req->base.flags, compl, data);
+       aead_request_set_crypt(subreq, req->dst, req->dst,
+                              req->cryptlen - ivsize, info);
+       aead_request_set_ad(subreq, req->assoclen + ivsize);
+
+       crypto_xor(info, ctx->salt, ivsize);
+       scatterwalk_map_and_copy(info, req->dst, req->assoclen, ivsize, 1);
+       echainiv_read_iv(info, ivsize);
+
+       err = crypto_aead_encrypt(subreq);
+       echainiv_encrypt_complete2(req, err);
+       return err;
+}
+
+static int echainiv_decrypt(struct aead_request *req)
+{
+       struct crypto_aead *geniv = crypto_aead_reqtfm(req);
+       struct echainiv_ctx *ctx = crypto_aead_ctx(geniv);
+       struct aead_request *subreq = aead_request_ctx(req);
+       crypto_completion_t compl;
+       void *data;
+       unsigned int ivsize = crypto_aead_ivsize(geniv);
+
+       if (req->cryptlen < ivsize + crypto_aead_authsize(geniv))
+               return -EINVAL;
+
+       aead_request_set_tfm(subreq, ctx->geniv.child);
+
+       compl = req->base.complete;
+       data = req->base.data;
+
+       aead_request_set_callback(subreq, req->base.flags, compl, data);
+       aead_request_set_crypt(subreq, req->src, req->dst,
+                              req->cryptlen - ivsize, req->iv);
+       aead_request_set_ad(subreq, req->assoclen + ivsize);
+
+       scatterwalk_map_and_copy(req->iv, req->src, req->assoclen, ivsize, 0);
+       if (req->src != req->dst)
+               scatterwalk_map_and_copy(req->iv, req->dst,
+                                        req->assoclen, ivsize, 1);
+
+       return crypto_aead_decrypt(subreq);
+}
+
+static int echainiv_init(struct crypto_tfm *tfm)
+{
+       struct crypto_aead *geniv = __crypto_aead_cast(tfm);
+       struct echainiv_ctx *ctx = crypto_aead_ctx(geniv);
+       int err;
+
+       spin_lock_init(&ctx->geniv.lock);
+
+       crypto_aead_set_reqsize(geniv, sizeof(struct aead_request));
+
+       err = crypto_get_default_rng();
+       if (err)
+               goto out;
+
+       err = crypto_rng_get_bytes(crypto_default_rng, ctx->salt,
+                                  crypto_aead_ivsize(geniv));
+       crypto_put_default_rng();
+       if (err)
+               goto out;
+
+       ctx->null = crypto_get_default_null_skcipher();
+       err = PTR_ERR(ctx->null);
+       if (IS_ERR(ctx->null))
+               goto out;
+
+       err = aead_geniv_init(tfm);
+       if (err)
+               goto drop_null;
+
+       ctx->geniv.child = geniv->child;
+       geniv->child = geniv;
+
+out:
+       return err;
+
+drop_null:
+       crypto_put_default_null_skcipher();
+       goto out;
+}
+
+static void echainiv_exit(struct crypto_tfm *tfm)
+{
+       struct echainiv_ctx *ctx = crypto_tfm_ctx(tfm);
+
+       crypto_free_aead(ctx->geniv.child);
+       crypto_put_default_null_skcipher();
+}
+
+static int echainiv_aead_create(struct crypto_template *tmpl,
+                               struct rtattr **tb)
+{
+       struct aead_instance *inst;
+       struct crypto_aead_spawn *spawn;
+       struct aead_alg *alg;
+       int err;
+
+       inst = aead_geniv_alloc(tmpl, tb, 0, 0);
+
+       if (IS_ERR(inst))
+               return PTR_ERR(inst);
+
+       spawn = aead_instance_ctx(inst);
+       alg = crypto_spawn_aead_alg(spawn);
+
+       if (alg->base.cra_aead.encrypt)
+               goto done;
+
+       err = -EINVAL;
+       if (inst->alg.ivsize & (sizeof(u32) - 1) ||
+           inst->alg.ivsize > MAX_IV_SIZE)
+               goto free_inst;
+
+       inst->alg.encrypt = echainiv_encrypt;
+       inst->alg.decrypt = echainiv_decrypt;
+
+       inst->alg.base.cra_init = echainiv_init;
+       inst->alg.base.cra_exit = echainiv_exit;
+
+       inst->alg.base.cra_alignmask |= __alignof__(u32) - 1;
+       inst->alg.base.cra_ctxsize = sizeof(struct echainiv_ctx);
+       inst->alg.base.cra_ctxsize += inst->alg.ivsize;
+
+done:
+       err = aead_register_instance(tmpl, inst);
+       if (err)
+               goto free_inst;
+
+out:
+       return err;
+
+free_inst:
+       aead_geniv_free(inst);
+       goto out;
+}
+
+static void echainiv_free(struct crypto_instance *inst)
+{
+       aead_geniv_free(aead_instance(inst));
+}
+
+static struct crypto_template echainiv_tmpl = {
+       .name = "echainiv",
+       .create = echainiv_aead_create,
+       .free = echainiv_free,
+       .module = THIS_MODULE,
+};
+
+static int __init echainiv_module_init(void)
+{
+       return crypto_register_template(&echainiv_tmpl);
+}
+
+static void __exit echainiv_module_exit(void)
+{
+       crypto_unregister_template(&echainiv_tmpl);
+}
+
+module_init(echainiv_module_init);
+module_exit(echainiv_module_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Encrypted Chain IV Generator");
+MODULE_ALIAS_CRYPTO("echainiv");
index f116fae766f81611c5530a6d92313c5e88d23fe1..16dda72fc4f88cc45f6b66f91b2ee1e1ccd5957e 100644 (file)
@@ -146,35 +146,13 @@ out:
        return err;
 }
 
-static int eseqiv_givencrypt_first(struct skcipher_givcrypt_request *req)
-{
-       struct crypto_ablkcipher *geniv = skcipher_givcrypt_reqtfm(req);
-       struct eseqiv_ctx *ctx = crypto_ablkcipher_ctx(geniv);
-       int err = 0;
-
-       spin_lock_bh(&ctx->lock);
-       if (crypto_ablkcipher_crt(geniv)->givencrypt != eseqiv_givencrypt_first)
-               goto unlock;
-
-       crypto_ablkcipher_crt(geniv)->givencrypt = eseqiv_givencrypt;
-       err = crypto_rng_get_bytes(crypto_default_rng, ctx->salt,
-                                  crypto_ablkcipher_ivsize(geniv));
-
-unlock:
-       spin_unlock_bh(&ctx->lock);
-
-       if (err)
-               return err;
-
-       return eseqiv_givencrypt(req);
-}
-
 static int eseqiv_init(struct crypto_tfm *tfm)
 {
        struct crypto_ablkcipher *geniv = __crypto_ablkcipher_cast(tfm);
        struct eseqiv_ctx *ctx = crypto_ablkcipher_ctx(geniv);
        unsigned long alignmask;
        unsigned int reqsize;
+       int err;
 
        spin_lock_init(&ctx->lock);
 
@@ -198,7 +176,15 @@ static int eseqiv_init(struct crypto_tfm *tfm)
        tfm->crt_ablkcipher.reqsize = reqsize +
                                      sizeof(struct ablkcipher_request);
 
-       return skcipher_geniv_init(tfm);
+       err = 0;
+       if (!crypto_get_default_rng()) {
+               crypto_ablkcipher_crt(geniv)->givencrypt = eseqiv_givencrypt;
+               err = crypto_rng_get_bytes(crypto_default_rng, ctx->salt,
+                                          crypto_ablkcipher_ivsize(geniv));
+               crypto_put_default_rng();
+       }
+
+       return err ?: skcipher_geniv_init(tfm);
 }
 
 static struct crypto_template eseqiv_tmpl;
@@ -208,20 +194,14 @@ static struct crypto_instance *eseqiv_alloc(struct rtattr **tb)
        struct crypto_instance *inst;
        int err;
 
-       err = crypto_get_default_rng();
-       if (err)
-               return ERR_PTR(err);
-
        inst = skcipher_geniv_alloc(&eseqiv_tmpl, tb, 0, 0);
        if (IS_ERR(inst))
-               goto put_rng;
+               goto out;
 
        err = -EINVAL;
        if (inst->alg.cra_ablkcipher.ivsize != inst->alg.cra_blocksize)
                goto free_inst;
 
-       inst->alg.cra_ablkcipher.givencrypt = eseqiv_givencrypt_first;
-
        inst->alg.cra_init = eseqiv_init;
        inst->alg.cra_exit = skcipher_geniv_exit;
 
@@ -234,21 +214,13 @@ out:
 free_inst:
        skcipher_geniv_free(inst);
        inst = ERR_PTR(err);
-put_rng:
-       crypto_put_default_rng();
        goto out;
 }
 
-static void eseqiv_free(struct crypto_instance *inst)
-{
-       skcipher_geniv_free(inst);
-       crypto_put_default_rng();
-}
-
 static struct crypto_template eseqiv_tmpl = {
        .name = "eseqiv",
        .alloc = eseqiv_alloc,
-       .free = eseqiv_free,
+       .free = skcipher_geniv_free,
        .module = THIS_MODULE,
 };
 
index 553970081c62e290f47c8bb446f2f6f32f609cd6..9d627c1cf8bc78a173248e82ce58135c1f306d44 100644 (file)
  *
  */
 
-#include "internal.h"
+#include <linux/export.h>
+#include <linux/fips.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sysctl.h>
 
 int fips_enabled;
 EXPORT_SYMBOL_GPL(fips_enabled);
@@ -25,3 +30,49 @@ static int fips_enable(char *str)
 }
 
 __setup("fips=", fips_enable);
+
+static struct ctl_table crypto_sysctl_table[] = {
+       {
+               .procname       = "fips_enabled",
+               .data           = &fips_enabled,
+               .maxlen         = sizeof(int),
+               .mode           = 0444,
+               .proc_handler   = proc_dointvec
+       },
+       {}
+};
+
+static struct ctl_table crypto_dir_table[] = {
+       {
+               .procname       = "crypto",
+               .mode           = 0555,
+               .child          = crypto_sysctl_table
+       },
+       {}
+};
+
+static struct ctl_table_header *crypto_sysctls;
+
+static void crypto_proc_fips_init(void)
+{
+       crypto_sysctls = register_sysctl_table(crypto_dir_table);
+}
+
+static void crypto_proc_fips_exit(void)
+{
+       unregister_sysctl_table(crypto_sysctls);
+}
+
+static int __init fips_init(void)
+{
+       crypto_proc_fips_init();
+       return 0;
+}
+
+static void __exit fips_exit(void)
+{
+       crypto_proc_fips_exit();
+}
+
+module_init(fips_init);
+module_exit(fips_exit);
index 2e403f6138c14bbcb048d59256f0fb40e2032f2d..7d32d4720564315f57a2766f5e1813d536d9ee69 100644 (file)
@@ -12,6 +12,7 @@
 #include <crypto/internal/aead.h>
 #include <crypto/internal/skcipher.h>
 #include <crypto/internal/hash.h>
+#include <crypto/null.h>
 #include <crypto/scatterwalk.h>
 #include <crypto/hash.h>
 #include "internal.h"
@@ -39,7 +40,6 @@ struct crypto_rfc4106_ctx {
 
 struct crypto_rfc4543_instance_ctx {
        struct crypto_aead_spawn aead;
-       struct crypto_skcipher_spawn null;
 };
 
 struct crypto_rfc4543_ctx {
@@ -49,25 +49,22 @@ struct crypto_rfc4543_ctx {
 };
 
 struct crypto_rfc4543_req_ctx {
-       u8 auth_tag[16];
-       u8 assocbuf[32];
-       struct scatterlist cipher[1];
-       struct scatterlist payload[2];
-       struct scatterlist assoc[2];
        struct aead_request subreq;
 };
 
 struct crypto_gcm_ghash_ctx {
        unsigned int cryptlen;
        struct scatterlist *src;
-       void (*complete)(struct aead_request *req, int err);
+       int (*complete)(struct aead_request *req, u32 flags);
 };
 
 struct crypto_gcm_req_priv_ctx {
+       u8 iv[16];
        u8 auth_tag[16];
        u8 iauth_tag[16];
-       struct scatterlist src[2];
-       struct scatterlist dst[2];
+       struct scatterlist src[3];
+       struct scatterlist dst[3];
+       struct scatterlist sg;
        struct crypto_gcm_ghash_ctx ghash_ctx;
        union {
                struct ahash_request ahreq;
@@ -80,7 +77,12 @@ struct crypto_gcm_setkey_result {
        struct completion completion;
 };
 
-static void *gcm_zeroes;
+static struct {
+       u8 buf[16];
+       struct scatterlist sg;
+} *gcm_zeroes;
+
+static int crypto_rfc4543_copy_src_to_dst(struct aead_request *req, bool enc);
 
 static inline struct crypto_gcm_req_priv_ctx *crypto_gcm_reqctx(
        struct aead_request *req)
@@ -120,15 +122,13 @@ static int crypto_gcm_setkey(struct crypto_aead *aead, const u8 *key,
 
        crypto_ablkcipher_clear_flags(ctr, CRYPTO_TFM_REQ_MASK);
        crypto_ablkcipher_set_flags(ctr, crypto_aead_get_flags(aead) &
-                                  CRYPTO_TFM_REQ_MASK);
-
+                                        CRYPTO_TFM_REQ_MASK);
        err = crypto_ablkcipher_setkey(ctr, key, keylen);
+       crypto_aead_set_flags(aead, crypto_ablkcipher_get_flags(ctr) &
+                                   CRYPTO_TFM_RES_MASK);
        if (err)
                return err;
 
-       crypto_aead_set_flags(aead, crypto_ablkcipher_get_flags(ctr) &
-                                      CRYPTO_TFM_RES_MASK);
-
        data = kzalloc(sizeof(*data) + crypto_ablkcipher_reqsize(ctr),
                       GFP_KERNEL);
        if (!data)
@@ -163,7 +163,7 @@ static int crypto_gcm_setkey(struct crypto_aead *aead, const u8 *key,
                              CRYPTO_TFM_RES_MASK);
 
 out:
-       kfree(data);
+       kzfree(data);
        return err;
 }
 
@@ -186,35 +186,46 @@ static int crypto_gcm_setauthsize(struct crypto_aead *tfm,
        return 0;
 }
 
-static void crypto_gcm_init_crypt(struct ablkcipher_request *ablk_req,
-                                 struct aead_request *req,
-                                 unsigned int cryptlen)
+static void crypto_gcm_init_common(struct aead_request *req)
 {
-       struct crypto_aead *aead = crypto_aead_reqtfm(req);
-       struct crypto_gcm_ctx *ctx = crypto_aead_ctx(aead);
        struct crypto_gcm_req_priv_ctx *pctx = crypto_gcm_reqctx(req);
-       struct scatterlist *dst;
        __be32 counter = cpu_to_be32(1);
+       struct scatterlist *sg;
 
        memset(pctx->auth_tag, 0, sizeof(pctx->auth_tag));
-       memcpy(req->iv + 12, &counter, 4);
+       memcpy(pctx->iv, req->iv, 12);
+       memcpy(pctx->iv + 12, &counter, 4);
 
-       sg_init_table(pctx->src, 2);
+       sg_init_table(pctx->src, 3);
        sg_set_buf(pctx->src, pctx->auth_tag, sizeof(pctx->auth_tag));
-       scatterwalk_sg_chain(pctx->src, 2, req->src);
+       sg = scatterwalk_ffwd(pctx->src + 1, req->src, req->assoclen);
+       if (sg != pctx->src + 1)
+               scatterwalk_sg_chain(pctx->src, 2, sg);
 
-       dst = pctx->src;
        if (req->src != req->dst) {
-               sg_init_table(pctx->dst, 2);
+               sg_init_table(pctx->dst, 3);
                sg_set_buf(pctx->dst, pctx->auth_tag, sizeof(pctx->auth_tag));
-               scatterwalk_sg_chain(pctx->dst, 2, req->dst);
-               dst = pctx->dst;
+               sg = scatterwalk_ffwd(pctx->dst + 1, req->dst, req->assoclen);
+               if (sg != pctx->dst + 1)
+                       scatterwalk_sg_chain(pctx->dst, 2, sg);
        }
+}
+
+static void crypto_gcm_init_crypt(struct aead_request *req,
+                                 unsigned int cryptlen)
+{
+       struct crypto_aead *aead = crypto_aead_reqtfm(req);
+       struct crypto_gcm_ctx *ctx = crypto_aead_ctx(aead);
+       struct crypto_gcm_req_priv_ctx *pctx = crypto_gcm_reqctx(req);
+       struct ablkcipher_request *ablk_req = &pctx->u.abreq;
+       struct scatterlist *dst;
+
+       dst = req->src == req->dst ? pctx->src : pctx->dst;
 
        ablkcipher_request_set_tfm(ablk_req, ctx->ctr);
        ablkcipher_request_set_crypt(ablk_req, pctx->src, dst,
                                     cryptlen + sizeof(pctx->auth_tag),
-                                    req->iv);
+                                    pctx->iv);
 }
 
 static inline unsigned int gcm_remain(unsigned int len)
@@ -224,41 +235,31 @@ static inline unsigned int gcm_remain(unsigned int len)
 }
 
 static void gcm_hash_len_done(struct crypto_async_request *areq, int err);
-static void gcm_hash_final_done(struct crypto_async_request *areq, int err);
 
 static int gcm_hash_update(struct aead_request *req,
-                          struct crypto_gcm_req_priv_ctx *pctx,
                           crypto_completion_t compl,
                           struct scatterlist *src,
-                          unsigned int len)
+                          unsigned int len, u32 flags)
 {
+       struct crypto_gcm_req_priv_ctx *pctx = crypto_gcm_reqctx(req);
        struct ahash_request *ahreq = &pctx->u.ahreq;
 
-       ahash_request_set_callback(ahreq, aead_request_flags(req),
-                                  compl, req);
+       ahash_request_set_callback(ahreq, flags, compl, req);
        ahash_request_set_crypt(ahreq, src, NULL, len);
 
        return crypto_ahash_update(ahreq);
 }
 
 static int gcm_hash_remain(struct aead_request *req,
-                          struct crypto_gcm_req_priv_ctx *pctx,
                           unsigned int remain,
-                          crypto_completion_t compl)
+                          crypto_completion_t compl, u32 flags)
 {
-       struct ahash_request *ahreq = &pctx->u.ahreq;
-
-       ahash_request_set_callback(ahreq, aead_request_flags(req),
-                                  compl, req);
-       sg_init_one(pctx->src, gcm_zeroes, remain);
-       ahash_request_set_crypt(ahreq, pctx->src, NULL, remain);
-
-       return crypto_ahash_update(ahreq);
+       return gcm_hash_update(req, compl, &gcm_zeroes->sg, remain, flags);
 }
 
-static int gcm_hash_len(struct aead_request *req,
-                       struct crypto_gcm_req_priv_ctx *pctx)
+static int gcm_hash_len(struct aead_request *req, u32 flags)
 {
+       struct crypto_gcm_req_priv_ctx *pctx = crypto_gcm_reqctx(req);
        struct ahash_request *ahreq = &pctx->u.ahreq;
        struct crypto_gcm_ghash_ctx *gctx = &pctx->ghash_ctx;
        u128 lengths;
@@ -266,76 +267,41 @@ static int gcm_hash_len(struct aead_request *req,
        lengths.a = cpu_to_be64(req->assoclen * 8);
        lengths.b = cpu_to_be64(gctx->cryptlen * 8);
        memcpy(pctx->iauth_tag, &lengths, 16);
-       sg_init_one(pctx->src, pctx->iauth_tag, 16);
-       ahash_request_set_callback(ahreq, aead_request_flags(req),
-                                  gcm_hash_len_done, req);
-       ahash_request_set_crypt(ahreq, pctx->src,
-                               NULL, sizeof(lengths));
+       sg_init_one(&pctx->sg, pctx->iauth_tag, 16);
+       ahash_request_set_callback(ahreq, flags, gcm_hash_len_done, req);
+       ahash_request_set_crypt(ahreq, &pctx->sg,
+                               pctx->iauth_tag, sizeof(lengths));
 
-       return crypto_ahash_update(ahreq);
-}
-
-static int gcm_hash_final(struct aead_request *req,
-                         struct crypto_gcm_req_priv_ctx *pctx)
-{
-       struct ahash_request *ahreq = &pctx->u.ahreq;
-
-       ahash_request_set_callback(ahreq, aead_request_flags(req),
-                                  gcm_hash_final_done, req);
-       ahash_request_set_crypt(ahreq, NULL, pctx->iauth_tag, 0);
-
-       return crypto_ahash_final(ahreq);
+       return crypto_ahash_finup(ahreq);
 }
 
-static void __gcm_hash_final_done(struct aead_request *req, int err)
+static int gcm_hash_len_continue(struct aead_request *req, u32 flags)
 {
        struct crypto_gcm_req_priv_ctx *pctx = crypto_gcm_reqctx(req);
        struct crypto_gcm_ghash_ctx *gctx = &pctx->ghash_ctx;
 
-       if (!err)
-               crypto_xor(pctx->auth_tag, pctx->iauth_tag, 16);
-
-       gctx->complete(req, err);
+       return gctx->complete(req, flags);
 }
 
-static void gcm_hash_final_done(struct crypto_async_request *areq, int err)
+static void gcm_hash_len_done(struct crypto_async_request *areq, int err)
 {
        struct aead_request *req = areq->data;
 
-       __gcm_hash_final_done(req, err);
-}
-
-static void __gcm_hash_len_done(struct aead_request *req, int err)
-{
-       struct crypto_gcm_req_priv_ctx *pctx = crypto_gcm_reqctx(req);
-
-       if (!err) {
-               err = gcm_hash_final(req, pctx);
-               if (err == -EINPROGRESS || err == -EBUSY)
-                       return;
-       }
-
-       __gcm_hash_final_done(req, err);
-}
+       if (err)
+               goto out;
 
-static void gcm_hash_len_done(struct crypto_async_request *areq, int err)
-{
-       struct aead_request *req = areq->data;
+       err = gcm_hash_len_continue(req, 0);
+       if (err == -EINPROGRESS)
+               return;
 
-       __gcm_hash_len_done(req, err);
+out:
+       aead_request_complete(req, err);
 }
 
-static void __gcm_hash_crypt_remain_done(struct aead_request *req, int err)
+static int gcm_hash_crypt_remain_continue(struct aead_request *req, u32 flags)
 {
-       struct crypto_gcm_req_priv_ctx *pctx = crypto_gcm_reqctx(req);
-
-       if (!err) {
-               err = gcm_hash_len(req, pctx);
-               if (err == -EINPROGRESS || err == -EBUSY)
-                       return;
-       }
-
-       __gcm_hash_len_done(req, err);
+       return gcm_hash_len(req, flags) ?:
+              gcm_hash_len_continue(req, flags);
 }
 
 static void gcm_hash_crypt_remain_done(struct crypto_async_request *areq,
@@ -343,55 +309,58 @@ static void gcm_hash_crypt_remain_done(struct crypto_async_request *areq,
 {
        struct aead_request *req = areq->data;
 
-       __gcm_hash_crypt_remain_done(req, err);
+       if (err)
+               goto out;
+
+       err = gcm_hash_crypt_remain_continue(req, 0);
+       if (err == -EINPROGRESS)
+               return;
+
+out:
+       aead_request_complete(req, err);
 }
 
-static void __gcm_hash_crypt_done(struct aead_request *req, int err)
+static int gcm_hash_crypt_continue(struct aead_request *req, u32 flags)
 {
        struct crypto_gcm_req_priv_ctx *pctx = crypto_gcm_reqctx(req);
        struct crypto_gcm_ghash_ctx *gctx = &pctx->ghash_ctx;
        unsigned int remain;
 
-       if (!err) {
-               remain = gcm_remain(gctx->cryptlen);
-               BUG_ON(!remain);
-               err = gcm_hash_remain(req, pctx, remain,
-                                     gcm_hash_crypt_remain_done);
-               if (err == -EINPROGRESS || err == -EBUSY)
-                       return;
-       }
+       remain = gcm_remain(gctx->cryptlen);
+       if (remain)
+               return gcm_hash_remain(req, remain,
+                                      gcm_hash_crypt_remain_done, flags) ?:
+                      gcm_hash_crypt_remain_continue(req, flags);
 
-       __gcm_hash_crypt_remain_done(req, err);
+       return gcm_hash_crypt_remain_continue(req, flags);
 }
 
 static void gcm_hash_crypt_done(struct crypto_async_request *areq, int err)
 {
        struct aead_request *req = areq->data;
 
-       __gcm_hash_crypt_done(req, err);
+       if (err)
+               goto out;
+
+       err = gcm_hash_crypt_continue(req, 0);
+       if (err == -EINPROGRESS)
+               return;
+
+out:
+       aead_request_complete(req, err);
 }
 
-static void __gcm_hash_assoc_remain_done(struct aead_request *req, int err)
+static int gcm_hash_assoc_remain_continue(struct aead_request *req, u32 flags)
 {
        struct crypto_gcm_req_priv_ctx *pctx = crypto_gcm_reqctx(req);
        struct crypto_gcm_ghash_ctx *gctx = &pctx->ghash_ctx;
-       crypto_completion_t compl;
-       unsigned int remain = 0;
-
-       if (!err && gctx->cryptlen) {
-               remain = gcm_remain(gctx->cryptlen);
-               compl = remain ? gcm_hash_crypt_done :
-                       gcm_hash_crypt_remain_done;
-               err = gcm_hash_update(req, pctx, compl,
-                                     gctx->src, gctx->cryptlen);
-               if (err == -EINPROGRESS || err == -EBUSY)
-                       return;
-       }
 
-       if (remain)
-               __gcm_hash_crypt_done(req, err);
-       else
-               __gcm_hash_crypt_remain_done(req, err);
+       if (gctx->cryptlen)
+               return gcm_hash_update(req, gcm_hash_crypt_done,
+                                      gctx->src, gctx->cryptlen, flags) ?:
+                      gcm_hash_crypt_continue(req, flags);
+
+       return gcm_hash_crypt_remain_continue(req, flags);
 }
 
 static void gcm_hash_assoc_remain_done(struct crypto_async_request *areq,
@@ -399,146 +368,120 @@ static void gcm_hash_assoc_remain_done(struct crypto_async_request *areq,
 {
        struct aead_request *req = areq->data;
 
-       __gcm_hash_assoc_remain_done(req, err);
+       if (err)
+               goto out;
+
+       err = gcm_hash_assoc_remain_continue(req, 0);
+       if (err == -EINPROGRESS)
+               return;
+
+out:
+       aead_request_complete(req, err);
 }
 
-static void __gcm_hash_assoc_done(struct aead_request *req, int err)
+static int gcm_hash_assoc_continue(struct aead_request *req, u32 flags)
 {
-       struct crypto_gcm_req_priv_ctx *pctx = crypto_gcm_reqctx(req);
        unsigned int remain;
 
-       if (!err) {
-               remain = gcm_remain(req->assoclen);
-               BUG_ON(!remain);
-               err = gcm_hash_remain(req, pctx, remain,
-                                     gcm_hash_assoc_remain_done);
-               if (err == -EINPROGRESS || err == -EBUSY)
-                       return;
-       }
+       remain = gcm_remain(req->assoclen);
+       if (remain)
+               return gcm_hash_remain(req, remain,
+                                      gcm_hash_assoc_remain_done, flags) ?:
+                      gcm_hash_assoc_remain_continue(req, flags);
 
-       __gcm_hash_assoc_remain_done(req, err);
+       return gcm_hash_assoc_remain_continue(req, flags);
 }
 
 static void gcm_hash_assoc_done(struct crypto_async_request *areq, int err)
 {
        struct aead_request *req = areq->data;
 
-       __gcm_hash_assoc_done(req, err);
+       if (err)
+               goto out;
+
+       err = gcm_hash_assoc_continue(req, 0);
+       if (err == -EINPROGRESS)
+               return;
+
+out:
+       aead_request_complete(req, err);
 }
 
-static void __gcm_hash_init_done(struct aead_request *req, int err)
+static int gcm_hash_init_continue(struct aead_request *req, u32 flags)
 {
-       struct crypto_gcm_req_priv_ctx *pctx = crypto_gcm_reqctx(req);
-       crypto_completion_t compl;
-       unsigned int remain = 0;
-
-       if (!err && req->assoclen) {
-               remain = gcm_remain(req->assoclen);
-               compl = remain ? gcm_hash_assoc_done :
-                       gcm_hash_assoc_remain_done;
-               err = gcm_hash_update(req, pctx, compl,
-                                     req->assoc, req->assoclen);
-               if (err == -EINPROGRESS || err == -EBUSY)
-                       return;
-       }
+       if (req->assoclen)
+               return gcm_hash_update(req, gcm_hash_assoc_done,
+                                      req->src, req->assoclen, flags) ?:
+                      gcm_hash_assoc_continue(req, flags);
 
-       if (remain)
-               __gcm_hash_assoc_done(req, err);
-       else
-               __gcm_hash_assoc_remain_done(req, err);
+       return gcm_hash_assoc_remain_continue(req, flags);
 }
 
 static void gcm_hash_init_done(struct crypto_async_request *areq, int err)
 {
        struct aead_request *req = areq->data;
 
-       __gcm_hash_init_done(req, err);
+       if (err)
+               goto out;
+
+       err = gcm_hash_init_continue(req, 0);
+       if (err == -EINPROGRESS)
+               return;
+
+out:
+       aead_request_complete(req, err);
 }
 
-static int gcm_hash(struct aead_request *req,
-                   struct crypto_gcm_req_priv_ctx *pctx)
+static int gcm_hash(struct aead_request *req, u32 flags)
 {
+       struct crypto_gcm_req_priv_ctx *pctx = crypto_gcm_reqctx(req);
        struct ahash_request *ahreq = &pctx->u.ahreq;
-       struct crypto_gcm_ghash_ctx *gctx = &pctx->ghash_ctx;
-       struct crypto_gcm_ctx *ctx = crypto_tfm_ctx(req->base.tfm);
-       unsigned int remain;
-       crypto_completion_t compl;
-       int err;
+       struct crypto_gcm_ctx *ctx = crypto_aead_ctx(crypto_aead_reqtfm(req));
 
        ahash_request_set_tfm(ahreq, ctx->ghash);
 
-       ahash_request_set_callback(ahreq, aead_request_flags(req),
-                                  gcm_hash_init_done, req);
-       err = crypto_ahash_init(ahreq);
-       if (err)
-               return err;
-       remain = gcm_remain(req->assoclen);
-       compl = remain ? gcm_hash_assoc_done : gcm_hash_assoc_remain_done;
-       err = gcm_hash_update(req, pctx, compl, req->assoc, req->assoclen);
-       if (err)
-               return err;
-       if (remain) {
-               err = gcm_hash_remain(req, pctx, remain,
-                                     gcm_hash_assoc_remain_done);
-               if (err)
-                       return err;
-       }
-       remain = gcm_remain(gctx->cryptlen);
-       compl = remain ? gcm_hash_crypt_done : gcm_hash_crypt_remain_done;
-       err = gcm_hash_update(req, pctx, compl, gctx->src, gctx->cryptlen);
-       if (err)
-               return err;
-       if (remain) {
-               err = gcm_hash_remain(req, pctx, remain,
-                                     gcm_hash_crypt_remain_done);
-               if (err)
-                       return err;
-       }
-       err = gcm_hash_len(req, pctx);
-       if (err)
-               return err;
-       err = gcm_hash_final(req, pctx);
-       if (err)
-               return err;
-
-       return 0;
+       ahash_request_set_callback(ahreq, flags, gcm_hash_init_done, req);
+       return crypto_ahash_init(ahreq) ?:
+              gcm_hash_init_continue(req, flags);
 }
 
-static void gcm_enc_copy_hash(struct aead_request *req,
-                             struct crypto_gcm_req_priv_ctx *pctx)
+static int gcm_enc_copy_hash(struct aead_request *req, u32 flags)
 {
+       struct crypto_gcm_req_priv_ctx *pctx = crypto_gcm_reqctx(req);
        struct crypto_aead *aead = crypto_aead_reqtfm(req);
        u8 *auth_tag = pctx->auth_tag;
 
-       scatterwalk_map_and_copy(auth_tag, req->dst, req->cryptlen,
+       crypto_xor(auth_tag, pctx->iauth_tag, 16);
+       scatterwalk_map_and_copy(auth_tag, req->dst,
+                                req->assoclen + req->cryptlen,
                                 crypto_aead_authsize(aead), 1);
+       return 0;
 }
 
-static void gcm_enc_hash_done(struct aead_request *req, int err)
+static int gcm_encrypt_continue(struct aead_request *req, u32 flags)
 {
        struct crypto_gcm_req_priv_ctx *pctx = crypto_gcm_reqctx(req);
+       struct crypto_gcm_ghash_ctx *gctx = &pctx->ghash_ctx;
 
-       if (!err)
-               gcm_enc_copy_hash(req, pctx);
+       gctx->src = sg_next(req->src == req->dst ? pctx->src : pctx->dst);
+       gctx->cryptlen = req->cryptlen;
+       gctx->complete = gcm_enc_copy_hash;
 
-       aead_request_complete(req, err);
+       return gcm_hash(req, flags);
 }
 
 static void gcm_encrypt_done(struct crypto_async_request *areq, int err)
 {
        struct aead_request *req = areq->data;
-       struct crypto_gcm_req_priv_ctx *pctx = crypto_gcm_reqctx(req);
 
-       if (!err) {
-               err = gcm_hash(req, pctx);
-               if (err == -EINPROGRESS || err == -EBUSY)
-                       return;
-               else if (!err) {
-                       crypto_xor(pctx->auth_tag, pctx->iauth_tag, 16);
-                       gcm_enc_copy_hash(req, pctx);
-               }
-       }
+       if (err)
+               goto out;
+
+       err = gcm_encrypt_continue(req, 0);
+       if (err == -EINPROGRESS)
+               return;
 
+out:
        aead_request_complete(req, err);
 }
 
@@ -546,34 +489,19 @@ static int crypto_gcm_encrypt(struct aead_request *req)
 {
        struct crypto_gcm_req_priv_ctx *pctx = crypto_gcm_reqctx(req);
        struct ablkcipher_request *abreq = &pctx->u.abreq;
-       struct crypto_gcm_ghash_ctx *gctx = &pctx->ghash_ctx;
-       int err;
-
-       crypto_gcm_init_crypt(abreq, req, req->cryptlen);
-       ablkcipher_request_set_callback(abreq, aead_request_flags(req),
-                                       gcm_encrypt_done, req);
-
-       gctx->src = req->dst;
-       gctx->cryptlen = req->cryptlen;
-       gctx->complete = gcm_enc_hash_done;
-
-       err = crypto_ablkcipher_encrypt(abreq);
-       if (err)
-               return err;
-
-       err = gcm_hash(req, pctx);
-       if (err)
-               return err;
+       u32 flags = aead_request_flags(req);
 
-       crypto_xor(pctx->auth_tag, pctx->iauth_tag, 16);
-       gcm_enc_copy_hash(req, pctx);
+       crypto_gcm_init_common(req);
+       crypto_gcm_init_crypt(req, req->cryptlen);
+       ablkcipher_request_set_callback(abreq, flags, gcm_encrypt_done, req);
 
-       return 0;
+       return crypto_ablkcipher_encrypt(abreq) ?:
+              gcm_encrypt_continue(req, flags);
 }
 
-static int crypto_gcm_verify(struct aead_request *req,
-                            struct crypto_gcm_req_priv_ctx *pctx)
+static int crypto_gcm_verify(struct aead_request *req)
 {
+       struct crypto_gcm_req_priv_ctx *pctx = crypto_gcm_reqctx(req);
        struct crypto_aead *aead = crypto_aead_reqtfm(req);
        u8 *auth_tag = pctx->auth_tag;
        u8 *iauth_tag = pctx->iauth_tag;
@@ -581,78 +509,57 @@ static int crypto_gcm_verify(struct aead_request *req,
        unsigned int cryptlen = req->cryptlen - authsize;
 
        crypto_xor(auth_tag, iauth_tag, 16);
-       scatterwalk_map_and_copy(iauth_tag, req->src, cryptlen, authsize, 0);
+       scatterwalk_map_and_copy(iauth_tag, req->src,
+                                req->assoclen + cryptlen, authsize, 0);
        return crypto_memneq(iauth_tag, auth_tag, authsize) ? -EBADMSG : 0;
 }
 
 static void gcm_decrypt_done(struct crypto_async_request *areq, int err)
 {
        struct aead_request *req = areq->data;
-       struct crypto_gcm_req_priv_ctx *pctx = crypto_gcm_reqctx(req);
 
        if (!err)
-               err = crypto_gcm_verify(req, pctx);
+               err = crypto_gcm_verify(req);
 
        aead_request_complete(req, err);
 }
 
-static void gcm_dec_hash_done(struct aead_request *req, int err)
+static int gcm_dec_hash_continue(struct aead_request *req, u32 flags)
 {
        struct crypto_gcm_req_priv_ctx *pctx = crypto_gcm_reqctx(req);
        struct ablkcipher_request *abreq = &pctx->u.abreq;
        struct crypto_gcm_ghash_ctx *gctx = &pctx->ghash_ctx;
 
-       if (!err) {
-               ablkcipher_request_set_callback(abreq, aead_request_flags(req),
-                                               gcm_decrypt_done, req);
-               crypto_gcm_init_crypt(abreq, req, gctx->cryptlen);
-               err = crypto_ablkcipher_decrypt(abreq);
-               if (err == -EINPROGRESS || err == -EBUSY)
-                       return;
-               else if (!err)
-                       err = crypto_gcm_verify(req, pctx);
-       }
-
-       aead_request_complete(req, err);
+       crypto_gcm_init_crypt(req, gctx->cryptlen);
+       ablkcipher_request_set_callback(abreq, flags, gcm_decrypt_done, req);
+       return crypto_ablkcipher_decrypt(abreq) ?: crypto_gcm_verify(req);
 }
 
 static int crypto_gcm_decrypt(struct aead_request *req)
 {
        struct crypto_aead *aead = crypto_aead_reqtfm(req);
        struct crypto_gcm_req_priv_ctx *pctx = crypto_gcm_reqctx(req);
-       struct ablkcipher_request *abreq = &pctx->u.abreq;
        struct crypto_gcm_ghash_ctx *gctx = &pctx->ghash_ctx;
        unsigned int authsize = crypto_aead_authsize(aead);
        unsigned int cryptlen = req->cryptlen;
-       int err;
+       u32 flags = aead_request_flags(req);
 
-       if (cryptlen < authsize)
-               return -EINVAL;
        cryptlen -= authsize;
 
-       gctx->src = req->src;
-       gctx->cryptlen = cryptlen;
-       gctx->complete = gcm_dec_hash_done;
-
-       err = gcm_hash(req, pctx);
-       if (err)
-               return err;
+       crypto_gcm_init_common(req);
 
-       ablkcipher_request_set_callback(abreq, aead_request_flags(req),
-                                       gcm_decrypt_done, req);
-       crypto_gcm_init_crypt(abreq, req, cryptlen);
-       err = crypto_ablkcipher_decrypt(abreq);
-       if (err)
-               return err;
+       gctx->src = sg_next(pctx->src);
+       gctx->cryptlen = cryptlen;
+       gctx->complete = gcm_dec_hash_continue;
 
-       return crypto_gcm_verify(req, pctx);
+       return gcm_hash(req, flags);
 }
 
-static int crypto_gcm_init_tfm(struct crypto_tfm *tfm)
+static int crypto_gcm_init_tfm(struct crypto_aead *tfm)
 {
-       struct crypto_instance *inst = (void *)tfm->__crt_alg;
-       struct gcm_instance_ctx *ictx = crypto_instance_ctx(inst);
-       struct crypto_gcm_ctx *ctx = crypto_tfm_ctx(tfm);
+       struct aead_instance *inst = aead_alg_instance(tfm);
+       struct gcm_instance_ctx *ictx = aead_instance_ctx(inst);
+       struct crypto_gcm_ctx *ctx = crypto_aead_ctx(tfm);
        struct crypto_ablkcipher *ctr;
        struct crypto_ahash *ghash;
        unsigned long align;
@@ -670,14 +577,14 @@ static int crypto_gcm_init_tfm(struct crypto_tfm *tfm)
        ctx->ctr = ctr;
        ctx->ghash = ghash;
 
-       align = crypto_tfm_alg_alignmask(tfm);
+       align = crypto_aead_alignmask(tfm);
        align &= ~(crypto_tfm_ctx_alignment() - 1);
-       tfm->crt_aead.reqsize = align +
-               offsetof(struct crypto_gcm_req_priv_ctx, u) +
+       crypto_aead_set_reqsize(tfm,
+               align + offsetof(struct crypto_gcm_req_priv_ctx, u) +
                max(sizeof(struct ablkcipher_request) +
                    crypto_ablkcipher_reqsize(ctr),
                    sizeof(struct ahash_request) +
-                   crypto_ahash_reqsize(ghash));
+                   crypto_ahash_reqsize(ghash)));
 
        return 0;
 
@@ -686,53 +593,59 @@ err_free_hash:
        return err;
 }
 
-static void crypto_gcm_exit_tfm(struct crypto_tfm *tfm)
+static void crypto_gcm_exit_tfm(struct crypto_aead *tfm)
 {
-       struct crypto_gcm_ctx *ctx = crypto_tfm_ctx(tfm);
+       struct crypto_gcm_ctx *ctx = crypto_aead_ctx(tfm);
 
        crypto_free_ahash(ctx->ghash);
        crypto_free_ablkcipher(ctx->ctr);
 }
 
-static struct crypto_instance *crypto_gcm_alloc_common(struct rtattr **tb,
-                                                      const char *full_name,
-                                                      const char *ctr_name,
-                                                      const char *ghash_name)
+static int crypto_gcm_create_common(struct crypto_template *tmpl,
+                                   struct rtattr **tb,
+                                   const char *full_name,
+                                   const char *ctr_name,
+                                   const char *ghash_name)
 {
        struct crypto_attr_type *algt;
-       struct crypto_instance *inst;
+       struct aead_instance *inst;
        struct crypto_alg *ctr;
        struct crypto_alg *ghash_alg;
-       struct ahash_alg *ghash_ahash_alg;
+       struct hash_alg_common *ghash;
        struct gcm_instance_ctx *ctx;
        int err;
 
        algt = crypto_get_attr_type(tb);
        if (IS_ERR(algt))
-               return ERR_CAST(algt);
+               return PTR_ERR(algt);
 
        if ((algt->type ^ CRYPTO_ALG_TYPE_AEAD) & algt->mask)
-               return ERR_PTR(-EINVAL);
+               return -EINVAL;
 
        ghash_alg = crypto_find_alg(ghash_name, &crypto_ahash_type,
                                    CRYPTO_ALG_TYPE_HASH,
                                    CRYPTO_ALG_TYPE_AHASH_MASK);
        if (IS_ERR(ghash_alg))
-               return ERR_CAST(ghash_alg);
+               return PTR_ERR(ghash_alg);
+
+       ghash = __crypto_hash_alg_common(ghash_alg);
 
        err = -ENOMEM;
        inst = kzalloc(sizeof(*inst) + sizeof(*ctx), GFP_KERNEL);
        if (!inst)
                goto out_put_ghash;
 
-       ctx = crypto_instance_ctx(inst);
-       ghash_ahash_alg = container_of(ghash_alg, struct ahash_alg, halg.base);
-       err = crypto_init_ahash_spawn(&ctx->ghash, &ghash_ahash_alg->halg,
-                                     inst);
+       ctx = aead_instance_ctx(inst);
+       err = crypto_init_ahash_spawn(&ctx->ghash, ghash,
+                                     aead_crypto_instance(inst));
        if (err)
                goto err_free_inst;
 
-       crypto_set_skcipher_spawn(&ctx->ctr, inst);
+       err = -EINVAL;
+       if (ghash->digestsize != 16)
+               goto err_drop_ghash;
+
+       crypto_set_skcipher_spawn(&ctx->ctr, aead_crypto_instance(inst));
        err = crypto_grab_skcipher(&ctx->ctr, ctr_name, 0,
                                   crypto_requires_sync(algt->type,
                                                        algt->mask));
@@ -751,33 +664,38 @@ static struct crypto_instance *crypto_gcm_alloc_common(struct rtattr **tb,
                goto out_put_ctr;
 
        err = -ENAMETOOLONG;
-       if (snprintf(inst->alg.cra_driver_name, CRYPTO_MAX_ALG_NAME,
+       if (snprintf(inst->alg.base.cra_driver_name, CRYPTO_MAX_ALG_NAME,
                     "gcm_base(%s,%s)", ctr->cra_driver_name,
                     ghash_alg->cra_driver_name) >=
            CRYPTO_MAX_ALG_NAME)
                goto out_put_ctr;
 
-       memcpy(inst->alg.cra_name, full_name, CRYPTO_MAX_ALG_NAME);
-
-       inst->alg.cra_flags = CRYPTO_ALG_TYPE_AEAD;
-       inst->alg.cra_flags |= ctr->cra_flags & CRYPTO_ALG_ASYNC;
-       inst->alg.cra_priority = ctr->cra_priority;
-       inst->alg.cra_blocksize = 1;
-       inst->alg.cra_alignmask = ctr->cra_alignmask | (__alignof__(u64) - 1);
-       inst->alg.cra_type = &crypto_aead_type;
-       inst->alg.cra_aead.ivsize = 16;
-       inst->alg.cra_aead.maxauthsize = 16;
-       inst->alg.cra_ctxsize = sizeof(struct crypto_gcm_ctx);
-       inst->alg.cra_init = crypto_gcm_init_tfm;
-       inst->alg.cra_exit = crypto_gcm_exit_tfm;
-       inst->alg.cra_aead.setkey = crypto_gcm_setkey;
-       inst->alg.cra_aead.setauthsize = crypto_gcm_setauthsize;
-       inst->alg.cra_aead.encrypt = crypto_gcm_encrypt;
-       inst->alg.cra_aead.decrypt = crypto_gcm_decrypt;
+       memcpy(inst->alg.base.cra_name, full_name, CRYPTO_MAX_ALG_NAME);
+
+       inst->alg.base.cra_flags = (ghash->base.cra_flags | ctr->cra_flags) &
+                                  CRYPTO_ALG_ASYNC;
+       inst->alg.base.cra_priority = (ghash->base.cra_priority +
+                                      ctr->cra_priority) / 2;
+       inst->alg.base.cra_blocksize = 1;
+       inst->alg.base.cra_alignmask = ghash->base.cra_alignmask |
+                                      ctr->cra_alignmask;
+       inst->alg.base.cra_ctxsize = sizeof(struct crypto_gcm_ctx);
+       inst->alg.ivsize = 12;
+       inst->alg.maxauthsize = 16;
+       inst->alg.init = crypto_gcm_init_tfm;
+       inst->alg.exit = crypto_gcm_exit_tfm;
+       inst->alg.setkey = crypto_gcm_setkey;
+       inst->alg.setauthsize = crypto_gcm_setauthsize;
+       inst->alg.encrypt = crypto_gcm_encrypt;
+       inst->alg.decrypt = crypto_gcm_decrypt;
+
+       err = aead_register_instance(tmpl, inst);
+       if (err)
+               goto out_put_ctr;
 
-out:
+out_put_ghash:
        crypto_mod_put(ghash_alg);
-       return inst;
+       return err;
 
 out_put_ctr:
        crypto_drop_skcipher(&ctx->ctr);
@@ -785,12 +703,10 @@ err_drop_ghash:
        crypto_drop_ahash(&ctx->ghash);
 err_free_inst:
        kfree(inst);
-out_put_ghash:
-       inst = ERR_PTR(err);
-       goto out;
+       goto out_put_ghash;
 }
 
-static struct crypto_instance *crypto_gcm_alloc(struct rtattr **tb)
+static int crypto_gcm_create(struct crypto_template *tmpl, struct rtattr **tb)
 {
        const char *cipher_name;
        char ctr_name[CRYPTO_MAX_ALG_NAME];
@@ -798,17 +714,18 @@ static struct crypto_instance *crypto_gcm_alloc(struct rtattr **tb)
 
        cipher_name = crypto_attr_alg_name(tb[1]);
        if (IS_ERR(cipher_name))
-               return ERR_CAST(cipher_name);
+               return PTR_ERR(cipher_name);
 
        if (snprintf(ctr_name, CRYPTO_MAX_ALG_NAME, "ctr(%s)", cipher_name) >=
            CRYPTO_MAX_ALG_NAME)
-               return ERR_PTR(-ENAMETOOLONG);
+               return -ENAMETOOLONG;
 
        if (snprintf(full_name, CRYPTO_MAX_ALG_NAME, "gcm(%s)", cipher_name) >=
            CRYPTO_MAX_ALG_NAME)
-               return ERR_PTR(-ENAMETOOLONG);
+               return -ENAMETOOLONG;
 
-       return crypto_gcm_alloc_common(tb, full_name, ctr_name, "ghash");
+       return crypto_gcm_create_common(tmpl, tb, full_name,
+                                       ctr_name, "ghash");
 }
 
 static void crypto_gcm_free(struct crypto_instance *inst)
@@ -817,17 +734,18 @@ static void crypto_gcm_free(struct crypto_instance *inst)
 
        crypto_drop_skcipher(&ctx->ctr);
        crypto_drop_ahash(&ctx->ghash);
-       kfree(inst);
+       kfree(aead_instance(inst));
 }
 
 static struct crypto_template crypto_gcm_tmpl = {
        .name = "gcm",
-       .alloc = crypto_gcm_alloc,
+       .create = crypto_gcm_create,
        .free = crypto_gcm_free,
        .module = THIS_MODULE,
 };
 
-static struct crypto_instance *crypto_gcm_base_alloc(struct rtattr **tb)
+static int crypto_gcm_base_create(struct crypto_template *tmpl,
+                                 struct rtattr **tb)
 {
        const char *ctr_name;
        const char *ghash_name;
@@ -835,22 +753,23 @@ static struct crypto_instance *crypto_gcm_base_alloc(struct rtattr **tb)
 
        ctr_name = crypto_attr_alg_name(tb[1]);
        if (IS_ERR(ctr_name))
-               return ERR_CAST(ctr_name);
+               return PTR_ERR(ctr_name);
 
        ghash_name = crypto_attr_alg_name(tb[2]);
        if (IS_ERR(ghash_name))
-               return ERR_CAST(ghash_name);
+               return PTR_ERR(ghash_name);
 
        if (snprintf(full_name, CRYPTO_MAX_ALG_NAME, "gcm_base(%s,%s)",
                     ctr_name, ghash_name) >= CRYPTO_MAX_ALG_NAME)
-               return ERR_PTR(-ENAMETOOLONG);
+               return -ENAMETOOLONG;
 
-       return crypto_gcm_alloc_common(tb, full_name, ctr_name, ghash_name);
+       return crypto_gcm_create_common(tmpl, tb, full_name,
+                                       ctr_name, ghash_name);
 }
 
 static struct crypto_template crypto_gcm_base_tmpl = {
        .name = "gcm_base",
-       .alloc = crypto_gcm_base_alloc,
+       .create = crypto_gcm_base_create,
        .free = crypto_gcm_free,
        .module = THIS_MODULE,
 };
@@ -911,7 +830,7 @@ static struct aead_request *crypto_rfc4106_crypt(struct aead_request *req)
        aead_request_set_callback(subreq, req->base.flags, req->base.complete,
                                  req->base.data);
        aead_request_set_crypt(subreq, req->src, req->dst, req->cryptlen, iv);
-       aead_request_set_assoc(subreq, req->assoc, req->assoclen);
+       aead_request_set_ad(subreq, req->assoclen);
 
        return subreq;
 }
@@ -930,11 +849,11 @@ static int crypto_rfc4106_decrypt(struct aead_request *req)
        return crypto_aead_decrypt(req);
 }
 
-static int crypto_rfc4106_init_tfm(struct crypto_tfm *tfm)
+static int crypto_rfc4106_init_tfm(struct crypto_aead *tfm)
 {
-       struct crypto_instance *inst = (void *)tfm->__crt_alg;
-       struct crypto_aead_spawn *spawn = crypto_instance_ctx(inst);
-       struct crypto_rfc4106_ctx *ctx = crypto_tfm_ctx(tfm);
+       struct aead_instance *inst = aead_alg_instance(tfm);
+       struct crypto_aead_spawn *spawn = aead_instance_ctx(inst);
+       struct crypto_rfc4106_ctx *ctx = crypto_aead_ctx(tfm);
        struct crypto_aead *aead;
        unsigned long align;
 
@@ -946,126 +865,120 @@ static int crypto_rfc4106_init_tfm(struct crypto_tfm *tfm)
 
        align = crypto_aead_alignmask(aead);
        align &= ~(crypto_tfm_ctx_alignment() - 1);
-       tfm->crt_aead.reqsize = sizeof(struct aead_request) +
-                               ALIGN(crypto_aead_reqsize(aead),
-                                     crypto_tfm_ctx_alignment()) +
-                               align + 16;
+       crypto_aead_set_reqsize(
+               tfm,
+               sizeof(struct aead_request) +
+               ALIGN(crypto_aead_reqsize(aead), crypto_tfm_ctx_alignment()) +
+               align + 12);
 
        return 0;
 }
 
-static void crypto_rfc4106_exit_tfm(struct crypto_tfm *tfm)
+static void crypto_rfc4106_exit_tfm(struct crypto_aead *tfm)
 {
-       struct crypto_rfc4106_ctx *ctx = crypto_tfm_ctx(tfm);
+       struct crypto_rfc4106_ctx *ctx = crypto_aead_ctx(tfm);
 
        crypto_free_aead(ctx->child);
 }
 
-static struct crypto_instance *crypto_rfc4106_alloc(struct rtattr **tb)
+static int crypto_rfc4106_create(struct crypto_template *tmpl,
+                                struct rtattr **tb)
 {
        struct crypto_attr_type *algt;
-       struct crypto_instance *inst;
+       struct aead_instance *inst;
        struct crypto_aead_spawn *spawn;
-       struct crypto_alg *alg;
+       struct aead_alg *alg;
        const char *ccm_name;
        int err;
 
        algt = crypto_get_attr_type(tb);
        if (IS_ERR(algt))
-               return ERR_CAST(algt);
+               return PTR_ERR(algt);
 
        if ((algt->type ^ CRYPTO_ALG_TYPE_AEAD) & algt->mask)
-               return ERR_PTR(-EINVAL);
+               return -EINVAL;
 
        ccm_name = crypto_attr_alg_name(tb[1]);
        if (IS_ERR(ccm_name))
-               return ERR_CAST(ccm_name);
+               return PTR_ERR(ccm_name);
 
        inst = kzalloc(sizeof(*inst) + sizeof(*spawn), GFP_KERNEL);
        if (!inst)
-               return ERR_PTR(-ENOMEM);
+               return -ENOMEM;
 
-       spawn = crypto_instance_ctx(inst);
-       crypto_set_aead_spawn(spawn, inst);
+       spawn = aead_instance_ctx(inst);
+       crypto_set_aead_spawn(spawn, aead_crypto_instance(inst));
        err = crypto_grab_aead(spawn, ccm_name, 0,
                               crypto_requires_sync(algt->type, algt->mask));
        if (err)
                goto out_free_inst;
 
-       alg = crypto_aead_spawn_alg(spawn);
+       alg = crypto_spawn_aead_alg(spawn);
 
        err = -EINVAL;
 
-       /* We only support 16-byte blocks. */
-       if (alg->cra_aead.ivsize != 16)
+       /* Underlying IV size must be 12. */
+       if (crypto_aead_alg_ivsize(alg) != 12)
                goto out_drop_alg;
 
        /* Not a stream cipher? */
-       if (alg->cra_blocksize != 1)
+       if (alg->base.cra_blocksize != 1)
                goto out_drop_alg;
 
        err = -ENAMETOOLONG;
-       if (snprintf(inst->alg.cra_name, CRYPTO_MAX_ALG_NAME,
-                    "rfc4106(%s)", alg->cra_name) >= CRYPTO_MAX_ALG_NAME ||
-           snprintf(inst->alg.cra_driver_name, CRYPTO_MAX_ALG_NAME,
-                    "rfc4106(%s)", alg->cra_driver_name) >=
+       if (snprintf(inst->alg.base.cra_name, CRYPTO_MAX_ALG_NAME,
+                    "rfc4106(%s)", alg->base.cra_name) >=
+           CRYPTO_MAX_ALG_NAME ||
+           snprintf(inst->alg.base.cra_driver_name, CRYPTO_MAX_ALG_NAME,
+                    "rfc4106(%s)", alg->base.cra_driver_name) >=
            CRYPTO_MAX_ALG_NAME)
                goto out_drop_alg;
 
-       inst->alg.cra_flags = CRYPTO_ALG_TYPE_AEAD;
-       inst->alg.cra_flags |= alg->cra_flags & CRYPTO_ALG_ASYNC;
-       inst->alg.cra_priority = alg->cra_priority;
-       inst->alg.cra_blocksize = 1;
-       inst->alg.cra_alignmask = alg->cra_alignmask;
-       inst->alg.cra_type = &crypto_nivaead_type;
+       inst->alg.base.cra_flags |= alg->base.cra_flags & CRYPTO_ALG_ASYNC;
+       inst->alg.base.cra_priority = alg->base.cra_priority;
+       inst->alg.base.cra_blocksize = 1;
+       inst->alg.base.cra_alignmask = alg->base.cra_alignmask;
 
-       inst->alg.cra_aead.ivsize = 8;
-       inst->alg.cra_aead.maxauthsize = 16;
+       inst->alg.base.cra_ctxsize = sizeof(struct crypto_rfc4106_ctx);
 
-       inst->alg.cra_ctxsize = sizeof(struct crypto_rfc4106_ctx);
+       inst->alg.ivsize = 8;
+       inst->alg.maxauthsize = crypto_aead_alg_maxauthsize(alg);
 
-       inst->alg.cra_init = crypto_rfc4106_init_tfm;
-       inst->alg.cra_exit = crypto_rfc4106_exit_tfm;
+       inst->alg.init = crypto_rfc4106_init_tfm;
+       inst->alg.exit = crypto_rfc4106_exit_tfm;
 
-       inst->alg.cra_aead.setkey = crypto_rfc4106_setkey;
-       inst->alg.cra_aead.setauthsize = crypto_rfc4106_setauthsize;
-       inst->alg.cra_aead.encrypt = crypto_rfc4106_encrypt;
-       inst->alg.cra_aead.decrypt = crypto_rfc4106_decrypt;
+       inst->alg.setkey = crypto_rfc4106_setkey;
+       inst->alg.setauthsize = crypto_rfc4106_setauthsize;
+       inst->alg.encrypt = crypto_rfc4106_encrypt;
+       inst->alg.decrypt = crypto_rfc4106_decrypt;
 
-       inst->alg.cra_aead.geniv = "seqiv";
+       err = aead_register_instance(tmpl, inst);
+       if (err)
+               goto out_drop_alg;
 
 out:
-       return inst;
+       return err;
 
 out_drop_alg:
        crypto_drop_aead(spawn);
 out_free_inst:
        kfree(inst);
-       inst = ERR_PTR(err);
        goto out;
 }
 
 static void crypto_rfc4106_free(struct crypto_instance *inst)
 {
-       crypto_drop_spawn(crypto_instance_ctx(inst));
-       kfree(inst);
+       crypto_drop_aead(crypto_instance_ctx(inst));
+       kfree(aead_instance(inst));
 }
 
 static struct crypto_template crypto_rfc4106_tmpl = {
        .name = "rfc4106",
-       .alloc = crypto_rfc4106_alloc,
+       .create = crypto_rfc4106_create,
        .free = crypto_rfc4106_free,
        .module = THIS_MODULE,
 };
 
-static inline struct crypto_rfc4543_req_ctx *crypto_rfc4543_reqctx(
-       struct aead_request *req)
-{
-       unsigned long align = crypto_aead_alignmask(crypto_aead_reqtfm(req));
-
-       return (void *)PTR_ALIGN((u8 *)aead_request_ctx(req), align + 1);
-}
-
 static int crypto_rfc4543_setkey(struct crypto_aead *parent, const u8 *key,
                                 unsigned int keylen)
 {
@@ -1100,83 +1013,35 @@ static int crypto_rfc4543_setauthsize(struct crypto_aead *parent,
        return crypto_aead_setauthsize(ctx->child, authsize);
 }
 
-static void crypto_rfc4543_done(struct crypto_async_request *areq, int err)
-{
-       struct aead_request *req = areq->data;
-       struct crypto_aead *aead = crypto_aead_reqtfm(req);
-       struct crypto_rfc4543_req_ctx *rctx = crypto_rfc4543_reqctx(req);
-
-       if (!err) {
-               scatterwalk_map_and_copy(rctx->auth_tag, req->dst,
-                                        req->cryptlen,
-                                        crypto_aead_authsize(aead), 1);
-       }
-
-       aead_request_complete(req, err);
-}
-
-static struct aead_request *crypto_rfc4543_crypt(struct aead_request *req,
-                                                bool enc)
+static int crypto_rfc4543_crypt(struct aead_request *req, bool enc)
 {
        struct crypto_aead *aead = crypto_aead_reqtfm(req);
        struct crypto_rfc4543_ctx *ctx = crypto_aead_ctx(aead);
-       struct crypto_rfc4543_req_ctx *rctx = crypto_rfc4543_reqctx(req);
+       struct crypto_rfc4543_req_ctx *rctx = aead_request_ctx(req);
        struct aead_request *subreq = &rctx->subreq;
-       struct scatterlist *src = req->src;
-       struct scatterlist *cipher = rctx->cipher;
-       struct scatterlist *payload = rctx->payload;
-       struct scatterlist *assoc = rctx->assoc;
        unsigned int authsize = crypto_aead_authsize(aead);
-       unsigned int assoclen = req->assoclen;
-       struct page *srcp;
-       u8 *vsrc;
        u8 *iv = PTR_ALIGN((u8 *)(rctx + 1) + crypto_aead_reqsize(ctx->child),
                           crypto_aead_alignmask(ctx->child) + 1);
+       int err;
+
+       if (req->src != req->dst) {
+               err = crypto_rfc4543_copy_src_to_dst(req, enc);
+               if (err)
+                       return err;
+       }
 
        memcpy(iv, ctx->nonce, 4);
        memcpy(iv + 4, req->iv, 8);
 
-       /* construct cipher/plaintext */
-       if (enc)
-               memset(rctx->auth_tag, 0, authsize);
-       else
-               scatterwalk_map_and_copy(rctx->auth_tag, src,
-                                        req->cryptlen - authsize,
-                                        authsize, 0);
-
-       sg_init_one(cipher, rctx->auth_tag, authsize);
-
-       /* construct the aad */
-       srcp = sg_page(src);
-       vsrc = PageHighMem(srcp) ? NULL : page_address(srcp) + src->offset;
-
-       sg_init_table(payload, 2);
-       sg_set_buf(payload, req->iv, 8);
-       scatterwalk_crypto_chain(payload, src, vsrc == req->iv + 8, 2);
-       assoclen += 8 + req->cryptlen - (enc ? 0 : authsize);
-
-       if (req->assoc->length == req->assoclen) {
-               sg_init_table(assoc, 2);
-               sg_set_page(assoc, sg_page(req->assoc), req->assoc->length,
-                           req->assoc->offset);
-       } else {
-               BUG_ON(req->assoclen > sizeof(rctx->assocbuf));
-
-               scatterwalk_map_and_copy(rctx->assocbuf, req->assoc, 0,
-                                        req->assoclen, 0);
-
-               sg_init_table(assoc, 2);
-               sg_set_buf(assoc, rctx->assocbuf, req->assoclen);
-       }
-       scatterwalk_crypto_chain(assoc, payload, 0, 2);
-
        aead_request_set_tfm(subreq, ctx->child);
-       aead_request_set_callback(subreq, req->base.flags, crypto_rfc4543_done,
-                                 req);
-       aead_request_set_crypt(subreq, cipher, cipher, enc ? 0 : authsize, iv);
-       aead_request_set_assoc(subreq, assoc, assoclen);
-
-       return subreq;
+       aead_request_set_callback(subreq, req->base.flags,
+                                 req->base.complete, req->base.data);
+       aead_request_set_crypt(subreq, req->src, req->dst,
+                              enc ? 0 : authsize, iv);
+       aead_request_set_ad(subreq, req->assoclen + req->cryptlen -
+                                   subreq->cryptlen);
+
+       return enc ? crypto_aead_encrypt(subreq) : crypto_aead_decrypt(subreq);
 }
 
 static int crypto_rfc4543_copy_src_to_dst(struct aead_request *req, bool enc)
@@ -1184,7 +1049,8 @@ static int crypto_rfc4543_copy_src_to_dst(struct aead_request *req, bool enc)
        struct crypto_aead *aead = crypto_aead_reqtfm(req);
        struct crypto_rfc4543_ctx *ctx = crypto_aead_ctx(aead);
        unsigned int authsize = crypto_aead_authsize(aead);
-       unsigned int nbytes = req->cryptlen - (enc ? 0 : authsize);
+       unsigned int nbytes = req->assoclen + req->cryptlen -
+                             (enc ? 0 : authsize);
        struct blkcipher_desc desc = {
                .tfm = ctx->null,
        };
@@ -1194,49 +1060,20 @@ static int crypto_rfc4543_copy_src_to_dst(struct aead_request *req, bool enc)
 
 static int crypto_rfc4543_encrypt(struct aead_request *req)
 {
-       struct crypto_aead *aead = crypto_aead_reqtfm(req);
-       struct crypto_rfc4543_req_ctx *rctx = crypto_rfc4543_reqctx(req);
-       struct aead_request *subreq;
-       int err;
-
-       if (req->src != req->dst) {
-               err = crypto_rfc4543_copy_src_to_dst(req, true);
-               if (err)
-                       return err;
-       }
-
-       subreq = crypto_rfc4543_crypt(req, true);
-       err = crypto_aead_encrypt(subreq);
-       if (err)
-               return err;
-
-       scatterwalk_map_and_copy(rctx->auth_tag, req->dst, req->cryptlen,
-                                crypto_aead_authsize(aead), 1);
-
-       return 0;
+       return crypto_rfc4543_crypt(req, true);
 }
 
 static int crypto_rfc4543_decrypt(struct aead_request *req)
 {
-       int err;
-
-       if (req->src != req->dst) {
-               err = crypto_rfc4543_copy_src_to_dst(req, false);
-               if (err)
-                       return err;
-       }
-
-       req = crypto_rfc4543_crypt(req, false);
-
-       return crypto_aead_decrypt(req);
+       return crypto_rfc4543_crypt(req, false);
 }
 
-static int crypto_rfc4543_init_tfm(struct crypto_tfm *tfm)
+static int crypto_rfc4543_init_tfm(struct crypto_aead *tfm)
 {
-       struct crypto_instance *inst = (void *)tfm->__crt_alg;
-       struct crypto_rfc4543_instance_ctx *ictx = crypto_instance_ctx(inst);
+       struct aead_instance *inst = aead_alg_instance(tfm);
+       struct crypto_rfc4543_instance_ctx *ictx = aead_instance_ctx(inst);
        struct crypto_aead_spawn *spawn = &ictx->aead;
-       struct crypto_rfc4543_ctx *ctx = crypto_tfm_ctx(tfm);
+       struct crypto_rfc4543_ctx *ctx = crypto_aead_ctx(tfm);
        struct crypto_aead *aead;
        struct crypto_blkcipher *null;
        unsigned long align;
@@ -1246,7 +1083,7 @@ static int crypto_rfc4543_init_tfm(struct crypto_tfm *tfm)
        if (IS_ERR(aead))
                return PTR_ERR(aead);
 
-       null = crypto_spawn_blkcipher(&ictx->null.base);
+       null = crypto_get_default_null_skcipher();
        err = PTR_ERR(null);
        if (IS_ERR(null))
                goto err_free_aead;
@@ -1256,10 +1093,11 @@ static int crypto_rfc4543_init_tfm(struct crypto_tfm *tfm)
 
        align = crypto_aead_alignmask(aead);
        align &= ~(crypto_tfm_ctx_alignment() - 1);
-       tfm->crt_aead.reqsize = sizeof(struct crypto_rfc4543_req_ctx) +
-                               ALIGN(crypto_aead_reqsize(aead),
-                                     crypto_tfm_ctx_alignment()) +
-                               align + 16;
+       crypto_aead_set_reqsize(
+               tfm,
+               sizeof(struct crypto_rfc4543_req_ctx) +
+               ALIGN(crypto_aead_reqsize(aead), crypto_tfm_ctx_alignment()) +
+               align + 12);
 
        return 0;
 
@@ -1268,107 +1106,98 @@ err_free_aead:
        return err;
 }
 
-static void crypto_rfc4543_exit_tfm(struct crypto_tfm *tfm)
+static void crypto_rfc4543_exit_tfm(struct crypto_aead *tfm)
 {
-       struct crypto_rfc4543_ctx *ctx = crypto_tfm_ctx(tfm);
+       struct crypto_rfc4543_ctx *ctx = crypto_aead_ctx(tfm);
 
        crypto_free_aead(ctx->child);
-       crypto_free_blkcipher(ctx->null);
+       crypto_put_default_null_skcipher();
 }
 
-static struct crypto_instance *crypto_rfc4543_alloc(struct rtattr **tb)
+static int crypto_rfc4543_create(struct crypto_template *tmpl,
+                               struct rtattr **tb)
 {
        struct crypto_attr_type *algt;
-       struct crypto_instance *inst;
+       struct aead_instance *inst;
        struct crypto_aead_spawn *spawn;
-       struct crypto_alg *alg;
+       struct aead_alg *alg;
        struct crypto_rfc4543_instance_ctx *ctx;
        const char *ccm_name;
        int err;
 
        algt = crypto_get_attr_type(tb);
        if (IS_ERR(algt))
-               return ERR_CAST(algt);
+               return PTR_ERR(algt);
 
        if ((algt->type ^ CRYPTO_ALG_TYPE_AEAD) & algt->mask)
-               return ERR_PTR(-EINVAL);
+               return -EINVAL;
 
        ccm_name = crypto_attr_alg_name(tb[1]);
        if (IS_ERR(ccm_name))
-               return ERR_CAST(ccm_name);
+               return PTR_ERR(ccm_name);
 
        inst = kzalloc(sizeof(*inst) + sizeof(*ctx), GFP_KERNEL);
        if (!inst)
-               return ERR_PTR(-ENOMEM);
+               return -ENOMEM;
 
-       ctx = crypto_instance_ctx(inst);
+       ctx = aead_instance_ctx(inst);
        spawn = &ctx->aead;
-       crypto_set_aead_spawn(spawn, inst);
+       crypto_set_aead_spawn(spawn, aead_crypto_instance(inst));
        err = crypto_grab_aead(spawn, ccm_name, 0,
                               crypto_requires_sync(algt->type, algt->mask));
        if (err)
                goto out_free_inst;
 
-       alg = crypto_aead_spawn_alg(spawn);
-
-       crypto_set_skcipher_spawn(&ctx->null, inst);
-       err = crypto_grab_skcipher(&ctx->null, "ecb(cipher_null)", 0,
-                                  CRYPTO_ALG_ASYNC);
-       if (err)
-               goto out_drop_alg;
-
-       crypto_skcipher_spawn_alg(&ctx->null);
+       alg = crypto_spawn_aead_alg(spawn);
 
        err = -EINVAL;
 
-       /* We only support 16-byte blocks. */
-       if (alg->cra_aead.ivsize != 16)
-               goto out_drop_ecbnull;
+       /* Underlying IV size must be 12. */
+       if (crypto_aead_alg_ivsize(alg) != 12)
+               goto out_drop_alg;
 
        /* Not a stream cipher? */
-       if (alg->cra_blocksize != 1)
-               goto out_drop_ecbnull;
+       if (alg->base.cra_blocksize != 1)
+               goto out_drop_alg;
 
        err = -ENAMETOOLONG;
-       if (snprintf(inst->alg.cra_name, CRYPTO_MAX_ALG_NAME,
-                    "rfc4543(%s)", alg->cra_name) >= CRYPTO_MAX_ALG_NAME ||
-           snprintf(inst->alg.cra_driver_name, CRYPTO_MAX_ALG_NAME,
-                    "rfc4543(%s)", alg->cra_driver_name) >=
+       if (snprintf(inst->alg.base.cra_name, CRYPTO_MAX_ALG_NAME,
+                    "rfc4543(%s)", alg->base.cra_name) >=
+           CRYPTO_MAX_ALG_NAME ||
+           snprintf(inst->alg.base.cra_driver_name, CRYPTO_MAX_ALG_NAME,
+                    "rfc4543(%s)", alg->base.cra_driver_name) >=
            CRYPTO_MAX_ALG_NAME)
-               goto out_drop_ecbnull;
+               goto out_drop_alg;
 
-       inst->alg.cra_flags = CRYPTO_ALG_TYPE_AEAD;
-       inst->alg.cra_flags |= alg->cra_flags & CRYPTO_ALG_ASYNC;
-       inst->alg.cra_priority = alg->cra_priority;
-       inst->alg.cra_blocksize = 1;
-       inst->alg.cra_alignmask = alg->cra_alignmask;
-       inst->alg.cra_type = &crypto_nivaead_type;
+       inst->alg.base.cra_flags = alg->base.cra_flags & CRYPTO_ALG_ASYNC;
+       inst->alg.base.cra_priority = alg->base.cra_priority;
+       inst->alg.base.cra_blocksize = 1;
+       inst->alg.base.cra_alignmask = alg->base.cra_alignmask;
 
-       inst->alg.cra_aead.ivsize = 8;
-       inst->alg.cra_aead.maxauthsize = 16;
+       inst->alg.base.cra_ctxsize = sizeof(struct crypto_rfc4543_ctx);
 
-       inst->alg.cra_ctxsize = sizeof(struct crypto_rfc4543_ctx);
+       inst->alg.ivsize = 8;
+       inst->alg.maxauthsize = crypto_aead_alg_maxauthsize(alg);
 
-       inst->alg.cra_init = crypto_rfc4543_init_tfm;
-       inst->alg.cra_exit = crypto_rfc4543_exit_tfm;
+       inst->alg.init = crypto_rfc4543_init_tfm;
+       inst->alg.exit = crypto_rfc4543_exit_tfm;
 
-       inst->alg.cra_aead.setkey = crypto_rfc4543_setkey;
-       inst->alg.cra_aead.setauthsize = crypto_rfc4543_setauthsize;
-       inst->alg.cra_aead.encrypt = crypto_rfc4543_encrypt;
-       inst->alg.cra_aead.decrypt = crypto_rfc4543_decrypt;
+       inst->alg.setkey = crypto_rfc4543_setkey;
+       inst->alg.setauthsize = crypto_rfc4543_setauthsize;
+       inst->alg.encrypt = crypto_rfc4543_encrypt;
+       inst->alg.decrypt = crypto_rfc4543_decrypt;
 
-       inst->alg.cra_aead.geniv = "seqiv";
+       err = aead_register_instance(tmpl, inst);
+       if (err)
+               goto out_drop_alg;
 
 out:
-       return inst;
+       return err;
 
-out_drop_ecbnull:
-       crypto_drop_skcipher(&ctx->null);
 out_drop_alg:
        crypto_drop_aead(spawn);
 out_free_inst:
        kfree(inst);
-       inst = ERR_PTR(err);
        goto out;
 }
 
@@ -1377,14 +1206,13 @@ static void crypto_rfc4543_free(struct crypto_instance *inst)
        struct crypto_rfc4543_instance_ctx *ctx = crypto_instance_ctx(inst);
 
        crypto_drop_aead(&ctx->aead);
-       crypto_drop_skcipher(&ctx->null);
 
-       kfree(inst);
+       kfree(aead_instance(inst));
 }
 
 static struct crypto_template crypto_rfc4543_tmpl = {
        .name = "rfc4543",
-       .alloc = crypto_rfc4543_alloc,
+       .create = crypto_rfc4543_create,
        .free = crypto_rfc4543_free,
        .module = THIS_MODULE,
 };
@@ -1393,10 +1221,12 @@ static int __init crypto_gcm_module_init(void)
 {
        int err;
 
-       gcm_zeroes = kzalloc(16, GFP_KERNEL);
+       gcm_zeroes = kzalloc(sizeof(*gcm_zeroes), GFP_KERNEL);
        if (!gcm_zeroes)
                return -ENOMEM;
 
+       sg_init_one(&gcm_zeroes->sg, gcm_zeroes->buf, sizeof(gcm_zeroes->buf));
+
        err = crypto_register_template(&crypto_gcm_base_tmpl);
        if (err)
                goto out;
index bd39bfc92eabc1acf465e1cf8614d644eaafb402..00e42a3ed81431638b78a8df1616de978da1eaa6 100644 (file)
@@ -25,7 +25,6 @@
 #include <linux/notifier.h>
 #include <linux/rwsem.h>
 #include <linux/slab.h>
-#include <linux/fips.h>
 
 /* Crypto notification events. */
 enum {
@@ -103,6 +102,8 @@ int crypto_register_notifier(struct notifier_block *nb);
 int crypto_unregister_notifier(struct notifier_block *nb);
 int crypto_probing_notify(unsigned long val, void *v);
 
+unsigned int crypto_alg_extsize(struct crypto_alg *alg);
+
 static inline struct crypto_alg *crypto_alg_get(struct crypto_alg *alg)
 {
        atomic_inc(&alg->cra_refcnt);
diff --git a/crypto/jitterentropy.c b/crypto/jitterentropy.c
new file mode 100644 (file)
index 0000000..d3c3045
--- /dev/null
@@ -0,0 +1,928 @@
+/*
+ * Non-physical true random number generator based on timing jitter.
+ *
+ * Copyright Stephan Mueller <smueller@chronox.de>, 2014
+ *
+ * Design
+ * ======
+ *
+ * See http://www.chronox.de/jent.html
+ *
+ * License
+ * =======
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, and the entire permission notice in its entirety,
+ *    including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ *    products derived from this software without specific prior
+ *    written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU General Public License, in which case the provisions of the GPL2 are
+ * required INSTEAD OF the above restrictions.  (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+/*
+ * This Jitterentropy RNG is based on the jitterentropy library
+ * version 1.1.0 provided at http://www.chronox.de/jent.html
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/fips.h>
+#include <linux/time.h>
+#include <linux/crypto.h>
+#include <crypto/internal/rng.h>
+
+/* The entropy pool */
+struct rand_data {
+       /* all data values that are vital to maintain the security
+        * of the RNG are marked as SENSITIVE. A user must not
+        * access that information while the RNG executes its loops to
+        * calculate the next random value. */
+       __u64 data;             /* SENSITIVE Actual random number */
+       __u64 old_data;         /* SENSITIVE Previous random number */
+       __u64 prev_time;        /* SENSITIVE Previous time stamp */
+#define DATA_SIZE_BITS ((sizeof(__u64)) * 8)
+       __u64 last_delta;       /* SENSITIVE stuck test */
+       __s64 last_delta2;      /* SENSITIVE stuck test */
+       unsigned int stuck:1;   /* Time measurement stuck */
+       unsigned int osr;       /* Oversample rate */
+       unsigned int stir:1;            /* Post-processing stirring */
+       unsigned int disable_unbias:1;  /* Deactivate Von-Neuman unbias */
+#define JENT_MEMORY_BLOCKS 64
+#define JENT_MEMORY_BLOCKSIZE 32
+#define JENT_MEMORY_ACCESSLOOPS 128
+#define JENT_MEMORY_SIZE (JENT_MEMORY_BLOCKS*JENT_MEMORY_BLOCKSIZE)
+       unsigned char *mem;     /* Memory access location with size of
+                                * memblocks * memblocksize */
+       unsigned int memlocation; /* Pointer to byte in *mem */
+       unsigned int memblocks; /* Number of memory blocks in *mem */
+       unsigned int memblocksize; /* Size of one memory block in bytes */
+       unsigned int memaccessloops; /* Number of memory accesses per random
+                                     * bit generation */
+};
+
+/* Flags that can be used to initialize the RNG */
+#define JENT_DISABLE_STIR (1<<0) /* Disable stirring the entropy pool */
+#define JENT_DISABLE_UNBIAS (1<<1) /* Disable the Von-Neuman Unbiaser */
+#define JENT_DISABLE_MEMORY_ACCESS (1<<2) /* Disable memory access for more
+                                          * entropy, saves MEMORY_SIZE RAM for
+                                          * entropy collector */
+
+#define DRIVER_NAME     "jitterentropy"
+
+/* -- error codes for init function -- */
+#define JENT_ENOTIME           1 /* Timer service not available */
+#define JENT_ECOARSETIME       2 /* Timer too coarse for RNG */
+#define JENT_ENOMONOTONIC      3 /* Timer is not monotonic increasing */
+#define JENT_EMINVARIATION     4 /* Timer variations too small for RNG */
+#define JENT_EVARVAR           5 /* Timer does not produce variations of
+                                  * variations (2nd derivation of time is
+                                  * zero). */
+#define JENT_EMINVARVAR                6 /* Timer variations of variations is tooi
+                                  * small. */
+
+/***************************************************************************
+ * Helper functions
+ ***************************************************************************/
+
+static inline void jent_get_nstime(__u64 *out)
+{
+       struct timespec ts;
+       __u64 tmp = 0;
+
+       tmp = random_get_entropy();
+
+       /*
+        * If random_get_entropy does not return a value (which is possible on,
+        * for example, MIPS), invoke __getnstimeofday
+        * hoping that there are timers we can work with.
+        *
+        * The list of available timers can be obtained from
+        * /sys/devices/system/clocksource/clocksource0/available_clocksource
+        * and are registered with clocksource_register()
+        */
+       if ((0 == tmp) &&
+          (0 == __getnstimeofday(&ts))) {
+               tmp = ts.tv_sec;
+               tmp = tmp << 32;
+               tmp = tmp | ts.tv_nsec;
+       }
+
+       *out = tmp;
+}
+
+
+/**
+ * Update of the loop count used for the next round of
+ * an entropy collection.
+ *
+ * Input:
+ * @ec entropy collector struct -- may be NULL
+ * @bits is the number of low bits of the timer to consider
+ * @min is the number of bits we shift the timer value to the right at
+ *     the end to make sure we have a guaranteed minimum value
+ *
+ * @return Newly calculated loop counter
+ */
+static __u64 jent_loop_shuffle(struct rand_data *ec,
+                              unsigned int bits, unsigned int min)
+{
+       __u64 time = 0;
+       __u64 shuffle = 0;
+       unsigned int i = 0;
+       unsigned int mask = (1<<bits) - 1;
+
+       jent_get_nstime(&time);
+       /*
+        * mix the current state of the random number into the shuffle
+        * calculation to balance that shuffle a bit more
+        */
+       if (ec)
+               time ^= ec->data;
+       /*
+        * we fold the time value as much as possible to ensure that as many
+        * bits of the time stamp are included as possible
+        */
+       for (i = 0; (DATA_SIZE_BITS / bits) > i; i++) {
+               shuffle ^= time & mask;
+               time = time >> bits;
+       }
+
+       /*
+        * We add a lower boundary value to ensure we have a minimum
+        * RNG loop count.
+        */
+       return (shuffle + (1<<min));
+}
+
+/***************************************************************************
+ * Noise sources
+ ***************************************************************************/
+
+/*
+ * The disabling of the optimizations is performed as documented and assessed
+ * thoroughly in http://www.chronox.de/jent.html. However, instead of disabling
+ * the optimization of the entire C file, only the main functions the jitter is
+ * measured for are not optimized. These functions include the noise sources as
+ * well as the main functions triggering the noise sources. As the time
+ * measurement is done from one invocation of the jitter noise source to the
+ * next, even the execution jitter of the code invoking the noise sources
+ * contribute to the overall randomness as well. The behavior of the RNG and the
+ * statistical characteristics when only the mentioned functions are not
+ * optimized is almost equal to the a completely non-optimized RNG compilation
+ * as tested with the test tools provided at the initially mentioned web site.
+ */
+
+/**
+ * CPU Jitter noise source -- this is the noise source based on the CPU
+ *                           execution time jitter
+ *
+ * This function folds the time into one bit units by iterating
+ * through the DATA_SIZE_BITS bit time value as follows: assume our time value
+ * is 0xabcd
+ * 1st loop, 1st shift generates 0xd000
+ * 1st loop, 2nd shift generates 0x000d
+ * 2nd loop, 1st shift generates 0xcd00
+ * 2nd loop, 2nd shift generates 0x000c
+ * 3rd loop, 1st shift generates 0xbcd0
+ * 3rd loop, 2nd shift generates 0x000b
+ * 4th loop, 1st shift generates 0xabcd
+ * 4th loop, 2nd shift generates 0x000a
+ * Now, the values at the end of the 2nd shifts are XORed together.
+ *
+ * The code is deliberately inefficient and shall stay that way. This function
+ * is the root cause why the code shall be compiled without optimization. This
+ * function not only acts as folding operation, but this function's execution
+ * is used to measure the CPU execution time jitter. Any change to the loop in
+ * this function implies that careful retesting must be done.
+ *
+ * Input:
+ * @ec entropy collector struct -- may be NULL
+ * @time time stamp to be folded
+ * @loop_cnt if a value not equal to 0 is set, use the given value as number of
+ *          loops to perform the folding
+ *
+ * Output:
+ * @folded result of folding operation
+ *
+ * @return Number of loops the folding operation is performed
+ */
+#pragma GCC push_options
+#pragma GCC optimize ("-O0")
+static __u64 jent_fold_time(struct rand_data *ec, __u64 time,
+                           __u64 *folded, __u64 loop_cnt)
+{
+       unsigned int i;
+       __u64 j = 0;
+       __u64 new = 0;
+#define MAX_FOLD_LOOP_BIT 4
+#define MIN_FOLD_LOOP_BIT 0
+       __u64 fold_loop_cnt =
+               jent_loop_shuffle(ec, MAX_FOLD_LOOP_BIT, MIN_FOLD_LOOP_BIT);
+
+       /*
+        * testing purposes -- allow test app to set the counter, not
+        * needed during runtime
+        */
+       if (loop_cnt)
+               fold_loop_cnt = loop_cnt;
+       for (j = 0; j < fold_loop_cnt; j++) {
+               new = 0;
+               for (i = 1; (DATA_SIZE_BITS) >= i; i++) {
+                       __u64 tmp = time << (DATA_SIZE_BITS - i);
+
+                       tmp = tmp >> (DATA_SIZE_BITS - 1);
+                       new ^= tmp;
+               }
+       }
+       *folded = new;
+       return fold_loop_cnt;
+}
+#pragma GCC pop_options
+
+/**
+ * Memory Access noise source -- this is a noise source based on variations in
+ *                              memory access times
+ *
+ * This function performs memory accesses which will add to the timing
+ * variations due to an unknown amount of CPU wait states that need to be
+ * added when accessing memory. The memory size should be larger than the L1
+ * caches as outlined in the documentation and the associated testing.
+ *
+ * The L1 cache has a very high bandwidth, albeit its access rate is  usually
+ * slower than accessing CPU registers. Therefore, L1 accesses only add minimal
+ * variations as the CPU has hardly to wait. Starting with L2, significant
+ * variations are added because L2 typically does not belong to the CPU any more
+ * and therefore a wider range of CPU wait states is necessary for accesses.
+ * L3 and real memory accesses have even a wider range of wait states. However,
+ * to reliably access either L3 or memory, the ec->mem memory must be quite
+ * large which is usually not desirable.
+ *
+ * Input:
+ * @ec Reference to the entropy collector with the memory access data -- if
+ *     the reference to the memory block to be accessed is NULL, this noise
+ *     source is disabled
+ * @loop_cnt if a value not equal to 0 is set, use the given value as number of
+ *          loops to perform the folding
+ *
+ * @return Number of memory access operations
+ */
+#pragma GCC push_options
+#pragma GCC optimize ("-O0")
+static unsigned int jent_memaccess(struct rand_data *ec, __u64 loop_cnt)
+{
+       unsigned char *tmpval = NULL;
+       unsigned int wrap = 0;
+       __u64 i = 0;
+#define MAX_ACC_LOOP_BIT 7
+#define MIN_ACC_LOOP_BIT 0
+       __u64 acc_loop_cnt =
+               jent_loop_shuffle(ec, MAX_ACC_LOOP_BIT, MIN_ACC_LOOP_BIT);
+
+       if (NULL == ec || NULL == ec->mem)
+               return 0;
+       wrap = ec->memblocksize * ec->memblocks;
+
+       /*
+        * testing purposes -- allow test app to set the counter, not
+        * needed during runtime
+        */
+       if (loop_cnt)
+               acc_loop_cnt = loop_cnt;
+
+       for (i = 0; i < (ec->memaccessloops + acc_loop_cnt); i++) {
+               tmpval = ec->mem + ec->memlocation;
+               /*
+                * memory access: just add 1 to one byte,
+                * wrap at 255 -- memory access implies read
+                * from and write to memory location
+                */
+               *tmpval = (*tmpval + 1) & 0xff;
+               /*
+                * Addition of memblocksize - 1 to pointer
+                * with wrap around logic to ensure that every
+                * memory location is hit evenly
+                */
+               ec->memlocation = ec->memlocation + ec->memblocksize - 1;
+               ec->memlocation = ec->memlocation % wrap;
+       }
+       return i;
+}
+#pragma GCC pop_options
+
+/***************************************************************************
+ * Start of entropy processing logic
+ ***************************************************************************/
+
+/**
+ * Stuck test by checking the:
+ *     1st derivation of the jitter measurement (time delta)
+ *     2nd derivation of the jitter measurement (delta of time deltas)
+ *     3rd derivation of the jitter measurement (delta of delta of time deltas)
+ *
+ * All values must always be non-zero.
+ *
+ * Input:
+ * @ec Reference to entropy collector
+ * @current_delta Jitter time delta
+ *
+ * @return
+ *     0 jitter measurement not stuck (good bit)
+ *     1 jitter measurement stuck (reject bit)
+ */
+static void jent_stuck(struct rand_data *ec, __u64 current_delta)
+{
+       __s64 delta2 = ec->last_delta - current_delta;
+       __s64 delta3 = delta2 - ec->last_delta2;
+
+       ec->last_delta = current_delta;
+       ec->last_delta2 = delta2;
+
+       if (!current_delta || !delta2 || !delta3)
+               ec->stuck = 1;
+}
+
+/**
+ * This is the heart of the entropy generation: calculate time deltas and
+ * use the CPU jitter in the time deltas. The jitter is folded into one
+ * bit. You can call this function the "random bit generator" as it
+ * produces one random bit per invocation.
+ *
+ * WARNING: ensure that ->prev_time is primed before using the output
+ *         of this function! This can be done by calling this function
+ *         and not using its result.
+ *
+ * Input:
+ * @entropy_collector Reference to entropy collector
+ *
+ * @return One random bit
+ */
+#pragma GCC push_options
+#pragma GCC optimize ("-O0")
+static __u64 jent_measure_jitter(struct rand_data *ec)
+{
+       __u64 time = 0;
+       __u64 data = 0;
+       __u64 current_delta = 0;
+
+       /* Invoke one noise source before time measurement to add variations */
+       jent_memaccess(ec, 0);
+
+       /*
+        * Get time stamp and calculate time delta to previous
+        * invocation to measure the timing variations
+        */
+       jent_get_nstime(&time);
+       current_delta = time - ec->prev_time;
+       ec->prev_time = time;
+
+       /* Now call the next noise sources which also folds the data */
+       jent_fold_time(ec, current_delta, &data, 0);
+
+       /*
+        * Check whether we have a stuck measurement. The enforcement
+        * is performed after the stuck value has been mixed into the
+        * entropy pool.
+        */
+       jent_stuck(ec, current_delta);
+
+       return data;
+}
+#pragma GCC pop_options
+
+/**
+ * Von Neuman unbias as explained in RFC 4086 section 4.2. As shown in the
+ * documentation of that RNG, the bits from jent_measure_jitter are considered
+ * independent which implies that the Von Neuman unbias operation is applicable.
+ * A proof of the Von-Neumann unbias operation to remove skews is given in the
+ * document "A proposal for: Functionality classes for random number
+ * generators", version 2.0 by Werner Schindler, section 5.4.1.
+ *
+ * Input:
+ * @entropy_collector Reference to entropy collector
+ *
+ * @return One random bit
+ */
+static __u64 jent_unbiased_bit(struct rand_data *entropy_collector)
+{
+       do {
+               __u64 a = jent_measure_jitter(entropy_collector);
+               __u64 b = jent_measure_jitter(entropy_collector);
+
+               if (a == b)
+                       continue;
+               if (1 == a)
+                       return 1;
+               else
+                       return 0;
+       } while (1);
+}
+
+/**
+ * Shuffle the pool a bit by mixing some value with a bijective function (XOR)
+ * into the pool.
+ *
+ * The function generates a mixer value that depends on the bits set and the
+ * location of the set bits in the random number generated by the entropy
+ * source. Therefore, based on the generated random number, this mixer value
+ * can have 2**64 different values. That mixer value is initialized with the
+ * first two SHA-1 constants. After obtaining the mixer value, it is XORed into
+ * the random number.
+ *
+ * The mixer value is not assumed to contain any entropy. But due to the XOR
+ * operation, it can also not destroy any entropy present in the entropy pool.
+ *
+ * Input:
+ * @entropy_collector Reference to entropy collector
+ */
+static void jent_stir_pool(struct rand_data *entropy_collector)
+{
+       /*
+        * to shut up GCC on 32 bit, we have to initialize the 64 variable
+        * with two 32 bit variables
+        */
+       union c {
+               __u64 u64;
+               __u32 u32[2];
+       };
+       /*
+        * This constant is derived from the first two 32 bit initialization
+        * vectors of SHA-1 as defined in FIPS 180-4 section 5.3.1
+        */
+       union c constant;
+       /*
+        * The start value of the mixer variable is derived from the third
+        * and fourth 32 bit initialization vector of SHA-1 as defined in
+        * FIPS 180-4 section 5.3.1
+        */
+       union c mixer;
+       unsigned int i = 0;
+
+       /*
+        * Store the SHA-1 constants in reverse order to make up the 64 bit
+        * value -- this applies to a little endian system, on a big endian
+        * system, it reverses as expected. But this really does not matter
+        * as we do not rely on the specific numbers. We just pick the SHA-1
+        * constants as they have a good mix of bit set and unset.
+        */
+       constant.u32[1] = 0x67452301;
+       constant.u32[0] = 0xefcdab89;
+       mixer.u32[1] = 0x98badcfe;
+       mixer.u32[0] = 0x10325476;
+
+       for (i = 0; i < DATA_SIZE_BITS; i++) {
+               /*
+                * get the i-th bit of the input random number and only XOR
+                * the constant into the mixer value when that bit is set
+                */
+               if ((entropy_collector->data >> i) & 1)
+                       mixer.u64 ^= constant.u64;
+               mixer.u64 = rol64(mixer.u64, 1);
+       }
+       entropy_collector->data ^= mixer.u64;
+}
+
+/**
+ * Generator of one 64 bit random number
+ * Function fills rand_data->data
+ *
+ * Input:
+ * @ec Reference to entropy collector
+ */
+#pragma GCC push_options
+#pragma GCC optimize ("-O0")
+static void jent_gen_entropy(struct rand_data *ec)
+{
+       unsigned int k = 0;
+
+       /* priming of the ->prev_time value */
+       jent_measure_jitter(ec);
+
+       while (1) {
+               __u64 data = 0;
+
+               if (ec->disable_unbias == 1)
+                       data = jent_measure_jitter(ec);
+               else
+                       data = jent_unbiased_bit(ec);
+
+               /* enforcement of the jent_stuck test */
+               if (ec->stuck) {
+                       /*
+                        * We only mix in the bit considered not appropriate
+                        * without the LSFR. The reason is that if we apply
+                        * the LSFR and we do not rotate, the 2nd bit with LSFR
+                        * will cancel out the first LSFR application on the
+                        * bad bit.
+                        *
+                        * And we do not rotate as we apply the next bit to the
+                        * current bit location again.
+                        */
+                       ec->data ^= data;
+                       ec->stuck = 0;
+                       continue;
+               }
+
+               /*
+                * Fibonacci LSFR with polynom of
+                *  x^64 + x^61 + x^56 + x^31 + x^28 + x^23 + 1 which is
+                *  primitive according to
+                *   http://poincare.matf.bg.ac.rs/~ezivkovm/publications/primpol1.pdf
+                * (the shift values are the polynom values minus one
+                * due to counting bits from 0 to 63). As the current
+                * position is always the LSB, the polynom only needs
+                * to shift data in from the left without wrap.
+                */
+               ec->data ^= data;
+               ec->data ^= ((ec->data >> 63) & 1);
+               ec->data ^= ((ec->data >> 60) & 1);
+               ec->data ^= ((ec->data >> 55) & 1);
+               ec->data ^= ((ec->data >> 30) & 1);
+               ec->data ^= ((ec->data >> 27) & 1);
+               ec->data ^= ((ec->data >> 22) & 1);
+               ec->data = rol64(ec->data, 1);
+
+               /*
+                * We multiply the loop value with ->osr to obtain the
+                * oversampling rate requested by the caller
+                */
+               if (++k >= (DATA_SIZE_BITS * ec->osr))
+                       break;
+       }
+       if (ec->stir)
+               jent_stir_pool(ec);
+}
+#pragma GCC pop_options
+
+/**
+ * The continuous test required by FIPS 140-2 -- the function automatically
+ * primes the test if needed.
+ *
+ * Return:
+ * 0 if FIPS test passed
+ * < 0 if FIPS test failed
+ */
+static void jent_fips_test(struct rand_data *ec)
+{
+       if (!fips_enabled)
+               return;
+
+       /* prime the FIPS test */
+       if (!ec->old_data) {
+               ec->old_data = ec->data;
+               jent_gen_entropy(ec);
+       }
+
+       if (ec->data == ec->old_data)
+               panic(DRIVER_NAME ": Duplicate output detected\n");
+
+       ec->old_data = ec->data;
+}
+
+
+/**
+ * Entry function: Obtain entropy for the caller.
+ *
+ * This function invokes the entropy gathering logic as often to generate
+ * as many bytes as requested by the caller. The entropy gathering logic
+ * creates 64 bit per invocation.
+ *
+ * This function truncates the last 64 bit entropy value output to the exact
+ * size specified by the caller.
+ *
+ * Input:
+ * @ec Reference to entropy collector
+ * @data pointer to buffer for storing random data -- buffer must already
+ *      exist
+ * @len size of the buffer, specifying also the requested number of random
+ *     in bytes
+ *
+ * @return 0 when request is fulfilled or an error
+ *
+ * The following error codes can occur:
+ *     -1      entropy_collector is NULL
+ */
+static ssize_t jent_read_entropy(struct rand_data *ec, u8 *data, size_t len)
+{
+       u8 *p = data;
+
+       if (!ec)
+               return -EINVAL;
+
+       while (0 < len) {
+               size_t tocopy;
+
+               jent_gen_entropy(ec);
+               jent_fips_test(ec);
+               if ((DATA_SIZE_BITS / 8) < len)
+                       tocopy = (DATA_SIZE_BITS / 8);
+               else
+                       tocopy = len;
+               memcpy(p, &ec->data, tocopy);
+
+               len -= tocopy;
+               p += tocopy;
+       }
+
+       return 0;
+}
+
+/***************************************************************************
+ * Initialization logic
+ ***************************************************************************/
+
+static struct rand_data *jent_entropy_collector_alloc(unsigned int osr,
+                                                     unsigned int flags)
+{
+       struct rand_data *entropy_collector;
+
+       entropy_collector = kzalloc(sizeof(struct rand_data), GFP_KERNEL);
+       if (!entropy_collector)
+               return NULL;
+
+       if (!(flags & JENT_DISABLE_MEMORY_ACCESS)) {
+               /* Allocate memory for adding variations based on memory
+                * access
+                */
+               entropy_collector->mem = kzalloc(JENT_MEMORY_SIZE, GFP_KERNEL);
+               if (!entropy_collector->mem) {
+                       kfree(entropy_collector);
+                       return NULL;
+               }
+               entropy_collector->memblocksize = JENT_MEMORY_BLOCKSIZE;
+               entropy_collector->memblocks = JENT_MEMORY_BLOCKS;
+               entropy_collector->memaccessloops = JENT_MEMORY_ACCESSLOOPS;
+       }
+
+       /* verify and set the oversampling rate */
+       if (0 == osr)
+               osr = 1; /* minimum sampling rate is 1 */
+       entropy_collector->osr = osr;
+
+       entropy_collector->stir = 1;
+       if (flags & JENT_DISABLE_STIR)
+               entropy_collector->stir = 0;
+       if (flags & JENT_DISABLE_UNBIAS)
+               entropy_collector->disable_unbias = 1;
+
+       /* fill the data pad with non-zero values */
+       jent_gen_entropy(entropy_collector);
+
+       return entropy_collector;
+}
+
+static void jent_entropy_collector_free(struct rand_data *entropy_collector)
+{
+       if (entropy_collector->mem)
+               kzfree(entropy_collector->mem);
+       entropy_collector->mem = NULL;
+       if (entropy_collector)
+               kzfree(entropy_collector);
+       entropy_collector = NULL;
+}
+
+static int jent_entropy_init(void)
+{
+       int i;
+       __u64 delta_sum = 0;
+       __u64 old_delta = 0;
+       int time_backwards = 0;
+       int count_var = 0;
+       int count_mod = 0;
+
+       /* We could perform statistical tests here, but the problem is
+        * that we only have a few loop counts to do testing. These
+        * loop counts may show some slight skew and we produce
+        * false positives.
+        *
+        * Moreover, only old systems show potentially problematic
+        * jitter entropy that could potentially be caught here. But
+        * the RNG is intended for hardware that is available or widely
+        * used, but not old systems that are long out of favor. Thus,
+        * no statistical tests.
+        */
+
+       /*
+        * We could add a check for system capabilities such as clock_getres or
+        * check for CONFIG_X86_TSC, but it does not make much sense as the
+        * following sanity checks verify that we have a high-resolution
+        * timer.
+        */
+       /*
+        * TESTLOOPCOUNT needs some loops to identify edge systems. 100 is
+        * definitely too little.
+        */
+#define TESTLOOPCOUNT 300
+#define CLEARCACHE 100
+       for (i = 0; (TESTLOOPCOUNT + CLEARCACHE) > i; i++) {
+               __u64 time = 0;
+               __u64 time2 = 0;
+               __u64 folded = 0;
+               __u64 delta = 0;
+               unsigned int lowdelta = 0;
+
+               jent_get_nstime(&time);
+               jent_fold_time(NULL, time, &folded, 1<<MIN_FOLD_LOOP_BIT);
+               jent_get_nstime(&time2);
+
+               /* test whether timer works */
+               if (!time || !time2)
+                       return JENT_ENOTIME;
+               delta = time2 - time;
+               /*
+                * test whether timer is fine grained enough to provide
+                * delta even when called shortly after each other -- this
+                * implies that we also have a high resolution timer
+                */
+               if (!delta)
+                       return JENT_ECOARSETIME;
+
+               /*
+                * up to here we did not modify any variable that will be
+                * evaluated later, but we already performed some work. Thus we
+                * already have had an impact on the caches, branch prediction,
+                * etc. with the goal to clear it to get the worst case
+                * measurements.
+                */
+               if (CLEARCACHE > i)
+                       continue;
+
+               /* test whether we have an increasing timer */
+               if (!(time2 > time))
+                       time_backwards++;
+
+               /*
+                * Avoid modulo of 64 bit integer to allow code to compile
+                * on 32 bit architectures.
+                */
+               lowdelta = time2 - time;
+               if (!(lowdelta % 100))
+                       count_mod++;
+
+               /*
+                * ensure that we have a varying delta timer which is necessary
+                * for the calculation of entropy -- perform this check
+                * only after the first loop is executed as we need to prime
+                * the old_data value
+                */
+               if (i) {
+                       if (delta != old_delta)
+                               count_var++;
+                       if (delta > old_delta)
+                               delta_sum += (delta - old_delta);
+                       else
+                               delta_sum += (old_delta - delta);
+               }
+               old_delta = delta;
+       }
+
+       /*
+        * we allow up to three times the time running backwards.
+        * CLOCK_REALTIME is affected by adjtime and NTP operations. Thus,
+        * if such an operation just happens to interfere with our test, it
+        * should not fail. The value of 3 should cover the NTP case being
+        * performed during our test run.
+        */
+       if (3 < time_backwards)
+               return JENT_ENOMONOTONIC;
+       /* Error if the time variances are always identical */
+       if (!delta_sum)
+               return JENT_EVARVAR;
+
+       /*
+        * Variations of deltas of time must on average be larger
+        * than 1 to ensure the entropy estimation
+        * implied with 1 is preserved
+        */
+       if (delta_sum <= 1)
+               return JENT_EMINVARVAR;
+
+       /*
+        * Ensure that we have variations in the time stamp below 10 for at
+        * least 10% of all checks -- on some platforms, the counter
+        * increments in multiples of 100, but not always
+        */
+       if ((TESTLOOPCOUNT/10 * 9) < count_mod)
+               return JENT_ECOARSETIME;
+
+       return 0;
+}
+
+/***************************************************************************
+ * Kernel crypto API interface
+ ***************************************************************************/
+
+struct jitterentropy {
+       spinlock_t jent_lock;
+       struct rand_data *entropy_collector;
+};
+
+static int jent_kcapi_init(struct crypto_tfm *tfm)
+{
+       struct jitterentropy *rng = crypto_tfm_ctx(tfm);
+       int ret = 0;
+
+       rng->entropy_collector = jent_entropy_collector_alloc(1, 0);
+       if (!rng->entropy_collector)
+               ret = -ENOMEM;
+
+       spin_lock_init(&rng->jent_lock);
+       return ret;
+}
+
+static void jent_kcapi_cleanup(struct crypto_tfm *tfm)
+{
+       struct jitterentropy *rng = crypto_tfm_ctx(tfm);
+
+       spin_lock(&rng->jent_lock);
+       if (rng->entropy_collector)
+               jent_entropy_collector_free(rng->entropy_collector);
+       rng->entropy_collector = NULL;
+       spin_unlock(&rng->jent_lock);
+}
+
+static int jent_kcapi_random(struct crypto_rng *tfm,
+                            const u8 *src, unsigned int slen,
+                            u8 *rdata, unsigned int dlen)
+{
+       struct jitterentropy *rng = crypto_rng_ctx(tfm);
+       int ret = 0;
+
+       spin_lock(&rng->jent_lock);
+       ret = jent_read_entropy(rng->entropy_collector, rdata, dlen);
+       spin_unlock(&rng->jent_lock);
+
+       return ret;
+}
+
+static int jent_kcapi_reset(struct crypto_rng *tfm,
+                           const u8 *seed, unsigned int slen)
+{
+       return 0;
+}
+
+static struct rng_alg jent_alg = {
+       .generate               = jent_kcapi_random,
+       .seed                   = jent_kcapi_reset,
+       .seedsize               = 0,
+       .base                   = {
+               .cra_name               = "jitterentropy_rng",
+               .cra_driver_name        = "jitterentropy_rng",
+               .cra_priority           = 100,
+               .cra_ctxsize            = sizeof(struct jitterentropy),
+               .cra_module             = THIS_MODULE,
+               .cra_init               = jent_kcapi_init,
+               .cra_exit               = jent_kcapi_cleanup,
+
+       }
+};
+
+static int __init jent_mod_init(void)
+{
+       int ret = 0;
+
+       ret = jent_entropy_init();
+       if (ret) {
+               pr_info(DRIVER_NAME ": Initialization failed with host not compliant with requirements: %d\n", ret);
+               return -EFAULT;
+       }
+       return crypto_register_rng(&jent_alg);
+}
+
+static void __exit jent_mod_exit(void)
+{
+       crypto_unregister_rng(&jent_alg);
+}
+
+module_init(jent_mod_init);
+module_exit(jent_mod_exit);
+
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_AUTHOR("Stephan Mueller <smueller@chronox.de>");
+MODULE_DESCRIPTION("Non-physical True Random Number Generator based on CPU Jitter");
+MODULE_ALIAS_CRYPTO("jitterentropy_rng");
diff --git a/crypto/krng.c b/crypto/krng.c
deleted file mode 100644 (file)
index 0224841..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * RNG implementation using standard kernel RNG.
- *
- * Copyright (c) 2008 Herbert Xu <herbert@gondor.apana.org.au>
- *
- * 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
- * any later version.
- *
- */
-
-#include <crypto/internal/rng.h>
-#include <linux/err.h>
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/random.h>
-
-static int krng_get_random(struct crypto_rng *tfm, u8 *rdata, unsigned int dlen)
-{
-       get_random_bytes(rdata, dlen);
-       return 0;
-}
-
-static int krng_reset(struct crypto_rng *tfm, u8 *seed, unsigned int slen)
-{
-       return 0;
-}
-
-static struct crypto_alg krng_alg = {
-       .cra_name               = "stdrng",
-       .cra_driver_name        = "krng",
-       .cra_priority           = 200,
-       .cra_flags              = CRYPTO_ALG_TYPE_RNG,
-       .cra_ctxsize            = 0,
-       .cra_type               = &crypto_rng_type,
-       .cra_module             = THIS_MODULE,
-       .cra_u                  = {
-               .rng = {
-                       .rng_make_random        = krng_get_random,
-                       .rng_reset              = krng_reset,
-                       .seedsize               = 0,
-               }
-       }
-};
-
-
-/* Module initalization */
-static int __init krng_mod_init(void)
-{
-       return crypto_register_alg(&krng_alg);
-}
-
-static void __exit krng_mod_fini(void)
-{
-       crypto_unregister_alg(&krng_alg);
-       return;
-}
-
-module_init(krng_mod_init);
-module_exit(krng_mod_fini);
-
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("Kernel Random Number Generator");
-MODULE_ALIAS_CRYPTO("stdrng");
-MODULE_ALIAS_CRYPTO("krng");
index 36f5e5b103f302dbeda611466fb97ab94b87511f..33d17e9a87025e7847e592371d659a58bae8d854 100644 (file)
@@ -51,10 +51,10 @@ static int md5_init(struct shash_desc *desc)
 {
        struct md5_state *mctx = shash_desc_ctx(desc);
 
-       mctx->hash[0] = 0x67452301;
-       mctx->hash[1] = 0xefcdab89;
-       mctx->hash[2] = 0x98badcfe;
-       mctx->hash[3] = 0x10325476;
+       mctx->hash[0] = MD5_H0;
+       mctx->hash[1] = MD5_H1;
+       mctx->hash[2] = MD5_H2;
+       mctx->hash[3] = MD5_H3;
        mctx->byte_count = 0;
 
        return 0;
index 7140fe70c7af04b2f544839b734de9aa5c461533..7a13b4088857bf3d6b490867450edbbd4848bacf 100644 (file)
@@ -38,11 +38,6 @@ static int crypto_pcomp_init(struct crypto_tfm *tfm, u32 type, u32 mask)
        return 0;
 }
 
-static unsigned int crypto_pcomp_extsize(struct crypto_alg *alg)
-{
-       return alg->cra_ctxsize;
-}
-
 static int crypto_pcomp_init_tfm(struct crypto_tfm *tfm)
 {
        return 0;
@@ -77,7 +72,7 @@ static void crypto_pcomp_show(struct seq_file *m, struct crypto_alg *alg)
 }
 
 static const struct crypto_type crypto_pcomp_type = {
-       .extsize        = crypto_pcomp_extsize,
+       .extsize        = crypto_alg_extsize,
        .init           = crypto_pcomp_init,
        .init_tfm       = crypto_pcomp_init_tfm,
 #ifdef CONFIG_PROC_FS
index c305d4112735cd3b7b18b477299b534b8c0e1b77..45e7d515567294506142904fd771005c8e1ca70c 100644 (file)
@@ -20,6 +20,7 @@
 
 #include <crypto/algapi.h>
 #include <crypto/internal/aead.h>
+#include <linux/atomic.h>
 #include <linux/err.h>
 #include <linux/init.h>
 #include <linux/module.h>
@@ -60,8 +61,8 @@ static struct padata_pcrypt pdecrypt;
 static struct kset           *pcrypt_kset;
 
 struct pcrypt_instance_ctx {
-       struct crypto_spawn spawn;
-       unsigned int tfm_count;
+       struct crypto_aead_spawn spawn;
+       atomic_t tfm_count;
 };
 
 struct pcrypt_aead_ctx {
@@ -122,14 +123,6 @@ static void pcrypt_aead_serial(struct padata_priv *padata)
        aead_request_complete(req->base.data, padata->info);
 }
 
-static void pcrypt_aead_giv_serial(struct padata_priv *padata)
-{
-       struct pcrypt_request *preq = pcrypt_padata_request(padata);
-       struct aead_givcrypt_request *req = pcrypt_request_ctx(preq);
-
-       aead_request_complete(req->areq.base.data, padata->info);
-}
-
 static void pcrypt_aead_done(struct crypto_async_request *areq, int err)
 {
        struct aead_request *req = areq->data;
@@ -175,7 +168,7 @@ static int pcrypt_aead_encrypt(struct aead_request *req)
                                  pcrypt_aead_done, req);
        aead_request_set_crypt(creq, req->src, req->dst,
                               req->cryptlen, req->iv);
-       aead_request_set_assoc(creq, req->assoc, req->assoclen);
+       aead_request_set_ad(creq, req->assoclen);
 
        err = pcrypt_do_parallel(padata, &ctx->cb_cpu, &pencrypt);
        if (!err)
@@ -217,7 +210,7 @@ static int pcrypt_aead_decrypt(struct aead_request *req)
                                  pcrypt_aead_done, req);
        aead_request_set_crypt(creq, req->src, req->dst,
                               req->cryptlen, req->iv);
-       aead_request_set_assoc(creq, req->assoc, req->assoclen);
+       aead_request_set_ad(creq, req->assoclen);
 
        err = pcrypt_do_parallel(padata, &ctx->cb_cpu, &pdecrypt);
        if (!err)
@@ -226,182 +219,134 @@ static int pcrypt_aead_decrypt(struct aead_request *req)
        return err;
 }
 
-static void pcrypt_aead_givenc(struct padata_priv *padata)
-{
-       struct pcrypt_request *preq = pcrypt_padata_request(padata);
-       struct aead_givcrypt_request *req = pcrypt_request_ctx(preq);
-
-       padata->info = crypto_aead_givencrypt(req);
-
-       if (padata->info == -EINPROGRESS)
-               return;
-
-       padata_do_serial(padata);
-}
-
-static int pcrypt_aead_givencrypt(struct aead_givcrypt_request *req)
-{
-       int err;
-       struct aead_request *areq = &req->areq;
-       struct pcrypt_request *preq = aead_request_ctx(areq);
-       struct aead_givcrypt_request *creq = pcrypt_request_ctx(preq);
-       struct padata_priv *padata = pcrypt_request_padata(preq);
-       struct crypto_aead *aead = aead_givcrypt_reqtfm(req);
-       struct pcrypt_aead_ctx *ctx = crypto_aead_ctx(aead);
-       u32 flags = aead_request_flags(areq);
-
-       memset(padata, 0, sizeof(struct padata_priv));
-
-       padata->parallel = pcrypt_aead_givenc;
-       padata->serial = pcrypt_aead_giv_serial;
-
-       aead_givcrypt_set_tfm(creq, ctx->child);
-       aead_givcrypt_set_callback(creq, flags & ~CRYPTO_TFM_REQ_MAY_SLEEP,
-                                  pcrypt_aead_done, areq);
-       aead_givcrypt_set_crypt(creq, areq->src, areq->dst,
-                               areq->cryptlen, areq->iv);
-       aead_givcrypt_set_assoc(creq, areq->assoc, areq->assoclen);
-       aead_givcrypt_set_giv(creq, req->giv, req->seq);
-
-       err = pcrypt_do_parallel(padata, &ctx->cb_cpu, &pencrypt);
-       if (!err)
-               return -EINPROGRESS;
-
-       return err;
-}
-
-static int pcrypt_aead_init_tfm(struct crypto_tfm *tfm)
+static int pcrypt_aead_init_tfm(struct crypto_aead *tfm)
 {
        int cpu, cpu_index;
-       struct crypto_instance *inst = crypto_tfm_alg_instance(tfm);
-       struct pcrypt_instance_ctx *ictx = crypto_instance_ctx(inst);
-       struct pcrypt_aead_ctx *ctx = crypto_tfm_ctx(tfm);
+       struct aead_instance *inst = aead_alg_instance(tfm);
+       struct pcrypt_instance_ctx *ictx = aead_instance_ctx(inst);
+       struct pcrypt_aead_ctx *ctx = crypto_aead_ctx(tfm);
        struct crypto_aead *cipher;
 
-       ictx->tfm_count++;
-
-       cpu_index = ictx->tfm_count % cpumask_weight(cpu_online_mask);
+       cpu_index = (unsigned int)atomic_inc_return(&ictx->tfm_count) %
+                   cpumask_weight(cpu_online_mask);
 
        ctx->cb_cpu = cpumask_first(cpu_online_mask);
        for (cpu = 0; cpu < cpu_index; cpu++)
                ctx->cb_cpu = cpumask_next(ctx->cb_cpu, cpu_online_mask);
 
-       cipher = crypto_spawn_aead(crypto_instance_ctx(inst));
+       cipher = crypto_spawn_aead(&ictx->spawn);
 
        if (IS_ERR(cipher))
                return PTR_ERR(cipher);
 
        ctx->child = cipher;
-       tfm->crt_aead.reqsize = sizeof(struct pcrypt_request)
-               + sizeof(struct aead_givcrypt_request)
-               + crypto_aead_reqsize(cipher);
+       crypto_aead_set_reqsize(tfm, sizeof(struct pcrypt_request) +
+                                    sizeof(struct aead_request) +
+                                    crypto_aead_reqsize(cipher));
 
        return 0;
 }
 
-static void pcrypt_aead_exit_tfm(struct crypto_tfm *tfm)
+static void pcrypt_aead_exit_tfm(struct crypto_aead *tfm)
 {
-       struct pcrypt_aead_ctx *ctx = crypto_tfm_ctx(tfm);
+       struct pcrypt_aead_ctx *ctx = crypto_aead_ctx(tfm);
 
        crypto_free_aead(ctx->child);
 }
 
-static struct crypto_instance *pcrypt_alloc_instance(struct crypto_alg *alg)
+static int pcrypt_init_instance(struct crypto_instance *inst,
+                               struct crypto_alg *alg)
 {
-       struct crypto_instance *inst;
-       struct pcrypt_instance_ctx *ctx;
-       int err;
-
-       inst = kzalloc(sizeof(*inst) + sizeof(*ctx), GFP_KERNEL);
-       if (!inst) {
-               inst = ERR_PTR(-ENOMEM);
-               goto out;
-       }
-
-       err = -ENAMETOOLONG;
        if (snprintf(inst->alg.cra_driver_name, CRYPTO_MAX_ALG_NAME,
                     "pcrypt(%s)", alg->cra_driver_name) >= CRYPTO_MAX_ALG_NAME)
-               goto out_free_inst;
+               return -ENAMETOOLONG;
 
        memcpy(inst->alg.cra_name, alg->cra_name, CRYPTO_MAX_ALG_NAME);
 
-       ctx = crypto_instance_ctx(inst);
-       err = crypto_init_spawn(&ctx->spawn, alg, inst,
-                               CRYPTO_ALG_TYPE_MASK);
-       if (err)
-               goto out_free_inst;
-
        inst->alg.cra_priority = alg->cra_priority + 100;
        inst->alg.cra_blocksize = alg->cra_blocksize;
        inst->alg.cra_alignmask = alg->cra_alignmask;
 
-out:
-       return inst;
-
-out_free_inst:
-       kfree(inst);
-       inst = ERR_PTR(err);
-       goto out;
+       return 0;
 }
 
-static struct crypto_instance *pcrypt_alloc_aead(struct rtattr **tb,
-                                                u32 type, u32 mask)
+static int pcrypt_create_aead(struct crypto_template *tmpl, struct rtattr **tb,
+                             u32 type, u32 mask)
 {
-       struct crypto_instance *inst;
-       struct crypto_alg *alg;
+       struct pcrypt_instance_ctx *ctx;
+       struct aead_instance *inst;
+       struct aead_alg *alg;
+       const char *name;
+       int err;
+
+       name = crypto_attr_alg_name(tb[1]);
+       if (IS_ERR(name))
+               return PTR_ERR(name);
+
+       inst = kzalloc(sizeof(*inst) + sizeof(*ctx), GFP_KERNEL);
+       if (!inst)
+               return -ENOMEM;
+
+       ctx = aead_instance_ctx(inst);
+       crypto_set_aead_spawn(&ctx->spawn, aead_crypto_instance(inst));
+
+       err = crypto_grab_aead(&ctx->spawn, name, 0, 0);
+       if (err)
+               goto out_free_inst;
 
-       alg = crypto_get_attr_alg(tb, type, (mask & CRYPTO_ALG_TYPE_MASK));
-       if (IS_ERR(alg))
-               return ERR_CAST(alg);
+       alg = crypto_spawn_aead_alg(&ctx->spawn);
+       err = pcrypt_init_instance(aead_crypto_instance(inst), &alg->base);
+       if (err)
+               goto out_drop_aead;
 
-       inst = pcrypt_alloc_instance(alg);
-       if (IS_ERR(inst))
-               goto out_put_alg;
+       inst->alg.ivsize = crypto_aead_alg_ivsize(alg);
+       inst->alg.maxauthsize = crypto_aead_alg_maxauthsize(alg);
 
-       inst->alg.cra_flags = CRYPTO_ALG_TYPE_AEAD | CRYPTO_ALG_ASYNC;
-       inst->alg.cra_type = &crypto_aead_type;
+       inst->alg.base.cra_ctxsize = sizeof(struct pcrypt_aead_ctx);
 
-       inst->alg.cra_aead.ivsize = alg->cra_aead.ivsize;
-       inst->alg.cra_aead.geniv = alg->cra_aead.geniv;
-       inst->alg.cra_aead.maxauthsize = alg->cra_aead.maxauthsize;
+       inst->alg.init = pcrypt_aead_init_tfm;
+       inst->alg.exit = pcrypt_aead_exit_tfm;
 
-       inst->alg.cra_ctxsize = sizeof(struct pcrypt_aead_ctx);
+       inst->alg.setkey = pcrypt_aead_setkey;
+       inst->alg.setauthsize = pcrypt_aead_setauthsize;
+       inst->alg.encrypt = pcrypt_aead_encrypt;
+       inst->alg.decrypt = pcrypt_aead_decrypt;
 
-       inst->alg.cra_init = pcrypt_aead_init_tfm;
-       inst->alg.cra_exit = pcrypt_aead_exit_tfm;
+       err = aead_register_instance(tmpl, inst);
+       if (err)
+               goto out_drop_aead;
 
-       inst->alg.cra_aead.setkey = pcrypt_aead_setkey;
-       inst->alg.cra_aead.setauthsize = pcrypt_aead_setauthsize;
-       inst->alg.cra_aead.encrypt = pcrypt_aead_encrypt;
-       inst->alg.cra_aead.decrypt = pcrypt_aead_decrypt;
-       inst->alg.cra_aead.givencrypt = pcrypt_aead_givencrypt;
+out:
+       return err;
 
-out_put_alg:
-       crypto_mod_put(alg);
-       return inst;
+out_drop_aead:
+       crypto_drop_aead(&ctx->spawn);
+out_free_inst:
+       kfree(inst);
+       goto out;
 }
 
-static struct crypto_instance *pcrypt_alloc(struct rtattr **tb)
+static int pcrypt_create(struct crypto_template *tmpl, struct rtattr **tb)
 {
        struct crypto_attr_type *algt;
 
        algt = crypto_get_attr_type(tb);
        if (IS_ERR(algt))
-               return ERR_CAST(algt);
+               return PTR_ERR(algt);
 
        switch (algt->type & algt->mask & CRYPTO_ALG_TYPE_MASK) {
        case CRYPTO_ALG_TYPE_AEAD:
-               return pcrypt_alloc_aead(tb, algt->type, algt->mask);
+               return pcrypt_create_aead(tmpl, tb, algt->type, algt->mask);
        }
 
-       return ERR_PTR(-EINVAL);
+       return -EINVAL;
 }
 
 static void pcrypt_free(struct crypto_instance *inst)
 {
        struct pcrypt_instance_ctx *ctx = crypto_instance_ctx(inst);
 
-       crypto_drop_spawn(&ctx->spawn);
+       crypto_drop_aead(&ctx->spawn);
        kfree(inst);
 }
 
@@ -516,7 +461,7 @@ static void pcrypt_fini_padata(struct padata_pcrypt *pcrypt)
 
 static struct crypto_template pcrypt_tmpl = {
        .name = "pcrypt",
-       .alloc = pcrypt_alloc,
+       .create = pcrypt_create,
        .free = pcrypt_free,
        .module = THIS_MODULE,
 };
diff --git a/crypto/poly1305_generic.c b/crypto/poly1305_generic.c
new file mode 100644 (file)
index 0000000..387b5c8
--- /dev/null
@@ -0,0 +1,321 @@
+/*
+ * Poly1305 authenticator algorithm, RFC7539
+ *
+ * Copyright (C) 2015 Martin Willi
+ *
+ * Based on public domain code by Andrew Moon and Daniel J. Bernstein.
+ *
+ * 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 <crypto/algapi.h>
+#include <crypto/internal/hash.h>
+#include <linux/crypto.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+#define POLY1305_BLOCK_SIZE    16
+#define POLY1305_KEY_SIZE      32
+#define POLY1305_DIGEST_SIZE   16
+
+struct poly1305_desc_ctx {
+       /* key */
+       u32 r[5];
+       /* finalize key */
+       u32 s[4];
+       /* accumulator */
+       u32 h[5];
+       /* partial buffer */
+       u8 buf[POLY1305_BLOCK_SIZE];
+       /* bytes used in partial buffer */
+       unsigned int buflen;
+       /* r key has been set */
+       bool rset;
+       /* s key has been set */
+       bool sset;
+};
+
+static inline u64 mlt(u64 a, u64 b)
+{
+       return a * b;
+}
+
+static inline u32 sr(u64 v, u_char n)
+{
+       return v >> n;
+}
+
+static inline u32 and(u32 v, u32 mask)
+{
+       return v & mask;
+}
+
+static inline u32 le32_to_cpuvp(const void *p)
+{
+       return le32_to_cpup(p);
+}
+
+static int poly1305_init(struct shash_desc *desc)
+{
+       struct poly1305_desc_ctx *dctx = shash_desc_ctx(desc);
+
+       memset(dctx->h, 0, sizeof(dctx->h));
+       dctx->buflen = 0;
+       dctx->rset = false;
+       dctx->sset = false;
+
+       return 0;
+}
+
+static int poly1305_setkey(struct crypto_shash *tfm,
+                          const u8 *key, unsigned int keylen)
+{
+       /* Poly1305 requires a unique key for each tag, which implies that
+        * we can't set it on the tfm that gets accessed by multiple users
+        * simultaneously. Instead we expect the key as the first 32 bytes in
+        * the update() call. */
+       return -ENOTSUPP;
+}
+
+static void poly1305_setrkey(struct poly1305_desc_ctx *dctx, const u8 *key)
+{
+       /* r &= 0xffffffc0ffffffc0ffffffc0fffffff */
+       dctx->r[0] = (le32_to_cpuvp(key +  0) >> 0) & 0x3ffffff;
+       dctx->r[1] = (le32_to_cpuvp(key +  3) >> 2) & 0x3ffff03;
+       dctx->r[2] = (le32_to_cpuvp(key +  6) >> 4) & 0x3ffc0ff;
+       dctx->r[3] = (le32_to_cpuvp(key +  9) >> 6) & 0x3f03fff;
+       dctx->r[4] = (le32_to_cpuvp(key + 12) >> 8) & 0x00fffff;
+}
+
+static void poly1305_setskey(struct poly1305_desc_ctx *dctx, const u8 *key)
+{
+       dctx->s[0] = le32_to_cpuvp(key +  0);
+       dctx->s[1] = le32_to_cpuvp(key +  4);
+       dctx->s[2] = le32_to_cpuvp(key +  8);
+       dctx->s[3] = le32_to_cpuvp(key + 12);
+}
+
+static unsigned int poly1305_blocks(struct poly1305_desc_ctx *dctx,
+                                   const u8 *src, unsigned int srclen,
+                                   u32 hibit)
+{
+       u32 r0, r1, r2, r3, r4;
+       u32 s1, s2, s3, s4;
+       u32 h0, h1, h2, h3, h4;
+       u64 d0, d1, d2, d3, d4;
+
+       if (unlikely(!dctx->sset)) {
+               if (!dctx->rset && srclen >= POLY1305_BLOCK_SIZE) {
+                       poly1305_setrkey(dctx, src);
+                       src += POLY1305_BLOCK_SIZE;
+                       srclen -= POLY1305_BLOCK_SIZE;
+                       dctx->rset = true;
+               }
+               if (srclen >= POLY1305_BLOCK_SIZE) {
+                       poly1305_setskey(dctx, src);
+                       src += POLY1305_BLOCK_SIZE;
+                       srclen -= POLY1305_BLOCK_SIZE;
+                       dctx->sset = true;
+               }
+       }
+
+       r0 = dctx->r[0];
+       r1 = dctx->r[1];
+       r2 = dctx->r[2];
+       r3 = dctx->r[3];
+       r4 = dctx->r[4];
+
+       s1 = r1 * 5;
+       s2 = r2 * 5;
+       s3 = r3 * 5;
+       s4 = r4 * 5;
+
+       h0 = dctx->h[0];
+       h1 = dctx->h[1];
+       h2 = dctx->h[2];
+       h3 = dctx->h[3];
+       h4 = dctx->h[4];
+
+       while (likely(srclen >= POLY1305_BLOCK_SIZE)) {
+
+               /* h += m[i] */
+               h0 += (le32_to_cpuvp(src +  0) >> 0) & 0x3ffffff;
+               h1 += (le32_to_cpuvp(src +  3) >> 2) & 0x3ffffff;
+               h2 += (le32_to_cpuvp(src +  6) >> 4) & 0x3ffffff;
+               h3 += (le32_to_cpuvp(src +  9) >> 6) & 0x3ffffff;
+               h4 += (le32_to_cpuvp(src + 12) >> 8) | hibit;
+
+               /* h *= r */
+               d0 = mlt(h0, r0) + mlt(h1, s4) + mlt(h2, s3) +
+                    mlt(h3, s2) + mlt(h4, s1);
+               d1 = mlt(h0, r1) + mlt(h1, r0) + mlt(h2, s4) +
+                    mlt(h3, s3) + mlt(h4, s2);
+               d2 = mlt(h0, r2) + mlt(h1, r1) + mlt(h2, r0) +
+                    mlt(h3, s4) + mlt(h4, s3);
+               d3 = mlt(h0, r3) + mlt(h1, r2) + mlt(h2, r1) +
+                    mlt(h3, r0) + mlt(h4, s4);
+               d4 = mlt(h0, r4) + mlt(h1, r3) + mlt(h2, r2) +
+                    mlt(h3, r1) + mlt(h4, r0);
+
+               /* (partial) h %= p */
+               d1 += sr(d0, 26);     h0 = and(d0, 0x3ffffff);
+               d2 += sr(d1, 26);     h1 = and(d1, 0x3ffffff);
+               d3 += sr(d2, 26);     h2 = and(d2, 0x3ffffff);
+               d4 += sr(d3, 26);     h3 = and(d3, 0x3ffffff);
+               h0 += sr(d4, 26) * 5; h4 = and(d4, 0x3ffffff);
+               h1 += h0 >> 26;       h0 = h0 & 0x3ffffff;
+
+               src += POLY1305_BLOCK_SIZE;
+               srclen -= POLY1305_BLOCK_SIZE;
+       }
+
+       dctx->h[0] = h0;
+       dctx->h[1] = h1;
+       dctx->h[2] = h2;
+       dctx->h[3] = h3;
+       dctx->h[4] = h4;
+
+       return srclen;
+}
+
+static int poly1305_update(struct shash_desc *desc,
+                          const u8 *src, unsigned int srclen)
+{
+       struct poly1305_desc_ctx *dctx = shash_desc_ctx(desc);
+       unsigned int bytes;
+
+       if (unlikely(dctx->buflen)) {
+               bytes = min(srclen, POLY1305_BLOCK_SIZE - dctx->buflen);
+               memcpy(dctx->buf + dctx->buflen, src, bytes);
+               src += bytes;
+               srclen -= bytes;
+               dctx->buflen += bytes;
+
+               if (dctx->buflen == POLY1305_BLOCK_SIZE) {
+                       poly1305_blocks(dctx, dctx->buf,
+                                       POLY1305_BLOCK_SIZE, 1 << 24);
+                       dctx->buflen = 0;
+               }
+       }
+
+       if (likely(srclen >= POLY1305_BLOCK_SIZE)) {
+               bytes = poly1305_blocks(dctx, src, srclen, 1 << 24);
+               src += srclen - bytes;
+               srclen = bytes;
+       }
+
+       if (unlikely(srclen)) {
+               dctx->buflen = srclen;
+               memcpy(dctx->buf, src, srclen);
+       }
+
+       return 0;
+}
+
+static int poly1305_final(struct shash_desc *desc, u8 *dst)
+{
+       struct poly1305_desc_ctx *dctx = shash_desc_ctx(desc);
+       __le32 *mac = (__le32 *)dst;
+       u32 h0, h1, h2, h3, h4;
+       u32 g0, g1, g2, g3, g4;
+       u32 mask;
+       u64 f = 0;
+
+       if (unlikely(!dctx->sset))
+               return -ENOKEY;
+
+       if (unlikely(dctx->buflen)) {
+               dctx->buf[dctx->buflen++] = 1;
+               memset(dctx->buf + dctx->buflen, 0,
+                      POLY1305_BLOCK_SIZE - dctx->buflen);
+               poly1305_blocks(dctx, dctx->buf, POLY1305_BLOCK_SIZE, 0);
+       }
+
+       /* fully carry h */
+       h0 = dctx->h[0];
+       h1 = dctx->h[1];
+       h2 = dctx->h[2];
+       h3 = dctx->h[3];
+       h4 = dctx->h[4];
+
+       h2 += (h1 >> 26);     h1 = h1 & 0x3ffffff;
+       h3 += (h2 >> 26);     h2 = h2 & 0x3ffffff;
+       h4 += (h3 >> 26);     h3 = h3 & 0x3ffffff;
+       h0 += (h4 >> 26) * 5; h4 = h4 & 0x3ffffff;
+       h1 += (h0 >> 26);     h0 = h0 & 0x3ffffff;
+
+       /* compute h + -p */
+       g0 = h0 + 5;
+       g1 = h1 + (g0 >> 26);             g0 &= 0x3ffffff;
+       g2 = h2 + (g1 >> 26);             g1 &= 0x3ffffff;
+       g3 = h3 + (g2 >> 26);             g2 &= 0x3ffffff;
+       g4 = h4 + (g3 >> 26) - (1 << 26); g3 &= 0x3ffffff;
+
+       /* select h if h < p, or h + -p if h >= p */
+       mask = (g4 >> ((sizeof(u32) * 8) - 1)) - 1;
+       g0 &= mask;
+       g1 &= mask;
+       g2 &= mask;
+       g3 &= mask;
+       g4 &= mask;
+       mask = ~mask;
+       h0 = (h0 & mask) | g0;
+       h1 = (h1 & mask) | g1;
+       h2 = (h2 & mask) | g2;
+       h3 = (h3 & mask) | g3;
+       h4 = (h4 & mask) | g4;
+
+       /* h = h % (2^128) */
+       h0 = (h0 >>  0) | (h1 << 26);
+       h1 = (h1 >>  6) | (h2 << 20);
+       h2 = (h2 >> 12) | (h3 << 14);
+       h3 = (h3 >> 18) | (h4 <<  8);
+
+       /* mac = (h + s) % (2^128) */
+       f = (f >> 32) + h0 + dctx->s[0]; mac[0] = cpu_to_le32(f);
+       f = (f >> 32) + h1 + dctx->s[1]; mac[1] = cpu_to_le32(f);
+       f = (f >> 32) + h2 + dctx->s[2]; mac[2] = cpu_to_le32(f);
+       f = (f >> 32) + h3 + dctx->s[3]; mac[3] = cpu_to_le32(f);
+
+       return 0;
+}
+
+static struct shash_alg poly1305_alg = {
+       .digestsize     = POLY1305_DIGEST_SIZE,
+       .init           = poly1305_init,
+       .update         = poly1305_update,
+       .final          = poly1305_final,
+       .setkey         = poly1305_setkey,
+       .descsize       = sizeof(struct poly1305_desc_ctx),
+       .base           = {
+               .cra_name               = "poly1305",
+               .cra_driver_name        = "poly1305-generic",
+               .cra_priority           = 100,
+               .cra_flags              = CRYPTO_ALG_TYPE_SHASH,
+               .cra_alignmask          = sizeof(u32) - 1,
+               .cra_blocksize          = POLY1305_BLOCK_SIZE,
+               .cra_module             = THIS_MODULE,
+       },
+};
+
+static int __init poly1305_mod_init(void)
+{
+       return crypto_register_shash(&poly1305_alg);
+}
+
+static void __exit poly1305_mod_exit(void)
+{
+       crypto_unregister_shash(&poly1305_alg);
+}
+
+module_init(poly1305_mod_init);
+module_exit(poly1305_mod_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Martin Willi <martin@strongswan.org>");
+MODULE_DESCRIPTION("Poly1305 authenticator");
+MODULE_ALIAS_CRYPTO("poly1305");
+MODULE_ALIAS_CRYPTO("poly1305-generic");
index 4ffe73b51612fc45ca377fb3ccba21e3f401d339..2cc10c96d7530f486f90afb2336909ed44b61490 100644 (file)
 #include <linux/rwsem.h>
 #include <linux/proc_fs.h>
 #include <linux/seq_file.h>
-#include <linux/sysctl.h>
 #include "internal.h"
 
-#ifdef CONFIG_CRYPTO_FIPS
-static struct ctl_table crypto_sysctl_table[] = {
-       {
-               .procname       = "fips_enabled",
-               .data           = &fips_enabled,
-               .maxlen         = sizeof(int),
-               .mode           = 0444,
-               .proc_handler   = proc_dointvec
-       },
-       {}
-};
-
-static struct ctl_table crypto_dir_table[] = {
-       {
-               .procname       = "crypto",
-               .mode           = 0555,
-               .child          = crypto_sysctl_table
-       },
-       {}
-};
-
-static struct ctl_table_header *crypto_sysctls;
-
-static void crypto_proc_fips_init(void)
-{
-       crypto_sysctls = register_sysctl_table(crypto_dir_table);
-}
-
-static void crypto_proc_fips_exit(void)
-{
-       if (crypto_sysctls)
-               unregister_sysctl_table(crypto_sysctls);
-}
-#else
-#define crypto_proc_fips_init()
-#define crypto_proc_fips_exit()
-#endif
-
 static void *c_start(struct seq_file *m, loff_t *pos)
 {
        down_read(&crypto_alg_sem);
@@ -148,11 +109,9 @@ static const struct file_operations proc_crypto_ops = {
 void __init crypto_init_proc(void)
 {
        proc_create("crypto", 0, NULL, &proc_crypto_ops);
-       crypto_proc_fips_init();
 }
 
 void __exit crypto_exit_proc(void)
 {
-       crypto_proc_fips_exit();
        remove_proc_entry("crypto", NULL);
 }
index e0a25c2456de4f7f92fa4ac1cc4daf9c48b5b209..b81cffb13babc35cc2203ce5355ac6a04b18c5c4 100644 (file)
@@ -4,6 +4,7 @@
  * RNG operations.
  *
  * Copyright (c) 2008 Neil Horman <nhorman@tuxdriver.com>
+ * Copyright (c) 2015 Herbert Xu <herbert@gondor.apana.org.au>
  *
  * 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
 #include <linux/cryptouser.h>
 #include <net/netlink.h>
 
+#include "internal.h"
+
 static DEFINE_MUTEX(crypto_default_rng_lock);
 struct crypto_rng *crypto_default_rng;
 EXPORT_SYMBOL_GPL(crypto_default_rng);
 static int crypto_default_rng_refcnt;
 
-static int rngapi_reset(struct crypto_rng *tfm, u8 *seed, unsigned int slen)
+static inline struct crypto_rng *__crypto_rng_cast(struct crypto_tfm *tfm)
+{
+       return container_of(tfm, struct crypto_rng, base);
+}
+
+int crypto_rng_reset(struct crypto_rng *tfm, const u8 *seed, unsigned int slen)
 {
        u8 *buf = NULL;
        int err;
@@ -43,21 +51,23 @@ static int rngapi_reset(struct crypto_rng *tfm, u8 *seed, unsigned int slen)
                seed = buf;
        }
 
-       err = crypto_rng_alg(tfm)->rng_reset(tfm, seed, slen);
+       err = crypto_rng_alg(tfm)->seed(tfm, seed, slen);
 
-       kfree(buf);
+       kzfree(buf);
        return err;
 }
+EXPORT_SYMBOL_GPL(crypto_rng_reset);
 
-static int crypto_init_rng_ops(struct crypto_tfm *tfm, u32 type, u32 mask)
+static int crypto_rng_init_tfm(struct crypto_tfm *tfm)
 {
-       struct rng_alg *alg = &tfm->__crt_alg->cra_rng;
-       struct rng_tfm *ops = &tfm->crt_rng;
+       return 0;
+}
 
-       ops->rng_gen_random = alg->rng_make_random;
-       ops->rng_reset = rngapi_reset;
+static unsigned int seedsize(struct crypto_alg *alg)
+{
+       struct rng_alg *ralg = container_of(alg, struct rng_alg, base);
 
-       return 0;
+       return ralg->seedsize;
 }
 
 #ifdef CONFIG_NET
@@ -67,7 +77,7 @@ static int crypto_rng_report(struct sk_buff *skb, struct crypto_alg *alg)
 
        strncpy(rrng.type, "rng", sizeof(rrng.type));
 
-       rrng.seedsize = alg->cra_rng.seedsize;
+       rrng.seedsize = seedsize(alg);
 
        if (nla_put(skb, CRYPTOCFGA_REPORT_RNG,
                    sizeof(struct crypto_report_rng), &rrng))
@@ -89,24 +99,27 @@ static void crypto_rng_show(struct seq_file *m, struct crypto_alg *alg)
 static void crypto_rng_show(struct seq_file *m, struct crypto_alg *alg)
 {
        seq_printf(m, "type         : rng\n");
-       seq_printf(m, "seedsize     : %u\n", alg->cra_rng.seedsize);
-}
-
-static unsigned int crypto_rng_ctxsize(struct crypto_alg *alg, u32 type,
-                                      u32 mask)
-{
-       return alg->cra_ctxsize;
+       seq_printf(m, "seedsize     : %u\n", seedsize(alg));
 }
 
-const struct crypto_type crypto_rng_type = {
-       .ctxsize = crypto_rng_ctxsize,
-       .init = crypto_init_rng_ops,
+static const struct crypto_type crypto_rng_type = {
+       .extsize = crypto_alg_extsize,
+       .init_tfm = crypto_rng_init_tfm,
 #ifdef CONFIG_PROC_FS
        .show = crypto_rng_show,
 #endif
        .report = crypto_rng_report,
+       .maskclear = ~CRYPTO_ALG_TYPE_MASK,
+       .maskset = CRYPTO_ALG_TYPE_MASK,
+       .type = CRYPTO_ALG_TYPE_RNG,
+       .tfmsize = offsetof(struct crypto_rng, base),
 };
-EXPORT_SYMBOL_GPL(crypto_rng_type);
+
+struct crypto_rng *crypto_alloc_rng(const char *alg_name, u32 type, u32 mask)
+{
+       return crypto_alloc_tfm(alg_name, &crypto_rng_type, type, mask);
+}
+EXPORT_SYMBOL_GPL(crypto_alloc_rng);
 
 int crypto_get_default_rng(void)
 {
@@ -142,13 +155,82 @@ EXPORT_SYMBOL_GPL(crypto_get_default_rng);
 void crypto_put_default_rng(void)
 {
        mutex_lock(&crypto_default_rng_lock);
-       if (!--crypto_default_rng_refcnt) {
-               crypto_free_rng(crypto_default_rng);
-               crypto_default_rng = NULL;
-       }
+       crypto_default_rng_refcnt--;
        mutex_unlock(&crypto_default_rng_lock);
 }
 EXPORT_SYMBOL_GPL(crypto_put_default_rng);
 
+#if defined(CONFIG_CRYPTO_RNG) || defined(CONFIG_CRYPTO_RNG_MODULE)
+int crypto_del_default_rng(void)
+{
+       int err = -EBUSY;
+
+       mutex_lock(&crypto_default_rng_lock);
+       if (crypto_default_rng_refcnt)
+               goto out;
+
+       crypto_free_rng(crypto_default_rng);
+       crypto_default_rng = NULL;
+
+       err = 0;
+
+out:
+       mutex_unlock(&crypto_default_rng_lock);
+
+       return err;
+}
+EXPORT_SYMBOL_GPL(crypto_del_default_rng);
+#endif
+
+int crypto_register_rng(struct rng_alg *alg)
+{
+       struct crypto_alg *base = &alg->base;
+
+       if (alg->seedsize > PAGE_SIZE / 8)
+               return -EINVAL;
+
+       base->cra_type = &crypto_rng_type;
+       base->cra_flags &= ~CRYPTO_ALG_TYPE_MASK;
+       base->cra_flags |= CRYPTO_ALG_TYPE_RNG;
+
+       return crypto_register_alg(base);
+}
+EXPORT_SYMBOL_GPL(crypto_register_rng);
+
+void crypto_unregister_rng(struct rng_alg *alg)
+{
+       crypto_unregister_alg(&alg->base);
+}
+EXPORT_SYMBOL_GPL(crypto_unregister_rng);
+
+int crypto_register_rngs(struct rng_alg *algs, int count)
+{
+       int i, ret;
+
+       for (i = 0; i < count; i++) {
+               ret = crypto_register_rng(algs + i);
+               if (ret)
+                       goto err;
+       }
+
+       return 0;
+
+err:
+       for (--i; i >= 0; --i)
+               crypto_unregister_rng(algs + i);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(crypto_register_rngs);
+
+void crypto_unregister_rngs(struct rng_alg *algs, int count)
+{
+       int i;
+
+       for (i = count - 1; i >= 0; --i)
+               crypto_unregister_rng(algs + i);
+}
+EXPORT_SYMBOL_GPL(crypto_unregister_rngs);
+
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("Random Number Generator");
diff --git a/crypto/rsa.c b/crypto/rsa.c
new file mode 100644 (file)
index 0000000..752af06
--- /dev/null
@@ -0,0 +1,315 @@
+/* RSA asymmetric public-key algorithm [RFC3447]
+ *
+ * Copyright (c) 2015, Intel Corporation
+ * Authors: Tadeusz Struk <tadeusz.struk@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <crypto/internal/rsa.h>
+#include <crypto/internal/akcipher.h>
+#include <crypto/akcipher.h>
+
+/*
+ * RSAEP function [RFC3447 sec 5.1.1]
+ * c = m^e mod n;
+ */
+static int _rsa_enc(const struct rsa_key *key, MPI c, MPI m)
+{
+       /* (1) Validate 0 <= m < n */
+       if (mpi_cmp_ui(m, 0) < 0 || mpi_cmp(m, key->n) >= 0)
+               return -EINVAL;
+
+       /* (2) c = m^e mod n */
+       return mpi_powm(c, m, key->e, key->n);
+}
+
+/*
+ * RSADP function [RFC3447 sec 5.1.2]
+ * m = c^d mod n;
+ */
+static int _rsa_dec(const struct rsa_key *key, MPI m, MPI c)
+{
+       /* (1) Validate 0 <= c < n */
+       if (mpi_cmp_ui(c, 0) < 0 || mpi_cmp(c, key->n) >= 0)
+               return -EINVAL;
+
+       /* (2) m = c^d mod n */
+       return mpi_powm(m, c, key->d, key->n);
+}
+
+/*
+ * RSASP1 function [RFC3447 sec 5.2.1]
+ * s = m^d mod n
+ */
+static int _rsa_sign(const struct rsa_key *key, MPI s, MPI m)
+{
+       /* (1) Validate 0 <= m < n */
+       if (mpi_cmp_ui(m, 0) < 0 || mpi_cmp(m, key->n) >= 0)
+               return -EINVAL;
+
+       /* (2) s = m^d mod n */
+       return mpi_powm(s, m, key->d, key->n);
+}
+
+/*
+ * RSAVP1 function [RFC3447 sec 5.2.2]
+ * m = s^e mod n;
+ */
+static int _rsa_verify(const struct rsa_key *key, MPI m, MPI s)
+{
+       /* (1) Validate 0 <= s < n */
+       if (mpi_cmp_ui(s, 0) < 0 || mpi_cmp(s, key->n) >= 0)
+               return -EINVAL;
+
+       /* (2) m = s^e mod n */
+       return mpi_powm(m, s, key->e, key->n);
+}
+
+static inline struct rsa_key *rsa_get_key(struct crypto_akcipher *tfm)
+{
+       return akcipher_tfm_ctx(tfm);
+}
+
+static int rsa_enc(struct akcipher_request *req)
+{
+       struct crypto_akcipher *tfm = crypto_akcipher_reqtfm(req);
+       const struct rsa_key *pkey = rsa_get_key(tfm);
+       MPI m, c = mpi_alloc(0);
+       int ret = 0;
+       int sign;
+
+       if (!c)
+               return -ENOMEM;
+
+       if (unlikely(!pkey->n || !pkey->e)) {
+               ret = -EINVAL;
+               goto err_free_c;
+       }
+
+       if (req->dst_len < mpi_get_size(pkey->n)) {
+               req->dst_len = mpi_get_size(pkey->n);
+               ret = -EOVERFLOW;
+               goto err_free_c;
+       }
+
+       m = mpi_read_raw_data(req->src, req->src_len);
+       if (!m) {
+               ret = -ENOMEM;
+               goto err_free_c;
+       }
+
+       ret = _rsa_enc(pkey, c, m);
+       if (ret)
+               goto err_free_m;
+
+       ret = mpi_read_buffer(c, req->dst, req->dst_len, &req->dst_len, &sign);
+       if (ret)
+               goto err_free_m;
+
+       if (sign < 0) {
+               ret = -EBADMSG;
+               goto err_free_m;
+       }
+
+err_free_m:
+       mpi_free(m);
+err_free_c:
+       mpi_free(c);
+       return ret;
+}
+
+static int rsa_dec(struct akcipher_request *req)
+{
+       struct crypto_akcipher *tfm = crypto_akcipher_reqtfm(req);
+       const struct rsa_key *pkey = rsa_get_key(tfm);
+       MPI c, m = mpi_alloc(0);
+       int ret = 0;
+       int sign;
+
+       if (!m)
+               return -ENOMEM;
+
+       if (unlikely(!pkey->n || !pkey->d)) {
+               ret = -EINVAL;
+               goto err_free_m;
+       }
+
+       if (req->dst_len < mpi_get_size(pkey->n)) {
+               req->dst_len = mpi_get_size(pkey->n);
+               ret = -EOVERFLOW;
+               goto err_free_m;
+       }
+
+       c = mpi_read_raw_data(req->src, req->src_len);
+       if (!c) {
+               ret = -ENOMEM;
+               goto err_free_m;
+       }
+
+       ret = _rsa_dec(pkey, m, c);
+       if (ret)
+               goto err_free_c;
+
+       ret = mpi_read_buffer(m, req->dst, req->dst_len, &req->dst_len, &sign);
+       if (ret)
+               goto err_free_c;
+
+       if (sign < 0) {
+               ret = -EBADMSG;
+               goto err_free_c;
+       }
+
+err_free_c:
+       mpi_free(c);
+err_free_m:
+       mpi_free(m);
+       return ret;
+}
+
+static int rsa_sign(struct akcipher_request *req)
+{
+       struct crypto_akcipher *tfm = crypto_akcipher_reqtfm(req);
+       const struct rsa_key *pkey = rsa_get_key(tfm);
+       MPI m, s = mpi_alloc(0);
+       int ret = 0;
+       int sign;
+
+       if (!s)
+               return -ENOMEM;
+
+       if (unlikely(!pkey->n || !pkey->d)) {
+               ret = -EINVAL;
+               goto err_free_s;
+       }
+
+       if (req->dst_len < mpi_get_size(pkey->n)) {
+               req->dst_len = mpi_get_size(pkey->n);
+               ret = -EOVERFLOW;
+               goto err_free_s;
+       }
+
+       m = mpi_read_raw_data(req->src, req->src_len);
+       if (!m) {
+               ret = -ENOMEM;
+               goto err_free_s;
+       }
+
+       ret = _rsa_sign(pkey, s, m);
+       if (ret)
+               goto err_free_m;
+
+       ret = mpi_read_buffer(s, req->dst, req->dst_len, &req->dst_len, &sign);
+       if (ret)
+               goto err_free_m;
+
+       if (sign < 0) {
+               ret = -EBADMSG;
+               goto err_free_m;
+       }
+
+err_free_m:
+       mpi_free(m);
+err_free_s:
+       mpi_free(s);
+       return ret;
+}
+
+static int rsa_verify(struct akcipher_request *req)
+{
+       struct crypto_akcipher *tfm = crypto_akcipher_reqtfm(req);
+       const struct rsa_key *pkey = rsa_get_key(tfm);
+       MPI s, m = mpi_alloc(0);
+       int ret = 0;
+       int sign;
+
+       if (!m)
+               return -ENOMEM;
+
+       if (unlikely(!pkey->n || !pkey->e)) {
+               ret = -EINVAL;
+               goto err_free_m;
+       }
+
+       if (req->dst_len < mpi_get_size(pkey->n)) {
+               req->dst_len = mpi_get_size(pkey->n);
+               ret = -EOVERFLOW;
+               goto err_free_m;
+       }
+
+       s = mpi_read_raw_data(req->src, req->src_len);
+       if (!s) {
+               ret = -ENOMEM;
+               goto err_free_m;
+       }
+
+       ret = _rsa_verify(pkey, m, s);
+       if (ret)
+               goto err_free_s;
+
+       ret = mpi_read_buffer(m, req->dst, req->dst_len, &req->dst_len, &sign);
+       if (ret)
+               goto err_free_s;
+
+       if (sign < 0) {
+               ret = -EBADMSG;
+               goto err_free_s;
+       }
+
+err_free_s:
+       mpi_free(s);
+err_free_m:
+       mpi_free(m);
+       return ret;
+}
+
+static int rsa_setkey(struct crypto_akcipher *tfm, const void *key,
+                     unsigned int keylen)
+{
+       struct rsa_key *pkey = akcipher_tfm_ctx(tfm);
+
+       return rsa_parse_key(pkey, key, keylen);
+}
+
+static void rsa_exit_tfm(struct crypto_akcipher *tfm)
+{
+       struct rsa_key *pkey = akcipher_tfm_ctx(tfm);
+
+       rsa_free_key(pkey);
+}
+
+static struct akcipher_alg rsa = {
+       .encrypt = rsa_enc,
+       .decrypt = rsa_dec,
+       .sign = rsa_sign,
+       .verify = rsa_verify,
+       .setkey = rsa_setkey,
+       .exit = rsa_exit_tfm,
+       .base = {
+               .cra_name = "rsa",
+               .cra_driver_name = "rsa-generic",
+               .cra_priority = 100,
+               .cra_module = THIS_MODULE,
+               .cra_ctxsize = sizeof(struct rsa_key),
+       },
+};
+
+static int rsa_init(void)
+{
+       return crypto_register_akcipher(&rsa);
+}
+
+static void rsa_exit(void)
+{
+       crypto_unregister_akcipher(&rsa);
+}
+
+module_init(rsa_init);
+module_exit(rsa_exit);
+MODULE_ALIAS_CRYPTO("rsa");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("RSA generic algorithm");
diff --git a/crypto/rsa_helper.c b/crypto/rsa_helper.c
new file mode 100644 (file)
index 0000000..3e8e0a9
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ * RSA key extract helper
+ *
+ * Copyright (c) 2015, Intel Corporation
+ * Authors: Tadeusz Struk <tadeusz.struk@intel.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/kernel.h>
+#include <linux/export.h>
+#include <linux/err.h>
+#include <linux/fips.h>
+#include <crypto/internal/rsa.h>
+#include "rsakey-asn1.h"
+
+int rsa_get_n(void *context, size_t hdrlen, unsigned char tag,
+             const void *value, size_t vlen)
+{
+       struct rsa_key *key = context;
+
+       key->n = mpi_read_raw_data(value, vlen);
+
+       if (!key->n)
+               return -ENOMEM;
+
+       /* In FIPS mode only allow key size 2K & 3K */
+       if (fips_enabled && (mpi_get_size(key->n) != 256 ||
+                            mpi_get_size(key->n) != 384)) {
+               pr_err("RSA: key size not allowed in FIPS mode\n");
+               mpi_free(key->n);
+               key->n = NULL;
+               return -EINVAL;
+       }
+       return 0;
+}
+
+int rsa_get_e(void *context, size_t hdrlen, unsigned char tag,
+             const void *value, size_t vlen)
+{
+       struct rsa_key *key = context;
+
+       key->e = mpi_read_raw_data(value, vlen);
+
+       if (!key->e)
+               return -ENOMEM;
+
+       return 0;
+}
+
+int rsa_get_d(void *context, size_t hdrlen, unsigned char tag,
+             const void *value, size_t vlen)
+{
+       struct rsa_key *key = context;
+
+       key->d = mpi_read_raw_data(value, vlen);
+
+       if (!key->d)
+               return -ENOMEM;
+
+       /* In FIPS mode only allow key size 2K & 3K */
+       if (fips_enabled && (mpi_get_size(key->d) != 256 ||
+                            mpi_get_size(key->d) != 384)) {
+               pr_err("RSA: key size not allowed in FIPS mode\n");
+               mpi_free(key->d);
+               key->d = NULL;
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static void free_mpis(struct rsa_key *key)
+{
+       mpi_free(key->n);
+       mpi_free(key->e);
+       mpi_free(key->d);
+       key->n = NULL;
+       key->e = NULL;
+       key->d = NULL;
+}
+
+/**
+ * rsa_free_key() - frees rsa key allocated by rsa_parse_key()
+ *
+ * @rsa_key:   struct rsa_key key representation
+ */
+void rsa_free_key(struct rsa_key *key)
+{
+       free_mpis(key);
+}
+EXPORT_SYMBOL_GPL(rsa_free_key);
+
+/**
+ * rsa_parse_key() - extracts an rsa key from BER encoded buffer
+ *                  and stores it in the provided struct rsa_key
+ *
+ * @rsa_key:   struct rsa_key key representation
+ * @key:       key in BER format
+ * @key_len:   length of key
+ *
+ * Return:     0 on success or error code in case of error
+ */
+int rsa_parse_key(struct rsa_key *rsa_key, const void *key,
+                 unsigned int key_len)
+{
+       int ret;
+
+       free_mpis(rsa_key);
+       ret = asn1_ber_decoder(&rsakey_decoder, rsa_key, key, key_len);
+       if (ret < 0)
+               goto error;
+
+       return 0;
+error:
+       free_mpis(rsa_key);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(rsa_parse_key);
diff --git a/crypto/rsakey.asn1 b/crypto/rsakey.asn1
new file mode 100644 (file)
index 0000000..3c7b5df
--- /dev/null
@@ -0,0 +1,5 @@
+RsaKey ::= SEQUENCE {
+       n INTEGER ({ rsa_get_n }),
+       e INTEGER ({ rsa_get_e }),
+       d INTEGER ({ rsa_get_d })
+}
index 3bd749c7bb7062d33ee694e4131d13ce7c051c31..ea5815c5e12817912e1749d6aaf1a84606d7e7f3 100644 (file)
@@ -54,7 +54,11 @@ static void scatterwalk_pagedone(struct scatter_walk *walk, int out,
                struct page *page;
 
                page = sg_page(walk->sg) + ((walk->offset - 1) >> PAGE_SHIFT);
-               if (!PageSlab(page))
+               /* Test ARCH_IMPLEMENTS_FLUSH_DCACHE_PAGE first as
+                * PageSlab cannot be optimised away per se due to
+                * use of volatile pointer.
+                */
+               if (ARCH_IMPLEMENTS_FLUSH_DCACHE_PAGE && !PageSlab(page))
                        flush_dcache_page(page);
        }
 
@@ -104,22 +108,18 @@ void scatterwalk_map_and_copy(void *buf, struct scatterlist *sg,
                              unsigned int start, unsigned int nbytes, int out)
 {
        struct scatter_walk walk;
-       unsigned int offset = 0;
+       struct scatterlist tmp[2];
 
        if (!nbytes)
                return;
 
-       for (;;) {
-               scatterwalk_start(&walk, sg);
-
-               if (start < offset + sg->length)
-                       break;
+       sg = scatterwalk_ffwd(tmp, sg, start);
 
-               offset += sg->length;
-               sg = sg_next(sg);
-       }
+       if (sg_page(sg) == virt_to_page(buf) &&
+           sg->offset == offset_in_page(buf))
+               return;
 
-       scatterwalk_advance(&walk, start - offset);
+       scatterwalk_start(&walk, sg);
        scatterwalk_copychunks(buf, &walk, nbytes, out);
        scatterwalk_done(&walk, out, 0);
 }
@@ -146,3 +146,26 @@ int scatterwalk_bytes_sglen(struct scatterlist *sg, int num_bytes)
        return n;
 }
 EXPORT_SYMBOL_GPL(scatterwalk_bytes_sglen);
+
+struct scatterlist *scatterwalk_ffwd(struct scatterlist dst[2],
+                                    struct scatterlist *src,
+                                    unsigned int len)
+{
+       for (;;) {
+               if (!len)
+                       return src;
+
+               if (src->length > len)
+                       break;
+
+               len -= src->length;
+               src = sg_next(src);
+       }
+
+       sg_init_table(dst, 2);
+       sg_set_page(dst, sg_page(src), src->length - len, src->offset + len);
+       scatterwalk_crypto_chain(dst, sg_next(src), 0, 2);
+
+       return dst;
+}
+EXPORT_SYMBOL_GPL(scatterwalk_ffwd);
index b7bb9a2f4a31c095d7a39fe1762d9a2ad88f06cf..122c56e3491b9819b20cc34871a8cb4f6d6fdaac 100644 (file)
  *
  */
 
-#include <crypto/internal/aead.h>
+#include <crypto/internal/geniv.h>
 #include <crypto/internal/skcipher.h>
+#include <crypto/null.h>
 #include <crypto/rng.h>
+#include <crypto/scatterwalk.h>
 #include <linux/err.h>
 #include <linux/init.h>
 #include <linux/kernel.h>
 #include <linux/spinlock.h>
 #include <linux/string.h>
 
+struct seqniv_request_ctx {
+       struct scatterlist dst[2];
+       struct aead_request subreq;
+};
+
 struct seqiv_ctx {
        spinlock_t lock;
        u8 salt[] __attribute__ ((aligned(__alignof__(u32))));
 };
 
+struct seqiv_aead_ctx {
+       /* aead_geniv_ctx must be first the element */
+       struct aead_geniv_ctx geniv;
+       struct crypto_blkcipher *null;
+       u8 salt[] __attribute__ ((aligned(__alignof__(u32))));
+};
+
+static void seqiv_free(struct crypto_instance *inst);
+
 static void seqiv_complete2(struct skcipher_givcrypt_request *req, int err)
 {
        struct ablkcipher_request *subreq = skcipher_givcrypt_reqctx(req);
@@ -81,6 +97,77 @@ static void seqiv_aead_complete(struct crypto_async_request *base, int err)
        aead_givcrypt_complete(req, err);
 }
 
+static void seqiv_aead_encrypt_complete2(struct aead_request *req, int err)
+{
+       struct aead_request *subreq = aead_request_ctx(req);
+       struct crypto_aead *geniv;
+
+       if (err == -EINPROGRESS)
+               return;
+
+       if (err)
+               goto out;
+
+       geniv = crypto_aead_reqtfm(req);
+       memcpy(req->iv, subreq->iv, crypto_aead_ivsize(geniv));
+
+out:
+       kzfree(subreq->iv);
+}
+
+static void seqiv_aead_encrypt_complete(struct crypto_async_request *base,
+                                       int err)
+{
+       struct aead_request *req = base->data;
+
+       seqiv_aead_encrypt_complete2(req, err);
+       aead_request_complete(req, err);
+}
+
+static void seqniv_aead_encrypt_complete2(struct aead_request *req, int err)
+{
+       unsigned int ivsize = 8;
+       u8 data[20];
+
+       if (err == -EINPROGRESS)
+               return;
+
+       /* Swap IV and ESP header back to correct order. */
+       scatterwalk_map_and_copy(data, req->dst, 0, req->assoclen + ivsize, 0);
+       scatterwalk_map_and_copy(data + ivsize, req->dst, 0, req->assoclen, 1);
+       scatterwalk_map_and_copy(data, req->dst, req->assoclen, ivsize, 1);
+}
+
+static void seqniv_aead_encrypt_complete(struct crypto_async_request *base,
+                                       int err)
+{
+       struct aead_request *req = base->data;
+
+       seqniv_aead_encrypt_complete2(req, err);
+       aead_request_complete(req, err);
+}
+
+static void seqniv_aead_decrypt_complete2(struct aead_request *req, int err)
+{
+       u8 data[4];
+
+       if (err == -EINPROGRESS)
+               return;
+
+       /* Move ESP header back to correct location. */
+       scatterwalk_map_and_copy(data, req->dst, 16, req->assoclen - 8, 0);
+       scatterwalk_map_and_copy(data, req->dst, 8, req->assoclen - 8, 1);
+}
+
+static void seqniv_aead_decrypt_complete(struct crypto_async_request *base,
+                                        int err)
+{
+       struct aead_request *req = base->data;
+
+       seqniv_aead_decrypt_complete2(req, err);
+       aead_request_complete(req, err);
+}
+
 static void seqiv_geniv(struct seqiv_ctx *ctx, u8 *info, u64 seq,
                        unsigned int ivsize)
 {
@@ -186,160 +273,477 @@ static int seqiv_aead_givencrypt(struct aead_givcrypt_request *req)
        return err;
 }
 
-static int seqiv_givencrypt_first(struct skcipher_givcrypt_request *req)
+static int seqniv_aead_encrypt(struct aead_request *req)
 {
-       struct crypto_ablkcipher *geniv = skcipher_givcrypt_reqtfm(req);
-       struct seqiv_ctx *ctx = crypto_ablkcipher_ctx(geniv);
-       int err = 0;
+       struct crypto_aead *geniv = crypto_aead_reqtfm(req);
+       struct seqiv_aead_ctx *ctx = crypto_aead_ctx(geniv);
+       struct seqniv_request_ctx *rctx = aead_request_ctx(req);
+       struct aead_request *subreq = &rctx->subreq;
+       struct scatterlist *dst;
+       crypto_completion_t compl;
+       void *data;
+       unsigned int ivsize = 8;
+       u8 buf[20] __attribute__ ((aligned(__alignof__(u32))));
+       int err;
 
-       spin_lock_bh(&ctx->lock);
-       if (crypto_ablkcipher_crt(geniv)->givencrypt != seqiv_givencrypt_first)
-               goto unlock;
+       if (req->cryptlen < ivsize)
+               return -EINVAL;
 
-       crypto_ablkcipher_crt(geniv)->givencrypt = seqiv_givencrypt;
-       err = crypto_rng_get_bytes(crypto_default_rng, ctx->salt,
-                                  crypto_ablkcipher_ivsize(geniv));
+       /* ESP AD is at most 12 bytes (ESN). */
+       if (req->assoclen > 12)
+               return -EINVAL;
 
-unlock:
-       spin_unlock_bh(&ctx->lock);
+       aead_request_set_tfm(subreq, ctx->geniv.child);
 
-       if (err)
-               return err;
+       compl = seqniv_aead_encrypt_complete;
+       data = req;
+
+       if (req->src != req->dst) {
+               struct blkcipher_desc desc = {
+                       .tfm = ctx->null,
+               };
+
+               err = crypto_blkcipher_encrypt(&desc, req->dst, req->src,
+                                              req->assoclen + req->cryptlen);
+               if (err)
+                       return err;
+       }
 
-       return seqiv_givencrypt(req);
+       dst = scatterwalk_ffwd(rctx->dst, req->dst, ivsize);
+
+       aead_request_set_callback(subreq, req->base.flags, compl, data);
+       aead_request_set_crypt(subreq, dst, dst,
+                              req->cryptlen - ivsize, req->iv);
+       aead_request_set_ad(subreq, req->assoclen);
+
+       memcpy(buf, req->iv, ivsize);
+       crypto_xor(buf, ctx->salt, ivsize);
+       memcpy(req->iv, buf, ivsize);
+
+       /* Swap order of IV and ESP AD for ICV generation. */
+       scatterwalk_map_and_copy(buf + ivsize, req->dst, 0, req->assoclen, 0);
+       scatterwalk_map_and_copy(buf, req->dst, 0, req->assoclen + ivsize, 1);
+
+       err = crypto_aead_encrypt(subreq);
+       seqniv_aead_encrypt_complete2(req, err);
+       return err;
 }
 
-static int seqiv_aead_givencrypt_first(struct aead_givcrypt_request *req)
+static int seqiv_aead_encrypt(struct aead_request *req)
 {
-       struct crypto_aead *geniv = aead_givcrypt_reqtfm(req);
-       struct seqiv_ctx *ctx = crypto_aead_ctx(geniv);
-       int err = 0;
+       struct crypto_aead *geniv = crypto_aead_reqtfm(req);
+       struct seqiv_aead_ctx *ctx = crypto_aead_ctx(geniv);
+       struct aead_request *subreq = aead_request_ctx(req);
+       crypto_completion_t compl;
+       void *data;
+       u8 *info;
+       unsigned int ivsize = 8;
+       int err;
 
-       spin_lock_bh(&ctx->lock);
-       if (crypto_aead_crt(geniv)->givencrypt != seqiv_aead_givencrypt_first)
-               goto unlock;
+       if (req->cryptlen < ivsize)
+               return -EINVAL;
 
-       crypto_aead_crt(geniv)->givencrypt = seqiv_aead_givencrypt;
-       err = crypto_rng_get_bytes(crypto_default_rng, ctx->salt,
-                                  crypto_aead_ivsize(geniv));
+       aead_request_set_tfm(subreq, ctx->geniv.child);
 
-unlock:
-       spin_unlock_bh(&ctx->lock);
+       compl = req->base.complete;
+       data = req->base.data;
+       info = req->iv;
 
-       if (err)
-               return err;
+       if (req->src != req->dst) {
+               struct blkcipher_desc desc = {
+                       .tfm = ctx->null,
+               };
 
-       return seqiv_aead_givencrypt(req);
+               err = crypto_blkcipher_encrypt(&desc, req->dst, req->src,
+                                              req->assoclen + req->cryptlen);
+               if (err)
+                       return err;
+       }
+
+       if (unlikely(!IS_ALIGNED((unsigned long)info,
+                                crypto_aead_alignmask(geniv) + 1))) {
+               info = kmalloc(ivsize, req->base.flags &
+                                      CRYPTO_TFM_REQ_MAY_SLEEP ? GFP_KERNEL:
+                                                                 GFP_ATOMIC);
+               if (!info)
+                       return -ENOMEM;
+
+               memcpy(info, req->iv, ivsize);
+               compl = seqiv_aead_encrypt_complete;
+               data = req;
+       }
+
+       aead_request_set_callback(subreq, req->base.flags, compl, data);
+       aead_request_set_crypt(subreq, req->dst, req->dst,
+                              req->cryptlen - ivsize, info);
+       aead_request_set_ad(subreq, req->assoclen + ivsize);
+
+       crypto_xor(info, ctx->salt, ivsize);
+       scatterwalk_map_and_copy(info, req->dst, req->assoclen, ivsize, 1);
+
+       err = crypto_aead_encrypt(subreq);
+       if (unlikely(info != req->iv))
+               seqiv_aead_encrypt_complete2(req, err);
+       return err;
+}
+
+static int seqniv_aead_decrypt(struct aead_request *req)
+{
+       struct crypto_aead *geniv = crypto_aead_reqtfm(req);
+       struct seqiv_aead_ctx *ctx = crypto_aead_ctx(geniv);
+       struct seqniv_request_ctx *rctx = aead_request_ctx(req);
+       struct aead_request *subreq = &rctx->subreq;
+       struct scatterlist *dst;
+       crypto_completion_t compl;
+       void *data;
+       unsigned int ivsize = 8;
+       u8 buf[20];
+       int err;
+
+       if (req->cryptlen < ivsize + crypto_aead_authsize(geniv))
+               return -EINVAL;
+
+       aead_request_set_tfm(subreq, ctx->geniv.child);
+
+       compl = req->base.complete;
+       data = req->base.data;
+
+       if (req->assoclen > 12)
+               return -EINVAL;
+       else if (req->assoclen > 8) {
+               compl = seqniv_aead_decrypt_complete;
+               data = req;
+       }
+
+       if (req->src != req->dst) {
+               struct blkcipher_desc desc = {
+                       .tfm = ctx->null,
+               };
+
+               err = crypto_blkcipher_encrypt(&desc, req->dst, req->src,
+                                              req->assoclen + req->cryptlen);
+               if (err)
+                       return err;
+       }
+
+       /* Move ESP AD forward for ICV generation. */
+       scatterwalk_map_and_copy(buf, req->dst, 0, req->assoclen + ivsize, 0);
+       memcpy(req->iv, buf + req->assoclen, ivsize);
+       scatterwalk_map_and_copy(buf, req->dst, ivsize, req->assoclen, 1);
+
+       dst = scatterwalk_ffwd(rctx->dst, req->dst, ivsize);
+
+       aead_request_set_callback(subreq, req->base.flags, compl, data);
+       aead_request_set_crypt(subreq, dst, dst,
+                              req->cryptlen - ivsize, req->iv);
+       aead_request_set_ad(subreq, req->assoclen);
+
+       err = crypto_aead_decrypt(subreq);
+       if (req->assoclen > 8)
+               seqniv_aead_decrypt_complete2(req, err);
+       return err;
+}
+
+static int seqiv_aead_decrypt(struct aead_request *req)
+{
+       struct crypto_aead *geniv = crypto_aead_reqtfm(req);
+       struct seqiv_aead_ctx *ctx = crypto_aead_ctx(geniv);
+       struct aead_request *subreq = aead_request_ctx(req);
+       crypto_completion_t compl;
+       void *data;
+       unsigned int ivsize = 8;
+
+       if (req->cryptlen < ivsize + crypto_aead_authsize(geniv))
+               return -EINVAL;
+
+       aead_request_set_tfm(subreq, ctx->geniv.child);
+
+       compl = req->base.complete;
+       data = req->base.data;
+
+       aead_request_set_callback(subreq, req->base.flags, compl, data);
+       aead_request_set_crypt(subreq, req->src, req->dst,
+                              req->cryptlen - ivsize, req->iv);
+       aead_request_set_ad(subreq, req->assoclen + ivsize);
+
+       scatterwalk_map_and_copy(req->iv, req->src, req->assoclen, ivsize, 0);
+       if (req->src != req->dst)
+               scatterwalk_map_and_copy(req->iv, req->dst,
+                                        req->assoclen, ivsize, 1);
+
+       return crypto_aead_decrypt(subreq);
 }
 
 static int seqiv_init(struct crypto_tfm *tfm)
 {
        struct crypto_ablkcipher *geniv = __crypto_ablkcipher_cast(tfm);
        struct seqiv_ctx *ctx = crypto_ablkcipher_ctx(geniv);
+       int err;
 
        spin_lock_init(&ctx->lock);
 
        tfm->crt_ablkcipher.reqsize = sizeof(struct ablkcipher_request);
 
-       return skcipher_geniv_init(tfm);
+       err = 0;
+       if (!crypto_get_default_rng()) {
+               crypto_ablkcipher_crt(geniv)->givencrypt = seqiv_givencrypt;
+               err = crypto_rng_get_bytes(crypto_default_rng, ctx->salt,
+                                          crypto_ablkcipher_ivsize(geniv));
+               crypto_put_default_rng();
+       }
+
+       return err ?: skcipher_geniv_init(tfm);
 }
 
-static int seqiv_aead_init(struct crypto_tfm *tfm)
+static int seqiv_old_aead_init(struct crypto_tfm *tfm)
 {
        struct crypto_aead *geniv = __crypto_aead_cast(tfm);
        struct seqiv_ctx *ctx = crypto_aead_ctx(geniv);
+       int err;
 
        spin_lock_init(&ctx->lock);
 
-       tfm->crt_aead.reqsize = sizeof(struct aead_request);
+       crypto_aead_set_reqsize(__crypto_aead_cast(tfm),
+                               sizeof(struct aead_request));
+       err = 0;
+       if (!crypto_get_default_rng()) {
+               geniv->givencrypt = seqiv_aead_givencrypt;
+               err = crypto_rng_get_bytes(crypto_default_rng, ctx->salt,
+                                          crypto_aead_ivsize(geniv));
+               crypto_put_default_rng();
+       }
 
-       return aead_geniv_init(tfm);
+       return err ?: aead_geniv_init(tfm);
 }
 
-static struct crypto_template seqiv_tmpl;
-
-static struct crypto_instance *seqiv_ablkcipher_alloc(struct rtattr **tb)
+static int seqiv_aead_init_common(struct crypto_tfm *tfm, unsigned int reqsize)
 {
-       struct crypto_instance *inst;
+       struct crypto_aead *geniv = __crypto_aead_cast(tfm);
+       struct seqiv_aead_ctx *ctx = crypto_aead_ctx(geniv);
+       int err;
 
-       inst = skcipher_geniv_alloc(&seqiv_tmpl, tb, 0, 0);
+       spin_lock_init(&ctx->geniv.lock);
 
-       if (IS_ERR(inst))
+       crypto_aead_set_reqsize(geniv, sizeof(struct aead_request));
+
+       err = crypto_get_default_rng();
+       if (err)
                goto out;
 
-       if (inst->alg.cra_ablkcipher.ivsize < sizeof(u64)) {
-               skcipher_geniv_free(inst);
-               inst = ERR_PTR(-EINVAL);
+       err = crypto_rng_get_bytes(crypto_default_rng, ctx->salt,
+                                  crypto_aead_ivsize(geniv));
+       crypto_put_default_rng();
+       if (err)
                goto out;
-       }
 
-       inst->alg.cra_ablkcipher.givencrypt = seqiv_givencrypt_first;
+       ctx->null = crypto_get_default_null_skcipher();
+       err = PTR_ERR(ctx->null);
+       if (IS_ERR(ctx->null))
+               goto out;
 
-       inst->alg.cra_init = seqiv_init;
-       inst->alg.cra_exit = skcipher_geniv_exit;
+       err = aead_geniv_init(tfm);
+       if (err)
+               goto drop_null;
 
-       inst->alg.cra_ctxsize += inst->alg.cra_ablkcipher.ivsize;
+       ctx->geniv.child = geniv->child;
+       geniv->child = geniv;
 
 out:
-       return inst;
+       return err;
+
+drop_null:
+       crypto_put_default_null_skcipher();
+       goto out;
+}
+
+static int seqiv_aead_init(struct crypto_tfm *tfm)
+{
+       return seqiv_aead_init_common(tfm, sizeof(struct aead_request));
+}
+
+static int seqniv_aead_init(struct crypto_tfm *tfm)
+{
+       return seqiv_aead_init_common(tfm, sizeof(struct seqniv_request_ctx));
+}
+
+static void seqiv_aead_exit(struct crypto_tfm *tfm)
+{
+       struct seqiv_aead_ctx *ctx = crypto_tfm_ctx(tfm);
+
+       crypto_free_aead(ctx->geniv.child);
+       crypto_put_default_null_skcipher();
 }
 
-static struct crypto_instance *seqiv_aead_alloc(struct rtattr **tb)
+static int seqiv_ablkcipher_create(struct crypto_template *tmpl,
+                                  struct rtattr **tb)
 {
        struct crypto_instance *inst;
+       int err;
 
-       inst = aead_geniv_alloc(&seqiv_tmpl, tb, 0, 0);
+       inst = skcipher_geniv_alloc(tmpl, tb, 0, 0);
 
        if (IS_ERR(inst))
-               goto out;
+               return PTR_ERR(inst);
 
-       if (inst->alg.cra_aead.ivsize < sizeof(u64)) {
-               aead_geniv_free(inst);
-               inst = ERR_PTR(-EINVAL);
-               goto out;
-       }
+       err = -EINVAL;
+       if (inst->alg.cra_ablkcipher.ivsize < sizeof(u64))
+               goto free_inst;
+
+       inst->alg.cra_init = seqiv_init;
+       inst->alg.cra_exit = skcipher_geniv_exit;
+
+       inst->alg.cra_ctxsize += inst->alg.cra_ablkcipher.ivsize;
+       inst->alg.cra_ctxsize += sizeof(struct seqiv_ctx);
+
+       inst->alg.cra_alignmask |= __alignof__(u32) - 1;
 
-       inst->alg.cra_aead.givencrypt = seqiv_aead_givencrypt_first;
+       err = crypto_register_instance(tmpl, inst);
+       if (err)
+               goto free_inst;
 
-       inst->alg.cra_init = seqiv_aead_init;
+out:
+       return err;
+
+free_inst:
+       skcipher_geniv_free(inst);
+       goto out;
+}
+
+static int seqiv_old_aead_create(struct crypto_template *tmpl,
+                                struct aead_instance *aead)
+{
+       struct crypto_instance *inst = aead_crypto_instance(aead);
+       int err = -EINVAL;
+
+       if (inst->alg.cra_aead.ivsize < sizeof(u64))
+               goto free_inst;
+
+       inst->alg.cra_init = seqiv_old_aead_init;
        inst->alg.cra_exit = aead_geniv_exit;
 
        inst->alg.cra_ctxsize = inst->alg.cra_aead.ivsize;
+       inst->alg.cra_ctxsize += sizeof(struct seqiv_ctx);
+
+       err = crypto_register_instance(tmpl, inst);
+       if (err)
+               goto free_inst;
 
 out:
-       return inst;
+       return err;
+
+free_inst:
+       aead_geniv_free(aead);
+       goto out;
 }
 
-static struct crypto_instance *seqiv_alloc(struct rtattr **tb)
+static int seqiv_aead_create(struct crypto_template *tmpl, struct rtattr **tb)
+{
+       struct aead_instance *inst;
+       struct crypto_aead_spawn *spawn;
+       struct aead_alg *alg;
+       int err;
+
+       inst = aead_geniv_alloc(tmpl, tb, 0, 0);
+
+       if (IS_ERR(inst))
+               return PTR_ERR(inst);
+
+       inst->alg.base.cra_alignmask |= __alignof__(u32) - 1;
+
+       if (inst->alg.base.cra_aead.encrypt)
+               return seqiv_old_aead_create(tmpl, inst);
+
+       spawn = aead_instance_ctx(inst);
+       alg = crypto_spawn_aead_alg(spawn);
+
+       if (alg->base.cra_aead.encrypt)
+               goto done;
+
+       err = -EINVAL;
+       if (inst->alg.ivsize != sizeof(u64))
+               goto free_inst;
+
+       inst->alg.encrypt = seqiv_aead_encrypt;
+       inst->alg.decrypt = seqiv_aead_decrypt;
+
+       inst->alg.base.cra_init = seqiv_aead_init;
+       inst->alg.base.cra_exit = seqiv_aead_exit;
+
+       inst->alg.base.cra_ctxsize = sizeof(struct seqiv_aead_ctx);
+       inst->alg.base.cra_ctxsize += inst->alg.base.cra_aead.ivsize;
+
+done:
+       err = aead_register_instance(tmpl, inst);
+       if (err)
+               goto free_inst;
+
+out:
+       return err;
+
+free_inst:
+       aead_geniv_free(inst);
+       goto out;
+}
+
+static int seqiv_create(struct crypto_template *tmpl, struct rtattr **tb)
 {
        struct crypto_attr_type *algt;
-       struct crypto_instance *inst;
        int err;
 
        algt = crypto_get_attr_type(tb);
        if (IS_ERR(algt))
-               return ERR_CAST(algt);
-
-       err = crypto_get_default_rng();
-       if (err)
-               return ERR_PTR(err);
+               return PTR_ERR(algt);
 
        if ((algt->type ^ CRYPTO_ALG_TYPE_AEAD) & CRYPTO_ALG_TYPE_MASK)
-               inst = seqiv_ablkcipher_alloc(tb);
+               err = seqiv_ablkcipher_create(tmpl, tb);
        else
-               inst = seqiv_aead_alloc(tb);
+               err = seqiv_aead_create(tmpl, tb);
 
+       return err;
+}
+
+static int seqniv_create(struct crypto_template *tmpl, struct rtattr **tb)
+{
+       struct aead_instance *inst;
+       struct crypto_aead_spawn *spawn;
+       struct aead_alg *alg;
+       int err;
+
+       inst = aead_geniv_alloc(tmpl, tb, 0, 0);
+       err = PTR_ERR(inst);
        if (IS_ERR(inst))
-               goto put_rng;
+               goto out;
 
-       inst->alg.cra_alignmask |= __alignof__(u32) - 1;
-       inst->alg.cra_ctxsize += sizeof(struct seqiv_ctx);
+       spawn = aead_instance_ctx(inst);
+       alg = crypto_spawn_aead_alg(spawn);
+
+       if (alg->base.cra_aead.encrypt)
+               goto done;
+
+       err = -EINVAL;
+       if (inst->alg.ivsize != sizeof(u64))
+               goto free_inst;
+
+       inst->alg.encrypt = seqniv_aead_encrypt;
+       inst->alg.decrypt = seqniv_aead_decrypt;
+
+       inst->alg.base.cra_init = seqniv_aead_init;
+       inst->alg.base.cra_exit = seqiv_aead_exit;
+
+       inst->alg.base.cra_alignmask |= __alignof__(u32) - 1;
+       inst->alg.base.cra_ctxsize = sizeof(struct seqiv_aead_ctx);
+       inst->alg.base.cra_ctxsize += inst->alg.ivsize;
+
+done:
+       err = aead_register_instance(tmpl, inst);
+       if (err)
+               goto free_inst;
 
 out:
-       return inst;
+       return err;
 
-put_rng:
-       crypto_put_default_rng();
+free_inst:
+       aead_geniv_free(inst);
        goto out;
 }
 
@@ -348,24 +752,46 @@ static void seqiv_free(struct crypto_instance *inst)
        if ((inst->alg.cra_flags ^ CRYPTO_ALG_TYPE_AEAD) & CRYPTO_ALG_TYPE_MASK)
                skcipher_geniv_free(inst);
        else
-               aead_geniv_free(inst);
-       crypto_put_default_rng();
+               aead_geniv_free(aead_instance(inst));
 }
 
 static struct crypto_template seqiv_tmpl = {
        .name = "seqiv",
-       .alloc = seqiv_alloc,
+       .create = seqiv_create,
+       .free = seqiv_free,
+       .module = THIS_MODULE,
+};
+
+static struct crypto_template seqniv_tmpl = {
+       .name = "seqniv",
+       .create = seqniv_create,
        .free = seqiv_free,
        .module = THIS_MODULE,
 };
 
 static int __init seqiv_module_init(void)
 {
-       return crypto_register_template(&seqiv_tmpl);
+       int err;
+
+       err = crypto_register_template(&seqiv_tmpl);
+       if (err)
+               goto out;
+
+       err = crypto_register_template(&seqniv_tmpl);
+       if (err)
+               goto out_undo_niv;
+
+out:
+       return err;
+
+out_undo_niv:
+       crypto_unregister_template(&seqiv_tmpl);
+       goto out;
 }
 
 static void __exit seqiv_module_exit(void)
 {
+       crypto_unregister_template(&seqniv_tmpl);
        crypto_unregister_template(&seqiv_tmpl);
 }
 
@@ -375,3 +801,4 @@ module_exit(seqiv_module_exit);
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("Sequence Number IV Generator");
 MODULE_ALIAS_CRYPTO("seqiv");
+MODULE_ALIAS_CRYPTO("seqniv");
index 47c713954bf30e797e76bbfd427d2b79cd5580d5..ecb1e3d39bf0776a0d8805a9f8fadd3ec288bcb0 100644 (file)
@@ -520,11 +520,6 @@ static int crypto_shash_init_tfm(struct crypto_tfm *tfm)
        return 0;
 }
 
-static unsigned int crypto_shash_extsize(struct crypto_alg *alg)
-{
-       return alg->cra_ctxsize;
-}
-
 #ifdef CONFIG_NET
 static int crypto_shash_report(struct sk_buff *skb, struct crypto_alg *alg)
 {
@@ -564,7 +559,7 @@ static void crypto_shash_show(struct seq_file *m, struct crypto_alg *alg)
 
 static const struct crypto_type crypto_shash_type = {
        .ctxsize = crypto_shash_ctxsize,
-       .extsize = crypto_shash_extsize,
+       .extsize = crypto_alg_extsize,
        .init = crypto_init_shash_ops,
        .init_tfm = crypto_shash_init_tfm,
 #ifdef CONFIG_PROC_FS
index 1a2800107fc89d55d933f78e56edc0d7b4370d15..9f6f10b498ba4aab02849c760b1da61bd5a7fd99 100644 (file)
  *
  */
 
+#include <crypto/aead.h>
 #include <crypto/hash.h>
 #include <linux/err.h>
+#include <linux/fips.h>
 #include <linux/init.h>
 #include <linux/gfp.h>
 #include <linux/module.h>
@@ -34,7 +36,6 @@
 #include <linux/timex.h>
 #include <linux/interrupt.h>
 #include "tcrypt.h"
-#include "internal.h"
 
 /*
  * Need slab memory for testing (size in number of pages).
@@ -257,12 +258,12 @@ static void sg_init_aead(struct scatterlist *sg, char *xbuf[XBUFSIZE],
                rem = buflen % PAGE_SIZE;
        }
 
-       sg_init_table(sg, np);
+       sg_init_table(sg, np + 1);
        np--;
        for (k = 0; k < np; k++)
-               sg_set_buf(&sg[k], xbuf[k], PAGE_SIZE);
+               sg_set_buf(&sg[k + 1], xbuf[k], PAGE_SIZE);
 
-       sg_set_buf(&sg[k], xbuf[k], rem);
+       sg_set_buf(&sg[k + 1], xbuf[k], rem);
 }
 
 static void test_aead_speed(const char *algo, int enc, unsigned int secs,
@@ -276,7 +277,6 @@ static void test_aead_speed(const char *algo, int enc, unsigned int secs,
        const char *key;
        struct aead_request *req;
        struct scatterlist *sg;
-       struct scatterlist *asg;
        struct scatterlist *sgout;
        const char *e;
        void *assoc;
@@ -308,11 +308,10 @@ static void test_aead_speed(const char *algo, int enc, unsigned int secs,
        if (testmgr_alloc_buf(xoutbuf))
                goto out_nooutbuf;
 
-       sg = kmalloc(sizeof(*sg) * 8 * 3, GFP_KERNEL);
+       sg = kmalloc(sizeof(*sg) * 9 * 2, GFP_KERNEL);
        if (!sg)
                goto out_nosg;
-       asg = &sg[8];
-       sgout = &asg[8];
+       sgout = &sg[9];
 
        tfm = crypto_alloc_aead(algo, 0, 0);
 
@@ -338,7 +337,6 @@ static void test_aead_speed(const char *algo, int enc, unsigned int secs,
                do {
                        assoc = axbuf[0];
                        memset(assoc, 0xff, aad_size);
-                       sg_init_one(&asg[0], assoc, aad_size);
 
                        if ((*keysize + *b_size) > TVMEMSIZE * PAGE_SIZE) {
                                pr_err("template (%u) too big for tvmem (%lu)\n",
@@ -374,14 +372,17 @@ static void test_aead_speed(const char *algo, int enc, unsigned int secs,
                                goto out;
                        }
 
-                       sg_init_aead(&sg[0], xbuf,
+                       sg_init_aead(sg, xbuf,
                                    *b_size + (enc ? authsize : 0));
 
-                       sg_init_aead(&sgout[0], xoutbuf,
+                       sg_init_aead(sgout, xoutbuf,
                                    *b_size + (enc ? authsize : 0));
 
+                       sg_set_buf(&sg[0], assoc, aad_size);
+                       sg_set_buf(&sgout[0], assoc, aad_size);
+
                        aead_request_set_crypt(req, sg, sgout, *b_size, iv);
-                       aead_request_set_assoc(req, asg, aad_size);
+                       aead_request_set_ad(req, aad_size);
 
                        if (secs)
                                ret = test_aead_jiffies(req, enc, *b_size,
@@ -808,7 +809,7 @@ static int test_ahash_jiffies(struct ahash_request *req, int blen,
 
        for (start = jiffies, end = start + secs * HZ, bcount = 0;
             time_before(jiffies, end); bcount++) {
-               ret = crypto_ahash_init(req);
+               ret = do_one_ahash_op(req, crypto_ahash_init(req));
                if (ret)
                        return ret;
                for (pcount = 0; pcount < blen; pcount += plen) {
@@ -877,7 +878,7 @@ static int test_ahash_cycles(struct ahash_request *req, int blen,
 
        /* Warm-up run. */
        for (i = 0; i < 4; i++) {
-               ret = crypto_ahash_init(req);
+               ret = do_one_ahash_op(req, crypto_ahash_init(req));
                if (ret)
                        goto out;
                for (pcount = 0; pcount < blen; pcount += plen) {
@@ -896,7 +897,7 @@ static int test_ahash_cycles(struct ahash_request *req, int blen,
 
                start = get_cycles();
 
-               ret = crypto_ahash_init(req);
+               ret = do_one_ahash_op(req, crypto_ahash_init(req));
                if (ret)
                        goto out;
                for (pcount = 0; pcount < blen; pcount += plen) {
@@ -1761,6 +1762,11 @@ static int do_test(const char *alg, u32 type, u32 mask, int m)
                                NULL, 0, 16, 8, aead_speed_template_20);
                break;
 
+       case 212:
+               test_aead_speed("rfc4309(ccm(aes))", ENCRYPT, sec,
+                               NULL, 0, 16, 8, aead_speed_template_19);
+               break;
+
        case 300:
                if (alg) {
                        test_hash_speed(alg, sec, generic_hash_speed_template);
index 6c7e21a09f78f170905abf74a897c21dee7a39e9..6cc1b856871b96d5b0d9064fc6ae028649fe89b6 100644 (file)
@@ -65,6 +65,7 @@ static u8 speed_template_32_64[] = {32, 64, 0};
 /*
  * AEAD speed tests
  */
+static u8 aead_speed_template_19[] = {19, 0};
 static u8 aead_speed_template_20[] = {20, 0};
 
 /*
index f9bce3d7ee7f840e55ea6bc4d5abb03ba7df72b0..975e1eac3e2d2fdbc12bad5241fa275a968bbad4 100644 (file)
  *
  */
 
+#include <crypto/aead.h>
 #include <crypto/hash.h>
 #include <linux/err.h>
+#include <linux/fips.h>
 #include <linux/module.h>
 #include <linux/scatterlist.h>
 #include <linux/slab.h>
 #include <linux/string.h>
 #include <crypto/rng.h>
 #include <crypto/drbg.h>
+#include <crypto/akcipher.h>
 
 #include "internal.h"
 
@@ -114,6 +117,11 @@ struct drbg_test_suite {
        unsigned int count;
 };
 
+struct akcipher_test_suite {
+       struct akcipher_testvec *vecs;
+       unsigned int count;
+};
+
 struct alg_test_desc {
        const char *alg;
        int (*test)(const struct alg_test_desc *desc, const char *driver,
@@ -128,6 +136,7 @@ struct alg_test_desc {
                struct hash_test_suite hash;
                struct cprng_test_suite cprng;
                struct drbg_test_suite drbg;
+               struct akcipher_test_suite akcipher;
        } suite;
 };
 
@@ -425,7 +434,6 @@ static int __test_aead(struct crypto_aead *tfm, int enc,
        char *key;
        struct aead_request *req;
        struct scatterlist *sg;
-       struct scatterlist *asg;
        struct scatterlist *sgout;
        const char *e, *d;
        struct tcrypt_result result;
@@ -452,11 +460,10 @@ static int __test_aead(struct crypto_aead *tfm, int enc,
                goto out_nooutbuf;
 
        /* avoid "the frame size is larger than 1024 bytes" compiler warning */
-       sg = kmalloc(sizeof(*sg) * 8 * (diff_dst ? 3 : 2), GFP_KERNEL);
+       sg = kmalloc(sizeof(*sg) * 8 * (diff_dst ? 4 : 2), GFP_KERNEL);
        if (!sg)
                goto out_nosg;
-       asg = &sg[8];
-       sgout = &asg[8];
+       sgout = &sg[16];
 
        if (diff_dst)
                d = "-ddst";
@@ -535,23 +542,27 @@ static int __test_aead(struct crypto_aead *tfm, int enc,
                        goto out;
                }
 
+               k = !!template[i].alen;
+               sg_init_table(sg, k + 1);
+               sg_set_buf(&sg[0], assoc, template[i].alen);
+               sg_set_buf(&sg[k], input,
+                          template[i].ilen + (enc ? authsize : 0));
+               output = input;
+
                if (diff_dst) {
+                       sg_init_table(sgout, k + 1);
+                       sg_set_buf(&sgout[0], assoc, template[i].alen);
+
                        output = xoutbuf[0];
                        output += align_offset;
-                       sg_init_one(&sg[0], input, template[i].ilen);
-                       sg_init_one(&sgout[0], output, template[i].rlen);
-               } else {
-                       sg_init_one(&sg[0], input,
-                                   template[i].ilen + (enc ? authsize : 0));
-                       output = input;
+                       sg_set_buf(&sgout[k], output,
+                                  template[i].rlen + (enc ? 0 : authsize));
                }
 
-               sg_init_one(&asg[0], assoc, template[i].alen);
-
                aead_request_set_crypt(req, sg, (diff_dst) ? sgout : sg,
                                       template[i].ilen, iv);
 
-               aead_request_set_assoc(req, asg, template[i].alen);
+               aead_request_set_ad(req, template[i].alen);
 
                ret = enc ? crypto_aead_encrypt(req) : crypto_aead_decrypt(req);
 
@@ -631,9 +642,29 @@ static int __test_aead(struct crypto_aead *tfm, int enc,
                authsize = abs(template[i].rlen - template[i].ilen);
 
                ret = -EINVAL;
-               sg_init_table(sg, template[i].np);
+               sg_init_table(sg, template[i].anp + template[i].np);
                if (diff_dst)
-                       sg_init_table(sgout, template[i].np);
+                       sg_init_table(sgout, template[i].anp + template[i].np);
+
+               ret = -EINVAL;
+               for (k = 0, temp = 0; k < template[i].anp; k++) {
+                       if (WARN_ON(offset_in_page(IDX[k]) +
+                                   template[i].atap[k] > PAGE_SIZE))
+                               goto out;
+                       sg_set_buf(&sg[k],
+                                  memcpy(axbuf[IDX[k] >> PAGE_SHIFT] +
+                                         offset_in_page(IDX[k]),
+                                         template[i].assoc + temp,
+                                         template[i].atap[k]),
+                                  template[i].atap[k]);
+                       if (diff_dst)
+                               sg_set_buf(&sgout[k],
+                                          axbuf[IDX[k] >> PAGE_SHIFT] +
+                                          offset_in_page(IDX[k]),
+                                          template[i].atap[k]);
+                       temp += template[i].atap[k];
+               }
+
                for (k = 0, temp = 0; k < template[i].np; k++) {
                        if (WARN_ON(offset_in_page(IDX[k]) +
                                    template[i].tap[k] > PAGE_SIZE))
@@ -641,7 +672,8 @@ static int __test_aead(struct crypto_aead *tfm, int enc,
 
                        q = xbuf[IDX[k] >> PAGE_SHIFT] + offset_in_page(IDX[k]);
                        memcpy(q, template[i].input + temp, template[i].tap[k]);
-                       sg_set_buf(&sg[k], q, template[i].tap[k]);
+                       sg_set_buf(&sg[template[i].anp + k],
+                                  q, template[i].tap[k]);
 
                        if (diff_dst) {
                                q = xoutbuf[IDX[k] >> PAGE_SHIFT] +
@@ -649,7 +681,8 @@ static int __test_aead(struct crypto_aead *tfm, int enc,
 
                                memset(q, 0, template[i].tap[k]);
 
-                               sg_set_buf(&sgout[k], q, template[i].tap[k]);
+                               sg_set_buf(&sgout[template[i].anp + k],
+                                          q, template[i].tap[k]);
                        }
 
                        n = template[i].tap[k];
@@ -669,39 +702,24 @@ static int __test_aead(struct crypto_aead *tfm, int enc,
                }
 
                if (enc) {
-                       if (WARN_ON(sg[k - 1].offset +
-                                   sg[k - 1].length + authsize >
-                                   PAGE_SIZE)) {
+                       if (WARN_ON(sg[template[i].anp + k - 1].offset +
+                                   sg[template[i].anp + k - 1].length +
+                                   authsize > PAGE_SIZE)) {
                                ret = -EINVAL;
                                goto out;
                        }
 
                        if (diff_dst)
-                               sgout[k - 1].length += authsize;
-                       else
-                               sg[k - 1].length += authsize;
-               }
-
-               sg_init_table(asg, template[i].anp);
-               ret = -EINVAL;
-               for (k = 0, temp = 0; k < template[i].anp; k++) {
-                       if (WARN_ON(offset_in_page(IDX[k]) +
-                                   template[i].atap[k] > PAGE_SIZE))
-                               goto out;
-                       sg_set_buf(&asg[k],
-                                  memcpy(axbuf[IDX[k] >> PAGE_SHIFT] +
-                                         offset_in_page(IDX[k]),
-                                         template[i].assoc + temp,
-                                         template[i].atap[k]),
-                                  template[i].atap[k]);
-                       temp += template[i].atap[k];
+                               sgout[template[i].anp + k - 1].length +=
+                                       authsize;
+                       sg[template[i].anp + k - 1].length += authsize;
                }
 
                aead_request_set_crypt(req, sg, (diff_dst) ? sgout : sg,
                                       template[i].ilen,
                                       iv);
 
-               aead_request_set_assoc(req, asg, template[i].alen);
+               aead_request_set_ad(req, template[i].alen);
 
                ret = enc ? crypto_aead_encrypt(req) : crypto_aead_decrypt(req);
 
@@ -1814,6 +1832,147 @@ static int alg_test_drbg(const struct alg_test_desc *desc, const char *driver,
 
 }
 
+static int do_test_rsa(struct crypto_akcipher *tfm,
+                      struct akcipher_testvec *vecs)
+{
+       struct akcipher_request *req;
+       void *outbuf_enc = NULL;
+       void *outbuf_dec = NULL;
+       struct tcrypt_result result;
+       unsigned int out_len_max, out_len = 0;
+       int err = -ENOMEM;
+
+       req = akcipher_request_alloc(tfm, GFP_KERNEL);
+       if (!req)
+               return err;
+
+       init_completion(&result.completion);
+       err = crypto_akcipher_setkey(tfm, vecs->key, vecs->key_len);
+       if (err)
+               goto free_req;
+
+       akcipher_request_set_crypt(req, vecs->m, outbuf_enc, vecs->m_size,
+                                  out_len);
+       /* expect this to fail, and update the required buf len */
+       crypto_akcipher_encrypt(req);
+       out_len = req->dst_len;
+       if (!out_len) {
+               err = -EINVAL;
+               goto free_req;
+       }
+
+       out_len_max = out_len;
+       err = -ENOMEM;
+       outbuf_enc = kzalloc(out_len_max, GFP_KERNEL);
+       if (!outbuf_enc)
+               goto free_req;
+
+       akcipher_request_set_crypt(req, vecs->m, outbuf_enc, vecs->m_size,
+                                  out_len);
+       akcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
+                                     tcrypt_complete, &result);
+
+       /* Run RSA encrypt - c = m^e mod n;*/
+       err = wait_async_op(&result, crypto_akcipher_encrypt(req));
+       if (err) {
+               pr_err("alg: rsa: encrypt test failed. err %d\n", err);
+               goto free_all;
+       }
+       if (out_len != vecs->c_size) {
+               pr_err("alg: rsa: encrypt test failed. Invalid output len\n");
+               err = -EINVAL;
+               goto free_all;
+       }
+       /* verify that encrypted message is equal to expected */
+       if (memcmp(vecs->c, outbuf_enc, vecs->c_size)) {
+               pr_err("alg: rsa: encrypt test failed. Invalid output\n");
+               err = -EINVAL;
+               goto free_all;
+       }
+       /* Don't invoke decrypt for vectors with public key */
+       if (vecs->public_key_vec) {
+               err = 0;
+               goto free_all;
+       }
+       outbuf_dec = kzalloc(out_len_max, GFP_KERNEL);
+       if (!outbuf_dec) {
+               err = -ENOMEM;
+               goto free_all;
+       }
+       init_completion(&result.completion);
+       akcipher_request_set_crypt(req, outbuf_enc, outbuf_dec, vecs->c_size,
+                                  out_len);
+
+       /* Run RSA decrypt - m = c^d mod n;*/
+       err = wait_async_op(&result, crypto_akcipher_decrypt(req));
+       if (err) {
+               pr_err("alg: rsa: decrypt test failed. err %d\n", err);
+               goto free_all;
+       }
+       out_len = req->dst_len;
+       if (out_len != vecs->m_size) {
+               pr_err("alg: rsa: decrypt test failed. Invalid output len\n");
+               err = -EINVAL;
+               goto free_all;
+       }
+       /* verify that decrypted message is equal to the original msg */
+       if (memcmp(vecs->m, outbuf_dec, vecs->m_size)) {
+               pr_err("alg: rsa: decrypt test failed. Invalid output\n");
+               err = -EINVAL;
+       }
+free_all:
+       kfree(outbuf_dec);
+       kfree(outbuf_enc);
+free_req:
+       akcipher_request_free(req);
+       return err;
+}
+
+static int test_rsa(struct crypto_akcipher *tfm, struct akcipher_testvec *vecs,
+                   unsigned int tcount)
+{
+       int ret, i;
+
+       for (i = 0; i < tcount; i++) {
+               ret = do_test_rsa(tfm, vecs++);
+               if (ret) {
+                       pr_err("alg: rsa: test failed on vector %d, err=%d\n",
+                              i + 1, ret);
+                       return ret;
+               }
+       }
+       return 0;
+}
+
+static int test_akcipher(struct crypto_akcipher *tfm, const char *alg,
+                        struct akcipher_testvec *vecs, unsigned int tcount)
+{
+       if (strncmp(alg, "rsa", 3) == 0)
+               return test_rsa(tfm, vecs, tcount);
+
+       return 0;
+}
+
+static int alg_test_akcipher(const struct alg_test_desc *desc,
+                            const char *driver, u32 type, u32 mask)
+{
+       struct crypto_akcipher *tfm;
+       int err = 0;
+
+       tfm = crypto_alloc_akcipher(driver, type | CRYPTO_ALG_INTERNAL, mask);
+       if (IS_ERR(tfm)) {
+               pr_err("alg: akcipher: Failed to load tfm for %s: %ld\n",
+                      driver, PTR_ERR(tfm));
+               return PTR_ERR(tfm);
+       }
+       if (desc->suite.akcipher.vecs)
+               err = test_akcipher(tfm, desc->alg, desc->suite.akcipher.vecs,
+                                   desc->suite.akcipher.count);
+
+       crypto_free_akcipher(tfm);
+       return err;
+}
+
 static int alg_test_null(const struct alg_test_desc *desc,
                             const char *driver, u32 type, u32 mask)
 {
@@ -2296,6 +2455,21 @@ static const struct alg_test_desc alg_test_descs[] = {
                                }
                        }
                }
+       }, {
+               .alg = "chacha20",
+               .test = alg_test_skcipher,
+               .suite = {
+                       .cipher = {
+                               .enc = {
+                                       .vecs = chacha20_enc_tv_template,
+                                       .count = CHACHA20_ENC_TEST_VECTORS
+                               },
+                               .dec = {
+                                       .vecs = chacha20_enc_tv_template,
+                                       .count = CHACHA20_ENC_TEST_VECTORS
+                               },
+                       }
+               }
        }, {
                .alg = "cmac(aes)",
                .test = alg_test_hash,
@@ -2317,6 +2491,15 @@ static const struct alg_test_desc alg_test_descs[] = {
        }, {
                .alg = "compress_null",
                .test = alg_test_null,
+       }, {
+               .alg = "crc32",
+               .test = alg_test_hash,
+               .suite = {
+                       .hash = {
+                               .vecs = crc32_tv_template,
+                               .count = CRC32_TEST_VECTORS
+                       }
+               }
        }, {
                .alg = "crc32c",
                .test = alg_test_crc32c,
@@ -3094,6 +3277,10 @@ static const struct alg_test_desc alg_test_descs[] = {
                                .count = HMAC_SHA512_TEST_VECTORS
                        }
                }
+       }, {
+               .alg = "jitterentropy_rng",
+               .fips_allowed = 1,
+               .test = alg_test_null,
        }, {
                .alg = "lrw(aes)",
                .test = alg_test_skcipher,
@@ -3275,6 +3462,15 @@ static const struct alg_test_desc alg_test_descs[] = {
                                }
                        }
                }
+       }, {
+               .alg = "poly1305",
+               .test = alg_test_hash,
+               .suite = {
+                       .hash = {
+                               .vecs = poly1305_tv_template,
+                               .count = POLY1305_TEST_VECTORS
+                       }
+               }
        }, {
                .alg = "rfc3686(ctr(aes))",
                .test = alg_test_skcipher,
@@ -3338,6 +3534,36 @@ static const struct alg_test_desc alg_test_descs[] = {
                                },
                        }
                }
+       }, {
+               .alg = "rfc7539(chacha20,poly1305)",
+               .test = alg_test_aead,
+               .suite = {
+                       .aead = {
+                               .enc = {
+                                       .vecs = rfc7539_enc_tv_template,
+                                       .count = RFC7539_ENC_TEST_VECTORS
+                               },
+                               .dec = {
+                                       .vecs = rfc7539_dec_tv_template,
+                                       .count = RFC7539_DEC_TEST_VECTORS
+                               },
+                       }
+               }
+       }, {
+               .alg = "rfc7539esp(chacha20,poly1305)",
+               .test = alg_test_aead,
+               .suite = {
+                       .aead = {
+                               .enc = {
+                                       .vecs = rfc7539esp_enc_tv_template,
+                                       .count = RFC7539ESP_ENC_TEST_VECTORS
+                               },
+                               .dec = {
+                                       .vecs = rfc7539esp_dec_tv_template,
+                                       .count = RFC7539ESP_DEC_TEST_VECTORS
+                               },
+                       }
+               }
        }, {
                .alg = "rmd128",
                .test = alg_test_hash,
@@ -3374,6 +3600,16 @@ static const struct alg_test_desc alg_test_descs[] = {
                                .count = RMD320_TEST_VECTORS
                        }
                }
+       }, {
+               .alg = "rsa",
+               .test = alg_test_akcipher,
+               .fips_allowed = 1,
+               .suite = {
+                       .akcipher = {
+                               .vecs = rsa_tv_template,
+                               .count = RSA_TEST_VECTORS
+                       }
+               }
        }, {
                .alg = "salsa20",
                .test = alg_test_skcipher,
index 62e2485bb428c03de6d8b1e6c546fc825a95fcb7..868edf11704142deec8e05c44a2edfb37661ad27 100644 (file)
@@ -46,6 +46,24 @@ struct hash_testvec {
        unsigned char ksize;
 };
 
+/*
+ * cipher_testvec:     structure to describe a cipher test
+ * @key:       A pointer to a key used by the test
+ * @klen:      The length of @key
+ * @iv:                A pointer to the IV used by the test
+ * @input:     A pointer to data used as input
+ * @ilen       The length of data in @input
+ * @result:    A pointer to what the test need to produce
+ * @rlen:      The length of data in @result
+ * @fail:      If set to one, the test need to fail
+ * @wk:                Does the test need CRYPTO_TFM_REQ_WEAK_KEY
+ *             ( e.g. test needs to fail due to a weak key )
+ * @np:        numbers of SG to distribute data in (from 1 to MAX_TAP)
+ * @tap:       How to distribute data in @np SGs
+ * @also_non_np:       if set to 1, the test will be also done without
+ *                     splitting data in @np SGs
+ */
+
 struct cipher_testvec {
        char *key;
        char *iv;
@@ -54,7 +72,7 @@ struct cipher_testvec {
        unsigned short tap[MAX_TAP];
        int np;
        unsigned char also_non_np;
-       unsigned char fail;
+       bool fail;
        unsigned char wk; /* weak key flag */
        unsigned char klen;
        unsigned short ilen;
@@ -71,7 +89,7 @@ struct aead_testvec {
        unsigned char atap[MAX_TAP];
        int np;
        int anp;
-       unsigned char fail;
+       bool fail;
        unsigned char novrfy;   /* ccm dec verification failure expected */
        unsigned char wk; /* weak key flag */
        unsigned char klen;
@@ -107,8 +125,195 @@ struct drbg_testvec {
        size_t expectedlen;
 };
 
+struct akcipher_testvec {
+       unsigned char *key;
+       unsigned char *m;
+       unsigned char *c;
+       unsigned int key_len;
+       unsigned int m_size;
+       unsigned int c_size;
+       bool public_key_vec;
+};
+
 static char zeroed_string[48];
 
+/*
+ * RSA test vectors. Borrowed from openSSL.
+ */
+#ifdef CONFIG_CRYPTO_FIPS
+#define RSA_TEST_VECTORS       2
+#else
+#define RSA_TEST_VECTORS       4
+#endif
+static struct akcipher_testvec rsa_tv_template[] = {
+       {
+#ifndef CONFIG_CRYPTO_FIPS
+       .key =
+       "\x30\x81\x88" /* sequence of 136 bytes */
+       "\x02\x41" /* modulus - integer of 65 bytes */
+       "\x00\xAA\x36\xAB\xCE\x88\xAC\xFD\xFF\x55\x52\x3C\x7F\xC4\x52\x3F"
+       "\x90\xEF\xA0\x0D\xF3\x77\x4A\x25\x9F\x2E\x62\xB4\xC5\xD9\x9C\xB5"
+       "\xAD\xB3\x00\xA0\x28\x5E\x53\x01\x93\x0E\x0C\x70\xFB\x68\x76\x93"
+       "\x9C\xE6\x16\xCE\x62\x4A\x11\xE0\x08\x6D\x34\x1E\xBC\xAC\xA0\xA1"
+       "\xF5"
+       "\x02\x01\x11" /* public key - integer of 1 byte */
+       "\x02\x40" /* private key - integer of 64 bytes */
+       "\x0A\x03\x37\x48\x62\x64\x87\x69\x5F\x5F\x30\xBC\x38\xB9\x8B\x44"
+       "\xC2\xCD\x2D\xFF\x43\x40\x98\xCD\x20\xD8\xA1\x38\xD0\x90\xBF\x64"
+       "\x79\x7C\x3F\xA7\xA2\xCD\xCB\x3C\xD1\xE0\xBD\xBA\x26\x54\xB4\xF9"
+       "\xDF\x8E\x8A\xE5\x9D\x73\x3D\x9F\x33\xB3\x01\x62\x4A\xFD\x1D\x51",
+       .m = "\x54\x85\x9b\x34\x2c\x49\xea\x2a",
+       .c =
+       "\x63\x1c\xcd\x7b\xe1\x7e\xe4\xde\xc9\xa8\x89\xa1\x74\xcb\x3c\x63"
+       "\x7d\x24\xec\x83\xc3\x15\xe4\x7f\x73\x05\x34\xd1\xec\x22\xbb\x8a"
+       "\x5e\x32\x39\x6d\xc1\x1d\x7d\x50\x3b\x9f\x7a\xad\xf0\x2e\x25\x53"
+       "\x9f\x6e\xbd\x4c\x55\x84\x0c\x9b\xcf\x1a\x4b\x51\x1e\x9e\x0c\x06",
+       .key_len = 139,
+       .m_size = 8,
+       .c_size = 64,
+       }, {
+       .key =
+       "\x30\x82\x01\x0B" /* sequence of 267 bytes */
+       "\x02\x81\x81" /* modulus - integer of 129 bytes */
+       "\x00\xBB\xF8\x2F\x09\x06\x82\xCE\x9C\x23\x38\xAC\x2B\x9D\xA8\x71"
+       "\xF7\x36\x8D\x07\xEE\xD4\x10\x43\xA4\x40\xD6\xB6\xF0\x74\x54\xF5"
+       "\x1F\xB8\xDF\xBA\xAF\x03\x5C\x02\xAB\x61\xEA\x48\xCE\xEB\x6F\xCD"
+       "\x48\x76\xED\x52\x0D\x60\xE1\xEC\x46\x19\x71\x9D\x8A\x5B\x8B\x80"
+       "\x7F\xAF\xB8\xE0\xA3\xDF\xC7\x37\x72\x3E\xE6\xB4\xB7\xD9\x3A\x25"
+       "\x84\xEE\x6A\x64\x9D\x06\x09\x53\x74\x88\x34\xB2\x45\x45\x98\x39"
+       "\x4E\xE0\xAA\xB1\x2D\x7B\x61\xA5\x1F\x52\x7A\x9A\x41\xF6\xC1\x68"
+       "\x7F\xE2\x53\x72\x98\xCA\x2A\x8F\x59\x46\xF8\xE5\xFD\x09\x1D\xBD"
+       "\xCB"
+       "\x02\x01\x11" /* public key - integer of 1 byte */
+       "\x02\x81\x81"  /* private key - integer of 129 bytes */
+       "\x00\xA5\xDA\xFC\x53\x41\xFA\xF2\x89\xC4\xB9\x88\xDB\x30\xC1\xCD"
+       "\xF8\x3F\x31\x25\x1E\x06\x68\xB4\x27\x84\x81\x38\x01\x57\x96\x41"
+       "\xB2\x94\x10\xB3\xC7\x99\x8D\x6B\xC4\x65\x74\x5E\x5C\x39\x26\x69"
+       "\xD6\x87\x0D\xA2\xC0\x82\xA9\x39\xE3\x7F\xDC\xB8\x2E\xC9\x3E\xDA"
+       "\xC9\x7F\xF3\xAD\x59\x50\xAC\xCF\xBC\x11\x1C\x76\xF1\xA9\x52\x94"
+       "\x44\xE5\x6A\xAF\x68\xC5\x6C\x09\x2C\xD3\x8D\xC3\xBE\xF5\xD2\x0A"
+       "\x93\x99\x26\xED\x4F\x74\xA1\x3E\xDD\xFB\xE1\xA1\xCE\xCC\x48\x94"
+       "\xAF\x94\x28\xC2\xB7\xB8\x88\x3F\xE4\x46\x3A\x4B\xC8\x5B\x1C\xB3"
+       "\xC1",
+       .key_len = 271,
+       .m = "\x54\x85\x9b\x34\x2c\x49\xea\x2a",
+       .c =
+       "\x74\x1b\x55\xac\x47\xb5\x08\x0a\x6e\x2b\x2d\xf7\x94\xb8\x8a\x95"
+       "\xed\xa3\x6b\xc9\x29\xee\xb2\x2c\x80\xc3\x39\x3b\x8c\x62\x45\x72"
+       "\xc2\x7f\x74\x81\x91\x68\x44\x48\x5a\xdc\xa0\x7e\xa7\x0b\x05\x7f"
+       "\x0e\xa0\x6c\xe5\x8f\x19\x4d\xce\x98\x47\x5f\xbd\x5f\xfe\xe5\x34"
+       "\x59\x89\xaf\xf0\xba\x44\xd7\xf1\x1a\x50\x72\xef\x5e\x4a\xb6\xb7"
+       "\x54\x34\xd1\xc4\x83\x09\xdf\x0f\x91\x5f\x7d\x91\x70\x2f\xd4\x13"
+       "\xcc\x5e\xa4\x6c\xc3\x4d\x28\xef\xda\xaf\xec\x14\x92\xfc\xa3\x75"
+       "\x13\xb4\xc1\xa1\x11\xfc\x40\x2f\x4c\x9d\xdf\x16\x76\x11\x20\x6b",
+       .m_size = 8,
+       .c_size = 128,
+       }, {
+#endif
+       .key =
+       "\x30\x82\x02\x0D" /* sequence of 525 bytes */
+       "\x02\x82\x01\x00" /* modulus - integer of 256 bytes */
+       "\xDB\x10\x1A\xC2\xA3\xF1\xDC\xFF\x13\x6B\xED\x44\xDF\xF0\x02\x6D"
+       "\x13\xC7\x88\xDA\x70\x6B\x54\xF1\xE8\x27\xDC\xC3\x0F\x99\x6A\xFA"
+       "\xC6\x67\xFF\x1D\x1E\x3C\x1D\xC1\xB5\x5F\x6C\xC0\xB2\x07\x3A\x6D"
+       "\x41\xE4\x25\x99\xAC\xFC\xD2\x0F\x02\xD3\xD1\x54\x06\x1A\x51\x77"
+       "\xBD\xB6\xBF\xEA\xA7\x5C\x06\xA9\x5D\x69\x84\x45\xD7\xF5\x05\xBA"
+       "\x47\xF0\x1B\xD7\x2B\x24\xEC\xCB\x9B\x1B\x10\x8D\x81\xA0\xBE\xB1"
+       "\x8C\x33\xE4\x36\xB8\x43\xEB\x19\x2A\x81\x8D\xDE\x81\x0A\x99\x48"
+       "\xB6\xF6\xBC\xCD\x49\x34\x3A\x8F\x26\x94\xE3\x28\x82\x1A\x7C\x8F"
+       "\x59\x9F\x45\xE8\x5D\x1A\x45\x76\x04\x56\x05\xA1\xD0\x1B\x8C\x77"
+       "\x6D\xAF\x53\xFA\x71\xE2\x67\xE0\x9A\xFE\x03\xA9\x85\xD2\xC9\xAA"
+       "\xBA\x2A\xBC\xF4\xA0\x08\xF5\x13\x98\x13\x5D\xF0\xD9\x33\x34\x2A"
+       "\x61\xC3\x89\x55\xF0\xAE\x1A\x9C\x22\xEE\x19\x05\x8D\x32\xFE\xEC"
+       "\x9C\x84\xBA\xB7\xF9\x6C\x3A\x4F\x07\xFC\x45\xEB\x12\xE5\x7B\xFD"
+       "\x55\xE6\x29\x69\xD1\xC2\xE8\xB9\x78\x59\xF6\x79\x10\xC6\x4E\xEB"
+       "\x6A\x5E\xB9\x9A\xC7\xC4\x5B\x63\xDA\xA3\x3F\x5E\x92\x7A\x81\x5E"
+       "\xD6\xB0\xE2\x62\x8F\x74\x26\xC2\x0C\xD3\x9A\x17\x47\xE6\x8E\xAB"
+       "\x02\x03\x01\x00\x01" /* public key - integer of 3 bytes */
+       "\x02\x82\x01\x00" /* private key - integer of 256 bytes */
+       "\x52\x41\xF4\xDA\x7B\xB7\x59\x55\xCA\xD4\x2F\x0F\x3A\xCB\xA4\x0D"
+       "\x93\x6C\xCC\x9D\xC1\xB2\xFB\xFD\xAE\x40\x31\xAC\x69\x52\x21\x92"
+       "\xB3\x27\xDF\xEA\xEE\x2C\x82\xBB\xF7\x40\x32\xD5\x14\xC4\x94\x12"
+       "\xEC\xB8\x1F\xCA\x59\xE3\xC1\x78\xF3\x85\xD8\x47\xA5\xD7\x02\x1A"
+       "\x65\x79\x97\x0D\x24\xF4\xF0\x67\x6E\x75\x2D\xBF\x10\x3D\xA8\x7D"
+       "\xEF\x7F\x60\xE4\xE6\x05\x82\x89\x5D\xDF\xC6\xD2\x6C\x07\x91\x33"
+       "\x98\x42\xF0\x02\x00\x25\x38\xC5\x85\x69\x8A\x7D\x2F\x95\x6C\x43"
+       "\x9A\xB8\x81\xE2\xD0\x07\x35\xAA\x05\x41\xC9\x1E\xAF\xE4\x04\x3B"
+       "\x19\xB8\x73\xA2\xAC\x4B\x1E\x66\x48\xD8\x72\x1F\xAC\xF6\xCB\xBC"
+       "\x90\x09\xCA\xEC\x0C\xDC\xF9\x2C\xD7\xEB\xAE\xA3\xA4\x47\xD7\x33"
+       "\x2F\x8A\xCA\xBC\x5E\xF0\x77\xE4\x97\x98\x97\xC7\x10\x91\x7D\x2A"
+       "\xA6\xFF\x46\x83\x97\xDE\xE9\xE2\x17\x03\x06\x14\xE2\xD7\xB1\x1D"
+       "\x77\xAF\x51\x27\x5B\x5E\x69\xB8\x81\xE6\x11\xC5\x43\x23\x81\x04"
+       "\x62\xFF\xE9\x46\xB8\xD8\x44\xDB\xA5\xCC\x31\x54\x34\xCE\x3E\x82"
+       "\xD6\xBF\x7A\x0B\x64\x21\x6D\x88\x7E\x5B\x45\x12\x1E\x63\x8D\x49"
+       "\xA7\x1D\xD9\x1E\x06\xCD\xE8\xBA\x2C\x8C\x69\x32\xEA\xBE\x60\x71",
+       .key_len = 529,
+       .m = "\x54\x85\x9b\x34\x2c\x49\xea\x2a",
+       .c =
+       "\xb2\x97\x76\xb4\xae\x3e\x38\x3c\x7e\x64\x1f\xcc\xa2\x7f\xf6\xbe"
+       "\xcf\x49\xbc\x48\xd3\x6c\x8f\x0a\x0e\xc1\x73\xbd\x7b\x55\x79\x36"
+       "\x0e\xa1\x87\x88\xb9\x2c\x90\xa6\x53\x5e\xe9\xef\xc4\xe2\x4d\xdd"
+       "\xf7\xa6\x69\x82\x3f\x56\xa4\x7b\xfb\x62\xe0\xae\xb8\xd3\x04\xb3"
+       "\xac\x5a\x15\x2a\xe3\x19\x9b\x03\x9a\x0b\x41\xda\x64\xec\x0a\x69"
+       "\xfc\xf2\x10\x92\xf3\xc1\xbf\x84\x7f\xfd\x2c\xae\xc8\xb5\xf6\x41"
+       "\x70\xc5\x47\x03\x8a\xf8\xff\x6f\x3f\xd2\x6f\x09\xb4\x22\xf3\x30"
+       "\xbe\xa9\x85\xcb\x9c\x8d\xf9\x8f\xeb\x32\x91\xa2\x25\x84\x8f\xf5"
+       "\xdc\xc7\x06\x9c\x2d\xe5\x11\x2c\x09\x09\x87\x09\xa9\xf6\x33\x73"
+       "\x90\xf1\x60\xf2\x65\xdd\x30\xa5\x66\xce\x62\x7b\xd0\xf8\x2d\x3d"
+       "\x19\x82\x77\xe3\x0a\x5f\x75\x2f\x8e\xb1\xe5\xe8\x91\x35\x1b\x3b"
+       "\x33\xb7\x66\x92\xd1\xf2\x8e\x6f\xe5\x75\x0c\xad\x36\xfb\x4e\xd0"
+       "\x66\x61\xbd\x49\xfe\xf4\x1a\xa2\x2b\x49\xfe\x03\x4c\x74\x47\x8d"
+       "\x9a\x66\xb2\x49\x46\x4d\x77\xea\x33\x4d\x6b\x3c\xb4\x49\x4a\xc6"
+       "\x7d\x3d\xb5\xb9\x56\x41\x15\x67\x0f\x94\x3c\x93\x65\x27\xe0\x21"
+       "\x5d\x59\xc3\x62\xd5\xa6\xda\x38\x26\x22\x5e\x34\x1c\x94\xaf\x98",
+       .m_size = 8,
+       .c_size = 256,
+       }, {
+       .key =
+       "\x30\x82\x01\x09" /* sequence of 265 bytes */
+       "\x02\x82\x01\x00" /* modulus - integer of 256 bytes */
+       "\xDB\x10\x1A\xC2\xA3\xF1\xDC\xFF\x13\x6B\xED\x44\xDF\xF0\x02\x6D"
+       "\x13\xC7\x88\xDA\x70\x6B\x54\xF1\xE8\x27\xDC\xC3\x0F\x99\x6A\xFA"
+       "\xC6\x67\xFF\x1D\x1E\x3C\x1D\xC1\xB5\x5F\x6C\xC0\xB2\x07\x3A\x6D"
+       "\x41\xE4\x25\x99\xAC\xFC\xD2\x0F\x02\xD3\xD1\x54\x06\x1A\x51\x77"
+       "\xBD\xB6\xBF\xEA\xA7\x5C\x06\xA9\x5D\x69\x84\x45\xD7\xF5\x05\xBA"
+       "\x47\xF0\x1B\xD7\x2B\x24\xEC\xCB\x9B\x1B\x10\x8D\x81\xA0\xBE\xB1"
+       "\x8C\x33\xE4\x36\xB8\x43\xEB\x19\x2A\x81\x8D\xDE\x81\x0A\x99\x48"
+       "\xB6\xF6\xBC\xCD\x49\x34\x3A\x8F\x26\x94\xE3\x28\x82\x1A\x7C\x8F"
+       "\x59\x9F\x45\xE8\x5D\x1A\x45\x76\x04\x56\x05\xA1\xD0\x1B\x8C\x77"
+       "\x6D\xAF\x53\xFA\x71\xE2\x67\xE0\x9A\xFE\x03\xA9\x85\xD2\xC9\xAA"
+       "\xBA\x2A\xBC\xF4\xA0\x08\xF5\x13\x98\x13\x5D\xF0\xD9\x33\x34\x2A"
+       "\x61\xC3\x89\x55\xF0\xAE\x1A\x9C\x22\xEE\x19\x05\x8D\x32\xFE\xEC"
+       "\x9C\x84\xBA\xB7\xF9\x6C\x3A\x4F\x07\xFC\x45\xEB\x12\xE5\x7B\xFD"
+       "\x55\xE6\x29\x69\xD1\xC2\xE8\xB9\x78\x59\xF6\x79\x10\xC6\x4E\xEB"
+       "\x6A\x5E\xB9\x9A\xC7\xC4\x5B\x63\xDA\xA3\x3F\x5E\x92\x7A\x81\x5E"
+       "\xD6\xB0\xE2\x62\x8F\x74\x26\xC2\x0C\xD3\x9A\x17\x47\xE6\x8E\xAB"
+       "\x02\x03\x01\x00\x01", /* public key - integer of 3 bytes */
+       .key_len = 269,
+       .m = "\x54\x85\x9b\x34\x2c\x49\xea\x2a",
+       .c =
+       "\xb2\x97\x76\xb4\xae\x3e\x38\x3c\x7e\x64\x1f\xcc\xa2\x7f\xf6\xbe"
+       "\xcf\x49\xbc\x48\xd3\x6c\x8f\x0a\x0e\xc1\x73\xbd\x7b\x55\x79\x36"
+       "\x0e\xa1\x87\x88\xb9\x2c\x90\xa6\x53\x5e\xe9\xef\xc4\xe2\x4d\xdd"
+       "\xf7\xa6\x69\x82\x3f\x56\xa4\x7b\xfb\x62\xe0\xae\xb8\xd3\x04\xb3"
+       "\xac\x5a\x15\x2a\xe3\x19\x9b\x03\x9a\x0b\x41\xda\x64\xec\x0a\x69"
+       "\xfc\xf2\x10\x92\xf3\xc1\xbf\x84\x7f\xfd\x2c\xae\xc8\xb5\xf6\x41"
+       "\x70\xc5\x47\x03\x8a\xf8\xff\x6f\x3f\xd2\x6f\x09\xb4\x22\xf3\x30"
+       "\xbe\xa9\x85\xcb\x9c\x8d\xf9\x8f\xeb\x32\x91\xa2\x25\x84\x8f\xf5"
+       "\xdc\xc7\x06\x9c\x2d\xe5\x11\x2c\x09\x09\x87\x09\xa9\xf6\x33\x73"
+       "\x90\xf1\x60\xf2\x65\xdd\x30\xa5\x66\xce\x62\x7b\xd0\xf8\x2d\x3d"
+       "\x19\x82\x77\xe3\x0a\x5f\x75\x2f\x8e\xb1\xe5\xe8\x91\x35\x1b\x3b"
+       "\x33\xb7\x66\x92\xd1\xf2\x8e\x6f\xe5\x75\x0c\xad\x36\xfb\x4e\xd0"
+       "\x66\x61\xbd\x49\xfe\xf4\x1a\xa2\x2b\x49\xfe\x03\x4c\x74\x47\x8d"
+       "\x9a\x66\xb2\x49\x46\x4d\x77\xea\x33\x4d\x6b\x3c\xb4\x49\x4a\xc6"
+       "\x7d\x3d\xb5\xb9\x56\x41\x15\x67\x0f\x94\x3c\x93\x65\x27\xe0\x21"
+       "\x5d\x59\xc3\x62\xd5\xa6\xda\x38\x26\x22\x5e\x34\x1c\x94\xaf\x98",
+       .m_size = 8,
+       .c_size = 256,
+       .public_key_vec = true,
+       }
+};
+
 /*
  * MD4 test vectors from RFC1320
  */
@@ -1822,7 +2027,7 @@ static struct hash_testvec tgr128_tv_template[] = {
        },
 };
 
-#define GHASH_TEST_VECTORS 5
+#define GHASH_TEST_VECTORS 6
 
 static struct hash_testvec ghash_tv_template[] =
 {
@@ -1875,6 +2080,63 @@ static struct hash_testvec ghash_tv_template[] =
                .psize  = 20,
                .digest = "\xf8\x94\x87\x2a\x4b\x63\x99\x28"
                          "\x23\xf7\x93\xf7\x19\xf5\x96\xd9",
+       }, {
+               .key    = "\x0a\x1b\x2c\x3d\x4e\x5f\x64\x71"
+                       "\x82\x93\xa4\xb5\xc6\xd7\xe8\xf9",
+               .ksize  = 16,
+               .plaintext = "\x56\x6f\x72\x20\x6c\x61\x75\x74"
+                       "\x65\x72\x20\x4c\x61\x75\x73\x63"
+                       "\x68\x65\x6e\x20\x75\x6e\x64\x20"
+                       "\x53\x74\x61\x75\x6e\x65\x6e\x20"
+                       "\x73\x65\x69\x20\x73\x74\x69\x6c"
+                       "\x6c\x2c\x0a\x64\x75\x20\x6d\x65"
+                       "\x69\x6e\x20\x74\x69\x65\x66\x74"
+                       "\x69\x65\x66\x65\x73\x20\x4c\x65"
+                       "\x62\x65\x6e\x3b\x0a\x64\x61\x73"
+                       "\x73\x20\x64\x75\x20\x77\x65\x69"
+                       "\xc3\x9f\x74\x20\x77\x61\x73\x20"
+                       "\x64\x65\x72\x20\x57\x69\x6e\x64"
+                       "\x20\x64\x69\x72\x20\x77\x69\x6c"
+                       "\x6c\x2c\x0a\x65\x68\x20\x6e\x6f"
+                       "\x63\x68\x20\x64\x69\x65\x20\x42"
+                       "\x69\x72\x6b\x65\x6e\x20\x62\x65"
+                       "\x62\x65\x6e\x2e\x0a\x0a\x55\x6e"
+                       "\x64\x20\x77\x65\x6e\x6e\x20\x64"
+                       "\x69\x72\x20\x65\x69\x6e\x6d\x61"
+                       "\x6c\x20\x64\x61\x73\x20\x53\x63"
+                       "\x68\x77\x65\x69\x67\x65\x6e\x20"
+                       "\x73\x70\x72\x61\x63\x68\x2c\x0a"
+                       "\x6c\x61\x73\x73\x20\x64\x65\x69"
+                       "\x6e\x65\x20\x53\x69\x6e\x6e\x65"
+                       "\x20\x62\x65\x73\x69\x65\x67\x65"
+                       "\x6e\x2e\x0a\x4a\x65\x64\x65\x6d"
+                       "\x20\x48\x61\x75\x63\x68\x65\x20"
+                       "\x67\x69\x62\x74\x20\x64\x69\x63"
+                       "\x68\x2c\x20\x67\x69\x62\x20\x6e"
+                       "\x61\x63\x68\x2c\x0a\x65\x72\x20"
+                       "\x77\x69\x72\x64\x20\x64\x69\x63"
+                       "\x68\x20\x6c\x69\x65\x62\x65\x6e"
+                       "\x20\x75\x6e\x64\x20\x77\x69\x65"
+                       "\x67\x65\x6e\x2e\x0a\x0a\x55\x6e"
+                       "\x64\x20\x64\x61\x6e\x6e\x20\x6d"
+                       "\x65\x69\x6e\x65\x20\x53\x65\x65"
+                       "\x6c\x65\x20\x73\x65\x69\x74\x20"
+                       "\x77\x65\x69\x74\x2c\x20\x73\x65"
+                       "\x69\x20\x77\x65\x69\x74\x2c\x0a"
+                       "\x64\x61\x73\x73\x20\x64\x69\x72"
+                       "\x20\x64\x61\x73\x20\x4c\x65\x62"
+                       "\x65\x6e\x20\x67\x65\x6c\x69\x6e"
+                       "\x67\x65\x2c\x0a\x62\x72\x65\x69"
+                       "\x74\x65\x20\x64\x69\x63\x68\x20"
+                       "\x77\x69\x65\x20\x65\x69\x6e\x20"
+                       "\x46\x65\x69\x65\x72\x6b\x6c\x65"
+                       "\x69\x64\x0a\xc3\xbc\x62\x65\x72"
+                       "\x20\x64\x69\x65\x20\x73\x69\x6e"
+                       "\x6e\x65\x6e\x64\x65\x6e\x20\x44"
+                       "\x69\x6e\x67\x65\x2e\x2e\x2e\x0a",
+               .psize  = 400,
+               .digest = "\xad\xb1\xc1\xe9\x56\x70\x31\x1d"
+                       "\xbb\x5b\xdf\x5e\x70\x72\x1a\x57",
        },
 };
 
@@ -2968,6 +3230,254 @@ static struct hash_testvec hmac_sha512_tv_template[] = {
        },
 };
 
+/*
+ * Poly1305 test vectors from RFC7539 A.3.
+ */
+
+#define POLY1305_TEST_VECTORS  11
+
+static struct hash_testvec poly1305_tv_template[] = {
+       { /* Test Vector #1 */
+               .plaintext      = "\x00\x00\x00\x00\x00\x00\x00\x00"
+                                 "\x00\x00\x00\x00\x00\x00\x00\x00"
+                                 "\x00\x00\x00\x00\x00\x00\x00\x00"
+                                 "\x00\x00\x00\x00\x00\x00\x00\x00"
+                                 "\x00\x00\x00\x00\x00\x00\x00\x00"
+                                 "\x00\x00\x00\x00\x00\x00\x00\x00"
+                                 "\x00\x00\x00\x00\x00\x00\x00\x00"
+                                 "\x00\x00\x00\x00\x00\x00\x00\x00"
+                                 "\x00\x00\x00\x00\x00\x00\x00\x00"
+                                 "\x00\x00\x00\x00\x00\x00\x00\x00"
+                                 "\x00\x00\x00\x00\x00\x00\x00\x00"
+                                 "\x00\x00\x00\x00\x00\x00\x00\x00",
+               .psize          = 96,
+               .digest         = "\x00\x00\x00\x00\x00\x00\x00\x00"
+                                 "\x00\x00\x00\x00\x00\x00\x00\x00",
+       }, { /* Test Vector #2 */
+               .plaintext      = "\x00\x00\x00\x00\x00\x00\x00\x00"
+                                 "\x00\x00\x00\x00\x00\x00\x00\x00"
+                                 "\x36\xe5\xf6\xb5\xc5\xe0\x60\x70"
+                                 "\xf0\xef\xca\x96\x22\x7a\x86\x3e"
+                                 "\x41\x6e\x79\x20\x73\x75\x62\x6d"
+                                 "\x69\x73\x73\x69\x6f\x6e\x20\x74"
+                                 "\x6f\x20\x74\x68\x65\x20\x49\x45"
+                                 "\x54\x46\x20\x69\x6e\x74\x65\x6e"
+                                 "\x64\x65\x64\x20\x62\x79\x20\x74"
+                                 "\x68\x65\x20\x43\x6f\x6e\x74\x72"
+                                 "\x69\x62\x75\x74\x6f\x72\x20\x66"
+                                 "\x6f\x72\x20\x70\x75\x62\x6c\x69"
+                                 "\x63\x61\x74\x69\x6f\x6e\x20\x61"
+                                 "\x73\x20\x61\x6c\x6c\x20\x6f\x72"
+                                 "\x20\x70\x61\x72\x74\x20\x6f\x66"
+                                 "\x20\x61\x6e\x20\x49\x45\x54\x46"
+                                 "\x20\x49\x6e\x74\x65\x72\x6e\x65"
+                                 "\x74\x2d\x44\x72\x61\x66\x74\x20"
+                                 "\x6f\x72\x20\x52\x46\x43\x20\x61"
+                                 "\x6e\x64\x20\x61\x6e\x79\x20\x73"
+                                 "\x74\x61\x74\x65\x6d\x65\x6e\x74"
+                                 "\x20\x6d\x61\x64\x65\x20\x77\x69"
+                                 "\x74\x68\x69\x6e\x20\x74\x68\x65"
+                                 "\x20\x63\x6f\x6e\x74\x65\x78\x74"
+                                 "\x20\x6f\x66\x20\x61\x6e\x20\x49"
+                                 "\x45\x54\x46\x20\x61\x63\x74\x69"
+                                 "\x76\x69\x74\x79\x20\x69\x73\x20"
+                                 "\x63\x6f\x6e\x73\x69\x64\x65\x72"
+                                 "\x65\x64\x20\x61\x6e\x20\x22\x49"
+                                 "\x45\x54\x46\x20\x43\x6f\x6e\x74"
+                                 "\x72\x69\x62\x75\x74\x69\x6f\x6e"
+                                 "\x22\x2e\x20\x53\x75\x63\x68\x20"
+                                 "\x73\x74\x61\x74\x65\x6d\x65\x6e"
+                                 "\x74\x73\x20\x69\x6e\x63\x6c\x75"
+                                 "\x64\x65\x20\x6f\x72\x61\x6c\x20"
+                                 "\x73\x74\x61\x74\x65\x6d\x65\x6e"
+                                 "\x74\x73\x20\x69\x6e\x20\x49\x45"
+                                 "\x54\x46\x20\x73\x65\x73\x73\x69"
+                                 "\x6f\x6e\x73\x2c\x20\x61\x73\x20"
+                                 "\x77\x65\x6c\x6c\x20\x61\x73\x20"
+                                 "\x77\x72\x69\x74\x74\x65\x6e\x20"
+                                 "\x61\x6e\x64\x20\x65\x6c\x65\x63"
+                                 "\x74\x72\x6f\x6e\x69\x63\x20\x63"
+                                 "\x6f\x6d\x6d\x75\x6e\x69\x63\x61"
+                                 "\x74\x69\x6f\x6e\x73\x20\x6d\x61"
+                                 "\x64\x65\x20\x61\x74\x20\x61\x6e"
+                                 "\x79\x20\x74\x69\x6d\x65\x20\x6f"
+                                 "\x72\x20\x70\x6c\x61\x63\x65\x2c"
+                                 "\x20\x77\x68\x69\x63\x68\x20\x61"
+                                 "\x72\x65\x20\x61\x64\x64\x72\x65"
+                                 "\x73\x73\x65\x64\x20\x74\x6f",
+               .psize          = 407,
+               .digest         = "\x36\xe5\xf6\xb5\xc5\xe0\x60\x70"
+                                 "\xf0\xef\xca\x96\x22\x7a\x86\x3e",
+       }, { /* Test Vector #3 */
+               .plaintext      = "\x36\xe5\xf6\xb5\xc5\xe0\x60\x70"
+                                 "\xf0\xef\xca\x96\x22\x7a\x86\x3e"
+                                 "\x00\x00\x00\x00\x00\x00\x00\x00"
+                                 "\x00\x00\x00\x00\x00\x00\x00\x00"
+                                 "\x41\x6e\x79\x20\x73\x75\x62\x6d"
+                                 "\x69\x73\x73\x69\x6f\x6e\x20\x74"
+                                 "\x6f\x20\x74\x68\x65\x20\x49\x45"
+                                 "\x54\x46\x20\x69\x6e\x74\x65\x6e"
+                                 "\x64\x65\x64\x20\x62\x79\x20\x74"
+                                 "\x68\x65\x20\x43\x6f\x6e\x74\x72"
+                                 "\x69\x62\x75\x74\x6f\x72\x20\x66"
+                                 "\x6f\x72\x20\x70\x75\x62\x6c\x69"
+                                 "\x63\x61\x74\x69\x6f\x6e\x20\x61"
+                                 "\x73\x20\x61\x6c\x6c\x20\x6f\x72"
+                                 "\x20\x70\x61\x72\x74\x20\x6f\x66"
+                                 "\x20\x61\x6e\x20\x49\x45\x54\x46"
+                                 "\x20\x49\x6e\x74\x65\x72\x6e\x65"
+                                 "\x74\x2d\x44\x72\x61\x66\x74\x20"
+                                 "\x6f\x72\x20\x52\x46\x43\x20\x61"
+                                 "\x6e\x64\x20\x61\x6e\x79\x20\x73"
+                                 "\x74\x61\x74\x65\x6d\x65\x6e\x74"
+                                 "\x20\x6d\x61\x64\x65\x20\x77\x69"
+                                 "\x74\x68\x69\x6e\x20\x74\x68\x65"
+                                 "\x20\x63\x6f\x6e\x74\x65\x78\x74"
+                                 "\x20\x6f\x66\x20\x61\x6e\x20\x49"
+                                 "\x45\x54\x46\x20\x61\x63\x74\x69"
+                                 "\x76\x69\x74\x79\x20\x69\x73\x20"
+                                 "\x63\x6f\x6e\x73\x69\x64\x65\x72"
+                                 "\x65\x64\x20\x61\x6e\x20\x22\x49"
+                                 "\x45\x54\x46\x20\x43\x6f\x6e\x74"
+                                 "\x72\x69\x62\x75\x74\x69\x6f\x6e"
+                                 "\x22\x2e\x20\x53\x75\x63\x68\x20"
+                                 "\x73\x74\x61\x74\x65\x6d\x65\x6e"
+                                 "\x74\x73\x20\x69\x6e\x63\x6c\x75"
+                                 "\x64\x65\x20\x6f\x72\x61\x6c\x20"
+                                 "\x73\x74\x61\x74\x65\x6d\x65\x6e"
+                                 "\x74\x73\x20\x69\x6e\x20\x49\x45"
+                                 "\x54\x46\x20\x73\x65\x73\x73\x69"
+                                 "\x6f\x6e\x73\x2c\x20\x61\x73\x20"
+                                 "\x77\x65\x6c\x6c\x20\x61\x73\x20"
+                                 "\x77\x72\x69\x74\x74\x65\x6e\x20"
+                                 "\x61\x6e\x64\x20\x65\x6c\x65\x63"
+                                 "\x74\x72\x6f\x6e\x69\x63\x20\x63"
+                                 "\x6f\x6d\x6d\x75\x6e\x69\x63\x61"
+                                 "\x74\x69\x6f\x6e\x73\x20\x6d\x61"
+                                 "\x64\x65\x20\x61\x74\x20\x61\x6e"
+                                 "\x79\x20\x74\x69\x6d\x65\x20\x6f"
+                                 "\x72\x20\x70\x6c\x61\x63\x65\x2c"
+                                 "\x20\x77\x68\x69\x63\x68\x20\x61"
+                                 "\x72\x65\x20\x61\x64\x64\x72\x65"
+                                 "\x73\x73\x65\x64\x20\x74\x6f",
+               .psize          = 407,
+               .digest         = "\xf3\x47\x7e\x7c\xd9\x54\x17\xaf"
+                                 "\x89\xa6\xb8\x79\x4c\x31\x0c\xf0",
+       }, { /* Test Vector #4 */
+               .plaintext      = "\x1c\x92\x40\xa5\xeb\x55\xd3\x8a"
+                                 "\xf3\x33\x88\x86\x04\xf6\xb5\xf0"
+                                 "\x47\x39\x17\xc1\x40\x2b\x80\x09"
+                                 "\x9d\xca\x5c\xbc\x20\x70\x75\xc0"
+                                 "\x27\x54\x77\x61\x73\x20\x62\x72"
+                                 "\x69\x6c\x6c\x69\x67\x2c\x20\x61"
+                                 "\x6e\x64\x20\x74\x68\x65\x20\x73"
+                                 "\x6c\x69\x74\x68\x79\x20\x74\x6f"
+                                 "\x76\x65\x73\x0a\x44\x69\x64\x20"
+                                 "\x67\x79\x72\x65\x20\x61\x6e\x64"
+                                 "\x20\x67\x69\x6d\x62\x6c\x65\x20"
+                                 "\x69\x6e\x20\x74\x68\x65\x20\x77"
+                                 "\x61\x62\x65\x3a\x0a\x41\x6c\x6c"
+                                 "\x20\x6d\x69\x6d\x73\x79\x20\x77"
+                                 "\x65\x72\x65\x20\x74\x68\x65\x20"
+                                 "\x62\x6f\x72\x6f\x67\x6f\x76\x65"
+                                 "\x73\x2c\x0a\x41\x6e\x64\x20\x74"
+                                 "\x68\x65\x20\x6d\x6f\x6d\x65\x20"
+                                 "\x72\x61\x74\x68\x73\x20\x6f\x75"
+                                 "\x74\x67\x72\x61\x62\x65\x2e",
+               .psize          = 159,
+               .digest         = "\x45\x41\x66\x9a\x7e\xaa\xee\x61"
+                                 "\xe7\x08\xdc\x7c\xbc\xc5\xeb\x62",
+       }, { /* Test Vector #5 */
+               .plaintext      = "\x02\x00\x00\x00\x00\x00\x00\x00"
+                                 "\x00\x00\x00\x00\x00\x00\x00\x00"
+                                 "\x00\x00\x00\x00\x00\x00\x00\x00"
+                                 "\x00\x00\x00\x00\x00\x00\x00\x00"
+                                 "\xff\xff\xff\xff\xff\xff\xff\xff"
+                                 "\xff\xff\xff\xff\xff\xff\xff\xff",
+               .psize          = 48,
+               .digest         = "\x03\x00\x00\x00\x00\x00\x00\x00"
+                                 "\x00\x00\x00\x00\x00\x00\x00\x00",
+       }, { /* Test Vector #6 */
+               .plaintext      = "\x02\x00\x00\x00\x00\x00\x00\x00"
+                                 "\x00\x00\x00\x00\x00\x00\x00\x00"
+                                 "\xff\xff\xff\xff\xff\xff\xff\xff"
+                                 "\xff\xff\xff\xff\xff\xff\xff\xff"
+                                 "\x02\x00\x00\x00\x00\x00\x00\x00"
+                                 "\x00\x00\x00\x00\x00\x00\x00\x00",
+               .psize          = 48,
+               .digest         = "\x03\x00\x00\x00\x00\x00\x00\x00"
+                                 "\x00\x00\x00\x00\x00\x00\x00\x00",
+       }, { /* Test Vector #7 */
+               .plaintext      = "\x01\x00\x00\x00\x00\x00\x00\x00"
+                                 "\x00\x00\x00\x00\x00\x00\x00\x00"
+                                 "\x00\x00\x00\x00\x00\x00\x00\x00"
+                                 "\x00\x00\x00\x00\x00\x00\x00\x00"
+                                 "\xff\xff\xff\xff\xff\xff\xff\xff"
+                                 "\xff\xff\xff\xff\xff\xff\xff\xff"
+                                 "\xf0\xff\xff\xff\xff\xff\xff\xff"
+                                 "\xff\xff\xff\xff\xff\xff\xff\xff"
+                                 "\x11\x00\x00\x00\x00\x00\x00\x00"
+                                 "\x00\x00\x00\x00\x00\x00\x00\x00",
+               .psize          = 80,
+               .digest         = "\x05\x00\x00\x00\x00\x00\x00\x00"
+                                 "\x00\x00\x00\x00\x00\x00\x00\x00",
+       }, { /* Test Vector #8 */
+               .plaintext      = "\x01\x00\x00\x00\x00\x00\x00\x00"
+                                 "\x00\x00\x00\x00\x00\x00\x00\x00"
+                                 "\x00\x00\x00\x00\x00\x00\x00\x00"
+                                 "\x00\x00\x00\x00\x00\x00\x00\x00"
+                                 "\xff\xff\xff\xff\xff\xff\xff\xff"
+                                 "\xff\xff\xff\xff\xff\xff\xff\xff"
+                                 "\xfb\xfe\xfe\xfe\xfe\xfe\xfe\xfe"
+                                 "\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe"
+                                 "\x01\x01\x01\x01\x01\x01\x01\x01"
+                                 "\x01\x01\x01\x01\x01\x01\x01\x01",
+               .psize          = 80,
+               .digest         = "\x00\x00\x00\x00\x00\x00\x00\x00"
+                                 "\x00\x00\x00\x00\x00\x00\x00\x00",
+       }, { /* Test Vector #9 */
+               .plaintext      = "\x02\x00\x00\x00\x00\x00\x00\x00"
+                                 "\x00\x00\x00\x00\x00\x00\x00\x00"
+                                 "\x00\x00\x00\x00\x00\x00\x00\x00"
+                                 "\x00\x00\x00\x00\x00\x00\x00\x00"
+                                 "\xfd\xff\xff\xff\xff\xff\xff\xff"
+                                 "\xff\xff\xff\xff\xff\xff\xff\xff",
+               .psize          = 48,
+               .digest         = "\xfa\xff\xff\xff\xff\xff\xff\xff"
+                                 "\xff\xff\xff\xff\xff\xff\xff\xff",
+       }, { /* Test Vector #10 */
+               .plaintext      = "\x01\x00\x00\x00\x00\x00\x00\x00"
+                                 "\x04\x00\x00\x00\x00\x00\x00\x00"
+                                 "\x00\x00\x00\x00\x00\x00\x00\x00"
+                                 "\x00\x00\x00\x00\x00\x00\x00\x00"
+                                 "\xe3\x35\x94\xd7\x50\x5e\x43\xb9"
+                                 "\x00\x00\x00\x00\x00\x00\x00\x00"
+                                 "\x33\x94\xd7\x50\x5e\x43\x79\xcd"
+                                 "\x01\x00\x00\x00\x00\x00\x00\x00"
+                                 "\x00\x00\x00\x00\x00\x00\x00\x00"
+                                 "\x00\x00\x00\x00\x00\x00\x00\x00"
+                                 "\x01\x00\x00\x00\x00\x00\x00\x00"
+                                 "\x00\x00\x00\x00\x00\x00\x00\x00",
+               .psize          = 96,
+               .digest         = "\x14\x00\x00\x00\x00\x00\x00\x00"
+                                 "\x55\x00\x00\x00\x00\x00\x00\x00",
+       }, { /* Test Vector #11 */
+               .plaintext      = "\x01\x00\x00\x00\x00\x00\x00\x00"
+                                 "\x04\x00\x00\x00\x00\x00\x00\x00"
+                                 "\x00\x00\x00\x00\x00\x00\x00\x00"
+                                 "\x00\x00\x00\x00\x00\x00\x00\x00"
+                                 "\xe3\x35\x94\xd7\x50\x5e\x43\xb9"
+                                 "\x00\x00\x00\x00\x00\x00\x00\x00"
+                                 "\x33\x94\xd7\x50\x5e\x43\x79\xcd"
+                                 "\x01\x00\x00\x00\x00\x00\x00\x00"
+                                 "\x00\x00\x00\x00\x00\x00\x00\x00"
+                                 "\x00\x00\x00\x00\x00\x00\x00\x00",
+               .psize          = 80,
+               .digest         = "\x13\x00\x00\x00\x00\x00\x00\x00"
+                                 "\x00\x00\x00\x00\x00\x00\x00\x00",
+       },
+};
+
 /*
  * DES test vectors.
  */
@@ -3018,7 +3528,7 @@ static struct cipher_testvec des_enc_tv_template[] = {
                          "\xb4\x99\x26\xf7\x1f\xe1\xd4\x90",
                .rlen   = 24,
        }, { /* Weak key */
-               .fail   = 1,
+               .fail   = true,
                .wk     = 1,
                .key    = "\x01\x01\x01\x01\x01\x01\x01\x01",
                .klen   = 8,
@@ -13629,8 +14139,8 @@ static struct cipher_testvec cast6_xts_dec_tv_template[] = {
 #define AES_CTR_3686_DEC_TEST_VECTORS 6
 #define AES_GCM_ENC_TEST_VECTORS 9
 #define AES_GCM_DEC_TEST_VECTORS 8
-#define AES_GCM_4106_ENC_TEST_VECTORS 7
-#define AES_GCM_4106_DEC_TEST_VECTORS 7
+#define AES_GCM_4106_ENC_TEST_VECTORS 23
+#define AES_GCM_4106_DEC_TEST_VECTORS 23
 #define AES_GCM_4543_ENC_TEST_VECTORS 1
 #define AES_GCM_4543_DEC_TEST_VECTORS 2
 #define AES_CCM_ENC_TEST_VECTORS 8
@@ -19789,6 +20299,428 @@ static struct aead_testvec aes_gcm_rfc4106_enc_tv_template[] = {
                          "\x37\x08\x1C\xCF\xBA\x5D\x71\x46"
                          "\x80\x72\xB0\x4C\x82\x0D\x60\x3C",
                .rlen   = 208,
+       }, { /* From draft-mcgrew-gcm-test-01 */
+               .key    = "\x4C\x80\xCD\xEF\xBB\x5D\x10\xDA"
+                         "\x90\x6A\xC7\x3C\x36\x13\xA6\x34"
+                         "\x2E\x44\x3B\x68",
+               .klen   = 20,
+               .iv     = "\x49\x56\xED\x7E\x3B\x24\x4C\xFE",
+               .input  = "\x45\x00\x00\x48\x69\x9A\x00\x00"
+                         "\x80\x11\x4D\xB7\xC0\xA8\x01\x02"
+                         "\xC0\xA8\x01\x01\x0A\x9B\xF1\x56"
+                         "\x38\xD3\x01\x00\x00\x01\x00\x00"
+                         "\x00\x00\x00\x00\x04\x5F\x73\x69"
+                         "\x70\x04\x5F\x75\x64\x70\x03\x73"
+                         "\x69\x70\x09\x63\x79\x62\x65\x72"
+                         "\x63\x69\x74\x79\x02\x64\x6B\x00"
+                         "\x00\x21\x00\x01\x01\x02\x02\x01",
+               .ilen   = 72,
+               .assoc  = "\x00\x00\x43\x21\x87\x65\x43\x21"
+                         "\x00\x00\x00\x00",
+               .alen   = 12,
+               .result = "\xFE\xCF\x53\x7E\x72\x9D\x5B\x07"
+                         "\xDC\x30\xDF\x52\x8D\xD2\x2B\x76"
+                         "\x8D\x1B\x98\x73\x66\x96\xA6\xFD"
+                         "\x34\x85\x09\xFA\x13\xCE\xAC\x34"
+                         "\xCF\xA2\x43\x6F\x14\xA3\xF3\xCF"
+                         "\x65\x92\x5B\xF1\xF4\xA1\x3C\x5D"
+                         "\x15\xB2\x1E\x18\x84\xF5\xFF\x62"
+                         "\x47\xAE\xAB\xB7\x86\xB9\x3B\xCE"
+                         "\x61\xBC\x17\xD7\x68\xFD\x97\x32"
+                         "\x45\x90\x18\x14\x8F\x6C\xBE\x72"
+                         "\x2F\xD0\x47\x96\x56\x2D\xFD\xB4",
+               .rlen   = 88,
+       }, {
+               .key    = "\xFE\xFF\xE9\x92\x86\x65\x73\x1C"
+                         "\x6D\x6A\x8F\x94\x67\x30\x83\x08"
+                         "\xCA\xFE\xBA\xBE",
+               .klen   = 20,
+               .iv     = "\xFA\xCE\xDB\xAD\xDE\xCA\xF8\x88",
+               .input  = "\x45\x00\x00\x3E\x69\x8F\x00\x00"
+                         "\x80\x11\x4D\xCC\xC0\xA8\x01\x02"
+                         "\xC0\xA8\x01\x01\x0A\x98\x00\x35"
+                         "\x00\x2A\x23\x43\xB2\xD0\x01\x00"
+                         "\x00\x01\x00\x00\x00\x00\x00\x00"
+                         "\x03\x73\x69\x70\x09\x63\x79\x62"
+                         "\x65\x72\x63\x69\x74\x79\x02\x64"
+                         "\x6B\x00\x00\x01\x00\x01\x00\x01",
+               .ilen   = 64,
+               .assoc  = "\x00\x00\xA5\xF8\x00\x00\x00\x0A",
+               .alen   = 8,
+               .result = "\xDE\xB2\x2C\xD9\xB0\x7C\x72\xC1"
+                         "\x6E\x3A\x65\xBE\xEB\x8D\xF3\x04"
+                         "\xA5\xA5\x89\x7D\x33\xAE\x53\x0F"
+                         "\x1B\xA7\x6D\x5D\x11\x4D\x2A\x5C"
+                         "\x3D\xE8\x18\x27\xC1\x0E\x9A\x4F"
+                         "\x51\x33\x0D\x0E\xEC\x41\x66\x42"
+                         "\xCF\xBB\x85\xA5\xB4\x7E\x48\xA4"
+                         "\xEC\x3B\x9B\xA9\x5D\x91\x8B\xD1"
+                         "\x83\xB7\x0D\x3A\xA8\xBC\x6E\xE4"
+                         "\xC3\x09\xE9\xD8\x5A\x41\xAD\x4A",
+               .rlen   = 80,
+       }, {
+               .key    = "\xAB\xBC\xCD\xDE\xF0\x01\x12\x23"
+                         "\x34\x45\x56\x67\x78\x89\x9A\xAB"
+                         "\xAB\xBC\xCD\xDE\xF0\x01\x12\x23"
+                         "\x34\x45\x56\x67\x78\x89\x9A\xAB"
+                         "\x11\x22\x33\x44",
+               .klen   = 36,
+               .iv     = "\x01\x02\x03\x04\x05\x06\x07\x08",
+               .input  = "\x45\x00\x00\x30\x69\xA6\x40\x00"
+                         "\x80\x06\x26\x90\xC0\xA8\x01\x02"
+                         "\x93\x89\x15\x5E\x0A\x9E\x00\x8B"
+                         "\x2D\xC5\x7E\xE0\x00\x00\x00\x00"
+                         "\x70\x02\x40\x00\x20\xBF\x00\x00"
+                         "\x02\x04\x05\xB4\x01\x01\x04\x02"
+                         "\x01\x02\x02\x01",
+               .ilen   = 52,
+               .assoc  = "\x4A\x2C\xBF\xE3\x00\x00\x00\x02",
+               .alen   = 8,
+               .result = "\xFF\x42\x5C\x9B\x72\x45\x99\xDF"
+                         "\x7A\x3B\xCD\x51\x01\x94\xE0\x0D"
+                         "\x6A\x78\x10\x7F\x1B\x0B\x1C\xBF"
+                         "\x06\xEF\xAE\x9D\x65\xA5\xD7\x63"
+                         "\x74\x8A\x63\x79\x85\x77\x1D\x34"
+                         "\x7F\x05\x45\x65\x9F\x14\xE9\x9D"
+                         "\xEF\x84\x2D\x8E\xB3\x35\xF4\xEE"
+                         "\xCF\xDB\xF8\x31\x82\x4B\x4C\x49"
+                         "\x15\x95\x6C\x96",
+               .rlen   = 68,
+       }, {
+               .key    = "\x00\x00\x00\x00\x00\x00\x00\x00"
+                         "\x00\x00\x00\x00\x00\x00\x00\x00"
+                         "\x00\x00\x00\x00",
+               .klen   = 20,
+               .iv     = "\x00\x00\x00\x00\x00\x00\x00\x00",
+               .input  = "\x45\x00\x00\x3C\x99\xC5\x00\x00"
+                         "\x80\x01\xCB\x7A\x40\x67\x93\x18"
+                         "\x01\x01\x01\x01\x08\x00\x07\x5C"
+                         "\x02\x00\x44\x00\x61\x62\x63\x64"
+                         "\x65\x66\x67\x68\x69\x6A\x6B\x6C"
+                         "\x6D\x6E\x6F\x70\x71\x72\x73\x74"
+                         "\x75\x76\x77\x61\x62\x63\x64\x65"
+                         "\x66\x67\x68\x69\x01\x02\x02\x01",
+               .ilen   = 64,
+               .assoc  = "\x00\x00\x00\x00\x00\x00\x00\x01",
+               .alen   = 8,
+               .result = "\x46\x88\xDA\xF2\xF9\x73\xA3\x92"
+                         "\x73\x29\x09\xC3\x31\xD5\x6D\x60"
+                         "\xF6\x94\xAB\xAA\x41\x4B\x5E\x7F"
+                         "\xF5\xFD\xCD\xFF\xF5\xE9\xA2\x84"
+                         "\x45\x64\x76\x49\x27\x19\xFF\xB6"
+                         "\x4D\xE7\xD9\xDC\xA1\xE1\xD8\x94"
+                         "\xBC\x3B\xD5\x78\x73\xED\x4D\x18"
+                         "\x1D\x19\xD4\xD5\xC8\xC1\x8A\xF3"
+                         "\xF8\x21\xD4\x96\xEE\xB0\x96\xE9"
+                         "\x8A\xD2\xB6\x9E\x47\x99\xC7\x1D",
+               .rlen   = 80,
+       }, {
+               .key    = "\x3D\xE0\x98\x74\xB3\x88\xE6\x49"
+                         "\x19\x88\xD0\xC3\x60\x7E\xAE\x1F"
+                         "\x57\x69\x0E\x43",
+               .klen   = 20,
+               .iv     = "\x4E\x28\x00\x00\xA2\xFC\xA1\xA3",
+               .input  = "\x45\x00\x00\x3C\x99\xC3\x00\x00"
+                         "\x80\x01\xCB\x7C\x40\x67\x93\x18"
+                         "\x01\x01\x01\x01\x08\x00\x08\x5C"
+                         "\x02\x00\x43\x00\x61\x62\x63\x64"
+                         "\x65\x66\x67\x68\x69\x6A\x6B\x6C"
+                         "\x6D\x6E\x6F\x70\x71\x72\x73\x74"
+                         "\x75\x76\x77\x61\x62\x63\x64\x65"
+                         "\x66\x67\x68\x69\x01\x02\x02\x01",
+               .ilen   = 64,
+               .assoc  = "\x42\xF6\x7E\x3F\x10\x10\x10\x10"
+                         "\x10\x10\x10\x10",
+               .alen   = 12,
+               .result = "\xFB\xA2\xCA\xA4\x85\x3C\xF9\xF0"
+                         "\xF2\x2C\xB1\x0D\x86\xDD\x83\xB0"
+                         "\xFE\xC7\x56\x91\xCF\x1A\x04\xB0"
+                         "\x0D\x11\x38\xEC\x9C\x35\x79\x17"
+                         "\x65\xAC\xBD\x87\x01\xAD\x79\x84"
+                         "\x5B\xF9\xFE\x3F\xBA\x48\x7B\xC9"
+                         "\x17\x55\xE6\x66\x2B\x4C\x8D\x0D"
+                         "\x1F\x5E\x22\x73\x95\x30\x32\x0A"
+                         "\xE0\xD7\x31\xCC\x97\x8E\xCA\xFA"
+                         "\xEA\xE8\x8F\x00\xE8\x0D\x6E\x48",
+               .rlen   = 80,
+       }, {
+               .key    = "\x3D\xE0\x98\x74\xB3\x88\xE6\x49"
+                         "\x19\x88\xD0\xC3\x60\x7E\xAE\x1F"
+                         "\x57\x69\x0E\x43",
+               .klen   = 20,
+               .iv     = "\x4E\x28\x00\x00\xA2\xFC\xA1\xA3",
+               .input  = "\x45\x00\x00\x1C\x42\xA2\x00\x00"
+                         "\x80\x01\x44\x1F\x40\x67\x93\xB6"
+                         "\xE0\x00\x00\x02\x0A\x00\xF5\xFF"
+                         "\x01\x02\x02\x01",
+               .ilen   = 28,
+               .assoc  = "\x42\xF6\x7E\x3F\x10\x10\x10\x10"
+                         "\x10\x10\x10\x10",
+               .alen   = 12,
+               .result = "\xFB\xA2\xCA\x84\x5E\x5D\xF9\xF0"
+                         "\xF2\x2C\x3E\x6E\x86\xDD\x83\x1E"
+                         "\x1F\xC6\x57\x92\xCD\x1A\xF9\x13"
+                         "\x0E\x13\x79\xED\x36\x9F\x07\x1F"
+                         "\x35\xE0\x34\xBE\x95\xF1\x12\xE4"
+                         "\xE7\xD0\x5D\x35",
+               .rlen   = 44,
+       }, {
+               .key    = "\xFE\xFF\xE9\x92\x86\x65\x73\x1C"
+                         "\x6D\x6A\x8F\x94\x67\x30\x83\x08"
+                         "\xFE\xFF\xE9\x92\x86\x65\x73\x1C"
+                         "\xCA\xFE\xBA\xBE",
+               .klen   = 28,
+               .iv     = "\xFA\xCE\xDB\xAD\xDE\xCA\xF8\x88",
+               .input  = "\x45\x00\x00\x28\xA4\xAD\x40\x00"
+                         "\x40\x06\x78\x80\x0A\x01\x03\x8F"
+                         "\x0A\x01\x06\x12\x80\x23\x06\xB8"
+                         "\xCB\x71\x26\x02\xDD\x6B\xB0\x3E"
+                         "\x50\x10\x16\xD0\x75\x68\x00\x01",
+               .ilen   = 40,
+               .assoc  = "\x00\x00\xA5\xF8\x00\x00\x00\x0A",
+               .alen   = 8,
+               .result = "\xA5\xB1\xF8\x06\x60\x29\xAE\xA4"
+                         "\x0E\x59\x8B\x81\x22\xDE\x02\x42"
+                         "\x09\x38\xB3\xAB\x33\xF8\x28\xE6"
+                         "\x87\xB8\x85\x8B\x5B\xFB\xDB\xD0"
+                         "\x31\x5B\x27\x45\x21\x44\xCC\x77"
+                         "\x95\x45\x7B\x96\x52\x03\x7F\x53"
+                         "\x18\x02\x7B\x5B\x4C\xD7\xA6\x36",
+               .rlen   = 56,
+       }, {
+               .key    = "\xAB\xBC\xCD\xDE\xF0\x01\x12\x23"
+                         "\x34\x45\x56\x67\x78\x89\x9A\xAB"
+                         "\xDE\xCA\xF8\x88",
+               .klen   = 20,
+               .iv     = "\xCA\xFE\xDE\xBA\xCE\xFA\xCE\x74",
+               .input  = "\x45\x00\x00\x49\x33\xBA\x00\x00"
+                         "\x7F\x11\x91\x06\xC3\xFB\x1D\x10"
+                         "\xC2\xB1\xD3\x26\xC0\x28\x31\xCE"
+                         "\x00\x35\xDD\x7B\x80\x03\x02\xD5"
+                         "\x00\x00\x4E\x20\x00\x1E\x8C\x18"
+                         "\xD7\x5B\x81\xDC\x91\xBA\xA0\x47"
+                         "\x6B\x91\xB9\x24\xB2\x80\x38\x9D"
+                         "\x92\xC9\x63\xBA\xC0\x46\xEC\x95"
+                         "\x9B\x62\x66\xC0\x47\x22\xB1\x49"
+                         "\x23\x01\x01\x01",
+               .ilen   = 76,
+               .assoc  = "\x00\x00\x01\x00\x00\x00\x00\x00"
+                         "\x00\x00\x00\x01",
+               .alen   = 12,
+               .result = "\x18\xA6\xFD\x42\xF7\x2C\xBF\x4A"
+                         "\xB2\xA2\xEA\x90\x1F\x73\xD8\x14"
+                         "\xE3\xE7\xF2\x43\xD9\x54\x12\xE1"
+                         "\xC3\x49\xC1\xD2\xFB\xEC\x16\x8F"
+                         "\x91\x90\xFE\xEB\xAF\x2C\xB0\x19"
+                         "\x84\xE6\x58\x63\x96\x5D\x74\x72"
+                         "\xB7\x9D\xA3\x45\xE0\xE7\x80\x19"
+                         "\x1F\x0D\x2F\x0E\x0F\x49\x6C\x22"
+                         "\x6F\x21\x27\xB2\x7D\xB3\x57\x24"
+                         "\xE7\x84\x5D\x68\x65\x1F\x57\xE6"
+                         "\x5F\x35\x4F\x75\xFF\x17\x01\x57"
+                         "\x69\x62\x34\x36",
+               .rlen   = 92,
+       }, {
+               .key    = "\xAB\xBC\xCD\xDE\xF0\x01\x12\x23"
+                         "\x34\x45\x56\x67\x78\x89\x9A\xAB"
+                         "\xAB\xBC\xCD\xDE\xF0\x01\x12\x23"
+                         "\x34\x45\x56\x67\x78\x89\x9A\xAB"
+                         "\x73\x61\x6C\x74",
+               .klen   = 36,
+               .iv     = "\x61\x6E\x64\x01\x69\x76\x65\x63",
+               .input  = "\x45\x08\x00\x28\x73\x2C\x00\x00"
+                         "\x40\x06\xE9\xF9\x0A\x01\x06\x12"
+                         "\x0A\x01\x03\x8F\x06\xB8\x80\x23"
+                         "\xDD\x6B\xAF\xBE\xCB\x71\x26\x02"
+                         "\x50\x10\x1F\x64\x6D\x54\x00\x01",
+               .ilen   = 40,
+               .assoc  = "\x17\x40\x5E\x67\x15\x6F\x31\x26"
+                         "\xDD\x0D\xB9\x9B",
+               .alen   = 12,
+               .result = "\xF2\xD6\x9E\xCD\xBD\x5A\x0D\x5B"
+                         "\x8D\x5E\xF3\x8B\xAD\x4D\xA5\x8D"
+                         "\x1F\x27\x8F\xDE\x98\xEF\x67\x54"
+                         "\x9D\x52\x4A\x30\x18\xD9\xA5\x7F"
+                         "\xF4\xD3\xA3\x1C\xE6\x73\x11\x9E"
+                         "\x45\x16\x26\xC2\x41\x57\x71\xE3"
+                         "\xB7\xEE\xBC\xA6\x14\xC8\x9B\x35",
+               .rlen   = 56,
+       }, {
+               .key    = "\x3D\xE0\x98\x74\xB3\x88\xE6\x49"
+                         "\x19\x88\xD0\xC3\x60\x7E\xAE\x1F"
+                         "\x57\x69\x0E\x43",
+               .klen   = 20,
+               .iv     = "\x4E\x28\x00\x00\xA2\xFC\xA1\xA3",
+               .input  = "\x45\x00\x00\x49\x33\x3E\x00\x00"
+                         "\x7F\x11\x91\x82\xC3\xFB\x1D\x10"
+                         "\xC2\xB1\xD3\x26\xC0\x28\x31\xCE"
+                         "\x00\x35\xCB\x45\x80\x03\x02\x5B"
+                         "\x00\x00\x01\xE0\x00\x1E\x8C\x18"
+                         "\xD6\x57\x59\xD5\x22\x84\xA0\x35"
+                         "\x2C\x71\x47\x5C\x88\x80\x39\x1C"
+                         "\x76\x4D\x6E\x5E\xE0\x49\x6B\x32"
+                         "\x5A\xE2\x70\xC0\x38\x99\x49\x39"
+                         "\x15\x01\x01\x01",
+               .ilen   = 76,
+               .assoc  = "\x42\xF6\x7E\x3F\x10\x10\x10\x10"
+                         "\x10\x10\x10\x10",
+               .alen   = 12,
+               .result = "\xFB\xA2\xCA\xD1\x2F\xC1\xF9\xF0"
+                         "\x0D\x3C\xEB\xF3\x05\x41\x0D\xB8"
+                         "\x3D\x77\x84\xB6\x07\x32\x3D\x22"
+                         "\x0F\x24\xB0\xA9\x7D\x54\x18\x28"
+                         "\x00\xCA\xDB\x0F\x68\xD9\x9E\xF0"
+                         "\xE0\xC0\xC8\x9A\xE9\xBE\xA8\x88"
+                         "\x4E\x52\xD6\x5B\xC1\xAF\xD0\x74"
+                         "\x0F\x74\x24\x44\x74\x7B\x5B\x39"
+                         "\xAB\x53\x31\x63\xAA\xD4\x55\x0E"
+                         "\xE5\x16\x09\x75\xCD\xB6\x08\xC5"
+                         "\x76\x91\x89\x60\x97\x63\xB8\xE1"
+                         "\x8C\xAA\x81\xE2",
+               .rlen   = 92,
+       }, {
+               .key    = "\xAB\xBC\xCD\xDE\xF0\x01\x12\x23"
+                         "\x34\x45\x56\x67\x78\x89\x9A\xAB"
+                         "\xAB\xBC\xCD\xDE\xF0\x01\x12\x23"
+                         "\x34\x45\x56\x67\x78\x89\x9A\xAB"
+                         "\x73\x61\x6C\x74",
+               .klen   = 36,
+               .iv     = "\x61\x6E\x64\x01\x69\x76\x65\x63",
+               .input  = "\x63\x69\x73\x63\x6F\x01\x72\x75"
+                         "\x6C\x65\x73\x01\x74\x68\x65\x01"
+                         "\x6E\x65\x74\x77\x65\x01\x64\x65"
+                         "\x66\x69\x6E\x65\x01\x74\x68\x65"
+                         "\x74\x65\x63\x68\x6E\x6F\x6C\x6F"
+                         "\x67\x69\x65\x73\x01\x74\x68\x61"
+                         "\x74\x77\x69\x6C\x6C\x01\x64\x65"
+                         "\x66\x69\x6E\x65\x74\x6F\x6D\x6F"
+                         "\x72\x72\x6F\x77\x01\x02\x02\x01",
+               .ilen   = 72,
+               .assoc  = "\x17\x40\x5E\x67\x15\x6F\x31\x26"
+                         "\xDD\x0D\xB9\x9B",
+               .alen   = 12,
+               .result = "\xD4\xB7\xED\x86\xA1\x77\x7F\x2E"
+                         "\xA1\x3D\x69\x73\xD3\x24\xC6\x9E"
+                         "\x7B\x43\xF8\x26\xFB\x56\x83\x12"
+                         "\x26\x50\x8B\xEB\xD2\xDC\xEB\x18"
+                         "\xD0\xA6\xDF\x10\xE5\x48\x7D\xF0"
+                         "\x74\x11\x3E\x14\xC6\x41\x02\x4E"
+                         "\x3E\x67\x73\xD9\x1A\x62\xEE\x42"
+                         "\x9B\x04\x3A\x10\xE3\xEF\xE6\xB0"
+                         "\x12\xA4\x93\x63\x41\x23\x64\xF8"
+                         "\xC0\xCA\xC5\x87\xF2\x49\xE5\x6B"
+                         "\x11\xE2\x4F\x30\xE4\x4C\xCC\x76",
+               .rlen   = 88,
+       }, {
+               .key    = "\x7D\x77\x3D\x00\xC1\x44\xC5\x25"
+                         "\xAC\x61\x9D\x18\xC8\x4A\x3F\x47"
+                         "\xD9\x66\x42\x67",
+               .klen   = 20,
+               .iv     = "\x43\x45\x7E\x91\x82\x44\x3B\xC6",
+               .input  = "\x01\x02\x02\x01",
+               .ilen   = 4,
+               .assoc  = "\x33\x54\x67\xAE\xFF\xFF\xFF\xFF",
+               .alen   = 8,
+               .result = "\x43\x7F\x86\x6B\xCB\x3F\x69\x9F"
+                         "\xE9\xB0\x82\x2B\xAC\x96\x1C\x45"
+                         "\x04\xBE\xF2\x70",
+               .rlen   = 20,
+       }, {
+               .key    = "\xAB\xBC\xCD\xDE\xF0\x01\x12\x23"
+                         "\x34\x45\x56\x67\x78\x89\x9A\xAB"
+                         "\xDE\xCA\xF8\x88",
+               .klen   = 20,
+               .iv     = "\xCA\xFE\xDE\xBA\xCE\xFA\xCE\x74",
+               .input  = "\x74\x6F\x01\x62\x65\x01\x6F\x72"
+                         "\x01\x6E\x6F\x74\x01\x74\x6F\x01"
+                         "\x62\x65\x00\x01",
+               .ilen   = 20,
+               .assoc  = "\x00\x00\x01\x00\x00\x00\x00\x00"
+                         "\x00\x00\x00\x01",
+               .alen   = 12,
+               .result = "\x29\xC9\xFC\x69\xA1\x97\xD0\x38"
+                         "\xCC\xDD\x14\xE2\xDD\xFC\xAA\x05"
+                         "\x43\x33\x21\x64\x41\x25\x03\x52"
+                         "\x43\x03\xED\x3C\x6C\x5F\x28\x38"
+                         "\x43\xAF\x8C\x3E",
+               .rlen   = 36,
+       }, {
+               .key    = "\x6C\x65\x67\x61\x6C\x69\x7A\x65"
+                         "\x6D\x61\x72\x69\x6A\x75\x61\x6E"
+                         "\x61\x61\x6E\x64\x64\x6F\x69\x74"
+                         "\x62\x65\x66\x6F\x72\x65\x69\x61"
+                         "\x74\x75\x72\x6E",
+               .klen   = 36,
+               .iv     = "\x33\x30\x21\x69\x67\x65\x74\x6D",
+               .input  = "\x45\x00\x00\x30\xDA\x3A\x00\x00"
+                         "\x80\x01\xDF\x3B\xC0\xA8\x00\x05"
+                         "\xC0\xA8\x00\x01\x08\x00\xC6\xCD"
+                         "\x02\x00\x07\x00\x61\x62\x63\x64"
+                         "\x65\x66\x67\x68\x69\x6A\x6B\x6C"
+                         "\x6D\x6E\x6F\x70\x71\x72\x73\x74"
+                         "\x01\x02\x02\x01",
+               .ilen   = 52,
+               .assoc  = "\x79\x6B\x69\x63\xFF\xFF\xFF\xFF"
+                         "\xFF\xFF\xFF\xFF",
+               .alen   = 12,
+               .result = "\xF9\x7A\xB2\xAA\x35\x6D\x8E\xDC"
+                         "\xE1\x76\x44\xAC\x8C\x78\xE2\x5D"
+                         "\xD2\x4D\xED\xBB\x29\xEB\xF1\xB6"
+                         "\x4A\x27\x4B\x39\xB4\x9C\x3A\x86"
+                         "\x4C\xD3\xD7\x8C\xA4\xAE\x68\xA3"
+                         "\x2B\x42\x45\x8F\xB5\x7D\xBE\x82"
+                         "\x1D\xCC\x63\xB9\xD0\x93\x7B\xA2"
+                         "\x94\x5F\x66\x93\x68\x66\x1A\x32"
+                         "\x9F\xB4\xC0\x53",
+               .rlen   = 68,
+       }, {
+               .key    = "\x3D\xE0\x98\x74\xB3\x88\xE6\x49"
+                         "\x19\x88\xD0\xC3\x60\x7E\xAE\x1F"
+                         "\x57\x69\x0E\x43",
+               .klen   = 20,
+               .iv     = "\x4E\x28\x00\x00\xA2\xFC\xA1\xA3",
+               .input  = "\x45\x00\x00\x30\xDA\x3A\x00\x00"
+                         "\x80\x01\xDF\x3B\xC0\xA8\x00\x05"
+                         "\xC0\xA8\x00\x01\x08\x00\xC6\xCD"
+                         "\x02\x00\x07\x00\x61\x62\x63\x64"
+                         "\x65\x66\x67\x68\x69\x6A\x6B\x6C"
+                         "\x6D\x6E\x6F\x70\x71\x72\x73\x74"
+                         "\x01\x02\x02\x01",
+               .ilen   = 52,
+               .assoc  = "\x3F\x7E\xF6\x42\x10\x10\x10\x10"
+                         "\x10\x10\x10\x10",
+               .alen   = 12,
+               .result = "\xFB\xA2\xCA\xA8\xC6\xC5\xF9\xF0"
+                         "\xF2\x2C\xA5\x4A\x06\x12\x10\xAD"
+                         "\x3F\x6E\x57\x91\xCF\x1A\xCA\x21"
+                         "\x0D\x11\x7C\xEC\x9C\x35\x79\x17"
+                         "\x65\xAC\xBD\x87\x01\xAD\x79\x84"
+                         "\x5B\xF9\xFE\x3F\xBA\x48\x7B\xC9"
+                         "\x63\x21\x93\x06\x84\xEE\xCA\xDB"
+                         "\x56\x91\x25\x46\xE7\xA9\x5C\x97"
+                         "\x40\xD7\xCB\x05",
+               .rlen   = 68,
+       }, {
+               .key    = "\x4C\x80\xCD\xEF\xBB\x5D\x10\xDA"
+                         "\x90\x6A\xC7\x3C\x36\x13\xA6\x34"
+                         "\x22\x43\x3C\x64",
+               .klen   = 20,
+               .iv     = "\x48\x55\xEC\x7D\x3A\x23\x4B\xFD",
+               .input  = "\x08\x00\xC6\xCD\x02\x00\x07\x00"
+                         "\x61\x62\x63\x64\x65\x66\x67\x68"
+                         "\x69\x6A\x6B\x6C\x6D\x6E\x6F\x70"
+                         "\x71\x72\x73\x74\x01\x02\x02\x01",
+               .ilen   = 32,
+               .assoc  = "\x00\x00\x43\x21\x87\x65\x43\x21"
+                         "\x00\x00\x00\x07",
+               .alen   = 12,
+               .result = "\x74\x75\x2E\x8A\xEB\x5D\x87\x3C"
+                         "\xD7\xC0\xF4\xAC\xC3\x6C\x4B\xFF"
+                         "\x84\xB7\xD7\xB9\x8F\x0C\xA8\xB6"
+                         "\xAC\xDA\x68\x94\xBC\x61\x90\x69"
+                         "\xEF\x9C\xBC\x28\xFE\x1B\x56\xA7"
+                         "\xC4\xE0\xD5\x8C\x86\xCD\x2B\xC0",
+               .rlen   = 48,
        }
 };
 
@@ -19964,7 +20896,428 @@ static struct aead_testvec aes_gcm_rfc4106_dec_tv_template[] = {
                           "\xff\xff\xff\xff\xff\xff\xff\xff"
                           "\xff\xff\xff\xff\xff\xff\xff\xff",
                 .rlen   = 192,
-
+       }, {
+               .key    = "\x4C\x80\xCD\xEF\xBB\x5D\x10\xDA"
+                         "\x90\x6A\xC7\x3C\x36\x13\xA6\x34"
+                         "\x2E\x44\x3B\x68",
+               .klen   = 20,
+               .iv     = "\x49\x56\xED\x7E\x3B\x24\x4C\xFE",
+               .result = "\x45\x00\x00\x48\x69\x9A\x00\x00"
+                         "\x80\x11\x4D\xB7\xC0\xA8\x01\x02"
+                         "\xC0\xA8\x01\x01\x0A\x9B\xF1\x56"
+                         "\x38\xD3\x01\x00\x00\x01\x00\x00"
+                         "\x00\x00\x00\x00\x04\x5F\x73\x69"
+                         "\x70\x04\x5F\x75\x64\x70\x03\x73"
+                         "\x69\x70\x09\x63\x79\x62\x65\x72"
+                         "\x63\x69\x74\x79\x02\x64\x6B\x00"
+                         "\x00\x21\x00\x01\x01\x02\x02\x01",
+               .rlen   = 72,
+               .assoc  = "\x00\x00\x43\x21\x87\x65\x43\x21"
+                         "\x00\x00\x00\x00",
+               .alen   = 12,
+               .input  = "\xFE\xCF\x53\x7E\x72\x9D\x5B\x07"
+                         "\xDC\x30\xDF\x52\x8D\xD2\x2B\x76"
+                         "\x8D\x1B\x98\x73\x66\x96\xA6\xFD"
+                         "\x34\x85\x09\xFA\x13\xCE\xAC\x34"
+                         "\xCF\xA2\x43\x6F\x14\xA3\xF3\xCF"
+                         "\x65\x92\x5B\xF1\xF4\xA1\x3C\x5D"
+                         "\x15\xB2\x1E\x18\x84\xF5\xFF\x62"
+                         "\x47\xAE\xAB\xB7\x86\xB9\x3B\xCE"
+                         "\x61\xBC\x17\xD7\x68\xFD\x97\x32"
+                         "\x45\x90\x18\x14\x8F\x6C\xBE\x72"
+                         "\x2F\xD0\x47\x96\x56\x2D\xFD\xB4",
+               .ilen   = 88,
+       }, {
+               .key    = "\xFE\xFF\xE9\x92\x86\x65\x73\x1C"
+                         "\x6D\x6A\x8F\x94\x67\x30\x83\x08"
+                         "\xCA\xFE\xBA\xBE",
+               .klen   = 20,
+               .iv     = "\xFA\xCE\xDB\xAD\xDE\xCA\xF8\x88",
+               .result = "\x45\x00\x00\x3E\x69\x8F\x00\x00"
+                         "\x80\x11\x4D\xCC\xC0\xA8\x01\x02"
+                         "\xC0\xA8\x01\x01\x0A\x98\x00\x35"
+                         "\x00\x2A\x23\x43\xB2\xD0\x01\x00"
+                         "\x00\x01\x00\x00\x00\x00\x00\x00"
+                         "\x03\x73\x69\x70\x09\x63\x79\x62"
+                         "\x65\x72\x63\x69\x74\x79\x02\x64"
+                         "\x6B\x00\x00\x01\x00\x01\x00\x01",
+               .rlen   = 64,
+               .assoc  = "\x00\x00\xA5\xF8\x00\x00\x00\x0A",
+               .alen   = 8,
+               .input  = "\xDE\xB2\x2C\xD9\xB0\x7C\x72\xC1"
+                         "\x6E\x3A\x65\xBE\xEB\x8D\xF3\x04"
+                         "\xA5\xA5\x89\x7D\x33\xAE\x53\x0F"
+                         "\x1B\xA7\x6D\x5D\x11\x4D\x2A\x5C"
+                         "\x3D\xE8\x18\x27\xC1\x0E\x9A\x4F"
+                         "\x51\x33\x0D\x0E\xEC\x41\x66\x42"
+                         "\xCF\xBB\x85\xA5\xB4\x7E\x48\xA4"
+                         "\xEC\x3B\x9B\xA9\x5D\x91\x8B\xD1"
+                         "\x83\xB7\x0D\x3A\xA8\xBC\x6E\xE4"
+                         "\xC3\x09\xE9\xD8\x5A\x41\xAD\x4A",
+               .ilen   = 80,
+       }, {
+               .key    = "\xAB\xBC\xCD\xDE\xF0\x01\x12\x23"
+                         "\x34\x45\x56\x67\x78\x89\x9A\xAB"
+                         "\xAB\xBC\xCD\xDE\xF0\x01\x12\x23"
+                         "\x34\x45\x56\x67\x78\x89\x9A\xAB"
+                         "\x11\x22\x33\x44",
+               .klen   = 36,
+               .iv     = "\x01\x02\x03\x04\x05\x06\x07\x08",
+               .result = "\x45\x00\x00\x30\x69\xA6\x40\x00"
+                         "\x80\x06\x26\x90\xC0\xA8\x01\x02"
+                         "\x93\x89\x15\x5E\x0A\x9E\x00\x8B"
+                         "\x2D\xC5\x7E\xE0\x00\x00\x00\x00"
+                         "\x70\x02\x40\x00\x20\xBF\x00\x00"
+                         "\x02\x04\x05\xB4\x01\x01\x04\x02"
+                         "\x01\x02\x02\x01",
+               .rlen   = 52,
+               .assoc  = "\x4A\x2C\xBF\xE3\x00\x00\x00\x02",
+               .alen   = 8,
+               .input  = "\xFF\x42\x5C\x9B\x72\x45\x99\xDF"
+                         "\x7A\x3B\xCD\x51\x01\x94\xE0\x0D"
+                         "\x6A\x78\x10\x7F\x1B\x0B\x1C\xBF"
+                         "\x06\xEF\xAE\x9D\x65\xA5\xD7\x63"
+                         "\x74\x8A\x63\x79\x85\x77\x1D\x34"
+                         "\x7F\x05\x45\x65\x9F\x14\xE9\x9D"
+                         "\xEF\x84\x2D\x8E\xB3\x35\xF4\xEE"
+                         "\xCF\xDB\xF8\x31\x82\x4B\x4C\x49"
+                         "\x15\x95\x6C\x96",
+               .ilen   = 68,
+       }, {
+               .key    = "\x00\x00\x00\x00\x00\x00\x00\x00"
+                         "\x00\x00\x00\x00\x00\x00\x00\x00"
+                         "\x00\x00\x00\x00",
+               .klen   = 20,
+               .iv     = "\x00\x00\x00\x00\x00\x00\x00\x00",
+               .result = "\x45\x00\x00\x3C\x99\xC5\x00\x00"
+                         "\x80\x01\xCB\x7A\x40\x67\x93\x18"
+                         "\x01\x01\x01\x01\x08\x00\x07\x5C"
+                         "\x02\x00\x44\x00\x61\x62\x63\x64"
+                         "\x65\x66\x67\x68\x69\x6A\x6B\x6C"
+                         "\x6D\x6E\x6F\x70\x71\x72\x73\x74"
+                         "\x75\x76\x77\x61\x62\x63\x64\x65"
+                         "\x66\x67\x68\x69\x01\x02\x02\x01",
+               .rlen   = 64,
+               .assoc  = "\x00\x00\x00\x00\x00\x00\x00\x01",
+               .alen   = 8,
+               .input  = "\x46\x88\xDA\xF2\xF9\x73\xA3\x92"
+                         "\x73\x29\x09\xC3\x31\xD5\x6D\x60"
+                         "\xF6\x94\xAB\xAA\x41\x4B\x5E\x7F"
+                         "\xF5\xFD\xCD\xFF\xF5\xE9\xA2\x84"
+                         "\x45\x64\x76\x49\x27\x19\xFF\xB6"
+                         "\x4D\xE7\xD9\xDC\xA1\xE1\xD8\x94"
+                         "\xBC\x3B\xD5\x78\x73\xED\x4D\x18"
+                         "\x1D\x19\xD4\xD5\xC8\xC1\x8A\xF3"
+                         "\xF8\x21\xD4\x96\xEE\xB0\x96\xE9"
+                         "\x8A\xD2\xB6\x9E\x47\x99\xC7\x1D",
+               .ilen   = 80,
+       }, {
+               .key    = "\x3D\xE0\x98\x74\xB3\x88\xE6\x49"
+                         "\x19\x88\xD0\xC3\x60\x7E\xAE\x1F"
+                         "\x57\x69\x0E\x43",
+               .klen   = 20,
+               .iv     = "\x4E\x28\x00\x00\xA2\xFC\xA1\xA3",
+               .result = "\x45\x00\x00\x3C\x99\xC3\x00\x00"
+                         "\x80\x01\xCB\x7C\x40\x67\x93\x18"
+                         "\x01\x01\x01\x01\x08\x00\x08\x5C"
+                         "\x02\x00\x43\x00\x61\x62\x63\x64"
+                         "\x65\x66\x67\x68\x69\x6A\x6B\x6C"
+                         "\x6D\x6E\x6F\x70\x71\x72\x73\x74"
+                         "\x75\x76\x77\x61\x62\x63\x64\x65"
+                         "\x66\x67\x68\x69\x01\x02\x02\x01",
+               .rlen   = 64,
+               .assoc  = "\x42\xF6\x7E\x3F\x10\x10\x10\x10"
+                         "\x10\x10\x10\x10",
+               .alen   = 12,
+               .input  = "\xFB\xA2\xCA\xA4\x85\x3C\xF9\xF0"
+                         "\xF2\x2C\xB1\x0D\x86\xDD\x83\xB0"
+                         "\xFE\xC7\x56\x91\xCF\x1A\x04\xB0"
+                         "\x0D\x11\x38\xEC\x9C\x35\x79\x17"
+                         "\x65\xAC\xBD\x87\x01\xAD\x79\x84"
+                         "\x5B\xF9\xFE\x3F\xBA\x48\x7B\xC9"
+                         "\x17\x55\xE6\x66\x2B\x4C\x8D\x0D"
+                         "\x1F\x5E\x22\x73\x95\x30\x32\x0A"
+                         "\xE0\xD7\x31\xCC\x97\x8E\xCA\xFA"
+                         "\xEA\xE8\x8F\x00\xE8\x0D\x6E\x48",
+               .ilen   = 80,
+       }, {
+               .key    = "\x3D\xE0\x98\x74\xB3\x88\xE6\x49"
+                         "\x19\x88\xD0\xC3\x60\x7E\xAE\x1F"
+                         "\x57\x69\x0E\x43",
+               .klen   = 20,
+               .iv     = "\x4E\x28\x00\x00\xA2\xFC\xA1\xA3",
+               .result = "\x45\x00\x00\x1C\x42\xA2\x00\x00"
+                         "\x80\x01\x44\x1F\x40\x67\x93\xB6"
+                         "\xE0\x00\x00\x02\x0A\x00\xF5\xFF"
+                         "\x01\x02\x02\x01",
+               .rlen   = 28,
+               .assoc  = "\x42\xF6\x7E\x3F\x10\x10\x10\x10"
+                         "\x10\x10\x10\x10",
+               .alen   = 12,
+               .input  = "\xFB\xA2\xCA\x84\x5E\x5D\xF9\xF0"
+                         "\xF2\x2C\x3E\x6E\x86\xDD\x83\x1E"
+                         "\x1F\xC6\x57\x92\xCD\x1A\xF9\x13"
+                         "\x0E\x13\x79\xED\x36\x9F\x07\x1F"
+                         "\x35\xE0\x34\xBE\x95\xF1\x12\xE4"
+                         "\xE7\xD0\x5D\x35",
+               .ilen   = 44,
+       }, {
+               .key    = "\xFE\xFF\xE9\x92\x86\x65\x73\x1C"
+                         "\x6D\x6A\x8F\x94\x67\x30\x83\x08"
+                         "\xFE\xFF\xE9\x92\x86\x65\x73\x1C"
+                         "\xCA\xFE\xBA\xBE",
+               .klen   = 28,
+               .iv     = "\xFA\xCE\xDB\xAD\xDE\xCA\xF8\x88",
+               .result = "\x45\x00\x00\x28\xA4\xAD\x40\x00"
+                         "\x40\x06\x78\x80\x0A\x01\x03\x8F"
+                         "\x0A\x01\x06\x12\x80\x23\x06\xB8"
+                         "\xCB\x71\x26\x02\xDD\x6B\xB0\x3E"
+                         "\x50\x10\x16\xD0\x75\x68\x00\x01",
+               .rlen   = 40,
+               .assoc  = "\x00\x00\xA5\xF8\x00\x00\x00\x0A",
+               .alen   = 8,
+               .input  = "\xA5\xB1\xF8\x06\x60\x29\xAE\xA4"
+                         "\x0E\x59\x8B\x81\x22\xDE\x02\x42"
+                         "\x09\x38\xB3\xAB\x33\xF8\x28\xE6"
+                         "\x87\xB8\x85\x8B\x5B\xFB\xDB\xD0"
+                         "\x31\x5B\x27\x45\x21\x44\xCC\x77"
+                         "\x95\x45\x7B\x96\x52\x03\x7F\x53"
+                         "\x18\x02\x7B\x5B\x4C\xD7\xA6\x36",
+               .ilen   = 56,
+       }, {
+               .key    = "\xAB\xBC\xCD\xDE\xF0\x01\x12\x23"
+                         "\x34\x45\x56\x67\x78\x89\x9A\xAB"
+                         "\xDE\xCA\xF8\x88",
+               .klen   = 20,
+               .iv     = "\xCA\xFE\xDE\xBA\xCE\xFA\xCE\x74",
+               .result = "\x45\x00\x00\x49\x33\xBA\x00\x00"
+                         "\x7F\x11\x91\x06\xC3\xFB\x1D\x10"
+                         "\xC2\xB1\xD3\x26\xC0\x28\x31\xCE"
+                         "\x00\x35\xDD\x7B\x80\x03\x02\xD5"
+                         "\x00\x00\x4E\x20\x00\x1E\x8C\x18"
+                         "\xD7\x5B\x81\xDC\x91\xBA\xA0\x47"
+                         "\x6B\x91\xB9\x24\xB2\x80\x38\x9D"
+                         "\x92\xC9\x63\xBA\xC0\x46\xEC\x95"
+                         "\x9B\x62\x66\xC0\x47\x22\xB1\x49"
+                         "\x23\x01\x01\x01",
+               .rlen   = 76,
+               .assoc  = "\x00\x00\x01\x00\x00\x00\x00\x00"
+                         "\x00\x00\x00\x01",
+               .alen   = 12,
+               .input  = "\x18\xA6\xFD\x42\xF7\x2C\xBF\x4A"
+                         "\xB2\xA2\xEA\x90\x1F\x73\xD8\x14"
+                         "\xE3\xE7\xF2\x43\xD9\x54\x12\xE1"
+                         "\xC3\x49\xC1\xD2\xFB\xEC\x16\x8F"
+                         "\x91\x90\xFE\xEB\xAF\x2C\xB0\x19"
+                         "\x84\xE6\x58\x63\x96\x5D\x74\x72"
+                         "\xB7\x9D\xA3\x45\xE0\xE7\x80\x19"
+                         "\x1F\x0D\x2F\x0E\x0F\x49\x6C\x22"
+                         "\x6F\x21\x27\xB2\x7D\xB3\x57\x24"
+                         "\xE7\x84\x5D\x68\x65\x1F\x57\xE6"
+                         "\x5F\x35\x4F\x75\xFF\x17\x01\x57"
+                         "\x69\x62\x34\x36",
+               .ilen   = 92,
+       }, {
+               .key    = "\xAB\xBC\xCD\xDE\xF0\x01\x12\x23"
+                         "\x34\x45\x56\x67\x78\x89\x9A\xAB"
+                         "\xAB\xBC\xCD\xDE\xF0\x01\x12\x23"
+                         "\x34\x45\x56\x67\x78\x89\x9A\xAB"
+                         "\x73\x61\x6C\x74",
+               .klen   = 36,
+               .iv     = "\x61\x6E\x64\x01\x69\x76\x65\x63",
+               .result = "\x45\x08\x00\x28\x73\x2C\x00\x00"
+                         "\x40\x06\xE9\xF9\x0A\x01\x06\x12"
+                         "\x0A\x01\x03\x8F\x06\xB8\x80\x23"
+                         "\xDD\x6B\xAF\xBE\xCB\x71\x26\x02"
+                         "\x50\x10\x1F\x64\x6D\x54\x00\x01",
+               .rlen   = 40,
+               .assoc  = "\x17\x40\x5E\x67\x15\x6F\x31\x26"
+                         "\xDD\x0D\xB9\x9B",
+               .alen   = 12,
+               .input  = "\xF2\xD6\x9E\xCD\xBD\x5A\x0D\x5B"
+                         "\x8D\x5E\xF3\x8B\xAD\x4D\xA5\x8D"
+                         "\x1F\x27\x8F\xDE\x98\xEF\x67\x54"
+                         "\x9D\x52\x4A\x30\x18\xD9\xA5\x7F"
+                         "\xF4\xD3\xA3\x1C\xE6\x73\x11\x9E"
+                         "\x45\x16\x26\xC2\x41\x57\x71\xE3"
+                         "\xB7\xEE\xBC\xA6\x14\xC8\x9B\x35",
+               .ilen   = 56,
+       }, {
+               .key    = "\x3D\xE0\x98\x74\xB3\x88\xE6\x49"
+                         "\x19\x88\xD0\xC3\x60\x7E\xAE\x1F"
+                         "\x57\x69\x0E\x43",
+               .klen   = 20,
+               .iv     = "\x4E\x28\x00\x00\xA2\xFC\xA1\xA3",
+               .result = "\x45\x00\x00\x49\x33\x3E\x00\x00"
+                         "\x7F\x11\x91\x82\xC3\xFB\x1D\x10"
+                         "\xC2\xB1\xD3\x26\xC0\x28\x31\xCE"
+                         "\x00\x35\xCB\x45\x80\x03\x02\x5B"
+                         "\x00\x00\x01\xE0\x00\x1E\x8C\x18"
+                         "\xD6\x57\x59\xD5\x22\x84\xA0\x35"
+                         "\x2C\x71\x47\x5C\x88\x80\x39\x1C"
+                         "\x76\x4D\x6E\x5E\xE0\x49\x6B\x32"
+                         "\x5A\xE2\x70\xC0\x38\x99\x49\x39"
+                         "\x15\x01\x01\x01",
+               .rlen   = 76,
+               .assoc  = "\x42\xF6\x7E\x3F\x10\x10\x10\x10"
+                         "\x10\x10\x10\x10",
+               .alen   = 12,
+               .input  = "\xFB\xA2\xCA\xD1\x2F\xC1\xF9\xF0"
+                         "\x0D\x3C\xEB\xF3\x05\x41\x0D\xB8"
+                         "\x3D\x77\x84\xB6\x07\x32\x3D\x22"
+                         "\x0F\x24\xB0\xA9\x7D\x54\x18\x28"
+                         "\x00\xCA\xDB\x0F\x68\xD9\x9E\xF0"
+                         "\xE0\xC0\xC8\x9A\xE9\xBE\xA8\x88"
+                         "\x4E\x52\xD6\x5B\xC1\xAF\xD0\x74"
+                         "\x0F\x74\x24\x44\x74\x7B\x5B\x39"
+                         "\xAB\x53\x31\x63\xAA\xD4\x55\x0E"
+                         "\xE5\x16\x09\x75\xCD\xB6\x08\xC5"
+                         "\x76\x91\x89\x60\x97\x63\xB8\xE1"
+                         "\x8C\xAA\x81\xE2",
+               .ilen   = 92,
+       }, {
+               .key    = "\xAB\xBC\xCD\xDE\xF0\x01\x12\x23"
+                         "\x34\x45\x56\x67\x78\x89\x9A\xAB"
+                         "\xAB\xBC\xCD\xDE\xF0\x01\x12\x23"
+                         "\x34\x45\x56\x67\x78\x89\x9A\xAB"
+                         "\x73\x61\x6C\x74",
+               .klen   = 36,
+               .iv     = "\x61\x6E\x64\x01\x69\x76\x65\x63",
+               .result = "\x63\x69\x73\x63\x6F\x01\x72\x75"
+                         "\x6C\x65\x73\x01\x74\x68\x65\x01"
+                         "\x6E\x65\x74\x77\x65\x01\x64\x65"
+                         "\x66\x69\x6E\x65\x01\x74\x68\x65"
+                         "\x74\x65\x63\x68\x6E\x6F\x6C\x6F"
+                         "\x67\x69\x65\x73\x01\x74\x68\x61"
+                         "\x74\x77\x69\x6C\x6C\x01\x64\x65"
+                         "\x66\x69\x6E\x65\x74\x6F\x6D\x6F"
+                         "\x72\x72\x6F\x77\x01\x02\x02\x01",
+               .rlen   = 72,
+               .assoc  = "\x17\x40\x5E\x67\x15\x6F\x31\x26"
+                         "\xDD\x0D\xB9\x9B",
+               .alen   = 12,
+               .input  = "\xD4\xB7\xED\x86\xA1\x77\x7F\x2E"
+                         "\xA1\x3D\x69\x73\xD3\x24\xC6\x9E"
+                         "\x7B\x43\xF8\x26\xFB\x56\x83\x12"
+                         "\x26\x50\x8B\xEB\xD2\xDC\xEB\x18"
+                         "\xD0\xA6\xDF\x10\xE5\x48\x7D\xF0"
+                         "\x74\x11\x3E\x14\xC6\x41\x02\x4E"
+                         "\x3E\x67\x73\xD9\x1A\x62\xEE\x42"
+                         "\x9B\x04\x3A\x10\xE3\xEF\xE6\xB0"
+                         "\x12\xA4\x93\x63\x41\x23\x64\xF8"
+                         "\xC0\xCA\xC5\x87\xF2\x49\xE5\x6B"
+                         "\x11\xE2\x4F\x30\xE4\x4C\xCC\x76",
+               .ilen   = 88,
+       }, {
+               .key    = "\x7D\x77\x3D\x00\xC1\x44\xC5\x25"
+                         "\xAC\x61\x9D\x18\xC8\x4A\x3F\x47"
+                         "\xD9\x66\x42\x67",
+               .klen   = 20,
+               .iv     = "\x43\x45\x7E\x91\x82\x44\x3B\xC6",
+               .result = "\x01\x02\x02\x01",
+               .rlen   = 4,
+               .assoc  = "\x33\x54\x67\xAE\xFF\xFF\xFF\xFF",
+               .alen   = 8,
+               .input  = "\x43\x7F\x86\x6B\xCB\x3F\x69\x9F"
+                         "\xE9\xB0\x82\x2B\xAC\x96\x1C\x45"
+                         "\x04\xBE\xF2\x70",
+               .ilen   = 20,
+       }, {
+               .key    = "\xAB\xBC\xCD\xDE\xF0\x01\x12\x23"
+                         "\x34\x45\x56\x67\x78\x89\x9A\xAB"
+                         "\xDE\xCA\xF8\x88",
+               .klen   = 20,
+               .iv     = "\xCA\xFE\xDE\xBA\xCE\xFA\xCE\x74",
+               .result = "\x74\x6F\x01\x62\x65\x01\x6F\x72"
+                         "\x01\x6E\x6F\x74\x01\x74\x6F\x01"
+                         "\x62\x65\x00\x01",
+               .rlen   = 20,
+               .assoc  = "\x00\x00\x01\x00\x00\x00\x00\x00"
+                         "\x00\x00\x00\x01",
+               .alen   = 12,
+               .input  = "\x29\xC9\xFC\x69\xA1\x97\xD0\x38"
+                         "\xCC\xDD\x14\xE2\xDD\xFC\xAA\x05"
+                         "\x43\x33\x21\x64\x41\x25\x03\x52"
+                         "\x43\x03\xED\x3C\x6C\x5F\x28\x38"
+                         "\x43\xAF\x8C\x3E",
+               .ilen   = 36,
+       }, {
+               .key    = "\x6C\x65\x67\x61\x6C\x69\x7A\x65"
+                         "\x6D\x61\x72\x69\x6A\x75\x61\x6E"
+                         "\x61\x61\x6E\x64\x64\x6F\x69\x74"
+                         "\x62\x65\x66\x6F\x72\x65\x69\x61"
+                         "\x74\x75\x72\x6E",
+               .klen   = 36,
+               .iv     = "\x33\x30\x21\x69\x67\x65\x74\x6D",
+               .result = "\x45\x00\x00\x30\xDA\x3A\x00\x00"
+                         "\x80\x01\xDF\x3B\xC0\xA8\x00\x05"
+                         "\xC0\xA8\x00\x01\x08\x00\xC6\xCD"
+                         "\x02\x00\x07\x00\x61\x62\x63\x64"
+                         "\x65\x66\x67\x68\x69\x6A\x6B\x6C"
+                         "\x6D\x6E\x6F\x70\x71\x72\x73\x74"
+                         "\x01\x02\x02\x01",
+               .rlen   = 52,
+               .assoc  = "\x79\x6B\x69\x63\xFF\xFF\xFF\xFF"
+                         "\xFF\xFF\xFF\xFF",
+               .alen   = 12,
+               .input  = "\xF9\x7A\xB2\xAA\x35\x6D\x8E\xDC"
+                         "\xE1\x76\x44\xAC\x8C\x78\xE2\x5D"
+                         "\xD2\x4D\xED\xBB\x29\xEB\xF1\xB6"
+                         "\x4A\x27\x4B\x39\xB4\x9C\x3A\x86"
+                         "\x4C\xD3\xD7\x8C\xA4\xAE\x68\xA3"
+                         "\x2B\x42\x45\x8F\xB5\x7D\xBE\x82"
+                         "\x1D\xCC\x63\xB9\xD0\x93\x7B\xA2"
+                         "\x94\x5F\x66\x93\x68\x66\x1A\x32"
+                         "\x9F\xB4\xC0\x53",
+               .ilen   = 68,
+       }, {
+               .key    = "\x3D\xE0\x98\x74\xB3\x88\xE6\x49"
+                         "\x19\x88\xD0\xC3\x60\x7E\xAE\x1F"
+                         "\x57\x69\x0E\x43",
+               .klen   = 20,
+               .iv     = "\x4E\x28\x00\x00\xA2\xFC\xA1\xA3",
+               .result = "\x45\x00\x00\x30\xDA\x3A\x00\x00"
+                         "\x80\x01\xDF\x3B\xC0\xA8\x00\x05"
+                         "\xC0\xA8\x00\x01\x08\x00\xC6\xCD"
+                         "\x02\x00\x07\x00\x61\x62\x63\x64"
+                         "\x65\x66\x67\x68\x69\x6A\x6B\x6C"
+                         "\x6D\x6E\x6F\x70\x71\x72\x73\x74"
+                         "\x01\x02\x02\x01",
+               .rlen   = 52,
+               .assoc  = "\x3F\x7E\xF6\x42\x10\x10\x10\x10"
+                         "\x10\x10\x10\x10",
+               .alen   = 12,
+               .input  = "\xFB\xA2\xCA\xA8\xC6\xC5\xF9\xF0"
+                         "\xF2\x2C\xA5\x4A\x06\x12\x10\xAD"
+                         "\x3F\x6E\x57\x91\xCF\x1A\xCA\x21"
+                         "\x0D\x11\x7C\xEC\x9C\x35\x79\x17"
+                         "\x65\xAC\xBD\x87\x01\xAD\x79\x84"
+                         "\x5B\xF9\xFE\x3F\xBA\x48\x7B\xC9"
+                         "\x63\x21\x93\x06\x84\xEE\xCA\xDB"
+                         "\x56\x91\x25\x46\xE7\xA9\x5C\x97"
+                         "\x40\xD7\xCB\x05",
+               .ilen   = 68,
+       }, {
+               .key    = "\x4C\x80\xCD\xEF\xBB\x5D\x10\xDA"
+                         "\x90\x6A\xC7\x3C\x36\x13\xA6\x34"
+                         "\x22\x43\x3C\x64",
+               .klen   = 20,
+               .iv     = "\x48\x55\xEC\x7D\x3A\x23\x4B\xFD",
+               .result = "\x08\x00\xC6\xCD\x02\x00\x07\x00"
+                         "\x61\x62\x63\x64\x65\x66\x67\x68"
+                         "\x69\x6A\x6B\x6C\x6D\x6E\x6F\x70"
+                         "\x71\x72\x73\x74\x01\x02\x02\x01",
+               .rlen   = 32,
+               .assoc  = "\x00\x00\x43\x21\x87\x65\x43\x21"
+                         "\x00\x00\x00\x07",
+               .alen   = 12,
+               .input  = "\x74\x75\x2E\x8A\xEB\x5D\x87\x3C"
+                         "\xD7\xC0\xF4\xAC\xC3\x6C\x4B\xFF"
+                         "\x84\xB7\xD7\xB9\x8F\x0C\xA8\xB6"
+                         "\xAC\xDA\x68\x94\xBC\x61\x90\x69"
+                         "\xEF\x9C\xBC\x28\xFE\x1B\x56\xA7"
+                         "\xC4\xE0\xD5\x8C\x86\xCD\x2B\xC0",
+               .ilen   = 48,
        }
 };
 
@@ -19975,8 +21328,9 @@ static struct aead_testvec aes_gcm_rfc4543_enc_tv_template[] = {
                          "\x22\x43\x3c\x64",
                .klen   = 20,
                .iv     = zeroed_string,
-               .assoc  = "\x00\x00\x43\x21\x00\x00\x00\x07",
-               .alen   = 8,
+               .assoc  = "\x00\x00\x43\x21\x00\x00\x00\x07"
+                         "\x00\x00\x00\x00\x00\x00\x00\x00",
+               .alen   = 16,
                .input  = "\x45\x00\x00\x30\xda\x3a\x00\x00"
                          "\x80\x01\xdf\x3b\xc0\xa8\x00\x05"
                          "\xc0\xa8\x00\x01\x08\x00\xc6\xcd"
@@ -20005,8 +21359,9 @@ static struct aead_testvec aes_gcm_rfc4543_dec_tv_template[] = {
                          "\x22\x43\x3c\x64",
                .klen   = 20,
                .iv     = zeroed_string,
-               .assoc  = "\x00\x00\x43\x21\x00\x00\x00\x07",
-               .alen   = 8,
+               .assoc  = "\x00\x00\x43\x21\x00\x00\x00\x07"
+                         "\x00\x00\x00\x00\x00\x00\x00\x00",
+               .alen   = 16,
                .input  = "\x45\x00\x00\x30\xda\x3a\x00\x00"
                          "\x80\x01\xdf\x3b\xc0\xa8\x00\x05"
                          "\xc0\xa8\x00\x01\x08\x00\xc6\xcd"
@@ -20031,8 +21386,9 @@ static struct aead_testvec aes_gcm_rfc4543_dec_tv_template[] = {
                          "\x22\x43\x3c\x64",
                .klen   = 20,
                .iv     = zeroed_string,
-               .assoc  = "\x00\x00\x43\x21\x00\x00\x00\x07",
-               .alen   = 8,
+               .assoc  = "\x00\x00\x43\x21\x00\x00\x00\x07"
+                         "\x00\x00\x00\x00\x00\x00\x00\x00",
+               .alen   = 16,
                .input  = "\x45\x00\x00\x30\xda\x3a\x00\x00"
                          "\x80\x01\xdf\x3b\xc0\xa8\x00\x05"
                          "\xc0\xa8\x00\x01\x08\x00\xc6\xcd"
@@ -20703,6 +22059,454 @@ static struct aead_testvec aes_ccm_rfc4309_dec_tv_template[] = {
        },
 };
 
+/*
+ * ChaCha20-Poly1305 AEAD test vectors from RFC7539 2.8.2./A.5.
+ */
+#define RFC7539_ENC_TEST_VECTORS 2
+#define RFC7539_DEC_TEST_VECTORS 2
+static struct aead_testvec rfc7539_enc_tv_template[] = {
+       {
+               .key    = "\x80\x81\x82\x83\x84\x85\x86\x87"
+                         "\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f"
+                         "\x90\x91\x92\x93\x94\x95\x96\x97"
+                         "\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f",
+               .klen   = 32,
+               .iv     = "\x07\x00\x00\x00\x40\x41\x42\x43"
+                         "\x44\x45\x46\x47",
+               .assoc  = "\x50\x51\x52\x53\xc0\xc1\xc2\xc3"
+                         "\xc4\xc5\xc6\xc7",
+               .alen   = 12,
+               .input  = "\x4c\x61\x64\x69\x65\x73\x20\x61"
+                         "\x6e\x64\x20\x47\x65\x6e\x74\x6c"
+                         "\x65\x6d\x65\x6e\x20\x6f\x66\x20"
+                         "\x74\x68\x65\x20\x63\x6c\x61\x73"
+                         "\x73\x20\x6f\x66\x20\x27\x39\x39"
+                         "\x3a\x20\x49\x66\x20\x49\x20\x63"
+                         "\x6f\x75\x6c\x64\x20\x6f\x66\x66"
+                         "\x65\x72\x20\x79\x6f\x75\x20\x6f"
+                         "\x6e\x6c\x79\x20\x6f\x6e\x65\x20"
+                         "\x74\x69\x70\x20\x66\x6f\x72\x20"
+                         "\x74\x68\x65\x20\x66\x75\x74\x75"
+                         "\x72\x65\x2c\x20\x73\x75\x6e\x73"
+                         "\x63\x72\x65\x65\x6e\x20\x77\x6f"
+                         "\x75\x6c\x64\x20\x62\x65\x20\x69"
+                         "\x74\x2e",
+               .ilen   = 114,
+               .result = "\xd3\x1a\x8d\x34\x64\x8e\x60\xdb"
+                         "\x7b\x86\xaf\xbc\x53\xef\x7e\xc2"
+                         "\xa4\xad\xed\x51\x29\x6e\x08\xfe"
+                         "\xa9\xe2\xb5\xa7\x36\xee\x62\xd6"
+                         "\x3d\xbe\xa4\x5e\x8c\xa9\x67\x12"
+                         "\x82\xfa\xfb\x69\xda\x92\x72\x8b"
+                         "\x1a\x71\xde\x0a\x9e\x06\x0b\x29"
+                         "\x05\xd6\xa5\xb6\x7e\xcd\x3b\x36"
+                         "\x92\xdd\xbd\x7f\x2d\x77\x8b\x8c"
+                         "\x98\x03\xae\xe3\x28\x09\x1b\x58"
+                         "\xfa\xb3\x24\xe4\xfa\xd6\x75\x94"
+                         "\x55\x85\x80\x8b\x48\x31\xd7\xbc"
+                         "\x3f\xf4\xde\xf0\x8e\x4b\x7a\x9d"
+                         "\xe5\x76\xd2\x65\x86\xce\xc6\x4b"
+                         "\x61\x16\x1a\xe1\x0b\x59\x4f\x09"
+                         "\xe2\x6a\x7e\x90\x2e\xcb\xd0\x60"
+                         "\x06\x91",
+               .rlen   = 130,
+       }, {
+               .key    = "\x1c\x92\x40\xa5\xeb\x55\xd3\x8a"
+                         "\xf3\x33\x88\x86\x04\xf6\xb5\xf0"
+                         "\x47\x39\x17\xc1\x40\x2b\x80\x09"
+                         "\x9d\xca\x5c\xbc\x20\x70\x75\xc0",
+               .klen   = 32,
+               .iv     = "\x00\x00\x00\x00\x01\x02\x03\x04"
+                         "\x05\x06\x07\x08",
+               .assoc  = "\xf3\x33\x88\x86\x00\x00\x00\x00"
+                         "\x00\x00\x4e\x91",
+               .alen   = 12,
+               .input  = "\x49\x6e\x74\x65\x72\x6e\x65\x74"
+                         "\x2d\x44\x72\x61\x66\x74\x73\x20"
+                         "\x61\x72\x65\x20\x64\x72\x61\x66"
+                         "\x74\x20\x64\x6f\x63\x75\x6d\x65"
+                         "\x6e\x74\x73\x20\x76\x61\x6c\x69"
+                         "\x64\x20\x66\x6f\x72\x20\x61\x20"
+                         "\x6d\x61\x78\x69\x6d\x75\x6d\x20"
+                         "\x6f\x66\x20\x73\x69\x78\x20\x6d"
+                         "\x6f\x6e\x74\x68\x73\x20\x61\x6e"
+                         "\x64\x20\x6d\x61\x79\x20\x62\x65"
+                         "\x20\x75\x70\x64\x61\x74\x65\x64"
+                         "\x2c\x20\x72\x65\x70\x6c\x61\x63"
+                         "\x65\x64\x2c\x20\x6f\x72\x20\x6f"
+                         "\x62\x73\x6f\x6c\x65\x74\x65\x64"
+                         "\x20\x62\x79\x20\x6f\x74\x68\x65"
+                         "\x72\x20\x64\x6f\x63\x75\x6d\x65"
+                         "\x6e\x74\x73\x20\x61\x74\x20\x61"
+                         "\x6e\x79\x20\x74\x69\x6d\x65\x2e"
+                         "\x20\x49\x74\x20\x69\x73\x20\x69"
+                         "\x6e\x61\x70\x70\x72\x6f\x70\x72"
+                         "\x69\x61\x74\x65\x20\x74\x6f\x20"
+                         "\x75\x73\x65\x20\x49\x6e\x74\x65"
+                         "\x72\x6e\x65\x74\x2d\x44\x72\x61"
+                         "\x66\x74\x73\x20\x61\x73\x20\x72"
+                         "\x65\x66\x65\x72\x65\x6e\x63\x65"
+                         "\x20\x6d\x61\x74\x65\x72\x69\x61"
+                         "\x6c\x20\x6f\x72\x20\x74\x6f\x20"
+                         "\x63\x69\x74\x65\x20\x74\x68\x65"
+                         "\x6d\x20\x6f\x74\x68\x65\x72\x20"
+                         "\x74\x68\x61\x6e\x20\x61\x73\x20"
+                         "\x2f\xe2\x80\x9c\x77\x6f\x72\x6b"
+                         "\x20\x69\x6e\x20\x70\x72\x6f\x67"
+                         "\x72\x65\x73\x73\x2e\x2f\xe2\x80"
+                         "\x9d",
+               .ilen   = 265,
+               .result = "\x64\xa0\x86\x15\x75\x86\x1a\xf4"
+                         "\x60\xf0\x62\xc7\x9b\xe6\x43\xbd"
+                         "\x5e\x80\x5c\xfd\x34\x5c\xf3\x89"
+                         "\xf1\x08\x67\x0a\xc7\x6c\x8c\xb2"
+                         "\x4c\x6c\xfc\x18\x75\x5d\x43\xee"
+                         "\xa0\x9e\xe9\x4e\x38\x2d\x26\xb0"
+                         "\xbd\xb7\xb7\x3c\x32\x1b\x01\x00"
+                         "\xd4\xf0\x3b\x7f\x35\x58\x94\xcf"
+                         "\x33\x2f\x83\x0e\x71\x0b\x97\xce"
+                         "\x98\xc8\xa8\x4a\xbd\x0b\x94\x81"
+                         "\x14\xad\x17\x6e\x00\x8d\x33\xbd"
+                         "\x60\xf9\x82\xb1\xff\x37\xc8\x55"
+                         "\x97\x97\xa0\x6e\xf4\xf0\xef\x61"
+                         "\xc1\x86\x32\x4e\x2b\x35\x06\x38"
+                         "\x36\x06\x90\x7b\x6a\x7c\x02\xb0"
+                         "\xf9\xf6\x15\x7b\x53\xc8\x67\xe4"
+                         "\xb9\x16\x6c\x76\x7b\x80\x4d\x46"
+                         "\xa5\x9b\x52\x16\xcd\xe7\xa4\xe9"
+                         "\x90\x40\xc5\xa4\x04\x33\x22\x5e"
+                         "\xe2\x82\xa1\xb0\xa0\x6c\x52\x3e"
+                         "\xaf\x45\x34\xd7\xf8\x3f\xa1\x15"
+                         "\x5b\x00\x47\x71\x8c\xbc\x54\x6a"
+                         "\x0d\x07\x2b\x04\xb3\x56\x4e\xea"
+                         "\x1b\x42\x22\x73\xf5\x48\x27\x1a"
+                         "\x0b\xb2\x31\x60\x53\xfa\x76\x99"
+                         "\x19\x55\xeb\xd6\x31\x59\x43\x4e"
+                         "\xce\xbb\x4e\x46\x6d\xae\x5a\x10"
+                         "\x73\xa6\x72\x76\x27\x09\x7a\x10"
+                         "\x49\xe6\x17\xd9\x1d\x36\x10\x94"
+                         "\xfa\x68\xf0\xff\x77\x98\x71\x30"
+                         "\x30\x5b\xea\xba\x2e\xda\x04\xdf"
+                         "\x99\x7b\x71\x4d\x6c\x6f\x2c\x29"
+                         "\xa6\xad\x5c\xb4\x02\x2b\x02\x70"
+                         "\x9b\xee\xad\x9d\x67\x89\x0c\xbb"
+                         "\x22\x39\x23\x36\xfe\xa1\x85\x1f"
+                         "\x38",
+               .rlen   = 281,
+       },
+};
+
+static struct aead_testvec rfc7539_dec_tv_template[] = {
+       {
+               .key    = "\x80\x81\x82\x83\x84\x85\x86\x87"
+                         "\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f"
+                         "\x90\x91\x92\x93\x94\x95\x96\x97"
+                         "\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f",
+               .klen   = 32,
+               .iv     = "\x07\x00\x00\x00\x40\x41\x42\x43"
+                         "\x44\x45\x46\x47",
+               .assoc  = "\x50\x51\x52\x53\xc0\xc1\xc2\xc3"
+                         "\xc4\xc5\xc6\xc7",
+               .alen   = 12,
+               .input  = "\xd3\x1a\x8d\x34\x64\x8e\x60\xdb"
+                         "\x7b\x86\xaf\xbc\x53\xef\x7e\xc2"
+                         "\xa4\xad\xed\x51\x29\x6e\x08\xfe"
+                         "\xa9\xe2\xb5\xa7\x36\xee\x62\xd6"
+                         "\x3d\xbe\xa4\x5e\x8c\xa9\x67\x12"
+                         "\x82\xfa\xfb\x69\xda\x92\x72\x8b"
+                         "\x1a\x71\xde\x0a\x9e\x06\x0b\x29"
+                         "\x05\xd6\xa5\xb6\x7e\xcd\x3b\x36"
+                         "\x92\xdd\xbd\x7f\x2d\x77\x8b\x8c"
+                         "\x98\x03\xae\xe3\x28\x09\x1b\x58"
+                         "\xfa\xb3\x24\xe4\xfa\xd6\x75\x94"
+                         "\x55\x85\x80\x8b\x48\x31\xd7\xbc"
+                         "\x3f\xf4\xde\xf0\x8e\x4b\x7a\x9d"
+                         "\xe5\x76\xd2\x65\x86\xce\xc6\x4b"
+                         "\x61\x16\x1a\xe1\x0b\x59\x4f\x09"
+                         "\xe2\x6a\x7e\x90\x2e\xcb\xd0\x60"
+                         "\x06\x91",
+               .ilen   = 130,
+               .result = "\x4c\x61\x64\x69\x65\x73\x20\x61"
+                         "\x6e\x64\x20\x47\x65\x6e\x74\x6c"
+                         "\x65\x6d\x65\x6e\x20\x6f\x66\x20"
+                         "\x74\x68\x65\x20\x63\x6c\x61\x73"
+                         "\x73\x20\x6f\x66\x20\x27\x39\x39"
+                         "\x3a\x20\x49\x66\x20\x49\x20\x63"
+                         "\x6f\x75\x6c\x64\x20\x6f\x66\x66"
+                         "\x65\x72\x20\x79\x6f\x75\x20\x6f"
+                         "\x6e\x6c\x79\x20\x6f\x6e\x65\x20"
+                         "\x74\x69\x70\x20\x66\x6f\x72\x20"
+                         "\x74\x68\x65\x20\x66\x75\x74\x75"
+                         "\x72\x65\x2c\x20\x73\x75\x6e\x73"
+                         "\x63\x72\x65\x65\x6e\x20\x77\x6f"
+                         "\x75\x6c\x64\x20\x62\x65\x20\x69"
+                         "\x74\x2e",
+               .rlen   = 114,
+       }, {
+               .key    = "\x1c\x92\x40\xa5\xeb\x55\xd3\x8a"
+                         "\xf3\x33\x88\x86\x04\xf6\xb5\xf0"
+                         "\x47\x39\x17\xc1\x40\x2b\x80\x09"
+                         "\x9d\xca\x5c\xbc\x20\x70\x75\xc0",
+               .klen   = 32,
+               .iv     = "\x00\x00\x00\x00\x01\x02\x03\x04"
+                         "\x05\x06\x07\x08",
+               .assoc  = "\xf3\x33\x88\x86\x00\x00\x00\x00"
+                         "\x00\x00\x4e\x91",
+               .alen   = 12,
+               .input  = "\x64\xa0\x86\x15\x75\x86\x1a\xf4"
+                         "\x60\xf0\x62\xc7\x9b\xe6\x43\xbd"
+                         "\x5e\x80\x5c\xfd\x34\x5c\xf3\x89"
+                         "\xf1\x08\x67\x0a\xc7\x6c\x8c\xb2"
+                         "\x4c\x6c\xfc\x18\x75\x5d\x43\xee"
+                         "\xa0\x9e\xe9\x4e\x38\x2d\x26\xb0"
+                         "\xbd\xb7\xb7\x3c\x32\x1b\x01\x00"
+                         "\xd4\xf0\x3b\x7f\x35\x58\x94\xcf"
+                         "\x33\x2f\x83\x0e\x71\x0b\x97\xce"
+                         "\x98\xc8\xa8\x4a\xbd\x0b\x94\x81"
+                         "\x14\xad\x17\x6e\x00\x8d\x33\xbd"
+                         "\x60\xf9\x82\xb1\xff\x37\xc8\x55"
+                         "\x97\x97\xa0\x6e\xf4\xf0\xef\x61"
+                         "\xc1\x86\x32\x4e\x2b\x35\x06\x38"
+                         "\x36\x06\x90\x7b\x6a\x7c\x02\xb0"
+                         "\xf9\xf6\x15\x7b\x53\xc8\x67\xe4"
+                         "\xb9\x16\x6c\x76\x7b\x80\x4d\x46"
+                         "\xa5\x9b\x52\x16\xcd\xe7\xa4\xe9"
+                         "\x90\x40\xc5\xa4\x04\x33\x22\x5e"
+                         "\xe2\x82\xa1\xb0\xa0\x6c\x52\x3e"
+                         "\xaf\x45\x34\xd7\xf8\x3f\xa1\x15"
+                         "\x5b\x00\x47\x71\x8c\xbc\x54\x6a"
+                         "\x0d\x07\x2b\x04\xb3\x56\x4e\xea"
+                         "\x1b\x42\x22\x73\xf5\x48\x27\x1a"
+                         "\x0b\xb2\x31\x60\x53\xfa\x76\x99"
+                         "\x19\x55\xeb\xd6\x31\x59\x43\x4e"
+                         "\xce\xbb\x4e\x46\x6d\xae\x5a\x10"
+                         "\x73\xa6\x72\x76\x27\x09\x7a\x10"
+                         "\x49\xe6\x17\xd9\x1d\x36\x10\x94"
+                         "\xfa\x68\xf0\xff\x77\x98\x71\x30"
+                         "\x30\x5b\xea\xba\x2e\xda\x04\xdf"
+                         "\x99\x7b\x71\x4d\x6c\x6f\x2c\x29"
+                         "\xa6\xad\x5c\xb4\x02\x2b\x02\x70"
+                         "\x9b\xee\xad\x9d\x67\x89\x0c\xbb"
+                         "\x22\x39\x23\x36\xfe\xa1\x85\x1f"
+                         "\x38",
+               .ilen   = 281,
+               .result = "\x49\x6e\x74\x65\x72\x6e\x65\x74"
+                         "\x2d\x44\x72\x61\x66\x74\x73\x20"
+                         "\x61\x72\x65\x20\x64\x72\x61\x66"
+                         "\x74\x20\x64\x6f\x63\x75\x6d\x65"
+                         "\x6e\x74\x73\x20\x76\x61\x6c\x69"
+                         "\x64\x20\x66\x6f\x72\x20\x61\x20"
+                         "\x6d\x61\x78\x69\x6d\x75\x6d\x20"
+                         "\x6f\x66\x20\x73\x69\x78\x20\x6d"
+                         "\x6f\x6e\x74\x68\x73\x20\x61\x6e"
+                         "\x64\x20\x6d\x61\x79\x20\x62\x65"
+                         "\x20\x75\x70\x64\x61\x74\x65\x64"
+                         "\x2c\x20\x72\x65\x70\x6c\x61\x63"
+                         "\x65\x64\x2c\x20\x6f\x72\x20\x6f"
+                         "\x62\x73\x6f\x6c\x65\x74\x65\x64"
+                         "\x20\x62\x79\x20\x6f\x74\x68\x65"
+                         "\x72\x20\x64\x6f\x63\x75\x6d\x65"
+                         "\x6e\x74\x73\x20\x61\x74\x20\x61"
+                         "\x6e\x79\x20\x74\x69\x6d\x65\x2e"
+                         "\x20\x49\x74\x20\x69\x73\x20\x69"
+                         "\x6e\x61\x70\x70\x72\x6f\x70\x72"
+                         "\x69\x61\x74\x65\x20\x74\x6f\x20"
+                         "\x75\x73\x65\x20\x49\x6e\x74\x65"
+                         "\x72\x6e\x65\x74\x2d\x44\x72\x61"
+                         "\x66\x74\x73\x20\x61\x73\x20\x72"
+                         "\x65\x66\x65\x72\x65\x6e\x63\x65"
+                         "\x20\x6d\x61\x74\x65\x72\x69\x61"
+                         "\x6c\x20\x6f\x72\x20\x74\x6f\x20"
+                         "\x63\x69\x74\x65\x20\x74\x68\x65"
+                         "\x6d\x20\x6f\x74\x68\x65\x72\x20"
+                         "\x74\x68\x61\x6e\x20\x61\x73\x20"
+                         "\x2f\xe2\x80\x9c\x77\x6f\x72\x6b"
+                         "\x20\x69\x6e\x20\x70\x72\x6f\x67"
+                         "\x72\x65\x73\x73\x2e\x2f\xe2\x80"
+                         "\x9d",
+               .rlen   = 265,
+       },
+};
+
+/*
+ * draft-irtf-cfrg-chacha20-poly1305
+ */
+#define RFC7539ESP_DEC_TEST_VECTORS 1
+#define RFC7539ESP_ENC_TEST_VECTORS 1
+static struct aead_testvec rfc7539esp_enc_tv_template[] = {
+       {
+               .key    = "\x1c\x92\x40\xa5\xeb\x55\xd3\x8a"
+                         "\xf3\x33\x88\x86\x04\xf6\xb5\xf0"
+                         "\x47\x39\x17\xc1\x40\x2b\x80\x09"
+                         "\x9d\xca\x5c\xbc\x20\x70\x75\xc0"
+                         "\x00\x00\x00\x00",
+               .klen   = 36,
+               .iv     = "\x01\x02\x03\x04\x05\x06\x07\x08",
+               .assoc  = "\xf3\x33\x88\x86\x00\x00\x00\x00"
+                         "\x00\x00\x4e\x91",
+               .alen   = 12,
+               .input  = "\x49\x6e\x74\x65\x72\x6e\x65\x74"
+                         "\x2d\x44\x72\x61\x66\x74\x73\x20"
+                         "\x61\x72\x65\x20\x64\x72\x61\x66"
+                         "\x74\x20\x64\x6f\x63\x75\x6d\x65"
+                         "\x6e\x74\x73\x20\x76\x61\x6c\x69"
+                         "\x64\x20\x66\x6f\x72\x20\x61\x20"
+                         "\x6d\x61\x78\x69\x6d\x75\x6d\x20"
+                         "\x6f\x66\x20\x73\x69\x78\x20\x6d"
+                         "\x6f\x6e\x74\x68\x73\x20\x61\x6e"
+                         "\x64\x20\x6d\x61\x79\x20\x62\x65"
+                         "\x20\x75\x70\x64\x61\x74\x65\x64"
+                         "\x2c\x20\x72\x65\x70\x6c\x61\x63"
+                         "\x65\x64\x2c\x20\x6f\x72\x20\x6f"
+                         "\x62\x73\x6f\x6c\x65\x74\x65\x64"
+                         "\x20\x62\x79\x20\x6f\x74\x68\x65"
+                         "\x72\x20\x64\x6f\x63\x75\x6d\x65"
+                         "\x6e\x74\x73\x20\x61\x74\x20\x61"
+                         "\x6e\x79\x20\x74\x69\x6d\x65\x2e"
+                         "\x20\x49\x74\x20\x69\x73\x20\x69"
+                         "\x6e\x61\x70\x70\x72\x6f\x70\x72"
+                         "\x69\x61\x74\x65\x20\x74\x6f\x20"
+                         "\x75\x73\x65\x20\x49\x6e\x74\x65"
+                         "\x72\x6e\x65\x74\x2d\x44\x72\x61"
+                         "\x66\x74\x73\x20\x61\x73\x20\x72"
+                         "\x65\x66\x65\x72\x65\x6e\x63\x65"
+                         "\x20\x6d\x61\x74\x65\x72\x69\x61"
+                         "\x6c\x20\x6f\x72\x20\x74\x6f\x20"
+                         "\x63\x69\x74\x65\x20\x74\x68\x65"
+                         "\x6d\x20\x6f\x74\x68\x65\x72\x20"
+                         "\x74\x68\x61\x6e\x20\x61\x73\x20"
+                         "\x2f\xe2\x80\x9c\x77\x6f\x72\x6b"
+                         "\x20\x69\x6e\x20\x70\x72\x6f\x67"
+                         "\x72\x65\x73\x73\x2e\x2f\xe2\x80"
+                         "\x9d",
+               .ilen   = 265,
+               .result = "\x64\xa0\x86\x15\x75\x86\x1a\xf4"
+                         "\x60\xf0\x62\xc7\x9b\xe6\x43\xbd"
+                         "\x5e\x80\x5c\xfd\x34\x5c\xf3\x89"
+                         "\xf1\x08\x67\x0a\xc7\x6c\x8c\xb2"
+                         "\x4c\x6c\xfc\x18\x75\x5d\x43\xee"
+                         "\xa0\x9e\xe9\x4e\x38\x2d\x26\xb0"
+                         "\xbd\xb7\xb7\x3c\x32\x1b\x01\x00"
+                         "\xd4\xf0\x3b\x7f\x35\x58\x94\xcf"
+                         "\x33\x2f\x83\x0e\x71\x0b\x97\xce"
+                         "\x98\xc8\xa8\x4a\xbd\x0b\x94\x81"
+                         "\x14\xad\x17\x6e\x00\x8d\x33\xbd"
+                         "\x60\xf9\x82\xb1\xff\x37\xc8\x55"
+                         "\x97\x97\xa0\x6e\xf4\xf0\xef\x61"
+                         "\xc1\x86\x32\x4e\x2b\x35\x06\x38"
+                         "\x36\x06\x90\x7b\x6a\x7c\x02\xb0"
+                         "\xf9\xf6\x15\x7b\x53\xc8\x67\xe4"
+                         "\xb9\x16\x6c\x76\x7b\x80\x4d\x46"
+                         "\xa5\x9b\x52\x16\xcd\xe7\xa4\xe9"
+                         "\x90\x40\xc5\xa4\x04\x33\x22\x5e"
+                         "\xe2\x82\xa1\xb0\xa0\x6c\x52\x3e"
+                         "\xaf\x45\x34\xd7\xf8\x3f\xa1\x15"
+                         "\x5b\x00\x47\x71\x8c\xbc\x54\x6a"
+                         "\x0d\x07\x2b\x04\xb3\x56\x4e\xea"
+                         "\x1b\x42\x22\x73\xf5\x48\x27\x1a"
+                         "\x0b\xb2\x31\x60\x53\xfa\x76\x99"
+                         "\x19\x55\xeb\xd6\x31\x59\x43\x4e"
+                         "\xce\xbb\x4e\x46\x6d\xae\x5a\x10"
+                         "\x73\xa6\x72\x76\x27\x09\x7a\x10"
+                         "\x49\xe6\x17\xd9\x1d\x36\x10\x94"
+                         "\xfa\x68\xf0\xff\x77\x98\x71\x30"
+                         "\x30\x5b\xea\xba\x2e\xda\x04\xdf"
+                         "\x99\x7b\x71\x4d\x6c\x6f\x2c\x29"
+                         "\xa6\xad\x5c\xb4\x02\x2b\x02\x70"
+                         "\x9b\xee\xad\x9d\x67\x89\x0c\xbb"
+                         "\x22\x39\x23\x36\xfe\xa1\x85\x1f"
+                         "\x38",
+               .rlen   = 281,
+       },
+};
+
+static struct aead_testvec rfc7539esp_dec_tv_template[] = {
+       {
+               .key    = "\x1c\x92\x40\xa5\xeb\x55\xd3\x8a"
+                         "\xf3\x33\x88\x86\x04\xf6\xb5\xf0"
+                         "\x47\x39\x17\xc1\x40\x2b\x80\x09"
+                         "\x9d\xca\x5c\xbc\x20\x70\x75\xc0"
+                         "\x00\x00\x00\x00",
+               .klen   = 36,
+               .iv     = "\x01\x02\x03\x04\x05\x06\x07\x08",
+               .assoc  = "\xf3\x33\x88\x86\x00\x00\x00\x00"
+                         "\x00\x00\x4e\x91",
+               .alen   = 12,
+               .input  = "\x64\xa0\x86\x15\x75\x86\x1a\xf4"
+                         "\x60\xf0\x62\xc7\x9b\xe6\x43\xbd"
+                         "\x5e\x80\x5c\xfd\x34\x5c\xf3\x89"
+                         "\xf1\x08\x67\x0a\xc7\x6c\x8c\xb2"
+                         "\x4c\x6c\xfc\x18\x75\x5d\x43\xee"
+                         "\xa0\x9e\xe9\x4e\x38\x2d\x26\xb0"
+                         "\xbd\xb7\xb7\x3c\x32\x1b\x01\x00"
+                         "\xd4\xf0\x3b\x7f\x35\x58\x94\xcf"
+                         "\x33\x2f\x83\x0e\x71\x0b\x97\xce"
+                         "\x98\xc8\xa8\x4a\xbd\x0b\x94\x81"
+                         "\x14\xad\x17\x6e\x00\x8d\x33\xbd"
+                         "\x60\xf9\x82\xb1\xff\x37\xc8\x55"
+                         "\x97\x97\xa0\x6e\xf4\xf0\xef\x61"
+                         "\xc1\x86\x32\x4e\x2b\x35\x06\x38"
+                         "\x36\x06\x90\x7b\x6a\x7c\x02\xb0"
+                         "\xf9\xf6\x15\x7b\x53\xc8\x67\xe4"
+                         "\xb9\x16\x6c\x76\x7b\x80\x4d\x46"
+                         "\xa5\x9b\x52\x16\xcd\xe7\xa4\xe9"
+                         "\x90\x40\xc5\xa4\x04\x33\x22\x5e"
+                         "\xe2\x82\xa1\xb0\xa0\x6c\x52\x3e"
+                         "\xaf\x45\x34\xd7\xf8\x3f\xa1\x15"
+                         "\x5b\x00\x47\x71\x8c\xbc\x54\x6a"
+                         "\x0d\x07\x2b\x04\xb3\x56\x4e\xea"
+                         "\x1b\x42\x22\x73\xf5\x48\x27\x1a"
+                         "\x0b\xb2\x31\x60\x53\xfa\x76\x99"
+                         "\x19\x55\xeb\xd6\x31\x59\x43\x4e"
+                         "\xce\xbb\x4e\x46\x6d\xae\x5a\x10"
+                         "\x73\xa6\x72\x76\x27\x09\x7a\x10"
+                         "\x49\xe6\x17\xd9\x1d\x36\x10\x94"
+                         "\xfa\x68\xf0\xff\x77\x98\x71\x30"
+                         "\x30\x5b\xea\xba\x2e\xda\x04\xdf"
+                         "\x99\x7b\x71\x4d\x6c\x6f\x2c\x29"
+                         "\xa6\xad\x5c\xb4\x02\x2b\x02\x70"
+                         "\x9b\xee\xad\x9d\x67\x89\x0c\xbb"
+                         "\x22\x39\x23\x36\xfe\xa1\x85\x1f"
+                         "\x38",
+               .ilen   = 281,
+               .result = "\x49\x6e\x74\x65\x72\x6e\x65\x74"
+                         "\x2d\x44\x72\x61\x66\x74\x73\x20"
+                         "\x61\x72\x65\x20\x64\x72\x61\x66"
+                         "\x74\x20\x64\x6f\x63\x75\x6d\x65"
+                         "\x6e\x74\x73\x20\x76\x61\x6c\x69"
+                         "\x64\x20\x66\x6f\x72\x20\x61\x20"
+                         "\x6d\x61\x78\x69\x6d\x75\x6d\x20"
+                         "\x6f\x66\x20\x73\x69\x78\x20\x6d"
+                         "\x6f\x6e\x74\x68\x73\x20\x61\x6e"
+                         "\x64\x20\x6d\x61\x79\x20\x62\x65"
+                         "\x20\x75\x70\x64\x61\x74\x65\x64"
+                         "\x2c\x20\x72\x65\x70\x6c\x61\x63"
+                         "\x65\x64\x2c\x20\x6f\x72\x20\x6f"
+                         "\x62\x73\x6f\x6c\x65\x74\x65\x64"
+                         "\x20\x62\x79\x20\x6f\x74\x68\x65"
+                         "\x72\x20\x64\x6f\x63\x75\x6d\x65"
+                         "\x6e\x74\x73\x20\x61\x74\x20\x61"
+                         "\x6e\x79\x20\x74\x69\x6d\x65\x2e"
+                         "\x20\x49\x74\x20\x69\x73\x20\x69"
+                         "\x6e\x61\x70\x70\x72\x6f\x70\x72"
+                         "\x69\x61\x74\x65\x20\x74\x6f\x20"
+                         "\x75\x73\x65\x20\x49\x6e\x74\x65"
+                         "\x72\x6e\x65\x74\x2d\x44\x72\x61"
+                         "\x66\x74\x73\x20\x61\x73\x20\x72"
+                         "\x65\x66\x65\x72\x65\x6e\x63\x65"
+                         "\x20\x6d\x61\x74\x65\x72\x69\x61"
+                         "\x6c\x20\x6f\x72\x20\x74\x6f\x20"
+                         "\x63\x69\x74\x65\x20\x74\x68\x65"
+                         "\x6d\x20\x6f\x74\x68\x65\x72\x20"
+                         "\x74\x68\x61\x6e\x20\x61\x73\x20"
+                         "\x2f\xe2\x80\x9c\x77\x6f\x72\x6b"
+                         "\x20\x69\x6e\x20\x70\x72\x6f\x67"
+                         "\x72\x65\x73\x73\x2e\x2f\xe2\x80"
+                         "\x9d",
+               .rlen   = 265,
+       },
+};
+
 /*
  * ANSI X9.31 Continuous Pseudo-Random Number Generator (AES mode)
  * test vectors, taken from Appendix B.2.9 and B.2.10:
@@ -28370,6 +30174,183 @@ static struct cipher_testvec salsa20_stream_enc_tv_template[] = {
        },
 };
 
+#define CHACHA20_ENC_TEST_VECTORS 3
+static struct cipher_testvec chacha20_enc_tv_template[] = {
+       { /* RFC7539 A.2. Test Vector #1 */
+               .key    = "\x00\x00\x00\x00\x00\x00\x00\x00"
+                         "\x00\x00\x00\x00\x00\x00\x00\x00"
+                         "\x00\x00\x00\x00\x00\x00\x00\x00"
+                         "\x00\x00\x00\x00\x00\x00\x00\x00",
+               .klen   = 32,
+               .iv     = "\x00\x00\x00\x00\x00\x00\x00\x00"
+                         "\x00\x00\x00\x00\x00\x00\x00\x00",
+               .input  = "\x00\x00\x00\x00\x00\x00\x00\x00"
+                         "\x00\x00\x00\x00\x00\x00\x00\x00"
+                         "\x00\x00\x00\x00\x00\x00\x00\x00"
+                         "\x00\x00\x00\x00\x00\x00\x00\x00"
+                         "\x00\x00\x00\x00\x00\x00\x00\x00"
+                         "\x00\x00\x00\x00\x00\x00\x00\x00"
+                         "\x00\x00\x00\x00\x00\x00\x00\x00"
+                         "\x00\x00\x00\x00\x00\x00\x00\x00",
+               .ilen   = 64,
+               .result = "\x76\xb8\xe0\xad\xa0\xf1\x3d\x90"
+                         "\x40\x5d\x6a\xe5\x53\x86\xbd\x28"
+                         "\xbd\xd2\x19\xb8\xa0\x8d\xed\x1a"
+                         "\xa8\x36\xef\xcc\x8b\x77\x0d\xc7"
+                         "\xda\x41\x59\x7c\x51\x57\x48\x8d"
+                         "\x77\x24\xe0\x3f\xb8\xd8\x4a\x37"
+                         "\x6a\x43\xb8\xf4\x15\x18\xa1\x1c"
+                         "\xc3\x87\xb6\x69\xb2\xee\x65\x86",
+               .rlen   = 64,
+       }, { /* RFC7539 A.2. Test Vector #2 */
+               .key    = "\x00\x00\x00\x00\x00\x00\x00\x00"
+                         "\x00\x00\x00\x00\x00\x00\x00\x00"
+                         "\x00\x00\x00\x00\x00\x00\x00\x00"
+                         "\x00\x00\x00\x00\x00\x00\x00\x01",
+               .klen   = 32,
+               .iv     = "\x01\x00\x00\x00\x00\x00\x00\x00"
+                         "\x00\x00\x00\x00\x00\x00\x00\x02",
+               .input  = "\x41\x6e\x79\x20\x73\x75\x62\x6d"
+                         "\x69\x73\x73\x69\x6f\x6e\x20\x74"
+                         "\x6f\x20\x74\x68\x65\x20\x49\x45"
+                         "\x54\x46\x20\x69\x6e\x74\x65\x6e"
+                         "\x64\x65\x64\x20\x62\x79\x20\x74"
+                         "\x68\x65\x20\x43\x6f\x6e\x74\x72"
+                         "\x69\x62\x75\x74\x6f\x72\x20\x66"
+                         "\x6f\x72\x20\x70\x75\x62\x6c\x69"
+                         "\x63\x61\x74\x69\x6f\x6e\x20\x61"
+                         "\x73\x20\x61\x6c\x6c\x20\x6f\x72"
+                         "\x20\x70\x61\x72\x74\x20\x6f\x66"
+                         "\x20\x61\x6e\x20\x49\x45\x54\x46"
+                         "\x20\x49\x6e\x74\x65\x72\x6e\x65"
+                         "\x74\x2d\x44\x72\x61\x66\x74\x20"
+                         "\x6f\x72\x20\x52\x46\x43\x20\x61"
+                         "\x6e\x64\x20\x61\x6e\x79\x20\x73"
+                         "\x74\x61\x74\x65\x6d\x65\x6e\x74"
+                         "\x20\x6d\x61\x64\x65\x20\x77\x69"
+                         "\x74\x68\x69\x6e\x20\x74\x68\x65"
+                         "\x20\x63\x6f\x6e\x74\x65\x78\x74"
+                         "\x20\x6f\x66\x20\x61\x6e\x20\x49"
+                         "\x45\x54\x46\x20\x61\x63\x74\x69"
+                         "\x76\x69\x74\x79\x20\x69\x73\x20"
+                         "\x63\x6f\x6e\x73\x69\x64\x65\x72"
+                         "\x65\x64\x20\x61\x6e\x20\x22\x49"
+                         "\x45\x54\x46\x20\x43\x6f\x6e\x74"
+                         "\x72\x69\x62\x75\x74\x69\x6f\x6e"
+                         "\x22\x2e\x20\x53\x75\x63\x68\x20"
+                         "\x73\x74\x61\x74\x65\x6d\x65\x6e"
+                         "\x74\x73\x20\x69\x6e\x63\x6c\x75"
+                         "\x64\x65\x20\x6f\x72\x61\x6c\x20"
+                         "\x73\x74\x61\x74\x65\x6d\x65\x6e"
+                         "\x74\x73\x20\x69\x6e\x20\x49\x45"
+                         "\x54\x46\x20\x73\x65\x73\x73\x69"
+                         "\x6f\x6e\x73\x2c\x20\x61\x73\x20"
+                         "\x77\x65\x6c\x6c\x20\x61\x73\x20"
+                         "\x77\x72\x69\x74\x74\x65\x6e\x20"
+                         "\x61\x6e\x64\x20\x65\x6c\x65\x63"
+                         "\x74\x72\x6f\x6e\x69\x63\x20\x63"
+                         "\x6f\x6d\x6d\x75\x6e\x69\x63\x61"
+                         "\x74\x69\x6f\x6e\x73\x20\x6d\x61"
+                         "\x64\x65\x20\x61\x74\x20\x61\x6e"
+                         "\x79\x20\x74\x69\x6d\x65\x20\x6f"
+                         "\x72\x20\x70\x6c\x61\x63\x65\x2c"
+                         "\x20\x77\x68\x69\x63\x68\x20\x61"
+                         "\x72\x65\x20\x61\x64\x64\x72\x65"
+                         "\x73\x73\x65\x64\x20\x74\x6f",
+               .ilen   = 375,
+               .result = "\xa3\xfb\xf0\x7d\xf3\xfa\x2f\xde"
+                         "\x4f\x37\x6c\xa2\x3e\x82\x73\x70"
+                         "\x41\x60\x5d\x9f\x4f\x4f\x57\xbd"
+                         "\x8c\xff\x2c\x1d\x4b\x79\x55\xec"
+                         "\x2a\x97\x94\x8b\xd3\x72\x29\x15"
+                         "\xc8\xf3\xd3\x37\xf7\xd3\x70\x05"
+                         "\x0e\x9e\x96\xd6\x47\xb7\xc3\x9f"
+                         "\x56\xe0\x31\xca\x5e\xb6\x25\x0d"
+                         "\x40\x42\xe0\x27\x85\xec\xec\xfa"
+                         "\x4b\x4b\xb5\xe8\xea\xd0\x44\x0e"
+                         "\x20\xb6\xe8\xdb\x09\xd8\x81\xa7"
+                         "\xc6\x13\x2f\x42\x0e\x52\x79\x50"
+                         "\x42\xbd\xfa\x77\x73\xd8\xa9\x05"
+                         "\x14\x47\xb3\x29\x1c\xe1\x41\x1c"
+                         "\x68\x04\x65\x55\x2a\xa6\xc4\x05"
+                         "\xb7\x76\x4d\x5e\x87\xbe\xa8\x5a"
+                         "\xd0\x0f\x84\x49\xed\x8f\x72\xd0"
+                         "\xd6\x62\xab\x05\x26\x91\xca\x66"
+                         "\x42\x4b\xc8\x6d\x2d\xf8\x0e\xa4"
+                         "\x1f\x43\xab\xf9\x37\xd3\x25\x9d"
+                         "\xc4\xb2\xd0\xdf\xb4\x8a\x6c\x91"
+                         "\x39\xdd\xd7\xf7\x69\x66\xe9\x28"
+                         "\xe6\x35\x55\x3b\xa7\x6c\x5c\x87"
+                         "\x9d\x7b\x35\xd4\x9e\xb2\xe6\x2b"
+                         "\x08\x71\xcd\xac\x63\x89\x39\xe2"
+                         "\x5e\x8a\x1e\x0e\xf9\xd5\x28\x0f"
+                         "\xa8\xca\x32\x8b\x35\x1c\x3c\x76"
+                         "\x59\x89\xcb\xcf\x3d\xaa\x8b\x6c"
+                         "\xcc\x3a\xaf\x9f\x39\x79\xc9\x2b"
+                         "\x37\x20\xfc\x88\xdc\x95\xed\x84"
+                         "\xa1\xbe\x05\x9c\x64\x99\xb9\xfd"
+                         "\xa2\x36\xe7\xe8\x18\xb0\x4b\x0b"
+                         "\xc3\x9c\x1e\x87\x6b\x19\x3b\xfe"
+                         "\x55\x69\x75\x3f\x88\x12\x8c\xc0"
+                         "\x8a\xaa\x9b\x63\xd1\xa1\x6f\x80"
+                         "\xef\x25\x54\xd7\x18\x9c\x41\x1f"
+                         "\x58\x69\xca\x52\xc5\xb8\x3f\xa3"
+                         "\x6f\xf2\x16\xb9\xc1\xd3\x00\x62"
+                         "\xbe\xbc\xfd\x2d\xc5\xbc\xe0\x91"
+                         "\x19\x34\xfd\xa7\x9a\x86\xf6\xe6"
+                         "\x98\xce\xd7\x59\xc3\xff\x9b\x64"
+                         "\x77\x33\x8f\x3d\xa4\xf9\xcd\x85"
+                         "\x14\xea\x99\x82\xcc\xaf\xb3\x41"
+                         "\xb2\x38\x4d\xd9\x02\xf3\xd1\xab"
+                         "\x7a\xc6\x1d\xd2\x9c\x6f\x21\xba"
+                         "\x5b\x86\x2f\x37\x30\xe3\x7c\xfd"
+                         "\xc4\xfd\x80\x6c\x22\xf2\x21",
+               .rlen   = 375,
+       }, { /* RFC7539 A.2. Test Vector #3 */
+               .key    = "\x1c\x92\x40\xa5\xeb\x55\xd3\x8a"
+                         "\xf3\x33\x88\x86\x04\xf6\xb5\xf0"
+                         "\x47\x39\x17\xc1\x40\x2b\x80\x09"
+                         "\x9d\xca\x5c\xbc\x20\x70\x75\xc0",
+               .klen   = 32,
+               .iv     = "\x2a\x00\x00\x00\x00\x00\x00\x00"
+                         "\x00\x00\x00\x00\x00\x00\x00\x02",
+               .input  = "\x27\x54\x77\x61\x73\x20\x62\x72"
+                         "\x69\x6c\x6c\x69\x67\x2c\x20\x61"
+                         "\x6e\x64\x20\x74\x68\x65\x20\x73"
+                         "\x6c\x69\x74\x68\x79\x20\x74\x6f"
+                         "\x76\x65\x73\x0a\x44\x69\x64\x20"
+                         "\x67\x79\x72\x65\x20\x61\x6e\x64"
+                         "\x20\x67\x69\x6d\x62\x6c\x65\x20"
+                         "\x69\x6e\x20\x74\x68\x65\x20\x77"
+                         "\x61\x62\x65\x3a\x0a\x41\x6c\x6c"
+                         "\x20\x6d\x69\x6d\x73\x79\x20\x77"
+                         "\x65\x72\x65\x20\x74\x68\x65\x20"
+                         "\x62\x6f\x72\x6f\x67\x6f\x76\x65"
+                         "\x73\x2c\x0a\x41\x6e\x64\x20\x74"
+                         "\x68\x65\x20\x6d\x6f\x6d\x65\x20"
+                         "\x72\x61\x74\x68\x73\x20\x6f\x75"
+                         "\x74\x67\x72\x61\x62\x65\x2e",
+               .ilen   = 127,
+               .result = "\x62\xe6\x34\x7f\x95\xed\x87\xa4"
+                         "\x5f\xfa\xe7\x42\x6f\x27\xa1\xdf"
+                         "\x5f\xb6\x91\x10\x04\x4c\x0d\x73"
+                         "\x11\x8e\xff\xa9\x5b\x01\xe5\xcf"
+                         "\x16\x6d\x3d\xf2\xd7\x21\xca\xf9"
+                         "\xb2\x1e\x5f\xb1\x4c\x61\x68\x71"
+                         "\xfd\x84\xc5\x4f\x9d\x65\xb2\x83"
+                         "\x19\x6c\x7f\xe4\xf6\x05\x53\xeb"
+                         "\xf3\x9c\x64\x02\xc4\x22\x34\xe3"
+                         "\x2a\x35\x6b\x3e\x76\x43\x12\xa6"
+                         "\x1a\x55\x32\x05\x57\x16\xea\xd6"
+                         "\x96\x25\x68\xf8\x7d\x3f\x3f\x77"
+                         "\x04\xc6\xa8\xd1\xbc\xd1\xbf\x4d"
+                         "\x50\xd6\x15\x4b\x6d\xa7\x31\xb1"
+                         "\x87\xb5\x8d\xfd\x72\x8a\xfa\x36"
+                         "\x75\x7a\x79\x7a\xc1\x88\xd1",
+               .rlen   = 127,
+       },
+};
+
 /*
  * CTS (Cipher Text Stealing) mode tests
  */
@@ -28591,7 +30572,7 @@ struct comp_testvec {
 };
 
 struct pcomp_testvec {
-       void *params;
+       const void *params;
        unsigned int paramsize;
        int inlen, outlen;
        char input[COMP_BUF_SIZE];
@@ -28945,6 +30926,440 @@ static struct hash_testvec michael_mic_tv_template[] = {
        }
 };
 
+/*
+ * CRC32 test vectors
+ */
+#define CRC32_TEST_VECTORS 14
+
+static struct hash_testvec crc32_tv_template[] = {
+       {
+               .key = "\x87\xa9\xcb\xed",
+               .ksize = 4,
+               .psize = 0,
+               .digest = "\x87\xa9\xcb\xed",
+       },
+       {
+               .key = "\xff\xff\xff\xff",
+               .ksize = 4,
+               .plaintext = "\x01\x02\x03\x04\x05\x06\x07\x08"
+                            "\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10"
+                            "\x11\x12\x13\x14\x15\x16\x17\x18"
+                            "\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20"
+                            "\x21\x22\x23\x24\x25\x26\x27\x28",
+               .psize = 40,
+               .digest = "\x3a\xdf\x4b\xb0",
+       },
+       {
+               .key = "\xff\xff\xff\xff",
+               .ksize = 4,
+               .plaintext = "\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30"
+                            "\x31\x32\x33\x34\x35\x36\x37\x38"
+                            "\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40"
+                            "\x41\x42\x43\x44\x45\x46\x47\x48"
+                            "\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50",
+               .psize = 40,
+               .digest = "\xa9\x7a\x7f\x7b",
+       },
+       {
+               .key = "\xff\xff\xff\xff",
+               .ksize = 4,
+               .plaintext = "\x51\x52\x53\x54\x55\x56\x57\x58"
+                            "\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60"
+                            "\x61\x62\x63\x64\x65\x66\x67\x68"
+                            "\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70"
+                            "\x71\x72\x73\x74\x75\x76\x77\x78",
+               .psize = 40,
+               .digest = "\xba\xd3\xf8\x1c",
+       },
+       {
+               .key = "\xff\xff\xff\xff",
+               .ksize = 4,
+               .plaintext = "\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80"
+                            "\x81\x82\x83\x84\x85\x86\x87\x88"
+                            "\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90"
+                            "\x91\x92\x93\x94\x95\x96\x97\x98"
+                            "\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0",
+               .psize = 40,
+               .digest = "\xa8\xa9\xc2\x02",
+       },
+       {
+               .key = "\xff\xff\xff\xff",
+               .ksize = 4,
+               .plaintext = "\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8"
+                            "\xa9\xaa\xab\xac\xad\xae\xaf\xb0"
+                            "\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8"
+                            "\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0"
+                            "\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8",
+               .psize = 40,
+               .digest = "\x27\xf0\x57\xe2",
+       },
+       {
+               .key = "\xff\xff\xff\xff",
+               .ksize = 4,
+               .plaintext = "\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0"
+                            "\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8"
+                            "\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0"
+                            "\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8"
+                            "\xe9\xea\xeb\xec\xed\xee\xef\xf0",
+               .psize = 40,
+               .digest = "\x49\x78\x10\x08",
+       },
+       {
+               .key = "\x80\xea\xd3\xf1",
+               .ksize = 4,
+               .plaintext = "\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30"
+                            "\x31\x32\x33\x34\x35\x36\x37\x38"
+                            "\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40"
+                            "\x41\x42\x43\x44\x45\x46\x47\x48"
+                            "\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50",
+               .psize = 40,
+               .digest = "\x9a\xb1\xdc\xf0",
+       },
+       {
+               .key = "\xf3\x4a\x1d\x5d",
+               .ksize = 4,
+               .plaintext = "\x51\x52\x53\x54\x55\x56\x57\x58"
+                            "\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60"
+                            "\x61\x62\x63\x64\x65\x66\x67\x68"
+                            "\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70"
+                            "\x71\x72\x73\x74\x75\x76\x77\x78",
+               .psize = 40,
+               .digest = "\xb4\x97\xcc\xd4",
+       },
+       {
+               .key = "\x2e\x80\x04\x59",
+               .ksize = 4,
+               .plaintext = "\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80"
+                            "\x81\x82\x83\x84\x85\x86\x87\x88"
+                            "\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90"
+                            "\x91\x92\x93\x94\x95\x96\x97\x98"
+                            "\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0",
+               .psize = 40,
+               .digest = "\x67\x9b\xfa\x79",
+       },
+       {
+               .key = "\xa6\xcc\x19\x85",
+               .ksize = 4,
+               .plaintext = "\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8"
+                            "\xa9\xaa\xab\xac\xad\xae\xaf\xb0"
+                            "\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8"
+                            "\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0"
+                            "\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8",
+               .psize = 40,
+               .digest = "\x24\xb5\x16\xef",
+       },
+       {
+               .key = "\x41\xfc\xfe\x2d",
+               .ksize = 4,
+               .plaintext = "\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0"
+                            "\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8"
+                            "\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0"
+                            "\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8"
+                            "\xe9\xea\xeb\xec\xed\xee\xef\xf0",
+               .psize = 40,
+               .digest = "\x15\x94\x80\x39",
+       },
+       {
+               .key = "\xff\xff\xff\xff",
+               .ksize = 4,
+               .plaintext = "\x01\x02\x03\x04\x05\x06\x07\x08"
+                            "\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10"
+                            "\x11\x12\x13\x14\x15\x16\x17\x18"
+                            "\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20"
+                            "\x21\x22\x23\x24\x25\x26\x27\x28"
+                            "\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30"
+                            "\x31\x32\x33\x34\x35\x36\x37\x38"
+                            "\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40"
+                            "\x41\x42\x43\x44\x45\x46\x47\x48"
+                            "\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50"
+                            "\x51\x52\x53\x54\x55\x56\x57\x58"
+                            "\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60"
+                            "\x61\x62\x63\x64\x65\x66\x67\x68"
+                            "\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70"
+                            "\x71\x72\x73\x74\x75\x76\x77\x78"
+                            "\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80"
+                            "\x81\x82\x83\x84\x85\x86\x87\x88"
+                            "\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90"
+                            "\x91\x92\x93\x94\x95\x96\x97\x98"
+                            "\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0"
+                            "\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8"
+                            "\xa9\xaa\xab\xac\xad\xae\xaf\xb0"
+                            "\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8"
+                            "\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0"
+                            "\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8"
+                            "\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0"
+                            "\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8"
+                            "\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0"
+                            "\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8"
+                            "\xe9\xea\xeb\xec\xed\xee\xef\xf0",
+               .psize = 240,
+               .digest = "\x6c\xc6\x56\xde",
+               .np = 2,
+               .tap = { 31, 209 }
+       }, {
+               .key = "\xff\xff\xff\xff",
+               .ksize = 4,
+               .plaintext =    "\x6e\x05\x79\x10\xa7\x1b\xb2\x49"
+                               "\xe0\x54\xeb\x82\x19\x8d\x24\xbb"
+                               "\x2f\xc6\x5d\xf4\x68\xff\x96\x0a"
+                               "\xa1\x38\xcf\x43\xda\x71\x08\x7c"
+                               "\x13\xaa\x1e\xb5\x4c\xe3\x57\xee"
+                               "\x85\x1c\x90\x27\xbe\x32\xc9\x60"
+                               "\xf7\x6b\x02\x99\x0d\xa4\x3b\xd2"
+                               "\x46\xdd\x74\x0b\x7f\x16\xad\x21"
+                               "\xb8\x4f\xe6\x5a\xf1\x88\x1f\x93"
+                               "\x2a\xc1\x35\xcc\x63\xfa\x6e\x05"
+                               "\x9c\x10\xa7\x3e\xd5\x49\xe0\x77"
+                               "\x0e\x82\x19\xb0\x24\xbb\x52\xe9"
+                               "\x5d\xf4\x8b\x22\x96\x2d\xc4\x38"
+                               "\xcf\x66\xfd\x71\x08\x9f\x13\xaa"
+                               "\x41\xd8\x4c\xe3\x7a\x11\x85\x1c"
+                               "\xb3\x27\xbe\x55\xec\x60\xf7\x8e"
+                               "\x02\x99\x30\xc7\x3b\xd2\x69\x00"
+                               "\x74\x0b\xa2\x16\xad\x44\xdb\x4f"
+                               "\xe6\x7d\x14\x88\x1f\xb6\x2a\xc1"
+                               "\x58\xef\x63\xfa\x91\x05\x9c\x33"
+                               "\xca\x3e\xd5\x6c\x03\x77\x0e\xa5"
+                               "\x19\xb0\x47\xde\x52\xe9\x80\x17"
+                               "\x8b\x22\xb9\x2d\xc4\x5b\xf2\x66"
+                               "\xfd\x94\x08\x9f\x36\xcd\x41\xd8"
+                               "\x6f\x06\x7a\x11\xa8\x1c\xb3\x4a"
+                               "\xe1\x55\xec\x83\x1a\x8e\x25\xbc"
+                               "\x30\xc7\x5e\xf5\x69\x00\x97\x0b"
+                               "\xa2\x39\xd0\x44\xdb\x72\x09\x7d"
+                               "\x14\xab\x1f\xb6\x4d\xe4\x58\xef"
+                               "\x86\x1d\x91\x28\xbf\x33\xca\x61"
+                               "\xf8\x6c\x03\x9a\x0e\xa5\x3c\xd3"
+                               "\x47\xde\x75\x0c\x80\x17\xae\x22"
+                               "\xb9\x50\xe7\x5b\xf2\x89\x20\x94"
+                               "\x2b\xc2\x36\xcd\x64\xfb\x6f\x06"
+                               "\x9d\x11\xa8\x3f\xd6\x4a\xe1\x78"
+                               "\x0f\x83\x1a\xb1\x25\xbc\x53\xea"
+                               "\x5e\xf5\x8c\x00\x97\x2e\xc5\x39"
+                               "\xd0\x67\xfe\x72\x09\xa0\x14\xab"
+                               "\x42\xd9\x4d\xe4\x7b\x12\x86\x1d"
+                               "\xb4\x28\xbf\x56\xed\x61\xf8\x8f"
+                               "\x03\x9a\x31\xc8\x3c\xd3\x6a\x01"
+                               "\x75\x0c\xa3\x17\xae\x45\xdc\x50"
+                               "\xe7\x7e\x15\x89\x20\xb7\x2b\xc2"
+                               "\x59\xf0\x64\xfb\x92\x06\x9d\x34"
+                               "\xcb\x3f\xd6\x6d\x04\x78\x0f\xa6"
+                               "\x1a\xb1\x48\xdf\x53\xea\x81\x18"
+                               "\x8c\x23\xba\x2e\xc5\x5c\xf3\x67"
+                               "\xfe\x95\x09\xa0\x37\xce\x42\xd9"
+                               "\x70\x07\x7b\x12\xa9\x1d\xb4\x4b"
+                               "\xe2\x56\xed\x84\x1b\x8f\x26\xbd"
+                               "\x31\xc8\x5f\xf6\x6a\x01\x98\x0c"
+                               "\xa3\x3a\xd1\x45\xdc\x73\x0a\x7e"
+                               "\x15\xac\x20\xb7\x4e\xe5\x59\xf0"
+                               "\x87\x1e\x92\x29\xc0\x34\xcb\x62"
+                               "\xf9\x6d\x04\x9b\x0f\xa6\x3d\xd4"
+                               "\x48\xdf\x76\x0d\x81\x18\xaf\x23"
+                               "\xba\x51\xe8\x5c\xf3\x8a\x21\x95"
+                               "\x2c\xc3\x37\xce\x65\xfc\x70\x07"
+                               "\x9e\x12\xa9\x40\xd7\x4b\xe2\x79"
+                               "\x10\x84\x1b\xb2\x26\xbd\x54\xeb"
+                               "\x5f\xf6\x8d\x01\x98\x2f\xc6\x3a"
+                               "\xd1\x68\xff\x73\x0a\xa1\x15\xac"
+                               "\x43\xda\x4e\xe5\x7c\x13\x87\x1e"
+                               "\xb5\x29\xc0\x57\xee\x62\xf9\x90"
+                               "\x04\x9b\x32\xc9\x3d\xd4\x6b\x02"
+                               "\x76\x0d\xa4\x18\xaf\x46\xdd\x51"
+                               "\xe8\x7f\x16\x8a\x21\xb8\x2c\xc3"
+                               "\x5a\xf1\x65\xfc\x93\x07\x9e\x35"
+                               "\xcc\x40\xd7\x6e\x05\x79\x10\xa7"
+                               "\x1b\xb2\x49\xe0\x54\xeb\x82\x19"
+                               "\x8d\x24\xbb\x2f\xc6\x5d\xf4\x68"
+                               "\xff\x96\x0a\xa1\x38\xcf\x43\xda"
+                               "\x71\x08\x7c\x13\xaa\x1e\xb5\x4c"
+                               "\xe3\x57\xee\x85\x1c\x90\x27\xbe"
+                               "\x32\xc9\x60\xf7\x6b\x02\x99\x0d"
+                               "\xa4\x3b\xd2\x46\xdd\x74\x0b\x7f"
+                               "\x16\xad\x21\xb8\x4f\xe6\x5a\xf1"
+                               "\x88\x1f\x93\x2a\xc1\x35\xcc\x63"
+                               "\xfa\x6e\x05\x9c\x10\xa7\x3e\xd5"
+                               "\x49\xe0\x77\x0e\x82\x19\xb0\x24"
+                               "\xbb\x52\xe9\x5d\xf4\x8b\x22\x96"
+                               "\x2d\xc4\x38\xcf\x66\xfd\x71\x08"
+                               "\x9f\x13\xaa\x41\xd8\x4c\xe3\x7a"
+                               "\x11\x85\x1c\xb3\x27\xbe\x55\xec"
+                               "\x60\xf7\x8e\x02\x99\x30\xc7\x3b"
+                               "\xd2\x69\x00\x74\x0b\xa2\x16\xad"
+                               "\x44\xdb\x4f\xe6\x7d\x14\x88\x1f"
+                               "\xb6\x2a\xc1\x58\xef\x63\xfa\x91"
+                               "\x05\x9c\x33\xca\x3e\xd5\x6c\x03"
+                               "\x77\x0e\xa5\x19\xb0\x47\xde\x52"
+                               "\xe9\x80\x17\x8b\x22\xb9\x2d\xc4"
+                               "\x5b\xf2\x66\xfd\x94\x08\x9f\x36"
+                               "\xcd\x41\xd8\x6f\x06\x7a\x11\xa8"
+                               "\x1c\xb3\x4a\xe1\x55\xec\x83\x1a"
+                               "\x8e\x25\xbc\x30\xc7\x5e\xf5\x69"
+                               "\x00\x97\x0b\xa2\x39\xd0\x44\xdb"
+                               "\x72\x09\x7d\x14\xab\x1f\xb6\x4d"
+                               "\xe4\x58\xef\x86\x1d\x91\x28\xbf"
+                               "\x33\xca\x61\xf8\x6c\x03\x9a\x0e"
+                               "\xa5\x3c\xd3\x47\xde\x75\x0c\x80"
+                               "\x17\xae\x22\xb9\x50\xe7\x5b\xf2"
+                               "\x89\x20\x94\x2b\xc2\x36\xcd\x64"
+                               "\xfb\x6f\x06\x9d\x11\xa8\x3f\xd6"
+                               "\x4a\xe1\x78\x0f\x83\x1a\xb1\x25"
+                               "\xbc\x53\xea\x5e\xf5\x8c\x00\x97"
+                               "\x2e\xc5\x39\xd0\x67\xfe\x72\x09"
+                               "\xa0\x14\xab\x42\xd9\x4d\xe4\x7b"
+                               "\x12\x86\x1d\xb4\x28\xbf\x56\xed"
+                               "\x61\xf8\x8f\x03\x9a\x31\xc8\x3c"
+                               "\xd3\x6a\x01\x75\x0c\xa3\x17\xae"
+                               "\x45\xdc\x50\xe7\x7e\x15\x89\x20"
+                               "\xb7\x2b\xc2\x59\xf0\x64\xfb\x92"
+                               "\x06\x9d\x34\xcb\x3f\xd6\x6d\x04"
+                               "\x78\x0f\xa6\x1a\xb1\x48\xdf\x53"
+                               "\xea\x81\x18\x8c\x23\xba\x2e\xc5"
+                               "\x5c\xf3\x67\xfe\x95\x09\xa0\x37"
+                               "\xce\x42\xd9\x70\x07\x7b\x12\xa9"
+                               "\x1d\xb4\x4b\xe2\x56\xed\x84\x1b"
+                               "\x8f\x26\xbd\x31\xc8\x5f\xf6\x6a"
+                               "\x01\x98\x0c\xa3\x3a\xd1\x45\xdc"
+                               "\x73\x0a\x7e\x15\xac\x20\xb7\x4e"
+                               "\xe5\x59\xf0\x87\x1e\x92\x29\xc0"
+                               "\x34\xcb\x62\xf9\x6d\x04\x9b\x0f"
+                               "\xa6\x3d\xd4\x48\xdf\x76\x0d\x81"
+                               "\x18\xaf\x23\xba\x51\xe8\x5c\xf3"
+                               "\x8a\x21\x95\x2c\xc3\x37\xce\x65"
+                               "\xfc\x70\x07\x9e\x12\xa9\x40\xd7"
+                               "\x4b\xe2\x79\x10\x84\x1b\xb2\x26"
+                               "\xbd\x54\xeb\x5f\xf6\x8d\x01\x98"
+                               "\x2f\xc6\x3a\xd1\x68\xff\x73\x0a"
+                               "\xa1\x15\xac\x43\xda\x4e\xe5\x7c"
+                               "\x13\x87\x1e\xb5\x29\xc0\x57\xee"
+                               "\x62\xf9\x90\x04\x9b\x32\xc9\x3d"
+                               "\xd4\x6b\x02\x76\x0d\xa4\x18\xaf"
+                               "\x46\xdd\x51\xe8\x7f\x16\x8a\x21"
+                               "\xb8\x2c\xc3\x5a\xf1\x65\xfc\x93"
+                               "\x07\x9e\x35\xcc\x40\xd7\x6e\x05"
+                               "\x79\x10\xa7\x1b\xb2\x49\xe0\x54"
+                               "\xeb\x82\x19\x8d\x24\xbb\x2f\xc6"
+                               "\x5d\xf4\x68\xff\x96\x0a\xa1\x38"
+                               "\xcf\x43\xda\x71\x08\x7c\x13\xaa"
+                               "\x1e\xb5\x4c\xe3\x57\xee\x85\x1c"
+                               "\x90\x27\xbe\x32\xc9\x60\xf7\x6b"
+                               "\x02\x99\x0d\xa4\x3b\xd2\x46\xdd"
+                               "\x74\x0b\x7f\x16\xad\x21\xb8\x4f"
+                               "\xe6\x5a\xf1\x88\x1f\x93\x2a\xc1"
+                               "\x35\xcc\x63\xfa\x6e\x05\x9c\x10"
+                               "\xa7\x3e\xd5\x49\xe0\x77\x0e\x82"
+                               "\x19\xb0\x24\xbb\x52\xe9\x5d\xf4"
+                               "\x8b\x22\x96\x2d\xc4\x38\xcf\x66"
+                               "\xfd\x71\x08\x9f\x13\xaa\x41\xd8"
+                               "\x4c\xe3\x7a\x11\x85\x1c\xb3\x27"
+                               "\xbe\x55\xec\x60\xf7\x8e\x02\x99"
+                               "\x30\xc7\x3b\xd2\x69\x00\x74\x0b"
+                               "\xa2\x16\xad\x44\xdb\x4f\xe6\x7d"
+                               "\x14\x88\x1f\xb6\x2a\xc1\x58\xef"
+                               "\x63\xfa\x91\x05\x9c\x33\xca\x3e"
+                               "\xd5\x6c\x03\x77\x0e\xa5\x19\xb0"
+                               "\x47\xde\x52\xe9\x80\x17\x8b\x22"
+                               "\xb9\x2d\xc4\x5b\xf2\x66\xfd\x94"
+                               "\x08\x9f\x36\xcd\x41\xd8\x6f\x06"
+                               "\x7a\x11\xa8\x1c\xb3\x4a\xe1\x55"
+                               "\xec\x83\x1a\x8e\x25\xbc\x30\xc7"
+                               "\x5e\xf5\x69\x00\x97\x0b\xa2\x39"
+                               "\xd0\x44\xdb\x72\x09\x7d\x14\xab"
+                               "\x1f\xb6\x4d\xe4\x58\xef\x86\x1d"
+                               "\x91\x28\xbf\x33\xca\x61\xf8\x6c"
+                               "\x03\x9a\x0e\xa5\x3c\xd3\x47\xde"
+                               "\x75\x0c\x80\x17\xae\x22\xb9\x50"
+                               "\xe7\x5b\xf2\x89\x20\x94\x2b\xc2"
+                               "\x36\xcd\x64\xfb\x6f\x06\x9d\x11"
+                               "\xa8\x3f\xd6\x4a\xe1\x78\x0f\x83"
+                               "\x1a\xb1\x25\xbc\x53\xea\x5e\xf5"
+                               "\x8c\x00\x97\x2e\xc5\x39\xd0\x67"
+                               "\xfe\x72\x09\xa0\x14\xab\x42\xd9"
+                               "\x4d\xe4\x7b\x12\x86\x1d\xb4\x28"
+                               "\xbf\x56\xed\x61\xf8\x8f\x03\x9a"
+                               "\x31\xc8\x3c\xd3\x6a\x01\x75\x0c"
+                               "\xa3\x17\xae\x45\xdc\x50\xe7\x7e"
+                               "\x15\x89\x20\xb7\x2b\xc2\x59\xf0"
+                               "\x64\xfb\x92\x06\x9d\x34\xcb\x3f"
+                               "\xd6\x6d\x04\x78\x0f\xa6\x1a\xb1"
+                               "\x48\xdf\x53\xea\x81\x18\x8c\x23"
+                               "\xba\x2e\xc5\x5c\xf3\x67\xfe\x95"
+                               "\x09\xa0\x37\xce\x42\xd9\x70\x07"
+                               "\x7b\x12\xa9\x1d\xb4\x4b\xe2\x56"
+                               "\xed\x84\x1b\x8f\x26\xbd\x31\xc8"
+                               "\x5f\xf6\x6a\x01\x98\x0c\xa3\x3a"
+                               "\xd1\x45\xdc\x73\x0a\x7e\x15\xac"
+                               "\x20\xb7\x4e\xe5\x59\xf0\x87\x1e"
+                               "\x92\x29\xc0\x34\xcb\x62\xf9\x6d"
+                               "\x04\x9b\x0f\xa6\x3d\xd4\x48\xdf"
+                               "\x76\x0d\x81\x18\xaf\x23\xba\x51"
+                               "\xe8\x5c\xf3\x8a\x21\x95\x2c\xc3"
+                               "\x37\xce\x65\xfc\x70\x07\x9e\x12"
+                               "\xa9\x40\xd7\x4b\xe2\x79\x10\x84"
+                               "\x1b\xb2\x26\xbd\x54\xeb\x5f\xf6"
+                               "\x8d\x01\x98\x2f\xc6\x3a\xd1\x68"
+                               "\xff\x73\x0a\xa1\x15\xac\x43\xda"
+                               "\x4e\xe5\x7c\x13\x87\x1e\xb5\x29"
+                               "\xc0\x57\xee\x62\xf9\x90\x04\x9b"
+                               "\x32\xc9\x3d\xd4\x6b\x02\x76\x0d"
+                               "\xa4\x18\xaf\x46\xdd\x51\xe8\x7f"
+                               "\x16\x8a\x21\xb8\x2c\xc3\x5a\xf1"
+                               "\x65\xfc\x93\x07\x9e\x35\xcc\x40"
+                               "\xd7\x6e\x05\x79\x10\xa7\x1b\xb2"
+                               "\x49\xe0\x54\xeb\x82\x19\x8d\x24"
+                               "\xbb\x2f\xc6\x5d\xf4\x68\xff\x96"
+                               "\x0a\xa1\x38\xcf\x43\xda\x71\x08"
+                               "\x7c\x13\xaa\x1e\xb5\x4c\xe3\x57"
+                               "\xee\x85\x1c\x90\x27\xbe\x32\xc9"
+                               "\x60\xf7\x6b\x02\x99\x0d\xa4\x3b"
+                               "\xd2\x46\xdd\x74\x0b\x7f\x16\xad"
+                               "\x21\xb8\x4f\xe6\x5a\xf1\x88\x1f"
+                               "\x93\x2a\xc1\x35\xcc\x63\xfa\x6e"
+                               "\x05\x9c\x10\xa7\x3e\xd5\x49\xe0"
+                               "\x77\x0e\x82\x19\xb0\x24\xbb\x52"
+                               "\xe9\x5d\xf4\x8b\x22\x96\x2d\xc4"
+                               "\x38\xcf\x66\xfd\x71\x08\x9f\x13"
+                               "\xaa\x41\xd8\x4c\xe3\x7a\x11\x85"
+                               "\x1c\xb3\x27\xbe\x55\xec\x60\xf7"
+                               "\x8e\x02\x99\x30\xc7\x3b\xd2\x69"
+                               "\x00\x74\x0b\xa2\x16\xad\x44\xdb"
+                               "\x4f\xe6\x7d\x14\x88\x1f\xb6\x2a"
+                               "\xc1\x58\xef\x63\xfa\x91\x05\x9c"
+                               "\x33\xca\x3e\xd5\x6c\x03\x77\x0e"
+                               "\xa5\x19\xb0\x47\xde\x52\xe9\x80"
+                               "\x17\x8b\x22\xb9\x2d\xc4\x5b\xf2"
+                               "\x66\xfd\x94\x08\x9f\x36\xcd\x41"
+                               "\xd8\x6f\x06\x7a\x11\xa8\x1c\xb3"
+                               "\x4a\xe1\x55\xec\x83\x1a\x8e\x25"
+                               "\xbc\x30\xc7\x5e\xf5\x69\x00\x97"
+                               "\x0b\xa2\x39\xd0\x44\xdb\x72\x09"
+                               "\x7d\x14\xab\x1f\xb6\x4d\xe4\x58"
+                               "\xef\x86\x1d\x91\x28\xbf\x33\xca"
+                               "\x61\xf8\x6c\x03\x9a\x0e\xa5\x3c"
+                               "\xd3\x47\xde\x75\x0c\x80\x17\xae"
+                               "\x22\xb9\x50\xe7\x5b\xf2\x89\x20"
+                               "\x94\x2b\xc2\x36\xcd\x64\xfb\x6f"
+                               "\x06\x9d\x11\xa8\x3f\xd6\x4a\xe1"
+                               "\x78\x0f\x83\x1a\xb1\x25\xbc\x53"
+                               "\xea\x5e\xf5\x8c\x00\x97\x2e\xc5"
+                               "\x39\xd0\x67\xfe\x72\x09\xa0\x14"
+                               "\xab\x42\xd9\x4d\xe4\x7b\x12\x86"
+                               "\x1d\xb4\x28\xbf\x56\xed\x61\xf8"
+                               "\x8f\x03\x9a\x31\xc8\x3c\xd3\x6a"
+                               "\x01\x75\x0c\xa3\x17\xae\x45\xdc"
+                               "\x50\xe7\x7e\x15\x89\x20\xb7\x2b"
+                               "\xc2\x59\xf0\x64\xfb\x92\x06\x9d"
+                               "\x34\xcb\x3f\xd6\x6d\x04\x78\x0f"
+                               "\xa6\x1a\xb1\x48\xdf\x53\xea\x81"
+                               "\x18\x8c\x23\xba\x2e\xc5\x5c\xf3"
+                               "\x67\xfe\x95\x09\xa0\x37\xce\x42"
+                               "\xd9\x70\x07\x7b\x12\xa9\x1d\xb4"
+                               "\x4b\xe2\x56\xed\x84\x1b\x8f\x26"
+                               "\xbd\x31\xc8\x5f\xf6\x6a\x01\x98",
+               .psize = 2048,
+               .digest = "\xfb\x3a\x7a\xda",
+       }
+};
+
 /*
  * CRC32C test vectors
  */
index 0eefa9d237ace7f3b607bbfcc4f657580eaa60a0..d51a30a29e421fe6a80af8d0ce0ec78b048fe5e5 100644 (file)
@@ -78,7 +78,7 @@ static void zlib_exit(struct crypto_tfm *tfm)
 }
 
 
-static int zlib_compress_setup(struct crypto_pcomp *tfm, void *params,
+static int zlib_compress_setup(struct crypto_pcomp *tfm, const void *params,
                               unsigned int len)
 {
        struct zlib_ctx *ctx = crypto_tfm_ctx(crypto_pcomp_tfm(tfm));
@@ -209,7 +209,7 @@ static int zlib_compress_final(struct crypto_pcomp *tfm,
 }
 
 
-static int zlib_decompress_setup(struct crypto_pcomp *tfm, void *params,
+static int zlib_decompress_setup(struct crypto_pcomp *tfm, const void *params,
                                 unsigned int len)
 {
        struct zlib_ctx *ctx = crypto_tfm_ctx(crypto_pcomp_tfm(tfm));
index ab2cbb51c6aaccde0ec2f97af343383c6f360dff..35da507411a0ae90b112538152fbf5569fb65b45 100644 (file)
@@ -54,6 +54,9 @@ config ACPI_GENERIC_GSI
 config ACPI_SYSTEM_POWER_STATES_SUPPORT
        bool
 
+config ACPI_CCA_REQUIRED
+       bool
+
 config ACPI_SLEEP
        bool
        depends on SUSPEND || HIBERNATION
@@ -62,7 +65,7 @@ config ACPI_SLEEP
 
 config ACPI_PROCFS_POWER
        bool "Deprecated power /proc/acpi directories"
-       depends on PROC_FS
+       depends on X86 && PROC_FS
        help
          For backwards compatibility, this option allows
           deprecated power /proc/acpi/ directories to exist, even when
index 8a063e276530e244b4020a1e6a6ef6ebec60920e..73d840bef455c9e512eb04539f5a5668c9ec9182 100644 (file)
@@ -52,9 +52,6 @@ acpi-$(CONFIG_X86)            += acpi_cmos_rtc.o
 acpi-$(CONFIG_DEBUG_FS)                += debugfs.o
 acpi-$(CONFIG_ACPI_NUMA)       += numa.o
 acpi-$(CONFIG_ACPI_PROCFS_POWER) += cm_sbs.o
-ifdef CONFIG_ACPI_VIDEO
-acpi-y                         += video_detect.o
-endif
 acpi-y                         += acpi_lpat.o
 acpi-$(CONFIG_ACPI_GENERIC_GSI) += gsi.o
 
@@ -95,3 +92,5 @@ obj-$(CONFIG_ACPI_EXTLOG)     += acpi_extlog.o
 obj-$(CONFIG_PMIC_OPREGION)    += pmic/intel_pmic.o
 obj-$(CONFIG_CRC_PMIC_OPREGION) += pmic/intel_pmic_crc.o
 obj-$(CONFIG_XPOWER_PMIC_OPREGION) += pmic/intel_pmic_xpower.o
+
+video-objs                     += acpi_video.o video_detect.o
index bbcc2b5a70d4aa53a9b014c871241e93aaab66e3..9b5354a2cd0897f1626b302f88722a78d0bd4f96 100644 (file)
@@ -308,7 +308,7 @@ static int thinkpad_e530_quirk(const struct dmi_system_id *d)
        return 0;
 }
 
-static struct dmi_system_id ac_dmi_table[] = {
+static const struct dmi_system_id ac_dmi_table[] = {
        {
        .callback = thinkpad_e530_quirk,
        .ident = "thinkpad e530",
index 37fb1904760396751f27778e819d90c0fd9af83b..569ee090343fee43c318f7a1bafee0eadb65e1f6 100644 (file)
@@ -129,50 +129,50 @@ static void byt_i2c_setup(struct lpss_private_data *pdata)
        writel(0, pdata->mmio_base + LPSS_I2C_ENABLE);
 }
 
-static struct lpss_device_desc lpt_dev_desc = {
+static const struct lpss_device_desc lpt_dev_desc = {
        .flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_LTR,
        .prv_offset = 0x800,
 };
 
-static struct lpss_device_desc lpt_i2c_dev_desc = {
+static const struct lpss_device_desc lpt_i2c_dev_desc = {
        .flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_LTR,
        .prv_offset = 0x800,
 };
 
-static struct lpss_device_desc lpt_uart_dev_desc = {
+static const struct lpss_device_desc lpt_uart_dev_desc = {
        .flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_LTR,
        .clk_con_id = "baudclk",
        .prv_offset = 0x800,
        .setup = lpss_uart_setup,
 };
 
-static struct lpss_device_desc lpt_sdio_dev_desc = {
+static const struct lpss_device_desc lpt_sdio_dev_desc = {
        .flags = LPSS_LTR,
        .prv_offset = 0x1000,
        .prv_size_override = 0x1018,
 };
 
-static struct lpss_device_desc byt_pwm_dev_desc = {
+static const struct lpss_device_desc byt_pwm_dev_desc = {
        .flags = LPSS_SAVE_CTX,
 };
 
-static struct lpss_device_desc byt_uart_dev_desc = {
+static const struct lpss_device_desc byt_uart_dev_desc = {
        .flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_SAVE_CTX,
        .clk_con_id = "baudclk",
        .prv_offset = 0x800,
        .setup = lpss_uart_setup,
 };
 
-static struct lpss_device_desc byt_spi_dev_desc = {
+static const struct lpss_device_desc byt_spi_dev_desc = {
        .flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_SAVE_CTX,
        .prv_offset = 0x400,
 };
 
-static struct lpss_device_desc byt_sdio_dev_desc = {
+static const struct lpss_device_desc byt_sdio_dev_desc = {
        .flags = LPSS_CLK,
 };
 
-static struct lpss_device_desc byt_i2c_dev_desc = {
+static const struct lpss_device_desc byt_i2c_dev_desc = {
        .flags = LPSS_CLK | LPSS_SAVE_CTX,
        .prv_offset = 0x800,
        .setup = byt_i2c_setup,
@@ -323,14 +323,14 @@ out:
 static int acpi_lpss_create_device(struct acpi_device *adev,
                                   const struct acpi_device_id *id)
 {
-       struct lpss_device_desc *dev_desc;
+       const struct lpss_device_desc *dev_desc;
        struct lpss_private_data *pdata;
        struct resource_entry *rentry;
        struct list_head resource_list;
        struct platform_device *pdev;
        int ret;
 
-       dev_desc = (struct lpss_device_desc *)id->driver_data;
+       dev_desc = (const struct lpss_device_desc *)id->driver_data;
        if (!dev_desc) {
                pdev = acpi_create_platform_device(adev);
                return IS_ERR_OR_NULL(pdev) ? PTR_ERR(pdev) : 1;
index 4bf75597f732e90def62ba607590e7eb8e451da4..06a67d5f28461a0ea5a29f7791f7865e45979082 100644 (file)
@@ -103,7 +103,7 @@ struct platform_device *acpi_create_platform_device(struct acpi_device *adev)
        pdevinfo.res = resources;
        pdevinfo.num_res = count;
        pdevinfo.fwnode = acpi_fwnode_handle(adev);
-       pdevinfo.dma_mask = DMA_BIT_MASK(32);
+       pdevinfo.dma_mask = acpi_check_dma(adev, NULL) ? DMA_BIT_MASK(32) : 0;
        pdev = platform_device_register_full(&pdevinfo);
        if (IS_ERR(pdev))
                dev_err(&adev->dev, "platform device creation failed: %ld\n",
index 58f335ca2e75f9cc3ca47eb7ef2140fb010ee7e4..92a5f738e3707657f48dc59fdc3280866fad5746 100644 (file)
@@ -170,7 +170,7 @@ static int acpi_processor_hotadd_init(struct acpi_processor *pr)
        acpi_status status;
        int ret;
 
-       if (pr->phys_id == PHYS_CPUID_INVALID)
+       if (invalid_phys_cpuid(pr->phys_id))
                return -ENODEV;
 
        status = acpi_evaluate_integer(pr->handle, "_STA", NULL, &sta);
@@ -215,8 +215,7 @@ static int acpi_processor_get_info(struct acpi_device *device)
        union acpi_object object = { 0 };
        struct acpi_buffer buffer = { sizeof(union acpi_object), &object };
        struct acpi_processor *pr = acpi_driver_data(device);
-       phys_cpuid_t phys_id;
-       int cpu_index, device_declaration = 0;
+       int device_declaration = 0;
        acpi_status status = AE_OK;
        static int cpu0_initialized;
        unsigned long long value;
@@ -263,29 +262,28 @@ static int acpi_processor_get_info(struct acpi_device *device)
                pr->acpi_id = value;
        }
 
-       phys_id = acpi_get_phys_id(pr->handle, device_declaration, pr->acpi_id);
-       if (phys_id == PHYS_CPUID_INVALID)
+       pr->phys_id = acpi_get_phys_id(pr->handle, device_declaration,
+                                       pr->acpi_id);
+       if (invalid_phys_cpuid(pr->phys_id))
                acpi_handle_debug(pr->handle, "failed to get CPU physical ID.\n");
-       pr->phys_id = phys_id;
 
-       cpu_index = acpi_map_cpuid(pr->phys_id, pr->acpi_id);
+       pr->id = acpi_map_cpuid(pr->phys_id, pr->acpi_id);
        if (!cpu0_initialized && !acpi_has_cpu_in_madt()) {
                cpu0_initialized = 1;
                /*
                 * Handle UP system running SMP kernel, with no CPU
                 * entry in MADT
                 */
-               if ((cpu_index == -1) && (num_online_cpus() == 1))
-                       cpu_index = 0;
+               if (invalid_logical_cpuid(pr->id) && (num_online_cpus() == 1))
+                       pr->id = 0;
        }
-       pr->id = cpu_index;
 
        /*
         *  Extra Processor objects may be enumerated on MP systems with
         *  less than the max # of CPUs. They should be ignored _iff
         *  they are physically not present.
         */
-       if (pr->id == -1) {
+       if (invalid_logical_cpuid(pr->id)) {
                int ret = acpi_processor_hotadd_init(pr);
                if (ret)
                        return ret;
similarity index 88%
rename from drivers/acpi/video.c
rename to drivers/acpi/acpi_video.c
index cc79d3fedfb2a33aa8ca273865c1bf84ec2b190b..8c2fe2f2f9fdcae010f7df99e172ade584885847 100644 (file)
@@ -43,7 +43,7 @@
 #include <acpi/video.h>
 #include <asm/uaccess.h>
 
-#include "internal.h"
+#define PREFIX "ACPI: "
 
 #define ACPI_VIDEO_BUS_NAME            "Video Bus"
 #define ACPI_VIDEO_DEVICE_NAME         "Video Device"
@@ -78,26 +78,17 @@ module_param(brightness_switch_enabled, bool, 0644);
 static bool allow_duplicates;
 module_param(allow_duplicates, bool, 0644);
 
-/*
- * For Windows 8 systems: used to decide if video module
- * should skip registering backlight interface of its own.
- */
-enum {
-       NATIVE_BACKLIGHT_NOT_SET = -1,
-       NATIVE_BACKLIGHT_OFF,
-       NATIVE_BACKLIGHT_ON,
-};
-
-static int use_native_backlight_param = NATIVE_BACKLIGHT_NOT_SET;
-module_param_named(use_native_backlight, use_native_backlight_param, int, 0444);
-static int use_native_backlight_dmi = NATIVE_BACKLIGHT_NOT_SET;
+static int disable_backlight_sysfs_if = -1;
+module_param(disable_backlight_sysfs_if, int, 0444);
 
 static int register_count;
+static DEFINE_MUTEX(register_count_mutex);
 static struct mutex video_list_lock;
 static struct list_head video_bus_head;
 static int acpi_video_bus_add(struct acpi_device *device);
 static int acpi_video_bus_remove(struct acpi_device *device);
 static void acpi_video_bus_notify(struct acpi_device *device, u32 event);
+void acpi_video_detect_exit(void);
 
 static const struct acpi_device_id video_device_ids[] = {
        {ACPI_VIDEO_HID, 0},
@@ -157,7 +148,6 @@ struct acpi_video_enumerated_device {
 struct acpi_video_bus {
        struct acpi_device *device;
        bool backlight_registered;
-       bool backlight_notifier_registered;
        u8 dos_setting;
        struct acpi_video_enumerated_device *attached_array;
        u8 attached_count;
@@ -170,7 +160,6 @@ struct acpi_video_bus {
        struct input_dev *input;
        char phys[32];  /* for input device */
        struct notifier_block pm_nb;
-       struct notifier_block backlight_nb;
 };
 
 struct acpi_video_device_flags {
@@ -241,24 +230,6 @@ static int acpi_video_get_next_level(struct acpi_video_device *device,
                                     u32 level_current, u32 event);
 static void acpi_video_switch_brightness(struct work_struct *work);
 
-static bool acpi_video_use_native_backlight(void)
-{
-       if (use_native_backlight_param != NATIVE_BACKLIGHT_NOT_SET)
-               return use_native_backlight_param;
-       else if (use_native_backlight_dmi != NATIVE_BACKLIGHT_NOT_SET)
-               return use_native_backlight_dmi;
-       return acpi_osi_is_win8();
-}
-
-bool acpi_video_verify_backlight_support(void)
-{
-       if (acpi_video_use_native_backlight() &&
-           backlight_device_registered(BACKLIGHT_RAW))
-               return false;
-       return acpi_video_backlight_support();
-}
-EXPORT_SYMBOL_GPL(acpi_video_verify_backlight_support);
-
 /* backlight device sysfs support */
 static int acpi_video_get_brightness(struct backlight_device *bd)
 {
@@ -413,25 +384,21 @@ acpi_video_device_lcd_set_level(struct acpi_video_device *device, int level)
  */
 
 static int bqc_offset_aml_bug_workaround;
-static int __init video_set_bqc_offset(const struct dmi_system_id *d)
+static int video_set_bqc_offset(const struct dmi_system_id *d)
 {
        bqc_offset_aml_bug_workaround = 9;
        return 0;
 }
 
-static int __init video_disable_native_backlight(const struct dmi_system_id *d)
-{
-       use_native_backlight_dmi = NATIVE_BACKLIGHT_OFF;
-       return 0;
-}
-
-static int __init video_enable_native_backlight(const struct dmi_system_id *d)
+static int video_disable_backlight_sysfs_if(
+       const struct dmi_system_id *d)
 {
-       use_native_backlight_dmi = NATIVE_BACKLIGHT_ON;
+       if (disable_backlight_sysfs_if == -1)
+               disable_backlight_sysfs_if = 1;
        return 0;
 }
 
-static struct dmi_system_id video_dmi_table[] __initdata = {
+static struct dmi_system_id video_dmi_table[] = {
        /*
         * Broken _BQC workaround http://bugzilla.kernel.org/show_bug.cgi?id=13121
         */
@@ -477,110 +444,19 @@ static struct dmi_system_id video_dmi_table[] __initdata = {
        },
 
        /*
-        * These models have a working acpi_video backlight control, and using
-        * native backlight causes a regression where backlight does not work
-        * when userspace is not handling brightness key events. Disable
-        * native_backlight on these to fix this:
-        * https://bugzilla.kernel.org/show_bug.cgi?id=81691
+        * Some machines have a broken acpi-video interface for brightness
+        * control, but still need an acpi_video_device_lcd_set_level() call
+        * on resume to turn the backlight power on.  We Enable backlight
+        * control on these systems, but do not register a backlight sysfs
+        * as brightness control does not work.
         */
        {
-        .callback = video_disable_native_backlight,
-        .ident = "ThinkPad T420",
-        .matches = {
-               DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
-               DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad T420"),
-               },
-       },
-       {
-        .callback = video_disable_native_backlight,
-        .ident = "ThinkPad T520",
-        .matches = {
-               DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
-               DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad T520"),
-               },
-       },
-       {
-        .callback = video_disable_native_backlight,
-        .ident = "ThinkPad X201s",
-        .matches = {
-               DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
-               DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad X201s"),
-               },
-       },
-
-       /* The native backlight controls do not work on some older machines */
-       {
-        /* https://bugs.freedesktop.org/show_bug.cgi?id=81515 */
-        .callback = video_disable_native_backlight,
-        .ident = "HP ENVY 15 Notebook",
-        .matches = {
-               DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
-               DMI_MATCH(DMI_PRODUCT_NAME, "HP ENVY 15 Notebook PC"),
-               },
-       },
-
-       {
-        .callback = video_disable_native_backlight,
-        .ident = "SAMSUNG 870Z5E/880Z5E/680Z5E",
-        .matches = {
-               DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
-               DMI_MATCH(DMI_PRODUCT_NAME, "870Z5E/880Z5E/680Z5E"),
-               },
-       },
-       {
-        .callback = video_disable_native_backlight,
-        .ident = "SAMSUNG 370R4E/370R4V/370R5E/3570RE/370R5V",
-        .matches = {
-               DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
-               DMI_MATCH(DMI_PRODUCT_NAME, "370R4E/370R4V/370R5E/3570RE/370R5V"),
-               },
-       },
-       {
-        /* https://bugzilla.redhat.com/show_bug.cgi?id=1186097 */
-        .callback = video_disable_native_backlight,
-        .ident = "SAMSUNG 3570R/370R/470R/450R/510R/4450RV",
-        .matches = {
-               DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
-               DMI_MATCH(DMI_PRODUCT_NAME, "3570R/370R/470R/450R/510R/4450RV"),
-               },
-       },
-       {
-        /* https://bugzilla.redhat.com/show_bug.cgi?id=1094948 */
-        .callback = video_disable_native_backlight,
-        .ident = "SAMSUNG 730U3E/740U3E",
-        .matches = {
-               DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
-               DMI_MATCH(DMI_PRODUCT_NAME, "730U3E/740U3E"),
-               },
-       },
-       {
-        /* https://bugs.freedesktop.org/show_bug.cgi?id=87286 */
-        .callback = video_disable_native_backlight,
-        .ident = "SAMSUNG 900X3C/900X3D/900X3E/900X4C/900X4D",
-        .matches = {
-               DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
-               DMI_MATCH(DMI_PRODUCT_NAME, "900X3C/900X3D/900X3E/900X4C/900X4D"),
-               },
-       },
-
-       {
-        /* https://bugzilla.redhat.com/show_bug.cgi?id=1163574 */
-        .callback = video_disable_native_backlight,
-        .ident = "Dell XPS15 L521X",
+        /* https://bugs.freedesktop.org/show_bug.cgi?id=82634 */
+        .callback = video_disable_backlight_sysfs_if,
+        .ident = "Toshiba Portege R830",
         .matches = {
-               DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
-               DMI_MATCH(DMI_PRODUCT_NAME, "XPS L521X"),
-               },
-       },
-
-       /* Non win8 machines which need native backlight nevertheless */
-       {
-        /* https://bugzilla.redhat.com/show_bug.cgi?id=1187004 */
-        .callback = video_enable_native_backlight,
-        .ident = "Lenovo Ideapad Z570",
-        .matches = {
-               DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
-               DMI_MATCH(DMI_PRODUCT_NAME, "102434U"),
+               DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
+               DMI_MATCH(DMI_PRODUCT_NAME, "PORTEGE R830"),
                },
        },
        {}
@@ -1382,7 +1258,7 @@ acpi_video_switch_brightness(struct work_struct *work)
        int result = -EINVAL;
 
        /* no warning message if acpi_backlight=vendor or a quirk is used */
-       if (!acpi_video_verify_backlight_support())
+       if (!device->backlight)
                return;
 
        if (!device->brightness)
@@ -1657,8 +1533,9 @@ static int acpi_video_resume(struct notifier_block *nb,
 
        for (i = 0; i < video->attached_count; i++) {
                video_device = video->attached_array[i].bind_info;
-               if (video_device && video_device->backlight)
-                       acpi_video_set_brightness(video_device->backlight);
+               if (video_device && video_device->brightness)
+                       acpi_video_device_lcd_set_level(video_device,
+                                       video_device->brightness->curr);
        }
 
        return NOTIFY_OK;
@@ -1707,6 +1584,10 @@ static void acpi_video_dev_register_backlight(struct acpi_video_device *device)
        result = acpi_video_init_brightness(device);
        if (result)
                return;
+
+       if (disable_backlight_sysfs_if > 0)
+               return;
+
        name = kasprintf(GFP_KERNEL, "acpi_video%d", count);
        if (!name)
                return;
@@ -1729,8 +1610,10 @@ static void acpi_video_dev_register_backlight(struct acpi_video_device *device)
                                                      &acpi_backlight_ops,
                                                      &props);
        kfree(name);
-       if (IS_ERR(device->backlight))
+       if (IS_ERR(device->backlight)) {
+               device->backlight = NULL;
                return;
+       }
 
        /*
         * Save current brightness level in case we have to restore it
@@ -1787,7 +1670,7 @@ static int acpi_video_bus_register_backlight(struct acpi_video_bus *video)
 
        acpi_video_run_bcl_for_osi(video);
 
-       if (!acpi_video_verify_backlight_support())
+       if (acpi_video_get_backlight_type() != acpi_backlight_video)
                return 0;
 
        mutex_lock(&video->device_list_lock);
@@ -1931,56 +1814,6 @@ static void acpi_video_bus_remove_notify_handler(struct acpi_video_bus *video)
        video->input = NULL;
 }
 
-static int acpi_video_backlight_notify(struct notifier_block *nb,
-                                       unsigned long val, void *bd)
-{
-       struct backlight_device *backlight = bd;
-       struct acpi_video_bus *video;
-
-       /* acpi_video_verify_backlight_support only cares about raw devices */
-       if (backlight->props.type != BACKLIGHT_RAW)
-               return NOTIFY_DONE;
-
-       video = container_of(nb, struct acpi_video_bus, backlight_nb);
-
-       switch (val) {
-       case BACKLIGHT_REGISTERED:
-               if (!acpi_video_verify_backlight_support())
-                       acpi_video_bus_unregister_backlight(video);
-               break;
-       case BACKLIGHT_UNREGISTERED:
-               acpi_video_bus_register_backlight(video);
-               break;
-       }
-
-       return NOTIFY_OK;
-}
-
-static int acpi_video_bus_add_backlight_notify_handler(
-                                               struct acpi_video_bus *video)
-{
-       int error;
-
-       video->backlight_nb.notifier_call = acpi_video_backlight_notify;
-       video->backlight_nb.priority = 0;
-       error = backlight_register_notifier(&video->backlight_nb);
-       if (error == 0)
-               video->backlight_notifier_registered = true;
-
-       return error;
-}
-
-static int acpi_video_bus_remove_backlight_notify_handler(
-                                               struct acpi_video_bus *video)
-{
-       if (!video->backlight_notifier_registered)
-               return 0;
-
-       video->backlight_notifier_registered = false;
-
-       return backlight_unregister_notifier(&video->backlight_nb);
-}
-
 static int acpi_video_bus_put_devices(struct acpi_video_bus *video)
 {
        struct acpi_video_device *dev, *next;
@@ -2062,7 +1895,6 @@ static int acpi_video_bus_add(struct acpi_device *device)
 
        acpi_video_bus_register_backlight(video);
        acpi_video_bus_add_notify_handler(video);
-       acpi_video_bus_add_backlight_notify_handler(video);
 
        return 0;
 
@@ -2086,7 +1918,6 @@ static int acpi_video_bus_remove(struct acpi_device *device)
 
        video = acpi_driver_data(device);
 
-       acpi_video_bus_remove_backlight_notify_handler(video);
        acpi_video_bus_remove_notify_handler(video);
        acpi_video_bus_unregister_backlight(video);
        acpi_video_bus_put_devices(video);
@@ -2134,22 +1965,25 @@ static int __init intel_opregion_present(void)
 
 int acpi_video_register(void)
 {
-       int ret;
+       int ret = 0;
 
+       mutex_lock(&register_count_mutex);
        if (register_count) {
                /*
                 * if the function of acpi_video_register is already called,
                 * don't register the acpi_vide_bus again and return no error.
                 */
-               return 0;
+               goto leave;
        }
 
        mutex_init(&video_list_lock);
        INIT_LIST_HEAD(&video_bus_head);
 
+       dmi_check_system(video_dmi_table);
+
        ret = acpi_bus_register_driver(&acpi_video_bus);
        if (ret)
-               return ret;
+               goto leave;
 
        /*
         * When the acpi_video_bus is loaded successfully, increase
@@ -2157,24 +1991,20 @@ int acpi_video_register(void)
         */
        register_count = 1;
 
-       return 0;
+leave:
+       mutex_unlock(&register_count_mutex);
+       return ret;
 }
 EXPORT_SYMBOL(acpi_video_register);
 
 void acpi_video_unregister(void)
 {
-       if (!register_count) {
-               /*
-                * If the acpi video bus is already unloaded, don't
-                * unload it again and return directly.
-                */
-               return;
+       mutex_lock(&register_count_mutex);
+       if (register_count) {
+               acpi_bus_unregister_driver(&acpi_video_bus);
+               register_count = 0;
        }
-       acpi_bus_unregister_driver(&acpi_video_bus);
-
-       register_count = 0;
-
-       return;
+       mutex_unlock(&register_count_mutex);
 }
 EXPORT_SYMBOL(acpi_video_unregister);
 
@@ -2182,15 +2012,15 @@ void acpi_video_unregister_backlight(void)
 {
        struct acpi_video_bus *video;
 
-       if (!register_count)
-               return;
-
-       mutex_lock(&video_list_lock);
-       list_for_each_entry(video, &video_bus_head, entry)
-               acpi_video_bus_unregister_backlight(video);
-       mutex_unlock(&video_list_lock);
+       mutex_lock(&register_count_mutex);
+       if (register_count) {
+               mutex_lock(&video_list_lock);
+               list_for_each_entry(video, &video_bus_head, entry)
+                       acpi_video_bus_unregister_backlight(video);
+               mutex_unlock(&video_list_lock);
+       }
+       mutex_unlock(&register_count_mutex);
 }
-EXPORT_SYMBOL(acpi_video_unregister_backlight);
 
 /*
  * This is kind of nasty. Hardware using Intel chipsets may require
@@ -2212,8 +2042,6 @@ static int __init acpi_video_init(void)
        if (acpi_disabled)
                return 0;
 
-       dmi_check_system(video_dmi_table);
-
        if (intel_opregion_present())
                return 0;
 
@@ -2222,6 +2050,7 @@ static int __init acpi_video_init(void)
 
 static void __exit acpi_video_exit(void)
 {
+       acpi_video_detect_exit();
        acpi_video_unregister();
 
        return;
index 4169bb87a99690c98dc5d5c81d3eb62925e98370..43685dd36c77c78650ea174b986f1e5aa2cb7618 100644 (file)
@@ -231,7 +231,9 @@ void acpi_db_open_debug_file(char *name);
 acpi_status acpi_db_load_acpi_table(char *filename);
 
 acpi_status
-acpi_db_get_table_from_file(char *filename, struct acpi_table_header **table);
+acpi_db_get_table_from_file(char *filename,
+                           struct acpi_table_header **table,
+                           u8 must_be_aml_table);
 
 /*
  * dbhistry - debugger HISTORY command
index 87b27521fcacb7d5a6173fc02d0ef7ef0a35eb5c..ffdb956391f614786ba2580fede7a7ce529b0897 100644 (file)
@@ -352,11 +352,21 @@ struct acpi_package_info3 {
        u16 reserved;
 };
 
+struct acpi_package_info4 {
+       u8 type;
+       u8 object_type1;
+       u8 count1;
+       u8 sub_object_types;
+       u8 pkg_count;
+       u16 reserved;
+};
+
 union acpi_predefined_info {
        struct acpi_name_info info;
        struct acpi_package_info ret_info;
        struct acpi_package_info2 ret_info2;
        struct acpi_package_info3 ret_info3;
+       struct acpi_package_info4 ret_info4;
 };
 
 /* Reset to default packing */
@@ -1165,4 +1175,9 @@ struct ah_uuid {
        char *string;
 };
 
+struct ah_table {
+       char *signature;
+       char *description;
+};
+
 #endif                         /* __ACLOCAL_H__ */
index 74a390c6db16997785d7a02a74a7f97f4ce831f0..0cdd2fce493a9c15e34139b633b54ac8e531a151 100644 (file)
@@ -70,6 +70,9 @@
  *
  *****************************************************************************/
 
+extern const u8 acpi_gbl_short_op_index[];
+extern const u8 acpi_gbl_long_op_index[];
+
 /*
  * psxface - Parser external interfaces
  */
index a972d11c97c97e26504a71e82302a93b0890b096..b9474b529fcba090b5ee85747311c4b984e66b0d 100644 (file)
  *      count = 0 (optional)
  *      (Used for _DLM)
  *
+ * ACPI_PTYPE2_VAR_VAR: Variable number of subpackages, each of either a
+ *      constant or variable length. The subpackages are preceded by a
+ *      constant number of objects.
+ *      (Used for _LPI, _RDI)
+ *
  * ACPI_PTYPE2_UUID_PAIR: Each subpackage is preceded by a UUID Buffer. The UUID
  *      defines the format of the package. Zero-length parent package is
  *      allowed.
@@ -123,7 +128,8 @@ enum acpi_return_package_types {
        ACPI_PTYPE2_MIN = 8,
        ACPI_PTYPE2_REV_FIXED = 9,
        ACPI_PTYPE2_FIX_VAR = 10,
-       ACPI_PTYPE2_UUID_PAIR = 11
+       ACPI_PTYPE2_VAR_VAR = 11,
+       ACPI_PTYPE2_UUID_PAIR = 12
 };
 
 /* Support macros for users of the predefined info table */
@@ -172,7 +178,7 @@ enum acpi_return_package_types {
  * These are the names that can actually be evaluated via acpi_evaluate_object.
  * Not present in this table are the following:
  *
- *      1) Predefined/Reserved names that are never evaluated via
+ *      1) Predefined/Reserved names that are not usually evaluated via
  *         acpi_evaluate_object:
  *              _Lxx and _Exx GPE methods
  *              _Qxx EC methods
@@ -361,6 +367,9 @@ const union acpi_predefined_info acpi_gbl_predefined_methods[] = {
          METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Fixed-length (4 Int) */
        PACKAGE_INFO(ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 4, 0, 0, 0),
 
+       {{"_BTH", METHOD_1ARGS(ACPI_TYPE_INTEGER),      /* ACPI 6.0 */
+         METHOD_NO_RETURN_VALUE}},
+
        {{"_BTM", METHOD_1ARGS(ACPI_TYPE_INTEGER),
          METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
 
@@ -390,6 +399,9 @@ const union acpi_predefined_info acpi_gbl_predefined_methods[] = {
        PACKAGE_INFO(ACPI_PTYPE1_VAR, ACPI_RTYPE_INTEGER | ACPI_RTYPE_BUFFER, 0,
                     0, 0, 0),
 
+       {{"_CR3", METHOD_0ARGS, /* ACPI 6.0 */
+         METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
        {{"_CRS", METHOD_0ARGS,
          METHOD_RETURNS(ACPI_RTYPE_BUFFER)}},
 
@@ -445,7 +457,7 @@ const union acpi_predefined_info acpi_gbl_predefined_methods[] = {
        {{"_DOS", METHOD_1ARGS(ACPI_TYPE_INTEGER),
          METHOD_NO_RETURN_VALUE}},
 
-       {{"_DSD", METHOD_0ARGS,
+       {{"_DSD", METHOD_0ARGS, /* ACPI 6.0 */
          METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Pkgs) each: 1 Buf, 1 Pkg */
        PACKAGE_INFO(ACPI_PTYPE2_UUID_PAIR, ACPI_RTYPE_BUFFER, 1,
                     ACPI_RTYPE_PACKAGE, 1, 0),
@@ -604,6 +616,12 @@ const union acpi_predefined_info acpi_gbl_predefined_methods[] = {
          METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (1 Int(rev), n Pkg (2 Int) */
        PACKAGE_INFO(ACPI_PTYPE2_REV_FIXED, ACPI_RTYPE_INTEGER, 2, 0, 0, 0),
 
+       {{"_LPI", METHOD_0ARGS, /* ACPI 6.0 */
+         METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (3 Int, n Pkg (10 Int/Buf) */
+       PACKAGE_INFO(ACPI_PTYPE2_VAR_VAR, ACPI_RTYPE_INTEGER, 3,
+                    ACPI_RTYPE_INTEGER | ACPI_RTYPE_BUFFER | ACPI_RTYPE_STRING,
+                    10, 0),
+
        {{"_MAT", METHOD_0ARGS,
          METHOD_RETURNS(ACPI_RTYPE_BUFFER)}},
 
@@ -624,6 +642,9 @@ const union acpi_predefined_info acpi_gbl_predefined_methods[] = {
                       ACPI_TYPE_INTEGER),
          METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
 
+       {{"_MTL", METHOD_0ARGS, /* ACPI 6.0 */
+         METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
        {{"_NTT", METHOD_0ARGS,
          METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
 
@@ -716,6 +737,10 @@ const union acpi_predefined_info acpi_gbl_predefined_methods[] = {
          METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Refs) */
        PACKAGE_INFO(ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0, 0, 0, 0),
 
+       {{"_PRR", METHOD_0ARGS, /* ACPI 6.0 */
+         METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Fixed-length (1 Ref) */
+       PACKAGE_INFO(ACPI_PTYPE1_FIXED, ACPI_RTYPE_REFERENCE, 1, 0, 0, 0),
+
        {{"_PRS", METHOD_0ARGS,
          METHOD_RETURNS(ACPI_RTYPE_BUFFER)}},
 
@@ -796,6 +821,11 @@ const union acpi_predefined_info acpi_gbl_predefined_methods[] = {
        {{"_PXM", METHOD_0ARGS,
          METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
 
+       {{"_RDI", METHOD_0ARGS, /* ACPI 6.0 */
+         METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (1 Int, n Pkg (m Ref)) */
+       PACKAGE_INFO(ACPI_PTYPE2_VAR_VAR, ACPI_RTYPE_INTEGER, 1,
+                    ACPI_RTYPE_REFERENCE, 0, 0),
+
        {{"_REG", METHOD_2ARGS(ACPI_TYPE_INTEGER, ACPI_TYPE_INTEGER),
          METHOD_NO_RETURN_VALUE}},
 
@@ -808,6 +838,9 @@ const union acpi_predefined_info acpi_gbl_predefined_methods[] = {
        {{"_ROM", METHOD_2ARGS(ACPI_TYPE_INTEGER, ACPI_TYPE_INTEGER),
          METHOD_RETURNS(ACPI_RTYPE_BUFFER)}},
 
+       {{"_RST", METHOD_0ARGS, /* ACPI 6.0 */
+         METHOD_NO_RETURN_VALUE}},
+
        {{"_RTV", METHOD_0ARGS,
          METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
 
@@ -935,6 +968,9 @@ const union acpi_predefined_info acpi_gbl_predefined_methods[] = {
        {{"_TDL", METHOD_0ARGS,
          METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
 
+       {{"_TFP", METHOD_0ARGS, /* ACPI 6.0 */
+         METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
+
        {{"_TIP", METHOD_1ARGS(ACPI_TYPE_INTEGER),
          METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
 
@@ -959,6 +995,9 @@ const union acpi_predefined_info acpi_gbl_predefined_methods[] = {
          METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Pkgs) each 5 Int with count */
        PACKAGE_INFO(ACPI_PTYPE2_COUNT, ACPI_RTYPE_INTEGER, 5, 0, 0, 0),
 
+       {{"_TSN", METHOD_0ARGS, /* ACPI 6.0 */
+         METHOD_RETURNS(ACPI_RTYPE_REFERENCE)}},
+
        {{"_TSP", METHOD_0ARGS,
          METHOD_RETURNS(ACPI_RTYPE_INTEGER)}},
 
index 2b3c5bd222f17be2069b3f58e0818637c4f46f40..d49f5c7a20d90197ece8b625dd2fcd22fca4c4b1 100644 (file)
@@ -251,7 +251,7 @@ extern const u8 _acpi_ctype[];
 #define _ACPI_DI     0x04      /* '0'-'9' */
 #define _ACPI_LO     0x02      /* 'a'-'z' */
 #define _ACPI_PU     0x10      /* punctuation */
-#define _ACPI_SP     0x08      /* space */
+#define _ACPI_SP     0x08      /* space, tab, CR, LF, VT, FF */
 #define _ACPI_UP     0x01      /* 'A'-'Z' */
 #define _ACPI_XD     0x80      /* '0'-'9', 'A'-'F', 'a'-'f' */
 
index d72565a3c646ddb6b4c819019da46b3320f19024..85bb951430d9640180268eab6d108f887eea1d0d 100644 (file)
@@ -116,6 +116,7 @@ acpi_ds_auto_serialize_method(struct acpi_namespace_node *node,
        walk_state =
            acpi_ds_create_walk_state(node->owner_id, NULL, NULL, NULL);
        if (!walk_state) {
+               acpi_ps_free_op(op);
                return_ACPI_STATUS(AE_NO_MEMORY);
        }
 
@@ -125,6 +126,7 @@ acpi_ds_auto_serialize_method(struct acpi_namespace_node *node,
                                  obj_desc->method.aml_length, NULL, 0);
        if (ACPI_FAILURE(status)) {
                acpi_ds_delete_walk_state(walk_state);
+               acpi_ps_free_op(op);
                return_ACPI_STATUS(status);
        }
 
@@ -133,9 +135,6 @@ acpi_ds_auto_serialize_method(struct acpi_namespace_node *node,
        /* Parse the method, scan for creation of named objects */
 
        status = acpi_ps_parse_aml(walk_state);
-       if (ACPI_FAILURE(status)) {
-               return_ACPI_STATUS(status);
-       }
 
        acpi_ps_delete_parse_tree(op);
        return_ACPI_STATUS(status);
index c5214dec49880cd0678943e15ece6c6211bf3291..f785ea7883565d54d35228e2a725a6815327047a 100644 (file)
@@ -123,7 +123,7 @@ acpi_hw_derive_pci_id(struct acpi_pci_id *pci_id,
                      acpi_handle root_pci_device, acpi_handle pci_region)
 {
        acpi_status status;
-       struct acpi_pci_device *list_head = NULL;
+       struct acpi_pci_device *list_head;
 
        ACPI_FUNCTION_TRACE(hw_derive_pci_id);
 
@@ -177,13 +177,13 @@ acpi_hw_build_pci_list(acpi_handle root_pci_device,
        acpi_handle parent_device;
        acpi_status status;
        struct acpi_pci_device *list_element;
-       struct acpi_pci_device *list_head = NULL;
 
        /*
         * Ascend namespace branch until the root_pci_device is reached, building
         * a list of device nodes. Loop will exit when either the PCI device is
         * found, or the root of the namespace is reached.
         */
+       *return_list_head = NULL;
        current_device = pci_region;
        while (1) {
                status = acpi_get_parent(current_device, &parent_device);
@@ -198,7 +198,6 @@ acpi_hw_build_pci_list(acpi_handle root_pci_device,
                /* Finished when we reach the PCI root device (PNP0A03 or PNP0A08) */
 
                if (parent_device == root_pci_device) {
-                       *return_list_head = list_head;
                        return (AE_OK);
                }
 
@@ -213,9 +212,9 @@ acpi_hw_build_pci_list(acpi_handle root_pci_device,
 
                /* Put new element at the head of the list */
 
-               list_element->next = list_head;
+               list_element->next = *return_list_head;
                list_element->device = parent_device;
-               list_head = list_element;
+               *return_list_head = list_element;
 
                current_device = parent_device;
        }
index 8b79958b7aca35609d54f762d191dcc386cf04d0..9bb251932b45c88a7deb186d9e8b2e3abadecee4 100644 (file)
@@ -316,6 +316,13 @@ acpi_ns_check_package(struct acpi_evaluate_info *info,
                    acpi_ns_check_package_list(info, package, elements, count);
                break;
 
+       case ACPI_PTYPE2_VAR_VAR:
+               /*
+                * Returns a variable list of packages, each with a variable list
+                * of objects.
+                */
+               break;
+
        case ACPI_PTYPE2_UUID_PAIR:
 
                /* The package must contain pairs of (UUID + type) */
@@ -487,6 +494,12 @@ acpi_ns_check_package_list(struct acpi_evaluate_info *info,
                        }
                        break;
 
+               case ACPI_PTYPE2_VAR_VAR:
+                       /*
+                        * Each subpackage has a fixed or variable number of elements
+                        */
+                       break;
+
                case ACPI_PTYPE2_FIXED:
 
                        /* Each subpackage has a fixed length */
index 151fcd95ba849e8fd6a5fe5ccdd29e418bf043bd..77d8103d0094287c27ca7362a3a072ab9e458b2e 100644 (file)
@@ -497,10 +497,10 @@ acpi_ns_remove_null_elements(struct acpi_evaluate_info *info,
        case ACPI_PTYPE2_MIN:
        case ACPI_PTYPE2_REV_FIXED:
        case ACPI_PTYPE2_FIX_VAR:
-
                break;
 
        default:
+       case ACPI_PTYPE2_VAR_VAR:
        case ACPI_PTYPE1_FIXED:
        case ACPI_PTYPE1_OPTION:
                return;
index 20e1a35169fc4f94d5e18d7f706cc80eb8d43bb0..58310907fa7be69c3b8470537b165db2badb7b17 100644 (file)
@@ -50,9 +50,6 @@
 #define _COMPONENT          ACPI_PARSER
 ACPI_MODULE_NAME("psopinfo")
 
-extern const u8 acpi_gbl_short_op_index[];
-extern const u8 acpi_gbl_long_op_index[];
-
 static const u8 acpi_gbl_argument_count[] =
     { 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 6 };
 
index 7e1168be39fa6d192a8fc18c49df9557d4c6f713..857af824337bebae5b6de938be7f1afeafc676b6 100644 (file)
@@ -198,11 +198,8 @@ acpi_ut_read_table(FILE * fp,
                             table_header.length, file_size);
 
 #ifdef ACPI_ASL_COMPILER
-                       status = fl_check_for_ascii(fp, NULL, FALSE);
-                       if (ACPI_SUCCESS(status)) {
-                               acpi_os_printf
-                                   ("File appears to be ASCII only, must be binary\n");
-                       }
+                       acpi_os_printf("File is corrupt or is ASCII text -- "
+                                      "it must be a binary file\n");
 #endif
                        return (AE_BAD_HEADER);
                }
@@ -315,7 +312,7 @@ acpi_ut_read_table_from_file(char *filename, struct acpi_table_header ** table)
        /* Get the entire file */
 
        fprintf(stderr,
-               "Loading Acpi table from file %10s - Length %.8u (%06X)\n",
+               "Reading ACPI table from file %10s - Length %.8u (0x%06X)\n",
                filename, file_size, file_size);
 
        status = acpi_ut_read_table(file, table, &table_length);
index aa448278ba28f91e27ef732a1945e70978ca632f..fda8b3def81c64d9d18ca81373dd2836bafa1658 100644 (file)
@@ -75,9 +75,9 @@ char acpi_ut_hex_to_ascii_char(u64 integer, u32 position)
 
 /*******************************************************************************
  *
- * FUNCTION:    acpi_ut_hex_char_to_value
+ * FUNCTION:    acpi_ut_ascii_char_to_hex
  *
- * PARAMETERS:  ascii_char            - Hex character in Ascii
+ * PARAMETERS:  hex_char                - Hex character in Ascii
  *
  * RETURN:      The binary value of the ascii/hex character
  *
index 306e785f94182c276599cf29b20d8cd5c1dbc83e..98d578753101e3ff4b7bcf73c83fdb679bc7c765 100644 (file)
@@ -107,9 +107,16 @@ acpi_exception(const char *module_name,
        va_list arg_list;
 
        ACPI_MSG_REDIRECT_BEGIN;
-       acpi_os_printf(ACPI_MSG_EXCEPTION "%s, ",
-                      acpi_format_exception(status));
 
+       /* For AE_OK, just print the message */
+
+       if (ACPI_SUCCESS(status)) {
+               acpi_os_printf(ACPI_MSG_EXCEPTION);
+
+       } else {
+               acpi_os_printf(ACPI_MSG_EXCEPTION "%s, ",
+                              acpi_format_exception(status));
+       }
        va_start(arg_list, format);
        acpi_os_vprintf(format, arg_list);
        ACPI_MSG_SUFFIX;
index ed65e9c4b5b0415c1dcc77690a4add725a49a0bd..3670bbab57a34e3c24e674f2e0e21112403d0490 100644 (file)
@@ -35,6 +35,7 @@
 #include <linux/nmi.h>
 #include <linux/hardirq.h>
 #include <linux/pstore.h>
+#include <linux/vmalloc.h>
 #include <acpi/apei.h>
 
 #include "apei-internal.h"
index e82d0976a5d079bc3aa6b6f956d91d943a685f3b..2bfd53cbfe8070950d26156a34aaa9ded7cba3ff 100644 (file)
@@ -729,10 +729,10 @@ static struct llist_head ghes_estatus_llist;
 static struct irq_work ghes_proc_irq_work;
 
 /*
- * NMI may be triggered on any CPU, so ghes_nmi_lock is used for
- * mutual exclusion.
+ * NMI may be triggered on any CPU, so ghes_in_nmi is used for
+ * having only one concurrent reader.
  */
-static DEFINE_RAW_SPINLOCK(ghes_nmi_lock);
+static atomic_t ghes_in_nmi = ATOMIC_INIT(0);
 
 static LIST_HEAD(ghes_nmi);
 
@@ -797,73 +797,75 @@ static void ghes_print_queued_estatus(void)
        }
 }
 
+/* Save estatus for further processing in IRQ context */
+static void __process_error(struct ghes *ghes)
+{
+#ifdef CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG
+       u32 len, node_len;
+       struct ghes_estatus_node *estatus_node;
+       struct acpi_hest_generic_status *estatus;
+
+       if (ghes_estatus_cached(ghes->estatus))
+               return;
+
+       len = cper_estatus_len(ghes->estatus);
+       node_len = GHES_ESTATUS_NODE_LEN(len);
+
+       estatus_node = (void *)gen_pool_alloc(ghes_estatus_pool, node_len);
+       if (!estatus_node)
+               return;
+
+       estatus_node->ghes = ghes;
+       estatus_node->generic = ghes->generic;
+       estatus = GHES_ESTATUS_FROM_NODE(estatus_node);
+       memcpy(estatus, ghes->estatus, len);
+       llist_add(&estatus_node->llnode, &ghes_estatus_llist);
+#endif
+}
+
+static void __ghes_panic(struct ghes *ghes)
+{
+       oops_begin();
+       ghes_print_queued_estatus();
+       __ghes_print_estatus(KERN_EMERG, ghes->generic, ghes->estatus);
+
+       /* reboot to log the error! */
+       if (panic_timeout == 0)
+               panic_timeout = ghes_panic_timeout;
+       panic("Fatal hardware error!");
+}
+
 static int ghes_notify_nmi(unsigned int cmd, struct pt_regs *regs)
 {
-       struct ghes *ghes, *ghes_global = NULL;
-       int sev, sev_global = -1;
-       int ret = NMI_DONE;
+       struct ghes *ghes;
+       int sev, ret = NMI_DONE;
+
+       if (!atomic_add_unless(&ghes_in_nmi, 1, 1))
+               return ret;
 
-       raw_spin_lock(&ghes_nmi_lock);
        list_for_each_entry_rcu(ghes, &ghes_nmi, list) {
                if (ghes_read_estatus(ghes, 1)) {
                        ghes_clear_estatus(ghes);
                        continue;
                }
-               sev = ghes_severity(ghes->estatus->error_severity);
-               if (sev > sev_global) {
-                       sev_global = sev;
-                       ghes_global = ghes;
-               }
-               ret = NMI_HANDLED;
-       }
-
-       if (ret == NMI_DONE)
-               goto out;
 
-       if (sev_global >= GHES_SEV_PANIC) {
-               oops_begin();
-               ghes_print_queued_estatus();
-               __ghes_print_estatus(KERN_EMERG, ghes_global->generic,
-                                    ghes_global->estatus);
-               /* reboot to log the error! */
-               if (panic_timeout == 0)
-                       panic_timeout = ghes_panic_timeout;
-               panic("Fatal hardware error!");
-       }
+               sev = ghes_severity(ghes->estatus->error_severity);
+               if (sev >= GHES_SEV_PANIC)
+                       __ghes_panic(ghes);
 
-       list_for_each_entry_rcu(ghes, &ghes_nmi, list) {
-#ifdef CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG
-               u32 len, node_len;
-               struct ghes_estatus_node *estatus_node;
-               struct acpi_hest_generic_status *estatus;
-#endif
                if (!(ghes->flags & GHES_TO_CLEAR))
                        continue;
-#ifdef CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG
-               if (ghes_estatus_cached(ghes->estatus))
-                       goto next;
-               /* Save estatus for further processing in IRQ context */
-               len = cper_estatus_len(ghes->estatus);
-               node_len = GHES_ESTATUS_NODE_LEN(len);
-               estatus_node = (void *)gen_pool_alloc(ghes_estatus_pool,
-                                                     node_len);
-               if (estatus_node) {
-                       estatus_node->ghes = ghes;
-                       estatus_node->generic = ghes->generic;
-                       estatus = GHES_ESTATUS_FROM_NODE(estatus_node);
-                       memcpy(estatus, ghes->estatus, len);
-                       llist_add(&estatus_node->llnode, &ghes_estatus_llist);
-               }
-next:
-#endif
+
+               __process_error(ghes);
                ghes_clear_estatus(ghes);
+
+               ret = NMI_HANDLED;
        }
+
 #ifdef CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG
        irq_work_queue(&ghes_proc_irq_work);
 #endif
-
-out:
-       raw_spin_unlock(&ghes_nmi_lock);
+       atomic_dec(&ghes_in_nmi);
        return ret;
 }
 
index 63d43677f644bcc4c9d3aab0079167f35e7fdda8..b3628cc01a535991d7d4c864d44c5dd337f9ca8d 100644 (file)
@@ -70,6 +70,7 @@ MODULE_AUTHOR("Alexey Starikovskiy <astarikovskiy@suse.de>");
 MODULE_DESCRIPTION("ACPI Battery Driver");
 MODULE_LICENSE("GPL");
 
+static async_cookie_t async_cookie;
 static int battery_bix_broken_package;
 static int battery_notification_delay_ms;
 static unsigned int cache_time = 1000;
@@ -338,14 +339,6 @@ static enum power_supply_property energy_battery_props[] = {
        POWER_SUPPLY_PROP_SERIAL_NUMBER,
 };
 
-#ifdef CONFIG_ACPI_PROCFS_POWER
-inline char *acpi_battery_units(struct acpi_battery *battery)
-{
-       return (battery->power_unit == ACPI_BATTERY_POWER_UNIT_MA) ?
-               "mA" : "mW";
-}
-#endif
-
 /* --------------------------------------------------------------------------
                                Battery Management
    -------------------------------------------------------------------------- */
@@ -354,14 +347,14 @@ struct acpi_offsets {
        u8 mode;                /* int or string? */
 };
 
-static struct acpi_offsets state_offsets[] = {
+static const struct acpi_offsets state_offsets[] = {
        {offsetof(struct acpi_battery, state), 0},
        {offsetof(struct acpi_battery, rate_now), 0},
        {offsetof(struct acpi_battery, capacity_now), 0},
        {offsetof(struct acpi_battery, voltage_now), 0},
 };
 
-static struct acpi_offsets info_offsets[] = {
+static const struct acpi_offsets info_offsets[] = {
        {offsetof(struct acpi_battery, power_unit), 0},
        {offsetof(struct acpi_battery, design_capacity), 0},
        {offsetof(struct acpi_battery, full_charge_capacity), 0},
@@ -377,7 +370,7 @@ static struct acpi_offsets info_offsets[] = {
        {offsetof(struct acpi_battery, oem_info), 1},
 };
 
-static struct acpi_offsets extended_info_offsets[] = {
+static const struct acpi_offsets extended_info_offsets[] = {
        {offsetof(struct acpi_battery, revision), 0},
        {offsetof(struct acpi_battery, power_unit), 0},
        {offsetof(struct acpi_battery, design_capacity), 0},
@@ -402,7 +395,7 @@ static struct acpi_offsets extended_info_offsets[] = {
 
 static int extract_package(struct acpi_battery *battery,
                           union acpi_object *package,
-                          struct acpi_offsets *offsets, int num)
+                          const struct acpi_offsets *offsets, int num)
 {
        int i;
        union acpi_object *element;
@@ -792,6 +785,12 @@ static void acpi_battery_refresh(struct acpi_battery *battery)
 #ifdef CONFIG_ACPI_PROCFS_POWER
 static struct proc_dir_entry *acpi_battery_dir;
 
+static const char *acpi_battery_units(const struct acpi_battery *battery)
+{
+       return (battery->power_unit == ACPI_BATTERY_POWER_UNIT_MA) ?
+               "mA" : "mW";
+}
+
 static int acpi_battery_print_info(struct seq_file *seq, int result)
 {
        struct acpi_battery *battery = seq->private;
@@ -1125,19 +1124,21 @@ static int battery_notify(struct notifier_block *nb,
        return 0;
 }
 
-static int battery_bix_broken_package_quirk(const struct dmi_system_id *d)
+static int __init
+battery_bix_broken_package_quirk(const struct dmi_system_id *d)
 {
        battery_bix_broken_package = 1;
        return 0;
 }
 
-static int battery_notification_delay_quirk(const struct dmi_system_id *d)
+static int __init
+battery_notification_delay_quirk(const struct dmi_system_id *d)
 {
        battery_notification_delay_ms = 1000;
        return 0;
 }
 
-static struct dmi_system_id bat_dmi_table[] = {
+static const struct dmi_system_id bat_dmi_table[] __initconst = {
        {
                .callback = battery_bix_broken_package_quirk,
                .ident = "NEC LZ750/LS",
@@ -1292,33 +1293,34 @@ static struct acpi_driver acpi_battery_driver = {
 
 static void __init acpi_battery_init_async(void *unused, async_cookie_t cookie)
 {
-       if (acpi_disabled)
-               return;
+       int result;
 
        dmi_check_system(bat_dmi_table);
-       
+
 #ifdef CONFIG_ACPI_PROCFS_POWER
        acpi_battery_dir = acpi_lock_battery_dir();
        if (!acpi_battery_dir)
                return;
 #endif
-       if (acpi_bus_register_driver(&acpi_battery_driver) < 0) {
+       result = acpi_bus_register_driver(&acpi_battery_driver);
 #ifdef CONFIG_ACPI_PROCFS_POWER
+       if (result < 0)
                acpi_unlock_battery_dir(acpi_battery_dir);
 #endif
-               return;
-       }
-       return;
 }
 
 static int __init acpi_battery_init(void)
 {
-       async_schedule(acpi_battery_init_async, NULL);
+       if (acpi_disabled)
+               return -ENODEV;
+
+       async_cookie = async_schedule(acpi_battery_init_async, NULL);
        return 0;
 }
 
 static void __exit acpi_battery_exit(void)
 {
+       async_synchronize_cookie(async_cookie);
        acpi_bus_unregister_driver(&acpi_battery_driver);
 #ifdef CONFIG_ACPI_PROCFS_POWER
        acpi_unlock_battery_dir(acpi_battery_dir);
index c412fdb28d34430913fa2080af6e68619050c107..513e7230e3d04bbf8e7fd9ded2cc4300c938c58e 100644 (file)
@@ -470,6 +470,16 @@ static int __init acpi_bus_init_irq(void)
        return 0;
 }
 
+/**
+ * acpi_early_init - Initialize ACPICA and populate the ACPI namespace.
+ *
+ * The ACPI tables are accessible after this, but the handling of events has not
+ * been initialized and the global lock is not available yet, so AML should not
+ * be executed at this point.
+ *
+ * Doing this before switching the EFI runtime services to virtual mode allows
+ * the EfiBootServices memory to be freed slightly earlier on boot.
+ */
 void __init acpi_early_init(void)
 {
        acpi_status status;
@@ -533,26 +543,42 @@ void __init acpi_early_init(void)
                acpi_gbl_FADT.sci_interrupt = acpi_sci_override_gsi;
        }
 #endif
+       return;
+
+ error0:
+       disable_acpi();
+}
+
+/**
+ * acpi_subsystem_init - Finalize the early initialization of ACPI.
+ *
+ * Switch over the platform to the ACPI mode (if possible), initialize the
+ * handling of ACPI events, install the interrupt and global lock handlers.
+ *
+ * Doing this too early is generally unsafe, but at the same time it needs to be
+ * done before all things that really depend on ACPI.  The right spot appears to
+ * be before finalizing the EFI initialization.
+ */
+void __init acpi_subsystem_init(void)
+{
+       acpi_status status;
+
+       if (acpi_disabled)
+               return;
 
        status = acpi_enable_subsystem(~ACPI_NO_ACPI_ENABLE);
        if (ACPI_FAILURE(status)) {
                printk(KERN_ERR PREFIX "Unable to enable ACPI\n");
-               goto error0;
+               disable_acpi();
+       } else {
+               /*
+                * If the system is using ACPI then we can be reasonably
+                * confident that any regulators are managed by the firmware
+                * so tell the regulator core it has everything it needs to
+                * know.
+                */
+               regulator_has_full_constraints();
        }
-
-       /*
-        * If the system is using ACPI then we can be reasonably
-        * confident that any regulators are managed by the firmware
-        * so tell the regulator core it has everything it needs to
-        * know.
-        */
-       regulator_has_full_constraints();
-
-       return;
-
-      error0:
-       disable_acpi();
-       return;
 }
 
 static int __init acpi_bus_init(void)
index 735db11a9b001fdf9811d2ac292ba62bc4e2ff4b..717afcdb5f4a9657a9ca6f6af9825e109eba9688 100644 (file)
@@ -98,17 +98,16 @@ int acpi_device_get_power(struct acpi_device *device, int *state)
 
                /*
                 * The power resources settings may indicate a power state
-                * shallower than the actual power state of the device.
+                * shallower than the actual power state of the device, because
+                * the same power resources may be referenced by other devices.
                 *
-                * Moreover, on systems predating ACPI 4.0, if the device
-                * doesn't depend on any power resources and _PSC returns 3,
-                * that means "power off".  We need to maintain compatibility
-                * with those systems.
+                * For systems predating ACPI 4.0 we assume that D3hot is the
+                * deepest state that can be supported.
                 */
                if (psc > result && psc < ACPI_STATE_D3_COLD)
                        result = psc;
                else if (result == ACPI_STATE_UNKNOWN)
-                       result = psc > ACPI_STATE_D2 ? ACPI_STATE_D3_COLD : psc;
+                       result = psc > ACPI_STATE_D2 ? ACPI_STATE_D3_HOT : psc;
        }
 
        /*
@@ -153,8 +152,8 @@ static int acpi_dev_pm_explicit_set(struct acpi_device *adev, int state)
  */
 int acpi_device_set_power(struct acpi_device *device, int state)
 {
+       int target_state = state;
        int result = 0;
-       bool cut_power = false;
 
        if (!device || !device->flags.power_manageable
            || (state < ACPI_STATE_D0) || (state > ACPI_STATE_D3_COLD))
@@ -169,11 +168,21 @@ int acpi_device_set_power(struct acpi_device *device, int state)
                return 0;
        }
 
-       if (!device->power.states[state].flags.valid) {
+       if (state == ACPI_STATE_D3_COLD) {
+               /*
+                * For transitions to D3cold we need to execute _PS3 and then
+                * possibly drop references to the power resources in use.
+                */
+               state = ACPI_STATE_D3_HOT;
+               /* If _PR3 is not available, use D3hot as the target state. */
+               if (!device->power.states[ACPI_STATE_D3_COLD].flags.valid)
+                       target_state = state;
+       } else if (!device->power.states[state].flags.valid) {
                dev_warn(&device->dev, "Power state %s not supported\n",
                         acpi_power_state_string(state));
                return -ENODEV;
        }
+
        if (!device->power.flags.ignore_parent &&
            device->parent && (state < device->parent->power.state)) {
                dev_warn(&device->dev,
@@ -183,39 +192,38 @@ int acpi_device_set_power(struct acpi_device *device, int state)
                return -ENODEV;
        }
 
-       /* For D3cold we should first transition into D3hot. */
-       if (state == ACPI_STATE_D3_COLD
-           && device->power.states[ACPI_STATE_D3_COLD].flags.os_accessible) {
-               state = ACPI_STATE_D3_HOT;
-               cut_power = true;
-       }
-
-       if (state < device->power.state && state != ACPI_STATE_D0
-           && device->power.state >= ACPI_STATE_D3_HOT) {
-               dev_warn(&device->dev,
-                        "Cannot transition to non-D0 state from D3\n");
-               return -ENODEV;
-       }
-
        /*
         * Transition Power
         * ----------------
-        * In accordance with the ACPI specification first apply power (via
-        * power resources) and then evaluate _PSx.
+        * In accordance with ACPI 6, _PSx is executed before manipulating power
+        * resources, unless the target state is D0, in which case _PS0 is
+        * supposed to be executed after turning the power resources on.
         */
-       if (device->power.flags.power_resources) {
-               result = acpi_power_transition(device, state);
+       if (state > ACPI_STATE_D0) {
+               /*
+                * According to ACPI 6, devices cannot go from lower-power
+                * (deeper) states to higher-power (shallower) states.
+                */
+               if (state < device->power.state) {
+                       dev_warn(&device->dev, "Cannot transition from %s to %s\n",
+                                acpi_power_state_string(device->power.state),
+                                acpi_power_state_string(state));
+                       return -ENODEV;
+               }
+
+               result = acpi_dev_pm_explicit_set(device, state);
                if (result)
                        goto end;
-       }
-       result = acpi_dev_pm_explicit_set(device, state);
-       if (result)
-               goto end;
 
-       if (cut_power) {
-               device->power.state = state;
-               state = ACPI_STATE_D3_COLD;
-               result = acpi_power_transition(device, state);
+               if (device->power.flags.power_resources)
+                       result = acpi_power_transition(device, target_state);
+       } else {
+               if (device->power.flags.power_resources) {
+                       result = acpi_power_transition(device, ACPI_STATE_D0);
+                       if (result)
+                               goto end;
+               }
+               result = acpi_dev_pm_explicit_set(device, ACPI_STATE_D0);
        }
 
  end:
@@ -264,13 +272,24 @@ int acpi_bus_init_power(struct acpi_device *device)
                return result;
 
        if (state < ACPI_STATE_D3_COLD && device->power.flags.power_resources) {
+               /* Reference count the power resources. */
                result = acpi_power_on_resources(device, state);
                if (result)
                        return result;
 
-               result = acpi_dev_pm_explicit_set(device, state);
-               if (result)
-                       return result;
+               if (state == ACPI_STATE_D0) {
+                       /*
+                        * If _PSC is not present and the state inferred from
+                        * power resources appears to be D0, it still may be
+                        * necessary to execute _PS0 at this point, because
+                        * another device using the same power resources may
+                        * have been put into D0 previously and that's why we
+                        * see D0 here.
+                        */
+                       result = acpi_dev_pm_explicit_set(device, state);
+                       if (result)
+                               return result;
+               }
        } else if (state == ACPI_STATE_UNKNOWN) {
                /*
                 * No power resources and missing _PSC?  Cross fingers and make
@@ -603,12 +622,12 @@ int acpi_pm_device_sleep_state(struct device *dev, int *d_min_p, int d_max_in)
        if (d_max_in < ACPI_STATE_D0 || d_max_in > ACPI_STATE_D3_COLD)
                return -EINVAL;
 
-       if (d_max_in > ACPI_STATE_D3_HOT) {
+       if (d_max_in > ACPI_STATE_D2) {
                enum pm_qos_flags_status stat;
 
                stat = dev_pm_qos_flags(dev, PM_QOS_FLAG_NO_POWER_OFF);
                if (stat == PM_QOS_FLAGS_ALL)
-                       d_max_in = ACPI_STATE_D3_HOT;
+                       d_max_in = ACPI_STATE_D2;
        }
 
        adev = ACPI_COMPANION(dev);
@@ -953,6 +972,7 @@ EXPORT_SYMBOL_GPL(acpi_subsys_prepare);
  */
 void acpi_subsys_complete(struct device *dev)
 {
+       pm_generic_complete(dev);
        /*
         * If the device had been runtime-suspended before the system went into
         * the sleep state it is going out of and it has never been resumed till
index 5e8fed448850fc1c8f50448f6b344066d2e730ef..9d4761d2f6b77b82f10dda7027c707d4e241895c 100644 (file)
 #define ACPI_EC_FLAG_BURST     0x10    /* burst mode */
 #define ACPI_EC_FLAG_SCI       0x20    /* EC-SCI occurred */
 
+/*
+ * The SCI_EVT clearing timing is not defined by the ACPI specification.
+ * This leads to lots of practical timing issues for the host EC driver.
+ * The following variations are defined (from the target EC firmware's
+ * perspective):
+ * STATUS: After indicating SCI_EVT edge triggered IRQ to the host, the
+ *         target can clear SCI_EVT at any time so long as the host can see
+ *         the indication by reading the status register (EC_SC). So the
+ *         host should re-check SCI_EVT after the first time the SCI_EVT
+ *         indication is seen, which is the same time the query request
+ *         (QR_EC) is written to the command register (EC_CMD). SCI_EVT set
+ *         at any later time could indicate another event. Normally such
+ *         kind of EC firmware has implemented an event queue and will
+ *         return 0x00 to indicate "no outstanding event".
+ * QUERY: After seeing the query request (QR_EC) written to the command
+ *        register (EC_CMD) by the host and having prepared the responding
+ *        event value in the data register (EC_DATA), the target can safely
+ *        clear SCI_EVT because the target can confirm that the current
+ *        event is being handled by the host. The host then should check
+ *        SCI_EVT right after reading the event response from the data
+ *        register (EC_DATA).
+ * EVENT: After seeing the event response read from the data register
+ *        (EC_DATA) by the host, the target can clear SCI_EVT. As the
+ *        target requires time to notice the change in the data register
+ *        (EC_DATA), the host may be required to wait additional guarding
+ *        time before checking the SCI_EVT again. Such guarding may not be
+ *        necessary if the host is notified via another IRQ.
+ */
+#define ACPI_EC_EVT_TIMING_STATUS      0x00
+#define ACPI_EC_EVT_TIMING_QUERY       0x01
+#define ACPI_EC_EVT_TIMING_EVENT       0x02
+
 /* EC commands */
 enum ec_command {
        ACPI_EC_COMMAND_READ = 0x80,
@@ -70,13 +102,13 @@ enum ec_command {
 
 #define ACPI_EC_DELAY          500     /* Wait 500ms max. during EC ops */
 #define ACPI_EC_UDELAY_GLK     1000    /* Wait 1ms max. to get global lock */
-#define ACPI_EC_MSI_UDELAY     550     /* Wait 550us for MSI EC */
-#define ACPI_EC_UDELAY_POLL    1000    /* Wait 1ms for EC transaction polling */
+#define ACPI_EC_UDELAY_POLL    550     /* Wait 1ms for EC transaction polling */
 #define ACPI_EC_CLEAR_MAX      100     /* Maximum number of events to query
                                         * when trying to clear the EC */
 
 enum {
        EC_FLAGS_QUERY_PENDING,         /* Query is pending */
+       EC_FLAGS_QUERY_GUARDING,        /* Guard for SCI_EVT check */
        EC_FLAGS_HANDLERS_INSTALLED,    /* Handlers for GPE and
                                         * OpReg are installed */
        EC_FLAGS_STARTED,               /* Driver is started */
@@ -93,6 +125,16 @@ static unsigned int ec_delay __read_mostly = ACPI_EC_DELAY;
 module_param(ec_delay, uint, 0644);
 MODULE_PARM_DESC(ec_delay, "Timeout(ms) waited until an EC command completes");
 
+static bool ec_busy_polling __read_mostly;
+module_param(ec_busy_polling, bool, 0644);
+MODULE_PARM_DESC(ec_busy_polling, "Use busy polling to advance EC transaction");
+
+static unsigned int ec_polling_guard __read_mostly = ACPI_EC_UDELAY_POLL;
+module_param(ec_polling_guard, uint, 0644);
+MODULE_PARM_DESC(ec_polling_guard, "Guard time(us) between EC accesses in polling modes");
+
+static unsigned int ec_event_clearing __read_mostly = ACPI_EC_EVT_TIMING_QUERY;
+
 /*
  * If the number of false interrupts per one transaction exceeds
  * this threshold, will think there is a GPE storm happened and
@@ -121,7 +163,6 @@ struct transaction {
        u8 wlen;
        u8 rlen;
        u8 flags;
-       unsigned long timestamp;
 };
 
 static int acpi_ec_query(struct acpi_ec *ec, u8 *data);
@@ -130,7 +171,6 @@ static void advance_transaction(struct acpi_ec *ec);
 struct acpi_ec *boot_ec, *first_ec;
 EXPORT_SYMBOL(first_ec);
 
-static int EC_FLAGS_MSI; /* Out-of-spec MSI controller */
 static int EC_FLAGS_VALIDATE_ECDT; /* ASUStec ECDTs need to be validated */
 static int EC_FLAGS_SKIP_DSDT_SCAN; /* Not all BIOS survive early DSDT scan */
 static int EC_FLAGS_CLEAR_ON_RESUME; /* Needs acpi_ec_clear() on boot/resume */
@@ -218,7 +258,7 @@ static inline u8 acpi_ec_read_data(struct acpi_ec *ec)
 {
        u8 x = inb(ec->data_addr);
 
-       ec->curr->timestamp = jiffies;
+       ec->timestamp = jiffies;
        ec_dbg_raw("EC_DATA(R) = 0x%2.2x", x);
        return x;
 }
@@ -227,14 +267,14 @@ static inline void acpi_ec_write_cmd(struct acpi_ec *ec, u8 command)
 {
        ec_dbg_raw("EC_SC(W) = 0x%2.2x", command);
        outb(command, ec->command_addr);
-       ec->curr->timestamp = jiffies;
+       ec->timestamp = jiffies;
 }
 
 static inline void acpi_ec_write_data(struct acpi_ec *ec, u8 data)
 {
        ec_dbg_raw("EC_DATA(W) = 0x%2.2x", data);
        outb(data, ec->data_addr);
-       ec->curr->timestamp = jiffies;
+       ec->timestamp = jiffies;
 }
 
 #ifdef DEBUG
@@ -267,7 +307,7 @@ static inline bool acpi_ec_is_gpe_raised(struct acpi_ec *ec)
        acpi_event_status gpe_status = 0;
 
        (void)acpi_get_gpe_status(NULL, ec->gpe, &gpe_status);
-       return (gpe_status & ACPI_EVENT_FLAG_SET) ? true : false;
+       return (gpe_status & ACPI_EVENT_FLAG_STATUS_SET) ? true : false;
 }
 
 static inline void acpi_ec_enable_gpe(struct acpi_ec *ec, bool open)
@@ -379,19 +419,49 @@ static bool acpi_ec_submit_flushable_request(struct acpi_ec *ec)
 static void acpi_ec_submit_query(struct acpi_ec *ec)
 {
        if (!test_and_set_bit(EC_FLAGS_QUERY_PENDING, &ec->flags)) {
-               ec_dbg_req("Event started");
+               ec_dbg_evt("Command(%s) submitted/blocked",
+                          acpi_ec_cmd_string(ACPI_EC_COMMAND_QUERY));
+               ec->nr_pending_queries++;
                schedule_work(&ec->work);
        }
 }
 
 static void acpi_ec_complete_query(struct acpi_ec *ec)
 {
-       if (ec->curr->command == ACPI_EC_COMMAND_QUERY) {
+       if (test_bit(EC_FLAGS_QUERY_PENDING, &ec->flags)) {
                clear_bit(EC_FLAGS_QUERY_PENDING, &ec->flags);
-               ec_dbg_req("Event stopped");
+               ec_dbg_evt("Command(%s) unblocked",
+                          acpi_ec_cmd_string(ACPI_EC_COMMAND_QUERY));
        }
 }
 
+static bool acpi_ec_guard_event(struct acpi_ec *ec)
+{
+       if (ec_event_clearing == ACPI_EC_EVT_TIMING_STATUS ||
+           ec_event_clearing == ACPI_EC_EVT_TIMING_QUERY ||
+           !test_bit(EC_FLAGS_QUERY_PENDING, &ec->flags) ||
+           (ec->curr && ec->curr->command == ACPI_EC_COMMAND_QUERY))
+               return false;
+
+       /*
+        * Postpone the query submission to allow the firmware to proceed,
+        * we shouldn't check SCI_EVT before the firmware reflagging it.
+        */
+       return true;
+}
+
+static int ec_transaction_polled(struct acpi_ec *ec)
+{
+       unsigned long flags;
+       int ret = 0;
+
+       spin_lock_irqsave(&ec->lock, flags);
+       if (ec->curr && (ec->curr->flags & ACPI_EC_COMMAND_POLL))
+               ret = 1;
+       spin_unlock_irqrestore(&ec->lock, flags);
+       return ret;
+}
+
 static int ec_transaction_completed(struct acpi_ec *ec)
 {
        unsigned long flags;
@@ -404,6 +474,22 @@ static int ec_transaction_completed(struct acpi_ec *ec)
        return ret;
 }
 
+static inline void ec_transaction_transition(struct acpi_ec *ec, unsigned long flag)
+{
+       ec->curr->flags |= flag;
+       if (ec->curr->command == ACPI_EC_COMMAND_QUERY) {
+               if (ec_event_clearing == ACPI_EC_EVT_TIMING_STATUS &&
+                   flag == ACPI_EC_COMMAND_POLL)
+                       acpi_ec_complete_query(ec);
+               if (ec_event_clearing == ACPI_EC_EVT_TIMING_QUERY &&
+                   flag == ACPI_EC_COMMAND_COMPLETE)
+                       acpi_ec_complete_query(ec);
+               if (ec_event_clearing == ACPI_EC_EVT_TIMING_EVENT &&
+                   flag == ACPI_EC_COMMAND_COMPLETE)
+                       set_bit(EC_FLAGS_QUERY_GUARDING, &ec->flags);
+       }
+}
+
 static void advance_transaction(struct acpi_ec *ec)
 {
        struct transaction *t;
@@ -420,6 +506,18 @@ static void advance_transaction(struct acpi_ec *ec)
        acpi_ec_clear_gpe(ec);
        status = acpi_ec_read_status(ec);
        t = ec->curr;
+       /*
+        * Another IRQ or a guarded polling mode advancement is detected,
+        * the next QR_EC submission is then allowed.
+        */
+       if (!t || !(t->flags & ACPI_EC_COMMAND_POLL)) {
+               if (ec_event_clearing == ACPI_EC_EVT_TIMING_EVENT &&
+                   (!ec->nr_pending_queries ||
+                    test_bit(EC_FLAGS_QUERY_GUARDING, &ec->flags))) {
+                       clear_bit(EC_FLAGS_QUERY_GUARDING, &ec->flags);
+                       acpi_ec_complete_query(ec);
+               }
+       }
        if (!t)
                goto err;
        if (t->flags & ACPI_EC_COMMAND_POLL) {
@@ -432,17 +530,17 @@ static void advance_transaction(struct acpi_ec *ec)
                        if ((status & ACPI_EC_FLAG_OBF) == 1) {
                                t->rdata[t->ri++] = acpi_ec_read_data(ec);
                                if (t->rlen == t->ri) {
-                                       t->flags |= ACPI_EC_COMMAND_COMPLETE;
+                                       ec_transaction_transition(ec, ACPI_EC_COMMAND_COMPLETE);
                                        if (t->command == ACPI_EC_COMMAND_QUERY)
-                                               ec_dbg_req("Command(%s) hardware completion",
-                                                          acpi_ec_cmd_string(t->command));
+                                               ec_dbg_evt("Command(%s) completed by hardware",
+                                                          acpi_ec_cmd_string(ACPI_EC_COMMAND_QUERY));
                                        wakeup = true;
                                }
                        } else
                                goto err;
                } else if (t->wlen == t->wi &&
                           (status & ACPI_EC_FLAG_IBF) == 0) {
-                       t->flags |= ACPI_EC_COMMAND_COMPLETE;
+                       ec_transaction_transition(ec, ACPI_EC_COMMAND_COMPLETE);
                        wakeup = true;
                }
                goto out;
@@ -450,17 +548,15 @@ static void advance_transaction(struct acpi_ec *ec)
                if (EC_FLAGS_QUERY_HANDSHAKE &&
                    !(status & ACPI_EC_FLAG_SCI) &&
                    (t->command == ACPI_EC_COMMAND_QUERY)) {
-                       t->flags |= ACPI_EC_COMMAND_POLL;
-                       acpi_ec_complete_query(ec);
+                       ec_transaction_transition(ec, ACPI_EC_COMMAND_POLL);
                        t->rdata[t->ri++] = 0x00;
-                       t->flags |= ACPI_EC_COMMAND_COMPLETE;
-                       ec_dbg_req("Command(%s) software completion",
-                                  acpi_ec_cmd_string(t->command));
+                       ec_transaction_transition(ec, ACPI_EC_COMMAND_COMPLETE);
+                       ec_dbg_evt("Command(%s) completed by software",
+                                  acpi_ec_cmd_string(ACPI_EC_COMMAND_QUERY));
                        wakeup = true;
                } else if ((status & ACPI_EC_FLAG_IBF) == 0) {
                        acpi_ec_write_cmd(ec, t->command);
-                       t->flags |= ACPI_EC_COMMAND_POLL;
-                       acpi_ec_complete_query(ec);
+                       ec_transaction_transition(ec, ACPI_EC_COMMAND_POLL);
                } else
                        goto err;
                goto out;
@@ -490,8 +586,39 @@ static void start_transaction(struct acpi_ec *ec)
 {
        ec->curr->irq_count = ec->curr->wi = ec->curr->ri = 0;
        ec->curr->flags = 0;
-       ec->curr->timestamp = jiffies;
-       advance_transaction(ec);
+}
+
+static int ec_guard(struct acpi_ec *ec)
+{
+       unsigned long guard = usecs_to_jiffies(ec_polling_guard);
+       unsigned long timeout = ec->timestamp + guard;
+
+       do {
+               if (ec_busy_polling) {
+                       /* Perform busy polling */
+                       if (ec_transaction_completed(ec))
+                               return 0;
+                       udelay(jiffies_to_usecs(guard));
+               } else {
+                       /*
+                        * Perform wait polling
+                        *
+                        * For SCI_EVT clearing timing of "event",
+                        * performing guarding before re-checking the
+                        * SCI_EVT. Otherwise, such guarding is not needed
+                        * due to the old practices.
+                        */
+                       if (!ec_transaction_polled(ec) &&
+                           !acpi_ec_guard_event(ec))
+                               break;
+                       if (wait_event_timeout(ec->wait,
+                                              ec_transaction_completed(ec),
+                                              guard))
+                               return 0;
+               }
+               /* Guard the register accesses for the polling modes */
+       } while (time_before(jiffies, timeout));
+       return -ETIME;
 }
 
 static int ec_poll(struct acpi_ec *ec)
@@ -502,25 +629,11 @@ static int ec_poll(struct acpi_ec *ec)
        while (repeat--) {
                unsigned long delay = jiffies +
                        msecs_to_jiffies(ec_delay);
-               unsigned long usecs = ACPI_EC_UDELAY_POLL;
                do {
-                       /* don't sleep with disabled interrupts */
-                       if (EC_FLAGS_MSI || irqs_disabled()) {
-                               usecs = ACPI_EC_MSI_UDELAY;
-                               udelay(usecs);
-                               if (ec_transaction_completed(ec))
-                                       return 0;
-                       } else {
-                               if (wait_event_timeout(ec->wait,
-                                               ec_transaction_completed(ec),
-                                               usecs_to_jiffies(usecs)))
-                                       return 0;
-                       }
+                       if (!ec_guard(ec))
+                               return 0;
                        spin_lock_irqsave(&ec->lock, flags);
-                       if (time_after(jiffies,
-                                       ec->curr->timestamp +
-                                       usecs_to_jiffies(usecs)))
-                               advance_transaction(ec);
+                       advance_transaction(ec);
                        spin_unlock_irqrestore(&ec->lock, flags);
                } while (time_before(jiffies, delay));
                pr_debug("controller reset, restart transaction\n");
@@ -537,8 +650,6 @@ static int acpi_ec_transaction_unlocked(struct acpi_ec *ec,
        unsigned long tmp;
        int ret = 0;
 
-       if (EC_FLAGS_MSI)
-               udelay(ACPI_EC_MSI_UDELAY);
        /* start transaction */
        spin_lock_irqsave(&ec->lock, tmp);
        /* Enable GPE for command processing (IBF=0/OBF=1) */
@@ -552,7 +663,9 @@ static int acpi_ec_transaction_unlocked(struct acpi_ec *ec,
        ec_dbg_req("Command(%s) started", acpi_ec_cmd_string(t->command));
        start_transaction(ec);
        spin_unlock_irqrestore(&ec->lock, tmp);
+
        ret = ec_poll(ec);
+
        spin_lock_irqsave(&ec->lock, tmp);
        if (t->irq_count == ec_storm_threshold)
                acpi_ec_clear_storm(ec, EC_FLAGS_COMMAND_STORM);
@@ -575,6 +688,7 @@ static int acpi_ec_transaction(struct acpi_ec *ec, struct transaction *t)
                return -EINVAL;
        if (t->rdata)
                memset(t->rdata, 0, t->rlen);
+
        mutex_lock(&ec->mutex);
        if (ec->global_lock) {
                status = acpi_acquire_global_lock(ACPI_EC_UDELAY_GLK, &glk);
@@ -586,8 +700,6 @@ static int acpi_ec_transaction(struct acpi_ec *ec, struct transaction *t)
 
        status = acpi_ec_transaction_unlocked(ec, t);
 
-       if (test_bit(EC_FLAGS_COMMAND_STORM, &ec->flags))
-               msleep(1);
        if (ec->global_lock)
                acpi_release_global_lock(glk);
 unlock:
@@ -923,11 +1035,54 @@ static int acpi_ec_query(struct acpi_ec *ec, u8 *data)
        return result;
 }
 
-static void acpi_ec_gpe_poller(struct work_struct *work)
+static void acpi_ec_check_event(struct acpi_ec *ec)
 {
+       unsigned long flags;
+
+       if (ec_event_clearing == ACPI_EC_EVT_TIMING_EVENT) {
+               if (ec_guard(ec)) {
+                       spin_lock_irqsave(&ec->lock, flags);
+                       /*
+                        * Take care of the SCI_EVT unless no one else is
+                        * taking care of it.
+                        */
+                       if (!ec->curr)
+                               advance_transaction(ec);
+                       spin_unlock_irqrestore(&ec->lock, flags);
+               }
+       }
+}
+
+static void acpi_ec_event_handler(struct work_struct *work)
+{
+       unsigned long flags;
        struct acpi_ec *ec = container_of(work, struct acpi_ec, work);
 
-       acpi_ec_query(ec, NULL);
+       ec_dbg_evt("Event started");
+
+       spin_lock_irqsave(&ec->lock, flags);
+       while (ec->nr_pending_queries) {
+               spin_unlock_irqrestore(&ec->lock, flags);
+               (void)acpi_ec_query(ec, NULL);
+               spin_lock_irqsave(&ec->lock, flags);
+               ec->nr_pending_queries--;
+               /*
+                * Before exit, make sure that this work item can be
+                * scheduled again. There might be QR_EC failures, leaving
+                * EC_FLAGS_QUERY_PENDING uncleared and preventing this work
+                * item from being scheduled again.
+                */
+               if (!ec->nr_pending_queries) {
+                       if (ec_event_clearing == ACPI_EC_EVT_TIMING_STATUS ||
+                           ec_event_clearing == ACPI_EC_EVT_TIMING_QUERY)
+                               acpi_ec_complete_query(ec);
+               }
+       }
+       spin_unlock_irqrestore(&ec->lock, flags);
+
+       ec_dbg_evt("Event stopped");
+
+       acpi_ec_check_event(ec);
 }
 
 static u32 acpi_ec_gpe_handler(acpi_handle gpe_device,
@@ -961,7 +1116,7 @@ acpi_ec_space_handler(u32 function, acpi_physical_address address,
        if (function != ACPI_READ && function != ACPI_WRITE)
                return AE_BAD_PARAMETER;
 
-       if (EC_FLAGS_MSI || bits > 8)
+       if (ec_busy_polling || bits > 8)
                acpi_ec_burst_enable(ec);
 
        for (i = 0; i < bytes; ++i, ++address, ++value)
@@ -969,7 +1124,7 @@ acpi_ec_space_handler(u32 function, acpi_physical_address address,
                        acpi_ec_read(ec, address, value) :
                        acpi_ec_write(ec, address, *value);
 
-       if (EC_FLAGS_MSI || bits > 8)
+       if (ec_busy_polling || bits > 8)
                acpi_ec_burst_disable(ec);
 
        switch (result) {
@@ -1002,7 +1157,8 @@ static struct acpi_ec *make_acpi_ec(void)
        init_waitqueue_head(&ec->wait);
        INIT_LIST_HEAD(&ec->list);
        spin_lock_init(&ec->lock);
-       INIT_WORK(&ec->work, acpi_ec_gpe_poller);
+       INIT_WORK(&ec->work, acpi_ec_event_handler);
+       ec->timestamp = jiffies;
        return ec;
 }
 
@@ -1237,30 +1393,13 @@ static int ec_validate_ecdt(const struct dmi_system_id *id)
        return 0;
 }
 
-/* MSI EC needs special treatment, enable it */
-static int ec_flag_msi(const struct dmi_system_id *id)
-{
-       pr_debug("Detected MSI hardware, enabling workarounds.\n");
-       EC_FLAGS_MSI = 1;
-       EC_FLAGS_VALIDATE_ECDT = 1;
-       return 0;
-}
-
-/*
- * Clevo M720 notebook actually works ok with IRQ mode, if we lifted
- * the GPE storm threshold back to 20
- */
-static int ec_enlarge_storm_threshold(const struct dmi_system_id *id)
-{
-       pr_debug("Setting the EC GPE storm threshold to 20\n");
-       ec_storm_threshold  = 20;
-       return 0;
-}
-
+#if 0
 /*
- * Acer EC firmware refuses to respond QR_EC when SCI_EVT is not set, for
- * which case, we complete the QR_EC without issuing it to the firmware.
- * https://bugzilla.kernel.org/show_bug.cgi?id=86211
+ * Some EC firmware variations refuses to respond QR_EC when SCI_EVT is not
+ * set, for which case, we complete the QR_EC without issuing it to the
+ * firmware.
+ * https://bugzilla.kernel.org/show_bug.cgi?id=82611
+ * https://bugzilla.kernel.org/show_bug.cgi?id=97381
  */
 static int ec_flag_query_handshake(const struct dmi_system_id *id)
 {
@@ -1268,6 +1407,7 @@ static int ec_flag_query_handshake(const struct dmi_system_id *id)
        EC_FLAGS_QUERY_HANDSHAKE = 1;
        return 0;
 }
+#endif
 
 /*
  * On some hardware it is necessary to clear events accumulated by the EC during
@@ -1290,6 +1430,7 @@ static int ec_clear_on_resume(const struct dmi_system_id *id)
 {
        pr_debug("Detected system needing EC poll on resume.\n");
        EC_FLAGS_CLEAR_ON_RESUME = 1;
+       ec_event_clearing = ACPI_EC_EVT_TIMING_STATUS;
        return 0;
 }
 
@@ -1299,29 +1440,9 @@ static struct dmi_system_id ec_dmi_table[] __initdata = {
        DMI_MATCH(DMI_BIOS_VENDOR, "COMPAL"),
        DMI_MATCH(DMI_BOARD_NAME, "JFL92") }, NULL},
        {
-       ec_flag_msi, "MSI hardware", {
-       DMI_MATCH(DMI_BIOS_VENDOR, "Micro-Star")}, NULL},
-       {
-       ec_flag_msi, "MSI hardware", {
-       DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star")}, NULL},
-       {
-       ec_flag_msi, "MSI hardware", {
-       DMI_MATCH(DMI_CHASSIS_VENDOR, "MICRO-Star")}, NULL},
-       {
-       ec_flag_msi, "MSI hardware", {
-       DMI_MATCH(DMI_CHASSIS_VENDOR, "MICRO-STAR")}, NULL},
-       {
-       ec_flag_msi, "Quanta hardware", {
-       DMI_MATCH(DMI_SYS_VENDOR, "Quanta"),
-       DMI_MATCH(DMI_PRODUCT_NAME, "TW8/SW8/DW8"),}, NULL},
-       {
-       ec_flag_msi, "Quanta hardware", {
-       DMI_MATCH(DMI_SYS_VENDOR, "Quanta"),
-       DMI_MATCH(DMI_PRODUCT_NAME, "TW9/SW9"),}, NULL},
-       {
-       ec_flag_msi, "Clevo W350etq", {
-       DMI_MATCH(DMI_SYS_VENDOR, "CLEVO CO."),
-       DMI_MATCH(DMI_PRODUCT_NAME, "W35_37ET"),}, NULL},
+       ec_validate_ecdt, "MSI MS-171F", {
+       DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star"),
+       DMI_MATCH(DMI_PRODUCT_NAME, "MS-171F"),}, NULL},
        {
        ec_validate_ecdt, "ASUS hardware", {
        DMI_MATCH(DMI_BIOS_VENDOR, "ASUS") }, NULL},
@@ -1329,10 +1450,6 @@ static struct dmi_system_id ec_dmi_table[] __initdata = {
        ec_validate_ecdt, "ASUS hardware", {
        DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer Inc.") }, NULL},
        {
-       ec_enlarge_storm_threshold, "CLEVO hardware", {
-       DMI_MATCH(DMI_SYS_VENDOR, "CLEVO Co."),
-       DMI_MATCH(DMI_PRODUCT_NAME, "M720T/M730T"),}, NULL},
-       {
        ec_skip_dsdt_scan, "HP Folio 13", {
        DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
        DMI_MATCH(DMI_PRODUCT_NAME, "HP Folio 13"),}, NULL},
@@ -1343,9 +1460,6 @@ static struct dmi_system_id ec_dmi_table[] __initdata = {
        {
        ec_clear_on_resume, "Samsung hardware", {
        DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD.")}, NULL},
-       {
-       ec_flag_query_handshake, "Acer hardware", {
-       DMI_MATCH(DMI_SYS_VENDOR, "Acer"), }, NULL},
        {},
 };
 
@@ -1427,6 +1541,43 @@ error:
        return -ENODEV;
 }
 
+static int param_set_event_clearing(const char *val, struct kernel_param *kp)
+{
+       int result = 0;
+
+       if (!strncmp(val, "status", sizeof("status") - 1)) {
+               ec_event_clearing = ACPI_EC_EVT_TIMING_STATUS;
+               pr_info("Assuming SCI_EVT clearing on EC_SC accesses\n");
+       } else if (!strncmp(val, "query", sizeof("query") - 1)) {
+               ec_event_clearing = ACPI_EC_EVT_TIMING_QUERY;
+               pr_info("Assuming SCI_EVT clearing on QR_EC writes\n");
+       } else if (!strncmp(val, "event", sizeof("event") - 1)) {
+               ec_event_clearing = ACPI_EC_EVT_TIMING_EVENT;
+               pr_info("Assuming SCI_EVT clearing on event reads\n");
+       } else
+               result = -EINVAL;
+       return result;
+}
+
+static int param_get_event_clearing(char *buffer, struct kernel_param *kp)
+{
+       switch (ec_event_clearing) {
+       case ACPI_EC_EVT_TIMING_STATUS:
+               return sprintf(buffer, "status");
+       case ACPI_EC_EVT_TIMING_QUERY:
+               return sprintf(buffer, "query");
+       case ACPI_EC_EVT_TIMING_EVENT:
+               return sprintf(buffer, "event");
+       default:
+               return sprintf(buffer, "invalid");
+       }
+       return 0;
+}
+
+module_param_call(ec_event_clearing, param_set_event_clearing, param_get_event_clearing,
+                 NULL, 0644);
+MODULE_PARM_DESC(ec_event_clearing, "Assumed SCI_EVT clearing timing");
+
 static struct acpi_driver acpi_ec_driver = {
        .name = "ec",
        .class = ACPI_EC_CLASS,
index 7a36f02598a6f20db9fe2a78c61a0f8fbafe6ecb..bea0bbaafa979dd998d71695c62ac3366bd48e43 100644 (file)
@@ -158,8 +158,9 @@ static int fan_get_state(struct acpi_device *device, unsigned long *state)
        if (result)
                return result;
 
-       *state = (acpi_state == ACPI_STATE_D3_COLD ? 0 :
-                (acpi_state == ACPI_STATE_D0 ? 1 : -1));
+       *state = acpi_state == ACPI_STATE_D3_COLD
+                       || acpi_state == ACPI_STATE_D3_HOT ?
+               0 : (acpi_state == ACPI_STATE_D0 ? 1 : -1);
        return 0;
 }
 
index 39c485b0c25c0d6ddccabb35e3c7014fb92788a9..b9657af751d1051d9b44ed2a22e2c347dd9c9846 100644 (file)
@@ -13,6 +13,7 @@
 #include <linux/slab.h>
 #include <linux/rwsem.h>
 #include <linux/acpi.h>
+#include <linux/dma-mapping.h>
 
 #include "internal.h"
 
@@ -167,6 +168,7 @@ int acpi_bind_one(struct device *dev, struct acpi_device *acpi_dev)
        struct list_head *physnode_list;
        unsigned int node_id;
        int retval = -EINVAL;
+       bool coherent;
 
        if (has_acpi_companion(dev)) {
                if (acpi_dev) {
@@ -223,6 +225,9 @@ int acpi_bind_one(struct device *dev, struct acpi_device *acpi_dev)
        if (!has_acpi_companion(dev))
                ACPI_COMPANION_SET(dev, acpi_dev);
 
+       if (acpi_check_dma(acpi_dev, &coherent))
+               arch_setup_dma_ops(dev, 0, 0, NULL, coherent);
+
        acpi_physnode_link_name(physical_node_name, node_id);
        retval = sysfs_create_link(&acpi_dev->dev.kobj, &dev->kobj,
                                   physical_node_name);
index aafe3ca829c28b4a22a8f2a708df6d69093349e9..a322710b5ba47cd13ec1c35120db05ff9e834a1c 100644 (file)
@@ -27,7 +27,7 @@
 #include <linux/acpi.h>
 #include <acpi/hed.h>
 
-static struct acpi_device_id acpi_hed_ids[] = {
+static const struct acpi_device_id acpi_hed_ids[] = {
        {"PNP0C33", 0},
        {"", 0},
 };
index ba4a61e964be4d1d2abdb59bfab85a25aa3048c4..787c629bc9b41e83ac11496f76bb646437d3b077 100644 (file)
@@ -138,6 +138,8 @@ struct acpi_ec {
        struct transaction *curr;
        spinlock_t lock;
        struct work_struct work;
+       unsigned long timestamp;
+       unsigned long nr_pending_queries;
 };
 
 extern struct acpi_ec *first_ec;
@@ -181,16 +183,11 @@ static inline int suspend_nvs_save(void) { return 0; }
 static inline void suspend_nvs_restore(void) {}
 #endif
 
-/*--------------------------------------------------------------------------
-                                       Video
-  -------------------------------------------------------------------------- */
-#if defined(CONFIG_ACPI_VIDEO) || defined(CONFIG_ACPI_VIDEO_MODULE)
-bool acpi_osi_is_win8(void);
-#endif
-
 /*--------------------------------------------------------------------------
                                Device properties
   -------------------------------------------------------------------------- */
+#define ACPI_DT_NAMESPACE_HID  "PRP0001"
+
 void acpi_init_properties(struct acpi_device *adev);
 void acpi_free_properties(struct acpi_device *adev);
 
index 7ccba395c9ddbeb7a6725b336d69d01abd4b82b1..a5dc9034efeeda6f06044521600518f70bd322d3 100644 (file)
@@ -175,11 +175,7 @@ static void __init acpi_request_region (struct acpi_generic_address *gas,
        if (!addr || !length)
                return;
 
-       /* Resources are never freed */
-       if (gas->space_id == ACPI_ADR_SPACE_SYSTEM_IO)
-               request_region(addr, length, desc);
-       else if (gas->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY)
-               request_mem_region(addr, length, desc);
+       acpi_reserve_region(addr, length, gas->space_id, 0, desc);
 }
 
 static void __init acpi_reserve_resources(void)
@@ -540,7 +536,7 @@ static char acpi_os_name[ACPI_MAX_OVERRIDE_LEN];
 
 acpi_status
 acpi_os_predefined_override(const struct acpi_predefined_names *init_val,
-                           acpi_string * new_val)
+                           char **new_val)
 {
        if (!init_val || !new_val)
                return AE_BAD_PARAMETER;
@@ -1684,6 +1680,12 @@ int acpi_resources_are_enforced(void)
 }
 EXPORT_SYMBOL(acpi_resources_are_enforced);
 
+bool acpi_osi_is_win8(void)
+{
+       return acpi_gbl_osi_data >= ACPI_OSI_WIN_8;
+}
+EXPORT_SYMBOL(acpi_osi_is_win8);
+
 /*
  * Deallocate the memory for a spinlock.
  */
index b1def411c0b89cbf7847b767063c5c2ab528e8a8..304eccb0ae5cf32042885ceebb4f1feeae98adce 100644 (file)
@@ -44,7 +44,6 @@
 ACPI_MODULE_NAME("pci_irq");
 
 struct acpi_prt_entry {
-       struct list_head        list;
        struct acpi_pci_id      id;
        u8                      pin;
        acpi_handle             link;
@@ -163,7 +162,7 @@ static int acpi_pci_irq_check_entry(acpi_handle handle, struct pci_dev *dev,
 {
        int segment = pci_domain_nr(dev->bus);
        int bus = dev->bus->number;
-       int device = PCI_SLOT(dev->devfn);
+       int device = pci_ari_enabled(dev->bus) ? 0 : PCI_SLOT(dev->devfn);
        struct acpi_prt_entry *entry;
 
        if (((prt->address >> 16) & 0xffff) != device ||
index e0bcfb642b52654f1dfb2f8948c1cac62ff96c68..93eac53b5110bc67f79077fdd17c73b8b3d79235 100644 (file)
@@ -684,7 +684,8 @@ int acpi_power_get_inferred_state(struct acpi_device *device, int *state)
                }
        }
 
-       *state = ACPI_STATE_D3_COLD;
+       *state = device->power.states[ACPI_STATE_D3_COLD].flags.valid ?
+               ACPI_STATE_D3_COLD : ACPI_STATE_D3_HOT;
        return 0;
 }
 
@@ -710,8 +711,6 @@ int acpi_power_transition(struct acpi_device *device, int state)
            || (device->power.state > ACPI_STATE_D3_COLD))
                return -ENODEV;
 
-       /* TBD: Resources must be ordered. */
-
        /*
         * First we reference all power resources required in the target list
         * (e.g. so the device doesn't lose power while transitioning).  Then,
@@ -761,6 +760,25 @@ static void acpi_power_sysfs_remove(struct acpi_device *device)
        device_remove_file(&device->dev, &dev_attr_resource_in_use);
 }
 
+static void acpi_power_add_resource_to_list(struct acpi_power_resource *resource)
+{
+       mutex_lock(&power_resource_list_lock);
+
+       if (!list_empty(&acpi_power_resource_list)) {
+               struct acpi_power_resource *r;
+
+               list_for_each_entry(r, &acpi_power_resource_list, list_node)
+                       if (r->order > resource->order) {
+                               list_add_tail(&resource->list_node, &r->list_node);
+                               goto out;
+                       }
+       }
+       list_add_tail(&resource->list_node, &acpi_power_resource_list);
+
+ out:
+       mutex_unlock(&power_resource_list_lock);
+}
+
 int acpi_add_power_resource(acpi_handle handle)
 {
        struct acpi_power_resource *resource;
@@ -811,9 +829,7 @@ int acpi_add_power_resource(acpi_handle handle)
        if (!device_create_file(&device->dev, &dev_attr_resource_in_use))
                device->remove = acpi_power_sysfs_remove;
 
-       mutex_lock(&power_resource_list_lock);
-       list_add(&resource->list_node, &acpi_power_resource_list);
-       mutex_unlock(&power_resource_list_lock);
+       acpi_power_add_resource_to_list(resource);
        acpi_device_add_finalize(device);
        return 0;
 
@@ -844,7 +860,22 @@ void acpi_resume_power_resources(void)
                    && resource->ref_count) {
                        dev_info(&resource->device.dev, "Turning ON\n");
                        __acpi_power_on(resource);
-               } else if (state == ACPI_POWER_RESOURCE_STATE_ON
+               }
+
+               mutex_unlock(&resource->resource_lock);
+       }
+       list_for_each_entry_reverse(resource, &acpi_power_resource_list, list_node) {
+               int result, state;
+
+               mutex_lock(&resource->resource_lock);
+
+               result = acpi_power_get_state(resource->device.handle, &state);
+               if (result) {
+                       mutex_unlock(&resource->resource_lock);
+                       continue;
+               }
+
+               if (state == ACPI_POWER_RESOURCE_STATE_ON
                    && !resource->ref_count) {
                        dev_info(&resource->device.dev, "Turning OFF\n");
                        __acpi_power_off(resource);
index b1ec78b8a6455c68f08af8123a529e5513475b03..33a38d60463009197f0f80db545ce5e15ce7f880 100644 (file)
@@ -184,7 +184,7 @@ phys_cpuid_t acpi_get_phys_id(acpi_handle handle, int type, u32 acpi_id)
        phys_cpuid_t phys_id;
 
        phys_id = map_mat_entry(handle, type, acpi_id);
-       if (phys_id == PHYS_CPUID_INVALID)
+       if (invalid_phys_cpuid(phys_id))
                phys_id = map_madt_entry(type, acpi_id);
 
        return phys_id;
@@ -196,7 +196,7 @@ int acpi_map_cpuid(phys_cpuid_t phys_id, u32 acpi_id)
        int i;
 #endif
 
-       if (phys_id == PHYS_CPUID_INVALID) {
+       if (invalid_phys_cpuid(phys_id)) {
                /*
                 * On UP processor, there is no _MAT or MADT table.
                 * So above phys_id is always set to PHYS_CPUID_INVALID.
@@ -215,12 +215,12 @@ int acpi_map_cpuid(phys_cpuid_t phys_id, u32 acpi_id)
                 * Ignores phys_id and always returns 0 for the processor
                 * handle with acpi id 0 if nr_cpu_ids is 1.
                 * This should be the case if SMP tables are not found.
-                * Return -1 for other CPU's handle.
+                * Return -EINVAL for other CPU's handle.
                 */
                if (nr_cpu_ids <= 1 && acpi_id == 0)
                        return acpi_id;
                else
-                       return -1;
+                       return -EINVAL;
        }
 
 #ifdef CONFIG_SMP
@@ -233,7 +233,7 @@ int acpi_map_cpuid(phys_cpuid_t phys_id, u32 acpi_id)
        if (phys_id == 0)
                return phys_id;
 #endif
-       return -1;
+       return -ENODEV;
 }
 
 int acpi_get_cpuid(acpi_handle handle, int type, u32 acpi_id)
index 39e0c8e36244f75aac3de4f09a0a89817e035b91..d540f42c9232818b7f0a1f524eb6244122dcc8b6 100644 (file)
@@ -94,7 +94,7 @@ static int set_max_cstate(const struct dmi_system_id *id)
        return 0;
 }
 
-static struct dmi_system_id processor_power_dmi_table[] = {
+static const struct dmi_system_id processor_power_dmi_table[] = {
        { set_max_cstate, "Clevo 5600D", {
          DMI_MATCH(DMI_BIOS_VENDOR,"Phoenix Technologies LTD"),
          DMI_MATCH(DMI_BIOS_VERSION,"SHE845M0.86C.0013.D.0302131307")},
index e5dd8080093038d1755f9b7474b83e74ff62cc64..7cfbda4d7c512d19801c0813ce67ac083f445097 100644 (file)
@@ -52,10 +52,7 @@ static bool __init processor_physically_present(acpi_handle handle)
        type = (acpi_type == ACPI_TYPE_DEVICE) ? 1 : 0;
        cpuid = acpi_get_cpuid(handle, type, acpi_id);
 
-       if (cpuid == -1)
-               return false;
-
-       return true;
+       return !invalid_logical_cpuid(cpuid);
 }
 
 static void acpi_set_pdc_bits(u32 *buf)
index 0d083736e25babf920140d9b0c88a84b47d6d729..7836e2e980f41943010825429f0c0c42a575362c 100644 (file)
@@ -79,50 +79,51 @@ static bool acpi_properties_format_valid(const union acpi_object *properties)
 static void acpi_init_of_compatible(struct acpi_device *adev)
 {
        const union acpi_object *of_compatible;
-       struct acpi_hardware_id *hwid;
-       bool acpi_of = false;
        int ret;
 
-       /*
-        * Check if the special PRP0001 ACPI ID is present and in that
-        * case we fill in Device Tree compatible properties for this
-        * device.
-        */
-       list_for_each_entry(hwid, &adev->pnp.ids, list) {
-               if (!strcmp(hwid->id, "PRP0001")) {
-                       acpi_of = true;
-                       break;
-               }
-       }
-
-       if (!acpi_of)
-               return;
-
        ret = acpi_dev_get_property_array(adev, "compatible", ACPI_TYPE_STRING,
                                          &of_compatible);
        if (ret) {
                ret = acpi_dev_get_property(adev, "compatible",
                                            ACPI_TYPE_STRING, &of_compatible);
                if (ret) {
-                       acpi_handle_warn(adev->handle,
-                                        "PRP0001 requires compatible property\n");
+                       if (adev->parent
+                           && adev->parent->flags.of_compatible_ok)
+                               goto out;
+
                        return;
                }
        }
        adev->data.of_compatible = of_compatible;
+
+ out:
+       adev->flags.of_compatible_ok = 1;
 }
 
 void acpi_init_properties(struct acpi_device *adev)
 {
        struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER };
+       bool acpi_of = false;
+       struct acpi_hardware_id *hwid;
        const union acpi_object *desc;
        acpi_status status;
        int i;
 
+       /*
+        * Check if ACPI_DT_NAMESPACE_HID is present and inthat case we fill in
+        * Device Tree compatible properties for this device.
+        */
+       list_for_each_entry(hwid, &adev->pnp.ids, list) {
+               if (!strcmp(hwid->id, ACPI_DT_NAMESPACE_HID)) {
+                       acpi_of = true;
+                       break;
+               }
+       }
+
        status = acpi_evaluate_object_typed(adev->handle, "_DSD", NULL, &buf,
                                            ACPI_TYPE_PACKAGE);
        if (ACPI_FAILURE(status))
-               return;
+               goto out;
 
        desc = buf.pointer;
        if (desc->package.count % 2)
@@ -156,13 +157,20 @@ void acpi_init_properties(struct acpi_device *adev)
                adev->data.pointer = buf.pointer;
                adev->data.properties = properties;
 
-               acpi_init_of_compatible(adev);
-               return;
+               if (acpi_of)
+                       acpi_init_of_compatible(adev);
+
+               goto out;
        }
 
  fail:
-       dev_warn(&adev->dev, "Returned _DSD data is not valid, skipping\n");
+       dev_dbg(&adev->dev, "Returned _DSD data is not valid, skipping\n");
        ACPI_FREE(buf.pointer);
+
+ out:
+       if (acpi_of && !adev->flags.of_compatible_ok)
+               acpi_handle_info(adev->handle,
+                        ACPI_DT_NAMESPACE_HID " requires 'compatible' property\n");
 }
 
 void acpi_free_properties(struct acpi_device *adev)
index 8244f013f21095a9508e80ef01621e0ffbaab106..fcb7807ea8b73de79163bb99c20091f4b202da0d 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/device.h>
 #include <linux/export.h>
 #include <linux/ioport.h>
+#include <linux/list.h>
 #include <linux/slab.h>
 
 #ifdef CONFIG_X86
@@ -621,3 +622,162 @@ int acpi_dev_filter_resource_type(struct acpi_resource *ares,
        return (type & types) ? 0 : 1;
 }
 EXPORT_SYMBOL_GPL(acpi_dev_filter_resource_type);
+
+struct reserved_region {
+       struct list_head node;
+       u64 start;
+       u64 end;
+};
+
+static LIST_HEAD(reserved_io_regions);
+static LIST_HEAD(reserved_mem_regions);
+
+static int request_range(u64 start, u64 end, u8 space_id, unsigned long flags,
+                        char *desc)
+{
+       unsigned int length = end - start + 1;
+       struct resource *res;
+
+       res = space_id == ACPI_ADR_SPACE_SYSTEM_IO ?
+               request_region(start, length, desc) :
+               request_mem_region(start, length, desc);
+       if (!res)
+               return -EIO;
+
+       res->flags &= ~flags;
+       return 0;
+}
+
+static int add_region_before(u64 start, u64 end, u8 space_id,
+                            unsigned long flags, char *desc,
+                            struct list_head *head)
+{
+       struct reserved_region *reg;
+       int error;
+
+       reg = kmalloc(sizeof(*reg), GFP_KERNEL);
+       if (!reg)
+               return -ENOMEM;
+
+       error = request_range(start, end, space_id, flags, desc);
+       if (error)
+               return error;
+
+       reg->start = start;
+       reg->end = end;
+       list_add_tail(&reg->node, head);
+       return 0;
+}
+
+/**
+ * acpi_reserve_region - Reserve an I/O or memory region as a system resource.
+ * @start: Starting address of the region.
+ * @length: Length of the region.
+ * @space_id: Identifier of address space to reserve the region from.
+ * @flags: Resource flags to clear for the region after requesting it.
+ * @desc: Region description (for messages).
+ *
+ * Reserve an I/O or memory region as a system resource to prevent others from
+ * using it.  If the new region overlaps with one of the regions (in the given
+ * address space) already reserved by this routine, only the non-overlapping
+ * parts of it will be reserved.
+ *
+ * Returned is either 0 (success) or a negative error code indicating a resource
+ * reservation problem.  It is the code of the first encountered error, but the
+ * routine doesn't abort until it has attempted to request all of the parts of
+ * the new region that don't overlap with other regions reserved previously.
+ *
+ * The resources requested by this routine are never released.
+ */
+int acpi_reserve_region(u64 start, unsigned int length, u8 space_id,
+                       unsigned long flags, char *desc)
+{
+       struct list_head *regions;
+       struct reserved_region *reg;
+       u64 end = start + length - 1;
+       int ret = 0, error = 0;
+
+       if (space_id == ACPI_ADR_SPACE_SYSTEM_IO)
+               regions = &reserved_io_regions;
+       else if (space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY)
+               regions = &reserved_mem_regions;
+       else
+               return -EINVAL;
+
+       if (list_empty(regions))
+               return add_region_before(start, end, space_id, flags, desc, regions);
+
+       list_for_each_entry(reg, regions, node)
+               if (reg->start == end + 1) {
+                       /* The new region can be prepended to this one. */
+                       ret = request_range(start, end, space_id, flags, desc);
+                       if (!ret)
+                               reg->start = start;
+
+                       return ret;
+               } else if (reg->start > end) {
+                       /* No overlap.  Add the new region here and get out. */
+                       return add_region_before(start, end, space_id, flags,
+                                                desc, &reg->node);
+               } else if (reg->end == start - 1) {
+                       goto combine;
+               } else if (reg->end >= start) {
+                       goto overlap;
+               }
+
+       /* The new region goes after the last existing one. */
+       return add_region_before(start, end, space_id, flags, desc, regions);
+
+ overlap:
+       /*
+        * The new region overlaps an existing one.
+        *
+        * The head part of the new region immediately preceding the existing
+        * overlapping one can be combined with it right away.
+        */
+       if (reg->start > start) {
+               error = request_range(start, reg->start - 1, space_id, flags, desc);
+               if (error)
+                       ret = error;
+               else
+                       reg->start = start;
+       }
+
+ combine:
+       /*
+        * The new region is adjacent to an existing one.  If it extends beyond
+        * that region all the way to the next one, it is possible to combine
+        * all three of them.
+        */
+       while (reg->end < end) {
+               struct reserved_region *next = NULL;
+               u64 a = reg->end + 1, b = end;
+
+               if (!list_is_last(&reg->node, regions)) {
+                       next = list_next_entry(reg, node);
+                       if (next->start <= end)
+                               b = next->start - 1;
+               }
+               error = request_range(a, b, space_id, flags, desc);
+               if (!error) {
+                       if (next && next->start == b + 1) {
+                               reg->end = next->end;
+                               list_del(&next->node);
+                               kfree(next);
+                       } else {
+                               reg->end = end;
+                               break;
+                       }
+               } else if (next) {
+                       if (!ret)
+                               ret = error;
+
+                       reg = next;
+               } else {
+                       break;
+               }
+       }
+
+       return ret ? ret : error;
+}
+EXPORT_SYMBOL_GPL(acpi_reserve_region);
index 03141aa4ea9500bce3a973605ecbf203af96a060..2649a068671d3914d5f1eae97e4d91b1c2c56ed1 100644 (file)
@@ -11,6 +11,7 @@
 #include <linux/kthread.h>
 #include <linux/dmi.h>
 #include <linux/nls.h>
+#include <linux/dma-mapping.h>
 
 #include <asm/pgtable.h>
 
@@ -135,12 +136,13 @@ static int create_pnp_modalias(struct acpi_device *acpi_dev, char *modalias,
        struct acpi_hardware_id *id;
 
        /*
-        * Since we skip PRP0001 from the modalias below, 0 should be returned
-        * if PRP0001 is the only ACPI/PNP ID in the device's list.
+        * Since we skip ACPI_DT_NAMESPACE_HID from the modalias below, 0 should
+        * be returned if ACPI_DT_NAMESPACE_HID is the only ACPI/PNP ID in the
+        * device's list.
         */
        count = 0;
        list_for_each_entry(id, &acpi_dev->pnp.ids, list)
-               if (strcmp(id->id, "PRP0001"))
+               if (strcmp(id->id, ACPI_DT_NAMESPACE_HID))
                        count++;
 
        if (!count)
@@ -153,7 +155,7 @@ static int create_pnp_modalias(struct acpi_device *acpi_dev, char *modalias,
        size -= len;
 
        list_for_each_entry(id, &acpi_dev->pnp.ids, list) {
-               if (!strcmp(id->id, "PRP0001"))
+               if (!strcmp(id->id, ACPI_DT_NAMESPACE_HID))
                        continue;
 
                count = snprintf(&modalias[len], size, "%s:", id->id);
@@ -177,7 +179,8 @@ static int create_pnp_modalias(struct acpi_device *acpi_dev, char *modalias,
  * @size: Size of the buffer.
  *
  * Expose DT compatible modalias as of:NnameTCcompatible.  This function should
- * only be called for devices having PRP0001 in their list of ACPI/PNP IDs.
+ * only be called for devices having ACPI_DT_NAMESPACE_HID in their list of
+ * ACPI/PNP IDs.
  */
 static int create_of_modalias(struct acpi_device *acpi_dev, char *modalias,
                              int size)
@@ -980,9 +983,9 @@ static void acpi_device_remove_files(struct acpi_device *dev)
  * @adev: ACPI device object to match.
  * @of_match_table: List of device IDs to match against.
  *
- * If @dev has an ACPI companion which has the special PRP0001 device ID in its
- * list of identifiers and a _DSD object with the "compatible" property, use
- * that property to match against the given list of identifiers.
+ * If @dev has an ACPI companion which has ACPI_DT_NAMESPACE_HID in its list of
+ * identifiers and a _DSD object with the "compatible" property, use that
+ * property to match against the given list of identifiers.
  */
 static bool acpi_of_match_device(struct acpi_device *adev,
                                 const struct of_device_id *of_match_table)
@@ -1038,14 +1041,14 @@ static const struct acpi_device_id *__acpi_match_device(
                                return id;
 
                /*
-                * Next, check the special "PRP0001" ID and try to match the
+                * Next, check ACPI_DT_NAMESPACE_HID and try to match the
                 * "compatible" property if found.
                 *
                 * The id returned by the below is not valid, but the only
                 * caller passing non-NULL of_ids here is only interested in
                 * whether or not the return value is NULL.
                 */
-               if (!strcmp("PRP0001", hwid->id)
+               if (!strcmp(ACPI_DT_NAMESPACE_HID, hwid->id)
                    && acpi_of_match_device(device, of_ids))
                        return id;
        }
@@ -1671,7 +1674,7 @@ static int acpi_bus_extract_wakeup_device_power_package(acpi_handle handle,
 
 static void acpi_wakeup_gpe_init(struct acpi_device *device)
 {
-       struct acpi_device_id button_device_ids[] = {
+       static const struct acpi_device_id button_device_ids[] = {
                {"PNP0C0C", 0},
                {"PNP0C0D", 0},
                {"PNP0C0E", 0},
@@ -1766,15 +1769,9 @@ static void acpi_bus_init_power_state(struct acpi_device *device, int state)
        if (acpi_has_method(device->handle, pathname))
                ps->flags.explicit_set = 1;
 
-       /*
-        * State is valid if there are means to put the device into it.
-        * D3hot is only valid if _PR3 present.
-        */
-       if (!list_empty(&ps->resources)
-           || (ps->flags.explicit_set && state < ACPI_STATE_D3_HOT)) {
+       /* State is valid if there are means to put the device into it. */
+       if (!list_empty(&ps->resources) || ps->flags.explicit_set)
                ps->flags.valid = 1;
-               ps->flags.os_accessible = 1;
-       }
 
        ps->power = -1;         /* Unknown - driver assigned */
        ps->latency = -1;       /* Unknown - driver assigned */
@@ -1810,21 +1807,13 @@ static void acpi_bus_get_power_flags(struct acpi_device *device)
                acpi_bus_init_power_state(device, i);
 
        INIT_LIST_HEAD(&device->power.states[ACPI_STATE_D3_COLD].resources);
+       if (!list_empty(&device->power.states[ACPI_STATE_D3_HOT].resources))
+               device->power.states[ACPI_STATE_D3_COLD].flags.valid = 1;
 
-       /* Set defaults for D0 and D3 states (always valid) */
+       /* Set defaults for D0 and D3hot states (always valid) */
        device->power.states[ACPI_STATE_D0].flags.valid = 1;
        device->power.states[ACPI_STATE_D0].power = 100;
-       device->power.states[ACPI_STATE_D3_COLD].flags.valid = 1;
-       device->power.states[ACPI_STATE_D3_COLD].power = 0;
-
-       /* Set D3cold's explicit_set flag if _PS3 exists. */
-       if (device->power.states[ACPI_STATE_D3_HOT].flags.explicit_set)
-               device->power.states[ACPI_STATE_D3_COLD].flags.explicit_set = 1;
-
-       /* Presence of _PS3 or _PRx means we can put the device into D3 cold */
-       if (device->power.states[ACPI_STATE_D3_HOT].flags.explicit_set ||
-                       device->power.flags.power_resources)
-               device->power.states[ACPI_STATE_D3_COLD].flags.os_accessible = 1;
+       device->power.states[ACPI_STATE_D3_HOT].flags.valid = 1;
 
        if (acpi_bus_init_power(device))
                device->flags.power_manageable = 0;
@@ -1947,6 +1936,62 @@ bool acpi_dock_match(acpi_handle handle)
        return acpi_has_method(handle, "_DCK");
 }
 
+static acpi_status
+acpi_backlight_cap_match(acpi_handle handle, u32 level, void *context,
+                         void **return_value)
+{
+       long *cap = context;
+
+       if (acpi_has_method(handle, "_BCM") &&
+           acpi_has_method(handle, "_BCL")) {
+               ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found generic backlight "
+                                 "support\n"));
+               *cap |= ACPI_VIDEO_BACKLIGHT;
+               if (!acpi_has_method(handle, "_BQC"))
+                       printk(KERN_WARNING FW_BUG PREFIX "No _BQC method, "
+                               "cannot determine initial brightness\n");
+               /* We have backlight support, no need to scan further */
+               return AE_CTRL_TERMINATE;
+       }
+       return 0;
+}
+
+/* Returns true if the ACPI object is a video device which can be
+ * handled by video.ko.
+ * The device will get a Linux specific CID added in scan.c to
+ * identify the device as an ACPI graphics device
+ * Be aware that the graphics device may not be physically present
+ * Use acpi_video_get_capabilities() to detect general ACPI video
+ * capabilities of present cards
+ */
+long acpi_is_video_device(acpi_handle handle)
+{
+       long video_caps = 0;
+
+       /* Is this device able to support video switching ? */
+       if (acpi_has_method(handle, "_DOD") || acpi_has_method(handle, "_DOS"))
+               video_caps |= ACPI_VIDEO_OUTPUT_SWITCHING;
+
+       /* Is this device able to retrieve a video ROM ? */
+       if (acpi_has_method(handle, "_ROM"))
+               video_caps |= ACPI_VIDEO_ROM_AVAILABLE;
+
+       /* Is this device able to configure which video head to be POSTed ? */
+       if (acpi_has_method(handle, "_VPO") &&
+           acpi_has_method(handle, "_GPD") &&
+           acpi_has_method(handle, "_SPD"))
+               video_caps |= ACPI_VIDEO_DEVICE_POSTING;
+
+       /* Only check for backlight functionality if one of the above hit. */
+       if (video_caps)
+               acpi_walk_namespace(ACPI_TYPE_DEVICE, handle,
+                                   ACPI_UINT32_MAX, acpi_backlight_cap_match, NULL,
+                                   &video_caps, NULL);
+
+       return video_caps;
+}
+EXPORT_SYMBOL(acpi_is_video_device);
+
 const char *acpi_device_hid(struct acpi_device *device)
 {
        struct acpi_hardware_id *hid;
@@ -2109,6 +2154,39 @@ void acpi_free_pnp_ids(struct acpi_device_pnp *pnp)
        kfree(pnp->unique_id);
 }
 
+static void acpi_init_coherency(struct acpi_device *adev)
+{
+       unsigned long long cca = 0;
+       acpi_status status;
+       struct acpi_device *parent = adev->parent;
+
+       if (parent && parent->flags.cca_seen) {
+               /*
+                * From ACPI spec, OSPM will ignore _CCA if an ancestor
+                * already saw one.
+                */
+               adev->flags.cca_seen = 1;
+               cca = parent->flags.coherent_dma;
+       } else {
+               status = acpi_evaluate_integer(adev->handle, "_CCA",
+                                              NULL, &cca);
+               if (ACPI_SUCCESS(status))
+                       adev->flags.cca_seen = 1;
+               else if (!IS_ENABLED(CONFIG_ACPI_CCA_REQUIRED))
+                       /*
+                        * If architecture does not specify that _CCA is
+                        * required for DMA-able devices (e.g. x86),
+                        * we default to _CCA=1.
+                        */
+                       cca = 1;
+               else
+                       acpi_handle_debug(adev->handle,
+                                         "ACPI device is missing _CCA.\n");
+       }
+
+       adev->flags.coherent_dma = cca;
+}
+
 void acpi_init_device_object(struct acpi_device *device, acpi_handle handle,
                             int type, unsigned long long sta)
 {
@@ -2127,6 +2205,7 @@ void acpi_init_device_object(struct acpi_device *device, acpi_handle handle,
        device->flags.visited = false;
        device_initialize(&device->dev);
        dev_set_uevent_suppress(&device->dev, true);
+       acpi_init_coherency(device);
 }
 
 void acpi_device_add_finalize(struct acpi_device *device)
@@ -2405,7 +2484,7 @@ static void acpi_default_enumeration(struct acpi_device *device)
 }
 
 static const struct acpi_device_id generic_device_ids[] = {
-       {"PRP0001", },
+       {ACPI_DT_NAMESPACE_HID, },
        {"", },
 };
 
@@ -2413,8 +2492,8 @@ static int acpi_generic_device_attach(struct acpi_device *adev,
                                      const struct acpi_device_id *not_used)
 {
        /*
-        * Since PRP0001 is the only ID handled here, the test below can be
-        * unconditional.
+        * Since ACPI_DT_NAMESPACE_HID is the only ID handled here, the test
+        * below can be unconditional.
         */
        if (adev->data.of_compatible)
                acpi_default_enumeration(adev);
index cd49a3982b6ab9563bf18e4ea7c21ed58febd4d9..67c548ad3764c938c87d9a41a271f4d3d0554487 100644 (file)
@@ -712,3 +712,18 @@ bool acpi_check_dsm(acpi_handle handle, const u8 *uuid, int rev, u64 funcs)
        return false;
 }
 EXPORT_SYMBOL(acpi_check_dsm);
+
+/*
+ * acpi_backlight= handling, this is done here rather then in video_detect.c
+ * because __setup cannot be used in modules.
+ */
+char acpi_video_backlight_string[16];
+EXPORT_SYMBOL(acpi_video_backlight_string);
+
+static int __init acpi_backlight(char *str)
+{
+       strlcpy(acpi_video_backlight_string, str,
+               sizeof(acpi_video_backlight_string));
+       return 1;
+}
+__setup("acpi_backlight=", acpi_backlight);
index c42feb2bacd0eb783bc94f0e10187d08ea907eea..815f75ef24119eab28ce3c0c2047295c6e464c58 100644 (file)
 /*
+ *  Copyright (C) 2015       Red Hat Inc.
+ *                           Hans de Goede <hdegoede@redhat.com>
  *  Copyright (C) 2008       SuSE Linux Products GmbH
  *                           Thomas Renninger <trenn@suse.de>
  *
  *  May be copied or modified under the terms of the GNU General Public License
  *
  * video_detect.c:
- * Provides acpi_is_video_device() for early scanning of ACPI devices in scan.c
- * There a Linux specific (Spec does not provide a HID for video devices) is
- * assigned
- *
  * After PCI devices are glued with ACPI devices
  * acpi_get_pci_dev() can be called to identify ACPI graphics
  * devices for which a real graphics card is plugged in
  *
- * Now acpi_video_get_capabilities() can be called to check which
- * capabilities the graphics cards plugged in support. The check for general
- * video capabilities will be triggered by the first caller of
- * acpi_video_get_capabilities(NULL); which will happen when the first
- * backlight switching supporting driver calls:
- * acpi_video_backlight_support();
- *
  * Depending on whether ACPI graphics extensions (cmp. ACPI spec Appendix B)
  * are available, video.ko should be used to handle the device.
  *
  * Otherwise vendor specific drivers like thinkpad_acpi, asus-laptop,
  * sony_acpi,... can take care about backlight brightness.
  *
- * If CONFIG_ACPI_VIDEO is neither set as "compiled in" (y) nor as a module (m)
- * this file will not be compiled, acpi_video_get_capabilities() and
- * acpi_video_backlight_support() will always return 0 and vendor specific
- * drivers always can handle backlight.
+ * Backlight drivers can use acpi_video_get_backlight_type() to determine
+ * which driver should handle the backlight.
  *
+ * If CONFIG_ACPI_VIDEO is neither set as "compiled in" (y) nor as a module (m)
+ * this file will not be compiled and acpi_video_get_backlight_type() will
+ * always return acpi_backlight_vendor.
  */
 
 #include <linux/export.h>
 #include <linux/acpi.h>
+#include <linux/backlight.h>
 #include <linux/dmi.h>
+#include <linux/module.h>
 #include <linux/pci.h>
-
-#include "internal.h"
+#include <linux/types.h>
+#include <acpi/video.h>
 
 ACPI_MODULE_NAME("video");
 #define _COMPONENT             ACPI_VIDEO_COMPONENT
 
-static long acpi_video_support;
-static bool acpi_video_caps_checked;
+void acpi_video_unregister_backlight(void);
 
-static acpi_status
-acpi_backlight_cap_match(acpi_handle handle, u32 level, void *context,
-                         void **return_value)
-{
-       long *cap = context;
+static bool backlight_notifier_registered;
+static struct notifier_block backlight_nb;
 
-       if (acpi_has_method(handle, "_BCM") &&
-           acpi_has_method(handle, "_BCL")) {
-               ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found generic backlight "
-                                 "support\n"));
-               *cap |= ACPI_VIDEO_BACKLIGHT;
-               if (!acpi_has_method(handle, "_BQC"))
-                       printk(KERN_WARNING FW_BUG PREFIX "No _BQC method, "
-                               "cannot determine initial brightness\n");
-               /* We have backlight support, no need to scan further */
-               return AE_CTRL_TERMINATE;
-       }
-       return 0;
-}
+static enum acpi_backlight_type acpi_backlight_cmdline = acpi_backlight_undef;
+static enum acpi_backlight_type acpi_backlight_dmi = acpi_backlight_undef;
 
-/* Returns true if the ACPI object is a video device which can be
- * handled by video.ko.
- * The device will get a Linux specific CID added in scan.c to
- * identify the device as an ACPI graphics device
- * Be aware that the graphics device may not be physically present
- * Use acpi_video_get_capabilities() to detect general ACPI video
- * capabilities of present cards
- */
-long acpi_is_video_device(acpi_handle handle)
+static void acpi_video_parse_cmdline(void)
 {
-       long video_caps = 0;
-
-       /* Is this device able to support video switching ? */
-       if (acpi_has_method(handle, "_DOD") || acpi_has_method(handle, "_DOS"))
-               video_caps |= ACPI_VIDEO_OUTPUT_SWITCHING;
-
-       /* Is this device able to retrieve a video ROM ? */
-       if (acpi_has_method(handle, "_ROM"))
-               video_caps |= ACPI_VIDEO_ROM_AVAILABLE;
-
-       /* Is this device able to configure which video head to be POSTed ? */
-       if (acpi_has_method(handle, "_VPO") &&
-           acpi_has_method(handle, "_GPD") &&
-           acpi_has_method(handle, "_SPD"))
-               video_caps |= ACPI_VIDEO_DEVICE_POSTING;
-
-       /* Only check for backlight functionality if one of the above hit. */
-       if (video_caps)
-               acpi_walk_namespace(ACPI_TYPE_DEVICE, handle,
-                                   ACPI_UINT32_MAX, acpi_backlight_cap_match, NULL,
-                                   &video_caps, NULL);
-
-       return video_caps;
+       if (!strcmp("vendor", acpi_video_backlight_string))
+               acpi_backlight_cmdline = acpi_backlight_vendor;
+       if (!strcmp("video", acpi_video_backlight_string))
+               acpi_backlight_cmdline = acpi_backlight_video;
+       if (!strcmp("native", acpi_video_backlight_string))
+               acpi_backlight_cmdline = acpi_backlight_native;
+       if (!strcmp("none", acpi_video_backlight_string))
+               acpi_backlight_cmdline = acpi_backlight_none;
 }
-EXPORT_SYMBOL(acpi_is_video_device);
 
 static acpi_status
 find_video(acpi_handle handle, u32 lvl, void *context, void **rv)
@@ -109,7 +64,7 @@ find_video(acpi_handle handle, u32 lvl, void *context, void **rv)
        struct pci_dev *dev;
        struct acpi_device *acpi_dev;
 
-       const struct acpi_device_id video_ids[] = {
+       static const struct acpi_device_id video_ids[] = {
                {ACPI_VIDEO_HID, 0},
                {"", 0},
        };
@@ -130,11 +85,23 @@ find_video(acpi_handle handle, u32 lvl, void *context, void **rv)
  * buggy */
 static int video_detect_force_vendor(const struct dmi_system_id *d)
 {
-       acpi_video_support |= ACPI_VIDEO_BACKLIGHT_DMI_VENDOR;
+       acpi_backlight_dmi = acpi_backlight_vendor;
+       return 0;
+}
+
+static int video_detect_force_video(const struct dmi_system_id *d)
+{
+       acpi_backlight_dmi = acpi_backlight_video;
        return 0;
 }
 
-static struct dmi_system_id video_detect_dmi_table[] = {
+static int video_detect_force_native(const struct dmi_system_id *d)
+{
+       acpi_backlight_dmi = acpi_backlight_native;
+       return 0;
+}
+
+static const struct dmi_system_id video_detect_dmi_table[] = {
        /* On Samsung X360, the BIOS will set a flag (VDRV) if generic
         * ACPI backlight device is used. This flag will definitively break
         * the backlight interface (even the vendor interface) untill next
@@ -174,137 +141,209 @@ static struct dmi_system_id video_detect_dmi_table[] = {
                DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 5737"),
                },
        },
+
+       /*
+        * These models have a working acpi_video backlight control, and using
+        * native backlight causes a regression where backlight does not work
+        * when userspace is not handling brightness key events. Disable
+        * native_backlight on these to fix this:
+        * https://bugzilla.kernel.org/show_bug.cgi?id=81691
+        */
+       {
+        .callback = video_detect_force_video,
+        .ident = "ThinkPad T420",
+        .matches = {
+               DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+               DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad T420"),
+               },
+       },
+       {
+        .callback = video_detect_force_video,
+        .ident = "ThinkPad T520",
+        .matches = {
+               DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+               DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad T520"),
+               },
+       },
+       {
+        .callback = video_detect_force_video,
+        .ident = "ThinkPad X201s",
+        .matches = {
+               DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+               DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad X201s"),
+               },
+       },
+
+       /* The native backlight controls do not work on some older machines */
+       {
+        /* https://bugs.freedesktop.org/show_bug.cgi?id=81515 */
+        .callback = video_detect_force_video,
+        .ident = "HP ENVY 15 Notebook",
+        .matches = {
+               DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
+               DMI_MATCH(DMI_PRODUCT_NAME, "HP ENVY 15 Notebook PC"),
+               },
+       },
+       {
+        .callback = video_detect_force_video,
+        .ident = "SAMSUNG 870Z5E/880Z5E/680Z5E",
+        .matches = {
+               DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
+               DMI_MATCH(DMI_PRODUCT_NAME, "870Z5E/880Z5E/680Z5E"),
+               },
+       },
+       {
+        .callback = video_detect_force_video,
+        .ident = "SAMSUNG 370R4E/370R4V/370R5E/3570RE/370R5V",
+        .matches = {
+               DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
+               DMI_MATCH(DMI_PRODUCT_NAME,
+                         "370R4E/370R4V/370R5E/3570RE/370R5V"),
+               },
+       },
+       {
+        /* https://bugzilla.redhat.com/show_bug.cgi?id=1186097 */
+        .callback = video_detect_force_video,
+        .ident = "SAMSUNG 3570R/370R/470R/450R/510R/4450RV",
+        .matches = {
+               DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
+               DMI_MATCH(DMI_PRODUCT_NAME,
+                         "3570R/370R/470R/450R/510R/4450RV"),
+               },
+       },
+       {
+        /* https://bugzilla.redhat.com/show_bug.cgi?id=1094948 */
+        .callback = video_detect_force_video,
+        .ident = "SAMSUNG 730U3E/740U3E",
+        .matches = {
+               DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
+               DMI_MATCH(DMI_PRODUCT_NAME, "730U3E/740U3E"),
+               },
+       },
+       {
+        /* https://bugs.freedesktop.org/show_bug.cgi?id=87286 */
+        .callback = video_detect_force_video,
+        .ident = "SAMSUNG 900X3C/900X3D/900X3E/900X4C/900X4D",
+        .matches = {
+               DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
+               DMI_MATCH(DMI_PRODUCT_NAME,
+                         "900X3C/900X3D/900X3E/900X4C/900X4D"),
+               },
+       },
+       {
+        /* https://bugzilla.redhat.com/show_bug.cgi?id=1163574 */
+        .callback = video_detect_force_video,
+        .ident = "Dell XPS15 L521X",
+        .matches = {
+               DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+               DMI_MATCH(DMI_PRODUCT_NAME, "XPS L521X"),
+               },
+       },
+
+       /* Non win8 machines which need native backlight nevertheless */
+       {
+        /* https://bugzilla.redhat.com/show_bug.cgi?id=1187004 */
+        .callback = video_detect_force_native,
+        .ident = "Lenovo Ideapad Z570",
+        .matches = {
+               DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+               DMI_MATCH(DMI_PRODUCT_NAME, "102434U"),
+               },
+       },
+       {
+        /* https://bugzilla.redhat.com/show_bug.cgi?id=1217249 */
+        .callback = video_detect_force_native,
+        .ident = "Apple MacBook Pro 12,1",
+        .matches = {
+               DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
+               DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro12,1"),
+               },
+       },
        { },
 };
 
+static int acpi_video_backlight_notify(struct notifier_block *nb,
+                                      unsigned long val, void *bd)
+{
+       struct backlight_device *backlight = bd;
+
+       /* A raw bl registering may change video -> native */
+       if (backlight->props.type == BACKLIGHT_RAW &&
+           val == BACKLIGHT_REGISTERED &&
+           acpi_video_get_backlight_type() != acpi_backlight_video)
+               acpi_video_unregister_backlight();
+
+       return NOTIFY_OK;
+}
+
 /*
- * Returns the video capabilities of a specific ACPI graphics device
+ * Determine which type of backlight interface to use on this system,
+ * First check cmdline, then dmi quirks, then do autodetect.
  *
- * if NULL is passed as argument all ACPI devices are enumerated and
- * all graphics capabilities of physically present devices are
- * summarized and returned. This is cached and done only once.
+ * The autodetect order is:
+ * 1) Is the acpi-video backlight interface supported ->
+ *  no, use a vendor interface
+ * 2) Is this a win8 "ready" BIOS and do we have a native interface ->
+ *  yes, use a native interface
+ * 3) Else use the acpi-video interface
+ *
+ * Arguably the native on win8 check should be done first, but that would
+ * be a behavior change, which may causes issues.
  */
-long acpi_video_get_capabilities(acpi_handle graphics_handle)
+enum acpi_backlight_type acpi_video_get_backlight_type(void)
 {
-       long caps = 0;
-       struct acpi_device *tmp_dev;
-       acpi_status status;
-
-       if (acpi_video_caps_checked && graphics_handle == NULL)
-               return acpi_video_support;
-
-       if (!graphics_handle) {
-               /* Only do the global walk through all graphics devices once */
-               acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
-                                   ACPI_UINT32_MAX, find_video, NULL,
-                                   &caps, NULL);
-               /* There might be boot param flags set already... */
-               acpi_video_support |= caps;
-               acpi_video_caps_checked = 1;
-               /* Add blacklists here. Be careful to use the right *DMI* bits
-                * to still be able to override logic via boot params, e.g.:
-                *
-                *   if (dmi_name_in_vendors("XY")) {
-                *      acpi_video_support |=
-                *              ACPI_VIDEO_BACKLIGHT_DMI_VENDOR;
-                *}
-                */
+       static DEFINE_MUTEX(init_mutex);
+       static bool init_done;
+       static long video_caps;
 
+       /* Parse cmdline, dmi and acpi only once */
+       mutex_lock(&init_mutex);
+       if (!init_done) {
+               acpi_video_parse_cmdline();
                dmi_check_system(video_detect_dmi_table);
-       } else {
-               status = acpi_bus_get_device(graphics_handle, &tmp_dev);
-               if (ACPI_FAILURE(status)) {
-                       ACPI_EXCEPTION((AE_INFO, status, "Invalid device"));
-                       return 0;
-               }
-               acpi_walk_namespace(ACPI_TYPE_DEVICE, graphics_handle,
+               acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
                                    ACPI_UINT32_MAX, find_video, NULL,
-                                   &caps, NULL);
+                                   &video_caps, NULL);
+               backlight_nb.notifier_call = acpi_video_backlight_notify;
+               backlight_nb.priority = 0;
+               if (backlight_register_notifier(&backlight_nb) == 0)
+                       backlight_notifier_registered = true;
+               init_done = true;
        }
-       ACPI_DEBUG_PRINT((ACPI_DB_INFO, "We have 0x%lX video support %s %s\n",
-                         graphics_handle ? caps : acpi_video_support,
-                         graphics_handle ? "on device " : "in general",
-                         graphics_handle ? acpi_device_bid(tmp_dev) : ""));
-       return caps;
-}
-EXPORT_SYMBOL(acpi_video_get_capabilities);
+       mutex_unlock(&init_mutex);
 
-static void acpi_video_caps_check(void)
-{
-       /*
-        * We must check whether the ACPI graphics device is physically plugged
-        * in. Therefore this must be called after binding PCI and ACPI devices
-        */
-       if (!acpi_video_caps_checked)
-               acpi_video_get_capabilities(NULL);
-}
+       if (acpi_backlight_cmdline != acpi_backlight_undef)
+               return acpi_backlight_cmdline;
 
-bool acpi_osi_is_win8(void)
-{
-       return acpi_gbl_osi_data >= ACPI_OSI_WIN_8;
-}
-EXPORT_SYMBOL(acpi_osi_is_win8);
+       if (acpi_backlight_dmi != acpi_backlight_undef)
+               return acpi_backlight_dmi;
 
-/* Promote the vendor interface instead of the generic video module.
- * This function allow DMI blacklists to be implemented by externals
- * platform drivers instead of putting a big blacklist in video_detect.c
- * After calling this function you will probably want to call
- * acpi_video_unregister() to make sure the video module is not loaded
- */
-void acpi_video_dmi_promote_vendor(void)
-{
-       acpi_video_caps_check();
-       acpi_video_support |= ACPI_VIDEO_BACKLIGHT_DMI_VENDOR;
-}
-EXPORT_SYMBOL(acpi_video_dmi_promote_vendor);
-
-/* To be called when a driver who previously promoted the vendor
- * interface */
-void acpi_video_dmi_demote_vendor(void)
-{
-       acpi_video_caps_check();
-       acpi_video_support &= ~ACPI_VIDEO_BACKLIGHT_DMI_VENDOR;
-}
-EXPORT_SYMBOL(acpi_video_dmi_demote_vendor);
-
-/* Returns true if video.ko can do backlight switching */
-int acpi_video_backlight_support(void)
-{
-       acpi_video_caps_check();
-
-       /* First check for boot param -> highest prio */
-       if (acpi_video_support & ACPI_VIDEO_BACKLIGHT_FORCE_VENDOR)
-               return 0;
-       else if (acpi_video_support & ACPI_VIDEO_BACKLIGHT_FORCE_VIDEO)
-               return 1;
+       if (!(video_caps & ACPI_VIDEO_BACKLIGHT))
+               return acpi_backlight_vendor;
 
-       /* Then check for DMI blacklist -> second highest prio */
-       if (acpi_video_support & ACPI_VIDEO_BACKLIGHT_DMI_VENDOR)
-               return 0;
-       else if (acpi_video_support & ACPI_VIDEO_BACKLIGHT_DMI_VIDEO)
-               return 1;
+       if (acpi_osi_is_win8() && backlight_device_registered(BACKLIGHT_RAW))
+               return acpi_backlight_native;
 
-       /* Then go the default way */
-       return acpi_video_support & ACPI_VIDEO_BACKLIGHT;
+       return acpi_backlight_video;
 }
-EXPORT_SYMBOL(acpi_video_backlight_support);
+EXPORT_SYMBOL(acpi_video_get_backlight_type);
 
 /*
- * Use acpi_backlight=vendor/video to force that backlight switching
- * is processed by vendor specific acpi drivers or video.ko driver.
+ * Set the preferred backlight interface type based on DMI info.
+ * This function allows DMI blacklists to be implemented by external
+ * platform drivers instead of putting a big blacklist in video_detect.c
  */
-static int __init acpi_backlight(char *str)
+void acpi_video_set_dmi_backlight_type(enum acpi_backlight_type type)
 {
-       if (str == NULL || *str == '\0')
-               return 1;
-       else {
-               if (!strcmp("vendor", str))
-                       acpi_video_support |=
-                               ACPI_VIDEO_BACKLIGHT_FORCE_VENDOR;
-               if (!strcmp("video", str))
-                       acpi_video_support |=
-                               ACPI_VIDEO_BACKLIGHT_FORCE_VIDEO;
-       }
-       return 1;
+       acpi_backlight_dmi = type;
+       /* Remove acpi-video backlight interface if it is no longer desired */
+       if (acpi_video_get_backlight_type() != acpi_backlight_video)
+               acpi_video_unregister_backlight();
+}
+EXPORT_SYMBOL(acpi_video_set_dmi_backlight_type);
+
+void __exit acpi_video_detect_exit(void)
+{
+       if (backlight_notifier_registered)
+               backlight_unregister_notifier(&backlight_nb);
 }
-__setup("acpi_backlight=", acpi_backlight);
index 23716dd8a7ec3f569f82db531e1ed71bc330c7d6..5928d0746a270e7b6b2ee12a022b19ed731f03fe 100644 (file)
@@ -45,7 +45,7 @@ static void ahci_mvebu_mbus_config(struct ahci_host_priv *hpriv,
                writel((cs->mbus_attr << 8) |
                       (dram->mbus_dram_target_id << 4) | 1,
                       hpriv->mmio + AHCI_WINDOW_CTRL(i));
-               writel(cs->base, hpriv->mmio + AHCI_WINDOW_BASE(i));
+               writel(cs->base >> 16, hpriv->mmio + AHCI_WINDOW_BASE(i));
                writel(((cs->size - 1) & 0xffff0000),
                       hpriv->mmio + AHCI_WINDOW_SIZE(i));
        }
index 80a80548ad0a80acf28c3407e6a6048825f92af3..27245957eee3cd906f546d67853d2ebd6ce54d30 100644 (file)
@@ -1053,7 +1053,7 @@ static struct of_device_id octeon_cf_match[] = {
        },
        {},
 };
-MODULE_DEVICE_TABLE(of, octeon_i2c_match);
+MODULE_DEVICE_TABLE(of, octeon_cf_match);
 
 static struct platform_driver octeon_cf_driver = {
        .probe          = octeon_cf_probe,
index 9c2ba1c97c4257016503a8ed4d2166ac19dea9c0..df0c66cb7ad3719016436dd7eb16ab1d3234568d 100644 (file)
@@ -179,7 +179,7 @@ static int detect_cache_attributes(unsigned int cpu)
 {
        int ret;
 
-       if (init_cache_level(cpu))
+       if (init_cache_level(cpu) || !cache_leaves(cpu))
                return -ENOENT;
 
        per_cpu_cacheinfo(cpu) = kcalloc(cache_leaves(cpu),
index da033d3bab3c69d14e55d63c4286632905120ae2..48c0e220acc0a1b8192ca6b523ad35ab7073eba7 100644 (file)
@@ -8,6 +8,7 @@
 #include <linux/device.h>
 #include <linux/init.h>
 #include <linux/memory.h>
+#include <linux/of.h>
 
 #include "base.h"
 
@@ -34,4 +35,5 @@ void __init driver_init(void)
        cpu_dev_init();
        memory_dev_init();
        container_dev_init();
+       of_core_init();
 }
index 1cb8544598d5584ac8f69ece8d6e9a113a734942..f94a6ccfe78710bb386ca17d8032f9b9f2a5dd7f 100644 (file)
@@ -1,4 +1,4 @@
-obj-$(CONFIG_PM)       += sysfs.o generic_ops.o common.o qos.o runtime.o
+obj-$(CONFIG_PM)       += sysfs.o generic_ops.o common.o qos.o runtime.o wakeirq.o
 obj-$(CONFIG_PM_SLEEP) += main.o wakeup.o
 obj-$(CONFIG_PM_TRACE_RTC)     += trace.o
 obj-$(CONFIG_PM_OPP)   += opp.o
index 7fdd0172605afe1be1f74fa90593932a97153787..acef9f9f759a2530629d4a43fd5553290c6d6c54 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/clkdev.h>
 #include <linux/slab.h>
 #include <linux/err.h>
+#include <linux/pm_runtime.h>
 
 #ifdef CONFIG_PM
 
@@ -67,7 +68,8 @@ static void pm_clk_acquire(struct device *dev, struct pm_clock_entry *ce)
        } else {
                clk_prepare(ce->clk);
                ce->status = PCE_STATUS_ACQUIRED;
-               dev_dbg(dev, "Clock %s managed by runtime PM.\n", ce->con_id);
+               dev_dbg(dev, "Clock %pC con_id %s managed by runtime PM.\n",
+                       ce->clk, ce->con_id);
        }
 }
 
@@ -93,7 +95,7 @@ static int __pm_clk_add(struct device *dev, const char *con_id,
                        return -ENOMEM;
                }
        } else {
-               if (IS_ERR(ce->clk) || !__clk_get(clk)) {
+               if (IS_ERR(clk) || !__clk_get(clk)) {
                        kfree(ce);
                        return -ENOENT;
                }
@@ -367,6 +369,43 @@ static int pm_clk_notify(struct notifier_block *nb,
        return 0;
 }
 
+int pm_clk_runtime_suspend(struct device *dev)
+{
+       int ret;
+
+       dev_dbg(dev, "%s\n", __func__);
+
+       ret = pm_generic_runtime_suspend(dev);
+       if (ret) {
+               dev_err(dev, "failed to suspend device\n");
+               return ret;
+       }
+
+       ret = pm_clk_suspend(dev);
+       if (ret) {
+               dev_err(dev, "failed to suspend clock\n");
+               pm_generic_runtime_resume(dev);
+               return ret;
+       }
+
+       return 0;
+}
+
+int pm_clk_runtime_resume(struct device *dev)
+{
+       int ret;
+
+       dev_dbg(dev, "%s\n", __func__);
+
+       ret = pm_clk_resume(dev);
+       if (ret) {
+               dev_err(dev, "failed to resume clock\n");
+               return ret;
+       }
+
+       return pm_generic_runtime_resume(dev);
+}
+
 #else /* !CONFIG_PM */
 
 /**
index 2327613d453929db41e605df6614bc94a9b91067..cdd547bd67df82184dd201b4e1cf9626cf90dd2e 100644 (file)
@@ -181,7 +181,7 @@ static void genpd_recalc_cpu_exit_latency(struct generic_pm_domain *genpd)
        genpd->cpuidle_data->idle_state->exit_latency = usecs64;
 }
 
-static int genpd_power_on(struct generic_pm_domain *genpd)
+static int genpd_power_on(struct generic_pm_domain *genpd, bool timed)
 {
        ktime_t time_start;
        s64 elapsed_ns;
@@ -190,6 +190,9 @@ static int genpd_power_on(struct generic_pm_domain *genpd)
        if (!genpd->power_on)
                return 0;
 
+       if (!timed)
+               return genpd->power_on(genpd);
+
        time_start = ktime_get();
        ret = genpd->power_on(genpd);
        if (ret)
@@ -208,7 +211,7 @@ static int genpd_power_on(struct generic_pm_domain *genpd)
        return ret;
 }
 
-static int genpd_power_off(struct generic_pm_domain *genpd)
+static int genpd_power_off(struct generic_pm_domain *genpd, bool timed)
 {
        ktime_t time_start;
        s64 elapsed_ns;
@@ -217,6 +220,9 @@ static int genpd_power_off(struct generic_pm_domain *genpd)
        if (!genpd->power_off)
                return 0;
 
+       if (!timed)
+               return genpd->power_off(genpd);
+
        time_start = ktime_get();
        ret = genpd->power_off(genpd);
        if (ret == -EBUSY)
@@ -305,7 +311,7 @@ static int __pm_genpd_poweron(struct generic_pm_domain *genpd)
                }
        }
 
-       ret = genpd_power_on(genpd);
+       ret = genpd_power_on(genpd, true);
        if (ret)
                goto err;
 
@@ -615,7 +621,7 @@ static int pm_genpd_poweroff(struct generic_pm_domain *genpd)
                 * the pm_genpd_poweron() restore power for us (this shouldn't
                 * happen very often).
                 */
-               ret = genpd_power_off(genpd);
+               ret = genpd_power_off(genpd, true);
                if (ret == -EBUSY) {
                        genpd_set_active(genpd);
                        goto out;
@@ -827,6 +833,7 @@ static bool genpd_dev_active_wakeup(struct generic_pm_domain *genpd,
 /**
  * pm_genpd_sync_poweroff - Synchronously power off a PM domain and its masters.
  * @genpd: PM domain to power off, if possible.
+ * @timed: True if latency measurements are allowed.
  *
  * Check if the given PM domain can be powered off (during system suspend or
  * hibernation) and do that if so.  Also, in that case propagate to its masters.
@@ -836,7 +843,8 @@ static bool genpd_dev_active_wakeup(struct generic_pm_domain *genpd,
  * executed sequentially, so it is guaranteed that it will never run twice in
  * parallel).
  */
-static void pm_genpd_sync_poweroff(struct generic_pm_domain *genpd)
+static void pm_genpd_sync_poweroff(struct generic_pm_domain *genpd,
+                                  bool timed)
 {
        struct gpd_link *link;
 
@@ -847,26 +855,28 @@ static void pm_genpd_sync_poweroff(struct generic_pm_domain *genpd)
            || atomic_read(&genpd->sd_count) > 0)
                return;
 
-       genpd_power_off(genpd);
+       genpd_power_off(genpd, timed);
 
        genpd->status = GPD_STATE_POWER_OFF;
 
        list_for_each_entry(link, &genpd->slave_links, slave_node) {
                genpd_sd_counter_dec(link->master);
-               pm_genpd_sync_poweroff(link->master);
+               pm_genpd_sync_poweroff(link->master, timed);
        }
 }
 
 /**
  * pm_genpd_sync_poweron - Synchronously power on a PM domain and its masters.
  * @genpd: PM domain to power on.
+ * @timed: True if latency measurements are allowed.
  *
  * This function is only called in "noirq" and "syscore" stages of system power
  * transitions, so it need not acquire locks (all of the "noirq" callbacks are
  * executed sequentially, so it is guaranteed that it will never run twice in
  * parallel).
  */
-static void pm_genpd_sync_poweron(struct generic_pm_domain *genpd)
+static void pm_genpd_sync_poweron(struct generic_pm_domain *genpd,
+                                 bool timed)
 {
        struct gpd_link *link;
 
@@ -874,11 +884,11 @@ static void pm_genpd_sync_poweron(struct generic_pm_domain *genpd)
                return;
 
        list_for_each_entry(link, &genpd->slave_links, slave_node) {
-               pm_genpd_sync_poweron(link->master);
+               pm_genpd_sync_poweron(link->master, timed);
                genpd_sd_counter_inc(link->master);
        }
 
-       genpd_power_on(genpd);
+       genpd_power_on(genpd, timed);
 
        genpd->status = GPD_STATE_ACTIVE;
 }
@@ -1056,7 +1066,7 @@ static int pm_genpd_suspend_noirq(struct device *dev)
         * the same PM domain, so it is not necessary to use locking here.
         */
        genpd->suspended_count++;
-       pm_genpd_sync_poweroff(genpd);
+       pm_genpd_sync_poweroff(genpd, true);
 
        return 0;
 }
@@ -1086,7 +1096,7 @@ static int pm_genpd_resume_noirq(struct device *dev)
         * guaranteed that this function will never run twice in parallel for
         * the same PM domain, so it is not necessary to use locking here.
         */
-       pm_genpd_sync_poweron(genpd);
+       pm_genpd_sync_poweron(genpd, true);
        genpd->suspended_count--;
 
        return genpd_start_dev(genpd, dev);
@@ -1300,7 +1310,7 @@ static int pm_genpd_restore_noirq(struct device *dev)
                         * If the domain was off before the hibernation, make
                         * sure it will be off going forward.
                         */
-                       genpd_power_off(genpd);
+                       genpd_power_off(genpd, true);
 
                        return 0;
                }
@@ -1309,7 +1319,7 @@ static int pm_genpd_restore_noirq(struct device *dev)
        if (genpd->suspend_power_off)
                return 0;
 
-       pm_genpd_sync_poweron(genpd);
+       pm_genpd_sync_poweron(genpd, true);
 
        return genpd_start_dev(genpd, dev);
 }
@@ -1367,9 +1377,9 @@ static void genpd_syscore_switch(struct device *dev, bool suspend)
 
        if (suspend) {
                genpd->suspended_count++;
-               pm_genpd_sync_poweroff(genpd);
+               pm_genpd_sync_poweroff(genpd, false);
        } else {
-               pm_genpd_sync_poweron(genpd);
+               pm_genpd_sync_poweron(genpd, false);
                genpd->suspended_count--;
        }
 }
index 3d874eca71046dcf0eb983ff3ac2ec170a82d969..30b7bbfdc5588d000dfc79d02ee53b0a886302e3 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/pm.h>
 #include <linux/pm_runtime.h>
 #include <linux/pm-trace.h>
+#include <linux/pm_wakeirq.h>
 #include <linux/interrupt.h>
 #include <linux/sched.h>
 #include <linux/async.h>
@@ -587,6 +588,7 @@ void dpm_resume_noirq(pm_message_t state)
        async_synchronize_full();
        dpm_show_time(starttime, state, "noirq");
        resume_device_irqs();
+       device_wakeup_disarm_wake_irqs();
        cpuidle_resume();
        trace_suspend_resume(TPS("dpm_resume_noirq"), state.event, false);
 }
@@ -920,9 +922,7 @@ static void device_complete(struct device *dev, pm_message_t state)
 
        if (callback) {
                pm_dev_dbg(dev, state, info);
-               trace_device_pm_callback_start(dev, info, state.event);
                callback(dev);
-               trace_device_pm_callback_end(dev, 0);
        }
 
        device_unlock(dev);
@@ -954,7 +954,9 @@ void dpm_complete(pm_message_t state)
                list_move(&dev->power.entry, &list);
                mutex_unlock(&dpm_list_mtx);
 
+               trace_device_pm_callback_start(dev, "", state.event);
                device_complete(dev, state);
+               trace_device_pm_callback_end(dev, 0);
 
                mutex_lock(&dpm_list_mtx);
                put_device(dev);
@@ -1104,6 +1106,7 @@ int dpm_suspend_noirq(pm_message_t state)
 
        trace_suspend_resume(TPS("dpm_suspend_noirq"), state.event, true);
        cpuidle_pause();
+       device_wakeup_arm_wake_irqs();
        suspend_device_irqs();
        mutex_lock(&dpm_list_mtx);
        pm_transition = state;
@@ -1585,11 +1588,8 @@ static int device_prepare(struct device *dev, pm_message_t state)
                callback = dev->driver->pm->prepare;
        }
 
-       if (callback) {
-               trace_device_pm_callback_start(dev, info, state.event);
+       if (callback)
                ret = callback(dev);
-               trace_device_pm_callback_end(dev, ret);
-       }
 
        device_unlock(dev);
 
@@ -1631,7 +1631,9 @@ int dpm_prepare(pm_message_t state)
                get_device(dev);
                mutex_unlock(&dpm_list_mtx);
 
+               trace_device_pm_callback_start(dev, "", state.event);
                error = device_prepare(dev, state);
+               trace_device_pm_callback_end(dev, error);
 
                mutex_lock(&dpm_list_mtx);
                if (error) {
index b6b8a273c5da46d0ec3bb3c8dc3902e655a6a78c..f1a5d95e7b207816ef522ef0c9b20de192fdc28e 100644 (file)
@@ -20,6 +20,46 @@ static inline void pm_runtime_early_init(struct device *dev)
 extern void pm_runtime_init(struct device *dev);
 extern void pm_runtime_remove(struct device *dev);
 
+struct wake_irq {
+       struct device *dev;
+       int irq;
+       bool dedicated_irq:1;
+};
+
+extern void dev_pm_arm_wake_irq(struct wake_irq *wirq);
+extern void dev_pm_disarm_wake_irq(struct wake_irq *wirq);
+
+#ifdef CONFIG_PM_SLEEP
+
+extern int device_wakeup_attach_irq(struct device *dev,
+                                   struct wake_irq *wakeirq);
+extern void device_wakeup_detach_irq(struct device *dev);
+extern void device_wakeup_arm_wake_irqs(void);
+extern void device_wakeup_disarm_wake_irqs(void);
+
+#else
+
+static inline int
+device_wakeup_attach_irq(struct device *dev,
+                        struct wake_irq *wakeirq)
+{
+       return 0;
+}
+
+static inline void device_wakeup_detach_irq(struct device *dev)
+{
+}
+
+static inline void device_wakeup_arm_wake_irqs(void)
+{
+}
+
+static inline void device_wakeup_disarm_wake_irqs(void)
+{
+}
+
+#endif /* CONFIG_PM_SLEEP */
+
 /*
  * sysfs.c
  */
@@ -52,6 +92,14 @@ static inline void wakeup_sysfs_remove(struct device *dev) {}
 static inline int pm_qos_sysfs_add(struct device *dev) { return 0; }
 static inline void pm_qos_sysfs_remove(struct device *dev) {}
 
+static inline void dev_pm_arm_wake_irq(struct wake_irq *wirq)
+{
+}
+
+static inline void dev_pm_disarm_wake_irq(struct wake_irq *wirq)
+{
+}
+
 #endif
 
 #ifdef CONFIG_PM_SLEEP
index 5070c4fe8542fc85ffa9afd0a945eb8d83048eac..e1a10a03df8ec0f92cb43813e881e5d7bb0237c5 100644 (file)
@@ -10,6 +10,7 @@
 #include <linux/sched.h>
 #include <linux/export.h>
 #include <linux/pm_runtime.h>
+#include <linux/pm_wakeirq.h>
 #include <trace/events/rpm.h>
 #include "power.h"
 
@@ -514,6 +515,7 @@ static int rpm_suspend(struct device *dev, int rpmflags)
 
        callback = RPM_GET_CALLBACK(dev, runtime_suspend);
 
+       dev_pm_enable_wake_irq(dev);
        retval = rpm_callback(callback, dev);
        if (retval)
                goto fail;
@@ -552,6 +554,7 @@ static int rpm_suspend(struct device *dev, int rpmflags)
        return retval;
 
  fail:
+       dev_pm_disable_wake_irq(dev);
        __update_runtime_status(dev, RPM_ACTIVE);
        dev->power.deferred_resume = false;
        wake_up_all(&dev->power.wait_queue);
@@ -734,13 +737,16 @@ static int rpm_resume(struct device *dev, int rpmflags)
 
        callback = RPM_GET_CALLBACK(dev, runtime_resume);
 
+       dev_pm_disable_wake_irq(dev);
        retval = rpm_callback(callback, dev);
        if (retval) {
                __update_runtime_status(dev, RPM_SUSPENDED);
                pm_runtime_cancel_pending(dev);
+               dev_pm_enable_wake_irq(dev);
        } else {
  no_callback:
                __update_runtime_status(dev, RPM_ACTIVE);
+               pm_runtime_mark_last_busy(dev);
                if (parent)
                        atomic_inc(&parent->power.child_count);
        }
diff --git a/drivers/base/power/wakeirq.c b/drivers/base/power/wakeirq.c
new file mode 100644 (file)
index 0000000..7470004
--- /dev/null
@@ -0,0 +1,273 @@
+/*
+ * wakeirq.c - Device wakeirq helper functions
+ *
+ * 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 "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/slab.h>
+#include <linux/pm_runtime.h>
+#include <linux/pm_wakeirq.h>
+
+#include "power.h"
+
+/**
+ * dev_pm_attach_wake_irq - Attach device interrupt as a wake IRQ
+ * @dev: Device entry
+ * @irq: Device wake-up capable interrupt
+ * @wirq: Wake irq specific data
+ *
+ * Internal function to attach either a device IO interrupt or a
+ * dedicated wake-up interrupt as a wake IRQ.
+ */
+static int dev_pm_attach_wake_irq(struct device *dev, int irq,
+                                 struct wake_irq *wirq)
+{
+       unsigned long flags;
+       int err;
+
+       if (!dev || !wirq)
+               return -EINVAL;
+
+       spin_lock_irqsave(&dev->power.lock, flags);
+       if (dev_WARN_ONCE(dev, dev->power.wakeirq,
+                         "wake irq already initialized\n")) {
+               spin_unlock_irqrestore(&dev->power.lock, flags);
+               return -EEXIST;
+       }
+
+       dev->power.wakeirq = wirq;
+       spin_unlock_irqrestore(&dev->power.lock, flags);
+
+       err = device_wakeup_attach_irq(dev, wirq);
+       if (err)
+               return err;
+
+       return 0;
+}
+
+/**
+ * dev_pm_set_wake_irq - Attach device IO interrupt as wake IRQ
+ * @dev: Device entry
+ * @irq: Device IO interrupt
+ *
+ * Attach a device IO interrupt as a wake IRQ. The wake IRQ gets
+ * automatically configured for wake-up from suspend  based
+ * on the device specific sysfs wakeup entry. Typically called
+ * during driver probe after calling device_init_wakeup().
+ */
+int dev_pm_set_wake_irq(struct device *dev, int irq)
+{
+       struct wake_irq *wirq;
+       int err;
+
+       wirq = kzalloc(sizeof(*wirq), GFP_KERNEL);
+       if (!wirq)
+               return -ENOMEM;
+
+       wirq->dev = dev;
+       wirq->irq = irq;
+
+       err = dev_pm_attach_wake_irq(dev, irq, wirq);
+       if (err)
+               kfree(wirq);
+
+       return err;
+}
+EXPORT_SYMBOL_GPL(dev_pm_set_wake_irq);
+
+/**
+ * dev_pm_clear_wake_irq - Detach a device IO interrupt wake IRQ
+ * @dev: Device entry
+ *
+ * Detach a device wake IRQ and free resources.
+ *
+ * Note that it's OK for drivers to call this without calling
+ * dev_pm_set_wake_irq() as all the driver instances may not have
+ * a wake IRQ configured. This avoid adding wake IRQ specific
+ * checks into the drivers.
+ */
+void dev_pm_clear_wake_irq(struct device *dev)
+{
+       struct wake_irq *wirq = dev->power.wakeirq;
+       unsigned long flags;
+
+       if (!wirq)
+               return;
+
+       spin_lock_irqsave(&dev->power.lock, flags);
+       dev->power.wakeirq = NULL;
+       spin_unlock_irqrestore(&dev->power.lock, flags);
+
+       device_wakeup_detach_irq(dev);
+       if (wirq->dedicated_irq)
+               free_irq(wirq->irq, wirq);
+       kfree(wirq);
+}
+EXPORT_SYMBOL_GPL(dev_pm_clear_wake_irq);
+
+/**
+ * handle_threaded_wake_irq - Handler for dedicated wake-up interrupts
+ * @irq: Device specific dedicated wake-up interrupt
+ * @_wirq: Wake IRQ data
+ *
+ * Some devices have a separate wake-up interrupt in addition to the
+ * device IO interrupt. The wake-up interrupt signals that a device
+ * should be woken up from it's idle state. This handler uses device
+ * specific pm_runtime functions to wake the device, and then it's
+ * up to the device to do whatever it needs to. Note that as the
+ * device may need to restore context and start up regulators, we
+ * use a threaded IRQ.
+ *
+ * Also note that we are not resending the lost device interrupts.
+ * We assume that the wake-up interrupt just needs to wake-up the
+ * device, and then device's pm_runtime_resume() can deal with the
+ * situation.
+ */
+static irqreturn_t handle_threaded_wake_irq(int irq, void *_wirq)
+{
+       struct wake_irq *wirq = _wirq;
+       int res;
+
+       /* We don't want RPM_ASYNC or RPM_NOWAIT here */
+       res = pm_runtime_resume(wirq->dev);
+       if (res < 0)
+               dev_warn(wirq->dev,
+                        "wake IRQ with no resume: %i\n", res);
+
+       return IRQ_HANDLED;
+}
+
+/**
+ * dev_pm_set_dedicated_wake_irq - Request a dedicated wake-up interrupt
+ * @dev: Device entry
+ * @irq: Device wake-up interrupt
+ *
+ * Unless your hardware has separate wake-up interrupts in addition
+ * to the device IO interrupts, you don't need this.
+ *
+ * Sets up a threaded interrupt handler for a device that has
+ * a dedicated wake-up interrupt in addition to the device IO
+ * interrupt.
+ *
+ * The interrupt starts disabled, and needs to be managed for
+ * the device by the bus code or the device driver using
+ * dev_pm_enable_wake_irq() and dev_pm_disable_wake_irq()
+ * functions.
+ */
+int dev_pm_set_dedicated_wake_irq(struct device *dev, int irq)
+{
+       struct wake_irq *wirq;
+       int err;
+
+       wirq = kzalloc(sizeof(*wirq), GFP_KERNEL);
+       if (!wirq)
+               return -ENOMEM;
+
+       wirq->dev = dev;
+       wirq->irq = irq;
+       wirq->dedicated_irq = true;
+       irq_set_status_flags(irq, IRQ_NOAUTOEN);
+
+       /*
+        * Consumer device may need to power up and restore state
+        * so we use a threaded irq.
+        */
+       err = request_threaded_irq(irq, NULL, handle_threaded_wake_irq,
+                                  IRQF_ONESHOT, dev_name(dev), wirq);
+       if (err)
+               goto err_free;
+
+       err = dev_pm_attach_wake_irq(dev, irq, wirq);
+       if (err)
+               goto err_free_irq;
+
+       return err;
+
+err_free_irq:
+       free_irq(irq, wirq);
+err_free:
+       kfree(wirq);
+
+       return err;
+}
+EXPORT_SYMBOL_GPL(dev_pm_set_dedicated_wake_irq);
+
+/**
+ * dev_pm_enable_wake_irq - Enable device wake-up interrupt
+ * @dev: Device
+ *
+ * Called from the bus code or the device driver for
+ * runtime_suspend() to enable the wake-up interrupt while
+ * the device is running.
+ *
+ * Note that for runtime_suspend()) the wake-up interrupts
+ * should be unconditionally enabled unlike for suspend()
+ * that is conditional.
+ */
+void dev_pm_enable_wake_irq(struct device *dev)
+{
+       struct wake_irq *wirq = dev->power.wakeirq;
+
+       if (wirq && wirq->dedicated_irq)
+               enable_irq(wirq->irq);
+}
+EXPORT_SYMBOL_GPL(dev_pm_enable_wake_irq);
+
+/**
+ * dev_pm_disable_wake_irq - Disable device wake-up interrupt
+ * @dev: Device
+ *
+ * Called from the bus code or the device driver for
+ * runtime_resume() to disable the wake-up interrupt while
+ * the device is running.
+ */
+void dev_pm_disable_wake_irq(struct device *dev)
+{
+       struct wake_irq *wirq = dev->power.wakeirq;
+
+       if (wirq && wirq->dedicated_irq)
+               disable_irq_nosync(wirq->irq);
+}
+EXPORT_SYMBOL_GPL(dev_pm_disable_wake_irq);
+
+/**
+ * dev_pm_arm_wake_irq - Arm device wake-up
+ * @wirq: Device wake-up interrupt
+ *
+ * Sets up the wake-up event conditionally based on the
+ * device_may_wake().
+ */
+void dev_pm_arm_wake_irq(struct wake_irq *wirq)
+{
+       if (!wirq)
+               return;
+
+       if (device_may_wakeup(wirq->dev))
+               enable_irq_wake(wirq->irq);
+}
+
+/**
+ * dev_pm_disarm_wake_irq - Disarm device wake-up
+ * @wirq: Device wake-up interrupt
+ *
+ * Clears up the wake-up event conditionally based on the
+ * device_may_wake().
+ */
+void dev_pm_disarm_wake_irq(struct wake_irq *wirq)
+{
+       if (!wirq)
+               return;
+
+       if (device_may_wakeup(wirq->dev))
+               disable_irq_wake(wirq->irq);
+}
index 77262009f89dbc39c1c93307f14c81e7d61db08b..40f71603378cb5872265c9df994e6ca8cb756ffc 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/suspend.h>
 #include <linux/seq_file.h>
 #include <linux/debugfs.h>
+#include <linux/pm_wakeirq.h>
 #include <trace/events/power.h>
 
 #include "power.h"
@@ -56,6 +57,11 @@ static LIST_HEAD(wakeup_sources);
 
 static DECLARE_WAIT_QUEUE_HEAD(wakeup_count_wait_queue);
 
+static struct wakeup_source deleted_ws = {
+       .name = "deleted",
+       .lock =  __SPIN_LOCK_UNLOCKED(deleted_ws.lock),
+};
+
 /**
  * wakeup_source_prepare - Prepare a new wakeup source for initialization.
  * @ws: Wakeup source to prepare.
@@ -107,6 +113,34 @@ void wakeup_source_drop(struct wakeup_source *ws)
 }
 EXPORT_SYMBOL_GPL(wakeup_source_drop);
 
+/*
+ * Record wakeup_source statistics being deleted into a dummy wakeup_source.
+ */
+static void wakeup_source_record(struct wakeup_source *ws)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&deleted_ws.lock, flags);
+
+       if (ws->event_count) {
+               deleted_ws.total_time =
+                       ktime_add(deleted_ws.total_time, ws->total_time);
+               deleted_ws.prevent_sleep_time =
+                       ktime_add(deleted_ws.prevent_sleep_time,
+                                 ws->prevent_sleep_time);
+               deleted_ws.max_time =
+                       ktime_compare(deleted_ws.max_time, ws->max_time) > 0 ?
+                               deleted_ws.max_time : ws->max_time;
+               deleted_ws.event_count += ws->event_count;
+               deleted_ws.active_count += ws->active_count;
+               deleted_ws.relax_count += ws->relax_count;
+               deleted_ws.expire_count += ws->expire_count;
+               deleted_ws.wakeup_count += ws->wakeup_count;
+       }
+
+       spin_unlock_irqrestore(&deleted_ws.lock, flags);
+}
+
 /**
  * wakeup_source_destroy - Destroy a struct wakeup_source object.
  * @ws: Wakeup source to destroy.
@@ -119,6 +153,7 @@ void wakeup_source_destroy(struct wakeup_source *ws)
                return;
 
        wakeup_source_drop(ws);
+       wakeup_source_record(ws);
        kfree(ws->name);
        kfree(ws);
 }
@@ -238,6 +273,97 @@ int device_wakeup_enable(struct device *dev)
 }
 EXPORT_SYMBOL_GPL(device_wakeup_enable);
 
+/**
+ * device_wakeup_attach_irq - Attach a wakeirq to a wakeup source
+ * @dev: Device to handle
+ * @wakeirq: Device specific wakeirq entry
+ *
+ * Attach a device wakeirq to the wakeup source so the device
+ * wake IRQ can be configured automatically for suspend and
+ * resume.
+ */
+int device_wakeup_attach_irq(struct device *dev,
+                            struct wake_irq *wakeirq)
+{
+       struct wakeup_source *ws;
+       int ret = 0;
+
+       spin_lock_irq(&dev->power.lock);
+       ws = dev->power.wakeup;
+       if (!ws) {
+               dev_err(dev, "forgot to call call device_init_wakeup?\n");
+               ret = -EINVAL;
+               goto unlock;
+       }
+
+       if (ws->wakeirq) {
+               ret = -EEXIST;
+               goto unlock;
+       }
+
+       ws->wakeirq = wakeirq;
+
+unlock:
+       spin_unlock_irq(&dev->power.lock);
+
+       return ret;
+}
+
+/**
+ * device_wakeup_detach_irq - Detach a wakeirq from a wakeup source
+ * @dev: Device to handle
+ *
+ * Removes a device wakeirq from the wakeup source.
+ */
+void device_wakeup_detach_irq(struct device *dev)
+{
+       struct wakeup_source *ws;
+
+       spin_lock_irq(&dev->power.lock);
+       ws = dev->power.wakeup;
+       if (!ws)
+               goto unlock;
+
+       ws->wakeirq = NULL;
+
+unlock:
+       spin_unlock_irq(&dev->power.lock);
+}
+
+/**
+ * device_wakeup_arm_wake_irqs(void)
+ *
+ * Itereates over the list of device wakeirqs to arm them.
+ */
+void device_wakeup_arm_wake_irqs(void)
+{
+       struct wakeup_source *ws;
+
+       rcu_read_lock();
+       list_for_each_entry_rcu(ws, &wakeup_sources, entry) {
+               if (ws->wakeirq)
+                       dev_pm_arm_wake_irq(ws->wakeirq);
+       }
+       rcu_read_unlock();
+}
+
+/**
+ * device_wakeup_disarm_wake_irqs(void)
+ *
+ * Itereates over the list of device wakeirqs to disarm them.
+ */
+void device_wakeup_disarm_wake_irqs(void)
+{
+       struct wakeup_source *ws;
+
+       rcu_read_lock();
+       list_for_each_entry_rcu(ws, &wakeup_sources, entry) {
+               if (ws->wakeirq)
+                       dev_pm_disarm_wake_irq(ws->wakeirq);
+       }
+       rcu_read_unlock();
+}
+
 /**
  * device_wakeup_detach - Detach a device's wakeup source object from it.
  * @dev: Device to detach the wakeup source object from.
@@ -351,6 +477,20 @@ int device_set_wakeup_enable(struct device *dev, bool enable)
 }
 EXPORT_SYMBOL_GPL(device_set_wakeup_enable);
 
+/**
+ * wakeup_source_not_registered - validate the given wakeup source.
+ * @ws: Wakeup source to be validated.
+ */
+static bool wakeup_source_not_registered(struct wakeup_source *ws)
+{
+       /*
+        * Use timer struct to check if the given source is initialized
+        * by wakeup_source_add.
+        */
+       return ws->timer.function != pm_wakeup_timer_fn ||
+                  ws->timer.data != (unsigned long)ws;
+}
+
 /*
  * The functions below use the observation that each wakeup event starts a
  * period in which the system should not be suspended.  The moment this period
@@ -391,6 +531,10 @@ static void wakeup_source_activate(struct wakeup_source *ws)
 {
        unsigned int cec;
 
+       if (WARN_ONCE(wakeup_source_not_registered(ws),
+                       "unregistered wakeup source\n"))
+               return;
+
        /*
         * active wakeup source should bring the system
         * out of PM_SUSPEND_FREEZE state
@@ -894,6 +1038,8 @@ static int wakeup_sources_stats_show(struct seq_file *m, void *unused)
                print_wakeup_source_stats(m, ws);
        rcu_read_unlock();
 
+       print_wakeup_source_stats(m, &deleted_ws);
+
        return 0;
 }
 
index 1d0b116cae959041682eaf6bb7ea91895b390e58..e645852396ba44430d77273b0a513cb5e8deb78a 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/export.h>
 #include <linux/kernel.h>
 #include <linux/of.h>
+#include <linux/of_address.h>
 #include <linux/property.h>
 
 /**
@@ -519,3 +520,16 @@ unsigned int device_get_child_node_count(struct device *dev)
        return count;
 }
 EXPORT_SYMBOL_GPL(device_get_child_node_count);
+
+bool device_dma_is_coherent(struct device *dev)
+{
+       bool coherent = false;
+
+       if (IS_ENABLED(CONFIG_OF) && dev->of_node)
+               coherent = of_dma_is_coherent(dev->of_node);
+       else
+               acpi_check_dma(ACPI_COMPANION(dev), &coherent);
+
+       return coherent;
+}
+EXPORT_SYMBOL_GPL(device_dma_is_coherent);
index a13587b5c2be32b912f9fe0fd7544761a280bd57..b2b2849fc6d3b34d1294a02ef009a10af86d49de 100644 (file)
@@ -131,7 +131,10 @@ struct regmap {
        struct reg_default *reg_defaults;
        const void *reg_defaults_raw;
        void *cache;
+       /* if set, the cache contains newer data than the HW */
        u32 cache_dirty;
+       /* if set, the HW registers are known to match map->reg_defaults */
+       bool no_sync_defaults;
 
        struct reg_default *patch;
        int patch_regs;
index 7eb7b3b98794849c6718c068504c7ac1acf3a2c9..b9862d741a56210885638b3cfc9ce8c92f435254 100644 (file)
@@ -249,6 +249,22 @@ int regcache_write(struct regmap *map,
        return 0;
 }
 
+static bool regcache_reg_needs_sync(struct regmap *map, unsigned int reg,
+                                   unsigned int val)
+{
+       int ret;
+
+       /* If we don't know the chip just got reset, then sync everything. */
+       if (!map->no_sync_defaults)
+               return true;
+
+       /* Is this the hardware default?  If so skip. */
+       ret = regcache_lookup_reg(map, reg);
+       if (ret >= 0 && val == map->reg_defaults[ret].def)
+               return false;
+       return true;
+}
+
 static int regcache_default_sync(struct regmap *map, unsigned int min,
                                 unsigned int max)
 {
@@ -266,9 +282,7 @@ static int regcache_default_sync(struct regmap *map, unsigned int min,
                if (ret)
                        return ret;
 
-               /* Is this the hardware default?  If so skip. */
-               ret = regcache_lookup_reg(map, reg);
-               if (ret >= 0 && val == map->reg_defaults[ret].def)
+               if (!regcache_reg_needs_sync(map, reg, val))
                        continue;
 
                map->cache_bypass = 1;
@@ -342,6 +356,7 @@ out:
        /* Restore the bypass state */
        map->async = false;
        map->cache_bypass = bypass;
+       map->no_sync_defaults = false;
        map->unlock(map->lock_arg);
 
        regmap_async_complete(map);
@@ -397,6 +412,7 @@ out:
        /* Restore the bypass state */
        map->cache_bypass = bypass;
        map->async = false;
+       map->no_sync_defaults = false;
        map->unlock(map->lock_arg);
 
        regmap_async_complete(map);
@@ -461,18 +477,23 @@ void regcache_cache_only(struct regmap *map, bool enable)
 EXPORT_SYMBOL_GPL(regcache_cache_only);
 
 /**
- * regcache_mark_dirty: Mark the register cache as dirty
+ * regcache_mark_dirty: Indicate that HW registers were reset to default values
  *
  * @map: map to mark
  *
- * Mark the register cache as dirty, for example due to the device
- * having been powered down for suspend.  If the cache is not marked
- * as dirty then the cache sync will be suppressed.
+ * Inform regcache that the device has been powered down or reset, so that
+ * on resume, regcache_sync() knows to write out all non-default values
+ * stored in the cache.
+ *
+ * If this function is not called, regcache_sync() will assume that
+ * the hardware state still matches the cache state, modulo any writes that
+ * happened when cache_only was true.
  */
 void regcache_mark_dirty(struct regmap *map)
 {
        map->lock(map->lock_arg);
        map->cache_dirty = true;
+       map->no_sync_defaults = true;
        map->unlock(map->lock_arg);
 }
 EXPORT_SYMBOL_GPL(regcache_mark_dirty);
@@ -613,10 +634,7 @@ static int regcache_sync_block_single(struct regmap *map, void *block,
                        continue;
 
                val = regcache_get_val(map, block, i);
-
-               /* Is this the hardware default?  If so skip. */
-               ret = regcache_lookup_reg(map, regtmp);
-               if (ret >= 0 && val == map->reg_defaults[ret].def)
+               if (!regcache_reg_needs_sync(map, regtmp, val))
                        continue;
 
                map->cache_bypass = 1;
@@ -688,10 +706,7 @@ static int regcache_sync_block_raw(struct regmap *map, void *block,
                }
 
                val = regcache_get_val(map, block, i);
-
-               /* Is this the hardware default?  If so skip. */
-               ret = regcache_lookup_reg(map, regtmp);
-               if (ret >= 0 && val == map->reg_defaults[ret].def) {
+               if (!regcache_reg_needs_sync(map, regtmp, val)) {
                        ret = regcache_sync_block_raw_flush(map, &data,
                                                            base, regtmp);
                        if (ret != 0)
index a6c3f75b4b01e1145c6eb5bb9816182a0f9c9fde..2597600a5d26d8c115321a8688c871e29b66d2a4 100644 (file)
@@ -109,7 +109,7 @@ static void regmap_irq_sync_unlock(struct irq_data *data)
                if (!d->chip->init_ack_masked)
                        continue;
                /*
-                * Ack all the masked interrupts uncondictionly,
+                * Ack all the masked interrupts unconditionally,
                 * OR if there is masked interrupt which hasn't been Acked,
                 * it'll be ignored in irq handler, then may introduce irq storm
                 */
@@ -306,19 +306,12 @@ static int regmap_irq_map(struct irq_domain *h, unsigned int virq,
        irq_set_chip_data(virq, data);
        irq_set_chip(virq, &data->irq_chip);
        irq_set_nested_thread(virq, 1);
-
-       /* ARM needs us to explicitly flag the IRQ as valid
-        * and will set them noprobe when we do so. */
-#ifdef CONFIG_ARM
-       set_irq_flags(virq, IRQF_VALID);
-#else
        irq_set_noprobe(virq);
-#endif
 
        return 0;
 }
 
-static struct irq_domain_ops regmap_domain_ops = {
+static const struct irq_domain_ops regmap_domain_ops = {
        .map    = regmap_irq_map,
        .xlate  = irq_domain_xlate_twocell,
 };
index 6273ff072f3eaa4cbd777b1a4f41fb3cf533a42a..7111d04f26218be0529f4702cb1b361ae07a0b00 100644 (file)
@@ -945,11 +945,10 @@ EXPORT_SYMBOL_GPL(devm_regmap_init);
 static void regmap_field_init(struct regmap_field *rm_field,
        struct regmap *regmap, struct reg_field reg_field)
 {
-       int field_bits = reg_field.msb - reg_field.lsb + 1;
        rm_field->regmap = regmap;
        rm_field->reg = reg_field.reg;
        rm_field->shift = reg_field.lsb;
-       rm_field->mask = ((BIT(field_bits) - 1) << reg_field.lsb);
+       rm_field->mask = GENMASK(reg_field.msb, reg_field.lsb);
        rm_field->id_size = reg_field.id_size;
        rm_field->id_offset = reg_field.id_offset;
 }
@@ -2318,7 +2317,7 @@ int regmap_bulk_read(struct regmap *map, unsigned int reg, void *val,
                                          &ival);
                        if (ret != 0)
                                return ret;
-                       memcpy(val + (i * val_bytes), &ival, val_bytes);
+                       map->format.format_val(val + (i * val_bytes), ival, 0);
                }
        }
 
@@ -2583,10 +2582,7 @@ int regmap_register_patch(struct regmap *map, const struct reg_default *regs,
        map->async = true;
 
        ret = _regmap_multi_reg_write(map, regs, num_regs);
-       if (ret != 0)
-               goto out;
 
-out:
        map->async = false;
        map->cache_bypass = bypass;
 
@@ -2613,6 +2609,30 @@ int regmap_get_val_bytes(struct regmap *map)
 }
 EXPORT_SYMBOL_GPL(regmap_get_val_bytes);
 
+/**
+ * regmap_get_max_register(): Report the max register value
+ *
+ * Report the max register value, mainly intended to for use by
+ * generic infrastructure built on top of regmap.
+ */
+int regmap_get_max_register(struct regmap *map)
+{
+       return map->max_register ? map->max_register : -EINVAL;
+}
+EXPORT_SYMBOL_GPL(regmap_get_max_register);
+
+/**
+ * regmap_get_reg_stride(): Report the register address stride
+ *
+ * Report the register address stride, mainly intended to for use by
+ * generic infrastructure built on top of regmap.
+ */
+int regmap_get_reg_stride(struct regmap *map)
+{
+       return map->reg_stride;
+}
+EXPORT_SYMBOL_GPL(regmap_get_reg_stride);
+
 int regmap_parse_val(struct regmap *map, const void *buf,
                        unsigned int *val)
 {
index eb1fed5bd516ffac33c850eed47fad402250c686..3ccef9eba6f9dc53cecb785c23582cbdeb3b8618 100644 (file)
@@ -406,6 +406,7 @@ config BLK_DEV_RAM_DAX
 
 config BLK_DEV_PMEM
        tristate "Persistent memory block device support"
+       depends on HAS_IOMEM
        help
          Saying Y here will allow you to use a contiguous range of reserved
          memory as one or more persistent block devices.
index ff20f192b0f67a77fc9e38092c457429e215f8a7..0422c47261c3a06ad7bc6796d09ae5a84603a514 100644 (file)
@@ -139,8 +139,6 @@ static struct board_type products[] = {
        {0x3214103C, "Smart Array E200i", &SA5_access},
        {0x3215103C, "Smart Array E200i", &SA5_access},
        {0x3237103C, "Smart Array E500", &SA5_access},
-       {0x3223103C, "Smart Array P800", &SA5_access},
-       {0x3234103C, "Smart Array P400", &SA5_access},
        {0x323D103C, "Smart Array P700m", &SA5_access},
 };
 
@@ -574,8 +572,6 @@ static void cciss_procinit(ctlr_info_t *h)
 
 /* List of controllers which cannot be hard reset on kexec with reset_devices */
 static u32 unresettable_controller[] = {
-       0x324a103C, /* Smart Array P712m */
-       0x324b103C, /* SmartArray P711m */
        0x3223103C, /* Smart Array P800 */
        0x3234103C, /* Smart Array P400 */
        0x3235103C, /* Smart Array P400i */
@@ -586,12 +582,32 @@ static u32 unresettable_controller[] = {
        0x3215103C, /* Smart Array E200i */
        0x3237103C, /* Smart Array E500 */
        0x323D103C, /* Smart Array P700m */
+       0x40800E11, /* Smart Array 5i */
        0x409C0E11, /* Smart Array 6400 */
        0x409D0E11, /* Smart Array 6400 EM */
+       0x40700E11, /* Smart Array 5300 */
+       0x40820E11, /* Smart Array 532 */
+       0x40830E11, /* Smart Array 5312 */
+       0x409A0E11, /* Smart Array 641 */
+       0x409B0E11, /* Smart Array 642 */
+       0x40910E11, /* Smart Array 6i */
 };
 
 /* List of controllers which cannot even be soft reset */
 static u32 soft_unresettable_controller[] = {
+       0x40800E11, /* Smart Array 5i */
+       0x40700E11, /* Smart Array 5300 */
+       0x40820E11, /* Smart Array 532 */
+       0x40830E11, /* Smart Array 5312 */
+       0x409A0E11, /* Smart Array 641 */
+       0x409B0E11, /* Smart Array 642 */
+       0x40910E11, /* Smart Array 6i */
+       /* Exclude 640x boards.  These are two pci devices in one slot
+        * which share a battery backed cache module.  One controls the
+        * cache, the other accesses the cache through the one that controls
+        * it.  If we reset the one controlling the cache, the other will
+        * likely not be happy.  Just forbid resetting this conjoined mess.
+        */
        0x409C0E11, /* Smart Array 6400 */
        0x409D0E11, /* Smart Array 6400 EM */
 };
@@ -4667,8 +4683,7 @@ static int cciss_kdump_hard_reset_controller(struct pci_dev *pdev)
         */
        cciss_lookup_board_id(pdev, &board_id);
        if (!ctlr_is_resettable(board_id)) {
-               dev_warn(&pdev->dev, "Cannot reset Smart Array 640x "
-                               "due to shared cache module.");
+               dev_warn(&pdev->dev, "Controller not resettable\n");
                return -ENODEV;
        }
 
index ecd845cd28d8d111a42bbb120dbf8d006f1a0340..1537302e56e3de1889d3069170dee2e55bfd73d5 100644 (file)
@@ -84,7 +84,6 @@ static struct scsi_host_template cciss_driver_template = {
        .show_info              = cciss_scsi_show_info,
        .queuecommand           = cciss_scsi_queue_command,
        .this_id                = 7,
-       .cmd_per_lun            = 1,
        .use_clustering         = DISABLE_CLUSTERING,
        /* Can't have eh_bus_reset_handler or eh_host_reset_handler for cciss */
        .eh_device_reset_handler= cciss_eh_device_reset_handler,
index 85b8036deaa3b7daaba5317ed746936a1f5183db..683dff272562b16d325df65495ad6a868cf45b14 100644 (file)
@@ -1750,6 +1750,7 @@ static int nvme_submit_io(struct nvme_ns *ns, struct nvme_user_io __user *uio)
        struct nvme_iod *iod;
        dma_addr_t meta_dma = 0;
        void *meta = NULL;
+       void __user *metadata;
 
        if (copy_from_user(&io, uio, sizeof(io)))
                return -EFAULT;
@@ -1763,6 +1764,8 @@ static int nvme_submit_io(struct nvme_ns *ns, struct nvme_user_io __user *uio)
                meta_len = 0;
        }
 
+       metadata = (void __user *)(unsigned long)io.metadata;
+
        write = io.opcode & 1;
 
        switch (io.opcode) {
@@ -1786,13 +1789,13 @@ static int nvme_submit_io(struct nvme_ns *ns, struct nvme_user_io __user *uio)
        if (meta_len) {
                meta = dma_alloc_coherent(&dev->pci_dev->dev, meta_len,
                                                &meta_dma, GFP_KERNEL);
+
                if (!meta) {
                        status = -ENOMEM;
                        goto unmap;
                }
                if (write) {
-                       if (copy_from_user(meta, (void __user *)io.metadata,
-                                                               meta_len)) {
+                       if (copy_from_user(meta, metadata, meta_len)) {
                                status = -EFAULT;
                                goto unmap;
                        }
@@ -1819,8 +1822,7 @@ static int nvme_submit_io(struct nvme_ns *ns, struct nvme_user_io __user *uio)
        nvme_free_iod(dev, iod);
        if (meta) {
                if (status == NVME_SC_SUCCESS && !write) {
-                       if (copy_to_user((void __user *)io.metadata, meta,
-                                                               meta_len))
+                       if (copy_to_user(metadata, meta, meta_len))
                                status = -EFAULT;
                }
                dma_free_coherent(&dev->pci_dev->dev, meta_len, meta, meta_dma);
index eabf4a8d00855ef06c2c2e9cd4178954903fe47c..095dfaadcaa5f3ceefd42acd8c044a8103d71c44 100644 (file)
@@ -139,11 +139,11 @@ static struct pmem_device *pmem_alloc(struct device *dev, struct resource *res)
        }
 
        /*
-        * Map the memory as non-cachable, as we can't write back the contents
+        * Map the memory as write-through, as we can't write back the contents
         * of the CPU caches in case of a crash.
         */
        err = -ENOMEM;
-       pmem->virt_addr = ioremap_nocache(pmem->phys_addr, pmem->size);
+       pmem->virt_addr = ioremap_wt(pmem->phys_addr, pmem->size);
        if (!pmem->virt_addr)
                goto out_release_region;
 
index 8dcbced0eafd5f8dc0a53dc8d8e9d4b37bad9bab..6e134f4759c0c9e98b93f221e7687004d4418342 100644 (file)
@@ -805,7 +805,9 @@ static void zram_reset_device(struct zram *zram)
        memset(&zram->stats, 0, sizeof(zram->stats));
        zram->disksize = 0;
        zram->max_comp_streams = 1;
+
        set_capacity(zram->disk, 0);
+       part_stat_set_all(&zram->disk->part0, 0);
 
        up_write(&zram->init_lock);
        /* I/O operation under all of CPU are done so let's free */
index fb9ec6221730a2d594f66d15e54471aea75cc750..c43c3d2baf73c2e7663d367ad35aa97be8c6ba33 100644 (file)
@@ -57,8 +57,8 @@
 #include <linux/of_address.h>
 #include <linux/debugfs.h>
 #include <linux/log2.h>
-#include <linux/syscore_ops.h>
 #include <linux/memblock.h>
+#include <linux/syscore_ops.h>
 
 /*
  * DDR target is the same on all platforms.
@@ -70,6 +70,7 @@
  */
 #define WIN_CTRL_OFF           0x0000
 #define   WIN_CTRL_ENABLE       BIT(0)
+/* Only on HW I/O coherency capable platforms */
 #define   WIN_CTRL_SYNCBARRIER  BIT(1)
 #define   WIN_CTRL_TGT_MASK     0xf0
 #define   WIN_CTRL_TGT_SHIFT    4
 
 /* Relative to mbusbridge_base */
 #define MBUS_BRIDGE_CTRL_OFF   0x0
-#define  MBUS_BRIDGE_SIZE_MASK  0xffff0000
 #define MBUS_BRIDGE_BASE_OFF   0x4
-#define  MBUS_BRIDGE_BASE_MASK  0xffff0000
 
 /* Maximum number of windows, for all known platforms */
 #define MBUS_WINS_MAX           20
@@ -154,13 +153,39 @@ struct mvebu_mbus_state {
 
 static struct mvebu_mbus_state mbus_state;
 
+/*
+ * We provide two variants of the mv_mbus_dram_info() function:
+ *
+ * - The normal one, where the described DRAM ranges may overlap with
+ *   the I/O windows, but for which the DRAM ranges are guaranteed to
+ *   have a power of two size. Such ranges are suitable for the DMA
+ *   masters that only DMA between the RAM and the device, which is
+ *   actually all devices except the crypto engines.
+ *
+ * - The 'nooverlap' one, where the described DRAM ranges are
+ *   guaranteed to not overlap with the I/O windows, but for which the
+ *   DRAM ranges will not have power of two sizes. They will only be
+ *   aligned on a 64 KB boundary, and have a size multiple of 64
+ *   KB. Such ranges are suitable for the DMA masters that DMA between
+ *   the crypto SRAM (which is mapped through an I/O window) and a
+ *   device. This is the case for the crypto engines.
+ */
+
 static struct mbus_dram_target_info mvebu_mbus_dram_info;
+static struct mbus_dram_target_info mvebu_mbus_dram_info_nooverlap;
+
 const struct mbus_dram_target_info *mv_mbus_dram_info(void)
 {
        return &mvebu_mbus_dram_info;
 }
 EXPORT_SYMBOL_GPL(mv_mbus_dram_info);
 
+const struct mbus_dram_target_info *mv_mbus_dram_info_nooverlap(void)
+{
+       return &mvebu_mbus_dram_info_nooverlap;
+}
+EXPORT_SYMBOL_GPL(mv_mbus_dram_info_nooverlap);
+
 /* Checks whether the given window has remap capability */
 static bool mvebu_mbus_window_is_remappable(struct mvebu_mbus_state *mbus,
                                            const int win)
@@ -323,8 +348,9 @@ static int mvebu_mbus_setup_window(struct mvebu_mbus_state *mbus,
        ctrl = ((size - 1) & WIN_CTRL_SIZE_MASK) |
                (attr << WIN_CTRL_ATTR_SHIFT)    |
                (target << WIN_CTRL_TGT_SHIFT)   |
-               WIN_CTRL_SYNCBARRIER             |
                WIN_CTRL_ENABLE;
+       if (mbus->hw_io_coherency)
+               ctrl |= WIN_CTRL_SYNCBARRIER;
 
        writel(base & WIN_BASE_LOW, addr + WIN_BASE_OFF);
        writel(ctrl, addr + WIN_CTRL_OFF);
@@ -592,7 +618,7 @@ mvebu_mbus_find_bridge_hole(uint64_t *start, uint64_t *end)
                 * This part of the memory is above 4 GB, so we don't
                 * care for the MBus bridge hole.
                 */
-               if (r->base >= 0x100000000)
+               if (r->base >= 0x100000000ULL)
                        continue;
 
                /*
@@ -604,49 +630,32 @@ mvebu_mbus_find_bridge_hole(uint64_t *start, uint64_t *end)
        }
 
        *start = s;
-       *end = 0x100000000;
+       *end = 0x100000000ULL;
 }
 
+/*
+ * This function fills in the mvebu_mbus_dram_info_nooverlap data
+ * structure, by looking at the mvebu_mbus_dram_info data, and
+ * removing the parts of it that overlap with I/O windows.
+ */
 static void __init
-mvebu_mbus_default_setup_cpu_target(struct mvebu_mbus_state *mbus)
+mvebu_mbus_setup_cpu_target_nooverlap(struct mvebu_mbus_state *mbus)
 {
-       int i;
-       int cs;
        uint64_t mbus_bridge_base, mbus_bridge_end;
-
-       mvebu_mbus_dram_info.mbus_dram_target_id = TARGET_DDR;
+       int cs_nooverlap = 0;
+       int i;
 
        mvebu_mbus_find_bridge_hole(&mbus_bridge_base, &mbus_bridge_end);
 
-       for (i = 0, cs = 0; i < 4; i++) {
-               u64 base = readl(mbus->sdramwins_base + DDR_BASE_CS_OFF(i));
-               u64 size = readl(mbus->sdramwins_base + DDR_SIZE_CS_OFF(i));
-               u64 end;
+       for (i = 0; i < mvebu_mbus_dram_info.num_cs; i++) {
                struct mbus_dram_window *w;
+               u64 base, size, end;
 
-               /* Ignore entries that are not enabled */
-               if (!(size & DDR_SIZE_ENABLED))
-                       continue;
-
-               /*
-                * Ignore entries whose base address is above 2^32,
-                * since devices cannot DMA to such high addresses
-                */
-               if (base & DDR_BASE_CS_HIGH_MASK)
-                       continue;
-
-               base = base & DDR_BASE_CS_LOW_MASK;
-               size = (size | ~DDR_SIZE_MASK) + 1;
+               w = &mvebu_mbus_dram_info.cs[i];
+               base = w->base;
+               size = w->size;
                end = base + size;
 
-               /*
-                * Adjust base/size of the current CS to make sure it
-                * doesn't overlap with the MBus bridge hole. This is
-                * particularly important for devices that do DMA from
-                * DRAM to a SRAM mapped in a MBus window, such as the
-                * CESA cryptographic engine.
-                */
-
                /*
                 * The CS is fully enclosed inside the MBus bridge
                 * area, so ignore it.
@@ -670,7 +679,7 @@ mvebu_mbus_default_setup_cpu_target(struct mvebu_mbus_state *mbus)
                if (base < mbus_bridge_base && end > mbus_bridge_base)
                        size -= end - mbus_bridge_base;
 
-               w = &mvebu_mbus_dram_info.cs[cs++];
+               w = &mvebu_mbus_dram_info_nooverlap.cs[cs_nooverlap++];
                w->cs_index = i;
                w->mbus_attr = 0xf & ~(1 << i);
                if (mbus->hw_io_coherency)
@@ -678,6 +687,42 @@ mvebu_mbus_default_setup_cpu_target(struct mvebu_mbus_state *mbus)
                w->base = base;
                w->size = size;
        }
+
+       mvebu_mbus_dram_info_nooverlap.mbus_dram_target_id = TARGET_DDR;
+       mvebu_mbus_dram_info_nooverlap.num_cs = cs_nooverlap;
+}
+
+static void __init
+mvebu_mbus_default_setup_cpu_target(struct mvebu_mbus_state *mbus)
+{
+       int i;
+       int cs;
+
+       mvebu_mbus_dram_info.mbus_dram_target_id = TARGET_DDR;
+
+       for (i = 0, cs = 0; i < 4; i++) {
+               u32 base = readl(mbus->sdramwins_base + DDR_BASE_CS_OFF(i));
+               u32 size = readl(mbus->sdramwins_base + DDR_SIZE_CS_OFF(i));
+
+               /*
+                * We only take care of entries for which the chip
+                * select is enabled, and that don't have high base
+                * address bits set (devices can only access the first
+                * 32 bits of the memory).
+                */
+               if ((size & DDR_SIZE_ENABLED) &&
+                   !(base & DDR_BASE_CS_HIGH_MASK)) {
+                       struct mbus_dram_window *w;
+
+                       w = &mvebu_mbus_dram_info.cs[cs++];
+                       w->cs_index = i;
+                       w->mbus_attr = 0xf & ~(1 << i);
+                       if (mbus->hw_io_coherency)
+                               w->mbus_attr |= ATTR_HW_COHERENCY;
+                       w->base = base & DDR_BASE_CS_LOW_MASK;
+                       w->size = (size | ~DDR_SIZE_MASK) + 1;
+               }
+       }
        mvebu_mbus_dram_info.num_cs = cs;
 }
 
@@ -1035,6 +1080,7 @@ static int __init mvebu_mbus_common_init(struct mvebu_mbus_state *mbus,
                mvebu_mbus_disable_window(mbus, win);
 
        mbus->soc->setup_cpu_target(mbus);
+       mvebu_mbus_setup_cpu_target_nooverlap(mbus);
 
        if (is_coherent)
                writel(UNIT_SYNC_BARRIER_ALL,
index ebee57d715d2314df6b3ab0bff60cde656691a93..5012e3ad12256f952616a2dcca95e91a4783ac62 100644 (file)
@@ -301,7 +301,7 @@ static int omap_l3_probe(struct platform_device *pdev)
        return ret;
 }
 
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
 
 /**
  * l3_resume_noirq() - resume function for l3_noc
@@ -347,7 +347,7 @@ static int l3_resume_noirq(struct device *dev)
 }
 
 static const struct dev_pm_ops l3_dev_pm_ops = {
-       .resume_noirq           = l3_resume_noirq,
+       SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(NULL, l3_resume_noirq)
 };
 
 #define L3_DEV_PM_OPS (&l3_dev_pm_ops)
index a3bebef255ad3af669c2134ce85805d0af4b0523..0c98a9d51a2494e6a49ef49e6bfb557cefca1974 100644 (file)
@@ -33,7 +33,7 @@
 #include <asm/io.h>
 #include <asm/msr.h>
 #include <asm/cpufeature.h>
-#include <asm/i387.h>
+#include <asm/fpu/api.h>
 
 
 
index 8753b0f6a317790562cc1126805a11eea3a25a5d..9b409c0f14f7ccff38fa13f964c72e3d336666a1 100644 (file)
@@ -15,6 +15,8 @@
 #include <linux/list.h>
 #include <linux/module.h>
 #include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/interrupt.h>
 
 #include <asm/opal.h>
 
@@ -23,8 +25,7 @@ struct ipmi_smi_powernv {
        u64                     interface_id;
        struct ipmi_device_id   ipmi_id;
        ipmi_smi_t              intf;
-       u64                     event;
-       struct notifier_block   event_nb;
+       unsigned int            irq;
 
        /**
         * We assume that there can only be one outstanding request, so
@@ -197,15 +198,12 @@ static struct ipmi_smi_handlers ipmi_powernv_smi_handlers = {
        .poll                   = ipmi_powernv_poll,
 };
 
-static int ipmi_opal_event(struct notifier_block *nb,
-                         unsigned long events, void *change)
+static irqreturn_t ipmi_opal_event(int irq, void *data)
 {
-       struct ipmi_smi_powernv *smi = container_of(nb,
-                                       struct ipmi_smi_powernv, event_nb);
+       struct ipmi_smi_powernv *smi = data;
 
-       if (events & smi->event)
-               ipmi_powernv_recv(smi);
-       return 0;
+       ipmi_powernv_recv(smi);
+       return IRQ_HANDLED;
 }
 
 static int ipmi_powernv_probe(struct platform_device *pdev)
@@ -240,13 +238,16 @@ static int ipmi_powernv_probe(struct platform_device *pdev)
                goto err_free;
        }
 
-       ipmi->event = 1ull << prop;
-       ipmi->event_nb.notifier_call = ipmi_opal_event;
+       ipmi->irq = irq_of_parse_and_map(dev->of_node, 0);
+       if (!ipmi->irq) {
+               dev_info(dev, "Unable to map irq from device tree\n");
+               ipmi->irq = opal_event_request(prop);
+       }
 
-       rc = opal_notifier_register(&ipmi->event_nb);
-       if (rc) {
-               dev_warn(dev, "OPAL notifier registration failed (%d)\n", rc);
-               goto err_free;
+       if (request_irq(ipmi->irq, ipmi_opal_event, IRQ_TYPE_LEVEL_HIGH,
+                               "opal-ipmi", ipmi)) {
+               dev_warn(dev, "Unable to request irq\n");
+               goto err_dispose;
        }
 
        ipmi->opal_msg = devm_kmalloc(dev,
@@ -271,7 +272,9 @@ static int ipmi_powernv_probe(struct platform_device *pdev)
 err_free_msg:
        devm_kfree(dev, ipmi->opal_msg);
 err_unregister:
-       opal_notifier_unregister(&ipmi->event_nb);
+       free_irq(ipmi->irq, ipmi);
+err_dispose:
+       irq_dispose_mapping(ipmi->irq);
 err_free:
        devm_kfree(dev, ipmi);
        return rc;
@@ -282,7 +285,9 @@ static int ipmi_powernv_remove(struct platform_device *pdev)
        struct ipmi_smi_powernv *smi = dev_get_drvdata(&pdev->dev);
 
        ipmi_unregister_smi(smi->intf);
-       opal_notifier_unregister(&smi->event_nb);
+       free_irq(smi->irq, smi);
+       irq_dispose_mapping(smi->irq);
+
        return 0;
 }
 
index 8dd48a2be911db2626dd972c44fa2421c609ba8b..fc061f7c2bd1f7a850f96737c00cb06ac5dd56c1 100644 (file)
@@ -532,9 +532,8 @@ static int reader_config(struct pcmcia_device *link, int devno)
 
        fail_rc = pcmcia_enable_device(link);
        if (fail_rc != 0) {
-               dev_printk(KERN_INFO, &link->dev,
-                          "pcmcia_enable_device failed 0x%x\n",
-                          fail_rc);
+               dev_info(&link->dev, "pcmcia_enable_device failed 0x%x\n",
+                        fail_rc);
                goto cs_release;
        }
 
index 9cd6968e2f924bf7eb5c545c4298445651d5665d..d0da5d852d41e5588bb9bd192431a403a9696848 100644 (file)
@@ -409,6 +409,9 @@ static DECLARE_WAIT_QUEUE_HEAD(random_write_wait);
 static DECLARE_WAIT_QUEUE_HEAD(urandom_init_wait);
 static struct fasync_struct *fasync;
 
+static DEFINE_SPINLOCK(random_ready_list_lock);
+static LIST_HEAD(random_ready_list);
+
 /**********************************************************************
  *
  * OS independent entropy store.   Here are the functions which handle
@@ -589,6 +592,22 @@ static void fast_mix(struct fast_pool *f)
        f->count++;
 }
 
+static void process_random_ready_list(void)
+{
+       unsigned long flags;
+       struct random_ready_callback *rdy, *tmp;
+
+       spin_lock_irqsave(&random_ready_list_lock, flags);
+       list_for_each_entry_safe(rdy, tmp, &random_ready_list, list) {
+               struct module *owner = rdy->owner;
+
+               list_del_init(&rdy->list);
+               rdy->func(rdy);
+               module_put(owner);
+       }
+       spin_unlock_irqrestore(&random_ready_list_lock, flags);
+}
+
 /*
  * Credit (or debit) the entropy store with n bits of entropy.
  * Use credit_entropy_bits_safe() if the value comes from userspace
@@ -660,7 +679,8 @@ retry:
                r->entropy_total = 0;
                if (r == &nonblocking_pool) {
                        prandom_reseed_late();
-                       wake_up_interruptible(&urandom_init_wait);
+                       process_random_ready_list();
+                       wake_up_all(&urandom_init_wait);
                        pr_notice("random: %s pool is initialized\n", r->name);
                }
        }
@@ -1244,6 +1264,64 @@ void get_random_bytes(void *buf, int nbytes)
 }
 EXPORT_SYMBOL(get_random_bytes);
 
+/*
+ * Add a callback function that will be invoked when the nonblocking
+ * pool is initialised.
+ *
+ * returns: 0 if callback is successfully added
+ *         -EALREADY if pool is already initialised (callback not called)
+ *         -ENOENT if module for callback is not alive
+ */
+int add_random_ready_callback(struct random_ready_callback *rdy)
+{
+       struct module *owner;
+       unsigned long flags;
+       int err = -EALREADY;
+
+       if (likely(nonblocking_pool.initialized))
+               return err;
+
+       owner = rdy->owner;
+       if (!try_module_get(owner))
+               return -ENOENT;
+
+       spin_lock_irqsave(&random_ready_list_lock, flags);
+       if (nonblocking_pool.initialized)
+               goto out;
+
+       owner = NULL;
+
+       list_add(&rdy->list, &random_ready_list);
+       err = 0;
+
+out:
+       spin_unlock_irqrestore(&random_ready_list_lock, flags);
+
+       module_put(owner);
+
+       return err;
+}
+EXPORT_SYMBOL(add_random_ready_callback);
+
+/*
+ * Delete a previously registered readiness callback function.
+ */
+void del_random_ready_callback(struct random_ready_callback *rdy)
+{
+       unsigned long flags;
+       struct module *owner = NULL;
+
+       spin_lock_irqsave(&random_ready_list_lock, flags);
+       if (!list_empty(&rdy->list)) {
+               list_del_init(&rdy->list);
+               owner = rdy->owner;
+       }
+       spin_unlock_irqrestore(&random_ready_list_lock, flags);
+
+       module_put(owner);
+}
+EXPORT_SYMBOL(del_random_ready_callback);
+
 /*
  * This function will use the architecture-specific hardware random
  * number generator if it is available.  The arch-specific hw RNG will
index 597fed423d7d31906b1ca37f3fa9931231a63460..df2c1afa52b4acaa6204d5595a76c65f7dc0cb70 100644 (file)
@@ -29,7 +29,7 @@
 #define PERIPHERAL_RSHIFT_MASK 0x3
 #define PERIPHERAL_RSHIFT(val) (((val) >> 16) & PERIPHERAL_RSHIFT_MASK)
 
-#define PERIPHERAL_MAX_SHIFT   4
+#define PERIPHERAL_MAX_SHIFT   3
 
 struct clk_peripheral {
        struct clk_hw hw;
@@ -242,7 +242,7 @@ static long clk_sam9x5_peripheral_round_rate(struct clk_hw *hw,
                return *parent_rate;
 
        if (periph->range.max) {
-               for (; shift < PERIPHERAL_MAX_SHIFT; shift++) {
+               for (; shift <= PERIPHERAL_MAX_SHIFT; shift++) {
                        cur_rate = *parent_rate >> shift;
                        if (cur_rate <= periph->range.max)
                                break;
@@ -254,7 +254,7 @@ static long clk_sam9x5_peripheral_round_rate(struct clk_hw *hw,
 
        best_diff = cur_rate - rate;
        best_rate = cur_rate;
-       for (; shift < PERIPHERAL_MAX_SHIFT; shift++) {
+       for (; shift <= PERIPHERAL_MAX_SHIFT; shift++) {
                cur_rate = *parent_rate >> shift;
                if (cur_rate < rate)
                        cur_diff = rate - cur_rate;
@@ -289,7 +289,7 @@ static int clk_sam9x5_peripheral_set_rate(struct clk_hw *hw,
        if (periph->range.max && rate > periph->range.max)
                return -EINVAL;
 
-       for (shift = 0; shift < PERIPHERAL_MAX_SHIFT; shift++) {
+       for (shift = 0; shift <= PERIPHERAL_MAX_SHIFT; shift++) {
                if (parent_rate >> shift == rate) {
                        periph->auto_div = false;
                        periph->div = shift;
index 6ec79dbc0840ad8940e9e9ab599a0f865f1cd881..cbbe40377ad622a7f9d38aca5651916dda549e54 100644 (file)
@@ -173,8 +173,7 @@ static long clk_pll_get_best_div_mul(struct clk_pll *pll, unsigned long rate,
        int i = 0;
 
        /* Check if parent_rate is a valid input rate */
-       if (parent_rate < characteristics->input.min ||
-           parent_rate > characteristics->input.max)
+       if (parent_rate < characteristics->input.min)
                return -ERANGE;
 
        /*
@@ -187,6 +186,15 @@ static long clk_pll_get_best_div_mul(struct clk_pll *pll, unsigned long rate,
        if (!mindiv)
                mindiv = 1;
 
+       if (parent_rate > characteristics->input.max) {
+               tmpdiv = DIV_ROUND_UP(parent_rate, characteristics->input.max);
+               if (tmpdiv > PLL_DIV_MAX)
+                       return -ERANGE;
+
+               if (tmpdiv > mindiv)
+                       mindiv = tmpdiv;
+       }
+
        /*
         * Calculate the maximum divider which is limited by PLL register
         * layout (limited by the MUL or DIV field size).
index 69abb08cf146513b0307a4a78449b2e5da971282..eb8e5dc9076d46f07901a98db214fbeec0b0a3dc 100644 (file)
@@ -121,7 +121,7 @@ extern void __init of_at91sam9x5_clk_smd_setup(struct device_node *np,
                                               struct at91_pmc *pmc);
 #endif
 
-#if defined(CONFIG_HAVE_AT91_SMD)
+#if defined(CONFIG_HAVE_AT91_H32MX)
 extern void __init of_sama5d4_clk_h32mx_setup(struct device_node *np,
                                              struct at91_pmc *pmc);
 #endif
index bfa1e64e267d36a9286d5701069305133b5a092c..9b13a303d3f81e432f6ddac4ffe85dd27d4c8f82 100644 (file)
@@ -242,14 +242,12 @@ static int s2mps11_clk_probe(struct platform_device *pdev)
                        goto err_reg;
                }
 
-               s2mps11_clk->lookup = clkdev_alloc(s2mps11_clk->clk,
+               s2mps11_clk->lookup = clkdev_create(s2mps11_clk->clk,
                                        s2mps11_name(s2mps11_clk), NULL);
                if (!s2mps11_clk->lookup) {
                        ret = -ENOMEM;
                        goto err_lup;
                }
-
-               clkdev_add(s2mps11_clk->lookup);
        }
 
        for (i = 0; i < S2MPS11_CLKS_NUM; i++) {
index 1fcb6ef2cdacd75afe77257eec3da8d769052505..c0eaf0973bd2bca88e269dbca7f44fd3fcdaf3c7 100644 (file)
@@ -177,7 +177,7 @@ struct clk *clk_get_sys(const char *dev_id, const char *con_id)
        if (!cl)
                goto out;
 
-       clk = __clk_create_clk(__clk_get_hw(cl->clk), dev_id, con_id);
+       clk = __clk_create_clk(cl->clk_hw, dev_id, con_id);
        if (IS_ERR(clk))
                goto out;
 
@@ -215,18 +215,26 @@ void clk_put(struct clk *clk)
 }
 EXPORT_SYMBOL(clk_put);
 
-void clkdev_add(struct clk_lookup *cl)
+static void __clkdev_add(struct clk_lookup *cl)
 {
        mutex_lock(&clocks_mutex);
        list_add_tail(&cl->node, &clocks);
        mutex_unlock(&clocks_mutex);
 }
+
+void clkdev_add(struct clk_lookup *cl)
+{
+       if (!cl->clk_hw)
+               cl->clk_hw = __clk_get_hw(cl->clk);
+       __clkdev_add(cl);
+}
 EXPORT_SYMBOL(clkdev_add);
 
-void __init clkdev_add_table(struct clk_lookup *cl, size_t num)
+void clkdev_add_table(struct clk_lookup *cl, size_t num)
 {
        mutex_lock(&clocks_mutex);
        while (num--) {
+               cl->clk_hw = __clk_get_hw(cl->clk);
                list_add_tail(&cl->node, &clocks);
                cl++;
        }
@@ -243,7 +251,7 @@ struct clk_lookup_alloc {
 };
 
 static struct clk_lookup * __init_refok
-vclkdev_alloc(struct clk *clk, const char *con_id, const char *dev_fmt,
+vclkdev_alloc(struct clk_hw *hw, const char *con_id, const char *dev_fmt,
        va_list ap)
 {
        struct clk_lookup_alloc *cla;
@@ -252,7 +260,7 @@ vclkdev_alloc(struct clk *clk, const char *con_id, const char *dev_fmt,
        if (!cla)
                return NULL;
 
-       cla->cl.clk = clk;
+       cla->cl.clk_hw = hw;
        if (con_id) {
                strlcpy(cla->con_id, con_id, sizeof(cla->con_id));
                cla->cl.con_id = cla->con_id;
@@ -266,6 +274,19 @@ vclkdev_alloc(struct clk *clk, const char *con_id, const char *dev_fmt,
        return &cla->cl;
 }
 
+static struct clk_lookup *
+vclkdev_create(struct clk_hw *hw, const char *con_id, const char *dev_fmt,
+       va_list ap)
+{
+       struct clk_lookup *cl;
+
+       cl = vclkdev_alloc(hw, con_id, dev_fmt, ap);
+       if (cl)
+               __clkdev_add(cl);
+
+       return cl;
+}
+
 struct clk_lookup * __init_refok
 clkdev_alloc(struct clk *clk, const char *con_id, const char *dev_fmt, ...)
 {
@@ -273,28 +294,49 @@ clkdev_alloc(struct clk *clk, const char *con_id, const char *dev_fmt, ...)
        va_list ap;
 
        va_start(ap, dev_fmt);
-       cl = vclkdev_alloc(clk, con_id, dev_fmt, ap);
+       cl = vclkdev_alloc(__clk_get_hw(clk), con_id, dev_fmt, ap);
        va_end(ap);
 
        return cl;
 }
 EXPORT_SYMBOL(clkdev_alloc);
 
-int clk_add_alias(const char *alias, const char *alias_dev_name, char *id,
-       struct device *dev)
+/**
+ * clkdev_create - allocate and add a clkdev lookup structure
+ * @clk: struct clk to associate with all clk_lookups
+ * @con_id: connection ID string on device
+ * @dev_fmt: format string describing device name
+ *
+ * Returns a clk_lookup structure, which can be later unregistered and
+ * freed.
+ */
+struct clk_lookup *clkdev_create(struct clk *clk, const char *con_id,
+       const char *dev_fmt, ...)
 {
-       struct clk *r = clk_get(dev, id);
+       struct clk_lookup *cl;
+       va_list ap;
+
+       va_start(ap, dev_fmt);
+       cl = vclkdev_create(__clk_get_hw(clk), con_id, dev_fmt, ap);
+       va_end(ap);
+
+       return cl;
+}
+EXPORT_SYMBOL_GPL(clkdev_create);
+
+int clk_add_alias(const char *alias, const char *alias_dev_name,
+       const char *con_id, struct device *dev)
+{
+       struct clk *r = clk_get(dev, con_id);
        struct clk_lookup *l;
 
        if (IS_ERR(r))
                return PTR_ERR(r);
 
-       l = clkdev_alloc(r, alias, alias_dev_name);
+       l = clkdev_create(r, alias, "%s", alias_dev_name);
        clk_put(r);
-       if (!l)
-               return -ENODEV;
-       clkdev_add(l);
-       return 0;
+
+       return l ? 0 : -ENODEV;
 }
 EXPORT_SYMBOL(clk_add_alias);
 
@@ -334,15 +376,10 @@ int clk_register_clkdev(struct clk *clk, const char *con_id,
                return PTR_ERR(clk);
 
        va_start(ap, dev_fmt);
-       cl = vclkdev_alloc(clk, con_id, dev_fmt, ap);
+       cl = vclkdev_create(__clk_get_hw(clk), con_id, dev_fmt, ap);
        va_end(ap);
 
-       if (!cl)
-               return -ENOMEM;
-
-       clkdev_add(cl);
-
-       return 0;
+       return cl ? 0 : -ENOMEM;
 }
 EXPORT_SYMBOL(clk_register_clkdev);
 
@@ -365,8 +402,8 @@ int clk_register_clkdevs(struct clk *clk, struct clk_lookup *cl, size_t num)
                return PTR_ERR(clk);
 
        for (i = 0; i < num; i++, cl++) {
-               cl->clk = clk;
-               clkdev_add(cl);
+               cl->clk_hw = __clk_get_hw(clk);
+               __clkdev_add(cl);
        }
 
        return 0;
index 5d2217ae447873656a9869649f0e89abe1028106..63b8323df91836265203015b120af5e2bce82e0c 100644 (file)
@@ -305,13 +305,14 @@ static struct ti_dt_clk dra7xx_clks[] = {
        DT_CLK("4882c000.timer", "timer_sys_ck", "timer_sys_clk_div"),
        DT_CLK("4882e000.timer", "timer_sys_ck", "timer_sys_clk_div"),
        DT_CLK(NULL, "sys_clkin", "sys_clkin1"),
+       DT_CLK(NULL, "dss_deshdcp_clk", "dss_deshdcp_clk"),
        { .node_name = NULL },
 };
 
 int __init dra7xx_dt_clk_init(void)
 {
        int rc;
-       struct clk *abe_dpll_mux, *sys_clkin2, *dpll_ck;
+       struct clk *abe_dpll_mux, *sys_clkin2, *dpll_ck, *hdcp_ck;
 
        ti_dt_clocks_register(dra7xx_clks);
 
@@ -347,5 +348,10 @@ int __init dra7xx_dt_clk_init(void)
        if (rc)
                pr_err("%s: failed to set USB_DPLL M2 OUT\n", __func__);
 
+       hdcp_ck = clk_get_sys(NULL, "dss_deshdcp_clk");
+       rc = clk_prepare_enable(hdcp_ck);
+       if (rc)
+               pr_err("%s: failed to set dss_deshdcp_clk\n", __func__);
+
        return rc;
 }
index 51d7865fdddb6d59ae7a9406ae8d88cb4da30066..32164ba3d36a75e12a205c53aa8cf000aaa5454a 100644 (file)
@@ -106,6 +106,16 @@ config CLKSRC_EFM32
          Support to use the timers of EFM32 SoCs as clock source and clock
          event device.
 
+config CLKSRC_LPC32XX
+       bool
+       select CLKSRC_MMIO
+       select CLKSRC_OF
+
+config CLKSRC_STM32
+       bool "Clocksource for STM32 SoCs" if !ARCH_STM32
+       depends on OF && ARM && (ARCH_STM32 || COMPILE_TEST)
+       select CLKSRC_MMIO
+
 config ARM_ARCH_TIMER
        bool
        select CLKSRC_OF if OF
@@ -139,6 +149,13 @@ config CLKSRC_ARM_GLOBAL_TIMER_SCHED_CLOCK
        help
         Use ARM global timer clock source as sched_clock
 
+config ARMV7M_SYSTICK
+       bool
+       select CLKSRC_OF if OF
+       select CLKSRC_MMIO
+       help
+         This options enables support for the ARMv7M system timer unit
+
 config ATMEL_PIT
        select CLKSRC_OF if OF
        def_bool SOC_AT91SAM9 || SOC_SAMA5
index 5b85f6adb25834c807c24aafa08ed0529b2e8a53..1831a588b988b508e885687313e189bacffda4a2 100644 (file)
@@ -36,7 +36,9 @@ obj-$(CONFIG_ARCH_NSPIRE)     += zevio-timer.o
 obj-$(CONFIG_ARCH_BCM_MOBILE)  += bcm_kona_timer.o
 obj-$(CONFIG_CADENCE_TTC_TIMER)        += cadence_ttc_timer.o
 obj-$(CONFIG_CLKSRC_EFM32)     += time-efm32.o
+obj-$(CONFIG_CLKSRC_STM32)     += timer-stm32.o
 obj-$(CONFIG_CLKSRC_EXYNOS_MCT)        += exynos_mct.o
+obj-$(CONFIG_CLKSRC_LPC32XX)   += time-lpc32xx.o
 obj-$(CONFIG_CLKSRC_SAMSUNG_PWM)       += samsung_pwm_timer.o
 obj-$(CONFIG_FSL_FTM_TIMER)    += fsl_ftm_timer.o
 obj-$(CONFIG_VF_PIT_TIMER)     += vf_pit_timer.o
@@ -45,6 +47,7 @@ obj-$(CONFIG_MTK_TIMER)               += mtk_timer.o
 
 obj-$(CONFIG_ARM_ARCH_TIMER)           += arm_arch_timer.o
 obj-$(CONFIG_ARM_GLOBAL_TIMER)         += arm_global_timer.o
+obj-$(CONFIG_ARMV7M_SYSTICK)           += armv7m_systick.o
 obj-$(CONFIG_CLKSRC_METAG_GENERIC)     += metag_generic.o
 obj-$(CONFIG_ARCH_HAS_TICK_BROADCAST)  += dummy_timer.o
 obj-$(CONFIG_ARCH_KEYSTONE)            += timer-keystone.o
diff --git a/drivers/clocksource/armv7m_systick.c b/drivers/clocksource/armv7m_systick.c
new file mode 100644 (file)
index 0000000..addfd2c
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) Maxime Coquelin 2015
+ * Author:  Maxime Coquelin <mcoquelin.stm32@gmail.com>
+ * License terms:  GNU General Public License (GPL), version 2
+ */
+
+#include <linux/kernel.h>
+#include <linux/clocksource.h>
+#include <linux/clockchips.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/clk.h>
+#include <linux/bitops.h>
+
+#define SYST_CSR       0x00
+#define SYST_RVR       0x04
+#define SYST_CVR       0x08
+#define SYST_CALIB     0x0c
+
+#define SYST_CSR_ENABLE BIT(0)
+
+#define SYSTICK_LOAD_RELOAD_MASK 0x00FFFFFF
+
+static void __init system_timer_of_register(struct device_node *np)
+{
+       struct clk *clk = NULL;
+       void __iomem *base;
+       u32 rate;
+       int ret;
+
+       base = of_iomap(np, 0);
+       if (!base) {
+               pr_warn("system-timer: invalid base address\n");
+               return;
+       }
+
+       ret = of_property_read_u32(np, "clock-frequency", &rate);
+       if (ret) {
+               clk = of_clk_get(np, 0);
+               if (IS_ERR(clk))
+                       goto out_unmap;
+
+               ret = clk_prepare_enable(clk);
+               if (ret)
+                       goto out_clk_put;
+
+               rate = clk_get_rate(clk);
+               if (!rate)
+                       goto out_clk_disable;
+       }
+
+       writel_relaxed(SYSTICK_LOAD_RELOAD_MASK, base + SYST_RVR);
+       writel_relaxed(SYST_CSR_ENABLE, base + SYST_CSR);
+
+       ret = clocksource_mmio_init(base + SYST_CVR, "arm_system_timer", rate,
+                       200, 24, clocksource_mmio_readl_down);
+       if (ret) {
+               pr_err("failed to init clocksource (%d)\n", ret);
+               if (clk)
+                       goto out_clk_disable;
+               else
+                       goto out_unmap;
+       }
+
+       pr_info("ARM System timer initialized as clocksource\n");
+
+       return;
+
+out_clk_disable:
+       clk_disable_unprepare(clk);
+out_clk_put:
+       clk_put(clk);
+out_unmap:
+       iounmap(base);
+       pr_warn("ARM System timer register failed (%d)\n", ret);
+}
+
+CLOCKSOURCE_OF_DECLARE(arm_systick, "arm,armv7m-systick",
+                       system_timer_of_register);
index 2c9c993727c85a1975b845d3fe7b49d3115def97..4c2ba59897e84bda7c12368874e3c8f7716ae70c 100644 (file)
@@ -178,7 +178,7 @@ static void __init asm9260_timer_init(struct device_node *np)
        unsigned long rate;
 
        priv.base = of_io_request_and_map(np, 0, np->name);
-       if (!priv.base)
+       if (IS_ERR(priv.base))
                panic("%s: unable to map resource", np->name);
 
        clk = of_clk_get(np, 0);
index 83564c9cfdbe3b18dfb07a3799b73b991ffe00c7..935b05936dbdd9588764b0c04bf84db32b425af0 100644 (file)
@@ -209,7 +209,7 @@ static void exynos4_frc_resume(struct clocksource *cs)
        exynos4_mct_frc_start();
 }
 
-struct clocksource mct_frc = {
+static struct clocksource mct_frc = {
        .name           = "mct-frc",
        .rating         = 400,
        .read           = exynos4_frc_read,
@@ -413,7 +413,7 @@ static inline void exynos4_tick_set_mode(enum clock_event_mode mode,
        }
 }
 
-static int exynos4_mct_tick_clear(struct mct_clock_event_device *mevt)
+static void exynos4_mct_tick_clear(struct mct_clock_event_device *mevt)
 {
        struct clock_event_device *evt = &mevt->evt;
 
@@ -426,12 +426,8 @@ static int exynos4_mct_tick_clear(struct mct_clock_event_device *mevt)
                exynos4_mct_tick_stop(mevt);
 
        /* Clear the MCT tick interrupt */
-       if (readl_relaxed(reg_base + mevt->base + MCT_L_INT_CSTAT_OFFSET) & 1) {
+       if (readl_relaxed(reg_base + mevt->base + MCT_L_INT_CSTAT_OFFSET) & 1)
                exynos4_mct_write(0x1, mevt->base + MCT_L_INT_CSTAT_OFFSET);
-               return 1;
-       } else {
-               return 0;
-       }
 }
 
 static irqreturn_t exynos4_mct_tick_isr(int irq, void *dev_id)
@@ -564,18 +560,6 @@ out_irq:
        free_percpu_irq(mct_irqs[MCT_L0_IRQ], &percpu_mct_tick);
 }
 
-void __init mct_init(void __iomem *base, int irq_g0, int irq_l0, int irq_l1)
-{
-       mct_irqs[MCT_G0_IRQ] = irq_g0;
-       mct_irqs[MCT_L0_IRQ] = irq_l0;
-       mct_irqs[MCT_L1_IRQ] = irq_l1;
-       mct_int_type = MCT_INT_SPI;
-
-       exynos4_timer_resources(NULL, base);
-       exynos4_clocksource_init();
-       exynos4_clockevent_init();
-}
-
 static void __init mct_init_dt(struct device_node *np, unsigned int int_type)
 {
        u32 nr_irqs, i;
index 098c542e5c537dca3f159771960b453d61b578a8..cba2d015564c2cfe9dd9735ef771d7bc355ecca9 100644 (file)
@@ -40,8 +40,6 @@
 
 #define GPT_HZ 32768
 
-#define MSM_DGT_SHIFT 5
-
 static void __iomem *event_base;
 static void __iomem *sts_base;
 
@@ -232,7 +230,6 @@ err:
        register_current_timer_delay(&msm_delay_timer);
 }
 
-#ifdef CONFIG_ARCH_QCOM
 static void __init msm_dt_timer_init(struct device_node *np)
 {
        u32 freq;
@@ -285,59 +282,3 @@ static void __init msm_dt_timer_init(struct device_node *np)
 }
 CLOCKSOURCE_OF_DECLARE(kpss_timer, "qcom,kpss-timer", msm_dt_timer_init);
 CLOCKSOURCE_OF_DECLARE(scss_timer, "qcom,scss-timer", msm_dt_timer_init);
-#else
-
-static int __init msm_timer_map(phys_addr_t addr, u32 event, u32 source,
-                               u32 sts)
-{
-       void __iomem *base;
-
-       base = ioremap(addr, SZ_256);
-       if (!base) {
-               pr_err("Failed to map timer base\n");
-               return -ENOMEM;
-       }
-       event_base = base + event;
-       source_base = base + source;
-       if (sts)
-               sts_base = base + sts;
-
-       return 0;
-}
-
-static notrace cycle_t msm_read_timer_count_shift(struct clocksource *cs)
-{
-       /*
-        * Shift timer count down by a constant due to unreliable lower bits
-        * on some targets.
-        */
-       return msm_read_timer_count(cs) >> MSM_DGT_SHIFT;
-}
-
-void __init msm7x01_timer_init(void)
-{
-       struct clocksource *cs = &msm_clocksource;
-
-       if (msm_timer_map(0xc0100000, 0x0, 0x10, 0x0))
-               return;
-       cs->read = msm_read_timer_count_shift;
-       cs->mask = CLOCKSOURCE_MASK((32 - MSM_DGT_SHIFT));
-       /* 600 KHz */
-       msm_timer_init(19200000 >> MSM_DGT_SHIFT, 32 - MSM_DGT_SHIFT, 7,
-                       false);
-}
-
-void __init msm7x30_timer_init(void)
-{
-       if (msm_timer_map(0xc0100000, 0x4, 0x24, 0x80))
-               return;
-       msm_timer_init(24576000 / 4, 32, 1, false);
-}
-
-void __init qsd8x50_timer_init(void)
-{
-       if (msm_timer_map(0xAC100000, 0x0, 0x10, 0x34))
-               return;
-       msm_timer_init(19200000 / 4, 32, 7, false);
-}
-#endif
diff --git a/drivers/clocksource/time-lpc32xx.c b/drivers/clocksource/time-lpc32xx.c
new file mode 100644 (file)
index 0000000..a1c06a2
--- /dev/null
@@ -0,0 +1,272 @@
+/*
+ * Clocksource driver for NXP LPC32xx/18xx/43xx timer
+ *
+ * Copyright (C) 2015 Joachim Eastwood <manabian@gmail.com>
+ *
+ * Based on:
+ * time-efm32 Copyright (C) 2013 Pengutronix
+ * mach-lpc32xx/timer.c Copyright (C) 2009 - 2010 NXP Semiconductors
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ *
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/clk.h>
+#include <linux/clockchips.h>
+#include <linux/clocksource.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/sched_clock.h>
+
+#define LPC32XX_TIMER_IR               0x000
+#define  LPC32XX_TIMER_IR_MR0INT       BIT(0)
+#define LPC32XX_TIMER_TCR              0x004
+#define  LPC32XX_TIMER_TCR_CEN         BIT(0)
+#define  LPC32XX_TIMER_TCR_CRST                BIT(1)
+#define LPC32XX_TIMER_TC               0x008
+#define LPC32XX_TIMER_PR               0x00c
+#define LPC32XX_TIMER_MCR              0x014
+#define  LPC32XX_TIMER_MCR_MR0I                BIT(0)
+#define  LPC32XX_TIMER_MCR_MR0R                BIT(1)
+#define  LPC32XX_TIMER_MCR_MR0S                BIT(2)
+#define LPC32XX_TIMER_MR0              0x018
+#define LPC32XX_TIMER_CTCR             0x070
+
+struct lpc32xx_clock_event_ddata {
+       struct clock_event_device evtdev;
+       void __iomem *base;
+};
+
+/* Needed for the sched clock */
+static void __iomem *clocksource_timer_counter;
+
+static u64 notrace lpc32xx_read_sched_clock(void)
+{
+       return readl(clocksource_timer_counter);
+}
+
+static int lpc32xx_clkevt_next_event(unsigned long delta,
+                                    struct clock_event_device *evtdev)
+{
+       struct lpc32xx_clock_event_ddata *ddata =
+               container_of(evtdev, struct lpc32xx_clock_event_ddata, evtdev);
+
+       /*
+        * Place timer in reset and program the delta in the prescale
+        * register (PR). When the prescale counter matches the value
+        * in PR the counter register is incremented and the compare
+        * match will trigger. After setup the timer is released from
+        * reset and enabled.
+        */
+       writel_relaxed(LPC32XX_TIMER_TCR_CRST, ddata->base + LPC32XX_TIMER_TCR);
+       writel_relaxed(delta, ddata->base + LPC32XX_TIMER_PR);
+       writel_relaxed(LPC32XX_TIMER_TCR_CEN, ddata->base + LPC32XX_TIMER_TCR);
+
+       return 0;
+}
+
+static int lpc32xx_clkevt_shutdown(struct clock_event_device *evtdev)
+{
+       struct lpc32xx_clock_event_ddata *ddata =
+               container_of(evtdev, struct lpc32xx_clock_event_ddata, evtdev);
+
+       /* Disable the timer */
+       writel_relaxed(0, ddata->base + LPC32XX_TIMER_TCR);
+
+       return 0;
+}
+
+static int lpc32xx_clkevt_oneshot(struct clock_event_device *evtdev)
+{
+       /*
+        * When using oneshot, we must also disable the timer
+        * to wait for the first call to set_next_event().
+        */
+       return lpc32xx_clkevt_shutdown(evtdev);
+}
+
+static irqreturn_t lpc32xx_clock_event_handler(int irq, void *dev_id)
+{
+       struct lpc32xx_clock_event_ddata *ddata = dev_id;
+
+       /* Clear match on channel 0 */
+       writel_relaxed(LPC32XX_TIMER_IR_MR0INT, ddata->base + LPC32XX_TIMER_IR);
+
+       ddata->evtdev.event_handler(&ddata->evtdev);
+
+       return IRQ_HANDLED;
+}
+
+static struct lpc32xx_clock_event_ddata lpc32xx_clk_event_ddata = {
+       .evtdev = {
+               .name                   = "lpc3220 clockevent",
+               .features               = CLOCK_EVT_FEAT_ONESHOT,
+               .rating                 = 300,
+               .set_next_event         = lpc32xx_clkevt_next_event,
+               .set_state_shutdown     = lpc32xx_clkevt_shutdown,
+               .set_state_oneshot      = lpc32xx_clkevt_oneshot,
+       },
+};
+
+static int __init lpc32xx_clocksource_init(struct device_node *np)
+{
+       void __iomem *base;
+       unsigned long rate;
+       struct clk *clk;
+       int ret;
+
+       clk = of_clk_get_by_name(np, "timerclk");
+       if (IS_ERR(clk)) {
+               pr_err("clock get failed (%lu)\n", PTR_ERR(clk));
+               return PTR_ERR(clk);
+       }
+
+       ret = clk_prepare_enable(clk);
+       if (ret) {
+               pr_err("clock enable failed (%d)\n", ret);
+               goto err_clk_enable;
+       }
+
+       base = of_iomap(np, 0);
+       if (!base) {
+               pr_err("unable to map registers\n");
+               ret = -EADDRNOTAVAIL;
+               goto err_iomap;
+       }
+
+       /*
+        * Disable and reset timer then set it to free running timer
+        * mode (CTCR) with no prescaler (PR) or match operations (MCR).
+        * After setup the timer is released from reset and enabled.
+        */
+       writel_relaxed(LPC32XX_TIMER_TCR_CRST, base + LPC32XX_TIMER_TCR);
+       writel_relaxed(0, base + LPC32XX_TIMER_PR);
+       writel_relaxed(0, base + LPC32XX_TIMER_MCR);
+       writel_relaxed(0, base + LPC32XX_TIMER_CTCR);
+       writel_relaxed(LPC32XX_TIMER_TCR_CEN, base + LPC32XX_TIMER_TCR);
+
+       rate = clk_get_rate(clk);
+       ret = clocksource_mmio_init(base + LPC32XX_TIMER_TC, "lpc3220 timer",
+                                   rate, 300, 32, clocksource_mmio_readl_up);
+       if (ret) {
+               pr_err("failed to init clocksource (%d)\n", ret);
+               goto err_clocksource_init;
+       }
+
+       clocksource_timer_counter = base + LPC32XX_TIMER_TC;
+       sched_clock_register(lpc32xx_read_sched_clock, 32, rate);
+
+       return 0;
+
+err_clocksource_init:
+       iounmap(base);
+err_iomap:
+       clk_disable_unprepare(clk);
+err_clk_enable:
+       clk_put(clk);
+       return ret;
+}
+
+static int __init lpc32xx_clockevent_init(struct device_node *np)
+{
+       void __iomem *base;
+       unsigned long rate;
+       struct clk *clk;
+       int ret, irq;
+
+       clk = of_clk_get_by_name(np, "timerclk");
+       if (IS_ERR(clk)) {
+               pr_err("clock get failed (%lu)\n", PTR_ERR(clk));
+               return PTR_ERR(clk);
+       }
+
+       ret = clk_prepare_enable(clk);
+       if (ret) {
+               pr_err("clock enable failed (%d)\n", ret);
+               goto err_clk_enable;
+       }
+
+       base = of_iomap(np, 0);
+       if (!base) {
+               pr_err("unable to map registers\n");
+               ret = -EADDRNOTAVAIL;
+               goto err_iomap;
+       }
+
+       irq = irq_of_parse_and_map(np, 0);
+       if (!irq) {
+               pr_err("get irq failed\n");
+               ret = -ENOENT;
+               goto err_irq;
+       }
+
+       /*
+        * Disable timer and clear any pending interrupt (IR) on match
+        * channel 0 (MR0). Configure a compare match value of 1 on MR0
+        * and enable interrupt, reset on match and stop on match (MCR).
+        */
+       writel_relaxed(0, base + LPC32XX_TIMER_TCR);
+       writel_relaxed(0, base + LPC32XX_TIMER_CTCR);
+       writel_relaxed(LPC32XX_TIMER_IR_MR0INT, base + LPC32XX_TIMER_IR);
+       writel_relaxed(1, base + LPC32XX_TIMER_MR0);
+       writel_relaxed(LPC32XX_TIMER_MCR_MR0I | LPC32XX_TIMER_MCR_MR0R |
+                      LPC32XX_TIMER_MCR_MR0S, base + LPC32XX_TIMER_MCR);
+
+       rate = clk_get_rate(clk);
+       lpc32xx_clk_event_ddata.base = base;
+       clockevents_config_and_register(&lpc32xx_clk_event_ddata.evtdev,
+                                       rate, 1, -1);
+
+       ret = request_irq(irq, lpc32xx_clock_event_handler,
+                         IRQF_TIMER | IRQF_IRQPOLL, "lpc3220 clockevent",
+                         &lpc32xx_clk_event_ddata);
+       if (ret) {
+               pr_err("request irq failed\n");
+               goto err_irq;
+       }
+
+       return 0;
+
+err_irq:
+       iounmap(base);
+err_iomap:
+       clk_disable_unprepare(clk);
+err_clk_enable:
+       clk_put(clk);
+       return ret;
+}
+
+/*
+ * This function asserts that we have exactly one clocksource and one
+ * clock_event_device in the end.
+ */
+static void __init lpc32xx_timer_init(struct device_node *np)
+{
+       static int has_clocksource, has_clockevent;
+       int ret;
+
+       if (!has_clocksource) {
+               ret = lpc32xx_clocksource_init(np);
+               if (!ret) {
+                       has_clocksource = 1;
+                       return;
+               }
+       }
+
+       if (!has_clockevent) {
+               ret = lpc32xx_clockevent_init(np);
+               if (!ret) {
+                       has_clockevent = 1;
+                       return;
+               }
+       }
+}
+CLOCKSOURCE_OF_DECLARE(lpc32xx_timer, "nxp,lpc3220-timer", lpc32xx_timer_init);
index b9efd30513d56214612913dfc0f0a30b9464ea70..c97d1980c0f856f37b3f264d6b525f3a9e68540a 100644 (file)
@@ -166,7 +166,7 @@ static void __init integrator_ap_timer_init_of(struct device_node *node)
        struct device_node *sec_node;
 
        base = of_io_request_and_map(node, 0, "integrator-timer");
-       if (!base)
+       if (IS_ERR(base))
                return;
 
        clk = of_clk_get(node, 0);
diff --git a/drivers/clocksource/timer-stm32.c b/drivers/clocksource/timer-stm32.c
new file mode 100644 (file)
index 0000000..a97e8b5
--- /dev/null
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) Maxime Coquelin 2015
+ * Author:  Maxime Coquelin <mcoquelin.stm32@gmail.com>
+ * License terms:  GNU General Public License (GPL), version 2
+ *
+ * Inspired by time-efm32.c from Uwe Kleine-Koenig
+ */
+
+#include <linux/kernel.h>
+#include <linux/clocksource.h>
+#include <linux/clockchips.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/clk.h>
+#include <linux/reset.h>
+
+#define TIM_CR1                0x00
+#define TIM_DIER       0x0c
+#define TIM_SR         0x10
+#define TIM_EGR                0x14
+#define TIM_PSC                0x28
+#define TIM_ARR                0x2c
+
+#define TIM_CR1_CEN    BIT(0)
+#define TIM_CR1_OPM    BIT(3)
+#define TIM_CR1_ARPE   BIT(7)
+
+#define TIM_DIER_UIE   BIT(0)
+
+#define TIM_SR_UIF     BIT(0)
+
+#define TIM_EGR_UG     BIT(0)
+
+struct stm32_clock_event_ddata {
+       struct clock_event_device evtdev;
+       unsigned periodic_top;
+       void __iomem *base;
+};
+
+static void stm32_clock_event_set_mode(enum clock_event_mode mode,
+                                      struct clock_event_device *evtdev)
+{
+       struct stm32_clock_event_ddata *data =
+               container_of(evtdev, struct stm32_clock_event_ddata, evtdev);
+       void *base = data->base;
+
+       switch (mode) {
+       case CLOCK_EVT_MODE_PERIODIC:
+               writel_relaxed(data->periodic_top, base + TIM_ARR);
+               writel_relaxed(TIM_CR1_ARPE | TIM_CR1_CEN, base + TIM_CR1);
+               break;
+
+       case CLOCK_EVT_MODE_ONESHOT:
+       default:
+               writel_relaxed(0, base + TIM_CR1);
+               break;
+       }
+}
+
+static int stm32_clock_event_set_next_event(unsigned long evt,
+                                           struct clock_event_device *evtdev)
+{
+       struct stm32_clock_event_ddata *data =
+               container_of(evtdev, struct stm32_clock_event_ddata, evtdev);
+
+       writel_relaxed(evt, data->base + TIM_ARR);
+       writel_relaxed(TIM_CR1_ARPE | TIM_CR1_OPM | TIM_CR1_CEN,
+                      data->base + TIM_CR1);
+
+       return 0;
+}
+
+static irqreturn_t stm32_clock_event_handler(int irq, void *dev_id)
+{
+       struct stm32_clock_event_ddata *data = dev_id;
+
+       writel_relaxed(0, data->base + TIM_SR);
+
+       data->evtdev.event_handler(&data->evtdev);
+
+       return IRQ_HANDLED;
+}
+
+static struct stm32_clock_event_ddata clock_event_ddata = {
+       .evtdev = {
+               .name = "stm32 clockevent",
+               .features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_PERIODIC,
+               .set_mode = stm32_clock_event_set_mode,
+               .set_next_event = stm32_clock_event_set_next_event,
+               .rating = 200,
+       },
+};
+
+static void __init stm32_clockevent_init(struct device_node *np)
+{
+       struct stm32_clock_event_ddata *data = &clock_event_ddata;
+       struct clk *clk;
+       struct reset_control *rstc;
+       unsigned long rate, max_delta;
+       int irq, ret, bits, prescaler = 1;
+
+       clk = of_clk_get(np, 0);
+       if (IS_ERR(clk)) {
+               ret = PTR_ERR(clk);
+               pr_err("failed to get clock for clockevent (%d)\n", ret);
+               goto err_clk_get;
+       }
+
+       ret = clk_prepare_enable(clk);
+       if (ret) {
+               pr_err("failed to enable timer clock for clockevent (%d)\n",
+                      ret);
+               goto err_clk_enable;
+       }
+
+       rate = clk_get_rate(clk);
+
+       rstc = of_reset_control_get(np, NULL);
+       if (!IS_ERR(rstc)) {
+               reset_control_assert(rstc);
+               reset_control_deassert(rstc);
+       }
+
+       data->base = of_iomap(np, 0);
+       if (!data->base) {
+               pr_err("failed to map registers for clockevent\n");
+               goto err_iomap;
+       }
+
+       irq = irq_of_parse_and_map(np, 0);
+       if (!irq) {
+               pr_err("%s: failed to get irq.\n", np->full_name);
+               goto err_get_irq;
+       }
+
+       /* Detect whether the timer is 16 or 32 bits */
+       writel_relaxed(~0U, data->base + TIM_ARR);
+       max_delta = readl_relaxed(data->base + TIM_ARR);
+       if (max_delta == ~0U) {
+               prescaler = 1;
+               bits = 32;
+       } else {
+               prescaler = 1024;
+               bits = 16;
+       }
+       writel_relaxed(0, data->base + TIM_ARR);
+
+       writel_relaxed(prescaler - 1, data->base + TIM_PSC);
+       writel_relaxed(TIM_EGR_UG, data->base + TIM_EGR);
+       writel_relaxed(TIM_DIER_UIE, data->base + TIM_DIER);
+       writel_relaxed(0, data->base + TIM_SR);
+
+       data->periodic_top = DIV_ROUND_CLOSEST(rate, prescaler * HZ);
+
+       clockevents_config_and_register(&data->evtdev,
+                                       DIV_ROUND_CLOSEST(rate, prescaler),
+                                       0x1, max_delta);
+
+       ret = request_irq(irq, stm32_clock_event_handler, IRQF_TIMER,
+                       "stm32 clockevent", data);
+       if (ret) {
+               pr_err("%s: failed to request irq.\n", np->full_name);
+               goto err_get_irq;
+       }
+
+       pr_info("%s: STM32 clockevent driver initialized (%d bits)\n",
+                       np->full_name, bits);
+
+       return;
+
+err_get_irq:
+       iounmap(data->base);
+err_iomap:
+       clk_disable_unprepare(clk);
+err_clk_enable:
+       clk_put(clk);
+err_clk_get:
+       return;
+}
+
+CLOCKSOURCE_OF_DECLARE(stm32, "st,stm32-timer", stm32_clockevent_init);
index 28aa4b7bb6020c416974ec52c8f80eb12366e705..0ffb4ea7c9253eb883afe47cd9682722f5c588c6 100644 (file)
@@ -324,7 +324,7 @@ static void __init sun5i_timer_init(struct device_node *node)
        int irq;
 
        timer_base = of_io_request_and_map(node, 0, of_node_full_name(node));
-       if (!timer_base)
+       if (IS_ERR(timer_base))
                panic("Can't map registers");
 
        irq = irq_of_parse_and_map(node, 0);
index 4f3dbc8cf7292773d9fcc442edfc8ef695a4a432..611cb09239ebe1214837c1e5048e479e5c50d71e 100644 (file)
@@ -5,7 +5,7 @@
 # big LITTLE core layer and glue drivers
 config ARM_BIG_LITTLE_CPUFREQ
        tristate "Generic ARM big LITTLE CPUfreq driver"
-       depends on ARM && BIG_LITTLE && ARM_CPU_TOPOLOGY && HAVE_CLK
+       depends on (ARM_CPU_TOPOLOGY || ARM64) && HAVE_CLK
        select PM_OPP
        help
          This enables the Generic CPUfreq driver for ARM big.LITTLE platforms.
index e1a6ba66a7f5568fc2fbb5541e5c8ddb460f9ead..f1e42f8ce0fcc75a5e67ae4e5365d098d83cfc32 100644 (file)
@@ -31,7 +31,6 @@
 #include <linux/slab.h>
 #include <linux/topology.h>
 #include <linux/types.h>
-#include <asm/bL_switcher.h>
 
 #include "arm_big_little.h"
 
 #define MAX_CLUSTERS   2
 
 #ifdef CONFIG_BL_SWITCHER
+#include <asm/bL_switcher.h>
 static bool bL_switching_enabled;
 #define is_bL_switching_enabled()      bL_switching_enabled
 #define set_switching_enabled(x)       (bL_switching_enabled = (x))
 #else
 #define is_bL_switching_enabled()      false
 #define set_switching_enabled(x)       do { } while (0)
+#define bL_switch_request(...)         do { } while (0)
+#define bL_switcher_put_enabled()      do { } while (0)
+#define bL_switcher_get_enabled()      do { } while (0)
 #endif
 
 #define ACTUAL_FREQ(cluster, freq)  ((cluster == A7_CLUSTER) ? freq << 1 : freq)
@@ -186,6 +189,15 @@ bL_cpufreq_set_rate(u32 cpu, u32 old_cluster, u32 new_cluster, u32 rate)
                mutex_unlock(&cluster_lock[old_cluster]);
        }
 
+       /*
+        * FIXME: clk_set_rate has to handle the case where clk_change_rate
+        * can fail due to hardware or firmware issues. Until the clk core
+        * layer is fixed, we can check here. In most of the cases we will
+        * be reading only the cached value anyway. This needs to  be removed
+        * once clk core is fixed.
+        */
+       if (bL_cpufreq_get_rate(cpu) != new_rate)
+               return -EIO;
        return 0;
 }
 
@@ -322,7 +334,6 @@ static void put_cluster_clk_and_freq_table(struct device *cpu_dev)
 static int _get_cluster_clk_and_freq_table(struct device *cpu_dev)
 {
        u32 cluster = raw_cpu_to_cluster(cpu_dev->id);
-       char name[14] = "cpu-cluster.";
        int ret;
 
        if (freq_table[cluster])
@@ -342,8 +353,7 @@ static int _get_cluster_clk_and_freq_table(struct device *cpu_dev)
                goto free_opp_table;
        }
 
-       name[12] = cluster + '0';
-       clk[cluster] = clk_get(cpu_dev, name);
+       clk[cluster] = clk_get(cpu_dev, NULL);
        if (!IS_ERR(clk[cluster])) {
                dev_dbg(cpu_dev, "%s: clk: %p & freq table: %p, cluster: %d\n",
                                __func__, clk[cluster], freq_table[cluster],
@@ -506,6 +516,7 @@ static struct cpufreq_driver bL_cpufreq_driver = {
        .attr                   = cpufreq_generic_attr,
 };
 
+#ifdef CONFIG_BL_SWITCHER
 static int bL_cpufreq_switcher_notifier(struct notifier_block *nfb,
                                        unsigned long action, void *_arg)
 {
@@ -538,6 +549,20 @@ static struct notifier_block bL_switcher_notifier = {
        .notifier_call = bL_cpufreq_switcher_notifier,
 };
 
+static int __bLs_register_notifier(void)
+{
+       return bL_switcher_register_notifier(&bL_switcher_notifier);
+}
+
+static int __bLs_unregister_notifier(void)
+{
+       return bL_switcher_unregister_notifier(&bL_switcher_notifier);
+}
+#else
+static int __bLs_register_notifier(void) { return 0; }
+static int __bLs_unregister_notifier(void) { return 0; }
+#endif
+
 int bL_cpufreq_register(struct cpufreq_arm_bL_ops *ops)
 {
        int ret, i;
@@ -555,8 +580,7 @@ int bL_cpufreq_register(struct cpufreq_arm_bL_ops *ops)
 
        arm_bL_ops = ops;
 
-       ret = bL_switcher_get_enabled();
-       set_switching_enabled(ret);
+       set_switching_enabled(bL_switcher_get_enabled());
 
        for (i = 0; i < MAX_CLUSTERS; i++)
                mutex_init(&cluster_lock[i]);
@@ -567,7 +591,7 @@ int bL_cpufreq_register(struct cpufreq_arm_bL_ops *ops)
                                __func__, ops->name, ret);
                arm_bL_ops = NULL;
        } else {
-               ret = bL_switcher_register_notifier(&bL_switcher_notifier);
+               ret = __bLs_register_notifier();
                if (ret) {
                        cpufreq_unregister_driver(&bL_cpufreq_driver);
                        arm_bL_ops = NULL;
@@ -591,7 +615,7 @@ void bL_cpufreq_unregister(struct cpufreq_arm_bL_ops *ops)
        }
 
        bL_switcher_get_enabled();
-       bL_switcher_unregister_notifier(&bL_switcher_notifier);
+       __bLs_unregister_notifier();
        cpufreq_unregister_driver(&bL_cpufreq_driver);
        bL_switcher_put_enabled();
        pr_info("%s: Un-registered platform driver: %s\n", __func__,
index bab67db54b7eb4fbed633ef0d65eece0637edcaa..528a82bf50386c52ad220b48c5c06b6db6083129 100644 (file)
@@ -416,6 +416,7 @@ static struct platform_driver dt_cpufreq_platdrv = {
 };
 module_platform_driver(dt_cpufreq_platdrv);
 
+MODULE_ALIAS("platform:cpufreq-dt");
 MODULE_AUTHOR("Viresh Kumar <viresh.kumar@linaro.org>");
 MODULE_AUTHOR("Shawn Guo <shawn.guo@linaro.org>");
 MODULE_DESCRIPTION("Generic cpufreq driver");
index a2258090b58b9fa8ad5711516da71f5c5e491f02..db69eeb501a7d49514d7ddd255c65d2e96e4bbeb 100644 (file)
@@ -414,7 +414,7 @@ static int nforce2_detect_chipset(void)
  * nforce2_init - initializes the nForce2 CPUFreq driver
  *
  * Initializes the nForce2 FSB support. Returns -ENODEV on unsupported
- * devices, -EINVAL on problems during initiatization, and zero on
+ * devices, -EINVAL on problems during initialization, and zero on
  * success.
  */
 static int __init nforce2_init(void)
index 8ae655c364f48aeeeec7e4717aab6e6bfe2d9f95..b612411655f99309929ae760887c05a8608d01d6 100644 (file)
 #include <linux/tick.h>
 #include <trace/events/power.h>
 
-/* Macros to iterate over lists */
-/* Iterate over online CPUs policies */
 static LIST_HEAD(cpufreq_policy_list);
-#define for_each_policy(__policy)                              \
+
+static inline bool policy_is_inactive(struct cpufreq_policy *policy)
+{
+       return cpumask_empty(policy->cpus);
+}
+
+static bool suitable_policy(struct cpufreq_policy *policy, bool active)
+{
+       return active == !policy_is_inactive(policy);
+}
+
+/* Finds Next Acive/Inactive policy */
+static struct cpufreq_policy *next_policy(struct cpufreq_policy *policy,
+                                         bool active)
+{
+       do {
+               policy = list_next_entry(policy, policy_list);
+
+               /* No more policies in the list */
+               if (&policy->policy_list == &cpufreq_policy_list)
+                       return NULL;
+       } while (!suitable_policy(policy, active));
+
+       return policy;
+}
+
+static struct cpufreq_policy *first_policy(bool active)
+{
+       struct cpufreq_policy *policy;
+
+       /* No policies in the list */
+       if (list_empty(&cpufreq_policy_list))
+               return NULL;
+
+       policy = list_first_entry(&cpufreq_policy_list, typeof(*policy),
+                                 policy_list);
+
+       if (!suitable_policy(policy, active))
+               policy = next_policy(policy, active);
+
+       return policy;
+}
+
+/* Macros to iterate over CPU policies */
+#define for_each_suitable_policy(__policy, __active)   \
+       for (__policy = first_policy(__active);         \
+            __policy;                                  \
+            __policy = next_policy(__policy, __active))
+
+#define for_each_active_policy(__policy)               \
+       for_each_suitable_policy(__policy, true)
+#define for_each_inactive_policy(__policy)             \
+       for_each_suitable_policy(__policy, false)
+
+#define for_each_policy(__policy)                      \
        list_for_each_entry(__policy, &cpufreq_policy_list, policy_list)
 
 /* Iterate over governors */
@@ -49,13 +101,9 @@ static LIST_HEAD(cpufreq_governor_list);
  */
 static struct cpufreq_driver *cpufreq_driver;
 static DEFINE_PER_CPU(struct cpufreq_policy *, cpufreq_cpu_data);
-static DEFINE_PER_CPU(struct cpufreq_policy *, cpufreq_cpu_data_fallback);
 static DEFINE_RWLOCK(cpufreq_driver_lock);
 DEFINE_MUTEX(cpufreq_governor_lock);
 
-/* This one keeps track of the previously set governor of a removed CPU */
-static DEFINE_PER_CPU(char[CPUFREQ_NAME_LEN], cpufreq_cpu_governor);
-
 /* Flag to suspend/resume CPUFreq governors */
 static bool cpufreq_suspended;
 
@@ -178,7 +226,7 @@ int cpufreq_generic_init(struct cpufreq_policy *policy,
        policy->cpuinfo.transition_latency = transition_latency;
 
        /*
-        * The driver only supports the SMP configuartion where all processors
+        * The driver only supports the SMP configuration where all processors
         * share the clock and voltage and clock.
         */
        cpumask_setall(policy->cpus);
@@ -187,10 +235,18 @@ int cpufreq_generic_init(struct cpufreq_policy *policy,
 }
 EXPORT_SYMBOL_GPL(cpufreq_generic_init);
 
-unsigned int cpufreq_generic_get(unsigned int cpu)
+/* Only for cpufreq core internal use */
+struct cpufreq_policy *cpufreq_cpu_get_raw(unsigned int cpu)
 {
        struct cpufreq_policy *policy = per_cpu(cpufreq_cpu_data, cpu);
 
+       return policy && cpumask_test_cpu(cpu, policy->cpus) ? policy : NULL;
+}
+
+unsigned int cpufreq_generic_get(unsigned int cpu)
+{
+       struct cpufreq_policy *policy = cpufreq_cpu_get_raw(cpu);
+
        if (!policy || IS_ERR(policy->clk)) {
                pr_err("%s: No %s associated to cpu: %d\n",
                       __func__, policy ? "clk" : "policy", cpu);
@@ -201,18 +257,29 @@ unsigned int cpufreq_generic_get(unsigned int cpu)
 }
 EXPORT_SYMBOL_GPL(cpufreq_generic_get);
 
-/* Only for cpufreq core internal use */
-struct cpufreq_policy *cpufreq_cpu_get_raw(unsigned int cpu)
-{
-       return per_cpu(cpufreq_cpu_data, cpu);
-}
-
+/**
+ * cpufreq_cpu_get: returns policy for a cpu and marks it busy.
+ *
+ * @cpu: cpu to find policy for.
+ *
+ * This returns policy for 'cpu', returns NULL if it doesn't exist.
+ * It also increments the kobject reference count to mark it busy and so would
+ * require a corresponding call to cpufreq_cpu_put() to decrement it back.
+ * If corresponding call cpufreq_cpu_put() isn't made, the policy wouldn't be
+ * freed as that depends on the kobj count.
+ *
+ * It also takes a read-lock of 'cpufreq_rwsem' and doesn't put it back if a
+ * valid policy is found. This is done to make sure the driver doesn't get
+ * unregistered while the policy is being used.
+ *
+ * Return: A valid policy on success, otherwise NULL on failure.
+ */
 struct cpufreq_policy *cpufreq_cpu_get(unsigned int cpu)
 {
        struct cpufreq_policy *policy = NULL;
        unsigned long flags;
 
-       if (cpu >= nr_cpu_ids)
+       if (WARN_ON(cpu >= nr_cpu_ids))
                return NULL;
 
        if (!down_read_trylock(&cpufreq_rwsem))
@@ -223,7 +290,7 @@ struct cpufreq_policy *cpufreq_cpu_get(unsigned int cpu)
 
        if (cpufreq_driver) {
                /* get the CPU */
-               policy = per_cpu(cpufreq_cpu_data, cpu);
+               policy = cpufreq_cpu_get_raw(cpu);
                if (policy)
                        kobject_get(&policy->kobj);
        }
@@ -237,6 +304,16 @@ struct cpufreq_policy *cpufreq_cpu_get(unsigned int cpu)
 }
 EXPORT_SYMBOL_GPL(cpufreq_cpu_get);
 
+/**
+ * cpufreq_cpu_put: Decrements the usage count of a policy
+ *
+ * @policy: policy earlier returned by cpufreq_cpu_get().
+ *
+ * This decrements the kobject reference count incremented earlier by calling
+ * cpufreq_cpu_get().
+ *
+ * It also drops the read-lock of 'cpufreq_rwsem' taken at cpufreq_cpu_get().
+ */
 void cpufreq_cpu_put(struct cpufreq_policy *policy)
 {
        kobject_put(&policy->kobj);
@@ -798,11 +875,18 @@ static ssize_t store(struct kobject *kobj, struct attribute *attr,
 
        down_write(&policy->rwsem);
 
+       /* Updating inactive policies is invalid, so avoid doing that. */
+       if (unlikely(policy_is_inactive(policy))) {
+               ret = -EBUSY;
+               goto unlock_policy_rwsem;
+       }
+
        if (fattr->store)
                ret = fattr->store(policy, buf, count);
        else
                ret = -EIO;
 
+unlock_policy_rwsem:
        up_write(&policy->rwsem);
 
        up_read(&cpufreq_rwsem);
@@ -873,28 +957,67 @@ void cpufreq_sysfs_remove_file(const struct attribute *attr)
 }
 EXPORT_SYMBOL(cpufreq_sysfs_remove_file);
 
-/* symlink affected CPUs */
+static int add_cpu_dev_symlink(struct cpufreq_policy *policy, int cpu)
+{
+       struct device *cpu_dev;
+
+       pr_debug("%s: Adding symlink for CPU: %u\n", __func__, cpu);
+
+       if (!policy)
+               return 0;
+
+       cpu_dev = get_cpu_device(cpu);
+       if (WARN_ON(!cpu_dev))
+               return 0;
+
+       return sysfs_create_link(&cpu_dev->kobj, &policy->kobj, "cpufreq");
+}
+
+static void remove_cpu_dev_symlink(struct cpufreq_policy *policy, int cpu)
+{
+       struct device *cpu_dev;
+
+       pr_debug("%s: Removing symlink for CPU: %u\n", __func__, cpu);
+
+       cpu_dev = get_cpu_device(cpu);
+       if (WARN_ON(!cpu_dev))
+               return;
+
+       sysfs_remove_link(&cpu_dev->kobj, "cpufreq");
+}
+
+/* Add/remove symlinks for all related CPUs */
 static int cpufreq_add_dev_symlink(struct cpufreq_policy *policy)
 {
        unsigned int j;
        int ret = 0;
 
-       for_each_cpu(j, policy->cpus) {
-               struct device *cpu_dev;
-
-               if (j == policy->cpu)
+       /* Some related CPUs might not be present (physically hotplugged) */
+       for_each_cpu_and(j, policy->related_cpus, cpu_present_mask) {
+               if (j == policy->kobj_cpu)
                        continue;
 
-               pr_debug("Adding link for CPU: %u\n", j);
-               cpu_dev = get_cpu_device(j);
-               ret = sysfs_create_link(&cpu_dev->kobj, &policy->kobj,
-                                       "cpufreq");
+               ret = add_cpu_dev_symlink(policy, j);
                if (ret)
                        break;
        }
+
        return ret;
 }
 
+static void cpufreq_remove_dev_symlink(struct cpufreq_policy *policy)
+{
+       unsigned int j;
+
+       /* Some related CPUs might not be present (physically hotplugged) */
+       for_each_cpu_and(j, policy->related_cpus, cpu_present_mask) {
+               if (j == policy->kobj_cpu)
+                       continue;
+
+               remove_cpu_dev_symlink(policy, j);
+       }
+}
+
 static int cpufreq_add_dev_interface(struct cpufreq_policy *policy,
                                     struct device *dev)
 {
@@ -937,7 +1060,7 @@ static void cpufreq_init_policy(struct cpufreq_policy *policy)
        memcpy(&new_policy, policy, sizeof(*policy));
 
        /* Update governor of new_policy to the governor used before hotplug */
-       gov = find_governor(per_cpu(cpufreq_cpu_governor, policy->cpu));
+       gov = find_governor(policy->last_governor);
        if (gov)
                pr_debug("Restoring governor %s for cpu %d\n",
                                policy->governor->name, policy->cpu);
@@ -963,7 +1086,10 @@ static int cpufreq_add_policy_cpu(struct cpufreq_policy *policy,
                                  unsigned int cpu, struct device *dev)
 {
        int ret = 0;
-       unsigned long flags;
+
+       /* Has this CPU been taken care of already? */
+       if (cpumask_test_cpu(cpu, policy->cpus))
+               return 0;
 
        if (has_target()) {
                ret = __cpufreq_governor(policy, CPUFREQ_GOV_STOP);
@@ -974,13 +1100,7 @@ static int cpufreq_add_policy_cpu(struct cpufreq_policy *policy,
        }
 
        down_write(&policy->rwsem);
-
-       write_lock_irqsave(&cpufreq_driver_lock, flags);
-
        cpumask_set_cpu(cpu, policy->cpus);
-       per_cpu(cpufreq_cpu_data, cpu) = policy;
-       write_unlock_irqrestore(&cpufreq_driver_lock, flags);
-
        up_write(&policy->rwsem);
 
        if (has_target()) {
@@ -994,7 +1114,7 @@ static int cpufreq_add_policy_cpu(struct cpufreq_policy *policy,
                }
        }
 
-       return sysfs_create_link(&dev->kobj, &policy->kobj, "cpufreq");
+       return 0;
 }
 
 static struct cpufreq_policy *cpufreq_policy_restore(unsigned int cpu)
@@ -1003,20 +1123,25 @@ static struct cpufreq_policy *cpufreq_policy_restore(unsigned int cpu)
        unsigned long flags;
 
        read_lock_irqsave(&cpufreq_driver_lock, flags);
-
-       policy = per_cpu(cpufreq_cpu_data_fallback, cpu);
-
+       policy = per_cpu(cpufreq_cpu_data, cpu);
        read_unlock_irqrestore(&cpufreq_driver_lock, flags);
 
-       if (policy)
-               policy->governor = NULL;
+       if (likely(policy)) {
+               /* Policy should be inactive here */
+               WARN_ON(!policy_is_inactive(policy));
+
+               down_write(&policy->rwsem);
+               policy->cpu = cpu;
+               up_write(&policy->rwsem);
+       }
 
        return policy;
 }
 
-static struct cpufreq_policy *cpufreq_policy_alloc(void)
+static struct cpufreq_policy *cpufreq_policy_alloc(struct device *dev)
 {
        struct cpufreq_policy *policy;
+       int ret;
 
        policy = kzalloc(sizeof(*policy), GFP_KERNEL);
        if (!policy)
@@ -1028,6 +1153,13 @@ static struct cpufreq_policy *cpufreq_policy_alloc(void)
        if (!zalloc_cpumask_var(&policy->related_cpus, GFP_KERNEL))
                goto err_free_cpumask;
 
+       ret = kobject_init_and_add(&policy->kobj, &ktype_cpufreq, &dev->kobj,
+                                  "cpufreq");
+       if (ret) {
+               pr_err("%s: failed to init policy->kobj: %d\n", __func__, ret);
+               goto err_free_rcpumask;
+       }
+
        INIT_LIST_HEAD(&policy->policy_list);
        init_rwsem(&policy->rwsem);
        spin_lock_init(&policy->transition_lock);
@@ -1035,8 +1167,15 @@ static struct cpufreq_policy *cpufreq_policy_alloc(void)
        init_completion(&policy->kobj_unregister);
        INIT_WORK(&policy->update, handle_update);
 
+       policy->cpu = dev->id;
+
+       /* Set this once on allocation */
+       policy->kobj_cpu = dev->id;
+
        return policy;
 
+err_free_rcpumask:
+       free_cpumask_var(policy->related_cpus);
 err_free_cpumask:
        free_cpumask_var(policy->cpus);
 err_free_policy:
@@ -1045,18 +1184,20 @@ err_free_policy:
        return NULL;
 }
 
-static void cpufreq_policy_put_kobj(struct cpufreq_policy *policy)
+static void cpufreq_policy_put_kobj(struct cpufreq_policy *policy, bool notify)
 {
        struct kobject *kobj;
        struct completion *cmp;
 
-       blocking_notifier_call_chain(&cpufreq_policy_notifier_list,
-                       CPUFREQ_REMOVE_POLICY, policy);
+       if (notify)
+               blocking_notifier_call_chain(&cpufreq_policy_notifier_list,
+                                            CPUFREQ_REMOVE_POLICY, policy);
 
-       down_read(&policy->rwsem);
+       down_write(&policy->rwsem);
+       cpufreq_remove_dev_symlink(policy);
        kobj = &policy->kobj;
        cmp = &policy->kobj_unregister;
-       up_read(&policy->rwsem);
+       up_write(&policy->rwsem);
        kobject_put(kobj);
 
        /*
@@ -1069,68 +1210,64 @@ static void cpufreq_policy_put_kobj(struct cpufreq_policy *policy)
        pr_debug("wait complete\n");
 }
 
-static void cpufreq_policy_free(struct cpufreq_policy *policy)
-{
-       free_cpumask_var(policy->related_cpus);
-       free_cpumask_var(policy->cpus);
-       kfree(policy);
-}
-
-static int update_policy_cpu(struct cpufreq_policy *policy, unsigned int cpu,
-                            struct device *cpu_dev)
+static void cpufreq_policy_free(struct cpufreq_policy *policy, bool notify)
 {
-       int ret;
-
-       if (WARN_ON(cpu == policy->cpu))
-               return 0;
+       unsigned long flags;
+       int cpu;
 
-       /* Move kobject to the new policy->cpu */
-       ret = kobject_move(&policy->kobj, &cpu_dev->kobj);
-       if (ret) {
-               pr_err("%s: Failed to move kobj: %d\n", __func__, ret);
-               return ret;
-       }
+       /* Remove policy from list */
+       write_lock_irqsave(&cpufreq_driver_lock, flags);
+       list_del(&policy->policy_list);
 
-       down_write(&policy->rwsem);
-       policy->cpu = cpu;
-       up_write(&policy->rwsem);
+       for_each_cpu(cpu, policy->related_cpus)
+               per_cpu(cpufreq_cpu_data, cpu) = NULL;
+       write_unlock_irqrestore(&cpufreq_driver_lock, flags);
 
-       return 0;
+       cpufreq_policy_put_kobj(policy, notify);
+       free_cpumask_var(policy->related_cpus);
+       free_cpumask_var(policy->cpus);
+       kfree(policy);
 }
 
-static int __cpufreq_add_dev(struct device *dev, struct subsys_interface *sif)
+/**
+ * cpufreq_add_dev - add a CPU device
+ *
+ * Adds the cpufreq interface for a CPU device.
+ *
+ * The Oracle says: try running cpufreq registration/unregistration concurrently
+ * with with cpu hotplugging and all hell will break loose. Tried to clean this
+ * mess up, but more thorough testing is needed. - Mathieu
+ */
+static int cpufreq_add_dev(struct device *dev, struct subsys_interface *sif)
 {
        unsigned int j, cpu = dev->id;
        int ret = -ENOMEM;
        struct cpufreq_policy *policy;
        unsigned long flags;
-       bool recover_policy = cpufreq_suspended;
-
-       if (cpu_is_offline(cpu))
-               return 0;
+       bool recover_policy = !sif;
 
        pr_debug("adding CPU %u\n", cpu);
 
-       /* check whether a different CPU already registered this
-        * CPU because it is in the same boat. */
-       policy = cpufreq_cpu_get_raw(cpu);
-       if (unlikely(policy))
-               return 0;
+       /*
+        * Only possible if 'cpu' wasn't physically present earlier and we are
+        * here from subsys_interface add callback. A hotplug notifier will
+        * follow and we will handle it like logical CPU hotplug then. For now,
+        * just create the sysfs link.
+        */
+       if (cpu_is_offline(cpu))
+               return add_cpu_dev_symlink(per_cpu(cpufreq_cpu_data, cpu), cpu);
 
        if (!down_read_trylock(&cpufreq_rwsem))
                return 0;
 
-       /* Check if this cpu was hot-unplugged earlier and has siblings */
-       read_lock_irqsave(&cpufreq_driver_lock, flags);
-       for_each_policy(policy) {
-               if (cpumask_test_cpu(cpu, policy->related_cpus)) {
-                       read_unlock_irqrestore(&cpufreq_driver_lock, flags);
-                       ret = cpufreq_add_policy_cpu(policy, cpu, dev);
-                       up_read(&cpufreq_rwsem);
-                       return ret;
-               }
+       /* Check if this CPU already has a policy to manage it */
+       policy = per_cpu(cpufreq_cpu_data, cpu);
+       if (policy && !policy_is_inactive(policy)) {
+               WARN_ON(!cpumask_test_cpu(cpu, policy->related_cpus));
+               ret = cpufreq_add_policy_cpu(policy, cpu, dev);
+               up_read(&cpufreq_rwsem);
+               return ret;
        }
-       read_unlock_irqrestore(&cpufreq_driver_lock, flags);
 
        /*
         * Restore the saved policy when doing light-weight init and fall back
@@ -1139,22 +1276,11 @@ static int __cpufreq_add_dev(struct device *dev, struct subsys_interface *sif)
        policy = recover_policy ? cpufreq_policy_restore(cpu) : NULL;
        if (!policy) {
                recover_policy = false;
-               policy = cpufreq_policy_alloc();
+               policy = cpufreq_policy_alloc(dev);
                if (!policy)
                        goto nomem_out;
        }
 
-       /*
-        * In the resume path, since we restore a saved policy, the assignment
-        * to policy->cpu is like an update of the existing policy, rather than
-        * the creation of a brand new one. So we need to perform this update
-        * by invoking update_policy_cpu().
-        */
-       if (recover_policy && cpu != policy->cpu)
-               WARN_ON(update_policy_cpu(policy, cpu, dev));
-       else
-               policy->cpu = cpu;
-
        cpumask_copy(policy->cpus, cpumask_of(cpu));
 
        /* call driver. From then on the cpufreq must be able
@@ -1181,21 +1307,12 @@ static int __cpufreq_add_dev(struct device *dev, struct subsys_interface *sif)
                policy->user_policy.min = policy->min;
                policy->user_policy.max = policy->max;
 
-               /* prepare interface data */
-               ret = kobject_init_and_add(&policy->kobj, &ktype_cpufreq,
-                                          &dev->kobj, "cpufreq");
-               if (ret) {
-                       pr_err("%s: failed to init policy->kobj: %d\n",
-                              __func__, ret);
-                       goto err_init_policy_kobj;
-               }
+               write_lock_irqsave(&cpufreq_driver_lock, flags);
+               for_each_cpu(j, policy->related_cpus)
+                       per_cpu(cpufreq_cpu_data, j) = policy;
+               write_unlock_irqrestore(&cpufreq_driver_lock, flags);
        }
 
-       write_lock_irqsave(&cpufreq_driver_lock, flags);
-       for_each_cpu(j, policy->cpus)
-               per_cpu(cpufreq_cpu_data, j) = policy;
-       write_unlock_irqrestore(&cpufreq_driver_lock, flags);
-
        if (cpufreq_driver->get && !cpufreq_driver->setpolicy) {
                policy->cur = cpufreq_driver->get(policy->cpu);
                if (!policy->cur) {
@@ -1253,11 +1370,11 @@ static int __cpufreq_add_dev(struct device *dev, struct subsys_interface *sif)
                        goto err_out_unregister;
                blocking_notifier_call_chain(&cpufreq_policy_notifier_list,
                                CPUFREQ_CREATE_POLICY, policy);
-       }
 
-       write_lock_irqsave(&cpufreq_driver_lock, flags);
-       list_add(&policy->policy_list, &cpufreq_policy_list);
-       write_unlock_irqrestore(&cpufreq_driver_lock, flags);
+               write_lock_irqsave(&cpufreq_driver_lock, flags);
+               list_add(&policy->policy_list, &cpufreq_policy_list);
+               write_unlock_irqrestore(&cpufreq_driver_lock, flags);
+       }
 
        cpufreq_init_policy(policy);
 
@@ -1281,68 +1398,28 @@ static int __cpufreq_add_dev(struct device *dev, struct subsys_interface *sif)
 
 err_out_unregister:
 err_get_freq:
-       write_lock_irqsave(&cpufreq_driver_lock, flags);
-       for_each_cpu(j, policy->cpus)
-               per_cpu(cpufreq_cpu_data, j) = NULL;
-       write_unlock_irqrestore(&cpufreq_driver_lock, flags);
-
-       if (!recover_policy) {
-               kobject_put(&policy->kobj);
-               wait_for_completion(&policy->kobj_unregister);
-       }
-err_init_policy_kobj:
        up_write(&policy->rwsem);
 
        if (cpufreq_driver->exit)
                cpufreq_driver->exit(policy);
 err_set_policy_cpu:
-       if (recover_policy) {
-               /* Do not leave stale fallback data behind. */
-               per_cpu(cpufreq_cpu_data_fallback, cpu) = NULL;
-               cpufreq_policy_put_kobj(policy);
-       }
-       cpufreq_policy_free(policy);
-
+       cpufreq_policy_free(policy, recover_policy);
 nomem_out:
        up_read(&cpufreq_rwsem);
 
        return ret;
 }
 
-/**
- * cpufreq_add_dev - add a CPU device
- *
- * Adds the cpufreq interface for a CPU device.
- *
- * The Oracle says: try running cpufreq registration/unregistration concurrently
- * with with cpu hotplugging and all hell will break loose. Tried to clean this
- * mess up, but more thorough testing is needed. - Mathieu
- */
-static int cpufreq_add_dev(struct device *dev, struct subsys_interface *sif)
-{
-       return __cpufreq_add_dev(dev, sif);
-}
-
 static int __cpufreq_remove_dev_prepare(struct device *dev,
                                        struct subsys_interface *sif)
 {
-       unsigned int cpu = dev->id, cpus;
-       int ret;
-       unsigned long flags;
+       unsigned int cpu = dev->id;
+       int ret = 0;
        struct cpufreq_policy *policy;
 
        pr_debug("%s: unregistering CPU %u\n", __func__, cpu);
 
-       write_lock_irqsave(&cpufreq_driver_lock, flags);
-
-       policy = per_cpu(cpufreq_cpu_data, cpu);
-
-       /* Save the policy somewhere when doing a light-weight tear-down */
-       if (cpufreq_suspended)
-               per_cpu(cpufreq_cpu_data_fallback, cpu) = policy;
-
-       write_unlock_irqrestore(&cpufreq_driver_lock, flags);
-
+       policy = cpufreq_cpu_get_raw(cpu);
        if (!policy) {
                pr_debug("%s: No cpu_data found\n", __func__);
                return -EINVAL;
@@ -1354,108 +1431,75 @@ static int __cpufreq_remove_dev_prepare(struct device *dev,
                        pr_err("%s: Failed to stop governor\n", __func__);
                        return ret;
                }
-
-               strncpy(per_cpu(cpufreq_cpu_governor, cpu),
-                       policy->governor->name, CPUFREQ_NAME_LEN);
        }
 
-       down_read(&policy->rwsem);
-       cpus = cpumask_weight(policy->cpus);
-       up_read(&policy->rwsem);
+       down_write(&policy->rwsem);
+       cpumask_clear_cpu(cpu, policy->cpus);
 
-       if (cpu != policy->cpu) {
-               sysfs_remove_link(&dev->kobj, "cpufreq");
-       } else if (cpus > 1) {
+       if (policy_is_inactive(policy)) {
+               if (has_target())
+                       strncpy(policy->last_governor, policy->governor->name,
+                               CPUFREQ_NAME_LEN);
+       } else if (cpu == policy->cpu) {
                /* Nominate new CPU */
-               int new_cpu = cpumask_any_but(policy->cpus, cpu);
-               struct device *cpu_dev = get_cpu_device(new_cpu);
+               policy->cpu = cpumask_any(policy->cpus);
+       }
+       up_write(&policy->rwsem);
 
-               sysfs_remove_link(&cpu_dev->kobj, "cpufreq");
-               ret = update_policy_cpu(policy, new_cpu, cpu_dev);
-               if (ret) {
-                       if (sysfs_create_link(&cpu_dev->kobj, &policy->kobj,
-                                             "cpufreq"))
-                               pr_err("%s: Failed to restore kobj link to cpu:%d\n",
-                                      __func__, cpu_dev->id);
-                       return ret;
-               }
+       /* Start governor again for active policy */
+       if (!policy_is_inactive(policy)) {
+               if (has_target()) {
+                       ret = __cpufreq_governor(policy, CPUFREQ_GOV_START);
+                       if (!ret)
+                               ret = __cpufreq_governor(policy, CPUFREQ_GOV_LIMITS);
 
-               if (!cpufreq_suspended)
-                       pr_debug("%s: policy Kobject moved to cpu: %d from: %d\n",
-                                __func__, new_cpu, cpu);
+                       if (ret)
+                               pr_err("%s: Failed to start governor\n", __func__);
+               }
        } else if (cpufreq_driver->stop_cpu) {
                cpufreq_driver->stop_cpu(policy);
        }
 
-       return 0;
+       return ret;
 }
 
 static int __cpufreq_remove_dev_finish(struct device *dev,
                                       struct subsys_interface *sif)
 {
-       unsigned int cpu = dev->id, cpus;
+       unsigned int cpu = dev->id;
        int ret;
-       unsigned long flags;
-       struct cpufreq_policy *policy;
-
-       write_lock_irqsave(&cpufreq_driver_lock, flags);
-       policy = per_cpu(cpufreq_cpu_data, cpu);
-       per_cpu(cpufreq_cpu_data, cpu) = NULL;
-       write_unlock_irqrestore(&cpufreq_driver_lock, flags);
+       struct cpufreq_policy *policy = per_cpu(cpufreq_cpu_data, cpu);
 
        if (!policy) {
                pr_debug("%s: No cpu_data found\n", __func__);
                return -EINVAL;
        }
 
-       down_write(&policy->rwsem);
-       cpus = cpumask_weight(policy->cpus);
-
-       if (cpus > 1)
-               cpumask_clear_cpu(cpu, policy->cpus);
-       up_write(&policy->rwsem);
+       /* Only proceed for inactive policies */
+       if (!policy_is_inactive(policy))
+               return 0;
 
        /* If cpu is last user of policy, free policy */
-       if (cpus == 1) {
-               if (has_target()) {
-                       ret = __cpufreq_governor(policy,
-                                       CPUFREQ_GOV_POLICY_EXIT);
-                       if (ret) {
-                               pr_err("%s: Failed to exit governor\n",
-                                      __func__);
-                               return ret;
-                       }
-               }
-
-               if (!cpufreq_suspended)
-                       cpufreq_policy_put_kobj(policy);
-
-               /*
-                * Perform the ->exit() even during light-weight tear-down,
-                * since this is a core component, and is essential for the
-                * subsequent light-weight ->init() to succeed.
-                */
-               if (cpufreq_driver->exit)
-                       cpufreq_driver->exit(policy);
-
-               /* Remove policy from list of active policies */
-               write_lock_irqsave(&cpufreq_driver_lock, flags);
-               list_del(&policy->policy_list);
-               write_unlock_irqrestore(&cpufreq_driver_lock, flags);
-
-               if (!cpufreq_suspended)
-                       cpufreq_policy_free(policy);
-       } else if (has_target()) {
-               ret = __cpufreq_governor(policy, CPUFREQ_GOV_START);
-               if (!ret)
-                       ret = __cpufreq_governor(policy, CPUFREQ_GOV_LIMITS);
-
+       if (has_target()) {
+               ret = __cpufreq_governor(policy, CPUFREQ_GOV_POLICY_EXIT);
                if (ret) {
-                       pr_err("%s: Failed to start governor\n", __func__);
+                       pr_err("%s: Failed to exit governor\n", __func__);
                        return ret;
                }
        }
 
+       /*
+        * Perform the ->exit() even during light-weight tear-down,
+        * since this is a core component, and is essential for the
+        * subsequent light-weight ->init() to succeed.
+        */
+       if (cpufreq_driver->exit)
+               cpufreq_driver->exit(policy);
+
+       /* Free the policy only if the driver is getting removed. */
+       if (sif)
+               cpufreq_policy_free(policy, true);
+
        return 0;
 }
 
@@ -1469,8 +1513,33 @@ static int cpufreq_remove_dev(struct device *dev, struct subsys_interface *sif)
        unsigned int cpu = dev->id;
        int ret;
 
-       if (cpu_is_offline(cpu))
+       /*
+        * Only possible if 'cpu' is getting physically removed now. A hotplug
+        * notifier should have already been called and we just need to remove
+        * link or free policy here.
+        */
+       if (cpu_is_offline(cpu)) {
+               struct cpufreq_policy *policy = per_cpu(cpufreq_cpu_data, cpu);
+               struct cpumask mask;
+
+               if (!policy)
+                       return 0;
+
+               cpumask_copy(&mask, policy->related_cpus);
+               cpumask_clear_cpu(cpu, &mask);
+
+               /*
+                * Free policy only if all policy->related_cpus are removed
+                * physically.
+                */
+               if (cpumask_intersects(&mask, cpu_present_mask)) {
+                       remove_cpu_dev_symlink(policy, cpu);
+                       return 0;
+               }
+
+               cpufreq_policy_free(policy, true);
                return 0;
+       }
 
        ret = __cpufreq_remove_dev_prepare(dev, sif);
 
@@ -1567,6 +1636,10 @@ static unsigned int __cpufreq_get(struct cpufreq_policy *policy)
 
        ret_freq = cpufreq_driver->get(policy->cpu);
 
+       /* Updating inactive policies is invalid, so avoid doing that. */
+       if (unlikely(policy_is_inactive(policy)))
+               return ret_freq;
+
        if (ret_freq && policy->cur &&
                !(cpufreq_driver->flags & CPUFREQ_CONST_LOOPS)) {
                /* verify no discrepancy between actual and
@@ -1656,7 +1729,7 @@ void cpufreq_suspend(void)
 
        pr_debug("%s: Suspending Governors\n", __func__);
 
-       for_each_policy(policy) {
+       for_each_active_policy(policy) {
                if (__cpufreq_governor(policy, CPUFREQ_GOV_STOP))
                        pr_err("%s: Failed to stop governor for policy: %p\n",
                                __func__, policy);
@@ -1690,7 +1763,7 @@ void cpufreq_resume(void)
 
        pr_debug("%s: Resuming Governors\n", __func__);
 
-       for_each_policy(policy) {
+       for_each_active_policy(policy) {
                if (cpufreq_driver->resume && cpufreq_driver->resume(policy))
                        pr_err("%s: Failed to resume driver: %p\n", __func__,
                                policy);
@@ -1891,7 +1964,7 @@ static int __target_index(struct cpufreq_policy *policy,
                 * Failed after setting to intermediate freq? Driver should have
                 * reverted back to initial frequency and so should we. Check
                 * here for intermediate_freq instead of get_intermediate, in
-                * case we have't switched to intermediate freq at all.
+                * case we haven't switched to intermediate freq at all.
                 */
                if (unlikely(retval && intermediate_freq)) {
                        freqs.old = intermediate_freq;
@@ -2092,7 +2165,8 @@ EXPORT_SYMBOL_GPL(cpufreq_register_governor);
 
 void cpufreq_unregister_governor(struct cpufreq_governor *governor)
 {
-       int cpu;
+       struct cpufreq_policy *policy;
+       unsigned long flags;
 
        if (!governor)
                return;
@@ -2100,12 +2174,15 @@ void cpufreq_unregister_governor(struct cpufreq_governor *governor)
        if (cpufreq_disabled())
                return;
 
-       for_each_present_cpu(cpu) {
-               if (cpu_online(cpu))
-                       continue;
-               if (!strcmp(per_cpu(cpufreq_cpu_governor, cpu), governor->name))
-                       strcpy(per_cpu(cpufreq_cpu_governor, cpu), "\0");
+       /* clear last_governor for all inactive policies */
+       read_lock_irqsave(&cpufreq_driver_lock, flags);
+       for_each_inactive_policy(policy) {
+               if (!strcmp(policy->last_governor, governor->name)) {
+                       policy->governor = NULL;
+                       strcpy(policy->last_governor, "\0");
+               }
        }
+       read_unlock_irqrestore(&cpufreq_driver_lock, flags);
 
        mutex_lock(&cpufreq_governor_mutex);
        list_del(&governor->governor_list);
@@ -2304,7 +2381,7 @@ static int cpufreq_cpu_callback(struct notifier_block *nfb,
        if (dev) {
                switch (action & ~CPU_TASKS_FROZEN) {
                case CPU_ONLINE:
-                       __cpufreq_add_dev(dev, NULL);
+                       cpufreq_add_dev(dev, NULL);
                        break;
 
                case CPU_DOWN_PREPARE:
@@ -2316,7 +2393,7 @@ static int cpufreq_cpu_callback(struct notifier_block *nfb,
                        break;
 
                case CPU_DOWN_FAILED:
-                       __cpufreq_add_dev(dev, NULL);
+                       cpufreq_add_dev(dev, NULL);
                        break;
                }
        }
@@ -2336,7 +2413,7 @@ static int cpufreq_boost_set_sw(int state)
        struct cpufreq_policy *policy;
        int ret = -EINVAL;
 
-       for_each_policy(policy) {
+       for_each_active_policy(policy) {
                freq_table = cpufreq_frequency_get_table(policy->cpu);
                if (freq_table) {
                        ret = cpufreq_frequency_table_cpuinfo(policy,
index 25a70d06c5bf243efe85ff3ed2dfbd2a7cc590df..c86a10c309123c682a571b2ae93c6c2bda6f5394 100644 (file)
@@ -148,6 +148,10 @@ static int dbs_cpufreq_notifier(struct notifier_block *nb, unsigned long val,
        return 0;
 }
 
+static struct notifier_block cs_cpufreq_notifier_block = {
+       .notifier_call = dbs_cpufreq_notifier,
+};
+
 /************************** sysfs interface ************************/
 static struct common_dbs_data cs_dbs_cdata;
 
@@ -317,7 +321,7 @@ static struct attribute_group cs_attr_group_gov_pol = {
 
 /************************** sysfs end ************************/
 
-static int cs_init(struct dbs_data *dbs_data)
+static int cs_init(struct dbs_data *dbs_data, bool notify)
 {
        struct cs_dbs_tuners *tuners;
 
@@ -336,25 +340,25 @@ static int cs_init(struct dbs_data *dbs_data)
        dbs_data->tuners = tuners;
        dbs_data->min_sampling_rate = MIN_SAMPLING_RATE_RATIO *
                jiffies_to_usecs(10);
-       mutex_init(&dbs_data->mutex);
+
+       if (notify)
+               cpufreq_register_notifier(&cs_cpufreq_notifier_block,
+                                         CPUFREQ_TRANSITION_NOTIFIER);
+
        return 0;
 }
 
-static void cs_exit(struct dbs_data *dbs_data)
+static void cs_exit(struct dbs_data *dbs_data, bool notify)
 {
+       if (notify)
+               cpufreq_unregister_notifier(&cs_cpufreq_notifier_block,
+                                           CPUFREQ_TRANSITION_NOTIFIER);
+
        kfree(dbs_data->tuners);
 }
 
 define_get_cpu_dbs_routines(cs_cpu_dbs_info);
 
-static struct notifier_block cs_cpufreq_notifier_block = {
-       .notifier_call = dbs_cpufreq_notifier,
-};
-
-static struct cs_ops cs_ops = {
-       .notifier_block = &cs_cpufreq_notifier_block,
-};
-
 static struct common_dbs_data cs_dbs_cdata = {
        .governor = GOV_CONSERVATIVE,
        .attr_group_gov_sys = &cs_attr_group_gov_sys,
@@ -363,9 +367,9 @@ static struct common_dbs_data cs_dbs_cdata = {
        .get_cpu_dbs_info_s = get_cpu_dbs_info_s,
        .gov_dbs_timer = cs_dbs_timer,
        .gov_check_cpu = cs_check_cpu,
-       .gov_ops = &cs_ops,
        .init = cs_init,
        .exit = cs_exit,
+       .mutex = __MUTEX_INITIALIZER(cs_dbs_cdata.mutex),
 };
 
 static int cs_cpufreq_governor_dbs(struct cpufreq_policy *policy,
index 1b44496b2d2b3548bab6c1aff39a5c4f25c17428..57a39f8a92b7fd41689ac3083decf3a5db4943d9 100644 (file)
@@ -239,211 +239,242 @@ static void set_sampling_rate(struct dbs_data *dbs_data,
        }
 }
 
-int cpufreq_governor_dbs(struct cpufreq_policy *policy,
-               struct common_dbs_data *cdata, unsigned int event)
+static int cpufreq_governor_init(struct cpufreq_policy *policy,
+                                struct dbs_data *dbs_data,
+                                struct common_dbs_data *cdata)
 {
-       struct dbs_data *dbs_data;
-       struct od_cpu_dbs_info_s *od_dbs_info = NULL;
-       struct cs_cpu_dbs_info_s *cs_dbs_info = NULL;
-       struct od_ops *od_ops = NULL;
-       struct od_dbs_tuners *od_tuners = NULL;
-       struct cs_dbs_tuners *cs_tuners = NULL;
-       struct cpu_dbs_common_info *cpu_cdbs;
-       unsigned int sampling_rate, latency, ignore_nice, j, cpu = policy->cpu;
-       int io_busy = 0;
-       int rc;
-
-       if (have_governor_per_policy())
-               dbs_data = policy->governor_data;
-       else
-               dbs_data = cdata->gdbs_data;
-
-       WARN_ON(!dbs_data && (event != CPUFREQ_GOV_POLICY_INIT));
-
-       switch (event) {
-       case CPUFREQ_GOV_POLICY_INIT:
-               if (have_governor_per_policy()) {
-                       WARN_ON(dbs_data);
-               } else if (dbs_data) {
-                       dbs_data->usage_count++;
-                       policy->governor_data = dbs_data;
-                       return 0;
-               }
-
-               dbs_data = kzalloc(sizeof(*dbs_data), GFP_KERNEL);
-               if (!dbs_data) {
-                       pr_err("%s: POLICY_INIT: kzalloc failed\n", __func__);
-                       return -ENOMEM;
-               }
+       unsigned int latency;
+       int ret;
 
-               dbs_data->cdata = cdata;
-               dbs_data->usage_count = 1;
-               rc = cdata->init(dbs_data);
-               if (rc) {
-                       pr_err("%s: POLICY_INIT: init() failed\n", __func__);
-                       kfree(dbs_data);
-                       return rc;
-               }
+       if (dbs_data) {
+               if (WARN_ON(have_governor_per_policy()))
+                       return -EINVAL;
+               dbs_data->usage_count++;
+               policy->governor_data = dbs_data;
+               return 0;
+       }
 
-               if (!have_governor_per_policy())
-                       WARN_ON(cpufreq_get_global_kobject());
+       dbs_data = kzalloc(sizeof(*dbs_data), GFP_KERNEL);
+       if (!dbs_data)
+               return -ENOMEM;
 
-               rc = sysfs_create_group(get_governor_parent_kobj(policy),
-                               get_sysfs_attr(dbs_data));
-               if (rc) {
-                       cdata->exit(dbs_data);
-                       kfree(dbs_data);
-                       return rc;
-               }
+       dbs_data->cdata = cdata;
+       dbs_data->usage_count = 1;
 
-               policy->governor_data = dbs_data;
+       ret = cdata->init(dbs_data, !policy->governor->initialized);
+       if (ret)
+               goto free_dbs_data;
 
-               /* policy latency is in ns. Convert it to us first */
-               latency = policy->cpuinfo.transition_latency / 1000;
-               if (latency == 0)
-                       latency = 1;
+       /* policy latency is in ns. Convert it to us first */
+       latency = policy->cpuinfo.transition_latency / 1000;
+       if (latency == 0)
+               latency = 1;
 
-               /* Bring kernel and HW constraints together */
-               dbs_data->min_sampling_rate = max(dbs_data->min_sampling_rate,
-                               MIN_LATENCY_MULTIPLIER * latency);
-               set_sampling_rate(dbs_data, max(dbs_data->min_sampling_rate,
+       /* Bring kernel and HW constraints together */
+       dbs_data->min_sampling_rate = max(dbs_data->min_sampling_rate,
+                                         MIN_LATENCY_MULTIPLIER * latency);
+       set_sampling_rate(dbs_data, max(dbs_data->min_sampling_rate,
                                        latency * LATENCY_MULTIPLIER));
 
-               if ((cdata->governor == GOV_CONSERVATIVE) &&
-                               (!policy->governor->initialized)) {
-                       struct cs_ops *cs_ops = dbs_data->cdata->gov_ops;
-
-                       cpufreq_register_notifier(cs_ops->notifier_block,
-                                       CPUFREQ_TRANSITION_NOTIFIER);
+       if (!have_governor_per_policy()) {
+               if (WARN_ON(cpufreq_get_global_kobject())) {
+                       ret = -EINVAL;
+                       goto cdata_exit;
                }
+               cdata->gdbs_data = dbs_data;
+       }
 
-               if (!have_governor_per_policy())
-                       cdata->gdbs_data = dbs_data;
+       ret = sysfs_create_group(get_governor_parent_kobj(policy),
+                                get_sysfs_attr(dbs_data));
+       if (ret)
+               goto put_kobj;
 
-               return 0;
-       case CPUFREQ_GOV_POLICY_EXIT:
-               if (!--dbs_data->usage_count) {
-                       sysfs_remove_group(get_governor_parent_kobj(policy),
-                                       get_sysfs_attr(dbs_data));
+       policy->governor_data = dbs_data;
 
-                       if (!have_governor_per_policy())
-                               cpufreq_put_global_kobject();
+       return 0;
 
-                       if ((dbs_data->cdata->governor == GOV_CONSERVATIVE) &&
-                               (policy->governor->initialized == 1)) {
-                               struct cs_ops *cs_ops = dbs_data->cdata->gov_ops;
+put_kobj:
+       if (!have_governor_per_policy()) {
+               cdata->gdbs_data = NULL;
+               cpufreq_put_global_kobject();
+       }
+cdata_exit:
+       cdata->exit(dbs_data, !policy->governor->initialized);
+free_dbs_data:
+       kfree(dbs_data);
+       return ret;
+}
+
+static void cpufreq_governor_exit(struct cpufreq_policy *policy,
+                                 struct dbs_data *dbs_data)
+{
+       struct common_dbs_data *cdata = dbs_data->cdata;
 
-                               cpufreq_unregister_notifier(cs_ops->notifier_block,
-                                               CPUFREQ_TRANSITION_NOTIFIER);
-                       }
+       policy->governor_data = NULL;
+       if (!--dbs_data->usage_count) {
+               sysfs_remove_group(get_governor_parent_kobj(policy),
+                                  get_sysfs_attr(dbs_data));
 
-                       cdata->exit(dbs_data);
-                       kfree(dbs_data);
+               if (!have_governor_per_policy()) {
                        cdata->gdbs_data = NULL;
+                       cpufreq_put_global_kobject();
                }
 
-               policy->governor_data = NULL;
-               return 0;
+               cdata->exit(dbs_data, policy->governor->initialized == 1);
+               kfree(dbs_data);
        }
+}
 
-       cpu_cdbs = dbs_data->cdata->get_cpu_cdbs(cpu);
+static int cpufreq_governor_start(struct cpufreq_policy *policy,
+                                 struct dbs_data *dbs_data)
+{
+       struct common_dbs_data *cdata = dbs_data->cdata;
+       unsigned int sampling_rate, ignore_nice, j, cpu = policy->cpu;
+       struct cpu_dbs_common_info *cpu_cdbs = cdata->get_cpu_cdbs(cpu);
+       int io_busy = 0;
+
+       if (!policy->cur)
+               return -EINVAL;
+
+       if (cdata->governor == GOV_CONSERVATIVE) {
+               struct cs_dbs_tuners *cs_tuners = dbs_data->tuners;
 
-       if (dbs_data->cdata->governor == GOV_CONSERVATIVE) {
-               cs_tuners = dbs_data->tuners;
-               cs_dbs_info = dbs_data->cdata->get_cpu_dbs_info_s(cpu);
                sampling_rate = cs_tuners->sampling_rate;
                ignore_nice = cs_tuners->ignore_nice_load;
        } else {
-               od_tuners = dbs_data->tuners;
-               od_dbs_info = dbs_data->cdata->get_cpu_dbs_info_s(cpu);
+               struct od_dbs_tuners *od_tuners = dbs_data->tuners;
+
                sampling_rate = od_tuners->sampling_rate;
                ignore_nice = od_tuners->ignore_nice_load;
-               od_ops = dbs_data->cdata->gov_ops;
                io_busy = od_tuners->io_is_busy;
        }
 
-       switch (event) {
-       case CPUFREQ_GOV_START:
-               if (!policy->cur)
-                       return -EINVAL;
+       for_each_cpu(j, policy->cpus) {
+               struct cpu_dbs_common_info *j_cdbs = cdata->get_cpu_cdbs(j);
+               unsigned int prev_load;
 
-               mutex_lock(&dbs_data->mutex);
+               j_cdbs->cpu = j;
+               j_cdbs->cur_policy = policy;
+               j_cdbs->prev_cpu_idle =
+                       get_cpu_idle_time(j, &j_cdbs->prev_cpu_wall, io_busy);
 
-               for_each_cpu(j, policy->cpus) {
-                       struct cpu_dbs_common_info *j_cdbs =
-                               dbs_data->cdata->get_cpu_cdbs(j);
-                       unsigned int prev_load;
+               prev_load = (unsigned int)(j_cdbs->prev_cpu_wall -
+                                           j_cdbs->prev_cpu_idle);
+               j_cdbs->prev_load = 100 * prev_load /
+                                   (unsigned int)j_cdbs->prev_cpu_wall;
 
-                       j_cdbs->cpu = j;
-                       j_cdbs->cur_policy = policy;
-                       j_cdbs->prev_cpu_idle = get_cpu_idle_time(j,
-                                              &j_cdbs->prev_cpu_wall, io_busy);
+               if (ignore_nice)
+                       j_cdbs->prev_cpu_nice = kcpustat_cpu(j).cpustat[CPUTIME_NICE];
 
-                       prev_load = (unsigned int)
-                               (j_cdbs->prev_cpu_wall - j_cdbs->prev_cpu_idle);
-                       j_cdbs->prev_load = 100 * prev_load /
-                                       (unsigned int) j_cdbs->prev_cpu_wall;
+               mutex_init(&j_cdbs->timer_mutex);
+               INIT_DEFERRABLE_WORK(&j_cdbs->work, cdata->gov_dbs_timer);
+       }
 
-                       if (ignore_nice)
-                               j_cdbs->prev_cpu_nice =
-                                       kcpustat_cpu(j).cpustat[CPUTIME_NICE];
+       if (cdata->governor == GOV_CONSERVATIVE) {
+               struct cs_cpu_dbs_info_s *cs_dbs_info =
+                       cdata->get_cpu_dbs_info_s(cpu);
 
-                       mutex_init(&j_cdbs->timer_mutex);
-                       INIT_DEFERRABLE_WORK(&j_cdbs->work,
-                                            dbs_data->cdata->gov_dbs_timer);
-               }
+               cs_dbs_info->down_skip = 0;
+               cs_dbs_info->enable = 1;
+               cs_dbs_info->requested_freq = policy->cur;
+       } else {
+               struct od_ops *od_ops = cdata->gov_ops;
+               struct od_cpu_dbs_info_s *od_dbs_info = cdata->get_cpu_dbs_info_s(cpu);
 
-               if (dbs_data->cdata->governor == GOV_CONSERVATIVE) {
-                       cs_dbs_info->down_skip = 0;
-                       cs_dbs_info->enable = 1;
-                       cs_dbs_info->requested_freq = policy->cur;
-               } else {
-                       od_dbs_info->rate_mult = 1;
-                       od_dbs_info->sample_type = OD_NORMAL_SAMPLE;
-                       od_ops->powersave_bias_init_cpu(cpu);
-               }
+               od_dbs_info->rate_mult = 1;
+               od_dbs_info->sample_type = OD_NORMAL_SAMPLE;
+               od_ops->powersave_bias_init_cpu(cpu);
+       }
 
-               mutex_unlock(&dbs_data->mutex);
+       /* Initiate timer time stamp */
+       cpu_cdbs->time_stamp = ktime_get();
 
-               /* Initiate timer time stamp */
-               cpu_cdbs->time_stamp = ktime_get();
+       gov_queue_work(dbs_data, policy, delay_for_sampling_rate(sampling_rate),
+                      true);
+       return 0;
+}
 
-               gov_queue_work(dbs_data, policy,
-                               delay_for_sampling_rate(sampling_rate), true);
-               break;
+static void cpufreq_governor_stop(struct cpufreq_policy *policy,
+                                 struct dbs_data *dbs_data)
+{
+       struct common_dbs_data *cdata = dbs_data->cdata;
+       unsigned int cpu = policy->cpu;
+       struct cpu_dbs_common_info *cpu_cdbs = cdata->get_cpu_cdbs(cpu);
 
-       case CPUFREQ_GOV_STOP:
-               if (dbs_data->cdata->governor == GOV_CONSERVATIVE)
-                       cs_dbs_info->enable = 0;
+       if (cdata->governor == GOV_CONSERVATIVE) {
+               struct cs_cpu_dbs_info_s *cs_dbs_info =
+                       cdata->get_cpu_dbs_info_s(cpu);
 
-               gov_cancel_work(dbs_data, policy);
+               cs_dbs_info->enable = 0;
+       }
 
-               mutex_lock(&dbs_data->mutex);
-               mutex_destroy(&cpu_cdbs->timer_mutex);
-               cpu_cdbs->cur_policy = NULL;
+       gov_cancel_work(dbs_data, policy);
 
-               mutex_unlock(&dbs_data->mutex);
+       mutex_destroy(&cpu_cdbs->timer_mutex);
+       cpu_cdbs->cur_policy = NULL;
+}
 
-               break;
+static void cpufreq_governor_limits(struct cpufreq_policy *policy,
+                                   struct dbs_data *dbs_data)
+{
+       struct common_dbs_data *cdata = dbs_data->cdata;
+       unsigned int cpu = policy->cpu;
+       struct cpu_dbs_common_info *cpu_cdbs = cdata->get_cpu_cdbs(cpu);
+
+       if (!cpu_cdbs->cur_policy)
+               return;
+
+       mutex_lock(&cpu_cdbs->timer_mutex);
+       if (policy->max < cpu_cdbs->cur_policy->cur)
+               __cpufreq_driver_target(cpu_cdbs->cur_policy, policy->max,
+                                       CPUFREQ_RELATION_H);
+       else if (policy->min > cpu_cdbs->cur_policy->cur)
+               __cpufreq_driver_target(cpu_cdbs->cur_policy, policy->min,
+                                       CPUFREQ_RELATION_L);
+       dbs_check_cpu(dbs_data, cpu);
+       mutex_unlock(&cpu_cdbs->timer_mutex);
+}
+
+int cpufreq_governor_dbs(struct cpufreq_policy *policy,
+                        struct common_dbs_data *cdata, unsigned int event)
+{
+       struct dbs_data *dbs_data;
+       int ret = 0;
 
+       /* Lock governor to block concurrent initialization of governor */
+       mutex_lock(&cdata->mutex);
+
+       if (have_governor_per_policy())
+               dbs_data = policy->governor_data;
+       else
+               dbs_data = cdata->gdbs_data;
+
+       if (WARN_ON(!dbs_data && (event != CPUFREQ_GOV_POLICY_INIT))) {
+               ret = -EINVAL;
+               goto unlock;
+       }
+
+       switch (event) {
+       case CPUFREQ_GOV_POLICY_INIT:
+               ret = cpufreq_governor_init(policy, dbs_data, cdata);
+               break;
+       case CPUFREQ_GOV_POLICY_EXIT:
+               cpufreq_governor_exit(policy, dbs_data);
+               break;
+       case CPUFREQ_GOV_START:
+               ret = cpufreq_governor_start(policy, dbs_data);
+               break;
+       case CPUFREQ_GOV_STOP:
+               cpufreq_governor_stop(policy, dbs_data);
+               break;
        case CPUFREQ_GOV_LIMITS:
-               mutex_lock(&dbs_data->mutex);
-               if (!cpu_cdbs->cur_policy) {
-                       mutex_unlock(&dbs_data->mutex);
-                       break;
-               }
-               mutex_lock(&cpu_cdbs->timer_mutex);
-               if (policy->max < cpu_cdbs->cur_policy->cur)
-                       __cpufreq_driver_target(cpu_cdbs->cur_policy,
-                                       policy->max, CPUFREQ_RELATION_H);
-               else if (policy->min > cpu_cdbs->cur_policy->cur)
-                       __cpufreq_driver_target(cpu_cdbs->cur_policy,
-                                       policy->min, CPUFREQ_RELATION_L);
-               dbs_check_cpu(dbs_data, cpu);
-               mutex_unlock(&cpu_cdbs->timer_mutex);
-               mutex_unlock(&dbs_data->mutex);
+               cpufreq_governor_limits(policy, dbs_data);
                break;
        }
-       return 0;
+
+unlock:
+       mutex_unlock(&cdata->mutex);
+
+       return ret;
 }
 EXPORT_SYMBOL_GPL(cpufreq_governor_dbs);
index cc401d147e727615c8255a951a08065ae954fb23..34736f5e869d139339366a625c07c0f30a858e9b 100644 (file)
@@ -208,11 +208,16 @@ struct common_dbs_data {
        void *(*get_cpu_dbs_info_s)(int cpu);
        void (*gov_dbs_timer)(struct work_struct *work);
        void (*gov_check_cpu)(int cpu, unsigned int load);
-       int (*init)(struct dbs_data *dbs_data);
-       void (*exit)(struct dbs_data *dbs_data);
+       int (*init)(struct dbs_data *dbs_data, bool notify);
+       void (*exit)(struct dbs_data *dbs_data, bool notify);
 
        /* Governor specific ops, see below */
        void *gov_ops;
+
+       /*
+        * Protects governor's data (struct dbs_data and struct common_dbs_data)
+        */
+       struct mutex mutex;
 };
 
 /* Governor Per policy data */
@@ -221,9 +226,6 @@ struct dbs_data {
        unsigned int min_sampling_rate;
        int usage_count;
        void *tuners;
-
-       /* dbs_mutex protects dbs_enable in governor start/stop */
-       struct mutex mutex;
 };
 
 /* Governor specific ops, will be passed to dbs_data->gov_ops */
@@ -234,10 +236,6 @@ struct od_ops {
        void (*freq_increase)(struct cpufreq_policy *policy, unsigned int freq);
 };
 
-struct cs_ops {
-       struct notifier_block *notifier_block;
-};
-
 static inline int delay_for_sampling_rate(unsigned int sampling_rate)
 {
        int delay = usecs_to_jiffies(sampling_rate);
index ad3f38fd3eb9feefa12212362e1312884974d745..3c1e10f2304cd27476a3c8cef6a449f11eaa8855 100644 (file)
@@ -475,7 +475,7 @@ static struct attribute_group od_attr_group_gov_pol = {
 
 /************************** sysfs end ************************/
 
-static int od_init(struct dbs_data *dbs_data)
+static int od_init(struct dbs_data *dbs_data, bool notify)
 {
        struct od_dbs_tuners *tuners;
        u64 idle_time;
@@ -513,11 +513,10 @@ static int od_init(struct dbs_data *dbs_data)
        tuners->io_is_busy = should_io_be_busy();
 
        dbs_data->tuners = tuners;
-       mutex_init(&dbs_data->mutex);
        return 0;
 }
 
-static void od_exit(struct dbs_data *dbs_data)
+static void od_exit(struct dbs_data *dbs_data, bool notify)
 {
        kfree(dbs_data->tuners);
 }
@@ -541,6 +540,7 @@ static struct common_dbs_data od_dbs_cdata = {
        .gov_ops = &od_ops,
        .init = od_init,
        .exit = od_exit,
+       .mutex = __MUTEX_INITIALIZER(od_dbs_cdata.mutex),
 };
 
 static void od_set_powersave_bias(unsigned int powersave_bias)
index 1d723dc8880c58605a1e0b407a9e5e38bbb61308..3488c9c175eb2ed90164d1b2d20fcf2c089262b7 100644 (file)
@@ -144,7 +144,7 @@ module_param(max_duration, int, 0444);
 
 
 /**
- * we can detect a core multipiler from dir0_lsb
+ * we can detect a core multiplier from dir0_lsb
  * from GX1 datasheet p.56,
  *     MULT[3:0]:
  *     0000 = SYSCLK multiplied by 4 (test only)
@@ -346,7 +346,7 @@ static int cpufreq_gx_verify(struct cpufreq_policy *policy)
 
        /* it needs to be assured that at least one supported frequency is
         * within policy->min and policy->max. If it is not, policy->max
-        * needs to be increased until one freuqency is supported.
+        * needs to be increased until one frequency is supported.
         * policy->min may not be decreased, though. This way we guarantee a
         * specific processing capacity.
         */
index 6414661ac1c46a2ccdbaa2408e2491ac93e4dc46..15ada47bb720b710454795d8d7e83c235c2fccfc 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/fs.h>
 #include <linux/debugfs.h>
 #include <linux/acpi.h>
+#include <linux/vmalloc.h>
 #include <trace/events/power.h>
 
 #include <asm/div64.h>
@@ -48,9 +49,9 @@ static inline int32_t mul_fp(int32_t x, int32_t y)
        return ((int64_t)x * (int64_t)y) >> FRAC_BITS;
 }
 
-static inline int32_t div_fp(int32_t x, int32_t y)
+static inline int32_t div_fp(s64 x, s64 y)
 {
-       return div_s64((int64_t)x << FRAC_BITS, y);
+       return div64_s64((int64_t)x << FRAC_BITS, y);
 }
 
 static inline int ceiling_fp(int32_t x)
@@ -68,6 +69,7 @@ struct sample {
        int32_t core_pct_busy;
        u64 aperf;
        u64 mperf;
+       u64 tsc;
        int freq;
        ktime_t time;
 };
@@ -109,6 +111,7 @@ struct cpudata {
        ktime_t last_sample_time;
        u64     prev_aperf;
        u64     prev_mperf;
+       u64     prev_tsc;
        struct sample sample;
 };
 
@@ -396,7 +399,7 @@ static ssize_t store_no_turbo(struct kobject *a, struct attribute *b,
 
        update_turbo_state();
        if (limits.turbo_disabled) {
-               pr_warn("Turbo disabled by BIOS or unavailable on processor\n");
+               pr_warn("intel_pstate: Turbo disabled by BIOS or unavailable on processor\n");
                return -EPERM;
        }
 
@@ -484,7 +487,7 @@ static void __init intel_pstate_sysfs_expose_params(void)
 static void intel_pstate_hwp_enable(void)
 {
        hwp_active++;
-       pr_info("intel_pstate HWP enabled\n");
+       pr_info("intel_pstate: HWP enabled\n");
 
        wrmsrl( MSR_PM_ENABLE, 0x1);
 }
@@ -535,7 +538,7 @@ static void byt_set_pstate(struct cpudata *cpudata, int pstate)
 
        val |= vid;
 
-       wrmsrl(MSR_IA32_PERF_CTL, val);
+       wrmsrl_on_cpu(cpudata->cpu, MSR_IA32_PERF_CTL, val);
 }
 
 #define BYT_BCLK_FREQS 5
@@ -704,19 +707,20 @@ static void intel_pstate_get_min_max(struct cpudata *cpu, int *min, int *max)
        *min = clamp_t(int, min_perf, cpu->pstate.min_pstate, max_perf);
 }
 
-static void intel_pstate_set_pstate(struct cpudata *cpu, int pstate)
+static void intel_pstate_set_pstate(struct cpudata *cpu, int pstate, bool force)
 {
        int max_perf, min_perf;
 
-       update_turbo_state();
-
-       intel_pstate_get_min_max(cpu, &min_perf, &max_perf);
+       if (force) {
+               update_turbo_state();
 
-       pstate = clamp_t(int, pstate, min_perf, max_perf);
+               intel_pstate_get_min_max(cpu, &min_perf, &max_perf);
 
-       if (pstate == cpu->pstate.current_pstate)
-               return;
+               pstate = clamp_t(int, pstate, min_perf, max_perf);
 
+               if (pstate == cpu->pstate.current_pstate)
+                       return;
+       }
        trace_cpu_frequency(pstate * cpu->pstate.scaling, cpu->cpu);
 
        cpu->pstate.current_pstate = pstate;
@@ -733,7 +737,7 @@ static void intel_pstate_get_cpu_pstates(struct cpudata *cpu)
 
        if (pstate_funcs.get_vid)
                pstate_funcs.get_vid(cpu);
-       intel_pstate_set_pstate(cpu, cpu->pstate.min_pstate);
+       intel_pstate_set_pstate(cpu, cpu->pstate.min_pstate, false);
 }
 
 static inline void intel_pstate_calc_busy(struct cpudata *cpu)
@@ -756,23 +760,28 @@ static inline void intel_pstate_sample(struct cpudata *cpu)
 {
        u64 aperf, mperf;
        unsigned long flags;
+       u64 tsc;
 
        local_irq_save(flags);
        rdmsrl(MSR_IA32_APERF, aperf);
        rdmsrl(MSR_IA32_MPERF, mperf);
+       tsc = native_read_tsc();
        local_irq_restore(flags);
 
        cpu->last_sample_time = cpu->sample.time;
        cpu->sample.time = ktime_get();
        cpu->sample.aperf = aperf;
        cpu->sample.mperf = mperf;
+       cpu->sample.tsc =  tsc;
        cpu->sample.aperf -= cpu->prev_aperf;
        cpu->sample.mperf -= cpu->prev_mperf;
+       cpu->sample.tsc -= cpu->prev_tsc;
 
        intel_pstate_calc_busy(cpu);
 
        cpu->prev_aperf = aperf;
        cpu->prev_mperf = mperf;
+       cpu->prev_tsc = tsc;
 }
 
 static inline void intel_hwp_set_sample_time(struct cpudata *cpu)
@@ -794,7 +803,7 @@ static inline void intel_pstate_set_sample_time(struct cpudata *cpu)
 static inline int32_t intel_pstate_get_scaled_busy(struct cpudata *cpu)
 {
        int32_t core_busy, max_pstate, current_pstate, sample_ratio;
-       u32 duration_us;
+       s64 duration_us;
        u32 sample_time;
 
        /*
@@ -821,8 +830,8 @@ static inline int32_t intel_pstate_get_scaled_busy(struct cpudata *cpu)
         * to adjust our busyness.
         */
        sample_time = pid_params.sample_rate_ms  * USEC_PER_MSEC;
-       duration_us = (u32) ktime_us_delta(cpu->sample.time,
-                                          cpu->last_sample_time);
+       duration_us = ktime_us_delta(cpu->sample.time,
+                                    cpu->last_sample_time);
        if (duration_us > sample_time * 3) {
                sample_ratio = div_fp(int_tofp(sample_time),
                                      int_tofp(duration_us));
@@ -837,6 +846,10 @@ static inline void intel_pstate_adjust_busy_pstate(struct cpudata *cpu)
        int32_t busy_scaled;
        struct _pid *pid;
        signed int ctl;
+       int from;
+       struct sample *sample;
+
+       from = cpu->pstate.current_pstate;
 
        pid = &cpu->pid;
        busy_scaled = intel_pstate_get_scaled_busy(cpu);
@@ -844,7 +857,17 @@ static inline void intel_pstate_adjust_busy_pstate(struct cpudata *cpu)
        ctl = pid_calc(pid, busy_scaled);
 
        /* Negative values of ctl increase the pstate and vice versa */
-       intel_pstate_set_pstate(cpu, cpu->pstate.current_pstate - ctl);
+       intel_pstate_set_pstate(cpu, cpu->pstate.current_pstate - ctl, true);
+
+       sample = &cpu->sample;
+       trace_pstate_sample(fp_toint(sample->core_pct_busy),
+               fp_toint(busy_scaled),
+               from,
+               cpu->pstate.current_pstate,
+               sample->mperf,
+               sample->aperf,
+               sample->tsc,
+               sample->freq);
 }
 
 static void intel_hwp_timer_func(unsigned long __data)
@@ -858,21 +881,11 @@ static void intel_hwp_timer_func(unsigned long __data)
 static void intel_pstate_timer_func(unsigned long __data)
 {
        struct cpudata *cpu = (struct cpudata *) __data;
-       struct sample *sample;
 
        intel_pstate_sample(cpu);
 
-       sample = &cpu->sample;
-
        intel_pstate_adjust_busy_pstate(cpu);
 
-       trace_pstate_sample(fp_toint(sample->core_pct_busy),
-                       fp_toint(intel_pstate_get_scaled_busy(cpu)),
-                       cpu->pstate.current_pstate,
-                       sample->mperf,
-                       sample->aperf,
-                       sample->freq);
-
        intel_pstate_set_sample_time(cpu);
 }
 
@@ -935,7 +948,7 @@ static int intel_pstate_init_cpu(unsigned int cpunum)
 
        add_timer_on(&cpu->timer, cpunum);
 
-       pr_debug("Intel pstate controlling: cpu %d\n", cpunum);
+       pr_debug("intel_pstate: controlling: cpu %d\n", cpunum);
 
        return 0;
 }
@@ -1001,13 +1014,13 @@ static void intel_pstate_stop_cpu(struct cpufreq_policy *policy)
        int cpu_num = policy->cpu;
        struct cpudata *cpu = all_cpu_data[cpu_num];
 
-       pr_info("intel_pstate CPU %d exiting\n", cpu_num);
+       pr_debug("intel_pstate: CPU %d exiting\n", cpu_num);
 
        del_timer_sync(&all_cpu_data[cpu_num]->timer);
        if (hwp_active)
                return;
 
-       intel_pstate_set_pstate(cpu, cpu->pstate.min_pstate);
+       intel_pstate_set_pstate(cpu, cpu->pstate.min_pstate, false);
 }
 
 static int intel_pstate_cpu_init(struct cpufreq_policy *policy)
index e24269ab4e9bd91208b9971f2378b4a69a92648d..1d99c97defa9204f52ad8605fd5f25c1d2ad0540 100644 (file)
@@ -56,7 +56,7 @@ module_param(pxa27x_maxfreq, uint, 0);
 MODULE_PARM_DESC(pxa27x_maxfreq, "Set the pxa27x maxfreq in MHz"
                 "(typically 624=>pxa270, 416=>pxa271, 520=>pxa272)");
 
-typedef struct {
+struct pxa_freqs {
        unsigned int khz;
        unsigned int membus;
        unsigned int cccr;
@@ -64,7 +64,7 @@ typedef struct {
        unsigned int cclkcfg;
        int vmin;
        int vmax;
-} pxa_freqs_t;
+};
 
 /* Define the refresh period in mSec for the SDRAM and the number of rows */
 #define SDRAM_TREF     64      /* standard 64ms SDRAM */
@@ -86,7 +86,7 @@ static unsigned int sdram_rows;
 /* Use the run mode frequencies for the CPUFREQ_POLICY_PERFORMANCE policy */
 #define CCLKCFG                        CCLKCFG_TURBO | CCLKCFG_FCS
 
-static pxa_freqs_t pxa255_run_freqs[] =
+static const struct pxa_freqs pxa255_run_freqs[] =
 {
        /* CPU   MEMBUS  CCCR  DIV2 CCLKCFG                run  turbo PXbus SDRAM */
        { 99500,  99500, 0x121, 1,  CCLKCFG, -1, -1},   /*  99,   99,   50,   50  */
@@ -98,7 +98,7 @@ static pxa_freqs_t pxa255_run_freqs[] =
 };
 
 /* Use the turbo mode frequencies for the CPUFREQ_POLICY_POWERSAVE policy */
-static pxa_freqs_t pxa255_turbo_freqs[] =
+static const struct pxa_freqs pxa255_turbo_freqs[] =
 {
        /* CPU   MEMBUS  CCCR  DIV2 CCLKCFG        run  turbo PXbus SDRAM */
        { 99500, 99500,  0x121, 1,  CCLKCFG, -1, -1},   /*  99,   99,   50,   50  */
@@ -153,7 +153,7 @@ MODULE_PARM_DESC(pxa255_turbo_table, "Selects the frequency table (0 = run table
    ((HT) ? CCLKCFG_HALFTURBO : 0) | \
    ((T)  ? CCLKCFG_TURBO : 0))
 
-static pxa_freqs_t pxa27x_freqs[] = {
+static struct pxa_freqs pxa27x_freqs[] = {
        {104000, 104000, PXA27x_CCCR(1,  8, 2), 0, CCLKCFG2(1, 0, 1),  900000, 1705000 },
        {156000, 104000, PXA27x_CCCR(1,  8, 3), 0, CCLKCFG2(1, 0, 1), 1000000, 1705000 },
        {208000, 208000, PXA27x_CCCR(0, 16, 2), 1, CCLKCFG2(0, 0, 1), 1180000, 1705000 },
@@ -171,7 +171,7 @@ extern unsigned get_clk_frequency_khz(int info);
 
 #ifdef CONFIG_REGULATOR
 
-static int pxa_cpufreq_change_voltage(pxa_freqs_t *pxa_freq)
+static int pxa_cpufreq_change_voltage(const struct pxa_freqs *pxa_freq)
 {
        int ret = 0;
        int vmin, vmax;
@@ -202,7 +202,7 @@ static void __init pxa_cpufreq_init_voltages(void)
        }
 }
 #else
-static int pxa_cpufreq_change_voltage(pxa_freqs_t *pxa_freq)
+static int pxa_cpufreq_change_voltage(struct pxa_freqs *pxa_freq)
 {
        return 0;
 }
@@ -211,7 +211,7 @@ static void __init pxa_cpufreq_init_voltages(void) { }
 #endif
 
 static void find_freq_tables(struct cpufreq_frequency_table **freq_table,
-                            pxa_freqs_t **pxa_freqs)
+                            const struct pxa_freqs **pxa_freqs)
 {
        if (cpu_is_pxa25x()) {
                if (!pxa255_turbo_table) {
@@ -270,7 +270,7 @@ static unsigned int pxa_cpufreq_get(unsigned int cpu)
 static int pxa_set_target(struct cpufreq_policy *policy, unsigned int idx)
 {
        struct cpufreq_frequency_table *pxa_freqs_table;
-       pxa_freqs_t *pxa_freq_settings;
+       const struct pxa_freqs *pxa_freq_settings;
        unsigned long flags;
        unsigned int new_freq_cpu, new_freq_mem;
        unsigned int unused, preset_mdrefr, postset_mdrefr, cclkcfg;
@@ -361,7 +361,7 @@ static int pxa_cpufreq_init(struct cpufreq_policy *policy)
        int i;
        unsigned int freq;
        struct cpufreq_frequency_table *pxa255_freq_table;
-       pxa_freqs_t *pxa255_freqs;
+       const struct pxa_freqs *pxa255_freqs;
 
        /* try to guess pxa27x cpu */
        if (cpu_is_pxa27x())
index 88b21ae0d6b078bdfc9d36507f95bf5fe7e79338..358f0752c31e26956adc41b0a93867921069de8f 100644 (file)
 
 /**
  * struct cpu_data
- * @parent: the parent node of cpu clock
+ * @pclk: the parent clock of cpu
  * @table: frequency table
  */
 struct cpu_data {
-       struct device_node *parent;
+       struct clk **pclk;
        struct cpufreq_frequency_table *table;
 };
 
@@ -196,7 +196,7 @@ static void freq_table_sort(struct cpufreq_frequency_table *freq_table,
 
 static int qoriq_cpufreq_cpu_init(struct cpufreq_policy *policy)
 {
-       struct device_node *np;
+       struct device_node *np, *pnode;
        int i, count, ret;
        u32 freq, mask;
        struct clk *clk;
@@ -219,17 +219,23 @@ static int qoriq_cpufreq_cpu_init(struct cpufreq_policy *policy)
                goto err_nomem2;
        }
 
-       data->parent = of_parse_phandle(np, "clocks", 0);
-       if (!data->parent) {
+       pnode = of_parse_phandle(np, "clocks", 0);
+       if (!pnode) {
                pr_err("%s: could not get clock information\n", __func__);
                goto err_nomem2;
        }
 
-       count = of_property_count_strings(data->parent, "clock-names");
+       count = of_property_count_strings(pnode, "clock-names");
+       data->pclk = kcalloc(count, sizeof(struct clk *), GFP_KERNEL);
+       if (!data->pclk) {
+               pr_err("%s: no memory\n", __func__);
+               goto err_node;
+       }
+
        table = kcalloc(count + 1, sizeof(*table), GFP_KERNEL);
        if (!table) {
                pr_err("%s: no memory\n", __func__);
-               goto err_node;
+               goto err_pclk;
        }
 
        if (fmask)
@@ -238,7 +244,8 @@ static int qoriq_cpufreq_cpu_init(struct cpufreq_policy *policy)
                mask = 0x0;
 
        for (i = 0; i < count; i++) {
-               clk = of_clk_get(data->parent, i);
+               clk = of_clk_get(pnode, i);
+               data->pclk[i] = clk;
                freq = clk_get_rate(clk);
                /*
                 * the clock is valid if its frequency is not masked
@@ -273,13 +280,16 @@ static int qoriq_cpufreq_cpu_init(struct cpufreq_policy *policy)
        policy->cpuinfo.transition_latency = u64temp + 1;
 
        of_node_put(np);
+       of_node_put(pnode);
 
        return 0;
 
 err_nomem1:
        kfree(table);
+err_pclk:
+       kfree(data->pclk);
 err_node:
-       of_node_put(data->parent);
+       of_node_put(pnode);
 err_nomem2:
        policy->driver_data = NULL;
        kfree(data);
@@ -293,7 +303,7 @@ static int __exit qoriq_cpufreq_cpu_exit(struct cpufreq_policy *policy)
 {
        struct cpu_data *data = policy->driver_data;
 
-       of_node_put(data->parent);
+       kfree(data->pclk);
        kfree(data->table);
        kfree(data);
        policy->driver_data = NULL;
@@ -307,7 +317,7 @@ static int qoriq_cpufreq_target(struct cpufreq_policy *policy,
        struct clk *parent;
        struct cpu_data *data = policy->driver_data;
 
-       parent = of_clk_get(data->parent, data->table[index].driver_data);
+       parent = data->pclk[data->table[index].driver_data];
        return clk_set_parent(policy->clk, parent);
 }
 
index 59372077ec7c1a1b7d64e5ac67880b5e625526b5..1e3ef5ec4784dcfc3b758a0d67cac25c0d33e4ba 100644 (file)
@@ -29,18 +29,25 @@ struct cpuidle_driver powernv_idle_driver = {
 
 static int max_idle_state;
 static struct cpuidle_state *cpuidle_state_table;
+static u64 snooze_timeout;
+static bool snooze_timeout_en;
 
 static int snooze_loop(struct cpuidle_device *dev,
                        struct cpuidle_driver *drv,
                        int index)
 {
+       u64 snooze_exit_time;
+
        local_irq_enable();
        set_thread_flag(TIF_POLLING_NRFLAG);
 
+       snooze_exit_time = get_tb() + snooze_timeout;
        ppc64_runlatch_off();
        while (!need_resched()) {
                HMT_low();
                HMT_very_low();
+               if (snooze_timeout_en && get_tb() > snooze_exit_time)
+                       break;
        }
 
        HMT_medium();
@@ -252,6 +259,11 @@ static int powernv_idle_probe(void)
                cpuidle_state_table = powernv_states;
                /* Device tree can indicate more idle states */
                max_idle_state = powernv_add_idle_states();
+               if (max_idle_state > 1) {
+                       snooze_timeout_en = true;
+                       snooze_timeout = powernv_states[1].target_residency *
+                                        tb_ticks_per_usec;
+               }
        } else
                return -ENODEV;
 
index bb9e2b6f3ecc33b5e9fde738d6c1405a4a2d642e..07135e009d8b9ce7590c3b442d610c790a1ff206 100644 (file)
@@ -27,6 +27,8 @@ struct cpuidle_driver pseries_idle_driver = {
 
 static int max_idle_state;
 static struct cpuidle_state *cpuidle_state_table;
+static u64 snooze_timeout;
+static bool snooze_timeout_en;
 
 static inline void idle_loop_prolog(unsigned long *in_purr)
 {
@@ -58,14 +60,18 @@ static int snooze_loop(struct cpuidle_device *dev,
                        int index)
 {
        unsigned long in_purr;
+       u64 snooze_exit_time;
 
        idle_loop_prolog(&in_purr);
        local_irq_enable();
        set_thread_flag(TIF_POLLING_NRFLAG);
+       snooze_exit_time = get_tb() + snooze_timeout;
 
        while (!need_resched()) {
                HMT_low();
                HMT_very_low();
+               if (snooze_timeout_en && get_tb() > snooze_exit_time)
+                       break;
        }
 
        HMT_medium();
@@ -244,6 +250,11 @@ static int pseries_idle_probe(void)
        } else
                return -ENODEV;
 
+       if (max_idle_state > 1) {
+               snooze_timeout_en = true;
+               snooze_timeout = cpuidle_state_table[1].target_residency *
+                                tb_ticks_per_usec;
+       }
        return 0;
 }
 
index 61c417b9e53f8795175b29d71dfd201f15be151c..e8e2775c3821e26dc179ea6e300025d0cf6e75e5 100644 (file)
@@ -65,7 +65,7 @@ int cpuidle_play_dead(void)
                return -ENODEV;
 
        /* Find lowest-power state that supports long-term idle */
-       for (i = drv->state_count - 1; i >= CPUIDLE_DRIVER_STATE_START; i--)
+       for (i = drv->state_count - 1; i >= 0; i--)
                if (drv->states[i].enter_dead)
                        return drv->states[i].enter_dead(dev, i);
 
@@ -73,16 +73,21 @@ int cpuidle_play_dead(void)
 }
 
 static int find_deepest_state(struct cpuidle_driver *drv,
-                             struct cpuidle_device *dev, bool freeze)
+                             struct cpuidle_device *dev,
+                             unsigned int max_latency,
+                             unsigned int forbidden_flags,
+                             bool freeze)
 {
        unsigned int latency_req = 0;
-       int i, ret = freeze ? -1 : CPUIDLE_DRIVER_STATE_START - 1;
+       int i, ret = -ENXIO;
 
-       for (i = CPUIDLE_DRIVER_STATE_START; i < drv->state_count; i++) {
+       for (i = 0; i < drv->state_count; i++) {
                struct cpuidle_state *s = &drv->states[i];
                struct cpuidle_state_usage *su = &dev->states_usage[i];
 
                if (s->disabled || su->disable || s->exit_latency <= latency_req
+                   || s->exit_latency > max_latency
+                   || (s->flags & forbidden_flags)
                    || (freeze && !s->enter_freeze))
                        continue;
 
@@ -92,6 +97,7 @@ static int find_deepest_state(struct cpuidle_driver *drv,
        return ret;
 }
 
+#ifdef CONFIG_SUSPEND
 /**
  * cpuidle_find_deepest_state - Find the deepest available idle state.
  * @drv: cpuidle driver for the given CPU.
@@ -100,7 +106,7 @@ static int find_deepest_state(struct cpuidle_driver *drv,
 int cpuidle_find_deepest_state(struct cpuidle_driver *drv,
                               struct cpuidle_device *dev)
 {
-       return find_deepest_state(drv, dev, false);
+       return find_deepest_state(drv, dev, UINT_MAX, 0, false);
 }
 
 static void enter_freeze_proper(struct cpuidle_driver *drv,
@@ -139,18 +145,19 @@ int cpuidle_enter_freeze(struct cpuidle_driver *drv, struct cpuidle_device *dev)
         * that interrupts won't be enabled when it exits and allows the tick to
         * be frozen safely.
         */
-       index = find_deepest_state(drv, dev, true);
+       index = find_deepest_state(drv, dev, UINT_MAX, 0, true);
        if (index >= 0)
                enter_freeze_proper(drv, dev, index);
 
        return index;
 }
+#endif /* CONFIG_SUSPEND */
 
 /**
  * cpuidle_enter_state - enter the state and update stats
  * @dev: cpuidle device for this cpu
  * @drv: cpuidle driver for this cpu
- * @next_state: index into drv->states of the state to enter
+ * @index: index into the states table in @drv of the state to enter
  */
 int cpuidle_enter_state(struct cpuidle_device *dev, struct cpuidle_driver *drv,
                        int index)
@@ -167,8 +174,18 @@ int cpuidle_enter_state(struct cpuidle_device *dev, struct cpuidle_driver *drv,
         * local timer will be shut down.  If a local timer is used from another
         * CPU as a broadcast timer, this call may fail if it is not available.
         */
-       if (broadcast && tick_broadcast_enter())
-               return -EBUSY;
+       if (broadcast && tick_broadcast_enter()) {
+               index = find_deepest_state(drv, dev, target_state->exit_latency,
+                                          CPUIDLE_FLAG_TIMER_STOP, false);
+               if (index < 0) {
+                       default_idle_call();
+                       return -EBUSY;
+               }
+               target_state = &drv->states[index];
+       }
+
+       /* Take note of the planned idle state. */
+       sched_idle_set_state(target_state);
 
        trace_cpu_idle_rcuidle(index, dev->cpu);
        time_start = ktime_get();
@@ -178,6 +195,9 @@ int cpuidle_enter_state(struct cpuidle_device *dev, struct cpuidle_driver *drv,
        time_end = ktime_get();
        trace_cpu_idle_rcuidle(PWR_EVENT_EXIT, dev->cpu);
 
+       /* The cpu is no longer idle or about to enter idle. */
+       sched_idle_set_state(NULL);
+
        if (broadcast) {
                if (WARN_ON_ONCE(!irqs_disabled()))
                        local_irq_disable();
@@ -249,7 +269,7 @@ int cpuidle_enter(struct cpuidle_driver *drv, struct cpuidle_device *dev,
  */
 void cpuidle_reflect(struct cpuidle_device *dev, int index)
 {
-       if (cpuidle_curr_governor->reflect)
+       if (cpuidle_curr_governor->reflect && index >= 0)
                cpuidle_curr_governor->reflect(dev, index);
 }
 
index b8a5fa15ca24af1d20928ff4fd22ff36785a2552..22e4463d1787ab3d37aaaa61cfc402c16a315a2d 100644 (file)
@@ -367,9 +367,9 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev)
 static void menu_reflect(struct cpuidle_device *dev, int index)
 {
        struct menu_device *data = this_cpu_ptr(&menu_devices);
+
        data->last_state_idx = index;
-       if (index >= 0)
-               data->needs_update = 1;
+       data->needs_update = 1;
 }
 
 /**
index 033c0c86f6ec051af426b970825f84146223f840..4044125fb5d5fa7589e2cbcb12623546d0effa75 100644 (file)
@@ -162,10 +162,10 @@ config CRYPTO_GHASH_S390
 config CRYPTO_DEV_MV_CESA
        tristate "Marvell's Cryptographic Engine"
        depends on PLAT_ORION
-       select CRYPTO_ALGAPI
        select CRYPTO_AES
-       select CRYPTO_BLKCIPHER2
+       select CRYPTO_BLKCIPHER
        select CRYPTO_HASH
+       select SRAM
        help
          This driver allows you to utilize the Cryptographic Engines and
          Security Accelerator (CESA) which can be found on the Marvell Orion
@@ -173,10 +173,27 @@ config CRYPTO_DEV_MV_CESA
 
          Currently the driver supports AES in ECB and CBC mode without DMA.
 
+config CRYPTO_DEV_MARVELL_CESA
+       tristate "New Marvell's Cryptographic Engine driver"
+       depends on PLAT_ORION || ARCH_MVEBU
+       select CRYPTO_AES
+       select CRYPTO_DES
+       select CRYPTO_BLKCIPHER
+       select CRYPTO_HASH
+       select SRAM
+       help
+         This driver allows you to utilize the Cryptographic Engines and
+         Security Accelerator (CESA) which can be found on the Armada 370.
+         This driver supports CPU offload through DMA transfers.
+
+         This driver is aimed at replacing the mv_cesa driver. This will only
+         happen once it has received proper testing.
+
 config CRYPTO_DEV_NIAGARA2
        tristate "Niagara2 Stream Processing Unit driver"
        select CRYPTO_DES
-       select CRYPTO_ALGAPI
+       select CRYPTO_BLKCIPHER
+       select CRYPTO_HASH
        depends on SPARC64
        help
          Each core of a Niagara2 processor contains a Stream
@@ -189,7 +206,6 @@ config CRYPTO_DEV_NIAGARA2
 config CRYPTO_DEV_HIFN_795X
        tristate "Driver HIFN 795x crypto accelerator chips"
        select CRYPTO_DES
-       select CRYPTO_ALGAPI
        select CRYPTO_BLKCIPHER
        select HW_RANDOM if CRYPTO_DEV_HIFN_795X_RNG
        depends on PCI
@@ -208,8 +224,10 @@ source drivers/crypto/caam/Kconfig
 
 config CRYPTO_DEV_TALITOS
        tristate "Talitos Freescale Security Engine (SEC)"
-       select CRYPTO_ALGAPI
+       select CRYPTO_AEAD
        select CRYPTO_AUTHENC
+       select CRYPTO_BLKCIPHER
+       select CRYPTO_HASH
        select HW_RANDOM
        depends on FSL_SOC
        help
@@ -222,11 +240,29 @@ config CRYPTO_DEV_TALITOS
          To compile this driver as a module, choose M here: the module
          will be called talitos.
 
+config CRYPTO_DEV_TALITOS1
+       bool "SEC1 (SEC 1.0 and SEC Lite 1.2)"
+       depends on CRYPTO_DEV_TALITOS
+       depends on PPC_8xx || PPC_82xx
+       default y
+       help
+         Say 'Y' here to use the Freescale Security Engine (SEC) version 1.0
+         found on MPC82xx or the Freescale Security Engine (SEC Lite)
+         version 1.2 found on MPC8xx
+
+config CRYPTO_DEV_TALITOS2
+       bool "SEC2+ (SEC version 2.0 or upper)"
+       depends on CRYPTO_DEV_TALITOS
+       default y if !PPC_8xx
+       help
+         Say 'Y' here to use the Freescale Security Engine (SEC)
+         version 2 and following as found on MPC83xx, MPC85xx, etc ...
+
 config CRYPTO_DEV_IXP4XX
        tristate "Driver for IXP4xx crypto hardware acceleration"
        depends on ARCH_IXP4XX && IXP4XX_QMGR && IXP4XX_NPE
        select CRYPTO_DES
-       select CRYPTO_ALGAPI
+       select CRYPTO_AEAD
        select CRYPTO_AUTHENC
        select CRYPTO_BLKCIPHER
        help
@@ -236,7 +272,6 @@ config CRYPTO_DEV_PPC4XX
        tristate "Driver AMCC PPC4xx crypto accelerator"
        depends on PPC && 4xx
        select CRYPTO_HASH
-       select CRYPTO_ALGAPI
        select CRYPTO_BLKCIPHER
        help
          This option allows you to have support for AMCC crypto acceleration.
@@ -257,7 +292,7 @@ config CRYPTO_DEV_OMAP_AES
        tristate "Support for OMAP AES hw engine"
        depends on ARCH_OMAP2 || ARCH_OMAP3 || ARCH_OMAP2PLUS
        select CRYPTO_AES
-       select CRYPTO_BLKCIPHER2
+       select CRYPTO_BLKCIPHER
        help
          OMAP processors have AES module accelerator. Select this if you
          want to use the OMAP module for AES algorithms.
@@ -266,7 +301,7 @@ config CRYPTO_DEV_OMAP_DES
        tristate "Support for OMAP DES3DES hw engine"
        depends on ARCH_OMAP2PLUS
        select CRYPTO_DES
-       select CRYPTO_BLKCIPHER2
+       select CRYPTO_BLKCIPHER
        help
          OMAP processors have DES/3DES module accelerator. Select this if you
          want to use the OMAP module for DES and 3DES algorithms. Currently
@@ -276,9 +311,10 @@ config CRYPTO_DEV_OMAP_DES
 config CRYPTO_DEV_PICOXCELL
        tristate "Support for picoXcell IPSEC and Layer2 crypto engines"
        depends on ARCH_PICOXCELL && HAVE_CLK
+       select CRYPTO_AEAD
        select CRYPTO_AES
        select CRYPTO_AUTHENC
-       select CRYPTO_ALGAPI
+       select CRYPTO_BLKCIPHER
        select CRYPTO_DES
        select CRYPTO_CBC
        select CRYPTO_ECB
@@ -304,7 +340,6 @@ config CRYPTO_DEV_S5P
        tristate "Support for Samsung S5PV210/Exynos crypto accelerator"
        depends on ARCH_S5PV210 || ARCH_EXYNOS
        select CRYPTO_AES
-       select CRYPTO_ALGAPI
        select CRYPTO_BLKCIPHER
        help
          This option allows you to have support for S5P crypto acceleration.
@@ -312,11 +347,13 @@ config CRYPTO_DEV_S5P
          algorithms execution.
 
 config CRYPTO_DEV_NX
-       bool "Support for IBM Power7+ in-Nest cryptographic acceleration"
-       depends on PPC64 && IBMVIO && !CPU_LITTLE_ENDIAN
-       default n
+       bool "Support for IBM PowerPC Nest (NX) cryptographic acceleration"
+       depends on PPC64
        help
-         Support for Power7+ in-Nest cryptographic acceleration.
+         This enables support for the NX hardware cryptographic accelerator
+         coprocessor that is in IBM PowerPC P7+ or later processors.  This
+         does not actually enable any drivers, it only allows you to select
+         which acceleration type (encryption and/or compression) to enable.
 
 if CRYPTO_DEV_NX
        source "drivers/crypto/nx/Kconfig"
@@ -325,7 +362,6 @@ endif
 config CRYPTO_DEV_UX500
        tristate "Driver for ST-Ericsson UX500 crypto hardware acceleration"
        depends on ARCH_U8500
-       select CRYPTO_ALGAPI
        help
          Driver for ST-Ericsson UX500 crypto engine.
 
@@ -343,10 +379,7 @@ config CRYPTO_DEV_BFIN_CRC
 config CRYPTO_DEV_ATMEL_AES
        tristate "Support for Atmel AES hw accelerator"
        depends on ARCH_AT91
-       select CRYPTO_CBC
-       select CRYPTO_ECB
        select CRYPTO_AES
-       select CRYPTO_ALGAPI
        select CRYPTO_BLKCIPHER
        select AT_HDMAC
        help
@@ -361,9 +394,6 @@ config CRYPTO_DEV_ATMEL_TDES
        tristate "Support for Atmel DES/TDES hw accelerator"
        depends on ARCH_AT91
        select CRYPTO_DES
-       select CRYPTO_CBC
-       select CRYPTO_ECB
-       select CRYPTO_ALGAPI
        select CRYPTO_BLKCIPHER
        help
          Some Atmel processors have DES/TDES hw accelerator.
@@ -376,10 +406,7 @@ config CRYPTO_DEV_ATMEL_TDES
 config CRYPTO_DEV_ATMEL_SHA
        tristate "Support for Atmel SHA hw accelerator"
        depends on ARCH_AT91
-       select CRYPTO_SHA1
-       select CRYPTO_SHA256
-       select CRYPTO_SHA512
-       select CRYPTO_ALGAPI
+       select CRYPTO_HASH
        help
          Some Atmel processors have SHA1/SHA224/SHA256/SHA384/SHA512
          hw accelerator.
@@ -392,7 +419,6 @@ config CRYPTO_DEV_ATMEL_SHA
 config CRYPTO_DEV_CCP
        bool "Support for AMD Cryptographic Coprocessor"
        depends on ((X86 && PCI) || (ARM64 && (OF_ADDRESS || ACPI))) && HAS_IOMEM
-       default n
        help
          The AMD Cryptographic Coprocessor provides hardware support
          for encryption, hashing and related operations.
@@ -404,13 +430,11 @@ endif
 config CRYPTO_DEV_MXS_DCP
        tristate "Support for Freescale MXS DCP"
        depends on ARCH_MXS
-       select CRYPTO_SHA1
-       select CRYPTO_SHA256
        select CRYPTO_CBC
        select CRYPTO_ECB
        select CRYPTO_AES
        select CRYPTO_BLKCIPHER
-       select CRYPTO_ALGAPI
+       select CRYPTO_HASH
        help
          The Freescale i.MX23/i.MX28 has SHA1/SHA256 and AES128 CBC/ECB
          co-processor on the die.
@@ -429,7 +453,6 @@ config CRYPTO_DEV_QCE
        select CRYPTO_CBC
        select CRYPTO_XTS
        select CRYPTO_CTR
-       select CRYPTO_ALGAPI
        select CRYPTO_BLKCIPHER
        help
          This driver supports Qualcomm crypto engine accelerator
@@ -439,7 +462,6 @@ config CRYPTO_DEV_QCE
 config CRYPTO_DEV_VMX
        bool "Support for VMX cryptographic acceleration instructions"
        depends on PPC64
-       default n
        help
          Support for VMX cryptographic acceleration instructions.
 
@@ -449,7 +471,6 @@ config CRYPTO_DEV_IMGTEC_HASH
        tristate "Imagination Technologies hardware hash accelerator"
        depends on MIPS || COMPILE_TEST
        depends on HAS_DMA
-       select CRYPTO_ALGAPI
        select CRYPTO_MD5
        select CRYPTO_SHA1
        select CRYPTO_SHA256
index fb84be7e6be5faacea4a99191a38afbb2cc2b5b9..e35c07a8da8568c59d56e35f0f9c30b2362ecbca 100644 (file)
@@ -9,6 +9,7 @@ obj-$(CONFIG_CRYPTO_DEV_HIFN_795X) += hifn_795x.o
 obj-$(CONFIG_CRYPTO_DEV_IMGTEC_HASH) += img-hash.o
 obj-$(CONFIG_CRYPTO_DEV_IXP4XX) += ixp4xx_crypto.o
 obj-$(CONFIG_CRYPTO_DEV_MV_CESA) += mv_cesa.o
+obj-$(CONFIG_CRYPTO_DEV_MARVELL_CESA) += marvell/
 obj-$(CONFIG_CRYPTO_DEV_MXS_DCP) += mxs-dcp.o
 obj-$(CONFIG_CRYPTO_DEV_NIAGARA2) += n2_crypto.o
 n2_crypto-y := n2_core.o n2_asm.o
index e7555ff4cafdb4b0444a09f2de4b2b153f9e78e9..e286e285aa8a48f462b205638986d7b52cb01642 100644 (file)
@@ -45,7 +45,6 @@ config CRYPTO_DEV_FSL_CAAM_RINGSIZE
 config CRYPTO_DEV_FSL_CAAM_INTC
        bool "Job Ring interrupt coalescing"
        depends on CRYPTO_DEV_FSL_CAAM_JR
-       default n
        help
          Enable the Job Ring's interrupt coalescing feature.
 
@@ -77,8 +76,9 @@ config CRYPTO_DEV_FSL_CAAM_CRYPTO_API
        tristate "Register algorithm implementations with the Crypto API"
        depends on CRYPTO_DEV_FSL_CAAM && CRYPTO_DEV_FSL_CAAM_JR
        default y
-       select CRYPTO_ALGAPI
+       select CRYPTO_AEAD
        select CRYPTO_AUTHENC
+       select CRYPTO_BLKCIPHER
        help
          Selecting this will offload crypto for users of the
          scatterlist crypto API (such as the linux native IPSec
@@ -115,7 +115,6 @@ config CRYPTO_DEV_FSL_CAAM_RNG_API
 config CRYPTO_DEV_FSL_CAAM_DEBUG
        bool "Enable debug output in CAAM driver"
        depends on CRYPTO_DEV_FSL_CAAM
-       default n
        help
          Selecting this will enable printing of various debug
          information in the CAAM driver.
index 29071a156cbe131fd58b63abdffd947d7601d325..daca933a82ec9ea1c918a868516e859ffcdbca98 100644 (file)
 /* max IV is max of AES_BLOCK_SIZE, DES3_EDE_BLOCK_SIZE */
 #define CAAM_MAX_IV_LENGTH             16
 
+#define AEAD_DESC_JOB_IO_LEN           (DESC_JOB_IO_LEN + CAAM_CMD_SZ * 2)
+#define GCM_DESC_JOB_IO_LEN            (AEAD_DESC_JOB_IO_LEN + \
+                                        CAAM_CMD_SZ * 4)
+
 /* length of descriptors text */
 #define DESC_AEAD_BASE                 (4 * CAAM_CMD_SZ)
 #define DESC_AEAD_ENC_LEN              (DESC_AEAD_BASE + 15 * CAAM_CMD_SZ)
 #define DESC_AEAD_NULL_DEC_LEN         (DESC_AEAD_NULL_BASE + 17 * CAAM_CMD_SZ)
 
 #define DESC_GCM_BASE                  (3 * CAAM_CMD_SZ)
-#define DESC_GCM_ENC_LEN               (DESC_GCM_BASE + 23 * CAAM_CMD_SZ)
-#define DESC_GCM_DEC_LEN               (DESC_GCM_BASE + 19 * CAAM_CMD_SZ)
+#define DESC_GCM_ENC_LEN               (DESC_GCM_BASE + 16 * CAAM_CMD_SZ)
+#define DESC_GCM_DEC_LEN               (DESC_GCM_BASE + 12 * CAAM_CMD_SZ)
 
 #define DESC_RFC4106_BASE              (3 * CAAM_CMD_SZ)
-#define DESC_RFC4106_ENC_LEN           (DESC_RFC4106_BASE + 15 * CAAM_CMD_SZ)
-#define DESC_RFC4106_DEC_LEN           (DESC_RFC4106_BASE + 14 * CAAM_CMD_SZ)
-#define DESC_RFC4106_GIVENC_LEN                (DESC_RFC4106_BASE + 21 * CAAM_CMD_SZ)
+#define DESC_RFC4106_ENC_LEN           (DESC_RFC4106_BASE + 10 * CAAM_CMD_SZ)
+#define DESC_RFC4106_DEC_LEN           (DESC_RFC4106_BASE + 10 * CAAM_CMD_SZ)
 
 #define DESC_RFC4543_BASE              (3 * CAAM_CMD_SZ)
-#define DESC_RFC4543_ENC_LEN           (DESC_RFC4543_BASE + 25 * CAAM_CMD_SZ)
-#define DESC_RFC4543_DEC_LEN           (DESC_RFC4543_BASE + 27 * CAAM_CMD_SZ)
-#define DESC_RFC4543_GIVENC_LEN                (DESC_RFC4543_BASE + 30 * CAAM_CMD_SZ)
+#define DESC_RFC4543_ENC_LEN           (DESC_RFC4543_BASE + 11 * CAAM_CMD_SZ)
+#define DESC_RFC4543_DEC_LEN           (DESC_RFC4543_BASE + 12 * CAAM_CMD_SZ)
 
 #define DESC_ABLKCIPHER_BASE           (3 * CAAM_CMD_SZ)
 #define DESC_ABLKCIPHER_ENC_LEN                (DESC_ABLKCIPHER_BASE + \
 #define DESC_ABLKCIPHER_DEC_LEN                (DESC_ABLKCIPHER_BASE + \
                                         15 * CAAM_CMD_SZ)
 
-#define DESC_MAX_USED_BYTES            (DESC_RFC4543_GIVENC_LEN + \
-                                        CAAM_MAX_KEY_SIZE)
+#define DESC_MAX_USED_BYTES            (CAAM_DESC_BYTES_MAX - DESC_JOB_IO_LEN)
 #define DESC_MAX_USED_LEN              (DESC_MAX_USED_BYTES / CAAM_CMD_SZ)
 
 #ifdef DEBUG
@@ -258,7 +259,7 @@ static void init_sh_desc_key_aead(u32 *desc, struct caam_ctx *ctx,
 
 static int aead_null_set_sh_desc(struct crypto_aead *aead)
 {
-       struct aead_tfm *tfm = &aead->base.crt_aead;
+       unsigned int ivsize = crypto_aead_ivsize(aead);
        struct caam_ctx *ctx = crypto_aead_ctx(aead);
        struct device *jrdev = ctx->jrdev;
        bool keys_fit_inline = false;
@@ -273,7 +274,7 @@ static int aead_null_set_sh_desc(struct crypto_aead *aead)
            ctx->split_key_pad_len <= CAAM_DESC_BYTES_MAX)
                keys_fit_inline = true;
 
-       /* aead_encrypt shared descriptor */
+       /* old_aead_encrypt shared descriptor */
        desc = ctx->sh_desc_enc;
 
        init_sh_desc(desc, HDR_SHARE_SERIAL);
@@ -362,7 +363,7 @@ static int aead_null_set_sh_desc(struct crypto_aead *aead)
 
        desc = ctx->sh_desc_dec;
 
-       /* aead_decrypt shared descriptor */
+       /* old_aead_decrypt shared descriptor */
        init_sh_desc(desc, HDR_SHARE_SERIAL);
 
        /* Skip if already shared */
@@ -383,7 +384,7 @@ static int aead_null_set_sh_desc(struct crypto_aead *aead)
 
        /* assoclen + cryptlen = seqinlen - ivsize - authsize */
        append_math_sub_imm_u32(desc, REG3, SEQINLEN, IMM,
-                               ctx->authsize + tfm->ivsize);
+                               ctx->authsize + ivsize);
        /* assoclen = (assoclen + cryptlen) - cryptlen */
        append_math_sub(desc, REG2, SEQOUTLEN, REG0, CAAM_CMD_SZ);
        append_math_sub(desc, VARSEQINLEN, REG3, REG2, CAAM_CMD_SZ);
@@ -449,7 +450,7 @@ static int aead_null_set_sh_desc(struct crypto_aead *aead)
 
 static int aead_set_sh_desc(struct crypto_aead *aead)
 {
-       struct aead_tfm *tfm = &aead->base.crt_aead;
+       unsigned int ivsize = crypto_aead_ivsize(aead);
        struct caam_ctx *ctx = crypto_aead_ctx(aead);
        struct crypto_tfm *ctfm = crypto_aead_tfm(aead);
        const char *alg_name = crypto_tfm_alg_name(ctfm);
@@ -496,7 +497,7 @@ static int aead_set_sh_desc(struct crypto_aead *aead)
            CAAM_DESC_BYTES_MAX)
                keys_fit_inline = true;
 
-       /* aead_encrypt shared descriptor */
+       /* old_aead_encrypt shared descriptor */
        desc = ctx->sh_desc_enc;
 
        /* Note: Context registers are saved. */
@@ -510,7 +511,7 @@ static int aead_set_sh_desc(struct crypto_aead *aead)
        append_math_sub_imm_u32(desc, REG3, SEQOUTLEN, IMM, ctx->authsize);
 
        /* assoclen + cryptlen = seqinlen - ivsize */
-       append_math_sub_imm_u32(desc, REG2, SEQINLEN, IMM, tfm->ivsize);
+       append_math_sub_imm_u32(desc, REG2, SEQINLEN, IMM, ivsize);
 
        /* assoclen = (assoclen + cryptlen) - cryptlen */
        append_math_sub(desc, VARSEQINLEN, REG2, REG3, CAAM_CMD_SZ);
@@ -518,7 +519,7 @@ static int aead_set_sh_desc(struct crypto_aead *aead)
        /* read assoc before reading payload */
        append_seq_fifo_load(desc, 0, FIFOLD_CLASS_CLASS2 | FIFOLD_TYPE_MSG |
                             KEY_VLF);
-       aead_append_ld_iv(desc, tfm->ivsize, ctx1_iv_off);
+       aead_append_ld_iv(desc, ivsize, ctx1_iv_off);
 
        /* Load Counter into CONTEXT1 reg */
        if (is_rfc3686)
@@ -565,7 +566,7 @@ static int aead_set_sh_desc(struct crypto_aead *aead)
            CAAM_DESC_BYTES_MAX)
                keys_fit_inline = true;
 
-       /* aead_decrypt shared descriptor */
+       /* old_aead_decrypt shared descriptor */
        desc = ctx->sh_desc_dec;
 
        /* Note: Context registers are saved. */
@@ -577,7 +578,7 @@ static int aead_set_sh_desc(struct crypto_aead *aead)
 
        /* assoclen + cryptlen = seqinlen - ivsize - authsize */
        append_math_sub_imm_u32(desc, REG3, SEQINLEN, IMM,
-                               ctx->authsize + tfm->ivsize);
+                               ctx->authsize + ivsize);
        /* assoclen = (assoclen + cryptlen) - cryptlen */
        append_math_sub(desc, REG2, SEQOUTLEN, REG0, CAAM_CMD_SZ);
        append_math_sub(desc, VARSEQINLEN, REG3, REG2, CAAM_CMD_SZ);
@@ -586,7 +587,7 @@ static int aead_set_sh_desc(struct crypto_aead *aead)
        append_seq_fifo_load(desc, 0, FIFOLD_CLASS_CLASS2 | FIFOLD_TYPE_MSG |
                             KEY_VLF);
 
-       aead_append_ld_iv(desc, tfm->ivsize, ctx1_iv_off);
+       aead_append_ld_iv(desc, ivsize, ctx1_iv_off);
 
        /* Load Counter into CONTEXT1 reg */
        if (is_rfc3686)
@@ -645,20 +646,20 @@ static int aead_set_sh_desc(struct crypto_aead *aead)
        /* Generate IV */
        geniv = NFIFOENTRY_STYPE_PAD | NFIFOENTRY_DEST_DECO |
                NFIFOENTRY_DTYPE_MSG | NFIFOENTRY_LC1 |
-               NFIFOENTRY_PTYPE_RND | (tfm->ivsize << NFIFOENTRY_DLEN_SHIFT);
+               NFIFOENTRY_PTYPE_RND | (ivsize << NFIFOENTRY_DLEN_SHIFT);
        append_load_imm_u32(desc, geniv, LDST_CLASS_IND_CCB |
                            LDST_SRCDST_WORD_INFO_FIFO | LDST_IMM);
        append_cmd(desc, CMD_LOAD | DISABLE_AUTO_INFO_FIFO);
        append_move(desc, MOVE_WAITCOMP |
                    MOVE_SRC_INFIFO | MOVE_DEST_CLASS1CTX |
                    (ctx1_iv_off << MOVE_OFFSET_SHIFT) |
-                   (tfm->ivsize << MOVE_LEN_SHIFT));
+                   (ivsize << MOVE_LEN_SHIFT));
        append_cmd(desc, CMD_LOAD | ENABLE_AUTO_INFO_FIFO);
 
        /* Copy IV to class 1 context */
        append_move(desc, MOVE_SRC_CLASS1CTX | MOVE_DEST_OUTFIFO |
                    (ctx1_iv_off << MOVE_OFFSET_SHIFT) |
-                   (tfm->ivsize << MOVE_LEN_SHIFT));
+                   (ivsize << MOVE_LEN_SHIFT));
 
        /* Return to encryption */
        append_operation(desc, ctx->class2_alg_type |
@@ -676,10 +677,10 @@ static int aead_set_sh_desc(struct crypto_aead *aead)
 
        /* Copy iv from outfifo to class 2 fifo */
        moveiv = NFIFOENTRY_STYPE_OFIFO | NFIFOENTRY_DEST_CLASS2 |
-                NFIFOENTRY_DTYPE_MSG | (tfm->ivsize << NFIFOENTRY_DLEN_SHIFT);
+                NFIFOENTRY_DTYPE_MSG | (ivsize << NFIFOENTRY_DLEN_SHIFT);
        append_load_imm_u32(desc, moveiv, LDST_CLASS_IND_CCB |
                            LDST_SRCDST_WORD_INFO_FIFO | LDST_IMM);
-       append_load_imm_u32(desc, tfm->ivsize, LDST_CLASS_2_CCB |
+       append_load_imm_u32(desc, ivsize, LDST_CLASS_2_CCB |
                            LDST_SRCDST_WORD_DATASZ_REG | LDST_IMM);
 
        /* Load Counter into CONTEXT1 reg */
@@ -698,7 +699,7 @@ static int aead_set_sh_desc(struct crypto_aead *aead)
        append_math_add(desc, VARSEQOUTLEN, SEQINLEN, REG0, CAAM_CMD_SZ);
 
        /* Not need to reload iv */
-       append_seq_fifo_load(desc, tfm->ivsize,
+       append_seq_fifo_load(desc, ivsize,
                             FIFOLD_CLASS_SKIP);
 
        /* Will read cryptlen */
@@ -738,7 +739,6 @@ static int aead_setauthsize(struct crypto_aead *authenc,
 
 static int gcm_set_sh_desc(struct crypto_aead *aead)
 {
-       struct aead_tfm *tfm = &aead->base.crt_aead;
        struct caam_ctx *ctx = crypto_aead_ctx(aead);
        struct device *jrdev = ctx->jrdev;
        bool keys_fit_inline = false;
@@ -754,7 +754,7 @@ static int gcm_set_sh_desc(struct crypto_aead *aead)
         * Job Descriptor and Shared Descriptor
         * must fit into the 64-word Descriptor h/w Buffer
         */
-       if (DESC_GCM_ENC_LEN + DESC_JOB_IO_LEN +
+       if (DESC_GCM_ENC_LEN + GCM_DESC_JOB_IO_LEN +
            ctx->enckeylen <= CAAM_DESC_BYTES_MAX)
                keys_fit_inline = true;
 
@@ -777,34 +777,34 @@ static int gcm_set_sh_desc(struct crypto_aead *aead)
        append_operation(desc, ctx->class1_alg_type |
                         OP_ALG_AS_INITFINAL | OP_ALG_ENCRYPT);
 
-       /* cryptlen = seqoutlen - authsize */
-       append_math_sub_imm_u32(desc, REG3, SEQOUTLEN, IMM, ctx->authsize);
+       /* if assoclen + cryptlen is ZERO, skip to ICV write */
+       append_math_sub(desc, VARSEQOUTLEN, SEQINLEN, REG0, CAAM_CMD_SZ);
+       zero_assoc_jump_cmd2 = append_jump(desc, JUMP_TEST_ALL |
+                                                JUMP_COND_MATH_Z);
 
-       /* assoclen + cryptlen = seqinlen - ivsize */
-       append_math_sub_imm_u32(desc, REG2, SEQINLEN, IMM, tfm->ivsize);
+       /* if assoclen is ZERO, skip reading the assoc data */
+       append_math_add(desc, VARSEQINLEN, ZERO, REG3, CAAM_CMD_SZ);
+       zero_assoc_jump_cmd1 = append_jump(desc, JUMP_TEST_ALL |
+                                                JUMP_COND_MATH_Z);
 
-       /* assoclen = (assoclen + cryptlen) - cryptlen */
-       append_math_sub(desc, REG1, REG2, REG3, CAAM_CMD_SZ);
+       append_math_add(desc, VARSEQOUTLEN, ZERO, REG3, CAAM_CMD_SZ);
+
+       /* skip assoc data */
+       append_seq_fifo_store(desc, 0, FIFOST_TYPE_SKIP | FIFOLDST_VLF);
+
+       /* cryptlen = seqinlen - assoclen */
+       append_math_sub(desc, VARSEQOUTLEN, SEQINLEN, REG3, CAAM_CMD_SZ);
 
        /* if cryptlen is ZERO jump to zero-payload commands */
-       append_math_add(desc, VARSEQOUTLEN, ZERO, REG3, CAAM_CMD_SZ);
        zero_payload_jump_cmd = append_jump(desc, JUMP_TEST_ALL |
                                            JUMP_COND_MATH_Z);
-       /* read IV */
-       append_seq_fifo_load(desc, tfm->ivsize, FIFOLD_CLASS_CLASS1 |
-                            FIFOLD_TYPE_IV | FIFOLD_TYPE_FLUSH1);
-
-       /* if assoclen is ZERO, skip reading the assoc data */
-       append_math_add(desc, VARSEQINLEN, ZERO, REG1, CAAM_CMD_SZ);
-       zero_assoc_jump_cmd1 = append_jump(desc, JUMP_TEST_ALL |
-                                          JUMP_COND_MATH_Z);
 
        /* read assoc data */
        append_seq_fifo_load(desc, 0, FIFOLD_CLASS_CLASS1 | FIFOLDST_VLF |
                             FIFOLD_TYPE_AAD | FIFOLD_TYPE_FLUSH1);
        set_jump_tgt_here(desc, zero_assoc_jump_cmd1);
 
-       append_math_add(desc, VARSEQINLEN, ZERO, REG3, CAAM_CMD_SZ);
+       append_math_sub(desc, VARSEQINLEN, SEQINLEN, REG0, CAAM_CMD_SZ);
 
        /* write encrypted data */
        append_seq_fifo_store(desc, 0, FIFOST_TYPE_MESSAGE_DATA | FIFOLDST_VLF);
@@ -814,31 +814,17 @@ static int gcm_set_sh_desc(struct crypto_aead *aead)
                             FIFOLD_TYPE_MSG | FIFOLD_TYPE_LAST1);
 
        /* jump the zero-payload commands */
-       append_jump(desc, JUMP_TEST_ALL | 7);
+       append_jump(desc, JUMP_TEST_ALL | 2);
 
        /* zero-payload commands */
        set_jump_tgt_here(desc, zero_payload_jump_cmd);
 
-       /* if assoclen is ZERO, jump to IV reading - is the only input data */
-       append_math_add(desc, VARSEQINLEN, ZERO, REG1, CAAM_CMD_SZ);
-       zero_assoc_jump_cmd2 = append_jump(desc, JUMP_TEST_ALL |
-                                          JUMP_COND_MATH_Z);
-       /* read IV */
-       append_seq_fifo_load(desc, tfm->ivsize, FIFOLD_CLASS_CLASS1 |
-                            FIFOLD_TYPE_IV | FIFOLD_TYPE_FLUSH1);
-
        /* read assoc data */
        append_seq_fifo_load(desc, 0, FIFOLD_CLASS_CLASS1 | FIFOLDST_VLF |
                             FIFOLD_TYPE_AAD | FIFOLD_TYPE_LAST1);
 
-       /* jump to ICV writing */
-       append_jump(desc, JUMP_TEST_ALL | 2);
-
-       /* read IV - is the only input data */
+       /* There is no input data */
        set_jump_tgt_here(desc, zero_assoc_jump_cmd2);
-       append_seq_fifo_load(desc, tfm->ivsize, FIFOLD_CLASS_CLASS1 |
-                            FIFOLD_TYPE_IV | FIFOLD_TYPE_FLUSH1 |
-                            FIFOLD_TYPE_LAST1);
 
        /* write ICV */
        append_seq_store(desc, ctx->authsize, LDST_CLASS_1_CCB |
@@ -862,7 +848,7 @@ static int gcm_set_sh_desc(struct crypto_aead *aead)
         * must all fit into the 64-word Descriptor h/w Buffer
         */
        keys_fit_inline = false;
-       if (DESC_GCM_DEC_LEN + DESC_JOB_IO_LEN +
+       if (DESC_GCM_DEC_LEN + GCM_DESC_JOB_IO_LEN +
            ctx->enckeylen <= CAAM_DESC_BYTES_MAX)
                keys_fit_inline = true;
 
@@ -886,33 +872,30 @@ static int gcm_set_sh_desc(struct crypto_aead *aead)
        append_operation(desc, ctx->class1_alg_type |
                         OP_ALG_AS_INITFINAL | OP_ALG_DECRYPT | OP_ALG_ICV_ON);
 
-       /* assoclen + cryptlen = seqinlen - ivsize - icvsize */
-       append_math_sub_imm_u32(desc, REG3, SEQINLEN, IMM,
-                               ctx->authsize + tfm->ivsize);
-
-       /* assoclen = (assoclen + cryptlen) - cryptlen */
-       append_math_sub(desc, REG2, SEQOUTLEN, REG0, CAAM_CMD_SZ);
-       append_math_sub(desc, REG1, REG3, REG2, CAAM_CMD_SZ);
+       /* if assoclen is ZERO, skip reading the assoc data */
+       append_math_add(desc, VARSEQINLEN, ZERO, REG3, CAAM_CMD_SZ);
+       zero_assoc_jump_cmd1 = append_jump(desc, JUMP_TEST_ALL |
+                                                JUMP_COND_MATH_Z);
 
-       /* read IV */
-       append_seq_fifo_load(desc, tfm->ivsize, FIFOLD_CLASS_CLASS1 |
-                            FIFOLD_TYPE_IV | FIFOLD_TYPE_FLUSH1);
+       append_math_add(desc, VARSEQOUTLEN, ZERO, REG3, CAAM_CMD_SZ);
 
-       /* jump to zero-payload command if cryptlen is zero */
-       append_math_add(desc, VARSEQOUTLEN, ZERO, REG2, CAAM_CMD_SZ);
-       zero_payload_jump_cmd = append_jump(desc, JUMP_TEST_ALL |
-                                           JUMP_COND_MATH_Z);
+       /* skip assoc data */
+       append_seq_fifo_store(desc, 0, FIFOST_TYPE_SKIP | FIFOLDST_VLF);
 
-       append_math_add(desc, VARSEQINLEN, ZERO, REG1, CAAM_CMD_SZ);
-       /* if asoclen is ZERO, skip reading assoc data */
-       zero_assoc_jump_cmd1 = append_jump(desc, JUMP_TEST_ALL |
-                                          JUMP_COND_MATH_Z);
        /* read assoc data */
        append_seq_fifo_load(desc, 0, FIFOLD_CLASS_CLASS1 | FIFOLDST_VLF |
                             FIFOLD_TYPE_AAD | FIFOLD_TYPE_FLUSH1);
+
        set_jump_tgt_here(desc, zero_assoc_jump_cmd1);
 
-       append_math_add(desc, VARSEQINLEN, ZERO, REG2, CAAM_CMD_SZ);
+       /* cryptlen = seqoutlen - assoclen */
+       append_math_sub(desc, VARSEQINLEN, SEQOUTLEN, REG0, CAAM_CMD_SZ);
+
+       /* jump to zero-payload command if cryptlen is zero */
+       zero_payload_jump_cmd = append_jump(desc, JUMP_TEST_ALL |
+                                           JUMP_COND_MATH_Z);
+
+       append_math_sub(desc, VARSEQOUTLEN, SEQOUTLEN, REG0, CAAM_CMD_SZ);
 
        /* store encrypted data */
        append_seq_fifo_store(desc, 0, FIFOST_TYPE_MESSAGE_DATA | FIFOLDST_VLF);
@@ -921,21 +904,9 @@ static int gcm_set_sh_desc(struct crypto_aead *aead)
        append_seq_fifo_load(desc, 0, FIFOLD_CLASS_CLASS1 | FIFOLDST_VLF |
                             FIFOLD_TYPE_MSG | FIFOLD_TYPE_FLUSH1);
 
-       /* jump the zero-payload commands */
-       append_jump(desc, JUMP_TEST_ALL | 4);
-
        /* zero-payload command */
        set_jump_tgt_here(desc, zero_payload_jump_cmd);
 
-       /* if assoclen is ZERO, jump to ICV reading */
-       append_math_add(desc, VARSEQINLEN, ZERO, REG1, CAAM_CMD_SZ);
-       zero_assoc_jump_cmd2 = append_jump(desc, JUMP_TEST_ALL |
-                                          JUMP_COND_MATH_Z);
-       /* read assoc data */
-       append_seq_fifo_load(desc, 0, FIFOLD_CLASS_CLASS1 | FIFOLDST_VLF |
-                            FIFOLD_TYPE_AAD | FIFOLD_TYPE_FLUSH1);
-       set_jump_tgt_here(desc, zero_assoc_jump_cmd2);
-
        /* read ICV */
        append_seq_fifo_load(desc, ctx->authsize, FIFOLD_CLASS_CLASS1 |
                             FIFOLD_TYPE_ICV | FIFOLD_TYPE_LAST1);
@@ -968,13 +939,11 @@ static int gcm_setauthsize(struct crypto_aead *authenc, unsigned int authsize)
 
 static int rfc4106_set_sh_desc(struct crypto_aead *aead)
 {
-       struct aead_tfm *tfm = &aead->base.crt_aead;
        struct caam_ctx *ctx = crypto_aead_ctx(aead);
        struct device *jrdev = ctx->jrdev;
        bool keys_fit_inline = false;
-       u32 *key_jump_cmd, *move_cmd, *write_iv_cmd;
+       u32 *key_jump_cmd;
        u32 *desc;
-       u32 geniv;
 
        if (!ctx->enckeylen || !ctx->authsize)
                return 0;
@@ -984,7 +953,7 @@ static int rfc4106_set_sh_desc(struct crypto_aead *aead)
         * Job Descriptor and Shared Descriptor
         * must fit into the 64-word Descriptor h/w Buffer
         */
-       if (DESC_RFC4106_ENC_LEN + DESC_JOB_IO_LEN +
+       if (DESC_RFC4106_ENC_LEN + GCM_DESC_JOB_IO_LEN +
            ctx->enckeylen <= CAAM_DESC_BYTES_MAX)
                keys_fit_inline = true;
 
@@ -1007,29 +976,21 @@ static int rfc4106_set_sh_desc(struct crypto_aead *aead)
        append_operation(desc, ctx->class1_alg_type |
                         OP_ALG_AS_INITFINAL | OP_ALG_ENCRYPT);
 
-       /* cryptlen = seqoutlen - authsize */
-       append_math_sub_imm_u32(desc, REG3, SEQOUTLEN, IMM, ctx->authsize);
+       append_math_add(desc, VARSEQINLEN, ZERO, REG3, CAAM_CMD_SZ);
        append_math_add(desc, VARSEQOUTLEN, ZERO, REG3, CAAM_CMD_SZ);
 
-       /* assoclen + cryptlen = seqinlen - ivsize */
-       append_math_sub_imm_u32(desc, REG2, SEQINLEN, IMM, tfm->ivsize);
-
-       /* assoclen = (assoclen + cryptlen) - cryptlen */
-       append_math_sub(desc, VARSEQINLEN, REG2, REG3, CAAM_CMD_SZ);
-
-       /* Read Salt */
-       append_fifo_load_as_imm(desc, (void *)(ctx->key + ctx->enckeylen),
-                               4, FIFOLD_CLASS_CLASS1 | FIFOLD_TYPE_IV);
-       /* Read AES-GCM-ESP IV */
-       append_seq_fifo_load(desc, tfm->ivsize, FIFOLD_CLASS_CLASS1 |
-                            FIFOLD_TYPE_IV | FIFOLD_TYPE_FLUSH1);
+       /* Skip assoc data */
+       append_seq_fifo_store(desc, 0, FIFOST_TYPE_SKIP | FIFOLDST_VLF);
 
        /* Read assoc data */
        append_seq_fifo_load(desc, 0, FIFOLD_CLASS_CLASS1 | FIFOLDST_VLF |
                             FIFOLD_TYPE_AAD | FIFOLD_TYPE_FLUSH1);
 
+       /* cryptlen = seqoutlen - assoclen */
+       append_math_sub(desc, VARSEQOUTLEN, SEQINLEN, REG0, CAAM_CMD_SZ);
+
        /* Will read cryptlen bytes */
-       append_math_add(desc, VARSEQINLEN, ZERO, REG3, CAAM_CMD_SZ);
+       append_math_sub(desc, VARSEQINLEN, SEQINLEN, REG0, CAAM_CMD_SZ);
 
        /* Write encrypted data */
        append_seq_fifo_store(desc, 0, FIFOST_TYPE_MESSAGE_DATA | FIFOLDST_VLF);
@@ -1083,30 +1044,21 @@ static int rfc4106_set_sh_desc(struct crypto_aead *aead)
        append_operation(desc, ctx->class1_alg_type |
                         OP_ALG_AS_INITFINAL | OP_ALG_DECRYPT | OP_ALG_ICV_ON);
 
-       /* assoclen + cryptlen = seqinlen - ivsize - icvsize */
-       append_math_sub_imm_u32(desc, REG3, SEQINLEN, IMM,
-                               ctx->authsize + tfm->ivsize);
-
-       /* assoclen = (assoclen + cryptlen) - cryptlen */
-       append_math_sub(desc, REG2, SEQOUTLEN, REG0, CAAM_CMD_SZ);
-       append_math_sub(desc, VARSEQINLEN, REG3, REG2, CAAM_CMD_SZ);
-
-       /* Will write cryptlen bytes */
-       append_math_sub(desc, VARSEQOUTLEN, SEQOUTLEN, REG0, CAAM_CMD_SZ);
+       append_math_add(desc, VARSEQINLEN, ZERO, REG3, CAAM_CMD_SZ);
+       append_math_add(desc, VARSEQOUTLEN, ZERO, REG3, CAAM_CMD_SZ);
 
-       /* Read Salt */
-       append_fifo_load_as_imm(desc, (void *)(ctx->key + ctx->enckeylen),
-                               4, FIFOLD_CLASS_CLASS1 | FIFOLD_TYPE_IV);
-       /* Read AES-GCM-ESP IV */
-       append_seq_fifo_load(desc, tfm->ivsize, FIFOLD_CLASS_CLASS1 |
-                            FIFOLD_TYPE_IV | FIFOLD_TYPE_FLUSH1);
+       /* Skip assoc data */
+       append_seq_fifo_store(desc, 0, FIFOST_TYPE_SKIP | FIFOLDST_VLF);
 
        /* Read assoc data */
        append_seq_fifo_load(desc, 0, FIFOLD_CLASS_CLASS1 | FIFOLDST_VLF |
                             FIFOLD_TYPE_AAD | FIFOLD_TYPE_FLUSH1);
 
+       /* Will write cryptlen bytes */
+       append_math_sub(desc, VARSEQOUTLEN, SEQOUTLEN, REG0, CAAM_CMD_SZ);
+
        /* Will read cryptlen bytes */
-       append_math_add(desc, VARSEQINLEN, ZERO, REG2, CAAM_CMD_SZ);
+       append_math_sub(desc, VARSEQINLEN, SEQOUTLEN, REG0, CAAM_CMD_SZ);
 
        /* Store payload data */
        append_seq_fifo_store(desc, 0, FIFOST_TYPE_MESSAGE_DATA | FIFOLDST_VLF);
@@ -1132,107 +1084,6 @@ static int rfc4106_set_sh_desc(struct crypto_aead *aead)
                       desc_bytes(desc), 1);
 #endif
 
-       /*
-        * Job Descriptor and Shared Descriptors
-        * must all fit into the 64-word Descriptor h/w Buffer
-        */
-       keys_fit_inline = false;
-       if (DESC_RFC4106_GIVENC_LEN + DESC_JOB_IO_LEN +
-           ctx->split_key_pad_len + ctx->enckeylen <=
-           CAAM_DESC_BYTES_MAX)
-               keys_fit_inline = true;
-
-       /* rfc4106_givencrypt shared descriptor */
-       desc = ctx->sh_desc_givenc;
-
-       init_sh_desc(desc, HDR_SHARE_SERIAL);
-
-       /* Skip key loading if it is loaded due to sharing */
-       key_jump_cmd = append_jump(desc, JUMP_JSL | JUMP_TEST_ALL |
-                                  JUMP_COND_SHRD);
-       if (keys_fit_inline)
-               append_key_as_imm(desc, (void *)ctx->key, ctx->enckeylen,
-                                 ctx->enckeylen, CLASS_1 | KEY_DEST_CLASS_REG);
-       else
-               append_key(desc, ctx->key_dma, ctx->enckeylen,
-                          CLASS_1 | KEY_DEST_CLASS_REG);
-       set_jump_tgt_here(desc, key_jump_cmd);
-
-       /* Generate IV */
-       geniv = NFIFOENTRY_STYPE_PAD | NFIFOENTRY_DEST_DECO |
-               NFIFOENTRY_DTYPE_MSG | NFIFOENTRY_LC1 |
-               NFIFOENTRY_PTYPE_RND | (tfm->ivsize << NFIFOENTRY_DLEN_SHIFT);
-       append_load_imm_u32(desc, geniv, LDST_CLASS_IND_CCB |
-                           LDST_SRCDST_WORD_INFO_FIFO | LDST_IMM);
-       append_cmd(desc, CMD_LOAD | DISABLE_AUTO_INFO_FIFO);
-       move_cmd = append_move(desc, MOVE_SRC_INFIFO | MOVE_DEST_DESCBUF |
-                              (tfm->ivsize << MOVE_LEN_SHIFT));
-       append_cmd(desc, CMD_LOAD | ENABLE_AUTO_INFO_FIFO);
-
-       /* Copy generated IV to OFIFO */
-       write_iv_cmd = append_move(desc, MOVE_SRC_DESCBUF | MOVE_DEST_OUTFIFO |
-                                  (tfm->ivsize << MOVE_LEN_SHIFT));
-
-       /* Class 1 operation */
-       append_operation(desc, ctx->class1_alg_type |
-                        OP_ALG_AS_INITFINAL | OP_ALG_ENCRYPT);
-
-       /* ivsize + cryptlen = seqoutlen - authsize */
-       append_math_sub_imm_u32(desc, REG3, SEQOUTLEN, IMM, ctx->authsize);
-
-       /* assoclen = seqinlen - (ivsize + cryptlen) */
-       append_math_sub(desc, VARSEQINLEN, SEQINLEN, REG3, CAAM_CMD_SZ);
-
-       /* Will write ivsize + cryptlen */
-       append_math_add(desc, VARSEQOUTLEN, REG3, REG0, CAAM_CMD_SZ);
-
-       /* Read Salt and generated IV */
-       append_cmd(desc, CMD_FIFO_LOAD | FIFOLD_CLASS_CLASS1 | FIFOLD_TYPE_IV |
-                  FIFOLD_TYPE_FLUSH1 | IMMEDIATE | 12);
-       /* Append Salt */
-       append_data(desc, (void *)(ctx->key + ctx->enckeylen), 4);
-       set_move_tgt_here(desc, move_cmd);
-       set_move_tgt_here(desc, write_iv_cmd);
-       /* Blank commands. Will be overwritten by generated IV. */
-       append_cmd(desc, 0x00000000);
-       append_cmd(desc, 0x00000000);
-       /* End of blank commands */
-
-       /* No need to reload iv */
-       append_seq_fifo_load(desc, tfm->ivsize, FIFOLD_CLASS_SKIP);
-
-       /* Read assoc data */
-       append_seq_fifo_load(desc, 0, FIFOLD_CLASS_CLASS1 | FIFOLDST_VLF |
-                            FIFOLD_TYPE_AAD | FIFOLD_TYPE_FLUSH1);
-
-       /* Will read cryptlen */
-       append_math_add(desc, VARSEQINLEN, SEQINLEN, REG0, CAAM_CMD_SZ);
-
-       /* Store generated IV and encrypted data */
-       append_seq_fifo_store(desc, 0, FIFOST_TYPE_MESSAGE_DATA | FIFOLDST_VLF);
-
-       /* Read payload data */
-       append_seq_fifo_load(desc, 0, FIFOLD_CLASS_CLASS1 | FIFOLDST_VLF |
-                            FIFOLD_TYPE_MSG | FIFOLD_TYPE_LAST1);
-
-       /* Write ICV */
-       append_seq_store(desc, ctx->authsize, LDST_CLASS_1_CCB |
-                        LDST_SRCDST_BYTE_CONTEXT);
-
-       ctx->sh_desc_givenc_dma = dma_map_single(jrdev, desc,
-                                                desc_bytes(desc),
-                                                DMA_TO_DEVICE);
-       if (dma_mapping_error(jrdev, ctx->sh_desc_givenc_dma)) {
-               dev_err(jrdev, "unable to map shared descriptor\n");
-               return -ENOMEM;
-       }
-#ifdef DEBUG
-       print_hex_dump(KERN_ERR,
-                      "rfc4106 givenc shdesc@"__stringify(__LINE__)": ",
-                      DUMP_PREFIX_ADDRESS, 16, 4, desc,
-                      desc_bytes(desc), 1);
-#endif
-
        return 0;
 }
 
@@ -1249,14 +1100,12 @@ static int rfc4106_setauthsize(struct crypto_aead *authenc,
 
 static int rfc4543_set_sh_desc(struct crypto_aead *aead)
 {
-       struct aead_tfm *tfm = &aead->base.crt_aead;
        struct caam_ctx *ctx = crypto_aead_ctx(aead);
        struct device *jrdev = ctx->jrdev;
        bool keys_fit_inline = false;
-       u32 *key_jump_cmd, *write_iv_cmd, *write_aad_cmd;
+       u32 *key_jump_cmd;
        u32 *read_move_cmd, *write_move_cmd;
        u32 *desc;
-       u32 geniv;
 
        if (!ctx->enckeylen || !ctx->authsize)
                return 0;
@@ -1266,7 +1115,7 @@ static int rfc4543_set_sh_desc(struct crypto_aead *aead)
         * Job Descriptor and Shared Descriptor
         * must fit into the 64-word Descriptor h/w Buffer
         */
-       if (DESC_RFC4543_ENC_LEN + DESC_JOB_IO_LEN +
+       if (DESC_RFC4543_ENC_LEN + GCM_DESC_JOB_IO_LEN +
            ctx->enckeylen <= CAAM_DESC_BYTES_MAX)
                keys_fit_inline = true;
 
@@ -1289,48 +1138,8 @@ static int rfc4543_set_sh_desc(struct crypto_aead *aead)
        append_operation(desc, ctx->class1_alg_type |
                         OP_ALG_AS_INITFINAL | OP_ALG_ENCRYPT);
 
-       /* Load AES-GMAC ESP IV into Math1 register */
-       append_cmd(desc, CMD_SEQ_LOAD | LDST_SRCDST_WORD_DECO_MATH1 |
-                  LDST_CLASS_DECO | tfm->ivsize);
-
-       /* Wait the DMA transaction to finish */
-       append_jump(desc, JUMP_TEST_ALL | JUMP_COND_CALM |
-                   (1 << JUMP_OFFSET_SHIFT));
-
-       /* Overwrite blank immediate AES-GMAC ESP IV data */
-       write_iv_cmd = append_move(desc, MOVE_SRC_MATH1 | MOVE_DEST_DESCBUF |
-                                  (tfm->ivsize << MOVE_LEN_SHIFT));
-
-       /* Overwrite blank immediate AAD data */
-       write_aad_cmd = append_move(desc, MOVE_SRC_MATH1 | MOVE_DEST_DESCBUF |
-                                   (tfm->ivsize << MOVE_LEN_SHIFT));
-
-       /* cryptlen = seqoutlen - authsize */
-       append_math_sub_imm_u32(desc, REG3, SEQOUTLEN, IMM, ctx->authsize);
-
-       /* assoclen = (seqinlen - ivsize) - cryptlen */
-       append_math_sub(desc, VARSEQINLEN, SEQINLEN, REG3, CAAM_CMD_SZ);
-
-       /* Read Salt and AES-GMAC ESP IV */
-       append_cmd(desc, CMD_FIFO_LOAD | FIFOLD_CLASS_CLASS1 | IMMEDIATE |
-                  FIFOLD_TYPE_IV | FIFOLD_TYPE_FLUSH1 | (4 + tfm->ivsize));
-       /* Append Salt */
-       append_data(desc, (void *)(ctx->key + ctx->enckeylen), 4);
-       set_move_tgt_here(desc, write_iv_cmd);
-       /* Blank commands. Will be overwritten by AES-GMAC ESP IV. */
-       append_cmd(desc, 0x00000000);
-       append_cmd(desc, 0x00000000);
-       /* End of blank commands */
-
-       /* Read assoc data */
-       append_seq_fifo_load(desc, 0, FIFOLD_CLASS_CLASS1 | FIFOLDST_VLF |
-                            FIFOLD_TYPE_AAD);
-
-       /* Will read cryptlen bytes */
-       append_math_add(desc, VARSEQINLEN, ZERO, REG3, CAAM_CMD_SZ);
-
-       /* Will write cryptlen bytes */
-       append_math_add(desc, VARSEQOUTLEN, ZERO, REG3, CAAM_CMD_SZ);
+       /* assoclen + cryptlen = seqinlen */
+       append_math_sub(desc, REG3, SEQINLEN, REG0, CAAM_CMD_SZ);
 
        /*
         * MOVE_LEN opcode is not available in all SEC HW revisions,
@@ -1342,16 +1151,13 @@ static int rfc4543_set_sh_desc(struct crypto_aead *aead)
        write_move_cmd = append_move(desc, MOVE_SRC_MATH3 | MOVE_DEST_DESCBUF |
                                     (0x8 << MOVE_LEN_SHIFT));
 
-       /* Authenticate AES-GMAC ESP IV  */
-       append_cmd(desc, CMD_FIFO_LOAD | FIFOLD_CLASS_CLASS1 | IMMEDIATE |
-                  FIFOLD_TYPE_AAD | tfm->ivsize);
-       set_move_tgt_here(desc, write_aad_cmd);
-       /* Blank commands. Will be overwritten by AES-GMAC ESP IV. */
-       append_cmd(desc, 0x00000000);
-       append_cmd(desc, 0x00000000);
-       /* End of blank commands */
+       /* Will read assoclen + cryptlen bytes */
+       append_math_sub(desc, VARSEQINLEN, SEQINLEN, REG0, CAAM_CMD_SZ);
 
-       /* Read and write cryptlen bytes */
+       /* Will write assoclen + cryptlen bytes */
+       append_math_sub(desc, VARSEQOUTLEN, SEQINLEN, REG0, CAAM_CMD_SZ);
+
+       /* Read and write assoclen + cryptlen bytes */
        aead_append_src_dst(desc, FIFOLD_TYPE_AAD);
 
        set_move_tgt_here(desc, read_move_cmd);
@@ -1382,7 +1188,7 @@ static int rfc4543_set_sh_desc(struct crypto_aead *aead)
         * must all fit into the 64-word Descriptor h/w Buffer
         */
        keys_fit_inline = false;
-       if (DESC_RFC4543_DEC_LEN + DESC_JOB_IO_LEN +
+       if (DESC_RFC4543_DEC_LEN + GCM_DESC_JOB_IO_LEN +
            ctx->enckeylen <= CAAM_DESC_BYTES_MAX)
                keys_fit_inline = true;
 
@@ -1405,28 +1211,8 @@ static int rfc4543_set_sh_desc(struct crypto_aead *aead)
        append_operation(desc, ctx->class1_alg_type |
                         OP_ALG_AS_INITFINAL | OP_ALG_DECRYPT | OP_ALG_ICV_ON);
 
-       /* Load AES-GMAC ESP IV into Math1 register */
-       append_cmd(desc, CMD_SEQ_LOAD | LDST_SRCDST_WORD_DECO_MATH1 |
-                  LDST_CLASS_DECO | tfm->ivsize);
-
-       /* Wait the DMA transaction to finish */
-       append_jump(desc, JUMP_TEST_ALL | JUMP_COND_CALM |
-                   (1 << JUMP_OFFSET_SHIFT));
-
-       /* assoclen + cryptlen = (seqinlen - ivsize) - icvsize */
-       append_math_sub_imm_u32(desc, REG3, SEQINLEN, IMM, ctx->authsize);
-
-       /* Overwrite blank immediate AES-GMAC ESP IV data */
-       write_iv_cmd = append_move(desc, MOVE_SRC_MATH1 | MOVE_DEST_DESCBUF |
-                                  (tfm->ivsize << MOVE_LEN_SHIFT));
-
-       /* Overwrite blank immediate AAD data */
-       write_aad_cmd = append_move(desc, MOVE_SRC_MATH1 | MOVE_DEST_DESCBUF |
-                                   (tfm->ivsize << MOVE_LEN_SHIFT));
-
-       /* assoclen = (assoclen + cryptlen) - cryptlen */
-       append_math_sub(desc, REG2, SEQOUTLEN, REG0, CAAM_CMD_SZ);
-       append_math_sub(desc, VARSEQINLEN, REG3, REG2, CAAM_CMD_SZ);
+       /* assoclen + cryptlen = seqoutlen */
+       append_math_sub(desc, REG3, SEQOUTLEN, REG0, CAAM_CMD_SZ);
 
        /*
         * MOVE_LEN opcode is not available in all SEC HW revisions,
@@ -1438,40 +1224,16 @@ static int rfc4543_set_sh_desc(struct crypto_aead *aead)
        write_move_cmd = append_move(desc, MOVE_SRC_MATH3 | MOVE_DEST_DESCBUF |
                                     (0x8 << MOVE_LEN_SHIFT));
 
-       /* Read Salt and AES-GMAC ESP IV */
-       append_cmd(desc, CMD_FIFO_LOAD | FIFOLD_CLASS_CLASS1 | IMMEDIATE |
-                  FIFOLD_TYPE_IV | FIFOLD_TYPE_FLUSH1 | (4 + tfm->ivsize));
-       /* Append Salt */
-       append_data(desc, (void *)(ctx->key + ctx->enckeylen), 4);
-       set_move_tgt_here(desc, write_iv_cmd);
-       /* Blank commands. Will be overwritten by AES-GMAC ESP IV. */
-       append_cmd(desc, 0x00000000);
-       append_cmd(desc, 0x00000000);
-       /* End of blank commands */
-
-       /* Read assoc data */
-       append_seq_fifo_load(desc, 0, FIFOLD_CLASS_CLASS1 | FIFOLDST_VLF |
-                            FIFOLD_TYPE_AAD);
-
-       /* Will read cryptlen bytes */
-       append_math_add(desc, VARSEQINLEN, ZERO, REG2, CAAM_CMD_SZ);
-
-       /* Will write cryptlen bytes */
-       append_math_add(desc, VARSEQOUTLEN, ZERO, REG2, CAAM_CMD_SZ);
+       /* Will read assoclen + cryptlen bytes */
+       append_math_sub(desc, VARSEQINLEN, SEQOUTLEN, REG0, CAAM_CMD_SZ);
 
-       /* Authenticate AES-GMAC ESP IV  */
-       append_cmd(desc, CMD_FIFO_LOAD | FIFOLD_CLASS_CLASS1 | IMMEDIATE |
-                  FIFOLD_TYPE_AAD | tfm->ivsize);
-       set_move_tgt_here(desc, write_aad_cmd);
-       /* Blank commands. Will be overwritten by AES-GMAC ESP IV. */
-       append_cmd(desc, 0x00000000);
-       append_cmd(desc, 0x00000000);
-       /* End of blank commands */
+       /* Will write assoclen + cryptlen bytes */
+       append_math_sub(desc, VARSEQOUTLEN, SEQOUTLEN, REG0, CAAM_CMD_SZ);
 
        /* Store payload data */
        append_seq_fifo_store(desc, 0, FIFOST_TYPE_MESSAGE_DATA | FIFOLDST_VLF);
 
-       /* In-snoop cryptlen data */
+       /* In-snoop assoclen + cryptlen data */
        append_seq_fifo_load(desc, 0, FIFOLD_CLASS_BOTH | FIFOLDST_VLF |
                             FIFOLD_TYPE_AAD | FIFOLD_TYPE_LAST2FLUSH1);
 
@@ -1499,156 +1261,27 @@ static int rfc4543_set_sh_desc(struct crypto_aead *aead)
                       desc_bytes(desc), 1);
 #endif
 
-       /*
-        * Job Descriptor and Shared Descriptors
-        * must all fit into the 64-word Descriptor h/w Buffer
-        */
-       keys_fit_inline = false;
-       if (DESC_RFC4543_GIVENC_LEN + DESC_JOB_IO_LEN +
-           ctx->enckeylen <= CAAM_DESC_BYTES_MAX)
-               keys_fit_inline = true;
-
-       /* rfc4543_givencrypt shared descriptor */
-       desc = ctx->sh_desc_givenc;
-
-       init_sh_desc(desc, HDR_SHARE_SERIAL);
-
-       /* Skip key loading if it is loaded due to sharing */
-       key_jump_cmd = append_jump(desc, JUMP_JSL | JUMP_TEST_ALL |
-                                  JUMP_COND_SHRD);
-       if (keys_fit_inline)
-               append_key_as_imm(desc, (void *)ctx->key, ctx->enckeylen,
-                                 ctx->enckeylen, CLASS_1 | KEY_DEST_CLASS_REG);
-       else
-               append_key(desc, ctx->key_dma, ctx->enckeylen,
-                          CLASS_1 | KEY_DEST_CLASS_REG);
-       set_jump_tgt_here(desc, key_jump_cmd);
-
-       /* Generate IV */
-       geniv = NFIFOENTRY_STYPE_PAD | NFIFOENTRY_DEST_DECO |
-               NFIFOENTRY_DTYPE_MSG | NFIFOENTRY_LC1 |
-               NFIFOENTRY_PTYPE_RND | (tfm->ivsize << NFIFOENTRY_DLEN_SHIFT);
-       append_load_imm_u32(desc, geniv, LDST_CLASS_IND_CCB |
-                           LDST_SRCDST_WORD_INFO_FIFO | LDST_IMM);
-       append_cmd(desc, CMD_LOAD | DISABLE_AUTO_INFO_FIFO);
-       /* Move generated IV to Math1 register */
-       append_move(desc, MOVE_SRC_INFIFO | MOVE_DEST_MATH1 |
-                   (tfm->ivsize << MOVE_LEN_SHIFT));
-       append_cmd(desc, CMD_LOAD | ENABLE_AUTO_INFO_FIFO);
+       return 0;
+}
 
-       /* Overwrite blank immediate AES-GMAC IV data */
-       write_iv_cmd = append_move(desc, MOVE_SRC_MATH1 | MOVE_DEST_DESCBUF |
-                                  (tfm->ivsize << MOVE_LEN_SHIFT));
+static int rfc4543_setauthsize(struct crypto_aead *authenc,
+                              unsigned int authsize)
+{
+       struct caam_ctx *ctx = crypto_aead_ctx(authenc);
 
-       /* Overwrite blank immediate AAD data */
-       write_aad_cmd = append_move(desc, MOVE_SRC_MATH1 | MOVE_DEST_DESCBUF |
-                                   (tfm->ivsize << MOVE_LEN_SHIFT));
+       ctx->authsize = authsize;
+       rfc4543_set_sh_desc(authenc);
 
-       /* Copy generated IV to OFIFO */
-       append_move(desc, MOVE_SRC_MATH1 | MOVE_DEST_OUTFIFO |
-                   (tfm->ivsize << MOVE_LEN_SHIFT));
+       return 0;
+}
 
-       /* Class 1 operation */
-       append_operation(desc, ctx->class1_alg_type |
-                        OP_ALG_AS_INITFINAL | OP_ALG_ENCRYPT);
-
-       /* ivsize + cryptlen = seqoutlen - authsize */
-       append_math_sub_imm_u32(desc, REG3, SEQOUTLEN, IMM, ctx->authsize);
-
-       /* assoclen = seqinlen - (ivsize + cryptlen) */
-       append_math_sub(desc, VARSEQINLEN, SEQINLEN, REG3, CAAM_CMD_SZ);
-
-       /* Will write ivsize + cryptlen */
-       append_math_add(desc, VARSEQOUTLEN, REG3, REG0, CAAM_CMD_SZ);
-
-       /*
-        * MOVE_LEN opcode is not available in all SEC HW revisions,
-        * thus need to do some magic, i.e. self-patch the descriptor
-        * buffer.
-        */
-       read_move_cmd = append_move(desc, MOVE_SRC_DESCBUF | MOVE_DEST_MATH3 |
-                                   (0x6 << MOVE_LEN_SHIFT));
-       write_move_cmd = append_move(desc, MOVE_SRC_MATH3 | MOVE_DEST_DESCBUF |
-                                    (0x8 << MOVE_LEN_SHIFT));
-
-       /* Read Salt and AES-GMAC generated IV */
-       append_cmd(desc, CMD_FIFO_LOAD | FIFOLD_CLASS_CLASS1 | IMMEDIATE |
-                  FIFOLD_TYPE_IV | FIFOLD_TYPE_FLUSH1 | (4 + tfm->ivsize));
-       /* Append Salt */
-       append_data(desc, (void *)(ctx->key + ctx->enckeylen), 4);
-       set_move_tgt_here(desc, write_iv_cmd);
-       /* Blank commands. Will be overwritten by AES-GMAC generated IV. */
-       append_cmd(desc, 0x00000000);
-       append_cmd(desc, 0x00000000);
-       /* End of blank commands */
-
-       /* No need to reload iv */
-       append_seq_fifo_load(desc, tfm->ivsize, FIFOLD_CLASS_SKIP);
-
-       /* Read assoc data */
-       append_seq_fifo_load(desc, 0, FIFOLD_CLASS_CLASS1 | FIFOLDST_VLF |
-                            FIFOLD_TYPE_AAD);
-
-       /* Will read cryptlen */
-       append_math_add(desc, VARSEQINLEN, SEQINLEN, REG0, CAAM_CMD_SZ);
-
-       /* Authenticate AES-GMAC IV  */
-       append_cmd(desc, CMD_FIFO_LOAD | FIFOLD_CLASS_CLASS1 | IMMEDIATE |
-                  FIFOLD_TYPE_AAD | tfm->ivsize);
-       set_move_tgt_here(desc, write_aad_cmd);
-       /* Blank commands. Will be overwritten by AES-GMAC IV. */
-       append_cmd(desc, 0x00000000);
-       append_cmd(desc, 0x00000000);
-       /* End of blank commands */
-
-       /* Read and write cryptlen bytes */
-       aead_append_src_dst(desc, FIFOLD_TYPE_AAD);
-
-       set_move_tgt_here(desc, read_move_cmd);
-       set_move_tgt_here(desc, write_move_cmd);
-       append_cmd(desc, CMD_LOAD | DISABLE_AUTO_INFO_FIFO);
-       /* Move payload data to OFIFO */
-       append_move(desc, MOVE_SRC_INFIFO_CL | MOVE_DEST_OUTFIFO);
-
-       /* Write ICV */
-       append_seq_store(desc, ctx->authsize, LDST_CLASS_1_CCB |
-                        LDST_SRCDST_BYTE_CONTEXT);
-
-       ctx->sh_desc_givenc_dma = dma_map_single(jrdev, desc,
-                                                desc_bytes(desc),
-                                                DMA_TO_DEVICE);
-       if (dma_mapping_error(jrdev, ctx->sh_desc_givenc_dma)) {
-               dev_err(jrdev, "unable to map shared descriptor\n");
-               return -ENOMEM;
-       }
-#ifdef DEBUG
-       print_hex_dump(KERN_ERR,
-                      "rfc4543 givenc shdesc@"__stringify(__LINE__)": ",
-                      DUMP_PREFIX_ADDRESS, 16, 4, desc,
-                      desc_bytes(desc), 1);
-#endif
-
-       return 0;
-}
-
-static int rfc4543_setauthsize(struct crypto_aead *authenc,
-                              unsigned int authsize)
-{
-       struct caam_ctx *ctx = crypto_aead_ctx(authenc);
-
-       ctx->authsize = authsize;
-       rfc4543_set_sh_desc(authenc);
-
-       return 0;
-}
-
-static u32 gen_split_aead_key(struct caam_ctx *ctx, const u8 *key_in,
-                             u32 authkeylen)
-{
-       return gen_split_key(ctx->jrdev, ctx->key, ctx->split_key_len,
-                              ctx->split_key_pad_len, key_in, authkeylen,
-                              ctx->alg_op);
-}
+static u32 gen_split_aead_key(struct caam_ctx *ctx, const u8 *key_in,
+                             u32 authkeylen)
+{
+       return gen_split_key(ctx->jrdev, ctx->key, ctx->split_key_len,
+                              ctx->split_key_pad_len, key_in, authkeylen,
+                              ctx->alg_op);
+}
 
 static int aead_setkey(struct crypto_aead *aead,
                               const u8 *key, unsigned int keylen)
@@ -2100,7 +1733,7 @@ struct aead_edesc {
        int sec4_sg_bytes;
        dma_addr_t sec4_sg_dma;
        struct sec4_sg_entry *sec4_sg;
-       u32 hw_desc[0];
+       u32 hw_desc[];
 };
 
 /*
@@ -2153,6 +1786,16 @@ static void caam_unmap(struct device *dev, struct scatterlist *src,
 static void aead_unmap(struct device *dev,
                       struct aead_edesc *edesc,
                       struct aead_request *req)
+{
+       caam_unmap(dev, req->src, req->dst,
+                  edesc->src_nents, edesc->src_chained, edesc->dst_nents,
+                  edesc->dst_chained, 0, 0,
+                  edesc->sec4_sg_dma, edesc->sec4_sg_bytes);
+}
+
+static void old_aead_unmap(struct device *dev,
+                          struct aead_edesc *edesc,
+                          struct aead_request *req)
 {
        struct crypto_aead *aead = crypto_aead_reqtfm(req);
        int ivsize = crypto_aead_ivsize(aead);
@@ -2184,6 +1827,28 @@ static void aead_encrypt_done(struct device *jrdev, u32 *desc, u32 err,
 {
        struct aead_request *req = context;
        struct aead_edesc *edesc;
+
+#ifdef DEBUG
+       dev_err(jrdev, "%s %d: err 0x%x\n", __func__, __LINE__, err);
+#endif
+
+       edesc = container_of(desc, struct aead_edesc, hw_desc[0]);
+
+       if (err)
+               caam_jr_strstatus(jrdev, err);
+
+       aead_unmap(jrdev, edesc, req);
+
+       kfree(edesc);
+
+       aead_request_complete(req, err);
+}
+
+static void old_aead_encrypt_done(struct device *jrdev, u32 *desc, u32 err,
+                                 void *context)
+{
+       struct aead_request *req = context;
+       struct aead_edesc *edesc;
 #ifdef DEBUG
        struct crypto_aead *aead = crypto_aead_reqtfm(req);
        struct caam_ctx *ctx = crypto_aead_ctx(aead);
@@ -2198,7 +1863,7 @@ static void aead_encrypt_done(struct device *jrdev, u32 *desc, u32 err,
        if (err)
                caam_jr_strstatus(jrdev, err);
 
-       aead_unmap(jrdev, edesc, req);
+       old_aead_unmap(jrdev, edesc, req);
 
 #ifdef DEBUG
        print_hex_dump(KERN_ERR, "assoc  @"__stringify(__LINE__)": ",
@@ -2223,6 +1888,34 @@ static void aead_decrypt_done(struct device *jrdev, u32 *desc, u32 err,
 {
        struct aead_request *req = context;
        struct aead_edesc *edesc;
+
+#ifdef DEBUG
+       dev_err(jrdev, "%s %d: err 0x%x\n", __func__, __LINE__, err);
+#endif
+
+       edesc = container_of(desc, struct aead_edesc, hw_desc[0]);
+
+       if (err)
+               caam_jr_strstatus(jrdev, err);
+
+       aead_unmap(jrdev, edesc, req);
+
+       /*
+        * verify hw auth check passed else return -EBADMSG
+        */
+       if ((err & JRSTA_CCBERR_ERRID_MASK) == JRSTA_CCBERR_ERRID_ICVCHK)
+               err = -EBADMSG;
+
+       kfree(edesc);
+
+       aead_request_complete(req, err);
+}
+
+static void old_aead_decrypt_done(struct device *jrdev, u32 *desc, u32 err,
+                                 void *context)
+{
+       struct aead_request *req = context;
+       struct aead_edesc *edesc;
 #ifdef DEBUG
        struct crypto_aead *aead = crypto_aead_reqtfm(req);
        struct caam_ctx *ctx = crypto_aead_ctx(aead);
@@ -2246,7 +1939,7 @@ static void aead_decrypt_done(struct device *jrdev, u32 *desc, u32 err,
        if (err)
                caam_jr_strstatus(jrdev, err);
 
-       aead_unmap(jrdev, edesc, req);
+       old_aead_unmap(jrdev, edesc, req);
 
        /*
         * verify hw auth check passed else return -EBADMSG
@@ -2342,10 +2035,10 @@ static void ablkcipher_decrypt_done(struct device *jrdev, u32 *desc, u32 err,
 /*
  * Fill in aead job descriptor
  */
-static void init_aead_job(u32 *sh_desc, dma_addr_t ptr,
-                         struct aead_edesc *edesc,
-                         struct aead_request *req,
-                         bool all_contig, bool encrypt)
+static void old_init_aead_job(u32 *sh_desc, dma_addr_t ptr,
+                             struct aead_edesc *edesc,
+                             struct aead_request *req,
+                             bool all_contig, bool encrypt)
 {
        struct crypto_aead *aead = crypto_aead_reqtfm(req);
        struct caam_ctx *ctx = crypto_aead_ctx(aead);
@@ -2424,6 +2117,97 @@ static void init_aead_job(u32 *sh_desc, dma_addr_t ptr,
                                   out_options);
 }
 
+/*
+ * Fill in aead job descriptor
+ */
+static void init_aead_job(struct aead_request *req,
+                         struct aead_edesc *edesc,
+                         bool all_contig, bool encrypt)
+{
+       struct crypto_aead *aead = crypto_aead_reqtfm(req);
+       struct caam_ctx *ctx = crypto_aead_ctx(aead);
+       int authsize = ctx->authsize;
+       u32 *desc = edesc->hw_desc;
+       u32 out_options, in_options;
+       dma_addr_t dst_dma, src_dma;
+       int len, sec4_sg_index = 0;
+       dma_addr_t ptr;
+       u32 *sh_desc;
+
+       sh_desc = encrypt ? ctx->sh_desc_enc : ctx->sh_desc_dec;
+       ptr = encrypt ? ctx->sh_desc_enc_dma : ctx->sh_desc_dec_dma;
+
+       len = desc_len(sh_desc);
+       init_job_desc_shared(desc, ptr, len, HDR_SHARE_DEFER | HDR_REVERSE);
+
+       if (all_contig) {
+               src_dma = sg_dma_address(req->src);
+               in_options = 0;
+       } else {
+               src_dma = edesc->sec4_sg_dma;
+               sec4_sg_index += edesc->src_nents;
+               in_options = LDST_SGF;
+       }
+
+       append_seq_in_ptr(desc, src_dma, req->assoclen + req->cryptlen,
+                         in_options);
+
+       dst_dma = src_dma;
+       out_options = in_options;
+
+       if (unlikely(req->src != req->dst)) {
+               if (!edesc->dst_nents) {
+                       dst_dma = sg_dma_address(req->dst);
+               } else {
+                       dst_dma = edesc->sec4_sg_dma +
+                                 sec4_sg_index *
+                                 sizeof(struct sec4_sg_entry);
+                       out_options = LDST_SGF;
+               }
+       }
+
+       if (encrypt)
+               append_seq_out_ptr(desc, dst_dma,
+                                  req->assoclen + req->cryptlen + authsize,
+                                  out_options);
+       else
+               append_seq_out_ptr(desc, dst_dma,
+                                  req->assoclen + req->cryptlen - authsize,
+                                  out_options);
+
+       /* REG3 = assoclen */
+       append_math_add_imm_u32(desc, REG3, ZERO, IMM, req->assoclen);
+}
+
+static void init_gcm_job(struct aead_request *req,
+                        struct aead_edesc *edesc,
+                        bool all_contig, bool encrypt)
+{
+       struct crypto_aead *aead = crypto_aead_reqtfm(req);
+       struct caam_ctx *ctx = crypto_aead_ctx(aead);
+       unsigned int ivsize = crypto_aead_ivsize(aead);
+       u32 *desc = edesc->hw_desc;
+       bool generic_gcm = (ivsize == 12);
+       unsigned int last;
+
+       init_aead_job(req, edesc, all_contig, encrypt);
+
+       /* BUG This should not be specific to generic GCM. */
+       last = 0;
+       if (encrypt && generic_gcm && !(req->assoclen + req->cryptlen))
+               last = FIFOLD_TYPE_LAST1;
+
+       /* Read GCM IV */
+       append_cmd(desc, CMD_FIFO_LOAD | FIFOLD_CLASS_CLASS1 | IMMEDIATE |
+                        FIFOLD_TYPE_IV | FIFOLD_TYPE_FLUSH1 | 12 | last);
+       /* Append Salt */
+       if (!generic_gcm)
+               append_data(desc, ctx->key + ctx->enckeylen, 4);
+       /* Append IV */
+       append_data(desc, req->iv, ivsize);
+       /* End of blank commands */
+}
+
 /*
  * Fill in aead givencrypt job descriptor
  */
@@ -2608,9 +2392,10 @@ static void init_ablkcipher_giv_job(u32 *sh_desc, dma_addr_t ptr,
 /*
  * allocate and map the aead extended descriptor
  */
-static struct aead_edesc *aead_edesc_alloc(struct aead_request *req,
-                                          int desc_bytes, bool *all_contig_ptr,
-                                          bool encrypt)
+static struct aead_edesc *old_aead_edesc_alloc(struct aead_request *req,
+                                              int desc_bytes,
+                                              bool *all_contig_ptr,
+                                              bool encrypt)
 {
        struct crypto_aead *aead = crypto_aead_reqtfm(req);
        struct caam_ctx *ctx = crypto_aead_ctx(aead);
@@ -2655,35 +2440,138 @@ static struct aead_edesc *aead_edesc_alloc(struct aead_request *req,
                                         DMA_FROM_DEVICE, dst_chained);
        }
 
-       iv_dma = dma_map_single(jrdev, req->iv, ivsize, DMA_TO_DEVICE);
-       if (dma_mapping_error(jrdev, iv_dma)) {
-               dev_err(jrdev, "unable to map IV\n");
-               return ERR_PTR(-ENOMEM);
+       iv_dma = dma_map_single(jrdev, req->iv, ivsize, DMA_TO_DEVICE);
+       if (dma_mapping_error(jrdev, iv_dma)) {
+               dev_err(jrdev, "unable to map IV\n");
+               return ERR_PTR(-ENOMEM);
+       }
+
+       if (((ctx->class1_alg_type & OP_ALG_ALGSEL_MASK) ==
+             OP_ALG_ALGSEL_AES) &&
+           ((ctx->class1_alg_type & OP_ALG_AAI_MASK) == OP_ALG_AAI_GCM))
+               is_gcm = true;
+
+       /*
+        * Check if data are contiguous.
+        * GCM expected input sequence: IV, AAD, text
+        * All other - expected input sequence: AAD, IV, text
+        */
+       if (is_gcm)
+               all_contig = (!assoc_nents &&
+                             iv_dma + ivsize == sg_dma_address(req->assoc) &&
+                             !src_nents && sg_dma_address(req->assoc) +
+                             req->assoclen == sg_dma_address(req->src));
+       else
+               all_contig = (!assoc_nents && sg_dma_address(req->assoc) +
+                             req->assoclen == iv_dma && !src_nents &&
+                             iv_dma + ivsize == sg_dma_address(req->src));
+       if (!all_contig) {
+               assoc_nents = assoc_nents ? : 1;
+               src_nents = src_nents ? : 1;
+               sec4_sg_len = assoc_nents + 1 + src_nents;
+       }
+
+       sec4_sg_len += dst_nents;
+
+       sec4_sg_bytes = sec4_sg_len * sizeof(struct sec4_sg_entry);
+
+       /* allocate space for base edesc and hw desc commands, link tables */
+       edesc = kmalloc(sizeof(struct aead_edesc) + desc_bytes +
+                       sec4_sg_bytes, GFP_DMA | flags);
+       if (!edesc) {
+               dev_err(jrdev, "could not allocate extended descriptor\n");
+               return ERR_PTR(-ENOMEM);
+       }
+
+       edesc->assoc_nents = assoc_nents;
+       edesc->assoc_chained = assoc_chained;
+       edesc->src_nents = src_nents;
+       edesc->src_chained = src_chained;
+       edesc->dst_nents = dst_nents;
+       edesc->dst_chained = dst_chained;
+       edesc->iv_dma = iv_dma;
+       edesc->sec4_sg_bytes = sec4_sg_bytes;
+       edesc->sec4_sg = (void *)edesc + sizeof(struct aead_edesc) +
+                        desc_bytes;
+       *all_contig_ptr = all_contig;
+
+       sec4_sg_index = 0;
+       if (!all_contig) {
+               if (!is_gcm) {
+                       sg_to_sec4_sg_len(req->assoc, req->assoclen,
+                                         edesc->sec4_sg + sec4_sg_index);
+                       sec4_sg_index += assoc_nents;
+               }
+
+               dma_to_sec4_sg_one(edesc->sec4_sg + sec4_sg_index,
+                                  iv_dma, ivsize, 0);
+               sec4_sg_index += 1;
+
+               if (is_gcm) {
+                       sg_to_sec4_sg_len(req->assoc, req->assoclen,
+                                         edesc->sec4_sg + sec4_sg_index);
+                       sec4_sg_index += assoc_nents;
+               }
+
+               sg_to_sec4_sg_last(req->src,
+                                  src_nents,
+                                  edesc->sec4_sg +
+                                  sec4_sg_index, 0);
+               sec4_sg_index += src_nents;
+       }
+       if (dst_nents) {
+               sg_to_sec4_sg_last(req->dst, dst_nents,
+                                  edesc->sec4_sg + sec4_sg_index, 0);
+       }
+       edesc->sec4_sg_dma = dma_map_single(jrdev, edesc->sec4_sg,
+                                           sec4_sg_bytes, DMA_TO_DEVICE);
+       if (dma_mapping_error(jrdev, edesc->sec4_sg_dma)) {
+               dev_err(jrdev, "unable to map S/G table\n");
+               return ERR_PTR(-ENOMEM);
+       }
+
+       return edesc;
+}
+
+/*
+ * allocate and map the aead extended descriptor
+ */
+static struct aead_edesc *aead_edesc_alloc(struct aead_request *req,
+                                          int desc_bytes, bool *all_contig_ptr,
+                                          bool encrypt)
+{
+       struct crypto_aead *aead = crypto_aead_reqtfm(req);
+       struct caam_ctx *ctx = crypto_aead_ctx(aead);
+       struct device *jrdev = ctx->jrdev;
+       gfp_t flags = (req->base.flags & (CRYPTO_TFM_REQ_MAY_BACKLOG |
+                      CRYPTO_TFM_REQ_MAY_SLEEP)) ? GFP_KERNEL : GFP_ATOMIC;
+       int src_nents, dst_nents = 0;
+       struct aead_edesc *edesc;
+       int sgc;
+       bool all_contig = true;
+       bool src_chained = false, dst_chained = false;
+       int sec4_sg_index, sec4_sg_len = 0, sec4_sg_bytes;
+       unsigned int authsize = ctx->authsize;
+
+       if (unlikely(req->dst != req->src)) {
+               src_nents = sg_count(req->src, req->assoclen + req->cryptlen,
+                                    &src_chained);
+               dst_nents = sg_count(req->dst,
+                                    req->assoclen + req->cryptlen +
+                                       (encrypt ? authsize : (-authsize)),
+                                    &dst_chained);
+       } else {
+               src_nents = sg_count(req->src,
+                                    req->assoclen + req->cryptlen +
+                                       (encrypt ? authsize : 0),
+                                    &src_chained);
        }
 
-       if (((ctx->class1_alg_type & OP_ALG_ALGSEL_MASK) ==
-             OP_ALG_ALGSEL_AES) &&
-           ((ctx->class1_alg_type & OP_ALG_AAI_MASK) == OP_ALG_AAI_GCM))
-               is_gcm = true;
-
-       /*
-        * Check if data are contiguous.
-        * GCM expected input sequence: IV, AAD, text
-        * All other - expected input sequence: AAD, IV, text
-        */
-       if (is_gcm)
-               all_contig = (!assoc_nents &&
-                             iv_dma + ivsize == sg_dma_address(req->assoc) &&
-                             !src_nents && sg_dma_address(req->assoc) +
-                             req->assoclen == sg_dma_address(req->src));
-       else
-               all_contig = (!assoc_nents && sg_dma_address(req->assoc) +
-                             req->assoclen == iv_dma && !src_nents &&
-                             iv_dma + ivsize == sg_dma_address(req->src));
+       /* Check if data are contiguous. */
+       all_contig = !src_nents;
        if (!all_contig) {
-               assoc_nents = assoc_nents ? : 1;
                src_nents = src_nents ? : 1;
-               sec4_sg_len = assoc_nents + 1 + src_nents;
+               sec4_sg_len = src_nents;
        }
 
        sec4_sg_len += dst_nents;
@@ -2691,68 +2579,78 @@ static struct aead_edesc *aead_edesc_alloc(struct aead_request *req,
        sec4_sg_bytes = sec4_sg_len * sizeof(struct sec4_sg_entry);
 
        /* allocate space for base edesc and hw desc commands, link tables */
-       edesc = kmalloc(sizeof(struct aead_edesc) + desc_bytes +
+       edesc = kzalloc(sizeof(struct aead_edesc) + desc_bytes +
                        sec4_sg_bytes, GFP_DMA | flags);
        if (!edesc) {
                dev_err(jrdev, "could not allocate extended descriptor\n");
                return ERR_PTR(-ENOMEM);
        }
 
-       edesc->assoc_nents = assoc_nents;
-       edesc->assoc_chained = assoc_chained;
+       if (likely(req->src == req->dst)) {
+               sgc = dma_map_sg_chained(jrdev, req->src, src_nents ? : 1,
+                                        DMA_BIDIRECTIONAL, src_chained);
+               if (unlikely(!sgc)) {
+                       dev_err(jrdev, "unable to map source\n");
+                       kfree(edesc);
+                       return ERR_PTR(-ENOMEM);
+               }
+       } else {
+               sgc = dma_map_sg_chained(jrdev, req->src, src_nents ? : 1,
+                                        DMA_TO_DEVICE, src_chained);
+               if (unlikely(!sgc)) {
+                       dev_err(jrdev, "unable to map source\n");
+                       kfree(edesc);
+                       return ERR_PTR(-ENOMEM);
+               }
+
+               sgc = dma_map_sg_chained(jrdev, req->dst, dst_nents ? : 1,
+                                        DMA_FROM_DEVICE, dst_chained);
+               if (unlikely(!sgc)) {
+                       dev_err(jrdev, "unable to map destination\n");
+                       dma_unmap_sg_chained(jrdev, req->src, src_nents ? : 1,
+                                            DMA_TO_DEVICE, src_chained);
+                       kfree(edesc);
+                       return ERR_PTR(-ENOMEM);
+               }
+       }
+
        edesc->src_nents = src_nents;
        edesc->src_chained = src_chained;
        edesc->dst_nents = dst_nents;
        edesc->dst_chained = dst_chained;
-       edesc->iv_dma = iv_dma;
-       edesc->sec4_sg_bytes = sec4_sg_bytes;
        edesc->sec4_sg = (void *)edesc + sizeof(struct aead_edesc) +
                         desc_bytes;
        *all_contig_ptr = all_contig;
 
        sec4_sg_index = 0;
        if (!all_contig) {
-               if (!is_gcm) {
-                       sg_to_sec4_sg(req->assoc,
-                                     assoc_nents,
-                                     edesc->sec4_sg +
-                                     sec4_sg_index, 0);
-                       sec4_sg_index += assoc_nents;
-               }
-
-               dma_to_sec4_sg_one(edesc->sec4_sg + sec4_sg_index,
-                                  iv_dma, ivsize, 0);
-               sec4_sg_index += 1;
-
-               if (is_gcm) {
-                       sg_to_sec4_sg(req->assoc,
-                                     assoc_nents,
-                                     edesc->sec4_sg +
-                                     sec4_sg_index, 0);
-                       sec4_sg_index += assoc_nents;
-               }
-
-               sg_to_sec4_sg_last(req->src,
-                                  src_nents,
-                                  edesc->sec4_sg +
-                                  sec4_sg_index, 0);
+               sg_to_sec4_sg_last(req->src, src_nents,
+                             edesc->sec4_sg + sec4_sg_index, 0);
                sec4_sg_index += src_nents;
        }
        if (dst_nents) {
                sg_to_sec4_sg_last(req->dst, dst_nents,
                                   edesc->sec4_sg + sec4_sg_index, 0);
        }
+
+       if (!sec4_sg_bytes)
+               return edesc;
+
        edesc->sec4_sg_dma = dma_map_single(jrdev, edesc->sec4_sg,
                                            sec4_sg_bytes, DMA_TO_DEVICE);
        if (dma_mapping_error(jrdev, edesc->sec4_sg_dma)) {
                dev_err(jrdev, "unable to map S/G table\n");
+               aead_unmap(jrdev, edesc, req);
+               kfree(edesc);
                return ERR_PTR(-ENOMEM);
        }
 
+       edesc->sec4_sg_bytes = sec4_sg_bytes;
+
        return edesc;
 }
 
-static int aead_encrypt(struct aead_request *req)
+static int gcm_encrypt(struct aead_request *req)
 {
        struct aead_edesc *edesc;
        struct crypto_aead *aead = crypto_aead_reqtfm(req);
@@ -2763,14 +2661,12 @@ static int aead_encrypt(struct aead_request *req)
        int ret = 0;
 
        /* allocate extended descriptor */
-       edesc = aead_edesc_alloc(req, DESC_JOB_IO_LEN *
-                                CAAM_CMD_SZ, &all_contig, true);
+       edesc = aead_edesc_alloc(req, GCM_DESC_JOB_IO_LEN, &all_contig, true);
        if (IS_ERR(edesc))
                return PTR_ERR(edesc);
 
        /* Create and submit job descriptor */
-       init_aead_job(ctx->sh_desc_enc, ctx->sh_desc_enc_dma, edesc, req,
-                     all_contig, true);
+       init_gcm_job(req, edesc, all_contig, true);
 #ifdef DEBUG
        print_hex_dump(KERN_ERR, "aead jobdesc@"__stringify(__LINE__)": ",
                       DUMP_PREFIX_ADDRESS, 16, 4, edesc->hw_desc,
@@ -2789,7 +2685,79 @@ static int aead_encrypt(struct aead_request *req)
        return ret;
 }
 
-static int aead_decrypt(struct aead_request *req)
+static int old_aead_encrypt(struct aead_request *req)
+{
+       struct aead_edesc *edesc;
+       struct crypto_aead *aead = crypto_aead_reqtfm(req);
+       struct caam_ctx *ctx = crypto_aead_ctx(aead);
+       struct device *jrdev = ctx->jrdev;
+       bool all_contig;
+       u32 *desc;
+       int ret = 0;
+
+       /* allocate extended descriptor */
+       edesc = old_aead_edesc_alloc(req, DESC_JOB_IO_LEN *
+                                    CAAM_CMD_SZ, &all_contig, true);
+       if (IS_ERR(edesc))
+               return PTR_ERR(edesc);
+
+       /* Create and submit job descriptor */
+       old_init_aead_job(ctx->sh_desc_enc, ctx->sh_desc_enc_dma, edesc, req,
+                         all_contig, true);
+#ifdef DEBUG
+       print_hex_dump(KERN_ERR, "aead jobdesc@"__stringify(__LINE__)": ",
+                      DUMP_PREFIX_ADDRESS, 16, 4, edesc->hw_desc,
+                      desc_bytes(edesc->hw_desc), 1);
+#endif
+
+       desc = edesc->hw_desc;
+       ret = caam_jr_enqueue(jrdev, desc, old_aead_encrypt_done, req);
+       if (!ret) {
+               ret = -EINPROGRESS;
+       } else {
+               old_aead_unmap(jrdev, edesc, req);
+               kfree(edesc);
+       }
+
+       return ret;
+}
+
+static int gcm_decrypt(struct aead_request *req)
+{
+       struct aead_edesc *edesc;
+       struct crypto_aead *aead = crypto_aead_reqtfm(req);
+       struct caam_ctx *ctx = crypto_aead_ctx(aead);
+       struct device *jrdev = ctx->jrdev;
+       bool all_contig;
+       u32 *desc;
+       int ret = 0;
+
+       /* allocate extended descriptor */
+       edesc = aead_edesc_alloc(req, GCM_DESC_JOB_IO_LEN, &all_contig, false);
+       if (IS_ERR(edesc))
+               return PTR_ERR(edesc);
+
+       /* Create and submit job descriptor*/
+       init_gcm_job(req, edesc, all_contig, false);
+#ifdef DEBUG
+       print_hex_dump(KERN_ERR, "aead jobdesc@"__stringify(__LINE__)": ",
+                      DUMP_PREFIX_ADDRESS, 16, 4, edesc->hw_desc,
+                      desc_bytes(edesc->hw_desc), 1);
+#endif
+
+       desc = edesc->hw_desc;
+       ret = caam_jr_enqueue(jrdev, desc, aead_decrypt_done, req);
+       if (!ret) {
+               ret = -EINPROGRESS;
+       } else {
+               aead_unmap(jrdev, edesc, req);
+               kfree(edesc);
+       }
+
+       return ret;
+}
+
+static int old_aead_decrypt(struct aead_request *req)
 {
        struct aead_edesc *edesc;
        struct crypto_aead *aead = crypto_aead_reqtfm(req);
@@ -2800,8 +2768,8 @@ static int aead_decrypt(struct aead_request *req)
        int ret = 0;
 
        /* allocate extended descriptor */
-       edesc = aead_edesc_alloc(req, DESC_JOB_IO_LEN *
-                                CAAM_CMD_SZ, &all_contig, false);
+       edesc = old_aead_edesc_alloc(req, DESC_JOB_IO_LEN *
+                                    CAAM_CMD_SZ, &all_contig, false);
        if (IS_ERR(edesc))
                return PTR_ERR(edesc);
 
@@ -2812,8 +2780,8 @@ static int aead_decrypt(struct aead_request *req)
 #endif
 
        /* Create and submit job descriptor*/
-       init_aead_job(ctx->sh_desc_dec,
-                     ctx->sh_desc_dec_dma, edesc, req, all_contig, false);
+       old_init_aead_job(ctx->sh_desc_dec,
+                         ctx->sh_desc_dec_dma, edesc, req, all_contig, false);
 #ifdef DEBUG
        print_hex_dump(KERN_ERR, "aead jobdesc@"__stringify(__LINE__)": ",
                       DUMP_PREFIX_ADDRESS, 16, 4, edesc->hw_desc,
@@ -2821,11 +2789,11 @@ static int aead_decrypt(struct aead_request *req)
 #endif
 
        desc = edesc->hw_desc;
-       ret = caam_jr_enqueue(jrdev, desc, aead_decrypt_done, req);
+       ret = caam_jr_enqueue(jrdev, desc, old_aead_decrypt_done, req);
        if (!ret) {
                ret = -EINPROGRESS;
        } else {
-               aead_unmap(jrdev, edesc, req);
+               old_aead_unmap(jrdev, edesc, req);
                kfree(edesc);
        }
 
@@ -2953,8 +2921,8 @@ static struct aead_edesc *aead_giv_edesc_alloc(struct aead_givcrypt_request
        sec4_sg_index = 0;
        if (!(contig & GIV_SRC_CONTIG)) {
                if (!is_gcm) {
-                       sg_to_sec4_sg(req->assoc, assoc_nents,
-                                     edesc->sec4_sg + sec4_sg_index, 0);
+                       sg_to_sec4_sg_len(req->assoc, req->assoclen,
+                                         edesc->sec4_sg + sec4_sg_index);
                        sec4_sg_index += assoc_nents;
                }
 
@@ -2963,8 +2931,8 @@ static struct aead_edesc *aead_giv_edesc_alloc(struct aead_givcrypt_request
                sec4_sg_index += 1;
 
                if (is_gcm) {
-                       sg_to_sec4_sg(req->assoc, assoc_nents,
-                                     edesc->sec4_sg + sec4_sg_index, 0);
+                       sg_to_sec4_sg_len(req->assoc, req->assoclen,
+                                         edesc->sec4_sg + sec4_sg_index);
                        sec4_sg_index += assoc_nents;
                }
 
@@ -2999,7 +2967,7 @@ static struct aead_edesc *aead_giv_edesc_alloc(struct aead_givcrypt_request
        return edesc;
 }
 
-static int aead_givencrypt(struct aead_givcrypt_request *areq)
+static int old_aead_givencrypt(struct aead_givcrypt_request *areq)
 {
        struct aead_request *req = &areq->areq;
        struct aead_edesc *edesc;
@@ -3033,11 +3001,11 @@ static int aead_givencrypt(struct aead_givcrypt_request *areq)
 #endif
 
        desc = edesc->hw_desc;
-       ret = caam_jr_enqueue(jrdev, desc, aead_encrypt_done, req);
+       ret = caam_jr_enqueue(jrdev, desc, old_aead_encrypt_done, req);
        if (!ret) {
                ret = -EINPROGRESS;
        } else {
-               aead_unmap(jrdev, edesc, req);
+               old_aead_unmap(jrdev, edesc, req);
                kfree(edesc);
        }
 
@@ -3046,7 +3014,7 @@ static int aead_givencrypt(struct aead_givcrypt_request *areq)
 
 static int aead_null_givencrypt(struct aead_givcrypt_request *areq)
 {
-       return aead_encrypt(&areq->areq);
+       return old_aead_encrypt(&areq->areq);
 }
 
 /*
@@ -3379,11 +3347,7 @@ struct caam_alg_template {
        u32 type;
        union {
                struct ablkcipher_alg ablkcipher;
-               struct aead_alg aead;
-               struct blkcipher_alg blkcipher;
-               struct cipher_alg cipher;
-               struct compress_alg compress;
-               struct rng_alg rng;
+               struct old_aead_alg aead;
        } template_u;
        u32 class1_alg_type;
        u32 class2_alg_type;
@@ -3400,8 +3364,8 @@ static struct caam_alg_template driver_algs[] = {
                .template_aead = {
                        .setkey = aead_setkey,
                        .setauthsize = aead_setauthsize,
-                       .encrypt = aead_encrypt,
-                       .decrypt = aead_decrypt,
+                       .encrypt = old_aead_encrypt,
+                       .decrypt = old_aead_decrypt,
                        .givencrypt = aead_null_givencrypt,
                        .geniv = "<built-in>",
                        .ivsize = NULL_IV_SIZE,
@@ -3419,8 +3383,8 @@ static struct caam_alg_template driver_algs[] = {
                .template_aead = {
                        .setkey = aead_setkey,
                        .setauthsize = aead_setauthsize,
-                       .encrypt = aead_encrypt,
-                       .decrypt = aead_decrypt,
+                       .encrypt = old_aead_encrypt,
+                       .decrypt = old_aead_decrypt,
                        .givencrypt = aead_null_givencrypt,
                        .geniv = "<built-in>",
                        .ivsize = NULL_IV_SIZE,
@@ -3438,8 +3402,8 @@ static struct caam_alg_template driver_algs[] = {
                .template_aead = {
                        .setkey = aead_setkey,
                        .setauthsize = aead_setauthsize,
-                       .encrypt = aead_encrypt,
-                       .decrypt = aead_decrypt,
+                       .encrypt = old_aead_encrypt,
+                       .decrypt = old_aead_decrypt,
                        .givencrypt = aead_null_givencrypt,
                        .geniv = "<built-in>",
                        .ivsize = NULL_IV_SIZE,
@@ -3458,8 +3422,8 @@ static struct caam_alg_template driver_algs[] = {
                .template_aead = {
                        .setkey = aead_setkey,
                        .setauthsize = aead_setauthsize,
-                       .encrypt = aead_encrypt,
-                       .decrypt = aead_decrypt,
+                       .encrypt = old_aead_encrypt,
+                       .decrypt = old_aead_decrypt,
                        .givencrypt = aead_null_givencrypt,
                        .geniv = "<built-in>",
                        .ivsize = NULL_IV_SIZE,
@@ -3478,8 +3442,8 @@ static struct caam_alg_template driver_algs[] = {
                .template_aead = {
                        .setkey = aead_setkey,
                        .setauthsize = aead_setauthsize,
-                       .encrypt = aead_encrypt,
-                       .decrypt = aead_decrypt,
+                       .encrypt = old_aead_encrypt,
+                       .decrypt = old_aead_decrypt,
                        .givencrypt = aead_null_givencrypt,
                        .geniv = "<built-in>",
                        .ivsize = NULL_IV_SIZE,
@@ -3498,8 +3462,8 @@ static struct caam_alg_template driver_algs[] = {
                .template_aead = {
                        .setkey = aead_setkey,
                        .setauthsize = aead_setauthsize,
-                       .encrypt = aead_encrypt,
-                       .decrypt = aead_decrypt,
+                       .encrypt = old_aead_encrypt,
+                       .decrypt = old_aead_decrypt,
                        .givencrypt = aead_null_givencrypt,
                        .geniv = "<built-in>",
                        .ivsize = NULL_IV_SIZE,
@@ -3518,9 +3482,9 @@ static struct caam_alg_template driver_algs[] = {
                .template_aead = {
                        .setkey = aead_setkey,
                        .setauthsize = aead_setauthsize,
-                       .encrypt = aead_encrypt,
-                       .decrypt = aead_decrypt,
-                       .givencrypt = aead_givencrypt,
+                       .encrypt = old_aead_encrypt,
+                       .decrypt = old_aead_decrypt,
+                       .givencrypt = old_aead_givencrypt,
                        .geniv = "<built-in>",
                        .ivsize = AES_BLOCK_SIZE,
                        .maxauthsize = MD5_DIGEST_SIZE,
@@ -3537,9 +3501,9 @@ static struct caam_alg_template driver_algs[] = {
                .template_aead = {
                        .setkey = aead_setkey,
                        .setauthsize = aead_setauthsize,
-                       .encrypt = aead_encrypt,
-                       .decrypt = aead_decrypt,
-                       .givencrypt = aead_givencrypt,
+                       .encrypt = old_aead_encrypt,
+                       .decrypt = old_aead_decrypt,
+                       .givencrypt = old_aead_givencrypt,
                        .geniv = "<built-in>",
                        .ivsize = AES_BLOCK_SIZE,
                        .maxauthsize = SHA1_DIGEST_SIZE,
@@ -3556,9 +3520,9 @@ static struct caam_alg_template driver_algs[] = {
                .template_aead = {
                        .setkey = aead_setkey,
                        .setauthsize = aead_setauthsize,
-                       .encrypt = aead_encrypt,
-                       .decrypt = aead_decrypt,
-                       .givencrypt = aead_givencrypt,
+                       .encrypt = old_aead_encrypt,
+                       .decrypt = old_aead_decrypt,
+                       .givencrypt = old_aead_givencrypt,
                        .geniv = "<built-in>",
                        .ivsize = AES_BLOCK_SIZE,
                        .maxauthsize = SHA224_DIGEST_SIZE,
@@ -3576,9 +3540,9 @@ static struct caam_alg_template driver_algs[] = {
                .template_aead = {
                        .setkey = aead_setkey,
                        .setauthsize = aead_setauthsize,
-                       .encrypt = aead_encrypt,
-                       .decrypt = aead_decrypt,
-                       .givencrypt = aead_givencrypt,
+                       .encrypt = old_aead_encrypt,
+                       .decrypt = old_aead_decrypt,
+                       .givencrypt = old_aead_givencrypt,
                        .geniv = "<built-in>",
                        .ivsize = AES_BLOCK_SIZE,
                        .maxauthsize = SHA256_DIGEST_SIZE,
@@ -3596,9 +3560,9 @@ static struct caam_alg_template driver_algs[] = {
                .template_aead = {
                        .setkey = aead_setkey,
                        .setauthsize = aead_setauthsize,
-                       .encrypt = aead_encrypt,
-                       .decrypt = aead_decrypt,
-                       .givencrypt = aead_givencrypt,
+                       .encrypt = old_aead_encrypt,
+                       .decrypt = old_aead_decrypt,
+                       .givencrypt = old_aead_givencrypt,
                        .geniv = "<built-in>",
                        .ivsize = AES_BLOCK_SIZE,
                        .maxauthsize = SHA384_DIGEST_SIZE,
@@ -3617,9 +3581,9 @@ static struct caam_alg_template driver_algs[] = {
                .template_aead = {
                        .setkey = aead_setkey,
                        .setauthsize = aead_setauthsize,
-                       .encrypt = aead_encrypt,
-                       .decrypt = aead_decrypt,
-                       .givencrypt = aead_givencrypt,
+                       .encrypt = old_aead_encrypt,
+                       .decrypt = old_aead_decrypt,
+                       .givencrypt = old_aead_givencrypt,
                        .geniv = "<built-in>",
                        .ivsize = AES_BLOCK_SIZE,
                        .maxauthsize = SHA512_DIGEST_SIZE,
@@ -3637,9 +3601,9 @@ static struct caam_alg_template driver_algs[] = {
                .template_aead = {
                        .setkey = aead_setkey,
                        .setauthsize = aead_setauthsize,
-                       .encrypt = aead_encrypt,
-                       .decrypt = aead_decrypt,
-                       .givencrypt = aead_givencrypt,
+                       .encrypt = old_aead_encrypt,
+                       .decrypt = old_aead_decrypt,
+                       .givencrypt = old_aead_givencrypt,
                        .geniv = "<built-in>",
                        .ivsize = DES3_EDE_BLOCK_SIZE,
                        .maxauthsize = MD5_DIGEST_SIZE,
@@ -3656,9 +3620,9 @@ static struct caam_alg_template driver_algs[] = {
                .template_aead = {
                        .setkey = aead_setkey,
                        .setauthsize = aead_setauthsize,
-                       .encrypt = aead_encrypt,
-                       .decrypt = aead_decrypt,
-                       .givencrypt = aead_givencrypt,
+                       .encrypt = old_aead_encrypt,
+                       .decrypt = old_aead_decrypt,
+                       .givencrypt = old_aead_givencrypt,
                        .geniv = "<built-in>",
                        .ivsize = DES3_EDE_BLOCK_SIZE,
                        .maxauthsize = SHA1_DIGEST_SIZE,
@@ -3675,9 +3639,9 @@ static struct caam_alg_template driver_algs[] = {
                .template_aead = {
                        .setkey = aead_setkey,
                        .setauthsize = aead_setauthsize,
-                       .encrypt = aead_encrypt,
-                       .decrypt = aead_decrypt,
-                       .givencrypt = aead_givencrypt,
+                       .encrypt = old_aead_encrypt,
+                       .decrypt = old_aead_decrypt,
+                       .givencrypt = old_aead_givencrypt,
                        .geniv = "<built-in>",
                        .ivsize = DES3_EDE_BLOCK_SIZE,
                        .maxauthsize = SHA224_DIGEST_SIZE,
@@ -3695,9 +3659,9 @@ static struct caam_alg_template driver_algs[] = {
                .template_aead = {
                        .setkey = aead_setkey,
                        .setauthsize = aead_setauthsize,
-                       .encrypt = aead_encrypt,
-                       .decrypt = aead_decrypt,
-                       .givencrypt = aead_givencrypt,
+                       .encrypt = old_aead_encrypt,
+                       .decrypt = old_aead_decrypt,
+                       .givencrypt = old_aead_givencrypt,
                        .geniv = "<built-in>",
                        .ivsize = DES3_EDE_BLOCK_SIZE,
                        .maxauthsize = SHA256_DIGEST_SIZE,
@@ -3715,9 +3679,9 @@ static struct caam_alg_template driver_algs[] = {
                .template_aead = {
                        .setkey = aead_setkey,
                        .setauthsize = aead_setauthsize,
-                       .encrypt = aead_encrypt,
-                       .decrypt = aead_decrypt,
-                       .givencrypt = aead_givencrypt,
+                       .encrypt = old_aead_encrypt,
+                       .decrypt = old_aead_decrypt,
+                       .givencrypt = old_aead_givencrypt,
                        .geniv = "<built-in>",
                        .ivsize = DES3_EDE_BLOCK_SIZE,
                        .maxauthsize = SHA384_DIGEST_SIZE,
@@ -3735,9 +3699,9 @@ static struct caam_alg_template driver_algs[] = {
                .template_aead = {
                        .setkey = aead_setkey,
                        .setauthsize = aead_setauthsize,
-                       .encrypt = aead_encrypt,
-                       .decrypt = aead_decrypt,
-                       .givencrypt = aead_givencrypt,
+                       .encrypt = old_aead_encrypt,
+                       .decrypt = old_aead_decrypt,
+                       .givencrypt = old_aead_givencrypt,
                        .geniv = "<built-in>",
                        .ivsize = DES3_EDE_BLOCK_SIZE,
                        .maxauthsize = SHA512_DIGEST_SIZE,
@@ -3755,9 +3719,9 @@ static struct caam_alg_template driver_algs[] = {
                .template_aead = {
                        .setkey = aead_setkey,
                        .setauthsize = aead_setauthsize,
-                       .encrypt = aead_encrypt,
-                       .decrypt = aead_decrypt,
-                       .givencrypt = aead_givencrypt,
+                       .encrypt = old_aead_encrypt,
+                       .decrypt = old_aead_decrypt,
+                       .givencrypt = old_aead_givencrypt,
                        .geniv = "<built-in>",
                        .ivsize = DES_BLOCK_SIZE,
                        .maxauthsize = MD5_DIGEST_SIZE,
@@ -3774,9 +3738,9 @@ static struct caam_alg_template driver_algs[] = {
                .template_aead = {
                        .setkey = aead_setkey,
                        .setauthsize = aead_setauthsize,
-                       .encrypt = aead_encrypt,
-                       .decrypt = aead_decrypt,
-                       .givencrypt = aead_givencrypt,
+                       .encrypt = old_aead_encrypt,
+                       .decrypt = old_aead_decrypt,
+                       .givencrypt = old_aead_givencrypt,
                        .geniv = "<built-in>",
                        .ivsize = DES_BLOCK_SIZE,
                        .maxauthsize = SHA1_DIGEST_SIZE,
@@ -3793,9 +3757,9 @@ static struct caam_alg_template driver_algs[] = {
                .template_aead = {
                        .setkey = aead_setkey,
                        .setauthsize = aead_setauthsize,
-                       .encrypt = aead_encrypt,
-                       .decrypt = aead_decrypt,
-                       .givencrypt = aead_givencrypt,
+                       .encrypt = old_aead_encrypt,
+                       .decrypt = old_aead_decrypt,
+                       .givencrypt = old_aead_givencrypt,
                        .geniv = "<built-in>",
                        .ivsize = DES_BLOCK_SIZE,
                        .maxauthsize = SHA224_DIGEST_SIZE,
@@ -3813,9 +3777,9 @@ static struct caam_alg_template driver_algs[] = {
                .template_aead = {
                        .setkey = aead_setkey,
                        .setauthsize = aead_setauthsize,
-                       .encrypt = aead_encrypt,
-                       .decrypt = aead_decrypt,
-                       .givencrypt = aead_givencrypt,
+                       .encrypt = old_aead_encrypt,
+                       .decrypt = old_aead_decrypt,
+                       .givencrypt = old_aead_givencrypt,
                        .geniv = "<built-in>",
                        .ivsize = DES_BLOCK_SIZE,
                        .maxauthsize = SHA256_DIGEST_SIZE,
@@ -3833,9 +3797,9 @@ static struct caam_alg_template driver_algs[] = {
                .template_aead = {
                        .setkey = aead_setkey,
                        .setauthsize = aead_setauthsize,
-                       .encrypt = aead_encrypt,
-                       .decrypt = aead_decrypt,
-                       .givencrypt = aead_givencrypt,
+                       .encrypt = old_aead_encrypt,
+                       .decrypt = old_aead_decrypt,
+                       .givencrypt = old_aead_givencrypt,
                        .geniv = "<built-in>",
                        .ivsize = DES_BLOCK_SIZE,
                        .maxauthsize = SHA384_DIGEST_SIZE,
@@ -3853,9 +3817,9 @@ static struct caam_alg_template driver_algs[] = {
                .template_aead = {
                        .setkey = aead_setkey,
                        .setauthsize = aead_setauthsize,
-                       .encrypt = aead_encrypt,
-                       .decrypt = aead_decrypt,
-                       .givencrypt = aead_givencrypt,
+                       .encrypt = old_aead_encrypt,
+                       .decrypt = old_aead_decrypt,
+                       .givencrypt = old_aead_givencrypt,
                        .geniv = "<built-in>",
                        .ivsize = DES_BLOCK_SIZE,
                        .maxauthsize = SHA512_DIGEST_SIZE,
@@ -3873,9 +3837,9 @@ static struct caam_alg_template driver_algs[] = {
                .template_aead = {
                        .setkey = aead_setkey,
                        .setauthsize = aead_setauthsize,
-                       .encrypt = aead_encrypt,
-                       .decrypt = aead_decrypt,
-                       .givencrypt = aead_givencrypt,
+                       .encrypt = old_aead_encrypt,
+                       .decrypt = old_aead_decrypt,
+                       .givencrypt = old_aead_givencrypt,
                        .geniv = "<built-in>",
                        .ivsize = CTR_RFC3686_IV_SIZE,
                        .maxauthsize = MD5_DIGEST_SIZE,
@@ -3892,9 +3856,9 @@ static struct caam_alg_template driver_algs[] = {
                .template_aead = {
                        .setkey = aead_setkey,
                        .setauthsize = aead_setauthsize,
-                       .encrypt = aead_encrypt,
-                       .decrypt = aead_decrypt,
-                       .givencrypt = aead_givencrypt,
+                       .encrypt = old_aead_encrypt,
+                       .decrypt = old_aead_decrypt,
+                       .givencrypt = old_aead_givencrypt,
                        .geniv = "<built-in>",
                        .ivsize = CTR_RFC3686_IV_SIZE,
                        .maxauthsize = SHA1_DIGEST_SIZE,
@@ -3911,9 +3875,9 @@ static struct caam_alg_template driver_algs[] = {
                .template_aead = {
                        .setkey = aead_setkey,
                        .setauthsize = aead_setauthsize,
-                       .encrypt = aead_encrypt,
-                       .decrypt = aead_decrypt,
-                       .givencrypt = aead_givencrypt,
+                       .encrypt = old_aead_encrypt,
+                       .decrypt = old_aead_decrypt,
+                       .givencrypt = old_aead_givencrypt,
                        .geniv = "<built-in>",
                        .ivsize = CTR_RFC3686_IV_SIZE,
                        .maxauthsize = SHA224_DIGEST_SIZE,
@@ -3931,9 +3895,9 @@ static struct caam_alg_template driver_algs[] = {
                .template_aead = {
                        .setkey = aead_setkey,
                        .setauthsize = aead_setauthsize,
-                       .encrypt = aead_encrypt,
-                       .decrypt = aead_decrypt,
-                       .givencrypt = aead_givencrypt,
+                       .encrypt = old_aead_encrypt,
+                       .decrypt = old_aead_decrypt,
+                       .givencrypt = old_aead_givencrypt,
                        .geniv = "<built-in>",
                        .ivsize = CTR_RFC3686_IV_SIZE,
                        .maxauthsize = SHA256_DIGEST_SIZE,
@@ -3951,9 +3915,9 @@ static struct caam_alg_template driver_algs[] = {
                .template_aead = {
                        .setkey = aead_setkey,
                        .setauthsize = aead_setauthsize,
-                       .encrypt = aead_encrypt,
-                       .decrypt = aead_decrypt,
-                       .givencrypt = aead_givencrypt,
+                       .encrypt = old_aead_encrypt,
+                       .decrypt = old_aead_decrypt,
+                       .givencrypt = old_aead_givencrypt,
                        .geniv = "<built-in>",
                        .ivsize = CTR_RFC3686_IV_SIZE,
                        .maxauthsize = SHA384_DIGEST_SIZE,
@@ -3971,9 +3935,9 @@ static struct caam_alg_template driver_algs[] = {
                .template_aead = {
                        .setkey = aead_setkey,
                        .setauthsize = aead_setauthsize,
-                       .encrypt = aead_encrypt,
-                       .decrypt = aead_decrypt,
-                       .givencrypt = aead_givencrypt,
+                       .encrypt = old_aead_encrypt,
+                       .decrypt = old_aead_decrypt,
+                       .givencrypt = old_aead_givencrypt,
                        .geniv = "<built-in>",
                        .ivsize = CTR_RFC3686_IV_SIZE,
                        .maxauthsize = SHA512_DIGEST_SIZE,
@@ -3983,58 +3947,6 @@ static struct caam_alg_template driver_algs[] = {
                                   OP_ALG_AAI_HMAC_PRECOMP,
                .alg_op = OP_ALG_ALGSEL_SHA512 | OP_ALG_AAI_HMAC,
        },
-       {
-               .name = "rfc4106(gcm(aes))",
-               .driver_name = "rfc4106-gcm-aes-caam",
-               .blocksize = 1,
-               .type = CRYPTO_ALG_TYPE_AEAD,
-               .template_aead = {
-                       .setkey = rfc4106_setkey,
-                       .setauthsize = rfc4106_setauthsize,
-                       .encrypt = aead_encrypt,
-                       .decrypt = aead_decrypt,
-                       .givencrypt = aead_givencrypt,
-                       .geniv = "<built-in>",
-                       .ivsize = 8,
-                       .maxauthsize = AES_BLOCK_SIZE,
-                       },
-               .class1_alg_type = OP_ALG_ALGSEL_AES | OP_ALG_AAI_GCM,
-       },
-       {
-               .name = "rfc4543(gcm(aes))",
-               .driver_name = "rfc4543-gcm-aes-caam",
-               .blocksize = 1,
-               .type = CRYPTO_ALG_TYPE_AEAD,
-               .template_aead = {
-                       .setkey = rfc4543_setkey,
-                       .setauthsize = rfc4543_setauthsize,
-                       .encrypt = aead_encrypt,
-                       .decrypt = aead_decrypt,
-                       .givencrypt = aead_givencrypt,
-                       .geniv = "<built-in>",
-                       .ivsize = 8,
-                       .maxauthsize = AES_BLOCK_SIZE,
-                       },
-               .class1_alg_type = OP_ALG_ALGSEL_AES | OP_ALG_AAI_GCM,
-       },
-       /* Galois Counter Mode */
-       {
-               .name = "gcm(aes)",
-               .driver_name = "gcm-aes-caam",
-               .blocksize = 1,
-               .type = CRYPTO_ALG_TYPE_AEAD,
-               .template_aead = {
-                       .setkey = gcm_setkey,
-                       .setauthsize = gcm_setauthsize,
-                       .encrypt = aead_encrypt,
-                       .decrypt = aead_decrypt,
-                       .givencrypt = NULL,
-                       .geniv = "<built-in>",
-                       .ivsize = 12,
-                       .maxauthsize = AES_BLOCK_SIZE,
-                       },
-               .class1_alg_type = OP_ALG_ALGSEL_AES | OP_ALG_AAI_GCM,
-       },
        /* ablkcipher descriptor */
        {
                .name = "cbc(aes)",
@@ -4124,21 +4036,84 @@ static struct caam_alg_template driver_algs[] = {
        }
 };
 
-struct caam_crypto_alg {
-       struct list_head entry;
+struct caam_alg_entry {
        int class1_alg_type;
        int class2_alg_type;
        int alg_op;
+};
+
+struct caam_aead_alg {
+       struct aead_alg aead;
+       struct caam_alg_entry caam;
+       bool registered;
+};
+
+static struct caam_aead_alg driver_aeads[] = {
+       {
+               .aead = {
+                       .base = {
+                               .cra_name = "rfc4106(gcm(aes))",
+                               .cra_driver_name = "rfc4106-gcm-aes-caam",
+                               .cra_blocksize = 1,
+                       },
+                       .setkey = rfc4106_setkey,
+                       .setauthsize = rfc4106_setauthsize,
+                       .encrypt = gcm_encrypt,
+                       .decrypt = gcm_decrypt,
+                       .ivsize = 8,
+                       .maxauthsize = AES_BLOCK_SIZE,
+               },
+               .caam = {
+                       .class1_alg_type = OP_ALG_ALGSEL_AES | OP_ALG_AAI_GCM,
+               },
+       },
+       {
+               .aead = {
+                       .base = {
+                               .cra_name = "rfc4543(gcm(aes))",
+                               .cra_driver_name = "rfc4543-gcm-aes-caam",
+                               .cra_blocksize = 1,
+                       },
+                       .setkey = rfc4543_setkey,
+                       .setauthsize = rfc4543_setauthsize,
+                       .encrypt = gcm_encrypt,
+                       .decrypt = gcm_decrypt,
+                       .ivsize = 8,
+                       .maxauthsize = AES_BLOCK_SIZE,
+               },
+               .caam = {
+                       .class1_alg_type = OP_ALG_ALGSEL_AES | OP_ALG_AAI_GCM,
+               },
+       },
+       /* Galois Counter Mode */
+       {
+               .aead = {
+                       .base = {
+                               .cra_name = "gcm(aes)",
+                               .cra_driver_name = "gcm-aes-caam",
+                               .cra_blocksize = 1,
+                       },
+                       .setkey = gcm_setkey,
+                       .setauthsize = gcm_setauthsize,
+                       .encrypt = gcm_encrypt,
+                       .decrypt = gcm_decrypt,
+                       .ivsize = 12,
+                       .maxauthsize = AES_BLOCK_SIZE,
+               },
+               .caam = {
+                       .class1_alg_type = OP_ALG_ALGSEL_AES | OP_ALG_AAI_GCM,
+               },
+       },
+};
+
+struct caam_crypto_alg {
        struct crypto_alg crypto_alg;
+       struct list_head entry;
+       struct caam_alg_entry caam;
 };
 
-static int caam_cra_init(struct crypto_tfm *tfm)
+static int caam_init_common(struct caam_ctx *ctx, struct caam_alg_entry *caam)
 {
-       struct crypto_alg *alg = tfm->__crt_alg;
-       struct caam_crypto_alg *caam_alg =
-                container_of(alg, struct caam_crypto_alg, crypto_alg);
-       struct caam_ctx *ctx = crypto_tfm_ctx(tfm);
-
        ctx->jrdev = caam_jr_alloc();
        if (IS_ERR(ctx->jrdev)) {
                pr_err("Job Ring Device allocation for transform failed\n");
@@ -4146,17 +4121,35 @@ static int caam_cra_init(struct crypto_tfm *tfm)
        }
 
        /* copy descriptor header template value */
-       ctx->class1_alg_type = OP_TYPE_CLASS1_ALG | caam_alg->class1_alg_type;
-       ctx->class2_alg_type = OP_TYPE_CLASS2_ALG | caam_alg->class2_alg_type;
-       ctx->alg_op = OP_TYPE_CLASS2_ALG | caam_alg->alg_op;
+       ctx->class1_alg_type = OP_TYPE_CLASS1_ALG | caam->class1_alg_type;
+       ctx->class2_alg_type = OP_TYPE_CLASS2_ALG | caam->class2_alg_type;
+       ctx->alg_op = OP_TYPE_CLASS2_ALG | caam->alg_op;
 
        return 0;
 }
 
-static void caam_cra_exit(struct crypto_tfm *tfm)
+static int caam_cra_init(struct crypto_tfm *tfm)
 {
+       struct crypto_alg *alg = tfm->__crt_alg;
+       struct caam_crypto_alg *caam_alg =
+                container_of(alg, struct caam_crypto_alg, crypto_alg);
        struct caam_ctx *ctx = crypto_tfm_ctx(tfm);
 
+       return caam_init_common(ctx, &caam_alg->caam);
+}
+
+static int caam_aead_init(struct crypto_aead *tfm)
+{
+       struct aead_alg *alg = crypto_aead_alg(tfm);
+       struct caam_aead_alg *caam_alg =
+                container_of(alg, struct caam_aead_alg, aead);
+       struct caam_ctx *ctx = crypto_aead_ctx(tfm);
+
+       return caam_init_common(ctx, &caam_alg->caam);
+}
+
+static void caam_exit_common(struct caam_ctx *ctx)
+{
        if (ctx->sh_desc_enc_dma &&
            !dma_mapping_error(ctx->jrdev, ctx->sh_desc_enc_dma))
                dma_unmap_single(ctx->jrdev, ctx->sh_desc_enc_dma,
@@ -4179,10 +4172,28 @@ static void caam_cra_exit(struct crypto_tfm *tfm)
        caam_jr_free(ctx->jrdev);
 }
 
+static void caam_cra_exit(struct crypto_tfm *tfm)
+{
+       caam_exit_common(crypto_tfm_ctx(tfm));
+}
+
+static void caam_aead_exit(struct crypto_aead *tfm)
+{
+       caam_exit_common(crypto_aead_ctx(tfm));
+}
+
 static void __exit caam_algapi_exit(void)
 {
 
        struct caam_crypto_alg *t_alg, *n;
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(driver_aeads); i++) {
+               struct caam_aead_alg *t_alg = driver_aeads + i;
+
+               if (t_alg->registered)
+                       crypto_unregister_aead(&t_alg->aead);
+       }
 
        if (!alg_list.next)
                return;
@@ -4235,13 +4246,26 @@ static struct caam_crypto_alg *caam_alg_alloc(struct caam_alg_template
                break;
        }
 
-       t_alg->class1_alg_type = template->class1_alg_type;
-       t_alg->class2_alg_type = template->class2_alg_type;
-       t_alg->alg_op = template->alg_op;
+       t_alg->caam.class1_alg_type = template->class1_alg_type;
+       t_alg->caam.class2_alg_type = template->class2_alg_type;
+       t_alg->caam.alg_op = template->alg_op;
 
        return t_alg;
 }
 
+static void caam_aead_alg_init(struct caam_aead_alg *t_alg)
+{
+       struct aead_alg *alg = &t_alg->aead;
+
+       alg->base.cra_module = THIS_MODULE;
+       alg->base.cra_priority = CAAM_CRA_PRIORITY;
+       alg->base.cra_ctxsize = sizeof(struct caam_ctx);
+       alg->base.cra_flags = CRYPTO_ALG_ASYNC | CRYPTO_ALG_KERN_DRIVER_ONLY;
+
+       alg->init = caam_aead_init;
+       alg->exit = caam_aead_exit;
+}
+
 static int __init caam_algapi_init(void)
 {
        struct device_node *dev_node;
@@ -4249,6 +4273,7 @@ static int __init caam_algapi_init(void)
        struct device *ctrldev;
        void *priv;
        int i = 0, err = 0;
+       bool registered = false;
 
        dev_node = of_find_compatible_node(NULL, NULL, "fsl,sec-v4.0");
        if (!dev_node) {
@@ -4295,10 +4320,30 @@ static int __init caam_algapi_init(void)
                        pr_warn("%s alg registration failed\n",
                                t_alg->crypto_alg.cra_driver_name);
                        kfree(t_alg);
-               } else
-                       list_add_tail(&t_alg->entry, &alg_list);
+                       continue;
+               }
+
+               list_add_tail(&t_alg->entry, &alg_list);
+               registered = true;
+       }
+
+       for (i = 0; i < ARRAY_SIZE(driver_aeads); i++) {
+               struct caam_aead_alg *t_alg = driver_aeads + i;
+
+               caam_aead_alg_init(t_alg);
+
+               err = crypto_register_aead(&t_alg->aead);
+               if (err) {
+                       pr_warn("%s alg registration failed\n",
+                               t_alg->aead.base.cra_driver_name);
+                       continue;
+               }
+
+               t_alg->registered = true;
+               registered = true;
        }
-       if (!list_empty(&alg_list))
+
+       if (registered)
                pr_info("caam algorithms registered in /proc/crypto\n");
 
        return err;
index ba0532efd3ae68d0368a00a1018dd22ee76f99b0..dae1e8099969a192b302703ec291da96ebac3429 100644 (file)
@@ -835,17 +835,17 @@ static int ahash_update_ctx(struct ahash_request *req)
                        src_map_to_sec4_sg(jrdev, req->src, src_nents,
                                           edesc->sec4_sg + sec4_sg_src_index,
                                           chained);
-                       if (*next_buflen) {
+                       if (*next_buflen)
                                scatterwalk_map_and_copy(next_buf, req->src,
                                                         to_hash - *buflen,
                                                         *next_buflen, 0);
-                               state->current_buf = !state->current_buf;
-                       }
                } else {
                        (edesc->sec4_sg + sec4_sg_src_index - 1)->len |=
                                                        SEC4_SG_LEN_FIN;
                }
 
+               state->current_buf = !state->current_buf;
+
                sh_len = desc_len(sh_desc);
                desc = edesc->hw_desc;
                init_job_desc_shared(desc, ptr, sh_len, HDR_SHARE_DEFER |
@@ -1268,9 +1268,10 @@ static int ahash_update_no_ctx(struct ahash_request *req)
                        scatterwalk_map_and_copy(next_buf, req->src,
                                                 to_hash - *buflen,
                                                 *next_buflen, 0);
-                       state->current_buf = !state->current_buf;
                }
 
+               state->current_buf = !state->current_buf;
+
                sh_len = desc_len(sh_desc);
                desc = edesc->hw_desc;
                init_job_desc_shared(desc, ptr, sh_len, HDR_SHARE_DEFER |
@@ -1544,6 +1545,8 @@ static int ahash_init(struct ahash_request *req)
 
        state->current_buf = 0;
        state->buf_dma = 0;
+       state->buflen_0 = 0;
+       state->buflen_1 = 0;
 
        return 0;
 }
index 26a544b505f1e17166f95cf0e0dccdc5191015d3..5095337205b830c148696a37d53a8902643b317f 100644 (file)
@@ -56,7 +56,7 @@
 
 /* Buffer, its dma address and lock */
 struct buf_data {
-       u8 buf[RN_BUF_SIZE];
+       u8 buf[RN_BUF_SIZE] ____cacheline_aligned;
        dma_addr_t addr;
        struct completion filled;
        u32 hw_desc[DESC_JOB_O_LEN];
index acd7743e2603cdd0e9f25aed0c4a933dfc423b70..f57f395db33f73e8fb5630bd7d2c779a4ca2ff7f 100644 (file)
@@ -32,7 +32,7 @@
 #include <crypto/des.h>
 #include <crypto/sha.h>
 #include <crypto/md5.h>
-#include <crypto/aead.h>
+#include <crypto/internal/aead.h>
 #include <crypto/authenc.h>
 #include <crypto/scatterwalk.h>
 #include <crypto/internal/skcipher.h>
index efba4ccd4facaa13da181e2fa57a11a4171c2744..efacab7539ef6a8afdacbfb8d9965a0ec4f9ef75 100644 (file)
@@ -301,7 +301,7 @@ static int caam_remove(struct platform_device *pdev)
 #endif
 
        /* Unmap controller region */
-       iounmap(&ctrl);
+       iounmap(ctrl);
 
        return ret;
 }
@@ -496,7 +496,7 @@ static int caam_probe(struct platform_device *pdev)
                                        sizeof(struct platform_device *) * rspec,
                                        GFP_KERNEL);
        if (ctrlpriv->jrpdev == NULL) {
-               iounmap(&ctrl);
+               iounmap(ctrl);
                return -ENOMEM;
        }
 
index 378ddc17f60e1e6f686579824e64f3b0e73163e7..672c97489505340abd440dc694cbc16d97045760 100644 (file)
 #endif
 #endif
 
+/*
+ * The only users of these wr/rd_reg64 functions is the Job Ring (JR).
+ * The DMA address registers in the JR are a pair of 32-bit registers.
+ * The layout is:
+ *
+ *    base + 0x0000 : most-significant 32 bits
+ *    base + 0x0004 : least-significant 32 bits
+ *
+ * The 32-bit version of this core therefore has to write to base + 0x0004
+ * to set the 32-bit wide DMA address. This seems to be independent of the
+ * endianness of the written/read data.
+ */
+
 #ifndef CONFIG_64BIT
-#ifdef __BIG_ENDIAN
-static inline void wr_reg64(u64 __iomem *reg, u64 data)
-{
-       wr_reg32((u32 __iomem *)reg, (data & 0xffffffff00000000ull) >> 32);
-       wr_reg32((u32 __iomem *)reg + 1, data & 0x00000000ffffffffull);
-}
+#define REG64_MS32(reg) ((u32 __iomem *)(reg))
+#define REG64_LS32(reg) ((u32 __iomem *)(reg) + 1)
 
-static inline u64 rd_reg64(u64 __iomem *reg)
-{
-       return (((u64)rd_reg32((u32 __iomem *)reg)) << 32) |
-               ((u64)rd_reg32((u32 __iomem *)reg + 1));
-}
-#else
-#ifdef __LITTLE_ENDIAN
 static inline void wr_reg64(u64 __iomem *reg, u64 data)
 {
-       wr_reg32((u32 __iomem *)reg + 1, (data & 0xffffffff00000000ull) >> 32);
-       wr_reg32((u32 __iomem *)reg, data & 0x00000000ffffffffull);
+       wr_reg32(REG64_MS32(reg), data >> 32);
+       wr_reg32(REG64_LS32(reg), data);
 }
 
 static inline u64 rd_reg64(u64 __iomem *reg)
 {
-       return (((u64)rd_reg32((u32 __iomem *)reg + 1)) << 32) |
-               ((u64)rd_reg32((u32 __iomem *)reg));
+       return ((u64)rd_reg32(REG64_MS32(reg)) << 32 |
+               (u64)rd_reg32(REG64_LS32(reg)));
 }
 #endif
-#endif
-#endif
 
 /*
  * jr_outentry
index 3b918218aa4c56436e378f595f45d2f2cc067e1a..b68b74cc7b778dcd89eb75d8528fb88bc121b4f3 100644 (file)
@@ -55,6 +55,21 @@ static inline void sg_to_sec4_sg_last(struct scatterlist *sg, int sg_count,
        sec4_sg_ptr->len |= SEC4_SG_LEN_FIN;
 }
 
+static inline struct sec4_sg_entry *sg_to_sec4_sg_len(
+       struct scatterlist *sg, unsigned int total,
+       struct sec4_sg_entry *sec4_sg_ptr)
+{
+       do {
+               unsigned int len = min(sg_dma_len(sg), total);
+
+               dma_to_sec4_sg_one(sec4_sg_ptr, sg_dma_address(sg), len, 0);
+               sec4_sg_ptr++;
+               sg = sg_next(sg);
+               total -= len;
+       } while (total);
+       return sec4_sg_ptr - 1;
+}
+
 /* count number of elements in scatterlist */
 static inline int __sg_count(struct scatterlist *sg_list, int nbytes,
                             bool *chained)
@@ -85,34 +100,41 @@ static inline int sg_count(struct scatterlist *sg_list, int nbytes,
        return sg_nents;
 }
 
-static int dma_map_sg_chained(struct device *dev, struct scatterlist *sg,
-                             unsigned int nents, enum dma_data_direction dir,
-                             bool chained)
+static inline void dma_unmap_sg_chained(
+       struct device *dev, struct scatterlist *sg, unsigned int nents,
+       enum dma_data_direction dir, bool chained)
 {
        if (unlikely(chained)) {
                int i;
                for (i = 0; i < nents; i++) {
-                       dma_map_sg(dev, sg, 1, dir);
+                       dma_unmap_sg(dev, sg, 1, dir);
                        sg = sg_next(sg);
                }
-       } else {
-               dma_map_sg(dev, sg, nents, dir);
+       } else if (nents) {
+               dma_unmap_sg(dev, sg, nents, dir);
        }
-       return nents;
 }
 
-static int dma_unmap_sg_chained(struct device *dev, struct scatterlist *sg,
-                               unsigned int nents, enum dma_data_direction dir,
-                               bool chained)
+static inline int dma_map_sg_chained(
+       struct device *dev, struct scatterlist *sg, unsigned int nents,
+       enum dma_data_direction dir, bool chained)
 {
+       struct scatterlist *first = sg;
+
        if (unlikely(chained)) {
                int i;
                for (i = 0; i < nents; i++) {
-                       dma_unmap_sg(dev, sg, 1, dir);
+                       if (!dma_map_sg(dev, sg, 1, dir)) {
+                               dma_unmap_sg_chained(dev, first, i, dir,
+                                                    chained);
+                               nents = 0;
+                               break;
+                       }
+
                        sg = sg_next(sg);
                }
-       } else {
-               dma_unmap_sg(dev, sg, nents, dir);
-       }
+       } else
+               nents = dma_map_sg(dev, sg, nents, dir);
+
        return nents;
 }
index 7639ffc36c68b69807dba847592185a076a56053..ae38f6b6cc10c3c97d2f3543aa896a454c1bbd9a 100644 (file)
@@ -13,7 +13,6 @@ config CRYPTO_DEV_CCP_CRYPTO
        tristate "Encryption and hashing acceleration support"
        depends on CRYPTO_DEV_CCP_DD
        default m
-       select CRYPTO_ALGAPI
        select CRYPTO_HASH
        select CRYPTO_BLKCIPHER
        select CRYPTO_AUTHENC
index 71f2e3c8942416b719a53c84a1ae124d0d4363b9..d09c6c4af4aabfcba7cdd48e33c6d88ab80b5453 100644 (file)
@@ -52,8 +52,7 @@ struct ccp_dm_workarea {
 
 struct ccp_sg_workarea {
        struct scatterlist *sg;
-       unsigned int nents;
-       unsigned int length;
+       int nents;
 
        struct scatterlist *dma_sg;
        struct device *dma_dev;
@@ -496,8 +495,10 @@ static int ccp_init_sg_workarea(struct ccp_sg_workarea *wa, struct device *dev,
        if (!sg)
                return 0;
 
-       wa->nents = sg_nents(sg);
-       wa->length = sg->length;
+       wa->nents = sg_nents_for_len(sg, len);
+       if (wa->nents < 0)
+               return wa->nents;
+
        wa->bytes_left = len;
        wa->sg_used = 0;
 
index b1c20b2b564712eb320a0b2eb59d252e14151a1c..f2e6de361fd1805094b651d3a36c6141d0ad8fbb 100644 (file)
@@ -90,58 +90,6 @@ static struct resource *ccp_find_mmio_area(struct ccp_device *ccp)
        return NULL;
 }
 
-#ifdef CONFIG_ACPI
-static int ccp_acpi_support(struct ccp_device *ccp)
-{
-       struct ccp_platform *ccp_platform = ccp->dev_specific;
-       struct acpi_device *adev = ACPI_COMPANION(ccp->dev);
-       acpi_handle handle;
-       acpi_status status;
-       unsigned long long data;
-       int cca;
-
-       /* Retrieve the device cache coherency value */
-       handle = adev->handle;
-       do {
-               status = acpi_evaluate_integer(handle, "_CCA", NULL, &data);
-               if (!ACPI_FAILURE(status)) {
-                       cca = data;
-                       break;
-               }
-       } while (!ACPI_FAILURE(status));
-
-       if (ACPI_FAILURE(status)) {
-               dev_err(ccp->dev, "error obtaining acpi coherency value\n");
-               return -EINVAL;
-       }
-
-       ccp_platform->coherent = !!cca;
-
-       return 0;
-}
-#else  /* CONFIG_ACPI */
-static int ccp_acpi_support(struct ccp_device *ccp)
-{
-       return -EINVAL;
-}
-#endif
-
-#ifdef CONFIG_OF
-static int ccp_of_support(struct ccp_device *ccp)
-{
-       struct ccp_platform *ccp_platform = ccp->dev_specific;
-
-       ccp_platform->coherent = of_dma_is_coherent(ccp->dev->of_node);
-
-       return 0;
-}
-#else
-static int ccp_of_support(struct ccp_device *ccp)
-{
-       return -EINVAL;
-}
-#endif
-
 static int ccp_platform_probe(struct platform_device *pdev)
 {
        struct ccp_device *ccp;
@@ -174,21 +122,13 @@ static int ccp_platform_probe(struct platform_device *pdev)
        }
        ccp->io_regs = ccp->io_map;
 
-       if (!dev->dma_mask)
-               dev->dma_mask = &dev->coherent_dma_mask;
        ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(48));
        if (ret) {
                dev_err(dev, "dma_set_mask_and_coherent failed (%d)\n", ret);
                goto e_err;
        }
 
-       if (ccp_platform->use_acpi)
-               ret = ccp_acpi_support(ccp);
-       else
-               ret = ccp_of_support(ccp);
-       if (ret)
-               goto e_err;
-
+       ccp_platform->coherent = device_dma_is_coherent(ccp->dev);
        if (ccp_platform->coherent)
                ccp->axcache = CACHE_WB_NO_ALLOC;
        else
index 48f453555f1fe4572d4b3619b41e8b7dbb30d8a7..7ba495f7537042f898ef1cd7cdba7a6263f2059b 100644 (file)
@@ -25,7 +25,7 @@
 #include <crypto/aes.h>
 #include <crypto/sha.h>
 #include <crypto/algapi.h>
-#include <crypto/aead.h>
+#include <crypto/internal/aead.h>
 #include <crypto/authenc.h>
 #include <crypto/scatterwalk.h>
 
@@ -575,7 +575,8 @@ static int init_tfm_ablk(struct crypto_tfm *tfm)
 
 static int init_tfm_aead(struct crypto_tfm *tfm)
 {
-       tfm->crt_aead.reqsize = sizeof(struct aead_ctx);
+       crypto_aead_set_reqsize(__crypto_aead_cast(tfm),
+                               sizeof(struct aead_ctx));
        return init_tfm(tfm);
 }
 
@@ -1096,7 +1097,7 @@ static int aead_setup(struct crypto_aead *tfm, unsigned int authsize)
 {
        struct ixp_ctx *ctx = crypto_aead_ctx(tfm);
        u32 *flags = &tfm->base.crt_flags;
-       unsigned digest_len = crypto_aead_alg(tfm)->maxauthsize;
+       unsigned digest_len = crypto_aead_maxauthsize(tfm);
        int ret;
 
        if (!ctx->enckey_len && !ctx->authkey_len)
@@ -1138,7 +1139,7 @@ out:
 
 static int aead_setauthsize(struct crypto_aead *tfm, unsigned int authsize)
 {
-       int max = crypto_aead_alg(tfm)->maxauthsize >> 2;
+       int max = crypto_aead_maxauthsize(tfm) >> 2;
 
        if ((authsize>>2) < 1 || (authsize>>2) > max || (authsize & 3))
                return -EINVAL;
diff --git a/drivers/crypto/marvell/Makefile b/drivers/crypto/marvell/Makefile
new file mode 100644 (file)
index 0000000..0c12b13
--- /dev/null
@@ -0,0 +1,2 @@
+obj-$(CONFIG_CRYPTO_DEV_MARVELL_CESA) += marvell-cesa.o
+marvell-cesa-objs := cesa.o cipher.o hash.o tdma.o
diff --git a/drivers/crypto/marvell/cesa.c b/drivers/crypto/marvell/cesa.c
new file mode 100644 (file)
index 0000000..a432633
--- /dev/null
@@ -0,0 +1,548 @@
+/*
+ * Support for Marvell's Cryptographic Engine and Security Accelerator (CESA)
+ * that can be found on the following platform: Orion, Kirkwood, Armada. This
+ * driver supports the TDMA engine on platforms on which it is available.
+ *
+ * Author: Boris Brezillon <boris.brezillon@free-electrons.com>
+ * Author: Arnaud Ebalard <arno@natisbad.org>
+ *
+ * This work is based on an initial version written by
+ * Sebastian Andrzej Siewior < sebastian at breakpoint dot cc >
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+
+#include <linux/delay.h>
+#include <linux/genalloc.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kthread.h>
+#include <linux/mbus.h>
+#include <linux/platform_device.h>
+#include <linux/scatterlist.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/clk.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/of_irq.h>
+
+#include "cesa.h"
+
+static int allhwsupport = !IS_ENABLED(CONFIG_CRYPTO_DEV_MV_CESA);
+module_param_named(allhwsupport, allhwsupport, int, 0444);
+MODULE_PARM_DESC(allhwsupport, "Enable support for all hardware (even it if overlaps with the mv_cesa driver)");
+
+struct mv_cesa_dev *cesa_dev;
+
+static void mv_cesa_dequeue_req_unlocked(struct mv_cesa_engine *engine)
+{
+       struct crypto_async_request *req, *backlog;
+       struct mv_cesa_ctx *ctx;
+
+       spin_lock_bh(&cesa_dev->lock);
+       backlog = crypto_get_backlog(&cesa_dev->queue);
+       req = crypto_dequeue_request(&cesa_dev->queue);
+       engine->req = req;
+       spin_unlock_bh(&cesa_dev->lock);
+
+       if (!req)
+               return;
+
+       if (backlog)
+               backlog->complete(backlog, -EINPROGRESS);
+
+       ctx = crypto_tfm_ctx(req->tfm);
+       ctx->ops->prepare(req, engine);
+       ctx->ops->step(req);
+}
+
+static irqreturn_t mv_cesa_int(int irq, void *priv)
+{
+       struct mv_cesa_engine *engine = priv;
+       struct crypto_async_request *req;
+       struct mv_cesa_ctx *ctx;
+       u32 status, mask;
+       irqreturn_t ret = IRQ_NONE;
+
+       while (true) {
+               int res;
+
+               mask = mv_cesa_get_int_mask(engine);
+               status = readl(engine->regs + CESA_SA_INT_STATUS);
+
+               if (!(status & mask))
+                       break;
+
+               /*
+                * TODO: avoid clearing the FPGA_INT_STATUS if this not
+                * relevant on some platforms.
+                */
+               writel(~status, engine->regs + CESA_SA_FPGA_INT_STATUS);
+               writel(~status, engine->regs + CESA_SA_INT_STATUS);
+
+               ret = IRQ_HANDLED;
+               spin_lock_bh(&engine->lock);
+               req = engine->req;
+               spin_unlock_bh(&engine->lock);
+               if (req) {
+                       ctx = crypto_tfm_ctx(req->tfm);
+                       res = ctx->ops->process(req, status & mask);
+                       if (res != -EINPROGRESS) {
+                               spin_lock_bh(&engine->lock);
+                               engine->req = NULL;
+                               mv_cesa_dequeue_req_unlocked(engine);
+                               spin_unlock_bh(&engine->lock);
+                               ctx->ops->cleanup(req);
+                               local_bh_disable();
+                               req->complete(req, res);
+                               local_bh_enable();
+                       } else {
+                               ctx->ops->step(req);
+                       }
+               }
+       }
+
+       return ret;
+}
+
+int mv_cesa_queue_req(struct crypto_async_request *req)
+{
+       int ret;
+       int i;
+
+       spin_lock_bh(&cesa_dev->lock);
+       ret = crypto_enqueue_request(&cesa_dev->queue, req);
+       spin_unlock_bh(&cesa_dev->lock);
+
+       if (ret != -EINPROGRESS)
+               return ret;
+
+       for (i = 0; i < cesa_dev->caps->nengines; i++) {
+               spin_lock_bh(&cesa_dev->engines[i].lock);
+               if (!cesa_dev->engines[i].req)
+                       mv_cesa_dequeue_req_unlocked(&cesa_dev->engines[i]);
+               spin_unlock_bh(&cesa_dev->engines[i].lock);
+       }
+
+       return -EINPROGRESS;
+}
+
+static int mv_cesa_add_algs(struct mv_cesa_dev *cesa)
+{
+       int ret;
+       int i, j;
+
+       for (i = 0; i < cesa->caps->ncipher_algs; i++) {
+               ret = crypto_register_alg(cesa->caps->cipher_algs[i]);
+               if (ret)
+                       goto err_unregister_crypto;
+       }
+
+       for (i = 0; i < cesa->caps->nahash_algs; i++) {
+               ret = crypto_register_ahash(cesa->caps->ahash_algs[i]);
+               if (ret)
+                       goto err_unregister_ahash;
+       }
+
+       return 0;
+
+err_unregister_ahash:
+       for (j = 0; j < i; j++)
+               crypto_unregister_ahash(cesa->caps->ahash_algs[j]);
+       i = cesa->caps->ncipher_algs;
+
+err_unregister_crypto:
+       for (j = 0; j < i; j++)
+               crypto_unregister_alg(cesa->caps->cipher_algs[j]);
+
+       return ret;
+}
+
+static void mv_cesa_remove_algs(struct mv_cesa_dev *cesa)
+{
+       int i;
+
+       for (i = 0; i < cesa->caps->nahash_algs; i++)
+               crypto_unregister_ahash(cesa->caps->ahash_algs[i]);
+
+       for (i = 0; i < cesa->caps->ncipher_algs; i++)
+               crypto_unregister_alg(cesa->caps->cipher_algs[i]);
+}
+
+static struct crypto_alg *orion_cipher_algs[] = {
+       &mv_cesa_ecb_des_alg,
+       &mv_cesa_cbc_des_alg,
+       &mv_cesa_ecb_des3_ede_alg,
+       &mv_cesa_cbc_des3_ede_alg,
+       &mv_cesa_ecb_aes_alg,
+       &mv_cesa_cbc_aes_alg,
+};
+
+static struct ahash_alg *orion_ahash_algs[] = {
+       &mv_md5_alg,
+       &mv_sha1_alg,
+       &mv_ahmac_md5_alg,
+       &mv_ahmac_sha1_alg,
+};
+
+static struct crypto_alg *armada_370_cipher_algs[] = {
+       &mv_cesa_ecb_des_alg,
+       &mv_cesa_cbc_des_alg,
+       &mv_cesa_ecb_des3_ede_alg,
+       &mv_cesa_cbc_des3_ede_alg,
+       &mv_cesa_ecb_aes_alg,
+       &mv_cesa_cbc_aes_alg,
+};
+
+static struct ahash_alg *armada_370_ahash_algs[] = {
+       &mv_md5_alg,
+       &mv_sha1_alg,
+       &mv_sha256_alg,
+       &mv_ahmac_md5_alg,
+       &mv_ahmac_sha1_alg,
+       &mv_ahmac_sha256_alg,
+};
+
+static const struct mv_cesa_caps orion_caps = {
+       .nengines = 1,
+       .cipher_algs = orion_cipher_algs,
+       .ncipher_algs = ARRAY_SIZE(orion_cipher_algs),
+       .ahash_algs = orion_ahash_algs,
+       .nahash_algs = ARRAY_SIZE(orion_ahash_algs),
+       .has_tdma = false,
+};
+
+static const struct mv_cesa_caps kirkwood_caps = {
+       .nengines = 1,
+       .cipher_algs = orion_cipher_algs,
+       .ncipher_algs = ARRAY_SIZE(orion_cipher_algs),
+       .ahash_algs = orion_ahash_algs,
+       .nahash_algs = ARRAY_SIZE(orion_ahash_algs),
+       .has_tdma = true,
+};
+
+static const struct mv_cesa_caps armada_370_caps = {
+       .nengines = 1,
+       .cipher_algs = armada_370_cipher_algs,
+       .ncipher_algs = ARRAY_SIZE(armada_370_cipher_algs),
+       .ahash_algs = armada_370_ahash_algs,
+       .nahash_algs = ARRAY_SIZE(armada_370_ahash_algs),
+       .has_tdma = true,
+};
+
+static const struct mv_cesa_caps armada_xp_caps = {
+       .nengines = 2,
+       .cipher_algs = armada_370_cipher_algs,
+       .ncipher_algs = ARRAY_SIZE(armada_370_cipher_algs),
+       .ahash_algs = armada_370_ahash_algs,
+       .nahash_algs = ARRAY_SIZE(armada_370_ahash_algs),
+       .has_tdma = true,
+};
+
+static const struct of_device_id mv_cesa_of_match_table[] = {
+       { .compatible = "marvell,orion-crypto", .data = &orion_caps },
+       { .compatible = "marvell,kirkwood-crypto", .data = &kirkwood_caps },
+       { .compatible = "marvell,dove-crypto", .data = &kirkwood_caps },
+       { .compatible = "marvell,armada-370-crypto", .data = &armada_370_caps },
+       { .compatible = "marvell,armada-xp-crypto", .data = &armada_xp_caps },
+       { .compatible = "marvell,armada-375-crypto", .data = &armada_xp_caps },
+       { .compatible = "marvell,armada-38x-crypto", .data = &armada_xp_caps },
+       {}
+};
+MODULE_DEVICE_TABLE(of, mv_cesa_of_match_table);
+
+static void
+mv_cesa_conf_mbus_windows(struct mv_cesa_engine *engine,
+                         const struct mbus_dram_target_info *dram)
+{
+       void __iomem *iobase = engine->regs;
+       int i;
+
+       for (i = 0; i < 4; i++) {
+               writel(0, iobase + CESA_TDMA_WINDOW_CTRL(i));
+               writel(0, iobase + CESA_TDMA_WINDOW_BASE(i));
+       }
+
+       for (i = 0; i < dram->num_cs; i++) {
+               const struct mbus_dram_window *cs = dram->cs + i;
+
+               writel(((cs->size - 1) & 0xffff0000) |
+                      (cs->mbus_attr << 8) |
+                      (dram->mbus_dram_target_id << 4) | 1,
+                      iobase + CESA_TDMA_WINDOW_CTRL(i));
+               writel(cs->base, iobase + CESA_TDMA_WINDOW_BASE(i));
+       }
+}
+
+static int mv_cesa_dev_dma_init(struct mv_cesa_dev *cesa)
+{
+       struct device *dev = cesa->dev;
+       struct mv_cesa_dev_dma *dma;
+
+       if (!cesa->caps->has_tdma)
+               return 0;
+
+       dma = devm_kzalloc(dev, sizeof(*dma), GFP_KERNEL);
+       if (!dma)
+               return -ENOMEM;
+
+       dma->tdma_desc_pool = dmam_pool_create("tdma_desc", dev,
+                                       sizeof(struct mv_cesa_tdma_desc),
+                                       16, 0);
+       if (!dma->tdma_desc_pool)
+               return -ENOMEM;
+
+       dma->op_pool = dmam_pool_create("cesa_op", dev,
+                                       sizeof(struct mv_cesa_op_ctx), 16, 0);
+       if (!dma->op_pool)
+               return -ENOMEM;
+
+       dma->cache_pool = dmam_pool_create("cesa_cache", dev,
+                                          CESA_MAX_HASH_BLOCK_SIZE, 1, 0);
+       if (!dma->cache_pool)
+               return -ENOMEM;
+
+       dma->padding_pool = dmam_pool_create("cesa_padding", dev, 72, 1, 0);
+       if (!dma->cache_pool)
+               return -ENOMEM;
+
+       cesa->dma = dma;
+
+       return 0;
+}
+
+static int mv_cesa_get_sram(struct platform_device *pdev, int idx)
+{
+       struct mv_cesa_dev *cesa = platform_get_drvdata(pdev);
+       struct mv_cesa_engine *engine = &cesa->engines[idx];
+       const char *res_name = "sram";
+       struct resource *res;
+
+       engine->pool = of_get_named_gen_pool(cesa->dev->of_node,
+                                            "marvell,crypto-srams",
+                                            idx);
+       if (engine->pool) {
+               engine->sram = gen_pool_dma_alloc(engine->pool,
+                                                 cesa->sram_size,
+                                                 &engine->sram_dma);
+               if (engine->sram)
+                       return 0;
+
+               engine->pool = NULL;
+               return -ENOMEM;
+       }
+
+       if (cesa->caps->nengines > 1) {
+               if (!idx)
+                       res_name = "sram0";
+               else
+                       res_name = "sram1";
+       }
+
+       res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+                                          res_name);
+       if (!res || resource_size(res) < cesa->sram_size)
+               return -EINVAL;
+
+       engine->sram = devm_ioremap_resource(cesa->dev, res);
+       if (IS_ERR(engine->sram))
+               return PTR_ERR(engine->sram);
+
+       engine->sram_dma = phys_to_dma(cesa->dev,
+                                      (phys_addr_t)res->start);
+
+       return 0;
+}
+
+static void mv_cesa_put_sram(struct platform_device *pdev, int idx)
+{
+       struct mv_cesa_dev *cesa = platform_get_drvdata(pdev);
+       struct mv_cesa_engine *engine = &cesa->engines[idx];
+
+       if (!engine->pool)
+               return;
+
+       gen_pool_free(engine->pool, (unsigned long)engine->sram,
+                     cesa->sram_size);
+}
+
+static int mv_cesa_probe(struct platform_device *pdev)
+{
+       const struct mv_cesa_caps *caps = &orion_caps;
+       const struct mbus_dram_target_info *dram;
+       const struct of_device_id *match;
+       struct device *dev = &pdev->dev;
+       struct mv_cesa_dev *cesa;
+       struct mv_cesa_engine *engines;
+       struct resource *res;
+       int irq, ret, i;
+       u32 sram_size;
+
+       if (cesa_dev) {
+               dev_err(&pdev->dev, "Only one CESA device authorized\n");
+               return -EEXIST;
+       }
+
+       if (dev->of_node) {
+               match = of_match_node(mv_cesa_of_match_table, dev->of_node);
+               if (!match || !match->data)
+                       return -ENOTSUPP;
+
+               caps = match->data;
+       }
+
+       if ((caps == &orion_caps || caps == &kirkwood_caps) && !allhwsupport)
+               return -ENOTSUPP;
+
+       cesa = devm_kzalloc(dev, sizeof(*cesa), GFP_KERNEL);
+       if (!cesa)
+               return -ENOMEM;
+
+       cesa->caps = caps;
+       cesa->dev = dev;
+
+       sram_size = CESA_SA_DEFAULT_SRAM_SIZE;
+       of_property_read_u32(cesa->dev->of_node, "marvell,crypto-sram-size",
+                            &sram_size);
+       if (sram_size < CESA_SA_MIN_SRAM_SIZE)
+               sram_size = CESA_SA_MIN_SRAM_SIZE;
+
+       cesa->sram_size = sram_size;
+       cesa->engines = devm_kzalloc(dev, caps->nengines * sizeof(*engines),
+                                    GFP_KERNEL);
+       if (!cesa->engines)
+               return -ENOMEM;
+
+       spin_lock_init(&cesa->lock);
+       crypto_init_queue(&cesa->queue, 50);
+       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs");
+       cesa->regs = devm_ioremap_resource(dev, res);
+       if (IS_ERR(cesa->regs))
+               return -ENOMEM;
+
+       ret = mv_cesa_dev_dma_init(cesa);
+       if (ret)
+               return ret;
+
+       dram = mv_mbus_dram_info_nooverlap();
+
+       platform_set_drvdata(pdev, cesa);
+
+       for (i = 0; i < caps->nengines; i++) {
+               struct mv_cesa_engine *engine = &cesa->engines[i];
+               char res_name[7];
+
+               engine->id = i;
+               spin_lock_init(&engine->lock);
+
+               ret = mv_cesa_get_sram(pdev, i);
+               if (ret)
+                       goto err_cleanup;
+
+               irq = platform_get_irq(pdev, i);
+               if (irq < 0) {
+                       ret = irq;
+                       goto err_cleanup;
+               }
+
+               /*
+                * Not all platforms can gate the CESA clocks: do not complain
+                * if the clock does not exist.
+                */
+               snprintf(res_name, sizeof(res_name), "cesa%d", i);
+               engine->clk = devm_clk_get(dev, res_name);
+               if (IS_ERR(engine->clk)) {
+                       engine->clk = devm_clk_get(dev, NULL);
+                       if (IS_ERR(engine->clk))
+                               engine->clk = NULL;
+               }
+
+               snprintf(res_name, sizeof(res_name), "cesaz%d", i);
+               engine->zclk = devm_clk_get(dev, res_name);
+               if (IS_ERR(engine->zclk))
+                       engine->zclk = NULL;
+
+               ret = clk_prepare_enable(engine->clk);
+               if (ret)
+                       goto err_cleanup;
+
+               ret = clk_prepare_enable(engine->zclk);
+               if (ret)
+                       goto err_cleanup;
+
+               engine->regs = cesa->regs + CESA_ENGINE_OFF(i);
+
+               if (dram && cesa->caps->has_tdma)
+                       mv_cesa_conf_mbus_windows(&cesa->engines[i], dram);
+
+               writel(0, cesa->engines[i].regs + CESA_SA_INT_STATUS);
+               writel(CESA_SA_CFG_STOP_DIG_ERR,
+                      cesa->engines[i].regs + CESA_SA_CFG);
+               writel(engine->sram_dma & CESA_SA_SRAM_MSK,
+                      cesa->engines[i].regs + CESA_SA_DESC_P0);
+
+               ret = devm_request_threaded_irq(dev, irq, NULL, mv_cesa_int,
+                                               IRQF_ONESHOT,
+                                               dev_name(&pdev->dev),
+                                               &cesa->engines[i]);
+               if (ret)
+                       goto err_cleanup;
+       }
+
+       cesa_dev = cesa;
+
+       ret = mv_cesa_add_algs(cesa);
+       if (ret) {
+               cesa_dev = NULL;
+               goto err_cleanup;
+       }
+
+       dev_info(dev, "CESA device successfully registered\n");
+
+       return 0;
+
+err_cleanup:
+       for (i = 0; i < caps->nengines; i++) {
+               clk_disable_unprepare(cesa->engines[i].zclk);
+               clk_disable_unprepare(cesa->engines[i].clk);
+               mv_cesa_put_sram(pdev, i);
+       }
+
+       return ret;
+}
+
+static int mv_cesa_remove(struct platform_device *pdev)
+{
+       struct mv_cesa_dev *cesa = platform_get_drvdata(pdev);
+       int i;
+
+       mv_cesa_remove_algs(cesa);
+
+       for (i = 0; i < cesa->caps->nengines; i++) {
+               clk_disable_unprepare(cesa->engines[i].zclk);
+               clk_disable_unprepare(cesa->engines[i].clk);
+               mv_cesa_put_sram(pdev, i);
+       }
+
+       return 0;
+}
+
+static struct platform_driver marvell_cesa = {
+       .probe          = mv_cesa_probe,
+       .remove         = mv_cesa_remove,
+       .driver         = {
+               .owner  = THIS_MODULE,
+               .name   = "marvell-cesa",
+               .of_match_table = mv_cesa_of_match_table,
+       },
+};
+module_platform_driver(marvell_cesa);
+
+MODULE_ALIAS("platform:mv_crypto");
+MODULE_AUTHOR("Boris Brezillon <boris.brezillon@free-electrons.com>");
+MODULE_AUTHOR("Arnaud Ebalard <arno@natisbad.org>");
+MODULE_DESCRIPTION("Support for Marvell's cryptographic engine");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/crypto/marvell/cesa.h b/drivers/crypto/marvell/cesa.h
new file mode 100644 (file)
index 0000000..b60698b
--- /dev/null
@@ -0,0 +1,791 @@
+#ifndef __MARVELL_CESA_H__
+#define __MARVELL_CESA_H__
+
+#include <crypto/algapi.h>
+#include <crypto/hash.h>
+#include <crypto/internal/hash.h>
+
+#include <linux/crypto.h>
+#include <linux/dmapool.h>
+
+#define CESA_ENGINE_OFF(i)                     (((i) * 0x2000))
+
+#define CESA_TDMA_BYTE_CNT                     0x800
+#define CESA_TDMA_SRC_ADDR                     0x810
+#define CESA_TDMA_DST_ADDR                     0x820
+#define CESA_TDMA_NEXT_ADDR                    0x830
+
+#define CESA_TDMA_CONTROL                      0x840
+#define CESA_TDMA_DST_BURST                    GENMASK(2, 0)
+#define CESA_TDMA_DST_BURST_32B                        3
+#define CESA_TDMA_DST_BURST_128B               4
+#define CESA_TDMA_OUT_RD_EN                    BIT(4)
+#define CESA_TDMA_SRC_BURST                    GENMASK(8, 6)
+#define CESA_TDMA_SRC_BURST_32B                        (3 << 6)
+#define CESA_TDMA_SRC_BURST_128B               (4 << 6)
+#define CESA_TDMA_CHAIN                                BIT(9)
+#define CESA_TDMA_BYTE_SWAP                    BIT(11)
+#define CESA_TDMA_NO_BYTE_SWAP                 BIT(11)
+#define CESA_TDMA_EN                           BIT(12)
+#define CESA_TDMA_FETCH_ND                     BIT(13)
+#define CESA_TDMA_ACT                          BIT(14)
+
+#define CESA_TDMA_CUR                          0x870
+#define CESA_TDMA_ERROR_CAUSE                  0x8c8
+#define CESA_TDMA_ERROR_MSK                    0x8cc
+
+#define CESA_TDMA_WINDOW_BASE(x)               (((x) * 0x8) + 0xa00)
+#define CESA_TDMA_WINDOW_CTRL(x)               (((x) * 0x8) + 0xa04)
+
+#define CESA_IVDIG(x)                          (0xdd00 + ((x) * 4) +   \
+                                                (((x) < 5) ? 0 : 0x14))
+
+#define CESA_SA_CMD                            0xde00
+#define CESA_SA_CMD_EN_CESA_SA_ACCL0           BIT(0)
+#define CESA_SA_CMD_EN_CESA_SA_ACCL1           BIT(1)
+#define CESA_SA_CMD_DISABLE_SEC                        BIT(2)
+
+#define CESA_SA_DESC_P0                                0xde04
+
+#define CESA_SA_DESC_P1                                0xde14
+
+#define CESA_SA_CFG                            0xde08
+#define CESA_SA_CFG_STOP_DIG_ERR               GENMASK(1, 0)
+#define CESA_SA_CFG_DIG_ERR_CONT               0
+#define CESA_SA_CFG_DIG_ERR_SKIP               1
+#define CESA_SA_CFG_DIG_ERR_STOP               3
+#define CESA_SA_CFG_CH0_W_IDMA                 BIT(7)
+#define CESA_SA_CFG_CH1_W_IDMA                 BIT(8)
+#define CESA_SA_CFG_ACT_CH0_IDMA               BIT(9)
+#define CESA_SA_CFG_ACT_CH1_IDMA               BIT(10)
+#define CESA_SA_CFG_MULTI_PKT                  BIT(11)
+#define CESA_SA_CFG_PARA_DIS                   BIT(13)
+
+#define CESA_SA_ACCEL_STATUS                   0xde0c
+#define CESA_SA_ST_ACT_0                       BIT(0)
+#define CESA_SA_ST_ACT_1                       BIT(1)
+
+/*
+ * CESA_SA_FPGA_INT_STATUS looks like a FPGA leftover and is documented only
+ * in Errata 4.12. It looks like that it was part of an IRQ-controller in FPGA
+ * and someone forgot to remove  it while switching to the core and moving to
+ * CESA_SA_INT_STATUS.
+ */
+#define CESA_SA_FPGA_INT_STATUS                        0xdd68
+#define CESA_SA_INT_STATUS                     0xde20
+#define CESA_SA_INT_AUTH_DONE                  BIT(0)
+#define CESA_SA_INT_DES_E_DONE                 BIT(1)
+#define CESA_SA_INT_AES_E_DONE                 BIT(2)
+#define CESA_SA_INT_AES_D_DONE                 BIT(3)
+#define CESA_SA_INT_ENC_DONE                   BIT(4)
+#define CESA_SA_INT_ACCEL0_DONE                        BIT(5)
+#define CESA_SA_INT_ACCEL1_DONE                        BIT(6)
+#define CESA_SA_INT_ACC0_IDMA_DONE             BIT(7)
+#define CESA_SA_INT_ACC1_IDMA_DONE             BIT(8)
+#define CESA_SA_INT_IDMA_DONE                  BIT(9)
+#define CESA_SA_INT_IDMA_OWN_ERR               BIT(10)
+
+#define CESA_SA_INT_MSK                                0xde24
+
+#define CESA_SA_DESC_CFG_OP_MAC_ONLY           0
+#define CESA_SA_DESC_CFG_OP_CRYPT_ONLY         1
+#define CESA_SA_DESC_CFG_OP_MAC_CRYPT          2
+#define CESA_SA_DESC_CFG_OP_CRYPT_MAC          3
+#define CESA_SA_DESC_CFG_OP_MSK                        GENMASK(1, 0)
+#define CESA_SA_DESC_CFG_MACM_SHA256           (1 << 4)
+#define CESA_SA_DESC_CFG_MACM_HMAC_SHA256      (3 << 4)
+#define CESA_SA_DESC_CFG_MACM_MD5              (4 << 4)
+#define CESA_SA_DESC_CFG_MACM_SHA1             (5 << 4)
+#define CESA_SA_DESC_CFG_MACM_HMAC_MD5         (6 << 4)
+#define CESA_SA_DESC_CFG_MACM_HMAC_SHA1                (7 << 4)
+#define CESA_SA_DESC_CFG_MACM_MSK              GENMASK(6, 4)
+#define CESA_SA_DESC_CFG_CRYPTM_DES            (1 << 8)
+#define CESA_SA_DESC_CFG_CRYPTM_3DES           (2 << 8)
+#define CESA_SA_DESC_CFG_CRYPTM_AES            (3 << 8)
+#define CESA_SA_DESC_CFG_CRYPTM_MSK            GENMASK(9, 8)
+#define CESA_SA_DESC_CFG_DIR_ENC               (0 << 12)
+#define CESA_SA_DESC_CFG_DIR_DEC               (1 << 12)
+#define CESA_SA_DESC_CFG_CRYPTCM_ECB           (0 << 16)
+#define CESA_SA_DESC_CFG_CRYPTCM_CBC           (1 << 16)
+#define CESA_SA_DESC_CFG_CRYPTCM_MSK           BIT(16)
+#define CESA_SA_DESC_CFG_3DES_EEE              (0 << 20)
+#define CESA_SA_DESC_CFG_3DES_EDE              (1 << 20)
+#define CESA_SA_DESC_CFG_AES_LEN_128           (0 << 24)
+#define CESA_SA_DESC_CFG_AES_LEN_192           (1 << 24)
+#define CESA_SA_DESC_CFG_AES_LEN_256           (2 << 24)
+#define CESA_SA_DESC_CFG_AES_LEN_MSK           GENMASK(25, 24)
+#define CESA_SA_DESC_CFG_NOT_FRAG              (0 << 30)
+#define CESA_SA_DESC_CFG_FIRST_FRAG            (1 << 30)
+#define CESA_SA_DESC_CFG_LAST_FRAG             (2 << 30)
+#define CESA_SA_DESC_CFG_MID_FRAG              (3 << 30)
+#define CESA_SA_DESC_CFG_FRAG_MSK              GENMASK(31, 30)
+
+/*
+ * /-----------\ 0
+ * | ACCEL CFG |       4 * 8
+ * |-----------| 0x20
+ * | CRYPT KEY |       8 * 4
+ * |-----------| 0x40
+ * |  IV   IN  |       4 * 4
+ * |-----------| 0x40 (inplace)
+ * |  IV BUF   |       4 * 4
+ * |-----------| 0x80
+ * |  DATA IN  |       16 * x (max ->max_req_size)
+ * |-----------| 0x80 (inplace operation)
+ * |  DATA OUT |       16 * x (max ->max_req_size)
+ * \-----------/ SRAM size
+ */
+
+/*
+ * Hashing memory map:
+ * /-----------\ 0
+ * | ACCEL CFG |        4 * 8
+ * |-----------| 0x20
+ * | Inner IV  |        8 * 4
+ * |-----------| 0x40
+ * | Outer IV  |        8 * 4
+ * |-----------| 0x60
+ * | Output BUF|        8 * 4
+ * |-----------| 0x80
+ * |  DATA IN  |        64 * x (max ->max_req_size)
+ * \-----------/ SRAM size
+ */
+
+#define CESA_SA_CFG_SRAM_OFFSET                        0x00
+#define CESA_SA_DATA_SRAM_OFFSET               0x80
+
+#define CESA_SA_CRYPT_KEY_SRAM_OFFSET          0x20
+#define CESA_SA_CRYPT_IV_SRAM_OFFSET           0x40
+
+#define CESA_SA_MAC_IIV_SRAM_OFFSET            0x20
+#define CESA_SA_MAC_OIV_SRAM_OFFSET            0x40
+#define CESA_SA_MAC_DIG_SRAM_OFFSET            0x60
+
+#define CESA_SA_DESC_CRYPT_DATA(offset)                                        \
+       cpu_to_le32((CESA_SA_DATA_SRAM_OFFSET + (offset)) |             \
+                   ((CESA_SA_DATA_SRAM_OFFSET + (offset)) << 16))
+
+#define CESA_SA_DESC_CRYPT_IV(offset)                                  \
+       cpu_to_le32((CESA_SA_CRYPT_IV_SRAM_OFFSET + (offset)) | \
+                   ((CESA_SA_CRYPT_IV_SRAM_OFFSET + (offset)) << 16))
+
+#define CESA_SA_DESC_CRYPT_KEY(offset)                                 \
+       cpu_to_le32(CESA_SA_CRYPT_KEY_SRAM_OFFSET + (offset))
+
+#define CESA_SA_DESC_MAC_DATA(offset)                                  \
+       cpu_to_le32(CESA_SA_DATA_SRAM_OFFSET + (offset))
+#define CESA_SA_DESC_MAC_DATA_MSK              GENMASK(15, 0)
+
+#define CESA_SA_DESC_MAC_TOTAL_LEN(total_len)  cpu_to_le32((total_len) << 16)
+#define CESA_SA_DESC_MAC_TOTAL_LEN_MSK         GENMASK(31, 16)
+
+#define CESA_SA_DESC_MAC_SRC_TOTAL_LEN_MAX     0xffff
+
+#define CESA_SA_DESC_MAC_DIGEST(offset)                                        \
+       cpu_to_le32(CESA_SA_MAC_DIG_SRAM_OFFSET + (offset))
+#define CESA_SA_DESC_MAC_DIGEST_MSK            GENMASK(15, 0)
+
+#define CESA_SA_DESC_MAC_FRAG_LEN(frag_len)    cpu_to_le32((frag_len) << 16)
+#define CESA_SA_DESC_MAC_FRAG_LEN_MSK          GENMASK(31, 16)
+
+#define CESA_SA_DESC_MAC_IV(offset)                                    \
+       cpu_to_le32((CESA_SA_MAC_IIV_SRAM_OFFSET + (offset)) |          \
+                   ((CESA_SA_MAC_OIV_SRAM_OFFSET + (offset)) << 16))
+
+#define CESA_SA_SRAM_SIZE                      2048
+#define CESA_SA_SRAM_PAYLOAD_SIZE              (cesa_dev->sram_size - \
+                                                CESA_SA_DATA_SRAM_OFFSET)
+
+#define CESA_SA_DEFAULT_SRAM_SIZE              2048
+#define CESA_SA_MIN_SRAM_SIZE                  1024
+
+#define CESA_SA_SRAM_MSK                       (2048 - 1)
+
+#define CESA_MAX_HASH_BLOCK_SIZE               64
+#define CESA_HASH_BLOCK_SIZE_MSK               (CESA_MAX_HASH_BLOCK_SIZE - 1)
+
+/**
+ * struct mv_cesa_sec_accel_desc - security accelerator descriptor
+ * @config:    engine config
+ * @enc_p:     input and output data pointers for a cipher operation
+ * @enc_len:   cipher operation length
+ * @enc_key_p: cipher key pointer
+ * @enc_iv:    cipher IV pointers
+ * @mac_src_p: input pointer and total hash length
+ * @mac_digest:        digest pointer and hash operation length
+ * @mac_iv:    hmac IV pointers
+ *
+ * Structure passed to the CESA engine to describe the crypto operation
+ * to be executed.
+ */
+struct mv_cesa_sec_accel_desc {
+       u32 config;
+       u32 enc_p;
+       u32 enc_len;
+       u32 enc_key_p;
+       u32 enc_iv;
+       u32 mac_src_p;
+       u32 mac_digest;
+       u32 mac_iv;
+};
+
+/**
+ * struct mv_cesa_blkcipher_op_ctx - cipher operation context
+ * @key:       cipher key
+ * @iv:                cipher IV
+ *
+ * Context associated to a cipher operation.
+ */
+struct mv_cesa_blkcipher_op_ctx {
+       u32 key[8];
+       u32 iv[4];
+};
+
+/**
+ * struct mv_cesa_hash_op_ctx - hash or hmac operation context
+ * @key:       cipher key
+ * @iv:                cipher IV
+ *
+ * Context associated to an hash or hmac operation.
+ */
+struct mv_cesa_hash_op_ctx {
+       u32 iv[16];
+       u32 hash[8];
+};
+
+/**
+ * struct mv_cesa_op_ctx - crypto operation context
+ * @desc:      CESA descriptor
+ * @ctx:       context associated to the crypto operation
+ *
+ * Context associated to a crypto operation.
+ */
+struct mv_cesa_op_ctx {
+       struct mv_cesa_sec_accel_desc desc;
+       union {
+               struct mv_cesa_blkcipher_op_ctx blkcipher;
+               struct mv_cesa_hash_op_ctx hash;
+       } ctx;
+};
+
+/* TDMA descriptor flags */
+#define CESA_TDMA_DST_IN_SRAM                  BIT(31)
+#define CESA_TDMA_SRC_IN_SRAM                  BIT(30)
+#define CESA_TDMA_TYPE_MSK                     GENMASK(29, 0)
+#define CESA_TDMA_DUMMY                                0
+#define CESA_TDMA_DATA                         1
+#define CESA_TDMA_OP                           2
+
+/**
+ * struct mv_cesa_tdma_desc - TDMA descriptor
+ * @byte_cnt:  number of bytes to transfer
+ * @src:       DMA address of the source
+ * @dst:       DMA address of the destination
+ * @next_dma:  DMA address of the next TDMA descriptor
+ * @cur_dma:   DMA address of this TDMA descriptor
+ * @next:      pointer to the next TDMA descriptor
+ * @op:                CESA operation attached to this TDMA descriptor
+ * @data:      raw data attached to this TDMA descriptor
+ * @flags:     flags describing the TDMA transfer. See the
+ *             "TDMA descriptor flags" section above
+ *
+ * TDMA descriptor used to create a transfer chain describing a crypto
+ * operation.
+ */
+struct mv_cesa_tdma_desc {
+       u32 byte_cnt;
+       u32 src;
+       u32 dst;
+       u32 next_dma;
+       u32 cur_dma;
+       struct mv_cesa_tdma_desc *next;
+       union {
+               struct mv_cesa_op_ctx *op;
+               void *data;
+       };
+       u32 flags;
+};
+
+/**
+ * struct mv_cesa_sg_dma_iter - scatter-gather iterator
+ * @dir:       transfer direction
+ * @sg:                scatter list
+ * @offset:    current position in the scatter list
+ * @op_offset: current position in the crypto operation
+ *
+ * Iterator used to iterate over a scatterlist while creating a TDMA chain for
+ * a crypto operation.
+ */
+struct mv_cesa_sg_dma_iter {
+       enum dma_data_direction dir;
+       struct scatterlist *sg;
+       unsigned int offset;
+       unsigned int op_offset;
+};
+
+/**
+ * struct mv_cesa_dma_iter - crypto operation iterator
+ * @len:       the crypto operation length
+ * @offset:    current position in the crypto operation
+ * @op_len:    sub-operation length (the crypto engine can only act on 2kb
+ *             chunks)
+ *
+ * Iterator used to create a TDMA chain for a given crypto operation.
+ */
+struct mv_cesa_dma_iter {
+       unsigned int len;
+       unsigned int offset;
+       unsigned int op_len;
+};
+
+/**
+ * struct mv_cesa_tdma_chain - TDMA chain
+ * @first:     first entry in the TDMA chain
+ * @last:      last entry in the TDMA chain
+ *
+ * Stores a TDMA chain for a specific crypto operation.
+ */
+struct mv_cesa_tdma_chain {
+       struct mv_cesa_tdma_desc *first;
+       struct mv_cesa_tdma_desc *last;
+};
+
+struct mv_cesa_engine;
+
+/**
+ * struct mv_cesa_caps - CESA device capabilities
+ * @engines:           number of engines
+ * @has_tdma:          whether this device has a TDMA block
+ * @cipher_algs:       supported cipher algorithms
+ * @ncipher_algs:      number of supported cipher algorithms
+ * @ahash_algs:                supported hash algorithms
+ * @nahash_algs:       number of supported hash algorithms
+ *
+ * Structure used to describe CESA device capabilities.
+ */
+struct mv_cesa_caps {
+       int nengines;
+       bool has_tdma;
+       struct crypto_alg **cipher_algs;
+       int ncipher_algs;
+       struct ahash_alg **ahash_algs;
+       int nahash_algs;
+};
+
+/**
+ * struct mv_cesa_dev_dma - DMA pools
+ * @tdma_desc_pool:    TDMA desc pool
+ * @op_pool:           crypto operation pool
+ * @cache_pool:                data cache pool (used by hash implementation when the
+ *                     hash request is smaller than the hash block size)
+ * @padding_pool:      padding pool (used by hash implementation when hardware
+ *                     padding cannot be used)
+ *
+ * Structure containing the different DMA pools used by this driver.
+ */
+struct mv_cesa_dev_dma {
+       struct dma_pool *tdma_desc_pool;
+       struct dma_pool *op_pool;
+       struct dma_pool *cache_pool;
+       struct dma_pool *padding_pool;
+};
+
+/**
+ * struct mv_cesa_dev - CESA device
+ * @caps:      device capabilities
+ * @regs:      device registers
+ * @sram_size: usable SRAM size
+ * @lock:      device lock
+ * @queue:     crypto request queue
+ * @engines:   array of engines
+ * @dma:       dma pools
+ *
+ * Structure storing CESA device information.
+ */
+struct mv_cesa_dev {
+       const struct mv_cesa_caps *caps;
+       void __iomem *regs;
+       struct device *dev;
+       unsigned int sram_size;
+       spinlock_t lock;
+       struct crypto_queue queue;
+       struct mv_cesa_engine *engines;
+       struct mv_cesa_dev_dma *dma;
+};
+
+/**
+ * struct mv_cesa_engine - CESA engine
+ * @id:                        engine id
+ * @regs:              engine registers
+ * @sram:              SRAM memory region
+ * @sram_dma:          DMA address of the SRAM memory region
+ * @lock:              engine lock
+ * @req:               current crypto request
+ * @clk:               engine clk
+ * @zclk:              engine zclk
+ * @max_req_len:       maximum chunk length (useful to create the TDMA chain)
+ * @int_mask:          interrupt mask cache
+ * @pool:              memory pool pointing to the memory region reserved in
+ *                     SRAM
+ *
+ * Structure storing CESA engine information.
+ */
+struct mv_cesa_engine {
+       int id;
+       void __iomem *regs;
+       void __iomem *sram;
+       dma_addr_t sram_dma;
+       spinlock_t lock;
+       struct crypto_async_request *req;
+       struct clk *clk;
+       struct clk *zclk;
+       size_t max_req_len;
+       u32 int_mask;
+       struct gen_pool *pool;
+};
+
+/**
+ * struct mv_cesa_req_ops - CESA request operations
+ * @prepare:   prepare a request to be executed on the specified engine
+ * @process:   process a request chunk result (should return 0 if the
+ *             operation, -EINPROGRESS if it needs more steps or an error
+ *             code)
+ * @step:      launch the crypto operation on the next chunk
+ * @cleanup:   cleanup the crypto request (release associated data)
+ */
+struct mv_cesa_req_ops {
+       void (*prepare)(struct crypto_async_request *req,
+                       struct mv_cesa_engine *engine);
+       int (*process)(struct crypto_async_request *req, u32 status);
+       void (*step)(struct crypto_async_request *req);
+       void (*cleanup)(struct crypto_async_request *req);
+};
+
+/**
+ * struct mv_cesa_ctx - CESA operation context
+ * @ops:       crypto operations
+ *
+ * Base context structure inherited by operation specific ones.
+ */
+struct mv_cesa_ctx {
+       const struct mv_cesa_req_ops *ops;
+};
+
+/**
+ * struct mv_cesa_hash_ctx - CESA hash operation context
+ * @base:      base context structure
+ *
+ * Hash context structure.
+ */
+struct mv_cesa_hash_ctx {
+       struct mv_cesa_ctx base;
+};
+
+/**
+ * struct mv_cesa_hash_ctx - CESA hmac operation context
+ * @base:      base context structure
+ * @iv:                initialization vectors
+ *
+ * HMAC context structure.
+ */
+struct mv_cesa_hmac_ctx {
+       struct mv_cesa_ctx base;
+       u32 iv[16];
+};
+
+/**
+ * enum mv_cesa_req_type - request type definitions
+ * @CESA_STD_REQ:      standard request
+ * @CESA_DMA_REQ:      DMA request
+ */
+enum mv_cesa_req_type {
+       CESA_STD_REQ,
+       CESA_DMA_REQ,
+};
+
+/**
+ * struct mv_cesa_req - CESA request
+ * @type:      request type
+ * @engine:    engine associated with this request
+ */
+struct mv_cesa_req {
+       enum mv_cesa_req_type type;
+       struct mv_cesa_engine *engine;
+};
+
+/**
+ * struct mv_cesa_tdma_req - CESA TDMA request
+ * @base:      base information
+ * @chain:     TDMA chain
+ */
+struct mv_cesa_tdma_req {
+       struct mv_cesa_req base;
+       struct mv_cesa_tdma_chain chain;
+};
+
+/**
+ * struct mv_cesa_sg_std_iter - CESA scatter-gather iterator for standard
+ *                             requests
+ * @iter:      sg mapping iterator
+ * @offset:    current offset in the SG entry mapped in memory
+ */
+struct mv_cesa_sg_std_iter {
+       struct sg_mapping_iter iter;
+       unsigned int offset;
+};
+
+/**
+ * struct mv_cesa_ablkcipher_std_req - cipher standard request
+ * @base:      base information
+ * @op:                operation context
+ * @offset:    current operation offset
+ * @size:      size of the crypto operation
+ */
+struct mv_cesa_ablkcipher_std_req {
+       struct mv_cesa_req base;
+       struct mv_cesa_op_ctx op;
+       unsigned int offset;
+       unsigned int size;
+       bool skip_ctx;
+};
+
+/**
+ * struct mv_cesa_ablkcipher_req - cipher request
+ * @req:       type specific request information
+ * @src_nents: number of entries in the src sg list
+ * @dst_nents: number of entries in the dest sg list
+ */
+struct mv_cesa_ablkcipher_req {
+       union {
+               struct mv_cesa_req base;
+               struct mv_cesa_tdma_req dma;
+               struct mv_cesa_ablkcipher_std_req std;
+       } req;
+       int src_nents;
+       int dst_nents;
+};
+
+/**
+ * struct mv_cesa_ahash_std_req - standard hash request
+ * @base:      base information
+ * @offset:    current operation offset
+ */
+struct mv_cesa_ahash_std_req {
+       struct mv_cesa_req base;
+       unsigned int offset;
+};
+
+/**
+ * struct mv_cesa_ahash_dma_req - DMA hash request
+ * @base:              base information
+ * @padding:           padding buffer
+ * @padding_dma:       DMA address of the padding buffer
+ * @cache_dma:         DMA address of the cache buffer
+ */
+struct mv_cesa_ahash_dma_req {
+       struct mv_cesa_tdma_req base;
+       u8 *padding;
+       dma_addr_t padding_dma;
+       dma_addr_t cache_dma;
+};
+
+/**
+ * struct mv_cesa_ahash_req - hash request
+ * @req:               type specific request information
+ * @cache:             cache buffer
+ * @cache_ptr:         write pointer in the cache buffer
+ * @len:               hash total length
+ * @src_nents:         number of entries in the scatterlist
+ * @last_req:          define whether the current operation is the last one
+ *                     or not
+ * @state:             hash state
+ */
+struct mv_cesa_ahash_req {
+       union {
+               struct mv_cesa_req base;
+               struct mv_cesa_ahash_dma_req dma;
+               struct mv_cesa_ahash_std_req std;
+       } req;
+       struct mv_cesa_op_ctx op_tmpl;
+       u8 *cache;
+       unsigned int cache_ptr;
+       u64 len;
+       int src_nents;
+       bool last_req;
+       __be32 state[8];
+};
+
+/* CESA functions */
+
+extern struct mv_cesa_dev *cesa_dev;
+
+static inline void mv_cesa_update_op_cfg(struct mv_cesa_op_ctx *op,
+                                        u32 cfg, u32 mask)
+{
+       op->desc.config &= cpu_to_le32(~mask);
+       op->desc.config |= cpu_to_le32(cfg);
+}
+
+static inline u32 mv_cesa_get_op_cfg(struct mv_cesa_op_ctx *op)
+{
+       return le32_to_cpu(op->desc.config);
+}
+
+static inline void mv_cesa_set_op_cfg(struct mv_cesa_op_ctx *op, u32 cfg)
+{
+       op->desc.config = cpu_to_le32(cfg);
+}
+
+static inline void mv_cesa_adjust_op(struct mv_cesa_engine *engine,
+                                    struct mv_cesa_op_ctx *op)
+{
+       u32 offset = engine->sram_dma & CESA_SA_SRAM_MSK;
+
+       op->desc.enc_p = CESA_SA_DESC_CRYPT_DATA(offset);
+       op->desc.enc_key_p = CESA_SA_DESC_CRYPT_KEY(offset);
+       op->desc.enc_iv = CESA_SA_DESC_CRYPT_IV(offset);
+       op->desc.mac_src_p &= ~CESA_SA_DESC_MAC_DATA_MSK;
+       op->desc.mac_src_p |= CESA_SA_DESC_MAC_DATA(offset);
+       op->desc.mac_digest &= ~CESA_SA_DESC_MAC_DIGEST_MSK;
+       op->desc.mac_digest |= CESA_SA_DESC_MAC_DIGEST(offset);
+       op->desc.mac_iv = CESA_SA_DESC_MAC_IV(offset);
+}
+
+static inline void mv_cesa_set_crypt_op_len(struct mv_cesa_op_ctx *op, int len)
+{
+       op->desc.enc_len = cpu_to_le32(len);
+}
+
+static inline void mv_cesa_set_mac_op_total_len(struct mv_cesa_op_ctx *op,
+                                               int len)
+{
+       op->desc.mac_src_p &= ~CESA_SA_DESC_MAC_TOTAL_LEN_MSK;
+       op->desc.mac_src_p |= CESA_SA_DESC_MAC_TOTAL_LEN(len);
+}
+
+static inline void mv_cesa_set_mac_op_frag_len(struct mv_cesa_op_ctx *op,
+                                              int len)
+{
+       op->desc.mac_digest &= ~CESA_SA_DESC_MAC_FRAG_LEN_MSK;
+       op->desc.mac_digest |= CESA_SA_DESC_MAC_FRAG_LEN(len);
+}
+
+static inline void mv_cesa_set_int_mask(struct mv_cesa_engine *engine,
+                                       u32 int_mask)
+{
+       if (int_mask == engine->int_mask)
+               return;
+
+       writel(int_mask, engine->regs + CESA_SA_INT_MSK);
+       engine->int_mask = int_mask;
+}
+
+static inline u32 mv_cesa_get_int_mask(struct mv_cesa_engine *engine)
+{
+       return engine->int_mask;
+}
+
+int mv_cesa_queue_req(struct crypto_async_request *req);
+
+/* TDMA functions */
+
+static inline void mv_cesa_req_dma_iter_init(struct mv_cesa_dma_iter *iter,
+                                            unsigned int len)
+{
+       iter->len = len;
+       iter->op_len = min(len, CESA_SA_SRAM_PAYLOAD_SIZE);
+       iter->offset = 0;
+}
+
+static inline void mv_cesa_sg_dma_iter_init(struct mv_cesa_sg_dma_iter *iter,
+                                           struct scatterlist *sg,
+                                           enum dma_data_direction dir)
+{
+       iter->op_offset = 0;
+       iter->offset = 0;
+       iter->sg = sg;
+       iter->dir = dir;
+}
+
+static inline unsigned int
+mv_cesa_req_dma_iter_transfer_len(struct mv_cesa_dma_iter *iter,
+                                 struct mv_cesa_sg_dma_iter *sgiter)
+{
+       return min(iter->op_len - sgiter->op_offset,
+                  sg_dma_len(sgiter->sg) - sgiter->offset);
+}
+
+bool mv_cesa_req_dma_iter_next_transfer(struct mv_cesa_dma_iter *chain,
+                                       struct mv_cesa_sg_dma_iter *sgiter,
+                                       unsigned int len);
+
+static inline bool mv_cesa_req_dma_iter_next_op(struct mv_cesa_dma_iter *iter)
+{
+       iter->offset += iter->op_len;
+       iter->op_len = min(iter->len - iter->offset,
+                          CESA_SA_SRAM_PAYLOAD_SIZE);
+
+       return iter->op_len;
+}
+
+void mv_cesa_dma_step(struct mv_cesa_tdma_req *dreq);
+
+static inline int mv_cesa_dma_process(struct mv_cesa_tdma_req *dreq,
+                                     u32 status)
+{
+       if (!(status & CESA_SA_INT_ACC0_IDMA_DONE))
+               return -EINPROGRESS;
+
+       if (status & CESA_SA_INT_IDMA_OWN_ERR)
+               return -EINVAL;
+
+       return 0;
+}
+
+void mv_cesa_dma_prepare(struct mv_cesa_tdma_req *dreq,
+                        struct mv_cesa_engine *engine);
+
+void mv_cesa_dma_cleanup(struct mv_cesa_tdma_req *dreq);
+
+static inline void
+mv_cesa_tdma_desc_iter_init(struct mv_cesa_tdma_chain *chain)
+{
+       memset(chain, 0, sizeof(*chain));
+}
+
+struct mv_cesa_op_ctx *mv_cesa_dma_add_op(struct mv_cesa_tdma_chain *chain,
+                                       const struct mv_cesa_op_ctx *op_templ,
+                                       bool skip_ctx,
+                                       gfp_t flags);
+
+int mv_cesa_dma_add_data_transfer(struct mv_cesa_tdma_chain *chain,
+                                 dma_addr_t dst, dma_addr_t src, u32 size,
+                                 u32 flags, gfp_t gfp_flags);
+
+int mv_cesa_dma_add_dummy_launch(struct mv_cesa_tdma_chain *chain,
+                                u32 flags);
+
+int mv_cesa_dma_add_dummy_end(struct mv_cesa_tdma_chain *chain, u32 flags);
+
+int mv_cesa_dma_add_op_transfers(struct mv_cesa_tdma_chain *chain,
+                                struct mv_cesa_dma_iter *dma_iter,
+                                struct mv_cesa_sg_dma_iter *sgiter,
+                                gfp_t gfp_flags);
+
+/* Algorithm definitions */
+
+extern struct ahash_alg mv_md5_alg;
+extern struct ahash_alg mv_sha1_alg;
+extern struct ahash_alg mv_sha256_alg;
+extern struct ahash_alg mv_ahmac_md5_alg;
+extern struct ahash_alg mv_ahmac_sha1_alg;
+extern struct ahash_alg mv_ahmac_sha256_alg;
+
+extern struct crypto_alg mv_cesa_ecb_des_alg;
+extern struct crypto_alg mv_cesa_cbc_des_alg;
+extern struct crypto_alg mv_cesa_ecb_des3_ede_alg;
+extern struct crypto_alg mv_cesa_cbc_des3_ede_alg;
+extern struct crypto_alg mv_cesa_ecb_aes_alg;
+extern struct crypto_alg mv_cesa_cbc_aes_alg;
+
+#endif /* __MARVELL_CESA_H__ */
diff --git a/drivers/crypto/marvell/cipher.c b/drivers/crypto/marvell/cipher.c
new file mode 100644 (file)
index 0000000..0745cf3
--- /dev/null
@@ -0,0 +1,797 @@
+/*
+ * Cipher algorithms supported by the CESA: DES, 3DES and AES.
+ *
+ * Author: Boris Brezillon <boris.brezillon@free-electrons.com>
+ * Author: Arnaud Ebalard <arno@natisbad.org>
+ *
+ * This work is based on an initial version written by
+ * Sebastian Andrzej Siewior < sebastian at breakpoint dot cc >
+ *
+ * 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 <crypto/aes.h>
+#include <crypto/des.h>
+
+#include "cesa.h"
+
+struct mv_cesa_des_ctx {
+       struct mv_cesa_ctx base;
+       u8 key[DES_KEY_SIZE];
+};
+
+struct mv_cesa_des3_ctx {
+       struct mv_cesa_ctx base;
+       u8 key[DES3_EDE_KEY_SIZE];
+};
+
+struct mv_cesa_aes_ctx {
+       struct mv_cesa_ctx base;
+       struct crypto_aes_ctx aes;
+};
+
+struct mv_cesa_ablkcipher_dma_iter {
+       struct mv_cesa_dma_iter base;
+       struct mv_cesa_sg_dma_iter src;
+       struct mv_cesa_sg_dma_iter dst;
+};
+
+static inline void
+mv_cesa_ablkcipher_req_iter_init(struct mv_cesa_ablkcipher_dma_iter *iter,
+                                struct ablkcipher_request *req)
+{
+       mv_cesa_req_dma_iter_init(&iter->base, req->nbytes);
+       mv_cesa_sg_dma_iter_init(&iter->src, req->src, DMA_TO_DEVICE);
+       mv_cesa_sg_dma_iter_init(&iter->dst, req->dst, DMA_FROM_DEVICE);
+}
+
+static inline bool
+mv_cesa_ablkcipher_req_iter_next_op(struct mv_cesa_ablkcipher_dma_iter *iter)
+{
+       iter->src.op_offset = 0;
+       iter->dst.op_offset = 0;
+
+       return mv_cesa_req_dma_iter_next_op(&iter->base);
+}
+
+static inline void
+mv_cesa_ablkcipher_dma_cleanup(struct ablkcipher_request *req)
+{
+       struct mv_cesa_ablkcipher_req *creq = ablkcipher_request_ctx(req);
+
+       if (req->dst != req->src) {
+               dma_unmap_sg(cesa_dev->dev, req->dst, creq->dst_nents,
+                            DMA_FROM_DEVICE);
+               dma_unmap_sg(cesa_dev->dev, req->src, creq->src_nents,
+                            DMA_TO_DEVICE);
+       } else {
+               dma_unmap_sg(cesa_dev->dev, req->src, creq->src_nents,
+                            DMA_BIDIRECTIONAL);
+       }
+       mv_cesa_dma_cleanup(&creq->req.dma);
+}
+
+static inline void mv_cesa_ablkcipher_cleanup(struct ablkcipher_request *req)
+{
+       struct mv_cesa_ablkcipher_req *creq = ablkcipher_request_ctx(req);
+
+       if (creq->req.base.type == CESA_DMA_REQ)
+               mv_cesa_ablkcipher_dma_cleanup(req);
+}
+
+static void mv_cesa_ablkcipher_std_step(struct ablkcipher_request *req)
+{
+       struct mv_cesa_ablkcipher_req *creq = ablkcipher_request_ctx(req);
+       struct mv_cesa_ablkcipher_std_req *sreq = &creq->req.std;
+       struct mv_cesa_engine *engine = sreq->base.engine;
+       size_t  len = min_t(size_t, req->nbytes - sreq->offset,
+                           CESA_SA_SRAM_PAYLOAD_SIZE);
+
+       len = sg_pcopy_to_buffer(req->src, creq->src_nents,
+                                engine->sram + CESA_SA_DATA_SRAM_OFFSET,
+                                len, sreq->offset);
+
+       sreq->size = len;
+       mv_cesa_set_crypt_op_len(&sreq->op, len);
+
+       /* FIXME: only update enc_len field */
+       if (!sreq->skip_ctx) {
+               memcpy(engine->sram, &sreq->op, sizeof(sreq->op));
+               sreq->skip_ctx = true;
+       } else {
+               memcpy(engine->sram, &sreq->op, sizeof(sreq->op.desc));
+       }
+
+       mv_cesa_set_int_mask(engine, CESA_SA_INT_ACCEL0_DONE);
+       writel(CESA_SA_CFG_PARA_DIS, engine->regs + CESA_SA_CFG);
+       writel(CESA_SA_CMD_EN_CESA_SA_ACCL0, engine->regs + CESA_SA_CMD);
+}
+
+static int mv_cesa_ablkcipher_std_process(struct ablkcipher_request *req,
+                                         u32 status)
+{
+       struct mv_cesa_ablkcipher_req *creq = ablkcipher_request_ctx(req);
+       struct mv_cesa_ablkcipher_std_req *sreq = &creq->req.std;
+       struct mv_cesa_engine *engine = sreq->base.engine;
+       size_t len;
+
+       len = sg_pcopy_from_buffer(req->dst, creq->dst_nents,
+                                  engine->sram + CESA_SA_DATA_SRAM_OFFSET,
+                                  sreq->size, sreq->offset);
+
+       sreq->offset += len;
+       if (sreq->offset < req->nbytes)
+               return -EINPROGRESS;
+
+       return 0;
+}
+
+static int mv_cesa_ablkcipher_process(struct crypto_async_request *req,
+                                     u32 status)
+{
+       struct ablkcipher_request *ablkreq = ablkcipher_request_cast(req);
+       struct mv_cesa_ablkcipher_req *creq = ablkcipher_request_ctx(ablkreq);
+       struct mv_cesa_ablkcipher_std_req *sreq = &creq->req.std;
+       struct mv_cesa_engine *engine = sreq->base.engine;
+       int ret;
+
+       if (creq->req.base.type == CESA_DMA_REQ)
+               ret = mv_cesa_dma_process(&creq->req.dma, status);
+       else
+               ret = mv_cesa_ablkcipher_std_process(ablkreq, status);
+
+       if (ret)
+               return ret;
+
+       memcpy(ablkreq->info, engine->sram + CESA_SA_CRYPT_IV_SRAM_OFFSET,
+              crypto_ablkcipher_ivsize(crypto_ablkcipher_reqtfm(ablkreq)));
+
+       return 0;
+}
+
+static void mv_cesa_ablkcipher_step(struct crypto_async_request *req)
+{
+       struct ablkcipher_request *ablkreq = ablkcipher_request_cast(req);
+       struct mv_cesa_ablkcipher_req *creq = ablkcipher_request_ctx(ablkreq);
+
+       if (creq->req.base.type == CESA_DMA_REQ)
+               mv_cesa_dma_step(&creq->req.dma);
+       else
+               mv_cesa_ablkcipher_std_step(ablkreq);
+}
+
+static inline void
+mv_cesa_ablkcipher_dma_prepare(struct ablkcipher_request *req)
+{
+       struct mv_cesa_ablkcipher_req *creq = ablkcipher_request_ctx(req);
+       struct mv_cesa_tdma_req *dreq = &creq->req.dma;
+
+       mv_cesa_dma_prepare(dreq, dreq->base.engine);
+}
+
+static inline void
+mv_cesa_ablkcipher_std_prepare(struct ablkcipher_request *req)
+{
+       struct mv_cesa_ablkcipher_req *creq = ablkcipher_request_ctx(req);
+       struct mv_cesa_ablkcipher_std_req *sreq = &creq->req.std;
+       struct mv_cesa_engine *engine = sreq->base.engine;
+
+       sreq->size = 0;
+       sreq->offset = 0;
+       mv_cesa_adjust_op(engine, &sreq->op);
+       memcpy(engine->sram, &sreq->op, sizeof(sreq->op));
+}
+
+static inline void mv_cesa_ablkcipher_prepare(struct crypto_async_request *req,
+                                             struct mv_cesa_engine *engine)
+{
+       struct ablkcipher_request *ablkreq = ablkcipher_request_cast(req);
+       struct mv_cesa_ablkcipher_req *creq = ablkcipher_request_ctx(ablkreq);
+
+       creq->req.base.engine = engine;
+
+       if (creq->req.base.type == CESA_DMA_REQ)
+               mv_cesa_ablkcipher_dma_prepare(ablkreq);
+       else
+               mv_cesa_ablkcipher_std_prepare(ablkreq);
+}
+
+static inline void
+mv_cesa_ablkcipher_req_cleanup(struct crypto_async_request *req)
+{
+       struct ablkcipher_request *ablkreq = ablkcipher_request_cast(req);
+
+       mv_cesa_ablkcipher_cleanup(ablkreq);
+}
+
+static const struct mv_cesa_req_ops mv_cesa_ablkcipher_req_ops = {
+       .step = mv_cesa_ablkcipher_step,
+       .process = mv_cesa_ablkcipher_process,
+       .prepare = mv_cesa_ablkcipher_prepare,
+       .cleanup = mv_cesa_ablkcipher_req_cleanup,
+};
+
+static int mv_cesa_ablkcipher_cra_init(struct crypto_tfm *tfm)
+{
+       struct mv_cesa_aes_ctx *ctx = crypto_tfm_ctx(tfm);
+
+       ctx->base.ops = &mv_cesa_ablkcipher_req_ops;
+
+       tfm->crt_ablkcipher.reqsize = sizeof(struct mv_cesa_ablkcipher_req);
+
+       return 0;
+}
+
+static int mv_cesa_aes_setkey(struct crypto_ablkcipher *cipher, const u8 *key,
+                             unsigned int len)
+{
+       struct crypto_tfm *tfm = crypto_ablkcipher_tfm(cipher);
+       struct mv_cesa_aes_ctx *ctx = crypto_tfm_ctx(tfm);
+       int remaining;
+       int offset;
+       int ret;
+       int i;
+
+       ret = crypto_aes_expand_key(&ctx->aes, key, len);
+       if (ret) {
+               crypto_ablkcipher_set_flags(cipher, CRYPTO_TFM_RES_BAD_KEY_LEN);
+               return ret;
+       }
+
+       remaining = (ctx->aes.key_length - 16) / 4;
+       offset = ctx->aes.key_length + 24 - remaining;
+       for (i = 0; i < remaining; i++)
+               ctx->aes.key_dec[4 + i] =
+                       cpu_to_le32(ctx->aes.key_enc[offset + i]);
+
+       return 0;
+}
+
+static int mv_cesa_des_setkey(struct crypto_ablkcipher *cipher, const u8 *key,
+                             unsigned int len)
+{
+       struct crypto_tfm *tfm = crypto_ablkcipher_tfm(cipher);
+       struct mv_cesa_des_ctx *ctx = crypto_tfm_ctx(tfm);
+       u32 tmp[DES_EXPKEY_WORDS];
+       int ret;
+
+       if (len != DES_KEY_SIZE) {
+               crypto_ablkcipher_set_flags(cipher, CRYPTO_TFM_RES_BAD_KEY_LEN);
+               return -EINVAL;
+       }
+
+       ret = des_ekey(tmp, key);
+       if (!ret && (tfm->crt_flags & CRYPTO_TFM_REQ_WEAK_KEY)) {
+               tfm->crt_flags |= CRYPTO_TFM_RES_WEAK_KEY;
+               return -EINVAL;
+       }
+
+       memcpy(ctx->key, key, DES_KEY_SIZE);
+
+       return 0;
+}
+
+static int mv_cesa_des3_ede_setkey(struct crypto_ablkcipher *cipher,
+                                  const u8 *key, unsigned int len)
+{
+       struct crypto_tfm *tfm = crypto_ablkcipher_tfm(cipher);
+       struct mv_cesa_des_ctx *ctx = crypto_tfm_ctx(tfm);
+
+       if (len != DES3_EDE_KEY_SIZE) {
+               crypto_ablkcipher_set_flags(cipher, CRYPTO_TFM_RES_BAD_KEY_LEN);
+               return -EINVAL;
+       }
+
+       memcpy(ctx->key, key, DES3_EDE_KEY_SIZE);
+
+       return 0;
+}
+
+static int mv_cesa_ablkcipher_dma_req_init(struct ablkcipher_request *req,
+                               const struct mv_cesa_op_ctx *op_templ)
+{
+       struct mv_cesa_ablkcipher_req *creq = ablkcipher_request_ctx(req);
+       gfp_t flags = (req->base.flags & CRYPTO_TFM_REQ_MAY_SLEEP) ?
+                     GFP_KERNEL : GFP_ATOMIC;
+       struct mv_cesa_tdma_req *dreq = &creq->req.dma;
+       struct mv_cesa_ablkcipher_dma_iter iter;
+       struct mv_cesa_tdma_chain chain;
+       bool skip_ctx = false;
+       int ret;
+
+       dreq->base.type = CESA_DMA_REQ;
+       dreq->chain.first = NULL;
+       dreq->chain.last = NULL;
+
+       if (req->src != req->dst) {
+               ret = dma_map_sg(cesa_dev->dev, req->src, creq->src_nents,
+                                DMA_TO_DEVICE);
+               if (!ret)
+                       return -ENOMEM;
+
+               ret = dma_map_sg(cesa_dev->dev, req->dst, creq->dst_nents,
+                                DMA_FROM_DEVICE);
+               if (!ret) {
+                       ret = -ENOMEM;
+                       goto err_unmap_src;
+               }
+       } else {
+               ret = dma_map_sg(cesa_dev->dev, req->src, creq->src_nents,
+                                DMA_BIDIRECTIONAL);
+               if (!ret)
+                       return -ENOMEM;
+       }
+
+       mv_cesa_tdma_desc_iter_init(&chain);
+       mv_cesa_ablkcipher_req_iter_init(&iter, req);
+
+       do {
+               struct mv_cesa_op_ctx *op;
+
+               op = mv_cesa_dma_add_op(&chain, op_templ, skip_ctx, flags);
+               if (IS_ERR(op)) {
+                       ret = PTR_ERR(op);
+                       goto err_free_tdma;
+               }
+               skip_ctx = true;
+
+               mv_cesa_set_crypt_op_len(op, iter.base.op_len);
+
+               /* Add input transfers */
+               ret = mv_cesa_dma_add_op_transfers(&chain, &iter.base,
+                                                  &iter.src, flags);
+               if (ret)
+                       goto err_free_tdma;
+
+               /* Add dummy desc to launch the crypto operation */
+               ret = mv_cesa_dma_add_dummy_launch(&chain, flags);
+               if (ret)
+                       goto err_free_tdma;
+
+               /* Add output transfers */
+               ret = mv_cesa_dma_add_op_transfers(&chain, &iter.base,
+                                                  &iter.dst, flags);
+               if (ret)
+                       goto err_free_tdma;
+
+       } while (mv_cesa_ablkcipher_req_iter_next_op(&iter));
+
+       dreq->chain = chain;
+
+       return 0;
+
+err_free_tdma:
+       mv_cesa_dma_cleanup(dreq);
+       if (req->dst != req->src)
+               dma_unmap_sg(cesa_dev->dev, req->dst, creq->dst_nents,
+                            DMA_FROM_DEVICE);
+
+err_unmap_src:
+       dma_unmap_sg(cesa_dev->dev, req->src, creq->src_nents,
+                    req->dst != req->src ? DMA_TO_DEVICE : DMA_BIDIRECTIONAL);
+
+       return ret;
+}
+
+static inline int
+mv_cesa_ablkcipher_std_req_init(struct ablkcipher_request *req,
+                               const struct mv_cesa_op_ctx *op_templ)
+{
+       struct mv_cesa_ablkcipher_req *creq = ablkcipher_request_ctx(req);
+       struct mv_cesa_ablkcipher_std_req *sreq = &creq->req.std;
+
+       sreq->base.type = CESA_STD_REQ;
+       sreq->op = *op_templ;
+       sreq->skip_ctx = false;
+
+       return 0;
+}
+
+static int mv_cesa_ablkcipher_req_init(struct ablkcipher_request *req,
+                                      struct mv_cesa_op_ctx *tmpl)
+{
+       struct mv_cesa_ablkcipher_req *creq = ablkcipher_request_ctx(req);
+       struct crypto_ablkcipher *tfm = crypto_ablkcipher_reqtfm(req);
+       unsigned int blksize = crypto_ablkcipher_blocksize(tfm);
+       int ret;
+
+       if (!IS_ALIGNED(req->nbytes, blksize))
+               return -EINVAL;
+
+       creq->src_nents = sg_nents_for_len(req->src, req->nbytes);
+       creq->dst_nents = sg_nents_for_len(req->dst, req->nbytes);
+
+       mv_cesa_update_op_cfg(tmpl, CESA_SA_DESC_CFG_OP_CRYPT_ONLY,
+                             CESA_SA_DESC_CFG_OP_MSK);
+
+       /* TODO: add a threshold for DMA usage */
+       if (cesa_dev->caps->has_tdma)
+               ret = mv_cesa_ablkcipher_dma_req_init(req, tmpl);
+       else
+               ret = mv_cesa_ablkcipher_std_req_init(req, tmpl);
+
+       return ret;
+}
+
+static int mv_cesa_des_op(struct ablkcipher_request *req,
+                         struct mv_cesa_op_ctx *tmpl)
+{
+       struct mv_cesa_des_ctx *ctx = crypto_tfm_ctx(req->base.tfm);
+       int ret;
+
+       mv_cesa_update_op_cfg(tmpl, CESA_SA_DESC_CFG_CRYPTM_DES,
+                             CESA_SA_DESC_CFG_CRYPTM_MSK);
+
+       memcpy(tmpl->ctx.blkcipher.key, ctx->key, DES_KEY_SIZE);
+
+       ret = mv_cesa_ablkcipher_req_init(req, tmpl);
+       if (ret)
+               return ret;
+
+       ret = mv_cesa_queue_req(&req->base);
+       if (ret && ret != -EINPROGRESS)
+               mv_cesa_ablkcipher_cleanup(req);
+
+       return ret;
+}
+
+static int mv_cesa_ecb_des_encrypt(struct ablkcipher_request *req)
+{
+       struct mv_cesa_op_ctx tmpl;
+
+       mv_cesa_set_op_cfg(&tmpl,
+                          CESA_SA_DESC_CFG_CRYPTCM_ECB |
+                          CESA_SA_DESC_CFG_DIR_ENC);
+
+       return mv_cesa_des_op(req, &tmpl);
+}
+
+static int mv_cesa_ecb_des_decrypt(struct ablkcipher_request *req)
+{
+       struct mv_cesa_op_ctx tmpl;
+
+       mv_cesa_set_op_cfg(&tmpl,
+                          CESA_SA_DESC_CFG_CRYPTCM_ECB |
+                          CESA_SA_DESC_CFG_DIR_DEC);
+
+       return mv_cesa_des_op(req, &tmpl);
+}
+
+struct crypto_alg mv_cesa_ecb_des_alg = {
+       .cra_name = "ecb(des)",
+       .cra_driver_name = "mv-ecb-des",
+       .cra_priority = 300,
+       .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER |
+                    CRYPTO_ALG_KERN_DRIVER_ONLY | CRYPTO_ALG_ASYNC,
+       .cra_blocksize = DES_BLOCK_SIZE,
+       .cra_ctxsize = sizeof(struct mv_cesa_des_ctx),
+       .cra_alignmask = 0,
+       .cra_type = &crypto_ablkcipher_type,
+       .cra_module = THIS_MODULE,
+       .cra_init = mv_cesa_ablkcipher_cra_init,
+       .cra_u = {
+               .ablkcipher = {
+                       .min_keysize = DES_KEY_SIZE,
+                       .max_keysize = DES_KEY_SIZE,
+                       .setkey = mv_cesa_des_setkey,
+                       .encrypt = mv_cesa_ecb_des_encrypt,
+                       .decrypt = mv_cesa_ecb_des_decrypt,
+               },
+       },
+};
+
+static int mv_cesa_cbc_des_op(struct ablkcipher_request *req,
+                             struct mv_cesa_op_ctx *tmpl)
+{
+       mv_cesa_update_op_cfg(tmpl, CESA_SA_DESC_CFG_CRYPTCM_CBC,
+                             CESA_SA_DESC_CFG_CRYPTCM_MSK);
+
+       memcpy(tmpl->ctx.blkcipher.iv, req->info, DES_BLOCK_SIZE);
+
+       return mv_cesa_des_op(req, tmpl);
+}
+
+static int mv_cesa_cbc_des_encrypt(struct ablkcipher_request *req)
+{
+       struct mv_cesa_op_ctx tmpl;
+
+       mv_cesa_set_op_cfg(&tmpl, CESA_SA_DESC_CFG_DIR_ENC);
+
+       return mv_cesa_cbc_des_op(req, &tmpl);
+}
+
+static int mv_cesa_cbc_des_decrypt(struct ablkcipher_request *req)
+{
+       struct mv_cesa_op_ctx tmpl;
+
+       mv_cesa_set_op_cfg(&tmpl, CESA_SA_DESC_CFG_DIR_DEC);
+
+       return mv_cesa_cbc_des_op(req, &tmpl);
+}
+
+struct crypto_alg mv_cesa_cbc_des_alg = {
+       .cra_name = "cbc(des)",
+       .cra_driver_name = "mv-cbc-des",
+       .cra_priority = 300,
+       .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER |
+                    CRYPTO_ALG_KERN_DRIVER_ONLY | CRYPTO_ALG_ASYNC,
+       .cra_blocksize = DES_BLOCK_SIZE,
+       .cra_ctxsize = sizeof(struct mv_cesa_des_ctx),
+       .cra_alignmask = 0,
+       .cra_type = &crypto_ablkcipher_type,
+       .cra_module = THIS_MODULE,
+       .cra_init = mv_cesa_ablkcipher_cra_init,
+       .cra_u = {
+               .ablkcipher = {
+                       .min_keysize = DES_KEY_SIZE,
+                       .max_keysize = DES_KEY_SIZE,
+                       .ivsize      = DES_BLOCK_SIZE,
+                       .setkey = mv_cesa_des_setkey,
+                       .encrypt = mv_cesa_cbc_des_encrypt,
+                       .decrypt = mv_cesa_cbc_des_decrypt,
+               },
+       },
+};
+
+static int mv_cesa_des3_op(struct ablkcipher_request *req,
+                          struct mv_cesa_op_ctx *tmpl)
+{
+       struct mv_cesa_des3_ctx *ctx = crypto_tfm_ctx(req->base.tfm);
+       int ret;
+
+       mv_cesa_update_op_cfg(tmpl, CESA_SA_DESC_CFG_CRYPTM_3DES,
+                             CESA_SA_DESC_CFG_CRYPTM_MSK);
+
+       memcpy(tmpl->ctx.blkcipher.key, ctx->key, DES3_EDE_KEY_SIZE);
+
+       ret = mv_cesa_ablkcipher_req_init(req, tmpl);
+       if (ret)
+               return ret;
+
+       ret = mv_cesa_queue_req(&req->base);
+       if (ret && ret != -EINPROGRESS)
+               mv_cesa_ablkcipher_cleanup(req);
+
+       return ret;
+}
+
+static int mv_cesa_ecb_des3_ede_encrypt(struct ablkcipher_request *req)
+{
+       struct mv_cesa_op_ctx tmpl;
+
+       mv_cesa_set_op_cfg(&tmpl,
+                          CESA_SA_DESC_CFG_CRYPTCM_ECB |
+                          CESA_SA_DESC_CFG_3DES_EDE |
+                          CESA_SA_DESC_CFG_DIR_ENC);
+
+       return mv_cesa_des3_op(req, &tmpl);
+}
+
+static int mv_cesa_ecb_des3_ede_decrypt(struct ablkcipher_request *req)
+{
+       struct mv_cesa_op_ctx tmpl;
+
+       mv_cesa_set_op_cfg(&tmpl,
+                          CESA_SA_DESC_CFG_CRYPTCM_ECB |
+                          CESA_SA_DESC_CFG_3DES_EDE |
+                          CESA_SA_DESC_CFG_DIR_DEC);
+
+       return mv_cesa_des3_op(req, &tmpl);
+}
+
+struct crypto_alg mv_cesa_ecb_des3_ede_alg = {
+       .cra_name = "ecb(des3_ede)",
+       .cra_driver_name = "mv-ecb-des3-ede",
+       .cra_priority = 300,
+       .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER |
+                    CRYPTO_ALG_KERN_DRIVER_ONLY | CRYPTO_ALG_ASYNC,
+       .cra_blocksize = DES3_EDE_BLOCK_SIZE,
+       .cra_ctxsize = sizeof(struct mv_cesa_des3_ctx),
+       .cra_alignmask = 0,
+       .cra_type = &crypto_ablkcipher_type,
+       .cra_module = THIS_MODULE,
+       .cra_init = mv_cesa_ablkcipher_cra_init,
+       .cra_u = {
+               .ablkcipher = {
+                       .min_keysize = DES3_EDE_KEY_SIZE,
+                       .max_keysize = DES3_EDE_KEY_SIZE,
+                       .ivsize      = DES3_EDE_BLOCK_SIZE,
+                       .setkey = mv_cesa_des3_ede_setkey,
+                       .encrypt = mv_cesa_ecb_des3_ede_encrypt,
+                       .decrypt = mv_cesa_ecb_des3_ede_decrypt,
+               },
+       },
+};
+
+static int mv_cesa_cbc_des3_op(struct ablkcipher_request *req,
+                              struct mv_cesa_op_ctx *tmpl)
+{
+       memcpy(tmpl->ctx.blkcipher.iv, req->info, DES3_EDE_BLOCK_SIZE);
+
+       return mv_cesa_des3_op(req, tmpl);
+}
+
+static int mv_cesa_cbc_des3_ede_encrypt(struct ablkcipher_request *req)
+{
+       struct mv_cesa_op_ctx tmpl;
+
+       mv_cesa_set_op_cfg(&tmpl,
+                          CESA_SA_DESC_CFG_CRYPTCM_CBC |
+                          CESA_SA_DESC_CFG_3DES_EDE |
+                          CESA_SA_DESC_CFG_DIR_ENC);
+
+       return mv_cesa_cbc_des3_op(req, &tmpl);
+}
+
+static int mv_cesa_cbc_des3_ede_decrypt(struct ablkcipher_request *req)
+{
+       struct mv_cesa_op_ctx tmpl;
+
+       mv_cesa_set_op_cfg(&tmpl,
+                          CESA_SA_DESC_CFG_CRYPTCM_CBC |
+                          CESA_SA_DESC_CFG_3DES_EDE |
+                          CESA_SA_DESC_CFG_DIR_DEC);
+
+       return mv_cesa_cbc_des3_op(req, &tmpl);
+}
+
+struct crypto_alg mv_cesa_cbc_des3_ede_alg = {
+       .cra_name = "cbc(des3_ede)",
+       .cra_driver_name = "mv-cbc-des3-ede",
+       .cra_priority = 300,
+       .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER |
+                    CRYPTO_ALG_KERN_DRIVER_ONLY | CRYPTO_ALG_ASYNC,
+       .cra_blocksize = DES3_EDE_BLOCK_SIZE,
+       .cra_ctxsize = sizeof(struct mv_cesa_des3_ctx),
+       .cra_alignmask = 0,
+       .cra_type = &crypto_ablkcipher_type,
+       .cra_module = THIS_MODULE,
+       .cra_init = mv_cesa_ablkcipher_cra_init,
+       .cra_u = {
+               .ablkcipher = {
+                       .min_keysize = DES3_EDE_KEY_SIZE,
+                       .max_keysize = DES3_EDE_KEY_SIZE,
+                       .ivsize      = DES3_EDE_BLOCK_SIZE,
+                       .setkey = mv_cesa_des3_ede_setkey,
+                       .encrypt = mv_cesa_cbc_des3_ede_encrypt,
+                       .decrypt = mv_cesa_cbc_des3_ede_decrypt,
+               },
+       },
+};
+
+static int mv_cesa_aes_op(struct ablkcipher_request *req,
+                         struct mv_cesa_op_ctx *tmpl)
+{
+       struct mv_cesa_aes_ctx *ctx = crypto_tfm_ctx(req->base.tfm);
+       int ret, i;
+       u32 *key;
+       u32 cfg;
+
+       cfg = CESA_SA_DESC_CFG_CRYPTM_AES;
+
+       if (mv_cesa_get_op_cfg(tmpl) & CESA_SA_DESC_CFG_DIR_DEC)
+               key = ctx->aes.key_dec;
+       else
+               key = ctx->aes.key_enc;
+
+       for (i = 0; i < ctx->aes.key_length / sizeof(u32); i++)
+               tmpl->ctx.blkcipher.key[i] = cpu_to_le32(key[i]);
+
+       if (ctx->aes.key_length == 24)
+               cfg |= CESA_SA_DESC_CFG_AES_LEN_192;
+       else if (ctx->aes.key_length == 32)
+               cfg |= CESA_SA_DESC_CFG_AES_LEN_256;
+
+       mv_cesa_update_op_cfg(tmpl, cfg,
+                             CESA_SA_DESC_CFG_CRYPTM_MSK |
+                             CESA_SA_DESC_CFG_AES_LEN_MSK);
+
+       ret = mv_cesa_ablkcipher_req_init(req, tmpl);
+       if (ret)
+               return ret;
+
+       ret = mv_cesa_queue_req(&req->base);
+       if (ret && ret != -EINPROGRESS)
+               mv_cesa_ablkcipher_cleanup(req);
+
+       return ret;
+}
+
+static int mv_cesa_ecb_aes_encrypt(struct ablkcipher_request *req)
+{
+       struct mv_cesa_op_ctx tmpl;
+
+       mv_cesa_set_op_cfg(&tmpl,
+                          CESA_SA_DESC_CFG_CRYPTCM_ECB |
+                          CESA_SA_DESC_CFG_DIR_ENC);
+
+       return mv_cesa_aes_op(req, &tmpl);
+}
+
+static int mv_cesa_ecb_aes_decrypt(struct ablkcipher_request *req)
+{
+       struct mv_cesa_op_ctx tmpl;
+
+       mv_cesa_set_op_cfg(&tmpl,
+                          CESA_SA_DESC_CFG_CRYPTCM_ECB |
+                          CESA_SA_DESC_CFG_DIR_DEC);
+
+       return mv_cesa_aes_op(req, &tmpl);
+}
+
+struct crypto_alg mv_cesa_ecb_aes_alg = {
+       .cra_name = "ecb(aes)",
+       .cra_driver_name = "mv-ecb-aes",
+       .cra_priority = 300,
+       .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER |
+                    CRYPTO_ALG_KERN_DRIVER_ONLY | CRYPTO_ALG_ASYNC,
+       .cra_blocksize = AES_BLOCK_SIZE,
+       .cra_ctxsize = sizeof(struct mv_cesa_aes_ctx),
+       .cra_alignmask = 0,
+       .cra_type = &crypto_ablkcipher_type,
+       .cra_module = THIS_MODULE,
+       .cra_init = mv_cesa_ablkcipher_cra_init,
+       .cra_u = {
+               .ablkcipher = {
+                       .min_keysize = AES_MIN_KEY_SIZE,
+                       .max_keysize = AES_MAX_KEY_SIZE,
+                       .setkey = mv_cesa_aes_setkey,
+                       .encrypt = mv_cesa_ecb_aes_encrypt,
+                       .decrypt = mv_cesa_ecb_aes_decrypt,
+               },
+       },
+};
+
+static int mv_cesa_cbc_aes_op(struct ablkcipher_request *req,
+                             struct mv_cesa_op_ctx *tmpl)
+{
+       mv_cesa_update_op_cfg(tmpl, CESA_SA_DESC_CFG_CRYPTCM_CBC,
+                             CESA_SA_DESC_CFG_CRYPTCM_MSK);
+       memcpy(tmpl->ctx.blkcipher.iv, req->info, AES_BLOCK_SIZE);
+
+       return mv_cesa_aes_op(req, tmpl);
+}
+
+static int mv_cesa_cbc_aes_encrypt(struct ablkcipher_request *req)
+{
+       struct mv_cesa_op_ctx tmpl;
+
+       mv_cesa_set_op_cfg(&tmpl, CESA_SA_DESC_CFG_DIR_ENC);
+
+       return mv_cesa_cbc_aes_op(req, &tmpl);
+}
+
+static int mv_cesa_cbc_aes_decrypt(struct ablkcipher_request *req)
+{
+       struct mv_cesa_op_ctx tmpl;
+
+       mv_cesa_set_op_cfg(&tmpl, CESA_SA_DESC_CFG_DIR_DEC);
+
+       return mv_cesa_cbc_aes_op(req, &tmpl);
+}
+
+struct crypto_alg mv_cesa_cbc_aes_alg = {
+       .cra_name = "cbc(aes)",
+       .cra_driver_name = "mv-cbc-aes",
+       .cra_priority = 300,
+       .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER |
+                    CRYPTO_ALG_KERN_DRIVER_ONLY | CRYPTO_ALG_ASYNC,
+       .cra_blocksize = AES_BLOCK_SIZE,
+       .cra_ctxsize = sizeof(struct mv_cesa_aes_ctx),
+       .cra_alignmask = 0,
+       .cra_type = &crypto_ablkcipher_type,
+       .cra_module = THIS_MODULE,
+       .cra_init = mv_cesa_ablkcipher_cra_init,
+       .cra_u = {
+               .ablkcipher = {
+                       .min_keysize = AES_MIN_KEY_SIZE,
+                       .max_keysize = AES_MAX_KEY_SIZE,
+                       .ivsize = AES_BLOCK_SIZE,
+                       .setkey = mv_cesa_aes_setkey,
+                       .encrypt = mv_cesa_cbc_aes_encrypt,
+                       .decrypt = mv_cesa_cbc_aes_decrypt,
+               },
+       },
+};
diff --git a/drivers/crypto/marvell/hash.c b/drivers/crypto/marvell/hash.c
new file mode 100644 (file)
index 0000000..ae9272e
--- /dev/null
@@ -0,0 +1,1441 @@
+/*
+ * Hash algorithms supported by the CESA: MD5, SHA1 and SHA256.
+ *
+ * Author: Boris Brezillon <boris.brezillon@free-electrons.com>
+ * Author: Arnaud Ebalard <arno@natisbad.org>
+ *
+ * This work is based on an initial version written by
+ * Sebastian Andrzej Siewior < sebastian at breakpoint dot cc >
+ *
+ * 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 <crypto/md5.h>
+#include <crypto/sha.h>
+
+#include "cesa.h"
+
+struct mv_cesa_ahash_dma_iter {
+       struct mv_cesa_dma_iter base;
+       struct mv_cesa_sg_dma_iter src;
+};
+
+static inline void
+mv_cesa_ahash_req_iter_init(struct mv_cesa_ahash_dma_iter *iter,
+                           struct ahash_request *req)
+{
+       struct mv_cesa_ahash_req *creq = ahash_request_ctx(req);
+       unsigned int len = req->nbytes;
+
+       if (!creq->last_req)
+               len = (len + creq->cache_ptr) & ~CESA_HASH_BLOCK_SIZE_MSK;
+
+       mv_cesa_req_dma_iter_init(&iter->base, len);
+       mv_cesa_sg_dma_iter_init(&iter->src, req->src, DMA_TO_DEVICE);
+       iter->src.op_offset = creq->cache_ptr;
+}
+
+static inline bool
+mv_cesa_ahash_req_iter_next_op(struct mv_cesa_ahash_dma_iter *iter)
+{
+       iter->src.op_offset = 0;
+
+       return mv_cesa_req_dma_iter_next_op(&iter->base);
+}
+
+static inline int mv_cesa_ahash_dma_alloc_cache(struct mv_cesa_ahash_req *creq,
+                                               gfp_t flags)
+{
+       struct mv_cesa_ahash_dma_req *dreq = &creq->req.dma;
+
+       creq->cache = dma_pool_alloc(cesa_dev->dma->cache_pool, flags,
+                                    &dreq->cache_dma);
+       if (!creq->cache)
+               return -ENOMEM;
+
+       return 0;
+}
+
+static inline int mv_cesa_ahash_std_alloc_cache(struct mv_cesa_ahash_req *creq,
+                                               gfp_t flags)
+{
+       creq->cache = kzalloc(CESA_MAX_HASH_BLOCK_SIZE, flags);
+       if (!creq->cache)
+               return -ENOMEM;
+
+       return 0;
+}
+
+static int mv_cesa_ahash_alloc_cache(struct ahash_request *req)
+{
+       struct mv_cesa_ahash_req *creq = ahash_request_ctx(req);
+       gfp_t flags = (req->base.flags & CRYPTO_TFM_REQ_MAY_SLEEP) ?
+                     GFP_KERNEL : GFP_ATOMIC;
+       int ret;
+
+       if (creq->cache)
+               return 0;
+
+       if (creq->req.base.type == CESA_DMA_REQ)
+               ret = mv_cesa_ahash_dma_alloc_cache(creq, flags);
+       else
+               ret = mv_cesa_ahash_std_alloc_cache(creq, flags);
+
+       return ret;
+}
+
+static inline void mv_cesa_ahash_dma_free_cache(struct mv_cesa_ahash_req *creq)
+{
+       dma_pool_free(cesa_dev->dma->cache_pool, creq->cache,
+                     creq->req.dma.cache_dma);
+}
+
+static inline void mv_cesa_ahash_std_free_cache(struct mv_cesa_ahash_req *creq)
+{
+       kfree(creq->cache);
+}
+
+static void mv_cesa_ahash_free_cache(struct mv_cesa_ahash_req *creq)
+{
+       if (!creq->cache)
+               return;
+
+       if (creq->req.base.type == CESA_DMA_REQ)
+               mv_cesa_ahash_dma_free_cache(creq);
+       else
+               mv_cesa_ahash_std_free_cache(creq);
+
+       creq->cache = NULL;
+}
+
+static int mv_cesa_ahash_dma_alloc_padding(struct mv_cesa_ahash_dma_req *req,
+                                          gfp_t flags)
+{
+       if (req->padding)
+               return 0;
+
+       req->padding = dma_pool_alloc(cesa_dev->dma->padding_pool, flags,
+                                     &req->padding_dma);
+       if (!req->padding)
+               return -ENOMEM;
+
+       return 0;
+}
+
+static void mv_cesa_ahash_dma_free_padding(struct mv_cesa_ahash_dma_req *req)
+{
+       if (!req->padding)
+               return;
+
+       dma_pool_free(cesa_dev->dma->padding_pool, req->padding,
+                     req->padding_dma);
+       req->padding = NULL;
+}
+
+static inline void mv_cesa_ahash_dma_last_cleanup(struct ahash_request *req)
+{
+       struct mv_cesa_ahash_req *creq = ahash_request_ctx(req);
+
+       mv_cesa_ahash_dma_free_padding(&creq->req.dma);
+}
+
+static inline void mv_cesa_ahash_dma_cleanup(struct ahash_request *req)
+{
+       struct mv_cesa_ahash_req *creq = ahash_request_ctx(req);
+
+       dma_unmap_sg(cesa_dev->dev, req->src, creq->src_nents, DMA_TO_DEVICE);
+       mv_cesa_dma_cleanup(&creq->req.dma.base);
+}
+
+static inline void mv_cesa_ahash_cleanup(struct ahash_request *req)
+{
+       struct mv_cesa_ahash_req *creq = ahash_request_ctx(req);
+
+       if (creq->req.base.type == CESA_DMA_REQ)
+               mv_cesa_ahash_dma_cleanup(req);
+}
+
+static void mv_cesa_ahash_last_cleanup(struct ahash_request *req)
+{
+       struct mv_cesa_ahash_req *creq = ahash_request_ctx(req);
+
+       mv_cesa_ahash_free_cache(creq);
+
+       if (creq->req.base.type == CESA_DMA_REQ)
+               mv_cesa_ahash_dma_last_cleanup(req);
+}
+
+static int mv_cesa_ahash_pad_len(struct mv_cesa_ahash_req *creq)
+{
+       unsigned int index, padlen;
+
+       index = creq->len & CESA_HASH_BLOCK_SIZE_MSK;
+       padlen = (index < 56) ? (56 - index) : (64 + 56 - index);
+
+       return padlen;
+}
+
+static int mv_cesa_ahash_pad_req(struct mv_cesa_ahash_req *creq, u8 *buf)
+{
+       __be64 bits = cpu_to_be64(creq->len << 3);
+       unsigned int index, padlen;
+
+       buf[0] = 0x80;
+       /* Pad out to 56 mod 64 */
+       index = creq->len & CESA_HASH_BLOCK_SIZE_MSK;
+       padlen = mv_cesa_ahash_pad_len(creq);
+       memset(buf + 1, 0, padlen - 1);
+       memcpy(buf + padlen, &bits, sizeof(bits));
+
+       return padlen + 8;
+}
+
+static void mv_cesa_ahash_std_step(struct ahash_request *req)
+{
+       struct mv_cesa_ahash_req *creq = ahash_request_ctx(req);
+       struct mv_cesa_ahash_std_req *sreq = &creq->req.std;
+       struct mv_cesa_engine *engine = sreq->base.engine;
+       struct mv_cesa_op_ctx *op;
+       unsigned int new_cache_ptr = 0;
+       u32 frag_mode;
+       size_t  len;
+
+       if (creq->cache_ptr)
+               memcpy(engine->sram + CESA_SA_DATA_SRAM_OFFSET, creq->cache,
+                      creq->cache_ptr);
+
+       len = min_t(size_t, req->nbytes + creq->cache_ptr - sreq->offset,
+                   CESA_SA_SRAM_PAYLOAD_SIZE);
+
+       if (!creq->last_req) {
+               new_cache_ptr = len & CESA_HASH_BLOCK_SIZE_MSK;
+               len &= ~CESA_HASH_BLOCK_SIZE_MSK;
+       }
+
+       if (len - creq->cache_ptr)
+               sreq->offset += sg_pcopy_to_buffer(req->src, creq->src_nents,
+                                                  engine->sram +
+                                                  CESA_SA_DATA_SRAM_OFFSET +
+                                                  creq->cache_ptr,
+                                                  len - creq->cache_ptr,
+                                                  sreq->offset);
+
+       op = &creq->op_tmpl;
+
+       frag_mode = mv_cesa_get_op_cfg(op) & CESA_SA_DESC_CFG_FRAG_MSK;
+
+       if (creq->last_req && sreq->offset == req->nbytes &&
+           creq->len <= CESA_SA_DESC_MAC_SRC_TOTAL_LEN_MAX) {
+               if (frag_mode == CESA_SA_DESC_CFG_FIRST_FRAG)
+                       frag_mode = CESA_SA_DESC_CFG_NOT_FRAG;
+               else if (frag_mode == CESA_SA_DESC_CFG_MID_FRAG)
+                       frag_mode = CESA_SA_DESC_CFG_LAST_FRAG;
+       }
+
+       if (frag_mode == CESA_SA_DESC_CFG_NOT_FRAG ||
+           frag_mode == CESA_SA_DESC_CFG_LAST_FRAG) {
+               if (len &&
+                   creq->len <= CESA_SA_DESC_MAC_SRC_TOTAL_LEN_MAX) {
+                       mv_cesa_set_mac_op_total_len(op, creq->len);
+               } else {
+                       int trailerlen = mv_cesa_ahash_pad_len(creq) + 8;
+
+                       if (len + trailerlen > CESA_SA_SRAM_PAYLOAD_SIZE) {
+                               len &= CESA_HASH_BLOCK_SIZE_MSK;
+                               new_cache_ptr = 64 - trailerlen;
+                               memcpy(creq->cache,
+                                      engine->sram +
+                                      CESA_SA_DATA_SRAM_OFFSET + len,
+                                      new_cache_ptr);
+                       } else {
+                               len += mv_cesa_ahash_pad_req(creq,
+                                               engine->sram + len +
+                                               CESA_SA_DATA_SRAM_OFFSET);
+                       }
+
+                       if (frag_mode == CESA_SA_DESC_CFG_LAST_FRAG)
+                               frag_mode = CESA_SA_DESC_CFG_MID_FRAG;
+                       else
+                               frag_mode = CESA_SA_DESC_CFG_FIRST_FRAG;
+               }
+       }
+
+       mv_cesa_set_mac_op_frag_len(op, len);
+       mv_cesa_update_op_cfg(op, frag_mode, CESA_SA_DESC_CFG_FRAG_MSK);
+
+       /* FIXME: only update enc_len field */
+       memcpy(engine->sram, op, sizeof(*op));
+
+       if (frag_mode == CESA_SA_DESC_CFG_FIRST_FRAG)
+               mv_cesa_update_op_cfg(op, CESA_SA_DESC_CFG_MID_FRAG,
+                                     CESA_SA_DESC_CFG_FRAG_MSK);
+
+       creq->cache_ptr = new_cache_ptr;
+
+       mv_cesa_set_int_mask(engine, CESA_SA_INT_ACCEL0_DONE);
+       writel(CESA_SA_CFG_PARA_DIS, engine->regs + CESA_SA_CFG);
+       writel(CESA_SA_CMD_EN_CESA_SA_ACCL0, engine->regs + CESA_SA_CMD);
+}
+
+static int mv_cesa_ahash_std_process(struct ahash_request *req, u32 status)
+{
+       struct mv_cesa_ahash_req *creq = ahash_request_ctx(req);
+       struct mv_cesa_ahash_std_req *sreq = &creq->req.std;
+
+       if (sreq->offset < (req->nbytes - creq->cache_ptr))
+               return -EINPROGRESS;
+
+       return 0;
+}
+
+static inline void mv_cesa_ahash_dma_prepare(struct ahash_request *req)
+{
+       struct mv_cesa_ahash_req *creq = ahash_request_ctx(req);
+       struct mv_cesa_tdma_req *dreq = &creq->req.dma.base;
+
+       mv_cesa_dma_prepare(dreq, dreq->base.engine);
+}
+
+static void mv_cesa_ahash_std_prepare(struct ahash_request *req)
+{
+       struct mv_cesa_ahash_req *creq = ahash_request_ctx(req);
+       struct mv_cesa_ahash_std_req *sreq = &creq->req.std;
+       struct mv_cesa_engine *engine = sreq->base.engine;
+
+       sreq->offset = 0;
+       mv_cesa_adjust_op(engine, &creq->op_tmpl);
+       memcpy(engine->sram, &creq->op_tmpl, sizeof(creq->op_tmpl));
+}
+
+static void mv_cesa_ahash_step(struct crypto_async_request *req)
+{
+       struct ahash_request *ahashreq = ahash_request_cast(req);
+       struct mv_cesa_ahash_req *creq = ahash_request_ctx(ahashreq);
+
+       if (creq->req.base.type == CESA_DMA_REQ)
+               mv_cesa_dma_step(&creq->req.dma.base);
+       else
+               mv_cesa_ahash_std_step(ahashreq);
+}
+
+static int mv_cesa_ahash_process(struct crypto_async_request *req, u32 status)
+{
+       struct ahash_request *ahashreq = ahash_request_cast(req);
+       struct mv_cesa_ahash_req *creq = ahash_request_ctx(ahashreq);
+       struct mv_cesa_engine *engine = creq->req.base.engine;
+       unsigned int digsize;
+       int ret, i;
+
+       if (creq->req.base.type == CESA_DMA_REQ)
+               ret = mv_cesa_dma_process(&creq->req.dma.base, status);
+       else
+               ret = mv_cesa_ahash_std_process(ahashreq, status);
+
+       if (ret == -EINPROGRESS)
+               return ret;
+
+       digsize = crypto_ahash_digestsize(crypto_ahash_reqtfm(ahashreq));
+       for (i = 0; i < digsize / 4; i++)
+               creq->state[i] = readl(engine->regs + CESA_IVDIG(i));
+
+       if (creq->cache_ptr)
+               sg_pcopy_to_buffer(ahashreq->src, creq->src_nents,
+                                  creq->cache,
+                                  creq->cache_ptr,
+                                  ahashreq->nbytes - creq->cache_ptr);
+
+       if (creq->last_req) {
+               for (i = 0; i < digsize / 4; i++) {
+                       /*
+                        * Hardware provides MD5 digest in a different
+                        * endianness than SHA-1 and SHA-256 ones.
+                        */
+                       if (digsize == MD5_DIGEST_SIZE)
+                               creq->state[i] = cpu_to_le32(creq->state[i]);
+                       else
+                               creq->state[i] = cpu_to_be32(creq->state[i]);
+               }
+
+               memcpy(ahashreq->result, creq->state, digsize);
+       }
+
+       return ret;
+}
+
+static void mv_cesa_ahash_prepare(struct crypto_async_request *req,
+                                 struct mv_cesa_engine *engine)
+{
+       struct ahash_request *ahashreq = ahash_request_cast(req);
+       struct mv_cesa_ahash_req *creq = ahash_request_ctx(ahashreq);
+       unsigned int digsize;
+       int i;
+
+       creq->req.base.engine = engine;
+
+       if (creq->req.base.type == CESA_DMA_REQ)
+               mv_cesa_ahash_dma_prepare(ahashreq);
+       else
+               mv_cesa_ahash_std_prepare(ahashreq);
+
+       digsize = crypto_ahash_digestsize(crypto_ahash_reqtfm(ahashreq));
+       for (i = 0; i < digsize / 4; i++)
+               writel(creq->state[i],
+                      engine->regs + CESA_IVDIG(i));
+}
+
+static void mv_cesa_ahash_req_cleanup(struct crypto_async_request *req)
+{
+       struct ahash_request *ahashreq = ahash_request_cast(req);
+       struct mv_cesa_ahash_req *creq = ahash_request_ctx(ahashreq);
+
+       if (creq->last_req)
+               mv_cesa_ahash_last_cleanup(ahashreq);
+
+       mv_cesa_ahash_cleanup(ahashreq);
+}
+
+static const struct mv_cesa_req_ops mv_cesa_ahash_req_ops = {
+       .step = mv_cesa_ahash_step,
+       .process = mv_cesa_ahash_process,
+       .prepare = mv_cesa_ahash_prepare,
+       .cleanup = mv_cesa_ahash_req_cleanup,
+};
+
+static int mv_cesa_ahash_init(struct ahash_request *req,
+                             struct mv_cesa_op_ctx *tmpl)
+{
+       struct mv_cesa_ahash_req *creq = ahash_request_ctx(req);
+
+       memset(creq, 0, sizeof(*creq));
+       mv_cesa_update_op_cfg(tmpl,
+                             CESA_SA_DESC_CFG_OP_MAC_ONLY |
+                             CESA_SA_DESC_CFG_FIRST_FRAG,
+                             CESA_SA_DESC_CFG_OP_MSK |
+                             CESA_SA_DESC_CFG_FRAG_MSK);
+       mv_cesa_set_mac_op_total_len(tmpl, 0);
+       mv_cesa_set_mac_op_frag_len(tmpl, 0);
+       creq->op_tmpl = *tmpl;
+       creq->len = 0;
+
+       return 0;
+}
+
+static inline int mv_cesa_ahash_cra_init(struct crypto_tfm *tfm)
+{
+       struct mv_cesa_hash_ctx *ctx = crypto_tfm_ctx(tfm);
+
+       ctx->base.ops = &mv_cesa_ahash_req_ops;
+
+       crypto_ahash_set_reqsize(__crypto_ahash_cast(tfm),
+                                sizeof(struct mv_cesa_ahash_req));
+       return 0;
+}
+
+static int mv_cesa_ahash_cache_req(struct ahash_request *req, bool *cached)
+{
+       struct mv_cesa_ahash_req *creq = ahash_request_ctx(req);
+       int ret;
+
+       if (((creq->cache_ptr + req->nbytes) & CESA_HASH_BLOCK_SIZE_MSK) &&
+           !creq->last_req) {
+               ret = mv_cesa_ahash_alloc_cache(req);
+               if (ret)
+                       return ret;
+       }
+
+       if (creq->cache_ptr + req->nbytes < 64 && !creq->last_req) {
+               *cached = true;
+
+               if (!req->nbytes)
+                       return 0;
+
+               sg_pcopy_to_buffer(req->src, creq->src_nents,
+                                  creq->cache + creq->cache_ptr,
+                                  req->nbytes, 0);
+
+               creq->cache_ptr += req->nbytes;
+       }
+
+       return 0;
+}
+
+static struct mv_cesa_op_ctx *
+mv_cesa_ahash_dma_add_cache(struct mv_cesa_tdma_chain *chain,
+                           struct mv_cesa_ahash_dma_iter *dma_iter,
+                           struct mv_cesa_ahash_req *creq,
+                           gfp_t flags)
+{
+       struct mv_cesa_ahash_dma_req *ahashdreq = &creq->req.dma;
+       struct mv_cesa_op_ctx *op = NULL;
+       int ret;
+
+       if (!creq->cache_ptr)
+               return NULL;
+
+       ret = mv_cesa_dma_add_data_transfer(chain,
+                                           CESA_SA_DATA_SRAM_OFFSET,
+                                           ahashdreq->cache_dma,
+                                           creq->cache_ptr,
+                                           CESA_TDMA_DST_IN_SRAM,
+                                           flags);
+       if (ret)
+               return ERR_PTR(ret);
+
+       if (!dma_iter->base.op_len) {
+               op = mv_cesa_dma_add_op(chain, &creq->op_tmpl, false, flags);
+               if (IS_ERR(op))
+                       return op;
+
+               mv_cesa_set_mac_op_frag_len(op, creq->cache_ptr);
+
+               /* Add dummy desc to launch crypto operation */
+               ret = mv_cesa_dma_add_dummy_launch(chain, flags);
+               if (ret)
+                       return ERR_PTR(ret);
+       }
+
+       return op;
+}
+
+static struct mv_cesa_op_ctx *
+mv_cesa_ahash_dma_add_data(struct mv_cesa_tdma_chain *chain,
+                          struct mv_cesa_ahash_dma_iter *dma_iter,
+                          struct mv_cesa_ahash_req *creq,
+                          gfp_t flags)
+{
+       struct mv_cesa_op_ctx *op;
+       int ret;
+
+       op = mv_cesa_dma_add_op(chain, &creq->op_tmpl, false, flags);
+       if (IS_ERR(op))
+               return op;
+
+       mv_cesa_set_mac_op_frag_len(op, dma_iter->base.op_len);
+
+       if ((mv_cesa_get_op_cfg(&creq->op_tmpl) & CESA_SA_DESC_CFG_FRAG_MSK) ==
+           CESA_SA_DESC_CFG_FIRST_FRAG)
+               mv_cesa_update_op_cfg(&creq->op_tmpl,
+                                     CESA_SA_DESC_CFG_MID_FRAG,
+                                     CESA_SA_DESC_CFG_FRAG_MSK);
+
+       /* Add input transfers */
+       ret = mv_cesa_dma_add_op_transfers(chain, &dma_iter->base,
+                                          &dma_iter->src, flags);
+       if (ret)
+               return ERR_PTR(ret);
+
+       /* Add dummy desc to launch crypto operation */
+       ret = mv_cesa_dma_add_dummy_launch(chain, flags);
+       if (ret)
+               return ERR_PTR(ret);
+
+       return op;
+}
+
+static struct mv_cesa_op_ctx *
+mv_cesa_ahash_dma_last_req(struct mv_cesa_tdma_chain *chain,
+                          struct mv_cesa_ahash_dma_iter *dma_iter,
+                          struct mv_cesa_ahash_req *creq,
+                          struct mv_cesa_op_ctx *op,
+                          gfp_t flags)
+{
+       struct mv_cesa_ahash_dma_req *ahashdreq = &creq->req.dma;
+       unsigned int len, trailerlen, padoff = 0;
+       int ret;
+
+       if (!creq->last_req)
+               return op;
+
+       if (op && creq->len <= CESA_SA_DESC_MAC_SRC_TOTAL_LEN_MAX) {
+               u32 frag = CESA_SA_DESC_CFG_NOT_FRAG;
+
+               if ((mv_cesa_get_op_cfg(op) & CESA_SA_DESC_CFG_FRAG_MSK) !=
+                   CESA_SA_DESC_CFG_FIRST_FRAG)
+                       frag = CESA_SA_DESC_CFG_LAST_FRAG;
+
+               mv_cesa_update_op_cfg(op, frag, CESA_SA_DESC_CFG_FRAG_MSK);
+
+               return op;
+       }
+
+       ret = mv_cesa_ahash_dma_alloc_padding(ahashdreq, flags);
+       if (ret)
+               return ERR_PTR(ret);
+
+       trailerlen = mv_cesa_ahash_pad_req(creq, ahashdreq->padding);
+
+       if (op) {
+               len = min(CESA_SA_SRAM_PAYLOAD_SIZE - dma_iter->base.op_len,
+                         trailerlen);
+               if (len) {
+                       ret = mv_cesa_dma_add_data_transfer(chain,
+                                               CESA_SA_DATA_SRAM_OFFSET +
+                                               dma_iter->base.op_len,
+                                               ahashdreq->padding_dma,
+                                               len, CESA_TDMA_DST_IN_SRAM,
+                                               flags);
+                       if (ret)
+                               return ERR_PTR(ret);
+
+                       mv_cesa_update_op_cfg(op, CESA_SA_DESC_CFG_MID_FRAG,
+                                             CESA_SA_DESC_CFG_FRAG_MSK);
+                       mv_cesa_set_mac_op_frag_len(op,
+                                       dma_iter->base.op_len + len);
+                       padoff += len;
+               }
+       }
+
+       if (padoff >= trailerlen)
+               return op;
+
+       if ((mv_cesa_get_op_cfg(&creq->op_tmpl) & CESA_SA_DESC_CFG_FRAG_MSK) !=
+           CESA_SA_DESC_CFG_FIRST_FRAG)
+               mv_cesa_update_op_cfg(&creq->op_tmpl,
+                                     CESA_SA_DESC_CFG_MID_FRAG,
+                                     CESA_SA_DESC_CFG_FRAG_MSK);
+
+       op = mv_cesa_dma_add_op(chain, &creq->op_tmpl, false, flags);
+       if (IS_ERR(op))
+               return op;
+
+       mv_cesa_set_mac_op_frag_len(op, trailerlen - padoff);
+
+       ret = mv_cesa_dma_add_data_transfer(chain,
+                                           CESA_SA_DATA_SRAM_OFFSET,
+                                           ahashdreq->padding_dma +
+                                           padoff,
+                                           trailerlen - padoff,
+                                           CESA_TDMA_DST_IN_SRAM,
+                                           flags);
+       if (ret)
+               return ERR_PTR(ret);
+
+       /* Add dummy desc to launch crypto operation */
+       ret = mv_cesa_dma_add_dummy_launch(chain, flags);
+       if (ret)
+               return ERR_PTR(ret);
+
+       return op;
+}
+
+static int mv_cesa_ahash_dma_req_init(struct ahash_request *req)
+{
+       struct mv_cesa_ahash_req *creq = ahash_request_ctx(req);
+       gfp_t flags = (req->base.flags & CRYPTO_TFM_REQ_MAY_SLEEP) ?
+                     GFP_KERNEL : GFP_ATOMIC;
+       struct mv_cesa_ahash_dma_req *ahashdreq = &creq->req.dma;
+       struct mv_cesa_tdma_req *dreq = &ahashdreq->base;
+       struct mv_cesa_tdma_chain chain;
+       struct mv_cesa_ahash_dma_iter iter;
+       struct mv_cesa_op_ctx *op = NULL;
+       int ret;
+
+       dreq->chain.first = NULL;
+       dreq->chain.last = NULL;
+
+       if (creq->src_nents) {
+               ret = dma_map_sg(cesa_dev->dev, req->src, creq->src_nents,
+                                DMA_TO_DEVICE);
+               if (!ret) {
+                       ret = -ENOMEM;
+                       goto err;
+               }
+       }
+
+       mv_cesa_tdma_desc_iter_init(&chain);
+       mv_cesa_ahash_req_iter_init(&iter, req);
+
+       op = mv_cesa_ahash_dma_add_cache(&chain, &iter,
+                                        creq, flags);
+       if (IS_ERR(op)) {
+               ret = PTR_ERR(op);
+               goto err_free_tdma;
+       }
+
+       do {
+               if (!iter.base.op_len)
+                       break;
+
+               op = mv_cesa_ahash_dma_add_data(&chain, &iter,
+                                               creq, flags);
+               if (IS_ERR(op)) {
+                       ret = PTR_ERR(op);
+                       goto err_free_tdma;
+               }
+       } while (mv_cesa_ahash_req_iter_next_op(&iter));
+
+       op = mv_cesa_ahash_dma_last_req(&chain, &iter, creq, op, flags);
+       if (IS_ERR(op)) {
+               ret = PTR_ERR(op);
+               goto err_free_tdma;
+       }
+
+       if (op) {
+               /* Add dummy desc to wait for crypto operation end */
+               ret = mv_cesa_dma_add_dummy_end(&chain, flags);
+               if (ret)
+                       goto err_free_tdma;
+       }
+
+       if (!creq->last_req)
+               creq->cache_ptr = req->nbytes + creq->cache_ptr -
+                                 iter.base.len;
+       else
+               creq->cache_ptr = 0;
+
+       dreq->chain = chain;
+
+       return 0;
+
+err_free_tdma:
+       mv_cesa_dma_cleanup(dreq);
+       dma_unmap_sg(cesa_dev->dev, req->src, creq->src_nents, DMA_TO_DEVICE);
+
+err:
+       mv_cesa_ahash_last_cleanup(req);
+
+       return ret;
+}
+
+static int mv_cesa_ahash_req_init(struct ahash_request *req, bool *cached)
+{
+       struct mv_cesa_ahash_req *creq = ahash_request_ctx(req);
+       int ret;
+
+       if (cesa_dev->caps->has_tdma)
+               creq->req.base.type = CESA_DMA_REQ;
+       else
+               creq->req.base.type = CESA_STD_REQ;
+
+       creq->src_nents = sg_nents_for_len(req->src, req->nbytes);
+
+       ret = mv_cesa_ahash_cache_req(req, cached);
+       if (ret)
+               return ret;
+
+       if (*cached)
+               return 0;
+
+       if (creq->req.base.type == CESA_DMA_REQ)
+               ret = mv_cesa_ahash_dma_req_init(req);
+
+       return ret;
+}
+
+static int mv_cesa_ahash_update(struct ahash_request *req)
+{
+       struct mv_cesa_ahash_req *creq = ahash_request_ctx(req);
+       bool cached = false;
+       int ret;
+
+       creq->len += req->nbytes;
+       ret = mv_cesa_ahash_req_init(req, &cached);
+       if (ret)
+               return ret;
+
+       if (cached)
+               return 0;
+
+       ret = mv_cesa_queue_req(&req->base);
+       if (ret && ret != -EINPROGRESS) {
+               mv_cesa_ahash_cleanup(req);
+               return ret;
+       }
+
+       return ret;
+}
+
+static int mv_cesa_ahash_final(struct ahash_request *req)
+{
+       struct mv_cesa_ahash_req *creq = ahash_request_ctx(req);
+       struct mv_cesa_op_ctx *tmpl = &creq->op_tmpl;
+       bool cached = false;
+       int ret;
+
+       mv_cesa_set_mac_op_total_len(tmpl, creq->len);
+       creq->last_req = true;
+       req->nbytes = 0;
+
+       ret = mv_cesa_ahash_req_init(req, &cached);
+       if (ret)
+               return ret;
+
+       if (cached)
+               return 0;
+
+       ret = mv_cesa_queue_req(&req->base);
+       if (ret && ret != -EINPROGRESS)
+               mv_cesa_ahash_cleanup(req);
+
+       return ret;
+}
+
+static int mv_cesa_ahash_finup(struct ahash_request *req)
+{
+       struct mv_cesa_ahash_req *creq = ahash_request_ctx(req);
+       struct mv_cesa_op_ctx *tmpl = &creq->op_tmpl;
+       bool cached = false;
+       int ret;
+
+       creq->len += req->nbytes;
+       mv_cesa_set_mac_op_total_len(tmpl, creq->len);
+       creq->last_req = true;
+
+       ret = mv_cesa_ahash_req_init(req, &cached);
+       if (ret)
+               return ret;
+
+       if (cached)
+               return 0;
+
+       ret = mv_cesa_queue_req(&req->base);
+       if (ret && ret != -EINPROGRESS)
+               mv_cesa_ahash_cleanup(req);
+
+       return ret;
+}
+
+static int mv_cesa_md5_init(struct ahash_request *req)
+{
+       struct mv_cesa_op_ctx tmpl;
+
+       mv_cesa_set_op_cfg(&tmpl, CESA_SA_DESC_CFG_MACM_MD5);
+
+       mv_cesa_ahash_init(req, &tmpl);
+
+       return 0;
+}
+
+static int mv_cesa_md5_export(struct ahash_request *req, void *out)
+{
+       struct md5_state *out_state = out;
+       struct crypto_ahash *ahash = crypto_ahash_reqtfm(req);
+       struct mv_cesa_ahash_req *creq = ahash_request_ctx(req);
+       unsigned int digsize = crypto_ahash_digestsize(ahash);
+
+       out_state->byte_count = creq->len;
+       memcpy(out_state->hash, creq->state, digsize);
+       memset(out_state->block, 0, sizeof(out_state->block));
+       if (creq->cache)
+               memcpy(out_state->block, creq->cache, creq->cache_ptr);
+
+       return 0;
+}
+
+static int mv_cesa_md5_import(struct ahash_request *req, const void *in)
+{
+       const struct md5_state *in_state = in;
+       struct crypto_ahash *ahash = crypto_ahash_reqtfm(req);
+       struct mv_cesa_ahash_req *creq = ahash_request_ctx(req);
+       unsigned int digsize = crypto_ahash_digestsize(ahash);
+       unsigned int cache_ptr;
+       int ret;
+
+       creq->len = in_state->byte_count;
+       memcpy(creq->state, in_state->hash, digsize);
+       creq->cache_ptr = 0;
+
+       cache_ptr = creq->len % sizeof(in_state->block);
+       if (!cache_ptr)
+               return 0;
+
+       ret = mv_cesa_ahash_alloc_cache(req);
+       if (ret)
+               return ret;
+
+       memcpy(creq->cache, in_state->block, cache_ptr);
+       creq->cache_ptr = cache_ptr;
+
+       return 0;
+}
+
+static int mv_cesa_md5_digest(struct ahash_request *req)
+{
+       int ret;
+
+       ret = mv_cesa_md5_init(req);
+       if (ret)
+               return ret;
+
+       return mv_cesa_ahash_finup(req);
+}
+
+struct ahash_alg mv_md5_alg = {
+       .init = mv_cesa_md5_init,
+       .update = mv_cesa_ahash_update,
+       .final = mv_cesa_ahash_final,
+       .finup = mv_cesa_ahash_finup,
+       .digest = mv_cesa_md5_digest,
+       .export = mv_cesa_md5_export,
+       .import = mv_cesa_md5_import,
+       .halg = {
+               .digestsize = MD5_DIGEST_SIZE,
+               .base = {
+                       .cra_name = "md5",
+                       .cra_driver_name = "mv-md5",
+                       .cra_priority = 300,
+                       .cra_flags = CRYPTO_ALG_ASYNC |
+                                    CRYPTO_ALG_KERN_DRIVER_ONLY,
+                       .cra_blocksize = MD5_HMAC_BLOCK_SIZE,
+                       .cra_ctxsize = sizeof(struct mv_cesa_hash_ctx),
+                       .cra_init = mv_cesa_ahash_cra_init,
+                       .cra_module = THIS_MODULE,
+                }
+       }
+};
+
+static int mv_cesa_sha1_init(struct ahash_request *req)
+{
+       struct mv_cesa_op_ctx tmpl;
+
+       mv_cesa_set_op_cfg(&tmpl, CESA_SA_DESC_CFG_MACM_SHA1);
+
+       mv_cesa_ahash_init(req, &tmpl);
+
+       return 0;
+}
+
+static int mv_cesa_sha1_export(struct ahash_request *req, void *out)
+{
+       struct sha1_state *out_state = out;
+       struct crypto_ahash *ahash = crypto_ahash_reqtfm(req);
+       struct mv_cesa_ahash_req *creq = ahash_request_ctx(req);
+       unsigned int digsize = crypto_ahash_digestsize(ahash);
+
+       out_state->count = creq->len;
+       memcpy(out_state->state, creq->state, digsize);
+       memset(out_state->buffer, 0, sizeof(out_state->buffer));
+       if (creq->cache)
+               memcpy(out_state->buffer, creq->cache, creq->cache_ptr);
+
+       return 0;
+}
+
+static int mv_cesa_sha1_import(struct ahash_request *req, const void *in)
+{
+       const struct sha1_state *in_state = in;
+       struct crypto_ahash *ahash = crypto_ahash_reqtfm(req);
+       struct mv_cesa_ahash_req *creq = ahash_request_ctx(req);
+       unsigned int digsize = crypto_ahash_digestsize(ahash);
+       unsigned int cache_ptr;
+       int ret;
+
+       creq->len = in_state->count;
+       memcpy(creq->state, in_state->state, digsize);
+       creq->cache_ptr = 0;
+
+       cache_ptr = creq->len % SHA1_BLOCK_SIZE;
+       if (!cache_ptr)
+               return 0;
+
+       ret = mv_cesa_ahash_alloc_cache(req);
+       if (ret)
+               return ret;
+
+       memcpy(creq->cache, in_state->buffer, cache_ptr);
+       creq->cache_ptr = cache_ptr;
+
+       return 0;
+}
+
+static int mv_cesa_sha1_digest(struct ahash_request *req)
+{
+       int ret;
+
+       ret = mv_cesa_sha1_init(req);
+       if (ret)
+               return ret;
+
+       return mv_cesa_ahash_finup(req);
+}
+
+struct ahash_alg mv_sha1_alg = {
+       .init = mv_cesa_sha1_init,
+       .update = mv_cesa_ahash_update,
+       .final = mv_cesa_ahash_final,
+       .finup = mv_cesa_ahash_finup,
+       .digest = mv_cesa_sha1_digest,
+       .export = mv_cesa_sha1_export,
+       .import = mv_cesa_sha1_import,
+       .halg = {
+               .digestsize = SHA1_DIGEST_SIZE,
+               .base = {
+                       .cra_name = "sha1",
+                       .cra_driver_name = "mv-sha1",
+                       .cra_priority = 300,
+                       .cra_flags = CRYPTO_ALG_ASYNC |
+                                    CRYPTO_ALG_KERN_DRIVER_ONLY,
+                       .cra_blocksize = SHA1_BLOCK_SIZE,
+                       .cra_ctxsize = sizeof(struct mv_cesa_hash_ctx),
+                       .cra_init = mv_cesa_ahash_cra_init,
+                       .cra_module = THIS_MODULE,
+                }
+       }
+};
+
+static int mv_cesa_sha256_init(struct ahash_request *req)
+{
+       struct mv_cesa_op_ctx tmpl;
+
+       mv_cesa_set_op_cfg(&tmpl, CESA_SA_DESC_CFG_MACM_SHA256);
+
+       mv_cesa_ahash_init(req, &tmpl);
+
+       return 0;
+}
+
+static int mv_cesa_sha256_digest(struct ahash_request *req)
+{
+       int ret;
+
+       ret = mv_cesa_sha256_init(req);
+       if (ret)
+               return ret;
+
+       return mv_cesa_ahash_finup(req);
+}
+
+static int mv_cesa_sha256_export(struct ahash_request *req, void *out)
+{
+       struct sha256_state *out_state = out;
+       struct crypto_ahash *ahash = crypto_ahash_reqtfm(req);
+       struct mv_cesa_ahash_req *creq = ahash_request_ctx(req);
+       unsigned int ds = crypto_ahash_digestsize(ahash);
+
+       out_state->count = creq->len;
+       memcpy(out_state->state, creq->state, ds);
+       memset(out_state->buf, 0, sizeof(out_state->buf));
+       if (creq->cache)
+               memcpy(out_state->buf, creq->cache, creq->cache_ptr);
+
+       return 0;
+}
+
+static int mv_cesa_sha256_import(struct ahash_request *req, const void *in)
+{
+       const struct sha256_state *in_state = in;
+       struct crypto_ahash *ahash = crypto_ahash_reqtfm(req);
+       struct mv_cesa_ahash_req *creq = ahash_request_ctx(req);
+       unsigned int digsize = crypto_ahash_digestsize(ahash);
+       unsigned int cache_ptr;
+       int ret;
+
+       creq->len = in_state->count;
+       memcpy(creq->state, in_state->state, digsize);
+       creq->cache_ptr = 0;
+
+       cache_ptr = creq->len % SHA256_BLOCK_SIZE;
+       if (!cache_ptr)
+               return 0;
+
+       ret = mv_cesa_ahash_alloc_cache(req);
+       if (ret)
+               return ret;
+
+       memcpy(creq->cache, in_state->buf, cache_ptr);
+       creq->cache_ptr = cache_ptr;
+
+       return 0;
+}
+
+struct ahash_alg mv_sha256_alg = {
+       .init = mv_cesa_sha256_init,
+       .update = mv_cesa_ahash_update,
+       .final = mv_cesa_ahash_final,
+       .finup = mv_cesa_ahash_finup,
+       .digest = mv_cesa_sha256_digest,
+       .export = mv_cesa_sha256_export,
+       .import = mv_cesa_sha256_import,
+       .halg = {
+               .digestsize = SHA256_DIGEST_SIZE,
+               .base = {
+                       .cra_name = "sha256",
+                       .cra_driver_name = "mv-sha256",
+                       .cra_priority = 300,
+                       .cra_flags = CRYPTO_ALG_ASYNC |
+                                    CRYPTO_ALG_KERN_DRIVER_ONLY,
+                       .cra_blocksize = SHA256_BLOCK_SIZE,
+                       .cra_ctxsize = sizeof(struct mv_cesa_hash_ctx),
+                       .cra_init = mv_cesa_ahash_cra_init,
+                       .cra_module = THIS_MODULE,
+                }
+       }
+};
+
+struct mv_cesa_ahash_result {
+       struct completion completion;
+       int error;
+};
+
+static void mv_cesa_hmac_ahash_complete(struct crypto_async_request *req,
+                                       int error)
+{
+       struct mv_cesa_ahash_result *result = req->data;
+
+       if (error == -EINPROGRESS)
+               return;
+
+       result->error = error;
+       complete(&result->completion);
+}
+
+static int mv_cesa_ahmac_iv_state_init(struct ahash_request *req, u8 *pad,
+                                      void *state, unsigned int blocksize)
+{
+       struct mv_cesa_ahash_result result;
+       struct scatterlist sg;
+       int ret;
+
+       ahash_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
+                                  mv_cesa_hmac_ahash_complete, &result);
+       sg_init_one(&sg, pad, blocksize);
+       ahash_request_set_crypt(req, &sg, pad, blocksize);
+       init_completion(&result.completion);
+
+       ret = crypto_ahash_init(req);
+       if (ret)
+               return ret;
+
+       ret = crypto_ahash_update(req);
+       if (ret && ret != -EINPROGRESS)
+               return ret;
+
+       wait_for_completion_interruptible(&result.completion);
+       if (result.error)
+               return result.error;
+
+       ret = crypto_ahash_export(req, state);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+static int mv_cesa_ahmac_pad_init(struct ahash_request *req,
+                                 const u8 *key, unsigned int keylen,
+                                 u8 *ipad, u8 *opad,
+                                 unsigned int blocksize)
+{
+       struct mv_cesa_ahash_result result;
+       struct scatterlist sg;
+       int ret;
+       int i;
+
+       if (keylen <= blocksize) {
+               memcpy(ipad, key, keylen);
+       } else {
+               u8 *keydup = kmemdup(key, keylen, GFP_KERNEL);
+
+               if (!keydup)
+                       return -ENOMEM;
+
+               ahash_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
+                                          mv_cesa_hmac_ahash_complete,
+                                          &result);
+               sg_init_one(&sg, keydup, keylen);
+               ahash_request_set_crypt(req, &sg, ipad, keylen);
+               init_completion(&result.completion);
+
+               ret = crypto_ahash_digest(req);
+               if (ret == -EINPROGRESS) {
+                       wait_for_completion_interruptible(&result.completion);
+                       ret = result.error;
+               }
+
+               /* Set the memory region to 0 to avoid any leak. */
+               memset(keydup, 0, keylen);
+               kfree(keydup);
+
+               if (ret)
+                       return ret;
+
+               keylen = crypto_ahash_digestsize(crypto_ahash_reqtfm(req));
+       }
+
+       memset(ipad + keylen, 0, blocksize - keylen);
+       memcpy(opad, ipad, blocksize);
+
+       for (i = 0; i < blocksize; i++) {
+               ipad[i] ^= 0x36;
+               opad[i] ^= 0x5c;
+       }
+
+       return 0;
+}
+
+static int mv_cesa_ahmac_setkey(const char *hash_alg_name,
+                               const u8 *key, unsigned int keylen,
+                               void *istate, void *ostate)
+{
+       struct ahash_request *req;
+       struct crypto_ahash *tfm;
+       unsigned int blocksize;
+       u8 *ipad = NULL;
+       u8 *opad;
+       int ret;
+
+       tfm = crypto_alloc_ahash(hash_alg_name, CRYPTO_ALG_TYPE_AHASH,
+                                CRYPTO_ALG_TYPE_AHASH_MASK);
+       if (IS_ERR(tfm))
+               return PTR_ERR(tfm);
+
+       req = ahash_request_alloc(tfm, GFP_KERNEL);
+       if (!req) {
+               ret = -ENOMEM;
+               goto free_ahash;
+       }
+
+       crypto_ahash_clear_flags(tfm, ~0);
+
+       blocksize = crypto_tfm_alg_blocksize(crypto_ahash_tfm(tfm));
+
+       ipad = kzalloc(2 * blocksize, GFP_KERNEL);
+       if (!ipad) {
+               ret = -ENOMEM;
+               goto free_req;
+       }
+
+       opad = ipad + blocksize;
+
+       ret = mv_cesa_ahmac_pad_init(req, key, keylen, ipad, opad, blocksize);
+       if (ret)
+               goto free_ipad;
+
+       ret = mv_cesa_ahmac_iv_state_init(req, ipad, istate, blocksize);
+       if (ret)
+               goto free_ipad;
+
+       ret = mv_cesa_ahmac_iv_state_init(req, opad, ostate, blocksize);
+
+free_ipad:
+       kfree(ipad);
+free_req:
+       ahash_request_free(req);
+free_ahash:
+       crypto_free_ahash(tfm);
+
+       return ret;
+}
+
+static int mv_cesa_ahmac_cra_init(struct crypto_tfm *tfm)
+{
+       struct mv_cesa_hmac_ctx *ctx = crypto_tfm_ctx(tfm);
+
+       ctx->base.ops = &mv_cesa_ahash_req_ops;
+
+       crypto_ahash_set_reqsize(__crypto_ahash_cast(tfm),
+                                sizeof(struct mv_cesa_ahash_req));
+       return 0;
+}
+
+static int mv_cesa_ahmac_md5_init(struct ahash_request *req)
+{
+       struct mv_cesa_hmac_ctx *ctx = crypto_tfm_ctx(req->base.tfm);
+       struct mv_cesa_op_ctx tmpl;
+
+       mv_cesa_set_op_cfg(&tmpl, CESA_SA_DESC_CFG_MACM_HMAC_MD5);
+       memcpy(tmpl.ctx.hash.iv, ctx->iv, sizeof(ctx->iv));
+
+       mv_cesa_ahash_init(req, &tmpl);
+
+       return 0;
+}
+
+static int mv_cesa_ahmac_md5_setkey(struct crypto_ahash *tfm, const u8 *key,
+                                   unsigned int keylen)
+{
+       struct mv_cesa_hmac_ctx *ctx = crypto_tfm_ctx(crypto_ahash_tfm(tfm));
+       struct md5_state istate, ostate;
+       int ret, i;
+
+       ret = mv_cesa_ahmac_setkey("mv-md5", key, keylen, &istate, &ostate);
+       if (ret)
+               return ret;
+
+       for (i = 0; i < ARRAY_SIZE(istate.hash); i++)
+               ctx->iv[i] = be32_to_cpu(istate.hash[i]);
+
+       for (i = 0; i < ARRAY_SIZE(ostate.hash); i++)
+               ctx->iv[i + 8] = be32_to_cpu(ostate.hash[i]);
+
+       return 0;
+}
+
+static int mv_cesa_ahmac_md5_digest(struct ahash_request *req)
+{
+       int ret;
+
+       ret = mv_cesa_ahmac_md5_init(req);
+       if (ret)
+               return ret;
+
+       return mv_cesa_ahash_finup(req);
+}
+
+struct ahash_alg mv_ahmac_md5_alg = {
+       .init = mv_cesa_ahmac_md5_init,
+       .update = mv_cesa_ahash_update,
+       .final = mv_cesa_ahash_final,
+       .finup = mv_cesa_ahash_finup,
+       .digest = mv_cesa_ahmac_md5_digest,
+       .setkey = mv_cesa_ahmac_md5_setkey,
+       .export = mv_cesa_md5_export,
+       .import = mv_cesa_md5_import,
+       .halg = {
+               .digestsize = MD5_DIGEST_SIZE,
+               .statesize = sizeof(struct md5_state),
+               .base = {
+                       .cra_name = "hmac(md5)",
+                       .cra_driver_name = "mv-hmac-md5",
+                       .cra_priority = 300,
+                       .cra_flags = CRYPTO_ALG_ASYNC |
+                                    CRYPTO_ALG_KERN_DRIVER_ONLY,
+                       .cra_blocksize = MD5_HMAC_BLOCK_SIZE,
+                       .cra_ctxsize = sizeof(struct mv_cesa_hmac_ctx),
+                       .cra_init = mv_cesa_ahmac_cra_init,
+                       .cra_module = THIS_MODULE,
+                }
+       }
+};
+
+static int mv_cesa_ahmac_sha1_init(struct ahash_request *req)
+{
+       struct mv_cesa_hmac_ctx *ctx = crypto_tfm_ctx(req->base.tfm);
+       struct mv_cesa_op_ctx tmpl;
+
+       mv_cesa_set_op_cfg(&tmpl, CESA_SA_DESC_CFG_MACM_HMAC_SHA1);
+       memcpy(tmpl.ctx.hash.iv, ctx->iv, sizeof(ctx->iv));
+
+       mv_cesa_ahash_init(req, &tmpl);
+
+       return 0;
+}
+
+static int mv_cesa_ahmac_sha1_setkey(struct crypto_ahash *tfm, const u8 *key,
+                                    unsigned int keylen)
+{
+       struct mv_cesa_hmac_ctx *ctx = crypto_tfm_ctx(crypto_ahash_tfm(tfm));
+       struct sha1_state istate, ostate;
+       int ret, i;
+
+       ret = mv_cesa_ahmac_setkey("mv-sha1", key, keylen, &istate, &ostate);
+       if (ret)
+               return ret;
+
+       for (i = 0; i < ARRAY_SIZE(istate.state); i++)
+               ctx->iv[i] = be32_to_cpu(istate.state[i]);
+
+       for (i = 0; i < ARRAY_SIZE(ostate.state); i++)
+               ctx->iv[i + 8] = be32_to_cpu(ostate.state[i]);
+
+       return 0;
+}
+
+static int mv_cesa_ahmac_sha1_digest(struct ahash_request *req)
+{
+       int ret;
+
+       ret = mv_cesa_ahmac_sha1_init(req);
+       if (ret)
+               return ret;
+
+       return mv_cesa_ahash_finup(req);
+}
+
+struct ahash_alg mv_ahmac_sha1_alg = {
+       .init = mv_cesa_ahmac_sha1_init,
+       .update = mv_cesa_ahash_update,
+       .final = mv_cesa_ahash_final,
+       .finup = mv_cesa_ahash_finup,
+       .digest = mv_cesa_ahmac_sha1_digest,
+       .setkey = mv_cesa_ahmac_sha1_setkey,
+       .export = mv_cesa_sha1_export,
+       .import = mv_cesa_sha1_import,
+       .halg = {
+               .digestsize = SHA1_DIGEST_SIZE,
+               .statesize = sizeof(struct sha1_state),
+               .base = {
+                       .cra_name = "hmac(sha1)",
+                       .cra_driver_name = "mv-hmac-sha1",
+                       .cra_priority = 300,
+                       .cra_flags = CRYPTO_ALG_ASYNC |
+                                    CRYPTO_ALG_KERN_DRIVER_ONLY,
+                       .cra_blocksize = SHA1_BLOCK_SIZE,
+                       .cra_ctxsize = sizeof(struct mv_cesa_hmac_ctx),
+                       .cra_init = mv_cesa_ahmac_cra_init,
+                       .cra_module = THIS_MODULE,
+                }
+       }
+};
+
+static int mv_cesa_ahmac_sha256_setkey(struct crypto_ahash *tfm, const u8 *key,
+                                      unsigned int keylen)
+{
+       struct mv_cesa_hmac_ctx *ctx = crypto_tfm_ctx(crypto_ahash_tfm(tfm));
+       struct sha256_state istate, ostate;
+       int ret, i;
+
+       ret = mv_cesa_ahmac_setkey("mv-sha256", key, keylen, &istate, &ostate);
+       if (ret)
+               return ret;
+
+       for (i = 0; i < ARRAY_SIZE(istate.state); i++)
+               ctx->iv[i] = be32_to_cpu(istate.state[i]);
+
+       for (i = 0; i < ARRAY_SIZE(ostate.state); i++)
+               ctx->iv[i + 8] = be32_to_cpu(ostate.state[i]);
+
+       return 0;
+}
+
+static int mv_cesa_ahmac_sha256_init(struct ahash_request *req)
+{
+       struct mv_cesa_hmac_ctx *ctx = crypto_tfm_ctx(req->base.tfm);
+       struct mv_cesa_op_ctx tmpl;
+
+       mv_cesa_set_op_cfg(&tmpl, CESA_SA_DESC_CFG_MACM_HMAC_SHA256);
+       memcpy(tmpl.ctx.hash.iv, ctx->iv, sizeof(ctx->iv));
+
+       mv_cesa_ahash_init(req, &tmpl);
+
+       return 0;
+}
+
+static int mv_cesa_ahmac_sha256_digest(struct ahash_request *req)
+{
+       int ret;
+
+       ret = mv_cesa_ahmac_sha256_init(req);
+       if (ret)
+               return ret;
+
+       return mv_cesa_ahash_finup(req);
+}
+
+struct ahash_alg mv_ahmac_sha256_alg = {
+       .init = mv_cesa_ahmac_sha256_init,
+       .update = mv_cesa_ahash_update,
+       .final = mv_cesa_ahash_final,
+       .finup = mv_cesa_ahash_finup,
+       .digest = mv_cesa_ahmac_sha256_digest,
+       .setkey = mv_cesa_ahmac_sha256_setkey,
+       .export = mv_cesa_sha256_export,
+       .import = mv_cesa_sha256_import,
+       .halg = {
+               .digestsize = SHA256_DIGEST_SIZE,
+               .statesize = sizeof(struct sha256_state),
+               .base = {
+                       .cra_name = "hmac(sha256)",
+                       .cra_driver_name = "mv-hmac-sha256",
+                       .cra_priority = 300,
+                       .cra_flags = CRYPTO_ALG_ASYNC |
+                                    CRYPTO_ALG_KERN_DRIVER_ONLY,
+                       .cra_blocksize = SHA256_BLOCK_SIZE,
+                       .cra_ctxsize = sizeof(struct mv_cesa_hmac_ctx),
+                       .cra_init = mv_cesa_ahmac_cra_init,
+                       .cra_module = THIS_MODULE,
+                }
+       }
+};
diff --git a/drivers/crypto/marvell/tdma.c b/drivers/crypto/marvell/tdma.c
new file mode 100644 (file)
index 0000000..64a366c
--- /dev/null
@@ -0,0 +1,224 @@
+/*
+ * Provide TDMA helper functions used by cipher and hash algorithm
+ * implementations.
+ *
+ * Author: Boris Brezillon <boris.brezillon@free-electrons.com>
+ * Author: Arnaud Ebalard <arno@natisbad.org>
+ *
+ * This work is based on an initial version written by
+ * Sebastian Andrzej Siewior < sebastian at breakpoint dot cc >
+ *
+ * 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 "cesa.h"
+
+bool mv_cesa_req_dma_iter_next_transfer(struct mv_cesa_dma_iter *iter,
+                                       struct mv_cesa_sg_dma_iter *sgiter,
+                                       unsigned int len)
+{
+       if (!sgiter->sg)
+               return false;
+
+       sgiter->op_offset += len;
+       sgiter->offset += len;
+       if (sgiter->offset == sg_dma_len(sgiter->sg)) {
+               if (sg_is_last(sgiter->sg))
+                       return false;
+               sgiter->offset = 0;
+               sgiter->sg = sg_next(sgiter->sg);
+       }
+
+       if (sgiter->op_offset == iter->op_len)
+               return false;
+
+       return true;
+}
+
+void mv_cesa_dma_step(struct mv_cesa_tdma_req *dreq)
+{
+       struct mv_cesa_engine *engine = dreq->base.engine;
+
+       writel(0, engine->regs + CESA_SA_CFG);
+
+       mv_cesa_set_int_mask(engine, CESA_SA_INT_ACC0_IDMA_DONE);
+       writel(CESA_TDMA_DST_BURST_128B | CESA_TDMA_SRC_BURST_128B |
+              CESA_TDMA_NO_BYTE_SWAP | CESA_TDMA_EN,
+              engine->regs + CESA_TDMA_CONTROL);
+
+       writel(CESA_SA_CFG_ACT_CH0_IDMA | CESA_SA_CFG_MULTI_PKT |
+              CESA_SA_CFG_CH0_W_IDMA | CESA_SA_CFG_PARA_DIS,
+              engine->regs + CESA_SA_CFG);
+       writel(dreq->chain.first->cur_dma,
+              engine->regs + CESA_TDMA_NEXT_ADDR);
+       writel(CESA_SA_CMD_EN_CESA_SA_ACCL0, engine->regs + CESA_SA_CMD);
+}
+
+void mv_cesa_dma_cleanup(struct mv_cesa_tdma_req *dreq)
+{
+       struct mv_cesa_tdma_desc *tdma;
+
+       for (tdma = dreq->chain.first; tdma;) {
+               struct mv_cesa_tdma_desc *old_tdma = tdma;
+
+               if (tdma->flags & CESA_TDMA_OP)
+                       dma_pool_free(cesa_dev->dma->op_pool, tdma->op,
+                                     le32_to_cpu(tdma->src));
+
+               tdma = tdma->next;
+               dma_pool_free(cesa_dev->dma->tdma_desc_pool, old_tdma,
+                             le32_to_cpu(old_tdma->cur_dma));
+       }
+
+       dreq->chain.first = NULL;
+       dreq->chain.last = NULL;
+}
+
+void mv_cesa_dma_prepare(struct mv_cesa_tdma_req *dreq,
+                        struct mv_cesa_engine *engine)
+{
+       struct mv_cesa_tdma_desc *tdma;
+
+       for (tdma = dreq->chain.first; tdma; tdma = tdma->next) {
+               if (tdma->flags & CESA_TDMA_DST_IN_SRAM)
+                       tdma->dst = cpu_to_le32(tdma->dst + engine->sram_dma);
+
+               if (tdma->flags & CESA_TDMA_SRC_IN_SRAM)
+                       tdma->src = cpu_to_le32(tdma->src + engine->sram_dma);
+
+               if (tdma->flags & CESA_TDMA_OP)
+                       mv_cesa_adjust_op(engine, tdma->op);
+       }
+}
+
+static struct mv_cesa_tdma_desc *
+mv_cesa_dma_add_desc(struct mv_cesa_tdma_chain *chain, gfp_t flags)
+{
+       struct mv_cesa_tdma_desc *new_tdma = NULL;
+       dma_addr_t dma_handle;
+
+       new_tdma = dma_pool_alloc(cesa_dev->dma->tdma_desc_pool, flags,
+                                 &dma_handle);
+       if (!new_tdma)
+               return ERR_PTR(-ENOMEM);
+
+       memset(new_tdma, 0, sizeof(*new_tdma));
+       new_tdma->cur_dma = cpu_to_le32(dma_handle);
+       if (chain->last) {
+               chain->last->next_dma = new_tdma->cur_dma;
+               chain->last->next = new_tdma;
+       } else {
+               chain->first = new_tdma;
+       }
+
+       chain->last = new_tdma;
+
+       return new_tdma;
+}
+
+struct mv_cesa_op_ctx *mv_cesa_dma_add_op(struct mv_cesa_tdma_chain *chain,
+                                       const struct mv_cesa_op_ctx *op_templ,
+                                       bool skip_ctx,
+                                       gfp_t flags)
+{
+       struct mv_cesa_tdma_desc *tdma;
+       struct mv_cesa_op_ctx *op;
+       dma_addr_t dma_handle;
+
+       tdma = mv_cesa_dma_add_desc(chain, flags);
+       if (IS_ERR(tdma))
+               return ERR_CAST(tdma);
+
+       op = dma_pool_alloc(cesa_dev->dma->op_pool, flags, &dma_handle);
+       if (!op)
+               return ERR_PTR(-ENOMEM);
+
+       *op = *op_templ;
+
+       tdma = chain->last;
+       tdma->op = op;
+       tdma->byte_cnt = (skip_ctx ? sizeof(op->desc) : sizeof(*op)) | BIT(31);
+       tdma->src = dma_handle;
+       tdma->flags = CESA_TDMA_DST_IN_SRAM | CESA_TDMA_OP;
+
+       return op;
+}
+
+int mv_cesa_dma_add_data_transfer(struct mv_cesa_tdma_chain *chain,
+                                 dma_addr_t dst, dma_addr_t src, u32 size,
+                                 u32 flags, gfp_t gfp_flags)
+{
+       struct mv_cesa_tdma_desc *tdma;
+
+       tdma = mv_cesa_dma_add_desc(chain, gfp_flags);
+       if (IS_ERR(tdma))
+               return PTR_ERR(tdma);
+
+       tdma->byte_cnt = size | BIT(31);
+       tdma->src = src;
+       tdma->dst = dst;
+
+       flags &= (CESA_TDMA_DST_IN_SRAM | CESA_TDMA_SRC_IN_SRAM);
+       tdma->flags = flags | CESA_TDMA_DATA;
+
+       return 0;
+}
+
+int mv_cesa_dma_add_dummy_launch(struct mv_cesa_tdma_chain *chain,
+                                u32 flags)
+{
+       struct mv_cesa_tdma_desc *tdma;
+
+       tdma = mv_cesa_dma_add_desc(chain, flags);
+       if (IS_ERR(tdma))
+               return PTR_ERR(tdma);
+
+       return 0;
+}
+
+int mv_cesa_dma_add_dummy_end(struct mv_cesa_tdma_chain *chain, u32 flags)
+{
+       struct mv_cesa_tdma_desc *tdma;
+
+       tdma = mv_cesa_dma_add_desc(chain, flags);
+       if (IS_ERR(tdma))
+               return PTR_ERR(tdma);
+
+       tdma->byte_cnt = BIT(31);
+
+       return 0;
+}
+
+int mv_cesa_dma_add_op_transfers(struct mv_cesa_tdma_chain *chain,
+                                struct mv_cesa_dma_iter *dma_iter,
+                                struct mv_cesa_sg_dma_iter *sgiter,
+                                gfp_t gfp_flags)
+{
+       u32 flags = sgiter->dir == DMA_TO_DEVICE ?
+                   CESA_TDMA_DST_IN_SRAM : CESA_TDMA_SRC_IN_SRAM;
+       unsigned int len;
+
+       do {
+               dma_addr_t dst, src;
+               int ret;
+
+               len = mv_cesa_req_dma_iter_transfer_len(dma_iter, sgiter);
+               if (sgiter->dir == DMA_TO_DEVICE) {
+                       dst = CESA_SA_DATA_SRAM_OFFSET + sgiter->op_offset;
+                       src = sg_dma_address(sgiter->sg) + sgiter->offset;
+               } else {
+                       dst = sg_dma_address(sgiter->sg) + sgiter->offset;
+                       src = CESA_SA_DATA_SRAM_OFFSET + sgiter->op_offset;
+               }
+
+               ret = mv_cesa_dma_add_data_transfer(chain, dst, src, len,
+                                                   flags, gfp_flags);
+               if (ret)
+                       return ret;
+
+       } while (mv_cesa_req_dma_iter_next_transfer(dma_iter, sgiter, len));
+
+       return 0;
+}
index f91f15ddee926ffeb7d470d3823f699194bc9d40..5bcd575fa96f1b80d3e9a15cabc2898c79d50fdf 100644 (file)
@@ -9,6 +9,7 @@
 #include <crypto/aes.h>
 #include <crypto/algapi.h>
 #include <linux/crypto.h>
+#include <linux/genalloc.h>
 #include <linux/interrupt.h>
 #include <linux/io.h>
 #include <linux/kthread.h>
@@ -29,6 +30,8 @@
 #define MAX_HW_HASH_SIZE       0xFFFF
 #define MV_CESA_EXPIRE         500 /* msec */
 
+#define MV_CESA_DEFAULT_SRAM_SIZE      2048
+
 /*
  * STM:
  *   /---------------------------------------\
@@ -83,6 +86,8 @@ struct req_progress {
 struct crypto_priv {
        void __iomem *reg;
        void __iomem *sram;
+       struct gen_pool *sram_pool;
+       dma_addr_t sram_dma;
        int irq;
        struct clk *clk;
        struct task_struct *queue_th;
@@ -595,7 +600,7 @@ static int queue_manag(void *data)
        cpg->eng_st = ENGINE_IDLE;
        do {
                struct crypto_async_request *async_req = NULL;
-               struct crypto_async_request *backlog;
+               struct crypto_async_request *backlog = NULL;
 
                __set_current_state(TASK_INTERRUPTIBLE);
 
@@ -1019,6 +1024,39 @@ static struct ahash_alg mv_hmac_sha1_alg = {
                 }
 };
 
+static int mv_cesa_get_sram(struct platform_device *pdev,
+                           struct crypto_priv *cp)
+{
+       struct resource *res;
+       u32 sram_size = MV_CESA_DEFAULT_SRAM_SIZE;
+
+       of_property_read_u32(pdev->dev.of_node, "marvell,crypto-sram-size",
+                            &sram_size);
+
+       cp->sram_size = sram_size;
+       cp->sram_pool = of_get_named_gen_pool(pdev->dev.of_node,
+                                             "marvell,crypto-srams", 0);
+       if (cp->sram_pool) {
+               cp->sram = gen_pool_dma_alloc(cp->sram_pool, sram_size,
+                                             &cp->sram_dma);
+               if (cp->sram)
+                       return 0;
+
+               return -ENOMEM;
+       }
+
+       res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+                                          "sram");
+       if (!res || resource_size(res) < cp->sram_size)
+               return -EINVAL;
+
+       cp->sram = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(cp->sram))
+               return PTR_ERR(cp->sram);
+
+       return 0;
+}
+
 static int mv_probe(struct platform_device *pdev)
 {
        struct crypto_priv *cp;
@@ -1041,24 +1079,17 @@ static int mv_probe(struct platform_device *pdev)
 
        spin_lock_init(&cp->lock);
        crypto_init_queue(&cp->queue, 50);
-       cp->reg = ioremap(res->start, resource_size(res));
-       if (!cp->reg) {
-               ret = -ENOMEM;
+       cp->reg = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(cp->reg)) {
+               ret = PTR_ERR(cp->reg);
                goto err;
        }
 
-       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sram");
-       if (!res) {
-               ret = -ENXIO;
-               goto err_unmap_reg;
-       }
-       cp->sram_size = resource_size(res);
+       ret = mv_cesa_get_sram(pdev, cp);
+       if (ret)
+               goto err;
+
        cp->max_req_size = cp->sram_size - SRAM_CFG_SPACE;
-       cp->sram = ioremap(res->start, cp->sram_size);
-       if (!cp->sram) {
-               ret = -ENOMEM;
-               goto err_unmap_reg;
-       }
 
        if (pdev->dev.of_node)
                irq = irq_of_parse_and_map(pdev->dev.of_node, 0);
@@ -1066,7 +1097,7 @@ static int mv_probe(struct platform_device *pdev)
                irq = platform_get_irq(pdev, 0);
        if (irq < 0 || irq == NO_IRQ) {
                ret = irq;
-               goto err_unmap_sram;
+               goto err;
        }
        cp->irq = irq;
 
@@ -1076,7 +1107,7 @@ static int mv_probe(struct platform_device *pdev)
        cp->queue_th = kthread_run(queue_manag, cp, "mv_crypto");
        if (IS_ERR(cp->queue_th)) {
                ret = PTR_ERR(cp->queue_th);
-               goto err_unmap_sram;
+               goto err;
        }
 
        ret = request_irq(irq, crypto_int, 0, dev_name(&pdev->dev),
@@ -1134,10 +1165,6 @@ err_irq:
        }
 err_thread:
        kthread_stop(cp->queue_th);
-err_unmap_sram:
-       iounmap(cp->sram);
-err_unmap_reg:
-       iounmap(cp->reg);
 err:
        kfree(cp);
        cpg = NULL;
@@ -1157,8 +1184,6 @@ static int mv_remove(struct platform_device *pdev)
        kthread_stop(cp->queue_th);
        free_irq(cp->irq, cp);
        memset(cp->sram, 0, cp->sram_size);
-       iounmap(cp->sram);
-       iounmap(cp->reg);
 
        if (!IS_ERR(cp->clk)) {
                clk_disable_unprepare(cp->clk);
@@ -1172,6 +1197,8 @@ static int mv_remove(struct platform_device *pdev)
 
 static const struct of_device_id mv_cesa_of_match_table[] = {
        { .compatible = "marvell,orion-crypto", },
+       { .compatible = "marvell,kirkwood-crypto", },
+       { .compatible = "marvell,dove-crypto", },
        {}
 };
 MODULE_DEVICE_TABLE(of, mv_cesa_of_match_table);
index 10a9aeff1666ed69bc0dac693a0cdf7b553f225d..2e8dab9d4263b000c51b8c8e584cdeb636fe1e14 100644 (file)
@@ -1281,10 +1281,10 @@ static const char md5_zero[MD5_DIGEST_SIZE] = {
        0xe9, 0x80, 0x09, 0x98, 0xec, 0xf8, 0x42, 0x7e,
 };
 static const u32 md5_init[MD5_HASH_WORDS] = {
-       cpu_to_le32(0x67452301),
-       cpu_to_le32(0xefcdab89),
-       cpu_to_le32(0x98badcfe),
-       cpu_to_le32(0x10325476),
+       cpu_to_le32(MD5_H0),
+       cpu_to_le32(MD5_H1),
+       cpu_to_le32(MD5_H2),
+       cpu_to_le32(MD5_H3),
 };
 static const char sha1_zero[SHA1_DIGEST_SIZE] = {
        0xda, 0x39, 0xa3, 0xee, 0x5e, 0x6b, 0x4b, 0x0d, 0x32,
index f82616621ae13e868210e34f1b26b83584848f8b..e421c96c763a6781ac1b40f4f30eb5966dad0e91 100644 (file)
@@ -1,26 +1,55 @@
+
 config CRYPTO_DEV_NX_ENCRYPT
-       tristate "Encryption acceleration support"
-       depends on PPC64 && IBMVIO
+       tristate "Encryption acceleration support on pSeries platform"
+       depends on PPC_PSERIES && IBMVIO && !CPU_LITTLE_ENDIAN
        default y
        select CRYPTO_AES
-       select CRYPTO_CBC
-       select CRYPTO_ECB
        select CRYPTO_CCM
-       select CRYPTO_GCM
-       select CRYPTO_AUTHENC
-       select CRYPTO_XCBC
-       select CRYPTO_SHA256
-       select CRYPTO_SHA512
        help
-         Support for Power7+ in-Nest encryption acceleration. This
-         module supports acceleration for AES and SHA2 algorithms. If you
-         choose 'M' here, this module will be called nx_crypto.
+         Support for PowerPC Nest (NX) encryption acceleration. This
+         module supports acceleration for AES and SHA2 algorithms on
+         the pSeries platform.  If you choose 'M' here, this module
+         will be called nx_crypto.
 
 config CRYPTO_DEV_NX_COMPRESS
        tristate "Compression acceleration support"
-       depends on PPC64 && IBMVIO
        default y
        help
-         Support for Power7+ in-Nest compression acceleration. This
-         module supports acceleration for AES and SHA2 algorithms. If you
-         choose 'M' here, this module will be called nx_compress.
+         Support for PowerPC Nest (NX) compression acceleration. This
+         module supports acceleration for compressing memory with the 842
+         algorithm.  One of the platform drivers must be selected also.
+         If you choose 'M' here, this module will be called nx_compress.
+
+if CRYPTO_DEV_NX_COMPRESS
+
+config CRYPTO_DEV_NX_COMPRESS_PSERIES
+       tristate "Compression acceleration support on pSeries platform"
+       depends on PPC_PSERIES && IBMVIO
+       default y
+       help
+         Support for PowerPC Nest (NX) compression acceleration. This
+         module supports acceleration for compressing memory with the 842
+         algorithm.  This supports NX hardware on the pSeries platform.
+         If you choose 'M' here, this module will be called nx_compress_pseries.
+
+config CRYPTO_DEV_NX_COMPRESS_POWERNV
+       tristate "Compression acceleration support on PowerNV platform"
+       depends on PPC_POWERNV
+       default y
+       help
+         Support for PowerPC Nest (NX) compression acceleration. This
+         module supports acceleration for compressing memory with the 842
+         algorithm.  This supports NX hardware on the PowerNV platform.
+         If you choose 'M' here, this module will be called nx_compress_powernv.
+
+config CRYPTO_DEV_NX_COMPRESS_CRYPTO
+       tristate "Compression acceleration cryptographic interface"
+       select CRYPTO_ALGAPI
+       select 842_DECOMPRESS
+       default y
+       help
+         Support for PowerPC Nest (NX) accelerators using the cryptographic
+         API.  If you choose 'M' here, this module will be called
+         nx_compress_crypto.
+
+endif
index bb770ea45ce9390f47ac3bd52e5ecbb132c361c3..e1684f5adb11e0f19a398496e47038dd924eada5 100644 (file)
@@ -10,5 +10,12 @@ nx-crypto-objs := nx.o \
                  nx-sha256.o \
                  nx-sha512.o
 
-obj-$(CONFIG_CRYPTO_DEV_NX_COMPRESS) += nx-compress.o
+obj-$(CONFIG_CRYPTO_DEV_NX_COMPRESS) += nx-compress.o nx-compress-platform.o
+obj-$(CONFIG_CRYPTO_DEV_NX_COMPRESS_PSERIES) += nx-compress-pseries.o
+obj-$(CONFIG_CRYPTO_DEV_NX_COMPRESS_POWERNV) += nx-compress-powernv.o
+obj-$(CONFIG_CRYPTO_DEV_NX_COMPRESS_CRYPTO) += nx-compress-crypto.o
 nx-compress-objs := nx-842.o
+nx-compress-platform-objs := nx-842-platform.o
+nx-compress-pseries-objs := nx-842-pseries.o
+nx-compress-powernv-objs := nx-842-powernv.o
+nx-compress-crypto-objs := nx-842-crypto.o
diff --git a/drivers/crypto/nx/nx-842-crypto.c b/drivers/crypto/nx/nx-842-crypto.c
new file mode 100644 (file)
index 0000000..d53a1dc
--- /dev/null
@@ -0,0 +1,580 @@
+/*
+ * Cryptographic API for the NX-842 hardware compression.
+ *
+ * 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.
+ *
+ * Copyright (C) IBM Corporation, 2011-2015
+ *
+ * Original Authors: Robert Jennings <rcj@linux.vnet.ibm.com>
+ *                   Seth Jennings <sjenning@linux.vnet.ibm.com>
+ *
+ * Rewrite: Dan Streetman <ddstreet@ieee.org>
+ *
+ * This is an interface to the NX-842 compression hardware in PowerPC
+ * processors.  Most of the complexity of this drvier is due to the fact that
+ * the NX-842 compression hardware requires the input and output data buffers
+ * to be specifically aligned, to be a specific multiple in length, and within
+ * specific minimum and maximum lengths.  Those restrictions, provided by the
+ * nx-842 driver via nx842_constraints, mean this driver must use bounce
+ * buffers and headers to correct misaligned in or out buffers, and to split
+ * input buffers that are too large.
+ *
+ * This driver will fall back to software decompression if the hardware
+ * decompression fails, so this driver's decompression should never fail as
+ * long as the provided compressed buffer is valid.  Any compressed buffer
+ * created by this driver will have a header (except ones where the input
+ * perfectly matches the constraints); so users of this driver cannot simply
+ * pass a compressed buffer created by this driver over to the 842 software
+ * decompression library.  Instead, users must use this driver to decompress;
+ * if the hardware fails or is unavailable, the compressed buffer will be
+ * parsed and the header removed, and the raw 842 buffer(s) passed to the 842
+ * software decompression library.
+ *
+ * This does not fall back to software compression, however, since the caller
+ * of this function is specifically requesting hardware compression; if the
+ * hardware compression fails, the caller can fall back to software
+ * compression, and the raw 842 compressed buffer that the software compressor
+ * creates can be passed to this driver for hardware decompression; any
+ * buffer without our specific header magic is assumed to be a raw 842 buffer
+ * and passed directly to the hardware.  Note that the software compression
+ * library will produce a compressed buffer that is incompatible with the
+ * hardware decompressor if the original input buffer length is not a multiple
+ * of 8; if such a compressed buffer is passed to this driver for
+ * decompression, the hardware will reject it and this driver will then pass
+ * it over to the software library for decompression.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/crypto.h>
+#include <linux/vmalloc.h>
+#include <linux/sw842.h>
+#include <linux/ratelimit.h>
+
+#include "nx-842.h"
+
+/* The first 5 bits of this magic are 0x1f, which is an invalid 842 5-bit
+ * template (see lib/842/842.h), so this magic number will never appear at
+ * the start of a raw 842 compressed buffer.  That is important, as any buffer
+ * passed to us without this magic is assumed to be a raw 842 compressed
+ * buffer, and passed directly to the hardware to decompress.
+ */
+#define NX842_CRYPTO_MAGIC     (0xf842)
+#define NX842_CRYPTO_GROUP_MAX (0x20)
+#define NX842_CRYPTO_HEADER_SIZE(g)                            \
+       (sizeof(struct nx842_crypto_header) +                   \
+        sizeof(struct nx842_crypto_header_group) * (g))
+#define NX842_CRYPTO_HEADER_MAX_SIZE                           \
+       NX842_CRYPTO_HEADER_SIZE(NX842_CRYPTO_GROUP_MAX)
+
+/* bounce buffer size */
+#define BOUNCE_BUFFER_ORDER    (2)
+#define BOUNCE_BUFFER_SIZE                                     \
+       ((unsigned int)(PAGE_SIZE << BOUNCE_BUFFER_ORDER))
+
+/* try longer on comp because we can fallback to sw decomp if hw is busy */
+#define COMP_BUSY_TIMEOUT      (250) /* ms */
+#define DECOMP_BUSY_TIMEOUT    (50) /* ms */
+
+struct nx842_crypto_header_group {
+       __be16 padding;                 /* unused bytes at start of group */
+       __be32 compressed_length;       /* compressed bytes in group */
+       __be32 uncompressed_length;     /* bytes after decompression */
+} __packed;
+
+struct nx842_crypto_header {
+       __be16 magic;           /* NX842_CRYPTO_MAGIC */
+       __be16 ignore;          /* decompressed end bytes to ignore */
+       u8 groups;              /* total groups in this header */
+       struct nx842_crypto_header_group group[];
+} __packed;
+
+struct nx842_crypto_param {
+       u8 *in;
+       unsigned int iremain;
+       u8 *out;
+       unsigned int oremain;
+       unsigned int ototal;
+};
+
+static int update_param(struct nx842_crypto_param *p,
+                       unsigned int slen, unsigned int dlen)
+{
+       if (p->iremain < slen)
+               return -EOVERFLOW;
+       if (p->oremain < dlen)
+               return -ENOSPC;
+
+       p->in += slen;
+       p->iremain -= slen;
+       p->out += dlen;
+       p->oremain -= dlen;
+       p->ototal += dlen;
+
+       return 0;
+}
+
+struct nx842_crypto_ctx {
+       u8 *wmem;
+       u8 *sbounce, *dbounce;
+
+       struct nx842_crypto_header header;
+       struct nx842_crypto_header_group group[NX842_CRYPTO_GROUP_MAX];
+};
+
+static int nx842_crypto_init(struct crypto_tfm *tfm)
+{
+       struct nx842_crypto_ctx *ctx = crypto_tfm_ctx(tfm);
+
+       ctx->wmem = kmalloc(nx842_workmem_size(), GFP_KERNEL);
+       ctx->sbounce = (u8 *)__get_free_pages(GFP_KERNEL, BOUNCE_BUFFER_ORDER);
+       ctx->dbounce = (u8 *)__get_free_pages(GFP_KERNEL, BOUNCE_BUFFER_ORDER);
+       if (!ctx->wmem || !ctx->sbounce || !ctx->dbounce) {
+               kfree(ctx->wmem);
+               free_page((unsigned long)ctx->sbounce);
+               free_page((unsigned long)ctx->dbounce);
+               return -ENOMEM;
+       }
+
+       return 0;
+}
+
+static void nx842_crypto_exit(struct crypto_tfm *tfm)
+{
+       struct nx842_crypto_ctx *ctx = crypto_tfm_ctx(tfm);
+
+       kfree(ctx->wmem);
+       free_page((unsigned long)ctx->sbounce);
+       free_page((unsigned long)ctx->dbounce);
+}
+
+static int read_constraints(struct nx842_constraints *c)
+{
+       int ret;
+
+       ret = nx842_constraints(c);
+       if (ret) {
+               pr_err_ratelimited("could not get nx842 constraints : %d\n",
+                                  ret);
+               return ret;
+       }
+
+       /* limit maximum, to always have enough bounce buffer to decompress */
+       if (c->maximum > BOUNCE_BUFFER_SIZE) {
+               c->maximum = BOUNCE_BUFFER_SIZE;
+               pr_info_once("limiting nx842 maximum to %x\n", c->maximum);
+       }
+
+       return 0;
+}
+
+static int nx842_crypto_add_header(struct nx842_crypto_header *hdr, u8 *buf)
+{
+       int s = NX842_CRYPTO_HEADER_SIZE(hdr->groups);
+
+       /* compress should have added space for header */
+       if (s > be16_to_cpu(hdr->group[0].padding)) {
+               pr_err("Internal error: no space for header\n");
+               return -EINVAL;
+       }
+
+       memcpy(buf, hdr, s);
+
+       print_hex_dump_debug("header ", DUMP_PREFIX_OFFSET, 16, 1, buf, s, 0);
+
+       return 0;
+}
+
+static int compress(struct nx842_crypto_ctx *ctx,
+                   struct nx842_crypto_param *p,
+                   struct nx842_crypto_header_group *g,
+                   struct nx842_constraints *c,
+                   u16 *ignore,
+                   unsigned int hdrsize)
+{
+       unsigned int slen = p->iremain, dlen = p->oremain, tmplen;
+       unsigned int adj_slen = slen;
+       u8 *src = p->in, *dst = p->out;
+       int ret, dskip = 0;
+       ktime_t timeout;
+
+       if (p->iremain == 0)
+               return -EOVERFLOW;
+
+       if (p->oremain == 0 || hdrsize + c->minimum > dlen)
+               return -ENOSPC;
+
+       if (slen % c->multiple)
+               adj_slen = round_up(slen, c->multiple);
+       if (slen < c->minimum)
+               adj_slen = c->minimum;
+       if (slen > c->maximum)
+               adj_slen = slen = c->maximum;
+       if (adj_slen > slen || (u64)src % c->alignment) {
+               adj_slen = min(adj_slen, BOUNCE_BUFFER_SIZE);
+               slen = min(slen, BOUNCE_BUFFER_SIZE);
+               if (adj_slen > slen)
+                       memset(ctx->sbounce + slen, 0, adj_slen - slen);
+               memcpy(ctx->sbounce, src, slen);
+               src = ctx->sbounce;
+               slen = adj_slen;
+               pr_debug("using comp sbounce buffer, len %x\n", slen);
+       }
+
+       dst += hdrsize;
+       dlen -= hdrsize;
+
+       if ((u64)dst % c->alignment) {
+               dskip = (int)(PTR_ALIGN(dst, c->alignment) - dst);
+               dst += dskip;
+               dlen -= dskip;
+       }
+       if (dlen % c->multiple)
+               dlen = round_down(dlen, c->multiple);
+       if (dlen < c->minimum) {
+nospc:
+               dst = ctx->dbounce;
+               dlen = min(p->oremain, BOUNCE_BUFFER_SIZE);
+               dlen = round_down(dlen, c->multiple);
+               dskip = 0;
+               pr_debug("using comp dbounce buffer, len %x\n", dlen);
+       }
+       if (dlen > c->maximum)
+               dlen = c->maximum;
+
+       tmplen = dlen;
+       timeout = ktime_add_ms(ktime_get(), COMP_BUSY_TIMEOUT);
+       do {
+               dlen = tmplen; /* reset dlen, if we're retrying */
+               ret = nx842_compress(src, slen, dst, &dlen, ctx->wmem);
+               /* possibly we should reduce the slen here, instead of
+                * retrying with the dbounce buffer?
+                */
+               if (ret == -ENOSPC && dst != ctx->dbounce)
+                       goto nospc;
+       } while (ret == -EBUSY && ktime_before(ktime_get(), timeout));
+       if (ret)
+               return ret;
+
+       dskip += hdrsize;
+
+       if (dst == ctx->dbounce)
+               memcpy(p->out + dskip, dst, dlen);
+
+       g->padding = cpu_to_be16(dskip);
+       g->compressed_length = cpu_to_be32(dlen);
+       g->uncompressed_length = cpu_to_be32(slen);
+
+       if (p->iremain < slen) {
+               *ignore = slen - p->iremain;
+               slen = p->iremain;
+       }
+
+       pr_debug("compress slen %x ignore %x dlen %x padding %x\n",
+                slen, *ignore, dlen, dskip);
+
+       return update_param(p, slen, dskip + dlen);
+}
+
+static int nx842_crypto_compress(struct crypto_tfm *tfm,
+                                const u8 *src, unsigned int slen,
+                                u8 *dst, unsigned int *dlen)
+{
+       struct nx842_crypto_ctx *ctx = crypto_tfm_ctx(tfm);
+       struct nx842_crypto_header *hdr = &ctx->header;
+       struct nx842_crypto_param p;
+       struct nx842_constraints c;
+       unsigned int groups, hdrsize, h;
+       int ret, n;
+       bool add_header;
+       u16 ignore = 0;
+
+       p.in = (u8 *)src;
+       p.iremain = slen;
+       p.out = dst;
+       p.oremain = *dlen;
+       p.ototal = 0;
+
+       *dlen = 0;
+
+       ret = read_constraints(&c);
+       if (ret)
+               return ret;
+
+       groups = min_t(unsigned int, NX842_CRYPTO_GROUP_MAX,
+                      DIV_ROUND_UP(p.iremain, c.maximum));
+       hdrsize = NX842_CRYPTO_HEADER_SIZE(groups);
+
+       /* skip adding header if the buffers meet all constraints */
+       add_header = (p.iremain % c.multiple    ||
+                     p.iremain < c.minimum     ||
+                     p.iremain > c.maximum     ||
+                     (u64)p.in % c.alignment   ||
+                     p.oremain % c.multiple    ||
+                     p.oremain < c.minimum     ||
+                     p.oremain > c.maximum     ||
+                     (u64)p.out % c.alignment);
+
+       hdr->magic = cpu_to_be16(NX842_CRYPTO_MAGIC);
+       hdr->groups = 0;
+       hdr->ignore = 0;
+
+       while (p.iremain > 0) {
+               n = hdr->groups++;
+               if (hdr->groups > NX842_CRYPTO_GROUP_MAX)
+                       return -ENOSPC;
+
+               /* header goes before first group */
+               h = !n && add_header ? hdrsize : 0;
+
+               if (ignore)
+                       pr_warn("interal error, ignore is set %x\n", ignore);
+
+               ret = compress(ctx, &p, &hdr->group[n], &c, &ignore, h);
+               if (ret)
+                       return ret;
+       }
+
+       if (!add_header && hdr->groups > 1) {
+               pr_err("Internal error: No header but multiple groups\n");
+               return -EINVAL;
+       }
+
+       /* ignore indicates the input stream needed to be padded */
+       hdr->ignore = cpu_to_be16(ignore);
+       if (ignore)
+               pr_debug("marked %d bytes as ignore\n", ignore);
+
+       if (add_header)
+               ret = nx842_crypto_add_header(hdr, dst);
+       if (ret)
+               return ret;
+
+       *dlen = p.ototal;
+
+       pr_debug("compress total slen %x dlen %x\n", slen, *dlen);
+
+       return 0;
+}
+
+static int decompress(struct nx842_crypto_ctx *ctx,
+                     struct nx842_crypto_param *p,
+                     struct nx842_crypto_header_group *g,
+                     struct nx842_constraints *c,
+                     u16 ignore,
+                     bool usehw)
+{
+       unsigned int slen = be32_to_cpu(g->compressed_length);
+       unsigned int required_len = be32_to_cpu(g->uncompressed_length);
+       unsigned int dlen = p->oremain, tmplen;
+       unsigned int adj_slen = slen;
+       u8 *src = p->in, *dst = p->out;
+       u16 padding = be16_to_cpu(g->padding);
+       int ret, spadding = 0, dpadding = 0;
+       ktime_t timeout;
+
+       if (!slen || !required_len)
+               return -EINVAL;
+
+       if (p->iremain <= 0 || padding + slen > p->iremain)
+               return -EOVERFLOW;
+
+       if (p->oremain <= 0 || required_len - ignore > p->oremain)
+               return -ENOSPC;
+
+       src += padding;
+
+       if (!usehw)
+               goto usesw;
+
+       if (slen % c->multiple)
+               adj_slen = round_up(slen, c->multiple);
+       if (slen < c->minimum)
+               adj_slen = c->minimum;
+       if (slen > c->maximum)
+               goto usesw;
+       if (slen < adj_slen || (u64)src % c->alignment) {
+               /* we can append padding bytes because the 842 format defines
+                * an "end" template (see lib/842/842_decompress.c) and will
+                * ignore any bytes following it.
+                */
+               if (slen < adj_slen)
+                       memset(ctx->sbounce + slen, 0, adj_slen - slen);
+               memcpy(ctx->sbounce, src, slen);
+               src = ctx->sbounce;
+               spadding = adj_slen - slen;
+               slen = adj_slen;
+               pr_debug("using decomp sbounce buffer, len %x\n", slen);
+       }
+
+       if (dlen % c->multiple)
+               dlen = round_down(dlen, c->multiple);
+       if (dlen < required_len || (u64)dst % c->alignment) {
+               dst = ctx->dbounce;
+               dlen = min(required_len, BOUNCE_BUFFER_SIZE);
+               pr_debug("using decomp dbounce buffer, len %x\n", dlen);
+       }
+       if (dlen < c->minimum)
+               goto usesw;
+       if (dlen > c->maximum)
+               dlen = c->maximum;
+
+       tmplen = dlen;
+       timeout = ktime_add_ms(ktime_get(), DECOMP_BUSY_TIMEOUT);
+       do {
+               dlen = tmplen; /* reset dlen, if we're retrying */
+               ret = nx842_decompress(src, slen, dst, &dlen, ctx->wmem);
+       } while (ret == -EBUSY && ktime_before(ktime_get(), timeout));
+       if (ret) {
+usesw:
+               /* reset everything, sw doesn't have constraints */
+               src = p->in + padding;
+               slen = be32_to_cpu(g->compressed_length);
+               spadding = 0;
+               dst = p->out;
+               dlen = p->oremain;
+               dpadding = 0;
+               if (dlen < required_len) { /* have ignore bytes */
+                       dst = ctx->dbounce;
+                       dlen = BOUNCE_BUFFER_SIZE;
+               }
+               pr_info_ratelimited("using software 842 decompression\n");
+               ret = sw842_decompress(src, slen, dst, &dlen);
+       }
+       if (ret)
+               return ret;
+
+       slen -= spadding;
+
+       dlen -= ignore;
+       if (ignore)
+               pr_debug("ignoring last %x bytes\n", ignore);
+
+       if (dst == ctx->dbounce)
+               memcpy(p->out, dst, dlen);
+
+       pr_debug("decompress slen %x padding %x dlen %x ignore %x\n",
+                slen, padding, dlen, ignore);
+
+       return update_param(p, slen + padding, dlen);
+}
+
+static int nx842_crypto_decompress(struct crypto_tfm *tfm,
+                                  const u8 *src, unsigned int slen,
+                                  u8 *dst, unsigned int *dlen)
+{
+       struct nx842_crypto_ctx *ctx = crypto_tfm_ctx(tfm);
+       struct nx842_crypto_header *hdr;
+       struct nx842_crypto_param p;
+       struct nx842_constraints c;
+       int n, ret, hdr_len;
+       u16 ignore = 0;
+       bool usehw = true;
+
+       p.in = (u8 *)src;
+       p.iremain = slen;
+       p.out = dst;
+       p.oremain = *dlen;
+       p.ototal = 0;
+
+       *dlen = 0;
+
+       if (read_constraints(&c))
+               usehw = false;
+
+       hdr = (struct nx842_crypto_header *)src;
+
+       /* If it doesn't start with our header magic number, assume it's a raw
+        * 842 compressed buffer and pass it directly to the hardware driver
+        */
+       if (be16_to_cpu(hdr->magic) != NX842_CRYPTO_MAGIC) {
+               struct nx842_crypto_header_group g = {
+                       .padding =              0,
+                       .compressed_length =    cpu_to_be32(p.iremain),
+                       .uncompressed_length =  cpu_to_be32(p.oremain),
+               };
+
+               ret = decompress(ctx, &p, &g, &c, 0, usehw);
+               if (ret)
+                       return ret;
+
+               *dlen = p.ototal;
+
+               return 0;
+       }
+
+       if (!hdr->groups) {
+               pr_err("header has no groups\n");
+               return -EINVAL;
+       }
+       if (hdr->groups > NX842_CRYPTO_GROUP_MAX) {
+               pr_err("header has too many groups %x, max %x\n",
+                      hdr->groups, NX842_CRYPTO_GROUP_MAX);
+               return -EINVAL;
+       }
+
+       hdr_len = NX842_CRYPTO_HEADER_SIZE(hdr->groups);
+       if (hdr_len > slen)
+               return -EOVERFLOW;
+
+       memcpy(&ctx->header, src, hdr_len);
+       hdr = &ctx->header;
+
+       for (n = 0; n < hdr->groups; n++) {
+               /* ignore applies to last group */
+               if (n + 1 == hdr->groups)
+                       ignore = be16_to_cpu(hdr->ignore);
+
+               ret = decompress(ctx, &p, &hdr->group[n], &c, ignore, usehw);
+               if (ret)
+                       return ret;
+       }
+
+       *dlen = p.ototal;
+
+       pr_debug("decompress total slen %x dlen %x\n", slen, *dlen);
+
+       return 0;
+}
+
+static struct crypto_alg alg = {
+       .cra_name               = "842",
+       .cra_driver_name        = "842-nx",
+       .cra_priority           = 300,
+       .cra_flags              = CRYPTO_ALG_TYPE_COMPRESS,
+       .cra_ctxsize            = sizeof(struct nx842_crypto_ctx),
+       .cra_module             = THIS_MODULE,
+       .cra_init               = nx842_crypto_init,
+       .cra_exit               = nx842_crypto_exit,
+       .cra_u                  = { .compress = {
+       .coa_compress           = nx842_crypto_compress,
+       .coa_decompress         = nx842_crypto_decompress } }
+};
+
+static int __init nx842_crypto_mod_init(void)
+{
+       return crypto_register_alg(&alg);
+}
+module_init(nx842_crypto_mod_init);
+
+static void __exit nx842_crypto_mod_exit(void)
+{
+       crypto_unregister_alg(&alg);
+}
+module_exit(nx842_crypto_mod_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("IBM PowerPC Nest (NX) 842 Hardware Compression Interface");
+MODULE_ALIAS_CRYPTO("842");
+MODULE_ALIAS_CRYPTO("842-nx");
+MODULE_AUTHOR("Dan Streetman <ddstreet@ieee.org>");
diff --git a/drivers/crypto/nx/nx-842-platform.c b/drivers/crypto/nx/nx-842-platform.c
new file mode 100644 (file)
index 0000000..664f13d
--- /dev/null
@@ -0,0 +1,84 @@
+
+#include "nx-842.h"
+
+/* this is needed, separate from the main nx-842.c driver, because that main
+ * driver loads the platform drivers during its init(), and it expects one
+ * (or none) of the platform drivers to set this pointer to its driver.
+ * That means this pointer can't be in the main nx-842 driver, because it
+ * wouldn't be accessible until after the main driver loaded, which wouldn't
+ * be possible as it's waiting for the platform driver to load.  So place it
+ * here.
+ */
+static struct nx842_driver *driver;
+static DEFINE_SPINLOCK(driver_lock);
+
+struct nx842_driver *nx842_platform_driver(void)
+{
+       return driver;
+}
+EXPORT_SYMBOL_GPL(nx842_platform_driver);
+
+bool nx842_platform_driver_set(struct nx842_driver *_driver)
+{
+       bool ret = false;
+
+       spin_lock(&driver_lock);
+
+       if (!driver) {
+               driver = _driver;
+               ret = true;
+       } else
+               WARN(1, "can't set platform driver, already set to %s\n",
+                    driver->name);
+
+       spin_unlock(&driver_lock);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(nx842_platform_driver_set);
+
+/* only call this from the platform driver exit function */
+void nx842_platform_driver_unset(struct nx842_driver *_driver)
+{
+       spin_lock(&driver_lock);
+
+       if (driver == _driver)
+               driver = NULL;
+       else if (driver)
+               WARN(1, "can't unset platform driver %s, currently set to %s\n",
+                    _driver->name, driver->name);
+       else
+               WARN(1, "can't unset platform driver, already unset\n");
+
+       spin_unlock(&driver_lock);
+}
+EXPORT_SYMBOL_GPL(nx842_platform_driver_unset);
+
+bool nx842_platform_driver_get(void)
+{
+       bool ret = false;
+
+       spin_lock(&driver_lock);
+
+       if (driver)
+               ret = try_module_get(driver->owner);
+
+       spin_unlock(&driver_lock);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(nx842_platform_driver_get);
+
+void nx842_platform_driver_put(void)
+{
+       spin_lock(&driver_lock);
+
+       if (driver)
+               module_put(driver->owner);
+
+       spin_unlock(&driver_lock);
+}
+EXPORT_SYMBOL_GPL(nx842_platform_driver_put);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Dan Streetman <ddstreet@ieee.org>");
+MODULE_DESCRIPTION("842 H/W Compression platform driver");
diff --git a/drivers/crypto/nx/nx-842-powernv.c b/drivers/crypto/nx/nx-842-powernv.c
new file mode 100644 (file)
index 0000000..33b3b0a
--- /dev/null
@@ -0,0 +1,637 @@
+/*
+ * Driver for IBM PowerNV 842 compression accelerator
+ *
+ * Copyright (C) 2015 Dan Streetman, IBM Corp
+ *
+ * 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.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include "nx-842.h"
+
+#include <linux/timer.h>
+
+#include <asm/prom.h>
+#include <asm/icswx.h>
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Dan Streetman <ddstreet@ieee.org>");
+MODULE_DESCRIPTION("842 H/W Compression driver for IBM PowerNV processors");
+
+#define WORKMEM_ALIGN  (CRB_ALIGN)
+#define CSB_WAIT_MAX   (5000) /* ms */
+
+struct nx842_workmem {
+       /* Below fields must be properly aligned */
+       struct coprocessor_request_block crb; /* CRB_ALIGN align */
+       struct data_descriptor_entry ddl_in[DDL_LEN_MAX]; /* DDE_ALIGN align */
+       struct data_descriptor_entry ddl_out[DDL_LEN_MAX]; /* DDE_ALIGN align */
+       /* Above fields must be properly aligned */
+
+       ktime_t start;
+
+       char padding[WORKMEM_ALIGN]; /* unused, to allow alignment */
+} __packed __aligned(WORKMEM_ALIGN);
+
+struct nx842_coproc {
+       unsigned int chip_id;
+       unsigned int ct;
+       unsigned int ci;
+       struct list_head list;
+};
+
+/* no cpu hotplug on powernv, so this list never changes after init */
+static LIST_HEAD(nx842_coprocs);
+static unsigned int nx842_ct;
+
+/**
+ * setup_indirect_dde - Setup an indirect DDE
+ *
+ * The DDE is setup with the the DDE count, byte count, and address of
+ * first direct DDE in the list.
+ */
+static void setup_indirect_dde(struct data_descriptor_entry *dde,
+                              struct data_descriptor_entry *ddl,
+                              unsigned int dde_count, unsigned int byte_count)
+{
+       dde->flags = 0;
+       dde->count = dde_count;
+       dde->index = 0;
+       dde->length = cpu_to_be32(byte_count);
+       dde->address = cpu_to_be64(nx842_get_pa(ddl));
+}
+
+/**
+ * setup_direct_dde - Setup single DDE from buffer
+ *
+ * The DDE is setup with the buffer and length.  The buffer must be properly
+ * aligned.  The used length is returned.
+ * Returns:
+ *   N    Successfully set up DDE with N bytes
+ */
+static unsigned int setup_direct_dde(struct data_descriptor_entry *dde,
+                                    unsigned long pa, unsigned int len)
+{
+       unsigned int l = min_t(unsigned int, len, LEN_ON_PAGE(pa));
+
+       dde->flags = 0;
+       dde->count = 0;
+       dde->index = 0;
+       dde->length = cpu_to_be32(l);
+       dde->address = cpu_to_be64(pa);
+
+       return l;
+}
+
+/**
+ * setup_ddl - Setup DDL from buffer
+ *
+ * Returns:
+ *   0         Successfully set up DDL
+ */
+static int setup_ddl(struct data_descriptor_entry *dde,
+                    struct data_descriptor_entry *ddl,
+                    unsigned char *buf, unsigned int len,
+                    bool in)
+{
+       unsigned long pa = nx842_get_pa(buf);
+       int i, ret, total_len = len;
+
+       if (!IS_ALIGNED(pa, DDE_BUFFER_ALIGN)) {
+               pr_debug("%s buffer pa 0x%lx not 0x%x-byte aligned\n",
+                        in ? "input" : "output", pa, DDE_BUFFER_ALIGN);
+               return -EINVAL;
+       }
+
+       /* only need to check last mult; since buffer must be
+        * DDE_BUFFER_ALIGN aligned, and that is a multiple of
+        * DDE_BUFFER_SIZE_MULT, and pre-last page DDE buffers
+        * are guaranteed a multiple of DDE_BUFFER_SIZE_MULT.
+        */
+       if (len % DDE_BUFFER_LAST_MULT) {
+               pr_debug("%s buffer len 0x%x not a multiple of 0x%x\n",
+                        in ? "input" : "output", len, DDE_BUFFER_LAST_MULT);
+               if (in)
+                       return -EINVAL;
+               len = round_down(len, DDE_BUFFER_LAST_MULT);
+       }
+
+       /* use a single direct DDE */
+       if (len <= LEN_ON_PAGE(pa)) {
+               ret = setup_direct_dde(dde, pa, len);
+               WARN_ON(ret < len);
+               return 0;
+       }
+
+       /* use the DDL */
+       for (i = 0; i < DDL_LEN_MAX && len > 0; i++) {
+               ret = setup_direct_dde(&ddl[i], pa, len);
+               buf += ret;
+               len -= ret;
+               pa = nx842_get_pa(buf);
+       }
+
+       if (len > 0) {
+               pr_debug("0x%x total %s bytes 0x%x too many for DDL.\n",
+                        total_len, in ? "input" : "output", len);
+               if (in)
+                       return -EMSGSIZE;
+               total_len -= len;
+       }
+       setup_indirect_dde(dde, ddl, i, total_len);
+
+       return 0;
+}
+
+#define CSB_ERR(csb, msg, ...)                                 \
+       pr_err("ERROR: " msg " : %02x %02x %02x %02x %08x\n",   \
+              ##__VA_ARGS__, (csb)->flags,                     \
+              (csb)->cs, (csb)->cc, (csb)->ce,                 \
+              be32_to_cpu((csb)->count))
+
+#define CSB_ERR_ADDR(csb, msg, ...)                            \
+       CSB_ERR(csb, msg " at %lx", ##__VA_ARGS__,              \
+               (unsigned long)be64_to_cpu((csb)->address))
+
+/**
+ * wait_for_csb
+ */
+static int wait_for_csb(struct nx842_workmem *wmem,
+                       struct coprocessor_status_block *csb)
+{
+       ktime_t start = wmem->start, now = ktime_get();
+       ktime_t timeout = ktime_add_ms(start, CSB_WAIT_MAX);
+
+       while (!(ACCESS_ONCE(csb->flags) & CSB_V)) {
+               cpu_relax();
+               now = ktime_get();
+               if (ktime_after(now, timeout))
+                       break;
+       }
+
+       /* hw has updated csb and output buffer */
+       barrier();
+
+       /* check CSB flags */
+       if (!(csb->flags & CSB_V)) {
+               CSB_ERR(csb, "CSB still not valid after %ld us, giving up",
+                       (long)ktime_us_delta(now, start));
+               return -ETIMEDOUT;
+       }
+       if (csb->flags & CSB_F) {
+               CSB_ERR(csb, "Invalid CSB format");
+               return -EPROTO;
+       }
+       if (csb->flags & CSB_CH) {
+               CSB_ERR(csb, "Invalid CSB chaining state");
+               return -EPROTO;
+       }
+
+       /* verify CSB completion sequence is 0 */
+       if (csb->cs) {
+               CSB_ERR(csb, "Invalid CSB completion sequence");
+               return -EPROTO;
+       }
+
+       /* check CSB Completion Code */
+       switch (csb->cc) {
+       /* no error */
+       case CSB_CC_SUCCESS:
+               break;
+       case CSB_CC_TPBC_GT_SPBC:
+               /* not an error, but the compressed data is
+                * larger than the uncompressed data :(
+                */
+               break;
+
+       /* input data errors */
+       case CSB_CC_OPERAND_OVERLAP:
+               /* input and output buffers overlap */
+               CSB_ERR(csb, "Operand Overlap error");
+               return -EINVAL;
+       case CSB_CC_INVALID_OPERAND:
+               CSB_ERR(csb, "Invalid operand");
+               return -EINVAL;
+       case CSB_CC_NOSPC:
+               /* output buffer too small */
+               return -ENOSPC;
+       case CSB_CC_ABORT:
+               CSB_ERR(csb, "Function aborted");
+               return -EINTR;
+       case CSB_CC_CRC_MISMATCH:
+               CSB_ERR(csb, "CRC mismatch");
+               return -EINVAL;
+       case CSB_CC_TEMPL_INVALID:
+               CSB_ERR(csb, "Compressed data template invalid");
+               return -EINVAL;
+       case CSB_CC_TEMPL_OVERFLOW:
+               CSB_ERR(csb, "Compressed data template shows data past end");
+               return -EINVAL;
+
+       /* these should not happen */
+       case CSB_CC_INVALID_ALIGN:
+               /* setup_ddl should have detected this */
+               CSB_ERR_ADDR(csb, "Invalid alignment");
+               return -EINVAL;
+       case CSB_CC_DATA_LENGTH:
+               /* setup_ddl should have detected this */
+               CSB_ERR(csb, "Invalid data length");
+               return -EINVAL;
+       case CSB_CC_WR_TRANSLATION:
+       case CSB_CC_TRANSLATION:
+       case CSB_CC_TRANSLATION_DUP1:
+       case CSB_CC_TRANSLATION_DUP2:
+       case CSB_CC_TRANSLATION_DUP3:
+       case CSB_CC_TRANSLATION_DUP4:
+       case CSB_CC_TRANSLATION_DUP5:
+       case CSB_CC_TRANSLATION_DUP6:
+               /* should not happen, we use physical addrs */
+               CSB_ERR_ADDR(csb, "Translation error");
+               return -EPROTO;
+       case CSB_CC_WR_PROTECTION:
+       case CSB_CC_PROTECTION:
+       case CSB_CC_PROTECTION_DUP1:
+       case CSB_CC_PROTECTION_DUP2:
+       case CSB_CC_PROTECTION_DUP3:
+       case CSB_CC_PROTECTION_DUP4:
+       case CSB_CC_PROTECTION_DUP5:
+       case CSB_CC_PROTECTION_DUP6:
+               /* should not happen, we use physical addrs */
+               CSB_ERR_ADDR(csb, "Protection error");
+               return -EPROTO;
+       case CSB_CC_PRIVILEGE:
+               /* shouldn't happen, we're in HYP mode */
+               CSB_ERR(csb, "Insufficient Privilege error");
+               return -EPROTO;
+       case CSB_CC_EXCESSIVE_DDE:
+               /* shouldn't happen, setup_ddl doesn't use many dde's */
+               CSB_ERR(csb, "Too many DDEs in DDL");
+               return -EINVAL;
+       case CSB_CC_TRANSPORT:
+               /* shouldn't happen, we setup CRB correctly */
+               CSB_ERR(csb, "Invalid CRB");
+               return -EINVAL;
+       case CSB_CC_SEGMENTED_DDL:
+               /* shouldn't happen, setup_ddl creates DDL right */
+               CSB_ERR(csb, "Segmented DDL error");
+               return -EINVAL;
+       case CSB_CC_DDE_OVERFLOW:
+               /* shouldn't happen, setup_ddl creates DDL right */
+               CSB_ERR(csb, "DDE overflow error");
+               return -EINVAL;
+       case CSB_CC_SESSION:
+               /* should not happen with ICSWX */
+               CSB_ERR(csb, "Session violation error");
+               return -EPROTO;
+       case CSB_CC_CHAIN:
+               /* should not happen, we don't use chained CRBs */
+               CSB_ERR(csb, "Chained CRB error");
+               return -EPROTO;
+       case CSB_CC_SEQUENCE:
+               /* should not happen, we don't use chained CRBs */
+               CSB_ERR(csb, "CRB seqeunce number error");
+               return -EPROTO;
+       case CSB_CC_UNKNOWN_CODE:
+               CSB_ERR(csb, "Unknown subfunction code");
+               return -EPROTO;
+
+       /* hardware errors */
+       case CSB_CC_RD_EXTERNAL:
+       case CSB_CC_RD_EXTERNAL_DUP1:
+       case CSB_CC_RD_EXTERNAL_DUP2:
+       case CSB_CC_RD_EXTERNAL_DUP3:
+               CSB_ERR_ADDR(csb, "Read error outside coprocessor");
+               return -EPROTO;
+       case CSB_CC_WR_EXTERNAL:
+               CSB_ERR_ADDR(csb, "Write error outside coprocessor");
+               return -EPROTO;
+       case CSB_CC_INTERNAL:
+               CSB_ERR(csb, "Internal error in coprocessor");
+               return -EPROTO;
+       case CSB_CC_PROVISION:
+               CSB_ERR(csb, "Storage provision error");
+               return -EPROTO;
+       case CSB_CC_HW:
+               CSB_ERR(csb, "Correctable hardware error");
+               return -EPROTO;
+
+       default:
+               CSB_ERR(csb, "Invalid CC %d", csb->cc);
+               return -EPROTO;
+       }
+
+       /* check Completion Extension state */
+       if (csb->ce & CSB_CE_TERMINATION) {
+               CSB_ERR(csb, "CSB request was terminated");
+               return -EPROTO;
+       }
+       if (csb->ce & CSB_CE_INCOMPLETE) {
+               CSB_ERR(csb, "CSB request not complete");
+               return -EPROTO;
+       }
+       if (!(csb->ce & CSB_CE_TPBC)) {
+               CSB_ERR(csb, "TPBC not provided, unknown target length");
+               return -EPROTO;
+       }
+
+       /* successful completion */
+       pr_debug_ratelimited("Processed %u bytes in %lu us\n", csb->count,
+                            (unsigned long)ktime_us_delta(now, start));
+
+       return 0;
+}
+
+/**
+ * nx842_powernv_function - compress/decompress data using the 842 algorithm
+ *
+ * (De)compression provided by the NX842 coprocessor on IBM PowerNV systems.
+ * This compresses or decompresses the provided input buffer into the provided
+ * output buffer.
+ *
+ * Upon return from this function @outlen contains the length of the
+ * output data.  If there is an error then @outlen will be 0 and an
+ * error will be specified by the return code from this function.
+ *
+ * The @workmem buffer should only be used by one function call at a time.
+ *
+ * @in: input buffer pointer
+ * @inlen: input buffer size
+ * @out: output buffer pointer
+ * @outlenp: output buffer size pointer
+ * @workmem: working memory buffer pointer, size determined by
+ *           nx842_powernv_driver.workmem_size
+ * @fc: function code, see CCW Function Codes in nx-842.h
+ *
+ * Returns:
+ *   0         Success, output of length @outlenp stored in the buffer at @out
+ *   -ENODEV   Hardware unavailable
+ *   -ENOSPC   Output buffer is to small
+ *   -EMSGSIZE Input buffer too large
+ *   -EINVAL   buffer constraints do not fix nx842_constraints
+ *   -EPROTO   hardware error during operation
+ *   -ETIMEDOUT        hardware did not complete operation in reasonable time
+ *   -EINTR    operation was aborted
+ */
+static int nx842_powernv_function(const unsigned char *in, unsigned int inlen,
+                                 unsigned char *out, unsigned int *outlenp,
+                                 void *workmem, int fc)
+{
+       struct coprocessor_request_block *crb;
+       struct coprocessor_status_block *csb;
+       struct nx842_workmem *wmem;
+       int ret;
+       u64 csb_addr;
+       u32 ccw;
+       unsigned int outlen = *outlenp;
+
+       wmem = PTR_ALIGN(workmem, WORKMEM_ALIGN);
+
+       *outlenp = 0;
+
+       /* shoudn't happen, we don't load without a coproc */
+       if (!nx842_ct) {
+               pr_err_ratelimited("coprocessor CT is 0");
+               return -ENODEV;
+       }
+
+       crb = &wmem->crb;
+       csb = &crb->csb;
+
+       /* Clear any previous values */
+       memset(crb, 0, sizeof(*crb));
+
+       /* set up DDLs */
+       ret = setup_ddl(&crb->source, wmem->ddl_in,
+                       (unsigned char *)in, inlen, true);
+       if (ret)
+               return ret;
+       ret = setup_ddl(&crb->target, wmem->ddl_out,
+                       out, outlen, false);
+       if (ret)
+               return ret;
+
+       /* set up CCW */
+       ccw = 0;
+       ccw = SET_FIELD(ccw, CCW_CT, nx842_ct);
+       ccw = SET_FIELD(ccw, CCW_CI_842, 0); /* use 0 for hw auto-selection */
+       ccw = SET_FIELD(ccw, CCW_FC_842, fc);
+
+       /* set up CRB's CSB addr */
+       csb_addr = nx842_get_pa(csb) & CRB_CSB_ADDRESS;
+       csb_addr |= CRB_CSB_AT; /* Addrs are phys */
+       crb->csb_addr = cpu_to_be64(csb_addr);
+
+       wmem->start = ktime_get();
+
+       /* do ICSWX */
+       ret = icswx(cpu_to_be32(ccw), crb);
+
+       pr_debug_ratelimited("icswx CR %x ccw %x crb->ccw %x\n", ret,
+                            (unsigned int)ccw,
+                            (unsigned int)be32_to_cpu(crb->ccw));
+
+       switch (ret) {
+       case ICSWX_INITIATED:
+               ret = wait_for_csb(wmem, csb);
+               break;
+       case ICSWX_BUSY:
+               pr_debug_ratelimited("842 Coprocessor busy\n");
+               ret = -EBUSY;
+               break;
+       case ICSWX_REJECTED:
+               pr_err_ratelimited("ICSWX rejected\n");
+               ret = -EPROTO;
+               break;
+       default:
+               pr_err_ratelimited("Invalid ICSWX return code %x\n", ret);
+               ret = -EPROTO;
+               break;
+       }
+
+       if (!ret)
+               *outlenp = be32_to_cpu(csb->count);
+
+       return ret;
+}
+
+/**
+ * nx842_powernv_compress - Compress data using the 842 algorithm
+ *
+ * Compression provided by the NX842 coprocessor on IBM PowerNV systems.
+ * The input buffer is compressed and the result is stored in the
+ * provided output buffer.
+ *
+ * Upon return from this function @outlen contains the length of the
+ * compressed data.  If there is an error then @outlen will be 0 and an
+ * error will be specified by the return code from this function.
+ *
+ * @in: input buffer pointer
+ * @inlen: input buffer size
+ * @out: output buffer pointer
+ * @outlenp: output buffer size pointer
+ * @workmem: working memory buffer pointer, size determined by
+ *           nx842_powernv_driver.workmem_size
+ *
+ * Returns: see @nx842_powernv_function()
+ */
+static int nx842_powernv_compress(const unsigned char *in, unsigned int inlen,
+                                 unsigned char *out, unsigned int *outlenp,
+                                 void *wmem)
+{
+       return nx842_powernv_function(in, inlen, out, outlenp,
+                                     wmem, CCW_FC_842_COMP_NOCRC);
+}
+
+/**
+ * nx842_powernv_decompress - Decompress data using the 842 algorithm
+ *
+ * Decompression provided by the NX842 coprocessor on IBM PowerNV systems.
+ * The input buffer is decompressed and the result is stored in the
+ * provided output buffer.
+ *
+ * Upon return from this function @outlen contains the length of the
+ * decompressed data.  If there is an error then @outlen will be 0 and an
+ * error will be specified by the return code from this function.
+ *
+ * @in: input buffer pointer
+ * @inlen: input buffer size
+ * @out: output buffer pointer
+ * @outlenp: output buffer size pointer
+ * @workmem: working memory buffer pointer, size determined by
+ *           nx842_powernv_driver.workmem_size
+ *
+ * Returns: see @nx842_powernv_function()
+ */
+static int nx842_powernv_decompress(const unsigned char *in, unsigned int inlen,
+                                   unsigned char *out, unsigned int *outlenp,
+                                   void *wmem)
+{
+       return nx842_powernv_function(in, inlen, out, outlenp,
+                                     wmem, CCW_FC_842_DECOMP_NOCRC);
+}
+
+static int __init nx842_powernv_probe(struct device_node *dn)
+{
+       struct nx842_coproc *coproc;
+       struct property *ct_prop, *ci_prop;
+       unsigned int ct, ci;
+       int chip_id;
+
+       chip_id = of_get_ibm_chip_id(dn);
+       if (chip_id < 0) {
+               pr_err("ibm,chip-id missing\n");
+               return -EINVAL;
+       }
+       ct_prop = of_find_property(dn, "ibm,842-coprocessor-type", NULL);
+       if (!ct_prop) {
+               pr_err("ibm,842-coprocessor-type missing\n");
+               return -EINVAL;
+       }
+       ct = be32_to_cpu(*(unsigned int *)ct_prop->value);
+       ci_prop = of_find_property(dn, "ibm,842-coprocessor-instance", NULL);
+       if (!ci_prop) {
+               pr_err("ibm,842-coprocessor-instance missing\n");
+               return -EINVAL;
+       }
+       ci = be32_to_cpu(*(unsigned int *)ci_prop->value);
+
+       coproc = kmalloc(sizeof(*coproc), GFP_KERNEL);
+       if (!coproc)
+               return -ENOMEM;
+
+       coproc->chip_id = chip_id;
+       coproc->ct = ct;
+       coproc->ci = ci;
+       INIT_LIST_HEAD(&coproc->list);
+       list_add(&coproc->list, &nx842_coprocs);
+
+       pr_info("coprocessor found on chip %d, CT %d CI %d\n", chip_id, ct, ci);
+
+       if (!nx842_ct)
+               nx842_ct = ct;
+       else if (nx842_ct != ct)
+               pr_err("NX842 chip %d, CT %d != first found CT %d\n",
+                      chip_id, ct, nx842_ct);
+
+       return 0;
+}
+
+static struct nx842_constraints nx842_powernv_constraints = {
+       .alignment =    DDE_BUFFER_ALIGN,
+       .multiple =     DDE_BUFFER_LAST_MULT,
+       .minimum =      DDE_BUFFER_LAST_MULT,
+       .maximum =      (DDL_LEN_MAX - 1) * PAGE_SIZE,
+};
+
+static struct nx842_driver nx842_powernv_driver = {
+       .name =         KBUILD_MODNAME,
+       .owner =        THIS_MODULE,
+       .workmem_size = sizeof(struct nx842_workmem),
+       .constraints =  &nx842_powernv_constraints,
+       .compress =     nx842_powernv_compress,
+       .decompress =   nx842_powernv_decompress,
+};
+
+static __init int nx842_powernv_init(void)
+{
+       struct device_node *dn;
+
+       /* verify workmem size/align restrictions */
+       BUILD_BUG_ON(WORKMEM_ALIGN % CRB_ALIGN);
+       BUILD_BUG_ON(CRB_ALIGN % DDE_ALIGN);
+       BUILD_BUG_ON(CRB_SIZE % DDE_ALIGN);
+       /* verify buffer size/align restrictions */
+       BUILD_BUG_ON(PAGE_SIZE % DDE_BUFFER_ALIGN);
+       BUILD_BUG_ON(DDE_BUFFER_ALIGN % DDE_BUFFER_SIZE_MULT);
+       BUILD_BUG_ON(DDE_BUFFER_SIZE_MULT % DDE_BUFFER_LAST_MULT);
+
+       pr_info("loading\n");
+
+       for_each_compatible_node(dn, NULL, "ibm,power-nx")
+               nx842_powernv_probe(dn);
+
+       if (!nx842_ct) {
+               pr_err("no coprocessors found\n");
+               return -ENODEV;
+       }
+
+       if (!nx842_platform_driver_set(&nx842_powernv_driver)) {
+               struct nx842_coproc *coproc, *n;
+
+               list_for_each_entry_safe(coproc, n, &nx842_coprocs, list) {
+                       list_del(&coproc->list);
+                       kfree(coproc);
+               }
+
+               return -EEXIST;
+       }
+
+       pr_info("loaded\n");
+
+       return 0;
+}
+module_init(nx842_powernv_init);
+
+static void __exit nx842_powernv_exit(void)
+{
+       struct nx842_coproc *coproc, *n;
+
+       nx842_platform_driver_unset(&nx842_powernv_driver);
+
+       list_for_each_entry_safe(coproc, n, &nx842_coprocs, list) {
+               list_del(&coproc->list);
+               kfree(coproc);
+       }
+
+       pr_info("unloaded\n");
+}
+module_exit(nx842_powernv_exit);
diff --git a/drivers/crypto/nx/nx-842-pseries.c b/drivers/crypto/nx/nx-842-pseries.c
new file mode 100644 (file)
index 0000000..3040a60
--- /dev/null
@@ -0,0 +1,1140 @@
+/*
+ * Driver for IBM Power 842 compression accelerator
+ *
+ * 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, 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ * Copyright (C) IBM Corporation, 2012
+ *
+ * Authors: Robert Jennings <rcj@linux.vnet.ibm.com>
+ *          Seth Jennings <sjenning@linux.vnet.ibm.com>
+ */
+
+#include <asm/vio.h>
+
+#include "nx-842.h"
+#include "nx_csbcpb.h" /* struct nx_csbcpb */
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Robert Jennings <rcj@linux.vnet.ibm.com>");
+MODULE_DESCRIPTION("842 H/W Compression driver for IBM Power processors");
+
+static struct nx842_constraints nx842_pseries_constraints = {
+       .alignment =    DDE_BUFFER_ALIGN,
+       .multiple =     DDE_BUFFER_LAST_MULT,
+       .minimum =      DDE_BUFFER_LAST_MULT,
+       .maximum =      PAGE_SIZE, /* dynamic, max_sync_size */
+};
+
+static int check_constraints(unsigned long buf, unsigned int *len, bool in)
+{
+       if (!IS_ALIGNED(buf, nx842_pseries_constraints.alignment)) {
+               pr_debug("%s buffer 0x%lx not aligned to 0x%x\n",
+                        in ? "input" : "output", buf,
+                        nx842_pseries_constraints.alignment);
+               return -EINVAL;
+       }
+       if (*len % nx842_pseries_constraints.multiple) {
+               pr_debug("%s buffer len 0x%x not multiple of 0x%x\n",
+                        in ? "input" : "output", *len,
+                        nx842_pseries_constraints.multiple);
+               if (in)
+                       return -EINVAL;
+               *len = round_down(*len, nx842_pseries_constraints.multiple);
+       }
+       if (*len < nx842_pseries_constraints.minimum) {
+               pr_debug("%s buffer len 0x%x under minimum 0x%x\n",
+                        in ? "input" : "output", *len,
+                        nx842_pseries_constraints.minimum);
+               return -EINVAL;
+       }
+       if (*len > nx842_pseries_constraints.maximum) {
+               pr_debug("%s buffer len 0x%x over maximum 0x%x\n",
+                        in ? "input" : "output", *len,
+                        nx842_pseries_constraints.maximum);
+               if (in)
+                       return -EINVAL;
+               *len = nx842_pseries_constraints.maximum;
+       }
+       return 0;
+}
+
+/* I assume we need to align the CSB? */
+#define WORKMEM_ALIGN  (256)
+
+struct nx842_workmem {
+       /* scatterlist */
+       char slin[4096];
+       char slout[4096];
+       /* coprocessor status/parameter block */
+       struct nx_csbcpb csbcpb;
+
+       char padding[WORKMEM_ALIGN];
+} __aligned(WORKMEM_ALIGN);
+
+/* Macros for fields within nx_csbcpb */
+/* Check the valid bit within the csbcpb valid field */
+#define NX842_CSBCBP_VALID_CHK(x) (x & BIT_MASK(7))
+
+/* CE macros operate on the completion_extension field bits in the csbcpb.
+ * CE0 0=full completion, 1=partial completion
+ * CE1 0=CE0 indicates completion, 1=termination (output may be modified)
+ * CE2 0=processed_bytes is source bytes, 1=processed_bytes is target bytes */
+#define NX842_CSBCPB_CE0(x)    (x & BIT_MASK(7))
+#define NX842_CSBCPB_CE1(x)    (x & BIT_MASK(6))
+#define NX842_CSBCPB_CE2(x)    (x & BIT_MASK(5))
+
+/* The NX unit accepts data only on 4K page boundaries */
+#define NX842_HW_PAGE_SIZE     (4096)
+#define NX842_HW_PAGE_MASK     (~(NX842_HW_PAGE_SIZE-1))
+
+enum nx842_status {
+       UNAVAILABLE,
+       AVAILABLE
+};
+
+struct ibm_nx842_counters {
+       atomic64_t comp_complete;
+       atomic64_t comp_failed;
+       atomic64_t decomp_complete;
+       atomic64_t decomp_failed;
+       atomic64_t swdecomp;
+       atomic64_t comp_times[32];
+       atomic64_t decomp_times[32];
+};
+
+static struct nx842_devdata {
+       struct vio_dev *vdev;
+       struct device *dev;
+       struct ibm_nx842_counters *counters;
+       unsigned int max_sg_len;
+       unsigned int max_sync_size;
+       unsigned int max_sync_sg;
+       enum nx842_status status;
+} __rcu *devdata;
+static DEFINE_SPINLOCK(devdata_mutex);
+
+#define NX842_COUNTER_INC(_x) \
+static inline void nx842_inc_##_x( \
+       const struct nx842_devdata *dev) { \
+       if (dev) \
+               atomic64_inc(&dev->counters->_x); \
+}
+NX842_COUNTER_INC(comp_complete);
+NX842_COUNTER_INC(comp_failed);
+NX842_COUNTER_INC(decomp_complete);
+NX842_COUNTER_INC(decomp_failed);
+NX842_COUNTER_INC(swdecomp);
+
+#define NX842_HIST_SLOTS 16
+
+static void ibm_nx842_incr_hist(atomic64_t *times, unsigned int time)
+{
+       int bucket = fls(time);
+
+       if (bucket)
+               bucket = min((NX842_HIST_SLOTS - 1), bucket - 1);
+
+       atomic64_inc(&times[bucket]);
+}
+
+/* NX unit operation flags */
+#define NX842_OP_COMPRESS      0x0
+#define NX842_OP_CRC           0x1
+#define NX842_OP_DECOMPRESS    0x2
+#define NX842_OP_COMPRESS_CRC   (NX842_OP_COMPRESS | NX842_OP_CRC)
+#define NX842_OP_DECOMPRESS_CRC (NX842_OP_DECOMPRESS | NX842_OP_CRC)
+#define NX842_OP_ASYNC         (1<<23)
+#define NX842_OP_NOTIFY                (1<<22)
+#define NX842_OP_NOTIFY_INT(x) ((x & 0xff)<<8)
+
+static unsigned long nx842_get_desired_dma(struct vio_dev *viodev)
+{
+       /* No use of DMA mappings within the driver. */
+       return 0;
+}
+
+struct nx842_slentry {
+       __be64 ptr; /* Real address (use __pa()) */
+       __be64 len;
+};
+
+/* pHyp scatterlist entry */
+struct nx842_scatterlist {
+       int entry_nr; /* number of slentries */
+       struct nx842_slentry *entries; /* ptr to array of slentries */
+};
+
+/* Does not include sizeof(entry_nr) in the size */
+static inline unsigned long nx842_get_scatterlist_size(
+                               struct nx842_scatterlist *sl)
+{
+       return sl->entry_nr * sizeof(struct nx842_slentry);
+}
+
+static int nx842_build_scatterlist(unsigned long buf, int len,
+                       struct nx842_scatterlist *sl)
+{
+       unsigned long entrylen;
+       struct nx842_slentry *entry;
+
+       sl->entry_nr = 0;
+
+       entry = sl->entries;
+       while (len) {
+               entry->ptr = cpu_to_be64(nx842_get_pa((void *)buf));
+               entrylen = min_t(int, len,
+                                LEN_ON_SIZE(buf, NX842_HW_PAGE_SIZE));
+               entry->len = cpu_to_be64(entrylen);
+
+               len -= entrylen;
+               buf += entrylen;
+
+               sl->entry_nr++;
+               entry++;
+       }
+
+       return 0;
+}
+
+static int nx842_validate_result(struct device *dev,
+       struct cop_status_block *csb)
+{
+       /* The csb must be valid after returning from vio_h_cop_sync */
+       if (!NX842_CSBCBP_VALID_CHK(csb->valid)) {
+               dev_err(dev, "%s: cspcbp not valid upon completion.\n",
+                               __func__);
+               dev_dbg(dev, "valid:0x%02x cs:0x%02x cc:0x%02x ce:0x%02x\n",
+                               csb->valid,
+                               csb->crb_seq_number,
+                               csb->completion_code,
+                               csb->completion_extension);
+               dev_dbg(dev, "processed_bytes:%d address:0x%016lx\n",
+                               be32_to_cpu(csb->processed_byte_count),
+                               (unsigned long)be64_to_cpu(csb->address));
+               return -EIO;
+       }
+
+       /* Check return values from the hardware in the CSB */
+       switch (csb->completion_code) {
+       case 0: /* Completed without error */
+               break;
+       case 64: /* Target bytes > Source bytes during compression */
+       case 13: /* Output buffer too small */
+               dev_dbg(dev, "%s: Compression output larger than input\n",
+                                       __func__);
+               return -ENOSPC;
+       case 66: /* Input data contains an illegal template field */
+       case 67: /* Template indicates data past the end of the input stream */
+               dev_dbg(dev, "%s: Bad data for decompression (code:%d)\n",
+                                       __func__, csb->completion_code);
+               return -EINVAL;
+       default:
+               dev_dbg(dev, "%s: Unspecified error (code:%d)\n",
+                                       __func__, csb->completion_code);
+               return -EIO;
+       }
+
+       /* Hardware sanity check */
+       if (!NX842_CSBCPB_CE2(csb->completion_extension)) {
+               dev_err(dev, "%s: No error returned by hardware, but "
+                               "data returned is unusable, contact support.\n"
+                               "(Additional info: csbcbp->processed bytes "
+                               "does not specify processed bytes for the "
+                               "target buffer.)\n", __func__);
+               return -EIO;
+       }
+
+       return 0;
+}
+
+/**
+ * nx842_pseries_compress - Compress data using the 842 algorithm
+ *
+ * Compression provide by the NX842 coprocessor on IBM Power systems.
+ * The input buffer is compressed and the result is stored in the
+ * provided output buffer.
+ *
+ * Upon return from this function @outlen contains the length of the
+ * compressed data.  If there is an error then @outlen will be 0 and an
+ * error will be specified by the return code from this function.
+ *
+ * @in: Pointer to input buffer
+ * @inlen: Length of input buffer
+ * @out: Pointer to output buffer
+ * @outlen: Length of output buffer
+ * @wrkmem: ptr to buffer for working memory, size determined by
+ *          nx842_pseries_driver.workmem_size
+ *
+ * Returns:
+ *   0         Success, output of length @outlen stored in the buffer at @out
+ *   -ENOMEM   Unable to allocate internal buffers
+ *   -ENOSPC   Output buffer is to small
+ *   -EIO      Internal error
+ *   -ENODEV   Hardware unavailable
+ */
+static int nx842_pseries_compress(const unsigned char *in, unsigned int inlen,
+                                 unsigned char *out, unsigned int *outlen,
+                                 void *wmem)
+{
+       struct nx842_devdata *local_devdata;
+       struct device *dev = NULL;
+       struct nx842_workmem *workmem;
+       struct nx842_scatterlist slin, slout;
+       struct nx_csbcpb *csbcpb;
+       int ret = 0, max_sync_size;
+       unsigned long inbuf, outbuf;
+       struct vio_pfo_op op = {
+               .done = NULL,
+               .handle = 0,
+               .timeout = 0,
+       };
+       unsigned long start = get_tb();
+
+       inbuf = (unsigned long)in;
+       if (check_constraints(inbuf, &inlen, true))
+               return -EINVAL;
+
+       outbuf = (unsigned long)out;
+       if (check_constraints(outbuf, outlen, false))
+               return -EINVAL;
+
+       rcu_read_lock();
+       local_devdata = rcu_dereference(devdata);
+       if (!local_devdata || !local_devdata->dev) {
+               rcu_read_unlock();
+               return -ENODEV;
+       }
+       max_sync_size = local_devdata->max_sync_size;
+       dev = local_devdata->dev;
+
+       /* Init scatterlist */
+       workmem = PTR_ALIGN(wmem, WORKMEM_ALIGN);
+       slin.entries = (struct nx842_slentry *)workmem->slin;
+       slout.entries = (struct nx842_slentry *)workmem->slout;
+
+       /* Init operation */
+       op.flags = NX842_OP_COMPRESS;
+       csbcpb = &workmem->csbcpb;
+       memset(csbcpb, 0, sizeof(*csbcpb));
+       op.csbcpb = nx842_get_pa(csbcpb);
+
+       if ((inbuf & NX842_HW_PAGE_MASK) ==
+           ((inbuf + inlen - 1) & NX842_HW_PAGE_MASK)) {
+               /* Create direct DDE */
+               op.in = nx842_get_pa((void *)inbuf);
+               op.inlen = inlen;
+       } else {
+               /* Create indirect DDE (scatterlist) */
+               nx842_build_scatterlist(inbuf, inlen, &slin);
+               op.in = nx842_get_pa(slin.entries);
+               op.inlen = -nx842_get_scatterlist_size(&slin);
+       }
+
+       if ((outbuf & NX842_HW_PAGE_MASK) ==
+           ((outbuf + *outlen - 1) & NX842_HW_PAGE_MASK)) {
+               /* Create direct DDE */
+               op.out = nx842_get_pa((void *)outbuf);
+               op.outlen = *outlen;
+       } else {
+               /* Create indirect DDE (scatterlist) */
+               nx842_build_scatterlist(outbuf, *outlen, &slout);
+               op.out = nx842_get_pa(slout.entries);
+               op.outlen = -nx842_get_scatterlist_size(&slout);
+       }
+
+       dev_dbg(dev, "%s: op.in %lx op.inlen %ld op.out %lx op.outlen %ld\n",
+               __func__, (unsigned long)op.in, (long)op.inlen,
+               (unsigned long)op.out, (long)op.outlen);
+
+       /* Send request to pHyp */
+       ret = vio_h_cop_sync(local_devdata->vdev, &op);
+
+       /* Check for pHyp error */
+       if (ret) {
+               dev_dbg(dev, "%s: vio_h_cop_sync error (ret=%d, hret=%ld)\n",
+                       __func__, ret, op.hcall_err);
+               ret = -EIO;
+               goto unlock;
+       }
+
+       /* Check for hardware error */
+       ret = nx842_validate_result(dev, &csbcpb->csb);
+       if (ret)
+               goto unlock;
+
+       *outlen = be32_to_cpu(csbcpb->csb.processed_byte_count);
+       dev_dbg(dev, "%s: processed_bytes=%d\n", __func__, *outlen);
+
+unlock:
+       if (ret)
+               nx842_inc_comp_failed(local_devdata);
+       else {
+               nx842_inc_comp_complete(local_devdata);
+               ibm_nx842_incr_hist(local_devdata->counters->comp_times,
+                       (get_tb() - start) / tb_ticks_per_usec);
+       }
+       rcu_read_unlock();
+       return ret;
+}
+
+/**
+ * nx842_pseries_decompress - Decompress data using the 842 algorithm
+ *
+ * Decompression provide by the NX842 coprocessor on IBM Power systems.
+ * The input buffer is decompressed and the result is stored in the
+ * provided output buffer.  The size allocated to the output buffer is
+ * provided by the caller of this function in @outlen.  Upon return from
+ * this function @outlen contains the length of the decompressed data.
+ * If there is an error then @outlen will be 0 and an error will be
+ * specified by the return code from this function.
+ *
+ * @in: Pointer to input buffer
+ * @inlen: Length of input buffer
+ * @out: Pointer to output buffer
+ * @outlen: Length of output buffer
+ * @wrkmem: ptr to buffer for working memory, size determined by
+ *          nx842_pseries_driver.workmem_size
+ *
+ * Returns:
+ *   0         Success, output of length @outlen stored in the buffer at @out
+ *   -ENODEV   Hardware decompression device is unavailable
+ *   -ENOMEM   Unable to allocate internal buffers
+ *   -ENOSPC   Output buffer is to small
+ *   -EINVAL   Bad input data encountered when attempting decompress
+ *   -EIO      Internal error
+ */
+static int nx842_pseries_decompress(const unsigned char *in, unsigned int inlen,
+                                   unsigned char *out, unsigned int *outlen,
+                                   void *wmem)
+{
+       struct nx842_devdata *local_devdata;
+       struct device *dev = NULL;
+       struct nx842_workmem *workmem;
+       struct nx842_scatterlist slin, slout;
+       struct nx_csbcpb *csbcpb;
+       int ret = 0, max_sync_size;
+       unsigned long inbuf, outbuf;
+       struct vio_pfo_op op = {
+               .done = NULL,
+               .handle = 0,
+               .timeout = 0,
+       };
+       unsigned long start = get_tb();
+
+       /* Ensure page alignment and size */
+       inbuf = (unsigned long)in;
+       if (check_constraints(inbuf, &inlen, true))
+               return -EINVAL;
+
+       outbuf = (unsigned long)out;
+       if (check_constraints(outbuf, outlen, false))
+               return -EINVAL;
+
+       rcu_read_lock();
+       local_devdata = rcu_dereference(devdata);
+       if (!local_devdata || !local_devdata->dev) {
+               rcu_read_unlock();
+               return -ENODEV;
+       }
+       max_sync_size = local_devdata->max_sync_size;
+       dev = local_devdata->dev;
+
+       workmem = PTR_ALIGN(wmem, WORKMEM_ALIGN);
+
+       /* Init scatterlist */
+       slin.entries = (struct nx842_slentry *)workmem->slin;
+       slout.entries = (struct nx842_slentry *)workmem->slout;
+
+       /* Init operation */
+       op.flags = NX842_OP_DECOMPRESS;
+       csbcpb = &workmem->csbcpb;
+       memset(csbcpb, 0, sizeof(*csbcpb));
+       op.csbcpb = nx842_get_pa(csbcpb);
+
+       if ((inbuf & NX842_HW_PAGE_MASK) ==
+           ((inbuf + inlen - 1) & NX842_HW_PAGE_MASK)) {
+               /* Create direct DDE */
+               op.in = nx842_get_pa((void *)inbuf);
+               op.inlen = inlen;
+       } else {
+               /* Create indirect DDE (scatterlist) */
+               nx842_build_scatterlist(inbuf, inlen, &slin);
+               op.in = nx842_get_pa(slin.entries);
+               op.inlen = -nx842_get_scatterlist_size(&slin);
+       }
+
+       if ((outbuf & NX842_HW_PAGE_MASK) ==
+           ((outbuf + *outlen - 1) & NX842_HW_PAGE_MASK)) {
+               /* Create direct DDE */
+               op.out = nx842_get_pa((void *)outbuf);
+               op.outlen = *outlen;
+       } else {
+               /* Create indirect DDE (scatterlist) */
+               nx842_build_scatterlist(outbuf, *outlen, &slout);
+               op.out = nx842_get_pa(slout.entries);
+               op.outlen = -nx842_get_scatterlist_size(&slout);
+       }
+
+       dev_dbg(dev, "%s: op.in %lx op.inlen %ld op.out %lx op.outlen %ld\n",
+               __func__, (unsigned long)op.in, (long)op.inlen,
+               (unsigned long)op.out, (long)op.outlen);
+
+       /* Send request to pHyp */
+       ret = vio_h_cop_sync(local_devdata->vdev, &op);
+
+       /* Check for pHyp error */
+       if (ret) {
+               dev_dbg(dev, "%s: vio_h_cop_sync error (ret=%d, hret=%ld)\n",
+                       __func__, ret, op.hcall_err);
+               goto unlock;
+       }
+
+       /* Check for hardware error */
+       ret = nx842_validate_result(dev, &csbcpb->csb);
+       if (ret)
+               goto unlock;
+
+       *outlen = be32_to_cpu(csbcpb->csb.processed_byte_count);
+
+unlock:
+       if (ret)
+               /* decompress fail */
+               nx842_inc_decomp_failed(local_devdata);
+       else {
+               nx842_inc_decomp_complete(local_devdata);
+               ibm_nx842_incr_hist(local_devdata->counters->decomp_times,
+                       (get_tb() - start) / tb_ticks_per_usec);
+       }
+
+       rcu_read_unlock();
+       return ret;
+}
+
+/**
+ * nx842_OF_set_defaults -- Set default (disabled) values for devdata
+ *
+ * @devdata - struct nx842_devdata to update
+ *
+ * Returns:
+ *  0 on success
+ *  -ENOENT if @devdata ptr is NULL
+ */
+static int nx842_OF_set_defaults(struct nx842_devdata *devdata)
+{
+       if (devdata) {
+               devdata->max_sync_size = 0;
+               devdata->max_sync_sg = 0;
+               devdata->max_sg_len = 0;
+               devdata->status = UNAVAILABLE;
+               return 0;
+       } else
+               return -ENOENT;
+}
+
+/**
+ * nx842_OF_upd_status -- Update the device info from OF status prop
+ *
+ * The status property indicates if the accelerator is enabled.  If the
+ * device is in the OF tree it indicates that the hardware is present.
+ * The status field indicates if the device is enabled when the status
+ * is 'okay'.  Otherwise the device driver will be disabled.
+ *
+ * @devdata - struct nx842_devdata to update
+ * @prop - struct property point containing the maxsyncop for the update
+ *
+ * Returns:
+ *  0 - Device is available
+ *  -EINVAL - Device is not available
+ */
+static int nx842_OF_upd_status(struct nx842_devdata *devdata,
+                                       struct property *prop) {
+       int ret = 0;
+       const char *status = (const char *)prop->value;
+
+       if (!strncmp(status, "okay", (size_t)prop->length)) {
+               devdata->status = AVAILABLE;
+       } else {
+               dev_info(devdata->dev, "%s: status '%s' is not 'okay'\n",
+                               __func__, status);
+               devdata->status = UNAVAILABLE;
+       }
+
+       return ret;
+}
+
+/**
+ * nx842_OF_upd_maxsglen -- Update the device info from OF maxsglen prop
+ *
+ * Definition of the 'ibm,max-sg-len' OF property:
+ *  This field indicates the maximum byte length of a scatter list
+ *  for the platform facility. It is a single cell encoded as with encode-int.
+ *
+ * Example:
+ *  # od -x ibm,max-sg-len
+ *  0000000 0000 0ff0
+ *
+ *  In this example, the maximum byte length of a scatter list is
+ *  0x0ff0 (4,080).
+ *
+ * @devdata - struct nx842_devdata to update
+ * @prop - struct property point containing the maxsyncop for the update
+ *
+ * Returns:
+ *  0 on success
+ *  -EINVAL on failure
+ */
+static int nx842_OF_upd_maxsglen(struct nx842_devdata *devdata,
+                                       struct property *prop) {
+       int ret = 0;
+       const unsigned int maxsglen = of_read_number(prop->value, 1);
+
+       if (prop->length != sizeof(maxsglen)) {
+               dev_err(devdata->dev, "%s: unexpected format for ibm,max-sg-len property\n", __func__);
+               dev_dbg(devdata->dev, "%s: ibm,max-sg-len is %d bytes long, expected %lu bytes\n", __func__,
+                               prop->length, sizeof(maxsglen));
+               ret = -EINVAL;
+       } else {
+               devdata->max_sg_len = min_t(unsigned int,
+                                           maxsglen, NX842_HW_PAGE_SIZE);
+       }
+
+       return ret;
+}
+
+/**
+ * nx842_OF_upd_maxsyncop -- Update the device info from OF maxsyncop prop
+ *
+ * Definition of the 'ibm,max-sync-cop' OF property:
+ *  Two series of cells.  The first series of cells represents the maximums
+ *  that can be synchronously compressed. The second series of cells
+ *  represents the maximums that can be synchronously decompressed.
+ *  1. The first cell in each series contains the count of the number of
+ *     data length, scatter list elements pairs that follow – each being
+ *     of the form
+ *    a. One cell data byte length
+ *    b. One cell total number of scatter list elements
+ *
+ * Example:
+ *  # od -x ibm,max-sync-cop
+ *  0000000 0000 0001 0000 1000 0000 01fe 0000 0001
+ *  0000020 0000 1000 0000 01fe
+ *
+ *  In this example, compression supports 0x1000 (4,096) data byte length
+ *  and 0x1fe (510) total scatter list elements.  Decompression supports
+ *  0x1000 (4,096) data byte length and 0x1f3 (510) total scatter list
+ *  elements.
+ *
+ * @devdata - struct nx842_devdata to update
+ * @prop - struct property point containing the maxsyncop for the update
+ *
+ * Returns:
+ *  0 on success
+ *  -EINVAL on failure
+ */
+static int nx842_OF_upd_maxsyncop(struct nx842_devdata *devdata,
+                                       struct property *prop) {
+       int ret = 0;
+       unsigned int comp_data_limit, decomp_data_limit;
+       unsigned int comp_sg_limit, decomp_sg_limit;
+       const struct maxsynccop_t {
+               __be32 comp_elements;
+               __be32 comp_data_limit;
+               __be32 comp_sg_limit;
+               __be32 decomp_elements;
+               __be32 decomp_data_limit;
+               __be32 decomp_sg_limit;
+       } *maxsynccop;
+
+       if (prop->length != sizeof(*maxsynccop)) {
+               dev_err(devdata->dev, "%s: unexpected format for ibm,max-sync-cop property\n", __func__);
+               dev_dbg(devdata->dev, "%s: ibm,max-sync-cop is %d bytes long, expected %lu bytes\n", __func__, prop->length,
+                               sizeof(*maxsynccop));
+               ret = -EINVAL;
+               goto out;
+       }
+
+       maxsynccop = (const struct maxsynccop_t *)prop->value;
+       comp_data_limit = be32_to_cpu(maxsynccop->comp_data_limit);
+       comp_sg_limit = be32_to_cpu(maxsynccop->comp_sg_limit);
+       decomp_data_limit = be32_to_cpu(maxsynccop->decomp_data_limit);
+       decomp_sg_limit = be32_to_cpu(maxsynccop->decomp_sg_limit);
+
+       /* Use one limit rather than separate limits for compression and
+        * decompression. Set a maximum for this so as not to exceed the
+        * size that the header can support and round the value down to
+        * the hardware page size (4K) */
+       devdata->max_sync_size = min(comp_data_limit, decomp_data_limit);
+
+       devdata->max_sync_size = min_t(unsigned int, devdata->max_sync_size,
+                                       65536);
+
+       if (devdata->max_sync_size < 4096) {
+               dev_err(devdata->dev, "%s: hardware max data size (%u) is "
+                               "less than the driver minimum, unable to use "
+                               "the hardware device\n",
+                               __func__, devdata->max_sync_size);
+               ret = -EINVAL;
+               goto out;
+       }
+
+       nx842_pseries_constraints.maximum = devdata->max_sync_size;
+
+       devdata->max_sync_sg = min(comp_sg_limit, decomp_sg_limit);
+       if (devdata->max_sync_sg < 1) {
+               dev_err(devdata->dev, "%s: hardware max sg size (%u) is "
+                               "less than the driver minimum, unable to use "
+                               "the hardware device\n",
+                               __func__, devdata->max_sync_sg);
+               ret = -EINVAL;
+               goto out;
+       }
+
+out:
+       return ret;
+}
+
+/**
+ *
+ * nx842_OF_upd -- Handle OF properties updates for the device.
+ *
+ * Set all properties from the OF tree.  Optionally, a new property
+ * can be provided by the @new_prop pointer to overwrite an existing value.
+ * The device will remain disabled until all values are valid, this function
+ * will return an error for updates unless all values are valid.
+ *
+ * @new_prop: If not NULL, this property is being updated.  If NULL, update
+ *  all properties from the current values in the OF tree.
+ *
+ * Returns:
+ *  0 - Success
+ *  -ENOMEM - Could not allocate memory for new devdata structure
+ *  -EINVAL - property value not found, new_prop is not a recognized
+ *     property for the device or property value is not valid.
+ *  -ENODEV - Device is not available
+ */
+static int nx842_OF_upd(struct property *new_prop)
+{
+       struct nx842_devdata *old_devdata = NULL;
+       struct nx842_devdata *new_devdata = NULL;
+       struct device_node *of_node = NULL;
+       struct property *status = NULL;
+       struct property *maxsglen = NULL;
+       struct property *maxsyncop = NULL;
+       int ret = 0;
+       unsigned long flags;
+
+       spin_lock_irqsave(&devdata_mutex, flags);
+       old_devdata = rcu_dereference_check(devdata,
+                       lockdep_is_held(&devdata_mutex));
+       if (old_devdata)
+               of_node = old_devdata->dev->of_node;
+
+       if (!old_devdata || !of_node) {
+               pr_err("%s: device is not available\n", __func__);
+               spin_unlock_irqrestore(&devdata_mutex, flags);
+               return -ENODEV;
+       }
+
+       new_devdata = kzalloc(sizeof(*new_devdata), GFP_NOFS);
+       if (!new_devdata) {
+               dev_err(old_devdata->dev, "%s: Could not allocate memory for device data\n", __func__);
+               ret = -ENOMEM;
+               goto error_out;
+       }
+
+       memcpy(new_devdata, old_devdata, sizeof(*old_devdata));
+       new_devdata->counters = old_devdata->counters;
+
+       /* Set ptrs for existing properties */
+       status = of_find_property(of_node, "status", NULL);
+       maxsglen = of_find_property(of_node, "ibm,max-sg-len", NULL);
+       maxsyncop = of_find_property(of_node, "ibm,max-sync-cop", NULL);
+       if (!status || !maxsglen || !maxsyncop) {
+               dev_err(old_devdata->dev, "%s: Could not locate device properties\n", __func__);
+               ret = -EINVAL;
+               goto error_out;
+       }
+
+       /*
+        * If this is a property update, there are only certain properties that
+        * we care about. Bail if it isn't in the below list
+        */
+       if (new_prop && (strncmp(new_prop->name, "status", new_prop->length) ||
+                        strncmp(new_prop->name, "ibm,max-sg-len", new_prop->length) ||
+                        strncmp(new_prop->name, "ibm,max-sync-cop", new_prop->length)))
+               goto out;
+
+       /* Perform property updates */
+       ret = nx842_OF_upd_status(new_devdata, status);
+       if (ret)
+               goto error_out;
+
+       ret = nx842_OF_upd_maxsglen(new_devdata, maxsglen);
+       if (ret)
+               goto error_out;
+
+       ret = nx842_OF_upd_maxsyncop(new_devdata, maxsyncop);
+       if (ret)
+               goto error_out;
+
+out:
+       dev_info(old_devdata->dev, "%s: max_sync_size new:%u old:%u\n",
+                       __func__, new_devdata->max_sync_size,
+                       old_devdata->max_sync_size);
+       dev_info(old_devdata->dev, "%s: max_sync_sg new:%u old:%u\n",
+                       __func__, new_devdata->max_sync_sg,
+                       old_devdata->max_sync_sg);
+       dev_info(old_devdata->dev, "%s: max_sg_len new:%u old:%u\n",
+                       __func__, new_devdata->max_sg_len,
+                       old_devdata->max_sg_len);
+
+       rcu_assign_pointer(devdata, new_devdata);
+       spin_unlock_irqrestore(&devdata_mutex, flags);
+       synchronize_rcu();
+       dev_set_drvdata(new_devdata->dev, new_devdata);
+       kfree(old_devdata);
+       return 0;
+
+error_out:
+       if (new_devdata) {
+               dev_info(old_devdata->dev, "%s: device disabled\n", __func__);
+               nx842_OF_set_defaults(new_devdata);
+               rcu_assign_pointer(devdata, new_devdata);
+               spin_unlock_irqrestore(&devdata_mutex, flags);
+               synchronize_rcu();
+               dev_set_drvdata(new_devdata->dev, new_devdata);
+               kfree(old_devdata);
+       } else {
+               dev_err(old_devdata->dev, "%s: could not update driver from hardware\n", __func__);
+               spin_unlock_irqrestore(&devdata_mutex, flags);
+       }
+
+       if (!ret)
+               ret = -EINVAL;
+       return ret;
+}
+
+/**
+ * nx842_OF_notifier - Process updates to OF properties for the device
+ *
+ * @np: notifier block
+ * @action: notifier action
+ * @update: struct pSeries_reconfig_prop_update pointer if action is
+ *     PSERIES_UPDATE_PROPERTY
+ *
+ * Returns:
+ *     NOTIFY_OK on success
+ *     NOTIFY_BAD encoded with error number on failure, use
+ *             notifier_to_errno() to decode this value
+ */
+static int nx842_OF_notifier(struct notifier_block *np, unsigned long action,
+                            void *data)
+{
+       struct of_reconfig_data *upd = data;
+       struct nx842_devdata *local_devdata;
+       struct device_node *node = NULL;
+
+       rcu_read_lock();
+       local_devdata = rcu_dereference(devdata);
+       if (local_devdata)
+               node = local_devdata->dev->of_node;
+
+       if (local_devdata &&
+                       action == OF_RECONFIG_UPDATE_PROPERTY &&
+                       !strcmp(upd->dn->name, node->name)) {
+               rcu_read_unlock();
+               nx842_OF_upd(upd->prop);
+       } else
+               rcu_read_unlock();
+
+       return NOTIFY_OK;
+}
+
+static struct notifier_block nx842_of_nb = {
+       .notifier_call = nx842_OF_notifier,
+};
+
+#define nx842_counter_read(_name)                                      \
+static ssize_t nx842_##_name##_show(struct device *dev,                \
+               struct device_attribute *attr,                          \
+               char *buf) {                                            \
+       struct nx842_devdata *local_devdata;                    \
+       int p = 0;                                                      \
+       rcu_read_lock();                                                \
+       local_devdata = rcu_dereference(devdata);                       \
+       if (local_devdata)                                              \
+               p = snprintf(buf, PAGE_SIZE, "%ld\n",                   \
+                      atomic64_read(&local_devdata->counters->_name)); \
+       rcu_read_unlock();                                              \
+       return p;                                                       \
+}
+
+#define NX842DEV_COUNTER_ATTR_RO(_name)                                        \
+       nx842_counter_read(_name);                                      \
+       static struct device_attribute dev_attr_##_name = __ATTR(_name, \
+                                               0444,                   \
+                                               nx842_##_name##_show,\
+                                               NULL);
+
+NX842DEV_COUNTER_ATTR_RO(comp_complete);
+NX842DEV_COUNTER_ATTR_RO(comp_failed);
+NX842DEV_COUNTER_ATTR_RO(decomp_complete);
+NX842DEV_COUNTER_ATTR_RO(decomp_failed);
+NX842DEV_COUNTER_ATTR_RO(swdecomp);
+
+static ssize_t nx842_timehist_show(struct device *,
+               struct device_attribute *, char *);
+
+static struct device_attribute dev_attr_comp_times = __ATTR(comp_times, 0444,
+               nx842_timehist_show, NULL);
+static struct device_attribute dev_attr_decomp_times = __ATTR(decomp_times,
+               0444, nx842_timehist_show, NULL);
+
+static ssize_t nx842_timehist_show(struct device *dev,
+               struct device_attribute *attr, char *buf) {
+       char *p = buf;
+       struct nx842_devdata *local_devdata;
+       atomic64_t *times;
+       int bytes_remain = PAGE_SIZE;
+       int bytes;
+       int i;
+
+       rcu_read_lock();
+       local_devdata = rcu_dereference(devdata);
+       if (!local_devdata) {
+               rcu_read_unlock();
+               return 0;
+       }
+
+       if (attr == &dev_attr_comp_times)
+               times = local_devdata->counters->comp_times;
+       else if (attr == &dev_attr_decomp_times)
+               times = local_devdata->counters->decomp_times;
+       else {
+               rcu_read_unlock();
+               return 0;
+       }
+
+       for (i = 0; i < (NX842_HIST_SLOTS - 2); i++) {
+               bytes = snprintf(p, bytes_remain, "%u-%uus:\t%ld\n",
+                              i ? (2<<(i-1)) : 0, (2<<i)-1,
+                              atomic64_read(&times[i]));
+               bytes_remain -= bytes;
+               p += bytes;
+       }
+       /* The last bucket holds everything over
+        * 2<<(NX842_HIST_SLOTS - 2) us */
+       bytes = snprintf(p, bytes_remain, "%uus - :\t%ld\n",
+                       2<<(NX842_HIST_SLOTS - 2),
+                       atomic64_read(&times[(NX842_HIST_SLOTS - 1)]));
+       p += bytes;
+
+       rcu_read_unlock();
+       return p - buf;
+}
+
+static struct attribute *nx842_sysfs_entries[] = {
+       &dev_attr_comp_complete.attr,
+       &dev_attr_comp_failed.attr,
+       &dev_attr_decomp_complete.attr,
+       &dev_attr_decomp_failed.attr,
+       &dev_attr_swdecomp.attr,
+       &dev_attr_comp_times.attr,
+       &dev_attr_decomp_times.attr,
+       NULL,
+};
+
+static struct attribute_group nx842_attribute_group = {
+       .name = NULL,           /* put in device directory */
+       .attrs = nx842_sysfs_entries,
+};
+
+static struct nx842_driver nx842_pseries_driver = {
+       .name =         KBUILD_MODNAME,
+       .owner =        THIS_MODULE,
+       .workmem_size = sizeof(struct nx842_workmem),
+       .constraints =  &nx842_pseries_constraints,
+       .compress =     nx842_pseries_compress,
+       .decompress =   nx842_pseries_decompress,
+};
+
+static int __init nx842_probe(struct vio_dev *viodev,
+                                 const struct vio_device_id *id)
+{
+       struct nx842_devdata *old_devdata, *new_devdata = NULL;
+       unsigned long flags;
+       int ret = 0;
+
+       spin_lock_irqsave(&devdata_mutex, flags);
+       old_devdata = rcu_dereference_check(devdata,
+                       lockdep_is_held(&devdata_mutex));
+
+       if (old_devdata && old_devdata->vdev != NULL) {
+               dev_err(&viodev->dev, "%s: Attempt to register more than one instance of the hardware\n", __func__);
+               ret = -1;
+               goto error_unlock;
+       }
+
+       dev_set_drvdata(&viodev->dev, NULL);
+
+       new_devdata = kzalloc(sizeof(*new_devdata), GFP_NOFS);
+       if (!new_devdata) {
+               dev_err(&viodev->dev, "%s: Could not allocate memory for device data\n", __func__);
+               ret = -ENOMEM;
+               goto error_unlock;
+       }
+
+       new_devdata->counters = kzalloc(sizeof(*new_devdata->counters),
+                       GFP_NOFS);
+       if (!new_devdata->counters) {
+               dev_err(&viodev->dev, "%s: Could not allocate memory for performance counters\n", __func__);
+               ret = -ENOMEM;
+               goto error_unlock;
+       }
+
+       new_devdata->vdev = viodev;
+       new_devdata->dev = &viodev->dev;
+       nx842_OF_set_defaults(new_devdata);
+
+       rcu_assign_pointer(devdata, new_devdata);
+       spin_unlock_irqrestore(&devdata_mutex, flags);
+       synchronize_rcu();
+       kfree(old_devdata);
+
+       of_reconfig_notifier_register(&nx842_of_nb);
+
+       ret = nx842_OF_upd(NULL);
+       if (ret && ret != -ENODEV) {
+               dev_err(&viodev->dev, "could not parse device tree. %d\n", ret);
+               ret = -1;
+               goto error;
+       }
+
+       rcu_read_lock();
+       dev_set_drvdata(&viodev->dev, rcu_dereference(devdata));
+       rcu_read_unlock();
+
+       if (sysfs_create_group(&viodev->dev.kobj, &nx842_attribute_group)) {
+               dev_err(&viodev->dev, "could not create sysfs device attributes\n");
+               ret = -1;
+               goto error;
+       }
+
+       return 0;
+
+error_unlock:
+       spin_unlock_irqrestore(&devdata_mutex, flags);
+       if (new_devdata)
+               kfree(new_devdata->counters);
+       kfree(new_devdata);
+error:
+       return ret;
+}
+
+static int __exit nx842_remove(struct vio_dev *viodev)
+{
+       struct nx842_devdata *old_devdata;
+       unsigned long flags;
+
+       pr_info("Removing IBM Power 842 compression device\n");
+       sysfs_remove_group(&viodev->dev.kobj, &nx842_attribute_group);
+
+       spin_lock_irqsave(&devdata_mutex, flags);
+       old_devdata = rcu_dereference_check(devdata,
+                       lockdep_is_held(&devdata_mutex));
+       of_reconfig_notifier_unregister(&nx842_of_nb);
+       RCU_INIT_POINTER(devdata, NULL);
+       spin_unlock_irqrestore(&devdata_mutex, flags);
+       synchronize_rcu();
+       dev_set_drvdata(&viodev->dev, NULL);
+       if (old_devdata)
+               kfree(old_devdata->counters);
+       kfree(old_devdata);
+
+       return 0;
+}
+
+static struct vio_device_id nx842_vio_driver_ids[] = {
+       {"ibm,compression-v1", "ibm,compression"},
+       {"", ""},
+};
+
+static struct vio_driver nx842_vio_driver = {
+       .name = KBUILD_MODNAME,
+       .probe = nx842_probe,
+       .remove = __exit_p(nx842_remove),
+       .get_desired_dma = nx842_get_desired_dma,
+       .id_table = nx842_vio_driver_ids,
+};
+
+static int __init nx842_init(void)
+{
+       struct nx842_devdata *new_devdata;
+       int ret;
+
+       pr_info("Registering IBM Power 842 compression driver\n");
+
+       if (!of_find_compatible_node(NULL, NULL, "ibm,compression"))
+               return -ENODEV;
+
+       RCU_INIT_POINTER(devdata, NULL);
+       new_devdata = kzalloc(sizeof(*new_devdata), GFP_KERNEL);
+       if (!new_devdata) {
+               pr_err("Could not allocate memory for device data\n");
+               return -ENOMEM;
+       }
+       new_devdata->status = UNAVAILABLE;
+       RCU_INIT_POINTER(devdata, new_devdata);
+
+       ret = vio_register_driver(&nx842_vio_driver);
+       if (ret) {
+               pr_err("Could not register VIO driver %d\n", ret);
+
+               kfree(new_devdata);
+               return ret;
+       }
+
+       if (!nx842_platform_driver_set(&nx842_pseries_driver)) {
+               vio_unregister_driver(&nx842_vio_driver);
+               kfree(new_devdata);
+               return -EEXIST;
+       }
+
+       return 0;
+}
+
+module_init(nx842_init);
+
+static void __exit nx842_exit(void)
+{
+       struct nx842_devdata *old_devdata;
+       unsigned long flags;
+
+       pr_info("Exiting IBM Power 842 compression driver\n");
+       nx842_platform_driver_unset(&nx842_pseries_driver);
+       spin_lock_irqsave(&devdata_mutex, flags);
+       old_devdata = rcu_dereference_check(devdata,
+                       lockdep_is_held(&devdata_mutex));
+       RCU_INIT_POINTER(devdata, NULL);
+       spin_unlock_irqrestore(&devdata_mutex, flags);
+       synchronize_rcu();
+       if (old_devdata && old_devdata->dev)
+               dev_set_drvdata(old_devdata->dev, NULL);
+       kfree(old_devdata);
+       vio_unregister_driver(&nx842_vio_driver);
+}
+
+module_exit(nx842_exit);
+
index 887196e9b50c08250ee57fac412c14b1b8be0142..6e5e0d60d0c8c9b887a19fe9e9f7a193ee298882 100644 (file)
@@ -1,5 +1,10 @@
 /*
- * Driver for IBM Power 842 compression accelerator
+ * Driver frontend for IBM Power 842 compression accelerator
+ *
+ * Copyright (C) 2015 Dan Streetman, IBM Corp
+ *
+ * Designer of the Power data compression engine:
+ *   Bulent Abali <abali@us.ibm.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
  * 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, 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- *
- * Copyright (C) IBM Corporation, 2012
- *
- * Authors: Robert Jennings <rcj@linux.vnet.ibm.com>
- *          Seth Jennings <sjenning@linux.vnet.ibm.com>
  */
 
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/nx842.h>
-#include <linux/of.h>
-#include <linux/slab.h>
-
-#include <asm/page.h>
-#include <asm/vio.h>
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
-#include "nx_csbcpb.h" /* struct nx_csbcpb */
+#include "nx-842.h"
 
-#define MODULE_NAME "nx-compress"
 MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Robert Jennings <rcj@linux.vnet.ibm.com>");
+MODULE_AUTHOR("Dan Streetman <ddstreet@ieee.org>");
 MODULE_DESCRIPTION("842 H/W Compression driver for IBM Power processors");
 
-#define SHIFT_4K 12
-#define SHIFT_64K 16
-#define SIZE_4K (1UL << SHIFT_4K)
-#define SIZE_64K (1UL << SHIFT_64K)
-
-/* IO buffer must be 128 byte aligned */
-#define IO_BUFFER_ALIGN 128
-
-struct nx842_header {
-       int blocks_nr; /* number of compressed blocks */
-       int offset; /* offset of the first block (from beginning of header) */
-       int sizes[0]; /* size of compressed blocks */
-};
-
-static inline int nx842_header_size(const struct nx842_header *hdr)
-{
-       return sizeof(struct nx842_header) +
-                       hdr->blocks_nr * sizeof(hdr->sizes[0]);
-}
-
-/* Macros for fields within nx_csbcpb */
-/* Check the valid bit within the csbcpb valid field */
-#define NX842_CSBCBP_VALID_CHK(x) (x & BIT_MASK(7))
-
-/* CE macros operate on the completion_extension field bits in the csbcpb.
- * CE0 0=full completion, 1=partial completion
- * CE1 0=CE0 indicates completion, 1=termination (output may be modified)
- * CE2 0=processed_bytes is source bytes, 1=processed_bytes is target bytes */
-#define NX842_CSBCPB_CE0(x)    (x & BIT_MASK(7))
-#define NX842_CSBCPB_CE1(x)    (x & BIT_MASK(6))
-#define NX842_CSBCPB_CE2(x)    (x & BIT_MASK(5))
-
-/* The NX unit accepts data only on 4K page boundaries */
-#define NX842_HW_PAGE_SHIFT    SHIFT_4K
-#define NX842_HW_PAGE_SIZE     (ASM_CONST(1) << NX842_HW_PAGE_SHIFT)
-#define NX842_HW_PAGE_MASK     (~(NX842_HW_PAGE_SIZE-1))
-
-enum nx842_status {
-       UNAVAILABLE,
-       AVAILABLE
-};
-
-struct ibm_nx842_counters {
-       atomic64_t comp_complete;
-       atomic64_t comp_failed;
-       atomic64_t decomp_complete;
-       atomic64_t decomp_failed;
-       atomic64_t swdecomp;
-       atomic64_t comp_times[32];
-       atomic64_t decomp_times[32];
-};
-
-static struct nx842_devdata {
-       struct vio_dev *vdev;
-       struct device *dev;
-       struct ibm_nx842_counters *counters;
-       unsigned int max_sg_len;
-       unsigned int max_sync_size;
-       unsigned int max_sync_sg;
-       enum nx842_status status;
-} __rcu *devdata;
-static DEFINE_SPINLOCK(devdata_mutex);
-
-#define NX842_COUNTER_INC(_x) \
-static inline void nx842_inc_##_x( \
-       const struct nx842_devdata *dev) { \
-       if (dev) \
-               atomic64_inc(&dev->counters->_x); \
-}
-NX842_COUNTER_INC(comp_complete);
-NX842_COUNTER_INC(comp_failed);
-NX842_COUNTER_INC(decomp_complete);
-NX842_COUNTER_INC(decomp_failed);
-NX842_COUNTER_INC(swdecomp);
-
-#define NX842_HIST_SLOTS 16
-
-static void ibm_nx842_incr_hist(atomic64_t *times, unsigned int time)
-{
-       int bucket = fls(time);
-
-       if (bucket)
-               bucket = min((NX842_HIST_SLOTS - 1), bucket - 1);
-
-       atomic64_inc(&times[bucket]);
-}
-
-/* NX unit operation flags */
-#define NX842_OP_COMPRESS      0x0
-#define NX842_OP_CRC           0x1
-#define NX842_OP_DECOMPRESS    0x2
-#define NX842_OP_COMPRESS_CRC   (NX842_OP_COMPRESS | NX842_OP_CRC)
-#define NX842_OP_DECOMPRESS_CRC (NX842_OP_DECOMPRESS | NX842_OP_CRC)
-#define NX842_OP_ASYNC         (1<<23)
-#define NX842_OP_NOTIFY                (1<<22)
-#define NX842_OP_NOTIFY_INT(x) ((x & 0xff)<<8)
-
-static unsigned long nx842_get_desired_dma(struct vio_dev *viodev)
-{
-       /* No use of DMA mappings within the driver. */
-       return 0;
-}
-
-struct nx842_slentry {
-       unsigned long ptr; /* Real address (use __pa()) */
-       unsigned long len;
-};
-
-/* pHyp scatterlist entry */
-struct nx842_scatterlist {
-       int entry_nr; /* number of slentries */
-       struct nx842_slentry *entries; /* ptr to array of slentries */
-};
-
-/* Does not include sizeof(entry_nr) in the size */
-static inline unsigned long nx842_get_scatterlist_size(
-                               struct nx842_scatterlist *sl)
-{
-       return sl->entry_nr * sizeof(struct nx842_slentry);
-}
-
-static inline unsigned long nx842_get_pa(void *addr)
-{
-       if (is_vmalloc_addr(addr))
-               return page_to_phys(vmalloc_to_page(addr))
-                      + offset_in_page(addr);
-       else
-               return __pa(addr);
-}
-
-static int nx842_build_scatterlist(unsigned long buf, int len,
-                       struct nx842_scatterlist *sl)
-{
-       unsigned long nextpage;
-       struct nx842_slentry *entry;
-
-       sl->entry_nr = 0;
-
-       entry = sl->entries;
-       while (len) {
-               entry->ptr = nx842_get_pa((void *)buf);
-               nextpage = ALIGN(buf + 1, NX842_HW_PAGE_SIZE);
-               if (nextpage < buf + len) {
-                       /* we aren't at the end yet */
-                       if (IS_ALIGNED(buf, NX842_HW_PAGE_SIZE))
-                               /* we are in the middle (or beginning) */
-                               entry->len = NX842_HW_PAGE_SIZE;
-                       else
-                               /* we are at the beginning */
-                               entry->len = nextpage - buf;
-               } else {
-                       /* at the end */
-                       entry->len = len;
-               }
-
-               len -= entry->len;
-               buf += entry->len;
-               sl->entry_nr++;
-               entry++;
-       }
-
-       return 0;
-}
-
-/*
- * Working memory for software decompression
- */
-struct sw842_fifo {
-       union {
-               char f8[256][8];
-               char f4[512][4];
-       };
-       char f2[256][2];
-       unsigned char f84_full;
-       unsigned char f2_full;
-       unsigned char f8_count;
-       unsigned char f2_count;
-       unsigned int f4_count;
-};
-
-/*
- * Working memory for crypto API
+/**
+ * nx842_constraints
+ *
+ * This provides the driver's constraints.  Different nx842 implementations
+ * may have varying requirements.  The constraints are:
+ *   @alignment:       All buffers should be aligned to this
+ *   @multiple:                All buffer lengths should be a multiple of this
+ *   @minimum:         Buffer lengths must not be less than this amount
+ *   @maximum:         Buffer lengths must not be more than this amount
+ *
+ * The constraints apply to all buffers and lengths, both input and output,
+ * for both compression and decompression, except for the minimum which
+ * only applies to compression input and decompression output; the
+ * compressed data can be less than the minimum constraint.  It can be
+ * assumed that compressed data will always adhere to the multiple
+ * constraint.
+ *
+ * The driver may succeed even if these constraints are violated;
+ * however the driver can return failure or suffer reduced performance
+ * if any constraint is not met.
  */
-struct nx842_workmem {
-       char bounce[PAGE_SIZE]; /* bounce buffer for decompression input */
-       union {
-               /* hardware working memory */
-               struct {
-                       /* scatterlist */
-                       char slin[SIZE_4K];
-                       char slout[SIZE_4K];
-                       /* coprocessor status/parameter block */
-                       struct nx_csbcpb csbcpb;
-               };
-               /* software working memory */
-               struct sw842_fifo swfifo; /* software decompression fifo */
-       };
-};
-
-int nx842_get_workmem_size(void)
-{
-       return sizeof(struct nx842_workmem) + NX842_HW_PAGE_SIZE;
-}
-EXPORT_SYMBOL_GPL(nx842_get_workmem_size);
-
-int nx842_get_workmem_size_aligned(void)
-{
-       return sizeof(struct nx842_workmem);
-}
-EXPORT_SYMBOL_GPL(nx842_get_workmem_size_aligned);
-
-static int nx842_validate_result(struct device *dev,
-       struct cop_status_block *csb)
+int nx842_constraints(struct nx842_constraints *c)
 {
-       /* The csb must be valid after returning from vio_h_cop_sync */
-       if (!NX842_CSBCBP_VALID_CHK(csb->valid)) {
-               dev_err(dev, "%s: cspcbp not valid upon completion.\n",
-                               __func__);
-               dev_dbg(dev, "valid:0x%02x cs:0x%02x cc:0x%02x ce:0x%02x\n",
-                               csb->valid,
-                               csb->crb_seq_number,
-                               csb->completion_code,
-                               csb->completion_extension);
-               dev_dbg(dev, "processed_bytes:%d address:0x%016lx\n",
-                               csb->processed_byte_count,
-                               (unsigned long)csb->address);
-               return -EIO;
-       }
-
-       /* Check return values from the hardware in the CSB */
-       switch (csb->completion_code) {
-       case 0: /* Completed without error */
-               break;
-       case 64: /* Target bytes > Source bytes during compression */
-       case 13: /* Output buffer too small */
-               dev_dbg(dev, "%s: Compression output larger than input\n",
-                                       __func__);
-               return -ENOSPC;
-       case 66: /* Input data contains an illegal template field */
-       case 67: /* Template indicates data past the end of the input stream */
-               dev_dbg(dev, "%s: Bad data for decompression (code:%d)\n",
-                                       __func__, csb->completion_code);
-               return -EINVAL;
-       default:
-               dev_dbg(dev, "%s: Unspecified error (code:%d)\n",
-                                       __func__, csb->completion_code);
-               return -EIO;
-       }
-
-       /* Hardware sanity check */
-       if (!NX842_CSBCPB_CE2(csb->completion_extension)) {
-               dev_err(dev, "%s: No error returned by hardware, but "
-                               "data returned is unusable, contact support.\n"
-                               "(Additional info: csbcbp->processed bytes "
-                               "does not specify processed bytes for the "
-                               "target buffer.)\n", __func__);
-               return -EIO;
-       }
-
+       memcpy(c, nx842_platform_driver()->constraints, sizeof(*c));
        return 0;
 }
+EXPORT_SYMBOL_GPL(nx842_constraints);
 
 /**
- * nx842_compress - Compress data using the 842 algorithm
- *
- * Compression provide by the NX842 coprocessor on IBM Power systems.
- * The input buffer is compressed and the result is stored in the
- * provided output buffer.
- *
- * Upon return from this function @outlen contains the length of the
- * compressed data.  If there is an error then @outlen will be 0 and an
- * error will be specified by the return code from this function.
- *
- * @in: Pointer to input buffer, must be page aligned
- * @inlen: Length of input buffer, must be PAGE_SIZE
- * @out: Pointer to output buffer
- * @outlen: Length of output buffer
- * @wrkmem: ptr to buffer for working memory, size determined by
- *          nx842_get_workmem_size()
+ * nx842_workmem_size
  *
- * Returns:
- *   0         Success, output of length @outlen stored in the buffer at @out
- *   -ENOMEM   Unable to allocate internal buffers
- *   -ENOSPC   Output buffer is to small
- *   -EMSGSIZE XXX Difficult to describe this limitation
- *   -EIO      Internal error
- *   -ENODEV   Hardware unavailable
+ * Get the amount of working memory the driver requires.
  */
-int nx842_compress(const unsigned char *in, unsigned int inlen,
-                      unsigned char *out, unsigned int *outlen, void *wmem)
+size_t nx842_workmem_size(void)
 {
-       struct nx842_header *hdr;
-       struct nx842_devdata *local_devdata;
-       struct device *dev = NULL;
-       struct nx842_workmem *workmem;
-       struct nx842_scatterlist slin, slout;
-       struct nx_csbcpb *csbcpb;
-       int ret = 0, max_sync_size, i, bytesleft, size, hdrsize;
-       unsigned long inbuf, outbuf, padding;
-       struct vio_pfo_op op = {
-               .done = NULL,
-               .handle = 0,
-               .timeout = 0,
-       };
-       unsigned long start_time = get_tb();
-
-       /*
-        * Make sure input buffer is 64k page aligned.  This is assumed since
-        * this driver is designed for page compression only (for now).  This
-        * is very nice since we can now use direct DDE(s) for the input and
-        * the alignment is guaranteed.
-       */
-       inbuf = (unsigned long)in;
-       if (!IS_ALIGNED(inbuf, PAGE_SIZE) || inlen != PAGE_SIZE)
-               return -EINVAL;
-
-       rcu_read_lock();
-       local_devdata = rcu_dereference(devdata);
-       if (!local_devdata || !local_devdata->dev) {
-               rcu_read_unlock();
-               return -ENODEV;
-       }
-       max_sync_size = local_devdata->max_sync_size;
-       dev = local_devdata->dev;
-
-       /* Create the header */
-       hdr = (struct nx842_header *)out;
-       hdr->blocks_nr = PAGE_SIZE / max_sync_size;
-       hdrsize = nx842_header_size(hdr);
-       outbuf = (unsigned long)out + hdrsize;
-       bytesleft = *outlen - hdrsize;
-
-       /* Init scatterlist */
-       workmem = (struct nx842_workmem *)ALIGN((unsigned long)wmem,
-               NX842_HW_PAGE_SIZE);
-       slin.entries = (struct nx842_slentry *)workmem->slin;
-       slout.entries = (struct nx842_slentry *)workmem->slout;
-
-       /* Init operation */
-       op.flags = NX842_OP_COMPRESS;
-       csbcpb = &workmem->csbcpb;
-       memset(csbcpb, 0, sizeof(*csbcpb));
-       op.csbcpb = nx842_get_pa(csbcpb);
-       op.out = nx842_get_pa(slout.entries);
-
-       for (i = 0; i < hdr->blocks_nr; i++) {
-               /*
-                * Aligning the output blocks to 128 bytes does waste space,
-                * but it prevents the need for bounce buffers and memory
-                * copies.  It also simplifies the code a lot.  In the worst
-                * case (64k page, 4k max_sync_size), you lose up to
-                * (128*16)/64k = ~3% the compression factor. For 64k
-                * max_sync_size, the loss would be at most 128/64k = ~0.2%.
-                */
-               padding = ALIGN(outbuf, IO_BUFFER_ALIGN) - outbuf;
-               outbuf += padding;
-               bytesleft -= padding;
-               if (i == 0)
-                       /* save offset into first block in header */
-                       hdr->offset = padding + hdrsize;
-
-               if (bytesleft <= 0) {
-                       ret = -ENOSPC;
-                       goto unlock;
-               }
-
-               /*
-                * NOTE: If the default max_sync_size is changed from 4k
-                * to 64k, remove the "likely" case below, since a
-                * scatterlist will always be needed.
-                */
-               if (likely(max_sync_size == NX842_HW_PAGE_SIZE)) {
-                       /* Create direct DDE */
-                       op.in = nx842_get_pa((void *)inbuf);
-                       op.inlen = max_sync_size;
-
-               } else {
-                       /* Create indirect DDE (scatterlist) */
-                       nx842_build_scatterlist(inbuf, max_sync_size, &slin);
-                       op.in = nx842_get_pa(slin.entries);
-                       op.inlen = -nx842_get_scatterlist_size(&slin);
-               }
-
-               /*
-                * If max_sync_size != NX842_HW_PAGE_SIZE, an indirect
-                * DDE is required for the outbuf.
-                * If max_sync_size == NX842_HW_PAGE_SIZE, outbuf must
-                * also be page aligned (1 in 128/4k=32 chance) in order
-                * to use a direct DDE.
-                * This is unlikely, just use an indirect DDE always.
-                */
-               nx842_build_scatterlist(outbuf,
-                       min(bytesleft, max_sync_size), &slout);
-               /* op.out set before loop */
-               op.outlen = -nx842_get_scatterlist_size(&slout);
-
-               /* Send request to pHyp */
-               ret = vio_h_cop_sync(local_devdata->vdev, &op);
-
-               /* Check for pHyp error */
-               if (ret) {
-                       dev_dbg(dev, "%s: vio_h_cop_sync error (ret=%d, hret=%ld)\n",
-                               __func__, ret, op.hcall_err);
-                       ret = -EIO;
-                       goto unlock;
-               }
-
-               /* Check for hardware error */
-               ret = nx842_validate_result(dev, &csbcpb->csb);
-               if (ret && ret != -ENOSPC)
-                       goto unlock;
-
-               /* Handle incompressible data */
-               if (unlikely(ret == -ENOSPC)) {
-                       if (bytesleft < max_sync_size) {
-                               /*
-                                * Not enough space left in the output buffer
-                                * to store uncompressed block
-                                */
-                               goto unlock;
-                       } else {
-                               /* Store incompressible block */
-                               memcpy((void *)outbuf, (void *)inbuf,
-                                       max_sync_size);
-                               hdr->sizes[i] = -max_sync_size;
-                               outbuf += max_sync_size;
-                               bytesleft -= max_sync_size;
-                               /* Reset ret, incompressible data handled */
-                               ret = 0;
-                       }
-               } else {
-                       /* Normal case, compression was successful */
-                       size = csbcpb->csb.processed_byte_count;
-                       dev_dbg(dev, "%s: processed_bytes=%d\n",
-                               __func__, size);
-                       hdr->sizes[i] = size;
-                       outbuf += size;
-                       bytesleft -= size;
-               }
-
-               inbuf += max_sync_size;
-       }
-
-       *outlen = (unsigned int)(outbuf - (unsigned long)out);
-
-unlock:
-       if (ret)
-               nx842_inc_comp_failed(local_devdata);
-       else {
-               nx842_inc_comp_complete(local_devdata);
-               ibm_nx842_incr_hist(local_devdata->counters->comp_times,
-                       (get_tb() - start_time) / tb_ticks_per_usec);
-       }
-       rcu_read_unlock();
-       return ret;
+       return nx842_platform_driver()->workmem_size;
 }
-EXPORT_SYMBOL_GPL(nx842_compress);
-
-static int sw842_decompress(const unsigned char *, int, unsigned char *, int *,
-                       const void *);
+EXPORT_SYMBOL_GPL(nx842_workmem_size);
 
-/**
- * nx842_decompress - Decompress data using the 842 algorithm
- *
- * Decompression provide by the NX842 coprocessor on IBM Power systems.
- * The input buffer is decompressed and the result is stored in the
- * provided output buffer.  The size allocated to the output buffer is
- * provided by the caller of this function in @outlen.  Upon return from
- * this function @outlen contains the length of the decompressed data.
- * If there is an error then @outlen will be 0 and an error will be
- * specified by the return code from this function.
- *
- * @in: Pointer to input buffer, will use bounce buffer if not 128 byte
- *      aligned
- * @inlen: Length of input buffer
- * @out: Pointer to output buffer, must be page aligned
- * @outlen: Length of output buffer, must be PAGE_SIZE
- * @wrkmem: ptr to buffer for working memory, size determined by
- *          nx842_get_workmem_size()
- *
- * Returns:
- *   0         Success, output of length @outlen stored in the buffer at @out
- *   -ENODEV   Hardware decompression device is unavailable
- *   -ENOMEM   Unable to allocate internal buffers
- *   -ENOSPC   Output buffer is to small
- *   -EINVAL   Bad input data encountered when attempting decompress
- *   -EIO      Internal error
- */
-int nx842_decompress(const unsigned char *in, unsigned int inlen,
-                        unsigned char *out, unsigned int *outlen, void *wmem)
+int nx842_compress(const unsigned char *in, unsigned int ilen,
+                  unsigned char *out, unsigned int *olen, void *wmem)
 {
-       struct nx842_header *hdr;
-       struct nx842_devdata *local_devdata;
-       struct device *dev = NULL;
-       struct nx842_workmem *workmem;
-       struct nx842_scatterlist slin, slout;
-       struct nx_csbcpb *csbcpb;
-       int ret = 0, i, size, max_sync_size;
-       unsigned long inbuf, outbuf;
-       struct vio_pfo_op op = {
-               .done = NULL,
-               .handle = 0,
-               .timeout = 0,
-       };
-       unsigned long start_time = get_tb();
-
-       /* Ensure page alignment and size */
-       outbuf = (unsigned long)out;
-       if (!IS_ALIGNED(outbuf, PAGE_SIZE) || *outlen != PAGE_SIZE)
-               return -EINVAL;
-
-       rcu_read_lock();
-       local_devdata = rcu_dereference(devdata);
-       if (local_devdata)
-               dev = local_devdata->dev;
-
-       /* Get header */
-       hdr = (struct nx842_header *)in;
-
-       workmem = (struct nx842_workmem *)ALIGN((unsigned long)wmem,
-               NX842_HW_PAGE_SIZE);
-
-       inbuf = (unsigned long)in + hdr->offset;
-       if (likely(!IS_ALIGNED(inbuf, IO_BUFFER_ALIGN))) {
-               /* Copy block(s) into bounce buffer for alignment */
-               memcpy(workmem->bounce, in + hdr->offset, inlen - hdr->offset);
-               inbuf = (unsigned long)workmem->bounce;
-       }
-
-       /* Init scatterlist */
-       slin.entries = (struct nx842_slentry *)workmem->slin;
-       slout.entries = (struct nx842_slentry *)workmem->slout;
-
-       /* Init operation */
-       op.flags = NX842_OP_DECOMPRESS;
-       csbcpb = &workmem->csbcpb;
-       memset(csbcpb, 0, sizeof(*csbcpb));
-       op.csbcpb = nx842_get_pa(csbcpb);
-
-       /*
-        * max_sync_size may have changed since compression,
-        * so we can't read it from the device info. We need
-        * to derive it from hdr->blocks_nr.
-        */
-       max_sync_size = PAGE_SIZE / hdr->blocks_nr;
-
-       for (i = 0; i < hdr->blocks_nr; i++) {
-               /* Skip padding */
-               inbuf = ALIGN(inbuf, IO_BUFFER_ALIGN);
-
-               if (hdr->sizes[i] < 0) {
-                       /* Negative sizes indicate uncompressed data blocks */
-                       size = abs(hdr->sizes[i]);
-                       memcpy((void *)outbuf, (void *)inbuf, size);
-                       outbuf += size;
-                       inbuf += size;
-                       continue;
-               }
-
-               if (!dev)
-                       goto sw;
-
-               /*
-                * The better the compression, the more likely the "likely"
-                * case becomes.
-                */
-               if (likely((inbuf & NX842_HW_PAGE_MASK) ==
-                       ((inbuf + hdr->sizes[i] - 1) & NX842_HW_PAGE_MASK))) {
-                       /* Create direct DDE */
-                       op.in = nx842_get_pa((void *)inbuf);
-                       op.inlen = hdr->sizes[i];
-               } else {
-                       /* Create indirect DDE (scatterlist) */
-                       nx842_build_scatterlist(inbuf, hdr->sizes[i] , &slin);
-                       op.in = nx842_get_pa(slin.entries);
-                       op.inlen = -nx842_get_scatterlist_size(&slin);
-               }
-
-               /*
-                * NOTE: If the default max_sync_size is changed from 4k
-                * to 64k, remove the "likely" case below, since a
-                * scatterlist will always be needed.
-                */
-               if (likely(max_sync_size == NX842_HW_PAGE_SIZE)) {
-                       /* Create direct DDE */
-                       op.out = nx842_get_pa((void *)outbuf);
-                       op.outlen = max_sync_size;
-               } else {
-                       /* Create indirect DDE (scatterlist) */
-                       nx842_build_scatterlist(outbuf, max_sync_size, &slout);
-                       op.out = nx842_get_pa(slout.entries);
-                       op.outlen = -nx842_get_scatterlist_size(&slout);
-               }
-
-               /* Send request to pHyp */
-               ret = vio_h_cop_sync(local_devdata->vdev, &op);
-
-               /* Check for pHyp error */
-               if (ret) {
-                       dev_dbg(dev, "%s: vio_h_cop_sync error (ret=%d, hret=%ld)\n",
-                               __func__, ret, op.hcall_err);
-                       dev = NULL;
-                       goto sw;
-               }
-
-               /* Check for hardware error */
-               ret = nx842_validate_result(dev, &csbcpb->csb);
-               if (ret) {
-                       dev = NULL;
-                       goto sw;
-               }
-
-               /* HW decompression success */
-               inbuf += hdr->sizes[i];
-               outbuf += csbcpb->csb.processed_byte_count;
-               continue;
-
-sw:
-               /* software decompression */
-               size = max_sync_size;
-               ret = sw842_decompress(
-                       (unsigned char *)inbuf, hdr->sizes[i],
-                       (unsigned char *)outbuf, &size, wmem);
-               if (ret)
-                       pr_debug("%s: sw842_decompress failed with %d\n",
-                               __func__, ret);
-
-               if (ret) {
-                       if (ret != -ENOSPC && ret != -EINVAL &&
-                                       ret != -EMSGSIZE)
-                               ret = -EIO;
-                       goto unlock;
-               }
-
-               /* SW decompression success */
-               inbuf += hdr->sizes[i];
-               outbuf += size;
-       }
-
-       *outlen = (unsigned int)(outbuf - (unsigned long)out);
-
-unlock:
-       if (ret)
-               /* decompress fail */
-               nx842_inc_decomp_failed(local_devdata);
-       else {
-               if (!dev)
-                       /* software decompress */
-                       nx842_inc_swdecomp(local_devdata);
-               nx842_inc_decomp_complete(local_devdata);
-               ibm_nx842_incr_hist(local_devdata->counters->decomp_times,
-                       (get_tb() - start_time) / tb_ticks_per_usec);
-       }
-
-       rcu_read_unlock();
-       return ret;
+       return nx842_platform_driver()->compress(in, ilen, out, olen, wmem);
 }
-EXPORT_SYMBOL_GPL(nx842_decompress);
+EXPORT_SYMBOL_GPL(nx842_compress);
 
-/**
- * nx842_OF_set_defaults -- Set default (disabled) values for devdata
- *
- * @devdata - struct nx842_devdata to update
- *
- * Returns:
- *  0 on success
- *  -ENOENT if @devdata ptr is NULL
- */
-static int nx842_OF_set_defaults(struct nx842_devdata *devdata)
+int nx842_decompress(const unsigned char *in, unsigned int ilen,
+                    unsigned char *out, unsigned int *olen, void *wmem)
 {
-       if (devdata) {
-               devdata->max_sync_size = 0;
-               devdata->max_sync_sg = 0;
-               devdata->max_sg_len = 0;
-               devdata->status = UNAVAILABLE;
-               return 0;
-       } else
-               return -ENOENT;
-}
-
-/**
- * nx842_OF_upd_status -- Update the device info from OF status prop
- *
- * The status property indicates if the accelerator is enabled.  If the
- * device is in the OF tree it indicates that the hardware is present.
- * The status field indicates if the device is enabled when the status
- * is 'okay'.  Otherwise the device driver will be disabled.
- *
- * @devdata - struct nx842_devdata to update
- * @prop - struct property point containing the maxsyncop for the update
- *
- * Returns:
- *  0 - Device is available
- *  -EINVAL - Device is not available
- */
-static int nx842_OF_upd_status(struct nx842_devdata *devdata,
-                                       struct property *prop) {
-       int ret = 0;
-       const char *status = (const char *)prop->value;
-
-       if (!strncmp(status, "okay", (size_t)prop->length)) {
-               devdata->status = AVAILABLE;
-       } else {
-               dev_info(devdata->dev, "%s: status '%s' is not 'okay'\n",
-                               __func__, status);
-               devdata->status = UNAVAILABLE;
-       }
-
-       return ret;
-}
-
-/**
- * nx842_OF_upd_maxsglen -- Update the device info from OF maxsglen prop
- *
- * Definition of the 'ibm,max-sg-len' OF property:
- *  This field indicates the maximum byte length of a scatter list
- *  for the platform facility. It is a single cell encoded as with encode-int.
- *
- * Example:
- *  # od -x ibm,max-sg-len
- *  0000000 0000 0ff0
- *
- *  In this example, the maximum byte length of a scatter list is
- *  0x0ff0 (4,080).
- *
- * @devdata - struct nx842_devdata to update
- * @prop - struct property point containing the maxsyncop for the update
- *
- * Returns:
- *  0 on success
- *  -EINVAL on failure
- */
-static int nx842_OF_upd_maxsglen(struct nx842_devdata *devdata,
-                                       struct property *prop) {
-       int ret = 0;
-       const int *maxsglen = prop->value;
-
-       if (prop->length != sizeof(*maxsglen)) {
-               dev_err(devdata->dev, "%s: unexpected format for ibm,max-sg-len property\n", __func__);
-               dev_dbg(devdata->dev, "%s: ibm,max-sg-len is %d bytes long, expected %lu bytes\n", __func__,
-                               prop->length, sizeof(*maxsglen));
-               ret = -EINVAL;
-       } else {
-               devdata->max_sg_len = (unsigned int)min(*maxsglen,
-                               (int)NX842_HW_PAGE_SIZE);
-       }
-
-       return ret;
-}
-
-/**
- * nx842_OF_upd_maxsyncop -- Update the device info from OF maxsyncop prop
- *
- * Definition of the 'ibm,max-sync-cop' OF property:
- *  Two series of cells.  The first series of cells represents the maximums
- *  that can be synchronously compressed. The second series of cells
- *  represents the maximums that can be synchronously decompressed.
- *  1. The first cell in each series contains the count of the number of
- *     data length, scatter list elements pairs that follow – each being
- *     of the form
- *    a. One cell data byte length
- *    b. One cell total number of scatter list elements
- *
- * Example:
- *  # od -x ibm,max-sync-cop
- *  0000000 0000 0001 0000 1000 0000 01fe 0000 0001
- *  0000020 0000 1000 0000 01fe
- *
- *  In this example, compression supports 0x1000 (4,096) data byte length
- *  and 0x1fe (510) total scatter list elements.  Decompression supports
- *  0x1000 (4,096) data byte length and 0x1f3 (510) total scatter list
- *  elements.
- *
- * @devdata - struct nx842_devdata to update
- * @prop - struct property point containing the maxsyncop for the update
- *
- * Returns:
- *  0 on success
- *  -EINVAL on failure
- */
-static int nx842_OF_upd_maxsyncop(struct nx842_devdata *devdata,
-                                       struct property *prop) {
-       int ret = 0;
-       const struct maxsynccop_t {
-               int comp_elements;
-               int comp_data_limit;
-               int comp_sg_limit;
-               int decomp_elements;
-               int decomp_data_limit;
-               int decomp_sg_limit;
-       } *maxsynccop;
-
-       if (prop->length != sizeof(*maxsynccop)) {
-               dev_err(devdata->dev, "%s: unexpected format for ibm,max-sync-cop property\n", __func__);
-               dev_dbg(devdata->dev, "%s: ibm,max-sync-cop is %d bytes long, expected %lu bytes\n", __func__, prop->length,
-                               sizeof(*maxsynccop));
-               ret = -EINVAL;
-               goto out;
-       }
-
-       maxsynccop = (const struct maxsynccop_t *)prop->value;
-
-       /* Use one limit rather than separate limits for compression and
-        * decompression. Set a maximum for this so as not to exceed the
-        * size that the header can support and round the value down to
-        * the hardware page size (4K) */
-       devdata->max_sync_size =
-                       (unsigned int)min(maxsynccop->comp_data_limit,
-                                       maxsynccop->decomp_data_limit);
-
-       devdata->max_sync_size = min_t(unsigned int, devdata->max_sync_size,
-                                       SIZE_64K);
-
-       if (devdata->max_sync_size < SIZE_4K) {
-               dev_err(devdata->dev, "%s: hardware max data size (%u) is "
-                               "less than the driver minimum, unable to use "
-                               "the hardware device\n",
-                               __func__, devdata->max_sync_size);
-               ret = -EINVAL;
-               goto out;
-       }
-
-       devdata->max_sync_sg = (unsigned int)min(maxsynccop->comp_sg_limit,
-                                               maxsynccop->decomp_sg_limit);
-       if (devdata->max_sync_sg < 1) {
-               dev_err(devdata->dev, "%s: hardware max sg size (%u) is "
-                               "less than the driver minimum, unable to use "
-                               "the hardware device\n",
-                               __func__, devdata->max_sync_sg);
-               ret = -EINVAL;
-               goto out;
-       }
-
-out:
-       return ret;
+       return nx842_platform_driver()->decompress(in, ilen, out, olen, wmem);
 }
+EXPORT_SYMBOL_GPL(nx842_decompress);
 
-/**
- *
- * nx842_OF_upd -- Handle OF properties updates for the device.
- *
- * Set all properties from the OF tree.  Optionally, a new property
- * can be provided by the @new_prop pointer to overwrite an existing value.
- * The device will remain disabled until all values are valid, this function
- * will return an error for updates unless all values are valid.
- *
- * @new_prop: If not NULL, this property is being updated.  If NULL, update
- *  all properties from the current values in the OF tree.
- *
- * Returns:
- *  0 - Success
- *  -ENOMEM - Could not allocate memory for new devdata structure
- *  -EINVAL - property value not found, new_prop is not a recognized
- *     property for the device or property value is not valid.
- *  -ENODEV - Device is not available
- */
-static int nx842_OF_upd(struct property *new_prop)
+static __init int nx842_init(void)
 {
-       struct nx842_devdata *old_devdata = NULL;
-       struct nx842_devdata *new_devdata = NULL;
-       struct device_node *of_node = NULL;
-       struct property *status = NULL;
-       struct property *maxsglen = NULL;
-       struct property *maxsyncop = NULL;
-       int ret = 0;
-       unsigned long flags;
-
-       spin_lock_irqsave(&devdata_mutex, flags);
-       old_devdata = rcu_dereference_check(devdata,
-                       lockdep_is_held(&devdata_mutex));
-       if (old_devdata)
-               of_node = old_devdata->dev->of_node;
+       request_module("nx-compress-powernv");
+       request_module("nx-compress-pseries");
 
-       if (!old_devdata || !of_node) {
-               pr_err("%s: device is not available\n", __func__);
-               spin_unlock_irqrestore(&devdata_mutex, flags);
-               return -ENODEV;
-       }
-
-       new_devdata = kzalloc(sizeof(*new_devdata), GFP_NOFS);
-       if (!new_devdata) {
-               dev_err(old_devdata->dev, "%s: Could not allocate memory for device data\n", __func__);
-               ret = -ENOMEM;
-               goto error_out;
-       }
-
-       memcpy(new_devdata, old_devdata, sizeof(*old_devdata));
-       new_devdata->counters = old_devdata->counters;
-
-       /* Set ptrs for existing properties */
-       status = of_find_property(of_node, "status", NULL);
-       maxsglen = of_find_property(of_node, "ibm,max-sg-len", NULL);
-       maxsyncop = of_find_property(of_node, "ibm,max-sync-cop", NULL);
-       if (!status || !maxsglen || !maxsyncop) {
-               dev_err(old_devdata->dev, "%s: Could not locate device properties\n", __func__);
-               ret = -EINVAL;
-               goto error_out;
-       }
-
-       /*
-        * If this is a property update, there are only certain properties that
-        * we care about. Bail if it isn't in the below list
+       /* we prevent loading if there's no platform driver, and we get the
+        * module that set it so it won't unload, so we don't need to check
+        * if it's set in any of the above functions
         */
-       if (new_prop && (strncmp(new_prop->name, "status", new_prop->length) ||
-                        strncmp(new_prop->name, "ibm,max-sg-len", new_prop->length) ||
-                        strncmp(new_prop->name, "ibm,max-sync-cop", new_prop->length)))
-               goto out;
-
-       /* Perform property updates */
-       ret = nx842_OF_upd_status(new_devdata, status);
-       if (ret)
-               goto error_out;
-
-       ret = nx842_OF_upd_maxsglen(new_devdata, maxsglen);
-       if (ret)
-               goto error_out;
-
-       ret = nx842_OF_upd_maxsyncop(new_devdata, maxsyncop);
-       if (ret)
-               goto error_out;
-
-out:
-       dev_info(old_devdata->dev, "%s: max_sync_size new:%u old:%u\n",
-                       __func__, new_devdata->max_sync_size,
-                       old_devdata->max_sync_size);
-       dev_info(old_devdata->dev, "%s: max_sync_sg new:%u old:%u\n",
-                       __func__, new_devdata->max_sync_sg,
-                       old_devdata->max_sync_sg);
-       dev_info(old_devdata->dev, "%s: max_sg_len new:%u old:%u\n",
-                       __func__, new_devdata->max_sg_len,
-                       old_devdata->max_sg_len);
-
-       rcu_assign_pointer(devdata, new_devdata);
-       spin_unlock_irqrestore(&devdata_mutex, flags);
-       synchronize_rcu();
-       dev_set_drvdata(new_devdata->dev, new_devdata);
-       kfree(old_devdata);
-       return 0;
-
-error_out:
-       if (new_devdata) {
-               dev_info(old_devdata->dev, "%s: device disabled\n", __func__);
-               nx842_OF_set_defaults(new_devdata);
-               rcu_assign_pointer(devdata, new_devdata);
-               spin_unlock_irqrestore(&devdata_mutex, flags);
-               synchronize_rcu();
-               dev_set_drvdata(new_devdata->dev, new_devdata);
-               kfree(old_devdata);
-       } else {
-               dev_err(old_devdata->dev, "%s: could not update driver from hardware\n", __func__);
-               spin_unlock_irqrestore(&devdata_mutex, flags);
-       }
-
-       if (!ret)
-               ret = -EINVAL;
-       return ret;
-}
-
-/**
- * nx842_OF_notifier - Process updates to OF properties for the device
- *
- * @np: notifier block
- * @action: notifier action
- * @update: struct pSeries_reconfig_prop_update pointer if action is
- *     PSERIES_UPDATE_PROPERTY
- *
- * Returns:
- *     NOTIFY_OK on success
- *     NOTIFY_BAD encoded with error number on failure, use
- *             notifier_to_errno() to decode this value
- */
-static int nx842_OF_notifier(struct notifier_block *np, unsigned long action,
-                            void *data)
-{
-       struct of_reconfig_data *upd = data;
-       struct nx842_devdata *local_devdata;
-       struct device_node *node = NULL;
-
-       rcu_read_lock();
-       local_devdata = rcu_dereference(devdata);
-       if (local_devdata)
-               node = local_devdata->dev->of_node;
-
-       if (local_devdata &&
-                       action == OF_RECONFIG_UPDATE_PROPERTY &&
-                       !strcmp(upd->dn->name, node->name)) {
-               rcu_read_unlock();
-               nx842_OF_upd(upd->prop);
-       } else
-               rcu_read_unlock();
-
-       return NOTIFY_OK;
-}
-
-static struct notifier_block nx842_of_nb = {
-       .notifier_call = nx842_OF_notifier,
-};
-
-#define nx842_counter_read(_name)                                      \
-static ssize_t nx842_##_name##_show(struct device *dev,                \
-               struct device_attribute *attr,                          \
-               char *buf) {                                            \
-       struct nx842_devdata *local_devdata;                    \
-       int p = 0;                                                      \
-       rcu_read_lock();                                                \
-       local_devdata = rcu_dereference(devdata);                       \
-       if (local_devdata)                                              \
-               p = snprintf(buf, PAGE_SIZE, "%ld\n",                   \
-                      atomic64_read(&local_devdata->counters->_name)); \
-       rcu_read_unlock();                                              \
-       return p;                                                       \
-}
-
-#define NX842DEV_COUNTER_ATTR_RO(_name)                                        \
-       nx842_counter_read(_name);                                      \
-       static struct device_attribute dev_attr_##_name = __ATTR(_name, \
-                                               0444,                   \
-                                               nx842_##_name##_show,\
-                                               NULL);
-
-NX842DEV_COUNTER_ATTR_RO(comp_complete);
-NX842DEV_COUNTER_ATTR_RO(comp_failed);
-NX842DEV_COUNTER_ATTR_RO(decomp_complete);
-NX842DEV_COUNTER_ATTR_RO(decomp_failed);
-NX842DEV_COUNTER_ATTR_RO(swdecomp);
-
-static ssize_t nx842_timehist_show(struct device *,
-               struct device_attribute *, char *);
-
-static struct device_attribute dev_attr_comp_times = __ATTR(comp_times, 0444,
-               nx842_timehist_show, NULL);
-static struct device_attribute dev_attr_decomp_times = __ATTR(decomp_times,
-               0444, nx842_timehist_show, NULL);
-
-static ssize_t nx842_timehist_show(struct device *dev,
-               struct device_attribute *attr, char *buf) {
-       char *p = buf;
-       struct nx842_devdata *local_devdata;
-       atomic64_t *times;
-       int bytes_remain = PAGE_SIZE;
-       int bytes;
-       int i;
-
-       rcu_read_lock();
-       local_devdata = rcu_dereference(devdata);
-       if (!local_devdata) {
-               rcu_read_unlock();
-               return 0;
-       }
-
-       if (attr == &dev_attr_comp_times)
-               times = local_devdata->counters->comp_times;
-       else if (attr == &dev_attr_decomp_times)
-               times = local_devdata->counters->decomp_times;
-       else {
-               rcu_read_unlock();
-               return 0;
-       }
-
-       for (i = 0; i < (NX842_HIST_SLOTS - 2); i++) {
-               bytes = snprintf(p, bytes_remain, "%u-%uus:\t%ld\n",
-                              i ? (2<<(i-1)) : 0, (2<<i)-1,
-                              atomic64_read(&times[i]));
-               bytes_remain -= bytes;
-               p += bytes;
-       }
-       /* The last bucket holds everything over
-        * 2<<(NX842_HIST_SLOTS - 2) us */
-       bytes = snprintf(p, bytes_remain, "%uus - :\t%ld\n",
-                       2<<(NX842_HIST_SLOTS - 2),
-                       atomic64_read(&times[(NX842_HIST_SLOTS - 1)]));
-       p += bytes;
-
-       rcu_read_unlock();
-       return p - buf;
-}
-
-static struct attribute *nx842_sysfs_entries[] = {
-       &dev_attr_comp_complete.attr,
-       &dev_attr_comp_failed.attr,
-       &dev_attr_decomp_complete.attr,
-       &dev_attr_decomp_failed.attr,
-       &dev_attr_swdecomp.attr,
-       &dev_attr_comp_times.attr,
-       &dev_attr_decomp_times.attr,
-       NULL,
-};
-
-static struct attribute_group nx842_attribute_group = {
-       .name = NULL,           /* put in device directory */
-       .attrs = nx842_sysfs_entries,
-};
-
-static int __init nx842_probe(struct vio_dev *viodev,
-                                 const struct vio_device_id *id)
-{
-       struct nx842_devdata *old_devdata, *new_devdata = NULL;
-       unsigned long flags;
-       int ret = 0;
-
-       spin_lock_irqsave(&devdata_mutex, flags);
-       old_devdata = rcu_dereference_check(devdata,
-                       lockdep_is_held(&devdata_mutex));
-
-       if (old_devdata && old_devdata->vdev != NULL) {
-               dev_err(&viodev->dev, "%s: Attempt to register more than one instance of the hardware\n", __func__);
-               ret = -1;
-               goto error_unlock;
-       }
-
-       dev_set_drvdata(&viodev->dev, NULL);
-
-       new_devdata = kzalloc(sizeof(*new_devdata), GFP_NOFS);
-       if (!new_devdata) {
-               dev_err(&viodev->dev, "%s: Could not allocate memory for device data\n", __func__);
-               ret = -ENOMEM;
-               goto error_unlock;
-       }
-
-       new_devdata->counters = kzalloc(sizeof(*new_devdata->counters),
-                       GFP_NOFS);
-       if (!new_devdata->counters) {
-               dev_err(&viodev->dev, "%s: Could not allocate memory for performance counters\n", __func__);
-               ret = -ENOMEM;
-               goto error_unlock;
-       }
-
-       new_devdata->vdev = viodev;
-       new_devdata->dev = &viodev->dev;
-       nx842_OF_set_defaults(new_devdata);
-
-       rcu_assign_pointer(devdata, new_devdata);
-       spin_unlock_irqrestore(&devdata_mutex, flags);
-       synchronize_rcu();
-       kfree(old_devdata);
-
-       of_reconfig_notifier_register(&nx842_of_nb);
-
-       ret = nx842_OF_upd(NULL);
-       if (ret && ret != -ENODEV) {
-               dev_err(&viodev->dev, "could not parse device tree. %d\n", ret);
-               ret = -1;
-               goto error;
-       }
-
-       rcu_read_lock();
-       dev_set_drvdata(&viodev->dev, rcu_dereference(devdata));
-       rcu_read_unlock();
-
-       if (sysfs_create_group(&viodev->dev.kobj, &nx842_attribute_group)) {
-               dev_err(&viodev->dev, "could not create sysfs device attributes\n");
-               ret = -1;
-               goto error;
+       if (!nx842_platform_driver_get()) {
+               pr_err("no nx842 driver found.\n");
+               return -ENODEV;
        }
 
        return 0;
-
-error_unlock:
-       spin_unlock_irqrestore(&devdata_mutex, flags);
-       if (new_devdata)
-               kfree(new_devdata->counters);
-       kfree(new_devdata);
-error:
-       return ret;
-}
-
-static int __exit nx842_remove(struct vio_dev *viodev)
-{
-       struct nx842_devdata *old_devdata;
-       unsigned long flags;
-
-       pr_info("Removing IBM Power 842 compression device\n");
-       sysfs_remove_group(&viodev->dev.kobj, &nx842_attribute_group);
-
-       spin_lock_irqsave(&devdata_mutex, flags);
-       old_devdata = rcu_dereference_check(devdata,
-                       lockdep_is_held(&devdata_mutex));
-       of_reconfig_notifier_unregister(&nx842_of_nb);
-       RCU_INIT_POINTER(devdata, NULL);
-       spin_unlock_irqrestore(&devdata_mutex, flags);
-       synchronize_rcu();
-       dev_set_drvdata(&viodev->dev, NULL);
-       if (old_devdata)
-               kfree(old_devdata->counters);
-       kfree(old_devdata);
-       return 0;
-}
-
-static struct vio_device_id nx842_driver_ids[] = {
-       {"ibm,compression-v1", "ibm,compression"},
-       {"", ""},
-};
-
-static struct vio_driver nx842_driver = {
-       .name = MODULE_NAME,
-       .probe = nx842_probe,
-       .remove = __exit_p(nx842_remove),
-       .get_desired_dma = nx842_get_desired_dma,
-       .id_table = nx842_driver_ids,
-};
-
-static int __init nx842_init(void)
-{
-       struct nx842_devdata *new_devdata;
-       pr_info("Registering IBM Power 842 compression driver\n");
-
-       RCU_INIT_POINTER(devdata, NULL);
-       new_devdata = kzalloc(sizeof(*new_devdata), GFP_KERNEL);
-       if (!new_devdata) {
-               pr_err("Could not allocate memory for device data\n");
-               return -ENOMEM;
-       }
-       new_devdata->status = UNAVAILABLE;
-       RCU_INIT_POINTER(devdata, new_devdata);
-
-       return vio_register_driver(&nx842_driver);
 }
-
 module_init(nx842_init);
 
 static void __exit nx842_exit(void)
 {
-       struct nx842_devdata *old_devdata;
-       unsigned long flags;
-
-       pr_info("Exiting IBM Power 842 compression driver\n");
-       spin_lock_irqsave(&devdata_mutex, flags);
-       old_devdata = rcu_dereference_check(devdata,
-                       lockdep_is_held(&devdata_mutex));
-       RCU_INIT_POINTER(devdata, NULL);
-       spin_unlock_irqrestore(&devdata_mutex, flags);
-       synchronize_rcu();
-       if (old_devdata)
-               dev_set_drvdata(old_devdata->dev, NULL);
-       kfree(old_devdata);
-       vio_unregister_driver(&nx842_driver);
+       nx842_platform_driver_put();
 }
-
 module_exit(nx842_exit);
-
-/*********************************
- * 842 software decompressor
-*********************************/
-typedef int (*sw842_template_op)(const char **, int *, unsigned char **,
-                                               struct sw842_fifo *);
-
-static int sw842_data8(const char **, int *, unsigned char **,
-                                               struct sw842_fifo *);
-static int sw842_data4(const char **, int *, unsigned char **,
-                                               struct sw842_fifo *);
-static int sw842_data2(const char **, int *, unsigned char **,
-                                               struct sw842_fifo *);
-static int sw842_ptr8(const char **, int *, unsigned char **,
-                                               struct sw842_fifo *);
-static int sw842_ptr4(const char **, int *, unsigned char **,
-                                               struct sw842_fifo *);
-static int sw842_ptr2(const char **, int *, unsigned char **,
-                                               struct sw842_fifo *);
-
-/* special templates */
-#define SW842_TMPL_REPEAT 0x1B
-#define SW842_TMPL_ZEROS 0x1C
-#define SW842_TMPL_EOF 0x1E
-
-static sw842_template_op sw842_tmpl_ops[26][4] = {
-       { sw842_data8, NULL}, /* 0 (00000) */
-       { sw842_data4, sw842_data2, sw842_ptr2,  NULL},
-       { sw842_data4, sw842_ptr2,  sw842_data2, NULL},
-       { sw842_data4, sw842_ptr2,  sw842_ptr2,  NULL},
-       { sw842_data4, sw842_ptr4,  NULL},
-       { sw842_data2, sw842_ptr2,  sw842_data4, NULL},
-       { sw842_data2, sw842_ptr2,  sw842_data2, sw842_ptr2},
-       { sw842_data2, sw842_ptr2,  sw842_ptr2,  sw842_data2},
-       { sw842_data2, sw842_ptr2,  sw842_ptr2,  sw842_ptr2,},
-       { sw842_data2, sw842_ptr2,  sw842_ptr4,  NULL},
-       { sw842_ptr2,  sw842_data2, sw842_data4, NULL}, /* 10 (01010) */
-       { sw842_ptr2,  sw842_data4, sw842_ptr2,  NULL},
-       { sw842_ptr2,  sw842_data2, sw842_ptr2,  sw842_data2},
-       { sw842_ptr2,  sw842_data2, sw842_ptr2,  sw842_ptr2},
-       { sw842_ptr2,  sw842_data2, sw842_ptr4,  NULL},
-       { sw842_ptr2,  sw842_ptr2,  sw842_data4, NULL},
-       { sw842_ptr2,  sw842_ptr2,  sw842_data2, sw842_ptr2},
-       { sw842_ptr2,  sw842_ptr2,  sw842_ptr2,  sw842_data2},
-       { sw842_ptr2,  sw842_ptr2,  sw842_ptr2,  sw842_ptr2},
-       { sw842_ptr2,  sw842_ptr2,  sw842_ptr4,  NULL},
-       { sw842_ptr4,  sw842_data4, NULL}, /* 20 (10100) */
-       { sw842_ptr4,  sw842_data2, sw842_ptr2,  NULL},
-       { sw842_ptr4,  sw842_ptr2,  sw842_data2, NULL},
-       { sw842_ptr4,  sw842_ptr2,  sw842_ptr2,  NULL},
-       { sw842_ptr4,  sw842_ptr4,  NULL},
-       { sw842_ptr8,  NULL}
-};
-
-/* Software decompress helpers */
-
-static uint8_t sw842_get_byte(const char *buf, int bit)
-{
-       uint8_t tmpl;
-       uint16_t tmp;
-       tmp = htons(*(uint16_t *)(buf));
-       tmp = (uint16_t)(tmp << bit);
-       tmp = ntohs(tmp);
-       memcpy(&tmpl, &tmp, 1);
-       return tmpl;
-}
-
-static uint8_t sw842_get_template(const char **buf, int *bit)
-{
-       uint8_t byte;
-       byte = sw842_get_byte(*buf, *bit);
-       byte = byte >> 3;
-       byte &= 0x1F;
-       *buf += (*bit + 5) / 8;
-       *bit = (*bit + 5) % 8;
-       return byte;
-}
-
-/* repeat_count happens to be 5-bit too (like the template) */
-static uint8_t sw842_get_repeat_count(const char **buf, int *bit)
-{
-       uint8_t byte;
-       byte = sw842_get_byte(*buf, *bit);
-       byte = byte >> 2;
-       byte &= 0x3F;
-       *buf += (*bit + 6) / 8;
-       *bit = (*bit + 6) % 8;
-       return byte;
-}
-
-static uint8_t sw842_get_ptr2(const char **buf, int *bit)
-{
-       uint8_t ptr;
-       ptr = sw842_get_byte(*buf, *bit);
-       (*buf)++;
-       return ptr;
-}
-
-static uint16_t sw842_get_ptr4(const char **buf, int *bit,
-               struct sw842_fifo *fifo)
-{
-       uint16_t ptr;
-       ptr = htons(*(uint16_t *)(*buf));
-       ptr = (uint16_t)(ptr << *bit);
-       ptr = ptr >> 7;
-       ptr &= 0x01FF;
-       *buf += (*bit + 9) / 8;
-       *bit = (*bit + 9) % 8;
-       return ptr;
-}
-
-static uint8_t sw842_get_ptr8(const char **buf, int *bit,
-               struct sw842_fifo *fifo)
-{
-       return sw842_get_ptr2(buf, bit);
-}
-
-/* Software decompress template ops */
-
-static int sw842_data8(const char **inbuf, int *inbit,
-               unsigned char **outbuf, struct sw842_fifo *fifo)
-{
-       int ret;
-
-       ret = sw842_data4(inbuf, inbit, outbuf, fifo);
-       if (ret)
-               return ret;
-       ret = sw842_data4(inbuf, inbit, outbuf, fifo);
-       return ret;
-}
-
-static int sw842_data4(const char **inbuf, int *inbit,
-               unsigned char **outbuf, struct sw842_fifo *fifo)
-{
-       int ret;
-
-       ret = sw842_data2(inbuf, inbit, outbuf, fifo);
-       if (ret)
-               return ret;
-       ret = sw842_data2(inbuf, inbit, outbuf, fifo);
-       return ret;
-}
-
-static int sw842_data2(const char **inbuf, int *inbit,
-               unsigned char **outbuf, struct sw842_fifo *fifo)
-{
-       **outbuf = sw842_get_byte(*inbuf, *inbit);
-       (*inbuf)++;
-       (*outbuf)++;
-       **outbuf = sw842_get_byte(*inbuf, *inbit);
-       (*inbuf)++;
-       (*outbuf)++;
-       return 0;
-}
-
-static int sw842_ptr8(const char **inbuf, int *inbit,
-               unsigned char **outbuf, struct sw842_fifo *fifo)
-{
-       uint8_t ptr;
-       ptr = sw842_get_ptr8(inbuf, inbit, fifo);
-       if (!fifo->f84_full && (ptr >= fifo->f8_count))
-               return 1;
-       memcpy(*outbuf, fifo->f8[ptr], 8);
-       *outbuf += 8;
-       return 0;
-}
-
-static int sw842_ptr4(const char **inbuf, int *inbit,
-               unsigned char **outbuf, struct sw842_fifo *fifo)
-{
-       uint16_t ptr;
-       ptr = sw842_get_ptr4(inbuf, inbit, fifo);
-       if (!fifo->f84_full && (ptr >= fifo->f4_count))
-               return 1;
-       memcpy(*outbuf, fifo->f4[ptr], 4);
-       *outbuf += 4;
-       return 0;
-}
-
-static int sw842_ptr2(const char **inbuf, int *inbit,
-               unsigned char **outbuf, struct sw842_fifo *fifo)
-{
-       uint8_t ptr;
-       ptr = sw842_get_ptr2(inbuf, inbit);
-       if (!fifo->f2_full && (ptr >= fifo->f2_count))
-               return 1;
-       memcpy(*outbuf, fifo->f2[ptr], 2);
-       *outbuf += 2;
-       return 0;
-}
-
-static void sw842_copy_to_fifo(const char *buf, struct sw842_fifo *fifo)
-{
-       unsigned char initial_f2count = fifo->f2_count;
-
-       memcpy(fifo->f8[fifo->f8_count], buf, 8);
-       fifo->f4_count += 2;
-       fifo->f8_count += 1;
-
-       if (!fifo->f84_full && fifo->f4_count >= 512) {
-               fifo->f84_full = 1;
-               fifo->f4_count /= 512;
-       }
-
-       memcpy(fifo->f2[fifo->f2_count++], buf, 2);
-       memcpy(fifo->f2[fifo->f2_count++], buf + 2, 2);
-       memcpy(fifo->f2[fifo->f2_count++], buf + 4, 2);
-       memcpy(fifo->f2[fifo->f2_count++], buf + 6, 2);
-       if (fifo->f2_count < initial_f2count)
-               fifo->f2_full = 1;
-}
-
-static int sw842_decompress(const unsigned char *src, int srclen,
-                       unsigned char *dst, int *destlen,
-                       const void *wrkmem)
-{
-       uint8_t tmpl;
-       const char *inbuf;
-       int inbit = 0;
-       unsigned char *outbuf, *outbuf_end, *origbuf, *prevbuf;
-       const char *inbuf_end;
-       sw842_template_op op;
-       int opindex;
-       int i, repeat_count;
-       struct sw842_fifo *fifo;
-       int ret = 0;
-
-       fifo = &((struct nx842_workmem *)(wrkmem))->swfifo;
-       memset(fifo, 0, sizeof(*fifo));
-
-       origbuf = NULL;
-       inbuf = src;
-       inbuf_end = src + srclen;
-       outbuf = dst;
-       outbuf_end = dst + *destlen;
-
-       while ((tmpl = sw842_get_template(&inbuf, &inbit)) != SW842_TMPL_EOF) {
-               if (inbuf >= inbuf_end) {
-                       ret = -EINVAL;
-                       goto out;
-               }
-
-               opindex = 0;
-               prevbuf = origbuf;
-               origbuf = outbuf;
-               switch (tmpl) {
-               case SW842_TMPL_REPEAT:
-                       if (prevbuf == NULL) {
-                               ret = -EINVAL;
-                               goto out;
-                       }
-
-                       repeat_count = sw842_get_repeat_count(&inbuf,
-                                                               &inbit) + 1;
-
-                       /* Did the repeat count advance past the end of input */
-                       if (inbuf > inbuf_end) {
-                               ret = -EINVAL;
-                               goto out;
-                       }
-
-                       for (i = 0; i < repeat_count; i++) {
-                               /* Would this overflow the output buffer */
-                               if ((outbuf + 8) > outbuf_end) {
-                                       ret = -ENOSPC;
-                                       goto out;
-                               }
-
-                               memcpy(outbuf, prevbuf, 8);
-                               sw842_copy_to_fifo(outbuf, fifo);
-                               outbuf += 8;
-                       }
-                       break;
-
-               case SW842_TMPL_ZEROS:
-                       /* Would this overflow the output buffer */
-                       if ((outbuf + 8) > outbuf_end) {
-                               ret = -ENOSPC;
-                               goto out;
-                       }
-
-                       memset(outbuf, 0, 8);
-                       sw842_copy_to_fifo(outbuf, fifo);
-                       outbuf += 8;
-                       break;
-
-               default:
-                       if (tmpl > 25) {
-                               ret = -EINVAL;
-                               goto out;
-                       }
-
-                       /* Does this go past the end of the input buffer */
-                       if ((inbuf + 2) > inbuf_end) {
-                               ret = -EINVAL;
-                               goto out;
-                       }
-
-                       /* Would this overflow the output buffer */
-                       if ((outbuf + 8) > outbuf_end) {
-                               ret = -ENOSPC;
-                               goto out;
-                       }
-
-                       while (opindex < 4 &&
-                               (op = sw842_tmpl_ops[tmpl][opindex++])
-                                       != NULL) {
-                               ret = (*op)(&inbuf, &inbit, &outbuf, fifo);
-                               if (ret) {
-                                       ret = -EINVAL;
-                                       goto out;
-                               }
-                               sw842_copy_to_fifo(origbuf, fifo);
-                       }
-               }
-       }
-
-out:
-       if (!ret)
-               *destlen = (unsigned int)(outbuf - dst);
-       else
-               *destlen = 0;
-
-       return ret;
-}
diff --git a/drivers/crypto/nx/nx-842.h b/drivers/crypto/nx/nx-842.h
new file mode 100644 (file)
index 0000000..ac0ea79
--- /dev/null
@@ -0,0 +1,144 @@
+
+#ifndef __NX_842_H__
+#define __NX_842_H__
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sw842.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/mm.h>
+#include <linux/ratelimit.h>
+
+/* Restrictions on Data Descriptor List (DDL) and Entry (DDE) buffers
+ *
+ * From NX P8 workbook, sec 4.9.1 "842 details"
+ *   Each DDE buffer is 128 byte aligned
+ *   Each DDE buffer size is a multiple of 32 bytes (except the last)
+ *   The last DDE buffer size is a multiple of 8 bytes
+ */
+#define DDE_BUFFER_ALIGN       (128)
+#define DDE_BUFFER_SIZE_MULT   (32)
+#define DDE_BUFFER_LAST_MULT   (8)
+
+/* Arbitrary DDL length limit
+ * Allows max buffer size of MAX-1 to MAX pages
+ * (depending on alignment)
+ */
+#define DDL_LEN_MAX            (17)
+
+/* CCW 842 CI/FC masks
+ * NX P8 workbook, section 4.3.1, figure 4-6
+ * "CI/FC Boundary by NX CT type"
+ */
+#define CCW_CI_842             (0x00003ff8)
+#define CCW_FC_842             (0x00000007)
+
+/* CCW Function Codes (FC) for 842
+ * NX P8 workbook, section 4.9, table 4-28
+ * "Function Code Definitions for 842 Memory Compression"
+ */
+#define CCW_FC_842_COMP_NOCRC  (0)
+#define CCW_FC_842_COMP_CRC    (1)
+#define CCW_FC_842_DECOMP_NOCRC        (2)
+#define CCW_FC_842_DECOMP_CRC  (3)
+#define CCW_FC_842_MOVE                (4)
+
+/* CSB CC Error Types for 842
+ * NX P8 workbook, section 4.10.3, table 4-30
+ * "Reported Error Types Summary Table"
+ */
+/* These are all duplicates of existing codes defined in icswx.h. */
+#define CSB_CC_TRANSLATION_DUP1        (80)
+#define CSB_CC_TRANSLATION_DUP2        (82)
+#define CSB_CC_TRANSLATION_DUP3        (84)
+#define CSB_CC_TRANSLATION_DUP4        (86)
+#define CSB_CC_TRANSLATION_DUP5        (92)
+#define CSB_CC_TRANSLATION_DUP6        (94)
+#define CSB_CC_PROTECTION_DUP1 (81)
+#define CSB_CC_PROTECTION_DUP2 (83)
+#define CSB_CC_PROTECTION_DUP3 (85)
+#define CSB_CC_PROTECTION_DUP4 (87)
+#define CSB_CC_PROTECTION_DUP5 (93)
+#define CSB_CC_PROTECTION_DUP6 (95)
+#define CSB_CC_RD_EXTERNAL_DUP1        (89)
+#define CSB_CC_RD_EXTERNAL_DUP2        (90)
+#define CSB_CC_RD_EXTERNAL_DUP3        (91)
+/* These are specific to NX */
+/* 842 codes */
+#define CSB_CC_TPBC_GT_SPBC    (64) /* no error, but >1 comp ratio */
+#define CSB_CC_CRC_MISMATCH    (65) /* decomp crc mismatch */
+#define CSB_CC_TEMPL_INVALID   (66) /* decomp invalid template value */
+#define CSB_CC_TEMPL_OVERFLOW  (67) /* decomp template shows data after end */
+/* sym crypt codes */
+#define CSB_CC_DECRYPT_OVERFLOW        (64)
+/* asym crypt codes */
+#define CSB_CC_MINV_OVERFLOW   (128)
+/* These are reserved for hypervisor use */
+#define CSB_CC_HYP_RESERVE_START       (240)
+#define CSB_CC_HYP_RESERVE_END         (253)
+#define CSB_CC_HYP_NO_HW               (254)
+#define CSB_CC_HYP_HANG_ABORTED                (255)
+
+/* CCB Completion Modes (CM) for 842
+ * NX P8 workbook, section 4.3, figure 4-5
+ * "CRB Details - Normal Cop_Req (CL=00, C=1)"
+ */
+#define CCB_CM_EXTRA_WRITE     (CCB_CM0_ALL_COMPLETIONS & CCB_CM12_STORE)
+#define CCB_CM_INTERRUPT       (CCB_CM0_ALL_COMPLETIONS & CCB_CM12_INTERRUPT)
+
+#define LEN_ON_SIZE(pa, size)  ((size) - ((pa) & ((size) - 1)))
+#define LEN_ON_PAGE(pa)                LEN_ON_SIZE(pa, PAGE_SIZE)
+
+static inline unsigned long nx842_get_pa(void *addr)
+{
+       if (!is_vmalloc_addr(addr))
+               return __pa(addr);
+
+       return page_to_phys(vmalloc_to_page(addr)) + offset_in_page(addr);
+}
+
+/* Get/Set bit fields */
+#define MASK_LSH(m)            (__builtin_ffsl(m) - 1)
+#define GET_FIELD(v, m)                (((v) & (m)) >> MASK_LSH(m))
+#define SET_FIELD(v, m, val)   (((v) & ~(m)) | (((val) << MASK_LSH(m)) & (m)))
+
+struct nx842_constraints {
+       int alignment;
+       int multiple;
+       int minimum;
+       int maximum;
+};
+
+struct nx842_driver {
+       char *name;
+       struct module *owner;
+       size_t workmem_size;
+
+       struct nx842_constraints *constraints;
+
+       int (*compress)(const unsigned char *in, unsigned int in_len,
+                       unsigned char *out, unsigned int *out_len,
+                       void *wrkmem);
+       int (*decompress)(const unsigned char *in, unsigned int in_len,
+                         unsigned char *out, unsigned int *out_len,
+                         void *wrkmem);
+};
+
+struct nx842_driver *nx842_platform_driver(void);
+bool nx842_platform_driver_set(struct nx842_driver *driver);
+void nx842_platform_driver_unset(struct nx842_driver *driver);
+bool nx842_platform_driver_get(void);
+void nx842_platform_driver_put(void);
+
+size_t nx842_workmem_size(void);
+
+int nx842_constraints(struct nx842_constraints *constraints);
+
+int nx842_compress(const unsigned char *in, unsigned int in_len,
+                  unsigned char *out, unsigned int *out_len, void *wrkmem);
+int nx842_decompress(const unsigned char *in, unsigned int in_len,
+                    unsigned char *out, unsigned int *out_len, void *wrkmem);
+
+#endif /* __NX_842_H__ */
index 88c562434bc0b737b2a6d99856e6dda284424634..08ac6d48688c7cf80589965b34724f374ec41b61 100644 (file)
@@ -93,17 +93,6 @@ out:
        return rc;
 }
 
-static int gcm_aes_nx_setauthsize(struct crypto_aead *tfm,
-                                 unsigned int authsize)
-{
-       if (authsize > crypto_aead_alg(tfm)->maxauthsize)
-               return -EINVAL;
-
-       crypto_aead_crt(tfm)->authsize = authsize;
-
-       return 0;
-}
-
 static int gcm4106_aes_nx_setauthsize(struct crypto_aead *tfm,
                                      unsigned int authsize)
 {
@@ -116,8 +105,6 @@ static int gcm4106_aes_nx_setauthsize(struct crypto_aead *tfm,
                return -EINVAL;
        }
 
-       crypto_aead_crt(tfm)->authsize = authsize;
-
        return 0;
 }
 
@@ -134,7 +121,7 @@ static int nx_gca(struct nx_crypto_ctx  *nx_ctx,
        unsigned int max_sg_len;
 
        if (nbytes <= AES_BLOCK_SIZE) {
-               scatterwalk_start(&walk, req->assoc);
+               scatterwalk_start(&walk, req->src);
                scatterwalk_copychunks(out, &walk, nbytes, SCATTERWALK_FROM_SG);
                scatterwalk_done(&walk, SCATTERWALK_FROM_SG, 0);
                return 0;
@@ -159,7 +146,7 @@ static int nx_gca(struct nx_crypto_ctx  *nx_ctx,
                                   NX_PAGE_SIZE * (max_sg_len - 1));
 
                nx_sg = nx_walk_and_build(nx_ctx->in_sg, max_sg_len,
-                                         req->assoc, processed, &to_process);
+                                         req->src, processed, &to_process);
 
                if ((to_process + processed) < nbytes)
                        NX_CPB_FDM(csbcpb_aead) |= NX_FDM_INTERMEDIATE;
@@ -225,7 +212,7 @@ static int gmac(struct aead_request *req, struct blkcipher_desc *desc)
                                   NX_PAGE_SIZE * (max_sg_len - 1));
 
                nx_sg = nx_walk_and_build(nx_ctx->in_sg, max_sg_len,
-                                         req->assoc, processed, &to_process);
+                                         req->src, processed, &to_process);
 
                if ((to_process + processed) < nbytes)
                        NX_CPB_FDM(csbcpb) |= NX_FDM_INTERMEDIATE;
@@ -377,7 +364,8 @@ static int gcm_aes_nx_crypt(struct aead_request *req, int enc)
                csbcpb->cpb.aes_gcm.bit_length_data = nbytes * 8;
                desc.tfm = (struct crypto_blkcipher *) req->base.tfm;
                rc = nx_build_sg_lists(nx_ctx, &desc, req->dst,
-                                      req->src, &to_process, processed,
+                                      req->src, &to_process,
+                                      processed + req->assoclen,
                                       csbcpb->cpb.aes_gcm.iv_or_cnt);
 
                if (rc)
@@ -412,17 +400,19 @@ static int gcm_aes_nx_crypt(struct aead_request *req, int enc)
 mac:
        if (enc) {
                /* copy out the auth tag */
-               scatterwalk_map_and_copy(csbcpb->cpb.aes_gcm.out_pat_or_mac,
-                                req->dst, nbytes,
-                                crypto_aead_authsize(crypto_aead_reqtfm(req)),
-                                SCATTERWALK_TO_SG);
+               scatterwalk_map_and_copy(
+                       csbcpb->cpb.aes_gcm.out_pat_or_mac,
+                       req->dst, req->assoclen + nbytes,
+                       crypto_aead_authsize(crypto_aead_reqtfm(req)),
+                       SCATTERWALK_TO_SG);
        } else {
                u8 *itag = nx_ctx->priv.gcm.iauth_tag;
                u8 *otag = csbcpb->cpb.aes_gcm.out_pat_or_mac;
 
-               scatterwalk_map_and_copy(itag, req->src, nbytes,
-                                crypto_aead_authsize(crypto_aead_reqtfm(req)),
-                                SCATTERWALK_FROM_SG);
+               scatterwalk_map_and_copy(
+                       itag, req->src, req->assoclen + nbytes,
+                       crypto_aead_authsize(crypto_aead_reqtfm(req)),
+                       SCATTERWALK_FROM_SG);
                rc = memcmp(itag, otag,
                            crypto_aead_authsize(crypto_aead_reqtfm(req))) ?
                     -EBADMSG : 0;
@@ -481,45 +471,39 @@ static int gcm4106_aes_nx_decrypt(struct aead_request *req)
  * during encrypt/decrypt doesn't solve this problem, because it calls
  * blkcipher_walk_done under the covers, which doesn't use walk->blocksize,
  * but instead uses this tfm->blocksize. */
-struct crypto_alg nx_gcm_aes_alg = {
-       .cra_name        = "gcm(aes)",
-       .cra_driver_name = "gcm-aes-nx",
-       .cra_priority    = 300,
-       .cra_flags       = CRYPTO_ALG_TYPE_AEAD,
-       .cra_blocksize   = 1,
-       .cra_ctxsize     = sizeof(struct nx_crypto_ctx),
-       .cra_type        = &crypto_aead_type,
-       .cra_module      = THIS_MODULE,
-       .cra_init        = nx_crypto_ctx_aes_gcm_init,
-       .cra_exit        = nx_crypto_ctx_exit,
-       .cra_aead = {
-               .ivsize      = AES_BLOCK_SIZE,
-               .maxauthsize = AES_BLOCK_SIZE,
-               .setkey      = gcm_aes_nx_set_key,
-               .setauthsize = gcm_aes_nx_setauthsize,
-               .encrypt     = gcm_aes_nx_encrypt,
-               .decrypt     = gcm_aes_nx_decrypt,
-       }
+struct aead_alg nx_gcm_aes_alg = {
+       .base = {
+               .cra_name        = "gcm(aes)",
+               .cra_driver_name = "gcm-aes-nx",
+               .cra_priority    = 300,
+               .cra_blocksize   = 1,
+               .cra_ctxsize     = sizeof(struct nx_crypto_ctx),
+               .cra_module      = THIS_MODULE,
+       },
+       .init        = nx_crypto_ctx_aes_gcm_init,
+       .exit        = nx_crypto_ctx_aead_exit,
+       .ivsize      = 12,
+       .maxauthsize = AES_BLOCK_SIZE,
+       .setkey      = gcm_aes_nx_set_key,
+       .encrypt     = gcm_aes_nx_encrypt,
+       .decrypt     = gcm_aes_nx_decrypt,
 };
 
-struct crypto_alg nx_gcm4106_aes_alg = {
-       .cra_name        = "rfc4106(gcm(aes))",
-       .cra_driver_name = "rfc4106-gcm-aes-nx",
-       .cra_priority    = 300,
-       .cra_flags       = CRYPTO_ALG_TYPE_AEAD,
-       .cra_blocksize   = 1,
-       .cra_ctxsize     = sizeof(struct nx_crypto_ctx),
-       .cra_type        = &crypto_nivaead_type,
-       .cra_module      = THIS_MODULE,
-       .cra_init        = nx_crypto_ctx_aes_gcm_init,
-       .cra_exit        = nx_crypto_ctx_exit,
-       .cra_aead = {
-               .ivsize      = 8,
-               .maxauthsize = AES_BLOCK_SIZE,
-               .geniv       = "seqiv",
-               .setkey      = gcm4106_aes_nx_set_key,
-               .setauthsize = gcm4106_aes_nx_setauthsize,
-               .encrypt     = gcm4106_aes_nx_encrypt,
-               .decrypt     = gcm4106_aes_nx_decrypt,
-       }
+struct aead_alg nx_gcm4106_aes_alg = {
+       .base = {
+               .cra_name        = "rfc4106(gcm(aes))",
+               .cra_driver_name = "rfc4106-gcm-aes-nx",
+               .cra_priority    = 300,
+               .cra_blocksize   = 1,
+               .cra_ctxsize     = sizeof(struct nx_crypto_ctx),
+               .cra_module      = THIS_MODULE,
+       },
+       .init        = nx_crypto_ctx_aes_gcm_init,
+       .exit        = nx_crypto_ctx_aead_exit,
+       .ivsize      = 8,
+       .maxauthsize = AES_BLOCK_SIZE,
+       .setkey      = gcm4106_aes_nx_set_key,
+       .setauthsize = gcm4106_aes_nx_setauthsize,
+       .encrypt     = gcm4106_aes_nx_encrypt,
+       .decrypt     = gcm4106_aes_nx_decrypt,
 };
index 23621da624c35b3192d31f1bd897b10d32fc2a20..4e91bdb83c594c3491bcb183cddd4021e8d8c596 100644 (file)
@@ -33,8 +33,9 @@ static int nx_sha256_init(struct shash_desc *desc)
 {
        struct sha256_state *sctx = shash_desc_ctx(desc);
        struct nx_crypto_ctx *nx_ctx = crypto_tfm_ctx(&desc->tfm->base);
+       struct nx_sg *out_sg;
        int len;
-       int rc;
+       u32 max_sg_len;
 
        nx_ctx_init(nx_ctx, HCOP_FC_SHA);
 
@@ -44,15 +45,18 @@ static int nx_sha256_init(struct shash_desc *desc)
 
        NX_CPB_SET_DIGEST_SIZE(nx_ctx->csbcpb, NX_DS_SHA256);
 
+       max_sg_len = min_t(u64, nx_ctx->ap->sglen,
+                       nx_driver.of.max_sg_len/sizeof(struct nx_sg));
+       max_sg_len = min_t(u64, max_sg_len,
+                       nx_ctx->ap->databytelen/NX_PAGE_SIZE);
+
        len = SHA256_DIGEST_SIZE;
-       rc = nx_sha_build_sg_list(nx_ctx, nx_ctx->out_sg,
-                                 &nx_ctx->op.outlen,
-                                 &len,
-                                 (u8 *) sctx->state,
-                                 NX_DS_SHA256);
+       out_sg = nx_build_sg_list(nx_ctx->out_sg, (u8 *)sctx->state,
+                                 &len, max_sg_len);
+       nx_ctx->op.outlen = (nx_ctx->out_sg - out_sg) * sizeof(struct nx_sg);
 
-       if (rc)
-               goto out;
+       if (len != SHA256_DIGEST_SIZE)
+               return -EINVAL;
 
        sctx->state[0] = __cpu_to_be32(SHA256_H0);
        sctx->state[1] = __cpu_to_be32(SHA256_H1);
@@ -64,7 +68,6 @@ static int nx_sha256_init(struct shash_desc *desc)
        sctx->state[7] = __cpu_to_be32(SHA256_H7);
        sctx->count = 0;
 
-out:
        return 0;
 }
 
@@ -74,10 +77,12 @@ static int nx_sha256_update(struct shash_desc *desc, const u8 *data,
        struct sha256_state *sctx = shash_desc_ctx(desc);
        struct nx_crypto_ctx *nx_ctx = crypto_tfm_ctx(&desc->tfm->base);
        struct nx_csbcpb *csbcpb = (struct nx_csbcpb *)nx_ctx->csbcpb;
+       struct nx_sg *in_sg;
        u64 to_process = 0, leftover, total;
        unsigned long irq_flags;
        int rc = 0;
        int data_len;
+       u32 max_sg_len;
        u64 buf_len = (sctx->count % SHA256_BLOCK_SIZE);
 
        spin_lock_irqsave(&nx_ctx->lock, irq_flags);
@@ -97,6 +102,12 @@ static int nx_sha256_update(struct shash_desc *desc, const u8 *data,
        NX_CPB_FDM(csbcpb) |= NX_FDM_INTERMEDIATE;
        NX_CPB_FDM(csbcpb) |= NX_FDM_CONTINUATION;
 
+       in_sg = nx_ctx->in_sg;
+       max_sg_len = min_t(u64, nx_ctx->ap->sglen,
+                       nx_driver.of.max_sg_len/sizeof(struct nx_sg));
+       max_sg_len = min_t(u64, max_sg_len,
+                       nx_ctx->ap->databytelen/NX_PAGE_SIZE);
+
        do {
                /*
                 * to_process: the SHA256_BLOCK_SIZE data chunk to process in
@@ -108,25 +119,22 @@ static int nx_sha256_update(struct shash_desc *desc, const u8 *data,
 
                if (buf_len) {
                        data_len = buf_len;
-                       rc = nx_sha_build_sg_list(nx_ctx, nx_ctx->in_sg,
-                                                 &nx_ctx->op.inlen,
-                                                 &data_len,
-                                                 (u8 *) sctx->buf,
-                                                 NX_DS_SHA256);
+                       in_sg = nx_build_sg_list(nx_ctx->in_sg,
+                                                (u8 *) sctx->buf,
+                                                &data_len,
+                                                max_sg_len);
 
-                       if (rc || data_len != buf_len)
+                       if (data_len != buf_len) {
+                               rc = -EINVAL;
                                goto out;
+                       }
                }
 
                data_len = to_process - buf_len;
-               rc = nx_sha_build_sg_list(nx_ctx, nx_ctx->in_sg,
-                                         &nx_ctx->op.inlen,
-                                         &data_len,
-                                         (u8 *) data,
-                                         NX_DS_SHA256);
+               in_sg = nx_build_sg_list(in_sg, (u8 *) data,
+                                        &data_len, max_sg_len);
 
-               if (rc)
-                       goto out;
+               nx_ctx->op.inlen = (nx_ctx->in_sg - in_sg) * sizeof(struct nx_sg);
 
                to_process = (data_len + buf_len);
                leftover = total - to_process;
@@ -173,12 +181,19 @@ static int nx_sha256_final(struct shash_desc *desc, u8 *out)
        struct sha256_state *sctx = shash_desc_ctx(desc);
        struct nx_crypto_ctx *nx_ctx = crypto_tfm_ctx(&desc->tfm->base);
        struct nx_csbcpb *csbcpb = (struct nx_csbcpb *)nx_ctx->csbcpb;
+       struct nx_sg *in_sg, *out_sg;
        unsigned long irq_flags;
-       int rc;
+       u32 max_sg_len;
+       int rc = 0;
        int len;
 
        spin_lock_irqsave(&nx_ctx->lock, irq_flags);
 
+       max_sg_len = min_t(u64, nx_ctx->ap->sglen,
+                       nx_driver.of.max_sg_len/sizeof(struct nx_sg));
+       max_sg_len = min_t(u64, max_sg_len,
+                       nx_ctx->ap->databytelen/NX_PAGE_SIZE);
+
        /* final is represented by continuing the operation and indicating that
         * this is not an intermediate operation */
        if (sctx->count >= SHA256_BLOCK_SIZE) {
@@ -195,25 +210,24 @@ static int nx_sha256_final(struct shash_desc *desc, u8 *out)
        csbcpb->cpb.sha256.message_bit_length = (u64) (sctx->count * 8);
 
        len = sctx->count & (SHA256_BLOCK_SIZE - 1);
-       rc = nx_sha_build_sg_list(nx_ctx, nx_ctx->in_sg,
-                                 &nx_ctx->op.inlen,
-                                 &len,
-                                 (u8 *) sctx->buf,
-                                 NX_DS_SHA256);
+       in_sg = nx_build_sg_list(nx_ctx->in_sg, (u8 *) sctx->buf,
+                                &len, max_sg_len);
 
-       if (rc || len != (sctx->count & (SHA256_BLOCK_SIZE - 1)))
+       if (len != (sctx->count & (SHA256_BLOCK_SIZE - 1))) {
+               rc = -EINVAL;
                goto out;
+       }
 
        len = SHA256_DIGEST_SIZE;
-       rc = nx_sha_build_sg_list(nx_ctx, nx_ctx->out_sg,
-                                 &nx_ctx->op.outlen,
-                                 &len,
-                                 out,
-                                 NX_DS_SHA256);
+       out_sg = nx_build_sg_list(nx_ctx->out_sg, out, &len, max_sg_len);
 
-       if (rc || len != SHA256_DIGEST_SIZE)
+       if (len != SHA256_DIGEST_SIZE) {
+               rc = -EINVAL;
                goto out;
+       }
 
+       nx_ctx->op.inlen = (nx_ctx->in_sg - in_sg) * sizeof(struct nx_sg);
+       nx_ctx->op.outlen = (nx_ctx->out_sg - out_sg) * sizeof(struct nx_sg);
        if (!nx_ctx->op.outlen) {
                rc = -EINVAL;
                goto out;
index b3adf10226733e57fea0871220f9e0ae294f3299..e6a58d2ee62894e369c563f5b826d0342a6a729d 100644 (file)
@@ -32,8 +32,9 @@ static int nx_sha512_init(struct shash_desc *desc)
 {
        struct sha512_state *sctx = shash_desc_ctx(desc);
        struct nx_crypto_ctx *nx_ctx = crypto_tfm_ctx(&desc->tfm->base);
+       struct nx_sg *out_sg;
        int len;
-       int rc;
+       u32 max_sg_len;
 
        nx_ctx_init(nx_ctx, HCOP_FC_SHA);
 
@@ -43,15 +44,18 @@ static int nx_sha512_init(struct shash_desc *desc)
 
        NX_CPB_SET_DIGEST_SIZE(nx_ctx->csbcpb, NX_DS_SHA512);
 
+       max_sg_len = min_t(u64, nx_ctx->ap->sglen,
+                       nx_driver.of.max_sg_len/sizeof(struct nx_sg));
+       max_sg_len = min_t(u64, max_sg_len,
+                       nx_ctx->ap->databytelen/NX_PAGE_SIZE);
+
        len = SHA512_DIGEST_SIZE;
-       rc = nx_sha_build_sg_list(nx_ctx, nx_ctx->out_sg,
-                                 &nx_ctx->op.outlen,
-                                 &len,
-                                 (u8 *)sctx->state,
-                                 NX_DS_SHA512);
+       out_sg = nx_build_sg_list(nx_ctx->out_sg, (u8 *)sctx->state,
+                                 &len, max_sg_len);
+       nx_ctx->op.outlen = (nx_ctx->out_sg - out_sg) * sizeof(struct nx_sg);
 
-       if (rc || len != SHA512_DIGEST_SIZE)
-               goto out;
+       if (len != SHA512_DIGEST_SIZE)
+               return -EINVAL;
 
        sctx->state[0] = __cpu_to_be64(SHA512_H0);
        sctx->state[1] = __cpu_to_be64(SHA512_H1);
@@ -63,7 +67,6 @@ static int nx_sha512_init(struct shash_desc *desc)
        sctx->state[7] = __cpu_to_be64(SHA512_H7);
        sctx->count[0] = 0;
 
-out:
        return 0;
 }
 
@@ -73,10 +76,12 @@ static int nx_sha512_update(struct shash_desc *desc, const u8 *data,
        struct sha512_state *sctx = shash_desc_ctx(desc);
        struct nx_crypto_ctx *nx_ctx = crypto_tfm_ctx(&desc->tfm->base);
        struct nx_csbcpb *csbcpb = (struct nx_csbcpb *)nx_ctx->csbcpb;
+       struct nx_sg *in_sg;
        u64 to_process, leftover = 0, total;
        unsigned long irq_flags;
        int rc = 0;
        int data_len;
+       u32 max_sg_len;
        u64 buf_len = (sctx->count[0] % SHA512_BLOCK_SIZE);
 
        spin_lock_irqsave(&nx_ctx->lock, irq_flags);
@@ -96,6 +101,12 @@ static int nx_sha512_update(struct shash_desc *desc, const u8 *data,
        NX_CPB_FDM(csbcpb) |= NX_FDM_INTERMEDIATE;
        NX_CPB_FDM(csbcpb) |= NX_FDM_CONTINUATION;
 
+       in_sg = nx_ctx->in_sg;
+       max_sg_len = min_t(u64, nx_ctx->ap->sglen,
+                       nx_driver.of.max_sg_len/sizeof(struct nx_sg));
+       max_sg_len = min_t(u64, max_sg_len,
+                       nx_ctx->ap->databytelen/NX_PAGE_SIZE);
+
        do {
                /*
                 * to_process: the SHA512_BLOCK_SIZE data chunk to process in
@@ -108,25 +119,26 @@ static int nx_sha512_update(struct shash_desc *desc, const u8 *data,
 
                if (buf_len) {
                        data_len = buf_len;
-                       rc = nx_sha_build_sg_list(nx_ctx, nx_ctx->in_sg,
-                                                 &nx_ctx->op.inlen,
-                                                 &data_len,
-                                                 (u8 *) sctx->buf,
-                                                 NX_DS_SHA512);
+                       in_sg = nx_build_sg_list(nx_ctx->in_sg,
+                                                (u8 *) sctx->buf,
+                                                &data_len, max_sg_len);
 
-                       if (rc || data_len != buf_len)
+                       if (data_len != buf_len) {
+                               rc = -EINVAL;
                                goto out;
+                       }
                }
 
                data_len = to_process - buf_len;
-               rc = nx_sha_build_sg_list(nx_ctx, nx_ctx->in_sg,
-                                         &nx_ctx->op.inlen,
-                                         &data_len,
-                                         (u8 *) data,
-                                         NX_DS_SHA512);
+               in_sg = nx_build_sg_list(in_sg, (u8 *) data,
+                                        &data_len, max_sg_len);
 
-               if (rc || data_len != (to_process - buf_len))
+               nx_ctx->op.inlen = (nx_ctx->in_sg - in_sg) * sizeof(struct nx_sg);
+
+               if (data_len != (to_process - buf_len)) {
+                       rc = -EINVAL;
                        goto out;
+               }
 
                to_process = (data_len + buf_len);
                leftover = total - to_process;
@@ -172,13 +184,20 @@ static int nx_sha512_final(struct shash_desc *desc, u8 *out)
        struct sha512_state *sctx = shash_desc_ctx(desc);
        struct nx_crypto_ctx *nx_ctx = crypto_tfm_ctx(&desc->tfm->base);
        struct nx_csbcpb *csbcpb = (struct nx_csbcpb *)nx_ctx->csbcpb;
+       struct nx_sg *in_sg, *out_sg;
+       u32 max_sg_len;
        u64 count0;
        unsigned long irq_flags;
-       int rc;
+       int rc = 0;
        int len;
 
        spin_lock_irqsave(&nx_ctx->lock, irq_flags);
 
+       max_sg_len = min_t(u64, nx_ctx->ap->sglen,
+                       nx_driver.of.max_sg_len/sizeof(struct nx_sg));
+       max_sg_len = min_t(u64, max_sg_len,
+                       nx_ctx->ap->databytelen/NX_PAGE_SIZE);
+
        /* final is represented by continuing the operation and indicating that
         * this is not an intermediate operation */
        if (sctx->count[0] >= SHA512_BLOCK_SIZE) {
@@ -200,24 +219,20 @@ static int nx_sha512_final(struct shash_desc *desc, u8 *out)
        csbcpb->cpb.sha512.message_bit_length_lo = count0;
 
        len = sctx->count[0] & (SHA512_BLOCK_SIZE - 1);
-       rc = nx_sha_build_sg_list(nx_ctx, nx_ctx->in_sg,
-                                 &nx_ctx->op.inlen,
-                                 &len,
-                                 (u8 *)sctx->buf,
-                                 NX_DS_SHA512);
+       in_sg = nx_build_sg_list(nx_ctx->in_sg, sctx->buf, &len,
+                                max_sg_len);
 
-       if (rc || len != (sctx->count[0] & (SHA512_BLOCK_SIZE - 1)))
+       if (len != (sctx->count[0] & (SHA512_BLOCK_SIZE - 1))) {
+               rc = -EINVAL;
                goto out;
+       }
 
        len = SHA512_DIGEST_SIZE;
-       rc = nx_sha_build_sg_list(nx_ctx, nx_ctx->out_sg,
-                                 &nx_ctx->op.outlen,
-                                 &len,
-                                 out,
-                                 NX_DS_SHA512);
+       out_sg = nx_build_sg_list(nx_ctx->out_sg, out, &len,
+                                max_sg_len);
 
-       if (rc)
-               goto out;
+       nx_ctx->op.inlen = (nx_ctx->in_sg - in_sg) * sizeof(struct nx_sg);
+       nx_ctx->op.outlen = (nx_ctx->out_sg - out_sg) * sizeof(struct nx_sg);
 
        if (!nx_ctx->op.outlen) {
                rc = -EINVAL;
index 1da6dc59d0dd1ccdbaae806fb0c5d23813c6772a..f6198f29a4a811b0cbcb2fb5062ece5a5d2bb421 100644 (file)
@@ -19,8 +19,8 @@
  * Author: Kent Yoder <yoder1@us.ibm.com>
  */
 
+#include <crypto/internal/aead.h>
 #include <crypto/internal/hash.h>
-#include <crypto/hash.h>
 #include <crypto/aes.h>
 #include <crypto/sha.h>
 #include <crypto/algapi.h>
 #include <linux/moduleparam.h>
 #include <linux/types.h>
 #include <linux/mm.h>
-#include <linux/crypto.h>
 #include <linux/scatterlist.h>
 #include <linux/device.h>
 #include <linux/of.h>
+#include <linux/types.h>
 #include <asm/hvcall.h>
 #include <asm/vio.h>
 
@@ -215,8 +215,15 @@ struct nx_sg *nx_walk_and_build(struct nx_sg       *nx_dst,
  * @delta:  is the amount we need to crop in order to bound the list.
  *
  */
-static long int trim_sg_list(struct nx_sg *sg, struct nx_sg *end, unsigned int delta)
+static long int trim_sg_list(struct nx_sg *sg,
+                            struct nx_sg *end,
+                            unsigned int delta,
+                            unsigned int *nbytes)
 {
+       long int oplen;
+       long int data_back;
+       unsigned int is_delta = delta;
+
        while (delta && end > sg) {
                struct nx_sg *last = end - 1;
 
@@ -228,54 +235,20 @@ static long int trim_sg_list(struct nx_sg *sg, struct nx_sg *end, unsigned int d
                        delta -= last->len;
                }
        }
-       return (sg - end) * sizeof(struct nx_sg);
-}
 
-/**
- * nx_sha_build_sg_list - walk and build sg list to sha modes
- *                       using right bounds and limits.
- * @nx_ctx: NX crypto context for the lists we're building
- * @nx_sg: current sg list in or out list
- * @op_len: current op_len to be used in order to build a sg list
- * @nbytes:  number or bytes to be processed
- * @offset: buf offset
- * @mode: SHA256 or SHA512
- */
-int nx_sha_build_sg_list(struct nx_crypto_ctx *nx_ctx,
-                         struct nx_sg        *nx_in_outsg,
-                         s64                 *op_len,
-                         unsigned int        *nbytes,
-                         u8                  *offset,
-                         u32                 mode)
-{
-       unsigned int delta = 0;
-       unsigned int total = *nbytes;
-       struct nx_sg *nx_insg = nx_in_outsg;
-       unsigned int max_sg_len;
-
-       max_sg_len = min_t(u64, nx_ctx->ap->sglen,
-                       nx_driver.of.max_sg_len/sizeof(struct nx_sg));
-       max_sg_len = min_t(u64, max_sg_len,
-                       nx_ctx->ap->databytelen/NX_PAGE_SIZE);
-
-       *nbytes = min_t(u64, *nbytes, nx_ctx->ap->databytelen);
-       nx_insg = nx_build_sg_list(nx_insg, offset, nbytes, max_sg_len);
-
-       switch (mode) {
-       case NX_DS_SHA256:
-               if (*nbytes < total)
-                       delta = *nbytes - (*nbytes & ~(SHA256_BLOCK_SIZE - 1));
-               break;
-       case NX_DS_SHA512:
-               if (*nbytes < total)
-                       delta = *nbytes - (*nbytes & ~(SHA512_BLOCK_SIZE - 1));
-               break;
-       default:
-               return -EINVAL;
+       /* There are cases where we need to crop list in order to make it
+        * a block size multiple, but we also need to align data. In order to
+        * that we need to calculate how much we need to put back to be
+        * processed
+        */
+       oplen = (sg - end) * sizeof(struct nx_sg);
+       if (is_delta) {
+               data_back = (abs(oplen) / AES_BLOCK_SIZE) *  sg->len;
+               data_back = *nbytes - (data_back & ~(AES_BLOCK_SIZE - 1));
+               *nbytes -= data_back;
        }
-       *op_len = trim_sg_list(nx_in_outsg, nx_insg, delta);
 
-       return 0;
+       return oplen;
 }
 
 /**
@@ -330,8 +303,8 @@ int nx_build_sg_lists(struct nx_crypto_ctx  *nx_ctx,
        /* these lengths should be negative, which will indicate to phyp that
         * the input and output parameters are scatterlists, not linear
         * buffers */
-       nx_ctx->op.inlen = trim_sg_list(nx_ctx->in_sg, nx_insg, delta);
-       nx_ctx->op.outlen = trim_sg_list(nx_ctx->out_sg, nx_outsg, delta);
+       nx_ctx->op.inlen = trim_sg_list(nx_ctx->in_sg, nx_insg, delta, nbytes);
+       nx_ctx->op.outlen = trim_sg_list(nx_ctx->out_sg, nx_outsg, delta, nbytes);
 
        return 0;
 }
@@ -426,6 +399,13 @@ static void nx_of_update_msc(struct device   *dev,
                                goto next_loop;
                        }
 
+                       if (!trip->sglen || trip->databytelen < NX_PAGE_SIZE) {
+                               dev_warn(dev, "bogus sglen/databytelen: "
+                                        "%u/%u (ignored)\n", trip->sglen,
+                                        trip->databytelen);
+                               goto next_loop;
+                       }
+
                        switch (trip->keybitlen) {
                        case 128:
                        case 160:
@@ -518,6 +498,72 @@ static void nx_of_init(struct device *dev, struct nx_of *props)
                nx_of_update_msc(dev, p, props);
 }
 
+static bool nx_check_prop(struct device *dev, u32 fc, u32 mode, int slot)
+{
+       struct alg_props *props = &nx_driver.of.ap[fc][mode][slot];
+
+       if (!props->sglen || props->databytelen < NX_PAGE_SIZE) {
+               if (dev)
+                       dev_warn(dev, "bogus sglen/databytelen for %u/%u/%u: "
+                                "%u/%u (ignored)\n", fc, mode, slot,
+                                props->sglen, props->databytelen);
+               return false;
+       }
+
+       return true;
+}
+
+static bool nx_check_props(struct device *dev, u32 fc, u32 mode)
+{
+       int i;
+
+       for (i = 0; i < 3; i++)
+               if (!nx_check_prop(dev, fc, mode, i))
+                       return false;
+
+       return true;
+}
+
+static int nx_register_alg(struct crypto_alg *alg, u32 fc, u32 mode)
+{
+       return nx_check_props(&nx_driver.viodev->dev, fc, mode) ?
+              crypto_register_alg(alg) : 0;
+}
+
+static int nx_register_aead(struct aead_alg *alg, u32 fc, u32 mode)
+{
+       return nx_check_props(&nx_driver.viodev->dev, fc, mode) ?
+              crypto_register_aead(alg) : 0;
+}
+
+static int nx_register_shash(struct shash_alg *alg, u32 fc, u32 mode, int slot)
+{
+       return (slot >= 0 ? nx_check_prop(&nx_driver.viodev->dev,
+                                         fc, mode, slot) :
+                           nx_check_props(&nx_driver.viodev->dev, fc, mode)) ?
+              crypto_register_shash(alg) : 0;
+}
+
+static void nx_unregister_alg(struct crypto_alg *alg, u32 fc, u32 mode)
+{
+       if (nx_check_props(NULL, fc, mode))
+               crypto_unregister_alg(alg);
+}
+
+static void nx_unregister_aead(struct aead_alg *alg, u32 fc, u32 mode)
+{
+       if (nx_check_props(NULL, fc, mode))
+               crypto_unregister_aead(alg);
+}
+
+static void nx_unregister_shash(struct shash_alg *alg, u32 fc, u32 mode,
+                               int slot)
+{
+       if (slot >= 0 ? nx_check_prop(NULL, fc, mode, slot) :
+                       nx_check_props(NULL, fc, mode))
+               crypto_unregister_shash(alg);
+}
+
 /**
  * nx_register_algs - register algorithms with the crypto API
  *
@@ -542,72 +588,77 @@ static int nx_register_algs(void)
 
        nx_driver.of.status = NX_OKAY;
 
-       rc = crypto_register_alg(&nx_ecb_aes_alg);
+       rc = nx_register_alg(&nx_ecb_aes_alg, NX_FC_AES, NX_MODE_AES_ECB);
        if (rc)
                goto out;
 
-       rc = crypto_register_alg(&nx_cbc_aes_alg);
+       rc = nx_register_alg(&nx_cbc_aes_alg, NX_FC_AES, NX_MODE_AES_CBC);
        if (rc)
                goto out_unreg_ecb;
 
-       rc = crypto_register_alg(&nx_ctr_aes_alg);
+       rc = nx_register_alg(&nx_ctr_aes_alg, NX_FC_AES, NX_MODE_AES_CTR);
        if (rc)
                goto out_unreg_cbc;
 
-       rc = crypto_register_alg(&nx_ctr3686_aes_alg);
+       rc = nx_register_alg(&nx_ctr3686_aes_alg, NX_FC_AES, NX_MODE_AES_CTR);
        if (rc)
                goto out_unreg_ctr;
 
-       rc = crypto_register_alg(&nx_gcm_aes_alg);
+       rc = nx_register_aead(&nx_gcm_aes_alg, NX_FC_AES, NX_MODE_AES_GCM);
        if (rc)
                goto out_unreg_ctr3686;
 
-       rc = crypto_register_alg(&nx_gcm4106_aes_alg);
+       rc = nx_register_aead(&nx_gcm4106_aes_alg, NX_FC_AES, NX_MODE_AES_GCM);
        if (rc)
                goto out_unreg_gcm;
 
-       rc = crypto_register_alg(&nx_ccm_aes_alg);
+       rc = nx_register_alg(&nx_ccm_aes_alg, NX_FC_AES, NX_MODE_AES_CCM);
        if (rc)
                goto out_unreg_gcm4106;
 
-       rc = crypto_register_alg(&nx_ccm4309_aes_alg);
+       rc = nx_register_alg(&nx_ccm4309_aes_alg, NX_FC_AES, NX_MODE_AES_CCM);
        if (rc)
                goto out_unreg_ccm;
 
-       rc = crypto_register_shash(&nx_shash_sha256_alg);
+       rc = nx_register_shash(&nx_shash_sha256_alg, NX_FC_SHA, NX_MODE_SHA,
+                              NX_PROPS_SHA256);
        if (rc)
                goto out_unreg_ccm4309;
 
-       rc = crypto_register_shash(&nx_shash_sha512_alg);
+       rc = nx_register_shash(&nx_shash_sha512_alg, NX_FC_SHA, NX_MODE_SHA,
+                              NX_PROPS_SHA512);
        if (rc)
                goto out_unreg_s256;
 
-       rc = crypto_register_shash(&nx_shash_aes_xcbc_alg);
+       rc = nx_register_shash(&nx_shash_aes_xcbc_alg,
+                              NX_FC_AES, NX_MODE_AES_XCBC_MAC, -1);
        if (rc)
                goto out_unreg_s512;
 
        goto out;
 
 out_unreg_s512:
-       crypto_unregister_shash(&nx_shash_sha512_alg);
+       nx_unregister_shash(&nx_shash_sha512_alg, NX_FC_SHA, NX_MODE_SHA,
+                           NX_PROPS_SHA512);
 out_unreg_s256:
-       crypto_unregister_shash(&nx_shash_sha256_alg);
+       nx_unregister_shash(&nx_shash_sha256_alg, NX_FC_SHA, NX_MODE_SHA,
+                           NX_PROPS_SHA256);
 out_unreg_ccm4309:
-       crypto_unregister_alg(&nx_ccm4309_aes_alg);
+       nx_unregister_alg(&nx_ccm4309_aes_alg, NX_FC_AES, NX_MODE_AES_CCM);
 out_unreg_ccm:
-       crypto_unregister_alg(&nx_ccm_aes_alg);
+       nx_unregister_alg(&nx_ccm_aes_alg, NX_FC_AES, NX_MODE_AES_CCM);
 out_unreg_gcm4106:
-       crypto_unregister_alg(&nx_gcm4106_aes_alg);
+       nx_unregister_aead(&nx_gcm4106_aes_alg, NX_FC_AES, NX_MODE_AES_GCM);
 out_unreg_gcm:
-       crypto_unregister_alg(&nx_gcm_aes_alg);
+       nx_unregister_aead(&nx_gcm_aes_alg, NX_FC_AES, NX_MODE_AES_GCM);
 out_unreg_ctr3686:
-       crypto_unregister_alg(&nx_ctr3686_aes_alg);
+       nx_unregister_alg(&nx_ctr3686_aes_alg, NX_FC_AES, NX_MODE_AES_CTR);
 out_unreg_ctr:
-       crypto_unregister_alg(&nx_ctr_aes_alg);
+       nx_unregister_alg(&nx_ctr_aes_alg, NX_FC_AES, NX_MODE_AES_CTR);
 out_unreg_cbc:
-       crypto_unregister_alg(&nx_cbc_aes_alg);
+       nx_unregister_alg(&nx_cbc_aes_alg, NX_FC_AES, NX_MODE_AES_CBC);
 out_unreg_ecb:
-       crypto_unregister_alg(&nx_ecb_aes_alg);
+       nx_unregister_alg(&nx_ecb_aes_alg, NX_FC_AES, NX_MODE_AES_ECB);
 out:
        return rc;
 }
@@ -666,9 +717,9 @@ int nx_crypto_ctx_aes_ccm_init(struct crypto_tfm *tfm)
                                  NX_MODE_AES_CCM);
 }
 
-int nx_crypto_ctx_aes_gcm_init(struct crypto_tfm *tfm)
+int nx_crypto_ctx_aes_gcm_init(struct crypto_aead *tfm)
 {
-       return nx_crypto_ctx_init(crypto_tfm_ctx(tfm), NX_FC_AES,
+       return nx_crypto_ctx_init(crypto_aead_ctx(tfm), NX_FC_AES,
                                  NX_MODE_AES_GCM);
 }
 
@@ -720,6 +771,13 @@ void nx_crypto_ctx_exit(struct crypto_tfm *tfm)
        nx_ctx->out_sg = NULL;
 }
 
+void nx_crypto_ctx_aead_exit(struct crypto_aead *tfm)
+{
+       struct nx_crypto_ctx *nx_ctx = crypto_aead_ctx(tfm);
+
+       kzfree(nx_ctx->kmem);
+}
+
 static int nx_probe(struct vio_dev *viodev, const struct vio_device_id *id)
 {
        dev_dbg(&viodev->dev, "driver probed: %s resource id: 0x%x\n",
@@ -746,17 +804,24 @@ static int nx_remove(struct vio_dev *viodev)
        if (nx_driver.of.status == NX_OKAY) {
                NX_DEBUGFS_FINI(&nx_driver);
 
-               crypto_unregister_alg(&nx_ccm_aes_alg);
-               crypto_unregister_alg(&nx_ccm4309_aes_alg);
-               crypto_unregister_alg(&nx_gcm_aes_alg);
-               crypto_unregister_alg(&nx_gcm4106_aes_alg);
-               crypto_unregister_alg(&nx_ctr_aes_alg);
-               crypto_unregister_alg(&nx_ctr3686_aes_alg);
-               crypto_unregister_alg(&nx_cbc_aes_alg);
-               crypto_unregister_alg(&nx_ecb_aes_alg);
-               crypto_unregister_shash(&nx_shash_sha256_alg);
-               crypto_unregister_shash(&nx_shash_sha512_alg);
-               crypto_unregister_shash(&nx_shash_aes_xcbc_alg);
+               nx_unregister_shash(&nx_shash_aes_xcbc_alg,
+                                   NX_FC_AES, NX_MODE_AES_XCBC_MAC, -1);
+               nx_unregister_shash(&nx_shash_sha512_alg,
+                                   NX_FC_SHA, NX_MODE_SHA, NX_PROPS_SHA256);
+               nx_unregister_shash(&nx_shash_sha256_alg,
+                                   NX_FC_SHA, NX_MODE_SHA, NX_PROPS_SHA512);
+               nx_unregister_alg(&nx_ccm4309_aes_alg,
+                                 NX_FC_AES, NX_MODE_AES_CCM);
+               nx_unregister_alg(&nx_ccm_aes_alg, NX_FC_AES, NX_MODE_AES_CCM);
+               nx_unregister_aead(&nx_gcm4106_aes_alg,
+                                  NX_FC_AES, NX_MODE_AES_GCM);
+               nx_unregister_aead(&nx_gcm_aes_alg,
+                                  NX_FC_AES, NX_MODE_AES_GCM);
+               nx_unregister_alg(&nx_ctr3686_aes_alg,
+                                 NX_FC_AES, NX_MODE_AES_CTR);
+               nx_unregister_alg(&nx_ctr_aes_alg, NX_FC_AES, NX_MODE_AES_CTR);
+               nx_unregister_alg(&nx_cbc_aes_alg, NX_FC_AES, NX_MODE_AES_CBC);
+               nx_unregister_alg(&nx_ecb_aes_alg, NX_FC_AES, NX_MODE_AES_ECB);
        }
 
        return 0;
index 6c9ecaaead52fdb390aa08f6af9759bdbde19b31..de3ea8738146ba100faf1ede99f5058d6d6f5824 100644 (file)
@@ -143,18 +143,17 @@ struct nx_crypto_ctx {
 
 /* prototypes */
 int nx_crypto_ctx_aes_ccm_init(struct crypto_tfm *tfm);
-int nx_crypto_ctx_aes_gcm_init(struct crypto_tfm *tfm);
+int nx_crypto_ctx_aes_gcm_init(struct crypto_aead *tfm);
 int nx_crypto_ctx_aes_xcbc_init(struct crypto_tfm *tfm);
 int nx_crypto_ctx_aes_ctr_init(struct crypto_tfm *tfm);
 int nx_crypto_ctx_aes_cbc_init(struct crypto_tfm *tfm);
 int nx_crypto_ctx_aes_ecb_init(struct crypto_tfm *tfm);
 int nx_crypto_ctx_sha_init(struct crypto_tfm *tfm);
 void nx_crypto_ctx_exit(struct crypto_tfm *tfm);
+void nx_crypto_ctx_aead_exit(struct crypto_aead *tfm);
 void nx_ctx_init(struct nx_crypto_ctx *nx_ctx, unsigned int function);
 int nx_hcall_sync(struct nx_crypto_ctx *ctx, struct vio_pfo_op *op,
                  u32 may_sleep);
-int nx_sha_build_sg_list(struct nx_crypto_ctx *, struct nx_sg *,
-                        s64 *, unsigned int *, u8 *, u32);
 struct nx_sg *nx_build_sg_list(struct nx_sg *, u8 *, unsigned int *, u32);
 int nx_build_sg_lists(struct nx_crypto_ctx *, struct blkcipher_desc *,
                      struct scatterlist *, struct scatterlist *, unsigned int *,
@@ -178,8 +177,8 @@ void nx_debugfs_fini(struct nx_crypto_driver *);
 
 extern struct crypto_alg nx_cbc_aes_alg;
 extern struct crypto_alg nx_ecb_aes_alg;
-extern struct crypto_alg nx_gcm_aes_alg;
-extern struct crypto_alg nx_gcm4106_aes_alg;
+extern struct aead_alg nx_gcm_aes_alg;
+extern struct aead_alg nx_gcm4106_aes_alg;
 extern struct crypto_alg nx_ctr_aes_alg;
 extern struct crypto_alg nx_ctr3686_aes_alg;
 extern struct crypto_alg nx_ccm_aes_alg;
index 4d63e0d4da9a3ef8f445ef65dd3d4cf014465b6a..b2024c95a3cff82df482478b314155f53d743ff8 100644 (file)
@@ -362,7 +362,13 @@ static void omap_sham_copy_ready_hash(struct ahash_request *req)
 
 static int omap_sham_hw_init(struct omap_sham_dev *dd)
 {
-       pm_runtime_get_sync(dd->dev);
+       int err;
+
+       err = pm_runtime_get_sync(dd->dev);
+       if (err < 0) {
+               dev_err(dd->dev, "failed to get sync: %d\n", err);
+               return err;
+       }
 
        if (!test_bit(FLAGS_INIT, &dd->flags)) {
                set_bit(FLAGS_INIT, &dd->flags);
@@ -1792,6 +1798,10 @@ static const struct of_device_id omap_sham_of_match[] = {
                .compatible     = "ti,omap2-sham",
                .data           = &omap_sham_pdata_omap2,
        },
+       {
+               .compatible     = "ti,omap3-sham",
+               .data           = &omap_sham_pdata_omap2,
+       },
        {
                .compatible     = "ti,omap4-sham",
                .data           = &omap_sham_pdata_omap4,
@@ -1947,7 +1957,13 @@ static int omap_sham_probe(struct platform_device *pdev)
 
        pm_runtime_enable(dev);
        pm_runtime_irq_safe(dev);
-       pm_runtime_get_sync(dev);
+
+       err = pm_runtime_get_sync(dev);
+       if (err < 0) {
+               dev_err(dev, "failed to get sync: %d\n", err);
+               goto err_pm;
+       }
+
        rev = omap_sham_read(dd, SHA_REG_REV(dd));
        pm_runtime_put_sync(&pdev->dev);
 
@@ -1977,6 +1993,7 @@ err_algs:
                for (j = dd->pdata->algs_info[i].registered - 1; j >= 0; j--)
                        crypto_unregister_ahash(
                                        &dd->pdata->algs_info[i].algs_list[j]);
+err_pm:
        pm_runtime_disable(dev);
        if (dd->dma_lch)
                dma_release_channel(dd->dma_lch);
@@ -2019,7 +2036,11 @@ static int omap_sham_suspend(struct device *dev)
 
 static int omap_sham_resume(struct device *dev)
 {
-       pm_runtime_get_sync(dev);
+       int err = pm_runtime_get_sync(dev);
+       if (err < 0) {
+               dev_err(dev, "failed to get sync: %d\n", err);
+               return err;
+       }
        return 0;
 }
 #endif
index c178ed8c3908d3a92e55432aecb86e0e6e1501ee..da2d6777bd092f0a373e14cc960514b8a4f5d148 100644 (file)
@@ -22,7 +22,7 @@
 #include <asm/cpu_device_id.h>
 #include <asm/byteorder.h>
 #include <asm/processor.h>
-#include <asm/i387.h>
+#include <asm/fpu/api.h>
 
 /*
  * Number of data blocks actually fetched for each xcrypt insn.
index 95f7d27ce491f000458a257e5dfa55a6105b433b..4e154c9b92064bb1fbafeb805bcb77f9cc2d1bdf 100644 (file)
@@ -23,7 +23,7 @@
 #include <linux/kernel.h>
 #include <linux/scatterlist.h>
 #include <asm/cpu_device_id.h>
-#include <asm/i387.h>
+#include <asm/fpu/api.h>
 
 struct padlock_sha_desc {
        struct shash_desc fallback;
index 5da5b98b8f297a36a98dca4d90b7ede3cdce6a07..4f56f3681abdfc6e2a4ecf95e34f3ea30c231961 100644 (file)
@@ -15,7 +15,7 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
-#include <crypto/aead.h>
+#include <crypto/internal/aead.h>
 #include <crypto/aes.h>
 #include <crypto/algapi.h>
 #include <crypto/authenc.h>
@@ -40,6 +40,7 @@
 #include <linux/rtnetlink.h>
 #include <linux/scatterlist.h>
 #include <linux/sched.h>
+#include <linux/sizes.h>
 #include <linux/slab.h>
 #include <linux/timer.h>
 
@@ -261,18 +262,9 @@ static unsigned spacc_load_ctx(struct spacc_generic_ctx *ctx,
 }
 
 /* Count the number of scatterlist entries in a scatterlist. */
-static int sg_count(struct scatterlist *sg_list, int nbytes)
+static inline int sg_count(struct scatterlist *sg_list, int nbytes)
 {
-       struct scatterlist *sg = sg_list;
-       int sg_nents = 0;
-
-       while (nbytes > 0) {
-               ++sg_nents;
-               nbytes -= sg->length;
-               sg = sg_next(sg);
-       }
-
-       return sg_nents;
+       return sg_nents_for_len(sg_list, nbytes);
 }
 
 static inline void ddt_set(struct spacc_ddt *ddt, dma_addr_t phys, size_t len)
@@ -326,6 +318,7 @@ static int spacc_aead_make_ddts(struct spacc_req *req, u8 *giv)
        struct spacc_ddt *src_ddt, *dst_ddt;
        unsigned ivsize = crypto_aead_ivsize(crypto_aead_reqtfm(areq));
        unsigned nents = sg_count(areq->src, areq->cryptlen);
+       unsigned total;
        dma_addr_t iv_addr;
        struct scatterlist *cur;
        int i, dst_ents, src_ents, assoc_ents;
@@ -369,11 +362,18 @@ static int spacc_aead_make_ddts(struct spacc_req *req, u8 *giv)
         * Map the associated data. For decryption we don't copy the
         * associated data.
         */
+       total = areq->assoclen;
        for_each_sg(areq->assoc, cur, assoc_ents, i) {
-               ddt_set(src_ddt++, sg_dma_address(cur), sg_dma_len(cur));
+               unsigned len = sg_dma_len(cur);
+
+               if (len > total)
+                       len = total;
+
+               total -= len;
+
+               ddt_set(src_ddt++, sg_dma_address(cur), len);
                if (req->is_encrypt)
-                       ddt_set(dst_ddt++, sg_dma_address(cur),
-                               sg_dma_len(cur));
+                       ddt_set(dst_ddt++, sg_dma_address(cur), len);
        }
        ddt_set(src_ddt++, iv_addr, ivsize);
 
@@ -790,7 +790,8 @@ static int spacc_aead_cra_init(struct crypto_tfm *tfm)
 
        get_random_bytes(ctx->salt, sizeof(ctx->salt));
 
-       tfm->crt_aead.reqsize = sizeof(struct spacc_req);
+       crypto_aead_set_reqsize(__crypto_aead_cast(tfm),
+                               sizeof(struct spacc_req));
 
        return 0;
 }
@@ -1754,15 +1755,15 @@ static int spacc_probe(struct platform_device *pdev)
                return PTR_ERR(engine->clk);
        }
 
-       if (clk_enable(engine->clk)) {
-               dev_info(&pdev->dev, "unable to enable clk\n");
+       if (clk_prepare_enable(engine->clk)) {
+               dev_info(&pdev->dev, "unable to prepare/enable clk\n");
                clk_put(engine->clk);
                return -EIO;
        }
 
        err = device_create_file(&pdev->dev, &dev_attr_stat_irq_thresh);
        if (err) {
-               clk_disable(engine->clk);
+               clk_disable_unprepare(engine->clk);
                clk_put(engine->clk);
                return err;
        }
@@ -1830,7 +1831,7 @@ static int spacc_remove(struct platform_device *pdev)
                crypto_unregister_alg(&alg->alg);
        }
 
-       clk_disable(engine->clk);
+       clk_disable_unprepare(engine->clk);
        clk_put(engine->clk);
 
        return 0;
index 49bede2a9f77df49afd3ddecf3a3dd4290ce46d7..6fdb9e8b22a75247971fca0b500414b8028bc7dd 100644 (file)
@@ -2,9 +2,8 @@ config CRYPTO_DEV_QAT
        tristate
        select CRYPTO_AEAD
        select CRYPTO_AUTHENC
-       select CRYPTO_ALGAPI
-       select CRYPTO_AES
-       select CRYPTO_CBC
+       select CRYPTO_BLKCIPHER
+       select CRYPTO_HMAC
        select CRYPTO_SHA1
        select CRYPTO_SHA256
        select CRYPTO_SHA512
@@ -13,7 +12,6 @@ config CRYPTO_DEV_QAT
 config CRYPTO_DEV_QAT_DH895xCC
        tristate "Support for Intel(R) DH895xCC"
        depends on X86 && PCI
-       default n
        select CRYPTO_DEV_QAT
        help
          Support for Intel(R) DH895xcc with Intel(R) QuickAssist Technology
index f22ce7169fa5ceaff0a4f35531ef2a67eaa951a0..5fe90296762083e12690e70ace018b273d9e5569 100644 (file)
@@ -48,7 +48,6 @@
 #define ADF_ACCEL_DEVICES_H_
 #include <linux/module.h>
 #include <linux/list.h>
-#include <linux/proc_fs.h>
 #include <linux/io.h>
 #include "adf_cfg_common.h"
 
index 0c38a155a865a18320fd7f4e6bc08e1bb014179c..ef5988afd4c60f59287e8a446af62e8ac6b783b9 100644 (file)
 struct adf_user_cfg_key_val {
        char key[ADF_CFG_MAX_KEY_LEN_IN_BYTES];
        char val[ADF_CFG_MAX_VAL_LEN_IN_BYTES];
-       union {
-               char *user_val_ptr;
-               uint64_t padding1;
-       };
-       union {
-               struct adf_user_cfg_key_val *prev;
-               uint64_t padding2;
-       };
        union {
                struct adf_user_cfg_key_val *next;
                uint64_t padding3;
@@ -74,10 +66,6 @@ struct adf_user_cfg_section {
                struct adf_user_cfg_key_val *params;
                uint64_t padding1;
        };
-       union {
-               struct adf_user_cfg_section *prev;
-               uint64_t padding2;
-       };
        union {
                struct adf_user_cfg_section *next;
                uint64_t padding3;
index 0666ee6a3360feac17675575d9d44c61b38a190e..27e16c09230bffa24e505a03c8848fc7ab2bb40d 100644 (file)
 #include "icp_qat_fw_loader_handle.h"
 #include "icp_qat_hal.h"
 
+#define ADF_MAJOR_VERSION      0
+#define ADF_MINOR_VERSION      1
+#define ADF_BUILD_VERSION      3
+#define ADF_DRV_VERSION                __stringify(ADF_MAJOR_VERSION) "." \
+                               __stringify(ADF_MINOR_VERSION) "." \
+                               __stringify(ADF_BUILD_VERSION)
+
 #define ADF_STATUS_RESTARTING 0
 #define ADF_STATUS_STARTING 1
 #define ADF_STATUS_CONFIGURED 2
index cb5f066e93a6422a8666c8a78ede1ec6730bca8e..e056b9e9bf8a99068aa0c0097fc57fe9da25a27f 100644 (file)
@@ -504,3 +504,4 @@ MODULE_LICENSE("Dual BSD/GPL");
 MODULE_AUTHOR("Intel");
 MODULE_DESCRIPTION("Intel(R) QuickAssist Technology");
 MODULE_ALIAS_CRYPTO("intel_qat");
+MODULE_VERSION(ADF_DRV_VERSION);
index 1dc5b0a17cf7205f45f01b7995464860634cdbc9..067402c7c2a93fdc02ca3242f918deba460dae91 100644 (file)
@@ -47,7 +47,7 @@
 #include <linux/module.h>
 #include <linux/slab.h>
 #include <linux/crypto.h>
-#include <crypto/aead.h>
+#include <crypto/internal/aead.h>
 #include <crypto/aes.h>
 #include <crypto/sha.h>
 #include <crypto/hash.h>
@@ -653,7 +653,7 @@ static void qat_alg_free_bufl(struct qat_crypto_instance *inst,
 }
 
 static int qat_alg_sgl_to_bufl(struct qat_crypto_instance *inst,
-                              struct scatterlist *assoc,
+                              struct scatterlist *assoc, int assoclen,
                               struct scatterlist *sgl,
                               struct scatterlist *sglout, uint8_t *iv,
                               uint8_t ivlen,
@@ -685,15 +685,21 @@ static int qat_alg_sgl_to_bufl(struct qat_crypto_instance *inst,
        for_each_sg(assoc, sg, assoc_n, i) {
                if (!sg->length)
                        continue;
-               bufl->bufers[bufs].addr = dma_map_single(dev,
-                                                        sg_virt(sg),
-                                                        sg->length,
-                                                        DMA_BIDIRECTIONAL);
-               bufl->bufers[bufs].len = sg->length;
+
+               if (!(assoclen > 0))
+                       break;
+
+               bufl->bufers[bufs].addr =
+                       dma_map_single(dev, sg_virt(sg),
+                                      min_t(int, assoclen, sg->length),
+                                      DMA_BIDIRECTIONAL);
+               bufl->bufers[bufs].len = min_t(int, assoclen, sg->length);
                if (unlikely(dma_mapping_error(dev, bufl->bufers[bufs].addr)))
                        goto err;
                bufs++;
+               assoclen -= sg->length;
        }
+
        if (ivlen) {
                bufl->bufers[bufs].addr = dma_map_single(dev, iv, ivlen,
                                                         DMA_BIDIRECTIONAL);
@@ -845,8 +851,9 @@ static int qat_alg_aead_dec(struct aead_request *areq)
        int digst_size = crypto_aead_crt(aead_tfm)->authsize;
        int ret, ctr = 0;
 
-       ret = qat_alg_sgl_to_bufl(ctx->inst, areq->assoc, areq->src, areq->dst,
-                                 areq->iv, AES_BLOCK_SIZE, qat_req);
+       ret = qat_alg_sgl_to_bufl(ctx->inst, areq->assoc, areq->assoclen,
+                                 areq->src, areq->dst, areq->iv,
+                                 AES_BLOCK_SIZE, qat_req);
        if (unlikely(ret))
                return ret;
 
@@ -889,8 +896,9 @@ static int qat_alg_aead_enc_internal(struct aead_request *areq, uint8_t *iv,
        struct icp_qat_fw_la_bulk_req *msg;
        int ret, ctr = 0;
 
-       ret = qat_alg_sgl_to_bufl(ctx->inst, areq->assoc, areq->src, areq->dst,
-                                 iv, AES_BLOCK_SIZE, qat_req);
+       ret = qat_alg_sgl_to_bufl(ctx->inst, areq->assoc, areq->assoclen,
+                                 areq->src, areq->dst, iv, AES_BLOCK_SIZE,
+                                 qat_req);
        if (unlikely(ret))
                return ret;
 
@@ -1017,7 +1025,7 @@ static int qat_alg_ablkcipher_encrypt(struct ablkcipher_request *req)
        struct icp_qat_fw_la_bulk_req *msg;
        int ret, ctr = 0;
 
-       ret = qat_alg_sgl_to_bufl(ctx->inst, NULL, req->src, req->dst,
+       ret = qat_alg_sgl_to_bufl(ctx->inst, NULL, 0, req->src, req->dst,
                                  NULL, 0, qat_req);
        if (unlikely(ret))
                return ret;
@@ -1055,7 +1063,7 @@ static int qat_alg_ablkcipher_decrypt(struct ablkcipher_request *req)
        struct icp_qat_fw_la_bulk_req *msg;
        int ret, ctr = 0;
 
-       ret = qat_alg_sgl_to_bufl(ctx->inst, NULL, req->src, req->dst,
+       ret = qat_alg_sgl_to_bufl(ctx->inst, NULL, 0, req->src, req->dst,
                                  NULL, 0, qat_req);
        if (unlikely(ret))
                return ret;
@@ -1094,8 +1102,9 @@ static int qat_alg_aead_init(struct crypto_tfm *tfm,
                return -EFAULT;
        spin_lock_init(&ctx->lock);
        ctx->qat_hash_alg = hash;
-       tfm->crt_aead.reqsize = sizeof(struct aead_request) +
-                               sizeof(struct qat_crypto_request);
+       crypto_aead_set_reqsize(__crypto_aead_cast(tfm),
+               sizeof(struct aead_request) +
+               sizeof(struct qat_crypto_request));
        ctx->tfm = tfm;
        return 0;
 }
index 9decea2779c637e6e6dc885546a9a4e988b05344..1bde45b7a3c560f5e409c17f45c4bf6bb9eb730e 100644 (file)
@@ -300,6 +300,8 @@ static int adf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        if (ret)
                goto out_err;
 
+       pcie_set_readrq(pdev, 1024);
+
        /* enable PCI device */
        if (pci_enable_device(pdev)) {
                ret = -EFAULT;
@@ -417,5 +419,6 @@ module_exit(adfdrv_release);
 
 MODULE_LICENSE("Dual BSD/GPL");
 MODULE_AUTHOR("Intel");
-MODULE_FIRMWARE("qat_895xcc.bin");
+MODULE_FIRMWARE(ADF_DH895XCC_FW);
 MODULE_DESCRIPTION("Intel(R) QuickAssist Technology");
+MODULE_VERSION(ADF_DRV_VERSION);
index 6be377f6b9e7019c44ba0e5b303b1d7cdd918170..397a500b3d8a9edbaf214ae28a8cc88e64582eb0 100644 (file)
@@ -1578,8 +1578,12 @@ static int sahara_probe(struct platform_device *pdev)
 
        init_completion(&dev->dma_completion);
 
-       clk_prepare_enable(dev->clk_ipg);
-       clk_prepare_enable(dev->clk_ahb);
+       err = clk_prepare_enable(dev->clk_ipg);
+       if (err)
+               goto err_link;
+       err = clk_prepare_enable(dev->clk_ahb);
+       if (err)
+               goto clk_ipg_disable;
 
        version = sahara_read(dev, SAHARA_REG_VERSION);
        if (of_device_is_compatible(pdev->dev.of_node, "fsl,imx27-sahara")) {
@@ -1619,10 +1623,11 @@ err_algs:
        dma_free_coherent(&pdev->dev,
                          SAHARA_MAX_HW_LINK * sizeof(struct sahara_hw_link),
                          dev->hw_link[0], dev->hw_phys_link[0]);
-       clk_disable_unprepare(dev->clk_ipg);
-       clk_disable_unprepare(dev->clk_ahb);
        kthread_stop(dev->kthread);
        dev_ptr = NULL;
+       clk_disable_unprepare(dev->clk_ahb);
+clk_ipg_disable:
+       clk_disable_unprepare(dev->clk_ipg);
 err_link:
        dma_free_coherent(&pdev->dev,
                          2 * AES_KEYSIZE_128,
index 857414afa29a823e88a6d1751a4f4075fcfaa7dd..83aca95a95bc226e6b3d1b083c7ba5373660cc6b 100644 (file)
@@ -46,7 +46,7 @@
 #include <crypto/des.h>
 #include <crypto/sha.h>
 #include <crypto/md5.h>
-#include <crypto/aead.h>
+#include <crypto/internal/aead.h>
 #include <crypto/authenc.h>
 #include <crypto/skcipher.h>
 #include <crypto/hash.h>
 
 #include "talitos.h"
 
-static void to_talitos_ptr(struct talitos_ptr *talitos_ptr, dma_addr_t dma_addr)
+static void to_talitos_ptr(struct talitos_ptr *ptr, dma_addr_t dma_addr,
+                          bool is_sec1)
 {
-       talitos_ptr->ptr = cpu_to_be32(lower_32_bits(dma_addr));
-       talitos_ptr->eptr = upper_32_bits(dma_addr);
+       ptr->ptr = cpu_to_be32(lower_32_bits(dma_addr));
+       if (!is_sec1)
+               ptr->eptr = upper_32_bits(dma_addr);
+}
+
+static void to_talitos_ptr_len(struct talitos_ptr *ptr, unsigned int len,
+                              bool is_sec1)
+{
+       if (is_sec1) {
+               ptr->res = 0;
+               ptr->len1 = cpu_to_be16(len);
+       } else {
+               ptr->len = cpu_to_be16(len);
+       }
+}
+
+static unsigned short from_talitos_ptr_len(struct talitos_ptr *ptr,
+                                          bool is_sec1)
+{
+       if (is_sec1)
+               return be16_to_cpu(ptr->len1);
+       else
+               return be16_to_cpu(ptr->len);
+}
+
+static void to_talitos_ptr_extent_clear(struct talitos_ptr *ptr, bool is_sec1)
+{
+       if (!is_sec1)
+               ptr->j_extent = 0;
 }
 
 /*
  * map virtual single (contiguous) pointer to h/w descriptor pointer
  */
 static void map_single_talitos_ptr(struct device *dev,
-                                  struct talitos_ptr *talitos_ptr,
-                                  unsigned short len, void *data,
-                                  unsigned char extent,
+                                  struct talitos_ptr *ptr,
+                                  unsigned int len, void *data,
                                   enum dma_data_direction dir)
 {
        dma_addr_t dma_addr = dma_map_single(dev, data, len, dir);
+       struct talitos_private *priv = dev_get_drvdata(dev);
+       bool is_sec1 = has_ftr_sec1(priv);
 
-       talitos_ptr->len = cpu_to_be16(len);
-       to_talitos_ptr(talitos_ptr, dma_addr);
-       talitos_ptr->j_extent = extent;
+       to_talitos_ptr_len(ptr, len, is_sec1);
+       to_talitos_ptr(ptr, dma_addr, is_sec1);
+       to_talitos_ptr_extent_clear(ptr, is_sec1);
 }
 
 /*
  * unmap bus single (contiguous) h/w descriptor pointer
  */
 static void unmap_single_talitos_ptr(struct device *dev,
-                                    struct talitos_ptr *talitos_ptr,
+                                    struct talitos_ptr *ptr,
                                     enum dma_data_direction dir)
 {
-       dma_unmap_single(dev, be32_to_cpu(talitos_ptr->ptr),
-                        be16_to_cpu(talitos_ptr->len), dir);
+       struct talitos_private *priv = dev_get_drvdata(dev);
+       bool is_sec1 = has_ftr_sec1(priv);
+
+       dma_unmap_single(dev, be32_to_cpu(ptr->ptr),
+                        from_talitos_ptr_len(ptr, is_sec1), dir);
 }
 
 static int reset_channel(struct device *dev, int ch)
 {
        struct talitos_private *priv = dev_get_drvdata(dev);
        unsigned int timeout = TALITOS_TIMEOUT;
+       bool is_sec1 = has_ftr_sec1(priv);
 
-       setbits32(priv->chan[ch].reg + TALITOS_CCCR, TALITOS_CCCR_RESET);
+       if (is_sec1) {
+               setbits32(priv->chan[ch].reg + TALITOS_CCCR_LO,
+                         TALITOS1_CCCR_LO_RESET);
 
-       while ((in_be32(priv->chan[ch].reg + TALITOS_CCCR) & TALITOS_CCCR_RESET)
-              && --timeout)
-               cpu_relax();
+               while ((in_be32(priv->chan[ch].reg + TALITOS_CCCR_LO) &
+                       TALITOS1_CCCR_LO_RESET) && --timeout)
+                       cpu_relax();
+       } else {
+               setbits32(priv->chan[ch].reg + TALITOS_CCCR,
+                         TALITOS2_CCCR_RESET);
+
+               while ((in_be32(priv->chan[ch].reg + TALITOS_CCCR) &
+                       TALITOS2_CCCR_RESET) && --timeout)
+                       cpu_relax();
+       }
 
        if (timeout == 0) {
                dev_err(dev, "failed to reset channel %d\n", ch);
@@ -120,11 +163,12 @@ static int reset_device(struct device *dev)
 {
        struct talitos_private *priv = dev_get_drvdata(dev);
        unsigned int timeout = TALITOS_TIMEOUT;
-       u32 mcr = TALITOS_MCR_SWR;
+       bool is_sec1 = has_ftr_sec1(priv);
+       u32 mcr = is_sec1 ? TALITOS1_MCR_SWR : TALITOS2_MCR_SWR;
 
        setbits32(priv->reg + TALITOS_MCR, mcr);
 
-       while ((in_be32(priv->reg + TALITOS_MCR) & TALITOS_MCR_SWR)
+       while ((in_be32(priv->reg + TALITOS_MCR) & mcr)
               && --timeout)
                cpu_relax();
 
@@ -148,6 +192,7 @@ static int init_device(struct device *dev)
 {
        struct talitos_private *priv = dev_get_drvdata(dev);
        int ch, err;
+       bool is_sec1 = has_ftr_sec1(priv);
 
        /*
         * Master reset
@@ -171,12 +216,19 @@ static int init_device(struct device *dev)
        }
 
        /* enable channel done and error interrupts */
-       setbits32(priv->reg + TALITOS_IMR, TALITOS_IMR_INIT);
-       setbits32(priv->reg + TALITOS_IMR_LO, TALITOS_IMR_LO_INIT);
+       if (is_sec1) {
+               clrbits32(priv->reg + TALITOS_IMR, TALITOS1_IMR_INIT);
+               clrbits32(priv->reg + TALITOS_IMR_LO, TALITOS1_IMR_LO_INIT);
+               /* disable parity error check in DEU (erroneous? test vect.) */
+               setbits32(priv->reg_deu + TALITOS_EUICR, TALITOS1_DEUICR_KPE);
+       } else {
+               setbits32(priv->reg + TALITOS_IMR, TALITOS2_IMR_INIT);
+               setbits32(priv->reg + TALITOS_IMR_LO, TALITOS2_IMR_LO_INIT);
+       }
 
        /* disable integrity check error interrupts (use writeback instead) */
        if (priv->features & TALITOS_FTR_HW_AUTH_CHECK)
-               setbits32(priv->reg + TALITOS_MDEUICR_LO,
+               setbits32(priv->reg_mdeu + TALITOS_EUICR_LO,
                          TALITOS_MDEUICR_LO_ICE);
 
        return 0;
@@ -204,6 +256,7 @@ int talitos_submit(struct device *dev, int ch, struct talitos_desc *desc,
        struct talitos_request *request;
        unsigned long flags;
        int head;
+       bool is_sec1 = has_ftr_sec1(priv);
 
        spin_lock_irqsave(&priv->chan[ch].head_lock, flags);
 
@@ -217,8 +270,17 @@ int talitos_submit(struct device *dev, int ch, struct talitos_desc *desc,
        request = &priv->chan[ch].fifo[head];
 
        /* map descriptor and save caller data */
-       request->dma_desc = dma_map_single(dev, desc, sizeof(*desc),
-                                          DMA_BIDIRECTIONAL);
+       if (is_sec1) {
+               desc->hdr1 = desc->hdr;
+               desc->next_desc = 0;
+               request->dma_desc = dma_map_single(dev, &desc->hdr1,
+                                                  TALITOS_DESC_SIZE,
+                                                  DMA_BIDIRECTIONAL);
+       } else {
+               request->dma_desc = dma_map_single(dev, desc,
+                                                  TALITOS_DESC_SIZE,
+                                                  DMA_BIDIRECTIONAL);
+       }
        request->callback = callback;
        request->context = context;
 
@@ -250,16 +312,21 @@ static void flush_channel(struct device *dev, int ch, int error, int reset_ch)
        struct talitos_request *request, saved_req;
        unsigned long flags;
        int tail, status;
+       bool is_sec1 = has_ftr_sec1(priv);
 
        spin_lock_irqsave(&priv->chan[ch].tail_lock, flags);
 
        tail = priv->chan[ch].tail;
        while (priv->chan[ch].fifo[tail].desc) {
+               __be32 hdr;
+
                request = &priv->chan[ch].fifo[tail];
 
                /* descriptors with their done bits set don't get the error */
                rmb();
-               if ((request->desc->hdr & DESC_HDR_DONE) == DESC_HDR_DONE)
+               hdr = is_sec1 ? request->desc->hdr1 : request->desc->hdr;
+
+               if ((hdr & DESC_HDR_DONE) == DESC_HDR_DONE)
                        status = 0;
                else
                        if (!error)
@@ -268,7 +335,7 @@ static void flush_channel(struct device *dev, int ch, int error, int reset_ch)
                                status = error;
 
                dma_unmap_single(dev, request->dma_desc,
-                                sizeof(struct talitos_desc),
+                                TALITOS_DESC_SIZE,
                                 DMA_BIDIRECTIONAL);
 
                /* copy entries so we can call callback outside lock */
@@ -302,8 +369,37 @@ static void flush_channel(struct device *dev, int ch, int error, int reset_ch)
 /*
  * process completed requests for channels that have done status
  */
-#define DEF_TALITOS_DONE(name, ch_done_mask)                           \
-static void talitos_done_##name(unsigned long data)                    \
+#define DEF_TALITOS1_DONE(name, ch_done_mask)                          \
+static void talitos1_done_##name(unsigned long data)                   \
+{                                                                      \
+       struct device *dev = (struct device *)data;                     \
+       struct talitos_private *priv = dev_get_drvdata(dev);            \
+       unsigned long flags;                                            \
+                                                                       \
+       if (ch_done_mask & 0x10000000)                                  \
+               flush_channel(dev, 0, 0, 0);                    \
+       if (priv->num_channels == 1)                                    \
+               goto out;                                               \
+       if (ch_done_mask & 0x40000000)                                  \
+               flush_channel(dev, 1, 0, 0);                    \
+       if (ch_done_mask & 0x00010000)                                  \
+               flush_channel(dev, 2, 0, 0);                    \
+       if (ch_done_mask & 0x00040000)                                  \
+               flush_channel(dev, 3, 0, 0);                    \
+                                                                       \
+out:                                                                   \
+       /* At this point, all completed channels have been processed */ \
+       /* Unmask done interrupts for channels completed later on. */   \
+       spin_lock_irqsave(&priv->reg_lock, flags);                      \
+       clrbits32(priv->reg + TALITOS_IMR, ch_done_mask);               \
+       clrbits32(priv->reg + TALITOS_IMR_LO, TALITOS1_IMR_LO_INIT);    \
+       spin_unlock_irqrestore(&priv->reg_lock, flags);                 \
+}
+
+DEF_TALITOS1_DONE(4ch, TALITOS1_ISR_4CHDONE)
+
+#define DEF_TALITOS2_DONE(name, ch_done_mask)                          \
+static void talitos2_done_##name(unsigned long data)                   \
 {                                                                      \
        struct device *dev = (struct device *)data;                     \
        struct talitos_private *priv = dev_get_drvdata(dev);            \
@@ -325,12 +421,13 @@ out:                                                                      \
        /* Unmask done interrupts for channels completed later on. */   \
        spin_lock_irqsave(&priv->reg_lock, flags);                      \
        setbits32(priv->reg + TALITOS_IMR, ch_done_mask);               \
-       setbits32(priv->reg + TALITOS_IMR_LO, TALITOS_IMR_LO_INIT);     \
+       setbits32(priv->reg + TALITOS_IMR_LO, TALITOS2_IMR_LO_INIT);    \
        spin_unlock_irqrestore(&priv->reg_lock, flags);                 \
 }
-DEF_TALITOS_DONE(4ch, TALITOS_ISR_4CHDONE)
-DEF_TALITOS_DONE(ch0_2, TALITOS_ISR_CH_0_2_DONE)
-DEF_TALITOS_DONE(ch1_3, TALITOS_ISR_CH_1_3_DONE)
+
+DEF_TALITOS2_DONE(4ch, TALITOS2_ISR_4CHDONE)
+DEF_TALITOS2_DONE(ch0_2, TALITOS2_ISR_CH_0_2_DONE)
+DEF_TALITOS2_DONE(ch1_3, TALITOS2_ISR_CH_1_3_DONE)
 
 /*
  * locate current (offending) descriptor
@@ -377,44 +474,44 @@ static void report_eu_error(struct device *dev, int ch, u32 desc_hdr)
        switch (desc_hdr & DESC_HDR_SEL0_MASK) {
        case DESC_HDR_SEL0_AFEU:
                dev_err(dev, "AFEUISR 0x%08x_%08x\n",
-                       in_be32(priv->reg + TALITOS_AFEUISR),
-                       in_be32(priv->reg + TALITOS_AFEUISR_LO));
+                       in_be32(priv->reg_afeu + TALITOS_EUISR),
+                       in_be32(priv->reg_afeu + TALITOS_EUISR_LO));
                break;
        case DESC_HDR_SEL0_DEU:
                dev_err(dev, "DEUISR 0x%08x_%08x\n",
-                       in_be32(priv->reg + TALITOS_DEUISR),
-                       in_be32(priv->reg + TALITOS_DEUISR_LO));
+                       in_be32(priv->reg_deu + TALITOS_EUISR),
+                       in_be32(priv->reg_deu + TALITOS_EUISR_LO));
                break;
        case DESC_HDR_SEL0_MDEUA:
        case DESC_HDR_SEL0_MDEUB:
                dev_err(dev, "MDEUISR 0x%08x_%08x\n",
-                       in_be32(priv->reg + TALITOS_MDEUISR),
-                       in_be32(priv->reg + TALITOS_MDEUISR_LO));
+                       in_be32(priv->reg_mdeu + TALITOS_EUISR),
+                       in_be32(priv->reg_mdeu + TALITOS_EUISR_LO));
                break;
        case DESC_HDR_SEL0_RNG:
                dev_err(dev, "RNGUISR 0x%08x_%08x\n",
-                       in_be32(priv->reg + TALITOS_RNGUISR),
-                       in_be32(priv->reg + TALITOS_RNGUISR_LO));
+                       in_be32(priv->reg_rngu + TALITOS_ISR),
+                       in_be32(priv->reg_rngu + TALITOS_ISR_LO));
                break;
        case DESC_HDR_SEL0_PKEU:
                dev_err(dev, "PKEUISR 0x%08x_%08x\n",
-                       in_be32(priv->reg + TALITOS_PKEUISR),
-                       in_be32(priv->reg + TALITOS_PKEUISR_LO));
+                       in_be32(priv->reg_pkeu + TALITOS_EUISR),
+                       in_be32(priv->reg_pkeu + TALITOS_EUISR_LO));
                break;
        case DESC_HDR_SEL0_AESU:
                dev_err(dev, "AESUISR 0x%08x_%08x\n",
-                       in_be32(priv->reg + TALITOS_AESUISR),
-                       in_be32(priv->reg + TALITOS_AESUISR_LO));
+                       in_be32(priv->reg_aesu + TALITOS_EUISR),
+                       in_be32(priv->reg_aesu + TALITOS_EUISR_LO));
                break;
        case DESC_HDR_SEL0_CRCU:
                dev_err(dev, "CRCUISR 0x%08x_%08x\n",
-                       in_be32(priv->reg + TALITOS_CRCUISR),
-                       in_be32(priv->reg + TALITOS_CRCUISR_LO));
+                       in_be32(priv->reg_crcu + TALITOS_EUISR),
+                       in_be32(priv->reg_crcu + TALITOS_EUISR_LO));
                break;
        case DESC_HDR_SEL0_KEU:
                dev_err(dev, "KEUISR 0x%08x_%08x\n",
-                       in_be32(priv->reg + TALITOS_KEUISR),
-                       in_be32(priv->reg + TALITOS_KEUISR_LO));
+                       in_be32(priv->reg_pkeu + TALITOS_EUISR),
+                       in_be32(priv->reg_pkeu + TALITOS_EUISR_LO));
                break;
        }
 
@@ -422,13 +519,13 @@ static void report_eu_error(struct device *dev, int ch, u32 desc_hdr)
        case DESC_HDR_SEL1_MDEUA:
        case DESC_HDR_SEL1_MDEUB:
                dev_err(dev, "MDEUISR 0x%08x_%08x\n",
-                       in_be32(priv->reg + TALITOS_MDEUISR),
-                       in_be32(priv->reg + TALITOS_MDEUISR_LO));
+                       in_be32(priv->reg_mdeu + TALITOS_EUISR),
+                       in_be32(priv->reg_mdeu + TALITOS_EUISR_LO));
                break;
        case DESC_HDR_SEL1_CRCU:
                dev_err(dev, "CRCUISR 0x%08x_%08x\n",
-                       in_be32(priv->reg + TALITOS_CRCUISR),
-                       in_be32(priv->reg + TALITOS_CRCUISR_LO));
+                       in_be32(priv->reg_crcu + TALITOS_EUISR),
+                       in_be32(priv->reg_crcu + TALITOS_EUISR_LO));
                break;
        }
 
@@ -445,17 +542,24 @@ static void talitos_error(struct device *dev, u32 isr, u32 isr_lo)
 {
        struct talitos_private *priv = dev_get_drvdata(dev);
        unsigned int timeout = TALITOS_TIMEOUT;
-       int ch, error, reset_dev = 0, reset_ch = 0;
-       u32 v, v_lo;
+       int ch, error, reset_dev = 0;
+       u32 v_lo;
+       bool is_sec1 = has_ftr_sec1(priv);
+       int reset_ch = is_sec1 ? 1 : 0; /* only SEC2 supports continuation */
 
        for (ch = 0; ch < priv->num_channels; ch++) {
                /* skip channels without errors */
-               if (!(isr & (1 << (ch * 2 + 1))))
-                       continue;
+               if (is_sec1) {
+                       /* bits 29, 31, 17, 19 */
+                       if (!(isr & (1 << (29 + (ch & 1) * 2 - (ch & 2) * 6))))
+                               continue;
+               } else {
+                       if (!(isr & (1 << (ch * 2 + 1))))
+                               continue;
+               }
 
                error = -EINVAL;
 
-               v = in_be32(priv->chan[ch].reg + TALITOS_CCPSR);
                v_lo = in_be32(priv->chan[ch].reg + TALITOS_CCPSR_LO);
 
                if (v_lo & TALITOS_CCPSR_LO_DOF) {
@@ -471,23 +575,28 @@ static void talitos_error(struct device *dev, u32 isr, u32 isr_lo)
                if (v_lo & TALITOS_CCPSR_LO_MDTE)
                        dev_err(dev, "master data transfer error\n");
                if (v_lo & TALITOS_CCPSR_LO_SGDLZ)
-                       dev_err(dev, "s/g data length zero error\n");
+                       dev_err(dev, is_sec1 ? "pointeur not complete error\n"
+                                            : "s/g data length zero error\n");
                if (v_lo & TALITOS_CCPSR_LO_FPZ)
-                       dev_err(dev, "fetch pointer zero error\n");
+                       dev_err(dev, is_sec1 ? "parity error\n"
+                                            : "fetch pointer zero error\n");
                if (v_lo & TALITOS_CCPSR_LO_IDH)
                        dev_err(dev, "illegal descriptor header error\n");
                if (v_lo & TALITOS_CCPSR_LO_IEU)
-                       dev_err(dev, "invalid execution unit error\n");
+                       dev_err(dev, is_sec1 ? "static assignment error\n"
+                                            : "invalid exec unit error\n");
                if (v_lo & TALITOS_CCPSR_LO_EU)
                        report_eu_error(dev, ch, current_desc_hdr(dev, ch));
-               if (v_lo & TALITOS_CCPSR_LO_GB)
-                       dev_err(dev, "gather boundary error\n");
-               if (v_lo & TALITOS_CCPSR_LO_GRL)
-                       dev_err(dev, "gather return/length error\n");
-               if (v_lo & TALITOS_CCPSR_LO_SB)
-                       dev_err(dev, "scatter boundary error\n");
-               if (v_lo & TALITOS_CCPSR_LO_SRL)
-                       dev_err(dev, "scatter return/length error\n");
+               if (!is_sec1) {
+                       if (v_lo & TALITOS_CCPSR_LO_GB)
+                               dev_err(dev, "gather boundary error\n");
+                       if (v_lo & TALITOS_CCPSR_LO_GRL)
+                               dev_err(dev, "gather return/length error\n");
+                       if (v_lo & TALITOS_CCPSR_LO_SB)
+                               dev_err(dev, "scatter boundary error\n");
+                       if (v_lo & TALITOS_CCPSR_LO_SRL)
+                               dev_err(dev, "scatter return/length error\n");
+               }
 
                flush_channel(dev, ch, error, reset_ch);
 
@@ -495,10 +604,10 @@ static void talitos_error(struct device *dev, u32 isr, u32 isr_lo)
                        reset_channel(dev, ch);
                } else {
                        setbits32(priv->chan[ch].reg + TALITOS_CCCR,
-                                 TALITOS_CCCR_CONT);
+                                 TALITOS2_CCCR_CONT);
                        setbits32(priv->chan[ch].reg + TALITOS_CCCR_LO, 0);
                        while ((in_be32(priv->chan[ch].reg + TALITOS_CCCR) &
-                              TALITOS_CCCR_CONT) && --timeout)
+                              TALITOS2_CCCR_CONT) && --timeout)
                                cpu_relax();
                        if (timeout == 0) {
                                dev_err(dev, "failed to restart channel %d\n",
@@ -507,9 +616,14 @@ static void talitos_error(struct device *dev, u32 isr, u32 isr_lo)
                        }
                }
        }
-       if (reset_dev || isr & ~TALITOS_ISR_4CHERR || isr_lo) {
-               dev_err(dev, "done overflow, internal time out, or rngu error: "
-                       "ISR 0x%08x_%08x\n", isr, isr_lo);
+       if (reset_dev || (is_sec1 && isr & ~TALITOS1_ISR_4CHERR) ||
+           (!is_sec1 && isr & ~TALITOS2_ISR_4CHERR) || isr_lo) {
+               if (is_sec1 && (isr_lo & TALITOS1_ISR_TEA_ERR))
+                       dev_err(dev, "TEA error: ISR 0x%08x_%08x\n",
+                               isr, isr_lo);
+               else
+                       dev_err(dev, "done overflow, internal time out, or "
+                               "rngu error: ISR 0x%08x_%08x\n", isr, isr_lo);
 
                /* purge request queues */
                for (ch = 0; ch < priv->num_channels; ch++)
@@ -520,8 +634,43 @@ static void talitos_error(struct device *dev, u32 isr, u32 isr_lo)
        }
 }
 
-#define DEF_TALITOS_INTERRUPT(name, ch_done_mask, ch_err_mask, tlet)          \
-static irqreturn_t talitos_interrupt_##name(int irq, void *data)              \
+#define DEF_TALITOS1_INTERRUPT(name, ch_done_mask, ch_err_mask, tlet)         \
+static irqreturn_t talitos1_interrupt_##name(int irq, void *data)             \
+{                                                                             \
+       struct device *dev = data;                                             \
+       struct talitos_private *priv = dev_get_drvdata(dev);                   \
+       u32 isr, isr_lo;                                                       \
+       unsigned long flags;                                                   \
+                                                                              \
+       spin_lock_irqsave(&priv->reg_lock, flags);                             \
+       isr = in_be32(priv->reg + TALITOS_ISR);                                \
+       isr_lo = in_be32(priv->reg + TALITOS_ISR_LO);                          \
+       /* Acknowledge interrupt */                                            \
+       out_be32(priv->reg + TALITOS_ICR, isr & (ch_done_mask | ch_err_mask)); \
+       out_be32(priv->reg + TALITOS_ICR_LO, isr_lo);                          \
+                                                                              \
+       if (unlikely(isr & ch_err_mask || isr_lo & TALITOS1_IMR_LO_INIT)) {    \
+               spin_unlock_irqrestore(&priv->reg_lock, flags);                \
+               talitos_error(dev, isr & ch_err_mask, isr_lo);                 \
+       }                                                                      \
+       else {                                                                 \
+               if (likely(isr & ch_done_mask)) {                              \
+                       /* mask further done interrupts. */                    \
+                       setbits32(priv->reg + TALITOS_IMR, ch_done_mask);      \
+                       /* done_task will unmask done interrupts at exit */    \
+                       tasklet_schedule(&priv->done_task[tlet]);              \
+               }                                                              \
+               spin_unlock_irqrestore(&priv->reg_lock, flags);                \
+       }                                                                      \
+                                                                              \
+       return (isr & (ch_done_mask | ch_err_mask) || isr_lo) ? IRQ_HANDLED :  \
+                                                               IRQ_NONE;      \
+}
+
+DEF_TALITOS1_INTERRUPT(4ch, TALITOS1_ISR_4CHDONE, TALITOS1_ISR_4CHERR, 0)
+
+#define DEF_TALITOS2_INTERRUPT(name, ch_done_mask, ch_err_mask, tlet)         \
+static irqreturn_t talitos2_interrupt_##name(int irq, void *data)             \
 {                                                                             \
        struct device *dev = data;                                             \
        struct talitos_private *priv = dev_get_drvdata(dev);                   \
@@ -552,9 +701,12 @@ static irqreturn_t talitos_interrupt_##name(int irq, void *data)          \
        return (isr & (ch_done_mask | ch_err_mask) || isr_lo) ? IRQ_HANDLED :  \
                                                                IRQ_NONE;      \
 }
-DEF_TALITOS_INTERRUPT(4ch, TALITOS_ISR_4CHDONE, TALITOS_ISR_4CHERR, 0)
-DEF_TALITOS_INTERRUPT(ch0_2, TALITOS_ISR_CH_0_2_DONE, TALITOS_ISR_CH_0_2_ERR, 0)
-DEF_TALITOS_INTERRUPT(ch1_3, TALITOS_ISR_CH_1_3_DONE, TALITOS_ISR_CH_1_3_ERR, 1)
+
+DEF_TALITOS2_INTERRUPT(4ch, TALITOS2_ISR_4CHDONE, TALITOS2_ISR_4CHERR, 0)
+DEF_TALITOS2_INTERRUPT(ch0_2, TALITOS2_ISR_CH_0_2_DONE, TALITOS2_ISR_CH_0_2_ERR,
+                      0)
+DEF_TALITOS2_INTERRUPT(ch1_3, TALITOS2_ISR_CH_1_3_DONE, TALITOS2_ISR_CH_1_3_ERR,
+                      1)
 
 /*
  * hwrng
@@ -567,7 +719,7 @@ static int talitos_rng_data_present(struct hwrng *rng, int wait)
        int i;
 
        for (i = 0; i < 20; i++) {
-               ofl = in_be32(priv->reg + TALITOS_RNGUSR_LO) &
+               ofl = in_be32(priv->reg_rngu + TALITOS_EUSR_LO) &
                      TALITOS_RNGUSR_LO_OFL;
                if (ofl || !wait)
                        break;
@@ -583,8 +735,8 @@ static int talitos_rng_data_read(struct hwrng *rng, u32 *data)
        struct talitos_private *priv = dev_get_drvdata(dev);
 
        /* rng fifo requires 64-bit accesses */
-       *data = in_be32(priv->reg + TALITOS_RNGU_FIFO);
-       *data = in_be32(priv->reg + TALITOS_RNGU_FIFO_LO);
+       *data = in_be32(priv->reg_rngu + TALITOS_EU_FIFO);
+       *data = in_be32(priv->reg_rngu + TALITOS_EU_FIFO_LO);
 
        return sizeof(u32);
 }
@@ -595,8 +747,9 @@ static int talitos_rng_init(struct hwrng *rng)
        struct talitos_private *priv = dev_get_drvdata(dev);
        unsigned int timeout = TALITOS_TIMEOUT;
 
-       setbits32(priv->reg + TALITOS_RNGURCR_LO, TALITOS_RNGURCR_LO_SR);
-       while (!(in_be32(priv->reg + TALITOS_RNGUSR_LO) & TALITOS_RNGUSR_LO_RD)
+       setbits32(priv->reg_rngu + TALITOS_EURCR_LO, TALITOS_RNGURCR_LO_SR);
+       while (!(in_be32(priv->reg_rngu + TALITOS_EUSR_LO)
+                & TALITOS_RNGUSR_LO_RD)
               && --timeout)
                cpu_relax();
        if (timeout == 0) {
@@ -605,7 +758,7 @@ static int talitos_rng_init(struct hwrng *rng)
        }
 
        /* start generating */
-       setbits32(priv->reg + TALITOS_RNGUDSR_LO, 0);
+       setbits32(priv->reg_rngu + TALITOS_EUDSR_LO, 0);
 
        return 0;
 }
@@ -661,7 +814,7 @@ struct talitos_ahash_req_ctx {
        unsigned int first;
        unsigned int last;
        unsigned int to_hash_later;
-       u64 nbuf;
+       unsigned int nbuf;
        struct scatterlist bufsl[2];
        struct scatterlist *psrc;
 };
@@ -712,9 +865,10 @@ badkey:
  * @dst_chained: whether dst is chained or not
  * @iv_dma: dma address of iv for checking continuity and link table
  * @dma_len: length of dma mapped link_tbl space
- * @dma_link_tbl: bus physical address of link_tbl
+ * @dma_link_tbl: bus physical address of link_tbl/buf
  * @desc: h/w descriptor
- * @link_tbl: input and output h/w link tables (if {src,dst}_nents > 1)
+ * @link_tbl: input and output h/w link tables (if {src,dst}_nents > 1) (SEC2)
+ * @buf: input and output buffeur (if {src,dst}_nents > 1) (SEC1)
  *
  * if decrypting (with authcheck), or either one of src_nents or dst_nents
  * is greater than 1, an integrity check value is concatenated to the end
@@ -731,7 +885,10 @@ struct talitos_edesc {
        int dma_len;
        dma_addr_t dma_link_tbl;
        struct talitos_desc desc;
-       struct talitos_ptr link_tbl[0];
+       union {
+               struct talitos_ptr link_tbl[0];
+               u8 buf[0];
+       };
 };
 
 static int talitos_map_sg(struct device *dev, struct scatterlist *sg,
@@ -907,8 +1064,8 @@ static int sg_to_link_tbl(struct scatterlist *sg, int sg_count,
 {
        int n_sg = sg_count;
 
-       while (n_sg--) {
-               to_talitos_ptr(link_tbl_ptr, sg_dma_address(sg));
+       while (sg && n_sg--) {
+               to_talitos_ptr(link_tbl_ptr, sg_dma_address(sg), 0);
                link_tbl_ptr->len = cpu_to_be16(sg_dma_len(sg));
                link_tbl_ptr->j_extent = 0;
                link_tbl_ptr++;
@@ -925,7 +1082,8 @@ static int sg_to_link_tbl(struct scatterlist *sg, int sg_count,
                sg_count--;
                link_tbl_ptr--;
        }
-       be16_add_cpu(&link_tbl_ptr->len, cryptlen);
+       link_tbl_ptr->len = cpu_to_be16(be16_to_cpu(link_tbl_ptr->len)
+                                       + cryptlen);
 
        /* tag end of link table */
        link_tbl_ptr->j_extent = DESC_PTR_LNKTBL_RETURN;
@@ -953,7 +1111,7 @@ static int ipsec_esp(struct talitos_edesc *edesc, struct aead_request *areq,
 
        /* hmac key */
        map_single_talitos_ptr(dev, &desc->ptr[0], ctx->authkeylen, &ctx->key,
-                              0, DMA_TO_DEVICE);
+                              DMA_TO_DEVICE);
 
        /* hmac data */
        desc->ptr[1].len = cpu_to_be16(areq->assoclen + ivsize);
@@ -962,7 +1120,7 @@ static int ipsec_esp(struct talitos_edesc *edesc, struct aead_request *areq,
                struct talitos_ptr *tbl_ptr = &edesc->link_tbl[tbl_off];
 
                to_talitos_ptr(&desc->ptr[1], edesc->dma_link_tbl + tbl_off *
-                              sizeof(struct talitos_ptr));
+                              sizeof(struct talitos_ptr), 0);
                desc->ptr[1].j_extent = DESC_PTR_LNKTBL_JUMP;
 
                /* assoc_nents - 1 entries for assoc, 1 for IV */
@@ -973,7 +1131,7 @@ static int ipsec_esp(struct talitos_edesc *edesc, struct aead_request *areq,
                tbl_ptr += sg_count - 1;
                tbl_ptr->j_extent = 0;
                tbl_ptr++;
-               to_talitos_ptr(tbl_ptr, edesc->iv_dma);
+               to_talitos_ptr(tbl_ptr, edesc->iv_dma, 0);
                tbl_ptr->len = cpu_to_be16(ivsize);
                tbl_ptr->j_extent = DESC_PTR_LNKTBL_RETURN;
 
@@ -982,14 +1140,14 @@ static int ipsec_esp(struct talitos_edesc *edesc, struct aead_request *areq,
        } else {
                if (areq->assoclen)
                        to_talitos_ptr(&desc->ptr[1],
-                                      sg_dma_address(areq->assoc));
+                                      sg_dma_address(areq->assoc), 0);
                else
-                       to_talitos_ptr(&desc->ptr[1], edesc->iv_dma);
+                       to_talitos_ptr(&desc->ptr[1], edesc->iv_dma, 0);
                desc->ptr[1].j_extent = 0;
        }
 
        /* cipher iv */
-       to_talitos_ptr(&desc->ptr[2], edesc->iv_dma);
+       to_talitos_ptr(&desc->ptr[2], edesc->iv_dma, 0);
        desc->ptr[2].len = cpu_to_be16(ivsize);
        desc->ptr[2].j_extent = 0;
        /* Sync needed for the aead_givencrypt case */
@@ -997,7 +1155,7 @@ static int ipsec_esp(struct talitos_edesc *edesc, struct aead_request *areq,
 
        /* cipher key */
        map_single_talitos_ptr(dev, &desc->ptr[3], ctx->enckeylen,
-                              (char *)&ctx->key + ctx->authkeylen, 0,
+                              (char *)&ctx->key + ctx->authkeylen,
                               DMA_TO_DEVICE);
 
        /*
@@ -1015,7 +1173,7 @@ static int ipsec_esp(struct talitos_edesc *edesc, struct aead_request *areq,
                                  edesc->src_chained);
 
        if (sg_count == 1) {
-               to_talitos_ptr(&desc->ptr[4], sg_dma_address(areq->src));
+               to_talitos_ptr(&desc->ptr[4], sg_dma_address(areq->src), 0);
        } else {
                sg_link_tbl_len = cryptlen;
 
@@ -1026,14 +1184,14 @@ static int ipsec_esp(struct talitos_edesc *edesc, struct aead_request *areq,
                                          &edesc->link_tbl[0]);
                if (sg_count > 1) {
                        desc->ptr[4].j_extent |= DESC_PTR_LNKTBL_JUMP;
-                       to_talitos_ptr(&desc->ptr[4], edesc->dma_link_tbl);
+                       to_talitos_ptr(&desc->ptr[4], edesc->dma_link_tbl, 0);
                        dma_sync_single_for_device(dev, edesc->dma_link_tbl,
                                                   edesc->dma_len,
                                                   DMA_BIDIRECTIONAL);
                } else {
                        /* Only one segment now, so no link tbl needed */
                        to_talitos_ptr(&desc->ptr[4],
-                                      sg_dma_address(areq->src));
+                                      sg_dma_address(areq->src), 0);
                }
        }
 
@@ -1047,13 +1205,13 @@ static int ipsec_esp(struct talitos_edesc *edesc, struct aead_request *areq,
                                          DMA_FROM_DEVICE, edesc->dst_chained);
 
        if (sg_count == 1) {
-               to_talitos_ptr(&desc->ptr[5], sg_dma_address(areq->dst));
+               to_talitos_ptr(&desc->ptr[5], sg_dma_address(areq->dst), 0);
        } else {
                int tbl_off = edesc->src_nents + 1;
                struct talitos_ptr *tbl_ptr = &edesc->link_tbl[tbl_off];
 
                to_talitos_ptr(&desc->ptr[5], edesc->dma_link_tbl +
-                              tbl_off * sizeof(struct talitos_ptr));
+                              tbl_off * sizeof(struct talitos_ptr), 0);
                sg_count = sg_to_link_tbl(areq->dst, sg_count, cryptlen,
                                          tbl_ptr);
 
@@ -1068,14 +1226,14 @@ static int ipsec_esp(struct talitos_edesc *edesc, struct aead_request *areq,
                to_talitos_ptr(tbl_ptr, edesc->dma_link_tbl +
                               (tbl_off + edesc->dst_nents + 1 +
                                edesc->assoc_nents) *
-                              sizeof(struct talitos_ptr));
+                              sizeof(struct talitos_ptr), 0);
                desc->ptr[5].j_extent |= DESC_PTR_LNKTBL_JUMP;
                dma_sync_single_for_device(ctx->dev, edesc->dma_link_tbl,
                                           edesc->dma_len, DMA_BIDIRECTIONAL);
        }
 
        /* iv out */
-       map_single_talitos_ptr(dev, &desc->ptr[6], ivsize, ctx->iv, 0,
+       map_single_talitos_ptr(dev, &desc->ptr[6], ivsize, ctx->iv,
                               DMA_FROM_DEVICE);
 
        ret = talitos_submit(dev, ctx->ch, desc, callback, areq);
@@ -1095,7 +1253,7 @@ static int sg_count(struct scatterlist *sg_list, int nbytes, bool *chained)
        int sg_nents = 0;
 
        *chained = false;
-       while (nbytes > 0) {
+       while (nbytes > 0 && sg) {
                sg_nents++;
                nbytes -= sg->length;
                if (!sg_is_last(sg) && (sg + 1)->length == 0)
@@ -1128,8 +1286,11 @@ static struct talitos_edesc *talitos_edesc_alloc(struct device *dev,
        dma_addr_t iv_dma = 0;
        gfp_t flags = cryptoflags & CRYPTO_TFM_REQ_MAY_SLEEP ? GFP_KERNEL :
                      GFP_ATOMIC;
+       struct talitos_private *priv = dev_get_drvdata(dev);
+       bool is_sec1 = has_ftr_sec1(priv);
+       int max_len = is_sec1 ? TALITOS1_MAX_DATA_LEN : TALITOS2_MAX_DATA_LEN;
 
-       if (cryptlen + authsize > TALITOS_MAX_DATA_LEN) {
+       if (cryptlen + authsize > max_len) {
                dev_err(dev, "length exceeds h/w max limit\n");
                return ERR_PTR(-EINVAL);
        }
@@ -1173,8 +1334,12 @@ static struct talitos_edesc *talitos_edesc_alloc(struct device *dev,
         */
        alloc_len = sizeof(struct talitos_edesc);
        if (assoc_nents || src_nents || dst_nents) {
-               dma_len = (src_nents + dst_nents + 2 + assoc_nents) *
-                         sizeof(struct talitos_ptr) + authsize;
+               if (is_sec1)
+                       dma_len = (src_nents ? cryptlen : 0) +
+                                 (dst_nents ? cryptlen : 0);
+               else
+                       dma_len = (src_nents + dst_nents + 2 + assoc_nents) *
+                                 sizeof(struct talitos_ptr) + authsize;
                alloc_len += dma_len;
        } else {
                dma_len = 0;
@@ -1327,16 +1492,43 @@ static int ablkcipher_setkey(struct crypto_ablkcipher *cipher,
        return 0;
 }
 
+static void unmap_sg_talitos_ptr(struct device *dev, struct scatterlist *src,
+                                struct scatterlist *dst, unsigned int len,
+                                struct talitos_edesc *edesc)
+{
+       struct talitos_private *priv = dev_get_drvdata(dev);
+       bool is_sec1 = has_ftr_sec1(priv);
+
+       if (is_sec1) {
+               if (!edesc->src_nents) {
+                       dma_unmap_sg(dev, src, 1,
+                                    dst != src ? DMA_TO_DEVICE
+                                               : DMA_BIDIRECTIONAL);
+               }
+               if (dst && edesc->dst_nents) {
+                       dma_sync_single_for_device(dev,
+                                                  edesc->dma_link_tbl + len,
+                                                  len, DMA_FROM_DEVICE);
+                       sg_copy_from_buffer(dst, edesc->dst_nents ? : 1,
+                                           edesc->buf + len, len);
+               } else if (dst && dst != src) {
+                       dma_unmap_sg(dev, dst, 1, DMA_FROM_DEVICE);
+               }
+       } else {
+               talitos_sg_unmap(dev, edesc, src, dst);
+       }
+}
+
 static void common_nonsnoop_unmap(struct device *dev,
                                  struct talitos_edesc *edesc,
                                  struct ablkcipher_request *areq)
 {
        unmap_single_talitos_ptr(dev, &edesc->desc.ptr[5], DMA_FROM_DEVICE);
+
+       unmap_sg_talitos_ptr(dev, areq->src, areq->dst, areq->nbytes, edesc);
        unmap_single_talitos_ptr(dev, &edesc->desc.ptr[2], DMA_TO_DEVICE);
        unmap_single_talitos_ptr(dev, &edesc->desc.ptr[1], DMA_TO_DEVICE);
 
-       talitos_sg_unmap(dev, edesc, areq->src, areq->dst);
-
        if (edesc->dma_len)
                dma_unmap_single(dev, edesc->dma_link_tbl, edesc->dma_len,
                                 DMA_BIDIRECTIONAL);
@@ -1358,6 +1550,102 @@ static void ablkcipher_done(struct device *dev,
        areq->base.complete(&areq->base, err);
 }
 
+int map_sg_in_talitos_ptr(struct device *dev, struct scatterlist *src,
+                         unsigned int len, struct talitos_edesc *edesc,
+                         enum dma_data_direction dir, struct talitos_ptr *ptr)
+{
+       int sg_count;
+       struct talitos_private *priv = dev_get_drvdata(dev);
+       bool is_sec1 = has_ftr_sec1(priv);
+
+       to_talitos_ptr_len(ptr, len, is_sec1);
+
+       if (is_sec1) {
+               sg_count = edesc->src_nents ? : 1;
+
+               if (sg_count == 1) {
+                       dma_map_sg(dev, src, 1, dir);
+                       to_talitos_ptr(ptr, sg_dma_address(src), is_sec1);
+               } else {
+                       sg_copy_to_buffer(src, sg_count, edesc->buf, len);
+                       to_talitos_ptr(ptr, edesc->dma_link_tbl, is_sec1);
+                       dma_sync_single_for_device(dev, edesc->dma_link_tbl,
+                                                  len, DMA_TO_DEVICE);
+               }
+       } else {
+               to_talitos_ptr_extent_clear(ptr, is_sec1);
+
+               sg_count = talitos_map_sg(dev, src, edesc->src_nents ? : 1, dir,
+                                         edesc->src_chained);
+
+               if (sg_count == 1) {
+                       to_talitos_ptr(ptr, sg_dma_address(src), is_sec1);
+               } else {
+                       sg_count = sg_to_link_tbl(src, sg_count, len,
+                                                 &edesc->link_tbl[0]);
+                       if (sg_count > 1) {
+                               to_talitos_ptr(ptr, edesc->dma_link_tbl, 0);
+                               ptr->j_extent |= DESC_PTR_LNKTBL_JUMP;
+                               dma_sync_single_for_device(dev,
+                                                          edesc->dma_link_tbl,
+                                                          edesc->dma_len,
+                                                          DMA_BIDIRECTIONAL);
+                       } else {
+                               /* Only one segment now, so no link tbl needed*/
+                               to_talitos_ptr(ptr, sg_dma_address(src),
+                                              is_sec1);
+                       }
+               }
+       }
+       return sg_count;
+}
+
+void map_sg_out_talitos_ptr(struct device *dev, struct scatterlist *dst,
+                           unsigned int len, struct talitos_edesc *edesc,
+                           enum dma_data_direction dir,
+                           struct talitos_ptr *ptr, int sg_count)
+{
+       struct talitos_private *priv = dev_get_drvdata(dev);
+       bool is_sec1 = has_ftr_sec1(priv);
+
+       if (dir != DMA_NONE)
+               sg_count = talitos_map_sg(dev, dst, edesc->dst_nents ? : 1,
+                                         dir, edesc->dst_chained);
+
+       to_talitos_ptr_len(ptr, len, is_sec1);
+
+       if (is_sec1) {
+               if (sg_count == 1) {
+                       if (dir != DMA_NONE)
+                               dma_map_sg(dev, dst, 1, dir);
+                       to_talitos_ptr(ptr, sg_dma_address(dst), is_sec1);
+               } else {
+                       to_talitos_ptr(ptr, edesc->dma_link_tbl + len, is_sec1);
+                       dma_sync_single_for_device(dev,
+                                                  edesc->dma_link_tbl + len,
+                                                  len, DMA_FROM_DEVICE);
+               }
+       } else {
+               to_talitos_ptr_extent_clear(ptr, is_sec1);
+
+               if (sg_count == 1) {
+                       to_talitos_ptr(ptr, sg_dma_address(dst), is_sec1);
+               } else {
+                       struct talitos_ptr *link_tbl_ptr =
+                               &edesc->link_tbl[edesc->src_nents + 1];
+
+                       to_talitos_ptr(ptr, edesc->dma_link_tbl +
+                                           (edesc->src_nents + 1) *
+                                            sizeof(struct talitos_ptr), 0);
+                       ptr->j_extent |= DESC_PTR_LNKTBL_JUMP;
+                       sg_to_link_tbl(dst, sg_count, len, link_tbl_ptr);
+                       dma_sync_single_for_device(dev, edesc->dma_link_tbl,
+                                                  edesc->dma_len,
+                                                  DMA_BIDIRECTIONAL);
+               }
+       }
+}
+
 static int common_nonsnoop(struct talitos_edesc *edesc,
                           struct ablkcipher_request *areq,
                           void (*callback) (struct device *dev,
@@ -1371,83 +1659,41 @@ static int common_nonsnoop(struct talitos_edesc *edesc,
        unsigned int cryptlen = areq->nbytes;
        unsigned int ivsize = crypto_ablkcipher_ivsize(cipher);
        int sg_count, ret;
+       struct talitos_private *priv = dev_get_drvdata(dev);
+       bool is_sec1 = has_ftr_sec1(priv);
 
        /* first DWORD empty */
-       desc->ptr[0].len = 0;
-       to_talitos_ptr(&desc->ptr[0], 0);
-       desc->ptr[0].j_extent = 0;
+       desc->ptr[0] = zero_entry;
 
        /* cipher iv */
-       to_talitos_ptr(&desc->ptr[1], edesc->iv_dma);
-       desc->ptr[1].len = cpu_to_be16(ivsize);
-       desc->ptr[1].j_extent = 0;
+       to_talitos_ptr(&desc->ptr[1], edesc->iv_dma, is_sec1);
+       to_talitos_ptr_len(&desc->ptr[1], ivsize, is_sec1);
+       to_talitos_ptr_extent_clear(&desc->ptr[1], is_sec1);
 
        /* cipher key */
        map_single_talitos_ptr(dev, &desc->ptr[2], ctx->keylen,
-                              (char *)&ctx->key, 0, DMA_TO_DEVICE);
+                              (char *)&ctx->key, DMA_TO_DEVICE);
 
        /*
         * cipher in
         */
-       desc->ptr[3].len = cpu_to_be16(cryptlen);
-       desc->ptr[3].j_extent = 0;
-
-       sg_count = talitos_map_sg(dev, areq->src, edesc->src_nents ? : 1,
-                                 (areq->src == areq->dst) ? DMA_BIDIRECTIONAL
-                                                          : DMA_TO_DEVICE,
-                                 edesc->src_chained);
-
-       if (sg_count == 1) {
-               to_talitos_ptr(&desc->ptr[3], sg_dma_address(areq->src));
-       } else {
-               sg_count = sg_to_link_tbl(areq->src, sg_count, cryptlen,
-                                         &edesc->link_tbl[0]);
-               if (sg_count > 1) {
-                       to_talitos_ptr(&desc->ptr[3], edesc->dma_link_tbl);
-                       desc->ptr[3].j_extent |= DESC_PTR_LNKTBL_JUMP;
-                       dma_sync_single_for_device(dev, edesc->dma_link_tbl,
-                                                  edesc->dma_len,
-                                                  DMA_BIDIRECTIONAL);
-               } else {
-                       /* Only one segment now, so no link tbl needed */
-                       to_talitos_ptr(&desc->ptr[3],
-                                      sg_dma_address(areq->src));
-               }
-       }
+       sg_count = map_sg_in_talitos_ptr(dev, areq->src, cryptlen, edesc,
+                                        (areq->src == areq->dst) ?
+                                         DMA_BIDIRECTIONAL : DMA_TO_DEVICE,
+                                         &desc->ptr[3]);
 
        /* cipher out */
-       desc->ptr[4].len = cpu_to_be16(cryptlen);
-       desc->ptr[4].j_extent = 0;
-
-       if (areq->src != areq->dst)
-               sg_count = talitos_map_sg(dev, areq->dst,
-                                         edesc->dst_nents ? : 1,
-                                         DMA_FROM_DEVICE, edesc->dst_chained);
-
-       if (sg_count == 1) {
-               to_talitos_ptr(&desc->ptr[4], sg_dma_address(areq->dst));
-       } else {
-               struct talitos_ptr *link_tbl_ptr =
-                       &edesc->link_tbl[edesc->src_nents + 1];
-
-               to_talitos_ptr(&desc->ptr[4], edesc->dma_link_tbl +
-                                             (edesc->src_nents + 1) *
-                                             sizeof(struct talitos_ptr));
-               desc->ptr[4].j_extent |= DESC_PTR_LNKTBL_JUMP;
-               sg_count = sg_to_link_tbl(areq->dst, sg_count, cryptlen,
-                                         link_tbl_ptr);
-               dma_sync_single_for_device(ctx->dev, edesc->dma_link_tbl,
-                                          edesc->dma_len, DMA_BIDIRECTIONAL);
-       }
+       map_sg_out_talitos_ptr(dev, areq->dst, cryptlen, edesc,
+                              (areq->src == areq->dst) ? DMA_NONE
+                                                       : DMA_FROM_DEVICE,
+                              &desc->ptr[4], sg_count);
 
        /* iv out */
-       map_single_talitos_ptr(dev, &desc->ptr[5], ivsize, ctx->iv, 0,
+       map_single_talitos_ptr(dev, &desc->ptr[5], ivsize, ctx->iv,
                               DMA_FROM_DEVICE);
 
        /* last DWORD empty */
-       desc->ptr[6].len = 0;
-       to_talitos_ptr(&desc->ptr[6], 0);
-       desc->ptr[6].j_extent = 0;
+       desc->ptr[6] = zero_entry;
 
        ret = talitos_submit(dev, ctx->ch, desc, callback, areq);
        if (ret != -EINPROGRESS) {
@@ -1507,20 +1753,22 @@ static void common_nonsnoop_hash_unmap(struct device *dev,
                                       struct ahash_request *areq)
 {
        struct talitos_ahash_req_ctx *req_ctx = ahash_request_ctx(areq);
+       struct talitos_private *priv = dev_get_drvdata(dev);
+       bool is_sec1 = has_ftr_sec1(priv);
 
        unmap_single_talitos_ptr(dev, &edesc->desc.ptr[5], DMA_FROM_DEVICE);
 
+       unmap_sg_talitos_ptr(dev, req_ctx->psrc, NULL, 0, edesc);
+
        /* When using hashctx-in, must unmap it. */
-       if (edesc->desc.ptr[1].len)
+       if (from_talitos_ptr_len(&edesc->desc.ptr[1], is_sec1))
                unmap_single_talitos_ptr(dev, &edesc->desc.ptr[1],
                                         DMA_TO_DEVICE);
 
-       if (edesc->desc.ptr[2].len)
+       if (from_talitos_ptr_len(&edesc->desc.ptr[2], is_sec1))
                unmap_single_talitos_ptr(dev, &edesc->desc.ptr[2],
                                         DMA_TO_DEVICE);
 
-       talitos_sg_unmap(dev, edesc, req_ctx->psrc, NULL);
-
        if (edesc->dma_len)
                dma_unmap_single(dev, edesc->dma_link_tbl, edesc->dma_len,
                                 DMA_BIDIRECTIONAL);
@@ -1548,6 +1796,27 @@ static void ahash_done(struct device *dev,
        areq->base.complete(&areq->base, err);
 }
 
+/*
+ * SEC1 doesn't like hashing of 0 sized message, so we do the padding
+ * ourself and submit a padded block
+ */
+void talitos_handle_buggy_hash(struct talitos_ctx *ctx,
+                              struct talitos_edesc *edesc,
+                              struct talitos_ptr *ptr)
+{
+       static u8 padded_hash[64] = {
+               0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+               0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+               0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+               0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+       };
+
+       pr_err_once("Bug in SEC1, padding ourself\n");
+       edesc->desc.hdr &= ~DESC_HDR_MODE0_MDEU_PAD;
+       map_single_talitos_ptr(ctx->dev, ptr, sizeof(padded_hash),
+                              (char *)padded_hash, DMA_TO_DEVICE);
+}
+
 static int common_nonsnoop_hash(struct talitos_edesc *edesc,
                                struct ahash_request *areq, unsigned int length,
                                void (*callback) (struct device *dev,
@@ -1559,7 +1828,9 @@ static int common_nonsnoop_hash(struct talitos_edesc *edesc,
        struct talitos_ahash_req_ctx *req_ctx = ahash_request_ctx(areq);
        struct device *dev = ctx->dev;
        struct talitos_desc *desc = &edesc->desc;
-       int sg_count, ret;
+       int ret;
+       struct talitos_private *priv = dev_get_drvdata(dev);
+       bool is_sec1 = has_ftr_sec1(priv);
 
        /* first DWORD empty */
        desc->ptr[0] = zero_entry;
@@ -1568,7 +1839,7 @@ static int common_nonsnoop_hash(struct talitos_edesc *edesc,
        if (!req_ctx->first || req_ctx->swinit) {
                map_single_talitos_ptr(dev, &desc->ptr[1],
                                       req_ctx->hw_context_size,
-                                      (char *)req_ctx->hw_context, 0,
+                                      (char *)req_ctx->hw_context,
                                       DMA_TO_DEVICE);
                req_ctx->swinit = 0;
        } else {
@@ -1580,38 +1851,15 @@ static int common_nonsnoop_hash(struct talitos_edesc *edesc,
        /* HMAC key */
        if (ctx->keylen)
                map_single_talitos_ptr(dev, &desc->ptr[2], ctx->keylen,
-                                      (char *)&ctx->key, 0, DMA_TO_DEVICE);
+                                      (char *)&ctx->key, DMA_TO_DEVICE);
        else
                desc->ptr[2] = zero_entry;
 
        /*
         * data in
         */
-       desc->ptr[3].len = cpu_to_be16(length);
-       desc->ptr[3].j_extent = 0;
-
-       sg_count = talitos_map_sg(dev, req_ctx->psrc,
-                                 edesc->src_nents ? : 1,
-                                 DMA_TO_DEVICE, edesc->src_chained);
-
-       if (sg_count == 1) {
-               to_talitos_ptr(&desc->ptr[3], sg_dma_address(req_ctx->psrc));
-       } else {
-               sg_count = sg_to_link_tbl(req_ctx->psrc, sg_count, length,
-                                         &edesc->link_tbl[0]);
-               if (sg_count > 1) {
-                       desc->ptr[3].j_extent |= DESC_PTR_LNKTBL_JUMP;
-                       to_talitos_ptr(&desc->ptr[3], edesc->dma_link_tbl);
-                       dma_sync_single_for_device(ctx->dev,
-                                                  edesc->dma_link_tbl,
-                                                  edesc->dma_len,
-                                                  DMA_BIDIRECTIONAL);
-               } else {
-                       /* Only one segment now, so no link tbl needed */
-                       to_talitos_ptr(&desc->ptr[3],
-                                      sg_dma_address(req_ctx->psrc));
-               }
-       }
+       map_sg_in_talitos_ptr(dev, req_ctx->psrc, length, edesc,
+                             DMA_TO_DEVICE, &desc->ptr[3]);
 
        /* fifth DWORD empty */
        desc->ptr[4] = zero_entry;
@@ -1620,15 +1868,18 @@ static int common_nonsnoop_hash(struct talitos_edesc *edesc,
        if (req_ctx->last)
                map_single_talitos_ptr(dev, &desc->ptr[5],
                                       crypto_ahash_digestsize(tfm),
-                                      areq->result, 0, DMA_FROM_DEVICE);
+                                      areq->result, DMA_FROM_DEVICE);
        else
                map_single_talitos_ptr(dev, &desc->ptr[5],
                                       req_ctx->hw_context_size,
-                                      req_ctx->hw_context, 0, DMA_FROM_DEVICE);
+                                      req_ctx->hw_context, DMA_FROM_DEVICE);
 
        /* last DWORD empty */
        desc->ptr[6] = zero_entry;
 
+       if (is_sec1 && from_talitos_ptr_len(&desc->ptr[3], true) == 0)
+               talitos_handle_buggy_hash(ctx, edesc, &desc->ptr[3]);
+
        ret = talitos_submit(dev, ctx->ch, desc, callback, areq);
        if (ret != -EINPROGRESS) {
                common_nonsnoop_hash_unmap(dev, edesc, areq);
@@ -2561,6 +2812,7 @@ static struct talitos_crypto_alg *talitos_alg_alloc(struct device *dev,
                break;
        default:
                dev_err(dev, "unknown algorithm type %d\n", t_alg->algt.type);
+               kfree(t_alg);
                return ERR_PTR(-EINVAL);
        }
 
@@ -2581,29 +2833,35 @@ static int talitos_probe_irq(struct platform_device *ofdev)
        struct device_node *np = ofdev->dev.of_node;
        struct talitos_private *priv = dev_get_drvdata(dev);
        int err;
+       bool is_sec1 = has_ftr_sec1(priv);
 
        priv->irq[0] = irq_of_parse_and_map(np, 0);
        if (!priv->irq[0]) {
                dev_err(dev, "failed to map irq\n");
                return -EINVAL;
        }
+       if (is_sec1) {
+               err = request_irq(priv->irq[0], talitos1_interrupt_4ch, 0,
+                                 dev_driver_string(dev), dev);
+               goto primary_out;
+       }
 
        priv->irq[1] = irq_of_parse_and_map(np, 1);
 
        /* get the primary irq line */
        if (!priv->irq[1]) {
-               err = request_irq(priv->irq[0], talitos_interrupt_4ch, 0,
+               err = request_irq(priv->irq[0], talitos2_interrupt_4ch, 0,
                                  dev_driver_string(dev), dev);
                goto primary_out;
        }
 
-       err = request_irq(priv->irq[0], talitos_interrupt_ch0_2, 0,
+       err = request_irq(priv->irq[0], talitos2_interrupt_ch0_2, 0,
                          dev_driver_string(dev), dev);
        if (err)
                goto primary_out;
 
        /* get the secondary irq line */
-       err = request_irq(priv->irq[1], talitos_interrupt_ch1_3, 0,
+       err = request_irq(priv->irq[1], talitos2_interrupt_ch1_3, 0,
                          dev_driver_string(dev), dev);
        if (err) {
                dev_err(dev, "failed to request secondary irq\n");
@@ -2630,6 +2888,7 @@ static int talitos_probe(struct platform_device *ofdev)
        struct talitos_private *priv;
        const unsigned int *prop;
        int i, err;
+       int stride;
 
        priv = kzalloc(sizeof(struct talitos_private), GFP_KERNEL);
        if (!priv)
@@ -2643,20 +2902,6 @@ static int talitos_probe(struct platform_device *ofdev)
 
        spin_lock_init(&priv->reg_lock);
 
-       err = talitos_probe_irq(ofdev);
-       if (err)
-               goto err_out;
-
-       if (!priv->irq[1]) {
-               tasklet_init(&priv->done_task[0], talitos_done_4ch,
-                            (unsigned long)dev);
-       } else {
-               tasklet_init(&priv->done_task[0], talitos_done_ch0_2,
-                            (unsigned long)dev);
-               tasklet_init(&priv->done_task[1], talitos_done_ch1_3,
-                            (unsigned long)dev);
-       }
-
        priv->reg = of_iomap(np, 0);
        if (!priv->reg) {
                dev_err(dev, "failed to of_iomap\n");
@@ -2696,6 +2941,53 @@ static int talitos_probe(struct platform_device *ofdev)
                                  TALITOS_FTR_SHA224_HWINIT |
                                  TALITOS_FTR_HMAC_OK;
 
+       if (of_device_is_compatible(np, "fsl,sec1.0"))
+               priv->features |= TALITOS_FTR_SEC1;
+
+       if (of_device_is_compatible(np, "fsl,sec1.2")) {
+               priv->reg_deu = priv->reg + TALITOS12_DEU;
+               priv->reg_aesu = priv->reg + TALITOS12_AESU;
+               priv->reg_mdeu = priv->reg + TALITOS12_MDEU;
+               stride = TALITOS1_CH_STRIDE;
+       } else if (of_device_is_compatible(np, "fsl,sec1.0")) {
+               priv->reg_deu = priv->reg + TALITOS10_DEU;
+               priv->reg_aesu = priv->reg + TALITOS10_AESU;
+               priv->reg_mdeu = priv->reg + TALITOS10_MDEU;
+               priv->reg_afeu = priv->reg + TALITOS10_AFEU;
+               priv->reg_rngu = priv->reg + TALITOS10_RNGU;
+               priv->reg_pkeu = priv->reg + TALITOS10_PKEU;
+               stride = TALITOS1_CH_STRIDE;
+       } else {
+               priv->reg_deu = priv->reg + TALITOS2_DEU;
+               priv->reg_aesu = priv->reg + TALITOS2_AESU;
+               priv->reg_mdeu = priv->reg + TALITOS2_MDEU;
+               priv->reg_afeu = priv->reg + TALITOS2_AFEU;
+               priv->reg_rngu = priv->reg + TALITOS2_RNGU;
+               priv->reg_pkeu = priv->reg + TALITOS2_PKEU;
+               priv->reg_keu = priv->reg + TALITOS2_KEU;
+               priv->reg_crcu = priv->reg + TALITOS2_CRCU;
+               stride = TALITOS2_CH_STRIDE;
+       }
+
+       err = talitos_probe_irq(ofdev);
+       if (err)
+               goto err_out;
+
+       if (of_device_is_compatible(np, "fsl,sec1.0")) {
+               tasklet_init(&priv->done_task[0], talitos1_done_4ch,
+                            (unsigned long)dev);
+       } else {
+               if (!priv->irq[1]) {
+                       tasklet_init(&priv->done_task[0], talitos2_done_4ch,
+                                    (unsigned long)dev);
+               } else {
+                       tasklet_init(&priv->done_task[0], talitos2_done_ch0_2,
+                                    (unsigned long)dev);
+                       tasklet_init(&priv->done_task[1], talitos2_done_ch1_3,
+                                    (unsigned long)dev);
+               }
+       }
+
        priv->chan = kzalloc(sizeof(struct talitos_channel) *
                             priv->num_channels, GFP_KERNEL);
        if (!priv->chan) {
@@ -2707,7 +2999,7 @@ static int talitos_probe(struct platform_device *ofdev)
        priv->fifo_len = roundup_pow_of_two(priv->chfifo_len);
 
        for (i = 0; i < priv->num_channels; i++) {
-               priv->chan[i].reg = priv->reg + TALITOS_CH_STRIDE * (i + 1);
+               priv->chan[i].reg = priv->reg + stride * (i + 1);
                if (!priv->irq[1] || !(i & 1))
                        priv->chan[i].reg += TALITOS_CH_BASE_OFFSET;
 
@@ -2794,9 +3086,16 @@ err_out:
 }
 
 static const struct of_device_id talitos_match[] = {
+#ifdef CONFIG_CRYPTO_DEV_TALITOS1
+       {
+               .compatible = "fsl,sec1.0",
+       },
+#endif
+#ifdef CONFIG_CRYPTO_DEV_TALITOS2
        {
                .compatible = "fsl,sec2.0",
        },
+#endif
        {},
 };
 MODULE_DEVICE_TABLE(of, talitos_match);
index 61a14054aa39414664f637e10089ad4170a0651b..314daf55e7f77791d075065a427b531bb361cce1 100644 (file)
@@ -29,7 +29,8 @@
  */
 
 #define TALITOS_TIMEOUT 100000
-#define TALITOS_MAX_DATA_LEN 65535
+#define TALITOS1_MAX_DATA_LEN 32768
+#define TALITOS2_MAX_DATA_LEN 65535
 
 #define DESC_TYPE(desc_hdr) ((be32_to_cpu(desc_hdr) >> 3) & 0x1f)
 #define PRIMARY_EU(desc_hdr) ((be32_to_cpu(desc_hdr) >> 28) & 0xf)
 
 /* descriptor pointer entry */
 struct talitos_ptr {
-       __be16 len;     /* length */
-       u8 j_extent;    /* jump to sg link table and/or extent */
-       u8 eptr;        /* extended address */
+       union {
+               struct {                /* SEC2 format */
+                       __be16 len;     /* length */
+                       u8 j_extent;    /* jump to sg link table and/or extent*/
+                       u8 eptr;        /* extended address */
+               };
+               struct {                        /* SEC1 format */
+                       __be16 res;
+                       __be16 len1;    /* length */
+               };
+       };
        __be32 ptr;     /* address */
 };
 
@@ -53,10 +62,16 @@ static const struct talitos_ptr zero_entry = {
 /* descriptor */
 struct talitos_desc {
        __be32 hdr;                     /* header high bits */
-       __be32 hdr_lo;                  /* header low bits */
+       union {
+               __be32 hdr_lo;          /* header low bits */
+               __be32 hdr1;            /* header for SEC1 */
+       };
        struct talitos_ptr ptr[7];      /* ptr/len pair array */
+       __be32 next_desc;               /* next descriptor (SEC1) */
 };
 
+#define TALITOS_DESC_SIZE      (sizeof(struct talitos_desc) - sizeof(__be32))
+
 /**
  * talitos_request - descriptor submission request
  * @desc: descriptor pointer (kernel virtual)
@@ -97,6 +112,14 @@ struct talitos_private {
        struct device *dev;
        struct platform_device *ofdev;
        void __iomem *reg;
+       void __iomem *reg_deu;
+       void __iomem *reg_aesu;
+       void __iomem *reg_mdeu;
+       void __iomem *reg_afeu;
+       void __iomem *reg_rngu;
+       void __iomem *reg_pkeu;
+       void __iomem *reg_keu;
+       void __iomem *reg_crcu;
        int irq[2];
 
        /* SEC global registers lock  */
@@ -144,49 +167,80 @@ extern int talitos_submit(struct device *dev, int ch, struct talitos_desc *desc,
 #define TALITOS_FTR_HW_AUTH_CHECK 0x00000002
 #define TALITOS_FTR_SHA224_HWINIT 0x00000004
 #define TALITOS_FTR_HMAC_OK 0x00000008
+#define TALITOS_FTR_SEC1 0x00000010
+
+/*
+ * If both CONFIG_CRYPTO_DEV_TALITOS1 and CONFIG_CRYPTO_DEV_TALITOS2 are
+ * defined, we check the features which are set according to the device tree.
+ * Otherwise, we answer true or false directly
+ */
+static inline bool has_ftr_sec1(struct talitos_private *priv)
+{
+#if defined(CONFIG_CRYPTO_DEV_TALITOS1) && defined(CONFIG_CRYPTO_DEV_TALITOS2)
+       return priv->features & TALITOS_FTR_SEC1 ? true : false;
+#elif defined(CONFIG_CRYPTO_DEV_TALITOS1)
+       return true;
+#else
+       return false;
+#endif
+}
 
 /*
  * TALITOS_xxx_LO addresses point to the low data bits (32-63) of the register
  */
 
+#define ISR1_FORMAT(x)                 (((x) << 28) | ((x) << 16))
+#define ISR2_FORMAT(x)                 (((x) << 4) | (x))
+
 /* global register offset addresses */
 #define TALITOS_MCR                    0x1030  /* master control register */
 #define   TALITOS_MCR_RCA0             (1 << 15) /* remap channel 0 */
 #define   TALITOS_MCR_RCA1             (1 << 14) /* remap channel 1 */
 #define   TALITOS_MCR_RCA2             (1 << 13) /* remap channel 2 */
 #define   TALITOS_MCR_RCA3             (1 << 12) /* remap channel 3 */
-#define   TALITOS_MCR_SWR              0x1     /* s/w reset */
+#define   TALITOS1_MCR_SWR             0x1000000     /* s/w reset */
+#define   TALITOS2_MCR_SWR             0x1     /* s/w reset */
 #define TALITOS_MCR_LO                 0x1034
 #define TALITOS_IMR                    0x1008  /* interrupt mask register */
-#define   TALITOS_IMR_INIT             0x100ff /* enable channel IRQs */
-#define   TALITOS_IMR_DONE             0x00055 /* done IRQs */
+/* enable channel IRQs */
+#define   TALITOS1_IMR_INIT            ISR1_FORMAT(0xf)
+#define   TALITOS1_IMR_DONE            ISR1_FORMAT(0x5) /* done IRQs */
+/* enable channel IRQs */
+#define   TALITOS2_IMR_INIT            (ISR2_FORMAT(0xf) | 0x10000)
+#define   TALITOS2_IMR_DONE            ISR1_FORMAT(0x5) /* done IRQs */
 #define TALITOS_IMR_LO                 0x100C
-#define   TALITOS_IMR_LO_INIT          0x20000 /* allow RNGU error IRQs */
+#define   TALITOS1_IMR_LO_INIT         0x2000000 /* allow RNGU error IRQs */
+#define   TALITOS2_IMR_LO_INIT         0x20000 /* allow RNGU error IRQs */
 #define TALITOS_ISR                    0x1010  /* interrupt status register */
-#define   TALITOS_ISR_4CHERR           0xaa    /* 4 channel errors mask */
-#define   TALITOS_ISR_4CHDONE          0x55    /* 4 channel done mask */
-#define   TALITOS_ISR_CH_0_2_ERR       0x22    /* channels 0, 2 errors mask */
-#define   TALITOS_ISR_CH_0_2_DONE      0x11    /* channels 0, 2 done mask */
-#define   TALITOS_ISR_CH_1_3_ERR       0x88    /* channels 1, 3 errors mask */
-#define   TALITOS_ISR_CH_1_3_DONE      0x44    /* channels 1, 3 done mask */
+#define   TALITOS1_ISR_4CHERR          ISR1_FORMAT(0xa) /* 4 ch errors mask */
+#define   TALITOS1_ISR_4CHDONE         ISR1_FORMAT(0x5) /* 4 ch done mask */
+#define   TALITOS1_ISR_TEA_ERR         0x00000040
+#define   TALITOS2_ISR_4CHERR          ISR2_FORMAT(0xa) /* 4 ch errors mask */
+#define   TALITOS2_ISR_4CHDONE         ISR2_FORMAT(0x5) /* 4 ch done mask */
+#define   TALITOS2_ISR_CH_0_2_ERR      ISR2_FORMAT(0x2) /* ch 0, 2 err mask */
+#define   TALITOS2_ISR_CH_0_2_DONE     ISR2_FORMAT(0x1) /* ch 0, 2 done mask */
+#define   TALITOS2_ISR_CH_1_3_ERR      ISR2_FORMAT(0x8) /* ch 1, 3 err mask */
+#define   TALITOS2_ISR_CH_1_3_DONE     ISR2_FORMAT(0x4) /* ch 1, 3 done mask */
 #define TALITOS_ISR_LO                 0x1014
 #define TALITOS_ICR                    0x1018  /* interrupt clear register */
 #define TALITOS_ICR_LO                 0x101C
 
 /* channel register address stride */
 #define TALITOS_CH_BASE_OFFSET         0x1000  /* default channel map base */
-#define TALITOS_CH_STRIDE              0x100
+#define TALITOS1_CH_STRIDE             0x1000
+#define TALITOS2_CH_STRIDE             0x100
 
 /* channel configuration register  */
 #define TALITOS_CCCR                   0x8
-#define   TALITOS_CCCR_CONT            0x2    /* channel continue */
-#define   TALITOS_CCCR_RESET           0x1    /* channel reset */
+#define   TALITOS2_CCCR_CONT           0x2    /* channel continue on SEC2 */
+#define   TALITOS2_CCCR_RESET          0x1    /* channel reset on SEC2 */
 #define TALITOS_CCCR_LO                        0xc
 #define   TALITOS_CCCR_LO_IWSE         0x80   /* chan. ICCR writeback enab. */
 #define   TALITOS_CCCR_LO_EAE          0x20   /* extended address enable */
 #define   TALITOS_CCCR_LO_CDWE         0x10   /* chan. done writeback enab. */
 #define   TALITOS_CCCR_LO_NT           0x4    /* notification type */
 #define   TALITOS_CCCR_LO_CDIE         0x2    /* channel done IRQ enable */
+#define   TALITOS1_CCCR_LO_RESET       0x1    /* channel reset on SEC1 */
 
 /* CCPSR: channel pointer status register */
 #define TALITOS_CCPSR                  0x10
@@ -224,37 +278,48 @@ extern int talitos_submit(struct device *dev, int ch, struct talitos_desc *desc,
 #define TALITOS_SCATTER                        0xe0
 #define TALITOS_SCATTER_LO             0xe4
 
+/* execution unit registers base */
+#define TALITOS2_DEU                   0x2000
+#define TALITOS2_AESU                  0x4000
+#define TALITOS2_MDEU                  0x6000
+#define TALITOS2_AFEU                  0x8000
+#define TALITOS2_RNGU                  0xa000
+#define TALITOS2_PKEU                  0xc000
+#define TALITOS2_KEU                   0xe000
+#define TALITOS2_CRCU                  0xf000
+
+#define TALITOS12_AESU                 0x4000
+#define TALITOS12_DEU                  0x5000
+#define TALITOS12_MDEU                 0x6000
+
+#define TALITOS10_AFEU                 0x8000
+#define TALITOS10_DEU                  0xa000
+#define TALITOS10_MDEU                 0xc000
+#define TALITOS10_RNGU                 0xe000
+#define TALITOS10_PKEU                 0x10000
+#define TALITOS10_AESU                 0x12000
+
 /* execution unit interrupt status registers */
-#define TALITOS_DEUISR                 0x2030 /* DES unit */
-#define TALITOS_DEUISR_LO              0x2034
-#define TALITOS_AESUISR                        0x4030 /* AES unit */
-#define TALITOS_AESUISR_LO             0x4034
-#define TALITOS_MDEUISR                        0x6030 /* message digest unit */
-#define TALITOS_MDEUISR_LO             0x6034
-#define TALITOS_MDEUICR                        0x6038 /* interrupt control */
-#define TALITOS_MDEUICR_LO             0x603c
+#define TALITOS_EUDSR                  0x10    /* data size */
+#define TALITOS_EUDSR_LO               0x14
+#define TALITOS_EURCR                  0x18 /* reset control*/
+#define TALITOS_EURCR_LO               0x1c
+#define TALITOS_EUSR                   0x28 /* rng status */
+#define TALITOS_EUSR_LO                        0x2c
+#define TALITOS_EUISR                  0x30
+#define TALITOS_EUISR_LO               0x34
+#define TALITOS_EUICR                  0x38 /* int. control */
+#define TALITOS_EUICR_LO               0x3c
+#define TALITOS_EU_FIFO                        0x800 /* output FIFO */
+#define TALITOS_EU_FIFO_LO             0x804 /* output FIFO */
+/* DES unit */
+#define   TALITOS1_DEUICR_KPE          0x00200000 /* Key Parity Error */
+/* message digest unit */
 #define   TALITOS_MDEUICR_LO_ICE       0x4000 /* integrity check IRQ enable */
-#define TALITOS_AFEUISR                        0x8030 /* arc4 unit */
-#define TALITOS_AFEUISR_LO             0x8034
-#define TALITOS_RNGUISR                        0xa030 /* random number unit */
-#define TALITOS_RNGUISR_LO             0xa034
-#define TALITOS_RNGUSR                 0xa028 /* rng status */
-#define TALITOS_RNGUSR_LO              0xa02c
+/* random number unit */
 #define   TALITOS_RNGUSR_LO_RD         0x1     /* reset done */
 #define   TALITOS_RNGUSR_LO_OFL                0xff0000/* output FIFO length */
-#define TALITOS_RNGUDSR                        0xa010  /* data size */
-#define TALITOS_RNGUDSR_LO             0xa014
-#define TALITOS_RNGU_FIFO              0xa800  /* output FIFO */
-#define TALITOS_RNGU_FIFO_LO           0xa804  /* output FIFO */
-#define TALITOS_RNGURCR                        0xa018  /* reset control */
-#define TALITOS_RNGURCR_LO             0xa01c
 #define   TALITOS_RNGURCR_LO_SR                0x1     /* software reset */
-#define TALITOS_PKEUISR                        0xc030 /* public key unit */
-#define TALITOS_PKEUISR_LO             0xc034
-#define TALITOS_KEUISR                 0xe030 /* kasumi unit */
-#define TALITOS_KEUISR_LO              0xe034
-#define TALITOS_CRCUISR                        0xf030 /* cyclic redundancy check unit*/
-#define TALITOS_CRCUISR_LO             0xf034
 
 #define TALITOS_MDEU_CONTEXT_SIZE_MD5_SHA1_SHA256      0x28
 #define TALITOS_MDEU_CONTEXT_SIZE_SHA384_SHA512                0x48
index b35e5c4b025af762bb80ade0bee326ebf18ecf59..30796441b0a6f83155b79fb372a2123b05be79df 100644 (file)
@@ -7,6 +7,8 @@
 config CRYPTO_DEV_UX500_CRYP
        tristate "UX500 crypto driver for CRYP block"
        depends on CRYPTO_DEV_UX500
+       select CRYPTO_ALGAPI
+       select CRYPTO_BLKCIPHER
        select CRYPTO_DES
        help
         This selects the crypto driver for the UX500_CRYP hardware. It supports
@@ -16,7 +18,6 @@ config CRYPTO_DEV_UX500_HASH
         tristate "UX500 crypto driver for HASH block"
         depends on CRYPTO_DEV_UX500
         select CRYPTO_HASH
-        select CRYPTO_HMAC
         help
           This selects the hash driver for the UX500_HASH hardware.
           Depends on UX500/STM DMA if running in DMA mode.
@@ -24,7 +25,6 @@ config CRYPTO_DEV_UX500_HASH
 config CRYPTO_DEV_UX500_DEBUG
        bool "Activate ux500 platform debug-mode for crypto and hash block"
        depends on CRYPTO_DEV_UX500_CRYP || CRYPTO_DEV_UX500_HASH
-       default n
        help
          Say Y if you want to add debug prints to ux500_hash and
          ux500_cryp devices.
index 771babf16aa044bd6375247c0b4a56ca545e1fb5..89d8208d985178b9743b534d99b04614d0369368 100644 (file)
@@ -1,6 +1,6 @@
 config CRYPTO_DEV_VMX_ENCRYPT
        tristate "Encryption acceleration support on P8 CPU"
-       depends on PPC64 && CRYPTO_DEV_VMX
+       depends on CRYPTO_DEV_VMX
        default y
        help
          Support for VMX cryptographic acceleration instructions on Power8 CPU.
index c699c6e6c82e6a5e2440dc8402eb7533e8f81c1e..d28ab96a2475901cdaad85bbeb9eee071f9a4ec6 100644 (file)
@@ -4,7 +4,7 @@ vmx-crypto-objs := vmx.o aesp8-ppc.o ghashp8-ppc.o aes.o aes_cbc.o aes_ctr.o gha
 ifeq ($(CONFIG_CPU_LITTLE_ENDIAN),y)
 TARGET := linux-ppc64le
 else
-TARGET := linux-pcc64
+TARGET := linux-ppc64
 endif
 
 quiet_cmd_perl = PERL $@
index a9064e36e7b5488c4f47f628bb317c48b7efa485..e79e567e43aacae4584b32c2d7fc9ae1e6c1e300 100644 (file)
 #include "aesp8-ppc.h"
 
 struct p8_aes_ctx {
-    struct crypto_cipher *fallback;
-    struct aes_key enc_key;
-    struct aes_key dec_key;
+       struct crypto_cipher *fallback;
+       struct aes_key enc_key;
+       struct aes_key dec_key;
 };
 
 static int p8_aes_init(struct crypto_tfm *tfm)
 {
-    const char *alg;
-    struct crypto_cipher *fallback;
-    struct p8_aes_ctx *ctx = crypto_tfm_ctx(tfm);
-
-    if (!(alg = crypto_tfm_alg_name(tfm))) {
-        printk(KERN_ERR "Failed to get algorithm name.\n");
-        return -ENOENT;
-    }
-
-    fallback = crypto_alloc_cipher(alg, 0 ,CRYPTO_ALG_NEED_FALLBACK);
-    if (IS_ERR(fallback)) {
-        printk(KERN_ERR "Failed to allocate transformation for '%s': %ld\n",
-                alg, PTR_ERR(fallback));
-        return PTR_ERR(fallback);
-    }
-    printk(KERN_INFO "Using '%s' as fallback implementation.\n",
-            crypto_tfm_alg_driver_name((struct crypto_tfm *) fallback));
-
-    crypto_cipher_set_flags(fallback,
-            crypto_cipher_get_flags((struct crypto_cipher *) tfm));
-    ctx->fallback = fallback;
-
-    return 0;
+       const char *alg;
+       struct crypto_cipher *fallback;
+       struct p8_aes_ctx *ctx = crypto_tfm_ctx(tfm);
+
+       if (!(alg = crypto_tfm_alg_name(tfm))) {
+               printk(KERN_ERR "Failed to get algorithm name.\n");
+               return -ENOENT;
+       }
+
+       fallback = crypto_alloc_cipher(alg, 0, CRYPTO_ALG_NEED_FALLBACK);
+       if (IS_ERR(fallback)) {
+               printk(KERN_ERR
+                      "Failed to allocate transformation for '%s': %ld\n",
+                      alg, PTR_ERR(fallback));
+               return PTR_ERR(fallback);
+       }
+       printk(KERN_INFO "Using '%s' as fallback implementation.\n",
+              crypto_tfm_alg_driver_name((struct crypto_tfm *) fallback));
+
+       crypto_cipher_set_flags(fallback,
+                               crypto_cipher_get_flags((struct
+                                                        crypto_cipher *)
+                                                       tfm));
+       ctx->fallback = fallback;
+
+       return 0;
 }
 
 static void p8_aes_exit(struct crypto_tfm *tfm)
 {
-    struct p8_aes_ctx *ctx = crypto_tfm_ctx(tfm);
+       struct p8_aes_ctx *ctx = crypto_tfm_ctx(tfm);
 
-    if (ctx->fallback) {
-        crypto_free_cipher(ctx->fallback);
-        ctx->fallback = NULL;
-    }
+       if (ctx->fallback) {
+               crypto_free_cipher(ctx->fallback);
+               ctx->fallback = NULL;
+       }
 }
 
 static int p8_aes_setkey(struct crypto_tfm *tfm, const u8 *key,
-    unsigned int keylen)
+                        unsigned int keylen)
 {
-    int ret;
-    struct p8_aes_ctx *ctx = crypto_tfm_ctx(tfm);
-
-    preempt_disable();
-    pagefault_disable();
-    enable_kernel_altivec();
-    ret = aes_p8_set_encrypt_key(key, keylen * 8, &ctx->enc_key);
-    ret += aes_p8_set_decrypt_key(key, keylen * 8, &ctx->dec_key);
-    pagefault_enable();
-    preempt_enable();
-
-    ret += crypto_cipher_setkey(ctx->fallback, key, keylen);
-    return ret;
+       int ret;
+       struct p8_aes_ctx *ctx = crypto_tfm_ctx(tfm);
+
+       preempt_disable();
+       pagefault_disable();
+       enable_kernel_altivec();
+       ret = aes_p8_set_encrypt_key(key, keylen * 8, &ctx->enc_key);
+       ret += aes_p8_set_decrypt_key(key, keylen * 8, &ctx->dec_key);
+       pagefault_enable();
+       preempt_enable();
+
+       ret += crypto_cipher_setkey(ctx->fallback, key, keylen);
+       return ret;
 }
 
 static void p8_aes_encrypt(struct crypto_tfm *tfm, u8 *dst, const u8 *src)
 {
-    struct p8_aes_ctx *ctx = crypto_tfm_ctx(tfm);
-
-    if (in_interrupt()) {
-        crypto_cipher_encrypt_one(ctx->fallback, dst, src);
-    } else {
-       preempt_disable();
-        pagefault_disable();
-        enable_kernel_altivec();
-        aes_p8_encrypt(src, dst, &ctx->enc_key);
-        pagefault_enable();
-       preempt_enable();
-    }
+       struct p8_aes_ctx *ctx = crypto_tfm_ctx(tfm);
+
+       if (in_interrupt()) {
+               crypto_cipher_encrypt_one(ctx->fallback, dst, src);
+       } else {
+               preempt_disable();
+               pagefault_disable();
+               enable_kernel_altivec();
+               aes_p8_encrypt(src, dst, &ctx->enc_key);
+               pagefault_enable();
+               preempt_enable();
+       }
 }
 
 static void p8_aes_decrypt(struct crypto_tfm *tfm, u8 *dst, const u8 *src)
 {
-    struct p8_aes_ctx *ctx = crypto_tfm_ctx(tfm);
-
-    if (in_interrupt()) {
-        crypto_cipher_decrypt_one(ctx->fallback, dst, src);
-    } else {
-       preempt_disable();
-        pagefault_disable();
-        enable_kernel_altivec();
-        aes_p8_decrypt(src, dst, &ctx->dec_key);
-        pagefault_enable();
-       preempt_enable();
-    }
+       struct p8_aes_ctx *ctx = crypto_tfm_ctx(tfm);
+
+       if (in_interrupt()) {
+               crypto_cipher_decrypt_one(ctx->fallback, dst, src);
+       } else {
+               preempt_disable();
+               pagefault_disable();
+               enable_kernel_altivec();
+               aes_p8_decrypt(src, dst, &ctx->dec_key);
+               pagefault_enable();
+               preempt_enable();
+       }
 }
 
 struct crypto_alg p8_aes_alg = {
-    .cra_name = "aes",
-    .cra_driver_name = "p8_aes",
-    .cra_module = THIS_MODULE,
-    .cra_priority = 1000,
-    .cra_type = NULL,
-    .cra_flags = CRYPTO_ALG_TYPE_CIPHER | CRYPTO_ALG_NEED_FALLBACK,
-    .cra_alignmask = 0,
-    .cra_blocksize = AES_BLOCK_SIZE,
-    .cra_ctxsize = sizeof(struct p8_aes_ctx),
-    .cra_init = p8_aes_init,
-    .cra_exit = p8_aes_exit,
-    .cra_cipher = {
-        .cia_min_keysize = AES_MIN_KEY_SIZE,
-        .cia_max_keysize = AES_MAX_KEY_SIZE,
-        .cia_setkey = p8_aes_setkey,
-        .cia_encrypt = p8_aes_encrypt,
-        .cia_decrypt = p8_aes_decrypt,
-    },
+       .cra_name = "aes",
+       .cra_driver_name = "p8_aes",
+       .cra_module = THIS_MODULE,
+       .cra_priority = 1000,
+       .cra_type = NULL,
+       .cra_flags = CRYPTO_ALG_TYPE_CIPHER | CRYPTO_ALG_NEED_FALLBACK,
+       .cra_alignmask = 0,
+       .cra_blocksize = AES_BLOCK_SIZE,
+       .cra_ctxsize = sizeof(struct p8_aes_ctx),
+       .cra_init = p8_aes_init,
+       .cra_exit = p8_aes_exit,
+       .cra_cipher = {
+                      .cia_min_keysize = AES_MIN_KEY_SIZE,
+                      .cia_max_keysize = AES_MAX_KEY_SIZE,
+                      .cia_setkey = p8_aes_setkey,
+                      .cia_encrypt = p8_aes_encrypt,
+                      .cia_decrypt = p8_aes_decrypt,
+       },
 };
-
index 477284abdd11dc9d738fe08858d7cbe48a65fe32..7299995c78ec3b34ea76e289cf84dc877f1175ef 100644 (file)
 #include "aesp8-ppc.h"
 
 struct p8_aes_cbc_ctx {
-    struct crypto_blkcipher *fallback;
-    struct aes_key enc_key;
-    struct aes_key dec_key;
+       struct crypto_blkcipher *fallback;
+       struct aes_key enc_key;
+       struct aes_key dec_key;
 };
 
 static int p8_aes_cbc_init(struct crypto_tfm *tfm)
 {
-    const char *alg;
-    struct crypto_blkcipher *fallback;
-    struct p8_aes_cbc_ctx *ctx = crypto_tfm_ctx(tfm);
-
-    if (!(alg = crypto_tfm_alg_name(tfm))) {
-        printk(KERN_ERR "Failed to get algorithm name.\n");
-        return -ENOENT;
-    }
-
-    fallback = crypto_alloc_blkcipher(alg, 0 ,CRYPTO_ALG_NEED_FALLBACK);
-    if (IS_ERR(fallback)) {
-        printk(KERN_ERR "Failed to allocate transformation for '%s': %ld\n",
-                alg, PTR_ERR(fallback));
-        return PTR_ERR(fallback);
-    }
-    printk(KERN_INFO "Using '%s' as fallback implementation.\n",
-            crypto_tfm_alg_driver_name((struct crypto_tfm *) fallback));
-
-    crypto_blkcipher_set_flags(fallback,
-            crypto_blkcipher_get_flags((struct crypto_blkcipher *) tfm));
-    ctx->fallback = fallback;
-
-    return 0;
+       const char *alg;
+       struct crypto_blkcipher *fallback;
+       struct p8_aes_cbc_ctx *ctx = crypto_tfm_ctx(tfm);
+
+       if (!(alg = crypto_tfm_alg_name(tfm))) {
+               printk(KERN_ERR "Failed to get algorithm name.\n");
+               return -ENOENT;
+       }
+
+       fallback =
+           crypto_alloc_blkcipher(alg, 0, CRYPTO_ALG_NEED_FALLBACK);
+       if (IS_ERR(fallback)) {
+               printk(KERN_ERR
+                      "Failed to allocate transformation for '%s': %ld\n",
+                      alg, PTR_ERR(fallback));
+               return PTR_ERR(fallback);
+       }
+       printk(KERN_INFO "Using '%s' as fallback implementation.\n",
+              crypto_tfm_alg_driver_name((struct crypto_tfm *) fallback));
+
+       crypto_blkcipher_set_flags(
+               fallback,
+               crypto_blkcipher_get_flags((struct crypto_blkcipher *)tfm));
+       ctx->fallback = fallback;
+
+       return 0;
 }
 
 static void p8_aes_cbc_exit(struct crypto_tfm *tfm)
 {
-    struct p8_aes_cbc_ctx *ctx = crypto_tfm_ctx(tfm);
+       struct p8_aes_cbc_ctx *ctx = crypto_tfm_ctx(tfm);
 
-    if (ctx->fallback) {
-        crypto_free_blkcipher(ctx->fallback);
-        ctx->fallback = NULL;
-    }
+       if (ctx->fallback) {
+               crypto_free_blkcipher(ctx->fallback);
+               ctx->fallback = NULL;
+       }
 }
 
 static int p8_aes_cbc_setkey(struct crypto_tfm *tfm, const u8 *key,
-    unsigned int keylen)
+                            unsigned int keylen)
 {
-    int ret;
-    struct p8_aes_cbc_ctx *ctx = crypto_tfm_ctx(tfm);
-
-    preempt_disable();
-    pagefault_disable();
-    enable_kernel_altivec();
-    ret = aes_p8_set_encrypt_key(key, keylen * 8, &ctx->enc_key);
-    ret += aes_p8_set_decrypt_key(key, keylen * 8, &ctx->dec_key);
-    pagefault_enable();
-    preempt_enable();
-
-    ret += crypto_blkcipher_setkey(ctx->fallback, key, keylen);
-    return ret;
+       int ret;
+       struct p8_aes_cbc_ctx *ctx = crypto_tfm_ctx(tfm);
+
+       preempt_disable();
+       pagefault_disable();
+       enable_kernel_altivec();
+       ret = aes_p8_set_encrypt_key(key, keylen * 8, &ctx->enc_key);
+       ret += aes_p8_set_decrypt_key(key, keylen * 8, &ctx->dec_key);
+       pagefault_enable();
+       preempt_enable();
+
+       ret += crypto_blkcipher_setkey(ctx->fallback, key, keylen);
+       return ret;
 }
 
 static int p8_aes_cbc_encrypt(struct blkcipher_desc *desc,
-    struct scatterlist *dst, struct scatterlist *src,
-    unsigned int nbytes)
+                             struct scatterlist *dst,
+                             struct scatterlist *src, unsigned int nbytes)
 {
-    int ret;
-    struct blkcipher_walk walk;
-    struct p8_aes_cbc_ctx *ctx = crypto_tfm_ctx(
-            crypto_blkcipher_tfm(desc->tfm));
-    struct blkcipher_desc fallback_desc = {
-        .tfm = ctx->fallback,
-        .info = desc->info,
-        .flags = desc->flags
-    };
-
-    if (in_interrupt()) {
-        ret = crypto_blkcipher_encrypt(&fallback_desc, dst, src, nbytes);
-    } else {
-       preempt_disable();
-        pagefault_disable();
-        enable_kernel_altivec();
-
-       blkcipher_walk_init(&walk, dst, src, nbytes);
-        ret = blkcipher_walk_virt(desc, &walk);
-        while ((nbytes = walk.nbytes)) {
-                       aes_p8_cbc_encrypt(walk.src.virt.addr, walk.dst.virt.addr,
-                               nbytes & AES_BLOCK_MASK, &ctx->enc_key, walk.iv, 1);
+       int ret;
+       struct blkcipher_walk walk;
+       struct p8_aes_cbc_ctx *ctx =
+               crypto_tfm_ctx(crypto_blkcipher_tfm(desc->tfm));
+       struct blkcipher_desc fallback_desc = {
+               .tfm = ctx->fallback,
+               .info = desc->info,
+               .flags = desc->flags
+       };
+
+       if (in_interrupt()) {
+               ret = crypto_blkcipher_encrypt(&fallback_desc, dst, src,
+                                              nbytes);
+       } else {
+               preempt_disable();
+               pagefault_disable();
+               enable_kernel_altivec();
+
+               blkcipher_walk_init(&walk, dst, src, nbytes);
+               ret = blkcipher_walk_virt(desc, &walk);
+               while ((nbytes = walk.nbytes)) {
+                       aes_p8_cbc_encrypt(walk.src.virt.addr,
+                                          walk.dst.virt.addr,
+                                          nbytes & AES_BLOCK_MASK,
+                                          &ctx->enc_key, walk.iv, 1);
                        nbytes &= AES_BLOCK_SIZE - 1;
                        ret = blkcipher_walk_done(desc, &walk, nbytes);
-       }
+               }
 
-        pagefault_enable();
-       preempt_enable();
-    }
+               pagefault_enable();
+               preempt_enable();
+       }
 
-    return ret;
+       return ret;
 }
 
 static int p8_aes_cbc_decrypt(struct blkcipher_desc *desc,
-    struct scatterlist *dst, struct scatterlist *src,
-    unsigned int nbytes)
+                             struct scatterlist *dst,
+                             struct scatterlist *src, unsigned int nbytes)
 {
-    int ret;
-    struct blkcipher_walk walk;
-    struct p8_aes_cbc_ctx *ctx = crypto_tfm_ctx(
-            crypto_blkcipher_tfm(desc->tfm));
-    struct blkcipher_desc fallback_desc = {
-        .tfm = ctx->fallback,
-        .info = desc->info,
-        .flags = desc->flags
-    };
-
-    if (in_interrupt()) {
-        ret = crypto_blkcipher_decrypt(&fallback_desc, dst, src, nbytes);
-    } else {
-       preempt_disable();
-        pagefault_disable();
-        enable_kernel_altivec();
-
-       blkcipher_walk_init(&walk, dst, src, nbytes);
-        ret = blkcipher_walk_virt(desc, &walk);
-        while ((nbytes = walk.nbytes)) {
-                       aes_p8_cbc_encrypt(walk.src.virt.addr, walk.dst.virt.addr,
-                               nbytes & AES_BLOCK_MASK, &ctx->dec_key, walk.iv, 0);
+       int ret;
+       struct blkcipher_walk walk;
+       struct p8_aes_cbc_ctx *ctx =
+               crypto_tfm_ctx(crypto_blkcipher_tfm(desc->tfm));
+       struct blkcipher_desc fallback_desc = {
+               .tfm = ctx->fallback,
+               .info = desc->info,
+               .flags = desc->flags
+       };
+
+       if (in_interrupt()) {
+               ret = crypto_blkcipher_decrypt(&fallback_desc, dst, src,
+                                              nbytes);
+       } else {
+               preempt_disable();
+               pagefault_disable();
+               enable_kernel_altivec();
+
+               blkcipher_walk_init(&walk, dst, src, nbytes);
+               ret = blkcipher_walk_virt(desc, &walk);
+               while ((nbytes = walk.nbytes)) {
+                       aes_p8_cbc_encrypt(walk.src.virt.addr,
+                                          walk.dst.virt.addr,
+                                          nbytes & AES_BLOCK_MASK,
+                                          &ctx->dec_key, walk.iv, 0);
                        nbytes &= AES_BLOCK_SIZE - 1;
                        ret = blkcipher_walk_done(desc, &walk, nbytes);
                }
 
-        pagefault_enable();
-       preempt_enable();
-    }
+               pagefault_enable();
+               preempt_enable();
+       }
 
-    return ret;
+       return ret;
 }
 
 
 struct crypto_alg p8_aes_cbc_alg = {
-    .cra_name = "cbc(aes)",
-    .cra_driver_name = "p8_aes_cbc",
-    .cra_module = THIS_MODULE,
-    .cra_priority = 1000,
-    .cra_type = &crypto_blkcipher_type,
-    .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER | CRYPTO_ALG_NEED_FALLBACK,
-    .cra_alignmask = 0,
-    .cra_blocksize = AES_BLOCK_SIZE,
-    .cra_ctxsize = sizeof(struct p8_aes_cbc_ctx),
-    .cra_init = p8_aes_cbc_init,
-    .cra_exit = p8_aes_cbc_exit,
-    .cra_blkcipher = {
-        .ivsize = 0,
-        .min_keysize = AES_MIN_KEY_SIZE,
-        .max_keysize = AES_MAX_KEY_SIZE,
-        .setkey = p8_aes_cbc_setkey,
-        .encrypt = p8_aes_cbc_encrypt,
-        .decrypt = p8_aes_cbc_decrypt,
-    },
+       .cra_name = "cbc(aes)",
+       .cra_driver_name = "p8_aes_cbc",
+       .cra_module = THIS_MODULE,
+       .cra_priority = 1000,
+       .cra_type = &crypto_blkcipher_type,
+       .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER | CRYPTO_ALG_NEED_FALLBACK,
+       .cra_alignmask = 0,
+       .cra_blocksize = AES_BLOCK_SIZE,
+       .cra_ctxsize = sizeof(struct p8_aes_cbc_ctx),
+       .cra_init = p8_aes_cbc_init,
+       .cra_exit = p8_aes_cbc_exit,
+       .cra_blkcipher = {
+                         .ivsize = 0,
+                         .min_keysize = AES_MIN_KEY_SIZE,
+                         .max_keysize = AES_MAX_KEY_SIZE,
+                         .setkey = p8_aes_cbc_setkey,
+                         .encrypt = p8_aes_cbc_encrypt,
+                         .decrypt = p8_aes_cbc_decrypt,
+       },
 };
-
index 96dbee4bf4a6ddab87ea3156e354c0510deacd6c..7adae42a7b79ea81a5bc35ae2db9db9b6a2437e2 100644 (file)
 #include "aesp8-ppc.h"
 
 struct p8_aes_ctr_ctx {
-    struct crypto_blkcipher *fallback;
-    struct aes_key enc_key;
+       struct crypto_blkcipher *fallback;
+       struct aes_key enc_key;
 };
 
 static int p8_aes_ctr_init(struct crypto_tfm *tfm)
 {
-    const char *alg;
-    struct crypto_blkcipher *fallback;
-    struct p8_aes_ctr_ctx *ctx = crypto_tfm_ctx(tfm);
-
-    if (!(alg = crypto_tfm_alg_name(tfm))) {
-        printk(KERN_ERR "Failed to get algorithm name.\n");
-        return -ENOENT;
-    }
-
-    fallback = crypto_alloc_blkcipher(alg, 0 ,CRYPTO_ALG_NEED_FALLBACK);
-    if (IS_ERR(fallback)) {
-        printk(KERN_ERR "Failed to allocate transformation for '%s': %ld\n",
-                alg, PTR_ERR(fallback));
-        return PTR_ERR(fallback);
-    }
-    printk(KERN_INFO "Using '%s' as fallback implementation.\n",
-            crypto_tfm_alg_driver_name((struct crypto_tfm *) fallback));
-
-    crypto_blkcipher_set_flags(fallback,
-            crypto_blkcipher_get_flags((struct crypto_blkcipher *) tfm));
-    ctx->fallback = fallback;
-
-    return 0;
+       const char *alg;
+       struct crypto_blkcipher *fallback;
+       struct p8_aes_ctr_ctx *ctx = crypto_tfm_ctx(tfm);
+
+       if (!(alg = crypto_tfm_alg_name(tfm))) {
+               printk(KERN_ERR "Failed to get algorithm name.\n");
+               return -ENOENT;
+       }
+
+       fallback =
+           crypto_alloc_blkcipher(alg, 0, CRYPTO_ALG_NEED_FALLBACK);
+       if (IS_ERR(fallback)) {
+               printk(KERN_ERR
+                      "Failed to allocate transformation for '%s': %ld\n",
+                      alg, PTR_ERR(fallback));
+               return PTR_ERR(fallback);
+       }
+       printk(KERN_INFO "Using '%s' as fallback implementation.\n",
+              crypto_tfm_alg_driver_name((struct crypto_tfm *) fallback));
+
+       crypto_blkcipher_set_flags(
+               fallback,
+               crypto_blkcipher_get_flags((struct crypto_blkcipher *)tfm));
+       ctx->fallback = fallback;
+
+       return 0;
 }
 
 static void p8_aes_ctr_exit(struct crypto_tfm *tfm)
 {
-    struct p8_aes_ctr_ctx *ctx = crypto_tfm_ctx(tfm);
+       struct p8_aes_ctr_ctx *ctx = crypto_tfm_ctx(tfm);
 
-    if (ctx->fallback) {
-        crypto_free_blkcipher(ctx->fallback);
-        ctx->fallback = NULL;
-    }
+       if (ctx->fallback) {
+               crypto_free_blkcipher(ctx->fallback);
+               ctx->fallback = NULL;
+       }
 }
 
 static int p8_aes_ctr_setkey(struct crypto_tfm *tfm, const u8 *key,
-    unsigned int keylen)
+                            unsigned int keylen)
 {
-    int ret;
-    struct p8_aes_ctr_ctx *ctx = crypto_tfm_ctx(tfm);
+       int ret;
+       struct p8_aes_ctr_ctx *ctx = crypto_tfm_ctx(tfm);
 
-    pagefault_disable();
-    enable_kernel_altivec();
-    ret = aes_p8_set_encrypt_key(key, keylen * 8, &ctx->enc_key);
-    pagefault_enable();
+       pagefault_disable();
+       enable_kernel_altivec();
+       ret = aes_p8_set_encrypt_key(key, keylen * 8, &ctx->enc_key);
+       pagefault_enable();
 
-    ret += crypto_blkcipher_setkey(ctx->fallback, key, keylen);
-    return ret;
+       ret += crypto_blkcipher_setkey(ctx->fallback, key, keylen);
+       return ret;
 }
 
 static void p8_aes_ctr_final(struct p8_aes_ctr_ctx *ctx,
-                struct blkcipher_walk *walk)
+                            struct blkcipher_walk *walk)
 {
-    u8 *ctrblk = walk->iv;
-    u8 keystream[AES_BLOCK_SIZE];
-    u8 *src = walk->src.virt.addr;
-    u8 *dst = walk->dst.virt.addr;
-    unsigned int nbytes = walk->nbytes;
-
-    pagefault_disable();
-    enable_kernel_altivec();
-    aes_p8_encrypt(ctrblk, keystream, &ctx->enc_key);
-    pagefault_enable();
-
-    crypto_xor(keystream, src, nbytes);
-    memcpy(dst, keystream, nbytes);
-    crypto_inc(ctrblk, AES_BLOCK_SIZE);
+       u8 *ctrblk = walk->iv;
+       u8 keystream[AES_BLOCK_SIZE];
+       u8 *src = walk->src.virt.addr;
+       u8 *dst = walk->dst.virt.addr;
+       unsigned int nbytes = walk->nbytes;
+
+       pagefault_disable();
+       enable_kernel_altivec();
+       aes_p8_encrypt(ctrblk, keystream, &ctx->enc_key);
+       pagefault_enable();
+
+       crypto_xor(keystream, src, nbytes);
+       memcpy(dst, keystream, nbytes);
+       crypto_inc(ctrblk, AES_BLOCK_SIZE);
 }
 
 static int p8_aes_ctr_crypt(struct blkcipher_desc *desc,
-    struct scatterlist *dst, struct scatterlist *src,
-    unsigned int nbytes)
+                           struct scatterlist *dst,
+                           struct scatterlist *src, unsigned int nbytes)
 {
-    int ret;
-    struct blkcipher_walk walk;
-    struct p8_aes_ctr_ctx *ctx = crypto_tfm_ctx(
-            crypto_blkcipher_tfm(desc->tfm));
-    struct blkcipher_desc fallback_desc = {
-        .tfm = ctx->fallback,
-        .info = desc->info,
-        .flags = desc->flags
-    };
-
-    if (in_interrupt()) {
-        ret = crypto_blkcipher_encrypt(&fallback_desc, dst, src, nbytes);
-    } else {
-        blkcipher_walk_init(&walk, dst, src, nbytes);
-        ret = blkcipher_walk_virt_block(desc, &walk, AES_BLOCK_SIZE);
-        while ((nbytes = walk.nbytes) >= AES_BLOCK_SIZE) {
-            pagefault_disable();
-            enable_kernel_altivec();
-            aes_p8_ctr32_encrypt_blocks(walk.src.virt.addr, walk.dst.virt.addr,
-                (nbytes & AES_BLOCK_MASK)/AES_BLOCK_SIZE, &ctx->enc_key, walk.iv);
-            pagefault_enable();
-
-            crypto_inc(walk.iv, AES_BLOCK_SIZE);
-            nbytes &= AES_BLOCK_SIZE - 1;
-            ret = blkcipher_walk_done(desc, &walk, nbytes);
-        }
-        if (walk.nbytes) {
-            p8_aes_ctr_final(ctx, &walk);
-            ret = blkcipher_walk_done(desc, &walk, 0);
-        }
-    }
-
-    return ret;
+       int ret;
+       struct blkcipher_walk walk;
+       struct p8_aes_ctr_ctx *ctx =
+               crypto_tfm_ctx(crypto_blkcipher_tfm(desc->tfm));
+       struct blkcipher_desc fallback_desc = {
+               .tfm = ctx->fallback,
+               .info = desc->info,
+               .flags = desc->flags
+       };
+
+       if (in_interrupt()) {
+               ret = crypto_blkcipher_encrypt(&fallback_desc, dst, src,
+                                              nbytes);
+       } else {
+               blkcipher_walk_init(&walk, dst, src, nbytes);
+               ret = blkcipher_walk_virt_block(desc, &walk, AES_BLOCK_SIZE);
+               while ((nbytes = walk.nbytes) >= AES_BLOCK_SIZE) {
+                       pagefault_disable();
+                       enable_kernel_altivec();
+                       aes_p8_ctr32_encrypt_blocks(walk.src.virt.addr,
+                                                   walk.dst.virt.addr,
+                                                   (nbytes &
+                                                    AES_BLOCK_MASK) /
+                                                   AES_BLOCK_SIZE,
+                                                   &ctx->enc_key,
+                                                   walk.iv);
+                       pagefault_enable();
+
+                       crypto_inc(walk.iv, AES_BLOCK_SIZE);
+                       nbytes &= AES_BLOCK_SIZE - 1;
+                       ret = blkcipher_walk_done(desc, &walk, nbytes);
+               }
+               if (walk.nbytes) {
+                       p8_aes_ctr_final(ctx, &walk);
+                       ret = blkcipher_walk_done(desc, &walk, 0);
+               }
+       }
+
+       return ret;
 }
 
 struct crypto_alg p8_aes_ctr_alg = {
-    .cra_name = "ctr(aes)",
-    .cra_driver_name = "p8_aes_ctr",
-    .cra_module = THIS_MODULE,
-    .cra_priority = 1000,
-    .cra_type = &crypto_blkcipher_type,
-    .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER | CRYPTO_ALG_NEED_FALLBACK,
-    .cra_alignmask = 0,
-    .cra_blocksize = 1,
-    .cra_ctxsize = sizeof(struct p8_aes_ctr_ctx),
-    .cra_init = p8_aes_ctr_init,
-    .cra_exit = p8_aes_ctr_exit,
-    .cra_blkcipher = {
-        .ivsize = 0,
-        .min_keysize = AES_MIN_KEY_SIZE,
-        .max_keysize = AES_MAX_KEY_SIZE,
-        .setkey = p8_aes_ctr_setkey,
-        .encrypt = p8_aes_ctr_crypt,
-        .decrypt = p8_aes_ctr_crypt,
-    },
+       .cra_name = "ctr(aes)",
+       .cra_driver_name = "p8_aes_ctr",
+       .cra_module = THIS_MODULE,
+       .cra_priority = 1000,
+       .cra_type = &crypto_blkcipher_type,
+       .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER | CRYPTO_ALG_NEED_FALLBACK,
+       .cra_alignmask = 0,
+       .cra_blocksize = 1,
+       .cra_ctxsize = sizeof(struct p8_aes_ctr_ctx),
+       .cra_init = p8_aes_ctr_init,
+       .cra_exit = p8_aes_ctr_exit,
+       .cra_blkcipher = {
+                         .ivsize = 0,
+                         .min_keysize = AES_MIN_KEY_SIZE,
+                         .max_keysize = AES_MAX_KEY_SIZE,
+                         .setkey = p8_aes_ctr_setkey,
+                         .encrypt = p8_aes_ctr_crypt,
+                         .decrypt = p8_aes_ctr_crypt,
+       },
 };
index e963945a83e1639ce99cabd7387e436d519f84d2..4cd34ee54a94da0ac2725b9c3da82776272234dc 100644 (file)
@@ -4,17 +4,18 @@
 #define AES_BLOCK_MASK  (~(AES_BLOCK_SIZE-1))
 
 struct aes_key {
-    u8 key[AES_MAX_KEYLENGTH];
-    int rounds;
+       u8 key[AES_MAX_KEYLENGTH];
+       int rounds;
 };
 
 int aes_p8_set_encrypt_key(const u8 *userKey, const int bits,
-        struct aes_key *key);
+                          struct aes_key *key);
 int aes_p8_set_decrypt_key(const u8 *userKey, const int bits,
-        struct aes_key *key);
+                          struct aes_key *key);
 void aes_p8_encrypt(const u8 *in, u8 *out, const struct aes_key *key);
-void aes_p8_decrypt(const u8 *in, u8 *out,const struct aes_key *key);
+void aes_p8_decrypt(const u8 *in, u8 *out, const struct aes_key *key);
 void aes_p8_cbc_encrypt(const u8 *in, u8 *out, size_t len,
-               const struct aes_key *key, u8 *iv, const int enc);
+                       const struct aes_key *key, u8 *iv, const int enc);
 void aes_p8_ctr32_encrypt_blocks(const u8 *in, u8 *out,
-        size_t len, const struct aes_key *key, const u8 *iv);
+                                size_t len, const struct aes_key *key,
+                                const u8 *iv);
index f255ec4a04d48d60a28e1025b3e2473cdcb7d8fc..b5e29002b66678337c54ec7858634d43285c4213 100644 (file)
 void gcm_init_p8(u128 htable[16], const u64 Xi[2]);
 void gcm_gmult_p8(u64 Xi[2], const u128 htable[16]);
 void gcm_ghash_p8(u64 Xi[2], const u128 htable[16],
-        const u8 *in,size_t len);
+                 const u8 *in, size_t len);
 
 struct p8_ghash_ctx {
-    u128 htable[16];
-    struct crypto_shash *fallback;
+       u128 htable[16];
+       struct crypto_shash *fallback;
 };
 
 struct p8_ghash_desc_ctx {
-    u64 shash[2];
-    u8 buffer[GHASH_DIGEST_SIZE];
-    int bytes;
-    struct shash_desc fallback_desc;
+       u64 shash[2];
+       u8 buffer[GHASH_DIGEST_SIZE];
+       int bytes;
+       struct shash_desc fallback_desc;
 };
 
 static int p8_ghash_init_tfm(struct crypto_tfm *tfm)
 {
-    const char *alg;
-    struct crypto_shash *fallback;
-    struct crypto_shash *shash_tfm = __crypto_shash_cast(tfm);
-    struct p8_ghash_ctx *ctx = crypto_tfm_ctx(tfm);
-
-    if (!(alg = crypto_tfm_alg_name(tfm))) {
-        printk(KERN_ERR "Failed to get algorithm name.\n");
-        return -ENOENT;
-    }
-
-    fallback = crypto_alloc_shash(alg, 0 ,CRYPTO_ALG_NEED_FALLBACK);
-    if (IS_ERR(fallback)) {
-        printk(KERN_ERR "Failed to allocate transformation for '%s': %ld\n",
-                alg, PTR_ERR(fallback));
-        return PTR_ERR(fallback);
-    }
-    printk(KERN_INFO "Using '%s' as fallback implementation.\n",
-            crypto_tfm_alg_driver_name(crypto_shash_tfm(fallback)));
-
-    crypto_shash_set_flags(fallback,
-            crypto_shash_get_flags((struct crypto_shash *) tfm));
-    ctx->fallback = fallback;
-
-    shash_tfm->descsize = sizeof(struct p8_ghash_desc_ctx)
-        + crypto_shash_descsize(fallback);
-
-    return 0;
+       const char *alg;
+       struct crypto_shash *fallback;
+       struct crypto_shash *shash_tfm = __crypto_shash_cast(tfm);
+       struct p8_ghash_ctx *ctx = crypto_tfm_ctx(tfm);
+
+       if (!(alg = crypto_tfm_alg_name(tfm))) {
+               printk(KERN_ERR "Failed to get algorithm name.\n");
+               return -ENOENT;
+       }
+
+       fallback = crypto_alloc_shash(alg, 0, CRYPTO_ALG_NEED_FALLBACK);
+       if (IS_ERR(fallback)) {
+               printk(KERN_ERR
+                      "Failed to allocate transformation for '%s': %ld\n",
+                      alg, PTR_ERR(fallback));
+               return PTR_ERR(fallback);
+       }
+       printk(KERN_INFO "Using '%s' as fallback implementation.\n",
+              crypto_tfm_alg_driver_name(crypto_shash_tfm(fallback)));
+
+       crypto_shash_set_flags(fallback,
+                              crypto_shash_get_flags((struct crypto_shash
+                                                      *) tfm));
+       ctx->fallback = fallback;
+
+       shash_tfm->descsize = sizeof(struct p8_ghash_desc_ctx)
+           + crypto_shash_descsize(fallback);
+
+       return 0;
 }
 
 static void p8_ghash_exit_tfm(struct crypto_tfm *tfm)
 {
-    struct p8_ghash_ctx *ctx = crypto_tfm_ctx(tfm);
+       struct p8_ghash_ctx *ctx = crypto_tfm_ctx(tfm);
 
-    if (ctx->fallback) {
-        crypto_free_shash(ctx->fallback);
-        ctx->fallback = NULL;
-    }
+       if (ctx->fallback) {
+               crypto_free_shash(ctx->fallback);
+               ctx->fallback = NULL;
+       }
 }
 
 static int p8_ghash_init(struct shash_desc *desc)
 {
-    struct p8_ghash_ctx *ctx = crypto_tfm_ctx(crypto_shash_tfm(desc->tfm));
-    struct p8_ghash_desc_ctx *dctx = shash_desc_ctx(desc);
-
-    dctx->bytes = 0;
-    memset(dctx->shash, 0, GHASH_DIGEST_SIZE);
-    dctx->fallback_desc.tfm = ctx->fallback;
-    dctx->fallback_desc.flags = desc->flags;
-    return crypto_shash_init(&dctx->fallback_desc);
+       struct p8_ghash_ctx *ctx = crypto_tfm_ctx(crypto_shash_tfm(desc->tfm));
+       struct p8_ghash_desc_ctx *dctx = shash_desc_ctx(desc);
+
+       dctx->bytes = 0;
+       memset(dctx->shash, 0, GHASH_DIGEST_SIZE);
+       dctx->fallback_desc.tfm = ctx->fallback;
+       dctx->fallback_desc.flags = desc->flags;
+       return crypto_shash_init(&dctx->fallback_desc);
 }
 
 static int p8_ghash_setkey(struct crypto_shash *tfm, const u8 *key,
-    unsigned int keylen)
+                          unsigned int keylen)
 {
-    struct p8_ghash_ctx *ctx = crypto_tfm_ctx(crypto_shash_tfm(tfm));
-
-    if (keylen != GHASH_KEY_LEN)
-        return -EINVAL;
-
-    preempt_disable();
-    pagefault_disable();
-    enable_kernel_altivec();
-    enable_kernel_fp();
-    gcm_init_p8(ctx->htable, (const u64 *) key);
-    pagefault_enable();
-    preempt_enable();
-    return crypto_shash_setkey(ctx->fallback, key, keylen);
+       struct p8_ghash_ctx *ctx = crypto_tfm_ctx(crypto_shash_tfm(tfm));
+
+       if (keylen != GHASH_KEY_LEN)
+               return -EINVAL;
+
+       preempt_disable();
+       pagefault_disable();
+       enable_kernel_altivec();
+       enable_kernel_fp();
+       gcm_init_p8(ctx->htable, (const u64 *) key);
+       pagefault_enable();
+       preempt_enable();
+       return crypto_shash_setkey(ctx->fallback, key, keylen);
 }
 
 static int p8_ghash_update(struct shash_desc *desc,
-        const u8 *src, unsigned int srclen)
+                          const u8 *src, unsigned int srclen)
 {
-    unsigned int len;
-    struct p8_ghash_ctx *ctx = crypto_tfm_ctx(crypto_shash_tfm(desc->tfm));
-    struct p8_ghash_desc_ctx *dctx = shash_desc_ctx(desc);
-
-    if (IN_INTERRUPT) {
-        return crypto_shash_update(&dctx->fallback_desc, src, srclen);
-    } else {
-        if (dctx->bytes) {
-            if (dctx->bytes + srclen < GHASH_DIGEST_SIZE) {
-                memcpy(dctx->buffer + dctx->bytes, src, srclen);
-                dctx->bytes += srclen;
-                return 0;
-            }
-            memcpy(dctx->buffer + dctx->bytes, src,
-                    GHASH_DIGEST_SIZE - dctx->bytes);
-           preempt_disable();
-            pagefault_disable();
-            enable_kernel_altivec();
-            enable_kernel_fp();
-            gcm_ghash_p8(dctx->shash, ctx->htable, dctx->buffer,
-                    GHASH_DIGEST_SIZE);
-            pagefault_enable();
-           preempt_enable();
-            src += GHASH_DIGEST_SIZE - dctx->bytes;
-            srclen -= GHASH_DIGEST_SIZE - dctx->bytes;
-            dctx->bytes = 0;
-        }
-        len = srclen & ~(GHASH_DIGEST_SIZE - 1);
-        if (len) {
-           preempt_disable();
-            pagefault_disable();
-            enable_kernel_altivec();
-            enable_kernel_fp();
-            gcm_ghash_p8(dctx->shash, ctx->htable, src, len);
-            pagefault_enable();
-           preempt_enable();
-            src += len;
-            srclen -= len;
-        }
-        if (srclen) {
-            memcpy(dctx->buffer, src, srclen);
-            dctx->bytes = srclen;
-        }
-        return 0;
-    }
+       unsigned int len;
+       struct p8_ghash_ctx *ctx = crypto_tfm_ctx(crypto_shash_tfm(desc->tfm));
+       struct p8_ghash_desc_ctx *dctx = shash_desc_ctx(desc);
+
+       if (IN_INTERRUPT) {
+               return crypto_shash_update(&dctx->fallback_desc, src,
+                                          srclen);
+       } else {
+               if (dctx->bytes) {
+                       if (dctx->bytes + srclen < GHASH_DIGEST_SIZE) {
+                               memcpy(dctx->buffer + dctx->bytes, src,
+                                      srclen);
+                               dctx->bytes += srclen;
+                               return 0;
+                       }
+                       memcpy(dctx->buffer + dctx->bytes, src,
+                              GHASH_DIGEST_SIZE - dctx->bytes);
+                       preempt_disable();
+                       pagefault_disable();
+                       enable_kernel_altivec();
+                       enable_kernel_fp();
+                       gcm_ghash_p8(dctx->shash, ctx->htable,
+                                    dctx->buffer, GHASH_DIGEST_SIZE);
+                       pagefault_enable();
+                       preempt_enable();
+                       src += GHASH_DIGEST_SIZE - dctx->bytes;
+                       srclen -= GHASH_DIGEST_SIZE - dctx->bytes;
+                       dctx->bytes = 0;
+               }
+               len = srclen & ~(GHASH_DIGEST_SIZE - 1);
+               if (len) {
+                       preempt_disable();
+                       pagefault_disable();
+                       enable_kernel_altivec();
+                       enable_kernel_fp();
+                       gcm_ghash_p8(dctx->shash, ctx->htable, src, len);
+                       pagefault_enable();
+                       preempt_enable();
+                       src += len;
+                       srclen -= len;
+               }
+               if (srclen) {
+                       memcpy(dctx->buffer, src, srclen);
+                       dctx->bytes = srclen;
+               }
+               return 0;
+       }
 }
 
 static int p8_ghash_final(struct shash_desc *desc, u8 *out)
 {
-    int i;
-    struct p8_ghash_ctx *ctx = crypto_tfm_ctx(crypto_shash_tfm(desc->tfm));
-    struct p8_ghash_desc_ctx *dctx = shash_desc_ctx(desc);
-
-    if (IN_INTERRUPT) {
-        return crypto_shash_final(&dctx->fallback_desc, out);
-    } else {
-        if (dctx->bytes) {
-            for (i = dctx->bytes; i < GHASH_DIGEST_SIZE; i++)
-                dctx->buffer[i] = 0;
-           preempt_disable();
-            pagefault_disable();
-            enable_kernel_altivec();
-            enable_kernel_fp();
-            gcm_ghash_p8(dctx->shash, ctx->htable, dctx->buffer,
-                    GHASH_DIGEST_SIZE);
-            pagefault_enable();
-           preempt_enable();
-            dctx->bytes = 0;
-        }
-        memcpy(out, dctx->shash, GHASH_DIGEST_SIZE);
-        return 0;
-    }
+       int i;
+       struct p8_ghash_ctx *ctx = crypto_tfm_ctx(crypto_shash_tfm(desc->tfm));
+       struct p8_ghash_desc_ctx *dctx = shash_desc_ctx(desc);
+
+       if (IN_INTERRUPT) {
+               return crypto_shash_final(&dctx->fallback_desc, out);
+       } else {
+               if (dctx->bytes) {
+                       for (i = dctx->bytes; i < GHASH_DIGEST_SIZE; i++)
+                               dctx->buffer[i] = 0;
+                       preempt_disable();
+                       pagefault_disable();
+                       enable_kernel_altivec();
+                       enable_kernel_fp();
+                       gcm_ghash_p8(dctx->shash, ctx->htable,
+                                    dctx->buffer, GHASH_DIGEST_SIZE);
+                       pagefault_enable();
+                       preempt_enable();
+                       dctx->bytes = 0;
+               }
+               memcpy(out, dctx->shash, GHASH_DIGEST_SIZE);
+               return 0;
+       }
 }
 
 struct shash_alg p8_ghash_alg = {
-    .digestsize = GHASH_DIGEST_SIZE,
-    .init       = p8_ghash_init,
-    .update     = p8_ghash_update,
-    .final      = p8_ghash_final,
-    .setkey     = p8_ghash_setkey,
-    .descsize   = sizeof(struct p8_ghash_desc_ctx),
-    .base       = {
-        .cra_name = "ghash",
-        .cra_driver_name = "p8_ghash",
-        .cra_priority = 1000,
-        .cra_flags = CRYPTO_ALG_TYPE_SHASH | CRYPTO_ALG_NEED_FALLBACK,
-        .cra_blocksize = GHASH_BLOCK_SIZE,
-        .cra_ctxsize = sizeof(struct p8_ghash_ctx),
-        .cra_module = THIS_MODULE,
-        .cra_init = p8_ghash_init_tfm,
-        .cra_exit = p8_ghash_exit_tfm,
-    },
+       .digestsize = GHASH_DIGEST_SIZE,
+       .init = p8_ghash_init,
+       .update = p8_ghash_update,
+       .final = p8_ghash_final,
+       .setkey = p8_ghash_setkey,
+       .descsize = sizeof(struct p8_ghash_desc_ctx),
+       .base = {
+                .cra_name = "ghash",
+                .cra_driver_name = "p8_ghash",
+                .cra_priority = 1000,
+                .cra_flags = CRYPTO_ALG_TYPE_SHASH | CRYPTO_ALG_NEED_FALLBACK,
+                .cra_blocksize = GHASH_BLOCK_SIZE,
+                .cra_ctxsize = sizeof(struct p8_ghash_ctx),
+                .cra_module = THIS_MODULE,
+                .cra_init = p8_ghash_init_tfm,
+                .cra_exit = p8_ghash_exit_tfm,
+       },
 };
index 44d8d5cfe40d2d6f6a48ab49ed131cbdd8228481..e163d5770438aa63f89cef0a74497b921423d42f 100644 (file)
@@ -32,57 +32,57 @@ extern struct crypto_alg p8_aes_alg;
 extern struct crypto_alg p8_aes_cbc_alg;
 extern struct crypto_alg p8_aes_ctr_alg;
 static struct crypto_alg *algs[] = {
-    &p8_aes_alg,
-    &p8_aes_cbc_alg,
-    &p8_aes_ctr_alg,
-    NULL,
+       &p8_aes_alg,
+       &p8_aes_cbc_alg,
+       &p8_aes_ctr_alg,
+       NULL,
 };
 
 int __init p8_init(void)
 {
-    int ret = 0;
-    struct crypto_alg **alg_it;
+       int ret = 0;
+       struct crypto_alg **alg_it;
 
-    if (!(cur_cpu_spec->cpu_user_features2 & PPC_FEATURE2_VEC_CRYPTO))
-        return -ENODEV;
+       if (!(cur_cpu_spec->cpu_user_features2 & PPC_FEATURE2_VEC_CRYPTO))
+               return -ENODEV;
 
-    for (alg_it = algs; *alg_it; alg_it++) {
-        ret = crypto_register_alg(*alg_it);
-        printk(KERN_INFO "crypto_register_alg '%s' = %d\n",
-                (*alg_it)->cra_name, ret);
-        if (ret) {
-            for (alg_it--; alg_it >= algs; alg_it--)
-                crypto_unregister_alg(*alg_it);
-            break;
-        }
-    }
-    if (ret)
-        return ret;
+       for (alg_it = algs; *alg_it; alg_it++) {
+               ret = crypto_register_alg(*alg_it);
+               printk(KERN_INFO "crypto_register_alg '%s' = %d\n",
+                      (*alg_it)->cra_name, ret);
+               if (ret) {
+                       for (alg_it--; alg_it >= algs; alg_it--)
+                               crypto_unregister_alg(*alg_it);
+                       break;
+               }
+       }
+       if (ret)
+               return ret;
 
-    ret = crypto_register_shash(&p8_ghash_alg);
-    if (ret) {
-        for (alg_it = algs; *alg_it; alg_it++)
-            crypto_unregister_alg(*alg_it);
-    }
-    return ret;
+       ret = crypto_register_shash(&p8_ghash_alg);
+       if (ret) {
+               for (alg_it = algs; *alg_it; alg_it++)
+                       crypto_unregister_alg(*alg_it);
+       }
+       return ret;
 }
 
 void __exit p8_exit(void)
 {
-    struct crypto_alg **alg_it;
+       struct crypto_alg **alg_it;
 
-    for (alg_it = algs; *alg_it; alg_it++) {
-        printk(KERN_INFO "Removing '%s'\n", (*alg_it)->cra_name);
-        crypto_unregister_alg(*alg_it);
-    }
-    crypto_unregister_shash(&p8_ghash_alg);
+       for (alg_it = algs; *alg_it; alg_it++) {
+               printk(KERN_INFO "Removing '%s'\n", (*alg_it)->cra_name);
+               crypto_unregister_alg(*alg_it);
+       }
+       crypto_unregister_shash(&p8_ghash_alg);
 }
 
 module_init(p8_init);
 module_exit(p8_exit);
 
 MODULE_AUTHOR("Marcelo Cerri<mhcerri@br.ibm.com>");
-MODULE_DESCRIPTION("IBM VMX cryptogaphic acceleration instructions support on Power 8");
+MODULE_DESCRIPTION("IBM VMX cryptographic acceleration instructions "
+                  "support on Power 8");
 MODULE_LICENSE("GPL");
 MODULE_VERSION("1.0.0");
-
index 933e4b338459284465d7970e3ff7dbf0f37314b8..7992164ea9ec2849f6ac3691629c47cda30aeb28 100644 (file)
 #define AT_XDMAC_MBR_UBC_NDV3          (0x3 << 27)     /* Next Descriptor View 3 */
 
 #define AT_XDMAC_MAX_CHAN      0x20
+#define AT_XDMAC_MAX_CSIZE     16      /* 16 data */
+#define AT_XDMAC_MAX_DWIDTH    8       /* 64 bits */
 
 #define AT_XDMAC_DMA_BUSWIDTHS\
        (BIT(DMA_SLAVE_BUSWIDTH_UNDEFINED) |\
@@ -192,20 +194,17 @@ struct at_xdmac_chan {
        struct dma_chan                 chan;
        void __iomem                    *ch_regs;
        u32                             mask;           /* Channel Mask */
-       u32                             cfg[2];         /* Channel Configuration Register */
-       #define AT_XDMAC_DEV_TO_MEM_CFG 0               /* Predifined dev to mem channel conf */
-       #define AT_XDMAC_MEM_TO_DEV_CFG 1               /* Predifined mem to dev channel conf */
+       u32                             cfg;            /* Channel Configuration Register */
        u8                              perid;          /* Peripheral ID */
        u8                              perif;          /* Peripheral Interface */
        u8                              memif;          /* Memory Interface */
-       u32                             per_src_addr;
-       u32                             per_dst_addr;
        u32                             save_cc;
        u32                             save_cim;
        u32                             save_cnda;
        u32                             save_cndc;
        unsigned long                   status;
        struct tasklet_struct           tasklet;
+       struct dma_slave_config         sconfig;
 
        spinlock_t                      lock;
 
@@ -415,8 +414,9 @@ static dma_cookie_t at_xdmac_tx_submit(struct dma_async_tx_descriptor *tx)
        struct at_xdmac_desc    *desc = txd_to_at_desc(tx);
        struct at_xdmac_chan    *atchan = to_at_xdmac_chan(tx->chan);
        dma_cookie_t            cookie;
+       unsigned long           irqflags;
 
-       spin_lock_bh(&atchan->lock);
+       spin_lock_irqsave(&atchan->lock, irqflags);
        cookie = dma_cookie_assign(tx);
 
        dev_vdbg(chan2dev(tx->chan), "%s: atchan 0x%p, add desc 0x%p to xfers_list\n",
@@ -425,7 +425,7 @@ static dma_cookie_t at_xdmac_tx_submit(struct dma_async_tx_descriptor *tx)
        if (list_is_singular(&atchan->xfers_list))
                at_xdmac_start_xfer(atchan, desc);
 
-       spin_unlock_bh(&atchan->lock);
+       spin_unlock_irqrestore(&atchan->lock, irqflags);
        return cookie;
 }
 
@@ -494,61 +494,94 @@ static struct dma_chan *at_xdmac_xlate(struct of_phandle_args *dma_spec,
        return chan;
 }
 
+static int at_xdmac_compute_chan_conf(struct dma_chan *chan,
+                                     enum dma_transfer_direction direction)
+{
+       struct at_xdmac_chan    *atchan = to_at_xdmac_chan(chan);
+       int                     csize, dwidth;
+
+       if (direction == DMA_DEV_TO_MEM) {
+               atchan->cfg =
+                       AT91_XDMAC_DT_PERID(atchan->perid)
+                       | AT_XDMAC_CC_DAM_INCREMENTED_AM
+                       | AT_XDMAC_CC_SAM_FIXED_AM
+                       | AT_XDMAC_CC_DIF(atchan->memif)
+                       | AT_XDMAC_CC_SIF(atchan->perif)
+                       | AT_XDMAC_CC_SWREQ_HWR_CONNECTED
+                       | AT_XDMAC_CC_DSYNC_PER2MEM
+                       | AT_XDMAC_CC_MBSIZE_SIXTEEN
+                       | AT_XDMAC_CC_TYPE_PER_TRAN;
+               csize = ffs(atchan->sconfig.src_maxburst) - 1;
+               if (csize < 0) {
+                       dev_err(chan2dev(chan), "invalid src maxburst value\n");
+                       return -EINVAL;
+               }
+               atchan->cfg |= AT_XDMAC_CC_CSIZE(csize);
+               dwidth = ffs(atchan->sconfig.src_addr_width) - 1;
+               if (dwidth < 0) {
+                       dev_err(chan2dev(chan), "invalid src addr width value\n");
+                       return -EINVAL;
+               }
+               atchan->cfg |= AT_XDMAC_CC_DWIDTH(dwidth);
+       } else if (direction == DMA_MEM_TO_DEV) {
+               atchan->cfg =
+                       AT91_XDMAC_DT_PERID(atchan->perid)
+                       | AT_XDMAC_CC_DAM_FIXED_AM
+                       | AT_XDMAC_CC_SAM_INCREMENTED_AM
+                       | AT_XDMAC_CC_DIF(atchan->perif)
+                       | AT_XDMAC_CC_SIF(atchan->memif)
+                       | AT_XDMAC_CC_SWREQ_HWR_CONNECTED
+                       | AT_XDMAC_CC_DSYNC_MEM2PER
+                       | AT_XDMAC_CC_MBSIZE_SIXTEEN
+                       | AT_XDMAC_CC_TYPE_PER_TRAN;
+               csize = ffs(atchan->sconfig.dst_maxburst) - 1;
+               if (csize < 0) {
+                       dev_err(chan2dev(chan), "invalid src maxburst value\n");
+                       return -EINVAL;
+               }
+               atchan->cfg |= AT_XDMAC_CC_CSIZE(csize);
+               dwidth = ffs(atchan->sconfig.dst_addr_width) - 1;
+               if (dwidth < 0) {
+                       dev_err(chan2dev(chan), "invalid dst addr width value\n");
+                       return -EINVAL;
+               }
+               atchan->cfg |= AT_XDMAC_CC_DWIDTH(dwidth);
+       }
+
+       dev_dbg(chan2dev(chan), "%s: cfg=0x%08x\n", __func__, atchan->cfg);
+
+       return 0;
+}
+
+/*
+ * Only check that maxburst and addr width values are supported by the
+ * the controller but not that the configuration is good to perform the
+ * transfer since we don't know the direction at this stage.
+ */
+static int at_xdmac_check_slave_config(struct dma_slave_config *sconfig)
+{
+       if ((sconfig->src_maxburst > AT_XDMAC_MAX_CSIZE)
+           || (sconfig->dst_maxburst > AT_XDMAC_MAX_CSIZE))
+               return -EINVAL;
+
+       if ((sconfig->src_addr_width > AT_XDMAC_MAX_DWIDTH)
+           || (sconfig->dst_addr_width > AT_XDMAC_MAX_DWIDTH))
+               return -EINVAL;
+
+       return 0;
+}
+
 static int at_xdmac_set_slave_config(struct dma_chan *chan,
                                      struct dma_slave_config *sconfig)
 {
        struct at_xdmac_chan    *atchan = to_at_xdmac_chan(chan);
-       u8 dwidth;
-       int csize;
 
-       atchan->cfg[AT_XDMAC_DEV_TO_MEM_CFG] =
-               AT91_XDMAC_DT_PERID(atchan->perid)
-               | AT_XDMAC_CC_DAM_INCREMENTED_AM
-               | AT_XDMAC_CC_SAM_FIXED_AM
-               | AT_XDMAC_CC_DIF(atchan->memif)
-               | AT_XDMAC_CC_SIF(atchan->perif)
-               | AT_XDMAC_CC_SWREQ_HWR_CONNECTED
-               | AT_XDMAC_CC_DSYNC_PER2MEM
-               | AT_XDMAC_CC_MBSIZE_SIXTEEN
-               | AT_XDMAC_CC_TYPE_PER_TRAN;
-       csize = at_xdmac_csize(sconfig->src_maxburst);
-       if (csize < 0) {
-               dev_err(chan2dev(chan), "invalid src maxburst value\n");
+       if (at_xdmac_check_slave_config(sconfig)) {
+               dev_err(chan2dev(chan), "invalid slave configuration\n");
                return -EINVAL;
        }
-       atchan->cfg[AT_XDMAC_DEV_TO_MEM_CFG] |= AT_XDMAC_CC_CSIZE(csize);
-       dwidth = ffs(sconfig->src_addr_width) - 1;
-       atchan->cfg[AT_XDMAC_DEV_TO_MEM_CFG] |= AT_XDMAC_CC_DWIDTH(dwidth);
-
-
-       atchan->cfg[AT_XDMAC_MEM_TO_DEV_CFG] =
-               AT91_XDMAC_DT_PERID(atchan->perid)
-               | AT_XDMAC_CC_DAM_FIXED_AM
-               | AT_XDMAC_CC_SAM_INCREMENTED_AM
-               | AT_XDMAC_CC_DIF(atchan->perif)
-               | AT_XDMAC_CC_SIF(atchan->memif)
-               | AT_XDMAC_CC_SWREQ_HWR_CONNECTED
-               | AT_XDMAC_CC_DSYNC_MEM2PER
-               | AT_XDMAC_CC_MBSIZE_SIXTEEN
-               | AT_XDMAC_CC_TYPE_PER_TRAN;
-       csize = at_xdmac_csize(sconfig->dst_maxburst);
-       if (csize < 0) {
-               dev_err(chan2dev(chan), "invalid src maxburst value\n");
-               return -EINVAL;
-       }
-       atchan->cfg[AT_XDMAC_MEM_TO_DEV_CFG] |= AT_XDMAC_CC_CSIZE(csize);
-       dwidth = ffs(sconfig->dst_addr_width) - 1;
-       atchan->cfg[AT_XDMAC_MEM_TO_DEV_CFG] |= AT_XDMAC_CC_DWIDTH(dwidth);
-
-       /* Src and dst addr are needed to configure the link list descriptor. */
-       atchan->per_src_addr = sconfig->src_addr;
-       atchan->per_dst_addr = sconfig->dst_addr;
 
-       dev_dbg(chan2dev(chan),
-               "%s: cfg[dev2mem]=0x%08x, cfg[mem2dev]=0x%08x, per_src_addr=0x%08x, per_dst_addr=0x%08x\n",
-               __func__, atchan->cfg[AT_XDMAC_DEV_TO_MEM_CFG],
-               atchan->cfg[AT_XDMAC_MEM_TO_DEV_CFG],
-               atchan->per_src_addr, atchan->per_dst_addr);
+       memcpy(&atchan->sconfig, sconfig, sizeof(atchan->sconfig));
 
        return 0;
 }
@@ -563,6 +596,8 @@ at_xdmac_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
        struct scatterlist      *sg;
        int                     i;
        unsigned int            xfer_size = 0;
+       unsigned long           irqflags;
+       struct dma_async_tx_descriptor  *ret = NULL;
 
        if (!sgl)
                return NULL;
@@ -578,7 +613,10 @@ at_xdmac_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
                 flags);
 
        /* Protect dma_sconfig field that can be modified by set_slave_conf. */
-       spin_lock_bh(&atchan->lock);
+       spin_lock_irqsave(&atchan->lock, irqflags);
+
+       if (at_xdmac_compute_chan_conf(chan, direction))
+               goto spin_unlock;
 
        /* Prepare descriptors. */
        for_each_sg(sgl, sg, sg_len, i) {
@@ -589,8 +627,7 @@ at_xdmac_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
                mem = sg_dma_address(sg);
                if (unlikely(!len)) {
                        dev_err(chan2dev(chan), "sg data length is zero\n");
-                       spin_unlock_bh(&atchan->lock);
-                       return NULL;
+                       goto spin_unlock;
                }
                dev_dbg(chan2dev(chan), "%s: * sg%d len=%u, mem=0x%08x\n",
                         __func__, i, len, mem);
@@ -600,20 +637,18 @@ at_xdmac_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
                        dev_err(chan2dev(chan), "can't get descriptor\n");
                        if (first)
                                list_splice_init(&first->descs_list, &atchan->free_descs_list);
-                       spin_unlock_bh(&atchan->lock);
-                       return NULL;
+                       goto spin_unlock;
                }
 
                /* Linked list descriptor setup. */
                if (direction == DMA_DEV_TO_MEM) {
-                       desc->lld.mbr_sa = atchan->per_src_addr;
+                       desc->lld.mbr_sa = atchan->sconfig.src_addr;
                        desc->lld.mbr_da = mem;
-                       desc->lld.mbr_cfg = atchan->cfg[AT_XDMAC_DEV_TO_MEM_CFG];
                } else {
                        desc->lld.mbr_sa = mem;
-                       desc->lld.mbr_da = atchan->per_dst_addr;
-                       desc->lld.mbr_cfg = atchan->cfg[AT_XDMAC_MEM_TO_DEV_CFG];
+                       desc->lld.mbr_da = atchan->sconfig.dst_addr;
                }
+               desc->lld.mbr_cfg = atchan->cfg;
                dwidth = at_xdmac_get_dwidth(desc->lld.mbr_cfg);
                fixed_dwidth = IS_ALIGNED(len, 1 << dwidth)
                               ? at_xdmac_get_dwidth(desc->lld.mbr_cfg)
@@ -645,13 +680,15 @@ at_xdmac_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
                xfer_size += len;
        }
 
-       spin_unlock_bh(&atchan->lock);
 
        first->tx_dma_desc.flags = flags;
        first->xfer_size = xfer_size;
        first->direction = direction;
+       ret = &first->tx_dma_desc;
 
-       return &first->tx_dma_desc;
+spin_unlock:
+       spin_unlock_irqrestore(&atchan->lock, irqflags);
+       return ret;
 }
 
 static struct dma_async_tx_descriptor *
@@ -664,6 +701,7 @@ at_xdmac_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t buf_addr,
        struct at_xdmac_desc    *first = NULL, *prev = NULL;
        unsigned int            periods = buf_len / period_len;
        int                     i;
+       unsigned long           irqflags;
 
        dev_dbg(chan2dev(chan), "%s: buf_addr=%pad, buf_len=%zd, period_len=%zd, dir=%s, flags=0x%lx\n",
                __func__, &buf_addr, buf_len, period_len,
@@ -679,32 +717,34 @@ at_xdmac_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t buf_addr,
                return NULL;
        }
 
+       if (at_xdmac_compute_chan_conf(chan, direction))
+               return NULL;
+
        for (i = 0; i < periods; i++) {
                struct at_xdmac_desc    *desc = NULL;
 
-               spin_lock_bh(&atchan->lock);
+               spin_lock_irqsave(&atchan->lock, irqflags);
                desc = at_xdmac_get_desc(atchan);
                if (!desc) {
                        dev_err(chan2dev(chan), "can't get descriptor\n");
                        if (first)
                                list_splice_init(&first->descs_list, &atchan->free_descs_list);
-                       spin_unlock_bh(&atchan->lock);
+                       spin_unlock_irqrestore(&atchan->lock, irqflags);
                        return NULL;
                }
-               spin_unlock_bh(&atchan->lock);
+               spin_unlock_irqrestore(&atchan->lock, irqflags);
                dev_dbg(chan2dev(chan),
                        "%s: desc=0x%p, tx_dma_desc.phys=%pad\n",
                        __func__, desc, &desc->tx_dma_desc.phys);
 
                if (direction == DMA_DEV_TO_MEM) {
-                       desc->lld.mbr_sa = atchan->per_src_addr;
+                       desc->lld.mbr_sa = atchan->sconfig.src_addr;
                        desc->lld.mbr_da = buf_addr + i * period_len;
-                       desc->lld.mbr_cfg = atchan->cfg[AT_XDMAC_DEV_TO_MEM_CFG];
                } else {
                        desc->lld.mbr_sa = buf_addr + i * period_len;
-                       desc->lld.mbr_da = atchan->per_dst_addr;
-                       desc->lld.mbr_cfg = atchan->cfg[AT_XDMAC_MEM_TO_DEV_CFG];
+                       desc->lld.mbr_da = atchan->sconfig.dst_addr;
                }
+               desc->lld.mbr_cfg = atchan->cfg;
                desc->lld.mbr_ubc = AT_XDMAC_MBR_UBC_NDV1
                        | AT_XDMAC_MBR_UBC_NDEN
                        | AT_XDMAC_MBR_UBC_NSEN
@@ -766,6 +806,7 @@ at_xdmac_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src,
                                        | AT_XDMAC_CC_SIF(0)
                                        | AT_XDMAC_CC_MBSIZE_SIXTEEN
                                        | AT_XDMAC_CC_TYPE_MEM_TRAN;
+       unsigned long           irqflags;
 
        dev_dbg(chan2dev(chan), "%s: src=%pad, dest=%pad, len=%zd, flags=0x%lx\n",
                __func__, &src, &dest, len, flags);
@@ -798,9 +839,9 @@ at_xdmac_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src,
 
                dev_dbg(chan2dev(chan), "%s: remaining_size=%zu\n", __func__, remaining_size);
 
-               spin_lock_bh(&atchan->lock);
+               spin_lock_irqsave(&atchan->lock, irqflags);
                desc = at_xdmac_get_desc(atchan);
-               spin_unlock_bh(&atchan->lock);
+               spin_unlock_irqrestore(&atchan->lock, irqflags);
                if (!desc) {
                        dev_err(chan2dev(chan), "can't get descriptor\n");
                        if (first)
@@ -886,6 +927,7 @@ at_xdmac_tx_status(struct dma_chan *chan, dma_cookie_t cookie,
        int                     residue;
        u32                     cur_nda, mask, value;
        u8                      dwidth = 0;
+       unsigned long           flags;
 
        ret = dma_cookie_status(chan, cookie, txstate);
        if (ret == DMA_COMPLETE)
@@ -894,7 +936,7 @@ at_xdmac_tx_status(struct dma_chan *chan, dma_cookie_t cookie,
        if (!txstate)
                return ret;
 
-       spin_lock_bh(&atchan->lock);
+       spin_lock_irqsave(&atchan->lock, flags);
 
        desc = list_first_entry(&atchan->xfers_list, struct at_xdmac_desc, xfer_node);
 
@@ -904,8 +946,7 @@ at_xdmac_tx_status(struct dma_chan *chan, dma_cookie_t cookie,
         */
        if (!desc->active_xfer) {
                dma_set_residue(txstate, desc->xfer_size);
-               spin_unlock_bh(&atchan->lock);
-               return ret;
+               goto spin_unlock;
        }
 
        residue = desc->xfer_size;
@@ -936,14 +977,14 @@ at_xdmac_tx_status(struct dma_chan *chan, dma_cookie_t cookie,
        }
        residue += at_xdmac_chan_read(atchan, AT_XDMAC_CUBC) << dwidth;
 
-       spin_unlock_bh(&atchan->lock);
-
        dma_set_residue(txstate, residue);
 
        dev_dbg(chan2dev(chan),
                 "%s: desc=0x%p, tx_dma_desc.phys=%pad, tx_status=%d, cookie=%d, residue=%d\n",
                 __func__, desc, &desc->tx_dma_desc.phys, ret, cookie, residue);
 
+spin_unlock:
+       spin_unlock_irqrestore(&atchan->lock, flags);
        return ret;
 }
 
@@ -964,8 +1005,9 @@ static void at_xdmac_remove_xfer(struct at_xdmac_chan *atchan,
 static void at_xdmac_advance_work(struct at_xdmac_chan *atchan)
 {
        struct at_xdmac_desc    *desc;
+       unsigned long           flags;
 
-       spin_lock_bh(&atchan->lock);
+       spin_lock_irqsave(&atchan->lock, flags);
 
        /*
         * If channel is enabled, do nothing, advance_work will be triggered
@@ -980,7 +1022,7 @@ static void at_xdmac_advance_work(struct at_xdmac_chan *atchan)
                        at_xdmac_start_xfer(atchan, desc);
        }
 
-       spin_unlock_bh(&atchan->lock);
+       spin_unlock_irqrestore(&atchan->lock, flags);
 }
 
 static void at_xdmac_handle_cyclic(struct at_xdmac_chan *atchan)
@@ -1116,12 +1158,13 @@ static int at_xdmac_device_config(struct dma_chan *chan,
 {
        struct at_xdmac_chan    *atchan = to_at_xdmac_chan(chan);
        int ret;
+       unsigned long           flags;
 
        dev_dbg(chan2dev(chan), "%s\n", __func__);
 
-       spin_lock_bh(&atchan->lock);
+       spin_lock_irqsave(&atchan->lock, flags);
        ret = at_xdmac_set_slave_config(chan, config);
-       spin_unlock_bh(&atchan->lock);
+       spin_unlock_irqrestore(&atchan->lock, flags);
 
        return ret;
 }
@@ -1130,18 +1173,19 @@ static int at_xdmac_device_pause(struct dma_chan *chan)
 {
        struct at_xdmac_chan    *atchan = to_at_xdmac_chan(chan);
        struct at_xdmac         *atxdmac = to_at_xdmac(atchan->chan.device);
+       unsigned long           flags;
 
        dev_dbg(chan2dev(chan), "%s\n", __func__);
 
        if (test_and_set_bit(AT_XDMAC_CHAN_IS_PAUSED, &atchan->status))
                return 0;
 
-       spin_lock_bh(&atchan->lock);
+       spin_lock_irqsave(&atchan->lock, flags);
        at_xdmac_write(atxdmac, AT_XDMAC_GRWS, atchan->mask);
        while (at_xdmac_chan_read(atchan, AT_XDMAC_CC)
               & (AT_XDMAC_CC_WRIP | AT_XDMAC_CC_RDIP))
                cpu_relax();
-       spin_unlock_bh(&atchan->lock);
+       spin_unlock_irqrestore(&atchan->lock, flags);
 
        return 0;
 }
@@ -1150,18 +1194,19 @@ static int at_xdmac_device_resume(struct dma_chan *chan)
 {
        struct at_xdmac_chan    *atchan = to_at_xdmac_chan(chan);
        struct at_xdmac         *atxdmac = to_at_xdmac(atchan->chan.device);
+       unsigned long           flags;
 
        dev_dbg(chan2dev(chan), "%s\n", __func__);
 
-       spin_lock_bh(&atchan->lock);
+       spin_lock_irqsave(&atchan->lock, flags);
        if (!at_xdmac_chan_is_paused(atchan)) {
-               spin_unlock_bh(&atchan->lock);
+               spin_unlock_irqrestore(&atchan->lock, flags);
                return 0;
        }
 
        at_xdmac_write(atxdmac, AT_XDMAC_GRWR, atchan->mask);
        clear_bit(AT_XDMAC_CHAN_IS_PAUSED, &atchan->status);
-       spin_unlock_bh(&atchan->lock);
+       spin_unlock_irqrestore(&atchan->lock, flags);
 
        return 0;
 }
@@ -1171,10 +1216,11 @@ static int at_xdmac_device_terminate_all(struct dma_chan *chan)
        struct at_xdmac_desc    *desc, *_desc;
        struct at_xdmac_chan    *atchan = to_at_xdmac_chan(chan);
        struct at_xdmac         *atxdmac = to_at_xdmac(atchan->chan.device);
+       unsigned long           flags;
 
        dev_dbg(chan2dev(chan), "%s\n", __func__);
 
-       spin_lock_bh(&atchan->lock);
+       spin_lock_irqsave(&atchan->lock, flags);
        at_xdmac_write(atxdmac, AT_XDMAC_GD, atchan->mask);
        while (at_xdmac_read(atxdmac, AT_XDMAC_GS) & atchan->mask)
                cpu_relax();
@@ -1184,7 +1230,7 @@ static int at_xdmac_device_terminate_all(struct dma_chan *chan)
                at_xdmac_remove_xfer(atchan, desc);
 
        clear_bit(AT_XDMAC_CHAN_IS_CYCLIC, &atchan->status);
-       spin_unlock_bh(&atchan->lock);
+       spin_unlock_irqrestore(&atchan->lock, flags);
 
        return 0;
 }
@@ -1194,8 +1240,9 @@ static int at_xdmac_alloc_chan_resources(struct dma_chan *chan)
        struct at_xdmac_chan    *atchan = to_at_xdmac_chan(chan);
        struct at_xdmac_desc    *desc;
        int                     i;
+       unsigned long           flags;
 
-       spin_lock_bh(&atchan->lock);
+       spin_lock_irqsave(&atchan->lock, flags);
 
        if (at_xdmac_chan_is_enabled(atchan)) {
                dev_err(chan2dev(chan),
@@ -1226,7 +1273,7 @@ static int at_xdmac_alloc_chan_resources(struct dma_chan *chan)
        dev_dbg(chan2dev(chan), "%s: allocated %d descriptors\n", __func__, i);
 
 spin_unlock:
-       spin_unlock_bh(&atchan->lock);
+       spin_unlock_irqrestore(&atchan->lock, flags);
        return i;
 }
 
index 2890d744bb1bb902cc095fb87841c492cef7542c..3ddfd1f6c23c0f0f891ed11d6f68cbcaaa3c6e03 100644 (file)
@@ -487,7 +487,11 @@ int dma_get_slave_caps(struct dma_chan *chan, struct dma_slave_caps *caps)
        caps->directions = device->directions;
        caps->residue_granularity = device->residue_granularity;
 
-       caps->cmd_pause = !!device->device_pause;
+       /*
+        * Some devices implement only pause (e.g. to get residuum) but no
+        * resume. However cmd_pause is advertised as pause AND resume.
+        */
+       caps->cmd_pause = !!(device->device_pause && device->device_resume);
        caps->cmd_terminate = !!device->device_terminate_all;
 
        return 0;
index 9b84def7a35373a45cf18efc9d53939bcc86baf0..f42f71e37e73767a55078aef4c81bcd238b02db1 100644 (file)
@@ -384,7 +384,10 @@ static int hsu_dma_terminate_all(struct dma_chan *chan)
        spin_lock_irqsave(&hsuc->vchan.lock, flags);
 
        hsu_dma_stop_channel(hsuc);
-       hsuc->desc = NULL;
+       if (hsuc->desc) {
+               hsu_dma_desc_free(&hsuc->desc->vdesc);
+               hsuc->desc = NULL;
+       }
 
        vchan_get_all_descriptors(&hsuc->vchan, &head);
        spin_unlock_irqrestore(&hsuc->vchan.lock, flags);
index 6de2e677be0401c6490d9a4b0a34a2b6b61a8450..74d9db05a5ad24beac7fb48b319d73240f5842b6 100644 (file)
@@ -22,6 +22,7 @@
 #include <linux/module.h>
 #include <linux/io.h>
 #include <linux/seq_file.h>
+#include <linux/vmalloc.h>
 
 #include "mic_x100_dma.h"
 
index a7d9d3029b145dfa29babeee33022bc1f7354d52..340f9e607cd8b90dfe75add027c18bd26d67f1e0 100644 (file)
@@ -2127,6 +2127,7 @@ static int pl330_terminate_all(struct dma_chan *chan)
        struct pl330_dmac *pl330 = pch->dmac;
        LIST_HEAD(list);
 
+       pm_runtime_get_sync(pl330->ddma.dev);
        spin_lock_irqsave(&pch->lock, flags);
        spin_lock(&pl330->lock);
        _stop(pch->thread);
@@ -2151,6 +2152,8 @@ static int pl330_terminate_all(struct dma_chan *chan)
        list_splice_tail_init(&pch->work_list, &pl330->desc_pool);
        list_splice_tail_init(&pch->completed_list, &pl330->desc_pool);
        spin_unlock_irqrestore(&pch->lock, flags);
+       pm_runtime_mark_last_busy(pl330->ddma.dev);
+       pm_runtime_put_autosuspend(pl330->ddma.dev);
 
        return 0;
 }
index c22606fe3d44bf06d273a657917a12fda4ce39de..6bac03999fd499d78bec4e1f476539051533faaa 100644 (file)
@@ -1611,7 +1611,6 @@ static struct scsi_host_template scsi_driver_template = {
        .this_id                = -1,
        .sg_tablesize           = SG_ALL,
        .use_clustering         = ENABLE_CLUSTERING,
-       .cmd_per_lun            = 1,
        .can_queue              = 1,
        .sdev_attrs             = sbp2_scsi_sysfs_attrs,
 };
index 8de4da5c9ab69c919389057078d352fd492fbcd3..54071c1483400d41e214c0f83512ca1f4600814a 100644 (file)
@@ -18,6 +18,11 @@ config EFI_VARS
          Subsequent efibootmgr releases may be found at:
          <http://github.com/vathpela/efibootmgr>
 
+config EFI_ESRT
+       bool
+       depends on EFI && !IA64
+       default y
+
 config EFI_VARS_PSTORE
        tristate "Register efivars backend for pstore"
        depends on EFI_VARS && PSTORE
index d8be608a9f3be733bf4d56a1e360ecc3c5471e7b..6fd3da938717c27c233af9c6b66b186f69fc93fa 100644 (file)
@@ -3,6 +3,7 @@
 #
 obj-$(CONFIG_EFI)                      += efi.o vars.o reboot.o
 obj-$(CONFIG_EFI_VARS)                 += efivars.o
+obj-$(CONFIG_EFI_ESRT)                 += esrt.o
 obj-$(CONFIG_EFI_VARS_PSTORE)          += efi-pstore.o
 obj-$(CONFIG_UEFI_CPER)                        += cper.o
 obj-$(CONFIG_EFI_RUNTIME_MAP)          += runtime-map.o
index 3061bb8629dc3fbdf19e19d373d75b2286cf8376..ca617f40574ac2bb8e5697b858a1c52f93c8fad9 100644 (file)
@@ -39,6 +39,7 @@ struct efi __read_mostly efi = {
        .fw_vendor  = EFI_INVALID_TABLE_ADDR,
        .runtime    = EFI_INVALID_TABLE_ADDR,
        .config_table  = EFI_INVALID_TABLE_ADDR,
+       .esrt       = EFI_INVALID_TABLE_ADDR,
 };
 EXPORT_SYMBOL(efi);
 
@@ -64,7 +65,7 @@ static int __init parse_efi_cmdline(char *str)
 }
 early_param("efi", parse_efi_cmdline);
 
-static struct kobject *efi_kobj;
+struct kobject *efi_kobj;
 static struct kobject *efivars_kobj;
 
 /*
@@ -85,10 +86,15 @@ static ssize_t systab_show(struct kobject *kobj,
                str += sprintf(str, "ACPI20=0x%lx\n", efi.acpi20);
        if (efi.acpi != EFI_INVALID_TABLE_ADDR)
                str += sprintf(str, "ACPI=0x%lx\n", efi.acpi);
-       if (efi.smbios != EFI_INVALID_TABLE_ADDR)
-               str += sprintf(str, "SMBIOS=0x%lx\n", efi.smbios);
+       /*
+        * If both SMBIOS and SMBIOS3 entry points are implemented, the
+        * SMBIOS3 entry point shall be preferred, so we list it first to
+        * let applications stop parsing after the first match.
+        */
        if (efi.smbios3 != EFI_INVALID_TABLE_ADDR)
                str += sprintf(str, "SMBIOS3=0x%lx\n", efi.smbios3);
+       if (efi.smbios != EFI_INVALID_TABLE_ADDR)
+               str += sprintf(str, "SMBIOS=0x%lx\n", efi.smbios);
        if (efi.hcdp != EFI_INVALID_TABLE_ADDR)
                str += sprintf(str, "HCDP=0x%lx\n", efi.hcdp);
        if (efi.boot_info != EFI_INVALID_TABLE_ADDR)
@@ -232,6 +238,84 @@ err_put:
 
 subsys_initcall(efisubsys_init);
 
+/*
+ * Find the efi memory descriptor for a given physical address.  Given a
+ * physicall address, determine if it exists within an EFI Memory Map entry,
+ * and if so, populate the supplied memory descriptor with the appropriate
+ * data.
+ */
+int __init efi_mem_desc_lookup(u64 phys_addr, efi_memory_desc_t *out_md)
+{
+       struct efi_memory_map *map = efi.memmap;
+       void *p, *e;
+
+       if (!efi_enabled(EFI_MEMMAP)) {
+               pr_err_once("EFI_MEMMAP is not enabled.\n");
+               return -EINVAL;
+       }
+
+       if (!map) {
+               pr_err_once("efi.memmap is not set.\n");
+               return -EINVAL;
+       }
+       if (!out_md) {
+               pr_err_once("out_md is null.\n");
+               return -EINVAL;
+        }
+       if (WARN_ON_ONCE(!map->phys_map))
+               return -EINVAL;
+       if (WARN_ON_ONCE(map->nr_map == 0) || WARN_ON_ONCE(map->desc_size == 0))
+               return -EINVAL;
+
+       e = map->phys_map + map->nr_map * map->desc_size;
+       for (p = map->phys_map; p < e; p += map->desc_size) {
+               efi_memory_desc_t *md;
+               u64 size;
+               u64 end;
+
+               /*
+                * If a driver calls this after efi_free_boot_services,
+                * ->map will be NULL, and the target may also not be mapped.
+                * So just always get our own virtual map on the CPU.
+                *
+                */
+               md = early_memremap((phys_addr_t)p, sizeof (*md));
+               if (!md) {
+                       pr_err_once("early_memremap(%p, %zu) failed.\n",
+                                   p, sizeof (*md));
+                       return -ENOMEM;
+               }
+
+               if (!(md->attribute & EFI_MEMORY_RUNTIME) &&
+                   md->type != EFI_BOOT_SERVICES_DATA &&
+                   md->type != EFI_RUNTIME_SERVICES_DATA) {
+                       early_memunmap(md, sizeof (*md));
+                       continue;
+               }
+
+               size = md->num_pages << EFI_PAGE_SHIFT;
+               end = md->phys_addr + size;
+               if (phys_addr >= md->phys_addr && phys_addr < end) {
+                       memcpy(out_md, md, sizeof(*out_md));
+                       early_memunmap(md, sizeof (*md));
+                       return 0;
+               }
+
+               early_memunmap(md, sizeof (*md));
+       }
+       pr_err_once("requested map not found.\n");
+       return -ENOENT;
+}
+
+/*
+ * Calculate the highest address of an efi memory descriptor.
+ */
+u64 __init efi_mem_desc_end(efi_memory_desc_t *md)
+{
+       u64 size = md->num_pages << EFI_PAGE_SHIFT;
+       u64 end = md->phys_addr + size;
+       return end;
+}
 
 /*
  * We can't ioremap data in EFI boot services RAM, because we've already mapped
@@ -274,6 +358,7 @@ static __initdata efi_config_table_type_t common_tables[] = {
        {SMBIOS_TABLE_GUID, "SMBIOS", &efi.smbios},
        {SMBIOS3_TABLE_GUID, "SMBIOS 3.0", &efi.smbios3},
        {UGA_IO_PROTOCOL_GUID, "UGA", &efi.uga},
+       {EFI_SYSTEM_RESOURCE_TABLE_GUID, "ESRT", &efi.esrt},
        {NULL_GUID, NULL, NULL},
 };
 
index 7b2e0496e0c084c4e9e319c04d61245abdde5edd..756eca8c4cf8f291025a3ad44f7cbb9981aeb5fd 100644 (file)
@@ -535,7 +535,7 @@ static ssize_t efivar_delete(struct file *filp, struct kobject *kobj,
  * efivar_create_sysfs_entry - create a new entry in sysfs
  * @new_var: efivar entry to create
  *
- * Returns 1 on failure, 0 on success
+ * Returns 0 on success, negative error code on failure
  */
 static int
 efivar_create_sysfs_entry(struct efivar_entry *new_var)
@@ -544,6 +544,7 @@ efivar_create_sysfs_entry(struct efivar_entry *new_var)
        char *short_name;
        unsigned long variable_name_size;
        efi_char16_t *variable_name;
+       int ret;
 
        variable_name = new_var->var.VariableName;
        variable_name_size = ucs2_strlen(variable_name) * sizeof(efi_char16_t);
@@ -558,7 +559,7 @@ efivar_create_sysfs_entry(struct efivar_entry *new_var)
        short_name = kzalloc(short_name_size, GFP_KERNEL);
 
        if (!short_name)
-               return 1;
+               return -ENOMEM;
 
        /* Convert Unicode to normal chars (assume top bits are 0),
           ala UTF-8 */
@@ -574,11 +575,11 @@ efivar_create_sysfs_entry(struct efivar_entry *new_var)
 
        new_var->kobj.kset = efivars_kset;
 
-       i = kobject_init_and_add(&new_var->kobj, &efivar_ktype,
+       ret = kobject_init_and_add(&new_var->kobj, &efivar_ktype,
                                   NULL, "%s", short_name);
        kfree(short_name);
-       if (i)
-               return 1;
+       if (ret)
+               return ret;
 
        kobject_uevent(&new_var->kobj, KOBJ_ADD);
        efivar_entry_add(new_var, &efivar_sysfs_list);
diff --git a/drivers/firmware/efi/esrt.c b/drivers/firmware/efi/esrt.c
new file mode 100644 (file)
index 0000000..a5b95d6
--- /dev/null
@@ -0,0 +1,471 @@
+/*
+ * esrt.c
+ *
+ * This module exports EFI System Resource Table (ESRT) entries into userspace
+ * through the sysfs file system. The ESRT provides a read-only catalog of
+ * system components for which the system accepts firmware upgrades via UEFI's
+ * "Capsule Update" feature. This module allows userland utilities to evaluate
+ * what firmware updates can be applied to this system, and potentially arrange
+ * for those updates to occur.
+ *
+ * Data is currently found below /sys/firmware/efi/esrt/...
+ */
+#define pr_fmt(fmt) "esrt: " fmt
+
+#include <linux/capability.h>
+#include <linux/device.h>
+#include <linux/efi.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/kobject.h>
+#include <linux/list.h>
+#include <linux/memblock.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+#include <asm/io.h>
+#include <asm/early_ioremap.h>
+
+struct efi_system_resource_entry_v1 {
+       efi_guid_t      fw_class;
+       u32             fw_type;
+       u32             fw_version;
+       u32             lowest_supported_fw_version;
+       u32             capsule_flags;
+       u32             last_attempt_version;
+       u32             last_attempt_status;
+};
+
+/*
+ * _count and _version are what they seem like.  _max is actually just
+ * accounting info for the firmware when creating the table; it should never
+ * have been exposed to us.  To wit, the spec says:
+ * The maximum number of resource array entries that can be within the
+ * table without reallocating the table, must not be zero.
+ * Since there's no guidance about what that means in terms of memory layout,
+ * it means nothing to us.
+ */
+struct efi_system_resource_table {
+       u32     fw_resource_count;
+       u32     fw_resource_count_max;
+       u64     fw_resource_version;
+       u8      entries[];
+};
+
+static phys_addr_t esrt_data;
+static size_t esrt_data_size;
+
+static struct efi_system_resource_table *esrt;
+
+struct esre_entry {
+       union {
+               struct efi_system_resource_entry_v1 *esre1;
+       } esre;
+
+       struct kobject kobj;
+       struct list_head list;
+};
+
+/* global list of esre_entry. */
+static LIST_HEAD(entry_list);
+
+/* entry attribute */
+struct esre_attribute {
+       struct attribute attr;
+       ssize_t (*show)(struct esre_entry *entry, char *buf);
+       ssize_t (*store)(struct esre_entry *entry,
+                        const char *buf, size_t count);
+};
+
+static struct esre_entry *to_entry(struct kobject *kobj)
+{
+       return container_of(kobj, struct esre_entry, kobj);
+}
+
+static struct esre_attribute *to_attr(struct attribute *attr)
+{
+       return container_of(attr, struct esre_attribute, attr);
+}
+
+static ssize_t esre_attr_show(struct kobject *kobj,
+                             struct attribute *_attr, char *buf)
+{
+       struct esre_entry *entry = to_entry(kobj);
+       struct esre_attribute *attr = to_attr(_attr);
+
+       /* Don't tell normal users what firmware versions we've got... */
+       if (!capable(CAP_SYS_ADMIN))
+               return -EACCES;
+
+       return attr->show(entry, buf);
+}
+
+static const struct sysfs_ops esre_attr_ops = {
+       .show = esre_attr_show,
+};
+
+/* Generic ESRT Entry ("ESRE") support. */
+static ssize_t esre_fw_class_show(struct esre_entry *entry, char *buf)
+{
+       char *str = buf;
+
+       efi_guid_to_str(&entry->esre.esre1->fw_class, str);
+       str += strlen(str);
+       str += sprintf(str, "\n");
+
+       return str - buf;
+}
+
+static struct esre_attribute esre_fw_class = __ATTR(fw_class, 0400,
+       esre_fw_class_show, NULL);
+
+#define esre_attr_decl(name, size, fmt) \
+static ssize_t esre_##name##_show(struct esre_entry *entry, char *buf) \
+{ \
+       return sprintf(buf, fmt "\n", \
+                      le##size##_to_cpu(entry->esre.esre1->name)); \
+} \
+\
+static struct esre_attribute esre_##name = __ATTR(name, 0400, \
+       esre_##name##_show, NULL)
+
+esre_attr_decl(fw_type, 32, "%u");
+esre_attr_decl(fw_version, 32, "%u");
+esre_attr_decl(lowest_supported_fw_version, 32, "%u");
+esre_attr_decl(capsule_flags, 32, "0x%x");
+esre_attr_decl(last_attempt_version, 32, "%u");
+esre_attr_decl(last_attempt_status, 32, "%u");
+
+static struct attribute *esre1_attrs[] = {
+       &esre_fw_class.attr,
+       &esre_fw_type.attr,
+       &esre_fw_version.attr,
+       &esre_lowest_supported_fw_version.attr,
+       &esre_capsule_flags.attr,
+       &esre_last_attempt_version.attr,
+       &esre_last_attempt_status.attr,
+       NULL
+};
+static void esre_release(struct kobject *kobj)
+{
+       struct esre_entry *entry = to_entry(kobj);
+
+       list_del(&entry->list);
+       kfree(entry);
+}
+
+static struct kobj_type esre1_ktype = {
+       .release = esre_release,
+       .sysfs_ops = &esre_attr_ops,
+       .default_attrs = esre1_attrs,
+};
+
+
+static struct kobject *esrt_kobj;
+static struct kset *esrt_kset;
+
+static int esre_create_sysfs_entry(void *esre, int entry_num)
+{
+       struct esre_entry *entry;
+       char name[20];
+
+       entry = kzalloc(sizeof(*entry), GFP_KERNEL);
+       if (!entry)
+               return -ENOMEM;
+
+       sprintf(name, "entry%d", entry_num);
+
+       entry->kobj.kset = esrt_kset;
+
+       if (esrt->fw_resource_version == 1) {
+               int rc = 0;
+
+               entry->esre.esre1 = esre;
+               rc = kobject_init_and_add(&entry->kobj, &esre1_ktype, NULL,
+                                         "%s", name);
+               if (rc) {
+                       kfree(entry);
+                       return rc;
+               }
+       }
+
+       list_add_tail(&entry->list, &entry_list);
+       return 0;
+}
+
+/* support for displaying ESRT fields at the top level */
+#define esrt_attr_decl(name, size, fmt) \
+static ssize_t esrt_##name##_show(struct kobject *kobj, \
+                                 struct kobj_attribute *attr, char *buf)\
+{ \
+       return sprintf(buf, fmt "\n", le##size##_to_cpu(esrt->name)); \
+} \
+\
+static struct kobj_attribute esrt_##name = __ATTR(name, 0400, \
+       esrt_##name##_show, NULL)
+
+esrt_attr_decl(fw_resource_count, 32, "%u");
+esrt_attr_decl(fw_resource_count_max, 32, "%u");
+esrt_attr_decl(fw_resource_version, 64, "%llu");
+
+static struct attribute *esrt_attrs[] = {
+       &esrt_fw_resource_count.attr,
+       &esrt_fw_resource_count_max.attr,
+       &esrt_fw_resource_version.attr,
+       NULL,
+};
+
+static inline int esrt_table_exists(void)
+{
+       if (!efi_enabled(EFI_CONFIG_TABLES))
+               return 0;
+       if (efi.esrt == EFI_INVALID_TABLE_ADDR)
+               return 0;
+       return 1;
+}
+
+static umode_t esrt_attr_is_visible(struct kobject *kobj,
+                                   struct attribute *attr, int n)
+{
+       if (!esrt_table_exists())
+               return 0;
+       return attr->mode;
+}
+
+static struct attribute_group esrt_attr_group = {
+       .attrs = esrt_attrs,
+       .is_visible = esrt_attr_is_visible,
+};
+
+/*
+ * remap the table, copy it to kmalloced pages, and unmap it.
+ */
+void __init efi_esrt_init(void)
+{
+       void *va;
+       struct efi_system_resource_table tmpesrt;
+       struct efi_system_resource_entry_v1 *v1_entries;
+       size_t size, max, entry_size, entries_size;
+       efi_memory_desc_t md;
+       int rc;
+       phys_addr_t end;
+
+       pr_debug("esrt-init: loading.\n");
+       if (!esrt_table_exists())
+               return;
+
+       rc = efi_mem_desc_lookup(efi.esrt, &md);
+       if (rc < 0) {
+               pr_err("ESRT header is not in the memory map.\n");
+               return;
+       }
+
+       max = efi_mem_desc_end(&md);
+       if (max < efi.esrt) {
+               pr_err("EFI memory descriptor is invalid. (esrt: %p max: %p)\n",
+                      (void *)efi.esrt, (void *)max);
+               return;
+       }
+
+       size = sizeof(*esrt);
+       max -= efi.esrt;
+
+       if (max < size) {
+               pr_err("ESRT header doen't fit on single memory map entry. (size: %zu max: %zu)\n",
+                      size, max);
+               return;
+       }
+
+       va = early_memremap(efi.esrt, size);
+       if (!va) {
+               pr_err("early_memremap(%p, %zu) failed.\n", (void *)efi.esrt,
+                      size);
+               return;
+       }
+
+       memcpy(&tmpesrt, va, sizeof(tmpesrt));
+
+       if (tmpesrt.fw_resource_version == 1) {
+               entry_size = sizeof (*v1_entries);
+       } else {
+               pr_err("Unsupported ESRT version %lld.\n",
+                      tmpesrt.fw_resource_version);
+               return;
+       }
+
+       if (tmpesrt.fw_resource_count > 0 && max - size < entry_size) {
+               pr_err("ESRT memory map entry can only hold the header. (max: %zu size: %zu)\n",
+                      max - size, entry_size);
+               goto err_memunmap;
+       }
+
+       /*
+        * The format doesn't really give us any boundary to test here,
+        * so I'm making up 128 as the max number of individually updatable
+        * components we support.
+        * 128 should be pretty excessive, but there's still some chance
+        * somebody will do that someday and we'll need to raise this.
+        */
+       if (tmpesrt.fw_resource_count > 128) {
+               pr_err("ESRT says fw_resource_count has very large value %d.\n",
+                      tmpesrt.fw_resource_count);
+               goto err_memunmap;
+       }
+
+       /*
+        * We know it can't be larger than N * sizeof() here, and N is limited
+        * by the previous test to a small number, so there's no overflow.
+        */
+       entries_size = tmpesrt.fw_resource_count * entry_size;
+       if (max < size + entries_size) {
+               pr_err("ESRT does not fit on single memory map entry (size: %zu max: %zu)\n",
+                      size, max);
+               goto err_memunmap;
+       }
+
+       /* remap it with our (plausible) new pages */
+       early_memunmap(va, size);
+       size += entries_size;
+       va = early_memremap(efi.esrt, size);
+       if (!va) {
+               pr_err("early_memremap(%p, %zu) failed.\n", (void *)efi.esrt,
+                      size);
+               return;
+       }
+
+       esrt_data = (phys_addr_t)efi.esrt;
+       esrt_data_size = size;
+
+       end = esrt_data + size;
+       pr_info("Reserving ESRT space from %pa to %pa.\n", &esrt_data, &end);
+       memblock_reserve(esrt_data, esrt_data_size);
+
+       pr_debug("esrt-init: loaded.\n");
+err_memunmap:
+       early_memunmap(va, size);
+}
+
+static int __init register_entries(void)
+{
+       struct efi_system_resource_entry_v1 *v1_entries = (void *)esrt->entries;
+       int i, rc;
+
+       if (!esrt_table_exists())
+               return 0;
+
+       for (i = 0; i < le32_to_cpu(esrt->fw_resource_count); i++) {
+               void *esre = NULL;
+               if (esrt->fw_resource_version == 1) {
+                       esre = &v1_entries[i];
+               } else {
+                       pr_err("Unsupported ESRT version %lld.\n",
+                              esrt->fw_resource_version);
+                       return -EINVAL;
+               }
+
+               rc = esre_create_sysfs_entry(esre, i);
+               if (rc < 0) {
+                       pr_err("ESRT entry creation failed with error %d.\n",
+                              rc);
+                       return rc;
+               }
+       }
+       return 0;
+}
+
+static void cleanup_entry_list(void)
+{
+       struct esre_entry *entry, *next;
+
+       list_for_each_entry_safe(entry, next, &entry_list, list) {
+               kobject_put(&entry->kobj);
+       }
+}
+
+static int __init esrt_sysfs_init(void)
+{
+       int error;
+       struct efi_system_resource_table __iomem *ioesrt;
+
+       pr_debug("esrt-sysfs: loading.\n");
+       if (!esrt_data || !esrt_data_size)
+               return -ENOSYS;
+
+       ioesrt = ioremap(esrt_data, esrt_data_size);
+       if (!ioesrt) {
+               pr_err("ioremap(%pa, %zu) failed.\n", &esrt_data,
+                      esrt_data_size);
+               return -ENOMEM;
+       }
+
+       esrt = kmalloc(esrt_data_size, GFP_KERNEL);
+       if (!esrt) {
+               pr_err("kmalloc failed. (wanted %zu bytes)\n", esrt_data_size);
+               iounmap(ioesrt);
+               return -ENOMEM;
+       }
+
+       memcpy_fromio(esrt, ioesrt, esrt_data_size);
+
+       esrt_kobj = kobject_create_and_add("esrt", efi_kobj);
+       if (!esrt_kobj) {
+               pr_err("Firmware table registration failed.\n");
+               error = -ENOMEM;
+               goto err;
+       }
+
+       error = sysfs_create_group(esrt_kobj, &esrt_attr_group);
+       if (error) {
+               pr_err("Sysfs attribute export failed with error %d.\n",
+                      error);
+               goto err_remove_esrt;
+       }
+
+       esrt_kset = kset_create_and_add("entries", NULL, esrt_kobj);
+       if (!esrt_kset) {
+               pr_err("kset creation failed.\n");
+               error = -ENOMEM;
+               goto err_remove_group;
+       }
+
+       error = register_entries();
+       if (error)
+               goto err_cleanup_list;
+
+       memblock_remove(esrt_data, esrt_data_size);
+
+       pr_debug("esrt-sysfs: loaded.\n");
+
+       return 0;
+err_cleanup_list:
+       cleanup_entry_list();
+       kset_unregister(esrt_kset);
+err_remove_group:
+       sysfs_remove_group(esrt_kobj, &esrt_attr_group);
+err_remove_esrt:
+       kobject_put(esrt_kobj);
+err:
+       kfree(esrt);
+       esrt = NULL;
+       return error;
+}
+
+static void __exit esrt_sysfs_exit(void)
+{
+       pr_debug("esrt-sysfs: unloading.\n");
+       cleanup_entry_list();
+       kset_unregister(esrt_kset);
+       sysfs_remove_group(esrt_kobj, &esrt_attr_group);
+       kfree(esrt);
+       esrt = NULL;
+       kobject_del(esrt_kobj);
+       kobject_put(esrt_kobj);
+}
+
+module_init(esrt_sysfs_init);
+module_exit(esrt_sysfs_exit);
+
+MODULE_AUTHOR("Peter Jones <pjones@redhat.com>");
+MODULE_DESCRIPTION("EFI System Resource Table support");
+MODULE_LICENSE("GPL");
index 071c2c969eec06ad929ecfb871c614297a615e9e..72791232e46ba44ff474cf7dadff5ccb433d7348 100644 (file)
@@ -186,8 +186,20 @@ struct ibft_kobject {
 
 static struct iscsi_boot_kset *boot_kset;
 
+/* fully null address */
 static const char nulls[16];
 
+/* IPv4-mapped IPv6 ::ffff:0.0.0.0 */
+static const char mapped_nulls[16] = { 0x00, 0x00, 0x00, 0x00,
+                                       0x00, 0x00, 0x00, 0x00,
+                                       0x00, 0x00, 0xff, 0xff,
+                                       0x00, 0x00, 0x00, 0x00 };
+
+static int address_not_null(u8 *ip)
+{
+       return (memcmp(ip, nulls, 16) && memcmp(ip, mapped_nulls, 16));
+}
+
 /*
  * Helper functions to parse data properly.
  */
@@ -445,7 +457,7 @@ static umode_t ibft_check_nic_for(void *data, int type)
                rc = S_IRUGO;
                break;
        case ISCSI_BOOT_ETH_IP_ADDR:
-               if (memcmp(nic->ip_addr, nulls, sizeof(nic->ip_addr)))
+               if (address_not_null(nic->ip_addr))
                        rc = S_IRUGO;
                break;
        case ISCSI_BOOT_ETH_SUBNET_MASK:
@@ -456,21 +468,19 @@ static umode_t ibft_check_nic_for(void *data, int type)
                rc = S_IRUGO;
                break;
        case ISCSI_BOOT_ETH_GATEWAY:
-               if (memcmp(nic->gateway, nulls, sizeof(nic->gateway)))
+               if (address_not_null(nic->gateway))
                        rc = S_IRUGO;
                break;
        case ISCSI_BOOT_ETH_PRIMARY_DNS:
-               if (memcmp(nic->primary_dns, nulls,
-                          sizeof(nic->primary_dns)))
+               if (address_not_null(nic->primary_dns))
                        rc = S_IRUGO;
                break;
        case ISCSI_BOOT_ETH_SECONDARY_DNS:
-               if (memcmp(nic->secondary_dns, nulls,
-                          sizeof(nic->secondary_dns)))
+               if (address_not_null(nic->secondary_dns))
                        rc = S_IRUGO;
                break;
        case ISCSI_BOOT_ETH_DHCP:
-               if (memcmp(nic->dhcp, nulls, sizeof(nic->dhcp)))
+               if (address_not_null(nic->dhcp))
                        rc = S_IRUGO;
                break;
        case ISCSI_BOOT_ETH_VLAN:
@@ -536,23 +546,19 @@ static umode_t __init ibft_check_initiator_for(void *data, int type)
                rc = S_IRUGO;
                break;
        case ISCSI_BOOT_INI_ISNS_SERVER:
-               if (memcmp(init->isns_server, nulls,
-                          sizeof(init->isns_server)))
+               if (address_not_null(init->isns_server))
                        rc = S_IRUGO;
                break;
        case ISCSI_BOOT_INI_SLP_SERVER:
-               if (memcmp(init->slp_server, nulls,
-                          sizeof(init->slp_server)))
+               if (address_not_null(init->slp_server))
                        rc = S_IRUGO;
                break;
        case ISCSI_BOOT_INI_PRI_RADIUS_SERVER:
-               if (memcmp(init->pri_radius_server, nulls,
-                          sizeof(init->pri_radius_server)))
+               if (address_not_null(init->pri_radius_server))
                        rc = S_IRUGO;
                break;
        case ISCSI_BOOT_INI_SEC_RADIUS_SERVER:
-               if (memcmp(init->sec_radius_server, nulls,
-                          sizeof(init->sec_radius_server)))
+               if (address_not_null(init->sec_radius_server))
                        rc = S_IRUGO;
                break;
        case ISCSI_BOOT_INI_INITIATOR_NAME:
index caefe806db5e6dfaff04a22b2efb0cf27111e33c..8f1fe739c985ef555d35372e292d74b31e99118a 100644 (file)
@@ -126,6 +126,14 @@ config GPIO_BCM_KONA
        help
          Turn on GPIO support for Broadcom "Kona" chips.
 
+config GPIO_BRCMSTB
+       tristate "BRCMSTB GPIO support"
+       default y if ARCH_BRCMSTB
+       depends on OF_GPIO && (ARCH_BRCMSTB || COMPILE_TEST)
+       select GPIO_GENERIC
+       help
+         Say yes here to enable GPIO support for Broadcom STB (BCM7XXX) SoCs.
+
 config GPIO_CLPS711X
        tristate "CLPS711X GPIO support"
        depends on ARCH_CLPS711X || COMPILE_TEST
@@ -159,6 +167,14 @@ config GPIO_EP93XX
        depends on ARCH_EP93XX
        select GPIO_GENERIC
 
+config GPIO_ETRAXFS
+       bool "Axis ETRAX FS General I/O"
+       depends on CRIS || COMPILE_TEST
+       depends on OF
+       select GPIO_GENERIC
+       help
+         Say yes here to support the GPIO controller on Axis ETRAX FS SoCs.
+
 config GPIO_F7188X
        tristate "F71869, F71869A, F71882FG and F71889F GPIO support"
        depends on X86
@@ -230,6 +246,14 @@ config GPIO_LOONGSON
        help
          driver for GPIO functionality on Loongson-2F/3A/3B processors.
 
+config GPIO_LPC18XX
+       bool "NXP LPC18XX/43XX GPIO support"
+       default y if ARCH_LPC18XX
+       depends on OF_GPIO && (ARCH_LPC18XX || COMPILE_TEST)
+       help
+         Select this option to enable GPIO driver for
+         NXP LPC18XX/43XX devices.
+
 config GPIO_LYNXPOINT
        tristate "Intel Lynxpoint GPIO support"
        depends on ACPI && X86
@@ -308,7 +332,7 @@ config GPIO_OCTEON
          family of SOCs.
 
 config GPIO_OMAP
-       bool "TI OMAP GPIO support" if COMPILE_TEST && !ARCH_OMAP2PLUS
+       tristate "TI OMAP GPIO support" if ARCH_OMAP2PLUS || COMPILE_TEST
        default y if ARCH_OMAP
        depends on ARM
        select GENERIC_IRQ_CHIP
@@ -488,6 +512,17 @@ config GPIO_XILINX
        help
          Say yes here to support the Xilinx FPGA GPIO device
 
+config GPIO_XLP
+       tristate "Netlogic XLP GPIO support"
+       depends on CPU_XLP
+       select GPIOLIB_IRQCHIP
+       help
+         This driver provides support for GPIO interface on Netlogic XLP MIPS64
+         SoCs. Currently supported XLP variants are XLP8XX, XLP3XX, XLP2XX,
+         XLP9XX and XLP5XX.
+
+         If unsure, say N.
+
 config GPIO_XTENSA
        bool "Xtensa GPIO32 support"
        depends on XTENSA
@@ -505,7 +540,7 @@ config GPIO_ZEVIO
 
 config GPIO_ZYNQ
        tristate "Xilinx Zynq GPIO support"
-       depends on ARCH_ZYNQ
+       depends on ARCH_ZYNQ || ARCH_ZYNQMP
        select GPIOLIB_IRQCHIP
        help
          Say yes here to support Xilinx Zynq GPIO controller.
index f71bb971329c9efe96f2bc58c2ab63f2d1673d69..f82cd678ce086e68da421506aceaed8d7b4f458b 100644 (file)
@@ -21,6 +21,7 @@ obj-$(CONFIG_GPIO_ALTERA)     += gpio-altera.o
 obj-$(CONFIG_GPIO_AMD8111)     += gpio-amd8111.o
 obj-$(CONFIG_GPIO_ARIZONA)     += gpio-arizona.o
 obj-$(CONFIG_GPIO_BCM_KONA)    += gpio-bcm-kona.o
+obj-$(CONFIG_GPIO_BRCMSTB)     += gpio-brcmstb.o
 obj-$(CONFIG_GPIO_BT8XX)       += gpio-bt8xx.o
 obj-$(CONFIG_GPIO_CLPS711X)    += gpio-clps711x.o
 obj-$(CONFIG_GPIO_CS5535)      += gpio-cs5535.o
@@ -32,6 +33,7 @@ obj-$(CONFIG_GPIO_DLN2)               += gpio-dln2.o
 obj-$(CONFIG_GPIO_DWAPB)       += gpio-dwapb.o
 obj-$(CONFIG_GPIO_EM)          += gpio-em.o
 obj-$(CONFIG_GPIO_EP93XX)      += gpio-ep93xx.o
+obj-$(CONFIG_GPIO_ETRAXFS)     += gpio-etraxfs.o
 obj-$(CONFIG_GPIO_F7188X)      += gpio-f7188x.o
 obj-$(CONFIG_GPIO_GE_FPGA)     += gpio-ge.o
 obj-$(CONFIG_GPIO_GRGPIO)      += gpio-grgpio.o
@@ -44,6 +46,7 @@ obj-$(CONFIG_ARCH_KS8695)     += gpio-ks8695.o
 obj-$(CONFIG_GPIO_INTEL_MID)   += gpio-intel-mid.o
 obj-$(CONFIG_GPIO_LOONGSON)    += gpio-loongson.o
 obj-$(CONFIG_GPIO_LP3943)      += gpio-lp3943.o
+obj-$(CONFIG_GPIO_LPC18XX)     += gpio-lpc18xx.o
 obj-$(CONFIG_ARCH_LPC32XX)     += gpio-lpc32xx.o
 obj-$(CONFIG_GPIO_LYNXPOINT)   += gpio-lynxpoint.o
 obj-$(CONFIG_GPIO_MAX730X)     += gpio-max730x.o
@@ -109,6 +112,7 @@ obj-$(CONFIG_GPIO_WM8994)   += gpio-wm8994.o
 obj-$(CONFIG_GPIO_XGENE)       += gpio-xgene.o
 obj-$(CONFIG_GPIO_XGENE_SB)    += gpio-xgene-sb.o
 obj-$(CONFIG_GPIO_XILINX)      += gpio-xilinx.o
+obj-$(CONFIG_GPIO_XLP)         += gpio-xlp.o
 obj-$(CONFIG_GPIO_XTENSA)      += gpio-xtensa.o
 obj-$(CONFIG_GPIO_ZEVIO)       += gpio-zevio.o
 obj-$(CONFIG_GPIO_ZYNQ)                += gpio-zynq.o
index 449fb46cb8a0c7205401d2b9f57d14e040cc1122..0f3d336d6303e9c4745f8da260ae5e7df8299805 100644 (file)
@@ -107,7 +107,8 @@ static int altera_gpio_irq_set_type(struct irq_data *d,
        return -EINVAL;
 }
 
-static unsigned int altera_gpio_irq_startup(struct irq_data *d) {
+static unsigned int altera_gpio_irq_startup(struct irq_data *d)
+{
        altera_gpio_irq_unmask(d);
 
        return 0;
index b164ce837b43fbd9f9ddda6ed93353d66ac33d45..a6e79225886d2e42a4f506ecf43a9e5b402c46fb 100644 (file)
@@ -122,6 +122,16 @@ static void bcm_kona_gpio_unlock_gpio(struct bcm_kona_gpio *kona_gpio,
        spin_unlock_irqrestore(&kona_gpio->lock, flags);
 }
 
+static int bcm_kona_gpio_get_dir(struct gpio_chip *chip, unsigned gpio)
+{
+       struct bcm_kona_gpio *kona_gpio = to_kona_gpio(chip);
+       void __iomem *reg_base = kona_gpio->reg_base;
+       u32 val;
+
+       val = readl(reg_base + GPIO_CONTROL(gpio)) & GPIO_GPCTR0_IOTR_MASK;
+       return val ? GPIOF_DIR_IN : GPIOF_DIR_OUT;
+}
+
 static void bcm_kona_gpio_set(struct gpio_chip *chip, unsigned gpio, int value)
 {
        struct bcm_kona_gpio *kona_gpio;
@@ -135,12 +145,8 @@ static void bcm_kona_gpio_set(struct gpio_chip *chip, unsigned gpio, int value)
        reg_base = kona_gpio->reg_base;
        spin_lock_irqsave(&kona_gpio->lock, flags);
 
-       /* determine the GPIO pin direction */
-       val = readl(reg_base + GPIO_CONTROL(gpio));
-       val &= GPIO_GPCTR0_IOTR_MASK;
-
        /* this function only applies to output pin */
-       if (GPIO_GPCTR0_IOTR_CMD_INPUT == val)
+       if (bcm_kona_gpio_get_dir(chip, gpio) == GPIOF_DIR_IN)
                goto out;
 
        reg_offset = value ? GPIO_OUT_SET(bank_id) : GPIO_OUT_CLEAR(bank_id);
@@ -166,13 +172,12 @@ static int bcm_kona_gpio_get(struct gpio_chip *chip, unsigned gpio)
        reg_base = kona_gpio->reg_base;
        spin_lock_irqsave(&kona_gpio->lock, flags);
 
-       /* determine the GPIO pin direction */
-       val = readl(reg_base + GPIO_CONTROL(gpio));
-       val &= GPIO_GPCTR0_IOTR_MASK;
+       if (bcm_kona_gpio_get_dir(chip, gpio) == GPIOF_DIR_IN)
+               reg_offset = GPIO_IN_STATUS(bank_id);
+       else
+               reg_offset = GPIO_OUT_STATUS(bank_id);
 
        /* read the GPIO bank status */
-       reg_offset = (GPIO_GPCTR0_IOTR_CMD_INPUT == val) ?
-           GPIO_IN_STATUS(bank_id) : GPIO_OUT_STATUS(bank_id);
        val = readl(reg_base + reg_offset);
 
        spin_unlock_irqrestore(&kona_gpio->lock, flags);
@@ -310,6 +315,7 @@ static struct gpio_chip template_chip = {
        .owner = THIS_MODULE,
        .request = bcm_kona_gpio_request,
        .free = bcm_kona_gpio_free,
+       .get_direction = bcm_kona_gpio_get_dir,
        .direction_input = bcm_kona_gpio_direction_input,
        .get = bcm_kona_gpio_get,
        .direction_output = bcm_kona_gpio_direction_output,
diff --git a/drivers/gpio/gpio-brcmstb.c b/drivers/gpio/gpio-brcmstb.c
new file mode 100644 (file)
index 0000000..7a3cb1f
--- /dev/null
@@ -0,0 +1,252 @@
+/*
+ * Copyright (C) 2015 Broadcom Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/bitops.h>
+#include <linux/gpio/driver.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+#include <linux/module.h>
+#include <linux/basic_mmio_gpio.h>
+
+#define GIO_BANK_SIZE           0x20
+#define GIO_ODEN(bank)          (((bank) * GIO_BANK_SIZE) + 0x00)
+#define GIO_DATA(bank)          (((bank) * GIO_BANK_SIZE) + 0x04)
+#define GIO_IODIR(bank)         (((bank) * GIO_BANK_SIZE) + 0x08)
+#define GIO_EC(bank)            (((bank) * GIO_BANK_SIZE) + 0x0c)
+#define GIO_EI(bank)            (((bank) * GIO_BANK_SIZE) + 0x10)
+#define GIO_MASK(bank)          (((bank) * GIO_BANK_SIZE) + 0x14)
+#define GIO_LEVEL(bank)         (((bank) * GIO_BANK_SIZE) + 0x18)
+#define GIO_STAT(bank)          (((bank) * GIO_BANK_SIZE) + 0x1c)
+
+struct brcmstb_gpio_bank {
+       struct list_head node;
+       int id;
+       struct bgpio_chip bgc;
+       struct brcmstb_gpio_priv *parent_priv;
+       u32 width;
+};
+
+struct brcmstb_gpio_priv {
+       struct list_head bank_list;
+       void __iomem *reg_base;
+       int num_banks;
+       struct platform_device *pdev;
+       int gpio_base;
+};
+
+#define MAX_GPIO_PER_BANK           32
+#define GPIO_BANK(gpio)         ((gpio) >> 5)
+/* assumes MAX_GPIO_PER_BANK is a multiple of 2 */
+#define GPIO_BIT(gpio)          ((gpio) & (MAX_GPIO_PER_BANK - 1))
+
+static inline struct brcmstb_gpio_bank *
+brcmstb_gpio_gc_to_bank(struct gpio_chip *gc)
+{
+       struct bgpio_chip *bgc = to_bgpio_chip(gc);
+       return container_of(bgc, struct brcmstb_gpio_bank, bgc);
+}
+
+static inline struct brcmstb_gpio_priv *
+brcmstb_gpio_gc_to_priv(struct gpio_chip *gc)
+{
+       struct brcmstb_gpio_bank *bank = brcmstb_gpio_gc_to_bank(gc);
+       return bank->parent_priv;
+}
+
+/* Make sure that the number of banks matches up between properties */
+static int brcmstb_gpio_sanity_check_banks(struct device *dev,
+               struct device_node *np, struct resource *res)
+{
+       int res_num_banks = resource_size(res) / GIO_BANK_SIZE;
+       int num_banks =
+               of_property_count_u32_elems(np, "brcm,gpio-bank-widths");
+
+       if (res_num_banks != num_banks) {
+               dev_err(dev, "Mismatch in banks: res had %d, bank-widths had %d\n",
+                               res_num_banks, num_banks);
+               return -EINVAL;
+       } else {
+               return 0;
+       }
+}
+
+static int brcmstb_gpio_remove(struct platform_device *pdev)
+{
+       struct brcmstb_gpio_priv *priv = platform_get_drvdata(pdev);
+       struct list_head *pos;
+       struct brcmstb_gpio_bank *bank;
+       int ret = 0;
+
+       list_for_each(pos, &priv->bank_list) {
+               bank = list_entry(pos, struct brcmstb_gpio_bank, node);
+               ret = bgpio_remove(&bank->bgc);
+               if (ret)
+                       dev_err(&pdev->dev, "gpiochip_remove fail in cleanup");
+       }
+       return ret;
+}
+
+static int brcmstb_gpio_of_xlate(struct gpio_chip *gc,
+               const struct of_phandle_args *gpiospec, u32 *flags)
+{
+       struct brcmstb_gpio_priv *priv = brcmstb_gpio_gc_to_priv(gc);
+       struct brcmstb_gpio_bank *bank = brcmstb_gpio_gc_to_bank(gc);
+       int offset;
+
+       if (gc->of_gpio_n_cells != 2) {
+               WARN_ON(1);
+               return -EINVAL;
+       }
+
+       if (WARN_ON(gpiospec->args_count < gc->of_gpio_n_cells))
+               return -EINVAL;
+
+       offset = gpiospec->args[0] - (gc->base - priv->gpio_base);
+       if (offset >= gc->ngpio)
+               return -EINVAL;
+
+       if (unlikely(offset >= bank->width)) {
+               dev_warn_ratelimited(&priv->pdev->dev,
+                       "Received request for invalid GPIO offset %d\n",
+                       gpiospec->args[0]);
+       }
+
+       if (flags)
+               *flags = gpiospec->args[1];
+
+       return offset;
+}
+
+static int brcmstb_gpio_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct device_node *np = dev->of_node;
+       void __iomem *reg_base;
+       struct brcmstb_gpio_priv *priv;
+       struct resource *res;
+       struct property *prop;
+       const __be32 *p;
+       u32 bank_width;
+       int err;
+       static int gpio_base;
+
+       priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       reg_base = devm_ioremap_resource(dev, res);
+       if (IS_ERR(reg_base))
+               return PTR_ERR(reg_base);
+
+       priv->gpio_base = gpio_base;
+       priv->reg_base = reg_base;
+       priv->pdev = pdev;
+
+       INIT_LIST_HEAD(&priv->bank_list);
+       if (brcmstb_gpio_sanity_check_banks(dev, np, res))
+               return -EINVAL;
+
+       of_property_for_each_u32(np, "brcm,gpio-bank-widths", prop, p,
+                       bank_width) {
+               struct brcmstb_gpio_bank *bank;
+               struct bgpio_chip *bgc;
+               struct gpio_chip *gc;
+
+               bank = devm_kzalloc(dev, sizeof(*bank), GFP_KERNEL);
+               if (!bank) {
+                       err = -ENOMEM;
+                       goto fail;
+               }
+
+               bank->parent_priv = priv;
+               bank->id = priv->num_banks;
+               if (bank_width <= 0 || bank_width > MAX_GPIO_PER_BANK) {
+                       dev_err(dev, "Invalid bank width %d\n", bank_width);
+                       goto fail;
+               } else {
+                       bank->width = bank_width;
+               }
+
+               /*
+                * Regs are 4 bytes wide, have data reg, no set/clear regs,
+                * and direction bits have 0 = output and 1 = input
+                */
+               bgc = &bank->bgc;
+               err = bgpio_init(bgc, dev, 4,
+                               reg_base + GIO_DATA(bank->id),
+                               NULL, NULL, NULL,
+                               reg_base + GIO_IODIR(bank->id), 0);
+               if (err) {
+                       dev_err(dev, "bgpio_init() failed\n");
+                       goto fail;
+               }
+
+               gc = &bgc->gc;
+               gc->of_node = np;
+               gc->owner = THIS_MODULE;
+               gc->label = np->full_name;
+               gc->base = gpio_base;
+               gc->of_gpio_n_cells = 2;
+               gc->of_xlate = brcmstb_gpio_of_xlate;
+               /* not all ngpio lines are valid, will use bank width later */
+               gc->ngpio = MAX_GPIO_PER_BANK;
+
+               err = gpiochip_add(gc);
+               if (err) {
+                       dev_err(dev, "Could not add gpiochip for bank %d\n",
+                                       bank->id);
+                       goto fail;
+               }
+               gpio_base += gc->ngpio;
+               dev_dbg(dev, "bank=%d, base=%d, ngpio=%d, width=%d\n", bank->id,
+                       gc->base, gc->ngpio, bank->width);
+
+               /* Everything looks good, so add bank to list */
+               list_add(&bank->node, &priv->bank_list);
+
+               priv->num_banks++;
+       }
+
+       dev_info(dev, "Registered %d banks (GPIO(s): %d-%d)\n",
+                       priv->num_banks, priv->gpio_base, gpio_base - 1);
+
+       platform_set_drvdata(pdev, priv);
+
+       return 0;
+
+fail:
+       (void) brcmstb_gpio_remove(pdev);
+       return err;
+}
+
+static const struct of_device_id brcmstb_gpio_of_match[] = {
+       { .compatible = "brcm,brcmstb-gpio" },
+       {},
+};
+
+MODULE_DEVICE_TABLE(of, brcmstb_gpio_of_match);
+
+static struct platform_driver brcmstb_gpio_driver = {
+       .driver = {
+               .name = "brcmstb-gpio",
+               .of_match_table = brcmstb_gpio_of_match,
+       },
+       .probe = brcmstb_gpio_probe,
+       .remove = brcmstb_gpio_remove,
+};
+module_platform_driver(brcmstb_gpio_driver);
+
+MODULE_AUTHOR("Gregory Fong");
+MODULE_DESCRIPTION("Driver for Broadcom BRCMSTB SoC UPG GPIO");
+MODULE_LICENSE("GPL v2");
index 91a7ffe831350adc2afe0a45ab472b937113ce57..fddd204dc9b68484c473c267803b9d1216fe4535 100644 (file)
@@ -16,6 +16,7 @@
  */
 
 #include <linux/interrupt.h>
+#include <linux/module.h>
 #include <linux/platform_device.h>
 #include <linux/gpio.h>
 #include <linux/seq_file.h>
@@ -94,9 +95,8 @@ static inline int to_reg(int gpio, enum ctrl_register reg_type)
 {
        int reg;
 
-       if (gpio == 94) {
+       if (gpio == 94)
                return GPIOPANELCTL;
-       }
 
        if (reg_type == CTRL_IN) {
                if (gpio < 8)
@@ -255,6 +255,7 @@ static struct irq_chip crystalcove_irqchip = {
        .irq_set_type           = crystalcove_irq_type,
        .irq_bus_lock           = crystalcove_bus_lock,
        .irq_bus_sync_unlock    = crystalcove_bus_sync_unlock,
+       .flags                  = IRQCHIP_SKIP_SET_WAKE,
 };
 
 static irqreturn_t crystalcove_gpio_irq_handler(int irq, void *data)
index dbdb4de82c6db34f4a339ac2b6f25a8490ba8de2..6685712c15cf0f2409aa10c7ea511fa1f5f913c5 100644 (file)
@@ -466,7 +466,6 @@ static int dln2_gpio_probe(struct platform_device *pdev)
        dln2->gpio.owner = THIS_MODULE;
        dln2->gpio.base = -1;
        dln2->gpio.ngpio = pins;
-       dln2->gpio.exported = true;
        dln2->gpio.can_sleep = true;
        dln2->gpio.irq_not_threaded = true;
        dln2->gpio.set = dln2_gpio_set;
diff --git a/drivers/gpio/gpio-etraxfs.c b/drivers/gpio/gpio-etraxfs.c
new file mode 100644 (file)
index 0000000..28071f4
--- /dev/null
@@ -0,0 +1,176 @@
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/basic_mmio_gpio.h>
+
+#define ETRAX_FS_rw_pa_dout    0
+#define ETRAX_FS_r_pa_din      4
+#define ETRAX_FS_rw_pa_oe      8
+#define ETRAX_FS_rw_intr_cfg   12
+#define ETRAX_FS_rw_intr_mask  16
+#define ETRAX_FS_rw_ack_intr   20
+#define ETRAX_FS_r_intr                24
+#define ETRAX_FS_rw_pb_dout    32
+#define ETRAX_FS_r_pb_din      36
+#define ETRAX_FS_rw_pb_oe      40
+#define ETRAX_FS_rw_pc_dout    48
+#define ETRAX_FS_r_pc_din      52
+#define ETRAX_FS_rw_pc_oe      56
+#define ETRAX_FS_rw_pd_dout    64
+#define ETRAX_FS_r_pd_din      68
+#define ETRAX_FS_rw_pd_oe      72
+#define ETRAX_FS_rw_pe_dout    80
+#define ETRAX_FS_r_pe_din      84
+#define ETRAX_FS_rw_pe_oe      88
+
+struct etraxfs_gpio_port {
+       const char *label;
+       unsigned int oe;
+       unsigned int dout;
+       unsigned int din;
+       unsigned int ngpio;
+};
+
+struct etraxfs_gpio_info {
+       unsigned int num_ports;
+       const struct etraxfs_gpio_port *ports;
+};
+
+static const struct etraxfs_gpio_port etraxfs_gpio_etraxfs_ports[] = {
+       {
+               .label  = "A",
+               .ngpio  = 8,
+               .oe     = ETRAX_FS_rw_pa_oe,
+               .dout   = ETRAX_FS_rw_pa_dout,
+               .din    = ETRAX_FS_r_pa_din,
+       },
+       {
+               .label  = "B",
+               .ngpio  = 18,
+               .oe     = ETRAX_FS_rw_pb_oe,
+               .dout   = ETRAX_FS_rw_pb_dout,
+               .din    = ETRAX_FS_r_pb_din,
+       },
+       {
+               .label  = "C",
+               .ngpio  = 18,
+               .oe     = ETRAX_FS_rw_pc_oe,
+               .dout   = ETRAX_FS_rw_pc_dout,
+               .din    = ETRAX_FS_r_pc_din,
+       },
+       {
+               .label  = "D",
+               .ngpio  = 18,
+               .oe     = ETRAX_FS_rw_pd_oe,
+               .dout   = ETRAX_FS_rw_pd_dout,
+               .din    = ETRAX_FS_r_pd_din,
+       },
+       {
+               .label  = "E",
+               .ngpio  = 18,
+               .oe     = ETRAX_FS_rw_pe_oe,
+               .dout   = ETRAX_FS_rw_pe_dout,
+               .din    = ETRAX_FS_r_pe_din,
+       },
+};
+
+static const struct etraxfs_gpio_info etraxfs_gpio_etraxfs = {
+       .num_ports = ARRAY_SIZE(etraxfs_gpio_etraxfs_ports),
+       .ports = etraxfs_gpio_etraxfs_ports,
+};
+
+static int etraxfs_gpio_of_xlate(struct gpio_chip *gc,
+                              const struct of_phandle_args *gpiospec,
+                              u32 *flags)
+{
+       /*
+        * Port numbers are A to E, and the properties are integers, so we
+        * specify them as 0xA - 0xE.
+        */
+       if (gc->label[0] - 'A' + 0xA != gpiospec->args[2])
+               return -EINVAL;
+
+       return of_gpio_simple_xlate(gc, gpiospec, flags);
+}
+
+static const struct of_device_id etraxfs_gpio_of_table[] = {
+       {
+               .compatible = "axis,etraxfs-gio",
+               .data = &etraxfs_gpio_etraxfs,
+       },
+       {},
+};
+
+static int etraxfs_gpio_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       const struct etraxfs_gpio_info *info;
+       const struct of_device_id *match;
+       struct bgpio_chip *chips;
+       struct resource *res;
+       void __iomem *regs;
+       int ret;
+       int i;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       regs = devm_ioremap_resource(dev, res);
+       if (!regs)
+               return -ENOMEM;
+
+       match = of_match_node(etraxfs_gpio_of_table, dev->of_node);
+       if (!match)
+               return -EINVAL;
+
+       info = match->data;
+
+       chips = devm_kzalloc(dev, sizeof(*chips) * info->num_ports, GFP_KERNEL);
+       if (!chips)
+               return -ENOMEM;
+
+       for (i = 0; i < info->num_ports; i++) {
+               struct bgpio_chip *bgc = &chips[i];
+               const struct etraxfs_gpio_port *port = &info->ports[i];
+
+               ret = bgpio_init(bgc, dev, 4,
+                                regs + port->din,      /* dat */
+                                regs + port->dout,     /* set */
+                                NULL,                  /* clr */
+                                regs + port->oe,       /* dirout */
+                                NULL,                  /* dirin */
+                                BGPIOF_UNREADABLE_REG_SET);
+               if (ret)
+                       return ret;
+
+               bgc->gc.ngpio = port->ngpio;
+               bgc->gc.label = port->label;
+
+               bgc->gc.of_node = dev->of_node;
+               bgc->gc.of_gpio_n_cells = 3;
+               bgc->gc.of_xlate = etraxfs_gpio_of_xlate;
+
+               ret = gpiochip_add(&bgc->gc);
+               if (ret)
+                       dev_err(dev, "Unable to register port %s\n",
+                               bgc->gc.label);
+       }
+
+       return 0;
+}
+
+static struct platform_driver etraxfs_gpio_driver = {
+       .driver = {
+               .name           = "etraxfs-gpio",
+               .of_match_table = of_match_ptr(etraxfs_gpio_of_table),
+       },
+       .probe  = etraxfs_gpio_probe,
+};
+
+static int __init etraxfs_gpio_init(void)
+{
+       return platform_driver_register(&etraxfs_gpio_driver);
+}
+
+device_initcall(etraxfs_gpio_init);
index dbda8433c4f7ef9af3855add277b6d5ed74e95fb..5e3c4fa67d820f4dfd22c5409c4194f7d1faff2c 100644 (file)
@@ -172,7 +172,7 @@ static struct f7188x_gpio_bank f71869a_gpio_bank[] = {
 };
 
 static struct f7188x_gpio_bank f71882_gpio_bank[] = {
-       F7188X_GPIO_BANK(0 , 8, 0xF0),
+       F7188X_GPIO_BANK(0, 8, 0xF0),
        F7188X_GPIO_BANK(10, 8, 0xE0),
        F7188X_GPIO_BANK(20, 8, 0xD0),
        F7188X_GPIO_BANK(30, 4, 0xC0),
@@ -180,7 +180,7 @@ static struct f7188x_gpio_bank f71882_gpio_bank[] = {
 };
 
 static struct f7188x_gpio_bank f71889_gpio_bank[] = {
-       F7188X_GPIO_BANK(0 , 7, 0xF0),
+       F7188X_GPIO_BANK(0, 7, 0xF0),
        F7188X_GPIO_BANK(10, 7, 0xE0),
        F7188X_GPIO_BANK(20, 8, 0xD0),
        F7188X_GPIO_BANK(30, 8, 0xC0),
index b92a690f5765c37457147b83ecac3cb810b5641c..9bda3727fac12df7480f451057ed2513fcae2618 100644 (file)
@@ -135,6 +135,17 @@ static unsigned long bgpio_pin2mask_be(struct bgpio_chip *bgc,
        return 1 << (bgc->bits - 1 - pin);
 }
 
+static int bgpio_get_set(struct gpio_chip *gc, unsigned int gpio)
+{
+       struct bgpio_chip *bgc = to_bgpio_chip(gc);
+       unsigned long pinmask = bgc->pin2mask(bgc, gpio);
+
+       if (bgc->dir & pinmask)
+               return bgc->read_reg(bgc->reg_set) & pinmask;
+       else
+               return bgc->read_reg(bgc->reg_dat) & pinmask;
+}
+
 static int bgpio_get(struct gpio_chip *gc, unsigned int gpio)
 {
        struct bgpio_chip *bgc = to_bgpio_chip(gc);
@@ -416,7 +427,8 @@ static int bgpio_setup_accessors(struct device *dev,
 static int bgpio_setup_io(struct bgpio_chip *bgc,
                          void __iomem *dat,
                          void __iomem *set,
-                         void __iomem *clr)
+                         void __iomem *clr,
+                         unsigned long flags)
 {
 
        bgc->reg_dat = dat;
@@ -437,7 +449,11 @@ static int bgpio_setup_io(struct bgpio_chip *bgc,
                bgc->gc.set_multiple = bgpio_set_multiple;
        }
 
-       bgc->gc.get = bgpio_get;
+       if (!(flags & BGPIOF_UNREADABLE_REG_SET) &&
+           (flags & BGPIOF_READ_OUTPUT_REG_SET))
+               bgc->gc.get = bgpio_get_set;
+       else
+               bgc->gc.get = bgpio_get;
 
        return 0;
 }
@@ -500,7 +516,7 @@ int bgpio_init(struct bgpio_chip *bgc, struct device *dev,
        bgc->gc.ngpio = bgc->bits;
        bgc->gc.request = bgpio_request;
 
-       ret = bgpio_setup_io(bgc, dat, set, clr);
+       ret = bgpio_setup_io(bgc, dat, set, clr, flags);
        if (ret)
                return ret;
 
index dadfc245cf0993ac27615fee19ca66262f683458..30a8f24c92c5c5b0d46835250b8b2b6aefb203a9 100644 (file)
@@ -123,7 +123,7 @@ static void it8761e_gpio_set(struct gpio_chip *gc,
 
        curr_vals = inb(reg);
        if (val)
-               outb(curr_vals | (1 << bit) , reg);
+               outb(curr_vals | (1 << bit), reg);
        else
                outb(curr_vals & ~(1 << bit), reg);
 
diff --git a/drivers/gpio/gpio-lpc18xx.c b/drivers/gpio/gpio-lpc18xx.c
new file mode 100644 (file)
index 0000000..eb68603
--- /dev/null
@@ -0,0 +1,180 @@
+/*
+ * GPIO driver for NXP LPC18xx/43xx.
+ *
+ * Copyright (C) 2015 Joachim Eastwood <manabian@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/gpio/driver.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/platform_device.h>
+
+/* LPC18xx GPIO register offsets */
+#define LPC18XX_REG_DIR(n)     (0x2000 + n * sizeof(u32))
+
+#define LPC18XX_MAX_PORTS      8
+#define LPC18XX_PINS_PER_PORT  32
+
+struct lpc18xx_gpio_chip {
+       struct gpio_chip gpio;
+       void __iomem *base;
+       struct clk *clk;
+       spinlock_t lock;
+};
+
+static inline struct lpc18xx_gpio_chip *to_lpc18xx_gpio(struct gpio_chip *chip)
+{
+       return container_of(chip, struct lpc18xx_gpio_chip, gpio);
+}
+
+static int lpc18xx_gpio_request(struct gpio_chip *chip, unsigned offset)
+{
+       return pinctrl_request_gpio(offset);
+}
+
+static void lpc18xx_gpio_free(struct gpio_chip *chip, unsigned offset)
+{
+       pinctrl_free_gpio(offset);
+}
+
+static void lpc18xx_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+       struct lpc18xx_gpio_chip *gc = to_lpc18xx_gpio(chip);
+       writeb(value ? 1 : 0, gc->base + offset);
+}
+
+static int lpc18xx_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+       struct lpc18xx_gpio_chip *gc = to_lpc18xx_gpio(chip);
+       return !!readb(gc->base + offset);
+}
+
+static int lpc18xx_gpio_direction(struct gpio_chip *chip, unsigned offset,
+                                 bool out)
+{
+       struct lpc18xx_gpio_chip *gc = to_lpc18xx_gpio(chip);
+       unsigned long flags;
+       u32 port, pin, dir;
+
+       port = offset / LPC18XX_PINS_PER_PORT;
+       pin  = offset % LPC18XX_PINS_PER_PORT;
+
+       spin_lock_irqsave(&gc->lock, flags);
+       dir = readl(gc->base + LPC18XX_REG_DIR(port));
+       if (out)
+               dir |= BIT(pin);
+       else
+               dir &= ~BIT(pin);
+       writel(dir, gc->base + LPC18XX_REG_DIR(port));
+       spin_unlock_irqrestore(&gc->lock, flags);
+
+       return 0;
+}
+
+static int lpc18xx_gpio_direction_input(struct gpio_chip *chip,
+                                       unsigned offset)
+{
+       return lpc18xx_gpio_direction(chip, offset, false);
+}
+
+static int lpc18xx_gpio_direction_output(struct gpio_chip *chip,
+                                        unsigned offset, int value)
+{
+       lpc18xx_gpio_set(chip, offset, value);
+       return lpc18xx_gpio_direction(chip, offset, true);
+}
+
+static struct gpio_chip lpc18xx_chip = {
+       .label                  = "lpc18xx/43xx-gpio",
+       .request                = lpc18xx_gpio_request,
+       .free                   = lpc18xx_gpio_free,
+       .direction_input        = lpc18xx_gpio_direction_input,
+       .direction_output       = lpc18xx_gpio_direction_output,
+       .set                    = lpc18xx_gpio_set,
+       .get                    = lpc18xx_gpio_get,
+       .ngpio                  = LPC18XX_MAX_PORTS * LPC18XX_PINS_PER_PORT,
+       .owner                  = THIS_MODULE,
+};
+
+static int lpc18xx_gpio_probe(struct platform_device *pdev)
+{
+       struct lpc18xx_gpio_chip *gc;
+       struct resource *res;
+       int ret;
+
+       gc = devm_kzalloc(&pdev->dev, sizeof(*gc), GFP_KERNEL);
+       if (!gc)
+               return -ENOMEM;
+
+       gc->gpio = lpc18xx_chip;
+       platform_set_drvdata(pdev, gc);
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       gc->base = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(gc->base))
+               return PTR_ERR(gc->base);
+
+       gc->clk = devm_clk_get(&pdev->dev, NULL);
+       if (IS_ERR(gc->clk)) {
+               dev_err(&pdev->dev, "input clock not found\n");
+               return PTR_ERR(gc->clk);
+       }
+
+       ret = clk_prepare_enable(gc->clk);
+       if (ret) {
+               dev_err(&pdev->dev, "unable to enable clock\n");
+               return ret;
+       }
+
+       spin_lock_init(&gc->lock);
+
+       gc->gpio.dev = &pdev->dev;
+
+       ret = gpiochip_add(&gc->gpio);
+       if (ret) {
+               dev_err(&pdev->dev, "failed to add gpio chip\n");
+               clk_disable_unprepare(gc->clk);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int lpc18xx_gpio_remove(struct platform_device *pdev)
+{
+       struct lpc18xx_gpio_chip *gc = platform_get_drvdata(pdev);
+
+       gpiochip_remove(&gc->gpio);
+       clk_disable_unprepare(gc->clk);
+
+       return 0;
+}
+
+static const struct of_device_id lpc18xx_gpio_match[] = {
+       { .compatible = "nxp,lpc1850-gpio" },
+       { }
+};
+MODULE_DEVICE_TABLE(of, lpc18xx_gpio_match);
+
+static struct platform_driver lpc18xx_gpio_driver = {
+       .probe  = lpc18xx_gpio_probe,
+       .remove = lpc18xx_gpio_remove,
+       .driver = {
+               .name           = "lpc18xx-gpio",
+               .of_match_table = lpc18xx_gpio_match,
+       },
+};
+module_platform_driver(lpc18xx_gpio_driver);
+
+MODULE_AUTHOR("Joachim Eastwood <manabian@gmail.com>");
+MODULE_DESCRIPTION("GPIO driver for LPC18xx/43xx");
+MODULE_LICENSE("GPL v2");
index 127c755b38dc28c5d78b515fa17c84090162fbf0..153af464c7a780e7ef57c8ba15e3e7d7b5307ede 100644 (file)
@@ -72,7 +72,7 @@ struct lp_gpio {
  *
  * per gpio specific registers consist of two 32bit registers per gpio
  * (LP_CONFIG1 and LP_CONFIG2), with 94 gpios there's a total of
- * 188 config registes.
+ * 188 config registers.
  *
  * A simplified view of the register layout look like this:
  *
index 0fa4543c5e02505871a2f1e3bfef6cb840d94e47..aed4ca9338bca1e15d8a3052438d68635661e01e 100644 (file)
@@ -429,6 +429,14 @@ static int max732x_irq_set_type(struct irq_data *d, unsigned int type)
        return 0;
 }
 
+static int max732x_irq_set_wake(struct irq_data *data, unsigned int on)
+{
+       struct max732x_chip *chip = irq_data_get_irq_chip_data(data);
+
+       irq_set_irq_wake(chip->client->irq, on);
+       return 0;
+}
+
 static struct irq_chip max732x_irq_chip = {
        .name                   = "max732x",
        .irq_mask               = max732x_irq_mask,
@@ -436,6 +444,7 @@ static struct irq_chip max732x_irq_chip = {
        .irq_bus_lock           = max732x_irq_bus_lock,
        .irq_bus_sync_unlock    = max732x_irq_bus_sync_unlock,
        .irq_set_type           = max732x_irq_set_type,
+       .irq_set_wake           = max732x_irq_set_wake,
 };
 
 static uint8_t max732x_irq_pending(struct max732x_chip *chip)
@@ -507,12 +516,10 @@ static int max732x_irq_setup(struct max732x_chip *chip,
                chip->irq_features = has_irq;
                mutex_init(&chip->irq_lock);
 
-               ret = devm_request_threaded_irq(&client->dev,
-                                       client->irq,
-                                       NULL,
-                                       max732x_irq_handler,
-                                       IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
-                                       dev_name(&client->dev), chip);
+               ret = devm_request_threaded_irq(&client->dev, client->irq,
+                               NULL, max732x_irq_handler, IRQF_ONESHOT |
+                               IRQF_TRIGGER_FALLING | IRQF_SHARED,
+                               dev_name(&client->dev), chip);
                if (ret) {
                        dev_err(&client->dev, "failed to request irq %d\n",
                                client->irq);
@@ -521,7 +528,7 @@ static int max732x_irq_setup(struct max732x_chip *chip,
                ret =  gpiochip_irqchip_add(&chip->gpio_chip,
                                            &max732x_irq_chip,
                                            irq_base,
-                                           handle_edge_irq,
+                                           handle_simple_irq,
                                            IRQ_TYPE_NONE);
                if (ret) {
                        dev_err(&client->dev,
index c3ab46e595dafc4ac3c3b84d7161e56a4a1feb71..abd8676ce2b69d5cdb26449c1c6d5b2fcd67d1b5 100644 (file)
@@ -39,17 +39,6 @@ static void moxart_gpio_free(struct gpio_chip *chip, unsigned offset)
        pinctrl_free_gpio(offset);
 }
 
-static int moxart_gpio_get(struct gpio_chip *chip, unsigned offset)
-{
-       struct bgpio_chip *bgc = to_bgpio_chip(chip);
-       u32 ret = bgc->read_reg(bgc->reg_dir);
-
-       if (ret & BIT(offset))
-               return !!(bgc->read_reg(bgc->reg_set) & BIT(offset));
-       else
-               return !!(bgc->read_reg(bgc->reg_dat) & BIT(offset));
-}
-
 static int moxart_gpio_probe(struct platform_device *pdev)
 {
        struct device *dev = &pdev->dev;
@@ -68,8 +57,9 @@ static int moxart_gpio_probe(struct platform_device *pdev)
                return PTR_ERR(base);
 
        ret = bgpio_init(bgc, dev, 4, base + GPIO_DATA_IN,
-                   base + GPIO_DATA_OUT, NULL,
-                   base + GPIO_PIN_DIRECTION, NULL, 0);
+                        base + GPIO_DATA_OUT, NULL,
+                        base + GPIO_PIN_DIRECTION, NULL,
+                        BGPIOF_READ_OUTPUT_REG_SET);
        if (ret) {
                dev_err(&pdev->dev, "bgpio_init failed\n");
                return ret;
@@ -78,7 +68,6 @@ static int moxart_gpio_probe(struct platform_device *pdev)
        bgc->gc.label = "moxart-gpio";
        bgc->gc.request = moxart_gpio_request;
        bgc->gc.free = moxart_gpio_free;
-       bgc->gc.get = moxart_gpio_get;
        bgc->data = bgc->read_reg(bgc->reg_set);
        bgc->gc.base = 0;
        bgc->gc.ngpio = 32;
index 9f7446a7ac64f0ee11a4fd675d87773d3b21f8e5..ec1eb1b7250ff145f5459f50b4c874154f1e23dc 100644 (file)
@@ -131,7 +131,7 @@ static struct mxc_gpio_hwdata *mxc_gpio_hwdata;
 #define GPIO_INT_FALL_EDGE     (mxc_gpio_hwdata->fall_edge)
 #define GPIO_INT_BOTH_EDGES    0x4
 
-static struct platform_device_id mxc_gpio_devtype[] = {
+static const struct platform_device_id mxc_gpio_devtype[] = {
        {
                .name = "imx1-gpio",
                .driver_data = IMX1_GPIO,
@@ -437,20 +437,20 @@ static int mxc_gpio_probe(struct platform_device *pdev)
                irq_set_chained_handler(port->irq, mx2_gpio_irq_handler);
        } else {
                /* setup one handler for each entry */
-               irq_set_chained_handler(port->irq, mx3_gpio_irq_handler);
-               irq_set_handler_data(port->irq, port);
-               if (port->irq_high > 0) {
+               irq_set_chained_handler_and_data(port->irq,
+                                                mx3_gpio_irq_handler, port);
+               if (port->irq_high > 0)
                        /* setup handler for GPIO 16 to 31 */
-                       irq_set_chained_handler(port->irq_high,
-                                               mx3_gpio_irq_handler);
-                       irq_set_handler_data(port->irq_high, port);
-               }
+                       irq_set_chained_handler_and_data(port->irq_high,
+                                                        mx3_gpio_irq_handler,
+                                                        port);
        }
 
        err = bgpio_init(&port->bgc, &pdev->dev, 4,
                         port->base + GPIO_PSR,
                         port->base + GPIO_DR, NULL,
-                        port->base + GPIO_GDIR, NULL, 0);
+                        port->base + GPIO_GDIR, NULL,
+                        BGPIOF_READ_OUTPUT_REG_SET);
        if (err)
                goto out_bgio;
 
index 84cbda6acdda793b343b19ee0ca8170a50da8805..551d15d7c369cc12b94496a020ccdaabd494cb3c 100644 (file)
@@ -239,7 +239,7 @@ static int mxs_gpio_get_direction(struct gpio_chip *gc, unsigned offset)
        return !(dir & mask);
 }
 
-static struct platform_device_id mxs_gpio_ids[] = {
+static const struct platform_device_id mxs_gpio_ids[] = {
        {
                .name = "imx23-gpio",
                .driver_data = IMX23_GPIO,
@@ -320,8 +320,8 @@ static int mxs_gpio_probe(struct platform_device *pdev)
        mxs_gpio_init_gc(port, irq_base);
 
        /* setup one handler for each entry */
-       irq_set_chained_handler(port->irq, mxs_gpio_irq_handler);
-       irq_set_handler_data(port->irq, port);
+       irq_set_chained_handler_and_data(port->irq, mxs_gpio_irq_handler,
+                                        port);
 
        err = bgpio_init(&port->bgc, &pdev->dev, 4,
                         port->base + PINCTRL_DIN(port),
index b232397ad7ec1599ffda494f73fdc02fe8e83875..b0c57d505be75ac133455a287dc092ff83f3283c 100644 (file)
@@ -488,9 +488,6 @@ static int omap_gpio_irq_type(struct irq_data *d, unsigned type)
        unsigned long flags;
        unsigned offset = d->hwirq;
 
-       if (!BANK_USED(bank))
-               pm_runtime_get_sync(bank->dev);
-
        if (type & ~IRQ_TYPE_SENSE_MASK)
                return -EINVAL;
 
@@ -498,12 +495,18 @@ static int omap_gpio_irq_type(struct irq_data *d, unsigned type)
                (type & (IRQ_TYPE_LEVEL_LOW|IRQ_TYPE_LEVEL_HIGH)))
                return -EINVAL;
 
+       if (!BANK_USED(bank))
+               pm_runtime_get_sync(bank->dev);
+
        spin_lock_irqsave(&bank->lock, flags);
        retval = omap_set_gpio_triggering(bank, offset, type);
+       if (retval)
+               goto error;
        omap_gpio_init_irq(bank, offset);
        if (!omap_gpio_is_input(bank, offset)) {
                spin_unlock_irqrestore(&bank->lock, flags);
-               return -EINVAL;
+               retval = -EINVAL;
+               goto error;
        }
        spin_unlock_irqrestore(&bank->lock, flags);
 
@@ -512,6 +515,11 @@ static int omap_gpio_irq_type(struct irq_data *d, unsigned type)
        else if (type & (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING))
                __irq_set_handler_locked(d->irq, handle_edge_irq);
 
+       return 0;
+
+error:
+       if (!BANK_USED(bank))
+               pm_runtime_put(bank->dev);
        return retval;
 }
 
@@ -638,15 +646,6 @@ static int omap_set_gpio_wakeup(struct gpio_bank *bank, unsigned offset,
        return 0;
 }
 
-static void omap_reset_gpio(struct gpio_bank *bank, unsigned offset)
-{
-       omap_set_gpio_direction(bank, offset, 1);
-       omap_set_gpio_irqenable(bank, offset, 0);
-       omap_clear_gpio_irqstatus(bank, offset);
-       omap_set_gpio_triggering(bank, offset, IRQ_TYPE_NONE);
-       omap_clear_gpio_debounce(bank, offset);
-}
-
 /* Use disable_irq_wake() and enable_irq_wake() functions from drivers */
 static int omap_gpio_wake_enable(struct irq_data *d, unsigned int enable)
 {
@@ -669,14 +668,7 @@ static int omap_gpio_request(struct gpio_chip *chip, unsigned offset)
                pm_runtime_get_sync(bank->dev);
 
        spin_lock_irqsave(&bank->lock, flags);
-       /* Set trigger to none. You need to enable the desired trigger with
-        * request_irq() or set_irq_type(). Only do this if the IRQ line has
-        * not already been requested.
-        */
-       if (!LINE_USED(bank->irq_usage, offset)) {
-               omap_set_gpio_triggering(bank, offset, IRQ_TYPE_NONE);
-               omap_enable_gpio_module(bank, offset);
-       }
+       omap_enable_gpio_module(bank, offset);
        bank->mod_usage |= BIT(offset);
        spin_unlock_irqrestore(&bank->lock, flags);
 
@@ -690,8 +682,11 @@ static void omap_gpio_free(struct gpio_chip *chip, unsigned offset)
 
        spin_lock_irqsave(&bank->lock, flags);
        bank->mod_usage &= ~(BIT(offset));
+       if (!LINE_USED(bank->irq_usage, offset)) {
+               omap_set_gpio_direction(bank, offset, 1);
+               omap_clear_gpio_debounce(bank, offset);
+       }
        omap_disable_gpio_module(bank, offset);
-       omap_reset_gpio(bank, offset);
        spin_unlock_irqrestore(&bank->lock, flags);
 
        /*
@@ -795,11 +790,23 @@ static unsigned int omap_gpio_irq_startup(struct irq_data *d)
                pm_runtime_get_sync(bank->dev);
 
        spin_lock_irqsave(&bank->lock, flags);
-       omap_gpio_init_irq(bank, offset);
+
+       if (!LINE_USED(bank->mod_usage, offset))
+               omap_set_gpio_direction(bank, offset, 1);
+       else if (!omap_gpio_is_input(bank, offset))
+               goto err;
+       omap_enable_gpio_module(bank, offset);
+       bank->irq_usage |= BIT(offset);
+
        spin_unlock_irqrestore(&bank->lock, flags);
        omap_gpio_unmask_irq(d);
 
        return 0;
+err:
+       spin_unlock_irqrestore(&bank->lock, flags);
+       if (!BANK_USED(bank))
+               pm_runtime_put(bank->dev);
+       return -EINVAL;
 }
 
 static void omap_gpio_irq_shutdown(struct irq_data *d)
@@ -810,8 +817,12 @@ static void omap_gpio_irq_shutdown(struct irq_data *d)
 
        spin_lock_irqsave(&bank->lock, flags);
        bank->irq_usage &= ~(BIT(offset));
+       omap_set_gpio_irqenable(bank, offset, 0);
+       omap_clear_gpio_irqstatus(bank, offset);
+       omap_set_gpio_triggering(bank, offset, IRQ_TYPE_NONE);
+       if (!LINE_USED(bank->mod_usage, offset))
+               omap_clear_gpio_debounce(bank, offset);
        omap_disable_gpio_module(bank, offset);
-       omap_reset_gpio(bank, offset);
        spin_unlock_irqrestore(&bank->lock, flags);
 
        /*
@@ -1233,6 +1244,17 @@ static int omap_gpio_probe(struct platform_device *pdev)
        return 0;
 }
 
+static int omap_gpio_remove(struct platform_device *pdev)
+{
+       struct gpio_bank *bank = platform_get_drvdata(pdev);
+
+       list_del(&bank->node);
+       gpiochip_remove(&bank->chip);
+       pm_runtime_disable(bank->dev);
+
+       return 0;
+}
+
 #ifdef CONFIG_ARCH_OMAP2PLUS
 
 #if defined(CONFIG_PM)
@@ -1418,6 +1440,7 @@ static int omap_gpio_runtime_resume(struct device *dev)
 }
 #endif /* CONFIG_PM */
 
+#if IS_BUILTIN(CONFIG_GPIO_OMAP)
 void omap2_gpio_prepare_for_idle(int pwr_mode)
 {
        struct gpio_bank *bank;
@@ -1443,6 +1466,7 @@ void omap2_gpio_resume_after_idle(void)
                pm_runtime_get_sync(bank->dev);
        }
 }
+#endif
 
 #if defined(CONFIG_PM)
 static void omap_gpio_init_context(struct gpio_bank *p)
@@ -1598,6 +1622,7 @@ MODULE_DEVICE_TABLE(of, omap_gpio_match);
 
 static struct platform_driver omap_gpio_driver = {
        .probe          = omap_gpio_probe,
+       .remove         = omap_gpio_remove,
        .driver         = {
                .name   = "omap_gpio",
                .pm     = &gpio_pm_ops,
@@ -1615,3 +1640,13 @@ static int __init omap_gpio_drv_reg(void)
        return platform_driver_register(&omap_gpio_driver);
 }
 postcore_initcall(omap_gpio_drv_reg);
+
+static void __exit omap_gpio_exit(void)
+{
+       platform_driver_unregister(&omap_gpio_driver);
+}
+module_exit(omap_gpio_exit);
+
+MODULE_DESCRIPTION("omap gpio driver");
+MODULE_ALIAS("platform:gpio-omap");
+MODULE_LICENSE("GPL v2");
index e2da64abbccd9a8ebc42a4b2f29b76f707c82e0b..d233eb3b81323342bb5a22122c1b1f570678c8b1 100644 (file)
@@ -443,12 +443,13 @@ static struct irq_chip pca953x_irq_chip = {
        .irq_set_type           = pca953x_irq_set_type,
 };
 
-static u8 pca953x_irq_pending(struct pca953x_chip *chip, u8 *pending)
+static bool pca953x_irq_pending(struct pca953x_chip *chip, u8 *pending)
 {
        u8 cur_stat[MAX_BANK];
        u8 old_stat[MAX_BANK];
-       u8 pendings = 0;
-       u8 trigger[MAX_BANK], triggers = 0;
+       bool pending_seen = false;
+       bool trigger_seen = false;
+       u8 trigger[MAX_BANK];
        int ret, i, offset = 0;
 
        switch (chip->chip_type) {
@@ -461,7 +462,7 @@ static u8 pca953x_irq_pending(struct pca953x_chip *chip, u8 *pending)
        }
        ret = pca953x_read_regs(chip, offset, cur_stat);
        if (ret)
-               return 0;
+               return false;
 
        /* Remove output pins from the equation */
        for (i = 0; i < NBANK(chip); i++)
@@ -471,11 +472,12 @@ static u8 pca953x_irq_pending(struct pca953x_chip *chip, u8 *pending)
 
        for (i = 0; i < NBANK(chip); i++) {
                trigger[i] = (cur_stat[i] ^ old_stat[i]) & chip->irq_mask[i];
-               triggers += trigger[i];
+               if (trigger[i])
+                       trigger_seen = true;
        }
 
-       if (!triggers)
-               return 0;
+       if (!trigger_seen)
+               return false;
 
        memcpy(chip->irq_stat, cur_stat, NBANK(chip));
 
@@ -483,10 +485,11 @@ static u8 pca953x_irq_pending(struct pca953x_chip *chip, u8 *pending)
                pending[i] = (old_stat[i] & chip->irq_trig_fall[i]) |
                        (cur_stat[i] & chip->irq_trig_raise[i]);
                pending[i] &= trigger[i];
-               pendings += pending[i];
+               if (pending[i])
+                       pending_seen = true;
        }
 
-       return pendings;
+       return pending_seen;
 }
 
 static irqreturn_t pca953x_irq_handler(int irq, void *devid)
@@ -630,7 +633,7 @@ static int device_pca957x_init(struct pca953x_chip *chip, u32 invert)
                memset(val, 0, NBANK(chip));
        pca953x_write_regs(chip, PCA957X_INVRT, val);
 
-       /* To enable register 6, 7 to controll pull up and pull down */
+       /* To enable register 6, 7 to control pull up and pull down */
        memset(val, 0x02, NBANK(chip));
        pca953x_write_regs(chip, PCA957X_BKEN, val);
 
index 945f0cda8529d38af0cf747e300d58ef1e71d1f6..404f3c61ef9b19f43243d61eef656e8d81a899c9 100644 (file)
@@ -91,6 +91,8 @@ struct pcf857x {
        spinlock_t              slock;          /* protect irq demux */
        unsigned                out;            /* software latch */
        unsigned                status;         /* current status */
+       unsigned int            irq_parent;
+       unsigned                irq_enabled;    /* enabled irqs */
 
        int (*write)(struct i2c_client *client, unsigned data);
        int (*read)(struct i2c_client *client);
@@ -194,7 +196,7 @@ static irqreturn_t pcf857x_irq(int irq, void *data)
         * interrupt source, just to avoid bad irqs
         */
 
-       change = (gpio->status ^ status);
+       change = (gpio->status ^ status) & gpio->irq_enabled;
        for_each_set_bit(i, &change, gpio->chip.ngpio)
                handle_nested_irq(irq_find_mapping(gpio->chip.irqdomain, i));
        gpio->status = status;
@@ -209,29 +211,62 @@ static irqreturn_t pcf857x_irq(int irq, void *data)
  */
 static void noop(struct irq_data *data) { }
 
-static unsigned int noop_ret(struct irq_data *data)
+static int pcf857x_irq_set_wake(struct irq_data *data, unsigned int on)
 {
-       return 0;
+       struct pcf857x *gpio = irq_data_get_irq_chip_data(data);
+
+       int error = 0;
+
+       if (gpio->irq_parent) {
+               error = irq_set_irq_wake(gpio->irq_parent, on);
+               if (error) {
+                       dev_dbg(&gpio->client->dev,
+                               "irq %u doesn't support irq_set_wake\n",
+                               gpio->irq_parent);
+                       gpio->irq_parent = 0;
+               }
+       }
+       return error;
 }
 
-static int pcf857x_irq_set_wake(struct irq_data *data, unsigned int on)
+static void pcf857x_irq_enable(struct irq_data *data)
 {
        struct pcf857x *gpio = irq_data_get_irq_chip_data(data);
 
-       irq_set_irq_wake(gpio->client->irq, on);
-       return 0;
+       gpio->irq_enabled |= (1 << data->hwirq);
+}
+
+static void pcf857x_irq_disable(struct irq_data *data)
+{
+       struct pcf857x *gpio = irq_data_get_irq_chip_data(data);
+
+       gpio->irq_enabled &= ~(1 << data->hwirq);
+}
+
+static void pcf857x_irq_bus_lock(struct irq_data *data)
+{
+       struct pcf857x *gpio = irq_data_get_irq_chip_data(data);
+
+       mutex_lock(&gpio->lock);
+}
+
+static void pcf857x_irq_bus_sync_unlock(struct irq_data *data)
+{
+       struct pcf857x *gpio = irq_data_get_irq_chip_data(data);
+
+       mutex_unlock(&gpio->lock);
 }
 
 static struct irq_chip pcf857x_irq_chip = {
        .name           = "pcf857x",
-       .irq_startup    = noop_ret,
-       .irq_shutdown   = noop,
-       .irq_enable     = noop,
-       .irq_disable    = noop,
+       .irq_enable     = pcf857x_irq_enable,
+       .irq_disable    = pcf857x_irq_disable,
        .irq_ack        = noop,
        .irq_mask       = noop,
        .irq_unmask     = noop,
        .irq_set_wake   = pcf857x_irq_set_wake,
+       .irq_bus_lock           = pcf857x_irq_bus_lock,
+       .irq_bus_sync_unlock    = pcf857x_irq_bus_sync_unlock,
 };
 
 /*-------------------------------------------------------------------------*/
@@ -364,6 +399,7 @@ static int pcf857x_probe(struct i2c_client *client,
 
                gpiochip_set_chained_irqchip(&gpio->chip, &pcf857x_irq_chip,
                                             client->irq, NULL);
+               gpio->irq_parent = client->irq;
        }
 
        /* Let platform code set up the GPIOs and their users.
index fd39774659484fa68fb76d229f1ec9834c480b60..1e14a6c74ed139413564417339fc8e4399c55341 100644 (file)
@@ -177,8 +177,17 @@ static int gpio_rcar_irq_set_wake(struct irq_data *d, unsigned int on)
        struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
        struct gpio_rcar_priv *p = container_of(gc, struct gpio_rcar_priv,
                                                gpio_chip);
-
-       irq_set_irq_wake(p->irq_parent, on);
+       int error;
+
+       if (p->irq_parent) {
+               error = irq_set_irq_wake(p->irq_parent, on);
+               if (error) {
+                       dev_dbg(&p->pdev->dev,
+                               "irq %u doesn't support irq_set_wake\n",
+                               p->irq_parent);
+                       p->irq_parent = 0;
+               }
+       }
 
        if (!p->clk)
                return 0;
index 202361eb72797a92e92f4c02b395c70af0908a3f..81bdbe7ba2a4bce1b8e103d3b06d789b13e92e70 100644 (file)
@@ -58,7 +58,7 @@
 #define XWAY_STP_ADSL_MASK     0x3
 
 /* 2 groups of 3 bits can be driven by the phys */
-#define XWAY_STP_PHY_MASK      0x3
+#define XWAY_STP_PHY_MASK      0x7
 #define XWAY_STP_PHY1_SHIFT    27
 #define XWAY_STP_PHY2_SHIFT    15
 
@@ -200,7 +200,7 @@ static int xway_stp_hw_init(struct xway_stp *chip)
 static int xway_stp_probe(struct platform_device *pdev)
 {
        struct resource *res;
-       const __be32 *shadow, *groups, *dsl, *phy;
+       u32 shadow, groups, dsl, phy;
        struct xway_stp *chip;
        struct clk *clk;
        int ret = 0;
@@ -223,33 +223,28 @@ static int xway_stp_probe(struct platform_device *pdev)
        chip->gc.owner = THIS_MODULE;
 
        /* store the shadow value if one was passed by the devicetree */
-       shadow = of_get_property(pdev->dev.of_node, "lantiq,shadow", NULL);
-       if (shadow)
-               chip->shadow = be32_to_cpu(*shadow);
+       if (!of_property_read_u32(pdev->dev.of_node, "lantiq,shadow", &shadow))
+               chip->shadow = shadow;
 
        /* find out which gpio groups should be enabled */
-       groups = of_get_property(pdev->dev.of_node, "lantiq,groups", NULL);
-       if (groups)
-               chip->groups = be32_to_cpu(*groups) & XWAY_STP_GROUP_MASK;
+       if (!of_property_read_u32(pdev->dev.of_node, "lantiq,groups", &groups))
+               chip->groups = groups & XWAY_STP_GROUP_MASK;
        else
                chip->groups = XWAY_STP_GROUP0;
        chip->gc.ngpio = fls(chip->groups) * 8;
 
        /* find out which gpios are controlled by the dsl core */
-       dsl = of_get_property(pdev->dev.of_node, "lantiq,dsl", NULL);
-       if (dsl)
-               chip->dsl = be32_to_cpu(*dsl) & XWAY_STP_ADSL_MASK;
+       if (!of_property_read_u32(pdev->dev.of_node, "lantiq,dsl", &dsl))
+               chip->dsl = dsl & XWAY_STP_ADSL_MASK;
 
        /* find out which gpios are controlled by the phys */
        if (of_machine_is_compatible("lantiq,ar9") ||
                        of_machine_is_compatible("lantiq,gr9") ||
                        of_machine_is_compatible("lantiq,vr9")) {
-               phy = of_get_property(pdev->dev.of_node, "lantiq,phy1", NULL);
-               if (phy)
-                       chip->phy1 = be32_to_cpu(*phy) & XWAY_STP_PHY_MASK;
-               phy = of_get_property(pdev->dev.of_node, "lantiq,phy2", NULL);
-               if (phy)
-                       chip->phy2 = be32_to_cpu(*phy) & XWAY_STP_PHY_MASK;
+               if (!of_property_read_u32(pdev->dev.of_node, "lantiq,phy1", &phy))
+                       chip->phy1 = phy & XWAY_STP_PHY_MASK;
+               if (!of_property_read_u32(pdev->dev.of_node, "lantiq,phy2", &phy))
+                       chip->phy2 = phy & XWAY_STP_PHY_MASK;
        }
 
        /* check which edge trigger we should use, default to a falling edge */
index 46b89614aa9121ba7d82a1a162f9a164a82e295c..12c99d969b983638ca16838919f34b0db76c7148 100644 (file)
@@ -292,7 +292,6 @@ static int tb10x_gpio_remove(struct platform_device *pdev)
                                        BIT(tb10x_gpio->gc.ngpio) - 1, 0, 0);
                kfree(tb10x_gpio->domain->gc);
                irq_domain_remove(tb10x_gpio->domain);
-               free_irq(tb10x_gpio->irq, tb10x_gpio);
        }
        gpiochip_remove(&tb10x_gpio->gc);
 
index 1741981d53c8fc2e9565e54d10f29ecf6552a7c7..9b25c90f725c21138f955dbc1a4745749114a9b4 100644 (file)
@@ -288,7 +288,7 @@ static void tegra_gpio_irq_handler(unsigned int irq, struct irq_desc *desc)
                        tegra_gpio_writel(1 << pin, GPIO_INT_CLR(gpio));
 
                        /* if gpio is edge triggered, clear condition
-                        * before executing the hander so that we don't
+                        * before executing the handler so that we don't
                         * miss edges
                         */
                        if (lvl & (0x100 << pin)) {
@@ -515,8 +515,8 @@ static int tegra_gpio_probe(struct platform_device *pdev)
        for (i = 0; i < tegra_gpio_bank_count; i++) {
                bank = &tegra_gpio_banks[i];
 
-               irq_set_chained_handler(bank->irq, tegra_gpio_irq_handler);
-               irq_set_handler_data(bank->irq, bank);
+               irq_set_chained_handler_and_data(bank->irq,
+                                                tegra_gpio_irq_handler, bank);
 
                for (j = 0; j < 4; j++)
                        spin_lock_init(&bank->lvl_lock[j]);
index 92fbabd82879553e3a897ffd5d250d405a56752f..b29a102d136b0a30d20bf234c8a0776d4749a980 100644 (file)
@@ -440,7 +440,7 @@ static int ts5500_dio_remove(struct platform_device *pdev)
        return 0;
 }
 
-static struct platform_device_id ts5500_dio_ids[] = {
+static const struct platform_device_id ts5500_dio_ids[] = {
        { "ts5500-dio1", TS5500_DIO1 },
        { "ts5500-dio2", TS5500_DIO2 },
        { "ts5500-dio-lcd", TS5500_LCD },
index fb9d29a5d584c0cf7793ab517f13c3383393405f..d57068b9083e10e280ffa1bc8dbd92ed70b7d970 100644 (file)
 #include <linux/of_gpio.h>
 #include <linux/gpio.h>
 #include <linux/gpio/driver.h>
+#include <linux/acpi.h>
 #include <linux/basic_mmio_gpio.h>
 
+#include "gpiolib.h"
+
 #define XGENE_MAX_GPIO_DS              22
 #define XGENE_MAX_GPIO_DS_IRQ          6
 
@@ -112,7 +115,6 @@ static int xgene_gpio_sb_probe(struct platform_device *pdev)
                                   GFP_KERNEL);
        if (!priv->irq)
                return -ENOMEM;
-       memset(priv->irq, 0, sizeof(u32) * XGENE_MAX_GPIO_DS);
 
        for (i = 0; i < priv->nirq; i++) {
                priv->irq[default_lines[i]] = platform_get_irq(pdev, i);
@@ -129,6 +131,11 @@ static int xgene_gpio_sb_probe(struct platform_device *pdev)
        else
                dev_info(&pdev->dev, "X-Gene GPIO Standby driver registered\n");
 
+       if (priv->nirq > 0) {
+               /* Register interrupt handlers for gpio signaled acpi events */
+               acpi_gpiochip_request_interrupts(&priv->bgc.gc);
+       }
+
        return ret;
 }
 
@@ -136,6 +143,10 @@ static int xgene_gpio_sb_remove(struct platform_device *pdev)
 {
        struct xgene_gpio_sb *priv = platform_get_drvdata(pdev);
 
+       if (priv->nirq > 0) {
+               acpi_gpiochip_free_interrupts(&priv->bgc.gc);
+       }
+
        return bgpio_remove(&priv->bgc);
 }
 
@@ -145,10 +156,19 @@ static const struct of_device_id xgene_gpio_sb_of_match[] = {
 };
 MODULE_DEVICE_TABLE(of, xgene_gpio_sb_of_match);
 
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id xgene_gpio_sb_acpi_match[] = {
+       {"APMC0D15", 0},
+       {},
+};
+MODULE_DEVICE_TABLE(acpi, xgene_gpio_sb_acpi_match);
+#endif
+
 static struct platform_driver xgene_gpio_sb_driver = {
        .driver = {
                   .name = "xgene-gpio-sb",
                   .of_match_table = xgene_gpio_sb_of_match,
+                  .acpi_match_table = ACPI_PTR(xgene_gpio_sb_acpi_match),
                   },
        .probe = xgene_gpio_sb_probe,
        .remove = xgene_gpio_sb_remove,
index 61243d177740299df00bbdb42de04c29057b5201..77fe5d3cb105b97057aab4b900ba595a837f4e50 100644 (file)
 /**
  * struct xgpio_instance - Stores information about GPIO device
  * @mmchip: OF GPIO chip for memory mapped banks
+ * @gpio_width: GPIO width for every channel
  * @gpio_state: GPIO state shadow register
  * @gpio_dir: GPIO direction shadow register
  * @gpio_lock: Lock used for synchronization
- * @inited: True if the port has been inited
  */
 struct xgpio_instance {
        struct of_mm_gpio_chip mmchip;
@@ -231,6 +231,8 @@ static void xgpio_save_regs(struct of_mm_gpio_chip *mm_gc)
  * @pdev: pointer to the platform device
  *
  * This function remove gpiochips and frees all the allocated resources.
+ *
+ * Return: 0 always
  */
 static int xgpio_remove(struct platform_device *pdev)
 {
diff --git a/drivers/gpio/gpio-xlp.c b/drivers/gpio/gpio-xlp.c
new file mode 100644 (file)
index 0000000..9bdab72
--- /dev/null
@@ -0,0 +1,427 @@
+/*
+ * Copyright (C) 2003-2015 Broadcom 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/gpio.h>
+#include <linux/platform_device.h>
+#include <linux/of_device.h>
+#include <linux/module.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+
+/*
+ * XLP GPIO has multiple 32 bit registers for each feature where each register
+ * controls 32 pins. So, pins up to 64 require 2 32-bit registers and up to 96
+ * require 3 32-bit registers for each feature.
+ * Here we only define offset of the first register for each feature. Offset of
+ * the registers for pins greater than 32 can be calculated as following(Use
+ * GPIO_INT_STAT as example):
+ *
+ * offset = (gpio / XLP_GPIO_REGSZ) * 4;
+ * reg_addr = addr + offset;
+ *
+ * where addr is base address of the that feature register and gpio is the pin.
+ */
+#define GPIO_OUTPUT_EN         0x00
+#define GPIO_PADDRV            0x08
+#define GPIO_INT_EN00          0x18
+#define GPIO_INT_EN10          0x20
+#define GPIO_INT_EN20          0x28
+#define GPIO_INT_EN30          0x30
+#define GPIO_INT_POL           0x38
+#define GPIO_INT_TYPE          0x40
+#define GPIO_INT_STAT          0x48
+
+#define GPIO_9XX_BYTESWAP      0X00
+#define GPIO_9XX_CTRL          0X04
+#define GPIO_9XX_OUTPUT_EN     0x14
+#define GPIO_9XX_PADDRV                0x24
+/*
+ * Only for 4 interrupt enable reg are defined for now,
+ * total reg available are 12.
+ */
+#define GPIO_9XX_INT_EN00      0x44
+#define GPIO_9XX_INT_EN10      0x54
+#define GPIO_9XX_INT_EN20      0x64
+#define GPIO_9XX_INT_EN30      0x74
+#define GPIO_9XX_INT_POL       0x104
+#define GPIO_9XX_INT_TYPE      0x114
+#define GPIO_9XX_INT_STAT      0x124
+
+#define GPIO_3XX_INT_EN00      0x18
+#define GPIO_3XX_INT_EN10      0x20
+#define GPIO_3XX_INT_EN20      0x28
+#define GPIO_3XX_INT_EN30      0x30
+#define GPIO_3XX_INT_POL       0x78
+#define GPIO_3XX_INT_TYPE      0x80
+#define GPIO_3XX_INT_STAT      0x88
+
+/* Interrupt type register mask */
+#define XLP_GPIO_IRQ_TYPE_LVL  0x0
+#define XLP_GPIO_IRQ_TYPE_EDGE 0x1
+
+/* Interrupt polarity register mask */
+#define XLP_GPIO_IRQ_POL_HIGH  0x0
+#define XLP_GPIO_IRQ_POL_LOW   0x1
+
+#define XLP_GPIO_REGSZ         32
+#define XLP_GPIO_IRQ_BASE      768
+#define XLP_MAX_NR_GPIO                96
+
+/* XLP variants supported by this driver */
+enum {
+       XLP_GPIO_VARIANT_XLP832 = 1,
+       XLP_GPIO_VARIANT_XLP316,
+       XLP_GPIO_VARIANT_XLP208,
+       XLP_GPIO_VARIANT_XLP980,
+       XLP_GPIO_VARIANT_XLP532
+};
+
+struct xlp_gpio_priv {
+       struct gpio_chip chip;
+       DECLARE_BITMAP(gpio_enabled_mask, XLP_MAX_NR_GPIO);
+       void __iomem *gpio_intr_en;     /* pointer to first intr enable reg */
+       void __iomem *gpio_intr_stat;   /* pointer to first intr status reg */
+       void __iomem *gpio_intr_type;   /* pointer to first intr type reg */
+       void __iomem *gpio_intr_pol;    /* pointer to first intr polarity reg */
+       void __iomem *gpio_out_en;      /* pointer to first output enable reg */
+       void __iomem *gpio_paddrv;      /* pointer to first pad drive reg */
+       spinlock_t lock;
+};
+
+static struct xlp_gpio_priv *gpio_chip_to_xlp_priv(struct gpio_chip *gc)
+{
+       return container_of(gc, struct xlp_gpio_priv, chip);
+}
+
+static int xlp_gpio_get_reg(void __iomem *addr, unsigned gpio)
+{
+       u32 pos, regset;
+
+       pos = gpio % XLP_GPIO_REGSZ;
+       regset = (gpio / XLP_GPIO_REGSZ) * 4;
+       return !!(readl(addr + regset) & BIT(pos));
+}
+
+static void xlp_gpio_set_reg(void __iomem *addr, unsigned gpio, int state)
+{
+       u32 value, pos, regset;
+
+       pos = gpio % XLP_GPIO_REGSZ;
+       regset = (gpio / XLP_GPIO_REGSZ) * 4;
+       value = readl(addr + regset);
+
+       if (state)
+               value |= BIT(pos);
+       else
+               value &= ~BIT(pos);
+
+       writel(value, addr + regset);
+}
+
+static void xlp_gpio_irq_disable(struct irq_data *d)
+{
+       struct gpio_chip *gc  = irq_data_get_irq_chip_data(d);
+       struct xlp_gpio_priv *priv = gpio_chip_to_xlp_priv(gc);
+       unsigned long flags;
+
+       spin_lock_irqsave(&priv->lock, flags);
+       xlp_gpio_set_reg(priv->gpio_intr_en, d->hwirq, 0x0);
+       __clear_bit(d->hwirq, priv->gpio_enabled_mask);
+       spin_unlock_irqrestore(&priv->lock, flags);
+}
+
+static void xlp_gpio_irq_mask_ack(struct irq_data *d)
+{
+       struct gpio_chip *gc  = irq_data_get_irq_chip_data(d);
+       struct xlp_gpio_priv *priv = gpio_chip_to_xlp_priv(gc);
+       unsigned long flags;
+
+       spin_lock_irqsave(&priv->lock, flags);
+       xlp_gpio_set_reg(priv->gpio_intr_en, d->hwirq, 0x0);
+       xlp_gpio_set_reg(priv->gpio_intr_stat, d->hwirq, 0x1);
+       __clear_bit(d->hwirq, priv->gpio_enabled_mask);
+       spin_unlock_irqrestore(&priv->lock, flags);
+}
+
+static void xlp_gpio_irq_unmask(struct irq_data *d)
+{
+       struct gpio_chip *gc  = irq_data_get_irq_chip_data(d);
+       struct xlp_gpio_priv *priv = gpio_chip_to_xlp_priv(gc);
+       unsigned long flags;
+
+       spin_lock_irqsave(&priv->lock, flags);
+       xlp_gpio_set_reg(priv->gpio_intr_en, d->hwirq, 0x1);
+       __set_bit(d->hwirq, priv->gpio_enabled_mask);
+       spin_unlock_irqrestore(&priv->lock, flags);
+}
+
+static int xlp_gpio_set_irq_type(struct irq_data *d, unsigned int type)
+{
+       struct gpio_chip *gc  = irq_data_get_irq_chip_data(d);
+       struct xlp_gpio_priv *priv = gpio_chip_to_xlp_priv(gc);
+       int pol, irq_type;
+
+       switch (type) {
+       case IRQ_TYPE_EDGE_RISING:
+               irq_type = XLP_GPIO_IRQ_TYPE_EDGE;
+               pol = XLP_GPIO_IRQ_POL_HIGH;
+               break;
+       case IRQ_TYPE_EDGE_FALLING:
+               irq_type = XLP_GPIO_IRQ_TYPE_EDGE;
+               pol = XLP_GPIO_IRQ_POL_LOW;
+               break;
+       case IRQ_TYPE_LEVEL_HIGH:
+               irq_type = XLP_GPIO_IRQ_TYPE_LVL;
+               pol = XLP_GPIO_IRQ_POL_HIGH;
+               break;
+       case IRQ_TYPE_LEVEL_LOW:
+               irq_type = XLP_GPIO_IRQ_TYPE_LVL;
+               pol = XLP_GPIO_IRQ_POL_LOW;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       xlp_gpio_set_reg(priv->gpio_intr_type, d->hwirq, irq_type);
+       xlp_gpio_set_reg(priv->gpio_intr_pol, d->hwirq, pol);
+
+       return 0;
+}
+
+static struct irq_chip xlp_gpio_irq_chip = {
+       .name           = "XLP-GPIO",
+       .irq_mask_ack   = xlp_gpio_irq_mask_ack,
+       .irq_disable    = xlp_gpio_irq_disable,
+       .irq_set_type   = xlp_gpio_set_irq_type,
+       .irq_unmask     = xlp_gpio_irq_unmask,
+       .flags          = IRQCHIP_ONESHOT_SAFE,
+};
+
+static irqreturn_t xlp_gpio_generic_handler(int irq, void *data)
+{
+       struct xlp_gpio_priv *priv = data;
+       int gpio, regoff;
+       u32 gpio_stat;
+
+       regoff = -1;
+       gpio_stat = 0;
+       for_each_set_bit(gpio, priv->gpio_enabled_mask, XLP_MAX_NR_GPIO) {
+               if (regoff != gpio / XLP_GPIO_REGSZ) {
+                       regoff = gpio / XLP_GPIO_REGSZ;
+                       gpio_stat = readl(priv->gpio_intr_stat + regoff * 4);
+               }
+               if (gpio_stat & BIT(gpio % XLP_GPIO_REGSZ))
+                       generic_handle_irq(irq_find_mapping(
+                                               priv->chip.irqdomain, gpio));
+       }
+
+       return IRQ_HANDLED;
+}
+
+static int xlp_gpio_dir_output(struct gpio_chip *gc, unsigned gpio, int state)
+{
+       struct xlp_gpio_priv *priv = gpio_chip_to_xlp_priv(gc);
+
+       BUG_ON(gpio >= gc->ngpio);
+       xlp_gpio_set_reg(priv->gpio_out_en, gpio, 0x1);
+
+       return 0;
+}
+
+static int xlp_gpio_dir_input(struct gpio_chip *gc, unsigned gpio)
+{
+       struct xlp_gpio_priv *priv = gpio_chip_to_xlp_priv(gc);
+
+       BUG_ON(gpio >= gc->ngpio);
+       xlp_gpio_set_reg(priv->gpio_out_en, gpio, 0x0);
+
+       return 0;
+}
+
+static int xlp_gpio_get(struct gpio_chip *gc, unsigned gpio)
+{
+       struct xlp_gpio_priv *priv = gpio_chip_to_xlp_priv(gc);
+
+       BUG_ON(gpio >= gc->ngpio);
+       return xlp_gpio_get_reg(priv->gpio_paddrv, gpio);
+}
+
+static void xlp_gpio_set(struct gpio_chip *gc, unsigned gpio, int state)
+{
+       struct xlp_gpio_priv *priv = gpio_chip_to_xlp_priv(gc);
+
+       BUG_ON(gpio >= gc->ngpio);
+       xlp_gpio_set_reg(priv->gpio_paddrv, gpio, state);
+}
+
+static const struct of_device_id xlp_gpio_of_ids[] = {
+       {
+               .compatible = "netlogic,xlp832-gpio",
+               .data       = (void *)XLP_GPIO_VARIANT_XLP832,
+       },
+       {
+               .compatible = "netlogic,xlp316-gpio",
+               .data       = (void *)XLP_GPIO_VARIANT_XLP316,
+       },
+       {
+               .compatible = "netlogic,xlp208-gpio",
+               .data       = (void *)XLP_GPIO_VARIANT_XLP208,
+       },
+       {
+               .compatible = "netlogic,xlp980-gpio",
+               .data       = (void *)XLP_GPIO_VARIANT_XLP980,
+       },
+       {
+               .compatible = "netlogic,xlp532-gpio",
+               .data       = (void *)XLP_GPIO_VARIANT_XLP532,
+       },
+       { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, xlp_gpio_of_ids);
+
+static int xlp_gpio_probe(struct platform_device *pdev)
+{
+       struct gpio_chip *gc;
+       struct resource *iores;
+       struct xlp_gpio_priv *priv;
+       const struct of_device_id *of_id;
+       void __iomem *gpio_base;
+       int irq_base, irq, err;
+       int ngpio;
+       u32 soc_type;
+
+       iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!iores)
+               return -ENODEV;
+
+       priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       gpio_base = devm_ioremap_resource(&pdev->dev, iores);
+       if (IS_ERR(gpio_base))
+               return PTR_ERR(gpio_base);
+
+       irq = platform_get_irq(pdev, 0);
+       if (irq < 0)
+               return irq;
+
+       of_id = of_match_device(xlp_gpio_of_ids, &pdev->dev);
+       if (!of_id) {
+               dev_err(&pdev->dev, "Failed to get soc type!\n");
+               return -ENODEV;
+       }
+
+       soc_type = (uintptr_t) of_id->data;
+
+       switch (soc_type) {
+       case XLP_GPIO_VARIANT_XLP832:
+               priv->gpio_out_en = gpio_base + GPIO_OUTPUT_EN;
+               priv->gpio_paddrv = gpio_base + GPIO_PADDRV;
+               priv->gpio_intr_stat = gpio_base + GPIO_INT_STAT;
+               priv->gpio_intr_type = gpio_base + GPIO_INT_TYPE;
+               priv->gpio_intr_pol = gpio_base + GPIO_INT_POL;
+               priv->gpio_intr_en = gpio_base + GPIO_INT_EN00;
+               ngpio = 41;
+               break;
+       case XLP_GPIO_VARIANT_XLP208:
+       case XLP_GPIO_VARIANT_XLP316:
+               priv->gpio_out_en = gpio_base + GPIO_OUTPUT_EN;
+               priv->gpio_paddrv = gpio_base + GPIO_PADDRV;
+               priv->gpio_intr_stat = gpio_base + GPIO_3XX_INT_STAT;
+               priv->gpio_intr_type = gpio_base + GPIO_3XX_INT_TYPE;
+               priv->gpio_intr_pol = gpio_base + GPIO_3XX_INT_POL;
+               priv->gpio_intr_en = gpio_base + GPIO_3XX_INT_EN00;
+
+               ngpio = (soc_type == XLP_GPIO_VARIANT_XLP208) ? 42 : 57;
+               break;
+       case XLP_GPIO_VARIANT_XLP980:
+       case XLP_GPIO_VARIANT_XLP532:
+               priv->gpio_out_en = gpio_base + GPIO_9XX_OUTPUT_EN;
+               priv->gpio_paddrv = gpio_base + GPIO_9XX_PADDRV;
+               priv->gpio_intr_stat = gpio_base + GPIO_9XX_INT_STAT;
+               priv->gpio_intr_type = gpio_base + GPIO_9XX_INT_TYPE;
+               priv->gpio_intr_pol = gpio_base + GPIO_9XX_INT_POL;
+               priv->gpio_intr_en = gpio_base + GPIO_9XX_INT_EN00;
+
+               ngpio = (soc_type == XLP_GPIO_VARIANT_XLP980) ? 66 : 67;
+               break;
+       default:
+               dev_err(&pdev->dev, "Unknown Processor type!\n");
+               return -ENODEV;
+       }
+
+       bitmap_zero(priv->gpio_enabled_mask, XLP_MAX_NR_GPIO);
+
+       gc = &priv->chip;
+
+       gc->owner = THIS_MODULE;
+       gc->label = dev_name(&pdev->dev);
+       gc->base = 0;
+       gc->dev = &pdev->dev;
+       gc->ngpio = ngpio;
+       gc->of_node = pdev->dev.of_node;
+       gc->direction_output = xlp_gpio_dir_output;
+       gc->direction_input = xlp_gpio_dir_input;
+       gc->set = xlp_gpio_set;
+       gc->get = xlp_gpio_get;
+
+       spin_lock_init(&priv->lock);
+
+       err = devm_request_irq(&pdev->dev, irq, xlp_gpio_generic_handler,
+                       IRQ_TYPE_NONE, pdev->name, priv);
+       if (err)
+               return err;
+
+       irq_base = irq_alloc_descs(-1, XLP_GPIO_IRQ_BASE, gc->ngpio, 0);
+       if (irq_base < 0) {
+               dev_err(&pdev->dev, "Failed to allocate IRQ numbers\n");
+               return err;
+       }
+
+       err = gpiochip_add(gc);
+       if (err < 0)
+               goto out_free_desc;
+
+       err = gpiochip_irqchip_add(gc, &xlp_gpio_irq_chip, irq_base,
+                               handle_level_irq, IRQ_TYPE_NONE);
+       if (err) {
+               dev_err(&pdev->dev, "Could not connect irqchip to gpiochip!\n");
+               goto out_gpio_remove;
+       }
+
+       dev_info(&pdev->dev, "registered %d GPIOs\n", gc->ngpio);
+
+       return 0;
+
+out_gpio_remove:
+       gpiochip_remove(gc);
+out_free_desc:
+       irq_free_descs(irq_base, gc->ngpio);
+       return err;
+}
+
+static struct platform_driver xlp_gpio_driver = {
+       .driver         = {
+               .name   = "xlp-gpio",
+               .of_match_table = xlp_gpio_of_ids,
+       },
+       .probe          = xlp_gpio_probe,
+};
+module_platform_driver(xlp_gpio_driver);
+
+MODULE_AUTHOR("Kamlakant Patel <kamlakant.patel@broadcom.com>");
+MODULE_AUTHOR("Ganesan Ramalingam <ganesanr@broadcom.com>");
+MODULE_DESCRIPTION("Netlogic XLP GPIO Driver");
+MODULE_LICENSE("GPL v2");
index 184c4b1b255800dfb4a53914e3d803b51e70760f..2e87c4b8da26d5336164359a6a6e27f0a32de68c 100644 (file)
 #include <linux/module.h>
 #include <linux/platform_device.h>
 #include <linux/pm_runtime.h>
+#include <linux/of.h>
 
 #define DRIVER_NAME "zynq-gpio"
 
 /* Maximum banks */
 #define ZYNQ_GPIO_MAX_BANK     4
+#define ZYNQMP_GPIO_MAX_BANK   6
 
 #define ZYNQ_GPIO_BANK0_NGPIO  32
 #define ZYNQ_GPIO_BANK1_NGPIO  22
 #define ZYNQ_GPIO_BANK2_NGPIO  32
 #define ZYNQ_GPIO_BANK3_NGPIO  32
 
-#define ZYNQ_GPIO_NR_GPIOS     (ZYNQ_GPIO_BANK0_NGPIO + \
-                                ZYNQ_GPIO_BANK1_NGPIO + \
-                                ZYNQ_GPIO_BANK2_NGPIO + \
-                                ZYNQ_GPIO_BANK3_NGPIO)
-
-#define ZYNQ_GPIO_BANK0_PIN_MIN        0
-#define ZYNQ_GPIO_BANK0_PIN_MAX        (ZYNQ_GPIO_BANK0_PIN_MIN + \
-                                       ZYNQ_GPIO_BANK0_NGPIO - 1)
-#define ZYNQ_GPIO_BANK1_PIN_MIN        (ZYNQ_GPIO_BANK0_PIN_MAX + 1)
-#define ZYNQ_GPIO_BANK1_PIN_MAX        (ZYNQ_GPIO_BANK1_PIN_MIN + \
-                                       ZYNQ_GPIO_BANK1_NGPIO - 1)
-#define ZYNQ_GPIO_BANK2_PIN_MIN        (ZYNQ_GPIO_BANK1_PIN_MAX + 1)
-#define ZYNQ_GPIO_BANK2_PIN_MAX        (ZYNQ_GPIO_BANK2_PIN_MIN + \
-                                       ZYNQ_GPIO_BANK2_NGPIO - 1)
-#define ZYNQ_GPIO_BANK3_PIN_MIN        (ZYNQ_GPIO_BANK2_PIN_MAX + 1)
-#define ZYNQ_GPIO_BANK3_PIN_MAX        (ZYNQ_GPIO_BANK3_PIN_MIN + \
-                                       ZYNQ_GPIO_BANK3_NGPIO - 1)
+#define ZYNQMP_GPIO_BANK0_NGPIO 26
+#define ZYNQMP_GPIO_BANK1_NGPIO 26
+#define ZYNQMP_GPIO_BANK2_NGPIO 26
+#define ZYNQMP_GPIO_BANK3_NGPIO 32
+#define ZYNQMP_GPIO_BANK4_NGPIO 32
+#define ZYNQMP_GPIO_BANK5_NGPIO 32
+
+#define        ZYNQ_GPIO_NR_GPIOS      118
+#define        ZYNQMP_GPIO_NR_GPIOS    174
+
+#define ZYNQ_GPIO_BANK0_PIN_MIN(str)   0
+#define ZYNQ_GPIO_BANK0_PIN_MAX(str)   (ZYNQ_GPIO_BANK0_PIN_MIN(str) + \
+                                       ZYNQ##str##_GPIO_BANK0_NGPIO - 1)
+#define ZYNQ_GPIO_BANK1_PIN_MIN(str)   (ZYNQ_GPIO_BANK0_PIN_MAX(str) + 1)
+#define ZYNQ_GPIO_BANK1_PIN_MAX(str)   (ZYNQ_GPIO_BANK1_PIN_MIN(str) + \
+                                       ZYNQ##str##_GPIO_BANK1_NGPIO - 1)
+#define ZYNQ_GPIO_BANK2_PIN_MIN(str)   (ZYNQ_GPIO_BANK1_PIN_MAX(str) + 1)
+#define ZYNQ_GPIO_BANK2_PIN_MAX(str)   (ZYNQ_GPIO_BANK2_PIN_MIN(str) + \
+                                       ZYNQ##str##_GPIO_BANK2_NGPIO - 1)
+#define ZYNQ_GPIO_BANK3_PIN_MIN(str)   (ZYNQ_GPIO_BANK2_PIN_MAX(str) + 1)
+#define ZYNQ_GPIO_BANK3_PIN_MAX(str)   (ZYNQ_GPIO_BANK3_PIN_MIN(str) + \
+                                       ZYNQ##str##_GPIO_BANK3_NGPIO - 1)
+#define ZYNQ_GPIO_BANK4_PIN_MIN(str)   (ZYNQ_GPIO_BANK3_PIN_MAX(str) + 1)
+#define ZYNQ_GPIO_BANK4_PIN_MAX(str)   (ZYNQ_GPIO_BANK4_PIN_MIN(str) + \
+                                       ZYNQ##str##_GPIO_BANK4_NGPIO - 1)
+#define ZYNQ_GPIO_BANK5_PIN_MIN(str)   (ZYNQ_GPIO_BANK4_PIN_MAX(str) + 1)
+#define ZYNQ_GPIO_BANK5_PIN_MAX(str)   (ZYNQ_GPIO_BANK5_PIN_MIN(str) + \
+                                       ZYNQ##str##_GPIO_BANK5_NGPIO - 1)
 
 
 /* Register offsets for the GPIO device */
  * @base_addr: base address of the GPIO device
  * @clk:       clock resource for this controller
  * @irq:       interrupt for the GPIO device
+ * @p_data:    pointer to platform data
  */
 struct zynq_gpio {
        struct gpio_chip chip;
        void __iomem *base_addr;
        struct clk *clk;
        int irq;
+       const struct zynq_platform_data *p_data;
+};
+
+/**
+ * struct zynq_platform_data -  zynq gpio platform data structure
+ * @label:     string to store in gpio->label
+ * @ngpio:     max number of gpio pins
+ * @max_bank:  maximum number of gpio banks
+ * @bank_min:  this array represents bank's min pin
+ * @bank_max:  this array represents bank's max pin
+*/
+struct zynq_platform_data {
+       const char *label;
+       u16 ngpio;
+       int max_bank;
+       int bank_min[ZYNQMP_GPIO_MAX_BANK];
+       int bank_max[ZYNQMP_GPIO_MAX_BANK];
 };
 
 static struct irq_chip zynq_gpio_level_irqchip;
@@ -112,39 +143,26 @@ static struct irq_chip zynq_gpio_edge_irqchip;
  */
 static inline void zynq_gpio_get_bank_pin(unsigned int pin_num,
                                          unsigned int *bank_num,
-                                         unsigned int *bank_pin_num)
+                                         unsigned int *bank_pin_num,
+                                         struct zynq_gpio *gpio)
 {
-       switch (pin_num) {
-       case ZYNQ_GPIO_BANK0_PIN_MIN ... ZYNQ_GPIO_BANK0_PIN_MAX:
-               *bank_num = 0;
-               *bank_pin_num = pin_num;
-               break;
-       case ZYNQ_GPIO_BANK1_PIN_MIN ... ZYNQ_GPIO_BANK1_PIN_MAX:
-               *bank_num = 1;
-               *bank_pin_num = pin_num - ZYNQ_GPIO_BANK1_PIN_MIN;
-               break;
-       case ZYNQ_GPIO_BANK2_PIN_MIN ... ZYNQ_GPIO_BANK2_PIN_MAX:
-               *bank_num = 2;
-               *bank_pin_num = pin_num - ZYNQ_GPIO_BANK2_PIN_MIN;
-               break;
-       case ZYNQ_GPIO_BANK3_PIN_MIN ... ZYNQ_GPIO_BANK3_PIN_MAX:
-               *bank_num = 3;
-               *bank_pin_num = pin_num - ZYNQ_GPIO_BANK3_PIN_MIN;
-               break;
-       default:
-               WARN(true, "invalid GPIO pin number: %u", pin_num);
-               *bank_num = 0;
-               *bank_pin_num = 0;
-               break;
+       int bank;
+
+       for (bank = 0; bank < gpio->p_data->max_bank; bank++) {
+               if ((pin_num >= gpio->p_data->bank_min[bank]) &&
+                       (pin_num <= gpio->p_data->bank_max[bank])) {
+                               *bank_num = bank;
+                               *bank_pin_num = pin_num -
+                                               gpio->p_data->bank_min[bank];
+                               return;
+               }
        }
-}
 
-static const unsigned int zynq_gpio_bank_offset[] = {
-       ZYNQ_GPIO_BANK0_PIN_MIN,
-       ZYNQ_GPIO_BANK1_PIN_MIN,
-       ZYNQ_GPIO_BANK2_PIN_MIN,
-       ZYNQ_GPIO_BANK3_PIN_MIN,
-};
+       /* default */
+       WARN(true, "invalid GPIO pin number: %u", pin_num);
+       *bank_num = 0;
+       *bank_pin_num = 0;
+}
 
 /**
  * zynq_gpio_get_value - Get the state of the specified pin of GPIO device
@@ -161,7 +179,7 @@ static int zynq_gpio_get_value(struct gpio_chip *chip, unsigned int pin)
        unsigned int bank_num, bank_pin_num;
        struct zynq_gpio *gpio = container_of(chip, struct zynq_gpio, chip);
 
-       zynq_gpio_get_bank_pin(pin, &bank_num, &bank_pin_num);
+       zynq_gpio_get_bank_pin(pin, &bank_num, &bank_pin_num, gpio);
 
        data = readl_relaxed(gpio->base_addr +
                             ZYNQ_GPIO_DATA_RO_OFFSET(bank_num));
@@ -185,7 +203,7 @@ static void zynq_gpio_set_value(struct gpio_chip *chip, unsigned int pin,
        unsigned int reg_offset, bank_num, bank_pin_num;
        struct zynq_gpio *gpio = container_of(chip, struct zynq_gpio, chip);
 
-       zynq_gpio_get_bank_pin(pin, &bank_num, &bank_pin_num);
+       zynq_gpio_get_bank_pin(pin, &bank_num, &bank_pin_num, gpio);
 
        if (bank_pin_num >= ZYNQ_GPIO_MID_PIN_NUM) {
                /* only 16 data bits in bit maskable reg */
@@ -222,7 +240,7 @@ static int zynq_gpio_dir_in(struct gpio_chip *chip, unsigned int pin)
        unsigned int bank_num, bank_pin_num;
        struct zynq_gpio *gpio = container_of(chip, struct zynq_gpio, chip);
 
-       zynq_gpio_get_bank_pin(pin, &bank_num, &bank_pin_num);
+       zynq_gpio_get_bank_pin(pin, &bank_num, &bank_pin_num, gpio);
 
        /* bank 0 pins 7 and 8 are special and cannot be used as inputs */
        if (bank_num == 0 && (bank_pin_num == 7 || bank_pin_num == 8))
@@ -255,7 +273,7 @@ static int zynq_gpio_dir_out(struct gpio_chip *chip, unsigned int pin,
        unsigned int bank_num, bank_pin_num;
        struct zynq_gpio *gpio = container_of(chip, struct zynq_gpio, chip);
 
-       zynq_gpio_get_bank_pin(pin, &bank_num, &bank_pin_num);
+       zynq_gpio_get_bank_pin(pin, &bank_num, &bank_pin_num, gpio);
 
        /* set the GPIO pin as output */
        reg = readl_relaxed(gpio->base_addr + ZYNQ_GPIO_DIRM_OFFSET(bank_num));
@@ -286,7 +304,7 @@ static void zynq_gpio_irq_mask(struct irq_data *irq_data)
        struct zynq_gpio *gpio = irq_data_get_irq_chip_data(irq_data);
 
        device_pin_num = irq_data->hwirq;
-       zynq_gpio_get_bank_pin(device_pin_num, &bank_num, &bank_pin_num);
+       zynq_gpio_get_bank_pin(device_pin_num, &bank_num, &bank_pin_num, gpio);
        writel_relaxed(BIT(bank_pin_num),
                       gpio->base_addr + ZYNQ_GPIO_INTDIS_OFFSET(bank_num));
 }
@@ -306,7 +324,7 @@ static void zynq_gpio_irq_unmask(struct irq_data *irq_data)
        struct zynq_gpio *gpio = irq_data_get_irq_chip_data(irq_data);
 
        device_pin_num = irq_data->hwirq;
-       zynq_gpio_get_bank_pin(device_pin_num, &bank_num, &bank_pin_num);
+       zynq_gpio_get_bank_pin(device_pin_num, &bank_num, &bank_pin_num, gpio);
        writel_relaxed(BIT(bank_pin_num),
                       gpio->base_addr + ZYNQ_GPIO_INTEN_OFFSET(bank_num));
 }
@@ -325,7 +343,7 @@ static void zynq_gpio_irq_ack(struct irq_data *irq_data)
        struct zynq_gpio *gpio = irq_data_get_irq_chip_data(irq_data);
 
        device_pin_num = irq_data->hwirq;
-       zynq_gpio_get_bank_pin(device_pin_num, &bank_num, &bank_pin_num);
+       zynq_gpio_get_bank_pin(device_pin_num, &bank_num, &bank_pin_num, gpio);
        writel_relaxed(BIT(bank_pin_num),
                       gpio->base_addr + ZYNQ_GPIO_INTSTS_OFFSET(bank_num));
 }
@@ -335,7 +353,7 @@ static void zynq_gpio_irq_ack(struct irq_data *irq_data)
  * @irq_data:  irq data containing irq number of gpio pin for the interrupt
  *             to enable
  *
- * Clears the INTSTS bit and unmasks the given interrrupt.
+ * Clears the INTSTS bit and unmasks the given interrupt.
  */
 static void zynq_gpio_irq_enable(struct irq_data *irq_data)
 {
@@ -375,7 +393,7 @@ static int zynq_gpio_set_irq_type(struct irq_data *irq_data, unsigned int type)
        struct zynq_gpio *gpio = irq_data_get_irq_chip_data(irq_data);
 
        device_pin_num = irq_data->hwirq;
-       zynq_gpio_get_bank_pin(device_pin_num, &bank_num, &bank_pin_num);
+       zynq_gpio_get_bank_pin(device_pin_num, &bank_num, &bank_pin_num, gpio);
 
        int_type = readl_relaxed(gpio->base_addr +
                                 ZYNQ_GPIO_INTTYPE_OFFSET(bank_num));
@@ -470,7 +488,7 @@ static void zynq_gpio_handle_bank_irq(struct zynq_gpio *gpio,
                                      unsigned int bank_num,
                                      unsigned long pending)
 {
-       unsigned int bank_offset = zynq_gpio_bank_offset[bank_num];
+       unsigned int bank_offset = gpio->p_data->bank_min[bank_num];
        struct irq_domain *irqdomain = gpio->chip.irqdomain;
        int offset;
 
@@ -505,7 +523,7 @@ static void zynq_gpio_irqhandler(unsigned int irq, struct irq_desc *desc)
 
        chained_irq_enter(irqchip, desc);
 
-       for (bank_num = 0; bank_num < ZYNQ_GPIO_MAX_BANK; bank_num++) {
+       for (bank_num = 0; bank_num < gpio->p_data->max_bank; bank_num++) {
                int_sts = readl_relaxed(gpio->base_addr +
                                        ZYNQ_GPIO_INTSTS_OFFSET(bank_num));
                int_enb = readl_relaxed(gpio->base_addr +
@@ -582,6 +600,46 @@ static const struct dev_pm_ops zynq_gpio_dev_pm_ops = {
                        zynq_gpio_runtime_resume, NULL)
 };
 
+static const struct zynq_platform_data zynqmp_gpio_def = {
+       .label = "zynqmp_gpio",
+       .ngpio = ZYNQMP_GPIO_NR_GPIOS,
+       .max_bank = ZYNQMP_GPIO_MAX_BANK,
+       .bank_min[0] = ZYNQ_GPIO_BANK0_PIN_MIN(MP),
+       .bank_max[0] = ZYNQ_GPIO_BANK0_PIN_MAX(MP),
+       .bank_min[1] = ZYNQ_GPIO_BANK1_PIN_MIN(MP),
+       .bank_max[1] = ZYNQ_GPIO_BANK1_PIN_MAX(MP),
+       .bank_min[2] = ZYNQ_GPIO_BANK2_PIN_MIN(MP),
+       .bank_max[2] = ZYNQ_GPIO_BANK2_PIN_MAX(MP),
+       .bank_min[3] = ZYNQ_GPIO_BANK3_PIN_MIN(MP),
+       .bank_max[3] = ZYNQ_GPIO_BANK3_PIN_MAX(MP),
+       .bank_min[4] = ZYNQ_GPIO_BANK4_PIN_MIN(MP),
+       .bank_max[4] = ZYNQ_GPIO_BANK4_PIN_MAX(MP),
+       .bank_min[5] = ZYNQ_GPIO_BANK5_PIN_MIN(MP),
+       .bank_max[5] = ZYNQ_GPIO_BANK5_PIN_MAX(MP),
+};
+
+static const struct zynq_platform_data zynq_gpio_def = {
+       .label = "zynq_gpio",
+       .ngpio = ZYNQ_GPIO_NR_GPIOS,
+       .max_bank = ZYNQ_GPIO_MAX_BANK,
+       .bank_min[0] = ZYNQ_GPIO_BANK0_PIN_MIN(),
+       .bank_max[0] = ZYNQ_GPIO_BANK0_PIN_MAX(),
+       .bank_min[1] = ZYNQ_GPIO_BANK1_PIN_MIN(),
+       .bank_max[1] = ZYNQ_GPIO_BANK1_PIN_MAX(),
+       .bank_min[2] = ZYNQ_GPIO_BANK2_PIN_MIN(),
+       .bank_max[2] = ZYNQ_GPIO_BANK2_PIN_MAX(),
+       .bank_min[3] = ZYNQ_GPIO_BANK3_PIN_MIN(),
+       .bank_max[3] = ZYNQ_GPIO_BANK3_PIN_MAX(),
+};
+
+static const struct of_device_id zynq_gpio_of_match[] = {
+       { .compatible = "xlnx,zynq-gpio-1.0", .data = (void *)&zynq_gpio_def },
+       { .compatible = "xlnx,zynqmp-gpio-1.0",
+                                       .data = (void *)&zynqmp_gpio_def },
+       { /* end of table */ }
+};
+MODULE_DEVICE_TABLE(of, zynq_gpio_of_match);
+
 /**
  * zynq_gpio_probe - Initialization method for a zynq_gpio device
  * @pdev:      platform device instance
@@ -599,11 +657,18 @@ static int zynq_gpio_probe(struct platform_device *pdev)
        struct zynq_gpio *gpio;
        struct gpio_chip *chip;
        struct resource *res;
+       const struct of_device_id *match;
 
        gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL);
        if (!gpio)
                return -ENOMEM;
 
+       match = of_match_node(zynq_gpio_of_match, pdev->dev.of_node);
+       if (!match) {
+               dev_err(&pdev->dev, "of_match_node() failed\n");
+               return -EINVAL;
+       }
+       gpio->p_data = match->data;
        platform_set_drvdata(pdev, gpio);
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -619,7 +684,7 @@ static int zynq_gpio_probe(struct platform_device *pdev)
 
        /* configure the gpio chip */
        chip = &gpio->chip;
-       chip->label = "zynq_gpio";
+       chip->label = gpio->p_data->label;
        chip->owner = THIS_MODULE;
        chip->dev = &pdev->dev;
        chip->get = zynq_gpio_get_value;
@@ -629,7 +694,7 @@ static int zynq_gpio_probe(struct platform_device *pdev)
        chip->direction_input = zynq_gpio_dir_in;
        chip->direction_output = zynq_gpio_dir_out;
        chip->base = -1;
-       chip->ngpio = ZYNQ_GPIO_NR_GPIOS;
+       chip->ngpio = gpio->p_data->ngpio;
 
        /* Enable GPIO clock */
        gpio->clk = devm_clk_get(&pdev->dev, NULL);
@@ -651,7 +716,7 @@ static int zynq_gpio_probe(struct platform_device *pdev)
        }
 
        /* disable interrupts for all banks */
-       for (bank_num = 0; bank_num < ZYNQ_GPIO_MAX_BANK; bank_num++)
+       for (bank_num = 0; bank_num < gpio->p_data->max_bank; bank_num++)
                writel_relaxed(ZYNQ_GPIO_IXR_DISABLE_ALL, gpio->base_addr +
                               ZYNQ_GPIO_INTDIS_OFFSET(bank_num));
 
@@ -695,12 +760,6 @@ static int zynq_gpio_remove(struct platform_device *pdev)
        return 0;
 }
 
-static struct of_device_id zynq_gpio_of_match[] = {
-       { .compatible = "xlnx,zynq-gpio-1.0", },
-       { /* end of table */ }
-};
-MODULE_DEVICE_TABLE(of, zynq_gpio_of_match);
-
 static struct platform_driver zynq_gpio_driver = {
        .driver = {
                .name = DRIVER_NAME,
index 725d16138b740e27a39d151ec5f7bfdedb9a969b..533fe5dbe6f8e8108ac6c0b36656a9bba2f137ea 100644 (file)
@@ -114,10 +114,11 @@ static inline int acpi_gpiochip_pin_to_gpio_offset(struct gpio_chip *chip,
  * @path:      ACPI GPIO controller full path name, (e.g. "\\_SB.GPO1")
  * @pin:       ACPI GPIO pin number (0-based, controller-relative)
  *
- * Returns GPIO descriptor to use with Linux generic GPIO API, or ERR_PTR
- * error value
+ * Return: GPIO descriptor to use with Linux generic GPIO API, or ERR_PTR
+ * error value. Specifically returns %-EPROBE_DEFER if the referenced GPIO
+ * controller does not have gpiochip registered at the moment. This is to
+ * support probe deferral.
  */
-
 static struct gpio_desc *acpi_get_gpiod(char *path, int pin)
 {
        struct gpio_chip *chip;
@@ -131,7 +132,7 @@ static struct gpio_desc *acpi_get_gpiod(char *path, int pin)
 
        chip = gpiochip_find(handle, acpi_gpiochip_find);
        if (!chip)
-               return ERR_PTR(-ENODEV);
+               return ERR_PTR(-EPROBE_DEFER);
 
        offset = acpi_gpiochip_pin_to_gpio_offset(chip, pin);
        if (offset < 0)
@@ -307,6 +308,7 @@ void acpi_gpiochip_request_interrupts(struct gpio_chip *chip)
        acpi_walk_resources(handle, "_AEI",
                            acpi_gpiochip_request_interrupt, acpi_gpio);
 }
+EXPORT_SYMBOL_GPL(acpi_gpiochip_request_interrupts);
 
 /**
  * acpi_gpiochip_free_interrupts() - Free GPIO ACPI event interrupts.
@@ -346,6 +348,7 @@ void acpi_gpiochip_free_interrupts(struct gpio_chip *chip)
                kfree(event);
        }
 }
+EXPORT_SYMBOL_GPL(acpi_gpiochip_free_interrupts);
 
 int acpi_dev_add_driver_gpios(struct acpi_device *adev,
                              const struct acpi_gpio_mapping *gpios)
@@ -514,6 +517,35 @@ struct gpio_desc *acpi_get_gpiod_by_index(struct acpi_device *adev,
        return lookup.desc ? lookup.desc : ERR_PTR(-ENOENT);
 }
 
+/**
+ * acpi_dev_gpio_irq_get() - Find GpioInt and translate it to Linux IRQ number
+ * @adev: pointer to a ACPI device to get IRQ from
+ * @index: index of GpioInt resource (starting from %0)
+ *
+ * If the device has one or more GpioInt resources, this function can be
+ * used to translate from the GPIO offset in the resource to the Linux IRQ
+ * number.
+ *
+ * Return: Linux IRQ number (>%0) on success, negative errno on failure.
+ */
+int acpi_dev_gpio_irq_get(struct acpi_device *adev, int index)
+{
+       int idx, i;
+
+       for (i = 0, idx = 0; idx <= index; i++) {
+               struct acpi_gpio_info info;
+               struct gpio_desc *desc;
+
+               desc = acpi_get_gpiod_by_index(adev, NULL, i, &info);
+               if (IS_ERR(desc))
+                       break;
+               if (info.gpioint && idx++ == index)
+                       return gpiod_to_irq(desc);
+       }
+       return -ENOENT;
+}
+EXPORT_SYMBOL_GPL(acpi_dev_gpio_irq_get);
+
 static acpi_status
 acpi_gpio_adr_space_handler(u32 function, acpi_physical_address address,
                            u32 bits, u64 *value, void *handler_context,
index a6c67c6b468045f9325e6249019f0c1080a46398..9a0ec48a47375d18d4d6d17f98e379f30f8e9ec8 100644 (file)
@@ -242,7 +242,7 @@ int of_gpio_simple_xlate(struct gpio_chip *gc,
 {
        /*
         * We're discouraging gpio_cells < 2, since that way you'll have to
-        * write your own xlate function (that will have to retrive the GPIO
+        * write your own xlate function (that will have to retrieve the GPIO
         * number and the flags from a single gpio cell -- this is possible,
         * but not recommended).
         */
index af3bc7a8033bdcbaa2e93602bb107fbe12968d35..b57ed8e55ab5f61b7f8307399bb4e3d16299e64b 100644 (file)
@@ -6,14 +6,29 @@
 #include <linux/gpio/driver.h>
 #include <linux/interrupt.h>
 #include <linux/kdev_t.h>
+#include <linux/slab.h>
 
 #include "gpiolib.h"
 
-static DEFINE_IDR(dirent_idr);
+#define GPIO_IRQF_TRIGGER_FALLING      BIT(0)
+#define GPIO_IRQF_TRIGGER_RISING       BIT(1)
+#define GPIO_IRQF_TRIGGER_BOTH         (GPIO_IRQF_TRIGGER_FALLING | \
+                                        GPIO_IRQF_TRIGGER_RISING)
 
+struct gpiod_data {
+       struct gpio_desc *desc;
+
+       struct mutex mutex;
+       struct kernfs_node *value_kn;
+       int irq;
+       unsigned char irq_flags;
 
-/* lock protects against unexport_gpio() being called while
- * sysfs files are active.
+       bool direction_can_change;
+};
+
+/*
+ * Lock to serialise gpiod export and unexport, and prevent re-export of
+ * gpiod whose chip is being unregistered.
  */
 static DEFINE_MUTEX(sysfs_lock);
 
@@ -38,38 +53,35 @@ static DEFINE_MUTEX(sysfs_lock);
  *        /edge configuration
  */
 
-static ssize_t gpio_direction_show(struct device *dev,
+static ssize_t direction_show(struct device *dev,
                struct device_attribute *attr, char *buf)
 {
-       struct gpio_desc        *desc = dev_get_drvdata(dev);
+       struct gpiod_data *data = dev_get_drvdata(dev);
+       struct gpio_desc *desc = data->desc;
        ssize_t                 status;
 
-       mutex_lock(&sysfs_lock);
+       mutex_lock(&data->mutex);
 
-       if (!test_bit(FLAG_EXPORT, &desc->flags)) {
-               status = -EIO;
-       } else {
-               gpiod_get_direction(desc);
-               status = sprintf(buf, "%s\n",
+       gpiod_get_direction(desc);
+       status = sprintf(buf, "%s\n",
                        test_bit(FLAG_IS_OUT, &desc->flags)
                                ? "out" : "in");
-       }
 
-       mutex_unlock(&sysfs_lock);
+       mutex_unlock(&data->mutex);
+
        return status;
 }
 
-static ssize_t gpio_direction_store(struct device *dev,
+static ssize_t direction_store(struct device *dev,
                struct device_attribute *attr, const char *buf, size_t size)
 {
-       struct gpio_desc        *desc = dev_get_drvdata(dev);
+       struct gpiod_data *data = dev_get_drvdata(dev);
+       struct gpio_desc *desc = data->desc;
        ssize_t                 status;
 
-       mutex_lock(&sysfs_lock);
+       mutex_lock(&data->mutex);
 
-       if (!test_bit(FLAG_EXPORT, &desc->flags))
-               status = -EIO;
-       else if (sysfs_streq(buf, "high"))
+       if (sysfs_streq(buf, "high"))
                status = gpiod_direction_output_raw(desc, 1);
        else if (sysfs_streq(buf, "out") || sysfs_streq(buf, "low"))
                status = gpiod_direction_output_raw(desc, 0);
@@ -78,43 +90,40 @@ static ssize_t gpio_direction_store(struct device *dev,
        else
                status = -EINVAL;
 
-       mutex_unlock(&sysfs_lock);
+       mutex_unlock(&data->mutex);
+
        return status ? : size;
 }
+static DEVICE_ATTR_RW(direction);
 
-static /* const */ DEVICE_ATTR(direction, 0644,
-               gpio_direction_show, gpio_direction_store);
-
-static ssize_t gpio_value_show(struct device *dev,
+static ssize_t value_show(struct device *dev,
                struct device_attribute *attr, char *buf)
 {
-       struct gpio_desc        *desc = dev_get_drvdata(dev);
+       struct gpiod_data *data = dev_get_drvdata(dev);
+       struct gpio_desc *desc = data->desc;
        ssize_t                 status;
 
-       mutex_lock(&sysfs_lock);
+       mutex_lock(&data->mutex);
 
-       if (!test_bit(FLAG_EXPORT, &desc->flags))
-               status = -EIO;
-       else
-               status = sprintf(buf, "%d\n", gpiod_get_value_cansleep(desc));
+       status = sprintf(buf, "%d\n", gpiod_get_value_cansleep(desc));
+
+       mutex_unlock(&data->mutex);
 
-       mutex_unlock(&sysfs_lock);
        return status;
 }
 
-static ssize_t gpio_value_store(struct device *dev,
+static ssize_t value_store(struct device *dev,
                struct device_attribute *attr, const char *buf, size_t size)
 {
-       struct gpio_desc        *desc = dev_get_drvdata(dev);
+       struct gpiod_data *data = dev_get_drvdata(dev);
+       struct gpio_desc *desc = data->desc;
        ssize_t                 status;
 
-       mutex_lock(&sysfs_lock);
+       mutex_lock(&data->mutex);
 
-       if (!test_bit(FLAG_EXPORT, &desc->flags))
-               status = -EIO;
-       else if (!test_bit(FLAG_IS_OUT, &desc->flags))
+       if (!test_bit(FLAG_IS_OUT, &desc->flags)) {
                status = -EPERM;
-       else {
+       else {
                long            value;
 
                status = kstrtol(buf, 0, &value);
@@ -124,172 +133,168 @@ static ssize_t gpio_value_store(struct device *dev,
                }
        }
 
-       mutex_unlock(&sysfs_lock);
+       mutex_unlock(&data->mutex);
+
        return status;
 }
-
-static DEVICE_ATTR(value, 0644,
-               gpio_value_show, gpio_value_store);
+static DEVICE_ATTR_RW(value);
 
 static irqreturn_t gpio_sysfs_irq(int irq, void *priv)
 {
-       struct kernfs_node      *value_sd = priv;
+       struct gpiod_data *data = priv;
+
+       sysfs_notify_dirent(data->value_kn);
 
-       sysfs_notify_dirent(value_sd);
        return IRQ_HANDLED;
 }
 
-static int gpio_setup_irq(struct gpio_desc *desc, struct device *dev,
-               unsigned long gpio_flags)
+/* Caller holds gpiod-data mutex. */
+static int gpio_sysfs_request_irq(struct device *dev, unsigned char flags)
 {
-       struct kernfs_node      *value_sd;
+       struct gpiod_data       *data = dev_get_drvdata(dev);
+       struct gpio_desc        *desc = data->desc;
        unsigned long           irq_flags;
-       int                     ret, irq, id;
+       int                     ret;
 
-       if ((desc->flags & GPIO_TRIGGER_MASK) == gpio_flags)
-               return 0;
-
-       irq = gpiod_to_irq(desc);
-       if (irq < 0)
+       data->irq = gpiod_to_irq(desc);
+       if (data->irq < 0)
                return -EIO;
 
-       id = desc->flags >> ID_SHIFT;
-       value_sd = idr_find(&dirent_idr, id);
-       if (value_sd)
-               free_irq(irq, value_sd);
-
-       desc->flags &= ~GPIO_TRIGGER_MASK;
-
-       if (!gpio_flags) {
-               gpiochip_unlock_as_irq(desc->chip, gpio_chip_hwgpio(desc));
-               ret = 0;
-               goto free_id;
-       }
+       data->value_kn = sysfs_get_dirent(dev->kobj.sd, "value");
+       if (!data->value_kn)
+               return -ENODEV;
 
        irq_flags = IRQF_SHARED;
-       if (test_bit(FLAG_TRIG_FALL, &gpio_flags))
+       if (flags & GPIO_IRQF_TRIGGER_FALLING)
                irq_flags |= test_bit(FLAG_ACTIVE_LOW, &desc->flags) ?
                        IRQF_TRIGGER_RISING : IRQF_TRIGGER_FALLING;
-       if (test_bit(FLAG_TRIG_RISE, &gpio_flags))
+       if (flags & GPIO_IRQF_TRIGGER_RISING)
                irq_flags |= test_bit(FLAG_ACTIVE_LOW, &desc->flags) ?
                        IRQF_TRIGGER_FALLING : IRQF_TRIGGER_RISING;
 
-       if (!value_sd) {
-               value_sd = sysfs_get_dirent(dev->kobj.sd, "value");
-               if (!value_sd) {
-                       ret = -ENODEV;
-                       goto err_out;
-               }
-
-               ret = idr_alloc(&dirent_idr, value_sd, 1, 0, GFP_KERNEL);
-               if (ret < 0)
-                       goto free_sd;
-               id = ret;
-
-               desc->flags &= GPIO_FLAGS_MASK;
-               desc->flags |= (unsigned long)id << ID_SHIFT;
-
-               if (desc->flags >> ID_SHIFT != id) {
-                       ret = -ERANGE;
-                       goto free_id;
-               }
-       }
+       /*
+        * FIXME: This should be done in the irq_request_resources callback
+        *        when the irq is requested, but a few drivers currently fail
+        *        to do so.
+        *
+        *        Remove this redundant call (along with the corresponding
+        *        unlock) when those drivers have been fixed.
+        */
+       ret = gpiochip_lock_as_irq(desc->chip, gpio_chip_hwgpio(desc));
+       if (ret < 0)
+               goto err_put_kn;
 
-       ret = request_any_context_irq(irq, gpio_sysfs_irq, irq_flags,
-                               "gpiolib", value_sd);
+       ret = request_any_context_irq(data->irq, gpio_sysfs_irq, irq_flags,
+                               "gpiolib", data);
        if (ret < 0)
-               goto free_id;
+               goto err_unlock;
 
-       ret = gpiochip_lock_as_irq(desc->chip, gpio_chip_hwgpio(desc));
-       if (ret < 0) {
-               gpiod_warn(desc, "failed to flag the GPIO for IRQ\n");
-               goto free_id;
-       }
+       data->irq_flags = flags;
 
-       desc->flags |= gpio_flags;
        return 0;
 
-free_id:
-       idr_remove(&dirent_idr, id);
-       desc->flags &= GPIO_FLAGS_MASK;
-free_sd:
-       if (value_sd)
-               sysfs_put(value_sd);
-err_out:
+err_unlock:
+       gpiochip_unlock_as_irq(desc->chip, gpio_chip_hwgpio(desc));
+err_put_kn:
+       sysfs_put(data->value_kn);
+
        return ret;
 }
 
+/*
+ * Caller holds gpiod-data mutex (unless called after class-device
+ * deregistration).
+ */
+static void gpio_sysfs_free_irq(struct device *dev)
+{
+       struct gpiod_data *data = dev_get_drvdata(dev);
+       struct gpio_desc *desc = data->desc;
+
+       data->irq_flags = 0;
+       free_irq(data->irq, data);
+       gpiochip_unlock_as_irq(desc->chip, gpio_chip_hwgpio(desc));
+       sysfs_put(data->value_kn);
+}
+
 static const struct {
        const char *name;
-       unsigned long flags;
+       unsigned char flags;
 } trigger_types[] = {
        { "none",    0 },
-       { "falling", BIT(FLAG_TRIG_FALL) },
-       { "rising",  BIT(FLAG_TRIG_RISE) },
-       { "both",    BIT(FLAG_TRIG_FALL) | BIT(FLAG_TRIG_RISE) },
+       { "falling", GPIO_IRQF_TRIGGER_FALLING },
+       { "rising",  GPIO_IRQF_TRIGGER_RISING },
+       { "both",    GPIO_IRQF_TRIGGER_BOTH },
 };
 
-static ssize_t gpio_edge_show(struct device *dev,
+static ssize_t edge_show(struct device *dev,
                struct device_attribute *attr, char *buf)
 {
-       const struct gpio_desc  *desc = dev_get_drvdata(dev);
-       ssize_t                 status;
+       struct gpiod_data *data = dev_get_drvdata(dev);
+       ssize_t status = 0;
+       int i;
 
-       mutex_lock(&sysfs_lock);
-
-       if (!test_bit(FLAG_EXPORT, &desc->flags))
-               status = -EIO;
-       else {
-               int i;
+       mutex_lock(&data->mutex);
 
-               status = 0;
-               for (i = 0; i < ARRAY_SIZE(trigger_types); i++)
-                       if ((desc->flags & GPIO_TRIGGER_MASK)
-                                       == trigger_types[i].flags) {
-                               status = sprintf(buf, "%s\n",
-                                                trigger_types[i].name);
-                               break;
-                       }
+       for (i = 0; i < ARRAY_SIZE(trigger_types); i++) {
+               if (data->irq_flags == trigger_types[i].flags) {
+                       status = sprintf(buf, "%s\n", trigger_types[i].name);
+                       break;
+               }
        }
 
-       mutex_unlock(&sysfs_lock);
+       mutex_unlock(&data->mutex);
+
        return status;
 }
 
-static ssize_t gpio_edge_store(struct device *dev,
+static ssize_t edge_store(struct device *dev,
                struct device_attribute *attr, const char *buf, size_t size)
 {
-       struct gpio_desc        *desc = dev_get_drvdata(dev);
-       ssize_t                 status;
-       int                     i;
+       struct gpiod_data *data = dev_get_drvdata(dev);
+       unsigned char flags;
+       ssize_t status = size;
+       int i;
 
-       for (i = 0; i < ARRAY_SIZE(trigger_types); i++)
+       for (i = 0; i < ARRAY_SIZE(trigger_types); i++) {
                if (sysfs_streq(trigger_types[i].name, buf))
-                       goto found;
-       return -EINVAL;
+                       break;
+       }
 
-found:
-       mutex_lock(&sysfs_lock);
+       if (i == ARRAY_SIZE(trigger_types))
+               return -EINVAL;
 
-       if (!test_bit(FLAG_EXPORT, &desc->flags))
-               status = -EIO;
-       else {
-               status = gpio_setup_irq(desc, dev, trigger_types[i].flags);
+       flags = trigger_types[i].flags;
+
+       mutex_lock(&data->mutex);
+
+       if (flags == data->irq_flags) {
+               status = size;
+               goto out_unlock;
+       }
+
+       if (data->irq_flags)
+               gpio_sysfs_free_irq(dev);
+
+       if (flags) {
+               status = gpio_sysfs_request_irq(dev, flags);
                if (!status)
                        status = size;
        }
 
-       mutex_unlock(&sysfs_lock);
+out_unlock:
+       mutex_unlock(&data->mutex);
 
        return status;
 }
+static DEVICE_ATTR_RW(edge);
 
-static DEVICE_ATTR(edge, 0644, gpio_edge_show, gpio_edge_store);
-
-static int sysfs_set_active_low(struct gpio_desc *desc, struct device *dev,
-                               int value)
+/* Caller holds gpiod-data mutex. */
+static int gpio_sysfs_set_active_low(struct device *dev, int value)
 {
+       struct gpiod_data       *data = dev_get_drvdata(dev);
+       struct gpio_desc        *desc = data->desc;
        int                     status = 0;
+       unsigned int            flags = data->irq_flags;
 
        if (!!test_bit(FLAG_ACTIVE_LOW, &desc->flags) == !!value)
                return 0;
@@ -300,69 +305,59 @@ static int sysfs_set_active_low(struct gpio_desc *desc, struct device *dev,
                clear_bit(FLAG_ACTIVE_LOW, &desc->flags);
 
        /* reconfigure poll(2) support if enabled on one edge only */
-       if (dev != NULL && (!!test_bit(FLAG_TRIG_RISE, &desc->flags) ^
-                               !!test_bit(FLAG_TRIG_FALL, &desc->flags))) {
-               unsigned long trigger_flags = desc->flags & GPIO_TRIGGER_MASK;
-
-               gpio_setup_irq(desc, dev, 0);
-               status = gpio_setup_irq(desc, dev, trigger_flags);
+       if (flags == GPIO_IRQF_TRIGGER_FALLING ||
+                                       flags == GPIO_IRQF_TRIGGER_RISING) {
+               gpio_sysfs_free_irq(dev);
+               status = gpio_sysfs_request_irq(dev, flags);
        }
 
        return status;
 }
 
-static ssize_t gpio_active_low_show(struct device *dev,
+static ssize_t active_low_show(struct device *dev,
                struct device_attribute *attr, char *buf)
 {
-       const struct gpio_desc  *desc = dev_get_drvdata(dev);
+       struct gpiod_data *data = dev_get_drvdata(dev);
+       struct gpio_desc *desc = data->desc;
        ssize_t                 status;
 
-       mutex_lock(&sysfs_lock);
+       mutex_lock(&data->mutex);
 
-       if (!test_bit(FLAG_EXPORT, &desc->flags))
-               status = -EIO;
-       else
-               status = sprintf(buf, "%d\n",
+       status = sprintf(buf, "%d\n",
                                !!test_bit(FLAG_ACTIVE_LOW, &desc->flags));
 
-       mutex_unlock(&sysfs_lock);
+       mutex_unlock(&data->mutex);
 
        return status;
 }
 
-static ssize_t gpio_active_low_store(struct device *dev,
+static ssize_t active_low_store(struct device *dev,
                struct device_attribute *attr, const char *buf, size_t size)
 {
-       struct gpio_desc        *desc = dev_get_drvdata(dev);
+       struct gpiod_data       *data = dev_get_drvdata(dev);
        ssize_t                 status;
+       long                    value;
 
-       mutex_lock(&sysfs_lock);
+       mutex_lock(&data->mutex);
 
-       if (!test_bit(FLAG_EXPORT, &desc->flags)) {
-               status = -EIO;
-       } else {
-               long            value;
+       status = kstrtol(buf, 0, &value);
+       if (status == 0)
+               status = gpio_sysfs_set_active_low(dev, value);
 
-               status = kstrtol(buf, 0, &value);
-               if (status == 0)
-                       status = sysfs_set_active_low(desc, dev, value != 0);
-       }
-
-       mutex_unlock(&sysfs_lock);
+       mutex_unlock(&data->mutex);
 
        return status ? : size;
 }
-
-static DEVICE_ATTR(active_low, 0644,
-               gpio_active_low_show, gpio_active_low_store);
+static DEVICE_ATTR_RW(active_low);
 
 static umode_t gpio_is_visible(struct kobject *kobj, struct attribute *attr,
                               int n)
 {
        struct device *dev = container_of(kobj, struct device, kobj);
-       struct gpio_desc *desc = dev_get_drvdata(dev);
+       struct gpiod_data *data = dev_get_drvdata(dev);
+       struct gpio_desc *desc = data->desc;
        umode_t mode = attr->mode;
-       bool show_direction = test_bit(FLAG_SYSFS_DIR, &desc->flags);
+       bool show_direction = data->direction_can_change;
 
        if (attr == &dev_attr_direction.attr) {
                if (!show_direction)
@@ -402,32 +397,32 @@ static const struct attribute_group *gpio_groups[] = {
  *   /ngpio ... matching gpio_chip.ngpio
  */
 
-static ssize_t chip_base_show(struct device *dev,
+static ssize_t base_show(struct device *dev,
                               struct device_attribute *attr, char *buf)
 {
        const struct gpio_chip  *chip = dev_get_drvdata(dev);
 
        return sprintf(buf, "%d\n", chip->base);
 }
-static DEVICE_ATTR(base, 0444, chip_base_show, NULL);
+static DEVICE_ATTR_RO(base);
 
-static ssize_t chip_label_show(struct device *dev,
+static ssize_t label_show(struct device *dev,
                               struct device_attribute *attr, char *buf)
 {
        const struct gpio_chip  *chip = dev_get_drvdata(dev);
 
        return sprintf(buf, "%s\n", chip->label ? : "");
 }
-static DEVICE_ATTR(label, 0444, chip_label_show, NULL);
+static DEVICE_ATTR_RO(label);
 
-static ssize_t chip_ngpio_show(struct device *dev,
+static ssize_t ngpio_show(struct device *dev,
                               struct device_attribute *attr, char *buf)
 {
        const struct gpio_chip  *chip = dev_get_drvdata(dev);
 
        return sprintf(buf, "%u\n", chip->ngpio);
 }
-static DEVICE_ATTR(ngpio, 0444, chip_ngpio_show, NULL);
+static DEVICE_ATTR_RO(ngpio);
 
 static struct attribute *gpiochip_attrs[] = {
        &dev_attr_base.attr,
@@ -552,6 +547,7 @@ static struct class gpio_class = {
 int gpiod_export(struct gpio_desc *desc, bool direction_may_change)
 {
        struct gpio_chip        *chip;
+       struct gpiod_data       *data;
        unsigned long           flags;
        int                     status;
        const char              *ioname = NULL;
@@ -574,9 +570,9 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change)
        mutex_lock(&sysfs_lock);
 
        /* check if chip is being removed */
-       if (!chip || !chip->exported) {
+       if (!chip || !chip->cdev) {
                status = -ENODEV;
-               goto fail_unlock;
+               goto err_unlock;
        }
 
        spin_lock_irqsave(&gpio_lock, flags);
@@ -588,43 +584,54 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change)
                                test_bit(FLAG_REQUESTED, &desc->flags),
                                test_bit(FLAG_EXPORT, &desc->flags));
                status = -EPERM;
-               goto fail_unlock;
+               goto err_unlock;
        }
+       spin_unlock_irqrestore(&gpio_lock, flags);
 
-       if (desc->chip->direction_input && desc->chip->direction_output &&
-                       direction_may_change) {
-               set_bit(FLAG_SYSFS_DIR, &desc->flags);
+       data = kzalloc(sizeof(*data), GFP_KERNEL);
+       if (!data) {
+               status = -ENOMEM;
+               goto err_unlock;
        }
 
-       spin_unlock_irqrestore(&gpio_lock, flags);
+       data->desc = desc;
+       mutex_init(&data->mutex);
+       if (chip->direction_input && chip->direction_output)
+               data->direction_can_change = direction_may_change;
+       else
+               data->direction_can_change = false;
 
        offset = gpio_chip_hwgpio(desc);
-       if (desc->chip->names && desc->chip->names[offset])
-               ioname = desc->chip->names[offset];
+       if (chip->names && chip->names[offset])
+               ioname = chip->names[offset];
 
-       dev = device_create_with_groups(&gpio_class, desc->chip->dev,
-                                       MKDEV(0, 0), desc, gpio_groups,
+       dev = device_create_with_groups(&gpio_class, chip->dev,
+                                       MKDEV(0, 0), data, gpio_groups,
                                        ioname ? ioname : "gpio%u",
                                        desc_to_gpio(desc));
        if (IS_ERR(dev)) {
                status = PTR_ERR(dev);
-               goto fail_unlock;
+               goto err_free_data;
        }
 
        set_bit(FLAG_EXPORT, &desc->flags);
        mutex_unlock(&sysfs_lock);
        return 0;
 
-fail_unlock:
+err_free_data:
+       kfree(data);
+err_unlock:
        mutex_unlock(&sysfs_lock);
        gpiod_dbg(desc, "%s: status %d\n", __func__, status);
        return status;
 }
 EXPORT_SYMBOL_GPL(gpiod_export);
 
-static int match_export(struct device *dev, const void *data)
+static int match_export(struct device *dev, const void *desc)
 {
-       return dev_get_drvdata(dev) == data;
+       struct gpiod_data *data = dev_get_drvdata(dev);
+
+       return data->desc == desc;
 }
 
 /**
@@ -641,81 +648,25 @@ static int match_export(struct device *dev, const void *data)
 int gpiod_export_link(struct device *dev, const char *name,
                      struct gpio_desc *desc)
 {
-       int                     status = -EINVAL;
+       struct device *cdev;
+       int ret;
 
        if (!desc) {
                pr_warn("%s: invalid GPIO\n", __func__);
                return -EINVAL;
        }
 
-       mutex_lock(&sysfs_lock);
-
-       if (test_bit(FLAG_EXPORT, &desc->flags)) {
-               struct device *tdev;
-
-               tdev = class_find_device(&gpio_class, NULL, desc, match_export);
-               if (tdev != NULL) {
-                       status = sysfs_create_link(&dev->kobj, &tdev->kobj,
-                                               name);
-                       put_device(tdev);
-               } else {
-                       status = -ENODEV;
-               }
-       }
-
-       mutex_unlock(&sysfs_lock);
+       cdev = class_find_device(&gpio_class, NULL, desc, match_export);
+       if (!cdev)
+               return -ENODEV;
 
-       if (status)
-               gpiod_dbg(desc, "%s: status %d\n", __func__, status);
+       ret = sysfs_create_link(&dev->kobj, &cdev->kobj, name);
+       put_device(cdev);
 
-       return status;
+       return ret;
 }
 EXPORT_SYMBOL_GPL(gpiod_export_link);
 
-/**
- * gpiod_sysfs_set_active_low - set the polarity of gpio sysfs value
- * @gpio: gpio to change
- * @value: non-zero to use active low, i.e. inverted values
- *
- * Set the polarity of /sys/class/gpio/gpioN/value sysfs attribute.
- * The GPIO does not have to be exported yet.  If poll(2) support has
- * been enabled for either rising or falling edge, it will be
- * reconfigured to follow the new polarity.
- *
- * Returns zero on success, else an error.
- */
-int gpiod_sysfs_set_active_low(struct gpio_desc *desc, int value)
-{
-       struct device           *dev = NULL;
-       int                     status = -EINVAL;
-
-       if (!desc) {
-               pr_warn("%s: invalid GPIO\n", __func__);
-               return -EINVAL;
-       }
-
-       mutex_lock(&sysfs_lock);
-
-       if (test_bit(FLAG_EXPORT, &desc->flags)) {
-               dev = class_find_device(&gpio_class, NULL, desc, match_export);
-               if (dev == NULL) {
-                       status = -ENODEV;
-                       goto unlock;
-               }
-       }
-
-       status = sysfs_set_active_low(desc, dev, value);
-       put_device(dev);
-unlock:
-       mutex_unlock(&sysfs_lock);
-
-       if (status)
-               gpiod_dbg(desc, "%s: status %d\n", __func__, status);
-
-       return status;
-}
-EXPORT_SYMBOL_GPL(gpiod_sysfs_set_active_low);
-
 /**
  * gpiod_unexport - reverse effect of gpio_export()
  * @gpio: gpio to make unavailable
@@ -724,8 +675,8 @@ EXPORT_SYMBOL_GPL(gpiod_sysfs_set_active_low);
  */
 void gpiod_unexport(struct gpio_desc *desc)
 {
-       int                     status = 0;
-       struct device           *dev = NULL;
+       struct gpiod_data *data;
+       struct device *dev;
 
        if (!desc) {
                pr_warn("%s: invalid GPIO\n", __func__);
@@ -734,82 +685,79 @@ void gpiod_unexport(struct gpio_desc *desc)
 
        mutex_lock(&sysfs_lock);
 
-       if (test_bit(FLAG_EXPORT, &desc->flags)) {
+       if (!test_bit(FLAG_EXPORT, &desc->flags))
+               goto err_unlock;
 
-               dev = class_find_device(&gpio_class, NULL, desc, match_export);
-               if (dev) {
-                       gpio_setup_irq(desc, dev, 0);
-                       clear_bit(FLAG_SYSFS_DIR, &desc->flags);
-                       clear_bit(FLAG_EXPORT, &desc->flags);
-               } else
-                       status = -ENODEV;
-       }
+       dev = class_find_device(&gpio_class, NULL, desc, match_export);
+       if (!dev)
+               goto err_unlock;
+
+       data = dev_get_drvdata(dev);
+
+       clear_bit(FLAG_EXPORT, &desc->flags);
+
+       device_unregister(dev);
+
+       /*
+        * Release irq after deregistration to prevent race with edge_store.
+        */
+       if (data->irq_flags)
+               gpio_sysfs_free_irq(dev);
 
        mutex_unlock(&sysfs_lock);
 
-       if (dev) {
-               device_unregister(dev);
-               put_device(dev);
-       }
+       put_device(dev);
+       kfree(data);
 
-       if (status)
-               gpiod_dbg(desc, "%s: status %d\n", __func__, status);
+       return;
+
+err_unlock:
+       mutex_unlock(&sysfs_lock);
 }
 EXPORT_SYMBOL_GPL(gpiod_unexport);
 
-int gpiochip_export(struct gpio_chip *chip)
+int gpiochip_sysfs_register(struct gpio_chip *chip)
 {
-       int             status;
        struct device   *dev;
 
-       /* Many systems register gpio chips for SOC support very early,
+       /*
+        * Many systems add gpio chips for SOC support very early,
         * before driver model support is available.  In those cases we
-        * export this later, in gpiolib_sysfs_init() ... here we just
+        * register later, in gpiolib_sysfs_init() ... here we just
         * verify that _some_ field of gpio_class got initialized.
         */
        if (!gpio_class.p)
                return 0;
 
        /* use chip->base for the ID; it's already known to be unique */
-       mutex_lock(&sysfs_lock);
        dev = device_create_with_groups(&gpio_class, chip->dev, MKDEV(0, 0),
                                        chip, gpiochip_groups,
                                        "gpiochip%d", chip->base);
        if (IS_ERR(dev))
-               status = PTR_ERR(dev);
-       else
-               status = 0;
-       chip->exported = (status == 0);
-       mutex_unlock(&sysfs_lock);
+               return PTR_ERR(dev);
 
-       if (status)
-               chip_dbg(chip, "%s: status %d\n", __func__, status);
+       mutex_lock(&sysfs_lock);
+       chip->cdev = dev;
+       mutex_unlock(&sysfs_lock);
 
-       return status;
+       return 0;
 }
 
-void gpiochip_unexport(struct gpio_chip *chip)
+void gpiochip_sysfs_unregister(struct gpio_chip *chip)
 {
-       int                     status;
-       struct device           *dev;
        struct gpio_desc *desc;
        unsigned int i;
 
+       if (!chip->cdev)
+               return;
+
+       device_unregister(chip->cdev);
+
+       /* prevent further gpiod exports */
        mutex_lock(&sysfs_lock);
-       dev = class_find_device(&gpio_class, NULL, chip, match_export);
-       if (dev) {
-               put_device(dev);
-               device_unregister(dev);
-               /* prevent further gpiod exports */
-               chip->exported = false;
-               status = 0;
-       } else
-               status = -ENODEV;
+       chip->cdev = NULL;
        mutex_unlock(&sysfs_lock);
 
-       if (status)
-               chip_dbg(chip, "%s: status %d\n", __func__, status);
-
        /* unregister gpiod class devices owned by sysfs */
        for (i = 0; i < chip->ngpio; i++) {
                desc = &chip->desc[i];
@@ -836,19 +784,20 @@ static int __init gpiolib_sysfs_init(void)
         */
        spin_lock_irqsave(&gpio_lock, flags);
        list_for_each_entry(chip, &gpio_chips, list) {
-               if (chip->exported)
+               if (chip->cdev)
                        continue;
 
                /*
-                * TODO we yield gpio_lock here because gpiochip_export()
-                * acquires a mutex. This is unsafe and needs to be fixed.
+                * TODO we yield gpio_lock here because
+                * gpiochip_sysfs_register() acquires a mutex. This is unsafe
+                * and needs to be fixed.
                 *
                 * Also it would be nice to use gpiochip_find() here so we
                 * can keep gpio_chips local to gpiolib.c, but the yield of
                 * gpio_lock prevents us from doing this.
                 */
                spin_unlock_irqrestore(&gpio_lock, flags);
-               status = gpiochip_export(chip);
+               status = gpiochip_sysfs_register(chip);
                spin_lock_irqsave(&gpio_lock, flags);
        }
        spin_unlock_irqrestore(&gpio_lock, flags);
index 6bc612b8a49fcf859261173e00d0e7389d7d2b05..be42ab368a801ff0a3c5508b9bd53c3fbf6cf78d 100644 (file)
@@ -290,7 +290,7 @@ int gpiochip_add(struct gpio_chip *chip)
        of_gpiochip_add(chip);
        acpi_gpiochip_add(chip);
 
-       status = gpiochip_export(chip);
+       status = gpiochip_sysfs_register(chip);
        if (status)
                goto err_remove_chip;
 
@@ -327,10 +327,12 @@ EXPORT_SYMBOL_GPL(gpiochip_add);
  */
 void gpiochip_remove(struct gpio_chip *chip)
 {
+       struct gpio_desc *desc;
        unsigned long   flags;
        unsigned        id;
+       bool            requested = false;
 
-       gpiochip_unexport(chip);
+       gpiochip_sysfs_unregister(chip);
 
        gpiochip_irqchip_remove(chip);
 
@@ -341,15 +343,17 @@ void gpiochip_remove(struct gpio_chip *chip)
 
        spin_lock_irqsave(&gpio_lock, flags);
        for (id = 0; id < chip->ngpio; id++) {
-               if (test_bit(FLAG_REQUESTED, &chip->desc[id].flags))
-                       dev_crit(chip->dev, "REMOVING GPIOCHIP WITH GPIOS STILL REQUESTED\n");
+               desc = &chip->desc[id];
+               desc->chip = NULL;
+               if (test_bit(FLAG_REQUESTED, &desc->flags))
+                       requested = true;
        }
-       for (id = 0; id < chip->ngpio; id++)
-               chip->desc[id].chip = NULL;
-
        list_del(&chip->list);
        spin_unlock_irqrestore(&gpio_lock, flags);
 
+       if (requested)
+               dev_crit(chip->dev, "REMOVING GPIOCHIP WITH GPIOS STILL REQUESTED\n");
+
        kfree(chip->desc);
        chip->desc = NULL;
 }
@@ -441,6 +445,8 @@ void gpiochip_set_chained_irqchip(struct gpio_chip *gpiochip,
                 */
                irq_set_handler_data(parent_irq, gpiochip);
                irq_set_chained_handler(parent_irq, parent_handler);
+
+               gpiochip->irq_parent = parent_irq;
        }
 
        /* Set the parent IRQ for all affected IRQs */
@@ -549,6 +555,11 @@ static void gpiochip_irqchip_remove(struct gpio_chip *gpiochip)
 
        acpi_gpiochip_free_interrupts(gpiochip);
 
+       if (gpiochip->irq_parent) {
+               irq_set_chained_handler(gpiochip->irq_parent, NULL);
+               irq_set_handler_data(gpiochip->irq_parent, NULL);
+       }
+
        /* Remove all IRQ mappings and delete the domain */
        if (gpiochip->irqdomain) {
                for (offset = 0; offset < gpiochip->ngpio; offset++)
@@ -608,7 +619,7 @@ int gpiochip_irqchip_add(struct gpio_chip *gpiochip,
        of_node = gpiochip->dev->of_node;
 #ifdef CONFIG_OF_GPIO
        /*
-        * If the gpiochip has an assigned OF node this takes precendence
+        * If the gpiochip has an assigned OF node this takes precedence
         * FIXME: get rid of this and use gpiochip->dev->of_node everywhere
         */
        if (gpiochip->of_node)
@@ -1211,7 +1222,7 @@ EXPORT_SYMBOL_GPL(gpiod_get_value);
 /*
  *  _gpio_set_open_drain_value() - Set the open drain gpio's value.
  * @desc: gpio descriptor whose state need to be set.
- * @value: Non-zero for setting it HIGH otherise it will set to LOW.
+ * @value: Non-zero for setting it HIGH otherwise it will set to LOW.
  */
 static void _gpio_set_open_drain_value(struct gpio_desc *desc, bool value)
 {
@@ -1238,7 +1249,7 @@ static void _gpio_set_open_drain_value(struct gpio_desc *desc, bool value)
 /*
  *  _gpio_set_open_source_value() - Set the open source gpio's value.
  * @desc: gpio descriptor whose state need to be set.
- * @value: Non-zero for setting it HIGH otherise it will set to LOW.
+ * @value: Non-zero for setting it HIGH otherwise it will set to LOW.
  */
 static void _gpio_set_open_source_value(struct gpio_desc *desc, bool value)
 {
@@ -1300,17 +1311,16 @@ static void gpio_chip_set_multiple(struct gpio_chip *chip,
                                continue;
                        }
                        /* set outputs if the corresponding mask bit is set */
-                       if (__test_and_clear_bit(i, mask)) {
+                       if (__test_and_clear_bit(i, mask))
                                chip->set(chip, i, test_bit(i, bits));
-                       }
                }
        }
 }
 
-static void gpiod_set_array_priv(bool raw, bool can_sleep,
-                                unsigned int array_size,
-                                struct gpio_desc **desc_array,
-                                int *value_array)
+static void gpiod_set_array_value_priv(bool raw, bool can_sleep,
+                                      unsigned int array_size,
+                                      struct gpio_desc **desc_array,
+                                      int *value_array)
 {
        int i = 0;
 
@@ -1320,9 +1330,9 @@ static void gpiod_set_array_priv(bool raw, bool can_sleep,
                unsigned long bits[BITS_TO_LONGS(chip->ngpio)];
                int count = 0;
 
-               if (!can_sleep) {
+               if (!can_sleep)
                        WARN_ON(chip->can_sleep);
-               }
+
                memset(mask, 0, sizeof(mask));
                do {
                        struct gpio_desc *desc = desc_array[i];
@@ -1337,24 +1347,22 @@ static void gpiod_set_array_priv(bool raw, bool can_sleep,
                         * open drain and open source outputs are set individually
                         */
                        if (test_bit(FLAG_OPEN_DRAIN, &desc->flags)) {
-                               _gpio_set_open_drain_value(desc,value);
+                               _gpio_set_open_drain_value(desc, value);
                        } else if (test_bit(FLAG_OPEN_SOURCE, &desc->flags)) {
                                _gpio_set_open_source_value(desc, value);
                        } else {
                                __set_bit(hwgpio, mask);
-                               if (value) {
+                               if (value)
                                        __set_bit(hwgpio, bits);
-                               } else {
+                               else
                                        __clear_bit(hwgpio, bits);
-                               }
                                count++;
                        }
                        i++;
                } while ((i < array_size) && (desc_array[i]->chip == chip));
                /* push collected bits to outputs */
-               if (count != 0) {
+               if (count != 0)
                        gpio_chip_set_multiple(chip, mask, bits);
-               }
        }
 }
 
@@ -1403,7 +1411,7 @@ void gpiod_set_value(struct gpio_desc *desc, int value)
 EXPORT_SYMBOL_GPL(gpiod_set_value);
 
 /**
- * gpiod_set_raw_array() - assign values to an array of GPIOs
+ * gpiod_set_raw_array_value() - assign values to an array of GPIOs
  * @array_size: number of elements in the descriptor / value arrays
  * @desc_array: array of GPIO descriptors whose values will be assigned
  * @value_array: array of values to assign
@@ -1414,17 +1422,18 @@ EXPORT_SYMBOL_GPL(gpiod_set_value);
  * This function should be called from contexts where we cannot sleep, and will
  * complain if the GPIO chip functions potentially sleep.
  */
-void gpiod_set_raw_array(unsigned int array_size,
+void gpiod_set_raw_array_value(unsigned int array_size,
                         struct gpio_desc **desc_array, int *value_array)
 {
        if (!desc_array)
                return;
-       gpiod_set_array_priv(true, false, array_size, desc_array, value_array);
+       gpiod_set_array_value_priv(true, false, array_size, desc_array,
+                                  value_array);
 }
-EXPORT_SYMBOL_GPL(gpiod_set_raw_array);
+EXPORT_SYMBOL_GPL(gpiod_set_raw_array_value);
 
 /**
- * gpiod_set_array() - assign values to an array of GPIOs
+ * gpiod_set_array_value() - assign values to an array of GPIOs
  * @array_size: number of elements in the descriptor / value arrays
  * @desc_array: array of GPIO descriptors whose values will be assigned
  * @value_array: array of values to assign
@@ -1435,14 +1444,15 @@ EXPORT_SYMBOL_GPL(gpiod_set_raw_array);
  * This function should be called from contexts where we cannot sleep, and will
  * complain if the GPIO chip functions potentially sleep.
  */
-void gpiod_set_array(unsigned int array_size,
-                    struct gpio_desc **desc_array, int *value_array)
+void gpiod_set_array_value(unsigned int array_size,
+                          struct gpio_desc **desc_array, int *value_array)
 {
        if (!desc_array)
                return;
-       gpiod_set_array_priv(false, false, array_size, desc_array, value_array);
+       gpiod_set_array_value_priv(false, false, array_size, desc_array,
+                                  value_array);
 }
-EXPORT_SYMBOL_GPL(gpiod_set_array);
+EXPORT_SYMBOL_GPL(gpiod_set_array_value);
 
 /**
  * gpiod_cansleep() - report whether gpio value access may sleep
@@ -1604,7 +1614,7 @@ void gpiod_set_value_cansleep(struct gpio_desc *desc, int value)
 EXPORT_SYMBOL_GPL(gpiod_set_value_cansleep);
 
 /**
- * gpiod_set_raw_array_cansleep() - assign values to an array of GPIOs
+ * gpiod_set_raw_array_value_cansleep() - assign values to an array of GPIOs
  * @array_size: number of elements in the descriptor / value arrays
  * @desc_array: array of GPIO descriptors whose values will be assigned
  * @value_array: array of values to assign
@@ -1614,19 +1624,20 @@ EXPORT_SYMBOL_GPL(gpiod_set_value_cansleep);
  *
  * This function is to be called from contexts that can sleep.
  */
-void gpiod_set_raw_array_cansleep(unsigned int array_size,
-                                 struct gpio_desc **desc_array,
-                                 int *value_array)
+void gpiod_set_raw_array_value_cansleep(unsigned int array_size,
+                                       struct gpio_desc **desc_array,
+                                       int *value_array)
 {
        might_sleep_if(extra_checks);
        if (!desc_array)
                return;
-       gpiod_set_array_priv(true, true, array_size, desc_array, value_array);
+       gpiod_set_array_value_priv(true, true, array_size, desc_array,
+                                  value_array);
 }
-EXPORT_SYMBOL_GPL(gpiod_set_raw_array_cansleep);
+EXPORT_SYMBOL_GPL(gpiod_set_raw_array_value_cansleep);
 
 /**
- * gpiod_set_array_cansleep() - assign values to an array of GPIOs
+ * gpiod_set_array_value_cansleep() - assign values to an array of GPIOs
  * @array_size: number of elements in the descriptor / value arrays
  * @desc_array: array of GPIO descriptors whose values will be assigned
  * @value_array: array of values to assign
@@ -1636,16 +1647,17 @@ EXPORT_SYMBOL_GPL(gpiod_set_raw_array_cansleep);
  *
  * This function is to be called from contexts that can sleep.
  */
-void gpiod_set_array_cansleep(unsigned int array_size,
-                             struct gpio_desc **desc_array,
-                             int *value_array)
+void gpiod_set_array_value_cansleep(unsigned int array_size,
+                                   struct gpio_desc **desc_array,
+                                   int *value_array)
 {
        might_sleep_if(extra_checks);
        if (!desc_array)
                return;
-       gpiod_set_array_priv(false, true, array_size, desc_array, value_array);
+       gpiod_set_array_value_priv(false, true, array_size, desc_array,
+                                  value_array);
 }
-EXPORT_SYMBOL_GPL(gpiod_set_array_cansleep);
+EXPORT_SYMBOL_GPL(gpiod_set_array_value_cansleep);
 
 /**
  * gpiod_add_lookup_table() - register GPIO device consumers
@@ -1880,7 +1892,7 @@ EXPORT_SYMBOL_GPL(gpiod_count);
  *
  * Return the GPIO descriptor corresponding to the function con_id of device
  * dev, -ENOENT if no GPIO has been assigned to the requested function, or
- * another IS_ERR() code if an error occured while trying to acquire the GPIO.
+ * another IS_ERR() code if an error occurred while trying to acquire the GPIO.
  */
 struct gpio_desc *__must_check __gpiod_get(struct device *dev, const char *con_id,
                                         enum gpiod_flags flags)
@@ -1960,7 +1972,7 @@ static int gpiod_configure_flags(struct gpio_desc *desc, const char *con_id,
  *
  * Return a valid GPIO descriptor, -ENOENT if no GPIO has been assigned to the
  * requested function and/or index, or another IS_ERR() code if an error
- * occured while trying to acquire the GPIO.
+ * occurred while trying to acquire the GPIO.
  */
 struct gpio_desc *__must_check __gpiod_get_index(struct device *dev,
                                               const char *con_id,
@@ -2118,13 +2130,15 @@ int gpiod_hog(struct gpio_desc *desc, const char *name,
 
        local_desc = gpiochip_request_own_desc(chip, hwnum, name);
        if (IS_ERR(local_desc)) {
-               pr_debug("requesting own GPIO %s failed\n", name);
+               pr_err("requesting hog GPIO %s (chip %s, offset %d) failed\n",
+                      name, chip->label, hwnum);
                return PTR_ERR(local_desc);
        }
 
        status = gpiod_configure_flags(desc, name, lflags, dflags);
        if (status < 0) {
-               pr_debug("setup of GPIO %s failed\n", name);
+               pr_err("setup of hog GPIO %s (chip %s, offset %d) failed\n",
+                      name, chip->label, hwnum);
                gpiochip_free_own_desc(desc);
                return status;
        }
index 594b1798c0e7c69e7841e1b401b03326daa8db07..bf343004b0085c4bcc21243a63195cb875ca636a 100644 (file)
@@ -83,20 +83,12 @@ struct gpio_desc {
 #define FLAG_IS_OUT    1
 #define FLAG_EXPORT    2       /* protected by sysfs_lock */
 #define FLAG_SYSFS     3       /* exported via /sys/class/gpio/control */
-#define FLAG_TRIG_FALL 4       /* trigger on falling edge */
-#define FLAG_TRIG_RISE 5       /* trigger on rising edge */
 #define FLAG_ACTIVE_LOW        6       /* value has active low */
 #define FLAG_OPEN_DRAIN        7       /* Gpio is open drain type */
 #define FLAG_OPEN_SOURCE 8     /* Gpio is open source type */
 #define FLAG_USED_AS_IRQ 9     /* GPIO is connected to an IRQ */
-#define FLAG_SYSFS_DIR 10      /* show sysfs direction attribute */
 #define FLAG_IS_HOGGED 11      /* GPIO is hogged */
 
-#define ID_SHIFT       16      /* add new flags before this one */
-
-#define GPIO_FLAGS_MASK                ((1 << ID_SHIFT) - 1)
-#define GPIO_TRIGGER_MASK      (BIT(FLAG_TRIG_FALL) | BIT(FLAG_TRIG_RISE))
-
        const char              *label;
 };
 
@@ -151,17 +143,17 @@ static int __maybe_unused gpio_chip_hwgpio(const struct gpio_desc *desc)
 
 #ifdef CONFIG_GPIO_SYSFS
 
-int gpiochip_export(struct gpio_chip *chip);
-void gpiochip_unexport(struct gpio_chip *chip);
+int gpiochip_sysfs_register(struct gpio_chip *chip);
+void gpiochip_sysfs_unregister(struct gpio_chip *chip);
 
 #else
 
-static inline int gpiochip_export(struct gpio_chip *chip)
+static inline int gpiochip_sysfs_register(struct gpio_chip *chip)
 {
        return 0;
 }
 
-static inline void gpiochip_unexport(struct gpio_chip *chip)
+static inline void gpiochip_sysfs_unregister(struct gpio_chip *chip)
 {
 }
 
index e469c4b2e8cc85981e3ba99cae0b28c1b7a9b2ec..c25728bc388a2be7134cb3e6b895a7a39d4189a2 100644 (file)
@@ -684,8 +684,6 @@ static ssize_t node_show(struct kobject *kobj, struct attribute *attr,
                        dev->node_props.cpu_core_id_base);
        sysfs_show_32bit_prop(buffer, "simd_id_base",
                        dev->node_props.simd_id_base);
-       sysfs_show_32bit_prop(buffer, "capability",
-                       dev->node_props.capability);
        sysfs_show_32bit_prop(buffer, "max_waves_per_simd",
                        dev->node_props.max_waves_per_simd);
        sysfs_show_32bit_prop(buffer, "lds_size_in_kb",
@@ -736,6 +734,8 @@ static ssize_t node_show(struct kobject *kobj, struct attribute *attr,
                        dev->gpu->kfd2kgd->get_fw_version(
                                                dev->gpu->kgd,
                                                KGD_ENGINE_MEC1));
+               sysfs_show_32bit_prop(buffer, "capability",
+                               dev->node_props.capability);
        }
 
        return sysfs_show_32bit_prop(buffer, "max_engine_clk_ccompute",
index 266dcd6cdf3bf3ad1d487ad70110496b294e5491..0a957828b3bd3161d17b21b9e5f946307e2d17da 100644 (file)
@@ -36,9 +36,6 @@
 
 #include <linux/pci.h>
 #include <linux/export.h>
-#ifdef CONFIG_X86
-#include <asm/mtrr.h>
-#endif
 
 static int drm_version(struct drm_device *dev, void *data,
                       struct drm_file *file_priv);
@@ -197,16 +194,7 @@ static int drm_getmap(struct drm_device *dev, void *data,
        map->type = r_list->map->type;
        map->flags = r_list->map->flags;
        map->handle = (void *)(unsigned long) r_list->user_token;
-
-#ifdef CONFIG_X86
-       /*
-        * There appears to be exactly one user of the mtrr index: dritest.
-        * It's easy enough to keep it working on non-PAT systems.
-        */
-       map->mtrr = phys_wc_to_mtrr_index(r_list->map->mtrr);
-#else
-       map->mtrr = -1;
-#endif
+       map->mtrr = arch_phys_wc_index(r_list->map->mtrr);
 
        mutex_unlock(&dev->struct_mutex);
 
index ffc305fc20768c29af6883eeb2d70553839cfa6e..eb7e61078a5b6f1088489b49b42b32abe8ffca42 100644 (file)
@@ -217,7 +217,7 @@ static ssize_t status_store(struct device *device,
 
        mutex_unlock(&dev->mode_config.mutex);
 
-       return ret;
+       return ret ? ret : count;
 }
 
 static ssize_t status_show(struct device *device,
index 007c7d7d82950f597bb05ef8388fb1696ef72b38..dc55c51964ab501720f02ae682118ce12a51f0ff 100644 (file)
@@ -1667,12 +1667,15 @@ static int i915_sr_status(struct seq_file *m, void *unused)
 
        if (HAS_PCH_SPLIT(dev))
                sr_enabled = I915_READ(WM1_LP_ILK) & WM1_LP_SR_EN;
-       else if (IS_CRESTLINE(dev) || IS_I945G(dev) || IS_I945GM(dev))
+       else if (IS_CRESTLINE(dev) || IS_G4X(dev) ||
+                IS_I945G(dev) || IS_I945GM(dev))
                sr_enabled = I915_READ(FW_BLC_SELF) & FW_BLC_SELF_EN;
        else if (IS_I915GM(dev))
                sr_enabled = I915_READ(INSTPM) & INSTPM_SELF_EN;
        else if (IS_PINEVIEW(dev))
                sr_enabled = I915_READ(DSPFW3) & PINEVIEW_SELF_REFRESH_EN;
+       else if (IS_VALLEYVIEW(dev))
+               sr_enabled = I915_READ(FW_BLC_SELF_VLV) & FW_CSPWRDWNEN;
 
        intel_runtime_pm_put(dev_priv);
 
index 53394f998a1f9429f87b78598a69e232a48d5b38..2d0995e7afc37482a594be7e25b5baaadc6a6798 100644 (file)
@@ -3003,8 +3003,8 @@ int i915_vma_unbind(struct i915_vma *vma)
                } else if (vma->ggtt_view.pages) {
                        sg_free_table(vma->ggtt_view.pages);
                        kfree(vma->ggtt_view.pages);
-                       vma->ggtt_view.pages = NULL;
                }
+               vma->ggtt_view.pages = NULL;
        }
 
        drm_mm_remove_node(&vma->node);
index f27346e907b1e9e4cb1d4d3eda9b33cca2f63033..d714a4b5711e4e7fa390ec6b659d2683ef41f585 100644 (file)
@@ -880,10 +880,8 @@ intel_dp_aux_ch(struct intel_dp *intel_dp,
                                      DP_AUX_CH_CTL_RECEIVE_ERROR))
                                continue;
                        if (status & DP_AUX_CH_CTL_DONE)
-                               break;
+                               goto done;
                }
-               if (status & DP_AUX_CH_CTL_DONE)
-                       break;
        }
 
        if ((status & DP_AUX_CH_CTL_DONE) == 0) {
@@ -892,6 +890,7 @@ intel_dp_aux_ch(struct intel_dp *intel_dp,
                goto out;
        }
 
+done:
        /* Check for timeout or receive error.
         * Timeouts occur when the sink is not connected
         */
index 56e437e3158021a09641d188affc6129f0b1eda8..ae628001fd97873b67f99fb0128167858948afe6 100644 (file)
@@ -435,7 +435,7 @@ gmbus_xfer(struct i2c_adapter *adapter,
                                               struct intel_gmbus,
                                               adapter);
        struct drm_i915_private *dev_priv = bus->dev_priv;
-       int i, reg_offset;
+       int i = 0, inc, try = 0, reg_offset;
        int ret = 0;
 
        intel_aux_display_runtime_get(dev_priv);
@@ -448,12 +448,14 @@ gmbus_xfer(struct i2c_adapter *adapter,
 
        reg_offset = dev_priv->gpio_mmio_base;
 
+retry:
        I915_WRITE(GMBUS0 + reg_offset, bus->reg0);
 
-       for (i = 0; i < num; i++) {
+       for (; i < num; i += inc) {
+               inc = 1;
                if (gmbus_is_index_read(msgs, i, num)) {
                        ret = gmbus_xfer_index_read(dev_priv, &msgs[i]);
-                       i += 1;  /* set i to the index of the read xfer */
+                       inc = 2; /* an index read is two msgs */
                } else if (msgs[i].flags & I2C_M_RD) {
                        ret = gmbus_xfer_read(dev_priv, &msgs[i], 0);
                } else {
@@ -525,6 +527,18 @@ clear_err:
                         adapter->name, msgs[i].addr,
                         (msgs[i].flags & I2C_M_RD) ? 'r' : 'w', msgs[i].len);
 
+       /*
+        * Passive adapters sometimes NAK the first probe. Retry the first
+        * message once on -ENXIO for GMBUS transfers; the bit banging algorithm
+        * has retries internally. See also the retry loop in
+        * drm_do_probe_ddc_edid, which bails out on the first -ENXIO.
+        */
+       if (ret == -ENXIO && i == 0 && try++ == 0) {
+               DRM_DEBUG_KMS("GMBUS [%s] NAK on first message, retry\n",
+                             adapter->name);
+               goto retry;
+       }
+
        goto out;
 
 timeout:
index 09df74b8e917b1dac90d460be50d1c4c5152881c..424e6219778712dcaf0e7c5c1ae7c51709fce6ba 100644 (file)
@@ -1134,6 +1134,12 @@ static int gen8_init_common_ring(struct intel_engine_cs *ring)
        I915_WRITE_IMR(ring, ~(ring->irq_enable_mask | ring->irq_keep_mask));
        I915_WRITE(RING_HWSTAM(ring->mmio_base), 0xffffffff);
 
+       if (ring->status_page.obj) {
+               I915_WRITE(RING_HWS_PGA(ring->mmio_base),
+                          (u32)ring->status_page.gfx_addr);
+               POSTING_READ(RING_HWS_PGA(ring->mmio_base));
+       }
+
        I915_WRITE(RING_MODE_GEN7(ring),
                   _MASKED_BIT_DISABLE(GFX_REPLAY_MODE) |
                   _MASKED_BIT_ENABLE(GFX_RUN_LIST_ENABLE));
index 71e87abdcae7c1aea0077949f7f7a91cf309732b..481337436f7215eae46d7c407f1596d8e31fe031 100644 (file)
@@ -396,16 +396,6 @@ int intel_opregion_notify_adapter(struct drm_device *dev, pci_power_t state)
        return -EINVAL;
 }
 
-/*
- * If the vendor backlight interface is not in use and ACPI backlight interface
- * is broken, do not bother processing backlight change requests from firmware.
- */
-static bool should_ignore_backlight_request(void)
-{
-       return acpi_video_backlight_support() &&
-              !acpi_video_verify_backlight_support();
-}
-
 static u32 asle_set_backlight(struct drm_device *dev, u32 bclp)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
@@ -414,7 +404,7 @@ static u32 asle_set_backlight(struct drm_device *dev, u32 bclp)
 
        DRM_DEBUG_DRIVER("bclp = 0x%08x\n", bclp);
 
-       if (should_ignore_backlight_request()) {
+       if (acpi_video_get_backlight_type() == acpi_backlight_native) {
                DRM_DEBUG_KMS("opregion backlight request ignored\n");
                return 0;
        }
index 441e2502b88946ff2d7455a9f26cc32faa87d8fc..005b5e04de4d74d13eee87af223c9e22687f6d35 100644 (file)
@@ -901,13 +901,6 @@ static int chv_init_workarounds(struct intel_engine_cs *ring)
                            GEN6_WIZ_HASHING_MASK,
                            GEN6_WIZ_HASHING_16x4);
 
-       if (INTEL_REVID(dev) == SKL_REVID_C0 ||
-           INTEL_REVID(dev) == SKL_REVID_D0)
-               /* WaBarrierPerformanceFixDisable:skl */
-               WA_SET_BIT_MASKED(HDC_CHICKEN0,
-                                 HDC_FENCE_DEST_SLM_DISABLE |
-                                 HDC_BARRIER_PERFORMANCE_DISABLE);
-
        return 0;
 }
 
@@ -1024,6 +1017,13 @@ static int skl_init_workarounds(struct intel_engine_cs *ring)
                WA_SET_BIT_MASKED(HIZ_CHICKEN,
                                  BDW_HIZ_POWER_COMPILER_CLOCK_GATING_DISABLE);
 
+       if (INTEL_REVID(dev) == SKL_REVID_C0 ||
+           INTEL_REVID(dev) == SKL_REVID_D0)
+               /* WaBarrierPerformanceFixDisable:skl */
+               WA_SET_BIT_MASKED(HDC_CHICKEN0,
+                                 HDC_FENCE_DEST_SLM_DISABLE |
+                                 HDC_BARRIER_PERFORMANCE_DISABLE);
+
        return skl_tune_iz_hashing(ring);
 }
 
index e87d2f418de4f381d50471494e5fe8050de4bdb2..987b81f31b0e693cfe7d505b2f66eecc7eac6539 100644 (file)
@@ -2550,7 +2550,7 @@ intel_sdvo_analog_init(struct intel_sdvo *intel_sdvo, int device)
 
        DRM_DEBUG_KMS("initialising analog device %d\n", device);
 
-       intel_sdvo_connector = kzalloc(sizeof(*intel_sdvo_connector), GFP_KERNEL);
+       intel_sdvo_connector = intel_sdvo_connector_alloc();
        if (!intel_sdvo_connector)
                return false;
 
index 6e84df9369a657223d17387ad14929cdf435e238..ad4b9010dfb0bbed135185e9f64aed98c3239a24 100644 (file)
@@ -1526,6 +1526,11 @@ static int mga_vga_mode_valid(struct drm_connector *connector,
                return MODE_BANDWIDTH;
        }
 
+       if ((mode->hdisplay % 8) != 0 || (mode->hsync_start % 8) != 0 ||
+           (mode->hsync_end % 8) != 0 || (mode->htotal % 8) != 0) {
+               return MODE_H_ILLEGAL;
+       }
+
        if (mode->crtc_hdisplay > 2048 || mode->crtc_hsync_start > 4096 ||
            mode->crtc_hsync_end > 4096 || mode->crtc_htotal > 4096 ||
            mode->crtc_vdisplay > 2048 || mode->crtc_vsync_start > 4096 ||
index 92be50c39ffd4a4b498d439093725e991bead162..ab89eed9ddd9c47e688c090884c8f2561ff2dc11 100644 (file)
@@ -7944,8 +7944,8 @@ typedef struct {
 typedef struct {
   AMD_ACPI_DESCRIPTION_HEADER SHeader;
   UCHAR TableUUID[16];    //0x24
-  ULONG VBIOSImageOffset; //0x34. Offset to the first GOP_VBIOS_CONTENT block from the beginning of the stucture.
-  ULONG Lib1ImageOffset;  //0x38. Offset to the first GOP_LIB1_CONTENT block from the beginning of the stucture.
+  ULONG VBIOSImageOffset; //0x34. Offset to the first GOP_VBIOS_CONTENT block from the beginning of the structure.
+  ULONG Lib1ImageOffset;  //0x38. Offset to the first GOP_LIB1_CONTENT block from the beginning of the structure.
   ULONG Reserved[4];      //0x3C
 }UEFI_ACPI_VFCT;
 
index e597ffc265633ef7439b2247301333a3affaeaea..dac78ad24b31558aa53d917fb802865b6a122b61 100644 (file)
@@ -580,9 +580,6 @@ static u32 atombios_adjust_pll(struct drm_crtc *crtc,
                else
                        radeon_crtc->pll_flags |= RADEON_PLL_PREFER_LOW_REF_DIV;
 
-               /* if there is no audio, set MINM_OVER_MAXP  */
-               if (!drm_detect_monitor_audio(radeon_connector_edid(connector)))
-                       radeon_crtc->pll_flags |= RADEON_PLL_PREFER_MINM_OVER_MAXP;
                if (rdev->family < CHIP_RV770)
                        radeon_crtc->pll_flags |= RADEON_PLL_PREFER_MINM_OVER_MAXP;
                /* use frac fb div on APUs */
@@ -1798,9 +1795,7 @@ static int radeon_get_shared_nondp_ppll(struct drm_crtc *crtc)
                        if ((crtc->mode.clock == test_crtc->mode.clock) &&
                            (adjusted_clock == test_adjusted_clock) &&
                            (radeon_crtc->ss_enabled == test_radeon_crtc->ss_enabled) &&
-                           (test_radeon_crtc->pll_id != ATOM_PPLL_INVALID) &&
-                           (drm_detect_monitor_audio(radeon_connector_edid(test_radeon_crtc->connector)) ==
-                            drm_detect_monitor_audio(radeon_connector_edid(radeon_crtc->connector))))
+                           (test_radeon_crtc->pll_id != ATOM_PPLL_INVALID))
                                return test_radeon_crtc->pll_id;
                }
        }
index f04205170b8a5942d73437ada72437bc18d028a8..cfa3a84a2af03c100741cb7e5b352781adf60b00 100644 (file)
@@ -173,7 +173,7 @@ void dce3_2_hdmi_update_acr(struct drm_encoder *encoder, long offset,
        struct drm_device *dev = encoder->dev;
        struct radeon_device *rdev = dev->dev_private;
 
-       WREG32(HDMI0_ACR_PACKET_CONTROL + offset,
+       WREG32(DCE3_HDMI0_ACR_PACKET_CONTROL + offset,
                HDMI0_ACR_SOURCE |              /* select SW CTS value */
                HDMI0_ACR_AUTO_SEND);   /* allow hw to sent ACR packets when required */
 
index b7ca4c51462120fab3ab146dd74f653e8bcb91cb..a7fdfa4f0857b3a416e67d79007a1da731455b80 100644 (file)
@@ -1463,6 +1463,21 @@ int radeon_device_init(struct radeon_device *rdev,
        if (r)
                DRM_ERROR("ib ring test failed (%d).\n", r);
 
+       /*
+        * Turks/Thames GPU will freeze whole laptop if DPM is not restarted
+        * after the CP ring have chew one packet at least. Hence here we stop
+        * and restart DPM after the radeon_ib_ring_tests().
+        */
+       if (rdev->pm.dpm_enabled &&
+           (rdev->pm.pm_method == PM_METHOD_DPM) &&
+           (rdev->family == CHIP_TURKS) &&
+           (rdev->flags & RADEON_IS_MOBILITY)) {
+               mutex_lock(&rdev->pm.mutex);
+               radeon_dpm_disable(rdev);
+               radeon_dpm_enable(rdev);
+               mutex_unlock(&rdev->pm.mutex);
+       }
+
        if ((radeon_testing & 1)) {
                if (rdev->accel_working)
                        radeon_test_moves(rdev);
index 2b98ed3e684d706a07e3c43b6da9f2232143e580..257b10be5cda902861339d9fde17c38e4f238d06 100644 (file)
@@ -663,12 +663,17 @@ int
 radeon_dp_mst_probe(struct radeon_connector *radeon_connector)
 {
        struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv;
+       struct drm_device *dev = radeon_connector->base.dev;
+       struct radeon_device *rdev = dev->dev_private;
        int ret;
        u8 msg[1];
 
        if (!radeon_mst)
                return 0;
 
+       if (!ASIC_IS_DCE5(rdev))
+               return 0;
+
        if (dig_connector->dpcd[DP_DPCD_REV] < 0x12)
                return 0;
 
index 7b2a7335cc5d557eafa6864d50cb6ebc9cdfb5ff..b0acf50d95581d9970cef89690be25b32324c7b3 100644 (file)
@@ -576,6 +576,9 @@ static int radeon_info_ioctl(struct drm_device *dev, void *data, struct drm_file
                if (radeon_get_allowed_info_register(rdev, *value, value))
                        return -EINVAL;
                break;
+       case RADEON_INFO_VA_UNMAP_WORKING:
+               *value = true;
+               break;
        default:
                DRM_DEBUG_KMS("Invalid request %d\n", info->request);
                return -EINVAL;
index de42fc4a22b869296ff44c85c859678c6155ddd7..9c3377ca17b75ecd2092e4fd78a2238c126d88f1 100644 (file)
@@ -458,14 +458,16 @@ int radeon_vm_bo_set_addr(struct radeon_device *rdev,
                /* make sure object fit at this offset */
                eoffset = soffset + size;
                if (soffset >= eoffset) {
-                       return -EINVAL;
+                       r = -EINVAL;
+                       goto error_unreserve;
                }
 
                last_pfn = eoffset / RADEON_GPU_PAGE_SIZE;
                if (last_pfn > rdev->vm_manager.max_pfn) {
                        dev_err(rdev->dev, "va above limit (0x%08X > 0x%08X)\n",
                                last_pfn, rdev->vm_manager.max_pfn);
-                       return -EINVAL;
+                       r = -EINVAL;
+                       goto error_unreserve;
                }
 
        } else {
@@ -486,7 +488,8 @@ int radeon_vm_bo_set_addr(struct radeon_device *rdev,
                                "(bo %p 0x%010lx 0x%010lx)\n", bo_va->bo,
                                soffset, tmp->bo, tmp->it.start, tmp->it.last);
                        mutex_unlock(&vm->mutex);
-                       return -EINVAL;
+                       r = -EINVAL;
+                       goto error_unreserve;
                }
        }
 
@@ -497,7 +500,8 @@ int radeon_vm_bo_set_addr(struct radeon_device *rdev,
                        tmp = kzalloc(sizeof(struct radeon_bo_va), GFP_KERNEL);
                        if (!tmp) {
                                mutex_unlock(&vm->mutex);
-                               return -ENOMEM;
+                               r = -ENOMEM;
+                               goto error_unreserve;
                        }
                        tmp->it.start = bo_va->it.start;
                        tmp->it.last = bo_va->it.last;
@@ -555,7 +559,6 @@ int radeon_vm_bo_set_addr(struct radeon_device *rdev,
                r = radeon_vm_clear_bo(rdev, pt);
                if (r) {
                        radeon_bo_unref(&pt);
-                       radeon_bo_reserve(bo_va->bo, false);
                        return r;
                }
 
@@ -575,6 +578,10 @@ int radeon_vm_bo_set_addr(struct radeon_device *rdev,
 
        mutex_unlock(&vm->mutex);
        return 0;
+
+error_unreserve:
+       radeon_bo_unreserve(bo_va->bo);
+       return r;
 }
 
 /**
index 67bab5c36056128ba75837e5ea041d390a9e580b..6d2f39d36e445bda4840f06d7eb1f779081f2c22 100644 (file)
@@ -1119,10 +1119,9 @@ static int ipu_irq_init(struct ipu_soc *ipu)
                ct->regs.mask = IPU_INT_CTRL(i / 32);
        }
 
-       irq_set_chained_handler(ipu->irq_sync, ipu_irq_handler);
-       irq_set_handler_data(ipu->irq_sync, ipu);
-       irq_set_chained_handler(ipu->irq_err, ipu_err_irq_handler);
-       irq_set_handler_data(ipu->irq_err, ipu);
+       irq_set_chained_handler_and_data(ipu->irq_sync, ipu_irq_handler, ipu);
+       irq_set_chained_handler_and_data(ipu->irq_err, ipu_err_irq_handler,
+                                        ipu);
 
        return 0;
 }
@@ -1131,10 +1130,8 @@ static void ipu_irq_exit(struct ipu_soc *ipu)
 {
        int i, irq;
 
-       irq_set_chained_handler(ipu->irq_err, NULL);
-       irq_set_handler_data(ipu->irq_err, NULL);
-       irq_set_chained_handler(ipu->irq_sync, NULL);
-       irq_set_handler_data(ipu->irq_sync, NULL);
+       irq_set_chained_handler_and_data(ipu->irq_err, NULL, NULL);
+       irq_set_chained_handler_and_data(ipu->irq_sync, NULL, NULL);
 
        /* TODO: remove irq_domain_generic_chips */
 
index 15338afdf7f9ab71c84a15dc3143c9dda11fa832..cc4c6649d19503e236f905e8feafe21440237fea 100644 (file)
@@ -634,7 +634,12 @@ config HID_PLANTRONICS
        tristate "Plantronics USB HID Driver"
        depends on HID
        ---help---
-       Provides HID support for Plantronics telephony devices.
+         Provides HID support for Plantronics USB audio devices.
+         Correctly maps vendor unique volume up/down HID usages to
+         KEY_VOLUMEUP and KEY_VOLUMEDOWN events and prevents core mapping
+         of other vendor unique HID usages to random mouse events.
+
+         Say M here if you may ever plug in a Plantronics USB audio device.
 
 config HID_PRIMAX
        tristate "Primax non-fully HID-compliant devices"
index e4a21dfd7ef3011506c9e4e50eb483480e05c5cd..2f8a41dc3cc8163a50197f7cd6928510012f27f0 100644 (file)
@@ -24,7 +24,7 @@ obj-$(CONFIG_HID_A4TECH)      += hid-a4tech.o
 obj-$(CONFIG_HID_ACRUX)                += hid-axff.o
 obj-$(CONFIG_HID_APPLE)                += hid-apple.o
 obj-$(CONFIG_HID_APPLEIR)      += hid-appleir.o
-obj-$(CONFIG_HID_AUREAL)        += hid-aureal.o
+obj-$(CONFIG_HID_AUREAL)       += hid-aureal.o
 obj-$(CONFIG_HID_BELKIN)       += hid-belkin.o
 obj-$(CONFIG_HID_BETOP_FF)     += hid-betopff.o
 obj-$(CONFIG_HID_CHERRY)       += hid-cherry.o
@@ -46,12 +46,12 @@ obj-$(CONFIG_HID_ICADE)             += hid-icade.o
 obj-$(CONFIG_HID_KENSINGTON)   += hid-kensington.o
 obj-$(CONFIG_HID_KEYTOUCH)     += hid-keytouch.o
 obj-$(CONFIG_HID_KYE)          += hid-kye.o
-obj-$(CONFIG_HID_LCPOWER)       += hid-lcpower.o
+obj-$(CONFIG_HID_LCPOWER)      += hid-lcpower.o
 obj-$(CONFIG_HID_LENOVO)       += hid-lenovo.o
 obj-$(CONFIG_HID_LOGITECH)     += hid-logitech.o
 obj-$(CONFIG_HID_LOGITECH_DJ)  += hid-logitech-dj.o
 obj-$(CONFIG_HID_LOGITECH_HIDPP)       += hid-logitech-hidpp.o
-obj-$(CONFIG_HID_MAGICMOUSE)    += hid-magicmouse.o
+obj-$(CONFIG_HID_MAGICMOUSE)   += hid-magicmouse.o
 obj-$(CONFIG_HID_MICROSOFT)    += hid-microsoft.o
 obj-$(CONFIG_HID_MONTEREY)     += hid-monterey.o
 obj-$(CONFIG_HID_MULTITOUCH)   += hid-multitouch.o
index 722a925795a2886557d9b86ed9befdc4c53b5514..157c627750535e8943769e3078de068e4f1c7a47 100644 (file)
@@ -706,7 +706,8 @@ static void hid_scan_collection(struct hid_parser *parser, unsigned type)
 
        if (hid->vendor == USB_VENDOR_ID_MICROSOFT &&
            (hid->product == USB_DEVICE_ID_MS_TYPE_COVER_3 ||
-            hid->product == USB_DEVICE_ID_MS_TYPE_COVER_3_JP) &&
+            hid->product == USB_DEVICE_ID_MS_TYPE_COVER_3_JP ||
+            hid->product == USB_DEVICE_ID_MS_POWER_COVER) &&
            hid->group == HID_GROUP_MULTITOUCH)
                hid->group = HID_GROUP_GENERIC;
 
@@ -1061,13 +1062,13 @@ static u32 s32ton(__s32 value, unsigned n)
  * Search linux-kernel and linux-usb-devel archives for "hid-core extract".
  */
 
-static __u32 extract(const struct hid_device *hid, __u8 *report,
+__u32 hid_field_extract(const struct hid_device *hid, __u8 *report,
                     unsigned offset, unsigned n)
 {
        u64 x;
 
        if (n > 32)
-               hid_warn(hid, "extract() called with n (%d) > 32! (%s)\n",
+               hid_warn(hid, "hid_field_extract() called with n (%d) > 32! (%s)\n",
                         n, current->comm);
 
        report += offset >> 3;  /* adjust byte index */
@@ -1076,6 +1077,7 @@ static __u32 extract(const struct hid_device *hid, __u8 *report,
        x = (x >> offset) & ((1ULL << n) - 1);  /* extract bit field */
        return (u32) x;
 }
+EXPORT_SYMBOL_GPL(hid_field_extract);
 
 /*
  * "implement" : set bits in a little endian bit stream.
@@ -1221,9 +1223,9 @@ static void hid_input_field(struct hid_device *hid, struct hid_field *field,
        for (n = 0; n < count; n++) {
 
                value[n] = min < 0 ?
-                       snto32(extract(hid, data, offset + n * size, size),
-                              size) :
-                       extract(hid, data, offset + n * size, size);
+                       snto32(hid_field_extract(hid, data, offset + n * size,
+                              size), size) :
+                       hid_field_extract(hid, data, offset + n * size, size);
 
                /* Ignore report if ErrorRollOver */
                if (!(field->flags & HID_MAIN_ITEM_VARIABLE) &&
@@ -1851,6 +1853,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
        { HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_TPKBD) },
        { HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_CUSBKBD) },
        { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_CBTKBD) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_TPPRODOCK) },
 #endif
        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_MX3000_RECEIVER) },
        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER) },
@@ -1901,6 +1904,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
        { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_OFFICE_KB) },
        { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_3) },
        { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_3_JP) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_POWER_COVER) },
        { HID_USB_DEVICE(USB_VENDOR_ID_MONTEREY, USB_DEVICE_ID_GENIUS_KB29E) },
        { HID_USB_DEVICE(USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL) },
        { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN) },
@@ -1959,9 +1963,12 @@ static const struct hid_device_id hid_have_special_driver[] = {
        { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SMK, USB_DEVICE_ID_SMK_PS3_BDREMOTE) },
        { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_BUZZ_CONTROLLER) },
        { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_WIRELESS_BUZZ_CONTROLLER) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_MOTION_CONTROLLER) },
+       { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_MOTION_CONTROLLER) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_NAVIGATION_CONTROLLER) },
+       { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_NAVIGATION_CONTROLLER) },
        { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_BDREMOTE) },
        { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) },
-       { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_NAVIGATION_CONTROLLER) },
        { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) },
        { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER) },
        { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER) },
@@ -1997,6 +2004,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
        { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP_LTD, USB_DEVICE_ID_SUPER_JOY_BOX_3_PRO) },
        { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP_LTD, USB_DEVICE_ID_SUPER_DUAL_BOX_PRO) },
        { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP_LTD, USB_DEVICE_ID_SUPER_JOY_BOX_5_PRO) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_PLAYDOTCOM, USB_DEVICE_ID_PLAYDOTCOM_EMS_USBII) },
        { HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_SLIM_TABLET_5_8_INCH) },
        { HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_SLIM_TABLET_12_1_INCH) },
        { HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_Q_PAD) },
@@ -2265,14 +2273,6 @@ static const struct hid_device_id hid_ignore_list[] = {
        { HID_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH, 0x0001) },
        { HID_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH, 0x0002) },
        { HID_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH, 0x0004) },
-       { HID_USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_4_PHIDGETSERVO_30) },
-       { HID_USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_1_PHIDGETSERVO_30) },
-       { HID_USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_0_4_IF_KIT) },
-       { HID_USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_16_16_IF_KIT) },
-       { HID_USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_8_8_8_IF_KIT) },
-       { HID_USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_8_7_IF_KIT) },
-       { HID_USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_8_8_IF_KIT) },
-       { HID_USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_PHIDGET_MOTORCONTROL) },
        { HID_USB_DEVICE(USB_VENDOR_ID_GOTOP, USB_DEVICE_ID_SUPER_Q2) },
        { HID_USB_DEVICE(USB_VENDOR_ID_GOTOP, USB_DEVICE_ID_GOGOPEN) },
        { HID_USB_DEVICE(USB_VENDOR_ID_GOTOP, USB_DEVICE_ID_PENPOWER) },
@@ -2399,14 +2399,6 @@ static const struct hid_device_id hid_ignore_list[] = {
        { HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_WTP) },
        { HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_DPAD) },
 #endif
-       { HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_LABPRO) },
-       { HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_GOTEMP) },
-       { HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_SKIP) },
-       { HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_CYCLOPS) },
-       { HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_LCSPEC) },
-       { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_4_PHIDGETSERVO_20) },
-       { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_1_PHIDGETSERVO_20) },
-       { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_8_8_4_IF_KIT) },
        { HID_USB_DEVICE(USB_VENDOR_ID_YEALINK, USB_DEVICE_ID_YEALINK_P1K_P4K_B2K) },
        { HID_USB_DEVICE(USB_VENDOR_ID_RISO_KAGAKU, USB_DEVICE_ID_RI_KA_WEBMAIL) },
        { }
index c4ef3bc726e34e5e08aa947b64ddfa4dc7f772b0..1b764d1745f3daa693a17ee706c302ff31ae0f0e 100644 (file)
@@ -41,13 +41,9 @@ static __u8 *cp_report_fixup(struct hid_device *hdev, __u8 *rdesc,
 
        for (i = 0; i < *rsize - 4; i++)
                if (rdesc[i] == 0x29 && rdesc[i + 2] == 0x19) {
-                       __u8 tmp;
-
                        rdesc[i] = 0x19;
                        rdesc[i + 2] = 0x29;
-                       tmp = rdesc[i + 3];
-                       rdesc[i + 3] = rdesc[i + 1];
-                       rdesc[i + 1] = tmp;
+                       swap(rdesc[i + 3], rdesc[i + 1]);
                }
        return rdesc;
 }
index 7ce93d927f62d8d029c3f32cefd3dc3f3b2f36dd..b04b0820d816323a01d147c702503b0797734ea4 100644 (file)
 #define USB_DEVICE_ID_CHICONY_TACTICAL_PAD     0x0418
 #define USB_DEVICE_ID_CHICONY_MULTI_TOUCH      0xb19d
 #define USB_DEVICE_ID_CHICONY_WIRELESS 0x0618
+#define USB_DEVICE_ID_CHICONY_PIXART_USB_OPTICAL_MOUSE 0x1053
 #define USB_DEVICE_ID_CHICONY_WIRELESS2        0x1123
 #define USB_DEVICE_ID_CHICONY_AK1D     0x1125
 
 #define USB_DEVICE_ID_GENERAL_TOUCH_WIN8_PIT_010A 0x010a
 #define USB_DEVICE_ID_GENERAL_TOUCH_WIN8_PIT_E100 0xe100
 
-#define USB_VENDOR_ID_GLAB             0x06c2
-#define USB_DEVICE_ID_4_PHIDGETSERVO_30        0x0038
-#define USB_DEVICE_ID_1_PHIDGETSERVO_30        0x0039
-#define USB_DEVICE_ID_0_0_4_IF_KIT     0x0040
-#define USB_DEVICE_ID_0_16_16_IF_KIT   0x0044
-#define USB_DEVICE_ID_8_8_8_IF_KIT     0x0045
-#define USB_DEVICE_ID_0_8_7_IF_KIT     0x0051
-#define USB_DEVICE_ID_0_8_8_IF_KIT     0x0053
-#define USB_DEVICE_ID_PHIDGET_MOTORCONTROL     0x0058
-
 #define USB_VENDOR_ID_GOODTOUCH                0x1aad
 #define USB_DEVICE_ID_GOODTOUCH_000f   0x000f
 
 #define USB_DEVICE_ID_LENOVO_TPKBD     0x6009
 #define USB_DEVICE_ID_LENOVO_CUSBKBD   0x6047
 #define USB_DEVICE_ID_LENOVO_CBTKBD    0x6048
+#define USB_DEVICE_ID_LENOVO_TPPRODOCK 0x6067
 
 #define USB_VENDOR_ID_LG               0x1fd2
 #define USB_DEVICE_ID_LG_MULTITOUCH    0x0064
 #define USB_DEVICE_ID_MS_TYPE_COVER_2    0x07a9
 #define USB_DEVICE_ID_MS_TYPE_COVER_3    0x07dc
 #define USB_DEVICE_ID_MS_TYPE_COVER_3_JP 0x07dd
+#define USB_DEVICE_ID_MS_POWER_COVER     0x07da
 
 #define USB_VENDOR_ID_MOJO             0x8282
 #define USB_DEVICE_ID_RETRO_ADAPTER    0x3201
 #define USB_DEVICE_ID_SONY_PS3_BDREMOTE                0x0306
 #define USB_DEVICE_ID_SONY_PS3_CONTROLLER      0x0268
 #define USB_DEVICE_ID_SONY_PS4_CONTROLLER      0x05c4
+#define USB_DEVICE_ID_SONY_MOTION_CONTROLLER   0x03d5
 #define USB_DEVICE_ID_SONY_NAVIGATION_CONTROLLER       0x042f
 #define USB_DEVICE_ID_SONY_BUZZ_CONTROLLER             0x0002
 #define USB_DEVICE_ID_SONY_WIRELESS_BUZZ_CONTROLLER    0x1000
 #define USB_DEVICE_ID_VELLEMAN_K8061_FIRST     0x8061
 #define USB_DEVICE_ID_VELLEMAN_K8061_LAST      0x8068
 
-#define USB_VENDOR_ID_VERNIER          0x08f7
-#define USB_DEVICE_ID_VERNIER_LABPRO   0x0001
-#define USB_DEVICE_ID_VERNIER_GOTEMP   0x0002
-#define USB_DEVICE_ID_VERNIER_SKIP     0x0003
-#define USB_DEVICE_ID_VERNIER_CYCLOPS  0x0004
-#define USB_DEVICE_ID_VERNIER_LCSPEC   0x0006
-
 #define USB_VENDOR_ID_VTL              0x0306
 #define USB_DEVICE_ID_VTL_MULTITOUCH_FF3F      0xff3f
 
 
 #define USB_VENDOR_ID_WISEGROUP                0x0925
 #define USB_DEVICE_ID_SMARTJOY_PLUS    0x0005
-#define USB_DEVICE_ID_1_PHIDGETSERVO_20        0x8101
-#define USB_DEVICE_ID_4_PHIDGETSERVO_20        0x8104
-#define USB_DEVICE_ID_8_8_4_IF_KIT     0x8201
 #define USB_DEVICE_ID_SUPER_JOY_BOX_3  0x8888
 #define USB_DEVICE_ID_QUAD_USB_JOYPAD  0x8800
 #define USB_DEVICE_ID_DUAL_USB_JOYPAD  0x8866
 #define USB_VENDOR_ID_RISO_KAGAKU      0x1294  /* Riso Kagaku Corp. */
 #define USB_DEVICE_ID_RI_KA_WEBMAIL    0x1320  /* Webmail Notifier */
 
+#define USB_VENDOR_ID_MULTIPLE_1781    0x1781
+#define USB_DEVICE_ID_RAPHNET_4NES4SNES_OLD    0x0a8d
+
+#define USB_VENDOR_ID_DRACAL_RAPHNET   0x289b
+#define USB_DEVICE_ID_RAPHNET_2NES2SNES        0x0002
+#define USB_DEVICE_ID_RAPHNET_4NES4SNES        0x0003
+
 #endif
index 008e89bf6f3c3d112d92640bdf91f1e935e0641c..3511bbaba505a4524ad382297ec1e486e21e7e48 100644 (file)
@@ -1157,7 +1157,8 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct
                return;
 
        /* report the usage code as scancode if the key status has changed */
-       if (usage->type == EV_KEY && !!test_bit(usage->code, input->key) != value)
+       if (usage->type == EV_KEY &&
+           (!test_bit(usage->code, input->key)) == value)
                input_event(input, EV_MSC, MSC_SCAN, usage->hid);
 
        input_event(input, usage->type, usage->code, value);
index c4c3f0952521f975fb914580f2fe0f086013038b..4f59bffd020538846d88d4c85ef76168542c980e 100644 (file)
@@ -43,6 +43,35 @@ struct lenovo_drvdata_cptkbd {
 
 #define map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, EV_KEY, (c))
 
+static const __u8 lenovo_pro_dock_need_fixup_collection[] = {
+       0x05, 0x88,             /* Usage Page (Vendor Usage Page 0x88)  */
+       0x09, 0x01,             /* Usage (Vendor Usage 0x01)            */
+       0xa1, 0x01,             /* Collection (Application)             */
+       0x85, 0x04,             /*  Report ID (4)                       */
+       0x19, 0x00,             /*  Usage Minimum (0)                   */
+       0x2a, 0xff, 0xff,       /*  Usage Maximum (65535)               */
+};
+
+static __u8 *lenovo_report_fixup(struct hid_device *hdev, __u8 *rdesc,
+               unsigned int *rsize)
+{
+       switch (hdev->product) {
+       case USB_DEVICE_ID_LENOVO_TPPRODOCK:
+               /* the fixups that need to be done:
+                *   - get a reasonable usage max for the vendor collection
+                *     0x8801 from the report ID 4
+                */
+               if (*rsize >= 153 &&
+                   memcmp(&rdesc[140], lenovo_pro_dock_need_fixup_collection,
+                         sizeof(lenovo_pro_dock_need_fixup_collection)) == 0) {
+                       rdesc[151] = 0x01;
+                       rdesc[152] = 0x00;
+               }
+               break;
+       }
+       return rdesc;
+}
+
 static int lenovo_input_mapping_tpkbd(struct hid_device *hdev,
                struct hid_input *hi, struct hid_field *field,
                struct hid_usage *usage, unsigned long **bit, int *max)
@@ -599,7 +628,8 @@ static int lenovo_probe_tpkbd(struct hid_device *hdev)
                                    GFP_KERNEL);
        if (data_pointer == NULL) {
                hid_err(hdev, "Could not allocate memory for driver data\n");
-               return -ENOMEM;
+               ret = -ENOMEM;
+               goto err;
        }
 
        // set same default values as windows driver
@@ -610,7 +640,8 @@ static int lenovo_probe_tpkbd(struct hid_device *hdev)
        name_micmute = devm_kzalloc(&hdev->dev, name_sz, GFP_KERNEL);
        if (name_mute == NULL || name_micmute == NULL) {
                hid_err(hdev, "Could not allocate memory for led data\n");
-               return -ENOMEM;
+               ret = -ENOMEM;
+               goto err;
        }
        snprintf(name_mute, name_sz, "%s:amber:mute", dev_name(dev));
        snprintf(name_micmute, name_sz, "%s:amber:micmute", dev_name(dev));
@@ -634,6 +665,9 @@ static int lenovo_probe_tpkbd(struct hid_device *hdev)
        lenovo_features_set_tpkbd(hdev);
 
        return 0;
+err:
+       sysfs_remove_group(&hdev->dev.kobj, &lenovo_attr_group_tpkbd);
+       return ret;
 }
 
 static int lenovo_probe_cptkbd(struct hid_device *hdev)
@@ -762,10 +796,29 @@ static void lenovo_remove(struct hid_device *hdev)
        hid_hw_stop(hdev);
 }
 
+static void lenovo_input_configured(struct hid_device *hdev,
+               struct hid_input *hi)
+{
+       switch (hdev->product) {
+               case USB_DEVICE_ID_LENOVO_TPKBD:
+               case USB_DEVICE_ID_LENOVO_CUSBKBD:
+               case USB_DEVICE_ID_LENOVO_CBTKBD:
+                       if (test_bit(EV_REL, hi->input->evbit)) {
+                               /* set only for trackpoint device */
+                               __set_bit(INPUT_PROP_POINTER, hi->input->propbit);
+                               __set_bit(INPUT_PROP_POINTING_STICK,
+                                               hi->input->propbit);
+                       }
+                       break;
+       }
+}
+
+
 static const struct hid_device_id lenovo_devices[] = {
        { HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_TPKBD) },
        { HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_CUSBKBD) },
        { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_CBTKBD) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_TPPRODOCK) },
        { }
 };
 
@@ -774,10 +827,12 @@ MODULE_DEVICE_TABLE(hid, lenovo_devices);
 static struct hid_driver lenovo_driver = {
        .name = "lenovo",
        .id_table = lenovo_devices,
+       .input_configured = lenovo_input_configured,
        .input_mapping = lenovo_input_mapping,
        .probe = lenovo_probe,
        .remove = lenovo_remove,
        .raw_event = lenovo_raw_event,
+       .report_fixup = lenovo_report_fixup,
 };
 module_hid_driver(lenovo_driver);
 
index b86c18e651ed361bedb2c1584f78c5b494df2ae7..429340d809b5546341d09fc6a3bea6fedb525497 100644 (file)
@@ -700,7 +700,8 @@ static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id)
                        /* insert a little delay of 10 jiffies ~ 40ms */
                        wait_queue_head_t wait;
                        init_waitqueue_head (&wait);
-                       wait_event_interruptible_timeout(wait, 0, 10);
+                       wait_event_interruptible_timeout(wait, 0,
+                                                        msecs_to_jiffies(40));
 
                        /* Select random Address */
                        buf[1] = 0xB2;
@@ -712,13 +713,16 @@ static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id)
        }
 
        if (drv_data->quirks & LG_FF)
-               lgff_init(hdev);
-       if (drv_data->quirks & LG_FF2)
-               lg2ff_init(hdev);
-       if (drv_data->quirks & LG_FF3)
-               lg3ff_init(hdev);
-       if (drv_data->quirks & LG_FF4)
-               lg4ff_init(hdev);
+               ret = lgff_init(hdev);
+       else if (drv_data->quirks & LG_FF2)
+               ret = lg2ff_init(hdev);
+       else if (drv_data->quirks & LG_FF3)
+               ret = lg3ff_init(hdev);
+       else if (drv_data->quirks & LG_FF4)
+               ret = lg4ff_init(hdev);
+
+       if (ret)
+               goto err_free;
 
        return 0;
 err_free:
@@ -731,8 +735,8 @@ static void lg_remove(struct hid_device *hdev)
        struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
        if (drv_data->quirks & LG_FF4)
                lg4ff_deinit(hdev);
-
-       hid_hw_stop(hdev);
+       else
+               hid_hw_stop(hdev);
        kfree(drv_data);
 }
 
index 1232210b1cc586f80703479eec634e595addbd9d..02cec83caac33f369ef3e46ca3421210144dad84 100644 (file)
 #define LG4FF_FFEX_REV_MAJ 0x21
 #define LG4FF_FFEX_REV_MIN 0x00
 
-static void hid_lg4ff_set_range_dfp(struct hid_device *hid, u16 range);
-static void hid_lg4ff_set_range_g25(struct hid_device *hid, u16 range);
-
-struct lg4ff_device_entry {
-       __u32 product_id;
-       __u16 range;
-       __u16 min_range;
-       __u16 max_range;
+static void lg4ff_set_range_dfp(struct hid_device *hid, u16 range);
+static void lg4ff_set_range_g25(struct hid_device *hid, u16 range);
+
+struct lg4ff_wheel_data {
+       const u32 product_id;
+       u16 range;
+       const u16 min_range;
+       const u16 max_range;
 #ifdef CONFIG_LEDS_CLASS
-       __u8  led_state;
+       u8  led_state;
        struct led_classdev *led[5];
 #endif
-       u32 alternate_modes;
-       const char *real_tag;
-       const char *real_name;
-       u16 real_product_id;
-       struct list_head list;
+       const u32 alternate_modes;
+       const char * const real_tag;
+       const char * const real_name;
+       const u16 real_product_id;
+
        void (*set_range)(struct hid_device *hid, u16 range);
 };
 
+struct lg4ff_device_entry {
+       spinlock_t report_lock; /* Protect output HID report */
+       struct hid_report *report;
+       struct lg4ff_wheel_data wdata;
+};
+
 static const signed short lg4ff_wheel_effects[] = {
        FF_CONSTANT,
        FF_AUTOCENTER,
@@ -95,16 +101,16 @@ static const signed short lg4ff_wheel_effects[] = {
 };
 
 struct lg4ff_wheel {
-       const __u32 product_id;
+       const u32 product_id;
        const signed short *ff_effects;
-       const __u16 min_range;
-       const __u16 max_range;
+       const u16 min_range;
+       const u16 max_range;
        void (*set_range)(struct hid_device *hid, u16 range);
 };
 
 struct lg4ff_compat_mode_switch {
-       const __u8 cmd_count;   /* Number of commands to send */
-       const __u8 cmd[];
+       const u8 cmd_count;     /* Number of commands to send */
+       const u8 cmd[];
 };
 
 struct lg4ff_wheel_ident_info {
@@ -134,10 +140,10 @@ struct lg4ff_alternate_mode {
 static const struct lg4ff_wheel lg4ff_devices[] = {
        {USB_DEVICE_ID_LOGITECH_WHEEL,       lg4ff_wheel_effects, 40, 270, NULL},
        {USB_DEVICE_ID_LOGITECH_MOMO_WHEEL,  lg4ff_wheel_effects, 40, 270, NULL},
-       {USB_DEVICE_ID_LOGITECH_DFP_WHEEL,   lg4ff_wheel_effects, 40, 900, hid_lg4ff_set_range_dfp},
-       {USB_DEVICE_ID_LOGITECH_G25_WHEEL,   lg4ff_wheel_effects, 40, 900, hid_lg4ff_set_range_g25},
-       {USB_DEVICE_ID_LOGITECH_DFGT_WHEEL,  lg4ff_wheel_effects, 40, 900, hid_lg4ff_set_range_g25},
-       {USB_DEVICE_ID_LOGITECH_G27_WHEEL,   lg4ff_wheel_effects, 40, 900, hid_lg4ff_set_range_g25},
+       {USB_DEVICE_ID_LOGITECH_DFP_WHEEL,   lg4ff_wheel_effects, 40, 900, lg4ff_set_range_dfp},
+       {USB_DEVICE_ID_LOGITECH_G25_WHEEL,   lg4ff_wheel_effects, 40, 900, lg4ff_set_range_g25},
+       {USB_DEVICE_ID_LOGITECH_DFGT_WHEEL,  lg4ff_wheel_effects, 40, 900, lg4ff_set_range_g25},
+       {USB_DEVICE_ID_LOGITECH_G27_WHEEL,   lg4ff_wheel_effects, 40, 900, lg4ff_set_range_g25},
        {USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2, lg4ff_wheel_effects, 40, 270, NULL},
        {USB_DEVICE_ID_LOGITECH_WII_WHEEL,   lg4ff_wheel_effects, 40, 270, NULL}
 };
@@ -245,10 +251,10 @@ static const struct lg4ff_compat_mode_switch lg4ff_mode_switch_ext16_g25 = {
 };
 
 /* Recalculates X axis value accordingly to currently selected range */
-static __s32 lg4ff_adjust_dfp_x_axis(__s32 value, __u16 range)
+static s32 lg4ff_adjust_dfp_x_axis(s32 value, u16 range)
 {
-       __u16 max_range;
-       __s32 new_value;
+       u16 max_range;
+       s32 new_value;
 
        if (range == 900)
                return value;
@@ -269,21 +275,21 @@ static __s32 lg4ff_adjust_dfp_x_axis(__s32 value, __u16 range)
 }
 
 int lg4ff_adjust_input_event(struct hid_device *hid, struct hid_field *field,
-                            struct hid_usage *usage, __s32 value, struct lg_drv_data *drv_data)
+                            struct hid_usage *usage, s32 value, struct lg_drv_data *drv_data)
 {
        struct lg4ff_device_entry *entry = drv_data->device_props;
-       __s32 new_value = 0;
+       s32 new_value = 0;
 
        if (!entry) {
                hid_err(hid, "Device properties not found");
                return 0;
        }
 
-       switch (entry->product_id) {
+       switch (entry->wdata.product_id) {
        case USB_DEVICE_ID_LOGITECH_DFP_WHEEL:
                switch (usage->code) {
                case ABS_X:
-                       new_value = lg4ff_adjust_dfp_x_axis(value, entry->range);
+                       new_value = lg4ff_adjust_dfp_x_axis(value, entry->wdata.range);
                        input_event(field->hidinput->input, usage->type, usage->code, new_value);
                        return 1;
                default:
@@ -294,14 +300,56 @@ int lg4ff_adjust_input_event(struct hid_device *hid, struct hid_field *field,
        }
 }
 
-static int hid_lg4ff_play(struct input_dev *dev, void *data, struct ff_effect *effect)
+static void lg4ff_init_wheel_data(struct lg4ff_wheel_data * const wdata, const struct lg4ff_wheel *wheel,
+                                 const struct lg4ff_multimode_wheel *mmode_wheel,
+                                 const u16 real_product_id)
+{
+       u32 alternate_modes = 0;
+       const char *real_tag = NULL;
+       const char *real_name = NULL;
+
+       if (mmode_wheel) {
+               alternate_modes = mmode_wheel->alternate_modes;
+               real_tag = mmode_wheel->real_tag;
+               real_name = mmode_wheel->real_name;
+       }
+
+       {
+               struct lg4ff_wheel_data t_wdata =  { .product_id = wheel->product_id,
+                                                    .real_product_id = real_product_id,
+                                                    .min_range = wheel->min_range,
+                                                    .max_range = wheel->max_range,
+                                                    .set_range = wheel->set_range,
+                                                    .alternate_modes = alternate_modes,
+                                                    .real_tag = real_tag,
+                                                    .real_name = real_name };
+
+               memcpy(wdata, &t_wdata, sizeof(t_wdata));
+       }
+}
+
+static int lg4ff_play(struct input_dev *dev, void *data, struct ff_effect *effect)
 {
        struct hid_device *hid = input_get_drvdata(dev);
-       struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
-       struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
-       __s32 *value = report->field[0]->value;
+       struct lg4ff_device_entry *entry;
+       struct lg_drv_data *drv_data;
+       unsigned long flags;
+       s32 *value;
        int x;
 
+       drv_data = hid_get_drvdata(hid);
+       if (!drv_data) {
+               hid_err(hid, "Private driver data not found!\n");
+               return -EINVAL;
+       }
+
+       entry = drv_data->device_props;
+       if (!entry) {
+               hid_err(hid, "Device properties not found!\n");
+               return -EINVAL;
+       }
+       value = entry->report->field[0]->value;
+
 #define CLAMP(x) do { if (x < 0) x = 0; else if (x > 0xff) x = 0xff; } while (0)
 
        switch (effect->type) {
@@ -309,6 +357,7 @@ static int hid_lg4ff_play(struct input_dev *dev, void *data, struct ff_effect *e
                x = effect->u.ramp.start_level + 0x80;  /* 0x80 is no force */
                CLAMP(x);
 
+               spin_lock_irqsave(&entry->report_lock, flags);
                if (x == 0x80) {
                        /* De-activate force in slot-1*/
                        value[0] = 0x13;
@@ -319,7 +368,8 @@ static int hid_lg4ff_play(struct input_dev *dev, void *data, struct ff_effect *e
                        value[5] = 0x00;
                        value[6] = 0x00;
 
-                       hid_hw_request(hid, report, HID_REQ_SET_REPORT);
+                       hid_hw_request(hid, entry->report, HID_REQ_SET_REPORT);
+                       spin_unlock_irqrestore(&entry->report_lock, flags);
                        return 0;
                }
 
@@ -331,7 +381,8 @@ static int hid_lg4ff_play(struct input_dev *dev, void *data, struct ff_effect *e
                value[5] = 0x00;
                value[6] = 0x00;
 
-               hid_hw_request(hid, report, HID_REQ_SET_REPORT);
+               hid_hw_request(hid, entry->report, HID_REQ_SET_REPORT);
+               spin_unlock_irqrestore(&entry->report_lock, flags);
                break;
        }
        return 0;
@@ -339,15 +390,16 @@ static int hid_lg4ff_play(struct input_dev *dev, void *data, struct ff_effect *e
 
 /* Sends default autocentering command compatible with
  * all wheels except Formula Force EX */
-static void hid_lg4ff_set_autocenter_default(struct input_dev *dev, u16 magnitude)
+static void lg4ff_set_autocenter_default(struct input_dev *dev, u16 magnitude)
 {
        struct hid_device *hid = input_get_drvdata(dev);
        struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
        struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
-       __s32 *value = report->field[0]->value;
-       __u32 expand_a, expand_b;
+       s32 *value = report->field[0]->value;
+       u32 expand_a, expand_b;
        struct lg4ff_device_entry *entry;
        struct lg_drv_data *drv_data;
+       unsigned long flags;
 
        drv_data = hid_get_drvdata(hid);
        if (!drv_data) {
@@ -360,8 +412,10 @@ static void hid_lg4ff_set_autocenter_default(struct input_dev *dev, u16 magnitud
                hid_err(hid, "Device properties not found!\n");
                return;
        }
+       value = entry->report->field[0]->value;
 
        /* De-activate Auto-Center */
+       spin_lock_irqsave(&entry->report_lock, flags);
        if (magnitude == 0) {
                value[0] = 0xf5;
                value[1] = 0x00;
@@ -371,7 +425,8 @@ static void hid_lg4ff_set_autocenter_default(struct input_dev *dev, u16 magnitud
                value[5] = 0x00;
                value[6] = 0x00;
 
-               hid_hw_request(hid, report, HID_REQ_SET_REPORT);
+               hid_hw_request(hid, entry->report, HID_REQ_SET_REPORT);
+               spin_unlock_irqrestore(&entry->report_lock, flags);
                return;
        }
 
@@ -384,7 +439,7 @@ static void hid_lg4ff_set_autocenter_default(struct input_dev *dev, u16 magnitud
        }
 
        /* Adjust for non-MOMO wheels */
-       switch (entry->product_id) {
+       switch (entry->wdata.product_id) {
        case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL:
        case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2:
                break;
@@ -401,7 +456,7 @@ static void hid_lg4ff_set_autocenter_default(struct input_dev *dev, u16 magnitud
        value[5] = 0x00;
        value[6] = 0x00;
 
-       hid_hw_request(hid, report, HID_REQ_SET_REPORT);
+       hid_hw_request(hid, entry->report, HID_REQ_SET_REPORT);
 
        /* Activate Auto-Center */
        value[0] = 0x14;
@@ -412,18 +467,34 @@ static void hid_lg4ff_set_autocenter_default(struct input_dev *dev, u16 magnitud
        value[5] = 0x00;
        value[6] = 0x00;
 
-       hid_hw_request(hid, report, HID_REQ_SET_REPORT);
+       hid_hw_request(hid, entry->report, HID_REQ_SET_REPORT);
+       spin_unlock_irqrestore(&entry->report_lock, flags);
 }
 
 /* Sends autocentering command compatible with Formula Force EX */
-static void hid_lg4ff_set_autocenter_ffex(struct input_dev *dev, u16 magnitude)
+static void lg4ff_set_autocenter_ffex(struct input_dev *dev, u16 magnitude)
 {
        struct hid_device *hid = input_get_drvdata(dev);
-       struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
-       struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
-       __s32 *value = report->field[0]->value;
+       struct lg4ff_device_entry *entry;
+       struct lg_drv_data *drv_data;
+       unsigned long flags;
+       s32 *value;
        magnitude = magnitude * 90 / 65535;
 
+       drv_data = hid_get_drvdata(hid);
+       if (!drv_data) {
+               hid_err(hid, "Private driver data not found!\n");
+               return;
+       }
+
+       entry = drv_data->device_props;
+       if (!entry) {
+               hid_err(hid, "Device properties not found!\n");
+               return;
+       }
+       value = entry->report->field[0]->value;
+
+       spin_lock_irqsave(&entry->report_lock, flags);
        value[0] = 0xfe;
        value[1] = 0x03;
        value[2] = magnitude >> 14;
@@ -432,18 +503,33 @@ static void hid_lg4ff_set_autocenter_ffex(struct input_dev *dev, u16 magnitude)
        value[5] = 0x00;
        value[6] = 0x00;
 
-       hid_hw_request(hid, report, HID_REQ_SET_REPORT);
+       hid_hw_request(hid, entry->report, HID_REQ_SET_REPORT);
+       spin_unlock_irqrestore(&entry->report_lock, flags);
 }
 
 /* Sends command to set range compatible with G25/G27/Driving Force GT */
-static void hid_lg4ff_set_range_g25(struct hid_device *hid, u16 range)
+static void lg4ff_set_range_g25(struct hid_device *hid, u16 range)
 {
-       struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
-       struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
-       __s32 *value = report->field[0]->value;
+       struct lg4ff_device_entry *entry;
+       struct lg_drv_data *drv_data;
+       unsigned long flags;
+       s32 *value;
 
+       drv_data = hid_get_drvdata(hid);
+       if (!drv_data) {
+               hid_err(hid, "Private driver data not found!\n");
+               return;
+       }
+
+       entry = drv_data->device_props;
+       if (!entry) {
+               hid_err(hid, "Device properties not found!\n");
+               return;
+       }
+       value = entry->report->field[0]->value;
        dbg_hid("G25/G27/DFGT: setting range to %u\n", range);
 
+       spin_lock_irqsave(&entry->report_lock, flags);
        value[0] = 0xf8;
        value[1] = 0x81;
        value[2] = range & 0x00ff;
@@ -452,20 +538,35 @@ static void hid_lg4ff_set_range_g25(struct hid_device *hid, u16 range)
        value[5] = 0x00;
        value[6] = 0x00;
 
-       hid_hw_request(hid, report, HID_REQ_SET_REPORT);
+       hid_hw_request(hid, entry->report, HID_REQ_SET_REPORT);
+       spin_unlock_irqrestore(&entry->report_lock, flags);
 }
 
 /* Sends commands to set range compatible with Driving Force Pro wheel */
-static void hid_lg4ff_set_range_dfp(struct hid_device *hid, __u16 range)
+static void lg4ff_set_range_dfp(struct hid_device *hid, u16 range)
 {
-       struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
-       struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
+       struct lg4ff_device_entry *entry;
+       struct lg_drv_data *drv_data;
+       unsigned long flags;
        int start_left, start_right, full_range;
-       __s32 *value = report->field[0]->value;
+       s32 *value;
+
+       drv_data = hid_get_drvdata(hid);
+       if (!drv_data) {
+               hid_err(hid, "Private driver data not found!\n");
+               return;
+       }
 
+       entry = drv_data->device_props;
+       if (!entry) {
+               hid_err(hid, "Device properties not found!\n");
+               return;
+       }
+       value = entry->report->field[0]->value;
        dbg_hid("Driving Force Pro: setting range to %u\n", range);
 
        /* Prepare "coarse" limit command */
+       spin_lock_irqsave(&entry->report_lock, flags);
        value[0] = 0xf8;
        value[1] = 0x00;        /* Set later */
        value[2] = 0x00;
@@ -475,13 +576,13 @@ static void hid_lg4ff_set_range_dfp(struct hid_device *hid, __u16 range)
        value[6] = 0x00;
 
        if (range > 200) {
-               report->field[0]->value[1] = 0x03;
+               value[1] = 0x03;
                full_range = 900;
        } else {
-               report->field[0]->value[1] = 0x02;
+               value[1] = 0x02;
                full_range = 200;
        }
-       hid_hw_request(hid, report, HID_REQ_SET_REPORT);
+       hid_hw_request(hid, entry->report, HID_REQ_SET_REPORT);
 
        /* Prepare "fine" limit command */
        value[0] = 0x81;
@@ -493,7 +594,8 @@ static void hid_lg4ff_set_range_dfp(struct hid_device *hid, __u16 range)
        value[6] = 0x00;
 
        if (range == 200 || range == 900) {     /* Do not apply any fine limit */
-               hid_hw_request(hid, report, HID_REQ_SET_REPORT);
+               hid_hw_request(hid, entry->report, HID_REQ_SET_REPORT);
+               spin_unlock_irqrestore(&entry->report_lock, flags);
                return;
        }
 
@@ -507,7 +609,8 @@ static void hid_lg4ff_set_range_dfp(struct hid_device *hid, __u16 range)
        value[5] = (start_right & 0xe) << 4 | (start_left & 0xe);
        value[6] = 0xff;
 
-       hid_hw_request(hid, report, HID_REQ_SET_REPORT);
+       hid_hw_request(hid, entry->report, HID_REQ_SET_REPORT);
+       spin_unlock_irqrestore(&entry->report_lock, flags);
 }
 
 static const struct lg4ff_compat_mode_switch *lg4ff_get_mode_switch_command(const u16 real_product_id, const u16 target_product_id)
@@ -569,19 +672,35 @@ static const struct lg4ff_compat_mode_switch *lg4ff_get_mode_switch_command(cons
 
 static int lg4ff_switch_compatibility_mode(struct hid_device *hid, const struct lg4ff_compat_mode_switch *s)
 {
-       struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
-       struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
-       __s32 *value = report->field[0]->value;
+       struct lg4ff_device_entry *entry;
+       struct lg_drv_data *drv_data;
+       unsigned long flags;
+       s32 *value;
        u8 i;
 
+       drv_data = hid_get_drvdata(hid);
+       if (!drv_data) {
+               hid_err(hid, "Private driver data not found!\n");
+               return -EINVAL;
+       }
+
+       entry = drv_data->device_props;
+       if (!entry) {
+               hid_err(hid, "Device properties not found!\n");
+               return -EINVAL;
+       }
+       value = entry->report->field[0]->value;
+
+       spin_lock_irqsave(&entry->report_lock, flags);
        for (i = 0; i < s->cmd_count; i++) {
                u8 j;
 
                for (j = 0; j < 7; j++)
                        value[j] = s->cmd[j + (7*i)];
 
-               hid_hw_request(hid, report, HID_REQ_SET_REPORT);
+               hid_hw_request(hid, entry->report, HID_REQ_SET_REPORT);
        }
+       spin_unlock_irqrestore(&entry->report_lock, flags);
        hid_hw_wait(hid);
        return 0;
 }
@@ -606,23 +725,23 @@ static ssize_t lg4ff_alternate_modes_show(struct device *dev, struct device_attr
                return 0;
        }
 
-       if (!entry->real_name) {
+       if (!entry->wdata.real_name) {
                hid_err(hid, "NULL pointer to string\n");
                return 0;
        }
 
        for (i = 0; i < LG4FF_MODE_MAX_IDX; i++) {
-               if (entry->alternate_modes & BIT(i)) {
+               if (entry->wdata.alternate_modes & BIT(i)) {
                        /* Print tag and full name */
                        count += scnprintf(buf + count, PAGE_SIZE - count, "%s: %s",
                                           lg4ff_alternate_modes[i].tag,
-                                          !lg4ff_alternate_modes[i].product_id ? entry->real_name : lg4ff_alternate_modes[i].name);
+                                          !lg4ff_alternate_modes[i].product_id ? entry->wdata.real_name : lg4ff_alternate_modes[i].name);
                        if (count >= PAGE_SIZE - 1)
                                return count;
 
                        /* Mark the currently active mode with an asterisk */
-                       if (lg4ff_alternate_modes[i].product_id == entry->product_id ||
-                           (lg4ff_alternate_modes[i].product_id == 0 && entry->product_id == entry->real_product_id))
+                       if (lg4ff_alternate_modes[i].product_id == entry->wdata.product_id ||
+                           (lg4ff_alternate_modes[i].product_id == 0 && entry->wdata.product_id == entry->wdata.real_product_id))
                                count += scnprintf(buf + count, PAGE_SIZE - count, " *\n");
                        else
                                count += scnprintf(buf + count, PAGE_SIZE - count, "\n");
@@ -675,10 +794,10 @@ static ssize_t lg4ff_alternate_modes_store(struct device *dev, struct device_att
                const u16 mode_product_id = lg4ff_alternate_modes[i].product_id;
                const char *tag = lg4ff_alternate_modes[i].tag;
 
-               if (entry->alternate_modes & BIT(i)) {
+               if (entry->wdata.alternate_modes & BIT(i)) {
                        if (!strcmp(tag, lbuf)) {
                                if (!mode_product_id)
-                                       target_product_id = entry->real_product_id;
+                                       target_product_id = entry->wdata.real_product_id;
                                else
                                        target_product_id = mode_product_id;
                                break;
@@ -693,24 +812,24 @@ static ssize_t lg4ff_alternate_modes_store(struct device *dev, struct device_att
        }
        kfree(lbuf); /* Not needed anymore */
 
-       if (target_product_id == entry->product_id) /* Nothing to do */
+       if (target_product_id == entry->wdata.product_id) /* Nothing to do */
                return count;
 
        /* Automatic switching has to be disabled for the switch to DF-EX mode to work correctly */
        if (target_product_id == USB_DEVICE_ID_LOGITECH_WHEEL && !lg4ff_no_autoswitch) {
                hid_info(hid, "\"%s\" cannot be switched to \"DF-EX\" mode. Load the \"hid_logitech\" module with \"lg4ff_no_autoswitch=1\" parameter set and try again\n",
-                        entry->real_name);
+                        entry->wdata.real_name);
                return -EINVAL;
        }
 
        /* Take care of hardware limitations */
-       if ((entry->real_product_id == USB_DEVICE_ID_LOGITECH_DFP_WHEEL || entry->real_product_id == USB_DEVICE_ID_LOGITECH_G25_WHEEL) &&
-           entry->product_id > target_product_id) {
-               hid_info(hid, "\"%s\" cannot be switched back into \"%s\" mode\n", entry->real_name, lg4ff_alternate_modes[i].name);
+       if ((entry->wdata.real_product_id == USB_DEVICE_ID_LOGITECH_DFP_WHEEL || entry->wdata.real_product_id == USB_DEVICE_ID_LOGITECH_G25_WHEEL) &&
+           entry->wdata.product_id > target_product_id) {
+               hid_info(hid, "\"%s\" cannot be switched back into \"%s\" mode\n", entry->wdata.real_name, lg4ff_alternate_modes[i].name);
                return -EINVAL;
        }
 
-       s = lg4ff_get_mode_switch_command(entry->real_product_id, target_product_id);
+       s = lg4ff_get_mode_switch_command(entry->wdata.real_product_id, target_product_id);
        if (!s) {
                hid_err(hid, "Invalid target product ID %X\n", target_product_id);
                return -EINVAL;
@@ -721,9 +840,9 @@ static ssize_t lg4ff_alternate_modes_store(struct device *dev, struct device_att
 }
 static DEVICE_ATTR(alternate_modes, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH, lg4ff_alternate_modes_show, lg4ff_alternate_modes_store);
 
-/* Read current range and display it in terminal */
-static ssize_t range_show(struct device *dev, struct device_attribute *attr,
-                         char *buf)
+/* Export the currently set range of the wheel */
+static ssize_t lg4ff_range_show(struct device *dev, struct device_attribute *attr,
+                               char *buf)
 {
        struct hid_device *hid = to_hid_device(dev);
        struct lg4ff_device_entry *entry;
@@ -742,19 +861,19 @@ static ssize_t range_show(struct device *dev, struct device_attribute *attr,
                return 0;
        }
 
-       count = scnprintf(buf, PAGE_SIZE, "%u\n", entry->range);
+       count = scnprintf(buf, PAGE_SIZE, "%u\n", entry->wdata.range);
        return count;
 }
 
 /* Set range to user specified value, call appropriate function
  * according to the type of the wheel */
-static ssize_t range_store(struct device *dev, struct device_attribute *attr,
-                          const char *buf, size_t count)
+static ssize_t lg4ff_range_store(struct device *dev, struct device_attribute *attr,
+                                const char *buf, size_t count)
 {
        struct hid_device *hid = to_hid_device(dev);
        struct lg4ff_device_entry *entry;
        struct lg_drv_data *drv_data;
-       __u16 range = simple_strtoul(buf, NULL, 10);
+       u16 range = simple_strtoul(buf, NULL, 10);
 
        drv_data = hid_get_drvdata(hid);
        if (!drv_data) {
@@ -769,18 +888,18 @@ static ssize_t range_store(struct device *dev, struct device_attribute *attr,
        }
 
        if (range == 0)
-               range = entry->max_range;
+               range = entry->wdata.max_range;
 
        /* Check if the wheel supports range setting
         * and that the range is within limits for the wheel */
-       if (entry->set_range != NULL && range >= entry->min_range && range <= entry->max_range) {
-               entry->set_range(hid, range);
-               entry->range = range;
+       if (entry->wdata.set_range && range >= entry->wdata.min_range && range <= entry->wdata.max_range) {
+               entry->wdata.set_range(hid, range);
+               entry->wdata.range = range;
        }
 
        return count;
 }
-static DEVICE_ATTR_RW(range);
+static DEVICE_ATTR(range, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH, lg4ff_range_show, lg4ff_range_store);
 
 static ssize_t lg4ff_real_id_show(struct device *dev, struct device_attribute *attr, char *buf)
 {
@@ -801,12 +920,12 @@ static ssize_t lg4ff_real_id_show(struct device *dev, struct device_attribute *a
                return 0;
        }
 
-       if (!entry->real_tag || !entry->real_name) {
+       if (!entry->wdata.real_tag || !entry->wdata.real_name) {
                hid_err(hid, "NULL pointer to string\n");
                return 0;
        }
 
-       count = scnprintf(buf, PAGE_SIZE, "%s: %s\n", entry->real_tag, entry->real_name);
+       count = scnprintf(buf, PAGE_SIZE, "%s: %s\n", entry->wdata.real_tag, entry->wdata.real_name);
        return count;
 }
 
@@ -818,12 +937,27 @@ static ssize_t lg4ff_real_id_store(struct device *dev, struct device_attribute *
 static DEVICE_ATTR(real_id, S_IRUGO, lg4ff_real_id_show, lg4ff_real_id_store);
 
 #ifdef CONFIG_LEDS_CLASS
-static void lg4ff_set_leds(struct hid_device *hid, __u8 leds)
+static void lg4ff_set_leds(struct hid_device *hid, u8 leds)
 {
-       struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
-       struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
-       __s32 *value = report->field[0]->value;
+       struct lg_drv_data *drv_data;
+       struct lg4ff_device_entry *entry;
+       unsigned long flags;
+       s32 *value;
+
+       drv_data = hid_get_drvdata(hid);
+       if (!drv_data) {
+               hid_err(hid, "Private driver data not found!\n");
+               return;
+       }
+
+       entry = drv_data->device_props;
+       if (!entry) {
+               hid_err(hid, "Device properties not found!\n");
+               return;
+       }
+       value = entry->report->field[0]->value;
 
+       spin_lock_irqsave(&entry->report_lock, flags);
        value[0] = 0xf8;
        value[1] = 0x12;
        value[2] = leds;
@@ -831,7 +965,8 @@ static void lg4ff_set_leds(struct hid_device *hid, __u8 leds)
        value[4] = 0x00;
        value[5] = 0x00;
        value[6] = 0x00;
-       hid_hw_request(hid, report, HID_REQ_SET_REPORT);
+       hid_hw_request(hid, entry->report, HID_REQ_SET_REPORT);
+       spin_unlock_irqrestore(&entry->report_lock, flags);
 }
 
 static void lg4ff_led_set_brightness(struct led_classdev *led_cdev,
@@ -848,7 +983,7 @@ static void lg4ff_led_set_brightness(struct led_classdev *led_cdev,
                return;
        }
 
-       entry = (struct lg4ff_device_entry *)drv_data->device_props;
+       entry = drv_data->device_props;
 
        if (!entry) {
                hid_err(hid, "Device properties not found.");
@@ -856,15 +991,15 @@ static void lg4ff_led_set_brightness(struct led_classdev *led_cdev,
        }
 
        for (i = 0; i < 5; i++) {
-               if (led_cdev != entry->led[i])
+               if (led_cdev != entry->wdata.led[i])
                        continue;
-               state = (entry->led_state >> i) & 1;
+               state = (entry->wdata.led_state >> i) & 1;
                if (value == LED_OFF && state) {
-                       entry->led_state &= ~(1 << i);
-                       lg4ff_set_leds(hid, entry->led_state);
+                       entry->wdata.led_state &= ~(1 << i);
+                       lg4ff_set_leds(hid, entry->wdata.led_state);
                } else if (value != LED_OFF && !state) {
-                       entry->led_state |= 1 << i;
-                       lg4ff_set_leds(hid, entry->led_state);
+                       entry->wdata.led_state |= 1 << i;
+                       lg4ff_set_leds(hid, entry->wdata.led_state);
                }
                break;
        }
@@ -883,7 +1018,7 @@ static enum led_brightness lg4ff_led_get_brightness(struct led_classdev *led_cde
                return LED_OFF;
        }
 
-       entry = (struct lg4ff_device_entry *)drv_data->device_props;
+       entry = drv_data->device_props;
 
        if (!entry) {
                hid_err(hid, "Device properties not found.");
@@ -891,8 +1026,8 @@ static enum led_brightness lg4ff_led_get_brightness(struct led_classdev *led_cde
        }
 
        for (i = 0; i < 5; i++)
-               if (led_cdev == entry->led[i]) {
-                       value = (entry->led_state >> i) & 1;
+               if (led_cdev == entry->wdata.led[i]) {
+                       value = (entry->wdata.led_state >> i) & 1;
                        break;
                }
 
@@ -991,8 +1126,11 @@ int lg4ff_init(struct hid_device *hid)
 {
        struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list);
        struct input_dev *dev = hidinput->input;
+       struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
+       struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
        const struct usb_device_descriptor *udesc = &(hid_to_usb_dev(hid)->descriptor);
        const u16 bcdDevice = le16_to_cpu(udesc->bcdDevice);
+       const struct lg4ff_multimode_wheel *mmode_wheel = NULL;
        struct lg4ff_device_entry *entry;
        struct lg_drv_data *drv_data;
        int error, i, j;
@@ -1003,6 +1141,18 @@ int lg4ff_init(struct hid_device *hid)
        if (!hid_validate_values(hid, HID_OUTPUT_REPORT, 0, 0, 7))
                return -1;
 
+       drv_data = hid_get_drvdata(hid);
+       if (!drv_data) {
+               hid_err(hid, "Cannot add device, private driver data not allocated\n");
+               return -1;
+       }
+       entry = kzalloc(sizeof(*entry), GFP_KERNEL);
+       if (!entry)
+               return -ENOMEM;
+       spin_lock_init(&entry->report_lock);
+       entry->report = report;
+       drv_data->device_props = entry;
+
        /* Check if a multimode wheel has been connected and
         * handle it appropriately */
        mmode_ret = lg4ff_handle_multimode_wheel(hid, &real_product_id, bcdDevice);
@@ -1012,6 +1162,11 @@ int lg4ff_init(struct hid_device *hid)
         */
        if (mmode_ret == LG4FF_MMODE_SWITCHED)
                return 0;
+       else if (mmode_ret < 0) {
+               hid_err(hid, "Unable to switch device mode during initialization, errno %d\n", mmode_ret);
+               error = mmode_ret;
+               goto err_init;
+       }
 
        /* Check what wheel has been connected */
        for (i = 0; i < ARRAY_SIZE(lg4ff_devices); i++) {
@@ -1022,9 +1177,11 @@ int lg4ff_init(struct hid_device *hid)
        }
 
        if (i == ARRAY_SIZE(lg4ff_devices)) {
-               hid_err(hid, "Device is not supported by lg4ff driver. If you think it should be, consider reporting a bug to"
-                            "LKML, Simon Wood <simon@mungewell.org> or Michal Maly <madcatxster@gmail.com>\n");
-               return -1;
+               hid_err(hid, "This device is flagged to be handled by the lg4ff module but this module does not know how to handle it. "
+                            "Please report this as a bug to LKML, Simon Wood <simon@mungewell.org> or "
+                            "Michal Maly <madcatxster@devoid-pointer.net>\n");
+               error = -1;
+               goto err_init;
        }
 
        if (mmode_ret == LG4FF_MMODE_IS_MULTIMODE) {
@@ -1035,7 +1192,8 @@ int lg4ff_init(struct hid_device *hid)
 
                if (mmode_idx == ARRAY_SIZE(lg4ff_multimode_wheels)) {
                        hid_err(hid, "Device product ID %X is not listed as a multimode wheel", real_product_id);
-                       return -1;
+                       error = -1;
+                       goto err_init;
                }
        }
 
@@ -1043,37 +1201,17 @@ int lg4ff_init(struct hid_device *hid)
        for (j = 0; lg4ff_devices[i].ff_effects[j] >= 0; j++)
                set_bit(lg4ff_devices[i].ff_effects[j], dev->ffbit);
 
-       error = input_ff_create_memless(dev, NULL, hid_lg4ff_play);
+       error = input_ff_create_memless(dev, NULL, lg4ff_play);
 
        if (error)
-               return error;
-
-       /* Get private driver data */
-       drv_data = hid_get_drvdata(hid);
-       if (!drv_data) {
-               hid_err(hid, "Cannot add device, private driver data not allocated\n");
-               return -1;
-       }
+               goto err_init;
 
        /* Initialize device properties */
-       entry = kzalloc(sizeof(struct lg4ff_device_entry), GFP_KERNEL);
-       if (!entry) {
-               hid_err(hid, "Cannot add device, insufficient memory to allocate device properties.\n");
-               return -ENOMEM;
-       }
-       drv_data->device_props = entry;
-
-       entry->product_id = lg4ff_devices[i].product_id;
-       entry->real_product_id = real_product_id;
-       entry->min_range = lg4ff_devices[i].min_range;
-       entry->max_range = lg4ff_devices[i].max_range;
-       entry->set_range = lg4ff_devices[i].set_range;
        if (mmode_ret == LG4FF_MMODE_IS_MULTIMODE) {
                BUG_ON(mmode_idx == -1);
-               entry->alternate_modes = lg4ff_multimode_wheels[mmode_idx].alternate_modes;
-               entry->real_tag = lg4ff_multimode_wheels[mmode_idx].real_tag;
-               entry->real_name = lg4ff_multimode_wheels[mmode_idx].real_name;
+               mmode_wheel = &lg4ff_multimode_wheels[mmode_idx];
        }
+       lg4ff_init_wheel_data(&entry->wdata, &lg4ff_devices[i], mmode_wheel, real_product_id);
 
        /* Check if autocentering is available and
         * set the centering force to zero by default */
@@ -1081,9 +1219,9 @@ int lg4ff_init(struct hid_device *hid)
                /* Formula Force EX expects different autocentering command */
                if ((bcdDevice >> 8) == LG4FF_FFEX_REV_MAJ &&
                    (bcdDevice & 0xff) == LG4FF_FFEX_REV_MIN)
-                       dev->ff->set_autocenter = hid_lg4ff_set_autocenter_ffex;
+                       dev->ff->set_autocenter = lg4ff_set_autocenter_ffex;
                else
-                       dev->ff->set_autocenter = hid_lg4ff_set_autocenter_default;
+                       dev->ff->set_autocenter = lg4ff_set_autocenter_default;
 
                dev->ff->set_autocenter(dev, 0);
        }
@@ -1091,27 +1229,27 @@ int lg4ff_init(struct hid_device *hid)
        /* Create sysfs interface */
        error = device_create_file(&hid->dev, &dev_attr_range);
        if (error)
-               return error;
+               hid_warn(hid, "Unable to create sysfs interface for \"range\", errno %d\n", error);
        if (mmode_ret == LG4FF_MMODE_IS_MULTIMODE) {
                error = device_create_file(&hid->dev, &dev_attr_real_id);
                if (error)
-                       return error;
+                       hid_warn(hid, "Unable to create sysfs interface for \"real_id\", errno %d\n", error);
                error = device_create_file(&hid->dev, &dev_attr_alternate_modes);
                if (error)
-                       return error;
+                       hid_warn(hid, "Unable to create sysfs interface for \"alternate_modes\", errno %d\n", error);
        }
        dbg_hid("sysfs interface created\n");
 
        /* Set the maximum range to start with */
-       entry->range = entry->max_range;
-       if (entry->set_range != NULL)
-               entry->set_range(hid, entry->range);
+       entry->wdata.range = entry->wdata.max_range;
+       if (entry->wdata.set_range)
+               entry->wdata.set_range(hid, entry->wdata.range);
 
 #ifdef CONFIG_LEDS_CLASS
        /* register led subsystem - G27 only */
-       entry->led_state = 0;
+       entry->wdata.led_state = 0;
        for (j = 0; j < 5; j++)
-               entry->led[j] = NULL;
+               entry->wdata.led[j] = NULL;
 
        if (lg4ff_devices[i].product_id == USB_DEVICE_ID_LOGITECH_G27_WHEEL) {
                struct led_classdev *led;
@@ -1126,7 +1264,7 @@ int lg4ff_init(struct hid_device *hid)
                        led = kzalloc(sizeof(struct led_classdev)+name_sz, GFP_KERNEL);
                        if (!led) {
                                hid_err(hid, "can't allocate memory for LED %d\n", j);
-                               goto err;
+                               goto err_leds;
                        }
 
                        name = (void *)(&led[1]);
@@ -1137,16 +1275,16 @@ int lg4ff_init(struct hid_device *hid)
                        led->brightness_get = lg4ff_led_get_brightness;
                        led->brightness_set = lg4ff_led_set_brightness;
 
-                       entry->led[j] = led;
+                       entry->wdata.led[j] = led;
                        error = led_classdev_register(&hid->dev, led);
 
                        if (error) {
                                hid_err(hid, "failed to register LED %d. Aborting.\n", j);
-err:
+err_leds:
                                /* Deregister LEDs (if any) */
                                for (j = 0; j < 5; j++) {
-                                       led = entry->led[j];
-                                       entry->led[j] = NULL;
+                                       led = entry->wdata.led[j];
+                                       entry->wdata.led[j] = NULL;
                                        if (!led)
                                                continue;
                                        led_classdev_unregister(led);
@@ -1160,6 +1298,11 @@ out:
 #endif
        hid_info(hid, "Force feedback support for Logitech Gaming Wheels\n");
        return 0;
+
+err_init:
+       drv_data->device_props = NULL;
+       kfree(entry);
+       return error;
 }
 
 int lg4ff_deinit(struct hid_device *hid)
@@ -1176,14 +1319,13 @@ int lg4ff_deinit(struct hid_device *hid)
        if (!entry)
                goto out; /* Nothing more to do */
 
-       device_remove_file(&hid->dev, &dev_attr_range);
-
        /* Multimode devices will have at least the "MODE_NATIVE" bit set */
-       if (entry->alternate_modes) {
+       if (entry->wdata.alternate_modes) {
                device_remove_file(&hid->dev, &dev_attr_real_id);
                device_remove_file(&hid->dev, &dev_attr_alternate_modes);
        }
 
+       device_remove_file(&hid->dev, &dev_attr_range);
 #ifdef CONFIG_LEDS_CLASS
        {
                int j;
@@ -1192,8 +1334,8 @@ int lg4ff_deinit(struct hid_device *hid)
                /* Deregister LEDs (if any) */
                for (j = 0; j < 5; j++) {
 
-                       led = entry->led[j];
-                       entry->led[j] = NULL;
+                       led = entry->wdata.led[j];
+                       entry->wdata.led[j] = NULL;
                        if (!led)
                                continue;
                        led_classdev_unregister(led);
@@ -1201,10 +1343,10 @@ int lg4ff_deinit(struct hid_device *hid)
                }
        }
 #endif
+       hid_hw_stop(hid);
+       drv_data->device_props = NULL;
 
-       /* Deallocate memory */
        kfree(entry);
-
 out:
        dbg_hid("Device successfully unregistered\n");
        return 0;
index 5b6a5086c47fb82a3a6cbb9738f47c1349763a1e..66201af44da33b6c699983c7e04330fd765c3c7e 100644 (file)
@@ -5,12 +5,12 @@
 extern int lg4ff_no_autoswitch; /* From hid-lg.c */
 
 int lg4ff_adjust_input_event(struct hid_device *hid, struct hid_field *field,
-                            struct hid_usage *usage, __s32 value, struct lg_drv_data *drv_data);
+                            struct hid_usage *usage, s32 value, struct lg_drv_data *drv_data);
 int lg4ff_init(struct hid_device *hdev);
 int lg4ff_deinit(struct hid_device *hdev);
 #else
 static inline int lg4ff_adjust_input_event(struct hid_device *hid, struct hid_field *field,
-                                          struct hid_usage *usage, __s32 value, struct lg_drv_data *drv_data) { return 0; }
+                                          struct hid_usage *usage, s32 value, struct lg_drv_data *drv_data) { return 0; }
 static inline int lg4ff_init(struct hid_device *hdev) { return -1; }
 static inline int lg4ff_deinit(struct hid_device *hdev) { return -1; }
 #endif
index 5fd530acf747c50fcd5bfbd51d19d3271e5d716b..484196459305577c19433fe4fe35b21c2ac39905 100644 (file)
@@ -40,8 +40,9 @@ MODULE_PARM_DESC(disable_raw_mode,
 #define HIDPP_REPORT_LONG_LENGTH               20
 
 #define HIDPP_QUIRK_CLASS_WTP                  BIT(0)
+#define HIDPP_QUIRK_CLASS_M560                 BIT(1)
 
-/* bits 1..20 are reserved for classes */
+/* bits 2..20 are reserved for classes */
 #define HIDPP_QUIRK_DELAYED_INIT               BIT(21)
 #define HIDPP_QUIRK_WTP_PHYSICAL_BUTTONS       BIT(22)
 
@@ -930,6 +931,207 @@ static int wtp_connect(struct hid_device *hdev, bool connected)
                        true, true);
 }
 
+/* ------------------------------------------------------------------------- */
+/* Logitech M560 devices                                                     */
+/* ------------------------------------------------------------------------- */
+
+/*
+ * Logitech M560 protocol overview
+ *
+ * The Logitech M560 mouse, is designed for windows 8. When the middle and/or
+ * the sides buttons are pressed, it sends some keyboard keys events
+ * instead of buttons ones.
+ * To complicate things further, the middle button keys sequence
+ * is different from the odd press and the even press.
+ *
+ * forward button -> Super_R
+ * backward button -> Super_L+'d' (press only)
+ * middle button -> 1st time: Alt_L+SuperL+XF86TouchpadOff (press only)
+ *                  2nd time: left-click (press only)
+ * NB: press-only means that when the button is pressed, the
+ * KeyPress/ButtonPress and KeyRelease/ButtonRelease events are generated
+ * together sequentially; instead when the button is released, no event is
+ * generated !
+ *
+ * With the command
+ *     10<xx>0a 3500af03 (where <xx> is the mouse id),
+ * the mouse reacts differently:
+ * - it never sends a keyboard key event
+ * - for the three mouse button it sends:
+ *     middle button               press   11<xx>0a 3500af00...
+ *     side 1 button (forward)     press   11<xx>0a 3500b000...
+ *     side 2 button (backward)    press   11<xx>0a 3500ae00...
+ *     middle/side1/side2 button   release 11<xx>0a 35000000...
+ */
+
+static const u8 m560_config_parameter[] = {0x00, 0xaf, 0x03};
+
+struct m560_private_data {
+       struct input_dev *input;
+};
+
+/* how buttons are mapped in the report */
+#define M560_MOUSE_BTN_LEFT            0x01
+#define M560_MOUSE_BTN_RIGHT           0x02
+#define M560_MOUSE_BTN_WHEEL_LEFT      0x08
+#define M560_MOUSE_BTN_WHEEL_RIGHT     0x10
+
+#define M560_SUB_ID                    0x0a
+#define M560_BUTTON_MODE_REGISTER      0x35
+
+static int m560_send_config_command(struct hid_device *hdev, bool connected)
+{
+       struct hidpp_report response;
+       struct hidpp_device *hidpp_dev;
+
+       hidpp_dev = hid_get_drvdata(hdev);
+
+       if (!connected)
+               return -ENODEV;
+
+       return hidpp_send_rap_command_sync(
+               hidpp_dev,
+               REPORT_ID_HIDPP_SHORT,
+               M560_SUB_ID,
+               M560_BUTTON_MODE_REGISTER,
+               (u8 *)m560_config_parameter,
+               sizeof(m560_config_parameter),
+               &response
+       );
+}
+
+static int m560_allocate(struct hid_device *hdev)
+{
+       struct hidpp_device *hidpp = hid_get_drvdata(hdev);
+       struct m560_private_data *d;
+
+       d = devm_kzalloc(&hdev->dev, sizeof(struct m560_private_data),
+                       GFP_KERNEL);
+       if (!d)
+               return -ENOMEM;
+
+       hidpp->private_data = d;
+
+       return 0;
+};
+
+static int m560_raw_event(struct hid_device *hdev, u8 *data, int size)
+{
+       struct hidpp_device *hidpp = hid_get_drvdata(hdev);
+       struct m560_private_data *mydata = hidpp->private_data;
+
+       /* sanity check */
+       if (!mydata || !mydata->input) {
+               hid_err(hdev, "error in parameter\n");
+               return -EINVAL;
+       }
+
+       if (size < 7) {
+               hid_err(hdev, "error in report\n");
+               return 0;
+       }
+
+       if (data[0] == REPORT_ID_HIDPP_LONG &&
+           data[2] == M560_SUB_ID && data[6] == 0x00) {
+               /*
+                * m560 mouse report for middle, forward and backward button
+                *
+                * data[0] = 0x11
+                * data[1] = device-id
+                * data[2] = 0x0a
+                * data[5] = 0xaf -> middle
+                *           0xb0 -> forward
+                *           0xae -> backward
+                *           0x00 -> release all
+                * data[6] = 0x00
+                */
+
+               switch (data[5]) {
+               case 0xaf:
+                       input_report_key(mydata->input, BTN_MIDDLE, 1);
+                       break;
+               case 0xb0:
+                       input_report_key(mydata->input, BTN_FORWARD, 1);
+                       break;
+               case 0xae:
+                       input_report_key(mydata->input, BTN_BACK, 1);
+                       break;
+               case 0x00:
+                       input_report_key(mydata->input, BTN_BACK, 0);
+                       input_report_key(mydata->input, BTN_FORWARD, 0);
+                       input_report_key(mydata->input, BTN_MIDDLE, 0);
+                       break;
+               default:
+                       hid_err(hdev, "error in report\n");
+                       return 0;
+               }
+               input_sync(mydata->input);
+
+       } else if (data[0] == 0x02) {
+               /*
+                * Logitech M560 mouse report
+                *
+                * data[0] = type (0x02)
+                * data[1..2] = buttons
+                * data[3..5] = xy
+                * data[6] = wheel
+                */
+
+               int v;
+
+               input_report_key(mydata->input, BTN_LEFT,
+                       !!(data[1] & M560_MOUSE_BTN_LEFT));
+               input_report_key(mydata->input, BTN_RIGHT,
+                       !!(data[1] & M560_MOUSE_BTN_RIGHT));
+
+               if (data[1] & M560_MOUSE_BTN_WHEEL_LEFT)
+                       input_report_rel(mydata->input, REL_HWHEEL, -1);
+               else if (data[1] & M560_MOUSE_BTN_WHEEL_RIGHT)
+                       input_report_rel(mydata->input, REL_HWHEEL, 1);
+
+               v = hid_snto32(hid_field_extract(hdev, data+3, 0, 12), 12);
+               input_report_rel(mydata->input, REL_X, v);
+
+               v = hid_snto32(hid_field_extract(hdev, data+3, 12, 12), 12);
+               input_report_rel(mydata->input, REL_Y, v);
+
+               v = hid_snto32(data[6], 8);
+               input_report_rel(mydata->input, REL_WHEEL, v);
+
+               input_sync(mydata->input);
+       }
+
+       return 1;
+}
+
+static void m560_populate_input(struct hidpp_device *hidpp,
+               struct input_dev *input_dev, bool origin_is_hid_core)
+{
+       struct m560_private_data *mydata = hidpp->private_data;
+
+       mydata->input = input_dev;
+
+       __set_bit(EV_KEY, mydata->input->evbit);
+       __set_bit(BTN_MIDDLE, mydata->input->keybit);
+       __set_bit(BTN_RIGHT, mydata->input->keybit);
+       __set_bit(BTN_LEFT, mydata->input->keybit);
+       __set_bit(BTN_BACK, mydata->input->keybit);
+       __set_bit(BTN_FORWARD, mydata->input->keybit);
+
+       __set_bit(EV_REL, mydata->input->evbit);
+       __set_bit(REL_X, mydata->input->relbit);
+       __set_bit(REL_Y, mydata->input->relbit);
+       __set_bit(REL_WHEEL, mydata->input->relbit);
+       __set_bit(REL_HWHEEL, mydata->input->relbit);
+}
+
+static int m560_input_mapping(struct hid_device *hdev, struct hid_input *hi,
+               struct hid_field *field, struct hid_usage *usage,
+               unsigned long **bit, int *max)
+{
+       return -1;
+}
+
 /* -------------------------------------------------------------------------- */
 /* Generic HID++ devices                                                      */
 /* -------------------------------------------------------------------------- */
@@ -942,6 +1144,9 @@ static int hidpp_input_mapping(struct hid_device *hdev, struct hid_input *hi,
 
        if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP)
                return wtp_input_mapping(hdev, hi, field, usage, bit, max);
+       else if (hidpp->quirks & HIDPP_QUIRK_CLASS_M560 &&
+                       field->application != HID_GD_MOUSE)
+               return m560_input_mapping(hdev, hi, field, usage, bit, max);
 
        return 0;
 }
@@ -951,6 +1156,8 @@ static void hidpp_populate_input(struct hidpp_device *hidpp,
 {
        if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP)
                wtp_populate_input(hidpp, input, origin_is_hid_core);
+       else if (hidpp->quirks & HIDPP_QUIRK_CLASS_M560)
+               m560_populate_input(hidpp, input, origin_is_hid_core);
 }
 
 static void hidpp_input_configured(struct hid_device *hdev,
@@ -1038,6 +1245,8 @@ static int hidpp_raw_event(struct hid_device *hdev, struct hid_report *report,
 
        if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP)
                return wtp_raw_event(hdev, data, size);
+       else if (hidpp->quirks & HIDPP_QUIRK_CLASS_M560)
+               return m560_raw_event(hdev, data, size);
 
        return 0;
 }
@@ -1115,6 +1324,10 @@ static void hidpp_connect_event(struct hidpp_device *hidpp)
                ret = wtp_connect(hdev, connected);
                if (ret)
                        return;
+       } else if (hidpp->quirks & HIDPP_QUIRK_CLASS_M560) {
+               ret = m560_send_config_command(hdev, connected);
+               if (ret)
+                       return;
        }
 
        if (!connected || hidpp->delayed_input)
@@ -1190,7 +1403,11 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
        if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP) {
                ret = wtp_allocate(hdev, id);
                if (ret)
-                       goto wtp_allocate_fail;
+                       goto allocate_fail;
+       } else if (hidpp->quirks & HIDPP_QUIRK_CLASS_M560) {
+               ret = m560_allocate(hdev);
+               if (ret)
+                       goto allocate_fail;
        }
 
        INIT_WORK(&hidpp->work, delayed_work_cb);
@@ -1253,7 +1470,7 @@ hid_hw_start_fail:
 hid_parse_fail:
        cancel_work_sync(&hidpp->work);
        mutex_destroy(&hidpp->send_mutex);
-wtp_allocate_fail:
+allocate_fail:
        hid_set_drvdata(hdev, NULL);
        return ret;
 }
@@ -1281,6 +1498,10 @@ static const struct hid_device_id hidpp_devices[] = {
          HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH,
                USB_DEVICE_ID_LOGITECH_T651),
          .driver_data = HIDPP_QUIRK_CLASS_WTP },
+       { /* Mouse logitech M560 */
+         HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE,
+               USB_VENDOR_ID_LOGITECH, 0x402d),
+         .driver_data = HIDPP_QUIRK_DELAYED_INIT | HIDPP_QUIRK_CLASS_M560 },
 
        { HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE,
                USB_VENDOR_ID_LOGITECH, HID_ANY_ID)},
index af935eb198c93549867c4e50bb48a997e5cd3f2b..32a596f554afe1605b9eaabc984c263f1398ff13 100644 (file)
@@ -280,6 +280,8 @@ static const struct hid_device_id ms_devices[] = {
                .driver_data = MS_HIDINPUT },
        { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_3_JP),
                .driver_data = MS_HIDINPUT },
+       { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_POWER_COVER),
+               .driver_data = MS_HIDINPUT },
 
        { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_BT),
                .driver_data = MS_PRESENTER },
index 2180e0789b76ebf794f7893752f71f8fcb1584af..febb21ee190e99134ae995887ceeeb86721e1440 100644 (file)
@@ -2,7 +2,7 @@
  *  Plantronics USB HID Driver
  *
  *  Copyright (c) 2014 JD Cole <jd.cole@plantronics.com>
- *  Copyright (c) 2014 Terry Junge <terry.junge@plantronics.com>
+ *  Copyright (c) 2015 Terry Junge <terry.junge@plantronics.com>
  */
 
 /*
 #include <linux/hid.h>
 #include <linux/module.h>
 
+#define PLT_HID_1_0_PAGE       0xffa00000
+#define PLT_HID_2_0_PAGE       0xffa20000
+
+#define PLT_BASIC_TELEPHONY    0x0003
+#define PLT_BASIC_EXCEPTION    0x0005
+
+#define PLT_VOL_UP             0x00b1
+#define PLT_VOL_DOWN           0x00b2
+
+#define PLT1_VOL_UP            (PLT_HID_1_0_PAGE | PLT_VOL_UP)
+#define PLT1_VOL_DOWN          (PLT_HID_1_0_PAGE | PLT_VOL_DOWN)
+#define PLT2_VOL_UP            (PLT_HID_2_0_PAGE | PLT_VOL_UP)
+#define PLT2_VOL_DOWN          (PLT_HID_2_0_PAGE | PLT_VOL_DOWN)
+
+#define PLT_DA60               0xda60
+#define PLT_BT300_MIN          0x0413
+#define PLT_BT300_MAX          0x0418
+
+
+#define PLT_ALLOW_CONSUMER (field->application == HID_CP_CONSUMERCONTROL && \
+                           (usage->hid & HID_USAGE_PAGE) == HID_UP_CONSUMER)
+
 static int plantronics_input_mapping(struct hid_device *hdev,
                                     struct hid_input *hi,
                                     struct hid_field *field,
                                     struct hid_usage *usage,
                                     unsigned long **bit, int *max)
 {
-       if (field->application == HID_CP_CONSUMERCONTROL
-           && (usage->hid & HID_USAGE_PAGE) == HID_UP_CONSUMER) {
-               hid_dbg(hdev, "usage: %08x (appl: %08x) - defaulted\n",
-                        usage->hid, field->application);
-               return 0;
+       unsigned short mapped_key;
+       unsigned long plt_type = (unsigned long)hid_get_drvdata(hdev);
+
+       /* handle volume up/down mapping */
+       /* non-standard types or multi-HID interfaces - plt_type is PID */
+       if (!(plt_type & HID_USAGE_PAGE)) {
+               switch (plt_type) {
+               case PLT_DA60:
+                       if (PLT_ALLOW_CONSUMER)
+                               goto defaulted;
+                       goto ignored;
+               default:
+                       if (PLT_ALLOW_CONSUMER)
+                               goto defaulted;
+               }
+       }
+       /* handle standard types - plt_type is 0xffa0uuuu or 0xffa2uuuu */
+       /* 'basic telephony compliant' - allow default consumer page map */
+       else if ((plt_type & HID_USAGE) >= PLT_BASIC_TELEPHONY &&
+                (plt_type & HID_USAGE) != PLT_BASIC_EXCEPTION) {
+               if (PLT_ALLOW_CONSUMER)
+                       goto defaulted;
+       }
+       /* not 'basic telephony' - apply legacy mapping */
+       /* only map if the field is in the device's primary vendor page */
+       else if (!((field->application ^ plt_type) & HID_USAGE_PAGE)) {
+               switch (usage->hid) {
+               case PLT1_VOL_UP:
+               case PLT2_VOL_UP:
+                       mapped_key = KEY_VOLUMEUP;
+                       goto mapped;
+               case PLT1_VOL_DOWN:
+               case PLT2_VOL_DOWN:
+                       mapped_key = KEY_VOLUMEDOWN;
+                       goto mapped;
+               }
        }
 
-       hid_dbg(hdev, "usage: %08x (appl: %08x) - ignored\n",
-               usage->hid, field->application);
+/*
+ * Future mapping of call control or other usages,
+ * if and when keys are defined would go here
+ * otherwise, ignore everything else that was not mapped
+ */
 
+ignored:
        return -1;
+
+defaulted:
+       hid_dbg(hdev, "usage: %08x (appl: %08x) - defaulted\n",
+               usage->hid, field->application);
+       return 0;
+
+mapped:
+       hid_map_usage_clear(hi, usage, bit, max, EV_KEY, mapped_key);
+       hid_dbg(hdev, "usage: %08x (appl: %08x) - mapped to key %d\n",
+               usage->hid, field->application, mapped_key);
+       return 1;
+}
+
+static unsigned long plantronics_device_type(struct hid_device *hdev)
+{
+       unsigned i, col_page;
+       unsigned long plt_type = hdev->product;
+
+       /* multi-HID interfaces? - plt_type is PID */
+       if (plt_type >= PLT_BT300_MIN && plt_type <= PLT_BT300_MAX)
+               goto exit;
+
+       /* determine primary vendor page */
+       for (i = 0; i < hdev->maxcollection; i++) {
+               col_page = hdev->collection[i].usage & HID_USAGE_PAGE;
+               if (col_page == PLT_HID_2_0_PAGE) {
+                       plt_type = hdev->collection[i].usage;
+                       break;
+               }
+               if (col_page == PLT_HID_1_0_PAGE)
+                       plt_type = hdev->collection[i].usage;
+       }
+
+exit:
+       hid_dbg(hdev, "plt_type decoded as: %08lx\n", plt_type);
+       return plt_type;
+}
+
+static int plantronics_probe(struct hid_device *hdev,
+                            const struct hid_device_id *id)
+{
+       int ret;
+
+       ret = hid_parse(hdev);
+       if (ret) {
+               hid_err(hdev, "parse failed\n");
+               goto err;
+       }
+
+       hid_set_drvdata(hdev, (void *)plantronics_device_type(hdev));
+
+       ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT |
+               HID_CONNECT_HIDINPUT_FORCE | HID_CONNECT_HIDDEV_FORCE);
+       if (ret)
+               hid_err(hdev, "hw start failed\n");
+
+err:
+       return ret;
 }
 
 static const struct hid_device_id plantronics_devices[] = {
@@ -46,6 +161,7 @@ static struct hid_driver plantronics_driver = {
        .name = "plantronics",
        .id_table = plantronics_devices,
        .input_mapping = plantronics_input_mapping,
+       .probe = plantronics_probe,
 };
 module_hid_driver(plantronics_driver);
 
index 91fab975063ce889b1ceae0bafbab2273d77186b..e3e98ccf137b54bae52fcd5cd0ded68b839742bf 100644 (file)
@@ -395,11 +395,10 @@ static int pcmidi_handle_report4(struct pcmidi_snd *pm, u8 *data)
 
        /* break keys */
        for (bit_index = 0; bit_index < 24; bit_index++) {
-               key = pm->last_key[bit_index];
                if (!((0x01 << bit_index) & bit_mask)) {
                        input_event(pm->input_ep82, EV_KEY,
                                pm->last_key[bit_index], 0);
-                               pm->last_key[bit_index] = 0;
+                       pm->last_key[bit_index] = 0;
                }
        }
 
index 368ffdf2c0a3af086d9242f00476f598a3fffdf9..4cf80bb276dc34f481f8aaf10bec55966fe80657 100644 (file)
@@ -29,9 +29,9 @@
 #define RMI_SET_RMI_MODE_REPORT_ID     0x0f /* Feature Report */
 
 /* flags */
-#define RMI_READ_REQUEST_PENDING       BIT(0)
-#define RMI_READ_DATA_PENDING          BIT(1)
-#define RMI_STARTED                    BIT(2)
+#define RMI_READ_REQUEST_PENDING       0
+#define RMI_READ_DATA_PENDING          1
+#define RMI_STARTED                    2
 
 /* device flags */
 #define RMI_DEVICE                     BIT(0)
@@ -1013,6 +1013,7 @@ static int rmi_populate_f30(struct hid_device *hdev)
 
 static int rmi_populate(struct hid_device *hdev)
 {
+       struct rmi_data *data = hid_get_drvdata(hdev);
        int ret;
 
        ret = rmi_scan_pdt(hdev);
@@ -1033,9 +1034,11 @@ static int rmi_populate(struct hid_device *hdev)
                return ret;
        }
 
-       ret = rmi_populate_f30(hdev);
-       if (ret)
-               hid_warn(hdev, "Error while initializing F30 (%d).\n", ret);
+       if (!(data->device_flags & RMI_DEVICE_HAS_PHYS_BUTTONS)) {
+               ret = rmi_populate_f30(hdev);
+               if (ret)
+                       hid_warn(hdev, "Error while initializing F30 (%d).\n", ret);
+       }
 
        return 0;
 }
index 37845eccddb566e138ee503876a0a0351918b131..36b6470af947b493b2e1e900d3cdc8e4833e5939 100644 (file)
@@ -166,6 +166,9 @@ static const struct hid_device_id sjoy_devices[] = {
        { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_DUAL_USB_JOYPAD),
                .driver_data = HID_QUIRK_MULTI_INPUT |
                               HID_QUIRK_SKIP_OUTPUT_REPORTS },
+       { HID_USB_DEVICE(USB_VENDOR_ID_PLAYDOTCOM, USB_DEVICE_ID_PLAYDOTCOM_EMS_USBII),
+               .driver_data = HID_QUIRK_MULTI_INPUT |
+                              HID_QUIRK_SKIP_OUTPUT_REPORTS },
        { }
 };
 MODULE_DEVICE_TABLE(hid, sjoy_devices);
index 6ca96cebb44ce1e01290ae7d718ad97c44d9998b..ed2f008f840377c394f1a910c331ea5f472a231e 100644 (file)
 #define PS3REMOTE                 BIT(4)
 #define DUALSHOCK4_CONTROLLER_USB BIT(5)
 #define DUALSHOCK4_CONTROLLER_BT  BIT(6)
+#define MOTION_CONTROLLER_USB     BIT(7)
+#define MOTION_CONTROLLER_BT      BIT(8)
+#define NAVIGATION_CONTROLLER_USB BIT(9)
+#define NAVIGATION_CONTROLLER_BT  BIT(10)
 
 #define SIXAXIS_CONTROLLER (SIXAXIS_CONTROLLER_USB | SIXAXIS_CONTROLLER_BT)
+#define MOTION_CONTROLLER (MOTION_CONTROLLER_USB | MOTION_CONTROLLER_BT)
+#define NAVIGATION_CONTROLLER (NAVIGATION_CONTROLLER_USB |\
+                               NAVIGATION_CONTROLLER_BT)
 #define DUALSHOCK4_CONTROLLER (DUALSHOCK4_CONTROLLER_USB |\
                                DUALSHOCK4_CONTROLLER_BT)
 #define SONY_LED_SUPPORT (SIXAXIS_CONTROLLER | BUZZ_CONTROLLER |\
-                               DUALSHOCK4_CONTROLLER)
-#define SONY_BATTERY_SUPPORT (SIXAXIS_CONTROLLER | DUALSHOCK4_CONTROLLER)
-#define SONY_FF_SUPPORT (SIXAXIS_CONTROLLER | DUALSHOCK4_CONTROLLER)
+                               DUALSHOCK4_CONTROLLER | MOTION_CONTROLLER |\
+                               NAVIGATION_CONTROLLER)
+#define SONY_BATTERY_SUPPORT (SIXAXIS_CONTROLLER | DUALSHOCK4_CONTROLLER |\
+                               MOTION_CONTROLLER_BT | NAVIGATION_CONTROLLER)
+#define SONY_FF_SUPPORT (SIXAXIS_CONTROLLER | DUALSHOCK4_CONTROLLER |\
+                               MOTION_CONTROLLER)
 
 #define MAX_LEDS 4
 
+/*
+ * The Sixaxis reports both digital and analog values for each button on the
+ * controller except for Start, Select and the PS button.  The controller ends
+ * up reporting 27 axes which causes them to spill over into the multi-touch
+ * axis values.  Additionally, the controller only has 20 actual, physical axes
+ * so there are several unused axes in between the used ones.
+ */
 static __u8 sixaxis_rdesc[] = {
        0x05, 0x01,         /*  Usage Page (Desktop),               */
-       0x09, 0x04,         /*  Usage (Joystik),                    */
+       0x09, 0x04,         /*  Usage (Joystick),                   */
        0xA1, 0x01,         /*  Collection (Application),           */
        0xA1, 0x02,         /*      Collection (Logical),           */
        0x85, 0x01,         /*          Report ID (1),              */
@@ -134,6 +151,186 @@ static __u8 sixaxis_rdesc[] = {
        0xC0                /*  End Collection                      */
 };
 
+/* PS/3 Motion controller */
+static __u8 motion_rdesc[] = {
+       0x05, 0x01,         /*  Usage Page (Desktop),               */
+       0x09, 0x04,         /*  Usage (Joystick),                   */
+       0xA1, 0x01,         /*  Collection (Application),           */
+       0xA1, 0x02,         /*      Collection (Logical),           */
+       0x85, 0x01,         /*          Report ID (1),              */
+       0x75, 0x01,         /*          Report Size (1),            */
+       0x95, 0x15,         /*          Report Count (21),          */
+       0x15, 0x00,         /*          Logical Minimum (0),        */
+       0x25, 0x01,         /*          Logical Maximum (1),        */
+       0x35, 0x00,         /*          Physical Minimum (0),       */
+       0x45, 0x01,         /*          Physical Maximum (1),       */
+       0x05, 0x09,         /*          Usage Page (Button),        */
+       0x19, 0x01,         /*          Usage Minimum (01h),        */
+       0x29, 0x15,         /*          Usage Maximum (15h),        */
+       0x81, 0x02,         /*          Input (Variable),           * Buttons */
+       0x95, 0x0B,         /*          Report Count (11),          */
+       0x06, 0x00, 0xFF,   /*          Usage Page (FF00h),         */
+       0x81, 0x03,         /*          Input (Constant, Variable), * Padding */
+       0x15, 0x00,         /*          Logical Minimum (0),        */
+       0x26, 0xFF, 0x00,   /*          Logical Maximum (255),      */
+       0x05, 0x01,         /*          Usage Page (Desktop),       */
+       0xA1, 0x00,         /*          Collection (Physical),      */
+       0x75, 0x08,         /*              Report Size (8),        */
+       0x95, 0x01,         /*              Report Count (1),       */
+       0x35, 0x00,         /*              Physical Minimum (0),   */
+       0x46, 0xFF, 0x00,   /*              Physical Maximum (255), */
+       0x09, 0x30,         /*              Usage (X),              */
+       0x81, 0x02,         /*              Input (Variable),       * Trigger */
+       0xC0,               /*          End Collection,             */
+       0x06, 0x00, 0xFF,   /*          Usage Page (FF00h),         */
+       0x75, 0x08,         /*          Report Size (8),            */
+       0x95, 0x07,         /*          Report Count (7),           * skip 7 bytes */
+       0x81, 0x02,         /*          Input (Variable),           */
+       0x05, 0x01,         /*          Usage Page (Desktop),       */
+       0x75, 0x10,         /*          Report Size (16),           */
+       0x46, 0xFF, 0xFF,   /*          Physical Maximum (65535),   */
+       0x27, 0xFF, 0xFF, 0x00, 0x00, /*      Logical Maximum (65535),    */
+       0x95, 0x03,         /*          Report Count (3),           * 3x Accels */
+       0x09, 0x33,         /*              Usage (rX),             */
+       0x09, 0x34,         /*              Usage (rY),             */
+       0x09, 0x35,         /*              Usage (rZ),             */
+       0x81, 0x02,         /*          Input (Variable),           */
+       0x06, 0x00, 0xFF,   /*          Usage Page (FF00h),         */
+       0x95, 0x03,         /*          Report Count (3),           * Skip Accels 2nd frame */
+       0x81, 0x02,         /*          Input (Variable),           */
+       0x05, 0x01,         /*          Usage Page (Desktop),       */
+       0x09, 0x01,         /*          Usage (Pointer),            */
+       0x95, 0x03,         /*          Report Count (3),           * 3x Gyros */
+       0x81, 0x02,         /*          Input (Variable),           */
+       0x06, 0x00, 0xFF,   /*          Usage Page (FF00h),         */
+       0x95, 0x03,         /*          Report Count (3),           * Skip Gyros 2nd frame */
+       0x81, 0x02,         /*          Input (Variable),           */
+       0x75, 0x0C,         /*          Report Size (12),           */
+       0x46, 0xFF, 0x0F,   /*          Physical Maximum (4095),    */
+       0x26, 0xFF, 0x0F,   /*          Logical Maximum (4095),     */
+       0x95, 0x04,         /*          Report Count (4),           * Skip Temp and Magnetometers */
+       0x81, 0x02,         /*          Input (Variable),           */
+       0x75, 0x08,         /*          Report Size (8),            */
+       0x46, 0xFF, 0x00,   /*          Physical Maximum (255),     */
+       0x26, 0xFF, 0x00,   /*          Logical Maximum (255),      */
+       0x95, 0x06,         /*          Report Count (6),           * Skip Timestamp and Extension Bytes */
+       0x81, 0x02,         /*          Input (Variable),           */
+       0x75, 0x08,         /*          Report Size (8),            */
+       0x95, 0x30,         /*          Report Count (48),          */
+       0x09, 0x01,         /*          Usage (Pointer),            */
+       0x91, 0x02,         /*          Output (Variable),          */
+       0x75, 0x08,         /*          Report Size (8),            */
+       0x95, 0x30,         /*          Report Count (48),          */
+       0x09, 0x01,         /*          Usage (Pointer),            */
+       0xB1, 0x02,         /*          Feature (Variable),         */
+       0xC0,               /*      End Collection,                 */
+       0xA1, 0x02,         /*      Collection (Logical),           */
+       0x85, 0x02,         /*          Report ID (2),              */
+       0x75, 0x08,         /*          Report Size (8),            */
+       0x95, 0x30,         /*          Report Count (48),          */
+       0x09, 0x01,         /*          Usage (Pointer),            */
+       0xB1, 0x02,         /*          Feature (Variable),         */
+       0xC0,               /*      End Collection,                 */
+       0xA1, 0x02,         /*      Collection (Logical),           */
+       0x85, 0xEE,         /*          Report ID (238),            */
+       0x75, 0x08,         /*          Report Size (8),            */
+       0x95, 0x30,         /*          Report Count (48),          */
+       0x09, 0x01,         /*          Usage (Pointer),            */
+       0xB1, 0x02,         /*          Feature (Variable),         */
+       0xC0,               /*      End Collection,                 */
+       0xA1, 0x02,         /*      Collection (Logical),           */
+       0x85, 0xEF,         /*          Report ID (239),            */
+       0x75, 0x08,         /*          Report Size (8),            */
+       0x95, 0x30,         /*          Report Count (48),          */
+       0x09, 0x01,         /*          Usage (Pointer),            */
+       0xB1, 0x02,         /*          Feature (Variable),         */
+       0xC0,               /*      End Collection,                 */
+       0xC0                /*  End Collection                      */
+};
+
+/* PS/3 Navigation controller */
+static __u8 navigation_rdesc[] = {
+       0x05, 0x01,         /*  Usage Page (Desktop),               */
+       0x09, 0x04,         /*  Usage (Joystik),                    */
+       0xA1, 0x01,         /*  Collection (Application),           */
+       0xA1, 0x02,         /*      Collection (Logical),           */
+       0x85, 0x01,         /*          Report ID (1),              */
+       0x75, 0x08,         /*          Report Size (8),            */
+       0x95, 0x01,         /*          Report Count (1),           */
+       0x15, 0x00,         /*          Logical Minimum (0),        */
+       0x26, 0xFF, 0x00,   /*          Logical Maximum (255),      */
+       0x81, 0x03,         /*          Input (Constant, Variable), */
+       0x75, 0x01,         /*          Report Size (1),            */
+       0x95, 0x13,         /*          Report Count (19),          */
+       0x15, 0x00,         /*          Logical Minimum (0),        */
+       0x25, 0x01,         /*          Logical Maximum (1),        */
+       0x35, 0x00,         /*          Physical Minimum (0),       */
+       0x45, 0x01,         /*          Physical Maximum (1),       */
+       0x05, 0x09,         /*          Usage Page (Button),        */
+       0x19, 0x01,         /*          Usage Minimum (01h),        */
+       0x29, 0x13,         /*          Usage Maximum (13h),        */
+       0x81, 0x02,         /*          Input (Variable),           */
+       0x75, 0x01,         /*          Report Size (1),            */
+       0x95, 0x0D,         /*          Report Count (13),          */
+       0x06, 0x00, 0xFF,   /*          Usage Page (FF00h),         */
+       0x81, 0x03,         /*          Input (Constant, Variable), */
+       0x15, 0x00,         /*          Logical Minimum (0),        */
+       0x26, 0xFF, 0x00,   /*          Logical Maximum (255),      */
+       0x05, 0x01,         /*          Usage Page (Desktop),       */
+       0x09, 0x01,         /*          Usage (Pointer),            */
+       0xA1, 0x00,         /*          Collection (Physical),      */
+       0x75, 0x08,         /*              Report Size (8),        */
+       0x95, 0x02,         /*              Report Count (2),       */
+       0x35, 0x00,         /*              Physical Minimum (0),   */
+       0x46, 0xFF, 0x00,   /*              Physical Maximum (255), */
+       0x09, 0x30,         /*              Usage (X),              */
+       0x09, 0x31,         /*              Usage (Y),              */
+       0x81, 0x02,         /*              Input (Variable),       */
+       0xC0,               /*          End Collection,             */
+       0x06, 0x00, 0xFF,   /*          Usage Page (FF00h),         */
+       0x95, 0x06,         /*          Report Count (6),           */
+       0x81, 0x03,         /*          Input (Constant, Variable), */
+       0x05, 0x01,         /*          Usage Page (Desktop),       */
+       0x75, 0x08,         /*          Report Size (8),            */
+       0x95, 0x05,         /*          Report Count (5),           */
+       0x09, 0x01,         /*          Usage (Pointer),            */
+       0x81, 0x02,         /*          Input (Variable),           */
+       0x06, 0x00, 0xFF,   /*          Usage Page (FF00h),         */
+       0x95, 0x20,         /*          Report Count (26),          */
+       0x81, 0x02,         /*          Input (Variable),           */
+       0x75, 0x08,         /*          Report Size (8),            */
+       0x95, 0x30,         /*          Report Count (48),          */
+       0x09, 0x01,         /*          Usage (Pointer),            */
+       0x91, 0x02,         /*          Output (Variable),          */
+       0x75, 0x08,         /*          Report Size (8),            */
+       0x95, 0x30,         /*          Report Count (48),          */
+       0x09, 0x01,         /*          Usage (Pointer),            */
+       0xB1, 0x02,         /*          Feature (Variable),         */
+       0xC0,               /*      End Collection,                 */
+       0xA1, 0x02,         /*      Collection (Logical),           */
+       0x85, 0x02,         /*          Report ID (2),              */
+       0x75, 0x08,         /*          Report Size (8),            */
+       0x95, 0x30,         /*          Report Count (48),          */
+       0x09, 0x01,         /*          Usage (Pointer),            */
+       0xB1, 0x02,         /*          Feature (Variable),         */
+       0xC0,               /*      End Collection,                 */
+       0xA1, 0x02,         /*      Collection (Logical),           */
+       0x85, 0xEE,         /*          Report ID (238),            */
+       0x75, 0x08,         /*          Report Size (8),            */
+       0x95, 0x30,         /*          Report Count (48),          */
+       0x09, 0x01,         /*          Usage (Pointer),            */
+       0xB1, 0x02,         /*          Feature (Variable),         */
+       0xC0,               /*      End Collection,                 */
+       0xA1, 0x02,         /*      Collection (Logical),           */
+       0x85, 0xEF,         /*          Report ID (239),            */
+       0x75, 0x08,         /*          Report Size (8),            */
+       0x95, 0x30,         /*          Report Count (48),          */
+       0x09, 0x01,         /*          Usage (Pointer),            */
+       0xB1, 0x02,         /*          Feature (Variable),         */
+       0xC0,               /*      End Collection,                 */
+       0xC0                /*  End Collection                      */
+};
+
 /*
  * The default descriptor doesn't provide mapping for the accelerometers
  * or orientation sensors.  This fixed descriptor maps the accelerometers
@@ -798,12 +995,20 @@ union sixaxis_output_report_01 {
        __u8 buf[36];
 };
 
+struct motion_output_report_02 {
+       u8 type, zero;
+       u8 r, g, b;
+       u8 zero2;
+       u8 rumble;
+};
+
 #define DS4_REPORT_0x02_SIZE 37
 #define DS4_REPORT_0x05_SIZE 32
 #define DS4_REPORT_0x11_SIZE 78
 #define DS4_REPORT_0x81_SIZE 7
 #define SIXAXIS_REPORT_0xF2_SIZE 17
 #define SIXAXIS_REPORT_0xF5_SIZE 8
+#define MOTION_REPORT_0x02_SIZE 49
 
 static DEFINE_SPINLOCK(sony_dev_list_lock);
 static LIST_HEAD(sony_device_list);
@@ -844,6 +1049,20 @@ static __u8 *sixaxis_fixup(struct hid_device *hdev, __u8 *rdesc,
        return sixaxis_rdesc;
 }
 
+static u8 *motion_fixup(struct hid_device *hdev, u8 *rdesc,
+                            unsigned int *rsize)
+{
+       *rsize = sizeof(motion_rdesc);
+       return motion_rdesc;
+}
+
+static u8 *navigation_fixup(struct hid_device *hdev, u8 *rdesc,
+                            unsigned int *rsize)
+{
+       *rsize = sizeof(navigation_rdesc);
+       return navigation_rdesc;
+}
+
 static __u8 *ps3remote_fixup(struct hid_device *hdev, __u8 *rdesc,
                             unsigned int *rsize)
 {
@@ -924,6 +1143,12 @@ static __u8 *sony_report_fixup(struct hid_device *hdev, __u8 *rdesc,
        if (sc->quirks & SIXAXIS_CONTROLLER)
                return sixaxis_fixup(hdev, rdesc, rsize);
 
+       if (sc->quirks & MOTION_CONTROLLER)
+               return motion_fixup(hdev, rdesc, rsize);
+
+       if (sc->quirks & NAVIGATION_CONTROLLER)
+               return navigation_fixup(hdev, rdesc, rsize);
+
        if (sc->quirks & PS3REMOTE)
                return ps3remote_fixup(hdev, rdesc, rsize);
 
@@ -934,6 +1159,7 @@ static void sixaxis_parse_report(struct sony_sc *sc, __u8 *rd, int size)
 {
        static const __u8 sixaxis_battery_capacity[] = { 0, 1, 25, 50, 75, 100 };
        unsigned long flags;
+       int offset;
        __u8 cable_state, battery_capacity, battery_charging;
 
        /*
@@ -942,12 +1168,14 @@ static void sixaxis_parse_report(struct sony_sc *sc, __u8 *rd, int size)
         * It does not report the actual level while charging so it
         * is set to 100% while charging is in progress.
         */
-       if (rd[30] >= 0xee) {
+       offset = (sc->quirks & MOTION_CONTROLLER) ? 12 : 30;
+
+       if (rd[offset] >= 0xee) {
                battery_capacity = 100;
-               battery_charging = !(rd[30] & 0x01);
+               battery_charging = !(rd[offset] & 0x01);
                cable_state = 1;
        } else {
-               __u8 index = rd[30] <= 5 ? rd[30] : 5;
+               __u8 index = rd[offset] <= 5 ? rd[offset] : 5;
                battery_capacity = sixaxis_battery_capacity[index];
                battery_charging = 0;
                cable_state = 0;
@@ -1048,6 +1276,11 @@ static int sony_raw_event(struct hid_device *hdev, struct hid_report *report,
                swap(rd[47], rd[48]);
 
                sixaxis_parse_report(sc, rd, size);
+       } else if ((sc->quirks & MOTION_CONTROLLER_BT) && rd[0] == 0x01 && size == 49) {
+               sixaxis_parse_report(sc, rd, size);
+       } else if ((sc->quirks & NAVIGATION_CONTROLLER) && rd[0] == 0x01 &&
+                       size == 49) {
+               sixaxis_parse_report(sc, rd, size);
        } else if (((sc->quirks & DUALSHOCK4_CONTROLLER_USB) && rd[0] == 0x01 &&
                        size == 64) || ((sc->quirks & DUALSHOCK4_CONTROLLER_BT)
                        && rd[0] == 0x11 && size == 78)) {
@@ -1208,7 +1441,7 @@ static int dualshock4_set_operational_bt(struct hid_device *hdev)
        return ret;
 }
 
-static void sixaxis_set_leds_from_id(int id, __u8 values[MAX_LEDS])
+static void sixaxis_set_leds_from_id(struct sony_sc *sc)
 {
        static const __u8 sixaxis_leds[10][4] = {
                                { 0x01, 0x00, 0x00, 0x00 },
@@ -1223,16 +1456,18 @@ static void sixaxis_set_leds_from_id(int id, __u8 values[MAX_LEDS])
                                { 0x01, 0x01, 0x01, 0x01 }
        };
 
-       BUG_ON(MAX_LEDS < ARRAY_SIZE(sixaxis_leds[0]));
+       int id = sc->device_id;
+
+       BUILD_BUG_ON(MAX_LEDS < ARRAY_SIZE(sixaxis_leds[0]));
 
        if (id < 0)
                return;
 
        id %= 10;
-       memcpy(values, sixaxis_leds[id], sizeof(sixaxis_leds[id]));
+       memcpy(sc->led_state, sixaxis_leds[id], sizeof(sixaxis_leds[id]));
 }
 
-static void dualshock4_set_leds_from_id(int id, __u8 values[MAX_LEDS])
+static void dualshock4_set_leds_from_id(struct sony_sc *sc)
 {
        /* The first 4 color/index entries match what the PS4 assigns */
        static const __u8 color_code[7][3] = {
@@ -1245,46 +1480,44 @@ static void dualshock4_set_leds_from_id(int id, __u8 values[MAX_LEDS])
                        /* White  */    { 0x01, 0x01, 0x01 }
        };
 
-       BUG_ON(MAX_LEDS < ARRAY_SIZE(color_code[0]));
+       int id = sc->device_id;
+
+       BUILD_BUG_ON(MAX_LEDS < ARRAY_SIZE(color_code[0]));
 
        if (id < 0)
                return;
 
        id %= 7;
-       memcpy(values, color_code[id], sizeof(color_code[id]));
+       memcpy(sc->led_state, color_code[id], sizeof(color_code[id]));
 }
 
-static void buzz_set_leds(struct hid_device *hdev, const __u8 *leds)
+static void buzz_set_leds(struct sony_sc *sc)
 {
+       struct hid_device *hdev = sc->hdev;
        struct list_head *report_list =
                &hdev->report_enum[HID_OUTPUT_REPORT].report_list;
        struct hid_report *report = list_entry(report_list->next,
                struct hid_report, list);
        __s32 *value = report->field[0]->value;
 
+       BUILD_BUG_ON(MAX_LEDS < 4);
+
        value[0] = 0x00;
-       value[1] = leds[0] ? 0xff : 0x00;
-       value[2] = leds[1] ? 0xff : 0x00;
-       value[3] = leds[2] ? 0xff : 0x00;
-       value[4] = leds[3] ? 0xff : 0x00;
+       value[1] = sc->led_state[0] ? 0xff : 0x00;
+       value[2] = sc->led_state[1] ? 0xff : 0x00;
+       value[3] = sc->led_state[2] ? 0xff : 0x00;
+       value[4] = sc->led_state[3] ? 0xff : 0x00;
        value[5] = 0x00;
        value[6] = 0x00;
        hid_hw_request(hdev, report, HID_REQ_SET_REPORT);
 }
 
-static void sony_set_leds(struct sony_sc *sc, const __u8 *leds, int count)
+static void sony_set_leds(struct sony_sc *sc)
 {
-       int n;
-
-       BUG_ON(count > MAX_LEDS);
-
-       if (sc->quirks & BUZZ_CONTROLLER && count == 4) {
-               buzz_set_leds(sc->hdev, leds);
-       } else {
-               for (n = 0; n < count; n++)
-                       sc->led_state[n] = leds[n];
+       if (!(sc->quirks & BUZZ_CONTROLLER))
                schedule_work(&sc->state_worker);
-       }
+       else
+               buzz_set_leds(sc);
 }
 
 static void sony_led_set_brightness(struct led_classdev *led,
@@ -1324,8 +1557,7 @@ static void sony_led_set_brightness(struct led_classdev *led,
                        drv_data->led_delay_on[n] = 0;
                        drv_data->led_delay_off[n] = 0;
 
-                       sony_set_leds(drv_data, drv_data->led_state,
-                                       drv_data->led_count);
+                       sony_set_leds(drv_data);
                        break;
                }
        }
@@ -1431,7 +1663,6 @@ static int sony_leds_init(struct sony_sc *sc)
        const char *name_fmt;
        static const char * const ds4_name_str[] = { "red", "green", "blue",
                                                  "global" };
-       __u8 initial_values[MAX_LEDS] = { 0 };
        __u8 max_brightness[MAX_LEDS] = { [0 ... (MAX_LEDS - 1)] = 1 };
        __u8 use_hw_blink[MAX_LEDS] = { 0 };
 
@@ -1446,16 +1677,31 @@ static int sony_leds_init(struct sony_sc *sc)
                if (!hid_validate_values(hdev, HID_OUTPUT_REPORT, 0, 0, 7))
                        return -ENODEV;
        } else if (sc->quirks & DUALSHOCK4_CONTROLLER) {
-               dualshock4_set_leds_from_id(sc->device_id, initial_values);
-               initial_values[3] = 1;
+               dualshock4_set_leds_from_id(sc);
+               sc->led_state[3] = 1;
                sc->led_count = 4;
                memset(max_brightness, 255, 3);
                use_hw_blink[3] = 1;
                use_ds4_names = 1;
                name_len = 0;
                name_fmt = "%s:%s";
+       } else if (sc->quirks & MOTION_CONTROLLER) {
+               sc->led_count = 3;
+               memset(max_brightness, 255, 3);
+               use_ds4_names = 1;
+               name_len = 0;
+               name_fmt = "%s:%s";
+       } else if (sc->quirks & NAVIGATION_CONTROLLER) {
+               static const __u8 navigation_leds[4] = {0x01, 0x00, 0x00, 0x00};
+
+               memcpy(sc->led_state, navigation_leds, sizeof(navigation_leds));
+               sc->led_count = 1;
+               memset(use_hw_blink, 1, 4);
+               use_ds4_names = 0;
+               name_len = strlen("::sony#");
+               name_fmt = "%s::sony%d";
        } else {
-               sixaxis_set_leds_from_id(sc->device_id, initial_values);
+               sixaxis_set_leds_from_id(sc);
                sc->led_count = 4;
                memset(use_hw_blink, 1, 4);
                use_ds4_names = 0;
@@ -1468,7 +1714,7 @@ static int sony_leds_init(struct sony_sc *sc)
         * only relevant if the driver is loaded after somebody actively set the
         * LEDs to on
         */
-       sony_set_leds(sc, initial_values, sc->led_count);
+       sony_set_leds(sc);
 
        name_sz = strlen(dev_name(&hdev->dev)) + name_len + 1;
 
@@ -1491,7 +1737,7 @@ static int sony_leds_init(struct sony_sc *sc)
                else
                        snprintf(name, name_sz, name_fmt, dev_name(&hdev->dev), n + 1);
                led->name = name;
-               led->brightness = initial_values[n];
+               led->brightness = sc->led_state[n];
                led->max_brightness = max_brightness[n];
                led->brightness_get = sony_led_get_brightness;
                led->brightness_set = sony_led_set_brightness;
@@ -1622,9 +1868,31 @@ static void dualshock4_state_worker(struct work_struct *work)
                                HID_OUTPUT_REPORT, HID_REQ_SET_REPORT);
 }
 
+static void motion_state_worker(struct work_struct *work)
+{
+       struct sony_sc *sc = container_of(work, struct sony_sc, state_worker);
+       struct hid_device *hdev = sc->hdev;
+       struct motion_output_report_02 *report =
+               (struct motion_output_report_02 *)sc->output_report_dmabuf;
+
+       memset(report, 0, MOTION_REPORT_0x02_SIZE);
+
+       report->type = 0x02; /* set leds */
+       report->r = sc->led_state[0];
+       report->g = sc->led_state[1];
+       report->b = sc->led_state[2];
+
+#ifdef CONFIG_SONY_FF
+       report->rumble = max(sc->right, sc->left);
+#endif
+
+       hid_hw_output_report(hdev, (__u8 *)report, MOTION_REPORT_0x02_SIZE);
+}
+
 static int sony_allocate_output_report(struct sony_sc *sc)
 {
-       if (sc->quirks & SIXAXIS_CONTROLLER)
+       if ((sc->quirks & SIXAXIS_CONTROLLER) ||
+                       (sc->quirks & NAVIGATION_CONTROLLER))
                sc->output_report_dmabuf =
                        kmalloc(sizeof(union sixaxis_output_report_01),
                                GFP_KERNEL);
@@ -1634,6 +1902,9 @@ static int sony_allocate_output_report(struct sony_sc *sc)
        else if (sc->quirks & DUALSHOCK4_CONTROLLER_USB)
                sc->output_report_dmabuf = kmalloc(DS4_REPORT_0x05_SIZE,
                                                GFP_KERNEL);
+       else if (sc->quirks & MOTION_CONTROLLER)
+               sc->output_report_dmabuf = kmalloc(MOTION_REPORT_0x02_SIZE,
+                                               GFP_KERNEL);
        else
                return 0;
 
@@ -1839,6 +2110,8 @@ static int sony_check_add(struct sony_sc *sc)
        int n, ret;
 
        if ((sc->quirks & DUALSHOCK4_CONTROLLER_BT) ||
+           (sc->quirks & MOTION_CONTROLLER_BT) ||
+           (sc->quirks & NAVIGATION_CONTROLLER_BT) ||
            (sc->quirks & SIXAXIS_CONTROLLER_BT)) {
                /*
                 * sony_get_bt_devaddr() attempts to parse the Bluetooth MAC
@@ -1871,7 +2144,8 @@ static int sony_check_add(struct sony_sc *sc)
                }
 
                memcpy(sc->mac_address, &buf[1], sizeof(sc->mac_address));
-       } else if (sc->quirks & SIXAXIS_CONTROLLER_USB) {
+       } else if ((sc->quirks & SIXAXIS_CONTROLLER_USB) ||
+                       (sc->quirks & NAVIGATION_CONTROLLER_USB)) {
                buf = kmalloc(SIXAXIS_REPORT_0xF2_SIZE, GFP_KERNEL);
                if (!buf)
                        return -ENOMEM;
@@ -1993,19 +2267,20 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
                return ret;
        }
 
-       ret = sony_allocate_output_report(sc);
+       ret = sony_set_device_id(sc);
        if (ret < 0) {
-               hid_err(hdev, "failed to allocate the output report buffer\n");
+               hid_err(hdev, "failed to allocate the device id\n");
                goto err_stop;
        }
 
-       ret = sony_set_device_id(sc);
+       ret = sony_allocate_output_report(sc);
        if (ret < 0) {
-               hid_err(hdev, "failed to allocate the device id\n");
+               hid_err(hdev, "failed to allocate the output report buffer\n");
                goto err_stop;
        }
 
-       if (sc->quirks & SIXAXIS_CONTROLLER_USB) {
+       if ((sc->quirks & SIXAXIS_CONTROLLER_USB) ||
+                       (sc->quirks & NAVIGATION_CONTROLLER_USB)) {
                /*
                 * The Sony Sixaxis does not handle HID Output Reports on the
                 * Interrupt EP like it could, so we need to force HID Output
@@ -2020,7 +2295,8 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
                hdev->quirks |= HID_QUIRK_SKIP_OUTPUT_REPORT_ID;
                ret = sixaxis_set_operational_usb(hdev);
                sony_init_work(sc, sixaxis_state_worker);
-       } else if (sc->quirks & SIXAXIS_CONTROLLER_BT) {
+       } else if ((sc->quirks & SIXAXIS_CONTROLLER_BT) ||
+                       (sc->quirks & NAVIGATION_CONTROLLER_BT)) {
                /*
                 * The Sixaxis wants output reports sent on the ctrl endpoint
                 * when connected via Bluetooth.
@@ -2043,6 +2319,8 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
                }
 
                sony_init_work(sc, dualshock4_state_worker);
+       } else if (sc->quirks & MOTION_CONTROLLER) {
+               sony_init_work(sc, motion_state_worker);
        } else {
                ret = 0;
        }
@@ -2122,7 +2400,13 @@ static const struct hid_device_id sony_devices[] = {
        { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER),
                .driver_data = SIXAXIS_CONTROLLER_USB },
        { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_NAVIGATION_CONTROLLER),
-               .driver_data = SIXAXIS_CONTROLLER_USB },
+               .driver_data = NAVIGATION_CONTROLLER_USB },
+       { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_NAVIGATION_CONTROLLER),
+               .driver_data = NAVIGATION_CONTROLLER_BT },
+       { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_MOTION_CONTROLLER),
+               .driver_data = MOTION_CONTROLLER_USB },
+       { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_MOTION_CONTROLLER),
+               .driver_data = MOTION_CONTROLLER_BT },
        { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER),
                .driver_data = SIXAXIS_CONTROLLER_BT },
        { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGX_MOUSE),
index 92d6cdf024607d848249e011d14f19e88d7c4a39..f77469d4edfb0ecb1709032361913d4a92ceb919 100644 (file)
@@ -42,9 +42,9 @@
 #include <linux/i2c/i2c-hid.h>
 
 /* flags */
-#define I2C_HID_STARTED                (1 << 0)
-#define I2C_HID_RESET_PENDING  (1 << 1)
-#define I2C_HID_READ_PENDING   (1 << 2)
+#define I2C_HID_STARTED                0
+#define I2C_HID_RESET_PENDING  1
+#define I2C_HID_READ_PENDING   2
 
 #define I2C_HID_PWR_ON         0x00
 #define I2C_HID_PWR_SLEEP      0x01
@@ -1019,7 +1019,6 @@ static int i2c_hid_probe(struct i2c_client *client,
        hid->driver_data = client;
        hid->ll_driver = &i2c_hid_ll_driver;
        hid->dev.parent = &client->dev;
-       ACPI_COMPANION_SET(&hid->dev, ACPI_COMPANION(&client->dev));
        hid->bus = BUS_I2C;
        hid->version = le16_to_cpu(ihid->hdesc.bcdVersion);
        hid->vendor = le16_to_cpu(ihid->hdesc.wVendorID);
index 4696895eb708316944dac63092e959e4b1bb1765..53e7de7cb9e25e6861f1acb5e3438e590b2e70ad 100644 (file)
@@ -52,7 +52,6 @@ static const struct hid_blacklist {
        { USB_VENDOR_ID_ETURBOTOUCH, USB_DEVICE_ID_ETURBOTOUCH_2968, HID_QUIRK_MULTI_INPUT },
        { USB_VENDOR_ID_GREENASIA, USB_DEVICE_ID_GREENASIA_DUAL_USB_JOYPAD, HID_QUIRK_MULTI_INPUT },
        { USB_VENDOR_ID_PANTHERLORD, USB_DEVICE_ID_PANTHERLORD_TWIN_USB_JOYSTICK, HID_QUIRK_MULTI_INPUT | HID_QUIRK_SKIP_OUTPUT_REPORTS },
-       { USB_VENDOR_ID_PLAYDOTCOM, USB_DEVICE_ID_PLAYDOTCOM_EMS_USBII, HID_QUIRK_MULTI_INPUT },
        { USB_VENDOR_ID_TOUCHPACK, USB_DEVICE_ID_TOUCHPACK_RTS, HID_QUIRK_MULTI_INPUT },
 
        { USB_VENDOR_ID_AIREN, USB_DEVICE_ID_AIREN_SLIMPLUS, HID_QUIRK_NOGET },
@@ -70,6 +69,7 @@ static const struct hid_blacklist {
        { USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_PRO_PEDALS, HID_QUIRK_NOGET },
        { USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_3AXIS_5BUTTON_STICK, HID_QUIRK_NOGET },
        { USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_AXIS_295, HID_QUIRK_NOGET },
+       { USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_PIXART_USB_OPTICAL_MOUSE, HID_QUIRK_ALWAYS_POLL },
        { USB_VENDOR_ID_DMI, USB_DEVICE_ID_DMI_ENC, HID_QUIRK_NOGET },
        { USB_VENDOR_ID_ELAN, USB_DEVICE_ID_ELAN_TOUCHSCREEN, HID_QUIRK_ALWAYS_POLL },
        { USB_VENDOR_ID_ELAN, USB_DEVICE_ID_ELAN_TOUCHSCREEN_009B, HID_QUIRK_ALWAYS_POLL },
@@ -89,6 +89,7 @@ static const struct hid_blacklist {
        { USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS, HID_QUIRK_NOGET },
        { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_3, HID_QUIRK_NO_INIT_REPORTS },
        { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_3_JP, HID_QUIRK_NO_INIT_REPORTS },
+       { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_POWER_COVER, HID_QUIRK_NO_INIT_REPORTS },
        { USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL, HID_QUIRK_NO_INIT_REPORTS },
        { USB_VENDOR_ID_NEXIO, USB_DEVICE_ID_NEXIO_MULTITOUCH_PTI0750, HID_QUIRK_NO_INIT_REPORTS },
        { USB_VENDOR_ID_NOVATEK, USB_DEVICE_ID_NOVATEK_MOUSE, HID_QUIRK_NO_INIT_REPORTS },
@@ -141,6 +142,9 @@ static const struct hid_blacklist {
        { USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_QUAD_HD, HID_QUIRK_NO_INIT_REPORTS },
        { USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_TP_V103, HID_QUIRK_NO_INIT_REPORTS },
        { USB_VENDOR_ID_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_KEYBOARD_A096, HID_QUIRK_NO_INIT_INPUT_REPORTS },
+       { USB_VENDOR_ID_MULTIPLE_1781, USB_DEVICE_ID_RAPHNET_4NES4SNES_OLD, HID_QUIRK_MULTI_INPUT },
+       { USB_VENDOR_ID_DRACAL_RAPHNET, USB_DEVICE_ID_RAPHNET_2NES2SNES, HID_QUIRK_MULTI_INPUT },
+       { USB_VENDOR_ID_DRACAL_RAPHNET, USB_DEVICE_ID_RAPHNET_4NES4SNES, HID_QUIRK_MULTI_INPUT },
 
        { 0, 0 }
 };
index 024f4d89d5792ae9a04739b5b895647177e6ecd4..a533787a6d857f815bcf816aefcde3c6d7ad7a4f 100644 (file)
@@ -134,8 +134,10 @@ static inline void wacom_schedule_work(struct wacom_wac *wacom_wac)
 extern const struct hid_device_id wacom_ids[];
 
 void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len);
-void wacom_setup_device_quirks(struct wacom_features *features);
-int wacom_setup_pentouch_input_capabilities(struct input_dev *input_dev,
+void wacom_setup_device_quirks(struct wacom *wacom);
+int wacom_setup_pen_input_capabilities(struct input_dev *input_dev,
+                                  struct wacom_wac *wacom_wac);
+int wacom_setup_touch_input_capabilities(struct input_dev *input_dev,
                                   struct wacom_wac *wacom_wac);
 int wacom_setup_pad_input_capabilities(struct input_dev *input_dev,
                                       struct wacom_wac *wacom_wac);
index e8607d0961384684ad94d94f2f4777adfdfbf42f..4c0ffca97befd61cf3cdd947138d14070c4e7895 100644 (file)
@@ -35,7 +35,11 @@ static int wacom_get_report(struct hid_device *hdev, u8 type, u8 *buf,
        do {
                retval = hid_hw_raw_request(hdev, buf[0], buf, size, type,
                                HID_REQ_GET_REPORT);
-       } while ((retval == -ETIMEDOUT || retval == -EPIPE) && --retries);
+       } while ((retval == -ETIMEDOUT || retval == -EAGAIN) && --retries);
+
+       if (retval < 0)
+               hid_err(hdev, "wacom_get_report: ran out of retries "
+                       "(last error = %d)\n", retval);
 
        return retval;
 }
@@ -48,7 +52,11 @@ static int wacom_set_report(struct hid_device *hdev, u8 type, u8 *buf,
        do {
                retval = hid_hw_raw_request(hdev, buf[0], buf, size, type,
                                HID_REQ_SET_REPORT);
-       } while ((retval == -ETIMEDOUT || retval == -EPIPE) && --retries);
+       } while ((retval == -ETIMEDOUT || retval == -EAGAIN) && --retries);
+
+       if (retval < 0)
+               hid_err(hdev, "wacom_set_report: ran out of retries "
+                       "(last error = %d)\n", retval);
 
        return retval;
 }
@@ -117,9 +125,16 @@ static void wacom_feature_mapping(struct hid_device *hdev,
                                break;
                        data[0] = field->report->id;
                        ret = wacom_get_report(hdev, HID_FEATURE_REPORT,
-                                               data, 2, 0);
-                       if (ret == 2)
+                                               data, 2, WAC_CMD_RETRIES);
+                       if (ret == 2) {
                                features->touch_max = data[1];
+                       } else {
+                               features->touch_max = 16;
+                               hid_warn(hdev, "wacom_feature_mapping: "
+                                        "could not get HID_DG_CONTACTMAX, "
+                                        "defaulting to %d\n",
+                                         features->touch_max);
+                       }
                        kfree(data);
                }
                break;
@@ -181,7 +196,11 @@ static void wacom_usage_mapping(struct hid_device *hdev,
        * X/Y values and some cases of invalid Digitizer X/Y
        * values commonly reported.
        */
-       if (!pen && !finger)
+       if (pen)
+               features->device_type |= WACOM_DEVICETYPE_PEN;
+       else if (finger)
+               features->device_type |= WACOM_DEVICETYPE_TOUCH;
+       else
                return;
 
        /*
@@ -198,14 +217,11 @@ static void wacom_usage_mapping(struct hid_device *hdev,
        case HID_GD_X:
                features->x_max = field->logical_maximum;
                if (finger) {
-                       features->device_type = BTN_TOOL_FINGER;
                        features->x_phy = field->physical_maximum;
                        if (features->type != BAMBOO_PT) {
                                features->unit = field->unit;
                                features->unitExpo = field->unit_exponent;
                        }
-               } else {
-                       features->device_type = BTN_TOOL_PEN;
                }
                break;
        case HID_GD_Y:
@@ -237,7 +253,7 @@ static void wacom_post_parse_hid(struct hid_device *hdev,
        if (features->type == HID_GENERIC) {
                /* Any last-minute generic device setup */
                if (features->touch_max > 1) {
-                       input_mt_init_slots(wacom_wac->input, wacom_wac->features.touch_max,
+                       input_mt_init_slots(wacom_wac->touch_input, wacom_wac->features.touch_max,
                                    INPUT_MT_DIRECT);
                }
        }
@@ -395,7 +411,7 @@ static int wacom_query_tablet_data(struct hid_device *hdev,
        if (features->type == HID_GENERIC)
                return wacom_hid_set_device_mode(hdev);
 
-       if (features->device_type == BTN_TOOL_FINGER) {
+       if (features->device_type & WACOM_DEVICETYPE_TOUCH) {
                if (features->type > TABLETPC) {
                        /* MT Tablet PC touch */
                        return wacom_set_device_mode(hdev, 3, 4, 4);
@@ -409,7 +425,7 @@ static int wacom_query_tablet_data(struct hid_device *hdev,
                else if (features->type == BAMBOO_PAD) {
                        return wacom_set_device_mode(hdev, 2, 2, 2);
                }
-       } else if (features->device_type == BTN_TOOL_PEN) {
+       } else if (features->device_type & WACOM_DEVICETYPE_PEN) {
                if (features->type <= BAMBOO_PT && features->type != WIRELESS) {
                        return wacom_set_device_mode(hdev, 2, 2, 2);
                }
@@ -425,7 +441,6 @@ static void wacom_retrieve_hid_descriptor(struct hid_device *hdev,
        struct usb_interface *intf = wacom->intf;
 
        /* default features */
-       features->device_type = BTN_TOOL_PEN;
        features->x_fuzz = 4;
        features->y_fuzz = 4;
        features->pressure_fuzz = 0;
@@ -439,17 +454,13 @@ static void wacom_retrieve_hid_descriptor(struct hid_device *hdev,
         */
        if (features->type == WIRELESS) {
                if (intf->cur_altsetting->desc.bInterfaceNumber == 0) {
-                       features->device_type = 0;
+                       features->device_type = WACOM_DEVICETYPE_NONE;
                } else if (intf->cur_altsetting->desc.bInterfaceNumber == 2) {
-                       features->device_type = BTN_TOOL_FINGER;
+                       features->device_type |= WACOM_DEVICETYPE_TOUCH;
                        features->pktlen = WACOM_PKGLEN_BBTOUCH3;
                }
        }
 
-       /* only devices that support touch need to retrieve the info */
-       if (features->type < BAMBOO_PT)
-               return;
-
        wacom_parse_hid(hdev, features);
 }
 
@@ -527,9 +538,9 @@ static int wacom_add_shared_data(struct hid_device *hdev)
 
        wacom_wac->shared = &data->shared;
 
-       if (wacom_wac->features.device_type == BTN_TOOL_FINGER)
+       if (wacom_wac->features.device_type & WACOM_DEVICETYPE_TOUCH)
                wacom_wac->shared->touch = hdev;
-       else if (wacom_wac->features.device_type == BTN_TOOL_PEN)
+       else if (wacom_wac->features.device_type & WACOM_DEVICETYPE_PEN)
                wacom_wac->shared->pen = hdev;
 
 out:
@@ -848,6 +859,9 @@ static int wacom_initialize_leds(struct wacom *wacom)
 {
        int error;
 
+       if (!(wacom->wacom_wac.features.device_type & WACOM_DEVICETYPE_PAD))
+               return 0;
+
        /* Initialize default values */
        switch (wacom->wacom_wac.features.type) {
        case INTUOS4S:
@@ -881,17 +895,14 @@ static int wacom_initialize_leds(struct wacom *wacom)
        case INTUOSPS:
        case INTUOSPM:
        case INTUOSPL:
-               if (wacom->wacom_wac.features.device_type == BTN_TOOL_PEN) {
-                       wacom->led.select[0] = 0;
-                       wacom->led.select[1] = 0;
-                       wacom->led.llv = 32;
-                       wacom->led.hlv = 0;
-                       wacom->led.img_lum = 0;
-
-                       error = sysfs_create_group(&wacom->hdev->dev.kobj,
-                                                 &intuos5_led_attr_group);
-               } else
-                       return 0;
+               wacom->led.select[0] = 0;
+               wacom->led.select[1] = 0;
+               wacom->led.llv = 32;
+               wacom->led.hlv = 0;
+               wacom->led.img_lum = 0;
+
+               error = sysfs_create_group(&wacom->hdev->dev.kobj,
+                                         &intuos5_led_attr_group);
                break;
 
        default:
@@ -914,6 +925,9 @@ static void wacom_destroy_leds(struct wacom *wacom)
        if (!wacom->led_initialized)
                return;
 
+       if (!(wacom->wacom_wac.features.device_type & WACOM_DEVICETYPE_PAD))
+               return;
+
        wacom->led_initialized = false;
 
        switch (wacom->wacom_wac.features.type) {
@@ -937,9 +951,8 @@ static void wacom_destroy_leds(struct wacom *wacom)
        case INTUOSPS:
        case INTUOSPM:
        case INTUOSPL:
-               if (wacom->wacom_wac.features.device_type == BTN_TOOL_PEN)
-                       sysfs_remove_group(&wacom->hdev->dev.kobj,
-                                          &intuos5_led_attr_group);
+               sysfs_remove_group(&wacom->hdev->dev.kobj,
+                                  &intuos5_led_attr_group);
                break;
        }
 }
@@ -1117,7 +1130,7 @@ static struct input_dev *wacom_allocate_input(struct wacom *wacom)
        if (!input_dev)
                return NULL;
 
-       input_dev->name = wacom_wac->name;
+       input_dev->name = wacom_wac->pen_name;
        input_dev->phys = hdev->phys;
        input_dev->dev.parent = &hdev->dev;
        input_dev->open = wacom_open;
@@ -1136,27 +1149,33 @@ static void wacom_free_inputs(struct wacom *wacom)
 {
        struct wacom_wac *wacom_wac = &(wacom->wacom_wac);
 
-       if (wacom_wac->input)
-               input_free_device(wacom_wac->input);
+       if (wacom_wac->pen_input)
+               input_free_device(wacom_wac->pen_input);
+       if (wacom_wac->touch_input)
+               input_free_device(wacom_wac->touch_input);
        if (wacom_wac->pad_input)
                input_free_device(wacom_wac->pad_input);
-       wacom_wac->input = NULL;
+       wacom_wac->pen_input = NULL;
+       wacom_wac->touch_input = NULL;
        wacom_wac->pad_input = NULL;
 }
 
 static int wacom_allocate_inputs(struct wacom *wacom)
 {
-       struct input_dev *input_dev, *pad_input_dev;
+       struct input_dev *pen_input_dev, *touch_input_dev, *pad_input_dev;
        struct wacom_wac *wacom_wac = &(wacom->wacom_wac);
 
-       input_dev = wacom_allocate_input(wacom);
+       pen_input_dev = wacom_allocate_input(wacom);
+       touch_input_dev = wacom_allocate_input(wacom);
        pad_input_dev = wacom_allocate_input(wacom);
-       if (!input_dev || !pad_input_dev) {
+       if (!pen_input_dev || !touch_input_dev || !pad_input_dev) {
                wacom_free_inputs(wacom);
                return -ENOMEM;
        }
 
-       wacom_wac->input = input_dev;
+       wacom_wac->pen_input = pen_input_dev;
+       wacom_wac->touch_input = touch_input_dev;
+       wacom_wac->touch_input->name = wacom_wac->touch_name;
        wacom_wac->pad_input = pad_input_dev;
        wacom_wac->pad_input->name = wacom_wac->pad_name;
 
@@ -1165,11 +1184,17 @@ static int wacom_allocate_inputs(struct wacom *wacom)
 
 static void wacom_clean_inputs(struct wacom *wacom)
 {
-       if (wacom->wacom_wac.input) {
-               if (wacom->wacom_wac.input_registered)
-                       input_unregister_device(wacom->wacom_wac.input);
+       if (wacom->wacom_wac.pen_input) {
+               if (wacom->wacom_wac.pen_registered)
+                       input_unregister_device(wacom->wacom_wac.pen_input);
                else
-                       input_free_device(wacom->wacom_wac.input);
+                       input_free_device(wacom->wacom_wac.pen_input);
+       }
+       if (wacom->wacom_wac.touch_input) {
+               if (wacom->wacom_wac.touch_registered)
+                       input_unregister_device(wacom->wacom_wac.touch_input);
+               else
+                       input_free_device(wacom->wacom_wac.touch_input);
        }
        if (wacom->wacom_wac.pad_input) {
                if (wacom->wacom_wac.pad_registered)
@@ -1177,29 +1202,49 @@ static void wacom_clean_inputs(struct wacom *wacom)
                else
                        input_free_device(wacom->wacom_wac.pad_input);
        }
-       wacom->wacom_wac.input = NULL;
+       wacom->wacom_wac.pen_input = NULL;
+       wacom->wacom_wac.touch_input = NULL;
        wacom->wacom_wac.pad_input = NULL;
        wacom_destroy_leds(wacom);
 }
 
 static int wacom_register_inputs(struct wacom *wacom)
 {
-       struct input_dev *input_dev, *pad_input_dev;
+       struct input_dev *pen_input_dev, *touch_input_dev, *pad_input_dev;
        struct wacom_wac *wacom_wac = &(wacom->wacom_wac);
-       int error;
+       int error = 0;
 
-       input_dev = wacom_wac->input;
+       pen_input_dev = wacom_wac->pen_input;
+       touch_input_dev = wacom_wac->touch_input;
        pad_input_dev = wacom_wac->pad_input;
 
-       if (!input_dev || !pad_input_dev)
+       if (!pen_input_dev || !touch_input_dev || !pad_input_dev)
                return -EINVAL;
 
-       error = wacom_setup_pentouch_input_capabilities(input_dev, wacom_wac);
-       if (!error) {
-               error = input_register_device(input_dev);
+       error = wacom_setup_pen_input_capabilities(pen_input_dev, wacom_wac);
+       if (error) {
+               /* no pen in use on this interface */
+               input_free_device(pen_input_dev);
+               wacom_wac->pen_input = NULL;
+               pen_input_dev = NULL;
+       } else {
+               error = input_register_device(pen_input_dev);
+               if (error)
+                       goto fail_register_pen_input;
+               wacom_wac->pen_registered = true;
+       }
+
+       error = wacom_setup_touch_input_capabilities(touch_input_dev, wacom_wac);
+       if (error) {
+               /* no touch in use on this interface */
+               input_free_device(touch_input_dev);
+               wacom_wac->touch_input = NULL;
+               touch_input_dev = NULL;
+       } else {
+               error = input_register_device(touch_input_dev);
                if (error)
-                       return error;
-               wacom_wac->input_registered = true;
+                       goto fail_register_touch_input;
+               wacom_wac->touch_registered = true;
        }
 
        error = wacom_setup_pad_input_capabilities(pad_input_dev, wacom_wac);
@@ -1226,9 +1271,14 @@ fail_leds:
        pad_input_dev = NULL;
        wacom_wac->pad_registered = false;
 fail_register_pad_input:
-       input_unregister_device(input_dev);
-       wacom_wac->input = NULL;
-       wacom_wac->input_registered = false;
+       input_unregister_device(touch_input_dev);
+       wacom_wac->touch_input = NULL;
+       wacom_wac->touch_registered = false;
+fail_register_touch_input:
+       input_unregister_device(pen_input_dev);
+       wacom_wac->pen_input = NULL;
+       wacom_wac->pen_registered = false;
+fail_register_pen_input:
        return error;
 }
 
@@ -1285,8 +1335,11 @@ static void wacom_wireless_work(struct work_struct *work)
                /* Stylus interface */
                wacom_wac1->features =
                        *((struct wacom_features *)id->driver_data);
-               wacom_wac1->features.device_type = BTN_TOOL_PEN;
-               snprintf(wacom_wac1->name, WACOM_NAME_MAX, "%s (WL) Pen",
+               wacom_wac1->features.device_type |= WACOM_DEVICETYPE_PEN;
+               if (wacom_wac1->features.type != INTUOSHT &&
+                   wacom_wac1->features.type != BAMBOO_PT)
+                       wacom_wac1->features.device_type |= WACOM_DEVICETYPE_PAD;
+               snprintf(wacom_wac1->pen_name, WACOM_NAME_MAX, "%s (WL) Pen",
                         wacom_wac1->features.name);
                snprintf(wacom_wac1->pad_name, WACOM_NAME_MAX, "%s (WL) Pad",
                         wacom_wac1->features.name);
@@ -1304,16 +1357,16 @@ static void wacom_wireless_work(struct work_struct *work)
                        wacom_wac2->features =
                                *((struct wacom_features *)id->driver_data);
                        wacom_wac2->features.pktlen = WACOM_PKGLEN_BBTOUCH3;
-                       wacom_wac2->features.device_type = BTN_TOOL_FINGER;
                        wacom_wac2->features.x_max = wacom_wac2->features.y_max = 4096;
-                       if (wacom_wac2->features.touch_max)
-                               snprintf(wacom_wac2->name, WACOM_NAME_MAX,
-                                        "%s (WL) Finger",wacom_wac2->features.name);
-                       else
-                               snprintf(wacom_wac2->name, WACOM_NAME_MAX,
-                                        "%s (WL) Pad",wacom_wac2->features.name);
+                       snprintf(wacom_wac2->touch_name, WACOM_NAME_MAX,
+                                "%s (WL) Finger",wacom_wac2->features.name);
                        snprintf(wacom_wac2->pad_name, WACOM_NAME_MAX,
-                                "%s (WL) Pad", wacom_wac2->features.name);
+                                "%s (WL) Pad",wacom_wac2->features.name);
+                       if (wacom_wac1->features.touch_max)
+                               wacom_wac2->features.device_type |= WACOM_DEVICETYPE_TOUCH;
+                       if (wacom_wac1->features.type == INTUOSHT ||
+                           wacom_wac1->features.type == BAMBOO_PT)
+                               wacom_wac2->features.device_type |= WACOM_DEVICETYPE_PAD;
                        wacom_wac2->pid = wacom_wac->pid;
                        error = wacom_allocate_inputs(wacom2) ||
                                wacom_register_inputs(wacom2);
@@ -1322,7 +1375,7 @@ static void wacom_wireless_work(struct work_struct *work)
 
                        if (wacom_wac1->features.type == INTUOSHT &&
                            wacom_wac1->features.touch_max)
-                               wacom_wac->shared->touch_input = wacom_wac2->input;
+                               wacom_wac->shared->touch_input = wacom_wac2->touch_input;
                }
 
                error = wacom_initialize_battery(wacom);
@@ -1369,6 +1422,12 @@ static void wacom_set_default_phy(struct wacom_features *features)
 
 static void wacom_calculate_res(struct wacom_features *features)
 {
+       /* set unit to "100th of a mm" for devices not reported by HID */
+       if (!features->unit) {
+               features->unit = 0x11;
+               features->unitExpo = -3;
+       }
+
        features->x_resolution = wacom_calc_hid_res(features->x_max,
                                                    features->x_phy,
                                                    features->unit,
@@ -1396,6 +1455,49 @@ static size_t wacom_compute_pktlen(struct hid_device *hdev)
        return size;
 }
 
+static void wacom_update_name(struct wacom *wacom)
+{
+       struct wacom_wac *wacom_wac = &wacom->wacom_wac;
+       struct wacom_features *features = &wacom_wac->features;
+       char name[WACOM_NAME_MAX];
+
+       /* Generic devices name unspecified */
+       if ((features->type == HID_GENERIC) && !strcmp("Wacom HID", features->name)) {
+               if (strstr(wacom->hdev->name, "Wacom") ||
+                   strstr(wacom->hdev->name, "wacom") ||
+                   strstr(wacom->hdev->name, "WACOM")) {
+                       /* name is in HID descriptor, use it */
+                       strlcpy(name, wacom->hdev->name, sizeof(name));
+
+                       /* strip out excess whitespaces */
+                       while (1) {
+                               char *gap = strstr(name, "  ");
+                               if (gap == NULL)
+                                       break;
+                               /* shift everything including the terminator */
+                               memmove(gap, gap+1, strlen(gap));
+                       }
+                       /* get rid of trailing whitespace */
+                       if (name[strlen(name)-1] == ' ')
+                               name[strlen(name)-1] = '\0';
+               } else {
+                       /* no meaningful name retrieved. use product ID */
+                       snprintf(name, sizeof(name),
+                                "%s %X", features->name, wacom->hdev->product);
+               }
+       } else {
+               strlcpy(name, features->name, sizeof(name));
+       }
+
+       /* Append the device type to the name */
+       snprintf(wacom_wac->pen_name, sizeof(wacom_wac->pen_name),
+               "%s Pen", name);
+       snprintf(wacom_wac->touch_name, sizeof(wacom_wac->touch_name),
+               "%s Finger", name);
+       snprintf(wacom_wac->pad_name, sizeof(wacom_wac->pad_name),
+               "%s Pad", name);
+}
+
 static int wacom_probe(struct hid_device *hdev,
                const struct hid_device_id *id)
 {
@@ -1474,64 +1576,25 @@ static int wacom_probe(struct hid_device *hdev,
 
        /* Retrieve the physical and logical size for touch devices */
        wacom_retrieve_hid_descriptor(hdev, features);
+       wacom_setup_device_quirks(wacom);
 
-       /*
-        * Intuos5 has no useful data about its touch interface in its
-        * HID descriptor. If this is the touch interface (PacketSize
-        * of WACOM_PKGLEN_BBTOUCH3), override the table values.
-        */
-       if (features->type >= INTUOS5S && features->type <= INTUOSHT) {
-               if (features->pktlen == WACOM_PKGLEN_BBTOUCH3) {
-                       features->device_type = BTN_TOOL_FINGER;
+       if (features->device_type == WACOM_DEVICETYPE_NONE &&
+           features->type != WIRELESS) {
+               error = features->type == HID_GENERIC ? -ENODEV : 0;
 
-                       features->x_max = 4096;
-                       features->y_max = 4096;
-               } else {
-                       features->device_type = BTN_TOOL_PEN;
-               }
-       }
+               dev_warn(&hdev->dev, "Unknown device_type for '%s'. %s.",
+                        hdev->name,
+                        error ? "Ignoring" : "Assuming pen");
 
-       /*
-        * Same thing for Bamboo 3rd gen.
-        */
-       if ((features->type == BAMBOO_PT) &&
-           (features->pktlen == WACOM_PKGLEN_BBTOUCH3) &&
-           (features->device_type == BTN_TOOL_PEN)) {
-               features->device_type = BTN_TOOL_FINGER;
+               if (error)
+                       goto fail_shared_data;
 
-               features->x_max = 4096;
-               features->y_max = 4096;
+               features->device_type |= WACOM_DEVICETYPE_PEN;
        }
 
-       /*
-        * Same thing for Bamboo PAD
-        */
-       if (features->type == BAMBOO_PAD)
-               features->device_type = BTN_TOOL_FINGER;
-
-       if (hdev->bus == BUS_BLUETOOTH)
-               features->quirks |= WACOM_QUIRK_BATTERY;
-
-       wacom_setup_device_quirks(features);
-
-       /* set unit to "100th of a mm" for devices not reported by HID */
-       if (!features->unit) {
-               features->unit = 0x11;
-               features->unitExpo = -3;
-       }
        wacom_calculate_res(features);
 
-       strlcpy(wacom_wac->name, features->name, sizeof(wacom_wac->name));
-       snprintf(wacom_wac->pad_name, sizeof(wacom_wac->pad_name),
-               "%s Pad", features->name);
-
-       /* Append the device type to the name */
-       if (features->device_type != BTN_TOOL_FINGER)
-               strlcat(wacom_wac->name, " Pen", WACOM_NAME_MAX);
-       else if (features->touch_max)
-               strlcat(wacom_wac->name, " Finger", WACOM_NAME_MAX);
-       else
-               strlcat(wacom_wac->name, " Pad", WACOM_NAME_MAX);
+       wacom_update_name(wacom);
 
        error = wacom_add_shared_data(hdev);
        if (error)
@@ -1574,9 +1637,9 @@ static int wacom_probe(struct hid_device *hdev,
        if (features->quirks & WACOM_QUIRK_MONITOR)
                error = hid_hw_open(hdev);
 
-       if (wacom_wac->features.type == INTUOSHT && wacom_wac->features.touch_max) {
-               if (wacom_wac->features.device_type == BTN_TOOL_FINGER)
-                       wacom_wac->shared->touch_input = wacom_wac->input;
+       if (wacom_wac->features.type == INTUOSHT && 
+           wacom_wac->features.device_type & WACOM_DEVICETYPE_TOUCH) {
+                       wacom_wac->shared->touch_input = wacom_wac->touch_input;
        }
 
        return 0;
index adf959dcfa5df9da7bca7414e2d4ac74a35e1457..232da89f4e886fe02b82d452c1a0868f0b65b967 100644 (file)
@@ -69,7 +69,7 @@ static void wacom_notify_battery(struct wacom_wac *wacom_wac,
 static int wacom_penpartner_irq(struct wacom_wac *wacom)
 {
        unsigned char *data = wacom->data;
-       struct input_dev *input = wacom->input;
+       struct input_dev *input = wacom->pen_input;
 
        switch (data[0]) {
        case 1:
@@ -114,7 +114,7 @@ static int wacom_pl_irq(struct wacom_wac *wacom)
 {
        struct wacom_features *features = &wacom->features;
        unsigned char *data = wacom->data;
-       struct input_dev *input = wacom->input;
+       struct input_dev *input = wacom->pen_input;
        int prox, pressure;
 
        if (data[0] != WACOM_REPORT_PENABLED) {
@@ -186,7 +186,7 @@ static int wacom_pl_irq(struct wacom_wac *wacom)
 static int wacom_ptu_irq(struct wacom_wac *wacom)
 {
        unsigned char *data = wacom->data;
-       struct input_dev *input = wacom->input;
+       struct input_dev *input = wacom->pen_input;
 
        if (data[0] != WACOM_REPORT_PENABLED) {
                dev_dbg(input->dev.parent,
@@ -215,7 +215,7 @@ static int wacom_ptu_irq(struct wacom_wac *wacom)
 static int wacom_dtu_irq(struct wacom_wac *wacom)
 {
        unsigned char *data = wacom->data;
-       struct input_dev *input = wacom->input;
+       struct input_dev *input = wacom->pen_input;
        int prox = data[1] & 0x20;
 
        dev_dbg(input->dev.parent,
@@ -245,7 +245,7 @@ static int wacom_dtu_irq(struct wacom_wac *wacom)
 static int wacom_dtus_irq(struct wacom_wac *wacom)
 {
        char *data = wacom->data;
-       struct input_dev *input = wacom->input;
+       struct input_dev *input = wacom->pen_input;
        unsigned short prox, pressure = 0;
 
        if (data[0] != WACOM_REPORT_DTUS && data[0] != WACOM_REPORT_DTUSPAD) {
@@ -297,7 +297,7 @@ static int wacom_graphire_irq(struct wacom_wac *wacom)
 {
        struct wacom_features *features = &wacom->features;
        unsigned char *data = wacom->data;
-       struct input_dev *input = wacom->input;
+       struct input_dev *input = wacom->pen_input;
        struct input_dev *pad_input = wacom->pad_input;
        int battery_capacity, ps_connected;
        int prox;
@@ -464,7 +464,7 @@ static int wacom_intuos_inout(struct wacom_wac *wacom)
 {
        struct wacom_features *features = &wacom->features;
        unsigned char *data = wacom->data;
-       struct input_dev *input = wacom->input;
+       struct input_dev *input = wacom->pen_input;
        int idx = 0;
 
        /* tool number */
@@ -649,7 +649,7 @@ static void wacom_intuos_general(struct wacom_wac *wacom)
 {
        struct wacom_features *features = &wacom->features;
        unsigned char *data = wacom->data;
-       struct input_dev *input = wacom->input;
+       struct input_dev *input = wacom->pen_input;
        unsigned int t;
 
        /* general pen packet */
@@ -681,7 +681,7 @@ static int wacom_intuos_irq(struct wacom_wac *wacom)
 {
        struct wacom_features *features = &wacom->features;
        unsigned char *data = wacom->data;
-       struct input_dev *input = wacom->input;
+       struct input_dev *input = wacom->pen_input;
        unsigned int t;
        int idx = 0, result;
 
@@ -1025,7 +1025,7 @@ static void wacom_intuos_bt_process_data(struct wacom_wac *wacom,
        memcpy(wacom->data, data, 10);
        wacom_intuos_irq(wacom);
 
-       input_sync(wacom->input);
+       input_sync(wacom->pen_input);
        if (wacom->pad_input)
                input_sync(wacom->pad_input);
 }
@@ -1057,7 +1057,7 @@ static int wacom_intuos_bt_irq(struct wacom_wac *wacom, size_t len)
                                     ps_connected);
                break;
        default:
-               dev_dbg(wacom->input->dev.parent,
+               dev_dbg(wacom->pen_input->dev.parent,
                                "Unknown report: %d,%d size:%zu\n",
                                data[0], data[1], len);
                return 0;
@@ -1067,7 +1067,7 @@ static int wacom_intuos_bt_irq(struct wacom_wac *wacom, size_t len)
 
 static int wacom_wac_finger_count_touches(struct wacom_wac *wacom)
 {
-       struct input_dev *input = wacom->input;
+       struct input_dev *input = wacom->touch_input;
        unsigned touch_max = wacom->features.touch_max;
        int count = 0;
        int i;
@@ -1075,9 +1075,8 @@ static int wacom_wac_finger_count_touches(struct wacom_wac *wacom)
        if (!touch_max)
                return 0;
 
-       /* non-HID_GENERIC single touch input doesn't call this routine */
-       if ((touch_max == 1) && (wacom->features.type == HID_GENERIC))
-               return wacom->hid_data.tipswitch &&
+       if (touch_max == 1)
+               return test_bit(BTN_TOUCH, input->key) &&
                       !wacom->shared->stylus_in_proximity;
 
        for (i = 0; i < input->mt->num_slots; i++) {
@@ -1092,7 +1091,7 @@ static int wacom_wac_finger_count_touches(struct wacom_wac *wacom)
 
 static int wacom_24hdt_irq(struct wacom_wac *wacom)
 {
-       struct input_dev *input = wacom->input;
+       struct input_dev *input = wacom->touch_input;
        unsigned char *data = wacom->data;
        int i;
        int current_num_contacts = data[61];
@@ -1160,7 +1159,7 @@ static int wacom_24hdt_irq(struct wacom_wac *wacom)
 
 static int wacom_mt_touch(struct wacom_wac *wacom)
 {
-       struct input_dev *input = wacom->input;
+       struct input_dev *input = wacom->touch_input;
        unsigned char *data = wacom->data;
        int i;
        int current_num_contacts = data[2];
@@ -1211,7 +1210,7 @@ static int wacom_mt_touch(struct wacom_wac *wacom)
 
 static int wacom_tpc_mt_touch(struct wacom_wac *wacom)
 {
-       struct input_dev *input = wacom->input;
+       struct input_dev *input = wacom->touch_input;
        unsigned char *data = wacom->data;
        int i;
 
@@ -1240,7 +1239,7 @@ static int wacom_tpc_mt_touch(struct wacom_wac *wacom)
 static int wacom_tpc_single_touch(struct wacom_wac *wacom, size_t len)
 {
        unsigned char *data = wacom->data;
-       struct input_dev *input = wacom->input;
+       struct input_dev *input = wacom->touch_input;
        bool prox = !wacom->shared->stylus_in_proximity;
        int x = 0, y = 0;
 
@@ -1276,7 +1275,7 @@ static int wacom_tpc_single_touch(struct wacom_wac *wacom, size_t len)
 static int wacom_tpc_pen(struct wacom_wac *wacom)
 {
        unsigned char *data = wacom->data;
-       struct input_dev *input = wacom->input;
+       struct input_dev *input = wacom->pen_input;
        bool prox = data[1] & 0x20;
 
        if (!wacom->shared->stylus_in_proximity) /* first in prox */
@@ -1305,8 +1304,12 @@ static int wacom_tpc_irq(struct wacom_wac *wacom, size_t len)
 {
        unsigned char *data = wacom->data;
 
-       dev_dbg(wacom->input->dev.parent,
-               "%s: received report #%d\n", __func__, data[0]);
+       if (wacom->pen_input)
+               dev_dbg(wacom->pen_input->dev.parent,
+                       "%s: received report #%d\n", __func__, data[0]);
+       else if (wacom->touch_input)
+               dev_dbg(wacom->touch_input->dev.parent,
+                       "%s: received report #%d\n", __func__, data[0]);
 
        switch (len) {
        case WACOM_PKGLEN_TPC1FG:
@@ -1338,11 +1341,9 @@ static int wacom_tpc_irq(struct wacom_wac *wacom, size_t len)
        return 0;
 }
 
-static void wacom_map_usage(struct wacom *wacom, struct hid_usage *usage,
+static void wacom_map_usage(struct input_dev *input, struct hid_usage *usage,
                struct hid_field *field, __u8 type, __u16 code, int fuzz)
 {
-       struct wacom_wac *wacom_wac = &wacom->wacom_wac;
-       struct input_dev *input = wacom_wac->input;
        int fmin = field->logical_minimum;
        int fmax = field->logical_maximum;
 
@@ -1370,36 +1371,38 @@ static void wacom_wac_pen_usage_mapping(struct hid_device *hdev,
                struct hid_field *field, struct hid_usage *usage)
 {
        struct wacom *wacom = hid_get_drvdata(hdev);
+       struct wacom_wac *wacom_wac = &wacom->wacom_wac;
+       struct input_dev *input = wacom_wac->pen_input;
 
        switch (usage->hid) {
        case HID_GD_X:
-               wacom_map_usage(wacom, usage, field, EV_ABS, ABS_X, 4);
+               wacom_map_usage(input, usage, field, EV_ABS, ABS_X, 4);
                break;
        case HID_GD_Y:
-               wacom_map_usage(wacom, usage, field, EV_ABS, ABS_Y, 4);
+               wacom_map_usage(input, usage, field, EV_ABS, ABS_Y, 4);
                break;
        case HID_DG_TIPPRESSURE:
-               wacom_map_usage(wacom, usage, field, EV_ABS, ABS_PRESSURE, 0);
+               wacom_map_usage(input, usage, field, EV_ABS, ABS_PRESSURE, 0);
                break;
        case HID_DG_INRANGE:
-               wacom_map_usage(wacom, usage, field, EV_KEY, BTN_TOOL_PEN, 0);
+               wacom_map_usage(input, usage, field, EV_KEY, BTN_TOOL_PEN, 0);
                break;
        case HID_DG_INVERT:
-               wacom_map_usage(wacom, usage, field, EV_KEY,
+               wacom_map_usage(input, usage, field, EV_KEY,
                                BTN_TOOL_RUBBER, 0);
                break;
        case HID_DG_ERASER:
        case HID_DG_TIPSWITCH:
-               wacom_map_usage(wacom, usage, field, EV_KEY, BTN_TOUCH, 0);
+               wacom_map_usage(input, usage, field, EV_KEY, BTN_TOUCH, 0);
                break;
        case HID_DG_BARRELSWITCH:
-               wacom_map_usage(wacom, usage, field, EV_KEY, BTN_STYLUS, 0);
+               wacom_map_usage(input, usage, field, EV_KEY, BTN_STYLUS, 0);
                break;
        case HID_DG_BARRELSWITCH2:
-               wacom_map_usage(wacom, usage, field, EV_KEY, BTN_STYLUS2, 0);
+               wacom_map_usage(input, usage, field, EV_KEY, BTN_STYLUS2, 0);
                break;
        case HID_DG_TOOLSERIALNUMBER:
-               wacom_map_usage(wacom, usage, field, EV_MSC, MSC_SERIAL, 0);
+               wacom_map_usage(input, usage, field, EV_MSC, MSC_SERIAL, 0);
                break;
        }
 }
@@ -1409,7 +1412,7 @@ static int wacom_wac_pen_event(struct hid_device *hdev, struct hid_field *field,
 {
        struct wacom *wacom = hid_get_drvdata(hdev);
        struct wacom_wac *wacom_wac = &wacom->wacom_wac;
-       struct input_dev *input = wacom_wac->input;
+       struct input_dev *input = wacom_wac->pen_input;
 
        /* checking which Tool / tip switch to send */
        switch (usage->hid) {
@@ -1439,7 +1442,7 @@ static void wacom_wac_pen_report(struct hid_device *hdev,
 {
        struct wacom *wacom = hid_get_drvdata(hdev);
        struct wacom_wac *wacom_wac = &wacom->wacom_wac;
-       struct input_dev *input = wacom_wac->input;
+       struct input_dev *input = wacom_wac->pen_input;
        bool prox = wacom_wac->hid_data.inrange_state;
 
        if (!wacom_wac->shared->stylus_in_proximity) /* first in prox */
@@ -1468,23 +1471,24 @@ static void wacom_wac_finger_usage_mapping(struct hid_device *hdev,
        struct wacom *wacom = hid_get_drvdata(hdev);
        struct wacom_wac *wacom_wac = &wacom->wacom_wac;
        struct wacom_features *features = &wacom_wac->features;
+       struct input_dev *input = wacom_wac->touch_input;
        unsigned touch_max = wacom_wac->features.touch_max;
 
        switch (usage->hid) {
        case HID_GD_X:
                features->last_slot_field = usage->hid;
                if (touch_max == 1)
-                       wacom_map_usage(wacom, usage, field, EV_ABS, ABS_X, 4);
+                       wacom_map_usage(input, usage, field, EV_ABS, ABS_X, 4);
                else
-                       wacom_map_usage(wacom, usage, field, EV_ABS,
+                       wacom_map_usage(input, usage, field, EV_ABS,
                                        ABS_MT_POSITION_X, 4);
                break;
        case HID_GD_Y:
                features->last_slot_field = usage->hid;
                if (touch_max == 1)
-                       wacom_map_usage(wacom, usage, field, EV_ABS, ABS_Y, 4);
+                       wacom_map_usage(input, usage, field, EV_ABS, ABS_Y, 4);
                else
-                       wacom_map_usage(wacom, usage, field, EV_ABS,
+                       wacom_map_usage(input, usage, field, EV_ABS,
                                        ABS_MT_POSITION_Y, 4);
                break;
        case HID_DG_CONTACTID:
@@ -1498,7 +1502,7 @@ static void wacom_wac_finger_usage_mapping(struct hid_device *hdev,
                break;
        case HID_DG_TIPSWITCH:
                features->last_slot_field = usage->hid;
-               wacom_map_usage(wacom, usage, field, EV_KEY, BTN_TOUCH, 0);
+               wacom_map_usage(input, usage, field, EV_KEY, BTN_TOUCH, 0);
                break;
        }
 }
@@ -1554,7 +1558,7 @@ static int wacom_wac_finger_event(struct hid_device *hdev,
 
        if (usage->usage_index + 1 == field->report_count) {
                if (usage->hid == wacom_wac->features.last_slot_field)
-                       wacom_wac_finger_slot(wacom_wac, wacom_wac->input);
+                       wacom_wac_finger_slot(wacom_wac, wacom_wac->touch_input);
        }
 
        return 0;
@@ -1565,7 +1569,7 @@ static void wacom_wac_finger_report(struct hid_device *hdev,
 {
        struct wacom *wacom = hid_get_drvdata(hdev);
        struct wacom_wac *wacom_wac = &wacom->wacom_wac;
-       struct input_dev *input = wacom_wac->input;
+       struct input_dev *input = wacom_wac->touch_input;
        unsigned touch_max = wacom_wac->features.touch_max;
 
        if (touch_max > 1)
@@ -1582,10 +1586,10 @@ void wacom_wac_usage_mapping(struct hid_device *hdev,
 {
        struct wacom *wacom = hid_get_drvdata(hdev);
        struct wacom_wac *wacom_wac = &wacom->wacom_wac;
-       struct input_dev *input = wacom_wac->input;
 
        /* currently, only direct devices have proper hid report descriptors */
-       __set_bit(INPUT_PROP_DIRECT, input->propbit);
+       __set_bit(INPUT_PROP_DIRECT, wacom_wac->pen_input->propbit);
+       __set_bit(INPUT_PROP_DIRECT, wacom_wac->touch_input->propbit);
 
        if (WACOM_PEN_FIELD(field))
                return wacom_wac_pen_usage_mapping(hdev, field, usage);
@@ -1630,7 +1634,7 @@ void wacom_wac_report(struct hid_device *hdev, struct hid_report *report)
 static int wacom_bpt_touch(struct wacom_wac *wacom)
 {
        struct wacom_features *features = &wacom->features;
-       struct input_dev *input = wacom->input;
+       struct input_dev *input = wacom->touch_input;
        struct input_dev *pad_input = wacom->pad_input;
        unsigned char *data = wacom->data;
        int i;
@@ -1678,7 +1682,7 @@ static int wacom_bpt_touch(struct wacom_wac *wacom)
 static void wacom_bpt3_touch_msg(struct wacom_wac *wacom, unsigned char *data)
 {
        struct wacom_features *features = &wacom->features;
-       struct input_dev *input = wacom->input;
+       struct input_dev *input = wacom->touch_input;
        bool touch = data[1] & 0x80;
        int slot = input_mt_get_slot_by_key(input, data[0]);
 
@@ -1736,7 +1740,6 @@ static void wacom_bpt3_button_msg(struct wacom_wac *wacom, unsigned char *data)
 
 static int wacom_bpt3_touch(struct wacom_wac *wacom)
 {
-       struct input_dev *input = wacom->input;
        unsigned char *data = wacom->data;
        int count = data[1] & 0x07;
        int i;
@@ -1755,8 +1758,12 @@ static int wacom_bpt3_touch(struct wacom_wac *wacom)
                        wacom_bpt3_button_msg(wacom, data + offset);
 
        }
-       input_mt_sync_frame(input);
-       wacom->shared->touch_down = wacom_wac_finger_count_touches(wacom);
+
+       /* only update the touch if we actually have a touchpad */
+       if (wacom->touch_registered) {
+               input_mt_sync_frame(wacom->touch_input);
+               wacom->shared->touch_down = wacom_wac_finger_count_touches(wacom);
+       }
 
        return 1;
 }
@@ -1764,7 +1771,7 @@ static int wacom_bpt3_touch(struct wacom_wac *wacom)
 static int wacom_bpt_pen(struct wacom_wac *wacom)
 {
        struct wacom_features *features = &wacom->features;
-       struct input_dev *input = wacom->input;
+       struct input_dev *input = wacom->pen_input;
        unsigned char *data = wacom->data;
        int prox = 0, x = 0, y = 0, p = 0, d = 0, pen = 0, btn1 = 0, btn2 = 0;
 
@@ -1873,7 +1880,7 @@ static void wacom_bamboo_pad_pen_event(struct wacom_wac *wacom,
 static int wacom_bamboo_pad_touch_event(struct wacom_wac *wacom,
                unsigned char *data)
 {
-       struct input_dev *input = wacom->input;
+       struct input_dev *input = wacom->touch_input;
        unsigned char *finger_data, prefix;
        unsigned id;
        int x, y;
@@ -2117,7 +2124,10 @@ void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len)
        }
 
        if (sync) {
-               input_sync(wacom_wac->input);
+               if (wacom_wac->pen_input)
+                       input_sync(wacom_wac->pen_input);
+               if (wacom_wac->touch_input)
+                       input_sync(wacom_wac->touch_input);
                if (wacom_wac->pad_input)
                        input_sync(wacom_wac->pad_input);
        }
@@ -2125,7 +2135,7 @@ void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len)
 
 static void wacom_setup_cintiq(struct wacom_wac *wacom_wac)
 {
-       struct input_dev *input_dev = wacom_wac->input;
+       struct input_dev *input_dev = wacom_wac->pen_input;
 
        input_set_capability(input_dev, EV_MSC, MSC_SERIAL);
 
@@ -2148,7 +2158,7 @@ static void wacom_setup_cintiq(struct wacom_wac *wacom_wac)
 
 static void wacom_setup_intuos(struct wacom_wac *wacom_wac)
 {
-       struct input_dev *input_dev = wacom_wac->input;
+       struct input_dev *input_dev = wacom_wac->pen_input;
 
        input_set_capability(input_dev, EV_REL, REL_WHEEL);
 
@@ -2167,15 +2177,57 @@ static void wacom_setup_intuos(struct wacom_wac *wacom_wac)
        input_set_abs_params(input_dev, ABS_THROTTLE, -1023, 1023, 0, 0);
 }
 
-void wacom_setup_device_quirks(struct wacom_features *features)
+void wacom_setup_device_quirks(struct wacom *wacom)
 {
+       struct wacom_features *features = &wacom->wacom_wac.features;
+
+       /* The pen and pad share the same interface on most devices */
+       if (features->type == GRAPHIRE_BT || features->type == WACOM_G4 ||
+           features->type == DTUS || features->type == WACOM_MO ||
+           (features->type >= INTUOS3S && features->type <= WACOM_13HD && 
+            features->type != INTUOSHT)) {
+               if (features->device_type & WACOM_DEVICETYPE_PEN)
+                       features->device_type |= WACOM_DEVICETYPE_PAD;
+       }
 
        /* touch device found but size is not defined. use default */
-       if (features->device_type == BTN_TOOL_FINGER && !features->x_max) {
+       if (features->device_type & WACOM_DEVICETYPE_TOUCH && !features->x_max) {
                features->x_max = 1023;
                features->y_max = 1023;
        }
 
+       /*
+        * Intuos5/Pro and Bamboo 3rd gen have no useful data about its
+        * touch interface in its HID descriptor. If this is the touch
+        * interface (PacketSize of WACOM_PKGLEN_BBTOUCH3), override the
+        * tablet values.
+        */
+       if ((features->type >= INTUOS5S && features->type <= INTUOSHT) ||
+               (features->type == BAMBOO_PT)) {
+               if (features->pktlen == WACOM_PKGLEN_BBTOUCH3) {
+                       if (features->touch_max)
+                               features->device_type |= WACOM_DEVICETYPE_TOUCH;
+                       if (features->type == BAMBOO_PT || features->type == INTUOSHT)
+                               features->device_type |= WACOM_DEVICETYPE_PAD;
+
+                       features->x_max = 4096;
+                       features->y_max = 4096;
+               }
+       }
+
+       /*
+        * Raw Wacom-mode pen and touch events both come from interface
+        * 0, whose HID descriptor has an application usage of 0xFF0D
+        * (i.e., WACOM_VENDORDEFINED_PEN). We route pen packets back
+        * out through the HID_GENERIC device created for interface 1,
+        * so rewrite this one to be of type BTN_TOOL_FINGER.
+        */
+       if (features->type == BAMBOO_PAD)
+               features->device_type |= WACOM_DEVICETYPE_TOUCH;
+
+       if (wacom->hdev->bus == BUS_BLUETOOTH)
+               features->quirks |= WACOM_QUIRK_BATTERY;
+
        /* quirk for bamboo touch with 2 low res touches */
        if (features->type == BAMBOO_PT &&
            features->pktlen == WACOM_PKGLEN_BBTOUCH) {
@@ -2192,61 +2244,23 @@ void wacom_setup_device_quirks(struct wacom_features *features)
                features->quirks |= WACOM_QUIRK_NO_INPUT;
 
                /* must be monitor interface if no device_type set */
-               if (!features->device_type) {
+               if (features->device_type == WACOM_DEVICETYPE_NONE) {
                        features->quirks |= WACOM_QUIRK_MONITOR;
                        features->quirks |= WACOM_QUIRK_BATTERY;
                }
        }
 }
 
-static void wacom_abs_set_axis(struct input_dev *input_dev,
-                              struct wacom_wac *wacom_wac)
-{
-       struct wacom_features *features = &wacom_wac->features;
-
-       if (features->device_type == BTN_TOOL_PEN) {
-               input_set_abs_params(input_dev, ABS_X, features->x_min,
-                                    features->x_max, features->x_fuzz, 0);
-               input_set_abs_params(input_dev, ABS_Y, features->y_min,
-                                    features->y_max, features->y_fuzz, 0);
-               input_set_abs_params(input_dev, ABS_PRESSURE, 0,
-                       features->pressure_max, features->pressure_fuzz, 0);
-
-               /* penabled devices have fixed resolution for each model */
-               input_abs_set_res(input_dev, ABS_X, features->x_resolution);
-               input_abs_set_res(input_dev, ABS_Y, features->y_resolution);
-       } else {
-               if (features->touch_max == 1) {
-                       input_set_abs_params(input_dev, ABS_X, 0,
-                               features->x_max, features->x_fuzz, 0);
-                       input_set_abs_params(input_dev, ABS_Y, 0,
-                               features->y_max, features->y_fuzz, 0);
-                       input_abs_set_res(input_dev, ABS_X,
-                                         features->x_resolution);
-                       input_abs_set_res(input_dev, ABS_Y,
-                                         features->y_resolution);
-               }
-
-               if (features->touch_max > 1) {
-                       input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0,
-                               features->x_max, features->x_fuzz, 0);
-                       input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0,
-                               features->y_max, features->y_fuzz, 0);
-                       input_abs_set_res(input_dev, ABS_MT_POSITION_X,
-                                         features->x_resolution);
-                       input_abs_set_res(input_dev, ABS_MT_POSITION_Y,
-                                         features->y_resolution);
-               }
-       }
-}
-
-int wacom_setup_pentouch_input_capabilities(struct input_dev *input_dev,
+int wacom_setup_pen_input_capabilities(struct input_dev *input_dev,
                                   struct wacom_wac *wacom_wac)
 {
        struct wacom_features *features = &wacom_wac->features;
 
        input_dev->evbit[0] |= BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
 
+       if (!(features->device_type & WACOM_DEVICETYPE_PEN))
+               return -ENODEV;
+
        if (features->type == HID_GENERIC)
                /* setup has already been done */
                return 0;
@@ -2254,7 +2268,17 @@ int wacom_setup_pentouch_input_capabilities(struct input_dev *input_dev,
        __set_bit(BTN_TOUCH, input_dev->keybit);
        __set_bit(ABS_MISC, input_dev->absbit);
 
-       wacom_abs_set_axis(input_dev, wacom_wac);
+       input_set_abs_params(input_dev, ABS_X, features->x_min,
+                            features->x_max, features->x_fuzz, 0);
+       input_set_abs_params(input_dev, ABS_Y, features->y_min,
+                            features->y_max, features->y_fuzz, 0);
+       input_set_abs_params(input_dev, ABS_PRESSURE, 0,
+               features->pressure_max, features->pressure_fuzz, 0);
+
+       /* penabled devices have fixed resolution for each model */
+       input_abs_set_res(input_dev, ABS_X, features->x_resolution);
+       input_abs_set_res(input_dev, ABS_Y, features->y_resolution);
+
 
        switch (features->type) {
        case GRAPHIRE_BT:
@@ -2323,53 +2347,25 @@ int wacom_setup_pentouch_input_capabilities(struct input_dev *input_dev,
        case INTUOSPS:
                __set_bit(INPUT_PROP_POINTER, input_dev->propbit);
 
-               if (features->device_type == BTN_TOOL_PEN) {
-                       input_set_abs_params(input_dev, ABS_DISTANCE, 0,
-                                             features->distance_max,
-                                             0, 0);
-
-                       input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0);
-                       input_abs_set_res(input_dev, ABS_Z, 287);
+               input_set_abs_params(input_dev, ABS_DISTANCE, 0,
+                                     features->distance_max,
+                                     0, 0);
 
-                       wacom_setup_intuos(wacom_wac);
-               } else if (features->device_type == BTN_TOOL_FINGER) {
-                       __clear_bit(ABS_MISC, input_dev->absbit);
+               input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0);
+               input_abs_set_res(input_dev, ABS_Z, 287);
 
-                       input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR,
-                                            0, features->x_max, 0, 0);
-                       input_set_abs_params(input_dev, ABS_MT_TOUCH_MINOR,
-                                            0, features->y_max, 0, 0);
-                       input_mt_init_slots(input_dev, features->touch_max, INPUT_MT_POINTER);
-               }
+               wacom_setup_intuos(wacom_wac);
                break;
 
        case WACOM_24HDT:
-               if (features->device_type == BTN_TOOL_FINGER) {
-                       input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, features->x_max, 0, 0);
-                       input_set_abs_params(input_dev, ABS_MT_WIDTH_MAJOR, 0, features->x_max, 0, 0);
-                       input_set_abs_params(input_dev, ABS_MT_WIDTH_MINOR, 0, features->y_max, 0, 0);
-                       input_set_abs_params(input_dev, ABS_MT_ORIENTATION, 0, 1, 0, 0);
-               }
-               /* fall through */
-
        case WACOM_27QHDT:
        case MTSCREEN:
        case MTTPC:
        case MTTPC_B:
        case TABLETPC2FG:
-               if (features->device_type == BTN_TOOL_FINGER && features->touch_max > 1)
-                       input_mt_init_slots(input_dev, features->touch_max, INPUT_MT_DIRECT);
-               /* fall through */
-
        case TABLETPC:
        case TABLETPCE:
                __clear_bit(ABS_MISC, input_dev->absbit);
-
-               __set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
-
-               if (features->device_type != BTN_TOOL_PEN)
-                       break;  /* no need to process stylus stuff */
-
                /* fall through */
 
        case DTUS:
@@ -2397,50 +2393,114 @@ int wacom_setup_pentouch_input_capabilities(struct input_dev *input_dev,
                break;
 
        case INTUOSHT:
-               if (features->touch_max &&
-                   features->device_type == BTN_TOOL_FINGER) {
-                       input_dev->evbit[0] |= BIT_MASK(EV_SW);
-                       __set_bit(SW_MUTE_DEVICE, input_dev->swbit);
-               }
-               /* fall through */
-
        case BAMBOO_PT:
                __clear_bit(ABS_MISC, input_dev->absbit);
 
-               if (features->device_type == BTN_TOOL_FINGER) {
+               __set_bit(INPUT_PROP_POINTER, input_dev->propbit);
+               __set_bit(BTN_TOOL_RUBBER, input_dev->keybit);
+               __set_bit(BTN_TOOL_PEN, input_dev->keybit);
+               __set_bit(BTN_STYLUS, input_dev->keybit);
+               __set_bit(BTN_STYLUS2, input_dev->keybit);
+               input_set_abs_params(input_dev, ABS_DISTANCE, 0,
+                                     features->distance_max,
+                                     0, 0);
+               break;
+       case BAMBOO_PAD:
+               __clear_bit(ABS_MISC, input_dev->absbit);
+               break;
+       }
+       return 0;
+}
 
-                       if (features->touch_max) {
-                               if (features->pktlen == WACOM_PKGLEN_BBTOUCH3) {
-                                       input_set_abs_params(input_dev,
-                                                    ABS_MT_TOUCH_MAJOR,
-                                                    0, features->x_max, 0, 0);
-                                       input_set_abs_params(input_dev,
-                                                    ABS_MT_TOUCH_MINOR,
-                                                    0, features->y_max, 0, 0);
-                               }
-                               input_mt_init_slots(input_dev, features->touch_max, INPUT_MT_POINTER);
-                       } else {
-                               /* buttons/keys only interface */
-                               __clear_bit(ABS_X, input_dev->absbit);
-                               __clear_bit(ABS_Y, input_dev->absbit);
-                               __clear_bit(BTN_TOUCH, input_dev->keybit);
+int wacom_setup_touch_input_capabilities(struct input_dev *input_dev,
+                                        struct wacom_wac *wacom_wac)
+{
+       struct wacom_features *features = &wacom_wac->features;
 
-                               /* PAD is setup by wacom_setup_pad_input_capabilities later */
-                               return 1;
-                       }
-               } else if (features->device_type == BTN_TOOL_PEN) {
-                       __set_bit(INPUT_PROP_POINTER, input_dev->propbit);
-                       __set_bit(BTN_TOOL_RUBBER, input_dev->keybit);
-                       __set_bit(BTN_TOOL_PEN, input_dev->keybit);
-                       __set_bit(BTN_STYLUS, input_dev->keybit);
-                       __set_bit(BTN_STYLUS2, input_dev->keybit);
-                       input_set_abs_params(input_dev, ABS_DISTANCE, 0,
-                                             features->distance_max,
-                                             0, 0);
+       input_dev->evbit[0] |= BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+
+       if (!(features->device_type & WACOM_DEVICETYPE_TOUCH))
+               return -ENODEV;
+
+       if (features->type == HID_GENERIC)
+               /* setup has already been done */
+               return 0;
+
+       __set_bit(BTN_TOUCH, input_dev->keybit);
+
+       if (features->touch_max == 1) {
+               input_set_abs_params(input_dev, ABS_X, 0,
+                       features->x_max, features->x_fuzz, 0);
+               input_set_abs_params(input_dev, ABS_Y, 0,
+                       features->y_max, features->y_fuzz, 0);
+               input_abs_set_res(input_dev, ABS_X,
+                                 features->x_resolution);
+               input_abs_set_res(input_dev, ABS_Y,
+                                 features->y_resolution);
+       }
+       else if (features->touch_max > 1) {
+               input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0,
+                       features->x_max, features->x_fuzz, 0);
+               input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0,
+                       features->y_max, features->y_fuzz, 0);
+               input_abs_set_res(input_dev, ABS_MT_POSITION_X,
+                                 features->x_resolution);
+               input_abs_set_res(input_dev, ABS_MT_POSITION_Y,
+                                 features->y_resolution);
+       }
+
+       switch (features->type) {
+       case INTUOS5:
+       case INTUOS5L:
+       case INTUOSPM:
+       case INTUOSPL:
+       case INTUOS5S:
+       case INTUOSPS:
+               __set_bit(INPUT_PROP_POINTER, input_dev->propbit);
+
+               input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, features->x_max, 0, 0);
+               input_set_abs_params(input_dev, ABS_MT_TOUCH_MINOR, 0, features->y_max, 0, 0);
+               input_mt_init_slots(input_dev, features->touch_max, INPUT_MT_POINTER);
+               break;
+
+       case WACOM_24HDT:
+               input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, features->x_max, 0, 0);
+               input_set_abs_params(input_dev, ABS_MT_WIDTH_MAJOR, 0, features->x_max, 0, 0);
+               input_set_abs_params(input_dev, ABS_MT_WIDTH_MINOR, 0, features->y_max, 0, 0);
+               input_set_abs_params(input_dev, ABS_MT_ORIENTATION, 0, 1, 0, 0);
+               /* fall through */
+
+       case WACOM_27QHDT:
+       case MTSCREEN:
+       case MTTPC:
+       case MTTPC_B:
+       case TABLETPC2FG:
+               input_mt_init_slots(input_dev, features->touch_max, INPUT_MT_DIRECT);
+               /*fall through */
+
+       case TABLETPC:
+       case TABLETPCE:
+               __set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
+               break;
+
+       case INTUOSHT:
+               input_dev->evbit[0] |= BIT_MASK(EV_SW);
+               __set_bit(SW_MUTE_DEVICE, input_dev->swbit);
+               /* fall through */
+
+       case BAMBOO_PT:
+               if (features->pktlen == WACOM_PKGLEN_BBTOUCH3) {
+                       input_set_abs_params(input_dev,
+                                    ABS_MT_TOUCH_MAJOR,
+                                    0, features->x_max, 0, 0);
+                       input_set_abs_params(input_dev,
+                                    ABS_MT_TOUCH_MINOR,
+                                    0, features->y_max, 0, 0);
                }
+               input_mt_init_slots(input_dev, features->touch_max, INPUT_MT_POINTER);
                break;
+
        case BAMBOO_PAD:
-               __clear_bit(ABS_MISC, input_dev->absbit);
                input_mt_init_slots(input_dev, features->touch_max,
                                    INPUT_MT_POINTER);
                __set_bit(BTN_LEFT, input_dev->keybit);
@@ -2456,6 +2516,9 @@ int wacom_setup_pad_input_capabilities(struct input_dev *input_dev,
        struct wacom_features *features = &wacom_wac->features;
        int i;
 
+       if (!(features->device_type & WACOM_DEVICETYPE_PAD))
+               return -ENODEV;
+
        input_dev->evbit[0] |= BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
 
        /* kept for making legacy xf86-input-wacom working with the wheels */
@@ -2592,10 +2655,6 @@ int wacom_setup_pad_input_capabilities(struct input_dev *input_dev,
 
        case INTUOS5S:
        case INTUOSPS:
-               /* touch interface does not have the pad device */
-               if (features->device_type != BTN_TOOL_PEN)
-                       return -ENODEV;
-
                for (i = 0; i < 7; i++)
                        __set_bit(BTN_0 + i, input_dev->keybit);
 
@@ -2637,12 +2696,6 @@ int wacom_setup_pad_input_capabilities(struct input_dev *input_dev,
 
        case INTUOSHT:
        case BAMBOO_PT:
-               /* pad device is on the touch interface */
-               if ((features->device_type != BTN_TOOL_FINGER) ||
-                   /* Bamboo Pen only tablet does not have pad */
-                   ((features->type == BAMBOO_PT) && !features->touch_max))
-                       return -ENODEV;
-
                __clear_bit(ABS_MISC, input_dev->absbit);
 
                __set_bit(BTN_LEFT, input_dev->keybit);
@@ -2922,6 +2975,9 @@ static const struct wacom_features wacom_features_0x32F =
        { "Wacom DTU1031X", 22472, 12728, 511, 0,
          DTUSX, WACOM_INTUOS_RES, WACOM_INTUOS_RES,
          WACOM_DTU_OFFSET, WACOM_DTU_OFFSET };
+static const struct wacom_features wacom_features_0x336 =
+       { "Wacom DTU1141", 23472, 13203, 1023, 0,
+         DTUS, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
 static const struct wacom_features wacom_features_0x57 =
        { "Wacom DTK2241", 95640, 54060, 2047, 63,
          DTK, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES,
@@ -3275,6 +3331,7 @@ const struct hid_device_id wacom_ids[] = {
        { USB_DEVICE_WACOM(0x32F) },
        { USB_DEVICE_WACOM(0x333) },
        { USB_DEVICE_WACOM(0x335) },
+       { USB_DEVICE_WACOM(0x336) },
        { USB_DEVICE_WACOM(0x4001) },
        { USB_DEVICE_WACOM(0x4004) },
        { USB_DEVICE_WACOM(0x5000) },
index 4700ac994a3b37c831b0903a34f62a5895f4b892..2978c303909d396acfe9f8e0cbe96307cf8aefa4 100644 (file)
 #define WACOM_NAME_MAX         64
 
 /* packet length for individual models */
-#define WACOM_PKGLEN_PENPRTN    7
-#define WACOM_PKGLEN_GRAPHIRE   8
 #define WACOM_PKGLEN_BBFUN      9
-#define WACOM_PKGLEN_INTUOS    10
 #define WACOM_PKGLEN_TPC1FG     5
 #define WACOM_PKGLEN_TPC1FG_B  10
 #define WACOM_PKGLEN_TPC2FG    14
@@ -29,9 +26,6 @@
 #define WACOM_PKGLEN_BBTOUCH3  64
 #define WACOM_PKGLEN_BBPEN     10
 #define WACOM_PKGLEN_WIRELESS  32
-#define WACOM_PKGLEN_MTOUCH    62
-#define WACOM_PKGLEN_MTTPC     40
-#define WACOM_PKGLEN_DTUS      68
 #define WACOM_PKGLEN_PENABLED   8
 #define WACOM_PKGLEN_BPAD_TOUCH        32
 #define WACOM_PKGLEN_BPAD_TOUCH_USB    64
 #define WACOM_QUIRK_MONITOR            0x0004
 #define WACOM_QUIRK_BATTERY            0x0008
 
+/* device types */
+#define WACOM_DEVICETYPE_NONE           0x0000
+#define WACOM_DEVICETYPE_PEN            0x0001
+#define WACOM_DEVICETYPE_TOUCH          0x0002
+#define WACOM_DEVICETYPE_PAD            0x0004
+
+#define WACOM_VENDORDEFINED_PEN                0xff0d0001
+
 #define WACOM_PEN_FIELD(f)     (((f)->logical == HID_DG_STYLUS) || \
                                 ((f)->physical == HID_DG_STYLUS) || \
                                 ((f)->physical == HID_DG_PEN) || \
-                                ((f)->application == HID_DG_PEN))
+                                ((f)->application == HID_DG_PEN) || \
+                                ((f)->application == HID_DG_DIGITIZER) || \
+                                ((f)->application == WACOM_VENDORDEFINED_PEN))
 #define WACOM_FINGER_FIELD(f)  (((f)->logical == HID_DG_FINGER) || \
                                 ((f)->physical == HID_DG_FINGER) || \
                                 ((f)->application == HID_DG_TOUCHSCREEN))
@@ -192,7 +196,8 @@ struct hid_data {
 };
 
 struct wacom_wac {
-       char name[WACOM_NAME_MAX];
+       char pen_name[WACOM_NAME_MAX];
+       char touch_name[WACOM_NAME_MAX];
        char pad_name[WACOM_NAME_MAX];
        char bat_name[WACOM_NAME_MAX];
        char ac_name[WACOM_NAME_MAX];
@@ -203,9 +208,11 @@ struct wacom_wac {
        bool reporting_data;
        struct wacom_features features;
        struct wacom_shared *shared;
-       struct input_dev *input;
+       struct input_dev *pen_input;
+       struct input_dev *touch_input;
        struct input_dev *pad_input;
-       bool input_registered;
+       bool pen_registered;
+       bool touch_registered;
        bool pad_registered;
        int pid;
        int battery_capacity;
index 4983529a9c6c3fce2aadc353c2a9a944bb621bfe..d04643f9548bbca84edee48659cfe7fac2600bfa 100644 (file)
@@ -451,9 +451,14 @@ static void cs_hsi_read_on_control_complete(struct hsi_msg *msg)
        dev_dbg(&hi->cl->device, "Read on control: %08X\n", cmd);
        cs_release_cmd(msg);
        if (hi->flags & CS_FEAT_TSTAMP_RX_CTRL) {
-               struct timespec *tstamp =
+               struct timespec tspec;
+               struct cs_timestamp *tstamp =
                        &hi->mmap_cfg->tstamp_rx_ctrl;
-               do_posix_clock_monotonic_gettime(tstamp);
+
+               ktime_get_ts(&tspec);
+
+               tstamp->tv_sec = (__u32) tspec.tv_sec;
+               tstamp->tv_nsec = (__u32) tspec.tv_nsec;
        }
        spin_unlock(&hi->lock);
 
index bbb19231fa82ed22267adf789f9f7fdb4de91d68..7f82c911ad74c4893c71eefcbe4f47bdd52652a3 100644 (file)
@@ -112,7 +112,8 @@ static int nokia_modem_gpio_probe(struct device *dev)
        modem->gpio_amount = gpio_count;
 
        for (i = 0; i < gpio_count; i++) {
-               modem->gpios[i].gpio = devm_gpiod_get_index(dev, NULL, i);
+               modem->gpios[i].gpio = devm_gpiod_get_index(dev, NULL, i,
+                                                           GPIOD_OUT_LOW);
                if (IS_ERR(modem->gpios[i].gpio)) {
                        dev_err(dev, "Could not get gpio %d\n", i);
                        return PTR_ERR(modem->gpios[i].gpio);
@@ -125,10 +126,6 @@ static int nokia_modem_gpio_probe(struct device *dev)
                        return err;
                }
 
-               err = gpiod_direction_output(modem->gpios[i].gpio, 0);
-               if (err)
-                       return err;
-
                err = gpiod_export(modem->gpios[i].gpio, 0);
                if (err)
                        return err;
@@ -208,7 +205,7 @@ static int nokia_modem_probe(struct device *dev)
 
        err = device_attach(&modem->ssi_protocol->device);
        if (err == 0) {
-               dev_err(dev, "Missing ssi-protocol driver\n");
+               dev_dbg(dev, "Missing ssi-protocol driver\n");
                err = -EPROBE_DEFER;
                goto error3;
        } else if (err < 0) {
@@ -231,7 +228,7 @@ static int nokia_modem_probe(struct device *dev)
 
        err = device_attach(&modem->cmt_speech->device);
        if (err == 0) {
-               dev_err(dev, "Missing cmt-speech driver\n");
+               dev_dbg(dev, "Missing cmt-speech driver\n");
                err = -EPROBE_DEFER;
                goto error4;
        } else if (err < 0) {
index 25d9e72627e9df97cb607de8038d2310d28c214b..54075a07d2a1674035f5a80a6fb211331ee1eaa7 100644 (file)
@@ -509,7 +509,7 @@ config SENSORS_G762
 
 config SENSORS_GPIO_FAN
        tristate "GPIO fan"
-       depends on GPIOLIB
+       depends on GPIOLIB || COMPILE_TEST
        depends on THERMAL || THERMAL=n
        help
          If you say yes here you get support for fans connected to GPIO lines.
@@ -1106,8 +1106,8 @@ config SENSORS_NTC_THERMISTOR
          send notifications about the temperature.
 
          Currently, this driver supports
-         NCP15WB473, NCP18WB473, NCP21WB473, NCP03WB473, and NCP15WL333
-         from Murata and B57330V2103 from EPCOS.
+         NCP15WB473, NCP18WB473, NCP21WB473, NCP03WB473, NCP15WL333,
+         and NCP03WF104 from Murata and B57330V2103 from EPCOS.
 
          This driver can also be built as a module.  If so, the module
          will be called ntc-thermistor.
@@ -1186,7 +1186,7 @@ config SENSORS_PWM_FAN
 
 config SENSORS_SHT15
        tristate "Sensiron humidity and temperature sensors. SHT15 and compat."
-       depends on GPIOLIB
+       depends on GPIOLIB || COMPILE_TEST
        help
          If you say yes here you get support for the Sensiron SHT10, SHT11,
          SHT15, SHT71, SHT75 humidity and temperature sensors.
@@ -1452,6 +1452,16 @@ config SENSORS_INA2XX
          This driver can also be built as a module.  If so, the module
          will be called ina2xx.
 
+config SENSORS_TC74
+       tristate "Microchip TC74"
+       depends on I2C
+       help
+         If you say yes here you get support for Microchip TC74 single
+         input temperature sensor chips.
+
+         This driver can also be built as a module.  If so, the module
+         will be called tc74.
+
 config SENSORS_THMC50
        tristate "Texas Instruments THMC50 / Analog Devices ADM1022"
        depends on I2C
index b4a40f17e2aa5211f767323f736cab872c16528d..ab904027f074ed0841c27945b6d6c6092fb52708 100644 (file)
@@ -140,6 +140,7 @@ obj-$(CONFIG_SENSORS_SMSC47B397)+= smsc47b397.o
 obj-$(CONFIG_SENSORS_SMSC47M1) += smsc47m1.o
 obj-$(CONFIG_SENSORS_SMSC47M192)+= smsc47m192.o
 obj-$(CONFIG_SENSORS_AMC6821)  += amc6821.o
+obj-$(CONFIG_SENSORS_TC74)     += tc74.o
 obj-$(CONFIG_SENSORS_THMC50)   += thmc50.o
 obj-$(CONFIG_SENSORS_TMP102)   += tmp102.o
 obj-$(CONFIG_SENSORS_TMP103)   += tmp103.o
index 4c829bb2f9db45ac28a1b38e1e8466ca88491721..f2f2f2fc755a25b27303f4eeff6dac1130fe159c 100644 (file)
  * 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.
- *
+ * The ATXP1 can reside on I2C addresses 0x37 or 0x4e. The chip is
+ * not auto-detected by the driver and must be instantiated explicitly.
+ * See Documentation/i2c/instantiating-devices for more information.
  */
 
 #include <linux/kernel.h>
@@ -43,8 +42,6 @@ MODULE_AUTHOR("Sebastian Witt <se.witt@gmx.net>");
 #define ATXP1_VIDMASK  0x1f
 #define ATXP1_GPIO1MASK        0x0f
 
-static const unsigned short normal_i2c[] = { 0x37, 0x4e, I2C_CLIENT_END };
-
 struct atxp1_data {
        struct i2c_client *client;
        struct mutex update_lock;
@@ -259,48 +256,6 @@ static struct attribute *atxp1_attrs[] = {
 };
 ATTRIBUTE_GROUPS(atxp1);
 
-/* Return 0 if detection is successful, -ENODEV otherwise */
-static int atxp1_detect(struct i2c_client *new_client,
-                       struct i2c_board_info *info)
-{
-       struct i2c_adapter *adapter = new_client->adapter;
-
-       u8 temp;
-
-       if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
-               return -ENODEV;
-
-       /* Detect ATXP1, checking if vendor ID registers are all zero */
-       if (!((i2c_smbus_read_byte_data(new_client, 0x3e) == 0) &&
-            (i2c_smbus_read_byte_data(new_client, 0x3f) == 0) &&
-            (i2c_smbus_read_byte_data(new_client, 0xfe) == 0) &&
-            (i2c_smbus_read_byte_data(new_client, 0xff) == 0)))
-               return -ENODEV;
-
-       /*
-        * No vendor ID, now checking if registers 0x10,0x11 (non-existent)
-        * showing the same as register 0x00
-        */
-       temp = i2c_smbus_read_byte_data(new_client, 0x00);
-
-       if (!((i2c_smbus_read_byte_data(new_client, 0x10) == temp) &&
-             (i2c_smbus_read_byte_data(new_client, 0x11) == temp)))
-               return -ENODEV;
-
-       /* Get VRM */
-       temp = vid_which_vrm();
-
-       if ((temp != 90) && (temp != 91)) {
-               dev_err(&adapter->dev, "atxp1: Not supporting VRM %d.%d\n",
-                               temp / 10, temp % 10);
-               return -ENODEV;
-       }
-
-       strlcpy(info->type, "atxp1", I2C_NAME_SIZE);
-
-       return 0;
-}
-
 static int atxp1_probe(struct i2c_client *client,
                       const struct i2c_device_id *id)
 {
@@ -314,6 +269,11 @@ static int atxp1_probe(struct i2c_client *client,
 
        /* Get VRM */
        data->vrm = vid_which_vrm();
+       if (data->vrm != 90 && data->vrm != 91) {
+               dev_err(dev, "atxp1: Not supporting VRM %d.%d\n",
+                       data->vrm / 10, data->vrm % 10);
+               return -ENODEV;
+       }
 
        data->client = client;
        mutex_init(&data->update_lock);
@@ -342,8 +302,6 @@ static struct i2c_driver atxp1_driver = {
        },
        .probe          = atxp1_probe,
        .id_table       = atxp1_id,
-       .detect         = atxp1_detect,
-       .address_list   = normal_i2c,
 };
 
 module_i2c_driver(atxp1_driver);
index cb0dcfda958c1a905c1fc9954f1a637a81a40467..07628569547aee23ffd60daceefb893101a14acb 100644 (file)
@@ -324,7 +324,7 @@ static int max197_remove(struct platform_device *pdev)
        return 0;
 }
 
-static struct platform_device_id max197_device_ids[] = {
+static const struct platform_device_id max197_device_ids[] = {
        { "max197", max197 },
        { "max199", max199 },
        { }
index 68800115876bf65867c187498902d0de6dcae687..dc0b76c5e3028018683b6a34c2c4f58c3906c650 100644 (file)
@@ -53,6 +53,7 @@ static const struct platform_device_id ntc_thermistor_id[] = {
        { "ncp03wb473", TYPE_NCPXXWB473 },
        { "ncp15wl333", TYPE_NCPXXWL333 },
        { "b57330v2103", TYPE_B57330V2103},
+       { "ncp03wf104", TYPE_NCPXXWF104 },
        { },
 };
 
@@ -135,6 +136,43 @@ static const struct ntc_compensation ncpXXwl333[] = {
        { .temp_c       = 125, .ohm     = 707 },
 };
 
+static const struct ntc_compensation ncpXXwf104[] = {
+       { .temp_c       = -40, .ohm     = 4397119 },
+       { .temp_c       = -35, .ohm     = 3088599 },
+       { .temp_c       = -30, .ohm     = 2197225 },
+       { .temp_c       = -25, .ohm     = 1581881 },
+       { .temp_c       = -20, .ohm     = 1151037 },
+       { .temp_c       = -15, .ohm     = 846579 },
+       { .temp_c       = -10, .ohm     = 628988 },
+       { .temp_c       = -5, .ohm      = 471632 },
+       { .temp_c       = 0, .ohm       = 357012 },
+       { .temp_c       = 5, .ohm       = 272500 },
+       { .temp_c       = 10, .ohm      = 209710 },
+       { .temp_c       = 15, .ohm      = 162651 },
+       { .temp_c       = 20, .ohm      = 127080 },
+       { .temp_c       = 25, .ohm      = 100000 },
+       { .temp_c       = 30, .ohm      = 79222 },
+       { .temp_c       = 35, .ohm      = 63167 },
+       { .temp_c       = 40, .ohm      = 50677 },
+       { .temp_c       = 45, .ohm      = 40904 },
+       { .temp_c       = 50, .ohm      = 33195 },
+       { .temp_c       = 55, .ohm      = 27091 },
+       { .temp_c       = 60, .ohm      = 22224 },
+       { .temp_c       = 65, .ohm      = 18323 },
+       { .temp_c       = 70, .ohm      = 15184 },
+       { .temp_c       = 75, .ohm      = 12635 },
+       { .temp_c       = 80, .ohm      = 10566 },
+       { .temp_c       = 85, .ohm      = 8873 },
+       { .temp_c       = 90, .ohm      = 7481 },
+       { .temp_c       = 95, .ohm      = 6337 },
+       { .temp_c       = 100, .ohm     = 5384 },
+       { .temp_c       = 105, .ohm     = 4594 },
+       { .temp_c       = 110, .ohm     = 3934 },
+       { .temp_c       = 115, .ohm     = 3380 },
+       { .temp_c       = 120, .ohm     = 2916 },
+       { .temp_c       = 125, .ohm     = 2522 },
+};
+
 /*
  * The following compensation table is from the specification of EPCOS NTC
  * Thermistors Datasheet
@@ -190,20 +228,21 @@ struct ntc_data {
 static int ntc_adc_iio_read(struct ntc_thermistor_platform_data *pdata)
 {
        struct iio_channel *channel = pdata->chan;
-       s64 result;
-       int val, ret;
+       int raw, uv, ret;
 
-       ret = iio_read_channel_raw(channel, &val);
+       ret = iio_read_channel_raw(channel, &raw);
        if (ret < 0) {
                pr_err("read channel() error: %d\n", ret);
                return ret;
        }
 
-       /* unit: mV */
-       result = pdata->pullup_uv * (s64) val;
-       result >>= 12;
+       ret = iio_convert_raw_to_processed(channel, raw, &uv, 1000);
+       if (ret < 0) {
+               /* Assume 12 bit ADC with vref at pullup_uv */
+               uv = (pdata->pullup_uv * (s64)raw) >> 12;
+       }
 
-       return (int)result;
+       return uv;
 }
 
 static const struct of_device_id ntc_match[] = {
@@ -219,6 +258,8 @@ static const struct of_device_id ntc_match[] = {
                .data = &ntc_thermistor_id[4] },
        { .compatible = "epcos,b57330v2103",
                .data = &ntc_thermistor_id[5]},
+       { .compatible = "murata,ncp03wf104",
+               .data = &ntc_thermistor_id[6] },
 
        /* Usage of vendor name "ntc" is deprecated */
        { .compatible = "ntc,ncp15wb473",
@@ -309,30 +350,27 @@ static inline u64 div64_u64_safe(u64 dividend, u64 divisor)
 static int get_ohm_of_thermistor(struct ntc_data *data, unsigned int uv)
 {
        struct ntc_thermistor_platform_data *pdata = data->pdata;
-       u64 mv = uv / 1000;
-       u64 pmv = pdata->pullup_uv / 1000;
+       u32 puv = pdata->pullup_uv;
        u64 n, puo, pdo;
        puo = pdata->pullup_ohm;
        pdo = pdata->pulldown_ohm;
 
-       if (mv == 0) {
-               if (pdata->connect == NTC_CONNECTED_POSITIVE)
-                       return INT_MAX;
-               return 0;
-       }
-       if (mv >= pmv)
+       if (uv == 0)
+               return (pdata->connect == NTC_CONNECTED_POSITIVE) ?
+                       INT_MAX : 0;
+       if (uv >= puv)
                return (pdata->connect == NTC_CONNECTED_POSITIVE) ?
                        0 : INT_MAX;
 
        if (pdata->connect == NTC_CONNECTED_POSITIVE && puo == 0)
-               n = div64_u64_safe(pdo * (pmv - mv), mv);
+               n = div_u64(pdo * (puv - uv), uv);
        else if (pdata->connect == NTC_CONNECTED_GROUND && pdo == 0)
-               n = div64_u64_safe(puo * mv, pmv - mv);
+               n = div_u64(puo * uv, puv - uv);
        else if (pdata->connect == NTC_CONNECTED_POSITIVE)
-               n = div64_u64_safe(pdo * puo * (pmv - mv),
-                               puo * mv - pdo * (pmv - mv));
+               n = div64_u64_safe(pdo * puo * (puv - uv),
+                               puo * uv - pdo * (puv - uv));
        else
-               n = div64_u64_safe(pdo * puo * mv, pdo * (pmv - mv) - puo * mv);
+               n = div64_u64_safe(pdo * puo * uv, pdo * (puv - uv) - puo * uv);
 
        if (n > INT_MAX)
                n = INT_MAX;
@@ -567,6 +605,10 @@ static int ntc_thermistor_probe(struct platform_device *pdev)
                data->comp = b57330v2103;
                data->n_comp = ARRAY_SIZE(b57330v2103);
                break;
+       case TYPE_NCPXXWF104:
+               data->comp = ncpXXwf104;
+               data->n_comp = ARRAY_SIZE(ncpXXwf104);
+               break;
        default:
                dev_err(&pdev->dev, "Unknown device type: %lu(%s)\n",
                                pdev_id->driver_data, pdev_id->name);
index d4f0935daaa11edfb8e56c5d58edc2f32eff570a..497a7f822a12c661aeaac71790ec3bd33f2cef54 100644 (file)
@@ -1074,7 +1074,7 @@ static int sht15_remove(struct platform_device *pdev)
        return 0;
 }
 
-static struct platform_device_id sht15_device_ids[] = {
+static const struct platform_device_id sht15_device_ids[] = {
        { "sht10", sht10 },
        { "sht11", sht11 },
        { "sht15", sht15 },
diff --git a/drivers/hwmon/tc74.c b/drivers/hwmon/tc74.c
new file mode 100644 (file)
index 0000000..d951651
--- /dev/null
@@ -0,0 +1,177 @@
+/*
+ * An hwmon driver for the Microchip TC74
+ *
+ * Copyright 2015 Maciej Szmigiero <mail@maciej.szmigiero.name>
+ *
+ * Based on ad7414.c:
+ *     Copyright 2006 Stefan Roese, DENX Software Engineering
+ *     Copyright 2008 Sean MacLennan, PIKA Technologies
+ *     Copyright 2008 Frank Edelhaeuser, Spansion 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.
+ */
+
+#include <linux/bitops.h>
+#include <linux/err.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/i2c.h>
+#include <linux/jiffies.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+
+/* TC74 registers */
+#define TC74_REG_TEMP          0x00
+#define TC74_REG_CONFIG                0x01
+
+struct tc74_data {
+       struct i2c_client       *client;
+       struct mutex            lock;   /* atomic read data updates */
+       bool                    valid;  /* validity of fields below */
+       unsigned long           next_update;    /* In jiffies */
+       s8                      temp_input;     /* Temp value in dC */
+};
+
+static int tc74_update_device(struct device *dev)
+{
+       struct tc74_data *data = dev_get_drvdata(dev);
+       struct i2c_client *client = data->client;
+       int ret;
+
+       ret = mutex_lock_interruptible(&data->lock);
+       if (ret)
+               return ret;
+
+       if (time_after(jiffies, data->next_update) || !data->valid) {
+               s32 value;
+
+               value = i2c_smbus_read_byte_data(client, TC74_REG_CONFIG);
+               if (value < 0) {
+                       dev_dbg(&client->dev, "TC74_REG_CONFIG read err %d\n",
+                               (int)value);
+
+                       ret = value;
+                       goto ret_unlock;
+               }
+
+               if (!(value & BIT(6))) {
+                       /* not ready yet */
+
+                       ret = -EAGAIN;
+                       goto ret_unlock;
+               }
+
+               value = i2c_smbus_read_byte_data(client, TC74_REG_TEMP);
+               if (value < 0) {
+                       dev_dbg(&client->dev, "TC74_REG_TEMP read err %d\n",
+                               (int)value);
+
+                       ret = value;
+                       goto ret_unlock;
+               }
+
+               data->temp_input = value;
+               data->next_update = jiffies + HZ / 4;
+               data->valid = true;
+       }
+
+ret_unlock:
+       mutex_unlock(&data->lock);
+
+       return ret;
+}
+
+static ssize_t show_temp_input(struct device *dev,
+                              struct device_attribute *attr, char *buf)
+{
+       struct tc74_data *data = dev_get_drvdata(dev);
+       int ret;
+
+       ret = tc74_update_device(dev);
+       if (ret)
+               return ret;
+
+       return sprintf(buf, "%d\n", data->temp_input * 1000);
+}
+static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp_input, NULL, 0);
+
+static struct attribute *tc74_attrs[] = {
+       &sensor_dev_attr_temp1_input.dev_attr.attr,
+       NULL
+};
+
+ATTRIBUTE_GROUPS(tc74);
+
+static int tc74_probe(struct i2c_client *client,
+                     const struct i2c_device_id *dev_id)
+{
+       struct device *dev = &client->dev;
+       struct tc74_data *data;
+       struct device *hwmon_dev;
+       s32 conf;
+
+       if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+               return -EOPNOTSUPP;
+
+       data = devm_kzalloc(dev, sizeof(struct tc74_data), GFP_KERNEL);
+       if (!data)
+               return -ENOMEM;
+
+       data->client = client;
+       mutex_init(&data->lock);
+
+       /* Make sure the chip is powered up. */
+       conf = i2c_smbus_read_byte_data(client, TC74_REG_CONFIG);
+       if (conf < 0) {
+               dev_err(dev, "unable to read config register\n");
+
+               return conf;
+       }
+
+       if (conf & 0x3f) {
+               dev_err(dev, "invalid config register value\n");
+
+               return -ENODEV;
+       }
+
+       if (conf & BIT(7)) {
+               s32 ret;
+
+               conf &= ~BIT(7);
+
+               ret = i2c_smbus_write_byte_data(client, TC74_REG_CONFIG, conf);
+               if (ret)
+                       dev_warn(dev, "unable to disable STANDBY\n");
+       }
+
+       hwmon_dev = devm_hwmon_device_register_with_groups(dev,
+                                                          client->name,
+                                                          data, tc74_groups);
+       return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+static const struct i2c_device_id tc74_id[] = {
+       { "tc74", 0 },
+       {}
+};
+MODULE_DEVICE_TABLE(i2c, tc74_id);
+
+static struct i2c_driver tc74_driver = {
+       .driver = {
+               .name   = "tc74",
+       },
+       .probe  = tc74_probe,
+       .id_table = tc74_id,
+};
+
+module_i2c_driver(tc74_driver);
+
+MODULE_AUTHOR("Maciej Szmigiero <mail@maciej.szmigiero.name>");
+
+MODULE_DESCRIPTION("TC74 driver");
+MODULE_LICENSE("GPL");
index 2255af23b9c70e69ce3105fd2f3d3e5b80a757c9..5f1c1c4f5d8719ba2b68648ecb428780529133a9 100644 (file)
@@ -1103,7 +1103,7 @@ config I2C_SIBYTE
 
 config I2C_CROS_EC_TUNNEL
        tristate "ChromeOS EC tunnel I2C bus"
-       depends on MFD_CROS_EC
+       depends on CROS_EC_PROTO
        help
          If you say yes here you get an I2C bus that will tunnel i2c commands
          through to the other side of the ChromeOS EC to the i2c bus
index fa8dedd8c3a2f88ca0fdf05ce784b79f3852c51c..a0d95ff682ae120186a689dd0dcdd74f3d65b0f3 100644 (file)
@@ -182,8 +182,9 @@ static int ec_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg i2c_msgs[],
        const u16 bus_num = bus->remote_bus;
        int request_len;
        int response_len;
+       int alloc_size;
        int result;
-       struct cros_ec_command msg = { };
+       struct cros_ec_command *msg;
 
        request_len = ec_i2c_count_message(i2c_msgs, num);
        if (request_len < 0) {
@@ -198,25 +199,39 @@ static int ec_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg i2c_msgs[],
                return response_len;
        }
 
-       result = ec_i2c_construct_message(msg.outdata, i2c_msgs, num, bus_num);
-       if (result)
-               return result;
+       alloc_size = max(request_len, response_len);
+       msg = kmalloc(sizeof(*msg) + alloc_size, GFP_KERNEL);
+       if (!msg)
+               return -ENOMEM;
 
-       msg.version = 0;
-       msg.command = EC_CMD_I2C_PASSTHRU;
-       msg.outsize = request_len;
-       msg.insize = response_len;
+       result = ec_i2c_construct_message(msg->data, i2c_msgs, num, bus_num);
+       if (result) {
+               dev_err(dev, "Error constructing EC i2c message %d\n", result);
+               goto exit;
+       }
 
-       result = cros_ec_cmd_xfer(bus->ec, &msg);
-       if (result < 0)
-               return result;
+       msg->version = 0;
+       msg->command = EC_CMD_I2C_PASSTHRU;
+       msg->outsize = request_len;
+       msg->insize = response_len;
 
-       result = ec_i2c_parse_response(msg.indata, i2c_msgs, &num);
-       if (result < 0)
-               return result;
+       result = cros_ec_cmd_xfer(bus->ec, msg);
+       if (result < 0) {
+               dev_err(dev, "Error transferring EC i2c message %d\n", result);
+               goto exit;
+       }
+
+       result = ec_i2c_parse_response(msg->data, i2c_msgs, &num);
+       if (result < 0) {
+               dev_err(dev, "Error parsing EC i2c message %d\n", result);
+               goto exit;
+       }
 
        /* Indicate success by saying how many messages were sent */
-       return num;
+       result = num;
+exit:
+       kfree(msg);
+       return result;
 }
 
 static u32 ec_i2c_functionality(struct i2c_adapter *adap)
index 8fe78d08e01cf1551ea0eaf53f50d2185dfff809..7c6966434ee7b9a2707da849e56128af41c0baf5 100644 (file)
@@ -554,4 +554,4 @@ module_platform_driver(hix5hd2_i2c_driver);
 MODULE_DESCRIPTION("Hix5hd2 I2C Bus driver");
 MODULE_AUTHOR("Wei Yan <sledge.yanwei@huawei.com>");
 MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:i2c-hix5hd2");
+MODULE_ALIAS("platform:hix5hd2-i2c");
index 67cbec6796a0eee50d0047e16a9db757f27836a9..630bce68bf3814e59d443773a30a3a4aaa01291f 100644 (file)
@@ -245,7 +245,7 @@ static int piix4_setup_sb800(struct pci_dev *PIIX4_dev,
             PIIX4_dev->device == PCI_DEVICE_ID_AMD_HUDSON2_SMBUS &&
             PIIX4_dev->revision >= 0x41) ||
            (PIIX4_dev->vendor == PCI_VENDOR_ID_AMD &&
-            PIIX4_dev->device == 0x790b &&
+            PIIX4_dev->device == PCI_DEVICE_ID_AMD_KERNCZ_SMBUS &&
             PIIX4_dev->revision >= 0x49))
                smb_en = 0x00;
        else
@@ -545,7 +545,7 @@ static const struct pci_device_id piix4_ids[] = {
        { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP400_SMBUS) },
        { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_SBX00_SMBUS) },
        { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_HUDSON2_SMBUS) },
-       { PCI_DEVICE(PCI_VENDOR_ID_AMD, 0x790b) },
+       { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_KERNCZ_SMBUS) },
        { PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS,
                     PCI_DEVICE_ID_SERVERWORKS_OSB4) },
        { PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS,
index 958c8db4ec30740e2d9aae00a7835256700d3424..297e9c9ac9432f5e645e06cf932710cd93c7f924 100644 (file)
@@ -1143,6 +1143,7 @@ static int s3c24xx_i2c_probe(struct platform_device *pdev)
                return -ENOMEM;
 
        i2c->quirks = s3c24xx_get_device_quirks(pdev);
+       i2c->sysreg = ERR_PTR(-ENOENT);
        if (pdata)
                memcpy(i2c->pdata, pdata, sizeof(*pdata));
        else
index 987c124432c501f7a5cabb83bdc638c0f337fcb7..fc2ee8213fb68b1f9419babdd587098a1a2920d9 100644 (file)
@@ -107,7 +107,7 @@ static int acpi_i2c_add_resource(struct acpi_resource *ares, void *data)
                        if (sb->access_mode == ACPI_I2C_10BIT_MODE)
                                info->flags |= I2C_CLIENT_TEN;
                }
-       } else if (info->irq < 0) {
+       } else if (!info->irq) {
                struct resource r;
 
                if (acpi_dev_resource_interrupt(ares, 0, &r))
@@ -134,7 +134,6 @@ static acpi_status acpi_i2c_add_device(acpi_handle handle, u32 level,
 
        memset(&info, 0, sizeof(info));
        info.fwnode = acpi_fwnode_handle(adev);
-       info.irq = -1;
 
        INIT_LIST_HEAD(&resource_list);
        ret = acpi_dev_get_resources(adev, &resource_list,
@@ -632,8 +631,13 @@ static int i2c_device_probe(struct device *dev)
        if (!client)
                return 0;
 
-       if (!client->irq && dev->of_node) {
-               int irq = of_irq_get(dev->of_node, 0);
+       if (!client->irq) {
+               int irq = -ENOENT;
+
+               if (dev->of_node)
+                       irq = of_irq_get(dev->of_node, 0);
+               else if (ACPI_COMPANION(dev))
+                       irq = acpi_dev_gpio_irq_get(ACPI_COMPANION(dev), 0);
 
                if (irq == -EPROBE_DEFER)
                        return irq;
index 89d8aa1d2818502f974c92f7925ea4440df3d97d..df12c57e6ce07a700d211b81c9b5d3c9c15ff2d3 100644 (file)
@@ -1001,7 +1001,7 @@ static struct platform_driver twl6030_gpadc_driver = {
 
 module_platform_driver(twl6030_gpadc_driver);
 
-MODULE_ALIAS("platform: " DRIVER_NAME);
+MODULE_ALIAS("platform:" DRIVER_NAME);
 MODULE_AUTHOR("Balaji T K <balajitk@ti.com>");
 MODULE_AUTHOR("Graeme Gregory <gg@slimlogic.co.uk>");
 MODULE_AUTHOR("Oleksandr Kozaruk <oleksandr.kozaruk@ti.com");
index 0916bf6b6c311c503931f26387712c6677b17645..73b189c1c0fb0fdcc73d64b7118ab4f79a12d41b 100644 (file)
 #define ADIS16400_NO_BURST             BIT(1)
 #define ADIS16400_HAS_SLOW_MODE                BIT(2)
 #define ADIS16400_HAS_SERIAL_NUMBER    BIT(3)
+#define ADIS16400_BURST_DIAG_STAT      BIT(4)
 
 struct adis16400_state;
 
@@ -165,6 +166,7 @@ struct adis16400_state {
        int                             filt_int;
 
        struct adis adis;
+       unsigned long avail_scan_mask[2];
 };
 
 /* At the moment triggers are only used for ring buffer
index 6e727ffe52621f43bb40f31466730705477961ef..90c24a23c679b8001e31cdff48b098cacb872682 100644 (file)
@@ -18,7 +18,8 @@ int adis16400_update_scan_mode(struct iio_dev *indio_dev,
 {
        struct adis16400_state *st = iio_priv(indio_dev);
        struct adis *adis = &st->adis;
-       uint16_t *tx;
+       unsigned int burst_length;
+       u8 *tx;
 
        if (st->variant->flags & ADIS16400_NO_BURST)
                return adis_update_scan_mode(indio_dev, scan_mask);
@@ -26,26 +27,29 @@ int adis16400_update_scan_mode(struct iio_dev *indio_dev,
        kfree(adis->xfer);
        kfree(adis->buffer);
 
+       /* All but the timestamp channel */
+       burst_length = (indio_dev->num_channels - 1) * sizeof(u16);
+       if (st->variant->flags & ADIS16400_BURST_DIAG_STAT)
+               burst_length += sizeof(u16);
+
        adis->xfer = kcalloc(2, sizeof(*adis->xfer), GFP_KERNEL);
        if (!adis->xfer)
                return -ENOMEM;
 
-       adis->buffer = kzalloc(indio_dev->scan_bytes + sizeof(u16),
-               GFP_KERNEL);
+       adis->buffer = kzalloc(burst_length + sizeof(u16), GFP_KERNEL);
        if (!adis->buffer)
                return -ENOMEM;
 
-       tx = adis->buffer + indio_dev->scan_bytes;
-
+       tx = adis->buffer + burst_length;
        tx[0] = ADIS_READ_REG(ADIS16400_GLOB_CMD);
        tx[1] = 0;
 
        adis->xfer[0].tx_buf = tx;
        adis->xfer[0].bits_per_word = 8;
        adis->xfer[0].len = 2;
-       adis->xfer[1].tx_buf = tx;
+       adis->xfer[1].rx_buf = adis->buffer;
        adis->xfer[1].bits_per_word = 8;
-       adis->xfer[1].len = indio_dev->scan_bytes;
+       adis->xfer[1].len = burst_length;
 
        spi_message_init(&adis->msg);
        spi_message_add_tail(&adis->xfer[0], &adis->msg);
@@ -61,6 +65,7 @@ irqreturn_t adis16400_trigger_handler(int irq, void *p)
        struct adis16400_state *st = iio_priv(indio_dev);
        struct adis *adis = &st->adis;
        u32 old_speed_hz = st->adis.spi->max_speed_hz;
+       void *buffer;
        int ret;
 
        if (!adis->buffer)
@@ -81,7 +86,12 @@ irqreturn_t adis16400_trigger_handler(int irq, void *p)
                spi_setup(st->adis.spi);
        }
 
-       iio_push_to_buffers_with_timestamp(indio_dev, adis->buffer,
+       if (st->variant->flags & ADIS16400_BURST_DIAG_STAT)
+               buffer = adis->buffer + sizeof(u16);
+       else
+               buffer = adis->buffer;
+
+       iio_push_to_buffers_with_timestamp(indio_dev, buffer,
                pf->timestamp);
 
        iio_trigger_notify_done(indio_dev->trig);
index fa795dcd5f75ec0a1e8de143bc0122ef36bf9409..2fd68f2219a7d422a604b91ce90138f1050528cd 100644 (file)
@@ -405,6 +405,11 @@ static int adis16400_read_raw(struct iio_dev *indio_dev,
                        *val = st->variant->temp_scale_nano / 1000000;
                        *val2 = (st->variant->temp_scale_nano % 1000000);
                        return IIO_VAL_INT_PLUS_MICRO;
+               case IIO_PRESSURE:
+                       /* 20 uBar = 0.002kPascal */
+                       *val = 0;
+                       *val2 = 2000;
+                       return IIO_VAL_INT_PLUS_MICRO;
                default:
                        return -EINVAL;
                }
@@ -454,10 +459,10 @@ static int adis16400_read_raw(struct iio_dev *indio_dev,
        }
 }
 
-#define ADIS16400_VOLTAGE_CHAN(addr, bits, name, si) { \
+#define ADIS16400_VOLTAGE_CHAN(addr, bits, name, si, chn) { \
        .type = IIO_VOLTAGE, \
        .indexed = 1, \
-       .channel = 0, \
+       .channel = chn, \
        .extend_name = name, \
        .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
                BIT(IIO_CHAN_INFO_SCALE), \
@@ -474,10 +479,10 @@ static int adis16400_read_raw(struct iio_dev *indio_dev,
 }
 
 #define ADIS16400_SUPPLY_CHAN(addr, bits) \
-       ADIS16400_VOLTAGE_CHAN(addr, bits, "supply", ADIS16400_SCAN_SUPPLY)
+       ADIS16400_VOLTAGE_CHAN(addr, bits, "supply", ADIS16400_SCAN_SUPPLY, 0)
 
 #define ADIS16400_AUX_ADC_CHAN(addr, bits) \
-       ADIS16400_VOLTAGE_CHAN(addr, bits, NULL, ADIS16400_SCAN_ADC)
+       ADIS16400_VOLTAGE_CHAN(addr, bits, NULL, ADIS16400_SCAN_ADC, 1)
 
 #define ADIS16400_GYRO_CHAN(mod, addr, bits) { \
        .type = IIO_ANGL_VEL, \
@@ -773,7 +778,8 @@ static struct adis16400_chip_info adis16400_chips[] = {
                .channels = adis16448_channels,
                .num_channels = ARRAY_SIZE(adis16448_channels),
                .flags = ADIS16400_HAS_PROD_ID |
-                               ADIS16400_HAS_SERIAL_NUMBER,
+                               ADIS16400_HAS_SERIAL_NUMBER |
+                               ADIS16400_BURST_DIAG_STAT,
                .gyro_scale_micro = IIO_DEGREE_TO_RAD(10000), /* 0.01 deg/s */
                .accel_scale_micro = IIO_G_TO_M_S_2(833), /* 1/1200 g */
                .temp_scale_nano = 73860000, /* 0.07386 C */
@@ -791,11 +797,6 @@ static const struct iio_info adis16400_info = {
        .debugfs_reg_access = adis_debugfs_reg_access,
 };
 
-static const unsigned long adis16400_burst_scan_mask[] = {
-       ~0UL,
-       0,
-};
-
 static const char * const adis16400_status_error_msgs[] = {
        [ADIS16400_DIAG_STAT_ZACCL_FAIL] = "Z-axis accelerometer self-test failure",
        [ADIS16400_DIAG_STAT_YACCL_FAIL] = "Y-axis accelerometer self-test failure",
@@ -843,6 +844,20 @@ static const struct adis_data adis16400_data = {
                BIT(ADIS16400_DIAG_STAT_POWER_LOW),
 };
 
+static void adis16400_setup_chan_mask(struct adis16400_state *st)
+{
+       const struct adis16400_chip_info *chip_info = st->variant;
+       unsigned i;
+
+       for (i = 0; i < chip_info->num_channels; i++) {
+               const struct iio_chan_spec *ch = &chip_info->channels[i];
+
+               if (ch->scan_index >= 0 &&
+                   ch->scan_index != ADIS16400_SCAN_TIMESTAMP)
+                       st->avail_scan_mask[0] |= BIT(ch->scan_index);
+       }
+}
+
 static int adis16400_probe(struct spi_device *spi)
 {
        struct adis16400_state *st;
@@ -866,8 +881,10 @@ static int adis16400_probe(struct spi_device *spi)
        indio_dev->info = &adis16400_info;
        indio_dev->modes = INDIO_DIRECT_MODE;
 
-       if (!(st->variant->flags & ADIS16400_NO_BURST))
-               indio_dev->available_scan_masks = adis16400_burst_scan_mask;
+       if (!(st->variant->flags & ADIS16400_NO_BURST)) {
+               adis16400_setup_chan_mask(st);
+               indio_dev->available_scan_masks = st->avail_scan_mask;
+       }
 
        ret = adis_init(&st->adis, indio_dev, spi, &adis16400_data);
        if (ret)
index 38339d220d7f52c402c08f581f20cf4ad8acea49..746cdf56bc76475831cdf39b93a131f24fe804a7 100644 (file)
@@ -457,8 +457,8 @@ static void resolve_cb(int status, struct sockaddr *src_addr,
        complete(&((struct resolve_cb_context *)context)->comp);
 }
 
-int rdma_addr_find_dmac_by_grh(union ib_gid *sgid, union ib_gid *dgid, u8 *dmac,
-                              u16 *vlan_id)
+int rdma_addr_find_dmac_by_grh(const union ib_gid *sgid, const union ib_gid *dgid,
+                              u8 *dmac, u16 *vlan_id)
 {
        int ret = 0;
        struct rdma_dev_addr dev_addr;
index f6d29614cb016e79e57a76c50452e16260709ca3..c7dcfe4ca5f10219e553cd4cb5acdd2e1658c95b 100644 (file)
@@ -54,7 +54,7 @@ static DEFINE_SPINLOCK(ib_agent_port_list_lock);
 static LIST_HEAD(ib_agent_port_list);
 
 static struct ib_agent_port_private *
-__ib_get_agent_port(struct ib_device *device, int port_num)
+__ib_get_agent_port(const struct ib_device *device, int port_num)
 {
        struct ib_agent_port_private *entry;
 
@@ -67,7 +67,7 @@ __ib_get_agent_port(struct ib_device *device, int port_num)
 }
 
 static struct ib_agent_port_private *
-ib_get_agent_port(struct ib_device *device, int port_num)
+ib_get_agent_port(const struct ib_device *device, int port_num)
 {
        struct ib_agent_port_private *entry;
        unsigned long flags;
@@ -78,9 +78,9 @@ ib_get_agent_port(struct ib_device *device, int port_num)
        return entry;
 }
 
-void agent_send_response(struct ib_mad *mad, struct ib_grh *grh,
-                        struct ib_wc *wc, struct ib_device *device,
-                        int port_num, int qpn)
+void agent_send_response(const struct ib_mad_hdr *mad_hdr, const struct ib_grh *grh,
+                        const struct ib_wc *wc, const struct ib_device *device,
+                        int port_num, int qpn, size_t resp_mad_len, bool opa)
 {
        struct ib_agent_port_private *port_priv;
        struct ib_mad_agent *agent;
@@ -106,15 +106,20 @@ void agent_send_response(struct ib_mad *mad, struct ib_grh *grh,
                return;
        }
 
+       if (opa && mad_hdr->base_version != OPA_MGMT_BASE_VERSION)
+               resp_mad_len = IB_MGMT_MAD_SIZE;
+
        send_buf = ib_create_send_mad(agent, wc->src_qp, wc->pkey_index, 0,
-                                     IB_MGMT_MAD_HDR, IB_MGMT_MAD_DATA,
-                                     GFP_KERNEL);
+                                     IB_MGMT_MAD_HDR,
+                                     resp_mad_len - IB_MGMT_MAD_HDR,
+                                     GFP_KERNEL,
+                                     mad_hdr->base_version);
        if (IS_ERR(send_buf)) {
                dev_err(&device->dev, "ib_create_send_mad error\n");
                goto err1;
        }
 
-       memcpy(send_buf->mad, mad, sizeof *mad);
+       memcpy(send_buf->mad, mad_hdr, resp_mad_len);
        send_buf->ah = ah;
 
        if (device->node_type == RDMA_NODE_IB_SWITCH) {
@@ -156,7 +161,7 @@ int ib_agent_port_open(struct ib_device *device, int port_num)
                goto error1;
        }
 
-       if (rdma_port_get_link_layer(device, port_num) == IB_LINK_LAYER_INFINIBAND) {
+       if (rdma_cap_ib_smi(device, port_num)) {
                /* Obtain send only MAD agent for SMI QP */
                port_priv->agent[0] = ib_register_mad_agent(device, port_num,
                                                            IB_QPT_SMI, NULL, 0,
index 6669287009c2fc4d60514d56da5fb2461532e395..65f92bedae448fc698de65804bc35b6114e2f22e 100644 (file)
@@ -44,8 +44,8 @@ extern int ib_agent_port_open(struct ib_device *device, int port_num);
 
 extern int ib_agent_port_close(struct ib_device *device, int port_num);
 
-extern void agent_send_response(struct ib_mad *mad, struct ib_grh *grh,
-                               struct ib_wc *wc, struct ib_device *device,
-                               int port_num, int qpn);
+extern void agent_send_response(const struct ib_mad_hdr *mad_hdr, const struct ib_grh *grh,
+                               const struct ib_wc *wc, const struct ib_device *device,
+                               int port_num, int qpn, size_t resp_mad_len, bool opa);
 
 #endif /* __AGENT_H_ */
index 80f6cf2449fb9b852533d254ab8e6cbabc706156..871da832d016a7a9b6305047f6e512bc2a936b68 100644 (file)
@@ -58,17 +58,6 @@ struct ib_update_work {
        u8                 port_num;
 };
 
-static inline int start_port(struct ib_device *device)
-{
-       return (device->node_type == RDMA_NODE_IB_SWITCH) ? 0 : 1;
-}
-
-static inline int end_port(struct ib_device *device)
-{
-       return (device->node_type == RDMA_NODE_IB_SWITCH) ?
-               0 : device->phys_port_cnt;
-}
-
 int ib_get_cached_gid(struct ib_device *device,
                      u8                port_num,
                      int               index,
@@ -78,12 +67,12 @@ int ib_get_cached_gid(struct ib_device *device,
        unsigned long flags;
        int ret = 0;
 
-       if (port_num < start_port(device) || port_num > end_port(device))
+       if (port_num < rdma_start_port(device) || port_num > rdma_end_port(device))
                return -EINVAL;
 
        read_lock_irqsave(&device->cache.lock, flags);
 
-       cache = device->cache.gid_cache[port_num - start_port(device)];
+       cache = device->cache.gid_cache[port_num - rdma_start_port(device)];
 
        if (index < 0 || index >= cache->table_len)
                ret = -EINVAL;
@@ -96,10 +85,10 @@ int ib_get_cached_gid(struct ib_device *device,
 }
 EXPORT_SYMBOL(ib_get_cached_gid);
 
-int ib_find_cached_gid(struct ib_device *device,
-                      union ib_gid     *gid,
-                      u8               *port_num,
-                      u16              *index)
+int ib_find_cached_gid(struct ib_device   *device,
+                      const union ib_gid *gid,
+                      u8                 *port_num,
+                      u16                *index)
 {
        struct ib_gid_cache *cache;
        unsigned long flags;
@@ -112,11 +101,11 @@ int ib_find_cached_gid(struct ib_device *device,
 
        read_lock_irqsave(&device->cache.lock, flags);
 
-       for (p = 0; p <= end_port(device) - start_port(device); ++p) {
+       for (p = 0; p <= rdma_end_port(device) - rdma_start_port(device); ++p) {
                cache = device->cache.gid_cache[p];
                for (i = 0; i < cache->table_len; ++i) {
                        if (!memcmp(gid, &cache->table[i], sizeof *gid)) {
-                               *port_num = p + start_port(device);
+                               *port_num = p + rdma_start_port(device);
                                if (index)
                                        *index = i;
                                ret = 0;
@@ -140,12 +129,12 @@ int ib_get_cached_pkey(struct ib_device *device,
        unsigned long flags;
        int ret = 0;
 
-       if (port_num < start_port(device) || port_num > end_port(device))
+       if (port_num < rdma_start_port(device) || port_num > rdma_end_port(device))
                return -EINVAL;
 
        read_lock_irqsave(&device->cache.lock, flags);
 
-       cache = device->cache.pkey_cache[port_num - start_port(device)];
+       cache = device->cache.pkey_cache[port_num - rdma_start_port(device)];
 
        if (index < 0 || index >= cache->table_len)
                ret = -EINVAL;
@@ -169,12 +158,12 @@ int ib_find_cached_pkey(struct ib_device *device,
        int ret = -ENOENT;
        int partial_ix = -1;
 
-       if (port_num < start_port(device) || port_num > end_port(device))
+       if (port_num < rdma_start_port(device) || port_num > rdma_end_port(device))
                return -EINVAL;
 
        read_lock_irqsave(&device->cache.lock, flags);
 
-       cache = device->cache.pkey_cache[port_num - start_port(device)];
+       cache = device->cache.pkey_cache[port_num - rdma_start_port(device)];
 
        *index = -1;
 
@@ -209,12 +198,12 @@ int ib_find_exact_cached_pkey(struct ib_device *device,
        int i;
        int ret = -ENOENT;
 
-       if (port_num < start_port(device) || port_num > end_port(device))
+       if (port_num < rdma_start_port(device) || port_num > rdma_end_port(device))
                return -EINVAL;
 
        read_lock_irqsave(&device->cache.lock, flags);
 
-       cache = device->cache.pkey_cache[port_num - start_port(device)];
+       cache = device->cache.pkey_cache[port_num - rdma_start_port(device)];
 
        *index = -1;
 
@@ -238,11 +227,11 @@ int ib_get_cached_lmc(struct ib_device *device,
        unsigned long flags;
        int ret = 0;
 
-       if (port_num < start_port(device) || port_num > end_port(device))
+       if (port_num < rdma_start_port(device) || port_num > rdma_end_port(device))
                return -EINVAL;
 
        read_lock_irqsave(&device->cache.lock, flags);
-       *lmc = device->cache.lmc_cache[port_num - start_port(device)];
+       *lmc = device->cache.lmc_cache[port_num - rdma_start_port(device)];
        read_unlock_irqrestore(&device->cache.lock, flags);
 
        return ret;
@@ -303,13 +292,13 @@ static void ib_cache_update(struct ib_device *device,
 
        write_lock_irq(&device->cache.lock);
 
-       old_pkey_cache = device->cache.pkey_cache[port - start_port(device)];
-       old_gid_cache  = device->cache.gid_cache [port - start_port(device)];
+       old_pkey_cache = device->cache.pkey_cache[port - rdma_start_port(device)];
+       old_gid_cache  = device->cache.gid_cache [port - rdma_start_port(device)];
 
-       device->cache.pkey_cache[port - start_port(device)] = pkey_cache;
-       device->cache.gid_cache [port - start_port(device)] = gid_cache;
+       device->cache.pkey_cache[port - rdma_start_port(device)] = pkey_cache;
+       device->cache.gid_cache [port - rdma_start_port(device)] = gid_cache;
 
-       device->cache.lmc_cache[port - start_port(device)] = tprops->lmc;
+       device->cache.lmc_cache[port - rdma_start_port(device)] = tprops->lmc;
 
        write_unlock_irq(&device->cache.lock);
 
@@ -363,14 +352,14 @@ static void ib_cache_setup_one(struct ib_device *device)
 
        device->cache.pkey_cache =
                kmalloc(sizeof *device->cache.pkey_cache *
-                       (end_port(device) - start_port(device) + 1), GFP_KERNEL);
+                       (rdma_end_port(device) - rdma_start_port(device) + 1), GFP_KERNEL);
        device->cache.gid_cache =
                kmalloc(sizeof *device->cache.gid_cache *
-                       (end_port(device) - start_port(device) + 1), GFP_KERNEL);
+                       (rdma_end_port(device) - rdma_start_port(device) + 1), GFP_KERNEL);
 
        device->cache.lmc_cache = kmalloc(sizeof *device->cache.lmc_cache *
-                                         (end_port(device) -
-                                          start_port(device) + 1),
+                                         (rdma_end_port(device) -
+                                          rdma_start_port(device) + 1),
                                          GFP_KERNEL);
 
        if (!device->cache.pkey_cache || !device->cache.gid_cache ||
@@ -380,10 +369,10 @@ static void ib_cache_setup_one(struct ib_device *device)
                goto err;
        }
 
-       for (p = 0; p <= end_port(device) - start_port(device); ++p) {
+       for (p = 0; p <= rdma_end_port(device) - rdma_start_port(device); ++p) {
                device->cache.pkey_cache[p] = NULL;
                device->cache.gid_cache [p] = NULL;
-               ib_cache_update(device, p + start_port(device));
+               ib_cache_update(device, p + rdma_start_port(device));
        }
 
        INIT_IB_EVENT_HANDLER(&device->cache.event_handler,
@@ -394,7 +383,7 @@ static void ib_cache_setup_one(struct ib_device *device)
        return;
 
 err_cache:
-       for (p = 0; p <= end_port(device) - start_port(device); ++p) {
+       for (p = 0; p <= rdma_end_port(device) - rdma_start_port(device); ++p) {
                kfree(device->cache.pkey_cache[p]);
                kfree(device->cache.gid_cache[p]);
        }
@@ -412,7 +401,7 @@ static void ib_cache_cleanup_one(struct ib_device *device)
        ib_unregister_event_handler(&device->cache.event_handler);
        flush_workqueue(ib_wq);
 
-       for (p = 0; p <= end_port(device) - start_port(device); ++p) {
+       for (p = 0; p <= rdma_end_port(device) - rdma_start_port(device); ++p) {
                kfree(device->cache.pkey_cache[p]);
                kfree(device->cache.gid_cache[p]);
        }
index 0271608a51c40ff2ade721f94b710997ffbe5c2d..dbddddd6fb5d111e94e44e2800282c84312131a0 100644 (file)
@@ -267,7 +267,8 @@ static int cm_alloc_msg(struct cm_id_private *cm_id_priv,
        m = ib_create_send_mad(mad_agent, cm_id_priv->id.remote_cm_qpn,
                               cm_id_priv->av.pkey_index,
                               0, IB_MGMT_MAD_HDR, IB_MGMT_MAD_DATA,
-                              GFP_ATOMIC);
+                              GFP_ATOMIC,
+                              IB_MGMT_BASE_VERSION);
        if (IS_ERR(m)) {
                ib_destroy_ah(ah);
                return PTR_ERR(m);
@@ -297,7 +298,8 @@ static int cm_alloc_response_msg(struct cm_port *port,
 
        m = ib_create_send_mad(port->mad_agent, 1, mad_recv_wc->wc->pkey_index,
                               0, IB_MGMT_MAD_HDR, IB_MGMT_MAD_DATA,
-                              GFP_ATOMIC);
+                              GFP_ATOMIC,
+                              IB_MGMT_BASE_VERSION);
        if (IS_ERR(m)) {
                ib_destroy_ah(ah);
                return PTR_ERR(m);
@@ -3759,11 +3761,9 @@ static void cm_add_one(struct ib_device *ib_device)
        };
        unsigned long flags;
        int ret;
+       int count = 0;
        u8 i;
 
-       if (rdma_node_get_transport(ib_device->node_type) != RDMA_TRANSPORT_IB)
-               return;
-
        cm_dev = kzalloc(sizeof(*cm_dev) + sizeof(*port) *
                         ib_device->phys_port_cnt, GFP_KERNEL);
        if (!cm_dev)
@@ -3782,6 +3782,9 @@ static void cm_add_one(struct ib_device *ib_device)
 
        set_bit(IB_MGMT_METHOD_SEND, reg_req.method_mask);
        for (i = 1; i <= ib_device->phys_port_cnt; i++) {
+               if (!rdma_cap_ib_cm(ib_device, i))
+                       continue;
+
                port = kzalloc(sizeof *port, GFP_KERNEL);
                if (!port)
                        goto error1;
@@ -3808,7 +3811,13 @@ static void cm_add_one(struct ib_device *ib_device)
                ret = ib_modify_port(ib_device, i, 0, &port_modify);
                if (ret)
                        goto error3;
+
+               count++;
        }
+
+       if (!count)
+               goto free;
+
        ib_set_client_data(ib_device, &cm_client, cm_dev);
 
        write_lock_irqsave(&cm.device_lock, flags);
@@ -3824,11 +3833,15 @@ error1:
        port_modify.set_port_cap_mask = 0;
        port_modify.clr_port_cap_mask = IB_PORT_CM_SUP;
        while (--i) {
+               if (!rdma_cap_ib_cm(ib_device, i))
+                       continue;
+
                port = cm_dev->port[i-1];
                ib_modify_port(ib_device, port->port_num, 0, &port_modify);
                ib_unregister_mad_agent(port->mad_agent);
                cm_remove_port_fs(port);
        }
+free:
        device_unregister(cm_dev->device);
        kfree(cm_dev);
 }
@@ -3852,6 +3865,9 @@ static void cm_remove_one(struct ib_device *ib_device)
        write_unlock_irqrestore(&cm.device_lock, flags);
 
        for (i = 1; i <= ib_device->phys_port_cnt; i++) {
+               if (!rdma_cap_ib_cm(ib_device, i))
+                       continue;
+
                port = cm_dev->port[i-1];
                ib_modify_port(ib_device, port->port_num, 0, &port_modify);
                ib_unregister_mad_agent(port->mad_agent);
index 38ffe098150351aef9ff2ac650726ae7926f6856..143ded2bbe7c7fbd8d51a6d952aab1f7cf101b35 100644 (file)
@@ -65,6 +65,34 @@ MODULE_LICENSE("Dual BSD/GPL");
 #define CMA_CM_MRA_SETTING (IB_CM_MRA_FLAG_DELAY | 24)
 #define CMA_IBOE_PACKET_LIFETIME 18
 
+static const char * const cma_events[] = {
+       [RDMA_CM_EVENT_ADDR_RESOLVED]    = "address resolved",
+       [RDMA_CM_EVENT_ADDR_ERROR]       = "address error",
+       [RDMA_CM_EVENT_ROUTE_RESOLVED]   = "route resolved ",
+       [RDMA_CM_EVENT_ROUTE_ERROR]      = "route error",
+       [RDMA_CM_EVENT_CONNECT_REQUEST]  = "connect request",
+       [RDMA_CM_EVENT_CONNECT_RESPONSE] = "connect response",
+       [RDMA_CM_EVENT_CONNECT_ERROR]    = "connect error",
+       [RDMA_CM_EVENT_UNREACHABLE]      = "unreachable",
+       [RDMA_CM_EVENT_REJECTED]         = "rejected",
+       [RDMA_CM_EVENT_ESTABLISHED]      = "established",
+       [RDMA_CM_EVENT_DISCONNECTED]     = "disconnected",
+       [RDMA_CM_EVENT_DEVICE_REMOVAL]   = "device removal",
+       [RDMA_CM_EVENT_MULTICAST_JOIN]   = "multicast join",
+       [RDMA_CM_EVENT_MULTICAST_ERROR]  = "multicast error",
+       [RDMA_CM_EVENT_ADDR_CHANGE]      = "address change",
+       [RDMA_CM_EVENT_TIMEWAIT_EXIT]    = "timewait exit",
+};
+
+const char *rdma_event_msg(enum rdma_cm_event_type event)
+{
+       size_t index = event;
+
+       return (index < ARRAY_SIZE(cma_events) && cma_events[index]) ?
+                       cma_events[index] : "unrecognized event";
+}
+EXPORT_SYMBOL(rdma_event_msg);
+
 static void cma_add_one(struct ib_device *device);
 static void cma_remove_one(struct ib_device *device);
 
@@ -349,18 +377,35 @@ static int cma_translate_addr(struct sockaddr *addr, struct rdma_dev_addr *dev_a
        return ret;
 }
 
+static inline int cma_validate_port(struct ib_device *device, u8 port,
+                                     union ib_gid *gid, int dev_type)
+{
+       u8 found_port;
+       int ret = -ENODEV;
+
+       if ((dev_type == ARPHRD_INFINIBAND) && !rdma_protocol_ib(device, port))
+               return ret;
+
+       if ((dev_type != ARPHRD_INFINIBAND) && rdma_protocol_ib(device, port))
+               return ret;
+
+       ret = ib_find_cached_gid(device, gid, &found_port, NULL);
+       if (port != found_port)
+               return -ENODEV;
+
+       return ret;
+}
+
 static int cma_acquire_dev(struct rdma_id_private *id_priv,
                           struct rdma_id_private *listen_id_priv)
 {
        struct rdma_dev_addr *dev_addr = &id_priv->id.route.addr.dev_addr;
        struct cma_device *cma_dev;
-       union ib_gid gid, iboe_gid;
+       union ib_gid gid, iboe_gid, *gidp;
        int ret = -ENODEV;
-       u8 port, found_port;
-       enum rdma_link_layer dev_ll = dev_addr->dev_type == ARPHRD_INFINIBAND ?
-               IB_LINK_LAYER_INFINIBAND : IB_LINK_LAYER_ETHERNET;
+       u8 port;
 
-       if (dev_ll != IB_LINK_LAYER_INFINIBAND &&
+       if (dev_addr->dev_type != ARPHRD_INFINIBAND &&
            id_priv->id.ps == RDMA_PS_IPOIB)
                return -EINVAL;
 
@@ -370,41 +415,36 @@ static int cma_acquire_dev(struct rdma_id_private *id_priv,
 
        memcpy(&gid, dev_addr->src_dev_addr +
               rdma_addr_gid_offset(dev_addr), sizeof gid);
-       if (listen_id_priv &&
-           rdma_port_get_link_layer(listen_id_priv->id.device,
-                                    listen_id_priv->id.port_num) == dev_ll) {
+
+       if (listen_id_priv) {
                cma_dev = listen_id_priv->cma_dev;
                port = listen_id_priv->id.port_num;
-               if (rdma_node_get_transport(cma_dev->device->node_type) == RDMA_TRANSPORT_IB &&
-                   rdma_port_get_link_layer(cma_dev->device, port) == IB_LINK_LAYER_ETHERNET)
-                       ret = ib_find_cached_gid(cma_dev->device, &iboe_gid,
-                                                &found_port, NULL);
-               else
-                       ret = ib_find_cached_gid(cma_dev->device, &gid,
-                                                &found_port, NULL);
+               gidp = rdma_protocol_roce(cma_dev->device, port) ?
+                      &iboe_gid : &gid;
 
-               if (!ret && (port  == found_port)) {
-                       id_priv->id.port_num = found_port;
+               ret = cma_validate_port(cma_dev->device, port, gidp,
+                                       dev_addr->dev_type);
+               if (!ret) {
+                       id_priv->id.port_num = port;
                        goto out;
                }
        }
+
        list_for_each_entry(cma_dev, &dev_list, list) {
                for (port = 1; port <= cma_dev->device->phys_port_cnt; ++port) {
                        if (listen_id_priv &&
                            listen_id_priv->cma_dev == cma_dev &&
                            listen_id_priv->id.port_num == port)
                                continue;
-                       if (rdma_port_get_link_layer(cma_dev->device, port) == dev_ll) {
-                               if (rdma_node_get_transport(cma_dev->device->node_type) == RDMA_TRANSPORT_IB &&
-                                   rdma_port_get_link_layer(cma_dev->device, port) == IB_LINK_LAYER_ETHERNET)
-                                       ret = ib_find_cached_gid(cma_dev->device, &iboe_gid, &found_port, NULL);
-                               else
-                                       ret = ib_find_cached_gid(cma_dev->device, &gid, &found_port, NULL);
-
-                               if (!ret && (port == found_port)) {
-                                       id_priv->id.port_num = found_port;
-                                       goto out;
-                               }
+
+                       gidp = rdma_protocol_roce(cma_dev->device, port) ?
+                              &iboe_gid : &gid;
+
+                       ret = cma_validate_port(cma_dev->device, port, gidp,
+                                               dev_addr->dev_type);
+                       if (!ret) {
+                               id_priv->id.port_num = port;
+                               goto out;
                        }
                }
        }
@@ -435,10 +475,10 @@ static int cma_resolve_ib_dev(struct rdma_id_private *id_priv)
        pkey = ntohs(addr->sib_pkey);
 
        list_for_each_entry(cur_dev, &dev_list, list) {
-               if (rdma_node_get_transport(cur_dev->device->node_type) != RDMA_TRANSPORT_IB)
-                       continue;
-
                for (p = 1; p <= cur_dev->device->phys_port_cnt; ++p) {
+                       if (!rdma_cap_af_ib(cur_dev->device, p))
+                               continue;
+
                        if (ib_find_cached_pkey(cur_dev->device, p, pkey, &index))
                                continue;
 
@@ -633,10 +673,9 @@ static int cma_modify_qp_rtr(struct rdma_id_private *id_priv,
        if (ret)
                goto out;
 
-       if (rdma_node_get_transport(id_priv->cma_dev->device->node_type)
-           == RDMA_TRANSPORT_IB &&
-           rdma_port_get_link_layer(id_priv->id.device, id_priv->id.port_num)
-           == IB_LINK_LAYER_ETHERNET) {
+       BUG_ON(id_priv->cma_dev->device != id_priv->id.device);
+
+       if (rdma_protocol_roce(id_priv->id.device, id_priv->id.port_num)) {
                ret = rdma_addr_find_smac_by_sgid(&sgid, qp_attr.smac, NULL);
 
                if (ret)
@@ -700,11 +739,10 @@ static int cma_ib_init_qp_attr(struct rdma_id_private *id_priv,
        int ret;
        u16 pkey;
 
-       if (rdma_port_get_link_layer(id_priv->id.device, id_priv->id.port_num) ==
-           IB_LINK_LAYER_INFINIBAND)
-               pkey = ib_addr_get_pkey(dev_addr);
-       else
+       if (rdma_cap_eth_ah(id_priv->id.device, id_priv->id.port_num))
                pkey = 0xffff;
+       else
+               pkey = ib_addr_get_pkey(dev_addr);
 
        ret = ib_find_cached_pkey(id_priv->id.device, id_priv->id.port_num,
                                  pkey, &qp_attr->pkey_index);
@@ -735,8 +773,7 @@ int rdma_init_qp_attr(struct rdma_cm_id *id, struct ib_qp_attr *qp_attr,
        int ret = 0;
 
        id_priv = container_of(id, struct rdma_id_private, id);
-       switch (rdma_node_get_transport(id_priv->id.device->node_type)) {
-       case RDMA_TRANSPORT_IB:
+       if (rdma_cap_ib_cm(id->device, id->port_num)) {
                if (!id_priv->cm_id.ib || (id_priv->id.qp_type == IB_QPT_UD))
                        ret = cma_ib_init_qp_attr(id_priv, qp_attr, qp_attr_mask);
                else
@@ -745,19 +782,15 @@ int rdma_init_qp_attr(struct rdma_cm_id *id, struct ib_qp_attr *qp_attr,
 
                if (qp_attr->qp_state == IB_QPS_RTR)
                        qp_attr->rq_psn = id_priv->seq_num;
-               break;
-       case RDMA_TRANSPORT_IWARP:
+       } else if (rdma_cap_iw_cm(id->device, id->port_num)) {
                if (!id_priv->cm_id.iw) {
                        qp_attr->qp_access_flags = 0;
                        *qp_attr_mask = IB_QP_STATE | IB_QP_ACCESS_FLAGS;
                } else
                        ret = iw_cm_init_qp_attr(id_priv->cm_id.iw, qp_attr,
                                                 qp_attr_mask);
-               break;
-       default:
+       } else
                ret = -ENOSYS;
-               break;
-       }
 
        return ret;
 }
@@ -945,13 +978,9 @@ static inline int cma_user_data_offset(struct rdma_id_private *id_priv)
 
 static void cma_cancel_route(struct rdma_id_private *id_priv)
 {
-       switch (rdma_port_get_link_layer(id_priv->id.device, id_priv->id.port_num)) {
-       case IB_LINK_LAYER_INFINIBAND:
+       if (rdma_cap_ib_sa(id_priv->id.device, id_priv->id.port_num)) {
                if (id_priv->query)
                        ib_sa_cancel_query(id_priv->query_id, id_priv->query);
-               break;
-       default:
-               break;
        }
 }
 
@@ -1023,17 +1052,12 @@ static void cma_leave_mc_groups(struct rdma_id_private *id_priv)
                mc = container_of(id_priv->mc_list.next,
                                  struct cma_multicast, list);
                list_del(&mc->list);
-               switch (rdma_port_get_link_layer(id_priv->cma_dev->device, id_priv->id.port_num)) {
-               case IB_LINK_LAYER_INFINIBAND:
+               if (rdma_cap_ib_mcast(id_priv->cma_dev->device,
+                                     id_priv->id.port_num)) {
                        ib_sa_free_multicast(mc->multicast.ib);
                        kfree(mc);
-                       break;
-               case IB_LINK_LAYER_ETHERNET:
+               } else
                        kref_put(&mc->mcref, release_mc);
-                       break;
-               default:
-                       break;
-               }
        }
 }
 
@@ -1054,17 +1078,12 @@ void rdma_destroy_id(struct rdma_cm_id *id)
        mutex_unlock(&id_priv->handler_mutex);
 
        if (id_priv->cma_dev) {
-               switch (rdma_node_get_transport(id_priv->id.device->node_type)) {
-               case RDMA_TRANSPORT_IB:
+               if (rdma_cap_ib_cm(id_priv->id.device, 1)) {
                        if (id_priv->cm_id.ib)
                                ib_destroy_cm_id(id_priv->cm_id.ib);
-                       break;
-               case RDMA_TRANSPORT_IWARP:
+               } else if (rdma_cap_iw_cm(id_priv->id.device, 1)) {
                        if (id_priv->cm_id.iw)
                                iw_destroy_cm_id(id_priv->cm_id.iw);
-                       break;
-               default:
-                       break;
                }
                cma_leave_mc_groups(id_priv);
                cma_release_dev(id_priv);
@@ -1610,6 +1629,7 @@ static int cma_iw_listen(struct rdma_id_private *id_priv, int backlog)
        if (IS_ERR(id))
                return PTR_ERR(id);
 
+       id->tos = id_priv->tos;
        id_priv->cm_id.iw = id;
 
        memcpy(&id_priv->cm_id.iw->local_addr, cma_src_addr(id_priv),
@@ -1642,8 +1662,7 @@ static void cma_listen_on_dev(struct rdma_id_private *id_priv,
        struct rdma_cm_id *id;
        int ret;
 
-       if (cma_family(id_priv) == AF_IB &&
-           rdma_node_get_transport(cma_dev->device->node_type) != RDMA_TRANSPORT_IB)
+       if (cma_family(id_priv) == AF_IB && !rdma_cap_ib_cm(cma_dev->device, 1))
                return;
 
        id = rdma_create_id(cma_listen_handler, id_priv, id_priv->id.ps,
@@ -1984,26 +2003,15 @@ int rdma_resolve_route(struct rdma_cm_id *id, int timeout_ms)
                return -EINVAL;
 
        atomic_inc(&id_priv->refcount);
-       switch (rdma_node_get_transport(id->device->node_type)) {
-       case RDMA_TRANSPORT_IB:
-               switch (rdma_port_get_link_layer(id->device, id->port_num)) {
-               case IB_LINK_LAYER_INFINIBAND:
-                       ret = cma_resolve_ib_route(id_priv, timeout_ms);
-                       break;
-               case IB_LINK_LAYER_ETHERNET:
-                       ret = cma_resolve_iboe_route(id_priv);
-                       break;
-               default:
-                       ret = -ENOSYS;
-               }
-               break;
-       case RDMA_TRANSPORT_IWARP:
+       if (rdma_cap_ib_sa(id->device, id->port_num))
+               ret = cma_resolve_ib_route(id_priv, timeout_ms);
+       else if (rdma_protocol_roce(id->device, id->port_num))
+               ret = cma_resolve_iboe_route(id_priv);
+       else if (rdma_protocol_iwarp(id->device, id->port_num))
                ret = cma_resolve_iw_route(id_priv, timeout_ms);
-               break;
-       default:
+       else
                ret = -ENOSYS;
-               break;
-       }
+
        if (ret)
                goto err;
 
@@ -2045,7 +2053,7 @@ static int cma_bind_loopback(struct rdma_id_private *id_priv)
        mutex_lock(&lock);
        list_for_each_entry(cur_dev, &dev_list, list) {
                if (cma_family(id_priv) == AF_IB &&
-                   rdma_node_get_transport(cur_dev->device->node_type) != RDMA_TRANSPORT_IB)
+                   !rdma_cap_ib_cm(cur_dev->device, 1))
                        continue;
 
                if (!cma_dev)
@@ -2077,7 +2085,7 @@ port_found:
                goto out;
 
        id_priv->id.route.addr.dev_addr.dev_type =
-               (rdma_port_get_link_layer(cma_dev->device, p) == IB_LINK_LAYER_INFINIBAND) ?
+               (rdma_protocol_ib(cma_dev->device, p)) ?
                ARPHRD_INFINIBAND : ARPHRD_ETHER;
 
        rdma_addr_set_sgid(&id_priv->id.route.addr.dev_addr, &gid);
@@ -2554,18 +2562,15 @@ int rdma_listen(struct rdma_cm_id *id, int backlog)
 
        id_priv->backlog = backlog;
        if (id->device) {
-               switch (rdma_node_get_transport(id->device->node_type)) {
-               case RDMA_TRANSPORT_IB:
+               if (rdma_cap_ib_cm(id->device, 1)) {
                        ret = cma_ib_listen(id_priv);
                        if (ret)
                                goto err;
-                       break;
-               case RDMA_TRANSPORT_IWARP:
+               } else if (rdma_cap_iw_cm(id->device, 1)) {
                        ret = cma_iw_listen(id_priv, backlog);
                        if (ret)
                                goto err;
-                       break;
-               default:
+               } else {
                        ret = -ENOSYS;
                        goto err;
                }
@@ -2857,6 +2862,7 @@ static int cma_connect_iw(struct rdma_id_private *id_priv,
        if (IS_ERR(cm_id))
                return PTR_ERR(cm_id);
 
+       cm_id->tos = id_priv->tos;
        id_priv->cm_id.iw = cm_id;
 
        memcpy(&cm_id->local_addr, cma_src_addr(id_priv),
@@ -2901,20 +2907,15 @@ int rdma_connect(struct rdma_cm_id *id, struct rdma_conn_param *conn_param)
                id_priv->srq = conn_param->srq;
        }
 
-       switch (rdma_node_get_transport(id->device->node_type)) {
-       case RDMA_TRANSPORT_IB:
+       if (rdma_cap_ib_cm(id->device, id->port_num)) {
                if (id->qp_type == IB_QPT_UD)
                        ret = cma_resolve_ib_udp(id_priv, conn_param);
                else
                        ret = cma_connect_ib(id_priv, conn_param);
-               break;
-       case RDMA_TRANSPORT_IWARP:
+       } else if (rdma_cap_iw_cm(id->device, id->port_num))
                ret = cma_connect_iw(id_priv, conn_param);
-               break;
-       default:
+       else
                ret = -ENOSYS;
-               break;
-       }
        if (ret)
                goto err;
 
@@ -3017,8 +3018,7 @@ int rdma_accept(struct rdma_cm_id *id, struct rdma_conn_param *conn_param)
                id_priv->srq = conn_param->srq;
        }
 
-       switch (rdma_node_get_transport(id->device->node_type)) {
-       case RDMA_TRANSPORT_IB:
+       if (rdma_cap_ib_cm(id->device, id->port_num)) {
                if (id->qp_type == IB_QPT_UD) {
                        if (conn_param)
                                ret = cma_send_sidr_rep(id_priv, IB_SIDR_SUCCESS,
@@ -3034,14 +3034,10 @@ int rdma_accept(struct rdma_cm_id *id, struct rdma_conn_param *conn_param)
                        else
                                ret = cma_rep_recv(id_priv);
                }
-               break;
-       case RDMA_TRANSPORT_IWARP:
+       } else if (rdma_cap_iw_cm(id->device, id->port_num))
                ret = cma_accept_iw(id_priv, conn_param);
-               break;
-       default:
+       else
                ret = -ENOSYS;
-               break;
-       }
 
        if (ret)
                goto reject;
@@ -3085,8 +3081,7 @@ int rdma_reject(struct rdma_cm_id *id, const void *private_data,
        if (!id_priv->cm_id.ib)
                return -EINVAL;
 
-       switch (rdma_node_get_transport(id->device->node_type)) {
-       case RDMA_TRANSPORT_IB:
+       if (rdma_cap_ib_cm(id->device, id->port_num)) {
                if (id->qp_type == IB_QPT_UD)
                        ret = cma_send_sidr_rep(id_priv, IB_SIDR_REJECT, 0,
                                                private_data, private_data_len);
@@ -3094,15 +3089,12 @@ int rdma_reject(struct rdma_cm_id *id, const void *private_data,
                        ret = ib_send_cm_rej(id_priv->cm_id.ib,
                                             IB_CM_REJ_CONSUMER_DEFINED, NULL,
                                             0, private_data, private_data_len);
-               break;
-       case RDMA_TRANSPORT_IWARP:
+       } else if (rdma_cap_iw_cm(id->device, id->port_num)) {
                ret = iw_cm_reject(id_priv->cm_id.iw,
                                   private_data, private_data_len);
-               break;
-       default:
+       } else
                ret = -ENOSYS;
-               break;
-       }
+
        return ret;
 }
 EXPORT_SYMBOL(rdma_reject);
@@ -3116,22 +3108,18 @@ int rdma_disconnect(struct rdma_cm_id *id)
        if (!id_priv->cm_id.ib)
                return -EINVAL;
 
-       switch (rdma_node_get_transport(id->device->node_type)) {
-       case RDMA_TRANSPORT_IB:
+       if (rdma_cap_ib_cm(id->device, id->port_num)) {
                ret = cma_modify_qp_err(id_priv);
                if (ret)
                        goto out;
                /* Initiate or respond to a disconnect. */
                if (ib_send_cm_dreq(id_priv->cm_id.ib, NULL, 0))
                        ib_send_cm_drep(id_priv->cm_id.ib, NULL, 0);
-               break;
-       case RDMA_TRANSPORT_IWARP:
+       } else if (rdma_cap_iw_cm(id->device, id->port_num)) {
                ret = iw_cm_disconnect(id_priv->cm_id.iw, 0);
-               break;
-       default:
+       } else
                ret = -EINVAL;
-               break;
-       }
+
 out:
        return ret;
 }
@@ -3377,24 +3365,13 @@ int rdma_join_multicast(struct rdma_cm_id *id, struct sockaddr *addr,
        list_add(&mc->list, &id_priv->mc_list);
        spin_unlock(&id_priv->lock);
 
-       switch (rdma_node_get_transport(id->device->node_type)) {
-       case RDMA_TRANSPORT_IB:
-               switch (rdma_port_get_link_layer(id->device, id->port_num)) {
-               case IB_LINK_LAYER_INFINIBAND:
-                       ret = cma_join_ib_multicast(id_priv, mc);
-                       break;
-               case IB_LINK_LAYER_ETHERNET:
-                       kref_init(&mc->mcref);
-                       ret = cma_iboe_join_multicast(id_priv, mc);
-                       break;
-               default:
-                       ret = -EINVAL;
-               }
-               break;
-       default:
+       if (rdma_protocol_roce(id->device, id->port_num)) {
+               kref_init(&mc->mcref);
+               ret = cma_iboe_join_multicast(id_priv, mc);
+       } else if (rdma_cap_ib_mcast(id->device, id->port_num))
+               ret = cma_join_ib_multicast(id_priv, mc);
+       else
                ret = -ENOSYS;
-               break;
-       }
 
        if (ret) {
                spin_lock_irq(&id_priv->lock);
@@ -3422,19 +3399,15 @@ void rdma_leave_multicast(struct rdma_cm_id *id, struct sockaddr *addr)
                                ib_detach_mcast(id->qp,
                                                &mc->multicast.ib->rec.mgid,
                                                be16_to_cpu(mc->multicast.ib->rec.mlid));
-                       if (rdma_node_get_transport(id_priv->cma_dev->device->node_type) == RDMA_TRANSPORT_IB) {
-                               switch (rdma_port_get_link_layer(id->device, id->port_num)) {
-                               case IB_LINK_LAYER_INFINIBAND:
-                                       ib_sa_free_multicast(mc->multicast.ib);
-                                       kfree(mc);
-                                       break;
-                               case IB_LINK_LAYER_ETHERNET:
-                                       kref_put(&mc->mcref, release_mc);
-                                       break;
-                               default:
-                                       break;
-                               }
-                       }
+
+                       BUG_ON(id_priv->cma_dev->device != id->device);
+
+                       if (rdma_cap_ib_mcast(id->device, id->port_num)) {
+                               ib_sa_free_multicast(mc->multicast.ib);
+                               kfree(mc);
+                       } else if (rdma_protocol_roce(id->device, id->port_num))
+                               kref_put(&mc->mcref, release_mc);
+
                        return;
                }
        }
index 18c1ece765f2c55b8317fba4daa5716df0ab814f..9567756ca4f9f9024032adcc0211938583e96066 100644 (file)
@@ -92,7 +92,8 @@ static int ib_device_check_mandatory(struct ib_device *device)
                IB_MANDATORY_FUNC(poll_cq),
                IB_MANDATORY_FUNC(req_notify_cq),
                IB_MANDATORY_FUNC(get_dma_mr),
-               IB_MANDATORY_FUNC(dereg_mr)
+               IB_MANDATORY_FUNC(dereg_mr),
+               IB_MANDATORY_FUNC(get_port_immutable)
        };
        int i;
 
@@ -151,18 +152,6 @@ static int alloc_name(char *name)
        return 0;
 }
 
-static int start_port(struct ib_device *device)
-{
-       return (device->node_type == RDMA_NODE_IB_SWITCH) ? 0 : 1;
-}
-
-
-static int end_port(struct ib_device *device)
-{
-       return (device->node_type == RDMA_NODE_IB_SWITCH) ?
-               0 : device->phys_port_cnt;
-}
-
 /**
  * ib_alloc_device - allocate an IB device struct
  * @size:size of structure to allocate
@@ -222,42 +211,49 @@ static int add_client_context(struct ib_device *device, struct ib_client *client
        return 0;
 }
 
-static int read_port_table_lengths(struct ib_device *device)
+static int verify_immutable(const struct ib_device *dev, u8 port)
 {
-       struct ib_port_attr *tprops = NULL;
-       int num_ports, ret = -ENOMEM;
-       u8 port_index;
-
-       tprops = kmalloc(sizeof *tprops, GFP_KERNEL);
-       if (!tprops)
-               goto out;
-
-       num_ports = end_port(device) - start_port(device) + 1;
+       return WARN_ON(!rdma_cap_ib_mad(dev, port) &&
+                           rdma_max_mad_size(dev, port) != 0);
+}
 
-       device->pkey_tbl_len = kmalloc(sizeof *device->pkey_tbl_len * num_ports,
-                                      GFP_KERNEL);
-       device->gid_tbl_len = kmalloc(sizeof *device->gid_tbl_len * num_ports,
-                                     GFP_KERNEL);
-       if (!device->pkey_tbl_len || !device->gid_tbl_len)
+static int read_port_immutable(struct ib_device *device)
+{
+       int ret = -ENOMEM;
+       u8 start_port = rdma_start_port(device);
+       u8 end_port = rdma_end_port(device);
+       u8 port;
+
+       /**
+        * device->port_immutable is indexed directly by the port number to make
+        * access to this data as efficient as possible.
+        *
+        * Therefore port_immutable is declared as a 1 based array with
+        * potential empty slots at the beginning.
+        */
+       device->port_immutable = kzalloc(sizeof(*device->port_immutable)
+                                        * (end_port + 1),
+                                        GFP_KERNEL);
+       if (!device->port_immutable)
                goto err;
 
-       for (port_index = 0; port_index < num_ports; ++port_index) {
-               ret = ib_query_port(device, port_index + start_port(device),
-                                       tprops);
+       for (port = start_port; port <= end_port; ++port) {
+               ret = device->get_port_immutable(device, port,
+                                                &device->port_immutable[port]);
                if (ret)
                        goto err;
-               device->pkey_tbl_len[port_index] = tprops->pkey_tbl_len;
-               device->gid_tbl_len[port_index]  = tprops->gid_tbl_len;
+
+               if (verify_immutable(device, port)) {
+                       ret = -EINVAL;
+                       goto err;
+               }
        }
 
        ret = 0;
        goto out;
-
 err:
-       kfree(device->gid_tbl_len);
-       kfree(device->pkey_tbl_len);
+       kfree(device->port_immutable);
 out:
-       kfree(tprops);
        return ret;
 }
 
@@ -294,9 +290,9 @@ int ib_register_device(struct ib_device *device,
        spin_lock_init(&device->event_handler_lock);
        spin_lock_init(&device->client_data_lock);
 
-       ret = read_port_table_lengths(device);
+       ret = read_port_immutable(device);
        if (ret) {
-               printk(KERN_WARNING "Couldn't create table lengths cache for device %s\n",
+               printk(KERN_WARNING "Couldn't create per port immutable data %s\n",
                       device->name);
                goto out;
        }
@@ -305,8 +301,7 @@ int ib_register_device(struct ib_device *device,
        if (ret) {
                printk(KERN_WARNING "Couldn't register device %s with driver model\n",
                       device->name);
-               kfree(device->gid_tbl_len);
-               kfree(device->pkey_tbl_len);
+               kfree(device->port_immutable);
                goto out;
        }
 
@@ -348,9 +343,6 @@ void ib_unregister_device(struct ib_device *device)
 
        list_del(&device->core_list);
 
-       kfree(device->gid_tbl_len);
-       kfree(device->pkey_tbl_len);
-
        mutex_unlock(&device_mutex);
 
        ib_device_unregister_sysfs(device);
@@ -558,7 +550,11 @@ EXPORT_SYMBOL(ib_dispatch_event);
 int ib_query_device(struct ib_device *device,
                    struct ib_device_attr *device_attr)
 {
-       return device->query_device(device, device_attr);
+       struct ib_udata uhw = {.outlen = 0, .inlen = 0};
+
+       memset(device_attr, 0, sizeof(*device_attr));
+
+       return device->query_device(device, device_attr, &uhw);
 }
 EXPORT_SYMBOL(ib_query_device);
 
@@ -575,7 +571,7 @@ int ib_query_port(struct ib_device *device,
                  u8 port_num,
                  struct ib_port_attr *port_attr)
 {
-       if (port_num < start_port(device) || port_num > end_port(device))
+       if (port_num < rdma_start_port(device) || port_num > rdma_end_port(device))
                return -EINVAL;
 
        return device->query_port(device, port_num, port_attr);
@@ -653,7 +649,7 @@ int ib_modify_port(struct ib_device *device,
        if (!device->modify_port)
                return -ENOSYS;
 
-       if (port_num < start_port(device) || port_num > end_port(device))
+       if (port_num < rdma_start_port(device) || port_num > rdma_end_port(device))
                return -EINVAL;
 
        return device->modify_port(device, port_num, port_modify_mask,
@@ -676,8 +672,8 @@ int ib_find_gid(struct ib_device *device, union ib_gid *gid,
        union ib_gid tmp_gid;
        int ret, port, i;
 
-       for (port = start_port(device); port <= end_port(device); ++port) {
-               for (i = 0; i < device->gid_tbl_len[port - start_port(device)]; ++i) {
+       for (port = rdma_start_port(device); port <= rdma_end_port(device); ++port) {
+               for (i = 0; i < device->port_immutable[port].gid_tbl_len; ++i) {
                        ret = ib_query_gid(device, port, i, &tmp_gid);
                        if (ret)
                                return ret;
@@ -709,7 +705,7 @@ int ib_find_pkey(struct ib_device *device,
        u16 tmp_pkey;
        int partial_ix = -1;
 
-       for (i = 0; i < device->pkey_tbl_len[port_num - start_port(device)]; ++i) {
+       for (i = 0; i < device->port_immutable[port_num].pkey_tbl_len; ++i) {
                ret = ib_query_pkey(device, port_num, i, &tmp_pkey);
                if (ret)
                        return ret;
index 74c30f4c557e015df74ec153417e09d626f8da2e..a4b1466c1bf686431db027309db9722c7b044455 100644 (file)
@@ -3,6 +3,7 @@
  * Copyright (c) 2005 Intel Corporation.  All rights reserved.
  * Copyright (c) 2005 Mellanox Technologies Ltd.  All rights reserved.
  * Copyright (c) 2009 HNR Consulting. All rights reserved.
+ * Copyright (c) 2014 Intel Corporation.  All rights reserved.
  *
  * 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
@@ -44,6 +45,7 @@
 #include "mad_priv.h"
 #include "mad_rmpp.h"
 #include "smi.h"
+#include "opa_smi.h"
 #include "agent.h"
 
 MODULE_LICENSE("Dual BSD/GPL");
@@ -59,8 +61,6 @@ MODULE_PARM_DESC(send_queue_size, "Size of send queue in number of work requests
 module_param_named(recv_queue_size, mad_recvq_size, int, 0444);
 MODULE_PARM_DESC(recv_queue_size, "Size of receive queue in number of work requests");
 
-static struct kmem_cache *ib_mad_cache;
-
 static struct list_head ib_mad_port_list;
 static u32 ib_mad_client_id = 0;
 
@@ -73,7 +73,7 @@ static int method_in_use(struct ib_mad_mgmt_method_table **method,
 static void remove_mad_reg_req(struct ib_mad_agent_private *priv);
 static struct ib_mad_agent_private *find_mad_agent(
                                        struct ib_mad_port_private *port_priv,
-                                       struct ib_mad *mad);
+                                       const struct ib_mad_hdr *mad);
 static int ib_mad_post_receive_mads(struct ib_mad_qp_info *qp_info,
                                    struct ib_mad_private *mad);
 static void cancel_mads(struct ib_mad_agent_private *mad_agent_priv);
@@ -179,12 +179,12 @@ static int is_vendor_method_in_use(
        return 0;
 }
 
-int ib_response_mad(struct ib_mad *mad)
+int ib_response_mad(const struct ib_mad_hdr *hdr)
 {
-       return ((mad->mad_hdr.method & IB_MGMT_METHOD_RESP) ||
-               (mad->mad_hdr.method == IB_MGMT_METHOD_TRAP_REPRESS) ||
-               ((mad->mad_hdr.mgmt_class == IB_MGMT_CLASS_BM) &&
-                (mad->mad_hdr.attr_mod & IB_BM_ATTR_MOD_RESP)));
+       return ((hdr->method & IB_MGMT_METHOD_RESP) ||
+               (hdr->method == IB_MGMT_METHOD_TRAP_REPRESS) ||
+               ((hdr->mgmt_class == IB_MGMT_CLASS_BM) &&
+                (hdr->attr_mod & IB_BM_ATTR_MOD_RESP)));
 }
 EXPORT_SYMBOL(ib_response_mad);
 
@@ -717,6 +717,32 @@ static void build_smp_wc(struct ib_qp *qp,
        wc->port_num = port_num;
 }
 
+static size_t mad_priv_size(const struct ib_mad_private *mp)
+{
+       return sizeof(struct ib_mad_private) + mp->mad_size;
+}
+
+static struct ib_mad_private *alloc_mad_private(size_t mad_size, gfp_t flags)
+{
+       size_t size = sizeof(struct ib_mad_private) + mad_size;
+       struct ib_mad_private *ret = kzalloc(size, flags);
+
+       if (ret)
+               ret->mad_size = mad_size;
+
+       return ret;
+}
+
+static size_t port_mad_size(const struct ib_mad_port_private *port_priv)
+{
+       return rdma_max_mad_size(port_priv->device, port_priv->port_num);
+}
+
+static size_t mad_priv_dma_size(const struct ib_mad_private *mp)
+{
+       return sizeof(struct ib_grh) + mp->mad_size;
+}
+
 /*
  * Return 0 if SMP is to be sent
  * Return 1 if SMP was consumed locally (whether or not solicited)
@@ -727,6 +753,7 @@ static int handle_outgoing_dr_smp(struct ib_mad_agent_private *mad_agent_priv,
 {
        int ret = 0;
        struct ib_smp *smp = mad_send_wr->send_buf.mad;
+       struct opa_smp *opa_smp = (struct opa_smp *)smp;
        unsigned long flags;
        struct ib_mad_local_private *local;
        struct ib_mad_private *mad_priv;
@@ -736,6 +763,11 @@ static int handle_outgoing_dr_smp(struct ib_mad_agent_private *mad_agent_priv,
        u8 port_num;
        struct ib_wc mad_wc;
        struct ib_send_wr *send_wr = &mad_send_wr->send_wr;
+       size_t mad_size = port_mad_size(mad_agent_priv->qp_info->port_priv);
+       u16 out_mad_pkey_index = 0;
+       u16 drslid;
+       bool opa = rdma_cap_opa_mad(mad_agent_priv->qp_info->port_priv->device,
+                                   mad_agent_priv->qp_info->port_priv->port_num);
 
        if (device->node_type == RDMA_NODE_IB_SWITCH &&
            smp->mgmt_class == IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE)
@@ -749,19 +781,48 @@ static int handle_outgoing_dr_smp(struct ib_mad_agent_private *mad_agent_priv,
         * If we are at the start of the LID routed part, don't update the
         * hop_ptr or hop_cnt.  See section 14.2.2, Vol 1 IB spec.
         */
-       if ((ib_get_smp_direction(smp) ? smp->dr_dlid : smp->dr_slid) ==
-            IB_LID_PERMISSIVE &&
-            smi_handle_dr_smp_send(smp, device->node_type, port_num) ==
-            IB_SMI_DISCARD) {
-               ret = -EINVAL;
-               dev_err(&device->dev, "Invalid directed route\n");
-               goto out;
-       }
+       if (opa && smp->class_version == OPA_SMP_CLASS_VERSION) {
+               u32 opa_drslid;
+
+               if ((opa_get_smp_direction(opa_smp)
+                    ? opa_smp->route.dr.dr_dlid : opa_smp->route.dr.dr_slid) ==
+                    OPA_LID_PERMISSIVE &&
+                    opa_smi_handle_dr_smp_send(opa_smp, device->node_type,
+                                               port_num) == IB_SMI_DISCARD) {
+                       ret = -EINVAL;
+                       dev_err(&device->dev, "OPA Invalid directed route\n");
+                       goto out;
+               }
+               opa_drslid = be32_to_cpu(opa_smp->route.dr.dr_slid);
+               if (opa_drslid != OPA_LID_PERMISSIVE &&
+                   opa_drslid & 0xffff0000) {
+                       ret = -EINVAL;
+                       dev_err(&device->dev, "OPA Invalid dr_slid 0x%x\n",
+                              opa_drslid);
+                       goto out;
+               }
+               drslid = (u16)(opa_drslid & 0x0000ffff);
 
-       /* Check to post send on QP or process locally */
-       if (smi_check_local_smp(smp, device) == IB_SMI_DISCARD &&
-           smi_check_local_returning_smp(smp, device) == IB_SMI_DISCARD)
-               goto out;
+               /* Check to post send on QP or process locally */
+               if (opa_smi_check_local_smp(opa_smp, device) == IB_SMI_DISCARD &&
+                   opa_smi_check_local_returning_smp(opa_smp, device) == IB_SMI_DISCARD)
+                       goto out;
+       } else {
+               if ((ib_get_smp_direction(smp) ? smp->dr_dlid : smp->dr_slid) ==
+                    IB_LID_PERMISSIVE &&
+                    smi_handle_dr_smp_send(smp, device->node_type, port_num) ==
+                    IB_SMI_DISCARD) {
+                       ret = -EINVAL;
+                       dev_err(&device->dev, "Invalid directed route\n");
+                       goto out;
+               }
+               drslid = be16_to_cpu(smp->dr_slid);
+
+               /* Check to post send on QP or process locally */
+               if (smi_check_local_smp(smp, device) == IB_SMI_DISCARD &&
+                   smi_check_local_returning_smp(smp, device) == IB_SMI_DISCARD)
+                       goto out;
+       }
 
        local = kmalloc(sizeof *local, GFP_ATOMIC);
        if (!local) {
@@ -771,7 +832,7 @@ static int handle_outgoing_dr_smp(struct ib_mad_agent_private *mad_agent_priv,
        }
        local->mad_priv = NULL;
        local->recv_mad_agent = NULL;
-       mad_priv = kmem_cache_alloc(ib_mad_cache, GFP_ATOMIC);
+       mad_priv = alloc_mad_private(mad_size, GFP_ATOMIC);
        if (!mad_priv) {
                ret = -ENOMEM;
                dev_err(&device->dev, "No memory for local response MAD\n");
@@ -780,18 +841,25 @@ static int handle_outgoing_dr_smp(struct ib_mad_agent_private *mad_agent_priv,
        }
 
        build_smp_wc(mad_agent_priv->agent.qp,
-                    send_wr->wr_id, be16_to_cpu(smp->dr_slid),
+                    send_wr->wr_id, drslid,
                     send_wr->wr.ud.pkey_index,
                     send_wr->wr.ud.port_num, &mad_wc);
 
+       if (opa && smp->base_version == OPA_MGMT_BASE_VERSION) {
+               mad_wc.byte_len = mad_send_wr->send_buf.hdr_len
+                                       + mad_send_wr->send_buf.data_len
+                                       + sizeof(struct ib_grh);
+       }
+
        /* No GRH for DR SMP */
        ret = device->process_mad(device, 0, port_num, &mad_wc, NULL,
-                                 (struct ib_mad *)smp,
-                                 (struct ib_mad *)&mad_priv->mad);
+                                 (const struct ib_mad_hdr *)smp, mad_size,
+                                 (struct ib_mad_hdr *)mad_priv->mad,
+                                 &mad_size, &out_mad_pkey_index);
        switch (ret)
        {
        case IB_MAD_RESULT_SUCCESS | IB_MAD_RESULT_REPLY:
-               if (ib_response_mad(&mad_priv->mad.mad) &&
+               if (ib_response_mad((const struct ib_mad_hdr *)mad_priv->mad) &&
                    mad_agent_priv->agent.recv_handler) {
                        local->mad_priv = mad_priv;
                        local->recv_mad_agent = mad_agent_priv;
@@ -801,39 +869,43 @@ static int handle_outgoing_dr_smp(struct ib_mad_agent_private *mad_agent_priv,
                         */
                        atomic_inc(&mad_agent_priv->refcount);
                } else
-                       kmem_cache_free(ib_mad_cache, mad_priv);
+                       kfree(mad_priv);
                break;
        case IB_MAD_RESULT_SUCCESS | IB_MAD_RESULT_CONSUMED:
-               kmem_cache_free(ib_mad_cache, mad_priv);
+               kfree(mad_priv);
                break;
        case IB_MAD_RESULT_SUCCESS:
                /* Treat like an incoming receive MAD */
                port_priv = ib_get_mad_port(mad_agent_priv->agent.device,
                                            mad_agent_priv->agent.port_num);
                if (port_priv) {
-                       memcpy(&mad_priv->mad.mad, smp, sizeof(struct ib_mad));
+                       memcpy(mad_priv->mad, smp, mad_priv->mad_size);
                        recv_mad_agent = find_mad_agent(port_priv,
-                                                       &mad_priv->mad.mad);
+                                                       (const struct ib_mad_hdr *)mad_priv->mad);
                }
                if (!port_priv || !recv_mad_agent) {
                        /*
                         * No receiving agent so drop packet and
                         * generate send completion.
                         */
-                       kmem_cache_free(ib_mad_cache, mad_priv);
+                       kfree(mad_priv);
                        break;
                }
                local->mad_priv = mad_priv;
                local->recv_mad_agent = recv_mad_agent;
                break;
        default:
-               kmem_cache_free(ib_mad_cache, mad_priv);
+               kfree(mad_priv);
                kfree(local);
                ret = -EINVAL;
                goto out;
        }
 
        local->mad_send_wr = mad_send_wr;
+       if (opa) {
+               local->mad_send_wr->send_wr.wr.ud.pkey_index = out_mad_pkey_index;
+               local->return_wc_byte_len = mad_size;
+       }
        /* Reference MAD agent until send side of local completion handled */
        atomic_inc(&mad_agent_priv->refcount);
        /* Queue local completion to local list */
@@ -847,11 +919,11 @@ out:
        return ret;
 }
 
-static int get_pad_size(int hdr_len, int data_len)
+static int get_pad_size(int hdr_len, int data_len, size_t mad_size)
 {
        int seg_size, pad;
 
-       seg_size = sizeof(struct ib_mad) - hdr_len;
+       seg_size = mad_size - hdr_len;
        if (data_len && seg_size) {
                pad = seg_size - data_len % seg_size;
                return pad == seg_size ? 0 : pad;
@@ -870,14 +942,15 @@ static void free_send_rmpp_list(struct ib_mad_send_wr_private *mad_send_wr)
 }
 
 static int alloc_send_rmpp_list(struct ib_mad_send_wr_private *send_wr,
-                               gfp_t gfp_mask)
+                               size_t mad_size, gfp_t gfp_mask)
 {
        struct ib_mad_send_buf *send_buf = &send_wr->send_buf;
        struct ib_rmpp_mad *rmpp_mad = send_buf->mad;
        struct ib_rmpp_segment *seg = NULL;
        int left, seg_size, pad;
 
-       send_buf->seg_size = sizeof (struct ib_mad) - send_buf->hdr_len;
+       send_buf->seg_size = mad_size - send_buf->hdr_len;
+       send_buf->seg_rmpp_size = mad_size - IB_MGMT_RMPP_HDR;
        seg_size = send_buf->seg_size;
        pad = send_wr->pad;
 
@@ -910,7 +983,7 @@ static int alloc_send_rmpp_list(struct ib_mad_send_wr_private *send_wr,
        return 0;
 }
 
-int ib_mad_kernel_rmpp_agent(struct ib_mad_agent *agent)
+int ib_mad_kernel_rmpp_agent(const struct ib_mad_agent *agent)
 {
        return agent->rmpp_version && !(agent->flags & IB_MAD_USER_RMPP);
 }
@@ -920,26 +993,37 @@ struct ib_mad_send_buf * ib_create_send_mad(struct ib_mad_agent *mad_agent,
                                            u32 remote_qpn, u16 pkey_index,
                                            int rmpp_active,
                                            int hdr_len, int data_len,
-                                           gfp_t gfp_mask)
+                                           gfp_t gfp_mask,
+                                           u8 base_version)
 {
        struct ib_mad_agent_private *mad_agent_priv;
        struct ib_mad_send_wr_private *mad_send_wr;
        int pad, message_size, ret, size;
        void *buf;
+       size_t mad_size;
+       bool opa;
 
        mad_agent_priv = container_of(mad_agent, struct ib_mad_agent_private,
                                      agent);
-       pad = get_pad_size(hdr_len, data_len);
+
+       opa = rdma_cap_opa_mad(mad_agent->device, mad_agent->port_num);
+
+       if (opa && base_version == OPA_MGMT_BASE_VERSION)
+               mad_size = sizeof(struct opa_mad);
+       else
+               mad_size = sizeof(struct ib_mad);
+
+       pad = get_pad_size(hdr_len, data_len, mad_size);
        message_size = hdr_len + data_len + pad;
 
        if (ib_mad_kernel_rmpp_agent(mad_agent)) {
-               if (!rmpp_active && message_size > sizeof(struct ib_mad))
+               if (!rmpp_active && message_size > mad_size)
                        return ERR_PTR(-EINVAL);
        } else
-               if (rmpp_active || message_size > sizeof(struct ib_mad))
+               if (rmpp_active || message_size > mad_size)
                        return ERR_PTR(-EINVAL);
 
-       size = rmpp_active ? hdr_len : sizeof(struct ib_mad);
+       size = rmpp_active ? hdr_len : mad_size;
        buf = kzalloc(sizeof *mad_send_wr + size, gfp_mask);
        if (!buf)
                return ERR_PTR(-ENOMEM);
@@ -954,7 +1038,14 @@ struct ib_mad_send_buf * ib_create_send_mad(struct ib_mad_agent *mad_agent,
        mad_send_wr->mad_agent_priv = mad_agent_priv;
        mad_send_wr->sg_list[0].length = hdr_len;
        mad_send_wr->sg_list[0].lkey = mad_agent->mr->lkey;
-       mad_send_wr->sg_list[1].length = sizeof(struct ib_mad) - hdr_len;
+
+       /* OPA MADs don't have to be the full 2048 bytes */
+       if (opa && base_version == OPA_MGMT_BASE_VERSION &&
+           data_len < mad_size - hdr_len)
+               mad_send_wr->sg_list[1].length = data_len;
+       else
+               mad_send_wr->sg_list[1].length = mad_size - hdr_len;
+
        mad_send_wr->sg_list[1].lkey = mad_agent->mr->lkey;
 
        mad_send_wr->send_wr.wr_id = (unsigned long) mad_send_wr;
@@ -967,7 +1058,7 @@ struct ib_mad_send_buf * ib_create_send_mad(struct ib_mad_agent *mad_agent,
        mad_send_wr->send_wr.wr.ud.pkey_index = pkey_index;
 
        if (rmpp_active) {
-               ret = alloc_send_rmpp_list(mad_send_wr, gfp_mask);
+               ret = alloc_send_rmpp_list(mad_send_wr, mad_size, gfp_mask);
                if (ret) {
                        kfree(buf);
                        return ERR_PTR(ret);
@@ -1237,7 +1328,7 @@ void ib_free_recv_mad(struct ib_mad_recv_wc *mad_recv_wc)
                                            recv_wc);
                priv = container_of(mad_priv_hdr, struct ib_mad_private,
                                    header);
-               kmem_cache_free(ib_mad_cache, priv);
+               kfree(priv);
        }
 }
 EXPORT_SYMBOL(ib_free_recv_mad);
@@ -1324,7 +1415,7 @@ static int check_vendor_class(struct ib_mad_mgmt_vendor_class *vendor_class)
 }
 
 static int find_vendor_oui(struct ib_mad_mgmt_vendor_class *vendor_class,
-                          char *oui)
+                          const char *oui)
 {
        int i;
 
@@ -1622,13 +1713,13 @@ out:
 
 static struct ib_mad_agent_private *
 find_mad_agent(struct ib_mad_port_private *port_priv,
-              struct ib_mad *mad)
+              const struct ib_mad_hdr *mad_hdr)
 {
        struct ib_mad_agent_private *mad_agent = NULL;
        unsigned long flags;
 
        spin_lock_irqsave(&port_priv->reg_lock, flags);
-       if (ib_response_mad(mad)) {
+       if (ib_response_mad(mad_hdr)) {
                u32 hi_tid;
                struct ib_mad_agent_private *entry;
 
@@ -1636,7 +1727,7 @@ find_mad_agent(struct ib_mad_port_private *port_priv,
                 * Routing is based on high 32 bits of transaction ID
                 * of MAD.
                 */
-               hi_tid = be64_to_cpu(mad->mad_hdr.tid) >> 32;
+               hi_tid = be64_to_cpu(mad_hdr->tid) >> 32;
                list_for_each_entry(entry, &port_priv->agent_list, agent_list) {
                        if (entry->agent.hi_tid == hi_tid) {
                                mad_agent = entry;
@@ -1648,45 +1739,45 @@ find_mad_agent(struct ib_mad_port_private *port_priv,
                struct ib_mad_mgmt_method_table *method;
                struct ib_mad_mgmt_vendor_class_table *vendor;
                struct ib_mad_mgmt_vendor_class *vendor_class;
-               struct ib_vendor_mad *vendor_mad;
+               const struct ib_vendor_mad *vendor_mad;
                int index;
 
                /*
                 * Routing is based on version, class, and method
                 * For "newer" vendor MADs, also based on OUI
                 */
-               if (mad->mad_hdr.class_version >= MAX_MGMT_VERSION)
+               if (mad_hdr->class_version >= MAX_MGMT_VERSION)
                        goto out;
-               if (!is_vendor_class(mad->mad_hdr.mgmt_class)) {
+               if (!is_vendor_class(mad_hdr->mgmt_class)) {
                        class = port_priv->version[
-                                       mad->mad_hdr.class_version].class;
+                                       mad_hdr->class_version].class;
                        if (!class)
                                goto out;
-                       if (convert_mgmt_class(mad->mad_hdr.mgmt_class) >=
+                       if (convert_mgmt_class(mad_hdr->mgmt_class) >=
                            IB_MGMT_MAX_METHODS)
                                goto out;
                        method = class->method_table[convert_mgmt_class(
-                                                       mad->mad_hdr.mgmt_class)];
+                                                       mad_hdr->mgmt_class)];
                        if (method)
-                               mad_agent = method->agent[mad->mad_hdr.method &
+                               mad_agent = method->agent[mad_hdr->method &
                                                          ~IB_MGMT_METHOD_RESP];
                } else {
                        vendor = port_priv->version[
-                                       mad->mad_hdr.class_version].vendor;
+                                       mad_hdr->class_version].vendor;
                        if (!vendor)
                                goto out;
                        vendor_class = vendor->vendor_class[vendor_class_index(
-                                               mad->mad_hdr.mgmt_class)];
+                                               mad_hdr->mgmt_class)];
                        if (!vendor_class)
                                goto out;
                        /* Find matching OUI */
-                       vendor_mad = (struct ib_vendor_mad *)mad;
+                       vendor_mad = (const struct ib_vendor_mad *)mad_hdr;
                        index = find_vendor_oui(vendor_class, vendor_mad->oui);
                        if (index == -1)
                                goto out;
                        method = vendor_class->method_table[index];
                        if (method) {
-                               mad_agent = method->agent[mad->mad_hdr.method &
+                               mad_agent = method->agent[mad_hdr->method &
                                                          ~IB_MGMT_METHOD_RESP];
                        }
                }
@@ -1708,20 +1799,24 @@ out:
        return mad_agent;
 }
 
-static int validate_mad(struct ib_mad *mad, u32 qp_num)
+static int validate_mad(const struct ib_mad_hdr *mad_hdr,
+                       const struct ib_mad_qp_info *qp_info,
+                       bool opa)
 {
        int valid = 0;
+       u32 qp_num = qp_info->qp->qp_num;
 
        /* Make sure MAD base version is understood */
-       if (mad->mad_hdr.base_version != IB_MGMT_BASE_VERSION) {
-               pr_err("MAD received with unsupported base version %d\n",
-                       mad->mad_hdr.base_version);
+       if (mad_hdr->base_version != IB_MGMT_BASE_VERSION &&
+           (!opa || mad_hdr->base_version != OPA_MGMT_BASE_VERSION)) {
+               pr_err("MAD received with unsupported base version %d %s\n",
+                      mad_hdr->base_version, opa ? "(opa)" : "");
                goto out;
        }
 
        /* Filter SMI packets sent to other than QP0 */
-       if ((mad->mad_hdr.mgmt_class == IB_MGMT_CLASS_SUBN_LID_ROUTED) ||
-           (mad->mad_hdr.mgmt_class == IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE)) {
+       if ((mad_hdr->mgmt_class == IB_MGMT_CLASS_SUBN_LID_ROUTED) ||
+           (mad_hdr->mgmt_class == IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE)) {
                if (qp_num == 0)
                        valid = 1;
        } else {
@@ -1734,8 +1829,8 @@ out:
        return valid;
 }
 
-static int is_data_mad(struct ib_mad_agent_private *mad_agent_priv,
-                      struct ib_mad_hdr *mad_hdr)
+static int is_rmpp_data_mad(const struct ib_mad_agent_private *mad_agent_priv,
+                           const struct ib_mad_hdr *mad_hdr)
 {
        struct ib_rmpp_mad *rmpp_mad;
 
@@ -1747,16 +1842,16 @@ static int is_data_mad(struct ib_mad_agent_private *mad_agent_priv,
                (rmpp_mad->rmpp_hdr.rmpp_type == IB_MGMT_RMPP_TYPE_DATA);
 }
 
-static inline int rcv_has_same_class(struct ib_mad_send_wr_private *wr,
-                                    struct ib_mad_recv_wc *rwc)
+static inline int rcv_has_same_class(const struct ib_mad_send_wr_private *wr,
+                                    const struct ib_mad_recv_wc *rwc)
 {
-       return ((struct ib_mad *)(wr->send_buf.mad))->mad_hdr.mgmt_class ==
+       return ((struct ib_mad_hdr *)(wr->send_buf.mad))->mgmt_class ==
                rwc->recv_buf.mad->mad_hdr.mgmt_class;
 }
 
-static inline int rcv_has_same_gid(struct ib_mad_agent_private *mad_agent_priv,
-                                  struct ib_mad_send_wr_private *wr,
-                                  struct ib_mad_recv_wc *rwc )
+static inline int rcv_has_same_gid(const struct ib_mad_agent_private *mad_agent_priv,
+                                  const struct ib_mad_send_wr_private *wr,
+                                  const struct ib_mad_recv_wc *rwc )
 {
        struct ib_ah_attr attr;
        u8 send_resp, rcv_resp;
@@ -1765,8 +1860,8 @@ static inline int rcv_has_same_gid(struct ib_mad_agent_private *mad_agent_priv,
        u8 port_num = mad_agent_priv->agent.port_num;
        u8 lmc;
 
-       send_resp = ib_response_mad((struct ib_mad *)wr->send_buf.mad);
-       rcv_resp = ib_response_mad(rwc->recv_buf.mad);
+       send_resp = ib_response_mad((struct ib_mad_hdr *)wr->send_buf.mad);
+       rcv_resp = ib_response_mad(&rwc->recv_buf.mad->mad_hdr);
 
        if (send_resp == rcv_resp)
                /* both requests, or both responses. GIDs different */
@@ -1811,22 +1906,22 @@ static inline int is_direct(u8 class)
 }
 
 struct ib_mad_send_wr_private*
-ib_find_send_mad(struct ib_mad_agent_private *mad_agent_priv,
-                struct ib_mad_recv_wc *wc)
+ib_find_send_mad(const struct ib_mad_agent_private *mad_agent_priv,
+                const struct ib_mad_recv_wc *wc)
 {
        struct ib_mad_send_wr_private *wr;
-       struct ib_mad *mad;
+       const struct ib_mad_hdr *mad_hdr;
 
-       mad = (struct ib_mad *)wc->recv_buf.mad;
+       mad_hdr = &wc->recv_buf.mad->mad_hdr;
 
        list_for_each_entry(wr, &mad_agent_priv->wait_list, agent_list) {
-               if ((wr->tid == mad->mad_hdr.tid) &&
+               if ((wr->tid == mad_hdr->tid) &&
                    rcv_has_same_class(wr, wc) &&
                    /*
                     * Don't check GID for direct routed MADs.
                     * These might have permissive LIDs.
                     */
-                   (is_direct(wc->recv_buf.mad->mad_hdr.mgmt_class) ||
+                   (is_direct(mad_hdr->mgmt_class) ||
                     rcv_has_same_gid(mad_agent_priv, wr, wc)))
                        return (wr->status == IB_WC_SUCCESS) ? wr : NULL;
        }
@@ -1836,15 +1931,15 @@ ib_find_send_mad(struct ib_mad_agent_private *mad_agent_priv,
         * been notified that the send has completed
         */
        list_for_each_entry(wr, &mad_agent_priv->send_list, agent_list) {
-               if (is_data_mad(mad_agent_priv, wr->send_buf.mad) &&
-                   wr->tid == mad->mad_hdr.tid &&
+               if (is_rmpp_data_mad(mad_agent_priv, wr->send_buf.mad) &&
+                   wr->tid == mad_hdr->tid &&
                    wr->timeout &&
                    rcv_has_same_class(wr, wc) &&
                    /*
                     * Don't check GID for direct routed MADs.
                     * These might have permissive LIDs.
                     */
-                   (is_direct(wc->recv_buf.mad->mad_hdr.mgmt_class) ||
+                   (is_direct(mad_hdr->mgmt_class) ||
                     rcv_has_same_gid(mad_agent_priv, wr, wc)))
                        /* Verify request has not been canceled */
                        return (wr->status == IB_WC_SUCCESS) ? wr : NULL;
@@ -1879,7 +1974,7 @@ static void ib_mad_complete_recv(struct ib_mad_agent_private *mad_agent_priv,
        }
 
        /* Complete corresponding request */
-       if (ib_response_mad(mad_recv_wc->recv_buf.mad)) {
+       if (ib_response_mad(&mad_recv_wc->recv_buf.mad->mad_hdr)) {
                spin_lock_irqsave(&mad_agent_priv->lock, flags);
                mad_send_wr = ib_find_send_mad(mad_agent_priv, mad_recv_wc);
                if (!mad_send_wr) {
@@ -1924,26 +2019,163 @@ static void ib_mad_complete_recv(struct ib_mad_agent_private *mad_agent_priv,
        }
 }
 
-static bool generate_unmatched_resp(struct ib_mad_private *recv,
-                                   struct ib_mad_private *response)
+static enum smi_action handle_ib_smi(const struct ib_mad_port_private *port_priv,
+                                    const struct ib_mad_qp_info *qp_info,
+                                    const struct ib_wc *wc,
+                                    int port_num,
+                                    struct ib_mad_private *recv,
+                                    struct ib_mad_private *response)
+{
+       enum smi_forward_action retsmi;
+       struct ib_smp *smp = (struct ib_smp *)recv->mad;
+
+       if (smi_handle_dr_smp_recv(smp,
+                                  port_priv->device->node_type,
+                                  port_num,
+                                  port_priv->device->phys_port_cnt) ==
+                                  IB_SMI_DISCARD)
+               return IB_SMI_DISCARD;
+
+       retsmi = smi_check_forward_dr_smp(smp);
+       if (retsmi == IB_SMI_LOCAL)
+               return IB_SMI_HANDLE;
+
+       if (retsmi == IB_SMI_SEND) { /* don't forward */
+               if (smi_handle_dr_smp_send(smp,
+                                          port_priv->device->node_type,
+                                          port_num) == IB_SMI_DISCARD)
+                       return IB_SMI_DISCARD;
+
+               if (smi_check_local_smp(smp, port_priv->device) == IB_SMI_DISCARD)
+                       return IB_SMI_DISCARD;
+       } else if (port_priv->device->node_type == RDMA_NODE_IB_SWITCH) {
+               /* forward case for switches */
+               memcpy(response, recv, mad_priv_size(response));
+               response->header.recv_wc.wc = &response->header.wc;
+               response->header.recv_wc.recv_buf.mad = (struct ib_mad *)response->mad;
+               response->header.recv_wc.recv_buf.grh = &response->grh;
+
+               agent_send_response((const struct ib_mad_hdr *)response->mad,
+                                   &response->grh, wc,
+                                   port_priv->device,
+                                   smi_get_fwd_port(smp),
+                                   qp_info->qp->qp_num,
+                                   response->mad_size,
+                                   false);
+
+               return IB_SMI_DISCARD;
+       }
+       return IB_SMI_HANDLE;
+}
+
+static bool generate_unmatched_resp(const struct ib_mad_private *recv,
+                                   struct ib_mad_private *response,
+                                   size_t *resp_len, bool opa)
 {
-       if (recv->mad.mad.mad_hdr.method == IB_MGMT_METHOD_GET ||
-           recv->mad.mad.mad_hdr.method == IB_MGMT_METHOD_SET) {
-               memcpy(response, recv, sizeof *response);
+       const struct ib_mad_hdr *recv_hdr = (const struct ib_mad_hdr *)recv->mad;
+       struct ib_mad_hdr *resp_hdr = (struct ib_mad_hdr *)response->mad;
+
+       if (recv_hdr->method == IB_MGMT_METHOD_GET ||
+           recv_hdr->method == IB_MGMT_METHOD_SET) {
+               memcpy(response, recv, mad_priv_size(response));
                response->header.recv_wc.wc = &response->header.wc;
-               response->header.recv_wc.recv_buf.mad = &response->mad.mad;
+               response->header.recv_wc.recv_buf.mad = (struct ib_mad *)response->mad;
                response->header.recv_wc.recv_buf.grh = &response->grh;
-               response->mad.mad.mad_hdr.method = IB_MGMT_METHOD_GET_RESP;
-               response->mad.mad.mad_hdr.status =
-                       cpu_to_be16(IB_MGMT_MAD_STATUS_UNSUPPORTED_METHOD_ATTRIB);
-               if (recv->mad.mad.mad_hdr.mgmt_class == IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE)
-                       response->mad.mad.mad_hdr.status |= IB_SMP_DIRECTION;
+               resp_hdr->method = IB_MGMT_METHOD_GET_RESP;
+               resp_hdr->status = cpu_to_be16(IB_MGMT_MAD_STATUS_UNSUPPORTED_METHOD_ATTRIB);
+               if (recv_hdr->mgmt_class == IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE)
+                       resp_hdr->status |= IB_SMP_DIRECTION;
+
+               if (opa && recv_hdr->base_version == OPA_MGMT_BASE_VERSION) {
+                       if (recv_hdr->mgmt_class ==
+                           IB_MGMT_CLASS_SUBN_LID_ROUTED ||
+                           recv_hdr->mgmt_class ==
+                           IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE)
+                               *resp_len = opa_get_smp_header_size(
+                                                       (struct opa_smp *)recv->mad);
+                       else
+                               *resp_len = sizeof(struct ib_mad_hdr);
+               }
 
                return true;
        } else {
                return false;
        }
 }
+
+static enum smi_action
+handle_opa_smi(struct ib_mad_port_private *port_priv,
+              struct ib_mad_qp_info *qp_info,
+              struct ib_wc *wc,
+              int port_num,
+              struct ib_mad_private *recv,
+              struct ib_mad_private *response)
+{
+       enum smi_forward_action retsmi;
+       struct opa_smp *smp = (struct opa_smp *)recv->mad;
+
+       if (opa_smi_handle_dr_smp_recv(smp,
+                                  port_priv->device->node_type,
+                                  port_num,
+                                  port_priv->device->phys_port_cnt) ==
+                                  IB_SMI_DISCARD)
+               return IB_SMI_DISCARD;
+
+       retsmi = opa_smi_check_forward_dr_smp(smp);
+       if (retsmi == IB_SMI_LOCAL)
+               return IB_SMI_HANDLE;
+
+       if (retsmi == IB_SMI_SEND) { /* don't forward */
+               if (opa_smi_handle_dr_smp_send(smp,
+                                          port_priv->device->node_type,
+                                          port_num) == IB_SMI_DISCARD)
+                       return IB_SMI_DISCARD;
+
+               if (opa_smi_check_local_smp(smp, port_priv->device) ==
+                   IB_SMI_DISCARD)
+                       return IB_SMI_DISCARD;
+
+       } else if (port_priv->device->node_type == RDMA_NODE_IB_SWITCH) {
+               /* forward case for switches */
+               memcpy(response, recv, mad_priv_size(response));
+               response->header.recv_wc.wc = &response->header.wc;
+               response->header.recv_wc.recv_buf.opa_mad =
+                               (struct opa_mad *)response->mad;
+               response->header.recv_wc.recv_buf.grh = &response->grh;
+
+               agent_send_response((const struct ib_mad_hdr *)response->mad,
+                                   &response->grh, wc,
+                                   port_priv->device,
+                                   opa_smi_get_fwd_port(smp),
+                                   qp_info->qp->qp_num,
+                                   recv->header.wc.byte_len,
+                                   true);
+
+               return IB_SMI_DISCARD;
+       }
+
+       return IB_SMI_HANDLE;
+}
+
+static enum smi_action
+handle_smi(struct ib_mad_port_private *port_priv,
+          struct ib_mad_qp_info *qp_info,
+          struct ib_wc *wc,
+          int port_num,
+          struct ib_mad_private *recv,
+          struct ib_mad_private *response,
+          bool opa)
+{
+       struct ib_mad_hdr *mad_hdr = (struct ib_mad_hdr *)recv->mad;
+
+       if (opa && mad_hdr->base_version == OPA_MGMT_BASE_VERSION &&
+           mad_hdr->class_version == OPA_SMI_CLASS_VERSION)
+               return handle_opa_smi(port_priv, qp_info, wc, port_num, recv,
+                                     response);
+
+       return handle_ib_smi(port_priv, qp_info, wc, port_num, recv, response);
+}
+
 static void ib_mad_recv_done_handler(struct ib_mad_port_private *port_priv,
                                     struct ib_wc *wc)
 {
@@ -1954,35 +2186,49 @@ static void ib_mad_recv_done_handler(struct ib_mad_port_private *port_priv,
        struct ib_mad_agent_private *mad_agent;
        int port_num;
        int ret = IB_MAD_RESULT_SUCCESS;
+       size_t mad_size;
+       u16 resp_mad_pkey_index = 0;
+       bool opa;
 
        mad_list = (struct ib_mad_list_head *)(unsigned long)wc->wr_id;
        qp_info = mad_list->mad_queue->qp_info;
        dequeue_mad(mad_list);
 
+       opa = rdma_cap_opa_mad(qp_info->port_priv->device,
+                              qp_info->port_priv->port_num);
+
        mad_priv_hdr = container_of(mad_list, struct ib_mad_private_header,
                                    mad_list);
        recv = container_of(mad_priv_hdr, struct ib_mad_private, header);
        ib_dma_unmap_single(port_priv->device,
                            recv->header.mapping,
-                           sizeof(struct ib_mad_private) -
-                             sizeof(struct ib_mad_private_header),
+                           mad_priv_dma_size(recv),
                            DMA_FROM_DEVICE);
 
        /* Setup MAD receive work completion from "normal" work completion */
        recv->header.wc = *wc;
        recv->header.recv_wc.wc = &recv->header.wc;
-       recv->header.recv_wc.mad_len = sizeof(struct ib_mad);
-       recv->header.recv_wc.recv_buf.mad = &recv->mad.mad;
+
+       if (opa && ((struct ib_mad_hdr *)(recv->mad))->base_version == OPA_MGMT_BASE_VERSION) {
+               recv->header.recv_wc.mad_len = wc->byte_len - sizeof(struct ib_grh);
+               recv->header.recv_wc.mad_seg_size = sizeof(struct opa_mad);
+       } else {
+               recv->header.recv_wc.mad_len = sizeof(struct ib_mad);
+               recv->header.recv_wc.mad_seg_size = sizeof(struct ib_mad);
+       }
+
+       recv->header.recv_wc.recv_buf.mad = (struct ib_mad *)recv->mad;
        recv->header.recv_wc.recv_buf.grh = &recv->grh;
 
        if (atomic_read(&qp_info->snoop_count))
                snoop_recv(qp_info, &recv->header.recv_wc, IB_MAD_SNOOP_RECVS);
 
        /* Validate MAD */
-       if (!validate_mad(&recv->mad.mad, qp_info->qp->qp_num))
+       if (!validate_mad((const struct ib_mad_hdr *)recv->mad, qp_info, opa))
                goto out;
 
-       response = kmem_cache_alloc(ib_mad_cache, GFP_KERNEL);
+       mad_size = recv->mad_size;
+       response = alloc_mad_private(mad_size, GFP_KERNEL);
        if (!response) {
                dev_err(&port_priv->device->dev,
                        "ib_mad_recv_done_handler no memory for response buffer\n");
@@ -1994,69 +2240,43 @@ static void ib_mad_recv_done_handler(struct ib_mad_port_private *port_priv,
        else
                port_num = port_priv->port_num;
 
-       if (recv->mad.mad.mad_hdr.mgmt_class ==
+       if (((struct ib_mad_hdr *)recv->mad)->mgmt_class ==
            IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE) {
-               enum smi_forward_action retsmi;
-
-               if (smi_handle_dr_smp_recv(&recv->mad.smp,
-                                          port_priv->device->node_type,
-                                          port_num,
-                                          port_priv->device->phys_port_cnt) ==
-                                          IB_SMI_DISCARD)
+               if (handle_smi(port_priv, qp_info, wc, port_num, recv,
+                              response, opa)
+                   == IB_SMI_DISCARD)
                        goto out;
-
-               retsmi = smi_check_forward_dr_smp(&recv->mad.smp);
-               if (retsmi == IB_SMI_LOCAL)
-                       goto local;
-
-               if (retsmi == IB_SMI_SEND) { /* don't forward */
-                       if (smi_handle_dr_smp_send(&recv->mad.smp,
-                                                  port_priv->device->node_type,
-                                                  port_num) == IB_SMI_DISCARD)
-                               goto out;
-
-                       if (smi_check_local_smp(&recv->mad.smp, port_priv->device) == IB_SMI_DISCARD)
-                               goto out;
-               } else if (port_priv->device->node_type == RDMA_NODE_IB_SWITCH) {
-                       /* forward case for switches */
-                       memcpy(response, recv, sizeof(*response));
-                       response->header.recv_wc.wc = &response->header.wc;
-                       response->header.recv_wc.recv_buf.mad = &response->mad.mad;
-                       response->header.recv_wc.recv_buf.grh = &response->grh;
-
-                       agent_send_response(&response->mad.mad,
-                                           &response->grh, wc,
-                                           port_priv->device,
-                                           smi_get_fwd_port(&recv->mad.smp),
-                                           qp_info->qp->qp_num);
-
-                       goto out;
-               }
        }
 
-local:
        /* Give driver "right of first refusal" on incoming MAD */
        if (port_priv->device->process_mad) {
                ret = port_priv->device->process_mad(port_priv->device, 0,
                                                     port_priv->port_num,
                                                     wc, &recv->grh,
-                                                    &recv->mad.mad,
-                                                    &response->mad.mad);
+                                                    (const struct ib_mad_hdr *)recv->mad,
+                                                    recv->mad_size,
+                                                    (struct ib_mad_hdr *)response->mad,
+                                                    &mad_size, &resp_mad_pkey_index);
+
+               if (opa)
+                       wc->pkey_index = resp_mad_pkey_index;
+
                if (ret & IB_MAD_RESULT_SUCCESS) {
                        if (ret & IB_MAD_RESULT_CONSUMED)
                                goto out;
                        if (ret & IB_MAD_RESULT_REPLY) {
-                               agent_send_response(&response->mad.mad,
+                               agent_send_response((const struct ib_mad_hdr *)response->mad,
                                                    &recv->grh, wc,
                                                    port_priv->device,
                                                    port_num,
-                                                   qp_info->qp->qp_num);
+                                                   qp_info->qp->qp_num,
+                                                   mad_size, opa);
                                goto out;
                        }
                }
        }
 
-       mad_agent = find_mad_agent(port_priv, &recv->mad.mad);
+       mad_agent = find_mad_agent(port_priv, (const struct ib_mad_hdr *)recv->mad);
        if (mad_agent) {
                ib_mad_complete_recv(mad_agent, &recv->header.recv_wc);
                /*
@@ -2065,17 +2285,17 @@ local:
                 */
                recv = NULL;
        } else if ((ret & IB_MAD_RESULT_SUCCESS) &&
-                  generate_unmatched_resp(recv, response)) {
-               agent_send_response(&response->mad.mad, &recv->grh, wc,
-                                   port_priv->device, port_num, qp_info->qp->qp_num);
+                  generate_unmatched_resp(recv, response, &mad_size, opa)) {
+               agent_send_response((const struct ib_mad_hdr *)response->mad, &recv->grh, wc,
+                                   port_priv->device, port_num,
+                                   qp_info->qp->qp_num, mad_size, opa);
        }
 
 out:
        /* Post another receive request for this QP */
        if (response) {
                ib_mad_post_receive_mads(qp_info, response);
-               if (recv)
-                       kmem_cache_free(ib_mad_cache, recv);
+               kfree(recv);
        } else
                ib_mad_post_receive_mads(qp_info, recv);
 }
@@ -2411,7 +2631,8 @@ find_send_wr(struct ib_mad_agent_private *mad_agent_priv,
 
        list_for_each_entry(mad_send_wr, &mad_agent_priv->send_list,
                            agent_list) {
-               if (is_data_mad(mad_agent_priv, mad_send_wr->send_buf.mad) &&
+               if (is_rmpp_data_mad(mad_agent_priv,
+                                    mad_send_wr->send_buf.mad) &&
                    &mad_send_wr->send_buf == send_buf)
                        return mad_send_wr;
        }
@@ -2468,10 +2689,14 @@ static void local_completions(struct work_struct *work)
        int free_mad;
        struct ib_wc wc;
        struct ib_mad_send_wc mad_send_wc;
+       bool opa;
 
        mad_agent_priv =
                container_of(work, struct ib_mad_agent_private, local_work);
 
+       opa = rdma_cap_opa_mad(mad_agent_priv->qp_info->port_priv->device,
+                              mad_agent_priv->qp_info->port_priv->port_num);
+
        spin_lock_irqsave(&mad_agent_priv->lock, flags);
        while (!list_empty(&mad_agent_priv->local_list)) {
                local = list_entry(mad_agent_priv->local_list.next,
@@ -2481,6 +2706,7 @@ static void local_completions(struct work_struct *work)
                spin_unlock_irqrestore(&mad_agent_priv->lock, flags);
                free_mad = 0;
                if (local->mad_priv) {
+                       u8 base_version;
                        recv_mad_agent = local->recv_mad_agent;
                        if (!recv_mad_agent) {
                                dev_err(&mad_agent_priv->agent.device->dev,
@@ -2496,17 +2722,26 @@ static void local_completions(struct work_struct *work)
                        build_smp_wc(recv_mad_agent->agent.qp,
                                     (unsigned long) local->mad_send_wr,
                                     be16_to_cpu(IB_LID_PERMISSIVE),
-                                    0, recv_mad_agent->agent.port_num, &wc);
+                                    local->mad_send_wr->send_wr.wr.ud.pkey_index,
+                                    recv_mad_agent->agent.port_num, &wc);
 
                        local->mad_priv->header.recv_wc.wc = &wc;
-                       local->mad_priv->header.recv_wc.mad_len =
-                                               sizeof(struct ib_mad);
+
+                       base_version = ((struct ib_mad_hdr *)(local->mad_priv->mad))->base_version;
+                       if (opa && base_version == OPA_MGMT_BASE_VERSION) {
+                               local->mad_priv->header.recv_wc.mad_len = local->return_wc_byte_len;
+                               local->mad_priv->header.recv_wc.mad_seg_size = sizeof(struct opa_mad);
+                       } else {
+                               local->mad_priv->header.recv_wc.mad_len = sizeof(struct ib_mad);
+                               local->mad_priv->header.recv_wc.mad_seg_size = sizeof(struct ib_mad);
+                       }
+
                        INIT_LIST_HEAD(&local->mad_priv->header.recv_wc.rmpp_list);
                        list_add(&local->mad_priv->header.recv_wc.recv_buf.list,
                                 &local->mad_priv->header.recv_wc.rmpp_list);
                        local->mad_priv->header.recv_wc.recv_buf.grh = NULL;
                        local->mad_priv->header.recv_wc.recv_buf.mad =
-                                               &local->mad_priv->mad.mad;
+                                               (struct ib_mad *)local->mad_priv->mad;
                        if (atomic_read(&recv_mad_agent->qp_info->snoop_count))
                                snoop_recv(recv_mad_agent->qp_info,
                                          &local->mad_priv->header.recv_wc,
@@ -2534,7 +2769,7 @@ local_send_completion:
                spin_lock_irqsave(&mad_agent_priv->lock, flags);
                atomic_dec(&mad_agent_priv->refcount);
                if (free_mad)
-                       kmem_cache_free(ib_mad_cache, local->mad_priv);
+                       kfree(local->mad_priv);
                kfree(local);
        }
        spin_unlock_irqrestore(&mad_agent_priv->lock, flags);
@@ -2649,7 +2884,6 @@ static int ib_mad_post_receive_mads(struct ib_mad_qp_info *qp_info,
        struct ib_mad_queue *recv_queue = &qp_info->recv_queue;
 
        /* Initialize common scatter list fields */
-       sg_list.length = sizeof *mad_priv - sizeof mad_priv->header;
        sg_list.lkey = (*qp_info->port_priv->mr).lkey;
 
        /* Initialize common receive WR fields */
@@ -2663,7 +2897,8 @@ static int ib_mad_post_receive_mads(struct ib_mad_qp_info *qp_info,
                        mad_priv = mad;
                        mad = NULL;
                } else {
-                       mad_priv = kmem_cache_alloc(ib_mad_cache, GFP_KERNEL);
+                       mad_priv = alloc_mad_private(port_mad_size(qp_info->port_priv),
+                                                    GFP_ATOMIC);
                        if (!mad_priv) {
                                dev_err(&qp_info->port_priv->device->dev,
                                        "No memory for receive buffer\n");
@@ -2671,10 +2906,10 @@ static int ib_mad_post_receive_mads(struct ib_mad_qp_info *qp_info,
                                break;
                        }
                }
+               sg_list.length = mad_priv_dma_size(mad_priv);
                sg_list.addr = ib_dma_map_single(qp_info->port_priv->device,
                                                 &mad_priv->grh,
-                                                sizeof *mad_priv -
-                                                  sizeof mad_priv->header,
+                                                mad_priv_dma_size(mad_priv),
                                                 DMA_FROM_DEVICE);
                if (unlikely(ib_dma_mapping_error(qp_info->port_priv->device,
                                                  sg_list.addr))) {
@@ -2698,10 +2933,9 @@ static int ib_mad_post_receive_mads(struct ib_mad_qp_info *qp_info,
                        spin_unlock_irqrestore(&recv_queue->lock, flags);
                        ib_dma_unmap_single(qp_info->port_priv->device,
                                            mad_priv->header.mapping,
-                                           sizeof *mad_priv -
-                                             sizeof mad_priv->header,
+                                           mad_priv_dma_size(mad_priv),
                                            DMA_FROM_DEVICE);
-                       kmem_cache_free(ib_mad_cache, mad_priv);
+                       kfree(mad_priv);
                        dev_err(&qp_info->port_priv->device->dev,
                                "ib_post_recv failed: %d\n", ret);
                        break;
@@ -2738,10 +2972,9 @@ static void cleanup_recv_queue(struct ib_mad_qp_info *qp_info)
 
                ib_dma_unmap_single(qp_info->port_priv->device,
                                    recv->header.mapping,
-                                   sizeof(struct ib_mad_private) -
-                                     sizeof(struct ib_mad_private_header),
+                                   mad_priv_dma_size(recv),
                                    DMA_FROM_DEVICE);
-               kmem_cache_free(ib_mad_cache, recv);
+               kfree(recv);
        }
 
        qp_info->recv_queue.count = 0;
@@ -2922,6 +3155,14 @@ static int ib_mad_port_open(struct ib_device *device,
        unsigned long flags;
        char name[sizeof "ib_mad123"];
        int has_smi;
+       struct ib_cq_init_attr cq_attr = {};
+
+       if (WARN_ON(rdma_max_mad_size(device, port_num) < IB_MGMT_MAD_SIZE))
+               return -EFAULT;
+
+       if (WARN_ON(rdma_cap_opa_mad(device, port_num) &&
+                   rdma_max_mad_size(device, port_num) < OPA_MGMT_MAD_SIZE))
+               return -EFAULT;
 
        /* Create new device info */
        port_priv = kzalloc(sizeof *port_priv, GFP_KERNEL);
@@ -2938,13 +3179,14 @@ static int ib_mad_port_open(struct ib_device *device,
        init_mad_qp(port_priv, &port_priv->qp_info[1]);
 
        cq_size = mad_sendq_size + mad_recvq_size;
-       has_smi = rdma_port_get_link_layer(device, port_num) == IB_LINK_LAYER_INFINIBAND;
+       has_smi = rdma_cap_ib_smi(device, port_num);
        if (has_smi)
                cq_size *= 2;
 
+       cq_attr.cqe = cq_size;
        port_priv->cq = ib_create_cq(port_priv->device,
                                     ib_mad_thread_completion_handler,
-                                    NULL, port_priv, cq_size, 0);
+                                    NULL, port_priv, &cq_attr);
        if (IS_ERR(port_priv->cq)) {
                dev_err(&device->dev, "Couldn't create ib_mad CQ\n");
                ret = PTR_ERR(port_priv->cq);
@@ -3057,9 +3299,6 @@ static void ib_mad_init_device(struct ib_device *device)
 {
        int start, end, i;
 
-       if (rdma_node_get_transport(device->node_type) != RDMA_TRANSPORT_IB)
-               return;
-
        if (device->node_type == RDMA_NODE_IB_SWITCH) {
                start = 0;
                end   = 0;
@@ -3069,6 +3308,9 @@ static void ib_mad_init_device(struct ib_device *device)
        }
 
        for (i = start; i <= end; i++) {
+               if (!rdma_cap_ib_mad(device, i))
+                       continue;
+
                if (ib_mad_port_open(device, i)) {
                        dev_err(&device->dev, "Couldn't open port %d\n", i);
                        goto error;
@@ -3086,40 +3328,39 @@ error_agent:
                dev_err(&device->dev, "Couldn't close port %d\n", i);
 
 error:
-       i--;
+       while (--i >= start) {
+               if (!rdma_cap_ib_mad(device, i))
+                       continue;
 
-       while (i >= start) {
                if (ib_agent_port_close(device, i))
                        dev_err(&device->dev,
                                "Couldn't close port %d for agents\n", i);
                if (ib_mad_port_close(device, i))
                        dev_err(&device->dev, "Couldn't close port %d\n", i);
-               i--;
        }
 }
 
 static void ib_mad_remove_device(struct ib_device *device)
 {
-       int i, num_ports, cur_port;
-
-       if (rdma_node_get_transport(device->node_type) != RDMA_TRANSPORT_IB)
-               return;
+       int start, end, i;
 
        if (device->node_type == RDMA_NODE_IB_SWITCH) {
-               num_ports = 1;
-               cur_port = 0;
+               start = 0;
+               end   = 0;
        } else {
-               num_ports = device->phys_port_cnt;
-               cur_port = 1;
+               start = 1;
+               end   = device->phys_port_cnt;
        }
-       for (i = 0; i < num_ports; i++, cur_port++) {
-               if (ib_agent_port_close(device, cur_port))
+
+       for (i = start; i <= end; i++) {
+               if (!rdma_cap_ib_mad(device, i))
+                       continue;
+
+               if (ib_agent_port_close(device, i))
                        dev_err(&device->dev,
-                               "Couldn't close port %d for agents\n",
-                               cur_port);
-               if (ib_mad_port_close(device, cur_port))
-                       dev_err(&device->dev, "Couldn't close port %d\n",
-                               cur_port);
+                               "Couldn't close port %d for agents\n", i);
+               if (ib_mad_port_close(device, i))
+                       dev_err(&device->dev, "Couldn't close port %d\n", i);
        }
 }
 
@@ -3131,45 +3372,25 @@ static struct ib_client mad_client = {
 
 static int __init ib_mad_init_module(void)
 {
-       int ret;
-
        mad_recvq_size = min(mad_recvq_size, IB_MAD_QP_MAX_SIZE);
        mad_recvq_size = max(mad_recvq_size, IB_MAD_QP_MIN_SIZE);
 
        mad_sendq_size = min(mad_sendq_size, IB_MAD_QP_MAX_SIZE);
        mad_sendq_size = max(mad_sendq_size, IB_MAD_QP_MIN_SIZE);
 
-       ib_mad_cache = kmem_cache_create("ib_mad",
-                                        sizeof(struct ib_mad_private),
-                                        0,
-                                        SLAB_HWCACHE_ALIGN,
-                                        NULL);
-       if (!ib_mad_cache) {
-               pr_err("Couldn't create ib_mad cache\n");
-               ret = -ENOMEM;
-               goto error1;
-       }
-
        INIT_LIST_HEAD(&ib_mad_port_list);
 
        if (ib_register_client(&mad_client)) {
                pr_err("Couldn't register ib_mad client\n");
-               ret = -EINVAL;
-               goto error2;
+               return -EINVAL;
        }
 
        return 0;
-
-error2:
-       kmem_cache_destroy(ib_mad_cache);
-error1:
-       return ret;
 }
 
 static void __exit ib_mad_cleanup_module(void)
 {
        ib_unregister_client(&mad_client);
-       kmem_cache_destroy(ib_mad_cache);
 }
 
 module_init(ib_mad_init_module);
index d1a0b0ee9444ccc35bc063b8accc3f8330107127..5be89f98928f203ff43208a5fed57f3890b6b82e 100644 (file)
@@ -41,6 +41,7 @@
 #include <linux/workqueue.h>
 #include <rdma/ib_mad.h>
 #include <rdma/ib_smi.h>
+#include <rdma/opa_smi.h>
 
 #define IB_MAD_QPS_CORE                2 /* Always QP0 and QP1 as a minimum */
 
@@ -56,7 +57,7 @@
 
 /* Registration table sizes */
 #define MAX_MGMT_CLASS         80
-#define MAX_MGMT_VERSION       8
+#define MAX_MGMT_VERSION       0x83
 #define MAX_MGMT_OUI           8
 #define MAX_MGMT_VENDOR_RANGE2 (IB_MGMT_CLASS_VENDOR_RANGE2_END - \
                                IB_MGMT_CLASS_VENDOR_RANGE2_START + 1)
@@ -75,12 +76,9 @@ struct ib_mad_private_header {
 
 struct ib_mad_private {
        struct ib_mad_private_header header;
+       size_t mad_size;
        struct ib_grh grh;
-       union {
-               struct ib_mad mad;
-               struct ib_rmpp_mad rmpp_mad;
-               struct ib_smp smp;
-       } mad;
+       u8 mad[0];
 } __attribute__ ((packed));
 
 struct ib_rmpp_segment {
@@ -150,6 +148,7 @@ struct ib_mad_local_private {
        struct ib_mad_private *mad_priv;
        struct ib_mad_agent_private *recv_mad_agent;
        struct ib_mad_send_wr_private *mad_send_wr;
+       size_t return_wc_byte_len;
 };
 
 struct ib_mad_mgmt_method_table {
@@ -213,8 +212,8 @@ struct ib_mad_port_private {
 int ib_send_mad(struct ib_mad_send_wr_private *mad_send_wr);
 
 struct ib_mad_send_wr_private *
-ib_find_send_mad(struct ib_mad_agent_private *mad_agent_priv,
-                struct ib_mad_recv_wc *mad_recv_wc);
+ib_find_send_mad(const struct ib_mad_agent_private *mad_agent_priv,
+                const struct ib_mad_recv_wc *mad_recv_wc);
 
 void ib_mad_complete_send_wr(struct ib_mad_send_wr_private *mad_send_wr,
                             struct ib_mad_send_wc *mad_send_wc);
index f37878c9c06eb43812b022ba72a034418a1a238e..382941b46e43aaef78219ad3437598b87a63504e 100644 (file)
@@ -1,6 +1,7 @@
 /*
  * Copyright (c) 2005 Intel Inc. All rights reserved.
  * Copyright (c) 2005-2006 Voltaire, Inc. All rights reserved.
+ * Copyright (c) 2014 Intel Corporation.  All rights reserved.
  *
  * 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
@@ -67,6 +68,7 @@ struct mad_rmpp_recv {
        u8 mgmt_class;
        u8 class_version;
        u8 method;
+       u8 base_version;
 };
 
 static inline void deref_rmpp_recv(struct mad_rmpp_recv *rmpp_recv)
@@ -139,7 +141,8 @@ static void ack_recv(struct mad_rmpp_recv *rmpp_recv,
        hdr_len = ib_get_mad_data_offset(recv_wc->recv_buf.mad->mad_hdr.mgmt_class);
        msg = ib_create_send_mad(&rmpp_recv->agent->agent, recv_wc->wc->src_qp,
                                 recv_wc->wc->pkey_index, 1, hdr_len,
-                                0, GFP_KERNEL);
+                                0, GFP_KERNEL,
+                                IB_MGMT_BASE_VERSION);
        if (IS_ERR(msg))
                return;
 
@@ -165,7 +168,8 @@ static struct ib_mad_send_buf *alloc_response_msg(struct ib_mad_agent *agent,
        hdr_len = ib_get_mad_data_offset(recv_wc->recv_buf.mad->mad_hdr.mgmt_class);
        msg = ib_create_send_mad(agent, recv_wc->wc->src_qp,
                                 recv_wc->wc->pkey_index, 1,
-                                hdr_len, 0, GFP_KERNEL);
+                                hdr_len, 0, GFP_KERNEL,
+                                IB_MGMT_BASE_VERSION);
        if (IS_ERR(msg))
                ib_destroy_ah(ah);
        else {
@@ -316,6 +320,7 @@ create_rmpp_recv(struct ib_mad_agent_private *agent,
        rmpp_recv->mgmt_class = mad_hdr->mgmt_class;
        rmpp_recv->class_version = mad_hdr->class_version;
        rmpp_recv->method  = mad_hdr->method;
+       rmpp_recv->base_version  = mad_hdr->base_version;
        return rmpp_recv;
 
 error: kfree(rmpp_recv);
@@ -431,14 +436,23 @@ static inline int get_mad_len(struct mad_rmpp_recv *rmpp_recv)
 {
        struct ib_rmpp_mad *rmpp_mad;
        int hdr_size, data_size, pad;
+       bool opa = rdma_cap_opa_mad(rmpp_recv->agent->qp_info->port_priv->device,
+                                   rmpp_recv->agent->qp_info->port_priv->port_num);
 
        rmpp_mad = (struct ib_rmpp_mad *)rmpp_recv->cur_seg_buf->mad;
 
        hdr_size = ib_get_mad_data_offset(rmpp_mad->mad_hdr.mgmt_class);
-       data_size = sizeof(struct ib_rmpp_mad) - hdr_size;
-       pad = IB_MGMT_RMPP_DATA - be32_to_cpu(rmpp_mad->rmpp_hdr.paylen_newwin);
-       if (pad > IB_MGMT_RMPP_DATA || pad < 0)
-               pad = 0;
+       if (opa && rmpp_recv->base_version == OPA_MGMT_BASE_VERSION) {
+               data_size = sizeof(struct opa_rmpp_mad) - hdr_size;
+               pad = OPA_MGMT_RMPP_DATA - be32_to_cpu(rmpp_mad->rmpp_hdr.paylen_newwin);
+               if (pad > OPA_MGMT_RMPP_DATA || pad < 0)
+                       pad = 0;
+       } else {
+               data_size = sizeof(struct ib_rmpp_mad) - hdr_size;
+               pad = IB_MGMT_RMPP_DATA - be32_to_cpu(rmpp_mad->rmpp_hdr.paylen_newwin);
+               if (pad > IB_MGMT_RMPP_DATA || pad < 0)
+                       pad = 0;
+       }
 
        return hdr_size + rmpp_recv->seg_num * data_size - pad;
 }
@@ -570,13 +584,14 @@ static int send_next_seg(struct ib_mad_send_wr_private *mad_send_wr)
 
        if (mad_send_wr->seg_num == 1) {
                rmpp_mad->rmpp_hdr.rmpp_rtime_flags |= IB_MGMT_RMPP_FLAG_FIRST;
-               paylen = mad_send_wr->send_buf.seg_count * IB_MGMT_RMPP_DATA -
-                        mad_send_wr->pad;
+               paylen = (mad_send_wr->send_buf.seg_count *
+                         mad_send_wr->send_buf.seg_rmpp_size) -
+                         mad_send_wr->pad;
        }
 
        if (mad_send_wr->seg_num == mad_send_wr->send_buf.seg_count) {
                rmpp_mad->rmpp_hdr.rmpp_rtime_flags |= IB_MGMT_RMPP_FLAG_LAST;
-               paylen = IB_MGMT_RMPP_DATA - mad_send_wr->pad;
+               paylen = mad_send_wr->send_buf.seg_rmpp_size - mad_send_wr->pad;
        }
        rmpp_mad->rmpp_hdr.paylen_newwin = cpu_to_be32(paylen);
 
index fa17b552ff78bc16b1547215682f26649f5af7ac..1244f02a5c6d402aa5389e206b6b8e5482ec2de2 100644 (file)
@@ -780,8 +780,7 @@ static void mcast_event_handler(struct ib_event_handler *handler,
        int index;
 
        dev = container_of(handler, struct mcast_device, event_handler);
-       if (rdma_port_get_link_layer(dev->device, event->element.port_num) !=
-           IB_LINK_LAYER_INFINIBAND)
+       if (!rdma_cap_ib_mcast(dev->device, event->element.port_num))
                return;
 
        index = event->element.port_num - dev->start_port;
@@ -808,9 +807,6 @@ static void mcast_add_one(struct ib_device *device)
        int i;
        int count = 0;
 
-       if (rdma_node_get_transport(device->node_type) != RDMA_TRANSPORT_IB)
-               return;
-
        dev = kmalloc(sizeof *dev + device->phys_port_cnt * sizeof *port,
                      GFP_KERNEL);
        if (!dev)
@@ -824,8 +820,7 @@ static void mcast_add_one(struct ib_device *device)
        }
 
        for (i = 0; i <= dev->end_port - dev->start_port; i++) {
-               if (rdma_port_get_link_layer(device, dev->start_port + i) !=
-                   IB_LINK_LAYER_INFINIBAND)
+               if (!rdma_cap_ib_mcast(device, dev->start_port + i))
                        continue;
                port = &dev->port[i];
                port->dev = dev;
@@ -863,8 +858,7 @@ static void mcast_remove_one(struct ib_device *device)
        flush_workqueue(mcast_wq);
 
        for (i = 0; i <= dev->end_port - dev->start_port; i++) {
-               if (rdma_port_get_link_layer(device, dev->start_port + i) ==
-                   IB_LINK_LAYER_INFINIBAND) {
+               if (rdma_cap_ib_mcast(device, dev->start_port + i)) {
                        port = &dev->port[i];
                        deref_port(port);
                        wait_for_completion(&port->comp);
diff --git a/drivers/infiniband/core/opa_smi.h b/drivers/infiniband/core/opa_smi.h
new file mode 100644 (file)
index 0000000..62d91bf
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2014 Intel Corporation.  All rights reserved.
+ *
+ * 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 __OPA_SMI_H_
+#define __OPA_SMI_H_
+
+#include <rdma/ib_smi.h>
+#include <rdma/opa_smi.h>
+
+#include "smi.h"
+
+enum smi_action opa_smi_handle_dr_smp_recv(struct opa_smp *smp, u8 node_type,
+                                      int port_num, int phys_port_cnt);
+int opa_smi_get_fwd_port(struct opa_smp *smp);
+extern enum smi_forward_action opa_smi_check_forward_dr_smp(struct opa_smp *smp);
+extern enum smi_action opa_smi_handle_dr_smp_send(struct opa_smp *smp,
+                                             u8 node_type, int port_num);
+
+/*
+ * Return IB_SMI_HANDLE if the SMP should be handled by the local SMA/SM
+ * via process_mad
+ */
+static inline enum smi_action opa_smi_check_local_smp(struct opa_smp *smp,
+                                                     struct ib_device *device)
+{
+       /* C14-9:3 -- We're at the end of the DR segment of path */
+       /* C14-9:4 -- Hop Pointer = Hop Count + 1 -> give to SMA/SM */
+       return (device->process_mad &&
+               !opa_get_smp_direction(smp) &&
+               (smp->hop_ptr == smp->hop_cnt + 1)) ?
+               IB_SMI_HANDLE : IB_SMI_DISCARD;
+}
+
+/*
+ * Return IB_SMI_HANDLE if the SMP should be handled by the local SMA/SM
+ * via process_mad
+ */
+static inline enum smi_action opa_smi_check_local_returning_smp(struct opa_smp *smp,
+                                                               struct ib_device *device)
+{
+       /* C14-13:3 -- We're at the end of the DR segment of path */
+       /* C14-13:4 -- Hop Pointer == 0 -> give to SM */
+       return (device->process_mad &&
+               opa_get_smp_direction(smp) &&
+               !smp->hop_ptr) ? IB_SMI_HANDLE : IB_SMI_DISCARD;
+}
+
+#endif /* __OPA_SMI_H_ */
index c38f030f0dc994d7e3edd6b4bd5af893c03d8214..0fae85062a65b8704ddc11a117751df62db9c9d4 100644 (file)
@@ -450,7 +450,7 @@ static void ib_sa_event(struct ib_event_handler *handler, struct ib_event *event
                struct ib_sa_port *port =
                        &sa_dev->port[event->element.port_num - sa_dev->start_port];
 
-               if (rdma_port_get_link_layer(handler->device, port->port_num) != IB_LINK_LAYER_INFINIBAND)
+               if (!rdma_cap_ib_sa(handler->device, port->port_num))
                        return;
 
                spin_lock_irqsave(&port->ah_lock, flags);
@@ -540,7 +540,7 @@ int ib_init_ah_from_path(struct ib_device *device, u8 port_num,
        ah_attr->port_num = port_num;
        ah_attr->static_rate = rec->rate;
 
-       force_grh = rdma_port_get_link_layer(device, port_num) == IB_LINK_LAYER_ETHERNET;
+       force_grh = rdma_cap_eth_ah(device, port_num);
 
        if (rec->hop_limit > 1 || force_grh) {
                ah_attr->ah_flags = IB_AH_GRH;
@@ -583,7 +583,8 @@ static int alloc_mad(struct ib_sa_query *query, gfp_t gfp_mask)
        query->mad_buf = ib_create_send_mad(query->port->agent, 1,
                                            query->sm_ah->pkey_index,
                                            0, IB_MGMT_SA_HDR, IB_MGMT_SA_DATA,
-                                           gfp_mask);
+                                           gfp_mask,
+                                           IB_MGMT_BASE_VERSION);
        if (IS_ERR(query->mad_buf)) {
                kref_put(&query->sm_ah->ref, free_sm_ah);
                return -ENOMEM;
@@ -1153,9 +1154,7 @@ static void ib_sa_add_one(struct ib_device *device)
 {
        struct ib_sa_device *sa_dev;
        int s, e, i;
-
-       if (rdma_node_get_transport(device->node_type) != RDMA_TRANSPORT_IB)
-               return;
+       int count = 0;
 
        if (device->node_type == RDMA_NODE_IB_SWITCH)
                s = e = 0;
@@ -1175,7 +1174,7 @@ static void ib_sa_add_one(struct ib_device *device)
 
        for (i = 0; i <= e - s; ++i) {
                spin_lock_init(&sa_dev->port[i].ah_lock);
-               if (rdma_port_get_link_layer(device, i + 1) != IB_LINK_LAYER_INFINIBAND)
+               if (!rdma_cap_ib_sa(device, i + 1))
                        continue;
 
                sa_dev->port[i].sm_ah    = NULL;
@@ -1189,8 +1188,13 @@ static void ib_sa_add_one(struct ib_device *device)
                        goto err;
 
                INIT_WORK(&sa_dev->port[i].update_task, update_sm_ah);
+
+               count++;
        }
 
+       if (!count)
+               goto free;
+
        ib_set_client_data(device, &sa_client, sa_dev);
 
        /*
@@ -1204,19 +1208,20 @@ static void ib_sa_add_one(struct ib_device *device)
        if (ib_register_event_handler(&sa_dev->event_handler))
                goto err;
 
-       for (i = 0; i <= e - s; ++i)
-               if (rdma_port_get_link_layer(device, i + 1) == IB_LINK_LAYER_INFINIBAND)
+       for (i = 0; i <= e - s; ++i) {
+               if (rdma_cap_ib_sa(device, i + 1))
                        update_sm_ah(&sa_dev->port[i].update_task);
+       }
 
        return;
 
 err:
-       while (--i >= 0)
-               if (rdma_port_get_link_layer(device, i + 1) == IB_LINK_LAYER_INFINIBAND)
+       while (--i >= 0) {
+               if (rdma_cap_ib_sa(device, i + 1))
                        ib_unregister_mad_agent(sa_dev->port[i].agent);
-
+       }
+free:
        kfree(sa_dev);
-
        return;
 }
 
@@ -1233,7 +1238,7 @@ static void ib_sa_remove_one(struct ib_device *device)
        flush_workqueue(ib_wq);
 
        for (i = 0; i <= sa_dev->end_port - sa_dev->start_port; ++i) {
-               if (rdma_port_get_link_layer(device, i + 1) == IB_LINK_LAYER_INFINIBAND) {
+               if (rdma_cap_ib_sa(device, i + 1)) {
                        ib_unregister_mad_agent(sa_dev->port[i].agent);
                        if (sa_dev->port[i].sm_ah)
                                kref_put(&sa_dev->port[i].sm_ah->ref, free_sm_ah);
index 5855e4405d9bf2ca2b04a0fac5aebabed46c538b..368a561d1a5d49d931ef45738c35f3be4b068725 100644 (file)
@@ -5,6 +5,7 @@
  * Copyright (c) 2004, 2005 Topspin Corporation.  All rights reserved.
  * Copyright (c) 2004-2007 Voltaire Corporation.  All rights reserved.
  * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright (c) 2014 Intel Corporation.  All rights reserved.
  *
  * 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
 
 #include <rdma/ib_smi.h>
 #include "smi.h"
-
-/*
- * Fixup a directed route SMP for sending
- * Return 0 if the SMP should be discarded
- */
-enum smi_action smi_handle_dr_smp_send(struct ib_smp *smp,
-                                      u8 node_type, int port_num)
+#include "opa_smi.h"
+
+static enum smi_action __smi_handle_dr_smp_send(u8 node_type, int port_num,
+                                               u8 *hop_ptr, u8 hop_cnt,
+                                               const u8 *initial_path,
+                                               const u8 *return_path,
+                                               u8 direction,
+                                               bool dr_dlid_is_permissive,
+                                               bool dr_slid_is_permissive)
 {
-       u8 hop_ptr, hop_cnt;
-
-       hop_ptr = smp->hop_ptr;
-       hop_cnt = smp->hop_cnt;
-
        /* See section 14.2.2.2, Vol 1 IB spec */
        /* C14-6 -- valid hop_cnt values are from 0 to 63 */
        if (hop_cnt >= IB_SMP_MAX_PATH_HOPS)
                return IB_SMI_DISCARD;
 
-       if (!ib_get_smp_direction(smp)) {
+       if (!direction) {
                /* C14-9:1 */
-               if (hop_cnt && hop_ptr == 0) {
-                       smp->hop_ptr++;
-                       return (smp->initial_path[smp->hop_ptr] ==
+               if (hop_cnt && *hop_ptr == 0) {
+                       (*hop_ptr)++;
+                       return (initial_path[*hop_ptr] ==
                                port_num ? IB_SMI_HANDLE : IB_SMI_DISCARD);
                }
 
                /* C14-9:2 */
-               if (hop_ptr && hop_ptr < hop_cnt) {
+               if (*hop_ptr && *hop_ptr < hop_cnt) {
                        if (node_type != RDMA_NODE_IB_SWITCH)
                                return IB_SMI_DISCARD;
 
-                       /* smp->return_path set when received */
-                       smp->hop_ptr++;
-                       return (smp->initial_path[smp->hop_ptr] ==
+                       /* return_path set when received */
+                       (*hop_ptr)++;
+                       return (initial_path[*hop_ptr] ==
                                port_num ? IB_SMI_HANDLE : IB_SMI_DISCARD);
                }
 
                /* C14-9:3 -- We're at the end of the DR segment of path */
-               if (hop_ptr == hop_cnt) {
-                       /* smp->return_path set when received */
-                       smp->hop_ptr++;
+               if (*hop_ptr == hop_cnt) {
+                       /* return_path set when received */
+                       (*hop_ptr)++;
                        return (node_type == RDMA_NODE_IB_SWITCH ||
-                               smp->dr_dlid == IB_LID_PERMISSIVE ?
+                               dr_dlid_is_permissive ?
                                IB_SMI_HANDLE : IB_SMI_DISCARD);
                }
 
                /* C14-9:4 -- hop_ptr = hop_cnt + 1 -> give to SMA/SM */
                /* C14-9:5 -- Fail unreasonable hop pointer */
-               return (hop_ptr == hop_cnt + 1 ? IB_SMI_HANDLE : IB_SMI_DISCARD);
+               return (*hop_ptr == hop_cnt + 1 ? IB_SMI_HANDLE : IB_SMI_DISCARD);
 
        } else {
                /* C14-13:1 */
-               if (hop_cnt && hop_ptr == hop_cnt + 1) {
-                       smp->hop_ptr--;
-                       return (smp->return_path[smp->hop_ptr] ==
+               if (hop_cnt && *hop_ptr == hop_cnt + 1) {
+                       (*hop_ptr)--;
+                       return (return_path[*hop_ptr] ==
                                port_num ? IB_SMI_HANDLE : IB_SMI_DISCARD);
                }
 
                /* C14-13:2 */
-               if (2 <= hop_ptr && hop_ptr <= hop_cnt) {
+               if (2 <= *hop_ptr && *hop_ptr <= hop_cnt) {
                        if (node_type != RDMA_NODE_IB_SWITCH)
                                return IB_SMI_DISCARD;
 
-                       smp->hop_ptr--;
-                       return (smp->return_path[smp->hop_ptr] ==
+                       (*hop_ptr)--;
+                       return (return_path[*hop_ptr] ==
                                port_num ? IB_SMI_HANDLE : IB_SMI_DISCARD);
                }
 
                /* C14-13:3 -- at the end of the DR segment of path */
-               if (hop_ptr == 1) {
-                       smp->hop_ptr--;
+               if (*hop_ptr == 1) {
+                       (*hop_ptr)--;
                        /* C14-13:3 -- SMPs destined for SM shouldn't be here */
                        return (node_type == RDMA_NODE_IB_SWITCH ||
-                               smp->dr_slid == IB_LID_PERMISSIVE ?
+                               dr_slid_is_permissive ?
                                IB_SMI_HANDLE : IB_SMI_DISCARD);
                }
 
                /* C14-13:4 -- hop_ptr = 0 -> should have gone to SM */
-               if (hop_ptr == 0)
+               if (*hop_ptr == 0)
                        return IB_SMI_HANDLE;
 
                /* C14-13:5 -- Check for unreasonable hop pointer */
@@ -125,105 +123,164 @@ enum smi_action smi_handle_dr_smp_send(struct ib_smp *smp,
 }
 
 /*
- * Adjust information for a received SMP
- * Return 0 if the SMP should be dropped
+ * Fixup a directed route SMP for sending
+ * Return IB_SMI_DISCARD if the SMP should be discarded
  */
-enum smi_action smi_handle_dr_smp_recv(struct ib_smp *smp, u8 node_type,
-                                      int port_num, int phys_port_cnt)
+enum smi_action smi_handle_dr_smp_send(struct ib_smp *smp,
+                                      u8 node_type, int port_num)
 {
-       u8 hop_ptr, hop_cnt;
+       return __smi_handle_dr_smp_send(node_type, port_num,
+                                       &smp->hop_ptr, smp->hop_cnt,
+                                       smp->initial_path,
+                                       smp->return_path,
+                                       ib_get_smp_direction(smp),
+                                       smp->dr_dlid == IB_LID_PERMISSIVE,
+                                       smp->dr_slid == IB_LID_PERMISSIVE);
+}
 
-       hop_ptr = smp->hop_ptr;
-       hop_cnt = smp->hop_cnt;
+enum smi_action opa_smi_handle_dr_smp_send(struct opa_smp *smp,
+                                      u8 node_type, int port_num)
+{
+       return __smi_handle_dr_smp_send(node_type, port_num,
+                                       &smp->hop_ptr, smp->hop_cnt,
+                                       smp->route.dr.initial_path,
+                                       smp->route.dr.return_path,
+                                       opa_get_smp_direction(smp),
+                                       smp->route.dr.dr_dlid ==
+                                       OPA_LID_PERMISSIVE,
+                                       smp->route.dr.dr_slid ==
+                                       OPA_LID_PERMISSIVE);
+}
 
+static enum smi_action __smi_handle_dr_smp_recv(u8 node_type, int port_num,
+                                               int phys_port_cnt,
+                                               u8 *hop_ptr, u8 hop_cnt,
+                                               const u8 *initial_path,
+                                               u8 *return_path,
+                                               u8 direction,
+                                               bool dr_dlid_is_permissive,
+                                               bool dr_slid_is_permissive)
+{
        /* See section 14.2.2.2, Vol 1 IB spec */
        /* C14-6 -- valid hop_cnt values are from 0 to 63 */
        if (hop_cnt >= IB_SMP_MAX_PATH_HOPS)
                return IB_SMI_DISCARD;
 
-       if (!ib_get_smp_direction(smp)) {
+       if (!direction) {
                /* C14-9:1 -- sender should have incremented hop_ptr */
-               if (hop_cnt && hop_ptr == 0)
+               if (hop_cnt && *hop_ptr == 0)
                        return IB_SMI_DISCARD;
 
                /* C14-9:2 -- intermediate hop */
-               if (hop_ptr && hop_ptr < hop_cnt) {
+               if (*hop_ptr && *hop_ptr < hop_cnt) {
                        if (node_type != RDMA_NODE_IB_SWITCH)
                                return IB_SMI_DISCARD;
 
-                       smp->return_path[hop_ptr] = port_num;
-                       /* smp->hop_ptr updated when sending */
-                       return (smp->initial_path[hop_ptr+1] <= phys_port_cnt ?
+                       return_path[*hop_ptr] = port_num;
+                       /* hop_ptr updated when sending */
+                       return (initial_path[*hop_ptr+1] <= phys_port_cnt ?
                                IB_SMI_HANDLE : IB_SMI_DISCARD);
                }
 
                /* C14-9:3 -- We're at the end of the DR segment of path */
-               if (hop_ptr == hop_cnt) {
+               if (*hop_ptr == hop_cnt) {
                        if (hop_cnt)
-                               smp->return_path[hop_ptr] = port_num;
-                       /* smp->hop_ptr updated when sending */
+                               return_path[*hop_ptr] = port_num;
+                       /* hop_ptr updated when sending */
 
                        return (node_type == RDMA_NODE_IB_SWITCH ||
-                               smp->dr_dlid == IB_LID_PERMISSIVE ?
+                               dr_dlid_is_permissive ?
                                IB_SMI_HANDLE : IB_SMI_DISCARD);
                }
 
                /* C14-9:4 -- hop_ptr = hop_cnt + 1 -> give to SMA/SM */
                /* C14-9:5 -- fail unreasonable hop pointer */
-               return (hop_ptr == hop_cnt + 1 ? IB_SMI_HANDLE : IB_SMI_DISCARD);
+               return (*hop_ptr == hop_cnt + 1 ? IB_SMI_HANDLE : IB_SMI_DISCARD);
 
        } else {
 
                /* C14-13:1 */
-               if (hop_cnt && hop_ptr == hop_cnt + 1) {
-                       smp->hop_ptr--;
-                       return (smp->return_path[smp->hop_ptr] ==
+               if (hop_cnt && *hop_ptr == hop_cnt + 1) {
+                       (*hop_ptr)--;
+                       return (return_path[*hop_ptr] ==
                                port_num ? IB_SMI_HANDLE : IB_SMI_DISCARD);
                }
 
                /* C14-13:2 */
-               if (2 <= hop_ptr && hop_ptr <= hop_cnt) {
+               if (2 <= *hop_ptr && *hop_ptr <= hop_cnt) {
                        if (node_type != RDMA_NODE_IB_SWITCH)
                                return IB_SMI_DISCARD;
 
-                       /* smp->hop_ptr updated when sending */
-                       return (smp->return_path[hop_ptr-1] <= phys_port_cnt ?
+                       /* hop_ptr updated when sending */
+                       return (return_path[*hop_ptr-1] <= phys_port_cnt ?
                                IB_SMI_HANDLE : IB_SMI_DISCARD);
                }
 
                /* C14-13:3 -- We're at the end of the DR segment of path */
-               if (hop_ptr == 1) {
-                       if (smp->dr_slid == IB_LID_PERMISSIVE) {
+               if (*hop_ptr == 1) {
+                       if (dr_slid_is_permissive) {
                                /* giving SMP to SM - update hop_ptr */
-                               smp->hop_ptr--;
+                               (*hop_ptr)--;
                                return IB_SMI_HANDLE;
                        }
-                       /* smp->hop_ptr updated when sending */
+                       /* hop_ptr updated when sending */
                        return (node_type == RDMA_NODE_IB_SWITCH ?
                                IB_SMI_HANDLE : IB_SMI_DISCARD);
                }
 
                /* C14-13:4 -- hop_ptr = 0 -> give to SM */
                /* C14-13:5 -- Check for unreasonable hop pointer */
-               return (hop_ptr == 0 ? IB_SMI_HANDLE : IB_SMI_DISCARD);
+               return (*hop_ptr == 0 ? IB_SMI_HANDLE : IB_SMI_DISCARD);
        }
 }
 
-enum smi_forward_action smi_check_forward_dr_smp(struct ib_smp *smp)
+/*
+ * Adjust information for a received SMP
+ * Return IB_SMI_DISCARD if the SMP should be dropped
+ */
+enum smi_action smi_handle_dr_smp_recv(struct ib_smp *smp, u8 node_type,
+                                      int port_num, int phys_port_cnt)
 {
-       u8 hop_ptr, hop_cnt;
+       return __smi_handle_dr_smp_recv(node_type, port_num, phys_port_cnt,
+                                       &smp->hop_ptr, smp->hop_cnt,
+                                       smp->initial_path,
+                                       smp->return_path,
+                                       ib_get_smp_direction(smp),
+                                       smp->dr_dlid == IB_LID_PERMISSIVE,
+                                       smp->dr_slid == IB_LID_PERMISSIVE);
+}
 
-       hop_ptr = smp->hop_ptr;
-       hop_cnt = smp->hop_cnt;
+/*
+ * Adjust information for a received SMP
+ * Return IB_SMI_DISCARD if the SMP should be dropped
+ */
+enum smi_action opa_smi_handle_dr_smp_recv(struct opa_smp *smp, u8 node_type,
+                                          int port_num, int phys_port_cnt)
+{
+       return __smi_handle_dr_smp_recv(node_type, port_num, phys_port_cnt,
+                                       &smp->hop_ptr, smp->hop_cnt,
+                                       smp->route.dr.initial_path,
+                                       smp->route.dr.return_path,
+                                       opa_get_smp_direction(smp),
+                                       smp->route.dr.dr_dlid ==
+                                       OPA_LID_PERMISSIVE,
+                                       smp->route.dr.dr_slid ==
+                                       OPA_LID_PERMISSIVE);
+}
 
-       if (!ib_get_smp_direction(smp)) {
+static enum smi_forward_action __smi_check_forward_dr_smp(u8 hop_ptr, u8 hop_cnt,
+                                                         u8 direction,
+                                                         bool dr_dlid_is_permissive,
+                                                         bool dr_slid_is_permissive)
+{
+       if (!direction) {
                /* C14-9:2 -- intermediate hop */
                if (hop_ptr && hop_ptr < hop_cnt)
                        return IB_SMI_FORWARD;
 
                /* C14-9:3 -- at the end of the DR segment of path */
                if (hop_ptr == hop_cnt)
-                       return (smp->dr_dlid == IB_LID_PERMISSIVE ?
+                       return (dr_dlid_is_permissive ?
                                IB_SMI_SEND : IB_SMI_LOCAL);
 
                /* C14-9:4 -- hop_ptr = hop_cnt + 1 -> give to SMA/SM */
@@ -236,10 +293,29 @@ enum smi_forward_action smi_check_forward_dr_smp(struct ib_smp *smp)
 
                /* C14-13:3 -- at the end of the DR segment of path */
                if (hop_ptr == 1)
-                       return (smp->dr_slid != IB_LID_PERMISSIVE ?
+                       return (!dr_slid_is_permissive ?
                                IB_SMI_SEND : IB_SMI_LOCAL);
        }
        return IB_SMI_LOCAL;
+
+}
+
+enum smi_forward_action smi_check_forward_dr_smp(struct ib_smp *smp)
+{
+       return __smi_check_forward_dr_smp(smp->hop_ptr, smp->hop_cnt,
+                                         ib_get_smp_direction(smp),
+                                         smp->dr_dlid == IB_LID_PERMISSIVE,
+                                         smp->dr_slid == IB_LID_PERMISSIVE);
+}
+
+enum smi_forward_action opa_smi_check_forward_dr_smp(struct opa_smp *smp)
+{
+       return __smi_check_forward_dr_smp(smp->hop_ptr, smp->hop_cnt,
+                                         opa_get_smp_direction(smp),
+                                         smp->route.dr.dr_dlid ==
+                                         OPA_LID_PERMISSIVE,
+                                         smp->route.dr.dr_slid ==
+                                         OPA_LID_PERMISSIVE);
 }
 
 /*
@@ -251,3 +327,13 @@ int smi_get_fwd_port(struct ib_smp *smp)
        return (!ib_get_smp_direction(smp) ? smp->initial_path[smp->hop_ptr+1] :
                smp->return_path[smp->hop_ptr-1]);
 }
+
+/*
+ * Return the forwarding port number from initial_path for outgoing SMP and
+ * from return_path for returning SMP
+ */
+int opa_smi_get_fwd_port(struct opa_smp *smp)
+{
+       return !opa_get_smp_direction(smp) ? smp->route.dr.initial_path[smp->hop_ptr+1] :
+               smp->route.dr.return_path[smp->hop_ptr-1];
+}
index cbd0383f622e0311bd4f56652648609604cb94c4..ed6b6c85c334b124e3fa4a47225c3f8a6b5c62df 100644 (file)
@@ -326,6 +326,8 @@ static ssize_t show_pma_counter(struct ib_port *p, struct port_attribute *attr,
        int width  = (tab_attr->index >> 16) & 0xff;
        struct ib_mad *in_mad  = NULL;
        struct ib_mad *out_mad = NULL;
+       size_t mad_size = sizeof(*out_mad);
+       u16 out_mad_pkey_index = 0;
        ssize_t ret;
 
        if (!p->ibdev->process_mad)
@@ -347,7 +349,10 @@ static ssize_t show_pma_counter(struct ib_port *p, struct port_attribute *attr,
        in_mad->data[41] = p->port_num; /* PortSelect field */
 
        if ((p->ibdev->process_mad(p->ibdev, IB_MAD_IGNORE_MKEY,
-                p->port_num, NULL, NULL, in_mad, out_mad) &
+                p->port_num, NULL, NULL,
+                (const struct ib_mad_hdr *)in_mad, mad_size,
+                (struct ib_mad_hdr *)out_mad, &mad_size,
+                &out_mad_pkey_index) &
             (IB_MAD_RESULT_SUCCESS | IB_MAD_RESULT_REPLY)) !=
            (IB_MAD_RESULT_SUCCESS | IB_MAD_RESULT_REPLY)) {
                ret = -EINVAL;
@@ -456,6 +461,7 @@ static void ib_device_release(struct device *device)
 {
        struct ib_device *dev = container_of(device, struct ib_device, dev);
 
+       kfree(dev->port_immutable);
        kfree(dev);
 }
 
index f2f63933e8a97e889eb8a1cd8b5a5c0b8ec3f475..62c24b1452b89e2546f2e023a560ee3a21f222e4 100644 (file)
@@ -1253,8 +1253,7 @@ static void ib_ucm_add_one(struct ib_device *device)
        dev_t base;
        struct ib_ucm_device *ucm_dev;
 
-       if (!device->alloc_ucontext ||
-           rdma_node_get_transport(device->node_type) != RDMA_TRANSPORT_IB)
+       if (!device->alloc_ucontext || !rdma_cap_ib_cm(device, 1))
                return;
 
        ucm_dev = kzalloc(sizeof *ucm_dev, GFP_KERNEL);
index 45d67e9228d75b44d71354d248cf97140dfe37ad..ad45469f7582dbe47788c5b1330803148c0b5dab 100644 (file)
@@ -722,26 +722,13 @@ static ssize_t ucma_query_route(struct ucma_file *file,
 
        resp.node_guid = (__force __u64) ctx->cm_id->device->node_guid;
        resp.port_num = ctx->cm_id->port_num;
-       switch (rdma_node_get_transport(ctx->cm_id->device->node_type)) {
-       case RDMA_TRANSPORT_IB:
-               switch (rdma_port_get_link_layer(ctx->cm_id->device,
-                       ctx->cm_id->port_num)) {
-               case IB_LINK_LAYER_INFINIBAND:
-                       ucma_copy_ib_route(&resp, &ctx->cm_id->route);
-                       break;
-               case IB_LINK_LAYER_ETHERNET:
-                       ucma_copy_iboe_route(&resp, &ctx->cm_id->route);
-                       break;
-               default:
-                       break;
-               }
-               break;
-       case RDMA_TRANSPORT_IWARP:
+
+       if (rdma_cap_ib_sa(ctx->cm_id->device, ctx->cm_id->port_num))
+               ucma_copy_ib_route(&resp, &ctx->cm_id->route);
+       else if (rdma_protocol_roce(ctx->cm_id->device, ctx->cm_id->port_num))
+               ucma_copy_iboe_route(&resp, &ctx->cm_id->route);
+       else if (rdma_protocol_iwarp(ctx->cm_id->device, ctx->cm_id->port_num))
                ucma_copy_iw_route(&resp, &ctx->cm_id->route);
-               break;
-       default:
-               break;
-       }
 
 out:
        if (copy_to_user((void __user *)(unsigned long)cmd.response,
index 928cdd20e2d11a1abd7c0afb2125f5e7cb6bcc6d..35567fffaa4e330cf7e1b4963f86daf2132b5574 100644 (file)
@@ -99,7 +99,6 @@ struct ib_umad_port {
 };
 
 struct ib_umad_device {
-       int                  start_port, end_port;
        struct kobject       kobj;
        struct ib_umad_port  port[0];
 };
@@ -263,20 +262,23 @@ static ssize_t copy_recv_mad(struct ib_umad_file *file, char __user *buf,
 {
        struct ib_mad_recv_buf *recv_buf;
        int left, seg_payload, offset, max_seg_payload;
+       size_t seg_size;
 
-       /* We need enough room to copy the first (or only) MAD segment. */
        recv_buf = &packet->recv_wc->recv_buf;
-       if ((packet->length <= sizeof (*recv_buf->mad) &&
+       seg_size = packet->recv_wc->mad_seg_size;
+
+       /* We need enough room to copy the first (or only) MAD segment. */
+       if ((packet->length <= seg_size &&
             count < hdr_size(file) + packet->length) ||
-           (packet->length > sizeof (*recv_buf->mad) &&
-            count < hdr_size(file) + sizeof (*recv_buf->mad)))
+           (packet->length > seg_size &&
+            count < hdr_size(file) + seg_size))
                return -EINVAL;
 
        if (copy_to_user(buf, &packet->mad, hdr_size(file)))
                return -EFAULT;
 
        buf += hdr_size(file);
-       seg_payload = min_t(int, packet->length, sizeof (*recv_buf->mad));
+       seg_payload = min_t(int, packet->length, seg_size);
        if (copy_to_user(buf, recv_buf->mad, seg_payload))
                return -EFAULT;
 
@@ -293,7 +295,7 @@ static ssize_t copy_recv_mad(struct ib_umad_file *file, char __user *buf,
                        return -ENOSPC;
                }
                offset = ib_get_mad_data_offset(recv_buf->mad->mad_hdr.mgmt_class);
-               max_seg_payload = sizeof (struct ib_mad) - offset;
+               max_seg_payload = seg_size - offset;
 
                for (left = packet->length - seg_payload, buf += seg_payload;
                     left; left -= seg_payload, buf += seg_payload) {
@@ -426,11 +428,11 @@ static int is_duplicate(struct ib_umad_file *file,
                 * the same TID, reject the second as a duplicate.  This is more
                 * restrictive than required by the spec.
                 */
-               if (!ib_response_mad((struct ib_mad *) hdr)) {
-                       if (!ib_response_mad((struct ib_mad *) sent_hdr))
+               if (!ib_response_mad(hdr)) {
+                       if (!ib_response_mad(sent_hdr))
                                return 1;
                        continue;
-               } else if (!ib_response_mad((struct ib_mad *) sent_hdr))
+               } else if (!ib_response_mad(sent_hdr))
                        continue;
 
                if (same_destination(&packet->mad.hdr, &sent_packet->mad.hdr))
@@ -451,6 +453,7 @@ static ssize_t ib_umad_write(struct file *filp, const char __user *buf,
        struct ib_rmpp_mad *rmpp_mad;
        __be64 *tid;
        int ret, data_len, hdr_len, copy_offset, rmpp_active;
+       u8 base_version;
 
        if (count < hdr_size(file) + IB_MGMT_RMPP_HDR)
                return -EINVAL;
@@ -517,11 +520,13 @@ static ssize_t ib_umad_write(struct file *filp, const char __user *buf,
                rmpp_active = 0;
        }
 
+       base_version = ((struct ib_mad_hdr *)&packet->mad.data)->base_version;
        data_len = count - hdr_size(file) - hdr_len;
        packet->msg = ib_create_send_mad(agent,
                                         be32_to_cpu(packet->mad.hdr.qpn),
                                         packet->mad.hdr.pkey_index, rmpp_active,
-                                        hdr_len, data_len, GFP_KERNEL);
+                                        hdr_len, data_len, GFP_KERNEL,
+                                        base_version);
        if (IS_ERR(packet->msg)) {
                ret = PTR_ERR(packet->msg);
                goto err_ah;
@@ -1273,16 +1278,10 @@ static void ib_umad_add_one(struct ib_device *device)
 {
        struct ib_umad_device *umad_dev;
        int s, e, i;
+       int count = 0;
 
-       if (rdma_node_get_transport(device->node_type) != RDMA_TRANSPORT_IB)
-               return;
-
-       if (device->node_type == RDMA_NODE_IB_SWITCH)
-               s = e = 0;
-       else {
-               s = 1;
-               e = device->phys_port_cnt;
-       }
+       s = rdma_start_port(device);
+       e = rdma_end_port(device);
 
        umad_dev = kzalloc(sizeof *umad_dev +
                           (e - s + 1) * sizeof (struct ib_umad_port),
@@ -1292,25 +1291,34 @@ static void ib_umad_add_one(struct ib_device *device)
 
        kobject_init(&umad_dev->kobj, &ib_umad_dev_ktype);
 
-       umad_dev->start_port = s;
-       umad_dev->end_port   = e;
-
        for (i = s; i <= e; ++i) {
+               if (!rdma_cap_ib_mad(device, i))
+                       continue;
+
                umad_dev->port[i - s].umad_dev = umad_dev;
 
                if (ib_umad_init_port(device, i, umad_dev,
                                      &umad_dev->port[i - s]))
                        goto err;
+
+               count++;
        }
 
+       if (!count)
+               goto free;
+
        ib_set_client_data(device, &umad_client, umad_dev);
 
        return;
 
 err:
-       while (--i >= s)
-               ib_umad_kill_port(&umad_dev->port[i - s]);
+       while (--i >= s) {
+               if (!rdma_cap_ib_mad(device, i))
+                       continue;
 
+               ib_umad_kill_port(&umad_dev->port[i - s]);
+       }
+free:
        kobject_put(&umad_dev->kobj);
 }
 
@@ -1322,8 +1330,10 @@ static void ib_umad_remove_one(struct ib_device *device)
        if (!umad_dev)
                return;
 
-       for (i = 0; i <= umad_dev->end_port - umad_dev->start_port; ++i)
-               ib_umad_kill_port(&umad_dev->port[i]);
+       for (i = 0; i <= rdma_end_port(device) - rdma_start_port(device); ++i) {
+               if (rdma_cap_ib_mad(device, i + rdma_start_port(device)))
+                       ib_umad_kill_port(&umad_dev->port[i]);
+       }
 
        kobject_put(&umad_dev->kobj);
 }
index b716b08156446e186c9ae608f3f4e6343c6f200f..ba365b6d1e8d561d891f358da5f3f05e54800740 100644 (file)
@@ -259,5 +259,6 @@ IB_UVERBS_DECLARE_CMD(close_xrcd);
 IB_UVERBS_DECLARE_EX_CMD(create_flow);
 IB_UVERBS_DECLARE_EX_CMD(destroy_flow);
 IB_UVERBS_DECLARE_EX_CMD(query_device);
+IB_UVERBS_DECLARE_EX_CMD(create_cq);
 
 #endif /* UVERBS_H */
index a9f048990dfcd833de09978c0448ad979749e4c9..bbb02ffe87df97ea2696e97e4baf9ae1718d0355 100644 (file)
@@ -1330,40 +1330,37 @@ ssize_t ib_uverbs_create_comp_channel(struct ib_uverbs_file *file,
        return in_len;
 }
 
-ssize_t ib_uverbs_create_cq(struct ib_uverbs_file *file,
-                           const char __user *buf, int in_len,
-                           int out_len)
+static struct ib_ucq_object *create_cq(struct ib_uverbs_file *file,
+                                      struct ib_udata *ucore,
+                                      struct ib_udata *uhw,
+                                      struct ib_uverbs_ex_create_cq *cmd,
+                                      size_t cmd_sz,
+                                      int (*cb)(struct ib_uverbs_file *file,
+                                                struct ib_ucq_object *obj,
+                                                struct ib_uverbs_ex_create_cq_resp *resp,
+                                                struct ib_udata *udata,
+                                                void *context),
+                                      void *context)
 {
-       struct ib_uverbs_create_cq      cmd;
-       struct ib_uverbs_create_cq_resp resp;
-       struct ib_udata                 udata;
        struct ib_ucq_object           *obj;
        struct ib_uverbs_event_file    *ev_file = NULL;
        struct ib_cq                   *cq;
        int                             ret;
+       struct ib_uverbs_ex_create_cq_resp resp;
+       struct ib_cq_init_attr attr = {};
 
-       if (out_len < sizeof resp)
-               return -ENOSPC;
-
-       if (copy_from_user(&cmd, buf, sizeof cmd))
-               return -EFAULT;
-
-       INIT_UDATA(&udata, buf + sizeof cmd,
-                  (unsigned long) cmd.response + sizeof resp,
-                  in_len - sizeof cmd, out_len - sizeof resp);
-
-       if (cmd.comp_vector >= file->device->num_comp_vectors)
-               return -EINVAL;
+       if (cmd->comp_vector >= file->device->num_comp_vectors)
+               return ERR_PTR(-EINVAL);
 
        obj = kmalloc(sizeof *obj, GFP_KERNEL);
        if (!obj)
-               return -ENOMEM;
+               return ERR_PTR(-ENOMEM);
 
-       init_uobj(&obj->uobject, cmd.user_handle, file->ucontext, &cq_lock_class);
+       init_uobj(&obj->uobject, cmd->user_handle, file->ucontext, &cq_lock_class);
        down_write(&obj->uobject.mutex);
 
-       if (cmd.comp_channel >= 0) {
-               ev_file = ib_uverbs_lookup_comp_file(cmd.comp_channel);
+       if (cmd->comp_channel >= 0) {
+               ev_file = ib_uverbs_lookup_comp_file(cmd->comp_channel);
                if (!ev_file) {
                        ret = -EINVAL;
                        goto err;
@@ -1376,9 +1373,14 @@ ssize_t ib_uverbs_create_cq(struct ib_uverbs_file *file,
        INIT_LIST_HEAD(&obj->comp_list);
        INIT_LIST_HEAD(&obj->async_list);
 
-       cq = file->device->ib_dev->create_cq(file->device->ib_dev, cmd.cqe,
-                                            cmd.comp_vector,
-                                            file->ucontext, &udata);
+       attr.cqe = cmd->cqe;
+       attr.comp_vector = cmd->comp_vector;
+
+       if (cmd_sz > offsetof(typeof(*cmd), flags) + sizeof(cmd->flags))
+               attr.flags = cmd->flags;
+
+       cq = file->device->ib_dev->create_cq(file->device->ib_dev, &attr,
+                                            file->ucontext, uhw);
        if (IS_ERR(cq)) {
                ret = PTR_ERR(cq);
                goto err_file;
@@ -1397,14 +1399,15 @@ ssize_t ib_uverbs_create_cq(struct ib_uverbs_file *file,
                goto err_free;
 
        memset(&resp, 0, sizeof resp);
-       resp.cq_handle = obj->uobject.id;
-       resp.cqe       = cq->cqe;
+       resp.base.cq_handle = obj->uobject.id;
+       resp.base.cqe       = cq->cqe;
 
-       if (copy_to_user((void __user *) (unsigned long) cmd.response,
-                        &resp, sizeof resp)) {
-               ret = -EFAULT;
-               goto err_copy;
-       }
+       resp.response_length = offsetof(typeof(resp), response_length) +
+               sizeof(resp.response_length);
+
+       ret = cb(file, obj, &resp, ucore, context);
+       if (ret)
+               goto err_cb;
 
        mutex_lock(&file->mutex);
        list_add_tail(&obj->uobject.list, &file->ucontext->cq_list);
@@ -1414,9 +1417,9 @@ ssize_t ib_uverbs_create_cq(struct ib_uverbs_file *file,
 
        up_write(&obj->uobject.mutex);
 
-       return in_len;
+       return obj;
 
-err_copy:
+err_cb:
        idr_remove_uobj(&ib_uverbs_cq_idr, &obj->uobject);
 
 err_free:
@@ -1428,7 +1431,106 @@ err_file:
 
 err:
        put_uobj_write(&obj->uobject);
-       return ret;
+
+       return ERR_PTR(ret);
+}
+
+static int ib_uverbs_create_cq_cb(struct ib_uverbs_file *file,
+                                 struct ib_ucq_object *obj,
+                                 struct ib_uverbs_ex_create_cq_resp *resp,
+                                 struct ib_udata *ucore, void *context)
+{
+       if (ib_copy_to_udata(ucore, &resp->base, sizeof(resp->base)))
+               return -EFAULT;
+
+       return 0;
+}
+
+ssize_t ib_uverbs_create_cq(struct ib_uverbs_file *file,
+                           const char __user *buf, int in_len,
+                           int out_len)
+{
+       struct ib_uverbs_create_cq      cmd;
+       struct ib_uverbs_ex_create_cq   cmd_ex;
+       struct ib_uverbs_create_cq_resp resp;
+       struct ib_udata                 ucore;
+       struct ib_udata                 uhw;
+       struct ib_ucq_object           *obj;
+
+       if (out_len < sizeof(resp))
+               return -ENOSPC;
+
+       if (copy_from_user(&cmd, buf, sizeof(cmd)))
+               return -EFAULT;
+
+       INIT_UDATA(&ucore, buf, cmd.response, sizeof(cmd), sizeof(resp));
+
+       INIT_UDATA(&uhw, buf + sizeof(cmd),
+                  (unsigned long)cmd.response + sizeof(resp),
+                  in_len - sizeof(cmd), out_len - sizeof(resp));
+
+       memset(&cmd_ex, 0, sizeof(cmd_ex));
+       cmd_ex.user_handle = cmd.user_handle;
+       cmd_ex.cqe = cmd.cqe;
+       cmd_ex.comp_vector = cmd.comp_vector;
+       cmd_ex.comp_channel = cmd.comp_channel;
+
+       obj = create_cq(file, &ucore, &uhw, &cmd_ex,
+                       offsetof(typeof(cmd_ex), comp_channel) +
+                       sizeof(cmd.comp_channel), ib_uverbs_create_cq_cb,
+                       NULL);
+
+       if (IS_ERR(obj))
+               return PTR_ERR(obj);
+
+       return in_len;
+}
+
+static int ib_uverbs_ex_create_cq_cb(struct ib_uverbs_file *file,
+                                    struct ib_ucq_object *obj,
+                                    struct ib_uverbs_ex_create_cq_resp *resp,
+                                    struct ib_udata *ucore, void *context)
+{
+       if (ib_copy_to_udata(ucore, resp, resp->response_length))
+               return -EFAULT;
+
+       return 0;
+}
+
+int ib_uverbs_ex_create_cq(struct ib_uverbs_file *file,
+                          struct ib_udata *ucore,
+                          struct ib_udata *uhw)
+{
+       struct ib_uverbs_ex_create_cq_resp resp;
+       struct ib_uverbs_ex_create_cq  cmd;
+       struct ib_ucq_object           *obj;
+       int err;
+
+       if (ucore->inlen < sizeof(cmd))
+               return -EINVAL;
+
+       err = ib_copy_from_udata(&cmd, ucore, sizeof(cmd));
+       if (err)
+               return err;
+
+       if (cmd.comp_mask)
+               return -EINVAL;
+
+       if (cmd.reserved)
+               return -EINVAL;
+
+       if (ucore->outlen < (offsetof(typeof(resp), response_length) +
+                            sizeof(resp.response_length)))
+               return -ENOSPC;
+
+       obj = create_cq(file, ucore, uhw, &cmd,
+                       min(ucore->inlen, sizeof(cmd)),
+                       ib_uverbs_ex_create_cq_cb, NULL);
+
+       if (IS_ERR(obj))
+               return PTR_ERR(obj);
+
+       return 0;
 }
 
 ssize_t ib_uverbs_resize_cq(struct ib_uverbs_file *file,
@@ -3324,7 +3426,9 @@ int ib_uverbs_ex_query_device(struct ib_uverbs_file *file,
        if (ucore->outlen < resp.response_length)
                return -ENOSPC;
 
-       err = device->query_device(device, &attr);
+       memset(&attr, 0, sizeof(attr));
+
+       err = device->query_device(device, &attr, uhw);
        if (err)
                return err;
 
@@ -3348,6 +3452,18 @@ int ib_uverbs_ex_query_device(struct ib_uverbs_file *file,
 #endif
        resp.response_length += sizeof(resp.odp_caps);
 
+       if (ucore->outlen < resp.response_length + sizeof(resp.timestamp_mask))
+               goto end;
+
+       resp.timestamp_mask = attr.timestamp_mask;
+       resp.response_length += sizeof(resp.timestamp_mask);
+
+       if (ucore->outlen < resp.response_length + sizeof(resp.hca_core_clock))
+               goto end;
+
+       resp.hca_core_clock = attr.hca_core_clock;
+       resp.response_length += sizeof(resp.hca_core_clock);
+
 end:
        err = ib_copy_to_udata(ucore, &resp, resp.response_length);
        if (err)
index 88cce9bb72fea78a7d03f9c556b394807ca87215..f6eef2da7097980b7066c62f1746d7722064f8f3 100644 (file)
@@ -124,6 +124,7 @@ static int (*uverbs_ex_cmd_table[])(struct ib_uverbs_file *file,
        [IB_USER_VERBS_EX_CMD_CREATE_FLOW]      = ib_uverbs_ex_create_flow,
        [IB_USER_VERBS_EX_CMD_DESTROY_FLOW]     = ib_uverbs_ex_destroy_flow,
        [IB_USER_VERBS_EX_CMD_QUERY_DEVICE]     = ib_uverbs_ex_query_device,
+       [IB_USER_VERBS_EX_CMD_CREATE_CQ]        = ib_uverbs_ex_create_cq,
 };
 
 static void ib_uverbs_add_one(struct ib_device *device);
index f93eb8da7b5ad443900c3b8b423da505d0531a95..bac3fb406a7470edb0deb1d66d6559118fe8ac53 100644 (file)
 
 #include "core_priv.h"
 
+static const char * const ib_events[] = {
+       [IB_EVENT_CQ_ERR]               = "CQ error",
+       [IB_EVENT_QP_FATAL]             = "QP fatal error",
+       [IB_EVENT_QP_REQ_ERR]           = "QP request error",
+       [IB_EVENT_QP_ACCESS_ERR]        = "QP access error",
+       [IB_EVENT_COMM_EST]             = "communication established",
+       [IB_EVENT_SQ_DRAINED]           = "send queue drained",
+       [IB_EVENT_PATH_MIG]             = "path migration successful",
+       [IB_EVENT_PATH_MIG_ERR]         = "path migration error",
+       [IB_EVENT_DEVICE_FATAL]         = "device fatal error",
+       [IB_EVENT_PORT_ACTIVE]          = "port active",
+       [IB_EVENT_PORT_ERR]             = "port error",
+       [IB_EVENT_LID_CHANGE]           = "LID change",
+       [IB_EVENT_PKEY_CHANGE]          = "P_key change",
+       [IB_EVENT_SM_CHANGE]            = "SM change",
+       [IB_EVENT_SRQ_ERR]              = "SRQ error",
+       [IB_EVENT_SRQ_LIMIT_REACHED]    = "SRQ limit reached",
+       [IB_EVENT_QP_LAST_WQE_REACHED]  = "last WQE reached",
+       [IB_EVENT_CLIENT_REREGISTER]    = "client reregister",
+       [IB_EVENT_GID_CHANGE]           = "GID changed",
+};
+
+const char *ib_event_msg(enum ib_event_type event)
+{
+       size_t index = event;
+
+       return (index < ARRAY_SIZE(ib_events) && ib_events[index]) ?
+                       ib_events[index] : "unrecognized event";
+}
+EXPORT_SYMBOL(ib_event_msg);
+
+static const char * const wc_statuses[] = {
+       [IB_WC_SUCCESS]                 = "success",
+       [IB_WC_LOC_LEN_ERR]             = "local length error",
+       [IB_WC_LOC_QP_OP_ERR]           = "local QP operation error",
+       [IB_WC_LOC_EEC_OP_ERR]          = "local EE context operation error",
+       [IB_WC_LOC_PROT_ERR]            = "local protection error",
+       [IB_WC_WR_FLUSH_ERR]            = "WR flushed",
+       [IB_WC_MW_BIND_ERR]             = "memory management operation error",
+       [IB_WC_BAD_RESP_ERR]            = "bad response error",
+       [IB_WC_LOC_ACCESS_ERR]          = "local access error",
+       [IB_WC_REM_INV_REQ_ERR]         = "invalid request error",
+       [IB_WC_REM_ACCESS_ERR]          = "remote access error",
+       [IB_WC_REM_OP_ERR]              = "remote operation error",
+       [IB_WC_RETRY_EXC_ERR]           = "transport retry counter exceeded",
+       [IB_WC_RNR_RETRY_EXC_ERR]       = "RNR retry counter exceeded",
+       [IB_WC_LOC_RDD_VIOL_ERR]        = "local RDD violation error",
+       [IB_WC_REM_INV_RD_REQ_ERR]      = "remote invalid RD request",
+       [IB_WC_REM_ABORT_ERR]           = "operation aborted",
+       [IB_WC_INV_EECN_ERR]            = "invalid EE context number",
+       [IB_WC_INV_EEC_STATE_ERR]       = "invalid EE context state",
+       [IB_WC_FATAL_ERR]               = "fatal error",
+       [IB_WC_RESP_TIMEOUT_ERR]        = "response timeout error",
+       [IB_WC_GENERAL_ERR]             = "general error",
+};
+
+const char *ib_wc_status_msg(enum ib_wc_status status)
+{
+       size_t index = status;
+
+       return (index < ARRAY_SIZE(wc_statuses) && wc_statuses[index]) ?
+                       wc_statuses[index] : "unrecognized status";
+}
+EXPORT_SYMBOL(ib_wc_status_msg);
+
 __attribute_const__ int ib_rate_to_mult(enum ib_rate rate)
 {
        switch (rate) {
@@ -192,17 +257,16 @@ struct ib_ah *ib_create_ah(struct ib_pd *pd, struct ib_ah_attr *ah_attr)
 }
 EXPORT_SYMBOL(ib_create_ah);
 
-int ib_init_ah_from_wc(struct ib_device *device, u8 port_num, struct ib_wc *wc,
-                      struct ib_grh *grh, struct ib_ah_attr *ah_attr)
+int ib_init_ah_from_wc(struct ib_device *device, u8 port_num,
+                      const struct ib_wc *wc, const struct ib_grh *grh,
+                      struct ib_ah_attr *ah_attr)
 {
        u32 flow_class;
        u16 gid_index;
        int ret;
-       int is_eth = (rdma_port_get_link_layer(device, port_num) ==
-                       IB_LINK_LAYER_ETHERNET);
 
        memset(ah_attr, 0, sizeof *ah_attr);
-       if (is_eth) {
+       if (rdma_cap_eth_ah(device, port_num)) {
                if (!(wc->wc_flags & IB_WC_GRH))
                        return -EPROTOTYPE;
 
@@ -244,8 +308,8 @@ int ib_init_ah_from_wc(struct ib_device *device, u8 port_num, struct ib_wc *wc,
 }
 EXPORT_SYMBOL(ib_init_ah_from_wc);
 
-struct ib_ah *ib_create_ah_from_wc(struct ib_pd *pd, struct ib_wc *wc,
-                                  struct ib_grh *grh, u8 port_num)
+struct ib_ah *ib_create_ah_from_wc(struct ib_pd *pd, const struct ib_wc *wc,
+                                  const struct ib_grh *grh, u8 port_num)
 {
        struct ib_ah_attr ah_attr;
        int ret;
@@ -871,7 +935,7 @@ int ib_resolve_eth_l2_attrs(struct ib_qp *qp,
        union ib_gid  sgid;
 
        if ((*qp_attr_mask & IB_QP_AV)  &&
-           (rdma_port_get_link_layer(qp->device, qp_attr->ah_attr.port_num) == IB_LINK_LAYER_ETHERNET)) {
+           (rdma_cap_eth_ah(qp->device, qp_attr->ah_attr.port_num))) {
                ret = ib_query_gid(qp->device, qp_attr->ah_attr.port_num,
                                   qp_attr->ah_attr.grh.sgid_index, &sgid);
                if (ret)
@@ -1012,11 +1076,12 @@ EXPORT_SYMBOL(ib_destroy_qp);
 struct ib_cq *ib_create_cq(struct ib_device *device,
                           ib_comp_handler comp_handler,
                           void (*event_handler)(struct ib_event *, void *),
-                          void *cq_context, int cqe, int comp_vector)
+                          void *cq_context,
+                          const struct ib_cq_init_attr *cq_attr)
 {
        struct ib_cq *cq;
 
-       cq = device->create_cq(device, cqe, comp_vector, NULL, NULL);
+       cq = device->create_cq(device, cq_attr, NULL, NULL);
 
        if (!IS_ERR(cq)) {
                cq->device        = device;
index bdf3507810cb767767cd8496ea510f9a14497f7c..25c3f008556380eb87678a3129fe2051158cdf82 100644 (file)
 #include "c2_provider.h"
 #include "c2_user.h"
 
-static int c2_query_device(struct ib_device *ibdev,
-                          struct ib_device_attr *props)
+static int c2_query_device(struct ib_device *ibdev, struct ib_device_attr *props,
+                          struct ib_udata *uhw)
 {
        struct c2_dev *c2dev = to_c2dev(ibdev);
 
        pr_debug("%s:%u\n", __func__, __LINE__);
 
+       if (uhw->inlen || uhw->outlen)
+               return -EINVAL;
+
        *props = c2dev->props;
        return 0;
 }
@@ -286,13 +289,18 @@ static int c2_destroy_qp(struct ib_qp *ib_qp)
        return 0;
 }
 
-static struct ib_cq *c2_create_cq(struct ib_device *ibdev, int entries, int vector,
+static struct ib_cq *c2_create_cq(struct ib_device *ibdev,
+                                 const struct ib_cq_init_attr *attr,
                                  struct ib_ucontext *context,
                                  struct ib_udata *udata)
 {
+       int entries = attr->cqe;
        struct c2_cq *cq;
        int err;
 
+       if (attr->flags)
+               return ERR_PTR(-EINVAL);
+
        cq = kmalloc(sizeof(*cq), GFP_KERNEL);
        if (!cq) {
                pr_debug("%s: Unable to allocate CQ\n", __func__);
@@ -582,9 +590,13 @@ static int c2_multicast_detach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid)
 static int c2_process_mad(struct ib_device *ibdev,
                          int mad_flags,
                          u8 port_num,
-                         struct ib_wc *in_wc,
-                         struct ib_grh *in_grh,
-                         struct ib_mad *in_mad, struct ib_mad *out_mad)
+                         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)
 {
        pr_debug("%s:%u\n", __func__, __LINE__);
        return -ENOSYS;
@@ -757,6 +769,23 @@ static struct net_device *c2_pseudo_netdev_init(struct c2_dev *c2dev)
        return netdev;
 }
 
+static int c2_port_immutable(struct ib_device *ibdev, u8 port_num,
+                            struct ib_port_immutable *immutable)
+{
+       struct ib_port_attr attr;
+       int err;
+
+       err = c2_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_IWARP;
+
+       return 0;
+}
+
 int c2_register_device(struct c2_dev *dev)
 {
        int ret = -ENOMEM;
@@ -820,6 +849,7 @@ int c2_register_device(struct c2_dev *dev)
        dev->ibdev.reg_phys_mr = c2_reg_phys_mr;
        dev->ibdev.reg_user_mr = c2_reg_user_mr;
        dev->ibdev.dereg_mr = c2_dereg_mr;
+       dev->ibdev.get_port_immutable = c2_port_immutable;
 
        dev->ibdev.alloc_fmr = NULL;
        dev->ibdev.unmap_fmr = NULL;
index 811b24a539c0037c83704339b19bcbdcf8060b77..b1b73232f21702161d7dc068f08e23e6759e04e8 100644 (file)
@@ -85,9 +85,13 @@ static int iwch_multicast_detach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid)
 static int iwch_process_mad(struct ib_device *ibdev,
                            int mad_flags,
                            u8 port_num,
-                           struct ib_wc *in_wc,
-                           struct ib_grh *in_grh,
-                           struct ib_mad *in_mad, struct ib_mad *out_mad)
+                           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)
 {
        return -ENOSYS;
 }
@@ -138,10 +142,12 @@ static int iwch_destroy_cq(struct ib_cq *ib_cq)
        return 0;
 }
 
-static struct ib_cq *iwch_create_cq(struct ib_device *ibdev, int entries, int vector,
-                            struct ib_ucontext *ib_context,
-                            struct ib_udata *udata)
+static struct ib_cq *iwch_create_cq(struct ib_device *ibdev,
+                                   const struct ib_cq_init_attr *attr,
+                                   struct ib_ucontext *ib_context,
+                                   struct ib_udata *udata)
 {
+       int entries = attr->cqe;
        struct iwch_dev *rhp;
        struct iwch_cq *chp;
        struct iwch_create_cq_resp uresp;
@@ -151,6 +157,9 @@ static struct ib_cq *iwch_create_cq(struct ib_device *ibdev, int entries, int ve
        size_t resplen;
 
        PDBG("%s ib_dev %p entries %d\n", __func__, ibdev, entries);
+       if (attr->flags)
+               return ERR_PTR(-EINVAL);
+
        rhp = to_iwch_dev(ibdev);
        chp = kzalloc(sizeof(*chp), GFP_KERNEL);
        if (!chp)
@@ -1145,13 +1154,17 @@ static u64 fw_vers_string_to_u64(struct iwch_dev *iwch_dev)
               (fw_mic & 0xffff);
 }
 
-static int iwch_query_device(struct ib_device *ibdev,
-                            struct ib_device_attr *props)
+static int iwch_query_device(struct ib_device *ibdev, struct ib_device_attr *props,
+                            struct ib_udata *uhw)
 {
 
        struct iwch_dev *dev;
+
        PDBG("%s ibdev %p\n", __func__, ibdev);
 
+       if (uhw->inlen || uhw->outlen)
+               return -EINVAL;
+
        dev = to_iwch_dev(ibdev);
        memset(props, 0, sizeof *props);
        memcpy(&props->sys_image_guid, dev->rdev.t3cdev_p->lldev->dev_addr, 6);
@@ -1343,6 +1356,23 @@ static struct device_attribute *iwch_class_attributes[] = {
        &dev_attr_board_id,
 };
 
+static int iwch_port_immutable(struct ib_device *ibdev, u8 port_num,
+                              struct ib_port_immutable *immutable)
+{
+       struct ib_port_attr attr;
+       int err;
+
+       err = iwch_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_IWARP;
+
+       return 0;
+}
+
 int iwch_register_device(struct iwch_dev *dev)
 {
        int ret;
@@ -1420,6 +1450,7 @@ int iwch_register_device(struct iwch_dev *dev)
        dev->ibdev.post_recv = iwch_post_receive;
        dev->ibdev.get_protocol_stats = iwch_get_mib;
        dev->ibdev.uverbs_abi_ver = IWCH_UVERBS_ABI_VERSION;
+       dev->ibdev.get_port_immutable = iwch_port_immutable;
 
        dev->ibdev.iwcm = kmalloc(sizeof(struct iw_cm_verbs), GFP_KERNEL);
        if (!dev->ibdev.iwcm)
index 68ddb37102152ec5382a7bbee7e9bab89e681b94..c7aab48f07cdfcdebf3efb6374416619c9095e04 100644 (file)
@@ -156,19 +156,17 @@ static int create_cq(struct c4iw_rdev *rdev, struct t4_cq *cq,
                goto err4;
 
        cq->gen = 1;
+       cq->gts = rdev->lldi.gts_reg;
        cq->rdev = rdev;
-       if (user) {
-               u32 off = (cq->cqid << rdev->cqshift) & PAGE_MASK;
 
-               cq->ugts = (u64)rdev->bar2_pa + off;
-       } else if (is_t4(rdev->lldi.adapter_type)) {
-               cq->gts = rdev->lldi.gts_reg;
-               cq->qid_mask = -1U;
-       } else {
-               u32 off = ((cq->cqid << rdev->cqshift) & PAGE_MASK) + 12;
-
-               cq->gts = rdev->bar2_kva + off;
-               cq->qid_mask = rdev->qpmask;
+       cq->bar2_va = c4iw_bar2_addrs(rdev, cq->cqid, T4_BAR2_QTYPE_INGRESS,
+                                     &cq->bar2_qid,
+                                     user ? &cq->bar2_pa : NULL);
+       if (user && !cq->bar2_va) {
+               pr_warn(MOD "%s: cqid %u not in BAR2 range.\n",
+                       pci_name(rdev->lldi.pdev), cq->cqid);
+               ret = -EINVAL;
+               goto err4;
        }
        return 0;
 err4:
@@ -866,10 +864,13 @@ int c4iw_destroy_cq(struct ib_cq *ib_cq)
        return 0;
 }
 
-struct ib_cq *c4iw_create_cq(struct ib_device *ibdev, int entries,
-                            int vector, struct ib_ucontext *ib_context,
+struct ib_cq *c4iw_create_cq(struct ib_device *ibdev,
+                            const struct ib_cq_init_attr *attr,
+                            struct ib_ucontext *ib_context,
                             struct ib_udata *udata)
 {
+       int entries = attr->cqe;
+       int vector = attr->comp_vector;
        struct c4iw_dev *rhp;
        struct c4iw_cq *chp;
        struct c4iw_create_cq_resp uresp;
@@ -879,6 +880,8 @@ struct ib_cq *c4iw_create_cq(struct ib_device *ibdev, int entries,
        struct c4iw_mm_entry *mm, *mm2;
 
        PDBG("%s ib_dev %p entries %d\n", __func__, ibdev, entries);
+       if (attr->flags)
+               return ERR_PTR(-EINVAL);
 
        rhp = to_c4iw_dev(ibdev);
 
@@ -971,7 +974,7 @@ struct ib_cq *c4iw_create_cq(struct ib_device *ibdev, int entries,
                insert_mmap(ucontext, mm);
 
                mm2->key = uresp.gts_key;
-               mm2->addr = chp->cq.ugts;
+               mm2->addr = chp->cq.bar2_pa;
                mm2->len = PAGE_SIZE;
                insert_mmap(ucontext, mm2);
        }
index 7e895d714b19e35a49ddd69f5a3432af5bcf1f71..1a297391b54c16c3a954650b57ef164ee471ca57 100644 (file)
@@ -795,13 +795,7 @@ static int c4iw_rdev_open(struct c4iw_rdev *rdev)
                goto err1;
        }
 
-       /*
-        * qpshift is the number of bits to shift the qpid left in order
-        * to get the correct address of the doorbell for that qp.
-        */
-       rdev->qpshift = PAGE_SHIFT - ilog2(rdev->lldi.udb_density);
        rdev->qpmask = rdev->lldi.udb_density - 1;
-       rdev->cqshift = PAGE_SHIFT - ilog2(rdev->lldi.ucq_density);
        rdev->cqmask = rdev->lldi.ucq_density - 1;
        PDBG("%s dev %s stag start 0x%0x size 0x%0x num stags %d "
             "pbl start 0x%0x size 0x%0x rq start 0x%0x size 0x%0x "
@@ -815,14 +809,12 @@ static int c4iw_rdev_open(struct c4iw_rdev *rdev)
             rdev->lldi.vr->qp.size,
             rdev->lldi.vr->cq.start,
             rdev->lldi.vr->cq.size);
-       PDBG("udb len 0x%x udb base %p db_reg %p gts_reg %p qpshift %lu "
-            "qpmask 0x%x cqshift %lu cqmask 0x%x\n",
+       PDBG("udb len 0x%x udb base %p db_reg %p gts_reg %p "
+            "qpmask 0x%x cqmask 0x%x\n",
             (unsigned)pci_resource_len(rdev->lldi.pdev, 2),
             (void *)pci_resource_start(rdev->lldi.pdev, 2),
-            rdev->lldi.db_reg,
-            rdev->lldi.gts_reg,
-            rdev->qpshift, rdev->qpmask,
-            rdev->cqshift, rdev->cqmask);
+            rdev->lldi.db_reg, rdev->lldi.gts_reg,
+            rdev->qpmask, rdev->cqmask);
 
        if (c4iw_num_stags(rdev) == 0) {
                err = -EINVAL;
index 97bb5550a6cf64bd77eb3d429b43b6c9d3e10b7e..cc77844fada38e2f3b00e51223e38dbbb7e29434 100644 (file)
@@ -165,9 +165,7 @@ struct wr_log_entry {
 
 struct c4iw_rdev {
        struct c4iw_resource resource;
-       unsigned long qpshift;
        u32 qpmask;
-       unsigned long cqshift;
        u32 cqmask;
        struct c4iw_dev_ucontext uctx;
        struct gen_pool *pbl_pool;
@@ -992,10 +990,10 @@ int c4iw_reregister_phys_mem(struct ib_mr *mr,
                                     int acc, u64 *iova_start);
 int c4iw_dereg_mr(struct ib_mr *ib_mr);
 int c4iw_destroy_cq(struct ib_cq *ib_cq);
-struct ib_cq *c4iw_create_cq(struct ib_device *ibdev, int entries,
-                                       int vector,
-                                       struct ib_ucontext *ib_context,
-                                       struct ib_udata *udata);
+struct ib_cq *c4iw_create_cq(struct ib_device *ibdev,
+                            const struct ib_cq_init_attr *attr,
+                            struct ib_ucontext *ib_context,
+                            struct ib_udata *udata);
 int c4iw_resize_cq(struct ib_cq *cq, int cqe, struct ib_udata *udata);
 int c4iw_arm_cq(struct ib_cq *ibcq, enum ib_cq_notify_flags flags);
 int c4iw_destroy_qp(struct ib_qp *ib_qp);
@@ -1032,6 +1030,9 @@ void c4iw_ev_dispatch(struct c4iw_dev *dev, struct t4_cqe *err_cqe);
 
 extern struct cxgb4_client t4c_client;
 extern c4iw_handler_func c4iw_handlers[NUM_CPL_CMDS];
+void __iomem *c4iw_bar2_addrs(struct c4iw_rdev *rdev, unsigned int qid,
+                             enum cxgb4_bar2_qtype qtype,
+                             unsigned int *pbar2_qid, u64 *pbar2_pa);
 extern void c4iw_log_wr_stats(struct t4_wq *wq, struct t4_cqe *cqe);
 extern int c4iw_wr_log;
 extern int db_fc_threshold;
index 66bd6a2ad83b04f34e2b5fbb822e0b8ff062359b..62c816af46e480a433e31bdeae22fbcd81f710e9 100644 (file)
@@ -80,9 +80,13 @@ static int c4iw_multicast_detach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid)
 }
 
 static int c4iw_process_mad(struct ib_device *ibdev, int mad_flags,
-                           u8 port_num, struct ib_wc *in_wc,
-                           struct ib_grh *in_grh, struct ib_mad *in_mad,
-                           struct ib_mad *out_mad)
+                           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)
 {
        return -ENOSYS;
 }
@@ -301,13 +305,17 @@ static int c4iw_query_gid(struct ib_device *ibdev, u8 port, int index,
        return 0;
 }
 
-static int c4iw_query_device(struct ib_device *ibdev,
-                            struct ib_device_attr *props)
+static int c4iw_query_device(struct ib_device *ibdev, struct ib_device_attr *props,
+                            struct ib_udata *uhw)
 {
 
        struct c4iw_dev *dev;
+
        PDBG("%s ibdev %p\n", __func__, ibdev);
 
+       if (uhw->inlen || uhw->outlen)
+               return -EINVAL;
+
        dev = to_c4iw_dev(ibdev);
        memset(props, 0, sizeof *props);
        memcpy(&props->sys_image_guid, dev->rdev.lldi.ports[0]->dev_addr, 6);
@@ -465,6 +473,23 @@ static struct device_attribute *c4iw_class_attributes[] = {
        &dev_attr_board_id,
 };
 
+static int c4iw_port_immutable(struct ib_device *ibdev, u8 port_num,
+                              struct ib_port_immutable *immutable)
+{
+       struct ib_port_attr attr;
+       int err;
+
+       err = c4iw_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_IWARP;
+
+       return 0;
+}
+
 int c4iw_register_device(struct c4iw_dev *dev)
 {
        int ret;
@@ -542,6 +567,7 @@ int c4iw_register_device(struct c4iw_dev *dev)
        dev->ibdev.post_recv = c4iw_post_receive;
        dev->ibdev.get_protocol_stats = c4iw_get_mib;
        dev->ibdev.uverbs_abi_ver = C4IW_UVERBS_ABI_VERSION;
+       dev->ibdev.get_port_immutable = c4iw_port_immutable;
 
        dev->ibdev.iwcm = kmalloc(sizeof(struct iw_cm_verbs), GFP_KERNEL);
        if (!dev->ibdev.iwcm)
index 389ced335bc5cc528f7ef4ba1e4121c5ec79295c..6517e1208ccb42dc38cd551a971e531e2eb7cd51 100644 (file)
@@ -165,6 +165,29 @@ static int destroy_qp(struct c4iw_rdev *rdev, struct t4_wq *wq,
        return 0;
 }
 
+/*
+ * Determine the BAR2 virtual address and qid. If pbar2_pa is not NULL,
+ * then this is a user mapping so compute the page-aligned physical address
+ * for mapping.
+ */
+void __iomem *c4iw_bar2_addrs(struct c4iw_rdev *rdev, unsigned int qid,
+                             enum cxgb4_bar2_qtype qtype,
+                             unsigned int *pbar2_qid, u64 *pbar2_pa)
+{
+       u64 bar2_qoffset;
+       int ret;
+
+       ret = cxgb4_bar2_sge_qregs(rdev->lldi.ports[0], qid, qtype,
+                                  pbar2_pa ? 1 : 0,
+                                  &bar2_qoffset, pbar2_qid);
+       if (ret)
+               return NULL;
+
+       if (pbar2_pa)
+               *pbar2_pa = (rdev->bar2_pa + bar2_qoffset) & PAGE_MASK;
+       return rdev->bar2_kva + bar2_qoffset;
+}
+
 static int create_qp(struct c4iw_rdev *rdev, struct t4_wq *wq,
                     struct t4_cq *rcq, struct t4_cq *scq,
                     struct c4iw_dev_ucontext *uctx)
@@ -236,25 +259,23 @@ static int create_qp(struct c4iw_rdev *rdev, struct t4_wq *wq,
        dma_unmap_addr_set(&wq->rq, mapping, wq->rq.dma_addr);
 
        wq->db = rdev->lldi.db_reg;
-       wq->gts = rdev->lldi.gts_reg;
-       if (user || is_t5(rdev->lldi.adapter_type)) {
-               u32 off;
 
-               off = (wq->sq.qid << rdev->qpshift) & PAGE_MASK;
-               if (user) {
-                       wq->sq.udb = (u64 __iomem *)(rdev->bar2_pa + off);
-               } else {
-                       off += 128 * (wq->sq.qid & rdev->qpmask) + 8;
-                       wq->sq.udb = (u64 __iomem *)(rdev->bar2_kva + off);
-               }
-               off = (wq->rq.qid << rdev->qpshift) & PAGE_MASK;
-               if (user) {
-                       wq->rq.udb = (u64 __iomem *)(rdev->bar2_pa + off);
-               } else {
-                       off += 128 * (wq->rq.qid & rdev->qpmask) + 8;
-                       wq->rq.udb = (u64 __iomem *)(rdev->bar2_kva + off);
-               }
+       wq->sq.bar2_va = c4iw_bar2_addrs(rdev, wq->sq.qid, T4_BAR2_QTYPE_EGRESS,
+                                        &wq->sq.bar2_qid,
+                                        user ? &wq->sq.bar2_pa : NULL);
+       wq->rq.bar2_va = c4iw_bar2_addrs(rdev, wq->rq.qid, T4_BAR2_QTYPE_EGRESS,
+                                        &wq->rq.bar2_qid,
+                                        user ? &wq->rq.bar2_pa : NULL);
+
+       /*
+        * User mode must have bar2 access.
+        */
+       if (user && (!wq->sq.bar2_va || !wq->rq.bar2_va)) {
+               pr_warn(MOD "%s: sqid %u or rqid %u not in BAR2 range.\n",
+                       pci_name(rdev->lldi.pdev), wq->sq.qid, wq->rq.qid);
+               goto free_dma;
        }
+
        wq->rdev = rdev;
        wq->rq.msn = 1;
 
@@ -336,10 +357,9 @@ static int create_qp(struct c4iw_rdev *rdev, struct t4_wq *wq,
        if (ret)
                goto free_dma;
 
-       PDBG("%s sqid 0x%x rqid 0x%x kdb 0x%p squdb 0x%lx rqudb 0x%lx\n",
+       PDBG("%s sqid 0x%x rqid 0x%x kdb 0x%p sq_bar2_addr %p rq_bar2_addr %p\n",
             __func__, wq->sq.qid, wq->rq.qid, wq->db,
-            (__force unsigned long) wq->sq.udb,
-            (__force unsigned long) wq->rq.udb);
+            wq->sq.bar2_va, wq->rq.bar2_va);
 
        return 0;
 free_dma:
@@ -1766,11 +1786,11 @@ struct ib_qp *c4iw_create_qp(struct ib_pd *pd, struct ib_qp_init_attr *attrs,
                mm2->len = PAGE_ALIGN(qhp->wq.rq.memsize);
                insert_mmap(ucontext, mm2);
                mm3->key = uresp.sq_db_gts_key;
-               mm3->addr = (__force unsigned long)qhp->wq.sq.udb;
+               mm3->addr = (__force unsigned long)qhp->wq.sq.bar2_pa;
                mm3->len = PAGE_SIZE;
                insert_mmap(ucontext, mm3);
                mm4->key = uresp.rq_db_gts_key;
-               mm4->addr = (__force unsigned long)qhp->wq.rq.udb;
+               mm4->addr = (__force unsigned long)qhp->wq.rq.bar2_pa;
                mm4->len = PAGE_SIZE;
                insert_mmap(ucontext, mm4);
                if (mm5) {
index 7f2a6c244d25d67ea922ab35ba568e8b73196ffe..274a7ab13befb367cedae3618a29e2ba0591b72a 100644 (file)
@@ -33,6 +33,7 @@
 
 #include "t4_hw.h"
 #include "t4_regs.h"
+#include "t4_values.h"
 #include "t4_msg.h"
 #include "t4fw_ri_api.h"
 
@@ -290,8 +291,10 @@ struct t4_sq {
        unsigned long phys_addr;
        struct t4_swsqe *sw_sq;
        struct t4_swsqe *oldest_read;
-       u64 __iomem *udb;
+       void __iomem *bar2_va;
+       u64 bar2_pa;
        size_t memsize;
+       u32 bar2_qid;
        u32 qid;
        u16 in_use;
        u16 size;
@@ -314,8 +317,10 @@ struct t4_rq {
        dma_addr_t dma_addr;
        DEFINE_DMA_UNMAP_ADDR(mapping);
        struct t4_swrqe *sw_rq;
-       u64 __iomem *udb;
+       void __iomem *bar2_va;
+       u64 bar2_pa;
        size_t memsize;
+       u32 bar2_qid;
        u32 qid;
        u32 msn;
        u32 rqt_hwaddr;
@@ -332,7 +337,6 @@ struct t4_wq {
        struct t4_sq sq;
        struct t4_rq rq;
        void __iomem *db;
-       void __iomem *gts;
        struct c4iw_rdev *rdev;
        int flushed;
 };
@@ -457,15 +461,18 @@ static inline void t4_ring_sq_db(struct t4_wq *wq, u16 inc, u8 t5,
 
        /* Flush host queue memory writes. */
        wmb();
-       if (t5) {
-               if (inc == 1 && wqe) {
+       if (wq->sq.bar2_va) {
+               if (inc == 1 && wq->sq.bar2_qid == 0 && wqe) {
                        PDBG("%s: WC wq->sq.pidx = %d\n",
                             __func__, wq->sq.pidx);
-                       pio_copy(wq->sq.udb + 7, (void *)wqe);
+                       pio_copy((u64 __iomem *)
+                                (wq->sq.bar2_va + SGE_UDB_WCDOORBELL),
+                                (u64 *)wqe);
                } else {
                        PDBG("%s: DB wq->sq.pidx = %d\n",
                             __func__, wq->sq.pidx);
-                       writel(PIDX_T5_V(inc), wq->sq.udb);
+                       writel(PIDX_T5_V(inc) | QID_V(wq->sq.bar2_qid),
+                              wq->sq.bar2_va + SGE_UDB_KDOORBELL);
                }
 
                /* Flush user doorbell area writes. */
@@ -481,15 +488,18 @@ static inline void t4_ring_rq_db(struct t4_wq *wq, u16 inc, u8 t5,
 
        /* Flush host queue memory writes. */
        wmb();
-       if (t5) {
-               if (inc == 1 && wqe) {
+       if (wq->rq.bar2_va) {
+               if (inc == 1 && wq->rq.bar2_qid == 0 && wqe) {
                        PDBG("%s: WC wq->rq.pidx = %d\n",
                             __func__, wq->rq.pidx);
-                       pio_copy(wq->rq.udb + 7, (void *)wqe);
+                       pio_copy((u64 __iomem *)
+                                (wq->rq.bar2_va + SGE_UDB_WCDOORBELL),
+                                (void *)wqe);
                } else {
                        PDBG("%s: DB wq->rq.pidx = %d\n",
                             __func__, wq->rq.pidx);
-                       writel(PIDX_T5_V(inc), wq->rq.udb);
+                       writel(PIDX_T5_V(inc) | QID_V(wq->rq.bar2_qid),
+                              wq->rq.bar2_va + SGE_UDB_KDOORBELL);
                }
 
                /* Flush user doorbell area writes. */
@@ -534,8 +544,10 @@ struct t4_cq {
        DEFINE_DMA_UNMAP_ADDR(mapping);
        struct t4_cqe *sw_queue;
        void __iomem *gts;
+       void __iomem *bar2_va;
+       u64 bar2_pa;
+       u32 bar2_qid;
        struct c4iw_rdev *rdev;
-       u64 ugts;
        size_t memsize;
        __be64 bits_type_ts;
        u32 cqid;
@@ -552,6 +564,15 @@ struct t4_cq {
        unsigned long flags;
 };
 
+static inline void write_gts(struct t4_cq *cq, u32 val)
+{
+       if (cq->bar2_va)
+               writel(val | INGRESSQID_V(cq->bar2_qid),
+                      cq->bar2_va + SGE_UDB_GTS);
+       else
+               writel(val | INGRESSQID_V(cq->cqid), cq->gts);
+}
+
 static inline int t4_clear_cq_armed(struct t4_cq *cq)
 {
        return test_and_clear_bit(CQ_ARMED, &cq->flags);
@@ -563,14 +584,12 @@ static inline int t4_arm_cq(struct t4_cq *cq, int se)
 
        set_bit(CQ_ARMED, &cq->flags);
        while (cq->cidx_inc > CIDXINC_M) {
-               val = SEINTARM_V(0) | CIDXINC_V(CIDXINC_M) | TIMERREG_V(7) |
-                     INGRESSQID_V(cq->cqid & cq->qid_mask);
-               writel(val, cq->gts);
+               val = SEINTARM_V(0) | CIDXINC_V(CIDXINC_M) | TIMERREG_V(7);
+               write_gts(cq, val);
                cq->cidx_inc -= CIDXINC_M;
        }
-       val = SEINTARM_V(se) | CIDXINC_V(cq->cidx_inc) | TIMERREG_V(6) |
-             INGRESSQID_V(cq->cqid & cq->qid_mask);
-       writel(val, cq->gts);
+       val = SEINTARM_V(se) | CIDXINC_V(cq->cidx_inc) | TIMERREG_V(6);
+       write_gts(cq, val);
        cq->cidx_inc = 0;
        return 0;
 }
@@ -601,9 +620,8 @@ static inline void t4_hwcq_consume(struct t4_cq *cq)
        if (++cq->cidx_inc == (cq->size >> 4) || cq->cidx_inc == CIDXINC_M) {
                u32 val;
 
-               val = SEINTARM_V(0) | CIDXINC_V(cq->cidx_inc) | TIMERREG_V(7) |
-                     INGRESSQID_V(cq->cqid & cq->qid_mask);
-               writel(val, cq->gts);
+               val = SEINTARM_V(0) | CIDXINC_V(cq->cidx_inc) | TIMERREG_V(7);
+               write_gts(cq, val);
                cq->cidx_inc = 0;
        }
        if (++cq->cidx == cq->size) {
index 8cc837537768f97f99041ec7ab69d2e111dba0c0..9b68b175069b9a1b6f95bfc1beae065b9ee6b682 100644 (file)
@@ -113,10 +113,12 @@ struct ehca_qp *ehca_cq_get_qp(struct ehca_cq *cq, int real_qp_num)
        return ret;
 }
 
-struct ib_cq *ehca_create_cq(struct ib_device *device, int cqe, int comp_vector,
+struct ib_cq *ehca_create_cq(struct ib_device *device,
+                            const struct ib_cq_init_attr *attr,
                             struct ib_ucontext *context,
                             struct ib_udata *udata)
 {
+       int cqe = attr->cqe;
        static const u32 additional_cqe = 20;
        struct ib_cq *cq;
        struct ehca_cq *my_cq;
@@ -131,6 +133,9 @@ struct ib_cq *ehca_create_cq(struct ib_device *device, int cqe, int comp_vector,
        int ipz_rc, i;
        unsigned long flags;
 
+       if (attr->flags)
+               return ERR_PTR(-EINVAL);
+
        if (cqe >= 0xFFFFFFFF - 64 - additional_cqe)
                return ERR_PTR(-EINVAL);
 
index 9ed4d2588304a2029e4d401aaee28e8cddd3403e..e8b1bb65797a97c19180a477a7bb62471f25dbf5 100644 (file)
@@ -50,7 +50,8 @@ static unsigned int limit_uint(unsigned int value)
        return min_t(unsigned int, value, INT_MAX);
 }
 
-int ehca_query_device(struct ib_device *ibdev, struct ib_device_attr *props)
+int ehca_query_device(struct ib_device *ibdev, struct ib_device_attr *props,
+                     struct ib_udata *uhw)
 {
        int i, ret = 0;
        struct ehca_shca *shca = container_of(ibdev, struct ehca_shca,
@@ -71,6 +72,9 @@ int ehca_query_device(struct ib_device *ibdev, struct ib_device_attr *props)
                IB_DEVICE_PORT_ACTIVE_EVENT,  HCA_CAP_PORT_ACTIVE_EVENT,
        };
 
+       if (uhw->inlen || uhw->outlen)
+               return -EINVAL;
+
        rblock = ehca_alloc_fw_ctrlblock(GFP_KERNEL);
        if (!rblock) {
                ehca_err(&shca->ib_device, "Can't allocate rblock memory.");
index 22f79afa7fc11b7eef3e3597119545fb659fc1ee..80e6a3d5df3e035ea1afdc3ec6bc013050ec9e3e 100644 (file)
 
 #include "ehca_classes.h"
 
-int ehca_query_device(struct ib_device *ibdev, struct ib_device_attr *props);
+int ehca_query_device(struct ib_device *ibdev, struct ib_device_attr *props,
+                     struct ib_udata *uhw);
 
 int ehca_query_port(struct ib_device *ibdev, u8 port,
                    struct ib_port_attr *props);
 
+enum rdma_protocol_type
+ehca_query_protocol(struct ib_device *device, u8 port_num);
+
 int ehca_query_sma_attr(struct ehca_shca *shca, u8 port,
                        struct ehca_sma_attr *attr);
 
@@ -126,7 +130,8 @@ int ehca_destroy_eq(struct ehca_shca *shca, struct ehca_eq *eq);
 void *ehca_poll_eq(struct ehca_shca *shca, struct ehca_eq *eq);
 
 
-struct ib_cq *ehca_create_cq(struct ib_device *device, int cqe, int comp_vector,
+struct ib_cq *ehca_create_cq(struct ib_device *device,
+                            const struct ib_cq_init_attr *attr,
                             struct ib_ucontext *context,
                             struct ib_udata *udata);
 
@@ -188,9 +193,10 @@ int ehca_dealloc_ucontext(struct ib_ucontext *context);
 int ehca_mmap(struct ib_ucontext *context, struct vm_area_struct *vma);
 
 int ehca_process_mad(struct ib_device *ibdev, int mad_flags, u8 port_num,
-                    struct ib_wc *in_wc, struct ib_grh *in_grh,
-                    struct ib_mad *in_mad,
-                    struct ib_mad *out_mad);
+                    const struct ib_wc *in_wc, const struct ib_grh *in_grh,
+                    const struct ib_mad_hdr *in, size_t in_mad_size,
+                    struct ib_mad_hdr *out, size_t *out_mad_size,
+                    u16 *out_mad_pkey_index);
 
 void ehca_poll_eqs(unsigned long data);
 
index cd8d290a09fc2029f5542660c42e652b9216fe3e..8246418cd4e085352f31954dc876c303a0b10daa 100644 (file)
@@ -46,6 +46,7 @@
 
 #include <linux/notifier.h>
 #include <linux/memory.h>
+#include <rdma/ib_mad.h>
 #include "ehca_classes.h"
 #include "ehca_iverbs.h"
 #include "ehca_mrmw.h"
@@ -431,6 +432,24 @@ init_node_guid1:
        return ret;
 }
 
+static int ehca_port_immutable(struct ib_device *ibdev, u8 port_num,
+                              struct ib_port_immutable *immutable)
+{
+       struct ib_port_attr attr;
+       int err;
+
+       err = ehca_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_IB;
+       immutable->max_mad_size = IB_MGMT_MAD_SIZE;
+
+       return 0;
+}
+
 static int ehca_init_device(struct ehca_shca *shca)
 {
        int ret;
@@ -510,6 +529,7 @@ static int ehca_init_device(struct ehca_shca *shca)
        shca->ib_device.process_mad         = ehca_process_mad;
        shca->ib_device.mmap                = ehca_mmap;
        shca->ib_device.dma_ops             = &ehca_dma_mapping_ops;
+       shca->ib_device.get_port_immutable  = ehca_port_immutable;
 
        if (EHCA_BMASK_GET(HCA_CAP_SRQ, shca->hca_cap)) {
                shca->ib_device.uverbs_cmd_mask |=
@@ -534,6 +554,7 @@ static int ehca_create_aqp1(struct ehca_shca *shca, u32 port)
        struct ib_cq *ibcq;
        struct ib_qp *ibqp;
        struct ib_qp_init_attr qp_init_attr;
+       struct ib_cq_init_attr cq_attr = {};
        int ret;
 
        if (sport->ibcq_aqp1) {
@@ -541,7 +562,9 @@ static int ehca_create_aqp1(struct ehca_shca *shca, u32 port)
                return -EPERM;
        }
 
-       ibcq = ib_create_cq(&shca->ib_device, NULL, NULL, (void *)(-1), 10, 0);
+       cq_attr.cqe = 10;
+       ibcq = ib_create_cq(&shca->ib_device, NULL, NULL, (void *)(-1),
+                           &cq_attr);
        if (IS_ERR(ibcq)) {
                ehca_err(&shca->ib_device, "Cannot create AQP1 CQ.");
                return PTR_ERR(ibcq);
index dba8f9f8b9964bcfe61a899b6a7c5e2500b807dc..12b5bc23832b13804c1f07b61ee4d83b8650dcd1 100644 (file)
@@ -140,10 +140,10 @@ struct vertcfl {
 } __attribute__ ((packed));
 
 static int ehca_process_perf(struct ib_device *ibdev, u8 port_num,
-                            struct ib_wc *in_wc, struct ib_grh *in_grh,
-                            struct ib_mad *in_mad, struct ib_mad *out_mad)
+                            const struct ib_wc *in_wc, const struct ib_grh *in_grh,
+                            const struct ib_mad *in_mad, struct ib_mad *out_mad)
 {
-       struct ib_perf *in_perf = (struct ib_perf *)in_mad;
+       const struct ib_perf *in_perf = (const struct ib_perf *)in_mad;
        struct ib_perf *out_perf = (struct ib_perf *)out_mad;
        struct ib_class_port_info *poi =
                (struct ib_class_port_info *)out_perf->data;
@@ -187,8 +187,8 @@ static int ehca_process_perf(struct ib_device *ibdev, u8 port_num,
 
                /* if request was globally routed, copy route info */
                if (in_grh) {
-                       struct vertcfl *vertcfl =
-                               (struct vertcfl *)&in_grh->version_tclass_flow;
+                       const struct vertcfl *vertcfl =
+                               (const struct vertcfl *)&in_grh->version_tclass_flow;
                        memcpy(poi->redirect_gid, in_grh->dgid.raw,
                               sizeof(poi->redirect_gid));
                        tcslfl->tc        = vertcfl->tc;
@@ -217,10 +217,17 @@ perf_reply:
 }
 
 int ehca_process_mad(struct ib_device *ibdev, int mad_flags, u8 port_num,
-                    struct ib_wc *in_wc, struct ib_grh *in_grh,
-                    struct ib_mad *in_mad, struct ib_mad *out_mad)
+                    const struct ib_wc *in_wc, const struct ib_grh *in_grh,
+                    const struct ib_mad_hdr *in, size_t in_mad_size,
+                    struct ib_mad_hdr *out, size_t *out_mad_size,
+                    u16 *out_mad_pkey_index)
 {
        int ret;
+       const struct ib_mad *in_mad = (const struct ib_mad *)in;
+       struct ib_mad *out_mad = (struct ib_mad *)out;
+
+       BUG_ON(in_mad_size != sizeof(*in_mad) ||
+              *out_mad_size != sizeof(*out_mad));
 
        if (!port_num || port_num > ibdev->phys_port_cnt || !in_wc)
                return IB_MAD_RESULT_FAILURE;
index 1d9bb115cbf60799cd3c705bc0b166bb7640e7d7..8fe54ff00580844479b16345e15e04590d316714 100644 (file)
@@ -9,3 +9,6 @@ config INFINIBAND_IPATH
        as IP-over-InfiniBand as well as with userspace applications
        (in conjunction with InfiniBand userspace access).
        For QLogic PCIe QLE based cards, use the QIB driver instead.
+
+       If you have this hardware you will need to boot with PAT disabled
+       on your x86-64 systems, use the nopat kernel parameter.
index 0416c6c0e126f3dd8728fe2518b0d1ce018ce6dc..e9dd9112e7184cd20a48e6ec6b2a58f3c9d602ed 100644 (file)
@@ -188,7 +188,7 @@ static void send_complete(unsigned long data)
 /**
  * ipath_create_cq - create a completion queue
  * @ibdev: the device this completion queue is attached to
- * @entries: the minimum size of the completion queue
+ * @attr: creation attributes
  * @context: unused by the InfiniPath driver
  * @udata: unused by the InfiniPath driver
  *
@@ -197,16 +197,21 @@ static void send_complete(unsigned long data)
  *
  * Called by ib_create_cq() in the generic verbs code.
  */
-struct ib_cq *ipath_create_cq(struct ib_device *ibdev, int entries, int comp_vector,
+struct ib_cq *ipath_create_cq(struct ib_device *ibdev,
+                             const struct ib_cq_init_attr *attr,
                              struct ib_ucontext *context,
                              struct ib_udata *udata)
 {
+       int entries = attr->cqe;
        struct ipath_ibdev *dev = to_idev(ibdev);
        struct ipath_cq *cq;
        struct ipath_cq_wc *wc;
        struct ib_cq *ret;
        u32 sz;
 
+       if (attr->flags)
+               return ERR_PTR(-EINVAL);
+
        if (entries < 1 || entries > ib_ipath_max_cqes) {
                ret = ERR_PTR(-EINVAL);
                goto done;
index bd0caedafe9955c07683b6be7bbb2bdd346a704c..2d7e503d13cb5b9c2855936ce162f41d49ca0ced 100644 (file)
@@ -42,6 +42,9 @@
 #include <linux/bitmap.h>
 #include <linux/slab.h>
 #include <linux/module.h>
+#ifdef CONFIG_X86_64
+#include <asm/pat.h>
+#endif
 
 #include "ipath_kernel.h"
 #include "ipath_verbs.h"
@@ -395,6 +398,14 @@ static int ipath_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
        unsigned long long addr;
        u32 bar0 = 0, bar1 = 0;
 
+#ifdef CONFIG_X86_64
+       if (WARN(pat_enabled(),
+                "ipath needs PAT disabled, boot with nopat kernel parameter\n")) {
+               ret = -ENODEV;
+               goto bail;
+       }
+#endif
+
        dd = ipath_alloc_devdata(pdev);
        if (IS_ERR(dd)) {
                ret = PTR_ERR(dd);
@@ -542,6 +553,7 @@ static int ipath_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
        dd->ipath_kregbase = __ioremap(addr, len,
                (_PAGE_NO_CACHE|_PAGE_WRITETHRU));
 #else
+       /* XXX: split this properly to enable on PAT */
        dd->ipath_kregbase = ioremap_nocache(addr, len);
 #endif
 
@@ -587,12 +599,8 @@ static int ipath_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
 
        ret = ipath_enable_wc(dd);
 
-       if (ret) {
-               ipath_dev_err(dd, "Write combining not enabled "
-                             "(err %d): performance may be poor\n",
-                             -ret);
+       if (ret)
                ret = 0;
-       }
 
        ipath_verify_pioperf(dd);
 
index e08db7020cd4939809dd456882ff3d4e69480480..f0f9471227793f99f386555100ea0254ff39a1f9 100644 (file)
@@ -463,9 +463,7 @@ struct ipath_devdata {
        /* offset in HT config space of slave/primary interface block */
        u8 ipath_ht_slave_off;
        /* for write combining settings */
-       unsigned long ipath_wc_cookie;
-       unsigned long ipath_wc_base;
-       unsigned long ipath_wc_len;
+       int wc_cookie;
        /* ref count for each pkey */
        atomic_t ipath_pkeyrefs[4];
        /* shadow copy of struct page *'s for exp tid pages */
index e890e5ba0e011b550d98442c192477fea19d4a34..948188e37f95ab3fc2dfb2dd4ab7a1698c0ae84f 100644 (file)
@@ -1257,7 +1257,7 @@ static int recv_pma_set_portcounters_ext(struct ib_pma_mad *pmp,
 }
 
 static int process_subn(struct ib_device *ibdev, int mad_flags,
-                       u8 port_num, struct ib_mad *in_mad,
+                       u8 port_num, const struct ib_mad *in_mad,
                        struct ib_mad *out_mad)
 {
        struct ib_smp *smp = (struct ib_smp *)out_mad;
@@ -1389,7 +1389,7 @@ bail:
 }
 
 static int process_perf(struct ib_device *ibdev, u8 port_num,
-                       struct ib_mad *in_mad,
+                       const struct ib_mad *in_mad,
                        struct ib_mad *out_mad)
 {
        struct ib_pma_mad *pmp = (struct ib_pma_mad *)out_mad;
@@ -1490,10 +1490,17 @@ bail:
  * This is called by the ib_mad module.
  */
 int ipath_process_mad(struct ib_device *ibdev, int mad_flags, u8 port_num,
-                     struct ib_wc *in_wc, struct ib_grh *in_grh,
-                     struct ib_mad *in_mad, struct ib_mad *out_mad)
+                     const struct ib_wc *in_wc, const struct ib_grh *in_grh,
+                     const struct ib_mad_hdr *in, size_t in_mad_size,
+                     struct ib_mad_hdr *out, size_t *out_mad_size,
+                     u16 *out_mad_pkey_index)
 {
        int ret;
+       const struct ib_mad *in_mad = (const struct ib_mad *)in;
+       struct ib_mad *out_mad = (struct ib_mad *)out;
+
+       BUG_ON(in_mad_size != sizeof(*in_mad) ||
+              *out_mad_size != sizeof(*out_mad));
 
        switch (in_mad->mad_hdr.mgmt_class) {
        case IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE:
index 44ea9390417ceb0a572058ff9097be0d3871fe8e..48253b839a6f741535c11a93cce685e2cce37901 100644 (file)
@@ -1495,11 +1495,14 @@ bail:
        return 0;
 }
 
-static int ipath_query_device(struct ib_device *ibdev,
-                             struct ib_device_attr *props)
+static int ipath_query_device(struct ib_device *ibdev, struct ib_device_attr *props,
+                             struct ib_udata *uhw)
 {
        struct ipath_ibdev *dev = to_idev(ibdev);
 
+       if (uhw->inlen || uhw->outlen)
+               return -EINVAL;
+
        memset(props, 0, sizeof(*props));
 
        props->device_cap_flags = IB_DEVICE_BAD_PKEY_CNTR |
@@ -1980,6 +1983,24 @@ static int disable_timer(struct ipath_devdata *dd)
        return 0;
 }
 
+static int ipath_port_immutable(struct ib_device *ibdev, u8 port_num,
+                               struct ib_port_immutable *immutable)
+{
+       struct ib_port_attr attr;
+       int err;
+
+       err = ipath_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_IB;
+       immutable->max_mad_size = IB_MGMT_MAD_SIZE;
+
+       return 0;
+}
+
 /**
  * ipath_register_ib_device - register our device with the infiniband core
  * @dd: the device data structure
@@ -2179,6 +2200,7 @@ int ipath_register_ib_device(struct ipath_devdata *dd)
        dev->process_mad = ipath_process_mad;
        dev->mmap = ipath_mmap;
        dev->dma_ops = &ipath_dma_mapping_ops;
+       dev->get_port_immutable = ipath_port_immutable;
 
        snprintf(dev->node_desc, sizeof(dev->node_desc),
                 IPATH_IDSTR " %s", init_utsname()->nodename);
index ae6cff4abffce9550d74a71c7097fd15bb7b9abc..ec167e545e15c3df3d9d61dbad49d7152bbe3846 100644 (file)
@@ -701,9 +701,11 @@ static inline void ipath_schedule_send(struct ipath_qp *qp)
 int ipath_process_mad(struct ib_device *ibdev,
                      int mad_flags,
                      u8 port_num,
-                     struct ib_wc *in_wc,
-                     struct ib_grh *in_grh,
-                     struct ib_mad *in_mad, struct ib_mad *out_mad);
+                     const struct ib_wc *in_wc,
+                     const struct ib_grh *in_grh,
+                     const struct ib_mad_hdr *in, size_t in_mad_size,
+                     struct ib_mad_hdr *out, size_t *out_mad_size,
+                     u16 *out_mad_pkey_index);
 
 /*
  * Compare the lower 24 bits of the two values.
@@ -807,7 +809,8 @@ void ipath_cq_enter(struct ipath_cq *cq, struct ib_wc *entry, int sig);
 
 int ipath_poll_cq(struct ib_cq *ibcq, int num_entries, struct ib_wc *entry);
 
-struct ib_cq *ipath_create_cq(struct ib_device *ibdev, int entries, int comp_vector,
+struct ib_cq *ipath_create_cq(struct ib_device *ibdev,
+                             const struct ib_cq_init_attr *attr,
                              struct ib_ucontext *context,
                              struct ib_udata *udata);
 
index 4ad0b932df1fab1c1897f144db9ffc8af35c5f73..7b6e4c843e19dce46ff287c416422b60378632ac 100644 (file)
@@ -37,7 +37,6 @@
  */
 
 #include <linux/pci.h>
-#include <asm/mtrr.h>
 #include <asm/processor.h>
 
 #include "ipath_kernel.h"
@@ -122,27 +121,14 @@ int ipath_enable_wc(struct ipath_devdata *dd)
        }
 
        if (!ret) {
-               int cookie;
-               ipath_cdbg(VERBOSE, "Setting mtrr for chip to WC "
-                          "(addr %llx, len=0x%llx)\n",
-                          (unsigned long long) pioaddr,
-                          (unsigned long long) piolen);
-               cookie = mtrr_add(pioaddr, piolen, MTRR_TYPE_WRCOMB, 0);
-               if (cookie < 0) {
-                       {
-                               dev_info(&dd->pcidev->dev,
-                                        "mtrr_add()  WC for PIO bufs "
-                                        "failed (%d)\n",
-                                        cookie);
-                               ret = -EINVAL;
-                       }
-               } else {
-                       ipath_cdbg(VERBOSE, "Set mtrr for chip to WC, "
-                                  "cookie is %d\n", cookie);
-                       dd->ipath_wc_cookie = cookie;
-                       dd->ipath_wc_base = (unsigned long) pioaddr;
-                       dd->ipath_wc_len = (unsigned long) piolen;
-               }
+               dd->wc_cookie = arch_phys_wc_add(pioaddr, piolen);
+               if (dd->wc_cookie < 0) {
+                       ipath_dev_err(dd, "Seting mtrr failed on PIO buffers\n");
+                       ret = -ENODEV;
+               } else if (dd->wc_cookie == 0)
+                       ipath_cdbg(VERBOSE, "Set mtrr for chip to WC not needed\n");
+               else
+                       ipath_cdbg(VERBOSE, "Set mtrr for chip to WC\n");
        }
 
        return ret;
@@ -154,16 +140,5 @@ int ipath_enable_wc(struct ipath_devdata *dd)
  */
 void ipath_disable_wc(struct ipath_devdata *dd)
 {
-       if (dd->ipath_wc_cookie) {
-               int r;
-               ipath_cdbg(VERBOSE, "undoing WCCOMB on pio buffers\n");
-               r = mtrr_del(dd->ipath_wc_cookie, dd->ipath_wc_base,
-                            dd->ipath_wc_len);
-               if (r < 0)
-                       dev_info(&dd->pcidev->dev,
-                                "mtrr_del(%lx, %lx, %lx) failed: %d\n",
-                                dd->ipath_wc_cookie, dd->ipath_wc_base,
-                                dd->ipath_wc_len, r);
-               dd->ipath_wc_cookie = 0; /* even on failure */
-       }
+       arch_phys_wc_del(dd->wc_cookie);
 }
index 0176caa5792c4576276470c2c3f86f0fca16a7bd..36eb3d012b6d34ac96823cb193001afb42ae95ce 100644 (file)
@@ -166,10 +166,14 @@ err_buf:
        return err;
 }
 
-struct ib_cq *mlx4_ib_create_cq(struct ib_device *ibdev, int entries, int vector,
+#define CQ_CREATE_FLAGS_SUPPORTED IB_CQ_FLAGS_TIMESTAMP_COMPLETION
+struct ib_cq *mlx4_ib_create_cq(struct ib_device *ibdev,
+                               const struct ib_cq_init_attr *attr,
                                struct ib_ucontext *context,
                                struct ib_udata *udata)
 {
+       int entries = attr->cqe;
+       int vector = attr->comp_vector;
        struct mlx4_ib_dev *dev = to_mdev(ibdev);
        struct mlx4_ib_cq *cq;
        struct mlx4_uar *uar;
@@ -178,6 +182,9 @@ struct ib_cq *mlx4_ib_create_cq(struct ib_device *ibdev, int entries, int vector
        if (entries < 1 || entries > dev->dev->caps.max_cqes)
                return ERR_PTR(-EINVAL);
 
+       if (attr->flags & ~CQ_CREATE_FLAGS_SUPPORTED)
+               return ERR_PTR(-EINVAL);
+
        cq = kmalloc(sizeof *cq, GFP_KERNEL);
        if (!cq)
                return ERR_PTR(-ENOMEM);
@@ -188,6 +195,7 @@ struct ib_cq *mlx4_ib_create_cq(struct ib_device *ibdev, int entries, int vector
        spin_lock_init(&cq->lock);
        cq->resize_buf = NULL;
        cq->resize_umem = NULL;
+       cq->create_flags = attr->flags;
        INIT_LIST_HEAD(&cq->send_qp_list);
        INIT_LIST_HEAD(&cq->recv_qp_list);
 
@@ -231,7 +239,8 @@ struct ib_cq *mlx4_ib_create_cq(struct ib_device *ibdev, int entries, int vector
                vector = dev->eq_table[vector % ibdev->num_comp_vectors];
 
        err = mlx4_cq_alloc(dev->dev, entries, &cq->buf.mtt, uar,
-                           cq->db.dma, &cq->mcq, vector, 0, 0);
+                           cq->db.dma, &cq->mcq, vector, 0,
+                           !!(cq->create_flags & IB_CQ_FLAGS_TIMESTAMP_COMPLETION));
        if (err)
                goto err_dbmap;
 
index 9cd2b002d7ae57fb4f33944cbaa14a3e2a27dedd..3e2dee46caa27163644338ab214c59e966d4cf27 100644 (file)
@@ -111,8 +111,9 @@ __be64 mlx4_ib_get_new_demux_tid(struct mlx4_ib_demux_ctx *ctx)
 }
 
 int mlx4_MAD_IFC(struct mlx4_ib_dev *dev, int mad_ifc_flags,
-                int port, struct ib_wc *in_wc, struct ib_grh *in_grh,
-                void *in_mad, void *response_mad)
+                int port, const struct ib_wc *in_wc,
+                const struct ib_grh *in_grh,
+                const void *in_mad, void *response_mad)
 {
        struct mlx4_cmd_mailbox *inmailbox, *outmailbox;
        void *inbox;
@@ -220,7 +221,7 @@ static void update_sm_ah(struct mlx4_ib_dev *dev, u8 port_num, u16 lid, u8 sl)
  * Snoop SM MADs for port info, GUID info, and  P_Key table sets, so we can
  * synthesize LID change, Client-Rereg, GID change, and P_Key change events.
  */
-static void smp_snoop(struct ib_device *ibdev, u8 port_num, struct ib_mad *mad,
+static void smp_snoop(struct ib_device *ibdev, u8 port_num, const struct ib_mad *mad,
                      u16 prev_lid)
 {
        struct ib_port_info *pinfo;
@@ -356,7 +357,7 @@ static void node_desc_override(struct ib_device *dev,
        }
 }
 
-static void forward_trap(struct mlx4_ib_dev *dev, u8 port_num, struct ib_mad *mad)
+static void forward_trap(struct mlx4_ib_dev *dev, u8 port_num, const struct ib_mad *mad)
 {
        int qpn = mad->mad_hdr.mgmt_class != IB_MGMT_CLASS_SUBN_LID_ROUTED;
        struct ib_mad_send_buf *send_buf;
@@ -366,7 +367,8 @@ static void forward_trap(struct mlx4_ib_dev *dev, u8 port_num, struct ib_mad *ma
 
        if (agent) {
                send_buf = ib_create_send_mad(agent, qpn, 0, 0, IB_MGMT_MAD_HDR,
-                                             IB_MGMT_MAD_DATA, GFP_ATOMIC);
+                                             IB_MGMT_MAD_DATA, GFP_ATOMIC,
+                                             IB_MGMT_BASE_VERSION);
                if (IS_ERR(send_buf))
                        return;
                /*
@@ -722,8 +724,8 @@ static int mlx4_ib_demux_mad(struct ib_device *ibdev, u8 port,
 }
 
 static int ib_process_mad(struct ib_device *ibdev, int mad_flags, u8 port_num,
-                       struct ib_wc *in_wc, struct ib_grh *in_grh,
-                       struct ib_mad *in_mad, struct ib_mad *out_mad)
+                       const struct ib_wc *in_wc, const struct ib_grh *in_grh,
+                       const struct ib_mad *in_mad, struct ib_mad *out_mad)
 {
        u16 slid, prev_lid = 0;
        int err;
@@ -825,8 +827,8 @@ static void edit_counter(struct mlx4_counter *cnt,
 }
 
 static int iboe_process_mad(struct ib_device *ibdev, int mad_flags, u8 port_num,
-                       struct ib_wc *in_wc, struct ib_grh *in_grh,
-                       struct ib_mad *in_mad, struct ib_mad *out_mad)
+                       const struct ib_wc *in_wc, const struct ib_grh *in_grh,
+                       const struct ib_mad *in_mad, struct ib_mad *out_mad)
 {
        struct mlx4_cmd_mailbox *mailbox;
        struct mlx4_ib_dev *dev = to_mdev(ibdev);
@@ -866,9 +868,17 @@ static int iboe_process_mad(struct ib_device *ibdev, int mad_flags, u8 port_num,
 }
 
 int mlx4_ib_process_mad(struct ib_device *ibdev, int mad_flags, u8 port_num,
-                       struct ib_wc *in_wc, struct ib_grh *in_grh,
-                       struct ib_mad *in_mad, struct ib_mad *out_mad)
+                       const struct ib_wc *in_wc, const struct ib_grh *in_grh,
+                       const struct ib_mad_hdr *in, size_t in_mad_size,
+                       struct ib_mad_hdr *out, size_t *out_mad_size,
+                       u16 *out_mad_pkey_index)
 {
+       const struct ib_mad *in_mad = (const struct ib_mad *)in;
+       struct ib_mad *out_mad = (struct ib_mad *)out;
+
+       BUG_ON(in_mad_size != sizeof(*in_mad) ||
+              *out_mad_size != sizeof(*out_mad));
+
        switch (rdma_port_get_link_layer(ibdev, port_num)) {
        case IB_LINK_LAYER_INFINIBAND:
                return ib_process_mad(ibdev, mad_flags, port_num, in_wc,
@@ -1773,6 +1783,7 @@ static int create_pv_resources(struct ib_device *ibdev, int slave, int port,
                               int create_tun, struct mlx4_ib_demux_pv_ctx *ctx)
 {
        int ret, cq_size;
+       struct ib_cq_init_attr cq_attr = {};
 
        if (ctx->state != DEMUX_PV_STATE_DOWN)
                return -EEXIST;
@@ -1801,8 +1812,9 @@ static int create_pv_resources(struct ib_device *ibdev, int slave, int port,
        if (ctx->has_smi)
                cq_size *= 2;
 
+       cq_attr.cqe = cq_size;
        ctx->cq = ib_create_cq(ctx->ib_dev, mlx4_ib_tunnel_comp_handler,
-                              NULL, ctx, cq_size, 0);
+                              NULL, ctx, &cq_attr);
        if (IS_ERR(ctx->cq)) {
                ret = PTR_ERR(ctx->cq);
                pr_err("Couldn't create tunnel CQ (%d)\n", ret);
index cc64400d41ace3005c8a878b4c6811b0506726f9..166da787780c9b19f23fb5355b0dec273020cab6 100644 (file)
@@ -132,14 +132,35 @@ static int num_ib_ports(struct mlx4_dev *dev)
 }
 
 static int mlx4_ib_query_device(struct ib_device *ibdev,
-                               struct ib_device_attr *props)
+                               struct ib_device_attr *props,
+                               struct ib_udata *uhw)
 {
        struct mlx4_ib_dev *dev = to_mdev(ibdev);
        struct ib_smp *in_mad  = NULL;
        struct ib_smp *out_mad = NULL;
        int err = -ENOMEM;
        int have_ib_ports;
+       struct mlx4_uverbs_ex_query_device cmd;
+       struct mlx4_uverbs_ex_query_device_resp resp = {.comp_mask = 0};
+       struct mlx4_clock_params clock_params;
 
+       if (uhw->inlen) {
+               if (uhw->inlen < sizeof(cmd))
+                       return -EINVAL;
+
+               err = ib_copy_from_udata(&cmd, uhw, sizeof(cmd));
+               if (err)
+                       return err;
+
+               if (cmd.comp_mask)
+                       return -EINVAL;
+
+               if (cmd.reserved)
+                       return -EINVAL;
+       }
+
+       resp.response_length = offsetof(typeof(resp), response_length) +
+               sizeof(resp.response_length);
        in_mad  = kzalloc(sizeof *in_mad, GFP_KERNEL);
        out_mad = kmalloc(sizeof *out_mad, GFP_KERNEL);
        if (!in_mad || !out_mad)
@@ -229,7 +250,24 @@ static int mlx4_ib_query_device(struct ib_device *ibdev,
        props->max_total_mcast_qp_attach = props->max_mcast_qp_attach *
                                           props->max_mcast_grp;
        props->max_map_per_fmr = dev->dev->caps.max_fmr_maps;
+       props->hca_core_clock = dev->dev->caps.hca_core_clock * 1000UL;
+       props->timestamp_mask = 0xFFFFFFFFFFFFULL;
+
+       err = mlx4_get_internal_clock_params(dev->dev, &clock_params);
+       if (err)
+               goto out;
+
+       if (uhw->outlen >= resp.response_length + sizeof(resp.hca_core_clock_offset)) {
+               resp.hca_core_clock_offset = clock_params.offset % PAGE_SIZE;
+               resp.response_length += sizeof(resp.hca_core_clock_offset);
+               resp.comp_mask |= QUERY_DEVICE_RESP_MASK_TIMESTAMP;
+       }
 
+       if (uhw->outlen) {
+               err = ib_copy_to_udata(uhw, &resp, resp.response_length);
+               if (err)
+                       goto out;
+       }
 out:
        kfree(in_mad);
        kfree(out_mad);
@@ -712,8 +750,24 @@ static int mlx4_ib_mmap(struct ib_ucontext *context, struct vm_area_struct *vma)
                                       dev->dev->caps.num_uars,
                                       PAGE_SIZE, vma->vm_page_prot))
                        return -EAGAIN;
-       } else
+       } else if (vma->vm_pgoff == 3) {
+               struct mlx4_clock_params params;
+               int ret = mlx4_get_internal_clock_params(dev->dev, &params);
+
+               if (ret)
+                       return ret;
+
+               vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+               if (io_remap_pfn_range(vma, vma->vm_start,
+                                      (pci_resource_start(dev->dev->persist->pdev,
+                                                          params.bar) +
+                                       params.offset)
+                                      >> PAGE_SHIFT,
+                                      PAGE_SIZE, vma->vm_page_prot))
+                       return -EAGAIN;
+       } else {
                return -EINVAL;
+       }
 
        return 0;
 }
@@ -758,6 +812,7 @@ static struct ib_xrcd *mlx4_ib_alloc_xrcd(struct ib_device *ibdev,
                                          struct ib_udata *udata)
 {
        struct mlx4_ib_xrcd *xrcd;
+       struct ib_cq_init_attr cq_attr = {};
        int err;
 
        if (!(to_mdev(ibdev)->dev->caps.flags & MLX4_DEV_CAP_FLAG_XRC))
@@ -777,7 +832,8 @@ static struct ib_xrcd *mlx4_ib_alloc_xrcd(struct ib_device *ibdev,
                goto err2;
        }
 
-       xrcd->cq = ib_create_cq(ibdev, NULL, NULL, xrcd, 1, 0);
+       cq_attr.cqe = 1;
+       xrcd->cq = ib_create_cq(ibdev, NULL, NULL, xrcd, &cq_attr);
        if (IS_ERR(xrcd->cq)) {
                err = PTR_ERR(xrcd->cq);
                goto err3;
@@ -1185,7 +1241,6 @@ static struct ib_flow *mlx4_ib_create_flow(struct ib_qp *qp,
                                            &mflow->reg_id[i].id);
                if (err)
                        goto err_create_flow;
-               i++;
                if (is_bonded) {
                        /* Application always sees one port so the mirror rule
                         * must be on port #2
@@ -1200,6 +1255,7 @@ static struct ib_flow *mlx4_ib_create_flow(struct ib_qp *qp,
                        j++;
                }
 
+               i++;
        }
 
        if (i < ARRAY_SIZE(type) && flow_attr->type == IB_FLOW_ATTR_NORMAL) {
@@ -1207,7 +1263,7 @@ static struct ib_flow *mlx4_ib_create_flow(struct ib_qp *qp,
                                               &mflow->reg_id[i].id);
                if (err)
                        goto err_create_flow;
-               i++;
+
                if (is_bonded) {
                        flow_attr->port = 2;
                        err = mlx4_ib_tunnel_steer_add(qp, flow_attr,
@@ -1218,6 +1274,7 @@ static struct ib_flow *mlx4_ib_create_flow(struct ib_qp *qp,
                        j++;
                }
                /* function to create mirror rule */
+               i++;
        }
 
        return &mflow->ibflow;
@@ -2114,6 +2171,29 @@ static void mlx4_ib_free_eqs(struct mlx4_dev *dev, struct mlx4_ib_dev *ibdev)
        kfree(ibdev->eq_table);
 }
 
+static int mlx4_port_immutable(struct ib_device *ibdev, u8 port_num,
+                              struct ib_port_immutable *immutable)
+{
+       struct ib_port_attr attr;
+       int err;
+
+       err = mlx4_ib_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;
+
+       if (mlx4_ib_port_link_layer(ibdev, port_num) == IB_LINK_LAYER_INFINIBAND)
+               immutable->core_cap_flags = RDMA_CORE_PORT_IBA_IB;
+       else
+               immutable->core_cap_flags = RDMA_CORE_PORT_IBA_ROCE;
+
+       immutable->max_mad_size = IB_MGMT_MAD_SIZE;
+
+       return 0;
+}
+
 static void *mlx4_ib_add(struct mlx4_dev *dev)
 {
        struct mlx4_ib_dev *ibdev;
@@ -2241,6 +2321,7 @@ static void *mlx4_ib_add(struct mlx4_dev *dev)
        ibdev->ib_dev.attach_mcast      = mlx4_ib_mcg_attach;
        ibdev->ib_dev.detach_mcast      = mlx4_ib_mcg_detach;
        ibdev->ib_dev.process_mad       = mlx4_ib_process_mad;
+       ibdev->ib_dev.get_port_immutable = mlx4_port_immutable;
 
        if (!mlx4_is_slave(ibdev->dev)) {
                ibdev->ib_dev.alloc_fmr         = mlx4_ib_fmr_alloc;
@@ -2278,6 +2359,10 @@ static void *mlx4_ib_add(struct mlx4_dev *dev)
                        (1ull << IB_USER_VERBS_EX_CMD_DESTROY_FLOW);
        }
 
+       ibdev->ib_dev.uverbs_ex_cmd_mask |=
+               (1ull << IB_USER_VERBS_EX_CMD_QUERY_DEVICE) |
+               (1ull << IB_USER_VERBS_EX_CMD_CREATE_CQ);
+
        mlx4_ib_alloc_eqs(dev, ibdev);
 
        spin_lock_init(&iboe->lock);
index fce3934372a161680e4e4f2dd9716963e1178790..7933adfff662c7f7c8f3a970bf6760b58445781c 100644 (file)
@@ -110,6 +110,7 @@ struct mlx4_ib_cq {
        struct mutex            resize_mutex;
        struct ib_umem         *umem;
        struct ib_umem         *resize_umem;
+       int                     create_flags;
        /* List of qps that it serves.*/
        struct list_head                send_qp_list;
        struct list_head                recv_qp_list;
@@ -555,6 +556,21 @@ struct mlx4_ib_qp_tunnel_init_attr {
        u8 port;
 };
 
+struct mlx4_uverbs_ex_query_device {
+       __u32 comp_mask;
+       __u32 reserved;
+};
+
+enum query_device_resp_mask {
+       QUERY_DEVICE_RESP_MASK_TIMESTAMP = 1UL << 0,
+};
+
+struct mlx4_uverbs_ex_query_device_resp {
+       __u32 comp_mask;
+       __u32 response_length;
+       __u64 hca_core_clock_offset;
+};
+
 static inline struct mlx4_ib_dev *to_mdev(struct ib_device *ibdev)
 {
        return container_of(ibdev, struct mlx4_ib_dev, ib_dev);
@@ -668,7 +684,8 @@ void mlx4_ib_free_fast_reg_page_list(struct ib_fast_reg_page_list *page_list);
 
 int mlx4_ib_modify_cq(struct ib_cq *cq, u16 cq_count, u16 cq_period);
 int mlx4_ib_resize_cq(struct ib_cq *ibcq, int entries, struct ib_udata *udata);
-struct ib_cq *mlx4_ib_create_cq(struct ib_device *ibdev, int entries, int vector,
+struct ib_cq *mlx4_ib_create_cq(struct ib_device *ibdev,
+                               const struct ib_cq_init_attr *attr,
                                struct ib_ucontext *context,
                                struct ib_udata *udata);
 int mlx4_ib_destroy_cq(struct ib_cq *cq);
@@ -706,11 +723,13 @@ int mlx4_ib_post_recv(struct ib_qp *ibqp, struct ib_recv_wr *wr,
                      struct ib_recv_wr **bad_wr);
 
 int mlx4_MAD_IFC(struct mlx4_ib_dev *dev, int mad_ifc_flags,
-                int port, struct ib_wc *in_wc, struct ib_grh *in_grh,
-                void *in_mad, void *response_mad);
+                int port, const struct ib_wc *in_wc, const struct ib_grh *in_grh,
+                const void *in_mad, void *response_mad);
 int mlx4_ib_process_mad(struct ib_device *ibdev, int mad_flags,        u8 port_num,
-                       struct ib_wc *in_wc, struct ib_grh *in_grh,
-                       struct ib_mad *in_mad, struct ib_mad *out_mad);
+                       const struct ib_wc *in_wc, const struct ib_grh *in_grh,
+                       const struct ib_mad_hdr *in, size_t in_mad_size,
+                       struct ib_mad_hdr *out, size_t *out_mad_size,
+                       u16 *out_mad_pkey_index);
 int mlx4_ib_mad_init(struct mlx4_ib_dev *dev);
 void mlx4_ib_mad_cleanup(struct mlx4_ib_dev *dev);
 
index 2ee6b105197544abb2799e552b129d37eff53906..09fbae618d35d7c486beb63066bae3abdd3873dc 100644 (file)
@@ -736,10 +736,13 @@ static void destroy_cq_kernel(struct mlx5_ib_dev *dev, struct mlx5_ib_cq *cq)
        mlx5_db_free(dev->mdev, &cq->db);
 }
 
-struct ib_cq *mlx5_ib_create_cq(struct ib_device *ibdev, int entries,
-                               int vector, struct ib_ucontext *context,
+struct ib_cq *mlx5_ib_create_cq(struct ib_device *ibdev,
+                               const struct ib_cq_init_attr *attr,
+                               struct ib_ucontext *context,
                                struct ib_udata *udata)
 {
+       int entries = attr->cqe;
+       int vector = attr->comp_vector;
        struct mlx5_create_cq_mbox_in *cqb = NULL;
        struct mlx5_ib_dev *dev = to_mdev(ibdev);
        struct mlx5_ib_cq *cq;
@@ -750,6 +753,9 @@ struct ib_cq *mlx5_ib_create_cq(struct ib_device *ibdev, int entries,
        int eqn;
        int err;
 
+       if (attr->flags)
+               return ERR_PTR(-EINVAL);
+
        if (entries < 0)
                return ERR_PTR(-EINVAL);
 
index 9cf9a37bb5ff9360303a0ea9197869b5fcfbaefc..8e45714fa369832ae9a42d81b76f19d18b2fd846 100644 (file)
@@ -41,8 +41,8 @@ enum {
 };
 
 int mlx5_MAD_IFC(struct mlx5_ib_dev *dev, int ignore_mkey, int ignore_bkey,
-                u8 port, struct ib_wc *in_wc, struct ib_grh *in_grh,
-                void *in_mad, void *response_mad)
+                u8 port, const struct ib_wc *in_wc, const struct ib_grh *in_grh,
+                const void *in_mad, void *response_mad)
 {
        u8 op_modifier = 0;
 
@@ -58,11 +58,18 @@ int mlx5_MAD_IFC(struct mlx5_ib_dev *dev, int ignore_mkey, int ignore_bkey,
 }
 
 int mlx5_ib_process_mad(struct ib_device *ibdev, int mad_flags, u8 port_num,
-                       struct ib_wc *in_wc, struct ib_grh *in_grh,
-                       struct ib_mad *in_mad, struct ib_mad *out_mad)
+                       const struct ib_wc *in_wc, const struct ib_grh *in_grh,
+                       const struct ib_mad_hdr *in, size_t in_mad_size,
+                       struct ib_mad_hdr *out, size_t *out_mad_size,
+                       u16 *out_mad_pkey_index)
 {
        u16 slid;
        int err;
+       const struct ib_mad *in_mad = (const struct ib_mad *)in;
+       struct ib_mad *out_mad = (struct ib_mad *)out;
+
+       BUG_ON(in_mad_size != sizeof(*in_mad) ||
+              *out_mad_size != sizeof(*out_mad));
 
        slid = in_wc ? in_wc->slid : be16_to_cpu(IB_LID_PERMISSIVE);
 
index 57c9809e8b8774e8aac47806134216ef97c46883..c6cb26e0c8669bb1138562adf818b626cf2394c3 100644 (file)
@@ -63,7 +63,8 @@ static char mlx5_version[] =
        DRIVER_VERSION " (" DRIVER_RELDATE ")\n";
 
 static int mlx5_ib_query_device(struct ib_device *ibdev,
-                               struct ib_device_attr *props)
+                               struct ib_device_attr *props,
+                               struct ib_udata *uhw)
 {
        struct mlx5_ib_dev *dev = to_mdev(ibdev);
        struct ib_smp *in_mad  = NULL;
@@ -74,6 +75,9 @@ static int mlx5_ib_query_device(struct ib_device *ibdev,
        int max_sq_sg;
        u64 flags;
 
+       if (uhw->inlen || uhw->outlen)
+               return -EINVAL;
+
        gen = &dev->mdev->caps.gen;
        in_mad  = kzalloc(sizeof(*in_mad), GFP_KERNEL);
        out_mad = kmalloc(sizeof(*out_mad), GFP_KERNEL);
@@ -910,6 +914,7 @@ static int get_port_caps(struct mlx5_ib_dev *dev)
        struct mlx5_general_caps *gen;
        int err = -ENOMEM;
        int port;
+       struct ib_udata uhw = {.inlen = 0, .outlen = 0};
 
        gen = &dev->mdev->caps.gen;
        pprops = kmalloc(sizeof(*pprops), GFP_KERNEL);
@@ -920,7 +925,7 @@ static int get_port_caps(struct mlx5_ib_dev *dev)
        if (!dprops)
                goto out;
 
-       err = mlx5_ib_query_device(&dev->ib_dev, dprops);
+       err = mlx5_ib_query_device(&dev->ib_dev, dprops, &uhw);
        if (err) {
                mlx5_ib_warn(dev, "query_device failed %d\n", err);
                goto out;
@@ -971,6 +976,7 @@ static int create_umr_res(struct mlx5_ib_dev *dev)
        struct ib_cq *cq;
        struct ib_qp *qp;
        struct ib_mr *mr;
+       struct ib_cq_init_attr cq_attr = {};
        int ret;
 
        attr = kzalloc(sizeof(*attr), GFP_KERNEL);
@@ -994,8 +1000,9 @@ static int create_umr_res(struct mlx5_ib_dev *dev)
                goto error_1;
        }
 
-       cq = ib_create_cq(&dev->ib_dev, mlx5_umr_cq_handler, NULL, NULL, 128,
-                         0);
+       cq_attr.cqe = 128;
+       cq = ib_create_cq(&dev->ib_dev, mlx5_umr_cq_handler, NULL, NULL,
+                         &cq_attr);
        if (IS_ERR(cq)) {
                mlx5_ib_dbg(dev, "Couldn't create CQ for sync UMR QP\n");
                ret = PTR_ERR(cq);
@@ -1087,6 +1094,7 @@ static int create_dev_resources(struct mlx5_ib_resources *devr)
 {
        struct ib_srq_init_attr attr;
        struct mlx5_ib_dev *dev;
+       struct ib_cq_init_attr cq_attr = {.cqe = 1};
        int ret = 0;
 
        dev = container_of(devr, struct mlx5_ib_dev, devr);
@@ -1100,7 +1108,7 @@ static int create_dev_resources(struct mlx5_ib_resources *devr)
        devr->p0->uobject = NULL;
        atomic_set(&devr->p0->usecnt, 0);
 
-       devr->c0 = mlx5_ib_create_cq(&dev->ib_dev, 1, 0, NULL, NULL);
+       devr->c0 = mlx5_ib_create_cq(&dev->ib_dev, &cq_attr, NULL, NULL);
        if (IS_ERR(devr->c0)) {
                ret = PTR_ERR(devr->c0);
                goto error1;
@@ -1182,6 +1190,24 @@ static void destroy_dev_resources(struct mlx5_ib_resources *devr)
        mlx5_ib_dealloc_pd(devr->p0);
 }
 
+static int mlx5_port_immutable(struct ib_device *ibdev, u8 port_num,
+                              struct ib_port_immutable *immutable)
+{
+       struct ib_port_attr attr;
+       int err;
+
+       err = mlx5_ib_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_IB;
+       immutable->max_mad_size = IB_MGMT_MAD_SIZE;
+
+       return 0;
+}
+
 static void *mlx5_ib_add(struct mlx5_core_dev *mdev)
 {
        struct mlx5_ib_dev *dev;
@@ -1285,6 +1311,7 @@ static void *mlx5_ib_add(struct mlx5_core_dev *mdev)
        dev->ib_dev.alloc_fast_reg_page_list = mlx5_ib_alloc_fast_reg_page_list;
        dev->ib_dev.free_fast_reg_page_list  = mlx5_ib_free_fast_reg_page_list;
        dev->ib_dev.check_mr_status     = mlx5_ib_check_mr_status;
+       dev->ib_dev.get_port_immutable  = mlx5_port_immutable;
 
        mlx5_ib_internal_query_odp_caps(dev);
 
index dff1cfcdf476cfed06d8835cd5316d234df09e1e..178314e764dab8ac6a6a3200b2a79fc6dbc63075 100644 (file)
@@ -525,8 +525,8 @@ void __mlx5_ib_cq_clean(struct mlx5_ib_cq *cq, u32 qpn, struct mlx5_ib_srq *srq)
 void mlx5_ib_cq_clean(struct mlx5_ib_cq *cq, u32 qpn, struct mlx5_ib_srq *srq);
 void mlx5_ib_free_srq_wqe(struct mlx5_ib_srq *srq, int wqe_index);
 int mlx5_MAD_IFC(struct mlx5_ib_dev *dev, int ignore_mkey, int ignore_bkey,
-                u8 port, struct ib_wc *in_wc, struct ib_grh *in_grh,
-                void *in_mad, void *response_mad);
+                u8 port, const struct ib_wc *in_wc, const struct ib_grh *in_grh,
+                const void *in_mad, void *response_mad);
 struct ib_ah *create_ib_ah(struct ib_ah_attr *ah_attr,
                           struct mlx5_ib_ah *ah);
 struct ib_ah *mlx5_ib_create_ah(struct ib_pd *pd, struct ib_ah_attr *ah_attr);
@@ -556,8 +556,9 @@ int mlx5_ib_post_recv(struct ib_qp *ibqp, struct ib_recv_wr *wr,
 void *mlx5_get_send_wqe(struct mlx5_ib_qp *qp, int n);
 int mlx5_ib_read_user_wqe(struct mlx5_ib_qp *qp, int send, int wqe_index,
                          void *buffer, u32 length);
-struct ib_cq *mlx5_ib_create_cq(struct ib_device *ibdev, int entries,
-                               int vector, struct ib_ucontext *context,
+struct ib_cq *mlx5_ib_create_cq(struct ib_device *ibdev,
+                               const struct ib_cq_init_attr *attr,
+                               struct ib_ucontext *context,
                                struct ib_udata *udata);
 int mlx5_ib_destroy_cq(struct ib_cq *cq);
 int mlx5_ib_poll_cq(struct ib_cq *ibcq, int num_entries, struct ib_wc *wc);
@@ -586,8 +587,10 @@ int mlx5_ib_map_phys_fmr(struct ib_fmr *ibfmr, u64 *page_list,
 int mlx5_ib_unmap_fmr(struct list_head *fmr_list);
 int mlx5_ib_fmr_dealloc(struct ib_fmr *ibfmr);
 int mlx5_ib_process_mad(struct ib_device *ibdev, int mad_flags, u8 port_num,
-                       struct ib_wc *in_wc, struct ib_grh *in_grh,
-                       struct ib_mad *in_mad, struct ib_mad *out_mad);
+                       const struct ib_wc *in_wc, const struct ib_grh *in_grh,
+                       const struct ib_mad_hdr *in, size_t in_mad_size,
+                       struct ib_mad_hdr *out, size_t *out_mad_size,
+                       u16 *out_mad_pkey_index);
 struct ib_xrcd *mlx5_ib_alloc_xrcd(struct ib_device *ibdev,
                                          struct ib_ucontext *context,
                                          struct ib_udata *udata);
index 9d3e5c1ac60e44ca5ba1014fa43fa1e6483694ec..c7f49bbb0c72b6d6af86eb2bb42984c5e3effb17 100644 (file)
@@ -1858,8 +1858,8 @@ int mthca_CONF_SPECIAL_QP(struct mthca_dev *dev, int type, u32 qpn)
 }
 
 int mthca_MAD_IFC(struct mthca_dev *dev, int ignore_mkey, int ignore_bkey,
-                 int port, struct ib_wc *in_wc, struct ib_grh *in_grh,
-                 void *in_mad, void *response_mad)
+                 int port, const struct ib_wc *in_wc, const struct ib_grh *in_grh,
+                 const void *in_mad, void *response_mad)
 {
        struct mthca_mailbox *inmailbox, *outmailbox;
        void *inbox;
index f952244c54de0d61c2ef8a6f70adb22218b8effb..d2e5b194b938b1afd3c30f12ddd6151ca89e1d38 100644 (file)
@@ -312,8 +312,8 @@ int mthca_QUERY_QP(struct mthca_dev *dev, u32 num, int is_ee,
                   struct mthca_mailbox *mailbox);
 int mthca_CONF_SPECIAL_QP(struct mthca_dev *dev, int type, u32 qpn);
 int mthca_MAD_IFC(struct mthca_dev *dev, int ignore_mkey, int ignore_bkey,
-                 int port, struct ib_wc *in_wc, struct ib_grh *in_grh,
-                 void *in_mad, void *response_mad);
+                 int port, const struct ib_wc *in_wc, const struct ib_grh *in_grh,
+                 const void *in_mad, void *response_mad);
 int mthca_READ_MGM(struct mthca_dev *dev, int index,
                   struct mthca_mailbox *mailbox);
 int mthca_WRITE_MGM(struct mthca_dev *dev, int index,
index 7e6a6d64ad4eb1bee96b0d2d244daf20898ab3b6..4393a022867badb9b5631f8e0110c4de629badbb 100644 (file)
@@ -576,10 +576,11 @@ int mthca_multicast_detach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid);
 int mthca_process_mad(struct ib_device *ibdev,
                      int mad_flags,
                      u8 port_num,
-                     struct ib_wc *in_wc,
-                     struct ib_grh *in_grh,
-                     struct ib_mad *in_mad,
-                     struct ib_mad *out_mad);
+                     const struct ib_wc *in_wc,
+                     const struct ib_grh *in_grh,
+                     const struct ib_mad_hdr *in, size_t in_mad_size,
+                     struct ib_mad_hdr *out, size_t *out_mad_size,
+                     u16 *out_mad_pkey_index);
 int mthca_create_agents(struct mthca_dev *dev);
 void mthca_free_agents(struct mthca_dev *dev);
 
index 8881fa376e06fa6e87b9bde29a1e1581727a0341..6b2418b74c99ab84345403afe9d310f04aa465e5 100644 (file)
@@ -104,7 +104,7 @@ static void update_sm_ah(struct mthca_dev *dev,
  */
 static void smp_snoop(struct ib_device *ibdev,
                      u8 port_num,
-                     struct ib_mad *mad,
+                     const struct ib_mad *mad,
                      u16 prev_lid)
 {
        struct ib_event event;
@@ -160,7 +160,7 @@ static void node_desc_override(struct ib_device *dev,
 
 static void forward_trap(struct mthca_dev *dev,
                         u8 port_num,
-                        struct ib_mad *mad)
+                        const struct ib_mad *mad)
 {
        int qpn = mad->mad_hdr.mgmt_class != IB_MGMT_CLASS_SUBN_LID_ROUTED;
        struct ib_mad_send_buf *send_buf;
@@ -170,7 +170,8 @@ static void forward_trap(struct mthca_dev *dev,
 
        if (agent) {
                send_buf = ib_create_send_mad(agent, qpn, 0, 0, IB_MGMT_MAD_HDR,
-                                             IB_MGMT_MAD_DATA, GFP_ATOMIC);
+                                             IB_MGMT_MAD_DATA, GFP_ATOMIC,
+                                             IB_MGMT_BASE_VERSION);
                if (IS_ERR(send_buf))
                        return;
                /*
@@ -195,15 +196,21 @@ static void forward_trap(struct mthca_dev *dev,
 int mthca_process_mad(struct ib_device *ibdev,
                      int mad_flags,
                      u8 port_num,
-                     struct ib_wc *in_wc,
-                     struct ib_grh *in_grh,
-                     struct ib_mad *in_mad,
-                     struct ib_mad *out_mad)
+                     const struct ib_wc *in_wc,
+                     const struct ib_grh *in_grh,
+                     const struct ib_mad_hdr *in, size_t in_mad_size,
+                     struct ib_mad_hdr *out, size_t *out_mad_size,
+                     u16 *out_mad_pkey_index)
 {
        int err;
        u16 slid = in_wc ? in_wc->slid : be16_to_cpu(IB_LID_PERMISSIVE);
        u16 prev_lid = 0;
        struct ib_port_attr pattr;
+       const struct ib_mad *in_mad = (const struct ib_mad *)in;
+       struct ib_mad *out_mad = (struct ib_mad *)out;
+
+       BUG_ON(in_mad_size != sizeof(*in_mad) ||
+              *out_mad_size != sizeof(*out_mad));
 
        /* Forward locally generated traps to the SM */
        if (in_mad->mad_hdr.method == IB_MGMT_METHOD_TRAP &&
index 8edb28a9a0e7593168f4b45d5ab9c4d3c1a8507c..15d064479ef6c5347f48ef804aa380a0c5eadb0f 100644 (file)
@@ -77,7 +77,6 @@ s64 mthca_make_profile(struct mthca_dev *dev,
        u64 mem_base, mem_avail;
        s64 total_size = 0;
        struct mthca_resource *profile;
-       struct mthca_resource tmp;
        int i, j;
 
        profile = kzalloc(MTHCA_RES_NUM * sizeof *profile, GFP_KERNEL);
@@ -136,11 +135,8 @@ s64 mthca_make_profile(struct mthca_dev *dev,
         */
        for (i = MTHCA_RES_NUM; i > 0; --i)
                for (j = 1; j < i; ++j) {
-                       if (profile[j].size > profile[j - 1].size) {
-                               tmp            = profile[j];
-                               profile[j]     = profile[j - 1];
-                               profile[j - 1] = tmp;
-                       }
+                       if (profile[j].size > profile[j - 1].size)
+                               swap(profile[j], profile[j - 1]);
                }
 
        for (i = 0; i < MTHCA_RES_NUM; ++i) {
index 415f8e1a54dbc82cf4ab81bf5ad98bd9034d0733..93ae51dcf2ffaefb715363e2706855e66f1ce41b 100644 (file)
@@ -57,14 +57,17 @@ static void init_query_mad(struct ib_smp *mad)
        mad->method        = IB_MGMT_METHOD_GET;
 }
 
-static int mthca_query_device(struct ib_device *ibdev,
-                             struct ib_device_attr *props)
+static int mthca_query_device(struct ib_device *ibdev, struct ib_device_attr *props,
+                             struct ib_udata *uhw)
 {
        struct ib_smp *in_mad  = NULL;
        struct ib_smp *out_mad = NULL;
        int err = -ENOMEM;
        struct mthca_dev *mdev = to_mdev(ibdev);
 
+       if (uhw->inlen || uhw->outlen)
+               return -EINVAL;
+
        in_mad  = kzalloc(sizeof *in_mad, GFP_KERNEL);
        out_mad = kmalloc(sizeof *out_mad, GFP_KERNEL);
        if (!in_mad || !out_mad)
@@ -641,16 +644,20 @@ static int mthca_destroy_qp(struct ib_qp *qp)
        return 0;
 }
 
-static struct ib_cq *mthca_create_cq(struct ib_device *ibdev, int entries,
-                                    int comp_vector,
+static struct ib_cq *mthca_create_cq(struct ib_device *ibdev,
+                                    const struct ib_cq_init_attr *attr,
                                     struct ib_ucontext *context,
                                     struct ib_udata *udata)
 {
+       int entries = attr->cqe;
        struct mthca_create_cq ucmd;
        struct mthca_cq *cq;
        int nent;
        int err;
 
+       if (attr->flags)
+               return ERR_PTR(-EINVAL);
+
        if (entries < 1 || entries > to_mdev(ibdev)->limits.max_cqes)
                return ERR_PTR(-EINVAL);
 
@@ -1244,6 +1251,24 @@ out:
        return err;
 }
 
+static int mthca_port_immutable(struct ib_device *ibdev, u8 port_num,
+                               struct ib_port_immutable *immutable)
+{
+       struct ib_port_attr attr;
+       int err;
+
+       err = mthca_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_IB;
+       immutable->max_mad_size = IB_MGMT_MAD_SIZE;
+
+       return 0;
+}
+
 int mthca_register_device(struct mthca_dev *dev)
 {
        int ret;
@@ -1323,6 +1348,7 @@ int mthca_register_device(struct mthca_dev *dev)
        dev->ib_dev.reg_phys_mr          = mthca_reg_phys_mr;
        dev->ib_dev.reg_user_mr          = mthca_reg_user_mr;
        dev->ib_dev.dereg_mr             = mthca_dereg_mr;
+       dev->ib_dev.get_port_immutable   = mthca_port_immutable;
 
        if (dev->mthca_flags & MTHCA_FLAG_FMR) {
                dev->ib_dev.alloc_fmr            = mthca_alloc_fmr;
index 72b43417cbe382aed9164b5554e80b449270c3ad..9047af4299065f543252a29297fbd40e603531b3 100644 (file)
@@ -1616,6 +1616,8 @@ static struct nes_cm_node *make_cm_node(struct nes_cm_core *cm_core,
                  &cm_node->loc_addr, cm_node->loc_port,
                  &cm_node->rem_addr, cm_node->rem_port);
        cm_node->listener = listener;
+       if (listener)
+               cm_node->tos = listener->tos;
        cm_node->netdev = nesvnic->netdev;
        cm_node->cm_id = cm_info->cm_id;
        memcpy(cm_node->loc_mac, nesvnic->netdev->dev_addr, ETH_ALEN);
@@ -2938,6 +2940,9 @@ static int nes_cm_init_tsa_conn(struct nes_qp *nesqp, struct nes_cm_node *cm_nod
 
        nesqp->nesqp_context->misc2 |= cpu_to_le32(64 << NES_QPCONTEXT_MISC2_TTL_SHIFT);
 
+       nesqp->nesqp_context->misc2 |= cpu_to_le32(
+               cm_node->tos << NES_QPCONTEXT_MISC2_TOS_SHIFT);
+
        nesqp->nesqp_context->mss |= cpu_to_le32(((u32)cm_node->tcp_cntxt.mss) << 16);
 
        nesqp->nesqp_context->tcp_state_flow_label |= cpu_to_le32(
@@ -3612,6 +3617,7 @@ int nes_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
                cm_node->ord_size = 1;
 
        cm_node->apbvt_set = apbvt_set;
+       cm_node->tos = cm_id->tos;
        nesqp->cm_node = cm_node;
        cm_node->nesqp = nesqp;
        nes_add_ref(&nesqp->ibqp);
@@ -3666,6 +3672,7 @@ int nes_create_listen(struct iw_cm_id *cm_id, int backlog)
        }
 
        cm_id->provider_data = cm_node;
+       cm_node->tos = cm_id->tos;
 
        if (!cm_node->reused_node) {
                if (nes_create_mapinfo(&cm_info))
index f522cf6397893c916f44c1a3bc0c297b5dde6264..32a6420c29400184ea8f392c9e7004eb51611b67 100644 (file)
@@ -303,6 +303,7 @@ struct nes_cm_listener {
        int                        backlog;
        enum nes_cm_listener_state listener_state;
        u32                        reused_node;
+       u8                         tos;
 };
 
 /* per connection node and node state information */
@@ -352,6 +353,7 @@ struct nes_cm_node {
        struct list_head        reset_entry;
        struct nes_qp           *nesqp;
        atomic_t                passive_state;
+       u8                      tos;
 };
 
 /* structure for client or CM to fill when making CM api calls. */
index c0d0296e7a003089dec6d8d180de2bb6dd05964d..fbc43e5f717b024b4c50832e7aab8a229554fc82 100644 (file)
@@ -512,12 +512,16 @@ static void nes_free_fast_reg_page_list(struct ib_fast_reg_page_list *pifrpl)
 /**
  * nes_query_device
  */
-static int nes_query_device(struct ib_device *ibdev, struct ib_device_attr *props)
+static int nes_query_device(struct ib_device *ibdev, struct ib_device_attr *props,
+                           struct ib_udata *uhw)
 {
        struct nes_vnic *nesvnic = to_nesvnic(ibdev);
        struct nes_device *nesdev = nesvnic->nesdev;
        struct nes_ib_device *nesibdev = nesvnic->nesibdev;
 
+       if (uhw->inlen || uhw->outlen)
+               return -EINVAL;
+
        memset(props, 0, sizeof(*props));
        memcpy(&props->sys_image_guid, nesvnic->netdev->dev_addr, 6);
 
@@ -606,7 +610,6 @@ static int nes_query_port(struct ib_device *ibdev, u8 port, struct ib_port_attr
        return 0;
 }
 
-
 /**
  * nes_query_pkey
  */
@@ -1527,10 +1530,12 @@ static int nes_destroy_qp(struct ib_qp *ibqp)
 /**
  * nes_create_cq
  */
-static struct ib_cq *nes_create_cq(struct ib_device *ibdev, int entries,
-               int comp_vector,
-               struct ib_ucontext *context, struct ib_udata *udata)
+static struct ib_cq *nes_create_cq(struct ib_device *ibdev,
+                                  const struct ib_cq_init_attr *attr,
+                                  struct ib_ucontext *context,
+                                  struct ib_udata *udata)
 {
+       int entries = attr->cqe;
        u64 u64temp;
        struct nes_vnic *nesvnic = to_nesvnic(ibdev);
        struct nes_device *nesdev = nesvnic->nesdev;
@@ -1550,6 +1555,9 @@ static struct ib_cq *nes_create_cq(struct ib_device *ibdev, int entries,
        unsigned long flags;
        int ret;
 
+       if (attr->flags)
+               return ERR_PTR(-EINVAL);
+
        if (entries > nesadapter->max_cqe)
                return ERR_PTR(-EINVAL);
 
@@ -3222,8 +3230,10 @@ static int nes_multicast_detach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid)
  * nes_process_mad
  */
 static int nes_process_mad(struct ib_device *ibdev, int mad_flags,
-               u8 port_num, struct ib_wc *in_wc, struct ib_grh *in_grh,
-               struct ib_mad *in_mad, struct ib_mad *out_mad)
+               u8 port_num, const struct ib_wc *in_wc, const struct ib_grh *in_grh,
+               const struct ib_mad_hdr *in, size_t in_mad_size,
+               struct ib_mad_hdr *out, size_t *out_mad_size,
+               u16 *out_mad_pkey_index)
 {
        nes_debug(NES_DBG_INIT, "\n");
        return -ENOSYS;
@@ -3828,6 +3838,22 @@ static int nes_req_notify_cq(struct ib_cq *ibcq, enum ib_cq_notify_flags notify_
        return 0;
 }
 
+static int nes_port_immutable(struct ib_device *ibdev, u8 port_num,
+                             struct ib_port_immutable *immutable)
+{
+       struct ib_port_attr attr;
+       int err;
+
+       err = nes_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_IWARP;
+
+       return 0;
+}
 
 /**
  * nes_init_ofa_device
@@ -3928,6 +3954,7 @@ struct nes_ib_device *nes_init_ofa_device(struct net_device *netdev)
        nesibdev->ibdev.iwcm->reject = nes_reject;
        nesibdev->ibdev.iwcm->create_listen = nes_create_listen;
        nesibdev->ibdev.iwcm->destroy_listen = nes_destroy_listen;
+       nesibdev->ibdev.get_port_immutable   = nes_port_immutable;
 
        return nesibdev;
 }
index f5a5ea836dbdc9fe4f12e6f26e1acca5dac3c6d1..4bafa15708d0fc4212587cb4dfd6fce7d3f3211b 100644 (file)
@@ -204,12 +204,19 @@ int ocrdma_modify_ah(struct ib_ah *ibah, struct ib_ah_attr *attr)
 int ocrdma_process_mad(struct ib_device *ibdev,
                       int process_mad_flags,
                       u8 port_num,
-                      struct ib_wc *in_wc,
-                      struct ib_grh *in_grh,
-                      struct ib_mad *in_mad, struct ib_mad *out_mad)
+                      const struct ib_wc *in_wc,
+                      const struct ib_grh *in_grh,
+                      const struct ib_mad_hdr *in, size_t in_mad_size,
+                      struct ib_mad_hdr *out, size_t *out_mad_size,
+                      u16 *out_mad_pkey_index)
 {
        int status;
        struct ocrdma_dev *dev;
+       const struct ib_mad *in_mad = (const struct ib_mad *)in;
+       struct ib_mad *out_mad = (struct ib_mad *)out;
+
+       BUG_ON(in_mad_size != sizeof(*in_mad) ||
+              *out_mad_size != sizeof(*out_mad));
 
        switch (in_mad->mad_hdr.mgmt_class) {
        case IB_MGMT_CLASS_PERF_MGMT:
index 726a87cf22dcb215d2a105f08a054395bb0c6804..cf366fe03cb822580fe96b69e5873ad255a34632 100644 (file)
@@ -42,7 +42,9 @@ int ocrdma_modify_ah(struct ib_ah *, struct ib_ah_attr *);
 int ocrdma_process_mad(struct ib_device *,
                       int process_mad_flags,
                       u8 port_num,
-                      struct ib_wc *in_wc,
-                      struct ib_grh *in_grh,
-                      struct ib_mad *in_mad, struct ib_mad *out_mad);
+                      const struct ib_wc *in_wc,
+                      const struct ib_grh *in_grh,
+                      const struct ib_mad_hdr *in, size_t in_mad_size,
+                      struct ib_mad_hdr *out, size_t *out_mad_size,
+                      u16 *out_mad_pkey_index);
 #endif                         /* __OCRDMA_AH_H__ */
index 7a2b59aca004bfac1eae4fc258fcb08d077bf449..8a1398b253a2bec42f2d0032e0aa915ce747bfd9 100644 (file)
@@ -30,6 +30,7 @@
 #include <rdma/ib_verbs.h>
 #include <rdma/ib_user_verbs.h>
 #include <rdma/ib_addr.h>
+#include <rdma/ib_mad.h>
 
 #include <linux/netdevice.h>
 #include <net/addrconf.h>
@@ -202,6 +203,24 @@ static enum rdma_link_layer ocrdma_link_layer(struct ib_device *device,
        return IB_LINK_LAYER_ETHERNET;
 }
 
+static int ocrdma_port_immutable(struct ib_device *ibdev, u8 port_num,
+                                struct ib_port_immutable *immutable)
+{
+       struct ib_port_attr attr;
+       int err;
+
+       err = ocrdma_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;
+       immutable->max_mad_size = IB_MGMT_MAD_SIZE;
+
+       return 0;
+}
+
 static int ocrdma_register_device(struct ocrdma_dev *dev)
 {
        strlcpy(dev->ibdev.name, "ocrdma%d", IB_DEVICE_NAME_MAX);
@@ -286,6 +305,7 @@ static int ocrdma_register_device(struct ocrdma_dev *dev)
        dev->ibdev.dma_device = &dev->nic_info.pdev->dev;
 
        dev->ibdev.process_mad = ocrdma_process_mad;
+       dev->ibdev.get_port_immutable = ocrdma_port_immutable;
 
        if (ocrdma_get_asic_type(dev) == OCRDMA_ASIC_GEN_SKH_R) {
                dev->ibdev.uverbs_cmd_mask |=
index 9dcb66077d6cbf9cd37bdaaa594414aadee4c96f..5bb61eb58f2c71859969d73ac6e326d4dafc51fd 100644 (file)
@@ -61,10 +61,14 @@ int ocrdma_query_gid(struct ib_device *ibdev, u8 port,
        return 0;
 }
 
-int ocrdma_query_device(struct ib_device *ibdev, struct ib_device_attr *attr)
+int ocrdma_query_device(struct ib_device *ibdev, struct ib_device_attr *attr,
+                       struct ib_udata *uhw)
 {
        struct ocrdma_dev *dev = get_ocrdma_dev(ibdev);
 
+       if (uhw->inlen || uhw->outlen)
+               return -EINVAL;
+
        memset(attr, 0, sizeof *attr);
        memcpy(&attr->fw_ver, &dev->attr.fw_ver[0],
               min(sizeof(dev->attr.fw_ver), sizeof(attr->fw_ver)));
@@ -375,7 +379,12 @@ static struct ocrdma_pd *_ocrdma_alloc_pd(struct ocrdma_dev *dev,
 
        if (dev->pd_mgr->pd_prealloc_valid) {
                status = ocrdma_get_pd_num(dev, pd);
-               return (status == 0) ? pd : ERR_PTR(status);
+               if (status == 0) {
+                       return pd;
+               } else {
+                       kfree(pd);
+                       return ERR_PTR(status);
+               }
        }
 
 retry:
@@ -679,7 +688,6 @@ err:
                ocrdma_release_ucontext_pd(uctx);
        } else {
                status = _ocrdma_dealloc_pd(dev, pd);
-               kfree(pd);
        }
 exit:
        return ERR_PTR(status);
@@ -1000,10 +1008,12 @@ err:
        return status;
 }
 
-struct ib_cq *ocrdma_create_cq(struct ib_device *ibdev, int entries, int vector,
+struct ib_cq *ocrdma_create_cq(struct ib_device *ibdev,
+                              const struct ib_cq_init_attr *attr,
                               struct ib_ucontext *ib_ctx,
                               struct ib_udata *udata)
 {
+       int entries = attr->cqe;
        struct ocrdma_cq *cq;
        struct ocrdma_dev *dev = get_ocrdma_dev(ibdev);
        struct ocrdma_ucontext *uctx = NULL;
@@ -1011,6 +1021,9 @@ struct ib_cq *ocrdma_create_cq(struct ib_device *ibdev, int entries, int vector,
        int status;
        struct ocrdma_create_cq_ureq ureq;
 
+       if (attr->flags)
+               return ERR_PTR(-EINVAL);
+
        if (udata) {
                if (ib_copy_from_udata(&ureq, udata, sizeof(ureq)))
                        return ERR_PTR(-EFAULT);
index b8f7853fd36ce7a61e53004105c2b9082af14ebb..b15c608efa7b03c72a2eb44e1f7b8af919db13b0 100644 (file)
@@ -36,11 +36,15 @@ int ocrdma_post_recv(struct ib_qp *, struct ib_recv_wr *,
 int ocrdma_poll_cq(struct ib_cq *, int num_entries, struct ib_wc *wc);
 int ocrdma_arm_cq(struct ib_cq *, enum ib_cq_notify_flags flags);
 
-int ocrdma_query_device(struct ib_device *, struct ib_device_attr *props);
+int ocrdma_query_device(struct ib_device *, struct ib_device_attr *props,
+                       struct ib_udata *uhw);
 int ocrdma_query_port(struct ib_device *, u8 port, struct ib_port_attr *props);
 int ocrdma_modify_port(struct ib_device *, u8 port, int mask,
                       struct ib_port_modify *props);
 
+enum rdma_protocol_type
+ocrdma_query_protocol(struct ib_device *device, u8 port_num);
+
 void ocrdma_get_guid(struct ocrdma_dev *, u8 *guid);
 int ocrdma_query_gid(struct ib_device *, u8 port,
                     int index, union ib_gid *gid);
@@ -56,8 +60,10 @@ struct ib_pd *ocrdma_alloc_pd(struct ib_device *,
                              struct ib_ucontext *, struct ib_udata *);
 int ocrdma_dealloc_pd(struct ib_pd *pd);
 
-struct ib_cq *ocrdma_create_cq(struct ib_device *, int entries, int vector,
-                              struct ib_ucontext *, struct ib_udata *);
+struct ib_cq *ocrdma_create_cq(struct ib_device *ibdev,
+                              const struct ib_cq_init_attr *attr,
+                              struct ib_ucontext *ib_ctx,
+                              struct ib_udata *udata);
 int ocrdma_resize_cq(struct ib_cq *, int cqe, struct ib_udata *);
 int ocrdma_destroy_cq(struct ib_cq *);
 
index ab4e11cfab15e3f5f4cb0fbfce159747e150ddda..2b45d0b023007dc7b6b14d44c7649cac2d5ac572 100644 (file)
@@ -203,7 +203,7 @@ static void send_complete(struct kthread_work *work)
 /**
  * qib_create_cq - create a completion queue
  * @ibdev: the device this completion queue is attached to
- * @entries: the minimum size of the completion queue
+ * @attr: creation attributes
  * @context: unused by the QLogic_IB driver
  * @udata: user data for libibverbs.so
  *
@@ -212,16 +212,21 @@ static void send_complete(struct kthread_work *work)
  *
  * Called by ib_create_cq() in the generic verbs code.
  */
-struct ib_cq *qib_create_cq(struct ib_device *ibdev, int entries,
-                           int comp_vector, struct ib_ucontext *context,
+struct ib_cq *qib_create_cq(struct ib_device *ibdev,
+                           const struct ib_cq_init_attr *attr,
+                           struct ib_ucontext *context,
                            struct ib_udata *udata)
 {
+       int entries = attr->cqe;
        struct qib_ibdev *dev = to_idev(ibdev);
        struct qib_cq *cq;
        struct qib_cq_wc *wc;
        struct ib_cq *ret;
        u32 sz;
 
+       if (attr->flags)
+               return ERR_PTR(-EINVAL);
+
        if (entries < 1 || entries > ib_qib_max_cqes) {
                ret = ERR_PTR(-EINVAL);
                goto done;
index f32b4628e9913e17dfd0606e1713945ff65930f2..6c8ff10101c0cac722d2da1128c85f81b376b62f 100644 (file)
@@ -5502,7 +5502,8 @@ static void try_7322_ipg(struct qib_pportdata *ppd)
                goto retry;
 
        send_buf = ib_create_send_mad(agent, 0, 0, 0, IB_MGMT_MAD_HDR,
-                                     IB_MGMT_MAD_DATA, GFP_ATOMIC);
+                                     IB_MGMT_MAD_DATA, GFP_ATOMIC,
+                                     IB_MGMT_BASE_VERSION);
        if (IS_ERR(send_buf))
                goto retry;
 
index 395f4046dba2054633ad41f4a0f67a4dbee57c63..05e3242d84425acd6229204e642084a4ce0f654d 100644 (file)
@@ -83,7 +83,8 @@ static void qib_send_trap(struct qib_ibport *ibp, void *data, unsigned len)
                return;
 
        send_buf = ib_create_send_mad(agent, 0, 0, 0, IB_MGMT_MAD_HDR,
-                                     IB_MGMT_MAD_DATA, GFP_ATOMIC);
+                                     IB_MGMT_MAD_DATA, GFP_ATOMIC,
+                                     IB_MGMT_BASE_VERSION);
        if (IS_ERR(send_buf))
                return;
 
@@ -1854,7 +1855,7 @@ static int pma_set_portcounters_ext(struct ib_pma_mad *pmp,
 }
 
 static int process_subn(struct ib_device *ibdev, int mad_flags,
-                       u8 port, struct ib_mad *in_mad,
+                       u8 port, const struct ib_mad *in_mad,
                        struct ib_mad *out_mad)
 {
        struct ib_smp *smp = (struct ib_smp *)out_mad;
@@ -2006,7 +2007,7 @@ bail:
 }
 
 static int process_perf(struct ib_device *ibdev, u8 port,
-                       struct ib_mad *in_mad,
+                       const struct ib_mad *in_mad,
                        struct ib_mad *out_mad)
 {
        struct ib_pma_mad *pmp = (struct ib_pma_mad *)out_mad;
@@ -2299,7 +2300,7 @@ static int check_cc_key(struct qib_ibport *ibp,
 }
 
 static int process_cc(struct ib_device *ibdev, int mad_flags,
-                       u8 port, struct ib_mad *in_mad,
+                       u8 port, const struct ib_mad *in_mad,
                        struct ib_mad *out_mad)
 {
        struct ib_cc_mad *ccp = (struct ib_cc_mad *)out_mad;
@@ -2400,12 +2401,19 @@ bail:
  * This is called by the ib_mad module.
  */
 int qib_process_mad(struct ib_device *ibdev, int mad_flags, u8 port,
-                   struct ib_wc *in_wc, struct ib_grh *in_grh,
-                   struct ib_mad *in_mad, struct ib_mad *out_mad)
+                   const struct ib_wc *in_wc, const struct ib_grh *in_grh,
+                   const struct ib_mad_hdr *in, size_t in_mad_size,
+                   struct ib_mad_hdr *out, size_t *out_mad_size,
+                   u16 *out_mad_pkey_index)
 {
        int ret;
        struct qib_ibport *ibp = to_iport(ibdev, port);
        struct qib_pportdata *ppd = ppd_from_ibp(ibp);
+       const struct ib_mad *in_mad = (const struct ib_mad *)in;
+       struct ib_mad *out_mad = (struct ib_mad *)out;
+
+       BUG_ON(in_mad_size != sizeof(*in_mad) ||
+              *out_mad_size != sizeof(*out_mad));
 
        switch (in_mad->mad_hdr.mgmt_class) {
        case IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE:
index 4a3599890ea5f114655a34e472bee9197d73bd2c..a05d1a372208a11f837bc10564fa51aec1fb7ab6 100644 (file)
@@ -1550,12 +1550,14 @@ full:
        }
 }
 
-static int qib_query_device(struct ib_device *ibdev,
-                           struct ib_device_attr *props)
+static int qib_query_device(struct ib_device *ibdev, struct ib_device_attr *props,
+                           struct ib_udata *uhw)
 {
        struct qib_devdata *dd = dd_from_ibdev(ibdev);
        struct qib_ibdev *dev = to_idev(ibdev);
 
+       if (uhw->inlen || uhw->outlen)
+               return -EINVAL;
        memset(props, 0, sizeof(*props));
 
        props->device_cap_flags = IB_DEVICE_BAD_PKEY_CNTR |
@@ -2040,6 +2042,24 @@ static void init_ibport(struct qib_pportdata *ppd)
        RCU_INIT_POINTER(ibp->qp1, NULL);
 }
 
+static int qib_port_immutable(struct ib_device *ibdev, u8 port_num,
+                             struct ib_port_immutable *immutable)
+{
+       struct ib_port_attr attr;
+       int err;
+
+       err = qib_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_IB;
+       immutable->max_mad_size = IB_MGMT_MAD_SIZE;
+
+       return 0;
+}
+
 /**
  * qib_register_ib_device - register our device with the infiniband core
  * @dd: the device data structure
@@ -2227,6 +2247,7 @@ int qib_register_ib_device(struct qib_devdata *dd)
        ibdev->process_mad = qib_process_mad;
        ibdev->mmap = qib_mmap;
        ibdev->dma_ops = &qib_dma_mapping_ops;
+       ibdev->get_port_immutable = qib_port_immutable;
 
        snprintf(ibdev->node_desc, sizeof(ibdev->node_desc),
                 "Intel Infiniband HCA %s", init_utsname()->nodename);
index bfc8948fdd3592589d14c611bf16e2a626dbde39..1635572752ce5bb37e05b3059dc194c196031880 100644 (file)
@@ -872,8 +872,10 @@ void qib_cap_mask_chg(struct qib_ibport *ibp);
 void qib_sys_guid_chg(struct qib_ibport *ibp);
 void qib_node_desc_chg(struct qib_ibport *ibp);
 int qib_process_mad(struct ib_device *ibdev, int mad_flags, u8 port_num,
-                   struct ib_wc *in_wc, struct ib_grh *in_grh,
-                   struct ib_mad *in_mad, struct ib_mad *out_mad);
+                   const struct ib_wc *in_wc, const struct ib_grh *in_grh,
+                   const struct ib_mad_hdr *in, size_t in_mad_size,
+                   struct ib_mad_hdr *out, size_t *out_mad_size,
+                   u16 *out_mad_pkey_index);
 int qib_create_agents(struct qib_ibdev *dev);
 void qib_free_agents(struct qib_ibdev *dev);
 
@@ -1007,8 +1009,9 @@ void qib_cq_enter(struct qib_cq *cq, struct ib_wc *entry, int sig);
 
 int qib_poll_cq(struct ib_cq *ibcq, int num_entries, struct ib_wc *entry);
 
-struct ib_cq *qib_create_cq(struct ib_device *ibdev, int entries,
-                           int comp_vector, struct ib_ucontext *context,
+struct ib_cq *qib_create_cq(struct ib_device *ibdev,
+                           const struct ib_cq_init_attr *attr,
+                           struct ib_ucontext *context,
                            struct ib_udata *udata);
 
 int qib_destroy_cq(struct ib_cq *ibcq);
index 0d0f98695d535315285e1d660accb71f3fedc4f6..34c49b8105feb4b59ae320997622e4145d24a742 100644 (file)
@@ -300,6 +300,22 @@ static struct notifier_block usnic_ib_inetaddr_notifier = {
 };
 /* End of inet section*/
 
+static int usnic_port_immutable(struct ib_device *ibdev, u8 port_num,
+                               struct ib_port_immutable *immutable)
+{
+       struct ib_port_attr attr;
+       int err;
+
+       err = usnic_ib_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;
+
+       return 0;
+}
+
 /* Start of PF discovery section */
 static void *usnic_ib_device_add(struct pci_dev *dev)
 {
@@ -383,6 +399,7 @@ static void *usnic_ib_device_add(struct pci_dev *dev)
        us_ibdev->ib_dev.poll_cq = usnic_ib_poll_cq;
        us_ibdev->ib_dev.req_notify_cq = usnic_ib_req_notify_cq;
        us_ibdev->ib_dev.get_dma_mr = usnic_ib_get_dma_mr;
+       us_ibdev->ib_dev.get_port_immutable = usnic_port_immutable;
 
 
        if (ib_register_device(&us_ibdev->ib_dev, NULL))
index 53bd6a2d9cdbbae4545a70d1d070b17eafb18154..7df43827cb29661a039d1794d24a20e6cbf1b094 100644 (file)
@@ -248,7 +248,8 @@ enum rdma_link_layer usnic_ib_port_link_layer(struct ib_device *device,
 }
 
 int usnic_ib_query_device(struct ib_device *ibdev,
-                               struct ib_device_attr *props)
+                         struct ib_device_attr *props,
+                         struct ib_udata *uhw)
 {
        struct usnic_ib_dev *us_ibdev = to_usdev(ibdev);
        union ib_gid gid;
@@ -257,6 +258,9 @@ int usnic_ib_query_device(struct ib_device *ibdev,
        int qp_per_vf;
 
        usnic_dbg("\n");
+       if (uhw->inlen || uhw->outlen)
+               return -EINVAL;
+
        mutex_lock(&us_ibdev->usdev_lock);
        us_ibdev->netdev->ethtool_ops->get_drvinfo(us_ibdev->netdev, &info);
        us_ibdev->netdev->ethtool_ops->get_settings(us_ibdev->netdev, &cmd);
@@ -570,13 +574,17 @@ int usnic_ib_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr,
        return status;
 }
 
-struct ib_cq *usnic_ib_create_cq(struct ib_device *ibdev, int entries,
-                                       int vector, struct ib_ucontext *context,
-                                       struct ib_udata *udata)
+struct ib_cq *usnic_ib_create_cq(struct ib_device *ibdev,
+                                const struct ib_cq_init_attr *attr,
+                                struct ib_ucontext *context,
+                                struct ib_udata *udata)
 {
        struct ib_cq *cq;
 
        usnic_dbg("\n");
+       if (attr->flags)
+               return ERR_PTR(-EINVAL);
+
        cq = kzalloc(sizeof(*cq), GFP_KERNEL);
        if (!cq)
                return ERR_PTR(-EBUSY);
index bb864f5aed708e1f2ea5bb287fa00ef586bdb1ea..0bd04efa16f33f514c30b3b5c32cf62f8488c271 100644 (file)
 enum rdma_link_layer usnic_ib_port_link_layer(struct ib_device *device,
                                                u8 port_num);
 int usnic_ib_query_device(struct ib_device *ibdev,
-                               struct ib_device_attr *props);
+                               struct ib_device_attr *props,
+                         struct ib_udata *uhw);
 int usnic_ib_query_port(struct ib_device *ibdev, u8 port,
                                struct ib_port_attr *props);
+enum rdma_protocol_type
+usnic_ib_query_protocol(struct ib_device *device, u8 port_num);
 int usnic_ib_query_qp(struct ib_qp *qp, struct ib_qp_attr *qp_attr,
                                int qp_attr_mask,
                                struct ib_qp_init_attr *qp_init_attr);
@@ -44,9 +47,10 @@ struct ib_qp *usnic_ib_create_qp(struct ib_pd *pd,
 int usnic_ib_destroy_qp(struct ib_qp *qp);
 int usnic_ib_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr,
                                int attr_mask, struct ib_udata *udata);
-struct ib_cq *usnic_ib_create_cq(struct ib_device *ibdev, int entries,
-                                       int vector, struct ib_ucontext *context,
-                                       struct ib_udata *udata);
+struct ib_cq *usnic_ib_create_cq(struct ib_device *ibdev,
+                                const struct ib_cq_init_attr *attr,
+                                struct ib_ucontext *context,
+                                struct ib_udata *udata);
 int usnic_ib_destroy_cq(struct ib_cq *cq);
 struct ib_mr *usnic_ib_reg_mr(struct ib_pd *pd, u64 start, u64 length,
                                u64 virt_addr, int access_flags,
index 417de1f329606e990fbcfa03580e3c3edcb51993..cb2337f0532b5dcbe0d328eb74a196d976b7b6dc 100644 (file)
@@ -472,11 +472,10 @@ struct usnic_uiom_pd *usnic_uiom_alloc_pd(void)
                return ERR_PTR(-ENOMEM);
 
        pd->domain = domain = iommu_domain_alloc(&pci_bus_type);
-       if (IS_ERR_OR_NULL(domain)) {
-               usnic_err("Failed to allocate IOMMU domain with err %ld\n",
-                               PTR_ERR(pd->domain));
+       if (!domain) {
+               usnic_err("Failed to allocate IOMMU domain");
                kfree(pd);
-               return ERR_PTR(domain ? PTR_ERR(domain) : -ENOMEM);
+               return ERR_PTR(-ENOMEM);
        }
 
        iommu_set_fault_handler(pd->domain, usnic_uiom_dma_fault, NULL);
index 9e1b203d756d272dfd4c8066b3f4bf64d681e0d5..da149c278cb8149a7541169c7b05147be82f8ed5 100644 (file)
@@ -1128,7 +1128,7 @@ static int ipoib_neigh_hash_init(struct ipoib_dev_priv *priv)
 {
        struct ipoib_neigh_table *ntbl = &priv->ntbl;
        struct ipoib_neigh_hash *htbl;
-       struct ipoib_neigh **buckets;
+       struct ipoib_neigh __rcu **buckets;
        u32 size;
 
        clear_bit(IPOIB_NEIGH_TBL_FLUSH, &priv->flags);
@@ -1146,7 +1146,7 @@ static int ipoib_neigh_hash_init(struct ipoib_dev_priv *priv)
        htbl->size = size;
        htbl->mask = (size - 1);
        htbl->buckets = buckets;
-       ntbl->htbl = htbl;
+       RCU_INIT_POINTER(ntbl->htbl, htbl);
        htbl->ntbl = ntbl;
        atomic_set(&ntbl->entries, 0);
 
@@ -1685,9 +1685,7 @@ static void ipoib_add_one(struct ib_device *device)
        struct net_device *dev;
        struct ipoib_dev_priv *priv;
        int s, e, p;
-
-       if (rdma_node_get_transport(device->node_type) != RDMA_TRANSPORT_IB)
-               return;
+       int count = 0;
 
        dev_list = kmalloc(sizeof *dev_list, GFP_KERNEL);
        if (!dev_list)
@@ -1704,15 +1702,21 @@ static void ipoib_add_one(struct ib_device *device)
        }
 
        for (p = s; p <= e; ++p) {
-               if (rdma_port_get_link_layer(device, p) != IB_LINK_LAYER_INFINIBAND)
+               if (!rdma_protocol_ib(device, p))
                        continue;
                dev = ipoib_add_port("ib%d", device, p);
                if (!IS_ERR(dev)) {
                        priv = netdev_priv(dev);
                        list_add_tail(&priv->list, dev_list);
+                       count++;
                }
        }
 
+       if (!count) {
+               kfree(dev_list);
+               return;
+       }
+
        ib_set_client_data(device, &ipoib_client, dev_list);
 }
 
@@ -1721,9 +1725,6 @@ static void ipoib_remove_one(struct ib_device *device)
        struct ipoib_dev_priv *priv, *tmp;
        struct list_head *dev_list;
 
-       if (rdma_node_get_transport(device->node_type) != RDMA_TRANSPORT_IB)
-               return;
-
        dev_list = ib_get_client_data(device, &ipoib_client);
        if (!dev_list)
                return;
index e5cc43074196dbab1ae216cb43135f7e5f081c66..9e6ee82a8fd76f490de93d6117754e2a6657ec72 100644 (file)
@@ -141,6 +141,7 @@ int ipoib_transport_dev_init(struct net_device *dev, struct ib_device *ca)
                .sq_sig_type = IB_SIGNAL_ALL_WR,
                .qp_type     = IB_QPT_UD
        };
+       struct ib_cq_init_attr cq_attr = {};
 
        int ret, size;
        int i;
@@ -178,14 +179,17 @@ int ipoib_transport_dev_init(struct net_device *dev, struct ib_device *ca)
        } else
                goto out_free_wq;
 
-       priv->recv_cq = ib_create_cq(priv->ca, ipoib_ib_completion, NULL, dev, size, 0);
+       cq_attr.cqe = size;
+       priv->recv_cq = ib_create_cq(priv->ca, ipoib_ib_completion, NULL,
+                                    dev, &cq_attr);
        if (IS_ERR(priv->recv_cq)) {
                printk(KERN_WARNING "%s: failed to create receive CQ\n", ca->name);
                goto out_cm_dev_cleanup;
        }
 
+       cq_attr.cqe = ipoib_sendq_size;
        priv->send_cq = ib_create_cq(priv->ca, ipoib_send_comp_handler, NULL,
-                                    dev, ipoib_sendq_size, 0);
+                                    dev, &cq_attr);
        if (IS_ERR(priv->send_cq)) {
                printk(KERN_WARNING "%s: failed to create send CQ\n", ca->name);
                goto out_free_recv_cq;
index cc2dd35ffbc08616af0ffddd854d1f4858570515..5c9f565ea0e88840c3c3c7a031e20180c3c8025d 100644 (file)
@@ -51,19 +51,22 @@ static void iser_cq_callback(struct ib_cq *cq, void *cq_context);
 
 static void iser_cq_event_callback(struct ib_event *cause, void *context)
 {
-       iser_err("got cq event %d \n", cause->event);
+       iser_err("cq event %s (%d)\n",
+                ib_event_msg(cause->event), cause->event);
 }
 
 static void iser_qp_event_callback(struct ib_event *cause, void *context)
 {
-       iser_err("got qp event %d\n",cause->event);
+       iser_err("qp event %s (%d)\n",
+                ib_event_msg(cause->event), cause->event);
 }
 
 static void iser_event_handler(struct ib_event_handler *handler,
                                struct ib_event *event)
 {
-       iser_err("async event %d on device %s port %d\n", event->event,
-               event->device->name, event->element.port_num);
+       iser_err("async event %s (%d) on device %s port %d\n",
+                ib_event_msg(event->event), event->event,
+                event->device->name, event->element.port_num);
 }
 
 /**
@@ -123,14 +126,17 @@ static int iser_create_device_ib_res(struct iser_device *device)
                goto pd_err;
 
        for (i = 0; i < device->comps_used; i++) {
+               struct ib_cq_init_attr cq_attr = {};
                struct iser_comp *comp = &device->comps[i];
 
                comp->device = device;
+               cq_attr.cqe = max_cqe;
+               cq_attr.comp_vector = i;
                comp->cq = ib_create_cq(device->ib_device,
                                        iser_cq_callback,
                                        iser_cq_event_callback,
                                        (void *)comp,
-                                       max_cqe, i);
+                                       &cq_attr);
                if (IS_ERR(comp->cq)) {
                        comp->cq = NULL;
                        goto cq_err;
@@ -873,8 +879,9 @@ static int iser_cma_handler(struct rdma_cm_id *cma_id, struct rdma_cm_event *eve
        int ret = 0;
 
        iser_conn = (struct iser_conn *)cma_id->context;
-       iser_info("event %d status %d conn %p id %p\n",
-                 event->event, event->status, cma_id->context, cma_id);
+       iser_info("%s (%d): status %d conn %p id %p\n",
+                 rdma_event_msg(event->event), event->event,
+                 event->status, cma_id->context, cma_id);
 
        mutex_lock(&iser_conn->state_mutex);
        switch (event->event) {
@@ -913,7 +920,8 @@ static int iser_cma_handler(struct rdma_cm_id *cma_id, struct rdma_cm_event *eve
                }
                break;
        default:
-               iser_err("Unexpected RDMA CM event (%d)\n", event->event);
+               iser_err("Unexpected RDMA CM event: %s (%d)\n",
+                        rdma_event_msg(event->event), event->event);
                break;
        }
        mutex_unlock(&iser_conn->state_mutex);
@@ -1173,10 +1181,13 @@ static void iser_handle_wc(struct ib_wc *wc)
                }
        } else {
                if (wc->status != IB_WC_WR_FLUSH_ERR)
-                       iser_err("wr id %llx status %d vend_err %x\n",
-                                wc->wr_id, wc->status, wc->vendor_err);
+                       iser_err("%s (%d): wr id %llx vend_err %x\n",
+                                ib_wc_status_msg(wc->status), wc->status,
+                                wc->wr_id, wc->vendor_err);
                else
-                       iser_dbg("flush error: wr id %llx\n", wc->wr_id);
+                       iser_dbg("%s (%d): wr id %llx\n",
+                                ib_wc_status_msg(wc->status), wc->status,
+                                wc->wr_id);
 
                if (wc->wr_id == ISER_BEACON_WRID)
                        /* all flush errors were consumed */
index 3f40319a55da364f2e757acb7bc0e83d86c78c38..f3b7a34e10d81c019217917ac6ebce0462278b50 100644 (file)
@@ -65,6 +65,8 @@ static int
 isert_rdma_accept(struct isert_conn *isert_conn);
 struct rdma_cm_id *isert_setup_id(struct isert_np *isert_np);
 
+static void isert_release_work(struct work_struct *work);
+
 static inline bool
 isert_prot_cmd(struct isert_conn *conn, struct se_cmd *cmd)
 {
@@ -78,7 +80,9 @@ isert_qp_event_callback(struct ib_event *e, void *context)
 {
        struct isert_conn *isert_conn = context;
 
-       isert_err("conn %p event: %d\n", isert_conn, e->event);
+       isert_err("%s (%d): conn %p\n",
+                 ib_event_msg(e->event), e->event, isert_conn);
+
        switch (e->event) {
        case IB_EVENT_COMM_EST:
                rdma_notify(isert_conn->cm_id, IB_EVENT_COMM_EST);
@@ -316,15 +320,18 @@ isert_alloc_comps(struct isert_device *device,
        max_cqe = min(ISER_MAX_CQ_LEN, attr->max_cqe);
 
        for (i = 0; i < device->comps_used; i++) {
+               struct ib_cq_init_attr cq_attr = {};
                struct isert_comp *comp = &device->comps[i];
 
                comp->device = device;
                INIT_WORK(&comp->work, isert_cq_work);
+               cq_attr.cqe = max_cqe;
+               cq_attr.comp_vector = i;
                comp->cq = ib_create_cq(device->ib_device,
                                        isert_cq_callback,
                                        isert_cq_event_callback,
                                        (void *)comp,
-                                       max_cqe, i);
+                                       &cq_attr);
                if (IS_ERR(comp->cq)) {
                        isert_err("Unable to allocate cq\n");
                        ret = PTR_ERR(comp->cq);
@@ -648,6 +655,7 @@ isert_init_conn(struct isert_conn *isert_conn)
        mutex_init(&isert_conn->mutex);
        spin_lock_init(&isert_conn->pool_lock);
        INIT_LIST_HEAD(&isert_conn->fr_pool);
+       INIT_WORK(&isert_conn->release_work, isert_release_work);
 }
 
 static void
@@ -897,7 +905,8 @@ static int
 isert_np_cma_handler(struct isert_np *isert_np,
                     enum rdma_cm_event_type event)
 {
-       isert_dbg("isert np %p, handling event %d\n", isert_np, event);
+       isert_dbg("%s (%d): isert np %p\n",
+                 rdma_event_msg(event), event, isert_np);
 
        switch (event) {
        case RDMA_CM_EVENT_DEVICE_REMOVAL:
@@ -925,6 +934,7 @@ isert_disconnected_handler(struct rdma_cm_id *cma_id,
 {
        struct isert_np *isert_np = cma_id->context;
        struct isert_conn *isert_conn;
+       bool terminating = false;
 
        if (isert_np->np_cm_id == cma_id)
                return isert_np_cma_handler(cma_id->context, event);
@@ -932,12 +942,25 @@ isert_disconnected_handler(struct rdma_cm_id *cma_id,
        isert_conn = cma_id->qp->qp_context;
 
        mutex_lock(&isert_conn->mutex);
+       terminating = (isert_conn->state == ISER_CONN_TERMINATING);
        isert_conn_terminate(isert_conn);
        mutex_unlock(&isert_conn->mutex);
 
        isert_info("conn %p completing wait\n", isert_conn);
        complete(&isert_conn->wait);
 
+       if (terminating)
+               goto out;
+
+       mutex_lock(&isert_np->np_accept_mutex);
+       if (!list_empty(&isert_conn->accept_node)) {
+               list_del_init(&isert_conn->accept_node);
+               isert_put_conn(isert_conn);
+               queue_work(isert_release_wq, &isert_conn->release_work);
+       }
+       mutex_unlock(&isert_np->np_accept_mutex);
+
+out:
        return 0;
 }
 
@@ -957,7 +980,8 @@ isert_cma_handler(struct rdma_cm_id *cma_id, struct rdma_cm_event *event)
 {
        int ret = 0;
 
-       isert_info("event %d status %d id %p np %p\n", event->event,
+       isert_info("%s (%d): status %d id %p np %p\n",
+                  rdma_event_msg(event->event), event->event,
                   event->status, cma_id, cma_id->context);
 
        switch (event->event) {
@@ -2091,10 +2115,13 @@ isert_handle_wc(struct ib_wc *wc)
                }
        } else {
                if (wc->status != IB_WC_WR_FLUSH_ERR)
-                       isert_err("wr id %llx status %d vend_err %x\n",
-                                 wc->wr_id, wc->status, wc->vendor_err);
+                       isert_err("%s (%d): wr id %llx vend_err %x\n",
+                                 ib_wc_status_msg(wc->status), wc->status,
+                                 wc->wr_id, wc->vendor_err);
                else
-                       isert_dbg("flush error: wr id %llx\n", wc->wr_id);
+                       isert_dbg("%s (%d): wr id %llx\n",
+                                 ib_wc_status_msg(wc->status), wc->status,
+                                 wc->wr_id);
 
                if (wc->wr_id != ISER_FASTREG_LI_WRID)
                        isert_cq_comp_err(isert_conn, wc);
@@ -2380,7 +2407,6 @@ isert_build_rdma_wr(struct isert_conn *isert_conn, struct isert_cmd *isert_cmd,
        page_off = offset % PAGE_SIZE;
 
        send_wr->sg_list = ib_sge;
-       send_wr->num_sge = sg_nents;
        send_wr->wr_id = (uintptr_t)&isert_cmd->tx_desc;
        /*
         * Perform mapping of TCM scatterlist memory ib_sge dma_addr.
@@ -2400,14 +2426,17 @@ isert_build_rdma_wr(struct isert_conn *isert_conn, struct isert_cmd *isert_cmd,
                          ib_sge->addr, ib_sge->length, ib_sge->lkey);
                page_off = 0;
                data_left -= ib_sge->length;
+               if (!data_left)
+                       break;
                ib_sge++;
                isert_dbg("Incrementing ib_sge pointer to %p\n", ib_sge);
        }
 
+       send_wr->num_sge = ++i;
        isert_dbg("Set outgoing sg_list: %p num_sg: %u from TCM SGLs\n",
                  send_wr->sg_list, send_wr->num_sge);
 
-       return sg_nents;
+       return send_wr->num_sge;
 }
 
 static int
@@ -3366,7 +3395,6 @@ static void isert_wait_conn(struct iscsi_conn *conn)
        isert_wait4flush(isert_conn);
        isert_wait4logout(isert_conn);
 
-       INIT_WORK(&isert_conn->release_work, isert_release_work);
        queue_work(isert_release_wq, &isert_conn->release_work);
 }
 
@@ -3374,6 +3402,7 @@ static void isert_free_conn(struct iscsi_conn *conn)
 {
        struct isert_conn *isert_conn = conn->context;
 
+       isert_wait4flush(isert_conn);
        isert_put_conn(isert_conn);
 }
 
index 918814cd0f806f5344e5f293e2bb059010237727..eada8f758ad4089ec0e15a7469ccc50cb626a98f 100644 (file)
 #define DRV_RELDATE    "July 1, 2013"
 
 MODULE_AUTHOR("Roland Dreier");
-MODULE_DESCRIPTION("InfiniBand SCSI RDMA Protocol initiator "
-                  "v" DRV_VERSION " (" DRV_RELDATE ")");
+MODULE_DESCRIPTION("InfiniBand SCSI RDMA Protocol initiator");
 MODULE_LICENSE("Dual BSD/GPL");
+MODULE_VERSION(DRV_VERSION);
+MODULE_INFO(release_date, DRV_RELDATE);
 
 static unsigned int srp_sg_tablesize;
 static unsigned int cmd_sg_entries;
@@ -253,7 +254,8 @@ static void srp_free_iu(struct srp_host *host, struct srp_iu *iu)
 
 static void srp_qp_event(struct ib_event *event, void *context)
 {
-       pr_debug("QP event %d\n", event->event);
+       pr_debug("QP event %s (%d)\n",
+                ib_event_msg(event->event), event->event);
 }
 
 static int srp_init_qp(struct srp_target_port *target,
@@ -465,14 +467,13 @@ static struct srp_fr_pool *srp_alloc_fr_pool(struct srp_target_port *target)
  */
 static void srp_destroy_qp(struct srp_rdma_ch *ch)
 {
-       struct srp_target_port *target = ch->target;
        static struct ib_qp_attr attr = { .qp_state = IB_QPS_ERR };
        static struct ib_recv_wr wr = { .wr_id = SRP_LAST_WR_ID };
        struct ib_recv_wr *bad_wr;
        int ret;
 
        /* Destroying a QP and reusing ch->done is only safe if not connected */
-       WARN_ON_ONCE(target->connected);
+       WARN_ON_ONCE(ch->connected);
 
        ret = ib_modify_qp(ch->qp, &attr, IB_QP_STATE);
        WARN_ONCE(ret, "ib_cm_init_qp_attr() returned %d\n", ret);
@@ -499,6 +500,7 @@ static int srp_create_ch_ib(struct srp_rdma_ch *ch)
        struct ib_fmr_pool *fmr_pool = NULL;
        struct srp_fr_pool *fr_pool = NULL;
        const int m = 1 + dev->use_fast_reg;
+       struct ib_cq_init_attr cq_attr = {};
        int ret;
 
        init_attr = kzalloc(sizeof *init_attr, GFP_KERNEL);
@@ -506,15 +508,19 @@ static int srp_create_ch_ib(struct srp_rdma_ch *ch)
                return -ENOMEM;
 
        /* + 1 for SRP_LAST_WR_ID */
+       cq_attr.cqe = target->queue_size + 1;
+       cq_attr.comp_vector = ch->comp_vector;
        recv_cq = ib_create_cq(dev->dev, srp_recv_completion, NULL, ch,
-                              target->queue_size + 1, ch->comp_vector);
+                              &cq_attr);
        if (IS_ERR(recv_cq)) {
                ret = PTR_ERR(recv_cq);
                goto err;
        }
 
+       cq_attr.cqe = m * target->queue_size;
+       cq_attr.comp_vector = ch->comp_vector;
        send_cq = ib_create_cq(dev->dev, srp_send_completion, NULL, ch,
-                              m * target->queue_size, ch->comp_vector);
+                              &cq_attr);
        if (IS_ERR(send_cq)) {
                ret = PTR_ERR(send_cq);
                goto err_recv_cq;
@@ -781,7 +787,7 @@ static int srp_send_req(struct srp_rdma_ch *ch, bool multich)
                shost_printk(KERN_DEBUG, target->scsi_host,
                             PFX "Topspin/Cisco initiator port ID workaround "
                             "activated for target GUID %016llx\n",
-                            (unsigned long long) be64_to_cpu(target->ioc_guid));
+                            be64_to_cpu(target->ioc_guid));
                memset(req->priv.initiator_port_id, 0, 8);
                memcpy(req->priv.initiator_port_id + 8,
                       &target->srp_host->srp_dev->dev->node_guid, 8);
@@ -811,35 +817,19 @@ static bool srp_queue_remove_work(struct srp_target_port *target)
        return changed;
 }
 
-static bool srp_change_conn_state(struct srp_target_port *target,
-                                 bool connected)
-{
-       bool changed = false;
-
-       spin_lock_irq(&target->lock);
-       if (target->connected != connected) {
-               target->connected = connected;
-               changed = true;
-       }
-       spin_unlock_irq(&target->lock);
-
-       return changed;
-}
-
 static void srp_disconnect_target(struct srp_target_port *target)
 {
        struct srp_rdma_ch *ch;
        int i;
 
-       if (srp_change_conn_state(target, false)) {
-               /* XXX should send SRP_I_LOGOUT request */
+       /* XXX should send SRP_I_LOGOUT request */
 
-               for (i = 0; i < target->ch_count; i++) {
-                       ch = &target->ch[i];
-                       if (ch->cm_id && ib_send_cm_dreq(ch->cm_id, NULL, 0)) {
-                               shost_printk(KERN_DEBUG, target->scsi_host,
-                                            PFX "Sending CM DREQ failed\n");
-                       }
+       for (i = 0; i < target->ch_count; i++) {
+               ch = &target->ch[i];
+               ch->connected = false;
+               if (ch->cm_id && ib_send_cm_dreq(ch->cm_id, NULL, 0)) {
+                       shost_printk(KERN_DEBUG, target->scsi_host,
+                                    PFX "Sending CM DREQ failed\n");
                }
        }
 }
@@ -852,7 +842,7 @@ static void srp_free_req_data(struct srp_target_port *target,
        struct srp_request *req;
        int i;
 
-       if (!ch->target || !ch->req_ring)
+       if (!ch->req_ring)
                return;
 
        for (i = 0; i < target->req_ring_size; ++i) {
@@ -986,14 +976,26 @@ static void srp_rport_delete(struct srp_rport *rport)
        srp_queue_remove_work(target);
 }
 
+/**
+ * srp_connected_ch() - number of connected channels
+ * @target: SRP target port.
+ */
+static int srp_connected_ch(struct srp_target_port *target)
+{
+       int i, c = 0;
+
+       for (i = 0; i < target->ch_count; i++)
+               c += target->ch[i].connected;
+
+       return c;
+}
+
 static int srp_connect_ch(struct srp_rdma_ch *ch, bool multich)
 {
        struct srp_target_port *target = ch->target;
        int ret;
 
-       WARN_ON_ONCE(!multich && target->connected);
-
-       target->qp_in_error = false;
+       WARN_ON_ONCE(!multich && srp_connected_ch(target) > 0);
 
        ret = srp_lookup_path(ch);
        if (ret)
@@ -1016,7 +1018,7 @@ static int srp_connect_ch(struct srp_rdma_ch *ch, bool multich)
                 */
                switch (ch->status) {
                case 0:
-                       srp_change_conn_state(target, true);
+                       ch->connected = true;
                        return 0;
 
                case SRP_PORT_REDIRECT:
@@ -1214,14 +1216,10 @@ static int srp_rport_reconnect(struct srp_rport *rport)
         */
        for (i = 0; i < target->ch_count; i++) {
                ch = &target->ch[i];
-               if (!ch->target)
-                       break;
                ret += srp_new_cm_id(ch);
        }
        for (i = 0; i < target->ch_count; i++) {
                ch = &target->ch[i];
-               if (!ch->target)
-                       break;
                for (j = 0; j < target->req_ring_size; ++j) {
                        struct srp_request *req = &ch->req_ring[j];
 
@@ -1230,8 +1228,6 @@ static int srp_rport_reconnect(struct srp_rport *rport)
        }
        for (i = 0; i < target->ch_count; i++) {
                ch = &target->ch[i];
-               if (!ch->target)
-                       break;
                /*
                 * Whether or not creating a new CM ID succeeded, create a new
                 * QP. This guarantees that all completion callback function
@@ -1243,13 +1239,13 @@ static int srp_rport_reconnect(struct srp_rport *rport)
                for (j = 0; j < target->queue_size; ++j)
                        list_add(&ch->tx_ring[j]->list, &ch->free_tx);
        }
+
+       target->qp_in_error = false;
+
        for (i = 0; i < target->ch_count; i++) {
                ch = &target->ch[i];
-               if (ret || !ch->target) {
-                       if (i > 1)
-                               ret = 0;
+               if (ret)
                        break;
-               }
                ret = srp_connect_ch(ch, multich);
                multich = true;
        }
@@ -1842,7 +1838,7 @@ static void srp_process_aer_req(struct srp_rdma_ch *ch,
        s32 delta = be32_to_cpu(req->req_lim_delta);
 
        shost_printk(KERN_ERR, target->scsi_host, PFX
-                    "ignoring AER for LUN %llu\n", be64_to_cpu(req->lun));
+                    "ignoring AER for LUN %llu\n", scsilun_to_int(&req->lun));
 
        if (srp_response_common(ch, delta, &rsp, sizeof(rsp)))
                shost_printk(KERN_ERR, target->scsi_host, PFX
@@ -1929,20 +1925,21 @@ static void srp_handle_qp_err(u64 wr_id, enum ib_wc_status wc_status,
                return;
        }
 
-       if (target->connected && !target->qp_in_error) {
+       if (ch->connected && !target->qp_in_error) {
                if (wr_id & LOCAL_INV_WR_ID_MASK) {
                        shost_printk(KERN_ERR, target->scsi_host, PFX
-                                    "LOCAL_INV failed with status %d\n",
-                                    wc_status);
+                                    "LOCAL_INV failed with status %s (%d)\n",
+                                    ib_wc_status_msg(wc_status), wc_status);
                } else if (wr_id & FAST_REG_WR_ID_MASK) {
                        shost_printk(KERN_ERR, target->scsi_host, PFX
-                                    "FAST_REG_MR failed status %d\n",
-                                    wc_status);
+                                    "FAST_REG_MR failed status %s (%d)\n",
+                                    ib_wc_status_msg(wc_status), wc_status);
                } else {
                        shost_printk(KERN_ERR, target->scsi_host,
-                                    PFX "failed %s status %d for iu %p\n",
+                                    PFX "failed %s status %s (%d) for iu %p\n",
                                     send_err ? "send" : "receive",
-                                    wc_status, (void *)(uintptr_t)wr_id);
+                                    ib_wc_status_msg(wc_status), wc_status,
+                                    (void *)(uintptr_t)wr_id);
                }
                queue_work(system_long_wq, &target->tl_err_work);
        }
@@ -2034,7 +2031,7 @@ static int srp_queuecommand(struct Scsi_Host *shost, struct scsi_cmnd *scmnd)
        memset(cmd, 0, sizeof *cmd);
 
        cmd->opcode = SRP_CMD;
-       cmd->lun    = cpu_to_be64((u64) scmnd->device->lun << 48);
+       int_to_scsilun(scmnd->device->lun, &cmd->lun);
        cmd->tag    = tag;
        memcpy(cmd->cdb, scmnd->cmnd, scmnd->cmd_len);
 
@@ -2367,7 +2364,7 @@ static int srp_cm_handler(struct ib_cm_id *cm_id, struct ib_cm_event *event)
        case IB_CM_DREQ_RECEIVED:
                shost_printk(KERN_WARNING, target->scsi_host,
                             PFX "DREQ received - connection closed\n");
-               srp_change_conn_state(target, false);
+               ch->connected = false;
                if (ib_send_cm_drep(cm_id, NULL, 0))
                        shost_printk(KERN_ERR, target->scsi_host,
                                     PFX "Sending CM DREP failed\n");
@@ -2414,8 +2411,8 @@ srp_change_queue_depth(struct scsi_device *sdev, int qdepth)
        return scsi_change_queue_depth(sdev, qdepth);
 }
 
-static int srp_send_tsk_mgmt(struct srp_rdma_ch *ch, u64 req_tag,
-                            unsigned int lun, u8 func)
+static int srp_send_tsk_mgmt(struct srp_rdma_ch *ch, u64 req_tag, u64 lun,
+                            u8 func)
 {
        struct srp_target_port *target = ch->target;
        struct srp_rport *rport = target->rport;
@@ -2423,7 +2420,7 @@ static int srp_send_tsk_mgmt(struct srp_rdma_ch *ch, u64 req_tag,
        struct srp_iu *iu;
        struct srp_tsk_mgmt *tsk_mgmt;
 
-       if (!target->connected || target->qp_in_error)
+       if (!ch->connected || target->qp_in_error)
                return -1;
 
        init_completion(&ch->tsk_mgmt_done);
@@ -2449,7 +2446,7 @@ static int srp_send_tsk_mgmt(struct srp_rdma_ch *ch, u64 req_tag,
        memset(tsk_mgmt, 0, sizeof *tsk_mgmt);
 
        tsk_mgmt->opcode        = SRP_TSK_MGMT;
-       tsk_mgmt->lun           = cpu_to_be64((u64) lun << 48);
+       int_to_scsilun(lun, &tsk_mgmt->lun);
        tsk_mgmt->tag           = req_tag | SRP_TAG_TSK_MGMT;
        tsk_mgmt->tsk_mgmt_func = func;
        tsk_mgmt->task_tag      = req_tag;
@@ -2563,8 +2560,7 @@ static ssize_t show_id_ext(struct device *dev, struct device_attribute *attr,
 {
        struct srp_target_port *target = host_to_target(class_to_shost(dev));
 
-       return sprintf(buf, "0x%016llx\n",
-                      (unsigned long long) be64_to_cpu(target->id_ext));
+       return sprintf(buf, "0x%016llx\n", be64_to_cpu(target->id_ext));
 }
 
 static ssize_t show_ioc_guid(struct device *dev, struct device_attribute *attr,
@@ -2572,8 +2568,7 @@ static ssize_t show_ioc_guid(struct device *dev, struct device_attribute *attr,
 {
        struct srp_target_port *target = host_to_target(class_to_shost(dev));
 
-       return sprintf(buf, "0x%016llx\n",
-                      (unsigned long long) be64_to_cpu(target->ioc_guid));
+       return sprintf(buf, "0x%016llx\n", be64_to_cpu(target->ioc_guid));
 }
 
 static ssize_t show_service_id(struct device *dev,
@@ -2581,8 +2576,7 @@ static ssize_t show_service_id(struct device *dev,
 {
        struct srp_target_port *target = host_to_target(class_to_shost(dev));
 
-       return sprintf(buf, "0x%016llx\n",
-                      (unsigned long long) be64_to_cpu(target->service_id));
+       return sprintf(buf, "0x%016llx\n", be64_to_cpu(target->service_id));
 }
 
 static ssize_t show_pkey(struct device *dev, struct device_attribute *attr,
@@ -2773,7 +2767,7 @@ static int srp_add_target(struct srp_host *host, struct srp_target_port *target)
 
        target->state = SRP_TARGET_SCANNING;
        sprintf(target->target_name, "SRP.T10:%016llX",
-                (unsigned long long) be64_to_cpu(target->id_ext));
+               be64_to_cpu(target->id_ext));
 
        if (scsi_add_host(target->scsi_host, host->srp_dev->dev->dma_device))
                return -ENODEV;
@@ -2797,7 +2791,8 @@ static int srp_add_target(struct srp_host *host, struct srp_target_port *target)
        scsi_scan_target(&target->scsi_host->shost_gendev,
                         0, target->scsi_id, SCAN_WILD_CARD, 0);
 
-       if (!target->connected || target->qp_in_error) {
+       if (srp_connected_ch(target) < target->ch_count ||
+           target->qp_in_error) {
                shost_printk(KERN_INFO, target->scsi_host,
                             PFX "SCSI scan failed - removing SCSI host\n");
                srp_queue_remove_work(target);
@@ -3146,7 +3141,7 @@ static ssize_t srp_create_target(struct device *dev,
        target_host->transportt  = ib_srp_transport_template;
        target_host->max_channel = 0;
        target_host->max_id      = 1;
-       target_host->max_lun     = SRP_MAX_LUN;
+       target_host->max_lun     = -1LL;
        target_host->max_cmd_len = sizeof ((struct srp_cmd *) (void *) 0L)->cdb;
 
        target = host_to_target(target_host);
@@ -3172,11 +3167,11 @@ static ssize_t srp_create_target(struct device *dev,
 
        ret = srp_parse_options(buf, target);
        if (ret)
-               goto err;
+               goto out;
 
        ret = scsi_init_shared_tag_map(target_host, target_host->can_queue);
        if (ret)
-               goto err;
+               goto out;
 
        target->req_ring_size = target->queue_size - SRP_TSK_MGMT_SQ_SIZE;
 
@@ -3187,7 +3182,7 @@ static ssize_t srp_create_target(struct device *dev,
                             be64_to_cpu(target->ioc_guid),
                             be64_to_cpu(target->initiator_ext));
                ret = -EEXIST;
-               goto err;
+               goto out;
        }
 
        if (!srp_dev->has_fmr && !srp_dev->has_fr && !target->allow_ext_sg &&
@@ -3208,7 +3203,7 @@ static ssize_t srp_create_target(struct device *dev,
        spin_lock_init(&target->lock);
        ret = ib_query_gid(ibdev, host->port, 0, &target->sgid);
        if (ret)
-               goto err;
+               goto out;
 
        ret = -ENOMEM;
        target->ch_count = max_t(unsigned, num_online_nodes(),
@@ -3219,7 +3214,7 @@ static ssize_t srp_create_target(struct device *dev,
        target->ch = kcalloc(target->ch_count, sizeof(*target->ch),
                             GFP_KERNEL);
        if (!target->ch)
-               goto err;
+               goto out;
 
        node_idx = 0;
        for_each_online_node(node) {
@@ -3315,9 +3310,6 @@ err_disconnect:
        }
 
        kfree(target->ch);
-
-err:
-       scsi_host_put(target_host);
        goto out;
 }
 
index a611556406ac05bb1a4befbc870f510880fce2fe..17ee3f80ba550aec9c9ae9d326e2e91e2bb7daf0 100644 (file)
@@ -54,7 +54,6 @@ enum {
        SRP_DLID_REDIRECT       = 2,
        SRP_STALE_CONN          = 3,
 
-       SRP_MAX_LUN             = 512,
        SRP_DEF_SG_TABLESIZE    = 12,
 
        SRP_DEFAULT_QUEUE_SIZE  = 1 << 6,
@@ -170,6 +169,7 @@ struct srp_rdma_ch {
 
        struct completion       tsk_mgmt_done;
        u8                      tsk_mgmt_status;
+       bool                    connected;
 };
 
 /**
@@ -214,7 +214,6 @@ struct srp_target_port {
        __be16                  pkey;
 
        u32                     rq_tmo_jiffies;
-       bool                    connected;
 
        int                     zero_req_lim;
 
index 9b84b4c0a000a32bdc896d6142fb63c7d5b13a3a..4556cd11288e755a348cc8553cdf28af38f08880 100644 (file)
@@ -41,6 +41,7 @@
 #include <linux/string.h>
 #include <linux/delay.h>
 #include <linux/atomic.h>
+#include <scsi/scsi_proto.h>
 #include <scsi/scsi_tcq.h>
 #include <target/configfs_macros.h>
 #include <target/target_core_base.h>
@@ -476,7 +477,8 @@ static void srpt_mad_recv_handler(struct ib_mad_agent *mad_agent,
        rsp = ib_create_send_mad(mad_agent, mad_wc->wc->src_qp,
                                 mad_wc->wc->pkey_index, 0,
                                 IB_MGMT_DEVICE_HDR, IB_MGMT_DEVICE_DATA,
-                                GFP_KERNEL);
+                                GFP_KERNEL,
+                                IB_MGMT_BASE_VERSION);
        if (IS_ERR(rsp))
                goto err_rsp;
 
@@ -2080,6 +2082,7 @@ static int srpt_create_ch_ib(struct srpt_rdma_ch *ch)
        struct srpt_port *sport = ch->sport;
        struct srpt_device *sdev = sport->sdev;
        u32 srp_sq_size = sport->port_attrib.srp_sq_size;
+       struct ib_cq_init_attr cq_attr = {};
        int ret;
 
        WARN_ON(ch->rq_size < 1);
@@ -2090,8 +2093,9 @@ static int srpt_create_ch_ib(struct srpt_rdma_ch *ch)
                goto out;
 
 retry:
+       cq_attr.cqe = ch->rq_size + srp_sq_size;
        ch->cq = ib_create_cq(sdev->device, srpt_completion, NULL, ch,
-                             ch->rq_size + srp_sq_size, 0);
+                             &cq_attr);
        if (IS_ERR(ch->cq)) {
                ret = PTR_ERR(ch->cq);
                pr_err("failed to create CQ cqe= %d ret= %d\n",
index 3dae156905de53c1f9e9c5f0717f4ef4c3729803..d85c0c2056257b0ffa9a19e49aa96dba0e33399e 100644 (file)
@@ -245,7 +245,7 @@ struct srpt_send_ioctx {
        u8                      n_rdma;
        u8                      n_rbuf;
        bool                    queue_status_only;
-       u8                      sense_data[SCSI_SENSE_BUFFERSIZE];
+       u8                      sense_data[TRANSPORT_SENSE_BUFFER];
 };
 
 /**
index 106fbac7f8c5b024acf458c5564ef18588f1158a..e8eb60c6d83eba8e92cd433e794464d2ff8ad013 100644 (file)
@@ -677,7 +677,7 @@ config KEYBOARD_W90P910
 config KEYBOARD_CROS_EC
        tristate "ChromeOS EC keyboard"
        select INPUT_MATRIXKMAP
-       depends on MFD_CROS_EC
+       depends on CROS_EC_PROTO
        help
          Say Y here to enable the matrix keyboard used by ChromeOS devices
          and implemented on the ChromeOS EC. You must enable one bus option
index b50c5b8b8a4de4bc121743c4dace3aa5f4dd5636..b01966dc7eb3db697c6313b4963e79912990b98f 100644 (file)
@@ -148,19 +148,28 @@ static void cros_ec_keyb_process(struct cros_ec_keyb *ckdev,
 
 static int cros_ec_keyb_get_state(struct cros_ec_keyb *ckdev, uint8_t *kb_state)
 {
-       int ret;
-       struct cros_ec_command msg = {
-               .command = EC_CMD_MKBP_STATE,
-               .insize = ckdev->cols,
-       };
+       int ret = 0;
+       struct cros_ec_command *msg;
 
-       ret = cros_ec_cmd_xfer(ckdev->ec, &msg);
-       if (ret < 0)
-               return ret;
+       msg = kmalloc(sizeof(*msg) + ckdev->cols, GFP_KERNEL);
+       if (!msg)
+               return -ENOMEM;
 
-       memcpy(kb_state, msg.indata, ckdev->cols);
+       msg->version = 0;
+       msg->command = EC_CMD_MKBP_STATE;
+       msg->insize = ckdev->cols;
+       msg->outsize = 0;
 
-       return 0;
+       ret = cros_ec_cmd_xfer(ckdev->ec, msg);
+       if (ret < 0) {
+               dev_err(ckdev->dev, "Error transferring EC message %d\n", ret);
+               goto exit;
+       }
+
+       memcpy(kb_state, msg->data, ckdev->cols);
+exit:
+       kfree(msg);
+       return ret;
 }
 
 static irqreturn_t cros_ec_keyb_irq(int irq, void *data)
@@ -266,7 +275,7 @@ static int cros_ec_keyb_probe(struct platform_device *pdev)
        ckdev->dev = dev;
        dev_set_drvdata(&pdev->dev, ckdev);
 
-       idev->name = ec->ec_name;
+       idev->name = CROS_EC_DEV_NAME;
        idev->phys = ec->phys_name;
        __set_bit(EV_REP, idev->evbit);
 
index 7752bd59d4b7d529218dad186c155a91a9922ad1..a353b7de6d22e91a52378cd4c106b17cafc26a07 100644 (file)
@@ -1063,9 +1063,8 @@ static void alps_process_trackstick_packet_v7(struct psmouse *psmouse)
        right = (packet[1] & 0x02) >> 1;
        middle = (packet[1] & 0x04) >> 2;
 
-       /* Divide 2 since trackpoint's speed is too fast */
-       input_report_rel(dev2, REL_X, (char)x / 2);
-       input_report_rel(dev2, REL_Y, -((char)y / 2));
+       input_report_rel(dev2, REL_X, (char)x);
+       input_report_rel(dev2, REL_Y, -((char)y));
 
        input_report_key(dev2, BTN_LEFT, left);
        input_report_key(dev2, BTN_RIGHT, right);
index 79363b6871959ec2b74c24f4b9e9b89b172d950d..ce3d40004458c87392339472f654462fae7cf0bc 100644 (file)
@@ -1376,10 +1376,11 @@ static bool elantech_is_signature_valid(const unsigned char *param)
                return true;
 
        /*
-        * Some models have a revision higher then 20. Meaning param[2] may
-        * be 10 or 20, skip the rates check for these.
+        * Some hw_version >= 4 models have a revision higher then 20. Meaning
+        * that param[2] may be 10 or 20, skip the rates check for these.
         */
-       if (param[0] == 0x46 && (param[1] & 0xef) == 0x0f && param[2] < 40)
+       if ((param[0] & 0x0f) >= 0x06 && (param[1] & 0xaf) == 0x0f &&
+           param[2] < 40)
                return true;
 
        for (i = 0; i < ARRAY_SIZE(rates); i++)
@@ -1555,6 +1556,7 @@ static int elantech_set_properties(struct elantech_data *etd)
                case 9:
                case 10:
                case 13:
+               case 14:
                        etd->hw_version = 4;
                        break;
                default:
index 630af73e98c488a5e266e4ccb6eed5dba622f3d3..35c8d0ceabeebf989b8eeff5cd54ee8f3ac2e247 100644 (file)
@@ -150,6 +150,11 @@ static const struct min_max_quirk min_max_pnpid_table[] = {
                {ANY_BOARD_ID, 2961},
                1024, 5112, 2024, 4832
        },
+       {
+               (const char * const []){"LEN2000", NULL},
+               {ANY_BOARD_ID, ANY_BOARD_ID},
+               1024, 5113, 2021, 4832
+       },
        {
                (const char * const []){"LEN2001", NULL},
                {ANY_BOARD_ID, ANY_BOARD_ID},
@@ -191,7 +196,7 @@ static const char * const topbuttonpad_pnp_ids[] = {
        "LEN0045",
        "LEN0047",
        "LEN0049",
-       "LEN2000",
+       "LEN2000", /* S540 */
        "LEN2001", /* Edge E431 */
        "LEN2002", /* Edge E531 */
        "LEN2003",
index 1ae4e547b419b909a9748b54cc6b973d31ff0221..40f37a2b4a8aba136181009e03713f0c1b1b835d 100644 (file)
@@ -339,6 +339,7 @@ config SPAPR_TCE_IOMMU
          Enables bits of IOMMU API required by VFIO. The iommu_ops
          is not implemented as it is not necessary for VFIO.
 
+# ARM IOMMU support
 config ARM_SMMU
        bool "ARM Ltd. System MMU (SMMU) Support"
        depends on (ARM64 || ARM) && MMU
@@ -352,4 +353,16 @@ config ARM_SMMU
          Say Y here if your SoC includes an IOMMU device implementing
          the ARM SMMU architecture.
 
+config ARM_SMMU_V3
+       bool "ARM Ltd. System MMU Version 3 (SMMUv3) Support"
+       depends on ARM64 && PCI
+       select IOMMU_API
+       select IOMMU_IO_PGTABLE_LPAE
+       help
+         Support for implementations of the ARM System MMU architecture
+         version 3 providing translation support to a PCIe root complex.
+
+         Say Y here if your system includes an IOMMU device implementing
+         the ARM SMMUv3 architecture.
+
 endif # IOMMU_SUPPORT
index 080ffab4ed1cf07a5d66c1e6671eb0704c86d738..c6dcc513d711299b41a6b1f926abbc4f6e3b9286 100644 (file)
@@ -9,6 +9,7 @@ obj-$(CONFIG_MSM_IOMMU) += msm_iommu.o msm_iommu_dev.o
 obj-$(CONFIG_AMD_IOMMU) += amd_iommu.o amd_iommu_init.o
 obj-$(CONFIG_AMD_IOMMU_V2) += amd_iommu_v2.o
 obj-$(CONFIG_ARM_SMMU) += arm-smmu.o
+obj-$(CONFIG_ARM_SMMU_V3) += arm-smmu-v3.o
 obj-$(CONFIG_DMAR_TABLE) += dmar.o
 obj-$(CONFIG_INTEL_IOMMU) += intel-iommu.o
 obj-$(CONFIG_IPMMU_VMSA) += ipmmu-vmsa.o
index e43d48956dea239fe6816bdb23f0174754c623ee..d3e5e9abe3b6cc36f4b488491ad38e24f0a4bf8b 100644 (file)
@@ -34,6 +34,7 @@
 #include <linux/irq.h>
 #include <linux/msi.h>
 #include <linux/dma-contiguous.h>
+#include <linux/irqdomain.h>
 #include <asm/irq_remapping.h>
 #include <asm/io_apic.h>
 #include <asm/apic.h>
 
 static DEFINE_RWLOCK(amd_iommu_devtable_lock);
 
-/* A list of preallocated protection domains */
-static LIST_HEAD(iommu_pd_list);
-static DEFINE_SPINLOCK(iommu_pd_list_lock);
-
 /* List of all available dev_data structures */
 static LIST_HEAD(dev_data_list);
 static DEFINE_SPINLOCK(dev_data_list_lock);
@@ -119,7 +116,7 @@ struct iommu_cmd {
 struct kmem_cache *amd_iommu_irq_cache;
 
 static void update_domain(struct protection_domain *domain);
-static int __init alloc_passthrough_domain(void);
+static int alloc_passthrough_domain(void);
 
 /****************************************************************************
  *
@@ -234,31 +231,38 @@ static bool pdev_pri_erratum(struct pci_dev *pdev, u32 erratum)
 }
 
 /*
- * In this function the list of preallocated protection domains is traversed to
- * find the domain for a specific device
+ * This function actually applies the mapping to the page table of the
+ * dma_ops domain.
  */
-static struct dma_ops_domain *find_protection_domain(u16 devid)
+static void alloc_unity_mapping(struct dma_ops_domain *dma_dom,
+                               struct unity_map_entry *e)
 {
-       struct dma_ops_domain *entry, *ret = NULL;
-       unsigned long flags;
-       u16 alias = amd_iommu_alias_table[devid];
-
-       if (list_empty(&iommu_pd_list))
-               return NULL;
-
-       spin_lock_irqsave(&iommu_pd_list_lock, flags);
+       u64 addr;
 
-       list_for_each_entry(entry, &iommu_pd_list, list) {
-               if (entry->target_dev == devid ||
-                   entry->target_dev == alias) {
-                       ret = entry;
-                       break;
-               }
+       for (addr = e->address_start; addr < e->address_end;
+            addr += PAGE_SIZE) {
+               if (addr < dma_dom->aperture_size)
+                       __set_bit(addr >> PAGE_SHIFT,
+                                 dma_dom->aperture[0]->bitmap);
        }
+}
+
+/*
+ * Inits the unity mappings required for a specific device
+ */
+static void init_unity_mappings_for_device(struct device *dev,
+                                          struct dma_ops_domain *dma_dom)
+{
+       struct unity_map_entry *e;
+       u16 devid;
 
-       spin_unlock_irqrestore(&iommu_pd_list_lock, flags);
+       devid = get_device_id(dev);
 
-       return ret;
+       list_for_each_entry(e, &amd_iommu_unity_map, list) {
+               if (!(devid >= e->devid_start && devid <= e->devid_end))
+                       continue;
+               alloc_unity_mapping(dma_dom, e);
+       }
 }
 
 /*
@@ -290,11 +294,23 @@ static bool check_device(struct device *dev)
 
 static void init_iommu_group(struct device *dev)
 {
+       struct dma_ops_domain *dma_domain;
+       struct iommu_domain *domain;
        struct iommu_group *group;
 
        group = iommu_group_get_for_dev(dev);
-       if (!IS_ERR(group))
-               iommu_group_put(group);
+       if (IS_ERR(group))
+               return;
+
+       domain = iommu_group_default_domain(group);
+       if (!domain)
+               goto out;
+
+       dma_domain = to_pdomain(domain)->priv;
+
+       init_unity_mappings_for_device(dev, dma_domain);
+out:
+       iommu_group_put(group);
 }
 
 static int __last_alias(struct pci_dev *pdev, u16 alias, void *data)
@@ -434,64 +450,15 @@ static void iommu_uninit_device(struct device *dev)
        /* Unlink from alias, it may change if another device is re-plugged */
        dev_data->alias_data = NULL;
 
+       /* Remove dma-ops */
+       dev->archdata.dma_ops = NULL;
+
        /*
         * We keep dev_data around for unplugged devices and reuse it when the
         * device is re-plugged - not doing so would introduce a ton of races.
         */
 }
 
-void __init amd_iommu_uninit_devices(void)
-{
-       struct iommu_dev_data *dev_data, *n;
-       struct pci_dev *pdev = NULL;
-
-       for_each_pci_dev(pdev) {
-
-               if (!check_device(&pdev->dev))
-                       continue;
-
-               iommu_uninit_device(&pdev->dev);
-       }
-
-       /* Free all of our dev_data structures */
-       list_for_each_entry_safe(dev_data, n, &dev_data_list, dev_data_list)
-               free_dev_data(dev_data);
-}
-
-int __init amd_iommu_init_devices(void)
-{
-       struct pci_dev *pdev = NULL;
-       int ret = 0;
-
-       for_each_pci_dev(pdev) {
-
-               if (!check_device(&pdev->dev))
-                       continue;
-
-               ret = iommu_init_device(&pdev->dev);
-               if (ret == -ENOTSUPP)
-                       iommu_ignore_device(&pdev->dev);
-               else if (ret)
-                       goto out_free;
-       }
-
-       /*
-        * Initialize IOMMU groups only after iommu_init_device() has
-        * had a chance to populate any IVRS defined aliases.
-        */
-       for_each_pci_dev(pdev) {
-               if (check_device(&pdev->dev))
-                       init_iommu_group(&pdev->dev);
-       }
-
-       return 0;
-
-out_free:
-
-       amd_iommu_uninit_devices();
-
-       return ret;
-}
 #ifdef CONFIG_AMD_IOMMU_STATS
 
 /*
@@ -1463,94 +1430,6 @@ static unsigned long iommu_unmap_page(struct protection_domain *dom,
        return unmapped;
 }
 
-/*
- * This function checks if a specific unity mapping entry is needed for
- * this specific IOMMU.
- */
-static int iommu_for_unity_map(struct amd_iommu *iommu,
-                              struct unity_map_entry *entry)
-{
-       u16 bdf, i;
-
-       for (i = entry->devid_start; i <= entry->devid_end; ++i) {
-               bdf = amd_iommu_alias_table[i];
-               if (amd_iommu_rlookup_table[bdf] == iommu)
-                       return 1;
-       }
-
-       return 0;
-}
-
-/*
- * This function actually applies the mapping to the page table of the
- * dma_ops domain.
- */
-static int dma_ops_unity_map(struct dma_ops_domain *dma_dom,
-                            struct unity_map_entry *e)
-{
-       u64 addr;
-       int ret;
-
-       for (addr = e->address_start; addr < e->address_end;
-            addr += PAGE_SIZE) {
-               ret = iommu_map_page(&dma_dom->domain, addr, addr, e->prot,
-                                    PAGE_SIZE);
-               if (ret)
-                       return ret;
-               /*
-                * if unity mapping is in aperture range mark the page
-                * as allocated in the aperture
-                */
-               if (addr < dma_dom->aperture_size)
-                       __set_bit(addr >> PAGE_SHIFT,
-                                 dma_dom->aperture[0]->bitmap);
-       }
-
-       return 0;
-}
-
-/*
- * Init the unity mappings for a specific IOMMU in the system
- *
- * Basically iterates over all unity mapping entries and applies them to
- * the default domain DMA of that IOMMU if necessary.
- */
-static int iommu_init_unity_mappings(struct amd_iommu *iommu)
-{
-       struct unity_map_entry *entry;
-       int ret;
-
-       list_for_each_entry(entry, &amd_iommu_unity_map, list) {
-               if (!iommu_for_unity_map(iommu, entry))
-                       continue;
-               ret = dma_ops_unity_map(iommu->default_dom, entry);
-               if (ret)
-                       return ret;
-       }
-
-       return 0;
-}
-
-/*
- * Inits the unity mappings required for a specific device
- */
-static int init_unity_mappings_for_device(struct dma_ops_domain *dma_dom,
-                                         u16 devid)
-{
-       struct unity_map_entry *e;
-       int ret;
-
-       list_for_each_entry(e, &amd_iommu_unity_map, list) {
-               if (!(devid >= e->devid_start && devid <= e->devid_end))
-                       continue;
-               ret = dma_ops_unity_map(dma_dom, e);
-               if (ret)
-                       return ret;
-       }
-
-       return 0;
-}
-
 /****************************************************************************
  *
  * The next functions belong to the address allocator for the dma_ops
@@ -1704,14 +1583,16 @@ static unsigned long dma_ops_area_alloc(struct device *dev,
        unsigned long next_bit = dom->next_address % APERTURE_RANGE_SIZE;
        int max_index = dom->aperture_size >> APERTURE_RANGE_SHIFT;
        int i = start >> APERTURE_RANGE_SHIFT;
-       unsigned long boundary_size;
+       unsigned long boundary_size, mask;
        unsigned long address = -1;
        unsigned long limit;
 
        next_bit >>= PAGE_SHIFT;
 
-       boundary_size = ALIGN(dma_get_seg_boundary(dev) + 1,
-                       PAGE_SIZE) >> PAGE_SHIFT;
+       mask = dma_get_seg_boundary(dev);
+
+       boundary_size = mask + 1 ? ALIGN(mask + 1, PAGE_SIZE) >> PAGE_SHIFT :
+                                  1UL << (BITS_PER_LONG - PAGE_SHIFT);
 
        for (;i < max_index; ++i) {
                unsigned long offset = dom->aperture[i]->offset >> PAGE_SHIFT;
@@ -1869,9 +1750,15 @@ static void free_pt_##LVL (unsigned long __pt)                   \
        pt = (u64 *)__pt;                                       \
                                                                \
        for (i = 0; i < 512; ++i) {                             \
+               /* PTE present? */                              \
                if (!IOMMU_PTE_PRESENT(pt[i]))                  \
                        continue;                               \
                                                                \
+               /* Large PTE? */                                \
+               if (PM_PTE_LEVEL(pt[i]) == 0 ||                 \
+                   PM_PTE_LEVEL(pt[i]) == 7)                   \
+                       continue;                               \
+                                                               \
                p = (unsigned long)IOMMU_PTE_PAGE(pt[i]);       \
                FN(p);                                          \
        }                                                       \
@@ -2008,7 +1895,6 @@ static struct dma_ops_domain *dma_ops_domain_alloc(void)
                goto free_dma_dom;
 
        dma_dom->need_flush = false;
-       dma_dom->target_dev = 0xffff;
 
        add_domain_to_list(&dma_dom->domain);
 
@@ -2373,110 +2259,67 @@ static void detach_device(struct device *dev)
        dev_data->ats.enabled = false;
 }
 
-/*
- * Find out the protection domain structure for a given PCI device. This
- * will give us the pointer to the page table root for example.
- */
-static struct protection_domain *domain_for_device(struct device *dev)
+static int amd_iommu_add_device(struct device *dev)
 {
        struct iommu_dev_data *dev_data;
-       struct protection_domain *dom = NULL;
-       unsigned long flags;
-
-       dev_data   = get_dev_data(dev);
-
-       if (dev_data->domain)
-               return dev_data->domain;
-
-       if (dev_data->alias_data != NULL) {
-               struct iommu_dev_data *alias_data = dev_data->alias_data;
-
-               read_lock_irqsave(&amd_iommu_devtable_lock, flags);
-               if (alias_data->domain != NULL) {
-                       __attach_device(dev_data, alias_data->domain);
-                       dom = alias_data->domain;
-               }
-               read_unlock_irqrestore(&amd_iommu_devtable_lock, flags);
-       }
-
-       return dom;
-}
-
-static int device_change_notifier(struct notifier_block *nb,
-                                 unsigned long action, void *data)
-{
-       struct dma_ops_domain *dma_domain;
-       struct protection_domain *domain;
-       struct iommu_dev_data *dev_data;
-       struct device *dev = data;
+       struct iommu_domain *domain;
        struct amd_iommu *iommu;
-       unsigned long flags;
        u16 devid;
+       int ret;
 
-       if (!check_device(dev))
+       if (!check_device(dev) || get_dev_data(dev))
                return 0;
 
-       devid    = get_device_id(dev);
-       iommu    = amd_iommu_rlookup_table[devid];
-       dev_data = get_dev_data(dev);
-
-       switch (action) {
-       case BUS_NOTIFY_ADD_DEVICE:
+       devid = get_device_id(dev);
+       iommu = amd_iommu_rlookup_table[devid];
 
-               iommu_init_device(dev);
-               init_iommu_group(dev);
+       ret = iommu_init_device(dev);
+       if (ret) {
+               if (ret != -ENOTSUPP)
+                       pr_err("Failed to initialize device %s - trying to proceed anyway\n",
+                               dev_name(dev));
 
-               /*
-                * dev_data is still NULL and
-                * got initialized in iommu_init_device
-                */
-               dev_data = get_dev_data(dev);
+               iommu_ignore_device(dev);
+               dev->archdata.dma_ops = &nommu_dma_ops;
+               goto out;
+       }
+       init_iommu_group(dev);
 
-               if (iommu_pass_through || dev_data->iommu_v2) {
-                       dev_data->passthrough = true;
-                       attach_device(dev, pt_domain);
-                       break;
-               }
+       dev_data = get_dev_data(dev);
 
-               domain = domain_for_device(dev);
+       BUG_ON(!dev_data);
 
-               /* allocate a protection domain if a device is added */
-               dma_domain = find_protection_domain(devid);
-               if (!dma_domain) {
-                       dma_domain = dma_ops_domain_alloc();
-                       if (!dma_domain)
-                               goto out;
-                       dma_domain->target_dev = devid;
-
-                       spin_lock_irqsave(&iommu_pd_list_lock, flags);
-                       list_add_tail(&dma_domain->list, &iommu_pd_list);
-                       spin_unlock_irqrestore(&iommu_pd_list_lock, flags);
-               }
+       if (dev_data->iommu_v2)
+               iommu_request_dm_for_dev(dev);
 
+       /* Domains are initialized for this device - have a look what we ended up with */
+       domain = iommu_get_domain_for_dev(dev);
+       if (domain->type == IOMMU_DOMAIN_IDENTITY) {
+               dev_data->passthrough = true;
+               dev->archdata.dma_ops = &nommu_dma_ops;
+       } else {
                dev->archdata.dma_ops = &amd_iommu_dma_ops;
-
-               break;
-       case BUS_NOTIFY_REMOVED_DEVICE:
-
-               iommu_uninit_device(dev);
-
-       default:
-               goto out;
        }
 
+out:
        iommu_completion_wait(iommu);
 
-out:
        return 0;
 }
 
-static struct notifier_block device_nb = {
-       .notifier_call = device_change_notifier,
-};
-
-void amd_iommu_init_notifier(void)
+static void amd_iommu_remove_device(struct device *dev)
 {
-       bus_register_notifier(&pci_bus_type, &device_nb);
+       struct amd_iommu *iommu;
+       u16 devid;
+
+       if (!check_device(dev))
+               return;
+
+       devid = get_device_id(dev);
+       iommu = amd_iommu_rlookup_table[devid];
+
+       iommu_uninit_device(dev);
+       iommu_completion_wait(iommu);
 }
 
 /*****************************************************************************
@@ -2495,28 +2338,20 @@ void amd_iommu_init_notifier(void)
 static struct protection_domain *get_domain(struct device *dev)
 {
        struct protection_domain *domain;
-       struct dma_ops_domain *dma_dom;
-       u16 devid = get_device_id(dev);
+       struct iommu_domain *io_domain;
 
        if (!check_device(dev))
                return ERR_PTR(-EINVAL);
 
-       domain = domain_for_device(dev);
-       if (domain != NULL && !dma_ops_domain(domain))
-               return ERR_PTR(-EBUSY);
-
-       if (domain != NULL)
-               return domain;
+       io_domain = iommu_get_domain_for_dev(dev);
+       if (!io_domain)
+               return NULL;
 
-       /* Device not bound yet - bind it */
-       dma_dom = find_protection_domain(devid);
-       if (!dma_dom)
-               dma_dom = amd_iommu_rlookup_table[devid]->default_dom;
-       attach_device(dev, &dma_dom->domain);
-       DUMP_printk("Using protection domain %d for device %s\n",
-                   dma_dom->domain.id, dev_name(dev));
+       domain = to_pdomain(io_domain);
+       if (!dma_ops_domain(domain))
+               return ERR_PTR(-EBUSY);
 
-       return &dma_dom->domain;
+       return domain;
 }
 
 static void update_device_table(struct protection_domain *domain)
@@ -2930,6 +2765,7 @@ static void *alloc_coherent(struct device *dev, size_t size,
        size      = PAGE_ALIGN(size);
        dma_mask  = dev->coherent_dma_mask;
        flag     &= ~(__GFP_DMA | __GFP_HIGHMEM | __GFP_DMA32);
+       flag     |= __GFP_ZERO;
 
        page = alloc_pages(flag | __GFP_NOWARN,  get_order(size));
        if (!page) {
@@ -3011,54 +2847,6 @@ static int amd_iommu_dma_supported(struct device *dev, u64 mask)
        return check_device(dev);
 }
 
-/*
- * The function for pre-allocating protection domains.
- *
- * If the driver core informs the DMA layer if a driver grabs a device
- * we don't need to preallocate the protection domains anymore.
- * For now we have to.
- */
-static void __init prealloc_protection_domains(void)
-{
-       struct iommu_dev_data *dev_data;
-       struct dma_ops_domain *dma_dom;
-       struct pci_dev *dev = NULL;
-       u16 devid;
-
-       for_each_pci_dev(dev) {
-
-               /* Do we handle this device? */
-               if (!check_device(&dev->dev))
-                       continue;
-
-               dev_data = get_dev_data(&dev->dev);
-               if (!amd_iommu_force_isolation && dev_data->iommu_v2) {
-                       /* Make sure passthrough domain is allocated */
-                       alloc_passthrough_domain();
-                       dev_data->passthrough = true;
-                       attach_device(&dev->dev, pt_domain);
-                       pr_info("AMD-Vi: Using passthrough domain for device %s\n",
-                               dev_name(&dev->dev));
-               }
-
-               /* Is there already any domain for it? */
-               if (domain_for_device(&dev->dev))
-                       continue;
-
-               devid = get_device_id(&dev->dev);
-
-               dma_dom = dma_ops_domain_alloc();
-               if (!dma_dom)
-                       continue;
-               init_unity_mappings_for_device(dma_dom, devid);
-               dma_dom->target_dev = devid;
-
-               attach_device(&dev->dev, &dma_dom->domain);
-
-               list_add_tail(&dma_dom->list, &iommu_pd_list);
-       }
-}
-
 static struct dma_map_ops amd_iommu_dma_ops = {
        .alloc = alloc_coherent,
        .free = free_coherent,
@@ -3069,76 +2857,16 @@ static struct dma_map_ops amd_iommu_dma_ops = {
        .dma_supported = amd_iommu_dma_supported,
 };
 
-static unsigned device_dma_ops_init(void)
+int __init amd_iommu_init_api(void)
 {
-       struct iommu_dev_data *dev_data;
-       struct pci_dev *pdev = NULL;
-       unsigned unhandled = 0;
-
-       for_each_pci_dev(pdev) {
-               if (!check_device(&pdev->dev)) {
-
-                       iommu_ignore_device(&pdev->dev);
-
-                       unhandled += 1;
-                       continue;
-               }
-
-               dev_data = get_dev_data(&pdev->dev);
-
-               if (!dev_data->passthrough)
-                       pdev->dev.archdata.dma_ops = &amd_iommu_dma_ops;
-               else
-                       pdev->dev.archdata.dma_ops = &nommu_dma_ops;
-       }
-
-       return unhandled;
-}
-
-/*
- * The function which clues the AMD IOMMU driver into dma_ops.
- */
-
-void __init amd_iommu_init_api(void)
-{
-       bus_set_iommu(&pci_bus_type, &amd_iommu_ops);
+       return bus_set_iommu(&pci_bus_type, &amd_iommu_ops);
 }
 
 int __init amd_iommu_init_dma_ops(void)
 {
-       struct amd_iommu *iommu;
-       int ret, unhandled;
-
-       /*
-        * first allocate a default protection domain for every IOMMU we
-        * found in the system. Devices not assigned to any other
-        * protection domain will be assigned to the default one.
-        */
-       for_each_iommu(iommu) {
-               iommu->default_dom = dma_ops_domain_alloc();
-               if (iommu->default_dom == NULL)
-                       return -ENOMEM;
-               iommu->default_dom->domain.flags |= PD_DEFAULT_MASK;
-               ret = iommu_init_unity_mappings(iommu);
-               if (ret)
-                       goto free_domains;
-       }
-
-       /*
-        * Pre-allocate the protection domains for each device.
-        */
-       prealloc_protection_domains();
-
        iommu_detected = 1;
        swiotlb = 0;
 
-       /* Make the driver finally visible to the drivers */
-       unhandled = device_dma_ops_init();
-       if (unhandled && max_pfn > MAX_DMA32_PFN) {
-               /* There are unhandled devices - initialize swiotlb for them */
-               swiotlb = 1;
-       }
-
        amd_iommu_stats_init();
 
        if (amd_iommu_unmap_flush)
@@ -3147,14 +2875,6 @@ int __init amd_iommu_init_dma_ops(void)
                pr_info("AMD-Vi: Lazy IO/TLB flushing enabled\n");
 
        return 0;
-
-free_domains:
-
-       for_each_iommu(iommu) {
-               dma_ops_domain_free(iommu->default_dom);
-       }
-
-       return ret;
 }
 
 /*****************************************************************************
@@ -3221,7 +2941,7 @@ out_err:
        return NULL;
 }
 
-static int __init alloc_passthrough_domain(void)
+static int alloc_passthrough_domain(void)
 {
        if (pt_domain != NULL)
                return 0;
@@ -3239,30 +2959,46 @@ static int __init alloc_passthrough_domain(void)
 static struct iommu_domain *amd_iommu_domain_alloc(unsigned type)
 {
        struct protection_domain *pdomain;
+       struct dma_ops_domain *dma_domain;
 
-       /* We only support unmanaged domains for now */
-       if (type != IOMMU_DOMAIN_UNMANAGED)
-               return NULL;
-
-       pdomain = protection_domain_alloc();
-       if (!pdomain)
-               goto out_free;
+       switch (type) {
+       case IOMMU_DOMAIN_UNMANAGED:
+               pdomain = protection_domain_alloc();
+               if (!pdomain)
+                       return NULL;
 
-       pdomain->mode    = PAGE_MODE_3_LEVEL;
-       pdomain->pt_root = (void *)get_zeroed_page(GFP_KERNEL);
-       if (!pdomain->pt_root)
-               goto out_free;
+               pdomain->mode    = PAGE_MODE_3_LEVEL;
+               pdomain->pt_root = (void *)get_zeroed_page(GFP_KERNEL);
+               if (!pdomain->pt_root) {
+                       protection_domain_free(pdomain);
+                       return NULL;
+               }
 
-       pdomain->domain.geometry.aperture_start = 0;
-       pdomain->domain.geometry.aperture_end   = ~0ULL;
-       pdomain->domain.geometry.force_aperture = true;
+               pdomain->domain.geometry.aperture_start = 0;
+               pdomain->domain.geometry.aperture_end   = ~0ULL;
+               pdomain->domain.geometry.force_aperture = true;
 
-       return &pdomain->domain;
+               break;
+       case IOMMU_DOMAIN_DMA:
+               dma_domain = dma_ops_domain_alloc();
+               if (!dma_domain) {
+                       pr_err("AMD-Vi: Failed to allocate\n");
+                       return NULL;
+               }
+               pdomain = &dma_domain->domain;
+               break;
+       case IOMMU_DOMAIN_IDENTITY:
+               pdomain = protection_domain_alloc();
+               if (!pdomain)
+                       return NULL;
 
-out_free:
-       protection_domain_free(pdomain);
+               pdomain->mode = PAGE_MODE_NONE;
+               break;
+       default:
+               return NULL;
+       }
 
-       return NULL;
+       return &pdomain->domain;
 }
 
 static void amd_iommu_domain_free(struct iommu_domain *dom)
@@ -3412,6 +3148,47 @@ static bool amd_iommu_capable(enum iommu_cap cap)
        return false;
 }
 
+static void amd_iommu_get_dm_regions(struct device *dev,
+                                    struct list_head *head)
+{
+       struct unity_map_entry *entry;
+       u16 devid;
+
+       devid = get_device_id(dev);
+
+       list_for_each_entry(entry, &amd_iommu_unity_map, list) {
+               struct iommu_dm_region *region;
+
+               if (devid < entry->devid_start || devid > entry->devid_end)
+                       continue;
+
+               region = kzalloc(sizeof(*region), GFP_KERNEL);
+               if (!region) {
+                       pr_err("Out of memory allocating dm-regions for %s\n",
+                               dev_name(dev));
+                       return;
+               }
+
+               region->start = entry->address_start;
+               region->length = entry->address_end - entry->address_start;
+               if (entry->prot & IOMMU_PROT_IR)
+                       region->prot |= IOMMU_READ;
+               if (entry->prot & IOMMU_PROT_IW)
+                       region->prot |= IOMMU_WRITE;
+
+               list_add_tail(&region->list, head);
+       }
+}
+
+static void amd_iommu_put_dm_regions(struct device *dev,
+                                    struct list_head *head)
+{
+       struct iommu_dm_region *entry, *next;
+
+       list_for_each_entry_safe(entry, next, head, list)
+               kfree(entry);
+}
+
 static const struct iommu_ops amd_iommu_ops = {
        .capable = amd_iommu_capable,
        .domain_alloc = amd_iommu_domain_alloc,
@@ -3422,6 +3199,10 @@ static const struct iommu_ops amd_iommu_ops = {
        .unmap = amd_iommu_unmap,
        .map_sg = default_iommu_map_sg,
        .iova_to_phys = amd_iommu_iova_to_phys,
+       .add_device = amd_iommu_add_device,
+       .remove_device = amd_iommu_remove_device,
+       .get_dm_regions = amd_iommu_get_dm_regions,
+       .put_dm_regions = amd_iommu_put_dm_regions,
        .pgsize_bitmap  = AMD_IOMMU_PGSIZES,
 };
 
@@ -3851,6 +3632,21 @@ union irte {
        } fields;
 };
 
+struct irq_2_irte {
+       u16 devid; /* Device ID for IRTE table */
+       u16 index; /* Index into IRTE table*/
+};
+
+struct amd_ir_data {
+       struct irq_2_irte                       irq_2_irte;
+       union irte                              irte_entry;
+       union {
+               struct msi_msg                  msi_entry;
+       };
+};
+
+static struct irq_chip amd_ir_chip;
+
 #define DTE_IRQ_PHYS_ADDR_MASK (((1ULL << 45)-1) << 6)
 #define DTE_IRQ_REMAP_INTCTL    (2ULL << 60)
 #define DTE_IRQ_TABLE_LEN       (8ULL << 1)
@@ -3944,7 +3740,7 @@ out_unlock:
        return table;
 }
 
-static int alloc_irq_index(struct irq_cfg *cfg, u16 devid, int count)
+static int alloc_irq_index(u16 devid, int count)
 {
        struct irq_remap_table *table;
        unsigned long flags;
@@ -3966,18 +3762,10 @@ static int alloc_irq_index(struct irq_cfg *cfg, u16 devid, int count)
                        c = 0;
 
                if (c == count) {
-                       struct irq_2_irte *irte_info;
-
                        for (; c != 0; --c)
                                table->table[index - c + 1] = IRTE_ALLOCATED;
 
                        index -= count - 1;
-
-                       cfg->remapped         = 1;
-                       irte_info             = &cfg->irq_2_irte;
-                       irte_info->devid      = devid;
-                       irte_info->index      = index;
-
                        goto out;
                }
        }
@@ -3990,22 +3778,6 @@ out:
        return index;
 }
 
-static int get_irte(u16 devid, int index, union irte *irte)
-{
-       struct irq_remap_table *table;
-       unsigned long flags;
-
-       table = get_irq_table(devid, false);
-       if (!table)
-               return -ENOMEM;
-
-       spin_lock_irqsave(&table->lock, flags);
-       irte->val = table->table[index];
-       spin_unlock_irqrestore(&table->lock, flags);
-
-       return 0;
-}
-
 static int modify_irte(u16 devid, int index, union irte irte)
 {
        struct irq_remap_table *table;
@@ -4052,243 +3824,316 @@ static void free_irte(u16 devid, int index)
        iommu_completion_wait(iommu);
 }
 
-static int setup_ioapic_entry(int irq, struct IO_APIC_route_entry *entry,
-                             unsigned int destination, int vector,
-                             struct io_apic_irq_attr *attr)
+static int get_devid(struct irq_alloc_info *info)
 {
-       struct irq_remap_table *table;
-       struct irq_2_irte *irte_info;
-       struct irq_cfg *cfg;
-       union irte irte;
-       int ioapic_id;
-       int index;
-       int devid;
-       int ret;
+       int devid = -1;
 
-       cfg = irq_cfg(irq);
-       if (!cfg)
-               return -EINVAL;
-
-       irte_info = &cfg->irq_2_irte;
-       ioapic_id = mpc_ioapic_id(attr->ioapic);
-       devid     = get_ioapic_devid(ioapic_id);
-
-       if (devid < 0)
-               return devid;
-
-       table = get_irq_table(devid, true);
-       if (table == NULL)
-               return -ENOMEM;
-
-       index = attr->ioapic_pin;
-
-       /* Setup IRQ remapping info */
-       cfg->remapped         = 1;
-       irte_info->devid      = devid;
-       irte_info->index      = index;
+       switch (info->type) {
+       case X86_IRQ_ALLOC_TYPE_IOAPIC:
+               devid     = get_ioapic_devid(info->ioapic_id);
+               break;
+       case X86_IRQ_ALLOC_TYPE_HPET:
+               devid     = get_hpet_devid(info->hpet_id);
+               break;
+       case X86_IRQ_ALLOC_TYPE_MSI:
+       case X86_IRQ_ALLOC_TYPE_MSIX:
+               devid = get_device_id(&info->msi_dev->dev);
+               break;
+       default:
+               BUG_ON(1);
+               break;
+       }
 
-       /* Setup IRTE for IOMMU */
-       irte.val                = 0;
-       irte.fields.vector      = vector;
-       irte.fields.int_type    = apic->irq_delivery_mode;
-       irte.fields.destination = destination;
-       irte.fields.dm          = apic->irq_dest_mode;
-       irte.fields.valid       = 1;
-
-       ret = modify_irte(devid, index, irte);
-       if (ret)
-               return ret;
+       return devid;
+}
 
-       /* Setup IOAPIC entry */
-       memset(entry, 0, sizeof(*entry));
+static struct irq_domain *get_ir_irq_domain(struct irq_alloc_info *info)
+{
+       struct amd_iommu *iommu;
+       int devid;
 
-       entry->vector        = index;
-       entry->mask          = 0;
-       entry->trigger       = attr->trigger;
-       entry->polarity      = attr->polarity;
+       if (!info)
+               return NULL;
 
-       /*
-        * Mask level triggered irqs.
-        */
-       if (attr->trigger)
-               entry->mask = 1;
+       devid = get_devid(info);
+       if (devid >= 0) {
+               iommu = amd_iommu_rlookup_table[devid];
+               if (iommu)
+                       return iommu->ir_domain;
+       }
 
-       return 0;
+       return NULL;
 }
 
-static int set_affinity(struct irq_data *data, const struct cpumask *mask,
-                       bool force)
+static struct irq_domain *get_irq_domain(struct irq_alloc_info *info)
 {
-       struct irq_2_irte *irte_info;
-       unsigned int dest, irq;
-       struct irq_cfg *cfg;
-       union irte irte;
-       int err;
-
-       if (!config_enabled(CONFIG_SMP))
-               return -1;
-
-       cfg       = irqd_cfg(data);
-       irq       = data->irq;
-       irte_info = &cfg->irq_2_irte;
+       struct amd_iommu *iommu;
+       int devid;
 
-       if (!cpumask_intersects(mask, cpu_online_mask))
-               return -EINVAL;
+       if (!info)
+               return NULL;
 
-       if (get_irte(irte_info->devid, irte_info->index, &irte))
-               return -EBUSY;
+       switch (info->type) {
+       case X86_IRQ_ALLOC_TYPE_MSI:
+       case X86_IRQ_ALLOC_TYPE_MSIX:
+               devid = get_device_id(&info->msi_dev->dev);
+               if (devid >= 0) {
+                       iommu = amd_iommu_rlookup_table[devid];
+                       if (iommu)
+                               return iommu->msi_domain;
+               }
+               break;
+       default:
+               break;
+       }
 
-       if (assign_irq_vector(irq, cfg, mask))
-               return -EBUSY;
+       return NULL;
+}
 
-       err = apic->cpu_mask_to_apicid_and(cfg->domain, mask, &dest);
-       if (err) {
-               if (assign_irq_vector(irq, cfg, data->affinity))
-                       pr_err("AMD-Vi: Failed to recover vector for irq %d\n", irq);
-               return err;
-       }
+struct irq_remap_ops amd_iommu_irq_ops = {
+       .prepare                = amd_iommu_prepare,
+       .enable                 = amd_iommu_enable,
+       .disable                = amd_iommu_disable,
+       .reenable               = amd_iommu_reenable,
+       .enable_faulting        = amd_iommu_enable_faulting,
+       .get_ir_irq_domain      = get_ir_irq_domain,
+       .get_irq_domain         = get_irq_domain,
+};
 
-       irte.fields.vector      = cfg->vector;
-       irte.fields.destination = dest;
+static void irq_remapping_prepare_irte(struct amd_ir_data *data,
+                                      struct irq_cfg *irq_cfg,
+                                      struct irq_alloc_info *info,
+                                      int devid, int index, int sub_handle)
+{
+       struct irq_2_irte *irte_info = &data->irq_2_irte;
+       struct msi_msg *msg = &data->msi_entry;
+       union irte *irte = &data->irte_entry;
+       struct IO_APIC_route_entry *entry;
 
-       modify_irte(irte_info->devid, irte_info->index, irte);
+       data->irq_2_irte.devid = devid;
+       data->irq_2_irte.index = index + sub_handle;
 
-       if (cfg->move_in_progress)
-               send_cleanup_vector(cfg);
+       /* Setup IRTE for IOMMU */
+       irte->val = 0;
+       irte->fields.vector      = irq_cfg->vector;
+       irte->fields.int_type    = apic->irq_delivery_mode;
+       irte->fields.destination = irq_cfg->dest_apicid;
+       irte->fields.dm          = apic->irq_dest_mode;
+       irte->fields.valid       = 1;
+
+       switch (info->type) {
+       case X86_IRQ_ALLOC_TYPE_IOAPIC:
+               /* Setup IOAPIC entry */
+               entry = info->ioapic_entry;
+               info->ioapic_entry = NULL;
+               memset(entry, 0, sizeof(*entry));
+               entry->vector        = index;
+               entry->mask          = 0;
+               entry->trigger       = info->ioapic_trigger;
+               entry->polarity      = info->ioapic_polarity;
+               /* Mask level triggered irqs. */
+               if (info->ioapic_trigger)
+                       entry->mask = 1;
+               break;
 
-       cpumask_copy(data->affinity, mask);
+       case X86_IRQ_ALLOC_TYPE_HPET:
+       case X86_IRQ_ALLOC_TYPE_MSI:
+       case X86_IRQ_ALLOC_TYPE_MSIX:
+               msg->address_hi = MSI_ADDR_BASE_HI;
+               msg->address_lo = MSI_ADDR_BASE_LO;
+               msg->data = irte_info->index;
+               break;
 
-       return 0;
+       default:
+               BUG_ON(1);
+               break;
+       }
 }
 
-static int free_irq(int irq)
+static int irq_remapping_alloc(struct irq_domain *domain, unsigned int virq,
+                              unsigned int nr_irqs, void *arg)
 {
-       struct irq_2_irte *irte_info;
+       struct irq_alloc_info *info = arg;
+       struct irq_data *irq_data;
+       struct amd_ir_data *data;
        struct irq_cfg *cfg;
+       int i, ret, devid;
+       int index = -1;
 
-       cfg = irq_cfg(irq);
-       if (!cfg)
+       if (!info)
+               return -EINVAL;
+       if (nr_irqs > 1 && info->type != X86_IRQ_ALLOC_TYPE_MSI &&
+           info->type != X86_IRQ_ALLOC_TYPE_MSIX)
                return -EINVAL;
 
-       irte_info = &cfg->irq_2_irte;
-
-       free_irte(irte_info->devid, irte_info->index);
+       /*
+        * With IRQ remapping enabled, don't need contiguous CPU vectors
+        * to support multiple MSI interrupts.
+        */
+       if (info->type == X86_IRQ_ALLOC_TYPE_MSI)
+               info->flags &= ~X86_IRQ_ALLOC_CONTIGUOUS_VECTORS;
 
-       return 0;
-}
+       devid = get_devid(info);
+       if (devid < 0)
+               return -EINVAL;
 
-static void compose_msi_msg(struct pci_dev *pdev,
-                           unsigned int irq, unsigned int dest,
-                           struct msi_msg *msg, u8 hpet_id)
-{
-       struct irq_2_irte *irte_info;
-       struct irq_cfg *cfg;
-       union irte irte;
+       ret = irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, arg);
+       if (ret < 0)
+               return ret;
 
-       cfg = irq_cfg(irq);
-       if (!cfg)
-               return;
+       ret = -ENOMEM;
+       data = kzalloc(sizeof(*data), GFP_KERNEL);
+       if (!data)
+               goto out_free_parent;
 
-       irte_info = &cfg->irq_2_irte;
+       if (info->type == X86_IRQ_ALLOC_TYPE_IOAPIC) {
+               if (get_irq_table(devid, true))
+                       index = info->ioapic_pin;
+               else
+                       ret = -ENOMEM;
+       } else {
+               index = alloc_irq_index(devid, nr_irqs);
+       }
+       if (index < 0) {
+               pr_warn("Failed to allocate IRTE\n");
+               kfree(data);
+               goto out_free_parent;
+       }
 
-       irte.val                = 0;
-       irte.fields.vector      = cfg->vector;
-       irte.fields.int_type    = apic->irq_delivery_mode;
-       irte.fields.destination = dest;
-       irte.fields.dm          = apic->irq_dest_mode;
-       irte.fields.valid       = 1;
+       for (i = 0; i < nr_irqs; i++) {
+               irq_data = irq_domain_get_irq_data(domain, virq + i);
+               cfg = irqd_cfg(irq_data);
+               if (!irq_data || !cfg) {
+                       ret = -EINVAL;
+                       goto out_free_data;
+               }
 
-       modify_irte(irte_info->devid, irte_info->index, irte);
+               if (i > 0) {
+                       data = kzalloc(sizeof(*data), GFP_KERNEL);
+                       if (!data)
+                               goto out_free_data;
+               }
+               irq_data->hwirq = (devid << 16) + i;
+               irq_data->chip_data = data;
+               irq_data->chip = &amd_ir_chip;
+               irq_remapping_prepare_irte(data, cfg, info, devid, index, i);
+               irq_set_status_flags(virq + i, IRQ_MOVE_PCNTXT);
+       }
+       return 0;
 
-       msg->address_hi = MSI_ADDR_BASE_HI;
-       msg->address_lo = MSI_ADDR_BASE_LO;
-       msg->data       = irte_info->index;
+out_free_data:
+       for (i--; i >= 0; i--) {
+               irq_data = irq_domain_get_irq_data(domain, virq + i);
+               if (irq_data)
+                       kfree(irq_data->chip_data);
+       }
+       for (i = 0; i < nr_irqs; i++)
+               free_irte(devid, index + i);
+out_free_parent:
+       irq_domain_free_irqs_common(domain, virq, nr_irqs);
+       return ret;
 }
 
-static int msi_alloc_irq(struct pci_dev *pdev, int irq, int nvec)
+static void irq_remapping_free(struct irq_domain *domain, unsigned int virq,
+                              unsigned int nr_irqs)
 {
-       struct irq_cfg *cfg;
-       int index;
-       u16 devid;
-
-       if (!pdev)
-               return -EINVAL;
+       struct irq_2_irte *irte_info;
+       struct irq_data *irq_data;
+       struct amd_ir_data *data;
+       int i;
 
-       cfg = irq_cfg(irq);
-       if (!cfg)
-               return -EINVAL;
+       for (i = 0; i < nr_irqs; i++) {
+               irq_data = irq_domain_get_irq_data(domain, virq  + i);
+               if (irq_data && irq_data->chip_data) {
+                       data = irq_data->chip_data;
+                       irte_info = &data->irq_2_irte;
+                       free_irte(irte_info->devid, irte_info->index);
+                       kfree(data);
+               }
+       }
+       irq_domain_free_irqs_common(domain, virq, nr_irqs);
+}
 
-       devid = get_device_id(&pdev->dev);
-       index = alloc_irq_index(cfg, devid, nvec);
+static void irq_remapping_activate(struct irq_domain *domain,
+                                  struct irq_data *irq_data)
+{
+       struct amd_ir_data *data = irq_data->chip_data;
+       struct irq_2_irte *irte_info = &data->irq_2_irte;
 
-       return index < 0 ? MAX_IRQS_PER_TABLE : index;
+       modify_irte(irte_info->devid, irte_info->index, data->irte_entry);
 }
 
-static int msi_setup_irq(struct pci_dev *pdev, unsigned int irq,
-                        int index, int offset)
+static void irq_remapping_deactivate(struct irq_domain *domain,
+                                    struct irq_data *irq_data)
 {
-       struct irq_2_irte *irte_info;
-       struct irq_cfg *cfg;
-       u16 devid;
+       struct amd_ir_data *data = irq_data->chip_data;
+       struct irq_2_irte *irte_info = &data->irq_2_irte;
+       union irte entry;
 
-       if (!pdev)
-               return -EINVAL;
+       entry.val = 0;
+       modify_irte(irte_info->devid, irte_info->index, data->irte_entry);
+}
 
-       cfg = irq_cfg(irq);
-       if (!cfg)
-               return -EINVAL;
+static struct irq_domain_ops amd_ir_domain_ops = {
+       .alloc = irq_remapping_alloc,
+       .free = irq_remapping_free,
+       .activate = irq_remapping_activate,
+       .deactivate = irq_remapping_deactivate,
+};
 
-       if (index >= MAX_IRQS_PER_TABLE)
-               return 0;
+static int amd_ir_set_affinity(struct irq_data *data,
+                              const struct cpumask *mask, bool force)
+{
+       struct amd_ir_data *ir_data = data->chip_data;
+       struct irq_2_irte *irte_info = &ir_data->irq_2_irte;
+       struct irq_cfg *cfg = irqd_cfg(data);
+       struct irq_data *parent = data->parent_data;
+       int ret;
+
+       ret = parent->chip->irq_set_affinity(parent, mask, force);
+       if (ret < 0 || ret == IRQ_SET_MASK_OK_DONE)
+               return ret;
 
-       devid           = get_device_id(&pdev->dev);
-       irte_info       = &cfg->irq_2_irte;
+       /*
+        * Atomically updates the IRTE with the new destination, vector
+        * and flushes the interrupt entry cache.
+        */
+       ir_data->irte_entry.fields.vector = cfg->vector;
+       ir_data->irte_entry.fields.destination = cfg->dest_apicid;
+       modify_irte(irte_info->devid, irte_info->index, ir_data->irte_entry);
 
-       cfg->remapped         = 1;
-       irte_info->devid      = devid;
-       irte_info->index      = index + offset;
+       /*
+        * After this point, all the interrupts will start arriving
+        * at the new destination. So, time to cleanup the previous
+        * vector allocation.
+        */
+       send_cleanup_vector(cfg);
 
-       return 0;
+       return IRQ_SET_MASK_OK_DONE;
 }
 
-static int alloc_hpet_msi(unsigned int irq, unsigned int id)
+static void ir_compose_msi_msg(struct irq_data *irq_data, struct msi_msg *msg)
 {
-       struct irq_2_irte *irte_info;
-       struct irq_cfg *cfg;
-       int index, devid;
+       struct amd_ir_data *ir_data = irq_data->chip_data;
 
-       cfg = irq_cfg(irq);
-       if (!cfg)
-               return -EINVAL;
+       *msg = ir_data->msi_entry;
+}
 
-       irte_info = &cfg->irq_2_irte;
-       devid     = get_hpet_devid(id);
-       if (devid < 0)
-               return devid;
+static struct irq_chip amd_ir_chip = {
+       .irq_ack = ir_ack_apic_edge,
+       .irq_set_affinity = amd_ir_set_affinity,
+       .irq_compose_msi_msg = ir_compose_msi_msg,
+};
 
-       index = alloc_irq_index(cfg, devid, 1);
-       if (index < 0)
-               return index;
+int amd_iommu_create_irq_domain(struct amd_iommu *iommu)
+{
+       iommu->ir_domain = irq_domain_add_tree(NULL, &amd_ir_domain_ops, iommu);
+       if (!iommu->ir_domain)
+               return -ENOMEM;
 
-       cfg->remapped         = 1;
-       irte_info->devid      = devid;
-       irte_info->index      = index;
+       iommu->ir_domain->parent = arch_get_ir_parent_domain();
+       iommu->msi_domain = arch_create_msi_irq_domain(iommu->ir_domain);
 
        return 0;
 }
-
-struct irq_remap_ops amd_iommu_irq_ops = {
-       .prepare                = amd_iommu_prepare,
-       .enable                 = amd_iommu_enable,
-       .disable                = amd_iommu_disable,
-       .reenable               = amd_iommu_reenable,
-       .enable_faulting        = amd_iommu_enable_faulting,
-       .setup_ioapic_entry     = setup_ioapic_entry,
-       .set_affinity           = set_affinity,
-       .free_irq               = free_irq,
-       .compose_msi_msg        = compose_msi_msg,
-       .msi_alloc_irq          = msi_alloc_irq,
-       .msi_setup_irq          = msi_setup_irq,
-       .alloc_hpet_msi         = alloc_hpet_msi,
-};
 #endif
index 450ef5001a65ab3bea19e1a9648324eea9951ede..dbda9ae68c5d70fff7926e39a2ef506b7165bf65 100644 (file)
@@ -226,6 +226,7 @@ static enum iommu_init_state init_state = IOMMU_START_STATE;
 
 static int amd_iommu_enable_interrupts(void);
 static int __init iommu_go_to_state(enum iommu_init_state state);
+static void init_device_table_dma(void);
 
 static inline void update_last_devid(u16 devid)
 {
@@ -1124,6 +1125,10 @@ static int __init init_iommu_one(struct amd_iommu *iommu, struct ivhd_header *h)
        if (ret)
                return ret;
 
+       ret = amd_iommu_create_irq_domain(iommu);
+       if (ret)
+               return ret;
+
        /*
         * Make sure IOMMU is not considered to translate itself. The IVRS
         * table tells us so, but this is a lie!
@@ -1385,9 +1390,15 @@ static int __init amd_iommu_init_pci(void)
                        break;
        }
 
-       ret = amd_iommu_init_devices();
+       init_device_table_dma();
+
+       for_each_iommu(iommu)
+               iommu_flush_all_caches(iommu);
 
-       print_iommu_info();
+       ret = amd_iommu_init_api();
+
+       if (!ret)
+               print_iommu_info();
 
        return ret;
 }
@@ -1825,8 +1836,6 @@ static bool __init check_ioapic_information(void)
 
 static void __init free_dma_resources(void)
 {
-       amd_iommu_uninit_devices();
-
        free_pages((unsigned long)amd_iommu_pd_alloc_bitmap,
                   get_order(MAX_DOMAIN_ID/8));
 
@@ -2019,27 +2028,10 @@ static bool detect_ivrs(void)
 
 static int amd_iommu_init_dma(void)
 {
-       struct amd_iommu *iommu;
-       int ret;
-
        if (iommu_pass_through)
-               ret = amd_iommu_init_passthrough();
+               return amd_iommu_init_passthrough();
        else
-               ret = amd_iommu_init_dma_ops();
-
-       if (ret)
-               return ret;
-
-       init_device_table_dma();
-
-       for_each_iommu(iommu)
-               iommu_flush_all_caches(iommu);
-
-       amd_iommu_init_api();
-
-       amd_iommu_init_notifier();
-
-       return 0;
+               return amd_iommu_init_dma_ops();
 }
 
 /****************************************************************************
index 72b0fd455e2444cc12a9ee2b678c6b0456c80503..0bd9eb374462c693292c826de41b7c8d8d9ab1e4 100644 (file)
@@ -30,7 +30,7 @@ extern void amd_iommu_reset_cmd_buffer(struct amd_iommu *iommu);
 extern int amd_iommu_init_devices(void);
 extern void amd_iommu_uninit_devices(void);
 extern void amd_iommu_init_notifier(void);
-extern void amd_iommu_init_api(void);
+extern int amd_iommu_init_api(void);
 
 /* Needed for interrupt remapping */
 extern int amd_iommu_prepare(void);
@@ -62,6 +62,15 @@ extern u8 amd_iommu_pc_get_max_counters(u16 devid);
 extern int amd_iommu_pc_get_set_reg_val(u16 devid, u8 bank, u8 cntr, u8 fxn,
                                    u64 *value, bool is_write);
 
+#ifdef CONFIG_IRQ_REMAP
+extern int amd_iommu_create_irq_domain(struct amd_iommu *iommu);
+#else
+static inline int amd_iommu_create_irq_domain(struct amd_iommu *iommu)
+{
+       return 0;
+}
+#endif
+
 #define PPR_SUCCESS                    0x0
 #define PPR_INVALID                    0x1
 #define PPR_FAILURE                    0xf
index 05030e523771a6ee3befbe890ed45b47a86f8f7f..f65908841be0184db61f049bc8e2b5a622ca2fbe 100644 (file)
@@ -398,6 +398,7 @@ struct amd_iommu_fault {
 
 
 struct iommu_domain;
+struct irq_domain;
 
 /*
  * This structure contains generic data for  IOMMU protection domains
@@ -446,8 +447,6 @@ struct aperture_range {
  * Data container for a dma_ops specific protection domain
  */
 struct dma_ops_domain {
-       struct list_head list;
-
        /* generic protection domain information */
        struct protection_domain domain;
 
@@ -462,12 +461,6 @@ struct dma_ops_domain {
 
        /* This will be set to true when TLB needs to be flushed */
        bool need_flush;
-
-       /*
-        * if this is a preallocated domain, keep the device for which it was
-        * preallocated in this variable
-        */
-       u16 target_dev;
 };
 
 /*
@@ -552,9 +545,6 @@ struct amd_iommu {
        /* if one, we need to send a completion wait command */
        bool need_sync;
 
-       /* default dma_ops domain for that IOMMU */
-       struct dma_ops_domain *default_dom;
-
        /* IOMMU sysfs device */
        struct device *iommu_dev;
 
@@ -579,6 +569,10 @@ struct amd_iommu {
        /* The maximum PC banks and counters/bank (PCSup=1) */
        u8 max_banks;
        u8 max_counters;
+#ifdef CONFIG_IRQ_REMAP
+       struct irq_domain *ir_domain;
+       struct irq_domain *msi_domain;
+#endif
 };
 
 struct devid_map {
diff --git a/drivers/iommu/arm-smmu-v3.c b/drivers/iommu/arm-smmu-v3.c
new file mode 100644 (file)
index 0000000..f141301
--- /dev/null
@@ -0,0 +1,2670 @@
+/*
+ * IOMMU API for ARM architected SMMUv3 implementations.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright (C) 2015 ARM Limited
+ *
+ * Author: Will Deacon <will.deacon@arm.com>
+ *
+ * This driver is powered by bad coffee and bombay mix.
+ */
+
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/iommu.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+
+#include "io-pgtable.h"
+
+/* MMIO registers */
+#define ARM_SMMU_IDR0                  0x0
+#define IDR0_ST_LVL_SHIFT              27
+#define IDR0_ST_LVL_MASK               0x3
+#define IDR0_ST_LVL_2LVL               (1 << IDR0_ST_LVL_SHIFT)
+#define IDR0_STALL_MODEL               (3 << 24)
+#define IDR0_TTENDIAN_SHIFT            21
+#define IDR0_TTENDIAN_MASK             0x3
+#define IDR0_TTENDIAN_LE               (2 << IDR0_TTENDIAN_SHIFT)
+#define IDR0_TTENDIAN_BE               (3 << IDR0_TTENDIAN_SHIFT)
+#define IDR0_TTENDIAN_MIXED            (0 << IDR0_TTENDIAN_SHIFT)
+#define IDR0_CD2L                      (1 << 19)
+#define IDR0_VMID16                    (1 << 18)
+#define IDR0_PRI                       (1 << 16)
+#define IDR0_SEV                       (1 << 14)
+#define IDR0_MSI                       (1 << 13)
+#define IDR0_ASID16                    (1 << 12)
+#define IDR0_ATS                       (1 << 10)
+#define IDR0_HYP                       (1 << 9)
+#define IDR0_COHACC                    (1 << 4)
+#define IDR0_TTF_SHIFT                 2
+#define IDR0_TTF_MASK                  0x3
+#define IDR0_TTF_AARCH64               (2 << IDR0_TTF_SHIFT)
+#define IDR0_S1P                       (1 << 1)
+#define IDR0_S2P                       (1 << 0)
+
+#define ARM_SMMU_IDR1                  0x4
+#define IDR1_TABLES_PRESET             (1 << 30)
+#define IDR1_QUEUES_PRESET             (1 << 29)
+#define IDR1_REL                       (1 << 28)
+#define IDR1_CMDQ_SHIFT                        21
+#define IDR1_CMDQ_MASK                 0x1f
+#define IDR1_EVTQ_SHIFT                        16
+#define IDR1_EVTQ_MASK                 0x1f
+#define IDR1_PRIQ_SHIFT                        11
+#define IDR1_PRIQ_MASK                 0x1f
+#define IDR1_SSID_SHIFT                        6
+#define IDR1_SSID_MASK                 0x1f
+#define IDR1_SID_SHIFT                 0
+#define IDR1_SID_MASK                  0x3f
+
+#define ARM_SMMU_IDR5                  0x14
+#define IDR5_STALL_MAX_SHIFT           16
+#define IDR5_STALL_MAX_MASK            0xffff
+#define IDR5_GRAN64K                   (1 << 6)
+#define IDR5_GRAN16K                   (1 << 5)
+#define IDR5_GRAN4K                    (1 << 4)
+#define IDR5_OAS_SHIFT                 0
+#define IDR5_OAS_MASK                  0x7
+#define IDR5_OAS_32_BIT                        (0 << IDR5_OAS_SHIFT)
+#define IDR5_OAS_36_BIT                        (1 << IDR5_OAS_SHIFT)
+#define IDR5_OAS_40_BIT                        (2 << IDR5_OAS_SHIFT)
+#define IDR5_OAS_42_BIT                        (3 << IDR5_OAS_SHIFT)
+#define IDR5_OAS_44_BIT                        (4 << IDR5_OAS_SHIFT)
+#define IDR5_OAS_48_BIT                        (5 << IDR5_OAS_SHIFT)
+
+#define ARM_SMMU_CR0                   0x20
+#define CR0_CMDQEN                     (1 << 3)
+#define CR0_EVTQEN                     (1 << 2)
+#define CR0_PRIQEN                     (1 << 1)
+#define CR0_SMMUEN                     (1 << 0)
+
+#define ARM_SMMU_CR0ACK                        0x24
+
+#define ARM_SMMU_CR1                   0x28
+#define CR1_SH_NSH                     0
+#define CR1_SH_OSH                     2
+#define CR1_SH_ISH                     3
+#define CR1_CACHE_NC                   0
+#define CR1_CACHE_WB                   1
+#define CR1_CACHE_WT                   2
+#define CR1_TABLE_SH_SHIFT             10
+#define CR1_TABLE_OC_SHIFT             8
+#define CR1_TABLE_IC_SHIFT             6
+#define CR1_QUEUE_SH_SHIFT             4
+#define CR1_QUEUE_OC_SHIFT             2
+#define CR1_QUEUE_IC_SHIFT             0
+
+#define ARM_SMMU_CR2                   0x2c
+#define CR2_PTM                                (1 << 2)
+#define CR2_RECINVSID                  (1 << 1)
+#define CR2_E2H                                (1 << 0)
+
+#define ARM_SMMU_IRQ_CTRL              0x50
+#define IRQ_CTRL_EVTQ_IRQEN            (1 << 2)
+#define IRQ_CTRL_GERROR_IRQEN          (1 << 0)
+
+#define ARM_SMMU_IRQ_CTRLACK           0x54
+
+#define ARM_SMMU_GERROR                        0x60
+#define GERROR_SFM_ERR                 (1 << 8)
+#define GERROR_MSI_GERROR_ABT_ERR      (1 << 7)
+#define GERROR_MSI_PRIQ_ABT_ERR                (1 << 6)
+#define GERROR_MSI_EVTQ_ABT_ERR                (1 << 5)
+#define GERROR_MSI_CMDQ_ABT_ERR                (1 << 4)
+#define GERROR_PRIQ_ABT_ERR            (1 << 3)
+#define GERROR_EVTQ_ABT_ERR            (1 << 2)
+#define GERROR_CMDQ_ERR                        (1 << 0)
+#define GERROR_ERR_MASK                        0xfd
+
+#define ARM_SMMU_GERRORN               0x64
+
+#define ARM_SMMU_GERROR_IRQ_CFG0       0x68
+#define ARM_SMMU_GERROR_IRQ_CFG1       0x70
+#define ARM_SMMU_GERROR_IRQ_CFG2       0x74
+
+#define ARM_SMMU_STRTAB_BASE           0x80
+#define STRTAB_BASE_RA                 (1UL << 62)
+#define STRTAB_BASE_ADDR_SHIFT         6
+#define STRTAB_BASE_ADDR_MASK          0x3ffffffffffUL
+
+#define ARM_SMMU_STRTAB_BASE_CFG       0x88
+#define STRTAB_BASE_CFG_LOG2SIZE_SHIFT 0
+#define STRTAB_BASE_CFG_LOG2SIZE_MASK  0x3f
+#define STRTAB_BASE_CFG_SPLIT_SHIFT    6
+#define STRTAB_BASE_CFG_SPLIT_MASK     0x1f
+#define STRTAB_BASE_CFG_FMT_SHIFT      16
+#define STRTAB_BASE_CFG_FMT_MASK       0x3
+#define STRTAB_BASE_CFG_FMT_LINEAR     (0 << STRTAB_BASE_CFG_FMT_SHIFT)
+#define STRTAB_BASE_CFG_FMT_2LVL       (1 << STRTAB_BASE_CFG_FMT_SHIFT)
+
+#define ARM_SMMU_CMDQ_BASE             0x90
+#define ARM_SMMU_CMDQ_PROD             0x98
+#define ARM_SMMU_CMDQ_CONS             0x9c
+
+#define ARM_SMMU_EVTQ_BASE             0xa0
+#define ARM_SMMU_EVTQ_PROD             0x100a8
+#define ARM_SMMU_EVTQ_CONS             0x100ac
+#define ARM_SMMU_EVTQ_IRQ_CFG0         0xb0
+#define ARM_SMMU_EVTQ_IRQ_CFG1         0xb8
+#define ARM_SMMU_EVTQ_IRQ_CFG2         0xbc
+
+#define ARM_SMMU_PRIQ_BASE             0xc0
+#define ARM_SMMU_PRIQ_PROD             0x100c8
+#define ARM_SMMU_PRIQ_CONS             0x100cc
+#define ARM_SMMU_PRIQ_IRQ_CFG0         0xd0
+#define ARM_SMMU_PRIQ_IRQ_CFG1         0xd8
+#define ARM_SMMU_PRIQ_IRQ_CFG2         0xdc
+
+/* Common MSI config fields */
+#define MSI_CFG0_SH_SHIFT              60
+#define MSI_CFG0_SH_NSH                        (0UL << MSI_CFG0_SH_SHIFT)
+#define MSI_CFG0_SH_OSH                        (2UL << MSI_CFG0_SH_SHIFT)
+#define MSI_CFG0_SH_ISH                        (3UL << MSI_CFG0_SH_SHIFT)
+#define MSI_CFG0_MEMATTR_SHIFT         56
+#define MSI_CFG0_MEMATTR_DEVICE_nGnRE  (0x1 << MSI_CFG0_MEMATTR_SHIFT)
+#define MSI_CFG0_ADDR_SHIFT            2
+#define MSI_CFG0_ADDR_MASK             0x3fffffffffffUL
+
+#define Q_IDX(q, p)                    ((p) & ((1 << (q)->max_n_shift) - 1))
+#define Q_WRP(q, p)                    ((p) & (1 << (q)->max_n_shift))
+#define Q_OVERFLOW_FLAG                        (1 << 31)
+#define Q_OVF(q, p)                    ((p) & Q_OVERFLOW_FLAG)
+#define Q_ENT(q, p)                    ((q)->base +                    \
+                                        Q_IDX(q, p) * (q)->ent_dwords)
+
+#define Q_BASE_RWA                     (1UL << 62)
+#define Q_BASE_ADDR_SHIFT              5
+#define Q_BASE_ADDR_MASK               0xfffffffffffUL
+#define Q_BASE_LOG2SIZE_SHIFT          0
+#define Q_BASE_LOG2SIZE_MASK           0x1fUL
+
+/*
+ * Stream table.
+ *
+ * Linear: Enough to cover 1 << IDR1.SIDSIZE entries
+ * 2lvl: 8k L1 entries, 256 lazy entries per table (each table covers a PCI bus)
+ */
+#define STRTAB_L1_SZ_SHIFT             16
+#define STRTAB_SPLIT                   8
+
+#define STRTAB_L1_DESC_DWORDS          1
+#define STRTAB_L1_DESC_SPAN_SHIFT      0
+#define STRTAB_L1_DESC_SPAN_MASK       0x1fUL
+#define STRTAB_L1_DESC_L2PTR_SHIFT     6
+#define STRTAB_L1_DESC_L2PTR_MASK      0x3ffffffffffUL
+
+#define STRTAB_STE_DWORDS              8
+#define STRTAB_STE_0_V                 (1UL << 0)
+#define STRTAB_STE_0_CFG_SHIFT         1
+#define STRTAB_STE_0_CFG_MASK          0x7UL
+#define STRTAB_STE_0_CFG_ABORT         (0UL << STRTAB_STE_0_CFG_SHIFT)
+#define STRTAB_STE_0_CFG_BYPASS                (4UL << STRTAB_STE_0_CFG_SHIFT)
+#define STRTAB_STE_0_CFG_S1_TRANS      (5UL << STRTAB_STE_0_CFG_SHIFT)
+#define STRTAB_STE_0_CFG_S2_TRANS      (6UL << STRTAB_STE_0_CFG_SHIFT)
+
+#define STRTAB_STE_0_S1FMT_SHIFT       4
+#define STRTAB_STE_0_S1FMT_LINEAR      (0UL << STRTAB_STE_0_S1FMT_SHIFT)
+#define STRTAB_STE_0_S1CTXPTR_SHIFT    6
+#define STRTAB_STE_0_S1CTXPTR_MASK     0x3ffffffffffUL
+#define STRTAB_STE_0_S1CDMAX_SHIFT     59
+#define STRTAB_STE_0_S1CDMAX_MASK      0x1fUL
+
+#define STRTAB_STE_1_S1C_CACHE_NC      0UL
+#define STRTAB_STE_1_S1C_CACHE_WBRA    1UL
+#define STRTAB_STE_1_S1C_CACHE_WT      2UL
+#define STRTAB_STE_1_S1C_CACHE_WB      3UL
+#define STRTAB_STE_1_S1C_SH_NSH                0UL
+#define STRTAB_STE_1_S1C_SH_OSH                2UL
+#define STRTAB_STE_1_S1C_SH_ISH                3UL
+#define STRTAB_STE_1_S1CIR_SHIFT       2
+#define STRTAB_STE_1_S1COR_SHIFT       4
+#define STRTAB_STE_1_S1CSH_SHIFT       6
+
+#define STRTAB_STE_1_S1STALLD          (1UL << 27)
+
+#define STRTAB_STE_1_EATS_ABT          0UL
+#define STRTAB_STE_1_EATS_TRANS                1UL
+#define STRTAB_STE_1_EATS_S1CHK                2UL
+#define STRTAB_STE_1_EATS_SHIFT                28
+
+#define STRTAB_STE_1_STRW_NSEL1                0UL
+#define STRTAB_STE_1_STRW_EL2          2UL
+#define STRTAB_STE_1_STRW_SHIFT                30
+
+#define STRTAB_STE_2_S2VMID_SHIFT      0
+#define STRTAB_STE_2_S2VMID_MASK       0xffffUL
+#define STRTAB_STE_2_VTCR_SHIFT                32
+#define STRTAB_STE_2_VTCR_MASK         0x7ffffUL
+#define STRTAB_STE_2_S2AA64            (1UL << 51)
+#define STRTAB_STE_2_S2ENDI            (1UL << 52)
+#define STRTAB_STE_2_S2PTW             (1UL << 54)
+#define STRTAB_STE_2_S2R               (1UL << 58)
+
+#define STRTAB_STE_3_S2TTB_SHIFT       4
+#define STRTAB_STE_3_S2TTB_MASK                0xfffffffffffUL
+
+/* Context descriptor (stage-1 only) */
+#define CTXDESC_CD_DWORDS              8
+#define CTXDESC_CD_0_TCR_T0SZ_SHIFT    0
+#define ARM64_TCR_T0SZ_SHIFT           0
+#define ARM64_TCR_T0SZ_MASK            0x1fUL
+#define CTXDESC_CD_0_TCR_TG0_SHIFT     6
+#define ARM64_TCR_TG0_SHIFT            14
+#define ARM64_TCR_TG0_MASK             0x3UL
+#define CTXDESC_CD_0_TCR_IRGN0_SHIFT   8
+#define ARM64_TCR_IRGN0_SHIFT          24
+#define ARM64_TCR_IRGN0_MASK           0x3UL
+#define CTXDESC_CD_0_TCR_ORGN0_SHIFT   10
+#define ARM64_TCR_ORGN0_SHIFT          26
+#define ARM64_TCR_ORGN0_MASK           0x3UL
+#define CTXDESC_CD_0_TCR_SH0_SHIFT     12
+#define ARM64_TCR_SH0_SHIFT            12
+#define ARM64_TCR_SH0_MASK             0x3UL
+#define CTXDESC_CD_0_TCR_EPD0_SHIFT    14
+#define ARM64_TCR_EPD0_SHIFT           7
+#define ARM64_TCR_EPD0_MASK            0x1UL
+#define CTXDESC_CD_0_TCR_EPD1_SHIFT    30
+#define ARM64_TCR_EPD1_SHIFT           23
+#define ARM64_TCR_EPD1_MASK            0x1UL
+
+#define CTXDESC_CD_0_ENDI              (1UL << 15)
+#define CTXDESC_CD_0_V                 (1UL << 31)
+
+#define CTXDESC_CD_0_TCR_IPS_SHIFT     32
+#define ARM64_TCR_IPS_SHIFT            32
+#define ARM64_TCR_IPS_MASK             0x7UL
+#define CTXDESC_CD_0_TCR_TBI0_SHIFT    38
+#define ARM64_TCR_TBI0_SHIFT           37
+#define ARM64_TCR_TBI0_MASK            0x1UL
+
+#define CTXDESC_CD_0_AA64              (1UL << 41)
+#define CTXDESC_CD_0_R                 (1UL << 45)
+#define CTXDESC_CD_0_A                 (1UL << 46)
+#define CTXDESC_CD_0_ASET_SHIFT                47
+#define CTXDESC_CD_0_ASET_SHARED       (0UL << CTXDESC_CD_0_ASET_SHIFT)
+#define CTXDESC_CD_0_ASET_PRIVATE      (1UL << CTXDESC_CD_0_ASET_SHIFT)
+#define CTXDESC_CD_0_ASID_SHIFT                48
+#define CTXDESC_CD_0_ASID_MASK         0xffffUL
+
+#define CTXDESC_CD_1_TTB0_SHIFT                4
+#define CTXDESC_CD_1_TTB0_MASK         0xfffffffffffUL
+
+#define CTXDESC_CD_3_MAIR_SHIFT                0
+
+/* Convert between AArch64 (CPU) TCR format and SMMU CD format */
+#define ARM_SMMU_TCR2CD(tcr, fld)                                      \
+       (((tcr) >> ARM64_TCR_##fld##_SHIFT & ARM64_TCR_##fld##_MASK)    \
+        << CTXDESC_CD_0_TCR_##fld##_SHIFT)
+
+/* Command queue */
+#define CMDQ_ENT_DWORDS                        2
+#define CMDQ_MAX_SZ_SHIFT              8
+
+#define CMDQ_ERR_SHIFT                 24
+#define CMDQ_ERR_MASK                  0x7f
+#define CMDQ_ERR_CERROR_NONE_IDX       0
+#define CMDQ_ERR_CERROR_ILL_IDX                1
+#define CMDQ_ERR_CERROR_ABT_IDX                2
+
+#define CMDQ_0_OP_SHIFT                        0
+#define CMDQ_0_OP_MASK                 0xffUL
+#define CMDQ_0_SSV                     (1UL << 11)
+
+#define CMDQ_PREFETCH_0_SID_SHIFT      32
+#define CMDQ_PREFETCH_1_SIZE_SHIFT     0
+#define CMDQ_PREFETCH_1_ADDR_MASK      ~0xfffUL
+
+#define CMDQ_CFGI_0_SID_SHIFT          32
+#define CMDQ_CFGI_0_SID_MASK           0xffffffffUL
+#define CMDQ_CFGI_1_LEAF               (1UL << 0)
+#define CMDQ_CFGI_1_RANGE_SHIFT                0
+#define CMDQ_CFGI_1_RANGE_MASK         0x1fUL
+
+#define CMDQ_TLBI_0_VMID_SHIFT         32
+#define CMDQ_TLBI_0_ASID_SHIFT         48
+#define CMDQ_TLBI_1_LEAF               (1UL << 0)
+#define CMDQ_TLBI_1_ADDR_MASK          ~0xfffUL
+
+#define CMDQ_PRI_0_SSID_SHIFT          12
+#define CMDQ_PRI_0_SSID_MASK           0xfffffUL
+#define CMDQ_PRI_0_SID_SHIFT           32
+#define CMDQ_PRI_0_SID_MASK            0xffffffffUL
+#define CMDQ_PRI_1_GRPID_SHIFT         0
+#define CMDQ_PRI_1_GRPID_MASK          0x1ffUL
+#define CMDQ_PRI_1_RESP_SHIFT          12
+#define CMDQ_PRI_1_RESP_DENY           (0UL << CMDQ_PRI_1_RESP_SHIFT)
+#define CMDQ_PRI_1_RESP_FAIL           (1UL << CMDQ_PRI_1_RESP_SHIFT)
+#define CMDQ_PRI_1_RESP_SUCC           (2UL << CMDQ_PRI_1_RESP_SHIFT)
+
+#define CMDQ_SYNC_0_CS_SHIFT           12
+#define CMDQ_SYNC_0_CS_NONE            (0UL << CMDQ_SYNC_0_CS_SHIFT)
+#define CMDQ_SYNC_0_CS_SEV             (2UL << CMDQ_SYNC_0_CS_SHIFT)
+
+/* Event queue */
+#define EVTQ_ENT_DWORDS                        4
+#define EVTQ_MAX_SZ_SHIFT              7
+
+#define EVTQ_0_ID_SHIFT                        0
+#define EVTQ_0_ID_MASK                 0xffUL
+
+/* PRI queue */
+#define PRIQ_ENT_DWORDS                        2
+#define PRIQ_MAX_SZ_SHIFT              8
+
+#define PRIQ_0_SID_SHIFT               0
+#define PRIQ_0_SID_MASK                        0xffffffffUL
+#define PRIQ_0_SSID_SHIFT              32
+#define PRIQ_0_SSID_MASK               0xfffffUL
+#define PRIQ_0_OF                      (1UL << 57)
+#define PRIQ_0_PERM_PRIV               (1UL << 58)
+#define PRIQ_0_PERM_EXEC               (1UL << 59)
+#define PRIQ_0_PERM_READ               (1UL << 60)
+#define PRIQ_0_PERM_WRITE              (1UL << 61)
+#define PRIQ_0_PRG_LAST                        (1UL << 62)
+#define PRIQ_0_SSID_V                  (1UL << 63)
+
+#define PRIQ_1_PRG_IDX_SHIFT           0
+#define PRIQ_1_PRG_IDX_MASK            0x1ffUL
+#define PRIQ_1_ADDR_SHIFT              12
+#define PRIQ_1_ADDR_MASK               0xfffffffffffffUL
+
+/* High-level queue structures */
+#define ARM_SMMU_POLL_TIMEOUT_US       100
+
+static bool disable_bypass;
+module_param_named(disable_bypass, disable_bypass, bool, S_IRUGO);
+MODULE_PARM_DESC(disable_bypass,
+       "Disable bypass streams such that incoming transactions from devices that are not attached to an iommu domain will report an abort back to the device and will not be allowed to pass through the SMMU.");
+
+enum pri_resp {
+       PRI_RESP_DENY,
+       PRI_RESP_FAIL,
+       PRI_RESP_SUCC,
+};
+
+struct arm_smmu_cmdq_ent {
+       /* Common fields */
+       u8                              opcode;
+       bool                            substream_valid;
+
+       /* Command-specific fields */
+       union {
+               #define CMDQ_OP_PREFETCH_CFG    0x1
+               struct {
+                       u32                     sid;
+                       u8                      size;
+                       u64                     addr;
+               } prefetch;
+
+               #define CMDQ_OP_CFGI_STE        0x3
+               #define CMDQ_OP_CFGI_ALL        0x4
+               struct {
+                       u32                     sid;
+                       union {
+                               bool            leaf;
+                               u8              span;
+                       };
+               } cfgi;
+
+               #define CMDQ_OP_TLBI_NH_ASID    0x11
+               #define CMDQ_OP_TLBI_NH_VA      0x12
+               #define CMDQ_OP_TLBI_EL2_ALL    0x20
+               #define CMDQ_OP_TLBI_S12_VMALL  0x28
+               #define CMDQ_OP_TLBI_S2_IPA     0x2a
+               #define CMDQ_OP_TLBI_NSNH_ALL   0x30
+               struct {
+                       u16                     asid;
+                       u16                     vmid;
+                       bool                    leaf;
+                       u64                     addr;
+               } tlbi;
+
+               #define CMDQ_OP_PRI_RESP        0x41
+               struct {
+                       u32                     sid;
+                       u32                     ssid;
+                       u16                     grpid;
+                       enum pri_resp           resp;
+               } pri;
+
+               #define CMDQ_OP_CMD_SYNC        0x46
+       };
+};
+
+struct arm_smmu_queue {
+       int                             irq; /* Wired interrupt */
+
+       __le64                          *base;
+       dma_addr_t                      base_dma;
+       u64                             q_base;
+
+       size_t                          ent_dwords;
+       u32                             max_n_shift;
+       u32                             prod;
+       u32                             cons;
+
+       u32 __iomem                     *prod_reg;
+       u32 __iomem                     *cons_reg;
+};
+
+struct arm_smmu_cmdq {
+       struct arm_smmu_queue           q;
+       spinlock_t                      lock;
+};
+
+struct arm_smmu_evtq {
+       struct arm_smmu_queue           q;
+       u32                             max_stalls;
+};
+
+struct arm_smmu_priq {
+       struct arm_smmu_queue           q;
+};
+
+/* High-level stream table and context descriptor structures */
+struct arm_smmu_strtab_l1_desc {
+       u8                              span;
+
+       __le64                          *l2ptr;
+       dma_addr_t                      l2ptr_dma;
+};
+
+struct arm_smmu_s1_cfg {
+       __le64                          *cdptr;
+       dma_addr_t                      cdptr_dma;
+
+       struct arm_smmu_ctx_desc {
+               u16     asid;
+               u64     ttbr;
+               u64     tcr;
+               u64     mair;
+       }                               cd;
+};
+
+struct arm_smmu_s2_cfg {
+       u16                             vmid;
+       u64                             vttbr;
+       u64                             vtcr;
+};
+
+struct arm_smmu_strtab_ent {
+       bool                            valid;
+
+       bool                            bypass; /* Overrides s1/s2 config */
+       struct arm_smmu_s1_cfg          *s1_cfg;
+       struct arm_smmu_s2_cfg          *s2_cfg;
+};
+
+struct arm_smmu_strtab_cfg {
+       __le64                          *strtab;
+       dma_addr_t                      strtab_dma;
+       struct arm_smmu_strtab_l1_desc  *l1_desc;
+       unsigned int                    num_l1_ents;
+
+       u64                             strtab_base;
+       u32                             strtab_base_cfg;
+};
+
+/* An SMMUv3 instance */
+struct arm_smmu_device {
+       struct device                   *dev;
+       void __iomem                    *base;
+
+#define ARM_SMMU_FEAT_2_LVL_STRTAB     (1 << 0)
+#define ARM_SMMU_FEAT_2_LVL_CDTAB      (1 << 1)
+#define ARM_SMMU_FEAT_TT_LE            (1 << 2)
+#define ARM_SMMU_FEAT_TT_BE            (1 << 3)
+#define ARM_SMMU_FEAT_PRI              (1 << 4)
+#define ARM_SMMU_FEAT_ATS              (1 << 5)
+#define ARM_SMMU_FEAT_SEV              (1 << 6)
+#define ARM_SMMU_FEAT_MSI              (1 << 7)
+#define ARM_SMMU_FEAT_COHERENCY                (1 << 8)
+#define ARM_SMMU_FEAT_TRANS_S1         (1 << 9)
+#define ARM_SMMU_FEAT_TRANS_S2         (1 << 10)
+#define ARM_SMMU_FEAT_STALLS           (1 << 11)
+#define ARM_SMMU_FEAT_HYP              (1 << 12)
+       u32                             features;
+
+       struct arm_smmu_cmdq            cmdq;
+       struct arm_smmu_evtq            evtq;
+       struct arm_smmu_priq            priq;
+
+       int                             gerr_irq;
+
+       unsigned long                   ias; /* IPA */
+       unsigned long                   oas; /* PA */
+
+#define ARM_SMMU_MAX_ASIDS             (1 << 16)
+       unsigned int                    asid_bits;
+       DECLARE_BITMAP(asid_map, ARM_SMMU_MAX_ASIDS);
+
+#define ARM_SMMU_MAX_VMIDS             (1 << 16)
+       unsigned int                    vmid_bits;
+       DECLARE_BITMAP(vmid_map, ARM_SMMU_MAX_VMIDS);
+
+       unsigned int                    ssid_bits;
+       unsigned int                    sid_bits;
+
+       struct arm_smmu_strtab_cfg      strtab_cfg;
+       struct list_head                list;
+};
+
+/* SMMU private data for an IOMMU group */
+struct arm_smmu_group {
+       struct arm_smmu_device          *smmu;
+       struct arm_smmu_domain          *domain;
+       int                             num_sids;
+       u32                             *sids;
+       struct arm_smmu_strtab_ent      ste;
+};
+
+/* SMMU private data for an IOMMU domain */
+enum arm_smmu_domain_stage {
+       ARM_SMMU_DOMAIN_S1 = 0,
+       ARM_SMMU_DOMAIN_S2,
+       ARM_SMMU_DOMAIN_NESTED,
+};
+
+struct arm_smmu_domain {
+       struct arm_smmu_device          *smmu;
+       struct mutex                    init_mutex; /* Protects smmu pointer */
+
+       struct io_pgtable_ops           *pgtbl_ops;
+       spinlock_t                      pgtbl_lock;
+
+       enum arm_smmu_domain_stage      stage;
+       union {
+               struct arm_smmu_s1_cfg  s1_cfg;
+               struct arm_smmu_s2_cfg  s2_cfg;
+       };
+
+       struct iommu_domain             domain;
+};
+
+/* Our list of SMMU instances */
+static DEFINE_SPINLOCK(arm_smmu_devices_lock);
+static LIST_HEAD(arm_smmu_devices);
+
+static struct arm_smmu_domain *to_smmu_domain(struct iommu_domain *dom)
+{
+       return container_of(dom, struct arm_smmu_domain, domain);
+}
+
+/* Low-level queue manipulation functions */
+static bool queue_full(struct arm_smmu_queue *q)
+{
+       return Q_IDX(q, q->prod) == Q_IDX(q, q->cons) &&
+              Q_WRP(q, q->prod) != Q_WRP(q, q->cons);
+}
+
+static bool queue_empty(struct arm_smmu_queue *q)
+{
+       return Q_IDX(q, q->prod) == Q_IDX(q, q->cons) &&
+              Q_WRP(q, q->prod) == Q_WRP(q, q->cons);
+}
+
+static void queue_sync_cons(struct arm_smmu_queue *q)
+{
+       q->cons = readl_relaxed(q->cons_reg);
+}
+
+static void queue_inc_cons(struct arm_smmu_queue *q)
+{
+       u32 cons = (Q_WRP(q, q->cons) | Q_IDX(q, q->cons)) + 1;
+
+       q->cons = Q_OVF(q, q->cons) | Q_WRP(q, cons) | Q_IDX(q, cons);
+       writel(q->cons, q->cons_reg);
+}
+
+static int queue_sync_prod(struct arm_smmu_queue *q)
+{
+       int ret = 0;
+       u32 prod = readl_relaxed(q->prod_reg);
+
+       if (Q_OVF(q, prod) != Q_OVF(q, q->prod))
+               ret = -EOVERFLOW;
+
+       q->prod = prod;
+       return ret;
+}
+
+static void queue_inc_prod(struct arm_smmu_queue *q)
+{
+       u32 prod = (Q_WRP(q, q->prod) | Q_IDX(q, q->prod)) + 1;
+
+       q->prod = Q_OVF(q, q->prod) | Q_WRP(q, prod) | Q_IDX(q, prod);
+       writel(q->prod, q->prod_reg);
+}
+
+static bool __queue_cons_before(struct arm_smmu_queue *q, u32 until)
+{
+       if (Q_WRP(q, q->cons) == Q_WRP(q, until))
+               return Q_IDX(q, q->cons) < Q_IDX(q, until);
+
+       return Q_IDX(q, q->cons) >= Q_IDX(q, until);
+}
+
+static int queue_poll_cons(struct arm_smmu_queue *q, u32 until, bool wfe)
+{
+       ktime_t timeout = ktime_add_us(ktime_get(), ARM_SMMU_POLL_TIMEOUT_US);
+
+       while (queue_sync_cons(q), __queue_cons_before(q, until)) {
+               if (ktime_compare(ktime_get(), timeout) > 0)
+                       return -ETIMEDOUT;
+
+               if (wfe) {
+                       wfe();
+               } else {
+                       cpu_relax();
+                       udelay(1);
+               }
+       }
+
+       return 0;
+}
+
+static void queue_write(__le64 *dst, u64 *src, size_t n_dwords)
+{
+       int i;
+
+       for (i = 0; i < n_dwords; ++i)
+               *dst++ = cpu_to_le64(*src++);
+}
+
+static int queue_insert_raw(struct arm_smmu_queue *q, u64 *ent)
+{
+       if (queue_full(q))
+               return -ENOSPC;
+
+       queue_write(Q_ENT(q, q->prod), ent, q->ent_dwords);
+       queue_inc_prod(q);
+       return 0;
+}
+
+static void queue_read(__le64 *dst, u64 *src, size_t n_dwords)
+{
+       int i;
+
+       for (i = 0; i < n_dwords; ++i)
+               *dst++ = le64_to_cpu(*src++);
+}
+
+static int queue_remove_raw(struct arm_smmu_queue *q, u64 *ent)
+{
+       if (queue_empty(q))
+               return -EAGAIN;
+
+       queue_read(ent, Q_ENT(q, q->cons), q->ent_dwords);
+       queue_inc_cons(q);
+       return 0;
+}
+
+/* High-level queue accessors */
+static int arm_smmu_cmdq_build_cmd(u64 *cmd, struct arm_smmu_cmdq_ent *ent)
+{
+       memset(cmd, 0, CMDQ_ENT_DWORDS << 3);
+       cmd[0] |= (ent->opcode & CMDQ_0_OP_MASK) << CMDQ_0_OP_SHIFT;
+
+       switch (ent->opcode) {
+       case CMDQ_OP_TLBI_EL2_ALL:
+       case CMDQ_OP_TLBI_NSNH_ALL:
+               break;
+       case CMDQ_OP_PREFETCH_CFG:
+               cmd[0] |= (u64)ent->prefetch.sid << CMDQ_PREFETCH_0_SID_SHIFT;
+               cmd[1] |= ent->prefetch.size << CMDQ_PREFETCH_1_SIZE_SHIFT;
+               cmd[1] |= ent->prefetch.addr & CMDQ_PREFETCH_1_ADDR_MASK;
+               break;
+       case CMDQ_OP_CFGI_STE:
+               cmd[0] |= (u64)ent->cfgi.sid << CMDQ_CFGI_0_SID_SHIFT;
+               cmd[1] |= ent->cfgi.leaf ? CMDQ_CFGI_1_LEAF : 0;
+               break;
+       case CMDQ_OP_CFGI_ALL:
+               /* Cover the entire SID range */
+               cmd[1] |= CMDQ_CFGI_1_RANGE_MASK << CMDQ_CFGI_1_RANGE_SHIFT;
+               break;
+       case CMDQ_OP_TLBI_NH_VA:
+               cmd[0] |= (u64)ent->tlbi.asid << CMDQ_TLBI_0_ASID_SHIFT;
+               /* Fallthrough */
+       case CMDQ_OP_TLBI_S2_IPA:
+               cmd[0] |= (u64)ent->tlbi.vmid << CMDQ_TLBI_0_VMID_SHIFT;
+               cmd[1] |= ent->tlbi.leaf ? CMDQ_TLBI_1_LEAF : 0;
+               cmd[1] |= ent->tlbi.addr & CMDQ_TLBI_1_ADDR_MASK;
+               break;
+       case CMDQ_OP_TLBI_NH_ASID:
+               cmd[0] |= (u64)ent->tlbi.asid << CMDQ_TLBI_0_ASID_SHIFT;
+               /* Fallthrough */
+       case CMDQ_OP_TLBI_S12_VMALL:
+               cmd[0] |= (u64)ent->tlbi.vmid << CMDQ_TLBI_0_VMID_SHIFT;
+               break;
+       case CMDQ_OP_PRI_RESP:
+               cmd[0] |= ent->substream_valid ? CMDQ_0_SSV : 0;
+               cmd[0] |= ent->pri.ssid << CMDQ_PRI_0_SSID_SHIFT;
+               cmd[0] |= (u64)ent->pri.sid << CMDQ_PRI_0_SID_SHIFT;
+               cmd[1] |= ent->pri.grpid << CMDQ_PRI_1_GRPID_SHIFT;
+               switch (ent->pri.resp) {
+               case PRI_RESP_DENY:
+                       cmd[1] |= CMDQ_PRI_1_RESP_DENY;
+                       break;
+               case PRI_RESP_FAIL:
+                       cmd[1] |= CMDQ_PRI_1_RESP_FAIL;
+                       break;
+               case PRI_RESP_SUCC:
+                       cmd[1] |= CMDQ_PRI_1_RESP_SUCC;
+                       break;
+               default:
+                       return -EINVAL;
+               }
+               break;
+       case CMDQ_OP_CMD_SYNC:
+               cmd[0] |= CMDQ_SYNC_0_CS_SEV;
+               break;
+       default:
+               return -ENOENT;
+       }
+
+       return 0;
+}
+
+static void arm_smmu_cmdq_skip_err(struct arm_smmu_device *smmu)
+{
+       static const char *cerror_str[] = {
+               [CMDQ_ERR_CERROR_NONE_IDX]      = "No error",
+               [CMDQ_ERR_CERROR_ILL_IDX]       = "Illegal command",
+               [CMDQ_ERR_CERROR_ABT_IDX]       = "Abort on command fetch",
+       };
+
+       int i;
+       u64 cmd[CMDQ_ENT_DWORDS];
+       struct arm_smmu_queue *q = &smmu->cmdq.q;
+       u32 cons = readl_relaxed(q->cons_reg);
+       u32 idx = cons >> CMDQ_ERR_SHIFT & CMDQ_ERR_MASK;
+       struct arm_smmu_cmdq_ent cmd_sync = {
+               .opcode = CMDQ_OP_CMD_SYNC,
+       };
+
+       dev_err(smmu->dev, "CMDQ error (cons 0x%08x): %s\n", cons,
+               cerror_str[idx]);
+
+       switch (idx) {
+       case CMDQ_ERR_CERROR_ILL_IDX:
+               break;
+       case CMDQ_ERR_CERROR_ABT_IDX:
+               dev_err(smmu->dev, "retrying command fetch\n");
+       case CMDQ_ERR_CERROR_NONE_IDX:
+               return;
+       }
+
+       /*
+        * We may have concurrent producers, so we need to be careful
+        * not to touch any of the shadow cmdq state.
+        */
+       queue_read(cmd, Q_ENT(q, idx), q->ent_dwords);
+       dev_err(smmu->dev, "skipping command in error state:\n");
+       for (i = 0; i < ARRAY_SIZE(cmd); ++i)
+               dev_err(smmu->dev, "\t0x%016llx\n", (unsigned long long)cmd[i]);
+
+       /* Convert the erroneous command into a CMD_SYNC */
+       if (arm_smmu_cmdq_build_cmd(cmd, &cmd_sync)) {
+               dev_err(smmu->dev, "failed to convert to CMD_SYNC\n");
+               return;
+       }
+
+       queue_write(cmd, Q_ENT(q, idx), q->ent_dwords);
+}
+
+static void arm_smmu_cmdq_issue_cmd(struct arm_smmu_device *smmu,
+                                   struct arm_smmu_cmdq_ent *ent)
+{
+       u32 until;
+       u64 cmd[CMDQ_ENT_DWORDS];
+       bool wfe = !!(smmu->features & ARM_SMMU_FEAT_SEV);
+       struct arm_smmu_queue *q = &smmu->cmdq.q;
+
+       if (arm_smmu_cmdq_build_cmd(cmd, ent)) {
+               dev_warn(smmu->dev, "ignoring unknown CMDQ opcode 0x%x\n",
+                        ent->opcode);
+               return;
+       }
+
+       spin_lock(&smmu->cmdq.lock);
+       while (until = q->prod + 1, queue_insert_raw(q, cmd) == -ENOSPC) {
+               /*
+                * Keep the queue locked, otherwise the producer could wrap
+                * twice and we could see a future consumer pointer that looks
+                * like it's behind us.
+                */
+               if (queue_poll_cons(q, until, wfe))
+                       dev_err_ratelimited(smmu->dev, "CMDQ timeout\n");
+       }
+
+       if (ent->opcode == CMDQ_OP_CMD_SYNC && queue_poll_cons(q, until, wfe))
+               dev_err_ratelimited(smmu->dev, "CMD_SYNC timeout\n");
+       spin_unlock(&smmu->cmdq.lock);
+}
+
+/* Context descriptor manipulation functions */
+static u64 arm_smmu_cpu_tcr_to_cd(u64 tcr)
+{
+       u64 val = 0;
+
+       /* Repack the TCR. Just care about TTBR0 for now */
+       val |= ARM_SMMU_TCR2CD(tcr, T0SZ);
+       val |= ARM_SMMU_TCR2CD(tcr, TG0);
+       val |= ARM_SMMU_TCR2CD(tcr, IRGN0);
+       val |= ARM_SMMU_TCR2CD(tcr, ORGN0);
+       val |= ARM_SMMU_TCR2CD(tcr, SH0);
+       val |= ARM_SMMU_TCR2CD(tcr, EPD0);
+       val |= ARM_SMMU_TCR2CD(tcr, EPD1);
+       val |= ARM_SMMU_TCR2CD(tcr, IPS);
+       val |= ARM_SMMU_TCR2CD(tcr, TBI0);
+
+       return val;
+}
+
+static void arm_smmu_write_ctx_desc(struct arm_smmu_device *smmu,
+                                   struct arm_smmu_s1_cfg *cfg)
+{
+       u64 val;
+
+       /*
+        * We don't need to issue any invalidation here, as we'll invalidate
+        * the STE when installing the new entry anyway.
+        */
+       val = arm_smmu_cpu_tcr_to_cd(cfg->cd.tcr) |
+#ifdef __BIG_ENDIAN
+             CTXDESC_CD_0_ENDI |
+#endif
+             CTXDESC_CD_0_R | CTXDESC_CD_0_A | CTXDESC_CD_0_ASET_PRIVATE |
+             CTXDESC_CD_0_AA64 | (u64)cfg->cd.asid << CTXDESC_CD_0_ASID_SHIFT |
+             CTXDESC_CD_0_V;
+       cfg->cdptr[0] = cpu_to_le64(val);
+
+       val = cfg->cd.ttbr & CTXDESC_CD_1_TTB0_MASK << CTXDESC_CD_1_TTB0_SHIFT;
+       cfg->cdptr[1] = cpu_to_le64(val);
+
+       cfg->cdptr[3] = cpu_to_le64(cfg->cd.mair << CTXDESC_CD_3_MAIR_SHIFT);
+}
+
+/* Stream table manipulation functions */
+static void
+arm_smmu_write_strtab_l1_desc(__le64 *dst, struct arm_smmu_strtab_l1_desc *desc)
+{
+       u64 val = 0;
+
+       val |= (desc->span & STRTAB_L1_DESC_SPAN_MASK)
+               << STRTAB_L1_DESC_SPAN_SHIFT;
+       val |= desc->l2ptr_dma &
+              STRTAB_L1_DESC_L2PTR_MASK << STRTAB_L1_DESC_L2PTR_SHIFT;
+
+       *dst = cpu_to_le64(val);
+}
+
+static void arm_smmu_sync_ste_for_sid(struct arm_smmu_device *smmu, u32 sid)
+{
+       struct arm_smmu_cmdq_ent cmd = {
+               .opcode = CMDQ_OP_CFGI_STE,
+               .cfgi   = {
+                       .sid    = sid,
+                       .leaf   = true,
+               },
+       };
+
+       arm_smmu_cmdq_issue_cmd(smmu, &cmd);
+       cmd.opcode = CMDQ_OP_CMD_SYNC;
+       arm_smmu_cmdq_issue_cmd(smmu, &cmd);
+}
+
+static void arm_smmu_write_strtab_ent(struct arm_smmu_device *smmu, u32 sid,
+                                     __le64 *dst, struct arm_smmu_strtab_ent *ste)
+{
+       /*
+        * This is hideously complicated, but we only really care about
+        * three cases at the moment:
+        *
+        * 1. Invalid (all zero) -> bypass  (init)
+        * 2. Bypass -> translation (attach)
+        * 3. Translation -> bypass (detach)
+        *
+        * Given that we can't update the STE atomically and the SMMU
+        * doesn't read the thing in a defined order, that leaves us
+        * with the following maintenance requirements:
+        *
+        * 1. Update Config, return (init time STEs aren't live)
+        * 2. Write everything apart from dword 0, sync, write dword 0, sync
+        * 3. Update Config, sync
+        */
+       u64 val = le64_to_cpu(dst[0]);
+       bool ste_live = false;
+       struct arm_smmu_cmdq_ent prefetch_cmd = {
+               .opcode         = CMDQ_OP_PREFETCH_CFG,
+               .prefetch       = {
+                       .sid    = sid,
+               },
+       };
+
+       if (val & STRTAB_STE_0_V) {
+               u64 cfg;
+
+               cfg = val & STRTAB_STE_0_CFG_MASK << STRTAB_STE_0_CFG_SHIFT;
+               switch (cfg) {
+               case STRTAB_STE_0_CFG_BYPASS:
+                       break;
+               case STRTAB_STE_0_CFG_S1_TRANS:
+               case STRTAB_STE_0_CFG_S2_TRANS:
+                       ste_live = true;
+                       break;
+               default:
+                       BUG(); /* STE corruption */
+               }
+       }
+
+       /* Nuke the existing Config, as we're going to rewrite it */
+       val &= ~(STRTAB_STE_0_CFG_MASK << STRTAB_STE_0_CFG_SHIFT);
+
+       if (ste->valid)
+               val |= STRTAB_STE_0_V;
+       else
+               val &= ~STRTAB_STE_0_V;
+
+       if (ste->bypass) {
+               val |= disable_bypass ? STRTAB_STE_0_CFG_ABORT
+                                     : STRTAB_STE_0_CFG_BYPASS;
+               dst[0] = cpu_to_le64(val);
+               dst[2] = 0; /* Nuke the VMID */
+               if (ste_live)
+                       arm_smmu_sync_ste_for_sid(smmu, sid);
+               return;
+       }
+
+       if (ste->s1_cfg) {
+               BUG_ON(ste_live);
+               dst[1] = cpu_to_le64(
+                        STRTAB_STE_1_S1C_CACHE_WBRA
+                        << STRTAB_STE_1_S1CIR_SHIFT |
+                        STRTAB_STE_1_S1C_CACHE_WBRA
+                        << STRTAB_STE_1_S1COR_SHIFT |
+                        STRTAB_STE_1_S1C_SH_ISH << STRTAB_STE_1_S1CSH_SHIFT |
+                        STRTAB_STE_1_S1STALLD |
+#ifdef CONFIG_PCI_ATS
+                        STRTAB_STE_1_EATS_TRANS << STRTAB_STE_1_EATS_SHIFT |
+#endif
+                        STRTAB_STE_1_STRW_NSEL1 << STRTAB_STE_1_STRW_SHIFT);
+
+               val |= (ste->s1_cfg->cdptr_dma & STRTAB_STE_0_S1CTXPTR_MASK
+                       << STRTAB_STE_0_S1CTXPTR_SHIFT) |
+                       STRTAB_STE_0_CFG_S1_TRANS;
+
+       }
+
+       if (ste->s2_cfg) {
+               BUG_ON(ste_live);
+               dst[2] = cpu_to_le64(
+                        ste->s2_cfg->vmid << STRTAB_STE_2_S2VMID_SHIFT |
+                        (ste->s2_cfg->vtcr & STRTAB_STE_2_VTCR_MASK)
+                         << STRTAB_STE_2_VTCR_SHIFT |
+#ifdef __BIG_ENDIAN
+                        STRTAB_STE_2_S2ENDI |
+#endif
+                        STRTAB_STE_2_S2PTW | STRTAB_STE_2_S2AA64 |
+                        STRTAB_STE_2_S2R);
+
+               dst[3] = cpu_to_le64(ste->s2_cfg->vttbr &
+                        STRTAB_STE_3_S2TTB_MASK << STRTAB_STE_3_S2TTB_SHIFT);
+
+               val |= STRTAB_STE_0_CFG_S2_TRANS;
+       }
+
+       arm_smmu_sync_ste_for_sid(smmu, sid);
+       dst[0] = cpu_to_le64(val);
+       arm_smmu_sync_ste_for_sid(smmu, sid);
+
+       /* It's likely that we'll want to use the new STE soon */
+       arm_smmu_cmdq_issue_cmd(smmu, &prefetch_cmd);
+}
+
+static void arm_smmu_init_bypass_stes(u64 *strtab, unsigned int nent)
+{
+       unsigned int i;
+       struct arm_smmu_strtab_ent ste = {
+               .valid  = true,
+               .bypass = true,
+       };
+
+       for (i = 0; i < nent; ++i) {
+               arm_smmu_write_strtab_ent(NULL, -1, strtab, &ste);
+               strtab += STRTAB_STE_DWORDS;
+       }
+}
+
+static int arm_smmu_init_l2_strtab(struct arm_smmu_device *smmu, u32 sid)
+{
+       size_t size;
+       void *strtab;
+       struct arm_smmu_strtab_cfg *cfg = &smmu->strtab_cfg;
+       struct arm_smmu_strtab_l1_desc *desc = &cfg->l1_desc[sid >> STRTAB_SPLIT];
+
+       if (desc->l2ptr)
+               return 0;
+
+       size = 1 << (STRTAB_SPLIT + ilog2(STRTAB_STE_DWORDS) + 3);
+       strtab = &cfg->strtab[sid >> STRTAB_SPLIT << STRTAB_L1_DESC_DWORDS];
+
+       desc->span = STRTAB_SPLIT + 1;
+       desc->l2ptr = dma_zalloc_coherent(smmu->dev, size, &desc->l2ptr_dma,
+                                         GFP_KERNEL);
+       if (!desc->l2ptr) {
+               dev_err(smmu->dev,
+                       "failed to allocate l2 stream table for SID %u\n",
+                       sid);
+               return -ENOMEM;
+       }
+
+       arm_smmu_init_bypass_stes(desc->l2ptr, 1 << STRTAB_SPLIT);
+       arm_smmu_write_strtab_l1_desc(strtab, desc);
+       return 0;
+}
+
+/* IRQ and event handlers */
+static irqreturn_t arm_smmu_evtq_thread(int irq, void *dev)
+{
+       int i;
+       struct arm_smmu_device *smmu = dev;
+       struct arm_smmu_queue *q = &smmu->evtq.q;
+       u64 evt[EVTQ_ENT_DWORDS];
+
+       while (!queue_remove_raw(q, evt)) {
+               u8 id = evt[0] >> EVTQ_0_ID_SHIFT & EVTQ_0_ID_MASK;
+
+               dev_info(smmu->dev, "event 0x%02x received:\n", id);
+               for (i = 0; i < ARRAY_SIZE(evt); ++i)
+                       dev_info(smmu->dev, "\t0x%016llx\n",
+                                (unsigned long long)evt[i]);
+       }
+
+       /* Sync our overflow flag, as we believe we're up to speed */
+       q->cons = Q_OVF(q, q->prod) | Q_WRP(q, q->cons) | Q_IDX(q, q->cons);
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t arm_smmu_evtq_handler(int irq, void *dev)
+{
+       irqreturn_t ret = IRQ_WAKE_THREAD;
+       struct arm_smmu_device *smmu = dev;
+       struct arm_smmu_queue *q = &smmu->evtq.q;
+
+       /*
+        * Not much we can do on overflow, so scream and pretend we're
+        * trying harder.
+        */
+       if (queue_sync_prod(q) == -EOVERFLOW)
+               dev_err(smmu->dev, "EVTQ overflow detected -- events lost\n");
+       else if (queue_empty(q))
+               ret = IRQ_NONE;
+
+       return ret;
+}
+
+static irqreturn_t arm_smmu_priq_thread(int irq, void *dev)
+{
+       struct arm_smmu_device *smmu = dev;
+       struct arm_smmu_queue *q = &smmu->priq.q;
+       u64 evt[PRIQ_ENT_DWORDS];
+
+       while (!queue_remove_raw(q, evt)) {
+               u32 sid, ssid;
+               u16 grpid;
+               bool ssv, last;
+
+               sid = evt[0] >> PRIQ_0_SID_SHIFT & PRIQ_0_SID_MASK;
+               ssv = evt[0] & PRIQ_0_SSID_V;
+               ssid = ssv ? evt[0] >> PRIQ_0_SSID_SHIFT & PRIQ_0_SSID_MASK : 0;
+               last = evt[0] & PRIQ_0_PRG_LAST;
+               grpid = evt[1] >> PRIQ_1_PRG_IDX_SHIFT & PRIQ_1_PRG_IDX_MASK;
+
+               dev_info(smmu->dev, "unexpected PRI request received:\n");
+               dev_info(smmu->dev,
+                        "\tsid 0x%08x.0x%05x: [%u%s] %sprivileged %s%s%s access at iova 0x%016llx\n",
+                        sid, ssid, grpid, last ? "L" : "",
+                        evt[0] & PRIQ_0_PERM_PRIV ? "" : "un",
+                        evt[0] & PRIQ_0_PERM_READ ? "R" : "",
+                        evt[0] & PRIQ_0_PERM_WRITE ? "W" : "",
+                        evt[0] & PRIQ_0_PERM_EXEC ? "X" : "",
+                        evt[1] & PRIQ_1_ADDR_MASK << PRIQ_1_ADDR_SHIFT);
+
+               if (last) {
+                       struct arm_smmu_cmdq_ent cmd = {
+                               .opcode                 = CMDQ_OP_PRI_RESP,
+                               .substream_valid        = ssv,
+                               .pri                    = {
+                                       .sid    = sid,
+                                       .ssid   = ssid,
+                                       .grpid  = grpid,
+                                       .resp   = PRI_RESP_DENY,
+                               },
+                       };
+
+                       arm_smmu_cmdq_issue_cmd(smmu, &cmd);
+               }
+       }
+
+       /* Sync our overflow flag, as we believe we're up to speed */
+       q->cons = Q_OVF(q, q->prod) | Q_WRP(q, q->cons) | Q_IDX(q, q->cons);
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t arm_smmu_priq_handler(int irq, void *dev)
+{
+       irqreturn_t ret = IRQ_WAKE_THREAD;
+       struct arm_smmu_device *smmu = dev;
+       struct arm_smmu_queue *q = &smmu->priq.q;
+
+       /* PRIQ overflow indicates a programming error */
+       if (queue_sync_prod(q) == -EOVERFLOW)
+               dev_err(smmu->dev, "PRIQ overflow detected -- requests lost\n");
+       else if (queue_empty(q))
+               ret = IRQ_NONE;
+
+       return ret;
+}
+
+static irqreturn_t arm_smmu_cmdq_sync_handler(int irq, void *dev)
+{
+       /* We don't actually use CMD_SYNC interrupts for anything */
+       return IRQ_HANDLED;
+}
+
+static int arm_smmu_device_disable(struct arm_smmu_device *smmu);
+
+static irqreturn_t arm_smmu_gerror_handler(int irq, void *dev)
+{
+       u32 gerror, gerrorn;
+       struct arm_smmu_device *smmu = dev;
+
+       gerror = readl_relaxed(smmu->base + ARM_SMMU_GERROR);
+       gerrorn = readl_relaxed(smmu->base + ARM_SMMU_GERRORN);
+
+       gerror ^= gerrorn;
+       if (!(gerror & GERROR_ERR_MASK))
+               return IRQ_NONE; /* No errors pending */
+
+       dev_warn(smmu->dev,
+                "unexpected global error reported (0x%08x), this could be serious\n",
+                gerror);
+
+       if (gerror & GERROR_SFM_ERR) {
+               dev_err(smmu->dev, "device has entered Service Failure Mode!\n");
+               arm_smmu_device_disable(smmu);
+       }
+
+       if (gerror & GERROR_MSI_GERROR_ABT_ERR)
+               dev_warn(smmu->dev, "GERROR MSI write aborted\n");
+
+       if (gerror & GERROR_MSI_PRIQ_ABT_ERR) {
+               dev_warn(smmu->dev, "PRIQ MSI write aborted\n");
+               arm_smmu_priq_handler(irq, smmu->dev);
+       }
+
+       if (gerror & GERROR_MSI_EVTQ_ABT_ERR) {
+               dev_warn(smmu->dev, "EVTQ MSI write aborted\n");
+               arm_smmu_evtq_handler(irq, smmu->dev);
+       }
+
+       if (gerror & GERROR_MSI_CMDQ_ABT_ERR) {
+               dev_warn(smmu->dev, "CMDQ MSI write aborted\n");
+               arm_smmu_cmdq_sync_handler(irq, smmu->dev);
+       }
+
+       if (gerror & GERROR_PRIQ_ABT_ERR)
+               dev_err(smmu->dev, "PRIQ write aborted -- events may have been lost\n");
+
+       if (gerror & GERROR_EVTQ_ABT_ERR)
+               dev_err(smmu->dev, "EVTQ write aborted -- events may have been lost\n");
+
+       if (gerror & GERROR_CMDQ_ERR)
+               arm_smmu_cmdq_skip_err(smmu);
+
+       writel(gerror, smmu->base + ARM_SMMU_GERRORN);
+       return IRQ_HANDLED;
+}
+
+/* IO_PGTABLE API */
+static void __arm_smmu_tlb_sync(struct arm_smmu_device *smmu)
+{
+       struct arm_smmu_cmdq_ent cmd;
+
+       cmd.opcode = CMDQ_OP_CMD_SYNC;
+       arm_smmu_cmdq_issue_cmd(smmu, &cmd);
+}
+
+static void arm_smmu_tlb_sync(void *cookie)
+{
+       struct arm_smmu_domain *smmu_domain = cookie;
+       __arm_smmu_tlb_sync(smmu_domain->smmu);
+}
+
+static void arm_smmu_tlb_inv_context(void *cookie)
+{
+       struct arm_smmu_domain *smmu_domain = cookie;
+       struct arm_smmu_device *smmu = smmu_domain->smmu;
+       struct arm_smmu_cmdq_ent cmd;
+
+       if (smmu_domain->stage == ARM_SMMU_DOMAIN_S1) {
+               cmd.opcode      = CMDQ_OP_TLBI_NH_ASID;
+               cmd.tlbi.asid   = smmu_domain->s1_cfg.cd.asid;
+               cmd.tlbi.vmid   = 0;
+       } else {
+               cmd.opcode      = CMDQ_OP_TLBI_S12_VMALL;
+               cmd.tlbi.vmid   = smmu_domain->s2_cfg.vmid;
+       }
+
+       arm_smmu_cmdq_issue_cmd(smmu, &cmd);
+       __arm_smmu_tlb_sync(smmu);
+}
+
+static void arm_smmu_tlb_inv_range_nosync(unsigned long iova, size_t size,
+                                         bool leaf, void *cookie)
+{
+       struct arm_smmu_domain *smmu_domain = cookie;
+       struct arm_smmu_device *smmu = smmu_domain->smmu;
+       struct arm_smmu_cmdq_ent cmd = {
+               .tlbi = {
+                       .leaf   = leaf,
+                       .addr   = iova,
+               },
+       };
+
+       if (smmu_domain->stage == ARM_SMMU_DOMAIN_S1) {
+               cmd.opcode      = CMDQ_OP_TLBI_NH_VA;
+               cmd.tlbi.asid   = smmu_domain->s1_cfg.cd.asid;
+       } else {
+               cmd.opcode      = CMDQ_OP_TLBI_S2_IPA;
+               cmd.tlbi.vmid   = smmu_domain->s2_cfg.vmid;
+       }
+
+       arm_smmu_cmdq_issue_cmd(smmu, &cmd);
+}
+
+static void arm_smmu_flush_pgtable(void *addr, size_t size, void *cookie)
+{
+       struct arm_smmu_domain *smmu_domain = cookie;
+       struct arm_smmu_device *smmu = smmu_domain->smmu;
+       unsigned long offset = (unsigned long)addr & ~PAGE_MASK;
+
+       if (smmu->features & ARM_SMMU_FEAT_COHERENCY) {
+               dsb(ishst);
+       } else {
+               dma_addr_t dma_addr;
+               struct device *dev = smmu->dev;
+
+               dma_addr = dma_map_page(dev, virt_to_page(addr), offset, size,
+                                       DMA_TO_DEVICE);
+
+               if (dma_mapping_error(dev, dma_addr))
+                       dev_err(dev, "failed to flush pgtable at %p\n", addr);
+               else
+                       dma_unmap_page(dev, dma_addr, size, DMA_TO_DEVICE);
+       }
+}
+
+static struct iommu_gather_ops arm_smmu_gather_ops = {
+       .tlb_flush_all  = arm_smmu_tlb_inv_context,
+       .tlb_add_flush  = arm_smmu_tlb_inv_range_nosync,
+       .tlb_sync       = arm_smmu_tlb_sync,
+       .flush_pgtable  = arm_smmu_flush_pgtable,
+};
+
+/* IOMMU API */
+static bool arm_smmu_capable(enum iommu_cap cap)
+{
+       switch (cap) {
+       case IOMMU_CAP_CACHE_COHERENCY:
+               return true;
+       case IOMMU_CAP_INTR_REMAP:
+               return true; /* MSIs are just memory writes */
+       case IOMMU_CAP_NOEXEC:
+               return true;
+       default:
+               return false;
+       }
+}
+
+static struct iommu_domain *arm_smmu_domain_alloc(unsigned type)
+{
+       struct arm_smmu_domain *smmu_domain;
+
+       if (type != IOMMU_DOMAIN_UNMANAGED)
+               return NULL;
+
+       /*
+        * Allocate the domain and initialise some of its data structures.
+        * We can't really do anything meaningful until we've added a
+        * master.
+        */
+       smmu_domain = kzalloc(sizeof(*smmu_domain), GFP_KERNEL);
+       if (!smmu_domain)
+               return NULL;
+
+       mutex_init(&smmu_domain->init_mutex);
+       spin_lock_init(&smmu_domain->pgtbl_lock);
+       return &smmu_domain->domain;
+}
+
+static int arm_smmu_bitmap_alloc(unsigned long *map, int span)
+{
+       int idx, size = 1 << span;
+
+       do {
+               idx = find_first_zero_bit(map, size);
+               if (idx == size)
+                       return -ENOSPC;
+       } while (test_and_set_bit(idx, map));
+
+       return idx;
+}
+
+static void arm_smmu_bitmap_free(unsigned long *map, int idx)
+{
+       clear_bit(idx, map);
+}
+
+static void arm_smmu_domain_free(struct iommu_domain *domain)
+{
+       struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
+       struct arm_smmu_device *smmu = smmu_domain->smmu;
+
+       if (smmu_domain->pgtbl_ops)
+               free_io_pgtable_ops(smmu_domain->pgtbl_ops);
+
+       /* Free the CD and ASID, if we allocated them */
+       if (smmu_domain->stage == ARM_SMMU_DOMAIN_S1) {
+               struct arm_smmu_s1_cfg *cfg = &smmu_domain->s1_cfg;
+
+               if (cfg->cdptr) {
+                       dma_free_coherent(smmu_domain->smmu->dev,
+                                         CTXDESC_CD_DWORDS << 3,
+                                         cfg->cdptr,
+                                         cfg->cdptr_dma);
+
+                       arm_smmu_bitmap_free(smmu->asid_map, cfg->cd.asid);
+               }
+       } else {
+               struct arm_smmu_s2_cfg *cfg = &smmu_domain->s2_cfg;
+               if (cfg->vmid)
+                       arm_smmu_bitmap_free(smmu->vmid_map, cfg->vmid);
+       }
+
+       kfree(smmu_domain);
+}
+
+static int arm_smmu_domain_finalise_s1(struct arm_smmu_domain *smmu_domain,
+                                      struct io_pgtable_cfg *pgtbl_cfg)
+{
+       int ret;
+       u16 asid;
+       struct arm_smmu_device *smmu = smmu_domain->smmu;
+       struct arm_smmu_s1_cfg *cfg = &smmu_domain->s1_cfg;
+
+       asid = arm_smmu_bitmap_alloc(smmu->asid_map, smmu->asid_bits);
+       if (IS_ERR_VALUE(asid))
+               return asid;
+
+       cfg->cdptr = dma_zalloc_coherent(smmu->dev, CTXDESC_CD_DWORDS << 3,
+                                        &cfg->cdptr_dma, GFP_KERNEL);
+       if (!cfg->cdptr) {
+               dev_warn(smmu->dev, "failed to allocate context descriptor\n");
+               goto out_free_asid;
+       }
+
+       cfg->cd.asid    = asid;
+       cfg->cd.ttbr    = pgtbl_cfg->arm_lpae_s1_cfg.ttbr[0];
+       cfg->cd.tcr     = pgtbl_cfg->arm_lpae_s1_cfg.tcr;
+       cfg->cd.mair    = pgtbl_cfg->arm_lpae_s1_cfg.mair[0];
+       return 0;
+
+out_free_asid:
+       arm_smmu_bitmap_free(smmu->asid_map, asid);
+       return ret;
+}
+
+static int arm_smmu_domain_finalise_s2(struct arm_smmu_domain *smmu_domain,
+                                      struct io_pgtable_cfg *pgtbl_cfg)
+{
+       u16 vmid;
+       struct arm_smmu_device *smmu = smmu_domain->smmu;
+       struct arm_smmu_s2_cfg *cfg = &smmu_domain->s2_cfg;
+
+       vmid = arm_smmu_bitmap_alloc(smmu->vmid_map, smmu->vmid_bits);
+       if (IS_ERR_VALUE(vmid))
+               return vmid;
+
+       cfg->vmid       = vmid;
+       cfg->vttbr      = pgtbl_cfg->arm_lpae_s2_cfg.vttbr;
+       cfg->vtcr       = pgtbl_cfg->arm_lpae_s2_cfg.vtcr;
+       return 0;
+}
+
+static struct iommu_ops arm_smmu_ops;
+
+static int arm_smmu_domain_finalise(struct iommu_domain *domain)
+{
+       int ret;
+       unsigned long ias, oas;
+       enum io_pgtable_fmt fmt;
+       struct io_pgtable_cfg pgtbl_cfg;
+       struct io_pgtable_ops *pgtbl_ops;
+       int (*finalise_stage_fn)(struct arm_smmu_domain *,
+                                struct io_pgtable_cfg *);
+       struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
+       struct arm_smmu_device *smmu = smmu_domain->smmu;
+
+       /* Restrict the stage to what we can actually support */
+       if (!(smmu->features & ARM_SMMU_FEAT_TRANS_S1))
+               smmu_domain->stage = ARM_SMMU_DOMAIN_S2;
+       if (!(smmu->features & ARM_SMMU_FEAT_TRANS_S2))
+               smmu_domain->stage = ARM_SMMU_DOMAIN_S1;
+
+       switch (smmu_domain->stage) {
+       case ARM_SMMU_DOMAIN_S1:
+               ias = VA_BITS;
+               oas = smmu->ias;
+               fmt = ARM_64_LPAE_S1;
+               finalise_stage_fn = arm_smmu_domain_finalise_s1;
+               break;
+       case ARM_SMMU_DOMAIN_NESTED:
+       case ARM_SMMU_DOMAIN_S2:
+               ias = smmu->ias;
+               oas = smmu->oas;
+               fmt = ARM_64_LPAE_S2;
+               finalise_stage_fn = arm_smmu_domain_finalise_s2;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       pgtbl_cfg = (struct io_pgtable_cfg) {
+               .pgsize_bitmap  = arm_smmu_ops.pgsize_bitmap,
+               .ias            = ias,
+               .oas            = oas,
+               .tlb            = &arm_smmu_gather_ops,
+       };
+
+       pgtbl_ops = alloc_io_pgtable_ops(fmt, &pgtbl_cfg, smmu_domain);
+       if (!pgtbl_ops)
+               return -ENOMEM;
+
+       arm_smmu_ops.pgsize_bitmap = pgtbl_cfg.pgsize_bitmap;
+       smmu_domain->pgtbl_ops = pgtbl_ops;
+
+       ret = finalise_stage_fn(smmu_domain, &pgtbl_cfg);
+       if (IS_ERR_VALUE(ret))
+               free_io_pgtable_ops(pgtbl_ops);
+
+       return ret;
+}
+
+static struct arm_smmu_group *arm_smmu_group_get(struct device *dev)
+{
+       struct iommu_group *group;
+       struct arm_smmu_group *smmu_group;
+
+       group = iommu_group_get(dev);
+       if (!group)
+               return NULL;
+
+       smmu_group = iommu_group_get_iommudata(group);
+       iommu_group_put(group);
+       return smmu_group;
+}
+
+static __le64 *arm_smmu_get_step_for_sid(struct arm_smmu_device *smmu, u32 sid)
+{
+       __le64 *step;
+       struct arm_smmu_strtab_cfg *cfg = &smmu->strtab_cfg;
+
+       if (smmu->features & ARM_SMMU_FEAT_2_LVL_STRTAB) {
+               struct arm_smmu_strtab_l1_desc *l1_desc;
+               int idx;
+
+               /* Two-level walk */
+               idx = (sid >> STRTAB_SPLIT) * STRTAB_L1_DESC_DWORDS;
+               l1_desc = &cfg->l1_desc[idx];
+               idx = (sid & ((1 << STRTAB_SPLIT) - 1)) * STRTAB_STE_DWORDS;
+               step = &l1_desc->l2ptr[idx];
+       } else {
+               /* Simple linear lookup */
+               step = &cfg->strtab[sid * STRTAB_STE_DWORDS];
+       }
+
+       return step;
+}
+
+static int arm_smmu_install_ste_for_group(struct arm_smmu_group *smmu_group)
+{
+       int i;
+       struct arm_smmu_domain *smmu_domain = smmu_group->domain;
+       struct arm_smmu_strtab_ent *ste = &smmu_group->ste;
+       struct arm_smmu_device *smmu = smmu_group->smmu;
+
+       if (smmu_domain->stage == ARM_SMMU_DOMAIN_S1) {
+               ste->s1_cfg = &smmu_domain->s1_cfg;
+               ste->s2_cfg = NULL;
+               arm_smmu_write_ctx_desc(smmu, ste->s1_cfg);
+       } else {
+               ste->s1_cfg = NULL;
+               ste->s2_cfg = &smmu_domain->s2_cfg;
+       }
+
+       for (i = 0; i < smmu_group->num_sids; ++i) {
+               u32 sid = smmu_group->sids[i];
+               __le64 *step = arm_smmu_get_step_for_sid(smmu, sid);
+
+               arm_smmu_write_strtab_ent(smmu, sid, step, ste);
+       }
+
+       return 0;
+}
+
+static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
+{
+       int ret = 0;
+       struct arm_smmu_device *smmu;
+       struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
+       struct arm_smmu_group *smmu_group = arm_smmu_group_get(dev);
+
+       if (!smmu_group)
+               return -ENOENT;
+
+       /* Already attached to a different domain? */
+       if (smmu_group->domain && smmu_group->domain != smmu_domain)
+               return -EEXIST;
+
+       smmu = smmu_group->smmu;
+       mutex_lock(&smmu_domain->init_mutex);
+
+       if (!smmu_domain->smmu) {
+               smmu_domain->smmu = smmu;
+               ret = arm_smmu_domain_finalise(domain);
+               if (ret) {
+                       smmu_domain->smmu = NULL;
+                       goto out_unlock;
+               }
+       } else if (smmu_domain->smmu != smmu) {
+               dev_err(dev,
+                       "cannot attach to SMMU %s (upstream of %s)\n",
+                       dev_name(smmu_domain->smmu->dev),
+                       dev_name(smmu->dev));
+               ret = -ENXIO;
+               goto out_unlock;
+       }
+
+       /* Group already attached to this domain? */
+       if (smmu_group->domain)
+               goto out_unlock;
+
+       smmu_group->domain      = smmu_domain;
+       smmu_group->ste.bypass  = false;
+
+       ret = arm_smmu_install_ste_for_group(smmu_group);
+       if (IS_ERR_VALUE(ret))
+               smmu_group->domain = NULL;
+
+out_unlock:
+       mutex_unlock(&smmu_domain->init_mutex);
+       return ret;
+}
+
+static void arm_smmu_detach_dev(struct iommu_domain *domain, struct device *dev)
+{
+       struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
+       struct arm_smmu_group *smmu_group = arm_smmu_group_get(dev);
+
+       BUG_ON(!smmu_domain);
+       BUG_ON(!smmu_group);
+
+       mutex_lock(&smmu_domain->init_mutex);
+       BUG_ON(smmu_group->domain != smmu_domain);
+
+       smmu_group->ste.bypass = true;
+       if (IS_ERR_VALUE(arm_smmu_install_ste_for_group(smmu_group)))
+               dev_warn(dev, "failed to install bypass STE\n");
+
+       smmu_group->domain = NULL;
+       mutex_unlock(&smmu_domain->init_mutex);
+}
+
+static int arm_smmu_map(struct iommu_domain *domain, unsigned long iova,
+                       phys_addr_t paddr, size_t size, int prot)
+{
+       int ret;
+       unsigned long flags;
+       struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
+       struct io_pgtable_ops *ops = smmu_domain->pgtbl_ops;
+
+       if (!ops)
+               return -ENODEV;
+
+       spin_lock_irqsave(&smmu_domain->pgtbl_lock, flags);
+       ret = ops->map(ops, iova, paddr, size, prot);
+       spin_unlock_irqrestore(&smmu_domain->pgtbl_lock, flags);
+       return ret;
+}
+
+static size_t
+arm_smmu_unmap(struct iommu_domain *domain, unsigned long iova, size_t size)
+{
+       size_t ret;
+       unsigned long flags;
+       struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
+       struct io_pgtable_ops *ops = smmu_domain->pgtbl_ops;
+
+       if (!ops)
+               return 0;
+
+       spin_lock_irqsave(&smmu_domain->pgtbl_lock, flags);
+       ret = ops->unmap(ops, iova, size);
+       spin_unlock_irqrestore(&smmu_domain->pgtbl_lock, flags);
+       return ret;
+}
+
+static phys_addr_t
+arm_smmu_iova_to_phys(struct iommu_domain *domain, dma_addr_t iova)
+{
+       phys_addr_t ret;
+       unsigned long flags;
+       struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
+       struct io_pgtable_ops *ops = smmu_domain->pgtbl_ops;
+
+       if (!ops)
+               return 0;
+
+       spin_lock_irqsave(&smmu_domain->pgtbl_lock, flags);
+       ret = ops->iova_to_phys(ops, iova);
+       spin_unlock_irqrestore(&smmu_domain->pgtbl_lock, flags);
+
+       return ret;
+}
+
+static int __arm_smmu_get_pci_sid(struct pci_dev *pdev, u16 alias, void *sidp)
+{
+       *(u32 *)sidp = alias;
+       return 0; /* Continue walking */
+}
+
+static void __arm_smmu_release_pci_iommudata(void *data)
+{
+       kfree(data);
+}
+
+static struct arm_smmu_device *arm_smmu_get_for_pci_dev(struct pci_dev *pdev)
+{
+       struct device_node *of_node;
+       struct arm_smmu_device *curr, *smmu = NULL;
+       struct pci_bus *bus = pdev->bus;
+
+       /* Walk up to the root bus */
+       while (!pci_is_root_bus(bus))
+               bus = bus->parent;
+
+       /* Follow the "iommus" phandle from the host controller */
+       of_node = of_parse_phandle(bus->bridge->parent->of_node, "iommus", 0);
+       if (!of_node)
+               return NULL;
+
+       /* See if we can find an SMMU corresponding to the phandle */
+       spin_lock(&arm_smmu_devices_lock);
+       list_for_each_entry(curr, &arm_smmu_devices, list) {
+               if (curr->dev->of_node == of_node) {
+                       smmu = curr;
+                       break;
+               }
+       }
+       spin_unlock(&arm_smmu_devices_lock);
+       of_node_put(of_node);
+       return smmu;
+}
+
+static bool arm_smmu_sid_in_range(struct arm_smmu_device *smmu, u32 sid)
+{
+       unsigned long limit = smmu->strtab_cfg.num_l1_ents;
+
+       if (smmu->features & ARM_SMMU_FEAT_2_LVL_STRTAB)
+               limit *= 1UL << STRTAB_SPLIT;
+
+       return sid < limit;
+}
+
+static int arm_smmu_add_device(struct device *dev)
+{
+       int i, ret;
+       u32 sid, *sids;
+       struct pci_dev *pdev;
+       struct iommu_group *group;
+       struct arm_smmu_group *smmu_group;
+       struct arm_smmu_device *smmu;
+
+       /* We only support PCI, for now */
+       if (!dev_is_pci(dev))
+               return -ENODEV;
+
+       pdev = to_pci_dev(dev);
+       group = iommu_group_get_for_dev(dev);
+       if (IS_ERR(group))
+               return PTR_ERR(group);
+
+       smmu_group = iommu_group_get_iommudata(group);
+       if (!smmu_group) {
+               smmu = arm_smmu_get_for_pci_dev(pdev);
+               if (!smmu) {
+                       ret = -ENOENT;
+                       goto out_put_group;
+               }
+
+               smmu_group = kzalloc(sizeof(*smmu_group), GFP_KERNEL);
+               if (!smmu_group) {
+                       ret = -ENOMEM;
+                       goto out_put_group;
+               }
+
+               smmu_group->ste.valid   = true;
+               smmu_group->smmu        = smmu;
+               iommu_group_set_iommudata(group, smmu_group,
+                                         __arm_smmu_release_pci_iommudata);
+       } else {
+               smmu = smmu_group->smmu;
+       }
+
+       /* Assume SID == RID until firmware tells us otherwise */
+       pci_for_each_dma_alias(pdev, __arm_smmu_get_pci_sid, &sid);
+       for (i = 0; i < smmu_group->num_sids; ++i) {
+               /* If we already know about this SID, then we're done */
+               if (smmu_group->sids[i] == sid)
+                       return 0;
+       }
+
+       /* Check the SID is in range of the SMMU and our stream table */
+       if (!arm_smmu_sid_in_range(smmu, sid)) {
+               ret = -ERANGE;
+               goto out_put_group;
+       }
+
+       /* Ensure l2 strtab is initialised */
+       if (smmu->features & ARM_SMMU_FEAT_2_LVL_STRTAB) {
+               ret = arm_smmu_init_l2_strtab(smmu, sid);
+               if (ret)
+                       goto out_put_group;
+       }
+
+       /* Resize the SID array for the group */
+       smmu_group->num_sids++;
+       sids = krealloc(smmu_group->sids, smmu_group->num_sids * sizeof(*sids),
+                       GFP_KERNEL);
+       if (!sids) {
+               smmu_group->num_sids--;
+               ret = -ENOMEM;
+               goto out_put_group;
+       }
+
+       /* Add the new SID */
+       sids[smmu_group->num_sids - 1] = sid;
+       smmu_group->sids = sids;
+       return 0;
+
+out_put_group:
+       iommu_group_put(group);
+       return ret;
+}
+
+static void arm_smmu_remove_device(struct device *dev)
+{
+       iommu_group_remove_device(dev);
+}
+
+static int arm_smmu_domain_get_attr(struct iommu_domain *domain,
+                                   enum iommu_attr attr, void *data)
+{
+       struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
+
+       switch (attr) {
+       case DOMAIN_ATTR_NESTING:
+               *(int *)data = (smmu_domain->stage == ARM_SMMU_DOMAIN_NESTED);
+               return 0;
+       default:
+               return -ENODEV;
+       }
+}
+
+static int arm_smmu_domain_set_attr(struct iommu_domain *domain,
+                                   enum iommu_attr attr, void *data)
+{
+       int ret = 0;
+       struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
+
+       mutex_lock(&smmu_domain->init_mutex);
+
+       switch (attr) {
+       case DOMAIN_ATTR_NESTING:
+               if (smmu_domain->smmu) {
+                       ret = -EPERM;
+                       goto out_unlock;
+               }
+
+               if (*(int *)data)
+                       smmu_domain->stage = ARM_SMMU_DOMAIN_NESTED;
+               else
+                       smmu_domain->stage = ARM_SMMU_DOMAIN_S1;
+
+               break;
+       default:
+               ret = -ENODEV;
+       }
+
+out_unlock:
+       mutex_unlock(&smmu_domain->init_mutex);
+       return ret;
+}
+
+static struct iommu_ops arm_smmu_ops = {
+       .capable                = arm_smmu_capable,
+       .domain_alloc           = arm_smmu_domain_alloc,
+       .domain_free            = arm_smmu_domain_free,
+       .attach_dev             = arm_smmu_attach_dev,
+       .detach_dev             = arm_smmu_detach_dev,
+       .map                    = arm_smmu_map,
+       .unmap                  = arm_smmu_unmap,
+       .iova_to_phys           = arm_smmu_iova_to_phys,
+       .add_device             = arm_smmu_add_device,
+       .remove_device          = arm_smmu_remove_device,
+       .domain_get_attr        = arm_smmu_domain_get_attr,
+       .domain_set_attr        = arm_smmu_domain_set_attr,
+       .pgsize_bitmap          = -1UL, /* Restricted during device attach */
+};
+
+/* Probing and initialisation functions */
+static int arm_smmu_init_one_queue(struct arm_smmu_device *smmu,
+                                  struct arm_smmu_queue *q,
+                                  unsigned long prod_off,
+                                  unsigned long cons_off,
+                                  size_t dwords)
+{
+       size_t qsz = ((1 << q->max_n_shift) * dwords) << 3;
+
+       q->base = dma_alloc_coherent(smmu->dev, qsz, &q->base_dma, GFP_KERNEL);
+       if (!q->base) {
+               dev_err(smmu->dev, "failed to allocate queue (0x%zx bytes)\n",
+                       qsz);
+               return -ENOMEM;
+       }
+
+       q->prod_reg     = smmu->base + prod_off;
+       q->cons_reg     = smmu->base + cons_off;
+       q->ent_dwords   = dwords;
+
+       q->q_base  = Q_BASE_RWA;
+       q->q_base |= q->base_dma & Q_BASE_ADDR_MASK << Q_BASE_ADDR_SHIFT;
+       q->q_base |= (q->max_n_shift & Q_BASE_LOG2SIZE_MASK)
+                    << Q_BASE_LOG2SIZE_SHIFT;
+
+       q->prod = q->cons = 0;
+       return 0;
+}
+
+static void arm_smmu_free_one_queue(struct arm_smmu_device *smmu,
+                                   struct arm_smmu_queue *q)
+{
+       size_t qsz = ((1 << q->max_n_shift) * q->ent_dwords) << 3;
+
+       dma_free_coherent(smmu->dev, qsz, q->base, q->base_dma);
+}
+
+static void arm_smmu_free_queues(struct arm_smmu_device *smmu)
+{
+       arm_smmu_free_one_queue(smmu, &smmu->cmdq.q);
+       arm_smmu_free_one_queue(smmu, &smmu->evtq.q);
+
+       if (smmu->features & ARM_SMMU_FEAT_PRI)
+               arm_smmu_free_one_queue(smmu, &smmu->priq.q);
+}
+
+static int arm_smmu_init_queues(struct arm_smmu_device *smmu)
+{
+       int ret;
+
+       /* cmdq */
+       spin_lock_init(&smmu->cmdq.lock);
+       ret = arm_smmu_init_one_queue(smmu, &smmu->cmdq.q, ARM_SMMU_CMDQ_PROD,
+                                     ARM_SMMU_CMDQ_CONS, CMDQ_ENT_DWORDS);
+       if (ret)
+               goto out;
+
+       /* evtq */
+       ret = arm_smmu_init_one_queue(smmu, &smmu->evtq.q, ARM_SMMU_EVTQ_PROD,
+                                     ARM_SMMU_EVTQ_CONS, EVTQ_ENT_DWORDS);
+       if (ret)
+               goto out_free_cmdq;
+
+       /* priq */
+       if (!(smmu->features & ARM_SMMU_FEAT_PRI))
+               return 0;
+
+       ret = arm_smmu_init_one_queue(smmu, &smmu->priq.q, ARM_SMMU_PRIQ_PROD,
+                                     ARM_SMMU_PRIQ_CONS, PRIQ_ENT_DWORDS);
+       if (ret)
+               goto out_free_evtq;
+
+       return 0;
+
+out_free_evtq:
+       arm_smmu_free_one_queue(smmu, &smmu->evtq.q);
+out_free_cmdq:
+       arm_smmu_free_one_queue(smmu, &smmu->cmdq.q);
+out:
+       return ret;
+}
+
+static void arm_smmu_free_l2_strtab(struct arm_smmu_device *smmu)
+{
+       int i;
+       size_t size;
+       struct arm_smmu_strtab_cfg *cfg = &smmu->strtab_cfg;
+
+       size = 1 << (STRTAB_SPLIT + ilog2(STRTAB_STE_DWORDS) + 3);
+       for (i = 0; i < cfg->num_l1_ents; ++i) {
+               struct arm_smmu_strtab_l1_desc *desc = &cfg->l1_desc[i];
+
+               if (!desc->l2ptr)
+                       continue;
+
+               dma_free_coherent(smmu->dev, size, desc->l2ptr,
+                                 desc->l2ptr_dma);
+       }
+}
+
+static int arm_smmu_init_l1_strtab(struct arm_smmu_device *smmu)
+{
+       unsigned int i;
+       struct arm_smmu_strtab_cfg *cfg = &smmu->strtab_cfg;
+       size_t size = sizeof(*cfg->l1_desc) * cfg->num_l1_ents;
+       void *strtab = smmu->strtab_cfg.strtab;
+
+       cfg->l1_desc = devm_kzalloc(smmu->dev, size, GFP_KERNEL);
+       if (!cfg->l1_desc) {
+               dev_err(smmu->dev, "failed to allocate l1 stream table desc\n");
+               return -ENOMEM;
+       }
+
+       for (i = 0; i < cfg->num_l1_ents; ++i) {
+               arm_smmu_write_strtab_l1_desc(strtab, &cfg->l1_desc[i]);
+               strtab += STRTAB_L1_DESC_DWORDS << 3;
+       }
+
+       return 0;
+}
+
+static int arm_smmu_init_strtab_2lvl(struct arm_smmu_device *smmu)
+{
+       void *strtab;
+       u64 reg;
+       u32 size;
+       int ret;
+       struct arm_smmu_strtab_cfg *cfg = &smmu->strtab_cfg;
+
+       /* Calculate the L1 size, capped to the SIDSIZE */
+       size = STRTAB_L1_SZ_SHIFT - (ilog2(STRTAB_L1_DESC_DWORDS) + 3);
+       size = min(size, smmu->sid_bits - STRTAB_SPLIT);
+       if (size + STRTAB_SPLIT < smmu->sid_bits)
+               dev_warn(smmu->dev,
+                        "2-level strtab only covers %u/%u bits of SID\n",
+                        size + STRTAB_SPLIT, smmu->sid_bits);
+
+       cfg->num_l1_ents = 1 << size;
+       size = cfg->num_l1_ents * (STRTAB_L1_DESC_DWORDS << 3);
+       strtab = dma_zalloc_coherent(smmu->dev, size, &cfg->strtab_dma,
+                                    GFP_KERNEL);
+       if (!strtab) {
+               dev_err(smmu->dev,
+                       "failed to allocate l1 stream table (%u bytes)\n",
+                       size);
+               return -ENOMEM;
+       }
+       cfg->strtab = strtab;
+
+       /* Configure strtab_base_cfg for 2 levels */
+       reg  = STRTAB_BASE_CFG_FMT_2LVL;
+       reg |= (size & STRTAB_BASE_CFG_LOG2SIZE_MASK)
+               << STRTAB_BASE_CFG_LOG2SIZE_SHIFT;
+       reg |= (STRTAB_SPLIT & STRTAB_BASE_CFG_SPLIT_MASK)
+               << STRTAB_BASE_CFG_SPLIT_SHIFT;
+       cfg->strtab_base_cfg = reg;
+
+       ret = arm_smmu_init_l1_strtab(smmu);
+       if (ret)
+               dma_free_coherent(smmu->dev,
+                                 cfg->num_l1_ents *
+                                 (STRTAB_L1_DESC_DWORDS << 3),
+                                 strtab,
+                                 cfg->strtab_dma);
+       return ret;
+}
+
+static int arm_smmu_init_strtab_linear(struct arm_smmu_device *smmu)
+{
+       void *strtab;
+       u64 reg;
+       u32 size;
+       struct arm_smmu_strtab_cfg *cfg = &smmu->strtab_cfg;
+
+       size = (1 << smmu->sid_bits) * (STRTAB_STE_DWORDS << 3);
+       strtab = dma_zalloc_coherent(smmu->dev, size, &cfg->strtab_dma,
+                                    GFP_KERNEL);
+       if (!strtab) {
+               dev_err(smmu->dev,
+                       "failed to allocate linear stream table (%u bytes)\n",
+                       size);
+               return -ENOMEM;
+       }
+       cfg->strtab = strtab;
+       cfg->num_l1_ents = 1 << smmu->sid_bits;
+
+       /* Configure strtab_base_cfg for a linear table covering all SIDs */
+       reg  = STRTAB_BASE_CFG_FMT_LINEAR;
+       reg |= (smmu->sid_bits & STRTAB_BASE_CFG_LOG2SIZE_MASK)
+               << STRTAB_BASE_CFG_LOG2SIZE_SHIFT;
+       cfg->strtab_base_cfg = reg;
+
+       arm_smmu_init_bypass_stes(strtab, cfg->num_l1_ents);
+       return 0;
+}
+
+static int arm_smmu_init_strtab(struct arm_smmu_device *smmu)
+{
+       u64 reg;
+       int ret;
+
+       if (smmu->features & ARM_SMMU_FEAT_2_LVL_STRTAB)
+               ret = arm_smmu_init_strtab_2lvl(smmu);
+       else
+               ret = arm_smmu_init_strtab_linear(smmu);
+
+       if (ret)
+               return ret;
+
+       /* Set the strtab base address */
+       reg  = smmu->strtab_cfg.strtab_dma &
+              STRTAB_BASE_ADDR_MASK << STRTAB_BASE_ADDR_SHIFT;
+       reg |= STRTAB_BASE_RA;
+       smmu->strtab_cfg.strtab_base = reg;
+
+       /* Allocate the first VMID for stage-2 bypass STEs */
+       set_bit(0, smmu->vmid_map);
+       return 0;
+}
+
+static void arm_smmu_free_strtab(struct arm_smmu_device *smmu)
+{
+       struct arm_smmu_strtab_cfg *cfg = &smmu->strtab_cfg;
+       u32 size = cfg->num_l1_ents;
+
+       if (smmu->features & ARM_SMMU_FEAT_2_LVL_STRTAB) {
+               arm_smmu_free_l2_strtab(smmu);
+               size *= STRTAB_L1_DESC_DWORDS << 3;
+       } else {
+               size *= STRTAB_STE_DWORDS * 3;
+       }
+
+       dma_free_coherent(smmu->dev, size, cfg->strtab, cfg->strtab_dma);
+}
+
+static int arm_smmu_init_structures(struct arm_smmu_device *smmu)
+{
+       int ret;
+
+       ret = arm_smmu_init_queues(smmu);
+       if (ret)
+               return ret;
+
+       ret = arm_smmu_init_strtab(smmu);
+       if (ret)
+               goto out_free_queues;
+
+       return 0;
+
+out_free_queues:
+       arm_smmu_free_queues(smmu);
+       return ret;
+}
+
+static void arm_smmu_free_structures(struct arm_smmu_device *smmu)
+{
+       arm_smmu_free_strtab(smmu);
+       arm_smmu_free_queues(smmu);
+}
+
+static int arm_smmu_write_reg_sync(struct arm_smmu_device *smmu, u32 val,
+                                  unsigned int reg_off, unsigned int ack_off)
+{
+       u32 reg;
+
+       writel_relaxed(val, smmu->base + reg_off);
+       return readl_relaxed_poll_timeout(smmu->base + ack_off, reg, reg == val,
+                                         1, ARM_SMMU_POLL_TIMEOUT_US);
+}
+
+static int arm_smmu_setup_irqs(struct arm_smmu_device *smmu)
+{
+       int ret, irq;
+
+       /* Disable IRQs first */
+       ret = arm_smmu_write_reg_sync(smmu, 0, ARM_SMMU_IRQ_CTRL,
+                                     ARM_SMMU_IRQ_CTRLACK);
+       if (ret) {
+               dev_err(smmu->dev, "failed to disable irqs\n");
+               return ret;
+       }
+
+       /* Clear the MSI address regs */
+       writeq_relaxed(0, smmu->base + ARM_SMMU_GERROR_IRQ_CFG0);
+       writeq_relaxed(0, smmu->base + ARM_SMMU_EVTQ_IRQ_CFG0);
+
+       /* Request wired interrupt lines */
+       irq = smmu->evtq.q.irq;
+       if (irq) {
+               ret = devm_request_threaded_irq(smmu->dev, irq,
+                                               arm_smmu_evtq_handler,
+                                               arm_smmu_evtq_thread,
+                                               0, "arm-smmu-v3-evtq", smmu);
+               if (IS_ERR_VALUE(ret))
+                       dev_warn(smmu->dev, "failed to enable evtq irq\n");
+       }
+
+       irq = smmu->cmdq.q.irq;
+       if (irq) {
+               ret = devm_request_irq(smmu->dev, irq,
+                                      arm_smmu_cmdq_sync_handler, 0,
+                                      "arm-smmu-v3-cmdq-sync", smmu);
+               if (IS_ERR_VALUE(ret))
+                       dev_warn(smmu->dev, "failed to enable cmdq-sync irq\n");
+       }
+
+       irq = smmu->gerr_irq;
+       if (irq) {
+               ret = devm_request_irq(smmu->dev, irq, arm_smmu_gerror_handler,
+                                      0, "arm-smmu-v3-gerror", smmu);
+               if (IS_ERR_VALUE(ret))
+                       dev_warn(smmu->dev, "failed to enable gerror irq\n");
+       }
+
+       if (smmu->features & ARM_SMMU_FEAT_PRI) {
+               writeq_relaxed(0, smmu->base + ARM_SMMU_PRIQ_IRQ_CFG0);
+
+               irq = smmu->priq.q.irq;
+               if (irq) {
+                       ret = devm_request_threaded_irq(smmu->dev, irq,
+                                                       arm_smmu_priq_handler,
+                                                       arm_smmu_priq_thread,
+                                                       0, "arm-smmu-v3-priq",
+                                                       smmu);
+                       if (IS_ERR_VALUE(ret))
+                               dev_warn(smmu->dev,
+                                        "failed to enable priq irq\n");
+               }
+       }
+
+       /* Enable interrupt generation on the SMMU */
+       ret = arm_smmu_write_reg_sync(smmu,
+                                     IRQ_CTRL_EVTQ_IRQEN |
+                                     IRQ_CTRL_GERROR_IRQEN,
+                                     ARM_SMMU_IRQ_CTRL, ARM_SMMU_IRQ_CTRLACK);
+       if (ret)
+               dev_warn(smmu->dev, "failed to enable irqs\n");
+
+       return 0;
+}
+
+static int arm_smmu_device_disable(struct arm_smmu_device *smmu)
+{
+       int ret;
+
+       ret = arm_smmu_write_reg_sync(smmu, 0, ARM_SMMU_CR0, ARM_SMMU_CR0ACK);
+       if (ret)
+               dev_err(smmu->dev, "failed to clear cr0\n");
+
+       return ret;
+}
+
+static int arm_smmu_device_reset(struct arm_smmu_device *smmu)
+{
+       int ret;
+       u32 reg, enables;
+       struct arm_smmu_cmdq_ent cmd;
+
+       /* Clear CR0 and sync (disables SMMU and queue processing) */
+       reg = readl_relaxed(smmu->base + ARM_SMMU_CR0);
+       if (reg & CR0_SMMUEN)
+               dev_warn(smmu->dev, "SMMU currently enabled! Resetting...\n");
+
+       ret = arm_smmu_device_disable(smmu);
+       if (ret)
+               return ret;
+
+       /* CR1 (table and queue memory attributes) */
+       reg = (CR1_SH_ISH << CR1_TABLE_SH_SHIFT) |
+             (CR1_CACHE_WB << CR1_TABLE_OC_SHIFT) |
+             (CR1_CACHE_WB << CR1_TABLE_IC_SHIFT) |
+             (CR1_SH_ISH << CR1_QUEUE_SH_SHIFT) |
+             (CR1_CACHE_WB << CR1_QUEUE_OC_SHIFT) |
+             (CR1_CACHE_WB << CR1_QUEUE_IC_SHIFT);
+       writel_relaxed(reg, smmu->base + ARM_SMMU_CR1);
+
+       /* CR2 (random crap) */
+       reg = CR2_PTM | CR2_RECINVSID | CR2_E2H;
+       writel_relaxed(reg, smmu->base + ARM_SMMU_CR2);
+
+       /* Stream table */
+       writeq_relaxed(smmu->strtab_cfg.strtab_base,
+                      smmu->base + ARM_SMMU_STRTAB_BASE);
+       writel_relaxed(smmu->strtab_cfg.strtab_base_cfg,
+                      smmu->base + ARM_SMMU_STRTAB_BASE_CFG);
+
+       /* Command queue */
+       writeq_relaxed(smmu->cmdq.q.q_base, smmu->base + ARM_SMMU_CMDQ_BASE);
+       writel_relaxed(smmu->cmdq.q.prod, smmu->base + ARM_SMMU_CMDQ_PROD);
+       writel_relaxed(smmu->cmdq.q.cons, smmu->base + ARM_SMMU_CMDQ_CONS);
+
+       enables = CR0_CMDQEN;
+       ret = arm_smmu_write_reg_sync(smmu, enables, ARM_SMMU_CR0,
+                                     ARM_SMMU_CR0ACK);
+       if (ret) {
+               dev_err(smmu->dev, "failed to enable command queue\n");
+               return ret;
+       }
+
+       /* Invalidate any cached configuration */
+       cmd.opcode = CMDQ_OP_CFGI_ALL;
+       arm_smmu_cmdq_issue_cmd(smmu, &cmd);
+       cmd.opcode = CMDQ_OP_CMD_SYNC;
+       arm_smmu_cmdq_issue_cmd(smmu, &cmd);
+
+       /* Invalidate any stale TLB entries */
+       if (smmu->features & ARM_SMMU_FEAT_HYP) {
+               cmd.opcode = CMDQ_OP_TLBI_EL2_ALL;
+               arm_smmu_cmdq_issue_cmd(smmu, &cmd);
+       }
+
+       cmd.opcode = CMDQ_OP_TLBI_NSNH_ALL;
+       arm_smmu_cmdq_issue_cmd(smmu, &cmd);
+       cmd.opcode = CMDQ_OP_CMD_SYNC;
+       arm_smmu_cmdq_issue_cmd(smmu, &cmd);
+
+       /* Event queue */
+       writeq_relaxed(smmu->evtq.q.q_base, smmu->base + ARM_SMMU_EVTQ_BASE);
+       writel_relaxed(smmu->evtq.q.prod, smmu->base + ARM_SMMU_EVTQ_PROD);
+       writel_relaxed(smmu->evtq.q.cons, smmu->base + ARM_SMMU_EVTQ_CONS);
+
+       enables |= CR0_EVTQEN;
+       ret = arm_smmu_write_reg_sync(smmu, enables, ARM_SMMU_CR0,
+                                     ARM_SMMU_CR0ACK);
+       if (ret) {
+               dev_err(smmu->dev, "failed to enable event queue\n");
+               return ret;
+       }
+
+       /* PRI queue */
+       if (smmu->features & ARM_SMMU_FEAT_PRI) {
+               writeq_relaxed(smmu->priq.q.q_base,
+                              smmu->base + ARM_SMMU_PRIQ_BASE);
+               writel_relaxed(smmu->priq.q.prod,
+                              smmu->base + ARM_SMMU_PRIQ_PROD);
+               writel_relaxed(smmu->priq.q.cons,
+                              smmu->base + ARM_SMMU_PRIQ_CONS);
+
+               enables |= CR0_PRIQEN;
+               ret = arm_smmu_write_reg_sync(smmu, enables, ARM_SMMU_CR0,
+                                             ARM_SMMU_CR0ACK);
+               if (ret) {
+                       dev_err(smmu->dev, "failed to enable PRI queue\n");
+                       return ret;
+               }
+       }
+
+       ret = arm_smmu_setup_irqs(smmu);
+       if (ret) {
+               dev_err(smmu->dev, "failed to setup irqs\n");
+               return ret;
+       }
+
+       /* Enable the SMMU interface */
+       enables |= CR0_SMMUEN;
+       ret = arm_smmu_write_reg_sync(smmu, enables, ARM_SMMU_CR0,
+                                     ARM_SMMU_CR0ACK);
+       if (ret) {
+               dev_err(smmu->dev, "failed to enable SMMU interface\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+static int arm_smmu_device_probe(struct arm_smmu_device *smmu)
+{
+       u32 reg;
+       bool coherent;
+       unsigned long pgsize_bitmap = 0;
+
+       /* IDR0 */
+       reg = readl_relaxed(smmu->base + ARM_SMMU_IDR0);
+
+       /* 2-level structures */
+       if ((reg & IDR0_ST_LVL_MASK << IDR0_ST_LVL_SHIFT) == IDR0_ST_LVL_2LVL)
+               smmu->features |= ARM_SMMU_FEAT_2_LVL_STRTAB;
+
+       if (reg & IDR0_CD2L)
+               smmu->features |= ARM_SMMU_FEAT_2_LVL_CDTAB;
+
+       /*
+        * Translation table endianness.
+        * We currently require the same endianness as the CPU, but this
+        * could be changed later by adding a new IO_PGTABLE_QUIRK.
+        */
+       switch (reg & IDR0_TTENDIAN_MASK << IDR0_TTENDIAN_SHIFT) {
+       case IDR0_TTENDIAN_MIXED:
+               smmu->features |= ARM_SMMU_FEAT_TT_LE | ARM_SMMU_FEAT_TT_BE;
+               break;
+#ifdef __BIG_ENDIAN
+       case IDR0_TTENDIAN_BE:
+               smmu->features |= ARM_SMMU_FEAT_TT_BE;
+               break;
+#else
+       case IDR0_TTENDIAN_LE:
+               smmu->features |= ARM_SMMU_FEAT_TT_LE;
+               break;
+#endif
+       default:
+               dev_err(smmu->dev, "unknown/unsupported TT endianness!\n");
+               return -ENXIO;
+       }
+
+       /* Boolean feature flags */
+       if (IS_ENABLED(CONFIG_PCI_PRI) && reg & IDR0_PRI)
+               smmu->features |= ARM_SMMU_FEAT_PRI;
+
+       if (IS_ENABLED(CONFIG_PCI_ATS) && reg & IDR0_ATS)
+               smmu->features |= ARM_SMMU_FEAT_ATS;
+
+       if (reg & IDR0_SEV)
+               smmu->features |= ARM_SMMU_FEAT_SEV;
+
+       if (reg & IDR0_MSI)
+               smmu->features |= ARM_SMMU_FEAT_MSI;
+
+       if (reg & IDR0_HYP)
+               smmu->features |= ARM_SMMU_FEAT_HYP;
+
+       /*
+        * The dma-coherent property is used in preference to the ID
+        * register, but warn on mismatch.
+        */
+       coherent = of_dma_is_coherent(smmu->dev->of_node);
+       if (coherent)
+               smmu->features |= ARM_SMMU_FEAT_COHERENCY;
+
+       if (!!(reg & IDR0_COHACC) != coherent)
+               dev_warn(smmu->dev, "IDR0.COHACC overridden by dma-coherent property (%s)\n",
+                        coherent ? "true" : "false");
+
+       if (reg & IDR0_STALL_MODEL)
+               smmu->features |= ARM_SMMU_FEAT_STALLS;
+
+       if (reg & IDR0_S1P)
+               smmu->features |= ARM_SMMU_FEAT_TRANS_S1;
+
+       if (reg & IDR0_S2P)
+               smmu->features |= ARM_SMMU_FEAT_TRANS_S2;
+
+       if (!(reg & (IDR0_S1P | IDR0_S2P))) {
+               dev_err(smmu->dev, "no translation support!\n");
+               return -ENXIO;
+       }
+
+       /* We only support the AArch64 table format at present */
+       if ((reg & IDR0_TTF_MASK << IDR0_TTF_SHIFT) < IDR0_TTF_AARCH64) {
+               dev_err(smmu->dev, "AArch64 table format not supported!\n");
+               return -ENXIO;
+       }
+
+       /* ASID/VMID sizes */
+       smmu->asid_bits = reg & IDR0_ASID16 ? 16 : 8;
+       smmu->vmid_bits = reg & IDR0_VMID16 ? 16 : 8;
+
+       /* IDR1 */
+       reg = readl_relaxed(smmu->base + ARM_SMMU_IDR1);
+       if (reg & (IDR1_TABLES_PRESET | IDR1_QUEUES_PRESET | IDR1_REL)) {
+               dev_err(smmu->dev, "embedded implementation not supported\n");
+               return -ENXIO;
+       }
+
+       /* Queue sizes, capped at 4k */
+       smmu->cmdq.q.max_n_shift = min((u32)CMDQ_MAX_SZ_SHIFT,
+                                      reg >> IDR1_CMDQ_SHIFT & IDR1_CMDQ_MASK);
+       if (!smmu->cmdq.q.max_n_shift) {
+               /* Odd alignment restrictions on the base, so ignore for now */
+               dev_err(smmu->dev, "unit-length command queue not supported\n");
+               return -ENXIO;
+       }
+
+       smmu->evtq.q.max_n_shift = min((u32)EVTQ_MAX_SZ_SHIFT,
+                                      reg >> IDR1_EVTQ_SHIFT & IDR1_EVTQ_MASK);
+       smmu->priq.q.max_n_shift = min((u32)PRIQ_MAX_SZ_SHIFT,
+                                      reg >> IDR1_PRIQ_SHIFT & IDR1_PRIQ_MASK);
+
+       /* SID/SSID sizes */
+       smmu->ssid_bits = reg >> IDR1_SSID_SHIFT & IDR1_SSID_MASK;
+       smmu->sid_bits = reg >> IDR1_SID_SHIFT & IDR1_SID_MASK;
+
+       /* IDR5 */
+       reg = readl_relaxed(smmu->base + ARM_SMMU_IDR5);
+
+       /* Maximum number of outstanding stalls */
+       smmu->evtq.max_stalls = reg >> IDR5_STALL_MAX_SHIFT
+                               & IDR5_STALL_MAX_MASK;
+
+       /* Page sizes */
+       if (reg & IDR5_GRAN64K)
+               pgsize_bitmap |= SZ_64K | SZ_512M;
+       if (reg & IDR5_GRAN16K)
+               pgsize_bitmap |= SZ_16K | SZ_32M;
+       if (reg & IDR5_GRAN4K)
+               pgsize_bitmap |= SZ_4K | SZ_2M | SZ_1G;
+
+       arm_smmu_ops.pgsize_bitmap &= pgsize_bitmap;
+
+       /* Output address size */
+       switch (reg & IDR5_OAS_MASK << IDR5_OAS_SHIFT) {
+       case IDR5_OAS_32_BIT:
+               smmu->oas = 32;
+               break;
+       case IDR5_OAS_36_BIT:
+               smmu->oas = 36;
+               break;
+       case IDR5_OAS_40_BIT:
+               smmu->oas = 40;
+               break;
+       case IDR5_OAS_42_BIT:
+               smmu->oas = 42;
+               break;
+       case IDR5_OAS_44_BIT:
+               smmu->oas = 44;
+               break;
+       case IDR5_OAS_48_BIT:
+               smmu->oas = 48;
+               break;
+       default:
+               dev_err(smmu->dev, "unknown output address size!\n");
+               return -ENXIO;
+       }
+
+       /* Set the DMA mask for our table walker */
+       if (dma_set_mask_and_coherent(smmu->dev, DMA_BIT_MASK(smmu->oas)))
+               dev_warn(smmu->dev,
+                        "failed to set DMA mask for table walker\n");
+
+       if (!smmu->ias)
+               smmu->ias = smmu->oas;
+
+       dev_info(smmu->dev, "ias %lu-bit, oas %lu-bit (features 0x%08x)\n",
+                smmu->ias, smmu->oas, smmu->features);
+       return 0;
+}
+
+static int arm_smmu_device_dt_probe(struct platform_device *pdev)
+{
+       int irq, ret;
+       struct resource *res;
+       struct arm_smmu_device *smmu;
+       struct device *dev = &pdev->dev;
+
+       smmu = devm_kzalloc(dev, sizeof(*smmu), GFP_KERNEL);
+       if (!smmu) {
+               dev_err(dev, "failed to allocate arm_smmu_device\n");
+               return -ENOMEM;
+       }
+       smmu->dev = dev;
+
+       /* Base address */
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (resource_size(res) + 1 < SZ_128K) {
+               dev_err(dev, "MMIO region too small (%pr)\n", res);
+               return -EINVAL;
+       }
+
+       smmu->base = devm_ioremap_resource(dev, res);
+       if (IS_ERR(smmu->base))
+               return PTR_ERR(smmu->base);
+
+       /* Interrupt lines */
+       irq = platform_get_irq_byname(pdev, "eventq");
+       if (irq > 0)
+               smmu->evtq.q.irq = irq;
+
+       irq = platform_get_irq_byname(pdev, "priq");
+       if (irq > 0)
+               smmu->priq.q.irq = irq;
+
+       irq = platform_get_irq_byname(pdev, "cmdq-sync");
+       if (irq > 0)
+               smmu->cmdq.q.irq = irq;
+
+       irq = platform_get_irq_byname(pdev, "gerror");
+       if (irq > 0)
+               smmu->gerr_irq = irq;
+
+       /* Probe the h/w */
+       ret = arm_smmu_device_probe(smmu);
+       if (ret)
+               return ret;
+
+       /* Initialise in-memory data structures */
+       ret = arm_smmu_init_structures(smmu);
+       if (ret)
+               return ret;
+
+       /* Reset the device */
+       ret = arm_smmu_device_reset(smmu);
+       if (ret)
+               goto out_free_structures;
+
+       /* Record our private device structure */
+       INIT_LIST_HEAD(&smmu->list);
+       spin_lock(&arm_smmu_devices_lock);
+       list_add(&smmu->list, &arm_smmu_devices);
+       spin_unlock(&arm_smmu_devices_lock);
+       return 0;
+
+out_free_structures:
+       arm_smmu_free_structures(smmu);
+       return ret;
+}
+
+static int arm_smmu_device_remove(struct platform_device *pdev)
+{
+       struct arm_smmu_device *curr, *smmu = NULL;
+       struct device *dev = &pdev->dev;
+
+       spin_lock(&arm_smmu_devices_lock);
+       list_for_each_entry(curr, &arm_smmu_devices, list) {
+               if (curr->dev == dev) {
+                       smmu = curr;
+                       list_del(&smmu->list);
+                       break;
+               }
+       }
+       spin_unlock(&arm_smmu_devices_lock);
+
+       if (!smmu)
+               return -ENODEV;
+
+       arm_smmu_device_disable(smmu);
+       arm_smmu_free_structures(smmu);
+       return 0;
+}
+
+static struct of_device_id arm_smmu_of_match[] = {
+       { .compatible = "arm,smmu-v3", },
+       { },
+};
+MODULE_DEVICE_TABLE(of, arm_smmu_of_match);
+
+static struct platform_driver arm_smmu_driver = {
+       .driver = {
+               .name           = "arm-smmu-v3",
+               .of_match_table = of_match_ptr(arm_smmu_of_match),
+       },
+       .probe  = arm_smmu_device_dt_probe,
+       .remove = arm_smmu_device_remove,
+};
+
+static int __init arm_smmu_init(void)
+{
+       struct device_node *np;
+       int ret;
+
+       np = of_find_matching_node(NULL, arm_smmu_of_match);
+       if (!np)
+               return 0;
+
+       of_node_put(np);
+
+       ret = platform_driver_register(&arm_smmu_driver);
+       if (ret)
+               return ret;
+
+       return bus_set_iommu(&pci_bus_type, &arm_smmu_ops);
+}
+
+static void __exit arm_smmu_exit(void)
+{
+       return platform_driver_unregister(&arm_smmu_driver);
+}
+
+subsys_initcall(arm_smmu_init);
+module_exit(arm_smmu_exit);
+
+MODULE_DESCRIPTION("IOMMU API for ARM architected SMMUv3 implementations");
+MODULE_AUTHOR("Will Deacon <will.deacon@arm.com>");
+MODULE_LICENSE("GPL v2");
index 66a803b9dd3af928024d853995ed31383e0a8dd2..dce041b1c1394be7057528a5b6fd23dcbbbf6253 100644 (file)
 #define ARM_SMMU_CB_S1_TLBIVAL         0x620
 #define ARM_SMMU_CB_S2_TLBIIPAS2       0x630
 #define ARM_SMMU_CB_S2_TLBIIPAS2L      0x638
-#define ARM_SMMU_CB_ATS1PR_LO          0x800
-#define ARM_SMMU_CB_ATS1PR_HI          0x804
+#define ARM_SMMU_CB_ATS1PR             0x800
 #define ARM_SMMU_CB_ATSR               0x8f0
 
 #define SCTLR_S1_ASIDPNE               (1 << 12)
 #define FSYNR0_WNR                     (1 << 4)
 
 static int force_stage;
-module_param_named(force_stage, force_stage, int, S_IRUGO | S_IWUSR);
+module_param_named(force_stage, force_stage, int, S_IRUGO);
 MODULE_PARM_DESC(force_stage,
        "Force SMMU mappings to be installed at a particular stage of translation. A value of '1' or '2' forces the corresponding stage. All other values are ignored (i.e. no stage is forced). Note that selecting a specific stage will disable support for nested translation.");
 
@@ -1229,18 +1228,18 @@ static phys_addr_t arm_smmu_iova_to_phys_hard(struct iommu_domain *domain,
        void __iomem *cb_base;
        u32 tmp;
        u64 phys;
+       unsigned long va;
 
        cb_base = ARM_SMMU_CB_BASE(smmu) + ARM_SMMU_CB(smmu, cfg->cbndx);
 
-       if (smmu->version == 1) {
-               u32 reg = iova & ~0xfff;
-               writel_relaxed(reg, cb_base + ARM_SMMU_CB_ATS1PR_LO);
-       } else {
-               u32 reg = iova & ~0xfff;
-               writel_relaxed(reg, cb_base + ARM_SMMU_CB_ATS1PR_LO);
-               reg = ((u64)iova & ~0xfff) >> 32;
-               writel_relaxed(reg, cb_base + ARM_SMMU_CB_ATS1PR_HI);
-       }
+       /* ATS1 registers can only be written atomically */
+       va = iova & ~0xfffUL;
+#ifdef CONFIG_64BIT
+       if (smmu->version == ARM_SMMU_V2)
+               writeq_relaxed(va, cb_base + ARM_SMMU_CB_ATS1PR);
+       else
+#endif
+               writel_relaxed(va, cb_base + ARM_SMMU_CB_ATS1PR);
 
        if (readl_poll_timeout_atomic(cb_base + ARM_SMMU_CB_ATSR, tmp,
                                      !(tmp & ATSR_ACTIVE), 5, 50)) {
index 9847613085e157976707e0d1aa0cc87c3e8b3c68..c9db04d4ef39ae36553279859b6ca0f1c7972db1 100644 (file)
@@ -26,7 +26,7 @@
  * These routines are used by both DMA-remapping and Interrupt-remapping
  */
 
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt /* has to precede printk.h */
+#define pr_fmt(fmt)     "DMAR: " fmt
 
 #include <linux/pci.h>
 #include <linux/dmar.h>
@@ -555,7 +555,7 @@ static int dmar_walk_remapping_entries(struct acpi_dmar_header *start,
                        break;
                } else if (next > end) {
                        /* Avoid passing table end */
-                       pr_warn(FW_BUG "record passes table end\n");
+                       pr_warn(FW_BUG "Record passes table end\n");
                        ret = -EINVAL;
                        break;
                }
@@ -802,7 +802,7 @@ int __init dmar_table_init(void)
                ret = parse_dmar_table();
                if (ret < 0) {
                        if (ret != -ENODEV)
-                               pr_info("parse DMAR table failure.\n");
+                               pr_info("Parse DMAR table failure.\n");
                } else  if (list_empty(&dmar_drhd_units)) {
                        pr_info("No DMAR devices found\n");
                        ret = -ENODEV;
@@ -847,7 +847,7 @@ dmar_validate_one_drhd(struct acpi_dmar_header *entry, void *arg)
        else
                addr = early_ioremap(drhd->address, VTD_PAGE_SIZE);
        if (!addr) {
-               pr_warn("IOMMU: can't validate: %llx\n", drhd->address);
+               pr_warn("Can't validate DRHD address: %llx\n", drhd->address);
                return -EINVAL;
        }
 
@@ -921,14 +921,14 @@ static int map_iommu(struct intel_iommu *iommu, u64 phys_addr)
        iommu->reg_size = VTD_PAGE_SIZE;
 
        if (!request_mem_region(iommu->reg_phys, iommu->reg_size, iommu->name)) {
-               pr_err("IOMMU: can't reserve memory\n");
+               pr_err("Can't reserve memory\n");
                err = -EBUSY;
                goto out;
        }
 
        iommu->reg = ioremap(iommu->reg_phys, iommu->reg_size);
        if (!iommu->reg) {
-               pr_err("IOMMU: can't map the region\n");
+               pr_err("Can't map the region\n");
                err = -ENOMEM;
                goto release;
        }
@@ -952,13 +952,13 @@ static int map_iommu(struct intel_iommu *iommu, u64 phys_addr)
                iommu->reg_size = map_size;
                if (!request_mem_region(iommu->reg_phys, iommu->reg_size,
                                        iommu->name)) {
-                       pr_err("IOMMU: can't reserve memory\n");
+                       pr_err("Can't reserve memory\n");
                        err = -EBUSY;
                        goto out;
                }
                iommu->reg = ioremap(iommu->reg_phys, iommu->reg_size);
                if (!iommu->reg) {
-                       pr_err("IOMMU: can't map the region\n");
+                       pr_err("Can't map the region\n");
                        err = -ENOMEM;
                        goto release;
                }
@@ -1014,14 +1014,14 @@ static int alloc_iommu(struct dmar_drhd_unit *drhd)
                return -ENOMEM;
 
        if (dmar_alloc_seq_id(iommu) < 0) {
-               pr_err("IOMMU: failed to allocate seq_id\n");
+               pr_err("Failed to allocate seq_id\n");
                err = -ENOSPC;
                goto error;
        }
 
        err = map_iommu(iommu, drhd->reg_base_addr);
        if (err) {
-               pr_err("IOMMU: failed to map %s\n", iommu->name);
+               pr_err("Failed to map %s\n", iommu->name);
                goto error_free_seq_id;
        }
 
@@ -1045,8 +1045,8 @@ static int alloc_iommu(struct dmar_drhd_unit *drhd)
        iommu->node = -1;
 
        ver = readl(iommu->reg + DMAR_VER_REG);
-       pr_info("IOMMU %d: reg_base_addr %llx ver %d:%d cap %llx ecap %llx\n",
-               iommu->seq_id,
+       pr_info("%s: reg_base_addr %llx ver %d:%d cap %llx ecap %llx\n",
+               iommu->name,
                (unsigned long long)drhd->reg_base_addr,
                DMAR_VER_MAJOR(ver), DMAR_VER_MINOR(ver),
                (unsigned long long)iommu->cap,
@@ -1087,8 +1087,8 @@ static void free_iommu(struct intel_iommu *iommu)
 
        if (iommu->irq) {
                free_irq(iommu->irq, iommu);
-               irq_set_handler_data(iommu->irq, NULL);
                dmar_free_hwirq(iommu->irq);
+               iommu->irq = 0;
        }
 
        if (iommu->qi) {
@@ -1642,26 +1642,17 @@ int dmar_set_interrupt(struct intel_iommu *iommu)
        if (iommu->irq)
                return 0;
 
-       irq = dmar_alloc_hwirq();
-       if (irq <= 0) {
-               pr_err("IOMMU: no free vectors\n");
+       irq = dmar_alloc_hwirq(iommu->seq_id, iommu->node, iommu);
+       if (irq > 0) {
+               iommu->irq = irq;
+       } else {
+               pr_err("No free IRQ vectors\n");
                return -EINVAL;
        }
 
-       irq_set_handler_data(irq, iommu);
-       iommu->irq = irq;
-
-       ret = arch_setup_dmar_msi(irq);
-       if (ret) {
-               irq_set_handler_data(irq, NULL);
-               iommu->irq = 0;
-               dmar_free_hwirq(irq);
-               return ret;
-       }
-
        ret = request_irq(irq, dmar_fault, IRQF_NO_THREAD, iommu->name, iommu);
        if (ret)
-               pr_err("IOMMU: can't request irq\n");
+               pr_err("Can't request irq\n");
        return ret;
 }
 
index 3e898504a7c45aee654857ab946d64380a1a1d0f..97c41b8ab5d980667667bde15ad1d16105278b65 100644 (file)
 #define DEBUG
 #endif
 
-#include <linux/io.h>
-#include <linux/interrupt.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
-#include <linux/pm_runtime.h>
 #include <linux/clk.h>
+#include <linux/dma-mapping.h>
 #include <linux/err.h>
-#include <linux/mm.h>
+#include <linux/io.h>
 #include <linux/iommu.h>
-#include <linux/errno.h>
+#include <linux/interrupt.h>
 #include <linux/list.h>
-#include <linux/memblock.h>
-#include <linux/export.h>
+#include <linux/of.h>
+#include <linux/of_iommu.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
 
 #include <asm/cacheflush.h>
+#include <asm/dma-iommu.h>
 #include <asm/pgtable.h>
 
 typedef u32 sysmmu_iova_t;
@@ -184,35 +185,50 @@ static char *sysmmu_fault_name[SYSMMU_FAULTS_NUM] = {
        "UNKNOWN FAULT"
 };
 
-/* attached to dev.archdata.iommu of the master device */
+/*
+ * This structure is attached to dev.archdata.iommu of the master device
+ * on device add, contains a list of SYSMMU controllers defined by device tree,
+ * which are bound to given master device. It is usually referenced by 'owner'
+ * pointer.
+*/
 struct exynos_iommu_owner {
-       struct list_head client; /* entry of exynos_iommu_domain.clients */
-       struct device *dev;
-       struct device *sysmmu;
-       struct iommu_domain *domain;
-       void *vmm_data;         /* IO virtual memory manager's data */
-       spinlock_t lock;        /* Lock to preserve consistency of System MMU */
+       struct list_head controllers;   /* list of sysmmu_drvdata.owner_node */
 };
 
+/*
+ * This structure exynos specific generalization of struct iommu_domain.
+ * It contains list of SYSMMU controllers from all master devices, which has
+ * been attached to this domain and page tables of IO address space defined by
+ * it. It is usually referenced by 'domain' pointer.
+ */
 struct exynos_iommu_domain {
-       struct list_head clients; /* list of sysmmu_drvdata.node */
-       sysmmu_pte_t *pgtable; /* lv1 page table, 16KB */
-       short *lv2entcnt; /* free lv2 entry counter for each section */
-       spinlock_t lock; /* lock for this structure */
-       spinlock_t pgtablelock; /* lock for modifying page table @ pgtable */
+       struct list_head clients; /* list of sysmmu_drvdata.domain_node */
+       sysmmu_pte_t *pgtable;  /* lv1 page table, 16KB */
+       short *lv2entcnt;       /* free lv2 entry counter for each section */
+       spinlock_t lock;        /* lock for modyfying list of clients */
+       spinlock_t pgtablelock; /* lock for modifying page table @ pgtable */
        struct iommu_domain domain; /* generic domain data structure */
 };
 
+/*
+ * This structure hold all data of a single SYSMMU controller, this includes
+ * hw resources like registers and clocks, pointers and list nodes to connect
+ * it to all other structures, internal state and parameters read from device
+ * tree. It is usually referenced by 'data' pointer.
+ */
 struct sysmmu_drvdata {
-       struct device *sysmmu;  /* System MMU's device descriptor */
-       struct device *master;  /* Owner of system MMU */
-       void __iomem *sfrbase;
-       struct clk *clk;
-       struct clk *clk_master;
-       int activations;
-       spinlock_t lock;
-       struct iommu_domain *domain;
-       phys_addr_t pgtable;
+       struct device *sysmmu;          /* SYSMMU controller device */
+       struct device *master;          /* master device (owner) */
+       void __iomem *sfrbase;          /* our registers */
+       struct clk *clk;                /* SYSMMU's clock */
+       struct clk *clk_master;         /* master's device clock */
+       int activations;                /* number of calls to sysmmu_enable */
+       spinlock_t lock;                /* lock for modyfying state */
+       struct exynos_iommu_domain *domain; /* domain we belong to */
+       struct list_head domain_node;   /* node for domain clients list */
+       struct list_head owner_node;    /* node for owner controllers list */
+       phys_addr_t pgtable;            /* assigned page table structure */
+       unsigned int version;           /* our version */
 };
 
 static struct exynos_iommu_domain *to_exynos_domain(struct iommu_domain *dom)
@@ -244,11 +260,6 @@ static void sysmmu_unblock(void __iomem *sfrbase)
        __raw_writel(CTRL_ENABLE, sfrbase + REG_MMU_CTRL);
 }
 
-static unsigned int __raw_sysmmu_version(struct sysmmu_drvdata *data)
-{
-       return MMU_RAW_VER(__raw_readl(data->sfrbase + REG_MMU_VERSION));
-}
-
 static bool sysmmu_block(void __iomem *sfrbase)
 {
        int i = 120;
@@ -345,7 +356,7 @@ static irqreturn_t exynos_sysmmu_irq(int irq, void *dev_id)
                show_fault_information(dev_name(data->sysmmu),
                                        itype, base, addr);
                if (data->domain)
-                       ret = report_iommu_fault(data->domain,
+                       ret = report_iommu_fault(&data->domain->domain,
                                        data->master, addr, itype);
        }
 
@@ -408,7 +419,7 @@ static void __sysmmu_init_config(struct sysmmu_drvdata *data)
        unsigned int cfg = CFG_LRU | CFG_QOS(15);
        unsigned int ver;
 
-       ver = __raw_sysmmu_version(data);
+       ver = MMU_RAW_VER(__raw_readl(data->sfrbase + REG_MMU_VERSION));
        if (MMU_MAJ_VER(ver) == 3) {
                if (MMU_MIN_VER(ver) >= 2) {
                        cfg |= CFG_FLPDCACHE;
@@ -422,6 +433,7 @@ static void __sysmmu_init_config(struct sysmmu_drvdata *data)
        }
 
        __raw_writel(cfg, data->sfrbase + REG_MMU_CFG);
+       data->version = ver;
 }
 
 static void __sysmmu_enable_nocount(struct sysmmu_drvdata *data)
@@ -442,8 +454,8 @@ static void __sysmmu_enable_nocount(struct sysmmu_drvdata *data)
                clk_disable(data->clk_master);
 }
 
-static int __sysmmu_enable(struct sysmmu_drvdata *data,
-                       phys_addr_t pgtable, struct iommu_domain *domain)
+static int __sysmmu_enable(struct sysmmu_drvdata *data, phys_addr_t pgtable,
+                          struct exynos_iommu_domain *domain)
 {
        int ret = 0;
        unsigned long flags;
@@ -470,77 +482,17 @@ static int __sysmmu_enable(struct sysmmu_drvdata *data,
        return ret;
 }
 
-/* __exynos_sysmmu_enable: Enables System MMU
- *
- * returns -error if an error occurred and System MMU is not enabled,
- * 0 if the System MMU has been just enabled and 1 if System MMU was already
- * enabled before.
- */
-static int __exynos_sysmmu_enable(struct device *dev, phys_addr_t pgtable,
-                                 struct iommu_domain *domain)
-{
-       int ret = 0;
-       unsigned long flags;
-       struct exynos_iommu_owner *owner = dev->archdata.iommu;
-       struct sysmmu_drvdata *data;
-
-       BUG_ON(!has_sysmmu(dev));
-
-       spin_lock_irqsave(&owner->lock, flags);
-
-       data = dev_get_drvdata(owner->sysmmu);
-
-       ret = __sysmmu_enable(data, pgtable, domain);
-       if (ret >= 0)
-               data->master = dev;
-
-       spin_unlock_irqrestore(&owner->lock, flags);
-
-       return ret;
-}
-
-int exynos_sysmmu_enable(struct device *dev, phys_addr_t pgtable)
-{
-       BUG_ON(!memblock_is_memory(pgtable));
-
-       return __exynos_sysmmu_enable(dev, pgtable, NULL);
-}
-
-static bool exynos_sysmmu_disable(struct device *dev)
-{
-       unsigned long flags;
-       bool disabled = true;
-       struct exynos_iommu_owner *owner = dev->archdata.iommu;
-       struct sysmmu_drvdata *data;
-
-       BUG_ON(!has_sysmmu(dev));
-
-       spin_lock_irqsave(&owner->lock, flags);
-
-       data = dev_get_drvdata(owner->sysmmu);
-
-       disabled = __sysmmu_disable(data);
-       if (disabled)
-               data->master = NULL;
-
-       spin_unlock_irqrestore(&owner->lock, flags);
-
-       return disabled;
-}
-
 static void __sysmmu_tlb_invalidate_flpdcache(struct sysmmu_drvdata *data,
                                              sysmmu_iova_t iova)
 {
-       if (__raw_sysmmu_version(data) == MAKE_MMU_VER(3, 3))
+       if (data->version == MAKE_MMU_VER(3, 3))
                __raw_writel(iova | 0x1, data->sfrbase + REG_MMU_FLUSH_ENTRY);
 }
 
-static void sysmmu_tlb_invalidate_flpdcache(struct device *dev,
+static void sysmmu_tlb_invalidate_flpdcache(struct sysmmu_drvdata *data,
                                            sysmmu_iova_t iova)
 {
        unsigned long flags;
-       struct exynos_iommu_owner *owner = dev->archdata.iommu;
-       struct sysmmu_drvdata *data = dev_get_drvdata(owner->sysmmu);
 
        if (!IS_ERR(data->clk_master))
                clk_enable(data->clk_master);
@@ -554,14 +506,10 @@ static void sysmmu_tlb_invalidate_flpdcache(struct device *dev,
                clk_disable(data->clk_master);
 }
 
-static void sysmmu_tlb_invalidate_entry(struct device *dev, sysmmu_iova_t iova,
-                                       size_t size)
+static void sysmmu_tlb_invalidate_entry(struct sysmmu_drvdata *data,
+                                       sysmmu_iova_t iova, size_t size)
 {
-       struct exynos_iommu_owner *owner = dev->archdata.iommu;
        unsigned long flags;
-       struct sysmmu_drvdata *data;
-
-       data = dev_get_drvdata(owner->sysmmu);
 
        spin_lock_irqsave(&data->lock, flags);
        if (is_sysmmu_active(data)) {
@@ -580,7 +528,7 @@ static void sysmmu_tlb_invalidate_entry(struct device *dev, sysmmu_iova_t iova,
                 * 1MB page can be cached in one of all sets.
                 * 64KB page can be one of 16 consecutive sets.
                 */
-               if (MMU_MAJ_VER(__raw_sysmmu_version(data)) == 2)
+               if (MMU_MAJ_VER(data->version) == 2)
                        num_inv = min_t(unsigned int, size / PAGE_SIZE, 64);
 
                if (sysmmu_block(data->sfrbase)) {
@@ -591,32 +539,8 @@ static void sysmmu_tlb_invalidate_entry(struct device *dev, sysmmu_iova_t iova,
                if (!IS_ERR(data->clk_master))
                        clk_disable(data->clk_master);
        } else {
-               dev_dbg(dev, "disabled. Skipping TLB invalidation @ %#x\n",
-                       iova);
-       }
-       spin_unlock_irqrestore(&data->lock, flags);
-}
-
-void exynos_sysmmu_tlb_invalidate(struct device *dev)
-{
-       struct exynos_iommu_owner *owner = dev->archdata.iommu;
-       unsigned long flags;
-       struct sysmmu_drvdata *data;
-
-       data = dev_get_drvdata(owner->sysmmu);
-
-       spin_lock_irqsave(&data->lock, flags);
-       if (is_sysmmu_active(data)) {
-               if (!IS_ERR(data->clk_master))
-                       clk_enable(data->clk_master);
-               if (sysmmu_block(data->sfrbase)) {
-                       __sysmmu_tlb_invalidate(data->sfrbase);
-                       sysmmu_unblock(data->sfrbase);
-               }
-               if (!IS_ERR(data->clk_master))
-                       clk_disable(data->clk_master);
-       } else {
-               dev_dbg(dev, "disabled. Skipping TLB invalidation\n");
+               dev_dbg(data->master,
+                       "disabled. Skipping TLB invalidation @ %#x\n", iova);
        }
        spin_unlock_irqrestore(&data->lock, flags);
 }
@@ -682,6 +606,36 @@ static int __init exynos_sysmmu_probe(struct platform_device *pdev)
        return 0;
 }
 
+#ifdef CONFIG_PM_SLEEP
+static int exynos_sysmmu_suspend(struct device *dev)
+{
+       struct sysmmu_drvdata *data = dev_get_drvdata(dev);
+
+       dev_dbg(dev, "suspend\n");
+       if (is_sysmmu_active(data)) {
+               __sysmmu_disable_nocount(data);
+               pm_runtime_put(dev);
+       }
+       return 0;
+}
+
+static int exynos_sysmmu_resume(struct device *dev)
+{
+       struct sysmmu_drvdata *data = dev_get_drvdata(dev);
+
+       dev_dbg(dev, "resume\n");
+       if (is_sysmmu_active(data)) {
+               pm_runtime_get_sync(dev);
+               __sysmmu_enable_nocount(data);
+       }
+       return 0;
+}
+#endif
+
+static const struct dev_pm_ops sysmmu_pm_ops = {
+       SET_LATE_SYSTEM_SLEEP_PM_OPS(exynos_sysmmu_suspend, exynos_sysmmu_resume)
+};
+
 static const struct of_device_id sysmmu_of_match[] __initconst = {
        { .compatible   = "samsung,exynos-sysmmu", },
        { },
@@ -692,6 +646,7 @@ static struct platform_driver exynos_sysmmu_driver __refdata = {
        .driver = {
                .name           = "exynos-sysmmu",
                .of_match_table = sysmmu_of_match,
+               .pm             = &sysmmu_pm_ops,
        }
 };
 
@@ -704,104 +659,108 @@ static inline void pgtable_flush(void *vastart, void *vaend)
 
 static struct iommu_domain *exynos_iommu_domain_alloc(unsigned type)
 {
-       struct exynos_iommu_domain *exynos_domain;
+       struct exynos_iommu_domain *domain;
        int i;
 
        if (type != IOMMU_DOMAIN_UNMANAGED)
                return NULL;
 
-       exynos_domain = kzalloc(sizeof(*exynos_domain), GFP_KERNEL);
-       if (!exynos_domain)
+       domain = kzalloc(sizeof(*domain), GFP_KERNEL);
+       if (!domain)
                return NULL;
 
-       exynos_domain->pgtable = (sysmmu_pte_t *)__get_free_pages(GFP_KERNEL, 2);
-       if (!exynos_domain->pgtable)
+       domain->pgtable = (sysmmu_pte_t *)__get_free_pages(GFP_KERNEL, 2);
+       if (!domain->pgtable)
                goto err_pgtable;
 
-       exynos_domain->lv2entcnt = (short *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, 1);
-       if (!exynos_domain->lv2entcnt)
+       domain->lv2entcnt = (short *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, 1);
+       if (!domain->lv2entcnt)
                goto err_counter;
 
        /* Workaround for System MMU v3.3 to prevent caching 1MiB mapping */
        for (i = 0; i < NUM_LV1ENTRIES; i += 8) {
-               exynos_domain->pgtable[i + 0] = ZERO_LV2LINK;
-               exynos_domain->pgtable[i + 1] = ZERO_LV2LINK;
-               exynos_domain->pgtable[i + 2] = ZERO_LV2LINK;
-               exynos_domain->pgtable[i + 3] = ZERO_LV2LINK;
-               exynos_domain->pgtable[i + 4] = ZERO_LV2LINK;
-               exynos_domain->pgtable[i + 5] = ZERO_LV2LINK;
-               exynos_domain->pgtable[i + 6] = ZERO_LV2LINK;
-               exynos_domain->pgtable[i + 7] = ZERO_LV2LINK;
+               domain->pgtable[i + 0] = ZERO_LV2LINK;
+               domain->pgtable[i + 1] = ZERO_LV2LINK;
+               domain->pgtable[i + 2] = ZERO_LV2LINK;
+               domain->pgtable[i + 3] = ZERO_LV2LINK;
+               domain->pgtable[i + 4] = ZERO_LV2LINK;
+               domain->pgtable[i + 5] = ZERO_LV2LINK;
+               domain->pgtable[i + 6] = ZERO_LV2LINK;
+               domain->pgtable[i + 7] = ZERO_LV2LINK;
        }
 
-       pgtable_flush(exynos_domain->pgtable, exynos_domain->pgtable + NUM_LV1ENTRIES);
+       pgtable_flush(domain->pgtable, domain->pgtable + NUM_LV1ENTRIES);
 
-       spin_lock_init(&exynos_domain->lock);
-       spin_lock_init(&exynos_domain->pgtablelock);
-       INIT_LIST_HEAD(&exynos_domain->clients);
+       spin_lock_init(&domain->lock);
+       spin_lock_init(&domain->pgtablelock);
+       INIT_LIST_HEAD(&domain->clients);
 
-       exynos_domain->domain.geometry.aperture_start = 0;
-       exynos_domain->domain.geometry.aperture_end   = ~0UL;
-       exynos_domain->domain.geometry.force_aperture = true;
+       domain->domain.geometry.aperture_start = 0;
+       domain->domain.geometry.aperture_end   = ~0UL;
+       domain->domain.geometry.force_aperture = true;
 
-       return &exynos_domain->domain;
+       return &domain->domain;
 
 err_counter:
-       free_pages((unsigned long)exynos_domain->pgtable, 2);
+       free_pages((unsigned long)domain->pgtable, 2);
 err_pgtable:
-       kfree(exynos_domain);
+       kfree(domain);
        return NULL;
 }
 
-static void exynos_iommu_domain_free(struct iommu_domain *domain)
+static void exynos_iommu_domain_free(struct iommu_domain *iommu_domain)
 {
-       struct exynos_iommu_domain *priv = to_exynos_domain(domain);
-       struct exynos_iommu_owner *owner;
+       struct exynos_iommu_domain *domain = to_exynos_domain(iommu_domain);
+       struct sysmmu_drvdata *data, *next;
        unsigned long flags;
        int i;
 
-       WARN_ON(!list_empty(&priv->clients));
+       WARN_ON(!list_empty(&domain->clients));
 
-       spin_lock_irqsave(&priv->lock, flags);
+       spin_lock_irqsave(&domain->lock, flags);
 
-       list_for_each_entry(owner, &priv->clients, client) {
-               while (!exynos_sysmmu_disable(owner->dev))
-                       ; /* until System MMU is actually disabled */
+       list_for_each_entry_safe(data, next, &domain->clients, domain_node) {
+               if (__sysmmu_disable(data))
+                       data->master = NULL;
+               list_del_init(&data->domain_node);
        }
 
-       while (!list_empty(&priv->clients))
-               list_del_init(priv->clients.next);
-
-       spin_unlock_irqrestore(&priv->lock, flags);
+       spin_unlock_irqrestore(&domain->lock, flags);
 
        for (i = 0; i < NUM_LV1ENTRIES; i++)
-               if (lv1ent_page(priv->pgtable + i))
+               if (lv1ent_page(domain->pgtable + i))
                        kmem_cache_free(lv2table_kmem_cache,
-                               phys_to_virt(lv2table_base(priv->pgtable + i)));
+                               phys_to_virt(lv2table_base(domain->pgtable + i)));
 
-       free_pages((unsigned long)priv->pgtable, 2);
-       free_pages((unsigned long)priv->lv2entcnt, 1);
-       kfree(priv);
+       free_pages((unsigned long)domain->pgtable, 2);
+       free_pages((unsigned long)domain->lv2entcnt, 1);
+       kfree(domain);
 }
 
-static int exynos_iommu_attach_device(struct iommu_domain *domain,
+static int exynos_iommu_attach_device(struct iommu_domain *iommu_domain,
                                   struct device *dev)
 {
        struct exynos_iommu_owner *owner = dev->archdata.iommu;
-       struct exynos_iommu_domain *priv = to_exynos_domain(domain);
-       phys_addr_t pagetable = virt_to_phys(priv->pgtable);
+       struct exynos_iommu_domain *domain = to_exynos_domain(iommu_domain);
+       struct sysmmu_drvdata *data;
+       phys_addr_t pagetable = virt_to_phys(domain->pgtable);
        unsigned long flags;
-       int ret;
+       int ret = -ENODEV;
 
-       spin_lock_irqsave(&priv->lock, flags);
+       if (!has_sysmmu(dev))
+               return -ENODEV;
 
-       ret = __exynos_sysmmu_enable(dev, pagetable, domain);
-       if (ret == 0) {
-               list_add_tail(&owner->client, &priv->clients);
-               owner->domain = domain;
-       }
+       list_for_each_entry(data, &owner->controllers, owner_node) {
+               pm_runtime_get_sync(data->sysmmu);
+               ret = __sysmmu_enable(data, pagetable, domain);
+               if (ret >= 0) {
+                       data->master = dev;
 
-       spin_unlock_irqrestore(&priv->lock, flags);
+                       spin_lock_irqsave(&domain->lock, flags);
+                       list_add_tail(&data->domain_node, &domain->clients);
+                       spin_unlock_irqrestore(&domain->lock, flags);
+               }
+       }
 
        if (ret < 0) {
                dev_err(dev, "%s: Failed to attach IOMMU with pgtable %pa\n",
@@ -815,36 +774,39 @@ static int exynos_iommu_attach_device(struct iommu_domain *domain,
        return ret;
 }
 
-static void exynos_iommu_detach_device(struct iommu_domain *domain,
+static void exynos_iommu_detach_device(struct iommu_domain *iommu_domain,
                                    struct device *dev)
 {
-       struct exynos_iommu_owner *owner;
-       struct exynos_iommu_domain *priv = to_exynos_domain(domain);
-       phys_addr_t pagetable = virt_to_phys(priv->pgtable);
+       struct exynos_iommu_domain *domain = to_exynos_domain(iommu_domain);
+       phys_addr_t pagetable = virt_to_phys(domain->pgtable);
+       struct sysmmu_drvdata *data, *next;
        unsigned long flags;
+       bool found = false;
 
-       spin_lock_irqsave(&priv->lock, flags);
+       if (!has_sysmmu(dev))
+               return;
 
-       list_for_each_entry(owner, &priv->clients, client) {
-               if (owner == dev->archdata.iommu) {
-                       if (exynos_sysmmu_disable(dev)) {
-                               list_del_init(&owner->client);
-                               owner->domain = NULL;
+       spin_lock_irqsave(&domain->lock, flags);
+       list_for_each_entry_safe(data, next, &domain->clients, domain_node) {
+               if (data->master == dev) {
+                       if (__sysmmu_disable(data)) {
+                               data->master = NULL;
+                               list_del_init(&data->domain_node);
                        }
-                       break;
+                       pm_runtime_put(data->sysmmu);
+                       found = true;
                }
        }
+       spin_unlock_irqrestore(&domain->lock, flags);
 
-       spin_unlock_irqrestore(&priv->lock, flags);
-
-       if (owner == dev->archdata.iommu)
+       if (found)
                dev_dbg(dev, "%s: Detached IOMMU with pgtable %pa\n",
                                        __func__, &pagetable);
        else
                dev_err(dev, "%s: No IOMMU is attached\n", __func__);
 }
 
-static sysmmu_pte_t *alloc_lv2entry(struct exynos_iommu_domain *priv,
+static sysmmu_pte_t *alloc_lv2entry(struct exynos_iommu_domain *domain,
                sysmmu_pte_t *sent, sysmmu_iova_t iova, short *pgcounter)
 {
        if (lv1ent_section(sent)) {
@@ -862,6 +824,7 @@ static sysmmu_pte_t *alloc_lv2entry(struct exynos_iommu_domain *priv,
                        return ERR_PTR(-ENOMEM);
 
                *sent = mk_lv1ent_page(virt_to_phys(pent));
+               kmemleak_ignore(pent);
                *pgcounter = NUM_LV2ENTRIES;
                pgtable_flush(pent, pent + NUM_LV2ENTRIES);
                pgtable_flush(sent, sent + 1);
@@ -884,20 +847,19 @@ static sysmmu_pte_t *alloc_lv2entry(struct exynos_iommu_domain *priv,
                 * not currently mapped.
                 */
                if (need_flush_flpd_cache) {
-                       struct exynos_iommu_owner *owner;
+                       struct sysmmu_drvdata *data;
 
-                       spin_lock(&priv->lock);
-                       list_for_each_entry(owner, &priv->clients, client)
-                               sysmmu_tlb_invalidate_flpdcache(
-                                                       owner->dev, iova);
-                       spin_unlock(&priv->lock);
+                       spin_lock(&domain->lock);
+                       list_for_each_entry(data, &domain->clients, domain_node)
+                               sysmmu_tlb_invalidate_flpdcache(data, iova);
+                       spin_unlock(&domain->lock);
                }
        }
 
        return page_entry(sent, iova);
 }
 
-static int lv1set_section(struct exynos_iommu_domain *priv,
+static int lv1set_section(struct exynos_iommu_domain *domain,
                          sysmmu_pte_t *sent, sysmmu_iova_t iova,
                          phys_addr_t paddr, short *pgcnt)
 {
@@ -922,17 +884,17 @@ static int lv1set_section(struct exynos_iommu_domain *priv,
 
        pgtable_flush(sent, sent + 1);
 
-       spin_lock(&priv->lock);
+       spin_lock(&domain->lock);
        if (lv1ent_page_zero(sent)) {
-               struct exynos_iommu_owner *owner;
+               struct sysmmu_drvdata *data;
                /*
                 * Flushing FLPD cache in System MMU v3.3 that may cache a FLPD
                 * entry by speculative prefetch of SLPD which has no mapping.
                 */
-               list_for_each_entry(owner, &priv->clients, client)
-                       sysmmu_tlb_invalidate_flpdcache(owner->dev, iova);
+               list_for_each_entry(data, &domain->clients, domain_node)
+                       sysmmu_tlb_invalidate_flpdcache(data, iova);
        }
-       spin_unlock(&priv->lock);
+       spin_unlock(&domain->lock);
 
        return 0;
 }
@@ -992,74 +954,75 @@ static int lv2set_page(sysmmu_pte_t *pent, phys_addr_t paddr, size_t size,
  *   than or equal to 128KiB.
  * - Start address of an I/O virtual region must be aligned by 128KiB.
  */
-static int exynos_iommu_map(struct iommu_domain *domain, unsigned long l_iova,
-                        phys_addr_t paddr, size_t size, int prot)
+static int exynos_iommu_map(struct iommu_domain *iommu_domain,
+                           unsigned long l_iova, phys_addr_t paddr, size_t size,
+                           int prot)
 {
-       struct exynos_iommu_domain *priv = to_exynos_domain(domain);
+       struct exynos_iommu_domain *domain = to_exynos_domain(iommu_domain);
        sysmmu_pte_t *entry;
        sysmmu_iova_t iova = (sysmmu_iova_t)l_iova;
        unsigned long flags;
        int ret = -ENOMEM;
 
-       BUG_ON(priv->pgtable == NULL);
+       BUG_ON(domain->pgtable == NULL);
 
-       spin_lock_irqsave(&priv->pgtablelock, flags);
+       spin_lock_irqsave(&domain->pgtablelock, flags);
 
-       entry = section_entry(priv->pgtable, iova);
+       entry = section_entry(domain->pgtable, iova);
 
        if (size == SECT_SIZE) {
-               ret = lv1set_section(priv, entry, iova, paddr,
-                                       &priv->lv2entcnt[lv1ent_offset(iova)]);
+               ret = lv1set_section(domain, entry, iova, paddr,
+                                    &domain->lv2entcnt[lv1ent_offset(iova)]);
        } else {
                sysmmu_pte_t *pent;
 
-               pent = alloc_lv2entry(priv, entry, iova,
-                                       &priv->lv2entcnt[lv1ent_offset(iova)]);
+               pent = alloc_lv2entry(domain, entry, iova,
+                                     &domain->lv2entcnt[lv1ent_offset(iova)]);
 
                if (IS_ERR(pent))
                        ret = PTR_ERR(pent);
                else
                        ret = lv2set_page(pent, paddr, size,
-                                       &priv->lv2entcnt[lv1ent_offset(iova)]);
+                                      &domain->lv2entcnt[lv1ent_offset(iova)]);
        }
 
        if (ret)
                pr_err("%s: Failed(%d) to map %#zx bytes @ %#x\n",
                        __func__, ret, size, iova);
 
-       spin_unlock_irqrestore(&priv->pgtablelock, flags);
+       spin_unlock_irqrestore(&domain->pgtablelock, flags);
 
        return ret;
 }
 
-static void exynos_iommu_tlb_invalidate_entry(struct exynos_iommu_domain *priv,
-                                               sysmmu_iova_t iova, size_t size)
+static void exynos_iommu_tlb_invalidate_entry(struct exynos_iommu_domain *domain,
+                                             sysmmu_iova_t iova, size_t size)
 {
-       struct exynos_iommu_owner *owner;
+       struct sysmmu_drvdata *data;
        unsigned long flags;
 
-       spin_lock_irqsave(&priv->lock, flags);
+       spin_lock_irqsave(&domain->lock, flags);
 
-       list_for_each_entry(owner, &priv->clients, client)
-               sysmmu_tlb_invalidate_entry(owner->dev, iova, size);
+       list_for_each_entry(data, &domain->clients, domain_node)
+               sysmmu_tlb_invalidate_entry(data, iova, size);
 
-       spin_unlock_irqrestore(&priv->lock, flags);
+       spin_unlock_irqrestore(&domain->lock, flags);
 }
 
-static size_t exynos_iommu_unmap(struct iommu_domain *domain,
-                                       unsigned long l_iova, size_t size)
+static size_t exynos_iommu_unmap(struct iommu_domain *iommu_domain,
+                                unsigned long l_iova, size_t size)
 {
-       struct exynos_iommu_domain *priv = to_exynos_domain(domain);
+       struct exynos_iommu_domain *domain = to_exynos_domain(iommu_domain);
        sysmmu_iova_t iova = (sysmmu_iova_t)l_iova;
        sysmmu_pte_t *ent;
        size_t err_pgsize;
        unsigned long flags;
 
-       BUG_ON(priv->pgtable == NULL);
+       BUG_ON(domain->pgtable == NULL);
 
-       spin_lock_irqsave(&priv->pgtablelock, flags);
+       spin_lock_irqsave(&domain->pgtablelock, flags);
 
-       ent = section_entry(priv->pgtable, iova);
+       ent = section_entry(domain->pgtable, iova);
 
        if (lv1ent_section(ent)) {
                if (WARN_ON(size < SECT_SIZE)) {
@@ -1093,7 +1056,7 @@ static size_t exynos_iommu_unmap(struct iommu_domain *domain,
                *ent = 0;
                size = SPAGE_SIZE;
                pgtable_flush(ent, ent + 1);
-               priv->lv2entcnt[lv1ent_offset(iova)] += 1;
+               domain->lv2entcnt[lv1ent_offset(iova)] += 1;
                goto done;
        }
 
@@ -1107,15 +1070,15 @@ static size_t exynos_iommu_unmap(struct iommu_domain *domain,
        pgtable_flush(ent, ent + SPAGES_PER_LPAGE);
 
        size = LPAGE_SIZE;
-       priv->lv2entcnt[lv1ent_offset(iova)] += SPAGES_PER_LPAGE;
+       domain->lv2entcnt[lv1ent_offset(iova)] += SPAGES_PER_LPAGE;
 done:
-       spin_unlock_irqrestore(&priv->pgtablelock, flags);
+       spin_unlock_irqrestore(&domain->pgtablelock, flags);
 
-       exynos_iommu_tlb_invalidate_entry(priv, iova, size);
+       exynos_iommu_tlb_invalidate_entry(domain, iova, size);
 
        return size;
 err:
-       spin_unlock_irqrestore(&priv->pgtablelock, flags);
+       spin_unlock_irqrestore(&domain->pgtablelock, flags);
 
        pr_err("%s: Failed: size(%#zx) @ %#x is smaller than page size %#zx\n",
                __func__, size, iova, err_pgsize);
@@ -1123,17 +1086,17 @@ err:
        return 0;
 }
 
-static phys_addr_t exynos_iommu_iova_to_phys(struct iommu_domain *domain,
+static phys_addr_t exynos_iommu_iova_to_phys(struct iommu_domain *iommu_domain,
                                          dma_addr_t iova)
 {
-       struct exynos_iommu_domain *priv = to_exynos_domain(domain);
+       struct exynos_iommu_domain *domain = to_exynos_domain(iommu_domain);
        sysmmu_pte_t *entry;
        unsigned long flags;
        phys_addr_t phys = 0;
 
-       spin_lock_irqsave(&priv->pgtablelock, flags);
+       spin_lock_irqsave(&domain->pgtablelock, flags);
 
-       entry = section_entry(priv->pgtable, iova);
+       entry = section_entry(domain->pgtable, iova);
 
        if (lv1ent_section(entry)) {
                phys = section_phys(entry) + section_offs(iova);
@@ -1146,7 +1109,7 @@ static phys_addr_t exynos_iommu_iova_to_phys(struct iommu_domain *domain,
                        phys = spage_phys(entry) + spage_offs(iova);
        }
 
-       spin_unlock_irqrestore(&priv->pgtablelock, flags);
+       spin_unlock_irqrestore(&domain->pgtablelock, flags);
 
        return phys;
 }
@@ -1156,6 +1119,9 @@ static int exynos_iommu_add_device(struct device *dev)
        struct iommu_group *group;
        int ret;
 
+       if (!has_sysmmu(dev))
+               return -ENODEV;
+
        group = iommu_group_get(dev);
 
        if (!group) {
@@ -1174,10 +1140,40 @@ static int exynos_iommu_add_device(struct device *dev)
 
 static void exynos_iommu_remove_device(struct device *dev)
 {
+       if (!has_sysmmu(dev))
+               return;
+
        iommu_group_remove_device(dev);
 }
 
-static const struct iommu_ops exynos_iommu_ops = {
+static int exynos_iommu_of_xlate(struct device *dev,
+                                struct of_phandle_args *spec)
+{
+       struct exynos_iommu_owner *owner = dev->archdata.iommu;
+       struct platform_device *sysmmu = of_find_device_by_node(spec->np);
+       struct sysmmu_drvdata *data;
+
+       if (!sysmmu)
+               return -ENODEV;
+
+       data = platform_get_drvdata(sysmmu);
+       if (!data)
+               return -ENODEV;
+
+       if (!owner) {
+               owner = kzalloc(sizeof(*owner), GFP_KERNEL);
+               if (!owner)
+                       return -ENOMEM;
+
+               INIT_LIST_HEAD(&owner->controllers);
+               dev->archdata.iommu = owner;
+       }
+
+       list_add_tail(&data->owner_node, &owner->controllers);
+       return 0;
+}
+
+static struct iommu_ops exynos_iommu_ops = {
        .domain_alloc = exynos_iommu_domain_alloc,
        .domain_free = exynos_iommu_domain_free,
        .attach_dev = exynos_iommu_attach_device,
@@ -1189,19 +1185,15 @@ static const struct iommu_ops exynos_iommu_ops = {
        .add_device = exynos_iommu_add_device,
        .remove_device = exynos_iommu_remove_device,
        .pgsize_bitmap = SECT_SIZE | LPAGE_SIZE | SPAGE_SIZE,
+       .of_xlate = exynos_iommu_of_xlate,
 };
 
+static bool init_done;
+
 static int __init exynos_iommu_init(void)
 {
-       struct device_node *np;
        int ret;
 
-       np = of_find_matching_node(NULL, sysmmu_of_match);
-       if (!np)
-               return 0;
-
-       of_node_put(np);
-
        lv2table_kmem_cache = kmem_cache_create("exynos-iommu-lv2table",
                                LV2TABLE_SIZE, LV2TABLE_SIZE, 0, NULL);
        if (!lv2table_kmem_cache) {
@@ -1230,6 +1222,8 @@ static int __init exynos_iommu_init(void)
                goto err_set_iommu;
        }
 
+       init_done = true;
+
        return 0;
 err_set_iommu:
        kmem_cache_free(lv2table_kmem_cache, zero_lv2_table);
@@ -1239,4 +1233,21 @@ err_reg_driver:
        kmem_cache_destroy(lv2table_kmem_cache);
        return ret;
 }
-subsys_initcall(exynos_iommu_init);
+
+static int __init exynos_iommu_of_setup(struct device_node *np)
+{
+       struct platform_device *pdev;
+
+       if (!init_done)
+               exynos_iommu_init();
+
+       pdev = of_platform_device_create(np, NULL, platform_bus_type.dev_root);
+       if (IS_ERR(pdev))
+               return PTR_ERR(pdev);
+
+       of_iommu_set_ops(np, &exynos_iommu_ops);
+       return 0;
+}
+
+IOMMU_OF_DECLARE(exynos_iommu_of, "samsung,exynos-sysmmu",
+                exynos_iommu_of_setup);
index 68d43beccb7e560f845ad49b8ae7d9e38872fcf7..a98a7b27aca1dec2cb2f53319df8a49abcf8e645 100644 (file)
  *          Shaohua Li <shaohua.li@intel.com>,
  *          Anil S Keshavamurthy <anil.s.keshavamurthy@intel.com>,
  *          Fenghua Yu <fenghua.yu@intel.com>
+ *          Joerg Roedel <jroedel@suse.de>
  */
 
+#define pr_fmt(fmt)     "DMAR: " fmt
+
 #include <linux/init.h>
 #include <linux/bitmap.h>
 #include <linux/debugfs.h>
@@ -40,6 +43,7 @@
 #include <linux/pci-ats.h>
 #include <linux/memblock.h>
 #include <linux/dma-contiguous.h>
+#include <linux/crash_dump.h>
 #include <asm/irq_remapping.h>
 #include <asm/cacheflush.h>
 #include <asm/iommu.h>
@@ -190,7 +194,29 @@ struct root_entry {
 };
 #define ROOT_ENTRY_NR (VTD_PAGE_SIZE/sizeof(struct root_entry))
 
+/*
+ * Take a root_entry and return the Lower Context Table Pointer (LCTP)
+ * if marked present.
+ */
+static phys_addr_t root_entry_lctp(struct root_entry *re)
+{
+       if (!(re->lo & 1))
+               return 0;
+
+       return re->lo & VTD_PAGE_MASK;
+}
+
+/*
+ * Take a root_entry and return the Upper Context Table Pointer (UCTP)
+ * if marked present.
+ */
+static phys_addr_t root_entry_uctp(struct root_entry *re)
+{
+       if (!(re->hi & 1))
+               return 0;
 
+       return re->hi & VTD_PAGE_MASK;
+}
 /*
  * low 64 bits:
  * 0: present
@@ -207,10 +233,38 @@ struct context_entry {
        u64 hi;
 };
 
-static inline bool context_present(struct context_entry *context)
+static inline void context_clear_pasid_enable(struct context_entry *context)
+{
+       context->lo &= ~(1ULL << 11);
+}
+
+static inline bool context_pasid_enabled(struct context_entry *context)
+{
+       return !!(context->lo & (1ULL << 11));
+}
+
+static inline void context_set_copied(struct context_entry *context)
+{
+       context->hi |= (1ull << 3);
+}
+
+static inline bool context_copied(struct context_entry *context)
+{
+       return !!(context->hi & (1ULL << 3));
+}
+
+static inline bool __context_present(struct context_entry *context)
 {
        return (context->lo & 1);
 }
+
+static inline bool context_present(struct context_entry *context)
+{
+       return context_pasid_enabled(context) ?
+            __context_present(context) :
+            __context_present(context) && !context_copied(context);
+}
+
 static inline void context_set_present(struct context_entry *context)
 {
        context->lo |= 1;
@@ -247,6 +301,11 @@ static inline void context_set_domain_id(struct context_entry *context,
        context->hi |= (value & ((1 << 16) - 1)) << 8;
 }
 
+static inline int context_domain_id(struct context_entry *c)
+{
+       return((c->hi >> 8) & 0xffff);
+}
+
 static inline void context_clear_entry(struct context_entry *context)
 {
        context->lo = 0;
@@ -422,6 +481,14 @@ static int dmar_map_gfx = 1;
 static int dmar_forcedac;
 static int intel_iommu_strict;
 static int intel_iommu_superpage = 1;
+static int intel_iommu_ecs = 1;
+
+/* We only actually use ECS when PASID support (on the new bit 40)
+ * is also advertised. Some early implementations — the ones with
+ * PASID support on bit 28 — have issues even when we *only* use
+ * extended root/context tables. */
+#define ecs_enabled(iommu) (intel_iommu_ecs && ecap_ecs(iommu->ecap) && \
+                           ecap_pasid(iommu->ecap))
 
 int intel_iommu_gfx_mapped;
 EXPORT_SYMBOL_GPL(intel_iommu_gfx_mapped);
@@ -432,6 +499,25 @@ static LIST_HEAD(device_domain_list);
 
 static const struct iommu_ops intel_iommu_ops;
 
+static bool translation_pre_enabled(struct intel_iommu *iommu)
+{
+       return (iommu->flags & VTD_FLAG_TRANS_PRE_ENABLED);
+}
+
+static void clear_translation_pre_enabled(struct intel_iommu *iommu)
+{
+       iommu->flags &= ~VTD_FLAG_TRANS_PRE_ENABLED;
+}
+
+static void init_translation_status(struct intel_iommu *iommu)
+{
+       u32 gsts;
+
+       gsts = readl(iommu->reg + DMAR_GSTS_REG);
+       if (gsts & DMA_GSTS_TES)
+               iommu->flags |= VTD_FLAG_TRANS_PRE_ENABLED;
+}
+
 /* Convert generic 'struct iommu_domain to private struct dmar_domain */
 static struct dmar_domain *to_dmar_domain(struct iommu_domain *dom)
 {
@@ -445,26 +531,26 @@ static int __init intel_iommu_setup(char *str)
        while (*str) {
                if (!strncmp(str, "on", 2)) {
                        dmar_disabled = 0;
-                       printk(KERN_INFO "Intel-IOMMU: enabled\n");
+                       pr_info("IOMMU enabled\n");
                } else if (!strncmp(str, "off", 3)) {
                        dmar_disabled = 1;
-                       printk(KERN_INFO "Intel-IOMMU: disabled\n");
+                       pr_info("IOMMU disabled\n");
                } else if (!strncmp(str, "igfx_off", 8)) {
                        dmar_map_gfx = 0;
-                       printk(KERN_INFO
-                               "Intel-IOMMU: disable GFX device mapping\n");
+                       pr_info("Disable GFX device mapping\n");
                } else if (!strncmp(str, "forcedac", 8)) {
-                       printk(KERN_INFO
-                               "Intel-IOMMU: Forcing DAC for PCI devices\n");
+                       pr_info("Forcing DAC for PCI devices\n");
                        dmar_forcedac = 1;
                } else if (!strncmp(str, "strict", 6)) {
-                       printk(KERN_INFO
-                               "Intel-IOMMU: disable batched IOTLB flush\n");
+                       pr_info("Disable batched IOTLB flush\n");
                        intel_iommu_strict = 1;
                } else if (!strncmp(str, "sp_off", 6)) {
-                       printk(KERN_INFO
-                               "Intel-IOMMU: disable supported super page\n");
+                       pr_info("Disable supported super page\n");
                        intel_iommu_superpage = 0;
+               } else if (!strncmp(str, "ecs_off", 7)) {
+                       printk(KERN_INFO
+                               "Intel-IOMMU: disable extended context table support\n");
+                       intel_iommu_ecs = 0;
                }
 
                str += strcspn(str, ",");
@@ -669,7 +755,7 @@ static inline struct context_entry *iommu_context_addr(struct intel_iommu *iommu
        struct context_entry *context;
        u64 *entry;
 
-       if (ecap_ecs(iommu->ecap)) {
+       if (ecs_enabled(iommu)) {
                if (devfn >= 0x80) {
                        devfn -= 0x80;
                        entry = &root->hi;
@@ -696,6 +782,11 @@ static inline struct context_entry *iommu_context_addr(struct intel_iommu *iommu
        return &context[devfn];
 }
 
+static int iommu_dummy(struct device *dev)
+{
+       return dev->archdata.iommu == DUMMY_DEVICE_DOMAIN_INFO;
+}
+
 static struct intel_iommu *device_to_iommu(struct device *dev, u8 *bus, u8 *devfn)
 {
        struct dmar_drhd_unit *drhd = NULL;
@@ -705,6 +796,9 @@ static struct intel_iommu *device_to_iommu(struct device *dev, u8 *bus, u8 *devf
        u16 segment = 0;
        int i;
 
+       if (iommu_dummy(dev))
+               return NULL;
+
        if (dev_is_pci(dev)) {
                pdev = to_pci_dev(dev);
                segment = pci_domain_nr(pdev->bus);
@@ -798,7 +892,7 @@ static void free_context_table(struct intel_iommu *iommu)
                if (context)
                        free_pgtable_page(context);
 
-               if (!ecap_ecs(iommu->ecap))
+               if (!ecs_enabled(iommu))
                        continue;
 
                context = iommu_context_addr(iommu, i, 0x80, 0);
@@ -1112,7 +1206,7 @@ static int iommu_alloc_root_entry(struct intel_iommu *iommu)
 
        root = (struct root_entry *)alloc_pgtable_page(iommu->node);
        if (!root) {
-               pr_err("IOMMU: allocating root entry for %s failed\n",
+               pr_err("Allocating root entry for %s failed\n",
                        iommu->name);
                return -ENOMEM;
        }
@@ -1133,7 +1227,7 @@ static void iommu_set_root_entry(struct intel_iommu *iommu)
        unsigned long flag;
 
        addr = virt_to_phys(iommu->root_entry);
-       if (ecap_ecs(iommu->ecap))
+       if (ecs_enabled(iommu))
                addr |= DMA_RTADDR_RTT;
 
        raw_spin_lock_irqsave(&iommu->register_lock, flag);
@@ -1250,9 +1344,9 @@ static void __iommu_flush_iotlb(struct intel_iommu *iommu, u16 did,
 
        /* check IOTLB invalidation granularity */
        if (DMA_TLB_IAIG(val) == 0)
-               printk(KERN_ERR"IOMMU: flush IOTLB failed\n");
+               pr_err("Flush IOTLB failed\n");
        if (DMA_TLB_IAIG(val) != DMA_TLB_IIRG(type))
-               pr_debug("IOMMU: tlb flush request %Lx, actual %Lx\n",
+               pr_debug("TLB flush request %Lx, actual %Lx\n",
                        (unsigned long long)DMA_TLB_IIRG(type),
                        (unsigned long long)DMA_TLB_IAIG(val));
 }
@@ -1423,8 +1517,8 @@ static int iommu_init_domains(struct intel_iommu *iommu)
        unsigned long nlongs;
 
        ndomains = cap_ndoms(iommu->cap);
-       pr_debug("IOMMU%d: Number of Domains supported <%ld>\n",
-                iommu->seq_id, ndomains);
+       pr_debug("%s: Number of Domains supported <%ld>\n",
+                iommu->name, ndomains);
        nlongs = BITS_TO_LONGS(ndomains);
 
        spin_lock_init(&iommu->lock);
@@ -1434,15 +1528,15 @@ static int iommu_init_domains(struct intel_iommu *iommu)
         */
        iommu->domain_ids = kcalloc(nlongs, sizeof(unsigned long), GFP_KERNEL);
        if (!iommu->domain_ids) {
-               pr_err("IOMMU%d: allocating domain id array failed\n",
-                      iommu->seq_id);
+               pr_err("%s: Allocating domain id array failed\n",
+                      iommu->name);
                return -ENOMEM;
        }
        iommu->domains = kcalloc(ndomains, sizeof(struct dmar_domain *),
                        GFP_KERNEL);
        if (!iommu->domains) {
-               pr_err("IOMMU%d: allocating domain array failed\n",
-                      iommu->seq_id);
+               pr_err("%s: Allocating domain array failed\n",
+                      iommu->name);
                kfree(iommu->domain_ids);
                iommu->domain_ids = NULL;
                return -ENOMEM;
@@ -1547,7 +1641,7 @@ static int iommu_attach_domain(struct dmar_domain *domain,
        num = __iommu_attach_domain(domain, iommu);
        spin_unlock_irqrestore(&iommu->lock, flags);
        if (num < 0)
-               pr_err("IOMMU: no free domain ids\n");
+               pr_err("%s: No free domain ids\n", iommu->name);
 
        return num;
 }
@@ -1639,7 +1733,7 @@ static int dmar_init_reserved_ranges(void)
        iova = reserve_iova(&reserved_iova_list, IOVA_PFN(IOAPIC_RANGE_START),
                IOVA_PFN(IOAPIC_RANGE_END));
        if (!iova) {
-               printk(KERN_ERR "Reserve IOAPIC range failed\n");
+               pr_err("Reserve IOAPIC range failed\n");
                return -ENODEV;
        }
 
@@ -1655,7 +1749,7 @@ static int dmar_init_reserved_ranges(void)
                                            IOVA_PFN(r->start),
                                            IOVA_PFN(r->end));
                        if (!iova) {
-                               printk(KERN_ERR "Reserve iova failed\n");
+                               pr_err("Reserve iova failed\n");
                                return -ENODEV;
                        }
                }
@@ -1702,7 +1796,7 @@ static int domain_init(struct dmar_domain *domain, int guest_width)
        sagaw = cap_sagaw(iommu->cap);
        if (!test_bit(agaw, &sagaw)) {
                /* hardware doesn't support it, choose a bigger one */
-               pr_debug("IOMMU: hardware doesn't support agaw %d\n", agaw);
+               pr_debug("Hardware doesn't support agaw %d\n", agaw);
                agaw = find_next_bit(&sagaw, 5, agaw);
                if (agaw >= 5)
                        return -ENODEV;
@@ -1795,6 +1889,8 @@ static int domain_context_mapping_one(struct dmar_domain *domain,
                return 0;
        }
 
+       context_clear_entry(context);
+
        id = domain->id;
        pgd = domain->pgd;
 
@@ -1803,7 +1899,7 @@ static int domain_context_mapping_one(struct dmar_domain *domain,
                        id = iommu_attach_vm_domain(domain, iommu);
                        if (id < 0) {
                                spin_unlock_irqrestore(&iommu->lock, flags);
-                               pr_err("IOMMU: no free domain ids\n");
+                               pr_err("%s: No free domain ids\n", iommu->name);
                                return -EFAULT;
                        }
                }
@@ -2030,8 +2126,8 @@ static int __domain_mapping(struct dmar_domain *domain, unsigned long iov_pfn,
                tmp = cmpxchg64_local(&pte->val, 0ULL, pteval);
                if (tmp) {
                        static int dumps = 5;
-                       printk(KERN_CRIT "ERROR: DMA PTE for vPFN 0x%lx already set (to %llx not %llx)\n",
-                              iov_pfn, tmp, (unsigned long long)pteval);
+                       pr_crit("ERROR: DMA PTE for vPFN 0x%lx already set (to %llx not %llx)\n",
+                               iov_pfn, tmp, (unsigned long long)pteval);
                        if (dumps) {
                                dumps--;
                                debug_dma_dump_mappings(NULL);
@@ -2303,7 +2399,7 @@ static int iommu_domain_identity_map(struct dmar_domain *domain,
 
        if (!reserve_iova(&domain->iovad, dma_to_mm_pfn(first_vpfn),
                          dma_to_mm_pfn(last_vpfn))) {
-               printk(KERN_ERR "IOMMU: reserve iova failed\n");
+               pr_err("Reserving iova failed\n");
                return -ENOMEM;
        }
 
@@ -2336,15 +2432,14 @@ static int iommu_prepare_identity_map(struct device *dev,
           range which is reserved in E820, so which didn't get set
           up to start with in si_domain */
        if (domain == si_domain && hw_pass_through) {
-               printk("Ignoring identity map for HW passthrough device %s [0x%Lx - 0x%Lx]\n",
-                      dev_name(dev), start, end);
+               pr_warn("Ignoring identity map for HW passthrough device %s [0x%Lx - 0x%Lx]\n",
+                       dev_name(dev), start, end);
                return 0;
        }
 
-       printk(KERN_INFO
-              "IOMMU: Setting identity map for device %s [0x%Lx - 0x%Lx]\n",
-              dev_name(dev), start, end);
-       
+       pr_info("Setting identity map for device %s [0x%Lx - 0x%Lx]\n",
+               dev_name(dev), start, end);
+
        if (end < start) {
                WARN(1, "Your BIOS is broken; RMRR ends before it starts!\n"
                        "BIOS vendor: %s; Ver: %s; Product Version: %s\n",
@@ -2401,12 +2496,11 @@ static inline void iommu_prepare_isa(void)
        if (!pdev)
                return;
 
-       printk(KERN_INFO "IOMMU: Prepare 0-16MiB unity mapping for LPC\n");
+       pr_info("Prepare 0-16MiB unity mapping for LPC\n");
        ret = iommu_prepare_identity_map(&pdev->dev, 0, 16*1024*1024 - 1);
 
        if (ret)
-               printk(KERN_ERR "IOMMU: Failed to create 0-16MiB identity map; "
-                      "floppy might not work\n");
+               pr_err("Failed to create 0-16MiB identity map - floppy might not work\n");
 
        pci_dev_put(pdev);
 }
@@ -2450,7 +2544,7 @@ static int __init si_domain_init(int hw)
                return -EFAULT;
        }
 
-       pr_debug("IOMMU: identity mapping domain is domain %d\n",
+       pr_debug("Identity mapping domain is domain %d\n",
                 si_domain->id);
 
        if (hw)
@@ -2650,8 +2744,8 @@ static int __init dev_prepare_static_identity_mapping(struct device *dev, int hw
                                  hw ? CONTEXT_TT_PASS_THROUGH :
                                       CONTEXT_TT_MULTI_LEVEL);
        if (!ret)
-               pr_info("IOMMU: %s identity mapping for device %s\n",
-                       hw ? "hardware" : "software", dev_name(dev));
+               pr_info("%s identity mapping for device %s\n",
+                       hw ? "Hardware" : "Software", dev_name(dev));
        else if (ret == -ENODEV)
                /* device not associated with an iommu */
                ret = 0;
@@ -2669,10 +2763,6 @@ static int __init iommu_prepare_static_identity_mapping(int hw)
        int i;
        int ret = 0;
 
-       ret = si_domain_init(hw);
-       if (ret)
-               return -EFAULT;
-
        for_each_pci_dev(pdev) {
                ret = dev_prepare_static_identity_mapping(&pdev->dev, hw);
                if (ret)
@@ -2686,7 +2776,7 @@ static int __init iommu_prepare_static_identity_mapping(int hw)
 
                        if (dev->bus != &acpi_bus_type)
                                continue;
-                               
+
                        adev= to_acpi_device(dev);
                        mutex_lock(&adev->physical_node_lock);
                        list_for_each_entry(pn, &adev->physical_node_list, node) {
@@ -2728,19 +2818,200 @@ static void intel_iommu_init_qi(struct intel_iommu *iommu)
                 */
                iommu->flush.flush_context = __iommu_flush_context;
                iommu->flush.flush_iotlb = __iommu_flush_iotlb;
-               pr_info("IOMMU: %s using Register based invalidation\n",
+               pr_info("%s: Using Register based invalidation\n",
                        iommu->name);
        } else {
                iommu->flush.flush_context = qi_flush_context;
                iommu->flush.flush_iotlb = qi_flush_iotlb;
-               pr_info("IOMMU: %s using Queued invalidation\n", iommu->name);
+               pr_info("%s: Using Queued invalidation\n", iommu->name);
        }
 }
 
+static int copy_context_table(struct intel_iommu *iommu,
+                             struct root_entry *old_re,
+                             struct context_entry **tbl,
+                             int bus, bool ext)
+{
+       struct context_entry *old_ce = NULL, *new_ce = NULL, ce;
+       int tbl_idx, pos = 0, idx, devfn, ret = 0, did;
+       phys_addr_t old_ce_phys;
+
+       tbl_idx = ext ? bus * 2 : bus;
+
+       for (devfn = 0; devfn < 256; devfn++) {
+               /* First calculate the correct index */
+               idx = (ext ? devfn * 2 : devfn) % 256;
+
+               if (idx == 0) {
+                       /* First save what we may have and clean up */
+                       if (new_ce) {
+                               tbl[tbl_idx] = new_ce;
+                               __iommu_flush_cache(iommu, new_ce,
+                                                   VTD_PAGE_SIZE);
+                               pos = 1;
+                       }
+
+                       if (old_ce)
+                               iounmap(old_ce);
+
+                       ret = 0;
+                       if (devfn < 0x80)
+                               old_ce_phys = root_entry_lctp(old_re);
+                       else
+                               old_ce_phys = root_entry_uctp(old_re);
+
+                       if (!old_ce_phys) {
+                               if (ext && devfn == 0) {
+                                       /* No LCTP, try UCTP */
+                                       devfn = 0x7f;
+                                       continue;
+                               } else {
+                                       goto out;
+                               }
+                       }
+
+                       ret = -ENOMEM;
+                       old_ce = ioremap_cache(old_ce_phys, PAGE_SIZE);
+                       if (!old_ce)
+                               goto out;
+
+                       new_ce = alloc_pgtable_page(iommu->node);
+                       if (!new_ce)
+                               goto out_unmap;
+
+                       ret = 0;
+               }
+
+               /* Now copy the context entry */
+               ce = old_ce[idx];
+
+               if (!__context_present(&ce))
+                       continue;
+
+               did = context_domain_id(&ce);
+               if (did >= 0 && did < cap_ndoms(iommu->cap))
+                       set_bit(did, iommu->domain_ids);
+
+               /*
+                * We need a marker for copied context entries. This
+                * marker needs to work for the old format as well as
+                * for extended context entries.
+                *
+                * Bit 67 of the context entry is used. In the old
+                * format this bit is available to software, in the
+                * extended format it is the PGE bit, but PGE is ignored
+                * by HW if PASIDs are disabled (and thus still
+                * available).
+                *
+                * So disable PASIDs first and then mark the entry
+                * copied. This means that we don't copy PASID
+                * translations from the old kernel, but this is fine as
+                * faults there are not fatal.
+                */
+               context_clear_pasid_enable(&ce);
+               context_set_copied(&ce);
+
+               new_ce[idx] = ce;
+       }
+
+       tbl[tbl_idx + pos] = new_ce;
+
+       __iommu_flush_cache(iommu, new_ce, VTD_PAGE_SIZE);
+
+out_unmap:
+       iounmap(old_ce);
+
+out:
+       return ret;
+}
+
+static int copy_translation_tables(struct intel_iommu *iommu)
+{
+       struct context_entry **ctxt_tbls;
+       struct root_entry *old_rt;
+       phys_addr_t old_rt_phys;
+       int ctxt_table_entries;
+       unsigned long flags;
+       u64 rtaddr_reg;
+       int bus, ret;
+       bool new_ext, ext;
+
+       rtaddr_reg = dmar_readq(iommu->reg + DMAR_RTADDR_REG);
+       ext        = !!(rtaddr_reg & DMA_RTADDR_RTT);
+       new_ext    = !!ecap_ecs(iommu->ecap);
+
+       /*
+        * The RTT bit can only be changed when translation is disabled,
+        * but disabling translation means to open a window for data
+        * corruption. So bail out and don't copy anything if we would
+        * have to change the bit.
+        */
+       if (new_ext != ext)
+               return -EINVAL;
+
+       old_rt_phys = rtaddr_reg & VTD_PAGE_MASK;
+       if (!old_rt_phys)
+               return -EINVAL;
+
+       old_rt = ioremap_cache(old_rt_phys, PAGE_SIZE);
+       if (!old_rt)
+               return -ENOMEM;
+
+       /* This is too big for the stack - allocate it from slab */
+       ctxt_table_entries = ext ? 512 : 256;
+       ret = -ENOMEM;
+       ctxt_tbls = kzalloc(ctxt_table_entries * sizeof(void *), GFP_KERNEL);
+       if (!ctxt_tbls)
+               goto out_unmap;
+
+       for (bus = 0; bus < 256; bus++) {
+               ret = copy_context_table(iommu, &old_rt[bus],
+                                        ctxt_tbls, bus, ext);
+               if (ret) {
+                       pr_err("%s: Failed to copy context table for bus %d\n",
+                               iommu->name, bus);
+                       continue;
+               }
+       }
+
+       spin_lock_irqsave(&iommu->lock, flags);
+
+       /* Context tables are copied, now write them to the root_entry table */
+       for (bus = 0; bus < 256; bus++) {
+               int idx = ext ? bus * 2 : bus;
+               u64 val;
+
+               if (ctxt_tbls[idx]) {
+                       val = virt_to_phys(ctxt_tbls[idx]) | 1;
+                       iommu->root_entry[bus].lo = val;
+               }
+
+               if (!ext || !ctxt_tbls[idx + 1])
+                       continue;
+
+               val = virt_to_phys(ctxt_tbls[idx + 1]) | 1;
+               iommu->root_entry[bus].hi = val;
+       }
+
+       spin_unlock_irqrestore(&iommu->lock, flags);
+
+       kfree(ctxt_tbls);
+
+       __iommu_flush_cache(iommu, iommu->root_entry, PAGE_SIZE);
+
+       ret = 0;
+
+out_unmap:
+       iounmap(old_rt);
+
+       return ret;
+}
+
 static int __init init_dmars(void)
 {
        struct dmar_drhd_unit *drhd;
        struct dmar_rmrr_unit *rmrr;
+       bool copied_tables = false;
        struct device *dev;
        struct intel_iommu *iommu;
        int i, ret;
@@ -2761,8 +3032,7 @@ static int __init init_dmars(void)
                        g_num_of_iommus++;
                        continue;
                }
-               printk_once(KERN_ERR "intel-iommu: exceeded %d IOMMUs\n",
-                         DMAR_UNITS_SUPPORTED);
+               pr_err_once("Exceeded %d IOMMUs\n", DMAR_UNITS_SUPPORTED);
        }
 
        /* Preallocate enough resources for IOMMU hot-addition */
@@ -2772,7 +3042,7 @@ static int __init init_dmars(void)
        g_iommus = kcalloc(g_num_of_iommus, sizeof(struct intel_iommu *),
                        GFP_KERNEL);
        if (!g_iommus) {
-               printk(KERN_ERR "Allocating global iommu array failed\n");
+               pr_err("Allocating global iommu array failed\n");
                ret = -ENOMEM;
                goto error;
        }
@@ -2787,10 +3057,21 @@ static int __init init_dmars(void)
        for_each_active_iommu(iommu, drhd) {
                g_iommus[iommu->seq_id] = iommu;
 
+               intel_iommu_init_qi(iommu);
+
                ret = iommu_init_domains(iommu);
                if (ret)
                        goto free_iommu;
 
+               init_translation_status(iommu);
+
+               if (translation_pre_enabled(iommu) && !is_kdump_kernel()) {
+                       iommu_disable_translation(iommu);
+                       clear_translation_pre_enabled(iommu);
+                       pr_warn("Translation was enabled for %s but we are not in kdump mode\n",
+                               iommu->name);
+               }
+
                /*
                 * TBD:
                 * we could share the same root & context tables
@@ -2799,13 +3080,41 @@ static int __init init_dmars(void)
                ret = iommu_alloc_root_entry(iommu);
                if (ret)
                        goto free_iommu;
+
+               if (translation_pre_enabled(iommu)) {
+                       pr_info("Translation already enabled - trying to copy translation structures\n");
+
+                       ret = copy_translation_tables(iommu);
+                       if (ret) {
+                               /*
+                                * We found the IOMMU with translation
+                                * enabled - but failed to copy over the
+                                * old root-entry table. Try to proceed
+                                * by disabling translation now and
+                                * allocating a clean root-entry table.
+                                * This might cause DMAR faults, but
+                                * probably the dump will still succeed.
+                                */
+                               pr_err("Failed to copy translation tables from previous kernel for %s\n",
+                                      iommu->name);
+                               iommu_disable_translation(iommu);
+                               clear_translation_pre_enabled(iommu);
+                       } else {
+                               pr_info("Copied translation tables from previous kernel for %s\n",
+                                       iommu->name);
+                               copied_tables = true;
+                       }
+               }
+
+               iommu_flush_write_buffer(iommu);
+               iommu_set_root_entry(iommu);
+               iommu->flush.flush_context(iommu, 0, 0, 0, DMA_CCMD_GLOBAL_INVL);
+               iommu->flush.flush_iotlb(iommu, 0, 0, 0, DMA_TLB_GLOBAL_FLUSH);
+
                if (!ecap_pass_through(iommu->ecap))
                        hw_pass_through = 0;
        }
 
-       for_each_active_iommu(iommu, drhd)
-               intel_iommu_init_qi(iommu);
-
        if (iommu_pass_through)
                iommu_identity_mapping |= IDENTMAP_ALL;
 
@@ -2813,8 +3122,23 @@ static int __init init_dmars(void)
        iommu_identity_mapping |= IDENTMAP_GFX;
 #endif
 
+       if (iommu_identity_mapping) {
+               ret = si_domain_init(hw_pass_through);
+               if (ret)
+                       goto free_iommu;
+       }
+
        check_tylersburg_isoch();
 
+       /*
+        * If we copied translations from a previous kernel in the kdump
+        * case, we can not assign the devices to domains now, as that
+        * would eliminate the old mappings. So skip this part and defer
+        * the assignment to device driver initialization time.
+        */
+       if (copied_tables)
+               goto domains_done;
+
        /*
         * If pass through is not set or not enabled, setup context entries for
         * identity mappings for rmrr, gfx, and isa and may fall back to static
@@ -2823,7 +3147,7 @@ static int __init init_dmars(void)
        if (iommu_identity_mapping) {
                ret = iommu_prepare_static_identity_mapping(hw_pass_through);
                if (ret) {
-                       printk(KERN_CRIT "Failed to setup IOMMU pass-through\n");
+                       pr_crit("Failed to setup IOMMU pass-through\n");
                        goto free_iommu;
                }
        }
@@ -2841,20 +3165,21 @@ static int __init init_dmars(void)
         *    endfor
         * endfor
         */
-       printk(KERN_INFO "IOMMU: Setting RMRR:\n");
+       pr_info("Setting RMRR:\n");
        for_each_rmrr_units(rmrr) {
                /* some BIOS lists non-exist devices in DMAR table. */
                for_each_active_dev_scope(rmrr->devices, rmrr->devices_cnt,
                                          i, dev) {
                        ret = iommu_prepare_rmrr_dev(rmrr, dev);
                        if (ret)
-                               printk(KERN_ERR
-                                      "IOMMU: mapping reserved region failed\n");
+                               pr_err("Mapping reserved region failed\n");
                }
        }
 
        iommu_prepare_isa();
 
+domains_done:
+
        /*
         * for each drhd
         *   enable fault log
@@ -2879,11 +3204,9 @@ static int __init init_dmars(void)
                if (ret)
                        goto free_iommu;
 
-               iommu_set_root_entry(iommu);
+               if (!translation_pre_enabled(iommu))
+                       iommu_enable_translation(iommu);
 
-               iommu->flush.flush_context(iommu, 0, 0, 0, DMA_CCMD_GLOBAL_INVL);
-               iommu->flush.flush_iotlb(iommu, 0, 0, 0, DMA_TLB_GLOBAL_FLUSH);
-               iommu_enable_translation(iommu);
                iommu_disable_protect_mem_regions(iommu);
        }
 
@@ -2924,7 +3247,7 @@ static struct iova *intel_alloc_iova(struct device *dev,
        }
        iova = alloc_iova(&domain->iovad, nrpages, IOVA_PFN(dma_mask), 1);
        if (unlikely(!iova)) {
-               printk(KERN_ERR "Allocating %ld-page iova for %s failed",
+               pr_err("Allocating %ld-page iova for %s failed",
                       nrpages, dev_name(dev));
                return NULL;
        }
@@ -2939,7 +3262,7 @@ static struct dmar_domain *__get_valid_domain_for_dev(struct device *dev)
 
        domain = get_domain_for_dev(dev, DEFAULT_DOMAIN_ADDRESS_WIDTH);
        if (!domain) {
-               printk(KERN_ERR "Allocating domain for %s failed",
+               pr_err("Allocating domain for %s failed\n",
                       dev_name(dev));
                return NULL;
        }
@@ -2948,7 +3271,7 @@ static struct dmar_domain *__get_valid_domain_for_dev(struct device *dev)
        if (unlikely(!domain_context_mapped(dev))) {
                ret = domain_context_mapping(domain, dev, CONTEXT_TT_MULTI_LEVEL);
                if (ret) {
-                       printk(KERN_ERR "Domain context map for %s failed",
+                       pr_err("Domain context map for %s failed\n",
                               dev_name(dev));
                        return NULL;
                }
@@ -2969,11 +3292,6 @@ static inline struct dmar_domain *get_valid_domain_for_dev(struct device *dev)
        return __get_valid_domain_for_dev(dev);
 }
 
-static int iommu_dummy(struct device *dev)
-{
-       return dev->archdata.iommu == DUMMY_DEVICE_DOMAIN_INFO;
-}
-
 /* Check if the dev needs to go through non-identity map and unmap process.*/
 static int iommu_no_mapping(struct device *dev)
 {
@@ -2995,8 +3313,8 @@ static int iommu_no_mapping(struct device *dev)
                         * to non-identity mapping.
                         */
                        domain_remove_one_dev_info(si_domain, dev);
-                       printk(KERN_INFO "32bit %s uses non-identity mapping\n",
-                              dev_name(dev));
+                       pr_info("32bit %s uses non-identity mapping\n",
+                               dev_name(dev));
                        return 0;
                }
        } else {
@@ -3011,8 +3329,8 @@ static int iommu_no_mapping(struct device *dev)
                                                  CONTEXT_TT_PASS_THROUGH :
                                                  CONTEXT_TT_MULTI_LEVEL);
                        if (!ret) {
-                               printk(KERN_INFO "64bit %s uses identity mapping\n",
-                                      dev_name(dev));
+                               pr_info("64bit %s uses identity mapping\n",
+                                       dev_name(dev));
                                return 1;
                        }
                }
@@ -3081,7 +3399,7 @@ static dma_addr_t __intel_map_single(struct device *dev, phys_addr_t paddr,
 error:
        if (iova)
                __free_iova(&domain->iovad, iova);
-       printk(KERN_ERR"Device %s request: %zx@%llx dir %d --- failed\n",
+       pr_err("Device %s request: %zx@%llx dir %d --- failed\n",
                dev_name(dev), size, (unsigned long long)paddr, dir);
        return 0;
 }
@@ -3396,7 +3714,7 @@ static inline int iommu_domain_cache_init(void)
 
                                         NULL);
        if (!iommu_domain_cache) {
-               printk(KERN_ERR "Couldn't create iommu_domain cache\n");
+               pr_err("Couldn't create iommu_domain cache\n");
                ret = -ENOMEM;
        }
 
@@ -3413,7 +3731,7 @@ static inline int iommu_devinfo_cache_init(void)
                                         SLAB_HWCACHE_ALIGN,
                                         NULL);
        if (!iommu_devinfo_cache) {
-               printk(KERN_ERR "Couldn't create devinfo cache\n");
+               pr_err("Couldn't create devinfo cache\n");
                ret = -ENOMEM;
        }
 
@@ -3790,19 +4108,19 @@ static int intel_iommu_add(struct dmar_drhd_unit *dmaru)
                return 0;
 
        if (hw_pass_through && !ecap_pass_through(iommu->ecap)) {
-               pr_warn("IOMMU: %s doesn't support hardware pass through.\n",
+               pr_warn("%s: Doesn't support hardware pass through.\n",
                        iommu->name);
                return -ENXIO;
        }
        if (!ecap_sc_support(iommu->ecap) &&
            domain_update_iommu_snooping(iommu)) {
-               pr_warn("IOMMU: %s doesn't support snooping.\n",
+               pr_warn("%s: Doesn't support snooping.\n",
                        iommu->name);
                return -ENXIO;
        }
        sp = domain_update_iommu_superpage(iommu) - 1;
        if (sp >= 0 && !(cap_super_page_val(iommu->cap) & (1 << sp))) {
-               pr_warn("IOMMU: %s doesn't support large page.\n",
+               pr_warn("%s: Doesn't support large page.\n",
                        iommu->name);
                return -ENXIO;
        }
@@ -4033,7 +4351,7 @@ static int intel_iommu_memory_notifier(struct notifier_block *nb,
                start = mhp->start_pfn << PAGE_SHIFT;
                end = ((mhp->start_pfn + mhp->nr_pages) << PAGE_SHIFT) - 1;
                if (iommu_domain_identity_map(si_domain, start, end)) {
-                       pr_warn("dmar: failed to build identity map for [%llx-%llx]\n",
+                       pr_warn("Failed to build identity map for [%llx-%llx]\n",
                                start, end);
                        return NOTIFY_BAD;
                }
@@ -4051,7 +4369,7 @@ static int intel_iommu_memory_notifier(struct notifier_block *nb,
 
                        iova = find_iova(&si_domain->iovad, start_vpfn);
                        if (iova == NULL) {
-                               pr_debug("dmar: failed get IOVA for PFN %lx\n",
+                               pr_debug("Failed get IOVA for PFN %lx\n",
                                         start_vpfn);
                                break;
                        }
@@ -4059,7 +4377,7 @@ static int intel_iommu_memory_notifier(struct notifier_block *nb,
                        iova = split_and_remove_iova(&si_domain->iovad, iova,
                                                     start_vpfn, last_vpfn);
                        if (iova == NULL) {
-                               pr_warn("dmar: failed to split IOVA PFN [%lx-%lx]\n",
+                               pr_warn("Failed to split IOVA PFN [%lx-%lx]\n",
                                        start_vpfn, last_vpfn);
                                return NOTIFY_BAD;
                        }
@@ -4168,13 +4486,6 @@ int __init intel_iommu_init(void)
                goto out_free_dmar;
        }
 
-       /*
-        * Disable translation if already enabled prior to OS handover.
-        */
-       for_each_active_iommu(iommu, drhd)
-               if (iommu->gcmd & DMA_GCMD_TE)
-                       iommu_disable_translation(iommu);
-
        if (dmar_dev_scope_init() < 0) {
                if (force_on)
                        panic("tboot: Failed to initialize DMAR device scope\n");
@@ -4185,10 +4496,10 @@ int __init intel_iommu_init(void)
                goto out_free_dmar;
 
        if (list_empty(&dmar_rmrr_units))
-               printk(KERN_INFO "DMAR: No RMRR found\n");
+               pr_info("No RMRR found\n");
 
        if (list_empty(&dmar_atsr_units))
-               printk(KERN_INFO "DMAR: No ATSR found\n");
+               pr_info("No ATSR found\n");
 
        if (dmar_init_reserved_ranges()) {
                if (force_on)
@@ -4202,12 +4513,11 @@ int __init intel_iommu_init(void)
        if (ret) {
                if (force_on)
                        panic("tboot: Failed to initialize DMARs\n");
-               printk(KERN_ERR "IOMMU: dmar init failed\n");
+               pr_err("Initialization failed\n");
                goto out_free_reserved_range;
        }
        up_write(&dmar_global_lock);
-       printk(KERN_INFO
-       "PCI-DMA: Intel(R) Virtualization Technology for Directed I/O\n");
+       pr_info("Intel(R) Virtualization Technology for Directed I/O\n");
 
        init_timer(&unmap_timer);
 #ifdef CONFIG_SWIOTLB
@@ -4349,13 +4659,11 @@ static struct iommu_domain *intel_iommu_domain_alloc(unsigned type)
 
        dmar_domain = alloc_domain(DOMAIN_FLAG_VIRTUAL_MACHINE);
        if (!dmar_domain) {
-               printk(KERN_ERR
-                       "intel_iommu_domain_init: dmar_domain == NULL\n");
+               pr_err("Can't allocate dmar_domain\n");
                return NULL;
        }
        if (md_domain_init(dmar_domain, DEFAULT_DOMAIN_ADDRESS_WIDTH)) {
-               printk(KERN_ERR
-                       "intel_iommu_domain_init() failed\n");
+               pr_err("Domain initialization failed\n");
                domain_exit(dmar_domain);
                return NULL;
        }
@@ -4414,7 +4722,7 @@ static int intel_iommu_attach_device(struct iommu_domain *domain,
                addr_width = cap_mgaw(iommu->cap);
 
        if (dmar_domain->max_addr > (1LL << addr_width)) {
-               printk(KERN_ERR "%s: iommu width (%d) is not "
+               pr_err("%s: iommu width (%d) is not "
                       "sufficient for the mapped address (%llx)\n",
                       __func__, addr_width, dmar_domain->max_addr);
                return -EFAULT;
@@ -4468,7 +4776,7 @@ static int intel_iommu_map(struct iommu_domain *domain,
                /* check if minimum agaw is sufficient for mapped address */
                end = __DOMAIN_MAX_ADDR(dmar_domain->gaw) + 1;
                if (end < max_addr) {
-                       printk(KERN_ERR "%s: iommu width (%d) is not "
+                       pr_err("%s: iommu width (%d) is not "
                               "sufficient for the mapped address (%llx)\n",
                               __func__, dmar_domain->gaw, max_addr);
                        return -EFAULT;
@@ -4609,7 +4917,7 @@ static const struct iommu_ops intel_iommu_ops = {
 static void quirk_iommu_g4x_gfx(struct pci_dev *dev)
 {
        /* G4x/GM45 integrated gfx dmar support is totally busted. */
-       printk(KERN_INFO "DMAR: Disabling IOMMU for graphics on this chipset\n");
+       pr_info("Disabling IOMMU for graphics on this chipset\n");
        dmar_map_gfx = 0;
 }
 
@@ -4627,7 +4935,7 @@ static void quirk_iommu_rwbf(struct pci_dev *dev)
         * Mobile 4 Series Chipset neglects to set RWBF capability,
         * but needs it. Same seems to hold for the desktop versions.
         */
-       printk(KERN_INFO "DMAR: Forcing write-buffer flush capability\n");
+       pr_info("Forcing write-buffer flush capability\n");
        rwbf_quirk = 1;
 }
 
@@ -4657,11 +4965,11 @@ static void quirk_calpella_no_shadow_gtt(struct pci_dev *dev)
                return;
 
        if (!(ggc & GGC_MEMORY_VT_ENABLED)) {
-               printk(KERN_INFO "DMAR: BIOS has allocated no shadow GTT; disabling IOMMU for graphics\n");
+               pr_info("BIOS has allocated no shadow GTT; disabling IOMMU for graphics\n");
                dmar_map_gfx = 0;
        } else if (dmar_map_gfx) {
                /* we have to ensure the gfx device is idle before we flush */
-               printk(KERN_INFO "DMAR: Disabling batched IOTLB flush on Ironlake\n");
+               pr_info("Disabling batched IOTLB flush on Ironlake\n");
                intel_iommu_strict = 1;
        }
 }
@@ -4723,7 +5031,7 @@ static void __init check_tylersburg_isoch(void)
                iommu_identity_mapping |= IDENTMAP_AZALIA;
                return;
        }
-       
-       printk(KERN_WARNING "DMAR: Recommended TLB entries for ISOCH unit is 16; your BIOS set %d\n",
+
+       pr_warn("Recommended TLB entries for ISOCH unit is 16; your BIOS set %d\n",
               vtisochctrl);
 }
index 5709ae9c3e771d2f82a1bda2a23d500d8f4faffe..f15692a410c7e7064e844be2b7d83feee20ef5c5 100644 (file)
@@ -1,3 +1,6 @@
+
+#define pr_fmt(fmt)     "DMAR-IR: " fmt
+
 #include <linux/interrupt.h>
 #include <linux/dmar.h>
 #include <linux/spinlock.h>
@@ -8,6 +11,8 @@
 #include <linux/irq.h>
 #include <linux/intel-iommu.h>
 #include <linux/acpi.h>
+#include <linux/irqdomain.h>
+#include <linux/crash_dump.h>
 #include <asm/io_apic.h>
 #include <asm/smp.h>
 #include <asm/cpu.h>
 
 #include "irq_remapping.h"
 
+enum irq_mode {
+       IRQ_REMAPPING,
+       IRQ_POSTING,
+};
+
 struct ioapic_scope {
        struct intel_iommu *iommu;
        unsigned int id;
@@ -31,6 +41,22 @@ struct hpet_scope {
        unsigned int devfn;
 };
 
+struct irq_2_iommu {
+       struct intel_iommu *iommu;
+       u16 irte_index;
+       u16 sub_handle;
+       u8  irte_mask;
+       enum irq_mode mode;
+};
+
+struct intel_ir_data {
+       struct irq_2_iommu                      irq_2_iommu;
+       struct irte                             irte_entry;
+       union {
+               struct msi_msg                  msi_entry;
+       };
+};
+
 #define IR_X2APIC_MODE(mode) (mode ? (1 << 11) : 0)
 #define IRTE_DEST(dest) ((eim_mode) ? dest : dest << 8)
 
@@ -50,43 +76,34 @@ static struct hpet_scope ir_hpet[MAX_HPET_TBS];
  * the dmar_global_lock.
  */
 static DEFINE_RAW_SPINLOCK(irq_2_ir_lock);
+static struct irq_domain_ops intel_ir_domain_ops;
 
+static void iommu_disable_irq_remapping(struct intel_iommu *iommu);
 static int __init parse_ioapics_under_ir(void);
 
-static struct irq_2_iommu *irq_2_iommu(unsigned int irq)
+static bool ir_pre_enabled(struct intel_iommu *iommu)
 {
-       struct irq_cfg *cfg = irq_cfg(irq);
-       return cfg ? &cfg->irq_2_iommu : NULL;
+       return (iommu->flags & VTD_FLAG_IRQ_REMAP_PRE_ENABLED);
 }
 
-static int get_irte(int irq, struct irte *entry)
+static void clear_ir_pre_enabled(struct intel_iommu *iommu)
 {
-       struct irq_2_iommu *irq_iommu = irq_2_iommu(irq);
-       unsigned long flags;
-       int index;
-
-       if (!entry || !irq_iommu)
-               return -1;
-
-       raw_spin_lock_irqsave(&irq_2_ir_lock, flags);
-
-       if (unlikely(!irq_iommu->iommu)) {
-               raw_spin_unlock_irqrestore(&irq_2_ir_lock, flags);
-               return -1;
-       }
+       iommu->flags &= ~VTD_FLAG_IRQ_REMAP_PRE_ENABLED;
+}
 
-       index = irq_iommu->irte_index + irq_iommu->sub_handle;
-       *entry = *(irq_iommu->iommu->ir_table->base + index);
+static void init_ir_status(struct intel_iommu *iommu)
+{
+       u32 gsts;
 
-       raw_spin_unlock_irqrestore(&irq_2_ir_lock, flags);
-       return 0;
+       gsts = readl(iommu->reg + DMAR_GSTS_REG);
+       if (gsts & DMA_GSTS_IRES)
+               iommu->flags |= VTD_FLAG_IRQ_REMAP_PRE_ENABLED;
 }
 
-static int alloc_irte(struct intel_iommu *iommu, int irq, u16 count)
+static int alloc_irte(struct intel_iommu *iommu, int irq,
+                     struct irq_2_iommu *irq_iommu, u16 count)
 {
        struct ir_table *table = iommu->ir_table;
-       struct irq_2_iommu *irq_iommu = irq_2_iommu(irq);
-       struct irq_cfg *cfg = irq_cfg(irq);
        unsigned int mask = 0;
        unsigned long flags;
        int index;
@@ -100,8 +117,7 @@ static int alloc_irte(struct intel_iommu *iommu, int irq, u16 count)
        }
 
        if (mask > ecap_max_handle_mask(iommu->ecap)) {
-               printk(KERN_ERR
-                      "Requested mask %x exceeds the max invalidation handle"
+               pr_err("Requested mask %x exceeds the max invalidation handle"
                       " mask value %Lx\n", mask,
                       ecap_max_handle_mask(iommu->ecap));
                return -1;
@@ -113,11 +129,11 @@ static int alloc_irte(struct intel_iommu *iommu, int irq, u16 count)
        if (index < 0) {
                pr_warn("IR%d: can't allocate an IRTE\n", iommu->seq_id);
        } else {
-               cfg->remapped = 1;
                irq_iommu->iommu = iommu;
                irq_iommu->irte_index =  index;
                irq_iommu->sub_handle = 0;
                irq_iommu->irte_mask = mask;
+               irq_iommu->mode = IRQ_REMAPPING;
        }
        raw_spin_unlock_irqrestore(&irq_2_ir_lock, flags);
 
@@ -135,47 +151,9 @@ static int qi_flush_iec(struct intel_iommu *iommu, int index, int mask)
        return qi_submit_sync(&desc, iommu);
 }
 
-static int map_irq_to_irte_handle(int irq, u16 *sub_handle)
-{
-       struct irq_2_iommu *irq_iommu = irq_2_iommu(irq);
-       unsigned long flags;
-       int index;
-
-       if (!irq_iommu)
-               return -1;
-
-       raw_spin_lock_irqsave(&irq_2_ir_lock, flags);
-       *sub_handle = irq_iommu->sub_handle;
-       index = irq_iommu->irte_index;
-       raw_spin_unlock_irqrestore(&irq_2_ir_lock, flags);
-       return index;
-}
-
-static int set_irte_irq(int irq, struct intel_iommu *iommu, u16 index, u16 subhandle)
-{
-       struct irq_2_iommu *irq_iommu = irq_2_iommu(irq);
-       struct irq_cfg *cfg = irq_cfg(irq);
-       unsigned long flags;
-
-       if (!irq_iommu)
-               return -1;
-
-       raw_spin_lock_irqsave(&irq_2_ir_lock, flags);
-
-       cfg->remapped = 1;
-       irq_iommu->iommu = iommu;
-       irq_iommu->irte_index = index;
-       irq_iommu->sub_handle = subhandle;
-       irq_iommu->irte_mask = 0;
-
-       raw_spin_unlock_irqrestore(&irq_2_ir_lock, flags);
-
-       return 0;
-}
-
-static int modify_irte(int irq, struct irte *irte_modified)
+static int modify_irte(struct irq_2_iommu *irq_iommu,
+                      struct irte *irte_modified)
 {
-       struct irq_2_iommu *irq_iommu = irq_2_iommu(irq);
        struct intel_iommu *iommu;
        unsigned long flags;
        struct irte *irte;
@@ -196,6 +174,9 @@ static int modify_irte(int irq, struct irte *irte_modified)
        __iommu_flush_cache(iommu, irte, sizeof(*irte));
 
        rc = qi_flush_iec(iommu, index, 0);
+
+       /* Update iommu mode according to the IRTE mode */
+       irq_iommu->mode = irte->pst ? IRQ_POSTING : IRQ_REMAPPING;
        raw_spin_unlock_irqrestore(&irq_2_ir_lock, flags);
 
        return rc;
@@ -242,7 +223,7 @@ static int clear_entries(struct irq_2_iommu *irq_iommu)
                return 0;
 
        iommu = irq_iommu->iommu;
-       index = irq_iommu->irte_index + irq_iommu->sub_handle;
+       index = irq_iommu->irte_index;
 
        start = iommu->ir_table->base + index;
        end = start + (1 << irq_iommu->irte_mask);
@@ -257,29 +238,6 @@ static int clear_entries(struct irq_2_iommu *irq_iommu)
        return qi_flush_iec(iommu, index, irq_iommu->irte_mask);
 }
 
-static int free_irte(int irq)
-{
-       struct irq_2_iommu *irq_iommu = irq_2_iommu(irq);
-       unsigned long flags;
-       int rc;
-
-       if (!irq_iommu)
-               return -1;
-
-       raw_spin_lock_irqsave(&irq_2_ir_lock, flags);
-
-       rc = clear_entries(irq_iommu);
-
-       irq_iommu->iommu = NULL;
-       irq_iommu->irte_index = 0;
-       irq_iommu->sub_handle = 0;
-       irq_iommu->irte_mask = 0;
-
-       raw_spin_unlock_irqrestore(&irq_2_ir_lock, flags);
-
-       return rc;
-}
-
 /*
  * source validation type
  */
@@ -333,7 +291,7 @@ static int set_ioapic_sid(struct irte *irte, int apic)
        up_read(&dmar_global_lock);
 
        if (sid == 0) {
-               pr_warning("Failed to set source-id of IOAPIC (%d)\n", apic);
+               pr_warn("Failed to set source-id of IOAPIC (%d)\n", apic);
                return -1;
        }
 
@@ -360,7 +318,7 @@ static int set_hpet_sid(struct irte *irte, u8 id)
        up_read(&dmar_global_lock);
 
        if (sid == 0) {
-               pr_warning("Failed to set source-id of HPET block (%d)\n", id);
+               pr_warn("Failed to set source-id of HPET block (%d)\n", id);
                return -1;
        }
 
@@ -424,11 +382,59 @@ static int set_msi_sid(struct irte *irte, struct pci_dev *dev)
        return 0;
 }
 
+static int iommu_load_old_irte(struct intel_iommu *iommu)
+{
+       struct irte *old_ir_table;
+       phys_addr_t irt_phys;
+       unsigned int i;
+       size_t size;
+       u64 irta;
+
+       if (!is_kdump_kernel()) {
+               pr_warn("IRQ remapping was enabled on %s but we are not in kdump mode\n",
+                       iommu->name);
+               clear_ir_pre_enabled(iommu);
+               iommu_disable_irq_remapping(iommu);
+               return -EINVAL;
+       }
+
+       /* Check whether the old ir-table has the same size as ours */
+       irta = dmar_readq(iommu->reg + DMAR_IRTA_REG);
+       if ((irta & INTR_REMAP_TABLE_REG_SIZE_MASK)
+            != INTR_REMAP_TABLE_REG_SIZE)
+               return -EINVAL;
+
+       irt_phys = irta & VTD_PAGE_MASK;
+       size     = INTR_REMAP_TABLE_ENTRIES*sizeof(struct irte);
+
+       /* Map the old IR table */
+       old_ir_table = ioremap_cache(irt_phys, size);
+       if (!old_ir_table)
+               return -ENOMEM;
+
+       /* Copy data over */
+       memcpy(iommu->ir_table->base, old_ir_table, size);
+
+       __iommu_flush_cache(iommu, iommu->ir_table->base, size);
+
+       /*
+        * Now check the table for used entries and mark those as
+        * allocated in the bitmap
+        */
+       for (i = 0; i < INTR_REMAP_TABLE_ENTRIES; i++) {
+               if (iommu->ir_table->base[i].present)
+                       bitmap_set(iommu->ir_table->bitmap, i, 1);
+       }
+
+       return 0;
+}
+
+
 static void iommu_set_irq_remapping(struct intel_iommu *iommu, int mode)
 {
+       unsigned long flags;
        u64 addr;
        u32 sts;
-       unsigned long flags;
 
        addr = virt_to_phys((void *)iommu->ir_table->base);
 
@@ -445,10 +451,16 @@ static void iommu_set_irq_remapping(struct intel_iommu *iommu, int mode)
        raw_spin_unlock_irqrestore(&iommu->register_lock, flags);
 
        /*
-        * global invalidation of interrupt entry cache before enabling
-        * interrupt-remapping.
+        * Global invalidation of interrupt entry cache to make sure the
+        * hardware uses the new irq remapping table.
         */
        qi_global_iec(iommu);
+}
+
+static void iommu_enable_irq_remapping(struct intel_iommu *iommu)
+{
+       unsigned long flags;
+       u32 sts;
 
        raw_spin_lock_irqsave(&iommu->register_lock, flags);
 
@@ -488,7 +500,6 @@ static int intel_setup_irq_remapping(struct intel_iommu *iommu)
 
        pages = alloc_pages_node(iommu->node, GFP_KERNEL | __GFP_ZERO,
                                 INTR_REMAP_PAGE_ORDER);
-
        if (!pages) {
                pr_err("IR%d: failed to allocate pages of order %d\n",
                       iommu->seq_id, INTR_REMAP_PAGE_ORDER);
@@ -502,21 +513,75 @@ static int intel_setup_irq_remapping(struct intel_iommu *iommu)
                goto out_free_pages;
        }
 
+       iommu->ir_domain = irq_domain_add_hierarchy(arch_get_ir_parent_domain(),
+                                                   0, INTR_REMAP_TABLE_ENTRIES,
+                                                   NULL, &intel_ir_domain_ops,
+                                                   iommu);
+       if (!iommu->ir_domain) {
+               pr_err("IR%d: failed to allocate irqdomain\n", iommu->seq_id);
+               goto out_free_bitmap;
+       }
+       iommu->ir_msi_domain = arch_create_msi_irq_domain(iommu->ir_domain);
+
        ir_table->base = page_address(pages);
        ir_table->bitmap = bitmap;
        iommu->ir_table = ir_table;
+
+       /*
+        * If the queued invalidation is already initialized,
+        * shouldn't disable it.
+        */
+       if (!iommu->qi) {
+               /*
+                * Clear previous faults.
+                */
+               dmar_fault(-1, iommu);
+               dmar_disable_qi(iommu);
+
+               if (dmar_enable_qi(iommu)) {
+                       pr_err("Failed to enable queued invalidation\n");
+                       goto out_free_bitmap;
+               }
+       }
+
+       init_ir_status(iommu);
+
+       if (ir_pre_enabled(iommu)) {
+               if (iommu_load_old_irte(iommu))
+                       pr_err("Failed to copy IR table for %s from previous kernel\n",
+                              iommu->name);
+               else
+                       pr_info("Copied IR table for %s from previous kernel\n",
+                               iommu->name);
+       }
+
+       iommu_set_irq_remapping(iommu, eim_mode);
+
        return 0;
 
+out_free_bitmap:
+       kfree(bitmap);
 out_free_pages:
        __free_pages(pages, INTR_REMAP_PAGE_ORDER);
 out_free_table:
        kfree(ir_table);
+
+       iommu->ir_table  = NULL;
+
        return -ENOMEM;
 }
 
 static void intel_teardown_irq_remapping(struct intel_iommu *iommu)
 {
        if (iommu && iommu->ir_table) {
+               if (iommu->ir_msi_domain) {
+                       irq_domain_remove(iommu->ir_msi_domain);
+                       iommu->ir_msi_domain = NULL;
+               }
+               if (iommu->ir_domain) {
+                       irq_domain_remove(iommu->ir_domain);
+                       iommu->ir_domain = NULL;
+               }
                free_pages((unsigned long)iommu->ir_table->base,
                           INTR_REMAP_PAGE_ORDER);
                kfree(iommu->ir_table->bitmap);
@@ -580,17 +645,17 @@ static void __init intel_cleanup_irq_remapping(void)
        }
 
        if (x2apic_supported())
-               pr_warn("Failed to enable irq remapping.  You are vulnerable to irq-injection attacks.\n");
+               pr_warn("Failed to enable irq remapping. You are vulnerable to irq-injection attacks.\n");
 }
 
 static int __init intel_prepare_irq_remapping(void)
 {
        struct dmar_drhd_unit *drhd;
        struct intel_iommu *iommu;
+       int eim = 0;
 
        if (irq_remap_broken) {
-               printk(KERN_WARNING
-                       "This system BIOS has enabled interrupt remapping\n"
+               pr_warn("This system BIOS has enabled interrupt remapping\n"
                        "on a chipset that contains an erratum making that\n"
                        "feature unstable.  To maintain system stability\n"
                        "interrupt remapping is being disabled.  Please\n"
@@ -606,7 +671,7 @@ static int __init intel_prepare_irq_remapping(void)
                return -ENODEV;
 
        if (parse_ioapics_under_ir() != 1) {
-               printk(KERN_INFO "Not enabling interrupt remapping\n");
+               pr_info("Not enabling interrupt remapping\n");
                goto error;
        }
 
@@ -615,85 +680,74 @@ static int __init intel_prepare_irq_remapping(void)
                if (!ecap_ir_support(iommu->ecap))
                        goto error;
 
-       /* Do the allocations early */
-       for_each_iommu(iommu, drhd)
-               if (intel_setup_irq_remapping(iommu))
-                       goto error;
-
-       return 0;
-
-error:
-       intel_cleanup_irq_remapping();
-       return -ENODEV;
-}
-
-static int __init intel_enable_irq_remapping(void)
-{
-       struct dmar_drhd_unit *drhd;
-       struct intel_iommu *iommu;
-       bool setup = false;
-       int eim = 0;
-
+       /* Detect remapping mode: lapic or x2apic */
        if (x2apic_supported()) {
                eim = !dmar_x2apic_optout();
-               if (!eim)
-                       pr_info("x2apic is disabled because BIOS sets x2apic opt out bit. You can use 'intremap=no_x2apic_optout' to override the BIOS setting.\n");
+               if (!eim) {
+                       pr_info("x2apic is disabled because BIOS sets x2apic opt out bit.");
+                       pr_info("Use 'intremap=no_x2apic_optout' to override the BIOS setting.\n");
+               }
        }
 
        for_each_iommu(iommu, drhd) {
-               /*
-                * If the queued invalidation is already initialized,
-                * shouldn't disable it.
-                */
-               if (iommu->qi)
-                       continue;
-
-               /*
-                * Clear previous faults.
-                */
-               dmar_fault(-1, iommu);
-
-               /*
-                * Disable intr remapping and queued invalidation, if already
-                * enabled prior to OS handover.
-                */
-               iommu_disable_irq_remapping(iommu);
-
-               dmar_disable_qi(iommu);
-       }
-
-       /*
-        * check for the Interrupt-remapping support
-        */
-       for_each_iommu(iommu, drhd)
                if (eim && !ecap_eim_support(iommu->ecap)) {
-                       printk(KERN_INFO "DRHD %Lx: EIM not supported by DRHD, "
-                              " ecap %Lx\n", drhd->reg_base_addr, iommu->ecap);
+                       pr_info("%s does not support EIM\n", iommu->name);
                        eim = 0;
                }
+       }
+
        eim_mode = eim;
        if (eim)
                pr_info("Queued invalidation will be enabled to support x2apic and Intr-remapping.\n");
 
-       /*
-        * Enable queued invalidation for all the DRHD's.
-        */
+       /* Do the initializations early */
        for_each_iommu(iommu, drhd) {
-               int ret = dmar_enable_qi(iommu);
-
-               if (ret) {
-                       printk(KERN_ERR "DRHD %Lx: failed to enable queued, "
-                              " invalidation, ecap %Lx, ret %d\n",
-                              drhd->reg_base_addr, iommu->ecap, ret);
+               if (intel_setup_irq_remapping(iommu)) {
+                       pr_err("Failed to setup irq remapping for %s\n",
+                              iommu->name);
                        goto error;
                }
        }
 
+       return 0;
+
+error:
+       intel_cleanup_irq_remapping();
+       return -ENODEV;
+}
+
+/*
+ * Set Posted-Interrupts capability.
+ */
+static inline void set_irq_posting_cap(void)
+{
+       struct dmar_drhd_unit *drhd;
+       struct intel_iommu *iommu;
+
+       if (!disable_irq_post) {
+               intel_irq_remap_ops.capability |= 1 << IRQ_POSTING_CAP;
+
+               for_each_iommu(iommu, drhd)
+                       if (!cap_pi_support(iommu->cap)) {
+                               intel_irq_remap_ops.capability &=
+                                               ~(1 << IRQ_POSTING_CAP);
+                               break;
+                       }
+       }
+}
+
+static int __init intel_enable_irq_remapping(void)
+{
+       struct dmar_drhd_unit *drhd;
+       struct intel_iommu *iommu;
+       bool setup = false;
+
        /*
         * Setup Interrupt-remapping for all the DRHD's now.
         */
        for_each_iommu(iommu, drhd) {
-               iommu_set_irq_remapping(iommu, eim);
+               if (!ir_pre_enabled(iommu))
+                       iommu_enable_irq_remapping(iommu);
                setup = true;
        }
 
@@ -702,16 +756,11 @@ static int __init intel_enable_irq_remapping(void)
 
        irq_remapping_enabled = 1;
 
-       /*
-        * VT-d has a different layout for IO-APIC entries when
-        * interrupt remapping is enabled. So it needs a special routine
-        * to print IO-APIC entries for debugging purposes too.
-        */
-       x86_io_apic_ops.print_entries = intel_ir_io_apic_print_entries;
+       set_irq_posting_cap();
 
-       pr_info("Enabled IRQ remapping in %s mode\n", eim ? "x2apic" : "xapic");
+       pr_info("Enabled IRQ remapping in %s mode\n", eim_mode ? "x2apic" : "xapic");
 
-       return eim ? IRQ_REMAP_X2APIC_MODE : IRQ_REMAP_XAPIC_MODE;
+       return eim_mode ? IRQ_REMAP_X2APIC_MODE : IRQ_REMAP_XAPIC_MODE;
 
 error:
        intel_cleanup_irq_remapping();
@@ -909,6 +958,12 @@ static void disable_irq_remapping(void)
 
                iommu_disable_irq_remapping(iommu);
        }
+
+       /*
+        * Clear Posted-Interrupts capability.
+        */
+       if (!disable_irq_post)
+               intel_irq_remap_ops.capability &= ~(1 << IRQ_POSTING_CAP);
 }
 
 static int reenable_irq_remapping(int eim)
@@ -930,12 +985,15 @@ static int reenable_irq_remapping(int eim)
 
                /* Set up interrupt remapping for iommu.*/
                iommu_set_irq_remapping(iommu, eim);
+               iommu_enable_irq_remapping(iommu);
                setup = true;
        }
 
        if (!setup)
                goto error;
 
+       set_irq_posting_cap();
+
        return 0;
 
 error:
@@ -945,8 +1003,7 @@ error:
        return -1;
 }
 
-static void prepare_irte(struct irte *irte, int vector,
-                        unsigned int dest)
+static void prepare_irte(struct irte *irte, int vector, unsigned int dest)
 {
        memset(irte, 0, sizeof(*irte));
 
@@ -966,76 +1023,63 @@ static void prepare_irte(struct irte *irte, int vector,
        irte->redir_hint = 1;
 }
 
-static int intel_setup_ioapic_entry(int irq,
-                                   struct IO_APIC_route_entry *route_entry,
-                                   unsigned int destination, int vector,
-                                   struct io_apic_irq_attr *attr)
+static struct irq_domain *intel_get_ir_irq_domain(struct irq_alloc_info *info)
 {
-       int ioapic_id = mpc_ioapic_id(attr->ioapic);
-       struct intel_iommu *iommu;
-       struct IR_IO_APIC_route_entry *entry;
-       struct irte irte;
-       int index;
-
-       down_read(&dmar_global_lock);
-       iommu = map_ioapic_to_ir(ioapic_id);
-       if (!iommu) {
-               pr_warn("No mapping iommu for ioapic %d\n", ioapic_id);
-               index = -ENODEV;
-       } else {
-               index = alloc_irte(iommu, irq, 1);
-               if (index < 0) {
-                       pr_warn("Failed to allocate IRTE for ioapic %d\n",
-                               ioapic_id);
-                       index = -ENOMEM;
-               }
-       }
-       up_read(&dmar_global_lock);
-       if (index < 0)
-               return index;
-
-       prepare_irte(&irte, vector, destination);
+       struct intel_iommu *iommu = NULL;
 
-       /* Set source-id of interrupt request */
-       set_ioapic_sid(&irte, ioapic_id);
+       if (!info)
+               return NULL;
 
-       modify_irte(irq, &irte);
+       switch (info->type) {
+       case X86_IRQ_ALLOC_TYPE_IOAPIC:
+               iommu = map_ioapic_to_ir(info->ioapic_id);
+               break;
+       case X86_IRQ_ALLOC_TYPE_HPET:
+               iommu = map_hpet_to_ir(info->hpet_id);
+               break;
+       case X86_IRQ_ALLOC_TYPE_MSI:
+       case X86_IRQ_ALLOC_TYPE_MSIX:
+               iommu = map_dev_to_ir(info->msi_dev);
+               break;
+       default:
+               BUG_ON(1);
+               break;
+       }
 
-       apic_printk(APIC_VERBOSE, KERN_DEBUG "IOAPIC[%d]: "
-               "Set IRTE entry (P:%d FPD:%d Dst_Mode:%d "
-               "Redir_hint:%d Trig_Mode:%d Dlvry_Mode:%X "
-               "Avail:%X Vector:%02X Dest:%08X "
-               "SID:%04X SQ:%X SVT:%X)\n",
-               attr->ioapic, irte.present, irte.fpd, irte.dst_mode,
-               irte.redir_hint, irte.trigger_mode, irte.dlvry_mode,
-               irte.avail, irte.vector, irte.dest_id,
-               irte.sid, irte.sq, irte.svt);
+       return iommu ? iommu->ir_domain : NULL;
+}
 
-       entry = (struct IR_IO_APIC_route_entry *)route_entry;
-       memset(entry, 0, sizeof(*entry));
+static struct irq_domain *intel_get_irq_domain(struct irq_alloc_info *info)
+{
+       struct intel_iommu *iommu;
 
-       entry->index2   = (index >> 15) & 0x1;
-       entry->zero     = 0;
-       entry->format   = 1;
-       entry->index    = (index & 0x7fff);
-       /*
-        * IO-APIC RTE will be configured with virtual vector.
-        * irq handler will do the explicit EOI to the io-apic.
-        */
-       entry->vector   = attr->ioapic_pin;
-       entry->mask     = 0;                    /* enable IRQ */
-       entry->trigger  = attr->trigger;
-       entry->polarity = attr->polarity;
+       if (!info)
+               return NULL;
 
-       /* Mask level triggered irqs.
-        * Use IRQ_DELAYED_DISABLE for edge triggered irqs.
-        */
-       if (attr->trigger)
-               entry->mask = 1;
+       switch (info->type) {
+       case X86_IRQ_ALLOC_TYPE_MSI:
+       case X86_IRQ_ALLOC_TYPE_MSIX:
+               iommu = map_dev_to_ir(info->msi_dev);
+               if (iommu)
+                       return iommu->ir_msi_domain;
+               break;
+       default:
+               break;
+       }
 
-       return 0;
+       return NULL;
 }
 
+struct irq_remap_ops intel_irq_remap_ops = {
+       .prepare                = intel_prepare_irq_remapping,
+       .enable                 = intel_enable_irq_remapping,
+       .disable                = disable_irq_remapping,
+       .reenable               = reenable_irq_remapping,
+       .enable_faulting        = enable_drhd_fault_handling,
+       .get_ir_irq_domain      = intel_get_ir_irq_domain,
+       .get_irq_domain         = intel_get_irq_domain,
+};
+
 /*
  * Migrate the IO-APIC irq in the presence of intr-remapping.
  *
@@ -1051,170 +1095,281 @@ static int intel_setup_ioapic_entry(int irq,
  * is used to migrate MSI irq's in the presence of interrupt-remapping.
  */
 static int
-intel_ioapic_set_affinity(struct irq_data *data, const struct cpumask *mask,
-                         bool force)
+intel_ir_set_affinity(struct irq_data *data, const struct cpumask *mask,
+                     bool force)
 {
+       struct intel_ir_data *ir_data = data->chip_data;
+       struct irte *irte = &ir_data->irte_entry;
        struct irq_cfg *cfg = irqd_cfg(data);
-       unsigned int dest, irq = data->irq;
-       struct irte irte;
-       int err;
-
-       if (!config_enabled(CONFIG_SMP))
-               return -EINVAL;
-
-       if (!cpumask_intersects(mask, cpu_online_mask))
-               return -EINVAL;
-
-       if (get_irte(irq, &irte))
-               return -EBUSY;
-
-       err = assign_irq_vector(irq, cfg, mask);
-       if (err)
-               return err;
-
-       err = apic->cpu_mask_to_apicid_and(cfg->domain, mask, &dest);
-       if (err) {
-               if (assign_irq_vector(irq, cfg, data->affinity))
-                       pr_err("Failed to recover vector for irq %d\n", irq);
-               return err;
-       }
+       struct irq_data *parent = data->parent_data;
+       int ret;
 
-       irte.vector = cfg->vector;
-       irte.dest_id = IRTE_DEST(dest);
+       ret = parent->chip->irq_set_affinity(parent, mask, force);
+       if (ret < 0 || ret == IRQ_SET_MASK_OK_DONE)
+               return ret;
 
        /*
         * Atomically updates the IRTE with the new destination, vector
         * and flushes the interrupt entry cache.
         */
-       modify_irte(irq, &irte);
+       irte->vector = cfg->vector;
+       irte->dest_id = IRTE_DEST(cfg->dest_apicid);
+
+       /* Update the hardware only if the interrupt is in remapped mode. */
+       if (ir_data->irq_2_iommu.mode == IRQ_REMAPPING)
+               modify_irte(&ir_data->irq_2_iommu, irte);
 
        /*
         * After this point, all the interrupts will start arriving
         * at the new destination. So, time to cleanup the previous
         * vector allocation.
         */
-       if (cfg->move_in_progress)
-               send_cleanup_vector(cfg);
+       send_cleanup_vector(cfg);
 
-       cpumask_copy(data->affinity, mask);
-       return 0;
+       return IRQ_SET_MASK_OK_DONE;
 }
 
-static void intel_compose_msi_msg(struct pci_dev *pdev,
-                                 unsigned int irq, unsigned int dest,
-                                 struct msi_msg *msg, u8 hpet_id)
+static void intel_ir_compose_msi_msg(struct irq_data *irq_data,
+                                    struct msi_msg *msg)
 {
-       struct irq_cfg *cfg;
-       struct irte irte;
-       u16 sub_handle = 0;
-       int ir_index;
-
-       cfg = irq_cfg(irq);
+       struct intel_ir_data *ir_data = irq_data->chip_data;
 
-       ir_index = map_irq_to_irte_handle(irq, &sub_handle);
-       BUG_ON(ir_index == -1);
+       *msg = ir_data->msi_entry;
+}
 
-       prepare_irte(&irte, cfg->vector, dest);
+static int intel_ir_set_vcpu_affinity(struct irq_data *data, void *info)
+{
+       struct intel_ir_data *ir_data = data->chip_data;
+       struct vcpu_data *vcpu_pi_info = info;
 
-       /* Set source-id of interrupt request */
-       if (pdev)
-               set_msi_sid(&irte, pdev);
-       else
-               set_hpet_sid(&irte, hpet_id);
+       /* stop posting interrupts, back to remapping mode */
+       if (!vcpu_pi_info) {
+               modify_irte(&ir_data->irq_2_iommu, &ir_data->irte_entry);
+       } else {
+               struct irte irte_pi;
 
-       modify_irte(irq, &irte);
+               /*
+                * We are not caching the posted interrupt entry. We
+                * copy the data from the remapped entry and modify
+                * the fields which are relevant for posted mode. The
+                * cached remapped entry is used for switching back to
+                * remapped mode.
+                */
+               memset(&irte_pi, 0, sizeof(irte_pi));
+               dmar_copy_shared_irte(&irte_pi, &ir_data->irte_entry);
+
+               /* Update the posted mode fields */
+               irte_pi.p_pst = 1;
+               irte_pi.p_urgent = 0;
+               irte_pi.p_vector = vcpu_pi_info->vector;
+               irte_pi.pda_l = (vcpu_pi_info->pi_desc_addr >>
+                               (32 - PDA_LOW_BIT)) & ~(-1UL << PDA_LOW_BIT);
+               irte_pi.pda_h = (vcpu_pi_info->pi_desc_addr >> 32) &
+                               ~(-1UL << PDA_HIGH_BIT);
+
+               modify_irte(&ir_data->irq_2_iommu, &irte_pi);
+       }
 
-       msg->address_hi = MSI_ADDR_BASE_HI;
-       msg->data = sub_handle;
-       msg->address_lo = MSI_ADDR_BASE_LO | MSI_ADDR_IR_EXT_INT |
-                         MSI_ADDR_IR_SHV |
-                         MSI_ADDR_IR_INDEX1(ir_index) |
-                         MSI_ADDR_IR_INDEX2(ir_index);
+       return 0;
 }
 
-/*
- * Map the PCI dev to the corresponding remapping hardware unit
- * and allocate 'nvec' consecutive interrupt-remapping table entries
- * in it.
- */
-static int intel_msi_alloc_irq(struct pci_dev *dev, int irq, int nvec)
+static struct irq_chip intel_ir_chip = {
+       .irq_ack = ir_ack_apic_edge,
+       .irq_set_affinity = intel_ir_set_affinity,
+       .irq_compose_msi_msg = intel_ir_compose_msi_msg,
+       .irq_set_vcpu_affinity = intel_ir_set_vcpu_affinity,
+};
+
+static void intel_irq_remapping_prepare_irte(struct intel_ir_data *data,
+                                            struct irq_cfg *irq_cfg,
+                                            struct irq_alloc_info *info,
+                                            int index, int sub_handle)
 {
-       struct intel_iommu *iommu;
-       int index;
+       struct IR_IO_APIC_route_entry *entry;
+       struct irte *irte = &data->irte_entry;
+       struct msi_msg *msg = &data->msi_entry;
+
+       prepare_irte(irte, irq_cfg->vector, irq_cfg->dest_apicid);
+       switch (info->type) {
+       case X86_IRQ_ALLOC_TYPE_IOAPIC:
+               /* Set source-id of interrupt request */
+               set_ioapic_sid(irte, info->ioapic_id);
+               apic_printk(APIC_VERBOSE, KERN_DEBUG "IOAPIC[%d]: Set IRTE entry (P:%d FPD:%d Dst_Mode:%d Redir_hint:%d Trig_Mode:%d Dlvry_Mode:%X Avail:%X Vector:%02X Dest:%08X SID:%04X SQ:%X SVT:%X)\n",
+                       info->ioapic_id, irte->present, irte->fpd,
+                       irte->dst_mode, irte->redir_hint,
+                       irte->trigger_mode, irte->dlvry_mode,
+                       irte->avail, irte->vector, irte->dest_id,
+                       irte->sid, irte->sq, irte->svt);
+
+               entry = (struct IR_IO_APIC_route_entry *)info->ioapic_entry;
+               info->ioapic_entry = NULL;
+               memset(entry, 0, sizeof(*entry));
+               entry->index2   = (index >> 15) & 0x1;
+               entry->zero     = 0;
+               entry->format   = 1;
+               entry->index    = (index & 0x7fff);
+               /*
+                * IO-APIC RTE will be configured with virtual vector.
+                * irq handler will do the explicit EOI to the io-apic.
+                */
+               entry->vector   = info->ioapic_pin;
+               entry->mask     = 0;                    /* enable IRQ */
+               entry->trigger  = info->ioapic_trigger;
+               entry->polarity = info->ioapic_polarity;
+               if (info->ioapic_trigger)
+                       entry->mask = 1; /* Mask level triggered irqs. */
+               break;
+
+       case X86_IRQ_ALLOC_TYPE_HPET:
+       case X86_IRQ_ALLOC_TYPE_MSI:
+       case X86_IRQ_ALLOC_TYPE_MSIX:
+               if (info->type == X86_IRQ_ALLOC_TYPE_HPET)
+                       set_hpet_sid(irte, info->hpet_id);
+               else
+                       set_msi_sid(irte, info->msi_dev);
+
+               msg->address_hi = MSI_ADDR_BASE_HI;
+               msg->data = sub_handle;
+               msg->address_lo = MSI_ADDR_BASE_LO | MSI_ADDR_IR_EXT_INT |
+                                 MSI_ADDR_IR_SHV |
+                                 MSI_ADDR_IR_INDEX1(index) |
+                                 MSI_ADDR_IR_INDEX2(index);
+               break;
+
+       default:
+               BUG_ON(1);
+               break;
+       }
+}
 
-       down_read(&dmar_global_lock);
-       iommu = map_dev_to_ir(dev);
-       if (!iommu) {
-               printk(KERN_ERR
-                      "Unable to map PCI %s to iommu\n", pci_name(dev));
-               index = -ENOENT;
-       } else {
-               index = alloc_irte(iommu, irq, nvec);
-               if (index < 0) {
-                       printk(KERN_ERR
-                              "Unable to allocate %d IRTE for PCI %s\n",
-                              nvec, pci_name(dev));
-                       index = -ENOSPC;
+static void intel_free_irq_resources(struct irq_domain *domain,
+                                    unsigned int virq, unsigned int nr_irqs)
+{
+       struct irq_data *irq_data;
+       struct intel_ir_data *data;
+       struct irq_2_iommu *irq_iommu;
+       unsigned long flags;
+       int i;
+       for (i = 0; i < nr_irqs; i++) {
+               irq_data = irq_domain_get_irq_data(domain, virq  + i);
+               if (irq_data && irq_data->chip_data) {
+                       data = irq_data->chip_data;
+                       irq_iommu = &data->irq_2_iommu;
+                       raw_spin_lock_irqsave(&irq_2_ir_lock, flags);
+                       clear_entries(irq_iommu);
+                       raw_spin_unlock_irqrestore(&irq_2_ir_lock, flags);
+                       irq_domain_reset_irq_data(irq_data);
+                       kfree(data);
                }
        }
-       up_read(&dmar_global_lock);
-
-       return index;
 }
 
-static int intel_msi_setup_irq(struct pci_dev *pdev, unsigned int irq,
-                              int index, int sub_handle)
+static int intel_irq_remapping_alloc(struct irq_domain *domain,
+                                    unsigned int virq, unsigned int nr_irqs,
+                                    void *arg)
 {
-       struct intel_iommu *iommu;
-       int ret = -ENOENT;
+       struct intel_iommu *iommu = domain->host_data;
+       struct irq_alloc_info *info = arg;
+       struct intel_ir_data *data, *ird;
+       struct irq_data *irq_data;
+       struct irq_cfg *irq_cfg;
+       int i, ret, index;
+
+       if (!info || !iommu)
+               return -EINVAL;
+       if (nr_irqs > 1 && info->type != X86_IRQ_ALLOC_TYPE_MSI &&
+           info->type != X86_IRQ_ALLOC_TYPE_MSIX)
+               return -EINVAL;
+
+       /*
+        * With IRQ remapping enabled, don't need contiguous CPU vectors
+        * to support multiple MSI interrupts.
+        */
+       if (info->type == X86_IRQ_ALLOC_TYPE_MSI)
+               info->flags &= ~X86_IRQ_ALLOC_CONTIGUOUS_VECTORS;
+
+       ret = irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, arg);
+       if (ret < 0)
+               return ret;
+
+       ret = -ENOMEM;
+       data = kzalloc(sizeof(*data), GFP_KERNEL);
+       if (!data)
+               goto out_free_parent;
 
        down_read(&dmar_global_lock);
-       iommu = map_dev_to_ir(pdev);
-       if (iommu) {
-               /*
-                * setup the mapping between the irq and the IRTE
-                * base index, the sub_handle pointing to the
-                * appropriate interrupt remap table entry.
-                */
-               set_irte_irq(irq, iommu, index, sub_handle);
-               ret = 0;
-       }
+       index = alloc_irte(iommu, virq, &data->irq_2_iommu, nr_irqs);
        up_read(&dmar_global_lock);
+       if (index < 0) {
+               pr_warn("Failed to allocate IRTE\n");
+               kfree(data);
+               goto out_free_parent;
+       }
+
+       for (i = 0; i < nr_irqs; i++) {
+               irq_data = irq_domain_get_irq_data(domain, virq + i);
+               irq_cfg = irqd_cfg(irq_data);
+               if (!irq_data || !irq_cfg) {
+                       ret = -EINVAL;
+                       goto out_free_data;
+               }
+
+               if (i > 0) {
+                       ird = kzalloc(sizeof(*ird), GFP_KERNEL);
+                       if (!ird)
+                               goto out_free_data;
+                       /* Initialize the common data */
+                       ird->irq_2_iommu = data->irq_2_iommu;
+                       ird->irq_2_iommu.sub_handle = i;
+               } else {
+                       ird = data;
+               }
 
+               irq_data->hwirq = (index << 16) + i;
+               irq_data->chip_data = ird;
+               irq_data->chip = &intel_ir_chip;
+               intel_irq_remapping_prepare_irte(ird, irq_cfg, info, index, i);
+               irq_set_status_flags(virq + i, IRQ_MOVE_PCNTXT);
+       }
+       return 0;
+
+out_free_data:
+       intel_free_irq_resources(domain, virq, i);
+out_free_parent:
+       irq_domain_free_irqs_common(domain, virq, nr_irqs);
        return ret;
 }
 
-static int intel_alloc_hpet_msi(unsigned int irq, unsigned int id)
+static void intel_irq_remapping_free(struct irq_domain *domain,
+                                    unsigned int virq, unsigned int nr_irqs)
 {
-       int ret = -1;
-       struct intel_iommu *iommu;
-       int index;
+       intel_free_irq_resources(domain, virq, nr_irqs);
+       irq_domain_free_irqs_common(domain, virq, nr_irqs);
+}
 
-       down_read(&dmar_global_lock);
-       iommu = map_hpet_to_ir(id);
-       if (iommu) {
-               index = alloc_irte(iommu, irq, 1);
-               if (index >= 0)
-                       ret = 0;
-       }
-       up_read(&dmar_global_lock);
+static void intel_irq_remapping_activate(struct irq_domain *domain,
+                                        struct irq_data *irq_data)
+{
+       struct intel_ir_data *data = irq_data->chip_data;
 
-       return ret;
+       modify_irte(&data->irq_2_iommu, &data->irte_entry);
 }
 
-struct irq_remap_ops intel_irq_remap_ops = {
-       .prepare                = intel_prepare_irq_remapping,
-       .enable                 = intel_enable_irq_remapping,
-       .disable                = disable_irq_remapping,
-       .reenable               = reenable_irq_remapping,
-       .enable_faulting        = enable_drhd_fault_handling,
-       .setup_ioapic_entry     = intel_setup_ioapic_entry,
-       .set_affinity           = intel_ioapic_set_affinity,
-       .free_irq               = free_irte,
-       .compose_msi_msg        = intel_compose_msi_msg,
-       .msi_alloc_irq          = intel_msi_alloc_irq,
-       .msi_setup_irq          = intel_msi_setup_irq,
-       .alloc_hpet_msi         = intel_alloc_hpet_msi,
+static void intel_irq_remapping_deactivate(struct irq_domain *domain,
+                                          struct irq_data *irq_data)
+{
+       struct intel_ir_data *data = irq_data->chip_data;
+       struct irte entry;
+
+       memset(&entry, 0, sizeof(entry));
+       modify_irte(&data->irq_2_iommu, &entry);
+}
+
+static struct irq_domain_ops intel_ir_domain_ops = {
+       .alloc = intel_irq_remapping_alloc,
+       .free = intel_irq_remapping_free,
+       .activate = intel_irq_remapping_activate,
+       .deactivate = intel_irq_remapping_deactivate,
 };
 
 /*
@@ -1242,28 +1397,12 @@ static int dmar_ir_add(struct dmar_drhd_unit *dmaru, struct intel_iommu *iommu)
        /* Setup Interrupt-remapping now. */
        ret = intel_setup_irq_remapping(iommu);
        if (ret) {
-               pr_err("DRHD %Lx: failed to allocate resource\n",
-                      iommu->reg_phys);
-               ir_remove_ioapic_hpet_scope(iommu);
-               return ret;
-       }
-
-       if (!iommu->qi) {
-               /* Clear previous faults. */
-               dmar_fault(-1, iommu);
-               iommu_disable_irq_remapping(iommu);
-               dmar_disable_qi(iommu);
-       }
-
-       /* Enable queued invalidation */
-       ret = dmar_enable_qi(iommu);
-       if (!ret) {
-               iommu_set_irq_remapping(iommu, eim);
-       } else {
-               pr_err("DRHD %Lx: failed to enable queued invalidation, ecap %Lx, ret %d\n",
-                      iommu->reg_phys, iommu->ecap, ret);
+               pr_err("Failed to setup irq remapping for %s\n",
+                      iommu->name);
                intel_teardown_irq_remapping(iommu);
                ir_remove_ioapic_hpet_scope(iommu);
+       } else {
+               iommu_enable_irq_remapping(iommu);
        }
 
        return ret;
@@ -1280,6 +1419,9 @@ int dmar_ir_hotplug(struct dmar_drhd_unit *dmaru, bool insert)
                return -EINVAL;
        if (!ecap_ir_support(iommu->ecap))
                return 0;
+       if (irq_remapping_cap(IRQ_POSTING_CAP) &&
+           !cap_pi_support(iommu->cap))
+               return -EBUSY;
 
        if (insert) {
                if (!iommu->ir_table)
index d4f527e5667936454bfb49e01db931703b180e73..49e7542510d15caac5622cdb01fdcf8b77bb80e8 100644 (file)
@@ -16,7 +16,7 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  */
 
-#define pr_fmt(fmt)    "%s: " fmt, __func__
+#define pr_fmt(fmt)    "iommu: " fmt
 
 #include <linux/device.h>
 #include <linux/kernel.h>
@@ -51,6 +51,8 @@ struct iommu_group {
        void (*iommu_data_release)(void *iommu_data);
        char *name;
        int id;
+       struct iommu_domain *default_domain;
+       struct iommu_domain *domain;
 };
 
 struct iommu_device {
@@ -75,6 +77,15 @@ struct iommu_group_attribute iommu_group_attr_##_name =              \
 #define to_iommu_group(_kobj)          \
        container_of(_kobj, struct iommu_group, kobj)
 
+static struct iommu_domain *__iommu_domain_alloc(struct bus_type *bus,
+                                                unsigned type);
+static int __iommu_attach_device(struct iommu_domain *domain,
+                                struct device *dev);
+static int __iommu_attach_group(struct iommu_domain *domain,
+                               struct iommu_group *group);
+static void __iommu_detach_group(struct iommu_domain *domain,
+                                struct iommu_group *group);
+
 static ssize_t iommu_group_attr_show(struct kobject *kobj,
                                     struct attribute *__attr, char *buf)
 {
@@ -128,6 +139,8 @@ static void iommu_group_release(struct kobject *kobj)
 {
        struct iommu_group *group = to_iommu_group(kobj);
 
+       pr_debug("Releasing group %d\n", group->id);
+
        if (group->iommu_data_release)
                group->iommu_data_release(group->iommu_data);
 
@@ -135,6 +148,9 @@ static void iommu_group_release(struct kobject *kobj)
        ida_remove(&iommu_group_ida, group->id);
        mutex_unlock(&iommu_group_mutex);
 
+       if (group->default_domain)
+               iommu_domain_free(group->default_domain);
+
        kfree(group->name);
        kfree(group);
 }
@@ -207,6 +223,8 @@ again:
         */
        kobject_put(&group->kobj);
 
+       pr_debug("Allocated group %d\n", group->id);
+
        return group;
 }
 EXPORT_SYMBOL_GPL(iommu_group_alloc);
@@ -307,6 +325,52 @@ int iommu_group_set_name(struct iommu_group *group, const char *name)
 }
 EXPORT_SYMBOL_GPL(iommu_group_set_name);
 
+static int iommu_group_create_direct_mappings(struct iommu_group *group,
+                                             struct device *dev)
+{
+       struct iommu_domain *domain = group->default_domain;
+       struct iommu_dm_region *entry;
+       struct list_head mappings;
+       unsigned long pg_size;
+       int ret = 0;
+
+       if (!domain || domain->type != IOMMU_DOMAIN_DMA)
+               return 0;
+
+       BUG_ON(!domain->ops->pgsize_bitmap);
+
+       pg_size = 1UL << __ffs(domain->ops->pgsize_bitmap);
+       INIT_LIST_HEAD(&mappings);
+
+       iommu_get_dm_regions(dev, &mappings);
+
+       /* We need to consider overlapping regions for different devices */
+       list_for_each_entry(entry, &mappings, list) {
+               dma_addr_t start, end, addr;
+
+               start = ALIGN(entry->start, pg_size);
+               end   = ALIGN(entry->start + entry->length, pg_size);
+
+               for (addr = start; addr < end; addr += pg_size) {
+                       phys_addr_t phys_addr;
+
+                       phys_addr = iommu_iova_to_phys(domain, addr);
+                       if (phys_addr)
+                               continue;
+
+                       ret = iommu_map(domain, addr, addr, pg_size, entry->prot);
+                       if (ret)
+                               goto out;
+               }
+
+       }
+
+out:
+       iommu_put_dm_regions(dev, &mappings);
+
+       return ret;
+}
+
 /**
  * iommu_group_add_device - add a device to an iommu group
  * @group: the group into which to add the device (reference should be held)
@@ -363,8 +427,12 @@ rename:
 
        dev->iommu_group = group;
 
+       iommu_group_create_direct_mappings(group, dev);
+
        mutex_lock(&group->mutex);
        list_add_tail(&device->list, &group->devices);
+       if (group->domain)
+               __iommu_attach_device(group->domain, dev);
        mutex_unlock(&group->mutex);
 
        /* Notify any listeners about change to group. */
@@ -372,6 +440,9 @@ rename:
                                     IOMMU_GROUP_NOTIFY_ADD_DEVICE, dev);
 
        trace_add_device_to_group(group->id, dev);
+
+       pr_info("Adding device %s to group %d\n", dev_name(dev), group->id);
+
        return 0;
 }
 EXPORT_SYMBOL_GPL(iommu_group_add_device);
@@ -388,6 +459,8 @@ void iommu_group_remove_device(struct device *dev)
        struct iommu_group *group = dev->iommu_group;
        struct iommu_device *tmp_device, *device = NULL;
 
+       pr_info("Removing device %s from group %d\n", dev_name(dev), group->id);
+
        /* Pre-notify listeners that a device is being removed. */
        blocking_notifier_call_chain(&group->notifier,
                                     IOMMU_GROUP_NOTIFY_DEL_DEVICE, dev);
@@ -417,6 +490,17 @@ void iommu_group_remove_device(struct device *dev)
 }
 EXPORT_SYMBOL_GPL(iommu_group_remove_device);
 
+static int iommu_group_device_count(struct iommu_group *group)
+{
+       struct iommu_device *entry;
+       int ret = 0;
+
+       list_for_each_entry(entry, &group->devices, list)
+               ret++;
+
+       return ret;
+}
+
 /**
  * iommu_group_for_each_dev - iterate over each device in the group
  * @group: the group
@@ -428,19 +512,30 @@ EXPORT_SYMBOL_GPL(iommu_group_remove_device);
  * The group->mutex is held across callbacks, which will block calls to
  * iommu_group_add/remove_device.
  */
-int iommu_group_for_each_dev(struct iommu_group *group, void *data,
-                            int (*fn)(struct device *, void *))
+static int __iommu_group_for_each_dev(struct iommu_group *group, void *data,
+                                     int (*fn)(struct device *, void *))
 {
        struct iommu_device *device;
        int ret = 0;
 
-       mutex_lock(&group->mutex);
        list_for_each_entry(device, &group->devices, list) {
                ret = fn(device->dev, data);
                if (ret)
                        break;
        }
+       return ret;
+}
+
+
+int iommu_group_for_each_dev(struct iommu_group *group, void *data,
+                            int (*fn)(struct device *, void *))
+{
+       int ret;
+
+       mutex_lock(&group->mutex);
+       ret = __iommu_group_for_each_dev(group, data, fn);
        mutex_unlock(&group->mutex);
+
        return ret;
 }
 EXPORT_SYMBOL_GPL(iommu_group_for_each_dev);
@@ -692,7 +787,19 @@ static struct iommu_group *iommu_group_get_for_pci_dev(struct pci_dev *pdev)
                return group;
 
        /* No shared group found, allocate new */
-       return iommu_group_alloc();
+       group = iommu_group_alloc();
+       if (IS_ERR(group))
+               return NULL;
+
+       /*
+        * Try to allocate a default domain - needs support from the
+        * IOMMU driver.
+        */
+       group->default_domain = __iommu_domain_alloc(pdev->dev.bus,
+                                                    IOMMU_DOMAIN_DMA);
+       group->domain = group->default_domain;
+
+       return group;
 }
 
 /**
@@ -731,6 +838,11 @@ struct iommu_group *iommu_group_get_for_dev(struct device *dev)
        return group;
 }
 
+struct iommu_domain *iommu_group_default_domain(struct iommu_group *group)
+{
+       return group->default_domain;
+}
+
 static int add_iommu_group(struct device *dev, void *data)
 {
        struct iommu_callback_data *cb = data;
@@ -741,7 +853,16 @@ static int add_iommu_group(struct device *dev, void *data)
 
        WARN_ON(dev->iommu_group);
 
-       ops->add_device(dev);
+       return ops->add_device(dev);
+}
+
+static int remove_iommu_group(struct device *dev, void *data)
+{
+       struct iommu_callback_data *cb = data;
+       const struct iommu_ops *ops = cb->ops;
+
+       if (ops->remove_device && dev->iommu_group)
+               ops->remove_device(dev);
 
        return 0;
 }
@@ -761,7 +882,7 @@ static int iommu_bus_notifier(struct notifier_block *nb,
        if (action == BUS_NOTIFY_ADD_DEVICE) {
                if (ops->add_device)
                        return ops->add_device(dev);
-       } else if (action == BUS_NOTIFY_DEL_DEVICE) {
+       } else if (action == BUS_NOTIFY_REMOVED_DEVICE) {
                if (ops->remove_device && dev->iommu_group) {
                        ops->remove_device(dev);
                        return 0;
@@ -814,19 +935,25 @@ static int iommu_bus_init(struct bus_type *bus, const struct iommu_ops *ops)
        nb->notifier_call = iommu_bus_notifier;
 
        err = bus_register_notifier(bus, nb);
-       if (err) {
-               kfree(nb);
-               return err;
-       }
+       if (err)
+               goto out_free;
 
        err = bus_for_each_dev(bus, NULL, &cb, add_iommu_group);
-       if (err) {
-               bus_unregister_notifier(bus, nb);
-               kfree(nb);
-               return err;
-       }
+       if (err)
+               goto out_err;
+
 
        return 0;
+
+out_err:
+       /* Clean up */
+       bus_for_each_dev(bus, NULL, &cb, remove_iommu_group);
+       bus_unregister_notifier(bus, nb);
+
+out_free:
+       kfree(nb);
+
+       return err;
 }
 
 /**
@@ -898,22 +1025,28 @@ void iommu_set_fault_handler(struct iommu_domain *domain,
 }
 EXPORT_SYMBOL_GPL(iommu_set_fault_handler);
 
-struct iommu_domain *iommu_domain_alloc(struct bus_type *bus)
+static struct iommu_domain *__iommu_domain_alloc(struct bus_type *bus,
+                                                unsigned type)
 {
        struct iommu_domain *domain;
 
        if (bus == NULL || bus->iommu_ops == NULL)
                return NULL;
 
-       domain = bus->iommu_ops->domain_alloc(IOMMU_DOMAIN_UNMANAGED);
+       domain = bus->iommu_ops->domain_alloc(type);
        if (!domain)
                return NULL;
 
        domain->ops  = bus->iommu_ops;
-       domain->type = IOMMU_DOMAIN_UNMANAGED;
+       domain->type = type;
 
        return domain;
 }
+
+struct iommu_domain *iommu_domain_alloc(struct bus_type *bus)
+{
+       return __iommu_domain_alloc(bus, IOMMU_DOMAIN_UNMANAGED);
+}
 EXPORT_SYMBOL_GPL(iommu_domain_alloc);
 
 void iommu_domain_free(struct iommu_domain *domain)
@@ -922,7 +1055,8 @@ void iommu_domain_free(struct iommu_domain *domain)
 }
 EXPORT_SYMBOL_GPL(iommu_domain_free);
 
-int iommu_attach_device(struct iommu_domain *domain, struct device *dev)
+static int __iommu_attach_device(struct iommu_domain *domain,
+                                struct device *dev)
 {
        int ret;
        if (unlikely(domain->ops->attach_dev == NULL))
@@ -933,9 +1067,38 @@ int iommu_attach_device(struct iommu_domain *domain, struct device *dev)
                trace_attach_device_to_domain(dev);
        return ret;
 }
+
+int iommu_attach_device(struct iommu_domain *domain, struct device *dev)
+{
+       struct iommu_group *group;
+       int ret;
+
+       group = iommu_group_get(dev);
+       /* FIXME: Remove this when groups a mandatory for iommu drivers */
+       if (group == NULL)
+               return __iommu_attach_device(domain, dev);
+
+       /*
+        * We have a group - lock it to make sure the device-count doesn't
+        * change while we are attaching
+        */
+       mutex_lock(&group->mutex);
+       ret = -EINVAL;
+       if (iommu_group_device_count(group) != 1)
+               goto out_unlock;
+
+       ret = __iommu_attach_group(domain, group);
+
+out_unlock:
+       mutex_unlock(&group->mutex);
+       iommu_group_put(group);
+
+       return ret;
+}
 EXPORT_SYMBOL_GPL(iommu_attach_device);
 
-void iommu_detach_device(struct iommu_domain *domain, struct device *dev)
+static void __iommu_detach_device(struct iommu_domain *domain,
+                                 struct device *dev)
 {
        if (unlikely(domain->ops->detach_dev == NULL))
                return;
@@ -943,8 +1106,48 @@ void iommu_detach_device(struct iommu_domain *domain, struct device *dev)
        domain->ops->detach_dev(domain, dev);
        trace_detach_device_from_domain(dev);
 }
+
+void iommu_detach_device(struct iommu_domain *domain, struct device *dev)
+{
+       struct iommu_group *group;
+
+       group = iommu_group_get(dev);
+       /* FIXME: Remove this when groups a mandatory for iommu drivers */
+       if (group == NULL)
+               return __iommu_detach_device(domain, dev);
+
+       mutex_lock(&group->mutex);
+       if (iommu_group_device_count(group) != 1) {
+               WARN_ON(1);
+               goto out_unlock;
+       }
+
+       __iommu_detach_group(domain, group);
+
+out_unlock:
+       mutex_unlock(&group->mutex);
+       iommu_group_put(group);
+}
 EXPORT_SYMBOL_GPL(iommu_detach_device);
 
+struct iommu_domain *iommu_get_domain_for_dev(struct device *dev)
+{
+       struct iommu_domain *domain;
+       struct iommu_group *group;
+
+       group = iommu_group_get(dev);
+       /* FIXME: Remove this when groups a mandatory for iommu drivers */
+       if (group == NULL)
+               return NULL;
+
+       domain = group->domain;
+
+       iommu_group_put(group);
+
+       return domain;
+}
+EXPORT_SYMBOL_GPL(iommu_get_domain_for_dev);
+
 /*
  * IOMMU groups are really the natrual working unit of the IOMMU, but
  * the IOMMU API works on domains and devices.  Bridge that gap by
@@ -959,13 +1162,34 @@ static int iommu_group_do_attach_device(struct device *dev, void *data)
 {
        struct iommu_domain *domain = data;
 
-       return iommu_attach_device(domain, dev);
+       return __iommu_attach_device(domain, dev);
+}
+
+static int __iommu_attach_group(struct iommu_domain *domain,
+                               struct iommu_group *group)
+{
+       int ret;
+
+       if (group->default_domain && group->domain != group->default_domain)
+               return -EBUSY;
+
+       ret = __iommu_group_for_each_dev(group, domain,
+                                        iommu_group_do_attach_device);
+       if (ret == 0)
+               group->domain = domain;
+
+       return ret;
 }
 
 int iommu_attach_group(struct iommu_domain *domain, struct iommu_group *group)
 {
-       return iommu_group_for_each_dev(group, domain,
-                                       iommu_group_do_attach_device);
+       int ret;
+
+       mutex_lock(&group->mutex);
+       ret = __iommu_attach_group(domain, group);
+       mutex_unlock(&group->mutex);
+
+       return ret;
 }
 EXPORT_SYMBOL_GPL(iommu_attach_group);
 
@@ -973,14 +1197,40 @@ static int iommu_group_do_detach_device(struct device *dev, void *data)
 {
        struct iommu_domain *domain = data;
 
-       iommu_detach_device(domain, dev);
+       __iommu_detach_device(domain, dev);
 
        return 0;
 }
 
+static void __iommu_detach_group(struct iommu_domain *domain,
+                                struct iommu_group *group)
+{
+       int ret;
+
+       if (!group->default_domain) {
+               __iommu_group_for_each_dev(group, domain,
+                                          iommu_group_do_detach_device);
+               group->domain = NULL;
+               return;
+       }
+
+       if (group->domain == group->default_domain)
+               return;
+
+       /* Detach by re-attaching to the default domain */
+       ret = __iommu_group_for_each_dev(group, group->default_domain,
+                                        iommu_group_do_attach_device);
+       if (ret != 0)
+               WARN_ON(1);
+       else
+               group->domain = group->default_domain;
+}
+
 void iommu_detach_group(struct iommu_domain *domain, struct iommu_group *group)
 {
-       iommu_group_for_each_dev(group, domain, iommu_group_do_detach_device);
+       mutex_lock(&group->mutex);
+       __iommu_detach_group(domain, group);
+       mutex_unlock(&group->mutex);
 }
 EXPORT_SYMBOL_GPL(iommu_detach_group);
 
@@ -1207,7 +1457,7 @@ static int __init iommu_init(void)
 
        return 0;
 }
-arch_initcall(iommu_init);
+core_initcall(iommu_init);
 
 int iommu_domain_get_attr(struct iommu_domain *domain,
                          enum iommu_attr attr, void *data)
@@ -1273,3 +1523,72 @@ int iommu_domain_set_attr(struct iommu_domain *domain,
        return ret;
 }
 EXPORT_SYMBOL_GPL(iommu_domain_set_attr);
+
+void iommu_get_dm_regions(struct device *dev, struct list_head *list)
+{
+       const struct iommu_ops *ops = dev->bus->iommu_ops;
+
+       if (ops && ops->get_dm_regions)
+               ops->get_dm_regions(dev, list);
+}
+
+void iommu_put_dm_regions(struct device *dev, struct list_head *list)
+{
+       const struct iommu_ops *ops = dev->bus->iommu_ops;
+
+       if (ops && ops->put_dm_regions)
+               ops->put_dm_regions(dev, list);
+}
+
+/* Request that a device is direct mapped by the IOMMU */
+int iommu_request_dm_for_dev(struct device *dev)
+{
+       struct iommu_domain *dm_domain;
+       struct iommu_group *group;
+       int ret;
+
+       /* Device must already be in a group before calling this function */
+       group = iommu_group_get_for_dev(dev);
+       if (IS_ERR(group))
+               return PTR_ERR(group);
+
+       mutex_lock(&group->mutex);
+
+       /* Check if the default domain is already direct mapped */
+       ret = 0;
+       if (group->default_domain &&
+           group->default_domain->type == IOMMU_DOMAIN_IDENTITY)
+               goto out;
+
+       /* Don't change mappings of existing devices */
+       ret = -EBUSY;
+       if (iommu_group_device_count(group) != 1)
+               goto out;
+
+       /* Allocate a direct mapped domain */
+       ret = -ENOMEM;
+       dm_domain = __iommu_domain_alloc(dev->bus, IOMMU_DOMAIN_IDENTITY);
+       if (!dm_domain)
+               goto out;
+
+       /* Attach the device to the domain */
+       ret = __iommu_attach_group(dm_domain, group);
+       if (ret) {
+               iommu_domain_free(dm_domain);
+               goto out;
+       }
+
+       /* Make the direct mapped domain the default for this group */
+       if (group->default_domain)
+               iommu_domain_free(group->default_domain);
+       group->default_domain = dm_domain;
+
+       pr_info("Using direct mapping for device %s\n", dev_name(dev));
+
+       ret = 0;
+out:
+       mutex_unlock(&group->mutex);
+       iommu_group_put(group);
+
+       return ret;
+}
index 9dd8208312c2e75874ce70dcc6008a166eb5ffca..b7c3d923f3e1c0569c42492d435b7c4d9a321caa 100644 (file)
@@ -227,6 +227,7 @@ iova_insert_rbtree(struct rb_root *root, struct iova *iova)
        /* Figure out where to put new node */
        while (*new) {
                struct iova *this = container_of(*new, struct iova, node);
+
                parent = *new;
 
                if (iova->pfn_lo < this->pfn_lo)
@@ -350,6 +351,7 @@ void
 free_iova(struct iova_domain *iovad, unsigned long pfn)
 {
        struct iova *iova = find_iova(iovad, pfn);
+
        if (iova)
                __free_iova(iovad, iova);
 
@@ -369,6 +371,7 @@ void put_iova_domain(struct iova_domain *iovad)
        node = rb_first(&iovad->rbroot);
        while (node) {
                struct iova *iova = container_of(node, struct iova, node);
+
                rb_erase(node, &iovad->rbroot);
                free_iova_mem(iova);
                node = rb_first(&iovad->rbroot);
@@ -482,6 +485,7 @@ copy_reserved_iova(struct iova_domain *from, struct iova_domain *to)
        for (node = rb_first(&from->rbroot); node; node = rb_next(node)) {
                struct iova *iova = container_of(node, struct iova, node);
                struct iova *new_iova;
+
                new_iova = reserve_iova(to, iova->pfn_lo, iova->pfn_hi);
                if (!new_iova)
                        printk(KERN_ERR "Reserve iova range %lx@%lx failed\n",
index 390079ee13507747388f635bf67c1c44dfb6c068..2d9993062ded6b2d543c89a9c09c3e6a85dfb17f 100644 (file)
@@ -6,6 +6,7 @@
 #include <linux/msi.h>
 #include <linux/irq.h>
 #include <linux/pci.h>
+#include <linux/irqdomain.h>
 
 #include <asm/hw_irq.h>
 #include <asm/irq_remapping.h>
@@ -21,21 +22,11 @@ int irq_remap_broken;
 int disable_sourceid_checking;
 int no_x2apic_optout;
 
+int disable_irq_post = 1;
+
 static int disable_irq_remap;
 static struct irq_remap_ops *remap_ops;
 
-static int msi_alloc_remapped_irq(struct pci_dev *pdev, int irq, int nvec);
-static int msi_setup_remapped_irq(struct pci_dev *pdev, unsigned int irq,
-                                 int index, int sub_handle);
-static int set_remapped_irq_affinity(struct irq_data *data,
-                                    const struct cpumask *mask,
-                                    bool force);
-
-static bool irq_remapped(struct irq_cfg *cfg)
-{
-       return (cfg->remapped == 1);
-}
-
 static void irq_remapping_disable_io_apic(void)
 {
        /*
@@ -49,117 +40,9 @@ static void irq_remapping_disable_io_apic(void)
                disconnect_bsp_APIC(0);
 }
 
-static int do_setup_msi_irqs(struct pci_dev *dev, int nvec)
-{
-       int ret, sub_handle, nvec_pow2, index = 0;
-       unsigned int irq;
-       struct msi_desc *msidesc;
-
-       msidesc = list_entry(dev->msi_list.next, struct msi_desc, list);
-
-       irq = irq_alloc_hwirqs(nvec, dev_to_node(&dev->dev));
-       if (irq == 0)
-               return -ENOSPC;
-
-       nvec_pow2 = __roundup_pow_of_two(nvec);
-       for (sub_handle = 0; sub_handle < nvec; sub_handle++) {
-               if (!sub_handle) {
-                       index = msi_alloc_remapped_irq(dev, irq, nvec_pow2);
-                       if (index < 0) {
-                               ret = index;
-                               goto error;
-                       }
-               } else {
-                       ret = msi_setup_remapped_irq(dev, irq + sub_handle,
-                                                    index, sub_handle);
-                       if (ret < 0)
-                               goto error;
-               }
-               ret = setup_msi_irq(dev, msidesc, irq, sub_handle);
-               if (ret < 0)
-                       goto error;
-       }
-       return 0;
-
-error:
-       irq_free_hwirqs(irq, nvec);
-
-       /*
-        * Restore altered MSI descriptor fields and prevent just destroyed
-        * IRQs from tearing down again in default_teardown_msi_irqs()
-        */
-       msidesc->irq = 0;
-
-       return ret;
-}
-
-static int do_setup_msix_irqs(struct pci_dev *dev, int nvec)
-{
-       int node, ret, sub_handle, index = 0;
-       struct msi_desc *msidesc;
-       unsigned int irq;
-
-       node            = dev_to_node(&dev->dev);
-       sub_handle      = 0;
-
-       list_for_each_entry(msidesc, &dev->msi_list, list) {
-
-               irq = irq_alloc_hwirq(node);
-               if (irq == 0)
-                       return -1;
-
-               if (sub_handle == 0)
-                       ret = index = msi_alloc_remapped_irq(dev, irq, nvec);
-               else
-                       ret = msi_setup_remapped_irq(dev, irq, index, sub_handle);
-
-               if (ret < 0)
-                       goto error;
-
-               ret = setup_msi_irq(dev, msidesc, irq, 0);
-               if (ret < 0)
-                       goto error;
-
-               sub_handle += 1;
-               irq        += 1;
-       }
-
-       return 0;
-
-error:
-       irq_free_hwirq(irq);
-       return ret;
-}
-
-static int irq_remapping_setup_msi_irqs(struct pci_dev *dev,
-                                       int nvec, int type)
-{
-       if (type == PCI_CAP_ID_MSI)
-               return do_setup_msi_irqs(dev, nvec);
-       else
-               return do_setup_msix_irqs(dev, nvec);
-}
-
-static void eoi_ioapic_pin_remapped(int apic, int pin, int vector)
-{
-       /*
-        * Intr-remapping uses pin number as the virtual vector
-        * in the RTE. Actual vector is programmed in
-        * intr-remapping table entry. Hence for the io-apic
-        * EOI we use the pin number.
-        */
-       io_apic_eoi(apic, pin);
-}
-
 static void __init irq_remapping_modify_x86_ops(void)
 {
        x86_io_apic_ops.disable         = irq_remapping_disable_io_apic;
-       x86_io_apic_ops.set_affinity    = set_remapped_irq_affinity;
-       x86_io_apic_ops.setup_entry     = setup_ioapic_remapped_entry;
-       x86_io_apic_ops.eoi_ioapic_pin  = eoi_ioapic_pin_remapped;
-       x86_msi.setup_msi_irqs          = irq_remapping_setup_msi_irqs;
-       x86_msi.setup_hpet_msi          = setup_hpet_msi_remapped;
-       x86_msi.compose_msi_msg         = compose_remapped_msi_msg;
 }
 
 static __init int setup_nointremap(char *str)
@@ -198,6 +81,15 @@ void set_irq_remapping_broken(void)
        irq_remap_broken = 1;
 }
 
+bool irq_remapping_cap(enum irq_remap_cap cap)
+{
+       if (!remap_ops || disable_irq_post)
+               return 0;
+
+       return (remap_ops->capability & (1 << cap));
+}
+EXPORT_SYMBOL_GPL(irq_remapping_cap);
+
 int __init irq_remapping_prepare(void)
 {
        if (disable_irq_remap)
@@ -254,113 +146,48 @@ int __init irq_remap_enable_fault_handling(void)
        return remap_ops->enable_faulting();
 }
 
-int setup_ioapic_remapped_entry(int irq,
-                               struct IO_APIC_route_entry *entry,
-                               unsigned int destination, int vector,
-                               struct io_apic_irq_attr *attr)
-{
-       if (!remap_ops->setup_ioapic_entry)
-               return -ENODEV;
-
-       return remap_ops->setup_ioapic_entry(irq, entry, destination,
-                                            vector, attr);
-}
-
-static int set_remapped_irq_affinity(struct irq_data *data,
-                                    const struct cpumask *mask, bool force)
-{
-       if (!config_enabled(CONFIG_SMP) || !remap_ops->set_affinity)
-               return 0;
-
-       return remap_ops->set_affinity(data, mask, force);
-}
-
-void free_remapped_irq(int irq)
-{
-       struct irq_cfg *cfg = irq_cfg(irq);
-
-       if (irq_remapped(cfg) && remap_ops->free_irq)
-               remap_ops->free_irq(irq);
-}
-
-void compose_remapped_msi_msg(struct pci_dev *pdev,
-                             unsigned int irq, unsigned int dest,
-                             struct msi_msg *msg, u8 hpet_id)
-{
-       struct irq_cfg *cfg = irq_cfg(irq);
-
-       if (!irq_remapped(cfg))
-               native_compose_msi_msg(pdev, irq, dest, msg, hpet_id);
-       else if (remap_ops->compose_msi_msg)
-               remap_ops->compose_msi_msg(pdev, irq, dest, msg, hpet_id);
-}
-
-static int msi_alloc_remapped_irq(struct pci_dev *pdev, int irq, int nvec)
-{
-       if (!remap_ops->msi_alloc_irq)
-               return -ENODEV;
-
-       return remap_ops->msi_alloc_irq(pdev, irq, nvec);
-}
-
-static int msi_setup_remapped_irq(struct pci_dev *pdev, unsigned int irq,
-                                 int index, int sub_handle)
-{
-       if (!remap_ops->msi_setup_irq)
-               return -ENODEV;
-
-       return remap_ops->msi_setup_irq(pdev, irq, index, sub_handle);
-}
-
-int setup_hpet_msi_remapped(unsigned int irq, unsigned int id)
-{
-       int ret;
-
-       if (!remap_ops->alloc_hpet_msi)
-               return -ENODEV;
-
-       ret = remap_ops->alloc_hpet_msi(irq, id);
-       if (ret)
-               return -EINVAL;
-
-       return default_setup_hpet_msi(irq, id);
-}
-
 void panic_if_irq_remap(const char *msg)
 {
        if (irq_remapping_enabled)
                panic(msg);
 }
 
-static void ir_ack_apic_edge(struct irq_data *data)
+void ir_ack_apic_edge(struct irq_data *data)
 {
        ack_APIC_irq();
 }
 
-static void ir_ack_apic_level(struct irq_data *data)
+/**
+ * irq_remapping_get_ir_irq_domain - Get the irqdomain associated with the IOMMU
+ *                                  device serving request @info
+ * @info: interrupt allocation information, used to identify the IOMMU device
+ *
+ * It's used to get parent irqdomain for HPET and IOAPIC irqdomains.
+ * Returns pointer to IRQ domain, or NULL on failure.
+ */
+struct irq_domain *
+irq_remapping_get_ir_irq_domain(struct irq_alloc_info *info)
 {
-       ack_APIC_irq();
-       eoi_ioapic_irq(data->irq, irqd_cfg(data));
-}
+       if (!remap_ops || !remap_ops->get_ir_irq_domain)
+               return NULL;
 
-static void ir_print_prefix(struct irq_data *data, struct seq_file *p)
-{
-       seq_printf(p, " IR-%s", data->chip->name);
+       return remap_ops->get_ir_irq_domain(info);
 }
 
-void irq_remap_modify_chip_defaults(struct irq_chip *chip)
+/**
+ * irq_remapping_get_irq_domain - Get the irqdomain serving the request @info
+ * @info: interrupt allocation information, used to identify the IOMMU device
+ *
+ * There will be one PCI MSI/MSIX irqdomain associated with each interrupt
+ * remapping device, so this interface is used to retrieve the PCI MSI/MSIX
+ * irqdomain serving request @info.
+ * Returns pointer to IRQ domain, or NULL on failure.
+ */
+struct irq_domain *
+irq_remapping_get_irq_domain(struct irq_alloc_info *info)
 {
-       chip->irq_print_chip = ir_print_prefix;
-       chip->irq_ack = ir_ack_apic_edge;
-       chip->irq_eoi = ir_ack_apic_level;
-       chip->irq_set_affinity = x86_io_apic_ops.set_affinity;
-}
+       if (!remap_ops || !remap_ops->get_irq_domain)
+               return NULL;
 
-bool setup_remapped_irq(int irq, struct irq_cfg *cfg, struct irq_chip *chip)
-{
-       if (!irq_remapped(cfg))
-               return false;
-       irq_set_status_flags(irq, IRQ_MOVE_PCNTXT);
-       irq_remap_modify_chip_defaults(chip);
-       return true;
+       return remap_ops->get_irq_domain(info);
 }
index 7c70cc29ffe6b4d5341587465de8cf3c643a8956..039c7af7b190f9c3481e46bb81f34d4fcbecc099 100644 (file)
 
 #ifdef CONFIG_IRQ_REMAP
 
-struct IO_APIC_route_entry;
-struct io_apic_irq_attr;
 struct irq_data;
-struct cpumask;
-struct pci_dev;
 struct msi_msg;
+struct irq_domain;
+struct irq_alloc_info;
 
 extern int irq_remap_broken;
 extern int disable_sourceid_checking;
 extern int no_x2apic_optout;
 extern int irq_remapping_enabled;
 
+extern int disable_irq_post;
+
 struct irq_remap_ops {
+       /* The supported capabilities */
+       int capability;
+
        /* Initializes hardware and makes it ready for remapping interrupts */
        int  (*prepare)(void);
 
@@ -52,40 +55,23 @@ struct irq_remap_ops {
        /* Enable fault handling */
        int  (*enable_faulting)(void);
 
-       /* IO-APIC setup routine */
-       int (*setup_ioapic_entry)(int irq, struct IO_APIC_route_entry *,
-                                 unsigned int, int,
-                                 struct io_apic_irq_attr *);
-
-       /* Set the CPU affinity of a remapped interrupt */
-       int (*set_affinity)(struct irq_data *data, const struct cpumask *mask,
-                           bool force);
-
-       /* Free an IRQ */
-       int (*free_irq)(int);
+       /* Get the irqdomain associated the IOMMU device */
+       struct irq_domain *(*get_ir_irq_domain)(struct irq_alloc_info *);
 
-       /* Create MSI msg to use for interrupt remapping */
-       void (*compose_msi_msg)(struct pci_dev *,
-                               unsigned int, unsigned int,
-                               struct msi_msg *, u8);
-
-       /* Allocate remapping resources for MSI */
-       int (*msi_alloc_irq)(struct pci_dev *, int, int);
-
-       /* Setup the remapped MSI irq */
-       int (*msi_setup_irq)(struct pci_dev *, unsigned int, int, int);
-
-       /* Setup interrupt remapping for an HPET MSI */
-       int (*alloc_hpet_msi)(unsigned int, unsigned int);
+       /* Get the MSI irqdomain associated with the IOMMU device */
+       struct irq_domain *(*get_irq_domain)(struct irq_alloc_info *);
 };
 
 extern struct irq_remap_ops intel_irq_remap_ops;
 extern struct irq_remap_ops amd_iommu_irq_ops;
 
+extern void ir_ack_apic_edge(struct irq_data *data);
+
 #else  /* CONFIG_IRQ_REMAP */
 
 #define irq_remapping_enabled 0
 #define irq_remap_broken      0
+#define disable_irq_post      1
 
 #endif /* CONFIG_IRQ_REMAP */
 
index cab214544237cf6f89754c3878f0e57f66ba360d..ebf0adb8e7ea729f5cab436de29a8e18afd8d78c 100644 (file)
@@ -551,6 +551,15 @@ static void rk_iommu_zap_iova(struct rk_iommu_domain *rk_domain,
        spin_unlock_irqrestore(&rk_domain->iommus_lock, flags);
 }
 
+static void rk_iommu_zap_iova_first_last(struct rk_iommu_domain *rk_domain,
+                                        dma_addr_t iova, size_t size)
+{
+       rk_iommu_zap_iova(rk_domain, iova, SPAGE_SIZE);
+       if (size > SPAGE_SIZE)
+               rk_iommu_zap_iova(rk_domain, iova + size - SPAGE_SIZE,
+                                       SPAGE_SIZE);
+}
+
 static u32 *rk_dte_get_page_table(struct rk_iommu_domain *rk_domain,
                                  dma_addr_t iova)
 {
@@ -575,12 +584,6 @@ static u32 *rk_dte_get_page_table(struct rk_iommu_domain *rk_domain,
        rk_table_flush(page_table, NUM_PT_ENTRIES);
        rk_table_flush(dte_addr, 1);
 
-       /*
-        * Zap the first iova of newly allocated page table so iommu evicts
-        * old cached value of new dte from the iotlb.
-        */
-       rk_iommu_zap_iova(rk_domain, iova, SPAGE_SIZE);
-
 done:
        pt_phys = rk_dte_pt_address(dte);
        return (u32 *)phys_to_virt(pt_phys);
@@ -630,6 +633,14 @@ static int rk_iommu_map_iova(struct rk_iommu_domain *rk_domain, u32 *pte_addr,
 
        rk_table_flush(pte_addr, pte_count);
 
+       /*
+        * Zap the first and last iova to evict from iotlb any previously
+        * mapped cachelines holding stale values for its dte and pte.
+        * We only zap the first and last iova, since only they could have
+        * dte or pte shared with an existing mapping.
+        */
+       rk_iommu_zap_iova_first_last(rk_domain, iova, size);
+
        return 0;
 unwind:
        /* Unmap the range of iovas that we just mapped */
@@ -774,7 +785,7 @@ static int rk_iommu_attach_device(struct iommu_domain *domain,
        list_add_tail(&iommu->node, &rk_domain->iommus);
        spin_unlock_irqrestore(&rk_domain->iommus_lock, flags);
 
-       dev_info(dev, "Attached to iommu domain\n");
+       dev_dbg(dev, "Attached to iommu domain\n");
 
        rk_iommu_disable_stall(iommu);
 
@@ -808,7 +819,7 @@ static void rk_iommu_detach_device(struct iommu_domain *domain,
 
        iommu->domain = NULL;
 
-       dev_info(dev, "Detached from iommu domain\n");
+       dev_dbg(dev, "Detached from iommu domain\n");
 }
 
 static struct iommu_domain *rk_iommu_domain_alloc(unsigned type)
index 6de62a96e79c80e9d442ca07f53549cf334f8c7c..99b9a979297531e67960a75d35917e93c2223368 100644 (file)
@@ -30,6 +30,7 @@ config ARM_GIC_V3_ITS
 config ARM_NVIC
        bool
        select IRQ_DOMAIN
+       select IRQ_DOMAIN_HIERARCHY
        select GENERIC_IRQ_CHIP
 
 config ARM_VIC
index 5945223b73fa2ceba647649e6c16c84fc5447d08..5c82e3bdafdf0f61f054b7ea14144346a4390002 100644 (file)
@@ -13,6 +13,7 @@
 #include <linux/init.h>
 #include <linux/io.h>
 #include <linux/slab.h>
+#include <linux/syscore_ops.h>
 #include <linux/irqdomain.h>
 #include <linux/irqchip/chained_irq.h>
 #include <linux/interrupt.h>
@@ -34,9 +35,14 @@ struct combiner_chip_data {
        unsigned int irq_mask;
        void __iomem *base;
        unsigned int parent_irq;
+#ifdef CONFIG_PM
+       u32 pm_save;
+#endif
 };
 
+static struct combiner_chip_data *combiner_data;
 static struct irq_domain *combiner_irq_domain;
+static unsigned int max_nr = 20;
 
 static inline void __iomem *combiner_base(struct irq_data *data)
 {
@@ -164,18 +170,16 @@ static int combiner_irq_domain_map(struct irq_domain *d, unsigned int irq,
        return 0;
 }
 
-static struct irq_domain_ops combiner_irq_domain_ops = {
+static const struct irq_domain_ops combiner_irq_domain_ops = {
        .xlate  = combiner_irq_domain_xlate,
        .map    = combiner_irq_domain_map,
 };
 
 static void __init combiner_init(void __iomem *combiner_base,
-                                struct device_node *np,
-                                unsigned int max_nr)
+                                struct device_node *np)
 {
        int i, irq;
        unsigned int nr_irq;
-       struct combiner_chip_data *combiner_data;
 
        nr_irq = max_nr * IRQ_IN_COMBINER;
 
@@ -201,11 +205,59 @@ static void __init combiner_init(void __iomem *combiner_base,
        }
 }
 
+#ifdef CONFIG_PM
+
+/**
+ * combiner_suspend - save interrupt combiner state before suspend
+ *
+ * Save the interrupt enable set register for all combiner groups since
+ * the state is lost when the system enters into a sleep state.
+ *
+ */
+static int combiner_suspend(void)
+{
+       int i;
+
+       for (i = 0; i < max_nr; i++)
+               combiner_data[i].pm_save =
+                       __raw_readl(combiner_data[i].base + COMBINER_ENABLE_SET);
+
+       return 0;
+}
+
+/**
+ * combiner_resume - restore interrupt combiner state after resume
+ *
+ * Restore the interrupt enable set register for all combiner groups since
+ * the state is lost when the system enters into a sleep state on suspend.
+ *
+ */
+static void combiner_resume(void)
+{
+       int i;
+
+       for (i = 0; i < max_nr; i++) {
+               __raw_writel(combiner_data[i].irq_mask,
+                            combiner_data[i].base + COMBINER_ENABLE_CLEAR);
+               __raw_writel(combiner_data[i].pm_save,
+                            combiner_data[i].base + COMBINER_ENABLE_SET);
+       }
+}
+
+#else
+#define combiner_suspend       NULL
+#define combiner_resume                NULL
+#endif
+
+static struct syscore_ops combiner_syscore_ops = {
+       .suspend        = combiner_suspend,
+       .resume         = combiner_resume,
+};
+
 static int __init combiner_of_init(struct device_node *np,
                                   struct device_node *parent)
 {
        void __iomem *combiner_base;
-       unsigned int max_nr = 20;
 
        combiner_base = of_iomap(np, 0);
        if (!combiner_base) {
@@ -219,7 +271,9 @@ static int __init combiner_of_init(struct device_node *np,
                        __func__, max_nr);
        }
 
-       combiner_init(combiner_base, np, max_nr);
+       combiner_init(combiner_base, np);
+
+       register_syscore_ops(&combiner_syscore_ops);
 
        return 0;
 }
index daccc8bdbb423fd5e6b700b4ffe6b57be0af30a1..0d3b0fe2f175e89912db13bc38e6d7235d60d835 100644 (file)
@@ -409,7 +409,7 @@ static struct notifier_block mpic_cascaded_cpu_notifier = {
 };
 #endif /* CONFIG_SMP */
 
-static struct irq_domain_ops armada_370_xp_mpic_irq_ops = {
+static const struct irq_domain_ops armada_370_xp_mpic_irq_ops = {
        .map = armada_370_xp_mpic_irq_map,
        .xlate = irq_domain_xlate_onecell,
 };
index a2e8c3f876cbd248ff084e755cd96a2da91729af..459bf4429d365794d4f84c19116cb0d57755c646 100644 (file)
@@ -339,6 +339,15 @@ static int __init aic5_of_init(struct device_node *node,
        return 0;
 }
 
+#define NR_SAMA5D2_IRQS                77
+
+static int __init sama5d2_aic5_of_init(struct device_node *node,
+                                      struct device_node *parent)
+{
+       return aic5_of_init(node, parent, NR_SAMA5D2_IRQS);
+}
+IRQCHIP_DECLARE(sama5d2_aic5, "atmel,sama5d2-aic", sama5d2_aic5_of_init);
+
 #define NR_SAMA5D3_IRQS                48
 
 static int __init sama5d3_aic5_of_init(struct device_node *node,
index 5916d6cdafa1c9b8b1909ce0aa77027e2ce2759f..e68c3b60a681ba460b24dd17499de544ea4a35fc 100644 (file)
@@ -135,7 +135,7 @@ static int armctrl_xlate(struct irq_domain *d, struct device_node *ctrlr,
        return 0;
 }
 
-static struct irq_domain_ops armctrl_ops = {
+static const struct irq_domain_ops armctrl_ops = {
        .xlate = armctrl_xlate
 };
 
index ad96ebb0c7abd882075f67162ff605b8969a792b..9448e391cb710363d18df4c3079ae0504b0cbd44 100644 (file)
 int gic_configure_irq(unsigned int irq, unsigned int type,
                       void __iomem *base, void (*sync_access)(void))
 {
-       u32 enablemask = 1 << (irq % 32);
-       u32 enableoff = (irq / 32) * 4;
        u32 confmask = 0x2 << ((irq % 16) * 2);
        u32 confoff = (irq / 16) * 4;
-       bool enabled = false;
        u32 val, oldval;
        int ret = 0;
 
@@ -42,17 +39,6 @@ int gic_configure_irq(unsigned int irq, unsigned int type,
        else if (type & IRQ_TYPE_EDGE_BOTH)
                val |= confmask;
 
-       /*
-        * As recommended by the spec, disable the interrupt before changing
-        * the configuration
-        */
-       if (readl_relaxed(base + GIC_DIST_ENABLE_SET + enableoff) & enablemask) {
-               writel_relaxed(enablemask, base + GIC_DIST_ENABLE_CLEAR + enableoff);
-               if (sync_access)
-                       sync_access();
-               enabled = true;
-       }
-
        /*
         * Write back the new configuration, and possibly re-enable
         * the interrupt. If we tried to write a new configuration and failed,
@@ -62,9 +48,6 @@ int gic_configure_irq(unsigned int irq, unsigned int type,
        if (readl_relaxed(base + GIC_DIST_CONFIG + confoff) != val && val != oldval)
                ret = -EINVAL;
 
-       if (enabled)
-               writel_relaxed(enablemask, base + GIC_DIST_ENABLE_SET + enableoff);
-
        if (sync_access)
                sync_access();
 
index 49875adb6b44bd4c293e3f7a95d422a40aafd3b5..c52f7ba205b4c872205323868ecf0349f8174b20 100644 (file)
@@ -658,6 +658,7 @@ static struct irq_chip gic_chip = {
        .irq_set_affinity       = gic_set_affinity,
        .irq_get_irqchip_state  = gic_irq_get_irqchip_state,
        .irq_set_irqchip_state  = gic_irq_set_irqchip_state,
+       .flags                  = IRQCHIP_SET_TYPE_MASKED,
 };
 
 #define GIC_ID_NR              (1U << gic_data.rdists.id_bits)
index 01999d74bd3af32c5d05b8f14c97c07637d4571e..8d7e1c8b6d566cb385c5cc92ff67c24f40692c9a 100644 (file)
@@ -324,6 +324,7 @@ static struct irq_chip gic_chip = {
 #endif
        .irq_get_irqchip_state  = gic_irq_get_irqchip_state,
        .irq_set_irqchip_state  = gic_irq_set_irqchip_state,
+       .flags                  = IRQCHIP_SET_TYPE_MASKED,
 };
 
 void __init gic_cascade_irq(unsigned int gic_nr, unsigned int irq)
index 7d6ffb5de84fce346ebd89c63c4a7a5fbb74e714..0cae45d106950783108b60c31432a9149e75f638 100644 (file)
@@ -202,6 +202,7 @@ static struct irq_chip hip04_irq_chip = {
 #ifdef CONFIG_SMP
        .irq_set_affinity       = hip04_irq_set_affinity,
 #endif
+       .flags                  = IRQCHIP_SET_TYPE_MASKED,
 };
 
 static u16 hip04_get_cpumask(struct hip04_irq_data *intc)
index 78e8b3ce5252ccac53d67debd8f5d2de05154da1..81e3cf5b9a1faa07ddbb1ab52b714aa08e0a4e09 100644 (file)
@@ -131,7 +131,7 @@ static int keystone_irq_map(struct irq_domain *h, unsigned int virq,
        return 0;
 }
 
-static struct irq_domain_ops keystone_irq_ops = {
+static const struct irq_domain_ops keystone_irq_ops = {
        .map    = keystone_irq_map,
        .xlate  = irq_domain_xlate_onecell,
 };
@@ -184,8 +184,7 @@ static int keystone_irq_probe(struct platform_device *pdev)
 
        platform_set_drvdata(pdev, kirq);
 
-       irq_set_chained_handler(kirq->irq, keystone_irq_handler);
-       irq_set_handler_data(kirq->irq, kirq);
+       irq_set_chained_handler_and_data(kirq->irq, keystone_irq_handler, kirq);
 
        /* clear all source bits */
        keystone_irq_writel(kirq, ~0x0);
index 57f09cb544644bcd97aa81bfc044c2686b350bbb..4400edd1a6c729129357f5df0bdb37f7e8f018db 100644 (file)
@@ -271,7 +271,7 @@ int gic_get_c0_fdc_int(void)
                                  GIC_LOCAL_TO_HWIRQ(GIC_LOCAL_INT_FDC));
 }
 
-static void gic_handle_shared_int(void)
+static void gic_handle_shared_int(bool chained)
 {
        unsigned int i, intr, virq;
        unsigned long *pcpu_mask;
@@ -299,7 +299,10 @@ static void gic_handle_shared_int(void)
        while (intr != gic_shared_intrs) {
                virq = irq_linear_revmap(gic_irq_domain,
                                         GIC_SHARED_TO_HWIRQ(intr));
-               do_IRQ(virq);
+               if (chained)
+                       generic_handle_irq(virq);
+               else
+                       do_IRQ(virq);
 
                /* go to next pending bit */
                bitmap_clear(pending, intr, 1);
@@ -431,7 +434,7 @@ static struct irq_chip gic_edge_irq_controller = {
 #endif
 };
 
-static void gic_handle_local_int(void)
+static void gic_handle_local_int(bool chained)
 {
        unsigned long pending, masked;
        unsigned int intr, virq;
@@ -445,7 +448,10 @@ static void gic_handle_local_int(void)
        while (intr != GIC_NUM_LOCAL_INTRS) {
                virq = irq_linear_revmap(gic_irq_domain,
                                         GIC_LOCAL_TO_HWIRQ(intr));
-               do_IRQ(virq);
+               if (chained)
+                       generic_handle_irq(virq);
+               else
+                       do_IRQ(virq);
 
                /* go to next pending bit */
                bitmap_clear(&pending, intr, 1);
@@ -509,13 +515,14 @@ static struct irq_chip gic_all_vpes_local_irq_controller = {
 
 static void __gic_irq_dispatch(void)
 {
-       gic_handle_local_int();
-       gic_handle_shared_int();
+       gic_handle_local_int(false);
+       gic_handle_shared_int(false);
 }
 
 static void gic_irq_dispatch(unsigned int irq, struct irq_desc *desc)
 {
-       __gic_irq_dispatch();
+       gic_handle_local_int(true);
+       gic_handle_shared_int(true);
 }
 
 #ifdef CONFIG_MIPS_GIC_IPI
@@ -739,7 +746,7 @@ static int gic_irq_domain_xlate(struct irq_domain *d, struct device_node *ctrlr,
        return 0;
 }
 
-static struct irq_domain_ops gic_irq_domain_ops = {
+static const struct irq_domain_ops gic_irq_domain_ops = {
        .map = gic_irq_domain_map,
        .xlate = gic_irq_domain_xlate,
 };
index eaf0a710e98aa9bf3040572d8be15a7c8dacf06a..15c13039bba211b53a309d05ba4fd0f333364065 100644 (file)
@@ -111,7 +111,7 @@ static int mtk_sysirq_domain_alloc(struct irq_domain *domain, unsigned int virq,
        return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, &gic_data);
 }
 
-static struct irq_domain_ops sysirq_domain_ops = {
+static const struct irq_domain_ops sysirq_domain_ops = {
        .xlate = mtk_sysirq_domain_xlate,
        .alloc = mtk_sysirq_domain_alloc,
        .free = irq_domain_free_irqs_common,
@@ -144,7 +144,7 @@ static int __init mtk_sysirq_of_init(struct device_node *node,
        chip_data->intpol_base = ioremap(res.start, size);
        if (!chip_data->intpol_base) {
                pr_err("mtk_sysirq: unable to map sysirq register\n");
-               ret = PTR_ERR(chip_data->intpol_base);
+               ret = -ENXIO;
                goto out_free;
        }
 
index e4acf1e3f8e3b0df693707709787202f78998c4e..04bf97b289cf4a10a6c4d4344247e6d26e4a22f5 100644 (file)
@@ -90,7 +90,7 @@ static int icoll_irq_domain_map(struct irq_domain *d, unsigned int virq,
        return 0;
 }
 
-static struct irq_domain_ops icoll_irq_domain_ops = {
+static const struct irq_domain_ops icoll_irq_domain_ops = {
        .map = icoll_irq_domain_map,
        .xlate = irq_domain_xlate_onecell,
 };
index 4ff0805fca017376ea879f918517b9d61abf5ff6..5fac9100f6cbee9f7abf144eb4dcb9efeb3aaee0 100644 (file)
@@ -49,6 +49,31 @@ nvic_handle_irq(irq_hw_number_t hwirq, struct pt_regs *regs)
        handle_IRQ(irq, regs);
 }
 
+static int nvic_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
+                               unsigned int nr_irqs, void *arg)
+{
+       int i, ret;
+       irq_hw_number_t hwirq;
+       unsigned int type = IRQ_TYPE_NONE;
+       struct of_phandle_args *irq_data = arg;
+
+       ret = irq_domain_xlate_onecell(domain, irq_data->np, irq_data->args,
+                                  irq_data->args_count, &hwirq, &type);
+       if (ret)
+               return ret;
+
+       for (i = 0; i < nr_irqs; i++)
+               irq_map_generic_chip(domain, virq + i, hwirq + i);
+
+       return 0;
+}
+
+static const struct irq_domain_ops nvic_irq_domain_ops = {
+       .xlate = irq_domain_xlate_onecell,
+       .alloc = nvic_irq_domain_alloc,
+       .free = irq_domain_free_irqs_top,
+};
+
 static int __init nvic_of_init(struct device_node *node,
                               struct device_node *parent)
 {
@@ -70,7 +95,8 @@ static int __init nvic_of_init(struct device_node *node,
                irqs = NVIC_MAX_IRQ;
 
        nvic_irq_domain =
-               irq_domain_add_linear(node, irqs, &irq_generic_chip_ops, NULL);
+               irq_domain_add_linear(node, irqs, &nvic_irq_domain_ops, NULL);
+
        if (!nvic_irq_domain) {
                pr_warn("Failed to allocate irq domain\n");
                return -ENOMEM;
index 9a0767b9c89da656be79048b109bda47fcd58ef6..0670ab4e3897bf612eb9e932a31ebc378cd3b803 100644 (file)
@@ -347,7 +347,7 @@ static int intc_irqpin_irq_domain_map(struct irq_domain *h, unsigned int virq,
        return 0;
 }
 
-static struct irq_domain_ops intc_irqpin_irq_domain_ops = {
+static const struct irq_domain_ops intc_irqpin_irq_domain_ops = {
        .map    = intc_irqpin_irq_domain_map,
        .xlate  = irq_domain_xlate_twocell,
 };
index cdf80b7794cd738e38ec956f3246ae216f9ddde1..778bd076aeea759a12fcfc49da0df952ffbc352a 100644 (file)
@@ -29,7 +29,6 @@
 #include <linux/err.h>
 #include <linux/slab.h>
 #include <linux/module.h>
-#include <linux/platform_data/irq-renesas-irqc.h>
 #include <linux/pm_runtime.h>
 
 #define IRQC_IRQ_MAX   32      /* maximum 32 interrupts per driver instance */
@@ -62,7 +61,6 @@ struct irqc_priv {
        void __iomem *iomem;
        void __iomem *cpu_int_base;
        struct irqc_irq irq[IRQC_IRQ_MAX];
-       struct renesas_irqc_config config;
        unsigned int number_of_irqs;
        struct platform_device *pdev;
        struct irq_chip irq_chip;
@@ -168,14 +166,13 @@ static int irqc_irq_domain_map(struct irq_domain *h, unsigned int virq,
        return 0;
 }
 
-static struct irq_domain_ops irqc_irq_domain_ops = {
+static const struct irq_domain_ops irqc_irq_domain_ops = {
        .map    = irqc_irq_domain_map,
        .xlate  = irq_domain_xlate_twocell,
 };
 
 static int irqc_probe(struct platform_device *pdev)
 {
-       struct renesas_irqc_config *pdata = pdev->dev.platform_data;
        struct irqc_priv *p;
        struct resource *io;
        struct resource *irq;
@@ -191,10 +188,6 @@ static int irqc_probe(struct platform_device *pdev)
                goto err0;
        }
 
-       /* deal with driver instance configuration */
-       if (pdata)
-               memcpy(&p->config, pdata, sizeof(*pdata));
-
        p->pdev = pdev;
        platform_set_drvdata(pdev, p);
 
@@ -251,8 +244,7 @@ static int irqc_probe(struct platform_device *pdev)
        irq_chip->flags = IRQCHIP_MASK_ON_SUSPEND;
 
        p->irq_domain = irq_domain_add_simple(pdev->dev.of_node,
-                                             p->number_of_irqs,
-                                             p->config.irq_base,
+                                             p->number_of_irqs, 0,
                                              &irqc_irq_domain_ops, p);
        if (!p->irq_domain) {
                ret = -ENXIO;
@@ -272,13 +264,6 @@ static int irqc_probe(struct platform_device *pdev)
 
        dev_info(&pdev->dev, "driving %d irqs\n", p->number_of_irqs);
 
-       /* warn in case of mismatch if irq base is specified */
-       if (p->config.irq_base) {
-               if (p->config.irq_base != p->irq[0].domain_irq)
-                       dev_warn(&pdev->dev, "irq base mismatch (%d/%d)\n",
-                                p->config.irq_base, p->irq[0].domain_irq);
-       }
-
        return 0;
 err3:
        while (--k >= 0)
index c8d373fcd823bd40e78f4003a77ea69aa357ff39..e96717f45ea15148adb46aefe5f74428a46b97a9 100644 (file)
@@ -502,7 +502,7 @@ err:
        return -EINVAL;
 }
 
-static struct irq_domain_ops s3c24xx_irq_ops = {
+static const struct irq_domain_ops s3c24xx_irq_ops = {
        .map = s3c24xx_irq_map,
        .xlate = irq_domain_xlate_twocell,
 };
@@ -1228,7 +1228,7 @@ static int s3c24xx_irq_xlate_of(struct irq_domain *d, struct device_node *n,
        return 0;
 }
 
-static struct irq_domain_ops s3c24xx_irq_ops_of = {
+static const struct irq_domain_ops s3c24xx_irq_ops_of = {
        .map = s3c24xx_irq_map_of,
        .xlate = s3c24xx_irq_xlate_of,
 };
index 64155b686081b81080fc8818fe5f9ac790e44ce5..83d6aa6464ee0b688c3b777c5eda519f0091af4e 100644 (file)
@@ -89,7 +89,7 @@ static int sun4i_irq_map(struct irq_domain *d, unsigned int virq,
        return 0;
 }
 
-static struct irq_domain_ops sun4i_irq_ops = {
+static const struct irq_domain_ops sun4i_irq_ops = {
        .map = sun4i_irq_map,
        .xlate = irq_domain_xlate_onecell,
 };
index 4a9ce5b50c5bba33b7428a0b67b88d26e31c4067..6b2b582433bde95062e85d17403e4a505c5a4ef9 100644 (file)
@@ -104,7 +104,7 @@ static int sunxi_sc_nmi_set_type(struct irq_data *data, unsigned int flow_type)
        irqd_set_trigger_type(data, flow_type);
        irq_setup_alt_chip(data, flow_type);
 
-       for (i = 0; i <= gc->num_ct; i++, ct++)
+       for (i = 0; i < gc->num_ct; i++, ct++)
                if (ct->type & flow_type)
                        ctrl_off = ct->regs.type;
 
index 1ab451729a5c5cd23936bf1b078c8a440253bfd9..888111b76ea0dd525c44ab46f68f96206be8e05d 100644 (file)
@@ -132,7 +132,7 @@ static int fpga_irqdomain_map(struct irq_domain *d, unsigned int irq,
        return 0;
 }
 
-static struct irq_domain_ops fpga_irqdomain_ops = {
+static const struct irq_domain_ops fpga_irqdomain_ops = {
        .map = fpga_irqdomain_map,
        .xlate = irq_domain_xlate_onetwocell,
 };
index 9521057d47448a4f290df3d760e7df044968c427..f5c01cbcc73ac1376627cf31aa227a9cf5c25179 100644 (file)
@@ -47,6 +47,7 @@ struct vf610_mscm_ir_chip_data {
        void __iomem *mscm_ir_base;
        u16 cpu_mask;
        u16 saved_irsprc[MSCM_IRSPRC_NUM];
+       bool is_nvic;
 };
 
 static struct vf610_mscm_ir_chip_data *mscm_ir_data;
@@ -101,7 +102,7 @@ static void vf610_mscm_ir_enable(struct irq_data *data)
        writew_relaxed(chip_data->cpu_mask,
                       chip_data->mscm_ir_base + MSCM_IRSPRC(hwirq));
 
-       irq_chip_unmask_parent(data);
+       irq_chip_enable_parent(data);
 }
 
 static void vf610_mscm_ir_disable(struct irq_data *data)
@@ -111,7 +112,7 @@ static void vf610_mscm_ir_disable(struct irq_data *data)
 
        writew_relaxed(0x0, chip_data->mscm_ir_base + MSCM_IRSPRC(hwirq));
 
-       irq_chip_mask_parent(data);
+       irq_chip_disable_parent(data);
 }
 
 static struct irq_chip vf610_mscm_ir_irq_chip = {
@@ -143,10 +144,17 @@ static int vf610_mscm_ir_domain_alloc(struct irq_domain *domain, unsigned int vi
                                              domain->host_data);
 
        gic_data.np = domain->parent->of_node;
-       gic_data.args_count = 3;
-       gic_data.args[0] = GIC_SPI;
-       gic_data.args[1] = irq_data->args[0];
-       gic_data.args[2] = irq_data->args[1];
+
+       if (mscm_ir_data->is_nvic) {
+               gic_data.args_count = 1;
+               gic_data.args[0] = irq_data->args[0];
+       } else {
+               gic_data.args_count = 3;
+               gic_data.args[0] = GIC_SPI;
+               gic_data.args[1] = irq_data->args[0];
+               gic_data.args[2] = irq_data->args[1];
+       }
+
        return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, &gic_data);
 }
 
@@ -174,10 +182,9 @@ static int __init vf610_mscm_ir_of_init(struct device_node *node,
                return -ENOMEM;
 
        mscm_ir_data->mscm_ir_base = of_io_request_and_map(node, 0, "mscm-ir");
-
-       if (!mscm_ir_data->mscm_ir_base) {
+       if (IS_ERR(mscm_ir_data->mscm_ir_base)) {
                pr_err("vf610_mscm_ir: unable to map mscm register\n");
-               ret = -ENOMEM;
+               ret = PTR_ERR(mscm_ir_data->mscm_ir_base);
                goto out_free;
        }
 
@@ -199,6 +206,9 @@ static int __init vf610_mscm_ir_of_init(struct device_node *node,
                goto out_unmap;
        }
 
+       if (of_device_is_compatible(domain->parent->of_node, "arm,armv7m-nvic"))
+               mscm_ir_data->is_nvic = true;
+
        cpu_pm_register_notifier(&mscm_ir_notifier_block);
 
        return 0;
index 54089debf2dca8b0b95baf0e31710be29d2c7c2e..d4ce331ea4a08eadbd447e61486eff0d124d842c 100644 (file)
@@ -256,7 +256,7 @@ static void __exception_irq_entry vic_handle_irq(struct pt_regs *regs)
        } while (handled);
 }
 
-static struct irq_domain_ops vic_irqdomain_ops = {
+static const struct irq_domain_ops vic_irqdomain_ops = {
        .map = vic_irqdomain_map,
        .xlate = irq_domain_xlate_onetwocell,
 };
index b7af816f276933c914c89e91e8f3305d47bb0e48..0b297009b85662888fbe29af6dd18b5bd45b5151 100644 (file)
@@ -173,7 +173,7 @@ static int vt8500_irq_map(struct irq_domain *h, unsigned int virq,
        return 0;
 }
 
-static struct irq_domain_ops vt8500_irq_domain_ops = {
+static const struct irq_domain_ops vt8500_irq_domain_ops = {
        .map = vt8500_irq_map,
        .xlate = irq_domain_xlate_onecell,
 };
index 9c145a7cb0567479f9fb7e38a0fe2f92f835df96..a45121546caff05acff16beb8d1dd2f4aa0d35c2 100644 (file)
@@ -207,8 +207,7 @@ static void __init spear_shirq_register(struct spear_shirq *shirq,
        if (!shirq->irq_chip)
                return;
 
-       irq_set_chained_handler(parent_irq, shirq_handler);
-       irq_set_handler_data(parent_irq, shirq);
+       irq_set_chained_handler_and_data(parent_irq, shirq_handler, shirq);
 
        for (i = 0; i < shirq->nr_irqs; i++) {
                irq_set_chip_and_handler(shirq->virq_base + i,
index 546b7e81161dd21df253aa7d5f0f41722a30c15e..aa5dd5668528912ec1f402deea49951fe8e15ff2 100644 (file)
@@ -58,7 +58,7 @@
  * About SOFTNET:
  * Most of the changes were pretty obvious and basically done by HE already.
  *
- * One problem of the isdn net device code is that is uses struct net_device
+ * One problem of the isdn net device code is that it uses struct net_device
  * for masters and slaves. However, only master interface are registered to
  * the network layer, and therefore, it only makes sense to call netif_*
  * functions on them.
index 728681debdbe437d086207f86422078bb560f774..7fb2a19ac649c55906f96f17eb2a9e988658260e 100644 (file)
@@ -187,6 +187,7 @@ void led_classdev_resume(struct led_classdev *led_cdev)
 }
 EXPORT_SYMBOL_GPL(led_classdev_resume);
 
+#ifdef CONFIG_PM_SLEEP
 static int led_suspend(struct device *dev)
 {
        struct led_classdev *led_cdev = dev_get_drvdata(dev);
@@ -206,11 +207,9 @@ static int led_resume(struct device *dev)
 
        return 0;
 }
+#endif
 
-static const struct dev_pm_ops leds_class_dev_pm_ops = {
-       .suspend        = led_suspend,
-       .resume         = led_resume,
-};
+static SIMPLE_DEV_PM_OPS(leds_class_dev_pm_ops, led_suspend, led_resume);
 
 static int match_name(struct device *dev, const void *data)
 {
index 5e7559be222adcd9724a08c2b8c2f135e5865769..eb934b0242e0e17652b7fc17c3255b5e70e40d15 100644 (file)
@@ -20,7 +20,7 @@
 #include "lg.h"
 
 /* Allow Guests to use a non-128 (ie. non-Linux) syscall trap. */
-static unsigned int syscall_vector = SYSCALL_VECTOR;
+static unsigned int syscall_vector = IA32_SYSCALL_VECTOR;
 module_param(syscall_vector, uint, 0444);
 
 /* The address of the interrupt handler is split into two bits: */
@@ -333,8 +333,8 @@ void set_interrupt(struct lg_cpu *cpu, unsigned int irq)
  */
 static bool could_be_syscall(unsigned int num)
 {
-       /* Normal Linux SYSCALL_VECTOR or reserved vector? */
-       return num == SYSCALL_VECTOR || num == syscall_vector;
+       /* Normal Linux IA32_SYSCALL_VECTOR or reserved vector? */
+       return num == IA32_SYSCALL_VECTOR || num == syscall_vector;
 }
 
 /* The syscall vector it wants must be unused by Host. */
@@ -351,7 +351,7 @@ bool check_syscall_vector(struct lguest *lg)
 int init_interrupts(void)
 {
        /* If they want some strange system call vector, reserve it now */
-       if (syscall_vector != SYSCALL_VECTOR) {
+       if (syscall_vector != IA32_SYSCALL_VECTOR) {
                if (test_bit(syscall_vector, used_vectors) ||
                    vector_used_by_percpu_irq(syscall_vector)) {
                        printk(KERN_ERR "lg: couldn't reserve syscall %u\n",
@@ -366,7 +366,7 @@ int init_interrupts(void)
 
 void free_interrupts(void)
 {
-       if (syscall_vector != SYSCALL_VECTOR)
+       if (syscall_vector != IA32_SYSCALL_VECTOR)
                clear_bit(syscall_vector, used_vectors);
 }
 
index 30f2aef69d787d7245b3e91c53b98a0a0216cdb9..6a4cd771a2be62b4172cc26a178ca85fbf7e6d27 100644 (file)
@@ -46,7 +46,7 @@
 #include <asm/setup.h>
 #include <asm/lguest.h>
 #include <asm/uaccess.h>
-#include <asm/i387.h>
+#include <asm/fpu/internal.h>
 #include <asm/tlbflush.h>
 #include "../lg.h"
 
@@ -251,7 +251,7 @@ void lguest_arch_run_guest(struct lg_cpu *cpu)
         * we set it now, so we can trap and pass that trap to the Guest if it
         * uses the FPU.
         */
-       if (cpu->ts && user_has_fpu())
+       if (cpu->ts && fpregs_active())
                stts();
 
        /*
@@ -283,7 +283,7 @@ void lguest_arch_run_guest(struct lg_cpu *cpu)
                wrmsr(MSR_IA32_SYSENTER_CS, __KERNEL_CS, 0);
 
        /* Clear the host TS bit if it was set above. */
-       if (cpu->ts && user_has_fpu())
+       if (cpu->ts && fpregs_active())
                clts();
 
        /*
@@ -297,12 +297,12 @@ void lguest_arch_run_guest(struct lg_cpu *cpu)
        /*
         * Similarly, if we took a trap because the Guest used the FPU,
         * we have to restore the FPU it expects to see.
-        * math_state_restore() may sleep and we may even move off to
+        * fpu__restore() may sleep and we may even move off to
         * a different CPU. So all the critical stuff should be done
         * before this.
         */
-       else if (cpu->regs->trapnum == 7 && !user_has_fpu())
-               math_state_restore();
+       else if (cpu->regs->trapnum == 7 && !fpregs_active())
+               fpu__restore(&current->thread.fpu);
 }
 
 /*H:130
diff --git a/drivers/macintosh/nvram.c b/drivers/macintosh/nvram.c
deleted file mode 100644 (file)
index f0e03e7..0000000
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
- * /dev/nvram driver for Power Macintosh.
- */
-
-#define NVRAM_VERSION "1.0"
-
-#include <linux/module.h>
-
-#include <linux/types.h>
-#include <linux/errno.h>
-#include <linux/fs.h>
-#include <linux/miscdevice.h>
-#include <linux/fcntl.h>
-#include <linux/nvram.h>
-#include <linux/init.h>
-#include <asm/uaccess.h>
-#include <asm/nvram.h>
-
-#define NVRAM_SIZE     8192
-
-static loff_t nvram_llseek(struct file *file, loff_t offset, int origin)
-{
-       switch (origin) {
-       case 0:
-               break;
-       case 1:
-               offset += file->f_pos;
-               break;
-       case 2:
-               offset += NVRAM_SIZE;
-               break;
-       default:
-               offset = -1;
-       }
-       if (offset < 0)
-               return -EINVAL;
-
-       file->f_pos = offset;
-       return file->f_pos;
-}
-
-static ssize_t read_nvram(struct file *file, char __user *buf,
-                         size_t count, loff_t *ppos)
-{
-       unsigned int i;
-       char __user *p = buf;
-
-       if (!access_ok(VERIFY_WRITE, buf, count))
-               return -EFAULT;
-       if (*ppos >= NVRAM_SIZE)
-               return 0;
-       for (i = *ppos; count > 0 && i < NVRAM_SIZE; ++i, ++p, --count)
-               if (__put_user(nvram_read_byte(i), p))
-                       return -EFAULT;
-       *ppos = i;
-       return p - buf;
-}
-
-static ssize_t write_nvram(struct file *file, const char __user *buf,
-                          size_t count, loff_t *ppos)
-{
-       unsigned int i;
-       const char __user *p = buf;
-       char c;
-
-       if (!access_ok(VERIFY_READ, buf, count))
-               return -EFAULT;
-       if (*ppos >= NVRAM_SIZE)
-               return 0;
-       for (i = *ppos; count > 0 && i < NVRAM_SIZE; ++i, ++p, --count) {
-               if (__get_user(c, p))
-                       return -EFAULT;
-               nvram_write_byte(c, i);
-       }
-       *ppos = i;
-       return p - buf;
-}
-
-static long nvram_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
-{
-       switch(cmd) {
-               case PMAC_NVRAM_GET_OFFSET:
-               {
-                       int part, offset;
-                       if (copy_from_user(&part, (void __user*)arg, sizeof(part)) != 0)
-                               return -EFAULT;
-                       if (part < pmac_nvram_OF || part > pmac_nvram_NR)
-                               return -EINVAL;
-                       offset = pmac_get_partition(part);
-                       if (copy_to_user((void __user*)arg, &offset, sizeof(offset)) != 0)
-                               return -EFAULT;
-                       break;
-               }
-
-               default:
-                       return -EINVAL;
-       }
-
-       return 0;
-}
-
-const struct file_operations nvram_fops = {
-       .owner          = THIS_MODULE,
-       .llseek         = nvram_llseek,
-       .read           = read_nvram,
-       .write          = write_nvram,
-       .unlocked_ioctl = nvram_ioctl,
-};
-
-static struct miscdevice nvram_dev = {
-       NVRAM_MINOR,
-       "nvram",
-       &nvram_fops
-};
-
-int __init nvram_init(void)
-{
-       printk(KERN_INFO "Macintosh non-volatile memory driver v%s\n",
-               NVRAM_VERSION);
-       return misc_register(&nvram_dev);
-}
-
-void __exit nvram_cleanup(void)
-{
-        misc_deregister( &nvram_dev );
-}
-
-module_init(nvram_init);
-module_exit(nvram_cleanup);
-MODULE_LICENSE("GPL");
index 27506302eb7aa42557bfc01547274957ccbace50..4dbed4a67aaf40e3c04bde925870c24d13cd1b4e 100644 (file)
@@ -3834,7 +3834,7 @@ array_state_store(struct mddev *mddev, const char *buf, size_t len)
                                err = -EBUSY;
                }
                spin_unlock(&mddev->lock);
-               return err;
+               return err ?: len;
        }
        err = mddev_lock(mddev);
        if (err)
@@ -4217,13 +4217,14 @@ action_store(struct mddev *mddev, const char *page, size_t len)
                        set_bit(MD_RECOVERY_FROZEN, &mddev->recovery);
                else
                        clear_bit(MD_RECOVERY_FROZEN, &mddev->recovery);
-               flush_workqueue(md_misc_wq);
-               if (mddev->sync_thread) {
-                       set_bit(MD_RECOVERY_INTR, &mddev->recovery);
-                       if (mddev_lock(mddev) == 0) {
+               if (test_bit(MD_RECOVERY_RUNNING, &mddev->recovery) &&
+                   mddev_lock(mddev) == 0) {
+                       flush_workqueue(md_misc_wq);
+                       if (mddev->sync_thread) {
+                               set_bit(MD_RECOVERY_INTR, &mddev->recovery);
                                md_reap_sync_thread(mddev);
-                               mddev_unlock(mddev);
                        }
+                       mddev_unlock(mddev);
                }
        } else if (test_bit(MD_RECOVERY_RUNNING, &mddev->recovery) ||
                   test_bit(MD_RECOVERY_NEEDED, &mddev->recovery))
@@ -8261,6 +8262,7 @@ void md_reap_sync_thread(struct mddev *mddev)
        if (mddev_is_clustered(mddev))
                md_cluster_ops->metadata_update_finish(mddev);
        clear_bit(MD_RECOVERY_RUNNING, &mddev->recovery);
+       clear_bit(MD_RECOVERY_DONE, &mddev->recovery);
        clear_bit(MD_RECOVERY_SYNC, &mddev->recovery);
        clear_bit(MD_RECOVERY_RESHAPE, &mddev->recovery);
        clear_bit(MD_RECOVERY_REQUESTED, &mddev->recovery);
index e793ab6b35705e0ed1ad6904ebe9353b6dbf6fd6..f55c3f35b7463141086afb727785c775c5185d76 100644 (file)
@@ -4156,6 +4156,7 @@ static int raid10_start_reshape(struct mddev *mddev)
 
        clear_bit(MD_RECOVERY_SYNC, &mddev->recovery);
        clear_bit(MD_RECOVERY_CHECK, &mddev->recovery);
+       clear_bit(MD_RECOVERY_DONE, &mddev->recovery);
        set_bit(MD_RECOVERY_RESHAPE, &mddev->recovery);
        set_bit(MD_RECOVERY_RUNNING, &mddev->recovery);
 
index 553d54b870528f0917e7518a9a783a28636d884a..b6793d2e051f3b278405f236e6623980bcdf1d04 100644 (file)
@@ -7354,6 +7354,7 @@ static int raid5_start_reshape(struct mddev *mddev)
 
        clear_bit(MD_RECOVERY_SYNC, &mddev->recovery);
        clear_bit(MD_RECOVERY_CHECK, &mddev->recovery);
+       clear_bit(MD_RECOVERY_DONE, &mddev->recovery);
        set_bit(MD_RECOVERY_RESHAPE, &mddev->recovery);
        set_bit(MD_RECOVERY_RUNNING, &mddev->recovery);
        mddev->sync_thread = md_register_thread(md_do_sync, mddev,
index 3ef0f90b128fc5bdf6d5e5dff0d5bbfbd5190d7b..157099243d6152190211b8625ba656d45feae003 100644 (file)
@@ -97,6 +97,7 @@ config MEDIA_CONTROLLER
 config MEDIA_CONTROLLER_DVB
        bool "Enable Media controller for DVB"
        depends on MEDIA_CONTROLLER
+       depends on BROKEN
        ---help---
          Enable the media controller API support for DVB.
 
index c98ac946b277d9e33a4f8d86c29a3c7ada7fe4ff..2e10643a86b7fb218b97beb274377796915e912a 100644 (file)
@@ -84,9 +84,9 @@
 #define        ABIST_BIN4_VGA3                         0x01D4
 #define        ABIST_BIN5_VGA4                         0x01D8
 #define        ABIST_BIN6_VGA5                         0x01DC
-#define        ABIST_BIN7_VGA6                         0x0x1E0
-#define        ABIST_CLAMP_A                           0x0x1E4
-#define        ABIST_CLAMP_B                           0x0x1E8
+#define        ABIST_BIN7_VGA6                         0x01E0
+#define        ABIST_CLAMP_A                           0x01E4
+#define        ABIST_CLAMP_B                           0x01E8
 #define        ABIST_CLAMP_C                           0x01EC
 #define        ABIST_CLAMP_D                           0x01F0
 #define        ABIST_CLAMP_E                           0x01F4
index dd6ee57e3a4c680da2708151275ce5e453a5892a..6e5867c57305253227096b3e9715bc92b4c761bd 100644 (file)
@@ -57,5 +57,8 @@ config VIDEO_FB_IVTV
          This is used in the Hauppauge PVR-350 card. There is a driver
          homepage at <http://www.ivtvdriver.org>.
 
+         In order to use this module, you will need to boot with PAT disabled
+         on x86 systems, using the nopat kernel parameter.
+
          To compile this driver as a module, choose M here: the
          module will be called ivtvfb.
index 9ff1230192e8308f08df35f0aad0a132bf14993a..4cb365d4ffdcc9c4e12cde82f46d411cb86c7a13 100644 (file)
@@ -44,8 +44,8 @@
 #include <linux/ivtvfb.h>
 #include <linux/slab.h>
 
-#ifdef CONFIG_MTRR
-#include <asm/mtrr.h>
+#ifdef CONFIG_X86_64
+#include <asm/pat.h>
 #endif
 
 #include "ivtv-driver.h"
@@ -155,12 +155,11 @@ struct osd_info {
        /* Buffer size */
        u32 video_buffer_size;
 
-#ifdef CONFIG_MTRR
        /* video_base rounded down as required by hardware MTRRs */
        unsigned long fb_start_aligned_physaddr;
        /* video_base rounded up as required by hardware MTRRs */
        unsigned long fb_end_aligned_physaddr;
-#endif
+       int wc_cookie;
 
        /* Store the buffer offset */
        int set_osd_coords_x;
@@ -1099,6 +1098,8 @@ static int ivtvfb_init_vidmode(struct ivtv *itv)
 static int ivtvfb_init_io(struct ivtv *itv)
 {
        struct osd_info *oi = itv->osd_info;
+       /* Find the largest power of two that maps the whole buffer */
+       int size_shift = 31;
 
        mutex_lock(&itv->serialize_lock);
        if (ivtv_init_on_first_open(itv)) {
@@ -1132,29 +1133,16 @@ static int ivtvfb_init_io(struct ivtv *itv)
                        oi->video_pbase, oi->video_vbase,
                        oi->video_buffer_size / 1024);
 
-#ifdef CONFIG_MTRR
-       {
-               /* Find the largest power of two that maps the whole buffer */
-               int size_shift = 31;
-
-               while (!(oi->video_buffer_size & (1 << size_shift))) {
-                       size_shift--;
-               }
-               size_shift++;
-               oi->fb_start_aligned_physaddr = oi->video_pbase & ~((1 << size_shift) - 1);
-               oi->fb_end_aligned_physaddr = oi->video_pbase + oi->video_buffer_size;
-               oi->fb_end_aligned_physaddr += (1 << size_shift) - 1;
-               oi->fb_end_aligned_physaddr &= ~((1 << size_shift) - 1);
-               if (mtrr_add(oi->fb_start_aligned_physaddr,
-                       oi->fb_end_aligned_physaddr - oi->fb_start_aligned_physaddr,
-                            MTRR_TYPE_WRCOMB, 1) < 0) {
-                       IVTVFB_INFO("disabled mttr\n");
-                       oi->fb_start_aligned_physaddr = 0;
-                       oi->fb_end_aligned_physaddr = 0;
-               }
-       }
-#endif
-
+       while (!(oi->video_buffer_size & (1 << size_shift)))
+               size_shift--;
+       size_shift++;
+       oi->fb_start_aligned_physaddr = oi->video_pbase & ~((1 << size_shift) - 1);
+       oi->fb_end_aligned_physaddr = oi->video_pbase + oi->video_buffer_size;
+       oi->fb_end_aligned_physaddr += (1 << size_shift) - 1;
+       oi->fb_end_aligned_physaddr &= ~((1 << size_shift) - 1);
+       oi->wc_cookie = arch_phys_wc_add(oi->fb_start_aligned_physaddr,
+                                        oi->fb_end_aligned_physaddr -
+                                        oi->fb_start_aligned_physaddr);
        /* Blank the entire osd. */
        memset_io(oi->video_vbase, 0, oi->video_buffer_size);
 
@@ -1172,14 +1160,7 @@ static void ivtvfb_release_buffers (struct ivtv *itv)
 
        /* Release pseudo palette */
        kfree(oi->ivtvfb_info.pseudo_palette);
-
-#ifdef CONFIG_MTRR
-       if (oi->fb_end_aligned_physaddr) {
-               mtrr_del(-1, oi->fb_start_aligned_physaddr,
-                       oi->fb_end_aligned_physaddr - oi->fb_start_aligned_physaddr);
-       }
-#endif
-
+       arch_phys_wc_del(oi->wc_cookie);
        kfree(oi);
        itv->osd_info = NULL;
 }
@@ -1284,6 +1265,13 @@ static int __init ivtvfb_init(void)
        int registered = 0;
        int err;
 
+#ifdef CONFIG_X86_64
+       if (WARN(pat_enabled(),
+                "ivtvfb needs PAT disabled, boot with nopat kernel parameter\n")) {
+               return -ENODEV;
+       }
+#endif
+
        if (ivtvfb_card_id < -1 || ivtvfb_card_id >= IVTV_MAX_CARDS) {
                printk(KERN_ERR "ivtvfb:  ivtvfb_card_id parameter is out of range (valid range: -1 - %d)\n",
                     IVTV_MAX_CARDS - 1);
index 187f83629f7ef8df1fcf4f5d7c6c7d83cff49fb5..5dcc0313c38a677d5f1960483dd64bb47f779891 100644 (file)
 #include <linux/delay.h>
 #include <linux/interrupt.h>           /* needed for in_interrupt() proto */
 #include <linux/dma-mapping.h>
-#include <asm/io.h>
-#ifdef CONFIG_MTRR
-#include <asm/mtrr.h>
-#endif
 #include <linux/kthread.h>
 #include <scsi/scsi_host.h>
 
@@ -2820,13 +2816,6 @@ mpt_adapter_dispose(MPT_ADAPTER *ioc)
        pci_disable_device(ioc->pcidev);
        pci_release_selected_regions(ioc->pcidev, ioc->bars);
 
-#if defined(CONFIG_MTRR) && 0
-       if (ioc->mtrr_reg > 0) {
-               mtrr_del(ioc->mtrr_reg, 0, 0);
-               dprintk(ioc, printk(MYIOC_s_INFO_FMT "MTRR region de-registered\n", ioc->name));
-       }
-#endif
-
        /*  Zap the adapter lookup ptr!  */
        list_del(&ioc->list);
 
@@ -4512,19 +4501,6 @@ PrimeIocFifos(MPT_ADAPTER *ioc)
 
                ioc->req_frames_low_dma = (u32) (alloc_dma & 0xFFFFFFFF);
 
-#if defined(CONFIG_MTRR) && 0
-               /*
-                *  Enable Write Combining MTRR for IOC's memory region.
-                *  (at least as much as we can; "size and base must be
-                *  multiples of 4 kiB"
-                */
-               ioc->mtrr_reg = mtrr_add(ioc->req_frames_dma,
-                                        sz,
-                                        MTRR_TYPE_WRCOMB, 1);
-               dprintk(ioc, printk(MYIOC_s_DEBUG_FMT "MTRR region registered (base:size=%08x:%x)\n",
-                               ioc->name, ioc->req_frames_dma, sz));
-#endif
-
                for (i = 0; i < ioc->req_depth; i++) {
                        alloc_dma += ioc->req_sz;
                        mem += ioc->req_sz;
index 8f14090b8b7151a66f43e23345c9bd5c280d1aa5..813d46311f6ac224a976545e1219d6465226a80d 100644 (file)
@@ -671,7 +671,6 @@ typedef struct _MPT_ADAPTER
        u8                      *HostPageBuffer; /* SAS - host page buffer support */
        u32                     HostPageBuffer_sz;
        dma_addr_t              HostPageBuffer_dma;
-       int                      mtrr_reg;
        struct pci_dev          *pcidev;        /* struct pci_dev pointer */
        int                     bars;           /* bitmask of BAR's that must be configured */
        int                     msi_enable;
index 5bdaae15a74257d9c252abed3b15ae5ce5cde69a..005a88b9f44029c42b2c700fa4e275482039c47f 100644 (file)
@@ -4090,7 +4090,7 @@ mptsas_handle_queue_full_event(struct fw_event_work *fw_event)
                                        continue;
                                }
                                depth = scsi_track_queue_full(sdev,
-                                   current_depth - 1);
+                                       sdev->queue_depth - 1);
                                if (depth > 0)
                                        sdev_printk(KERN_INFO, sdev,
                                        "Queue depth reduced to (%d)\n",
@@ -4100,7 +4100,7 @@ mptsas_handle_queue_full_event(struct fw_event_work *fw_event)
                                        "Tagged Command Queueing is being "
                                        "disabled\n");
                                else if (depth == 0)
-                                       sdev_printk(KERN_INFO, sdev,
+                                       sdev_printk(KERN_DEBUG, sdev,
                                        "Queue depth not changed yet\n");
                        }
                }
index d2a85cde68da50c2efe49a52d87cdc739b5aeef0..e03b7f45b8f7c7f73511fce4336b942cb23560a0 100644 (file)
@@ -566,7 +566,7 @@ static int pm860x_irq_domain_map(struct irq_domain *d, unsigned int virq,
        return 0;
 }
 
-static struct irq_domain_ops pm860x_irq_domain_ops = {
+static const struct irq_domain_ops pm860x_irq_domain_ops = {
        .map    = pm860x_irq_domain_map,
        .xlate  = irq_domain_xlate_onetwocell,
 };
index d5ad04dad081df4e78418a6bb19c9ab6997d2e3f..653815950aa2416b277718df69213545573aa557 100644 (file)
@@ -52,7 +52,8 @@ config PMIC_ADP5520
 config MFD_AAT2870_CORE
        bool "AnalogicTech AAT2870"
        select MFD_CORE
-       depends on I2C=y && GPIOLIB
+       depends on I2C=y
+       depends on GPIOLIB || COMPILE_TEST
        help
          If you say yes here you get support for the AAT2870.
          This driver provides common support for accessing the device,
@@ -94,6 +95,8 @@ config MFD_AXP20X
 config MFD_CROS_EC
        tristate "ChromeOS Embedded Controller"
        select MFD_CORE
+       select CHROME_PLATFORMS
+       select CROS_EC_PROTO
        help
          If you say Y here you get support for the ChromeOS Embedded
          Controller (EC) providing keyboard, battery and power services.
@@ -102,7 +105,7 @@ config MFD_CROS_EC
 
 config MFD_CROS_EC_I2C
        tristate "ChromeOS Embedded Controller (I2C)"
-       depends on MFD_CROS_EC && I2C
+       depends on MFD_CROS_EC && CROS_EC_PROTO && I2C
 
        help
          If you say Y here, you get support for talking to the ChromeOS
@@ -112,7 +115,7 @@ config MFD_CROS_EC_I2C
 
 config MFD_CROS_EC_SPI
        tristate "ChromeOS Embedded Controller (SPI)"
-       depends on MFD_CROS_EC && SPI && OF
+       depends on MFD_CROS_EC && CROS_EC_PROTO && SPI && OF
 
        ---help---
          If you say Y here, you get support for talking to the ChromeOS EC
@@ -1115,7 +1118,8 @@ config MFD_TPS6586X
 
 config MFD_TPS65910
        bool "TI TPS65910 Power Management chip"
-       depends on I2C=y && GPIOLIB
+       depends on I2C=y
+       depends on GPIOLIB || COMPILE_TEST
        select MFD_CORE
        select REGMAP_I2C
        select REGMAP_IRQ
index 0e5cfeba107ca4cc25d8169bd4afe76da4bc4162..ea40e076cb61b36d5a9aa4e4493a99f6caaab6d6 100644 (file)
@@ -39,13 +39,13 @@ obj-$(CONFIG_MFD_ARIZONA)   += arizona-core.o
 obj-$(CONFIG_MFD_ARIZONA)      += arizona-irq.o
 obj-$(CONFIG_MFD_ARIZONA_I2C)  += arizona-i2c.o
 obj-$(CONFIG_MFD_ARIZONA_SPI)  += arizona-spi.o
-ifneq ($(CONFIG_MFD_WM5102),n)
+ifeq ($(CONFIG_MFD_WM5102),y)
 obj-$(CONFIG_MFD_ARIZONA)      += wm5102-tables.o
 endif
-ifneq ($(CONFIG_MFD_WM5110),n)
+ifeq ($(CONFIG_MFD_WM5110),y)
 obj-$(CONFIG_MFD_ARIZONA)      += wm5110-tables.o
 endif
-ifneq ($(CONFIG_MFD_WM8997),n)
+ifeq ($(CONFIG_MFD_WM8997),y)
 obj-$(CONFIG_MFD_ARIZONA)      += wm8997-tables.o
 endif
 obj-$(CONFIG_MFD_WM8400)       += wm8400-core.o
index c80a2925f8e5f0df52131e88ab380fac44097ea7..000da72a0ae962f0f5f0132571a4fe9626b4fda7 100644 (file)
@@ -574,7 +574,7 @@ static int ab8500_irq_map(struct irq_domain *d, unsigned int virq,
        return 0;
 }
 
-static struct irq_domain_ops ab8500_irq_ops = {
+static const struct irq_domain_ops ab8500_irq_ops = {
        .map    = ab8500_irq_map,
        .xlate  = irq_domain_xlate_twocell,
 };
index cdd6f3d63314c90d8ccbf1467ff7e58eff024e1c..0236cd7cdce4f3fbfca4002c62f706f55a6b0eed 100644 (file)
@@ -2885,7 +2885,7 @@ static ssize_t ab8500_subscribe_write(struct file *file,
        }
 
        err = request_threaded_irq(user_val, NULL, ab8500_debug_handler,
-                                  IRQF_SHARED | IRQF_NO_SUSPEND,
+                                  IRQF_SHARED | IRQF_NO_SUSPEND | IRQF_ONESHOT,
                                   "ab8500-debug", &dev->kobj);
        if (err < 0) {
                pr_info("request_threaded_irq failed %d, %lu\n",
index dabbc93abdd717e5c40632b2d6b49209fb7149eb..c51c1b188d6401cb36fe77e5d05c0d55a95fa579 100644 (file)
@@ -948,7 +948,8 @@ static int ab8500_gpadc_probe(struct platform_device *pdev)
        if (gpadc->irq_sw >= 0) {
                ret = request_threaded_irq(gpadc->irq_sw, NULL,
                        ab8500_bm_gpadcconvend_handler,
-                       IRQF_NO_SUSPEND | IRQF_SHARED, "ab8500-gpadc-sw",
+                       IRQF_NO_SUSPEND | IRQF_SHARED | IRQF_ONESHOT,
+                       "ab8500-gpadc-sw",
                        gpadc);
                if (ret < 0) {
                        dev_err(gpadc->dev,
@@ -961,7 +962,8 @@ static int ab8500_gpadc_probe(struct platform_device *pdev)
        if (gpadc->irq_hw >= 0) {
                ret = request_threaded_irq(gpadc->irq_hw, NULL,
                        ab8500_bm_gpadcconvend_handler,
-                       IRQF_NO_SUSPEND | IRQF_SHARED, "ab8500-gpadc-hw",
+                       IRQF_NO_SUSPEND | IRQF_SHARED | IRQF_ONESHOT,
+                       "ab8500-gpadc-hw",
                        gpadc);
                if (ret < 0) {
                        dev_err(gpadc->dev,
index 6ca6dfab50ebf3a975c51dc5234061ec09b1401a..bebf58a06a6b2932d57798c0b5b81a31a12e9b7a 100644 (file)
@@ -250,20 +250,50 @@ static int arizona_wait_for_boot(struct arizona *arizona)
        return ret;
 }
 
-static int arizona_apply_hardware_patch(struct arizona* arizona)
+static inline void arizona_enable_reset(struct arizona *arizona)
+{
+       if (arizona->pdata.reset)
+               gpio_set_value_cansleep(arizona->pdata.reset, 0);
+}
+
+static void arizona_disable_reset(struct arizona *arizona)
+{
+       if (arizona->pdata.reset) {
+               switch (arizona->type) {
+               case WM5110:
+               case WM8280:
+                       /* Meet requirements for minimum reset duration */
+                       msleep(5);
+                       break;
+               default:
+                       break;
+               }
+
+               gpio_set_value_cansleep(arizona->pdata.reset, 1);
+               msleep(1);
+       }
+}
+
+struct arizona_sysclk_state {
+       unsigned int fll;
+       unsigned int sysclk;
+};
+
+static int arizona_enable_freerun_sysclk(struct arizona *arizona,
+                                        struct arizona_sysclk_state *state)
 {
-       unsigned int fll, sysclk;
        int ret, err;
 
        /* Cache existing FLL and SYSCLK settings */
-       ret = regmap_read(arizona->regmap, ARIZONA_FLL1_CONTROL_1, &fll);
-       if (ret != 0) {
+       ret = regmap_read(arizona->regmap, ARIZONA_FLL1_CONTROL_1, &state->fll);
+       if (ret) {
                dev_err(arizona->dev, "Failed to cache FLL settings: %d\n",
                        ret);
                return ret;
        }
-       ret = regmap_read(arizona->regmap, ARIZONA_SYSTEM_CLOCK_1, &sysclk);
-       if (ret != 0) {
+       ret = regmap_read(arizona->regmap, ARIZONA_SYSTEM_CLOCK_1,
+                         &state->sysclk);
+       if (ret) {
                dev_err(arizona->dev, "Failed to cache SYSCLK settings: %d\n",
                        ret);
                return ret;
@@ -272,7 +302,7 @@ static int arizona_apply_hardware_patch(struct arizona* arizona)
        /* Start up SYSCLK using the FLL in free running mode */
        ret = regmap_write(arizona->regmap, ARIZONA_FLL1_CONTROL_1,
                        ARIZONA_FLL1_ENA | ARIZONA_FLL1_FREERUN);
-       if (ret != 0) {
+       if (ret) {
                dev_err(arizona->dev,
                        "Failed to start FLL in freerunning mode: %d\n",
                        ret);
@@ -281,53 +311,137 @@ static int arizona_apply_hardware_patch(struct arizona* arizona)
        ret = arizona_poll_reg(arizona, 25, ARIZONA_INTERRUPT_RAW_STATUS_5,
                               ARIZONA_FLL1_CLOCK_OK_STS,
                               ARIZONA_FLL1_CLOCK_OK_STS);
-       if (ret != 0) {
+       if (ret) {
                ret = -ETIMEDOUT;
                goto err_fll;
        }
 
        ret = regmap_write(arizona->regmap, ARIZONA_SYSTEM_CLOCK_1, 0x0144);
-       if (ret != 0) {
+       if (ret) {
                dev_err(arizona->dev, "Failed to start SYSCLK: %d\n", ret);
                goto err_fll;
        }
 
+       return 0;
+
+err_fll:
+       err = regmap_write(arizona->regmap, ARIZONA_FLL1_CONTROL_1, state->fll);
+       if (err)
+               dev_err(arizona->dev,
+                       "Failed to re-apply old FLL settings: %d\n", err);
+
+       return ret;
+}
+
+static int arizona_disable_freerun_sysclk(struct arizona *arizona,
+                                         struct arizona_sysclk_state *state)
+{
+       int ret;
+
+       ret = regmap_write(arizona->regmap, ARIZONA_SYSTEM_CLOCK_1,
+                          state->sysclk);
+       if (ret) {
+               dev_err(arizona->dev,
+                       "Failed to re-apply old SYSCLK settings: %d\n", ret);
+               return ret;
+       }
+
+       ret = regmap_write(arizona->regmap, ARIZONA_FLL1_CONTROL_1, state->fll);
+       if (ret) {
+               dev_err(arizona->dev,
+                       "Failed to re-apply old FLL settings: %d\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int wm5102_apply_hardware_patch(struct arizona *arizona)
+{
+       struct arizona_sysclk_state state;
+       int err, ret;
+
+       ret = arizona_enable_freerun_sysclk(arizona, &state);
+       if (ret)
+               return ret;
+
        /* Start the write sequencer and wait for it to finish */
        ret = regmap_write(arizona->regmap, ARIZONA_WRITE_SEQUENCER_CTRL_0,
-                       ARIZONA_WSEQ_ENA | ARIZONA_WSEQ_START | 160);
-       if (ret != 0) {
+                          ARIZONA_WSEQ_ENA | ARIZONA_WSEQ_START | 160);
+       if (ret) {
                dev_err(arizona->dev, "Failed to start write sequencer: %d\n",
                        ret);
-               goto err_sysclk;
+               goto err;
        }
+
        ret = arizona_poll_reg(arizona, 5, ARIZONA_WRITE_SEQUENCER_CTRL_1,
                               ARIZONA_WSEQ_BUSY, 0);
-       if (ret != 0) {
+       if (ret) {
                regmap_write(arizona->regmap, ARIZONA_WRITE_SEQUENCER_CTRL_0,
-                               ARIZONA_WSEQ_ABORT);
+                            ARIZONA_WSEQ_ABORT);
                ret = -ETIMEDOUT;
        }
 
-err_sysclk:
-       err = regmap_write(arizona->regmap, ARIZONA_SYSTEM_CLOCK_1, sysclk);
-       if (err != 0) {
-               dev_err(arizona->dev,
-                       "Failed to re-apply old SYSCLK settings: %d\n",
-                       err);
-       }
+err:
+       err = arizona_disable_freerun_sysclk(arizona, &state);
 
-err_fll:
-       err = regmap_write(arizona->regmap, ARIZONA_FLL1_CONTROL_1, fll);
-       if (err != 0) {
+       return ret ?: err;
+}
+
+/*
+ * Register patch to some of the CODECs internal write sequences
+ * to ensure a clean exit from the low power sleep state.
+ */
+static const struct reg_default wm5110_sleep_patch[] = {
+       { 0x337A, 0xC100 },
+       { 0x337B, 0x0041 },
+       { 0x3300, 0xA210 },
+       { 0x3301, 0x050C },
+};
+
+static int wm5110_apply_sleep_patch(struct arizona *arizona)
+{
+       struct arizona_sysclk_state state;
+       int err, ret;
+
+       ret = arizona_enable_freerun_sysclk(arizona, &state);
+       if (ret)
+               return ret;
+
+       ret = regmap_multi_reg_write_bypassed(arizona->regmap,
+                                             wm5110_sleep_patch,
+                                             ARRAY_SIZE(wm5110_sleep_patch));
+
+       err = arizona_disable_freerun_sysclk(arizona, &state);
+
+       return ret ?: err;
+}
+
+static int wm5102_clear_write_sequencer(struct arizona *arizona)
+{
+       int ret;
+
+       ret = regmap_write(arizona->regmap, ARIZONA_WRITE_SEQUENCER_CTRL_3,
+                          0x0);
+       if (ret) {
                dev_err(arizona->dev,
-                       "Failed to re-apply old FLL settings: %d\n",
-                       err);
+                       "Failed to clear write sequencer state: %d\n", ret);
+               return ret;
        }
 
-       if (ret != 0)
+       arizona_enable_reset(arizona);
+       regulator_disable(arizona->dcvdd);
+
+       msleep(20);
+
+       ret = regulator_enable(arizona->dcvdd);
+       if (ret) {
+               dev_err(arizona->dev, "Failed to re-enable DCVDD: %d\n", ret);
                return ret;
-       else
-               return err;
+       }
+       arizona_disable_reset(arizona);
+
+       return 0;
 }
 
 #ifdef CONFIG_PM
@@ -338,12 +452,33 @@ static int arizona_runtime_resume(struct device *dev)
 
        dev_dbg(arizona->dev, "Leaving AoD mode\n");
 
+       if (arizona->has_fully_powered_off) {
+               dev_dbg(arizona->dev, "Re-enabling core supplies\n");
+
+               ret = regulator_bulk_enable(arizona->num_core_supplies,
+                                           arizona->core_supplies);
+               if (ret) {
+                       dev_err(dev, "Failed to enable core supplies: %d\n",
+                               ret);
+                       return ret;
+               }
+       }
+
        ret = regulator_enable(arizona->dcvdd);
        if (ret != 0) {
                dev_err(arizona->dev, "Failed to enable DCVDD: %d\n", ret);
+               if (arizona->has_fully_powered_off)
+                       regulator_bulk_disable(arizona->num_core_supplies,
+                                              arizona->core_supplies);
                return ret;
        }
 
+       if (arizona->has_fully_powered_off) {
+               arizona_disable_reset(arizona);
+               enable_irq(arizona->irq);
+               arizona->has_fully_powered_off = false;
+       }
+
        regcache_cache_only(arizona->regmap, false);
 
        switch (arizona->type) {
@@ -366,14 +501,53 @@ static int arizona_runtime_resume(struct device *dev)
                        goto err;
                }
 
-               ret = arizona_apply_hardware_patch(arizona);
-               if (ret != 0) {
+               ret = wm5102_apply_hardware_patch(arizona);
+               if (ret) {
                        dev_err(arizona->dev,
                                "Failed to apply hardware patch: %d\n",
                                ret);
                        goto err;
                }
                break;
+       case WM5110:
+       case WM8280:
+               ret = arizona_wait_for_boot(arizona);
+               if (ret)
+                       goto err;
+
+               if (arizona->external_dcvdd) {
+                       ret = regmap_update_bits(arizona->regmap,
+                                                ARIZONA_ISOLATION_CONTROL,
+                                                ARIZONA_ISOLATE_DCVDD1, 0);
+                       if (ret) {
+                               dev_err(arizona->dev,
+                                       "Failed to connect DCVDD: %d\n", ret);
+                               goto err;
+                       }
+               } else {
+                       /*
+                        * As this is only called for the internal regulator
+                        * (where we know voltage ranges available) it is ok
+                        * to request an exact range.
+                        */
+                       ret = regulator_set_voltage(arizona->dcvdd,
+                                                   1200000, 1200000);
+                       if (ret < 0) {
+                               dev_err(arizona->dev,
+                                       "Failed to set resume voltage: %d\n",
+                                       ret);
+                               goto err;
+                       }
+               }
+
+               ret = wm5110_apply_sleep_patch(arizona);
+               if (ret) {
+                       dev_err(arizona->dev,
+                               "Failed to re-apply sleep patch: %d\n",
+                               ret);
+                       goto err;
+               }
+               break;
        default:
                ret = arizona_wait_for_boot(arizona);
                if (ret != 0) {
@@ -410,10 +584,17 @@ err:
 static int arizona_runtime_suspend(struct device *dev)
 {
        struct arizona *arizona = dev_get_drvdata(dev);
+       unsigned int val;
        int ret;
 
        dev_dbg(arizona->dev, "Entering AoD mode\n");
 
+       ret = regmap_read(arizona->regmap, ARIZONA_JACK_DETECT_ANALOGUE, &val);
+       if (ret) {
+               dev_err(dev, "Failed to check jack det status: %d\n", ret);
+               return ret;
+       }
+
        if (arizona->external_dcvdd) {
                ret = regmap_update_bits(arizona->regmap,
                                         ARIZONA_ISOLATION_CONTROL,
@@ -426,10 +607,56 @@ static int arizona_runtime_suspend(struct device *dev)
                }
        }
 
+       switch (arizona->type) {
+       case WM5110:
+       case WM8280:
+               if (arizona->external_dcvdd)
+                       break;
+
+               /*
+                * As this is only called for the internal regulator
+                * (where we know voltage ranges available) it is ok
+                * to request an exact range.
+                */
+               ret = regulator_set_voltage(arizona->dcvdd, 1175000, 1175000);
+               if (ret < 0) {
+                       dev_err(arizona->dev,
+                               "Failed to set suspend voltage: %d\n", ret);
+                       return ret;
+               }
+               break;
+       case WM5102:
+               if (!(val & ARIZONA_JD1_ENA)) {
+                       ret = regmap_write(arizona->regmap,
+                                          ARIZONA_WRITE_SEQUENCER_CTRL_3, 0x0);
+                       if (ret) {
+                               dev_err(arizona->dev,
+                                       "Failed to clear write sequencer: %d\n",
+                                       ret);
+                               return ret;
+                       }
+               }
+               break;
+       default:
+               break;
+       }
+
        regcache_cache_only(arizona->regmap, true);
        regcache_mark_dirty(arizona->regmap);
        regulator_disable(arizona->dcvdd);
 
+       /* Allow us to completely power down if no jack detection */
+       if (!(val & ARIZONA_JD1_ENA)) {
+               dev_dbg(arizona->dev, "Fully powering off\n");
+
+               arizona->has_fully_powered_off = true;
+
+               disable_irq(arizona->irq);
+               arizona_enable_reset(arizona);
+               regulator_bulk_disable(arizona->num_core_supplies,
+                                      arizona->core_supplies);
+       }
+
        return 0;
 }
 #endif
@@ -728,9 +955,9 @@ int arizona_dev_init(struct arizona *arizona)
 
        if (arizona->pdata.reset) {
                /* Start out with /RESET low to put the chip into reset */
-               ret = gpio_request_one(arizona->pdata.reset,
-                                      GPIOF_DIR_OUT | GPIOF_INIT_LOW,
-                                      "arizona /RESET");
+               ret = devm_gpio_request_one(arizona->dev, arizona->pdata.reset,
+                                           GPIOF_DIR_OUT | GPIOF_INIT_LOW,
+                                           "arizona /RESET");
                if (ret != 0) {
                        dev_err(dev, "Failed to request /RESET: %d\n", ret);
                        goto err_dcvdd;
@@ -751,10 +978,7 @@ int arizona_dev_init(struct arizona *arizona)
                goto err_enable;
        }
 
-       if (arizona->pdata.reset) {
-               gpio_set_value_cansleep(arizona->pdata.reset, 1);
-               msleep(1);
-       }
+       arizona_disable_reset(arizona);
 
        regcache_cache_only(arizona->regmap, false);
 
@@ -777,8 +1001,6 @@ int arizona_dev_init(struct arizona *arizona)
 
        /* If we have a /RESET GPIO we'll already be reset */
        if (!arizona->pdata.reset) {
-               regcache_mark_dirty(arizona->regmap);
-
                ret = regmap_write(arizona->regmap, ARIZONA_SOFTWARE_RESET, 0);
                if (ret != 0) {
                        dev_err(dev, "Failed to reset device: %d\n", ret);
@@ -786,12 +1008,6 @@ int arizona_dev_init(struct arizona *arizona)
                }
 
                msleep(1);
-
-               ret = regcache_sync(arizona->regmap);
-               if (ret != 0) {
-                       dev_err(dev, "Failed to sync device: %d\n", ret);
-                       goto err_reset;
-               }
        }
 
        /* Ensure device startup is complete */
@@ -799,21 +1015,24 @@ int arizona_dev_init(struct arizona *arizona)
        case WM5102:
                ret = regmap_read(arizona->regmap,
                                  ARIZONA_WRITE_SEQUENCER_CTRL_3, &val);
-               if (ret != 0)
+               if (ret) {
                        dev_err(dev,
                                "Failed to check write sequencer state: %d\n",
                                ret);
-               else if (val & 0x01)
-                       break;
-               /* Fall through */
-       default:
-               ret = arizona_wait_for_boot(arizona);
-               if (ret != 0) {
-                       dev_err(arizona->dev,
-                               "Device failed initial boot: %d\n", ret);
-                       goto err_reset;
+               } else if (val & 0x01) {
+                       ret = wm5102_clear_write_sequencer(arizona);
+                       if (ret)
+                               return ret;
                }
                break;
+       default:
+               break;
+       }
+
+       ret = arizona_wait_for_boot(arizona);
+       if (ret) {
+               dev_err(arizona->dev, "Device failed initial boot: %d\n", ret);
+               goto err_reset;
        }
 
        /* Read the device ID information & do device specific stuff */
@@ -891,14 +1110,24 @@ int arizona_dev_init(struct arizona *arizona)
 
                switch (arizona->type) {
                case WM5102:
-                       ret = arizona_apply_hardware_patch(arizona);
-                       if (ret != 0) {
+                       ret = wm5102_apply_hardware_patch(arizona);
+                       if (ret) {
                                dev_err(arizona->dev,
                                        "Failed to apply hardware patch: %d\n",
                                        ret);
                                goto err_reset;
                        }
                        break;
+               case WM5110:
+               case WM8280:
+                       ret = wm5110_apply_sleep_patch(arizona);
+                       if (ret) {
+                               dev_err(arizona->dev,
+                                       "Failed to apply sleep patch: %d\n",
+                                       ret);
+                               goto err_reset;
+                       }
+                       break;
                default:
                        break;
                }
@@ -977,12 +1206,16 @@ int arizona_dev_init(struct arizona *arizona)
                /* Default for both is 0 so noop with defaults */
                val = arizona->pdata.dmic_ref[i]
                        << ARIZONA_IN1_DMIC_SUP_SHIFT;
-               val |= arizona->pdata.inmode[i] << ARIZONA_IN1_MODE_SHIFT;
+               if (arizona->pdata.inmode[i] & ARIZONA_INMODE_DMIC)
+                       val |= 1 << ARIZONA_IN1_MODE_SHIFT;
+               if (arizona->pdata.inmode[i] & ARIZONA_INMODE_SE)
+                       val |= 1 << ARIZONA_IN1_SINGLE_ENDED_SHIFT;
 
                regmap_update_bits(arizona->regmap,
                                   ARIZONA_IN1L_CONTROL + (i * 8),
                                   ARIZONA_IN1_DMIC_SUP_MASK |
-                                  ARIZONA_IN1_MODE_MASK, val);
+                                  ARIZONA_IN1_MODE_MASK |
+                                  ARIZONA_IN1_SINGLE_ENDED_MASK, val);
        }
 
        for (i = 0; i < ARIZONA_MAX_OUTPUT; i++) {
@@ -1054,10 +1287,7 @@ int arizona_dev_init(struct arizona *arizona)
 err_irq:
        arizona_irq_exit(arizona);
 err_reset:
-       if (arizona->pdata.reset) {
-               gpio_set_value_cansleep(arizona->pdata.reset, 0);
-               gpio_free(arizona->pdata.reset);
-       }
+       arizona_enable_reset(arizona);
        regulator_disable(arizona->dcvdd);
 err_enable:
        regulator_bulk_disable(arizona->num_core_supplies,
@@ -1082,8 +1312,7 @@ int arizona_dev_exit(struct arizona *arizona)
        arizona_free_irq(arizona, ARIZONA_IRQ_OVERCLOCKED, arizona);
        arizona_free_irq(arizona, ARIZONA_IRQ_CLKGEN_ERR, arizona);
        arizona_irq_exit(arizona);
-       if (arizona->pdata.reset)
-               gpio_set_value_cansleep(arizona->pdata.reset, 0);
+       arizona_enable_reset(arizona);
 
        regulator_bulk_disable(arizona->num_core_supplies,
                               arizona->core_supplies);
index d063b94b94b5c97236cc04bb6311d486bd3adac2..2b9965d53e4e5fc402e32059bf73e7cc258ad387 100644 (file)
@@ -186,7 +186,7 @@ static int arizona_irq_map(struct irq_domain *h, unsigned int virq,
        return 0;
 }
 
-static struct irq_domain_ops arizona_domain_ops = {
+static const struct irq_domain_ops arizona_domain_ops = {
        .map    = arizona_irq_map,
        .xlate  = irq_domain_xlate_twocell,
 };
index d18029be6a78e3c0fecfbe2c64961eef25714eb6..6df91556faf33d3193c50b9e2e11f6ef6f9ac4af 100644 (file)
@@ -32,6 +32,7 @@
 static const char * const axp20x_model_names[] = {
        "AXP202",
        "AXP209",
+       "AXP221",
        "AXP288",
 };
 
@@ -54,6 +55,25 @@ static const struct regmap_access_table axp20x_volatile_table = {
        .n_yes_ranges   = ARRAY_SIZE(axp20x_volatile_ranges),
 };
 
+static const struct regmap_range axp22x_writeable_ranges[] = {
+       regmap_reg_range(AXP20X_DATACACHE(0), AXP20X_IRQ5_STATE),
+       regmap_reg_range(AXP20X_DCDC_MODE, AXP22X_BATLOW_THRES1),
+};
+
+static const struct regmap_range axp22x_volatile_ranges[] = {
+       regmap_reg_range(AXP20X_IRQ1_EN, AXP20X_IRQ5_STATE),
+};
+
+static const struct regmap_access_table axp22x_writeable_table = {
+       .yes_ranges     = axp22x_writeable_ranges,
+       .n_yes_ranges   = ARRAY_SIZE(axp22x_writeable_ranges),
+};
+
+static const struct regmap_access_table axp22x_volatile_table = {
+       .yes_ranges     = axp22x_volatile_ranges,
+       .n_yes_ranges   = ARRAY_SIZE(axp22x_volatile_ranges),
+};
+
 static const struct regmap_range axp288_writeable_ranges[] = {
        regmap_reg_range(AXP20X_DATACACHE(0), AXP20X_IRQ6_STATE),
        regmap_reg_range(AXP20X_DCDC_MODE, AXP288_FG_TUNE5),
@@ -87,6 +107,20 @@ static struct resource axp20x_pek_resources[] = {
        },
 };
 
+static struct resource axp22x_pek_resources[] = {
+       {
+               .name   = "PEK_DBR",
+               .start  = AXP22X_IRQ_PEK_RIS_EDGE,
+               .end    = AXP22X_IRQ_PEK_RIS_EDGE,
+               .flags  = IORESOURCE_IRQ,
+       }, {
+               .name   = "PEK_DBF",
+               .start  = AXP22X_IRQ_PEK_FAL_EDGE,
+               .end    = AXP22X_IRQ_PEK_FAL_EDGE,
+               .flags  = IORESOURCE_IRQ,
+       },
+};
+
 static struct resource axp288_fuel_gauge_resources[] = {
        {
                .start = AXP288_IRQ_QWBTU,
@@ -129,6 +163,15 @@ static const struct regmap_config axp20x_regmap_config = {
        .cache_type     = REGCACHE_RBTREE,
 };
 
+static const struct regmap_config axp22x_regmap_config = {
+       .reg_bits       = 8,
+       .val_bits       = 8,
+       .wr_table       = &axp22x_writeable_table,
+       .volatile_table = &axp22x_volatile_table,
+       .max_register   = AXP22X_BATLOW_THRES1,
+       .cache_type     = REGCACHE_RBTREE,
+};
+
 static const struct regmap_config axp288_regmap_config = {
        .reg_bits       = 8,
        .val_bits       = 8,
@@ -181,6 +224,34 @@ static const struct regmap_irq axp20x_regmap_irqs[] = {
        INIT_REGMAP_IRQ(AXP20X, GPIO0_INPUT,            4, 0),
 };
 
+static const struct regmap_irq axp22x_regmap_irqs[] = {
+       INIT_REGMAP_IRQ(AXP22X, ACIN_OVER_V,            0, 7),
+       INIT_REGMAP_IRQ(AXP22X, ACIN_PLUGIN,            0, 6),
+       INIT_REGMAP_IRQ(AXP22X, ACIN_REMOVAL,           0, 5),
+       INIT_REGMAP_IRQ(AXP22X, VBUS_OVER_V,            0, 4),
+       INIT_REGMAP_IRQ(AXP22X, VBUS_PLUGIN,            0, 3),
+       INIT_REGMAP_IRQ(AXP22X, VBUS_REMOVAL,           0, 2),
+       INIT_REGMAP_IRQ(AXP22X, VBUS_V_LOW,             0, 1),
+       INIT_REGMAP_IRQ(AXP22X, BATT_PLUGIN,            1, 7),
+       INIT_REGMAP_IRQ(AXP22X, BATT_REMOVAL,           1, 6),
+       INIT_REGMAP_IRQ(AXP22X, BATT_ENT_ACT_MODE,      1, 5),
+       INIT_REGMAP_IRQ(AXP22X, BATT_EXIT_ACT_MODE,     1, 4),
+       INIT_REGMAP_IRQ(AXP22X, CHARG,                  1, 3),
+       INIT_REGMAP_IRQ(AXP22X, CHARG_DONE,             1, 2),
+       INIT_REGMAP_IRQ(AXP22X, BATT_TEMP_HIGH,         1, 1),
+       INIT_REGMAP_IRQ(AXP22X, BATT_TEMP_LOW,          1, 0),
+       INIT_REGMAP_IRQ(AXP22X, DIE_TEMP_HIGH,          2, 7),
+       INIT_REGMAP_IRQ(AXP22X, PEK_SHORT,              2, 1),
+       INIT_REGMAP_IRQ(AXP22X, PEK_LONG,               2, 0),
+       INIT_REGMAP_IRQ(AXP22X, LOW_PWR_LVL1,           3, 1),
+       INIT_REGMAP_IRQ(AXP22X, LOW_PWR_LVL2,           3, 0),
+       INIT_REGMAP_IRQ(AXP22X, TIMER,                  4, 7),
+       INIT_REGMAP_IRQ(AXP22X, PEK_RIS_EDGE,           4, 6),
+       INIT_REGMAP_IRQ(AXP22X, PEK_FAL_EDGE,           4, 5),
+       INIT_REGMAP_IRQ(AXP22X, GPIO1_INPUT,            4, 1),
+       INIT_REGMAP_IRQ(AXP22X, GPIO0_INPUT,            4, 0),
+};
+
 /* some IRQs are compatible with axp20x models */
 static const struct regmap_irq axp288_regmap_irqs[] = {
        INIT_REGMAP_IRQ(AXP288, VBUS_FALL,              0, 2),
@@ -224,6 +295,7 @@ static const struct regmap_irq axp288_regmap_irqs[] = {
 static const struct of_device_id axp20x_of_match[] = {
        { .compatible = "x-powers,axp202", .data = (void *) AXP202_ID },
        { .compatible = "x-powers,axp209", .data = (void *) AXP209_ID },
+       { .compatible = "x-powers,axp221", .data = (void *) AXP221_ID },
        { },
 };
 MODULE_DEVICE_TABLE(of, axp20x_of_match);
@@ -258,6 +330,18 @@ static const struct regmap_irq_chip axp20x_regmap_irq_chip = {
 
 };
 
+static const struct regmap_irq_chip axp22x_regmap_irq_chip = {
+       .name                   = "axp22x_irq_chip",
+       .status_base            = AXP20X_IRQ1_STATE,
+       .ack_base               = AXP20X_IRQ1_STATE,
+       .mask_base              = AXP20X_IRQ1_EN,
+       .mask_invert            = true,
+       .init_ack_masked        = true,
+       .irqs                   = axp22x_regmap_irqs,
+       .num_irqs               = ARRAY_SIZE(axp22x_regmap_irqs),
+       .num_regs               = 5,
+};
+
 static const struct regmap_irq_chip axp288_regmap_irq_chip = {
        .name                   = "axp288_irq_chip",
        .status_base            = AXP20X_IRQ1_STATE,
@@ -281,6 +365,16 @@ static struct mfd_cell axp20x_cells[] = {
        },
 };
 
+static struct mfd_cell axp22x_cells[] = {
+       {
+               .name                   = "axp20x-pek",
+               .num_resources          = ARRAY_SIZE(axp22x_pek_resources),
+               .resources              = axp22x_pek_resources,
+       }, {
+               .name                   = "axp20x-regulator",
+       },
+};
+
 static struct resource axp288_adc_resources[] = {
        {
                .name  = "GPADC",
@@ -426,6 +520,12 @@ static int axp20x_match_device(struct axp20x_dev *axp20x, struct device *dev)
                axp20x->regmap_cfg = &axp20x_regmap_config;
                axp20x->regmap_irq_chip = &axp20x_regmap_irq_chip;
                break;
+       case AXP221_ID:
+               axp20x->nr_cells = ARRAY_SIZE(axp22x_cells);
+               axp20x->cells = axp22x_cells;
+               axp20x->regmap_cfg = &axp22x_regmap_config;
+               axp20x->regmap_irq_chip = &axp22x_regmap_irq_chip;
+               break;
        case AXP288_ID:
                axp20x->cells = axp288_cells;
                axp20x->nr_cells = ARRAY_SIZE(axp288_cells);
index c4aecc6f83739779880f85ae6b5ac659a153c095..0eee635420383dbc2765490c21dd47aa18d72ac4 100644 (file)
  * battery charging and regulator control, firmware update.
  */
 
+#include <linux/of_platform.h>
 #include <linux/interrupt.h>
 #include <linux/slab.h>
 #include <linux/module.h>
 #include <linux/mfd/core.h>
 #include <linux/mfd/cros_ec.h>
-#include <linux/mfd/cros_ec_commands.h>
-#include <linux/delay.h>
 
-#define EC_COMMAND_RETRIES     50
+#define CROS_EC_DEV_EC_INDEX 0
+#define CROS_EC_DEV_PD_INDEX 1
 
-int cros_ec_prepare_tx(struct cros_ec_device *ec_dev,
-                      struct cros_ec_command *msg)
-{
-       uint8_t *out;
-       int csum, i;
-
-       BUG_ON(msg->outsize > EC_PROTO2_MAX_PARAM_SIZE);
-       out = ec_dev->dout;
-       out[0] = EC_CMD_VERSION0 + msg->version;
-       out[1] = msg->command;
-       out[2] = msg->outsize;
-       csum = out[0] + out[1] + out[2];
-       for (i = 0; i < msg->outsize; i++)
-               csum += out[EC_MSG_TX_HEADER_BYTES + i] = msg->outdata[i];
-       out[EC_MSG_TX_HEADER_BYTES + msg->outsize] = (uint8_t)(csum & 0xff);
-
-       return EC_MSG_TX_PROTO_BYTES + msg->outsize;
-}
-EXPORT_SYMBOL(cros_ec_prepare_tx);
-
-int cros_ec_check_result(struct cros_ec_device *ec_dev,
-                        struct cros_ec_command *msg)
-{
-       switch (msg->result) {
-       case EC_RES_SUCCESS:
-               return 0;
-       case EC_RES_IN_PROGRESS:
-               dev_dbg(ec_dev->dev, "command 0x%02x in progress\n",
-                       msg->command);
-               return -EAGAIN;
-       default:
-               dev_dbg(ec_dev->dev, "command 0x%02x returned %d\n",
-                       msg->command, msg->result);
-               return 0;
-       }
-}
-EXPORT_SYMBOL(cros_ec_check_result);
-
-int cros_ec_cmd_xfer(struct cros_ec_device *ec_dev,
-                    struct cros_ec_command *msg)
-{
-       int ret;
-
-       mutex_lock(&ec_dev->lock);
-       ret = ec_dev->cmd_xfer(ec_dev, msg);
-       if (msg->result == EC_RES_IN_PROGRESS) {
-               int i;
-               struct cros_ec_command status_msg = { };
-               struct ec_response_get_comms_status *status;
-
-               status_msg.command = EC_CMD_GET_COMMS_STATUS;
-               status_msg.insize = sizeof(*status);
-
-               /*
-                * Query the EC's status until it's no longer busy or
-                * we encounter an error.
-                */
-               for (i = 0; i < EC_COMMAND_RETRIES; i++) {
-                       usleep_range(10000, 11000);
-
-                       ret = ec_dev->cmd_xfer(ec_dev, &status_msg);
-                       if (ret < 0)
-                               break;
+static struct cros_ec_platform ec_p = {
+       .ec_name = CROS_EC_DEV_NAME,
+       .cmd_offset = EC_CMD_PASSTHRU_OFFSET(CROS_EC_DEV_EC_INDEX),
+};
 
-                       msg->result = status_msg.result;
-                       if (status_msg.result != EC_RES_SUCCESS)
-                               break;
+static struct cros_ec_platform pd_p = {
+       .ec_name = CROS_EC_DEV_PD_NAME,
+       .cmd_offset = EC_CMD_PASSTHRU_OFFSET(CROS_EC_DEV_PD_INDEX),
+};
 
-                       status = (struct ec_response_get_comms_status *)
-                                status_msg.indata;
-                       if (!(status->flags & EC_COMMS_STATUS_PROCESSING))
-                               break;
-               }
-       }
-       mutex_unlock(&ec_dev->lock);
+static const struct mfd_cell ec_cell = {
+       .name = "cros-ec-ctl",
+       .platform_data = &ec_p,
+       .pdata_size = sizeof(ec_p),
+};
 
-       return ret;
-}
-EXPORT_SYMBOL(cros_ec_cmd_xfer);
-
-static const struct mfd_cell cros_devs[] = {
-       {
-               .name = "cros-ec-keyb",
-               .id = 1,
-               .of_compatible = "google,cros-ec-keyb",
-       },
-       {
-               .name = "cros-ec-i2c-tunnel",
-               .id = 2,
-               .of_compatible = "google,cros-ec-i2c-tunnel",
-       },
-       {
-               .name = "cros-ec-ctl",
-               .id = 3,
-       },
+static const struct mfd_cell ec_pd_cell = {
+       .name = "cros-ec-ctl",
+       .platform_data = &pd_p,
+       .pdata_size = sizeof(pd_p),
 };
 
 int cros_ec_register(struct cros_ec_device *ec_dev)
@@ -129,27 +54,59 @@ int cros_ec_register(struct cros_ec_device *ec_dev)
        struct device *dev = ec_dev->dev;
        int err = 0;
 
-       if (ec_dev->din_size) {
-               ec_dev->din = devm_kzalloc(dev, ec_dev->din_size, GFP_KERNEL);
-               if (!ec_dev->din)
-                       return -ENOMEM;
-       }
-       if (ec_dev->dout_size) {
-               ec_dev->dout = devm_kzalloc(dev, ec_dev->dout_size, GFP_KERNEL);
-               if (!ec_dev->dout)
-                       return -ENOMEM;
-       }
+       ec_dev->max_request = sizeof(struct ec_params_hello);
+       ec_dev->max_response = sizeof(struct ec_response_get_protocol_info);
+       ec_dev->max_passthru = 0;
+
+       ec_dev->din = devm_kzalloc(dev, ec_dev->din_size, GFP_KERNEL);
+       if (!ec_dev->din)
+               return -ENOMEM;
+
+       ec_dev->dout = devm_kzalloc(dev, ec_dev->dout_size, GFP_KERNEL);
+       if (!ec_dev->dout)
+               return -ENOMEM;
 
        mutex_init(&ec_dev->lock);
 
-       err = mfd_add_devices(dev, 0, cros_devs,
-                             ARRAY_SIZE(cros_devs),
+       cros_ec_query_all(ec_dev);
+
+       err = mfd_add_devices(ec_dev->dev, PLATFORM_DEVID_AUTO, &ec_cell, 1,
                              NULL, ec_dev->irq, NULL);
        if (err) {
-               dev_err(dev, "failed to add mfd devices\n");
+               dev_err(dev,
+                       "Failed to register Embedded Controller subdevice %d\n",
+                       err);
                return err;
        }
 
+       if (ec_dev->max_passthru) {
+               /*
+                * Register a PD device as well on top of this device.
+                * We make the following assumptions:
+                * - behind an EC, we have a pd
+                * - only one device added.
+                * - the EC is responsive at init time (it is not true for a
+                *   sensor hub.
+                */
+               err = mfd_add_devices(ec_dev->dev, PLATFORM_DEVID_AUTO,
+                                     &ec_pd_cell, 1, NULL, ec_dev->irq, NULL);
+               if (err) {
+                       dev_err(dev,
+                               "Failed to register Power Delivery subdevice %d\n",
+                               err);
+                       return err;
+               }
+       }
+
+       if (IS_ENABLED(CONFIG_OF) && dev->of_node) {
+               err = of_platform_populate(dev->of_node, NULL, NULL, dev);
+               if (err) {
+                       mfd_remove_devices(dev);
+                       dev_err(dev, "Failed to register sub-devices\n");
+                       return err;
+               }
+       }
+
        dev_info(dev, "Chrome EC device registered\n");
 
        return 0;
index c0c30f4f946f98bd5e7dbd066e3511df0bfb7f89..b9a0963ca5c3d0acee362b02b309d3108073b7ee 100644 (file)
@@ -13,6 +13,7 @@
  * GNU General Public License for more details.
  */
 
+#include <linux/delay.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/i2c.h>
 #include <linux/platform_device.h>
 #include <linux/slab.h>
 
+/**
+ * Request format for protocol v3
+ * byte 0      0xda (EC_COMMAND_PROTOCOL_3)
+ * byte 1-8    struct ec_host_request
+ * byte 10-    response data
+ */
+struct ec_host_request_i2c {
+       /* Always 0xda to backward compatible with v2 struct */
+       uint8_t  command_protocol;
+       struct ec_host_request ec_request;
+} __packed;
+
+
+/*
+ * Response format for protocol v3
+ * byte 0      result code
+ * byte 1      packet_length
+ * byte 2-9    struct ec_host_response
+ * byte 10-    response data
+ */
+struct ec_host_response_i2c {
+       uint8_t result;
+       uint8_t packet_length;
+       struct ec_host_response ec_response;
+} __packed;
+
 static inline struct cros_ec_device *to_ec_dev(struct device *dev)
 {
        struct i2c_client *client = to_i2c_client(dev);
@@ -29,6 +56,134 @@ static inline struct cros_ec_device *to_ec_dev(struct device *dev)
        return i2c_get_clientdata(client);
 }
 
+static int cros_ec_pkt_xfer_i2c(struct cros_ec_device *ec_dev,
+                               struct cros_ec_command *msg)
+{
+       struct i2c_client *client = ec_dev->priv;
+       int ret = -ENOMEM;
+       int i;
+       int packet_len;
+       u8 *out_buf = NULL;
+       u8 *in_buf = NULL;
+       u8 sum;
+       struct i2c_msg i2c_msg[2];
+       struct ec_host_response *ec_response;
+       struct ec_host_request_i2c *ec_request_i2c;
+       struct ec_host_response_i2c *ec_response_i2c;
+       int request_header_size = sizeof(struct ec_host_request_i2c);
+       int response_header_size = sizeof(struct ec_host_response_i2c);
+
+       i2c_msg[0].addr = client->addr;
+       i2c_msg[0].flags = 0;
+       i2c_msg[1].addr = client->addr;
+       i2c_msg[1].flags = I2C_M_RD;
+
+       packet_len = msg->insize + response_header_size;
+       BUG_ON(packet_len > ec_dev->din_size);
+       in_buf = ec_dev->din;
+       i2c_msg[1].len = packet_len;
+       i2c_msg[1].buf = (char *) in_buf;
+
+       packet_len = msg->outsize + request_header_size;
+       BUG_ON(packet_len > ec_dev->dout_size);
+       out_buf = ec_dev->dout;
+       i2c_msg[0].len = packet_len;
+       i2c_msg[0].buf = (char *) out_buf;
+
+       /* create request data */
+       ec_request_i2c = (struct ec_host_request_i2c *) out_buf;
+       ec_request_i2c->command_protocol = EC_COMMAND_PROTOCOL_3;
+
+       ec_dev->dout++;
+       ret = cros_ec_prepare_tx(ec_dev, msg);
+       ec_dev->dout--;
+
+       /* send command to EC and read answer */
+       ret = i2c_transfer(client->adapter, i2c_msg, 2);
+       if (ret < 0) {
+               dev_dbg(ec_dev->dev, "i2c transfer failed: %d\n", ret);
+               goto done;
+       } else if (ret != 2) {
+               dev_err(ec_dev->dev, "failed to get response: %d\n", ret);
+               ret = -EIO;
+               goto done;
+       }
+
+       ec_response_i2c = (struct ec_host_response_i2c *) in_buf;
+       msg->result = ec_response_i2c->result;
+       ec_response = &ec_response_i2c->ec_response;
+
+       switch (msg->result) {
+       case EC_RES_SUCCESS:
+               break;
+       case EC_RES_IN_PROGRESS:
+               ret = -EAGAIN;
+               dev_dbg(ec_dev->dev, "command 0x%02x in progress\n",
+                       msg->command);
+               goto done;
+
+       default:
+               dev_dbg(ec_dev->dev, "command 0x%02x returned %d\n",
+                       msg->command, msg->result);
+               /*
+                * When we send v3 request to v2 ec, ec won't recognize the
+                * 0xda (EC_COMMAND_PROTOCOL_3) and will return with status
+                * EC_RES_INVALID_COMMAND with zero data length.
+                *
+                * In case of invalid command for v3 protocol the data length
+                * will be at least sizeof(struct ec_host_response)
+                */
+               if (ec_response_i2c->result == EC_RES_INVALID_COMMAND &&
+                   ec_response_i2c->packet_length == 0) {
+                       ret = -EPROTONOSUPPORT;
+                       goto done;
+               }
+       }
+
+       if (ec_response_i2c->packet_length < sizeof(struct ec_host_response)) {
+               dev_err(ec_dev->dev,
+                       "response of %u bytes too short; not a full header\n",
+                       ec_response_i2c->packet_length);
+               ret = -EBADMSG;
+               goto done;
+       }
+
+       if (msg->insize < ec_response->data_len) {
+               dev_err(ec_dev->dev,
+                       "response data size is too large: expected %u, got %u\n",
+                       msg->insize,
+                       ec_response->data_len);
+               ret = -EMSGSIZE;
+               goto done;
+       }
+
+       /* copy response packet payload and compute checksum */
+       sum = 0;
+       for (i = 0; i < sizeof(struct ec_host_response); i++)
+               sum += ((u8 *)ec_response)[i];
+
+       memcpy(msg->data,
+              in_buf + response_header_size,
+              ec_response->data_len);
+       for (i = 0; i < ec_response->data_len; i++)
+               sum += msg->data[i];
+
+       /* All bytes should sum to zero */
+       if (sum) {
+               dev_err(ec_dev->dev, "bad packet checksum\n");
+               ret = -EBADMSG;
+               goto done;
+       }
+
+       ret = ec_response->data_len;
+
+done:
+       if (msg->command == EC_CMD_REBOOT_EC)
+               msleep(EC_REBOOT_DELAY_MS);
+
+       return ret;
+}
+
 static int cros_ec_cmd_xfer_i2c(struct cros_ec_device *ec_dev,
                                struct cros_ec_command *msg)
 {
@@ -76,7 +231,7 @@ static int cros_ec_cmd_xfer_i2c(struct cros_ec_device *ec_dev,
        /* copy message payload and compute checksum */
        sum = out_buf[0] + out_buf[1] + out_buf[2];
        for (i = 0; i < msg->outsize; i++) {
-               out_buf[3 + i] = msg->outdata[i];
+               out_buf[3 + i] = msg->data[i];
                sum += out_buf[3 + i];
        }
        out_buf[3 + msg->outsize] = sum;
@@ -109,7 +264,7 @@ static int cros_ec_cmd_xfer_i2c(struct cros_ec_device *ec_dev,
        /* copy response packet payload and compute checksum */
        sum = in_buf[0] + in_buf[1];
        for (i = 0; i < len; i++) {
-               msg->indata[i] = in_buf[2 + i];
+               msg->data[i] = in_buf[2 + i];
                sum += in_buf[2 + i];
        }
        dev_dbg(ec_dev->dev, "packet: %*ph, sum = %02x\n",
@@ -121,9 +276,12 @@ static int cros_ec_cmd_xfer_i2c(struct cros_ec_device *ec_dev,
        }
 
        ret = len;
- done:
+done:
        kfree(in_buf);
        kfree(out_buf);
+       if (msg->command == EC_CMD_REBOOT_EC)
+               msleep(EC_REBOOT_DELAY_MS);
+
        return ret;
 }
 
@@ -143,9 +301,11 @@ static int cros_ec_i2c_probe(struct i2c_client *client,
        ec_dev->priv = client;
        ec_dev->irq = client->irq;
        ec_dev->cmd_xfer = cros_ec_cmd_xfer_i2c;
-       ec_dev->ec_name = client->name;
+       ec_dev->pkt_xfer = cros_ec_pkt_xfer_i2c;
        ec_dev->phys_name = client->adapter->name;
-       ec_dev->parent = &client->dev;
+       ec_dev->din_size = sizeof(struct ec_host_response_i2c) +
+                          sizeof(struct ec_response_get_protocol_info);
+       ec_dev->dout_size = sizeof(struct ec_host_request_i2c);
 
        err = cros_ec_register(ec_dev);
        if (err) {
index bf6e08e8013e30ad0e78f1d532954a4c13388f8d..16f228dc243f3740f241a5f1315820e23f08b978 100644 (file)
   */
 #define EC_SPI_RECOVERY_TIME_NS        (200 * 1000)
 
-/*
- * The EC is unresponsive for a time after a reboot command.  Add a
- * simple delay to make sure that the bus stays locked.
- */
-#define EC_REBOOT_DELAY_MS     50
-
 /**
  * struct cros_ec_spi - information about a SPI-connected EC
  *
  * @spi: SPI device we are connected to
  * @last_transfer_ns: time that we last finished a transfer, or 0 if there
  *     if no record
+ * @start_of_msg_delay: used to set the delay_usecs on the spi_transfer that
+ *      is sent when we want to turn on CS at the start of a transaction.
  * @end_of_msg_delay: used to set the delay_usecs on the spi_transfer that
  *      is sent when we want to turn off CS at the end of a transaction.
  */
 struct cros_ec_spi {
        struct spi_device *spi;
        s64 last_transfer_ns;
+       unsigned int start_of_msg_delay;
        unsigned int end_of_msg_delay;
 };
 
 static void debug_packet(struct device *dev, const char *name, u8 *ptr,
-                         int len)
+                        int len)
 {
 #ifdef DEBUG
        int i;
@@ -100,6 +97,172 @@ static void debug_packet(struct device *dev, const char *name, u8 *ptr,
 #endif
 }
 
+static int terminate_request(struct cros_ec_device *ec_dev)
+{
+       struct cros_ec_spi *ec_spi = ec_dev->priv;
+       struct spi_message msg;
+       struct spi_transfer trans;
+       int ret;
+
+       /*
+        * Turn off CS, possibly adding a delay to ensure the rising edge
+        * doesn't come too soon after the end of the data.
+        */
+       spi_message_init(&msg);
+       memset(&trans, 0, sizeof(trans));
+       trans.delay_usecs = ec_spi->end_of_msg_delay;
+       spi_message_add_tail(&trans, &msg);
+
+       ret = spi_sync(ec_spi->spi, &msg);
+
+       /* Reset end-of-response timer */
+       ec_spi->last_transfer_ns = ktime_get_ns();
+       if (ret < 0) {
+               dev_err(ec_dev->dev,
+                       "cs-deassert spi transfer failed: %d\n",
+                       ret);
+       }
+
+       return ret;
+}
+
+/**
+ * receive_n_bytes - receive n bytes from the EC.
+ *
+ * Assumes buf is a pointer into the ec_dev->din buffer
+ */
+static int receive_n_bytes(struct cros_ec_device *ec_dev, u8 *buf, int n)
+{
+       struct cros_ec_spi *ec_spi = ec_dev->priv;
+       struct spi_transfer trans;
+       struct spi_message msg;
+       int ret;
+
+       BUG_ON(buf - ec_dev->din + n > ec_dev->din_size);
+
+       memset(&trans, 0, sizeof(trans));
+       trans.cs_change = 1;
+       trans.rx_buf = buf;
+       trans.len = n;
+
+       spi_message_init(&msg);
+       spi_message_add_tail(&trans, &msg);
+       ret = spi_sync(ec_spi->spi, &msg);
+       if (ret < 0)
+               dev_err(ec_dev->dev, "spi transfer failed: %d\n", ret);
+
+       return ret;
+}
+
+/**
+ * cros_ec_spi_receive_packet - Receive a packet from the EC.
+ *
+ * This function has two phases: reading the preamble bytes (since if we read
+ * data from the EC before it is ready to send, we just get preamble) and
+ * reading the actual message.
+ *
+ * The received data is placed into ec_dev->din.
+ *
+ * @ec_dev: ChromeOS EC device
+ * @need_len: Number of message bytes we need to read
+ */
+static int cros_ec_spi_receive_packet(struct cros_ec_device *ec_dev,
+                                     int need_len)
+{
+       struct ec_host_response *response;
+       u8 *ptr, *end;
+       int ret;
+       unsigned long deadline;
+       int todo;
+
+       BUG_ON(EC_MSG_PREAMBLE_COUNT > ec_dev->din_size);
+
+       /* Receive data until we see the header byte */
+       deadline = jiffies + msecs_to_jiffies(EC_MSG_DEADLINE_MS);
+       while (true) {
+               unsigned long start_jiffies = jiffies;
+
+               ret = receive_n_bytes(ec_dev,
+                                     ec_dev->din,
+                                     EC_MSG_PREAMBLE_COUNT);
+               if (ret < 0)
+                       return ret;
+
+               ptr = ec_dev->din;
+               for (end = ptr + EC_MSG_PREAMBLE_COUNT; ptr != end; ptr++) {
+                       if (*ptr == EC_SPI_FRAME_START) {
+                               dev_dbg(ec_dev->dev, "msg found at %zd\n",
+                                       ptr - ec_dev->din);
+                               break;
+                       }
+               }
+               if (ptr != end)
+                       break;
+
+               /*
+                * Use the time at the start of the loop as a timeout.  This
+                * gives us one last shot at getting the transfer and is useful
+                * in case we got context switched out for a while.
+                */
+               if (time_after(start_jiffies, deadline)) {
+                       dev_warn(ec_dev->dev, "EC failed to respond in time\n");
+                       return -ETIMEDOUT;
+               }
+       }
+
+       /*
+        * ptr now points to the header byte. Copy any valid data to the
+        * start of our buffer
+        */
+       todo = end - ++ptr;
+       BUG_ON(todo < 0 || todo > ec_dev->din_size);
+       todo = min(todo, need_len);
+       memmove(ec_dev->din, ptr, todo);
+       ptr = ec_dev->din + todo;
+       dev_dbg(ec_dev->dev, "need %d, got %d bytes from preamble\n",
+               need_len, todo);
+       need_len -= todo;
+
+       /* If the entire response struct wasn't read, get the rest of it. */
+       if (todo < sizeof(*response)) {
+               ret = receive_n_bytes(ec_dev, ptr, sizeof(*response) - todo);
+               if (ret < 0)
+                       return -EBADMSG;
+               ptr += (sizeof(*response) - todo);
+               todo = sizeof(*response);
+       }
+
+       response = (struct ec_host_response *)ec_dev->din;
+
+       /* Abort if data_len is too large. */
+       if (response->data_len > ec_dev->din_size)
+               return -EMSGSIZE;
+
+       /* Receive data until we have it all */
+       while (need_len > 0) {
+               /*
+                * We can't support transfers larger than the SPI FIFO size
+                * unless we have DMA. We don't have DMA on the ISP SPI ports
+                * for Exynos. We need a way of asking SPI driver for
+                * maximum-supported transfer size.
+                */
+               todo = min(need_len, 256);
+               dev_dbg(ec_dev->dev, "loop, todo=%d, need_len=%d, ptr=%zd\n",
+                       todo, need_len, ptr - ec_dev->din);
+
+               ret = receive_n_bytes(ec_dev, ptr, todo);
+               if (ret < 0)
+                       return ret;
+
+               ptr += todo;
+               need_len -= todo;
+       }
+
+       dev_dbg(ec_dev->dev, "loop done, ptr=%zd\n", ptr - ec_dev->din);
+
+       return 0;
+}
+
 /**
  * cros_ec_spi_receive_response - Receive a response from the EC.
  *
@@ -115,34 +278,27 @@ static void debug_packet(struct device *dev, const char *name, u8 *ptr,
 static int cros_ec_spi_receive_response(struct cros_ec_device *ec_dev,
                                        int need_len)
 {
-       struct cros_ec_spi *ec_spi = ec_dev->priv;
-       struct spi_transfer trans;
-       struct spi_message msg;
        u8 *ptr, *end;
        int ret;
        unsigned long deadline;
        int todo;
 
+       BUG_ON(EC_MSG_PREAMBLE_COUNT > ec_dev->din_size);
+
        /* Receive data until we see the header byte */
        deadline = jiffies + msecs_to_jiffies(EC_MSG_DEADLINE_MS);
        while (true) {
                unsigned long start_jiffies = jiffies;
 
-               memset(&trans, 0, sizeof(trans));
-               trans.cs_change = 1;
-               trans.rx_buf = ptr = ec_dev->din;
-               trans.len = EC_MSG_PREAMBLE_COUNT;
-
-               spi_message_init(&msg);
-               spi_message_add_tail(&trans, &msg);
-               ret = spi_sync(ec_spi->spi, &msg);
-               if (ret < 0) {
-                       dev_err(ec_dev->dev, "spi transfer failed: %d\n", ret);
+               ret = receive_n_bytes(ec_dev,
+                                     ec_dev->din,
+                                     EC_MSG_PREAMBLE_COUNT);
+               if (ret < 0)
                        return ret;
-               }
 
+               ptr = ec_dev->din;
                for (end = ptr + EC_MSG_PREAMBLE_COUNT; ptr != end; ptr++) {
-                       if (*ptr == EC_MSG_HEADER) {
+                       if (*ptr == EC_SPI_FRAME_START) {
                                dev_dbg(ec_dev->dev, "msg found at %zd\n",
                                        ptr - ec_dev->din);
                                break;
@@ -187,21 +343,9 @@ static int cros_ec_spi_receive_response(struct cros_ec_device *ec_dev,
                dev_dbg(ec_dev->dev, "loop, todo=%d, need_len=%d, ptr=%zd\n",
                        todo, need_len, ptr - ec_dev->din);
 
-               memset(&trans, 0, sizeof(trans));
-               trans.cs_change = 1;
-               trans.rx_buf = ptr;
-               trans.len = todo;
-               spi_message_init(&msg);
-               spi_message_add_tail(&trans, &msg);
-
-               /* send command to EC and read answer */
-               BUG_ON((u8 *)trans.rx_buf - ec_dev->din + todo >
-                               ec_dev->din_size);
-               ret = spi_sync(ec_spi->spi, &msg);
-               if (ret < 0) {
-                       dev_err(ec_dev->dev, "spi transfer failed: %d\n", ret);
+               ret = receive_n_bytes(ec_dev, ptr, todo);
+               if (ret < 0)
                        return ret;
-               }
 
                debug_packet(ec_dev->dev, "interim", ptr, todo);
                ptr += todo;
@@ -213,6 +357,138 @@ static int cros_ec_spi_receive_response(struct cros_ec_device *ec_dev,
        return 0;
 }
 
+/**
+ * cros_ec_pkt_xfer_spi - Transfer a packet over SPI and receive the reply
+ *
+ * @ec_dev: ChromeOS EC device
+ * @ec_msg: Message to transfer
+ */
+static int cros_ec_pkt_xfer_spi(struct cros_ec_device *ec_dev,
+                               struct cros_ec_command *ec_msg)
+{
+       struct ec_host_request *request;
+       struct ec_host_response *response;
+       struct cros_ec_spi *ec_spi = ec_dev->priv;
+       struct spi_transfer trans, trans_delay;
+       struct spi_message msg;
+       int i, len;
+       u8 *ptr;
+       u8 *rx_buf;
+       u8 sum;
+       int ret = 0, final_ret;
+
+       len = cros_ec_prepare_tx(ec_dev, ec_msg);
+       request = (struct ec_host_request *)ec_dev->dout;
+       dev_dbg(ec_dev->dev, "prepared, len=%d\n", len);
+
+       /* If it's too soon to do another transaction, wait */
+       if (ec_spi->last_transfer_ns) {
+               unsigned long delay;    /* The delay completed so far */
+
+               delay = ktime_get_ns() - ec_spi->last_transfer_ns;
+               if (delay < EC_SPI_RECOVERY_TIME_NS)
+                       ndelay(EC_SPI_RECOVERY_TIME_NS - delay);
+       }
+
+       rx_buf = kzalloc(len, GFP_KERNEL);
+       if (!rx_buf) {
+               ret = -ENOMEM;
+               goto exit;
+       }
+
+       /*
+        * Leave a gap between CS assertion and clocking of data to allow the
+        * EC time to wakeup.
+        */
+       spi_message_init(&msg);
+       if (ec_spi->start_of_msg_delay) {
+               memset(&trans_delay, 0, sizeof(trans_delay));
+               trans_delay.delay_usecs = ec_spi->start_of_msg_delay;
+               spi_message_add_tail(&trans_delay, &msg);
+       }
+
+       /* Transmit phase - send our message */
+       memset(&trans, 0, sizeof(trans));
+       trans.tx_buf = ec_dev->dout;
+       trans.rx_buf = rx_buf;
+       trans.len = len;
+       trans.cs_change = 1;
+       spi_message_add_tail(&trans, &msg);
+       ret = spi_sync(ec_spi->spi, &msg);
+
+       /* Get the response */
+       if (!ret) {
+               /* Verify that EC can process command */
+               for (i = 0; i < len; i++) {
+                       switch (rx_buf[i]) {
+                       case EC_SPI_PAST_END:
+                       case EC_SPI_RX_BAD_DATA:
+                       case EC_SPI_NOT_READY:
+                               ret = -EAGAIN;
+                               ec_msg->result = EC_RES_IN_PROGRESS;
+                       default:
+                               break;
+                       }
+                       if (ret)
+                               break;
+               }
+               if (!ret)
+                       ret = cros_ec_spi_receive_packet(ec_dev,
+                                       ec_msg->insize + sizeof(*response));
+       } else {
+               dev_err(ec_dev->dev, "spi transfer failed: %d\n", ret);
+       }
+
+       final_ret = terminate_request(ec_dev);
+       if (!ret)
+               ret = final_ret;
+       if (ret < 0)
+               goto exit;
+
+       ptr = ec_dev->din;
+
+       /* check response error code */
+       response = (struct ec_host_response *)ptr;
+       ec_msg->result = response->result;
+
+       ret = cros_ec_check_result(ec_dev, ec_msg);
+       if (ret)
+               goto exit;
+
+       len = response->data_len;
+       sum = 0;
+       if (len > ec_msg->insize) {
+               dev_err(ec_dev->dev, "packet too long (%d bytes, expected %d)",
+                       len, ec_msg->insize);
+               ret = -EMSGSIZE;
+               goto exit;
+       }
+
+       for (i = 0; i < sizeof(*response); i++)
+               sum += ptr[i];
+
+       /* copy response packet payload and compute checksum */
+       memcpy(ec_msg->data, ptr + sizeof(*response), len);
+       for (i = 0; i < len; i++)
+               sum += ec_msg->data[i];
+
+       if (sum) {
+               dev_err(ec_dev->dev,
+                       "bad packet checksum, calculated %x\n",
+                       sum);
+               ret = -EBADMSG;
+               goto exit;
+       }
+
+       ret = len;
+exit:
+       kfree(rx_buf);
+       if (ec_msg->command == EC_CMD_REBOOT_EC)
+               msleep(EC_REBOOT_DELAY_MS);
+
+       return ret;
+}
+
 /**
  * cros_ec_cmd_xfer_spi - Transfer a message over SPI and receive the reply
  *
@@ -227,6 +503,7 @@ static int cros_ec_cmd_xfer_spi(struct cros_ec_device *ec_dev,
        struct spi_message msg;
        int i, len;
        u8 *ptr;
+       u8 *rx_buf;
        int sum;
        int ret = 0, final_ret;
 
@@ -242,10 +519,17 @@ static int cros_ec_cmd_xfer_spi(struct cros_ec_device *ec_dev,
                        ndelay(EC_SPI_RECOVERY_TIME_NS - delay);
        }
 
+       rx_buf = kzalloc(len, GFP_KERNEL);
+       if (!rx_buf) {
+               ret = -ENOMEM;
+               goto exit;
+       }
+
        /* Transmit phase - send our message */
        debug_packet(ec_dev->dev, "out", ec_dev->dout, len);
        memset(&trans, 0, sizeof(trans));
        trans.tx_buf = ec_dev->dout;
+       trans.rx_buf = rx_buf;
        trans.len = len;
        trans.cs_change = 1;
        spi_message_init(&msg);
@@ -254,29 +538,32 @@ static int cros_ec_cmd_xfer_spi(struct cros_ec_device *ec_dev,
 
        /* Get the response */
        if (!ret) {
-               ret = cros_ec_spi_receive_response(ec_dev,
-                               ec_msg->insize + EC_MSG_TX_PROTO_BYTES);
+               /* Verify that EC can process command */
+               for (i = 0; i < len; i++) {
+                       switch (rx_buf[i]) {
+                       case EC_SPI_PAST_END:
+                       case EC_SPI_RX_BAD_DATA:
+                       case EC_SPI_NOT_READY:
+                               ret = -EAGAIN;
+                               ec_msg->result = EC_RES_IN_PROGRESS;
+                       default:
+                               break;
+                       }
+                       if (ret)
+                               break;
+               }
+               if (!ret)
+                       ret = cros_ec_spi_receive_response(ec_dev,
+                                       ec_msg->insize + EC_MSG_TX_PROTO_BYTES);
        } else {
                dev_err(ec_dev->dev, "spi transfer failed: %d\n", ret);
        }
 
-       /*
-        * Turn off CS, possibly adding a delay to ensure the rising edge
-        * doesn't come too soon after the end of the data.
-        */
-       spi_message_init(&msg);
-       memset(&trans, 0, sizeof(trans));
-       trans.delay_usecs = ec_spi->end_of_msg_delay;
-       spi_message_add_tail(&trans, &msg);
-
-       final_ret = spi_sync(ec_spi->spi, &msg);
-       ec_spi->last_transfer_ns = ktime_get_ns();
+       final_ret = terminate_request(ec_dev);
        if (!ret)
                ret = final_ret;
-       if (ret < 0) {
-               dev_err(ec_dev->dev, "spi transfer failed: %d\n", ret);
+       if (ret < 0)
                goto exit;
-       }
 
        ptr = ec_dev->din;
 
@@ -299,7 +586,7 @@ static int cros_ec_cmd_xfer_spi(struct cros_ec_device *ec_dev,
        for (i = 0; i < len; i++) {
                sum += ptr[i + 2];
                if (ec_msg->insize)
-                       ec_msg->indata[i] = ptr[i + 2];
+                       ec_msg->data[i] = ptr[i + 2];
        }
        sum &= 0xff;
 
@@ -315,6 +602,7 @@ static int cros_ec_cmd_xfer_spi(struct cros_ec_device *ec_dev,
 
        ret = len;
 exit:
+       kfree(rx_buf);
        if (ec_msg->command == EC_CMD_REBOOT_EC)
                msleep(EC_REBOOT_DELAY_MS);
 
@@ -327,6 +615,10 @@ static void cros_ec_spi_dt_probe(struct cros_ec_spi *ec_spi, struct device *dev)
        u32 val;
        int ret;
 
+       ret = of_property_read_u32(np, "google,cros-ec-spi-pre-delay", &val);
+       if (!ret)
+               ec_spi->start_of_msg_delay = val;
+
        ret = of_property_read_u32(np, "google,cros-ec-spi-msg-delay", &val);
        if (!ret)
                ec_spi->end_of_msg_delay = val;
@@ -361,11 +653,13 @@ static int cros_ec_spi_probe(struct spi_device *spi)
        ec_dev->priv = ec_spi;
        ec_dev->irq = spi->irq;
        ec_dev->cmd_xfer = cros_ec_cmd_xfer_spi;
-       ec_dev->ec_name = ec_spi->spi->modalias;
+       ec_dev->pkt_xfer = cros_ec_pkt_xfer_spi;
        ec_dev->phys_name = dev_name(&ec_spi->spi->dev);
-       ec_dev->parent = &ec_spi->spi->dev;
-       ec_dev->din_size = EC_MSG_BYTES + EC_MSG_PREAMBLE_COUNT;
-       ec_dev->dout_size = EC_MSG_BYTES;
+       ec_dev->din_size = EC_MSG_PREAMBLE_COUNT +
+                          sizeof(struct ec_host_response) +
+                          sizeof(struct ec_response_get_protocol_info);
+       ec_dev->dout_size = sizeof(struct ec_host_request);
+
 
        err = cros_ec_register(ec_dev);
        if (err) {
index e65ca194fa98283a38539ab3b68393976a8c1604..f4cb4613140bf91ac31ffd396ec701bcdbde174e 100644 (file)
@@ -35,7 +35,7 @@
 #define DA9052_IRQ_MASK_POS_7          0x40
 #define DA9052_IRQ_MASK_POS_8          0x80
 
-static struct regmap_irq da9052_irqs[] = {
+static const struct regmap_irq da9052_irqs[] = {
        [DA9052_IRQ_DCIN] = {
                .reg_offset = 0,
                .mask = DA9052_IRQ_MASK_POS_1,
@@ -166,7 +166,7 @@ static struct regmap_irq da9052_irqs[] = {
        },
 };
 
-static struct regmap_irq_chip da9052_regmap_irq_chip = {
+static const struct regmap_irq_chip da9052_regmap_irq_chip = {
        .name = "da9052_irq",
        .status_base = DA9052_EVENT_A_REG,
        .mask_base = DA9052_IRQ_MASK_A_REG,
index b4d920c1ead1f2d92ce52433bc9c3a17b73fca7d..177e65a12c127343c3c4f6109ee98989ca9fc3d5 100644 (file)
@@ -222,7 +222,7 @@ static bool da9055_register_volatile(struct device *dev, unsigned int reg)
        }
 }
 
-static struct regmap_irq da9055_irqs[] = {
+static const struct regmap_irq da9055_irqs[] = {
        [DA9055_IRQ_NONKEY] = {
                .reg_offset = 0,
                .mask = DA9055_IRQ_NONKEY_MASK,
@@ -245,7 +245,7 @@ static struct regmap_irq da9055_irqs[] = {
        },
 };
 
-struct regmap_config da9055_regmap_config = {
+const struct regmap_config da9055_regmap_config = {
        .reg_bits = 8,
        .val_bits = 8,
 
@@ -367,7 +367,7 @@ static const struct mfd_cell da9055_devs[] = {
        },
 };
 
-static struct regmap_irq_chip da9055_regmap_irq_chip = {
+static const struct regmap_irq_chip da9055_regmap_irq_chip = {
        .name = "da9055_irq",
        .status_base = DA9055_REG_EVENT_A,
        .mask_base = DA9055_REG_IRQ_MASK_A,
index facd3610ac77f3b3fe19470181e26c9f4335a340..af841c165787523971a7336a81023785662be4f5 100644 (file)
@@ -60,6 +60,7 @@ static struct resource da9063_rtc_resources[] = {
 
 static struct resource da9063_onkey_resources[] = {
        {
+               .name   = "ONKEY",
                .start  = DA9063_IRQ_ONKEY,
                .end    = DA9063_IRQ_ONKEY,
                .flags  = IORESOURCE_IRQ,
@@ -97,6 +98,7 @@ static const struct mfd_cell da9063_devs[] = {
                .name           = DA9063_DRVNAME_ONKEY,
                .num_resources  = ARRAY_SIZE(da9063_onkey_resources),
                .resources      = da9063_onkey_resources,
+               .of_compatible = "dlg,da9063-onkey",
        },
        {
                .name           = DA9063_DRVNAME_RTC,
@@ -109,12 +111,64 @@ static const struct mfd_cell da9063_devs[] = {
        },
 };
 
+static int da9063_clear_fault_log(struct da9063 *da9063)
+{
+       int ret = 0;
+       int fault_log = 0;
+
+       ret = regmap_read(da9063->regmap, DA9063_REG_FAULT_LOG, &fault_log);
+       if (ret < 0) {
+               dev_err(da9063->dev, "Cannot read FAULT_LOG.\n");
+               return -EIO;
+       }
+
+       if (fault_log) {
+               if (fault_log & DA9063_TWD_ERROR)
+                       dev_dbg(da9063->dev,
+                               "Fault log entry detected: DA9063_TWD_ERROR\n");
+               if (fault_log & DA9063_POR)
+                       dev_dbg(da9063->dev,
+                               "Fault log entry detected: DA9063_POR\n");
+               if (fault_log & DA9063_VDD_FAULT)
+                       dev_dbg(da9063->dev,
+                               "Fault log entry detected: DA9063_VDD_FAULT\n");
+               if (fault_log & DA9063_VDD_START)
+                       dev_dbg(da9063->dev,
+                               "Fault log entry detected: DA9063_VDD_START\n");
+               if (fault_log & DA9063_TEMP_CRIT)
+                       dev_dbg(da9063->dev,
+                               "Fault log entry detected: DA9063_TEMP_CRIT\n");
+               if (fault_log & DA9063_KEY_RESET)
+                       dev_dbg(da9063->dev,
+                               "Fault log entry detected: DA9063_KEY_RESET\n");
+               if (fault_log & DA9063_NSHUTDOWN)
+                       dev_dbg(da9063->dev,
+                               "Fault log entry detected: DA9063_NSHUTDOWN\n");
+               if (fault_log & DA9063_WAIT_SHUT)
+                       dev_dbg(da9063->dev,
+                               "Fault log entry detected: DA9063_WAIT_SHUT\n");
+       }
+
+       ret = regmap_write(da9063->regmap,
+                          DA9063_REG_FAULT_LOG,
+                          fault_log);
+       if (ret < 0)
+               dev_err(da9063->dev,
+                       "Cannot reset FAULT_LOG values %d\n", ret);
+
+       return ret;
+}
+
 int da9063_device_init(struct da9063 *da9063, unsigned int irq)
 {
        struct da9063_pdata *pdata = da9063->dev->platform_data;
        int model, variant_id, variant_code;
        int ret;
 
+       ret = da9063_clear_fault_log(da9063);
+       if (ret < 0)
+               dev_err(da9063->dev, "Cannot clear fault log\n");
+
        if (pdata) {
                da9063->flags = pdata->flags;
                da9063->irq_base = pdata->irq_base;
index 822922602ce9d58c779d8f04284659f65241cddb..eaf1ec9208b20d56f65bb9885d7bc00684914d2e 100644 (file)
@@ -34,7 +34,7 @@ struct da9063_irq_data {
        u8 mask;
 };
 
-static struct regmap_irq da9063_irqs[] = {
+static const struct regmap_irq da9063_irqs[] = {
        /* DA9063 event A register */
        [DA9063_IRQ_ONKEY] = {
                .reg_offset = DA9063_REG_EVENT_A_OFFSET,
@@ -153,7 +153,7 @@ static struct regmap_irq da9063_irqs[] = {
        },
 };
 
-static struct regmap_irq_chip da9063_irq_chip = {
+static const struct regmap_irq_chip da9063_irq_chip = {
        .name = "da9063-irq",
        .irqs = da9063_irqs,
        .num_irqs = DA9063_NUM_IRQ,
index 5549817df32e7954776a8e2334d44196bd785e6e..94b9bbd1a69bab72e37ed5fef1f8a9e9166e105a 100644 (file)
@@ -164,7 +164,7 @@ void da9150_bulk_write(struct da9150 *da9150, u16 reg, int count, const u8 *buf)
 }
 EXPORT_SYMBOL_GPL(da9150_bulk_write);
 
-static struct regmap_irq da9150_irqs[] = {
+static const struct regmap_irq da9150_irqs[] = {
        [DA9150_IRQ_VBUS] = {
                .reg_offset = 0,
                .mask = DA9150_E_VBUS_MASK,
@@ -251,7 +251,7 @@ static struct regmap_irq da9150_irqs[] = {
        },
 };
 
-static struct regmap_irq_chip da9150_regmap_irq_chip = {
+static const struct regmap_irq_chip da9150_regmap_irq_chip = {
        .name = "da9150_irq",
        .status_base = DA9150_EVENT_E,
        .mask_base = DA9150_IRQ_MASK_E,
index cc1a404328c294d6ed119801e3dbf613c832c0ba..8b14740f9fca878c870a89f9588a51ae67cd14ed 100644 (file)
@@ -2659,7 +2659,7 @@ static int db8500_irq_map(struct irq_domain *d, unsigned int virq,
        return 0;
 }
 
-static struct irq_domain_ops db8500_irq_ops = {
+static const struct irq_domain_ops db8500_irq_ops = {
        .map    = db8500_irq_map,
        .xlate  = irq_domain_xlate_twocell,
 };
index ebb9cf19e347604f2ac57af7d412aa42ede83b48..b54baad3016456114da059f59375ea94a4177999 100644 (file)
@@ -564,7 +564,8 @@ static int htcpld_core_probe(struct platform_device *pdev)
                htcpld->chained_irq = res->start;
 
                /* Setup the chained interrupt handler */
-               flags = IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING;
+               flags = IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING |
+                       IRQF_ONESHOT;
                ret = request_threaded_irq(htcpld->chained_irq,
                                           NULL, htcpld_handler,
                                           flags, pdev->name, htcpld);
index 9498d6719847761f7f7a2b61d259ef8722a12739..ff2464bc172f2ef0f2fe28e30f5d09497e4e185a 100644 (file)
@@ -24,7 +24,7 @@ struct intel_soc_pmic_config {
        struct mfd_cell *cell_dev;
        int n_cell_devs;
        const struct regmap_config *regmap_config;
-       struct regmap_irq_chip *irq_chip;
+       const struct regmap_irq_chip *irq_chip;
 };
 
 extern struct intel_soc_pmic_config intel_soc_pmic_config_crc;
index 4cc1b324e971735615156cc76a6985d4efd5da08..7436075e89832b3271cbe337f968e4b6a9a2e988 100644 (file)
@@ -143,7 +143,7 @@ static const struct regmap_irq crystal_cove_irqs[] = {
        },
 };
 
-static struct regmap_irq_chip crystal_cove_irq_chip = {
+static const struct regmap_irq_chip crystal_cove_irq_chip = {
        .name = "Crystal Cove",
        .irqs = crystal_cove_irqs,
        .num_irqs = ARRAY_SIZE(crystal_cove_irqs),
index 23982dbf014d8518833b6f6e8eceea0bad07ed9e..a87f2b548f7120e48cb5235452ea5d1cca01df96 100644 (file)
@@ -151,7 +151,7 @@ static int lp8788_irq_map(struct irq_domain *d, unsigned int virq,
        return 0;
 }
 
-static struct irq_domain_ops lp8788_domain_ops = {
+static const struct irq_domain_ops lp8788_domain_ops = {
        .map = lp8788_irq_map,
 };
 
index 12d960a60ec459f1a2a063b228730070ddabc76e..8de34398abc085956831c40ed8cca6c85c3516d8 100644 (file)
@@ -934,8 +934,8 @@ gpe0_done:
        lpc_ich_enable_gpio_space(dev);
 
        lpc_ich_finalize_cell(dev, &lpc_ich_cells[LPC_GPIO]);
-       ret = mfd_add_devices(&dev->dev, -1, &lpc_ich_cells[LPC_GPIO],
-                             1, NULL, 0, NULL);
+       ret = mfd_add_devices(&dev->dev, PLATFORM_DEVID_AUTO,
+                             &lpc_ich_cells[LPC_GPIO], 1, NULL, 0, NULL);
 
 gpio_done:
        if (acpi_conflict)
@@ -1008,8 +1008,8 @@ static int lpc_ich_init_wdt(struct pci_dev *dev)
        }
 
        lpc_ich_finalize_cell(dev, &lpc_ich_cells[LPC_WDT]);
-       ret = mfd_add_devices(&dev->dev, -1, &lpc_ich_cells[LPC_WDT],
-                             1, NULL, 0, NULL);
+       ret = mfd_add_devices(&dev->dev, PLATFORM_DEVID_AUTO,
+                             &lpc_ich_cells[LPC_WDT], 1, NULL, 0, NULL);
 
 wdt_done:
        return ret;
index 97a787ab3d51d051a14e4abcf8ebdf96de40bc99..8520bd68c1ff9451f901c7b43f16340900025635 100644 (file)
@@ -658,7 +658,7 @@ static int max8925_irq_domain_map(struct irq_domain *d, unsigned int virq,
        return 0;
 }
 
-static struct irq_domain_ops max8925_irq_domain_ops = {
+static const struct irq_domain_ops max8925_irq_domain_ops = {
        .map    = max8925_irq_domain_map,
        .xlate  = irq_domain_xlate_onetwocell,
 };
index 43fa61413e935d5c5ce4a46016df34895ecb2b1d..d3025be57f3937d1482d1ac5429229cec8624d1b 100644 (file)
@@ -303,7 +303,7 @@ static int max8997_irq_domain_map(struct irq_domain *d, unsigned int irq,
        return 0;
 }
 
-static struct irq_domain_ops max8997_irq_domain_ops = {
+static const struct irq_domain_ops max8997_irq_domain_ops = {
        .map = max8997_irq_domain_map,
 };
 
index c469477eb7783fdc90b677507ced6975fdb95a5d..3702056628a84abc0b42e7487baffc366b8b32e4 100644 (file)
@@ -214,7 +214,7 @@ static int max8998_irq_domain_map(struct irq_domain *d, unsigned int irq,
        return 0;
 }
 
-static struct irq_domain_ops max8998_irq_domain_ops = {
+static const struct irq_domain_ops max8998_irq_domain_ops = {
        .map = max8998_irq_domain_map,
 };
 
index 25fd7116493a38efc7eddd141a8b469b603be072..3f9f4c874d2aa4c45505e2408c622e8f8c031af4 100644 (file)
@@ -163,7 +163,7 @@ int mc13xxx_irq_request(struct mc13xxx *mc13xxx, int irq,
        int virq = regmap_irq_get_virq(mc13xxx->irq_data, irq);
 
        return devm_request_threaded_irq(mc13xxx->dev, virq, NULL, handler,
-                                        0, name, dev);
+                                        IRQF_ONESHOT, name, dev);
 }
 EXPORT_SYMBOL(mc13xxx_irq_request);
 
index 1aed3b7b8d9be1e965904a0cd4f7923a2310de2e..14fd5cbcf0f2db27dbbea8378889addf4b2bb757 100644 (file)
@@ -207,9 +207,11 @@ static int mfd_add_device(struct device *parent, int id,
                }
 
                if (!cell->ignore_resource_conflicts) {
-                       ret = acpi_check_resource_conflict(&res[r]);
-                       if (ret)
-                               goto fail_alias;
+                       if (has_acpi_companion(&pdev->dev)) {
+                               ret = acpi_check_resource_conflict(&res[r]);
+                               if (ret)
+                                       goto fail_alias;
+                       }
                }
        }
 
index 09bc7804952a4a8bb2775e79b6d75fff309742db..38a0458f7834d6195c9103f713168f9d8be5e1a2 100644 (file)
@@ -34,6 +34,9 @@ static const struct mfd_cell mt6397_devs[] = {
        }, {
                .name = "mt6397-clk",
                .of_compatible = "mediatek,mt6397-clk",
+       }, {
+               .name = "mt6397-pinctrl",
+               .of_compatible = "mediatek,mt6397-pinctrl",
        },
 };
 
@@ -130,7 +133,7 @@ static int mt6397_irq_domain_map(struct irq_domain *d, unsigned int irq,
        return 0;
 }
 
-static struct irq_domain_ops mt6397_irq_domain_ops = {
+static const struct irq_domain_ops mt6397_irq_domain_ops = {
        .map = mt6397_irq_domain_map,
 };
 
index 7f87c62d91b3b67f92a3904bc61f94b062c0c408..e3deb466628bf53db45ec22d854ddce4a998b47f 100644 (file)
@@ -777,7 +777,8 @@ static int si476x_core_probe(struct i2c_client *client,
                rval = devm_request_threaded_irq(&client->dev,
                                                 client->irq, NULL,
                                                 si476x_core_interrupt,
-                                                IRQF_TRIGGER_FALLING,
+                                                IRQF_TRIGGER_FALLING |
+                                                IRQF_ONESHOT,
                                                 client->name, core);
                if (rval < 0) {
                        dev_err(&client->dev, "Could not request IRQ %d\n",
index 2d7fae94c861013c594463705e05ea413275e4d0..18c4d72d1d2a93b50a049c28d838eacc80ba7dd1 100644 (file)
@@ -989,7 +989,7 @@ static void stmpe_irq_unmap(struct irq_domain *d, unsigned int virq)
                irq_set_chip_data(virq, NULL);
 }
 
-static struct irq_domain_ops stmpe_irq_ops = {
+static const struct irq_domain_ops stmpe_irq_ops = {
         .map    = stmpe_irq_map,
         .unmap  = stmpe_irq_unmap,
         .xlate  = irq_domain_xlate_twocell,
index cf356395c9e980ead324e17299dcc09a2db6b941..96d420dfc15d6eedaacb34902eabc7efbe8a82ce 100644 (file)
@@ -233,7 +233,7 @@ static void tc3589x_irq_unmap(struct irq_domain *d, unsigned int virq)
        irq_set_chip_data(virq, NULL);
 }
 
-static struct irq_domain_ops tc3589x_irq_ops = {
+static const struct irq_domain_ops tc3589x_irq_ops = {
        .map    = tc3589x_irq_map,
        .unmap  = tc3589x_irq_unmap,
        .xlate  = irq_domain_xlate_onecell,
index 8e1dbc4695802a007edff2e2fe5a9d9851cbf6cd..e0a2583916ce2794c071e63851c9f82ca9afb032 100644 (file)
@@ -311,7 +311,7 @@ static int tps6586x_irq_map(struct irq_domain *h, unsigned int virq,
        return 0;
 }
 
-static struct irq_domain_ops tps6586x_domain_ops = {
+static const struct irq_domain_ops tps6586x_domain_ops = {
        .map    = tps6586x_irq_map,
        .xlate  = irq_domain_xlate_twocell,
 };
index 1b772ef761cbf63cf1af01a32f9d720258214669..a3fa7f4f1fb4d8ca142e2a22d4081c8494c37cab 100644 (file)
@@ -674,7 +674,7 @@ int twl4030_sih_setup(struct device *dev, int module, int irq_base)
        irq_set_handler_data(irq, agent);
        agent->irq_name = kasprintf(GFP_KERNEL, "twl4030_%s", sih->name);
        status = request_threaded_irq(irq, NULL, handle_twl4030_sih,
-                                     IRQF_EARLY_RESUME,
+                                     IRQF_EARLY_RESUME | IRQF_ONESHOT,
                                      agent->irq_name ?: sih->name, NULL);
 
        dev_info(dev, "%s (irq %d) chaining IRQs %d..%d\n", sih->name,
index f440aed613056a2b1b506f7be43f598f19c26002..04b539850e72a00ef3468be973759e3eef09a6af 100644 (file)
@@ -264,7 +264,9 @@ out:
        return err;
 }
 
-static int twl4030_config_wakeup12_sequence(u8 address)
+static int
+twl4030_config_wakeup12_sequence(const struct twl4030_power_data *pdata,
+                                u8 address)
 {
        int err = 0;
        u8 data;
@@ -293,13 +295,14 @@ static int twl4030_config_wakeup12_sequence(u8 address)
        if (err)
                goto out;
 
-       if (machine_is_omap_3430sdp() || machine_is_omap_ldp()) {
+       if (pdata->ac_charger_quirk || machine_is_omap_3430sdp() ||
+           machine_is_omap_ldp()) {
                /* Disabling AC charger effect on sleep-active transitions */
                err = twl_i2c_read_u8(TWL_MODULE_PM_MASTER, &data,
                                      R_CFG_P1_TRANSITION);
                if (err)
                        goto out;
-               data &= ~(1<<1);
+               data &= ~STARTON_CHG;
                err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, data,
                                       R_CFG_P1_TRANSITION);
                if (err)
@@ -459,8 +462,9 @@ static int twl4030_configure_resource(struct twl4030_resconfig *rconfig)
        return 0;
 }
 
-static int load_twl4030_script(struct twl4030_script *tscript,
-              u8 address)
+static int load_twl4030_script(const struct twl4030_power_data *pdata,
+                              struct twl4030_script *tscript,
+                              u8 address)
 {
        int err;
        static int order;
@@ -487,7 +491,7 @@ static int load_twl4030_script(struct twl4030_script *tscript,
                if (err)
                        goto out;
 
-               err = twl4030_config_wakeup12_sequence(address);
+               err = twl4030_config_wakeup12_sequence(pdata, address);
                if (err)
                        goto out;
                order = 1;
@@ -567,7 +571,7 @@ twl4030_power_configure_scripts(const struct twl4030_power_data *pdata)
        u8 address = twl4030_start_script_address;
 
        for (i = 0; i < pdata->num; i++) {
-               err = load_twl4030_script(pdata->scripts[i], address);
+               err = load_twl4030_script(pdata, pdata->scripts[i], address);
                if (err)
                        return err;
                address += pdata->scripts[i]->size;
@@ -829,6 +833,21 @@ static struct twl4030_power_data osc_off_idle = {
        .board_config           = osc_off_rconfig,
 };
 
+static struct twl4030_power_data omap3_idle_ac_quirk = {
+       .scripts                = omap3_idle_scripts,
+       .num                    = ARRAY_SIZE(omap3_idle_scripts),
+       .resource_config        = omap3_idle_rconfig,
+       .ac_charger_quirk       = true,
+};
+
+static struct twl4030_power_data omap3_idle_ac_quirk_osc_off = {
+       .scripts                = omap3_idle_scripts,
+       .num                    = ARRAY_SIZE(omap3_idle_scripts),
+       .resource_config        = omap3_idle_rconfig,
+       .board_config           = osc_off_rconfig,
+       .ac_charger_quirk       = true,
+};
+
 static const struct of_device_id twl4030_power_of_match[] = {
        {
                .compatible = "ti,twl4030-power",
@@ -845,6 +864,18 @@ static const struct of_device_id twl4030_power_of_match[] = {
                .compatible = "ti,twl4030-power-idle-osc-off",
                .data = &osc_off_idle,
        },
+       {
+               .compatible = "ti,twl4030-power-omap3-sdp",
+               .data = &omap3_idle_ac_quirk,
+       },
+       {
+               .compatible = "ti,twl4030-power-omap3-ldp",
+               .data = &omap3_idle_ac_quirk_osc_off,
+       },
+       {
+               .compatible = "ti,twl4030-power-omap3-evm",
+               .data = &omap3_idle_ac_quirk,
+       },
        { },
 };
 MODULE_DEVICE_TABLE(of, twl4030_power_of_match);
index 2807e1a956632d1c0e7102aa1678707c04aba10d..20fb58179adad3d3df48ea15123bbacea97bdd83 100644 (file)
@@ -376,7 +376,7 @@ static void twl6030_irq_unmap(struct irq_domain *d, unsigned int virq)
        irq_set_chip_data(virq, NULL);
 }
 
-static struct irq_domain_ops twl6030_irq_domain_ops = {
+static const struct irq_domain_ops twl6030_irq_domain_ops = {
        .map    = twl6030_irq_map,
        .unmap  = twl6030_irq_unmap,
        .xlate  = irq_domain_xlate_onetwocell,
index 58ea9fdd3a15c07c2331544e78e001d99f4abfcf..3591550598ad494f0f5c48db7f685a4ae97ed1dc 100644 (file)
@@ -566,8 +566,7 @@ static int ucb1x00_probe(struct mcp *mcp)
        }
 
        irq_set_irq_type(ucb->irq, IRQ_TYPE_EDGE_RISING);
-       irq_set_handler_data(ucb->irq, ucb);
-       irq_set_chained_handler(ucb->irq, ucb1x00_irq);
+       irq_set_chained_handler_and_data(ucb->irq, ucb1x00_irq, ucb);
 
        if (pdata && pdata->gpio_base) {
                ucb->gpio.label = dev_name(&ucb->dev);
index 6ee3018d8653f6d8b73b92d70d296d2ed01e8f62..fd789d2eb0f52329ae9aaf3421cb9138ae603459 100644 (file)
@@ -285,7 +285,8 @@ void wm831x_auxadc_init(struct wm831x *wm831x)
 
                ret = request_threaded_irq(wm831x_irq(wm831x,
                                                      WM831X_IRQ_AUXADC_DATA),
-                                          NULL, wm831x_auxadc_irq, 0,
+                                          NULL, wm831x_auxadc_irq,
+                                          IRQF_ONESHOT,
                                           "auxadc", wm831x);
                if (ret < 0) {
                        dev_err(wm831x->dev, "AUXADC IRQ request failed: %d\n",
index 64e512eadf1718de932218ba2c3f60b7c216e257..3da81263c764ad13afebfcd355c8630ff1c3f45d 100644 (file)
@@ -564,7 +564,7 @@ static int wm831x_irq_map(struct irq_domain *h, unsigned int virq,
        return 0;
 }
 
-static struct irq_domain_ops wm831x_irq_domain_ops = {
+static const struct irq_domain_ops wm831x_irq_domain_ops = {
        .map    = wm831x_irq_map,
        .xlate  = irq_domain_xlate_twocell,
 };
index f5124a8acad8e06fb8344c718b42d1048a37c455..8a07c5634aee4d78d11ac0b8aaeb7f5d586c3a94 100644 (file)
@@ -404,7 +404,8 @@ int wm8350_device_init(struct wm8350 *wm8350, int irq,
        if (wm8350->irq_base) {
                ret = request_threaded_irq(wm8350->irq_base +
                                           WM8350_IRQ_AUXADC_DATARDY,
-                                          NULL, wm8350_auxadc_irq, 0,
+                                          NULL, wm8350_auxadc_irq,
+                                          IRQF_ONESHOT,
                                           "auxadc", wm8350);
                if (ret < 0)
                        dev_warn(wm8350->dev,
index a14407edbd8913edd46b5450c9a5c0e2593999c6..55c380a676861208dc355a9e66fda859e9cc15bd 100644 (file)
@@ -28,7 +28,7 @@
 
 #include <linux/delay.h>
 
-static struct regmap_irq wm8994_irqs[] = {
+static const struct regmap_irq wm8994_irqs[] = {
        [WM8994_IRQ_TEMP_SHUT] = {
                .reg_offset = 1,
                .mask = WM8994_TEMP_SHUT_EINT,
@@ -128,7 +128,7 @@ static struct regmap_irq wm8994_irqs[] = {
        },
 };
 
-static struct regmap_irq_chip wm8994_irq_chip = {
+static const struct regmap_irq_chip wm8994_irq_chip = {
        .name = "wm8994",
        .irqs = wm8994_irqs,
        .num_irqs = ARRAY_SIZE(wm8994_irqs),
@@ -184,7 +184,7 @@ static int wm8994_edge_irq_map(struct irq_domain *h, unsigned int virq,
        return 0;
 }
 
-static struct irq_domain_ops wm8994_edge_irq_ops = {
+static const struct irq_domain_ops wm8994_edge_irq_ops = {
        .map    = wm8994_edge_irq_map,
        .xlate  = irq_domain_xlate_twocell,
 };
index a990b39b4dfb8716da5bb121f03ae241071ad33e..b6db9ebd52c298244fb10178a15c01f3523e3f74 100644 (file)
@@ -7,10 +7,15 @@ config CXL_BASE
        default n
        select PPC_COPRO_BASE
 
+config CXL_KERNEL_API
+       bool
+       default n
+
 config CXL
        tristate "Support for IBM Coherent Accelerators (CXL)"
        depends on PPC_POWERNV && PCI_MSI
        select CXL_BASE
+       select CXL_KERNEL_API
        default m
        help
          Select this option to enable driver support for IBM Coherent
index edb494d3ff271a038c0cfdd95ac1fe3f918c1637..14e3f8219a11cfec2723d22582091e3fffabe767 100644 (file)
@@ -1,4 +1,6 @@
-cxl-y                          += main.o file.o irq.o fault.o native.o context.o sysfs.o debugfs.o pci.o trace.o
+cxl-y                          += main.o file.o irq.o fault.o native.o
+cxl-y                          += context.o sysfs.o debugfs.o pci.o trace.o
+cxl-y                          += vphb.o api.o
 obj-$(CONFIG_CXL)              += cxl.o
 obj-$(CONFIG_CXL_BASE)         += base.o
 
diff --git a/drivers/misc/cxl/api.c b/drivers/misc/cxl/api.c
new file mode 100644 (file)
index 0000000..0c77240
--- /dev/null
@@ -0,0 +1,331 @@
+/*
+ * Copyright 2014 IBM Corp.
+ *
+ * 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>
+#include <linux/slab.h>
+#include <linux/anon_inodes.h>
+#include <linux/file.h>
+#include <misc/cxl.h>
+
+#include "cxl.h"
+
+struct cxl_context *cxl_dev_context_init(struct pci_dev *dev)
+{
+       struct cxl_afu *afu;
+       struct cxl_context  *ctx;
+       int rc;
+
+       afu = cxl_pci_to_afu(dev);
+
+       ctx = cxl_context_alloc();
+       if (IS_ERR(ctx))
+               return ctx;
+
+       /* Make it a slave context.  We can promote it later? */
+       rc = cxl_context_init(ctx, afu, false, NULL);
+       if (rc) {
+               kfree(ctx);
+               return ERR_PTR(-ENOMEM);
+       }
+       cxl_assign_psn_space(ctx);
+
+       return ctx;
+}
+EXPORT_SYMBOL_GPL(cxl_dev_context_init);
+
+struct cxl_context *cxl_get_context(struct pci_dev *dev)
+{
+       return dev->dev.archdata.cxl_ctx;
+}
+EXPORT_SYMBOL_GPL(cxl_get_context);
+
+struct device *cxl_get_phys_dev(struct pci_dev *dev)
+{
+       struct cxl_afu *afu;
+
+       afu = cxl_pci_to_afu(dev);
+
+       return afu->adapter->dev.parent;
+}
+EXPORT_SYMBOL_GPL(cxl_get_phys_dev);
+
+int cxl_release_context(struct cxl_context *ctx)
+{
+       if (ctx->status != CLOSED)
+               return -EBUSY;
+
+       cxl_context_free(ctx);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(cxl_release_context);
+
+int cxl_allocate_afu_irqs(struct cxl_context *ctx, int num)
+{
+       if (num == 0)
+               num = ctx->afu->pp_irqs;
+       return afu_allocate_irqs(ctx, num);
+}
+EXPORT_SYMBOL_GPL(cxl_allocate_afu_irqs);
+
+void cxl_free_afu_irqs(struct cxl_context *ctx)
+{
+       cxl_release_irq_ranges(&ctx->irqs, ctx->afu->adapter);
+}
+EXPORT_SYMBOL_GPL(cxl_free_afu_irqs);
+
+static irq_hw_number_t cxl_find_afu_irq(struct cxl_context *ctx, int num)
+{
+       __u16 range;
+       int r;
+
+       WARN_ON(num == 0);
+
+       for (r = 0; r < CXL_IRQ_RANGES; r++) {
+               range = ctx->irqs.range[r];
+               if (num < range) {
+                       return ctx->irqs.offset[r] + num;
+               }
+               num -= range;
+       }
+       return 0;
+}
+
+int cxl_map_afu_irq(struct cxl_context *ctx, int num,
+                   irq_handler_t handler, void *cookie, char *name)
+{
+       irq_hw_number_t hwirq;
+
+       /*
+        * Find interrupt we are to register.
+        */
+       hwirq = cxl_find_afu_irq(ctx, num);
+       if (!hwirq)
+               return -ENOENT;
+
+       return cxl_map_irq(ctx->afu->adapter, hwirq, handler, cookie, name);
+}
+EXPORT_SYMBOL_GPL(cxl_map_afu_irq);
+
+void cxl_unmap_afu_irq(struct cxl_context *ctx, int num, void *cookie)
+{
+       irq_hw_number_t hwirq;
+       unsigned int virq;
+
+       hwirq = cxl_find_afu_irq(ctx, num);
+       if (!hwirq)
+               return;
+
+       virq = irq_find_mapping(NULL, hwirq);
+       if (virq)
+               cxl_unmap_irq(virq, cookie);
+}
+EXPORT_SYMBOL_GPL(cxl_unmap_afu_irq);
+
+/*
+ * Start a context
+ * Code here similar to afu_ioctl_start_work().
+ */
+int cxl_start_context(struct cxl_context *ctx, u64 wed,
+                     struct task_struct *task)
+{
+       int rc = 0;
+       bool kernel = true;
+
+       pr_devel("%s: pe: %i\n", __func__, ctx->pe);
+
+       mutex_lock(&ctx->status_mutex);
+       if (ctx->status == STARTED)
+               goto out; /* already started */
+
+       if (task) {
+               ctx->pid = get_task_pid(task, PIDTYPE_PID);
+               get_pid(ctx->pid);
+               kernel = false;
+       }
+
+       cxl_ctx_get();
+
+       if ((rc = cxl_attach_process(ctx, kernel, wed , 0))) {
+               put_pid(ctx->pid);
+               cxl_ctx_put();
+               goto out;
+       }
+
+       ctx->status = STARTED;
+       get_device(&ctx->afu->dev);
+out:
+       mutex_unlock(&ctx->status_mutex);
+       return rc;
+}
+EXPORT_SYMBOL_GPL(cxl_start_context);
+
+int cxl_process_element(struct cxl_context *ctx)
+{
+       return ctx->pe;
+}
+EXPORT_SYMBOL_GPL(cxl_process_element);
+
+/* Stop a context.  Returns 0 on success, otherwise -Errno */
+int cxl_stop_context(struct cxl_context *ctx)
+{
+       int rc;
+
+       rc = __detach_context(ctx);
+       if (!rc)
+               put_device(&ctx->afu->dev);
+       return rc;
+}
+EXPORT_SYMBOL_GPL(cxl_stop_context);
+
+void cxl_set_master(struct cxl_context *ctx)
+{
+       ctx->master = true;
+       cxl_assign_psn_space(ctx);
+}
+EXPORT_SYMBOL_GPL(cxl_set_master);
+
+/* wrappers around afu_* file ops which are EXPORTED */
+int cxl_fd_open(struct inode *inode, struct file *file)
+{
+       return afu_open(inode, file);
+}
+EXPORT_SYMBOL_GPL(cxl_fd_open);
+int cxl_fd_release(struct inode *inode, struct file *file)
+{
+       return afu_release(inode, file);
+}
+EXPORT_SYMBOL_GPL(cxl_fd_release);
+long cxl_fd_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+       return afu_ioctl(file, cmd, arg);
+}
+EXPORT_SYMBOL_GPL(cxl_fd_ioctl);
+int cxl_fd_mmap(struct file *file, struct vm_area_struct *vm)
+{
+       return afu_mmap(file, vm);
+}
+EXPORT_SYMBOL_GPL(cxl_fd_mmap);
+unsigned int cxl_fd_poll(struct file *file, struct poll_table_struct *poll)
+{
+       return afu_poll(file, poll);
+}
+EXPORT_SYMBOL_GPL(cxl_fd_poll);
+ssize_t cxl_fd_read(struct file *file, char __user *buf, size_t count,
+                       loff_t *off)
+{
+       return afu_read(file, buf, count, off);
+}
+EXPORT_SYMBOL_GPL(cxl_fd_read);
+
+#define PATCH_FOPS(NAME) if (!fops->NAME) fops->NAME = afu_fops.NAME
+
+/* Get a struct file and fd for a context and attach the ops */
+struct file *cxl_get_fd(struct cxl_context *ctx, struct file_operations *fops,
+                       int *fd)
+{
+       struct file *file;
+       int rc, flags, fdtmp;
+
+       flags = O_RDWR | O_CLOEXEC;
+
+       /* This code is similar to anon_inode_getfd() */
+       rc = get_unused_fd_flags(flags);
+       if (rc < 0)
+               return ERR_PTR(rc);
+       fdtmp = rc;
+
+       /*
+        * Patch the file ops.  Needs to be careful that this is rentrant safe.
+        */
+       if (fops) {
+               PATCH_FOPS(open);
+               PATCH_FOPS(poll);
+               PATCH_FOPS(read);
+               PATCH_FOPS(release);
+               PATCH_FOPS(unlocked_ioctl);
+               PATCH_FOPS(compat_ioctl);
+               PATCH_FOPS(mmap);
+       } else /* use default ops */
+               fops = (struct file_operations *)&afu_fops;
+
+       file = anon_inode_getfile("cxl", fops, ctx, flags);
+       if (IS_ERR(file))
+               put_unused_fd(fdtmp);
+       *fd = fdtmp;
+       return file;
+}
+EXPORT_SYMBOL_GPL(cxl_get_fd);
+
+struct cxl_context *cxl_fops_get_context(struct file *file)
+{
+       return file->private_data;
+}
+EXPORT_SYMBOL_GPL(cxl_fops_get_context);
+
+int cxl_start_work(struct cxl_context *ctx,
+                  struct cxl_ioctl_start_work *work)
+{
+       int rc;
+
+       /* code taken from afu_ioctl_start_work */
+       if (!(work->flags & CXL_START_WORK_NUM_IRQS))
+               work->num_interrupts = ctx->afu->pp_irqs;
+       else if ((work->num_interrupts < ctx->afu->pp_irqs) ||
+                (work->num_interrupts > ctx->afu->irqs_max)) {
+               return -EINVAL;
+       }
+
+       rc = afu_register_irqs(ctx, work->num_interrupts);
+       if (rc)
+               return rc;
+
+       rc = cxl_start_context(ctx, work->work_element_descriptor, current);
+       if (rc < 0) {
+               afu_release_irqs(ctx, ctx);
+               return rc;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(cxl_start_work);
+
+void __iomem *cxl_psa_map(struct cxl_context *ctx)
+{
+       struct cxl_afu *afu = ctx->afu;
+       int rc;
+
+       rc = cxl_afu_check_and_enable(afu);
+       if (rc)
+               return NULL;
+
+       pr_devel("%s: psn_phys%llx size:%llx\n",
+                __func__, afu->psn_phys, afu->adapter->ps_size);
+       return ioremap(ctx->psn_phys, ctx->psn_size);
+}
+EXPORT_SYMBOL_GPL(cxl_psa_map);
+
+void cxl_psa_unmap(void __iomem *addr)
+{
+       iounmap(addr);
+}
+EXPORT_SYMBOL_GPL(cxl_psa_unmap);
+
+int cxl_afu_reset(struct cxl_context *ctx)
+{
+       struct cxl_afu *afu = ctx->afu;
+       int rc;
+
+       rc = __cxl_afu_reset(afu);
+       if (rc)
+               return rc;
+
+       return cxl_afu_check_and_enable(afu);
+}
+EXPORT_SYMBOL_GPL(cxl_afu_reset);
index 0654ad83675eb6fdc581b42f4a48cb859df5fab6..a9f0dd3255a2a0cfa81842647a9dd8118b049859 100644 (file)
@@ -10,7 +10,7 @@
 #include <linux/module.h>
 #include <linux/rcupdate.h>
 #include <asm/errno.h>
-#include <misc/cxl.h>
+#include <misc/cxl-base.h>
 #include "cxl.h"
 
 /* protected by rcu */
index d1b55fe62817dcd0261ab926a704a37f590ca67c..2a4c80ac322ad2500a13fc6162d8920ac4a6b8db 100644 (file)
@@ -174,7 +174,7 @@ int cxl_context_iomap(struct cxl_context *ctx, struct vm_area_struct *vma)
  * return until all outstanding interrupts for this context have completed. The
  * hardware should no longer access *ctx after this has returned.
  */
-static void __detach_context(struct cxl_context *ctx)
+int __detach_context(struct cxl_context *ctx)
 {
        enum cxl_context_status status;
 
@@ -183,12 +183,13 @@ static void __detach_context(struct cxl_context *ctx)
        ctx->status = CLOSED;
        mutex_unlock(&ctx->status_mutex);
        if (status != STARTED)
-               return;
+               return -EBUSY;
 
        WARN_ON(cxl_detach_process(ctx));
-       afu_release_irqs(ctx);
        flush_work(&ctx->fault_work); /* Only needed for dedicated process */
-       wake_up_all(&ctx->wq);
+       put_pid(ctx->pid);
+       cxl_ctx_put();
+       return 0;
 }
 
 /*
@@ -199,7 +200,14 @@ static void __detach_context(struct cxl_context *ctx)
  */
 void cxl_context_detach(struct cxl_context *ctx)
 {
-       __detach_context(ctx);
+       int rc;
+
+       rc = __detach_context(ctx);
+       if (rc)
+               return;
+
+       afu_release_irqs(ctx, ctx);
+       wake_up_all(&ctx->wq);
 }
 
 /*
@@ -216,7 +224,7 @@ void cxl_context_detach_all(struct cxl_afu *afu)
                 * Anything done in here needs to be setup before the IDR is
                 * created and torn down after the IDR removed
                 */
-               __detach_context(ctx);
+               cxl_context_detach(ctx);
 
                /*
                 * We are force detaching - remove any active PSA mappings so
@@ -232,16 +240,20 @@ void cxl_context_detach_all(struct cxl_afu *afu)
        mutex_unlock(&afu->contexts_lock);
 }
 
-void cxl_context_free(struct cxl_context *ctx)
+static void reclaim_ctx(struct rcu_head *rcu)
 {
-       mutex_lock(&ctx->afu->contexts_lock);
-       idr_remove(&ctx->afu->contexts_idr, ctx->pe);
-       mutex_unlock(&ctx->afu->contexts_lock);
-       synchronize_rcu();
+       struct cxl_context *ctx = container_of(rcu, struct cxl_context, rcu);
 
        free_page((u64)ctx->sstp);
        ctx->sstp = NULL;
 
-       put_pid(ctx->pid);
        kfree(ctx);
 }
+
+void cxl_context_free(struct cxl_context *ctx)
+{
+       mutex_lock(&ctx->afu->contexts_lock);
+       idr_remove(&ctx->afu->contexts_idr, ctx->pe);
+       mutex_unlock(&ctx->afu->contexts_lock);
+       call_rcu(&ctx->rcu, reclaim_ctx);
+}
index a1cee4767ec6c571344e93ca874e8bdac83ed3a5..4fd66cabde1ef174ba0781115400ca0f4b1d337a 100644 (file)
 #include <linux/pid.h>
 #include <linux/io.h>
 #include <linux/pci.h>
+#include <linux/fs.h>
 #include <asm/cputable.h>
 #include <asm/mmu.h>
 #include <asm/reg.h>
-#include <misc/cxl.h>
+#include <misc/cxl-base.h>
 
 #include <uapi/misc/cxl.h>
 
@@ -315,8 +316,6 @@ static const cxl_p2n_reg_t CXL_PSL_WED_An     = {0x0A0};
 #define CXL_MAX_SLICES 4
 #define MAX_AFU_MMIO_REGS 3
 
-#define CXL_MODE_DEDICATED   0x1
-#define CXL_MODE_DIRECTED    0x2
 #define CXL_MODE_TIME_SLICED 0x4
 #define CXL_SUPPORTED_MODES (CXL_MODE_DEDICATED | CXL_MODE_DIRECTED)
 
@@ -362,6 +361,10 @@ struct cxl_afu {
        struct mutex spa_mutex;
        spinlock_t afu_cntl_lock;
 
+       /* AFU error buffer fields and bin attribute for sysfs */
+       u64 eb_len, eb_offset;
+       struct bin_attribute attr_eb;
+
        /*
         * Only the first part of the SPA is used for the process element
         * linked list. The only other part that software needs to worry about
@@ -375,6 +378,9 @@ struct cxl_afu {
        int spa_max_procs;
        unsigned int psl_virq;
 
+       /* pointer to the vphb */
+       struct pci_controller *phb;
+
        int pp_irqs;
        int irqs_max;
        int num_procs;
@@ -455,6 +461,8 @@ struct cxl_context {
        bool pending_irq;
        bool pending_fault;
        bool pending_afu_err;
+
+       struct rcu_head rcu;
 };
 
 struct cxl {
@@ -563,6 +571,9 @@ static inline void __iomem *_cxl_p2n_addr(struct cxl_afu *afu, cxl_p2n_reg_t reg
 u16 cxl_afu_cr_read16(struct cxl_afu *afu, int cr, u64 off);
 u8 cxl_afu_cr_read8(struct cxl_afu *afu, int cr, u64 off);
 
+ssize_t cxl_afu_read_err_buffer(struct cxl_afu *afu, char *buf,
+                               loff_t off, size_t count);
+
 
 struct cxl_calls {
        void (*cxl_slbia)(struct mm_struct *mm);
@@ -606,7 +617,7 @@ void cxl_release_psl_err_irq(struct cxl *adapter);
 int cxl_register_serr_irq(struct cxl_afu *afu);
 void cxl_release_serr_irq(struct cxl_afu *afu);
 int afu_register_irqs(struct cxl_context *ctx, u32 count);
-void afu_release_irqs(struct cxl_context *ctx);
+void afu_release_irqs(struct cxl_context *ctx, void *cookie);
 irqreturn_t cxl_slice_irq_err(int irq, void *data);
 
 int cxl_debugfs_init(void);
@@ -629,6 +640,10 @@ int cxl_context_init(struct cxl_context *ctx, struct cxl_afu *afu, bool master,
                     struct address_space *mapping);
 void cxl_context_free(struct cxl_context *ctx);
 int cxl_context_iomap(struct cxl_context *ctx, struct vm_area_struct *vma);
+unsigned int cxl_map_irq(struct cxl *adapter, irq_hw_number_t hwirq,
+                        irq_handler_t handler, void *cookie, const char *name);
+void cxl_unmap_irq(unsigned int virq, void *cookie);
+int __detach_context(struct cxl_context *ctx);
 
 /* This matches the layout of the H_COLLECT_CA_INT_INFO retbuf */
 struct cxl_irq_info {
@@ -642,6 +657,7 @@ struct cxl_irq_info {
        u64 padding[3]; /* to match the expected retbuf size for plpar_hcall9 */
 };
 
+void cxl_assign_psn_space(struct cxl_context *ctx);
 int cxl_attach_process(struct cxl_context *ctx, bool kernel, u64 wed,
                            u64 amr);
 int cxl_detach_process(struct cxl_context *ctx);
@@ -653,11 +669,23 @@ int cxl_check_error(struct cxl_afu *afu);
 int cxl_afu_slbia(struct cxl_afu *afu);
 int cxl_tlb_slb_invalidate(struct cxl *adapter);
 int cxl_afu_disable(struct cxl_afu *afu);
-int cxl_afu_reset(struct cxl_afu *afu);
+int __cxl_afu_reset(struct cxl_afu *afu);
+int cxl_afu_check_and_enable(struct cxl_afu *afu);
 int cxl_psl_purge(struct cxl_afu *afu);
 
 void cxl_stop_trace(struct cxl *cxl);
+int cxl_pci_vphb_add(struct cxl_afu *afu);
+void cxl_pci_vphb_remove(struct cxl_afu *afu);
 
 extern struct pci_driver cxl_pci_driver;
+int afu_allocate_irqs(struct cxl_context *ctx, u32 count);
+
+int afu_open(struct inode *inode, struct file *file);
+int afu_release(struct inode *inode, struct file *file);
+long afu_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
+int afu_mmap(struct file *file, struct vm_area_struct *vm);
+unsigned int afu_poll(struct file *file, struct poll_table_struct *poll);
+ssize_t afu_read(struct file *file, char __user *buf, size_t count, loff_t *off);
+extern const struct file_operations afu_fops;
 
 #endif
index 5286b8b704f559eb21c1c7c5b2b4af3b9180444a..25a5418c55cb897e245a8faf8f728f059f5756fa 100644 (file)
@@ -172,8 +172,8 @@ void cxl_handle_fault(struct work_struct *fault_work)
                container_of(fault_work, struct cxl_context, fault_work);
        u64 dsisr = ctx->dsisr;
        u64 dar = ctx->dar;
-       struct task_struct *task;
-       struct mm_struct *mm;
+       struct task_struct *task = NULL;
+       struct mm_struct *mm = NULL;
 
        if (cxl_p2n_read(ctx->afu, CXL_PSL_DSISR_An) != dsisr ||
            cxl_p2n_read(ctx->afu, CXL_PSL_DAR_An) != dar ||
@@ -194,17 +194,19 @@ void cxl_handle_fault(struct work_struct *fault_work)
        pr_devel("CXL BOTTOM HALF handling fault for afu pe: %i. "
                "DSISR: %#llx DAR: %#llx\n", ctx->pe, dsisr, dar);
 
-       if (!(task = get_pid_task(ctx->pid, PIDTYPE_PID))) {
-               pr_devel("cxl_handle_fault unable to get task %i\n",
-                        pid_nr(ctx->pid));
-               cxl_ack_ae(ctx);
-               return;
-       }
-       if (!(mm = get_task_mm(task))) {
-               pr_devel("cxl_handle_fault unable to get mm %i\n",
-                        pid_nr(ctx->pid));
-               cxl_ack_ae(ctx);
-               goto out;
+       if (!ctx->kernel) {
+               if (!(task = get_pid_task(ctx->pid, PIDTYPE_PID))) {
+                       pr_devel("cxl_handle_fault unable to get task %i\n",
+                                pid_nr(ctx->pid));
+                       cxl_ack_ae(ctx);
+                       return;
+               }
+               if (!(mm = get_task_mm(task))) {
+                       pr_devel("cxl_handle_fault unable to get mm %i\n",
+                                pid_nr(ctx->pid));
+                       cxl_ack_ae(ctx);
+                       goto out;
+               }
        }
 
        if (dsisr & CXL_PSL_DSISR_An_DS)
@@ -214,9 +216,11 @@ void cxl_handle_fault(struct work_struct *fault_work)
        else
                WARN(1, "cxl_handle_fault has nothing to handle\n");
 
-       mmput(mm);
+       if (mm)
+               mmput(mm);
 out:
-       put_task_struct(task);
+       if (task)
+               put_task_struct(task);
 }
 
 static void cxl_prefault_one(struct cxl_context *ctx, u64 ea)
index 2364bcadb9a94c195abc6398a77c734f45a7e468..e3f4b69527a9bd2df592c36227c536c8a3321f50 100644 (file)
@@ -96,7 +96,8 @@ err_put_adapter:
        put_device(&adapter->dev);
        return rc;
 }
-static int afu_open(struct inode *inode, struct file *file)
+
+int afu_open(struct inode *inode, struct file *file)
 {
        return __afu_open(inode, file, false);
 }
@@ -106,7 +107,7 @@ static int afu_master_open(struct inode *inode, struct file *file)
        return __afu_open(inode, file, true);
 }
 
-static int afu_release(struct inode *inode, struct file *file)
+int afu_release(struct inode *inode, struct file *file)
 {
        struct cxl_context *ctx = file->private_data;
 
@@ -128,7 +129,6 @@ static int afu_release(struct inode *inode, struct file *file)
         */
        cxl_context_free(ctx);
 
-       cxl_ctx_put();
        return 0;
 }
 
@@ -191,7 +191,7 @@ static long afu_ioctl_start_work(struct cxl_context *ctx,
 
        if ((rc = cxl_attach_process(ctx, false, work.work_element_descriptor,
                                     amr))) {
-               afu_release_irqs(ctx);
+               afu_release_irqs(ctx, ctx);
                goto out;
        }
 
@@ -212,7 +212,26 @@ static long afu_ioctl_process_element(struct cxl_context *ctx,
        return 0;
 }
 
-static long afu_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+static long afu_ioctl_get_afu_id(struct cxl_context *ctx,
+                                struct cxl_afu_id __user *upafuid)
+{
+       struct cxl_afu_id afuid = { 0 };
+
+       afuid.card_id = ctx->afu->adapter->adapter_num;
+       afuid.afu_offset = ctx->afu->slice;
+       afuid.afu_mode = ctx->afu->current_mode;
+
+       /* set the flag bit in case the afu is a slave */
+       if (ctx->afu->current_mode == CXL_MODE_DIRECTED && !ctx->master)
+               afuid.flags |= CXL_AFUID_FLAG_SLAVE;
+
+       if (copy_to_user(upafuid, &afuid, sizeof(afuid)))
+               return -EFAULT;
+
+       return 0;
+}
+
+long afu_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 {
        struct cxl_context *ctx = file->private_data;
 
@@ -225,17 +244,20 @@ static long afu_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
                return afu_ioctl_start_work(ctx, (struct cxl_ioctl_start_work __user *)arg);
        case CXL_IOCTL_GET_PROCESS_ELEMENT:
                return afu_ioctl_process_element(ctx, (__u32 __user *)arg);
+       case CXL_IOCTL_GET_AFU_ID:
+               return afu_ioctl_get_afu_id(ctx, (struct cxl_afu_id __user *)
+                                           arg);
        }
        return -EINVAL;
 }
 
-static long afu_compat_ioctl(struct file *file, unsigned int cmd,
+long afu_compat_ioctl(struct file *file, unsigned int cmd,
                             unsigned long arg)
 {
        return afu_ioctl(file, cmd, arg);
 }
 
-static int afu_mmap(struct file *file, struct vm_area_struct *vm)
+int afu_mmap(struct file *file, struct vm_area_struct *vm)
 {
        struct cxl_context *ctx = file->private_data;
 
@@ -246,7 +268,7 @@ static int afu_mmap(struct file *file, struct vm_area_struct *vm)
        return cxl_context_iomap(ctx, vm);
 }
 
-static unsigned int afu_poll(struct file *file, struct poll_table_struct *poll)
+unsigned int afu_poll(struct file *file, struct poll_table_struct *poll)
 {
        struct cxl_context *ctx = file->private_data;
        int mask = 0;
@@ -278,7 +300,7 @@ static inline int ctx_event_pending(struct cxl_context *ctx)
            ctx->pending_afu_err || (ctx->status == CLOSED));
 }
 
-static ssize_t afu_read(struct file *file, char __user *buf, size_t count,
+ssize_t afu_read(struct file *file, char __user *buf, size_t count,
                        loff_t *off)
 {
        struct cxl_context *ctx = file->private_data;
@@ -359,7 +381,11 @@ out:
        return rc;
 }
 
-static const struct file_operations afu_fops = {
+/* 
+ * Note: if this is updated, we need to update api.c to patch the new ones in
+ * too
+ */
+const struct file_operations afu_fops = {
        .owner          = THIS_MODULE,
        .open           = afu_open,
        .poll           = afu_poll,
@@ -370,7 +396,7 @@ static const struct file_operations afu_fops = {
        .mmap           = afu_mmap,
 };
 
-static const struct file_operations afu_master_fops = {
+const struct file_operations afu_master_fops = {
        .owner          = THIS_MODULE,
        .open           = afu_master_open,
        .poll           = afu_poll,
index c8929c526691706b0c7d566b3e32bed760aad062..680cd263436db547c24ec890ca07ba114813fb33 100644 (file)
@@ -14,7 +14,7 @@
 #include <linux/slab.h>
 #include <linux/pid.h>
 #include <asm/cputable.h>
-#include <misc/cxl.h>
+#include <misc/cxl-base.h>
 
 #include "cxl.h"
 #include "trace.h"
@@ -416,9 +416,8 @@ void afu_irq_name_free(struct cxl_context *ctx)
        }
 }
 
-int afu_register_irqs(struct cxl_context *ctx, u32 count)
+int afu_allocate_irqs(struct cxl_context *ctx, u32 count)
 {
-       irq_hw_number_t hwirq;
        int rc, r, i, j = 1;
        struct cxl_irq_name *irq_name;
 
@@ -458,6 +457,18 @@ int afu_register_irqs(struct cxl_context *ctx, u32 count)
                        j++;
                }
        }
+       return 0;
+
+out:
+       afu_irq_name_free(ctx);
+       return -ENOMEM;
+}
+
+void afu_register_hwirqs(struct cxl_context *ctx)
+{
+       irq_hw_number_t hwirq;
+       struct cxl_irq_name *irq_name;
+       int r,i;
 
        /* We've allocated all memory now, so let's do the irq allocations */
        irq_name = list_first_entry(&ctx->irq_names, struct cxl_irq_name, list);
@@ -469,15 +480,21 @@ int afu_register_irqs(struct cxl_context *ctx, u32 count)
                        irq_name = list_next_entry(irq_name, list);
                }
        }
+}
 
-       return 0;
+int afu_register_irqs(struct cxl_context *ctx, u32 count)
+{
+       int rc;
 
-out:
-       afu_irq_name_free(ctx);
-       return -ENOMEM;
-}
+       rc = afu_allocate_irqs(ctx, count);
+       if (rc)
+               return rc;
+
+       afu_register_hwirqs(ctx);
+       return 0;
+ }
 
-void afu_release_irqs(struct cxl_context *ctx)
+void afu_release_irqs(struct cxl_context *ctx, void *cookie)
 {
        irq_hw_number_t hwirq;
        unsigned int virq;
@@ -488,7 +505,7 @@ void afu_release_irqs(struct cxl_context *ctx)
                for (i = 0; i < ctx->irqs.range[r]; hwirq++, i++) {
                        virq = irq_find_mapping(NULL, hwirq);
                        if (virq)
-                               cxl_unmap_irq(virq, ctx);
+                               cxl_unmap_irq(virq, cookie);
                }
        }
 
index 8ccddceead66715f64d1a9099a4533e1bf45ce1e..833348e2c9cbc161128d2666f0329e4b18ea536b 100644 (file)
@@ -20,7 +20,7 @@
 #include <linux/idr.h>
 #include <linux/pci.h>
 #include <asm/cputable.h>
-#include <misc/cxl.h>
+#include <misc/cxl-base.h>
 
 #include "cxl.h"
 #include "trace.h"
index 29185fc61276706986e2ca6d28b640efc01f3c6a..10567f245818b73bbdb1272763834b48a9c85c5a 100644 (file)
@@ -15,7 +15,7 @@
 #include <linux/mm.h>
 #include <linux/uaccess.h>
 #include <asm/synch.h>
-#include <misc/cxl.h>
+#include <misc/cxl-base.h>
 
 #include "cxl.h"
 #include "trace.h"
@@ -73,7 +73,7 @@ int cxl_afu_disable(struct cxl_afu *afu)
 }
 
 /* This will disable as well as reset */
-int cxl_afu_reset(struct cxl_afu *afu)
+int __cxl_afu_reset(struct cxl_afu *afu)
 {
        pr_devel("AFU reset request\n");
 
@@ -83,7 +83,7 @@ int cxl_afu_reset(struct cxl_afu *afu)
                           false);
 }
 
-static int afu_check_and_enable(struct cxl_afu *afu)
+int cxl_afu_check_and_enable(struct cxl_afu *afu)
 {
        if (afu->enabled)
                return 0;
@@ -379,7 +379,7 @@ static int remove_process_element(struct cxl_context *ctx)
 }
 
 
-static void assign_psn_space(struct cxl_context *ctx)
+void cxl_assign_psn_space(struct cxl_context *ctx)
 {
        if (!ctx->afu->pp_size || ctx->master) {
                ctx->psn_phys = ctx->afu->psn_phys;
@@ -430,34 +430,46 @@ err:
 #define set_endian(sr) ((sr) &= ~(CXL_PSL_SR_An_LE))
 #endif
 
+static u64 calculate_sr(struct cxl_context *ctx)
+{
+       u64 sr = 0;
+
+       if (ctx->master)
+               sr |= CXL_PSL_SR_An_MP;
+       if (mfspr(SPRN_LPCR) & LPCR_TC)
+               sr |= CXL_PSL_SR_An_TC;
+       if (ctx->kernel) {
+               sr |= CXL_PSL_SR_An_R | (mfmsr() & MSR_SF);
+               sr |= CXL_PSL_SR_An_HV;
+       } else {
+               sr |= CXL_PSL_SR_An_PR | CXL_PSL_SR_An_R;
+               set_endian(sr);
+               sr &= ~(CXL_PSL_SR_An_HV);
+               if (!test_tsk_thread_flag(current, TIF_32BIT))
+                       sr |= CXL_PSL_SR_An_SF;
+       }
+       return sr;
+}
+
 static int attach_afu_directed(struct cxl_context *ctx, u64 wed, u64 amr)
 {
-       u64 sr;
+       u32 pid;
        int r, result;
 
-       assign_psn_space(ctx);
+       cxl_assign_psn_space(ctx);
 
        ctx->elem->ctxtime = 0; /* disable */
        ctx->elem->lpid = cpu_to_be32(mfspr(SPRN_LPID));
        ctx->elem->haurp = 0; /* disable */
        ctx->elem->sdr = cpu_to_be64(mfspr(SPRN_SDR1));
 
-       sr = 0;
-       if (ctx->master)
-               sr |= CXL_PSL_SR_An_MP;
-       if (mfspr(SPRN_LPCR) & LPCR_TC)
-               sr |= CXL_PSL_SR_An_TC;
-       /* HV=0, PR=1, R=1 for userspace
-        * For kernel contexts: this would need to change
-        */
-       sr |= CXL_PSL_SR_An_PR | CXL_PSL_SR_An_R;
-       set_endian(sr);
-       sr &= ~(CXL_PSL_SR_An_HV);
-       if (!test_tsk_thread_flag(current, TIF_32BIT))
-               sr |= CXL_PSL_SR_An_SF;
-       ctx->elem->common.pid = cpu_to_be32(current->pid);
+       pid = current->pid;
+       if (ctx->kernel)
+               pid = 0;
        ctx->elem->common.tid = 0;
-       ctx->elem->sr = cpu_to_be64(sr);
+       ctx->elem->common.pid = cpu_to_be32(pid);
+
+       ctx->elem->sr = cpu_to_be64(calculate_sr(ctx));
 
        ctx->elem->common.csrp = 0; /* disable */
        ctx->elem->common.aurp0 = 0; /* disable */
@@ -477,7 +489,7 @@ static int attach_afu_directed(struct cxl_context *ctx, u64 wed, u64 amr)
        ctx->elem->common.wed = cpu_to_be64(wed);
 
        /* first guy needs to enable */
-       if ((result = afu_check_and_enable(ctx->afu)))
+       if ((result = cxl_afu_check_and_enable(ctx->afu)))
                return result;
 
        add_process_element(ctx);
@@ -495,7 +507,7 @@ static int deactivate_afu_directed(struct cxl_afu *afu)
        cxl_sysfs_afu_m_remove(afu);
        cxl_chardev_afu_remove(afu);
 
-       cxl_afu_reset(afu);
+       __cxl_afu_reset(afu);
        cxl_afu_disable(afu);
        cxl_psl_purge(afu);
 
@@ -530,20 +542,15 @@ static int activate_dedicated_process(struct cxl_afu *afu)
 static int attach_dedicated(struct cxl_context *ctx, u64 wed, u64 amr)
 {
        struct cxl_afu *afu = ctx->afu;
-       u64 sr;
+       u64 pid;
        int rc;
 
-       sr = 0;
-       set_endian(sr);
-       if (ctx->master)
-               sr |= CXL_PSL_SR_An_MP;
-       if (mfspr(SPRN_LPCR) & LPCR_TC)
-               sr |= CXL_PSL_SR_An_TC;
-       sr |= CXL_PSL_SR_An_PR | CXL_PSL_SR_An_R;
-       if (!test_tsk_thread_flag(current, TIF_32BIT))
-               sr |= CXL_PSL_SR_An_SF;
-       cxl_p2n_write(afu, CXL_PSL_PID_TID_An, (u64)current->pid << 32);
-       cxl_p1n_write(afu, CXL_PSL_SR_An, sr);
+       pid = (u64)current->pid << 32;
+       if (ctx->kernel)
+               pid = 0;
+       cxl_p2n_write(afu, CXL_PSL_PID_TID_An, pid);
+
+       cxl_p1n_write(afu, CXL_PSL_SR_An, calculate_sr(ctx));
 
        if ((rc = cxl_write_sstp(afu, ctx->sstp0, ctx->sstp1)))
                return rc;
@@ -564,9 +571,9 @@ static int attach_dedicated(struct cxl_context *ctx, u64 wed, u64 amr)
        cxl_p2n_write(afu, CXL_PSL_AMR_An, amr);
 
        /* master only context for dedicated */
-       assign_psn_space(ctx);
+       cxl_assign_psn_space(ctx);
 
-       if ((rc = cxl_afu_reset(afu)))
+       if ((rc = __cxl_afu_reset(afu)))
                return rc;
 
        cxl_p2n_write(afu, CXL_PSL_WED_An, wed);
@@ -629,7 +636,7 @@ int cxl_attach_process(struct cxl_context *ctx, bool kernel, u64 wed, u64 amr)
 
 static inline int detach_process_native_dedicated(struct cxl_context *ctx)
 {
-       cxl_afu_reset(ctx->afu);
+       __cxl_afu_reset(ctx->afu);
        cxl_afu_disable(ctx->afu);
        cxl_psl_purge(ctx->afu);
        return 0;
index 1ef01647265f99b6330bbba6fdd1832707228cab..c68ef5806dbe122503742f57c6d1259a599b7cf7 100644 (file)
@@ -90,6 +90,7 @@
 /* This works a little different than the p1/p2 register accesses to make it
  * easier to pull out individual fields */
 #define AFUD_READ(afu, off)            in_be64(afu->afu_desc_mmio + off)
+#define AFUD_READ_LE(afu, off)         in_le64(afu->afu_desc_mmio + off)
 #define EXTRACT_PPC_BIT(val, bit)      (!!(val & PPC_BIT(bit)))
 #define EXTRACT_PPC_BITS(val, bs, be)  ((val & PPC_BITMASK(bs, be)) >> PPC_BITLSHIFT(be))
 
@@ -204,7 +205,7 @@ static void dump_cxl_config_space(struct pci_dev *dev)
        dev_info(&dev->dev, "p1 regs: %#llx, len: %#llx\n",
                p1_base(dev), p1_size(dev));
        dev_info(&dev->dev, "p2 regs: %#llx, len: %#llx\n",
-               p1_base(dev), p2_size(dev));
+               p2_base(dev), p2_size(dev));
        dev_info(&dev->dev, "BAR 4/5: %#llx, len: %#llx\n",
                pci_resource_start(dev, 4), pci_resource_len(dev, 4));
 
@@ -286,7 +287,8 @@ static void dump_cxl_config_space(struct pci_dev *dev)
 
 static void dump_afu_descriptor(struct cxl_afu *afu)
 {
-       u64 val;
+       u64 val, afu_cr_num, afu_cr_off, afu_cr_len;
+       int i;
 
 #define show_reg(name, what) \
        dev_info(&afu->dev, "afu desc: %30s: %#llx\n", name, what)
@@ -296,6 +298,7 @@ static void dump_afu_descriptor(struct cxl_afu *afu)
        show_reg("num_of_processes", AFUD_NUM_PROCS(val));
        show_reg("num_of_afu_CRs", AFUD_NUM_CRS(val));
        show_reg("req_prog_mode", val & 0xffffULL);
+       afu_cr_num = AFUD_NUM_CRS(val);
 
        val = AFUD_READ(afu, 0x8);
        show_reg("Reserved", val);
@@ -307,8 +310,10 @@ static void dump_afu_descriptor(struct cxl_afu *afu)
        val = AFUD_READ_CR(afu);
        show_reg("Reserved", (val >> (63-7)) & 0xff);
        show_reg("AFU_CR_len", AFUD_CR_LEN(val));
+       afu_cr_len = AFUD_CR_LEN(val) * 256;
 
        val = AFUD_READ_CR_OFF(afu);
+       afu_cr_off = val;
        show_reg("AFU_CR_offset", val);
 
        val = AFUD_READ_PPPSA(afu);
@@ -325,6 +330,11 @@ static void dump_afu_descriptor(struct cxl_afu *afu)
        val = AFUD_READ_EB_OFF(afu);
        show_reg("AFU_EB_offset", val);
 
+       for (i = 0; i < afu_cr_num; i++) {
+               val = AFUD_READ_LE(afu, afu_cr_off + i * afu_cr_len);
+               show_reg("CR Vendor", val & 0xffff);
+               show_reg("CR Device", (val >> 16) & 0xffff);
+       }
 #undef show_reg
 }
 
@@ -593,6 +603,22 @@ static int cxl_read_afu_descriptor(struct cxl_afu *afu)
        afu->crs_len = AFUD_CR_LEN(val) * 256;
        afu->crs_offset = AFUD_READ_CR_OFF(afu);
 
+
+       /* eb_len is in multiple of 4K */
+       afu->eb_len = AFUD_EB_LEN(AFUD_READ_EB(afu)) * 4096;
+       afu->eb_offset = AFUD_READ_EB_OFF(afu);
+
+       /* eb_off is 4K aligned so lower 12 bits are always zero */
+       if (EXTRACT_PPC_BITS(afu->eb_offset, 0, 11) != 0) {
+               dev_warn(&afu->dev,
+                        "Invalid AFU error buffer offset %Lx\n",
+                        afu->eb_offset);
+               dev_info(&afu->dev,
+                        "Ignoring AFU error buffer in the descriptor\n");
+               /* indicate that no afu buffer exists */
+               afu->eb_len = 0;
+       }
+
        return 0;
 }
 
@@ -631,7 +657,7 @@ static int sanitise_afu_regs(struct cxl_afu *afu)
        reg = cxl_p2n_read(afu, CXL_AFU_Cntl_An);
        if ((reg & CXL_AFU_Cntl_An_ES_MASK) != CXL_AFU_Cntl_An_ES_Disabled) {
                dev_warn(&afu->dev, "WARNING: AFU was not disabled: %#.16llx\n", reg);
-               if (cxl_afu_reset(afu))
+               if (__cxl_afu_reset(afu))
                        return -EIO;
                if (cxl_afu_disable(afu))
                        return -EIO;
@@ -672,6 +698,50 @@ static int sanitise_afu_regs(struct cxl_afu *afu)
        return 0;
 }
 
+#define ERR_BUFF_MAX_COPY_SIZE PAGE_SIZE
+/*
+ * afu_eb_read:
+ * Called from sysfs and reads the afu error info buffer. The h/w only supports
+ * 4/8 bytes aligned access. So in case the requested offset/count arent 8 byte
+ * aligned the function uses a bounce buffer which can be max PAGE_SIZE.
+ */
+ssize_t cxl_afu_read_err_buffer(struct cxl_afu *afu, char *buf,
+                               loff_t off, size_t count)
+{
+       loff_t aligned_start, aligned_end;
+       size_t aligned_length;
+       void *tbuf;
+       const void __iomem *ebuf = afu->afu_desc_mmio + afu->eb_offset;
+
+       if (count == 0 || off < 0 || (size_t)off >= afu->eb_len)
+               return 0;
+
+       /* calculate aligned read window */
+       count = min((size_t)(afu->eb_len - off), count);
+       aligned_start = round_down(off, 8);
+       aligned_end = round_up(off + count, 8);
+       aligned_length = aligned_end - aligned_start;
+
+       /* max we can copy in one read is PAGE_SIZE */
+       if (aligned_length > ERR_BUFF_MAX_COPY_SIZE) {
+               aligned_length = ERR_BUFF_MAX_COPY_SIZE;
+               count = ERR_BUFF_MAX_COPY_SIZE - (off & 0x7);
+       }
+
+       /* use bounce buffer for copy */
+       tbuf = (void *)__get_free_page(GFP_TEMPORARY);
+       if (!tbuf)
+               return -ENOMEM;
+
+       /* perform aligned read from the mmio region */
+       memcpy_fromio(tbuf, ebuf + aligned_start, aligned_length);
+       memcpy(buf, tbuf + (off & 0x7), count);
+
+       free_page((unsigned long)tbuf);
+
+       return count;
+}
+
 static int cxl_init_afu(struct cxl *adapter, int slice, struct pci_dev *dev)
 {
        struct cxl_afu *afu;
@@ -691,7 +761,7 @@ static int cxl_init_afu(struct cxl *adapter, int slice, struct pci_dev *dev)
                goto err2;
 
        /* We need to reset the AFU before we can read the AFU descriptor */
-       if ((rc = cxl_afu_reset(afu)))
+       if ((rc = __cxl_afu_reset(afu)))
                goto err2;
 
        if (cxl_verbose)
@@ -731,6 +801,9 @@ static int cxl_init_afu(struct cxl *adapter, int slice, struct pci_dev *dev)
 
        adapter->afu[afu->slice] = afu;
 
+       if ((rc = cxl_pci_vphb_add(afu)))
+               dev_info(&afu->dev, "Can't register vPHB\n");
+
        return 0;
 
 err_put2:
@@ -783,8 +856,10 @@ int cxl_reset(struct cxl *adapter)
 
        dev_info(&dev->dev, "CXL reset\n");
 
-       for (i = 0; i < adapter->slices; i++)
+       for (i = 0; i < adapter->slices; i++) {
+               cxl_pci_vphb_remove(adapter->afu[i]);
                cxl_remove_afu(adapter->afu[i]);
+       }
 
        /* pcie_warm_reset requests a fundamental pci reset which includes a
         * PERST assert/deassert.  PERST triggers a loading of the image
@@ -857,13 +932,13 @@ static int cxl_read_vsec(struct cxl *adapter, struct pci_dev *dev)
        u8 image_state;
 
        if (!(vsec = find_cxl_vsec(dev))) {
-               dev_err(&adapter->dev, "ABORTING: CXL VSEC not found!\n");
+               dev_err(&dev->dev, "ABORTING: CXL VSEC not found!\n");
                return -ENODEV;
        }
 
        CXL_READ_VSEC_LENGTH(dev, vsec, &vseclen);
        if (vseclen < CXL_VSEC_MIN_SIZE) {
-               pr_err("ABORTING: CXL VSEC too short\n");
+               dev_err(&dev->dev, "ABORTING: CXL VSEC too short\n");
                return -EINVAL;
        }
 
@@ -902,24 +977,24 @@ static int cxl_vsec_looks_ok(struct cxl *adapter, struct pci_dev *dev)
                return -EBUSY;
 
        if (adapter->vsec_status & CXL_UNSUPPORTED_FEATURES) {
-               dev_err(&adapter->dev, "ABORTING: CXL requires unsupported features\n");
+               dev_err(&dev->dev, "ABORTING: CXL requires unsupported features\n");
                return -EINVAL;
        }
 
        if (!adapter->slices) {
                /* Once we support dynamic reprogramming we can use the card if
                 * it supports loadable AFUs */
-               dev_err(&adapter->dev, "ABORTING: Device has no AFUs\n");
+               dev_err(&dev->dev, "ABORTING: Device has no AFUs\n");
                return -EINVAL;
        }
 
        if (!adapter->afu_desc_off || !adapter->afu_desc_size) {
-               dev_err(&adapter->dev, "ABORTING: VSEC shows no AFU descriptors\n");
+               dev_err(&dev->dev, "ABORTING: VSEC shows no AFU descriptors\n");
                return -EINVAL;
        }
 
        if (adapter->ps_size > p2_size(dev) - adapter->ps_off) {
-               dev_err(&adapter->dev, "ABORTING: Problem state size larger than "
+               dev_err(&dev->dev, "ABORTING: Problem state size larger than "
                                   "available in BAR2: 0x%llx > 0x%llx\n",
                         adapter->ps_size, p2_size(dev) - adapter->ps_off);
                return -EINVAL;
@@ -968,6 +1043,15 @@ static struct cxl *cxl_init_adapter(struct pci_dev *dev)
        if (!(adapter = cxl_alloc_adapter(dev)))
                return ERR_PTR(-ENOMEM);
 
+       if ((rc = cxl_read_vsec(adapter, dev)))
+               goto err1;
+
+       if ((rc = cxl_vsec_looks_ok(adapter, dev)))
+               goto err1;
+
+       if ((rc = setup_cxl_bars(dev)))
+               goto err1;
+
        if ((rc = switch_card_to_cxl(dev)))
                goto err1;
 
@@ -977,12 +1061,6 @@ static struct cxl *cxl_init_adapter(struct pci_dev *dev)
        if ((rc = dev_set_name(&adapter->dev, "card%i", adapter->adapter_num)))
                goto err2;
 
-       if ((rc = cxl_read_vsec(adapter, dev)))
-               goto err2;
-
-       if ((rc = cxl_vsec_looks_ok(adapter, dev)))
-               goto err2;
-
        if ((rc = cxl_update_image_control(adapter)))
                goto err2;
 
@@ -1067,9 +1145,6 @@ static int cxl_probe(struct pci_dev *dev, const struct pci_device_id *id)
        if (cxl_verbose)
                dump_cxl_config_space(dev);
 
-       if ((rc = setup_cxl_bars(dev)))
-               return rc;
-
        if ((rc = pci_enable_device(dev))) {
                dev_err(&dev->dev, "pci_enable_device failed: %i\n", rc);
                return rc;
@@ -1078,6 +1153,7 @@ static int cxl_probe(struct pci_dev *dev, const struct pci_device_id *id)
        adapter = cxl_init_adapter(dev);
        if (IS_ERR(adapter)) {
                dev_err(&dev->dev, "cxl_init_adapter failed: %li\n", PTR_ERR(adapter));
+               pci_disable_device(dev);
                return PTR_ERR(adapter);
        }
 
@@ -1092,16 +1168,18 @@ static int cxl_probe(struct pci_dev *dev, const struct pci_device_id *id)
 static void cxl_remove(struct pci_dev *dev)
 {
        struct cxl *adapter = pci_get_drvdata(dev);
-       int afu;
-
-       dev_warn(&dev->dev, "pci remove\n");
+       struct cxl_afu *afu;
+       int i;
 
        /*
         * Lock to prevent someone grabbing a ref through the adapter list as
         * we are removing it
         */
-       for (afu = 0; afu < adapter->slices; afu++)
-               cxl_remove_afu(adapter->afu[afu]);
+       for (i = 0; i < adapter->slices; i++) {
+               afu = adapter->afu[i];
+               cxl_pci_vphb_remove(afu);
+               cxl_remove_afu(afu);
+       }
        cxl_remove_adapter(adapter);
 }
 
@@ -1110,4 +1188,5 @@ struct pci_driver cxl_pci_driver = {
        .id_table = cxl_pci_tbl,
        .probe = cxl_probe,
        .remove = cxl_remove,
+       .shutdown = cxl_remove,
 };
index d0c38c7bc0c4bfb552f568f59f4f4371809b3b1f..31f38bc71a3d5d113263998141b62c4b9285d343 100644 (file)
@@ -185,7 +185,7 @@ static ssize_t reset_store_afu(struct device *device,
                goto err;
        }
 
-       if ((rc = cxl_afu_reset(afu)))
+       if ((rc = __cxl_afu_reset(afu)))
                goto err;
 
        rc = count;
@@ -356,6 +356,16 @@ static ssize_t api_version_compatible_show(struct device *device,
        return scnprintf(buf, PAGE_SIZE, "%i\n", CXL_API_VERSION_COMPATIBLE);
 }
 
+static ssize_t afu_eb_read(struct file *filp, struct kobject *kobj,
+                              struct bin_attribute *bin_attr, char *buf,
+                              loff_t off, size_t count)
+{
+       struct cxl_afu *afu = to_cxl_afu(container_of(kobj,
+                                                     struct device, kobj));
+
+       return cxl_afu_read_err_buffer(afu, buf, off, count);
+}
+
 static struct device_attribute afu_attrs[] = {
        __ATTR_RO(mmio_size),
        __ATTR_RO(irqs_min),
@@ -534,6 +544,10 @@ void cxl_sysfs_afu_remove(struct cxl_afu *afu)
        struct afu_config_record *cr, *tmp;
        int i;
 
+       /* remove the err buffer bin attribute */
+       if (afu->eb_len)
+               device_remove_bin_file(&afu->dev, &afu->attr_eb);
+
        for (i = 0; i < ARRAY_SIZE(afu_attrs); i++)
                device_remove_file(&afu->dev, &afu_attrs[i]);
 
@@ -555,6 +569,22 @@ int cxl_sysfs_afu_add(struct cxl_afu *afu)
                        goto err;
        }
 
+       /* conditionally create the add the binary file for error info buffer */
+       if (afu->eb_len) {
+               afu->attr_eb.attr.name = "afu_err_buff";
+               afu->attr_eb.attr.mode = S_IRUGO;
+               afu->attr_eb.size = afu->eb_len;
+               afu->attr_eb.read = afu_eb_read;
+
+               rc = device_create_bin_file(&afu->dev, &afu->attr_eb);
+               if (rc) {
+                       dev_err(&afu->dev,
+                               "Unable to create eb attr for the afu. Err(%d)\n",
+                               rc);
+                       goto err;
+               }
+       }
+
        for (i = 0; i < afu->crs_num; i++) {
                cr = cxl_sysfs_afu_new_cr(afu, i);
                if (IS_ERR(cr)) {
@@ -570,6 +600,9 @@ err1:
        cxl_sysfs_afu_remove(afu);
        return rc;
 err:
+       /* reset the eb_len as we havent created the bin attr */
+       afu->eb_len = 0;
+
        for (i--; i >= 0; i--)
                device_remove_file(&afu->dev, &afu_attrs[i]);
        return rc;
diff --git a/drivers/misc/cxl/vphb.c b/drivers/misc/cxl/vphb.c
new file mode 100644 (file)
index 0000000..b1d1983
--- /dev/null
@@ -0,0 +1,270 @@
+/*
+ * Copyright 2014 IBM Corp.
+ *
+ * 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>
+#include <misc/cxl.h>
+#include "cxl.h"
+
+static int cxl_dma_set_mask(struct pci_dev *pdev, u64 dma_mask)
+{
+       if (dma_mask < DMA_BIT_MASK(64)) {
+               pr_info("%s only 64bit DMA supported on CXL", __func__);
+               return -EIO;
+       }
+
+       *(pdev->dev.dma_mask) = dma_mask;
+       return 0;
+}
+
+static int cxl_pci_probe_mode(struct pci_bus *bus)
+{
+       return PCI_PROBE_NORMAL;
+}
+
+static int cxl_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type)
+{
+       return -ENODEV;
+}
+
+static void cxl_teardown_msi_irqs(struct pci_dev *pdev)
+{
+       /*
+        * MSI should never be set but need still need to provide this call
+        * back.
+        */
+}
+
+static bool cxl_pci_enable_device_hook(struct pci_dev *dev)
+{
+       struct pci_controller *phb;
+       struct cxl_afu *afu;
+       struct cxl_context *ctx;
+
+       phb = pci_bus_to_host(dev->bus);
+       afu = (struct cxl_afu *)phb->private_data;
+       set_dma_ops(&dev->dev, &dma_direct_ops);
+       set_dma_offset(&dev->dev, PAGE_OFFSET);
+
+       /*
+        * Allocate a context to do cxl things too.  If we eventually do real
+        * DMA ops, we'll need a default context to attach them to
+        */
+       ctx = cxl_dev_context_init(dev);
+       if (!ctx)
+               return false;
+       dev->dev.archdata.cxl_ctx = ctx;
+
+       return (cxl_afu_check_and_enable(afu) == 0);
+}
+
+static void cxl_pci_disable_device(struct pci_dev *dev)
+{
+       struct cxl_context *ctx = cxl_get_context(dev);
+
+       if (ctx) {
+               if (ctx->status == STARTED) {
+                       dev_err(&dev->dev, "Default context started\n");
+                       return;
+               }
+               dev->dev.archdata.cxl_ctx = NULL;
+               cxl_release_context(ctx);
+       }
+}
+
+static resource_size_t cxl_pci_window_alignment(struct pci_bus *bus,
+                                               unsigned long type)
+{
+       return 1;
+}
+
+static void cxl_pci_reset_secondary_bus(struct pci_dev *dev)
+{
+       /* Should we do an AFU reset here ? */
+}
+
+static int cxl_pcie_cfg_record(u8 bus, u8 devfn)
+{
+       return (bus << 8) + devfn;
+}
+
+static unsigned long cxl_pcie_cfg_addr(struct pci_controller* phb,
+                                      u8 bus, u8 devfn, int offset)
+{
+       int record = cxl_pcie_cfg_record(bus, devfn);
+
+       return (unsigned long)phb->cfg_addr + ((unsigned long)phb->cfg_data * record) + offset;
+}
+
+
+static int cxl_pcie_config_info(struct pci_bus *bus, unsigned int devfn,
+                               int offset, int len,
+                               volatile void __iomem **ioaddr,
+                               u32 *mask, int *shift)
+{
+       struct pci_controller *phb;
+       struct cxl_afu *afu;
+       unsigned long addr;
+
+       phb = pci_bus_to_host(bus);
+       afu = (struct cxl_afu *)phb->private_data;
+       if (phb == NULL)
+               return PCIBIOS_DEVICE_NOT_FOUND;
+       if (cxl_pcie_cfg_record(bus->number, devfn) > afu->crs_num)
+               return PCIBIOS_DEVICE_NOT_FOUND;
+       if (offset >= (unsigned long)phb->cfg_data)
+               return PCIBIOS_BAD_REGISTER_NUMBER;
+       addr = cxl_pcie_cfg_addr(phb, bus->number, devfn, offset);
+
+       *ioaddr = (void *)(addr & ~0x3ULL);
+       *shift = ((addr & 0x3) * 8);
+       switch (len) {
+       case 1:
+               *mask = 0xff;
+               break;
+       case 2:
+               *mask = 0xffff;
+               break;
+       default:
+               *mask = 0xffffffff;
+               break;
+       }
+       return 0;
+}
+
+static int cxl_pcie_read_config(struct pci_bus *bus, unsigned int devfn,
+                               int offset, int len, u32 *val)
+{
+       volatile void __iomem *ioaddr;
+       int shift, rc;
+       u32 mask;
+
+       rc = cxl_pcie_config_info(bus, devfn, offset, len, &ioaddr,
+                                 &mask, &shift);
+       if (rc)
+               return rc;
+
+       /* Can only read 32 bits */
+       *val = (in_le32(ioaddr) >> shift) & mask;
+       return PCIBIOS_SUCCESSFUL;
+}
+
+static int cxl_pcie_write_config(struct pci_bus *bus, unsigned int devfn,
+                                int offset, int len, u32 val)
+{
+       volatile void __iomem *ioaddr;
+       u32 v, mask;
+       int shift, rc;
+
+       rc = cxl_pcie_config_info(bus, devfn, offset, len, &ioaddr,
+                                 &mask, &shift);
+       if (rc)
+               return rc;
+
+       /* Can only write 32 bits so do read-modify-write */
+       mask <<= shift;
+       val <<= shift;
+
+       v = (in_le32(ioaddr) & ~mask) || (val & mask);
+
+       out_le32(ioaddr, v);
+       return PCIBIOS_SUCCESSFUL;
+}
+
+static struct pci_ops cxl_pcie_pci_ops =
+{
+       .read = cxl_pcie_read_config,
+       .write = cxl_pcie_write_config,
+};
+
+
+static struct pci_controller_ops cxl_pci_controller_ops =
+{
+       .probe_mode = cxl_pci_probe_mode,
+       .enable_device_hook = cxl_pci_enable_device_hook,
+       .disable_device = cxl_pci_disable_device,
+       .release_device = cxl_pci_disable_device,
+       .window_alignment = cxl_pci_window_alignment,
+       .reset_secondary_bus = cxl_pci_reset_secondary_bus,
+       .setup_msi_irqs = cxl_setup_msi_irqs,
+       .teardown_msi_irqs = cxl_teardown_msi_irqs,
+       .dma_set_mask = cxl_dma_set_mask,
+};
+
+int cxl_pci_vphb_add(struct cxl_afu *afu)
+{
+       struct pci_dev *phys_dev;
+       struct pci_controller *phb, *phys_phb;
+
+       phys_dev = to_pci_dev(afu->adapter->dev.parent);
+       phys_phb = pci_bus_to_host(phys_dev->bus);
+
+       /* Alloc and setup PHB data structure */
+       phb = pcibios_alloc_controller(phys_phb->dn);
+
+       if (!phb)
+               return -ENODEV;
+
+       /* Setup parent in sysfs */
+       phb->parent = &phys_dev->dev;
+
+       /* Setup the PHB using arch provided callback */
+       phb->ops = &cxl_pcie_pci_ops;
+       phb->cfg_addr = afu->afu_desc_mmio + afu->crs_offset;
+       phb->cfg_data = (void *)(u64)afu->crs_len;
+       phb->private_data = afu;
+       phb->controller_ops = cxl_pci_controller_ops;
+
+       /* Scan the bus */
+       pcibios_scan_phb(phb);
+       if (phb->bus == NULL)
+               return -ENXIO;
+
+       /* Claim resources. This might need some rework as well depending
+        * whether we are doing probe-only or not, like assigning unassigned
+        * resources etc...
+        */
+       pcibios_claim_one_bus(phb->bus);
+
+       /* Add probed PCI devices to the device model */
+       pci_bus_add_devices(phb->bus);
+
+       afu->phb = phb;
+
+       return 0;
+}
+
+
+void cxl_pci_vphb_remove(struct cxl_afu *afu)
+{
+       struct pci_controller *phb;
+
+       /* If there is no configuration record we won't have one of these */
+       if (!afu || !afu->phb)
+               return;
+
+       phb = afu->phb;
+
+       pci_remove_root_bus(phb->bus);
+}
+
+struct cxl_afu *cxl_pci_to_afu(struct pci_dev *dev)
+{
+       struct pci_controller *phb;
+
+       phb = pci_bus_to_host(dev->bus);
+
+       return (struct cxl_afu *)phb->private_data;
+}
+EXPORT_SYMBOL_GPL(cxl_pci_to_afu);
+
+unsigned int cxl_pci_to_cfg_record(struct pci_dev *dev)
+{
+       return cxl_pcie_cfg_record(dev->bus->number, dev->devfn);
+}
+EXPORT_SYMBOL_GPL(cxl_pci_to_cfg_record);
index 60f7141a6b02e66c23b59404ea59ba7d716c057f..c9c3d20b784b669bf130cffb716b8525470738fa 100644 (file)
@@ -913,6 +913,9 @@ static int mmc_blk_cmd_recovery(struct mmc_card *card, struct request *req,
                if (!err)
                        break;
 
+               /* Re-tune if needed */
+               mmc_retune_recheck(card->host);
+
                prev_cmd_status_valid = false;
                pr_err("%s: error %d sending status command, %sing\n",
                       req->rq_disk->disk_name, err, retry ? "retry" : "abort");
@@ -1204,6 +1207,7 @@ static int mmc_blk_err_check(struct mmc_card *card,
                                                    mmc_active);
        struct mmc_blk_request *brq = &mq_mrq->brq;
        struct request *req = mq_mrq->req;
+       int need_retune = card->host->need_retune;
        int ecc_err = 0, gen_err = 0;
 
        /*
@@ -1271,6 +1275,12 @@ static int mmc_blk_err_check(struct mmc_card *card,
        }
 
        if (brq->data.error) {
+               if (need_retune && !brq->retune_retry_done) {
+                       pr_info("%s: retrying because a re-tune was needed\n",
+                               req->rq_disk->disk_name);
+                       brq->retune_retry_done = 1;
+                       return MMC_BLK_RETRY;
+               }
                pr_err("%s: error %d transferring data, sector %u, nr %u, cmd response %#x, card status %#x\n",
                       req->rq_disk->disk_name, brq->data.error,
                       (unsigned)blk_rq_pos(req),
@@ -1830,7 +1840,7 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)
        struct mmc_blk_data *md = mq->data;
        struct mmc_card *card = md->queue.card;
        struct mmc_blk_request *brq = &mq->mqrq_cur->brq;
-       int ret = 1, disable_multi = 0, retry = 0, type;
+       int ret = 1, disable_multi = 0, retry = 0, type, retune_retry_done = 0;
        enum mmc_blk_status status;
        struct mmc_queue_req *mq_rq;
        struct request *req = rqc;
@@ -1910,10 +1920,13 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)
                        break;
                case MMC_BLK_CMD_ERR:
                        ret = mmc_blk_cmd_err(md, card, brq, req, ret);
-                       if (!mmc_blk_reset(md, card->host, type))
-                               break;
-                       goto cmd_abort;
+                       if (mmc_blk_reset(md, card->host, type))
+                               goto cmd_abort;
+                       if (!ret)
+                               goto start_new_req;
+                       break;
                case MMC_BLK_RETRY:
+                       retune_retry_done = brq->retune_retry_done;
                        if (retry++ < 5)
                                break;
                        /* Fall through */
@@ -1976,6 +1989,7 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)
                                mmc_start_req(card->host,
                                                &mq_rq->mmc_active, NULL);
                        }
+                       mq_rq->brq.retune_retry_done = retune_retry_done;
                }
        } while (ret);
 
@@ -2217,7 +2231,8 @@ static struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card)
                 * The CSD capacity field is in units of read_blkbits.
                 * set_capacity takes units of 512 bytes.
                 */
-               size = card->csd.capacity << (card->csd.read_blkbits - 9);
+               size = (typeof(sector_t))card->csd.capacity
+                       << (card->csd.read_blkbits - 9);
        }
 
        return mmc_blk_alloc_req(card, &card->dev, size, false, NULL,
index 53b741398b9330e33136cc4b6e0b8eddae69cf96..b78cf5d403a33b74244a39245b9df6f9024c4d24 100644 (file)
@@ -268,8 +268,6 @@ static int mmc_test_wait_busy(struct mmc_test_card *test)
 static int mmc_test_buffer_transfer(struct mmc_test_card *test,
        u8 *buffer, unsigned addr, unsigned blksz, int write)
 {
-       int ret;
-
        struct mmc_request mrq = {0};
        struct mmc_command cmd = {0};
        struct mmc_command stop = {0};
@@ -292,11 +290,7 @@ static int mmc_test_buffer_transfer(struct mmc_test_card *test,
        if (data.error)
                return data.error;
 
-       ret = mmc_test_wait_busy(test);
-       if (ret)
-               return ret;
-
-       return 0;
+       return mmc_test_wait_busy(test);
 }
 
 static void mmc_test_free_mem(struct mmc_test_mem *mem)
@@ -826,9 +820,7 @@ static int mmc_test_nonblock_transfer(struct mmc_test_card *test,
                                mmc_test_nonblock_reset(&mrq1, &cmd1,
                                                        &stop1, &data1);
                }
-               done_areq = cur_areq;
-               cur_areq = other_areq;
-               other_areq = done_areq;
+               swap(cur_areq, other_areq);
                dev_addr += blocks;
        }
 
@@ -994,11 +986,7 @@ static int mmc_test_basic_write(struct mmc_test_card *test)
 
        sg_init_one(&sg, test->buffer, 512);
 
-       ret = mmc_test_simple_transfer(test, &sg, 1, 0, 1, 512, 1);
-       if (ret)
-               return ret;
-
-       return 0;
+       return mmc_test_simple_transfer(test, &sg, 1, 0, 1, 512, 1);
 }
 
 static int mmc_test_basic_read(struct mmc_test_card *test)
@@ -1012,44 +1000,29 @@ static int mmc_test_basic_read(struct mmc_test_card *test)
 
        sg_init_one(&sg, test->buffer, 512);
 
-       ret = mmc_test_simple_transfer(test, &sg, 1, 0, 1, 512, 0);
-       if (ret)
-               return ret;
-
-       return 0;
+       return mmc_test_simple_transfer(test, &sg, 1, 0, 1, 512, 0);
 }
 
 static int mmc_test_verify_write(struct mmc_test_card *test)
 {
-       int ret;
        struct scatterlist sg;
 
        sg_init_one(&sg, test->buffer, 512);
 
-       ret = mmc_test_transfer(test, &sg, 1, 0, 1, 512, 1);
-       if (ret)
-               return ret;
-
-       return 0;
+       return mmc_test_transfer(test, &sg, 1, 0, 1, 512, 1);
 }
 
 static int mmc_test_verify_read(struct mmc_test_card *test)
 {
-       int ret;
        struct scatterlist sg;
 
        sg_init_one(&sg, test->buffer, 512);
 
-       ret = mmc_test_transfer(test, &sg, 1, 0, 1, 512, 0);
-       if (ret)
-               return ret;
-
-       return 0;
+       return mmc_test_transfer(test, &sg, 1, 0, 1, 512, 0);
 }
 
 static int mmc_test_multi_write(struct mmc_test_card *test)
 {
-       int ret;
        unsigned int size;
        struct scatterlist sg;
 
@@ -1066,16 +1039,11 @@ static int mmc_test_multi_write(struct mmc_test_card *test)
 
        sg_init_one(&sg, test->buffer, size);
 
-       ret = mmc_test_transfer(test, &sg, 1, 0, size/512, 512, 1);
-       if (ret)
-               return ret;
-
-       return 0;
+       return mmc_test_transfer(test, &sg, 1, 0, size/512, 512, 1);
 }
 
 static int mmc_test_multi_read(struct mmc_test_card *test)
 {
-       int ret;
        unsigned int size;
        struct scatterlist sg;
 
@@ -1092,11 +1060,7 @@ static int mmc_test_multi_read(struct mmc_test_card *test)
 
        sg_init_one(&sg, test->buffer, size);
 
-       ret = mmc_test_transfer(test, &sg, 1, 0, size/512, 512, 0);
-       if (ret)
-               return ret;
-
-       return 0;
+       return mmc_test_transfer(test, &sg, 1, 0, size/512, 512, 0);
 }
 
 static int mmc_test_pow2_write(struct mmc_test_card *test)
@@ -1263,11 +1227,7 @@ static int mmc_test_xfersize_write(struct mmc_test_card *test)
        if (ret)
                return ret;
 
-       ret = mmc_test_broken_transfer(test, 1, 512, 1);
-       if (ret)
-               return ret;
-
-       return 0;
+       return mmc_test_broken_transfer(test, 1, 512, 1);
 }
 
 static int mmc_test_xfersize_read(struct mmc_test_card *test)
@@ -1278,11 +1238,7 @@ static int mmc_test_xfersize_read(struct mmc_test_card *test)
        if (ret)
                return ret;
 
-       ret = mmc_test_broken_transfer(test, 1, 512, 0);
-       if (ret)
-               return ret;
-
-       return 0;
+       return mmc_test_broken_transfer(test, 1, 512, 0);
 }
 
 static int mmc_test_multi_xfersize_write(struct mmc_test_card *test)
@@ -1296,11 +1252,7 @@ static int mmc_test_multi_xfersize_write(struct mmc_test_card *test)
        if (ret)
                return ret;
 
-       ret = mmc_test_broken_transfer(test, 2, 512, 1);
-       if (ret)
-               return ret;
-
-       return 0;
+       return mmc_test_broken_transfer(test, 2, 512, 1);
 }
 
 static int mmc_test_multi_xfersize_read(struct mmc_test_card *test)
@@ -1314,48 +1266,33 @@ static int mmc_test_multi_xfersize_read(struct mmc_test_card *test)
        if (ret)
                return ret;
 
-       ret = mmc_test_broken_transfer(test, 2, 512, 0);
-       if (ret)
-               return ret;
-
-       return 0;
+       return mmc_test_broken_transfer(test, 2, 512, 0);
 }
 
 #ifdef CONFIG_HIGHMEM
 
 static int mmc_test_write_high(struct mmc_test_card *test)
 {
-       int ret;
        struct scatterlist sg;
 
        sg_init_table(&sg, 1);
        sg_set_page(&sg, test->highmem, 512, 0);
 
-       ret = mmc_test_transfer(test, &sg, 1, 0, 1, 512, 1);
-       if (ret)
-               return ret;
-
-       return 0;
+       return mmc_test_transfer(test, &sg, 1, 0, 1, 512, 1);
 }
 
 static int mmc_test_read_high(struct mmc_test_card *test)
 {
-       int ret;
        struct scatterlist sg;
 
        sg_init_table(&sg, 1);
        sg_set_page(&sg, test->highmem, 512, 0);
 
-       ret = mmc_test_transfer(test, &sg, 1, 0, 1, 512, 0);
-       if (ret)
-               return ret;
-
-       return 0;
+       return mmc_test_transfer(test, &sg, 1, 0, 1, 512, 0);
 }
 
 static int mmc_test_multi_write_high(struct mmc_test_card *test)
 {
-       int ret;
        unsigned int size;
        struct scatterlist sg;
 
@@ -1373,16 +1310,11 @@ static int mmc_test_multi_write_high(struct mmc_test_card *test)
        sg_init_table(&sg, 1);
        sg_set_page(&sg, test->highmem, size, 0);
 
-       ret = mmc_test_transfer(test, &sg, 1, 0, size/512, 512, 1);
-       if (ret)
-               return ret;
-
-       return 0;
+       return mmc_test_transfer(test, &sg, 1, 0, size/512, 512, 1);
 }
 
 static int mmc_test_multi_read_high(struct mmc_test_card *test)
 {
-       int ret;
        unsigned int size;
        struct scatterlist sg;
 
@@ -1400,11 +1332,7 @@ static int mmc_test_multi_read_high(struct mmc_test_card *test)
        sg_init_table(&sg, 1);
        sg_set_page(&sg, test->highmem, size, 0);
 
-       ret = mmc_test_transfer(test, &sg, 1, 0, size/512, 512, 0);
-       if (ret)
-               return ret;
-
-       return 0;
+       return mmc_test_transfer(test, &sg, 1, 0, size/512, 512, 0);
 }
 
 #else
index 8efa3684aef849174ccef4053e049e3c95d8646f..b5a2b145d89f6b32c42c67e25cc029c1ef6744ea 100644 (file)
@@ -56,7 +56,6 @@ static int mmc_queue_thread(void *d)
        down(&mq->thread_sem);
        do {
                struct request *req = NULL;
-               struct mmc_queue_req *tmp;
                unsigned int cmd_flags = 0;
 
                spin_lock_irq(q->queue_lock);
@@ -69,6 +68,7 @@ static int mmc_queue_thread(void *d)
                        set_current_state(TASK_RUNNING);
                        cmd_flags = req ? req->cmd_flags : 0;
                        mq->issue_fn(mq, req);
+                       cond_resched();
                        if (mq->flags & MMC_QUEUE_NEW_REQUEST) {
                                mq->flags &= ~MMC_QUEUE_NEW_REQUEST;
                                continue; /* fetch again */
@@ -86,9 +86,7 @@ static int mmc_queue_thread(void *d)
 
                        mq->mqrq_prev->brq.mrq.data = NULL;
                        mq->mqrq_prev->req = NULL;
-                       tmp = mq->mqrq_prev;
-                       mq->mqrq_prev = mq->mqrq_cur;
-                       mq->mqrq_cur = tmp;
+                       swap(mq->mqrq_prev, mq->mqrq_cur);
                } else {
                        if (kthread_should_stop()) {
                                set_current_state(TASK_RUNNING);
index 99e6521e61696202c036dfb00fe6bdcd96c0f613..36cddab57d776322c3912241f274e06620a77251 100644 (file)
@@ -12,6 +12,7 @@ struct mmc_blk_request {
        struct mmc_command      cmd;
        struct mmc_command      stop;
        struct mmc_data         data;
+       int                     retune_retry_done;
 };
 
 enum mmc_packed_type {
index 92e7671426ebc214ce2d1ff2c35df50e68f02ee0..9ad73f30f744fd3f1f5261c6a0480ea90460a036 100644 (file)
@@ -133,6 +133,12 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq)
        struct mmc_command *cmd = mrq->cmd;
        int err = cmd->error;
 
+       /* Flag re-tuning needed on CRC errors */
+       if (err == -EILSEQ || (mrq->sbc && mrq->sbc->error == -EILSEQ) ||
+           (mrq->data && mrq->data->error == -EILSEQ) ||
+           (mrq->stop && mrq->stop->error == -EILSEQ))
+               mmc_retune_needed(host);
+
        if (err && cmd->retries && mmc_host_is_spi(host)) {
                if (cmd->resp[0] & R1_SPI_ILLEGAL_COMMAND)
                        cmd->retries = 0;
@@ -186,12 +192,29 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq)
 
 EXPORT_SYMBOL(mmc_request_done);
 
+static void __mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
+{
+       int err;
+
+       /* Assumes host controller has been runtime resumed by mmc_claim_host */
+       err = mmc_retune(host);
+       if (err) {
+               mrq->cmd->error = err;
+               mmc_request_done(host, mrq);
+               return;
+       }
+
+       host->ops->request(host, mrq);
+}
+
 static int mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
 {
 #ifdef CONFIG_MMC_DEBUG
        unsigned int i, sz;
        struct scatterlist *sg;
 #endif
+       mmc_retune_hold(host);
+
        if (mmc_card_removed(host->card))
                return -ENOMEDIUM;
 
@@ -252,7 +275,7 @@ static int mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
        }
        mmc_host_clk_hold(host);
        led_trigger_event(host->led, LED_FULL);
-       host->ops->request(host, mrq);
+       __mmc_start_request(host, mrq);
 
        return 0;
 }
@@ -301,12 +324,15 @@ void mmc_start_bkops(struct mmc_card *card, bool from_exception)
                use_busy_signal = false;
        }
 
+       mmc_retune_hold(card->host);
+
        err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
                        EXT_CSD_BKOPS_START, 1, timeout,
                        use_busy_signal, true, false);
        if (err) {
                pr_warn("%s: Error %d starting bkops\n",
                        mmc_hostname(card->host), err);
+               mmc_retune_release(card->host);
                goto out;
        }
 
@@ -317,6 +343,8 @@ void mmc_start_bkops(struct mmc_card *card, bool from_exception)
         */
        if (!use_busy_signal)
                mmc_card_set_doing_bkops(card);
+       else
+               mmc_retune_release(card->host);
 out:
        mmc_release_host(card->host);
 }
@@ -417,22 +445,22 @@ static int mmc_wait_for_data_req_done(struct mmc_host *host,
                                                            host->areq);
                                break; /* return err */
                        } else {
+                               mmc_retune_recheck(host);
                                pr_info("%s: req failed (CMD%u): %d, retrying...\n",
                                        mmc_hostname(host),
                                        cmd->opcode, cmd->error);
                                cmd->retries--;
                                cmd->error = 0;
-                               host->ops->request(host, mrq);
+                               __mmc_start_request(host, mrq);
                                continue; /* wait for done/new event again */
                        }
                } else if (context_info->is_new_req) {
                        context_info->is_new_req = false;
-                       if (!next_req) {
-                               err = MMC_BLK_NEW_REQUEST;
-                               break; /* return err */
-                       }
+                       if (!next_req)
+                               return MMC_BLK_NEW_REQUEST;
                }
        }
+       mmc_retune_release(host);
        return err;
 }
 
@@ -467,12 +495,16 @@ static void mmc_wait_for_req_done(struct mmc_host *host,
                    mmc_card_removed(host->card))
                        break;
 
+               mmc_retune_recheck(host);
+
                pr_debug("%s: req failed (CMD%u): %d, retrying...\n",
                         mmc_hostname(host), cmd->opcode, cmd->error);
                cmd->retries--;
                cmd->error = 0;
-               host->ops->request(host, mrq);
+               __mmc_start_request(host, mrq);
        }
+
+       mmc_retune_release(host);
 }
 
 /**
@@ -728,6 +760,7 @@ int mmc_stop_bkops(struct mmc_card *card)
         */
        if (!err || (err == -EINVAL)) {
                mmc_card_clr_doing_bkops(card);
+               mmc_retune_release(card->host);
                err = 0;
        }
 
@@ -1109,6 +1142,8 @@ int mmc_execute_tuning(struct mmc_card *card)
 
        if (err)
                pr_err("%s: tuning execution failed\n", mmc_hostname(host));
+       else
+               mmc_retune_enable(host);
 
        return err;
 }
@@ -1140,6 +1175,8 @@ void mmc_set_bus_width(struct mmc_host *host, unsigned int width)
  */
 void mmc_set_initial_state(struct mmc_host *host)
 {
+       mmc_retune_disable(host);
+
        if (mmc_host_is_spi(host))
                host->ios.chip_select = MMC_CS_HIGH;
        else
@@ -1147,6 +1184,7 @@ void mmc_set_initial_state(struct mmc_host *host)
        host->ios.bus_mode = MMC_BUSMODE_PUSHPULL;
        host->ios.bus_width = MMC_BUS_WIDTH_1;
        host->ios.timing = MMC_TIMING_LEGACY;
+       host->ios.drv_type = 0;
 
        mmc_set_ios(host);
 }
@@ -1551,8 +1589,8 @@ int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage, u32 ocr)
                goto power_cycle;
        }
 
-       /* Keep clock gated for at least 5 ms */
-       mmc_delay(5);
+       /* Keep clock gated for at least 10 ms, though spec only says 5 ms */
+       mmc_delay(10);
        host->ios.clock = clock;
        mmc_set_ios(host);
 
@@ -1601,6 +1639,44 @@ void mmc_set_driver_type(struct mmc_host *host, unsigned int drv_type)
        mmc_host_clk_release(host);
 }
 
+int mmc_select_drive_strength(struct mmc_card *card, unsigned int max_dtr,
+                             int card_drv_type, int *drv_type)
+{
+       struct mmc_host *host = card->host;
+       int host_drv_type = SD_DRIVER_TYPE_B;
+       int drive_strength;
+
+       *drv_type = 0;
+
+       if (!host->ops->select_drive_strength)
+               return 0;
+
+       /* Use SD definition of driver strength for hosts */
+       if (host->caps & MMC_CAP_DRIVER_TYPE_A)
+               host_drv_type |= SD_DRIVER_TYPE_A;
+
+       if (host->caps & MMC_CAP_DRIVER_TYPE_C)
+               host_drv_type |= SD_DRIVER_TYPE_C;
+
+       if (host->caps & MMC_CAP_DRIVER_TYPE_D)
+               host_drv_type |= SD_DRIVER_TYPE_D;
+
+       /*
+        * The drive strength that the hardware can support
+        * depends on the board design.  Pass the appropriate
+        * information and let the hardware specific code
+        * return what is possible given the options
+        */
+       mmc_host_clk_hold(host);
+       drive_strength = host->ops->select_drive_strength(card, max_dtr,
+                                                         host_drv_type,
+                                                         card_drv_type,
+                                                         drv_type);
+       mmc_host_clk_release(host);
+
+       return drive_strength;
+}
+
 /*
  * Apply power to the MMC stack.  This is a two-stage process.
  * First, we enable power to the card without the clock running.
@@ -1970,6 +2046,8 @@ static int mmc_do_erase(struct mmc_card *card, unsigned int from,
        unsigned long timeout;
        int err;
 
+       mmc_retune_hold(card->host);
+
        /*
         * qty is used to calculate the erase timeout which depends on how many
         * erase groups (or allocation units in SD terminology) are affected.
@@ -2073,6 +2151,7 @@ static int mmc_do_erase(struct mmc_card *card, unsigned int from,
        } while (!(cmd.resp[0] & R1_READY_FOR_DATA) ||
                 (R1_CURRENT_STATE(cmd.resp[0]) == R1_STATE_PRG));
 out:
+       mmc_retune_release(card->host);
        return err;
 }
 
@@ -2331,7 +2410,8 @@ int mmc_hw_reset(struct mmc_host *host)
        ret = host->bus_ops->reset(host);
        mmc_bus_put(host);
 
-       pr_warn("%s: tried to reset card\n", mmc_hostname(host));
+       if (ret != -EOPNOTSUPP)
+               pr_warn("%s: tried to reset card\n", mmc_hostname(host));
 
        return ret;
 }
index cfba3c05aab1ca8ec536ba3f87020a1a0b93437c..1a22a82209b26a30c261f4affb43f23641926f50 100644 (file)
@@ -50,6 +50,8 @@ int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage, u32 ocr);
 int __mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage);
 void mmc_set_timing(struct mmc_host *host, unsigned int timing);
 void mmc_set_driver_type(struct mmc_host *host, unsigned int drv_type);
+int mmc_select_drive_strength(struct mmc_card *card, unsigned int max_dtr,
+                             int card_drv_type, int *drv_type);
 void mmc_power_up(struct mmc_host *host, u32 ocr);
 void mmc_power_off(struct mmc_host *host);
 void mmc_power_cycle(struct mmc_host *host, u32 ocr);
@@ -88,6 +90,8 @@ void mmc_remove_card_debugfs(struct mmc_card *card);
 void mmc_init_context_info(struct mmc_host *host);
 
 int mmc_execute_tuning(struct mmc_card *card);
+int mmc_hs200_to_hs400(struct mmc_card *card);
+int mmc_hs400_to_hs200(struct mmc_card *card);
 
 #endif
 
index 8be0df758e68270e94f9dacef3fe8572dc45701e..99a9c9011c501011db0953319dc4d82b003ead6f 100644 (file)
@@ -301,6 +301,90 @@ static inline void mmc_host_clk_sysfs_init(struct mmc_host *host)
 
 #endif
 
+void mmc_retune_enable(struct mmc_host *host)
+{
+       host->can_retune = 1;
+       if (host->retune_period)
+               mod_timer(&host->retune_timer,
+                         jiffies + host->retune_period * HZ);
+}
+
+void mmc_retune_disable(struct mmc_host *host)
+{
+       host->can_retune = 0;
+       del_timer_sync(&host->retune_timer);
+       host->retune_now = 0;
+       host->need_retune = 0;
+}
+
+void mmc_retune_timer_stop(struct mmc_host *host)
+{
+       del_timer_sync(&host->retune_timer);
+}
+EXPORT_SYMBOL(mmc_retune_timer_stop);
+
+void mmc_retune_hold(struct mmc_host *host)
+{
+       if (!host->hold_retune)
+               host->retune_now = 1;
+       host->hold_retune += 1;
+}
+
+void mmc_retune_release(struct mmc_host *host)
+{
+       if (host->hold_retune)
+               host->hold_retune -= 1;
+       else
+               WARN_ON(1);
+}
+
+int mmc_retune(struct mmc_host *host)
+{
+       bool return_to_hs400 = false;
+       int err;
+
+       if (host->retune_now)
+               host->retune_now = 0;
+       else
+               return 0;
+
+       if (!host->need_retune || host->doing_retune || !host->card)
+               return 0;
+
+       host->need_retune = 0;
+
+       host->doing_retune = 1;
+
+       if (host->ios.timing == MMC_TIMING_MMC_HS400) {
+               err = mmc_hs400_to_hs200(host->card);
+               if (err)
+                       goto out;
+
+               return_to_hs400 = true;
+
+               if (host->ops->prepare_hs400_tuning)
+                       host->ops->prepare_hs400_tuning(host, &host->ios);
+       }
+
+       err = mmc_execute_tuning(host->card);
+       if (err)
+               goto out;
+
+       if (return_to_hs400)
+               err = mmc_hs200_to_hs400(host->card);
+out:
+       host->doing_retune = 0;
+
+       return err;
+}
+
+static void mmc_retune_timer(unsigned long data)
+{
+       struct mmc_host *host = (struct mmc_host *)data;
+
+       mmc_retune_needed(host);
+}
+
 /**
  *     mmc_of_parse() - parse host's device-tree node
  *     @host: host whose node should be parsed.
@@ -400,6 +484,9 @@ int mmc_of_parse(struct mmc_host *host)
        else if (ret != -ENOENT)
                return ret;
 
+       if (of_property_read_bool(np, "disable-wp"))
+               host->caps2 |= MMC_CAP2_NO_WRITE_PROTECT;
+
        /* See the comment on CD inversion above */
        if (ro_cap_invert ^ ro_gpio_invert)
                host->caps2 |= MMC_CAP2_RO_ACTIVE_HIGH;
@@ -504,6 +591,7 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
 #ifdef CONFIG_PM
        host->pm_notify.notifier_call = mmc_pm_notify;
 #endif
+       setup_timer(&host->retune_timer, mmc_retune_timer, (unsigned long)host);
 
        /*
         * By default, hosts do not support SGIO or large requests.
index f2ab9e5781265c29e1e62c6eaafb4618019532b9..992bf53976337f0edf3f64fe4f9a007f4693b3a6 100644 (file)
 int mmc_register_host_class(void);
 void mmc_unregister_host_class(void);
 
+void mmc_retune_enable(struct mmc_host *host);
+void mmc_retune_disable(struct mmc_host *host);
+void mmc_retune_hold(struct mmc_host *host);
+void mmc_retune_release(struct mmc_host *host);
+int mmc_retune(struct mmc_host *host);
+
 #endif
 
index f36c76f8b2321e11d7d142c90b9400cb8b950d79..e726903170a828cffd69a1cb2e7f80d59c0d5152 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/mmc/mmc.h>
 
 #include "core.h"
+#include "host.h"
 #include "bus.h"
 #include "mmc_ops.h"
 #include "sd_ops.h"
@@ -266,8 +267,10 @@ static void mmc_manage_enhanced_area(struct mmc_card *card, u8 *ext_csd)
                         * calculate the enhanced data area offset, in bytes
                         */
                        card->ext_csd.enhanced_area_offset =
-                               (ext_csd[139] << 24) + (ext_csd[138] << 16) +
-                               (ext_csd[137] << 8) + ext_csd[136];
+                               (((unsigned long long)ext_csd[139]) << 24) +
+                               (((unsigned long long)ext_csd[138]) << 16) +
+                               (((unsigned long long)ext_csd[137]) << 8) +
+                               (((unsigned long long)ext_csd[136]));
                        if (mmc_card_blockaddr(card))
                                card->ext_csd.enhanced_area_offset <<= 9;
                        /*
@@ -434,6 +437,7 @@ static int mmc_decode_ext_csd(struct mmc_card *card, u8 *ext_csd)
        card->ext_csd.raw_trim_mult =
                ext_csd[EXT_CSD_TRIM_MULT];
        card->ext_csd.raw_partition_support = ext_csd[EXT_CSD_PARTITION_SUPPORT];
+       card->ext_csd.raw_driver_strength = ext_csd[EXT_CSD_DRIVER_STRENGTH];
        if (card->ext_csd.rev >= 4) {
                if (ext_csd[EXT_CSD_PARTITION_SETTING_COMPLETED] &
                    EXT_CSD_PART_SETTING_COMPLETED)
@@ -1040,6 +1044,7 @@ static int mmc_select_hs400(struct mmc_card *card)
 {
        struct mmc_host *host = card->host;
        int err = 0;
+       u8 val;
 
        /*
         * HS400 mode requires 8-bit bus width
@@ -1055,8 +1060,10 @@ static int mmc_select_hs400(struct mmc_card *card)
        mmc_set_timing(card->host, MMC_TIMING_MMC_HS);
        mmc_set_bus_speed(card);
 
+       val = EXT_CSD_TIMING_HS |
+             card->drive_strength << EXT_CSD_DRV_STR_SHIFT;
        err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
-                          EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS,
+                          EXT_CSD_HS_TIMING, val,
                           card->ext_csd.generic_cmd6_time,
                           true, true, true);
        if (err) {
@@ -1075,8 +1082,10 @@ static int mmc_select_hs400(struct mmc_card *card)
                return err;
        }
 
+       val = EXT_CSD_TIMING_HS400 |
+             card->drive_strength << EXT_CSD_DRV_STR_SHIFT;
        err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
-                          EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS400,
+                          EXT_CSD_HS_TIMING, val,
                           card->ext_csd.generic_cmd6_time,
                           true, true, true);
        if (err) {
@@ -1091,6 +1100,115 @@ static int mmc_select_hs400(struct mmc_card *card)
        return 0;
 }
 
+int mmc_hs200_to_hs400(struct mmc_card *card)
+{
+       return mmc_select_hs400(card);
+}
+
+/* Caller must hold re-tuning */
+static int mmc_switch_status(struct mmc_card *card)
+{
+       u32 status;
+       int err;
+
+       err = mmc_send_status(card, &status);
+       if (err)
+               return err;
+
+       return mmc_switch_status_error(card->host, status);
+}
+
+int mmc_hs400_to_hs200(struct mmc_card *card)
+{
+       struct mmc_host *host = card->host;
+       bool send_status = true;
+       unsigned int max_dtr;
+       int err;
+       u8 val;
+
+       if (host->caps & MMC_CAP_WAIT_WHILE_BUSY)
+               send_status = false;
+
+       /* Reduce frequency to HS */
+       max_dtr = card->ext_csd.hs_max_dtr;
+       mmc_set_clock(host, max_dtr);
+
+       /* Switch HS400 to HS DDR */
+       val = EXT_CSD_TIMING_HS |
+             card->drive_strength << EXT_CSD_DRV_STR_SHIFT;
+       err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING,
+                          val, card->ext_csd.generic_cmd6_time,
+                          true, send_status, true);
+       if (err)
+               goto out_err;
+
+       mmc_set_timing(host, MMC_TIMING_MMC_DDR52);
+
+       if (!send_status) {
+               err = mmc_switch_status(card);
+               if (err)
+                       goto out_err;
+       }
+
+       /* Switch HS DDR to HS */
+       err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BUS_WIDTH,
+                          EXT_CSD_BUS_WIDTH_8, card->ext_csd.generic_cmd6_time,
+                          true, send_status, true);
+       if (err)
+               goto out_err;
+
+       mmc_set_timing(host, MMC_TIMING_MMC_HS);
+
+       if (!send_status) {
+               err = mmc_switch_status(card);
+               if (err)
+                       goto out_err;
+       }
+
+       /* Switch HS to HS200 */
+       val = EXT_CSD_TIMING_HS200 |
+             card->drive_strength << EXT_CSD_DRV_STR_SHIFT;
+       err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING,
+                          val, card->ext_csd.generic_cmd6_time, true,
+                          send_status, true);
+       if (err)
+               goto out_err;
+
+       mmc_set_timing(host, MMC_TIMING_MMC_HS200);
+
+       if (!send_status) {
+               err = mmc_switch_status(card);
+               if (err)
+                       goto out_err;
+       }
+
+       mmc_set_bus_speed(card);
+
+       return 0;
+
+out_err:
+       pr_err("%s: %s failed, error %d\n", mmc_hostname(card->host),
+              __func__, err);
+       return err;
+}
+
+static void mmc_select_driver_type(struct mmc_card *card)
+{
+       int card_drv_type, drive_strength, drv_type;
+
+       card_drv_type = card->ext_csd.raw_driver_strength |
+                       mmc_driver_type_mask(0);
+
+       drive_strength = mmc_select_drive_strength(card,
+                                                  card->ext_csd.hs200_max_dtr,
+                                                  card_drv_type, &drv_type);
+
+       card->drive_strength = drive_strength;
+
+       if (drv_type)
+               mmc_set_driver_type(card->host, drv_type);
+}
+
 /*
  * For device supporting HS200 mode, the following sequence
  * should be done before executing the tuning process.
@@ -1102,6 +1220,7 @@ static int mmc_select_hs200(struct mmc_card *card)
 {
        struct mmc_host *host = card->host;
        int err = -EINVAL;
+       u8 val;
 
        if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS200_1_2V)
                err = __mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_120);
@@ -1113,14 +1232,18 @@ static int mmc_select_hs200(struct mmc_card *card)
        if (err)
                goto err;
 
+       mmc_select_driver_type(card);
+
        /*
         * Set the bus width(4 or 8) with host's support and
         * switch to HS200 mode if bus width is set successfully.
         */
        err = mmc_select_bus_width(card);
        if (!IS_ERR_VALUE(err)) {
+               val = EXT_CSD_TIMING_HS200 |
+                     card->drive_strength << EXT_CSD_DRV_STR_SHIFT;
                err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
-                                  EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS200,
+                                  EXT_CSD_HS_TIMING, val,
                                   card->ext_csd.generic_cmd6_time,
                                   true, true, true);
                if (!err)
@@ -1511,9 +1634,12 @@ static int mmc_sleep(struct mmc_host *host)
        unsigned int timeout_ms = DIV_ROUND_UP(card->ext_csd.sa_timeout, 10000);
        int err;
 
+       /* Re-tuning can't be done once the card is deselected */
+       mmc_retune_hold(host);
+
        err = mmc_deselect_cards(host);
        if (err)
-               return err;
+               goto out_release;
 
        cmd.opcode = MMC_SLEEP_AWAKE;
        cmd.arg = card->rca << 16;
@@ -1534,7 +1660,7 @@ static int mmc_sleep(struct mmc_host *host)
 
        err = mmc_wait_for_cmd(host, &cmd, 0);
        if (err)
-               return err;
+               goto out_release;
 
        /*
         * If the host does not wait while the card signals busy, then we will
@@ -1545,6 +1671,8 @@ static int mmc_sleep(struct mmc_host *host)
        if (!cmd.busy_timeout || !(host->caps & MMC_CAP_WAIT_WHILE_BUSY))
                mmc_delay(timeout_ms);
 
+out_release:
+       mmc_retune_release(host);
        return err;
 }
 
@@ -1782,17 +1910,6 @@ static int mmc_runtime_resume(struct mmc_host *host)
        return 0;
 }
 
-static int mmc_power_restore(struct mmc_host *host)
-{
-       int ret;
-
-       mmc_claim_host(host);
-       ret = mmc_init_card(host, host->card->ocr, host->card);
-       mmc_release_host(host);
-
-       return ret;
-}
-
 int mmc_can_reset(struct mmc_card *card)
 {
        u8 rst_n_function;
@@ -1830,7 +1947,7 @@ static int mmc_reset(struct mmc_host *host)
        mmc_set_initial_state(host);
        mmc_host_clk_release(host);
 
-       return mmc_power_restore(host);
+       return mmc_init_card(host, card->ocr, card);
 }
 
 static const struct mmc_bus_ops mmc_ops = {
@@ -1840,7 +1957,6 @@ static const struct mmc_bus_ops mmc_ops = {
        .resume = mmc_resume,
        .runtime_suspend = mmc_runtime_suspend,
        .runtime_resume = mmc_runtime_resume,
-       .power_restore = mmc_power_restore,
        .alive = mmc_alive,
        .shutdown = mmc_shutdown,
        .reset = mmc_reset,
index 0ea042dc74433c3808af4f5680ec46c070d68ba8..0e9ae1c276c800b82228e6c3676e6aacb9ad2755 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/mmc/mmc.h>
 
 #include "core.h"
+#include "host.h"
 #include "mmc_ops.h"
 
 #define MMC_OPS_TIMEOUT_MS     (10 * 60 * 1000) /* 10 minute timeout */
@@ -449,6 +450,21 @@ int mmc_spi_set_crc(struct mmc_host *host, int use_crc)
        return err;
 }
 
+int mmc_switch_status_error(struct mmc_host *host, u32 status)
+{
+       if (mmc_host_is_spi(host)) {
+               if (status & R1_SPI_ILLEGAL_COMMAND)
+                       return -EBADMSG;
+       } else {
+               if (status & 0xFDFFA000)
+                       pr_warn("%s: unexpected status %#x after switch\n",
+                               mmc_hostname(host), status);
+               if (status & R1_SWITCH_ERROR)
+                       return -EBADMSG;
+       }
+       return 0;
+}
+
 /**
  *     __mmc_switch - modify EXT_CSD register
  *     @card: the MMC card associated with the data transfer
@@ -474,6 +490,8 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
        u32 status = 0;
        bool use_r1b_resp = use_busy_signal;
 
+       mmc_retune_hold(host);
+
        /*
         * If the cmd timeout and the max_busy_timeout of the host are both
         * specified, let's validate them. A failure means we need to prevent
@@ -506,11 +524,11 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
 
        err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES);
        if (err)
-               return err;
+               goto out;
 
        /* No need to check card status in case of unblocking command */
        if (!use_busy_signal)
-               return 0;
+               goto out;
 
        /*
         * CRC errors shall only be ignored in cases were CMD13 is used to poll
@@ -529,7 +547,7 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
                if (send_status) {
                        err = __mmc_send_status(card, &status, ignore_crc);
                        if (err)
-                               return err;
+                               goto out;
                }
                if ((host->caps & MMC_CAP_WAIT_WHILE_BUSY) && use_r1b_resp)
                        break;
@@ -543,29 +561,23 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
                 */
                if (!send_status) {
                        mmc_delay(timeout_ms);
-                       return 0;
+                       goto out;
                }
 
                /* Timeout if the device never leaves the program state. */
                if (time_after(jiffies, timeout)) {
                        pr_err("%s: Card stuck in programming state! %s\n",
                                mmc_hostname(host), __func__);
-                       return -ETIMEDOUT;
+                       err = -ETIMEDOUT;
+                       goto out;
                }
        } while (R1_CURRENT_STATE(status) == R1_STATE_PRG);
 
-       if (mmc_host_is_spi(host)) {
-               if (status & R1_SPI_ILLEGAL_COMMAND)
-                       return -EBADMSG;
-       } else {
-               if (status & 0xFDFFA000)
-                       pr_warn("%s: unexpected status %#x after switch\n",
-                               mmc_hostname(host), status);
-               if (status & R1_SWITCH_ERROR)
-                       return -EBADMSG;
-       }
+       err = mmc_switch_status_error(host, status);
+out:
+       mmc_retune_release(host);
 
-       return 0;
+       return err;
 }
 EXPORT_SYMBOL_GPL(__mmc_switch);
 
index 6f4b00ed93de9e7176464ee35d1c8a5aec0c6525..f498f9ae21f09bd405d687981ae959f061a63d3e 100644 (file)
@@ -27,6 +27,7 @@ int mmc_spi_set_crc(struct mmc_host *host, int use_crc);
 int mmc_bus_test(struct mmc_card *card, u8 bus_width);
 int mmc_send_hpi_cmd(struct mmc_card *card, u32 *status);
 int mmc_can_ext_csd(struct mmc_card *card);
+int mmc_switch_status_error(struct mmc_host *host, u32 status);
 
 #endif
 
index 31a9ef256d0652d5b2ce64623d3a161bbcfcd6b7..4e7366ab187f295faab7674b72bf83fc8fb49ba4 100644 (file)
@@ -386,64 +386,31 @@ out:
 
 static int sd_select_driver_type(struct mmc_card *card, u8 *status)
 {
-       int host_drv_type = SD_DRIVER_TYPE_B;
-       int card_drv_type = SD_DRIVER_TYPE_B;
-       int drive_strength;
+       int card_drv_type, drive_strength, drv_type;
        int err;
 
-       /*
-        * If the host doesn't support any of the Driver Types A,C or D,
-        * or there is no board specific handler then default Driver
-        * Type B is used.
-        */
-       if (!(card->host->caps & (MMC_CAP_DRIVER_TYPE_A | MMC_CAP_DRIVER_TYPE_C
-           | MMC_CAP_DRIVER_TYPE_D)))
-               return 0;
-
-       if (!card->host->ops->select_drive_strength)
-               return 0;
-
-       if (card->host->caps & MMC_CAP_DRIVER_TYPE_A)
-               host_drv_type |= SD_DRIVER_TYPE_A;
-
-       if (card->host->caps & MMC_CAP_DRIVER_TYPE_C)
-               host_drv_type |= SD_DRIVER_TYPE_C;
-
-       if (card->host->caps & MMC_CAP_DRIVER_TYPE_D)
-               host_drv_type |= SD_DRIVER_TYPE_D;
-
-       if (card->sw_caps.sd3_drv_type & SD_DRIVER_TYPE_A)
-               card_drv_type |= SD_DRIVER_TYPE_A;
-
-       if (card->sw_caps.sd3_drv_type & SD_DRIVER_TYPE_C)
-               card_drv_type |= SD_DRIVER_TYPE_C;
+       card->drive_strength = 0;
 
-       if (card->sw_caps.sd3_drv_type & SD_DRIVER_TYPE_D)
-               card_drv_type |= SD_DRIVER_TYPE_D;
+       card_drv_type = card->sw_caps.sd3_drv_type | SD_DRIVER_TYPE_B;
 
-       /*
-        * The drive strength that the hardware can support
-        * depends on the board design.  Pass the appropriate
-        * information and let the hardware specific code
-        * return what is possible given the options
-        */
-       mmc_host_clk_hold(card->host);
-       drive_strength = card->host->ops->select_drive_strength(
-               card->sw_caps.uhs_max_dtr,
-               host_drv_type, card_drv_type);
-       mmc_host_clk_release(card->host);
-
-       err = mmc_sd_switch(card, 1, 2, drive_strength, status);
-       if (err)
-               return err;
+       drive_strength = mmc_select_drive_strength(card,
+                                                  card->sw_caps.uhs_max_dtr,
+                                                  card_drv_type, &drv_type);
 
-       if ((status[15] & 0xF) != drive_strength) {
-               pr_warn("%s: Problem setting drive strength!\n",
-                       mmc_hostname(card->host));
-               return 0;
+       if (drive_strength) {
+               err = mmc_sd_switch(card, 1, 2, drive_strength, status);
+               if (err)
+                       return err;
+               if ((status[15] & 0xF) != drive_strength) {
+                       pr_warn("%s: Problem setting drive strength!\n",
+                               mmc_hostname(card->host));
+                       return 0;
+               }
+               card->drive_strength = drive_strength;
        }
 
-       mmc_set_driver_type(card->host, drive_strength);
+       if (drv_type)
+               mmc_set_driver_type(card->host, drv_type);
 
        return 0;
 }
@@ -804,6 +771,28 @@ int mmc_sd_get_csd(struct mmc_host *host, struct mmc_card *card)
        return 0;
 }
 
+static int mmc_sd_get_ro(struct mmc_host *host)
+{
+       int ro;
+
+       /*
+        * Some systems don't feature a write-protect pin and don't need one.
+        * E.g. because they only have micro-SD card slot. For those systems
+        * assume that the SD card is always read-write.
+        */
+       if (host->caps2 & MMC_CAP2_NO_WRITE_PROTECT)
+               return 0;
+
+       if (!host->ops->get_ro)
+               return -1;
+
+       mmc_host_clk_hold(host);
+       ro = host->ops->get_ro(host);
+       mmc_host_clk_release(host);
+
+       return ro;
+}
+
 int mmc_sd_setup_card(struct mmc_host *host, struct mmc_card *card,
        bool reinit)
 {
@@ -855,13 +844,7 @@ int mmc_sd_setup_card(struct mmc_host *host, struct mmc_card *card,
         * Check if read-only switch is active.
         */
        if (!reinit) {
-               int ro = -1;
-
-               if (host->ops->get_ro) {
-                       mmc_host_clk_hold(card->host);
-                       ro = host->ops->get_ro(host);
-                       mmc_host_clk_release(card->host);
-               }
+               int ro = mmc_sd_get_ro(host);
 
                if (ro < 0) {
                        pr_warn("%s: host does not support reading read-only switch, assuming write-enable\n",
@@ -1181,21 +1164,10 @@ static int mmc_sd_runtime_resume(struct mmc_host *host)
        return 0;
 }
 
-static int mmc_sd_power_restore(struct mmc_host *host)
-{
-       int ret;
-
-       mmc_claim_host(host);
-       ret = mmc_sd_init_card(host, host->card->ocr, host->card);
-       mmc_release_host(host);
-
-       return ret;
-}
-
 static int mmc_sd_reset(struct mmc_host *host)
 {
        mmc_power_cycle(host, host->card->ocr);
-       return mmc_sd_power_restore(host);
+       return mmc_sd_init_card(host, host->card->ocr, host->card);
 }
 
 static const struct mmc_bus_ops mmc_sd_ops = {
@@ -1205,7 +1177,6 @@ static const struct mmc_bus_ops mmc_sd_ops = {
        .runtime_resume = mmc_sd_runtime_resume,
        .suspend = mmc_sd_suspend,
        .resume = mmc_sd_resume,
-       .power_restore = mmc_sd_power_restore,
        .alive = mmc_sd_alive,
        .shutdown = mmc_sd_suspend,
        .reset = mmc_sd_reset,
index 5bc6c7dbbd6088153b6ee2bda65730caccea0a8a..b91abedcfdca7054654a774f22a357d93661293a 100644 (file)
@@ -402,69 +402,38 @@ static unsigned char host_drive_to_sdio_drive(int host_strength)
 
 static void sdio_select_driver_type(struct mmc_card *card)
 {
-       int host_drv_type = SD_DRIVER_TYPE_B;
-       int card_drv_type = SD_DRIVER_TYPE_B;
-       int drive_strength;
+       int card_drv_type, drive_strength, drv_type;
        unsigned char card_strength;
        int err;
 
-       /*
-        * If the host doesn't support any of the Driver Types A,C or D,
-        * or there is no board specific handler then default Driver
-        * Type B is used.
-        */
-       if (!(card->host->caps &
-               (MMC_CAP_DRIVER_TYPE_A |
-                MMC_CAP_DRIVER_TYPE_C |
-                MMC_CAP_DRIVER_TYPE_D)))
-               return;
-
-       if (!card->host->ops->select_drive_strength)
-               return;
-
-       if (card->host->caps & MMC_CAP_DRIVER_TYPE_A)
-               host_drv_type |= SD_DRIVER_TYPE_A;
-
-       if (card->host->caps & MMC_CAP_DRIVER_TYPE_C)
-               host_drv_type |= SD_DRIVER_TYPE_C;
+       card->drive_strength = 0;
 
-       if (card->host->caps & MMC_CAP_DRIVER_TYPE_D)
-               host_drv_type |= SD_DRIVER_TYPE_D;
+       card_drv_type = card->sw_caps.sd3_drv_type | SD_DRIVER_TYPE_B;
 
-       if (card->sw_caps.sd3_drv_type & SD_DRIVER_TYPE_A)
-               card_drv_type |= SD_DRIVER_TYPE_A;
-
-       if (card->sw_caps.sd3_drv_type & SD_DRIVER_TYPE_C)
-               card_drv_type |= SD_DRIVER_TYPE_C;
-
-       if (card->sw_caps.sd3_drv_type & SD_DRIVER_TYPE_D)
-               card_drv_type |= SD_DRIVER_TYPE_D;
-
-       /*
-        * The drive strength that the hardware can support
-        * depends on the board design.  Pass the appropriate
-        * information and let the hardware specific code
-        * return what is possible given the options
-        */
-       drive_strength = card->host->ops->select_drive_strength(
-               card->sw_caps.uhs_max_dtr,
-               host_drv_type, card_drv_type);
+       drive_strength = mmc_select_drive_strength(card,
+                                                  card->sw_caps.uhs_max_dtr,
+                                                  card_drv_type, &drv_type);
 
-       /* if error just use default for drive strength B */
-       err = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_DRIVE_STRENGTH, 0,
-               &card_strength);
-       if (err)
-               return;
+       if (drive_strength) {
+               /* if error just use default for drive strength B */
+               err = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_DRIVE_STRENGTH, 0,
+                                      &card_strength);
+               if (err)
+                       return;
 
-       card_strength &= ~(SDIO_DRIVE_DTSx_MASK<<SDIO_DRIVE_DTSx_SHIFT);
-       card_strength |= host_drive_to_sdio_drive(drive_strength);
+               card_strength &= ~(SDIO_DRIVE_DTSx_MASK<<SDIO_DRIVE_DTSx_SHIFT);
+               card_strength |= host_drive_to_sdio_drive(drive_strength);
 
-       err = mmc_io_rw_direct(card, 1, 0, SDIO_CCCR_DRIVE_STRENGTH,
-               card_strength, NULL);
+               /* if error default to drive strength B */
+               err = mmc_io_rw_direct(card, 1, 0, SDIO_CCCR_DRIVE_STRENGTH,
+                                      card_strength, NULL);
+               if (err)
+                       return;
+               card->drive_strength = drive_strength;
+       }
 
-       /* if error default to drive strength B */
-       if (!err)
-               mmc_set_driver_type(card->host, drive_strength);
+       if (drv_type)
+               mmc_set_driver_type(card->host, drv_type);
 }
 
 
@@ -934,8 +903,12 @@ static int mmc_sdio_suspend(struct mmc_host *host)
                mmc_release_host(host);
        }
 
-       if (!mmc_card_keep_power(host))
+       if (!mmc_card_keep_power(host)) {
                mmc_power_off(host);
+       } else if (host->retune_period) {
+               mmc_retune_timer_stop(host);
+               mmc_retune_needed(host);
+       }
 
        return 0;
 }
@@ -1056,6 +1029,12 @@ static int mmc_sdio_runtime_resume(struct mmc_host *host)
        return mmc_sdio_power_restore(host);
 }
 
+static int mmc_sdio_reset(struct mmc_host *host)
+{
+       mmc_power_cycle(host, host->card->ocr);
+       return mmc_sdio_power_restore(host);
+}
+
 static const struct mmc_bus_ops mmc_sdio_ops = {
        .remove = mmc_sdio_remove,
        .detect = mmc_sdio_detect,
@@ -1066,6 +1045,7 @@ static const struct mmc_bus_ops mmc_sdio_ops = {
        .runtime_resume = mmc_sdio_runtime_resume,
        .power_restore = mmc_sdio_power_restore,
        .alive = mmc_sdio_alive,
+       .reset = mmc_sdio_reset,
 };
 
 
index bee02e644d620b956e8bd4a21a856f373393ac47..7e327a6dd53da304cc59b9012e311504bfaf67df 100644 (file)
@@ -137,6 +137,10 @@ static int sdio_bus_probe(struct device *dev)
        if (!id)
                return -ENODEV;
 
+       ret = dev_pm_domain_attach(dev, false);
+       if (ret == -EPROBE_DEFER)
+               return ret;
+
        /* Unbound SDIO functions are always suspended.
         * During probe, the function is set active and the usage count
         * is incremented.  If the driver supports runtime PM,
@@ -166,6 +170,7 @@ static int sdio_bus_probe(struct device *dev)
 disable_runtimepm:
        if (func->card->host->caps & MMC_CAP_POWER_OFF_CARD)
                pm_runtime_put_noidle(dev);
+       dev_pm_domain_detach(dev, false);
        return ret;
 }
 
@@ -197,6 +202,8 @@ static int sdio_bus_remove(struct device *dev)
        if (func->card->host->caps & MMC_CAP_POWER_OFF_CARD)
                pm_runtime_put_sync(dev);
 
+       dev_pm_domain_detach(dev, false);
+
        return ret;
 }
 
@@ -316,10 +323,8 @@ int sdio_add_func(struct sdio_func *func)
        sdio_set_of_node(func);
        sdio_acpi_set_handle(func);
        ret = device_add(&func->dev);
-       if (ret == 0) {
+       if (ret == 0)
                sdio_func_set_present(func);
-               dev_pm_domain_attach(&func->dev, false);
-       }
 
        return ret;
 }
@@ -335,7 +340,6 @@ void sdio_remove_func(struct sdio_func *func)
        if (!sdio_func_present(func))
                return;
 
-       dev_pm_domain_detach(&func->dev, false);
        device_del(&func->dev);
        of_node_put(func->dev.of_node);
        put_device(&func->dev);
index b1f837e749fe83d51fa4ec1410081ac44d97fb33..fd9a58e216a50ffde01e8e9fb7610ef09c417af3 100644 (file)
@@ -219,6 +219,7 @@ config MMC_SDHCI_SIRF
        tristate "SDHCI support on CSR SiRFprimaII and SiRFmarco SoCs"
        depends on ARCH_SIRF
        depends on MMC_SDHCI_PLTFM
+       select MMC_SDHCI_IO_ACCESSORS
        help
          This selects the SDHCI support for SiRF System-on-Chip devices.
 
@@ -775,3 +776,11 @@ config MMC_TOSHIBA_PCI
        tristate "Toshiba Type A SD/MMC Card Interface Driver"
        depends on PCI
        help
+
+config MMC_MTK
+       tristate "MediaTek SD/MMC Card Interface support"
+       help
+         This selects the MediaTek(R) Secure digital and Multimedia card Interface.
+         If you have a machine with a integrated SD/MMC card reader, say Y or M here.
+         This is needed if support for any SD/SDIO/MMC devices is required.
+         If unsure, say N.
index e3ab5b968651f53e6479b64dc7385261428e9bbf..e928d61c5f4be3d70bd4b6d37b7852fccfe70b5d 100644 (file)
@@ -20,6 +20,7 @@ obj-$(CONFIG_MMC_SDHCI_F_SDH30)       += sdhci_f_sdh30.o
 obj-$(CONFIG_MMC_SDHCI_SPEAR)  += sdhci-spear.o
 obj-$(CONFIG_MMC_WBSD)         += wbsd.o
 obj-$(CONFIG_MMC_AU1X)         += au1xmmc.o
+obj-$(CONFIG_MMC_MTK)          += mtk-sd.o
 obj-$(CONFIG_MMC_OMAP)         += omap.o
 obj-$(CONFIG_MMC_OMAP_HS)      += omap_hsmmc.o
 obj-$(CONFIG_MMC_ATMELMCI)     += atmel-mci.o
index 1625f908dc70bbab91658c49da3a0e47a4f516bd..ea2a2ebc6b91320d928439518218b36a966396a3 100644 (file)
@@ -1161,7 +1161,7 @@ static void __init init_mmcsd_host(struct mmc_davinci_host *host)
        mmc_davinci_reset_ctrl(host, 0);
 }
 
-static struct platform_device_id davinci_mmc_devtype[] = {
+static const struct platform_device_id davinci_mmc_devtype[] = {
        {
                .name   = "dm6441-mmc",
                .driver_data = MMC_CTLR_VERSION_1,
index e761eb1b1441339e33a84822ff863b20165e97cf..1e75309898b76b33f7f2d91a05425269fb941ffb 100644 (file)
@@ -556,4 +556,4 @@ module_platform_driver(dw_mci_exynos_pltfm_driver);
 MODULE_DESCRIPTION("Samsung Specific DW-MSHC Driver Extension");
 MODULE_AUTHOR("Thomas Abraham <thomas.ab@samsung.com");
 MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:dwmmc-exynos");
+MODULE_ALIAS("platform:dwmmc_exynos");
index 650f9cc3f7a6b4bddd98e9c755cdf043835791d8..63c2e2ed12886abc83e516861b331ea9c3871c28 100644 (file)
@@ -8,16 +8,30 @@
  * (at your option) any later version.
  */
 
-#include <linux/module.h>
-#include <linux/platform_device.h>
 #include <linux/clk.h>
+#include <linux/mfd/syscon.h>
 #include <linux/mmc/host.h>
 #include <linux/mmc/dw_mmc.h>
+#include <linux/module.h>
 #include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
 
 #include "dw_mmc.h"
 #include "dw_mmc-pltfm.h"
 
+/*
+ * hi6220 sd only support io voltage 1.8v and 3v
+ * Also need config AO_SCTRL_SEL18 accordingly
+ */
+#define AO_SCTRL_SEL18         BIT(10)
+#define AO_SCTRL_CTRL3         0x40C
+
+struct k3_priv {
+       struct regmap   *reg;
+};
+
 static void dw_mci_k3_set_ios(struct dw_mci *host, struct mmc_ios *ios)
 {
        int ret;
@@ -33,8 +47,93 @@ static const struct dw_mci_drv_data k3_drv_data = {
        .set_ios                = dw_mci_k3_set_ios,
 };
 
+static int dw_mci_hi6220_parse_dt(struct dw_mci *host)
+{
+       struct k3_priv *priv;
+
+       priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       priv->reg = syscon_regmap_lookup_by_phandle(host->dev->of_node,
+                                        "hisilicon,peripheral-syscon");
+       if (IS_ERR(priv->reg))
+               priv->reg = NULL;
+
+       host->priv = priv;
+       return 0;
+}
+
+static int dw_mci_hi6220_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+       struct dw_mci_slot *slot = mmc_priv(mmc);
+       struct k3_priv *priv;
+       struct dw_mci *host;
+       int min_uv, max_uv;
+       int ret;
+
+       host = slot->host;
+       priv = host->priv;
+
+       if (!priv || !priv->reg)
+               return 0;
+
+       if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_330) {
+               ret = regmap_update_bits(priv->reg, AO_SCTRL_CTRL3,
+                                        AO_SCTRL_SEL18, 0);
+               min_uv = 3000000;
+               max_uv = 3000000;
+       } else if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_180) {
+               ret = regmap_update_bits(priv->reg, AO_SCTRL_CTRL3,
+                                        AO_SCTRL_SEL18, AO_SCTRL_SEL18);
+               min_uv = 1800000;
+               max_uv = 1800000;
+       } else {
+               dev_dbg(host->dev, "voltage not supported\n");
+               return -EINVAL;
+       }
+
+       if (ret) {
+               dev_dbg(host->dev, "switch voltage failed\n");
+               return ret;
+       }
+
+       if (IS_ERR_OR_NULL(mmc->supply.vqmmc))
+               return 0;
+
+       ret = regulator_set_voltage(mmc->supply.vqmmc, min_uv, max_uv);
+       if (ret) {
+               dev_dbg(host->dev, "Regulator set error %d: %d - %d\n",
+                                ret, min_uv, max_uv);
+               return ret;
+       }
+
+       return 0;
+}
+
+static void dw_mci_hi6220_set_ios(struct dw_mci *host, struct mmc_ios *ios)
+{
+       int ret;
+       unsigned int clock;
+
+       clock = (ios->clock <= 25000000) ? 25000000 : ios->clock;
+
+       ret = clk_set_rate(host->biu_clk, clock);
+       if (ret)
+               dev_warn(host->dev, "failed to set rate %uHz\n", clock);
+
+       host->bus_hz = clk_get_rate(host->biu_clk);
+}
+
+static const struct dw_mci_drv_data hi6220_data = {
+       .switch_voltage         = dw_mci_hi6220_switch_voltage,
+       .set_ios                = dw_mci_hi6220_set_ios,
+       .parse_dt               = dw_mci_hi6220_parse_dt,
+};
+
 static const struct of_device_id dw_mci_k3_match[] = {
        { .compatible = "hisilicon,hi4511-dw-mshc", .data = &k3_drv_data, },
+       { .compatible = "hisilicon,hi6220-dw-mshc", .data = &hi6220_data, },
        {},
 };
 MODULE_DEVICE_TABLE(of, dw_mci_k3_match);
@@ -94,4 +193,4 @@ module_platform_driver(dw_mci_k3_pltfm_driver);
 
 MODULE_DESCRIPTION("K3 Specific DW-MSHC Driver Extension");
 MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:dwmmc-k3");
+MODULE_ALIAS("platform:dwmmc_k3");
index dbf166f94f1b6a1457238e29b435f83171509e26..de15121bba7dffe07725451890100d57abba8b25 100644 (file)
@@ -153,5 +153,5 @@ module_platform_driver(dw_mci_rockchip_pltfm_driver);
 
 MODULE_AUTHOR("Addy Ke <addy.ke@rock-chips.com>");
 MODULE_DESCRIPTION("Rockchip Specific DW-MSHC Driver Extension");
-MODULE_ALIAS("platform:dwmmc-rockchip");
+MODULE_ALIAS("platform:dwmmc_rockchip");
 MODULE_LICENSE("GPL v2");
index 5f5adafb253afec16429c3bb357e15be8d07bbb7..40e9d8e45f25c64f1cb421807ced3af1c9ad1ca7 100644 (file)
@@ -1236,11 +1236,15 @@ static int dw_mci_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios)
 {
        struct dw_mci_slot *slot = mmc_priv(mmc);
        struct dw_mci *host = slot->host;
+       const struct dw_mci_drv_data *drv_data = host->drv_data;
        u32 uhs;
        u32 v18 = SDMMC_UHS_18V << slot->id;
        int min_uv, max_uv;
        int ret;
 
+       if (drv_data && drv_data->switch_voltage)
+               return drv_data->switch_voltage(mmc, ios);
+
        /*
         * Program the voltage.  Note that some instances of dw_mmc may use
         * the UHS_REG for this.  For other instances (like exynos) the UHS_REG
@@ -1278,10 +1282,7 @@ static int dw_mci_get_ro(struct mmc_host *mmc)
        int gpio_ro = mmc_gpio_get_ro(mmc);
 
        /* Use platform get_ro function, else try on board write protect */
-       if ((slot->quirks & DW_MCI_SLOT_QUIRK_NO_WRITE_PROTECT) ||
-                       (slot->host->quirks & DW_MCI_QUIRK_NO_WRITE_PROTECT))
-               read_only = 0;
-       else if (!IS_ERR_VALUE(gpio_ro))
+       if (!IS_ERR_VALUE(gpio_ro))
                read_only = gpio_ro;
        else
                read_only =
@@ -2280,9 +2281,10 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
 }
 
 #ifdef CONFIG_OF
-/* given a slot id, find out the device node representing that slot */
-static struct device_node *dw_mci_of_find_slot_node(struct device *dev, u8 slot)
+/* given a slot, find out the device node representing that slot */
+static struct device_node *dw_mci_of_find_slot_node(struct dw_mci_slot *slot)
 {
+       struct device *dev = slot->mmc->parent;
        struct device_node *np;
        const __be32 *addr;
        int len;
@@ -2294,42 +2296,28 @@ static struct device_node *dw_mci_of_find_slot_node(struct device *dev, u8 slot)
                addr = of_get_property(np, "reg", &len);
                if (!addr || (len < sizeof(int)))
                        continue;
-               if (be32_to_cpup(addr) == slot)
+               if (be32_to_cpup(addr) == slot->id)
                        return np;
        }
        return NULL;
 }
 
-static struct dw_mci_of_slot_quirks {
-       char *quirk;
-       int id;
-} of_slot_quirks[] = {
-       {
-               .quirk  = "disable-wp",
-               .id     = DW_MCI_SLOT_QUIRK_NO_WRITE_PROTECT,
-       },
-};
-
-static int dw_mci_of_get_slot_quirks(struct device *dev, u8 slot)
+static void dw_mci_slot_of_parse(struct dw_mci_slot *slot)
 {
-       struct device_node *np = dw_mci_of_find_slot_node(dev, slot);
-       int quirks = 0;
-       int idx;
+       struct device_node *np = dw_mci_of_find_slot_node(slot);
 
-       /* get quirks */
-       for (idx = 0; idx < ARRAY_SIZE(of_slot_quirks); idx++)
-               if (of_get_property(np, of_slot_quirks[idx].quirk, NULL)) {
-                       dev_warn(dev, "Slot quirk %s is deprecated\n",
-                                       of_slot_quirks[idx].quirk);
-                       quirks |= of_slot_quirks[idx].id;
-               }
+       if (!np)
+               return;
 
-       return quirks;
+       if (of_property_read_bool(np, "disable-wp")) {
+               slot->mmc->caps2 |= MMC_CAP2_NO_WRITE_PROTECT;
+               dev_warn(slot->mmc->parent,
+                       "Slot quirk 'disable-wp' is deprecated\n");
+       }
 }
 #else /* CONFIG_OF */
-static int dw_mci_of_get_slot_quirks(struct device *dev, u8 slot)
+static void dw_mci_slot_of_parse(struct dw_mci_slot *slot)
 {
-       return 0;
 }
 #endif /* CONFIG_OF */
 
@@ -2352,8 +2340,6 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
        slot->host = host;
        host->slot[id] = slot;
 
-       slot->quirks = dw_mci_of_get_slot_quirks(host->dev, slot->id);
-
        mmc->ops = &dw_mci_ops;
        if (of_property_read_u32_array(host->dev->of_node,
                                       "clock-freq-min-max", freq, 2)) {
@@ -2391,6 +2377,8 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
        if (host->pdata->caps2)
                mmc->caps2 = host->pdata->caps2;
 
+       dw_mci_slot_of_parse(slot);
+
        ret = mmc_of_parse(mmc);
        if (ret)
                goto err_host_allocated;
@@ -2618,9 +2606,6 @@ static struct dw_mci_of_quirks {
        {
                .quirk  = "broken-cd",
                .id     = DW_MCI_QUIRK_BROKEN_CARD_DETECTION,
-       }, {
-               .quirk  = "disable-wp",
-               .id     = DW_MCI_QUIRK_NO_WRITE_PROTECT,
        },
 };
 
@@ -2941,15 +2926,15 @@ void dw_mci_remove(struct dw_mci *host)
 {
        int i;
 
-       mci_writel(host, RINTSTS, 0xFFFFFFFF);
-       mci_writel(host, INTMASK, 0); /* disable all mmc interrupt first */
-
        for (i = 0; i < host->num_slots; i++) {
                dev_dbg(host->dev, "remove slot %d\n", i);
                if (host->slot[i])
                        dw_mci_cleanup_slot(host->slot[i], i);
        }
 
+       mci_writel(host, RINTSTS, 0xFFFFFFFF);
+       mci_writel(host, INTMASK, 0); /* disable all mmc interrupt first */
+
        /* disable clock to CIU */
        mci_writel(host, CLKENA, 0);
        mci_writel(host, CLKSRC, 0);
index f45ab91de33946ac86abc72ae8305eb1f18aa55e..8ce4674730a6ff8efde66e456136aa0d282e06c5 100644 (file)
@@ -227,7 +227,6 @@ extern int dw_mci_resume(struct dw_mci *host);
  * struct dw_mci_slot - MMC slot state
  * @mmc: The mmc_host representing this slot.
  * @host: The MMC controller this slot is using.
- * @quirks: Slot-level quirks (DW_MCI_SLOT_QUIRK_XXX)
  * @ctype: Card type for this slot.
  * @mrq: mmc_request currently being processed or waiting to be
  *     processed, or NULL when the slot is idle.
@@ -245,8 +244,6 @@ struct dw_mci_slot {
        struct mmc_host         *mmc;
        struct dw_mci           *host;
 
-       int                     quirks;
-
        u32                     ctype;
 
        struct mmc_request      *mrq;
@@ -287,5 +284,7 @@ struct dw_mci_drv_data {
        int             (*execute_tuning)(struct dw_mci_slot *slot);
        int             (*prepare_hs400_tuning)(struct dw_mci *host,
                                                struct mmc_ios *ios);
+       int             (*switch_voltage)(struct mmc_host *mmc,
+                                         struct mmc_ios *ios);
 };
 #endif /* _DW_MMC_H_ */
diff --git a/drivers/mmc/host/mtk-sd.c b/drivers/mmc/host/mtk-sd.c
new file mode 100644 (file)
index 0000000..7153500
--- /dev/null
@@ -0,0 +1,1462 @@
+/*
+ * Copyright (c) 2014-2015 MediaTek Inc.
+ * Author: Chaotian.Jing <chaotian.jing@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
+ * 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/clk.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/ioport.h>
+#include <linux/irq.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_gpio.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spinlock.h>
+
+#include <linux/mmc/card.h>
+#include <linux/mmc/core.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/mmc.h>
+#include <linux/mmc/sd.h>
+#include <linux/mmc/sdio.h>
+
+#define MAX_BD_NUM          1024
+
+/*--------------------------------------------------------------------------*/
+/* Common Definition                                                        */
+/*--------------------------------------------------------------------------*/
+#define MSDC_BUS_1BITS          0x0
+#define MSDC_BUS_4BITS          0x1
+#define MSDC_BUS_8BITS          0x2
+
+#define MSDC_BURST_64B          0x6
+
+/*--------------------------------------------------------------------------*/
+/* Register Offset                                                          */
+/*--------------------------------------------------------------------------*/
+#define MSDC_CFG         0x0
+#define MSDC_IOCON       0x04
+#define MSDC_PS          0x08
+#define MSDC_INT         0x0c
+#define MSDC_INTEN       0x10
+#define MSDC_FIFOCS      0x14
+#define SDC_CFG          0x30
+#define SDC_CMD          0x34
+#define SDC_ARG          0x38
+#define SDC_STS          0x3c
+#define SDC_RESP0        0x40
+#define SDC_RESP1        0x44
+#define SDC_RESP2        0x48
+#define SDC_RESP3        0x4c
+#define SDC_BLK_NUM      0x50
+#define SDC_ACMD_RESP    0x80
+#define MSDC_DMA_SA      0x90
+#define MSDC_DMA_CTRL    0x98
+#define MSDC_DMA_CFG     0x9c
+#define MSDC_PATCH_BIT   0xb0
+#define MSDC_PATCH_BIT1  0xb4
+#define MSDC_PAD_TUNE    0xec
+
+/*--------------------------------------------------------------------------*/
+/* Register Mask                                                            */
+/*--------------------------------------------------------------------------*/
+
+/* MSDC_CFG mask */
+#define MSDC_CFG_MODE           (0x1 << 0)     /* RW */
+#define MSDC_CFG_CKPDN          (0x1 << 1)     /* RW */
+#define MSDC_CFG_RST            (0x1 << 2)     /* RW */
+#define MSDC_CFG_PIO            (0x1 << 3)     /* RW */
+#define MSDC_CFG_CKDRVEN        (0x1 << 4)     /* RW */
+#define MSDC_CFG_BV18SDT        (0x1 << 5)     /* RW */
+#define MSDC_CFG_BV18PSS        (0x1 << 6)     /* R  */
+#define MSDC_CFG_CKSTB          (0x1 << 7)     /* R  */
+#define MSDC_CFG_CKDIV          (0xff << 8)    /* RW */
+#define MSDC_CFG_CKMOD          (0x3 << 16)    /* RW */
+
+/* MSDC_IOCON mask */
+#define MSDC_IOCON_SDR104CKS    (0x1 << 0)     /* RW */
+#define MSDC_IOCON_RSPL         (0x1 << 1)     /* RW */
+#define MSDC_IOCON_DSPL         (0x1 << 2)     /* RW */
+#define MSDC_IOCON_DDLSEL       (0x1 << 3)     /* RW */
+#define MSDC_IOCON_DDR50CKD     (0x1 << 4)     /* RW */
+#define MSDC_IOCON_DSPLSEL      (0x1 << 5)     /* RW */
+#define MSDC_IOCON_W_DSPL       (0x1 << 8)     /* RW */
+#define MSDC_IOCON_D0SPL        (0x1 << 16)    /* RW */
+#define MSDC_IOCON_D1SPL        (0x1 << 17)    /* RW */
+#define MSDC_IOCON_D2SPL        (0x1 << 18)    /* RW */
+#define MSDC_IOCON_D3SPL        (0x1 << 19)    /* RW */
+#define MSDC_IOCON_D4SPL        (0x1 << 20)    /* RW */
+#define MSDC_IOCON_D5SPL        (0x1 << 21)    /* RW */
+#define MSDC_IOCON_D6SPL        (0x1 << 22)    /* RW */
+#define MSDC_IOCON_D7SPL        (0x1 << 23)    /* RW */
+#define MSDC_IOCON_RISCSZ       (0x3 << 24)    /* RW */
+
+/* MSDC_PS mask */
+#define MSDC_PS_CDEN            (0x1 << 0)     /* RW */
+#define MSDC_PS_CDSTS           (0x1 << 1)     /* R  */
+#define MSDC_PS_CDDEBOUNCE      (0xf << 12)    /* RW */
+#define MSDC_PS_DAT             (0xff << 16)   /* R  */
+#define MSDC_PS_CMD             (0x1 << 24)    /* R  */
+#define MSDC_PS_WP              (0x1 << 31)    /* R  */
+
+/* MSDC_INT mask */
+#define MSDC_INT_MMCIRQ         (0x1 << 0)     /* W1C */
+#define MSDC_INT_CDSC           (0x1 << 1)     /* W1C */
+#define MSDC_INT_ACMDRDY        (0x1 << 3)     /* W1C */
+#define MSDC_INT_ACMDTMO        (0x1 << 4)     /* W1C */
+#define MSDC_INT_ACMDCRCERR     (0x1 << 5)     /* W1C */
+#define MSDC_INT_DMAQ_EMPTY     (0x1 << 6)     /* W1C */
+#define MSDC_INT_SDIOIRQ        (0x1 << 7)     /* W1C */
+#define MSDC_INT_CMDRDY         (0x1 << 8)     /* W1C */
+#define MSDC_INT_CMDTMO         (0x1 << 9)     /* W1C */
+#define MSDC_INT_RSPCRCERR      (0x1 << 10)    /* W1C */
+#define MSDC_INT_CSTA           (0x1 << 11)    /* R */
+#define MSDC_INT_XFER_COMPL     (0x1 << 12)    /* W1C */
+#define MSDC_INT_DXFER_DONE     (0x1 << 13)    /* W1C */
+#define MSDC_INT_DATTMO         (0x1 << 14)    /* W1C */
+#define MSDC_INT_DATCRCERR      (0x1 << 15)    /* W1C */
+#define MSDC_INT_ACMD19_DONE    (0x1 << 16)    /* W1C */
+#define MSDC_INT_DMA_BDCSERR    (0x1 << 17)    /* W1C */
+#define MSDC_INT_DMA_GPDCSERR   (0x1 << 18)    /* W1C */
+#define MSDC_INT_DMA_PROTECT    (0x1 << 19)    /* W1C */
+
+/* MSDC_INTEN mask */
+#define MSDC_INTEN_MMCIRQ       (0x1 << 0)     /* RW */
+#define MSDC_INTEN_CDSC         (0x1 << 1)     /* RW */
+#define MSDC_INTEN_ACMDRDY      (0x1 << 3)     /* RW */
+#define MSDC_INTEN_ACMDTMO      (0x1 << 4)     /* RW */
+#define MSDC_INTEN_ACMDCRCERR   (0x1 << 5)     /* RW */
+#define MSDC_INTEN_DMAQ_EMPTY   (0x1 << 6)     /* RW */
+#define MSDC_INTEN_SDIOIRQ      (0x1 << 7)     /* RW */
+#define MSDC_INTEN_CMDRDY       (0x1 << 8)     /* RW */
+#define MSDC_INTEN_CMDTMO       (0x1 << 9)     /* RW */
+#define MSDC_INTEN_RSPCRCERR    (0x1 << 10)    /* RW */
+#define MSDC_INTEN_CSTA         (0x1 << 11)    /* RW */
+#define MSDC_INTEN_XFER_COMPL   (0x1 << 12)    /* RW */
+#define MSDC_INTEN_DXFER_DONE   (0x1 << 13)    /* RW */
+#define MSDC_INTEN_DATTMO       (0x1 << 14)    /* RW */
+#define MSDC_INTEN_DATCRCERR    (0x1 << 15)    /* RW */
+#define MSDC_INTEN_ACMD19_DONE  (0x1 << 16)    /* RW */
+#define MSDC_INTEN_DMA_BDCSERR  (0x1 << 17)    /* RW */
+#define MSDC_INTEN_DMA_GPDCSERR (0x1 << 18)    /* RW */
+#define MSDC_INTEN_DMA_PROTECT  (0x1 << 19)    /* RW */
+
+/* MSDC_FIFOCS mask */
+#define MSDC_FIFOCS_RXCNT       (0xff << 0)    /* R */
+#define MSDC_FIFOCS_TXCNT       (0xff << 16)   /* R */
+#define MSDC_FIFOCS_CLR         (0x1 << 31)    /* RW */
+
+/* SDC_CFG mask */
+#define SDC_CFG_SDIOINTWKUP     (0x1 << 0)     /* RW */
+#define SDC_CFG_INSWKUP         (0x1 << 1)     /* RW */
+#define SDC_CFG_BUSWIDTH        (0x3 << 16)    /* RW */
+#define SDC_CFG_SDIO            (0x1 << 19)    /* RW */
+#define SDC_CFG_SDIOIDE         (0x1 << 20)    /* RW */
+#define SDC_CFG_INTATGAP        (0x1 << 21)    /* RW */
+#define SDC_CFG_DTOC            (0xff << 24)   /* RW */
+
+/* SDC_STS mask */
+#define SDC_STS_SDCBUSY         (0x1 << 0)     /* RW */
+#define SDC_STS_CMDBUSY         (0x1 << 1)     /* RW */
+#define SDC_STS_SWR_COMPL       (0x1 << 31)    /* RW */
+
+/* MSDC_DMA_CTRL mask */
+#define MSDC_DMA_CTRL_START     (0x1 << 0)     /* W */
+#define MSDC_DMA_CTRL_STOP      (0x1 << 1)     /* W */
+#define MSDC_DMA_CTRL_RESUME    (0x1 << 2)     /* W */
+#define MSDC_DMA_CTRL_MODE      (0x1 << 8)     /* RW */
+#define MSDC_DMA_CTRL_LASTBUF   (0x1 << 10)    /* RW */
+#define MSDC_DMA_CTRL_BRUSTSZ   (0x7 << 12)    /* RW */
+
+/* MSDC_DMA_CFG mask */
+#define MSDC_DMA_CFG_STS        (0x1 << 0)     /* R */
+#define MSDC_DMA_CFG_DECSEN     (0x1 << 1)     /* RW */
+#define MSDC_DMA_CFG_AHBHPROT2  (0x2 << 8)     /* RW */
+#define MSDC_DMA_CFG_ACTIVEEN   (0x2 << 12)    /* RW */
+#define MSDC_DMA_CFG_CS12B16B   (0x1 << 16)    /* RW */
+
+/* MSDC_PATCH_BIT mask */
+#define MSDC_PATCH_BIT_ODDSUPP    (0x1 <<  1)  /* RW */
+#define MSDC_INT_DAT_LATCH_CK_SEL (0x7 <<  7)
+#define MSDC_CKGEN_MSDC_DLY_SEL   (0x1f << 10)
+#define MSDC_PATCH_BIT_IODSSEL    (0x1 << 16)  /* RW */
+#define MSDC_PATCH_BIT_IOINTSEL   (0x1 << 17)  /* RW */
+#define MSDC_PATCH_BIT_BUSYDLY    (0xf << 18)  /* RW */
+#define MSDC_PATCH_BIT_WDOD       (0xf << 22)  /* RW */
+#define MSDC_PATCH_BIT_IDRTSEL    (0x1 << 26)  /* RW */
+#define MSDC_PATCH_BIT_CMDFSEL    (0x1 << 27)  /* RW */
+#define MSDC_PATCH_BIT_INTDLSEL   (0x1 << 28)  /* RW */
+#define MSDC_PATCH_BIT_SPCPUSH    (0x1 << 29)  /* RW */
+#define MSDC_PATCH_BIT_DECRCTMO   (0x1 << 30)  /* RW */
+
+#define REQ_CMD_EIO  (0x1 << 0)
+#define REQ_CMD_TMO  (0x1 << 1)
+#define REQ_DAT_ERR  (0x1 << 2)
+#define REQ_STOP_EIO (0x1 << 3)
+#define REQ_STOP_TMO (0x1 << 4)
+#define REQ_CMD_BUSY (0x1 << 5)
+
+#define MSDC_PREPARE_FLAG (0x1 << 0)
+#define MSDC_ASYNC_FLAG (0x1 << 1)
+#define MSDC_MMAP_FLAG (0x1 << 2)
+
+#define MTK_MMC_AUTOSUSPEND_DELAY      50
+#define CMD_TIMEOUT         (HZ/10 * 5)        /* 100ms x5 */
+#define DAT_TIMEOUT         (HZ    * 5)        /* 1000ms x5 */
+
+/*--------------------------------------------------------------------------*/
+/* Descriptor Structure                                                     */
+/*--------------------------------------------------------------------------*/
+struct mt_gpdma_desc {
+       u32 gpd_info;
+#define GPDMA_DESC_HWO         (0x1 << 0)
+#define GPDMA_DESC_BDP         (0x1 << 1)
+#define GPDMA_DESC_CHECKSUM    (0xff << 8) /* bit8 ~ bit15 */
+#define GPDMA_DESC_INT         (0x1 << 16)
+       u32 next;
+       u32 ptr;
+       u32 gpd_data_len;
+#define GPDMA_DESC_BUFLEN      (0xffff) /* bit0 ~ bit15 */
+#define GPDMA_DESC_EXTLEN      (0xff << 16) /* bit16 ~ bit23 */
+       u32 arg;
+       u32 blknum;
+       u32 cmd;
+};
+
+struct mt_bdma_desc {
+       u32 bd_info;
+#define BDMA_DESC_EOL          (0x1 << 0)
+#define BDMA_DESC_CHECKSUM     (0xff << 8) /* bit8 ~ bit15 */
+#define BDMA_DESC_BLKPAD       (0x1 << 17)
+#define BDMA_DESC_DWPAD                (0x1 << 18)
+       u32 next;
+       u32 ptr;
+       u32 bd_data_len;
+#define BDMA_DESC_BUFLEN       (0xffff) /* bit0 ~ bit15 */
+};
+
+struct msdc_dma {
+       struct scatterlist *sg; /* I/O scatter list */
+       struct mt_gpdma_desc *gpd;              /* pointer to gpd array */
+       struct mt_bdma_desc *bd;                /* pointer to bd array */
+       dma_addr_t gpd_addr;    /* the physical address of gpd array */
+       dma_addr_t bd_addr;     /* the physical address of bd array */
+};
+
+struct msdc_save_para {
+       u32 msdc_cfg;
+       u32 iocon;
+       u32 sdc_cfg;
+       u32 pad_tune;
+       u32 patch_bit0;
+       u32 patch_bit1;
+};
+
+struct msdc_host {
+       struct device *dev;
+       struct mmc_host *mmc;   /* mmc structure */
+       int cmd_rsp;
+
+       spinlock_t lock;
+       struct mmc_request *mrq;
+       struct mmc_command *cmd;
+       struct mmc_data *data;
+       int error;
+
+       void __iomem *base;             /* host base address */
+
+       struct msdc_dma dma;    /* dma channel */
+       u64 dma_mask;
+
+       u32 timeout_ns;         /* data timeout ns */
+       u32 timeout_clks;       /* data timeout clks */
+
+       struct pinctrl *pinctrl;
+       struct pinctrl_state *pins_default;
+       struct pinctrl_state *pins_uhs;
+       struct delayed_work req_timeout;
+       int irq;                /* host interrupt */
+
+       struct clk *src_clk;    /* msdc source clock */
+       struct clk *h_clk;      /* msdc h_clk */
+       u32 mclk;               /* mmc subsystem clock frequency */
+       u32 src_clk_freq;       /* source clock frequency */
+       u32 sclk;               /* SD/MS bus clock frequency */
+       bool ddr;
+       bool vqmmc_enabled;
+       struct msdc_save_para save_para; /* used when gate HCLK */
+};
+
+static void sdr_set_bits(void __iomem *reg, u32 bs)
+{
+       u32 val = readl(reg);
+
+       val |= bs;
+       writel(val, reg);
+}
+
+static void sdr_clr_bits(void __iomem *reg, u32 bs)
+{
+       u32 val = readl(reg);
+
+       val &= ~bs;
+       writel(val, reg);
+}
+
+static void sdr_set_field(void __iomem *reg, u32 field, u32 val)
+{
+       unsigned int tv = readl(reg);
+
+       tv &= ~field;
+       tv |= ((val) << (ffs((unsigned int)field) - 1));
+       writel(tv, reg);
+}
+
+static void sdr_get_field(void __iomem *reg, u32 field, u32 *val)
+{
+       unsigned int tv = readl(reg);
+
+       *val = ((tv & field) >> (ffs((unsigned int)field) - 1));
+}
+
+static void msdc_reset_hw(struct msdc_host *host)
+{
+       u32 val;
+
+       sdr_set_bits(host->base + MSDC_CFG, MSDC_CFG_RST);
+       while (readl(host->base + MSDC_CFG) & MSDC_CFG_RST)
+               cpu_relax();
+
+       sdr_set_bits(host->base + MSDC_FIFOCS, MSDC_FIFOCS_CLR);
+       while (readl(host->base + MSDC_FIFOCS) & MSDC_FIFOCS_CLR)
+               cpu_relax();
+
+       val = readl(host->base + MSDC_INT);
+       writel(val, host->base + MSDC_INT);
+}
+
+static void msdc_cmd_next(struct msdc_host *host,
+               struct mmc_request *mrq, struct mmc_command *cmd);
+
+static u32 data_ints_mask = MSDC_INTEN_XFER_COMPL | MSDC_INTEN_DATTMO |
+                       MSDC_INTEN_DATCRCERR | MSDC_INTEN_DMA_BDCSERR |
+                       MSDC_INTEN_DMA_GPDCSERR | MSDC_INTEN_DMA_PROTECT;
+
+static u8 msdc_dma_calcs(u8 *buf, u32 len)
+{
+       u32 i, sum = 0;
+
+       for (i = 0; i < len; i++)
+               sum += buf[i];
+       return 0xff - (u8) sum;
+}
+
+static inline void msdc_dma_setup(struct msdc_host *host, struct msdc_dma *dma,
+               struct mmc_data *data)
+{
+       unsigned int j, dma_len;
+       dma_addr_t dma_address;
+       u32 dma_ctrl;
+       struct scatterlist *sg;
+       struct mt_gpdma_desc *gpd;
+       struct mt_bdma_desc *bd;
+
+       sg = data->sg;
+
+       gpd = dma->gpd;
+       bd = dma->bd;
+
+       /* modify gpd */
+       gpd->gpd_info |= GPDMA_DESC_HWO;
+       gpd->gpd_info |= GPDMA_DESC_BDP;
+       /* need to clear first. use these bits to calc checksum */
+       gpd->gpd_info &= ~GPDMA_DESC_CHECKSUM;
+       gpd->gpd_info |= msdc_dma_calcs((u8 *) gpd, 16) << 8;
+
+       /* modify bd */
+       for_each_sg(data->sg, sg, data->sg_count, j) {
+               dma_address = sg_dma_address(sg);
+               dma_len = sg_dma_len(sg);
+
+               /* init bd */
+               bd[j].bd_info &= ~BDMA_DESC_BLKPAD;
+               bd[j].bd_info &= ~BDMA_DESC_DWPAD;
+               bd[j].ptr = (u32)dma_address;
+               bd[j].bd_data_len &= ~BDMA_DESC_BUFLEN;
+               bd[j].bd_data_len |= (dma_len & BDMA_DESC_BUFLEN);
+
+               if (j == data->sg_count - 1) /* the last bd */
+                       bd[j].bd_info |= BDMA_DESC_EOL;
+               else
+                       bd[j].bd_info &= ~BDMA_DESC_EOL;
+
+               /* checksume need to clear first */
+               bd[j].bd_info &= ~BDMA_DESC_CHECKSUM;
+               bd[j].bd_info |= msdc_dma_calcs((u8 *)(&bd[j]), 16) << 8;
+       }
+
+       sdr_set_field(host->base + MSDC_DMA_CFG, MSDC_DMA_CFG_DECSEN, 1);
+       dma_ctrl = readl_relaxed(host->base + MSDC_DMA_CTRL);
+       dma_ctrl &= ~(MSDC_DMA_CTRL_BRUSTSZ | MSDC_DMA_CTRL_MODE);
+       dma_ctrl |= (MSDC_BURST_64B << 12 | 1 << 8);
+       writel_relaxed(dma_ctrl, host->base + MSDC_DMA_CTRL);
+       writel((u32)dma->gpd_addr, host->base + MSDC_DMA_SA);
+}
+
+static void msdc_prepare_data(struct msdc_host *host, struct mmc_request *mrq)
+{
+       struct mmc_data *data = mrq->data;
+
+       if (!(data->host_cookie & MSDC_PREPARE_FLAG)) {
+               bool read = (data->flags & MMC_DATA_READ) != 0;
+
+               data->host_cookie |= MSDC_PREPARE_FLAG;
+               data->sg_count = dma_map_sg(host->dev, data->sg, data->sg_len,
+                                          read ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
+       }
+}
+
+static void msdc_unprepare_data(struct msdc_host *host, struct mmc_request *mrq)
+{
+       struct mmc_data *data = mrq->data;
+
+       if (data->host_cookie & MSDC_ASYNC_FLAG)
+               return;
+
+       if (data->host_cookie & MSDC_PREPARE_FLAG) {
+               bool read = (data->flags & MMC_DATA_READ) != 0;
+
+               dma_unmap_sg(host->dev, data->sg, data->sg_len,
+                            read ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
+               data->host_cookie &= ~MSDC_PREPARE_FLAG;
+       }
+}
+
+/* clock control primitives */
+static void msdc_set_timeout(struct msdc_host *host, u32 ns, u32 clks)
+{
+       u32 timeout, clk_ns;
+       u32 mode = 0;
+
+       host->timeout_ns = ns;
+       host->timeout_clks = clks;
+       if (host->sclk == 0) {
+               timeout = 0;
+       } else {
+               clk_ns  = 1000000000UL / host->sclk;
+               timeout = (ns + clk_ns - 1) / clk_ns + clks;
+               /* in 1048576 sclk cycle unit */
+               timeout = (timeout + (0x1 << 20) - 1) >> 20;
+               sdr_get_field(host->base + MSDC_CFG, MSDC_CFG_CKMOD, &mode);
+               /*DDR mode will double the clk cycles for data timeout */
+               timeout = mode >= 2 ? timeout * 2 : timeout;
+               timeout = timeout > 1 ? timeout - 1 : 0;
+               timeout = timeout > 255 ? 255 : timeout;
+       }
+       sdr_set_field(host->base + SDC_CFG, SDC_CFG_DTOC, timeout);
+}
+
+static void msdc_gate_clock(struct msdc_host *host)
+{
+       clk_disable_unprepare(host->src_clk);
+       clk_disable_unprepare(host->h_clk);
+}
+
+static void msdc_ungate_clock(struct msdc_host *host)
+{
+       clk_prepare_enable(host->h_clk);
+       clk_prepare_enable(host->src_clk);
+       while (!(readl(host->base + MSDC_CFG) & MSDC_CFG_CKSTB))
+               cpu_relax();
+}
+
+static void msdc_set_mclk(struct msdc_host *host, int ddr, u32 hz)
+{
+       u32 mode;
+       u32 flags;
+       u32 div;
+       u32 sclk;
+
+       if (!hz) {
+               dev_dbg(host->dev, "set mclk to 0\n");
+               host->mclk = 0;
+               sdr_clr_bits(host->base + MSDC_CFG, MSDC_CFG_CKPDN);
+               return;
+       }
+
+       flags = readl(host->base + MSDC_INTEN);
+       sdr_clr_bits(host->base + MSDC_INTEN, flags);
+       if (ddr) { /* may need to modify later */
+               mode = 0x2; /* ddr mode and use divisor */
+               if (hz >= (host->src_clk_freq >> 2)) {
+                       div = 0; /* mean div = 1/4 */
+                       sclk = host->src_clk_freq >> 2; /* sclk = clk / 4 */
+               } else {
+                       div = (host->src_clk_freq + ((hz << 2) - 1)) / (hz << 2);
+                       sclk = (host->src_clk_freq >> 2) / div;
+                       div = (div >> 1);
+               }
+       } else if (hz >= host->src_clk_freq) {
+               mode = 0x1; /* no divisor */
+               div = 0;
+               sclk = host->src_clk_freq;
+       } else {
+               mode = 0x0; /* use divisor */
+               if (hz >= (host->src_clk_freq >> 1)) {
+                       div = 0; /* mean div = 1/2 */
+                       sclk = host->src_clk_freq >> 1; /* sclk = clk / 2 */
+               } else {
+                       div = (host->src_clk_freq + ((hz << 2) - 1)) / (hz << 2);
+                       sclk = (host->src_clk_freq >> 2) / div;
+               }
+       }
+       sdr_set_field(host->base + MSDC_CFG, MSDC_CFG_CKMOD | MSDC_CFG_CKDIV,
+                       (mode << 8) | (div % 0xff));
+       sdr_set_bits(host->base + MSDC_CFG, MSDC_CFG_CKPDN);
+       while (!(readl(host->base + MSDC_CFG) & MSDC_CFG_CKSTB))
+               cpu_relax();
+       host->sclk = sclk;
+       host->mclk = hz;
+       host->ddr = ddr;
+       /* need because clk changed. */
+       msdc_set_timeout(host, host->timeout_ns, host->timeout_clks);
+       sdr_set_bits(host->base + MSDC_INTEN, flags);
+
+       dev_dbg(host->dev, "sclk: %d, ddr: %d\n", host->sclk, ddr);
+}
+
+static inline u32 msdc_cmd_find_resp(struct msdc_host *host,
+               struct mmc_request *mrq, struct mmc_command *cmd)
+{
+       u32 resp;
+
+       switch (mmc_resp_type(cmd)) {
+               /* Actually, R1, R5, R6, R7 are the same */
+       case MMC_RSP_R1:
+               resp = 0x1;
+               break;
+       case MMC_RSP_R1B:
+               resp = 0x7;
+               break;
+       case MMC_RSP_R2:
+               resp = 0x2;
+               break;
+       case MMC_RSP_R3:
+               resp = 0x3;
+               break;
+       case MMC_RSP_NONE:
+       default:
+               resp = 0x0;
+               break;
+       }
+
+       return resp;
+}
+
+static inline u32 msdc_cmd_prepare_raw_cmd(struct msdc_host *host,
+               struct mmc_request *mrq, struct mmc_command *cmd)
+{
+       /* rawcmd :
+        * vol_swt << 30 | auto_cmd << 28 | blklen << 16 | go_irq << 15 |
+        * stop << 14 | rw << 13 | dtype << 11 | rsptyp << 7 | brk << 6 | opcode
+        */
+       u32 opcode = cmd->opcode;
+       u32 resp = msdc_cmd_find_resp(host, mrq, cmd);
+       u32 rawcmd = (opcode & 0x3f) | ((resp & 0x7) << 7);
+
+       host->cmd_rsp = resp;
+
+       if ((opcode == SD_IO_RW_DIRECT && cmd->flags == (unsigned int) -1) ||
+           opcode == MMC_STOP_TRANSMISSION)
+               rawcmd |= (0x1 << 14);
+       else if (opcode == SD_SWITCH_VOLTAGE)
+               rawcmd |= (0x1 << 30);
+       else if (opcode == SD_APP_SEND_SCR ||
+                opcode == SD_APP_SEND_NUM_WR_BLKS ||
+                (opcode == SD_SWITCH && mmc_cmd_type(cmd) == MMC_CMD_ADTC) ||
+                (opcode == SD_APP_SD_STATUS && mmc_cmd_type(cmd) == MMC_CMD_ADTC) ||
+                (opcode == MMC_SEND_EXT_CSD && mmc_cmd_type(cmd) == MMC_CMD_ADTC))
+               rawcmd |= (0x1 << 11);
+
+       if (cmd->data) {
+               struct mmc_data *data = cmd->data;
+
+               if (mmc_op_multi(opcode)) {
+                       if (mmc_card_mmc(host->mmc->card) && mrq->sbc &&
+                           !(mrq->sbc->arg & 0xFFFF0000))
+                               rawcmd |= 0x2 << 28; /* AutoCMD23 */
+               }
+
+               rawcmd |= ((data->blksz & 0xFFF) << 16);
+               if (data->flags & MMC_DATA_WRITE)
+                       rawcmd |= (0x1 << 13);
+               if (data->blocks > 1)
+                       rawcmd |= (0x2 << 11);
+               else
+                       rawcmd |= (0x1 << 11);
+               /* Always use dma mode */
+               sdr_clr_bits(host->base + MSDC_CFG, MSDC_CFG_PIO);
+
+               if (host->timeout_ns != data->timeout_ns ||
+                   host->timeout_clks != data->timeout_clks)
+                       msdc_set_timeout(host, data->timeout_ns,
+                                       data->timeout_clks);
+
+               writel(data->blocks, host->base + SDC_BLK_NUM);
+       }
+       return rawcmd;
+}
+
+static void msdc_start_data(struct msdc_host *host, struct mmc_request *mrq,
+                           struct mmc_command *cmd, struct mmc_data *data)
+{
+       bool read;
+
+       WARN_ON(host->data);
+       host->data = data;
+       read = data->flags & MMC_DATA_READ;
+
+       mod_delayed_work(system_wq, &host->req_timeout, DAT_TIMEOUT);
+       msdc_dma_setup(host, &host->dma, data);
+       sdr_set_bits(host->base + MSDC_INTEN, data_ints_mask);
+       sdr_set_field(host->base + MSDC_DMA_CTRL, MSDC_DMA_CTRL_START, 1);
+       dev_dbg(host->dev, "DMA start\n");
+       dev_dbg(host->dev, "%s: cmd=%d DMA data: %d blocks; read=%d\n",
+                       __func__, cmd->opcode, data->blocks, read);
+}
+
+static int msdc_auto_cmd_done(struct msdc_host *host, int events,
+               struct mmc_command *cmd)
+{
+       u32 *rsp = cmd->resp;
+
+       rsp[0] = readl(host->base + SDC_ACMD_RESP);
+
+       if (events & MSDC_INT_ACMDRDY) {
+               cmd->error = 0;
+       } else {
+               msdc_reset_hw(host);
+               if (events & MSDC_INT_ACMDCRCERR) {
+                       cmd->error = -EILSEQ;
+                       host->error |= REQ_STOP_EIO;
+               } else if (events & MSDC_INT_ACMDTMO) {
+                       cmd->error = -ETIMEDOUT;
+                       host->error |= REQ_STOP_TMO;
+               }
+               dev_err(host->dev,
+                       "%s: AUTO_CMD%d arg=%08X; rsp %08X; cmd_error=%d\n",
+                       __func__, cmd->opcode, cmd->arg, rsp[0], cmd->error);
+       }
+       return cmd->error;
+}
+
+static void msdc_track_cmd_data(struct msdc_host *host,
+                               struct mmc_command *cmd, struct mmc_data *data)
+{
+       if (host->error)
+               dev_dbg(host->dev, "%s: cmd=%d arg=%08X; host->error=0x%08X\n",
+                       __func__, cmd->opcode, cmd->arg, host->error);
+}
+
+static void msdc_request_done(struct msdc_host *host, struct mmc_request *mrq)
+{
+       unsigned long flags;
+       bool ret;
+
+       ret = cancel_delayed_work(&host->req_timeout);
+       if (!ret) {
+               /* delay work already running */
+               return;
+       }
+       spin_lock_irqsave(&host->lock, flags);
+       host->mrq = NULL;
+       spin_unlock_irqrestore(&host->lock, flags);
+
+       msdc_track_cmd_data(host, mrq->cmd, mrq->data);
+       if (mrq->data)
+               msdc_unprepare_data(host, mrq);
+       mmc_request_done(host->mmc, mrq);
+
+       pm_runtime_mark_last_busy(host->dev);
+       pm_runtime_put_autosuspend(host->dev);
+}
+
+/* returns true if command is fully handled; returns false otherwise */
+static bool msdc_cmd_done(struct msdc_host *host, int events,
+                         struct mmc_request *mrq, struct mmc_command *cmd)
+{
+       bool done = false;
+       bool sbc_error;
+       unsigned long flags;
+       u32 *rsp = cmd->resp;
+
+       if (mrq->sbc && cmd == mrq->cmd &&
+           (events & (MSDC_INT_ACMDRDY | MSDC_INT_ACMDCRCERR
+                                  | MSDC_INT_ACMDTMO)))
+               msdc_auto_cmd_done(host, events, mrq->sbc);
+
+       sbc_error = mrq->sbc && mrq->sbc->error;
+
+       if (!sbc_error && !(events & (MSDC_INT_CMDRDY
+                                       | MSDC_INT_RSPCRCERR
+                                       | MSDC_INT_CMDTMO)))
+               return done;
+
+       spin_lock_irqsave(&host->lock, flags);
+       done = !host->cmd;
+       host->cmd = NULL;
+       spin_unlock_irqrestore(&host->lock, flags);
+
+       if (done)
+               return true;
+
+       sdr_clr_bits(host->base + MSDC_INTEN, MSDC_INTEN_CMDRDY |
+                       MSDC_INTEN_RSPCRCERR | MSDC_INTEN_CMDTMO |
+                       MSDC_INTEN_ACMDRDY | MSDC_INTEN_ACMDCRCERR |
+                       MSDC_INTEN_ACMDTMO);
+       writel(cmd->arg, host->base + SDC_ARG);
+
+       if (cmd->flags & MMC_RSP_PRESENT) {
+               if (cmd->flags & MMC_RSP_136) {
+                       rsp[0] = readl(host->base + SDC_RESP3);
+                       rsp[1] = readl(host->base + SDC_RESP2);
+                       rsp[2] = readl(host->base + SDC_RESP1);
+                       rsp[3] = readl(host->base + SDC_RESP0);
+               } else {
+                       rsp[0] = readl(host->base + SDC_RESP0);
+               }
+       }
+
+       if (!sbc_error && !(events & MSDC_INT_CMDRDY)) {
+               msdc_reset_hw(host);
+               if (events & MSDC_INT_RSPCRCERR) {
+                       cmd->error = -EILSEQ;
+                       host->error |= REQ_CMD_EIO;
+               } else if (events & MSDC_INT_CMDTMO) {
+                       cmd->error = -ETIMEDOUT;
+                       host->error |= REQ_CMD_TMO;
+               }
+       }
+       if (cmd->error)
+               dev_dbg(host->dev,
+                               "%s: cmd=%d arg=%08X; rsp %08X; cmd_error=%d\n",
+                               __func__, cmd->opcode, cmd->arg, rsp[0],
+                               cmd->error);
+
+       msdc_cmd_next(host, mrq, cmd);
+       return true;
+}
+
+/* It is the core layer's responsibility to ensure card status
+ * is correct before issue a request. but host design do below
+ * checks recommended.
+ */
+static inline bool msdc_cmd_is_ready(struct msdc_host *host,
+               struct mmc_request *mrq, struct mmc_command *cmd)
+{
+       /* The max busy time we can endure is 20ms */
+       unsigned long tmo = jiffies + msecs_to_jiffies(20);
+
+       while ((readl(host->base + SDC_STS) & SDC_STS_CMDBUSY) &&
+                       time_before(jiffies, tmo))
+               cpu_relax();
+       if (readl(host->base + SDC_STS) & SDC_STS_CMDBUSY) {
+               dev_err(host->dev, "CMD bus busy detected\n");
+               host->error |= REQ_CMD_BUSY;
+               msdc_cmd_done(host, MSDC_INT_CMDTMO, mrq, cmd);
+               return false;
+       }
+
+       if (mmc_resp_type(cmd) == MMC_RSP_R1B || cmd->data) {
+               tmo = jiffies + msecs_to_jiffies(20);
+               /* R1B or with data, should check SDCBUSY */
+               while ((readl(host->base + SDC_STS) & SDC_STS_SDCBUSY) &&
+                               time_before(jiffies, tmo))
+                       cpu_relax();
+               if (readl(host->base + SDC_STS) & SDC_STS_SDCBUSY) {
+                       dev_err(host->dev, "Controller busy detected\n");
+                       host->error |= REQ_CMD_BUSY;
+                       msdc_cmd_done(host, MSDC_INT_CMDTMO, mrq, cmd);
+                       return false;
+               }
+       }
+       return true;
+}
+
+static void msdc_start_command(struct msdc_host *host,
+               struct mmc_request *mrq, struct mmc_command *cmd)
+{
+       u32 rawcmd;
+
+       WARN_ON(host->cmd);
+       host->cmd = cmd;
+
+       if (!msdc_cmd_is_ready(host, mrq, cmd))
+               return;
+
+       if ((readl(host->base + MSDC_FIFOCS) & MSDC_FIFOCS_TXCNT) >> 16 ||
+           readl(host->base + MSDC_FIFOCS) & MSDC_FIFOCS_RXCNT) {
+               dev_err(host->dev, "TX/RX FIFO non-empty before start of IO. Reset\n");
+               msdc_reset_hw(host);
+       }
+
+       cmd->error = 0;
+       rawcmd = msdc_cmd_prepare_raw_cmd(host, mrq, cmd);
+       mod_delayed_work(system_wq, &host->req_timeout, DAT_TIMEOUT);
+
+       sdr_set_bits(host->base + MSDC_INTEN, MSDC_INTEN_CMDRDY |
+                       MSDC_INTEN_RSPCRCERR | MSDC_INTEN_CMDTMO |
+                       MSDC_INTEN_ACMDRDY | MSDC_INTEN_ACMDCRCERR |
+                       MSDC_INTEN_ACMDTMO);
+       writel(cmd->arg, host->base + SDC_ARG);
+       writel(rawcmd, host->base + SDC_CMD);
+}
+
+static void msdc_cmd_next(struct msdc_host *host,
+               struct mmc_request *mrq, struct mmc_command *cmd)
+{
+       if (cmd->error || (mrq->sbc && mrq->sbc->error))
+               msdc_request_done(host, mrq);
+       else if (cmd == mrq->sbc)
+               msdc_start_command(host, mrq, mrq->cmd);
+       else if (!cmd->data)
+               msdc_request_done(host, mrq);
+       else
+               msdc_start_data(host, mrq, cmd, cmd->data);
+}
+
+static void msdc_ops_request(struct mmc_host *mmc, struct mmc_request *mrq)
+{
+       struct msdc_host *host = mmc_priv(mmc);
+
+       host->error = 0;
+       WARN_ON(host->mrq);
+       host->mrq = mrq;
+
+       pm_runtime_get_sync(host->dev);
+
+       if (mrq->data)
+               msdc_prepare_data(host, mrq);
+
+       /* if SBC is required, we have HW option and SW option.
+        * if HW option is enabled, and SBC does not have "special" flags,
+        * use HW option,  otherwise use SW option
+        */
+       if (mrq->sbc && (!mmc_card_mmc(mmc->card) ||
+           (mrq->sbc->arg & 0xFFFF0000)))
+               msdc_start_command(host, mrq, mrq->sbc);
+       else
+               msdc_start_command(host, mrq, mrq->cmd);
+}
+
+static void msdc_pre_req(struct mmc_host *mmc, struct mmc_request *mrq,
+               bool is_first_req)
+{
+       struct msdc_host *host = mmc_priv(mmc);
+       struct mmc_data *data = mrq->data;
+
+       if (!data)
+               return;
+
+       msdc_prepare_data(host, mrq);
+       data->host_cookie |= MSDC_ASYNC_FLAG;
+}
+
+static void msdc_post_req(struct mmc_host *mmc, struct mmc_request *mrq,
+               int err)
+{
+       struct msdc_host *host = mmc_priv(mmc);
+       struct mmc_data *data;
+
+       data = mrq->data;
+       if (!data)
+               return;
+       if (data->host_cookie) {
+               data->host_cookie &= ~MSDC_ASYNC_FLAG;
+               msdc_unprepare_data(host, mrq);
+       }
+}
+
+static void msdc_data_xfer_next(struct msdc_host *host,
+                               struct mmc_request *mrq, struct mmc_data *data)
+{
+       if (mmc_op_multi(mrq->cmd->opcode) && mrq->stop && !mrq->stop->error &&
+           (!data->bytes_xfered || !mrq->sbc))
+               msdc_start_command(host, mrq, mrq->stop);
+       else
+               msdc_request_done(host, mrq);
+}
+
+static bool msdc_data_xfer_done(struct msdc_host *host, u32 events,
+                               struct mmc_request *mrq, struct mmc_data *data)
+{
+       struct mmc_command *stop = data->stop;
+       unsigned long flags;
+       bool done;
+       unsigned int check_data = events &
+           (MSDC_INT_XFER_COMPL | MSDC_INT_DATCRCERR | MSDC_INT_DATTMO
+            | MSDC_INT_DMA_BDCSERR | MSDC_INT_DMA_GPDCSERR
+            | MSDC_INT_DMA_PROTECT);
+
+       spin_lock_irqsave(&host->lock, flags);
+       done = !host->data;
+       if (check_data)
+               host->data = NULL;
+       spin_unlock_irqrestore(&host->lock, flags);
+
+       if (done)
+               return true;
+
+       if (check_data || (stop && stop->error)) {
+               dev_dbg(host->dev, "DMA status: 0x%8X\n",
+                               readl(host->base + MSDC_DMA_CFG));
+               sdr_set_field(host->base + MSDC_DMA_CTRL, MSDC_DMA_CTRL_STOP,
+                               1);
+               while (readl(host->base + MSDC_DMA_CFG) & MSDC_DMA_CFG_STS)
+                       cpu_relax();
+               sdr_clr_bits(host->base + MSDC_INTEN, data_ints_mask);
+               dev_dbg(host->dev, "DMA stop\n");
+
+               if ((events & MSDC_INT_XFER_COMPL) && (!stop || !stop->error)) {
+                       data->bytes_xfered = data->blocks * data->blksz;
+               } else {
+                       dev_err(host->dev, "interrupt events: %x\n", events);
+                       msdc_reset_hw(host);
+                       host->error |= REQ_DAT_ERR;
+                       data->bytes_xfered = 0;
+
+                       if (events & MSDC_INT_DATTMO)
+                               data->error = -ETIMEDOUT;
+
+                       dev_err(host->dev, "%s: cmd=%d; blocks=%d",
+                               __func__, mrq->cmd->opcode, data->blocks);
+                       dev_err(host->dev, "data_error=%d xfer_size=%d\n",
+                                       (int)data->error, data->bytes_xfered);
+               }
+
+               msdc_data_xfer_next(host, mrq, data);
+               done = true;
+       }
+       return done;
+}
+
+static void msdc_set_buswidth(struct msdc_host *host, u32 width)
+{
+       u32 val = readl(host->base + SDC_CFG);
+
+       val &= ~SDC_CFG_BUSWIDTH;
+
+       switch (width) {
+       default:
+       case MMC_BUS_WIDTH_1:
+               val |= (MSDC_BUS_1BITS << 16);
+               break;
+       case MMC_BUS_WIDTH_4:
+               val |= (MSDC_BUS_4BITS << 16);
+               break;
+       case MMC_BUS_WIDTH_8:
+               val |= (MSDC_BUS_8BITS << 16);
+               break;
+       }
+
+       writel(val, host->base + SDC_CFG);
+       dev_dbg(host->dev, "Bus Width = %d", width);
+}
+
+static int msdc_ops_switch_volt(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+       struct msdc_host *host = mmc_priv(mmc);
+       int min_uv, max_uv;
+       int ret = 0;
+
+       if (!IS_ERR(mmc->supply.vqmmc)) {
+               if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_330) {
+                       min_uv = 3300000;
+                       max_uv = 3300000;
+               } else if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_180) {
+                       min_uv = 1800000;
+                       max_uv = 1800000;
+               } else {
+                       dev_err(host->dev, "Unsupported signal voltage!\n");
+                       return -EINVAL;
+               }
+
+               ret = regulator_set_voltage(mmc->supply.vqmmc, min_uv, max_uv);
+               if (ret) {
+                       dev_err(host->dev,
+                                       "Regulator set error %d: %d - %d\n",
+                                       ret, min_uv, max_uv);
+               } else {
+                       /* Apply different pinctrl settings for different signal voltage */
+                       if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_180)
+                               pinctrl_select_state(host->pinctrl, host->pins_uhs);
+                       else
+                               pinctrl_select_state(host->pinctrl, host->pins_default);
+               }
+       }
+       return ret;
+}
+
+static int msdc_card_busy(struct mmc_host *mmc)
+{
+       struct msdc_host *host = mmc_priv(mmc);
+       u32 status = readl(host->base + MSDC_PS);
+
+       /* check if any pin between dat[0:3] is low */
+       if (((status >> 16) & 0xf) != 0xf)
+               return 1;
+
+       return 0;
+}
+
+static void msdc_request_timeout(struct work_struct *work)
+{
+       struct msdc_host *host = container_of(work, struct msdc_host,
+                       req_timeout.work);
+
+       /* simulate HW timeout status */
+       dev_err(host->dev, "%s: aborting cmd/data/mrq\n", __func__);
+       if (host->mrq) {
+               dev_err(host->dev, "%s: aborting mrq=%p cmd=%d\n", __func__,
+                               host->mrq, host->mrq->cmd->opcode);
+               if (host->cmd) {
+                       dev_err(host->dev, "%s: aborting cmd=%d\n",
+                                       __func__, host->cmd->opcode);
+                       msdc_cmd_done(host, MSDC_INT_CMDTMO, host->mrq,
+                                       host->cmd);
+               } else if (host->data) {
+                       dev_err(host->dev, "%s: abort data: cmd%d; %d blocks\n",
+                                       __func__, host->mrq->cmd->opcode,
+                                       host->data->blocks);
+                       msdc_data_xfer_done(host, MSDC_INT_DATTMO, host->mrq,
+                                       host->data);
+               }
+       }
+}
+
+static irqreturn_t msdc_irq(int irq, void *dev_id)
+{
+       struct msdc_host *host = (struct msdc_host *) dev_id;
+
+       while (true) {
+               unsigned long flags;
+               struct mmc_request *mrq;
+               struct mmc_command *cmd;
+               struct mmc_data *data;
+               u32 events, event_mask;
+
+               spin_lock_irqsave(&host->lock, flags);
+               events = readl(host->base + MSDC_INT);
+               event_mask = readl(host->base + MSDC_INTEN);
+               /* clear interrupts */
+               writel(events & event_mask, host->base + MSDC_INT);
+
+               mrq = host->mrq;
+               cmd = host->cmd;
+               data = host->data;
+               spin_unlock_irqrestore(&host->lock, flags);
+
+               if (!(events & event_mask))
+                       break;
+
+               if (!mrq) {
+                       dev_err(host->dev,
+                               "%s: MRQ=NULL; events=%08X; event_mask=%08X\n",
+                               __func__, events, event_mask);
+                       WARN_ON(1);
+                       break;
+               }
+
+               dev_dbg(host->dev, "%s: events=%08X\n", __func__, events);
+
+               if (cmd)
+                       msdc_cmd_done(host, events, mrq, cmd);
+               else if (data)
+                       msdc_data_xfer_done(host, events, mrq, data);
+       }
+
+       return IRQ_HANDLED;
+}
+
+static void msdc_init_hw(struct msdc_host *host)
+{
+       u32 val;
+
+       /* Configure to MMC/SD mode, clock free running */
+       sdr_set_bits(host->base + MSDC_CFG, MSDC_CFG_MODE | MSDC_CFG_CKPDN);
+
+       /* Reset */
+       msdc_reset_hw(host);
+
+       /* Disable card detection */
+       sdr_clr_bits(host->base + MSDC_PS, MSDC_PS_CDEN);
+
+       /* Disable and clear all interrupts */
+       writel(0, host->base + MSDC_INTEN);
+       val = readl(host->base + MSDC_INT);
+       writel(val, host->base + MSDC_INT);
+
+       writel(0, host->base + MSDC_PAD_TUNE);
+       writel(0, host->base + MSDC_IOCON);
+       sdr_set_field(host->base + MSDC_IOCON, MSDC_IOCON_DDLSEL, 1);
+       writel(0x403c004f, host->base + MSDC_PATCH_BIT);
+       sdr_set_field(host->base + MSDC_PATCH_BIT, MSDC_CKGEN_MSDC_DLY_SEL, 1);
+       writel(0xffff0089, host->base + MSDC_PATCH_BIT1);
+       /* Configure to enable SDIO mode.
+        * it's must otherwise sdio cmd5 failed
+        */
+       sdr_set_bits(host->base + SDC_CFG, SDC_CFG_SDIO);
+
+       /* disable detect SDIO device interrupt function */
+       sdr_clr_bits(host->base + SDC_CFG, SDC_CFG_SDIOIDE);
+
+       /* Configure to default data timeout */
+       sdr_set_field(host->base + SDC_CFG, SDC_CFG_DTOC, 3);
+
+       dev_dbg(host->dev, "init hardware done!");
+}
+
+static void msdc_deinit_hw(struct msdc_host *host)
+{
+       u32 val;
+       /* Disable and clear all interrupts */
+       writel(0, host->base + MSDC_INTEN);
+
+       val = readl(host->base + MSDC_INT);
+       writel(val, host->base + MSDC_INT);
+}
+
+/* init gpd and bd list in msdc_drv_probe */
+static void msdc_init_gpd_bd(struct msdc_host *host, struct msdc_dma *dma)
+{
+       struct mt_gpdma_desc *gpd = dma->gpd;
+       struct mt_bdma_desc *bd = dma->bd;
+       int i;
+
+       memset(gpd, 0, sizeof(struct mt_gpdma_desc));
+
+       gpd->gpd_info = GPDMA_DESC_BDP; /* hwo, cs, bd pointer */
+       gpd->ptr = (u32)dma->bd_addr; /* physical address */
+
+       memset(bd, 0, sizeof(struct mt_bdma_desc) * MAX_BD_NUM);
+       for (i = 0; i < (MAX_BD_NUM - 1); i++)
+               bd[i].next = (u32)dma->bd_addr + sizeof(*bd) * (i + 1);
+}
+
+static void msdc_ops_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+       struct msdc_host *host = mmc_priv(mmc);
+       int ret;
+       u32 ddr = 0;
+
+       pm_runtime_get_sync(host->dev);
+
+       if (ios->timing == MMC_TIMING_UHS_DDR50 ||
+           ios->timing == MMC_TIMING_MMC_DDR52)
+               ddr = 1;
+
+       msdc_set_buswidth(host, ios->bus_width);
+
+       /* Suspend/Resume will do power off/on */
+       switch (ios->power_mode) {
+       case MMC_POWER_UP:
+               if (!IS_ERR(mmc->supply.vmmc)) {
+                       ret = mmc_regulator_set_ocr(mmc, mmc->supply.vmmc,
+                                       ios->vdd);
+                       if (ret) {
+                               dev_err(host->dev, "Failed to set vmmc power!\n");
+                               goto end;
+                       }
+               }
+               break;
+       case MMC_POWER_ON:
+               if (!IS_ERR(mmc->supply.vqmmc) && !host->vqmmc_enabled) {
+                       ret = regulator_enable(mmc->supply.vqmmc);
+                       if (ret)
+                               dev_err(host->dev, "Failed to set vqmmc power!\n");
+                       else
+                               host->vqmmc_enabled = true;
+               }
+               break;
+       case MMC_POWER_OFF:
+               if (!IS_ERR(mmc->supply.vmmc))
+                       mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0);
+
+               if (!IS_ERR(mmc->supply.vqmmc) && host->vqmmc_enabled) {
+                       regulator_disable(mmc->supply.vqmmc);
+                       host->vqmmc_enabled = false;
+               }
+               break;
+       default:
+               break;
+       }
+
+       if (host->mclk != ios->clock || host->ddr != ddr)
+               msdc_set_mclk(host, ddr, ios->clock);
+
+end:
+       pm_runtime_mark_last_busy(host->dev);
+       pm_runtime_put_autosuspend(host->dev);
+}
+
+static struct mmc_host_ops mt_msdc_ops = {
+       .post_req = msdc_post_req,
+       .pre_req = msdc_pre_req,
+       .request = msdc_ops_request,
+       .set_ios = msdc_ops_set_ios,
+       .start_signal_voltage_switch = msdc_ops_switch_volt,
+       .card_busy = msdc_card_busy,
+};
+
+static int msdc_drv_probe(struct platform_device *pdev)
+{
+       struct mmc_host *mmc;
+       struct msdc_host *host;
+       struct resource *res;
+       int ret;
+
+       if (!pdev->dev.of_node) {
+               dev_err(&pdev->dev, "No DT found\n");
+               return -EINVAL;
+       }
+       /* Allocate MMC host for this device */
+       mmc = mmc_alloc_host(sizeof(struct msdc_host), &pdev->dev);
+       if (!mmc)
+               return -ENOMEM;
+
+       host = mmc_priv(mmc);
+       ret = mmc_of_parse(mmc);
+       if (ret)
+               goto host_free;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       host->base = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(host->base)) {
+               ret = PTR_ERR(host->base);
+               goto host_free;
+       }
+
+       ret = mmc_regulator_get_supply(mmc);
+       if (ret == -EPROBE_DEFER)
+               goto host_free;
+
+       host->src_clk = devm_clk_get(&pdev->dev, "source");
+       if (IS_ERR(host->src_clk)) {
+               ret = PTR_ERR(host->src_clk);
+               goto host_free;
+       }
+
+       host->h_clk = devm_clk_get(&pdev->dev, "hclk");
+       if (IS_ERR(host->h_clk)) {
+               ret = PTR_ERR(host->h_clk);
+               goto host_free;
+       }
+
+       host->irq = platform_get_irq(pdev, 0);
+       if (host->irq < 0) {
+               ret = -EINVAL;
+               goto host_free;
+       }
+
+       host->pinctrl = devm_pinctrl_get(&pdev->dev);
+       if (IS_ERR(host->pinctrl)) {
+               ret = PTR_ERR(host->pinctrl);
+               dev_err(&pdev->dev, "Cannot find pinctrl!\n");
+               goto host_free;
+       }
+
+       host->pins_default = pinctrl_lookup_state(host->pinctrl, "default");
+       if (IS_ERR(host->pins_default)) {
+               ret = PTR_ERR(host->pins_default);
+               dev_err(&pdev->dev, "Cannot find pinctrl default!\n");
+               goto host_free;
+       }
+
+       host->pins_uhs = pinctrl_lookup_state(host->pinctrl, "state_uhs");
+       if (IS_ERR(host->pins_uhs)) {
+               ret = PTR_ERR(host->pins_uhs);
+               dev_err(&pdev->dev, "Cannot find pinctrl uhs!\n");
+               goto host_free;
+       }
+
+       host->dev = &pdev->dev;
+       host->mmc = mmc;
+       host->src_clk_freq = clk_get_rate(host->src_clk);
+       /* Set host parameters to mmc */
+       mmc->ops = &mt_msdc_ops;
+       mmc->f_min = host->src_clk_freq / (4 * 255);
+
+       mmc->caps |= MMC_CAP_ERASE | MMC_CAP_CMD23;
+       /* MMC core transfer sizes tunable parameters */
+       mmc->max_segs = MAX_BD_NUM;
+       mmc->max_seg_size = BDMA_DESC_BUFLEN;
+       mmc->max_blk_size = 2048;
+       mmc->max_req_size = 512 * 1024;
+       mmc->max_blk_count = mmc->max_req_size / 512;
+       host->dma_mask = DMA_BIT_MASK(32);
+       mmc_dev(mmc)->dma_mask = &host->dma_mask;
+
+       host->timeout_clks = 3 * 1048576;
+       host->dma.gpd = dma_alloc_coherent(&pdev->dev,
+                               sizeof(struct mt_gpdma_desc),
+                               &host->dma.gpd_addr, GFP_KERNEL);
+       host->dma.bd = dma_alloc_coherent(&pdev->dev,
+                               MAX_BD_NUM * sizeof(struct mt_bdma_desc),
+                               &host->dma.bd_addr, GFP_KERNEL);
+       if (!host->dma.gpd || !host->dma.bd) {
+               ret = -ENOMEM;
+               goto release_mem;
+       }
+       msdc_init_gpd_bd(host, &host->dma);
+       INIT_DELAYED_WORK(&host->req_timeout, msdc_request_timeout);
+       spin_lock_init(&host->lock);
+
+       platform_set_drvdata(pdev, mmc);
+       msdc_ungate_clock(host);
+       msdc_init_hw(host);
+
+       ret = devm_request_irq(&pdev->dev, host->irq, msdc_irq,
+               IRQF_TRIGGER_LOW | IRQF_ONESHOT, pdev->name, host);
+       if (ret)
+               goto release;
+
+       pm_runtime_set_active(host->dev);
+       pm_runtime_set_autosuspend_delay(host->dev, MTK_MMC_AUTOSUSPEND_DELAY);
+       pm_runtime_use_autosuspend(host->dev);
+       pm_runtime_enable(host->dev);
+       ret = mmc_add_host(mmc);
+
+       if (ret)
+               goto end;
+
+       return 0;
+end:
+       pm_runtime_disable(host->dev);
+release:
+       platform_set_drvdata(pdev, NULL);
+       msdc_deinit_hw(host);
+       msdc_gate_clock(host);
+release_mem:
+       if (host->dma.gpd)
+               dma_free_coherent(&pdev->dev,
+                       sizeof(struct mt_gpdma_desc),
+                       host->dma.gpd, host->dma.gpd_addr);
+       if (host->dma.bd)
+               dma_free_coherent(&pdev->dev,
+                       MAX_BD_NUM * sizeof(struct mt_bdma_desc),
+                       host->dma.bd, host->dma.bd_addr);
+host_free:
+       mmc_free_host(mmc);
+
+       return ret;
+}
+
+static int msdc_drv_remove(struct platform_device *pdev)
+{
+       struct mmc_host *mmc;
+       struct msdc_host *host;
+
+       mmc = platform_get_drvdata(pdev);
+       host = mmc_priv(mmc);
+
+       pm_runtime_get_sync(host->dev);
+
+       platform_set_drvdata(pdev, NULL);
+       mmc_remove_host(host->mmc);
+       msdc_deinit_hw(host);
+       msdc_gate_clock(host);
+
+       pm_runtime_disable(host->dev);
+       pm_runtime_put_noidle(host->dev);
+       dma_free_coherent(&pdev->dev,
+                       sizeof(struct mt_gpdma_desc),
+                       host->dma.gpd, host->dma.gpd_addr);
+       dma_free_coherent(&pdev->dev, MAX_BD_NUM * sizeof(struct mt_bdma_desc),
+                       host->dma.bd, host->dma.bd_addr);
+
+       mmc_free_host(host->mmc);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM
+static void msdc_save_reg(struct msdc_host *host)
+{
+       host->save_para.msdc_cfg = readl(host->base + MSDC_CFG);
+       host->save_para.iocon = readl(host->base + MSDC_IOCON);
+       host->save_para.sdc_cfg = readl(host->base + SDC_CFG);
+       host->save_para.pad_tune = readl(host->base + MSDC_PAD_TUNE);
+       host->save_para.patch_bit0 = readl(host->base + MSDC_PATCH_BIT);
+       host->save_para.patch_bit1 = readl(host->base + MSDC_PATCH_BIT1);
+}
+
+static void msdc_restore_reg(struct msdc_host *host)
+{
+       writel(host->save_para.msdc_cfg, host->base + MSDC_CFG);
+       writel(host->save_para.iocon, host->base + MSDC_IOCON);
+       writel(host->save_para.sdc_cfg, host->base + SDC_CFG);
+       writel(host->save_para.pad_tune, host->base + MSDC_PAD_TUNE);
+       writel(host->save_para.patch_bit0, host->base + MSDC_PATCH_BIT);
+       writel(host->save_para.patch_bit1, host->base + MSDC_PATCH_BIT1);
+}
+
+static int msdc_runtime_suspend(struct device *dev)
+{
+       struct mmc_host *mmc = dev_get_drvdata(dev);
+       struct msdc_host *host = mmc_priv(mmc);
+
+       msdc_save_reg(host);
+       msdc_gate_clock(host);
+       return 0;
+}
+
+static int msdc_runtime_resume(struct device *dev)
+{
+       struct mmc_host *mmc = dev_get_drvdata(dev);
+       struct msdc_host *host = mmc_priv(mmc);
+
+       msdc_ungate_clock(host);
+       msdc_restore_reg(host);
+       return 0;
+}
+#endif
+
+static const struct dev_pm_ops msdc_dev_pm_ops = {
+       SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+                               pm_runtime_force_resume)
+       SET_RUNTIME_PM_OPS(msdc_runtime_suspend, msdc_runtime_resume, NULL)
+};
+
+static const struct of_device_id msdc_of_ids[] = {
+       {   .compatible = "mediatek,mt8135-mmc", },
+       {}
+};
+
+static struct platform_driver mt_msdc_driver = {
+       .probe = msdc_drv_probe,
+       .remove = msdc_drv_remove,
+       .driver = {
+               .name = "mtk-msdc",
+               .of_match_table = msdc_of_ids,
+               .pm = &msdc_dev_pm_ops,
+       },
+};
+
+module_platform_driver(mt_msdc_driver);
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("MediaTek SD/MMC Card Driver");
index 317d709f75500fbc6aff6331ab48edb4d79f45b8..d110f9e98c4b45cc587378f740988acc2b3b3cfa 100644 (file)
@@ -605,11 +605,7 @@ static int mxcmci_push(struct mxcmci_host *host, void *_buf, int bytes)
                mxcmci_writel(host, cpu_to_le32(tmp), MMC_REG_BUFFER_ACCESS);
        }
 
-       stat = mxcmci_poll_status(host, STATUS_BUF_WRITE_RDY);
-       if (stat)
-               return stat;
-
-       return 0;
+       return mxcmci_poll_status(host, STATUS_BUF_WRITE_RDY);
 }
 
 static int mxcmci_transfer_data(struct mxcmci_host *host)
index a82411a2c024e2182dde2201fc7dc59f927e1c2c..d839147e591d24f5d5d0a97d389ea04ffbaa9883 100644 (file)
@@ -549,7 +549,7 @@ static const struct mmc_host_ops mxs_mmc_ops = {
        .enable_sdio_irq = mxs_mmc_enable_sdio_irq,
 };
 
-static struct platform_device_id mxs_ssp_ids[] = {
+static const struct platform_device_id mxs_ssp_ids[] = {
        {
                .name = "imx23-mmc",
                .driver_data = IMX23_SSP,
index 1d3d6c4bfdc6800c8c97162f76a916d1c42f7401..93137483ecde93fc509328c9445b8c61c65f8a48 100644 (file)
@@ -1474,7 +1474,7 @@ static int rtsx_pci_sdmmc_drv_remove(struct platform_device *pdev)
        return 0;
 }
 
-static struct platform_device_id rtsx_pci_sdmmc_ids[] = {
+static const struct platform_device_id rtsx_pci_sdmmc_ids[] = {
        {
                .name = DRV_NAME_RTSX_PCI_SDMMC,
        }, {
index 88af827e086b9f66b5acbfe663cb8837b1d62d24..6c71fc9f76c7ecab19a5a7ac24a53ab38a6125fd 100644 (file)
@@ -1439,7 +1439,7 @@ static int rtsx_usb_sdmmc_drv_remove(struct platform_device *pdev)
        return 0;
 }
 
-static struct platform_device_id rtsx_usb_sdmmc_ids[] = {
+static const struct platform_device_id rtsx_usb_sdmmc_ids[] = {
        {
                .name = "rtsx_usb_sdmmc",
        }, {
index 94cddf381ba3d72d1e9359afffb694f06dce6118..6291d5042ef2a5004212b1b14aff0d5a2f4c8613 100644 (file)
@@ -1856,7 +1856,7 @@ static int s3cmci_remove(struct platform_device *pdev)
        return 0;
 }
 
-static struct platform_device_id s3cmci_driver_ids[] = {
+static const struct platform_device_id s3cmci_driver_ids[] = {
        {
                .name   = "s3c2410-sdi",
                .driver_data    = 0,
index 0ef0343c603ad492937ef338af8b058ecc9f51e9..1c65d4690e7027f734ceaa8ae95e9e1b5c07b25f 100644 (file)
@@ -172,9 +172,19 @@ static int bcm2835_sdhci_probe(struct platform_device *pdev)
                ret = PTR_ERR(pltfm_host->clk);
                goto err;
        }
+       ret = clk_prepare_enable(pltfm_host->clk);
+       if (ret) {
+               dev_err(&pdev->dev, "failed to enable host clk\n");
+               goto err;
+       }
 
-       return sdhci_add_host(host);
+       ret = sdhci_add_host(host);
+       if (ret)
+               goto err_clk;
 
+       return 0;
+err_clk:
+       clk_disable_unprepare(pltfm_host->clk);
 err:
        sdhci_pltfm_free(pdev);
        return ret;
index 82f512d87cb8916e76314f465339c820ef92b1f1..faf0cb910c968abcce26c431422adff3e438f81d 100644 (file)
@@ -4,7 +4,7 @@
  * derived from the OF-version.
  *
  * Copyright (c) 2010 Pengutronix e.K.
- *   Author: Wolfram Sang <w.sang@pengutronix.de>
+ *   Author: Wolfram Sang <kernel@pengutronix.de>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
 #define ESDHC_FLAG_STD_TUNING          BIT(5)
 /* The IP has SDHCI_CAPABILITIES_1 register */
 #define ESDHC_FLAG_HAVE_CAP1           BIT(6)
+/*
+ * The IP has errata ERR004536
+ * uSDHC: ADMA Length Mismatch Error occurs if the AHB read access is slow,
+ * when reading data from the card
+ */
+#define ESDHC_FLAG_ERR004536           BIT(7)
+/* The IP supports HS200 mode */
+#define ESDHC_FLAG_HS200               BIT(8)
 
 struct esdhc_soc_data {
        u32 flags;
@@ -139,7 +147,13 @@ static struct esdhc_soc_data usdhc_imx6q_data = {
 
 static struct esdhc_soc_data usdhc_imx6sl_data = {
        .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
-                       | ESDHC_FLAG_HAVE_CAP1,
+                       | ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_ERR004536
+                       | ESDHC_FLAG_HS200,
+};
+
+static struct esdhc_soc_data usdhc_imx6sx_data = {
+       .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
+                       | ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200,
 };
 
 struct pltfm_imx_data {
@@ -161,7 +175,7 @@ struct pltfm_imx_data {
        u32 is_ddr;
 };
 
-static struct platform_device_id imx_esdhc_devtype[] = {
+static const struct platform_device_id imx_esdhc_devtype[] = {
        {
                .name = "sdhci-esdhc-imx25",
                .driver_data = (kernel_ulong_t) &esdhc_imx25_data,
@@ -182,6 +196,7 @@ static const struct of_device_id imx_esdhc_dt_ids[] = {
        { .compatible = "fsl,imx35-esdhc", .data = &esdhc_imx35_data, },
        { .compatible = "fsl,imx51-esdhc", .data = &esdhc_imx51_data, },
        { .compatible = "fsl,imx53-esdhc", .data = &esdhc_imx53_data, },
+       { .compatible = "fsl,imx6sx-usdhc", .data = &usdhc_imx6sx_data, },
        { .compatible = "fsl,imx6sl-usdhc", .data = &usdhc_imx6sl_data, },
        { .compatible = "fsl,imx6q-usdhc", .data = &usdhc_imx6q_data, },
        { /* sentinel */ }
@@ -298,7 +313,7 @@ static void esdhc_writel_le(struct sdhci_host *host, u32 val, int reg)
        u32 data;
 
        if (unlikely(reg == SDHCI_INT_ENABLE || reg == SDHCI_SIGNAL_ENABLE)) {
-               if (val & SDHCI_INT_CARD_INT) {
+               if ((val & SDHCI_INT_CARD_INT) && !esdhc_is_usdhc(imx_data)) {
                        /*
                         * Clear and then set D3CD bit to avoid missing the
                         * card interrupt.  This is a eSDHC controller problem
@@ -313,6 +328,11 @@ static void esdhc_writel_le(struct sdhci_host *host, u32 val, int reg)
                        data |= ESDHC_CTRL_D3CD;
                        writel(data, host->ioaddr + SDHCI_HOST_CONTROL);
                }
+
+               if (val & SDHCI_INT_ADMA_ERROR) {
+                       val &= ~SDHCI_INT_ADMA_ERROR;
+                       val |= ESDHC_INT_VENDOR_SPEC_DMA_ERR;
+               }
        }
 
        if (unlikely((imx_data->socdata->flags & ESDHC_FLAG_MULTIBLK_NO_INT)
@@ -333,13 +353,6 @@ static void esdhc_writel_le(struct sdhci_host *host, u32 val, int reg)
                        }
        }
 
-       if (unlikely(reg == SDHCI_INT_ENABLE || reg == SDHCI_SIGNAL_ENABLE)) {
-               if (val & SDHCI_INT_ADMA_ERROR) {
-                       val &= ~SDHCI_INT_ADMA_ERROR;
-                       val |= ESDHC_INT_VENDOR_SPEC_DMA_ERR;
-               }
-       }
-
        writel(val, host->ioaddr + reg);
 }
 
@@ -903,7 +916,8 @@ sdhci_esdhc_imx_probe_dt(struct platform_device *pdev,
 
        mmc_of_parse_voltage(np, &host->ocr_mask);
 
-       return 0;
+       /* call to generic mmc_of_parse to support additional capabilities */
+       return mmc_of_parse(host->mmc);
 }
 #else
 static inline int
@@ -924,6 +938,7 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
        struct esdhc_platform_data *boarddata;
        int err;
        struct pltfm_imx_data *imx_data;
+       bool dt = true;
 
        host = sdhci_pltfm_init(pdev, &sdhci_esdhc_imx_pdata, 0);
        if (IS_ERR(host))
@@ -991,6 +1006,16 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
                writel(0x08100810, host->ioaddr + ESDHC_WTMK_LVL);
                host->quirks2 |= SDHCI_QUIRK2_PRESET_VALUE_BROKEN;
                host->mmc->caps |= MMC_CAP_1_8V_DDR;
+
+               if (!(imx_data->socdata->flags & ESDHC_FLAG_HS200))
+                       host->quirks2 |= SDHCI_QUIRK2_BROKEN_HS200;
+
+               /*
+               * errata ESDHC_FLAG_ERR004536 fix for MX6Q TO1.2 and MX6DL
+               * TO1.1, it's harmless for MX6SL
+               */
+               writel(readl(host->ioaddr + 0x6c) | BIT(7),
+                       host->ioaddr + 0x6c);
        }
 
        if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING)
@@ -1002,6 +1027,9 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
                        ESDHC_STD_TUNING_EN | ESDHC_TUNING_START_TAP,
                        host->ioaddr + ESDHC_TUNING_CTRL);
 
+       if (imx_data->socdata->flags & ESDHC_FLAG_ERR004536)
+               host->quirks |= SDHCI_QUIRK_BROKEN_ADMA;
+
        boarddata = &imx_data->boarddata;
        if (sdhci_esdhc_imx_probe_dt(pdev, host, boarddata) < 0) {
                if (!host->mmc->parent->platform_data) {
@@ -1011,11 +1039,44 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
                }
                imx_data->boarddata = *((struct esdhc_platform_data *)
                                        host->mmc->parent->platform_data);
+               dt = false;
+       }
+       /* write_protect */
+       if (boarddata->wp_type == ESDHC_WP_GPIO && !dt) {
+               err = mmc_gpio_request_ro(host->mmc, boarddata->wp_gpio);
+               if (err) {
+                       dev_err(mmc_dev(host->mmc),
+                               "failed to request write-protect gpio!\n");
+                       goto disable_clk;
+               }
+               host->mmc->caps2 |= MMC_CAP2_RO_ACTIVE_HIGH;
        }
 
        /* card_detect */
-       if (boarddata->cd_type == ESDHC_CD_CONTROLLER)
+       switch (boarddata->cd_type) {
+       case ESDHC_CD_GPIO:
+               if (dt)
+                       break;
+               err = mmc_gpio_request_cd(host->mmc, boarddata->cd_gpio, 0);
+               if (err) {
+                       dev_err(mmc_dev(host->mmc),
+                               "failed to request card-detect gpio!\n");
+                       goto disable_clk;
+               }
+               /* fall through */
+
+       case ESDHC_CD_CONTROLLER:
+               /* we have a working card_detect back */
                host->quirks &= ~SDHCI_QUIRK_BROKEN_CARD_DETECTION;
+               break;
+
+       case ESDHC_CD_PERMANENT:
+               host->mmc->caps |= MMC_CAP_NONREMOVABLE;
+               break;
+
+       case ESDHC_CD_NONE:
+               break;
+       }
 
        switch (boarddata->max_bus_width) {
        case 8:
@@ -1048,11 +1109,6 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
                host->quirks2 |= SDHCI_QUIRK2_NO_1_8_V;
        }
 
-       /* call to generic mmc_of_parse to support additional capabilities */
-       err = mmc_of_parse(host->mmc);
-       if (err)
-               goto disable_clk;
-
        err = sdhci_add_host(host);
        if (err)
                goto disable_clk;
@@ -1151,5 +1207,5 @@ static struct platform_driver sdhci_esdhc_imx_driver = {
 module_platform_driver(sdhci_esdhc_imx_driver);
 
 MODULE_DESCRIPTION("SDHCI driver for Freescale i.MX eSDHC");
-MODULE_AUTHOR("Wolfram Sang <w.sang@pengutronix.de>");
+MODULE_AUTHOR("Wolfram Sang <kernel@pengutronix.de>");
 MODULE_LICENSE("GPL v2");
index 6287d426c96bf933237aa948881b5bce53215fcb..21c0c08dfe54cf997e7d7031b9db5bac9ff77e64 100644 (file)
@@ -20,6 +20,7 @@
  */
 
 #include <linux/module.h>
+#include <linux/of_device.h>
 #include "sdhci-pltfm.h"
 
 #define SDHCI_ARASAN_CLK_CTRL_OFFSET   0x2c
@@ -168,6 +169,11 @@ static int sdhci_arasan_probe(struct platform_device *pdev)
                goto clk_disable_all;
        }
 
+       if (of_device_is_compatible(pdev->dev.of_node, "arasan,sdhci-4.9a")) {
+               host->quirks |= SDHCI_QUIRK_NO_HISPD_BIT;
+               host->quirks2 |= SDHCI_QUIRK2_HOST_NO_CMD23;
+       }
+
        sdhci_get_of_property(pdev);
        pltfm_host = sdhci_priv(host);
        pltfm_host->priv = sdhci_arasan;
@@ -208,6 +214,7 @@ static int sdhci_arasan_remove(struct platform_device *pdev)
 
 static const struct of_device_id sdhci_arasan_of_match[] = {
        { .compatible = "arasan,sdhci-8.9a" },
+       { .compatible = "arasan,sdhci-4.9a" },
        { }
 };
 MODULE_DEVICE_TABLE(of, sdhci_arasan_of_match);
index 22e9111b11ffcbfcfcfeba35ba9ab260256a9de7..797be7549a15c01a0e9acda2b0adaccb0a6c9416 100644 (file)
@@ -199,7 +199,7 @@ static unsigned int esdhc_of_get_min_clock(struct sdhci_host *host)
 
 static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock)
 {
-       int pre_div = 2;
+       int pre_div = 1;
        int div = 1;
        u32 temp;
 
@@ -229,7 +229,7 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock)
 
        dev_dbg(mmc_dev(host->mmc), "desired SD clock: %d, actual: %d\n",
                clock, host->max_clk / pre_div / div);
-
+       host->mmc->actual_clock = host->max_clk / pre_div / div;
        pre_div >>= 1;
        div--;
 
@@ -361,6 +361,13 @@ static int sdhci_esdhc_probe(struct platform_device *pdev)
        sdhci_get_of_property(pdev);
 
        np = pdev->dev.of_node;
+       if (of_device_is_compatible(np, "fsl,p5040-esdhc") ||
+           of_device_is_compatible(np, "fsl,p5020-esdhc") ||
+           of_device_is_compatible(np, "fsl,p4080-esdhc") ||
+           of_device_is_compatible(np, "fsl,p1020-esdhc") ||
+           of_device_is_compatible(np, "fsl,t1040-esdhc"))
+               host->quirks &= ~SDHCI_QUIRK_BROKEN_CARD_DETECTION;
+
        if (of_device_is_compatible(np, "fsl,p2020-esdhc")) {
                /*
                 * Freescale messed up with P2020 as it has a non-standard
index a611217769f5080fd664ef82bc98c273046bcf2f..56fddc622a545515e330492c39021233fde13af7 100644 (file)
@@ -3,3 +3,6 @@
 
 struct sdhci_pci_data *(*sdhci_pci_get_data)(struct pci_dev *pdev, int slotno);
 EXPORT_SYMBOL_GPL(sdhci_pci_get_data);
+
+int sdhci_pci_spt_drive_strength;
+EXPORT_SYMBOL_GPL(sdhci_pci_spt_drive_strength);
index 7a3fc16d0a6c601fdb65cac29451232f5f6d30aa..94f54d2772e885b891024db94bb152890468f548 100644 (file)
@@ -20,6 +20,7 @@
 #include <linux/slab.h>
 #include <linux/device.h>
 #include <linux/mmc/host.h>
+#include <linux/mmc/mmc.h>
 #include <linux/scatterlist.h>
 #include <linux/io.h>
 #include <linux/gpio.h>
@@ -266,6 +267,69 @@ static void sdhci_pci_int_hw_reset(struct sdhci_host *host)
        usleep_range(300, 1000);
 }
 
+static int spt_select_drive_strength(struct sdhci_host *host,
+                                    struct mmc_card *card,
+                                    unsigned int max_dtr,
+                                    int host_drv, int card_drv, int *drv_type)
+{
+       int drive_strength;
+
+       if (sdhci_pci_spt_drive_strength > 0)
+               drive_strength = sdhci_pci_spt_drive_strength & 0xf;
+       else
+               drive_strength = 1; /* 33-ohm */
+
+       if ((mmc_driver_type_mask(drive_strength) & card_drv) == 0)
+               drive_strength = 0; /* Default 50-ohm */
+
+       return drive_strength;
+}
+
+/* Try to read the drive strength from the card */
+static void spt_read_drive_strength(struct sdhci_host *host)
+{
+       u32 val, i, t;
+       u16 m;
+
+       if (sdhci_pci_spt_drive_strength)
+               return;
+
+       sdhci_pci_spt_drive_strength = -1;
+
+       m = sdhci_readw(host, SDHCI_HOST_CONTROL2) & 0x7;
+       if (m != 3 && m != 5)
+               return;
+       val = sdhci_readl(host, SDHCI_PRESENT_STATE);
+       if (val & 0x3)
+               return;
+       sdhci_writel(host, 0x007f0023, SDHCI_INT_ENABLE);
+       sdhci_writel(host, 0, SDHCI_SIGNAL_ENABLE);
+       sdhci_writew(host, 0x10, SDHCI_TRANSFER_MODE);
+       sdhci_writeb(host, 0xe, SDHCI_TIMEOUT_CONTROL);
+       sdhci_writew(host, 512, SDHCI_BLOCK_SIZE);
+       sdhci_writew(host, 1, SDHCI_BLOCK_COUNT);
+       sdhci_writel(host, 0, SDHCI_ARGUMENT);
+       sdhci_writew(host, 0x83b, SDHCI_COMMAND);
+       for (i = 0; i < 1000; i++) {
+               val = sdhci_readl(host, SDHCI_INT_STATUS);
+               if (val & 0xffff8000)
+                       return;
+               if (val & 0x20)
+                       break;
+               udelay(1);
+       }
+       val = sdhci_readl(host, SDHCI_PRESENT_STATE);
+       if (!(val & 0x800))
+               return;
+       for (i = 0; i < 47; i++)
+               val = sdhci_readl(host, SDHCI_BUFFER);
+       t = val & 0xf00;
+       if (t != 0x200 && t != 0x300)
+               return;
+
+       sdhci_pci_spt_drive_strength = 0x10 | ((val >> 12) & 0xf);
+}
+
 static int byt_emmc_probe_slot(struct sdhci_pci_slot *slot)
 {
        slot->host->mmc->caps |= MMC_CAP_8_BIT_DATA | MMC_CAP_NONREMOVABLE |
@@ -276,6 +340,10 @@ static int byt_emmc_probe_slot(struct sdhci_pci_slot *slot)
        slot->hw_reset = sdhci_pci_int_hw_reset;
        if (slot->chip->pdev->device == PCI_DEVICE_ID_INTEL_BSW_EMMC)
                slot->host->timeout_clk = 1000; /* 1000 kHz i.e. 1 MHz */
+       if (slot->chip->pdev->device == PCI_DEVICE_ID_INTEL_SPT_EMMC) {
+               spt_read_drive_strength(slot->host);
+               slot->select_drive_strength = spt_select_drive_strength;
+       }
        return 0;
 }
 
@@ -302,6 +370,7 @@ static const struct sdhci_pci_fixes sdhci_intel_byt_emmc = {
        .probe_slot     = byt_emmc_probe_slot,
        .quirks         = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
        .quirks2        = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
+                         SDHCI_QUIRK2_CAPS_BIT63_FOR_HS400 |
                          SDHCI_QUIRK2_STOP_WITH_TC,
 };
 
@@ -655,14 +724,37 @@ static const struct sdhci_pci_fixes sdhci_rtsx = {
        .probe_slot     = rtsx_probe_slot,
 };
 
+/*AMD chipset generation*/
+enum amd_chipset_gen {
+       AMD_CHIPSET_BEFORE_ML,
+       AMD_CHIPSET_CZ,
+       AMD_CHIPSET_NL,
+       AMD_CHIPSET_UNKNOWN,
+};
+
 static int amd_probe(struct sdhci_pci_chip *chip)
 {
        struct pci_dev  *smbus_dev;
+       enum amd_chipset_gen gen;
 
        smbus_dev = pci_get_device(PCI_VENDOR_ID_AMD,
                        PCI_DEVICE_ID_AMD_HUDSON2_SMBUS, NULL);
+       if (smbus_dev) {
+               gen = AMD_CHIPSET_BEFORE_ML;
+       } else {
+               smbus_dev = pci_get_device(PCI_VENDOR_ID_AMD,
+                               PCI_DEVICE_ID_AMD_KERNCZ_SMBUS, NULL);
+               if (smbus_dev) {
+                       if (smbus_dev->revision < 0x51)
+                               gen = AMD_CHIPSET_CZ;
+                       else
+                               gen = AMD_CHIPSET_NL;
+               } else {
+                       gen = AMD_CHIPSET_UNKNOWN;
+               }
+       }
 
-       if (smbus_dev && (smbus_dev->revision < 0x51)) {
+       if ((gen == AMD_CHIPSET_BEFORE_ML) || (gen == AMD_CHIPSET_CZ)) {
                chip->quirks2 |= SDHCI_QUIRK2_CLEAR_TRANSFERMODE_REG_BEFORE_CMD;
                chip->quirks2 |= SDHCI_QUIRK2_BROKEN_HS200;
        }
@@ -1203,6 +1295,20 @@ static void sdhci_pci_hw_reset(struct sdhci_host *host)
                slot->hw_reset(host);
 }
 
+static int sdhci_pci_select_drive_strength(struct sdhci_host *host,
+                                          struct mmc_card *card,
+                                          unsigned int max_dtr, int host_drv,
+                                          int card_drv, int *drv_type)
+{
+       struct sdhci_pci_slot *slot = sdhci_priv(host);
+
+       if (!slot->select_drive_strength)
+               return 0;
+
+       return slot->select_drive_strength(host, card, max_dtr, host_drv,
+                                          card_drv, drv_type);
+}
+
 static const struct sdhci_ops sdhci_pci_ops = {
        .set_clock      = sdhci_set_clock,
        .enable_dma     = sdhci_pci_enable_dma,
@@ -1210,6 +1316,7 @@ static const struct sdhci_ops sdhci_pci_ops = {
        .reset          = sdhci_reset,
        .set_uhs_signaling = sdhci_set_uhs_signaling,
        .hw_reset               = sdhci_pci_hw_reset,
+       .select_drive_strength  = sdhci_pci_select_drive_strength,
 };
 
 /*****************************************************************************\
index 1ec684d06d54733b35b2427d4007bfd5cfd3d52b..541f1cad5247f0f4ea04d18b9c89367e3f1801ba 100644 (file)
@@ -72,6 +72,10 @@ struct sdhci_pci_slot {
        bool                    cd_override_level;
 
        void (*hw_reset)(struct sdhci_host *host);
+       int (*select_drive_strength)(struct sdhci_host *host,
+                                    struct mmc_card *card,
+                                    unsigned int max_dtr, int host_drv,
+                                    int card_drv, int *drv_type);
 };
 
 struct sdhci_pci_chip {
index f98008b5ea77926726d0fc5bec79abea4829b39a..beffd8615489d73fb80042a2b95855a4c04f14f7 100644 (file)
@@ -252,9 +252,7 @@ static int sdhci_pxav2_remove(struct platform_device *pdev)
 static struct platform_driver sdhci_pxav2_driver = {
        .driver         = {
                .name   = "sdhci-pxav2",
-#ifdef CONFIG_OF
-               .of_match_table = sdhci_pxav2_of_match,
-#endif
+               .of_match_table = of_match_ptr(sdhci_pxav2_of_match),
                .pm     = SDHCI_PLTFM_PMOPS,
        },
        .probe          = sdhci_pxav2_probe,
index b5103a247bc1b6179373b2a027fda00d30c0ada9..9cd5fc62f130871aaa6390ac8e25b0f0f2fedf03 100644 (file)
@@ -457,12 +457,8 @@ static int sdhci_pxav3_probe(struct platform_device *pdev)
 
        platform_set_drvdata(pdev, host);
 
-       if (host->mmc->pm_caps & MMC_PM_KEEP_POWER) {
+       if (host->mmc->pm_caps & MMC_PM_WAKE_SDIO_IRQ)
                device_init_wakeup(&pdev->dev, 1);
-               host->mmc->pm_flags |= MMC_PM_WAKE_SDIO_IRQ;
-       } else {
-               device_init_wakeup(&pdev->dev, 0);
-       }
 
        pm_runtime_put_autosuspend(&pdev->dev);
 
@@ -578,9 +574,7 @@ static const struct dev_pm_ops sdhci_pxav3_pmops = {
 static struct platform_driver sdhci_pxav3_driver = {
        .driver         = {
                .name   = "sdhci-pxav3",
-#ifdef CONFIG_OF
-               .of_match_table = sdhci_pxav3_of_match,
-#endif
+               .of_match_table = of_match_ptr(sdhci_pxav3_of_match),
                .pm     = SDHCI_PXAV3_PMOPS,
        },
        .probe          = sdhci_pxav3_probe,
index c6d2dd7317c19fc9e867485aec84edb4221b0144..70c724bc6fc7844a9e559e72a0a74f7d33d60e66 100644 (file)
@@ -736,7 +736,7 @@ static struct sdhci_s3c_drv_data exynos4_sdhci_drv_data = {
 #define EXYNOS4_SDHCI_DRV_DATA ((kernel_ulong_t)NULL)
 #endif
 
-static struct platform_device_id sdhci_s3c_driver_ids[] = {
+static const struct platform_device_id sdhci_s3c_driver_ids[] = {
        {
                .name           = "s3c-sdhci",
                .driver_data    = (kernel_ulong_t)NULL,
index 32848eb7ad807d70acee4a8125e21f509f24f65e..0110bae25b7e8e1d6dcfb54a6a8a518c2763f122 100644 (file)
@@ -17,7 +17,7 @@
 
 #define SDHCI_CLK_DELAY_SETTING 0x4C
 #define SDHCI_SIRF_8BITBUS BIT(3)
-#define SIRF_TUNING_COUNT 128
+#define SIRF_TUNING_COUNT 16384
 
 struct sdhci_sirf_priv {
        int gpio_cd;
@@ -43,10 +43,43 @@ static void sdhci_sirf_set_bus_width(struct sdhci_host *host, int width)
        sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
 }
 
+static u32 sdhci_sirf_readl_le(struct sdhci_host *host, int reg)
+{
+       u32 val = readl(host->ioaddr + reg);
+
+       if (unlikely((reg == SDHCI_CAPABILITIES_1) &&
+                       (host->mmc->caps & MMC_CAP_UHS_SDR50))) {
+               /* fake CAP_1 register */
+               val = SDHCI_SUPPORT_SDR50 | SDHCI_USE_SDR50_TUNING;
+       }
+
+       if (unlikely(reg == SDHCI_SLOT_INT_STATUS)) {
+               u32 prss = val;
+               /* fake chips as V3.0 host conreoller */
+               prss &= ~(0xFF << 16);
+               val = prss | (SDHCI_SPEC_300 << 16);
+       }
+       return val;
+}
+
+static u16 sdhci_sirf_readw_le(struct sdhci_host *host, int reg)
+{
+       u16 ret = 0;
+
+       ret = readw(host->ioaddr + reg);
+
+       if (unlikely(reg == SDHCI_HOST_VERSION)) {
+               ret = readw(host->ioaddr + SDHCI_HOST_VERSION);
+               ret |= SDHCI_SPEC_300;
+       }
+
+       return ret;
+}
+
 static int sdhci_sirf_execute_tuning(struct sdhci_host *host, u32 opcode)
 {
        int tuning_seq_cnt = 3;
-       u8 phase, tuned_phases[SIRF_TUNING_COUNT];
+       int phase;
        u8 tuned_phase_cnt = 0;
        int rc = 0, longest_range = 0;
        int start = -1, end = 0, tuning_value = -1, range = 0;
@@ -58,6 +91,7 @@ static int sdhci_sirf_execute_tuning(struct sdhci_host *host, u32 opcode)
 
 retry:
        phase = 0;
+       tuned_phase_cnt = 0;
        do {
                sdhci_writel(host,
                        clock_setting | phase,
@@ -65,7 +99,7 @@ retry:
 
                if (!mmc_send_tuning(mmc)) {
                        /* Tuning is successful at this tuning point */
-                       tuned_phases[tuned_phase_cnt++] = phase;
+                       tuned_phase_cnt++;
                        dev_dbg(mmc_dev(mmc), "%s: Found good phase = %d\n",
                                 mmc_hostname(mmc), phase);
                        if (start == -1)
@@ -85,7 +119,7 @@ retry:
                        start = -1;
                        end = range = 0;
                }
-       } while (++phase < ARRAY_SIZE(tuned_phases));
+       } while (++phase < SIRF_TUNING_COUNT);
 
        if (tuned_phase_cnt && tuning_value > 0) {
                /*
@@ -112,6 +146,8 @@ retry:
 }
 
 static struct sdhci_ops sdhci_sirf_ops = {
+       .read_l = sdhci_sirf_readl_le,
+       .read_w = sdhci_sirf_readw_le,
        .platform_execute_tuning = sdhci_sirf_execute_tuning,
        .set_clock = sdhci_set_clock,
        .get_max_clock  = sdhci_pltfm_clk_get_max_clock,
index 682f2bb0f4bf3dc200ccb3bf0451a8759f1cd363..969c2b0d57fd335285c1f10316c5f0c39a3ea4c7 100644 (file)
@@ -509,4 +509,4 @@ module_platform_driver(sdhci_st_driver);
 MODULE_DESCRIPTION("SDHCI driver for STMicroelectronics SoCs");
 MODULE_AUTHOR("Giuseppe Cavallaro <peppe.cavallaro@st.com>");
 MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:st-sdhci");
+MODULE_ALIAS("platform:sdhci-st");
index c80287a027356e079e366401c2d5f64d9967461b..bc1445238fb3053b8d2a03a7bb35a25ddfff4b61 100644 (file)
@@ -52,7 +52,6 @@ static void sdhci_finish_data(struct sdhci_host *);
 
 static void sdhci_finish_command(struct sdhci_host *);
 static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode);
-static void sdhci_tuning_timer(unsigned long data);
 static void sdhci_enable_preset_value(struct sdhci_host *host, bool enable);
 static int sdhci_pre_dma_transfer(struct sdhci_host *host,
                                        struct mmc_data *data,
@@ -254,17 +253,6 @@ static void sdhci_init(struct sdhci_host *host, int soft)
 static void sdhci_reinit(struct sdhci_host *host)
 {
        sdhci_init(host, 0);
-       /*
-        * Retuning stuffs are affected by different cards inserted and only
-        * applicable to UHS-I cards. So reset these fields to their initial
-        * value when card is removed.
-        */
-       if (host->flags & SDHCI_USING_RETUNING_TIMER) {
-               host->flags &= ~SDHCI_USING_RETUNING_TIMER;
-
-               del_timer_sync(&host->tuning_timer);
-               host->flags &= ~SDHCI_NEEDS_RETUNING;
-       }
        sdhci_enable_card_detection(host);
 }
 
@@ -328,8 +316,7 @@ static void sdhci_read_block_pio(struct sdhci_host *host)
        local_irq_save(flags);
 
        while (blksize) {
-               if (!sg_miter_next(&host->sg_miter))
-                       BUG();
+               BUG_ON(!sg_miter_next(&host->sg_miter));
 
                len = min(host->sg_miter.length, blksize);
 
@@ -374,8 +361,7 @@ static void sdhci_write_block_pio(struct sdhci_host *host)
        local_irq_save(flags);
 
        while (blksize) {
-               if (!sg_miter_next(&host->sg_miter))
-                       BUG();
+               BUG_ON(!sg_miter_next(&host->sg_miter));
 
                len = min(host->sg_miter.length, blksize);
 
@@ -848,7 +834,7 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd)
                        int sg_cnt;
 
                        sg_cnt = sdhci_pre_dma_transfer(host, data, NULL);
-                       if (sg_cnt == 0) {
+                       if (sg_cnt <= 0) {
                                /*
                                 * This only happens when someone fed
                                 * us an invalid request.
@@ -1353,7 +1339,6 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
        struct sdhci_host *host;
        int present;
        unsigned long flags;
-       u32 tuning_opcode;
 
        host = mmc_priv(mmc);
 
@@ -1387,39 +1372,6 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
                host->mrq->cmd->error = -ENOMEDIUM;
                tasklet_schedule(&host->finish_tasklet);
        } else {
-               u32 present_state;
-
-               present_state = sdhci_readl(host, SDHCI_PRESENT_STATE);
-               /*
-                * Check if the re-tuning timer has already expired and there
-                * is no on-going data transfer and DAT0 is not busy. If so,
-                * we need to execute tuning procedure before sending command.
-                */
-               if ((host->flags & SDHCI_NEEDS_RETUNING) &&
-                   !(present_state & (SDHCI_DOING_WRITE | SDHCI_DOING_READ)) &&
-                   (present_state & SDHCI_DATA_0_LVL_MASK)) {
-                       if (mmc->card) {
-                               /* eMMC uses cmd21 but sd and sdio use cmd19 */
-                               tuning_opcode =
-                                       mmc->card->type == MMC_TYPE_MMC ?
-                                       MMC_SEND_TUNING_BLOCK_HS200 :
-                                       MMC_SEND_TUNING_BLOCK;
-
-                               /* Here we need to set the host->mrq to NULL,
-                                * in case the pending finish_tasklet
-                                * finishes it incorrectly.
-                                */
-                               host->mrq = NULL;
-
-                               spin_unlock_irqrestore(&host->lock, flags);
-                               sdhci_execute_tuning(mmc, tuning_opcode);
-                               spin_lock_irqsave(&host->lock, flags);
-
-                               /* Restore original mmc_request structure */
-                               host->mrq = mrq;
-                       }
-               }
-
                if (mrq->sbc && !(host->flags & SDHCI_AUTO_CMD23))
                        sdhci_send_command(host, mrq->sbc);
                else
@@ -1562,8 +1514,17 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios)
                        ctrl_2 &= ~SDHCI_CTRL_DRV_TYPE_MASK;
                        if (ios->drv_type == MMC_SET_DRIVER_TYPE_A)
                                ctrl_2 |= SDHCI_CTRL_DRV_TYPE_A;
+                       else if (ios->drv_type == MMC_SET_DRIVER_TYPE_B)
+                               ctrl_2 |= SDHCI_CTRL_DRV_TYPE_B;
                        else if (ios->drv_type == MMC_SET_DRIVER_TYPE_C)
                                ctrl_2 |= SDHCI_CTRL_DRV_TYPE_C;
+                       else if (ios->drv_type == MMC_SET_DRIVER_TYPE_D)
+                               ctrl_2 |= SDHCI_CTRL_DRV_TYPE_D;
+                       else {
+                               pr_warn("%s: invalid driver type, default to "
+                                       "driver type B\n", mmc_hostname(mmc));
+                               ctrl_2 |= SDHCI_CTRL_DRV_TYPE_B;
+                       }
 
                        sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
                } else {
@@ -2065,23 +2026,18 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
        }
 
 out:
-       host->flags &= ~SDHCI_NEEDS_RETUNING;
-
        if (tuning_count) {
-               host->flags |= SDHCI_USING_RETUNING_TIMER;
-               mod_timer(&host->tuning_timer, jiffies + tuning_count * HZ);
+               /*
+                * In case tuning fails, host controllers which support
+                * re-tuning can try tuning again at a later time, when the
+                * re-tuning timer expires.  So for these controllers, we
+                * return 0. Since there might be other controllers who do not
+                * have this capability, we return error for them.
+                */
+               err = 0;
        }
 
-       /*
-        * In case tuning fails, host controllers which support re-tuning can
-        * try tuning again at a later time, when the re-tuning timer expires.
-        * So for these controllers, we return 0. Since there might be other
-        * controllers who do not have this capability, we return error for
-        * them. SDHCI_USING_RETUNING_TIMER means the host is currently using
-        * a retuning timer to do the retuning for the card.
-        */
-       if (err && (host->flags & SDHCI_USING_RETUNING_TIMER))
-               err = 0;
+       host->mmc->retune_period = err ? 0 : tuning_count;
 
        sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
        sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
@@ -2092,6 +2048,18 @@ out_unlock:
        return err;
 }
 
+static int sdhci_select_drive_strength(struct mmc_card *card,
+                                      unsigned int max_dtr, int host_drv,
+                                      int card_drv, int *drv_type)
+{
+       struct sdhci_host *host = mmc_priv(card->host);
+
+       if (!host->ops->select_drive_strength)
+               return 0;
+
+       return host->ops->select_drive_strength(host, card, max_dtr, host_drv,
+                                               card_drv, drv_type);
+}
 
 static void sdhci_enable_preset_value(struct sdhci_host *host, bool enable)
 {
@@ -2236,6 +2204,7 @@ static const struct mmc_host_ops sdhci_ops = {
        .start_signal_voltage_switch    = sdhci_start_signal_voltage_switch,
        .prepare_hs400_tuning           = sdhci_prepare_hs400_tuning,
        .execute_tuning                 = sdhci_execute_tuning,
+       .select_drive_strength          = sdhci_select_drive_strength,
        .card_event                     = sdhci_card_event,
        .card_busy      = sdhci_card_busy,
 };
@@ -2337,20 +2306,6 @@ static void sdhci_timeout_timer(unsigned long data)
        spin_unlock_irqrestore(&host->lock, flags);
 }
 
-static void sdhci_tuning_timer(unsigned long data)
-{
-       struct sdhci_host *host;
-       unsigned long flags;
-
-       host = (struct sdhci_host *)data;
-
-       spin_lock_irqsave(&host->lock, flags);
-
-       host->flags |= SDHCI_NEEDS_RETUNING;
-
-       spin_unlock_irqrestore(&host->lock, flags);
-}
-
 /*****************************************************************************\
  *                                                                           *
  * Interrupt handling                                                        *
@@ -2728,11 +2683,8 @@ int sdhci_suspend_host(struct sdhci_host *host)
 {
        sdhci_disable_card_detection(host);
 
-       /* Disable tuning since we are suspending */
-       if (host->flags & SDHCI_USING_RETUNING_TIMER) {
-               del_timer_sync(&host->tuning_timer);
-               host->flags &= ~SDHCI_NEEDS_RETUNING;
-       }
+       mmc_retune_timer_stop(host->mmc);
+       mmc_retune_needed(host->mmc);
 
        if (!device_may_wakeup(mmc_dev(host->mmc))) {
                host->ier = 0;
@@ -2782,10 +2734,6 @@ int sdhci_resume_host(struct sdhci_host *host)
 
        sdhci_enable_card_detection(host);
 
-       /* Set the re-tuning expiration flag */
-       if (host->flags & SDHCI_USING_RETUNING_TIMER)
-               host->flags |= SDHCI_NEEDS_RETUNING;
-
        return ret;
 }
 
@@ -2822,11 +2770,8 @@ int sdhci_runtime_suspend_host(struct sdhci_host *host)
 {
        unsigned long flags;
 
-       /* Disable tuning since we are suspending */
-       if (host->flags & SDHCI_USING_RETUNING_TIMER) {
-               del_timer_sync(&host->tuning_timer);
-               host->flags &= ~SDHCI_NEEDS_RETUNING;
-       }
+       mmc_retune_timer_stop(host->mmc);
+       mmc_retune_needed(host->mmc);
 
        spin_lock_irqsave(&host->lock, flags);
        host->ier &= SDHCI_INT_CARD_INT;
@@ -2869,10 +2814,6 @@ int sdhci_runtime_resume_host(struct sdhci_host *host)
                spin_unlock_irqrestore(&host->lock, flags);
        }
 
-       /* Set the re-tuning expiration flag */
-       if (host->flags & SDHCI_USING_RETUNING_TIMER)
-               host->flags |= SDHCI_NEEDS_RETUNING;
-
        spin_lock_irqsave(&host->lock, flags);
 
        host->runtime_suspended = false;
@@ -3315,13 +3256,14 @@ int sdhci_add_host(struct sdhci_host *host)
                                   SDHCI_MAX_CURRENT_MULTIPLIER;
        }
 
-       /* If OCR set by external regulators, use it instead */
+       /* If OCR set by host, use it instead. */
+       if (host->ocr_mask)
+               ocr_avail = host->ocr_mask;
+
+       /* If OCR set by external regulators, give it highest prio. */
        if (mmc->ocr_avail)
                ocr_avail = mmc->ocr_avail;
 
-       if (host->ocr_mask)
-               ocr_avail &= host->ocr_mask;
-
        mmc->ocr_avail = ocr_avail;
        mmc->ocr_avail_sdio = ocr_avail;
        if (host->ocr_avail_sdio)
@@ -3408,13 +3350,6 @@ int sdhci_add_host(struct sdhci_host *host)
 
        init_waitqueue_head(&host->buf_ready_int);
 
-       if (host->version >= SDHCI_SPEC_300) {
-               /* Initialize re-tuning timer */
-               init_timer(&host->tuning_timer);
-               host->tuning_timer.data = (unsigned long)host;
-               host->tuning_timer.function = sdhci_tuning_timer;
-       }
-
        sdhci_init(host, 0);
 
        ret = request_threaded_irq(host->irq, sdhci_irq, sdhci_thread_irq,
index e639b7f435e564f6f8a56dc22a6747d23939c8f2..5521d29368e466d0d752839e34210a452823d62f 100644 (file)
@@ -432,13 +432,11 @@ struct sdhci_host {
 #define SDHCI_REQ_USE_DMA      (1<<2)  /* Use DMA for this req. */
 #define SDHCI_DEVICE_DEAD      (1<<3)  /* Device unresponsive */
 #define SDHCI_SDR50_NEEDS_TUNING (1<<4)        /* SDR50 needs tuning */
-#define SDHCI_NEEDS_RETUNING   (1<<5)  /* Host needs retuning */
 #define SDHCI_AUTO_CMD12       (1<<6)  /* Auto CMD12 support */
 #define SDHCI_AUTO_CMD23       (1<<7)  /* Auto CMD23 support */
 #define SDHCI_PV_ENABLED       (1<<8)  /* Preset value enabled */
 #define SDHCI_SDIO_IRQ_ENABLED (1<<9)  /* SDIO irq enabled */
 #define SDHCI_SDR104_NEEDS_TUNING (1<<10)      /* SDR104/HS200 needs tuning */
-#define SDHCI_USING_RETUNING_TIMER (1<<11)     /* Host is using a retuning timer for the card */
 #define SDHCI_USE_64_BIT_DMA   (1<<12) /* Use 64-bit DMA */
 #define SDHCI_HS400_TUNING     (1<<13) /* Tuning for HS400 */
 
@@ -504,7 +502,6 @@ struct sdhci_host {
        unsigned int            tuning_count;   /* Timer count for re-tuning */
        unsigned int            tuning_mode;    /* Re-tuning mode supported by host */
 #define SDHCI_TUNING_MODE_1    0
-       struct timer_list       tuning_timer;   /* Timer for tuning */
 
        struct sdhci_host_next  next_data;
        unsigned long private[0] ____cacheline_aligned;
@@ -541,6 +538,10 @@ struct sdhci_ops {
        void    (*platform_init)(struct sdhci_host *host);
        void    (*card_event)(struct sdhci_host *host);
        void    (*voltage_switch)(struct sdhci_host *host);
+       int     (*select_drive_strength)(struct sdhci_host *host,
+                                        struct mmc_card *card,
+                                        unsigned int max_dtr, int host_drv,
+                                        int card_drv, int *drv_type);
 };
 
 #ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS
index 2fe8b91481b3880fd3885834c25fd4b9f05b316b..983b8b32ef960057ea0f09ee309de5e7ae1e842c 100644 (file)
@@ -49,7 +49,7 @@ struct f_sdhost_priv {
        struct device *dev;
 };
 
-void sdhci_f_sdh30_soft_voltage_switch(struct sdhci_host *host)
+static void sdhci_f_sdh30_soft_voltage_switch(struct sdhci_host *host)
 {
        struct f_sdhost_priv *priv = sdhci_priv(host);
        u32 ctrl = 0;
@@ -77,12 +77,12 @@ void sdhci_f_sdh30_soft_voltage_switch(struct sdhci_host *host)
        sdhci_writel(host, ctrl, F_SDH30_TUNING_SETTING);
 }
 
-unsigned int sdhci_f_sdh30_get_min_clock(struct sdhci_host *host)
+static unsigned int sdhci_f_sdh30_get_min_clock(struct sdhci_host *host)
 {
        return F_SDH30_MIN_CLOCK;
 }
 
-void sdhci_f_sdh30_reset(struct sdhci_host *host, u8 mask)
+static void sdhci_f_sdh30_reset(struct sdhci_host *host, u8 mask)
 {
        if (sdhci_readw(host, SDHCI_CLOCK_CONTROL) == 0)
                sdhci_writew(host, 0xBC01, SDHCI_CLOCK_CONTROL);
@@ -114,8 +114,7 @@ static int sdhci_f_sdh30_probe(struct platform_device *pdev)
                return irq;
        }
 
-       host = sdhci_alloc_host(dev, sizeof(struct sdhci_host) +
-                                               sizeof(struct f_sdhost_priv));
+       host = sdhci_alloc_host(dev, sizeof(struct f_sdhost_priv));
        if (IS_ERR(host))
                return PTR_ERR(host);
 
index 7eff087cf515edfd1e4d51a68989197d82749744..5a1fdd405b1af14ff1725a8b9d9a3ab83e84b15a 100644 (file)
@@ -57,6 +57,7 @@
 #include <linux/mmc/slot-gpio.h>
 #include <linux/mod_devicetable.h>
 #include <linux/mutex.h>
+#include <linux/of_device.h>
 #include <linux/pagemap.h>
 #include <linux/platform_device.h>
 #include <linux/pm_qos.h>
 #define CLKDEV_MMC_DATA                20000000 /* 20MHz */
 #define CLKDEV_INIT            400000   /* 400 KHz */
 
-enum mmcif_state {
+enum sh_mmcif_state {
        STATE_IDLE,
        STATE_REQUEST,
        STATE_IOS,
        STATE_TIMEOUT,
 };
 
-enum mmcif_wait_for {
+enum sh_mmcif_wait_for {
        MMCIF_WAIT_FOR_REQUEST,
        MMCIF_WAIT_FOR_CMD,
        MMCIF_WAIT_FOR_MREAD,
@@ -224,12 +225,14 @@ enum mmcif_wait_for {
        MMCIF_WAIT_FOR_STOP,
 };
 
+/*
+ * difference for each SoC
+ */
 struct sh_mmcif_host {
        struct mmc_host *mmc;
        struct mmc_request *mrq;
        struct platform_device *pd;
-       struct clk *hclk;
-       unsigned int clk;
+       struct clk *clk;
        int bus_width;
        unsigned char timing;
        bool sd_error;
@@ -238,8 +241,8 @@ struct sh_mmcif_host {
        void __iomem *addr;
        u32 *pio_ptr;
        spinlock_t lock;                /* protect sh_mmcif_host::state */
-       enum mmcif_state state;
-       enum mmcif_wait_for wait_for;
+       enum sh_mmcif_state state;
+       enum sh_mmcif_wait_for wait_for;
        struct delayed_work timeout_work;
        size_t blocksize;
        int sg_idx;
@@ -249,6 +252,7 @@ struct sh_mmcif_host {
        bool ccs_enable;                /* Command Completion Signal support */
        bool clk_ctrl2_enable;
        struct mutex thread_lock;
+       u32 clkdiv_map;         /* see CE_CLK_CTRL::CLKDIV */
 
        /* DMA support */
        struct dma_chan         *chan_rx;
@@ -257,6 +261,14 @@ struct sh_mmcif_host {
        bool                    dma_active;
 };
 
+static const struct of_device_id sh_mmcif_of_match[] = {
+       { .compatible = "renesas,sh-mmcif" },
+       { }
+};
+MODULE_DEVICE_TABLE(of, sh_mmcif_of_match);
+
+#define sh_mmcif_host_to_dev(host) (&host->pd->dev)
+
 static inline void sh_mmcif_bitset(struct sh_mmcif_host *host,
                                        unsigned int reg, u32 val)
 {
@@ -269,15 +281,16 @@ static inline void sh_mmcif_bitclr(struct sh_mmcif_host *host,
        writel(~val & readl(host->addr + reg), host->addr + reg);
 }
 
-static void mmcif_dma_complete(void *arg)
+static void sh_mmcif_dma_complete(void *arg)
 {
        struct sh_mmcif_host *host = arg;
        struct mmc_request *mrq = host->mrq;
+       struct device *dev = sh_mmcif_host_to_dev(host);
 
-       dev_dbg(&host->pd->dev, "Command completed\n");
+       dev_dbg(dev, "Command completed\n");
 
        if (WARN(!mrq || !mrq->data, "%s: NULL data in DMA completion!\n",
-                dev_name(&host->pd->dev)))
+                dev_name(dev)))
                return;
 
        complete(&host->dma_complete);
@@ -289,6 +302,7 @@ static void sh_mmcif_start_dma_rx(struct sh_mmcif_host *host)
        struct scatterlist *sg = data->sg;
        struct dma_async_tx_descriptor *desc = NULL;
        struct dma_chan *chan = host->chan_rx;
+       struct device *dev = sh_mmcif_host_to_dev(host);
        dma_cookie_t cookie = -EINVAL;
        int ret;
 
@@ -301,13 +315,13 @@ static void sh_mmcif_start_dma_rx(struct sh_mmcif_host *host)
        }
 
        if (desc) {
-               desc->callback = mmcif_dma_complete;
+               desc->callback = sh_mmcif_dma_complete;
                desc->callback_param = host;
                cookie = dmaengine_submit(desc);
                sh_mmcif_bitset(host, MMCIF_CE_BUF_ACC, BUF_ACC_DMAREN);
                dma_async_issue_pending(chan);
        }
-       dev_dbg(&host->pd->dev, "%s(): mapped %d -> %d, cookie %d\n",
+       dev_dbg(dev, "%s(): mapped %d -> %d, cookie %d\n",
                __func__, data->sg_len, ret, cookie);
 
        if (!desc) {
@@ -323,12 +337,12 @@ static void sh_mmcif_start_dma_rx(struct sh_mmcif_host *host)
                        host->chan_tx = NULL;
                        dma_release_channel(chan);
                }
-               dev_warn(&host->pd->dev,
+               dev_warn(dev,
                         "DMA failed: %d, falling back to PIO\n", ret);
                sh_mmcif_bitclr(host, MMCIF_CE_BUF_ACC, BUF_ACC_DMAREN | BUF_ACC_DMAWEN);
        }
 
-       dev_dbg(&host->pd->dev, "%s(): desc %p, cookie %d, sg[%d]\n", __func__,
+       dev_dbg(dev, "%s(): desc %p, cookie %d, sg[%d]\n", __func__,
                desc, cookie, data->sg_len);
 }
 
@@ -338,6 +352,7 @@ static void sh_mmcif_start_dma_tx(struct sh_mmcif_host *host)
        struct scatterlist *sg = data->sg;
        struct dma_async_tx_descriptor *desc = NULL;
        struct dma_chan *chan = host->chan_tx;
+       struct device *dev = sh_mmcif_host_to_dev(host);
        dma_cookie_t cookie = -EINVAL;
        int ret;
 
@@ -350,13 +365,13 @@ static void sh_mmcif_start_dma_tx(struct sh_mmcif_host *host)
        }
 
        if (desc) {
-               desc->callback = mmcif_dma_complete;
+               desc->callback = sh_mmcif_dma_complete;
                desc->callback_param = host;
                cookie = dmaengine_submit(desc);
                sh_mmcif_bitset(host, MMCIF_CE_BUF_ACC, BUF_ACC_DMAWEN);
                dma_async_issue_pending(chan);
        }
-       dev_dbg(&host->pd->dev, "%s(): mapped %d -> %d, cookie %d\n",
+       dev_dbg(dev, "%s(): mapped %d -> %d, cookie %d\n",
                __func__, data->sg_len, ret, cookie);
 
        if (!desc) {
@@ -372,12 +387,12 @@ static void sh_mmcif_start_dma_tx(struct sh_mmcif_host *host)
                        host->chan_rx = NULL;
                        dma_release_channel(chan);
                }
-               dev_warn(&host->pd->dev,
+               dev_warn(dev,
                         "DMA failed: %d, falling back to PIO\n", ret);
                sh_mmcif_bitclr(host, MMCIF_CE_BUF_ACC, BUF_ACC_DMAREN | BUF_ACC_DMAWEN);
        }
 
-       dev_dbg(&host->pd->dev, "%s(): desc %p, cookie %d\n", __func__,
+       dev_dbg(dev, "%s(): desc %p, cookie %d\n", __func__,
                desc, cookie);
 }
 
@@ -390,6 +405,7 @@ sh_mmcif_request_dma_one(struct sh_mmcif_host *host,
        struct dma_chan *chan;
        void *slave_data = NULL;
        struct resource *res;
+       struct device *dev = sh_mmcif_host_to_dev(host);
        dma_cap_mask_t mask;
        int ret;
 
@@ -402,10 +418,10 @@ sh_mmcif_request_dma_one(struct sh_mmcif_host *host,
                        (void *)pdata->slave_id_rx;
 
        chan = dma_request_slave_channel_compat(mask, shdma_chan_filter,
-                               slave_data, &host->pd->dev,
+                               slave_data, dev,
                                direction == DMA_MEM_TO_DEV ? "tx" : "rx");
 
-       dev_dbg(&host->pd->dev, "%s: %s: got channel %p\n", __func__,
+       dev_dbg(dev, "%s: %s: got channel %p\n", __func__,
                direction == DMA_MEM_TO_DEV ? "TX" : "RX", chan);
 
        if (!chan)
@@ -435,12 +451,13 @@ sh_mmcif_request_dma_one(struct sh_mmcif_host *host,
 static void sh_mmcif_request_dma(struct sh_mmcif_host *host,
                                 struct sh_mmcif_plat_data *pdata)
 {
+       struct device *dev = sh_mmcif_host_to_dev(host);
        host->dma_active = false;
 
        if (pdata) {
                if (pdata->slave_id_tx <= 0 || pdata->slave_id_rx <= 0)
                        return;
-       } else if (!host->pd->dev.of_node) {
+       } else if (!dev->of_node) {
                return;
        }
 
@@ -476,21 +493,59 @@ static void sh_mmcif_release_dma(struct sh_mmcif_host *host)
 
 static void sh_mmcif_clock_control(struct sh_mmcif_host *host, unsigned int clk)
 {
-       struct sh_mmcif_plat_data *p = host->pd->dev.platform_data;
+       struct device *dev = sh_mmcif_host_to_dev(host);
+       struct sh_mmcif_plat_data *p = dev->platform_data;
        bool sup_pclk = p ? p->sup_pclk : false;
+       unsigned int current_clk = clk_get_rate(host->clk);
+       unsigned int clkdiv;
 
        sh_mmcif_bitclr(host, MMCIF_CE_CLK_CTRL, CLK_ENABLE);
        sh_mmcif_bitclr(host, MMCIF_CE_CLK_CTRL, CLK_CLEAR);
 
        if (!clk)
                return;
-       if (sup_pclk && clk == host->clk)
-               sh_mmcif_bitset(host, MMCIF_CE_CLK_CTRL, CLK_SUP_PCLK);
-       else
-               sh_mmcif_bitset(host, MMCIF_CE_CLK_CTRL, CLK_CLEAR &
-                               ((fls(DIV_ROUND_UP(host->clk,
-                                                  clk) - 1) - 1) << 16));
 
+       if (host->clkdiv_map) {
+               unsigned int freq, best_freq, myclk, div, diff_min, diff;
+               int i;
+
+               clkdiv = 0;
+               diff_min = ~0;
+               best_freq = 0;
+               for (i = 31; i >= 0; i--) {
+                       if (!((1 << i) & host->clkdiv_map))
+                               continue;
+
+                       /*
+                        * clk = parent_freq / div
+                        * -> parent_freq = clk x div
+                        */
+
+                       div = 1 << (i + 1);
+                       freq = clk_round_rate(host->clk, clk * div);
+                       myclk = freq / div;
+                       diff = (myclk > clk) ? myclk - clk : clk - myclk;
+
+                       if (diff <= diff_min) {
+                               best_freq = freq;
+                               clkdiv = i;
+                               diff_min = diff;
+                       }
+               }
+
+               dev_dbg(dev, "clk %u/%u (%u, 0x%x)\n",
+                       (best_freq / (1 << (clkdiv + 1))), clk,
+                       best_freq, clkdiv);
+
+               clk_set_rate(host->clk, best_freq);
+               clkdiv = clkdiv << 16;
+       } else if (sup_pclk && clk == current_clk) {
+               clkdiv = CLK_SUP_PCLK;
+       } else {
+               clkdiv = (fls(DIV_ROUND_UP(current_clk, clk) - 1) - 1) << 16;
+       }
+
+       sh_mmcif_bitset(host, MMCIF_CE_CLK_CTRL, CLK_CLEAR & clkdiv);
        sh_mmcif_bitset(host, MMCIF_CE_CLK_CTRL, CLK_ENABLE);
 }
 
@@ -514,6 +569,7 @@ static void sh_mmcif_sync_reset(struct sh_mmcif_host *host)
 
 static int sh_mmcif_error_manage(struct sh_mmcif_host *host)
 {
+       struct device *dev = sh_mmcif_host_to_dev(host);
        u32 state1, state2;
        int ret, timeout;
 
@@ -521,8 +577,8 @@ static int sh_mmcif_error_manage(struct sh_mmcif_host *host)
 
        state1 = sh_mmcif_readl(host->addr, MMCIF_CE_HOST_STS1);
        state2 = sh_mmcif_readl(host->addr, MMCIF_CE_HOST_STS2);
-       dev_dbg(&host->pd->dev, "ERR HOST_STS1 = %08x\n", state1);
-       dev_dbg(&host->pd->dev, "ERR HOST_STS2 = %08x\n", state2);
+       dev_dbg(dev, "ERR HOST_STS1 = %08x\n", state1);
+       dev_dbg(dev, "ERR HOST_STS2 = %08x\n", state2);
 
        if (state1 & STS1_CMDSEQ) {
                sh_mmcif_bitset(host, MMCIF_CE_CMD_CTRL, CMD_CTRL_BREAK);
@@ -534,25 +590,25 @@ static int sh_mmcif_error_manage(struct sh_mmcif_host *host)
                        mdelay(1);
                }
                if (!timeout) {
-                       dev_err(&host->pd->dev,
+                       dev_err(dev,
                                "Forced end of command sequence timeout err\n");
                        return -EIO;
                }
                sh_mmcif_sync_reset(host);
-               dev_dbg(&host->pd->dev, "Forced end of command sequence\n");
+               dev_dbg(dev, "Forced end of command sequence\n");
                return -EIO;
        }
 
        if (state2 & STS2_CRC_ERR) {
-               dev_err(&host->pd->dev, " CRC error: state %u, wait %u\n",
+               dev_err(dev, " CRC error: state %u, wait %u\n",
                        host->state, host->wait_for);
                ret = -EIO;
        } else if (state2 & STS2_TIMEOUT_ERR) {
-               dev_err(&host->pd->dev, " Timeout: state %u, wait %u\n",
+               dev_err(dev, " Timeout: state %u, wait %u\n",
                        host->state, host->wait_for);
                ret = -ETIMEDOUT;
        } else {
-               dev_dbg(&host->pd->dev, " End/Index error: state %u, wait %u\n",
+               dev_dbg(dev, " End/Index error: state %u, wait %u\n",
                        host->state, host->wait_for);
                ret = -EIO;
        }
@@ -593,13 +649,14 @@ static void sh_mmcif_single_read(struct sh_mmcif_host *host,
 
 static bool sh_mmcif_read_block(struct sh_mmcif_host *host)
 {
+       struct device *dev = sh_mmcif_host_to_dev(host);
        struct mmc_data *data = host->mrq->data;
        u32 *p = sg_virt(data->sg);
        int i;
 
        if (host->sd_error) {
                data->error = sh_mmcif_error_manage(host);
-               dev_dbg(&host->pd->dev, "%s(): %d\n", __func__, data->error);
+               dev_dbg(dev, "%s(): %d\n", __func__, data->error);
                return false;
        }
 
@@ -634,13 +691,14 @@ static void sh_mmcif_multi_read(struct sh_mmcif_host *host,
 
 static bool sh_mmcif_mread_block(struct sh_mmcif_host *host)
 {
+       struct device *dev = sh_mmcif_host_to_dev(host);
        struct mmc_data *data = host->mrq->data;
        u32 *p = host->pio_ptr;
        int i;
 
        if (host->sd_error) {
                data->error = sh_mmcif_error_manage(host);
-               dev_dbg(&host->pd->dev, "%s(): %d\n", __func__, data->error);
+               dev_dbg(dev, "%s(): %d\n", __func__, data->error);
                return false;
        }
 
@@ -671,13 +729,14 @@ static void sh_mmcif_single_write(struct sh_mmcif_host *host,
 
 static bool sh_mmcif_write_block(struct sh_mmcif_host *host)
 {
+       struct device *dev = sh_mmcif_host_to_dev(host);
        struct mmc_data *data = host->mrq->data;
        u32 *p = sg_virt(data->sg);
        int i;
 
        if (host->sd_error) {
                data->error = sh_mmcif_error_manage(host);
-               dev_dbg(&host->pd->dev, "%s(): %d\n", __func__, data->error);
+               dev_dbg(dev, "%s(): %d\n", __func__, data->error);
                return false;
        }
 
@@ -712,13 +771,14 @@ static void sh_mmcif_multi_write(struct sh_mmcif_host *host,
 
 static bool sh_mmcif_mwrite_block(struct sh_mmcif_host *host)
 {
+       struct device *dev = sh_mmcif_host_to_dev(host);
        struct mmc_data *data = host->mrq->data;
        u32 *p = host->pio_ptr;
        int i;
 
        if (host->sd_error) {
                data->error = sh_mmcif_error_manage(host);
-               dev_dbg(&host->pd->dev, "%s(): %d\n", __func__, data->error);
+               dev_dbg(dev, "%s(): %d\n", __func__, data->error);
                return false;
        }
 
@@ -756,6 +816,7 @@ static void sh_mmcif_get_cmd12response(struct sh_mmcif_host *host,
 static u32 sh_mmcif_set_cmd(struct sh_mmcif_host *host,
                            struct mmc_request *mrq)
 {
+       struct device *dev = sh_mmcif_host_to_dev(host);
        struct mmc_data *data = mrq->data;
        struct mmc_command *cmd = mrq->cmd;
        u32 opc = cmd->opcode;
@@ -775,7 +836,7 @@ static u32 sh_mmcif_set_cmd(struct sh_mmcif_host *host,
                tmp |= CMD_SET_RTYP_17B;
                break;
        default:
-               dev_err(&host->pd->dev, "Unsupported response type.\n");
+               dev_err(dev, "Unsupported response type.\n");
                break;
        }
        switch (opc) {
@@ -803,7 +864,7 @@ static u32 sh_mmcif_set_cmd(struct sh_mmcif_host *host,
                        tmp |= CMD_SET_DATW_8;
                        break;
                default:
-                       dev_err(&host->pd->dev, "Unsupported bus width.\n");
+                       dev_err(dev, "Unsupported bus width.\n");
                        break;
                }
                switch (host->timing) {
@@ -846,6 +907,8 @@ static u32 sh_mmcif_set_cmd(struct sh_mmcif_host *host,
 static int sh_mmcif_data_trans(struct sh_mmcif_host *host,
                               struct mmc_request *mrq, u32 opc)
 {
+       struct device *dev = sh_mmcif_host_to_dev(host);
+
        switch (opc) {
        case MMC_READ_MULTIPLE_BLOCK:
                sh_mmcif_multi_read(host, mrq);
@@ -861,7 +924,7 @@ static int sh_mmcif_data_trans(struct sh_mmcif_host *host,
                sh_mmcif_single_read(host, mrq);
                return 0;
        default:
-               dev_err(&host->pd->dev, "Unsupported CMD%d\n", opc);
+               dev_err(dev, "Unsupported CMD%d\n", opc);
                return -EINVAL;
        }
 }
@@ -918,6 +981,8 @@ static void sh_mmcif_start_cmd(struct sh_mmcif_host *host,
 static void sh_mmcif_stop_cmd(struct sh_mmcif_host *host,
                              struct mmc_request *mrq)
 {
+       struct device *dev = sh_mmcif_host_to_dev(host);
+
        switch (mrq->cmd->opcode) {
        case MMC_READ_MULTIPLE_BLOCK:
                sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MCMD12DRE);
@@ -926,7 +991,7 @@ static void sh_mmcif_stop_cmd(struct sh_mmcif_host *host,
                sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MCMD12RBE);
                break;
        default:
-               dev_err(&host->pd->dev, "unsupported stop cmd\n");
+               dev_err(dev, "unsupported stop cmd\n");
                mrq->stop->error = sh_mmcif_error_manage(host);
                return;
        }
@@ -937,11 +1002,13 @@ static void sh_mmcif_stop_cmd(struct sh_mmcif_host *host,
 static void sh_mmcif_request(struct mmc_host *mmc, struct mmc_request *mrq)
 {
        struct sh_mmcif_host *host = mmc_priv(mmc);
+       struct device *dev = sh_mmcif_host_to_dev(host);
        unsigned long flags;
 
        spin_lock_irqsave(&host->lock, flags);
        if (host->state != STATE_IDLE) {
-               dev_dbg(&host->pd->dev, "%s() rejected, state %u\n", __func__, host->state);
+               dev_dbg(dev, "%s() rejected, state %u\n",
+                       __func__, host->state);
                spin_unlock_irqrestore(&host->lock, flags);
                mrq->cmd->error = -EAGAIN;
                mmc_request_done(mmc, mrq);
@@ -972,17 +1039,37 @@ static void sh_mmcif_request(struct mmc_host *mmc, struct mmc_request *mrq)
        sh_mmcif_start_cmd(host, mrq);
 }
 
-static int sh_mmcif_clk_update(struct sh_mmcif_host *host)
+static void sh_mmcif_clk_setup(struct sh_mmcif_host *host)
 {
-       int ret = clk_prepare_enable(host->hclk);
+       struct device *dev = sh_mmcif_host_to_dev(host);
+
+       if (host->mmc->f_max) {
+               unsigned int f_max, f_min = 0, f_min_old;
+
+               f_max = host->mmc->f_max;
+               for (f_min_old = f_max; f_min_old > 2;) {
+                       f_min = clk_round_rate(host->clk, f_min_old / 2);
+                       if (f_min == f_min_old)
+                               break;
+                       f_min_old = f_min;
+               }
+
+               /*
+                * This driver assumes this SoC is R-Car Gen2 or later
+                */
+               host->clkdiv_map = 0x3ff;
+
+               host->mmc->f_max = f_max / (1 << ffs(host->clkdiv_map));
+               host->mmc->f_min = f_min / (1 << fls(host->clkdiv_map));
+       } else {
+               unsigned int clk = clk_get_rate(host->clk);
 
-       if (!ret) {
-               host->clk = clk_get_rate(host->hclk);
-               host->mmc->f_max = host->clk / 2;
-               host->mmc->f_min = host->clk / 512;
+               host->mmc->f_max = clk / 2;
+               host->mmc->f_min = clk / 512;
        }
 
-       return ret;
+       dev_dbg(dev, "clk max/min = %d/%d\n",
+               host->mmc->f_max, host->mmc->f_min);
 }
 
 static void sh_mmcif_set_power(struct sh_mmcif_host *host, struct mmc_ios *ios)
@@ -998,11 +1085,13 @@ static void sh_mmcif_set_power(struct sh_mmcif_host *host, struct mmc_ios *ios)
 static void sh_mmcif_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
 {
        struct sh_mmcif_host *host = mmc_priv(mmc);
+       struct device *dev = sh_mmcif_host_to_dev(host);
        unsigned long flags;
 
        spin_lock_irqsave(&host->lock, flags);
        if (host->state != STATE_IDLE) {
-               dev_dbg(&host->pd->dev, "%s() rejected, state %u\n", __func__, host->state);
+               dev_dbg(dev, "%s() rejected, state %u\n",
+                       __func__, host->state);
                spin_unlock_irqrestore(&host->lock, flags);
                return;
        }
@@ -1013,7 +1102,7 @@ static void sh_mmcif_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
        if (ios->power_mode == MMC_POWER_UP) {
                if (!host->card_present) {
                        /* See if we also get DMA */
-                       sh_mmcif_request_dma(host, host->pd->dev.platform_data);
+                       sh_mmcif_request_dma(host, dev->platform_data);
                        host->card_present = true;
                }
                sh_mmcif_set_power(host, ios);
@@ -1027,8 +1116,8 @@ static void sh_mmcif_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
                        }
                }
                if (host->power) {
-                       pm_runtime_put_sync(&host->pd->dev);
-                       clk_disable_unprepare(host->hclk);
+                       pm_runtime_put_sync(dev);
+                       clk_disable_unprepare(host->clk);
                        host->power = false;
                        if (ios->power_mode == MMC_POWER_OFF)
                                sh_mmcif_set_power(host, ios);
@@ -1039,8 +1128,9 @@ static void sh_mmcif_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
 
        if (ios->clock) {
                if (!host->power) {
-                       sh_mmcif_clk_update(host);
-                       pm_runtime_get_sync(&host->pd->dev);
+                       clk_prepare_enable(host->clk);
+
+                       pm_runtime_get_sync(dev);
                        host->power = true;
                        sh_mmcif_sync_reset(host);
                }
@@ -1055,7 +1145,8 @@ static void sh_mmcif_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
 static int sh_mmcif_get_cd(struct mmc_host *mmc)
 {
        struct sh_mmcif_host *host = mmc_priv(mmc);
-       struct sh_mmcif_plat_data *p = host->pd->dev.platform_data;
+       struct device *dev = sh_mmcif_host_to_dev(host);
+       struct sh_mmcif_plat_data *p = dev->platform_data;
        int ret = mmc_gpio_get_cd(mmc);
 
        if (ret >= 0)
@@ -1077,6 +1168,7 @@ static bool sh_mmcif_end_cmd(struct sh_mmcif_host *host)
 {
        struct mmc_command *cmd = host->mrq->cmd;
        struct mmc_data *data = host->mrq->data;
+       struct device *dev = sh_mmcif_host_to_dev(host);
        long time;
 
        if (host->sd_error) {
@@ -1090,7 +1182,7 @@ static bool sh_mmcif_end_cmd(struct sh_mmcif_host *host)
                        cmd->error = sh_mmcif_error_manage(host);
                        break;
                }
-               dev_dbg(&host->pd->dev, "CMD%d error %d\n",
+               dev_dbg(dev, "CMD%d error %d\n",
                        cmd->opcode, cmd->error);
                host->sd_error = false;
                return false;
@@ -1170,6 +1262,7 @@ static irqreturn_t sh_mmcif_irqt(int irq, void *dev_id)
 {
        struct sh_mmcif_host *host = dev_id;
        struct mmc_request *mrq;
+       struct device *dev = sh_mmcif_host_to_dev(host);
        bool wait = false;
        unsigned long flags;
        int wait_work;
@@ -1184,7 +1277,7 @@ static irqreturn_t sh_mmcif_irqt(int irq, void *dev_id)
 
        mrq = host->mrq;
        if (!mrq) {
-               dev_dbg(&host->pd->dev, "IRQ thread state %u, wait %u: NULL mrq!\n",
+               dev_dbg(dev, "IRQ thread state %u, wait %u: NULL mrq!\n",
                        host->state, host->wait_for);
                mutex_unlock(&host->thread_lock);
                return IRQ_HANDLED;
@@ -1222,7 +1315,7 @@ static irqreturn_t sh_mmcif_irqt(int irq, void *dev_id)
        case MMCIF_WAIT_FOR_STOP:
                if (host->sd_error) {
                        mrq->stop->error = sh_mmcif_error_manage(host);
-                       dev_dbg(&host->pd->dev, "%s(): %d\n", __func__, mrq->stop->error);
+                       dev_dbg(dev, "%s(): %d\n", __func__, mrq->stop->error);
                        break;
                }
                sh_mmcif_get_cmd12response(host, mrq->stop);
@@ -1232,7 +1325,7 @@ static irqreturn_t sh_mmcif_irqt(int irq, void *dev_id)
        case MMCIF_WAIT_FOR_WRITE_END:
                if (host->sd_error) {
                        mrq->data->error = sh_mmcif_error_manage(host);
-                       dev_dbg(&host->pd->dev, "%s(): %d\n", __func__, mrq->data->error);
+                       dev_dbg(dev, "%s(): %d\n", __func__, mrq->data->error);
                }
                break;
        default:
@@ -1275,6 +1368,7 @@ static irqreturn_t sh_mmcif_irqt(int irq, void *dev_id)
 static irqreturn_t sh_mmcif_intr(int irq, void *dev_id)
 {
        struct sh_mmcif_host *host = dev_id;
+       struct device *dev = sh_mmcif_host_to_dev(host);
        u32 state, mask;
 
        state = sh_mmcif_readl(host->addr, MMCIF_CE_INT);
@@ -1286,32 +1380,33 @@ static irqreturn_t sh_mmcif_intr(int irq, void *dev_id)
        sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, state & MASK_CLEAN);
 
        if (state & ~MASK_CLEAN)
-               dev_dbg(&host->pd->dev, "IRQ state = 0x%08x incompletely cleared\n",
+               dev_dbg(dev, "IRQ state = 0x%08x incompletely cleared\n",
                        state);
 
        if (state & INT_ERR_STS || state & ~INT_ALL) {
                host->sd_error = true;
-               dev_dbg(&host->pd->dev, "int err state = 0x%08x\n", state);
+               dev_dbg(dev, "int err state = 0x%08x\n", state);
        }
        if (state & ~(INT_CMD12RBE | INT_CMD12CRE)) {
                if (!host->mrq)
-                       dev_dbg(&host->pd->dev, "NULL IRQ state = 0x%08x\n", state);
+                       dev_dbg(dev, "NULL IRQ state = 0x%08x\n", state);
                if (!host->dma_active)
                        return IRQ_WAKE_THREAD;
                else if (host->sd_error)
-                       mmcif_dma_complete(host);
+                       sh_mmcif_dma_complete(host);
        } else {
-               dev_dbg(&host->pd->dev, "Unexpected IRQ 0x%x\n", state);
+               dev_dbg(dev, "Unexpected IRQ 0x%x\n", state);
        }
 
        return IRQ_HANDLED;
 }
 
-static void mmcif_timeout_work(struct work_struct *work)
+static void sh_mmcif_timeout_work(struct work_struct *work)
 {
        struct delayed_work *d = container_of(work, struct delayed_work, work);
        struct sh_mmcif_host *host = container_of(d, struct sh_mmcif_host, timeout_work);
        struct mmc_request *mrq = host->mrq;
+       struct device *dev = sh_mmcif_host_to_dev(host);
        unsigned long flags;
 
        if (host->dying)
@@ -1324,7 +1419,7 @@ static void mmcif_timeout_work(struct work_struct *work)
                return;
        }
 
-       dev_err(&host->pd->dev, "Timeout waiting for %u on CMD%u\n",
+       dev_err(dev, "Timeout waiting for %u on CMD%u\n",
                host->wait_for, mrq->cmd->opcode);
 
        host->state = STATE_TIMEOUT;
@@ -1361,7 +1456,8 @@ static void mmcif_timeout_work(struct work_struct *work)
 
 static void sh_mmcif_init_ocr(struct sh_mmcif_host *host)
 {
-       struct sh_mmcif_plat_data *pd = host->pd->dev.platform_data;
+       struct device *dev = sh_mmcif_host_to_dev(host);
+       struct sh_mmcif_plat_data *pd = dev->platform_data;
        struct mmc_host *mmc = host->mmc;
 
        mmc_regulator_get_supply(mmc);
@@ -1380,7 +1476,8 @@ static int sh_mmcif_probe(struct platform_device *pdev)
        int ret = 0, irq[2];
        struct mmc_host *mmc;
        struct sh_mmcif_host *host;
-       struct sh_mmcif_plat_data *pd = pdev->dev.platform_data;
+       struct device *dev = &pdev->dev;
+       struct sh_mmcif_plat_data *pd = dev->platform_data;
        struct resource *res;
        void __iomem *reg;
        const char *name;
@@ -1388,16 +1485,16 @@ static int sh_mmcif_probe(struct platform_device *pdev)
        irq[0] = platform_get_irq(pdev, 0);
        irq[1] = platform_get_irq(pdev, 1);
        if (irq[0] < 0) {
-               dev_err(&pdev->dev, "Get irq error\n");
+               dev_err(dev, "Get irq error\n");
                return -ENXIO;
        }
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       reg = devm_ioremap_resource(&pdev->dev, res);
+       reg = devm_ioremap_resource(dev, res);
        if (IS_ERR(reg))
                return PTR_ERR(reg);
 
-       mmc = mmc_alloc_host(sizeof(struct sh_mmcif_host), &pdev->dev);
+       mmc = mmc_alloc_host(sizeof(struct sh_mmcif_host), dev);
        if (!mmc)
                return -ENOMEM;
 
@@ -1430,41 +1527,44 @@ static int sh_mmcif_probe(struct platform_device *pdev)
 
        platform_set_drvdata(pdev, host);
 
-       pm_runtime_enable(&pdev->dev);
+       pm_runtime_enable(dev);
        host->power = false;
 
-       host->hclk = devm_clk_get(&pdev->dev, NULL);
-       if (IS_ERR(host->hclk)) {
-               ret = PTR_ERR(host->hclk);
-               dev_err(&pdev->dev, "cannot get clock: %d\n", ret);
+       host->clk = devm_clk_get(dev, NULL);
+       if (IS_ERR(host->clk)) {
+               ret = PTR_ERR(host->clk);
+               dev_err(dev, "cannot get clock: %d\n", ret);
                goto err_pm;
        }
-       ret = sh_mmcif_clk_update(host);
+
+       ret = clk_prepare_enable(host->clk);
        if (ret < 0)
                goto err_pm;
 
-       ret = pm_runtime_resume(&pdev->dev);
+       sh_mmcif_clk_setup(host);
+
+       ret = pm_runtime_resume(dev);
        if (ret < 0)
                goto err_clk;
 
-       INIT_DELAYED_WORK(&host->timeout_work, mmcif_timeout_work);
+       INIT_DELAYED_WORK(&host->timeout_work, sh_mmcif_timeout_work);
 
        sh_mmcif_sync_reset(host);
        sh_mmcif_writel(host->addr, MMCIF_CE_INT_MASK, MASK_ALL);
 
-       name = irq[1] < 0 ? dev_name(&pdev->dev) : "sh_mmc:error";
-       ret = devm_request_threaded_irq(&pdev->dev, irq[0], sh_mmcif_intr,
+       name = irq[1] < 0 ? dev_name(dev) : "sh_mmc:error";
+       ret = devm_request_threaded_irq(dev, irq[0], sh_mmcif_intr,
                                        sh_mmcif_irqt, 0, name, host);
        if (ret) {
-               dev_err(&pdev->dev, "request_irq error (%s)\n", name);
+               dev_err(dev, "request_irq error (%s)\n", name);
                goto err_clk;
        }
        if (irq[1] >= 0) {
-               ret = devm_request_threaded_irq(&pdev->dev, irq[1],
+               ret = devm_request_threaded_irq(dev, irq[1],
                                                sh_mmcif_intr, sh_mmcif_irqt,
                                                0, "sh_mmc:int", host);
                if (ret) {
-                       dev_err(&pdev->dev, "request_irq error (sh_mmc:int)\n");
+                       dev_err(dev, "request_irq error (sh_mmc:int)\n");
                        goto err_clk;
                }
        }
@@ -1481,19 +1581,19 @@ static int sh_mmcif_probe(struct platform_device *pdev)
        if (ret < 0)
                goto err_clk;
 
-       dev_pm_qos_expose_latency_limit(&pdev->dev, 100);
+       dev_pm_qos_expose_latency_limit(dev, 100);
 
-       dev_info(&pdev->dev, "Chip version 0x%04x, clock rate %luMHz\n",
+       dev_info(dev, "Chip version 0x%04x, clock rate %luMHz\n",
                 sh_mmcif_readl(host->addr, MMCIF_CE_VERSION) & 0xffff,
-                clk_get_rate(host->hclk) / 1000000UL);
+                clk_get_rate(host->clk) / 1000000UL);
 
-       clk_disable_unprepare(host->hclk);
+       clk_disable_unprepare(host->clk);
        return ret;
 
 err_clk:
-       clk_disable_unprepare(host->hclk);
+       clk_disable_unprepare(host->clk);
 err_pm:
-       pm_runtime_disable(&pdev->dev);
+       pm_runtime_disable(dev);
 err_host:
        mmc_free_host(mmc);
        return ret;
@@ -1504,7 +1604,7 @@ static int sh_mmcif_remove(struct platform_device *pdev)
        struct sh_mmcif_host *host = platform_get_drvdata(pdev);
 
        host->dying = true;
-       clk_prepare_enable(host->hclk);
+       clk_prepare_enable(host->clk);
        pm_runtime_get_sync(&pdev->dev);
 
        dev_pm_qos_hide_latency_limit(&pdev->dev);
@@ -1519,7 +1619,7 @@ static int sh_mmcif_remove(struct platform_device *pdev)
         */
        cancel_delayed_work_sync(&host->timeout_work);
 
-       clk_disable_unprepare(host->hclk);
+       clk_disable_unprepare(host->clk);
        mmc_free_host(host->mmc);
        pm_runtime_put_sync(&pdev->dev);
        pm_runtime_disable(&pdev->dev);
@@ -1543,12 +1643,6 @@ static int sh_mmcif_resume(struct device *dev)
 }
 #endif
 
-static const struct of_device_id mmcif_of_match[] = {
-       { .compatible = "renesas,sh-mmcif" },
-       { }
-};
-MODULE_DEVICE_TABLE(of, mmcif_of_match);
-
 static const struct dev_pm_ops sh_mmcif_dev_pm_ops = {
        SET_SYSTEM_SLEEP_PM_OPS(sh_mmcif_suspend, sh_mmcif_resume)
 };
@@ -1559,7 +1653,7 @@ static struct platform_driver sh_mmcif_driver = {
        .driver         = {
                .name   = DRIVER_NAME,
                .pm     = &sh_mmcif_dev_pm_ops,
-               .of_match_table = mmcif_of_match,
+               .of_match_table = sh_mmcif_of_match,
        },
 };
 
index f746df493892c160a9623b9687c17c81dadcd66e..e897e7fc3b14cd996e3a04a8f0a98e24ef2e8ee6 100644 (file)
@@ -85,8 +85,10 @@ static int tmio_mmc_probe(struct platform_device *pdev)
        }
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       if (!res)
-               return -EINVAL;
+       if (!res) {
+               ret = -EINVAL;
+               goto cell_disable;
+       }
 
        pdata->flags |= TMIO_MMC_HAVE_HIGH_REG;
 
@@ -101,7 +103,8 @@ static int tmio_mmc_probe(struct platform_device *pdev)
        if (ret)
                goto host_free;
 
-       ret = request_irq(irq, tmio_mmc_irq, IRQF_TRIGGER_FALLING,
+       ret = devm_request_irq(&pdev->dev, irq, tmio_mmc_irq,
+                               IRQF_TRIGGER_FALLING,
                                dev_name(&pdev->dev), host);
        if (ret)
                goto host_remove;
@@ -129,7 +132,6 @@ static int tmio_mmc_remove(struct platform_device *pdev)
 
        if (mmc) {
                struct tmio_mmc_host *host = mmc_priv(mmc);
-               free_irq(platform_get_irq(pdev, 0), host);
                tmio_mmc_host_remove(host);
                if (cell->disable)
                        cell->disable(pdev);
index dba7e1c19dd758e784f31c89afe7dafc66334a06..e3dcf31a8bd6a04e8abd174c964313db584fe1a1 100644 (file)
@@ -1108,7 +1108,8 @@ int tmio_mmc_host_probe(struct tmio_mmc_host *_host,
        if (ret < 0)
                goto host_free;
 
-       _host->ctl = ioremap(res_ctl->start, resource_size(res_ctl));
+       _host->ctl = devm_ioremap(&pdev->dev,
+                                 res_ctl->start, resource_size(res_ctl));
        if (!_host->ctl) {
                ret = -ENOMEM;
                goto host_free;
@@ -1230,8 +1231,6 @@ void tmio_mmc_host_remove(struct tmio_mmc_host *host)
 
        pm_runtime_put_sync(&pdev->dev);
        pm_runtime_disable(&pdev->dev);
-
-       iounmap(host->ctl);
 }
 EXPORT_SYMBOL(tmio_mmc_host_remove);
 
index 9f02c28c0204d1c6935551dbca443d3665eee31e..54479c481a7a815dbedc5cbdc3bc734039620925 100644 (file)
@@ -16,6 +16,7 @@ config MTD_CFI
 config MTD_JEDECPROBE
        tristate "Detect non-CFI AMD/JEDEC-compatible flash chips"
        select MTD_GEN_PROBE
+       select MTD_CFI_UTIL
        help
          This option enables JEDEC-style probing of flash chips which are not
          compatible with the Common Flash Interface, but will use the common
index c50d8cf0f60dd01b0b6c25822788b53dd2884bfd..c3624eb571d16f0067acc8137e8654f1bf829a37 100644 (file)
@@ -1295,7 +1295,7 @@ static int do_otp_write(struct map_info *map, struct flchip *chip, loff_t adr,
                unsigned long bus_ofs = adr & ~(map_bankwidth(map)-1);
                int gap = adr - bus_ofs;
                int n = min_t(int, len, map_bankwidth(map) - gap);
-               map_word datum;
+               map_word datum = map_word_ff(map);
 
                if (n != map_bankwidth(map)) {
                        /* partial write of a word, load old contents */
index 09c79bd0b4f4fbdfd16f87fab4cb5687be070209..6f16552cd59f48d4cb4aab5f6fb3a250190cb4c2 100644 (file)
 #include <linux/mtd/map.h>
 #include <linux/mtd/cfi.h>
 
+void cfi_udelay(int us)
+{
+       if (us >= 1000) {
+               msleep((us+999)/1000);
+       } else {
+               udelay(us);
+               cond_resched();
+       }
+}
+EXPORT_SYMBOL(cfi_udelay);
+
+/*
+ * Returns the command address according to the given geometry.
+ */
+uint32_t cfi_build_cmd_addr(uint32_t cmd_ofs,
+                               struct map_info *map, struct cfi_private *cfi)
+{
+       unsigned bankwidth = map_bankwidth(map);
+       unsigned interleave = cfi_interleave(cfi);
+       unsigned type = cfi->device_type;
+       uint32_t addr;
+
+       addr = (cmd_ofs * type) * interleave;
+
+       /* Modify the unlock address if we are in compatibility mode.
+        * For 16bit devices on 8 bit busses
+        * and 32bit devices on 16 bit busses
+        * set the low bit of the alternating bit sequence of the address.
+        */
+       if (((type * interleave) > bankwidth) && ((cmd_ofs & 0xff) == 0xaa))
+               addr |= (type >> 1)*interleave;
+
+       return  addr;
+}
+EXPORT_SYMBOL(cfi_build_cmd_addr);
+
+/*
+ * Transforms the CFI command for the given geometry (bus width & interleave).
+ * It looks too long to be inline, but in the common case it should almost all
+ * get optimised away.
+ */
+map_word cfi_build_cmd(u_long cmd, struct map_info *map, struct cfi_private *cfi)
+{
+       map_word val = { {0} };
+       int wordwidth, words_per_bus, chip_mode, chips_per_word;
+       unsigned long onecmd;
+       int i;
+
+       /* We do it this way to give the compiler a fighting chance
+          of optimising away all the crap for 'bankwidth' larger than
+          an unsigned long, in the common case where that support is
+          disabled */
+       if (map_bankwidth_is_large(map)) {
+               wordwidth = sizeof(unsigned long);
+               words_per_bus = (map_bankwidth(map)) / wordwidth; // i.e. normally 1
+       } else {
+               wordwidth = map_bankwidth(map);
+               words_per_bus = 1;
+       }
+
+       chip_mode = map_bankwidth(map) / cfi_interleave(cfi);
+       chips_per_word = wordwidth * cfi_interleave(cfi) / map_bankwidth(map);
+
+       /* First, determine what the bit-pattern should be for a single
+          device, according to chip mode and endianness... */
+       switch (chip_mode) {
+       default: BUG();
+       case 1:
+               onecmd = cmd;
+               break;
+       case 2:
+               onecmd = cpu_to_cfi16(map, cmd);
+               break;
+       case 4:
+               onecmd = cpu_to_cfi32(map, cmd);
+               break;
+       }
+
+       /* Now replicate it across the size of an unsigned long, or
+          just to the bus width as appropriate */
+       switch (chips_per_word) {
+       default: BUG();
+#if BITS_PER_LONG >= 64
+       case 8:
+               onecmd |= (onecmd << (chip_mode * 32));
+#endif
+       case 4:
+               onecmd |= (onecmd << (chip_mode * 16));
+       case 2:
+               onecmd |= (onecmd << (chip_mode * 8));
+       case 1:
+               ;
+       }
+
+       /* And finally, for the multi-word case, replicate it
+          in all words in the structure */
+       for (i=0; i < words_per_bus; i++) {
+               val.x[i] = onecmd;
+       }
+
+       return val;
+}
+EXPORT_SYMBOL(cfi_build_cmd);
+
+unsigned long cfi_merge_status(map_word val, struct map_info *map,
+                                          struct cfi_private *cfi)
+{
+       int wordwidth, words_per_bus, chip_mode, chips_per_word;
+       unsigned long onestat, res = 0;
+       int i;
+
+       /* We do it this way to give the compiler a fighting chance
+          of optimising away all the crap for 'bankwidth' larger than
+          an unsigned long, in the common case where that support is
+          disabled */
+       if (map_bankwidth_is_large(map)) {
+               wordwidth = sizeof(unsigned long);
+               words_per_bus = (map_bankwidth(map)) / wordwidth; // i.e. normally 1
+       } else {
+               wordwidth = map_bankwidth(map);
+               words_per_bus = 1;
+       }
+
+       chip_mode = map_bankwidth(map) / cfi_interleave(cfi);
+       chips_per_word = wordwidth * cfi_interleave(cfi) / map_bankwidth(map);
+
+       onestat = val.x[0];
+       /* Or all status words together */
+       for (i=1; i < words_per_bus; i++) {
+               onestat |= val.x[i];
+       }
+
+       res = onestat;
+       switch(chips_per_word) {
+       default: BUG();
+#if BITS_PER_LONG >= 64
+       case 8:
+               res |= (onestat >> (chip_mode * 32));
+#endif
+       case 4:
+               res |= (onestat >> (chip_mode * 16));
+       case 2:
+               res |= (onestat >> (chip_mode * 8));
+       case 1:
+               ;
+       }
+
+       /* Last, determine what the bit-pattern should be for a single
+          device, according to chip mode and endianness... */
+       switch (chip_mode) {
+       case 1:
+               break;
+       case 2:
+               res = cfi16_to_cpu(map, res);
+               break;
+       case 4:
+               res = cfi32_to_cpu(map, res);
+               break;
+       default: BUG();
+       }
+       return res;
+}
+EXPORT_SYMBOL(cfi_merge_status);
+
+/*
+ * Sends a CFI command to a bank of flash for the given geometry.
+ *
+ * Returns the offset in flash where the command was written.
+ * If prev_val is non-null, it will be set to the value at the command address,
+ * before the command was written.
+ */
+uint32_t cfi_send_gen_cmd(u_char cmd, uint32_t cmd_addr, uint32_t base,
+                               struct map_info *map, struct cfi_private *cfi,
+                               int type, map_word *prev_val)
+{
+       map_word val;
+       uint32_t addr = base + cfi_build_cmd_addr(cmd_addr, map, cfi);
+       val = cfi_build_cmd(cmd, map, cfi);
+
+       if (prev_val)
+               *prev_val = map_read(map, addr);
+
+       map_write(map, val, addr);
+
+       return addr - base;
+}
+EXPORT_SYMBOL(cfi_send_gen_cmd);
+
 int __xipram cfi_qry_present(struct map_info *map, __u32 base,
                             struct cfi_private *cfi)
 {
index c49d0b127fefb32c38539810247ff4adc5adc0fa..f73c41697a00e387f500e2f029cd6bc0299f2b57 100644 (file)
@@ -195,6 +195,14 @@ config MTD_BLOCK2MTD
          Testing MTD users (eg JFFS2) on large media and media that might
          be removed during a write (using the floppy drive).
 
+config MTD_POWERNV_FLASH
+       tristate "powernv flash MTD driver"
+       depends on PPC_POWERNV
+       help
+         This provides an MTD device to access flash on powernv OPAL
+         platforms from Linux. This device abstracts away the
+         firmware interface for flash access.
+
 comment "Disk-On-Chip Device Drivers"
 
 config MTD_DOCG3
index f0b0e611d1d6a564b460af1f68c965906f629223..7912d3a0ee343b045daf1dbdf9ae93061afad4eb 100644 (file)
@@ -16,6 +16,7 @@ obj-$(CONFIG_MTD_SPEAR_SMI)   += spear_smi.o
 obj-$(CONFIG_MTD_SST25L)       += sst25l.o
 obj-$(CONFIG_MTD_BCM47XXSFLASH)        += bcm47xxsflash.o
 obj-$(CONFIG_MTD_ST_SPI_FSM)    += st_spi_fsm.o
+obj-$(CONFIG_MTD_POWERNV_FLASH)        += powernv_flash.o
 
 
 CFLAGS_docg3.o                 += -I$(src)
index 866d319044750bda27e5679d36ff4b0e5d9d26aa..5e67b4acde780e648669c8de753776a89f99fff8 100644 (file)
@@ -1815,7 +1815,7 @@ static void doc_dbg_unregister(struct docg3 *docg3)
  * @chip_id: The chip ID of the supported chip
  * @mtd: The structure to fill
  */
-static void __init doc_set_driver_info(int chip_id, struct mtd_info *mtd)
+static int __init doc_set_driver_info(int chip_id, struct mtd_info *mtd)
 {
        struct docg3 *docg3 = mtd->priv;
        int cfg;
@@ -1828,6 +1828,8 @@ static void __init doc_set_driver_info(int chip_id, struct mtd_info *mtd)
        case DOC_CHIPID_G3:
                mtd->name = kasprintf(GFP_KERNEL, "docg3.%d",
                                      docg3->device_id);
+               if (!mtd->name)
+                       return -ENOMEM;
                docg3->max_block = 2047;
                break;
        }
@@ -1850,6 +1852,8 @@ static void __init doc_set_driver_info(int chip_id, struct mtd_info *mtd)
        mtd->_block_isbad = doc_block_isbad;
        mtd->ecclayout = &docg3_oobinfo;
        mtd->ecc_strength = DOC_ECC_BCH_T;
+
+       return 0;
 }
 
 /**
@@ -1900,7 +1904,7 @@ doc_probe_device(struct docg3_cascade *cascade, int floor, struct device *dev)
 
        ret = 0;
        if (chip_id != (u16)(~chip_id_inv)) {
-               goto nomem3;
+               goto nomem4;
        }
 
        switch (chip_id) {
@@ -1910,15 +1914,19 @@ doc_probe_device(struct docg3_cascade *cascade, int floor, struct device *dev)
                break;
        default:
                doc_err("Chip id %04x is not a DiskOnChip G3 chip\n", chip_id);
-               goto nomem3;
+               goto nomem4;
        }
 
-       doc_set_driver_info(chip_id, mtd);
+       ret = doc_set_driver_info(chip_id, mtd);
+       if (ret)
+               goto nomem4;
 
        doc_hamming_ecc_init(docg3, DOC_LAYOUT_OOB_PAGEINFO_SZ);
        doc_reload_bbt(docg3);
        return mtd;
 
+nomem4:
+       kfree(docg3->bbt);
 nomem3:
        kfree(mtd);
 nomem2:
@@ -2117,7 +2125,7 @@ static int docg3_release(struct platform_device *pdev)
 }
 
 #ifdef CONFIG_OF
-static struct of_device_id docg3_dt_ids[] = {
+static const struct of_device_id docg3_dt_ids[] = {
        { .compatible = "m-systems,diskonchip-g3" },
        {}
 };
index 3af137f49ac9aa49c0c58ad59977b53b9d81f358..d313f948b96c62f208f3a20372f34efff176b2a7 100644 (file)
@@ -261,45 +261,33 @@ static int m25p_remove(struct spi_device *spi)
  * keep them available as module aliases for existing platforms.
  */
 static const struct spi_device_id m25p_ids[] = {
-       {"at25fs010"},  {"at25fs040"},  {"at25df041a"}, {"at25df321a"},
-       {"at25df641"},  {"at26f004"},   {"at26df081a"}, {"at26df161a"},
-       {"at26df321"},  {"at45db081d"},
-       {"en25f32"},    {"en25p32"},    {"en25q32b"},   {"en25p64"},
-       {"en25q64"},    {"en25qh128"},  {"en25qh256"},
-       {"f25l32pa"},
-       {"mr25h256"},   {"mr25h10"},
-       {"gd25q32"},    {"gd25q64"},
-       {"160s33b"},    {"320s33b"},    {"640s33b"},
-       {"mx25l2005a"}, {"mx25l4005a"}, {"mx25l8005"},  {"mx25l1606e"},
-       {"mx25l3205d"}, {"mx25l3255e"}, {"mx25l6405d"}, {"mx25l12805d"},
-       {"mx25l12855e"},{"mx25l25635e"},{"mx25l25655e"},{"mx66l51235l"},
-       {"mx66l1g55g"},
-       {"n25q064"},    {"n25q128a11"}, {"n25q128a13"}, {"n25q256a"},
-       {"n25q512a"},   {"n25q512ax3"}, {"n25q00"},
-       {"pm25lv512"},  {"pm25lv010"},  {"pm25lq032"},
-       {"s25sl032p"},  {"s25sl064p"},  {"s25fl256s0"}, {"s25fl256s1"},
-       {"s25fl512s"},  {"s70fl01gs"},  {"s25sl12800"}, {"s25sl12801"},
-       {"s25fl129p0"}, {"s25fl129p1"}, {"s25sl004a"},  {"s25sl008a"},
-       {"s25sl016a"},  {"s25sl032a"},  {"s25sl064a"},  {"s25fl008k"},
-       {"s25fl016k"},  {"s25fl064k"},  {"s25fl132k"},
-       {"sst25vf040b"},{"sst25vf080b"},{"sst25vf016b"},{"sst25vf032b"},
-       {"sst25vf064c"},{"sst25wf512"}, {"sst25wf010"}, {"sst25wf020"},
-       {"sst25wf040"},
-       {"m25p05"},     {"m25p10"},     {"m25p20"},     {"m25p40"},
-       {"m25p80"},     {"m25p16"},     {"m25p32"},     {"m25p64"},
-       {"m25p128"},    {"n25q032"},
+       /*
+        * Entries not used in DTs that should be safe to drop after replacing
+        * them with "nor-jedec" in platform data.
+        */
+       {"s25sl064a"},  {"w25x16"},     {"m25p10"},     {"m25px64"},
+
+       /*
+        * Entries that were used in DTs without "nor-jedec" fallback and should
+        * be kept for backward compatibility.
+        */
+       {"at25df321a"}, {"at25df641"},  {"at26df081a"},
+       {"mr25h256"},
+       {"mx25l4005a"}, {"mx25l1606e"}, {"mx25l6405d"}, {"mx25l12805d"},
+       {"mx25l25635e"},{"mx66l51235l"},
+       {"n25q064"},    {"n25q128a11"}, {"n25q128a13"}, {"n25q512a"},
+       {"s25fl256s1"}, {"s25fl512s"},  {"s25sl12801"}, {"s25fl008k"},
+       {"s25fl064k"},
+       {"sst25vf040b"},{"sst25vf016b"},{"sst25vf032b"},{"sst25wf040"},
+       {"m25p40"},     {"m25p80"},     {"m25p16"},     {"m25p32"},
+       {"m25p64"},     {"m25p128"},
+       {"w25x80"},     {"w25x32"},     {"w25q32"},     {"w25q32dw"},
+       {"w25q80bl"},   {"w25q128"},    {"w25q256"},
+
+       /* Flashes that can't be detected using JEDEC */
        {"m25p05-nonjedec"},    {"m25p10-nonjedec"},    {"m25p20-nonjedec"},
        {"m25p40-nonjedec"},    {"m25p80-nonjedec"},    {"m25p16-nonjedec"},
        {"m25p32-nonjedec"},    {"m25p64-nonjedec"},    {"m25p128-nonjedec"},
-       {"m45pe10"},    {"m45pe80"},    {"m45pe16"},
-       {"m25pe20"},    {"m25pe80"},    {"m25pe16"},
-       {"m25px16"},    {"m25px32"},    {"m25px32-s0"}, {"m25px32-s1"},
-       {"m25px64"},    {"m25px80"},
-       {"w25x10"},     {"w25x20"},     {"w25x40"},     {"w25x80"},
-       {"w25x16"},     {"w25x32"},     {"w25q32"},     {"w25q32dw"},
-       {"w25x64"},     {"w25q64"},     {"w25q80"},     {"w25q80bl"},
-       {"w25q128"},    {"w25q256"},    {"cat25c11"},
-       {"cat25c03"},   {"cat25c09"},   {"cat25c17"},   {"cat25128"},
 
        /*
         * Generic support for SPI NOR that can be identified by the JEDEC READ
diff --git a/drivers/mtd/devices/powernv_flash.c b/drivers/mtd/devices/powernv_flash.c
new file mode 100644 (file)
index 0000000..d5b870b
--- /dev/null
@@ -0,0 +1,285 @@
+/*
+ * OPAL PNOR flash MTD abstraction
+ *
+ * Copyright IBM 2015
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+
+#include <asm/opal.h>
+
+
+/*
+ * This driver creates the a Linux MTD abstraction for platform PNOR flash
+ * backed by OPAL calls
+ */
+
+struct powernv_flash {
+       struct mtd_info mtd;
+       u32 id;
+};
+
+enum flash_op {
+       FLASH_OP_READ,
+       FLASH_OP_WRITE,
+       FLASH_OP_ERASE,
+};
+
+static int powernv_flash_async_op(struct mtd_info *mtd, enum flash_op op,
+               loff_t offset, size_t len, size_t *retlen, u_char *buf)
+{
+       struct powernv_flash *info = (struct powernv_flash *)mtd->priv;
+       struct device *dev = &mtd->dev;
+       int token;
+       struct opal_msg msg;
+       int rc;
+
+       dev_dbg(dev, "%s(op=%d, offset=0x%llx, len=%zu)\n",
+                       __func__, op, offset, len);
+
+       token = opal_async_get_token_interruptible();
+       if (token < 0) {
+               if (token != -ERESTARTSYS)
+                       dev_err(dev, "Failed to get an async token\n");
+
+               return token;
+       }
+
+       switch (op) {
+       case FLASH_OP_READ:
+               rc = opal_flash_read(info->id, offset, __pa(buf), len, token);
+               break;
+       case FLASH_OP_WRITE:
+               rc = opal_flash_write(info->id, offset, __pa(buf), len, token);
+               break;
+       case FLASH_OP_ERASE:
+               rc = opal_flash_erase(info->id, offset, len, token);
+               break;
+       default:
+               BUG_ON(1);
+       }
+
+       if (rc != OPAL_ASYNC_COMPLETION) {
+               dev_err(dev, "opal_flash_async_op(op=%d) failed (rc %d)\n",
+                               op, rc);
+               opal_async_release_token(token);
+               return -EIO;
+       }
+
+       rc = opal_async_wait_response(token, &msg);
+       opal_async_release_token(token);
+       if (rc) {
+               dev_err(dev, "opal async wait failed (rc %d)\n", rc);
+               return -EIO;
+       }
+
+       rc = be64_to_cpu(msg.params[1]);
+       if (rc == OPAL_SUCCESS) {
+               rc = 0;
+               if (retlen)
+                       *retlen = len;
+       } else {
+               rc = -EIO;
+       }
+
+       return rc;
+}
+
+/**
+ * @mtd: the device
+ * @from: the offset to read from
+ * @len: the number of bytes to read
+ * @retlen: the number of bytes actually read
+ * @buf: the filled in buffer
+ *
+ * Returns 0 if read successful, or -ERRNO if an error occurred
+ */
+static int powernv_flash_read(struct mtd_info *mtd, loff_t from, size_t len,
+            size_t *retlen, u_char *buf)
+{
+       return powernv_flash_async_op(mtd, FLASH_OP_READ, from,
+                       len, retlen, buf);
+}
+
+/**
+ * @mtd: the device
+ * @to: the offset to write to
+ * @len: the number of bytes to write
+ * @retlen: the number of bytes actually written
+ * @buf: the buffer to get bytes from
+ *
+ * Returns 0 if write successful, -ERRNO if error occurred
+ */
+static int powernv_flash_write(struct mtd_info *mtd, loff_t to, size_t len,
+                    size_t *retlen, const u_char *buf)
+{
+       return powernv_flash_async_op(mtd, FLASH_OP_WRITE, to,
+                       len, retlen, (u_char *)buf);
+}
+
+/**
+ * @mtd: the device
+ * @erase: the erase info
+ * Returns 0 if erase successful or -ERRNO if an error occurred
+ */
+static int powernv_flash_erase(struct mtd_info *mtd, struct erase_info *erase)
+{
+       int rc;
+
+       erase->state = MTD_ERASING;
+
+       /* todo: register our own notifier to do a true async implementation */
+       rc =  powernv_flash_async_op(mtd, FLASH_OP_ERASE, erase->addr,
+                       erase->len, NULL, NULL);
+
+       if (rc) {
+               erase->fail_addr = erase->addr;
+               erase->state = MTD_ERASE_FAILED;
+       } else {
+               erase->state = MTD_ERASE_DONE;
+       }
+       mtd_erase_callback(erase);
+       return rc;
+}
+
+/**
+ * powernv_flash_set_driver_info - Fill the mtd_info structure and docg3
+ * structure @pdev: The platform device
+ * @mtd: The structure to fill
+ */
+static int powernv_flash_set_driver_info(struct device *dev,
+               struct mtd_info *mtd)
+{
+       u64 size;
+       u32 erase_size;
+       int rc;
+
+       rc = of_property_read_u32(dev->of_node, "ibm,flash-block-size",
+                       &erase_size);
+       if (rc) {
+               dev_err(dev, "couldn't get resource block size information\n");
+               return rc;
+       }
+
+       rc = of_property_read_u64(dev->of_node, "reg", &size);
+       if (rc) {
+               dev_err(dev, "couldn't get resource size information\n");
+               return rc;
+       }
+
+       /*
+        * Going to have to check what details I need to set and how to
+        * get them
+        */
+       mtd->name = of_get_property(dev->of_node, "name", NULL);
+       mtd->type = MTD_NORFLASH;
+       mtd->flags = MTD_WRITEABLE;
+       mtd->size = size;
+       mtd->erasesize = erase_size;
+       mtd->writebufsize = mtd->writesize = 1;
+       mtd->owner = THIS_MODULE;
+       mtd->_erase = powernv_flash_erase;
+       mtd->_read = powernv_flash_read;
+       mtd->_write = powernv_flash_write;
+       mtd->dev.parent = dev;
+       return 0;
+}
+
+/**
+ * powernv_flash_probe
+ * @pdev: platform device
+ *
+ * Returns 0 on success, -ENOMEM, -ENXIO on error
+ */
+static int powernv_flash_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct powernv_flash *data;
+       int ret;
+
+       data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+       if (!data) {
+               ret = -ENOMEM;
+               goto out;
+       }
+       data->mtd.priv = data;
+
+       ret = of_property_read_u32(dev->of_node, "ibm,opal-id", &(data->id));
+       if (ret) {
+               dev_err(dev, "no device property 'ibm,opal-id'\n");
+               goto out;
+       }
+
+       ret = powernv_flash_set_driver_info(dev, &data->mtd);
+       if (ret)
+               goto out;
+
+       dev_set_drvdata(dev, data);
+
+       /*
+        * The current flash that skiboot exposes is one contiguous flash chip
+        * with an ffs partition at the start, it should prove easier for users
+        * to deal with partitions or not as they see fit
+        */
+       ret = mtd_device_register(&data->mtd, NULL, 0);
+
+out:
+       return ret;
+}
+
+/**
+ * op_release - Release the driver
+ * @pdev: the platform device
+ *
+ * Returns 0
+ */
+static int powernv_flash_release(struct platform_device *pdev)
+{
+       struct powernv_flash *data = dev_get_drvdata(&(pdev->dev));
+
+       /* All resources should be freed automatically */
+       return mtd_device_unregister(&(data->mtd));
+}
+
+static const struct of_device_id powernv_flash_match[] = {
+       { .compatible = "ibm,opal-flash" },
+       {}
+};
+
+static struct platform_driver powernv_flash_driver = {
+       .driver         = {
+               .name           = "powernv_flash",
+               .of_match_table = powernv_flash_match,
+       },
+       .remove         = powernv_flash_release,
+       .probe          = powernv_flash_probe,
+};
+
+module_platform_driver(powernv_flash_driver);
+
+MODULE_DEVICE_TABLE(of, powernv_flash_match);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Cyril Bur <cyril.bur@au1.ibm.com>");
+MODULE_DESCRIPTION("MTD abstraction for OPAL flash");
index 508bab3bd0c49b555b3cacf803f93b153b46ce13..04b24d2b03f285ab10c98d742b2915e48b3e4189 100644 (file)
@@ -1,8 +1,8 @@
 /*
  * SMI (Serial Memory Controller) device driver for Serial NOR Flash on
  * SPEAr platform
- * The serial nor interface is largely based on drivers/mtd/m25p80.c,
- * however the SPI interface has been replaced by SMI.
+ * The serial nor interface is largely based on m25p80.c, however the SPI
+ * interface has been replaced by SMI.
  *
  * Copyright © 2010 STMicroelectronics.
  * Ashish Priyadarshi
index e715ae90632f82e69991bdc7704cc5dbd35ab0f9..7c95a656f9e479339464ea2cc3423353fbbc808f 100644 (file)
@@ -326,7 +326,7 @@ config MTD_BFIN_ASYNC
 
 config MTD_GPIO_ADDR
        tristate "GPIO-assisted Flash Chip Support"
-       depends on GPIOLIB
+       depends on GPIOLIB || COMPILE_TEST
        depends on MTD_COMPLEX_MAPPINGS
        help
          Map driver which allows flashes to be partially physically addressed
index f7207b0a76dcf9d3537f6202c04dbf4696d3f069..f2b68667ea5904126daf90d2e7f719dfb7c25844 100644 (file)
@@ -138,7 +138,7 @@ static int amd76xrom_init_one(struct pci_dev *pdev,
        /*
         * Try to reserve the window mem region.  If this fails then
         * it is likely due to a fragment of the window being
-        * "reseved" by the BIOS.  In the case that the
+        * "reserved" by the BIOS.  In the case that the
         * request_mem_region() fails then once the rom size is
         * discovered we will try to reserve the unreserved fragment.
         */
index f8a7dd14cee0cc7a8b6767f0ef281ea95dd89149..70a3db3ab856f47c8da32959aed6bf996f83428c 100644 (file)
@@ -38,9 +38,9 @@ static void nw_en_write(void)
         * we want to write a bit pattern XXX1 to Xilinx to enable
         * the write gate, which will be open for about the next 2ms.
         */
-       spin_lock_irqsave(&nw_gpio_lock, flags);
+       raw_spin_lock_irqsave(&nw_gpio_lock, flags);
        nw_cpld_modify(CPLD_FLASH_WR_ENABLE, CPLD_FLASH_WR_ENABLE);
-       spin_unlock_irqrestore(&nw_gpio_lock, flags);
+       raw_spin_unlock_irqrestore(&nw_gpio_lock, flags);
 
        /*
         * let the ISA bus to catch on...
index f784cf0caa13b03d7a24542befe1a29e3638e35f..76ed651b515beef6275b7d67820eb1c4fee8e8dc 100644 (file)
@@ -234,7 +234,7 @@ static int esb2rom_init_one(struct pci_dev *pdev,
 
        /*
         * Try to reserve the window mem region.  If this fails then
-        * it is likely due to the window being "reseved" by the BIOS.
+        * it is likely due to the window being "reserved" by the BIOS.
         */
        window->rsrc.name = MOD_NAME;
        window->rsrc.start = window->phys;
index c7478e18f4858239319b34f916be6ad595695001..8636bba422009a1f54ff58b1611acac54cbf9b45 100644 (file)
@@ -167,7 +167,7 @@ static int ichxrom_init_one(struct pci_dev *pdev,
 
        /*
         * Try to reserve the window mem region.  If this fails then
-        * it is likely due to the window being "reseved" by the BIOS.
+        * it is likely due to the window being "reserved" by the BIOS.
         */
        window->rsrc.name = MOD_NAME;
        window->rsrc.start = window->phys;
index 33d26f5bee549ecd8bae2426ec9de3028e217d85..e2f878216048eed8ac69e973d5da7e9f4e668147 100644 (file)
@@ -45,7 +45,6 @@ struct ltq_mtd {
 };
 
 static const char ltq_map_name[] = "ltq_nor";
-static const char * const ltq_probe_types[] = { "cmdlinepart", "ofpart", NULL };
 
 static map_word
 ltq_read16(struct map_info *map, unsigned long adr)
@@ -168,8 +167,7 @@ ltq_mtd_probe(struct platform_device *pdev)
        cfi->addr_unlock2 ^= 1;
 
        ppdata.of_node = pdev->dev.of_node;
-       err = mtd_device_parse_register(ltq_mtd->mtd, ltq_probe_types,
-                                       &ppdata, NULL, 0);
+       err = mtd_device_parse_register(ltq_mtd->mtd, NULL, &ppdata, NULL, 0);
        if (err) {
                dev_err(&pdev->dev, "failed to add partitions\n");
                goto err_destroy;
index ff26e979b1a17c243e1525adfceed3886d479ba1..774b32fd29e673c10cf927cd159a5d50829d6e86 100644 (file)
@@ -147,7 +147,7 @@ static void of_free_probes(const char * const *probes)
                kfree(probes);
 }
 
-static struct of_device_id of_flash_match[];
+static const struct of_device_id of_flash_match[];
 static int of_flash_probe(struct platform_device *dev)
 {
        const char * const *part_probe_types;
@@ -327,7 +327,7 @@ err_flash_remove:
        return err;
 }
 
-static struct of_device_id of_flash_match[] = {
+static const struct of_device_id of_flash_match[] = {
        {
                .compatible     = "cfi-flash",
                .data           = (void *)"cfi_probe",
index 2b0c528709997a618d672e55c50bb44336a8cd69..41acc507b22ed942bee2f0df6f43dda073efeb99 100644 (file)
@@ -197,6 +197,7 @@ static int blktrans_open(struct block_device *bdev, fmode_t mode)
                return -ERESTARTSYS; /* FIXME: busy loop! -arnd*/
 
        mutex_lock(&dev->lock);
+       mutex_lock(&mtd_table_mutex);
 
        if (dev->open)
                goto unlock;
@@ -220,6 +221,7 @@ static int blktrans_open(struct block_device *bdev, fmode_t mode)
 
 unlock:
        dev->open++;
+       mutex_unlock(&mtd_table_mutex);
        mutex_unlock(&dev->lock);
        blktrans_dev_put(dev);
        return ret;
@@ -230,6 +232,7 @@ error_release:
 error_put:
        module_put(dev->tr->owner);
        kref_put(&dev->ref, blktrans_dev_release);
+       mutex_unlock(&mtd_table_mutex);
        mutex_unlock(&dev->lock);
        blktrans_dev_put(dev);
        return ret;
@@ -243,6 +246,7 @@ static void blktrans_release(struct gendisk *disk, fmode_t mode)
                return;
 
        mutex_lock(&dev->lock);
+       mutex_lock(&mtd_table_mutex);
 
        if (--dev->open)
                goto unlock;
@@ -256,6 +260,7 @@ static void blktrans_release(struct gendisk *disk, fmode_t mode)
                __put_mtd_device(dev->mtd);
        }
 unlock:
+       mutex_unlock(&mtd_table_mutex);
        mutex_unlock(&dev->lock);
        blktrans_dev_put(dev);
 }
@@ -273,7 +278,7 @@ static int blktrans_getgeo(struct block_device *bdev, struct hd_geometry *geo)
        if (!dev->mtd)
                goto unlock;
 
-       ret = dev->tr->getgeo ? dev->tr->getgeo(dev, geo) : 0;
+       ret = dev->tr->getgeo ? dev->tr->getgeo(dev, geo) : -ENOTTY;
 unlock:
        mutex_unlock(&dev->lock);
        blktrans_dev_put(dev);
index d172195fbd15fcbd947a2bc0780d0f9044cba2fd..8bbbb751bf45e2340e727439b6368c78d148089f 100644 (file)
 static struct backing_dev_info mtd_bdi = {
 };
 
-static int mtd_cls_suspend(struct device *dev, pm_message_t state);
-static int mtd_cls_resume(struct device *dev);
+#ifdef CONFIG_PM_SLEEP
+
+static int mtd_cls_suspend(struct device *dev)
+{
+       struct mtd_info *mtd = dev_get_drvdata(dev);
+
+       return mtd ? mtd_suspend(mtd) : 0;
+}
+
+static int mtd_cls_resume(struct device *dev)
+{
+       struct mtd_info *mtd = dev_get_drvdata(dev);
+
+       if (mtd)
+               mtd_resume(mtd);
+       return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(mtd_cls_pm_ops, mtd_cls_suspend, mtd_cls_resume);
+#define MTD_CLS_PM_OPS (&mtd_cls_pm_ops)
+#else
+#define MTD_CLS_PM_OPS NULL
+#endif
 
 static struct class mtd_class = {
        .name = "mtd",
        .owner = THIS_MODULE,
-       .suspend = mtd_cls_suspend,
-       .resume = mtd_cls_resume,
+       .pm = MTD_CLS_PM_OPS,
 };
 
 static DEFINE_IDR(mtd_idr);
@@ -88,22 +108,6 @@ static void mtd_release(struct device *dev)
        device_destroy(&mtd_class, index + 1);
 }
 
-static int mtd_cls_suspend(struct device *dev, pm_message_t state)
-{
-       struct mtd_info *mtd = dev_get_drvdata(dev);
-
-       return mtd ? mtd_suspend(mtd) : 0;
-}
-
-static int mtd_cls_resume(struct device *dev)
-{
-       struct mtd_info *mtd = dev_get_drvdata(dev);
-
-       if (mtd)
-               mtd_resume(mtd);
-       return 0;
-}
-
 static ssize_t mtd_type_show(struct device *dev,
                struct device_attribute *attr, char *buf)
 {
@@ -375,8 +379,7 @@ static int mtd_reboot_notifier(struct notifier_block *n, unsigned long state,
  *
  *     Add a device to the list of MTD devices present in the system, and
  *     notify each currently active MTD 'user' of its arrival. Returns
- *     zero on success or 1 on failure, which currently will only happen
- *     if there is insufficient memory or a sysfs error.
+ *     zero on success or non-zero on failure.
  */
 
 int add_mtd_device(struct mtd_info *mtd)
@@ -390,8 +393,10 @@ int add_mtd_device(struct mtd_info *mtd)
        mutex_lock(&mtd_table_mutex);
 
        i = idr_alloc(&mtd_idr, mtd, 0, 0, GFP_KERNEL);
-       if (i < 0)
+       if (i < 0) {
+               error = i;
                goto fail_locked;
+       }
 
        mtd->index = i;
        mtd->usecount = 0;
@@ -420,6 +425,8 @@ int add_mtd_device(struct mtd_info *mtd)
                        printk(KERN_WARNING
                               "%s: unlock failed, writes may not work\n",
                               mtd->name);
+               /* Ignore unlock failures? */
+               error = 0;
        }
 
        /* Caller should have set dev.parent to match the
@@ -430,7 +437,8 @@ int add_mtd_device(struct mtd_info *mtd)
        mtd->dev.devt = MTD_DEVT(i);
        dev_set_name(&mtd->dev, "mtd%d", i);
        dev_set_drvdata(&mtd->dev, mtd);
-       if (device_register(&mtd->dev) != 0)
+       error = device_register(&mtd->dev);
+       if (error)
                goto fail_added;
 
        device_create(&mtd_class, mtd->dev.parent, MTD_DEVT(i) + 1, NULL,
@@ -454,7 +462,7 @@ fail_added:
        idr_remove(&mtd_idr, i);
 fail_locked:
        mutex_unlock(&mtd_table_mutex);
-       return 1;
+       return error;
 }
 
 /**
@@ -510,8 +518,8 @@ static int mtd_add_device_partitions(struct mtd_info *mtd,
 
        if (nbparts == 0 || IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER)) {
                ret = add_mtd_device(mtd);
-               if (ret == 1)
-                       return -ENODEV;
+               if (ret)
+                       return ret;
        }
 
        if (nbparts > 0) {
index 5897d8d8fa5a962d896e6726edde207b99575d39..5b2806a7e5f75fe928242bcfdd625d1194738507 100644 (file)
@@ -76,7 +76,7 @@ config MTD_NAND_DENALI_SCRATCH_REG_ADDR
 
 config MTD_NAND_GPIO
        tristate "GPIO assisted NAND Flash driver"
-       depends on GPIOLIB
+       depends on GPIOLIB || COMPILE_TEST
        help
          This enables a NAND flash driver where control signals are
          connected to GPIO pins, and commands and data are communicated
@@ -394,6 +394,14 @@ config MTD_NAND_GPMI_NAND
         block, such as SD card. So pay attention to it when you enable
         the GPMI.
 
+config MTD_NAND_BRCMNAND
+       tristate "Broadcom STB NAND controller"
+       depends on ARM || MIPS
+       help
+         Enables the Broadcom NAND controller driver. The controller was
+         originally designed for Set-Top Box but is used on various BCM7xxx,
+         BCM3xxx, BCM63xxx, iProc/Cygnus and more.
+
 config MTD_NAND_BCM47XXNFLASH
        tristate "Support for NAND flash on BCM4706 BCMA bus"
        depends on BCMA_NFLASH
index 582bbd05aff7ab7df0a20c282bc25faa1532437a..1f897ec3c242a977a52128e37f56ef9431fbd548 100644 (file)
@@ -52,5 +52,6 @@ obj-$(CONFIG_MTD_NAND_XWAY)           += xway_nand.o
 obj-$(CONFIG_MTD_NAND_BCM47XXNFLASH)   += bcm47xxnflash/
 obj-$(CONFIG_MTD_NAND_SUNXI)           += sunxi_nand.o
 obj-$(CONFIG_MTD_NAND_HISI504)         += hisi504_nand.o
+obj-$(CONFIG_MTD_NAND_BRCMNAND)                += brcmnand/
 
 nand-objs := nand_base.o nand_bbt.o nand_timings.o
diff --git a/drivers/mtd/nand/brcmnand/Makefile b/drivers/mtd/nand/brcmnand/Makefile
new file mode 100644 (file)
index 0000000..3b1fbfd
--- /dev/null
@@ -0,0 +1,6 @@
+# link order matters; don't link the more generic brcmstb_nand.o before the
+# more specific iproc_nand.o, for instance
+obj-$(CONFIG_MTD_NAND_BRCMNAND)                += iproc_nand.o
+obj-$(CONFIG_MTD_NAND_BRCMNAND)                += bcm63138_nand.o
+obj-$(CONFIG_MTD_NAND_BRCMNAND)                += brcmstb_nand.o
+obj-$(CONFIG_MTD_NAND_BRCMNAND)                += brcmnand.o
diff --git a/drivers/mtd/nand/brcmnand/bcm63138_nand.c b/drivers/mtd/nand/brcmnand/bcm63138_nand.c
new file mode 100644 (file)
index 0000000..3f4c44c
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * Copyright © 2015 Broadcom Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include "brcmnand.h"
+
+struct bcm63138_nand_soc_priv {
+       void __iomem *base;
+};
+
+#define BCM63138_NAND_INT_STATUS               0x00
+#define BCM63138_NAND_INT_EN                   0x04
+
+enum {
+       BCM63138_CTLRDY         = BIT(4),
+};
+
+static bool bcm63138_nand_intc_ack(struct brcmnand_soc *soc)
+{
+       struct bcm63138_nand_soc_priv *priv = soc->priv;
+       void __iomem *mmio = priv->base + BCM63138_NAND_INT_STATUS;
+       u32 val = brcmnand_readl(mmio);
+
+       if (val & BCM63138_CTLRDY) {
+               brcmnand_writel(val & ~BCM63138_CTLRDY, mmio);
+               return true;
+       }
+
+       return false;
+}
+
+static void bcm63138_nand_intc_set(struct brcmnand_soc *soc, bool en)
+{
+       struct bcm63138_nand_soc_priv *priv = soc->priv;
+       void __iomem *mmio = priv->base + BCM63138_NAND_INT_EN;
+       u32 val = brcmnand_readl(mmio);
+
+       if (en)
+               val |= BCM63138_CTLRDY;
+       else
+               val &= ~BCM63138_CTLRDY;
+
+       brcmnand_writel(val, mmio);
+}
+
+static int bcm63138_nand_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct bcm63138_nand_soc_priv *priv;
+       struct brcmnand_soc *soc;
+       struct resource *res;
+
+       soc = devm_kzalloc(dev, sizeof(*soc), GFP_KERNEL);
+       if (!soc)
+               return -ENOMEM;
+
+       priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand-int-base");
+       priv->base = devm_ioremap_resource(dev, res);
+       if (IS_ERR(priv->base))
+               return PTR_ERR(priv->base);
+
+       soc->pdev = pdev;
+       soc->priv = priv;
+       soc->ctlrdy_ack = bcm63138_nand_intc_ack;
+       soc->ctlrdy_set_enabled = bcm63138_nand_intc_set;
+
+       return brcmnand_probe(pdev, soc);
+}
+
+static const struct of_device_id bcm63138_nand_of_match[] = {
+       { .compatible = "brcm,nand-bcm63138" },
+       {},
+};
+MODULE_DEVICE_TABLE(of, bcm63138_nand_of_match);
+
+static struct platform_driver bcm63138_nand_driver = {
+       .probe                  = bcm63138_nand_probe,
+       .remove                 = brcmnand_remove,
+       .driver = {
+               .name           = "bcm63138_nand",
+               .pm             = &brcmnand_pm_ops,
+               .of_match_table = bcm63138_nand_of_match,
+       }
+};
+module_platform_driver(bcm63138_nand_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Brian Norris");
+MODULE_DESCRIPTION("NAND driver for BCM63138");
diff --git a/drivers/mtd/nand/brcmnand/brcmnand.c b/drivers/mtd/nand/brcmnand/brcmnand.c
new file mode 100644 (file)
index 0000000..fddb795
--- /dev/null
@@ -0,0 +1,2246 @@
+/*
+ * Copyright © 2010-2015 Broadcom Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License 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/version.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/completion.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/dma-mapping.h>
+#include <linux/ioport.h>
+#include <linux/bug.h>
+#include <linux/kernel.h>
+#include <linux/bitops.h>
+#include <linux/mm.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/partitions.h>
+#include <linux/of.h>
+#include <linux/of_mtd.h>
+#include <linux/of_platform.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/log2.h>
+
+#include "brcmnand.h"
+
+/*
+ * This flag controls if WP stays on between erase/write commands to mitigate
+ * flash corruption due to power glitches. Values:
+ * 0: NAND_WP is not used or not available
+ * 1: NAND_WP is set by default, cleared for erase/write operations
+ * 2: NAND_WP is always cleared
+ */
+static int wp_on = 1;
+module_param(wp_on, int, 0444);
+
+/***********************************************************************
+ * Definitions
+ ***********************************************************************/
+
+#define DRV_NAME                       "brcmnand"
+
+#define CMD_NULL                       0x00
+#define CMD_PAGE_READ                  0x01
+#define CMD_SPARE_AREA_READ            0x02
+#define CMD_STATUS_READ                        0x03
+#define CMD_PROGRAM_PAGE               0x04
+#define CMD_PROGRAM_SPARE_AREA         0x05
+#define CMD_COPY_BACK                  0x06
+#define CMD_DEVICE_ID_READ             0x07
+#define CMD_BLOCK_ERASE                        0x08
+#define CMD_FLASH_RESET                        0x09
+#define CMD_BLOCKS_LOCK                        0x0a
+#define CMD_BLOCKS_LOCK_DOWN           0x0b
+#define CMD_BLOCKS_UNLOCK              0x0c
+#define CMD_READ_BLOCKS_LOCK_STATUS    0x0d
+#define CMD_PARAMETER_READ             0x0e
+#define CMD_PARAMETER_CHANGE_COL       0x0f
+#define CMD_LOW_LEVEL_OP               0x10
+
+struct brcm_nand_dma_desc {
+       u32 next_desc;
+       u32 next_desc_ext;
+       u32 cmd_irq;
+       u32 dram_addr;
+       u32 dram_addr_ext;
+       u32 tfr_len;
+       u32 total_len;
+       u32 flash_addr;
+       u32 flash_addr_ext;
+       u32 cs;
+       u32 pad2[5];
+       u32 status_valid;
+} __packed;
+
+/* Bitfields for brcm_nand_dma_desc::status_valid */
+#define FLASH_DMA_ECC_ERROR    (1 << 8)
+#define FLASH_DMA_CORR_ERROR   (1 << 9)
+
+/* 512B flash cache in the NAND controller HW */
+#define FC_SHIFT               9U
+#define FC_BYTES               512U
+#define FC_WORDS               (FC_BYTES >> 2)
+
+#define BRCMNAND_MIN_PAGESIZE  512
+#define BRCMNAND_MIN_BLOCKSIZE (8 * 1024)
+#define BRCMNAND_MIN_DEVSIZE   (4ULL * 1024 * 1024)
+
+/* Controller feature flags */
+enum {
+       BRCMNAND_HAS_1K_SECTORS                 = BIT(0),
+       BRCMNAND_HAS_PREFETCH                   = BIT(1),
+       BRCMNAND_HAS_CACHE_MODE                 = BIT(2),
+       BRCMNAND_HAS_WP                         = BIT(3),
+};
+
+struct brcmnand_controller {
+       struct device           *dev;
+       struct nand_hw_control  controller;
+       void __iomem            *nand_base;
+       void __iomem            *nand_fc; /* flash cache */
+       void __iomem            *flash_dma_base;
+       unsigned int            irq;
+       unsigned int            dma_irq;
+       int                     nand_version;
+
+       /* Some SoCs provide custom interrupt status register(s) */
+       struct brcmnand_soc     *soc;
+
+       int                     cmd_pending;
+       bool                    dma_pending;
+       struct completion       done;
+       struct completion       dma_done;
+
+       /* List of NAND hosts (one for each chip-select) */
+       struct list_head host_list;
+
+       struct brcm_nand_dma_desc *dma_desc;
+       dma_addr_t              dma_pa;
+
+       /* in-memory cache of the FLASH_CACHE, used only for some commands */
+       u32                     flash_cache[FC_WORDS];
+
+       /* Controller revision details */
+       const u16               *reg_offsets;
+       unsigned int            reg_spacing; /* between CS1, CS2, ... regs */
+       const u8                *cs_offsets; /* within each chip-select */
+       const u8                *cs0_offsets; /* within CS0, if different */
+       unsigned int            max_block_size;
+       const unsigned int      *block_sizes;
+       unsigned int            max_page_size;
+       const unsigned int      *page_sizes;
+       unsigned int            max_oob;
+       u32                     features;
+
+       /* for low-power standby/resume only */
+       u32                     nand_cs_nand_select;
+       u32                     nand_cs_nand_xor;
+       u32                     corr_stat_threshold;
+       u32                     flash_dma_mode;
+};
+
+struct brcmnand_cfg {
+       u64                     device_size;
+       unsigned int            block_size;
+       unsigned int            page_size;
+       unsigned int            spare_area_size;
+       unsigned int            device_width;
+       unsigned int            col_adr_bytes;
+       unsigned int            blk_adr_bytes;
+       unsigned int            ful_adr_bytes;
+       unsigned int            sector_size_1k;
+       unsigned int            ecc_level;
+       /* use for low-power standby/resume only */
+       u32                     acc_control;
+       u32                     config;
+       u32                     config_ext;
+       u32                     timing_1;
+       u32                     timing_2;
+};
+
+struct brcmnand_host {
+       struct list_head        node;
+       struct device_node      *of_node;
+
+       struct nand_chip        chip;
+       struct mtd_info         mtd;
+       struct platform_device  *pdev;
+       int                     cs;
+
+       unsigned int            last_cmd;
+       unsigned int            last_byte;
+       u64                     last_addr;
+       struct brcmnand_cfg     hwcfg;
+       struct brcmnand_controller *ctrl;
+};
+
+enum brcmnand_reg {
+       BRCMNAND_CMD_START = 0,
+       BRCMNAND_CMD_EXT_ADDRESS,
+       BRCMNAND_CMD_ADDRESS,
+       BRCMNAND_INTFC_STATUS,
+       BRCMNAND_CS_SELECT,
+       BRCMNAND_CS_XOR,
+       BRCMNAND_LL_OP,
+       BRCMNAND_CS0_BASE,
+       BRCMNAND_CS1_BASE,              /* CS1 regs, if non-contiguous */
+       BRCMNAND_CORR_THRESHOLD,
+       BRCMNAND_CORR_THRESHOLD_EXT,
+       BRCMNAND_UNCORR_COUNT,
+       BRCMNAND_CORR_COUNT,
+       BRCMNAND_CORR_EXT_ADDR,
+       BRCMNAND_CORR_ADDR,
+       BRCMNAND_UNCORR_EXT_ADDR,
+       BRCMNAND_UNCORR_ADDR,
+       BRCMNAND_SEMAPHORE,
+       BRCMNAND_ID,
+       BRCMNAND_ID_EXT,
+       BRCMNAND_LL_RDATA,
+       BRCMNAND_OOB_READ_BASE,
+       BRCMNAND_OOB_READ_10_BASE,      /* offset 0x10, if non-contiguous */
+       BRCMNAND_OOB_WRITE_BASE,
+       BRCMNAND_OOB_WRITE_10_BASE,     /* offset 0x10, if non-contiguous */
+       BRCMNAND_FC_BASE,
+};
+
+/* BRCMNAND v4.0 */
+static const u16 brcmnand_regs_v40[] = {
+       [BRCMNAND_CMD_START]            =  0x04,
+       [BRCMNAND_CMD_EXT_ADDRESS]      =  0x08,
+       [BRCMNAND_CMD_ADDRESS]          =  0x0c,
+       [BRCMNAND_INTFC_STATUS]         =  0x6c,
+       [BRCMNAND_CS_SELECT]            =  0x14,
+       [BRCMNAND_CS_XOR]               =  0x18,
+       [BRCMNAND_LL_OP]                = 0x178,
+       [BRCMNAND_CS0_BASE]             =  0x40,
+       [BRCMNAND_CS1_BASE]             =  0xd0,
+       [BRCMNAND_CORR_THRESHOLD]       =  0x84,
+       [BRCMNAND_CORR_THRESHOLD_EXT]   =     0,
+       [BRCMNAND_UNCORR_COUNT]         =     0,
+       [BRCMNAND_CORR_COUNT]           =     0,
+       [BRCMNAND_CORR_EXT_ADDR]        =  0x70,
+       [BRCMNAND_CORR_ADDR]            =  0x74,
+       [BRCMNAND_UNCORR_EXT_ADDR]      =  0x78,
+       [BRCMNAND_UNCORR_ADDR]          =  0x7c,
+       [BRCMNAND_SEMAPHORE]            =  0x58,
+       [BRCMNAND_ID]                   =  0x60,
+       [BRCMNAND_ID_EXT]               =  0x64,
+       [BRCMNAND_LL_RDATA]             = 0x17c,
+       [BRCMNAND_OOB_READ_BASE]        =  0x20,
+       [BRCMNAND_OOB_READ_10_BASE]     = 0x130,
+       [BRCMNAND_OOB_WRITE_BASE]       =  0x30,
+       [BRCMNAND_OOB_WRITE_10_BASE]    =     0,
+       [BRCMNAND_FC_BASE]              = 0x200,
+};
+
+/* BRCMNAND v5.0 */
+static const u16 brcmnand_regs_v50[] = {
+       [BRCMNAND_CMD_START]            =  0x04,
+       [BRCMNAND_CMD_EXT_ADDRESS]      =  0x08,
+       [BRCMNAND_CMD_ADDRESS]          =  0x0c,
+       [BRCMNAND_INTFC_STATUS]         =  0x6c,
+       [BRCMNAND_CS_SELECT]            =  0x14,
+       [BRCMNAND_CS_XOR]               =  0x18,
+       [BRCMNAND_LL_OP]                = 0x178,
+       [BRCMNAND_CS0_BASE]             =  0x40,
+       [BRCMNAND_CS1_BASE]             =  0xd0,
+       [BRCMNAND_CORR_THRESHOLD]       =  0x84,
+       [BRCMNAND_CORR_THRESHOLD_EXT]   =     0,
+       [BRCMNAND_UNCORR_COUNT]         =     0,
+       [BRCMNAND_CORR_COUNT]           =     0,
+       [BRCMNAND_CORR_EXT_ADDR]        =  0x70,
+       [BRCMNAND_CORR_ADDR]            =  0x74,
+       [BRCMNAND_UNCORR_EXT_ADDR]      =  0x78,
+       [BRCMNAND_UNCORR_ADDR]          =  0x7c,
+       [BRCMNAND_SEMAPHORE]            =  0x58,
+       [BRCMNAND_ID]                   =  0x60,
+       [BRCMNAND_ID_EXT]               =  0x64,
+       [BRCMNAND_LL_RDATA]             = 0x17c,
+       [BRCMNAND_OOB_READ_BASE]        =  0x20,
+       [BRCMNAND_OOB_READ_10_BASE]     = 0x130,
+       [BRCMNAND_OOB_WRITE_BASE]       =  0x30,
+       [BRCMNAND_OOB_WRITE_10_BASE]    = 0x140,
+       [BRCMNAND_FC_BASE]              = 0x200,
+};
+
+/* BRCMNAND v6.0 - v7.1 */
+static const u16 brcmnand_regs_v60[] = {
+       [BRCMNAND_CMD_START]            =  0x04,
+       [BRCMNAND_CMD_EXT_ADDRESS]      =  0x08,
+       [BRCMNAND_CMD_ADDRESS]          =  0x0c,
+       [BRCMNAND_INTFC_STATUS]         =  0x14,
+       [BRCMNAND_CS_SELECT]            =  0x18,
+       [BRCMNAND_CS_XOR]               =  0x1c,
+       [BRCMNAND_LL_OP]                =  0x20,
+       [BRCMNAND_CS0_BASE]             =  0x50,
+       [BRCMNAND_CS1_BASE]             =     0,
+       [BRCMNAND_CORR_THRESHOLD]       =  0xc0,
+       [BRCMNAND_CORR_THRESHOLD_EXT]   =  0xc4,
+       [BRCMNAND_UNCORR_COUNT]         =  0xfc,
+       [BRCMNAND_CORR_COUNT]           = 0x100,
+       [BRCMNAND_CORR_EXT_ADDR]        = 0x10c,
+       [BRCMNAND_CORR_ADDR]            = 0x110,
+       [BRCMNAND_UNCORR_EXT_ADDR]      = 0x114,
+       [BRCMNAND_UNCORR_ADDR]          = 0x118,
+       [BRCMNAND_SEMAPHORE]            = 0x150,
+       [BRCMNAND_ID]                   = 0x194,
+       [BRCMNAND_ID_EXT]               = 0x198,
+       [BRCMNAND_LL_RDATA]             = 0x19c,
+       [BRCMNAND_OOB_READ_BASE]        = 0x200,
+       [BRCMNAND_OOB_READ_10_BASE]     =     0,
+       [BRCMNAND_OOB_WRITE_BASE]       = 0x280,
+       [BRCMNAND_OOB_WRITE_10_BASE]    =     0,
+       [BRCMNAND_FC_BASE]              = 0x400,
+};
+
+enum brcmnand_cs_reg {
+       BRCMNAND_CS_CFG_EXT = 0,
+       BRCMNAND_CS_CFG,
+       BRCMNAND_CS_ACC_CONTROL,
+       BRCMNAND_CS_TIMING1,
+       BRCMNAND_CS_TIMING2,
+};
+
+/* Per chip-select offsets for v7.1 */
+static const u8 brcmnand_cs_offsets_v71[] = {
+       [BRCMNAND_CS_ACC_CONTROL]       = 0x00,
+       [BRCMNAND_CS_CFG_EXT]           = 0x04,
+       [BRCMNAND_CS_CFG]               = 0x08,
+       [BRCMNAND_CS_TIMING1]           = 0x0c,
+       [BRCMNAND_CS_TIMING2]           = 0x10,
+};
+
+/* Per chip-select offsets for pre v7.1, except CS0 on <= v5.0 */
+static const u8 brcmnand_cs_offsets[] = {
+       [BRCMNAND_CS_ACC_CONTROL]       = 0x00,
+       [BRCMNAND_CS_CFG_EXT]           = 0x04,
+       [BRCMNAND_CS_CFG]               = 0x04,
+       [BRCMNAND_CS_TIMING1]           = 0x08,
+       [BRCMNAND_CS_TIMING2]           = 0x0c,
+};
+
+/* Per chip-select offset for <= v5.0 on CS0 only */
+static const u8 brcmnand_cs_offsets_cs0[] = {
+       [BRCMNAND_CS_ACC_CONTROL]       = 0x00,
+       [BRCMNAND_CS_CFG_EXT]           = 0x08,
+       [BRCMNAND_CS_CFG]               = 0x08,
+       [BRCMNAND_CS_TIMING1]           = 0x10,
+       [BRCMNAND_CS_TIMING2]           = 0x14,
+};
+
+/* BRCMNAND_INTFC_STATUS */
+enum {
+       INTFC_FLASH_STATUS              = GENMASK(7, 0),
+
+       INTFC_ERASED                    = BIT(27),
+       INTFC_OOB_VALID                 = BIT(28),
+       INTFC_CACHE_VALID               = BIT(29),
+       INTFC_FLASH_READY               = BIT(30),
+       INTFC_CTLR_READY                = BIT(31),
+};
+
+static inline u32 nand_readreg(struct brcmnand_controller *ctrl, u32 offs)
+{
+       return brcmnand_readl(ctrl->nand_base + offs);
+}
+
+static inline void nand_writereg(struct brcmnand_controller *ctrl, u32 offs,
+                                u32 val)
+{
+       brcmnand_writel(val, ctrl->nand_base + offs);
+}
+
+static int brcmnand_revision_init(struct brcmnand_controller *ctrl)
+{
+       static const unsigned int block_sizes_v6[] = { 8, 16, 128, 256, 512, 1024, 2048, 0 };
+       static const unsigned int block_sizes_v4[] = { 16, 128, 8, 512, 256, 1024, 2048, 0 };
+       static const unsigned int page_sizes[] = { 512, 2048, 4096, 8192, 0 };
+
+       ctrl->nand_version = nand_readreg(ctrl, 0) & 0xffff;
+
+       /* Only support v4.0+? */
+       if (ctrl->nand_version < 0x0400) {
+               dev_err(ctrl->dev, "version %#x not supported\n",
+                       ctrl->nand_version);
+               return -ENODEV;
+       }
+
+       /* Register offsets */
+       if (ctrl->nand_version >= 0x0600)
+               ctrl->reg_offsets = brcmnand_regs_v60;
+       else if (ctrl->nand_version >= 0x0500)
+               ctrl->reg_offsets = brcmnand_regs_v50;
+       else if (ctrl->nand_version >= 0x0400)
+               ctrl->reg_offsets = brcmnand_regs_v40;
+
+       /* Chip-select stride */
+       if (ctrl->nand_version >= 0x0701)
+               ctrl->reg_spacing = 0x14;
+       else
+               ctrl->reg_spacing = 0x10;
+
+       /* Per chip-select registers */
+       if (ctrl->nand_version >= 0x0701) {
+               ctrl->cs_offsets = brcmnand_cs_offsets_v71;
+       } else {
+               ctrl->cs_offsets = brcmnand_cs_offsets;
+
+               /* v5.0 and earlier has a different CS0 offset layout */
+               if (ctrl->nand_version <= 0x0500)
+                       ctrl->cs0_offsets = brcmnand_cs_offsets_cs0;
+       }
+
+       /* Page / block sizes */
+       if (ctrl->nand_version >= 0x0701) {
+               /* >= v7.1 use nice power-of-2 values! */
+               ctrl->max_page_size = 16 * 1024;
+               ctrl->max_block_size = 2 * 1024 * 1024;
+       } else {
+               ctrl->page_sizes = page_sizes;
+               if (ctrl->nand_version >= 0x0600)
+                       ctrl->block_sizes = block_sizes_v6;
+               else
+                       ctrl->block_sizes = block_sizes_v4;
+
+               if (ctrl->nand_version < 0x0400) {
+                       ctrl->max_page_size = 4096;
+                       ctrl->max_block_size = 512 * 1024;
+               }
+       }
+
+       /* Maximum spare area sector size (per 512B) */
+       if (ctrl->nand_version >= 0x0600)
+               ctrl->max_oob = 64;
+       else if (ctrl->nand_version >= 0x0500)
+               ctrl->max_oob = 32;
+       else
+               ctrl->max_oob = 16;
+
+       /* v6.0 and newer (except v6.1) have prefetch support */
+       if (ctrl->nand_version >= 0x0600 && ctrl->nand_version != 0x0601)
+               ctrl->features |= BRCMNAND_HAS_PREFETCH;
+
+       /*
+        * v6.x has cache mode, but it's implemented differently. Ignore it for
+        * now.
+        */
+       if (ctrl->nand_version >= 0x0700)
+               ctrl->features |= BRCMNAND_HAS_CACHE_MODE;
+
+       if (ctrl->nand_version >= 0x0500)
+               ctrl->features |= BRCMNAND_HAS_1K_SECTORS;
+
+       if (ctrl->nand_version >= 0x0700)
+               ctrl->features |= BRCMNAND_HAS_WP;
+       else if (of_property_read_bool(ctrl->dev->of_node, "brcm,nand-has-wp"))
+               ctrl->features |= BRCMNAND_HAS_WP;
+
+       return 0;
+}
+
+static inline u32 brcmnand_read_reg(struct brcmnand_controller *ctrl,
+               enum brcmnand_reg reg)
+{
+       u16 offs = ctrl->reg_offsets[reg];
+
+       if (offs)
+               return nand_readreg(ctrl, offs);
+       else
+               return 0;
+}
+
+static inline void brcmnand_write_reg(struct brcmnand_controller *ctrl,
+                                     enum brcmnand_reg reg, u32 val)
+{
+       u16 offs = ctrl->reg_offsets[reg];
+
+       if (offs)
+               nand_writereg(ctrl, offs, val);
+}
+
+static inline void brcmnand_rmw_reg(struct brcmnand_controller *ctrl,
+                                   enum brcmnand_reg reg, u32 mask, unsigned
+                                   int shift, u32 val)
+{
+       u32 tmp = brcmnand_read_reg(ctrl, reg);
+
+       tmp &= ~mask;
+       tmp |= val << shift;
+       brcmnand_write_reg(ctrl, reg, tmp);
+}
+
+static inline u32 brcmnand_read_fc(struct brcmnand_controller *ctrl, int word)
+{
+       return __raw_readl(ctrl->nand_fc + word * 4);
+}
+
+static inline void brcmnand_write_fc(struct brcmnand_controller *ctrl,
+                                    int word, u32 val)
+{
+       __raw_writel(val, ctrl->nand_fc + word * 4);
+}
+
+static inline u16 brcmnand_cs_offset(struct brcmnand_controller *ctrl, int cs,
+                                    enum brcmnand_cs_reg reg)
+{
+       u16 offs_cs0 = ctrl->reg_offsets[BRCMNAND_CS0_BASE];
+       u16 offs_cs1 = ctrl->reg_offsets[BRCMNAND_CS1_BASE];
+       u8 cs_offs;
+
+       if (cs == 0 && ctrl->cs0_offsets)
+               cs_offs = ctrl->cs0_offsets[reg];
+       else
+               cs_offs = ctrl->cs_offsets[reg];
+
+       if (cs && offs_cs1)
+               return offs_cs1 + (cs - 1) * ctrl->reg_spacing + cs_offs;
+
+       return offs_cs0 + cs * ctrl->reg_spacing + cs_offs;
+}
+
+static inline u32 brcmnand_count_corrected(struct brcmnand_controller *ctrl)
+{
+       if (ctrl->nand_version < 0x0600)
+               return 1;
+       return brcmnand_read_reg(ctrl, BRCMNAND_CORR_COUNT);
+}
+
+static void brcmnand_wr_corr_thresh(struct brcmnand_host *host, u8 val)
+{
+       struct brcmnand_controller *ctrl = host->ctrl;
+       unsigned int shift = 0, bits;
+       enum brcmnand_reg reg = BRCMNAND_CORR_THRESHOLD;
+       int cs = host->cs;
+
+       if (ctrl->nand_version >= 0x0600)
+               bits = 6;
+       else if (ctrl->nand_version >= 0x0500)
+               bits = 5;
+       else
+               bits = 4;
+
+       if (ctrl->nand_version >= 0x0600) {
+               if (cs >= 5)
+                       reg = BRCMNAND_CORR_THRESHOLD_EXT;
+               shift = (cs % 5) * bits;
+       }
+       brcmnand_rmw_reg(ctrl, reg, (bits - 1) << shift, shift, val);
+}
+
+static inline int brcmnand_cmd_shift(struct brcmnand_controller *ctrl)
+{
+       if (ctrl->nand_version < 0x0700)
+               return 24;
+       return 0;
+}
+
+/***********************************************************************
+ * NAND ACC CONTROL bitfield
+ *
+ * Some bits have remained constant throughout hardware revision, while
+ * others have shifted around.
+ ***********************************************************************/
+
+/* Constant for all versions (where supported) */
+enum {
+       /* See BRCMNAND_HAS_CACHE_MODE */
+       ACC_CONTROL_CACHE_MODE                          = BIT(22),
+
+       /* See BRCMNAND_HAS_PREFETCH */
+       ACC_CONTROL_PREFETCH                            = BIT(23),
+
+       ACC_CONTROL_PAGE_HIT                            = BIT(24),
+       ACC_CONTROL_WR_PREEMPT                          = BIT(25),
+       ACC_CONTROL_PARTIAL_PAGE                        = BIT(26),
+       ACC_CONTROL_RD_ERASED                           = BIT(27),
+       ACC_CONTROL_FAST_PGM_RDIN                       = BIT(28),
+       ACC_CONTROL_WR_ECC                              = BIT(30),
+       ACC_CONTROL_RD_ECC                              = BIT(31),
+};
+
+static inline u32 brcmnand_spare_area_mask(struct brcmnand_controller *ctrl)
+{
+       if (ctrl->nand_version >= 0x0600)
+               return GENMASK(6, 0);
+       else
+               return GENMASK(5, 0);
+}
+
+#define NAND_ACC_CONTROL_ECC_SHIFT     16
+
+static inline u32 brcmnand_ecc_level_mask(struct brcmnand_controller *ctrl)
+{
+       u32 mask = (ctrl->nand_version >= 0x0600) ? 0x1f : 0x0f;
+
+       return mask << NAND_ACC_CONTROL_ECC_SHIFT;
+}
+
+static void brcmnand_set_ecc_enabled(struct brcmnand_host *host, int en)
+{
+       struct brcmnand_controller *ctrl = host->ctrl;
+       u16 offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_ACC_CONTROL);
+       u32 acc_control = nand_readreg(ctrl, offs);
+       u32 ecc_flags = ACC_CONTROL_WR_ECC | ACC_CONTROL_RD_ECC;
+
+       if (en) {
+               acc_control |= ecc_flags; /* enable RD/WR ECC */
+               acc_control |= host->hwcfg.ecc_level
+                              << NAND_ACC_CONTROL_ECC_SHIFT;
+       } else {
+               acc_control &= ~ecc_flags; /* disable RD/WR ECC */
+               acc_control &= ~brcmnand_ecc_level_mask(ctrl);
+       }
+
+       nand_writereg(ctrl, offs, acc_control);
+}
+
+static inline int brcmnand_sector_1k_shift(struct brcmnand_controller *ctrl)
+{
+       if (ctrl->nand_version >= 0x0600)
+               return 7;
+       else if (ctrl->nand_version >= 0x0500)
+               return 6;
+       else
+               return -1;
+}
+
+static int brcmnand_get_sector_size_1k(struct brcmnand_host *host)
+{
+       struct brcmnand_controller *ctrl = host->ctrl;
+       int shift = brcmnand_sector_1k_shift(ctrl);
+       u16 acc_control_offs = brcmnand_cs_offset(ctrl, host->cs,
+                                                 BRCMNAND_CS_ACC_CONTROL);
+
+       if (shift < 0)
+               return 0;
+
+       return (nand_readreg(ctrl, acc_control_offs) >> shift) & 0x1;
+}
+
+static void brcmnand_set_sector_size_1k(struct brcmnand_host *host, int val)
+{
+       struct brcmnand_controller *ctrl = host->ctrl;
+       int shift = brcmnand_sector_1k_shift(ctrl);
+       u16 acc_control_offs = brcmnand_cs_offset(ctrl, host->cs,
+                                                 BRCMNAND_CS_ACC_CONTROL);
+       u32 tmp;
+
+       if (shift < 0)
+               return;
+
+       tmp = nand_readreg(ctrl, acc_control_offs);
+       tmp &= ~(1 << shift);
+       tmp |= (!!val) << shift;
+       nand_writereg(ctrl, acc_control_offs, tmp);
+}
+
+/***********************************************************************
+ * CS_NAND_SELECT
+ ***********************************************************************/
+
+enum {
+       CS_SELECT_NAND_WP                       = BIT(29),
+       CS_SELECT_AUTO_DEVICE_ID_CFG            = BIT(30),
+};
+
+static inline void brcmnand_set_wp(struct brcmnand_controller *ctrl, bool en)
+{
+       u32 val = en ? CS_SELECT_NAND_WP : 0;
+
+       brcmnand_rmw_reg(ctrl, BRCMNAND_CS_SELECT, CS_SELECT_NAND_WP, 0, val);
+}
+
+/***********************************************************************
+ * Flash DMA
+ ***********************************************************************/
+
+enum flash_dma_reg {
+       FLASH_DMA_REVISION              = 0x00,
+       FLASH_DMA_FIRST_DESC            = 0x04,
+       FLASH_DMA_FIRST_DESC_EXT        = 0x08,
+       FLASH_DMA_CTRL                  = 0x0c,
+       FLASH_DMA_MODE                  = 0x10,
+       FLASH_DMA_STATUS                = 0x14,
+       FLASH_DMA_INTERRUPT_DESC        = 0x18,
+       FLASH_DMA_INTERRUPT_DESC_EXT    = 0x1c,
+       FLASH_DMA_ERROR_STATUS          = 0x20,
+       FLASH_DMA_CURRENT_DESC          = 0x24,
+       FLASH_DMA_CURRENT_DESC_EXT      = 0x28,
+};
+
+static inline bool has_flash_dma(struct brcmnand_controller *ctrl)
+{
+       return ctrl->flash_dma_base;
+}
+
+static inline bool flash_dma_buf_ok(const void *buf)
+{
+       return buf && !is_vmalloc_addr(buf) &&
+               likely(IS_ALIGNED((uintptr_t)buf, 4));
+}
+
+static inline void flash_dma_writel(struct brcmnand_controller *ctrl, u8 offs,
+                                   u32 val)
+{
+       brcmnand_writel(val, ctrl->flash_dma_base + offs);
+}
+
+static inline u32 flash_dma_readl(struct brcmnand_controller *ctrl, u8 offs)
+{
+       return brcmnand_readl(ctrl->flash_dma_base + offs);
+}
+
+/* Low-level operation types: command, address, write, or read */
+enum brcmnand_llop_type {
+       LL_OP_CMD,
+       LL_OP_ADDR,
+       LL_OP_WR,
+       LL_OP_RD,
+};
+
+/***********************************************************************
+ * Internal support functions
+ ***********************************************************************/
+
+static inline bool is_hamming_ecc(struct brcmnand_cfg *cfg)
+{
+       return cfg->sector_size_1k == 0 && cfg->spare_area_size == 16 &&
+               cfg->ecc_level == 15;
+}
+
+/*
+ * Returns a nand_ecclayout strucutre for the given layout/configuration.
+ * Returns NULL on failure.
+ */
+static struct nand_ecclayout *brcmnand_create_layout(int ecc_level,
+                                                    struct brcmnand_host *host)
+{
+       struct brcmnand_cfg *cfg = &host->hwcfg;
+       int i, j;
+       struct nand_ecclayout *layout;
+       int req;
+       int sectors;
+       int sas;
+       int idx1, idx2;
+
+       layout = devm_kzalloc(&host->pdev->dev, sizeof(*layout), GFP_KERNEL);
+       if (!layout)
+               return NULL;
+
+       sectors = cfg->page_size / (512 << cfg->sector_size_1k);
+       sas = cfg->spare_area_size << cfg->sector_size_1k;
+
+       /* Hamming */
+       if (is_hamming_ecc(cfg)) {
+               for (i = 0, idx1 = 0, idx2 = 0; i < sectors; i++) {
+                       /* First sector of each page may have BBI */
+                       if (i == 0) {
+                               layout->oobfree[idx2].offset = i * sas + 1;
+                               /* Small-page NAND use byte 6 for BBI */
+                               if (cfg->page_size == 512)
+                                       layout->oobfree[idx2].offset--;
+                               layout->oobfree[idx2].length = 5;
+                       } else {
+                               layout->oobfree[idx2].offset = i * sas;
+                               layout->oobfree[idx2].length = 6;
+                       }
+                       idx2++;
+                       layout->eccpos[idx1++] = i * sas + 6;
+                       layout->eccpos[idx1++] = i * sas + 7;
+                       layout->eccpos[idx1++] = i * sas + 8;
+                       layout->oobfree[idx2].offset = i * sas + 9;
+                       layout->oobfree[idx2].length = 7;
+                       idx2++;
+                       /* Leave zero-terminated entry for OOBFREE */
+                       if (idx1 >= MTD_MAX_ECCPOS_ENTRIES_LARGE ||
+                                   idx2 >= MTD_MAX_OOBFREE_ENTRIES_LARGE - 1)
+                               break;
+               }
+               goto out;
+       }
+
+       /*
+        * CONTROLLER_VERSION:
+        *   < v5.0: ECC_REQ = ceil(BCH_T * 13/8)
+        *  >= v5.0: ECC_REQ = ceil(BCH_T * 14/8)
+        * But we will just be conservative.
+        */
+       req = DIV_ROUND_UP(ecc_level * 14, 8);
+       if (req >= sas) {
+               dev_err(&host->pdev->dev,
+                       "error: ECC too large for OOB (ECC bytes %d, spare sector %d)\n",
+                       req, sas);
+               return NULL;
+       }
+
+       layout->eccbytes = req * sectors;
+       for (i = 0, idx1 = 0, idx2 = 0; i < sectors; i++) {
+               for (j = sas - req; j < sas && idx1 <
+                               MTD_MAX_ECCPOS_ENTRIES_LARGE; j++, idx1++)
+                       layout->eccpos[idx1] = i * sas + j;
+
+               /* First sector of each page may have BBI */
+               if (i == 0) {
+                       if (cfg->page_size == 512 && (sas - req >= 6)) {
+                               /* Small-page NAND use byte 6 for BBI */
+                               layout->oobfree[idx2].offset = 0;
+                               layout->oobfree[idx2].length = 5;
+                               idx2++;
+                               if (sas - req > 6) {
+                                       layout->oobfree[idx2].offset = 6;
+                                       layout->oobfree[idx2].length =
+                                               sas - req - 6;
+                                       idx2++;
+                               }
+                       } else if (sas > req + 1) {
+                               layout->oobfree[idx2].offset = i * sas + 1;
+                               layout->oobfree[idx2].length = sas - req - 1;
+                               idx2++;
+                       }
+               } else if (sas > req) {
+                       layout->oobfree[idx2].offset = i * sas;
+                       layout->oobfree[idx2].length = sas - req;
+                       idx2++;
+               }
+               /* Leave zero-terminated entry for OOBFREE */
+               if (idx1 >= MTD_MAX_ECCPOS_ENTRIES_LARGE ||
+                               idx2 >= MTD_MAX_OOBFREE_ENTRIES_LARGE - 1)
+                       break;
+       }
+out:
+       /* Sum available OOB */
+       for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES_LARGE; i++)
+               layout->oobavail += layout->oobfree[i].length;
+       return layout;
+}
+
+static struct nand_ecclayout *brcmstb_choose_ecc_layout(
+               struct brcmnand_host *host)
+{
+       struct nand_ecclayout *layout;
+       struct brcmnand_cfg *p = &host->hwcfg;
+       unsigned int ecc_level = p->ecc_level;
+
+       if (p->sector_size_1k)
+               ecc_level <<= 1;
+
+       layout = brcmnand_create_layout(ecc_level, host);
+       if (!layout) {
+               dev_err(&host->pdev->dev,
+                               "no proper ecc_layout for this NAND cfg\n");
+               return NULL;
+       }
+
+       return layout;
+}
+
+static void brcmnand_wp(struct mtd_info *mtd, int wp)
+{
+       struct nand_chip *chip = mtd->priv;
+       struct brcmnand_host *host = chip->priv;
+       struct brcmnand_controller *ctrl = host->ctrl;
+
+       if ((ctrl->features & BRCMNAND_HAS_WP) && wp_on == 1) {
+               static int old_wp = -1;
+
+               if (old_wp != wp) {
+                       dev_dbg(ctrl->dev, "WP %s\n", wp ? "on" : "off");
+                       old_wp = wp;
+               }
+               brcmnand_set_wp(ctrl, wp);
+       }
+}
+
+/* Helper functions for reading and writing OOB registers */
+static inline u8 oob_reg_read(struct brcmnand_controller *ctrl, u32 offs)
+{
+       u16 offset0, offset10, reg_offs;
+
+       offset0 = ctrl->reg_offsets[BRCMNAND_OOB_READ_BASE];
+       offset10 = ctrl->reg_offsets[BRCMNAND_OOB_READ_10_BASE];
+
+       if (offs >= ctrl->max_oob)
+               return 0x77;
+
+       if (offs >= 16 && offset10)
+               reg_offs = offset10 + ((offs - 0x10) & ~0x03);
+       else
+               reg_offs = offset0 + (offs & ~0x03);
+
+       return nand_readreg(ctrl, reg_offs) >> (24 - ((offs & 0x03) << 3));
+}
+
+static inline void oob_reg_write(struct brcmnand_controller *ctrl, u32 offs,
+                                u32 data)
+{
+       u16 offset0, offset10, reg_offs;
+
+       offset0 = ctrl->reg_offsets[BRCMNAND_OOB_WRITE_BASE];
+       offset10 = ctrl->reg_offsets[BRCMNAND_OOB_WRITE_10_BASE];
+
+       if (offs >= ctrl->max_oob)
+               return;
+
+       if (offs >= 16 && offset10)
+               reg_offs = offset10 + ((offs - 0x10) & ~0x03);
+       else
+               reg_offs = offset0 + (offs & ~0x03);
+
+       nand_writereg(ctrl, reg_offs, data);
+}
+
+/*
+ * read_oob_from_regs - read data from OOB registers
+ * @ctrl: NAND controller
+ * @i: sub-page sector index
+ * @oob: buffer to read to
+ * @sas: spare area sector size (i.e., OOB size per FLASH_CACHE)
+ * @sector_1k: 1 for 1KiB sectors, 0 for 512B, other values are illegal
+ */
+static int read_oob_from_regs(struct brcmnand_controller *ctrl, int i, u8 *oob,
+                             int sas, int sector_1k)
+{
+       int tbytes = sas << sector_1k;
+       int j;
+
+       /* Adjust OOB values for 1K sector size */
+       if (sector_1k && (i & 0x01))
+               tbytes = max(0, tbytes - (int)ctrl->max_oob);
+       tbytes = min_t(int, tbytes, ctrl->max_oob);
+
+       for (j = 0; j < tbytes; j++)
+               oob[j] = oob_reg_read(ctrl, j);
+       return tbytes;
+}
+
+/*
+ * write_oob_to_regs - write data to OOB registers
+ * @i: sub-page sector index
+ * @oob: buffer to write from
+ * @sas: spare area sector size (i.e., OOB size per FLASH_CACHE)
+ * @sector_1k: 1 for 1KiB sectors, 0 for 512B, other values are illegal
+ */
+static int write_oob_to_regs(struct brcmnand_controller *ctrl, int i,
+                            const u8 *oob, int sas, int sector_1k)
+{
+       int tbytes = sas << sector_1k;
+       int j;
+
+       /* Adjust OOB values for 1K sector size */
+       if (sector_1k && (i & 0x01))
+               tbytes = max(0, tbytes - (int)ctrl->max_oob);
+       tbytes = min_t(int, tbytes, ctrl->max_oob);
+
+       for (j = 0; j < tbytes; j += 4)
+               oob_reg_write(ctrl, j,
+                               (oob[j + 0] << 24) |
+                               (oob[j + 1] << 16) |
+                               (oob[j + 2] <<  8) |
+                               (oob[j + 3] <<  0));
+       return tbytes;
+}
+
+static irqreturn_t brcmnand_ctlrdy_irq(int irq, void *data)
+{
+       struct brcmnand_controller *ctrl = data;
+
+       /* Discard all NAND_CTLRDY interrupts during DMA */
+       if (ctrl->dma_pending)
+               return IRQ_HANDLED;
+
+       complete(&ctrl->done);
+       return IRQ_HANDLED;
+}
+
+/* Handle SoC-specific interrupt hardware */
+static irqreturn_t brcmnand_irq(int irq, void *data)
+{
+       struct brcmnand_controller *ctrl = data;
+
+       if (ctrl->soc->ctlrdy_ack(ctrl->soc))
+               return brcmnand_ctlrdy_irq(irq, data);
+
+       return IRQ_NONE;
+}
+
+static irqreturn_t brcmnand_dma_irq(int irq, void *data)
+{
+       struct brcmnand_controller *ctrl = data;
+
+       complete(&ctrl->dma_done);
+
+       return IRQ_HANDLED;
+}
+
+static void brcmnand_send_cmd(struct brcmnand_host *host, int cmd)
+{
+       struct brcmnand_controller *ctrl = host->ctrl;
+       u32 intfc;
+
+       dev_dbg(ctrl->dev, "send native cmd %d addr_lo 0x%x\n", cmd,
+               brcmnand_read_reg(ctrl, BRCMNAND_CMD_ADDRESS));
+       BUG_ON(ctrl->cmd_pending != 0);
+       ctrl->cmd_pending = cmd;
+
+       intfc = brcmnand_read_reg(ctrl, BRCMNAND_INTFC_STATUS);
+       BUG_ON(!(intfc & INTFC_CTLR_READY));
+
+       mb(); /* flush previous writes */
+       brcmnand_write_reg(ctrl, BRCMNAND_CMD_START,
+                          cmd << brcmnand_cmd_shift(ctrl));
+}
+
+/***********************************************************************
+ * NAND MTD API: read/program/erase
+ ***********************************************************************/
+
+static void brcmnand_cmd_ctrl(struct mtd_info *mtd, int dat,
+       unsigned int ctrl)
+{
+       /* intentionally left blank */
+}
+
+static int brcmnand_waitfunc(struct mtd_info *mtd, struct nand_chip *this)
+{
+       struct nand_chip *chip = mtd->priv;
+       struct brcmnand_host *host = chip->priv;
+       struct brcmnand_controller *ctrl = host->ctrl;
+       unsigned long timeo = msecs_to_jiffies(100);
+
+       dev_dbg(ctrl->dev, "wait on native cmd %d\n", ctrl->cmd_pending);
+       if (ctrl->cmd_pending &&
+                       wait_for_completion_timeout(&ctrl->done, timeo) <= 0) {
+               u32 cmd = brcmnand_read_reg(ctrl, BRCMNAND_CMD_START)
+                                       >> brcmnand_cmd_shift(ctrl);
+
+               dev_err_ratelimited(ctrl->dev,
+                       "timeout waiting for command %#02x\n", cmd);
+               dev_err_ratelimited(ctrl->dev, "intfc status %08x\n",
+                       brcmnand_read_reg(ctrl, BRCMNAND_INTFC_STATUS));
+       }
+       ctrl->cmd_pending = 0;
+       return brcmnand_read_reg(ctrl, BRCMNAND_INTFC_STATUS) &
+                                INTFC_FLASH_STATUS;
+}
+
+enum {
+       LLOP_RE                         = BIT(16),
+       LLOP_WE                         = BIT(17),
+       LLOP_ALE                        = BIT(18),
+       LLOP_CLE                        = BIT(19),
+       LLOP_RETURN_IDLE                = BIT(31),
+
+       LLOP_DATA_MASK                  = GENMASK(15, 0),
+};
+
+static int brcmnand_low_level_op(struct brcmnand_host *host,
+                                enum brcmnand_llop_type type, u32 data,
+                                bool last_op)
+{
+       struct mtd_info *mtd = &host->mtd;
+       struct nand_chip *chip = &host->chip;
+       struct brcmnand_controller *ctrl = host->ctrl;
+       u32 tmp;
+
+       tmp = data & LLOP_DATA_MASK;
+       switch (type) {
+       case LL_OP_CMD:
+               tmp |= LLOP_WE | LLOP_CLE;
+               break;
+       case LL_OP_ADDR:
+               /* WE | ALE */
+               tmp |= LLOP_WE | LLOP_ALE;
+               break;
+       case LL_OP_WR:
+               /* WE */
+               tmp |= LLOP_WE;
+               break;
+       case LL_OP_RD:
+               /* RE */
+               tmp |= LLOP_RE;
+               break;
+       }
+       if (last_op)
+               /* RETURN_IDLE */
+               tmp |= LLOP_RETURN_IDLE;
+
+       dev_dbg(ctrl->dev, "ll_op cmd %#x\n", tmp);
+
+       brcmnand_write_reg(ctrl, BRCMNAND_LL_OP, tmp);
+       (void)brcmnand_read_reg(ctrl, BRCMNAND_LL_OP);
+
+       brcmnand_send_cmd(host, CMD_LOW_LEVEL_OP);
+       return brcmnand_waitfunc(mtd, chip);
+}
+
+static void brcmnand_cmdfunc(struct mtd_info *mtd, unsigned command,
+                            int column, int page_addr)
+{
+       struct nand_chip *chip = mtd->priv;
+       struct brcmnand_host *host = chip->priv;
+       struct brcmnand_controller *ctrl = host->ctrl;
+       u64 addr = (u64)page_addr << chip->page_shift;
+       int native_cmd = 0;
+
+       if (command == NAND_CMD_READID || command == NAND_CMD_PARAM ||
+                       command == NAND_CMD_RNDOUT)
+               addr = (u64)column;
+       /* Avoid propagating a negative, don't-care address */
+       else if (page_addr < 0)
+               addr = 0;
+
+       dev_dbg(ctrl->dev, "cmd 0x%x addr 0x%llx\n", command,
+               (unsigned long long)addr);
+
+       host->last_cmd = command;
+       host->last_byte = 0;
+       host->last_addr = addr;
+
+       switch (command) {
+       case NAND_CMD_RESET:
+               native_cmd = CMD_FLASH_RESET;
+               break;
+       case NAND_CMD_STATUS:
+               native_cmd = CMD_STATUS_READ;
+               break;
+       case NAND_CMD_READID:
+               native_cmd = CMD_DEVICE_ID_READ;
+               break;
+       case NAND_CMD_READOOB:
+               native_cmd = CMD_SPARE_AREA_READ;
+               break;
+       case NAND_CMD_ERASE1:
+               native_cmd = CMD_BLOCK_ERASE;
+               brcmnand_wp(mtd, 0);
+               break;
+       case NAND_CMD_PARAM:
+               native_cmd = CMD_PARAMETER_READ;
+               break;
+       case NAND_CMD_SET_FEATURES:
+       case NAND_CMD_GET_FEATURES:
+               brcmnand_low_level_op(host, LL_OP_CMD, command, false);
+               brcmnand_low_level_op(host, LL_OP_ADDR, column, false);
+               break;
+       case NAND_CMD_RNDOUT:
+               native_cmd = CMD_PARAMETER_CHANGE_COL;
+               addr &= ~((u64)(FC_BYTES - 1));
+               /*
+                * HW quirk: PARAMETER_CHANGE_COL requires SECTOR_SIZE_1K=0
+                * NB: hwcfg.sector_size_1k may not be initialized yet
+                */
+               if (brcmnand_get_sector_size_1k(host)) {
+                       host->hwcfg.sector_size_1k =
+                               brcmnand_get_sector_size_1k(host);
+                       brcmnand_set_sector_size_1k(host, 0);
+               }
+               break;
+       }
+
+       if (!native_cmd)
+               return;
+
+       brcmnand_write_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS,
+               (host->cs << 16) | ((addr >> 32) & 0xffff));
+       (void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS);
+       brcmnand_write_reg(ctrl, BRCMNAND_CMD_ADDRESS, lower_32_bits(addr));
+       (void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_ADDRESS);
+
+       brcmnand_send_cmd(host, native_cmd);
+       brcmnand_waitfunc(mtd, chip);
+
+       if (native_cmd == CMD_PARAMETER_READ ||
+                       native_cmd == CMD_PARAMETER_CHANGE_COL) {
+               int i;
+
+               brcmnand_soc_data_bus_prepare(ctrl->soc);
+
+               /*
+                * Must cache the FLASH_CACHE now, since changes in
+                * SECTOR_SIZE_1K may invalidate it
+                */
+               for (i = 0; i < FC_WORDS; i++)
+                       ctrl->flash_cache[i] = brcmnand_read_fc(ctrl, i);
+
+               brcmnand_soc_data_bus_unprepare(ctrl->soc);
+
+               /* Cleanup from HW quirk: restore SECTOR_SIZE_1K */
+               if (host->hwcfg.sector_size_1k)
+                       brcmnand_set_sector_size_1k(host,
+                                                   host->hwcfg.sector_size_1k);
+       }
+
+       /* Re-enable protection is necessary only after erase */
+       if (command == NAND_CMD_ERASE1)
+               brcmnand_wp(mtd, 1);
+}
+
+static uint8_t brcmnand_read_byte(struct mtd_info *mtd)
+{
+       struct nand_chip *chip = mtd->priv;
+       struct brcmnand_host *host = chip->priv;
+       struct brcmnand_controller *ctrl = host->ctrl;
+       uint8_t ret = 0;
+       int addr, offs;
+
+       switch (host->last_cmd) {
+       case NAND_CMD_READID:
+               if (host->last_byte < 4)
+                       ret = brcmnand_read_reg(ctrl, BRCMNAND_ID) >>
+                               (24 - (host->last_byte << 3));
+               else if (host->last_byte < 8)
+                       ret = brcmnand_read_reg(ctrl, BRCMNAND_ID_EXT) >>
+                               (56 - (host->last_byte << 3));
+               break;
+
+       case NAND_CMD_READOOB:
+               ret = oob_reg_read(ctrl, host->last_byte);
+               break;
+
+       case NAND_CMD_STATUS:
+               ret = brcmnand_read_reg(ctrl, BRCMNAND_INTFC_STATUS) &
+                                       INTFC_FLASH_STATUS;
+               if (wp_on) /* hide WP status */
+                       ret |= NAND_STATUS_WP;
+               break;
+
+       case NAND_CMD_PARAM:
+       case NAND_CMD_RNDOUT:
+               addr = host->last_addr + host->last_byte;
+               offs = addr & (FC_BYTES - 1);
+
+               /* At FC_BYTES boundary, switch to next column */
+               if (host->last_byte > 0 && offs == 0)
+                       chip->cmdfunc(mtd, NAND_CMD_RNDOUT, addr, -1);
+
+               ret = ctrl->flash_cache[offs >> 2] >>
+                                       (24 - ((offs & 0x03) << 3));
+               break;
+       case NAND_CMD_GET_FEATURES:
+               if (host->last_byte >= ONFI_SUBFEATURE_PARAM_LEN) {
+                       ret = 0;
+               } else {
+                       bool last = host->last_byte ==
+                               ONFI_SUBFEATURE_PARAM_LEN - 1;
+                       brcmnand_low_level_op(host, LL_OP_RD, 0, last);
+                       ret = brcmnand_read_reg(ctrl, BRCMNAND_LL_RDATA) & 0xff;
+               }
+       }
+
+       dev_dbg(ctrl->dev, "read byte = 0x%02x\n", ret);
+       host->last_byte++;
+
+       return ret;
+}
+
+static void brcmnand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+       int i;
+
+       for (i = 0; i < len; i++, buf++)
+               *buf = brcmnand_read_byte(mtd);
+}
+
+static void brcmnand_write_buf(struct mtd_info *mtd, const uint8_t *buf,
+                                  int len)
+{
+       int i;
+       struct nand_chip *chip = mtd->priv;
+       struct brcmnand_host *host = chip->priv;
+
+       switch (host->last_cmd) {
+       case NAND_CMD_SET_FEATURES:
+               for (i = 0; i < len; i++)
+                       brcmnand_low_level_op(host, LL_OP_WR, buf[i],
+                                                 (i + 1) == len);
+               break;
+       default:
+               BUG();
+               break;
+       }
+}
+
+/**
+ * Construct a FLASH_DMA descriptor as part of a linked list. You must know the
+ * following ahead of time:
+ *  - Is this descriptor the beginning or end of a linked list?
+ *  - What is the (DMA) address of the next descriptor in the linked list?
+ */
+static int brcmnand_fill_dma_desc(struct brcmnand_host *host,
+                                 struct brcm_nand_dma_desc *desc, u64 addr,
+                                 dma_addr_t buf, u32 len, u8 dma_cmd,
+                                 bool begin, bool end,
+                                 dma_addr_t next_desc)
+{
+       memset(desc, 0, sizeof(*desc));
+       /* Descriptors are written in native byte order (wordwise) */
+       desc->next_desc = lower_32_bits(next_desc);
+       desc->next_desc_ext = upper_32_bits(next_desc);
+       desc->cmd_irq = (dma_cmd << 24) |
+               (end ? (0x03 << 8) : 0) | /* IRQ | STOP */
+               (!!begin) | ((!!end) << 1); /* head, tail */
+#ifdef CONFIG_CPU_BIG_ENDIAN
+       desc->cmd_irq |= 0x01 << 12;
+#endif
+       desc->dram_addr = lower_32_bits(buf);
+       desc->dram_addr_ext = upper_32_bits(buf);
+       desc->tfr_len = len;
+       desc->total_len = len;
+       desc->flash_addr = lower_32_bits(addr);
+       desc->flash_addr_ext = upper_32_bits(addr);
+       desc->cs = host->cs;
+       desc->status_valid = 0x01;
+       return 0;
+}
+
+/**
+ * Kick the FLASH_DMA engine, with a given DMA descriptor
+ */
+static void brcmnand_dma_run(struct brcmnand_host *host, dma_addr_t desc)
+{
+       struct brcmnand_controller *ctrl = host->ctrl;
+       unsigned long timeo = msecs_to_jiffies(100);
+
+       flash_dma_writel(ctrl, FLASH_DMA_FIRST_DESC, lower_32_bits(desc));
+       (void)flash_dma_readl(ctrl, FLASH_DMA_FIRST_DESC);
+       flash_dma_writel(ctrl, FLASH_DMA_FIRST_DESC_EXT, upper_32_bits(desc));
+       (void)flash_dma_readl(ctrl, FLASH_DMA_FIRST_DESC_EXT);
+
+       /* Start FLASH_DMA engine */
+       ctrl->dma_pending = true;
+       mb(); /* flush previous writes */
+       flash_dma_writel(ctrl, FLASH_DMA_CTRL, 0x03); /* wake | run */
+
+       if (wait_for_completion_timeout(&ctrl->dma_done, timeo) <= 0) {
+               dev_err(ctrl->dev,
+                               "timeout waiting for DMA; status %#x, error status %#x\n",
+                               flash_dma_readl(ctrl, FLASH_DMA_STATUS),
+                               flash_dma_readl(ctrl, FLASH_DMA_ERROR_STATUS));
+       }
+       ctrl->dma_pending = false;
+       flash_dma_writel(ctrl, FLASH_DMA_CTRL, 0); /* force stop */
+}
+
+static int brcmnand_dma_trans(struct brcmnand_host *host, u64 addr, u32 *buf,
+                             u32 len, u8 dma_cmd)
+{
+       struct brcmnand_controller *ctrl = host->ctrl;
+       dma_addr_t buf_pa;
+       int dir = dma_cmd == CMD_PAGE_READ ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
+
+       buf_pa = dma_map_single(ctrl->dev, buf, len, dir);
+       if (dma_mapping_error(ctrl->dev, buf_pa)) {
+               dev_err(ctrl->dev, "unable to map buffer for DMA\n");
+               return -ENOMEM;
+       }
+
+       brcmnand_fill_dma_desc(host, ctrl->dma_desc, addr, buf_pa, len,
+                                  dma_cmd, true, true, 0);
+
+       brcmnand_dma_run(host, ctrl->dma_pa);
+
+       dma_unmap_single(ctrl->dev, buf_pa, len, dir);
+
+       if (ctrl->dma_desc->status_valid & FLASH_DMA_ECC_ERROR)
+               return -EBADMSG;
+       else if (ctrl->dma_desc->status_valid & FLASH_DMA_CORR_ERROR)
+               return -EUCLEAN;
+
+       return 0;
+}
+
+/*
+ * Assumes proper CS is already set
+ */
+static int brcmnand_read_by_pio(struct mtd_info *mtd, struct nand_chip *chip,
+                               u64 addr, unsigned int trans, u32 *buf,
+                               u8 *oob, u64 *err_addr)
+{
+       struct brcmnand_host *host = chip->priv;
+       struct brcmnand_controller *ctrl = host->ctrl;
+       int i, j, ret = 0;
+
+       /* Clear error addresses */
+       brcmnand_write_reg(ctrl, BRCMNAND_UNCORR_ADDR, 0);
+       brcmnand_write_reg(ctrl, BRCMNAND_CORR_ADDR, 0);
+
+       brcmnand_write_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS,
+                       (host->cs << 16) | ((addr >> 32) & 0xffff));
+       (void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS);
+
+       for (i = 0; i < trans; i++, addr += FC_BYTES) {
+               brcmnand_write_reg(ctrl, BRCMNAND_CMD_ADDRESS,
+                                  lower_32_bits(addr));
+               (void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_ADDRESS);
+               /* SPARE_AREA_READ does not use ECC, so just use PAGE_READ */
+               brcmnand_send_cmd(host, CMD_PAGE_READ);
+               brcmnand_waitfunc(mtd, chip);
+
+               if (likely(buf)) {
+                       brcmnand_soc_data_bus_prepare(ctrl->soc);
+
+                       for (j = 0; j < FC_WORDS; j++, buf++)
+                               *buf = brcmnand_read_fc(ctrl, j);
+
+                       brcmnand_soc_data_bus_unprepare(ctrl->soc);
+               }
+
+               if (oob)
+                       oob += read_oob_from_regs(ctrl, i, oob,
+                                       mtd->oobsize / trans,
+                                       host->hwcfg.sector_size_1k);
+
+               if (!ret) {
+                       *err_addr = brcmnand_read_reg(ctrl,
+                                       BRCMNAND_UNCORR_ADDR) |
+                               ((u64)(brcmnand_read_reg(ctrl,
+                                               BRCMNAND_UNCORR_EXT_ADDR)
+                                       & 0xffff) << 32);
+                       if (*err_addr)
+                               ret = -EBADMSG;
+               }
+
+               if (!ret) {
+                       *err_addr = brcmnand_read_reg(ctrl,
+                                       BRCMNAND_CORR_ADDR) |
+                               ((u64)(brcmnand_read_reg(ctrl,
+                                               BRCMNAND_CORR_EXT_ADDR)
+                                       & 0xffff) << 32);
+                       if (*err_addr)
+                               ret = -EUCLEAN;
+               }
+       }
+
+       return ret;
+}
+
+static int brcmnand_read(struct mtd_info *mtd, struct nand_chip *chip,
+                        u64 addr, unsigned int trans, u32 *buf, u8 *oob)
+{
+       struct brcmnand_host *host = chip->priv;
+       struct brcmnand_controller *ctrl = host->ctrl;
+       u64 err_addr = 0;
+       int err;
+
+       dev_dbg(ctrl->dev, "read %llx -> %p\n", (unsigned long long)addr, buf);
+
+       brcmnand_write_reg(ctrl, BRCMNAND_UNCORR_COUNT, 0);
+
+       if (has_flash_dma(ctrl) && !oob && flash_dma_buf_ok(buf)) {
+               err = brcmnand_dma_trans(host, addr, buf, trans * FC_BYTES,
+                                            CMD_PAGE_READ);
+               if (err) {
+                       if (mtd_is_bitflip_or_eccerr(err))
+                               err_addr = addr;
+                       else
+                               return -EIO;
+               }
+       } else {
+               if (oob)
+                       memset(oob, 0x99, mtd->oobsize);
+
+               err = brcmnand_read_by_pio(mtd, chip, addr, trans, buf,
+                                              oob, &err_addr);
+       }
+
+       if (mtd_is_eccerr(err)) {
+               dev_dbg(ctrl->dev, "uncorrectable error at 0x%llx\n",
+                       (unsigned long long)err_addr);
+               mtd->ecc_stats.failed++;
+               /* NAND layer expects zero on ECC errors */
+               return 0;
+       }
+
+       if (mtd_is_bitflip(err)) {
+               unsigned int corrected = brcmnand_count_corrected(ctrl);
+
+               dev_dbg(ctrl->dev, "corrected error at 0x%llx\n",
+                       (unsigned long long)err_addr);
+               mtd->ecc_stats.corrected += corrected;
+               /* Always exceed the software-imposed threshold */
+               return max(mtd->bitflip_threshold, corrected);
+       }
+
+       return 0;
+}
+
+static int brcmnand_read_page(struct mtd_info *mtd, struct nand_chip *chip,
+                             uint8_t *buf, int oob_required, int page)
+{
+       struct brcmnand_host *host = chip->priv;
+       u8 *oob = oob_required ? (u8 *)chip->oob_poi : NULL;
+
+       return brcmnand_read(mtd, chip, host->last_addr,
+                       mtd->writesize >> FC_SHIFT, (u32 *)buf, oob);
+}
+
+static int brcmnand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
+                                 uint8_t *buf, int oob_required, int page)
+{
+       struct brcmnand_host *host = chip->priv;
+       u8 *oob = oob_required ? (u8 *)chip->oob_poi : NULL;
+       int ret;
+
+       brcmnand_set_ecc_enabled(host, 0);
+       ret = brcmnand_read(mtd, chip, host->last_addr,
+                       mtd->writesize >> FC_SHIFT, (u32 *)buf, oob);
+       brcmnand_set_ecc_enabled(host, 1);
+       return ret;
+}
+
+static int brcmnand_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
+                            int page)
+{
+       return brcmnand_read(mtd, chip, (u64)page << chip->page_shift,
+                       mtd->writesize >> FC_SHIFT,
+                       NULL, (u8 *)chip->oob_poi);
+}
+
+static int brcmnand_read_oob_raw(struct mtd_info *mtd, struct nand_chip *chip,
+                                int page)
+{
+       struct brcmnand_host *host = chip->priv;
+
+       brcmnand_set_ecc_enabled(host, 0);
+       brcmnand_read(mtd, chip, (u64)page << chip->page_shift,
+               mtd->writesize >> FC_SHIFT,
+               NULL, (u8 *)chip->oob_poi);
+       brcmnand_set_ecc_enabled(host, 1);
+       return 0;
+}
+
+static int brcmnand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
+                                uint32_t data_offs, uint32_t readlen,
+                                uint8_t *bufpoi, int page)
+{
+       struct brcmnand_host *host = chip->priv;
+
+       return brcmnand_read(mtd, chip, host->last_addr + data_offs,
+                       readlen >> FC_SHIFT, (u32 *)bufpoi, NULL);
+}
+
+static int brcmnand_write(struct mtd_info *mtd, struct nand_chip *chip,
+                         u64 addr, const u32 *buf, u8 *oob)
+{
+       struct brcmnand_host *host = chip->priv;
+       struct brcmnand_controller *ctrl = host->ctrl;
+       unsigned int i, j, trans = mtd->writesize >> FC_SHIFT;
+       int status, ret = 0;
+
+       dev_dbg(ctrl->dev, "write %llx <- %p\n", (unsigned long long)addr, buf);
+
+       if (unlikely((u32)buf & 0x03)) {
+               dev_warn(ctrl->dev, "unaligned buffer: %p\n", buf);
+               buf = (u32 *)((u32)buf & ~0x03);
+       }
+
+       brcmnand_wp(mtd, 0);
+
+       for (i = 0; i < ctrl->max_oob; i += 4)
+               oob_reg_write(ctrl, i, 0xffffffff);
+
+       if (has_flash_dma(ctrl) && !oob && flash_dma_buf_ok(buf)) {
+               if (brcmnand_dma_trans(host, addr, (u32 *)buf,
+                                       mtd->writesize, CMD_PROGRAM_PAGE))
+                       ret = -EIO;
+               goto out;
+       }
+
+       brcmnand_write_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS,
+                       (host->cs << 16) | ((addr >> 32) & 0xffff));
+       (void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS);
+
+       for (i = 0; i < trans; i++, addr += FC_BYTES) {
+               /* full address MUST be set before populating FC */
+               brcmnand_write_reg(ctrl, BRCMNAND_CMD_ADDRESS,
+                                  lower_32_bits(addr));
+               (void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_ADDRESS);
+
+               if (buf) {
+                       brcmnand_soc_data_bus_prepare(ctrl->soc);
+
+                       for (j = 0; j < FC_WORDS; j++, buf++)
+                               brcmnand_write_fc(ctrl, j, *buf);
+
+                       brcmnand_soc_data_bus_unprepare(ctrl->soc);
+               } else if (oob) {
+                       for (j = 0; j < FC_WORDS; j++)
+                               brcmnand_write_fc(ctrl, j, 0xffffffff);
+               }
+
+               if (oob) {
+                       oob += write_oob_to_regs(ctrl, i, oob,
+                                       mtd->oobsize / trans,
+                                       host->hwcfg.sector_size_1k);
+               }
+
+               /* we cannot use SPARE_AREA_PROGRAM when PARTIAL_PAGE_EN=0 */
+               brcmnand_send_cmd(host, CMD_PROGRAM_PAGE);
+               status = brcmnand_waitfunc(mtd, chip);
+
+               if (status & NAND_STATUS_FAIL) {
+                       dev_info(ctrl->dev, "program failed at %llx\n",
+                               (unsigned long long)addr);
+                       ret = -EIO;
+                       goto out;
+               }
+       }
+out:
+       brcmnand_wp(mtd, 1);
+       return ret;
+}
+
+static int brcmnand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
+                              const uint8_t *buf, int oob_required)
+{
+       struct brcmnand_host *host = chip->priv;
+       void *oob = oob_required ? chip->oob_poi : NULL;
+
+       brcmnand_write(mtd, chip, host->last_addr, (const u32 *)buf, oob);
+       return 0;
+}
+
+static int brcmnand_write_page_raw(struct mtd_info *mtd,
+                                  struct nand_chip *chip, const uint8_t *buf,
+                                  int oob_required)
+{
+       struct brcmnand_host *host = chip->priv;
+       void *oob = oob_required ? chip->oob_poi : NULL;
+
+       brcmnand_set_ecc_enabled(host, 0);
+       brcmnand_write(mtd, chip, host->last_addr, (const u32 *)buf, oob);
+       brcmnand_set_ecc_enabled(host, 1);
+       return 0;
+}
+
+static int brcmnand_write_oob(struct mtd_info *mtd, struct nand_chip *chip,
+                                 int page)
+{
+       return brcmnand_write(mtd, chip, (u64)page << chip->page_shift,
+                                 NULL, chip->oob_poi);
+}
+
+static int brcmnand_write_oob_raw(struct mtd_info *mtd, struct nand_chip *chip,
+                                 int page)
+{
+       struct brcmnand_host *host = chip->priv;
+       int ret;
+
+       brcmnand_set_ecc_enabled(host, 0);
+       ret = brcmnand_write(mtd, chip, (u64)page << chip->page_shift, NULL,
+                                (u8 *)chip->oob_poi);
+       brcmnand_set_ecc_enabled(host, 1);
+
+       return ret;
+}
+
+/***********************************************************************
+ * Per-CS setup (1 NAND device)
+ ***********************************************************************/
+
+static int brcmnand_set_cfg(struct brcmnand_host *host,
+                           struct brcmnand_cfg *cfg)
+{
+       struct brcmnand_controller *ctrl = host->ctrl;
+       struct nand_chip *chip = &host->chip;
+       u16 cfg_offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_CFG);
+       u16 cfg_ext_offs = brcmnand_cs_offset(ctrl, host->cs,
+                       BRCMNAND_CS_CFG_EXT);
+       u16 acc_control_offs = brcmnand_cs_offset(ctrl, host->cs,
+                       BRCMNAND_CS_ACC_CONTROL);
+       u8 block_size = 0, page_size = 0, device_size = 0;
+       u32 tmp;
+
+       if (ctrl->block_sizes) {
+               int i, found;
+
+               for (i = 0, found = 0; ctrl->block_sizes[i]; i++)
+                       if (ctrl->block_sizes[i] * 1024 == cfg->block_size) {
+                               block_size = i;
+                               found = 1;
+                       }
+               if (!found) {
+                       dev_warn(ctrl->dev, "invalid block size %u\n",
+                                       cfg->block_size);
+                       return -EINVAL;
+               }
+       } else {
+               block_size = ffs(cfg->block_size) - ffs(BRCMNAND_MIN_BLOCKSIZE);
+       }
+
+       if (cfg->block_size < BRCMNAND_MIN_BLOCKSIZE || (ctrl->max_block_size &&
+                               cfg->block_size > ctrl->max_block_size)) {
+               dev_warn(ctrl->dev, "invalid block size %u\n",
+                               cfg->block_size);
+               block_size = 0;
+       }
+
+       if (ctrl->page_sizes) {
+               int i, found;
+
+               for (i = 0, found = 0; ctrl->page_sizes[i]; i++)
+                       if (ctrl->page_sizes[i] == cfg->page_size) {
+                               page_size = i;
+                               found = 1;
+                       }
+               if (!found) {
+                       dev_warn(ctrl->dev, "invalid page size %u\n",
+                                       cfg->page_size);
+                       return -EINVAL;
+               }
+       } else {
+               page_size = ffs(cfg->page_size) - ffs(BRCMNAND_MIN_PAGESIZE);
+       }
+
+       if (cfg->page_size < BRCMNAND_MIN_PAGESIZE || (ctrl->max_page_size &&
+                               cfg->page_size > ctrl->max_page_size)) {
+               dev_warn(ctrl->dev, "invalid page size %u\n", cfg->page_size);
+               return -EINVAL;
+       }
+
+       if (fls64(cfg->device_size) < fls64(BRCMNAND_MIN_DEVSIZE)) {
+               dev_warn(ctrl->dev, "invalid device size 0x%llx\n",
+                       (unsigned long long)cfg->device_size);
+               return -EINVAL;
+       }
+       device_size = fls64(cfg->device_size) - fls64(BRCMNAND_MIN_DEVSIZE);
+
+       tmp = (cfg->blk_adr_bytes << 8) |
+               (cfg->col_adr_bytes << 12) |
+               (cfg->ful_adr_bytes << 16) |
+               (!!(cfg->device_width == 16) << 23) |
+               (device_size << 24);
+       if (cfg_offs == cfg_ext_offs) {
+               tmp |= (page_size << 20) | (block_size << 28);
+               nand_writereg(ctrl, cfg_offs, tmp);
+       } else {
+               nand_writereg(ctrl, cfg_offs, tmp);
+               tmp = page_size | (block_size << 4);
+               nand_writereg(ctrl, cfg_ext_offs, tmp);
+       }
+
+       tmp = nand_readreg(ctrl, acc_control_offs);
+       tmp &= ~brcmnand_ecc_level_mask(ctrl);
+       tmp |= cfg->ecc_level << NAND_ACC_CONTROL_ECC_SHIFT;
+       tmp &= ~brcmnand_spare_area_mask(ctrl);
+       tmp |= cfg->spare_area_size;
+       nand_writereg(ctrl, acc_control_offs, tmp);
+
+       brcmnand_set_sector_size_1k(host, cfg->sector_size_1k);
+
+       /* threshold = ceil(BCH-level * 0.75) */
+       brcmnand_wr_corr_thresh(host, DIV_ROUND_UP(chip->ecc.strength * 3, 4));
+
+       return 0;
+}
+
+static void brcmnand_print_cfg(char *buf, struct brcmnand_cfg *cfg)
+{
+       buf += sprintf(buf,
+               "%lluMiB total, %uKiB blocks, %u%s pages, %uB OOB, %u-bit",
+               (unsigned long long)cfg->device_size >> 20,
+               cfg->block_size >> 10,
+               cfg->page_size >= 1024 ? cfg->page_size >> 10 : cfg->page_size,
+               cfg->page_size >= 1024 ? "KiB" : "B",
+               cfg->spare_area_size, cfg->device_width);
+
+       /* Account for Hamming ECC and for BCH 512B vs 1KiB sectors */
+       if (is_hamming_ecc(cfg))
+               sprintf(buf, ", Hamming ECC");
+       else if (cfg->sector_size_1k)
+               sprintf(buf, ", BCH-%u (1KiB sector)", cfg->ecc_level << 1);
+       else
+               sprintf(buf, ", BCH-%u", cfg->ecc_level);
+}
+
+/*
+ * Minimum number of bytes to address a page. Calculated as:
+ *     roundup(log2(size / page-size) / 8)
+ *
+ * NB: the following does not "round up" for non-power-of-2 'size'; but this is
+ *     OK because many other things will break if 'size' is irregular...
+ */
+static inline int get_blk_adr_bytes(u64 size, u32 writesize)
+{
+       return ALIGN(ilog2(size) - ilog2(writesize), 8) >> 3;
+}
+
+static int brcmnand_setup_dev(struct brcmnand_host *host)
+{
+       struct mtd_info *mtd = &host->mtd;
+       struct nand_chip *chip = &host->chip;
+       struct brcmnand_controller *ctrl = host->ctrl;
+       struct brcmnand_cfg *cfg = &host->hwcfg;
+       char msg[128];
+       u32 offs, tmp, oob_sector;
+       int ret;
+
+       memset(cfg, 0, sizeof(*cfg));
+
+       ret = of_property_read_u32(chip->dn, "brcm,nand-oob-sector-size",
+                                  &oob_sector);
+       if (ret) {
+               /* Use detected size */
+               cfg->spare_area_size = mtd->oobsize /
+                                       (mtd->writesize >> FC_SHIFT);
+       } else {
+               cfg->spare_area_size = oob_sector;
+       }
+       if (cfg->spare_area_size > ctrl->max_oob)
+               cfg->spare_area_size = ctrl->max_oob;
+       /*
+        * Set oobsize to be consistent with controller's spare_area_size, as
+        * the rest is inaccessible.
+        */
+       mtd->oobsize = cfg->spare_area_size * (mtd->writesize >> FC_SHIFT);
+
+       cfg->device_size = mtd->size;
+       cfg->block_size = mtd->erasesize;
+       cfg->page_size = mtd->writesize;
+       cfg->device_width = (chip->options & NAND_BUSWIDTH_16) ? 16 : 8;
+       cfg->col_adr_bytes = 2;
+       cfg->blk_adr_bytes = get_blk_adr_bytes(mtd->size, mtd->writesize);
+
+       switch (chip->ecc.size) {
+       case 512:
+               if (chip->ecc.strength == 1) /* Hamming */
+                       cfg->ecc_level = 15;
+               else
+                       cfg->ecc_level = chip->ecc.strength;
+               cfg->sector_size_1k = 0;
+               break;
+       case 1024:
+               if (!(ctrl->features & BRCMNAND_HAS_1K_SECTORS)) {
+                       dev_err(ctrl->dev, "1KB sectors not supported\n");
+                       return -EINVAL;
+               }
+               if (chip->ecc.strength & 0x1) {
+                       dev_err(ctrl->dev,
+                               "odd ECC not supported with 1KB sectors\n");
+                       return -EINVAL;
+               }
+
+               cfg->ecc_level = chip->ecc.strength >> 1;
+               cfg->sector_size_1k = 1;
+               break;
+       default:
+               dev_err(ctrl->dev, "unsupported ECC size: %d\n",
+                       chip->ecc.size);
+               return -EINVAL;
+       }
+
+       cfg->ful_adr_bytes = cfg->blk_adr_bytes;
+       if (mtd->writesize > 512)
+               cfg->ful_adr_bytes += cfg->col_adr_bytes;
+       else
+               cfg->ful_adr_bytes += 1;
+
+       ret = brcmnand_set_cfg(host, cfg);
+       if (ret)
+               return ret;
+
+       brcmnand_set_ecc_enabled(host, 1);
+
+       brcmnand_print_cfg(msg, cfg);
+       dev_info(ctrl->dev, "detected %s\n", msg);
+
+       /* Configure ACC_CONTROL */
+       offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_ACC_CONTROL);
+       tmp = nand_readreg(ctrl, offs);
+       tmp &= ~ACC_CONTROL_PARTIAL_PAGE;
+       tmp &= ~ACC_CONTROL_RD_ERASED;
+       tmp &= ~ACC_CONTROL_FAST_PGM_RDIN;
+       if (ctrl->features & BRCMNAND_HAS_PREFETCH) {
+               /*
+                * FIXME: Flash DMA + prefetch may see spurious erased-page ECC
+                * errors
+                */
+               if (has_flash_dma(ctrl))
+                       tmp &= ~ACC_CONTROL_PREFETCH;
+               else
+                       tmp |= ACC_CONTROL_PREFETCH;
+       }
+       nand_writereg(ctrl, offs, tmp);
+
+       return 0;
+}
+
+static int brcmnand_init_cs(struct brcmnand_host *host)
+{
+       struct brcmnand_controller *ctrl = host->ctrl;
+       struct device_node *dn = host->of_node;
+       struct platform_device *pdev = host->pdev;
+       struct mtd_info *mtd;
+       struct nand_chip *chip;
+       int ret;
+       struct mtd_part_parser_data ppdata = { .of_node = dn };
+
+       ret = of_property_read_u32(dn, "reg", &host->cs);
+       if (ret) {
+               dev_err(&pdev->dev, "can't get chip-select\n");
+               return -ENXIO;
+       }
+
+       mtd = &host->mtd;
+       chip = &host->chip;
+
+       chip->dn = dn;
+       chip->priv = host;
+       mtd->priv = chip;
+       mtd->name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "brcmnand.%d",
+                                  host->cs);
+       mtd->owner = THIS_MODULE;
+       mtd->dev.parent = &pdev->dev;
+
+       chip->IO_ADDR_R = (void __iomem *)0xdeadbeef;
+       chip->IO_ADDR_W = (void __iomem *)0xdeadbeef;
+
+       chip->cmd_ctrl = brcmnand_cmd_ctrl;
+       chip->cmdfunc = brcmnand_cmdfunc;
+       chip->waitfunc = brcmnand_waitfunc;
+       chip->read_byte = brcmnand_read_byte;
+       chip->read_buf = brcmnand_read_buf;
+       chip->write_buf = brcmnand_write_buf;
+
+       chip->ecc.mode = NAND_ECC_HW;
+       chip->ecc.read_page = brcmnand_read_page;
+       chip->ecc.read_subpage = brcmnand_read_subpage;
+       chip->ecc.write_page = brcmnand_write_page;
+       chip->ecc.read_page_raw = brcmnand_read_page_raw;
+       chip->ecc.write_page_raw = brcmnand_write_page_raw;
+       chip->ecc.write_oob_raw = brcmnand_write_oob_raw;
+       chip->ecc.read_oob_raw = brcmnand_read_oob_raw;
+       chip->ecc.read_oob = brcmnand_read_oob;
+       chip->ecc.write_oob = brcmnand_write_oob;
+
+       chip->controller = &ctrl->controller;
+
+       if (nand_scan_ident(mtd, 1, NULL))
+               return -ENXIO;
+
+       chip->options |= NAND_NO_SUBPAGE_WRITE;
+       /*
+        * Avoid (for instance) kmap()'d buffers from JFFS2, which we can't DMA
+        * to/from, and have nand_base pass us a bounce buffer instead, as
+        * needed.
+        */
+       chip->options |= NAND_USE_BOUNCE_BUFFER;
+
+       if (of_get_nand_on_flash_bbt(dn))
+               chip->bbt_options |= NAND_BBT_USE_FLASH | NAND_BBT_NO_OOB;
+
+       if (brcmnand_setup_dev(host))
+               return -ENXIO;
+
+       chip->ecc.size = host->hwcfg.sector_size_1k ? 1024 : 512;
+       /* only use our internal HW threshold */
+       mtd->bitflip_threshold = 1;
+
+       chip->ecc.layout = brcmstb_choose_ecc_layout(host);
+       if (!chip->ecc.layout)
+               return -ENXIO;
+
+       if (nand_scan_tail(mtd))
+               return -ENXIO;
+
+       return mtd_device_parse_register(mtd, NULL, &ppdata, NULL, 0);
+}
+
+static void brcmnand_save_restore_cs_config(struct brcmnand_host *host,
+                                           int restore)
+{
+       struct brcmnand_controller *ctrl = host->ctrl;
+       u16 cfg_offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_CFG);
+       u16 cfg_ext_offs = brcmnand_cs_offset(ctrl, host->cs,
+                       BRCMNAND_CS_CFG_EXT);
+       u16 acc_control_offs = brcmnand_cs_offset(ctrl, host->cs,
+                       BRCMNAND_CS_ACC_CONTROL);
+       u16 t1_offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_TIMING1);
+       u16 t2_offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_TIMING2);
+
+       if (restore) {
+               nand_writereg(ctrl, cfg_offs, host->hwcfg.config);
+               if (cfg_offs != cfg_ext_offs)
+                       nand_writereg(ctrl, cfg_ext_offs,
+                                     host->hwcfg.config_ext);
+               nand_writereg(ctrl, acc_control_offs, host->hwcfg.acc_control);
+               nand_writereg(ctrl, t1_offs, host->hwcfg.timing_1);
+               nand_writereg(ctrl, t2_offs, host->hwcfg.timing_2);
+       } else {
+               host->hwcfg.config = nand_readreg(ctrl, cfg_offs);
+               if (cfg_offs != cfg_ext_offs)
+                       host->hwcfg.config_ext =
+                               nand_readreg(ctrl, cfg_ext_offs);
+               host->hwcfg.acc_control = nand_readreg(ctrl, acc_control_offs);
+               host->hwcfg.timing_1 = nand_readreg(ctrl, t1_offs);
+               host->hwcfg.timing_2 = nand_readreg(ctrl, t2_offs);
+       }
+}
+
+static int brcmnand_suspend(struct device *dev)
+{
+       struct brcmnand_controller *ctrl = dev_get_drvdata(dev);
+       struct brcmnand_host *host;
+
+       list_for_each_entry(host, &ctrl->host_list, node)
+               brcmnand_save_restore_cs_config(host, 0);
+
+       ctrl->nand_cs_nand_select = brcmnand_read_reg(ctrl, BRCMNAND_CS_SELECT);
+       ctrl->nand_cs_nand_xor = brcmnand_read_reg(ctrl, BRCMNAND_CS_XOR);
+       ctrl->corr_stat_threshold =
+               brcmnand_read_reg(ctrl, BRCMNAND_CORR_THRESHOLD);
+
+       if (has_flash_dma(ctrl))
+               ctrl->flash_dma_mode = flash_dma_readl(ctrl, FLASH_DMA_MODE);
+
+       return 0;
+}
+
+static int brcmnand_resume(struct device *dev)
+{
+       struct brcmnand_controller *ctrl = dev_get_drvdata(dev);
+       struct brcmnand_host *host;
+
+       if (has_flash_dma(ctrl)) {
+               flash_dma_writel(ctrl, FLASH_DMA_MODE, ctrl->flash_dma_mode);
+               flash_dma_writel(ctrl, FLASH_DMA_ERROR_STATUS, 0);
+       }
+
+       brcmnand_write_reg(ctrl, BRCMNAND_CS_SELECT, ctrl->nand_cs_nand_select);
+       brcmnand_write_reg(ctrl, BRCMNAND_CS_XOR, ctrl->nand_cs_nand_xor);
+       brcmnand_write_reg(ctrl, BRCMNAND_CORR_THRESHOLD,
+                       ctrl->corr_stat_threshold);
+       if (ctrl->soc) {
+               /* Clear/re-enable interrupt */
+               ctrl->soc->ctlrdy_ack(ctrl->soc);
+               ctrl->soc->ctlrdy_set_enabled(ctrl->soc, true);
+       }
+
+       list_for_each_entry(host, &ctrl->host_list, node) {
+               struct mtd_info *mtd = &host->mtd;
+               struct nand_chip *chip = mtd->priv;
+
+               brcmnand_save_restore_cs_config(host, 1);
+
+               /* Reset the chip, required by some chips after power-up */
+               chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
+       }
+
+       return 0;
+}
+
+const struct dev_pm_ops brcmnand_pm_ops = {
+       .suspend                = brcmnand_suspend,
+       .resume                 = brcmnand_resume,
+};
+EXPORT_SYMBOL_GPL(brcmnand_pm_ops);
+
+static const struct of_device_id brcmnand_of_match[] = {
+       { .compatible = "brcm,brcmnand-v4.0" },
+       { .compatible = "brcm,brcmnand-v5.0" },
+       { .compatible = "brcm,brcmnand-v6.0" },
+       { .compatible = "brcm,brcmnand-v6.1" },
+       { .compatible = "brcm,brcmnand-v7.0" },
+       { .compatible = "brcm,brcmnand-v7.1" },
+       {},
+};
+MODULE_DEVICE_TABLE(of, brcmnand_of_match);
+
+/***********************************************************************
+ * Platform driver setup (per controller)
+ ***********************************************************************/
+
+int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc)
+{
+       struct device *dev = &pdev->dev;
+       struct device_node *dn = dev->of_node, *child;
+       struct brcmnand_controller *ctrl;
+       struct resource *res;
+       int ret;
+
+       /* We only support device-tree instantiation */
+       if (!dn)
+               return -ENODEV;
+
+       if (!of_match_node(brcmnand_of_match, dn))
+               return -ENODEV;
+
+       ctrl = devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL);
+       if (!ctrl)
+               return -ENOMEM;
+
+       dev_set_drvdata(dev, ctrl);
+       ctrl->dev = dev;
+
+       init_completion(&ctrl->done);
+       init_completion(&ctrl->dma_done);
+       spin_lock_init(&ctrl->controller.lock);
+       init_waitqueue_head(&ctrl->controller.wq);
+       INIT_LIST_HEAD(&ctrl->host_list);
+
+       /* NAND register range */
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       ctrl->nand_base = devm_ioremap_resource(dev, res);
+       if (IS_ERR(ctrl->nand_base))
+               return PTR_ERR(ctrl->nand_base);
+
+       /* Initialize NAND revision */
+       ret = brcmnand_revision_init(ctrl);
+       if (ret)
+               return ret;
+
+       /*
+        * Most chips have this cache at a fixed offset within 'nand' block.
+        * Some must specify this region separately.
+        */
+       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand-cache");
+       if (res) {
+               ctrl->nand_fc = devm_ioremap_resource(dev, res);
+               if (IS_ERR(ctrl->nand_fc))
+                       return PTR_ERR(ctrl->nand_fc);
+       } else {
+               ctrl->nand_fc = ctrl->nand_base +
+                               ctrl->reg_offsets[BRCMNAND_FC_BASE];
+       }
+
+       /* FLASH_DMA */
+       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "flash-dma");
+       if (res) {
+               ctrl->flash_dma_base = devm_ioremap_resource(dev, res);
+               if (IS_ERR(ctrl->flash_dma_base))
+                       return PTR_ERR(ctrl->flash_dma_base);
+
+               flash_dma_writel(ctrl, FLASH_DMA_MODE, 1); /* linked-list */
+               flash_dma_writel(ctrl, FLASH_DMA_ERROR_STATUS, 0);
+
+               /* Allocate descriptor(s) */
+               ctrl->dma_desc = dmam_alloc_coherent(dev,
+                                                    sizeof(*ctrl->dma_desc),
+                                                    &ctrl->dma_pa, GFP_KERNEL);
+               if (!ctrl->dma_desc)
+                       return -ENOMEM;
+
+               ctrl->dma_irq = platform_get_irq(pdev, 1);
+               if ((int)ctrl->dma_irq < 0) {
+                       dev_err(dev, "missing FLASH_DMA IRQ\n");
+                       return -ENODEV;
+               }
+
+               ret = devm_request_irq(dev, ctrl->dma_irq,
+                               brcmnand_dma_irq, 0, DRV_NAME,
+                               ctrl);
+               if (ret < 0) {
+                       dev_err(dev, "can't allocate IRQ %d: error %d\n",
+                                       ctrl->dma_irq, ret);
+                       return ret;
+               }
+
+               dev_info(dev, "enabling FLASH_DMA\n");
+       }
+
+       /* Disable automatic device ID config, direct addressing */
+       brcmnand_rmw_reg(ctrl, BRCMNAND_CS_SELECT,
+                        CS_SELECT_AUTO_DEVICE_ID_CFG | 0xff, 0, 0);
+       /* Disable XOR addressing */
+       brcmnand_rmw_reg(ctrl, BRCMNAND_CS_XOR, 0xff, 0, 0);
+
+       if (ctrl->features & BRCMNAND_HAS_WP) {
+               /* Permanently disable write protection */
+               if (wp_on == 2)
+                       brcmnand_set_wp(ctrl, false);
+       } else {
+               wp_on = 0;
+       }
+
+       /* IRQ */
+       ctrl->irq = platform_get_irq(pdev, 0);
+       if ((int)ctrl->irq < 0) {
+               dev_err(dev, "no IRQ defined\n");
+               return -ENODEV;
+       }
+
+       /*
+        * Some SoCs integrate this controller (e.g., its interrupt bits) in
+        * interesting ways
+        */
+       if (soc) {
+               ctrl->soc = soc;
+
+               ret = devm_request_irq(dev, ctrl->irq, brcmnand_irq, 0,
+                                      DRV_NAME, ctrl);
+
+               /* Enable interrupt */
+               ctrl->soc->ctlrdy_ack(ctrl->soc);
+               ctrl->soc->ctlrdy_set_enabled(ctrl->soc, true);
+       } else {
+               /* Use standard interrupt infrastructure */
+               ret = devm_request_irq(dev, ctrl->irq, brcmnand_ctlrdy_irq, 0,
+                                      DRV_NAME, ctrl);
+       }
+       if (ret < 0) {
+               dev_err(dev, "can't allocate IRQ %d: error %d\n",
+                       ctrl->irq, ret);
+               return ret;
+       }
+
+       for_each_available_child_of_node(dn, child) {
+               if (of_device_is_compatible(child, "brcm,nandcs")) {
+                       struct brcmnand_host *host;
+
+                       host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL);
+                       if (!host)
+                               return -ENOMEM;
+                       host->pdev = pdev;
+                       host->ctrl = ctrl;
+                       host->of_node = child;
+
+                       ret = brcmnand_init_cs(host);
+                       if (ret)
+                               continue; /* Try all chip-selects */
+
+                       list_add_tail(&host->node, &ctrl->host_list);
+               }
+       }
+
+       /* No chip-selects could initialize properly */
+       if (list_empty(&ctrl->host_list))
+               return -ENODEV;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(brcmnand_probe);
+
+int brcmnand_remove(struct platform_device *pdev)
+{
+       struct brcmnand_controller *ctrl = dev_get_drvdata(&pdev->dev);
+       struct brcmnand_host *host;
+
+       list_for_each_entry(host, &ctrl->host_list, node)
+               nand_release(&host->mtd);
+
+       dev_set_drvdata(&pdev->dev, NULL);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(brcmnand_remove);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Kevin Cernekee");
+MODULE_AUTHOR("Brian Norris");
+MODULE_DESCRIPTION("NAND driver for Broadcom chips");
+MODULE_ALIAS("platform:brcmnand");
diff --git a/drivers/mtd/nand/brcmnand/brcmnand.h b/drivers/mtd/nand/brcmnand/brcmnand.h
new file mode 100644 (file)
index 0000000..a20c736
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Copyright © 2015 Broadcom Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License 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.
+ */
+
+#ifndef __BRCMNAND_H__
+#define __BRCMNAND_H__
+
+#include <linux/types.h>
+#include <linux/io.h>
+
+struct platform_device;
+struct dev_pm_ops;
+
+struct brcmnand_soc {
+       struct platform_device *pdev;
+       void *priv;
+       bool (*ctlrdy_ack)(struct brcmnand_soc *soc);
+       void (*ctlrdy_set_enabled)(struct brcmnand_soc *soc, bool en);
+       void (*prepare_data_bus)(struct brcmnand_soc *soc, bool prepare);
+};
+
+static inline void brcmnand_soc_data_bus_prepare(struct brcmnand_soc *soc)
+{
+       if (soc && soc->prepare_data_bus)
+               soc->prepare_data_bus(soc, true);
+}
+
+static inline void brcmnand_soc_data_bus_unprepare(struct brcmnand_soc *soc)
+{
+       if (soc && soc->prepare_data_bus)
+               soc->prepare_data_bus(soc, false);
+}
+
+static inline u32 brcmnand_readl(void __iomem *addr)
+{
+       /*
+        * MIPS endianness is configured by boot strap, which also reverses all
+        * bus endianness (i.e., big-endian CPU + big endian bus ==> native
+        * endian I/O).
+        *
+        * Other architectures (e.g., ARM) either do not support big endian, or
+        * else leave I/O in little endian mode.
+        */
+       if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(__BIG_ENDIAN))
+               return __raw_readl(addr);
+       else
+               return readl_relaxed(addr);
+}
+
+static inline void brcmnand_writel(u32 val, void __iomem *addr)
+{
+       /* See brcmnand_readl() comments */
+       if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(__BIG_ENDIAN))
+               __raw_writel(val, addr);
+       else
+               writel_relaxed(val, addr);
+}
+
+int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc);
+int brcmnand_remove(struct platform_device *pdev);
+
+extern const struct dev_pm_ops brcmnand_pm_ops;
+
+#endif /* __BRCMNAND_H__ */
diff --git a/drivers/mtd/nand/brcmnand/brcmstb_nand.c b/drivers/mtd/nand/brcmnand/brcmstb_nand.c
new file mode 100644 (file)
index 0000000..5c27107
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright © 2015 Broadcom Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include "brcmnand.h"
+
+static const struct of_device_id brcmstb_nand_of_match[] = {
+       { .compatible = "brcm,brcmnand" },
+       {},
+};
+MODULE_DEVICE_TABLE(of, brcmstb_nand_of_match);
+
+static int brcmstb_nand_probe(struct platform_device *pdev)
+{
+       return brcmnand_probe(pdev, NULL);
+}
+
+static struct platform_driver brcmstb_nand_driver = {
+       .probe                  = brcmstb_nand_probe,
+       .remove                 = brcmnand_remove,
+       .driver = {
+               .name           = "brcmstb_nand",
+               .pm             = &brcmnand_pm_ops,
+               .of_match_table = brcmstb_nand_of_match,
+       }
+};
+module_platform_driver(brcmstb_nand_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Brian Norris");
+MODULE_DESCRIPTION("NAND driver for Broadcom STB chips");
diff --git a/drivers/mtd/nand/brcmnand/iproc_nand.c b/drivers/mtd/nand/brcmnand/iproc_nand.c
new file mode 100644 (file)
index 0000000..683495c
--- /dev/null
@@ -0,0 +1,150 @@
+/*
+ * Copyright © 2015 Broadcom Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include "brcmnand.h"
+
+struct iproc_nand_soc_priv {
+       void __iomem *idm_base;
+       void __iomem *ext_base;
+       spinlock_t idm_lock;
+};
+
+#define IPROC_NAND_CTLR_READY_OFFSET       0x10
+#define IPROC_NAND_CTLR_READY              BIT(0)
+
+#define IPROC_NAND_IO_CTRL_OFFSET          0x00
+#define IPROC_NAND_APB_LE_MODE             BIT(24)
+#define IPROC_NAND_INT_CTRL_READ_ENABLE    BIT(6)
+
+static bool iproc_nand_intc_ack(struct brcmnand_soc *soc)
+{
+       struct iproc_nand_soc_priv *priv = soc->priv;
+       void __iomem *mmio = priv->ext_base + IPROC_NAND_CTLR_READY_OFFSET;
+       u32 val = brcmnand_readl(mmio);
+
+       if (val & IPROC_NAND_CTLR_READY) {
+               brcmnand_writel(IPROC_NAND_CTLR_READY, mmio);
+               return true;
+       }
+
+       return false;
+}
+
+static void iproc_nand_intc_set(struct brcmnand_soc *soc, bool en)
+{
+       struct iproc_nand_soc_priv *priv = soc->priv;
+       void __iomem *mmio = priv->idm_base + IPROC_NAND_IO_CTRL_OFFSET;
+       u32 val;
+       unsigned long flags;
+
+       spin_lock_irqsave(&priv->idm_lock, flags);
+
+       val = brcmnand_readl(mmio);
+
+       if (en)
+               val |= IPROC_NAND_INT_CTRL_READ_ENABLE;
+       else
+               val &= ~IPROC_NAND_INT_CTRL_READ_ENABLE;
+
+       brcmnand_writel(val, mmio);
+
+       spin_unlock_irqrestore(&priv->idm_lock, flags);
+}
+
+static void iproc_nand_apb_access(struct brcmnand_soc *soc, bool prepare)
+{
+       struct iproc_nand_soc_priv *priv = soc->priv;
+       void __iomem *mmio = priv->idm_base + IPROC_NAND_IO_CTRL_OFFSET;
+       u32 val;
+       unsigned long flags;
+
+       spin_lock_irqsave(&priv->idm_lock, flags);
+
+       val = brcmnand_readl(mmio);
+
+       if (prepare)
+               val |= IPROC_NAND_APB_LE_MODE;
+       else
+               val &= ~IPROC_NAND_APB_LE_MODE;
+
+       brcmnand_writel(val, mmio);
+
+       spin_unlock_irqrestore(&priv->idm_lock, flags);
+}
+
+static int iproc_nand_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct iproc_nand_soc_priv *priv;
+       struct brcmnand_soc *soc;
+       struct resource *res;
+
+       soc = devm_kzalloc(dev, sizeof(*soc), GFP_KERNEL);
+       if (!soc)
+               return -ENOMEM;
+
+       priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       spin_lock_init(&priv->idm_lock);
+
+       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "iproc-idm");
+       priv->idm_base = devm_ioremap_resource(dev, res);
+       if (IS_ERR(priv->idm_base))
+               return PTR_ERR(priv->idm_base);
+
+       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "iproc-ext");
+       priv->ext_base = devm_ioremap_resource(dev, res);
+       if (IS_ERR(priv->ext_base))
+               return PTR_ERR(priv->ext_base);
+
+       soc->pdev = pdev;
+       soc->priv = priv;
+       soc->ctlrdy_ack = iproc_nand_intc_ack;
+       soc->ctlrdy_set_enabled = iproc_nand_intc_set;
+       soc->prepare_data_bus = iproc_nand_apb_access;
+
+       return brcmnand_probe(pdev, soc);
+}
+
+static const struct of_device_id iproc_nand_of_match[] = {
+       { .compatible = "brcm,nand-iproc" },
+       {},
+};
+MODULE_DEVICE_TABLE(of, iproc_nand_of_match);
+
+static struct platform_driver iproc_nand_driver = {
+       .probe                  = iproc_nand_probe,
+       .remove                 = brcmnand_remove,
+       .driver = {
+               .name           = "iproc_nand",
+               .pm             = &brcmnand_pm_ops,
+               .of_match_table = iproc_nand_of_match,
+       }
+};
+module_platform_driver(iproc_nand_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Brian Norris");
+MODULE_AUTHOR("Ray Jui");
+MODULE_DESCRIPTION("NAND driver for Broadcom IPROC-based SoCs");
index 88109d375ae7f65546a2225e894e19ade98c41ba..aec6045058c7760d436e14ad865a64c1bcf38ec1 100644 (file)
@@ -237,17 +237,23 @@ static int __init cs553x_init_one(int cs, int mmio, unsigned long adr)
        /* Enable the following for a flash based bad block table */
        this->bbt_options = NAND_BBT_USE_FLASH;
 
+       new_mtd->name = kasprintf(GFP_KERNEL, "cs553x_nand_cs%d", cs);
+       if (!new_mtd->name) {
+               err = -ENOMEM;
+               goto out_ior;
+       }
+
        /* Scan to find existence of the device */
        if (nand_scan(new_mtd, 1)) {
                err = -ENXIO;
-               goto out_ior;
+               goto out_free;
        }
 
-       new_mtd->name = kasprintf(GFP_KERNEL, "cs553x_nand_cs%d", cs);
-
        cs553x_mtd[cs] = new_mtd;
        goto out;
 
+out_free:
+       kfree(new_mtd->name);
 out_ior:
        iounmap(this->IO_ADDR_R);
 out_mtd:
index f68a7bccecdc65878fac3ed3056ac499099992b7..7da266a5397990281e0a35625435168b2f11a78f 100644 (file)
@@ -69,6 +69,9 @@ struct doc_priv {
        int mh0_page;
        int mh1_page;
        struct mtd_info *nextdoc;
+
+       /* Handle the last stage of initialization (BBT scan, partitioning) */
+       int (*late_init)(struct mtd_info *mtd);
 };
 
 /* This is the syndrome computed by the HW ecc generator upon reading an empty
@@ -1294,14 +1297,11 @@ static int __init nftl_scan_bbt(struct mtd_info *mtd)
                this->bbt_md = NULL;
        }
 
-       /* It's safe to set bd=NULL below because NAND_BBT_CREATE is not set.
-          At least as nand_bbt.c is currently written. */
-       if ((ret = nand_scan_bbt(mtd, NULL)))
+       ret = this->scan_bbt(mtd);
+       if (ret)
                return ret;
-       mtd_device_register(mtd, NULL, 0);
-       if (!no_autopart)
-               mtd_device_register(mtd, parts, numparts);
-       return 0;
+
+       return mtd_device_register(mtd, parts, no_autopart ? 0 : numparts);
 }
 
 static int __init inftl_scan_bbt(struct mtd_info *mtd)
@@ -1344,10 +1344,10 @@ static int __init inftl_scan_bbt(struct mtd_info *mtd)
                this->bbt_md->pattern = "TBB_SYSM";
        }
 
-       /* It's safe to set bd=NULL below because NAND_BBT_CREATE is not set.
-          At least as nand_bbt.c is currently written. */
-       if ((ret = nand_scan_bbt(mtd, NULL)))
+       ret = this->scan_bbt(mtd);
+       if (ret)
                return ret;
+
        memset((char *)parts, 0, sizeof(parts));
        numparts = inftl_partscan(mtd, parts);
        /* At least for now, require the INFTL Media Header.  We could probably
@@ -1355,10 +1355,7 @@ static int __init inftl_scan_bbt(struct mtd_info *mtd)
           autopartitioning, but I want to give it more thought. */
        if (!numparts)
                return -EIO;
-       mtd_device_register(mtd, NULL, 0);
-       if (!no_autopart)
-               mtd_device_register(mtd, parts, numparts);
-       return 0;
+       return mtd_device_register(mtd, parts, no_autopart ? 0 : numparts);
 }
 
 static inline int __init doc2000_init(struct mtd_info *mtd)
@@ -1369,7 +1366,7 @@ static inline int __init doc2000_init(struct mtd_info *mtd)
        this->read_byte = doc2000_read_byte;
        this->write_buf = doc2000_writebuf;
        this->read_buf = doc2000_readbuf;
-       this->scan_bbt = nftl_scan_bbt;
+       doc->late_init = nftl_scan_bbt;
 
        doc->CDSNControl = CDSN_CTRL_FLASH_IO | CDSN_CTRL_ECC_IO;
        doc2000_count_chips(mtd);
@@ -1396,13 +1393,13 @@ static inline int __init doc2001_init(struct mtd_info *mtd)
                   can have multiple chips. */
                doc2000_count_chips(mtd);
                mtd->name = "DiskOnChip 2000 (INFTL Model)";
-               this->scan_bbt = inftl_scan_bbt;
+               doc->late_init = inftl_scan_bbt;
                return (4 * doc->chips_per_floor);
        } else {
                /* Bog-standard Millennium */
                doc->chips_per_floor = 1;
                mtd->name = "DiskOnChip Millennium";
-               this->scan_bbt = nftl_scan_bbt;
+               doc->late_init = nftl_scan_bbt;
                return 1;
        }
 }
@@ -1415,7 +1412,7 @@ static inline int __init doc2001plus_init(struct mtd_info *mtd)
        this->read_byte = doc2001plus_read_byte;
        this->write_buf = doc2001plus_writebuf;
        this->read_buf = doc2001plus_readbuf;
-       this->scan_bbt = inftl_scan_bbt;
+       doc->late_init = inftl_scan_bbt;
        this->cmd_ctrl = NULL;
        this->select_chip = doc2001plus_select_chip;
        this->cmdfunc = doc2001plus_command;
@@ -1591,6 +1588,8 @@ static int __init doc_probe(unsigned long physadr)
        nand->ecc.bytes         = 6;
        nand->ecc.strength      = 2;
        nand->bbt_options       = NAND_BBT_USE_FLASH;
+       /* Skip the automatic BBT scan so we can run it manually */
+       nand->options           |= NAND_SKIP_BBTSCAN;
 
        doc->physadr            = physadr;
        doc->virtadr            = virtadr;
@@ -1608,7 +1607,7 @@ static int __init doc_probe(unsigned long physadr)
        else
                numchips = doc2001_init(mtd);
 
-       if ((ret = nand_scan(mtd, numchips))) {
+       if ((ret = nand_scan(mtd, numchips)) || (ret = doc->late_init(mtd))) {
                /* DBB note: i believe nand_release is necessary here, as
                   buffers may have been allocated in nand_base.  Check with
                   Thomas. FIX ME! */
index e58af4bfa8c81d1752151adc8a8b86175351057e..793872f180657e65e7d82237853b511dd72e82b3 100644 (file)
@@ -562,6 +562,7 @@ static int dma_xfer(struct fsmc_nand_data *host, void *buffer, int len,
        dma_cookie_t cookie;
        unsigned long flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT;
        int ret;
+       unsigned long time_left;
 
        if (direction == DMA_TO_DEVICE)
                chan = host->write_dma_chan;
@@ -601,14 +602,13 @@ static int dma_xfer(struct fsmc_nand_data *host, void *buffer, int len,
 
        dma_async_issue_pending(chan);
 
-       ret =
+       time_left =
        wait_for_completion_timeout(&host->dma_access_complete,
                                msecs_to_jiffies(3000));
-       if (ret <= 0) {
+       if (time_left == 0) {
                dmaengine_terminate_all(chan);
                dev_err(host->dev, "wait_for_completion_timeout\n");
-               if (!ret)
-                       ret = -ETIMEDOUT;
+               ret = -ETIMEDOUT;
                goto unmap_dma;
        }
 
index 1f12e5bfbceda428ecb7650cc6bc0db1355d5124..2a49b53c8db96b03a3b56a7cd039502ebfee0c9d 100644 (file)
@@ -837,7 +837,7 @@ static int mpc5121_nfc_remove(struct platform_device *op)
        return 0;
 }
 
-static struct of_device_id mpc5121_nfc_match[] = {
+static const struct of_device_id mpc5121_nfc_match[] = {
        { .compatible = "fsl,mpc5121-nfc", },
        {},
 };
index 372e0e38f59b323f4388c6d96416ea5ea35f3c69..2426db88db36bf95f1f247eeae597ff69238c70d 100644 (file)
@@ -189,6 +189,7 @@ struct mxc_nand_host {
        int                     clk_act;
        int                     irq;
        int                     eccsize;
+       int                     used_oobsize;
        int                     active_cs;
 
        struct completion       op_completion;
@@ -280,12 +281,44 @@ static void memcpy32_fromio(void *trg, const void __iomem  *src, size_t size)
                *t++ = __raw_readl(s++);
 }
 
+static void memcpy16_fromio(void *trg, const void __iomem  *src, size_t size)
+{
+       int i;
+       u16 *t = trg;
+       const __iomem u16 *s = src;
+
+       /* We assume that src (IO) is always 32bit aligned */
+       if (PTR_ALIGN(trg, 4) == trg && IS_ALIGNED(size, 4)) {
+               memcpy32_fromio(trg, src, size);
+               return;
+       }
+
+       for (i = 0; i < (size >> 1); i++)
+               *t++ = __raw_readw(s++);
+}
+
 static inline void memcpy32_toio(void __iomem *trg, const void *src, int size)
 {
        /* __iowrite32_copy use 32bit size values so divide by 4 */
        __iowrite32_copy(trg, src, size / 4);
 }
 
+static void memcpy16_toio(void __iomem *trg, const void *src, int size)
+{
+       int i;
+       __iomem u16 *t = trg;
+       const u16 *s = src;
+
+       /* We assume that trg (IO) is always 32bit aligned */
+       if (PTR_ALIGN(src, 4) == src && IS_ALIGNED(size, 4)) {
+               memcpy32_toio(trg, src, size);
+               return;
+       }
+
+       for (i = 0; i < (size >> 1); i++)
+               __raw_writew(*s++, t++);
+}
+
 static int check_int_v3(struct mxc_nand_host *host)
 {
        uint32_t tmp;
@@ -807,32 +840,48 @@ static void mxc_nand_select_chip_v2(struct mtd_info *mtd, int chip)
 }
 
 /*
- * Function to transfer data to/from spare area.
+ * The controller splits a page into data chunks of 512 bytes + partial oob.
+ * There are writesize / 512 such chunks, the size of the partial oob parts is
+ * oobsize / #chunks rounded down to a multiple of 2. The last oob chunk then
+ * contains additionally the byte lost by rounding (if any).
+ * This function handles the needed shuffling between host->data_buf (which
+ * holds a page in natural order, i.e. writesize bytes data + oobsize bytes
+ * spare) and the NFC buffer.
  */
 static void copy_spare(struct mtd_info *mtd, bool bfrom)
 {
        struct nand_chip *this = mtd->priv;
        struct mxc_nand_host *host = this->priv;
-       u16 i, j;
-       u16 n = mtd->writesize >> 9;
+       u16 i, oob_chunk_size;
+       u16 num_chunks = mtd->writesize / 512;
+
        u8 *d = host->data_buf + mtd->writesize;
        u8 __iomem *s = host->spare0;
-       u16 t = host->devtype_data->spare_len;
+       u16 sparebuf_size = host->devtype_data->spare_len;
 
-       j = (mtd->oobsize / n >> 1) << 1;
+       /* size of oob chunk for all but possibly the last one */
+       oob_chunk_size = (host->used_oobsize / num_chunks) & ~1;
 
        if (bfrom) {
-               for (i = 0; i < n - 1; i++)
-                       memcpy32_fromio(d + i * j, s + i * t, j);
-
-               /* the last section */
-               memcpy32_fromio(d + i * j, s + i * t, mtd->oobsize - i * j);
+               for (i = 0; i < num_chunks - 1; i++)
+                       memcpy16_fromio(d + i * oob_chunk_size,
+                                       s + i * sparebuf_size,
+                                       oob_chunk_size);
+
+               /* the last chunk */
+               memcpy16_fromio(d + i * oob_chunk_size,
+                               s + i * sparebuf_size,
+                               host->used_oobsize - i * oob_chunk_size);
        } else {
-               for (i = 0; i < n - 1; i++)
-                       memcpy32_toio(&s[i * t], &d[i * j], j);
-
-               /* the last section */
-               memcpy32_toio(&s[i * t], &d[i * j], mtd->oobsize - i * j);
+               for (i = 0; i < num_chunks - 1; i++)
+                       memcpy16_toio(&s[i * sparebuf_size],
+                                     &d[i * oob_chunk_size],
+                                     oob_chunk_size);
+
+               /* the last chunk */
+               memcpy16_toio(&s[oob_chunk_size * sparebuf_size],
+                             &d[i * oob_chunk_size],
+                             host->used_oobsize - i * oob_chunk_size);
        }
 }
 
@@ -911,6 +960,23 @@ static int get_eccsize(struct mtd_info *mtd)
                return 8;
 }
 
+static void ecc_8bit_layout_4k(struct nand_ecclayout *layout)
+{
+       int i, j;
+
+       layout->eccbytes = 8*18;
+       for (i = 0; i < 8; i++)
+               for (j = 0; j < 18; j++)
+                       layout->eccpos[i*18 + j] = i*26 + j + 7;
+
+       layout->oobfree[0].offset = 2;
+       layout->oobfree[0].length = 4;
+       for (i = 1; i < 8; i++) {
+               layout->oobfree[i].offset = i*26;
+               layout->oobfree[i].length = 7;
+       }
+}
+
 static void preset_v1(struct mtd_info *mtd)
 {
        struct nand_chip *nand_chip = mtd->priv;
@@ -1350,7 +1416,7 @@ static inline int is_imx53_nfc(struct mxc_nand_host *host)
        return host->devtype_data == &imx53_nand_devtype_data;
 }
 
-static struct platform_device_id mxcnd_devtype[] = {
+static const struct platform_device_id mxcnd_devtype[] = {
        {
                .name = "imx21-nand",
                .driver_data = (kernel_ulong_t) &imx21_nand_devtype_data,
@@ -1587,8 +1653,20 @@ static int mxcnd_probe(struct platform_device *pdev)
 
        if (mtd->writesize == 2048)
                this->ecc.layout = host->devtype_data->ecclayout_2k;
-       else if (mtd->writesize == 4096)
+       else if (mtd->writesize == 4096) {
                this->ecc.layout = host->devtype_data->ecclayout_4k;
+               if (get_eccsize(mtd) == 8)
+                       ecc_8bit_layout_4k(this->ecc.layout);
+       }
+
+       /*
+        * Experimentation shows that i.MX NFC can only handle up to 218 oob
+        * bytes. Limit used_oobsize to 218 so as to not confuse copy_spare()
+        * into copying invalid data to/from the spare IO buffer, as this
+        * might cause ECC data corruption when doing sub-page write to a
+        * partially written page.
+        */
+       host->used_oobsize = min(mtd->oobsize, 218U);
 
        if (this->ecc.mode == NAND_ECC_HW) {
                if (is_imx21_nfc(host) || is_imx27_nfc(host))
index c2e1232cd45cc847197c3960ddbd3cd38584dd55..ceb68ca8277a9ebb49c763a72b0979f089b878bb 100644 (file)
@@ -1,6 +1,4 @@
 /*
- *  drivers/mtd/nand.c
- *
  *  Overview:
  *   This is the generic MTD driver for NAND flash devices. It should be
  *   capable of working with almost all NAND chips currently available.
@@ -48,6 +46,7 @@
 #include <linux/leds.h>
 #include <linux/io.h>
 #include <linux/mtd/partitions.h>
+#include <linux/of_mtd.h>
 
 /* Define default oob placement schemes for large and small page devices */
 static struct nand_ecclayout nand_oob_8 = {
@@ -2928,9 +2927,6 @@ static int nand_onfi_get_features(struct mtd_info *mtd, struct nand_chip *chip,
              & ONFI_OPT_CMD_SET_GET_FEATURES))
                return -EINVAL;
 
-       /* clear the sub feature parameters */
-       memset(subfeature_param, 0, ONFI_SUBFEATURE_PARAM_LEN);
-
        chip->cmdfunc(mtd, NAND_CMD_GET_FEATURES, addr, -1);
        for (i = 0; i < ONFI_SUBFEATURE_PARAM_LEN; ++i)
                *subfeature_param++ = chip->read_byte(mtd);
@@ -3689,7 +3685,7 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
                        if (find_full_id_nand(mtd, chip, type, id_data, &busw))
                                goto ident_done;
                } else if (*dev_id == type->dev_id) {
-                               break;
+                       break;
                }
        }
 
@@ -3798,6 +3794,39 @@ ident_done:
        return type;
 }
 
+static int nand_dt_init(struct mtd_info *mtd, struct nand_chip *chip,
+                       struct device_node *dn)
+{
+       int ecc_mode, ecc_strength, ecc_step;
+
+       if (of_get_nand_bus_width(dn) == 16)
+               chip->options |= NAND_BUSWIDTH_16;
+
+       if (of_get_nand_on_flash_bbt(dn))
+               chip->bbt_options |= NAND_BBT_USE_FLASH;
+
+       ecc_mode = of_get_nand_ecc_mode(dn);
+       ecc_strength = of_get_nand_ecc_strength(dn);
+       ecc_step = of_get_nand_ecc_step_size(dn);
+
+       if ((ecc_step >= 0 && !(ecc_strength >= 0)) ||
+           (!(ecc_step >= 0) && ecc_strength >= 0)) {
+               pr_err("must set both strength and step size in DT\n");
+               return -EINVAL;
+       }
+
+       if (ecc_mode >= 0)
+               chip->ecc.mode = ecc_mode;
+
+       if (ecc_strength >= 0)
+               chip->ecc.strength = ecc_strength;
+
+       if (ecc_step > 0)
+               chip->ecc.size = ecc_step;
+
+       return 0;
+}
+
 /**
  * nand_scan_ident - [NAND Interface] Scan for the NAND device
  * @mtd: MTD device structure
@@ -3815,6 +3844,13 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips,
        int i, nand_maf_id, nand_dev_id;
        struct nand_chip *chip = mtd->priv;
        struct nand_flash_dev *type;
+       int ret;
+
+       if (chip->dn) {
+               ret = nand_dt_init(mtd, chip, chip->dn);
+               if (ret)
+                       return ret;
+       }
 
        /* Set the default functions */
        nand_set_defaults(chip, chip->options & NAND_BUSWIDTH_16);
index 9bb8453d224ec81239a30b035d0aa59f2bc6912b..63a1a36a3f4b4b804f7c4b389eacdc8c19d30a43 100644 (file)
@@ -1,6 +1,4 @@
 /*
- *  drivers/mtd/nand_bbt.c
- *
  *  Overview:
  *   Bad block table support for the NAND driver
  *
@@ -64,7 +62,6 @@
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/bbm.h>
 #include <linux/mtd/nand.h>
-#include <linux/mtd/nand_ecc.h>
 #include <linux/bitops.h>
 #include <linux/delay.h>
 #include <linux/vmalloc.h>
@@ -720,7 +717,7 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf,
                /* Must we save the block contents? */
                if (td->options & NAND_BBT_SAVECONTENT) {
                        /* Make it block aligned */
-                       to &= ~((loff_t)((1 << this->bbt_erase_shift) - 1));
+                       to &= ~(((loff_t)1 << this->bbt_erase_shift) - 1);
                        len = 1 << this->bbt_erase_shift;
                        res = mtd_read(mtd, to, len, &retlen, buf);
                        if (res < 0) {
@@ -1075,10 +1072,10 @@ static void verify_bbt_descr(struct mtd_info *mtd, struct nand_bbt_descr *bd)
  * The bad block table memory is allocated here. It must be freed by calling
  * the nand_free_bbt function.
  */
-int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)
+static int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)
 {
        struct nand_chip *this = mtd->priv;
-       int len, res = 0;
+       int len, res;
        uint8_t *buf;
        struct nand_bbt_descr *td = this->bbt_td;
        struct nand_bbt_descr *md = this->bbt_md;
@@ -1099,10 +1096,9 @@ int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)
        if (!td) {
                if ((res = nand_memory_bbt(mtd, bd))) {
                        pr_err("nand_bbt: can't scan flash and build the RAM-based BBT\n");
-                       kfree(this->bbt);
-                       this->bbt = NULL;
+                       goto err;
                }
-               return res;
+               return 0;
        }
        verify_bbt_descr(mtd, td);
        verify_bbt_descr(mtd, md);
@@ -1112,9 +1108,8 @@ int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)
        len += (len >> this->page_shift) * mtd->oobsize;
        buf = vmalloc(len);
        if (!buf) {
-               kfree(this->bbt);
-               this->bbt = NULL;
-               return -ENOMEM;
+               res = -ENOMEM;
+               goto err;
        }
 
        /* Is the bbt at a given page? */
@@ -1126,6 +1121,8 @@ int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)
        }
 
        res = check_create(mtd, buf, bd);
+       if (res)
+               goto err;
 
        /* Prevent the bbt regions from erasing / writing */
        mark_bbt_region(mtd, td);
@@ -1133,6 +1130,11 @@ int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)
                mark_bbt_region(mtd, md);
 
        vfree(buf);
+       return 0;
+
+err:
+       kfree(this->bbt);
+       this->bbt = NULL;
        return res;
 }
 
index dd620c19c6192f200ecfb1a83d77a08580e84a4b..7124400d903b3b5feb5412371c5b5dc634fbb250 100644 (file)
@@ -1,6 +1,4 @@
 /*
- *  drivers/mtd/nandids.c
- *
  *  Copyright (C) 2002 Thomas Gleixner (tglx@linutronix.de)
  *
  * This program is free software; you can redistribute it and/or modify
index f2324271b94e9d19cc39aafae95cc0ccf934fc42..52c0c1a3899cd1d09bd4d5d9685405be04deaab5 100644 (file)
@@ -743,6 +743,11 @@ static int init_nandsim(struct mtd_info *mtd)
                        goto error;
                }
                ns->partitions[i].name   = get_partition_name(i);
+               if (!ns->partitions[i].name) {
+                       NS_ERR("unable to allocate memory.\n");
+                       ret = -ENOMEM;
+                       goto error;
+               }
                ns->partitions[i].offset = next_offset;
                ns->partitions[i].size   = part_sz;
                next_offset += ns->partitions[i].size;
@@ -756,6 +761,11 @@ static int init_nandsim(struct mtd_info *mtd)
                        goto error;
                }
                ns->partitions[i].name   = get_partition_name(i);
+               if (!ns->partitions[i].name) {
+                       NS_ERR("unable to allocate memory.\n");
+                       ret = -ENOMEM;
+                       goto error;
+               }
                ns->partitions[i].offset = next_offset;
                ns->partitions[i].size   = remains;
                ns->nbparts += 1;
index 3187c6b92d9aaa7537a4a9e2b090035672591347..67a1b3f911cfec41e723a6b6212c9551447544cc 100644 (file)
@@ -1,6 +1,4 @@
 /*
- *  drivers/mtd/ndfc.c
- *
  *  Overview:
  *   Platform independent driver for NDFC (NanD Flash Controller)
  *   integrated into EP440 cores
index 4535c263fae576e59b87594f924c2deb5ed0f3e1..717cf623fcdef81a761940cee99ceed616501d66 100644 (file)
@@ -24,8 +24,6 @@ struct plat_nand_data {
        void __iomem            *io_base;
 };
 
-static const char *part_probe_types[] = { "cmdlinepart", NULL };
-
 /*
  * Probe for the NAND device.
  */
@@ -95,7 +93,7 @@ static int plat_nand_probe(struct platform_device *pdev)
                goto out;
        }
 
-       part_types = pdata->chip.part_probe_types ? : part_probe_types;
+       part_types = pdata->chip.part_probe_types;
 
        ppdata.of_node = pdev->dev.of_node;
        err = mtd_device_parse_register(&data->mtd, part_types, &ppdata,
index a4615fcc3d001f6a0efba58ddfc5b5573ad91069..1259cc558ce98954f00de813facae4bb5317ac82 100644 (file)
 #include <linux/mtd/nand.h>
 #include <linux/mtd/partitions.h>
 #include <linux/io.h>
+#include <linux/iopoll.h>
 #include <linux/irq.h>
 #include <linux/slab.h>
 #include <linux/of.h>
 #include <linux/of_device.h>
 #include <linux/of_mtd.h>
 
-#if defined(CONFIG_ARCH_PXA) || defined(CONFIG_ARCH_MMP)
+#if defined(CONFIG_ARM) && (defined(CONFIG_ARCH_PXA) || defined(CONFIG_ARCH_MMP))
 #define ARCH_HAS_DMA
 #endif
 
@@ -483,7 +484,8 @@ static void disable_int(struct pxa3xx_nand_info *info, uint32_t int_mask)
 static void drain_fifo(struct pxa3xx_nand_info *info, void *data, int len)
 {
        if (info->ecc_bch) {
-               int timeout;
+               u32 val;
+               int ret;
 
                /*
                 * According to the datasheet, when reading from NDDB
@@ -494,18 +496,14 @@ static void drain_fifo(struct pxa3xx_nand_info *info, void *data, int len)
                 * the polling on the last read.
                 */
                while (len > 8) {
-                       __raw_readsl(info->mmio_base + NDDB, data, 8);
-
-                       for (timeout = 0;
-                            !(nand_readl(info, NDSR) & NDSR_RDDREQ);
-                            timeout++) {
-                               if (timeout >= 5) {
-                                       dev_err(&info->pdev->dev,
-                                               "Timeout on RDDREQ while draining the FIFO\n");
-                                       return;
-                               }
-
-                               mdelay(1);
+                       readsl(info->mmio_base + NDDB, data, 8);
+
+                       ret = readl_relaxed_poll_timeout(info->mmio_base + NDSR, val,
+                                                        val & NDSR_RDDREQ, 1000, 5000);
+                       if (ret) {
+                               dev_err(&info->pdev->dev,
+                                       "Timeout on RDDREQ while draining the FIFO\n");
+                               return;
                        }
 
                        data += 32;
@@ -513,7 +511,7 @@ static void drain_fifo(struct pxa3xx_nand_info *info, void *data, int len)
                }
        }
 
-       __raw_readsl(info->mmio_base + NDDB, data, len);
+       readsl(info->mmio_base + NDDB, data, len);
 }
 
 static void handle_data_pio(struct pxa3xx_nand_info *info)
@@ -522,14 +520,14 @@ static void handle_data_pio(struct pxa3xx_nand_info *info)
 
        switch (info->state) {
        case STATE_PIO_WRITING:
-               __raw_writesl(info->mmio_base + NDDB,
-                             info->data_buff + info->data_buff_pos,
-                             DIV_ROUND_UP(do_bytes, 4));
+               writesl(info->mmio_base + NDDB,
+                       info->data_buff + info->data_buff_pos,
+                       DIV_ROUND_UP(do_bytes, 4));
 
                if (info->oob_size > 0)
-                       __raw_writesl(info->mmio_base + NDDB,
-                                     info->oob_buff + info->oob_buff_pos,
-                                     DIV_ROUND_UP(info->oob_size, 4));
+                       writesl(info->mmio_base + NDDB,
+                               info->oob_buff + info->oob_buff_pos,
+                               DIV_ROUND_UP(info->oob_size, 4));
                break;
        case STATE_PIO_READING:
                drain_fifo(info,
@@ -1630,8 +1628,7 @@ static int alloc_nand_resource(struct platform_device *pdev)
        info->pdev = pdev;
        info->variant = pxa3xx_nand_get_variant(pdev);
        for (cs = 0; cs < pdata->num_cs; cs++) {
-               mtd = (struct mtd_info *)((unsigned int)&info[1] +
-                     (sizeof(*mtd) + sizeof(*host)) * cs);
+               mtd = (void *)&info[1] + (sizeof(*mtd) + sizeof(*host)) * cs;
                chip = (struct nand_chip *)(&mtd[1]);
                host = (struct pxa3xx_nand_host *)chip;
                info->host[cs] = host;
index baea83f4dea84f0f0f3d9917e1797b47cb262fca..77e96d2df96cac12194dc0679dfac1dceb098d5e 100644 (file)
@@ -653,11 +653,15 @@ static int r852_register_nand_device(struct r852_device *dev)
        if (sm_register_device(dev->mtd, dev->sm))
                goto error2;
 
-       if (device_create_file(&dev->mtd->dev, &dev_attr_media_type))
+       if (device_create_file(&dev->mtd->dev, &dev_attr_media_type)) {
                message("can't create media type sysfs attribute");
+               goto error3;
+       }
 
        dev->card_registred = 1;
        return 0;
+error3:
+       nand_release(dev->mtd);
 error2:
        kfree(dev->mtd);
 error1:
index 0e02be47ce1d4dbc959e7037a5bb08edc8e91b98..381f67ac6b5a0626a562ffb1508b66cb263fa245 100644 (file)
@@ -1105,7 +1105,7 @@ static int s3c24xx_nand_resume(struct platform_device *dev)
 
 /* driver device registration */
 
-static struct platform_device_id s3c24xx_driver_ids[] = {
+static const struct platform_device_id s3c24xx_driver_ids[] = {
        {
                .name           = "s3c2410-nand",
                .driver_data    = TYPE_S3C2410,
index 3f81dc8f214cdc0240c8a7d07cee734b96d544f1..3b28db458ea042794913dffed7c160c29d24b189 100644 (file)
@@ -160,14 +160,10 @@ static int xway_nand_probe(struct platform_device *pdev)
        return 0;
 }
 
-/* allow users to override the partition in DT using the cmdline */
-static const char *part_probes[] = { "cmdlinepart", "ofpart", NULL };
-
 static struct platform_nand_data xway_nand_data = {
        .chip = {
                .nr_chips               = 1,
                .chip_delay             = 30,
-               .part_probe_types       = part_probes,
        },
        .ctrl = {
                .probe          = xway_nand_probe,
index 19cfb97adbc0447deae8611f03ffc565973b6076..739259513055567114aa26649f74ef6b7d7a58c9 100644 (file)
@@ -1083,7 +1083,7 @@ static const struct dev_pm_ops s3c_pm_ops = {
        .resume         = s3c_pm_ops_resume,
 };
 
-static struct platform_device_id s3c_onenand_driver_ids[] = {
+static const struct platform_device_id s3c_onenand_driver_ids[] = {
        {
                .name           = "s3c6400-onenand",
                .driver_data    = TYPE_S3C6400,
index 5d5d36272bb5b3b5d50074e89515e683cb7f6084..52a872fa1b6e412649653490bcf2a64a008feb18 100644 (file)
@@ -662,7 +662,7 @@ static int fsl_qspi_nor_setup_last(struct fsl_qspi *q)
        return 0;
 }
 
-static struct of_device_id fsl_qspi_dt_ids[] = {
+static const struct of_device_id fsl_qspi_dt_ids[] = {
        { .compatible = "fsl,vf610-qspi", .data = (void *)&vybrid_data, },
        { .compatible = "fsl,imx6sx-qspi", .data = (void *)&imx6sx_data, },
        { /* sentinel */ }
index 14a5d2325dac0cdf0558d881a99fee2727b7a7bb..d78831b4422b4edb1d2b6f13b879ea888603f492 100644 (file)
@@ -513,6 +513,13 @@ static int spi_nor_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
 /* NOTE: double check command sets and memory organization when you add
  * more nor chips.  This current list focusses on newer chips, which
  * have been converging on command sets which including JEDEC ID.
+ *
+ * All newly added entries should describe *hardware* and should use SECT_4K
+ * (or SECT_4K_PMC) if hardware supports erasing 4 KiB sectors. For usage
+ * scenarios excluding small sectors there is config option that can be
+ * disabled: CONFIG_MTD_SPI_NOR_USE_4K_SECTORS.
+ * For historical (and compatibility) reasons (before we got above config) some
+ * old entries may be missing 4K flag.
  */
 static const struct spi_device_id spi_nor_ids[] = {
        /* Atmel -- some are (confusingly) marketed as "DataFlash" */
@@ -538,7 +545,7 @@ static const struct spi_device_id spi_nor_ids[] = {
        { "en25q64",    INFO(0x1c3017, 0, 64 * 1024,  128, SECT_4K) },
        { "en25qh128",  INFO(0x1c7018, 0, 64 * 1024,  256, 0) },
        { "en25qh256",  INFO(0x1c7019, 0, 64 * 1024,  512, 0) },
-       { "en25s64",    INFO(0x1c3817, 0, 64 * 1024,  128, 0) },
+       { "en25s64",    INFO(0x1c3817, 0, 64 * 1024,  128, SECT_4K) },
 
        /* ESMT */
        { "f25l32pa", INFO(0x8c2016, 0, 64 * 1024, 64, SECT_4K) },
@@ -560,7 +567,11 @@ static const struct spi_device_id spi_nor_ids[] = {
        { "320s33b",  INFO(0x898912, 0, 64 * 1024,  64, 0) },
        { "640s33b",  INFO(0x898913, 0, 64 * 1024, 128, 0) },
 
+       /* ISSI */
+       { "is25cd512", INFO(0x7f9d20, 0, 32 * 1024,   2, SECT_4K) },
+
        /* Macronix */
+       { "mx25l512e",   INFO(0xc22010, 0, 64 * 1024,   1, SECT_4K) },
        { "mx25l2005a",  INFO(0xc22012, 0, 64 * 1024,   4, SECT_4K) },
        { "mx25l4005a",  INFO(0xc22013, 0, 64 * 1024,   8, SECT_4K) },
        { "mx25l8005",   INFO(0xc22014, 0, 64 * 1024,  16, 0) },
@@ -602,7 +613,7 @@ static const struct spi_device_id spi_nor_ids[] = {
        { "s70fl01gs",  INFO(0x010221, 0x4d00, 256 * 1024, 256, 0) },
        { "s25sl12800", INFO(0x012018, 0x0300, 256 * 1024,  64, 0) },
        { "s25sl12801", INFO(0x012018, 0x0301,  64 * 1024, 256, 0) },
-       { "s25fl128s",  INFO6(0x012018, 0x4d0180, 64 * 1024, 256, SPI_NOR_QUAD_READ) },
+       { "s25fl128s",  INFO6(0x012018, 0x4d0180, 64 * 1024, 256, SECT_4K | SPI_NOR_QUAD_READ) },
        { "s25fl129p0", INFO(0x012018, 0x4d00, 256 * 1024,  64, 0) },
        { "s25fl129p1", INFO(0x012018, 0x4d01,  64 * 1024, 256, 0) },
        { "s25sl004a",  INFO(0x010212,      0,  64 * 1024,   8, 0) },
@@ -613,7 +624,8 @@ static const struct spi_device_id spi_nor_ids[] = {
        { "s25fl008k",  INFO(0xef4014,      0,  64 * 1024,  16, SECT_4K) },
        { "s25fl016k",  INFO(0xef4015,      0,  64 * 1024,  32, SECT_4K) },
        { "s25fl064k",  INFO(0xef4017,      0,  64 * 1024, 128, SECT_4K) },
-       { "s25fl132k",  INFO(0x014016,      0,  64 * 1024,  64, 0) },
+       { "s25fl132k",  INFO(0x014016,      0,  64 * 1024,  64, SECT_4K) },
+       { "s25fl164k",  INFO(0x014017,      0,  64 * 1024, 128, SECT_4K) },
 
        /* SST -- large erase sizes are "overlays", "sectors" are 4K */
        { "sst25vf040b", INFO(0xbf258d, 0, 64 * 1024,  8, SECT_4K | SST_WRITE) },
index db84ddcfec8464191a3edcccfd87c869ac1c5a7c..9fd6c69a8bac3c77d1c0c6e99eb4f3644561f78a 100644 (file)
@@ -423,7 +423,7 @@ static void xgbe_tx_timer(unsigned long data)
        if (napi_schedule_prep(napi)) {
                /* Disable Tx and Rx interrupts */
                if (pdata->per_channel_irq)
-                       disable_irq(channel->dma_irq);
+                       disable_irq_nosync(channel->dma_irq);
                else
                        xgbe_disable_rx_tx_ints(pdata);
 
index 7149053849008de10da3be7eb054884b4a808f8c..6d2c702c8e4a382fc0a9b15f195a9768120341a0 100644 (file)
@@ -168,13 +168,8 @@ static void xgbe_init_all_fptrs(struct xgbe_prv_data *pdata)
 #ifdef CONFIG_ACPI
 static int xgbe_acpi_support(struct xgbe_prv_data *pdata)
 {
-       struct acpi_device *adev = pdata->adev;
        struct device *dev = pdata->dev;
        u32 property;
-       acpi_handle handle;
-       acpi_status status;
-       unsigned long long data;
-       int cca;
        int ret;
 
        /* Obtain the system clock setting */
@@ -195,24 +190,6 @@ static int xgbe_acpi_support(struct xgbe_prv_data *pdata)
        }
        pdata->ptpclk_rate = property;
 
-       /* Retrieve the device cache coherency value */
-       handle = adev->handle;
-       do {
-               status = acpi_evaluate_integer(handle, "_CCA", NULL, &data);
-               if (!ACPI_FAILURE(status)) {
-                       cca = data;
-                       break;
-               }
-
-               status = acpi_get_parent(handle, &handle);
-       } while (!ACPI_FAILURE(status));
-
-       if (ACPI_FAILURE(status)) {
-               dev_err(dev, "error obtaining acpi coherency value\n");
-               return -EINVAL;
-       }
-       pdata->coherent = !!cca;
-
        return 0;
 }
 #else   /* CONFIG_ACPI */
@@ -243,9 +220,6 @@ static int xgbe_of_support(struct xgbe_prv_data *pdata)
        }
        pdata->ptpclk_rate = clk_get_rate(pdata->ptpclk);
 
-       /* Retrieve the device cache coherency value */
-       pdata->coherent = of_dma_is_coherent(dev->of_node);
-
        return 0;
 }
 #else   /* CONFIG_OF */
@@ -364,6 +338,7 @@ static int xgbe_probe(struct platform_device *pdev)
                goto err_io;
 
        /* Set the DMA coherency values */
+       pdata->coherent = device_dma_is_coherent(pdata->dev);
        if (pdata->coherent) {
                pdata->axdomain = XGBE_DMA_OS_AXDOMAIN;
                pdata->arcache = XGBE_DMA_OS_ARCACHE;
index 77363d6805321534a582e579552f46e254737e25..a3b1c07ae0af0935f3026ba8a56e21512e238e36 100644 (file)
@@ -2464,6 +2464,7 @@ err_out_powerdown:
        ssb_bus_may_powerdown(sdev->bus);
 
 err_out_free_dev:
+       netif_napi_del(&bp->napi);
        free_netdev(dev);
 
 out:
@@ -2480,6 +2481,7 @@ static void b44_remove_one(struct ssb_device *sdev)
                b44_unregister_phy_one(bp);
        ssb_device_disable(sdev, 0);
        ssb_bus_may_powerdown(sdev->bus);
+       netif_napi_del(&bp->napi);
        free_netdev(dev);
        ssb_pcihost_set_power_state(sdev, PCI_D3hot);
        ssb_set_drvdata(sdev, NULL);
index e7651b3c6c5767f7609115ef0430c13aac8d17a9..420949cc55aab6349b75c33f0c4f061aa384d537 100644 (file)
@@ -299,9 +299,6 @@ int bcmgenet_mii_config(struct net_device *dev, bool init)
                        phy_name = "external RGMII (no delay)";
                else
                        phy_name = "external RGMII (TX delay)";
-               reg = bcmgenet_ext_readl(priv, EXT_RGMII_OOB_CTRL);
-               reg |= RGMII_MODE_EN | id_mode_dis;
-               bcmgenet_ext_writel(priv, reg, EXT_RGMII_OOB_CTRL);
                bcmgenet_sys_writel(priv,
                                    PORT_MODE_EXT_GPHY, SYS_PORT_CTRL);
                break;
@@ -310,6 +307,15 @@ int bcmgenet_mii_config(struct net_device *dev, bool init)
                return -EINVAL;
        }
 
+       /* This is an external PHY (xMII), so we need to enable the RGMII
+        * block for the interface to work
+        */
+       if (priv->ext_phy) {
+               reg = bcmgenet_ext_readl(priv, EXT_RGMII_OOB_CTRL);
+               reg |= RGMII_MODE_EN | id_mode_dis;
+               bcmgenet_ext_writel(priv, reg, EXT_RGMII_OOB_CTRL);
+       }
+
        if (init)
                dev_info(kdev, "configuring instance for %s\n", phy_name);
 
index 524d11098c566d178d132fe0b3a05be557036440..e052f05558c7f511d9f0b0c8c2a9e24f9a54ae15 100644 (file)
@@ -1185,6 +1185,7 @@ enum t4_bar2_qtype { T4_BAR2_QTYPE_EGRESS, T4_BAR2_QTYPE_INGRESS };
 int cxgb4_t4_bar2_sge_qregs(struct adapter *adapter,
                      unsigned int qid,
                      enum t4_bar2_qtype qtype,
+                     int user,
                      u64 *pbar2_qoffset,
                      unsigned int *pbar2_qid);
 
index 803d91beec6f43b7a94c3378ff14d8623bc001a9..a9355593e65e8272c2346dc1dc59620e05381121 100644 (file)
@@ -2145,6 +2145,7 @@ EXPORT_SYMBOL(cxgb4_read_sge_timestamp);
 int cxgb4_bar2_sge_qregs(struct net_device *dev,
                         unsigned int qid,
                         enum cxgb4_bar2_qtype qtype,
+                        int user,
                         u64 *pbar2_qoffset,
                         unsigned int *pbar2_qid)
 {
@@ -2153,6 +2154,7 @@ int cxgb4_bar2_sge_qregs(struct net_device *dev,
                                 (qtype == CXGB4_BAR2_QTYPE_EGRESS
                                  ? T4_BAR2_QTYPE_EGRESS
                                  : T4_BAR2_QTYPE_INGRESS),
+                                user,
                                 pbar2_qoffset,
                                 pbar2_qid);
 }
@@ -2351,7 +2353,7 @@ static void process_db_drop(struct work_struct *work)
                int ret;
 
                ret = cxgb4_t4_bar2_sge_qregs(adap, qid, T4_BAR2_QTYPE_EGRESS,
-                                       &bar2_qoffset, &bar2_qid);
+                                             0, &bar2_qoffset, &bar2_qid);
                if (ret)
                        dev_err(adap->pdev_dev, "doorbell drop recovery: "
                                "qid=%d, pidx_inc=%d\n", qid, pidx_inc);
index 78ab4d406ce277509eb6da61780903129596e7ee..e33934a6f59e0b010eccff8d548b7871ed8a14b5 100644 (file)
@@ -306,6 +306,7 @@ enum cxgb4_bar2_qtype { CXGB4_BAR2_QTYPE_EGRESS, CXGB4_BAR2_QTYPE_INGRESS };
 int cxgb4_bar2_sge_qregs(struct net_device *dev,
                         unsigned int qid,
                         enum cxgb4_bar2_qtype qtype,
+                        int user,
                         u64 *pbar2_qoffset,
                         unsigned int *pbar2_qid);
 
index 0d2eddab04efbf7b2a0e1054ea46848273c97933..1b99aecde736067f77278db6be246cc47d23004b 100644 (file)
@@ -2429,8 +2429,8 @@ static void __iomem *bar2_address(struct adapter *adapter,
        u64 bar2_qoffset;
        int ret;
 
-       ret = cxgb4_t4_bar2_sge_qregs(adapter, qid, qtype,
-                               &bar2_qoffset, pbar2_qid);
+       ret = cxgb4_t4_bar2_sge_qregs(adapter, qid, qtype, 0,
+                                     &bar2_qoffset, pbar2_qid);
        if (ret)
                return NULL;
 
index e8578a742f2a29b14a2eaec01216a8e47a68e12a..61d8b3ec959ea1a5d753898f7a438886afc616de 100644 (file)
@@ -5102,6 +5102,7 @@ int t4_prep_adapter(struct adapter *adapter)
  *     @adapter: the adapter
  *     @qid: the Queue ID
  *     @qtype: the Ingress or Egress type for @qid
+ *     @user: true if this request is for a user mode queue
  *     @pbar2_qoffset: BAR2 Queue Offset
  *     @pbar2_qid: BAR2 Queue ID or 0 for Queue ID inferred SGE Queues
  *
@@ -5125,6 +5126,7 @@ int t4_prep_adapter(struct adapter *adapter)
 int cxgb4_t4_bar2_sge_qregs(struct adapter *adapter,
                      unsigned int qid,
                      enum t4_bar2_qtype qtype,
+                     int user,
                      u64 *pbar2_qoffset,
                      unsigned int *pbar2_qid)
 {
@@ -5132,9 +5134,8 @@ int cxgb4_t4_bar2_sge_qregs(struct adapter *adapter,
        u64 bar2_page_offset, bar2_qoffset;
        unsigned int bar2_qid, bar2_qid_offset, bar2_qinferred;
 
-       /* T4 doesn't support BAR2 SGE Queue registers.
-        */
-       if (is_t4(adapter->params.chip))
+       /* T4 doesn't support BAR2 SGE Queue registers for kernel mode queues */
+       if (!user && is_t4(adapter->params.chip))
                return -EINVAL;
 
        /* Get our SGE Page Size parameters.
index 28d9ca675a274f9876473bcce7e6995a14e1289e..68d47b196daec3d3c5d0b8af19f8d167735e1e79 100644 (file)
@@ -131,8 +131,15 @@ static void enic_get_drvinfo(struct net_device *netdev,
 {
        struct enic *enic = netdev_priv(netdev);
        struct vnic_devcmd_fw_info *fw_info;
+       int err;
 
-       enic_dev_fw_info(enic, &fw_info);
+       err = enic_dev_fw_info(enic, &fw_info);
+       /* return only when pci_zalloc_consistent fails in vnic_dev_fw_info
+        * For other failures, like devcmd failure, we return previously
+        * recorded info.
+        */
+       if (err == -ENOMEM)
+               return;
 
        strlcpy(drvinfo->driver, DRV_NAME, sizeof(drvinfo->driver));
        strlcpy(drvinfo->version, DRV_VERSION, sizeof(drvinfo->version));
@@ -181,8 +188,15 @@ static void enic_get_ethtool_stats(struct net_device *netdev,
        struct enic *enic = netdev_priv(netdev);
        struct vnic_stats *vstats;
        unsigned int i;
-
-       enic_dev_stats_dump(enic, &vstats);
+       int err;
+
+       err = enic_dev_stats_dump(enic, &vstats);
+       /* return only when pci_zalloc_consistent fails in vnic_dev_stats_dump
+        * For other failures, like devcmd failure, we return previously
+        * recorded stats.
+        */
+       if (err == -ENOMEM)
+               return;
 
        for (i = 0; i < enic_n_tx_stats; i++)
                *(data++) = ((u64 *)&vstats->tx)[enic_tx_stats[i].index];
index 204bd182473bceaaabaa5b1eba5ed618de751808..eadae1b412c652974dde24a9a76c5d74a8c3fa29 100644 (file)
@@ -615,8 +615,15 @@ static struct rtnl_link_stats64 *enic_get_stats(struct net_device *netdev,
 {
        struct enic *enic = netdev_priv(netdev);
        struct vnic_stats *stats;
+       int err;
 
-       enic_dev_stats_dump(enic, &stats);
+       err = enic_dev_stats_dump(enic, &stats);
+       /* return only when pci_zalloc_consistent fails in vnic_dev_stats_dump
+        * For other failures, like devcmd failure, we return previously
+        * recorded stats.
+        */
+       if (err == -ENOMEM)
+               return net_stats;
 
        net_stats->tx_packets = stats->tx.tx_frames_ok;
        net_stats->tx_bytes = stats->tx.tx_bytes_ok;
@@ -1407,6 +1414,7 @@ static int enic_poll_msix_rq(struct napi_struct *napi, int budget)
                 */
                enic_calc_int_moderation(enic, &enic->rq[rq]);
 
+       enic_poll_unlock_napi(&enic->rq[rq]);
        if (work_done < work_to_do) {
 
                /* Some work done, but not enough to stay in polling,
@@ -1418,7 +1426,6 @@ static int enic_poll_msix_rq(struct napi_struct *napi, int budget)
                        enic_set_int_moderation(enic, &enic->rq[rq]);
                vnic_intr_unmask(&enic->intr[intr]);
        }
-       enic_poll_unlock_napi(&enic->rq[rq]);
 
        return work_done;
 }
index 36a2ed606c911f21355360fad81eb39b18162c59..c4b2183bf352fb2a1881001777df91857c2d1f79 100644 (file)
@@ -188,16 +188,15 @@ void vnic_rq_clean(struct vnic_rq *rq,
        struct vnic_rq_buf *buf;
        u32 fetch_index;
        unsigned int count = rq->ring.desc_count;
+       int i;
 
        buf = rq->to_clean;
 
-       while (vnic_rq_desc_used(rq) > 0) {
-
+       for (i = 0; i < rq->ring.desc_count; i++) {
                (*buf_clean)(rq, buf);
-
-               buf = rq->to_clean = buf->next;
-               rq->ring.desc_avail++;
+               buf = buf->next;
        }
+       rq->ring.desc_avail = rq->ring.desc_count - 1;
 
        /* Use current fetch_index as the ring starting point */
        fetch_index = ioread32(&rq->ctrl->fetch_index);
index fb140faeafb1cbda612cd11a9a1aac04e936c4a3..c5e1d0ac75f909f843dd0397ad41b85eeb26a164 100644 (file)
@@ -1720,9 +1720,9 @@ int be_cmd_get_regs(struct be_adapter *adapter, u32 buf_len, void *buf)
        total_size = buf_len;
 
        get_fat_cmd.size = sizeof(struct be_cmd_req_get_fat) + 60*1024;
-       get_fat_cmd.va = pci_alloc_consistent(adapter->pdev,
-                                             get_fat_cmd.size,
-                                             &get_fat_cmd.dma);
+       get_fat_cmd.va = dma_zalloc_coherent(&adapter->pdev->dev,
+                                            get_fat_cmd.size,
+                                            &get_fat_cmd.dma, GFP_ATOMIC);
        if (!get_fat_cmd.va) {
                dev_err(&adapter->pdev->dev,
                        "Memory allocation failure while reading FAT data\n");
@@ -1767,8 +1767,8 @@ int be_cmd_get_regs(struct be_adapter *adapter, u32 buf_len, void *buf)
                log_offset += buf_size;
        }
 err:
-       pci_free_consistent(adapter->pdev, get_fat_cmd.size,
-                           get_fat_cmd.va, get_fat_cmd.dma);
+       dma_free_coherent(&adapter->pdev->dev, get_fat_cmd.size,
+                         get_fat_cmd.va, get_fat_cmd.dma);
        spin_unlock_bh(&adapter->mcc_lock);
        return status;
 }
@@ -2215,12 +2215,12 @@ int be_cmd_read_port_transceiver_data(struct be_adapter *adapter,
                return -EINVAL;
 
        cmd.size = sizeof(struct be_cmd_resp_port_type);
-       cmd.va = pci_alloc_consistent(adapter->pdev, cmd.size, &cmd.dma);
+       cmd.va = dma_zalloc_coherent(&adapter->pdev->dev, cmd.size, &cmd.dma,
+                                    GFP_ATOMIC);
        if (!cmd.va) {
                dev_err(&adapter->pdev->dev, "Memory allocation failed\n");
                return -ENOMEM;
        }
-       memset(cmd.va, 0, cmd.size);
 
        spin_lock_bh(&adapter->mcc_lock);
 
@@ -2245,7 +2245,7 @@ int be_cmd_read_port_transceiver_data(struct be_adapter *adapter,
        }
 err:
        spin_unlock_bh(&adapter->mcc_lock);
-       pci_free_consistent(adapter->pdev, cmd.size, cmd.va, cmd.dma);
+       dma_free_coherent(&adapter->pdev->dev, cmd.size, cmd.va, cmd.dma);
        return status;
 }
 
@@ -2720,7 +2720,8 @@ int be_cmd_get_phy_info(struct be_adapter *adapter)
                goto err;
        }
        cmd.size = sizeof(struct be_cmd_req_get_phy_info);
-       cmd.va = pci_alloc_consistent(adapter->pdev, cmd.size, &cmd.dma);
+       cmd.va = dma_zalloc_coherent(&adapter->pdev->dev, cmd.size, &cmd.dma,
+                                    GFP_ATOMIC);
        if (!cmd.va) {
                dev_err(&adapter->pdev->dev, "Memory alloc failure\n");
                status = -ENOMEM;
@@ -2754,7 +2755,7 @@ int be_cmd_get_phy_info(struct be_adapter *adapter)
                                BE_SUPPORTED_SPEED_1GBPS;
                }
        }
-       pci_free_consistent(adapter->pdev, cmd.size, cmd.va, cmd.dma);
+       dma_free_coherent(&adapter->pdev->dev, cmd.size, cmd.va, cmd.dma);
 err:
        spin_unlock_bh(&adapter->mcc_lock);
        return status;
@@ -2805,8 +2806,9 @@ int be_cmd_get_cntl_attributes(struct be_adapter *adapter)
 
        memset(&attribs_cmd, 0, sizeof(struct be_dma_mem));
        attribs_cmd.size = sizeof(struct be_cmd_resp_cntl_attribs);
-       attribs_cmd.va = pci_alloc_consistent(adapter->pdev, attribs_cmd.size,
-                                             &attribs_cmd.dma);
+       attribs_cmd.va = dma_zalloc_coherent(&adapter->pdev->dev,
+                                            attribs_cmd.size,
+                                            &attribs_cmd.dma, GFP_ATOMIC);
        if (!attribs_cmd.va) {
                dev_err(&adapter->pdev->dev, "Memory allocation failure\n");
                status = -ENOMEM;
@@ -2833,8 +2835,8 @@ int be_cmd_get_cntl_attributes(struct be_adapter *adapter)
 err:
        mutex_unlock(&adapter->mbox_lock);
        if (attribs_cmd.va)
-               pci_free_consistent(adapter->pdev, attribs_cmd.size,
-                                   attribs_cmd.va, attribs_cmd.dma);
+               dma_free_coherent(&adapter->pdev->dev, attribs_cmd.size,
+                                 attribs_cmd.va, attribs_cmd.dma);
        return status;
 }
 
@@ -2972,9 +2974,10 @@ int be_cmd_get_mac_from_list(struct be_adapter *adapter, u8 *mac,
 
        memset(&get_mac_list_cmd, 0, sizeof(struct be_dma_mem));
        get_mac_list_cmd.size = sizeof(struct be_cmd_resp_get_mac_list);
-       get_mac_list_cmd.va = pci_alloc_consistent(adapter->pdev,
-                                                  get_mac_list_cmd.size,
-                                                  &get_mac_list_cmd.dma);
+       get_mac_list_cmd.va = dma_zalloc_coherent(&adapter->pdev->dev,
+                                                 get_mac_list_cmd.size,
+                                                 &get_mac_list_cmd.dma,
+                                                 GFP_ATOMIC);
 
        if (!get_mac_list_cmd.va) {
                dev_err(&adapter->pdev->dev,
@@ -3047,8 +3050,8 @@ int be_cmd_get_mac_from_list(struct be_adapter *adapter, u8 *mac,
 
 out:
        spin_unlock_bh(&adapter->mcc_lock);
-       pci_free_consistent(adapter->pdev, get_mac_list_cmd.size,
-                           get_mac_list_cmd.va, get_mac_list_cmd.dma);
+       dma_free_coherent(&adapter->pdev->dev, get_mac_list_cmd.size,
+                         get_mac_list_cmd.va, get_mac_list_cmd.dma);
        return status;
 }
 
@@ -3101,8 +3104,8 @@ int be_cmd_set_mac_list(struct be_adapter *adapter, u8 *mac_array,
 
        memset(&cmd, 0, sizeof(struct be_dma_mem));
        cmd.size = sizeof(struct be_cmd_req_set_mac_list);
-       cmd.va = dma_alloc_coherent(&adapter->pdev->dev, cmd.size,
-                                   &cmd.dma, GFP_KERNEL);
+       cmd.va = dma_zalloc_coherent(&adapter->pdev->dev, cmd.size, &cmd.dma,
+                                    GFP_KERNEL);
        if (!cmd.va)
                return -ENOMEM;
 
@@ -3291,7 +3294,8 @@ int be_cmd_get_acpi_wol_cap(struct be_adapter *adapter)
 
        memset(&cmd, 0, sizeof(struct be_dma_mem));
        cmd.size = sizeof(struct be_cmd_resp_acpi_wol_magic_config_v1);
-       cmd.va = pci_alloc_consistent(adapter->pdev, cmd.size, &cmd.dma);
+       cmd.va = dma_zalloc_coherent(&adapter->pdev->dev, cmd.size, &cmd.dma,
+                                    GFP_ATOMIC);
        if (!cmd.va) {
                dev_err(&adapter->pdev->dev, "Memory allocation failure\n");
                status = -ENOMEM;
@@ -3326,7 +3330,8 @@ int be_cmd_get_acpi_wol_cap(struct be_adapter *adapter)
 err:
        mutex_unlock(&adapter->mbox_lock);
        if (cmd.va)
-               pci_free_consistent(adapter->pdev, cmd.size, cmd.va, cmd.dma);
+               dma_free_coherent(&adapter->pdev->dev, cmd.size, cmd.va,
+                                 cmd.dma);
        return status;
 
 }
@@ -3340,8 +3345,9 @@ int be_cmd_set_fw_log_level(struct be_adapter *adapter, u32 level)
 
        memset(&extfat_cmd, 0, sizeof(struct be_dma_mem));
        extfat_cmd.size = sizeof(struct be_cmd_resp_get_ext_fat_caps);
-       extfat_cmd.va = pci_alloc_consistent(adapter->pdev, extfat_cmd.size,
-                                            &extfat_cmd.dma);
+       extfat_cmd.va = dma_zalloc_coherent(&adapter->pdev->dev,
+                                           extfat_cmd.size, &extfat_cmd.dma,
+                                           GFP_ATOMIC);
        if (!extfat_cmd.va)
                return -ENOMEM;
 
@@ -3363,8 +3369,8 @@ int be_cmd_set_fw_log_level(struct be_adapter *adapter, u32 level)
 
        status = be_cmd_set_ext_fat_capabilites(adapter, &extfat_cmd, cfgs);
 err:
-       pci_free_consistent(adapter->pdev, extfat_cmd.size, extfat_cmd.va,
-                           extfat_cmd.dma);
+       dma_free_coherent(&adapter->pdev->dev, extfat_cmd.size, extfat_cmd.va,
+                         extfat_cmd.dma);
        return status;
 }
 
@@ -3377,8 +3383,9 @@ int be_cmd_get_fw_log_level(struct be_adapter *adapter)
 
        memset(&extfat_cmd, 0, sizeof(struct be_dma_mem));
        extfat_cmd.size = sizeof(struct be_cmd_resp_get_ext_fat_caps);
-       extfat_cmd.va = pci_alloc_consistent(adapter->pdev, extfat_cmd.size,
-                                            &extfat_cmd.dma);
+       extfat_cmd.va = dma_zalloc_coherent(&adapter->pdev->dev,
+                                           extfat_cmd.size, &extfat_cmd.dma,
+                                           GFP_ATOMIC);
 
        if (!extfat_cmd.va) {
                dev_err(&adapter->pdev->dev, "%s: Memory allocation failure\n",
@@ -3396,8 +3403,8 @@ int be_cmd_get_fw_log_level(struct be_adapter *adapter)
                                level = cfgs->module[0].trace_lvl[j].dbg_lvl;
                }
        }
-       pci_free_consistent(adapter->pdev, extfat_cmd.size, extfat_cmd.va,
-                           extfat_cmd.dma);
+       dma_free_coherent(&adapter->pdev->dev, extfat_cmd.size, extfat_cmd.va,
+                         extfat_cmd.dma);
 err:
        return level;
 }
@@ -3595,7 +3602,8 @@ int be_cmd_get_func_config(struct be_adapter *adapter, struct be_resources *res)
 
        memset(&cmd, 0, sizeof(struct be_dma_mem));
        cmd.size = sizeof(struct be_cmd_resp_get_func_config);
-       cmd.va = pci_alloc_consistent(adapter->pdev, cmd.size, &cmd.dma);
+       cmd.va = dma_zalloc_coherent(&adapter->pdev->dev, cmd.size, &cmd.dma,
+                                    GFP_ATOMIC);
        if (!cmd.va) {
                dev_err(&adapter->pdev->dev, "Memory alloc failure\n");
                status = -ENOMEM;
@@ -3635,7 +3643,8 @@ int be_cmd_get_func_config(struct be_adapter *adapter, struct be_resources *res)
 err:
        mutex_unlock(&adapter->mbox_lock);
        if (cmd.va)
-               pci_free_consistent(adapter->pdev, cmd.size, cmd.va, cmd.dma);
+               dma_free_coherent(&adapter->pdev->dev, cmd.size, cmd.va,
+                                 cmd.dma);
        return status;
 }
 
@@ -3656,7 +3665,8 @@ int be_cmd_get_profile_config(struct be_adapter *adapter,
 
        memset(&cmd, 0, sizeof(struct be_dma_mem));
        cmd.size = sizeof(struct be_cmd_resp_get_profile_config);
-       cmd.va = pci_alloc_consistent(adapter->pdev, cmd.size, &cmd.dma);
+       cmd.va = dma_zalloc_coherent(&adapter->pdev->dev, cmd.size, &cmd.dma,
+                                    GFP_ATOMIC);
        if (!cmd.va)
                return -ENOMEM;
 
@@ -3702,7 +3712,8 @@ int be_cmd_get_profile_config(struct be_adapter *adapter,
                res->vf_if_cap_flags = vf_res->cap_flags;
 err:
        if (cmd.va)
-               pci_free_consistent(adapter->pdev, cmd.size, cmd.va, cmd.dma);
+               dma_free_coherent(&adapter->pdev->dev, cmd.size, cmd.va,
+                                 cmd.dma);
        return status;
 }
 
@@ -3717,7 +3728,8 @@ static int be_cmd_set_profile_config(struct be_adapter *adapter, void *desc,
 
        memset(&cmd, 0, sizeof(struct be_dma_mem));
        cmd.size = sizeof(struct be_cmd_req_set_profile_config);
-       cmd.va = pci_alloc_consistent(adapter->pdev, cmd.size, &cmd.dma);
+       cmd.va = dma_zalloc_coherent(&adapter->pdev->dev, cmd.size, &cmd.dma,
+                                    GFP_ATOMIC);
        if (!cmd.va)
                return -ENOMEM;
 
@@ -3733,7 +3745,8 @@ static int be_cmd_set_profile_config(struct be_adapter *adapter, void *desc,
        status = be_cmd_notify_wait(adapter, &wrb);
 
        if (cmd.va)
-               pci_free_consistent(adapter->pdev, cmd.size, cmd.va, cmd.dma);
+               dma_free_coherent(&adapter->pdev->dev, cmd.size, cmd.va,
+                                 cmd.dma);
        return status;
 }
 
index b765c24625bf523fd7932be17f6dfa22840a8e46..2835dee5dc3930cc5d1d09ec958bd2557228a2cd 100644 (file)
@@ -264,8 +264,8 @@ static int lancer_cmd_read_file(struct be_adapter *adapter, u8 *file_name,
        int status = 0;
 
        read_cmd.size = LANCER_READ_FILE_CHUNK;
-       read_cmd.va = pci_alloc_consistent(adapter->pdev, read_cmd.size,
-                                          &read_cmd.dma);
+       read_cmd.va = dma_zalloc_coherent(&adapter->pdev->dev, read_cmd.size,
+                                         &read_cmd.dma, GFP_ATOMIC);
 
        if (!read_cmd.va) {
                dev_err(&adapter->pdev->dev,
@@ -289,8 +289,8 @@ static int lancer_cmd_read_file(struct be_adapter *adapter, u8 *file_name,
                        break;
                }
        }
-       pci_free_consistent(adapter->pdev, read_cmd.size, read_cmd.va,
-                           read_cmd.dma);
+       dma_free_coherent(&adapter->pdev->dev, read_cmd.size, read_cmd.va,
+                         read_cmd.dma);
 
        return status;
 }
@@ -818,8 +818,9 @@ static int be_test_ddr_dma(struct be_adapter *adapter)
        };
 
        ddrdma_cmd.size = sizeof(struct be_cmd_req_ddrdma_test);
-       ddrdma_cmd.va = dma_alloc_coherent(&adapter->pdev->dev, ddrdma_cmd.size,
-                                          &ddrdma_cmd.dma, GFP_KERNEL);
+       ddrdma_cmd.va = dma_zalloc_coherent(&adapter->pdev->dev,
+                                           ddrdma_cmd.size, &ddrdma_cmd.dma,
+                                           GFP_KERNEL);
        if (!ddrdma_cmd.va)
                return -ENOMEM;
 
@@ -941,8 +942,9 @@ static int be_read_eeprom(struct net_device *netdev,
 
        memset(&eeprom_cmd, 0, sizeof(struct be_dma_mem));
        eeprom_cmd.size = sizeof(struct be_cmd_req_seeprom_read);
-       eeprom_cmd.va = dma_alloc_coherent(&adapter->pdev->dev, eeprom_cmd.size,
-                                          &eeprom_cmd.dma, GFP_KERNEL);
+       eeprom_cmd.va = dma_zalloc_coherent(&adapter->pdev->dev,
+                                           eeprom_cmd.size, &eeprom_cmd.dma,
+                                           GFP_KERNEL);
 
        if (!eeprom_cmd.va)
                return -ENOMEM;
index 6f9ffb9026cd56825f90e0c300aab1c8cd2a7739..e43cc8a73ea7e85a927443c077c18ce6c673751a 100644 (file)
@@ -4605,8 +4605,8 @@ static int lancer_fw_download(struct be_adapter *adapter,
 
        flash_cmd.size = sizeof(struct lancer_cmd_req_write_object)
                                + LANCER_FW_DOWNLOAD_CHUNK;
-       flash_cmd.va = dma_alloc_coherent(dev, flash_cmd.size,
-                                         &flash_cmd.dma, GFP_KERNEL);
+       flash_cmd.va = dma_zalloc_coherent(dev, flash_cmd.size,
+                                          &flash_cmd.dma, GFP_KERNEL);
        if (!flash_cmd.va)
                return -ENOMEM;
 
@@ -4739,8 +4739,8 @@ static int be_fw_download(struct be_adapter *adapter, const struct firmware* fw)
        }
 
        flash_cmd.size = sizeof(struct be_cmd_write_flashrom);
-       flash_cmd.va = dma_alloc_coherent(dev, flash_cmd.size, &flash_cmd.dma,
-                                         GFP_KERNEL);
+       flash_cmd.va = dma_zalloc_coherent(dev, flash_cmd.size, &flash_cmd.dma,
+                                          GFP_KERNEL);
        if (!flash_cmd.va)
                return -ENOMEM;
 
@@ -5291,16 +5291,15 @@ static int be_drv_init(struct be_adapter *adapter)
        int status = 0;
 
        mbox_mem_alloc->size = sizeof(struct be_mcc_mailbox) + 16;
-       mbox_mem_alloc->va = dma_alloc_coherent(dev, mbox_mem_alloc->size,
-                                               &mbox_mem_alloc->dma,
-                                               GFP_KERNEL);
+       mbox_mem_alloc->va = dma_zalloc_coherent(dev, mbox_mem_alloc->size,
+                                                &mbox_mem_alloc->dma,
+                                                GFP_KERNEL);
        if (!mbox_mem_alloc->va)
                return -ENOMEM;
 
        mbox_mem_align->size = sizeof(struct be_mcc_mailbox);
        mbox_mem_align->va = PTR_ALIGN(mbox_mem_alloc->va, 16);
        mbox_mem_align->dma = PTR_ALIGN(mbox_mem_alloc->dma, 16);
-       memset(mbox_mem_align->va, 0, sizeof(struct be_mcc_mailbox));
 
        rx_filter->size = sizeof(struct be_cmd_req_rx_filter);
        rx_filter->va = dma_zalloc_coherent(dev, rx_filter->size,
index 33c35d3b7420fa9ae545aea4ebd5160036914718..5d47307121abbe413cd259ff74f9aa2ee68e6c45 100644 (file)
@@ -317,6 +317,7 @@ struct i40e_pf {
 #endif
 #define I40E_FLAG_PORT_ID_VALID                (u64)(1 << 28)
 #define I40E_FLAG_DCB_CAPABLE                  (u64)(1 << 29)
+#define I40E_FLAG_VEB_MODE_ENABLED             BIT_ULL(40)
 
        /* tracks features that get auto disabled by errors */
        u64 auto_disable_flags;
index 34170eabca7da939ba1c8b9b5fad14dc2f54370d..da0faf478af076199e4281b0f3da57ad92c5e62b 100644 (file)
@@ -1021,6 +1021,15 @@ static ssize_t i40e_dbg_command_write(struct file *filp,
                        goto command_write_done;
                }
 
+               /* By default we are in VEPA mode, if this is the first VF/VMDq
+                * VSI to be added switch to VEB mode.
+                */
+               if (!(pf->flags & I40E_FLAG_VEB_MODE_ENABLED)) {
+                       pf->flags |= I40E_FLAG_VEB_MODE_ENABLED;
+                       i40e_do_reset_safe(pf,
+                                          BIT_ULL(__I40E_PF_RESET_REQUESTED));
+               }
+
                vsi = i40e_vsi_setup(pf, I40E_VSI_VMDQ2, vsi_seid, 0);
                if (vsi)
                        dev_info(&pf->pdev->dev, "added VSI %d to relay %d\n",
index a54c14491e3b6a4dbc168980dd44d399b6766487..5b5bea159bd53c8684d0a69b310e492bc797c8b6 100644 (file)
@@ -6097,6 +6097,10 @@ static int i40e_reconstitute_veb(struct i40e_veb *veb)
        if (ret)
                goto end_reconstitute;
 
+       if (pf->flags & I40E_FLAG_VEB_MODE_ENABLED)
+               veb->bridge_mode = BRIDGE_MODE_VEB;
+       else
+               veb->bridge_mode = BRIDGE_MODE_VEPA;
        i40e_config_bridge_mode(veb);
 
        /* create the remaining VSIs attached to this VEB */
@@ -8031,7 +8035,12 @@ static int i40e_ndo_bridge_setlink(struct net_device *dev,
                } else if (mode != veb->bridge_mode) {
                        /* Existing HW bridge but different mode needs reset */
                        veb->bridge_mode = mode;
-                       i40e_do_reset(pf, (1 << __I40E_PF_RESET_REQUESTED));
+                       /* TODO: If no VFs or VMDq VSIs, disallow VEB mode */
+                       if (mode == BRIDGE_MODE_VEB)
+                               pf->flags |= I40E_FLAG_VEB_MODE_ENABLED;
+                       else
+                               pf->flags &= ~I40E_FLAG_VEB_MODE_ENABLED;
+                       i40e_do_reset(pf, BIT_ULL(__I40E_PF_RESET_REQUESTED));
                        break;
                }
        }
@@ -8343,11 +8352,12 @@ static int i40e_add_vsi(struct i40e_vsi *vsi)
                ctxt.uplink_seid = vsi->uplink_seid;
                ctxt.connection_type = I40E_AQ_VSI_CONN_TYPE_NORMAL;
                ctxt.flags = I40E_AQ_VSI_TYPE_PF;
-               if (i40e_is_vsi_uplink_mode_veb(vsi)) {
+               if ((pf->flags & I40E_FLAG_VEB_MODE_ENABLED) &&
+                   (i40e_is_vsi_uplink_mode_veb(vsi))) {
                        ctxt.info.valid_sections |=
-                               cpu_to_le16(I40E_AQ_VSI_PROP_SWITCH_VALID);
+                            cpu_to_le16(I40E_AQ_VSI_PROP_SWITCH_VALID);
                        ctxt.info.switch_id =
-                               cpu_to_le16(I40E_AQ_VSI_SW_ID_FLAG_ALLOW_LB);
+                          cpu_to_le16(I40E_AQ_VSI_SW_ID_FLAG_ALLOW_LB);
                }
                i40e_vsi_setup_queue_map(vsi, &ctxt, enabled_tc, true);
                break;
@@ -8746,6 +8756,14 @@ struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type,
                                         __func__);
                                return NULL;
                        }
+                       /* We come up by default in VEPA mode if SRIOV is not
+                        * already enabled, in which case we can't force VEPA
+                        * mode.
+                        */
+                       if (!(pf->flags & I40E_FLAG_VEB_MODE_ENABLED)) {
+                               veb->bridge_mode = BRIDGE_MODE_VEPA;
+                               pf->flags &= ~I40E_FLAG_VEB_MODE_ENABLED;
+                       }
                        i40e_config_bridge_mode(veb);
                }
                for (i = 0; i < I40E_MAX_VEB && !veb; i++) {
@@ -9856,6 +9874,15 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
                goto err_switch_setup;
        }
 
+#ifdef CONFIG_PCI_IOV
+       /* prep for VF support */
+       if ((pf->flags & I40E_FLAG_SRIOV_ENABLED) &&
+           (pf->flags & I40E_FLAG_MSIX_ENABLED) &&
+           !test_bit(__I40E_BAD_EEPROM, &pf->state)) {
+               if (pci_num_vf(pdev))
+                       pf->flags |= I40E_FLAG_VEB_MODE_ENABLED;
+       }
+#endif
        err = i40e_setup_pf_switch(pf, false);
        if (err) {
                dev_info(&pdev->dev, "setup_pf_switch failed: %d\n", err);
index 4bd3a80aba82998bba343a1870b2d21f59bca4e0..9d95042d5a0f5805824d53ecc847ff76a9909444 100644 (file)
@@ -2410,14 +2410,12 @@ static int i40e_maybe_stop_tx(struct i40e_ring *tx_ring, int size)
  * i40e_chk_linearize - Check if there are more than 8 fragments per packet
  * @skb:      send buffer
  * @tx_flags: collected send information
- * @hdr_len:  size of the packet header
  *
  * Note: Our HW can't scatter-gather more than 8 fragments to build
  * a packet on the wire and so we need to figure out the cases where we
  * need to linearize the skb.
  **/
-static bool i40e_chk_linearize(struct sk_buff *skb, u32 tx_flags,
-                              const u8 hdr_len)
+static bool i40e_chk_linearize(struct sk_buff *skb, u32 tx_flags)
 {
        struct skb_frag_struct *frag;
        bool linearize = false;
@@ -2429,7 +2427,7 @@ static bool i40e_chk_linearize(struct sk_buff *skb, u32 tx_flags,
        gso_segs = skb_shinfo(skb)->gso_segs;
 
        if (tx_flags & (I40E_TX_FLAGS_TSO | I40E_TX_FLAGS_FSO)) {
-               u16 j = 1;
+               u16 j = 0;
 
                if (num_frags < (I40E_MAX_BUFFER_TXD))
                        goto linearize_chk_done;
@@ -2440,21 +2438,18 @@ static bool i40e_chk_linearize(struct sk_buff *skb, u32 tx_flags,
                        goto linearize_chk_done;
                }
                frag = &skb_shinfo(skb)->frags[0];
-               size = hdr_len;
                /* we might still have more fragments per segment */
                do {
                        size += skb_frag_size(frag);
                        frag++; j++;
+                       if ((size >= skb_shinfo(skb)->gso_size) &&
+                           (j < I40E_MAX_BUFFER_TXD)) {
+                               size = (size % skb_shinfo(skb)->gso_size);
+                               j = (size) ? 1 : 0;
+                       }
                        if (j == I40E_MAX_BUFFER_TXD) {
-                               if (size < skb_shinfo(skb)->gso_size) {
-                                       linearize = true;
-                                       break;
-                               }
-                               j = 1;
-                               size -= skb_shinfo(skb)->gso_size;
-                               if (size)
-                                       j++;
-                               size += hdr_len;
+                               linearize = true;
+                               break;
                        }
                        num_frags--;
                } while (num_frags);
@@ -2724,7 +2719,7 @@ static netdev_tx_t i40e_xmit_frame_ring(struct sk_buff *skb,
        if (tsyn)
                tx_flags |= I40E_TX_FLAGS_TSYN;
 
-       if (i40e_chk_linearize(skb, tx_flags, hdr_len))
+       if (i40e_chk_linearize(skb, tx_flags))
                if (skb_linearize(skb))
                        goto out_drop;
 
index 78d1c4ff565e8853473b70c3827e6a727ff3ce1c..4e9376da051829969de7750c2dc7a66acc5e5f40 100644 (file)
@@ -1018,11 +1018,19 @@ int i40e_pci_sriov_configure(struct pci_dev *pdev, int num_vfs)
 {
        struct i40e_pf *pf = pci_get_drvdata(pdev);
 
-       if (num_vfs)
+       if (num_vfs) {
+               if (!(pf->flags & I40E_FLAG_VEB_MODE_ENABLED)) {
+                       pf->flags |= I40E_FLAG_VEB_MODE_ENABLED;
+                       i40e_do_reset_safe(pf,
+                                          BIT_ULL(__I40E_PF_RESET_REQUESTED));
+               }
                return i40e_pci_sriov_enable(pdev, num_vfs);
+       }
 
        if (!pci_vfs_assigned(pf->pdev)) {
                i40e_free_vfs(pf);
+               pf->flags &= ~I40E_FLAG_VEB_MODE_ENABLED;
+               i40e_do_reset_safe(pf, BIT_ULL(__I40E_PF_RESET_REQUESTED));
        } else {
                dev_warn(&pdev->dev, "Unable to free VFs because some are assigned to VMs.\n");
                return -EINVAL;
index b077e02a0cc7ac8f67ad90560cf990f8f7a66277..458fbb421090772d0bbc1620277624339e0cd757 100644 (file)
@@ -1619,14 +1619,12 @@ static void i40e_create_tx_ctx(struct i40e_ring *tx_ring,
  * i40e_chk_linearize - Check if there are more than 8 fragments per packet
  * @skb:      send buffer
  * @tx_flags: collected send information
- * @hdr_len:  size of the packet header
  *
  * Note: Our HW can't scatter-gather more than 8 fragments to build
  * a packet on the wire and so we need to figure out the cases where we
  * need to linearize the skb.
  **/
-static bool i40e_chk_linearize(struct sk_buff *skb, u32 tx_flags,
-                              const u8 hdr_len)
+static bool i40e_chk_linearize(struct sk_buff *skb, u32 tx_flags)
 {
        struct skb_frag_struct *frag;
        bool linearize = false;
@@ -1638,7 +1636,7 @@ static bool i40e_chk_linearize(struct sk_buff *skb, u32 tx_flags,
        gso_segs = skb_shinfo(skb)->gso_segs;
 
        if (tx_flags & (I40E_TX_FLAGS_TSO | I40E_TX_FLAGS_FSO)) {
-               u16 j = 1;
+               u16 j = 0;
 
                if (num_frags < (I40E_MAX_BUFFER_TXD))
                        goto linearize_chk_done;
@@ -1649,21 +1647,18 @@ static bool i40e_chk_linearize(struct sk_buff *skb, u32 tx_flags,
                        goto linearize_chk_done;
                }
                frag = &skb_shinfo(skb)->frags[0];
-               size = hdr_len;
                /* we might still have more fragments per segment */
                do {
                        size += skb_frag_size(frag);
                        frag++; j++;
+                       if ((size >= skb_shinfo(skb)->gso_size) &&
+                           (j < I40E_MAX_BUFFER_TXD)) {
+                               size = (size % skb_shinfo(skb)->gso_size);
+                               j = (size) ? 1 : 0;
+                       }
                        if (j == I40E_MAX_BUFFER_TXD) {
-                               if (size < skb_shinfo(skb)->gso_size) {
-                                       linearize = true;
-                                       break;
-                               }
-                               j = 1;
-                               size -= skb_shinfo(skb)->gso_size;
-                               if (size)
-                                       j++;
-                               size += hdr_len;
+                               linearize = true;
+                               break;
                        }
                        num_frags--;
                } while (num_frags);
@@ -1950,7 +1945,7 @@ static netdev_tx_t i40e_xmit_frame_ring(struct sk_buff *skb,
        else if (tso)
                tx_flags |= I40E_TX_FLAGS_TSO;
 
-       if (i40e_chk_linearize(skb, tx_flags, hdr_len))
+       if (i40e_chk_linearize(skb, tx_flags))
                if (skb_linearize(skb))
                        goto out_drop;
 
index e3b9b63ad01083cb987429f57c9ebef84d86f4db..c3a9392cbc192229f4178c913fad8ab64d8c44c3 100644 (file)
@@ -538,8 +538,8 @@ static int igb_ptp_feature_enable_i210(struct ptp_clock_info *ptp,
                        igb->perout[i].start.tv_nsec = rq->perout.start.nsec;
                        igb->perout[i].period.tv_sec = ts.tv_sec;
                        igb->perout[i].period.tv_nsec = ts.tv_nsec;
-                       wr32(trgttiml, rq->perout.start.sec);
-                       wr32(trgttimh, rq->perout.start.nsec);
+                       wr32(trgttimh, rq->perout.start.sec);
+                       wr32(trgttiml, rq->perout.start.nsec);
                        tsauxc |= tsauxc_mask;
                        tsim |= tsim_mask;
                } else {
index ced5ecab5aa754ad44ae055464608bba66d6b137..70de39c6a397efc66a595d4010c7354fae3fc276 100644 (file)
@@ -1674,6 +1674,25 @@ static int map_internal_clock(struct mlx4_dev *dev)
        return 0;
 }
 
+int mlx4_get_internal_clock_params(struct mlx4_dev *dev,
+                                  struct mlx4_clock_params *params)
+{
+       struct mlx4_priv *priv = mlx4_priv(dev);
+
+       if (mlx4_is_slave(dev))
+               return -ENOTSUPP;
+
+       if (!params)
+               return -EINVAL;
+
+       params->bar = priv->fw.clock_bar;
+       params->offset = priv->fw.clock_offset;
+       params->size = MLX4_CLOCK_SIZE;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(mlx4_get_internal_clock_params);
+
 static void unmap_internal_clock(struct mlx4_dev *dev)
 {
        struct mlx4_priv *priv = mlx4_priv(dev);
index ee1b0b965f34a3f4e29a71c79daf40e47693d67c..1368dac00da02a56bccdc076e662653920c91d50 100644 (file)
@@ -36,7 +36,7 @@
 #include <linux/mlx5/cmd.h>
 #include "mlx5_core.h"
 
-int mlx5_core_mad_ifc(struct mlx5_core_dev *dev, void *inb, void *outb,
+int mlx5_core_mad_ifc(struct mlx5_core_dev *dev, const void *inb, void *outb,
                      u16 opmod, u8 port)
 {
        struct mlx5_mad_ifc_mbox_in *in = NULL;
index 3dc1f68b322d357d14a915a115d92ebef8fef9a2..6ce973187225aec2668888009bfbc543975e8492 100644 (file)
@@ -3058,7 +3058,6 @@ static void cas_init_mac(struct cas *cp)
        /* setup core arbitration weight register */
        writel(CAWR_RR_DIS, cp->regs + REG_CAWR);
 
-       /* XXX Use pci_dma_burst_advice() */
 #if !defined(CONFIG_SPARC64) && !defined(CONFIG_ALPHA)
        /* set the infinite burst register for chips that don't have
         * pci issues.
index ea091bc5ff09dad379fde915fbb7ec073c613aa1..1e09243d5449d4f85b9c6e1eb9b0812c04c241f8 100644 (file)
@@ -28,6 +28,7 @@
 #include <linux/slab.h>
 #include <linux/netdevice.h>
 #include <linux/if_ether.h>
+#include <linux/vmalloc.h>
 #include <asm/sync_bitops.h>
 
 #include "hyperv_net.h"
index 9118cea918821cb6bbe83a2f97a71134a58fd5dd..35a482d526d9c5d0860e5a55e0a65f107e89ebd3 100644 (file)
@@ -27,6 +27,7 @@
 #include <linux/netdevice.h>
 #include <linux/if_vlan.h>
 #include <linux/nls.h>
+#include <linux/vmalloc.h>
 
 #include "hyperv_net.h"
 
index 66edd99bc302ddc5c5bdd0d0757acd7923adf8f4..7ddb1ab70891ce0b81fbf0c85fb13d093f1a0268 100644 (file)
@@ -35,7 +35,8 @@ static int mdio_mux_gpio_switch_fn(int current_child, int desired_child,
        for (n = 0; n < s->gpios->ndescs; n++)
                values[n] = (desired_child >> n) & 1;
 
-       gpiod_set_array_cansleep(s->gpios->ndescs, s->gpios->desc, values);
+       gpiod_set_array_value_cansleep(s->gpios->ndescs, s->gpios->desc,
+                                      values);
 
        return 0;
 }
index cd29b1038c5e3bf6f4a21659343c65584c44b969..3f6738612f453d60858f2b42df5eac78f5ea133e 100644 (file)
@@ -1313,8 +1313,6 @@ static int ntb_setup_intx(struct ntb_device *ndev)
        struct pci_dev *pdev = ndev->pdev;
        int rc;
 
-       pci_msi_off(pdev);
-
        /* Verify intx is enabled */
        pci_intx(pdev, 1);
 
@@ -1660,6 +1658,7 @@ static int ntb_atom_detect(struct ntb_device *ndev)
        u32 ppd;
 
        ndev->hw_type = BWD_HW;
+       ndev->limits.max_mw = BWD_MAX_MW;
 
        rc = pci_read_config_dword(ndev->pdev, NTB_PPD_OFFSET, &ppd);
        if (rc)
@@ -1778,7 +1777,7 @@ static int ntb_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
                        dev_warn(&pdev->dev, "Cannot remap BAR %d\n",
                                 MW_TO_BAR(i));
                        rc = -EIO;
-                       goto err3;
+                       goto err4;
                }
        }
 
index 78a7dcbec7d8990ac37adad938a0aff3420423e2..6906a3f61bd86d3874572c868480b8e7f6424c37 100644 (file)
@@ -765,7 +765,7 @@ unsigned long __weak pci_address_to_pio(phys_addr_t address)
        spin_lock(&io_range_lock);
        list_for_each_entry(res, &io_range_list, list) {
                if (address >= res->start && address < res->start + res->size) {
-                       addr = res->start - address + offset;
+                       addr = address - res->start + offset;
                        break;
                }
                offset += res->size;
index 99764db0875aa0e1b34ca348ca1606c2a8990258..f0650265febf95cc6a37d03dd3d5b38b0d7370af 100644 (file)
@@ -189,7 +189,7 @@ int __of_attach_node_sysfs(struct device_node *np)
        return 0;
 }
 
-static int __init of_init(void)
+void __init of_core_init(void)
 {
        struct device_node *np;
 
@@ -198,7 +198,8 @@ static int __init of_init(void)
        of_kset = kset_create_and_add("devicetree", NULL, firmware_kobj);
        if (!of_kset) {
                mutex_unlock(&of_mutex);
-               return -ENOMEM;
+               pr_err("devicetree: failed to register existing nodes\n");
+               return;
        }
        for_each_of_allnodes(np)
                __of_attach_node_sysfs(np);
@@ -207,10 +208,7 @@ static int __init of_init(void)
        /* Symlink in /proc as required by userspace ABI */
        if (of_root)
                proc_symlink("device-tree", NULL, "/sys/firmware/devicetree/base");
-
-       return 0;
 }
-core_initcall(of_init);
 
 static struct property *__of_find_property(const struct device_node *np,
                                           const char *name, int *lenp)
index 3351ef408125d757f52ac772700687ef7f735c06..53826b84e0ec6d46d3699705f46216070a471867 100644 (file)
@@ -225,7 +225,7 @@ void __of_attach_node(struct device_node *np)
        phandle = __of_get_property(np, "phandle", &sz);
        if (!phandle)
                phandle = __of_get_property(np, "linux,phandle", &sz);
-       if (IS_ENABLED(PPC_PSERIES) && !phandle)
+       if (IS_ENABLED(CONFIG_PPC_PSERIES) && !phandle)
                phandle = __of_get_property(np, "ibm,phandle", &sz);
        np->phandle = (phandle && (sz >= 4)) ? be32_to_cpup(phandle) : 0;
 
index cde35c5d0191bd5950c7953d5b8e88a2d139306f..f2dd23a32267d0476104af2547084d36d7d834f9 100644 (file)
@@ -580,11 +580,6 @@ void __init early_init_fdt_scan_reserved_mem(void)
        if (!initial_boot_params)
                return;
 
-       /* Reserve the dtb region */
-       early_init_dt_reserve_memory_arch(__pa(initial_boot_params),
-                                         fdt_totalsize(initial_boot_params),
-                                         0);
-
        /* Process header /memreserve/ fields */
        for (n = 0; ; n++) {
                fdt_get_mem_rsv(initial_boot_params, n, &base, &size);
@@ -597,6 +592,20 @@ void __init early_init_fdt_scan_reserved_mem(void)
        fdt_init_reserved_mem();
 }
 
+/**
+ * early_init_fdt_reserve_self() - reserve the memory used by the FDT blob
+ */
+void __init early_init_fdt_reserve_self(void)
+{
+       if (!initial_boot_params)
+               return;
+
+       /* Reserve the dtb region */
+       early_init_dt_reserve_memory_arch(__pa(initial_boot_params),
+                                         fdt_totalsize(initial_boot_params),
+                                         0);
+}
+
 /**
  * of_scan_flat_dt - scan flattened tree blob and call callback on each.
  * @it: callback function
index 7a8f1c5e65af19fce7bd7460fcc59cad5df2e905..73de4efcbe6edc85c8f3fb54e0c925a7ab30492b 100644 (file)
@@ -1,6 +1,10 @@
 #
 # PCI configuration
 #
+config PCI_BUS_ADDR_T_64BIT
+       def_bool y if (ARCH_DMA_ADDR_T_64BIT || 64BIT)
+       depends on PCI
+
 config PCI_MSI
        bool "Message Signaled Interrupts (MSI and MSI-X)"
        depends on PCI
index 90fa3a78fb7ce18018566f2f1f63e9c9f9be1dfd..6fbd3f2b5992a2cdd53c759147629292463789fd 100644 (file)
@@ -92,11 +92,11 @@ void pci_bus_remove_resources(struct pci_bus *bus)
 }
 
 static struct pci_bus_region pci_32_bit = {0, 0xffffffffULL};
-#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
+#ifdef CONFIG_PCI_BUS_ADDR_T_64BIT
 static struct pci_bus_region pci_64_bit = {0,
-                               (dma_addr_t) 0xffffffffffffffffULL};
-static struct pci_bus_region pci_high = {(dma_addr_t) 0x100000000ULL,
-                               (dma_addr_t) 0xffffffffffffffffULL};
+                               (pci_bus_addr_t) 0xffffffffffffffffULL};
+static struct pci_bus_region pci_high = {(pci_bus_addr_t) 0x100000000ULL,
+                               (pci_bus_addr_t) 0xffffffffffffffffULL};
 #endif
 
 /*
@@ -200,7 +200,7 @@ int pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res,
                                          resource_size_t),
                void *alignf_data)
 {
-#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
+#ifdef CONFIG_PCI_BUS_ADDR_T_64BIT
        int rc;
 
        if (res->flags & IORESOURCE_MEM_64) {
index 1dfb567b3522fca7d34bd86e4ed859f4579f46b6..c132bddc03f380a96a3c0acb5691a44df8fd2fa7 100644 (file)
@@ -89,11 +89,20 @@ config PCI_XGENE
        depends on ARCH_XGENE
        depends on OF
        select PCIEPORTBUS
+       select PCI_MSI_IRQ_DOMAIN if PCI_MSI
        help
          Say Y here if you want internal PCI support on APM X-Gene SoC.
          There are 5 internal PCIe ports available. Each port is GEN3 capable
          and have varied lanes from x1 to x8.
 
+config PCI_XGENE_MSI
+       bool "X-Gene v1 PCIe MSI feature"
+       depends on PCI_XGENE && PCI_MSI
+       default y
+       help
+         Say Y here if you want PCIe MSI support for the APM X-Gene v1 SoC.
+         This MSI driver supports 5 PCIe ports on the APM X-Gene v1 SoC.
+
 config PCI_LAYERSCAPE
        bool "Freescale Layerscape PCIe controller"
        depends on OF && ARM
@@ -125,4 +134,15 @@ config PCIE_IPROC_PLATFORM
          Say Y here if you want to use the Broadcom iProc PCIe controller
          through the generic platform bus interface
 
+config PCIE_IPROC_BCMA
+       bool "Broadcom iProc PCIe BCMA bus driver"
+       depends on ARCH_BCM_IPROC || (ARM && COMPILE_TEST)
+       select PCIE_IPROC
+       select BCMA
+       select PCI_DOMAINS
+       default ARCH_BCM_5301X
+       help
+         Say Y here if you want to use the Broadcom iProc PCIe controller
+         through the BCMA bus interface
+
 endmenu
index f733b4e27642ba34277ab92bca33201c38b88614..140d66f796e4b843673651bda56a00bdfb2317b1 100644 (file)
@@ -11,7 +11,9 @@ obj-$(CONFIG_PCIE_SPEAR13XX) += pcie-spear13xx.o
 obj-$(CONFIG_PCI_KEYSTONE) += pci-keystone-dw.o pci-keystone.o
 obj-$(CONFIG_PCIE_XILINX) += pcie-xilinx.o
 obj-$(CONFIG_PCI_XGENE) += pci-xgene.o
+obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
 obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
 obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
 obj-$(CONFIG_PCIE_IPROC) += pcie-iproc.o
 obj-$(CONFIG_PCIE_IPROC_PLATFORM) += pcie-iproc-platform.o
+obj-$(CONFIG_PCIE_IPROC_BCMA) += pcie-iproc-bcma.o
index 2d57e19a2cd43b02617eaff96046a4b75e36ad4f..80db09e47800dffb017d64177fe00775539d27b0 100644 (file)
@@ -93,9 +93,9 @@ static int dra7xx_pcie_link_up(struct pcie_port *pp)
 
 static int dra7xx_pcie_establish_link(struct pcie_port *pp)
 {
-       u32 reg;
-       unsigned int retries = 1000;
        struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pp);
+       u32 reg;
+       unsigned int retries;
 
        if (dw_pcie_link_up(pp)) {
                dev_err(pp->dev, "link is already up\n");
@@ -106,19 +106,14 @@ static int dra7xx_pcie_establish_link(struct pcie_port *pp)
        reg |= LTSSM_EN;
        dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_DEVICE_CMD, reg);
 
-       while (retries--) {
-               reg = dra7xx_pcie_readl(dra7xx, PCIECTRL_DRA7XX_CONF_PHY_CS);
-               if (reg & LINK_UP)
-                       break;
+       for (retries = 0; retries < 1000; retries++) {
+               if (dw_pcie_link_up(pp))
+                       return 0;
                usleep_range(10, 20);
        }
 
-       if (retries == 0) {
-               dev_err(pp->dev, "link is not up\n");
-               return -ETIMEDOUT;
-       }
-
-       return 0;
+       dev_err(pp->dev, "link is not up\n");
+       return -EINVAL;
 }
 
 static void dra7xx_pcie_enable_interrupts(struct pcie_port *pp)
index c139237e0e523cd9d9e36c9a9d7ce20b8a59f87f..f9f468d9a819d8402244e31805d51a034fb6392f 100644 (file)
@@ -316,9 +316,9 @@ static void exynos_pcie_assert_reset(struct pcie_port *pp)
 
 static int exynos_pcie_establish_link(struct pcie_port *pp)
 {
-       u32 val;
-       int count = 0;
        struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
+       u32 val;
+       unsigned int retries;
 
        if (dw_pcie_link_up(pp)) {
                dev_err(pp->dev, "Link already up\n");
@@ -357,27 +357,23 @@ static int exynos_pcie_establish_link(struct pcie_port *pp)
                          PCIE_APP_LTSSM_ENABLE);
 
        /* check if the link is up or not */
-       while (!dw_pcie_link_up(pp)) {
-               mdelay(100);
-               count++;
-               if (count == 10) {
-                       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);
-                       }
-                       /* power off phy */
-                       exynos_pcie_power_off_phy(pp);
-
-                       dev_err(pp->dev, "PCIe Link Fail\n");
-                       return -EINVAL;
+       for (retries = 0; retries < 10; retries++) {
+               if (dw_pcie_link_up(pp)) {
+                       dev_info(pp->dev, "Link up\n");
+                       return 0;
                }
+               mdelay(100);
        }
 
-       dev_info(pp->dev, "Link up\n");
+       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);
+       }
+       /* power off phy */
+       exynos_pcie_power_off_phy(pp);
 
-       return 0;
+       dev_err(pp->dev, "PCIe Link Fail\n");
+       return -EINVAL;
 }
 
 static void exynos_pcie_clear_irq_pulse(struct pcie_port *pp)
index fdb95367721e9b2e9758ee9f05beb4c878374dbd..233a196c6e6661c8dc9aea975a5cf15bf253c2fb 100644 (file)
@@ -47,6 +47,8 @@ struct imx6_pcie {
 #define PCIE_RC_LCR_MAX_LINK_SPEEDS_GEN2       0x2
 #define PCIE_RC_LCR_MAX_LINK_SPEEDS_MASK       0xf
 
+#define PCIE_RC_LCSR                           0x80
+
 /* PCIe Port Logic registers (memory-mapped) */
 #define PL_OFFSET 0x700
 #define PCIE_PL_PFLR (PL_OFFSET + 0x08)
@@ -335,21 +337,36 @@ static void imx6_pcie_init_phy(struct pcie_port *pp)
 
 static int imx6_pcie_wait_for_link(struct pcie_port *pp)
 {
-       int count = 200;
+       unsigned int retries;
 
-       while (!dw_pcie_link_up(pp)) {
+       for (retries = 0; retries < 200; retries++) {
+               if (dw_pcie_link_up(pp))
+                       return 0;
                usleep_range(100, 1000);
-               if (--count)
-                       continue;
-
-               dev_err(pp->dev, "phy link never came up\n");
-               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));
-               return -EINVAL;
        }
 
-       return 0;
+       dev_err(pp->dev, "phy link never came up\n");
+       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));
+       return -EINVAL;
+}
+
+static int imx6_pcie_wait_for_speed_change(struct pcie_port *pp)
+{
+       u32 tmp;
+       unsigned int retries;
+
+       for (retries = 0; retries < 200; retries++) {
+               tmp = readl(pp->dbi_base + 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");
+       return -EINVAL;
 }
 
 static irqreturn_t imx6_pcie_msi_handler(int irq, void *arg)
@@ -359,11 +376,11 @@ static irqreturn_t imx6_pcie_msi_handler(int irq, void *arg)
        return dw_handle_msi_irq(pp);
 }
 
-static int imx6_pcie_start_link(struct pcie_port *pp)
+static int imx6_pcie_establish_link(struct pcie_port *pp)
 {
        struct imx6_pcie *imx6_pcie = to_imx6_pcie(pp);
-       uint32_t tmp;
-       int ret, count;
+       u32 tmp;
+       int ret;
 
        /*
         * Force Gen1 operation when starting the link.  In case the link is
@@ -397,29 +414,22 @@ static int imx6_pcie_start_link(struct pcie_port *pp)
        tmp |= PORT_LOGIC_SPEED_CHANGE;
        writel(tmp, pp->dbi_base + PCIE_LINK_WIDTH_SPEED_CONTROL);
 
-       count = 200;
-       while (count--) {
-               tmp = readl(pp->dbi_base + PCIE_LINK_WIDTH_SPEED_CONTROL);
-               /* Test if the speed change finished. */
-               if (!(tmp & PORT_LOGIC_SPEED_CHANGE))
-                       break;
-               usleep_range(100, 1000);
+       ret = imx6_pcie_wait_for_speed_change(pp);
+       if (ret) {
+               dev_err(pp->dev, "Failed to bring link up!\n");
+               return ret;
        }
 
        /* Make sure link training is finished as well! */
-       if (count)
-               ret = imx6_pcie_wait_for_link(pp);
-       else
-               ret = -EINVAL;
-
+       ret = imx6_pcie_wait_for_link(pp);
        if (ret) {
                dev_err(pp->dev, "Failed to bring link up!\n");
-       } else {
-               tmp = readl(pp->dbi_base + 0x80);
-               dev_dbg(pp->dev, "Link up, Gen=%i\n", (tmp >> 16) & 0xf);
+               return ret;
        }
 
-       return ret;
+       tmp = readl(pp->dbi_base + PCIE_RC_LCSR);
+       dev_dbg(pp->dev, "Link up, Gen=%i\n", (tmp >> 16) & 0xf);
+       return 0;
 }
 
 static void imx6_pcie_host_init(struct pcie_port *pp)
@@ -432,7 +442,7 @@ static void imx6_pcie_host_init(struct pcie_port *pp)
 
        dw_pcie_setup_rc(pp);
 
-       imx6_pcie_start_link(pp);
+       imx6_pcie_establish_link(pp);
 
        if (IS_ENABLED(CONFIG_PCI_MSI))
                dw_pcie_msi_init(pp);
@@ -440,19 +450,19 @@ static void imx6_pcie_host_init(struct pcie_port *pp)
 
 static void imx6_pcie_reset_phy(struct pcie_port *pp)
 {
-       uint32_t temp;
+       u32 tmp;
 
-       pcie_phy_read(pp->dbi_base, PHY_RX_OVRD_IN_LO, &temp);
-       temp |= (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, temp);
+       pcie_phy_read(pp->dbi_base, 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);
 
        usleep_range(2000, 3000);
 
-       pcie_phy_read(pp->dbi_base, PHY_RX_OVRD_IN_LO, &temp);
-       temp &= ~(PHY_RX_OVRD_IN_LO_RX_DATA_EN |
+       pcie_phy_read(pp->dbi_base, 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, temp);
+       pcie_phy_write(pp->dbi_base, PHY_RX_OVRD_IN_LO, tmp);
 }
 
 static int imx6_pcie_link_up(struct pcie_port *pp)
index 75333b0c4f0ab8eee2927f584a98d11bebd22922..b75d684aefcd78a81947c269c81215f8cc5020d4 100644 (file)
@@ -88,7 +88,7 @@ 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;
-       int count = 200;
+       unsigned int retries;
 
        dw_pcie_setup_rc(pp);
 
@@ -99,17 +99,15 @@ static int ks_pcie_establish_link(struct keystone_pcie *ks_pcie)
 
        ks_dw_pcie_initiate_link_train(ks_pcie);
        /* check if the link is up or not */
-       while (!dw_pcie_link_up(pp)) {
+       for (retries = 0; retries < 200; retries++) {
+               if (dw_pcie_link_up(pp))
+                       return 0;
                usleep_range(100, 1000);
-               if (--count) {
-                       ks_dw_pcie_initiate_link_train(ks_pcie);
-                       continue;
-               }
-               dev_err(pp->dev, "phy link never came up\n");
-               return -EINVAL;
+               ks_dw_pcie_initiate_link_train(ks_pcie);
        }
 
-       return 0;
+       dev_err(pp->dev, "phy link never came up\n");
+       return -EINVAL;
 }
 
 static void ks_pcie_msi_irq_handler(unsigned int irq, struct irq_desc *desc)
index 4a6e62f67579807476ae973006855d28cbfab0d6..b2328ea13dcfe1cad3ee232c8b45f7a66cda7984 100644 (file)
@@ -62,22 +62,27 @@ static int ls_pcie_link_up(struct pcie_port *pp)
        return 1;
 }
 
+static int ls_pcie_establish_link(struct pcie_port *pp)
+{
+       unsigned int retries;
+
+       for (retries = 0; retries < 200; retries++) {
+               if (dw_pcie_link_up(pp))
+                       return 0;
+               usleep_range(100, 1000);
+       }
+
+       dev_err(pp->dev, "phy link never came up\n");
+       return -EINVAL;
+}
+
 static void ls_pcie_host_init(struct pcie_port *pp)
 {
        struct ls_pcie *pcie = to_ls_pcie(pp);
-       int count = 0;
        u32 val;
 
        dw_pcie_setup_rc(pp);
-
-       while (!ls_pcie_link_up(pp)) {
-               usleep_range(100, 1000);
-               count++;
-               if (count >= 200) {
-                       dev_err(pp->dev, "phy link never came up\n");
-                       return;
-               }
-       }
+       ls_pcie_establish_link(pp);
 
        /*
         * LS1021A Workaround for internal TKT228622
index 1ab863551920214dd95ec45dc7f42db398195503..70aa09556ec5ce3b5866766b7c7fec7bab332b19 100644 (file)
@@ -751,21 +751,6 @@ static int mvebu_pcie_setup(int nr, struct pci_sys_data *sys)
        return 1;
 }
 
-static struct pci_bus *mvebu_pcie_scan_bus(int nr, struct pci_sys_data *sys)
-{
-       struct mvebu_pcie *pcie = sys_to_pcie(sys);
-       struct pci_bus *bus;
-
-       bus = pci_create_root_bus(&pcie->pdev->dev, sys->busnr,
-                                 &mvebu_pcie_ops, sys, &sys->resources);
-       if (!bus)
-               return NULL;
-
-       pci_scan_child_bus(bus);
-
-       return bus;
-}
-
 static resource_size_t mvebu_pcie_align_resource(struct pci_dev *dev,
                                                 const struct resource *res,
                                                 resource_size_t start,
@@ -809,12 +794,11 @@ static void mvebu_pcie_enable(struct mvebu_pcie *pcie)
        hw.nr_controllers = 1;
        hw.private_data   = (void **)&pcie;
        hw.setup          = mvebu_pcie_setup;
-       hw.scan           = mvebu_pcie_scan_bus;
        hw.map_irq        = of_irq_parse_and_map_pci;
        hw.ops            = &mvebu_pcie_ops;
        hw.align_resource = mvebu_pcie_align_resource;
 
-       pci_common_init(&hw);
+       pci_common_init_dev(&pcie->pdev->dev, &hw);
 }
 
 /*
index 00e92720d7f79a37c1eca85d4059fc8d535b9107..10c05718dbfdcb27c31617b6b6ea725ff9d437d7 100644 (file)
@@ -630,21 +630,6 @@ static int tegra_pcie_map_irq(const struct pci_dev *pdev, u8 slot, u8 pin)
        return irq;
 }
 
-static struct pci_bus *tegra_pcie_scan_bus(int nr, struct pci_sys_data *sys)
-{
-       struct tegra_pcie *pcie = sys_to_pcie(sys);
-       struct pci_bus *bus;
-
-       bus = pci_create_root_bus(pcie->dev, sys->busnr, &tegra_pcie_ops, sys,
-                                 &sys->resources);
-       if (!bus)
-               return NULL;
-
-       pci_scan_child_bus(bus);
-
-       return bus;
-}
-
 static irqreturn_t tegra_pcie_isr(int irq, void *arg)
 {
        const char *err_msg[] = {
@@ -1831,7 +1816,6 @@ static int tegra_pcie_enable(struct tegra_pcie *pcie)
        hw.private_data = (void **)&pcie;
        hw.setup = tegra_pcie_setup;
        hw.map_irq = tegra_pcie_map_irq;
-       hw.scan = tegra_pcie_scan_bus;
        hw.ops = &tegra_pcie_ops;
 
        pci_common_init_dev(pcie->dev, &hw);
diff --git a/drivers/pci/host/pci-xgene-msi.c b/drivers/pci/host/pci-xgene-msi.c
new file mode 100644 (file)
index 0000000..2d31d4d
--- /dev/null
@@ -0,0 +1,596 @@
+/*
+ * APM X-Gene MSI Driver
+ *
+ * Copyright (c) 2014, Applied Micro Circuits Corporation
+ * Author: Tanmay Inamdar <tinamdar@apm.com>
+ *        Duc Dang <dhdang@apm.com>
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <linux/cpu.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/msi.h>
+#include <linux/of_irq.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/of_pci.h>
+
+#define MSI_IR0                        0x000000
+#define MSI_INT0               0x800000
+#define IDX_PER_GROUP          8
+#define IRQS_PER_IDX           16
+#define NR_HW_IRQS             16
+#define NR_MSI_VEC             (IDX_PER_GROUP * IRQS_PER_IDX * NR_HW_IRQS)
+
+struct xgene_msi_group {
+       struct xgene_msi        *msi;
+       int                     gic_irq;
+       u32                     msi_grp;
+};
+
+struct xgene_msi {
+       struct device_node      *node;
+       struct msi_controller   mchip;
+       struct irq_domain       *domain;
+       u64                     msi_addr;
+       void __iomem            *msi_regs;
+       unsigned long           *bitmap;
+       struct mutex            bitmap_lock;
+       struct xgene_msi_group  *msi_groups;
+       int                     num_cpus;
+};
+
+/* Global data */
+static struct xgene_msi xgene_msi_ctrl;
+
+static struct irq_chip xgene_msi_top_irq_chip = {
+       .name           = "X-Gene1 MSI",
+       .irq_enable     = pci_msi_unmask_irq,
+       .irq_disable    = pci_msi_mask_irq,
+       .irq_mask       = pci_msi_mask_irq,
+       .irq_unmask     = pci_msi_unmask_irq,
+};
+
+static struct  msi_domain_info xgene_msi_domain_info = {
+       .flags  = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
+                 MSI_FLAG_PCI_MSIX),
+       .chip   = &xgene_msi_top_irq_chip,
+};
+
+/*
+ * X-Gene v1 has 16 groups of MSI termination registers MSInIRx, where
+ * n is group number (0..F), x is index of registers in each group (0..7)
+ * The register layout is as follows:
+ * MSI0IR0                     base_addr
+ * MSI0IR1                     base_addr +  0x10000
+ * ...                         ...
+ * MSI0IR6                     base_addr +  0x60000
+ * MSI0IR7                     base_addr +  0x70000
+ * MSI1IR0                     base_addr +  0x80000
+ * MSI1IR1                     base_addr +  0x90000
+ * ...                         ...
+ * MSI1IR7                     base_addr +  0xF0000
+ * MSI2IR0                     base_addr + 0x100000
+ * ...                         ...
+ * MSIFIR0                     base_addr + 0x780000
+ * MSIFIR1                     base_addr + 0x790000
+ * ...                         ...
+ * MSIFIR7                     base_addr + 0x7F0000
+ * MSIINT0                     base_addr + 0x800000
+ * MSIINT1                     base_addr + 0x810000
+ * ...                         ...
+ * MSIINTF                     base_addr + 0x8F0000
+ *
+ * Each index register supports 16 MSI vectors (0..15) to generate interrupt.
+ * There are total 16 GIC IRQs assigned for these 16 groups of MSI termination
+ * registers.
+ *
+ * Each MSI termination group has 1 MSIINTn register (n is 0..15) to indicate
+ * the MSI pending status caused by 1 of its 8 index registers.
+ */
+
+/* MSInIRx read helper */
+static u32 xgene_msi_ir_read(struct xgene_msi *msi,
+                                   u32 msi_grp, u32 msir_idx)
+{
+       return readl_relaxed(msi->msi_regs + MSI_IR0 +
+                             (msi_grp << 19) + (msir_idx << 16));
+}
+
+/* MSIINTn read helper */
+static u32 xgene_msi_int_read(struct xgene_msi *msi, u32 msi_grp)
+{
+       return readl_relaxed(msi->msi_regs + MSI_INT0 + (msi_grp << 16));
+}
+
+/*
+ * With 2048 MSI vectors supported, the MSI message can be constructed using
+ * following scheme:
+ * - Divide into 8 256-vector groups
+ *             Group 0: 0-255
+ *             Group 1: 256-511
+ *             Group 2: 512-767
+ *             ...
+ *             Group 7: 1792-2047
+ * - Each 256-vector group is divided into 16 16-vector groups
+ *     As an example: 16 16-vector groups for 256-vector group 0-255 is
+ *             Group 0: 0-15
+ *             Group 1: 16-32
+ *             ...
+ *             Group 15: 240-255
+ * - The termination address of MSI vector in 256-vector group n and 16-vector
+ *   group x is the address of MSIxIRn
+ * - The data for MSI vector in 16-vector group x is x
+ */
+static u32 hwirq_to_reg_set(unsigned long hwirq)
+{
+       return (hwirq / (NR_HW_IRQS * IRQS_PER_IDX));
+}
+
+static u32 hwirq_to_group(unsigned long hwirq)
+{
+       return (hwirq % NR_HW_IRQS);
+}
+
+static u32 hwirq_to_msi_data(unsigned long hwirq)
+{
+       return ((hwirq / NR_HW_IRQS) % IRQS_PER_IDX);
+}
+
+static void xgene_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
+{
+       struct xgene_msi *msi = irq_data_get_irq_chip_data(data);
+       u32 reg_set = hwirq_to_reg_set(data->hwirq);
+       u32 group = hwirq_to_group(data->hwirq);
+       u64 target_addr = msi->msi_addr + (((8 * group) + reg_set) << 16);
+
+       msg->address_hi = upper_32_bits(target_addr);
+       msg->address_lo = lower_32_bits(target_addr);
+       msg->data = hwirq_to_msi_data(data->hwirq);
+}
+
+/*
+ * X-Gene v1 only has 16 MSI GIC IRQs for 2048 MSI vectors.  To maintain
+ * the expected behaviour of .set_affinity for each MSI interrupt, the 16
+ * MSI GIC IRQs are statically allocated to 8 X-Gene v1 cores (2 GIC IRQs
+ * for each core).  The MSI vector is moved fom 1 MSI GIC IRQ to another
+ * MSI GIC IRQ to steer its MSI interrupt to correct X-Gene v1 core.  As a
+ * consequence, the total MSI vectors that X-Gene v1 supports will be
+ * reduced to 256 (2048/8) vectors.
+ */
+static int hwirq_to_cpu(unsigned long hwirq)
+{
+       return (hwirq % xgene_msi_ctrl.num_cpus);
+}
+
+static unsigned long hwirq_to_canonical_hwirq(unsigned long hwirq)
+{
+       return (hwirq - hwirq_to_cpu(hwirq));
+}
+
+static int xgene_msi_set_affinity(struct irq_data *irqdata,
+                                 const struct cpumask *mask, bool force)
+{
+       int target_cpu = cpumask_first(mask);
+       int curr_cpu;
+
+       curr_cpu = hwirq_to_cpu(irqdata->hwirq);
+       if (curr_cpu == target_cpu)
+               return IRQ_SET_MASK_OK_DONE;
+
+       /* Update MSI number to target the new CPU */
+       irqdata->hwirq = hwirq_to_canonical_hwirq(irqdata->hwirq) + target_cpu;
+
+       return IRQ_SET_MASK_OK;
+}
+
+static struct irq_chip xgene_msi_bottom_irq_chip = {
+       .name                   = "MSI",
+       .irq_set_affinity       = xgene_msi_set_affinity,
+       .irq_compose_msi_msg    = xgene_compose_msi_msg,
+};
+
+static int xgene_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
+                                 unsigned int nr_irqs, void *args)
+{
+       struct xgene_msi *msi = domain->host_data;
+       int msi_irq;
+
+       mutex_lock(&msi->bitmap_lock);
+
+       msi_irq = bitmap_find_next_zero_area(msi->bitmap, NR_MSI_VEC, 0,
+                                            msi->num_cpus, 0);
+       if (msi_irq < NR_MSI_VEC)
+               bitmap_set(msi->bitmap, msi_irq, msi->num_cpus);
+       else
+               msi_irq = -ENOSPC;
+
+       mutex_unlock(&msi->bitmap_lock);
+
+       if (msi_irq < 0)
+               return msi_irq;
+
+       irq_domain_set_info(domain, virq, msi_irq,
+                           &xgene_msi_bottom_irq_chip, domain->host_data,
+                           handle_simple_irq, NULL, NULL);
+       set_irq_flags(virq, IRQF_VALID);
+
+       return 0;
+}
+
+static void xgene_irq_domain_free(struct irq_domain *domain,
+                                 unsigned int virq, unsigned int nr_irqs)
+{
+       struct irq_data *d = irq_domain_get_irq_data(domain, virq);
+       struct xgene_msi *msi = irq_data_get_irq_chip_data(d);
+       u32 hwirq;
+
+       mutex_lock(&msi->bitmap_lock);
+
+       hwirq = hwirq_to_canonical_hwirq(d->hwirq);
+       bitmap_clear(msi->bitmap, hwirq, msi->num_cpus);
+
+       mutex_unlock(&msi->bitmap_lock);
+
+       irq_domain_free_irqs_parent(domain, virq, nr_irqs);
+}
+
+static const struct irq_domain_ops msi_domain_ops = {
+       .alloc  = xgene_irq_domain_alloc,
+       .free   = xgene_irq_domain_free,
+};
+
+static int xgene_allocate_domains(struct xgene_msi *msi)
+{
+       msi->domain = irq_domain_add_linear(NULL, NR_MSI_VEC,
+                                           &msi_domain_ops, msi);
+       if (!msi->domain)
+               return -ENOMEM;
+
+       msi->mchip.domain = pci_msi_create_irq_domain(msi->mchip.of_node,
+                                                     &xgene_msi_domain_info,
+                                                     msi->domain);
+
+       if (!msi->mchip.domain) {
+               irq_domain_remove(msi->domain);
+               return -ENOMEM;
+       }
+
+       return 0;
+}
+
+static void xgene_free_domains(struct xgene_msi *msi)
+{
+       if (msi->mchip.domain)
+               irq_domain_remove(msi->mchip.domain);
+       if (msi->domain)
+               irq_domain_remove(msi->domain);
+}
+
+static int xgene_msi_init_allocator(struct xgene_msi *xgene_msi)
+{
+       int size = BITS_TO_LONGS(NR_MSI_VEC) * sizeof(long);
+
+       xgene_msi->bitmap = kzalloc(size, GFP_KERNEL);
+       if (!xgene_msi->bitmap)
+               return -ENOMEM;
+
+       mutex_init(&xgene_msi->bitmap_lock);
+
+       xgene_msi->msi_groups = kcalloc(NR_HW_IRQS,
+                                       sizeof(struct xgene_msi_group),
+                                       GFP_KERNEL);
+       if (!xgene_msi->msi_groups)
+               return -ENOMEM;
+
+       return 0;
+}
+
+static void xgene_msi_isr(unsigned int irq, struct irq_desc *desc)
+{
+       struct irq_chip *chip = irq_desc_get_chip(desc);
+       struct xgene_msi_group *msi_groups;
+       struct xgene_msi *xgene_msi;
+       unsigned int virq;
+       int msir_index, msir_val, hw_irq;
+       u32 intr_index, grp_select, msi_grp;
+
+       chained_irq_enter(chip, desc);
+
+       msi_groups = irq_desc_get_handler_data(desc);
+       xgene_msi = msi_groups->msi;
+       msi_grp = msi_groups->msi_grp;
+
+       /*
+        * MSIINTn (n is 0..F) indicates if there is a pending MSI interrupt
+        * If bit x of this register is set (x is 0..7), one or more interupts
+        * corresponding to MSInIRx is set.
+        */
+       grp_select = xgene_msi_int_read(xgene_msi, msi_grp);
+       while (grp_select) {
+               msir_index = ffs(grp_select) - 1;
+               /*
+                * Calculate MSInIRx address to read to check for interrupts
+                * (refer to termination address and data assignment
+                * described in xgene_compose_msi_msg() )
+                */
+               msir_val = xgene_msi_ir_read(xgene_msi, msi_grp, msir_index);
+               while (msir_val) {
+                       intr_index = ffs(msir_val) - 1;
+                       /*
+                        * Calculate MSI vector number (refer to the termination
+                        * address and data assignment described in
+                        * xgene_compose_msi_msg function)
+                        */
+                       hw_irq = (((msir_index * IRQS_PER_IDX) + intr_index) *
+                                NR_HW_IRQS) + msi_grp;
+                       /*
+                        * As we have multiple hw_irq that maps to single MSI,
+                        * always look up the virq using the hw_irq as seen from
+                        * CPU0
+                        */
+                       hw_irq = hwirq_to_canonical_hwirq(hw_irq);
+                       virq = irq_find_mapping(xgene_msi->domain, hw_irq);
+                       WARN_ON(!virq);
+                       if (virq != 0)
+                               generic_handle_irq(virq);
+                       msir_val &= ~(1 << intr_index);
+               }
+               grp_select &= ~(1 << msir_index);
+
+               if (!grp_select) {
+                       /*
+                        * We handled all interrupts happened in this group,
+                        * resample this group MSI_INTx register in case
+                        * something else has been made pending in the meantime
+                        */
+                       grp_select = xgene_msi_int_read(xgene_msi, msi_grp);
+               }
+       }
+
+       chained_irq_exit(chip, desc);
+}
+
+static int xgene_msi_remove(struct platform_device *pdev)
+{
+       int virq, i;
+       struct xgene_msi *msi = platform_get_drvdata(pdev);
+
+       for (i = 0; i < NR_HW_IRQS; i++) {
+               virq = msi->msi_groups[i].gic_irq;
+               if (virq != 0) {
+                       irq_set_chained_handler(virq, NULL);
+                       irq_set_handler_data(virq, NULL);
+               }
+       }
+       kfree(msi->msi_groups);
+
+       kfree(msi->bitmap);
+       msi->bitmap = NULL;
+
+       xgene_free_domains(msi);
+
+       return 0;
+}
+
+static int xgene_msi_hwirq_alloc(unsigned int cpu)
+{
+       struct xgene_msi *msi = &xgene_msi_ctrl;
+       struct xgene_msi_group *msi_group;
+       cpumask_var_t mask;
+       int i;
+       int err;
+
+       for (i = cpu; i < NR_HW_IRQS; i += msi->num_cpus) {
+               msi_group = &msi->msi_groups[i];
+               if (!msi_group->gic_irq)
+                       continue;
+
+               irq_set_chained_handler(msi_group->gic_irq,
+                                       xgene_msi_isr);
+               err = irq_set_handler_data(msi_group->gic_irq, msi_group);
+               if (err) {
+                       pr_err("failed to register GIC IRQ handler\n");
+                       return -EINVAL;
+               }
+               /*
+                * Statically allocate MSI GIC IRQs to each CPU core.
+                * With 8-core X-Gene v1, 2 MSI GIC IRQs are allocated
+                * to each core.
+                */
+               if (alloc_cpumask_var(&mask, GFP_KERNEL)) {
+                       cpumask_clear(mask);
+                       cpumask_set_cpu(cpu, mask);
+                       err = irq_set_affinity(msi_group->gic_irq, mask);
+                       if (err)
+                               pr_err("failed to set affinity for GIC IRQ");
+                       free_cpumask_var(mask);
+               } else {
+                       pr_err("failed to alloc CPU mask for affinity\n");
+                       err = -EINVAL;
+               }
+
+               if (err) {
+                       irq_set_chained_handler(msi_group->gic_irq, NULL);
+                       irq_set_handler_data(msi_group->gic_irq, NULL);
+                       return err;
+               }
+       }
+
+       return 0;
+}
+
+static void xgene_msi_hwirq_free(unsigned int cpu)
+{
+       struct xgene_msi *msi = &xgene_msi_ctrl;
+       struct xgene_msi_group *msi_group;
+       int i;
+
+       for (i = cpu; i < NR_HW_IRQS; i += msi->num_cpus) {
+               msi_group = &msi->msi_groups[i];
+               if (!msi_group->gic_irq)
+                       continue;
+
+               irq_set_chained_handler(msi_group->gic_irq, NULL);
+               irq_set_handler_data(msi_group->gic_irq, NULL);
+       }
+}
+
+static int xgene_msi_cpu_callback(struct notifier_block *nfb,
+                                 unsigned long action, void *hcpu)
+{
+       unsigned cpu = (unsigned long)hcpu;
+
+       switch (action) {
+       case CPU_ONLINE:
+       case CPU_ONLINE_FROZEN:
+               xgene_msi_hwirq_alloc(cpu);
+               break;
+       case CPU_DEAD:
+       case CPU_DEAD_FROZEN:
+               xgene_msi_hwirq_free(cpu);
+               break;
+       default:
+               break;
+       }
+
+       return NOTIFY_OK;
+}
+
+static struct notifier_block xgene_msi_cpu_notifier = {
+       .notifier_call = xgene_msi_cpu_callback,
+};
+
+static const struct of_device_id xgene_msi_match_table[] = {
+       {.compatible = "apm,xgene1-msi"},
+       {},
+};
+
+static int xgene_msi_probe(struct platform_device *pdev)
+{
+       struct resource *res;
+       int rc, irq_index;
+       struct xgene_msi *xgene_msi;
+       unsigned int cpu;
+       int virt_msir;
+       u32 msi_val, msi_idx;
+
+       xgene_msi = &xgene_msi_ctrl;
+
+       platform_set_drvdata(pdev, xgene_msi);
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       xgene_msi->msi_regs = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(xgene_msi->msi_regs)) {
+               dev_err(&pdev->dev, "no reg space\n");
+               rc = -EINVAL;
+               goto error;
+       }
+       xgene_msi->msi_addr = res->start;
+
+       xgene_msi->num_cpus = num_possible_cpus();
+
+       rc = xgene_msi_init_allocator(xgene_msi);
+       if (rc) {
+               dev_err(&pdev->dev, "Error allocating MSI bitmap\n");
+               goto error;
+       }
+
+       rc = xgene_allocate_domains(xgene_msi);
+       if (rc) {
+               dev_err(&pdev->dev, "Failed to allocate MSI domain\n");
+               goto error;
+       }
+
+       for (irq_index = 0; irq_index < NR_HW_IRQS; irq_index++) {
+               virt_msir = platform_get_irq(pdev, irq_index);
+               if (virt_msir < 0) {
+                       dev_err(&pdev->dev, "Cannot translate IRQ index %d\n",
+                               irq_index);
+                       rc = -EINVAL;
+                       goto error;
+               }
+               xgene_msi->msi_groups[irq_index].gic_irq = virt_msir;
+               xgene_msi->msi_groups[irq_index].msi_grp = irq_index;
+               xgene_msi->msi_groups[irq_index].msi = xgene_msi;
+       }
+
+       /*
+        * MSInIRx registers are read-to-clear; before registering
+        * interrupt handlers, read all of them to clear spurious
+        * interrupts that may occur before the driver is probed.
+        */
+       for (irq_index = 0; irq_index < NR_HW_IRQS; irq_index++) {
+               for (msi_idx = 0; msi_idx < IDX_PER_GROUP; msi_idx++)
+                       msi_val = xgene_msi_ir_read(xgene_msi, irq_index,
+                                                   msi_idx);
+               /* Read MSIINTn to confirm */
+               msi_val = xgene_msi_int_read(xgene_msi, irq_index);
+               if (msi_val) {
+                       dev_err(&pdev->dev, "Failed to clear spurious IRQ\n");
+                       rc = -EINVAL;
+                       goto error;
+               }
+       }
+
+       cpu_notifier_register_begin();
+
+       for_each_online_cpu(cpu)
+               if (xgene_msi_hwirq_alloc(cpu)) {
+                       dev_err(&pdev->dev, "failed to register MSI handlers\n");
+                       cpu_notifier_register_done();
+                       goto error;
+               }
+
+       rc = __register_hotcpu_notifier(&xgene_msi_cpu_notifier);
+       if (rc) {
+               dev_err(&pdev->dev, "failed to add CPU MSI notifier\n");
+               cpu_notifier_register_done();
+               goto error;
+       }
+
+       cpu_notifier_register_done();
+
+       xgene_msi->mchip.of_node = pdev->dev.of_node;
+       rc = of_pci_msi_chip_add(&xgene_msi->mchip);
+       if (rc) {
+               dev_err(&pdev->dev, "failed to add MSI controller chip\n");
+               goto error_notifier;
+       }
+
+       dev_info(&pdev->dev, "APM X-Gene PCIe MSI driver loaded\n");
+
+       return 0;
+
+error_notifier:
+       unregister_hotcpu_notifier(&xgene_msi_cpu_notifier);
+error:
+       xgene_msi_remove(pdev);
+       return rc;
+}
+
+static struct platform_driver xgene_msi_driver = {
+       .driver = {
+               .name = "xgene-msi",
+               .owner = THIS_MODULE,
+               .of_match_table = xgene_msi_match_table,
+       },
+       .probe = xgene_msi_probe,
+       .remove = xgene_msi_remove,
+};
+
+static int __init xgene_pcie_msi_init(void)
+{
+       return platform_driver_register(&xgene_msi_driver);
+}
+subsys_initcall(xgene_pcie_msi_init);
index ee082c0366ecca0d9978f9b23589646a0999a174..a9dfb70d623ae0acbb54d96ada58a68ecc3074fe 100644 (file)
 #define SZ_1T                          (SZ_1G*1024ULL)
 #define PIPE_PHY_RATE_RD(src)          ((0xc000 & (u32)(src)) >> 0xe)
 
+#define ROOT_CAP_AND_CTRL              0x5C
+
+/* PCIe IP version */
+#define XGENE_PCIE_IP_VER_UNKN         0
+#define XGENE_PCIE_IP_VER_1            1
+
 struct xgene_pcie_port {
        struct device_node      *node;
        struct device           *dev;
@@ -67,6 +73,7 @@ struct xgene_pcie_port {
        void __iomem            *cfg_base;
        unsigned long           cfg_addr;
        bool                    link_up;
+       u32                     version;
 };
 
 static inline u32 pcie_bar_low_val(u32 addr, u32 flags)
@@ -130,9 +137,7 @@ static bool xgene_pcie_hide_rc_bars(struct pci_bus *bus, int offset)
 static void __iomem *xgene_pcie_map_bus(struct pci_bus *bus, unsigned int devfn,
                              int offset)
 {
-       struct xgene_pcie_port *port = bus->sysdata;
-
-       if ((pci_is_root_bus(bus) && devfn != 0) || !port->link_up ||
+       if ((pci_is_root_bus(bus) && devfn != 0) ||
            xgene_pcie_hide_rc_bars(bus, offset))
                return NULL;
 
@@ -140,9 +145,37 @@ static void __iomem *xgene_pcie_map_bus(struct pci_bus *bus, unsigned int devfn,
        return xgene_pcie_get_cfg_base(bus) + offset;
 }
 
+static int xgene_pcie_config_read32(struct pci_bus *bus, unsigned int devfn,
+                                   int where, int size, u32 *val)
+{
+       struct xgene_pcie_port *port = bus->sysdata;
+
+       if (pci_generic_config_read32(bus, devfn, where & ~0x3, 4, val) !=
+           PCIBIOS_SUCCESSFUL)
+               return PCIBIOS_DEVICE_NOT_FOUND;
+
+       /*
+        * The v1 controller has a bug in its Configuration Request
+        * Retry Status (CRS) logic: when CRS is enabled and we read the
+        * Vendor and Device ID of a non-existent device, the controller
+        * fabricates return data of 0xFFFF0001 ("device exists but is not
+        * ready") instead of 0xFFFFFFFF ("device does not exist").  This
+        * causes the PCI core to retry the read until it times out.
+        * Avoid this by not claiming to support CRS.
+        */
+       if (pci_is_root_bus(bus) && (port->version == XGENE_PCIE_IP_VER_1) &&
+           ((where & ~0x3) == ROOT_CAP_AND_CTRL))
+               *val &= ~(PCI_EXP_RTCAP_CRSVIS << 16);
+
+       if (size <= 2)
+               *val = (*val >> (8 * (where & 3))) & ((1 << (size * 8)) - 1);
+
+       return PCIBIOS_SUCCESSFUL;
+}
+
 static struct pci_ops xgene_pcie_ops = {
        .map_bus = xgene_pcie_map_bus,
-       .read = pci_generic_config_read32,
+       .read = xgene_pcie_config_read32,
        .write = pci_generic_config_write32,
 };
 
@@ -468,6 +501,23 @@ static int xgene_pcie_setup(struct xgene_pcie_port *port,
        return 0;
 }
 
+static int xgene_pcie_msi_enable(struct pci_bus *bus)
+{
+       struct device_node *msi_node;
+
+       msi_node = of_parse_phandle(bus->dev.of_node,
+                                       "msi-parent", 0);
+       if (!msi_node)
+               return -ENODEV;
+
+       bus->msi = of_pci_find_msi_chip_by_node(msi_node);
+       if (!bus->msi)
+               return -ENODEV;
+
+       bus->msi->dev = &bus->dev;
+       return 0;
+}
+
 static int xgene_pcie_probe_bridge(struct platform_device *pdev)
 {
        struct device_node *dn = pdev->dev.of_node;
@@ -483,6 +533,10 @@ static int xgene_pcie_probe_bridge(struct platform_device *pdev)
        port->node = of_node_get(pdev->dev.of_node);
        port->dev = &pdev->dev;
 
+       port->version = XGENE_PCIE_IP_VER_UNKN;
+       if (of_device_is_compatible(port->node, "apm,xgene-pcie"))
+               port->version = XGENE_PCIE_IP_VER_1;
+
        ret = xgene_pcie_map_reg(port, pdev);
        if (ret)
                return ret;
@@ -504,6 +558,10 @@ static int xgene_pcie_probe_bridge(struct platform_device *pdev)
        if (!bus)
                return -ENOMEM;
 
+       if (IS_ENABLED(CONFIG_PCI_MSI))
+               if (xgene_pcie_msi_enable(bus))
+                       dev_info(port->dev, "failed to enable MSI\n");
+
        pci_scan_child_bus(bus);
        pci_assign_unassigned_bus_resources(bus);
        pci_bus_add_devices(bus);
index 2e9f84fdd9ceb3d39611c617573f4895ab36423e..69486be7181e18287a8a8bd421055b902233e3c4 100644 (file)
@@ -31,6 +31,7 @@
 #define PORT_LINK_MODE_1_LANES         (0x1 << 16)
 #define PORT_LINK_MODE_2_LANES         (0x3 << 16)
 #define PORT_LINK_MODE_4_LANES         (0x7 << 16)
+#define PORT_LINK_MODE_8_LANES         (0xf << 16)
 
 #define PCIE_LINK_WIDTH_SPEED_CONTROL  0x80C
 #define PORT_LOGIC_SPEED_CHANGE                (0x1 << 17)
@@ -38,6 +39,7 @@
 #define PORT_LOGIC_LINK_WIDTH_1_LANES  (0x1 << 8)
 #define PORT_LOGIC_LINK_WIDTH_2_LANES  (0x2 << 8)
 #define PORT_LOGIC_LINK_WIDTH_4_LANES  (0x4 << 8)
+#define PORT_LOGIC_LINK_WIDTH_8_LANES  (0x8 << 8)
 
 #define PCIE_MSI_ADDR_LO               0x820
 #define PCIE_MSI_ADDR_HI               0x824
@@ -150,6 +152,21 @@ static int dw_pcie_wr_own_conf(struct pcie_port *pp, int where, int size,
        return ret;
 }
 
+static void dw_pcie_prog_outbound_atu(struct pcie_port *pp, int index,
+               int type, u64 cpu_addr, u64 pci_addr, u32 size)
+{
+       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);
+}
+
 static struct irq_chip dw_msi_irq_chip = {
        .name = "PCI-MSI",
        .irq_enable = pci_msi_unmask_irq,
@@ -493,6 +510,11 @@ int dw_pcie_host_init(struct pcie_port *pp)
        if (pp->ops->host_init)
                pp->ops->host_init(pp);
 
+       if (!pp->ops->rd_other_conf)
+               dw_pcie_prog_outbound_atu(pp, PCIE_ATU_REGION_INDEX1,
+                                         PCIE_ATU_TYPE_MEM, pp->mem_mod_base,
+                                         pp->mem_bus_addr, pp->mem_size);
+
        dw_pcie_wr_own_conf(pp, PCI_BASE_ADDRESS_0, 4, 0);
 
        /* program correct class for RC */
@@ -515,115 +537,73 @@ int dw_pcie_host_init(struct pcie_port *pp)
        return 0;
 }
 
-static void dw_pcie_prog_viewport_cfg0(struct pcie_port *pp, u32 busdev)
-{
-       /* Program viewport 0 : OUTBOUND : CFG0 */
-       dw_pcie_writel_rc(pp, PCIE_ATU_REGION_OUTBOUND | PCIE_ATU_REGION_INDEX0,
-                         PCIE_ATU_VIEWPORT);
-       dw_pcie_writel_rc(pp, pp->cfg0_mod_base, PCIE_ATU_LOWER_BASE);
-       dw_pcie_writel_rc(pp, (pp->cfg0_mod_base >> 32), PCIE_ATU_UPPER_BASE);
-       dw_pcie_writel_rc(pp, pp->cfg0_mod_base + pp->cfg0_size - 1,
-                         PCIE_ATU_LIMIT);
-       dw_pcie_writel_rc(pp, busdev, PCIE_ATU_LOWER_TARGET);
-       dw_pcie_writel_rc(pp, 0, PCIE_ATU_UPPER_TARGET);
-       dw_pcie_writel_rc(pp, PCIE_ATU_TYPE_CFG0, PCIE_ATU_CR1);
-       dw_pcie_writel_rc(pp, PCIE_ATU_ENABLE, PCIE_ATU_CR2);
-}
-
-static void dw_pcie_prog_viewport_cfg1(struct pcie_port *pp, u32 busdev)
-{
-       /* Program viewport 1 : OUTBOUND : CFG1 */
-       dw_pcie_writel_rc(pp, PCIE_ATU_REGION_OUTBOUND | PCIE_ATU_REGION_INDEX1,
-                         PCIE_ATU_VIEWPORT);
-       dw_pcie_writel_rc(pp, PCIE_ATU_TYPE_CFG1, PCIE_ATU_CR1);
-       dw_pcie_writel_rc(pp, pp->cfg1_mod_base, PCIE_ATU_LOWER_BASE);
-       dw_pcie_writel_rc(pp, (pp->cfg1_mod_base >> 32), PCIE_ATU_UPPER_BASE);
-       dw_pcie_writel_rc(pp, pp->cfg1_mod_base + pp->cfg1_size - 1,
-                         PCIE_ATU_LIMIT);
-       dw_pcie_writel_rc(pp, busdev, PCIE_ATU_LOWER_TARGET);
-       dw_pcie_writel_rc(pp, 0, PCIE_ATU_UPPER_TARGET);
-       dw_pcie_writel_rc(pp, PCIE_ATU_ENABLE, PCIE_ATU_CR2);
-}
-
-static void dw_pcie_prog_viewport_mem_outbound(struct pcie_port *pp)
-{
-       /* Program viewport 0 : OUTBOUND : MEM */
-       dw_pcie_writel_rc(pp, PCIE_ATU_REGION_OUTBOUND | PCIE_ATU_REGION_INDEX0,
-                         PCIE_ATU_VIEWPORT);
-       dw_pcie_writel_rc(pp, PCIE_ATU_TYPE_MEM, PCIE_ATU_CR1);
-       dw_pcie_writel_rc(pp, pp->mem_mod_base, PCIE_ATU_LOWER_BASE);
-       dw_pcie_writel_rc(pp, (pp->mem_mod_base >> 32), PCIE_ATU_UPPER_BASE);
-       dw_pcie_writel_rc(pp, pp->mem_mod_base + pp->mem_size - 1,
-                         PCIE_ATU_LIMIT);
-       dw_pcie_writel_rc(pp, pp->mem_bus_addr, PCIE_ATU_LOWER_TARGET);
-       dw_pcie_writel_rc(pp, upper_32_bits(pp->mem_bus_addr),
-                         PCIE_ATU_UPPER_TARGET);
-       dw_pcie_writel_rc(pp, PCIE_ATU_ENABLE, PCIE_ATU_CR2);
-}
-
-static void dw_pcie_prog_viewport_io_outbound(struct pcie_port *pp)
-{
-       /* Program viewport 1 : OUTBOUND : IO */
-       dw_pcie_writel_rc(pp, PCIE_ATU_REGION_OUTBOUND | PCIE_ATU_REGION_INDEX1,
-                         PCIE_ATU_VIEWPORT);
-       dw_pcie_writel_rc(pp, PCIE_ATU_TYPE_IO, PCIE_ATU_CR1);
-       dw_pcie_writel_rc(pp, pp->io_mod_base, PCIE_ATU_LOWER_BASE);
-       dw_pcie_writel_rc(pp, (pp->io_mod_base >> 32), PCIE_ATU_UPPER_BASE);
-       dw_pcie_writel_rc(pp, pp->io_mod_base + pp->io_size - 1,
-                         PCIE_ATU_LIMIT);
-       dw_pcie_writel_rc(pp, pp->io_bus_addr, PCIE_ATU_LOWER_TARGET);
-       dw_pcie_writel_rc(pp, upper_32_bits(pp->io_bus_addr),
-                         PCIE_ATU_UPPER_TARGET);
-       dw_pcie_writel_rc(pp, PCIE_ATU_ENABLE, PCIE_ATU_CR2);
-}
-
 static int dw_pcie_rd_other_conf(struct pcie_port *pp, struct pci_bus *bus,
                u32 devfn, int where, int size, u32 *val)
 {
-       int ret = PCIBIOS_SUCCESSFUL;
-       u32 address, busdev;
+       int ret, type;
+       u32 address, busdev, cfg_size;
+       u64 cpu_addr;
+       void __iomem *va_cfg_base;
 
        busdev = PCIE_ATU_BUS(bus->number) | PCIE_ATU_DEV(PCI_SLOT(devfn)) |
                 PCIE_ATU_FUNC(PCI_FUNC(devfn));
        address = where & ~0x3;
 
        if (bus->parent->number == pp->root_bus_nr) {
-               dw_pcie_prog_viewport_cfg0(pp, busdev);
-               ret = dw_pcie_cfg_read(pp->va_cfg0_base + address, where, size,
-                               val);
-               dw_pcie_prog_viewport_mem_outbound(pp);
+               type = PCIE_ATU_TYPE_CFG0;
+               cpu_addr = pp->cfg0_mod_base;
+               cfg_size = pp->cfg0_size;
+               va_cfg_base = pp->va_cfg0_base;
        } else {
-               dw_pcie_prog_viewport_cfg1(pp, busdev);
-               ret = dw_pcie_cfg_read(pp->va_cfg1_base + address, where, size,
-                               val);
-               dw_pcie_prog_viewport_io_outbound(pp);
+               type = PCIE_ATU_TYPE_CFG1;
+               cpu_addr = pp->cfg1_mod_base;
+               cfg_size = pp->cfg1_size;
+               va_cfg_base = pp->va_cfg1_base;
        }
 
+       dw_pcie_prog_outbound_atu(pp, PCIE_ATU_REGION_INDEX0,
+                                 type, cpu_addr,
+                                 busdev, cfg_size);
+       ret = dw_pcie_cfg_read(va_cfg_base + address, where, size, val);
+       dw_pcie_prog_outbound_atu(pp, PCIE_ATU_REGION_INDEX0,
+                                 PCIE_ATU_TYPE_IO, pp->io_mod_base,
+                                 pp->io_bus_addr, pp->io_size);
+
        return ret;
 }
 
 static int dw_pcie_wr_other_conf(struct pcie_port *pp, struct pci_bus *bus,
                u32 devfn, int where, int size, u32 val)
 {
-       int ret = PCIBIOS_SUCCESSFUL;
-       u32 address, busdev;
+       int ret, type;
+       u32 address, busdev, cfg_size;
+       u64 cpu_addr;
+       void __iomem *va_cfg_base;
 
        busdev = PCIE_ATU_BUS(bus->number) | PCIE_ATU_DEV(PCI_SLOT(devfn)) |
                 PCIE_ATU_FUNC(PCI_FUNC(devfn));
        address = where & ~0x3;
 
        if (bus->parent->number == pp->root_bus_nr) {
-               dw_pcie_prog_viewport_cfg0(pp, busdev);
-               ret = dw_pcie_cfg_write(pp->va_cfg0_base + address, where, size,
-                               val);
-               dw_pcie_prog_viewport_mem_outbound(pp);
+               type = PCIE_ATU_TYPE_CFG0;
+               cpu_addr = pp->cfg0_mod_base;
+               cfg_size = pp->cfg0_size;
+               va_cfg_base = pp->va_cfg0_base;
        } else {
-               dw_pcie_prog_viewport_cfg1(pp, busdev);
-               ret = dw_pcie_cfg_write(pp->va_cfg1_base + address, where, size,
-                               val);
-               dw_pcie_prog_viewport_io_outbound(pp);
+               type = PCIE_ATU_TYPE_CFG1;
+               cpu_addr = pp->cfg1_mod_base;
+               cfg_size = pp->cfg1_size;
+               va_cfg_base = pp->va_cfg1_base;
        }
 
+       dw_pcie_prog_outbound_atu(pp, PCIE_ATU_REGION_INDEX0,
+                                 type, cpu_addr,
+                                 busdev, cfg_size);
+       ret = dw_pcie_cfg_write(va_cfg_base + address, where, size, val);
+       dw_pcie_prog_outbound_atu(pp, PCIE_ATU_REGION_INDEX0,
+                                 PCIE_ATU_TYPE_IO, pp->io_mod_base,
+                                 pp->io_bus_addr, pp->io_size);
+
        return ret;
 }
 
@@ -728,13 +708,11 @@ static struct pci_bus *dw_pcie_scan_bus(int nr, struct pci_sys_data *sys)
        struct pcie_port *pp = sys_to_pcie(sys);
 
        pp->root_bus_nr = sys->busnr;
-       bus = pci_create_root_bus(pp->dev, sys->busnr,
+       bus = pci_scan_root_bus(pp->dev, sys->busnr,
                                  &dw_pcie_ops, sys, &sys->resources);
        if (!bus)
                return NULL;
 
-       pci_scan_child_bus(bus);
-
        if (bus && pp->ops->scan_bus)
                pp->ops->scan_bus(pp);
 
@@ -778,6 +756,9 @@ void dw_pcie_setup_rc(struct pcie_port *pp)
        case 4:
                val |= PORT_LINK_MODE_4_LANES;
                break;
+       case 8:
+               val |= PORT_LINK_MODE_8_LANES;
+               break;
        }
        dw_pcie_writel_rc(pp, val, PCIE_PORT_LINK_CONTROL);
 
@@ -794,6 +775,9 @@ void dw_pcie_setup_rc(struct pcie_port *pp)
        case 4:
                val |= PORT_LOGIC_LINK_WIDTH_4_LANES;
                break;
+       case 8:
+               val |= PORT_LOGIC_LINK_WIDTH_8_LANES;
+               break;
        }
        dw_pcie_writel_rc(pp, val, PCIE_LINK_WIDTH_SPEED_CONTROL);
 
diff --git a/drivers/pci/host/pcie-iproc-bcma.c b/drivers/pci/host/pcie-iproc-bcma.c
new file mode 100644 (file)
index 0000000..96a7d99
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2015 Broadcom Corporation
+ * Copyright (C) 2015 Hauke Mehrtens <hauke@hauke-m.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/phy/phy.h>
+#include <linux/bcma/bcma.h>
+#include <linux/ioport.h>
+
+#include "pcie-iproc.h"
+
+
+/* NS: CLASS field is R/O, and set to wrong 0x200 value */
+static void bcma_pcie2_fixup_class(struct pci_dev *dev)
+{
+       dev->class = PCI_CLASS_BRIDGE_PCI << 8;
+}
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_BROADCOM, 0x8011, bcma_pcie2_fixup_class);
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_BROADCOM, 0x8012, bcma_pcie2_fixup_class);
+
+static int iproc_pcie_bcma_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
+{
+       struct pci_sys_data *sys = dev->sysdata;
+       struct iproc_pcie *pcie = sys->private_data;
+       struct bcma_device *bdev = container_of(pcie->dev, struct bcma_device, dev);
+
+       return bcma_core_irq(bdev, 5);
+}
+
+static int iproc_pcie_bcma_probe(struct bcma_device *bdev)
+{
+       struct iproc_pcie *pcie;
+       LIST_HEAD(res);
+       struct resource res_mem;
+       int ret;
+
+       pcie = devm_kzalloc(&bdev->dev, sizeof(*pcie), GFP_KERNEL);
+       if (!pcie)
+               return -ENOMEM;
+
+       pcie->dev = &bdev->dev;
+       bcma_set_drvdata(bdev, pcie);
+
+       pcie->base = bdev->io_addr;
+
+       res_mem.start = bdev->addr_s[0];
+       res_mem.end = bdev->addr_s[0] + SZ_128M - 1;
+       res_mem.name = "PCIe MEM space";
+       res_mem.flags = IORESOURCE_MEM;
+       pci_add_resource(&res, &res_mem);
+
+       pcie->map_irq = iproc_pcie_bcma_map_irq;
+
+       ret = iproc_pcie_setup(pcie, &res);
+       if (ret)
+               dev_err(pcie->dev, "PCIe controller setup failed\n");
+
+       pci_free_resource_list(&res);
+
+       return ret;
+}
+
+static void iproc_pcie_bcma_remove(struct bcma_device *bdev)
+{
+       struct iproc_pcie *pcie = bcma_get_drvdata(bdev);
+
+       iproc_pcie_remove(pcie);
+}
+
+static const struct bcma_device_id iproc_pcie_bcma_table[] = {
+       BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_NS_PCIEG2, BCMA_ANY_REV, BCMA_ANY_CLASS),
+       {},
+};
+MODULE_DEVICE_TABLE(bcma, iproc_pcie_bcma_table);
+
+static struct bcma_driver iproc_pcie_bcma_driver = {
+       .name           = KBUILD_MODNAME,
+       .id_table       = iproc_pcie_bcma_table,
+       .probe          = iproc_pcie_bcma_probe,
+       .remove         = iproc_pcie_bcma_remove,
+};
+
+static int __init iproc_pcie_bcma_init(void)
+{
+       return bcma_driver_register(&iproc_pcie_bcma_driver);
+}
+module_init(iproc_pcie_bcma_init);
+
+static void __exit iproc_pcie_bcma_exit(void)
+{
+       bcma_driver_unregister(&iproc_pcie_bcma_driver);
+}
+module_exit(iproc_pcie_bcma_exit);
+
+MODULE_AUTHOR("Hauke Mehrtens");
+MODULE_DESCRIPTION("Broadcom iProc PCIe BCMA driver");
+MODULE_LICENSE("GPL v2");
index afad6c21fcfa17832fc17ed8c44d8e100d48172d..9aedc8eb2c6eaa3ffcb29dc562171f9a75161e52 100644 (file)
@@ -69,15 +69,15 @@ static int iproc_pcie_pltfm_probe(struct platform_device *pdev)
                return ret;
        }
 
-       pcie->resources = &res;
+       pcie->map_irq = of_irq_parse_and_map_pci;
 
-       ret = iproc_pcie_setup(pcie);
-       if (ret) {
+       ret = iproc_pcie_setup(pcie, &res);
+       if (ret)
                dev_err(pcie->dev, "PCIe controller setup failed\n");
-               return ret;
-       }
 
-       return 0;
+       pci_free_resource_list(&res);
+
+       return ret;
 }
 
 static int iproc_pcie_pltfm_remove(struct platform_device *pdev)
index 329e1b54528b3806c1c64477cef27ac1e4c16412..d77481ea553e08de04527f885df842e3dc2f8d84 100644 (file)
@@ -183,7 +183,7 @@ static void iproc_pcie_enable(struct iproc_pcie *pcie)
        writel(SYS_RC_INTX_MASK, pcie->base + SYS_RC_INTX_EN);
 }
 
-int iproc_pcie_setup(struct iproc_pcie *pcie)
+int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res)
 {
        int ret;
        struct pci_bus *bus;
@@ -211,7 +211,7 @@ int iproc_pcie_setup(struct iproc_pcie *pcie)
        pcie->sysdata.private_data = pcie;
 
        bus = pci_create_root_bus(pcie->dev, 0, &iproc_pcie_ops,
-                                 &pcie->sysdata, pcie->resources);
+                                 &pcie->sysdata, res);
        if (!bus) {
                dev_err(pcie->dev, "unable to create PCI root bus\n");
                ret = -ENOMEM;
@@ -229,7 +229,7 @@ int iproc_pcie_setup(struct iproc_pcie *pcie)
 
        pci_scan_child_bus(bus);
        pci_assign_unassigned_bus_resources(bus);
-       pci_fixup_irqs(pci_common_swizzle, of_irq_parse_and_map_pci);
+       pci_fixup_irqs(pci_common_swizzle, pcie->map_irq);
        pci_bus_add_devices(bus);
 
        return 0;
index e28075ed185691005febba1b38e212a02c3f350f..ba0a108309ccf584446947120be074fed83883ba 100644 (file)
 struct iproc_pcie {
        struct device *dev;
        void __iomem *base;
-       struct list_head *resources;
        struct pci_sys_data sysdata;
        struct pci_bus *root_bus;
        struct phy *phy;
        int irqs[IPROC_PCIE_MAX_NUM_IRQS];
+       int (*map_irq)(const struct pci_dev *, u8, u8);
 };
 
-int iproc_pcie_setup(struct iproc_pcie *pcie);
+int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res);
 int iproc_pcie_remove(struct iproc_pcie *pcie);
 
 #endif /* _PCIE_IPROC_H */
index 020d788907191fd73ac1b7058b66b9a76f6b7351..dfec4281bd5012fa33901cfee3314b1deb9f5fda 100644 (file)
@@ -146,10 +146,10 @@ struct pcie_app_reg {
 static int spear13xx_pcie_establish_link(struct pcie_port *pp)
 {
        u32 val;
-       int count = 0;
        struct spear13xx_pcie *spear13xx_pcie = to_spear13xx_pcie(pp);
        struct pcie_app_reg *app_reg = spear13xx_pcie->app_base;
        u32 exp_cap_off = EXP_CAP_ID_OFFSET;
+       unsigned int retries;
 
        if (dw_pcie_link_up(pp)) {
                dev_err(pp->dev, "link already up\n");
@@ -201,17 +201,16 @@ static int spear13xx_pcie_establish_link(struct pcie_port *pp)
                        &app_reg->app_ctrl_0);
 
        /* check if the link is up or not */
-       while (!dw_pcie_link_up(pp)) {
-               mdelay(100);
-               count++;
-               if (count == 10) {
-                       dev_err(pp->dev, "link Fail\n");
-                       return -EINVAL;
+       for (retries = 0; retries < 10; retries++) {
+               if (dw_pcie_link_up(pp)) {
+                       dev_info(pp->dev, "link up\n");
+                       return 0;
                }
+               mdelay(100);
        }
-       dev_info(pp->dev, "link up\n");
 
-       return 0;
+       dev_err(pp->dev, "link Fail\n");
+       return -EINVAL;
 }
 
 static irqreturn_t spear13xx_pcie_irq_handler(int irq, void *arg)
index 4a9aa08b08f13600ecad592eb7e452c8ed4132b9..b616e7588ff47da6b9cda4959cdcc891dbd0d5bf 100644 (file)
@@ -61,9 +61,6 @@ pciehp-objs           :=      pciehp_core.o   \
                                pciehp_ctrl.o   \
                                pciehp_pci.o    \
                                pciehp_hpc.o
-ifdef CONFIG_ACPI
-pciehp-objs            +=      pciehp_acpi.o
-endif
 
 shpchp-objs            :=      shpchp_core.o   \
                                shpchp_ctrl.o   \
index bcb90e4888dd82d628337018c30d0d1712aad231..ff538568a61774af57290eac6b27cd6ea9457908 100644 (file)
@@ -632,15 +632,14 @@ static void trim_stale_devices(struct pci_dev *dev)
 {
        struct acpi_device *adev = ACPI_COMPANION(&dev->dev);
        struct pci_bus *bus = dev->subordinate;
-       bool alive = false;
+       bool alive = dev->ignore_hotplug;
 
        if (adev) {
                acpi_status status;
                unsigned long long sta;
 
                status = acpi_evaluate_integer(adev->handle, "_STA", NULL, &sta);
-               alive = (ACPI_SUCCESS(status) && device_status_valid(sta))
-                       || dev->ignore_hotplug;
+               alive = alive || (ACPI_SUCCESS(status) && device_status_valid(sta));
        }
        if (!alive)
                alive = pci_device_is_present(dev);
index b11521953485bb99892b85dd4c2c3e93378c2d56..57cd1327346f816ad2e1aed45fdc484ea5d8fba9 100644 (file)
@@ -132,11 +132,7 @@ struct controller {
 
 int pciehp_sysfs_enable_slot(struct slot *slot);
 int pciehp_sysfs_disable_slot(struct slot *slot);
-u8 pciehp_handle_attention_button(struct slot *p_slot);
-u8 pciehp_handle_switch_change(struct slot *p_slot);
-u8 pciehp_handle_presence_change(struct slot *p_slot);
-u8 pciehp_handle_power_fault(struct slot *p_slot);
-void pciehp_handle_linkstate_change(struct slot *p_slot);
+void pciehp_queue_interrupt_event(struct slot *slot, u32 event_type);
 int pciehp_configure_device(struct slot *p_slot);
 int pciehp_unconfigure_device(struct slot *p_slot);
 void pciehp_queue_pushbutton_work(struct work_struct *work);
@@ -167,21 +163,4 @@ static inline const char *slot_name(struct slot *slot)
        return hotplug_slot_name(slot->hotplug_slot);
 }
 
-#ifdef CONFIG_ACPI
-#include <linux/pci-acpi.h>
-
-void __init pciehp_acpi_slot_detection_init(void);
-int pciehp_acpi_slot_detection_check(struct pci_dev *dev);
-
-static inline void pciehp_firmware_init(void)
-{
-       pciehp_acpi_slot_detection_init();
-}
-#else
-#define pciehp_firmware_init()                         do {} while (0)
-static inline int pciehp_acpi_slot_detection_check(struct pci_dev *dev)
-{
-       return 0;
-}
-#endif                         /* CONFIG_ACPI */
 #endif                         /* _PCIEHP_H */
diff --git a/drivers/pci/hotplug/pciehp_acpi.c b/drivers/pci/hotplug/pciehp_acpi.c
deleted file mode 100644 (file)
index 93cc926..0000000
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * ACPI related functions for PCI Express Hot Plug driver.
- *
- * Copyright (C) 2008 Kenji Kaneshige
- * Copyright (C) 2008 Fujitsu Limited.
- *
- * All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or (at
- * your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
- * NON INFRINGEMENT.  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/acpi.h>
-#include <linux/pci.h>
-#include <linux/pci_hotplug.h>
-#include <linux/slab.h>
-#include <linux/module.h>
-#include "pciehp.h"
-
-#define PCIEHP_DETECT_PCIE     (0)
-#define PCIEHP_DETECT_ACPI     (1)
-#define PCIEHP_DETECT_AUTO     (2)
-#define PCIEHP_DETECT_DEFAULT  PCIEHP_DETECT_AUTO
-
-struct dummy_slot {
-       u32 number;
-       struct list_head list;
-};
-
-static int slot_detection_mode;
-static char *pciehp_detect_mode;
-module_param(pciehp_detect_mode, charp, 0444);
-MODULE_PARM_DESC(pciehp_detect_mode,
-        "Slot detection mode: pcie, acpi, auto\n"
-        "  pcie          - Use PCIe based slot detection\n"
-        "  acpi          - Use ACPI for slot detection\n"
-        "  auto(default) - Auto select mode. Use acpi option if duplicate\n"
-        "                  slot ids are found. Otherwise, use pcie option\n");
-
-int pciehp_acpi_slot_detection_check(struct pci_dev *dev)
-{
-       if (slot_detection_mode != PCIEHP_DETECT_ACPI)
-               return 0;
-       if (acpi_pci_detect_ejectable(ACPI_HANDLE(&dev->dev)))
-               return 0;
-       return -ENODEV;
-}
-
-static int __init parse_detect_mode(void)
-{
-       if (!pciehp_detect_mode)
-               return PCIEHP_DETECT_DEFAULT;
-       if (!strcmp(pciehp_detect_mode, "pcie"))
-               return PCIEHP_DETECT_PCIE;
-       if (!strcmp(pciehp_detect_mode, "acpi"))
-               return PCIEHP_DETECT_ACPI;
-       if (!strcmp(pciehp_detect_mode, "auto"))
-               return PCIEHP_DETECT_AUTO;
-       warn("bad specifier '%s' for pciehp_detect_mode. Use default\n",
-            pciehp_detect_mode);
-       return PCIEHP_DETECT_DEFAULT;
-}
-
-static int __initdata dup_slot_id;
-static int __initdata acpi_slot_detected;
-static struct list_head __initdata dummy_slots = LIST_HEAD_INIT(dummy_slots);
-
-/* Dummy driver for duplicate name detection */
-static int __init dummy_probe(struct pcie_device *dev)
-{
-       u32 slot_cap;
-       acpi_handle handle;
-       struct dummy_slot *slot, *tmp;
-       struct pci_dev *pdev = dev->port;
-
-       pcie_capability_read_dword(pdev, PCI_EXP_SLTCAP, &slot_cap);
-       slot = kzalloc(sizeof(*slot), GFP_KERNEL);
-       if (!slot)
-               return -ENOMEM;
-       slot->number = (slot_cap & PCI_EXP_SLTCAP_PSN) >> 19;
-       list_for_each_entry(tmp, &dummy_slots, list) {
-               if (tmp->number == slot->number)
-                       dup_slot_id++;
-       }
-       list_add_tail(&slot->list, &dummy_slots);
-       handle = ACPI_HANDLE(&pdev->dev);
-       if (!acpi_slot_detected && acpi_pci_detect_ejectable(handle))
-               acpi_slot_detected = 1;
-       return -ENODEV;         /* dummy driver always returns error */
-}
-
-static struct pcie_port_service_driver __initdata dummy_driver = {
-       .name           = "pciehp_dummy",
-       .port_type      = PCIE_ANY_PORT,
-       .service        = PCIE_PORT_SERVICE_HP,
-       .probe          = dummy_probe,
-};
-
-static int __init select_detection_mode(void)
-{
-       struct dummy_slot *slot, *tmp;
-
-       if (pcie_port_service_register(&dummy_driver))
-               return PCIEHP_DETECT_ACPI;
-       pcie_port_service_unregister(&dummy_driver);
-       list_for_each_entry_safe(slot, tmp, &dummy_slots, list) {
-               list_del(&slot->list);
-               kfree(slot);
-       }
-       if (acpi_slot_detected && dup_slot_id)
-               return PCIEHP_DETECT_ACPI;
-       return PCIEHP_DETECT_PCIE;
-}
-
-void __init pciehp_acpi_slot_detection_init(void)
-{
-       slot_detection_mode = parse_detect_mode();
-       if (slot_detection_mode != PCIEHP_DETECT_AUTO)
-               goto out;
-       slot_detection_mode = select_detection_mode();
-out:
-       if (slot_detection_mode == PCIEHP_DETECT_ACPI)
-               info("Using ACPI for slot detection.\n");
-}
index 07aa722bb12cd61a6a3a8767b2efe1dd826e6952..612b21a14df5931254d6b76059b45090a4a2fe12 100644 (file)
@@ -77,11 +77,6 @@ static int reset_slot                (struct hotplug_slot *slot, int probe);
  */
 static void release_slot(struct hotplug_slot *hotplug_slot)
 {
-       struct slot *slot = hotplug_slot->private;
-
-       ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
-                __func__, hotplug_slot_name(hotplug_slot));
-
        kfree(hotplug_slot->ops);
        kfree(hotplug_slot->info);
        kfree(hotplug_slot);
@@ -129,14 +124,10 @@ static int init_slot(struct controller *ctrl)
        slot->hotplug_slot = hotplug;
        snprintf(name, SLOT_NAME_SIZE, "%u", PSN(ctrl));
 
-       ctrl_dbg(ctrl, "Registering domain:bus:dev=%04x:%02x:00 sun=%x\n",
-                pci_domain_nr(ctrl->pcie->port->subordinate),
-                ctrl->pcie->port->subordinate->number, PSN(ctrl));
        retval = pci_hp_register(hotplug,
                                 ctrl->pcie->port->subordinate, 0, name);
        if (retval)
-               ctrl_err(ctrl,
-                        "pci_hp_register failed with error %d\n", retval);
+               ctrl_err(ctrl, "pci_hp_register failed: error %d\n", retval);
 out:
        if (retval) {
                kfree(ops);
@@ -158,9 +149,6 @@ static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 status)
 {
        struct slot *slot = hotplug_slot->private;
 
-       ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
-                 __func__, slot_name(slot));
-
        pciehp_set_attention_status(slot, status);
        return 0;
 }
@@ -170,9 +158,6 @@ static int enable_slot(struct hotplug_slot *hotplug_slot)
 {
        struct slot *slot = hotplug_slot->private;
 
-       ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
-                __func__, slot_name(slot));
-
        return pciehp_sysfs_enable_slot(slot);
 }
 
@@ -181,9 +166,6 @@ static int disable_slot(struct hotplug_slot *hotplug_slot)
 {
        struct slot *slot = hotplug_slot->private;
 
-       ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
-                 __func__, slot_name(slot));
-
        return pciehp_sysfs_disable_slot(slot);
 }
 
@@ -191,9 +173,6 @@ static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value)
 {
        struct slot *slot = hotplug_slot->private;
 
-       ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
-                 __func__, slot_name(slot));
-
        pciehp_get_power_status(slot, value);
        return 0;
 }
@@ -202,9 +181,6 @@ static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value)
 {
        struct slot *slot = hotplug_slot->private;
 
-       ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
-                 __func__, slot_name(slot));
-
        pciehp_get_attention_status(slot, value);
        return 0;
 }
@@ -213,9 +189,6 @@ static int get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value)
 {
        struct slot *slot = hotplug_slot->private;
 
-       ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
-                __func__, slot_name(slot));
-
        pciehp_get_latch_status(slot, value);
        return 0;
 }
@@ -224,9 +197,6 @@ static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
 {
        struct slot *slot = hotplug_slot->private;
 
-       ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
-                __func__, slot_name(slot));
-
        pciehp_get_adapter_status(slot, value);
        return 0;
 }
@@ -235,9 +205,6 @@ static int reset_slot(struct hotplug_slot *hotplug_slot, int probe)
 {
        struct slot *slot = hotplug_slot->private;
 
-       ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
-                __func__, slot_name(slot));
-
        return pciehp_reset_slot(slot, probe);
 }
 
@@ -248,24 +215,21 @@ static int pciehp_probe(struct pcie_device *dev)
        struct slot *slot;
        u8 occupied, poweron;
 
-       if (pciehp_force)
-               dev_info(&dev->device,
-                        "Bypassing BIOS check for pciehp use on %s\n",
-                        pci_name(dev->port));
-       else if (pciehp_acpi_slot_detection_check(dev->port))
-               goto err_out_none;
+       /* If this is not a "hotplug" service, we have no business here. */
+       if (dev->service != PCIE_PORT_SERVICE_HP)
+               return -ENODEV;
 
        if (!dev->port->subordinate) {
                /* Can happen if we run out of bus numbers during probe */
                dev_err(&dev->device,
                        "Hotplug bridge without secondary bus, ignoring\n");
-               goto err_out_none;
+               return -ENODEV;
        }
 
        ctrl = pcie_init(dev);
        if (!ctrl) {
                dev_err(&dev->device, "Controller initialization failed\n");
-               goto err_out_none;
+               return -ENODEV;
        }
        set_service_data(dev, ctrl);
 
@@ -275,14 +239,14 @@ static int pciehp_probe(struct pcie_device *dev)
                if (rc == -EBUSY)
                        ctrl_warn(ctrl, "Slot already registered by another hotplug driver\n");
                else
-                       ctrl_err(ctrl, "Slot initialization failed\n");
+                       ctrl_err(ctrl, "Slot initialization failed (%d)\n", rc);
                goto err_out_release_ctlr;
        }
 
        /* Enable events after we have setup the data structures */
        rc = pcie_init_notification(ctrl);
        if (rc) {
-               ctrl_err(ctrl, "Notification initialization failed\n");
+               ctrl_err(ctrl, "Notification initialization failed (%d)\n", rc);
                goto err_out_free_ctrl_slot;
        }
 
@@ -305,7 +269,6 @@ err_out_free_ctrl_slot:
        cleanup_slot(ctrl);
 err_out_release_ctlr:
        pciehp_release_ctrl(ctrl);
-err_out_none:
        return -ENODEV;
 }
 
@@ -366,7 +329,6 @@ static int __init pcied_init(void)
 {
        int retval = 0;
 
-       pciehp_firmware_init();
        retval = pcie_port_service_register(&hpdriver_portdrv);
        dbg("pcie_port_service_register = %d\n", retval);
        info(DRIVER_DESC " version: " DRIVER_VERSION "\n");
index f052e951b23e808a551456bed69587ee8ccee513..f3796124ad7cc9b5076ef2990d0a87b08bf5c94c 100644 (file)
 
 static void interrupt_event_handler(struct work_struct *work);
 
-static int queue_interrupt_event(struct slot *p_slot, u32 event_type)
+void pciehp_queue_interrupt_event(struct slot *p_slot, u32 event_type)
 {
        struct event_info *info;
 
        info = kmalloc(sizeof(*info), GFP_ATOMIC);
-       if (!info)
-               return -ENOMEM;
+       if (!info) {
+               ctrl_err(p_slot->ctrl, "dropped event %d (ENOMEM)\n", event_type);
+               return;
+       }
 
+       INIT_WORK(&info->work, interrupt_event_handler);
        info->event_type = event_type;
        info->p_slot = p_slot;
-       INIT_WORK(&info->work, interrupt_event_handler);
-
        queue_work(p_slot->wq, &info->work);
-
-       return 0;
-}
-
-u8 pciehp_handle_attention_button(struct slot *p_slot)
-{
-       u32 event_type;
-       struct controller *ctrl = p_slot->ctrl;
-
-       /* Attention Button Change */
-       ctrl_dbg(ctrl, "Attention button interrupt received\n");
-
-       /*
-        *  Button pressed - See if need to TAKE ACTION!!!
-        */
-       ctrl_info(ctrl, "Button pressed on Slot(%s)\n", slot_name(p_slot));
-       event_type = INT_BUTTON_PRESS;
-
-       queue_interrupt_event(p_slot, event_type);
-
-       return 0;
-}
-
-u8 pciehp_handle_switch_change(struct slot *p_slot)
-{
-       u8 getstatus;
-       u32 event_type;
-       struct controller *ctrl = p_slot->ctrl;
-
-       /* Switch Change */
-       ctrl_dbg(ctrl, "Switch interrupt received\n");
-
-       pciehp_get_latch_status(p_slot, &getstatus);
-       if (getstatus) {
-               /*
-                * Switch opened
-                */
-               ctrl_info(ctrl, "Latch open on Slot(%s)\n", slot_name(p_slot));
-               event_type = INT_SWITCH_OPEN;
-       } else {
-               /*
-                *  Switch closed
-                */
-               ctrl_info(ctrl, "Latch close on Slot(%s)\n", slot_name(p_slot));
-               event_type = INT_SWITCH_CLOSE;
-       }
-
-       queue_interrupt_event(p_slot, event_type);
-
-       return 1;
-}
-
-u8 pciehp_handle_presence_change(struct slot *p_slot)
-{
-       u32 event_type;
-       u8 presence_save;
-       struct controller *ctrl = p_slot->ctrl;
-
-       /* Presence Change */
-       ctrl_dbg(ctrl, "Presence/Notify input change\n");
-
-       /* Switch is open, assume a presence change
-        * Save the presence state
-        */
-       pciehp_get_adapter_status(p_slot, &presence_save);
-       if (presence_save) {
-               /*
-                * Card Present
-                */
-               ctrl_info(ctrl, "Card present on Slot(%s)\n", slot_name(p_slot));
-               event_type = INT_PRESENCE_ON;
-       } else {
-               /*
-                * Not Present
-                */
-               ctrl_info(ctrl, "Card not present on Slot(%s)\n",
-                         slot_name(p_slot));
-               event_type = INT_PRESENCE_OFF;
-       }
-
-       queue_interrupt_event(p_slot, event_type);
-
-       return 1;
-}
-
-u8 pciehp_handle_power_fault(struct slot *p_slot)
-{
-       u32 event_type;
-       struct controller *ctrl = p_slot->ctrl;
-
-       /* power fault */
-       ctrl_dbg(ctrl, "Power fault interrupt received\n");
-       ctrl_err(ctrl, "Power fault on slot %s\n", slot_name(p_slot));
-       event_type = INT_POWER_FAULT;
-       ctrl_info(ctrl, "Power fault bit %x set\n", 0);
-       queue_interrupt_event(p_slot, event_type);
-
-       return 1;
-}
-
-void pciehp_handle_linkstate_change(struct slot *p_slot)
-{
-       u32 event_type;
-       struct controller *ctrl = p_slot->ctrl;
-
-       /* Link Status Change */
-       ctrl_dbg(ctrl, "Data Link Layer State change\n");
-
-       if (pciehp_check_link_active(ctrl)) {
-               ctrl_info(ctrl, "slot(%s): Link Up event\n",
-                         slot_name(p_slot));
-               event_type = INT_LINK_UP;
-       } else {
-               ctrl_info(ctrl, "slot(%s): Link Down event\n",
-                         slot_name(p_slot));
-               event_type = INT_LINK_DOWN;
-       }
-
-       queue_interrupt_event(p_slot, event_type);
 }
 
 /* The following routines constitute the bulk of the
@@ -298,10 +180,6 @@ static void pciehp_power_thread(struct work_struct *work)
 
        switch (info->req) {
        case DISABLE_REQ:
-               ctrl_dbg(p_slot->ctrl,
-                        "Disabling domain:bus:device=%04x:%02x:00\n",
-                        pci_domain_nr(p_slot->ctrl->pcie->port->subordinate),
-                        p_slot->ctrl->pcie->port->subordinate->number);
                mutex_lock(&p_slot->hotplug_lock);
                pciehp_disable_slot(p_slot);
                mutex_unlock(&p_slot->hotplug_lock);
@@ -310,10 +188,6 @@ static void pciehp_power_thread(struct work_struct *work)
                mutex_unlock(&p_slot->lock);
                break;
        case ENABLE_REQ:
-               ctrl_dbg(p_slot->ctrl,
-                        "Enabling domain:bus:device=%04x:%02x:00\n",
-                        pci_domain_nr(p_slot->ctrl->pcie->port->subordinate),
-                        p_slot->ctrl->pcie->port->subordinate->number);
                mutex_lock(&p_slot->hotplug_lock);
                ret = pciehp_enable_slot(p_slot);
                mutex_unlock(&p_slot->hotplug_lock);
@@ -416,7 +290,7 @@ static void handle_button_press_event(struct slot *p_slot)
                ctrl_info(ctrl, "Button ignore on Slot(%s)\n", slot_name(p_slot));
                break;
        default:
-               ctrl_warn(ctrl, "Not a valid state\n");
+               ctrl_warn(ctrl, "ignoring invalid state %#x\n", p_slot->state);
                break;
        }
 }
@@ -507,8 +381,8 @@ static void handle_link_event(struct slot *p_slot, u32 event)
                }
                break;
        default:
-               ctrl_err(ctrl, "Not a valid state on slot(%s)\n",
-                        slot_name(p_slot));
+               ctrl_err(ctrl, "ignoring invalid state %#x on slot(%s)\n",
+                        p_slot->state, slot_name(p_slot));
                kfree(info);
                break;
        }
@@ -532,7 +406,6 @@ static void interrupt_event_handler(struct work_struct *work)
                pciehp_green_led_off(p_slot);
                break;
        case INT_PRESENCE_ON:
-               ctrl_dbg(ctrl, "Surprise Insertion\n");
                handle_surprise_event(p_slot);
                break;
        case INT_PRESENCE_OFF:
@@ -540,7 +413,6 @@ static void interrupt_event_handler(struct work_struct *work)
                 * Regardless of surprise capability, we need to
                 * definitely remove a card that has been pulled out!
                 */
-               ctrl_dbg(ctrl, "Surprise Removal\n");
                handle_surprise_event(p_slot);
                break;
        case INT_LINK_UP:
@@ -647,8 +519,8 @@ int pciehp_sysfs_enable_slot(struct slot *p_slot)
                          slot_name(p_slot));
                break;
        default:
-               ctrl_err(ctrl, "Not a valid state on slot %s\n",
-                        slot_name(p_slot));
+               ctrl_err(ctrl, "invalid state %#x on slot %s\n",
+                        p_slot->state, slot_name(p_slot));
                break;
        }
        mutex_unlock(&p_slot->lock);
@@ -682,8 +554,8 @@ int pciehp_sysfs_disable_slot(struct slot *p_slot)
                          slot_name(p_slot));
                break;
        default:
-               ctrl_err(ctrl, "Not a valid state on slot %s\n",
-                        slot_name(p_slot));
+               ctrl_err(ctrl, "invalid state %#x on slot %s\n",
+                        p_slot->state, slot_name(p_slot));
                break;
        }
        mutex_unlock(&p_slot->lock);
index 0ebf754fc1775d7afffdc6fb9bea37828408d24a..2913f7e68a10bdee3eefe169576dd7347965e31c 100644 (file)
@@ -176,20 +176,17 @@ static void pcie_wait_cmd(struct controller *ctrl)
                          jiffies_to_msecs(jiffies - ctrl->cmd_started));
 }
 
-/**
- * pcie_write_cmd - Issue controller command
- * @ctrl: controller to which the command is issued
- * @cmd:  command value written to slot control register
- * @mask: bitmask of slot control register to be modified
- */
-static void pcie_write_cmd(struct controller *ctrl, u16 cmd, u16 mask)
+static void pcie_do_write_cmd(struct controller *ctrl, u16 cmd,
+                             u16 mask, bool wait)
 {
        struct pci_dev *pdev = ctrl_dev(ctrl);
        u16 slot_ctrl;
 
        mutex_lock(&ctrl->ctrl_lock);
 
-       /* Wait for any previous command that might still be in progress */
+       /*
+        * Always wait for any previous command that might still be in progress
+        */
        pcie_wait_cmd(ctrl);
 
        pcie_capability_read_word(pdev, PCI_EXP_SLTCTL, &slot_ctrl);
@@ -201,9 +198,33 @@ static void pcie_write_cmd(struct controller *ctrl, u16 cmd, u16 mask)
        ctrl->cmd_started = jiffies;
        ctrl->slot_ctrl = slot_ctrl;
 
+       /*
+        * Optionally wait for the hardware to be ready for a new command,
+        * indicating completion of the above issued command.
+        */
+       if (wait)
+               pcie_wait_cmd(ctrl);
+
        mutex_unlock(&ctrl->ctrl_lock);
 }
 
+/**
+ * pcie_write_cmd - Issue controller command
+ * @ctrl: controller to which the command is issued
+ * @cmd:  command value written to slot control register
+ * @mask: bitmask of slot control register to be modified
+ */
+static void pcie_write_cmd(struct controller *ctrl, u16 cmd, u16 mask)
+{
+       pcie_do_write_cmd(ctrl, cmd, mask, true);
+}
+
+/* Same as above without waiting for the hardware to latch */
+static void pcie_write_cmd_nowait(struct controller *ctrl, u16 cmd, u16 mask)
+{
+       pcie_do_write_cmd(ctrl, cmd, mask, false);
+}
+
 bool pciehp_check_link_active(struct controller *ctrl)
 {
        struct pci_dev *pdev = ctrl_dev(ctrl);
@@ -291,7 +312,8 @@ int pciehp_check_link_status(struct controller *ctrl)
        ctrl_dbg(ctrl, "%s: lnk_status = %x\n", __func__, lnk_status);
        if ((lnk_status & PCI_EXP_LNKSTA_LT) ||
            !(lnk_status & PCI_EXP_LNKSTA_NLW)) {
-               ctrl_err(ctrl, "Link Training Error occurs\n");
+               ctrl_err(ctrl, "link training error: status %#06x\n",
+                        lnk_status);
                return -1;
        }
 
@@ -422,7 +444,7 @@ void pciehp_set_attention_status(struct slot *slot, u8 value)
        default:
                return;
        }
-       pcie_write_cmd(ctrl, slot_cmd, PCI_EXP_SLTCTL_AIC);
+       pcie_write_cmd_nowait(ctrl, slot_cmd, PCI_EXP_SLTCTL_AIC);
        ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__,
                 pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, slot_cmd);
 }
@@ -434,7 +456,8 @@ void pciehp_green_led_on(struct slot *slot)
        if (!PWR_LED(ctrl))
                return;
 
-       pcie_write_cmd(ctrl, PCI_EXP_SLTCTL_PWR_IND_ON, PCI_EXP_SLTCTL_PIC);
+       pcie_write_cmd_nowait(ctrl, PCI_EXP_SLTCTL_PWR_IND_ON,
+                             PCI_EXP_SLTCTL_PIC);
        ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__,
                 pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL,
                 PCI_EXP_SLTCTL_PWR_IND_ON);
@@ -447,7 +470,8 @@ void pciehp_green_led_off(struct slot *slot)
        if (!PWR_LED(ctrl))
                return;
 
-       pcie_write_cmd(ctrl, PCI_EXP_SLTCTL_PWR_IND_OFF, PCI_EXP_SLTCTL_PIC);
+       pcie_write_cmd_nowait(ctrl, PCI_EXP_SLTCTL_PWR_IND_OFF,
+                             PCI_EXP_SLTCTL_PIC);
        ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__,
                 pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL,
                 PCI_EXP_SLTCTL_PWR_IND_OFF);
@@ -460,7 +484,8 @@ void pciehp_green_led_blink(struct slot *slot)
        if (!PWR_LED(ctrl))
                return;
 
-       pcie_write_cmd(ctrl, PCI_EXP_SLTCTL_PWR_IND_BLINK, PCI_EXP_SLTCTL_PIC);
+       pcie_write_cmd_nowait(ctrl, PCI_EXP_SLTCTL_PWR_IND_BLINK,
+                             PCI_EXP_SLTCTL_PIC);
        ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__,
                 pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL,
                 PCI_EXP_SLTCTL_PWR_IND_BLINK);
@@ -510,6 +535,8 @@ static irqreturn_t pcie_isr(int irq, void *dev_id)
        struct pci_dev *dev;
        struct slot *slot = ctrl->slot;
        u16 detected, intr_loc;
+       u8 open, present;
+       bool link;
 
        /*
         * In order to guarantee that all interrupt events are
@@ -532,7 +559,7 @@ static irqreturn_t pcie_isr(int irq, void *dev_id)
                                                   intr_loc);
        } while (detected);
 
-       ctrl_dbg(ctrl, "%s: intr_loc %x\n", __func__, intr_loc);
+       ctrl_dbg(ctrl, "pending interrupts %#06x from Slot Status\n", intr_loc);
 
        /* Check Command Complete Interrupt Pending */
        if (intr_loc & PCI_EXP_SLTSTA_CC) {
@@ -555,25 +582,44 @@ static irqreturn_t pcie_isr(int irq, void *dev_id)
                return IRQ_HANDLED;
 
        /* Check MRL Sensor Changed */
-       if (intr_loc & PCI_EXP_SLTSTA_MRLSC)
-               pciehp_handle_switch_change(slot);
+       if (intr_loc & PCI_EXP_SLTSTA_MRLSC) {
+               pciehp_get_latch_status(slot, &open);
+               ctrl_info(ctrl, "Latch %s on Slot(%s)\n",
+                         open ? "open" : "close", slot_name(slot));
+               pciehp_queue_interrupt_event(slot, open ? INT_SWITCH_OPEN :
+                                            INT_SWITCH_CLOSE);
+       }
 
        /* Check Attention Button Pressed */
-       if (intr_loc & PCI_EXP_SLTSTA_ABP)
-               pciehp_handle_attention_button(slot);
+       if (intr_loc & PCI_EXP_SLTSTA_ABP) {
+               ctrl_info(ctrl, "Button pressed on Slot(%s)\n",
+                         slot_name(slot));
+               pciehp_queue_interrupt_event(slot, INT_BUTTON_PRESS);
+       }
 
        /* Check Presence Detect Changed */
-       if (intr_loc & PCI_EXP_SLTSTA_PDC)
-               pciehp_handle_presence_change(slot);
+       if (intr_loc & PCI_EXP_SLTSTA_PDC) {
+               pciehp_get_adapter_status(slot, &present);
+               ctrl_info(ctrl, "Card %spresent on Slot(%s)\n",
+                         present ? "" : "not ", slot_name(slot));
+               pciehp_queue_interrupt_event(slot, present ? INT_PRESENCE_ON :
+                                            INT_PRESENCE_OFF);
+       }
 
        /* Check Power Fault Detected */
        if ((intr_loc & PCI_EXP_SLTSTA_PFD) && !ctrl->power_fault_detected) {
                ctrl->power_fault_detected = 1;
-               pciehp_handle_power_fault(slot);
+               ctrl_err(ctrl, "Power fault on slot %s\n", slot_name(slot));
+               pciehp_queue_interrupt_event(slot, INT_POWER_FAULT);
        }
 
-       if (intr_loc & PCI_EXP_SLTSTA_DLLSC)
-               pciehp_handle_linkstate_change(slot);
+       if (intr_loc & PCI_EXP_SLTSTA_DLLSC) {
+               link = pciehp_check_link_active(ctrl);
+               ctrl_info(ctrl, "slot(%s): Link %s event\n",
+                         slot_name(slot), link ? "Up" : "Down");
+               pciehp_queue_interrupt_event(slot, link ? INT_LINK_UP :
+                                            INT_LINK_DOWN);
+       }
 
        return IRQ_HANDLED;
 }
@@ -613,7 +659,7 @@ void pcie_enable_notification(struct controller *ctrl)
                PCI_EXP_SLTCTL_HPIE | PCI_EXP_SLTCTL_CCIE |
                PCI_EXP_SLTCTL_DLLSCE);
 
-       pcie_write_cmd(ctrl, cmd, mask);
+       pcie_write_cmd_nowait(ctrl, cmd, mask);
        ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__,
                 pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, cmd);
 }
@@ -664,7 +710,7 @@ int pciehp_reset_slot(struct slot *slot, int probe)
        pci_reset_bridge_secondary_bus(ctrl->pcie->port);
 
        pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, stat_mask);
-       pcie_write_cmd(ctrl, ctrl_mask, ctrl_mask);
+       pcie_write_cmd_nowait(ctrl, ctrl_mask, ctrl_mask);
        ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__,
                 pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, ctrl_mask);
        if (pciehp_poll_mode)
@@ -724,48 +770,13 @@ static void pcie_cleanup_slot(struct controller *ctrl)
 
 static inline void dbg_ctrl(struct controller *ctrl)
 {
-       int i;
-       u16 reg16;
        struct pci_dev *pdev = ctrl->pcie->port;
+       u16 reg16;
 
        if (!pciehp_debug)
                return;
 
-       ctrl_info(ctrl, "Hotplug Controller:\n");
-       ctrl_info(ctrl, "  Seg/Bus/Dev/Func/IRQ : %s IRQ %d\n",
-                 pci_name(pdev), pdev->irq);
-       ctrl_info(ctrl, "  Vendor ID            : 0x%04x\n", pdev->vendor);
-       ctrl_info(ctrl, "  Device ID            : 0x%04x\n", pdev->device);
-       ctrl_info(ctrl, "  Subsystem ID         : 0x%04x\n",
-                 pdev->subsystem_device);
-       ctrl_info(ctrl, "  Subsystem Vendor ID  : 0x%04x\n",
-                 pdev->subsystem_vendor);
-       ctrl_info(ctrl, "  PCIe Cap offset      : 0x%02x\n",
-                 pci_pcie_cap(pdev));
-       for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) {
-               if (!pci_resource_len(pdev, i))
-                       continue;
-               ctrl_info(ctrl, "  PCI resource [%d]     : %pR\n",
-                         i, &pdev->resource[i]);
-       }
        ctrl_info(ctrl, "Slot Capabilities      : 0x%08x\n", ctrl->slot_cap);
-       ctrl_info(ctrl, "  Physical Slot Number : %d\n", PSN(ctrl));
-       ctrl_info(ctrl, "  Attention Button     : %3s\n",
-                 ATTN_BUTTN(ctrl) ? "yes" : "no");
-       ctrl_info(ctrl, "  Power Controller     : %3s\n",
-                 POWER_CTRL(ctrl) ? "yes" : "no");
-       ctrl_info(ctrl, "  MRL Sensor           : %3s\n",
-                 MRL_SENS(ctrl)   ? "yes" : "no");
-       ctrl_info(ctrl, "  Attention Indicator  : %3s\n",
-                 ATTN_LED(ctrl)   ? "yes" : "no");
-       ctrl_info(ctrl, "  Power Indicator      : %3s\n",
-                 PWR_LED(ctrl)    ? "yes" : "no");
-       ctrl_info(ctrl, "  Hot-Plug Surprise    : %3s\n",
-                 HP_SUPR_RM(ctrl) ? "yes" : "no");
-       ctrl_info(ctrl, "  EMI Present          : %3s\n",
-                 EMI(ctrl)        ? "yes" : "no");
-       ctrl_info(ctrl, "  Command Completed    : %3s\n",
-                 NO_CMD_CMPL(ctrl) ? "no" : "yes");
        pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &reg16);
        ctrl_info(ctrl, "Slot Status            : 0x%04x\n", reg16);
        pcie_capability_read_word(pdev, PCI_EXP_SLTCTL, &reg16);
@@ -794,10 +805,8 @@ struct controller *pcie_init(struct pcie_device *dev)
 
        /* Check if Data Link Layer Link Active Reporting is implemented */
        pcie_capability_read_dword(pdev, PCI_EXP_LNKCAP, &link_cap);
-       if (link_cap & PCI_EXP_LNKCAP_DLLLARC) {
-               ctrl_dbg(ctrl, "Link Active Reporting supported\n");
+       if (link_cap & PCI_EXP_LNKCAP_DLLLARC)
                ctrl->link_active_reporting = 1;
-       }
 
        /* Clear all remaining event bits in Slot Status register */
        pcie_capability_write_word(pdev, PCI_EXP_SLTSTA,
@@ -805,13 +814,15 @@ struct controller *pcie_init(struct pcie_device *dev)
                PCI_EXP_SLTSTA_MRLSC | PCI_EXP_SLTSTA_PDC |
                PCI_EXP_SLTSTA_CC | PCI_EXP_SLTSTA_DLLSC);
 
-       ctrl_info(ctrl, "Slot #%d AttnBtn%c AttnInd%c PwrInd%c PwrCtrl%c MRL%c Interlock%c NoCompl%c LLActRep%c\n",
+       ctrl_info(ctrl, "Slot #%d AttnBtn%c PwrCtrl%c MRL%c AttnInd%c PwrInd%c HotPlug%c Surprise%c Interlock%c NoCompl%c LLActRep%c\n",
                (slot_cap & PCI_EXP_SLTCAP_PSN) >> 19,
                FLAG(slot_cap, PCI_EXP_SLTCAP_ABP),
-               FLAG(slot_cap, PCI_EXP_SLTCAP_AIP),
-               FLAG(slot_cap, PCI_EXP_SLTCAP_PIP),
                FLAG(slot_cap, PCI_EXP_SLTCAP_PCP),
                FLAG(slot_cap, PCI_EXP_SLTCAP_MRLSP),
+               FLAG(slot_cap, PCI_EXP_SLTCAP_AIP),
+               FLAG(slot_cap, PCI_EXP_SLTCAP_PIP),
+               FLAG(slot_cap, PCI_EXP_SLTCAP_HPC),
+               FLAG(slot_cap, PCI_EXP_SLTCAP_HPS),
                FLAG(slot_cap, PCI_EXP_SLTCAP_EIP),
                FLAG(slot_cap, PCI_EXP_SLTCAP_NCCS),
                FLAG(link_cap, PCI_EXP_LNKCAP_DLLLARC));
index a94dd2c4183a0ddc7ac118b1cfc41a7014d2fc4c..7eb4109a3df4eb6941f6c4c8ee435e7bb98f7747 100644 (file)
  */
 static DEFINE_SPINLOCK(ht_irq_lock);
 
-struct ht_irq_cfg {
-       struct pci_dev *dev;
-        /* Update callback used to cope with buggy hardware */
-       ht_irq_update_t *update;
-       unsigned pos;
-       unsigned idx;
-       struct ht_irq_msg msg;
-};
-
-
 void write_ht_irq_msg(unsigned int irq, struct ht_irq_msg *msg)
 {
        struct ht_irq_cfg *cfg = irq_get_handler_data(irq);
        unsigned long flags;
+
        spin_lock_irqsave(&ht_irq_lock, flags);
        if (cfg->msg.address_lo != msg->address_lo) {
                pci_write_config_byte(cfg->dev, cfg->pos + 2, cfg->idx);
@@ -55,6 +46,7 @@ void write_ht_irq_msg(unsigned int irq, struct ht_irq_msg *msg)
 void fetch_ht_irq_msg(unsigned int irq, struct ht_irq_msg *msg)
 {
        struct ht_irq_cfg *cfg = irq_get_handler_data(irq);
+
        *msg = cfg->msg;
 }
 
@@ -86,7 +78,6 @@ void unmask_ht_irq(struct irq_data *data)
  */
 int __ht_create_irq(struct pci_dev *dev, int idx, ht_irq_update_t *update)
 {
-       struct ht_irq_cfg *cfg;
        int max_irq, pos, irq;
        unsigned long flags;
        u32 data;
@@ -105,29 +96,9 @@ int __ht_create_irq(struct pci_dev *dev, int idx, ht_irq_update_t *update)
        if (idx > max_irq)
                return -EINVAL;
 
-       cfg = kmalloc(sizeof(*cfg), GFP_KERNEL);
-       if (!cfg)
-               return -ENOMEM;
-
-       cfg->dev = dev;
-       cfg->update = update;
-       cfg->pos = pos;
-       cfg->idx = 0x10 + (idx * 2);
-       /* Initialize msg to a value that will never match the first write. */
-       cfg->msg.address_lo = 0xffffffff;
-       cfg->msg.address_hi = 0xffffffff;
-
-       irq = irq_alloc_hwirq(dev_to_node(&dev->dev));
-       if (!irq) {
-               kfree(cfg);
-               return -EBUSY;
-       }
-       irq_set_handler_data(irq, cfg);
-
-       if (arch_setup_ht_irq(irq, dev) < 0) {
-               ht_destroy_irq(irq);
-               return -EBUSY;
-       }
+       irq = arch_setup_ht_irq(idx, pos, dev, update);
+       if (irq > 0)
+               dev_dbg(&dev->dev, "irq %d for HT\n", irq);
 
        return irq;
 }
@@ -158,13 +129,6 @@ EXPORT_SYMBOL(ht_create_irq);
  */
 void ht_destroy_irq(unsigned int irq)
 {
-       struct ht_irq_cfg *cfg;
-
-       cfg = irq_get_handler_data(irq);
-       irq_set_chip(irq, NULL);
-       irq_set_handler_data(irq, NULL);
-       irq_free_hwirq(irq);
-
-       kfree(cfg);
+       arch_teardown_ht_irq(irq);
 }
 EXPORT_SYMBOL(ht_destroy_irq);
index c3e7dfcf9ff53b851a8dff2979b3bddc9c2905a3..f66be868ad2122efdb40e742f8ecb9fc43cbc508 100644 (file)
@@ -185,27 +185,6 @@ void __weak arch_restore_msi_irqs(struct pci_dev *dev)
        return default_restore_msi_irqs(dev);
 }
 
-static void msi_set_enable(struct pci_dev *dev, int enable)
-{
-       u16 control;
-
-       pci_read_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, &control);
-       control &= ~PCI_MSI_FLAGS_ENABLE;
-       if (enable)
-               control |= PCI_MSI_FLAGS_ENABLE;
-       pci_write_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, control);
-}
-
-static void msix_clear_and_set_ctrl(struct pci_dev *dev, u16 clear, u16 set)
-{
-       u16 ctrl;
-
-       pci_read_config_word(dev, dev->msix_cap + PCI_MSIX_FLAGS, &ctrl);
-       ctrl &= ~clear;
-       ctrl |= set;
-       pci_write_config_word(dev, dev->msix_cap + PCI_MSIX_FLAGS, ctrl);
-}
-
 static inline __attribute_const__ u32 msi_mask(unsigned x)
 {
        /* Don't shift by >= width of type */
@@ -452,7 +431,7 @@ static void __pci_restore_msi_state(struct pci_dev *dev)
        entry = irq_get_msi_desc(dev->irq);
 
        pci_intx_for_msi(dev, 0);
-       msi_set_enable(dev, 0);
+       pci_msi_set_enable(dev, 0);
        arch_restore_msi_irqs(dev);
 
        pci_read_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, &control);
@@ -473,14 +452,14 @@ static void __pci_restore_msix_state(struct pci_dev *dev)
 
        /* route the table */
        pci_intx_for_msi(dev, 0);
-       msix_clear_and_set_ctrl(dev, 0,
+       pci_msix_clear_and_set_ctrl(dev, 0,
                                PCI_MSIX_FLAGS_ENABLE | PCI_MSIX_FLAGS_MASKALL);
 
        arch_restore_msi_irqs(dev);
        list_for_each_entry(entry, &dev->msi_list, list)
                msix_mask_irq(entry, entry->masked);
 
-       msix_clear_and_set_ctrl(dev, PCI_MSIX_FLAGS_MASKALL, 0);
+       pci_msix_clear_and_set_ctrl(dev, PCI_MSIX_FLAGS_MASKALL, 0);
 }
 
 void pci_restore_msi_state(struct pci_dev *dev)
@@ -647,7 +626,7 @@ static int msi_capability_init(struct pci_dev *dev, int nvec)
        int ret;
        unsigned mask;
 
-       msi_set_enable(dev, 0); /* Disable MSI during set up */
+       pci_msi_set_enable(dev, 0);     /* Disable MSI during set up */
 
        entry = msi_setup_entry(dev, nvec);
        if (!entry)
@@ -683,7 +662,7 @@ static int msi_capability_init(struct pci_dev *dev, int nvec)
 
        /* Set MSI enabled bits  */
        pci_intx_for_msi(dev, 0);
-       msi_set_enable(dev, 1);
+       pci_msi_set_enable(dev, 1);
        dev->msi_enabled = 1;
 
        dev->irq = entry->irq;
@@ -775,7 +754,7 @@ static int msix_capability_init(struct pci_dev *dev,
        void __iomem *base;
 
        /* Ensure MSI-X is disabled while it is set up */
-       msix_clear_and_set_ctrl(dev, PCI_MSIX_FLAGS_ENABLE, 0);
+       pci_msix_clear_and_set_ctrl(dev, PCI_MSIX_FLAGS_ENABLE, 0);
 
        pci_read_config_word(dev, dev->msix_cap + PCI_MSIX_FLAGS, &control);
        /* Request & Map MSI-X table region */
@@ -801,7 +780,7 @@ static int msix_capability_init(struct pci_dev *dev,
         * MSI-X registers.  We need to mask all the vectors to prevent
         * interrupts coming in before they're fully set up.
         */
-       msix_clear_and_set_ctrl(dev, 0,
+       pci_msix_clear_and_set_ctrl(dev, 0,
                                PCI_MSIX_FLAGS_MASKALL | PCI_MSIX_FLAGS_ENABLE);
 
        msix_program_entries(dev, entries);
@@ -814,7 +793,7 @@ static int msix_capability_init(struct pci_dev *dev,
        pci_intx_for_msi(dev, 0);
        dev->msix_enabled = 1;
 
-       msix_clear_and_set_ctrl(dev, PCI_MSIX_FLAGS_MASKALL, 0);
+       pci_msix_clear_and_set_ctrl(dev, PCI_MSIX_FLAGS_MASKALL, 0);
 
        return 0;
 
@@ -919,7 +898,7 @@ void pci_msi_shutdown(struct pci_dev *dev)
        BUG_ON(list_empty(&dev->msi_list));
        desc = list_first_entry(&dev->msi_list, struct msi_desc, list);
 
-       msi_set_enable(dev, 0);
+       pci_msi_set_enable(dev, 0);
        pci_intx_for_msi(dev, 1);
        dev->msi_enabled = 0;
 
@@ -1027,7 +1006,7 @@ void pci_msix_shutdown(struct pci_dev *dev)
                __pci_msix_desc_mask_irq(entry, 1);
        }
 
-       msix_clear_and_set_ctrl(dev, PCI_MSIX_FLAGS_ENABLE, 0);
+       pci_msix_clear_and_set_ctrl(dev, PCI_MSIX_FLAGS_ENABLE, 0);
        pci_intx_for_msi(dev, 1);
        dev->msix_enabled = 0;
 }
@@ -1062,18 +1041,6 @@ EXPORT_SYMBOL(pci_msi_enabled);
 void pci_msi_init_pci_dev(struct pci_dev *dev)
 {
        INIT_LIST_HEAD(&dev->msi_list);
-
-       /* Disable the msi hardware to avoid screaming interrupts
-        * during boot.  This is the power on reset default so
-        * usually this should be a noop.
-        */
-       dev->msi_cap = pci_find_capability(dev, PCI_CAP_ID_MSI);
-       if (dev->msi_cap)
-               msi_set_enable(dev, 0);
-
-       dev->msix_cap = pci_find_capability(dev, PCI_CAP_ID_MSIX);
-       if (dev->msix_cap)
-               msix_clear_and_set_ctrl(dev, PCI_MSIX_FLAGS_ENABLE, 0);
 }
 
 /**
index 6f6f175f51f78003524e96c046fb58b7298407ad..314a625b78d6360093eff3be221ffc04193b771e 100644 (file)
@@ -420,7 +420,7 @@ static int acpi_pci_set_power_state(struct pci_dev *dev, pci_power_t state)
                [PCI_D0] = ACPI_STATE_D0,
                [PCI_D1] = ACPI_STATE_D1,
                [PCI_D2] = ACPI_STATE_D2,
-               [PCI_D3hot] = ACPI_STATE_D3_COLD,
+               [PCI_D3hot] = ACPI_STATE_D3_HOT,
                [PCI_D3cold] = ACPI_STATE_D3_COLD,
        };
        int error = -EINVAL;
index acc4b6ef78c4380273b1af144838582dde57f80a..0008c950452c31e71f8f591354449980893e58f6 100644 (file)
@@ -3101,39 +3101,6 @@ bool pci_check_and_unmask_intx(struct pci_dev *dev)
 }
 EXPORT_SYMBOL_GPL(pci_check_and_unmask_intx);
 
-/**
- * pci_msi_off - disables any MSI or MSI-X capabilities
- * @dev: the PCI device to operate on
- *
- * If you want to use MSI, see pci_enable_msi() and friends.
- * This is a lower-level primitive that allows us to disable
- * MSI operation at the device level.
- */
-void pci_msi_off(struct pci_dev *dev)
-{
-       int pos;
-       u16 control;
-
-       /*
-        * This looks like it could go in msi.c, but we need it even when
-        * CONFIG_PCI_MSI=n.  For the same reason, we can't use
-        * dev->msi_cap or dev->msix_cap here.
-        */
-       pos = pci_find_capability(dev, PCI_CAP_ID_MSI);
-       if (pos) {
-               pci_read_config_word(dev, pos + PCI_MSI_FLAGS, &control);
-               control &= ~PCI_MSI_FLAGS_ENABLE;
-               pci_write_config_word(dev, pos + PCI_MSI_FLAGS, control);
-       }
-       pos = pci_find_capability(dev, PCI_CAP_ID_MSIX);
-       if (pos) {
-               pci_read_config_word(dev, pos + PCI_MSIX_FLAGS, &control);
-               control &= ~PCI_MSIX_FLAGS_ENABLE;
-               pci_write_config_word(dev, pos + PCI_MSIX_FLAGS, control);
-       }
-}
-EXPORT_SYMBOL_GPL(pci_msi_off);
-
 int pci_set_dma_max_seg_size(struct pci_dev *dev, unsigned int size)
 {
        return dma_set_max_seg_size(&dev->dev, size);
@@ -4324,6 +4291,17 @@ bool pci_device_is_present(struct pci_dev *pdev)
 }
 EXPORT_SYMBOL_GPL(pci_device_is_present);
 
+void pci_ignore_hotplug(struct pci_dev *dev)
+{
+       struct pci_dev *bridge = dev->bus->self;
+
+       dev->ignore_hotplug = 1;
+       /* Propagate the "ignore hotplug" setting to the parent bridge. */
+       if (bridge)
+               bridge->ignore_hotplug = 1;
+}
+EXPORT_SYMBOL_GPL(pci_ignore_hotplug);
+
 #define RESOURCE_ALIGNMENT_PARAM_SIZE COMMAND_LINE_SIZE
 static char resource_alignment_param[RESOURCE_ALIGNMENT_PARAM_SIZE] = {0};
 static DEFINE_SPINLOCK(resource_alignment_lock);
index 9bd762c237abe2fd679e8c7163947c7a0453c46b..4ff0ff1c4088ff68f8ca7bb515e54ad382e74680 100644 (file)
@@ -146,6 +146,27 @@ static inline void pci_no_msi(void) { }
 static inline void pci_msi_init_pci_dev(struct pci_dev *dev) { }
 #endif
 
+static inline void pci_msi_set_enable(struct pci_dev *dev, int enable)
+{
+       u16 control;
+
+       pci_read_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, &control);
+       control &= ~PCI_MSI_FLAGS_ENABLE;
+       if (enable)
+               control |= PCI_MSI_FLAGS_ENABLE;
+       pci_write_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, control);
+}
+
+static inline void pci_msix_clear_and_set_ctrl(struct pci_dev *dev, u16 clear, u16 set)
+{
+       u16 ctrl;
+
+       pci_read_config_word(dev, dev->msix_cap + PCI_MSIX_FLAGS, &ctrl);
+       ctrl &= ~clear;
+       ctrl |= set;
+       pci_write_config_word(dev, dev->msix_cap + PCI_MSIX_FLAGS, ctrl);
+}
+
 void pci_realloc_get_opt(char *);
 
 static inline int pci_no_d1d2(struct pci_dev *dev)
@@ -216,17 +237,6 @@ void __pci_bus_assign_resources(const struct pci_bus *bus,
                                struct list_head *fail_head);
 bool pci_bus_clip_resource(struct pci_dev *dev, int idx);
 
-/**
- * pci_ari_enabled - query ARI forwarding status
- * @bus: the PCI bus
- *
- * Returns 1 if ARI forwarding is enabled, or 0 if not enabled;
- */
-static inline int pci_ari_enabled(struct pci_bus *bus)
-{
-       return bus->self && bus->self->ari_enabled;
-}
-
 void pci_reassigndev_resource_alignment(struct pci_dev *dev);
 void pci_disable_bridge_window(struct pci_dev *dev);
 
index 5653ea94547fc8a53caf0c050f0d499936cb4a41..9803e3d039febf7f5ea0e3613da5bb07243d501e 100644 (file)
@@ -425,8 +425,7 @@ static pci_ers_result_t reset_link(struct pci_dev *dev)
 
        if (driver && driver->reset_link) {
                status = driver->reset_link(udev);
-       } else if (pci_pcie_type(udev) == PCI_EXP_TYPE_DOWNSTREAM ||
-               pci_pcie_type(udev) == PCI_EXP_TYPE_ROOT_PORT) {
+       } else if (udev->has_secondary_link) {
                status = default_reset_link(udev);
        } else {
                dev_printk(KERN_DEBUG, &dev->dev,
index 7d4fcdc512aa0ab5d4768b94272eb8359a2599cb..317e3558a35e00d621d838c4825386f28d823910 100644 (file)
@@ -127,15 +127,12 @@ static void pcie_set_clkpm_nocheck(struct pcie_link_state *link, int enable)
 {
        struct pci_dev *child;
        struct pci_bus *linkbus = link->pdev->subordinate;
+       u32 val = enable ? PCI_EXP_LNKCTL_CLKREQ_EN : 0;
 
-       list_for_each_entry(child, &linkbus->devices, bus_list) {
-               if (enable)
-                       pcie_capability_set_word(child, PCI_EXP_LNKCTL,
-                                                PCI_EXP_LNKCTL_CLKREQ_EN);
-               else
-                       pcie_capability_clear_word(child, PCI_EXP_LNKCTL,
-                                                  PCI_EXP_LNKCTL_CLKREQ_EN);
-       }
+       list_for_each_entry(child, &linkbus->devices, bus_list)
+               pcie_capability_clear_and_set_word(child, PCI_EXP_LNKCTL,
+                                                  PCI_EXP_LNKCTL_CLKREQ_EN,
+                                                  val);
        link->clkpm_enabled = !!enable;
 }
 
@@ -525,7 +522,7 @@ static struct pcie_link_state *alloc_pcie_link_state(struct pci_dev *pdev)
        INIT_LIST_HEAD(&link->children);
        INIT_LIST_HEAD(&link->link);
        link->pdev = pdev;
-       if (pci_pcie_type(pdev) == PCI_EXP_TYPE_DOWNSTREAM) {
+       if (pci_pcie_type(pdev) != PCI_EXP_TYPE_ROOT_PORT) {
                struct pcie_link_state *parent;
                parent = pdev->bus->parent->self->link_state;
                if (!parent) {
@@ -559,10 +556,15 @@ void pcie_aspm_init_link_state(struct pci_dev *pdev)
        if (!aspm_support_enabled)
                return;
 
-       if (!pci_is_pcie(pdev) || pdev->link_state)
+       if (pdev->link_state)
                return;
-       if (pci_pcie_type(pdev) != PCI_EXP_TYPE_ROOT_PORT &&
-           pci_pcie_type(pdev) != PCI_EXP_TYPE_DOWNSTREAM)
+
+       /*
+        * We allocate pcie_link_state for the component on the upstream
+        * end of a Link, so there's nothing to do unless this device has a
+        * Link on its secondary side.
+        */
+       if (!pdev->has_secondary_link)
                return;
 
        /* VIA has a strange chipset, root port is under a bridge */
@@ -675,10 +677,7 @@ void pcie_aspm_pm_state_change(struct pci_dev *pdev)
 {
        struct pcie_link_state *link = pdev->link_state;
 
-       if (aspm_disabled || !pci_is_pcie(pdev) || !link)
-               return;
-       if ((pci_pcie_type(pdev) != PCI_EXP_TYPE_ROOT_PORT) &&
-           (pci_pcie_type(pdev) != PCI_EXP_TYPE_DOWNSTREAM))
+       if (aspm_disabled || !link)
                return;
        /*
         * Devices changed PM state, we should recheck if latency
@@ -696,16 +695,12 @@ void pcie_aspm_powersave_config_link(struct pci_dev *pdev)
 {
        struct pcie_link_state *link = pdev->link_state;
 
-       if (aspm_disabled || !pci_is_pcie(pdev) || !link)
+       if (aspm_disabled || !link)
                return;
 
        if (aspm_policy != POLICY_POWERSAVE)
                return;
 
-       if ((pci_pcie_type(pdev) != PCI_EXP_TYPE_ROOT_PORT) &&
-           (pci_pcie_type(pdev) != PCI_EXP_TYPE_DOWNSTREAM))
-               return;
-
        down_read(&pci_bus_sem);
        mutex_lock(&aspm_lock);
        pcie_config_aspm_path(link);
@@ -714,8 +709,7 @@ void pcie_aspm_powersave_config_link(struct pci_dev *pdev)
        up_read(&pci_bus_sem);
 }
 
-static void __pci_disable_link_state(struct pci_dev *pdev, int state, bool sem,
-                                    bool force)
+static void __pci_disable_link_state(struct pci_dev *pdev, int state, bool sem)
 {
        struct pci_dev *parent = pdev->bus->self;
        struct pcie_link_state *link;
@@ -723,8 +717,7 @@ static void __pci_disable_link_state(struct pci_dev *pdev, int state, bool sem,
        if (!pci_is_pcie(pdev))
                return;
 
-       if (pci_pcie_type(pdev) == PCI_EXP_TYPE_ROOT_PORT ||
-           pci_pcie_type(pdev) == PCI_EXP_TYPE_DOWNSTREAM)
+       if (pdev->has_secondary_link)
                parent = pdev;
        if (!parent || !parent->link_state)
                return;
@@ -737,7 +730,7 @@ static void __pci_disable_link_state(struct pci_dev *pdev, int state, bool sem,
         * a similar mechanism using "PciASPMOptOut", which is also
         * ignored in this situation.
         */
-       if (aspm_disabled && !force) {
+       if (aspm_disabled) {
                dev_warn(&pdev->dev, "can't disable ASPM; OS doesn't have ASPM control\n");
                return;
        }
@@ -763,7 +756,7 @@ static void __pci_disable_link_state(struct pci_dev *pdev, int state, bool sem,
 
 void pci_disable_link_state_locked(struct pci_dev *pdev, int state)
 {
-       __pci_disable_link_state(pdev, state, false, false);
+       __pci_disable_link_state(pdev, state, false);
 }
 EXPORT_SYMBOL(pci_disable_link_state_locked);
 
@@ -778,7 +771,7 @@ EXPORT_SYMBOL(pci_disable_link_state_locked);
  */
 void pci_disable_link_state(struct pci_dev *pdev, int state)
 {
-       __pci_disable_link_state(pdev, state, true, false);
+       __pci_disable_link_state(pdev, state, true);
 }
 EXPORT_SYMBOL(pci_disable_link_state);
 
@@ -907,9 +900,7 @@ void pcie_aspm_create_sysfs_dev_files(struct pci_dev *pdev)
 {
        struct pcie_link_state *link_state = pdev->link_state;
 
-       if (!pci_is_pcie(pdev) ||
-           (pci_pcie_type(pdev) != PCI_EXP_TYPE_ROOT_PORT &&
-            pci_pcie_type(pdev) != PCI_EXP_TYPE_DOWNSTREAM) || !link_state)
+       if (!link_state)
                return;
 
        if (link_state->aspm_support)
@@ -924,9 +915,7 @@ void pcie_aspm_remove_sysfs_dev_files(struct pci_dev *pdev)
 {
        struct pcie_link_state *link_state = pdev->link_state;
 
-       if (!pci_is_pcie(pdev) ||
-           (pci_pcie_type(pdev) != PCI_EXP_TYPE_ROOT_PORT &&
-            pci_pcie_type(pdev) != PCI_EXP_TYPE_DOWNSTREAM) || !link_state)
+       if (!link_state)
                return;
 
        if (link_state->aspm_support)
index 6675a7a1b9fc6a113379b9e5ac025b2a3df66ad3..cefd636681b6418ce75376879dc26fe3891bc47d 100644 (file)
@@ -254,8 +254,8 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type,
        }
 
        if (res->flags & IORESOURCE_MEM_64) {
-               if ((sizeof(dma_addr_t) < 8 || sizeof(resource_size_t) < 8) &&
-                   sz64 > 0x100000000ULL) {
+               if ((sizeof(pci_bus_addr_t) < 8 || sizeof(resource_size_t) < 8)
+                   && sz64 > 0x100000000ULL) {
                        res->flags |= IORESOURCE_UNSET | IORESOURCE_DISABLED;
                        res->start = 0;
                        res->end = 0;
@@ -264,7 +264,7 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type,
                        goto out;
                }
 
-               if ((sizeof(dma_addr_t) < 8) && l) {
+               if ((sizeof(pci_bus_addr_t) < 8) && l) {
                        /* Above 32-bit boundary; try to reallocate */
                        res->flags |= IORESOURCE_UNSET;
                        res->start = 0;
@@ -399,7 +399,7 @@ static void pci_read_bridge_mmio_pref(struct pci_bus *child)
        struct pci_dev *dev = child->self;
        u16 mem_base_lo, mem_limit_lo;
        u64 base64, limit64;
-       dma_addr_t base, limit;
+       pci_bus_addr_t base, limit;
        struct pci_bus_region region;
        struct resource *res;
 
@@ -426,8 +426,8 @@ static void pci_read_bridge_mmio_pref(struct pci_bus *child)
                }
        }
 
-       base = (dma_addr_t) base64;
-       limit = (dma_addr_t) limit64;
+       base = (pci_bus_addr_t) base64;
+       limit = (pci_bus_addr_t) limit64;
 
        if (base != base64) {
                dev_err(&dev->dev, "can't handle bridge window above 4GB (bus address %#010llx)\n",
@@ -973,6 +973,8 @@ void set_pcie_port_type(struct pci_dev *pdev)
 {
        int pos;
        u16 reg16;
+       int type;
+       struct pci_dev *parent;
 
        pos = pci_find_capability(pdev, PCI_CAP_ID_EXP);
        if (!pos)
@@ -982,6 +984,22 @@ void set_pcie_port_type(struct pci_dev *pdev)
        pdev->pcie_flags_reg = reg16;
        pci_read_config_word(pdev, pos + PCI_EXP_DEVCAP, &reg16);
        pdev->pcie_mpss = reg16 & PCI_EXP_DEVCAP_PAYLOAD;
+
+       /*
+        * A Root Port is always the upstream end of a Link.  No PCIe
+        * component has two Links.  Two Links are connected by a Switch
+        * that has a Port on each Link and internal logic to connect the
+        * two Ports.
+        */
+       type = pci_pcie_type(pdev);
+       if (type == PCI_EXP_TYPE_ROOT_PORT)
+               pdev->has_secondary_link = 1;
+       else if (type == PCI_EXP_TYPE_UPSTREAM ||
+                type == PCI_EXP_TYPE_DOWNSTREAM) {
+               parent = pci_upstream_bridge(pdev);
+               if (!parent->has_secondary_link)
+                       pdev->has_secondary_link = 1;
+       }
 }
 
 void set_pcie_hotplug_bridge(struct pci_dev *pdev)
@@ -1085,6 +1103,22 @@ int pci_cfg_space_size(struct pci_dev *dev)
 
 #define LEGACY_IO_RESOURCE     (IORESOURCE_IO | IORESOURCE_PCI_FIXED)
 
+static void pci_msi_setup_pci_dev(struct pci_dev *dev)
+{
+       /*
+        * Disable the MSI hardware to avoid screaming interrupts
+        * during boot.  This is the power on reset default so
+        * usually this should be a noop.
+        */
+       dev->msi_cap = pci_find_capability(dev, PCI_CAP_ID_MSI);
+       if (dev->msi_cap)
+               pci_msi_set_enable(dev, 0);
+
+       dev->msix_cap = pci_find_capability(dev, PCI_CAP_ID_MSIX);
+       if (dev->msix_cap)
+               pci_msix_clear_and_set_ctrl(dev, PCI_MSIX_FLAGS_ENABLE, 0);
+}
+
 /**
  * pci_setup_device - fill in class and map information of a device
  * @dev: the device structure to fill
@@ -1140,6 +1174,8 @@ int pci_setup_device(struct pci_dev *dev)
        /* "Unknown power state" */
        dev->current_state = PCI_UNKNOWN;
 
+       pci_msi_setup_pci_dev(dev);
+
        /* Early fixups, before probing the BARs */
        pci_fixup_device(pci_fixup_early, dev);
        /* device class may be changed after fixup */
@@ -1611,7 +1647,7 @@ static int only_one_child(struct pci_bus *bus)
                return 0;
        if (pci_pcie_type(parent) == PCI_EXP_TYPE_ROOT_PORT)
                return 1;
-       if (pci_pcie_type(parent) == PCI_EXP_TYPE_DOWNSTREAM &&
+       if (parent->has_secondary_link &&
            !pci_has_flag(PCI_SCAN_ALL_PCIE_DEVS))
                return 1;
        return 0;
@@ -2094,25 +2130,6 @@ struct pci_bus *pci_scan_root_bus(struct device *parent, int bus,
 }
 EXPORT_SYMBOL(pci_scan_root_bus);
 
-/* Deprecated; use pci_scan_root_bus() instead */
-struct pci_bus *pci_scan_bus_parented(struct device *parent,
-               int bus, struct pci_ops *ops, void *sysdata)
-{
-       LIST_HEAD(resources);
-       struct pci_bus *b;
-
-       pci_add_resource(&resources, &ioport_resource);
-       pci_add_resource(&resources, &iomem_resource);
-       pci_add_resource(&resources, &busn_resource);
-       b = pci_create_root_bus(parent, bus, ops, sysdata, &resources);
-       if (b)
-               pci_scan_child_bus(b);
-       else
-               pci_free_resource_list(&resources);
-       return b;
-}
-EXPORT_SYMBOL(pci_scan_bus_parented);
-
 struct pci_bus *pci_scan_bus(int bus, struct pci_ops *ops,
                                        void *sysdata)
 {
index c6dc1dfd25d55ea9ac536634143eafcf196ce2c2..e9fd0e90fa3b5c6656f7d8939a522add97c29b06 100644 (file)
@@ -819,13 +819,6 @@ static void quirk_amd_ioapic(struct pci_dev *dev)
        }
 }
 DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AMD,     PCI_DEVICE_ID_AMD_VIPER_7410,   quirk_amd_ioapic);
-
-static void quirk_ioapic_rmw(struct pci_dev *dev)
-{
-       if (dev->devfn == 0 && dev->bus->number == 0)
-               sis_apic_bug = 1;
-}
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_SI,      PCI_ANY_ID,                     quirk_ioapic_rmw);
 #endif /* CONFIG_X86_IO_APIC */
 
 /*
@@ -1600,7 +1593,6 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL,     PCI_DEVICE_ID_INTEL_EESSC,      quirk_a
 
 static void quirk_pcie_mch(struct pci_dev *pdev)
 {
-       pci_msi_off(pdev);
        pdev->no_msi = 1;
 }
 DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL,   PCI_DEVICE_ID_INTEL_E7520_MCH,  quirk_pcie_mch);
@@ -1614,7 +1606,6 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL,      PCI_DEVICE_ID_INTEL_E7525_MCH,  quir
  */
 static void quirk_pcie_pxh(struct pci_dev *dev)
 {
-       pci_msi_off(dev);
        dev->no_msi = 1;
        dev_warn(&dev->dev, "PXH quirk detected; SHPC device MSI disabled\n");
 }
@@ -3572,6 +3563,8 @@ static void quirk_dma_func1_alias(struct pci_dev *dev)
  * SKUs this function is not present, making this a ghost requester.
  * https://bugzilla.kernel.org/show_bug.cgi?id=42679
  */
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL_EXT, 0x9120,
+                        quirk_dma_func1_alias);
 DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL_EXT, 0x9123,
                         quirk_dma_func1_alias);
 /* https://bugzilla.kernel.org/show_bug.cgi?id=42679#c14 */
@@ -3740,6 +3733,8 @@ static const u16 pci_quirk_intel_pch_acs_ids[] = {
        /* Wellsburg (X99) PCH */
        0x8d10, 0x8d11, 0x8d12, 0x8d13, 0x8d14, 0x8d15, 0x8d16, 0x8d17,
        0x8d18, 0x8d19, 0x8d1a, 0x8d1b, 0x8d1c, 0x8d1d, 0x8d1e,
+       /* Lynx Point (9 series) PCH */
+       0x8c90, 0x8c92, 0x8c94, 0x8c96, 0x8c98, 0x8c9a, 0x8c9c, 0x8c9e,
 };
 
 static bool pci_quirk_intel_pch_acs_match(struct pci_dev *dev)
index 4fd0cacf7ca0ae0dfaebf5c612f457cdf6fa43f9..508cc56130e3f88d1b01716a7a00fead250fdf1c 100644 (file)
@@ -428,16 +428,19 @@ static void __assign_resources_sorted(struct list_head *head,
                 * consistent.
                 */
                if (add_align > dev_res->res->start) {
+                       resource_size_t r_size = resource_size(dev_res->res);
+
                        dev_res->res->start = add_align;
-                       dev_res->res->end = add_align +
-                                           resource_size(dev_res->res);
+                       dev_res->res->end = add_align + r_size - 1;
 
                        list_for_each_entry(dev_res2, head, list) {
                                align = pci_resource_alignment(dev_res2->dev,
                                                               dev_res2->res);
-                               if (add_align > align)
+                               if (add_align > align) {
                                        list_move_tail(&dev_res->list,
                                                       &dev_res2->list);
+                                       break;
+                               }
                        }
                }
 
index 7e1304d2e389c0e3341bf324738214896701bb2d..dfbab61a1b473d72cb6e631c6738d1c60fb99d96 100644 (file)
@@ -108,8 +108,7 @@ static void pci_vc_enable(struct pci_dev *dev, int pos, int res)
        struct pci_dev *link = NULL;
 
        /* Enable VCs from the downstream device */
-       if (pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT ||
-           pci_pcie_type(dev) == PCI_EXP_TYPE_DOWNSTREAM)
+       if (!dev->has_secondary_link)
                return;
 
        ctrl_pos = pos + PCI_VC_RES_CTRL + (res * PCI_CAP_VC_PER_VC_SIZEOF);
index 7cfd2db02deb3c5502227676a9a99154244b18e4..240f388720857f0c1e3df0635d35fa71e6f05787 100644 (file)
@@ -446,9 +446,15 @@ static int pcifront_scan_root(struct pcifront_device *pdev,
                                 unsigned int domain, unsigned int bus)
 {
        struct pci_bus *b;
+       LIST_HEAD(resources);
        struct pcifront_sd *sd = NULL;
        struct pci_bus_entry *bus_entry = NULL;
        int err = 0;
+       static struct resource busn_res = {
+               .start = 0,
+               .end = 255,
+               .flags = IORESOURCE_BUS,
+       };
 
 #ifndef CONFIG_PCI_DOMAINS
        if (domain != 0) {
@@ -470,17 +476,21 @@ static int pcifront_scan_root(struct pcifront_device *pdev,
                err = -ENOMEM;
                goto err_out;
        }
+       pci_add_resource(&resources, &ioport_resource);
+       pci_add_resource(&resources, &iomem_resource);
+       pci_add_resource(&resources, &busn_res);
        pcifront_init_sd(sd, domain, bus, pdev);
 
        pci_lock_rescan_remove();
 
-       b = pci_scan_bus_parented(&pdev->xdev->dev, bus,
-                                 &pcifront_bus_ops, sd);
+       b = pci_scan_root_bus(&pdev->xdev->dev, bus,
+                                 &pcifront_bus_ops, sd, &resources);
        if (!b) {
                dev_err(&pdev->xdev->dev,
                        "Error creating PCI Frontend Bus!\n");
                err = -ENOMEM;
                pci_unlock_rescan_remove();
+               pci_free_resource_list(&resources);
                goto err_out;
        }
 
@@ -488,7 +498,7 @@ static int pcifront_scan_root(struct pcifront_device *pdev,
 
        list_add(&bus_entry->list, &pdev->root_buses);
 
-       /* pci_scan_bus_parented skips devices which do not have a have
+       /* pci_scan_root_bus skips devices which do not have a
        * devfn==0. The pcifront_scan_bus enumerates all devfn. */
        err = pcifront_scan_bus(pdev, domain, bus, b);
 
index 64d0515b76bd5ade5136b46a566f78d9c23ba831..55ef7d1fd8da139caae507d928dd9f0b6fc70373 100644 (file)
@@ -94,8 +94,7 @@ static void __iomem *set_cis_map(struct pcmcia_socket *s,
                mem->res = pcmcia_find_mem_region(0, s->map_size,
                                                s->map_size, 0, s);
                if (mem->res == NULL) {
-                       dev_printk(KERN_NOTICE, &s->dev,
-                                  "cs: unable to map card memory!\n");
+                       dev_notice(&s->dev, "cs: unable to map card memory!\n");
                        return NULL;
                }
                s->cis_virt = NULL;
@@ -381,8 +380,7 @@ int verify_cis_cache(struct pcmcia_socket *s)
 
        buf = kmalloc(256, GFP_KERNEL);
        if (buf == NULL) {
-               dev_printk(KERN_WARNING, &s->dev,
-                          "no memory for verifying CIS\n");
+               dev_warn(&s->dev, "no memory for verifying CIS\n");
                return -ENOMEM;
        }
        mutex_lock(&s->ops_mutex);
@@ -414,14 +412,14 @@ int pcmcia_replace_cis(struct pcmcia_socket *s,
                       const u8 *data, const size_t len)
 {
        if (len > CISTPL_MAX_CIS_SIZE) {
-               dev_printk(KERN_WARNING, &s->dev, "replacement CIS too big\n");
+               dev_warn(&s->dev, "replacement CIS too big\n");
                return -EINVAL;
        }
        mutex_lock(&s->ops_mutex);
        kfree(s->fake_cis);
        s->fake_cis = kmalloc(len, GFP_KERNEL);
        if (s->fake_cis == NULL) {
-               dev_printk(KERN_WARNING, &s->dev, "no memory to replace CIS\n");
+               dev_warn(&s->dev, "no memory to replace CIS\n");
                mutex_unlock(&s->ops_mutex);
                return -ENOMEM;
        }
@@ -434,17 +432,17 @@ int pcmcia_replace_cis(struct pcmcia_socket *s,
 
 /* The high-level CIS tuple services */
 
-typedef struct tuple_flags {
+struct tuple_flags {
        u_int           link_space:4;
        u_int           has_link:1;
        u_int           mfc_fn:3;
        u_int           space:4;
-} tuple_flags;
+};
 
-#define LINK_SPACE(f)  (((tuple_flags *)(&(f)))->link_space)
-#define HAS_LINK(f)    (((tuple_flags *)(&(f)))->has_link)
-#define MFC_FN(f)      (((tuple_flags *)(&(f)))->mfc_fn)
-#define SPACE(f)       (((tuple_flags *)(&(f)))->space)
+#define LINK_SPACE(f)  (((struct tuple_flags *)(&(f)))->link_space)
+#define HAS_LINK(f)    (((struct tuple_flags *)(&(f)))->has_link)
+#define MFC_FN(f)      (((struct tuple_flags *)(&(f)))->mfc_fn)
+#define SPACE(f)       (((struct tuple_flags *)(&(f)))->space)
 
 int pccard_get_first_tuple(struct pcmcia_socket *s, unsigned int function,
                        tuple_t *tuple)
@@ -1451,26 +1449,16 @@ int pccard_validate_cis(struct pcmcia_socket *s, unsigned int *info)
 done:
        /* invalidate CIS cache on failure */
        if (!dev_ok || !ident_ok || !count) {
-#if defined(CONFIG_MTD_PCMCIA_ANONYMOUS)
-               /* Set up as an anonymous card. If we don't have anonymous
-                  memory support then just error the card as there is no
-                  point trying to second guess.
-
-                  Note: some cards have just a device entry, it may be
-                  worth extending support to cover these in future */
-               if (!dev_ok || !ident_ok) {
-                       dev_info(&s->dev, "no CIS, assuming an anonymous memory card.\n");
-                       pcmcia_replace_cis(s, "\xFF", 1);
-                       count = 1;
-                       ret = 0;
-               } else
-#endif
-               {
-                       mutex_lock(&s->ops_mutex);
-                       destroy_cis_cache(s);
-                       mutex_unlock(&s->ops_mutex);
+               mutex_lock(&s->ops_mutex);
+               destroy_cis_cache(s);
+               mutex_unlock(&s->ops_mutex);
+               /* We differentiate between dev_ok, ident_ok and count
+                  failures to allow for an override for anonymous cards
+                  in ds.c */
+               if (!dev_ok || !ident_ok)
                        ret = -EIO;
-               }
+               else
+                       ret = -EFAULT;
        }
 
        if (info)
index 5292db69c426cb31df6e19004edaaf282ee0bc49..8007bfda720a673604a57cc8c476f9575607bbec 100644 (file)
@@ -177,8 +177,8 @@ int pcmcia_register_socket(struct pcmcia_socket *socket)
 
        wait_for_completion(&socket->thread_done);
        if (!socket->thread) {
-               dev_printk(KERN_WARNING, &socket->dev,
-                          "PCMCIA: warning: socket thread did not start\n");
+               dev_warn(&socket->dev,
+                        "PCMCIA: warning: socket thread did not start\n");
                return -EIO;
        }
 
@@ -275,7 +275,7 @@ static int socket_reset(struct pcmcia_socket *skt)
                msleep(unreset_check * 10);
        }
 
-       dev_printk(KERN_ERR, &skt->dev, "time out after reset.\n");
+       dev_err(&skt->dev, "time out after reset\n");
        return -ETIMEDOUT;
 }
 
@@ -325,8 +325,8 @@ static void socket_shutdown(struct pcmcia_socket *s)
 
        s->ops->get_status(s, &status);
        if (status & SS_POWERON) {
-               dev_printk(KERN_ERR, &s->dev,
-                          "*** DANGER *** unable to remove socket power\n");
+               dev_err(&s->dev,
+                       "*** DANGER *** unable to remove socket power\n");
        }
 
        s->state &= ~SOCKET_INUSE;
@@ -356,15 +356,13 @@ static int socket_setup(struct pcmcia_socket *skt, int initial_delay)
        }
 
        if (status & SS_PENDING) {
-               dev_printk(KERN_ERR, &skt->dev,
-                          "voltage interrogation timed out.\n");
+               dev_err(&skt->dev, "voltage interrogation timed out\n");
                return -ETIMEDOUT;
        }
 
        if (status & SS_CARDBUS) {
                if (!(skt->features & SS_CAP_CARDBUS)) {
-                       dev_printk(KERN_ERR, &skt->dev,
-                               "cardbus cards are not supported.\n");
+                       dev_err(&skt->dev, "cardbus cards are not supported\n");
                        return -EINVAL;
                }
                skt->state |= SOCKET_CARDBUS;
@@ -379,7 +377,7 @@ static int socket_setup(struct pcmcia_socket *skt, int initial_delay)
        else if (!(status & SS_XVCARD))
                skt->socket.Vcc = skt->socket.Vpp = 50;
        else {
-               dev_printk(KERN_ERR, &skt->dev, "unsupported voltage key.\n");
+               dev_err(&skt->dev, "unsupported voltage key\n");
                return -EIO;
        }
 
@@ -396,7 +394,7 @@ static int socket_setup(struct pcmcia_socket *skt, int initial_delay)
 
        skt->ops->get_status(skt, &status);
        if (!(status & SS_POWERON)) {
-               dev_printk(KERN_ERR, &skt->dev, "unable to apply power.\n");
+               dev_err(&skt->dev, "unable to apply power\n");
                return -EIO;
        }
 
@@ -429,8 +427,7 @@ static int socket_insert(struct pcmcia_socket *skt)
        if (ret == 0) {
                skt->state |= SOCKET_PRESENT;
 
-               dev_printk(KERN_NOTICE, &skt->dev,
-                          "pccard: %s card inserted into slot %d\n",
+               dev_notice(&skt->dev, "pccard: %s card inserted into slot %d\n",
                           (skt->state & SOCKET_CARDBUS) ? "CardBus" : "PCMCIA",
                           skt->sock);
 
@@ -558,8 +555,7 @@ static int socket_resume(struct pcmcia_socket *skt)
 
 static void socket_remove(struct pcmcia_socket *skt)
 {
-       dev_printk(KERN_NOTICE, &skt->dev,
-                  "pccard: card ejected from slot %d\n", skt->sock);
+       dev_notice(&skt->dev, "pccard: card ejected from slot %d\n", skt->sock);
        socket_shutdown(skt);
 }
 
@@ -605,8 +601,7 @@ static int pccardd(void *__skt)
        /* register with the device core */
        ret = device_register(&skt->dev);
        if (ret) {
-               dev_printk(KERN_WARNING, &skt->dev,
-                          "PCMCIA: unable to register socket\n");
+               dev_warn(&skt->dev, "PCMCIA: unable to register socket\n");
                skt->thread = NULL;
                complete(&skt->thread_done);
                return 0;
index d3baf0bfca9f0692f9a2789465f727c911e0f7f2..0decee6c556e88c8f9bb30fd6f604f69c89d50f0 100644 (file)
@@ -81,8 +81,8 @@ static void pcmcia_check_driver(struct pcmcia_driver *p_drv)
 
 
 struct pcmcia_dynid {
-       struct list_head                node;
-       struct pcmcia_device_id         id;
+       struct list_head                node;
+       struct pcmcia_device_id         id;
 };
 
 /**
@@ -284,8 +284,8 @@ static int pcmcia_device_probe(struct device *dev)
                dev_dbg(dev, "base %x, regs %x", p_dev->config_base,
                        p_dev->config_regs);
        } else {
-               dev_printk(KERN_INFO, dev,
-                          "pcmcia: could not parse base and rmask0 of CIS\n");
+               dev_info(dev,
+                        "pcmcia: could not parse base and rmask0 of CIS\n");
                p_dev->config_base = 0;
                p_dev->config_regs = 0;
        }
@@ -382,15 +382,15 @@ static int pcmcia_device_remove(struct device *dev)
 
        /* check for proper unloading */
        if (p_dev->_irq || p_dev->_io || p_dev->_locked)
-               dev_printk(KERN_INFO, dev,
-                       "pcmcia: driver %s did not release config properly\n",
-                       p_drv->name);
+               dev_info(dev,
+                        "pcmcia: driver %s did not release config properly\n",
+                        p_drv->name);
 
        for (i = 0; i < MAX_WIN; i++)
                if (p_dev->_win & CLIENT_WIN_REQ(i))
-                       dev_printk(KERN_INFO, dev,
-                         "pcmcia: driver %s did not release window properly\n",
-                          p_drv->name);
+                       dev_info(dev,
+                                "pcmcia: driver %s did not release window properly\n",
+                                p_drv->name);
 
        /* references from pcmcia_probe_device */
        pcmcia_put_dev(p_dev);
@@ -566,7 +566,7 @@ static struct pcmcia_device *pcmcia_device_add(struct pcmcia_socket *s,
                        c->io[i].name = p_dev->devname;
                        c->io[i].flags = IORESOURCE_IO;
                }
-               for (i = 0; i< MAX_WIN; i++) {
+               for (i = 0; i < MAX_WIN; i++) {
                        c->mem[i].name = p_dev->devname;
                        c->mem[i].flags = IORESOURCE_MEM;
                }
@@ -578,8 +578,7 @@ static struct pcmcia_device *pcmcia_device_add(struct pcmcia_socket *s,
 
        mutex_unlock(&s->ops_mutex);
 
-       dev_printk(KERN_NOTICE, &p_dev->dev,
-                  "pcmcia: registering new device %s (IRQ: %d)\n",
+       dev_notice(&p_dev->dev, "pcmcia: registering new device %s (IRQ: %d)\n",
                   p_dev->devname, p_dev->irq);
 
        pcmcia_device_query(p_dev);
@@ -634,8 +633,24 @@ static int pcmcia_card_add(struct pcmcia_socket *s)
 
        ret = pccard_validate_cis(s, &no_chains);
        if (ret || !no_chains) {
-               dev_dbg(&s->dev, "invalid CIS or invalid resources\n");
-               return -ENODEV;
+#if defined(CONFIG_MTD_PCMCIA_ANONYMOUS)
+               /* Set up as an anonymous card. If we don't have anonymous
+                  memory support then just error the card as there is no
+                  point trying to second guess.
+
+                  Note: some cards have just a device entry, it may be
+                  worth extending support to cover these in future */
+               if (ret == -EIO) {
+                       dev_info(&s->dev, "no CIS, assuming an anonymous memory card.\n");
+                       pcmcia_replace_cis(s, "\xFF", 1);
+                       no_chains = 1;
+                       ret = 0;
+               } else
+#endif
+               {
+                       dev_dbg(&s->dev, "invalid CIS or invalid resources\n");
+                       return -ENODEV;
+               }
        }
 
        if (!pccard_read_tuple(s, BIND_FN_ALL, CISTPL_LONGLINK_MFC, &mfc))
@@ -651,7 +666,7 @@ static int pcmcia_card_add(struct pcmcia_socket *s)
 }
 
 
-static int pcmcia_requery_callback(struct device *dev, void * _data)
+static int pcmcia_requery_callback(struct device *dev, void *_data)
 {
        struct pcmcia_device *p_dev = to_pcmcia_dev(dev);
        if (!p_dev->dev.driver) {
@@ -729,7 +744,7 @@ static void pcmcia_requery(struct pcmcia_socket *s)
  * the one provided by the card is broken. The firmware files reside in
  * /lib/firmware/ in userspace.
  */
-static int pcmcia_load_firmware(struct pcmcia_device *dev, char * filename)
+static int pcmcia_load_firmware(struct pcmcia_device *dev, char *filename)
 {
        struct pcmcia_socket *s = dev->socket;
        const struct firmware *fw;
@@ -745,16 +760,14 @@ static int pcmcia_load_firmware(struct pcmcia_device *dev, char * filename)
        if (request_firmware(&fw, filename, &dev->dev) == 0) {
                if (fw->size >= CISTPL_MAX_CIS_SIZE) {
                        ret = -EINVAL;
-                       dev_printk(KERN_ERR, &dev->dev,
-                                  "pcmcia: CIS override is too big\n");
+                       dev_err(&dev->dev, "pcmcia: CIS override is too big\n");
                        goto release;
                }
 
                if (!pcmcia_replace_cis(s, fw->data, fw->size))
                        ret = 0;
                else {
-                       dev_printk(KERN_ERR, &dev->dev,
-                                  "pcmcia: CIS override failed\n");
+                       dev_err(&dev->dev, "pcmcia: CIS override failed\n");
                        goto release;
                }
 
@@ -781,7 +794,8 @@ static int pcmcia_load_firmware(struct pcmcia_device *dev, char * filename)
 
 #else /* !CONFIG_PCMCIA_LOAD_CIS */
 
-static inline int pcmcia_load_firmware(struct pcmcia_device *dev, char * filename)
+static inline int pcmcia_load_firmware(struct pcmcia_device *dev,
+                                      char *filename)
 {
        return -ENODEV;
 }
@@ -1148,10 +1162,9 @@ static int pcmcia_dev_suspend(struct device *dev, pm_message_t state)
        if (p_drv->suspend) {
                ret = p_drv->suspend(p_dev);
                if (ret) {
-                       dev_printk(KERN_ERR, dev,
-                                  "pcmcia: device %s (driver %s) did "
-                                  "not want to go to sleep (%d)\n",
-                                  p_dev->devname, p_drv->name, ret);
+                       dev_err(dev,
+                               "pcmcia: device %s (driver %s) did not want to go to sleep (%d)\n",
+                               p_dev->devname, p_drv->name, ret);
                        mutex_lock(&p_dev->socket->ops_mutex);
                        p_dev->suspended = 0;
                        mutex_unlock(&p_dev->socket->ops_mutex);
@@ -1206,7 +1219,7 @@ static int pcmcia_dev_resume(struct device *dev)
 }
 
 
-static int pcmcia_bus_suspend_callback(struct device *dev, void * _data)
+static int pcmcia_bus_suspend_callback(struct device *dev, void *_data)
 {
        struct pcmcia_socket *skt = _data;
        struct pcmcia_device *p_dev = to_pcmcia_dev(dev);
@@ -1217,7 +1230,7 @@ static int pcmcia_bus_suspend_callback(struct device *dev, void * _data)
        return runtime_suspend(dev);
 }
 
-static int pcmcia_bus_resume_callback(struct device *dev, void * _data)
+static int pcmcia_bus_resume_callback(struct device *dev, void *_data)
 {
        struct pcmcia_socket *skt = _data;
        struct pcmcia_device *p_dev = to_pcmcia_dev(dev);
@@ -1342,14 +1355,13 @@ static int pcmcia_bus_add_socket(struct device *dev,
 
        socket = pcmcia_get_socket(socket);
        if (!socket) {
-               dev_printk(KERN_ERR, dev,
-                          "PCMCIA obtaining reference to socket failed\n");
+               dev_err(dev, "PCMCIA obtaining reference to socket failed\n");
                return -ENODEV;
        }
 
        ret = sysfs_create_bin_file(&dev->kobj, &pccard_cis_attr);
        if (ret) {
-               dev_printk(KERN_ERR, dev, "PCMCIA registration failed\n");
+               dev_err(dev, "PCMCIA registration failed\n");
                pcmcia_put_socket(socket);
                return ret;
        }
@@ -1361,7 +1373,7 @@ static int pcmcia_bus_add_socket(struct device *dev,
 
        ret = pccard_register_pcmcia(socket, &pcmcia_bus_callback);
        if (ret) {
-               dev_printk(KERN_ERR, dev, "PCMCIA registration failed\n");
+               dev_err(dev, "PCMCIA registration failed\n");
                pcmcia_put_socket(socket);
                return ret;
        }
index 7f9950d324df037772f605e3f47a021269858411..61cf61ac621ebc8d253dd59afc36eda535378a67 100644 (file)
@@ -48,14 +48,14 @@ struct electra_cf_socket {
 
        struct platform_device  *ofdev;
        unsigned long           mem_phys;
-       void __iomem *          mem_base;
+       void __iomem            *mem_base;
        unsigned long           mem_size;
-       void __iomem *          io_virt;
+       void __iomem            *io_virt;
        unsigned int            io_base;
        unsigned int            io_size;
        u_int                   irq;
        struct resource         iomem;
-       void __iomem *          gpio_base;
+       void __iomem            *gpio_base;
        int                     gpio_detect;
        int                     gpio_vsense;
        int                     gpio_3v;
@@ -202,7 +202,7 @@ static int electra_cf_probe(struct platform_device *ofdev)
        if (err)
                return -EINVAL;
 
-       cf = kzalloc(sizeof *cf, GFP_KERNEL);
+       cf = kzalloc(sizeof(*cf), GFP_KERNEL);
        if (!cf)
                return -ENOMEM;
 
@@ -216,8 +216,10 @@ static int electra_cf_probe(struct platform_device *ofdev)
        cf->io_size = PAGE_ALIGN(resource_size(&io));
 
        area = __get_vm_area(cf->io_size, 0, PHB_IO_BASE, PHB_IO_END);
-       if (area == NULL)
-               return -ENOMEM;
+       if (area == NULL) {
+               status = -ENOMEM;
+               goto fail1;
+       }
 
        cf->io_virt = (void __iomem *)(area->addr);
 
@@ -320,7 +322,8 @@ fail1:
                iounmap(cf->mem_base);
        if (cf->gpio_base)
                iounmap(cf->gpio_base);
-       device_init_wakeup(&ofdev->dev, 0);
+       if (area)
+               device_init_wakeup(&ofdev->dev, 0);
        kfree(cf);
        return status;
 
@@ -369,5 +372,5 @@ static struct platform_driver electra_cf_driver = {
 module_platform_driver(electra_cf_driver);
 
 MODULE_LICENSE("GPL");
-MODULE_AUTHOR ("Olof Johansson <olof@lixom.net>");
+MODULE_AUTHOR("Olof Johansson <olof@lixom.net>");
 MODULE_DESCRIPTION("PA Semi Electra CF driver");
index a2c138719bac80907a86b2c3e70c871af8283f51..eb0d80a429e468ce81c7ffd9d4cb481985c08e07 100644 (file)
@@ -132,14 +132,14 @@ module_param(recov_time, int, 0444);
 
 /*====================================================================*/
 
-typedef struct cirrus_state_t {
+struct cirrus_state {
     u_char             misc1, misc2;
     u_char             timer[6];
-} cirrus_state_t;
+};
 
-typedef struct vg46x_state_t {
+struct vg46x_state {
     u_char             ctl, ema;
-} vg46x_state_t;
+};
 
 struct i82365_socket {
     u_short            type, flags;
@@ -149,8 +149,8 @@ struct i82365_socket {
     u_short            psock;
     u_char             cs_irq, intr;
     union {
-       cirrus_state_t          cirrus;
-       vg46x_state_t           vg46x;
+       struct cirrus_state             cirrus;
+       struct vg46x_state              vg46x;
     } state;
 };
 
@@ -173,11 +173,11 @@ static struct timer_list poll_timer;
 /*====================================================================*/
 
 /* These definitions must match the pcic table! */
-typedef enum pcic_id {
+enum pcic_id {
     IS_I82365A, IS_I82365B, IS_I82365DF,
     IS_IBM, IS_RF5Cx96, IS_VLSI, IS_VG468, IS_VG469,
     IS_PD6710, IS_PD672X, IS_VT83C469,
-} pcic_id;
+};
 
 /* Flags for classifying groups of controllers */
 #define IS_VADEM       0x0001
@@ -189,12 +189,12 @@ typedef enum pcic_id {
 #define IS_REGISTERED  0x2000
 #define IS_ALIVE       0x8000
 
-typedef struct pcic_t {
+struct pcic {
     char               *name;
     u_short            flags;
-} pcic_t;
+};
 
-static pcic_t pcic[] = {
+static struct pcic pcic[] = {
     { "Intel i82365sl A step", 0 },
     { "Intel i82365sl B step", 0 },
     { "Intel i82365sl DF", IS_DF_PWR },
@@ -208,7 +208,7 @@ static pcic_t pcic[] = {
     { "VIA VT83C469", IS_CIRRUS|IS_VIA },
 };
 
-#define PCIC_COUNT     (sizeof(pcic)/sizeof(pcic_t))
+#define PCIC_COUNT     ARRAY_SIZE(pcic)
 
 /*====================================================================*/
 
@@ -294,7 +294,7 @@ static void i365_set_pair(u_short sock, u_short reg, u_short data)
 static void cirrus_get_state(u_short s)
 {
     int i;
-    cirrus_state_t *p = &socket[s].state.cirrus;
+    struct cirrus_state *p = &socket[s].state.cirrus;
     p->misc1 = i365_get(s, PD67_MISC_CTL_1);
     p->misc1 &= (PD67_MC1_MEDIA_ENA | PD67_MC1_INPACK_ENA);
     p->misc2 = i365_get(s, PD67_MISC_CTL_2);
@@ -306,7 +306,7 @@ static void cirrus_set_state(u_short s)
 {
     int i;
     u_char misc;
-    cirrus_state_t *p = &socket[s].state.cirrus;
+    struct cirrus_state *p = &socket[s].state.cirrus;
 
     misc = i365_get(s, PD67_MISC_CTL_2);
     i365_set(s, PD67_MISC_CTL_2, p->misc2);
@@ -321,7 +321,7 @@ static void cirrus_set_state(u_short s)
 static u_int __init cirrus_set_opts(u_short s, char *buf)
 {
     struct i82365_socket *t = &socket[s];
-    cirrus_state_t *p = &socket[s].state.cirrus;
+    struct cirrus_state *p = &socket[s].state.cirrus;
     u_int mask = 0xffff;
 
     if (has_ring == -1) has_ring = 1;
@@ -377,7 +377,7 @@ static u_int __init cirrus_set_opts(u_short s, char *buf)
 
 static void vg46x_get_state(u_short s)
 {
-    vg46x_state_t *p = &socket[s].state.vg46x;
+    struct vg46x_state *p = &socket[s].state.vg46x;
     p->ctl = i365_get(s, VG468_CTL);
     if (socket[s].type == IS_VG469)
        p->ema = i365_get(s, VG469_EXT_MODE);
@@ -385,7 +385,7 @@ static void vg46x_get_state(u_short s)
 
 static void vg46x_set_state(u_short s)
 {
-    vg46x_state_t *p = &socket[s].state.vg46x;
+    struct vg46x_state *p = &socket[s].state.vg46x;
     i365_set(s, VG468_CTL, p->ctl);
     if (socket[s].type == IS_VG469)
        i365_set(s, VG469_EXT_MODE, p->ema);
@@ -393,7 +393,7 @@ static void vg46x_set_state(u_short s)
 
 static u_int __init vg46x_set_opts(u_short s, char *buf)
 {
-    vg46x_state_t *p = &socket[s].state.vg46x;
+    struct vg46x_state *p = &socket[s].state.vg46x;
     
     flip(p->ctl, VG468_CTL_ASYNC, async_clock);
     flip(p->ema, VG469_MODE_CABLE, cable_mode);
@@ -1285,13 +1285,6 @@ static int __init init_i82365(void)
            ret = pcmcia_register_socket(&socket[i].socket);
            if (!ret)
                    socket[i].flags |= IS_REGISTERED;
-
-#if 0 /* driver model ordering issue */
-          class_device_create_file(&socket[i].socket.dev,
-                                   &class_device_attr_info);
-          class_device_create_file(&socket[i].socket.dev,
-                                   &class_device_attr_exca);
-#endif
     }
 
     /* Finally, schedule a polling interrupt */
index 0075bd7162ed1d3c5bd3816b2cb447a5b3b3e34f..70b089430fcc45ace3f3698d3282c7cae427303c 100644 (file)
@@ -754,13 +754,6 @@ static int __init init_m32r_pcc(void)
                ret = pcmcia_register_socket(&socket[i].socket);
                if (!ret)
                        socket[i].flags |= IS_REGISTERED;
-
-#if 0  /* driver model ordering issue */
-               class_device_create_file(&socket[i].socket.dev,
-                                        &class_device_attr_info);
-               class_device_create_file(&socket[i].socket.dev,
-                                        &class_device_attr_exca);
-#endif
        }
 
        /* Finally, schedule a polling interrupt */
index a77e571b08b8ffe4974ba5199336028bf45a7884..eb126b98ed8a3d42cf83566c30c53546896f4c54 100644 (file)
@@ -716,13 +716,6 @@ static int __init init_m32r_pcc(void)
                ret = pcmcia_register_socket(&socket[i].socket);
                if (!ret)
                        socket[i].flags |= IS_REGISTERED;
-
-#if 0  /* driver model ordering issue */
-               class_device_create_file(&socket[i].socket.dev,
-                                        &class_device_attr_info);
-               class_device_create_file(&socket[i].socket.dev,
-                                        &class_device_attr_exca);
-#endif
        }
 
        /* Finally, schedule a polling interrupt */
index e2c92415b8924a89c27e6759652b4a7e5726139b..1c05d74e850dd9d9030ad2f1cbb884e3241dc2c2 100644 (file)
@@ -44,7 +44,7 @@ int pccard_read_tuple(struct pcmcia_socket *s, unsigned int function,
 
        buf = kmalloc(256, GFP_KERNEL);
        if (buf == NULL) {
-               dev_printk(KERN_WARNING, &s->dev, "no memory to read tuple\n");
+               dev_warn(&s->dev, "no memory to read tuple\n");
                return -ENOMEM;
        }
        tuple.DesiredTuple = code;
@@ -94,7 +94,7 @@ int pccard_loop_tuple(struct pcmcia_socket *s, unsigned int function,
 
        buf = kzalloc(256, GFP_KERNEL);
        if (buf == NULL) {
-               dev_printk(KERN_WARNING, &s->dev, "no memory to read tuple\n");
+               dev_warn(&s->dev, "no memory to read tuple\n");
                return -ENOMEM;
        }
 
index e8c19def1b0f501863008b3dc892d18bd4cdbe5c..34aad895a2395afc09f00ef9330a3447d0aa64e9 100644 (file)
@@ -508,8 +508,7 @@ int pcmcia_enable_device(struct pcmcia_device *p_dev)
        s->socket.Vpp = p_dev->vpp;
        if (s->ops->set_socket(s, &s->socket)) {
                mutex_unlock(&s->ops_mutex);
-               dev_printk(KERN_WARNING, &p_dev->dev,
-                          "Unable to set socket state\n");
+               dev_warn(&p_dev->dev, "Unable to set socket state\n");
                return -EINVAL;
        }
 
@@ -736,13 +735,11 @@ __pcmcia_request_exclusive_irq(struct pcmcia_device *p_dev,
        ret = request_irq(p_dev->irq, handler, 0, p_dev->devname, p_dev->priv);
        if (ret) {
                ret = pcmcia_request_irq(p_dev, handler);
-               dev_printk(KERN_WARNING, &p_dev->dev, "pcmcia: "
-                       "request for exclusive IRQ could not be fulfilled.\n");
-               dev_printk(KERN_WARNING, &p_dev->dev, "pcmcia: the driver "
-                       "needs updating to supported shared IRQ lines.\n");
+               dev_warn(&p_dev->dev, "pcmcia: request for exclusive IRQ could not be fulfilled\n");
+               dev_warn(&p_dev->dev, "pcmcia: the driver needs updating to supported shared IRQ lines\n");
        }
        if (ret)
-               dev_printk(KERN_INFO, &p_dev->dev, "request_irq() failed\n");
+               dev_info(&p_dev->dev, "request_irq() failed\n");
        else
                p_dev->_irq = 1;
 
index 065704c605d5230275b3dbf2d2c0ce1decc3818e..5ef7b46a25786b30f214dfc5a2e93cbc07f79eea 100644 (file)
@@ -191,15 +191,13 @@ static void do_io_probe(struct pcmcia_socket *s, unsigned int base,
        int any;
        u_char *b, hole, most;
 
-       dev_printk(KERN_INFO, &s->dev, "cs: IO port probe %#x-%#x:",
-               base, base+num-1);
+       dev_info(&s->dev, "cs: IO port probe %#x-%#x:", base, base+num-1);
 
        /* First, what does a floating port look like? */
        b = kzalloc(256, GFP_KERNEL);
        if (!b) {
-               printk("\n");
-               dev_printk(KERN_ERR, &s->dev,
-                       "do_io_probe: unable to kmalloc 256 bytes");
+               pr_cont("\n");
+               dev_err(&s->dev, "do_io_probe: unable to kmalloc 256 bytes\n");
                return;
        }
        for (i = base, most = 0; i < base+num; i += 8) {
@@ -223,7 +221,7 @@ static void do_io_probe(struct pcmcia_socket *s, unsigned int base,
                res = claim_region(s, i, 8, IORESOURCE_IO, "PCMCIA ioprobe");
                if (!res) {
                        if (!any)
-                               printk(" excluding");
+                               pr_cont(" excluding");
                        if (!bad)
                                bad = any = i;
                        continue;
@@ -234,13 +232,13 @@ static void do_io_probe(struct pcmcia_socket *s, unsigned int base,
                free_region(res);
                if (j < 8) {
                        if (!any)
-                               printk(" excluding");
+                               pr_cont(" excluding");
                        if (!bad)
                                bad = any = i;
                } else {
                        if (bad) {
                                sub_interval(&s_data->io_db, bad, i-bad);
-                               printk(" %#x-%#x", bad, i-1);
+                               pr_cont(" %#x-%#x", bad, i-1);
                                bad = 0;
                        }
                }
@@ -248,15 +246,15 @@ static void do_io_probe(struct pcmcia_socket *s, unsigned int base,
        if (bad) {
                if ((num > 16) && (bad == base) && (i == base+num)) {
                        sub_interval(&s_data->io_db, bad, i-bad);
-                       printk(" nothing: probe failed.\n");
+                       pr_cont(" nothing: probe failed.\n");
                        return;
                } else {
                        sub_interval(&s_data->io_db, bad, i-bad);
-                       printk(" %#x-%#x", bad, i-1);
+                       pr_cont(" %#x-%#x", bad, i-1);
                }
        }
 
-       printk(any ? "\n" : " clean.\n");
+       pr_cont("%s\n", !any ? " clean" : "");
 }
 #endif
 
@@ -413,8 +411,8 @@ static int do_mem_probe(struct pcmcia_socket *s, u_long base, u_long num,
        struct socket_data *s_data = s->resource_data;
        u_long i, j, bad, fail, step;
 
-       dev_printk(KERN_INFO, &s->dev, "cs: memory probe 0x%06lx-0x%06lx:",
-               base, base+num-1);
+       dev_info(&s->dev, "cs: memory probe 0x%06lx-0x%06lx:",
+                base, base+num-1);
        bad = fail = 0;
        step = (num < 0x20000) ? 0x2000 : ((num>>4) & ~0x1fff);
        /* don't allow too large steps */
@@ -438,13 +436,13 @@ static int do_mem_probe(struct pcmcia_socket *s, u_long base, u_long num,
                }
                if (i != j) {
                        if (!bad)
-                               printk(" excluding");
-                       printk(" %#05lx-%#05lx", i, j-1);
+                               pr_cont(" excluding");
+                       pr_cont(" %#05lx-%#05lx", i, j-1);
                        sub_interval(&s_data->mem_db, i, j-i);
                        bad += j-i;
                }
        }
-       printk(bad ? "\n" : " clean.\n");
+       pr_cont("%s\n", !bad ? " clean" : "");
        return num - bad;
 }
 
@@ -495,7 +493,7 @@ static int validate_mem(struct pcmcia_socket *s, unsigned int probe_mask)
                        return 0;
                if (s_data->mem_db_valid.next != &s_data->mem_db_valid)
                        return 0;
-               dev_printk(KERN_NOTICE, &s->dev,
+               dev_notice(&s->dev,
                           "cs: warning: no high memory space available!\n");
                return -ENODEV;
        }
@@ -975,9 +973,9 @@ static int nonstatic_autoadd_resources(struct pcmcia_socket *s)
                        if (res == &ioport_resource)
                                continue;
 
-                       dev_printk(KERN_INFO, &s->cb_dev->dev,
-                                  "pcmcia: parent PCI bridge window: %pR\n",
-                                  res);
+                       dev_info(&s->cb_dev->dev,
+                                "pcmcia: parent PCI bridge window: %pR\n",
+                                res);
                        if (!adjust_io(s, ADD_MANAGED_RESOURCE, res->start, res->end))
                                done |= IORESOURCE_IO;
 
@@ -990,9 +988,9 @@ static int nonstatic_autoadd_resources(struct pcmcia_socket *s)
                        if (res == &iomem_resource)
                                continue;
 
-                       dev_printk(KERN_INFO, &s->cb_dev->dev,
-                                  "pcmcia: parent PCI bridge window: %pR\n",
-                                  res);
+                       dev_info(&s->cb_dev->dev,
+                                "pcmcia: parent PCI bridge window: %pR\n",
+                                res);
                        if (!adjust_memory(s, ADD_MANAGED_RESOURCE, res->start, res->end))
                                done |= IORESOURCE_MEM;
                }
index a71789486cdf0d61cc159332ea22a5b9ae62e502..5cb670e037a0c6d5f5669b3efbfa3f83e5929171 100644 (file)
@@ -372,8 +372,8 @@ static void ti12xx_irqroute_func0(struct yenta_socket *socket)
 
        mfunc = mfunc_old = config_readl(socket, TI122X_MFUNC);
        devctl = config_readb(socket, TI113X_DEVICE_CONTROL);
-       dev_printk(KERN_INFO, &socket->dev->dev,
-                  "TI: mfunc 0x%08x, devctl 0x%02x\n", mfunc, devctl);
+       dev_info(&socket->dev->dev, "TI: mfunc 0x%08x, devctl 0x%02x\n",
+                mfunc, devctl);
 
        /* make sure PCI interrupts are enabled before probing */
        ti_init(socket);
@@ -387,8 +387,8 @@ static void ti12xx_irqroute_func0(struct yenta_socket *socket)
         * We're here which means PCI interrupts are _not_ delivered. try to
         * find the right setting (all serial or parallel)
         */
-       dev_printk(KERN_INFO, &socket->dev->dev,
-                  "TI: probing PCI interrupt failed, trying to fix\n");
+       dev_info(&socket->dev->dev,
+                "TI: probing PCI interrupt failed, trying to fix\n");
 
        /* for serial PCI make sure MFUNC3 is set to IRQSER */
        if ((devctl & TI113X_DCR_IMODE_MASK) == TI12XX_DCR_IMODE_ALL_SERIAL) {
@@ -412,8 +412,8 @@ static void ti12xx_irqroute_func0(struct yenta_socket *socket)
 
                                pci_irq_status = yenta_probe_cb_irq(socket);
                                if (pci_irq_status == 1) {
-                                       dev_printk(KERN_INFO, &socket->dev->dev,
-                                           "TI: all-serial interrupts ok\n");
+                                       dev_info(&socket->dev->dev,
+                                                "TI: all-serial interrupts ok\n");
                                        mfunc_old = mfunc;
                                        goto out;
                                }
@@ -428,8 +428,8 @@ static void ti12xx_irqroute_func0(struct yenta_socket *socket)
                }
 
                /* serial PCI interrupts not working fall back to parallel */
-               dev_printk(KERN_INFO, &socket->dev->dev,
-                          "TI: falling back to parallel PCI interrupts\n");
+               dev_info(&socket->dev->dev,
+                        "TI: falling back to parallel PCI interrupts\n");
                devctl &= ~TI113X_DCR_IMODE_MASK;
                devctl |= TI113X_DCR_IMODE_SERIAL; /* serial ISA could be right */
                config_writeb(socket, TI113X_DEVICE_CONTROL, devctl);
@@ -460,8 +460,7 @@ static void ti12xx_irqroute_func0(struct yenta_socket *socket)
        pci_irq_status = yenta_probe_cb_irq(socket);
        if (pci_irq_status == 1) {
                mfunc_old = mfunc;
-               dev_printk(KERN_INFO, &socket->dev->dev,
-                          "TI: parallel PCI interrupts ok\n");
+               dev_info(&socket->dev->dev, "TI: parallel PCI interrupts ok\n");
        } else {
                /* not working, back to old value */
                mfunc = mfunc_old;
@@ -473,9 +472,8 @@ static void ti12xx_irqroute_func0(struct yenta_socket *socket)
 out:
        if (pci_irq_status < 1) {
                socket->cb_irq = 0;
-               dev_printk(KERN_INFO, &socket->dev->dev,
-                          "Yenta TI: no PCI interrupts. Fish. "
-                          "Please report.\n");
+               dev_info(&socket->dev->dev,
+                        "Yenta TI: no PCI interrupts. Fish. Please report.\n");
        }
 }
 
@@ -547,9 +545,8 @@ static void ti12xx_irqroute_func1(struct yenta_socket *socket)
 
        mfunc = mfunc_old = config_readl(socket, TI122X_MFUNC);
        devctl = config_readb(socket, TI113X_DEVICE_CONTROL);
-       dev_printk(KERN_INFO, &socket->dev->dev,
-                  "TI: mfunc 0x%08x, devctl 0x%02x\n",
-                  mfunc, devctl);
+       dev_info(&socket->dev->dev, "TI: mfunc 0x%08x, devctl 0x%02x\n",
+                mfunc, devctl);
 
        /* if IRQs are configured as tied, align irq of func1 with func0 */
        sysctl = config_readl(socket, TI113X_SYSTEM_CONTROL);
@@ -568,8 +565,8 @@ static void ti12xx_irqroute_func1(struct yenta_socket *socket)
         * We're here which means PCI interrupts are _not_ delivered. try to
         * find the right setting
         */
-       dev_printk(KERN_INFO, &socket->dev->dev,
-                  "TI: probing PCI interrupt failed, trying to fix\n");
+       dev_info(&socket->dev->dev,
+                "TI: probing PCI interrupt failed, trying to fix\n");
 
        /* if all serial: set INTRTIE, probe again */
        if ((devctl & TI113X_DCR_IMODE_MASK) == TI12XX_DCR_IMODE_ALL_SERIAL) {
@@ -578,8 +575,8 @@ static void ti12xx_irqroute_func1(struct yenta_socket *socket)
                if (ti12xx_tie_interrupts(socket, &old_irq)) {
                        pci_irq_status = yenta_probe_cb_irq(socket);
                        if (pci_irq_status == 1) {
-                               dev_printk(KERN_INFO, &socket->dev->dev,
-                                       "TI: all-serial interrupts, tied ok\n");
+                               dev_info(&socket->dev->dev,
+                                        "TI: all-serial interrupts, tied ok\n");
                                goto out;
                        }
 
@@ -616,8 +613,8 @@ static void ti12xx_irqroute_func1(struct yenta_socket *socket)
 
                        pci_irq_status = yenta_probe_cb_irq(socket);
                        if (pci_irq_status == 1) {
-                               dev_printk(KERN_INFO, &socket->dev->dev,
-                                          "TI: parallel PCI interrupts ok\n");
+                               dev_info(&socket->dev->dev,
+                                        "TI: parallel PCI interrupts ok\n");
                                goto out;
                        }
 
@@ -632,8 +629,8 @@ static void ti12xx_irqroute_func1(struct yenta_socket *socket)
                if (ti12xx_tie_interrupts(socket, &old_irq)) {
                        pci_irq_status = yenta_probe_cb_irq(socket);
                        if (pci_irq_status == 1) {
-                               dev_printk(KERN_INFO, &socket->dev->dev,
-                                   "TI: parallel PCI interrupts, tied ok\n");
+                               dev_info(&socket->dev->dev,
+                                        "TI: parallel PCI interrupts, tied ok\n");
                                goto out;
                        }
 
@@ -644,8 +641,8 @@ static void ti12xx_irqroute_func1(struct yenta_socket *socket)
 out:
        if (pci_irq_status < 1) {
                socket->cb_irq = 0;
-               dev_printk(KERN_INFO, &socket->dev->dev,
-                          "TI: no PCI interrupts. Fish. Please report.\n");
+               dev_info(&socket->dev->dev,
+                        "TI: no PCI interrupts. Fish. Please report.\n");
        }
 }
 
@@ -849,13 +846,12 @@ static int ti12xx_override(struct yenta_socket *socket)
        /* make sure that memory burst is active */
        val_orig = val = config_readl(socket, TI113X_SYSTEM_CONTROL);
        if (disable_clkrun && PCI_FUNC(socket->dev->devfn) == 0) {
-               dev_printk(KERN_INFO, &socket->dev->dev,
-                          "Disabling CLKRUN feature\n");
+               dev_info(&socket->dev->dev, "Disabling CLKRUN feature\n");
                val |= TI113X_SCR_KEEPCLK;
        }
        if (!(val & TI122X_SCR_MRBURSTUP)) {
-               dev_printk(KERN_INFO, &socket->dev->dev,
-                          "Enabling burst memory read transactions\n");
+               dev_info(&socket->dev->dev,
+                        "Enabling burst memory read transactions\n");
                val |= TI122X_SCR_MRBURSTUP;
        }
        if (val_orig != val)
@@ -866,12 +862,10 @@ static int ti12xx_override(struct yenta_socket *socket)
         * CSC interrupts to PCI rather than INTVAL.
         */
        val = config_readb(socket, TI1250_DIAGNOSTIC);
-       dev_printk(KERN_INFO, &socket->dev->dev,
-                  "Using %s to route CSC interrupts to PCI\n",
-                  (val & TI1250_DIAG_PCI_CSC) ? "CSCINT" : "INTVAL");
-       dev_printk(KERN_INFO, &socket->dev->dev,
-                  "Routing CardBus interrupts to %s\n",
-                  (val & TI1250_DIAG_PCI_IREQ) ? "PCI" : "ISA");
+       dev_info(&socket->dev->dev, "Using %s to route CSC interrupts to PCI\n",
+                (val & TI1250_DIAG_PCI_CSC) ? "CSCINT" : "INTVAL");
+       dev_info(&socket->dev->dev, "Routing CardBus interrupts to %s\n",
+                (val & TI1250_DIAG_PCI_IREQ) ? "PCI" : "ISA");
 
        /* do irqrouting, depending on function */
        if (PCI_FUNC(socket->dev->devfn) == 0)
@@ -896,9 +890,9 @@ static int ti1250_override(struct yenta_socket *socket)
                diag |= TI1250_DIAG_PCI_CSC | TI1250_DIAG_PCI_IREQ;
 
        if (diag != old) {
-               dev_printk(KERN_INFO, &socket->dev->dev,
-                          "adjusting diagnostic: %02x -> %02x\n",
-                          old, diag);
+               dev_info(&socket->dev->dev,
+                        "adjusting diagnostic: %02x -> %02x\n",
+                        old, diag);
                config_writeb(socket, TI1250_DIAGNOSTIC, diag);
        }
 
@@ -963,9 +957,9 @@ static void ene_tune_bridge(struct pcmcia_socket *sock, struct pci_bus *bus)
                /* default to clear TLTEnable bit, old behaviour */
                test_c9 &= ~ENE_TEST_C9_TLTENABLE;
 
-       dev_printk(KERN_INFO, &socket->dev->dev,
-                  "EnE: chaning testregister 0xC9, %02x -> %02x\n",
-                  old_c9, test_c9);
+       dev_info(&socket->dev->dev,
+                "EnE: changing testregister 0xC9, %02x -> %02x\n",
+                old_c9, test_c9);
        config_writeb(socket, ENE_TEST_C9, test_c9);
 }
 
index 615a45a8fe8674fe4d920d87884bde653f2e3b3a..582688fe75054003e9f2c0927758b162e7dd6f3a 100644 (file)
 #define TOPIC_EXCA_IF_CONTROL          0x3e    /* 8 bit */
 #define TOPIC_EXCA_IFC_33V_ENA         0x01
 
+#define TOPIC_PCI_CFG_PPBCN            0x3e    /* 16-bit */
+#define TOPIC_PCI_CFG_PPBCN_WBEN       0x0400
+
 static void topic97_zoom_video(struct pcmcia_socket *sock, int onoff)
 {
        struct yenta_socket *socket = container_of(sock, struct yenta_socket, socket);
@@ -138,6 +141,7 @@ static int topic97_override(struct yenta_socket *socket)
 static int topic95_override(struct yenta_socket *socket)
 {
        u8 fctrl;
+       u16 ppbcn;
 
        /* enable 3.3V support for 16bit cards */
        fctrl = exca_readb(socket, TOPIC_EXCA_IF_CONTROL);
@@ -146,6 +150,18 @@ static int topic95_override(struct yenta_socket *socket)
        /* tell yenta to use exca registers to power 16bit cards */
        socket->flags |= YENTA_16BIT_POWER_EXCA | YENTA_16BIT_POWER_DF;
 
+       /* Disable write buffers to prevent lockups under load with numerous
+          Cardbus cards, observed on Tecra 500CDT and reported elsewhere on the
+          net.  This is not a power-on default according to the datasheet
+          but some BIOSes seem to set it. */
+       if (pci_read_config_word(socket->dev, TOPIC_PCI_CFG_PPBCN, &ppbcn) == 0
+           && socket->dev->revision <= 7
+           && (ppbcn & TOPIC_PCI_CFG_PPBCN_WBEN)) {
+               ppbcn &= ~TOPIC_PCI_CFG_PPBCN_WBEN;
+               pci_write_config_word(socket->dev, TOPIC_PCI_CFG_PPBCN, ppbcn);
+               dev_info(&socket->dev->dev, "Disabled ToPIC95 Cardbus write buffers.\n");
+       }
+
        return 0;
 }
 
index 21973d55a0550cc5f07188a00b4cb7139ecab9ec..1e5fa211fb70ccbf58c47f2a5307b9b1b43fbb28 100644 (file)
@@ -84,32 +84,32 @@ MODULE_LICENSE("GPL");
 #define IO_MAX_MAPS    2
 #define MEM_MAX_MAPS   5
 
-typedef enum {
+enum vrc4171_slot {
        SLOT_PROBE = 0,
        SLOT_NOPROBE_IO,
        SLOT_NOPROBE_MEM,
        SLOT_NOPROBE_ALL,
        SLOT_INITIALIZED,
-} vrc4171_slot_t;
+};
 
-typedef enum {
+enum vrc4171_slotb {
        SLOTB_IS_NONE,
        SLOTB_IS_PCCARD,
        SLOTB_IS_CF,
        SLOTB_IS_FLASHROM,
-} vrc4171_slotb_t;
+};
 
-typedef struct vrc4171_socket {
-       vrc4171_slot_t slot;
+struct vrc4171_socket {
+       enum vrc4171_slot slot;
        struct pcmcia_socket pcmcia_socket;
        char name[24];
        int csc_irq;
        int io_irq;
        spinlock_t lock;
-} vrc4171_socket_t;
+};
 
-static vrc4171_socket_t vrc4171_sockets[CARD_MAX_SLOTS];
-static vrc4171_slotb_t vrc4171_slotb = SLOTB_IS_NONE;
+static struct vrc4171_socket vrc4171_sockets[CARD_MAX_SLOTS];
+static enum vrc4171_slotb vrc4171_slotb = SLOTB_IS_NONE;
 static char vrc4171_card_name[] = "NEC VRC4171 Card Controller";
 static unsigned int vrc4171_irq;
 static uint16_t vrc4171_irq_mask = 0xdeb8;
@@ -141,7 +141,7 @@ static inline uint16_t vrc4171_get_irq_status(void)
        return inw(INTERRUPT_STATUS);
 }
 
-static inline void vrc4171_set_multifunction_pin(vrc4171_slotb_t config)
+static inline void vrc4171_set_multifunction_pin(enum vrc4171_slotb config)
 {
        uint16_t config1;
 
@@ -234,7 +234,7 @@ static inline int search_nonuse_irq(void)
 
 static int pccard_init(struct pcmcia_socket *sock)
 {
-       vrc4171_socket_t *socket;
+       struct vrc4171_socket *socket;
        unsigned int slot;
 
        sock->features |= SS_CAP_PCCARD | SS_CAP_PAGE_REGS;
@@ -317,7 +317,7 @@ static inline uint8_t set_Vcc_value(u_char Vcc)
 
 static int pccard_set_socket(struct pcmcia_socket *sock, socket_state_t *state)
 {
-       vrc4171_socket_t *socket;
+       struct vrc4171_socket *socket;
        unsigned int slot;
        uint8_t voltage, power, control, cscint;
 
@@ -517,7 +517,7 @@ static inline unsigned int get_events(int slot)
 
 static irqreturn_t pccard_interrupt(int irq, void *dev_id)
 {
-       vrc4171_socket_t *socket;
+       struct vrc4171_socket *socket;
        unsigned int events;
        irqreturn_t retval = IRQ_NONE;
        uint16_t status;
@@ -567,7 +567,7 @@ static inline void reserve_using_irq(int slot)
 
 static int vrc4171_add_sockets(void)
 {
-       vrc4171_socket_t *socket;
+       struct vrc4171_socket *socket;
        int slot, retval;
 
        for (slot = 0; slot < CARD_MAX_SLOTS; slot++) {
@@ -617,7 +617,7 @@ static int vrc4171_add_sockets(void)
 
 static void vrc4171_remove_sockets(void)
 {
-       vrc4171_socket_t *socket;
+       struct vrc4171_socket *socket;
        int slot;
 
        for (slot = 0; slot < CARD_MAX_SLOTS; slot++) {
index 965bd84912335868ad92f995bcbe6b553b65da37..5d6d9b1549bc4fa345a613da000e6efc1ea01b91 100644 (file)
@@ -712,10 +712,9 @@ static int yenta_allocate_res(struct yenta_socket *socket, int nr, unsigned type
                pcibios_bus_to_resource(dev->bus, res, &region);
                if (pci_claim_resource(dev, PCI_BRIDGE_RESOURCES + nr) == 0)
                        return 0;
-               dev_printk(KERN_INFO, &dev->dev,
-                          "Preassigned resource %d busy or not available, "
-                          "reconfiguring...\n",
-                          nr);
+               dev_info(&dev->dev,
+                        "Preassigned resource %d busy or not available, reconfiguring...\n",
+                        nr);
        }
 
        if (type & IORESOURCE_IO) {
@@ -738,9 +737,9 @@ static int yenta_allocate_res(struct yenta_socket *socket, int nr, unsigned type
                        return 1;
        }
 
-       dev_printk(KERN_INFO, &dev->dev,
-                  "no resource of type %x available, trying to continue...\n",
-                  type);
+       dev_info(&dev->dev,
+                "no resource of type %x available, trying to continue...\n",
+                type);
        res->start = res->end = res->flags = 0;
        return 0;
 }
@@ -802,13 +801,13 @@ static void yenta_close(struct pci_dev *dev)
        else
                del_timer_sync(&sock->poll_timer);
 
-       if (sock->base)
-               iounmap(sock->base);
+       iounmap(sock->base);
        yenta_free_resources(sock);
 
        pci_release_regions(dev);
        pci_disable_device(dev);
        pci_set_drvdata(dev, NULL);
+       kfree(sock);
 }
 
 
@@ -979,8 +978,8 @@ static int yenta_probe_cb_irq(struct yenta_socket *socket)
        socket->probe_status = 0;
 
        if (request_irq(socket->cb_irq, yenta_probe_handler, IRQF_SHARED, "yenta", socket)) {
-               dev_printk(KERN_WARNING, &socket->dev->dev,
-                          "request_irq() in yenta_probe_cb_irq() failed!\n");
+               dev_warn(&socket->dev->dev,
+                        "request_irq() in yenta_probe_cb_irq() failed!\n");
                return -1;
        }
 
@@ -1019,9 +1018,8 @@ static void yenta_get_socket_capabilities(struct yenta_socket *socket, u32 isa_i
        else
                socket->socket.irq_mask = 0;
 
-       dev_printk(KERN_INFO, &socket->dev->dev,
-                  "ISA IRQ mask 0x%04x, PCI irq %d\n",
-                  socket->socket.irq_mask, socket->cb_irq);
+       dev_info(&socket->dev->dev, "ISA IRQ mask 0x%04x, PCI irq %d\n",
+                socket->socket.irq_mask, socket->cb_irq);
 }
 
 /*
@@ -1111,9 +1109,9 @@ static void yenta_fixup_parent_bridge(struct pci_bus *cardbus_bridge)
 
        /* Show that the wanted subordinate number is not possible: */
        if (cardbus_bridge->busn_res.end > upper_limit)
-               dev_printk(KERN_WARNING, &cardbus_bridge->dev,
-                          "Upper limit for fixing this "
-                          "bridge's parent bridge: #%02x\n", upper_limit);
+               dev_warn(&cardbus_bridge->dev,
+                        "Upper limit for fixing this bridge's parent bridge: #%02x\n",
+                        upper_limit);
 
        /* If we have room to increase the bridge's subordinate number, */
        if (bridge_to_fix->busn_res.end < upper_limit) {
@@ -1122,11 +1120,11 @@ static void yenta_fixup_parent_bridge(struct pci_bus *cardbus_bridge)
                unsigned char subordinate_to_assign =
                        min_t(int, cardbus_bridge->busn_res.end, upper_limit);
 
-               dev_printk(KERN_INFO, &bridge_to_fix->dev,
-                          "Raising subordinate bus# of parent "
-                          "bus (#%02x) from #%02x to #%02x\n",
-                          bridge_to_fix->number,
-                          (int)bridge_to_fix->busn_res.end, subordinate_to_assign);
+               dev_info(&bridge_to_fix->dev,
+                        "Raising subordinate bus# of parent bus (#%02x) from #%02x to #%02x\n",
+                        bridge_to_fix->number,
+                        (int)bridge_to_fix->busn_res.end,
+                        subordinate_to_assign);
 
                /* Save the new subordinate in the bus struct of the bridge */
                bridge_to_fix->busn_res.end = subordinate_to_assign;
@@ -1153,8 +1151,7 @@ static int yenta_probe(struct pci_dev *dev, const struct pci_device_id *id)
         * Bail out if so.
         */
        if (!dev->subordinate) {
-               dev_printk(KERN_ERR, &dev->dev, "no bus associated! "
-                          "(try 'pci=assign-busses')\n");
+               dev_err(&dev->dev, "no bus associated! (try 'pci=assign-busses')\n");
                return -ENODEV;
        }
 
@@ -1189,7 +1186,7 @@ static int yenta_probe(struct pci_dev *dev, const struct pci_device_id *id)
                goto disable;
 
        if (!pci_resource_start(dev, 0)) {
-               dev_printk(KERN_ERR, &dev->dev, "No cardbus resource!\n");
+               dev_err(&dev->dev, "No cardbus resource!\n");
                ret = -ENODEV;
                goto release;
        }
@@ -1208,8 +1205,8 @@ static int yenta_probe(struct pci_dev *dev, const struct pci_device_id *id)
         * report the subsystem vendor and device for help debugging
         * the irq stuff...
         */
-       dev_printk(KERN_INFO, &dev->dev, "CardBus bridge found [%04x:%04x]\n",
-                  dev->subsystem_vendor, dev->subsystem_device);
+       dev_info(&dev->dev, "CardBus bridge found [%04x:%04x]\n",
+                dev->subsystem_vendor, dev->subsystem_device);
 
        yenta_config_init(socket);
 
@@ -1239,12 +1236,10 @@ static int yenta_probe(struct pci_dev *dev, const struct pci_device_id *id)
                setup_timer(&socket->poll_timer, yenta_interrupt_wrapper,
                            (unsigned long)socket);
                mod_timer(&socket->poll_timer, jiffies + HZ);
-               dev_printk(KERN_INFO, &dev->dev,
-                          "no PCI IRQ, CardBus support disabled for this "
-                          "socket.\n");
-               dev_printk(KERN_INFO, &dev->dev,
-                          "check your BIOS CardBus, BIOS IRQ or ACPI "
-                          "settings.\n");
+               dev_info(&dev->dev,
+                        "no PCI IRQ, CardBus support disabled for this socket.\n");
+               dev_info(&dev->dev,
+                        "check your BIOS CardBus, BIOS IRQ or ACPI settings.\n");
        } else {
                socket->socket.features |= SS_CAP_CARDBUS;
        }
@@ -1252,32 +1247,41 @@ static int yenta_probe(struct pci_dev *dev, const struct pci_device_id *id)
        /* Figure out what the dang thing can do for the PCMCIA layer... */
        yenta_interrogate(socket);
        yenta_get_socket_capabilities(socket, isa_interrupts);
-       dev_printk(KERN_INFO, &dev->dev,
-                  "Socket status: %08x\n", cb_readl(socket, CB_SOCKET_STATE));
+       dev_info(&dev->dev, "Socket status: %08x\n",
+                cb_readl(socket, CB_SOCKET_STATE));
 
        yenta_fixup_parent_bridge(dev->subordinate);
 
        /* Register it with the pcmcia layer.. */
        ret = pcmcia_register_socket(&socket->socket);
-       if (ret == 0) {
-               /* Add the yenta register attributes */
-               ret = device_create_file(&dev->dev, &dev_attr_yenta_registers);
-               if (ret == 0)
-                       goto out;
-
-               /* error path... */
-               pcmcia_unregister_socket(&socket->socket);
-       }
+       if (ret)
+               goto free_irq;
+
+       /* Add the yenta register attributes */
+       ret = device_create_file(&dev->dev, &dev_attr_yenta_registers);
+       if (ret)
+               goto unregister_socket;
 
+       return ret;
+
+       /* error path... */
+ unregister_socket:
+       pcmcia_unregister_socket(&socket->socket);
+ free_irq:
+       if (socket->cb_irq)
+               free_irq(socket->cb_irq, socket);
+       else
+               del_timer_sync(&socket->poll_timer);
  unmap:
        iounmap(socket->base);
+       yenta_free_resources(socket);
  release:
        pci_release_regions(dev);
  disable:
        pci_disable_device(dev);
  free:
+       pci_set_drvdata(dev, NULL);
        kfree(socket);
- out:
        return ret;
 }
 
index a53bd5b52df97ff48fa921a5009f2fa6937aa377..fc9b9f0ea91e8132b08c85478a592e3f820fc2cc 100644 (file)
@@ -38,7 +38,9 @@ config ARMADA375_USBCLUSTER_PHY
 config PHY_DM816X_USB
        tristate "TI dm816x USB PHY driver"
        depends on ARCH_OMAP2PLUS
+       depends on USB_SUPPORT
        select GENERIC_PHY
+       select USB_PHY
        help
          Enable this for dm816x USB to work.
 
@@ -97,8 +99,9 @@ config OMAP_CONTROL_PHY
 config OMAP_USB2
        tristate "OMAP USB2 PHY Driver"
        depends on ARCH_OMAP2PLUS
-       depends on USB_PHY
+       depends on USB_SUPPORT
        select GENERIC_PHY
+       select USB_PHY
        select OMAP_CONTROL_PHY
        depends on OMAP_OCP2SCP
        help
@@ -122,8 +125,9 @@ config TI_PIPE3
 config TWL4030_USB
        tristate "TWL4030 USB Transceiver Driver"
        depends on TWL4030_CORE && REGULATOR_TWL4030 && USB_MUSB_OMAP2PLUS
-       depends on USB_PHY
+       depends on USB_SUPPORT
        select GENERIC_PHY
+       select USB_PHY
        help
          Enable this to support the USB OTG transceiver on TWL4030
          family chips (including the TWL5030 and TPS659x0 devices).
@@ -304,7 +308,7 @@ config PHY_STIH41X_USB
 
 config PHY_QCOM_UFS
        tristate "Qualcomm UFS PHY driver"
-       depends on OF && ARCH_MSM
+       depends on OF && ARCH_QCOM
        select GENERIC_PHY
        help
          Support for UFS PHY on QCOM chipsets.
index 3791838f4bd4b14e145dd5718a3030c4b89d9f3b..63bc12d7a73e561a8e967ac4fb7f453c9a0d23ab 100644 (file)
@@ -530,7 +530,7 @@ struct phy *phy_optional_get(struct device *dev, const char *string)
 {
        struct phy *phy = phy_get(dev, string);
 
-       if (PTR_ERR(phy) == -ENODEV)
+       if (IS_ERR(phy) && (PTR_ERR(phy) == -ENODEV))
                phy = NULL;
 
        return phy;
@@ -584,7 +584,7 @@ struct phy *devm_phy_optional_get(struct device *dev, const char *string)
 {
        struct phy *phy = devm_phy_get(dev, string);
 
-       if (PTR_ERR(phy) == -ENODEV)
+       if (IS_ERR(phy) && (PTR_ERR(phy) == -ENODEV))
                phy = NULL;
 
        return phy;
index 183ef43681016ba0f238edfa98bbbef3684ab543..c1a468686bdc72433b7596512cb70852f3ef2420 100644 (file)
@@ -275,6 +275,7 @@ static int omap_usb2_probe(struct platform_device *pdev)
                phy->wkupclk = devm_clk_get(phy->dev, "usb_phy_cm_clk32k");
                if (IS_ERR(phy->wkupclk)) {
                        dev_err(&pdev->dev, "unable to get usb_phy_cm_clk32k\n");
+                       pm_runtime_disable(phy->dev);
                        return PTR_ERR(phy->wkupclk);
                } else {
                        dev_warn(&pdev->dev,
index 778276aba3aa0092d8e8e7bc2de15eae4f5a5a15..97d45f47d1ade847f9f0d7462d0ae91e91505974 100644 (file)
@@ -23,7 +23,7 @@
 #define USBHS_LPSTS                    0x02
 #define USBHS_UGCTRL                   0x80
 #define USBHS_UGCTRL2                  0x84
-#define USBHS_UGSTS                    0x88    /* The manuals have 0x90 */
+#define USBHS_UGSTS                    0x88    /* From technical update */
 
 /* Low Power Status register (LPSTS) */
 #define USBHS_LPSTS_SUSPM              0x4000
@@ -41,7 +41,7 @@
 #define USBHS_UGCTRL2_USB0SEL_HS_USB   0x00000030
 
 /* USB General status register (UGSTS) */
-#define USBHS_UGSTS_LOCK               0x00000300 /* The manuals have 0x3 */
+#define USBHS_UGSTS_LOCK               0x00000100 /* From technical update */
 
 #define PHYS_PER_CHANNEL       2
 
index 2a6531a5fde818fdfe5cdd7679434636f967e659..cb13299195271ffed4854ed6b0b593c5e8487019 100644 (file)
@@ -40,7 +40,7 @@ config CHROMEOS_PSTORE
 
 config CROS_EC_CHARDEV
         tristate "Chrome OS Embedded Controller userspace device interface"
-        depends on MFD_CROS_EC
+        depends on CROS_EC_PROTO
         ---help---
           This driver adds support to talk with the ChromeOS EC from userspace.
 
@@ -49,7 +49,7 @@ config CROS_EC_CHARDEV
 
 config CROS_EC_LPC
         tristate "ChromeOS Embedded Controller (LPC)"
-        depends on MFD_CROS_EC && (X86 || COMPILE_TEST)
+        depends on MFD_CROS_EC && CROS_EC_PROTO && (X86 || COMPILE_TEST)
         help
           If you say Y here, you get support for talking to the ChromeOS EC
           over an LPC bus. This uses a simple byte-level protocol with a
@@ -59,4 +59,9 @@ config CROS_EC_LPC
           To compile this driver as a module, choose M here: the
           module will be called cros_ec_lpc.
 
+config CROS_EC_PROTO
+        bool
+        help
+          ChromeOS EC communication protocol helpers.
+
 endif # CHROMEOS_PLATFORMS
index bd8d8601e87516e851389debd281ac3e0d67b7f1..4a11b010f5d8859e37daad2c1c0d1b381cc5d3a4 100644 (file)
@@ -4,3 +4,4 @@ obj-$(CONFIG_CHROMEOS_PSTORE)   += chromeos_pstore.o
 cros_ec_devs-objs               := cros_ec_dev.o cros_ec_sysfs.o cros_ec_lightbar.o
 obj-$(CONFIG_CROS_EC_CHARDEV)   += cros_ec_devs.o
 obj-$(CONFIG_CROS_EC_LPC)       += cros_ec_lpc.o
+obj-$(CONFIG_CROS_EC_PROTO)    += cros_ec_proto.o
index 6090d0b2826fa1b9502ed47b87f10f9dd9a06a08..e8fcdc237029a97908194a52b668ad0c4aa6920c 100644 (file)
 #include <linux/fs.h>
 #include <linux/module.h>
 #include <linux/platform_device.h>
+#include <linux/slab.h>
 #include <linux/uaccess.h>
 
 #include "cros_ec_dev.h"
 
 /* Device variables */
 #define CROS_MAX_DEV 128
-static struct class *cros_class;
 static int ec_major;
 
+static const struct attribute_group *cros_ec_groups[] = {
+       &cros_ec_attr_group,
+       &cros_ec_lightbar_attr_group,
+       NULL,
+};
+
+static struct class cros_class = {
+       .owner          = THIS_MODULE,
+       .name           = "chromeos",
+       .dev_groups     = cros_ec_groups,
+};
+
 /* Basic communication */
-static int ec_get_version(struct cros_ec_device *ec, char *str, int maxlen)
+static int ec_get_version(struct cros_ec_dev *ec, char *str, int maxlen)
 {
        struct ec_response_get_version *resp;
        static const char * const current_image_name[] = {
                "unknown", "read-only", "read-write", "invalid",
        };
-       struct cros_ec_command msg = {
-               .version = 0,
-               .command = EC_CMD_GET_VERSION,
-               .outdata = { 0 },
-               .outsize = 0,
-               .indata = { 0 },
-               .insize = sizeof(*resp),
-       };
+       struct cros_ec_command *msg;
        int ret;
 
-       ret = cros_ec_cmd_xfer(ec, &msg);
+       msg = kmalloc(sizeof(*msg) + sizeof(*resp), GFP_KERNEL);
+       if (!msg)
+               return -ENOMEM;
+
+       msg->version = 0;
+       msg->command = EC_CMD_GET_VERSION + ec->cmd_offset;
+       msg->insize = sizeof(*resp);
+       msg->outsize = 0;
+
+       ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
        if (ret < 0)
-               return ret;
+               goto exit;
 
-       if (msg.result != EC_RES_SUCCESS) {
+       if (msg->result != EC_RES_SUCCESS) {
                snprintf(str, maxlen,
                         "%s\nUnknown EC version: EC returned %d\n",
-                        CROS_EC_DEV_VERSION, msg.result);
-               return 0;
+                        CROS_EC_DEV_VERSION, msg->result);
+               ret = -EINVAL;
+               goto exit;
        }
 
-       resp = (struct ec_response_get_version *)msg.indata;
+       resp = (struct ec_response_get_version *)msg->data;
        if (resp->current_image >= ARRAY_SIZE(current_image_name))
                resp->current_image = 3; /* invalid */
 
@@ -65,14 +80,19 @@ static int ec_get_version(struct cros_ec_device *ec, char *str, int maxlen)
                 resp->version_string_ro, resp->version_string_rw,
                 current_image_name[resp->current_image]);
 
-       return 0;
+       ret = 0;
+exit:
+       kfree(msg);
+       return ret;
 }
 
 /* Device file ops */
 static int ec_device_open(struct inode *inode, struct file *filp)
 {
-       filp->private_data = container_of(inode->i_cdev,
-                                         struct cros_ec_device, cdev);
+       struct cros_ec_dev *ec = container_of(inode->i_cdev,
+                                             struct cros_ec_dev, cdev);
+       filp->private_data = ec;
+       nonseekable_open(inode, filp);
        return 0;
 }
 
@@ -84,7 +104,7 @@ static int ec_device_release(struct inode *inode, struct file *filp)
 static ssize_t ec_device_read(struct file *filp, char __user *buffer,
                              size_t length, loff_t *offset)
 {
-       struct cros_ec_device *ec = filp->private_data;
+       struct cros_ec_dev *ec = filp->private_data;
        char msg[sizeof(struct ec_response_get_version) +
                 sizeof(CROS_EC_DEV_VERSION)];
        size_t count;
@@ -107,38 +127,53 @@ static ssize_t ec_device_read(struct file *filp, char __user *buffer,
 }
 
 /* Ioctls */
-static long ec_device_ioctl_xcmd(struct cros_ec_device *ec, void __user *arg)
+static long ec_device_ioctl_xcmd(struct cros_ec_dev *ec, void __user *arg)
 {
        long ret;
-       struct cros_ec_command s_cmd = { };
+       struct cros_ec_command u_cmd;
+       struct cros_ec_command *s_cmd;
 
-       if (copy_from_user(&s_cmd, arg, sizeof(s_cmd)))
+       if (copy_from_user(&u_cmd, arg, sizeof(u_cmd)))
                return -EFAULT;
 
-       ret = cros_ec_cmd_xfer(ec, &s_cmd);
+       s_cmd = kmalloc(sizeof(*s_cmd) + max(u_cmd.outsize, u_cmd.insize),
+                       GFP_KERNEL);
+       if (!s_cmd)
+               return -ENOMEM;
+
+       if (copy_from_user(s_cmd, arg, sizeof(*s_cmd) + u_cmd.outsize)) {
+               ret = -EFAULT;
+               goto exit;
+       }
+
+       s_cmd->command += ec->cmd_offset;
+       ret = cros_ec_cmd_xfer(ec->ec_dev, s_cmd);
        /* Only copy data to userland if data was received. */
        if (ret < 0)
-               return ret;
-
-       if (copy_to_user(arg, &s_cmd, sizeof(s_cmd)))
-               return -EFAULT;
+               goto exit;
 
-       return 0;
+       if (copy_to_user(arg, s_cmd, sizeof(*s_cmd) + u_cmd.insize))
+               ret = -EFAULT;
+exit:
+       kfree(s_cmd);
+       return ret;
 }
 
-static long ec_device_ioctl_readmem(struct cros_ec_device *ec, void __user *arg)
+static long ec_device_ioctl_readmem(struct cros_ec_dev *ec, void __user *arg)
 {
+       struct cros_ec_device *ec_dev = ec->ec_dev;
        struct cros_ec_readmem s_mem = { };
        long num;
 
        /* Not every platform supports direct reads */
-       if (!ec->cmd_readmem)
+       if (!ec_dev->cmd_readmem)
                return -ENOTTY;
 
        if (copy_from_user(&s_mem, arg, sizeof(s_mem)))
                return -EFAULT;
 
-       num = ec->cmd_readmem(ec, s_mem.offset, s_mem.bytes, s_mem.buffer);
+       num = ec_dev->cmd_readmem(ec_dev, s_mem.offset, s_mem.bytes,
+                                 s_mem.buffer);
        if (num <= 0)
                return num;
 
@@ -151,7 +186,7 @@ static long ec_device_ioctl_readmem(struct cros_ec_device *ec, void __user *arg)
 static long ec_device_ioctl(struct file *filp, unsigned int cmd,
                            unsigned long arg)
 {
-       struct cros_ec_device *ec = filp->private_data;
+       struct cros_ec_dev *ec = filp->private_data;
 
        if (_IOC_TYPE(cmd) != CROS_EC_DEV_IOC)
                return -ENOTTY;
@@ -174,45 +209,81 @@ static const struct file_operations fops = {
        .unlocked_ioctl = ec_device_ioctl,
 };
 
+static void __remove(struct device *dev)
+{
+       struct cros_ec_dev *ec = container_of(dev, struct cros_ec_dev,
+                                             class_dev);
+       kfree(ec);
+}
+
 static int ec_device_probe(struct platform_device *pdev)
 {
-       struct cros_ec_device *ec = dev_get_drvdata(pdev->dev.parent);
-       int retval = -ENOTTY;
-       dev_t devno = MKDEV(ec_major, 0);
+       int retval = -ENOMEM;
+       struct device *dev = &pdev->dev;
+       struct cros_ec_platform *ec_platform = dev_get_platdata(dev);
+       dev_t devno = MKDEV(ec_major, pdev->id);
+       struct cros_ec_dev *ec = kzalloc(sizeof(*ec), GFP_KERNEL);
+
+       if (!ec)
+               return retval;
 
-       /* Instantiate it (and remember the EC) */
+       dev_set_drvdata(dev, ec);
+       ec->ec_dev = dev_get_drvdata(dev->parent);
+       ec->dev = dev;
+       ec->cmd_offset = ec_platform->cmd_offset;
+       device_initialize(&ec->class_dev);
        cdev_init(&ec->cdev, &fops);
 
+       /*
+        * Add the character device
+        * Link cdev to the class device to be sure device is not used
+        * before unbinding it.
+        */
+       ec->cdev.kobj.parent = &ec->class_dev.kobj;
        retval = cdev_add(&ec->cdev, devno, 1);
        if (retval) {
-               dev_err(&pdev->dev, ": failed to add character device\n");
-               return retval;
+               dev_err(dev, ": failed to add character device\n");
+               goto cdev_add_failed;
        }
 
-       ec->vdev = device_create(cros_class, NULL, devno, ec,
-                                CROS_EC_DEV_NAME);
-       if (IS_ERR(ec->vdev)) {
-               retval = PTR_ERR(ec->vdev);
-               dev_err(&pdev->dev, ": failed to create device\n");
-               cdev_del(&ec->cdev);
-               return retval;
+       /*
+        * Add the class device
+        * Link to the character device for creating the /dev entry
+        * in devtmpfs.
+        */
+       ec->class_dev.devt = ec->cdev.dev;
+       ec->class_dev.class = &cros_class;
+       ec->class_dev.parent = dev;
+       ec->class_dev.release = __remove;
+
+       retval = dev_set_name(&ec->class_dev, "%s", ec_platform->ec_name);
+       if (retval) {
+               dev_err(dev, "dev_set_name failed => %d\n", retval);
+               goto set_named_failed;
        }
 
-       /* Initialize extra interfaces */
-       ec_dev_sysfs_init(ec);
-       ec_dev_lightbar_init(ec);
+       retval = device_add(&ec->class_dev);
+       if (retval) {
+               dev_err(dev, "device_register failed => %d\n", retval);
+               goto dev_reg_failed;
+       }
 
        return 0;
+
+dev_reg_failed:
+set_named_failed:
+       dev_set_drvdata(dev, NULL);
+       cdev_del(&ec->cdev);
+cdev_add_failed:
+       kfree(ec);
+       return retval;
 }
 
 static int ec_device_remove(struct platform_device *pdev)
 {
-       struct cros_ec_device *ec = dev_get_drvdata(pdev->dev.parent);
-
-       ec_dev_lightbar_remove(ec);
-       ec_dev_sysfs_remove(ec);
-       device_destroy(cros_class, MKDEV(ec_major, 0));
+       struct cros_ec_dev *ec = dev_get_drvdata(&pdev->dev);
        cdev_del(&ec->cdev);
+       device_unregister(&ec->class_dev);
        return 0;
 }
 
@@ -229,10 +300,10 @@ static int __init cros_ec_dev_init(void)
        int ret;
        dev_t dev = 0;
 
-       cros_class = class_create(THIS_MODULE, "chromeos");
-       if (IS_ERR(cros_class)) {
+       ret  = class_register(&cros_class);
+       if (ret) {
                pr_err(CROS_EC_DEV_NAME ": failed to register device class\n");
-               return PTR_ERR(cros_class);
+               return ret;
        }
 
        /* Get a range of minor numbers (starting with 0) to work with */
@@ -254,7 +325,7 @@ static int __init cros_ec_dev_init(void)
 failed_devreg:
        unregister_chrdev_region(MKDEV(ec_major, 0), CROS_MAX_DEV);
 failed_chrdevreg:
-       class_destroy(cros_class);
+       class_unregister(&cros_class);
        return ret;
 }
 
@@ -262,7 +333,7 @@ static void __exit cros_ec_dev_exit(void)
 {
        platform_driver_unregister(&cros_ec_dev_driver);
        unregister_chrdev(ec_major, CROS_EC_DEV_NAME);
-       class_destroy(cros_class);
+       class_unregister(&cros_class);
 }
 
 module_init(cros_ec_dev_init);
index 45d67f7e518c0447277cb8a1b48862e1aa753633..bfd2c84c3571ccdff4ad3749cbf7db852b2ed9eb 100644 (file)
@@ -24,7 +24,6 @@
 #include <linux/types.h>
 #include <linux/mfd/cros_ec.h>
 
-#define CROS_EC_DEV_NAME "cros_ec"
 #define CROS_EC_DEV_VERSION "1.0.0"
 
 /*
@@ -44,10 +43,4 @@ struct cros_ec_readmem {
 #define CROS_EC_DEV_IOCXCMD   _IOWR(CROS_EC_DEV_IOC, 0, struct cros_ec_command)
 #define CROS_EC_DEV_IOCRDMEM  _IOWR(CROS_EC_DEV_IOC, 1, struct cros_ec_readmem)
 
-void ec_dev_sysfs_init(struct cros_ec_device *);
-void ec_dev_sysfs_remove(struct cros_ec_device *);
-
-void ec_dev_lightbar_init(struct cros_ec_device *);
-void ec_dev_lightbar_remove(struct cros_ec_device *);
-
 #endif /* _CROS_EC_DEV_H_ */
index b4ff47a9069ad3a8ae23468316327b215614554e..144e09df9b846551edb9b2c9df6c7be8f717c697 100644 (file)
@@ -31,6 +31,7 @@
 #include <linux/sched.h>
 #include <linux/types.h>
 #include <linux/uaccess.h>
+#include <linux/slab.h>
 
 #include "cros_ec_dev.h"
 
@@ -91,55 +92,81 @@ out:
        return ret;
 }
 
-#define INIT_MSG(P, R) { \
-               .command = EC_CMD_LIGHTBAR_CMD, \
-               .outsize = sizeof(*P), \
-               .insize = sizeof(*R), \
-       }
+static struct cros_ec_command *alloc_lightbar_cmd_msg(struct cros_ec_dev *ec)
+{
+       struct cros_ec_command *msg;
+       int len;
+
+       len = max(sizeof(struct ec_params_lightbar),
+                 sizeof(struct ec_response_lightbar));
+
+       msg = kmalloc(sizeof(*msg) + len, GFP_KERNEL);
+       if (!msg)
+               return NULL;
+
+       msg->version = 0;
+       msg->command = EC_CMD_LIGHTBAR_CMD + ec->cmd_offset;
+       msg->outsize = sizeof(struct ec_params_lightbar);
+       msg->insize = sizeof(struct ec_response_lightbar);
+
+       return msg;
+}
 
-static int get_lightbar_version(struct cros_ec_device *ec,
+static int get_lightbar_version(struct cros_ec_dev *ec,
                                uint32_t *ver_ptr, uint32_t *flg_ptr)
 {
        struct ec_params_lightbar *param;
        struct ec_response_lightbar *resp;
-       struct cros_ec_command msg = INIT_MSG(param, resp);
+       struct cros_ec_command *msg;
        int ret;
 
-       param = (struct ec_params_lightbar *)msg.outdata;
-       param->cmd = LIGHTBAR_CMD_VERSION;
-       ret = cros_ec_cmd_xfer(ec, &msg);
-       if (ret < 0)
+       msg = alloc_lightbar_cmd_msg(ec);
+       if (!msg)
                return 0;
 
-       switch (msg.result) {
+       param = (struct ec_params_lightbar *)msg->data;
+       param->cmd = LIGHTBAR_CMD_VERSION;
+       ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
+       if (ret < 0) {
+               ret = 0;
+               goto exit;
+       }
+
+       switch (msg->result) {
        case EC_RES_INVALID_PARAM:
                /* Pixel had no version command. */
                if (ver_ptr)
                        *ver_ptr = 0;
                if (flg_ptr)
                        *flg_ptr = 0;
-               return 1;
+               ret = 1;
+               goto exit;
 
        case EC_RES_SUCCESS:
-               resp = (struct ec_response_lightbar *)msg.indata;
+               resp = (struct ec_response_lightbar *)msg->data;
 
                /* Future devices w/lightbars should implement this command */
                if (ver_ptr)
                        *ver_ptr = resp->version.num;
                if (flg_ptr)
                        *flg_ptr = resp->version.flags;
-               return 1;
+               ret = 1;
+               goto exit;
        }
 
        /* Anything else (ie, EC_RES_INVALID_COMMAND) - no lightbar */
-       return 0;
+       ret = 0;
+exit:
+       kfree(msg);
+       return ret;
 }
 
 static ssize_t version_show(struct device *dev,
                            struct device_attribute *attr, char *buf)
 {
-       uint32_t version, flags;
-       struct cros_ec_device *ec = dev_get_drvdata(dev);
+       uint32_t version = 0, flags = 0;
+       struct cros_ec_dev *ec = container_of(dev,
+                                             struct cros_ec_dev, class_dev);
        int ret;
 
        ret = lb_throttle();
@@ -158,30 +185,39 @@ static ssize_t brightness_store(struct device *dev,
                                const char *buf, size_t count)
 {
        struct ec_params_lightbar *param;
-       struct ec_response_lightbar *resp;
-       struct cros_ec_command msg = INIT_MSG(param, resp);
+       struct cros_ec_command *msg;
        int ret;
        unsigned int val;
-       struct cros_ec_device *ec = dev_get_drvdata(dev);
+       struct cros_ec_dev *ec = container_of(dev,
+                                             struct cros_ec_dev, class_dev);
 
        if (kstrtouint(buf, 0, &val))
                return -EINVAL;
 
-       param = (struct ec_params_lightbar *)msg.outdata;
-       param->cmd = LIGHTBAR_CMD_BRIGHTNESS;
-       param->brightness.num = val;
+       msg = alloc_lightbar_cmd_msg(ec);
+       if (!msg)
+               return -ENOMEM;
+
+       param = (struct ec_params_lightbar *)msg->data;
+       param->cmd = LIGHTBAR_CMD_SET_BRIGHTNESS;
+       param->set_brightness.num = val;
        ret = lb_throttle();
        if (ret)
-               return ret;
+               goto exit;
 
-       ret = cros_ec_cmd_xfer(ec, &msg);
+       ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
        if (ret < 0)
-               return ret;
+               goto exit;
 
-       if (msg.result != EC_RES_SUCCESS)
-               return -EINVAL;
+       if (msg->result != EC_RES_SUCCESS) {
+               ret = -EINVAL;
+               goto exit;
+       }
 
-       return count;
+       ret = count;
+exit:
+       kfree(msg);
+       return ret;
 }
 
 
@@ -196,12 +232,16 @@ static ssize_t led_rgb_store(struct device *dev, struct device_attribute *attr,
                             const char *buf, size_t count)
 {
        struct ec_params_lightbar *param;
-       struct ec_response_lightbar *resp;
-       struct cros_ec_command msg = INIT_MSG(param, resp);
-       struct cros_ec_device *ec = dev_get_drvdata(dev);
+       struct cros_ec_command *msg;
+       struct cros_ec_dev *ec = container_of(dev,
+                                             struct cros_ec_dev, class_dev);
        unsigned int val[4];
        int ret, i = 0, j = 0, ok = 0;
 
+       msg = alloc_lightbar_cmd_msg(ec);
+       if (!msg)
+               return -ENOMEM;
+
        do {
                /* Skip any whitespace */
                while (*buf && isspace(*buf))
@@ -215,12 +255,12 @@ static ssize_t led_rgb_store(struct device *dev, struct device_attribute *attr,
                        return -EINVAL;
 
                if (i == 4) {
-                       param = (struct ec_params_lightbar *)msg.outdata;
-                       param->cmd = LIGHTBAR_CMD_RGB;
-                       param->rgb.led = val[0];
-                       param->rgb.red = val[1];
-                       param->rgb.green = val[2];
-                       param->rgb.blue = val[3];
+                       param = (struct ec_params_lightbar *)msg->data;
+                       param->cmd = LIGHTBAR_CMD_SET_RGB;
+                       param->set_rgb.led = val[0];
+                       param->set_rgb.red = val[1];
+                       param->set_rgb.green = val[2];
+                       param->set_rgb.blue = val[3];
                        /*
                         * Throttle only the first of every four transactions,
                         * so that the user can update all four LEDs at once.
@@ -231,12 +271,14 @@ static ssize_t led_rgb_store(struct device *dev, struct device_attribute *attr,
                                        return ret;
                        }
 
-                       ret = cros_ec_cmd_xfer(ec, &msg);
+                       ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
                        if (ret < 0)
-                               return ret;
+                               goto exit;
 
-                       if (msg.result != EC_RES_SUCCESS)
-                               return -EINVAL;
+                       if (msg->result != EC_RES_SUCCESS) {
+                               ret = -EINVAL;
+                               goto exit;
+                       }
 
                        i = 0;
                        ok = 1;
@@ -248,6 +290,8 @@ static ssize_t led_rgb_store(struct device *dev, struct device_attribute *attr,
 
        } while (*buf);
 
+exit:
+       kfree(msg);
        return (ok && i == 0) ? count : -EINVAL;
 }
 
@@ -261,41 +305,56 @@ static ssize_t sequence_show(struct device *dev,
 {
        struct ec_params_lightbar *param;
        struct ec_response_lightbar *resp;
-       struct cros_ec_command msg = INIT_MSG(param, resp);
+       struct cros_ec_command *msg;
        int ret;
-       struct cros_ec_device *ec = dev_get_drvdata(dev);
+       struct cros_ec_dev *ec = container_of(dev,
+                                             struct cros_ec_dev, class_dev);
+
+       msg = alloc_lightbar_cmd_msg(ec);
+       if (!msg)
+               return -ENOMEM;
 
-       param = (struct ec_params_lightbar *)msg.outdata;
+       param = (struct ec_params_lightbar *)msg->data;
        param->cmd = LIGHTBAR_CMD_GET_SEQ;
        ret = lb_throttle();
        if (ret)
-               return ret;
+               goto exit;
 
-       ret = cros_ec_cmd_xfer(ec, &msg);
+       ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
        if (ret < 0)
-               return ret;
+               goto exit;
 
-       if (msg.result != EC_RES_SUCCESS)
-               return scnprintf(buf, PAGE_SIZE,
-                                "ERROR: EC returned %d\n", msg.result);
+       if (msg->result != EC_RES_SUCCESS) {
+               ret = scnprintf(buf, PAGE_SIZE,
+                               "ERROR: EC returned %d\n", msg->result);
+               goto exit;
+       }
 
-       resp = (struct ec_response_lightbar *)msg.indata;
+       resp = (struct ec_response_lightbar *)msg->data;
        if (resp->get_seq.num >= ARRAY_SIZE(seqname))
-               return scnprintf(buf, PAGE_SIZE, "%d\n", resp->get_seq.num);
+               ret = scnprintf(buf, PAGE_SIZE, "%d\n", resp->get_seq.num);
        else
-               return scnprintf(buf, PAGE_SIZE, "%s\n",
-                                seqname[resp->get_seq.num]);
+               ret = scnprintf(buf, PAGE_SIZE, "%s\n",
+                               seqname[resp->get_seq.num]);
+
+exit:
+       kfree(msg);
+       return ret;
 }
 
 static ssize_t sequence_store(struct device *dev, struct device_attribute *attr,
                              const char *buf, size_t count)
 {
        struct ec_params_lightbar *param;
-       struct ec_response_lightbar *resp;
-       struct cros_ec_command msg = INIT_MSG(param, resp);
+       struct cros_ec_command *msg;
        unsigned int num;
        int ret, len;
-       struct cros_ec_device *ec = dev_get_drvdata(dev);
+       struct cros_ec_dev *ec = container_of(dev,
+                                             struct cros_ec_dev, class_dev);
+
+       msg = alloc_lightbar_cmd_msg(ec);
+       if (!msg)
+               return -ENOMEM;
 
        for (len = 0; len < count; len++)
                if (!isalnum(buf[len]))
@@ -311,18 +370,18 @@ static ssize_t sequence_store(struct device *dev, struct device_attribute *attr,
                        return ret;
        }
 
-       param = (struct ec_params_lightbar *)msg.outdata;
+       param = (struct ec_params_lightbar *)msg->data;
        param->cmd = LIGHTBAR_CMD_SEQ;
        param->seq.num = num;
        ret = lb_throttle();
        if (ret)
                return ret;
 
-       ret = cros_ec_cmd_xfer(ec, &msg);
+       ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
        if (ret < 0)
                return ret;
 
-       if (msg.result != EC_RES_SUCCESS)
+       if (msg->result != EC_RES_SUCCESS)
                return -EINVAL;
 
        return count;
@@ -343,25 +402,27 @@ static struct attribute *__lb_cmds_attrs[] = {
        &dev_attr_sequence.attr,
        NULL,
 };
-static struct attribute_group lb_cmds_attr_group = {
-       .name = "lightbar",
-       .attrs = __lb_cmds_attrs,
-};
 
-void ec_dev_lightbar_init(struct cros_ec_device *ec)
+static umode_t cros_ec_lightbar_attrs_are_visible(struct kobject *kobj,
+                                                 struct attribute *a, int n)
 {
-       int ret = 0;
+       struct device *dev = container_of(kobj, struct device, kobj);
+       struct cros_ec_dev *ec = container_of(dev,
+                                             struct cros_ec_dev, class_dev);
+       struct platform_device *pdev = container_of(ec->dev,
+                                                  struct platform_device, dev);
+       if (pdev->id != 0)
+               return 0;
 
        /* Only instantiate this stuff if the EC has a lightbar */
-       if (!get_lightbar_version(ec, NULL, NULL))
-               return;
-
-       ret = sysfs_create_group(&ec->vdev->kobj, &lb_cmds_attr_group);
-       if (ret)
-               pr_warn("sysfs_create_group() failed: %d\n", ret);
+       if (get_lightbar_version(ec, NULL, NULL))
+               return a->mode;
+       else
+               return 0;
 }
 
-void ec_dev_lightbar_remove(struct cros_ec_device *ec)
-{
-       sysfs_remove_group(&ec->vdev->kobj, &lb_cmds_attr_group);
-}
+struct attribute_group cros_ec_lightbar_attr_group = {
+       .name = "lightbar",
+       .attrs = __lb_cmds_attrs,
+       .is_visible = cros_ec_lightbar_attrs_are_visible,
+};
index 8f9ac4d7bbd0b1afb605458e412b2fca1820db79..bdd77ce45f0512494fa775dba51fc8655be1a8c4 100644 (file)
@@ -46,6 +46,77 @@ static int ec_response_timed_out(void)
        return 1;
 }
 
+static int cros_ec_pkt_xfer_lpc(struct cros_ec_device *ec,
+                               struct cros_ec_command *msg)
+{
+       struct ec_host_request *request;
+       struct ec_host_response response;
+       u8 sum = 0;
+       int i;
+       int ret = 0;
+       u8 *dout;
+
+       ret = cros_ec_prepare_tx(ec, msg);
+
+       /* Write buffer */
+       for (i = 0; i < ret; i++)
+               outb(ec->dout[i], EC_LPC_ADDR_HOST_PACKET + i);
+
+       request = (struct ec_host_request *)ec->dout;
+
+       /* Here we go */
+       outb(EC_COMMAND_PROTOCOL_3, EC_LPC_ADDR_HOST_CMD);
+
+       if (ec_response_timed_out()) {
+               dev_warn(ec->dev, "EC responsed timed out\n");
+               ret = -EIO;
+               goto done;
+       }
+
+       /* Check result */
+       msg->result = inb(EC_LPC_ADDR_HOST_DATA);
+       ret = cros_ec_check_result(ec, msg);
+       if (ret)
+               goto done;
+
+       /* Read back response */
+       dout = (u8 *)&response;
+       for (i = 0; i < sizeof(response); i++) {
+               dout[i] = inb(EC_LPC_ADDR_HOST_PACKET + i);
+               sum += dout[i];
+       }
+
+       msg->result = response.result;
+
+       if (response.data_len > msg->insize) {
+               dev_err(ec->dev,
+                       "packet too long (%d bytes, expected %d)",
+                       response.data_len, msg->insize);
+               ret = -EMSGSIZE;
+               goto done;
+       }
+
+       /* Read response and process checksum */
+       for (i = 0; i < response.data_len; i++) {
+               msg->data[i] =
+                       inb(EC_LPC_ADDR_HOST_PACKET + sizeof(response) + i);
+               sum += msg->data[i];
+       }
+
+       if (sum) {
+               dev_err(ec->dev,
+                       "bad packet checksum %02x\n",
+                       response.checksum);
+               ret = -EBADMSG;
+               goto done;
+       }
+
+       /* Return actual amount of data received */
+       ret = response.data_len;
+done:
+       return ret;
+}
+
 static int cros_ec_cmd_xfer_lpc(struct cros_ec_device *ec,
                                struct cros_ec_command *msg)
 {
@@ -73,8 +144,8 @@ static int cros_ec_cmd_xfer_lpc(struct cros_ec_device *ec,
 
        /* Copy data and update checksum */
        for (i = 0; i < msg->outsize; i++) {
-               outb(msg->outdata[i], EC_LPC_ADDR_HOST_PARAM + i);
-               csum += msg->outdata[i];
+               outb(msg->data[i], EC_LPC_ADDR_HOST_PARAM + i);
+               csum += msg->data[i];
        }
 
        /* Finalize checksum and write args */
@@ -129,8 +200,8 @@ static int cros_ec_cmd_xfer_lpc(struct cros_ec_device *ec,
 
        /* Read response and update checksum */
        for (i = 0; i < args.data_size; i++) {
-               msg->indata[i] = inb(EC_LPC_ADDR_HOST_PARAM + i);
-               csum += msg->indata[i];
+               msg->data[i] = inb(EC_LPC_ADDR_HOST_PARAM + i);
+               csum += msg->data[i];
        }
 
        /* Verify checksum */
@@ -212,11 +283,13 @@ static int cros_ec_lpc_probe(struct platform_device *pdev)
 
        platform_set_drvdata(pdev, ec_dev);
        ec_dev->dev = dev;
-       ec_dev->ec_name = pdev->name;
        ec_dev->phys_name = dev_name(dev);
-       ec_dev->parent = dev;
        ec_dev->cmd_xfer = cros_ec_cmd_xfer_lpc;
+       ec_dev->pkt_xfer = cros_ec_pkt_xfer_lpc;
        ec_dev->cmd_readmem = cros_ec_lpc_readmem;
+       ec_dev->din_size = sizeof(struct ec_host_response) +
+                          sizeof(struct ec_response_get_protocol_info);
+       ec_dev->dout_size = sizeof(struct ec_host_request);
 
        ret = cros_ec_register(ec_dev);
        if (ret) {
diff --git a/drivers/platform/chrome/cros_ec_proto.c b/drivers/platform/chrome/cros_ec_proto.c
new file mode 100644 (file)
index 0000000..990308c
--- /dev/null
@@ -0,0 +1,382 @@
+/*
+ * ChromeOS EC communication protocol helper functions
+ *
+ * Copyright (C) 2015 Google, Inc
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/mfd/cros_ec.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#define EC_COMMAND_RETRIES     50
+
+static int prepare_packet(struct cros_ec_device *ec_dev,
+                         struct cros_ec_command *msg)
+{
+       struct ec_host_request *request;
+       u8 *out;
+       int i;
+       u8 csum = 0;
+
+       BUG_ON(ec_dev->proto_version != EC_HOST_REQUEST_VERSION);
+       BUG_ON(msg->outsize + sizeof(*request) > ec_dev->dout_size);
+
+       out = ec_dev->dout;
+       request = (struct ec_host_request *)out;
+       request->struct_version = EC_HOST_REQUEST_VERSION;
+       request->checksum = 0;
+       request->command = msg->command;
+       request->command_version = msg->version;
+       request->reserved = 0;
+       request->data_len = msg->outsize;
+
+       for (i = 0; i < sizeof(*request); i++)
+               csum += out[i];
+
+       /* Copy data and update checksum */
+       memcpy(out + sizeof(*request), msg->data, msg->outsize);
+       for (i = 0; i < msg->outsize; i++)
+               csum += msg->data[i];
+
+       request->checksum = -csum;
+
+       return sizeof(*request) + msg->outsize;
+}
+
+static int send_command(struct cros_ec_device *ec_dev,
+                       struct cros_ec_command *msg)
+{
+       int ret;
+
+       if (ec_dev->proto_version > 2)
+               ret = ec_dev->pkt_xfer(ec_dev, msg);
+       else
+               ret = ec_dev->cmd_xfer(ec_dev, msg);
+
+       if (msg->result == EC_RES_IN_PROGRESS) {
+               int i;
+               struct cros_ec_command *status_msg;
+               struct ec_response_get_comms_status *status;
+
+               status_msg = kmalloc(sizeof(*status_msg) + sizeof(*status),
+                                    GFP_KERNEL);
+               if (!status_msg)
+                       return -ENOMEM;
+
+               status_msg->version = 0;
+               status_msg->command = EC_CMD_GET_COMMS_STATUS;
+               status_msg->insize = sizeof(*status);
+               status_msg->outsize = 0;
+
+               /*
+                * Query the EC's status until it's no longer busy or
+                * we encounter an error.
+                */
+               for (i = 0; i < EC_COMMAND_RETRIES; i++) {
+                       usleep_range(10000, 11000);
+
+                       ret = ec_dev->cmd_xfer(ec_dev, status_msg);
+                       if (ret < 0)
+                               break;
+
+                       msg->result = status_msg->result;
+                       if (status_msg->result != EC_RES_SUCCESS)
+                               break;
+
+                       status = (struct ec_response_get_comms_status *)
+                                status_msg->data;
+                       if (!(status->flags & EC_COMMS_STATUS_PROCESSING))
+                               break;
+               }
+
+               kfree(status_msg);
+       }
+
+       return ret;
+}
+
+int cros_ec_prepare_tx(struct cros_ec_device *ec_dev,
+                      struct cros_ec_command *msg)
+{
+       u8 *out;
+       u8 csum;
+       int i;
+
+       if (ec_dev->proto_version > 2)
+               return prepare_packet(ec_dev, msg);
+
+       BUG_ON(msg->outsize > EC_PROTO2_MAX_PARAM_SIZE);
+       out = ec_dev->dout;
+       out[0] = EC_CMD_VERSION0 + msg->version;
+       out[1] = msg->command;
+       out[2] = msg->outsize;
+       csum = out[0] + out[1] + out[2];
+       for (i = 0; i < msg->outsize; i++)
+               csum += out[EC_MSG_TX_HEADER_BYTES + i] = msg->data[i];
+       out[EC_MSG_TX_HEADER_BYTES + msg->outsize] = csum;
+
+       return EC_MSG_TX_PROTO_BYTES + msg->outsize;
+}
+EXPORT_SYMBOL(cros_ec_prepare_tx);
+
+int cros_ec_check_result(struct cros_ec_device *ec_dev,
+                        struct cros_ec_command *msg)
+{
+       switch (msg->result) {
+       case EC_RES_SUCCESS:
+               return 0;
+       case EC_RES_IN_PROGRESS:
+               dev_dbg(ec_dev->dev, "command 0x%02x in progress\n",
+                       msg->command);
+               return -EAGAIN;
+       default:
+               dev_dbg(ec_dev->dev, "command 0x%02x returned %d\n",
+                       msg->command, msg->result);
+               return 0;
+       }
+}
+EXPORT_SYMBOL(cros_ec_check_result);
+
+static int cros_ec_host_command_proto_query(struct cros_ec_device *ec_dev,
+                                           int devidx,
+                                           struct cros_ec_command *msg)
+{
+       /*
+        * Try using v3+ to query for supported protocols. If this
+        * command fails, fall back to v2. Returns the highest protocol
+        * supported by the EC.
+        * Also sets the max request/response/passthru size.
+        */
+       int ret;
+
+       if (!ec_dev->pkt_xfer)
+               return -EPROTONOSUPPORT;
+
+       memset(msg, 0, sizeof(*msg));
+       msg->command = EC_CMD_PASSTHRU_OFFSET(devidx) | EC_CMD_GET_PROTOCOL_INFO;
+       msg->insize = sizeof(struct ec_response_get_protocol_info);
+
+       ret = send_command(ec_dev, msg);
+
+       if (ret < 0) {
+               dev_dbg(ec_dev->dev,
+                       "failed to check for EC[%d] protocol version: %d\n",
+                       devidx, ret);
+               return ret;
+       }
+
+       if (devidx > 0 && msg->result == EC_RES_INVALID_COMMAND)
+               return -ENODEV;
+       else if (msg->result != EC_RES_SUCCESS)
+               return msg->result;
+
+       return 0;
+}
+
+static int cros_ec_host_command_proto_query_v2(struct cros_ec_device *ec_dev)
+{
+       struct cros_ec_command *msg;
+       struct ec_params_hello *hello_params;
+       struct ec_response_hello *hello_response;
+       int ret;
+       int len = max(sizeof(*hello_params), sizeof(*hello_response));
+
+       msg = kmalloc(sizeof(*msg) + len, GFP_KERNEL);
+       if (!msg)
+               return -ENOMEM;
+
+       msg->version = 0;
+       msg->command = EC_CMD_HELLO;
+       hello_params = (struct ec_params_hello *)msg->data;
+       msg->outsize = sizeof(*hello_params);
+       hello_response = (struct ec_response_hello *)msg->data;
+       msg->insize = sizeof(*hello_response);
+
+       hello_params->in_data = 0xa0b0c0d0;
+
+       ret = send_command(ec_dev, msg);
+
+       if (ret < 0) {
+               dev_dbg(ec_dev->dev,
+                       "EC failed to respond to v2 hello: %d\n",
+                       ret);
+               goto exit;
+       } else if (msg->result != EC_RES_SUCCESS) {
+               dev_err(ec_dev->dev,
+                       "EC responded to v2 hello with error: %d\n",
+                       msg->result);
+               ret = msg->result;
+               goto exit;
+       } else if (hello_response->out_data != 0xa1b2c3d4) {
+               dev_err(ec_dev->dev,
+                       "EC responded to v2 hello with bad result: %u\n",
+                       hello_response->out_data);
+               ret = -EBADMSG;
+               goto exit;
+       }
+
+       ret = 0;
+
+ exit:
+       kfree(msg);
+       return ret;
+}
+
+int cros_ec_query_all(struct cros_ec_device *ec_dev)
+{
+       struct device *dev = ec_dev->dev;
+       struct cros_ec_command *proto_msg;
+       struct ec_response_get_protocol_info *proto_info;
+       int ret;
+
+       proto_msg = kzalloc(sizeof(*proto_msg) + sizeof(*proto_info),
+                           GFP_KERNEL);
+       if (!proto_msg)
+               return -ENOMEM;
+
+       /* First try sending with proto v3. */
+       ec_dev->proto_version = 3;
+       ret = cros_ec_host_command_proto_query(ec_dev, 0, proto_msg);
+
+       if (ret == 0) {
+               proto_info = (struct ec_response_get_protocol_info *)
+                       proto_msg->data;
+               ec_dev->max_request = proto_info->max_request_packet_size -
+                       sizeof(struct ec_host_request);
+               ec_dev->max_response = proto_info->max_response_packet_size -
+                       sizeof(struct ec_host_response);
+               ec_dev->proto_version =
+                       min(EC_HOST_REQUEST_VERSION,
+                                       fls(proto_info->protocol_versions) - 1);
+               dev_dbg(ec_dev->dev,
+                       "using proto v%u\n",
+                       ec_dev->proto_version);
+
+               ec_dev->din_size = ec_dev->max_response +
+                       sizeof(struct ec_host_response) +
+                       EC_MAX_RESPONSE_OVERHEAD;
+               ec_dev->dout_size = ec_dev->max_request +
+                       sizeof(struct ec_host_request) +
+                       EC_MAX_REQUEST_OVERHEAD;
+
+               /*
+                * Check for PD
+                */
+               ret = cros_ec_host_command_proto_query(ec_dev, 1, proto_msg);
+
+               if (ret) {
+                       dev_dbg(ec_dev->dev, "no PD chip found: %d\n", ret);
+                       ec_dev->max_passthru = 0;
+               } else {
+                       dev_dbg(ec_dev->dev, "found PD chip\n");
+                       ec_dev->max_passthru =
+                               proto_info->max_request_packet_size -
+                               sizeof(struct ec_host_request);
+               }
+       } else {
+               /* Try querying with a v2 hello message. */
+               ec_dev->proto_version = 2;
+               ret = cros_ec_host_command_proto_query_v2(ec_dev);
+
+               if (ret == 0) {
+                       /* V2 hello succeeded. */
+                       dev_dbg(ec_dev->dev, "falling back to proto v2\n");
+
+                       ec_dev->max_request = EC_PROTO2_MAX_PARAM_SIZE;
+                       ec_dev->max_response = EC_PROTO2_MAX_PARAM_SIZE;
+                       ec_dev->max_passthru = 0;
+                       ec_dev->pkt_xfer = NULL;
+                       ec_dev->din_size = EC_MSG_BYTES;
+                       ec_dev->dout_size = EC_MSG_BYTES;
+               } else {
+                       /*
+                        * It's possible for a test to occur too early when
+                        * the EC isn't listening. If this happens, we'll
+                        * test later when the first command is run.
+                        */
+                       ec_dev->proto_version = EC_PROTO_VERSION_UNKNOWN;
+                       dev_dbg(ec_dev->dev, "EC query failed: %d\n", ret);
+                       goto exit;
+               }
+       }
+
+       devm_kfree(dev, ec_dev->din);
+       devm_kfree(dev, ec_dev->dout);
+
+       ec_dev->din = devm_kzalloc(dev, ec_dev->din_size, GFP_KERNEL);
+       if (!ec_dev->din) {
+               ret = -ENOMEM;
+               goto exit;
+       }
+
+       ec_dev->dout = devm_kzalloc(dev, ec_dev->dout_size, GFP_KERNEL);
+       if (!ec_dev->dout) {
+               devm_kfree(dev, ec_dev->din);
+               ret = -ENOMEM;
+               goto exit;
+       }
+
+exit:
+       kfree(proto_msg);
+       return ret;
+}
+EXPORT_SYMBOL(cros_ec_query_all);
+
+int cros_ec_cmd_xfer(struct cros_ec_device *ec_dev,
+                    struct cros_ec_command *msg)
+{
+       int ret;
+
+       mutex_lock(&ec_dev->lock);
+       if (ec_dev->proto_version == EC_PROTO_VERSION_UNKNOWN) {
+               ret = cros_ec_query_all(ec_dev);
+               if (ret) {
+                       dev_err(ec_dev->dev,
+                               "EC version unknown and query failed; aborting command\n");
+                       mutex_unlock(&ec_dev->lock);
+                       return ret;
+               }
+       }
+
+       if (msg->insize > ec_dev->max_response) {
+               dev_dbg(ec_dev->dev, "clamping message receive buffer\n");
+               msg->insize = ec_dev->max_response;
+       }
+
+       if (msg->command < EC_CMD_PASSTHRU_OFFSET(1)) {
+               if (msg->outsize > ec_dev->max_request) {
+                       dev_err(ec_dev->dev,
+                               "request of size %u is too big (max: %u)\n",
+                               msg->outsize,
+                               ec_dev->max_request);
+                       mutex_unlock(&ec_dev->lock);
+                       return -EMSGSIZE;
+               }
+       } else {
+               if (msg->outsize > ec_dev->max_passthru) {
+                       dev_err(ec_dev->dev,
+                               "passthru rq of size %u is too big (max: %u)\n",
+                               msg->outsize,
+                               ec_dev->max_passthru);
+                       mutex_unlock(&ec_dev->lock);
+                       return -EMSGSIZE;
+               }
+       }
+       ret = send_command(ec_dev, msg);
+       mutex_unlock(&ec_dev->lock);
+
+       return ret;
+}
+EXPORT_SYMBOL(cros_ec_cmd_xfer);
index fb62ab6cc6594f259c714f6057ab61c6db952422..f3baf9973989b8005ed88197e60a1a0319ac8808 100644 (file)
@@ -29,6 +29,7 @@
 #include <linux/module.h>
 #include <linux/platform_device.h>
 #include <linux/printk.h>
+#include <linux/slab.h>
 #include <linux/stat.h>
 #include <linux/types.h>
 #include <linux/uaccess.h>
@@ -66,13 +67,19 @@ static ssize_t store_ec_reboot(struct device *dev,
                {"hibernate",    EC_REBOOT_HIBERNATE, 0},
                {"at-shutdown",  -1, EC_REBOOT_FLAG_ON_AP_SHUTDOWN},
        };
-       struct cros_ec_command msg = { 0 };
-       struct ec_params_reboot_ec *param =
-               (struct ec_params_reboot_ec *)msg.outdata;
+       struct cros_ec_command *msg;
+       struct ec_params_reboot_ec *param;
        int got_cmd = 0, offset = 0;
        int i;
        int ret;
-       struct cros_ec_device *ec = dev_get_drvdata(dev);
+       struct cros_ec_dev *ec = container_of(dev,
+                                             struct cros_ec_dev, class_dev);
+
+       msg = kmalloc(sizeof(*msg) + sizeof(*param), GFP_KERNEL);
+       if (!msg)
+               return -ENOMEM;
+
+       param = (struct ec_params_reboot_ec *)msg->data;
 
        param->flags = 0;
        while (1) {
@@ -100,19 +107,26 @@ static ssize_t store_ec_reboot(struct device *dev,
                        offset++;
        }
 
-       if (!got_cmd)
-               return -EINVAL;
-
-       msg.command = EC_CMD_REBOOT_EC;
-       msg.outsize = sizeof(param);
-       ret = cros_ec_cmd_xfer(ec, &msg);
-       if (ret < 0)
-               return ret;
-       if (msg.result != EC_RES_SUCCESS) {
-               dev_dbg(ec->dev, "EC result %d\n", msg.result);
-               return -EINVAL;
+       if (!got_cmd) {
+               count = -EINVAL;
+               goto exit;
        }
 
+       msg->version = 0;
+       msg->command = EC_CMD_REBOOT_EC + ec->cmd_offset;
+       msg->outsize = sizeof(*param);
+       msg->insize = 0;
+       ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
+       if (ret < 0) {
+               count = ret;
+               goto exit;
+       }
+       if (msg->result != EC_RES_SUCCESS) {
+               dev_dbg(ec->dev, "EC result %d\n", msg->result);
+               count = -EINVAL;
+       }
+exit:
+       kfree(msg);
        return count;
 }
 
@@ -123,22 +137,33 @@ static ssize_t show_ec_version(struct device *dev,
        struct ec_response_get_version *r_ver;
        struct ec_response_get_chip_info *r_chip;
        struct ec_response_board_version *r_board;
-       struct cros_ec_command msg = { 0 };
+       struct cros_ec_command *msg;
        int ret;
        int count = 0;
-       struct cros_ec_device *ec = dev_get_drvdata(dev);
+       struct cros_ec_dev *ec = container_of(dev,
+                                             struct cros_ec_dev, class_dev);
+
+       msg = kmalloc(sizeof(*msg) + EC_HOST_PARAM_SIZE, GFP_KERNEL);
+       if (!msg)
+               return -ENOMEM;
 
        /* Get versions. RW may change. */
-       msg.command = EC_CMD_GET_VERSION;
-       msg.insize = sizeof(*r_ver);
-       ret = cros_ec_cmd_xfer(ec, &msg);
-       if (ret < 0)
-               return ret;
-       if (msg.result != EC_RES_SUCCESS)
-               return scnprintf(buf, PAGE_SIZE,
-                                "ERROR: EC returned %d\n", msg.result);
+       msg->version = 0;
+       msg->command = EC_CMD_GET_VERSION + ec->cmd_offset;
+       msg->insize = sizeof(*r_ver);
+       msg->outsize = 0;
+       ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
+       if (ret < 0) {
+               count = ret;
+               goto exit;
+       }
+       if (msg->result != EC_RES_SUCCESS) {
+               count = scnprintf(buf, PAGE_SIZE,
+                                 "ERROR: EC returned %d\n", msg->result);
+               goto exit;
+       }
 
-       r_ver = (struct ec_response_get_version *)msg.indata;
+       r_ver = (struct ec_response_get_version *)msg->data;
        /* Strings should be null-terminated, but let's be sure. */
        r_ver->version_string_ro[sizeof(r_ver->version_string_ro) - 1] = '\0';
        r_ver->version_string_rw[sizeof(r_ver->version_string_rw) - 1] = '\0';
@@ -152,33 +177,33 @@ static ssize_t show_ec_version(struct device *dev,
                            image_names[r_ver->current_image] : "?"));
 
        /* Get build info. */
-       msg.command = EC_CMD_GET_BUILD_INFO;
-       msg.insize = sizeof(msg.indata);
-       ret = cros_ec_cmd_xfer(ec, &msg);
+       msg->command = EC_CMD_GET_BUILD_INFO + ec->cmd_offset;
+       msg->insize = EC_HOST_PARAM_SIZE;
+       ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
        if (ret < 0)
                count += scnprintf(buf + count, PAGE_SIZE - count,
                                   "Build info:    XFER ERROR %d\n", ret);
-       else if (msg.result != EC_RES_SUCCESS)
+       else if (msg->result != EC_RES_SUCCESS)
                count += scnprintf(buf + count, PAGE_SIZE - count,
-                                  "Build info:    EC error %d\n", msg.result);
+                                  "Build info:    EC error %d\n", msg->result);
        else {
-               msg.indata[sizeof(msg.indata) - 1] = '\0';
+               msg->data[sizeof(msg->data) - 1] = '\0';
                count += scnprintf(buf + count, PAGE_SIZE - count,
-                                  "Build info:    %s\n", msg.indata);
+                                  "Build info:    %s\n", msg->data);
        }
 
        /* Get chip info. */
-       msg.command = EC_CMD_GET_CHIP_INFO;
-       msg.insize = sizeof(*r_chip);
-       ret = cros_ec_cmd_xfer(ec, &msg);
+       msg->command = EC_CMD_GET_CHIP_INFO + ec->cmd_offset;
+       msg->insize = sizeof(*r_chip);
+       ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
        if (ret < 0)
                count += scnprintf(buf + count, PAGE_SIZE - count,
                                   "Chip info:     XFER ERROR %d\n", ret);
-       else if (msg.result != EC_RES_SUCCESS)
+       else if (msg->result != EC_RES_SUCCESS)
                count += scnprintf(buf + count, PAGE_SIZE - count,
-                                  "Chip info:     EC error %d\n", msg.result);
+                                  "Chip info:     EC error %d\n", msg->result);
        else {
-               r_chip = (struct ec_response_get_chip_info *)msg.indata;
+               r_chip = (struct ec_response_get_chip_info *)msg->data;
 
                r_chip->vendor[sizeof(r_chip->vendor) - 1] = '\0';
                r_chip->name[sizeof(r_chip->name) - 1] = '\0';
@@ -192,23 +217,25 @@ static ssize_t show_ec_version(struct device *dev,
        }
 
        /* Get board version */
-       msg.command = EC_CMD_GET_BOARD_VERSION;
-       msg.insize = sizeof(*r_board);
-       ret = cros_ec_cmd_xfer(ec, &msg);
+       msg->command = EC_CMD_GET_BOARD_VERSION + ec->cmd_offset;
+       msg->insize = sizeof(*r_board);
+       ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
        if (ret < 0)
                count += scnprintf(buf + count, PAGE_SIZE - count,
                                   "Board version: XFER ERROR %d\n", ret);
-       else if (msg.result != EC_RES_SUCCESS)
+       else if (msg->result != EC_RES_SUCCESS)
                count += scnprintf(buf + count, PAGE_SIZE - count,
-                                  "Board version: EC error %d\n", msg.result);
+                                  "Board version: EC error %d\n", msg->result);
        else {
-               r_board = (struct ec_response_board_version *)msg.indata;
+               r_board = (struct ec_response_board_version *)msg->data;
 
                count += scnprintf(buf + count, PAGE_SIZE - count,
                                   "Board version: %d\n",
                                   r_board->board_version);
        }
 
+exit:
+       kfree(msg);
        return count;
 }
 
@@ -216,27 +243,39 @@ static ssize_t show_ec_flashinfo(struct device *dev,
                                 struct device_attribute *attr, char *buf)
 {
        struct ec_response_flash_info *resp;
-       struct cros_ec_command msg = { 0 };
+       struct cros_ec_command *msg;
        int ret;
-       struct cros_ec_device *ec = dev_get_drvdata(dev);
+       struct cros_ec_dev *ec = container_of(dev,
+                                             struct cros_ec_dev, class_dev);
+
+       msg = kmalloc(sizeof(*msg) + sizeof(*resp), GFP_KERNEL);
+       if (!msg)
+               return -ENOMEM;
 
        /* The flash info shouldn't ever change, but ask each time anyway. */
-       msg.command = EC_CMD_FLASH_INFO;
-       msg.insize = sizeof(*resp);
-       ret = cros_ec_cmd_xfer(ec, &msg);
+       msg->version = 0;
+       msg->command = EC_CMD_FLASH_INFO + ec->cmd_offset;
+       msg->insize = sizeof(*resp);
+       msg->outsize = 0;
+       ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
        if (ret < 0)
-               return ret;
-       if (msg.result != EC_RES_SUCCESS)
-               return scnprintf(buf, PAGE_SIZE,
-                                "ERROR: EC returned %d\n", msg.result);
-
-       resp = (struct ec_response_flash_info *)msg.indata;
-
-       return scnprintf(buf, PAGE_SIZE,
-                        "FlashSize %d\nWriteSize %d\n"
-                        "EraseSize %d\nProtectSize %d\n",
-                        resp->flash_size, resp->write_block_size,
-                        resp->erase_block_size, resp->protect_block_size);
+               goto exit;
+       if (msg->result != EC_RES_SUCCESS) {
+               ret = scnprintf(buf, PAGE_SIZE,
+                               "ERROR: EC returned %d\n", msg->result);
+               goto exit;
+       }
+
+       resp = (struct ec_response_flash_info *)msg->data;
+
+       ret = scnprintf(buf, PAGE_SIZE,
+                       "FlashSize %d\nWriteSize %d\n"
+                       "EraseSize %d\nProtectSize %d\n",
+                       resp->flash_size, resp->write_block_size,
+                       resp->erase_block_size, resp->protect_block_size);
+exit:
+       kfree(msg);
+       return ret;
 }
 
 /* Module initialization */
@@ -252,20 +291,7 @@ static struct attribute *__ec_attrs[] = {
        NULL,
 };
 
-static struct attribute_group ec_attr_group = {
+struct attribute_group cros_ec_attr_group = {
        .attrs = __ec_attrs,
 };
 
-void ec_dev_sysfs_init(struct cros_ec_device *ec)
-{
-       int error;
-
-       error = sysfs_create_group(&ec->vdev->kobj, &ec_attr_group);
-       if (error)
-               pr_warn("failed to create group: %d\n", error);
-}
-
-void ec_dev_sysfs_remove(struct cros_ec_device *ec)
-{
-       sysfs_remove_group(&ec->vdev->kobj, &ec_attr_group);
-}
index f9f205cb1f115a92617fca64002f028d1856faab..7137a077b9a6a6ae4b40f33c7a412c24f1a9112a 100644 (file)
@@ -71,9 +71,10 @@ config ASUS_LAPTOP
        depends on ACPI
        select LEDS_CLASS
        select NEW_LEDS
-       select BACKLIGHT_CLASS_DEVICE
+       depends on BACKLIGHT_CLASS_DEVICE
        depends on INPUT
        depends on RFKILL || RFKILL = n
+       depends on ACPI_VIDEO || ACPI_VIDEO = n
        select INPUT_SPARSEKMAP
        select INPUT_POLLDEV
        ---help---
@@ -95,6 +96,7 @@ config DELL_LAPTOP
        depends on X86
        depends on DCDBAS
        depends on BACKLIGHT_CLASS_DEVICE
+       depends on ACPI_VIDEO || ACPI_VIDEO = n
        depends on RFKILL || RFKILL = n
        depends on SERIO_I8042
        select POWER_SUPPLY
@@ -109,6 +111,7 @@ config DELL_WMI
        tristate "Dell WMI extras"
        depends on ACPI_WMI
        depends on INPUT
+       depends on ACPI_VIDEO || ACPI_VIDEO = n
        select INPUT_SPARSEKMAP
        ---help---
          Say Y here if you want to support WMI-based hotkeys on Dell laptops.
@@ -144,6 +147,7 @@ config FUJITSU_LAPTOP
        depends on ACPI
        depends on INPUT
        depends on BACKLIGHT_CLASS_DEVICE
+       depends on ACPI_VIDEO || ACPI_VIDEO = n
        depends on LEDS_CLASS || LEDS_CLASS=n
        ---help---
          This is a driver for laptops built by Fujitsu:
@@ -247,6 +251,7 @@ config MSI_LAPTOP
        tristate "MSI Laptop Extras"
        depends on ACPI
        depends on BACKLIGHT_CLASS_DEVICE
+       depends on ACPI_VIDEO || ACPI_VIDEO = n
        depends on RFKILL
        depends on INPUT && SERIO_I8042
        select INPUT_SPARSEKMAP
@@ -280,6 +285,7 @@ config COMPAL_LAPTOP
        tristate "Compal (and others) Laptop Extras"
        depends on ACPI
        depends on BACKLIGHT_CLASS_DEVICE
+       depends on ACPI_VIDEO || ACPI_VIDEO = n
        depends on RFKILL
        depends on HWMON
        depends on POWER_SUPPLY
@@ -296,7 +302,8 @@ config COMPAL_LAPTOP
 config SONY_LAPTOP
        tristate "Sony Laptop Extras"
        depends on ACPI
-       select BACKLIGHT_CLASS_DEVICE
+       depends on ACPI_VIDEO || ACPI_VIDEO = n
+       depends on BACKLIGHT_CLASS_DEVICE
        depends on INPUT
        depends on RFKILL
          ---help---
@@ -321,6 +328,7 @@ config IDEAPAD_LAPTOP
        depends on RFKILL && INPUT
        depends on SERIO_I8042
        depends on BACKLIGHT_CLASS_DEVICE
+       depends on ACPI_VIDEO || ACPI_VIDEO = n
        select INPUT_SPARSEKMAP
        help
          This is a driver for Lenovo IdeaPad netbooks contains drivers for
@@ -331,8 +339,8 @@ config THINKPAD_ACPI
        depends on ACPI
        depends on INPUT
        depends on RFKILL || RFKILL = n
-       select BACKLIGHT_LCD_SUPPORT
-       select BACKLIGHT_CLASS_DEVICE
+       depends on ACPI_VIDEO || ACPI_VIDEO = n
+       depends on BACKLIGHT_CLASS_DEVICE
        select HWMON
        select NVRAM
        select NEW_LEDS
@@ -500,8 +508,9 @@ config EEEPC_LAPTOP
        depends on ACPI
        depends on INPUT
        depends on RFKILL || RFKILL = n
+       depends on ACPI_VIDEO || ACPI_VIDEO = n
        depends on HOTPLUG_PCI
-       select BACKLIGHT_CLASS_DEVICE
+       depends on BACKLIGHT_CLASS_DEVICE
        select HWMON
        select LEDS_CLASS
        select NEW_LEDS
@@ -587,6 +596,7 @@ config MSI_WMI
        depends on ACPI_WMI
        depends on INPUT
        depends on BACKLIGHT_CLASS_DEVICE
+       depends on ACPI_VIDEO || ACPI_VIDEO = n
        select INPUT_SPARSEKMAP
        help
         Say Y here if you want to support WMI-based hotkeys on MSI laptops.
@@ -660,7 +670,7 @@ config TOSHIBA_HAPS
        depends on ACPI
        ---help---
          This driver adds support for the built-in accelerometer
-         found on recent Toshiba laptops equiped with HID TOS620A
+         found on recent Toshiba laptops equipped with HID TOS620A
          device.
 
          This driver receives ACPI notify events 0x80 when the sensor
@@ -669,7 +679,7 @@ config TOSHIBA_HAPS
          been stabilized.
 
          Also provides sysfs entries to get/set the desired protection
-         level and reseting the HDD protection interface.
+         level and resetting the HDD protection interface.
 
          If you have a recent Toshiba laptop with a built-in accelerometer
          device, say Y.
@@ -824,6 +834,7 @@ config MXM_WMI
 config INTEL_OAKTRAIL
        tristate "Intel Oaktrail Platform Extras"
        depends on ACPI
+       depends on ACPI_VIDEO || ACPI_VIDEO = n
        depends on RFKILL && BACKLIGHT_CLASS_DEVICE && ACPI
        ---help---
          Intel Oaktrail platform need this driver to provide interfaces to
index 3ac29a1e8f92616c69fcb73c92cca912cea44485..f6b280dbfb3331b847a3d9927b1d7105502f5bd4 100644 (file)
@@ -2246,14 +2246,10 @@ static int __init acer_wmi_init(void)
        set_quirks();
 
        if (dmi_check_system(video_vendor_dmi_table))
-               acpi_video_dmi_promote_vendor();
-       if (acpi_video_backlight_support()) {
+               acpi_video_set_dmi_backlight_type(acpi_backlight_vendor);
+
+       if (acpi_video_get_backlight_type() != acpi_backlight_vendor)
                interface->capability &= ~ACER_CAP_BRIGHTNESS;
-               pr_info("Brightness must be controlled by acpi video driver\n");
-       } else {
-               pr_info("Disabling ACPI video driver\n");
-               acpi_video_unregister_backlight();
-       }
 
        if (wmi_has_guid(WMID_GUID3)) {
                if (ec_raw_mode) {
index 6808715003f6e7e40d6b5bb9d60c6533efff2c26..0dec3f59917a7e3bcb034b242f2d60be7e7f98cb 100644 (file)
@@ -550,8 +550,7 @@ static int gmux_probe(struct pnp_dev *pnp, const struct pnp_device_id *id)
         * backlight control and supports more levels than other options.
         * Disable the other backlight choices.
         */
-       acpi_video_dmi_promote_vendor();
-       acpi_video_unregister();
+       acpi_video_set_dmi_backlight_type(acpi_backlight_vendor);
        apple_bl_unregister();
 
        gmux_data->power_state = VGA_SWITCHEROO_ON;
@@ -645,7 +644,6 @@ static void gmux_remove(struct pnp_dev *pnp)
        apple_gmux_data = NULL;
        kfree(gmux_data);
 
-       acpi_video_dmi_demote_vendor();
        acpi_video_register();
        apple_bl_register();
 }
index 46b27469387283eed295e664a6c728a639860fa5..58d29c4f2840c4974ab204aa63f15cf3ad961a56 100644 (file)
@@ -54,6 +54,7 @@
 #include <linux/slab.h>
 #include <linux/dmi.h>
 #include <linux/acpi.h>
+#include <acpi/video.h>
 
 #define ASUS_LAPTOP_VERSION    "0.42"
 
@@ -1884,12 +1885,11 @@ static int asus_acpi_add(struct acpi_device *device)
        if (result)
                goto fail_platform;
 
-       if (!acpi_video_backlight_support()) {
+       if (acpi_video_get_backlight_type() == acpi_backlight_vendor) {
                result = asus_backlight_init(asus);
                if (result)
                        goto fail_backlight;
-       } else
-               pr_info("Backlight controlled by ACPI video driver\n");
+       }
 
        result = asus_input_init(asus);
        if (result)
index 7543a56e0f4593c74fa74caee310a0ddbce4062c..6f8558f744a4e275df3ffda283c2b97d3232efd5 100644 (file)
@@ -1364,7 +1364,7 @@ static void asus_wmi_notify(u32 value, void *context)
                code = ASUS_WMI_BRN_DOWN;
 
        if (code == ASUS_WMI_BRN_DOWN || code == ASUS_WMI_BRN_UP) {
-               if (!acpi_video_backlight_support()) {
+               if (acpi_video_get_backlight_type() == acpi_backlight_vendor) {
                        asus_wmi_backlight_notify(asus, orig_code);
                        goto exit;
                }
@@ -1772,17 +1772,16 @@ static int asus_wmi_add(struct platform_device *pdev)
           stop this from showing up */
        chassis_type = dmi_get_system_info(DMI_CHASSIS_TYPE);
        if (chassis_type && !strcmp(chassis_type, "3"))
-               acpi_video_dmi_promote_vendor();
+               acpi_video_set_dmi_backlight_type(acpi_backlight_vendor);
+
        if (asus->driver->quirks->wmi_backlight_power)
-               acpi_video_dmi_promote_vendor();
-       if (!acpi_video_backlight_support()) {
-               pr_info("Disabling ACPI video driver\n");
-               acpi_video_unregister();
+               acpi_video_set_dmi_backlight_type(acpi_backlight_vendor);
+
+       if (acpi_video_get_backlight_type() == acpi_backlight_vendor) {
                err = asus_wmi_backlight_init(asus);
                if (err && err != -ENODEV)
                        goto fail_backlight;
-       } else
-               pr_info("Backlight controlled by ACPI video driver\n");
+       }
 
        status = wmi_install_notify_handler(asus->driver->event_guid,
                                            asus_wmi_notify, asus);
index b4e94471f3d57e049b99893cc156729e02bf255c..f2706d27adff39fe93f77ff7a4204ef34664d928 100644 (file)
@@ -82,7 +82,7 @@
 #include <linux/hwmon-sysfs.h>
 #include <linux/power_supply.h>
 #include <linux/fb.h>
-
+#include <acpi/video.h>
 
 /* ======= */
 /* Defines */
@@ -959,7 +959,7 @@ static int __init compal_init(void)
                return -ENODEV;
        }
 
-       if (!acpi_video_backlight_support()) {
+       if (acpi_video_get_backlight_type() == acpi_backlight_vendor) {
                struct backlight_properties props;
                memset(&props, 0, sizeof(struct backlight_properties));
                props.type = BACKLIGHT_PLATFORM;
index d688d806a8a51d9845ab9c5c5bc3322e748738f3..01d081052b508b795580cfec441fa0f8201b7d75 100644 (file)
@@ -31,6 +31,7 @@
 #include <linux/slab.h>
 #include <linux/debugfs.h>
 #include <linux/seq_file.h>
+#include <acpi/video.h>
 #include "../../firmware/dcdbas.h"
 
 #define BRIGHTNESS_TOKEN 0x7d
@@ -1920,13 +1921,8 @@ static int __init dell_init(void)
                debugfs_create_file("rfkill", 0444, dell_laptop_dir, NULL,
                                    &dell_debugfs_fops);
 
-#ifdef CONFIG_ACPI
-       /* In the event of an ACPI backlight being available, don't
-        * register the platform controller.
-        */
-       if (acpi_video_backlight_support())
+       if (acpi_video_get_backlight_type() != acpi_backlight_vendor)
                return 0;
-#endif
 
        get_buffer();
        buffer->input[0] = find_token_location(BRIGHTNESS_TOKEN);
index 6512a06bc0535d28e312ae7a697b6507de5dec40..f2d77fe696ac6577b605fb63e6a3b587d2121e74 100644 (file)
@@ -35,6 +35,7 @@
 #include <linux/acpi.h>
 #include <linux/string.h>
 #include <linux/dmi.h>
+#include <acpi/video.h>
 
 MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>");
 MODULE_DESCRIPTION("Dell laptop WMI hotkeys driver");
@@ -397,7 +398,7 @@ static int __init dell_wmi_init(void)
        }
 
        dmi_walk(find_hk_type, NULL);
-       acpi_video = acpi_video_backlight_support();
+       acpi_video = acpi_video_get_backlight_type() != acpi_backlight_vendor;
 
        err = dell_wmi_input_setup();
        if (err)
index 844c2096bde9244c3daaf504892f2bbcefad3db0..8cdf315f9730abd35fdbc9ed85b8f0237763a5c4 100644 (file)
@@ -37,6 +37,7 @@
 #include <linux/pci_hotplug.h>
 #include <linux/leds.h>
 #include <linux/dmi.h>
+#include <acpi/video.h>
 
 #define EEEPC_LAPTOP_VERSION   "0.1"
 #define EEEPC_LAPTOP_NAME      "Eee PC Hotkey Driver"
@@ -1433,12 +1434,10 @@ static int eeepc_acpi_add(struct acpi_device *device)
        if (result)
                goto fail_platform;
 
-       if (!acpi_video_backlight_support()) {
+       if (acpi_video_get_backlight_type() == acpi_backlight_vendor) {
                result = eeepc_backlight_init(eeepc);
                if (result)
                        goto fail_backlight;
-       } else {
-               pr_info("Backlight controlled by ACPI video driver\n");
        }
 
        result = eeepc_input_init(eeepc);
index 2a9afa261c615bffb1f1586f2fcb3a287fd33d4b..1c62caff93fdfce5e75924a84eb33a5af9fc00ae 100644 (file)
@@ -72,6 +72,7 @@
 #if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE)
 #include <linux/leds.h>
 #endif
+#include <acpi/video.h>
 
 #define FUJITSU_DRIVER_VERSION "0.6.0"
 
@@ -1099,7 +1100,7 @@ static int __init fujitsu_init(void)
 
        /* Register backlight stuff */
 
-       if (!acpi_video_backlight_support()) {
+       if (acpi_video_get_backlight_type() == acpi_backlight_vendor) {
                struct backlight_properties props;
 
                memset(&props, 0, sizeof(struct backlight_properties));
@@ -1137,8 +1138,7 @@ static int __init fujitsu_init(void)
        }
 
        /* Sync backlight power status (needs FUJ02E3 device, hence deferred) */
-
-       if (!acpi_video_backlight_support()) {
+       if (acpi_video_get_backlight_type() == acpi_backlight_vendor) {
                if (call_fext_func(FUNC_BACKLIGHT, 0x2, 0x4, 0x0) == 3)
                        fujitsu->bl_device->props.power = FB_BLANK_POWERDOWN;
                else
index b496db87bc0505368fe4501b6199f653f7a257e0..bea0228309443e1031607b09e0eef85e45b6011e 100644 (file)
@@ -38,6 +38,7 @@
 #include <linux/i8042.h>
 #include <linux/dmi.h>
 #include <linux/device.h>
+#include <acpi/video.h>
 
 #define IDEAPAD_RFKILL_DEV_NUM (3)
 
@@ -903,7 +904,7 @@ static int ideapad_acpi_add(struct platform_device *pdev)
        ideapad_sync_rfk_state(priv);
        ideapad_sync_touchpad_state(priv);
 
-       if (!acpi_video_backlight_support()) {
+       if (acpi_video_get_backlight_type() == acpi_backlight_vendor) {
                ret = ideapad_backlight_init(priv);
                if (ret && ret != -ENODEV)
                        goto backlight_failed;
index 8037c8b46241e12e615e1f634774d053877ae673..6aa33c4a809fedb28e727d00cf599defd2156e23 100644 (file)
@@ -50,6 +50,7 @@
 #include <linux/platform_device.h>
 #include <linux/dmi.h>
 #include <linux/rfkill.h>
+#include <acpi/video.h>
 
 #define DRIVER_NAME    "intel_oaktrail"
 #define DRIVER_VERSION "0.4ac1"
@@ -343,13 +344,11 @@ static int __init oaktrail_init(void)
                goto err_device_add;
        }
 
-       if (!acpi_video_backlight_support()) {
+       if (acpi_video_get_backlight_type() == acpi_backlight_vendor) {
                ret = oaktrail_backlight_init();
                if (ret)
                        goto err_backlight;
-
-       } else
-               pr_info("Backlight controlled by ACPI video driver\n");
+       }
 
        ret = oaktrail_rfkill_init();
        if (ret) {
index 085987730aabbe6ac6e15f588ad11b04ce311e39..42317704629dba4f5b35ee1ac1c8aaa7b85ae3c1 100644 (file)
@@ -64,6 +64,7 @@
 #include <linux/i8042.h>
 #include <linux/input.h>
 #include <linux/input/sparse-keymap.h>
+#include <acpi/video.h>
 
 #define MSI_DRIVER_VERSION "0.5"
 
@@ -1069,9 +1070,8 @@ static int __init msi_init(void)
 
        /* Register backlight stuff */
 
-       if (!quirks->old_ec_model || acpi_video_backlight_support()) {
-               pr_info("Brightness ignored, must be controlled by ACPI video driver\n");
-       } else {
+       if (quirks->old_ec_model ||
+           acpi_video_get_backlight_type() == acpi_backlight_vendor) {
                struct backlight_properties props;
                memset(&props, 0, sizeof(struct backlight_properties));
                props.type = BACKLIGHT_PLATFORM;
index 6d2bac0c463cd41f17eef2e4cfe41e16cc4f2658..978e6d6405729cb32391f73c65ac23a1b0eeebef 100644 (file)
@@ -29,6 +29,7 @@
 #include <linux/backlight.h>
 #include <linux/slab.h>
 #include <linux/module.h>
+#include <acpi/video.h>
 
 MODULE_AUTHOR("Thomas Renninger <trenn@suse.de>");
 MODULE_DESCRIPTION("MSI laptop WMI hotkeys driver");
@@ -320,7 +321,8 @@ static int __init msi_wmi_init(void)
                break;
        }
 
-       if (wmi_has_guid(MSIWMI_BIOS_GUID) && !acpi_video_backlight_support()) {
+       if (wmi_has_guid(MSIWMI_BIOS_GUID) &&
+           acpi_video_get_backlight_type() == acpi_backlight_vendor) {
                err = msi_wmi_backlight_setup();
                if (err) {
                        pr_err("Unable to setup backlight device\n");
index 9e701b2256f9571afca37e01a7185fe8e63e8399..8c146e2b6727acd818a38c937bc37d648f33ef38 100644 (file)
@@ -1720,27 +1720,14 @@ static int __init samsung_init(void)
        samsung->handle_backlight = true;
        samsung->quirks = quirks;
 
-
 #ifdef CONFIG_ACPI
        if (samsung->quirks->broken_acpi_video)
-               acpi_video_dmi_promote_vendor();
-
-       /* Don't handle backlight here if the acpi video already handle it */
-       if (acpi_video_backlight_support()) {
-               samsung->handle_backlight = false;
-       } else if (samsung->quirks->broken_acpi_video) {
-               pr_info("Disabling ACPI video driver\n");
-               acpi_video_unregister();
-       }
+               acpi_video_set_dmi_backlight_type(acpi_backlight_vendor);
+       if (samsung->quirks->use_native_backlight)
+               acpi_video_set_dmi_backlight_type(acpi_backlight_native);
 
-       if (samsung->quirks->use_native_backlight) {
-               pr_info("Using native backlight driver\n");
-               /* Tell acpi-video to not handle the backlight */
-               acpi_video_dmi_promote_vendor();
-               acpi_video_unregister();
-               /* And also do not handle it ourselves */
+       if (acpi_video_get_backlight_type() != acpi_backlight_vendor)
                samsung->handle_backlight = false;
-       }
 #endif
 
        ret = samsung_platform_init(samsung);
@@ -1751,12 +1738,6 @@ static int __init samsung_init(void)
        if (ret)
                goto error_sabi;
 
-#ifdef CONFIG_ACPI
-       /* Only log that if we are really on a sabi platform */
-       if (acpi_video_backlight_support())
-               pr_info("Backlight controlled by ACPI video driver\n");
-#endif
-
        ret = samsung_sysfs_init(samsung);
        if (ret)
                goto error_sysfs;
index e51c1e7536077306234e579121cf18badcb45a01..aeb80d1c2b07e7d8d9ef17e540bab09c7c48a139 100644 (file)
@@ -69,6 +69,7 @@
 #include <linux/miscdevice.h>
 #endif
 #include <asm/uaccess.h>
+#include <acpi/video.h>
 
 #define dprintk(fmt, ...)                      \
 do {                                           \
@@ -3198,12 +3199,8 @@ static int sony_nc_add(struct acpi_device *device)
                        sony_nc_function_setup(device, sony_pf_device);
        }
 
-       /* setup input devices and helper fifo */
-       if (acpi_video_backlight_support()) {
-               pr_info("brightness ignored, must be controlled by ACPI video driver\n");
-       } else {
+       if (acpi_video_get_backlight_type() == acpi_backlight_vendor)
                sony_nc_backlight_setup();
-       }
 
        /* create sony_pf sysfs attributes related to the SNC device */
        for (item = sony_nc_values; item->name; ++item) {
index 28f328136f0df78fe3253d6996ae9108229f87e5..33e488cf5569861391fc40844bcc6ce95b305c3c 100644 (file)
@@ -83,6 +83,7 @@
 #include <sound/control.h>
 #include <sound/initval.h>
 #include <asm/uaccess.h>
+#include <acpi/video.h>
 
 /* ThinkPad CMOS commands */
 #define TP_CMOS_VOLUME_DOWN    0
@@ -3487,7 +3488,7 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
        /* Do not issue duplicate brightness change events to
         * userspace. tpacpi_detect_brightness_capabilities() must have
         * been called before this point  */
-       if (acpi_video_backlight_support()) {
+       if (acpi_video_get_backlight_type() != acpi_backlight_vendor) {
                pr_info("This ThinkPad has standard ACPI backlight "
                        "brightness control, supported by the ACPI "
                        "video driver\n");
@@ -6491,7 +6492,7 @@ static int __init brightness_init(struct ibm_init_struct *iibm)
                return 1;
        }
 
-       if (acpi_video_backlight_support()) {
+       if (acpi_video_get_backlight_type() != acpi_backlight_vendor) {
                if (brightness_enable > 1) {
                        pr_info("Standard ACPI backlight interface "
                                "available, not loading native one\n");
index 9956b9902bb40aaeb59e724b60579e56a495795e..59bf27ed72d63a1e7d1fa253452081277473d5ee 100644 (file)
@@ -2640,14 +2640,11 @@ static int toshiba_acpi_setup_backlight(struct toshiba_acpi_dev *dev)
         */
        if (dev->tr_backlight_supported ||
            dmi_check_system(toshiba_vendor_backlight_dmi))
-               acpi_video_dmi_promote_vendor();
+               acpi_video_set_dmi_backlight_type(acpi_backlight_vendor);
 
-       if (acpi_video_backlight_support())
+       if (acpi_video_get_backlight_type() != acpi_backlight_vendor)
                return 0;
 
-       /* acpi-video may have loaded before we called dmi_promote_vendor() */
-       acpi_video_unregister_backlight();
-
        memset(&props, 0, sizeof(props));
        props.type = BACKLIGHT_PLATFORM;
        props.max_brightness = HCI_LCD_BRIGHTNESS_LEVELS - 1;
index ff0356fb378ff514984fac72383f78a01ed93487..05796495be0e4ada9ba9b2995be1698c79fed1e8 100644 (file)
@@ -28,8 +28,8 @@
 #include "../base.h"
 #include "pnpacpi.h"
 
-static void decode_irq_flags(struct pnp_dev *dev, int flags, int *triggering,
-                            int *polarity, int *shareable)
+static void decode_irq_flags(struct pnp_dev *dev, int flags, u8 *triggering,
+                            u8 *polarity, u8 *shareable)
 {
        switch (flags & (IORESOURCE_IRQ_LOWLEVEL | IORESOURCE_IRQ_HIGHLEVEL |
                         IORESOURCE_IRQ_LOWEDGE  | IORESOURCE_IRQ_HIGHEDGE)) {
@@ -654,7 +654,7 @@ static void pnpacpi_encode_irq(struct pnp_dev *dev,
                               struct resource *p)
 {
        struct acpi_resource_irq *irq = &resource->data.irq;
-       int triggering, polarity, shareable;
+       u8 triggering, polarity, shareable;
 
        if (!pnp_resource_enabled(p)) {
                irq->interrupt_count = 0;
@@ -683,7 +683,7 @@ static void pnpacpi_encode_ext_irq(struct pnp_dev *dev,
                                   struct resource *p)
 {
        struct acpi_resource_extended_irq *extended_irq = &resource->data.extended_irq;
-       int triggering, polarity, shareable;
+       u8 triggering, polarity, shareable;
 
        if (!pnp_resource_enabled(p)) {
                extended_irq->interrupt_count = 0;
@@ -873,7 +873,7 @@ int pnpacpi_encode_resources(struct pnp_dev *dev, struct acpi_buffer *buffer)
        /* pnpacpi_build_resource_template allocates extra mem */
        int res_cnt = (buffer->length - 1) / sizeof(struct acpi_resource) - 1;
        struct acpi_resource *resource = buffer->pointer;
-       int port = 0, irq = 0, dma = 0, mem = 0;
+       unsigned int port = 0, irq = 0, dma = 0, mem = 0;
 
        pnp_dbg(&dev->dev, "encode %d resources\n", res_cnt);
        while (i < res_cnt) {
index 49c1720df59a8550cdca31735e6ef519f6b88266..515f33882ab89ae27ea4d7006e818fa8b15fffb3 100644 (file)
@@ -7,6 +7,7 @@
  *     Bjorn Helgaas <bjorn.helgaas@hp.com>
  */
 
+#include <linux/acpi.h>
 #include <linux/pnp.h>
 #include <linux/device.h>
 #include <linux/init.h>
@@ -22,25 +23,41 @@ static const struct pnp_device_id pnp_dev_table[] = {
        {"", 0}
 };
 
+#ifdef CONFIG_ACPI
+static bool __reserve_range(u64 start, unsigned int length, bool io, char *desc)
+{
+       u8 space_id = io ? ACPI_ADR_SPACE_SYSTEM_IO : ACPI_ADR_SPACE_SYSTEM_MEMORY;
+       return !acpi_reserve_region(start, length, space_id, IORESOURCE_BUSY, desc);
+}
+#else
+static bool __reserve_range(u64 start, unsigned int length, bool io, char *desc)
+{
+       struct resource *res;
+
+       res = io ? request_region(start, length, desc) :
+               request_mem_region(start, length, desc);
+       if (res) {
+               res->flags &= ~IORESOURCE_BUSY;
+               return true;
+       }
+       return false;
+}
+#endif
+
 static void reserve_range(struct pnp_dev *dev, struct resource *r, int port)
 {
        char *regionid;
        const char *pnpid = dev_name(&dev->dev);
        resource_size_t start = r->start, end = r->end;
-       struct resource *res;
+       bool reserved;
 
        regionid = kmalloc(16, GFP_KERNEL);
        if (!regionid)
                return;
 
        snprintf(regionid, 16, "pnp %s", pnpid);
-       if (port)
-               res = request_region(start, end - start + 1, regionid);
-       else
-               res = request_mem_region(start, end - start + 1, regionid);
-       if (res)
-               res->flags &= ~IORESOURCE_BUSY;
-       else
+       reserved = __reserve_range(start, end - start + 1, !!port, regionid);
+       if (!reserved)
                kfree(regionid);
 
        /*
@@ -49,7 +66,7 @@ static void reserve_range(struct pnp_dev *dev, struct resource *r, int port)
         * have double reservations.
         */
        dev_info(&dev->dev, "%pR %s reserved\n", r,
-                res ? "has been" : "could not be");
+                reserved ? "has been" : "could not be");
 }
 
 static void reserve_resources_of_dev(struct pnp_dev *dev)
index 0e448c68c02bb9e4e6cba0a428e59b29e5231e68..297e72dc70e603aede8caa31d6f8d3cec22e8a61 100644 (file)
@@ -742,7 +742,6 @@ static int pm860x_charger_remove(struct platform_device *pdev)
        int i;
 
        power_supply_unregister(info->usb);
-       free_irq(info->irq[0], info);
        for (i = 0; i < info->irq_nums; i++)
                free_irq(info->irq[i], info);
        return 0;
index 4091fb092d067aca615a376452db4814a75e0aa6..08beeed5485d3067be3093633a1d3173ffab44c2 100644 (file)
@@ -204,6 +204,13 @@ config CHARGER_DA9150
          This driver can also be built as a module. If so, the module will be
          called da9150-charger.
 
+config AXP288_CHARGER
+       tristate "X-Powers AXP288 Charger"
+       depends on MFD_AXP20X && EXTCON_AXP288
+       help
+         Say yes here to have support X-Power AXP288 power management IC (PMIC)
+         integrated charger.
+
 config AXP288_FUEL_GAUGE
        tristate "X-Powers AXP288 Fuel Gauge"
        depends on MFD_AXP20X && IIO
@@ -388,12 +395,26 @@ config CHARGER_BQ24190
        help
          Say Y to enable support for the TI BQ24190 battery charger.
 
+config CHARGER_BQ24257
+       tristate "TI BQ24257 battery charger driver"
+       depends on I2C && GPIOLIB
+       depends on REGMAP_I2C
+       help
+         Say Y to enable support for the TI BQ24257 battery charger.
+
 config CHARGER_BQ24735
        tristate "TI BQ24735 battery charger support"
        depends on I2C && GPIOLIB
        help
          Say Y to enable support for the TI BQ24735 battery charger.
 
+config CHARGER_BQ25890
+       tristate "TI BQ25890 battery charger driver"
+       depends on I2C && GPIOLIB
+       select REGMAP_I2C
+       help
+         Say Y to enable support for the TI BQ25890 battery charger.
+
 config CHARGER_SMB347
        tristate "Summit Microelectronics SMB347 Battery Charger"
        depends on I2C
@@ -439,6 +460,13 @@ config BATTERY_RT5033
          The fuelgauge calculates and determines the battery state of charge
          according to battery open circuit voltage.
 
+config CHARGER_RT9455
+       tristate "Richtek RT9455 battery charger driver"
+       depends on I2C && GPIOLIB
+       select REGMAP_I2C
+       help
+         Say Y to enable support for Richtek RT9455 battery charger.
+
 source "drivers/power/reset/Kconfig"
 
 endif # POWER_SUPPLY
index b7b0181c95e583e453de6210fd0a87d0a9d5f6fc..5752ce818f5157657ea93b5cc3873b38aaf5c61b 100644 (file)
@@ -37,6 +37,7 @@ obj-$(CONFIG_BATTERY_MAX17040)        += max17040_battery.o
 obj-$(CONFIG_BATTERY_MAX17042) += max17042_battery.o
 obj-$(CONFIG_BATTERY_Z2)       += z2_battery.o
 obj-$(CONFIG_BATTERY_RT5033)   += rt5033_battery.o
+obj-$(CONFIG_CHARGER_RT9455)   += rt9455_charger.o
 obj-$(CONFIG_BATTERY_S3C_ADC)  += s3c_adc_battery.o
 obj-$(CONFIG_BATTERY_TWL4030_MADC)     += twl4030_madc_battery.o
 obj-$(CONFIG_CHARGER_88PM860X) += 88pm860x_charger.o
@@ -58,9 +59,12 @@ obj-$(CONFIG_CHARGER_MAX8997)        += max8997_charger.o
 obj-$(CONFIG_CHARGER_MAX8998)  += max8998_charger.o
 obj-$(CONFIG_CHARGER_BQ2415X)  += bq2415x_charger.o
 obj-$(CONFIG_CHARGER_BQ24190)  += bq24190_charger.o
+obj-$(CONFIG_CHARGER_BQ24257)  += bq24257_charger.o
 obj-$(CONFIG_CHARGER_BQ24735)  += bq24735-charger.o
+obj-$(CONFIG_CHARGER_BQ25890)  += bq25890_charger.o
 obj-$(CONFIG_POWER_AVS)                += avs/
 obj-$(CONFIG_CHARGER_SMB347)   += smb347-charger.o
 obj-$(CONFIG_CHARGER_TPS65090) += tps65090-charger.o
 obj-$(CONFIG_POWER_RESET)      += reset/
 obj-$(CONFIG_AXP288_FUEL_GAUGE) += axp288_fuel_gauge.o
+obj-$(CONFIG_AXP288_CHARGER)   += axp288_charger.o
diff --git a/drivers/power/axp288_charger.c b/drivers/power/axp288_charger.c
new file mode 100644 (file)
index 0000000..5680317
--- /dev/null
@@ -0,0 +1,941 @@
+/*
+ * axp288_charger.c - X-power AXP288 PMIC Charger driver
+ *
+ * Copyright (C) 2014 Intel Corporation
+ * Author: Ramakrishna Pallala <ramakrishna.pallala@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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/device.h>
+#include <linux/regmap.h>
+#include <linux/workqueue.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/usb/otg.h>
+#include <linux/notifier.h>
+#include <linux/power_supply.h>
+#include <linux/notifier.h>
+#include <linux/property.h>
+#include <linux/mfd/axp20x.h>
+#include <linux/extcon.h>
+
+#define PS_STAT_VBUS_TRIGGER           (1 << 0)
+#define PS_STAT_BAT_CHRG_DIR           (1 << 2)
+#define PS_STAT_VBAT_ABOVE_VHOLD       (1 << 3)
+#define PS_STAT_VBUS_VALID             (1 << 4)
+#define PS_STAT_VBUS_PRESENT           (1 << 5)
+
+#define CHRG_STAT_BAT_SAFE_MODE                (1 << 3)
+#define CHRG_STAT_BAT_VALID            (1 << 4)
+#define CHRG_STAT_BAT_PRESENT          (1 << 5)
+#define CHRG_STAT_CHARGING             (1 << 6)
+#define CHRG_STAT_PMIC_OTP             (1 << 7)
+
+#define VBUS_ISPOUT_CUR_LIM_MASK       0x03
+#define VBUS_ISPOUT_CUR_LIM_BIT_POS    0
+#define VBUS_ISPOUT_CUR_LIM_900MA      0x0     /* 900mA */
+#define VBUS_ISPOUT_CUR_LIM_1500MA     0x1     /* 1500mA */
+#define VBUS_ISPOUT_CUR_LIM_2000MA     0x2     /* 2000mA */
+#define VBUS_ISPOUT_CUR_NO_LIM         0x3     /* 2500mA */
+#define VBUS_ISPOUT_VHOLD_SET_MASK     0x31
+#define VBUS_ISPOUT_VHOLD_SET_BIT_POS  0x3
+#define VBUS_ISPOUT_VHOLD_SET_OFFSET   4000    /* 4000mV */
+#define VBUS_ISPOUT_VHOLD_SET_LSB_RES  100     /* 100mV */
+#define VBUS_ISPOUT_VHOLD_SET_4300MV   0x3     /* 4300mV */
+#define VBUS_ISPOUT_VBUS_PATH_DIS      (1 << 7)
+
+#define CHRG_CCCV_CC_MASK              0xf             /* 4 bits */
+#define CHRG_CCCV_CC_BIT_POS           0
+#define CHRG_CCCV_CC_OFFSET            200             /* 200mA */
+#define CHRG_CCCV_CC_LSB_RES           200             /* 200mA */
+#define CHRG_CCCV_ITERM_20P            (1 << 4)        /* 20% of CC */
+#define CHRG_CCCV_CV_MASK              0x60            /* 2 bits */
+#define CHRG_CCCV_CV_BIT_POS           5
+#define CHRG_CCCV_CV_4100MV            0x0             /* 4.10V */
+#define CHRG_CCCV_CV_4150MV            0x1             /* 4.15V */
+#define CHRG_CCCV_CV_4200MV            0x2             /* 4.20V */
+#define CHRG_CCCV_CV_4350MV            0x3             /* 4.35V */
+#define CHRG_CCCV_CHG_EN               (1 << 7)
+
+#define CNTL2_CC_TIMEOUT_MASK          0x3     /* 2 bits */
+#define CNTL2_CC_TIMEOUT_OFFSET                6       /* 6 Hrs */
+#define CNTL2_CC_TIMEOUT_LSB_RES       2       /* 2 Hrs */
+#define CNTL2_CC_TIMEOUT_12HRS         0x3     /* 12 Hrs */
+#define CNTL2_CHGLED_TYPEB             (1 << 4)
+#define CNTL2_CHG_OUT_TURNON           (1 << 5)
+#define CNTL2_PC_TIMEOUT_MASK          0xC0
+#define CNTL2_PC_TIMEOUT_OFFSET                40      /* 40 mins */
+#define CNTL2_PC_TIMEOUT_LSB_RES       10      /* 10 mins */
+#define CNTL2_PC_TIMEOUT_70MINS                0x3
+
+#define CHRG_ILIM_TEMP_LOOP_EN         (1 << 3)
+#define CHRG_VBUS_ILIM_MASK            0xf0
+#define CHRG_VBUS_ILIM_BIT_POS         4
+#define CHRG_VBUS_ILIM_100MA           0x0     /* 100mA */
+#define CHRG_VBUS_ILIM_500MA           0x1     /* 500mA */
+#define CHRG_VBUS_ILIM_900MA           0x2     /* 900mA */
+#define CHRG_VBUS_ILIM_1500MA          0x3     /* 1500mA */
+#define CHRG_VBUS_ILIM_2000MA          0x4     /* 2000mA */
+#define CHRG_VBUS_ILIM_2500MA          0x5     /* 2500mA */
+#define CHRG_VBUS_ILIM_3000MA          0x6     /* 3000mA */
+
+#define CHRG_VLTFC_0C                  0xA5    /* 0 DegC */
+#define CHRG_VHTFC_45C                 0x1F    /* 45 DegC */
+
+#define BAT_IRQ_CFG_CHRG_DONE          (1 << 2)
+#define BAT_IRQ_CFG_CHRG_START         (1 << 3)
+#define BAT_IRQ_CFG_BAT_SAFE_EXIT      (1 << 4)
+#define BAT_IRQ_CFG_BAT_SAFE_ENTER     (1 << 5)
+#define BAT_IRQ_CFG_BAT_DISCON         (1 << 6)
+#define BAT_IRQ_CFG_BAT_CONN           (1 << 7)
+#define BAT_IRQ_CFG_BAT_MASK           0xFC
+
+#define TEMP_IRQ_CFG_QCBTU             (1 << 4)
+#define TEMP_IRQ_CFG_CBTU              (1 << 5)
+#define TEMP_IRQ_CFG_QCBTO             (1 << 6)
+#define TEMP_IRQ_CFG_CBTO              (1 << 7)
+#define TEMP_IRQ_CFG_MASK              0xF0
+
+#define FG_CNTL_OCV_ADJ_EN             (1 << 3)
+
+#define CV_4100MV                      4100    /* 4100mV */
+#define CV_4150MV                      4150    /* 4150mV */
+#define CV_4200MV                      4200    /* 4200mV */
+#define CV_4350MV                      4350    /* 4350mV */
+
+#define CC_200MA                       200     /*  200mA */
+#define CC_600MA                       600     /*  600mA */
+#define CC_800MA                       800     /*  800mA */
+#define CC_1000MA                      1000    /* 1000mA */
+#define CC_1600MA                      1600    /* 1600mA */
+#define CC_2000MA                      2000    /* 2000mA */
+
+#define ILIM_100MA                     100     /* 100mA */
+#define ILIM_500MA                     500     /* 500mA */
+#define ILIM_900MA                     900     /* 900mA */
+#define ILIM_1500MA                    1500    /* 1500mA */
+#define ILIM_2000MA                    2000    /* 2000mA */
+#define ILIM_2500MA                    2500    /* 2500mA */
+#define ILIM_3000MA                    3000    /* 3000mA */
+
+#define AXP288_EXTCON_DEV_NAME         "axp288_extcon"
+
+#define AXP288_EXTCON_SLOW_CHARGER             "SLOW-CHARGER"
+#define AXP288_EXTCON_DOWNSTREAM_CHARGER       "CHARGE-DOWNSTREAM"
+#define AXP288_EXTCON_FAST_CHARGER             "FAST-CHARGER"
+
+enum {
+       VBUS_OV_IRQ = 0,
+       CHARGE_DONE_IRQ,
+       CHARGE_CHARGING_IRQ,
+       BAT_SAFE_QUIT_IRQ,
+       BAT_SAFE_ENTER_IRQ,
+       QCBTU_IRQ,
+       CBTU_IRQ,
+       QCBTO_IRQ,
+       CBTO_IRQ,
+       CHRG_INTR_END,
+};
+
+struct axp288_chrg_info {
+       struct platform_device *pdev;
+       struct axp20x_chrg_pdata *pdata;
+       struct regmap *regmap;
+       struct regmap_irq_chip_data *regmap_irqc;
+       int irq[CHRG_INTR_END];
+       struct power_supply *psy_usb;
+       struct mutex lock;
+
+       /* OTG/Host mode */
+       struct {
+               struct work_struct work;
+               struct extcon_specific_cable_nb cable;
+               struct notifier_block id_nb;
+               bool id_short;
+       } otg;
+
+       /* SDP/CDP/DCP USB charging cable notifications */
+       struct {
+               struct extcon_dev *edev;
+               bool connected;
+               enum power_supply_type chg_type;
+               struct notifier_block nb;
+               struct work_struct work;
+       } cable;
+
+       int health;
+       int inlmt;
+       int cc;
+       int cv;
+       int max_cc;
+       int max_cv;
+       bool online;
+       bool present;
+       bool enable_charger;
+       bool is_charger_enabled;
+};
+
+static inline int axp288_charger_set_cc(struct axp288_chrg_info *info, int cc)
+{
+       u8 reg_val;
+       int ret;
+
+       if (cc < CHRG_CCCV_CC_OFFSET)
+               cc = CHRG_CCCV_CC_OFFSET;
+       else if (cc > info->max_cc)
+               cc = info->max_cc;
+
+       reg_val = (cc - CHRG_CCCV_CC_OFFSET) / CHRG_CCCV_CC_LSB_RES;
+       cc = (reg_val * CHRG_CCCV_CC_LSB_RES) + CHRG_CCCV_CC_OFFSET;
+       reg_val = reg_val << CHRG_CCCV_CC_BIT_POS;
+
+       ret = regmap_update_bits(info->regmap,
+                               AXP20X_CHRG_CTRL1,
+                               CHRG_CCCV_CC_MASK, reg_val);
+       if (ret >= 0)
+               info->cc = cc;
+
+       return ret;
+}
+
+static inline int axp288_charger_set_cv(struct axp288_chrg_info *info, int cv)
+{
+       u8 reg_val;
+       int ret;
+
+       if (cv <= CV_4100MV) {
+               reg_val = CHRG_CCCV_CV_4100MV;
+               cv = CV_4100MV;
+       } else if (cv <= CV_4150MV) {
+               reg_val = CHRG_CCCV_CV_4150MV;
+               cv = CV_4150MV;
+       } else if (cv <= CV_4200MV) {
+               reg_val = CHRG_CCCV_CV_4200MV;
+               cv = CV_4200MV;
+       } else {
+               reg_val = CHRG_CCCV_CV_4350MV;
+               cv = CV_4350MV;
+       }
+
+       reg_val = reg_val << CHRG_CCCV_CV_BIT_POS;
+
+       ret = regmap_update_bits(info->regmap,
+                               AXP20X_CHRG_CTRL1,
+                               CHRG_CCCV_CV_MASK, reg_val);
+
+       if (ret >= 0)
+               info->cv = cv;
+
+       return ret;
+}
+
+static inline int axp288_charger_set_vbus_inlmt(struct axp288_chrg_info *info,
+                                          int inlmt)
+{
+       int ret;
+       unsigned int val;
+       u8 reg_val;
+
+       /* Read in limit register */
+       ret = regmap_read(info->regmap, AXP20X_CHRG_BAK_CTRL, &val);
+       if (ret < 0)
+               goto set_inlmt_fail;
+
+       if (inlmt <= ILIM_100MA) {
+               reg_val = CHRG_VBUS_ILIM_100MA;
+               inlmt = ILIM_100MA;
+       } else if (inlmt <= ILIM_500MA) {
+               reg_val = CHRG_VBUS_ILIM_500MA;
+               inlmt = ILIM_500MA;
+       } else if (inlmt <= ILIM_900MA) {
+               reg_val = CHRG_VBUS_ILIM_900MA;
+               inlmt = ILIM_900MA;
+       } else if (inlmt <= ILIM_1500MA) {
+               reg_val = CHRG_VBUS_ILIM_1500MA;
+               inlmt = ILIM_1500MA;
+       } else if (inlmt <= ILIM_2000MA) {
+               reg_val = CHRG_VBUS_ILIM_2000MA;
+               inlmt = ILIM_2000MA;
+       } else if (inlmt <= ILIM_2500MA) {
+               reg_val = CHRG_VBUS_ILIM_2500MA;
+               inlmt = ILIM_2500MA;
+       } else {
+               reg_val = CHRG_VBUS_ILIM_3000MA;
+               inlmt = ILIM_3000MA;
+       }
+
+       reg_val = (val & ~CHRG_VBUS_ILIM_MASK)
+                       | (reg_val << CHRG_VBUS_ILIM_BIT_POS);
+       ret = regmap_write(info->regmap, AXP20X_CHRG_BAK_CTRL, reg_val);
+       if (ret >= 0)
+               info->inlmt = inlmt;
+       else
+               dev_err(&info->pdev->dev, "charger BAK control %d\n", ret);
+
+
+set_inlmt_fail:
+       return ret;
+}
+
+static int axp288_charger_vbus_path_select(struct axp288_chrg_info *info,
+                                                               bool enable)
+{
+       int ret;
+
+       if (enable)
+               ret = regmap_update_bits(info->regmap, AXP20X_VBUS_IPSOUT_MGMT,
+                                       VBUS_ISPOUT_VBUS_PATH_DIS, 0);
+       else
+               ret = regmap_update_bits(info->regmap, AXP20X_VBUS_IPSOUT_MGMT,
+                       VBUS_ISPOUT_VBUS_PATH_DIS, VBUS_ISPOUT_VBUS_PATH_DIS);
+
+       if (ret < 0)
+               dev_err(&info->pdev->dev, "axp288 vbus path select %d\n", ret);
+
+
+       return ret;
+}
+
+static int axp288_charger_enable_charger(struct axp288_chrg_info *info,
+                                                               bool enable)
+{
+       int ret;
+
+       if (enable)
+               ret = regmap_update_bits(info->regmap, AXP20X_CHRG_CTRL1,
+                               CHRG_CCCV_CHG_EN, CHRG_CCCV_CHG_EN);
+       else
+               ret = regmap_update_bits(info->regmap, AXP20X_CHRG_CTRL1,
+                               CHRG_CCCV_CHG_EN, 0);
+       if (ret < 0)
+               dev_err(&info->pdev->dev, "axp288 enable charger %d\n", ret);
+       else
+               info->is_charger_enabled = enable;
+
+       return ret;
+}
+
+static int axp288_charger_is_present(struct axp288_chrg_info *info)
+{
+       int ret, present = 0;
+       unsigned int val;
+
+       ret = regmap_read(info->regmap, AXP20X_PWR_INPUT_STATUS, &val);
+       if (ret < 0)
+               return ret;
+
+       if (val & PS_STAT_VBUS_PRESENT)
+               present = 1;
+       return present;
+}
+
+static int axp288_charger_is_online(struct axp288_chrg_info *info)
+{
+       int ret, online = 0;
+       unsigned int val;
+
+       ret = regmap_read(info->regmap, AXP20X_PWR_INPUT_STATUS, &val);
+       if (ret < 0)
+               return ret;
+
+       if (val & PS_STAT_VBUS_VALID)
+               online = 1;
+       return online;
+}
+
+static int axp288_get_charger_health(struct axp288_chrg_info *info)
+{
+       int ret, pwr_stat, chrg_stat;
+       int health = POWER_SUPPLY_HEALTH_UNKNOWN;
+       unsigned int val;
+
+       ret = regmap_read(info->regmap, AXP20X_PWR_INPUT_STATUS, &val);
+       if ((ret < 0) || !(val & PS_STAT_VBUS_PRESENT))
+               goto health_read_fail;
+       else
+               pwr_stat = val;
+
+       ret = regmap_read(info->regmap, AXP20X_PWR_OP_MODE, &val);
+       if (ret < 0)
+               goto health_read_fail;
+       else
+               chrg_stat = val;
+
+       if (!(pwr_stat & PS_STAT_VBUS_VALID))
+               health = POWER_SUPPLY_HEALTH_DEAD;
+       else if (chrg_stat & CHRG_STAT_PMIC_OTP)
+               health = POWER_SUPPLY_HEALTH_OVERHEAT;
+       else if (chrg_stat & CHRG_STAT_BAT_SAFE_MODE)
+               health = POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE;
+       else
+               health = POWER_SUPPLY_HEALTH_GOOD;
+
+health_read_fail:
+       return health;
+}
+
+static int axp288_charger_usb_set_property(struct power_supply *psy,
+                                   enum power_supply_property psp,
+                                   const union power_supply_propval *val)
+{
+       struct axp288_chrg_info *info = power_supply_get_drvdata(psy);
+       int ret = 0;
+       int scaled_val;
+
+       mutex_lock(&info->lock);
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
+               scaled_val = min(val->intval, info->max_cc);
+               scaled_val = DIV_ROUND_CLOSEST(scaled_val, 1000);
+               ret = axp288_charger_set_cc(info, scaled_val);
+               if (ret < 0)
+                       dev_warn(&info->pdev->dev, "set charge current failed\n");
+               break;
+       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
+               scaled_val = min(val->intval, info->max_cv);
+               scaled_val = DIV_ROUND_CLOSEST(scaled_val, 1000);
+               ret = axp288_charger_set_cv(info, scaled_val);
+               if (ret < 0)
+                       dev_warn(&info->pdev->dev, "set charge voltage failed\n");
+               break;
+       default:
+               ret = -EINVAL;
+       }
+
+       mutex_unlock(&info->lock);
+       return ret;
+}
+
+static int axp288_charger_usb_get_property(struct power_supply *psy,
+                                   enum power_supply_property psp,
+                                   union power_supply_propval *val)
+{
+       struct axp288_chrg_info *info = power_supply_get_drvdata(psy);
+       int ret = 0;
+
+       mutex_lock(&info->lock);
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_PRESENT:
+               /* Check for OTG case first */
+               if (info->otg.id_short) {
+                       val->intval = 0;
+                       break;
+               }
+               ret = axp288_charger_is_present(info);
+               if (ret < 0)
+                       goto psy_get_prop_fail;
+               info->present = ret;
+               val->intval = info->present;
+               break;
+       case POWER_SUPPLY_PROP_ONLINE:
+               /* Check for OTG case first */
+               if (info->otg.id_short) {
+                       val->intval = 0;
+                       break;
+               }
+               ret = axp288_charger_is_online(info);
+               if (ret < 0)
+                       goto psy_get_prop_fail;
+               info->online = ret;
+               val->intval = info->online;
+               break;
+       case POWER_SUPPLY_PROP_HEALTH:
+               val->intval = axp288_get_charger_health(info);
+               break;
+       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
+               val->intval = info->cc * 1000;
+               break;
+       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
+               val->intval = info->max_cc * 1000;
+               break;
+       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
+               val->intval = info->cv * 1000;
+               break;
+       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX:
+               val->intval = info->max_cv * 1000;
+               break;
+       case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
+               val->intval = info->inlmt * 1000;
+               break;
+       default:
+               ret = -EINVAL;
+               goto psy_get_prop_fail;
+       }
+
+psy_get_prop_fail:
+       mutex_unlock(&info->lock);
+       return ret;
+}
+
+static int axp288_charger_property_is_writeable(struct power_supply *psy,
+               enum power_supply_property psp)
+{
+       int ret;
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
+       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
+               ret = 1;
+               break;
+       default:
+               ret = 0;
+       }
+
+       return ret;
+}
+
+static enum power_supply_property axp288_usb_props[] = {
+       POWER_SUPPLY_PROP_PRESENT,
+       POWER_SUPPLY_PROP_ONLINE,
+       POWER_SUPPLY_PROP_TYPE,
+       POWER_SUPPLY_PROP_HEALTH,
+       POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,
+       POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
+       POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,
+       POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX,
+       POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT,
+};
+
+static const struct power_supply_desc axp288_charger_desc = {
+       .name                   = "axp288_charger",
+       .type                   = POWER_SUPPLY_TYPE_USB,
+       .properties             = axp288_usb_props,
+       .num_properties         = ARRAY_SIZE(axp288_usb_props),
+       .get_property           = axp288_charger_usb_get_property,
+       .set_property           = axp288_charger_usb_set_property,
+       .property_is_writeable  = axp288_charger_property_is_writeable,
+};
+
+static irqreturn_t axp288_charger_irq_thread_handler(int irq, void *dev)
+{
+       struct axp288_chrg_info *info = dev;
+       int i;
+
+       for (i = 0; i < CHRG_INTR_END; i++) {
+               if (info->irq[i] == irq)
+                       break;
+       }
+
+       if (i >= CHRG_INTR_END) {
+               dev_warn(&info->pdev->dev, "spurious interrupt!!\n");
+               return IRQ_NONE;
+       }
+
+       switch (i) {
+       case VBUS_OV_IRQ:
+               dev_dbg(&info->pdev->dev, "VBUS Over Voltage INTR\n");
+               break;
+       case CHARGE_DONE_IRQ:
+               dev_dbg(&info->pdev->dev, "Charging Done INTR\n");
+               break;
+       case CHARGE_CHARGING_IRQ:
+               dev_dbg(&info->pdev->dev, "Start Charging IRQ\n");
+               break;
+       case BAT_SAFE_QUIT_IRQ:
+               dev_dbg(&info->pdev->dev,
+                       "Quit Safe Mode(restart timer) Charging IRQ\n");
+               break;
+       case BAT_SAFE_ENTER_IRQ:
+               dev_dbg(&info->pdev->dev,
+                       "Enter Safe Mode(timer expire) Charging IRQ\n");
+               break;
+       case QCBTU_IRQ:
+               dev_dbg(&info->pdev->dev,
+                       "Quit Battery Under Temperature(CHRG) INTR\n");
+               break;
+       case CBTU_IRQ:
+               dev_dbg(&info->pdev->dev,
+                       "Hit Battery Under Temperature(CHRG) INTR\n");
+               break;
+       case QCBTO_IRQ:
+               dev_dbg(&info->pdev->dev,
+                       "Quit Battery Over Temperature(CHRG) INTR\n");
+               break;
+       case CBTO_IRQ:
+               dev_dbg(&info->pdev->dev,
+                       "Hit Battery Over Temperature(CHRG) INTR\n");
+               break;
+       default:
+               dev_warn(&info->pdev->dev, "Spurious Interrupt!!!\n");
+               goto out;
+       }
+
+       power_supply_changed(info->psy_usb);
+out:
+       return IRQ_HANDLED;
+}
+
+static void axp288_charger_extcon_evt_worker(struct work_struct *work)
+{
+       struct axp288_chrg_info *info =
+           container_of(work, struct axp288_chrg_info, cable.work);
+       int ret, current_limit;
+       bool changed = false;
+       struct extcon_dev *edev = info->cable.edev;
+       bool old_connected = info->cable.connected;
+
+       /* Determine cable/charger type */
+       if (extcon_get_cable_state(edev, AXP288_EXTCON_SLOW_CHARGER) > 0) {
+               dev_dbg(&info->pdev->dev, "USB SDP charger  is connected");
+               info->cable.connected = true;
+               info->cable.chg_type = POWER_SUPPLY_TYPE_USB;
+       } else if (extcon_get_cable_state(edev,
+                               AXP288_EXTCON_DOWNSTREAM_CHARGER) > 0) {
+               dev_dbg(&info->pdev->dev, "USB CDP charger is connected");
+               info->cable.connected = true;
+               info->cable.chg_type = POWER_SUPPLY_TYPE_USB_CDP;
+       } else if (extcon_get_cable_state(edev,
+                                       AXP288_EXTCON_FAST_CHARGER) > 0) {
+               dev_dbg(&info->pdev->dev, "USB DCP charger is connected");
+               info->cable.connected = true;
+               info->cable.chg_type = POWER_SUPPLY_TYPE_USB_DCP;
+       } else {
+               if (old_connected)
+                       dev_dbg(&info->pdev->dev, "USB charger disconnected");
+               info->cable.connected = false;
+               info->cable.chg_type = POWER_SUPPLY_TYPE_USB;
+       }
+
+       /* Cable status changed */
+       if (old_connected != info->cable.connected)
+               changed = true;
+
+       if (!changed)
+               return;
+
+       mutex_lock(&info->lock);
+
+       if (info->is_charger_enabled && !info->cable.connected) {
+               info->enable_charger = false;
+               ret = axp288_charger_enable_charger(info, info->enable_charger);
+               if (ret < 0)
+                       dev_err(&info->pdev->dev,
+                               "cannot disable charger (%d)", ret);
+
+       } else if (!info->is_charger_enabled && info->cable.connected) {
+               switch (info->cable.chg_type) {
+               case POWER_SUPPLY_TYPE_USB:
+                       current_limit = ILIM_500MA;
+                       break;
+               case POWER_SUPPLY_TYPE_USB_CDP:
+                       current_limit = ILIM_1500MA;
+                       break;
+               case POWER_SUPPLY_TYPE_USB_DCP:
+                       current_limit = ILIM_2000MA;
+                       break;
+               default:
+                       /* Unknown */
+                       current_limit = 0;
+                       break;
+               }
+
+               /* Set vbus current limit first, then enable charger */
+               ret = axp288_charger_set_vbus_inlmt(info, current_limit);
+               if (ret < 0) {
+                       dev_err(&info->pdev->dev,
+                               "error setting current limit (%d)", ret);
+               } else {
+                       info->enable_charger = (current_limit > 0);
+                       ret = axp288_charger_enable_charger(info,
+                                                       info->enable_charger);
+                       if (ret < 0)
+                               dev_err(&info->pdev->dev,
+                                       "cannot enable charger (%d)", ret);
+               }
+       }
+
+       if (changed)
+               info->health = axp288_get_charger_health(info);
+
+       mutex_unlock(&info->lock);
+
+       if (changed)
+               power_supply_changed(info->psy_usb);
+}
+
+static int axp288_charger_handle_cable_evt(struct notifier_block *nb,
+                                         unsigned long event, void *param)
+{
+       struct axp288_chrg_info *info =
+           container_of(nb, struct axp288_chrg_info, cable.nb);
+
+       schedule_work(&info->cable.work);
+
+       return NOTIFY_OK;
+}
+
+static void axp288_charger_otg_evt_worker(struct work_struct *work)
+{
+       struct axp288_chrg_info *info =
+           container_of(work, struct axp288_chrg_info, otg.work);
+       int ret;
+
+       /* Disable VBUS path before enabling the 5V boost */
+       ret = axp288_charger_vbus_path_select(info, !info->otg.id_short);
+       if (ret < 0)
+               dev_warn(&info->pdev->dev, "vbus path disable failed\n");
+}
+
+static int axp288_charger_handle_otg_evt(struct notifier_block *nb,
+                                  unsigned long event, void *param)
+{
+       struct axp288_chrg_info *info =
+           container_of(nb, struct axp288_chrg_info, otg.id_nb);
+       struct extcon_dev *edev = param;
+       int usb_host = extcon_get_cable_state(edev, "USB-Host");
+
+       dev_dbg(&info->pdev->dev, "external connector USB-Host is %s\n",
+                               usb_host ? "attached" : "detached");
+
+       /*
+        * Set usb_id_short flag to avoid running charger detection logic
+        * in case usb host.
+        */
+       info->otg.id_short = usb_host;
+       schedule_work(&info->otg.work);
+
+       return NOTIFY_OK;
+}
+
+static void charger_init_hw_regs(struct axp288_chrg_info *info)
+{
+       int ret, cc, cv;
+       unsigned int val;
+
+       /* Program temperature thresholds */
+       ret = regmap_write(info->regmap, AXP20X_V_LTF_CHRG, CHRG_VLTFC_0C);
+       if (ret < 0)
+               dev_warn(&info->pdev->dev, "register(%x) write error(%d)\n",
+                                                       AXP20X_V_LTF_CHRG, ret);
+
+       ret = regmap_write(info->regmap, AXP20X_V_HTF_CHRG, CHRG_VHTFC_45C);
+       if (ret < 0)
+               dev_warn(&info->pdev->dev, "register(%x) write error(%d)\n",
+                                                       AXP20X_V_HTF_CHRG, ret);
+
+       /* Do not turn-off charger o/p after charge cycle ends */
+       ret = regmap_update_bits(info->regmap,
+                               AXP20X_CHRG_CTRL2,
+                               CNTL2_CHG_OUT_TURNON, 1);
+       if (ret < 0)
+               dev_warn(&info->pdev->dev, "register(%x) write error(%d)\n",
+                                               AXP20X_CHRG_CTRL2, ret);
+
+       /* Enable interrupts */
+       ret = regmap_update_bits(info->regmap,
+                               AXP20X_IRQ2_EN,
+                               BAT_IRQ_CFG_BAT_MASK, 1);
+       if (ret < 0)
+               dev_warn(&info->pdev->dev, "register(%x) write error(%d)\n",
+                                               AXP20X_IRQ2_EN, ret);
+
+       ret = regmap_update_bits(info->regmap, AXP20X_IRQ3_EN,
+                               TEMP_IRQ_CFG_MASK, 1);
+       if (ret < 0)
+               dev_warn(&info->pdev->dev, "register(%x) write error(%d)\n",
+                                               AXP20X_IRQ3_EN, ret);
+
+       /* Setup ending condition for charging to be 10% of I(chrg) */
+       ret = regmap_update_bits(info->regmap,
+                               AXP20X_CHRG_CTRL1,
+                               CHRG_CCCV_ITERM_20P, 0);
+       if (ret < 0)
+               dev_warn(&info->pdev->dev, "register(%x) write error(%d)\n",
+                                               AXP20X_CHRG_CTRL1, ret);
+
+       /* Disable OCV-SOC curve calibration */
+       ret = regmap_update_bits(info->regmap,
+                               AXP20X_CC_CTRL,
+                               FG_CNTL_OCV_ADJ_EN, 0);
+       if (ret < 0)
+               dev_warn(&info->pdev->dev, "register(%x) write error(%d)\n",
+                                               AXP20X_CC_CTRL, ret);
+
+       /* Init charging current and voltage */
+       info->max_cc = info->pdata->max_cc;
+       info->max_cv = info->pdata->max_cv;
+
+       /* Read current charge voltage and current limit */
+       ret = regmap_read(info->regmap, AXP20X_CHRG_CTRL1, &val);
+       if (ret < 0) {
+               /* Assume default if cannot read */
+               info->cc = info->pdata->def_cc;
+               info->cv = info->pdata->def_cv;
+       } else {
+               /* Determine charge voltage */
+               cv = (val & CHRG_CCCV_CV_MASK) >> CHRG_CCCV_CV_BIT_POS;
+               switch (cv) {
+               case CHRG_CCCV_CV_4100MV:
+                       info->cv = CV_4100MV;
+                       break;
+               case CHRG_CCCV_CV_4150MV:
+                       info->cv = CV_4150MV;
+                       break;
+               case CHRG_CCCV_CV_4200MV:
+                       info->cv = CV_4200MV;
+                       break;
+               case CHRG_CCCV_CV_4350MV:
+                       info->cv = CV_4350MV;
+                       break;
+               default:
+                       info->cv = INT_MAX;
+                       break;
+               }
+
+               /* Determine charge current limit */
+               cc = (ret & CHRG_CCCV_CC_MASK) >> CHRG_CCCV_CC_BIT_POS;
+               cc = (cc * CHRG_CCCV_CC_LSB_RES) + CHRG_CCCV_CC_OFFSET;
+               info->cc = cc;
+
+               /* Program default charging voltage and current */
+               cc = min(info->pdata->def_cc, info->max_cc);
+               cv = min(info->pdata->def_cv, info->max_cv);
+
+               ret = axp288_charger_set_cc(info, cc);
+               if (ret < 0)
+                       dev_warn(&info->pdev->dev,
+                                       "error(%d) in setting CC\n", ret);
+
+               ret = axp288_charger_set_cv(info, cv);
+               if (ret < 0)
+                       dev_warn(&info->pdev->dev,
+                                       "error(%d) in setting CV\n", ret);
+       }
+}
+
+static int axp288_charger_probe(struct platform_device *pdev)
+{
+       int ret, i, pirq;
+       struct axp288_chrg_info *info;
+       struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
+       struct power_supply_config charger_cfg = {};
+
+       info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
+       if (!info)
+               return -ENOMEM;
+
+       info->pdev = pdev;
+       info->regmap = axp20x->regmap;
+       info->regmap_irqc = axp20x->regmap_irqc;
+       info->pdata = pdev->dev.platform_data;
+
+       if (!info->pdata) {
+               /* Try ACPI provided pdata via device properties */
+               if (!device_property_present(&pdev->dev,
+                                               "axp288_charger_data\n"))
+                       dev_err(&pdev->dev, "failed to get platform data\n");
+               return -ENODEV;
+       }
+
+       info->cable.edev = extcon_get_extcon_dev(AXP288_EXTCON_DEV_NAME);
+       if (info->cable.edev == NULL) {
+               dev_dbg(&pdev->dev, "%s is not ready, probe deferred\n",
+                       AXP288_EXTCON_DEV_NAME);
+               return -EPROBE_DEFER;
+       }
+
+       /* Register for extcon notification */
+       INIT_WORK(&info->cable.work, axp288_charger_extcon_evt_worker);
+       info->cable.nb.notifier_call = axp288_charger_handle_cable_evt;
+       ret = extcon_register_notifier(info->cable.edev, &info->cable.nb);
+       if (ret) {
+               dev_err(&info->pdev->dev,
+                       "failed to register extcon notifier %d\n", ret);
+               return ret;
+       }
+
+       platform_set_drvdata(pdev, info);
+       mutex_init(&info->lock);
+
+       /* Register with power supply class */
+       charger_cfg.drv_data = info;
+       info->psy_usb = power_supply_register(&pdev->dev, &axp288_charger_desc,
+                                               &charger_cfg);
+       if (IS_ERR(info->psy_usb)) {
+               dev_err(&pdev->dev, "failed to register power supply charger\n");
+               ret = PTR_ERR(info->psy_usb);
+               goto psy_reg_failed;
+       }
+
+       /* Register for OTG notification */
+       INIT_WORK(&info->otg.work, axp288_charger_otg_evt_worker);
+       info->otg.id_nb.notifier_call = axp288_charger_handle_otg_evt;
+       ret = extcon_register_interest(&info->otg.cable, NULL, "USB-Host",
+                                      &info->otg.id_nb);
+       if (ret)
+               dev_warn(&pdev->dev, "failed to register otg notifier\n");
+
+       if (info->otg.cable.edev)
+               info->otg.id_short = extcon_get_cable_state(
+                                       info->otg.cable.edev, "USB-Host");
+
+       /* Register charger interrupts */
+       for (i = 0; i < CHRG_INTR_END; i++) {
+               pirq = platform_get_irq(info->pdev, i);
+               info->irq[i] = regmap_irq_get_virq(info->regmap_irqc, pirq);
+               if (info->irq[i] < 0) {
+                       dev_warn(&info->pdev->dev,
+                               "failed to get virtual interrupt=%d\n", pirq);
+                       ret = info->irq[i];
+                       goto intr_reg_failed;
+               }
+               ret = devm_request_threaded_irq(&info->pdev->dev, info->irq[i],
+                                       NULL, axp288_charger_irq_thread_handler,
+                                       IRQF_ONESHOT, info->pdev->name, info);
+               if (ret) {
+                       dev_err(&pdev->dev, "failed to request interrupt=%d\n",
+                                                               info->irq[i]);
+                       goto intr_reg_failed;
+               }
+       }
+
+       charger_init_hw_regs(info);
+
+       return 0;
+
+intr_reg_failed:
+       if (info->otg.cable.edev)
+               extcon_unregister_interest(&info->otg.cable);
+       power_supply_unregister(info->psy_usb);
+psy_reg_failed:
+       extcon_unregister_notifier(info->cable.edev, &info->cable.nb);
+       return ret;
+}
+
+static int axp288_charger_remove(struct platform_device *pdev)
+{
+       struct axp288_chrg_info *info =  dev_get_drvdata(&pdev->dev);
+
+       if (info->otg.cable.edev)
+               extcon_unregister_interest(&info->otg.cable);
+
+       extcon_unregister_notifier(info->cable.edev, &info->cable.nb);
+       power_supply_unregister(info->psy_usb);
+
+       return 0;
+}
+
+static struct platform_driver axp288_charger_driver = {
+       .probe = axp288_charger_probe,
+       .remove = axp288_charger_remove,
+       .driver = {
+               .name = "axp288_charger",
+       },
+};
+
+module_platform_driver(axp288_charger_driver);
+
+MODULE_AUTHOR("Ramakrishna Pallala <ramakrishna.pallala@intel.com>");
+MODULE_DESCRIPTION("X-power AXP288 Charger Driver");
+MODULE_LICENSE("GPL v2");
index bd1dbfee2515dda65c54176d9b2e547cb4e3beca..50c0110d6b58008df681c5bfcac66e89dcac5e46 100644 (file)
@@ -1117,7 +1117,7 @@ static int axp288_fuel_gauge_probe(struct platform_device *pdev)
        return ret;
 }
 
-static struct platform_device_id axp288_fg_id_table[] = {
+static const struct platform_device_id axp288_fg_id_table[] = {
        { .name = DEV_NAME },
        {},
 };
index 6c534dcbc19cb3c8639da09bef6b48281ac3cd44..e98dcb661cc9bfd2730eb966978488513f5e2deb 100644 (file)
@@ -35,6 +35,7 @@
 #include <linux/idr.h>
 #include <linux/i2c.h>
 #include <linux/slab.h>
+#include <linux/acpi.h>
 
 #include <linux/power/bq2415x_charger.h>
 
@@ -631,7 +632,7 @@ static int bq2415x_set_charge_current(struct bq2415x_device *bq, int mA)
        int val;
 
        if (bq->init_data.resistor_sense <= 0)
-               return -ENOSYS;
+               return -EINVAL;
 
        val = (mA * bq->init_data.resistor_sense - 37400) / 6800;
        if (val < 0)
@@ -650,7 +651,7 @@ static int bq2415x_get_charge_current(struct bq2415x_device *bq)
        int ret;
 
        if (bq->init_data.resistor_sense <= 0)
-               return -ENOSYS;
+               return -EINVAL;
 
        ret = bq2415x_i2c_read_mask(bq, BQ2415X_REG_CURRENT,
                        BQ2415X_MASK_VI_CHRG, BQ2415X_SHIFT_VI_CHRG);
@@ -665,7 +666,7 @@ static int bq2415x_set_termination_current(struct bq2415x_device *bq, int mA)
        int val;
 
        if (bq->init_data.resistor_sense <= 0)
-               return -ENOSYS;
+               return -EINVAL;
 
        val = (mA * bq->init_data.resistor_sense - 3400) / 3400;
        if (val < 0)
@@ -684,7 +685,7 @@ static int bq2415x_get_termination_current(struct bq2415x_device *bq)
        int ret;
 
        if (bq->init_data.resistor_sense <= 0)
-               return -ENOSYS;
+               return -EINVAL;
 
        ret = bq2415x_i2c_read_mask(bq, BQ2415X_REG_CURRENT,
                        BQ2415X_MASK_VI_TERM, BQ2415X_SHIFT_VI_TERM);
@@ -1166,7 +1167,7 @@ static ssize_t bq2415x_sysfs_set_mode(struct device *dev,
 
        if (strncmp(buf, "auto", 4) == 0) {
                if (bq->automode < 0)
-                       return -ENOSYS;
+                       return -EINVAL;
                bq->automode = 1;
                mode = bq->reported_mode;
        } else if (strncmp(buf, "off", 3) == 0) {
@@ -1530,13 +1531,14 @@ static int bq2415x_probe(struct i2c_client *client,
 {
        int ret;
        int num;
-       char *name;
+       char *name = NULL;
        struct bq2415x_device *bq;
        struct device_node *np = client->dev.of_node;
        struct bq2415x_platform_data *pdata = client->dev.platform_data;
+       const struct acpi_device_id *acpi_id = NULL;
 
-       if (!np && !pdata) {
-               dev_err(&client->dev, "platform data missing\n");
+       if (!np && !pdata && !ACPI_HANDLE(&client->dev)) {
+               dev_err(&client->dev, "Neither devicetree, nor platform data, nor ACPI support\n");
                return -ENODEV;
        }
 
@@ -1547,7 +1549,14 @@ static int bq2415x_probe(struct i2c_client *client,
        if (num < 0)
                return num;
 
-       name = kasprintf(GFP_KERNEL, "%s-%d", id->name, num);
+       if (id) {
+               name = kasprintf(GFP_KERNEL, "%s-%d", id->name, num);
+       } else if (ACPI_HANDLE(&client->dev)) {
+               acpi_id =
+                       acpi_match_device(client->dev.driver->acpi_match_table,
+                                         &client->dev);
+               name = kasprintf(GFP_KERNEL, "%s-%d", acpi_id->id, num);
+       }
        if (!name) {
                dev_err(&client->dev, "failed to allocate device name\n");
                ret = -ENOMEM;
@@ -1556,63 +1565,72 @@ static int bq2415x_probe(struct i2c_client *client,
 
        bq = devm_kzalloc(&client->dev, sizeof(*bq), GFP_KERNEL);
        if (!bq) {
-               dev_err(&client->dev, "failed to allocate device data\n");
                ret = -ENOMEM;
                goto error_2;
        }
 
        if (np) {
-               bq->notify_psy = power_supply_get_by_phandle(np, "ti,usb-charger-detection");
+               bq->notify_psy = power_supply_get_by_phandle(np,
+                                               "ti,usb-charger-detection");
 
                if (IS_ERR(bq->notify_psy)) {
                        dev_info(&client->dev,
-                               "no 'ti,usb-charger-detection' property (err=%ld)\n",
+                                "no 'ti,usb-charger-detection' property (err=%ld)\n",
                                PTR_ERR(bq->notify_psy));
                        bq->notify_psy = NULL;
                } else if (!bq->notify_psy) {
                        ret = -EPROBE_DEFER;
                        goto error_2;
                }
-       }
-       else if (pdata->notify_device)
+       } else if (pdata && pdata->notify_device) {
                bq->notify_psy = power_supply_get_by_name(pdata->notify_device);
-       else
+       } else {
                bq->notify_psy = NULL;
+       }
 
        i2c_set_clientdata(client, bq);
 
        bq->id = num;
        bq->dev = &client->dev;
-       bq->chip = id->driver_data;
+       if (id)
+               bq->chip = id->driver_data;
+       else if (ACPI_HANDLE(bq->dev))
+               bq->chip = acpi_id->driver_data;
        bq->name = name;
        bq->mode = BQ2415X_MODE_OFF;
        bq->reported_mode = BQ2415X_MODE_OFF;
        bq->autotimer = 0;
        bq->automode = 0;
 
-       if (np) {
-               ret = of_property_read_u32(np, "ti,current-limit",
-                               &bq->init_data.current_limit);
+       if (np || ACPI_HANDLE(bq->dev)) {
+               ret = device_property_read_u32(bq->dev,
+                                              "ti,current-limit",
+                                              &bq->init_data.current_limit);
                if (ret)
                        goto error_3;
-               ret = of_property_read_u32(np, "ti,weak-battery-voltage",
-                               &bq->init_data.weak_battery_voltage);
+               ret = device_property_read_u32(bq->dev,
+                                       "ti,weak-battery-voltage",
+                                       &bq->init_data.weak_battery_voltage);
                if (ret)
                        goto error_3;
-               ret = of_property_read_u32(np, "ti,battery-regulation-voltage",
+               ret = device_property_read_u32(bq->dev,
+                               "ti,battery-regulation-voltage",
                                &bq->init_data.battery_regulation_voltage);
                if (ret)
                        goto error_3;
-               ret = of_property_read_u32(np, "ti,charge-current",
-                               &bq->init_data.charge_current);
+               ret = device_property_read_u32(bq->dev,
+                                              "ti,charge-current",
+                                              &bq->init_data.charge_current);
                if (ret)
                        goto error_3;
-               ret = of_property_read_u32(np, "ti,termination-current",
+               ret = device_property_read_u32(bq->dev,
+                               "ti,termination-current",
                                &bq->init_data.termination_current);
                if (ret)
                        goto error_3;
-               ret = of_property_read_u32(np, "ti,resistor-sense",
-                               &bq->init_data.resistor_sense);
+               ret = device_property_read_u32(bq->dev,
+                                              "ti,resistor-sense",
+                                              &bq->init_data.resistor_sense);
                if (ret)
                        goto error_3;
        } else {
@@ -1648,7 +1666,8 @@ static int bq2415x_probe(struct i2c_client *client,
                }
 
                /* Query for initial reported_mode and set it */
-               bq2415x_notifier_call(&bq->nb, PSY_EVENT_PROP_CHANGED, bq->notify_psy);
+               bq2415x_notifier_call(&bq->nb, PSY_EVENT_PROP_CHANGED,
+                                     bq->notify_psy);
                bq2415x_set_mode(bq, bq->reported_mode);
 
                bq->automode = 1;
@@ -1727,9 +1746,28 @@ static const struct i2c_device_id bq2415x_i2c_id_table[] = {
 };
 MODULE_DEVICE_TABLE(i2c, bq2415x_i2c_id_table);
 
+static const struct acpi_device_id bq2415x_i2c_acpi_match[] = {
+       { "BQ2415X", BQUNKNOWN },
+       { "BQ241500", BQ24150 },
+       { "BQA24150", BQ24150A },
+       { "BQ241510", BQ24151 },
+       { "BQA24151", BQ24151A },
+       { "BQ241520", BQ24152 },
+       { "BQ241530", BQ24153 },
+       { "BQA24153", BQ24153A },
+       { "BQ241550", BQ24155 },
+       { "BQ241560", BQ24156 },
+       { "BQA24156", BQ24156A },
+       { "BQS24157", BQ24157S },
+       { "BQ241580", BQ24158 },
+       {},
+};
+MODULE_DEVICE_TABLE(acpi, bq2415x_i2c_acpi_match);
+
 static struct i2c_driver bq2415x_driver = {
        .driver = {
                .name = "bq2415x-charger",
+               .acpi_match_table = ACPI_PTR(bq2415x_i2c_acpi_match),
        },
        .probe = bq2415x_probe,
        .remove = bq2415x_remove,
index 407c4af83891331611ba710c62a7b5a8d1633d7b..052db78c373650979f620cd031cf7f7b0fa067b7 100644 (file)
@@ -1258,10 +1258,13 @@ static irqreturn_t bq24190_irq_handler_thread(int irq, void *data)
         * register reset so we should ignore that one (the very first
         * interrupt received).
         */
-       if (alert_userspace && !bdi->first_time) {
-               power_supply_changed(bdi->charger);
-               power_supply_changed(bdi->battery);
-               bdi->first_time = false;
+       if (alert_userspace) {
+               if (!bdi->first_time) {
+                       power_supply_changed(bdi->charger);
+                       power_supply_changed(bdi->battery);
+               } else {
+                       bdi->first_time = false;
+               }
        }
 
 out:
diff --git a/drivers/power/bq24257_charger.c b/drivers/power/bq24257_charger.c
new file mode 100644 (file)
index 0000000..5859bc7
--- /dev/null
@@ -0,0 +1,858 @@
+/*
+ * TI BQ24257 charger driver
+ *
+ * Copyright (C) 2015 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/power_supply.h>
+#include <linux/regmap.h>
+#include <linux/types.h>
+#include <linux/gpio/consumer.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+
+#include <linux/acpi.h>
+#include <linux/of.h>
+
+#define BQ24257_REG_1                  0x00
+#define BQ24257_REG_2                  0x01
+#define BQ24257_REG_3                  0x02
+#define BQ24257_REG_4                  0x03
+#define BQ24257_REG_5                  0x04
+#define BQ24257_REG_6                  0x05
+#define BQ24257_REG_7                  0x06
+
+#define BQ24257_MANUFACTURER           "Texas Instruments"
+#define BQ24257_STAT_IRQ               "stat"
+#define BQ24257_PG_GPIO                        "pg"
+
+#define BQ24257_ILIM_SET_DELAY         1000    /* msec */
+
+enum bq24257_fields {
+       F_WD_FAULT, F_WD_EN, F_STAT, F_FAULT,                       /* REG 1 */
+       F_RESET, F_IILIMIT, F_EN_STAT, F_EN_TERM, F_CE, F_HZ_MODE,  /* REG 2 */
+       F_VBAT, F_USB_DET,                                          /* REG 3 */
+       F_ICHG, F_ITERM,                                            /* REG 4 */
+       F_LOOP_STATUS, F_LOW_CHG, F_DPDM_EN, F_CE_STATUS, F_VINDPM, /* REG 5 */
+       F_X2_TMR_EN, F_TMR, F_SYSOFF, F_TS_STAT,                    /* REG 6 */
+       F_VOVP, F_CLR_VDP, F_FORCE_BATDET, F_FORCE_PTM,             /* REG 7 */
+
+       F_MAX_FIELDS
+};
+
+/* initial field values, converted from uV/uA */
+struct bq24257_init_data {
+       u8 ichg;        /* charge current      */
+       u8 vbat;        /* regulation voltage  */
+       u8 iterm;       /* termination current */
+};
+
+struct bq24257_state {
+       u8 status;
+       u8 fault;
+       bool power_good;
+};
+
+struct bq24257_device {
+       struct i2c_client *client;
+       struct device *dev;
+       struct power_supply *charger;
+
+       struct regmap *rmap;
+       struct regmap_field *rmap_fields[F_MAX_FIELDS];
+
+       struct gpio_desc *pg;
+
+       struct delayed_work iilimit_setup_work;
+
+       struct bq24257_init_data init_data;
+       struct bq24257_state state;
+
+       struct mutex lock; /* protect state data */
+};
+
+static bool bq24257_is_volatile_reg(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case BQ24257_REG_2:
+       case BQ24257_REG_4:
+               return false;
+
+       default:
+               return true;
+       }
+}
+
+static const struct regmap_config bq24257_regmap_config = {
+       .reg_bits = 8,
+       .val_bits = 8,
+
+       .max_register = BQ24257_REG_7,
+       .cache_type = REGCACHE_RBTREE,
+
+       .volatile_reg = bq24257_is_volatile_reg,
+};
+
+static const struct reg_field bq24257_reg_fields[] = {
+       /* REG 1 */
+       [F_WD_FAULT]            = REG_FIELD(BQ24257_REG_1, 7, 7),
+       [F_WD_EN]               = REG_FIELD(BQ24257_REG_1, 6, 6),
+       [F_STAT]                = REG_FIELD(BQ24257_REG_1, 4, 5),
+       [F_FAULT]               = REG_FIELD(BQ24257_REG_1, 0, 3),
+       /* REG 2 */
+       [F_RESET]               = REG_FIELD(BQ24257_REG_2, 7, 7),
+       [F_IILIMIT]             = REG_FIELD(BQ24257_REG_2, 4, 6),
+       [F_EN_STAT]             = REG_FIELD(BQ24257_REG_2, 3, 3),
+       [F_EN_TERM]             = REG_FIELD(BQ24257_REG_2, 2, 2),
+       [F_CE]                  = REG_FIELD(BQ24257_REG_2, 1, 1),
+       [F_HZ_MODE]             = REG_FIELD(BQ24257_REG_2, 0, 0),
+       /* REG 3 */
+       [F_VBAT]                = REG_FIELD(BQ24257_REG_3, 2, 7),
+       [F_USB_DET]             = REG_FIELD(BQ24257_REG_3, 0, 1),
+       /* REG 4 */
+       [F_ICHG]                = REG_FIELD(BQ24257_REG_4, 3, 7),
+       [F_ITERM]               = REG_FIELD(BQ24257_REG_4, 0, 2),
+       /* REG 5 */
+       [F_LOOP_STATUS]         = REG_FIELD(BQ24257_REG_5, 6, 7),
+       [F_LOW_CHG]             = REG_FIELD(BQ24257_REG_5, 5, 5),
+       [F_DPDM_EN]             = REG_FIELD(BQ24257_REG_5, 4, 4),
+       [F_CE_STATUS]           = REG_FIELD(BQ24257_REG_5, 3, 3),
+       [F_VINDPM]              = REG_FIELD(BQ24257_REG_5, 0, 2),
+       /* REG 6 */
+       [F_X2_TMR_EN]           = REG_FIELD(BQ24257_REG_6, 7, 7),
+       [F_TMR]                 = REG_FIELD(BQ24257_REG_6, 5, 6),
+       [F_SYSOFF]              = REG_FIELD(BQ24257_REG_6, 4, 4),
+       [F_TS_STAT]             = REG_FIELD(BQ24257_REG_6, 0, 2),
+       /* REG 7 */
+       [F_VOVP]                = REG_FIELD(BQ24257_REG_7, 5, 7),
+       [F_CLR_VDP]             = REG_FIELD(BQ24257_REG_7, 4, 4),
+       [F_FORCE_BATDET]        = REG_FIELD(BQ24257_REG_7, 3, 3),
+       [F_FORCE_PTM]           = REG_FIELD(BQ24257_REG_7, 2, 2)
+};
+
+static const u32 bq24257_vbat_map[] = {
+       3500000, 3520000, 3540000, 3560000, 3580000, 3600000, 3620000, 3640000,
+       3660000, 3680000, 3700000, 3720000, 3740000, 3760000, 3780000, 3800000,
+       3820000, 3840000, 3860000, 3880000, 3900000, 3920000, 3940000, 3960000,
+       3980000, 4000000, 4020000, 4040000, 4060000, 4080000, 4100000, 4120000,
+       4140000, 4160000, 4180000, 4200000, 4220000, 4240000, 4260000, 4280000,
+       4300000, 4320000, 4340000, 4360000, 4380000, 4400000, 4420000, 4440000
+};
+
+#define BQ24257_VBAT_MAP_SIZE          ARRAY_SIZE(bq24257_vbat_map)
+
+static const u32 bq24257_ichg_map[] = {
+       500000, 550000, 600000, 650000, 700000, 750000, 800000, 850000, 900000,
+       950000, 1000000, 1050000, 1100000, 1150000, 1200000, 1250000, 1300000,
+       1350000, 1400000, 1450000, 1500000, 1550000, 1600000, 1650000, 1700000,
+       1750000, 1800000, 1850000, 1900000, 1950000, 2000000
+};
+
+#define BQ24257_ICHG_MAP_SIZE          ARRAY_SIZE(bq24257_ichg_map)
+
+static const u32 bq24257_iterm_map[] = {
+       50000, 75000, 100000, 125000, 150000, 175000, 200000, 225000
+};
+
+#define BQ24257_ITERM_MAP_SIZE         ARRAY_SIZE(bq24257_iterm_map)
+
+static int bq24257_field_read(struct bq24257_device *bq,
+                             enum bq24257_fields field_id)
+{
+       int ret;
+       int val;
+
+       ret = regmap_field_read(bq->rmap_fields[field_id], &val);
+       if (ret < 0)
+               return ret;
+
+       return val;
+}
+
+static int bq24257_field_write(struct bq24257_device *bq,
+                              enum bq24257_fields field_id, u8 val)
+{
+       return regmap_field_write(bq->rmap_fields[field_id], val);
+}
+
+static u8 bq24257_find_idx(u32 value, const u32 *map, u8 map_size)
+{
+       u8 idx;
+
+       for (idx = 1; idx < map_size; idx++)
+               if (value < map[idx])
+                       break;
+
+       return idx - 1;
+}
+
+enum bq24257_status {
+       STATUS_READY,
+       STATUS_CHARGE_IN_PROGRESS,
+       STATUS_CHARGE_DONE,
+       STATUS_FAULT,
+};
+
+enum bq24257_fault {
+       FAULT_NORMAL,
+       FAULT_INPUT_OVP,
+       FAULT_INPUT_UVLO,
+       FAULT_SLEEP,
+       FAULT_BAT_TS,
+       FAULT_BAT_OVP,
+       FAULT_TS,
+       FAULT_TIMER,
+       FAULT_NO_BAT,
+       FAULT_ISET,
+       FAULT_INPUT_LDO_LOW,
+};
+
+static int bq24257_power_supply_get_property(struct power_supply *psy,
+                                            enum power_supply_property psp,
+                                            union power_supply_propval *val)
+{
+       struct bq24257_device *bq = power_supply_get_drvdata(psy);
+       struct bq24257_state state;
+
+       mutex_lock(&bq->lock);
+       state = bq->state;
+       mutex_unlock(&bq->lock);
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_STATUS:
+               if (!state.power_good)
+                       val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
+               else if (state.status == STATUS_READY)
+                       val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
+               else if (state.status == STATUS_CHARGE_IN_PROGRESS)
+                       val->intval = POWER_SUPPLY_STATUS_CHARGING;
+               else if (state.status == STATUS_CHARGE_DONE)
+                       val->intval = POWER_SUPPLY_STATUS_FULL;
+               else
+                       val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
+               break;
+
+       case POWER_SUPPLY_PROP_MANUFACTURER:
+               val->strval = BQ24257_MANUFACTURER;
+               break;
+
+       case POWER_SUPPLY_PROP_ONLINE:
+               val->intval = state.power_good;
+               break;
+
+       case POWER_SUPPLY_PROP_HEALTH:
+               switch (state.fault) {
+               case FAULT_NORMAL:
+                       val->intval = POWER_SUPPLY_HEALTH_GOOD;
+                       break;
+
+               case FAULT_INPUT_OVP:
+               case FAULT_BAT_OVP:
+                       val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
+                       break;
+
+               case FAULT_TS:
+               case FAULT_BAT_TS:
+                       val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
+                       break;
+
+               case FAULT_TIMER:
+                       val->intval = POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE;
+                       break;
+
+               default:
+                       val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
+                       break;
+               }
+
+               break;
+
+       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
+               val->intval = bq24257_ichg_map[bq->init_data.ichg];
+               break;
+
+       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
+               val->intval = bq24257_ichg_map[BQ24257_ICHG_MAP_SIZE - 1];
+               break;
+
+       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
+               val->intval = bq24257_vbat_map[bq->init_data.vbat];
+               break;
+
+       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX:
+               val->intval = bq24257_vbat_map[BQ24257_VBAT_MAP_SIZE - 1];
+               break;
+
+       case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT:
+               val->intval = bq24257_iterm_map[bq->init_data.iterm];
+               break;
+
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int bq24257_get_chip_state(struct bq24257_device *bq,
+                                 struct bq24257_state *state)
+{
+       int ret;
+
+       ret = bq24257_field_read(bq, F_STAT);
+       if (ret < 0)
+               return ret;
+
+       state->status = ret;
+
+       ret = bq24257_field_read(bq, F_FAULT);
+       if (ret < 0)
+               return ret;
+
+       state->fault = ret;
+
+       state->power_good = !gpiod_get_value_cansleep(bq->pg);
+
+       return 0;
+}
+
+static bool bq24257_state_changed(struct bq24257_device *bq,
+                                 struct bq24257_state *new_state)
+{
+       int ret;
+
+       mutex_lock(&bq->lock);
+       ret = (bq->state.status != new_state->status ||
+              bq->state.fault != new_state->fault ||
+              bq->state.power_good != new_state->power_good);
+       mutex_unlock(&bq->lock);
+
+       return ret;
+}
+
+enum bq24257_loop_status {
+       LOOP_STATUS_NONE,
+       LOOP_STATUS_IN_DPM,
+       LOOP_STATUS_IN_CURRENT_LIMIT,
+       LOOP_STATUS_THERMAL,
+};
+
+enum bq24257_in_ilimit {
+       IILIMIT_100,
+       IILIMIT_150,
+       IILIMIT_500,
+       IILIMIT_900,
+       IILIMIT_1500,
+       IILIMIT_2000,
+       IILIMIT_EXT,
+       IILIMIT_NONE,
+};
+
+enum bq24257_port_type {
+       PORT_TYPE_DCP,          /* Dedicated Charging Port */
+       PORT_TYPE_CDP,          /* Charging Downstream Port */
+       PORT_TYPE_SDP,          /* Standard Downstream Port */
+       PORT_TYPE_NON_STANDARD,
+};
+
+enum bq24257_safety_timer {
+       SAFETY_TIMER_45,
+       SAFETY_TIMER_360,
+       SAFETY_TIMER_540,
+       SAFETY_TIMER_NONE,
+};
+
+static int bq24257_iilimit_autoset(struct bq24257_device *bq)
+{
+       int loop_status;
+       int iilimit;
+       int port_type;
+       int ret;
+       const u8 new_iilimit[] = {
+               [PORT_TYPE_DCP] = IILIMIT_2000,
+               [PORT_TYPE_CDP] = IILIMIT_2000,
+               [PORT_TYPE_SDP] = IILIMIT_500,
+               [PORT_TYPE_NON_STANDARD] = IILIMIT_500
+       };
+
+       ret = bq24257_field_read(bq, F_LOOP_STATUS);
+       if (ret < 0)
+               goto error;
+
+       loop_status = ret;
+
+       ret = bq24257_field_read(bq, F_IILIMIT);
+       if (ret < 0)
+               goto error;
+
+       iilimit = ret;
+
+       /*
+        * All USB ports should be able to handle 500mA. If not, DPM will lower
+        * the charging current to accommodate the power source. No need to set
+        * a lower IILIMIT value.
+        */
+       if (loop_status == LOOP_STATUS_IN_DPM && iilimit == IILIMIT_500)
+               return 0;
+
+       ret = bq24257_field_read(bq, F_USB_DET);
+       if (ret < 0)
+               goto error;
+
+       port_type = ret;
+
+       ret = bq24257_field_write(bq, F_IILIMIT, new_iilimit[port_type]);
+       if (ret < 0)
+               goto error;
+
+       ret = bq24257_field_write(bq, F_TMR, SAFETY_TIMER_360);
+       if (ret < 0)
+               goto error;
+
+       ret = bq24257_field_write(bq, F_CLR_VDP, 1);
+       if (ret < 0)
+               goto error;
+
+       dev_dbg(bq->dev, "port/loop = %d/%d -> iilimit = %d\n",
+               port_type, loop_status, new_iilimit[port_type]);
+
+       return 0;
+
+error:
+       dev_err(bq->dev, "%s: Error communicating with the chip.\n", __func__);
+       return ret;
+}
+
+static void bq24257_iilimit_setup_work(struct work_struct *work)
+{
+       struct bq24257_device *bq = container_of(work, struct bq24257_device,
+                                                iilimit_setup_work.work);
+
+       bq24257_iilimit_autoset(bq);
+}
+
+static void bq24257_handle_state_change(struct bq24257_device *bq,
+                                       struct bq24257_state *new_state)
+{
+       int ret;
+       struct bq24257_state old_state;
+       bool reset_iilimit = false;
+       bool config_iilimit = false;
+
+       mutex_lock(&bq->lock);
+       old_state = bq->state;
+       mutex_unlock(&bq->lock);
+
+       if (!new_state->power_good) {                        /* power removed */
+               cancel_delayed_work_sync(&bq->iilimit_setup_work);
+
+               /* activate D+/D- port detection algorithm */
+               ret = bq24257_field_write(bq, F_DPDM_EN, 1);
+               if (ret < 0)
+                       goto error;
+
+               reset_iilimit = true;
+       } else if (!old_state.power_good) {                 /* power inserted */
+               config_iilimit = true;
+       } else if (new_state->fault == FAULT_NO_BAT) {     /* battery removed */
+               cancel_delayed_work_sync(&bq->iilimit_setup_work);
+
+               reset_iilimit = true;
+       } else if (old_state.fault == FAULT_NO_BAT) {    /* battery connected */
+               config_iilimit = true;
+       } else if (new_state->fault == FAULT_TIMER) { /* safety timer expired */
+               dev_err(bq->dev, "Safety timer expired! Battery dead?\n");
+       }
+
+       if (reset_iilimit) {
+               ret = bq24257_field_write(bq, F_IILIMIT, IILIMIT_500);
+               if (ret < 0)
+                       goto error;
+       } else if (config_iilimit) {
+               schedule_delayed_work(&bq->iilimit_setup_work,
+                                     msecs_to_jiffies(BQ24257_ILIM_SET_DELAY));
+       }
+
+       return;
+
+error:
+       dev_err(bq->dev, "%s: Error communicating with the chip.\n", __func__);
+}
+
+static irqreturn_t bq24257_irq_handler_thread(int irq, void *private)
+{
+       int ret;
+       struct bq24257_device *bq = private;
+       struct bq24257_state state;
+
+       ret = bq24257_get_chip_state(bq, &state);
+       if (ret < 0)
+               return IRQ_HANDLED;
+
+       if (!bq24257_state_changed(bq, &state))
+               return IRQ_HANDLED;
+
+       dev_dbg(bq->dev, "irq(state changed): status/fault/pg = %d/%d/%d\n",
+               state.status, state.fault, state.power_good);
+
+       bq24257_handle_state_change(bq, &state);
+
+       mutex_lock(&bq->lock);
+       bq->state = state;
+       mutex_unlock(&bq->lock);
+
+       power_supply_changed(bq->charger);
+
+       return IRQ_HANDLED;
+}
+
+static int bq24257_hw_init(struct bq24257_device *bq)
+{
+       int ret;
+       int i;
+       struct bq24257_state state;
+
+       const struct {
+               int field;
+               u32 value;
+       } init_data[] = {
+               {F_ICHG, bq->init_data.ichg},
+               {F_VBAT, bq->init_data.vbat},
+               {F_ITERM, bq->init_data.iterm}
+       };
+
+       /*
+        * Disable the watchdog timer to prevent the IC from going back to
+        * default settings after 50 seconds of I2C inactivity.
+        */
+       ret = bq24257_field_write(bq, F_WD_EN, 0);
+       if (ret < 0)
+               return ret;
+
+       /* configure the charge currents and voltages */
+       for (i = 0; i < ARRAY_SIZE(init_data); i++) {
+               ret = bq24257_field_write(bq, init_data[i].field,
+                                         init_data[i].value);
+               if (ret < 0)
+                       return ret;
+       }
+
+       ret = bq24257_get_chip_state(bq, &state);
+       if (ret < 0)
+               return ret;
+
+       mutex_lock(&bq->lock);
+       bq->state = state;
+       mutex_unlock(&bq->lock);
+
+       if (!state.power_good)
+               /* activate D+/D- detection algorithm */
+               ret = bq24257_field_write(bq, F_DPDM_EN, 1);
+       else if (state.fault != FAULT_NO_BAT)
+               ret = bq24257_iilimit_autoset(bq);
+
+       return ret;
+}
+
+static enum power_supply_property bq24257_power_supply_props[] = {
+       POWER_SUPPLY_PROP_MANUFACTURER,
+       POWER_SUPPLY_PROP_STATUS,
+       POWER_SUPPLY_PROP_ONLINE,
+       POWER_SUPPLY_PROP_HEALTH,
+       POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,
+       POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
+       POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,
+       POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX,
+       POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT,
+};
+
+static char *bq24257_charger_supplied_to[] = {
+       "main-battery",
+};
+
+static const struct power_supply_desc bq24257_power_supply_desc = {
+       .name = "bq24257-charger",
+       .type = POWER_SUPPLY_TYPE_USB,
+       .properties = bq24257_power_supply_props,
+       .num_properties = ARRAY_SIZE(bq24257_power_supply_props),
+       .get_property = bq24257_power_supply_get_property,
+};
+
+static int bq24257_power_supply_init(struct bq24257_device *bq)
+{
+       struct power_supply_config psy_cfg = { .drv_data = bq, };
+
+       psy_cfg.supplied_to = bq24257_charger_supplied_to;
+       psy_cfg.num_supplicants = ARRAY_SIZE(bq24257_charger_supplied_to);
+
+       bq->charger = power_supply_register(bq->dev, &bq24257_power_supply_desc,
+                                           &psy_cfg);
+       if (IS_ERR(bq->charger))
+               return PTR_ERR(bq->charger);
+
+       return 0;
+}
+
+static int bq24257_irq_probe(struct bq24257_device *bq)
+{
+       struct gpio_desc *stat_irq;
+
+       stat_irq = devm_gpiod_get_index(bq->dev, BQ24257_STAT_IRQ, 0, GPIOD_IN);
+       if (IS_ERR(stat_irq)) {
+               dev_err(bq->dev, "could not probe stat_irq pin\n");
+               return PTR_ERR(stat_irq);
+       }
+
+       return gpiod_to_irq(stat_irq);
+}
+
+static int bq24257_pg_gpio_probe(struct bq24257_device *bq)
+{
+       bq->pg = devm_gpiod_get_index(bq->dev, BQ24257_PG_GPIO, 0, GPIOD_IN);
+       if (IS_ERR(bq->pg)) {
+               dev_err(bq->dev, "could not probe PG pin\n");
+               return PTR_ERR(bq->pg);
+       }
+
+       return 0;
+}
+
+static int bq24257_fw_probe(struct bq24257_device *bq)
+{
+       int ret;
+       u32 property;
+
+       ret = device_property_read_u32(bq->dev, "ti,charge-current", &property);
+       if (ret < 0)
+               return ret;
+
+       bq->init_data.ichg = bq24257_find_idx(property, bq24257_ichg_map,
+                                             BQ24257_ICHG_MAP_SIZE);
+
+       ret = device_property_read_u32(bq->dev, "ti,battery-regulation-voltage",
+                                      &property);
+       if (ret < 0)
+               return ret;
+
+       bq->init_data.vbat = bq24257_find_idx(property, bq24257_vbat_map,
+                                             BQ24257_VBAT_MAP_SIZE);
+
+       ret = device_property_read_u32(bq->dev, "ti,termination-current",
+                                      &property);
+       if (ret < 0)
+               return ret;
+
+       bq->init_data.iterm = bq24257_find_idx(property, bq24257_iterm_map,
+                                              BQ24257_ITERM_MAP_SIZE);
+
+       return 0;
+}
+
+static int bq24257_probe(struct i2c_client *client,
+                        const struct i2c_device_id *id)
+{
+       struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
+       struct device *dev = &client->dev;
+       struct bq24257_device *bq;
+       int ret;
+       int i;
+
+       if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
+               dev_err(dev, "No support for SMBUS_BYTE_DATA\n");
+               return -ENODEV;
+       }
+
+       bq = devm_kzalloc(dev, sizeof(*bq), GFP_KERNEL);
+       if (!bq)
+               return -ENOMEM;
+
+       bq->client = client;
+       bq->dev = dev;
+
+       mutex_init(&bq->lock);
+
+       bq->rmap = devm_regmap_init_i2c(client, &bq24257_regmap_config);
+       if (IS_ERR(bq->rmap)) {
+               dev_err(dev, "failed to allocate register map\n");
+               return PTR_ERR(bq->rmap);
+       }
+
+       for (i = 0; i < ARRAY_SIZE(bq24257_reg_fields); i++) {
+               const struct reg_field *reg_fields = bq24257_reg_fields;
+
+               bq->rmap_fields[i] = devm_regmap_field_alloc(dev, bq->rmap,
+                                                            reg_fields[i]);
+               if (IS_ERR(bq->rmap_fields[i])) {
+                       dev_err(dev, "cannot allocate regmap field\n");
+                       return PTR_ERR(bq->rmap_fields[i]);
+               }
+       }
+
+       i2c_set_clientdata(client, bq);
+
+       INIT_DELAYED_WORK(&bq->iilimit_setup_work, bq24257_iilimit_setup_work);
+
+       if (!dev->platform_data) {
+               ret = bq24257_fw_probe(bq);
+               if (ret < 0) {
+                       dev_err(dev, "Cannot read device properties.\n");
+                       return ret;
+               }
+       } else {
+               return -ENODEV;
+       }
+
+       /* we can only check Power Good status by probing the PG pin */
+       ret = bq24257_pg_gpio_probe(bq);
+       if (ret < 0)
+               return ret;
+
+       /* reset all registers to defaults */
+       ret = bq24257_field_write(bq, F_RESET, 1);
+       if (ret < 0)
+               return ret;
+
+       /*
+        * Put the RESET bit back to 0, in cache. For some reason the HW always
+        * returns 1 on this bit, so this is the only way to avoid resetting the
+        * chip every time we update another field in this register.
+        */
+       ret = bq24257_field_write(bq, F_RESET, 0);
+       if (ret < 0)
+               return ret;
+
+       ret = bq24257_hw_init(bq);
+       if (ret < 0) {
+               dev_err(dev, "Cannot initialize the chip.\n");
+               return ret;
+       }
+
+       if (client->irq <= 0)
+               client->irq = bq24257_irq_probe(bq);
+
+       if (client->irq < 0) {
+               dev_err(dev, "no irq resource found\n");
+               return client->irq;
+       }
+
+       ret = devm_request_threaded_irq(dev, client->irq, NULL,
+                                       bq24257_irq_handler_thread,
+                                       IRQF_TRIGGER_FALLING |
+                                       IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+                                       BQ24257_STAT_IRQ, bq);
+       if (ret)
+               return ret;
+
+       ret = bq24257_power_supply_init(bq);
+       if (ret < 0)
+               dev_err(dev, "Failed to register power supply\n");
+
+       return ret;
+}
+
+static int bq24257_remove(struct i2c_client *client)
+{
+       struct bq24257_device *bq = i2c_get_clientdata(client);
+
+       cancel_delayed_work_sync(&bq->iilimit_setup_work);
+
+       power_supply_unregister(bq->charger);
+
+       bq24257_field_write(bq, F_RESET, 1); /* reset to defaults */
+
+       return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int bq24257_suspend(struct device *dev)
+{
+       struct bq24257_device *bq = dev_get_drvdata(dev);
+       int ret = 0;
+
+       cancel_delayed_work_sync(&bq->iilimit_setup_work);
+
+       /* reset all registers to default (and activate standalone mode) */
+       ret = bq24257_field_write(bq, F_RESET, 1);
+       if (ret < 0)
+               dev_err(bq->dev, "Cannot reset chip to standalone mode.\n");
+
+       return ret;
+}
+
+static int bq24257_resume(struct device *dev)
+{
+       int ret;
+       struct bq24257_device *bq = dev_get_drvdata(dev);
+
+       ret = regcache_drop_region(bq->rmap, BQ24257_REG_1, BQ24257_REG_7);
+       if (ret < 0)
+               return ret;
+
+       ret = bq24257_field_write(bq, F_RESET, 0);
+       if (ret < 0)
+               return ret;
+
+       ret = bq24257_hw_init(bq);
+       if (ret < 0) {
+               dev_err(bq->dev, "Cannot init chip after resume.\n");
+               return ret;
+       }
+
+       /* signal userspace, maybe state changed while suspended */
+       power_supply_changed(bq->charger);
+
+       return 0;
+}
+#endif
+
+static const struct dev_pm_ops bq24257_pm = {
+       SET_SYSTEM_SLEEP_PM_OPS(bq24257_suspend, bq24257_resume)
+};
+
+static const struct i2c_device_id bq24257_i2c_ids[] = {
+       { "bq24257", 0 },
+       {},
+};
+MODULE_DEVICE_TABLE(i2c, bq24257_i2c_ids);
+
+static const struct of_device_id bq24257_of_match[] = {
+       { .compatible = "ti,bq24257", },
+       { },
+};
+MODULE_DEVICE_TABLE(of, bq24257_of_match);
+
+static const struct acpi_device_id bq24257_acpi_match[] = {
+       {"BQ242570", 0},
+       {},
+};
+MODULE_DEVICE_TABLE(acpi, bq24257_acpi_match);
+
+static struct i2c_driver bq24257_driver = {
+       .driver = {
+               .name = "bq24257-charger",
+               .of_match_table = of_match_ptr(bq24257_of_match),
+               .acpi_match_table = ACPI_PTR(bq24257_acpi_match),
+               .pm = &bq24257_pm,
+       },
+       .probe = bq24257_probe,
+       .remove = bq24257_remove,
+       .id_table = bq24257_i2c_ids,
+};
+module_i2c_driver(bq24257_driver);
+
+MODULE_AUTHOR("Laurentiu Palcu <laurentiu.palcu@intel.com>");
+MODULE_DESCRIPTION("bq24257 charger driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/power/bq25890_charger.c b/drivers/power/bq25890_charger.c
new file mode 100644 (file)
index 0000000..f993a55
--- /dev/null
@@ -0,0 +1,994 @@
+/*
+ * TI BQ25890 charger driver
+ *
+ * Copyright (C) 2015 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/power_supply.h>
+#include <linux/regmap.h>
+#include <linux/types.h>
+#include <linux/gpio/consumer.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/usb/phy.h>
+
+#include <linux/acpi.h>
+#include <linux/of.h>
+
+#define BQ25890_MANUFACTURER           "Texas Instruments"
+#define BQ25890_IRQ_PIN                        "bq25890_irq"
+
+#define BQ25890_ID                     3
+
+enum bq25890_fields {
+       F_EN_HIZ, F_EN_ILIM, F_IILIM,                                /* Reg00 */
+       F_BHOT, F_BCOLD, F_VINDPM_OFS,                               /* Reg01 */
+       F_CONV_START, F_CONV_RATE, F_BOOSTF, F_ICO_EN,
+       F_HVDCP_EN, F_MAXC_EN, F_FORCE_DPM, F_AUTO_DPDM_EN,          /* Reg02 */
+       F_BAT_LOAD_EN, F_WD_RST, F_OTG_CFG, F_CHG_CFG, F_SYSVMIN,    /* Reg03 */
+       F_PUMPX_EN, F_ICHG,                                          /* Reg04 */
+       F_IPRECHG, F_ITERM,                                          /* Reg05 */
+       F_VREG, F_BATLOWV, F_VRECHG,                                 /* Reg06 */
+       F_TERM_EN, F_STAT_DIS, F_WD, F_TMR_EN, F_CHG_TMR,
+       F_JEITA_ISET,                                                /* Reg07 */
+       F_BATCMP, F_VCLAMP, F_TREG,                                  /* Reg08 */
+       F_FORCE_ICO, F_TMR2X_EN, F_BATFET_DIS, F_JEITA_VSET,
+       F_BATFET_DLY, F_BATFET_RST_EN, F_PUMPX_UP, F_PUMPX_DN,       /* Reg09 */
+       F_BOOSTV, F_BOOSTI,                                          /* Reg0A */
+       F_VBUS_STAT, F_CHG_STAT, F_PG_STAT, F_SDP_STAT, F_VSYS_STAT, /* Reg0B */
+       F_WD_FAULT, F_BOOST_FAULT, F_CHG_FAULT, F_BAT_FAULT,
+       F_NTC_FAULT,                                                 /* Reg0C */
+       F_FORCE_VINDPM, F_VINDPM,                                    /* Reg0D */
+       F_THERM_STAT, F_BATV,                                        /* Reg0E */
+       F_SYSV,                                                      /* Reg0F */
+       F_TSPCT,                                                     /* Reg10 */
+       F_VBUS_GD, F_VBUSV,                                          /* Reg11 */
+       F_ICHGR,                                                     /* Reg12 */
+       F_VDPM_STAT, F_IDPM_STAT, F_IDPM_LIM,                        /* Reg13 */
+       F_REG_RST, F_ICO_OPTIMIZED, F_PN, F_TS_PROFILE, F_DEV_REV,   /* Reg14 */
+
+       F_MAX_FIELDS
+};
+
+/* initial field values, converted to register values */
+struct bq25890_init_data {
+       u8 ichg;        /* charge current               */
+       u8 vreg;        /* regulation voltage           */
+       u8 iterm;       /* termination current          */
+       u8 iprechg;     /* precharge current            */
+       u8 sysvmin;     /* minimum system voltage limit */
+       u8 boostv;      /* boost regulation voltage     */
+       u8 boosti;      /* boost current limit          */
+       u8 boostf;      /* boost frequency              */
+       u8 ilim_en;     /* enable ILIM pin              */
+       u8 treg;        /* thermal regulation threshold */
+};
+
+struct bq25890_state {
+       u8 online;
+       u8 chrg_status;
+       u8 chrg_fault;
+       u8 vsys_status;
+       u8 boost_fault;
+       u8 bat_fault;
+};
+
+struct bq25890_device {
+       struct i2c_client *client;
+       struct device *dev;
+       struct power_supply *charger;
+
+       struct usb_phy *usb_phy;
+       struct notifier_block usb_nb;
+       struct work_struct usb_work;
+       unsigned long usb_event;
+
+       struct regmap *rmap;
+       struct regmap_field *rmap_fields[F_MAX_FIELDS];
+
+       int chip_id;
+       struct bq25890_init_data init_data;
+       struct bq25890_state state;
+
+       struct mutex lock; /* protect state data */
+};
+
+static const struct regmap_range bq25890_readonly_reg_ranges[] = {
+       regmap_reg_range(0x0b, 0x0c),
+       regmap_reg_range(0x0e, 0x13),
+};
+
+static const struct regmap_access_table bq25890_writeable_regs = {
+       .no_ranges = bq25890_readonly_reg_ranges,
+       .n_no_ranges = ARRAY_SIZE(bq25890_readonly_reg_ranges),
+};
+
+static const struct regmap_range bq25890_volatile_reg_ranges[] = {
+       regmap_reg_range(0x00, 0x00),
+       regmap_reg_range(0x09, 0x09),
+       regmap_reg_range(0x0b, 0x0c),
+       regmap_reg_range(0x0e, 0x14),
+};
+
+static const struct regmap_access_table bq25890_volatile_regs = {
+       .yes_ranges = bq25890_volatile_reg_ranges,
+       .n_yes_ranges = ARRAY_SIZE(bq25890_volatile_reg_ranges),
+};
+
+static const struct regmap_config bq25890_regmap_config = {
+       .reg_bits = 8,
+       .val_bits = 8,
+
+       .max_register = 0x14,
+       .cache_type = REGCACHE_RBTREE,
+
+       .wr_table = &bq25890_writeable_regs,
+       .volatile_table = &bq25890_volatile_regs,
+};
+
+static const struct reg_field bq25890_reg_fields[] = {
+       /* REG00 */
+       [F_EN_HIZ]              = REG_FIELD(0x00, 7, 7),
+       [F_EN_ILIM]             = REG_FIELD(0x00, 6, 6),
+       [F_IILIM]               = REG_FIELD(0x00, 0, 5),
+       /* REG01 */
+       [F_BHOT]                = REG_FIELD(0x01, 6, 7),
+       [F_BCOLD]               = REG_FIELD(0x01, 5, 5),
+       [F_VINDPM_OFS]          = REG_FIELD(0x01, 0, 4),
+       /* REG02 */
+       [F_CONV_START]          = REG_FIELD(0x02, 7, 7),
+       [F_CONV_RATE]           = REG_FIELD(0x02, 6, 6),
+       [F_BOOSTF]              = REG_FIELD(0x02, 5, 5),
+       [F_ICO_EN]              = REG_FIELD(0x02, 4, 4),
+       [F_HVDCP_EN]            = REG_FIELD(0x02, 3, 3),
+       [F_MAXC_EN]             = REG_FIELD(0x02, 2, 2),
+       [F_FORCE_DPM]           = REG_FIELD(0x02, 1, 1),
+       [F_AUTO_DPDM_EN]        = REG_FIELD(0x02, 0, 0),
+       /* REG03 */
+       [F_BAT_LOAD_EN]         = REG_FIELD(0x03, 7, 7),
+       [F_WD_RST]              = REG_FIELD(0x03, 6, 6),
+       [F_OTG_CFG]             = REG_FIELD(0x03, 5, 5),
+       [F_CHG_CFG]             = REG_FIELD(0x03, 4, 4),
+       [F_SYSVMIN]             = REG_FIELD(0x03, 1, 3),
+       /* REG04 */
+       [F_PUMPX_EN]            = REG_FIELD(0x04, 7, 7),
+       [F_ICHG]                = REG_FIELD(0x04, 0, 6),
+       /* REG05 */
+       [F_IPRECHG]             = REG_FIELD(0x05, 4, 7),
+       [F_ITERM]               = REG_FIELD(0x05, 0, 3),
+       /* REG06 */
+       [F_VREG]                = REG_FIELD(0x06, 2, 7),
+       [F_BATLOWV]             = REG_FIELD(0x06, 1, 1),
+       [F_VRECHG]              = REG_FIELD(0x06, 0, 0),
+       /* REG07 */
+       [F_TERM_EN]             = REG_FIELD(0x07, 7, 7),
+       [F_STAT_DIS]            = REG_FIELD(0x07, 6, 6),
+       [F_WD]                  = REG_FIELD(0x07, 4, 5),
+       [F_TMR_EN]              = REG_FIELD(0x07, 3, 3),
+       [F_CHG_TMR]             = REG_FIELD(0x07, 1, 2),
+       [F_JEITA_ISET]          = REG_FIELD(0x07, 0, 0),
+       /* REG08 */
+       [F_BATCMP]              = REG_FIELD(0x08, 6, 7),
+       [F_VCLAMP]              = REG_FIELD(0x08, 2, 4),
+       [F_TREG]                = REG_FIELD(0x08, 0, 1),
+       /* REG09 */
+       [F_FORCE_ICO]           = REG_FIELD(0x09, 7, 7),
+       [F_TMR2X_EN]            = REG_FIELD(0x09, 6, 6),
+       [F_BATFET_DIS]          = REG_FIELD(0x09, 5, 5),
+       [F_JEITA_VSET]          = REG_FIELD(0x09, 4, 4),
+       [F_BATFET_DLY]          = REG_FIELD(0x09, 3, 3),
+       [F_BATFET_RST_EN]       = REG_FIELD(0x09, 2, 2),
+       [F_PUMPX_UP]            = REG_FIELD(0x09, 1, 1),
+       [F_PUMPX_DN]            = REG_FIELD(0x09, 0, 0),
+       /* REG0A */
+       [F_BOOSTV]              = REG_FIELD(0x0A, 4, 7),
+       [F_BOOSTI]              = REG_FIELD(0x0A, 0, 2),
+       /* REG0B */
+       [F_VBUS_STAT]           = REG_FIELD(0x0B, 5, 7),
+       [F_CHG_STAT]            = REG_FIELD(0x0B, 3, 4),
+       [F_PG_STAT]             = REG_FIELD(0x0B, 2, 2),
+       [F_SDP_STAT]            = REG_FIELD(0x0B, 1, 1),
+       [F_VSYS_STAT]           = REG_FIELD(0x0B, 0, 0),
+       /* REG0C */
+       [F_WD_FAULT]            = REG_FIELD(0x0C, 7, 7),
+       [F_BOOST_FAULT]         = REG_FIELD(0x0C, 6, 6),
+       [F_CHG_FAULT]           = REG_FIELD(0x0C, 4, 5),
+       [F_BAT_FAULT]           = REG_FIELD(0x0C, 3, 3),
+       [F_NTC_FAULT]           = REG_FIELD(0x0C, 0, 2),
+       /* REG0D */
+       [F_FORCE_VINDPM]        = REG_FIELD(0x0D, 7, 7),
+       [F_VINDPM]              = REG_FIELD(0x0D, 0, 6),
+       /* REG0E */
+       [F_THERM_STAT]          = REG_FIELD(0x0E, 7, 7),
+       [F_BATV]                = REG_FIELD(0x0E, 0, 6),
+       /* REG0F */
+       [F_SYSV]                = REG_FIELD(0x0F, 0, 6),
+       /* REG10 */
+       [F_TSPCT]               = REG_FIELD(0x10, 0, 6),
+       /* REG11 */
+       [F_VBUS_GD]             = REG_FIELD(0x11, 7, 7),
+       [F_VBUSV]               = REG_FIELD(0x11, 0, 6),
+       /* REG12 */
+       [F_ICHGR]               = REG_FIELD(0x12, 0, 6),
+       /* REG13 */
+       [F_VDPM_STAT]           = REG_FIELD(0x13, 7, 7),
+       [F_IDPM_STAT]           = REG_FIELD(0x13, 6, 6),
+       [F_IDPM_LIM]            = REG_FIELD(0x13, 0, 5),
+       /* REG14 */
+       [F_REG_RST]             = REG_FIELD(0x14, 7, 7),
+       [F_ICO_OPTIMIZED]       = REG_FIELD(0x14, 6, 6),
+       [F_PN]                  = REG_FIELD(0x14, 3, 5),
+       [F_TS_PROFILE]          = REG_FIELD(0x14, 2, 2),
+       [F_DEV_REV]             = REG_FIELD(0x14, 0, 1)
+};
+
+/*
+ * Most of the val -> idx conversions can be computed, given the minimum,
+ * maximum and the step between values. For the rest of conversions, we use
+ * lookup tables.
+ */
+enum bq25890_table_ids {
+       /* range tables */
+       TBL_ICHG,
+       TBL_ITERM,
+       TBL_IPRECHG,
+       TBL_VREG,
+       TBL_BATCMP,
+       TBL_VCLAMP,
+       TBL_BOOSTV,
+       TBL_SYSVMIN,
+
+       /* lookup tables */
+       TBL_TREG,
+       TBL_BOOSTI,
+};
+
+/* Thermal Regulation Threshold lookup table, in degrees Celsius */
+static const u32 bq25890_treg_tbl[] = { 60, 80, 100, 120 };
+
+#define BQ25890_TREG_TBL_SIZE          ARRAY_SIZE(bq25890_treg_tbl)
+
+/* Boost mode current limit lookup table, in uA */
+static const u32 bq25890_boosti_tbl[] = {
+       500000, 700000, 1100000, 1300000, 1600000, 1800000, 2100000, 2400000
+};
+
+#define BQ25890_BOOSTI_TBL_SIZE                ARRAY_SIZE(bq25890_boosti_tbl)
+
+struct bq25890_range {
+       u32 min;
+       u32 max;
+       u32 step;
+};
+
+struct bq25890_lookup {
+       const u32 *tbl;
+       u32 size;
+};
+
+static const union {
+       struct bq25890_range  rt;
+       struct bq25890_lookup lt;
+} bq25890_tables[] = {
+       /* range tables */
+       [TBL_ICHG] =    { .rt = {0,       5056000, 64000} },     /* uA */
+       [TBL_ITERM] =   { .rt = {64000,   1024000, 64000} },     /* uA */
+       [TBL_VREG] =    { .rt = {3840000, 4608000, 16000} },     /* uV */
+       [TBL_BATCMP] =  { .rt = {0,       140,     20} },        /* mOhm */
+       [TBL_VCLAMP] =  { .rt = {0,       224000,  32000} },     /* uV */
+       [TBL_BOOSTV] =  { .rt = {4550000, 5510000, 64000} },     /* uV */
+       [TBL_SYSVMIN] = { .rt = {3000000, 3700000, 100000} },    /* uV */
+
+       /* lookup tables */
+       [TBL_TREG] =    { .lt = {bq25890_treg_tbl, BQ25890_TREG_TBL_SIZE} },
+       [TBL_BOOSTI] =  { .lt = {bq25890_boosti_tbl, BQ25890_BOOSTI_TBL_SIZE} }
+};
+
+static int bq25890_field_read(struct bq25890_device *bq,
+                             enum bq25890_fields field_id)
+{
+       int ret;
+       int val;
+
+       ret = regmap_field_read(bq->rmap_fields[field_id], &val);
+       if (ret < 0)
+               return ret;
+
+       return val;
+}
+
+static int bq25890_field_write(struct bq25890_device *bq,
+                              enum bq25890_fields field_id, u8 val)
+{
+       return regmap_field_write(bq->rmap_fields[field_id], val);
+}
+
+static u8 bq25890_find_idx(u32 value, enum bq25890_table_ids id)
+{
+       u8 idx;
+
+       if (id >= TBL_TREG) {
+               const u32 *tbl = bq25890_tables[id].lt.tbl;
+               u32 tbl_size = bq25890_tables[id].lt.size;
+
+               for (idx = 1; idx < tbl_size && tbl[idx] <= value; idx++)
+                       ;
+       } else {
+               const struct bq25890_range *rtbl = &bq25890_tables[id].rt;
+               u8 rtbl_size;
+
+               rtbl_size = (rtbl->max - rtbl->min) / rtbl->step + 1;
+
+               for (idx = 1;
+                    idx < rtbl_size && (idx * rtbl->step + rtbl->min <= value);
+                    idx++)
+                       ;
+       }
+
+       return idx - 1;
+}
+
+static u32 bq25890_find_val(u8 idx, enum bq25890_table_ids id)
+{
+       const struct bq25890_range *rtbl;
+
+       /* lookup table? */
+       if (id >= TBL_TREG)
+               return bq25890_tables[id].lt.tbl[idx];
+
+       /* range table */
+       rtbl = &bq25890_tables[id].rt;
+
+       return (rtbl->min + idx * rtbl->step);
+}
+
+enum bq25890_status {
+       STATUS_NOT_CHARGING,
+       STATUS_PRE_CHARGING,
+       STATUS_FAST_CHARGING,
+       STATUS_TERMINATION_DONE,
+};
+
+enum bq25890_chrg_fault {
+       CHRG_FAULT_NORMAL,
+       CHRG_FAULT_INPUT,
+       CHRG_FAULT_THERMAL_SHUTDOWN,
+       CHRG_FAULT_TIMER_EXPIRED,
+};
+
+static int bq25890_power_supply_get_property(struct power_supply *psy,
+                                            enum power_supply_property psp,
+                                            union power_supply_propval *val)
+{
+       int ret;
+       struct bq25890_device *bq = power_supply_get_drvdata(psy);
+       struct bq25890_state state;
+
+       mutex_lock(&bq->lock);
+       state = bq->state;
+       mutex_unlock(&bq->lock);
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_STATUS:
+               if (!state.online)
+                       val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
+               else if (state.chrg_status == STATUS_NOT_CHARGING)
+                       val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
+               else if (state.chrg_status == STATUS_PRE_CHARGING ||
+                        state.chrg_status == STATUS_FAST_CHARGING)
+                       val->intval = POWER_SUPPLY_STATUS_CHARGING;
+               else if (state.chrg_status == STATUS_TERMINATION_DONE)
+                       val->intval = POWER_SUPPLY_STATUS_FULL;
+               else
+                       val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
+
+               break;
+
+       case POWER_SUPPLY_PROP_MANUFACTURER:
+               val->strval = BQ25890_MANUFACTURER;
+               break;
+
+       case POWER_SUPPLY_PROP_ONLINE:
+               val->intval = state.online;
+               break;
+
+       case POWER_SUPPLY_PROP_HEALTH:
+               if (!state.chrg_fault && !state.bat_fault && !state.boost_fault)
+                       val->intval = POWER_SUPPLY_HEALTH_GOOD;
+               else if (state.bat_fault)
+                       val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
+               else if (state.chrg_fault == CHRG_FAULT_TIMER_EXPIRED)
+                       val->intval = POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE;
+               else if (state.chrg_fault == CHRG_FAULT_THERMAL_SHUTDOWN)
+                       val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
+               else
+                       val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
+               break;
+
+       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
+               ret = bq25890_field_read(bq, F_ICHGR); /* read measured value */
+               if (ret < 0)
+                       return ret;
+
+               /* converted_val = ADC_val * 50mA (table 10.3.19) */
+               val->intval = ret * 50000;
+               break;
+
+       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
+               val->intval = bq25890_tables[TBL_ICHG].rt.max;
+               break;
+
+       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
+               if (!state.online) {
+                       val->intval = 0;
+                       break;
+               }
+
+               ret = bq25890_field_read(bq, F_BATV); /* read measured value */
+               if (ret < 0)
+                       return ret;
+
+               /* converted_val = 2.304V + ADC_val * 20mV (table 10.3.15) */
+               val->intval = 2304000 + ret * 20000;
+               break;
+
+       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX:
+               val->intval = bq25890_tables[TBL_VREG].rt.max;
+               break;
+
+       case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT:
+               val->intval = bq25890_find_val(bq->init_data.iterm, TBL_ITERM);
+               break;
+
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int bq25890_get_chip_state(struct bq25890_device *bq,
+                                 struct bq25890_state *state)
+{
+       int i, ret;
+
+       struct {
+               enum bq25890_fields id;
+               u8 *data;
+       } state_fields[] = {
+               {F_CHG_STAT,    &state->chrg_status},
+               {F_PG_STAT,     &state->online},
+               {F_VSYS_STAT,   &state->vsys_status},
+               {F_BOOST_FAULT, &state->boost_fault},
+               {F_BAT_FAULT,   &state->bat_fault},
+               {F_CHG_FAULT,   &state->chrg_fault}
+       };
+
+       for (i = 0; i < ARRAY_SIZE(state_fields); i++) {
+               ret = bq25890_field_read(bq, state_fields[i].id);
+               if (ret < 0)
+                       return ret;
+
+               *state_fields[i].data = ret;
+       }
+
+       dev_dbg(bq->dev, "S:CHG/PG/VSYS=%d/%d/%d, F:CHG/BOOST/BAT=%d/%d/%d\n",
+               state->chrg_status, state->online, state->vsys_status,
+               state->chrg_fault, state->boost_fault, state->bat_fault);
+
+       return 0;
+}
+
+static bool bq25890_state_changed(struct bq25890_device *bq,
+                                 struct bq25890_state *new_state)
+{
+       struct bq25890_state old_state;
+
+       mutex_lock(&bq->lock);
+       old_state = bq->state;
+       mutex_unlock(&bq->lock);
+
+       return (old_state.chrg_status != new_state->chrg_status ||
+               old_state.chrg_fault != new_state->chrg_fault   ||
+               old_state.online != new_state->online           ||
+               old_state.bat_fault != new_state->bat_fault     ||
+               old_state.boost_fault != new_state->boost_fault ||
+               old_state.vsys_status != new_state->vsys_status);
+}
+
+static void bq25890_handle_state_change(struct bq25890_device *bq,
+                                       struct bq25890_state *new_state)
+{
+       int ret;
+       struct bq25890_state old_state;
+
+       mutex_lock(&bq->lock);
+       old_state = bq->state;
+       mutex_unlock(&bq->lock);
+
+       if (!new_state->online) {                            /* power removed */
+               /* disable ADC */
+               ret = bq25890_field_write(bq, F_CONV_START, 0);
+               if (ret < 0)
+                       goto error;
+       } else if (!old_state.online) {                     /* power inserted */
+               /* enable ADC, to have control of charge current/voltage */
+               ret = bq25890_field_write(bq, F_CONV_START, 1);
+               if (ret < 0)
+                       goto error;
+       }
+
+       return;
+
+error:
+       dev_err(bq->dev, "Error communicating with the chip.\n");
+}
+
+static irqreturn_t bq25890_irq_handler_thread(int irq, void *private)
+{
+       struct bq25890_device *bq = private;
+       int ret;
+       struct bq25890_state state;
+
+       ret = bq25890_get_chip_state(bq, &state);
+       if (ret < 0)
+               goto handled;
+
+       if (!bq25890_state_changed(bq, &state))
+               goto handled;
+
+       bq25890_handle_state_change(bq, &state);
+
+       mutex_lock(&bq->lock);
+       bq->state = state;
+       mutex_unlock(&bq->lock);
+
+       power_supply_changed(bq->charger);
+
+handled:
+       return IRQ_HANDLED;
+}
+
+static int bq25890_chip_reset(struct bq25890_device *bq)
+{
+       int ret;
+       int rst_check_counter = 10;
+
+       ret = bq25890_field_write(bq, F_REG_RST, 1);
+       if (ret < 0)
+               return ret;
+
+       do {
+               ret = bq25890_field_read(bq, F_REG_RST);
+               if (ret < 0)
+                       return ret;
+
+               usleep_range(5, 10);
+       } while (ret == 1 && --rst_check_counter);
+
+       if (!rst_check_counter)
+               return -ETIMEDOUT;
+
+       return 0;
+}
+
+static int bq25890_hw_init(struct bq25890_device *bq)
+{
+       int ret;
+       int i;
+       struct bq25890_state state;
+
+       const struct {
+               enum bq25890_fields id;
+               u32 value;
+       } init_data[] = {
+               {F_ICHG,         bq->init_data.ichg},
+               {F_VREG,         bq->init_data.vreg},
+               {F_ITERM,        bq->init_data.iterm},
+               {F_IPRECHG,      bq->init_data.iprechg},
+               {F_SYSVMIN,      bq->init_data.sysvmin},
+               {F_BOOSTV,       bq->init_data.boostv},
+               {F_BOOSTI,       bq->init_data.boosti},
+               {F_BOOSTF,       bq->init_data.boostf},
+               {F_EN_ILIM,      bq->init_data.ilim_en},
+               {F_TREG,         bq->init_data.treg}
+       };
+
+       ret = bq25890_chip_reset(bq);
+       if (ret < 0)
+               return ret;
+
+       /* disable watchdog */
+       ret = bq25890_field_write(bq, F_WD, 0);
+       if (ret < 0)
+               return ret;
+
+       /* initialize currents/voltages and other parameters */
+       for (i = 0; i < ARRAY_SIZE(init_data); i++) {
+               ret = bq25890_field_write(bq, init_data[i].id,
+                                         init_data[i].value);
+               if (ret < 0)
+                       return ret;
+       }
+
+       /* Configure ADC for continuous conversions. This does not enable it. */
+       ret = bq25890_field_write(bq, F_CONV_RATE, 1);
+       if (ret < 0)
+               return ret;
+
+       ret = bq25890_get_chip_state(bq, &state);
+       if (ret < 0)
+               return ret;
+
+       mutex_lock(&bq->lock);
+       bq->state = state;
+       mutex_unlock(&bq->lock);
+
+       return 0;
+}
+
+static enum power_supply_property bq25890_power_supply_props[] = {
+       POWER_SUPPLY_PROP_MANUFACTURER,
+       POWER_SUPPLY_PROP_STATUS,
+       POWER_SUPPLY_PROP_ONLINE,
+       POWER_SUPPLY_PROP_HEALTH,
+       POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,
+       POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
+       POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,
+       POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX,
+       POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT,
+};
+
+static char *bq25890_charger_supplied_to[] = {
+       "main-battery",
+};
+
+static const struct power_supply_desc bq25890_power_supply_desc = {
+       .name = "bq25890-charger",
+       .type = POWER_SUPPLY_TYPE_USB,
+       .properties = bq25890_power_supply_props,
+       .num_properties = ARRAY_SIZE(bq25890_power_supply_props),
+       .get_property = bq25890_power_supply_get_property,
+};
+
+static int bq25890_power_supply_init(struct bq25890_device *bq)
+{
+       struct power_supply_config psy_cfg = { .drv_data = bq, };
+
+       psy_cfg.supplied_to = bq25890_charger_supplied_to;
+       psy_cfg.num_supplicants = ARRAY_SIZE(bq25890_charger_supplied_to);
+
+       bq->charger = power_supply_register(bq->dev, &bq25890_power_supply_desc,
+                                           &psy_cfg);
+
+       return PTR_ERR_OR_ZERO(bq->charger);
+}
+
+static void bq25890_usb_work(struct work_struct *data)
+{
+       int ret;
+       struct bq25890_device *bq =
+                       container_of(data, struct bq25890_device, usb_work);
+
+       switch (bq->usb_event) {
+       case USB_EVENT_ID:
+               /* Enable boost mode */
+               ret = bq25890_field_write(bq, F_OTG_CFG, 1);
+               if (ret < 0)
+                       goto error;
+               break;
+
+       case USB_EVENT_NONE:
+               /* Disable boost mode */
+               ret = bq25890_field_write(bq, F_OTG_CFG, 0);
+               if (ret < 0)
+                       goto error;
+
+               power_supply_changed(bq->charger);
+               break;
+       }
+
+       return;
+
+error:
+       dev_err(bq->dev, "Error switching to boost/charger mode.\n");
+}
+
+static int bq25890_usb_notifier(struct notifier_block *nb, unsigned long val,
+                               void *priv)
+{
+       struct bq25890_device *bq =
+                       container_of(nb, struct bq25890_device, usb_nb);
+
+       bq->usb_event = val;
+       queue_work(system_power_efficient_wq, &bq->usb_work);
+
+       return NOTIFY_OK;
+}
+
+static int bq25890_irq_probe(struct bq25890_device *bq)
+{
+       struct gpio_desc *irq;
+
+       irq = devm_gpiod_get_index(bq->dev, BQ25890_IRQ_PIN, 0, GPIOD_IN);
+       if (IS_ERR(irq)) {
+               dev_err(bq->dev, "Could not probe irq pin.\n");
+               return PTR_ERR(irq);
+       }
+
+       return gpiod_to_irq(irq);
+}
+
+static int bq25890_fw_read_u32_props(struct bq25890_device *bq)
+{
+       int ret;
+       u32 property;
+       int i;
+       struct bq25890_init_data *init = &bq->init_data;
+       struct {
+               char *name;
+               bool optional;
+               enum bq25890_table_ids tbl_id;
+               u8 *conv_data; /* holds converted value from given property */
+       } props[] = {
+               /* required properties */
+               {"ti,charge-current", false, TBL_ICHG, &init->ichg},
+               {"ti,battery-regulation-voltage", false, TBL_VREG, &init->vreg},
+               {"ti,termination-current", false, TBL_ITERM, &init->iterm},
+               {"ti,precharge-current", false, TBL_ITERM, &init->iprechg},
+               {"ti,minimum-sys-voltage", false, TBL_SYSVMIN, &init->sysvmin},
+               {"ti,boost-voltage", false, TBL_BOOSTV, &init->boostv},
+               {"ti,boost-max-current", false, TBL_BOOSTI, &init->boosti},
+
+               /* optional properties */
+               {"ti,thermal-regulation-threshold", true, TBL_TREG, &init->treg}
+       };
+
+       /* initialize data for optional properties */
+       init->treg = 3; /* 120 degrees Celsius */
+
+       for (i = 0; i < ARRAY_SIZE(props); i++) {
+               ret = device_property_read_u32(bq->dev, props[i].name,
+                                              &property);
+               if (ret < 0) {
+                       if (props[i].optional)
+                               continue;
+
+                       return ret;
+               }
+
+               *props[i].conv_data = bq25890_find_idx(property,
+                                                      props[i].tbl_id);
+       }
+
+       return 0;
+}
+
+static int bq25890_fw_probe(struct bq25890_device *bq)
+{
+       int ret;
+       struct bq25890_init_data *init = &bq->init_data;
+
+       ret = bq25890_fw_read_u32_props(bq);
+       if (ret < 0)
+               return ret;
+
+       init->ilim_en = device_property_read_bool(bq->dev, "ti,use-ilim-pin");
+       init->boostf = device_property_read_bool(bq->dev, "ti,boost-low-freq");
+
+       return 0;
+}
+
+static int bq25890_probe(struct i2c_client *client,
+                        const struct i2c_device_id *id)
+{
+       struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
+       struct device *dev = &client->dev;
+       struct bq25890_device *bq;
+       int ret;
+       int i;
+
+       if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
+               dev_err(dev, "No support for SMBUS_BYTE_DATA\n");
+               return -ENODEV;
+       }
+
+       bq = devm_kzalloc(dev, sizeof(*bq), GFP_KERNEL);
+       if (!bq)
+               return -ENOMEM;
+
+       bq->client = client;
+       bq->dev = dev;
+
+       mutex_init(&bq->lock);
+
+       bq->rmap = devm_regmap_init_i2c(client, &bq25890_regmap_config);
+       if (IS_ERR(bq->rmap)) {
+               dev_err(dev, "failed to allocate register map\n");
+               return PTR_ERR(bq->rmap);
+       }
+
+       for (i = 0; i < ARRAY_SIZE(bq25890_reg_fields); i++) {
+               const struct reg_field *reg_fields = bq25890_reg_fields;
+
+               bq->rmap_fields[i] = devm_regmap_field_alloc(dev, bq->rmap,
+                                                            reg_fields[i]);
+               if (IS_ERR(bq->rmap_fields[i])) {
+                       dev_err(dev, "cannot allocate regmap field\n");
+                       return PTR_ERR(bq->rmap_fields[i]);
+               }
+       }
+
+       i2c_set_clientdata(client, bq);
+
+       bq->chip_id = bq25890_field_read(bq, F_PN);
+       if (bq->chip_id < 0) {
+               dev_err(dev, "Cannot read chip ID.\n");
+               return bq->chip_id;
+       }
+
+       if (bq->chip_id != BQ25890_ID) {
+               dev_err(dev, "Chip with ID=%d, not supported!\n", bq->chip_id);
+               return -ENODEV;
+       }
+
+       if (!dev->platform_data) {
+               ret = bq25890_fw_probe(bq);
+               if (ret < 0) {
+                       dev_err(dev, "Cannot read device properties.\n");
+                       return ret;
+               }
+       } else {
+               return -ENODEV;
+       }
+
+       ret = bq25890_hw_init(bq);
+       if (ret < 0) {
+               dev_err(dev, "Cannot initialize the chip.\n");
+               return ret;
+       }
+
+       if (client->irq <= 0)
+               client->irq = bq25890_irq_probe(bq);
+
+       if (client->irq < 0) {
+               dev_err(dev, "No irq resource found.\n");
+               return client->irq;
+       }
+
+       /* OTG reporting */
+       bq->usb_phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2);
+       if (!IS_ERR_OR_NULL(bq->usb_phy)) {
+               INIT_WORK(&bq->usb_work, bq25890_usb_work);
+               bq->usb_nb.notifier_call = bq25890_usb_notifier;
+               usb_register_notifier(bq->usb_phy, &bq->usb_nb);
+       }
+
+       ret = devm_request_threaded_irq(dev, client->irq, NULL,
+                                       bq25890_irq_handler_thread,
+                                       IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+                                       BQ25890_IRQ_PIN, bq);
+       if (ret)
+               goto irq_fail;
+
+       ret = bq25890_power_supply_init(bq);
+       if (ret < 0) {
+               dev_err(dev, "Failed to register power supply\n");
+               goto irq_fail;
+       }
+
+       return 0;
+
+irq_fail:
+       if (!IS_ERR_OR_NULL(bq->usb_phy))
+               usb_unregister_notifier(bq->usb_phy, &bq->usb_nb);
+
+       return ret;
+}
+
+static int bq25890_remove(struct i2c_client *client)
+{
+       struct bq25890_device *bq = i2c_get_clientdata(client);
+
+       power_supply_unregister(bq->charger);
+
+       if (!IS_ERR_OR_NULL(bq->usb_phy))
+               usb_unregister_notifier(bq->usb_phy, &bq->usb_nb);
+
+       /* reset all registers to default values */
+       bq25890_chip_reset(bq);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int bq25890_suspend(struct device *dev)
+{
+       struct bq25890_device *bq = dev_get_drvdata(dev);
+
+       /*
+        * If charger is removed, while in suspend, make sure ADC is diabled
+        * since it consumes slightly more power.
+        */
+       return bq25890_field_write(bq, F_CONV_START, 0);
+}
+
+static int bq25890_resume(struct device *dev)
+{
+       int ret;
+       struct bq25890_state state;
+       struct bq25890_device *bq = dev_get_drvdata(dev);
+
+       ret = bq25890_get_chip_state(bq, &state);
+       if (ret < 0)
+               return ret;
+
+       mutex_lock(&bq->lock);
+       bq->state = state;
+       mutex_unlock(&bq->lock);
+
+       /* Re-enable ADC only if charger is plugged in. */
+       if (state.online) {
+               ret = bq25890_field_write(bq, F_CONV_START, 1);
+               if (ret < 0)
+                       return ret;
+       }
+
+       /* signal userspace, maybe state changed while suspended */
+       power_supply_changed(bq->charger);
+
+       return 0;
+}
+#endif
+
+static const struct dev_pm_ops bq25890_pm = {
+       SET_SYSTEM_SLEEP_PM_OPS(bq25890_suspend, bq25890_resume)
+};
+
+static const struct i2c_device_id bq25890_i2c_ids[] = {
+       { "bq25890", 0 },
+       {},
+};
+MODULE_DEVICE_TABLE(i2c, bq25890_i2c_ids);
+
+static const struct of_device_id bq25890_of_match[] = {
+       { .compatible = "ti,bq25890", },
+       { },
+};
+MODULE_DEVICE_TABLE(of, bq25890_of_match);
+
+static const struct acpi_device_id bq25890_acpi_match[] = {
+       {"BQ258900", 0},
+       {},
+};
+MODULE_DEVICE_TABLE(acpi, bq25890_acpi_match);
+
+static struct i2c_driver bq25890_driver = {
+       .driver = {
+               .name = "bq25890-charger",
+               .of_match_table = of_match_ptr(bq25890_of_match),
+               .acpi_match_table = ACPI_PTR(bq25890_acpi_match),
+               .pm = &bq25890_pm,
+       },
+       .probe = bq25890_probe,
+       .remove = bq25890_remove,
+       .id_table = bq25890_i2c_ids,
+};
+module_i2c_driver(bq25890_driver);
+
+MODULE_AUTHOR("Laurentiu Palcu <laurentiu.palcu@intel.com>");
+MODULE_DESCRIPTION("bq25890 charger driver");
+MODULE_LICENSE("GPL");
index 0aed13f908914a5dfdf0b9303ff95587e7552492..1c202ccbd2a61e741d7dd820a5b22741874dd50e 100644 (file)
@@ -1768,7 +1768,8 @@ static int charger_manager_probe(struct platform_device *pdev)
 
        INIT_DELAYED_WORK(&cm->fullbatt_vchk_work, fullbatt_vchk);
 
-       cm->charger_psy = power_supply_register(NULL, &cm->charger_psy_desc,
+       cm->charger_psy = power_supply_register(&pdev->dev,
+                                               &cm->charger_psy_desc,
                                                &psy_cfg);
        if (IS_ERR(cm->charger_psy)) {
                dev_err(&pdev->dev, "Cannot register charger-manager with name \"%s\"\n",
index 6cc5e87ec031073ebda3dfea04e4899096622ba1..e8925576474572c37da8b82d634d59b1dba85b15 100644 (file)
@@ -63,6 +63,8 @@
 #define dP_ACC_100     0x1900
 #define dP_ACC_200     0x3200
 
+#define MAX17042_VMAX_TOLERANCE                50 /* 50 mV */
+
 struct max17042_chip {
        struct i2c_client *client;
        struct regmap *regmap;
@@ -85,10 +87,94 @@ static enum power_supply_property max17042_battery_props[] = {
        POWER_SUPPLY_PROP_CHARGE_FULL,
        POWER_SUPPLY_PROP_CHARGE_COUNTER,
        POWER_SUPPLY_PROP_TEMP,
+       POWER_SUPPLY_PROP_TEMP_ALERT_MIN,
+       POWER_SUPPLY_PROP_TEMP_ALERT_MAX,
+       POWER_SUPPLY_PROP_TEMP_MIN,
+       POWER_SUPPLY_PROP_TEMP_MAX,
+       POWER_SUPPLY_PROP_HEALTH,
        POWER_SUPPLY_PROP_CURRENT_NOW,
        POWER_SUPPLY_PROP_CURRENT_AVG,
 };
 
+static int max17042_get_temperature(struct max17042_chip *chip, int *temp)
+{
+       int ret;
+       u32 data;
+       struct regmap *map = chip->regmap;
+
+       ret = regmap_read(map, MAX17042_TEMP, &data);
+       if (ret < 0)
+               return ret;
+
+       *temp = data;
+       /* The value is signed. */
+       if (*temp & 0x8000) {
+               *temp = (0x7fff & ~*temp) + 1;
+               *temp *= -1;
+       }
+
+       /* The value is converted into deci-centigrade scale */
+       /* Units of LSB = 1 / 256 degree Celsius */
+       *temp = *temp * 10 / 256;
+       return 0;
+}
+
+static int max17042_get_battery_health(struct max17042_chip *chip, int *health)
+{
+       int temp, vavg, vbatt, ret;
+       u32 val;
+
+       ret = regmap_read(chip->regmap, MAX17042_AvgVCELL, &val);
+       if (ret < 0)
+               goto health_error;
+
+       /* bits [0-3] unused */
+       vavg = val * 625 / 8;
+       /* Convert to millivolts */
+       vavg /= 1000;
+
+       ret = regmap_read(chip->regmap, MAX17042_VCELL, &val);
+       if (ret < 0)
+               goto health_error;
+
+       /* bits [0-3] unused */
+       vbatt = val * 625 / 8;
+       /* Convert to millivolts */
+       vbatt /= 1000;
+
+       if (vavg < chip->pdata->vmin) {
+               *health = POWER_SUPPLY_HEALTH_DEAD;
+               goto out;
+       }
+
+       if (vbatt > chip->pdata->vmax + MAX17042_VMAX_TOLERANCE) {
+               *health = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
+               goto out;
+       }
+
+       ret = max17042_get_temperature(chip, &temp);
+       if (ret < 0)
+               goto health_error;
+
+       if (temp <= chip->pdata->temp_min) {
+               *health = POWER_SUPPLY_HEALTH_COLD;
+               goto out;
+       }
+
+       if (temp >= chip->pdata->temp_max) {
+               *health = POWER_SUPPLY_HEALTH_OVERHEAT;
+               goto out;
+       }
+
+       *health = POWER_SUPPLY_HEALTH_GOOD;
+
+out:
+       return 0;
+
+health_error:
+       return ret;
+}
+
 static int max17042_get_property(struct power_supply *psy,
                            enum power_supply_property psp,
                            union power_supply_propval *val)
@@ -181,19 +267,34 @@ static int max17042_get_property(struct power_supply *psy,
                val->intval = data * 1000 / 2;
                break;
        case POWER_SUPPLY_PROP_TEMP:
-               ret = regmap_read(map, MAX17042_TEMP, &data);
+               ret = max17042_get_temperature(chip, &val->intval);
+               if (ret < 0)
+                       return ret;
+               break;
+       case POWER_SUPPLY_PROP_TEMP_ALERT_MIN:
+               ret = regmap_read(map, MAX17042_TALRT_Th, &data);
+               if (ret < 0)
+                       return ret;
+               /* LSB is Alert Minimum. In deci-centigrade */
+               val->intval = (data & 0xff) * 10;
+               break;
+       case POWER_SUPPLY_PROP_TEMP_ALERT_MAX:
+               ret = regmap_read(map, MAX17042_TALRT_Th, &data);
+               if (ret < 0)
+                       return ret;
+               /* MSB is Alert Maximum. In deci-centigrade */
+               val->intval = (data >> 8) * 10;
+               break;
+       case POWER_SUPPLY_PROP_TEMP_MIN:
+               val->intval = chip->pdata->temp_min;
+               break;
+       case POWER_SUPPLY_PROP_TEMP_MAX:
+               val->intval = chip->pdata->temp_max;
+               break;
+       case POWER_SUPPLY_PROP_HEALTH:
+               ret = max17042_get_battery_health(chip, &val->intval);
                if (ret < 0)
                        return ret;
-
-               val->intval = data;
-               /* The value is signed. */
-               if (val->intval & 0x8000) {
-                       val->intval = (0x7fff & ~val->intval) + 1;
-                       val->intval *= -1;
-               }
-               /* The value is converted into deci-centigrade scale */
-               /* Units of LSB = 1 / 256 degree Celsius */
-               val->intval = val->intval * 10 / 256;
                break;
        case POWER_SUPPLY_PROP_CURRENT_NOW:
                if (chip->pdata->enable_current_sense) {
@@ -237,6 +338,69 @@ static int max17042_get_property(struct power_supply *psy,
        return 0;
 }
 
+static int max17042_set_property(struct power_supply *psy,
+                           enum power_supply_property psp,
+                           const union power_supply_propval *val)
+{
+       struct max17042_chip *chip = power_supply_get_drvdata(psy);
+       struct regmap *map = chip->regmap;
+       int ret = 0;
+       u32 data;
+       int8_t temp;
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_TEMP_ALERT_MIN:
+               ret = regmap_read(map, MAX17042_TALRT_Th, &data);
+               if (ret < 0)
+                       return ret;
+
+               /* Input in deci-centigrade, convert to centigrade */
+               temp = val->intval / 10;
+               /* force min < max */
+               if (temp >= (int8_t)(data >> 8))
+                       temp = (int8_t)(data >> 8) - 1;
+               /* Write both MAX and MIN ALERT */
+               data = (data & 0xff00) + temp;
+               ret = regmap_write(map, MAX17042_TALRT_Th, data);
+               break;
+       case POWER_SUPPLY_PROP_TEMP_ALERT_MAX:
+               ret = regmap_read(map, MAX17042_TALRT_Th, &data);
+               if (ret < 0)
+                       return ret;
+
+               /* Input in Deci-Centigrade, convert to centigrade */
+               temp = val->intval / 10;
+               /* force max > min */
+               if (temp <= (int8_t)(data & 0xff))
+                       temp = (int8_t)(data & 0xff) + 1;
+               /* Write both MAX and MIN ALERT */
+               data = (data & 0xff) + (temp << 8);
+               ret = regmap_write(map, MAX17042_TALRT_Th, data);
+               break;
+       default:
+               ret = -EINVAL;
+       }
+
+       return ret;
+}
+
+static int max17042_property_is_writeable(struct power_supply *psy,
+               enum power_supply_property psp)
+{
+       int ret;
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_TEMP_ALERT_MIN:
+       case POWER_SUPPLY_PROP_TEMP_ALERT_MAX:
+               ret = 1;
+               break;
+       default:
+               ret = 0;
+       }
+
+       return ret;
+}
+
 static int max17042_write_verify_reg(struct regmap *map, u8 reg, u32 value)
 {
        int retries = 8;
@@ -645,6 +809,15 @@ max17042_get_pdata(struct device *dev)
                pdata->enable_current_sense = true;
        }
 
+       if (of_property_read_s32(np, "maxim,cold-temp", &pdata->temp_min))
+               pdata->temp_min = INT_MIN;
+       if (of_property_read_s32(np, "maxim,over-heat-temp", &pdata->temp_max))
+               pdata->temp_max = INT_MAX;
+       if (of_property_read_s32(np, "maxim,dead-volt", &pdata->vmin))
+               pdata->vmin = INT_MIN;
+       if (of_property_read_s32(np, "maxim,over-volt", &pdata->vmax))
+               pdata->vmax = INT_MAX;
+
        return pdata;
 }
 #else
@@ -665,6 +838,8 @@ static const struct power_supply_desc max17042_psy_desc = {
        .name           = "max170xx_battery",
        .type           = POWER_SUPPLY_TYPE_BATTERY,
        .get_property   = max17042_get_property,
+       .set_property   = max17042_set_property,
+       .property_is_writeable  = max17042_property_is_writeable,
        .properties     = max17042_battery_props,
        .num_properties = ARRAY_SIZE(max17042_battery_props),
 };
@@ -673,6 +848,8 @@ static const struct power_supply_desc max17042_no_current_sense_psy_desc = {
        .name           = "max170xx_battery",
        .type           = POWER_SUPPLY_TYPE_BATTERY,
        .get_property   = max17042_get_property,
+       .set_property   = max17042_set_property,
+       .property_is_writeable  = max17042_property_is_writeable,
        .properties     = max17042_battery_props,
        .num_properties = ARRAY_SIZE(max17042_battery_props) - 2,
 };
index 2ed4a4a6b3c5cb32037904d33ecf1b16adfa41c9..869284c2e1e85e7d1035b281ffb64aae06f976e6 100644 (file)
@@ -30,6 +30,8 @@ EXPORT_SYMBOL_GPL(power_supply_notifier);
 
 static struct device_type power_supply_dev_type;
 
+#define POWER_SUPPLY_DEFERRED_REGISTER_TIME    msecs_to_jiffies(10)
+
 static bool __power_supply_is_supplied_by(struct power_supply *supplier,
                                         struct power_supply *supply)
 {
@@ -121,6 +123,30 @@ void power_supply_changed(struct power_supply *psy)
 }
 EXPORT_SYMBOL_GPL(power_supply_changed);
 
+/*
+ * Notify that power supply was registered after parent finished the probing.
+ *
+ * Often power supply is registered from driver's probe function. However
+ * calling power_supply_changed() directly from power_supply_register()
+ * would lead to execution of get_property() function provided by the driver
+ * too early - before the probe ends.
+ *
+ * Avoid that by waiting on parent's mutex.
+ */
+static void power_supply_deferred_register_work(struct work_struct *work)
+{
+       struct power_supply *psy = container_of(work, struct power_supply,
+                                               deferred_register_work.work);
+
+       if (psy->dev.parent)
+               mutex_lock(&psy->dev.parent->mutex);
+
+       power_supply_changed(psy);
+
+       if (psy->dev.parent)
+               mutex_unlock(&psy->dev.parent->mutex);
+}
+
 #ifdef CONFIG_OF
 #include <linux/of.h>
 
@@ -420,6 +446,45 @@ struct power_supply *power_supply_get_by_phandle(struct device_node *np,
        return psy;
 }
 EXPORT_SYMBOL_GPL(power_supply_get_by_phandle);
+
+static void devm_power_supply_put(struct device *dev, void *res)
+{
+       struct power_supply **psy = res;
+
+       power_supply_put(*psy);
+}
+
+/**
+ * devm_power_supply_get_by_phandle() - Resource managed version of
+ *  power_supply_get_by_phandle()
+ * @dev: Pointer to device holding phandle property
+ * @phandle_name: Name of property holding a power supply phandle
+ *
+ * Return: On success returns a reference to a power supply with
+ * matching name equals to value under @property, NULL or ERR_PTR otherwise.
+ */
+struct power_supply *devm_power_supply_get_by_phandle(struct device *dev,
+                                                     const char *property)
+{
+       struct power_supply **ptr, *psy;
+
+       if (!dev->of_node)
+               return ERR_PTR(-ENODEV);
+
+       ptr = devres_alloc(devm_power_supply_put, sizeof(*ptr), GFP_KERNEL);
+       if (!ptr)
+               return ERR_PTR(-ENOMEM);
+
+       psy = power_supply_get_by_phandle(dev->of_node, property);
+       if (IS_ERR_OR_NULL(psy)) {
+               devres_free(ptr);
+       } else {
+               *ptr = psy;
+               devres_add(dev, ptr);
+       }
+       return psy;
+}
+EXPORT_SYMBOL_GPL(devm_power_supply_get_by_phandle);
 #endif /* CONFIG_OF */
 
 int power_supply_get_property(struct power_supply *psy,
@@ -645,6 +710,10 @@ __power_supply_register(struct device *parent,
        struct power_supply *psy;
        int rc;
 
+       if (!parent)
+               pr_warn("%s: Expected proper parent device for '%s'\n",
+                       __func__, desc->name);
+
        psy = kzalloc(sizeof(*psy), GFP_KERNEL);
        if (!psy)
                return ERR_PTR(-ENOMEM);
@@ -659,7 +728,6 @@ __power_supply_register(struct device *parent,
        dev->release = power_supply_dev_release;
        dev_set_drvdata(dev, psy);
        psy->desc = desc;
-       atomic_inc(&psy->use_cnt);
        if (cfg) {
                psy->drv_data = cfg->drv_data;
                psy->of_node = cfg->of_node;
@@ -672,6 +740,8 @@ __power_supply_register(struct device *parent,
                goto dev_set_name_failed;
 
        INIT_WORK(&psy->changed_work, power_supply_changed_work);
+       INIT_DELAYED_WORK(&psy->deferred_register_work,
+                         power_supply_deferred_register_work);
 
        rc = power_supply_check_supplies(psy);
        if (rc) {
@@ -700,7 +770,20 @@ __power_supply_register(struct device *parent,
        if (rc)
                goto create_triggers_failed;
 
-       power_supply_changed(psy);
+       /*
+        * Update use_cnt after any uevents (most notably from device_add()).
+        * We are here still during driver's probe but
+        * the power_supply_uevent() calls back driver's get_property
+        * method so:
+        * 1. Driver did not assigned the returned struct power_supply,
+        * 2. Driver could not finish initialization (anything in its probe
+        *    after calling power_supply_register()).
+        */
+       atomic_inc(&psy->use_cnt);
+
+       queue_delayed_work(system_power_efficient_wq,
+                          &psy->deferred_register_work,
+                          POWER_SUPPLY_DEFERRED_REGISTER_TIME);
 
        return psy;
 
@@ -720,7 +803,8 @@ dev_set_name_failed:
 
 /**
  * power_supply_register() - Register new power supply
- * @parent:    Device to be a parent of power supply's device
+ * @parent:    Device to be a parent of power supply's device, usually
+ *             the device which probe function calls this
  * @desc:      Description of power supply, must be valid through whole
  *             lifetime of this power supply
  * @cfg:       Run-time specific configuration accessed during registering,
@@ -740,8 +824,9 @@ struct power_supply *__must_check power_supply_register(struct device *parent,
 EXPORT_SYMBOL_GPL(power_supply_register);
 
 /**
- * power_supply_register() - Register new non-waking-source power supply
- * @parent:    Device to be a parent of power supply's device
+ * power_supply_register_no_ws() - Register new non-waking-source power supply
+ * @parent:    Device to be a parent of power supply's device, usually
+ *             the device which probe function calls this
  * @desc:      Description of power supply, must be valid through whole
  *             lifetime of this power supply
  * @cfg:       Run-time specific configuration accessed during registering,
@@ -769,8 +854,9 @@ static void devm_power_supply_release(struct device *dev, void *res)
 }
 
 /**
- * power_supply_register() - Register managed power supply
- * @parent:    Device to be a parent of power supply's device
+ * devm_power_supply_register() - Register managed power supply
+ * @parent:    Device to be a parent of power supply's device, usually
+ *             the device which probe function calls this
  * @desc:      Description of power supply, must be valid through whole
  *             lifetime of this power supply
  * @cfg:       Run-time specific configuration accessed during registering,
@@ -804,8 +890,9 @@ devm_power_supply_register(struct device *parent,
 EXPORT_SYMBOL_GPL(devm_power_supply_register);
 
 /**
- * power_supply_register() - Register managed non-waking-source power supply
- * @parent:    Device to be a parent of power supply's device
+ * devm_power_supply_register_no_ws() - Register managed non-waking-source power supply
+ * @parent:    Device to be a parent of power supply's device, usually
+ *             the device which probe function calls this
  * @desc:      Description of power supply, must be valid through whole
  *             lifetime of this power supply
  * @cfg:       Run-time specific configuration accessed during registering,
@@ -849,6 +936,7 @@ void power_supply_unregister(struct power_supply *psy)
 {
        WARN_ON(atomic_dec_return(&psy->use_cnt));
        cancel_work_sync(&psy->changed_work);
+       cancel_delayed_work_sync(&psy->deferred_register_work);
        sysfs_remove_link(&psy->dev.kobj, "powers");
        power_supply_remove_triggers(psy);
        psy_unregister_cooler(psy);
index 2d41a43fc81acb967639b012b4e7811bab29572e..2277ad9c2f6825da0a96e77e087c4999d0f4f74e 100644 (file)
@@ -25,7 +25,7 @@ static void power_supply_update_bat_leds(struct power_supply *psy)
        unsigned long delay_on = 0;
        unsigned long delay_off = 0;
 
-       if (psy->desc->get_property(psy, POWER_SUPPLY_PROP_STATUS, &status))
+       if (power_supply_get_property(psy, POWER_SUPPLY_PROP_STATUS, &status))
                return;
 
        dev_dbg(&psy->dev, "%s %d\n", __func__, status.intval);
@@ -115,7 +115,7 @@ static void power_supply_update_gen_leds(struct power_supply *psy)
 {
        union power_supply_propval online;
 
-       if (psy->desc->get_property(psy, POWER_SUPPLY_PROP_ONLINE, &online))
+       if (power_supply_get_property(psy, POWER_SUPPLY_PROP_ONLINE, &online))
                return;
 
        dev_dbg(&psy->dev, "%s %d\n", __func__, online.intval);
index 9134e3d2d95ef9bfbfca479fd276d93ad2e8e7fb..ed2d7fd0c734dc47fc192fe7cccf825c63e1a35b 100644 (file)
@@ -125,7 +125,7 @@ static ssize_t power_supply_store_property(struct device *dev,
 
        value.intval = long_val;
 
-       ret = psy->desc->set_property(psy, off, &value);
+       ret = power_supply_set_property(psy, off, &value);
        if (ret < 0)
                return ret;
 
@@ -223,7 +223,7 @@ static umode_t power_supply_attr_is_visible(struct kobject *kobj,
 
                if (property == attrno) {
                        if (psy->desc->property_is_writeable &&
-                           power_supply_property_is_writeable(psy, property) > 0)
+                           psy->desc->property_is_writeable(psy, property) > 0)
                                mode |= S_IWUSR;
 
                        return mode;
index ca461ebc7ae8f73338059d6ce9547b850c269f1b..36dc52fb2ec8ba9aace408546359dc0879db40f2 100644 (file)
@@ -243,7 +243,7 @@ static int at91_reset_probe(struct platform_device *pdev)
        return 0;
 }
 
-static struct platform_device_id at91_reset_plat_match[] = {
+static const struct platform_device_id at91_reset_plat_match[] = {
        { "at91-sam9260-reset", (unsigned long)at91sam9260_restart },
        { "at91-sam9g45-reset", (unsigned long)at91sam9g45_restart },
        { /* sentinel */ }
index e5332f1db8a792128d04bf067561fdd6efa07c12..be3d81ff51cc3f510d85e4eed7a52960e51e7bc1 100644 (file)
@@ -48,6 +48,7 @@ static void gpio_poweroff_do_poweroff(void)
 static int gpio_poweroff_probe(struct platform_device *pdev)
 {
        bool input = false;
+       enum gpiod_flags flags;
 
        /* If a pm_power_off function has already been added, leave it alone */
        if (pm_power_off != NULL) {
@@ -57,25 +58,15 @@ static int gpio_poweroff_probe(struct platform_device *pdev)
                return -EBUSY;
        }
 
-       reset_gpio = devm_gpiod_get(&pdev->dev, NULL);
-       if (IS_ERR(reset_gpio))
-               return PTR_ERR(reset_gpio);
-
        input = of_property_read_bool(pdev->dev.of_node, "input");
+       if (input)
+               flags = GPIOD_IN;
+       else
+               flags = GPIOD_OUT_LOW;
 
-       if (input) {
-               if (gpiod_direction_input(reset_gpio)) {
-                       dev_err(&pdev->dev,
-                               "Could not set direction of reset GPIO to input\n");
-                       return -ENODEV;
-               }
-       } else {
-               if (gpiod_direction_output(reset_gpio, 0)) {
-                       dev_err(&pdev->dev,
-                               "Could not set direction of reset GPIO\n");
-                       return -ENODEV;
-               }
-       }
+       reset_gpio = devm_gpiod_get(&pdev->dev, NULL, flags);
+       if (IS_ERR(reset_gpio))
+               return PTR_ERR(reset_gpio);
 
        pm_power_off = &gpio_poweroff_do_poweroff;
        return 0;
index edb327efee8bda10b1dbadfb78a23da80dba2f17..829b45f420212c550d73d4918d01f6166484b7d9 100644 (file)
@@ -78,7 +78,7 @@ static int gpio_restart_probe(struct platform_device *pdev)
        }
 
        gpio_restart->restart_handler.notifier_call = gpio_restart_notify;
-       gpio_restart->restart_handler.priority = 128;
+       gpio_restart->restart_handler.priority = 129;
        gpio_restart->active_delay_ms = 100;
        gpio_restart->inactive_delay_ms = 100;
        gpio_restart->wait_delay_ms = 3000;
index 1e08195551fe7d505511a6dd92b07d1ab4300911..15fed9d8f871a715dfada3dd22b8ca0e461dd66a 100644 (file)
@@ -158,7 +158,6 @@ static irqreturn_t ltc2952_poweroff_handler(int irq, void *dev_id)
                              HRTIMER_MODE_REL);
        } else {
                hrtimer_cancel(&data->timer_trigger);
-               /* omitting return value check, timer should have been valid */
        }
        return IRQ_HANDLED;
 }
@@ -202,16 +201,15 @@ static int ltc2952_poweroff_init(struct platform_device *pdev)
                return ret;
        }
 
-       data->gpio_trigger = devm_gpiod_get(&pdev->dev, "trigger", GPIOD_IN);
+       data->gpio_trigger = devm_gpiod_get_optional(&pdev->dev, "trigger",
+                                                    GPIOD_IN);
        if (IS_ERR(data->gpio_trigger)) {
                /*
                 * It's not a problem if the trigger gpio isn't available, but
                 * it is worth a warning if its use was defined in the device
                 * tree.
                 */
-               if (PTR_ERR(data->gpio_trigger) != -ENOENT)
-                       dev_err(&pdev->dev,
-                               "unable to claim gpio \"trigger\"\n");
+               dev_err(&pdev->dev, "unable to claim gpio \"trigger\"\n");
                data->gpio_trigger = NULL;
        }
 
diff --git a/drivers/power/rt9455_charger.c b/drivers/power/rt9455_charger.c
new file mode 100644 (file)
index 0000000..08baac6
--- /dev/null
@@ -0,0 +1,1752 @@
+/*
+ * Driver for Richtek RT9455WSC battery charger.
+ *
+ * Copyright (C) 2015 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/of_irq.h>
+#include <linux/of_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/power_supply.h>
+#include <linux/i2c.h>
+#include <linux/acpi.h>
+#include <linux/usb/phy.h>
+#include <linux/regmap.h>
+
+#define RT9455_MANUFACTURER                    "Richtek"
+#define RT9455_MODEL_NAME                      "RT9455"
+#define RT9455_DRIVER_NAME                     "rt9455-charger"
+
+#define RT9455_IRQ_NAME                                "interrupt"
+
+#define RT9455_PWR_RDY_DELAY                   1 /* 1 second */
+#define RT9455_MAX_CHARGING_TIME               21600 /* 6 hrs */
+#define RT9455_BATT_PRESENCE_DELAY             60 /* 60 seconds */
+
+#define RT9455_CHARGE_MODE                     0x00
+#define RT9455_BOOST_MODE                      0x01
+
+#define RT9455_FAULT                           0x03
+
+#define RT9455_IAICR_100MA                     0x00
+#define RT9455_IAICR_500MA                     0x01
+#define RT9455_IAICR_NO_LIMIT                  0x03
+
+#define RT9455_CHARGE_DISABLE                  0x00
+#define RT9455_CHARGE_ENABLE                   0x01
+
+#define RT9455_PWR_FAULT                       0x00
+#define RT9455_PWR_GOOD                                0x01
+
+#define RT9455_REG_CTRL1                       0x00 /* CTRL1 reg address */
+#define RT9455_REG_CTRL2                       0x01 /* CTRL2 reg address */
+#define RT9455_REG_CTRL3                       0x02 /* CTRL3 reg address */
+#define RT9455_REG_DEV_ID                      0x03 /* DEV_ID reg address */
+#define RT9455_REG_CTRL4                       0x04 /* CTRL4 reg address */
+#define RT9455_REG_CTRL5                       0x05 /* CTRL5 reg address */
+#define RT9455_REG_CTRL6                       0x06 /* CTRL6 reg address */
+#define RT9455_REG_CTRL7                       0x07 /* CTRL7 reg address */
+#define RT9455_REG_IRQ1                                0x08 /* IRQ1 reg address */
+#define RT9455_REG_IRQ2                                0x09 /* IRQ2 reg address */
+#define RT9455_REG_IRQ3                                0x0A /* IRQ3 reg address */
+#define RT9455_REG_MASK1                       0x0B /* MASK1 reg address */
+#define RT9455_REG_MASK2                       0x0C /* MASK2 reg address */
+#define RT9455_REG_MASK3                       0x0D /* MASK3 reg address */
+
+enum rt9455_fields {
+       F_STAT, F_BOOST, F_PWR_RDY, F_OTG_PIN_POLARITY, /* CTRL1 reg fields */
+
+       F_IAICR, F_TE_SHDN_EN, F_HIGHER_OCP, F_TE, F_IAICR_INT, F_HIZ,
+       F_OPA_MODE, /* CTRL2 reg fields */
+
+       F_VOREG, F_OTG_PL, F_OTG_EN, /* CTRL3 reg fields */
+
+       F_VENDOR_ID, F_CHIP_REV, /* DEV_ID reg fields */
+
+       F_RST, /* CTRL4 reg fields */
+
+       F_TMR_EN, F_MIVR, F_IPREC, F_IEOC_PERCENTAGE, /* CTRL5 reg fields*/
+
+       F_IAICR_SEL, F_ICHRG, F_VPREC, /* CTRL6 reg fields */
+
+       F_BATD_EN, F_CHG_EN, F_VMREG, /* CTRL7 reg fields */
+
+       F_TSDI, F_VINOVPI, F_BATAB, /* IRQ1 reg fields */
+
+       F_CHRVPI, F_CHBATOVI, F_CHTERMI, F_CHRCHGI, F_CH32MI, F_CHTREGI,
+       F_CHMIVRI, /* IRQ2 reg fields */
+
+       F_BSTBUSOVI, F_BSTOLI, F_BSTLOWVI, F_BST32SI, /* IRQ3 reg fields */
+
+       F_TSDM, F_VINOVPIM, F_BATABM, /* MASK1 reg fields */
+
+       F_CHRVPIM, F_CHBATOVIM, F_CHTERMIM, F_CHRCHGIM, F_CH32MIM, F_CHTREGIM,
+       F_CHMIVRIM, /* MASK2 reg fields */
+
+       F_BSTVINOVIM, F_BSTOLIM, F_BSTLOWVIM, F_BST32SIM, /* MASK3 reg fields */
+
+       F_MAX_FIELDS
+};
+
+static const struct reg_field rt9455_reg_fields[] = {
+       [F_STAT]                = REG_FIELD(RT9455_REG_CTRL1, 4, 5),
+       [F_BOOST]               = REG_FIELD(RT9455_REG_CTRL1, 3, 3),
+       [F_PWR_RDY]             = REG_FIELD(RT9455_REG_CTRL1, 2, 2),
+       [F_OTG_PIN_POLARITY]    = REG_FIELD(RT9455_REG_CTRL1, 1, 1),
+
+       [F_IAICR]               = REG_FIELD(RT9455_REG_CTRL2, 6, 7),
+       [F_TE_SHDN_EN]          = REG_FIELD(RT9455_REG_CTRL2, 5, 5),
+       [F_HIGHER_OCP]          = REG_FIELD(RT9455_REG_CTRL2, 4, 4),
+       [F_TE]                  = REG_FIELD(RT9455_REG_CTRL2, 3, 3),
+       [F_IAICR_INT]           = REG_FIELD(RT9455_REG_CTRL2, 2, 2),
+       [F_HIZ]                 = REG_FIELD(RT9455_REG_CTRL2, 1, 1),
+       [F_OPA_MODE]            = REG_FIELD(RT9455_REG_CTRL2, 0, 0),
+
+       [F_VOREG]               = REG_FIELD(RT9455_REG_CTRL3, 2, 7),
+       [F_OTG_PL]              = REG_FIELD(RT9455_REG_CTRL3, 1, 1),
+       [F_OTG_EN]              = REG_FIELD(RT9455_REG_CTRL3, 0, 0),
+
+       [F_VENDOR_ID]           = REG_FIELD(RT9455_REG_DEV_ID, 4, 7),
+       [F_CHIP_REV]            = REG_FIELD(RT9455_REG_DEV_ID, 0, 3),
+
+       [F_RST]                 = REG_FIELD(RT9455_REG_CTRL4, 7, 7),
+
+       [F_TMR_EN]              = REG_FIELD(RT9455_REG_CTRL5, 7, 7),
+       [F_MIVR]                = REG_FIELD(RT9455_REG_CTRL5, 4, 5),
+       [F_IPREC]               = REG_FIELD(RT9455_REG_CTRL5, 2, 3),
+       [F_IEOC_PERCENTAGE]     = REG_FIELD(RT9455_REG_CTRL5, 0, 1),
+
+       [F_IAICR_SEL]           = REG_FIELD(RT9455_REG_CTRL6, 7, 7),
+       [F_ICHRG]               = REG_FIELD(RT9455_REG_CTRL6, 4, 6),
+       [F_VPREC]               = REG_FIELD(RT9455_REG_CTRL6, 0, 2),
+
+       [F_BATD_EN]             = REG_FIELD(RT9455_REG_CTRL7, 6, 6),
+       [F_CHG_EN]              = REG_FIELD(RT9455_REG_CTRL7, 4, 4),
+       [F_VMREG]               = REG_FIELD(RT9455_REG_CTRL7, 0, 3),
+
+       [F_TSDI]                = REG_FIELD(RT9455_REG_IRQ1, 7, 7),
+       [F_VINOVPI]             = REG_FIELD(RT9455_REG_IRQ1, 6, 6),
+       [F_BATAB]               = REG_FIELD(RT9455_REG_IRQ1, 0, 0),
+
+       [F_CHRVPI]              = REG_FIELD(RT9455_REG_IRQ2, 7, 7),
+       [F_CHBATOVI]            = REG_FIELD(RT9455_REG_IRQ2, 5, 5),
+       [F_CHTERMI]             = REG_FIELD(RT9455_REG_IRQ2, 4, 4),
+       [F_CHRCHGI]             = REG_FIELD(RT9455_REG_IRQ2, 3, 3),
+       [F_CH32MI]              = REG_FIELD(RT9455_REG_IRQ2, 2, 2),
+       [F_CHTREGI]             = REG_FIELD(RT9455_REG_IRQ2, 1, 1),
+       [F_CHMIVRI]             = REG_FIELD(RT9455_REG_IRQ2, 0, 0),
+
+       [F_BSTBUSOVI]           = REG_FIELD(RT9455_REG_IRQ3, 7, 7),
+       [F_BSTOLI]              = REG_FIELD(RT9455_REG_IRQ3, 6, 6),
+       [F_BSTLOWVI]            = REG_FIELD(RT9455_REG_IRQ3, 5, 5),
+       [F_BST32SI]             = REG_FIELD(RT9455_REG_IRQ3, 3, 3),
+
+       [F_TSDM]                = REG_FIELD(RT9455_REG_MASK1, 7, 7),
+       [F_VINOVPIM]            = REG_FIELD(RT9455_REG_MASK1, 6, 6),
+       [F_BATABM]              = REG_FIELD(RT9455_REG_MASK1, 0, 0),
+
+       [F_CHRVPIM]             = REG_FIELD(RT9455_REG_MASK2, 7, 7),
+       [F_CHBATOVIM]           = REG_FIELD(RT9455_REG_MASK2, 5, 5),
+       [F_CHTERMIM]            = REG_FIELD(RT9455_REG_MASK2, 4, 4),
+       [F_CHRCHGIM]            = REG_FIELD(RT9455_REG_MASK2, 3, 3),
+       [F_CH32MIM]             = REG_FIELD(RT9455_REG_MASK2, 2, 2),
+       [F_CHTREGIM]            = REG_FIELD(RT9455_REG_MASK2, 1, 1),
+       [F_CHMIVRIM]            = REG_FIELD(RT9455_REG_MASK2, 0, 0),
+
+       [F_BSTVINOVIM]          = REG_FIELD(RT9455_REG_MASK3, 7, 7),
+       [F_BSTOLIM]             = REG_FIELD(RT9455_REG_MASK3, 6, 6),
+       [F_BSTLOWVIM]           = REG_FIELD(RT9455_REG_MASK3, 5, 5),
+       [F_BST32SIM]            = REG_FIELD(RT9455_REG_MASK3, 3, 3),
+};
+
+#define GET_MASK(fid)  (BIT(rt9455_reg_fields[fid].msb + 1) - \
+                        BIT(rt9455_reg_fields[fid].lsb))
+
+/*
+ * Each array initialised below shows the possible real-world values for a
+ * group of bits belonging to RT9455 registers. The arrays are sorted in
+ * ascending order. The index of each real-world value represents the value
+ * that is encoded in the group of bits belonging to RT9455 registers.
+ */
+/* REG06[6:4] (ICHRG) in uAh */
+static const int rt9455_ichrg_values[] = {
+        500000,  650000,  800000,  950000, 1100000, 1250000, 1400000, 1550000
+};
+
+/*
+ * When the charger is in charge mode, REG02[7:2] represent battery regulation
+ * voltage.
+ */
+/* REG02[7:2] (VOREG) in uV */
+static const int rt9455_voreg_values[] = {
+       3500000, 3520000, 3540000, 3560000, 3580000, 3600000, 3620000, 3640000,
+       3660000, 3680000, 3700000, 3720000, 3740000, 3760000, 3780000, 3800000,
+       3820000, 3840000, 3860000, 3880000, 3900000, 3920000, 3940000, 3960000,
+       3980000, 4000000, 4020000, 4040000, 4060000, 4080000, 4100000, 4120000,
+       4140000, 4160000, 4180000, 4200000, 4220000, 4240000, 4260000, 4280000,
+       4300000, 4330000, 4350000, 4370000, 4390000, 4410000, 4430000, 4450000,
+       4450000, 4450000, 4450000, 4450000, 4450000, 4450000, 4450000, 4450000,
+       4450000, 4450000, 4450000, 4450000, 4450000, 4450000, 4450000, 4450000
+};
+
+/*
+ * When the charger is in boost mode, REG02[7:2] represent boost output
+ * voltage.
+ */
+/* REG02[7:2] (Boost output voltage) in uV */
+static const int rt9455_boost_voltage_values[] = {
+       4425000, 4450000, 4475000, 4500000, 4525000, 4550000, 4575000, 4600000,
+       4625000, 4650000, 4675000, 4700000, 4725000, 4750000, 4775000, 4800000,
+       4825000, 4850000, 4875000, 4900000, 4925000, 4950000, 4975000, 5000000,
+       5025000, 5050000, 5075000, 5100000, 5125000, 5150000, 5175000, 5200000,
+       5225000, 5250000, 5275000, 5300000, 5325000, 5350000, 5375000, 5400000,
+       5425000, 5450000, 5475000, 5500000, 5525000, 5550000, 5575000, 5600000,
+       5600000, 5600000, 5600000, 5600000, 5600000, 5600000, 5600000, 5600000,
+       5600000, 5600000, 5600000, 5600000, 5600000, 5600000, 5600000, 5600000,
+};
+
+/* REG07[3:0] (VMREG) in uV */
+static const int rt9455_vmreg_values[] = {
+       4200000, 4220000, 4240000, 4260000, 4280000, 4300000, 4320000, 4340000,
+       4360000, 4380000, 4400000, 4430000, 4450000, 4450000, 4450000, 4450000
+};
+
+/* REG05[5:4] (IEOC_PERCENTAGE) */
+static const int rt9455_ieoc_percentage_values[] = {
+       10, 30, 20, 30
+};
+
+/* REG05[1:0] (MIVR) in uV */
+static const int rt9455_mivr_values[] = {
+       4000000, 4250000, 4500000, 5000000
+};
+
+/* REG05[1:0] (IAICR) in uA */
+static const int rt9455_iaicr_values[] = {
+       100000, 500000, 1000000, 2000000
+};
+
+struct rt9455_info {
+       struct i2c_client               *client;
+       struct regmap                   *regmap;
+       struct regmap_field             *regmap_fields[F_MAX_FIELDS];
+       struct power_supply             *charger;
+#if IS_ENABLED(CONFIG_USB_PHY)
+       struct usb_phy                  *usb_phy;
+       struct notifier_block           nb;
+#endif
+       struct delayed_work             pwr_rdy_work;
+       struct delayed_work             max_charging_time_work;
+       struct delayed_work             batt_presence_work;
+       u32                             voreg;
+       u32                             boost_voltage;
+};
+
+/*
+ * Iterate through each element of the 'tbl' array until an element whose value
+ * is greater than v is found. Return the index of the respective element,
+ * or the index of the last element in the array, if no such element is found.
+ */
+static unsigned int rt9455_find_idx(const int tbl[], int tbl_size, int v)
+{
+       int i;
+
+       /*
+        * No need to iterate until the last index in the table because
+        * if no element greater than v is found in the table,
+        * or if only the last element is greater than v,
+        * function returns the index of the last element.
+        */
+       for (i = 0; i < tbl_size - 1; i++)
+               if (v <= tbl[i])
+                       return i;
+
+       return (tbl_size - 1);
+}
+
+static int rt9455_get_field_val(struct rt9455_info *info,
+                               enum rt9455_fields field,
+                               const int tbl[], int tbl_size, int *val)
+{
+       unsigned int v;
+       int ret;
+
+       ret = regmap_field_read(info->regmap_fields[field], &v);
+       if (ret)
+               return ret;
+
+       v = (v >= tbl_size) ? (tbl_size - 1) : v;
+       *val = tbl[v];
+
+       return 0;
+}
+
+static int rt9455_set_field_val(struct rt9455_info *info,
+                               enum rt9455_fields field,
+                               const int tbl[], int tbl_size, int val)
+{
+       unsigned int idx = rt9455_find_idx(tbl, tbl_size, val);
+
+       return regmap_field_write(info->regmap_fields[field], idx);
+}
+
+static int rt9455_register_reset(struct rt9455_info *info)
+{
+       struct device *dev = &info->client->dev;
+       unsigned int v;
+       int ret, limit = 100;
+
+       ret = regmap_field_write(info->regmap_fields[F_RST], 0x01);
+       if (ret) {
+               dev_err(dev, "Failed to set RST bit\n");
+               return ret;
+       }
+
+       /*
+        * To make sure that reset operation has finished, loop until RST bit
+        * is set to 0.
+        */
+       do {
+               ret = regmap_field_read(info->regmap_fields[F_RST], &v);
+               if (ret) {
+                       dev_err(dev, "Failed to read RST bit\n");
+                       return ret;
+               }
+
+               if (!v)
+                       break;
+
+               usleep_range(10, 100);
+       } while (--limit);
+
+       if (!limit)
+               return -EIO;
+
+       return 0;
+}
+
+/* Charger power supply property routines */
+static enum power_supply_property rt9455_charger_properties[] = {
+       POWER_SUPPLY_PROP_STATUS,
+       POWER_SUPPLY_PROP_HEALTH,
+       POWER_SUPPLY_PROP_PRESENT,
+       POWER_SUPPLY_PROP_ONLINE,
+       POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,
+       POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
+       POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,
+       POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX,
+       POWER_SUPPLY_PROP_SCOPE,
+       POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT,
+       POWER_SUPPLY_PROP_MODEL_NAME,
+       POWER_SUPPLY_PROP_MANUFACTURER,
+};
+
+static char *rt9455_charger_supplied_to[] = {
+       "main-battery",
+};
+
+static int rt9455_charger_get_status(struct rt9455_info *info,
+                                    union power_supply_propval *val)
+{
+       unsigned int v, pwr_rdy;
+       int ret;
+
+       ret = regmap_field_read(info->regmap_fields[F_PWR_RDY],
+                               &pwr_rdy);
+       if (ret) {
+               dev_err(&info->client->dev, "Failed to read PWR_RDY bit\n");
+               return ret;
+       }
+
+       /*
+        * If PWR_RDY bit is unset, the battery is discharging. Otherwise,
+        * STAT bits value must be checked.
+        */
+       if (!pwr_rdy) {
+               val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
+               return 0;
+       }
+
+       ret = regmap_field_read(info->regmap_fields[F_STAT], &v);
+       if (ret) {
+               dev_err(&info->client->dev, "Failed to read STAT bits\n");
+               return ret;
+       }
+
+       switch (v) {
+       case 0:
+               /*
+                * If PWR_RDY bit is set, but STAT bits value is 0, the charger
+                * may be in one of the following cases:
+                * 1. CHG_EN bit is 0.
+                * 2. CHG_EN bit is 1 but the battery is not connected.
+                * In any of these cases, POWER_SUPPLY_STATUS_NOT_CHARGING is
+                * returned.
+                */
+               val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
+               return 0;
+       case 1:
+               val->intval = POWER_SUPPLY_STATUS_CHARGING;
+               return 0;
+       case 2:
+               val->intval = POWER_SUPPLY_STATUS_FULL;
+               return 0;
+       default:
+               val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
+               return 0;
+       }
+}
+
+static int rt9455_charger_get_health(struct rt9455_info *info,
+                                    union power_supply_propval *val)
+{
+       struct device *dev = &info->client->dev;
+       unsigned int v;
+       int ret;
+
+       val->intval = POWER_SUPPLY_HEALTH_GOOD;
+
+       ret = regmap_read(info->regmap, RT9455_REG_IRQ1, &v);
+       if (ret) {
+               dev_err(dev, "Failed to read IRQ1 register\n");
+               return ret;
+       }
+
+       if (v & GET_MASK(F_TSDI)) {
+               val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
+               return 0;
+       }
+       if (v & GET_MASK(F_VINOVPI)) {
+               val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
+               return 0;
+       }
+       if (v & GET_MASK(F_BATAB)) {
+               val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
+               return 0;
+       }
+
+       ret = regmap_read(info->regmap, RT9455_REG_IRQ2, &v);
+       if (ret) {
+               dev_err(dev, "Failed to read IRQ2 register\n");
+               return ret;
+       }
+
+       if (v & GET_MASK(F_CHBATOVI)) {
+               val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
+               return 0;
+       }
+       if (v & GET_MASK(F_CH32MI)) {
+               val->intval = POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE;
+               return 0;
+       }
+
+       ret = regmap_read(info->regmap, RT9455_REG_IRQ3, &v);
+       if (ret) {
+               dev_err(dev, "Failed to read IRQ3 register\n");
+               return ret;
+       }
+
+       if (v & GET_MASK(F_BSTBUSOVI)) {
+               val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
+               return 0;
+       }
+       if (v & GET_MASK(F_BSTOLI)) {
+               val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
+               return 0;
+       }
+       if (v & GET_MASK(F_BSTLOWVI)) {
+               val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
+               return 0;
+       }
+       if (v & GET_MASK(F_BST32SI)) {
+               val->intval = POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE;
+               return 0;
+       }
+
+       ret = regmap_field_read(info->regmap_fields[F_STAT], &v);
+       if (ret) {
+               dev_err(dev, "Failed to read STAT bits\n");
+               return ret;
+       }
+
+       if (v == RT9455_FAULT) {
+               val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
+               return 0;
+       }
+
+       return 0;
+}
+
+static int rt9455_charger_get_battery_presence(struct rt9455_info *info,
+                                              union power_supply_propval *val)
+{
+       unsigned int v;
+       int ret;
+
+       ret = regmap_field_read(info->regmap_fields[F_BATAB], &v);
+       if (ret) {
+               dev_err(&info->client->dev, "Failed to read BATAB bit\n");
+               return ret;
+       }
+
+       /*
+        * Since BATAB is 1 when battery is NOT present and 0 otherwise,
+        * !BATAB is returned.
+        */
+       val->intval = !v;
+
+       return 0;
+}
+
+static int rt9455_charger_get_online(struct rt9455_info *info,
+                                    union power_supply_propval *val)
+{
+       unsigned int v;
+       int ret;
+
+       ret = regmap_field_read(info->regmap_fields[F_PWR_RDY], &v);
+       if (ret) {
+               dev_err(&info->client->dev, "Failed to read PWR_RDY bit\n");
+               return ret;
+       }
+
+       val->intval = (int)v;
+
+       return 0;
+}
+
+static int rt9455_charger_get_current(struct rt9455_info *info,
+                                     union power_supply_propval *val)
+{
+       int curr;
+       int ret;
+
+       ret = rt9455_get_field_val(info, F_ICHRG,
+                                  rt9455_ichrg_values,
+                                  ARRAY_SIZE(rt9455_ichrg_values),
+                                  &curr);
+       if (ret) {
+               dev_err(&info->client->dev, "Failed to read ICHRG value\n");
+               return ret;
+       }
+
+       val->intval = curr;
+
+       return 0;
+}
+
+static int rt9455_charger_get_current_max(struct rt9455_info *info,
+                                         union power_supply_propval *val)
+{
+       int idx = ARRAY_SIZE(rt9455_ichrg_values) - 1;
+
+       val->intval = rt9455_ichrg_values[idx];
+
+       return 0;
+}
+
+static int rt9455_charger_get_voltage(struct rt9455_info *info,
+                                     union power_supply_propval *val)
+{
+       int voltage;
+       int ret;
+
+       ret = rt9455_get_field_val(info, F_VOREG,
+                                  rt9455_voreg_values,
+                                  ARRAY_SIZE(rt9455_voreg_values),
+                                  &voltage);
+       if (ret) {
+               dev_err(&info->client->dev, "Failed to read VOREG value\n");
+               return ret;
+       }
+
+       val->intval = voltage;
+
+       return 0;
+}
+
+static int rt9455_charger_get_voltage_max(struct rt9455_info *info,
+                                         union power_supply_propval *val)
+{
+       int idx = ARRAY_SIZE(rt9455_vmreg_values) - 1;
+
+       val->intval = rt9455_vmreg_values[idx];
+
+       return 0;
+}
+
+static int rt9455_charger_get_term_current(struct rt9455_info *info,
+                                          union power_supply_propval *val)
+{
+       struct device *dev = &info->client->dev;
+       int ichrg, ieoc_percentage, ret;
+
+       ret = rt9455_get_field_val(info, F_ICHRG,
+                                  rt9455_ichrg_values,
+                                  ARRAY_SIZE(rt9455_ichrg_values),
+                                  &ichrg);
+       if (ret) {
+               dev_err(dev, "Failed to read ICHRG value\n");
+               return ret;
+       }
+
+       ret = rt9455_get_field_val(info, F_IEOC_PERCENTAGE,
+                                  rt9455_ieoc_percentage_values,
+                                  ARRAY_SIZE(rt9455_ieoc_percentage_values),
+                                  &ieoc_percentage);
+       if (ret) {
+               dev_err(dev, "Failed to read IEOC value\n");
+               return ret;
+       }
+
+       val->intval = ichrg * ieoc_percentage / 100;
+
+       return 0;
+}
+
+static int rt9455_charger_get_property(struct power_supply *psy,
+                                      enum power_supply_property psp,
+                                      union power_supply_propval *val)
+{
+       struct rt9455_info *info = power_supply_get_drvdata(psy);
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_STATUS:
+               return rt9455_charger_get_status(info, val);
+       case POWER_SUPPLY_PROP_HEALTH:
+               return rt9455_charger_get_health(info, val);
+       case POWER_SUPPLY_PROP_PRESENT:
+               return rt9455_charger_get_battery_presence(info, val);
+       case POWER_SUPPLY_PROP_ONLINE:
+               return rt9455_charger_get_online(info, val);
+       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
+               return rt9455_charger_get_current(info, val);
+       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
+               return rt9455_charger_get_current_max(info, val);
+       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
+               return rt9455_charger_get_voltage(info, val);
+       case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX:
+               return rt9455_charger_get_voltage_max(info, val);
+       case POWER_SUPPLY_PROP_SCOPE:
+               val->intval = POWER_SUPPLY_SCOPE_SYSTEM;
+               return 0;
+       case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT:
+               return rt9455_charger_get_term_current(info, val);
+       case POWER_SUPPLY_PROP_MODEL_NAME:
+               val->strval = RT9455_MODEL_NAME;
+               return 0;
+       case POWER_SUPPLY_PROP_MANUFACTURER:
+               val->strval = RT9455_MANUFACTURER;
+               return 0;
+       default:
+               return -ENODATA;
+       }
+}
+
+static int rt9455_hw_init(struct rt9455_info *info, u32 ichrg,
+                         u32 ieoc_percentage,
+                         u32 mivr, u32 iaicr)
+{
+       struct device *dev = &info->client->dev;
+       int idx, ret;
+
+       ret = rt9455_register_reset(info);
+       if (ret) {
+               dev_err(dev, "Power On Reset failed\n");
+               return ret;
+       }
+
+       /* Set TE bit in order to enable end of charge detection */
+       ret = regmap_field_write(info->regmap_fields[F_TE], 1);
+       if (ret) {
+               dev_err(dev, "Failed to set TE bit\n");
+               return ret;
+       }
+
+       /* Set TE_SHDN_EN bit in order to enable end of charge detection */
+       ret = regmap_field_write(info->regmap_fields[F_TE_SHDN_EN], 1);
+       if (ret) {
+               dev_err(dev, "Failed to set TE_SHDN_EN bit\n");
+               return ret;
+       }
+
+       /*
+        * Set BATD_EN bit in order to enable battery detection
+        * when charging is done
+        */
+       ret = regmap_field_write(info->regmap_fields[F_BATD_EN], 1);
+       if (ret) {
+               dev_err(dev, "Failed to set BATD_EN bit\n");
+               return ret;
+       }
+
+       /*
+        * Disable Safety Timer. In charge mode, this timer terminates charging
+        * if no read or write via I2C is done within 32 minutes. This timer
+        * avoids overcharging the baterry when the OS is not loaded and the
+        * charger is connected to a power source.
+        * In boost mode, this timer triggers BST32SI interrupt if no read or
+        * write via I2C is done within 32 seconds.
+        * When the OS is loaded and the charger driver is inserted, it is used
+        * delayed_work, named max_charging_time_work, to avoid overcharging
+        * the battery.
+        */
+       ret = regmap_field_write(info->regmap_fields[F_TMR_EN], 0x00);
+       if (ret) {
+               dev_err(dev, "Failed to disable Safety Timer\n");
+               return ret;
+       }
+
+       /* Set ICHRG to value retrieved from device-specific data */
+       ret = rt9455_set_field_val(info, F_ICHRG,
+                                  rt9455_ichrg_values,
+                                  ARRAY_SIZE(rt9455_ichrg_values), ichrg);
+       if (ret) {
+               dev_err(dev, "Failed to set ICHRG value\n");
+               return ret;
+       }
+
+       /* Set IEOC Percentage to value retrieved from device-specific data */
+       ret = rt9455_set_field_val(info, F_IEOC_PERCENTAGE,
+                                  rt9455_ieoc_percentage_values,
+                                  ARRAY_SIZE(rt9455_ieoc_percentage_values),
+                                  ieoc_percentage);
+       if (ret) {
+               dev_err(dev, "Failed to set IEOC Percentage value\n");
+               return ret;
+       }
+
+       /* Set VOREG to value retrieved from device-specific data */
+       ret = rt9455_set_field_val(info, F_VOREG,
+                                  rt9455_voreg_values,
+                                  ARRAY_SIZE(rt9455_voreg_values),
+                                  info->voreg);
+       if (ret) {
+               dev_err(dev, "Failed to set VOREG value\n");
+               return ret;
+       }
+
+       /* Set VMREG value to maximum (4.45V). */
+       idx = ARRAY_SIZE(rt9455_vmreg_values) - 1;
+       ret = rt9455_set_field_val(info, F_VMREG,
+                                  rt9455_vmreg_values,
+                                  ARRAY_SIZE(rt9455_vmreg_values),
+                                  rt9455_vmreg_values[idx]);
+       if (ret) {
+               dev_err(dev, "Failed to set VMREG value\n");
+               return ret;
+       }
+
+       /*
+        * Set MIVR to value retrieved from device-specific data.
+        * If no value is specified, default value for MIVR is 4.5V.
+        */
+       if (mivr == -1)
+               mivr = 4500000;
+
+       ret = rt9455_set_field_val(info, F_MIVR,
+                                  rt9455_mivr_values,
+                                  ARRAY_SIZE(rt9455_mivr_values), mivr);
+       if (ret) {
+               dev_err(dev, "Failed to set MIVR value\n");
+               return ret;
+       }
+
+       /*
+        * Set IAICR to value retrieved from device-specific data.
+        * If no value is specified, default value for IAICR is 500 mA.
+        */
+       if (iaicr == -1)
+               iaicr = 500000;
+
+       ret = rt9455_set_field_val(info, F_IAICR,
+                                  rt9455_iaicr_values,
+                                  ARRAY_SIZE(rt9455_iaicr_values), iaicr);
+       if (ret) {
+               dev_err(dev, "Failed to set IAICR value\n");
+               return ret;
+       }
+
+       /*
+        * Set IAICR_INT bit so that IAICR value is determined by IAICR bits
+        * and not by OTG pin.
+        */
+       ret = regmap_field_write(info->regmap_fields[F_IAICR_INT], 0x01);
+       if (ret) {
+               dev_err(dev, "Failed to set IAICR_INT bit\n");
+               return ret;
+       }
+
+       /*
+        * Disable CHMIVRI interrupt. Because the driver sets MIVR value,
+        * CHMIVRI is triggered, but there is no action to be taken by the
+        * driver when CHMIVRI is triggered.
+        */
+       ret = regmap_field_write(info->regmap_fields[F_CHMIVRIM], 0x01);
+       if (ret) {
+               dev_err(dev, "Failed to mask CHMIVRI interrupt\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+#if IS_ENABLED(CONFIG_USB_PHY)
+/*
+ * Before setting the charger into boost mode, boost output voltage is
+ * set. This is needed because boost output voltage may differ from battery
+ * regulation voltage. F_VOREG bits represent either battery regulation voltage
+ * or boost output voltage, depending on the mode the charger is. Both battery
+ * regulation voltage and boost output voltage are read from DT/ACPI during
+ * probe.
+ */
+static int rt9455_set_boost_voltage_before_boost_mode(struct rt9455_info *info)
+{
+       struct device *dev = &info->client->dev;
+       int ret;
+
+       ret = rt9455_set_field_val(info, F_VOREG,
+                                  rt9455_boost_voltage_values,
+                                  ARRAY_SIZE(rt9455_boost_voltage_values),
+                                  info->boost_voltage);
+       if (ret) {
+               dev_err(dev, "Failed to set boost output voltage value\n");
+               return ret;
+       }
+
+       return 0;
+}
+#endif
+
+/*
+ * Before setting the charger into charge mode, battery regulation voltage is
+ * set. This is needed because boost output voltage may differ from battery
+ * regulation voltage. F_VOREG bits represent either battery regulation voltage
+ * or boost output voltage, depending on the mode the charger is. Both battery
+ * regulation voltage and boost output voltage are read from DT/ACPI during
+ * probe.
+ */
+static int rt9455_set_voreg_before_charge_mode(struct rt9455_info *info)
+{
+       struct device *dev = &info->client->dev;
+       int ret;
+
+       ret = rt9455_set_field_val(info, F_VOREG,
+                                  rt9455_voreg_values,
+                                  ARRAY_SIZE(rt9455_voreg_values),
+                                  info->voreg);
+       if (ret) {
+               dev_err(dev, "Failed to set VOREG value\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+static int rt9455_irq_handler_check_irq1_register(struct rt9455_info *info,
+                                                 bool *_is_battery_absent,
+                                                 bool *_alert_userspace)
+{
+       unsigned int irq1, mask1, mask2;
+       struct device *dev = &info->client->dev;
+       bool is_battery_absent = false;
+       bool alert_userspace = false;
+       int ret;
+
+       ret = regmap_read(info->regmap, RT9455_REG_IRQ1, &irq1);
+       if (ret) {
+               dev_err(dev, "Failed to read IRQ1 register\n");
+               return ret;
+       }
+
+       ret = regmap_read(info->regmap, RT9455_REG_MASK1, &mask1);
+       if (ret) {
+               dev_err(dev, "Failed to read MASK1 register\n");
+               return ret;
+       }
+
+       if (irq1 & GET_MASK(F_TSDI)) {
+               dev_err(dev, "Thermal shutdown fault occurred\n");
+               alert_userspace = true;
+       }
+
+       if (irq1 & GET_MASK(F_VINOVPI)) {
+               dev_err(dev, "Overvoltage input occurred\n");
+               alert_userspace = true;
+       }
+
+       if (irq1 & GET_MASK(F_BATAB)) {
+               dev_err(dev, "Battery absence occurred\n");
+               is_battery_absent = true;
+               alert_userspace = true;
+
+               if ((mask1 & GET_MASK(F_BATABM)) == 0) {
+                       ret = regmap_field_write(info->regmap_fields[F_BATABM],
+                                                0x01);
+                       if (ret) {
+                               dev_err(dev, "Failed to mask BATAB interrupt\n");
+                               return ret;
+                       }
+               }
+
+               ret = regmap_read(info->regmap, RT9455_REG_MASK2, &mask2);
+               if (ret) {
+                       dev_err(dev, "Failed to read MASK2 register\n");
+                       return ret;
+               }
+
+               if (mask2 & GET_MASK(F_CHTERMIM)) {
+                       ret = regmap_field_write(
+                               info->regmap_fields[F_CHTERMIM], 0x00);
+                       if (ret) {
+                               dev_err(dev, "Failed to unmask CHTERMI interrupt\n");
+                               return ret;
+                       }
+               }
+
+               if (mask2 & GET_MASK(F_CHRCHGIM)) {
+                       ret = regmap_field_write(
+                               info->regmap_fields[F_CHRCHGIM], 0x00);
+                       if (ret) {
+                               dev_err(dev, "Failed to unmask CHRCHGI interrupt\n");
+                               return ret;
+                       }
+               }
+
+               /*
+                * When the battery is absent, max_charging_time_work is
+                * cancelled, since no charging is done.
+                */
+               cancel_delayed_work_sync(&info->max_charging_time_work);
+               /*
+                * Since no interrupt is triggered when the battery is
+                * reconnected, max_charging_time_work is not rescheduled.
+                * Therefore, batt_presence_work is scheduled to check whether
+                * the battery is still absent or not.
+                */
+               queue_delayed_work(system_power_efficient_wq,
+                                  &info->batt_presence_work,
+                                  RT9455_BATT_PRESENCE_DELAY * HZ);
+       }
+
+       *_is_battery_absent = is_battery_absent;
+
+       if (alert_userspace)
+               *_alert_userspace = alert_userspace;
+
+       return 0;
+}
+
+static int rt9455_irq_handler_check_irq2_register(struct rt9455_info *info,
+                                                 bool is_battery_absent,
+                                                 bool *_alert_userspace)
+{
+       unsigned int irq2, mask2;
+       struct device *dev = &info->client->dev;
+       bool alert_userspace = false;
+       int ret;
+
+       ret = regmap_read(info->regmap, RT9455_REG_IRQ2, &irq2);
+       if (ret) {
+               dev_err(dev, "Failed to read IRQ2 register\n");
+               return ret;
+       }
+
+       ret = regmap_read(info->regmap, RT9455_REG_MASK2, &mask2);
+       if (ret) {
+               dev_err(dev, "Failed to read MASK2 register\n");
+               return ret;
+       }
+
+       if (irq2 & GET_MASK(F_CHRVPI)) {
+               dev_dbg(dev, "Charger fault occurred\n");
+               alert_userspace = true;
+               /*
+                * CHRVPI bit is set in 2 cases:
+                * 1. when the power source is connected to the charger.
+                * 2. when the power source is disconnected from the charger.
+                * To identify the case, PWR_RDY bit is checked. Because
+                * PWR_RDY bit is set / cleared after CHRVPI interrupt is
+                * triggered, it is used delayed_work to later read PWR_RDY bit.
+                */
+               queue_delayed_work(system_power_efficient_wq,
+                                  &info->pwr_rdy_work,
+                                  RT9455_PWR_RDY_DELAY * HZ);
+       }
+       if (irq2 & GET_MASK(F_CHBATOVI)) {
+               dev_err(dev, "Battery OVP occurred\n");
+               alert_userspace = true;
+       }
+       if (irq2 & GET_MASK(F_CHTERMI)) {
+               dev_dbg(dev, "Charge terminated\n");
+               if (!is_battery_absent) {
+                       if ((mask2 & GET_MASK(F_CHTERMIM)) == 0) {
+                               ret = regmap_field_write(
+                                       info->regmap_fields[F_CHTERMIM], 0x01);
+                               if (ret) {
+                                       dev_err(dev, "Failed to mask CHTERMI interrupt\n");
+                                       return ret;
+                               }
+                               /*
+                                * Update MASK2 value, since CHTERMIM bit is
+                                * set.
+                                */
+                               mask2 = mask2 | GET_MASK(F_CHTERMIM);
+                       }
+                       cancel_delayed_work_sync(&info->max_charging_time_work);
+                       alert_userspace = true;
+               }
+       }
+       if (irq2 & GET_MASK(F_CHRCHGI)) {
+               dev_dbg(dev, "Recharge request\n");
+               ret = regmap_field_write(info->regmap_fields[F_CHG_EN],
+                                        RT9455_CHARGE_ENABLE);
+               if (ret) {
+                       dev_err(dev, "Failed to enable charging\n");
+                       return ret;
+               }
+               if (mask2 & GET_MASK(F_CHTERMIM)) {
+                       ret = regmap_field_write(
+                               info->regmap_fields[F_CHTERMIM], 0x00);
+                       if (ret) {
+                               dev_err(dev, "Failed to unmask CHTERMI interrupt\n");
+                               return ret;
+                       }
+                       /* Update MASK2 value, since CHTERMIM bit is cleared. */
+                       mask2 = mask2 & ~GET_MASK(F_CHTERMIM);
+               }
+               if (!is_battery_absent) {
+                       /*
+                        * No need to check whether the charger is connected to
+                        * power source when CHRCHGI is received, since CHRCHGI
+                        * is not triggered if the charger is not connected to
+                        * the power source.
+                        */
+                       queue_delayed_work(system_power_efficient_wq,
+                                          &info->max_charging_time_work,
+                                          RT9455_MAX_CHARGING_TIME * HZ);
+                       alert_userspace = true;
+               }
+       }
+       if (irq2 & GET_MASK(F_CH32MI)) {
+               dev_err(dev, "Charger fault. 32 mins timeout occurred\n");
+               alert_userspace = true;
+       }
+       if (irq2 & GET_MASK(F_CHTREGI)) {
+               dev_warn(dev,
+                        "Charger warning. Thermal regulation loop active\n");
+               alert_userspace = true;
+       }
+       if (irq2 & GET_MASK(F_CHMIVRI)) {
+               dev_dbg(dev,
+                       "Charger warning. Input voltage MIVR loop active\n");
+       }
+
+       if (alert_userspace)
+               *_alert_userspace = alert_userspace;
+
+       return 0;
+}
+
+static int rt9455_irq_handler_check_irq3_register(struct rt9455_info *info,
+                                                 bool *_alert_userspace)
+{
+       unsigned int irq3, mask3;
+       struct device *dev = &info->client->dev;
+       bool alert_userspace = false;
+       int ret;
+
+       ret = regmap_read(info->regmap, RT9455_REG_IRQ3, &irq3);
+       if (ret) {
+               dev_err(dev, "Failed to read IRQ3 register\n");
+               return ret;
+       }
+
+       ret = regmap_read(info->regmap, RT9455_REG_MASK3, &mask3);
+       if (ret) {
+               dev_err(dev, "Failed to read MASK3 register\n");
+               return ret;
+       }
+
+       if (irq3 & GET_MASK(F_BSTBUSOVI)) {
+               dev_err(dev, "Boost fault. Overvoltage input occurred\n");
+               alert_userspace = true;
+       }
+       if (irq3 & GET_MASK(F_BSTOLI)) {
+               dev_err(dev, "Boost fault. Overload\n");
+               alert_userspace = true;
+       }
+       if (irq3 & GET_MASK(F_BSTLOWVI)) {
+               dev_err(dev, "Boost fault. Battery voltage too low\n");
+               alert_userspace = true;
+       }
+       if (irq3 & GET_MASK(F_BST32SI)) {
+               dev_err(dev, "Boost fault. 32 seconds timeout occurred.\n");
+               alert_userspace = true;
+       }
+
+       if (alert_userspace) {
+               dev_info(dev, "Boost fault occurred, therefore the charger goes into charge mode\n");
+               ret = rt9455_set_voreg_before_charge_mode(info);
+               if (ret) {
+                       dev_err(dev, "Failed to set VOREG before entering charge mode\n");
+                       return ret;
+               }
+               ret = regmap_field_write(info->regmap_fields[F_OPA_MODE],
+                                        RT9455_CHARGE_MODE);
+               if (ret) {
+                       dev_err(dev, "Failed to set charger in charge mode\n");
+                       return ret;
+               }
+               *_alert_userspace = alert_userspace;
+       }
+
+       return 0;
+}
+
+static irqreturn_t rt9455_irq_handler_thread(int irq, void *data)
+{
+       struct rt9455_info *info = data;
+       struct device *dev;
+       bool alert_userspace = false;
+       bool is_battery_absent = false;
+       unsigned int status;
+       int ret;
+
+       if (!info)
+               return IRQ_NONE;
+
+       dev = &info->client->dev;
+
+       if (irq != info->client->irq) {
+               dev_err(dev, "Interrupt is not for RT9455 charger\n");
+               return IRQ_NONE;
+       }
+
+       ret = regmap_field_read(info->regmap_fields[F_STAT], &status);
+       if (ret) {
+               dev_err(dev, "Failed to read STAT bits\n");
+               return IRQ_HANDLED;
+       }
+       dev_dbg(dev, "Charger status is %d\n", status);
+
+       /*
+        * Each function that processes an IRQ register receives as output
+        * parameter alert_userspace pointer. alert_userspace is set to true
+        * in such a function only if an interrupt has occurred in the
+        * respective interrupt register. This way, it is avoided the following
+        * case: interrupt occurs only in IRQ1 register,
+        * rt9455_irq_handler_check_irq1_register() function sets to true
+        * alert_userspace, but rt9455_irq_handler_check_irq2_register()
+        * and rt9455_irq_handler_check_irq3_register() functions set to false
+        * alert_userspace and power_supply_changed() is never called.
+        */
+       ret = rt9455_irq_handler_check_irq1_register(info, &is_battery_absent,
+                                                    &alert_userspace);
+       if (ret) {
+               dev_err(dev, "Failed to handle IRQ1 register\n");
+               return IRQ_HANDLED;
+       }
+
+       ret = rt9455_irq_handler_check_irq2_register(info, is_battery_absent,
+                                                    &alert_userspace);
+       if (ret) {
+               dev_err(dev, "Failed to handle IRQ2 register\n");
+               return IRQ_HANDLED;
+       }
+
+       ret = rt9455_irq_handler_check_irq3_register(info, &alert_userspace);
+       if (ret) {
+               dev_err(dev, "Failed to handle IRQ3 register\n");
+               return IRQ_HANDLED;
+       }
+
+       if (alert_userspace) {
+               /*
+                * Sometimes, an interrupt occurs while rt9455_probe() function
+                * is executing and power_supply_register() is not yet called.
+                * Do not call power_supply_charged() in this case.
+                */
+               if (info->charger)
+                       power_supply_changed(info->charger);
+       }
+
+       return IRQ_HANDLED;
+}
+
+static int rt9455_discover_charger(struct rt9455_info *info, u32 *ichrg,
+                                  u32 *ieoc_percentage,
+                                  u32 *mivr, u32 *iaicr)
+{
+       struct device *dev = &info->client->dev;
+       int ret;
+
+       if (!dev->of_node && !ACPI_HANDLE(dev)) {
+               dev_err(dev, "No support for either device tree or ACPI\n");
+               return -EINVAL;
+       }
+       /*
+        * ICHRG, IEOC_PERCENTAGE, VOREG and boost output voltage are mandatory
+        * parameters.
+        */
+       ret = device_property_read_u32(dev, "richtek,output-charge-current",
+                                      ichrg);
+       if (ret) {
+               dev_err(dev, "Error: missing \"output-charge-current\" property\n");
+               return ret;
+       }
+
+       ret = device_property_read_u32(dev, "richtek,end-of-charge-percentage",
+                                      ieoc_percentage);
+       if (ret) {
+               dev_err(dev, "Error: missing \"end-of-charge-percentage\" property\n");
+               return ret;
+       }
+
+       ret = device_property_read_u32(dev,
+                                      "richtek,battery-regulation-voltage",
+                                      &info->voreg);
+       if (ret) {
+               dev_err(dev, "Error: missing \"battery-regulation-voltage\" property\n");
+               return ret;
+       }
+
+       ret = device_property_read_u32(dev, "richtek,boost-output-voltage",
+                                      &info->boost_voltage);
+       if (ret) {
+               dev_err(dev, "Error: missing \"boost-output-voltage\" property\n");
+               return ret;
+       }
+
+       /*
+        * MIVR and IAICR are optional parameters. Do not return error if one of
+        * them is not present in ACPI table or device tree specification.
+        */
+       device_property_read_u32(dev, "richtek,min-input-voltage-regulation",
+                                mivr);
+       device_property_read_u32(dev, "richtek,avg-input-current-regulation",
+                                iaicr);
+
+       return 0;
+}
+
+#if IS_ENABLED(CONFIG_USB_PHY)
+static int rt9455_usb_event_none(struct rt9455_info *info,
+                                u8 opa_mode, u8 iaicr)
+{
+       struct device *dev = &info->client->dev;
+       int ret;
+
+       if (opa_mode == RT9455_BOOST_MODE) {
+               ret = rt9455_set_voreg_before_charge_mode(info);
+               if (ret) {
+                       dev_err(dev, "Failed to set VOREG before entering charge mode\n");
+                       return ret;
+               }
+               /*
+                * If the charger is in boost mode, and it has received
+                * USB_EVENT_NONE, this means the consumer device powered by the
+                * charger is not connected anymore.
+                * In this case, the charger goes into charge mode.
+                */
+               dev_dbg(dev, "USB_EVENT_NONE received, therefore the charger goes into charge mode\n");
+               ret = regmap_field_write(info->regmap_fields[F_OPA_MODE],
+                                        RT9455_CHARGE_MODE);
+               if (ret) {
+                       dev_err(dev, "Failed to set charger in charge mode\n");
+                       return NOTIFY_DONE;
+               }
+       }
+
+       dev_dbg(dev, "USB_EVENT_NONE received, therefore IAICR is set to its minimum value\n");
+       if (iaicr != RT9455_IAICR_100MA) {
+               ret = regmap_field_write(info->regmap_fields[F_IAICR],
+                                        RT9455_IAICR_100MA);
+               if (ret) {
+                       dev_err(dev, "Failed to set IAICR value\n");
+                       return NOTIFY_DONE;
+               }
+       }
+
+       return NOTIFY_OK;
+}
+
+static int rt9455_usb_event_vbus(struct rt9455_info *info,
+                                u8 opa_mode, u8 iaicr)
+{
+       struct device *dev = &info->client->dev;
+       int ret;
+
+       if (opa_mode == RT9455_BOOST_MODE) {
+               ret = rt9455_set_voreg_before_charge_mode(info);
+               if (ret) {
+                       dev_err(dev, "Failed to set VOREG before entering charge mode\n");
+                       return ret;
+               }
+               /*
+                * If the charger is in boost mode, and it has received
+                * USB_EVENT_VBUS, this means the consumer device powered by the
+                * charger is not connected anymore.
+                * In this case, the charger goes into charge mode.
+                */
+               dev_dbg(dev, "USB_EVENT_VBUS received, therefore the charger goes into charge mode\n");
+               ret = regmap_field_write(info->regmap_fields[F_OPA_MODE],
+                                        RT9455_CHARGE_MODE);
+               if (ret) {
+                       dev_err(dev, "Failed to set charger in charge mode\n");
+                       return NOTIFY_DONE;
+               }
+       }
+
+       dev_dbg(dev, "USB_EVENT_VBUS received, therefore IAICR is set to 500 mA\n");
+       if (iaicr != RT9455_IAICR_500MA) {
+               ret = regmap_field_write(info->regmap_fields[F_IAICR],
+                                        RT9455_IAICR_500MA);
+               if (ret) {
+                       dev_err(dev, "Failed to set IAICR value\n");
+                       return NOTIFY_DONE;
+               }
+       }
+
+       return NOTIFY_OK;
+}
+
+static int rt9455_usb_event_id(struct rt9455_info *info,
+                              u8 opa_mode, u8 iaicr)
+{
+       struct device *dev = &info->client->dev;
+       int ret;
+
+       if (opa_mode == RT9455_CHARGE_MODE) {
+               ret = rt9455_set_boost_voltage_before_boost_mode(info);
+               if (ret) {
+                       dev_err(dev, "Failed to set boost output voltage before entering boost mode\n");
+                       return ret;
+               }
+               /*
+                * If the charger is in charge mode, and it has received
+                * USB_EVENT_ID, this means a consumer device is connected and
+                * it should be powered by the charger.
+                * In this case, the charger goes into boost mode.
+                */
+               dev_dbg(dev, "USB_EVENT_ID received, therefore the charger goes into boost mode\n");
+               ret = regmap_field_write(info->regmap_fields[F_OPA_MODE],
+                                        RT9455_BOOST_MODE);
+               if (ret) {
+                       dev_err(dev, "Failed to set charger in boost mode\n");
+                       return NOTIFY_DONE;
+               }
+       }
+
+       dev_dbg(dev, "USB_EVENT_ID received, therefore IAICR is set to its minimum value\n");
+       if (iaicr != RT9455_IAICR_100MA) {
+               ret = regmap_field_write(info->regmap_fields[F_IAICR],
+                                        RT9455_IAICR_100MA);
+               if (ret) {
+                       dev_err(dev, "Failed to set IAICR value\n");
+                       return NOTIFY_DONE;
+               }
+       }
+
+       return NOTIFY_OK;
+}
+
+static int rt9455_usb_event_charger(struct rt9455_info *info,
+                                   u8 opa_mode, u8 iaicr)
+{
+       struct device *dev = &info->client->dev;
+       int ret;
+
+       if (opa_mode == RT9455_BOOST_MODE) {
+               ret = rt9455_set_voreg_before_charge_mode(info);
+               if (ret) {
+                       dev_err(dev, "Failed to set VOREG before entering charge mode\n");
+                       return ret;
+               }
+               /*
+                * If the charger is in boost mode, and it has received
+                * USB_EVENT_CHARGER, this means the consumer device powered by
+                * the charger is not connected anymore.
+                * In this case, the charger goes into charge mode.
+                */
+               dev_dbg(dev, "USB_EVENT_CHARGER received, therefore the charger goes into charge mode\n");
+               ret = regmap_field_write(info->regmap_fields[F_OPA_MODE],
+                                        RT9455_CHARGE_MODE);
+               if (ret) {
+                       dev_err(dev, "Failed to set charger in charge mode\n");
+                       return NOTIFY_DONE;
+               }
+       }
+
+       dev_dbg(dev, "USB_EVENT_CHARGER received, therefore IAICR is set to no current limit\n");
+       if (iaicr != RT9455_IAICR_NO_LIMIT) {
+               ret = regmap_field_write(info->regmap_fields[F_IAICR],
+                                        RT9455_IAICR_NO_LIMIT);
+               if (ret) {
+                       dev_err(dev, "Failed to set IAICR value\n");
+                       return NOTIFY_DONE;
+               }
+       }
+
+       return NOTIFY_OK;
+}
+
+static int rt9455_usb_event(struct notifier_block *nb,
+                           unsigned long event, void *power)
+{
+       struct rt9455_info *info = container_of(nb, struct rt9455_info, nb);
+       struct device *dev = &info->client->dev;
+       unsigned int opa_mode, iaicr;
+       int ret;
+
+       /*
+        * Determine whether the charger is in charge mode
+        * or in boost mode.
+        */
+       ret = regmap_field_read(info->regmap_fields[F_OPA_MODE],
+                               &opa_mode);
+       if (ret) {
+               dev_err(dev, "Failed to read OPA_MODE value\n");
+               return NOTIFY_DONE;
+       }
+
+       ret = regmap_field_read(info->regmap_fields[F_IAICR],
+                               &iaicr);
+       if (ret) {
+               dev_err(dev, "Failed to read IAICR value\n");
+               return NOTIFY_DONE;
+       }
+
+       dev_dbg(dev, "Received USB event %lu\n", event);
+       switch (event) {
+       case USB_EVENT_NONE:
+               return rt9455_usb_event_none(info, opa_mode, iaicr);
+       case USB_EVENT_VBUS:
+               return rt9455_usb_event_vbus(info, opa_mode, iaicr);
+       case USB_EVENT_ID:
+               return rt9455_usb_event_id(info, opa_mode, iaicr);
+       case USB_EVENT_CHARGER:
+               return rt9455_usb_event_charger(info, opa_mode, iaicr);
+       default:
+               dev_err(dev, "Unknown USB event\n");
+       }
+       return NOTIFY_DONE;
+}
+#endif
+
+static void rt9455_pwr_rdy_work_callback(struct work_struct *work)
+{
+       struct rt9455_info *info = container_of(work, struct rt9455_info,
+                                               pwr_rdy_work.work);
+       struct device *dev = &info->client->dev;
+       unsigned int pwr_rdy;
+       int ret;
+
+       ret = regmap_field_read(info->regmap_fields[F_PWR_RDY], &pwr_rdy);
+       if (ret) {
+               dev_err(dev, "Failed to read PWR_RDY bit\n");
+               return;
+       }
+       switch (pwr_rdy) {
+       case RT9455_PWR_FAULT:
+               dev_dbg(dev, "Charger disconnected from power source\n");
+               cancel_delayed_work_sync(&info->max_charging_time_work);
+               break;
+       case RT9455_PWR_GOOD:
+               dev_dbg(dev, "Charger connected to power source\n");
+               ret = regmap_field_write(info->regmap_fields[F_CHG_EN],
+                                        RT9455_CHARGE_ENABLE);
+               if (ret) {
+                       dev_err(dev, "Failed to enable charging\n");
+                       return;
+               }
+               queue_delayed_work(system_power_efficient_wq,
+                                  &info->max_charging_time_work,
+                                  RT9455_MAX_CHARGING_TIME * HZ);
+               break;
+       }
+}
+
+static void rt9455_max_charging_time_work_callback(struct work_struct *work)
+{
+       struct rt9455_info *info = container_of(work, struct rt9455_info,
+                                               max_charging_time_work.work);
+       struct device *dev = &info->client->dev;
+       int ret;
+
+       dev_err(dev, "Battery has been charging for at least 6 hours and is not yet fully charged. Battery is dead, therefore charging is disabled.\n");
+       ret = regmap_field_write(info->regmap_fields[F_CHG_EN],
+                                RT9455_CHARGE_DISABLE);
+       if (ret)
+               dev_err(dev, "Failed to disable charging\n");
+}
+
+static void rt9455_batt_presence_work_callback(struct work_struct *work)
+{
+       struct rt9455_info *info = container_of(work, struct rt9455_info,
+                                               batt_presence_work.work);
+       struct device *dev = &info->client->dev;
+       unsigned int irq1, mask1;
+       int ret;
+
+       ret = regmap_read(info->regmap, RT9455_REG_IRQ1, &irq1);
+       if (ret) {
+               dev_err(dev, "Failed to read IRQ1 register\n");
+               return;
+       }
+
+       /*
+        * If the battery is still absent, batt_presence_work is rescheduled.
+        * Otherwise, max_charging_time is scheduled.
+        */
+       if (irq1 & GET_MASK(F_BATAB)) {
+               queue_delayed_work(system_power_efficient_wq,
+                                  &info->batt_presence_work,
+                                  RT9455_BATT_PRESENCE_DELAY * HZ);
+       } else {
+               queue_delayed_work(system_power_efficient_wq,
+                                  &info->max_charging_time_work,
+                                  RT9455_MAX_CHARGING_TIME * HZ);
+
+               ret = regmap_read(info->regmap, RT9455_REG_MASK1, &mask1);
+               if (ret) {
+                       dev_err(dev, "Failed to read MASK1 register\n");
+                       return;
+               }
+
+               if (mask1 & GET_MASK(F_BATABM)) {
+                       ret = regmap_field_write(info->regmap_fields[F_BATABM],
+                                                0x00);
+                       if (ret)
+                               dev_err(dev, "Failed to unmask BATAB interrupt\n");
+               }
+       }
+}
+
+static const struct power_supply_desc rt9455_charger_desc = {
+       .name                   = RT9455_DRIVER_NAME,
+       .type                   = POWER_SUPPLY_TYPE_USB,
+       .properties             = rt9455_charger_properties,
+       .num_properties         = ARRAY_SIZE(rt9455_charger_properties),
+       .get_property           = rt9455_charger_get_property,
+};
+
+static bool rt9455_is_writeable_reg(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case RT9455_REG_DEV_ID:
+       case RT9455_REG_IRQ1:
+       case RT9455_REG_IRQ2:
+       case RT9455_REG_IRQ3:
+               return false;
+       default:
+               return true;
+       }
+}
+
+static bool rt9455_is_volatile_reg(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case RT9455_REG_DEV_ID:
+       case RT9455_REG_CTRL5:
+       case RT9455_REG_CTRL6:
+               return false;
+       default:
+               return true;
+       }
+}
+
+static const struct regmap_config rt9455_regmap_config = {
+       .reg_bits       = 8,
+       .val_bits       = 8,
+       .writeable_reg  = rt9455_is_writeable_reg,
+       .volatile_reg   = rt9455_is_volatile_reg,
+       .max_register   = RT9455_REG_MASK3,
+       .cache_type     = REGCACHE_RBTREE,
+};
+
+static int rt9455_probe(struct i2c_client *client,
+                       const struct i2c_device_id *id)
+{
+       struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
+       struct device *dev = &client->dev;
+       struct rt9455_info *info;
+       struct power_supply_config rt9455_charger_config = {};
+       /*
+        * Mandatory device-specific data values. Also, VOREG and boost output
+        * voltage are mandatory values, but they are stored in rt9455_info
+        * structure.
+        */
+       u32 ichrg, ieoc_percentage;
+       /* Optional device-specific data values. */
+       u32 mivr = -1, iaicr = -1;
+       int i, ret;
+
+       if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
+               dev_err(dev, "No support for SMBUS_BYTE_DATA\n");
+               return -ENODEV;
+       }
+       info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
+       if (!info)
+               return -ENOMEM;
+
+       info->client = client;
+       i2c_set_clientdata(client, info);
+
+       info->regmap = devm_regmap_init_i2c(client,
+                                           &rt9455_regmap_config);
+       if (IS_ERR(info->regmap)) {
+               dev_err(dev, "Failed to initialize register map\n");
+               return -EINVAL;
+       }
+
+       for (i = 0; i < F_MAX_FIELDS; i++) {
+               info->regmap_fields[i] =
+                       devm_regmap_field_alloc(dev, info->regmap,
+                                               rt9455_reg_fields[i]);
+               if (IS_ERR(info->regmap_fields[i])) {
+                       dev_err(dev,
+                               "Failed to allocate regmap field = %d\n", i);
+                       return PTR_ERR(info->regmap_fields[i]);
+               }
+       }
+
+       ret = rt9455_discover_charger(info, &ichrg, &ieoc_percentage,
+                                     &mivr, &iaicr);
+       if (ret) {
+               dev_err(dev, "Failed to discover charger\n");
+               return ret;
+       }
+
+#if IS_ENABLED(CONFIG_USB_PHY)
+       info->usb_phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2);
+       if (IS_ERR(info->usb_phy)) {
+               dev_err(dev, "Failed to get USB transceiver\n");
+       } else {
+               info->nb.notifier_call = rt9455_usb_event;
+               ret = usb_register_notifier(info->usb_phy, &info->nb);
+               if (ret) {
+                       dev_err(dev, "Failed to register USB notifier\n");
+                       /*
+                        * If usb_register_notifier() fails, set notifier_call
+                        * to NULL, to avoid calling usb_unregister_notifier().
+                        */
+                       info->nb.notifier_call = NULL;
+               }
+       }
+#endif
+
+       INIT_DEFERRABLE_WORK(&info->pwr_rdy_work, rt9455_pwr_rdy_work_callback);
+       INIT_DEFERRABLE_WORK(&info->max_charging_time_work,
+                            rt9455_max_charging_time_work_callback);
+       INIT_DEFERRABLE_WORK(&info->batt_presence_work,
+                            rt9455_batt_presence_work_callback);
+
+       rt9455_charger_config.of_node           = dev->of_node;
+       rt9455_charger_config.drv_data          = info;
+       rt9455_charger_config.supplied_to       = rt9455_charger_supplied_to;
+       rt9455_charger_config.num_supplicants   =
+                                       ARRAY_SIZE(rt9455_charger_supplied_to);
+       ret = devm_request_threaded_irq(dev, client->irq, NULL,
+                                       rt9455_irq_handler_thread,
+                                       IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+                                       RT9455_DRIVER_NAME, info);
+       if (ret) {
+               dev_err(dev, "Failed to register IRQ handler\n");
+               goto put_usb_notifier;
+       }
+
+       ret = rt9455_hw_init(info, ichrg, ieoc_percentage, mivr, iaicr);
+       if (ret) {
+               dev_err(dev, "Failed to set charger to its default values\n");
+               goto put_usb_notifier;
+       }
+
+       info->charger = devm_power_supply_register(dev, &rt9455_charger_desc,
+                                                  &rt9455_charger_config);
+       if (IS_ERR(info->charger)) {
+               dev_err(dev, "Failed to register charger\n");
+               ret = PTR_ERR(info->charger);
+               goto put_usb_notifier;
+       }
+
+       return 0;
+
+put_usb_notifier:
+#if IS_ENABLED(CONFIG_USB_PHY)
+       if (info->nb.notifier_call)  {
+               usb_unregister_notifier(info->usb_phy, &info->nb);
+               info->nb.notifier_call = NULL;
+       }
+#endif
+       return ret;
+}
+
+static int rt9455_remove(struct i2c_client *client)
+{
+       int ret;
+       struct rt9455_info *info = i2c_get_clientdata(client);
+
+       ret = rt9455_register_reset(info);
+       if (ret)
+               dev_err(&info->client->dev, "Failed to set charger to its default values\n");
+
+#if IS_ENABLED(CONFIG_USB_PHY)
+       if (info->nb.notifier_call)
+               usb_unregister_notifier(info->usb_phy, &info->nb);
+#endif
+
+       cancel_delayed_work_sync(&info->pwr_rdy_work);
+       cancel_delayed_work_sync(&info->max_charging_time_work);
+       cancel_delayed_work_sync(&info->batt_presence_work);
+
+       return ret;
+}
+
+static const struct i2c_device_id rt9455_i2c_id_table[] = {
+       { RT9455_DRIVER_NAME, 0 },
+       { },
+};
+MODULE_DEVICE_TABLE(i2c, rt9455_i2c_id_table);
+
+static const struct of_device_id rt9455_of_match[] = {
+       { .compatible = "richtek,rt9455", },
+       { },
+};
+MODULE_DEVICE_TABLE(of, rt9455_of_match);
+
+static const struct acpi_device_id rt9455_i2c_acpi_match[] = {
+       { "RT945500", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(acpi, rt9455_i2c_acpi_match);
+
+static struct i2c_driver rt9455_driver = {
+       .probe          = rt9455_probe,
+       .remove         = rt9455_remove,
+       .id_table       = rt9455_i2c_id_table,
+       .driver = {
+               .name           = RT9455_DRIVER_NAME,
+               .of_match_table = of_match_ptr(rt9455_of_match),
+               .acpi_match_table = ACPI_PTR(rt9455_i2c_acpi_match),
+       },
+};
+module_i2c_driver(rt9455_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Anda-Maria Nicolae <anda-maria.nicolae@intel.com>");
+MODULE_ALIAS("i2c:rt9455-charger");
+MODULE_DESCRIPTION("Richtek RT9455 Charger Driver");
index de1178659d4b45beaee2ddcd04b1580236c0a3a1..d6226d68b5746d67cfb8561afa0906684280b76d 100644 (file)
@@ -28,6 +28,7 @@
 #include <linux/interrupt.h>
 #include <linux/gpio.h>
 #include <linux/of.h>
+#include <linux/stat.h>
 
 #include <linux/power/sbs-battery.h>
 
@@ -170,6 +171,7 @@ struct sbs_info {
 
 static char model_name[I2C_SMBUS_BLOCK_MAX + 1];
 static char manufacturer[I2C_SMBUS_BLOCK_MAX + 1];
+static bool force_load;
 
 static int sbs_read_word_data(struct i2c_client *client, u8 address)
 {
@@ -885,14 +887,17 @@ static int sbs_probe(struct i2c_client *client,
 
 skip_gpio:
        /*
-        * Before we register, we need to make sure we can actually talk
+        * Before we register, we might need to make sure we can actually talk
         * to the battery.
         */
-       rc = sbs_read_word_data(client, sbs_data[REG_STATUS].addr);
-       if (rc < 0) {
-               dev_err(&client->dev, "%s: Failed to get device status\n",
-                       __func__);
-               goto exit_psupply;
+       if (!force_load) {
+               rc = sbs_read_word_data(client, sbs_data[REG_STATUS].addr);
+
+               if (rc < 0) {
+                       dev_err(&client->dev, "%s: Failed to get device status\n",
+                               __func__);
+                       goto exit_psupply;
+               }
        }
 
        chip->power_supply = power_supply_register(&client->dev, sbs_desc,
@@ -991,3 +996,7 @@ module_i2c_driver(sbs_battery_driver);
 
 MODULE_DESCRIPTION("SBS battery monitor driver");
 MODULE_LICENSE("GPL");
+
+module_param(force_load, bool, S_IRUSR | S_IRGRP | S_IROTH);
+MODULE_PARM_DESC(force_load,
+                "Attempt to load the driver even if no battery is connected");
index 0161bdabd5a3c31c07c0ebe088596a561b258f70..db11ae6599f341546ebfd2d3995f06ecdbec5097 100644 (file)
@@ -609,6 +609,7 @@ static int wm831x_power_probe(struct platform_device *pdev)
        return ret;
 
 err_bat_irq:
+       --i;
        for (; i >= 0; i--) {
                irq = platform_get_irq_byname(pdev, wm831x_bat_irqs[i]);
                free_irq(irq, power);
index fd243231620ac10554b0b2616792c00e189d970d..482b22ddc7b2cc424da112aafa6b17065e75d927 100644 (file)
@@ -187,6 +187,7 @@ struct rapl_package {
 };
 
 struct rapl_defaults {
+       u8 floor_freq_reg_addr;
        int (*check_unit)(struct rapl_package *rp, int cpu);
        void (*set_floor_freq)(struct rapl_domain *rd, bool mode);
        u64 (*compute_time_window)(struct rapl_package *rp, u64 val,
@@ -196,7 +197,8 @@ struct rapl_defaults {
 static struct rapl_defaults *rapl_defaults;
 
 /* Sideband MBI registers */
-#define IOSF_CPU_POWER_BUDGET_CTL (0x2)
+#define IOSF_CPU_POWER_BUDGET_CTL_BYT (0x2)
+#define IOSF_CPU_POWER_BUDGET_CTL_TNG (0xdf)
 
 #define PACKAGE_PLN_INT_SAVED   BIT(0)
 #define MAX_PRIM_NAME (32)
@@ -358,7 +360,8 @@ static int set_domain_enable(struct powercap_zone *power_zone, bool mode)
 
        get_online_cpus();
        rapl_write_data_raw(rd, PL1_ENABLE, mode);
-       rapl_defaults->set_floor_freq(rd, mode);
+       if (rapl_defaults->set_floor_freq)
+               rapl_defaults->set_floor_freq(rd, mode);
        put_online_cpus();
 
        return 0;
@@ -979,16 +982,22 @@ static void set_floor_freq_atom(struct rapl_domain *rd, bool enable)
        static u32 power_ctrl_orig_val;
        u32 mdata;
 
+       if (!rapl_defaults->floor_freq_reg_addr) {
+               pr_err("Invalid floor frequency config register\n");
+               return;
+       }
+
        if (!power_ctrl_orig_val)
                iosf_mbi_read(BT_MBI_UNIT_PMC, BT_MBI_PMC_READ,
-                       IOSF_CPU_POWER_BUDGET_CTL, &power_ctrl_orig_val);
+                       rapl_defaults->floor_freq_reg_addr,
+                               &power_ctrl_orig_val);
        mdata = power_ctrl_orig_val;
        if (enable) {
                mdata &= ~(0x7f << 8);
                mdata |= 1 << 8;
        }
        iosf_mbi_write(BT_MBI_UNIT_PMC, BT_MBI_PMC_WRITE,
-               IOSF_CPU_POWER_BUDGET_CTL, mdata);
+               rapl_defaults->floor_freq_reg_addr, mdata);
 }
 
 static u64 rapl_compute_time_window_core(struct rapl_package *rp, u64 value,
@@ -1029,6 +1038,7 @@ static u64 rapl_compute_time_window_atom(struct rapl_package *rp, u64 value,
 }
 
 static const struct rapl_defaults rapl_defaults_core = {
+       .floor_freq_reg_addr = 0,
        .check_unit = rapl_check_unit_core,
        .set_floor_freq = set_floor_freq_default,
        .compute_time_window = rapl_compute_time_window_core,
@@ -1041,12 +1051,34 @@ static const struct rapl_defaults rapl_defaults_hsw_server = {
        .dram_domain_energy_unit = 15300,
 };
 
-static const struct rapl_defaults rapl_defaults_atom = {
+static const struct rapl_defaults rapl_defaults_byt = {
+       .floor_freq_reg_addr = IOSF_CPU_POWER_BUDGET_CTL_BYT,
+       .check_unit = rapl_check_unit_atom,
+       .set_floor_freq = set_floor_freq_atom,
+       .compute_time_window = rapl_compute_time_window_atom,
+};
+
+static const struct rapl_defaults rapl_defaults_tng = {
+       .floor_freq_reg_addr = IOSF_CPU_POWER_BUDGET_CTL_TNG,
        .check_unit = rapl_check_unit_atom,
        .set_floor_freq = set_floor_freq_atom,
        .compute_time_window = rapl_compute_time_window_atom,
 };
 
+static const struct rapl_defaults rapl_defaults_ann = {
+       .floor_freq_reg_addr = 0,
+       .check_unit = rapl_check_unit_atom,
+       .set_floor_freq = NULL,
+       .compute_time_window = rapl_compute_time_window_atom,
+};
+
+static const struct rapl_defaults rapl_defaults_cht = {
+       .floor_freq_reg_addr = 0,
+       .check_unit = rapl_check_unit_atom,
+       .set_floor_freq = NULL,
+       .compute_time_window = rapl_compute_time_window_atom,
+};
+
 #define RAPL_CPU(_model, _ops) {                       \
                .vendor = X86_VENDOR_INTEL,             \
                .family = 6,                            \
@@ -1057,7 +1089,7 @@ static const struct rapl_defaults rapl_defaults_atom = {
 static const struct x86_cpu_id rapl_ids[] __initconst = {
        RAPL_CPU(0x2a, rapl_defaults_core),/* Sandy Bridge */
        RAPL_CPU(0x2d, rapl_defaults_core),/* Sandy Bridge EP */
-       RAPL_CPU(0x37, rapl_defaults_atom),/* Valleyview */
+       RAPL_CPU(0x37, rapl_defaults_byt),/* Valleyview */
        RAPL_CPU(0x3a, rapl_defaults_core),/* Ivy Bridge */
        RAPL_CPU(0x3c, rapl_defaults_core),/* Haswell */
        RAPL_CPU(0x3d, rapl_defaults_core),/* Broadwell */
@@ -1065,10 +1097,11 @@ static const struct x86_cpu_id rapl_ids[] __initconst = {
        RAPL_CPU(0x4f, rapl_defaults_hsw_server),/* Broadwell servers */
        RAPL_CPU(0x45, rapl_defaults_core),/* Haswell ULT */
        RAPL_CPU(0x4E, rapl_defaults_core),/* Skylake */
-       RAPL_CPU(0x4C, rapl_defaults_atom),/* Braswell */
-       RAPL_CPU(0x4A, rapl_defaults_atom),/* Tangier */
+       RAPL_CPU(0x4C, rapl_defaults_cht),/* Braswell/Cherryview */
+       RAPL_CPU(0x4A, rapl_defaults_tng),/* Tangier */
        RAPL_CPU(0x56, rapl_defaults_core),/* Future Xeon */
-       RAPL_CPU(0x5A, rapl_defaults_atom),/* Annidale */
+       RAPL_CPU(0x5A, rapl_defaults_ann),/* Annidale */
+       RAPL_CPU(0x57, rapl_defaults_hsw_server),/* Knights Landing */
        {}
 };
 MODULE_DEVICE_TABLE(x86cpu, rapl_ids);
index ba34c7d8904232de070b78d4565377c1fab75740..3a7769fe53dee4f3c4c5f0bdca3e829e0fa8d5b6 100644 (file)
@@ -223,13 +223,16 @@ void *pwm_get_chip_data(struct pwm_device *pwm)
 EXPORT_SYMBOL_GPL(pwm_get_chip_data);
 
 /**
- * pwmchip_add() - register a new PWM chip
+ * pwmchip_add_with_polarity() - register a new PWM chip
  * @chip: the PWM chip to add
+ * @polarity: initial polarity of PWM channels
  *
  * Register a new PWM chip. If chip->base < 0 then a dynamically assigned base
- * will be used.
+ * will be used. The initial polarity for all channels is specified by the
+ * @polarity parameter.
  */
-int pwmchip_add(struct pwm_chip *chip)
+int pwmchip_add_with_polarity(struct pwm_chip *chip,
+                             enum pwm_polarity polarity)
 {
        struct pwm_device *pwm;
        unsigned int i;
@@ -259,6 +262,7 @@ int pwmchip_add(struct pwm_chip *chip)
                pwm->chip = chip;
                pwm->pwm = chip->base + i;
                pwm->hwpwm = i;
+               pwm->polarity = polarity;
 
                radix_tree_insert(&pwm_tree, pwm->pwm, pwm);
        }
@@ -279,6 +283,19 @@ out:
        mutex_unlock(&pwm_lock);
        return ret;
 }
+EXPORT_SYMBOL_GPL(pwmchip_add_with_polarity);
+
+/**
+ * pwmchip_add() - register a new PWM chip
+ * @chip: the PWM chip to add
+ *
+ * Register a new PWM chip. If chip->base < 0 then a dynamically assigned base
+ * will be used. The initial polarity for all channels is normal.
+ */
+int pwmchip_add(struct pwm_chip *chip)
+{
+       return pwmchip_add_with_polarity(chip, PWM_POLARITY_NORMAL);
+}
 EXPORT_SYMBOL_GPL(pwmchip_add);
 
 /**
@@ -585,6 +602,23 @@ void pwm_add_table(struct pwm_lookup *table, size_t num)
        mutex_unlock(&pwm_lookup_lock);
 }
 
+/**
+ * pwm_remove_table() - unregister PWM device consumers
+ * @table: array of consumers to unregister
+ * @num: number of consumers in table
+ */
+void pwm_remove_table(struct pwm_lookup *table, size_t num)
+{
+       mutex_lock(&pwm_lookup_lock);
+
+       while (num--) {
+               list_del(&table->list);
+               table++;
+       }
+
+       mutex_unlock(&pwm_lookup_lock);
+}
+
 /**
  * pwm_get() - look up and request a PWM device
  * @dev: device for PWM consumer
index d3c22de9ee47b8e7546557bc756e62a353c62556..a947c9095d9d6fc99d2b66be702e915eaa26014c 100644 (file)
@@ -8,9 +8,11 @@
  */
 
 #include <linux/clk.h>
+#include <linux/delay.h>
 #include <linux/err.h>
 #include <linux/io.h>
 #include <linux/module.h>
+#include <linux/mutex.h>
 #include <linux/of.h>
 #include <linux/of_device.h>
 #include <linux/platform_device.h>
@@ -21,6 +23,7 @@
 #define PWM_ENA                        0x04
 #define PWM_DIS                        0x08
 #define PWM_SR                 0x0C
+#define PWM_ISR                        0x1C
 /* Bit field in SR */
 #define PWM_SR_ALL_CH_ON       0x0F
 
@@ -60,6 +63,9 @@ struct atmel_pwm_chip {
        struct clk *clk;
        void __iomem *base;
 
+       unsigned int updated_pwms;
+       struct mutex isr_lock; /* ISR is cleared when read, ensure only one thread does that */
+
        void (*config)(struct pwm_chip *chip, struct pwm_device *pwm,
                       unsigned long dty, unsigned long prd);
 };
@@ -144,6 +150,10 @@ static int atmel_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
        val = (val & ~PWM_CMR_CPRE_MSK) | (pres & PWM_CMR_CPRE_MSK);
        atmel_pwm_ch_writel(atmel_pwm, pwm->hwpwm, PWM_CMR, val);
        atmel_pwm->config(chip, pwm, dty, prd);
+       mutex_lock(&atmel_pwm->isr_lock);
+       atmel_pwm->updated_pwms |= atmel_pwm_readl(atmel_pwm, PWM_ISR);
+       atmel_pwm->updated_pwms &= ~(1 << pwm->hwpwm);
+       mutex_unlock(&atmel_pwm->isr_lock);
 
        clk_disable(atmel_pwm->clk);
        return ret;
@@ -155,24 +165,25 @@ static void atmel_pwm_config_v1(struct pwm_chip *chip, struct pwm_device *pwm,
        struct atmel_pwm_chip *atmel_pwm = to_atmel_pwm_chip(chip);
        unsigned int val;
 
-       if (test_bit(PWMF_ENABLED, &pwm->flags)) {
-               /*
-                * If the PWM channel is enabled, using the update register,
-                * it needs to set bit 10 of CMR to 0
-                */
-               atmel_pwm_ch_writel(atmel_pwm, pwm->hwpwm, PWMV1_CUPD, dty);
 
-               val = atmel_pwm_ch_readl(atmel_pwm, pwm->hwpwm, PWM_CMR);
-               val &= ~PWM_CMR_UPD_CDTY;
-               atmel_pwm_ch_writel(atmel_pwm, pwm->hwpwm, PWM_CMR, val);
-       } else {
-               /*
-                * If the PWM channel is disabled, write value to duty and
-                * period registers directly.
-                */
-               atmel_pwm_ch_writel(atmel_pwm, pwm->hwpwm, PWMV1_CDTY, dty);
-               atmel_pwm_ch_writel(atmel_pwm, pwm->hwpwm, PWMV1_CPRD, prd);
-       }
+       atmel_pwm_ch_writel(atmel_pwm, pwm->hwpwm, PWMV1_CUPD, dty);
+
+       val = atmel_pwm_ch_readl(atmel_pwm, pwm->hwpwm, PWM_CMR);
+       val &= ~PWM_CMR_UPD_CDTY;
+       atmel_pwm_ch_writel(atmel_pwm, pwm->hwpwm, PWM_CMR, val);
+
+       /*
+        * If the PWM channel is enabled, only update CDTY by using the update
+        * register, it needs to set bit 10 of CMR to 0
+        */
+       if (test_bit(PWMF_ENABLED, &pwm->flags))
+               return;
+       /*
+        * If the PWM channel is disabled, write value to duty and period
+        * registers directly.
+        */
+       atmel_pwm_ch_writel(atmel_pwm, pwm->hwpwm, PWMV1_CDTY, dty);
+       atmel_pwm_ch_writel(atmel_pwm, pwm->hwpwm, PWMV1_CPRD, prd);
 }
 
 static void atmel_pwm_config_v2(struct pwm_chip *chip, struct pwm_device *pwm,
@@ -242,7 +253,22 @@ static int atmel_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
 static void atmel_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
 {
        struct atmel_pwm_chip *atmel_pwm = to_atmel_pwm_chip(chip);
+       unsigned long timeout = jiffies + 2 * HZ;
+
+       /*
+        * Wait for at least a complete period to have passed before disabling a
+        * channel to be sure that CDTY has been updated
+        */
+       mutex_lock(&atmel_pwm->isr_lock);
+       atmel_pwm->updated_pwms |= atmel_pwm_readl(atmel_pwm, PWM_ISR);
+
+       while (!(atmel_pwm->updated_pwms & (1 << pwm->hwpwm)) &&
+              time_before(jiffies, timeout)) {
+               usleep_range(10, 100);
+               atmel_pwm->updated_pwms |= atmel_pwm_readl(atmel_pwm, PWM_ISR);
+       }
 
+       mutex_unlock(&atmel_pwm->isr_lock);
        atmel_pwm_writel(atmel_pwm, PWM_DIS, 1 << pwm->hwpwm);
 
        clk_disable(atmel_pwm->clk);
@@ -357,6 +383,8 @@ static int atmel_pwm_probe(struct platform_device *pdev)
        atmel_pwm->chip.npwm = 4;
        atmel_pwm->chip.can_sleep = true;
        atmel_pwm->config = data->config;
+       atmel_pwm->updated_pwms = 0;
+       mutex_init(&atmel_pwm->isr_lock);
 
        ret = pwmchip_add(&atmel_pwm->chip);
        if (ret < 0) {
@@ -378,6 +406,7 @@ static int atmel_pwm_remove(struct platform_device *pdev)
        struct atmel_pwm_chip *atmel_pwm = platform_get_drvdata(pdev);
 
        clk_unprepare(atmel_pwm->clk);
+       mutex_destroy(&atmel_pwm->isr_lock);
 
        return pwmchip_remove(&atmel_pwm->chip);
 }
index 02bc048892a9f3cf4b50bb1499f6b9715311c74a..7af8fea2dc5b3e7f5f639fd03c6b1246f7fa4d60 100644 (file)
@@ -266,18 +266,15 @@ static int kona_pwmc_probe(struct platform_device *pdev)
                return ret;
        }
 
-       /* Set smooth mode, push/pull, and normal polarity for all channels */
-       for (chan = 0; chan < kp->chip.npwm; chan++) {
-               value |= (1 << PWM_CONTROL_SMOOTH_SHIFT(chan));
+       /* Set push/pull for all channels */
+       for (chan = 0; chan < kp->chip.npwm; chan++)
                value |= (1 << PWM_CONTROL_TYPE_SHIFT(chan));
-               value |= (1 << PWM_CONTROL_POLARITY_SHIFT(chan));
-       }
 
        writel(value, kp->base + PWM_CONTROL_OFFSET);
 
        clk_disable_unprepare(kp->clk);
 
-       ret = pwmchip_add(&kp->chip);
+       ret = pwmchip_add_with_polarity(&kp->chip, PWM_POLARITY_INVERSED);
        if (ret < 0)
                dev_err(&pdev->dev, "failed to add PWM chip: %d\n", ret);
 
index cf20d2beacdde44f74ff156360ab979419062741..45042c1b20463eeb1750c166731992d17a75caf6 100644 (file)
@@ -44,8 +44,10 @@ static void pwm_lpss_remove_pci(struct pci_dev *pdev)
 }
 
 static const struct pci_device_id pwm_lpss_pci_ids[] = {
+       { PCI_VDEVICE(INTEL, 0x0ac8), (unsigned long)&pwm_lpss_bsw_info},
        { PCI_VDEVICE(INTEL, 0x0f08), (unsigned long)&pwm_lpss_byt_info},
        { PCI_VDEVICE(INTEL, 0x0f09), (unsigned long)&pwm_lpss_byt_info},
+       { PCI_VDEVICE(INTEL, 0x1ac8), (unsigned long)&pwm_lpss_bsw_info},
        { PCI_VDEVICE(INTEL, 0x2288), (unsigned long)&pwm_lpss_bsw_info},
        { PCI_VDEVICE(INTEL, 0x2289), (unsigned long)&pwm_lpss_bsw_info},
        { },
index ff201e1b92197e1d354cacc3239a045b7b88e77c..ada2d326dc3e6117f9543f5283ac09c90bcb22ad 100644 (file)
@@ -456,6 +456,7 @@ static const struct of_device_id samsung_pwm_matches[] = {
        { .compatible = "samsung,exynos4210-pwm", .data = &s5p64x0_variant },
        {},
 };
+MODULE_DEVICE_TABLE(of, samsung_pwm_matches);
 
 static int pwm_samsung_parse_dt(struct samsung_pwm_chip *chip)
 {
index 47a1b2ea76c45a0a02d671bc4d7bee042ec1818c..d6a126c17c032ac0352a3fac42e4a4400937e07b 100644 (file)
@@ -83,7 +83,7 @@ static u16 rio_destid_alloc(struct rio_net *net)
  * @destid: destID to reserve
  *
  * Tries to reserve the specified destID.
- * Returns 0 if successfull.
+ * Returns 0 if successful.
  */
 static int rio_destid_reserve(struct rio_net *net, u16 destid)
 {
index c3d15427adc7b2052191884aa58d3a069410016e..b100a63ff3b3f6398b649129559f8228f66265c2 100644 (file)
@@ -404,7 +404,7 @@ static int pm8607_regulator_probe(struct platform_device *pdev)
        return 0;
 }
 
-static struct platform_device_id pm8607_regulator_driver_ids[] = {
+static const struct platform_device_id pm8607_regulator_driver_ids[] = {
        {
                .name   = "88pm860x-regulator",
                .driver_data    = 0,
index a6f116aa523532b56194407438419c5bd5d238d9..bef3bde6971b0425d2632b54d2f1bb447c643ce4 100644 (file)
@@ -178,6 +178,16 @@ config REGULATOR_DA9055
          This driver can also be built as a module. If so, the module
          will be called da9055-regulator.
 
+config REGULATOR_DA9062
+       tristate "Dialog Semiconductor DA9062 regulators"
+       depends on MFD_DA9062
+       help
+         Say y here to support the BUCKs and LDOs regulators found on
+         DA9062 PMICs.
+
+         This driver can also be built as a module. If so, the module
+         will be called da9062-regulator.
+
 config REGULATOR_DA9063
        tristate "Dialog Semiconductor DA9063 regulators"
        depends on MFD_DA9063
@@ -233,7 +243,7 @@ config REGULATOR_FAN53555
 
 config REGULATOR_GPIO
        tristate "GPIO regulator support"
-       depends on GPIOLIB
+       depends on GPIOLIB || COMPILE_TEST
        help
          This driver provides support for regulators that can be
          controlled via gpios.
@@ -512,6 +522,17 @@ config REGULATOR_QCOM_RPM
          Qualcomm RPM as a module. The module will be named
          "qcom_rpm-regulator".
 
+config REGULATOR_QCOM_SPMI
+       tristate "Qualcomm SPMI regulator driver"
+       depends on SPMI || COMPILE_TEST
+       help
+         If you say yes to this option, support will be included for the
+         regulators found in Qualcomm SPMI PMICs.
+
+         Say M here if you want to include support for the regulators on the
+         Qualcomm SPMI PMICs as a module. The module will be named
+         "qcom_spmi-regulator".
+
 config REGULATOR_RC5T583
        tristate "RICOH RC5T583 Power regulators"
        depends on MFD_RC5T583
index 2c4da15e1545a71076616fbd94f3648ad453473a..91bf76267404c9b8a9260dba5e0a0c8a793247e6 100644 (file)
@@ -25,6 +25,7 @@ obj-$(CONFIG_REGULATOR_BCM590XX) += bcm590xx-regulator.o
 obj-$(CONFIG_REGULATOR_DA903X) += da903x.o
 obj-$(CONFIG_REGULATOR_DA9052) += da9052-regulator.o
 obj-$(CONFIG_REGULATOR_DA9055) += da9055-regulator.o
+obj-$(CONFIG_REGULATOR_DA9062) += da9062-regulator.o
 obj-$(CONFIG_REGULATOR_DA9063) += da9063-regulator.o
 obj-$(CONFIG_REGULATOR_DA9210) += da9210-regulator.o
 obj-$(CONFIG_REGULATOR_DA9211) += da9211-regulator.o
@@ -61,6 +62,7 @@ obj-$(CONFIG_REGULATOR_MC13892) += mc13892-regulator.o
 obj-$(CONFIG_REGULATOR_MC13XXX_CORE) +=  mc13xxx-regulator-core.o
 obj-$(CONFIG_REGULATOR_MT6397) += mt6397-regulator.o
 obj-$(CONFIG_REGULATOR_QCOM_RPM) += qcom_rpm-regulator.o
+obj-$(CONFIG_REGULATOR_QCOM_SPMI) += qcom_spmi-regulator.o
 obj-$(CONFIG_REGULATOR_PALMAS) += palmas-regulator.o
 obj-$(CONFIG_REGULATOR_PFUZE100) += pfuze100-regulator.o
 obj-$(CONFIG_REGULATOR_PWM) += pwm-regulator.o
index a1d07d347c20e30af065d204d94a314d18c408e3..90941632efa9b1868033cb256820137f8c406542 100644 (file)
@@ -178,6 +178,16 @@ static const struct regulator_init_data arizona_ldo1_default = {
        .num_consumer_supplies = 1,
 };
 
+static const struct regulator_init_data arizona_ldo1_wm5110 = {
+       .constraints = {
+               .min_uV = 1175000,
+               .max_uV = 1200000,
+               .valid_ops_mask = REGULATOR_CHANGE_STATUS |
+                                 REGULATOR_CHANGE_VOLTAGE,
+       },
+       .num_consumer_supplies = 1,
+};
+
 static int arizona_ldo1_of_get_pdata(struct arizona *arizona,
                                     struct regulator_config *config,
                                     const struct regulator_desc *desc)
@@ -243,6 +253,11 @@ static int arizona_ldo1_probe(struct platform_device *pdev)
                desc = &arizona_ldo1_hc;
                ldo1->init_data = arizona_ldo1_dvfs;
                break;
+       case WM5110:
+       case WM8280:
+               desc = &arizona_ldo1;
+               ldo1->init_data = arizona_ldo1_wm5110;
+               break;
        default:
                desc = &arizona_ldo1;
                ldo1->init_data = arizona_ldo1_default;
index e4331f5e5d7d065e25fa835201359696c01bb2e9..646829132b5926ffeaf5e14a82d6296f1f32f159 100644 (file)
 #define AXP20X_IO_ENABLED              0x03
 #define AXP20X_IO_DISABLED             0x07
 
+#define AXP22X_IO_ENABLED              0x04
+#define AXP22X_IO_DISABLED             0x03
+
 #define AXP20X_WORKMODE_DCDC2_MASK     BIT(2)
 #define AXP20X_WORKMODE_DCDC3_MASK     BIT(1)
+#define AXP22X_WORKMODE_DCDCX_MASK(x)  BIT(x)
 
 #define AXP20X_FREQ_DCDC_MASK          0x0f
 
-#define AXP20X_DESC_IO(_id, _match, _supply, _min, _max, _step, _vreg, _vmask, \
-                      _ereg, _emask, _enable_val, _disable_val)                \
-       [AXP20X_##_id] = {                                                      \
+#define AXP_DESC_IO(_family, _id, _match, _supply, _min, _max, _step, _vreg,   \
+                   _vmask, _ereg, _emask, _enable_val, _disable_val)           \
+       [_family##_##_id] = {                                                   \
                .name           = #_id,                                         \
                .supply_name    = (_supply),                                    \
                .of_match       = of_match_ptr(_match),                         \
                .regulators_node = of_match_ptr("regulators"),                  \
                .type           = REGULATOR_VOLTAGE,                            \
-               .id             = AXP20X_##_id,                                 \
+               .id             = _family##_##_id,                              \
                .n_voltages     = (((_max) - (_min)) / (_step) + 1),            \
                .owner          = THIS_MODULE,                                  \
                .min_uV         = (_min) * 1000,                                \
                .ops            = &axp20x_ops,                                  \
        }
 
-#define AXP20X_DESC(_id, _match, _supply, _min, _max, _step, _vreg, _vmask,    \
-                   _ereg, _emask)                                              \
-       [AXP20X_##_id] = {                                                      \
+#define AXP_DESC(_family, _id, _match, _supply, _min, _max, _step, _vreg,      \
+                _vmask, _ereg, _emask)                                         \
+       [_family##_##_id] = {                                                   \
                .name           = #_id,                                         \
                .supply_name    = (_supply),                                    \
                .of_match       = of_match_ptr(_match),                         \
                .regulators_node = of_match_ptr("regulators"),                  \
                .type           = REGULATOR_VOLTAGE,                            \
-               .id             = AXP20X_##_id,                                 \
+               .id             = _family##_##_id,                              \
                .n_voltages     = (((_max) - (_min)) / (_step) + 1),            \
                .owner          = THIS_MODULE,                                  \
                .min_uV         = (_min) * 1000,                                \
                .ops            = &axp20x_ops,                                  \
        }
 
-#define AXP20X_DESC_FIXED(_id, _match, _supply, _volt)                         \
-       [AXP20X_##_id] = {                                                      \
+#define AXP_DESC_SW(_family, _id, _match, _supply, _min, _max, _step, _vreg,   \
+                   _vmask, _ereg, _emask)                                      \
+       [_family##_##_id] = {                                                   \
                .name           = #_id,                                         \
                .supply_name    = (_supply),                                    \
                .of_match       = of_match_ptr(_match),                         \
                .regulators_node = of_match_ptr("regulators"),                  \
                .type           = REGULATOR_VOLTAGE,                            \
-               .id             = AXP20X_##_id,                                 \
+               .id             = _family##_##_id,                              \
+               .n_voltages     = (((_max) - (_min)) / (_step) + 1),            \
+               .owner          = THIS_MODULE,                                  \
+               .min_uV         = (_min) * 1000,                                \
+               .uV_step        = (_step) * 1000,                               \
+               .vsel_reg       = (_vreg),                                      \
+               .vsel_mask      = (_vmask),                                     \
+               .enable_reg     = (_ereg),                                      \
+               .enable_mask    = (_emask),                                     \
+               .ops            = &axp20x_ops_sw,                               \
+       }
+
+#define AXP_DESC_FIXED(_family, _id, _match, _supply, _volt)                   \
+       [_family##_##_id] = {                                                   \
+               .name           = #_id,                                         \
+               .supply_name    = (_supply),                                    \
+               .of_match       = of_match_ptr(_match),                         \
+               .regulators_node = of_match_ptr("regulators"),                  \
+               .type           = REGULATOR_VOLTAGE,                            \
+               .id             = _family##_##_id,                              \
                .n_voltages     = 1,                                            \
                .owner          = THIS_MODULE,                                  \
                .min_uV         = (_volt) * 1000,                               \
                .ops            = &axp20x_ops_fixed                             \
        }
 
-#define AXP20X_DESC_TABLE(_id, _match, _supply, _table, _vreg, _vmask, _ereg,  \
-                         _emask)                                               \
-       [AXP20X_##_id] = {                                                      \
+#define AXP_DESC_TABLE(_family, _id, _match, _supply, _table, _vreg, _vmask,   \
+                      _ereg, _emask)                                           \
+       [_family##_##_id] = {                                                   \
                .name           = #_id,                                         \
                .supply_name    = (_supply),                                    \
                .of_match       = of_match_ptr(_match),                         \
                .regulators_node = of_match_ptr("regulators"),                  \
                .type           = REGULATOR_VOLTAGE,                            \
-               .id             = AXP20X_##_id,                                 \
+               .id             = _family##_##_id,                              \
                .n_voltages     = ARRAY_SIZE(_table),                           \
                .owner          = THIS_MODULE,                                  \
                .vsel_reg       = (_vreg),                                      \
@@ -135,38 +159,118 @@ static struct regulator_ops axp20x_ops = {
        .is_enabled             = regulator_is_enabled_regmap,
 };
 
+static struct regulator_ops axp20x_ops_sw = {
+       .get_voltage_sel        = regulator_get_voltage_sel_regmap,
+       .list_voltage           = regulator_list_voltage_linear,
+       .enable                 = regulator_enable_regmap,
+       .disable                = regulator_disable_regmap,
+       .is_enabled             = regulator_is_enabled_regmap,
+};
+
 static const struct regulator_desc axp20x_regulators[] = {
-       AXP20X_DESC(DCDC2, "dcdc2", "vin2", 700, 2275, 25, AXP20X_DCDC2_V_OUT,
-                   0x3f, AXP20X_PWR_OUT_CTRL, 0x10),
-       AXP20X_DESC(DCDC3, "dcdc3", "vin3", 700, 3500, 25, AXP20X_DCDC3_V_OUT,
-                   0x7f, AXP20X_PWR_OUT_CTRL, 0x02),
-       AXP20X_DESC_FIXED(LDO1, "ldo1", "acin", 1300),
-       AXP20X_DESC(LDO2, "ldo2", "ldo24in", 1800, 3300, 100,
-                   AXP20X_LDO24_V_OUT, 0xf0, AXP20X_PWR_OUT_CTRL, 0x04),
-       AXP20X_DESC(LDO3, "ldo3", "ldo3in", 700, 3500, 25, AXP20X_LDO3_V_OUT,
-                   0x7f, AXP20X_PWR_OUT_CTRL, 0x40),
-       AXP20X_DESC_TABLE(LDO4, "ldo4", "ldo24in", axp20x_ldo4_data,
-                         AXP20X_LDO24_V_OUT, 0x0f, AXP20X_PWR_OUT_CTRL, 0x08),
-       AXP20X_DESC_IO(LDO5, "ldo5", "ldo5in", 1800, 3300, 100,
-                      AXP20X_LDO5_V_OUT, 0xf0, AXP20X_GPIO0_CTRL, 0x07,
-                      AXP20X_IO_ENABLED, AXP20X_IO_DISABLED),
+       AXP_DESC(AXP20X, DCDC2, "dcdc2", "vin2", 700, 2275, 25,
+                AXP20X_DCDC2_V_OUT, 0x3f, AXP20X_PWR_OUT_CTRL, 0x10),
+       AXP_DESC(AXP20X, DCDC3, "dcdc3", "vin3", 700, 3500, 25,
+                AXP20X_DCDC3_V_OUT, 0x7f, AXP20X_PWR_OUT_CTRL, 0x02),
+       AXP_DESC_FIXED(AXP20X, LDO1, "ldo1", "acin", 1300),
+       AXP_DESC(AXP20X, LDO2, "ldo2", "ldo24in", 1800, 3300, 100,
+                AXP20X_LDO24_V_OUT, 0xf0, AXP20X_PWR_OUT_CTRL, 0x04),
+       AXP_DESC(AXP20X, LDO3, "ldo3", "ldo3in", 700, 3500, 25,
+                AXP20X_LDO3_V_OUT, 0x7f, AXP20X_PWR_OUT_CTRL, 0x40),
+       AXP_DESC_TABLE(AXP20X, LDO4, "ldo4", "ldo24in", axp20x_ldo4_data,
+                      AXP20X_LDO24_V_OUT, 0x0f, AXP20X_PWR_OUT_CTRL, 0x08),
+       AXP_DESC_IO(AXP20X, LDO5, "ldo5", "ldo5in", 1800, 3300, 100,
+                   AXP20X_LDO5_V_OUT, 0xf0, AXP20X_GPIO0_CTRL, 0x07,
+                   AXP20X_IO_ENABLED, AXP20X_IO_DISABLED),
+};
+
+static const struct regulator_desc axp22x_regulators[] = {
+       AXP_DESC(AXP22X, DCDC1, "dcdc1", "vin1", 1600, 3400, 100,
+                AXP22X_DCDC1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL1, BIT(1)),
+       AXP_DESC(AXP22X, DCDC2, "dcdc2", "vin2", 600, 1540, 20,
+                AXP22X_DCDC2_V_OUT, 0x3f, AXP22X_PWR_OUT_CTRL1, BIT(2)),
+       AXP_DESC(AXP22X, DCDC3, "dcdc3", "vin3", 600, 1860, 20,
+                AXP22X_DCDC3_V_OUT, 0x3f, AXP22X_PWR_OUT_CTRL1, BIT(3)),
+       AXP_DESC(AXP22X, DCDC4, "dcdc4", "vin4", 600, 1540, 20,
+                AXP22X_DCDC4_V_OUT, 0x3f, AXP22X_PWR_OUT_CTRL1, BIT(3)),
+       AXP_DESC(AXP22X, DCDC5, "dcdc5", "vin5", 1000, 2550, 50,
+                AXP22X_DCDC5_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL1, BIT(4)),
+       /* secondary switchable output of DCDC1 */
+       AXP_DESC_SW(AXP22X, DC1SW, "dc1sw", "dcdc1", 1600, 3400, 100,
+                   AXP22X_DCDC1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(7)),
+       /* LDO regulator internally chained to DCDC5 */
+       AXP_DESC(AXP22X, DC5LDO, "dc5ldo", "dcdc5", 700, 1400, 100,
+                AXP22X_DC5LDO_V_OUT, 0x7, AXP22X_PWR_OUT_CTRL1, BIT(0)),
+       AXP_DESC(AXP22X, ALDO1, "aldo1", "aldoin", 700, 3300, 100,
+                AXP22X_ALDO1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL1, BIT(6)),
+       AXP_DESC(AXP22X, ALDO2, "aldo2", "aldoin", 700, 3300, 100,
+                AXP22X_ALDO2_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL1, BIT(7)),
+       AXP_DESC(AXP22X, ALDO3, "aldo3", "aldoin", 700, 3300, 100,
+                AXP22X_ALDO3_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL3, BIT(7)),
+       AXP_DESC(AXP22X, DLDO1, "dldo1", "dldoin", 700, 3300, 100,
+                AXP22X_DLDO1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(3)),
+       AXP_DESC(AXP22X, DLDO2, "dldo2", "dldoin", 700, 3300, 100,
+                AXP22X_DLDO2_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(4)),
+       AXP_DESC(AXP22X, DLDO3, "dldo3", "dldoin", 700, 3300, 100,
+                AXP22X_DLDO3_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(5)),
+       AXP_DESC(AXP22X, DLDO4, "dldo4", "dldoin", 700, 3300, 100,
+                AXP22X_DLDO4_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(6)),
+       AXP_DESC(AXP22X, ELDO1, "eldo1", "eldoin", 700, 3300, 100,
+                AXP22X_ELDO1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(0)),
+       AXP_DESC(AXP22X, ELDO2, "eldo2", "eldoin", 700, 3300, 100,
+                AXP22X_ELDO2_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(1)),
+       AXP_DESC(AXP22X, ELDO3, "eldo3", "eldoin", 700, 3300, 100,
+                AXP22X_ELDO3_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(2)),
+       AXP_DESC_IO(AXP22X, LDO_IO0, "ldo_io0", "ips", 1800, 3300, 100,
+                   AXP22X_LDO_IO0_V_OUT, 0x1f, AXP20X_GPIO0_CTRL, 0x07,
+                   AXP22X_IO_ENABLED, AXP22X_IO_DISABLED),
+       AXP_DESC_IO(AXP22X, LDO_IO1, "ldo_io1", "ips", 1800, 3300, 100,
+                   AXP22X_LDO_IO1_V_OUT, 0x1f, AXP20X_GPIO1_CTRL, 0x07,
+                   AXP22X_IO_ENABLED, AXP22X_IO_DISABLED),
+       AXP_DESC_FIXED(AXP22X, RTC_LDO, "rtc_ldo", "ips", 3000),
 };
 
 static int axp20x_set_dcdc_freq(struct platform_device *pdev, u32 dcdcfreq)
 {
        struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
+       u32 min, max, def, step;
+
+       switch (axp20x->variant) {
+       case AXP202_ID:
+       case AXP209_ID:
+               min = 750;
+               max = 1875;
+               def = 1500;
+               step = 75;
+               break;
+       case AXP221_ID:
+               min = 1800;
+               max = 4050;
+               def = 3000;
+               step = 150;
+               break;
+       default:
+               dev_err(&pdev->dev,
+                       "Setting DCDC frequency for unsupported AXP variant\n");
+               return -EINVAL;
+       }
+
+       if (dcdcfreq == 0)
+               dcdcfreq = def;
 
-       if (dcdcfreq < 750) {
-               dcdcfreq = 750;
-               dev_warn(&pdev->dev, "DCDC frequency too low. Set to 750kHz\n");
+       if (dcdcfreq < min) {
+               dcdcfreq = min;
+               dev_warn(&pdev->dev, "DCDC frequency too low. Set to %ukHz\n",
+                        min);
        }
 
-       if (dcdcfreq > 1875) {
-               dcdcfreq = 1875;
-               dev_warn(&pdev->dev, "DCDC frequency too high. Set to 1875kHz\n");
+       if (dcdcfreq > max) {
+               dcdcfreq = max;
+               dev_warn(&pdev->dev, "DCDC frequency too high. Set to %ukHz\n",
+                        max);
        }
 
-       dcdcfreq = (dcdcfreq - 750) / 75;
+       dcdcfreq = (dcdcfreq - min) / step;
 
        return regmap_update_bits(axp20x->regmap, AXP20X_DCDC_FREQ,
                                  AXP20X_FREQ_DCDC_MASK, dcdcfreq);
@@ -176,7 +280,7 @@ static int axp20x_regulator_parse_dt(struct platform_device *pdev)
 {
        struct device_node *np, *regulators;
        int ret;
-       u32 dcdcfreq;
+       u32 dcdcfreq = 0;
 
        np = of_node_get(pdev->dev.parent->of_node);
        if (!np)
@@ -186,7 +290,6 @@ static int axp20x_regulator_parse_dt(struct platform_device *pdev)
        if (!regulators) {
                dev_warn(&pdev->dev, "regulators node not found\n");
        } else {
-               dcdcfreq = 1500;
                of_property_read_u32(regulators, "x-powers,dcdc-freq", &dcdcfreq);
                ret = axp20x_set_dcdc_freq(pdev, dcdcfreq);
                if (ret < 0) {
@@ -202,15 +305,35 @@ static int axp20x_regulator_parse_dt(struct platform_device *pdev)
 
 static int axp20x_set_dcdc_workmode(struct regulator_dev *rdev, int id, u32 workmode)
 {
-       unsigned int mask = AXP20X_WORKMODE_DCDC2_MASK;
+       struct axp20x_dev *axp20x = rdev_get_drvdata(rdev);
+       unsigned int mask;
 
-       if ((id != AXP20X_DCDC2) && (id != AXP20X_DCDC3))
-               return -EINVAL;
+       switch (axp20x->variant) {
+       case AXP202_ID:
+       case AXP209_ID:
+               if ((id != AXP20X_DCDC2) && (id != AXP20X_DCDC3))
+                       return -EINVAL;
+
+               mask = AXP20X_WORKMODE_DCDC2_MASK;
+               if (id == AXP20X_DCDC3)
+                       mask = AXP20X_WORKMODE_DCDC3_MASK;
+
+               workmode <<= ffs(mask) - 1;
+               break;
 
-       if (id == AXP20X_DCDC3)
-               mask = AXP20X_WORKMODE_DCDC3_MASK;
+       case AXP221_ID:
+               if (id < AXP22X_DCDC1 || id > AXP22X_DCDC5)
+                       return -EINVAL;
 
-       workmode <<= ffs(mask) - 1;
+               mask = AXP22X_WORKMODE_DCDCX_MASK(id - AXP22X_DCDC1);
+               workmode <<= id - AXP22X_DCDC1;
+               break;
+
+       default:
+               /* should not happen */
+               WARN_ON(1);
+               return -EINVAL;
+       }
 
        return regmap_update_bits(rdev->regmap, AXP20X_DCDC_MODE, mask, workmode);
 }
@@ -219,22 +342,40 @@ static int axp20x_regulator_probe(struct platform_device *pdev)
 {
        struct regulator_dev *rdev;
        struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
+       const struct regulator_desc *regulators;
        struct regulator_config config = {
                .dev = pdev->dev.parent,
                .regmap = axp20x->regmap,
+               .driver_data = axp20x,
        };
-       int ret, i;
+       int ret, i, nregulators;
        u32 workmode;
 
+       switch (axp20x->variant) {
+       case AXP202_ID:
+       case AXP209_ID:
+               regulators = axp20x_regulators;
+               nregulators = AXP20X_REG_ID_MAX;
+               break;
+       case AXP221_ID:
+               regulators = axp22x_regulators;
+               nregulators = AXP22X_REG_ID_MAX;
+               break;
+       default:
+               dev_err(&pdev->dev, "Unsupported AXP variant: %ld\n",
+                       axp20x->variant);
+               return -EINVAL;
+       }
+
        /* This only sets the dcdc freq. Ignore any errors */
        axp20x_regulator_parse_dt(pdev);
 
-       for (i = 0; i < AXP20X_REG_ID_MAX; i++) {
-               rdev = devm_regulator_register(&pdev->dev, &axp20x_regulators[i],
+       for (i = 0; i < nregulators; i++) {
+               rdev = devm_regulator_register(&pdev->dev, &regulators[i],
                                               &config);
                if (IS_ERR(rdev)) {
                        dev_err(&pdev->dev, "Failed to register %s\n",
-                               axp20x_regulators[i].name);
+                               regulators[i].name);
 
                        return PTR_ERR(rdev);
                }
@@ -245,7 +386,7 @@ static int axp20x_regulator_probe(struct platform_device *pdev)
                if (!ret) {
                        if (axp20x_set_dcdc_workmode(rdev, i, workmode))
                                dev_err(&pdev->dev, "Failed to set workmode on %s\n",
-                                       axp20x_regulators[i].name);
+                                       rdev->desc->name);
                }
        }
 
index 443eaab933fcfe680d5e402f72bbe7c55c3ae391..c9f72019bd689afbb4e51528932689dc097b191b 100644 (file)
@@ -678,6 +678,8 @@ static int drms_uA_update(struct regulator_dev *rdev)
        list_for_each_entry(sibling, &rdev->consumer_list, list)
                current_uA += sibling->uA_load;
 
+       current_uA += rdev->constraints->system_load;
+
        if (rdev->desc->ops->set_load) {
                /* set the optimum mode for our new total regulator load */
                err = rdev->desc->ops->set_load(rdev, current_uA);
@@ -779,59 +781,64 @@ static int suspend_prepare(struct regulator_dev *rdev, suspend_state_t state)
 static void print_constraints(struct regulator_dev *rdev)
 {
        struct regulation_constraints *constraints = rdev->constraints;
-       char buf[80] = "";
+       char buf[160] = "";
+       size_t len = sizeof(buf) - 1;
        int count = 0;
        int ret;
 
        if (constraints->min_uV && constraints->max_uV) {
                if (constraints->min_uV == constraints->max_uV)
-                       count += sprintf(buf + count, "%d mV ",
-                                        constraints->min_uV / 1000);
+                       count += scnprintf(buf + count, len - count, "%d mV ",
+                                          constraints->min_uV / 1000);
                else
-                       count += sprintf(buf + count, "%d <--> %d mV ",
-                                        constraints->min_uV / 1000,
-                                        constraints->max_uV / 1000);
+                       count += scnprintf(buf + count, len - count,
+                                          "%d <--> %d mV ",
+                                          constraints->min_uV / 1000,
+                                          constraints->max_uV / 1000);
        }
 
        if (!constraints->min_uV ||
            constraints->min_uV != constraints->max_uV) {
                ret = _regulator_get_voltage(rdev);
                if (ret > 0)
-                       count += sprintf(buf + count, "at %d mV ", ret / 1000);
+                       count += scnprintf(buf + count, len - count,
+                                          "at %d mV ", ret / 1000);
        }
 
        if (constraints->uV_offset)
-               count += sprintf(buf, "%dmV offset ",
-                                constraints->uV_offset / 1000);
+               count += scnprintf(buf + count, len - count, "%dmV offset ",
+                                  constraints->uV_offset / 1000);
 
        if (constraints->min_uA && constraints->max_uA) {
                if (constraints->min_uA == constraints->max_uA)
-                       count += sprintf(buf + count, "%d mA ",
-                                        constraints->min_uA / 1000);
+                       count += scnprintf(buf + count, len - count, "%d mA ",
+                                          constraints->min_uA / 1000);
                else
-                       count += sprintf(buf + count, "%d <--> %d mA ",
-                                        constraints->min_uA / 1000,
-                                        constraints->max_uA / 1000);
+                       count += scnprintf(buf + count, len - count,
+                                          "%d <--> %d mA ",
+                                          constraints->min_uA / 1000,
+                                          constraints->max_uA / 1000);
        }
 
        if (!constraints->min_uA ||
            constraints->min_uA != constraints->max_uA) {
                ret = _regulator_get_current_limit(rdev);
                if (ret > 0)
-                       count += sprintf(buf + count, "at %d mA ", ret / 1000);
+                       count += scnprintf(buf + count, len - count,
+                                          "at %d mA ", ret / 1000);
        }
 
        if (constraints->valid_modes_mask & REGULATOR_MODE_FAST)
-               count += sprintf(buf + count, "fast ");
+               count += scnprintf(buf + count, len - count, "fast ");
        if (constraints->valid_modes_mask & REGULATOR_MODE_NORMAL)
-               count += sprintf(buf + count, "normal ");
+               count += scnprintf(buf + count, len - count, "normal ");
        if (constraints->valid_modes_mask & REGULATOR_MODE_IDLE)
-               count += sprintf(buf + count, "idle ");
+               count += scnprintf(buf + count, len - count, "idle ");
        if (constraints->valid_modes_mask & REGULATOR_MODE_STANDBY)
-               count += sprintf(buf + count, "standby");
+               count += scnprintf(buf + count, len - count, "standby");
 
        if (!count)
-               sprintf(buf, "no parameters");
+               scnprintf(buf, len, "no parameters");
 
        rdev_dbg(rdev, "%s\n", buf);
 
@@ -1006,6 +1013,15 @@ static int set_machine_constraints(struct regulator_dev *rdev,
        if (ret != 0)
                goto out;
 
+       if (rdev->constraints->ilim_uA && ops->set_input_current_limit) {
+               ret = ops->set_input_current_limit(rdev,
+                                                  rdev->constraints->ilim_uA);
+               if (ret < 0) {
+                       rdev_err(rdev, "failed to set input limit\n");
+                       goto out;
+               }
+       }
+
        /* do we need to setup our suspend state */
        if (rdev->constraints->initial_state) {
                ret = suspend_prepare(rdev, rdev->constraints->initial_state);
@@ -1049,6 +1065,22 @@ static int set_machine_constraints(struct regulator_dev *rdev,
                }
        }
 
+       if (rdev->constraints->pull_down && ops->set_pull_down) {
+               ret = ops->set_pull_down(rdev);
+               if (ret < 0) {
+                       rdev_err(rdev, "failed to set pull down\n");
+                       goto out;
+               }
+       }
+
+       if (rdev->constraints->soft_start && ops->set_soft_start) {
+               ret = ops->set_soft_start(rdev);
+               if (ret < 0) {
+                       rdev_err(rdev, "failed to set soft start\n");
+                       goto out;
+               }
+       }
+
        print_constraints(rdev);
        return 0;
 out:
@@ -1192,10 +1224,10 @@ static struct regulator *create_regulator(struct regulator_dev *rdev,
                if (regulator->supply_name == NULL)
                        goto overflow_err;
 
-               err = sysfs_create_link(&rdev->dev.kobj, &dev->kobj,
+               err = sysfs_create_link_nowarn(&rdev->dev.kobj, &dev->kobj,
                                        buf);
                if (err) {
-                       rdev_warn(rdev, "could not add device link %s err %d\n",
+                       rdev_dbg(rdev, "could not add device link %s err %d\n",
                                  dev->kobj.name, err);
                        /* non-fatal */
                }
diff --git a/drivers/regulator/da9062-regulator.c b/drivers/regulator/da9062-regulator.c
new file mode 100644 (file)
index 0000000..dd76da0
--- /dev/null
@@ -0,0 +1,842 @@
+/*
+ * da9062-regulator.c - REGULATOR device driver for DA9062
+ * Copyright (C) 2015  Dialog Semiconductor Ltd.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/mfd/da9062/core.h>
+#include <linux/mfd/da9062/registers.h>
+
+/* Regulator IDs */
+enum {
+       DA9062_ID_BUCK1,
+       DA9062_ID_BUCK2,
+       DA9062_ID_BUCK3,
+       DA9062_ID_BUCK4,
+       DA9062_ID_LDO1,
+       DA9062_ID_LDO2,
+       DA9062_ID_LDO3,
+       DA9062_ID_LDO4,
+       DA9062_MAX_REGULATORS,
+};
+
+/* Regulator capabilities and registers description */
+struct da9062_regulator_info {
+       struct regulator_desc desc;
+       /* Current limiting */
+       unsigned int n_current_limits;
+       const int *current_limits;
+       /* Main register fields */
+       struct reg_field mode;
+       struct reg_field suspend;
+       struct reg_field sleep;
+       struct reg_field suspend_sleep;
+       unsigned int suspend_vsel_reg;
+       struct reg_field ilimit;
+       /* Event detection bit */
+       struct reg_field oc_event;
+};
+
+/* Single regulator settings */
+struct da9062_regulator {
+       struct regulator_desc                   desc;
+       struct regulator_dev                    *rdev;
+       struct da9062                           *hw;
+       const struct da9062_regulator_info      *info;
+
+       struct regmap_field                     *mode;
+       struct regmap_field                     *suspend;
+       struct regmap_field                     *sleep;
+       struct regmap_field                     *suspend_sleep;
+       struct regmap_field                     *ilimit;
+};
+
+/* Encapsulates all information for the regulators driver */
+struct da9062_regulators {
+       int                                     irq_ldo_lim;
+       unsigned                                n_regulators;
+       /* Array size to be defined during init. Keep at end. */
+       struct da9062_regulator                 regulator[0];
+};
+
+/* BUCK modes */
+enum {
+       BUCK_MODE_MANUAL,       /* 0 */
+       BUCK_MODE_SLEEP,        /* 1 */
+       BUCK_MODE_SYNC,         /* 2 */
+       BUCK_MODE_AUTO          /* 3 */
+};
+
+/* Regulator operations */
+
+/* Current limits array (in uA) BUCK1 and BUCK3.
+   Entry indexes corresponds to register values. */
+static const int da9062_buck_a_limits[] = {
+        500000,  600000,  700000,  800000,  900000, 1000000, 1100000, 1200000,
+       1300000, 1400000, 1500000, 1600000, 1700000, 1800000, 1900000, 2000000
+};
+
+/* Current limits array (in uA) for BUCK2.
+   Entry indexes corresponds to register values. */
+static const int da9062_buck_b_limits[] = {
+       1500000, 1600000, 1700000, 1800000, 1900000, 2000000, 2100000, 2200000,
+       2300000, 2400000, 2500000, 2600000, 2700000, 2800000, 2900000, 3000000
+};
+
+static int da9062_set_current_limit(struct regulator_dev *rdev,
+                                   int min_ua, int max_ua)
+{
+       struct da9062_regulator *regl = rdev_get_drvdata(rdev);
+       const struct da9062_regulator_info *rinfo = regl->info;
+       int n, tval;
+
+       for (n = 0; n < rinfo->n_current_limits; n++) {
+               tval = rinfo->current_limits[n];
+               if (tval >= min_ua && tval <= max_ua)
+                       return regmap_field_write(regl->ilimit, n);
+       }
+
+       return -EINVAL;
+}
+
+static int da9062_get_current_limit(struct regulator_dev *rdev)
+{
+       struct da9062_regulator *regl = rdev_get_drvdata(rdev);
+       const struct da9062_regulator_info *rinfo = regl->info;
+       unsigned int sel;
+       int ret;
+
+       ret = regmap_field_read(regl->ilimit, &sel);
+       if (ret < 0)
+               return ret;
+
+       if (sel >= rinfo->n_current_limits)
+               sel = rinfo->n_current_limits - 1;
+
+       return rinfo->current_limits[sel];
+}
+
+static int da9062_buck_set_mode(struct regulator_dev *rdev, unsigned mode)
+{
+       struct da9062_regulator *regl = rdev_get_drvdata(rdev);
+       unsigned val;
+
+       switch (mode) {
+       case REGULATOR_MODE_FAST:
+               val = BUCK_MODE_SYNC;
+               break;
+       case REGULATOR_MODE_NORMAL:
+               val = BUCK_MODE_AUTO;
+               break;
+       case REGULATOR_MODE_STANDBY:
+               val = BUCK_MODE_SLEEP;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return regmap_field_write(regl->mode, val);
+}
+
+/*
+ * Bucks use single mode register field for normal operation
+ * and suspend state.
+ * There are 3 modes to map to: FAST, NORMAL, and STANDBY.
+ */
+
+static unsigned da9062_buck_get_mode(struct regulator_dev *rdev)
+{
+       struct da9062_regulator *regl = rdev_get_drvdata(rdev);
+       struct regmap_field *field;
+       unsigned int val, mode = 0;
+       int ret;
+
+       ret = regmap_field_read(regl->mode, &val);
+       if (ret < 0)
+               return ret;
+
+       switch (val) {
+       default:
+       case BUCK_MODE_MANUAL:
+               mode = REGULATOR_MODE_FAST | REGULATOR_MODE_STANDBY;
+               /* Sleep flag bit decides the mode */
+               break;
+       case BUCK_MODE_SLEEP:
+               return REGULATOR_MODE_STANDBY;
+       case BUCK_MODE_SYNC:
+               return REGULATOR_MODE_FAST;
+       case BUCK_MODE_AUTO:
+               return REGULATOR_MODE_NORMAL;
+       }
+
+       /* Detect current regulator state */
+       ret = regmap_field_read(regl->suspend, &val);
+       if (ret < 0)
+               return 0;
+
+       /* Read regulator mode from proper register, depending on state */
+       if (val)
+               field = regl->suspend_sleep;
+       else
+               field = regl->sleep;
+
+       ret = regmap_field_read(field, &val);
+       if (ret < 0)
+               return 0;
+
+       if (val)
+               mode &= REGULATOR_MODE_STANDBY;
+       else
+               mode &= REGULATOR_MODE_NORMAL | REGULATOR_MODE_FAST;
+
+       return mode;
+}
+
+/*
+ * LDOs use sleep flags - one for normal and one for suspend state.
+ * There are 2 modes to map to: NORMAL and STANDBY (sleep) for each state.
+ */
+
+static int da9062_ldo_set_mode(struct regulator_dev *rdev, unsigned mode)
+{
+       struct da9062_regulator *regl = rdev_get_drvdata(rdev);
+       unsigned val;
+
+       switch (mode) {
+       case REGULATOR_MODE_NORMAL:
+               val = 0;
+               break;
+       case REGULATOR_MODE_STANDBY:
+               val = 1;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return regmap_field_write(regl->sleep, val);
+}
+
+static unsigned da9062_ldo_get_mode(struct regulator_dev *rdev)
+{
+       struct da9062_regulator *regl = rdev_get_drvdata(rdev);
+       struct regmap_field *field;
+       int ret, val;
+
+       /* Detect current regulator state */
+       ret = regmap_field_read(regl->suspend, &val);
+       if (ret < 0)
+               return 0;
+
+       /* Read regulator mode from proper register, depending on state */
+       if (val)
+               field = regl->suspend_sleep;
+       else
+               field = regl->sleep;
+
+       ret = regmap_field_read(field, &val);
+       if (ret < 0)
+               return 0;
+
+       if (val)
+               return REGULATOR_MODE_STANDBY;
+       else
+               return REGULATOR_MODE_NORMAL;
+}
+
+static int da9062_buck_get_status(struct regulator_dev *rdev)
+{
+       int ret = regulator_is_enabled_regmap(rdev);
+
+       if (ret == 0) {
+               ret = REGULATOR_STATUS_OFF;
+       } else if (ret > 0) {
+               ret = da9062_buck_get_mode(rdev);
+               if (ret > 0)
+                       ret = regulator_mode_to_status(ret);
+               else if (ret == 0)
+                       ret = -EIO;
+       }
+
+       return ret;
+}
+
+static int da9062_ldo_get_status(struct regulator_dev *rdev)
+{
+       int ret = regulator_is_enabled_regmap(rdev);
+
+       if (ret == 0) {
+               ret = REGULATOR_STATUS_OFF;
+       } else if (ret > 0) {
+               ret = da9062_ldo_get_mode(rdev);
+               if (ret > 0)
+                       ret = regulator_mode_to_status(ret);
+               else if (ret == 0)
+                       ret = -EIO;
+       }
+
+       return ret;
+}
+
+static int da9062_set_suspend_voltage(struct regulator_dev *rdev, int uv)
+{
+       struct da9062_regulator *regl = rdev_get_drvdata(rdev);
+       const struct da9062_regulator_info *rinfo = regl->info;
+       int ret, sel;
+
+       sel = regulator_map_voltage_linear(rdev, uv, uv);
+       if (sel < 0)
+               return sel;
+
+       sel <<= ffs(rdev->desc->vsel_mask) - 1;
+
+       ret = regmap_update_bits(regl->hw->regmap, rinfo->suspend_vsel_reg,
+                                rdev->desc->vsel_mask, sel);
+
+       return ret;
+}
+
+static int da9062_suspend_enable(struct regulator_dev *rdev)
+{
+       struct da9062_regulator *regl = rdev_get_drvdata(rdev);
+
+       return regmap_field_write(regl->suspend, 1);
+}
+
+static int da9062_suspend_disable(struct regulator_dev *rdev)
+{
+       struct da9062_regulator *regl = rdev_get_drvdata(rdev);
+
+       return regmap_field_write(regl->suspend, 0);
+}
+
+static int da9062_buck_set_suspend_mode(struct regulator_dev *rdev,
+                                       unsigned mode)
+{
+       struct da9062_regulator *regl = rdev_get_drvdata(rdev);
+       int val;
+
+       switch (mode) {
+       case REGULATOR_MODE_FAST:
+               val = BUCK_MODE_SYNC;
+               break;
+       case REGULATOR_MODE_NORMAL:
+               val = BUCK_MODE_AUTO;
+               break;
+       case REGULATOR_MODE_STANDBY:
+               val = BUCK_MODE_SLEEP;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return regmap_field_write(regl->mode, val);
+}
+
+static int da9062_ldo_set_suspend_mode(struct regulator_dev *rdev,
+                                               unsigned mode)
+{
+       struct da9062_regulator *regl = rdev_get_drvdata(rdev);
+       unsigned val;
+
+       switch (mode) {
+       case REGULATOR_MODE_NORMAL:
+               val = 0;
+               break;
+       case REGULATOR_MODE_STANDBY:
+               val = 1;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return regmap_field_write(regl->suspend_sleep, val);
+}
+
+static struct regulator_ops da9062_buck_ops = {
+       .enable                 = regulator_enable_regmap,
+       .disable                = regulator_disable_regmap,
+       .is_enabled             = regulator_is_enabled_regmap,
+       .get_voltage_sel        = regulator_get_voltage_sel_regmap,
+       .set_voltage_sel        = regulator_set_voltage_sel_regmap,
+       .list_voltage           = regulator_list_voltage_linear,
+       .set_current_limit      = da9062_set_current_limit,
+       .get_current_limit      = da9062_get_current_limit,
+       .set_mode               = da9062_buck_set_mode,
+       .get_mode               = da9062_buck_get_mode,
+       .get_status             = da9062_buck_get_status,
+       .set_suspend_voltage    = da9062_set_suspend_voltage,
+       .set_suspend_enable     = da9062_suspend_enable,
+       .set_suspend_disable    = da9062_suspend_disable,
+       .set_suspend_mode       = da9062_buck_set_suspend_mode,
+};
+
+static struct regulator_ops da9062_ldo_ops = {
+       .enable                 = regulator_enable_regmap,
+       .disable                = regulator_disable_regmap,
+       .is_enabled             = regulator_is_enabled_regmap,
+       .get_voltage_sel        = regulator_get_voltage_sel_regmap,
+       .set_voltage_sel        = regulator_set_voltage_sel_regmap,
+       .list_voltage           = regulator_list_voltage_linear,
+       .set_mode               = da9062_ldo_set_mode,
+       .get_mode               = da9062_ldo_get_mode,
+       .get_status             = da9062_ldo_get_status,
+       .set_suspend_voltage    = da9062_set_suspend_voltage,
+       .set_suspend_enable     = da9062_suspend_enable,
+       .set_suspend_disable    = da9062_suspend_disable,
+       .set_suspend_mode       = da9062_ldo_set_suspend_mode,
+};
+
+/* Regulator information */
+static const struct da9062_regulator_info local_regulator_info[] = {
+       {
+               .desc.id = DA9062_ID_BUCK1,
+               .desc.name = "DA9062 BUCK1",
+               .desc.of_match = of_match_ptr("buck1"),
+               .desc.regulators_node = of_match_ptr("regulators"),
+               .desc.ops = &da9062_buck_ops,
+               .desc.min_uV = (300) * 1000,
+               .desc.uV_step = (10) * 1000,
+               .desc.n_voltages = ((1570) - (300))/(10) + 1,
+               .current_limits = da9062_buck_a_limits,
+               .n_current_limits = ARRAY_SIZE(da9062_buck_a_limits),
+               .desc.enable_reg = DA9062AA_BUCK1_CONT,
+               .desc.enable_mask = DA9062AA_BUCK1_EN_MASK,
+               .desc.vsel_reg = DA9062AA_VBUCK1_A,
+               .desc.vsel_mask = DA9062AA_VBUCK1_A_MASK,
+               .desc.linear_min_sel = 0,
+               .sleep = REG_FIELD(DA9062AA_VBUCK1_A,
+                       __builtin_ffs((int)DA9062AA_BUCK1_SL_A_MASK) - 1,
+                       sizeof(unsigned int) * 8 -
+                       __builtin_clz((DA9062AA_BUCK1_SL_A_MASK)) - 1),
+               .suspend_sleep = REG_FIELD(DA9062AA_VBUCK1_B,
+                       __builtin_ffs((int)DA9062AA_BUCK1_SL_B_MASK) - 1,
+                       sizeof(unsigned int) * 8 -
+                       __builtin_clz((DA9062AA_BUCK1_SL_B_MASK)) - 1),
+               .suspend_vsel_reg = DA9062AA_VBUCK1_B,
+               .mode = REG_FIELD(DA9062AA_BUCK1_CFG,
+                       __builtin_ffs((int)DA9062AA_BUCK1_MODE_MASK) - 1,
+                       sizeof(unsigned int) * 8 -
+                       __builtin_clz((DA9062AA_BUCK1_MODE_MASK)) - 1),
+               .suspend = REG_FIELD(DA9062AA_DVC_1,
+                       __builtin_ffs((int)DA9062AA_VBUCK1_SEL_MASK) - 1,
+                       sizeof(unsigned int) * 8 -
+                       __builtin_clz((DA9062AA_VBUCK1_SEL_MASK)) - 1),
+               .ilimit = REG_FIELD(DA9062AA_BUCK_ILIM_C,
+                       __builtin_ffs((int)DA9062AA_BUCK1_ILIM_MASK) - 1,
+                       sizeof(unsigned int) * 8 -
+                       __builtin_clz((DA9062AA_BUCK1_ILIM_MASK)) - 1),
+       },
+       {
+               .desc.id = DA9062_ID_BUCK2,
+               .desc.name = "DA9062 BUCK2",
+               .desc.of_match = of_match_ptr("buck2"),
+               .desc.regulators_node = of_match_ptr("regulators"),
+               .desc.ops = &da9062_buck_ops,
+               .desc.min_uV = (300) * 1000,
+               .desc.uV_step = (10) * 1000,
+               .desc.n_voltages = ((1570) - (300))/(10) + 1,
+               .current_limits = da9062_buck_a_limits,
+               .n_current_limits = ARRAY_SIZE(da9062_buck_a_limits),
+               .desc.enable_reg = DA9062AA_BUCK2_CONT,
+               .desc.enable_mask = DA9062AA_BUCK2_EN_MASK,
+               .desc.vsel_reg = DA9062AA_VBUCK2_A,
+               .desc.vsel_mask = DA9062AA_VBUCK2_A_MASK,
+               .desc.linear_min_sel = 0,
+               .sleep = REG_FIELD(DA9062AA_VBUCK2_A,
+                       __builtin_ffs((int)DA9062AA_BUCK2_SL_A_MASK) - 1,
+                       sizeof(unsigned int) * 8 -
+                       __builtin_clz((DA9062AA_BUCK2_SL_A_MASK)) - 1),
+               .suspend_sleep = REG_FIELD(DA9062AA_VBUCK2_B,
+                       __builtin_ffs((int)DA9062AA_BUCK2_SL_B_MASK) - 1,
+                       sizeof(unsigned int) * 8 -
+                       __builtin_clz((DA9062AA_BUCK2_SL_B_MASK)) - 1),
+               .suspend_vsel_reg = DA9062AA_VBUCK2_B,
+               .mode = REG_FIELD(DA9062AA_BUCK2_CFG,
+                       __builtin_ffs((int)DA9062AA_BUCK2_MODE_MASK) - 1,
+                       sizeof(unsigned int) * 8 -
+                       __builtin_clz((DA9062AA_BUCK2_MODE_MASK)) - 1),
+               .suspend = REG_FIELD(DA9062AA_DVC_1,
+                       __builtin_ffs((int)DA9062AA_VBUCK2_SEL_MASK) - 1,
+                       sizeof(unsigned int) * 8 -
+                       __builtin_clz((DA9062AA_VBUCK2_SEL_MASK)) - 1),
+               .ilimit = REG_FIELD(DA9062AA_BUCK_ILIM_C,
+                       __builtin_ffs((int)DA9062AA_BUCK2_ILIM_MASK) - 1,
+                       sizeof(unsigned int) * 8 -
+                       __builtin_clz((DA9062AA_BUCK2_ILIM_MASK)) - 1),
+       },
+       {
+               .desc.id = DA9062_ID_BUCK3,
+               .desc.name = "DA9062 BUCK3",
+               .desc.of_match = of_match_ptr("buck3"),
+               .desc.regulators_node = of_match_ptr("regulators"),
+               .desc.ops = &da9062_buck_ops,
+               .desc.min_uV = (800) * 1000,
+               .desc.uV_step = (20) * 1000,
+               .desc.n_voltages = ((3340) - (800))/(20) + 1,
+               .current_limits = da9062_buck_b_limits,
+               .n_current_limits = ARRAY_SIZE(da9062_buck_b_limits),
+               .desc.enable_reg = DA9062AA_BUCK3_CONT,
+               .desc.enable_mask = DA9062AA_BUCK3_EN_MASK,
+               .desc.vsel_reg = DA9062AA_VBUCK3_A,
+               .desc.vsel_mask = DA9062AA_VBUCK3_A_MASK,
+               .desc.linear_min_sel = 0,
+               .sleep = REG_FIELD(DA9062AA_VBUCK3_A,
+                       __builtin_ffs((int)DA9062AA_BUCK3_SL_A_MASK) - 1,
+                       sizeof(unsigned int) * 8 -
+                       __builtin_clz((DA9062AA_BUCK3_SL_A_MASK)) - 1),
+               .suspend_sleep = REG_FIELD(DA9062AA_VBUCK3_B,
+                       __builtin_ffs((int)DA9062AA_BUCK3_SL_B_MASK) - 1,
+                       sizeof(unsigned int) * 8 -
+                       __builtin_clz((DA9062AA_BUCK3_SL_B_MASK)) - 1),
+               .suspend_vsel_reg = DA9062AA_VBUCK3_B,
+               .mode = REG_FIELD(DA9062AA_BUCK3_CFG,
+                       __builtin_ffs((int)DA9062AA_BUCK3_MODE_MASK) - 1,
+                       sizeof(unsigned int) * 8 -
+                       __builtin_clz((DA9062AA_BUCK3_MODE_MASK)) - 1),
+               .suspend = REG_FIELD(DA9062AA_DVC_1,
+                       __builtin_ffs((int)DA9062AA_VBUCK3_SEL_MASK) - 1,
+                       sizeof(unsigned int) * 8 -
+                       __builtin_clz((DA9062AA_VBUCK3_SEL_MASK)) - 1),
+               .ilimit = REG_FIELD(DA9062AA_BUCK_ILIM_A,
+                       __builtin_ffs((int)DA9062AA_BUCK3_ILIM_MASK) - 1,
+                       sizeof(unsigned int) * 8 -
+                       __builtin_clz((DA9062AA_BUCK3_ILIM_MASK)) - 1),
+       },
+       {
+               .desc.id = DA9062_ID_BUCK4,
+               .desc.name = "DA9062 BUCK4",
+               .desc.of_match = of_match_ptr("buck4"),
+               .desc.regulators_node = of_match_ptr("regulators"),
+               .desc.ops = &da9062_buck_ops,
+               .desc.min_uV = (530) * 1000,
+               .desc.uV_step = (10) * 1000,
+               .desc.n_voltages = ((1800) - (530))/(10) + 1,
+               .current_limits = da9062_buck_a_limits,
+               .n_current_limits = ARRAY_SIZE(da9062_buck_a_limits),
+               .desc.enable_reg = DA9062AA_BUCK4_CONT,
+               .desc.enable_mask = DA9062AA_BUCK4_EN_MASK,
+               .desc.vsel_reg = DA9062AA_VBUCK4_A,
+               .desc.vsel_mask = DA9062AA_VBUCK4_A_MASK,
+               .desc.linear_min_sel = 0,
+               .sleep = REG_FIELD(DA9062AA_VBUCK4_A,
+                       __builtin_ffs((int)DA9062AA_BUCK4_SL_A_MASK) - 1,
+                       sizeof(unsigned int) * 8 -
+                       __builtin_clz((DA9062AA_BUCK4_SL_A_MASK)) - 1),
+               .suspend_sleep = REG_FIELD(DA9062AA_VBUCK4_B,
+                       __builtin_ffs((int)DA9062AA_BUCK4_SL_B_MASK) - 1,
+                       sizeof(unsigned int) * 8 -
+                       __builtin_clz((DA9062AA_BUCK4_SL_B_MASK)) - 1),
+               .suspend_vsel_reg = DA9062AA_VBUCK4_B,
+               .mode = REG_FIELD(DA9062AA_BUCK4_CFG,
+                       __builtin_ffs((int)DA9062AA_BUCK4_MODE_MASK) - 1,
+                       sizeof(unsigned int) * 8 -
+                       __builtin_clz((DA9062AA_BUCK4_MODE_MASK)) - 1),
+               .suspend = REG_FIELD(DA9062AA_DVC_1,
+                       __builtin_ffs((int)DA9062AA_VBUCK4_SEL_MASK) - 1,
+                       sizeof(unsigned int) * 8 -
+                       __builtin_clz((DA9062AA_VBUCK4_SEL_MASK)) - 1),
+               .ilimit = REG_FIELD(DA9062AA_BUCK_ILIM_B,
+                       __builtin_ffs((int)DA9062AA_BUCK4_ILIM_MASK) - 1,
+                       sizeof(unsigned int) * 8 -
+                       __builtin_clz((DA9062AA_BUCK4_ILIM_MASK)) - 1),
+       },
+       {
+               .desc.id = DA9062_ID_LDO1,
+               .desc.name = "DA9062 LDO1",
+               .desc.of_match = of_match_ptr("ldo1"),
+               .desc.regulators_node = of_match_ptr("regulators"),
+               .desc.ops = &da9062_ldo_ops,
+               .desc.min_uV = (900) * 1000,
+               .desc.uV_step = (50) * 1000,
+               .desc.n_voltages = ((3600) - (900))/(50) + 1,
+               .desc.enable_reg = DA9062AA_LDO1_CONT,
+               .desc.enable_mask = DA9062AA_LDO1_EN_MASK,
+               .desc.vsel_reg = DA9062AA_VLDO1_A,
+               .desc.vsel_mask = DA9062AA_VLDO1_A_MASK,
+               .desc.linear_min_sel = 0,
+               .sleep = REG_FIELD(DA9062AA_VLDO1_A,
+                       __builtin_ffs((int)DA9062AA_LDO1_SL_A_MASK) - 1,
+                       sizeof(unsigned int) * 8 -
+                       __builtin_clz((DA9062AA_LDO1_SL_A_MASK)) - 1),
+               .suspend_sleep = REG_FIELD(DA9062AA_VLDO1_B,
+                       __builtin_ffs((int)DA9062AA_LDO1_SL_B_MASK) - 1,
+                       sizeof(unsigned int) * 8 -
+                       __builtin_clz((DA9062AA_LDO1_SL_B_MASK)) - 1),
+               .suspend_vsel_reg = DA9062AA_VLDO1_B,
+               .suspend = REG_FIELD(DA9062AA_DVC_1,
+                       __builtin_ffs((int)DA9062AA_VLDO1_SEL_MASK) - 1,
+                       sizeof(unsigned int) * 8 -
+                       __builtin_clz((DA9062AA_VLDO1_SEL_MASK)) - 1),
+               .oc_event = REG_FIELD(DA9062AA_STATUS_D,
+                       __builtin_ffs((int)DA9062AA_LDO1_ILIM_MASK) - 1,
+                       sizeof(unsigned int) * 8 -
+                       __builtin_clz((DA9062AA_LDO1_ILIM_MASK)) - 1),
+       },
+       {
+               .desc.id = DA9062_ID_LDO2,
+               .desc.name = "DA9062 LDO2",
+               .desc.of_match = of_match_ptr("ldo2"),
+               .desc.regulators_node = of_match_ptr("regulators"),
+               .desc.ops = &da9062_ldo_ops,
+               .desc.min_uV = (900) * 1000,
+               .desc.uV_step = (50) * 1000,
+               .desc.n_voltages = ((3600) - (600))/(50) + 1,
+               .desc.enable_reg = DA9062AA_LDO2_CONT,
+               .desc.enable_mask = DA9062AA_LDO2_EN_MASK,
+               .desc.vsel_reg = DA9062AA_VLDO2_A,
+               .desc.vsel_mask = DA9062AA_VLDO2_A_MASK,
+               .desc.linear_min_sel = 0,
+               .sleep = REG_FIELD(DA9062AA_VLDO2_A,
+                       __builtin_ffs((int)DA9062AA_LDO2_SL_A_MASK) - 1,
+                       sizeof(unsigned int) * 8 -
+                       __builtin_clz((DA9062AA_LDO2_SL_A_MASK)) - 1),
+               .suspend_sleep = REG_FIELD(DA9062AA_VLDO2_B,
+                       __builtin_ffs((int)DA9062AA_LDO2_SL_B_MASK) - 1,
+                       sizeof(unsigned int) * 8 -
+                       __builtin_clz((DA9062AA_LDO2_SL_B_MASK)) - 1),
+               .suspend_vsel_reg = DA9062AA_VLDO2_B,
+               .suspend = REG_FIELD(DA9062AA_DVC_1,
+                       __builtin_ffs((int)DA9062AA_VLDO2_SEL_MASK) - 1,
+                       sizeof(unsigned int) * 8 -
+                       __builtin_clz((DA9062AA_VLDO2_SEL_MASK)) - 1),
+               .oc_event = REG_FIELD(DA9062AA_STATUS_D,
+                       __builtin_ffs((int)DA9062AA_LDO2_ILIM_MASK) - 1,
+                       sizeof(unsigned int) * 8 -
+                       __builtin_clz((DA9062AA_LDO2_ILIM_MASK)) - 1),
+       },
+       {
+               .desc.id = DA9062_ID_LDO3,
+               .desc.name = "DA9062 LDO3",
+               .desc.of_match = of_match_ptr("ldo3"),
+               .desc.regulators_node = of_match_ptr("regulators"),
+               .desc.ops = &da9062_ldo_ops,
+               .desc.min_uV = (900) * 1000,
+               .desc.uV_step = (50) * 1000,
+               .desc.n_voltages = ((3600) - (900))/(50) + 1,
+               .desc.enable_reg = DA9062AA_LDO3_CONT,
+               .desc.enable_mask = DA9062AA_LDO3_EN_MASK,
+               .desc.vsel_reg = DA9062AA_VLDO3_A,
+               .desc.vsel_mask = DA9062AA_VLDO3_A_MASK,
+               .desc.linear_min_sel = 0,
+               .sleep = REG_FIELD(DA9062AA_VLDO3_A,
+                       __builtin_ffs((int)DA9062AA_LDO3_SL_A_MASK) - 1,
+                       sizeof(unsigned int) * 8 -
+                       __builtin_clz((DA9062AA_LDO3_SL_A_MASK)) - 1),
+               .suspend_sleep = REG_FIELD(DA9062AA_VLDO3_B,
+                       __builtin_ffs((int)DA9062AA_LDO3_SL_B_MASK) - 1,
+                       sizeof(unsigned int) * 8 -
+                       __builtin_clz((DA9062AA_LDO3_SL_B_MASK)) - 1),
+               .suspend_vsel_reg = DA9062AA_VLDO3_B,
+               .suspend = REG_FIELD(DA9062AA_DVC_1,
+                       __builtin_ffs((int)DA9062AA_VLDO3_SEL_MASK) - 1,
+                       sizeof(unsigned int) * 8 -
+                       __builtin_clz((DA9062AA_VLDO3_SEL_MASK)) - 1),
+               .oc_event = REG_FIELD(DA9062AA_STATUS_D,
+                       __builtin_ffs((int)DA9062AA_LDO3_ILIM_MASK) - 1,
+                       sizeof(unsigned int) * 8 -
+                       __builtin_clz((DA9062AA_LDO3_ILIM_MASK)) - 1),
+       },
+       {
+               .desc.id = DA9062_ID_LDO4,
+               .desc.name = "DA9062 LDO4",
+               .desc.of_match = of_match_ptr("ldo4"),
+               .desc.regulators_node = of_match_ptr("regulators"),
+               .desc.ops = &da9062_ldo_ops,
+               .desc.min_uV = (900) * 1000,
+               .desc.uV_step = (50) * 1000,
+               .desc.n_voltages = ((3600) - (900))/(50) + 1,
+               .desc.enable_reg = DA9062AA_LDO4_CONT,
+               .desc.enable_mask = DA9062AA_LDO4_EN_MASK,
+               .desc.vsel_reg = DA9062AA_VLDO4_A,
+               .desc.vsel_mask = DA9062AA_VLDO4_A_MASK,
+               .desc.linear_min_sel = 0,
+               .sleep = REG_FIELD(DA9062AA_VLDO4_A,
+                       __builtin_ffs((int)DA9062AA_LDO4_SL_A_MASK) - 1,
+                       sizeof(unsigned int) * 8 -
+                       __builtin_clz((DA9062AA_LDO4_SL_A_MASK)) - 1),
+               .suspend_sleep = REG_FIELD(DA9062AA_VLDO4_B,
+                       __builtin_ffs((int)DA9062AA_LDO4_SL_B_MASK) - 1,
+                       sizeof(unsigned int) * 8 -
+                       __builtin_clz((DA9062AA_LDO4_SL_B_MASK)) - 1),
+               .suspend_vsel_reg = DA9062AA_VLDO4_B,
+               .suspend = REG_FIELD(DA9062AA_DVC_1,
+                       __builtin_ffs((int)DA9062AA_VLDO4_SEL_MASK) - 1,
+                       sizeof(unsigned int) * 8 -
+                       __builtin_clz((DA9062AA_VLDO4_SEL_MASK)) - 1),
+               .oc_event = REG_FIELD(DA9062AA_STATUS_D,
+                       __builtin_ffs((int)DA9062AA_LDO4_ILIM_MASK) - 1,
+                       sizeof(unsigned int) * 8 -
+                       __builtin_clz((DA9062AA_LDO4_ILIM_MASK)) - 1),
+       },
+};
+
+/* Regulator interrupt handlers */
+static irqreturn_t da9062_ldo_lim_event(int irq, void *data)
+{
+       struct da9062_regulators *regulators = data;
+       struct da9062 *hw = regulators->regulator[0].hw;
+       struct da9062_regulator *regl;
+       int handled = IRQ_NONE;
+       int bits, i, ret;
+
+       ret = regmap_read(hw->regmap, DA9062AA_STATUS_D, &bits);
+       if (ret < 0) {
+               dev_err(hw->dev,
+                       "Failed to read LDO overcurrent indicator\n");
+               goto ldo_lim_error;
+       }
+
+       for (i = regulators->n_regulators - 1; i >= 0; i--) {
+               regl = &regulators->regulator[i];
+               if (regl->info->oc_event.reg != DA9062AA_STATUS_D)
+                       continue;
+
+               if (BIT(regl->info->oc_event.lsb) & bits) {
+                       regulator_notifier_call_chain(regl->rdev,
+                                       REGULATOR_EVENT_OVER_CURRENT, NULL);
+                       handled = IRQ_HANDLED;
+               }
+       }
+
+ldo_lim_error:
+       return handled;
+}
+
+static int da9062_regulator_probe(struct platform_device *pdev)
+{
+       struct da9062 *chip = dev_get_drvdata(pdev->dev.parent);
+       struct da9062_regulators *regulators;
+       struct da9062_regulator *regl;
+       struct regulator_config config = { };
+       int irq, n, ret;
+       size_t size;
+
+       /* Allocate memory required by usable regulators */
+       size = sizeof(struct da9062_regulators) +
+               DA9062_MAX_REGULATORS * sizeof(struct da9062_regulator);
+       regulators = devm_kzalloc(&pdev->dev, size, GFP_KERNEL);
+       if (!regulators)
+               return -ENOMEM;
+
+       regulators->n_regulators = DA9062_MAX_REGULATORS;
+       platform_set_drvdata(pdev, regulators);
+
+       n = 0;
+       while (n < regulators->n_regulators) {
+               /* Initialise regulator structure */
+               regl = &regulators->regulator[n];
+               regl->hw = chip;
+               regl->info = &local_regulator_info[n];
+               regl->desc = regl->info->desc;
+               regl->desc.type = REGULATOR_VOLTAGE;
+               regl->desc.owner = THIS_MODULE;
+
+               if (regl->info->mode.reg)
+                       regl->mode = devm_regmap_field_alloc(
+                                       &pdev->dev,
+                                       chip->regmap,
+                                       regl->info->mode);
+               if (regl->info->suspend.reg)
+                       regl->suspend = devm_regmap_field_alloc(
+                                       &pdev->dev,
+                                       chip->regmap,
+                                       regl->info->suspend);
+               if (regl->info->sleep.reg)
+                       regl->sleep = devm_regmap_field_alloc(
+                                       &pdev->dev,
+                                       chip->regmap,
+                                       regl->info->sleep);
+               if (regl->info->suspend_sleep.reg)
+                       regl->suspend_sleep = devm_regmap_field_alloc(
+                                       &pdev->dev,
+                                       chip->regmap,
+                                       regl->info->suspend_sleep);
+               if (regl->info->ilimit.reg)
+                       regl->ilimit = devm_regmap_field_alloc(
+                                       &pdev->dev,
+                                       chip->regmap,
+                                       regl->info->ilimit);
+
+               /* Register regulator */
+               memset(&config, 0, sizeof(config));
+               config.dev = chip->dev;
+               config.driver_data = regl;
+               config.regmap = chip->regmap;
+
+               regl->rdev = devm_regulator_register(&pdev->dev, &regl->desc,
+                                                    &config);
+               if (IS_ERR(regl->rdev)) {
+                       dev_err(&pdev->dev,
+                               "Failed to register %s regulator\n",
+                               regl->desc.name);
+                       return PTR_ERR(regl->rdev);
+               }
+
+               n++;
+       }
+
+       /* LDOs overcurrent event support */
+       irq = platform_get_irq_byname(pdev, "LDO_LIM");
+       if (irq < 0) {
+               dev_err(&pdev->dev, "Failed to get IRQ.\n");
+               return irq;
+       }
+       regulators->irq_ldo_lim = irq;
+
+       ret = devm_request_threaded_irq(&pdev->dev, irq,
+                                       NULL, da9062_ldo_lim_event,
+                                       IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+                                       "LDO_LIM", regulators);
+       if (ret) {
+               dev_warn(&pdev->dev,
+                        "Failed to request LDO_LIM IRQ.\n");
+               regulators->irq_ldo_lim = -ENXIO;
+       }
+
+       return 0;
+}
+
+static struct platform_driver da9062_regulator_driver = {
+       .driver = {
+               .name = "da9062-regulators",
+               .owner = THIS_MODULE,
+       },
+       .probe = da9062_regulator_probe,
+};
+
+static int __init da9062_regulator_init(void)
+{
+       return platform_driver_register(&da9062_regulator_driver);
+}
+subsys_initcall(da9062_regulator_init);
+
+static void __exit da9062_regulator_cleanup(void)
+{
+       platform_driver_unregister(&da9062_regulator_driver);
+}
+module_exit(da9062_regulator_cleanup);
+
+/* Module information */
+MODULE_AUTHOR("S Twiss <stwiss.opensource@diasemi.com>");
+MODULE_DESCRIPTION("REGULATOR device driver for Dialog DA9062");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:da9062-regulators");
index 31c2c593ae0b76886dadc0df2526c5c8341c34ad..aed1ad3dc964e231c76dc4131a42316f2b13a5fe 100644 (file)
@@ -117,9 +117,6 @@ struct da9063_regulator {
 
 /* Encapsulates all information for the regulators driver */
 struct da9063_regulators {
-       int                                     irq_ldo_lim;
-       int                                     irq_uvov;
-
        unsigned                                n_regulators;
        /* Array size to be defined during init. Keep at end. */
        struct da9063_regulator                 regulator[0];
@@ -867,35 +864,23 @@ static int da9063_regulator_probe(struct platform_device *pdev)
                return irq;
        }
 
-       ret = request_threaded_irq(irq,
+       ret = devm_request_threaded_irq(&pdev->dev, irq,
                                NULL, da9063_ldo_lim_event,
                                IRQF_TRIGGER_LOW | IRQF_ONESHOT,
                                "LDO_LIM", regulators);
        if (ret) {
-               dev_err(&pdev->dev,
-                               "Failed to request LDO_LIM IRQ.\n");
-               regulators->irq_ldo_lim = -ENXIO;
+               dev_err(&pdev->dev, "Failed to request LDO_LIM IRQ.\n");
+               return ret;
        }
 
        return 0;
 }
 
-static int da9063_regulator_remove(struct platform_device *pdev)
-{
-       struct da9063_regulators *regulators = platform_get_drvdata(pdev);
-
-       free_irq(regulators->irq_ldo_lim, regulators);
-       free_irq(regulators->irq_uvov, regulators);
-
-       return 0;
-}
-
 static struct platform_driver da9063_regulator_driver = {
        .driver = {
                .name = DA9063_DRVNAME_REGULATORS,
        },
        .probe = da9063_regulator_probe,
-       .remove = da9063_regulator_remove,
 };
 
 static int __init da9063_regulator_init(void)
index 3c25db89a021af927f183b9f363b270b76e691c4..42865681c00bfb18df9bc5085a0b84f6fd8a0851 100644 (file)
@@ -182,6 +182,7 @@ static int fan53555_set_ramp(struct regulator_dev *rdev, int ramp)
 static struct regulator_ops fan53555_regulator_ops = {
        .set_voltage_sel = regulator_set_voltage_sel_regmap,
        .get_voltage_sel = regulator_get_voltage_sel_regmap,
+       .set_voltage_time_sel = regulator_set_voltage_time_sel,
        .map_voltage = regulator_map_voltage_linear,
        .list_voltage = regulator_list_voltage_linear,
        .set_suspend_voltage = fan53555_set_suspend_voltage,
index cbc39096c78d436f8206aa7107811bc43da6fd20..3bbb32680a94265069eac502d1d2b679e22fa673 100644 (file)
@@ -275,7 +275,7 @@ int regulator_map_voltage_linear(struct regulator_dev *rdev,
 EXPORT_SYMBOL_GPL(regulator_map_voltage_linear);
 
 /**
- * regulator_map_voltage_linear - map_voltage() for multiple linear ranges
+ * regulator_map_voltage_linear_range - map_voltage() for multiple linear ranges
  *
  * @rdev: Regulator to operate on
  * @min_uV: Lower bound for voltage
index 4a415d4ee4635a45074205c5a5cdda998c09a720..d6773da925bad00f3bc227294255f8786c2eaf9b 100644 (file)
@@ -419,20 +419,16 @@ static int lp8755_int_config(struct lp8755_chip *pchip)
        }
 
        ret = lp8755_read(pchip, 0x0F, &regval);
-       if (ret < 0)
-               goto err_i2c;
-       pchip->irqmask = regval;
-       ret = request_threaded_irq(pchip->irq, NULL, lp8755_irq_handler,
-                                  IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
-                                  "lp8755-irq", pchip);
-       if (ret)
+       if (ret < 0) {
+               dev_err(pchip->dev, "i2c acceess error %s\n", __func__);
                return ret;
+       }
 
-       return ret;
-
-err_i2c:
-       dev_err(pchip->dev, "i2c acceess error %s\n", __func__);
-       return ret;
+       pchip->irqmask = regval;
+       return devm_request_threaded_irq(pchip->dev, pchip->irq, NULL,
+                                        lp8755_irq_handler,
+                                        IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+                                        "lp8755-irq", pchip);
 }
 
 static const struct regmap_config lp8755_regmap = {
@@ -514,9 +510,6 @@ static int lp8755_remove(struct i2c_client *client)
        for (icnt = 0; icnt < LP8755_BUCK_MAX; icnt++)
                lp8755_write(pchip, icnt, 0x00);
 
-       if (pchip->irq != 0)
-               free_irq(pchip->irq, pchip);
-
        return 0;
 }
 
index b3678d289619330ffca48f338b9ddf362cd76986..b2daa6641417caecedbd43fde480ef8159cd3652 100644 (file)
@@ -100,31 +100,34 @@ static struct regulator_ops max14577_charger_ops = {
        .set_current_limit      = max14577_reg_set_current_limit,
 };
 
+#define MAX14577_SAFEOUT_REG   { \
+       .name           = "SAFEOUT", \
+       .of_match       = of_match_ptr("SAFEOUT"), \
+       .regulators_node = of_match_ptr("regulators"), \
+       .id             = MAX14577_SAFEOUT, \
+       .ops            = &max14577_safeout_ops, \
+       .type           = REGULATOR_VOLTAGE, \
+       .owner          = THIS_MODULE, \
+       .n_voltages     = 1, \
+       .min_uV         = MAX14577_REGULATOR_SAFEOUT_VOLTAGE, \
+       .enable_reg     = MAX14577_REG_CONTROL2, \
+       .enable_mask    = CTRL2_SFOUTORD_MASK, \
+}
+#define MAX14577_CHARGER_REG   { \
+       .name           = "CHARGER", \
+       .of_match       = of_match_ptr("CHARGER"), \
+       .regulators_node = of_match_ptr("regulators"), \
+       .id             = MAX14577_CHARGER, \
+       .ops            = &max14577_charger_ops, \
+       .type           = REGULATOR_CURRENT, \
+       .owner          = THIS_MODULE, \
+       .enable_reg     = MAX14577_CHG_REG_CHG_CTRL2, \
+       .enable_mask    = CHGCTRL2_MBCHOSTEN_MASK, \
+}
+
 static const struct regulator_desc max14577_supported_regulators[] = {
-       [MAX14577_SAFEOUT] = {
-               .name           = "SAFEOUT",
-               .of_match       = of_match_ptr("SAFEOUT"),
-               .regulators_node = of_match_ptr("regulators"),
-               .id             = MAX14577_SAFEOUT,
-               .ops            = &max14577_safeout_ops,
-               .type           = REGULATOR_VOLTAGE,
-               .owner          = THIS_MODULE,
-               .n_voltages     = 1,
-               .min_uV         = MAX14577_REGULATOR_SAFEOUT_VOLTAGE,
-               .enable_reg     = MAX14577_REG_CONTROL2,
-               .enable_mask    = CTRL2_SFOUTORD_MASK,
-       },
-       [MAX14577_CHARGER] = {
-               .name           = "CHARGER",
-               .of_match       = of_match_ptr("CHARGER"),
-               .regulators_node = of_match_ptr("regulators"),
-               .id             = MAX14577_CHARGER,
-               .ops            = &max14577_charger_ops,
-               .type           = REGULATOR_CURRENT,
-               .owner          = THIS_MODULE,
-               .enable_reg     = MAX14577_CHG_REG_CHG_CTRL2,
-               .enable_mask    = CHGCTRL2_MBCHOSTEN_MASK,
-       },
+       [MAX14577_SAFEOUT] = MAX14577_SAFEOUT_REG,
+       [MAX14577_CHARGER] = MAX14577_CHARGER_REG,
 };
 
 static struct regulator_ops max77836_ldo_ops = {
@@ -138,63 +141,28 @@ static struct regulator_ops max77836_ldo_ops = {
        /* TODO: add .set_suspend_mode */
 };
 
+#define MAX77836_LDO_REG(num)  { \
+       .name           = "LDO" # num, \
+       .of_match       = of_match_ptr("LDO" # num), \
+       .regulators_node = of_match_ptr("regulators"), \
+       .id             = MAX77836_LDO ## num, \
+       .ops            = &max77836_ldo_ops, \
+       .type           = REGULATOR_VOLTAGE, \
+       .owner          = THIS_MODULE, \
+       .n_voltages     = MAX77836_REGULATOR_LDO_VOLTAGE_STEPS_NUM, \
+       .min_uV         = MAX77836_REGULATOR_LDO_VOLTAGE_MIN, \
+       .uV_step        = MAX77836_REGULATOR_LDO_VOLTAGE_STEP, \
+       .enable_reg     = MAX77836_LDO_REG_CNFG1_LDO ## num, \
+       .enable_mask    = MAX77836_CNFG1_LDO_PWRMD_MASK, \
+       .vsel_reg       = MAX77836_LDO_REG_CNFG1_LDO ## num, \
+       .vsel_mask      = MAX77836_CNFG1_LDO_TV_MASK, \
+}
+
 static const struct regulator_desc max77836_supported_regulators[] = {
-       [MAX14577_SAFEOUT] = {
-               .name           = "SAFEOUT",
-               .of_match       = of_match_ptr("SAFEOUT"),
-               .regulators_node = of_match_ptr("regulators"),
-               .id             = MAX14577_SAFEOUT,
-               .ops            = &max14577_safeout_ops,
-               .type           = REGULATOR_VOLTAGE,
-               .owner          = THIS_MODULE,
-               .n_voltages     = 1,
-               .min_uV         = MAX14577_REGULATOR_SAFEOUT_VOLTAGE,
-               .enable_reg     = MAX14577_REG_CONTROL2,
-               .enable_mask    = CTRL2_SFOUTORD_MASK,
-       },
-       [MAX14577_CHARGER] = {
-               .name           = "CHARGER",
-               .of_match       = of_match_ptr("CHARGER"),
-               .regulators_node = of_match_ptr("regulators"),
-               .id             = MAX14577_CHARGER,
-               .ops            = &max14577_charger_ops,
-               .type           = REGULATOR_CURRENT,
-               .owner          = THIS_MODULE,
-               .enable_reg     = MAX14577_CHG_REG_CHG_CTRL2,
-               .enable_mask    = CHGCTRL2_MBCHOSTEN_MASK,
-       },
-       [MAX77836_LDO1] = {
-               .name           = "LDO1",
-               .of_match       = of_match_ptr("LDO1"),
-               .regulators_node = of_match_ptr("regulators"),
-               .id             = MAX77836_LDO1,
-               .ops            = &max77836_ldo_ops,
-               .type           = REGULATOR_VOLTAGE,
-               .owner          = THIS_MODULE,
-               .n_voltages     = MAX77836_REGULATOR_LDO_VOLTAGE_STEPS_NUM,
-               .min_uV         = MAX77836_REGULATOR_LDO_VOLTAGE_MIN,
-               .uV_step        = MAX77836_REGULATOR_LDO_VOLTAGE_STEP,
-               .enable_reg     = MAX77836_LDO_REG_CNFG1_LDO1,
-               .enable_mask    = MAX77836_CNFG1_LDO_PWRMD_MASK,
-               .vsel_reg       = MAX77836_LDO_REG_CNFG1_LDO1,
-               .vsel_mask      = MAX77836_CNFG1_LDO_TV_MASK,
-       },
-       [MAX77836_LDO2] = {
-               .name           = "LDO2",
-               .of_match       = of_match_ptr("LDO2"),
-               .regulators_node = of_match_ptr("regulators"),
-               .id             = MAX77836_LDO2,
-               .ops            = &max77836_ldo_ops,
-               .type           = REGULATOR_VOLTAGE,
-               .owner          = THIS_MODULE,
-               .n_voltages     = MAX77836_REGULATOR_LDO_VOLTAGE_STEPS_NUM,
-               .min_uV         = MAX77836_REGULATOR_LDO_VOLTAGE_MIN,
-               .uV_step        = MAX77836_REGULATOR_LDO_VOLTAGE_STEP,
-               .enable_reg     = MAX77836_LDO_REG_CNFG1_LDO2,
-               .enable_mask    = MAX77836_CNFG1_LDO_PWRMD_MASK,
-               .vsel_reg       = MAX77836_LDO_REG_CNFG1_LDO2,
-               .vsel_mask      = MAX77836_CNFG1_LDO_TV_MASK,
-       },
+       [MAX14577_SAFEOUT] = MAX14577_SAFEOUT_REG,
+       [MAX14577_CHARGER] = MAX14577_CHARGER_REG,
+       [MAX77836_LDO1] = MAX77836_LDO_REG(1),
+       [MAX77836_LDO2] = MAX77836_LDO_REG(2),
 };
 
 #ifdef CONFIG_OF
index 15fb1416bfbde99c9724644dd25bf106695fda97..17ccf365a9c04d9cb33a246f74777f316d76f037 100644 (file)
@@ -2,7 +2,7 @@
  * max77686.c - Regulator driver for the Maxim 77686
  *
  * Copyright (C) 2012 Samsung Electronics
- * Chiwoong Byun <woong.byun@smasung.com>
+ * Chiwoong Byun <woong.byun@samsung.com>
  * Jonghwa Lee <jonghwa3.lee@samsung.com>
  *
  * This program is free software; you can redistribute it and/or modify
@@ -88,7 +88,7 @@ enum max77686_ramp_rate {
 };
 
 struct max77686_data {
-       u64 gpio_enabled:MAX77686_REGULATORS;
+       DECLARE_BITMAP(gpio_enabled, MAX77686_REGULATORS);
 
        /* Array indexed by regulator id */
        unsigned int opmode[MAX77686_REGULATORS];
@@ -121,7 +121,7 @@ static unsigned int max77686_map_normal_mode(struct max77686_data *max77686,
        case MAX77686_BUCK8:
        case MAX77686_BUCK9:
        case MAX77686_LDO20 ... MAX77686_LDO22:
-               if (max77686->gpio_enabled & (1 << id))
+               if (test_bit(id, max77686->gpio_enabled))
                        return MAX77686_GPIO_CONTROL;
        }
 
@@ -277,7 +277,7 @@ static int max77686_of_parse_cb(struct device_node *np,
        }
 
        if (gpio_is_valid(config->ena_gpio)) {
-               max77686->gpio_enabled |= (1 << desc->id);
+               set_bit(desc->id, max77686->gpio_enabled);
 
                return regmap_update_bits(config->regmap, desc->enable_reg,
                                          desc->enable_mask,
index 9665a488e2f16e7ffde015d9682521b63f822f62..38722c8311a52bc04e1d4bac151fcc9bfec7d548 100644 (file)
 
 #define CHGIN_ILIM_STEP_20mA                   20000
 
-/* CHARGER regulator ops */
-/* CHARGER regulator uses two bits for enabling */
-static int max77693_chg_is_enabled(struct regulator_dev *rdev)
-{
-       int ret;
-       unsigned int val;
-
-       ret = regmap_read(rdev->regmap, rdev->desc->enable_reg, &val);
-       if (ret)
-               return ret;
-
-       return (val & rdev->desc->enable_mask) == rdev->desc->enable_mask;
-}
-
 /*
  * CHARGER regulator - Min : 20mA, Max : 2580mA, step : 20mA
  * 0x00, 0x01, 0x2, 0x03       = 60 mA
@@ -118,7 +104,7 @@ static struct regulator_ops max77693_safeout_ops = {
 };
 
 static struct regulator_ops max77693_charger_ops = {
-       .is_enabled             = max77693_chg_is_enabled,
+       .is_enabled             = regulator_is_enabled_regmap,
        .enable                 = regulator_enable_regmap,
        .disable                = regulator_disable_regmap,
        .get_current_limit      = max77693_chg_get_current_limit,
@@ -155,6 +141,7 @@ static const struct regulator_desc regulators[] = {
                .enable_reg = MAX77693_CHG_REG_CHG_CNFG_00,
                .enable_mask = CHG_CNFG_00_CHG_MASK |
                                CHG_CNFG_00_BUCK_MASK,
+               .enable_val = CHG_CNFG_00_CHG_MASK | CHG_CNFG_00_BUCK_MASK,
        },
 };
 
index c132ef527cdddb307cfe9567d7eadb332739e62c..f4fd0d3cfa6ed4ea5d8e0c3fa4c38748c13968f8 100644 (file)
@@ -33,21 +33,6 @@ static const unsigned int max77843_safeout_voltage_table[] = {
        3300000,
 };
 
-static int max77843_reg_is_enabled(struct regulator_dev *rdev)
-{
-       struct regmap *regmap = rdev->regmap;
-       int ret;
-       unsigned int reg;
-
-       ret = regmap_read(regmap, rdev->desc->enable_reg, &reg);
-       if (ret) {
-               dev_err(&rdev->dev, "Fialed to read charger register\n");
-               return ret;
-       }
-
-       return (reg & rdev->desc->enable_mask) == rdev->desc->enable_mask;
-}
-
 static int max77843_reg_get_current_limit(struct regulator_dev *rdev)
 {
        struct regmap *regmap = rdev->regmap;
@@ -96,7 +81,7 @@ static int max77843_reg_set_current_limit(struct regulator_dev *rdev,
 }
 
 static struct regulator_ops max77843_charger_ops = {
-       .is_enabled             = max77843_reg_is_enabled,
+       .is_enabled             = regulator_is_enabled_regmap,
        .enable                 = regulator_enable_regmap,
        .disable                = regulator_disable_regmap,
        .get_current_limit      = max77843_reg_get_current_limit,
@@ -112,37 +97,25 @@ static struct regulator_ops max77843_regulator_ops = {
        .set_voltage_sel        = regulator_set_voltage_sel_regmap,
 };
 
+#define        MAX77843_SAFEOUT(num)   { \
+       .name           = "SAFEOUT" # num, \
+       .id             = MAX77843_SAFEOUT ## num, \
+       .ops            = &max77843_regulator_ops, \
+       .of_match       = of_match_ptr("SAFEOUT" # num), \
+       .regulators_node = of_match_ptr("regulators"), \
+       .type           = REGULATOR_VOLTAGE, \
+       .owner          = THIS_MODULE, \
+       .n_voltages     = ARRAY_SIZE(max77843_safeout_voltage_table), \
+       .volt_table     = max77843_safeout_voltage_table, \
+       .enable_reg     = MAX77843_SYS_REG_SAFEOUTCTRL, \
+       .enable_mask    = MAX77843_REG_SAFEOUTCTRL_ENSAFEOUT ## num, \
+       .vsel_reg       = MAX77843_SYS_REG_SAFEOUTCTRL, \
+       .vsel_mask      = MAX77843_REG_SAFEOUTCTRL_SAFEOUT ## num ## _MASK, \
+}
+
 static const struct regulator_desc max77843_supported_regulators[] = {
-       [MAX77843_SAFEOUT1] = {
-               .name           = "SAFEOUT1",
-               .id             = MAX77843_SAFEOUT1,
-               .ops            = &max77843_regulator_ops,
-               .of_match       = of_match_ptr("SAFEOUT1"),
-               .regulators_node = of_match_ptr("regulators"),
-               .type           = REGULATOR_VOLTAGE,
-               .owner          = THIS_MODULE,
-               .n_voltages     = ARRAY_SIZE(max77843_safeout_voltage_table),
-               .volt_table     = max77843_safeout_voltage_table,
-               .enable_reg     = MAX77843_SYS_REG_SAFEOUTCTRL,
-               .enable_mask    = MAX77843_REG_SAFEOUTCTRL_ENSAFEOUT1,
-               .vsel_reg       = MAX77843_SYS_REG_SAFEOUTCTRL,
-               .vsel_mask      = MAX77843_REG_SAFEOUTCTRL_SAFEOUT1_MASK,
-       },
-       [MAX77843_SAFEOUT2] = {
-               .name           = "SAFEOUT2",
-               .id             = MAX77843_SAFEOUT2,
-               .ops            = &max77843_regulator_ops,
-               .of_match       = of_match_ptr("SAFEOUT2"),
-               .regulators_node = of_match_ptr("regulators"),
-               .type           = REGULATOR_VOLTAGE,
-               .owner          = THIS_MODULE,
-               .n_voltages     = ARRAY_SIZE(max77843_safeout_voltage_table),
-               .volt_table     = max77843_safeout_voltage_table,
-               .enable_reg     = MAX77843_SYS_REG_SAFEOUTCTRL,
-               .enable_mask    = MAX77843_REG_SAFEOUTCTRL_ENSAFEOUT2,
-               .vsel_reg       = MAX77843_SYS_REG_SAFEOUTCTRL,
-               .vsel_mask      = MAX77843_REG_SAFEOUTCTRL_SAFEOUT2_MASK,
-       },
+       [MAX77843_SAFEOUT1] = MAX77843_SAFEOUT(1),
+       [MAX77843_SAFEOUT2] = MAX77843_SAFEOUT(2),
        [MAX77843_CHARGER] = {
                .name           = "CHARGER",
                .id             = MAX77843_CHARGER,
@@ -152,7 +125,8 @@ static const struct regulator_desc max77843_supported_regulators[] = {
                .type           = REGULATOR_CURRENT,
                .owner          = THIS_MODULE,
                .enable_reg     = MAX77843_CHG_REG_CHG_CNFG_00,
-               .enable_mask    = MAX77843_CHG_MASK,
+               .enable_mask    = MAX77843_CHG_MASK | MAX77843_CHG_BUCK_MASK,
+               .enable_val     = MAX77843_CHG_MASK | MAX77843_CHG_BUCK_MASK,
        },
 };
 
index c3d55c2db593b8f37a253c4a07bd21130db99353..6f2bdad8b4d8fd6ce2e552d4c44c8cf4a3ee60ba 100644 (file)
 #include <linux/init.h>
 #include <linux/err.h>
 #include <linux/of.h>
+#include <linux/of_device.h>
 #include <linux/platform_device.h>
 #include <linux/regulator/driver.h>
 #include <linux/regulator/machine.h>
 #include <linux/regulator/max8973-regulator.h>
 #include <linux/regulator/of_regulator.h>
 #include <linux/gpio.h>
+#include <linux/of_gpio.h>
 #include <linux/i2c.h>
 #include <linux/slab.h>
 #include <linux/regmap.h>
@@ -66,6 +68,7 @@
 #define MAX8973_RAMP_25mV_PER_US                       0x1
 #define MAX8973_RAMP_50mV_PER_US                       0x2
 #define MAX8973_RAMP_200mV_PER_US                      0x3
+#define MAX8973_RAMP_MASK                              0x3
 
 /* MAX8973_CONTROL2 */
 #define MAX8973_WDTMR_ENABLE                           BIT(6)
 #define MAX8973_VOLATGE_STEP                           6250
 #define MAX8973_BUCK_N_VOLTAGE                         0x80
 
+enum device_id {
+       MAX8973,
+       MAX77621
+};
+
 /* Maxim 8973 chip information */
 struct max8973_chip {
        struct device *dev;
        struct regulator_desc desc;
        struct regmap *regmap;
        bool enable_external_control;
+       int enable_gpio;
        int dvs_gpio;
        int lru_index[MAX8973_MAX_VOUT_REG];
        int curr_vout_val[MAX8973_MAX_VOUT_REG];
        int curr_vout_reg;
        int curr_gpio_val;
-       bool valid_dvs_gpio;
        struct regulator_ops ops;
+       enum device_id id;
 };
 
 /*
@@ -174,7 +183,7 @@ static int max8973_dcdc_set_voltage_sel(struct regulator_dev *rdev,
         * If gpios are available to select the VOUT register then least
         * recently used register for new configuration.
         */
-       if (max->valid_dvs_gpio)
+       if (gpio_is_valid(max->dvs_gpio))
                found = find_voltage_set_register(max, vsel,
                                        &vout_reg, &gpio_val);
 
@@ -191,7 +200,7 @@ static int max8973_dcdc_set_voltage_sel(struct regulator_dev *rdev,
        }
 
        /* Select proper VOUT register vio gpios */
-       if (max->valid_dvs_gpio) {
+       if (gpio_is_valid(max->dvs_gpio)) {
                gpio_set_value_cansleep(max->dvs_gpio, gpio_val & 0x1);
                max->curr_gpio_val = gpio_val;
        }
@@ -242,12 +251,45 @@ static unsigned int max8973_dcdc_get_mode(struct regulator_dev *rdev)
                REGULATOR_MODE_FAST : REGULATOR_MODE_NORMAL;
 }
 
+static int max8973_set_ramp_delay(struct regulator_dev *rdev,
+               int ramp_delay)
+{
+       struct max8973_chip *max = rdev_get_drvdata(rdev);
+       unsigned int control;
+       int ret;
+       int ret_val;
+
+       /* Set ramp delay */
+       if (ramp_delay < 25000) {
+               control = MAX8973_RAMP_12mV_PER_US;
+               ret_val = 12000;
+       } else if (ramp_delay < 50000) {
+               control = MAX8973_RAMP_25mV_PER_US;
+               ret_val = 25000;
+       } else if (ramp_delay < 200000) {
+               control = MAX8973_RAMP_50mV_PER_US;
+               ret_val = 50000;
+       } else {
+               control = MAX8973_RAMP_200mV_PER_US;
+               ret_val = 200000;
+       }
+
+       ret = regmap_update_bits(max->regmap, MAX8973_CONTROL1,
+                       MAX8973_RAMP_MASK, control);
+       if (ret < 0)
+               dev_err(max->dev, "register %d update failed, %d",
+                               MAX8973_CONTROL1, ret);
+       return ret;
+}
+
 static const struct regulator_ops max8973_dcdc_ops = {
        .get_voltage_sel        = max8973_dcdc_get_voltage_sel,
        .set_voltage_sel        = max8973_dcdc_set_voltage_sel,
        .list_voltage           = regulator_list_voltage_linear,
        .set_mode               = max8973_dcdc_set_mode,
        .get_mode               = max8973_dcdc_get_mode,
+       .set_voltage_time_sel   = regulator_set_voltage_time_sel,
+       .set_ramp_delay         = max8973_set_ramp_delay,
 };
 
 static int max8973_init_dcdc(struct max8973_chip *max,
@@ -256,6 +298,29 @@ static int max8973_init_dcdc(struct max8973_chip *max,
        int ret;
        uint8_t control1 = 0;
        uint8_t control2 = 0;
+       unsigned int data;
+
+       ret = regmap_read(max->regmap, MAX8973_CONTROL1, &data);
+       if (ret < 0) {
+               dev_err(max->dev, "register %d read failed, err = %d",
+                               MAX8973_CONTROL1, ret);
+               return ret;
+       }
+       control1 = data & MAX8973_RAMP_MASK;
+       switch (control1) {
+       case MAX8973_RAMP_12mV_PER_US:
+               max->desc.ramp_delay = 12000;
+               break;
+       case MAX8973_RAMP_25mV_PER_US:
+               max->desc.ramp_delay = 25000;
+               break;
+       case MAX8973_RAMP_50mV_PER_US:
+               max->desc.ramp_delay = 50000;
+               break;
+       case MAX8973_RAMP_200mV_PER_US:
+               max->desc.ramp_delay = 200000;
+               break;
+       }
 
        if (pdata->control_flags & MAX8973_CONTROL_REMOTE_SENSE_ENABLE)
                control1 |= MAX8973_SNS_ENABLE;
@@ -266,28 +331,16 @@ static int max8973_init_dcdc(struct max8973_chip *max,
        if (pdata->control_flags & MAX8973_CONTROL_OUTPUT_ACTIVE_DISCH_ENABLE)
                control1 |= MAX8973_AD_ENABLE;
 
-       if (pdata->control_flags & MAX8973_CONTROL_BIAS_ENABLE)
+       if (pdata->control_flags & MAX8973_CONTROL_BIAS_ENABLE) {
                control1 |= MAX8973_BIAS_ENABLE;
+               max->desc.enable_time = 20;
+       } else {
+               max->desc.enable_time = 240;
+       }
 
        if (pdata->control_flags & MAX8973_CONTROL_FREQ_SHIFT_9PER_ENABLE)
                control1 |= MAX8973_FREQSHIFT_9PER;
 
-       /* Set ramp delay */
-       if (pdata->reg_init_data &&
-                       pdata->reg_init_data->constraints.ramp_delay) {
-               if (pdata->reg_init_data->constraints.ramp_delay < 25000)
-                       control1 |= MAX8973_RAMP_12mV_PER_US;
-               else if (pdata->reg_init_data->constraints.ramp_delay < 50000)
-                       control1 |= MAX8973_RAMP_25mV_PER_US;
-               else if (pdata->reg_init_data->constraints.ramp_delay < 200000)
-                       control1 |= MAX8973_RAMP_50mV_PER_US;
-               else
-                       control1 |= MAX8973_RAMP_200mV_PER_US;
-       } else {
-               control1 |= MAX8973_RAMP_12mV_PER_US;
-               max->desc.ramp_delay = 12500;
-       }
-
        if (!(pdata->control_flags & MAX8973_CONTROL_PULL_DOWN_ENABLE))
                control2 |= MAX8973_DISCH_ENBABLE;
 
@@ -344,7 +397,7 @@ static int max8973_init_dcdc(struct max8973_chip *max,
        }
 
        /* If external control is enabled then disable EN bit */
-       if (max->enable_external_control) {
+       if (max->enable_external_control && (max->id == MAX8973)) {
                ret = regmap_update_bits(max->regmap, MAX8973_VOUT,
                                                MAX8973_VOUT_ENABLE, 0);
                if (ret < 0)
@@ -361,22 +414,82 @@ static const struct regmap_config max8973_regmap_config = {
        .cache_type             = REGCACHE_RBTREE,
 };
 
+static struct max8973_regulator_platform_data *max8973_parse_dt(
+               struct device *dev)
+{
+       struct max8973_regulator_platform_data *pdata;
+       struct device_node *np = dev->of_node;
+       int ret;
+       u32 pval;
+
+       pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+       if (!pdata)
+               return NULL;
+
+       pdata->enable_ext_control = of_property_read_bool(np,
+                                               "maxim,externally-enable");
+       pdata->enable_gpio = of_get_named_gpio(np, "maxim,enable-gpio", 0);
+       pdata->dvs_gpio = of_get_named_gpio(np, "maxim,dvs-gpio", 0);
+
+       ret = of_property_read_u32(np, "maxim,dvs-default-state", &pval);
+       if (!ret)
+               pdata->dvs_def_state = pval;
+
+       if (of_property_read_bool(np, "maxim,enable-remote-sense"))
+               pdata->control_flags  |= MAX8973_CONTROL_REMOTE_SENSE_ENABLE;
+
+       if (of_property_read_bool(np, "maxim,enable-falling-slew-rate"))
+               pdata->control_flags  |=
+                               MAX8973_CONTROL_FALLING_SLEW_RATE_ENABLE;
+
+       if (of_property_read_bool(np, "maxim,enable-active-discharge"))
+               pdata->control_flags  |=
+                               MAX8973_CONTROL_OUTPUT_ACTIVE_DISCH_ENABLE;
+
+       if (of_property_read_bool(np, "maxim,enable-frequency-shift"))
+               pdata->control_flags  |= MAX8973_CONTROL_FREQ_SHIFT_9PER_ENABLE;
+
+       if (of_property_read_bool(np, "maxim,enable-bias-control"))
+               pdata->control_flags  |= MAX8973_BIAS_ENABLE;
+
+       return pdata;
+}
+
+static const struct of_device_id of_max8973_match_tbl[] = {
+       { .compatible = "maxim,max8973", .data = (void *)MAX8973, },
+       { .compatible = "maxim,max77621", .data = (void *)MAX77621, },
+       { },
+};
+MODULE_DEVICE_TABLE(of, of_max8973_match_tbl);
+
 static int max8973_probe(struct i2c_client *client,
                         const struct i2c_device_id *id)
 {
        struct max8973_regulator_platform_data *pdata;
+       struct regulator_init_data *ridata;
        struct regulator_config config = { };
        struct regulator_dev *rdev;
        struct max8973_chip *max;
+       bool pdata_from_dt = false;
+       unsigned int chip_id;
        int ret;
 
        pdata = dev_get_platdata(&client->dev);
 
-       if (!pdata && !client->dev.of_node) {
+       if (!pdata && client->dev.of_node) {
+               pdata = max8973_parse_dt(&client->dev);
+               pdata_from_dt = true;
+       }
+
+       if (!pdata) {
                dev_err(&client->dev, "No Platform data");
                return -EIO;
        }
 
+       if ((pdata->dvs_gpio == -EPROBE_DEFER) ||
+               (pdata->enable_gpio == -EPROBE_DEFER))
+               return -EPROBE_DEFER;
+
        max = devm_kzalloc(&client->dev, sizeof(*max), GFP_KERNEL);
        if (!max)
                return -ENOMEM;
@@ -388,6 +501,27 @@ static int max8973_probe(struct i2c_client *client,
                return ret;
        }
 
+       if (client->dev.of_node) {
+               const struct of_device_id *match;
+
+               match = of_match_device(of_match_ptr(of_max8973_match_tbl),
+                               &client->dev);
+               if (!match)
+                       return -ENODATA;
+               max->id = (u32)((uintptr_t)match->data);
+       } else {
+               max->id = id->driver_data;
+       }
+
+       ret = regmap_read(max->regmap, MAX8973_CHIPID1, &chip_id);
+       if (ret < 0) {
+               dev_err(&client->dev, "register CHIPID1 read failed, %d", ret);
+               return ret;
+       }
+
+       dev_info(&client->dev, "CHIP-ID OTP: 0x%02x ID_M: 0x%02x\n",
+                       (chip_id >> 4) & 0xF, (chip_id >> 1) & 0x7);
+
        i2c_set_clientdata(client, max);
        max->ops = max8973_dcdc_ops;
        max->dev = &client->dev;
@@ -400,23 +534,14 @@ static int max8973_probe(struct i2c_client *client,
        max->desc.uV_step = MAX8973_VOLATGE_STEP;
        max->desc.n_voltages = MAX8973_BUCK_N_VOLTAGE;
 
-       if (!pdata || !pdata->enable_ext_control) {
-               max->desc.enable_reg = MAX8973_VOUT;
-               max->desc.enable_mask = MAX8973_VOUT_ENABLE;
-               max->ops.enable = regulator_enable_regmap;
-               max->ops.disable = regulator_disable_regmap;
-               max->ops.is_enabled = regulator_is_enabled_regmap;
-       }
+       max->dvs_gpio = (pdata->dvs_gpio) ? pdata->dvs_gpio : -EINVAL;
+       max->enable_gpio = (pdata->enable_gpio) ? pdata->enable_gpio : -EINVAL;
+       max->enable_external_control = pdata->enable_ext_control;
+       max->curr_gpio_val = pdata->dvs_def_state;
+       max->curr_vout_reg = MAX8973_VOUT + pdata->dvs_def_state;
 
-       if (pdata) {
-               max->dvs_gpio = pdata->dvs_gpio;
-               max->enable_external_control = pdata->enable_ext_control;
-               max->curr_gpio_val = pdata->dvs_def_state;
-               max->curr_vout_reg = MAX8973_VOUT + pdata->dvs_def_state;
-       } else {
-               max->dvs_gpio = -EINVAL;
-               max->curr_vout_reg = MAX8973_VOUT;
-       }
+       if (gpio_is_valid(max->enable_gpio))
+               max->enable_external_control = true;
 
        max->lru_index[0] = max->curr_vout_reg;
 
@@ -434,7 +559,6 @@ static int max8973_probe(struct i2c_client *client,
                                max->dvs_gpio, ret);
                        return ret;
                }
-               max->valid_dvs_gpio = true;
 
                /*
                 * Initialize the lru index with vout_reg id
@@ -444,22 +568,64 @@ static int max8973_probe(struct i2c_client *client,
                        max->lru_index[i] = i;
                max->lru_index[0] = max->curr_vout_reg;
                max->lru_index[max->curr_vout_reg] = 0;
-       } else {
-               max->valid_dvs_gpio = false;
        }
 
-       if (pdata) {
-               ret = max8973_init_dcdc(max, pdata);
-               if (ret < 0) {
-                       dev_err(max->dev, "Max8973 Init failed, err = %d\n", ret);
-                       return ret;
+       if (pdata_from_dt)
+               pdata->reg_init_data = of_get_regulator_init_data(&client->dev,
+                                       client->dev.of_node, &max->desc);
+
+       ridata = pdata->reg_init_data;
+       switch (max->id) {
+       case MAX8973:
+               if (!pdata->enable_ext_control) {
+                       max->desc.enable_reg = MAX8973_VOUT;
+                       max->desc.enable_mask = MAX8973_VOUT_ENABLE;
+                       max->ops.enable = regulator_enable_regmap;
+                       max->ops.disable = regulator_disable_regmap;
+                       max->ops.is_enabled = regulator_is_enabled_regmap;
+                       break;
+               }
+
+               if (gpio_is_valid(max->enable_gpio)) {
+                       config.ena_gpio_flags = GPIOF_OUT_INIT_LOW;
+                       if (ridata && (ridata->constraints.always_on ||
+                                       ridata->constraints.boot_on))
+                               config.ena_gpio_flags = GPIOF_OUT_INIT_HIGH;
+                       config.ena_gpio = max->enable_gpio;
                }
+               break;
+
+       case MAX77621:
+               if (gpio_is_valid(max->enable_gpio)) {
+                       ret = devm_gpio_request_one(&client->dev,
+                                       max->enable_gpio, GPIOF_OUT_INIT_HIGH,
+                                       "max8973-en-gpio");
+                       if (ret) {
+                               dev_err(&client->dev,
+                                       "gpio_request for gpio %d failed: %d\n",
+                                       max->enable_gpio, ret);
+                               return ret;
+                       }
+               }
+
+               max->desc.enable_reg = MAX8973_VOUT;
+               max->desc.enable_mask = MAX8973_VOUT_ENABLE;
+               max->ops.enable = regulator_enable_regmap;
+               max->ops.disable = regulator_disable_regmap;
+               max->ops.is_enabled = regulator_is_enabled_regmap;
+               break;
+       default:
+               break;
+       }
+
+       ret = max8973_init_dcdc(max, pdata);
+       if (ret < 0) {
+               dev_err(max->dev, "Max8973 Init failed, err = %d\n", ret);
+               return ret;
        }
 
        config.dev = &client->dev;
-       config.init_data = pdata ? pdata->reg_init_data :
-               of_get_regulator_init_data(&client->dev, client->dev.of_node,
-                                          &max->desc);
+       config.init_data = pdata->reg_init_data;
        config.driver_data = max;
        config.of_node = client->dev.of_node;
        config.regmap = max->regmap;
@@ -476,15 +642,16 @@ static int max8973_probe(struct i2c_client *client,
 }
 
 static const struct i2c_device_id max8973_id[] = {
-       {.name = "max8973",},
+       {.name = "max8973", .driver_data = MAX8973},
+       {.name = "max77621", .driver_data = MAX77621},
        {},
 };
-
 MODULE_DEVICE_TABLE(i2c, max8973_id);
 
 static struct i2c_driver max8973_i2c_driver = {
        .driver = {
                .name = "max8973",
+               .of_match_table = of_max8973_match_tbl,
                .owner = THIS_MODULE,
        },
        .probe = max8973_probe,
index 24e812c48d93076a36039e991c51bb371fb26d6e..b1c485b24ab246234edbab2dfb4217e4fe1df833 100644 (file)
@@ -58,6 +58,10 @@ static void of_get_regulation_constraints(struct device_node *np,
        if (!of_property_read_u32(np, "regulator-max-microamp", &pval))
                constraints->max_uA = pval;
 
+       if (!of_property_read_u32(np, "regulator-input-current-limit-microamp",
+                                 &pval))
+               constraints->ilim_uA = pval;
+
        /* Current change possible? */
        if (constraints->min_uA != constraints->max_uA)
                constraints->valid_ops_mask |= REGULATOR_CHANGE_CURRENT;
@@ -67,6 +71,8 @@ static void of_get_regulation_constraints(struct device_node *np,
        if (!constraints->always_on) /* status change should be possible. */
                constraints->valid_ops_mask |= REGULATOR_CHANGE_STATUS;
 
+       constraints->pull_down = of_property_read_bool(np, "regulator-pull-down");
+
        if (of_property_read_bool(np, "regulator-allow-bypass"))
                constraints->valid_ops_mask |= REGULATOR_CHANGE_BYPASS;
 
@@ -82,6 +88,9 @@ static void of_get_regulation_constraints(struct device_node *np,
        if (!ret)
                constraints->enable_time = pval;
 
+       constraints->soft_start = of_property_read_bool(np,
+                                       "regulator-soft-start");
+
        if (!of_property_read_u32(np, "regulator-initial-mode", &pval)) {
                if (desc && desc->of_map_mode) {
                        ret = desc->of_map_mode(pval);
@@ -95,6 +104,9 @@ static void of_get_regulation_constraints(struct device_node *np,
                }
        }
 
+       if (!of_property_read_u32(np, "regulator-system-load", &pval))
+               constraints->system_load = pval;
+
        for (i = 0; i < ARRAY_SIZE(regulator_states); i++) {
                switch (i) {
                case PM_SUSPEND_MEM:
@@ -108,7 +120,7 @@ static void of_get_regulation_constraints(struct device_node *np,
                case PM_SUSPEND_STANDBY:
                default:
                        continue;
-               };
+               }
 
                suspend_np = of_get_child_by_name(np, regulator_states[i]);
                if (!suspend_np || !suspend_state)
@@ -292,7 +304,7 @@ struct regulator_init_data *regulator_of_get_init_data(struct device *dev,
                return NULL;
        }
 
-       for_each_child_of_node(search, child) {
+       for_each_available_child_of_node(search, child) {
                name = of_get_property(child, "regulator-compatible", NULL);
                if (!name)
                        name = child->name;
index 253833ae35f357602c680618c0a0b54315963337..ffa96124a5e7206f4f86f7025098a3744f9ec315 100644 (file)
 #include <linux/pwm.h>
 
 struct pwm_regulator_data {
-       struct regulator_desc desc;
        struct pwm_voltages *duty_cycle_table;
        struct pwm_device *pwm;
-       bool enabled;
        int state;
 };
 
@@ -33,17 +31,17 @@ struct pwm_voltages {
        unsigned int dutycycle;
 };
 
-static int pwm_regulator_get_voltage_sel(struct regulator_dev *dev)
+static int pwm_regulator_get_voltage_sel(struct regulator_dev *rdev)
 {
-       struct pwm_regulator_data *drvdata = rdev_get_drvdata(dev);
+       struct pwm_regulator_data *drvdata = rdev_get_drvdata(rdev);
 
        return drvdata->state;
 }
 
-static int pwm_regulator_set_voltage_sel(struct regulator_dev *dev,
+static int pwm_regulator_set_voltage_sel(struct regulator_dev *rdev,
                                         unsigned selector)
 {
-       struct pwm_regulator_data *drvdata = rdev_get_drvdata(dev);
+       struct pwm_regulator_data *drvdata = rdev_get_drvdata(rdev);
        unsigned int pwm_reg_period;
        int dutycycle;
        int ret;
@@ -55,30 +53,27 @@ static int pwm_regulator_set_voltage_sel(struct regulator_dev *dev,
 
        ret = pwm_config(drvdata->pwm, dutycycle, pwm_reg_period);
        if (ret) {
-               dev_err(&dev->dev, "Failed to configure PWM\n");
+               dev_err(&rdev->dev, "Failed to configure PWM\n");
                return ret;
        }
 
        drvdata->state = selector;
 
-       if (!drvdata->enabled) {
-               ret = pwm_enable(drvdata->pwm);
-               if (ret) {
-                       dev_err(&dev->dev, "Failed to enable PWM\n");
-                       return ret;
-               }
-               drvdata->enabled = true;
+       ret = pwm_enable(drvdata->pwm);
+       if (ret) {
+               dev_err(&rdev->dev, "Failed to enable PWM\n");
+               return ret;
        }
 
        return 0;
 }
 
-static int pwm_regulator_list_voltage(struct regulator_dev *dev,
+static int pwm_regulator_list_voltage(struct regulator_dev *rdev,
                                      unsigned selector)
 {
-       struct pwm_regulator_data *drvdata = rdev_get_drvdata(dev);
+       struct pwm_regulator_data *drvdata = rdev_get_drvdata(rdev);
 
-       if (selector >= drvdata->desc.n_voltages)
+       if (selector >= rdev->desc->n_voltages)
                return -EINVAL;
 
        return drvdata->duty_cycle_table[selector].uV;
@@ -91,7 +86,7 @@ static struct regulator_ops pwm_regulator_voltage_ops = {
        .map_voltage     = regulator_map_voltage_iterate,
 };
 
-static const struct regulator_desc pwm_regulator_desc = {
+static struct regulator_desc pwm_regulator_desc = {
        .name           = "pwm-regulator",
        .ops            = &pwm_regulator_voltage_ops,
        .type           = REGULATOR_VOLTAGE,
@@ -117,8 +112,6 @@ static int pwm_regulator_probe(struct platform_device *pdev)
        if (!drvdata)
                return -ENOMEM;
 
-       memcpy(&drvdata->desc, &pwm_regulator_desc, sizeof(pwm_regulator_desc));
-
        /* determine the number of voltage-table */
        prop = of_find_property(np, "voltage-table", &length);
        if (!prop) {
@@ -133,7 +126,7 @@ static int pwm_regulator_probe(struct platform_device *pdev)
                return -EINVAL;
        }
 
-       drvdata->desc.n_voltages = length / sizeof(*drvdata->duty_cycle_table);
+       pwm_regulator_desc.n_voltages = length / sizeof(*drvdata->duty_cycle_table);
 
        drvdata->duty_cycle_table = devm_kzalloc(&pdev->dev,
                                                 length, GFP_KERNEL);
@@ -150,7 +143,7 @@ static int pwm_regulator_probe(struct platform_device *pdev)
        }
 
        config.init_data = of_get_regulator_init_data(&pdev->dev, np,
-                                                     &drvdata->desc);
+                                                     &pwm_regulator_desc);
        if (!config.init_data)
                return -ENOMEM;
 
@@ -165,10 +158,10 @@ static int pwm_regulator_probe(struct platform_device *pdev)
        }
 
        regulator = devm_regulator_register(&pdev->dev,
-                                           &drvdata->desc, &config);
+                                           &pwm_regulator_desc, &config);
        if (IS_ERR(regulator)) {
                dev_err(&pdev->dev, "Failed to register regulator %s\n",
-                       drvdata->desc.name);
+                       pwm_regulator_desc.name);
                return PTR_ERR(regulator);
        }
 
diff --git a/drivers/regulator/qcom_spmi-regulator.c b/drivers/regulator/qcom_spmi-regulator.c
new file mode 100644 (file)
index 0000000..850a30a
--- /dev/null
@@ -0,0 +1,1435 @@
+/*
+ * Copyright (c) 2012-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/module.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/bitops.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/ktime.h>
+#include <linux/regulator/driver.h>
+#include <linux/regmap.h>
+#include <linux/list.h>
+
+/* These types correspond to unique register layouts. */
+enum spmi_regulator_logical_type {
+       SPMI_REGULATOR_LOGICAL_TYPE_SMPS,
+       SPMI_REGULATOR_LOGICAL_TYPE_LDO,
+       SPMI_REGULATOR_LOGICAL_TYPE_VS,
+       SPMI_REGULATOR_LOGICAL_TYPE_BOOST,
+       SPMI_REGULATOR_LOGICAL_TYPE_FTSMPS,
+       SPMI_REGULATOR_LOGICAL_TYPE_BOOST_BYP,
+       SPMI_REGULATOR_LOGICAL_TYPE_LN_LDO,
+       SPMI_REGULATOR_LOGICAL_TYPE_ULT_LO_SMPS,
+       SPMI_REGULATOR_LOGICAL_TYPE_ULT_HO_SMPS,
+       SPMI_REGULATOR_LOGICAL_TYPE_ULT_LDO,
+};
+
+enum spmi_regulator_type {
+       SPMI_REGULATOR_TYPE_BUCK                = 0x03,
+       SPMI_REGULATOR_TYPE_LDO                 = 0x04,
+       SPMI_REGULATOR_TYPE_VS                  = 0x05,
+       SPMI_REGULATOR_TYPE_BOOST               = 0x1b,
+       SPMI_REGULATOR_TYPE_FTS                 = 0x1c,
+       SPMI_REGULATOR_TYPE_BOOST_BYP           = 0x1f,
+       SPMI_REGULATOR_TYPE_ULT_LDO             = 0x21,
+       SPMI_REGULATOR_TYPE_ULT_BUCK            = 0x22,
+};
+
+enum spmi_regulator_subtype {
+       SPMI_REGULATOR_SUBTYPE_GP_CTL           = 0x08,
+       SPMI_REGULATOR_SUBTYPE_RF_CTL           = 0x09,
+       SPMI_REGULATOR_SUBTYPE_N50              = 0x01,
+       SPMI_REGULATOR_SUBTYPE_N150             = 0x02,
+       SPMI_REGULATOR_SUBTYPE_N300             = 0x03,
+       SPMI_REGULATOR_SUBTYPE_N600             = 0x04,
+       SPMI_REGULATOR_SUBTYPE_N1200            = 0x05,
+       SPMI_REGULATOR_SUBTYPE_N600_ST          = 0x06,
+       SPMI_REGULATOR_SUBTYPE_N1200_ST         = 0x07,
+       SPMI_REGULATOR_SUBTYPE_N900_ST          = 0x14,
+       SPMI_REGULATOR_SUBTYPE_N300_ST          = 0x15,
+       SPMI_REGULATOR_SUBTYPE_P50              = 0x08,
+       SPMI_REGULATOR_SUBTYPE_P150             = 0x09,
+       SPMI_REGULATOR_SUBTYPE_P300             = 0x0a,
+       SPMI_REGULATOR_SUBTYPE_P600             = 0x0b,
+       SPMI_REGULATOR_SUBTYPE_P1200            = 0x0c,
+       SPMI_REGULATOR_SUBTYPE_LN               = 0x10,
+       SPMI_REGULATOR_SUBTYPE_LV_P50           = 0x28,
+       SPMI_REGULATOR_SUBTYPE_LV_P150          = 0x29,
+       SPMI_REGULATOR_SUBTYPE_LV_P300          = 0x2a,
+       SPMI_REGULATOR_SUBTYPE_LV_P600          = 0x2b,
+       SPMI_REGULATOR_SUBTYPE_LV_P1200         = 0x2c,
+       SPMI_REGULATOR_SUBTYPE_LV_P450          = 0x2d,
+       SPMI_REGULATOR_SUBTYPE_LV100            = 0x01,
+       SPMI_REGULATOR_SUBTYPE_LV300            = 0x02,
+       SPMI_REGULATOR_SUBTYPE_MV300            = 0x08,
+       SPMI_REGULATOR_SUBTYPE_MV500            = 0x09,
+       SPMI_REGULATOR_SUBTYPE_HDMI             = 0x10,
+       SPMI_REGULATOR_SUBTYPE_OTG              = 0x11,
+       SPMI_REGULATOR_SUBTYPE_5V_BOOST         = 0x01,
+       SPMI_REGULATOR_SUBTYPE_FTS_CTL          = 0x08,
+       SPMI_REGULATOR_SUBTYPE_FTS2p5_CTL       = 0x09,
+       SPMI_REGULATOR_SUBTYPE_BB_2A            = 0x01,
+       SPMI_REGULATOR_SUBTYPE_ULT_HF_CTL1      = 0x0d,
+       SPMI_REGULATOR_SUBTYPE_ULT_HF_CTL2      = 0x0e,
+       SPMI_REGULATOR_SUBTYPE_ULT_HF_CTL3      = 0x0f,
+       SPMI_REGULATOR_SUBTYPE_ULT_HF_CTL4      = 0x10,
+};
+
+enum spmi_common_regulator_registers {
+       SPMI_COMMON_REG_DIG_MAJOR_REV           = 0x01,
+       SPMI_COMMON_REG_TYPE                    = 0x04,
+       SPMI_COMMON_REG_SUBTYPE                 = 0x05,
+       SPMI_COMMON_REG_VOLTAGE_RANGE           = 0x40,
+       SPMI_COMMON_REG_VOLTAGE_SET             = 0x41,
+       SPMI_COMMON_REG_MODE                    = 0x45,
+       SPMI_COMMON_REG_ENABLE                  = 0x46,
+       SPMI_COMMON_REG_PULL_DOWN               = 0x48,
+       SPMI_COMMON_REG_SOFT_START              = 0x4c,
+       SPMI_COMMON_REG_STEP_CTRL               = 0x61,
+};
+
+enum spmi_vs_registers {
+       SPMI_VS_REG_OCP                         = 0x4a,
+       SPMI_VS_REG_SOFT_START                  = 0x4c,
+};
+
+enum spmi_boost_registers {
+       SPMI_BOOST_REG_CURRENT_LIMIT            = 0x4a,
+};
+
+enum spmi_boost_byp_registers {
+       SPMI_BOOST_BYP_REG_CURRENT_LIMIT        = 0x4b,
+};
+
+/* Used for indexing into ctrl_reg.  These are offets from 0x40 */
+enum spmi_common_control_register_index {
+       SPMI_COMMON_IDX_VOLTAGE_RANGE           = 0,
+       SPMI_COMMON_IDX_VOLTAGE_SET             = 1,
+       SPMI_COMMON_IDX_MODE                    = 5,
+       SPMI_COMMON_IDX_ENABLE                  = 6,
+};
+
+/* Common regulator control register layout */
+#define SPMI_COMMON_ENABLE_MASK                        0x80
+#define SPMI_COMMON_ENABLE                     0x80
+#define SPMI_COMMON_DISABLE                    0x00
+#define SPMI_COMMON_ENABLE_FOLLOW_HW_EN3_MASK  0x08
+#define SPMI_COMMON_ENABLE_FOLLOW_HW_EN2_MASK  0x04
+#define SPMI_COMMON_ENABLE_FOLLOW_HW_EN1_MASK  0x02
+#define SPMI_COMMON_ENABLE_FOLLOW_HW_EN0_MASK  0x01
+#define SPMI_COMMON_ENABLE_FOLLOW_ALL_MASK     0x0f
+
+/* Common regulator mode register layout */
+#define SPMI_COMMON_MODE_HPM_MASK              0x80
+#define SPMI_COMMON_MODE_AUTO_MASK             0x40
+#define SPMI_COMMON_MODE_BYPASS_MASK           0x20
+#define SPMI_COMMON_MODE_FOLLOW_AWAKE_MASK     0x10
+#define SPMI_COMMON_MODE_FOLLOW_HW_EN3_MASK    0x08
+#define SPMI_COMMON_MODE_FOLLOW_HW_EN2_MASK    0x04
+#define SPMI_COMMON_MODE_FOLLOW_HW_EN1_MASK    0x02
+#define SPMI_COMMON_MODE_FOLLOW_HW_EN0_MASK    0x01
+#define SPMI_COMMON_MODE_FOLLOW_ALL_MASK       0x1f
+
+/* Common regulator pull down control register layout */
+#define SPMI_COMMON_PULL_DOWN_ENABLE_MASK      0x80
+
+/* LDO regulator current limit control register layout */
+#define SPMI_LDO_CURRENT_LIMIT_ENABLE_MASK     0x80
+
+/* LDO regulator soft start control register layout */
+#define SPMI_LDO_SOFT_START_ENABLE_MASK                0x80
+
+/* VS regulator over current protection control register layout */
+#define SPMI_VS_OCP_OVERRIDE                   0x01
+#define SPMI_VS_OCP_NO_OVERRIDE                        0x00
+
+/* VS regulator soft start control register layout */
+#define SPMI_VS_SOFT_START_ENABLE_MASK         0x80
+#define SPMI_VS_SOFT_START_SEL_MASK            0x03
+
+/* Boost regulator current limit control register layout */
+#define SPMI_BOOST_CURRENT_LIMIT_ENABLE_MASK   0x80
+#define SPMI_BOOST_CURRENT_LIMIT_MASK          0x07
+
+#define SPMI_VS_OCP_DEFAULT_MAX_RETRIES                10
+#define SPMI_VS_OCP_DEFAULT_RETRY_DELAY_MS     30
+#define SPMI_VS_OCP_FALL_DELAY_US              90
+#define SPMI_VS_OCP_FAULT_DELAY_US             20000
+
+#define SPMI_FTSMPS_STEP_CTRL_STEP_MASK                0x18
+#define SPMI_FTSMPS_STEP_CTRL_STEP_SHIFT       3
+#define SPMI_FTSMPS_STEP_CTRL_DELAY_MASK       0x07
+#define SPMI_FTSMPS_STEP_CTRL_DELAY_SHIFT      0
+
+/* Clock rate in kHz of the FTSMPS regulator reference clock. */
+#define SPMI_FTSMPS_CLOCK_RATE         19200
+
+/* Minimum voltage stepper delay for each step. */
+#define SPMI_FTSMPS_STEP_DELAY         8
+
+/*
+ * The ratio SPMI_FTSMPS_STEP_MARGIN_NUM/SPMI_FTSMPS_STEP_MARGIN_DEN is used to
+ * adjust the step rate in order to account for oscillator variance.
+ */
+#define SPMI_FTSMPS_STEP_MARGIN_NUM    4
+#define SPMI_FTSMPS_STEP_MARGIN_DEN    5
+
+/*
+ * This voltage in uV is returned by get_voltage functions when there is no way
+ * to determine the current voltage level.  It is needed because the regulator
+ * framework treats a 0 uV voltage as an error.
+ */
+#define VOLTAGE_UNKNOWN 1
+
+/* VSET value to decide the range of ULT SMPS */
+#define ULT_SMPS_RANGE_SPLIT 0x60
+
+/**
+ * struct spmi_voltage_range - regulator set point voltage mapping description
+ * @min_uV:            Minimum programmable output voltage resulting from
+ *                     set point register value 0x00
+ * @max_uV:            Maximum programmable output voltage
+ * @step_uV:           Output voltage increase resulting from the set point
+ *                     register value increasing by 1
+ * @set_point_min_uV:  Minimum allowed voltage
+ * @set_point_max_uV:  Maximum allowed voltage.  This may be tweaked in order
+ *                     to pick which range should be used in the case of
+ *                     overlapping set points.
+ * @n_voltages:                Number of preferred voltage set points present in this
+ *                     range
+ * @range_sel:         Voltage range register value corresponding to this range
+ *
+ * The following relationships must be true for the values used in this struct:
+ * (max_uV - min_uV) % step_uV == 0
+ * (set_point_min_uV - min_uV) % step_uV == 0*
+ * (set_point_max_uV - min_uV) % step_uV == 0*
+ * n_voltages = (set_point_max_uV - set_point_min_uV) / step_uV + 1
+ *
+ * *Note, set_point_min_uV == set_point_max_uV == 0 is allowed in order to
+ * specify that the voltage range has meaning, but is not preferred.
+ */
+struct spmi_voltage_range {
+       int                                     min_uV;
+       int                                     max_uV;
+       int                                     step_uV;
+       int                                     set_point_min_uV;
+       int                                     set_point_max_uV;
+       unsigned                                n_voltages;
+       u8                                      range_sel;
+};
+
+/*
+ * The ranges specified in the spmi_voltage_set_points struct must be listed
+ * so that range[i].set_point_max_uV < range[i+1].set_point_min_uV.
+ */
+struct spmi_voltage_set_points {
+       struct spmi_voltage_range               *range;
+       int                                     count;
+       unsigned                                n_voltages;
+};
+
+struct spmi_regulator {
+       struct regulator_desc                   desc;
+       struct device                           *dev;
+       struct delayed_work                     ocp_work;
+       struct regmap                           *regmap;
+       struct spmi_voltage_set_points          *set_points;
+       enum spmi_regulator_logical_type        logical_type;
+       int                                     ocp_irq;
+       int                                     ocp_count;
+       int                                     ocp_max_retries;
+       int                                     ocp_retry_delay_ms;
+       int                                     hpm_min_load;
+       int                                     slew_rate;
+       ktime_t                                 vs_enable_time;
+       u16                                     base;
+       struct list_head                        node;
+};
+
+struct spmi_regulator_mapping {
+       enum spmi_regulator_type                type;
+       enum spmi_regulator_subtype             subtype;
+       enum spmi_regulator_logical_type        logical_type;
+       u32                                     revision_min;
+       u32                                     revision_max;
+       struct regulator_ops                    *ops;
+       struct spmi_voltage_set_points          *set_points;
+       int                                     hpm_min_load;
+};
+
+struct spmi_regulator_data {
+       const char                      *name;
+       u16                             base;
+       const char                      *supply;
+       const char                      *ocp;
+       u16                             force_type;
+};
+
+#define SPMI_VREG(_type, _subtype, _dig_major_min, _dig_major_max, \
+                     _logical_type, _ops_val, _set_points_val, _hpm_min_load) \
+       { \
+               .type           = SPMI_REGULATOR_TYPE_##_type, \
+               .subtype        = SPMI_REGULATOR_SUBTYPE_##_subtype, \
+               .revision_min   = _dig_major_min, \
+               .revision_max   = _dig_major_max, \
+               .logical_type   = SPMI_REGULATOR_LOGICAL_TYPE_##_logical_type, \
+               .ops            = &spmi_##_ops_val##_ops, \
+               .set_points     = &_set_points_val##_set_points, \
+               .hpm_min_load   = _hpm_min_load, \
+       }
+
+#define SPMI_VREG_VS(_subtype, _dig_major_min, _dig_major_max) \
+       { \
+               .type           = SPMI_REGULATOR_TYPE_VS, \
+               .subtype        = SPMI_REGULATOR_SUBTYPE_##_subtype, \
+               .revision_min   = _dig_major_min, \
+               .revision_max   = _dig_major_max, \
+               .logical_type   = SPMI_REGULATOR_LOGICAL_TYPE_VS, \
+               .ops            = &spmi_vs_ops, \
+       }
+
+#define SPMI_VOLTAGE_RANGE(_range_sel, _min_uV, _set_point_min_uV, \
+                       _set_point_max_uV, _max_uV, _step_uV) \
+       { \
+               .min_uV                 = _min_uV, \
+               .max_uV                 = _max_uV, \
+               .set_point_min_uV       = _set_point_min_uV, \
+               .set_point_max_uV       = _set_point_max_uV, \
+               .step_uV                = _step_uV, \
+               .range_sel              = _range_sel, \
+       }
+
+#define DEFINE_SPMI_SET_POINTS(name) \
+struct spmi_voltage_set_points name##_set_points = { \
+       .range  = name##_ranges, \
+       .count  = ARRAY_SIZE(name##_ranges), \
+}
+
+/*
+ * These tables contain the physically available PMIC regulator voltage setpoint
+ * ranges.  Where two ranges overlap in hardware, one of the ranges is trimmed
+ * to ensure that the setpoints available to software are monotonically
+ * increasing and unique.  The set_voltage callback functions expect these
+ * properties to hold.
+ */
+static struct spmi_voltage_range pldo_ranges[] = {
+       SPMI_VOLTAGE_RANGE(2,  750000,  750000, 1537500, 1537500, 12500),
+       SPMI_VOLTAGE_RANGE(3, 1500000, 1550000, 3075000, 3075000, 25000),
+       SPMI_VOLTAGE_RANGE(4, 1750000, 3100000, 4900000, 4900000, 50000),
+};
+
+static struct spmi_voltage_range nldo1_ranges[] = {
+       SPMI_VOLTAGE_RANGE(2,  750000,  750000, 1537500, 1537500, 12500),
+};
+
+static struct spmi_voltage_range nldo2_ranges[] = {
+       SPMI_VOLTAGE_RANGE(0,  375000,       0,       0, 1537500, 12500),
+       SPMI_VOLTAGE_RANGE(1,  375000,  375000,  768750,  768750,  6250),
+       SPMI_VOLTAGE_RANGE(2,  750000,  775000, 1537500, 1537500, 12500),
+};
+
+static struct spmi_voltage_range nldo3_ranges[] = {
+       SPMI_VOLTAGE_RANGE(0,  375000,  375000, 1537500, 1537500, 12500),
+       SPMI_VOLTAGE_RANGE(1,  375000,       0,       0, 1537500, 12500),
+       SPMI_VOLTAGE_RANGE(2,  750000,       0,       0, 1537500, 12500),
+};
+
+static struct spmi_voltage_range ln_ldo_ranges[] = {
+       SPMI_VOLTAGE_RANGE(1,  690000,  690000, 1110000, 1110000, 60000),
+       SPMI_VOLTAGE_RANGE(0, 1380000, 1380000, 2220000, 2220000, 120000),
+};
+
+static struct spmi_voltage_range smps_ranges[] = {
+       SPMI_VOLTAGE_RANGE(0,  375000,  375000, 1562500, 1562500, 12500),
+       SPMI_VOLTAGE_RANGE(1, 1550000, 1575000, 3125000, 3125000, 25000),
+};
+
+static struct spmi_voltage_range ftsmps_ranges[] = {
+       SPMI_VOLTAGE_RANGE(0,       0,  350000, 1275000, 1275000,  5000),
+       SPMI_VOLTAGE_RANGE(1,       0, 1280000, 2040000, 2040000, 10000),
+};
+
+static struct spmi_voltage_range ftsmps2p5_ranges[] = {
+       SPMI_VOLTAGE_RANGE(0,   80000,  350000, 1355000, 1355000,  5000),
+       SPMI_VOLTAGE_RANGE(1,  160000, 1360000, 2200000, 2200000, 10000),
+};
+
+static struct spmi_voltage_range boost_ranges[] = {
+       SPMI_VOLTAGE_RANGE(0, 4000000, 4000000, 5550000, 5550000, 50000),
+};
+
+static struct spmi_voltage_range boost_byp_ranges[] = {
+       SPMI_VOLTAGE_RANGE(0, 2500000, 2500000, 5200000, 5650000, 50000),
+};
+
+static struct spmi_voltage_range ult_lo_smps_ranges[] = {
+       SPMI_VOLTAGE_RANGE(0,  375000,  375000, 1562500, 1562500, 12500),
+       SPMI_VOLTAGE_RANGE(1,  750000,       0,       0, 1525000, 25000),
+};
+
+static struct spmi_voltage_range ult_ho_smps_ranges[] = {
+       SPMI_VOLTAGE_RANGE(0, 1550000, 1550000, 2325000, 2325000, 25000),
+};
+
+static struct spmi_voltage_range ult_nldo_ranges[] = {
+       SPMI_VOLTAGE_RANGE(0,  375000,  375000, 1537500, 1537500, 12500),
+};
+
+static struct spmi_voltage_range ult_pldo_ranges[] = {
+       SPMI_VOLTAGE_RANGE(0, 1750000, 1750000, 3337500, 3337500, 12500),
+};
+
+static DEFINE_SPMI_SET_POINTS(pldo);
+static DEFINE_SPMI_SET_POINTS(nldo1);
+static DEFINE_SPMI_SET_POINTS(nldo2);
+static DEFINE_SPMI_SET_POINTS(nldo3);
+static DEFINE_SPMI_SET_POINTS(ln_ldo);
+static DEFINE_SPMI_SET_POINTS(smps);
+static DEFINE_SPMI_SET_POINTS(ftsmps);
+static DEFINE_SPMI_SET_POINTS(ftsmps2p5);
+static DEFINE_SPMI_SET_POINTS(boost);
+static DEFINE_SPMI_SET_POINTS(boost_byp);
+static DEFINE_SPMI_SET_POINTS(ult_lo_smps);
+static DEFINE_SPMI_SET_POINTS(ult_ho_smps);
+static DEFINE_SPMI_SET_POINTS(ult_nldo);
+static DEFINE_SPMI_SET_POINTS(ult_pldo);
+
+static inline int spmi_vreg_read(struct spmi_regulator *vreg, u16 addr, u8 *buf,
+                                int len)
+{
+       return regmap_bulk_read(vreg->regmap, vreg->base + addr, buf, len);
+}
+
+static inline int spmi_vreg_write(struct spmi_regulator *vreg, u16 addr,
+                               u8 *buf, int len)
+{
+       return regmap_bulk_write(vreg->regmap, vreg->base + addr, buf, len);
+}
+
+static int spmi_vreg_update_bits(struct spmi_regulator *vreg, u16 addr, u8 val,
+               u8 mask)
+{
+       return regmap_update_bits(vreg->regmap, vreg->base + addr, mask, val);
+}
+
+static int spmi_regulator_common_is_enabled(struct regulator_dev *rdev)
+{
+       struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+       u8 reg;
+
+       spmi_vreg_read(vreg, SPMI_COMMON_REG_ENABLE, &reg, 1);
+
+       return (reg & SPMI_COMMON_ENABLE_MASK) == SPMI_COMMON_ENABLE;
+}
+
+static int spmi_regulator_common_enable(struct regulator_dev *rdev)
+{
+       struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+
+       return spmi_vreg_update_bits(vreg, SPMI_COMMON_REG_ENABLE,
+               SPMI_COMMON_ENABLE, SPMI_COMMON_ENABLE_MASK);
+}
+
+static int spmi_regulator_vs_enable(struct regulator_dev *rdev)
+{
+       struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+
+       if (vreg->ocp_irq) {
+               vreg->ocp_count = 0;
+               vreg->vs_enable_time = ktime_get();
+       }
+
+       return spmi_regulator_common_enable(rdev);
+}
+
+static int spmi_regulator_common_disable(struct regulator_dev *rdev)
+{
+       struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+
+       return spmi_vreg_update_bits(vreg, SPMI_COMMON_REG_ENABLE,
+               SPMI_COMMON_DISABLE, SPMI_COMMON_ENABLE_MASK);
+}
+
+static int spmi_regulator_select_voltage(struct spmi_regulator *vreg,
+               int min_uV, int max_uV, u8 *range_sel, u8 *voltage_sel,
+               unsigned *selector)
+{
+       const struct spmi_voltage_range *range;
+       int uV = min_uV;
+       int lim_min_uV, lim_max_uV, i, range_id, range_max_uV;
+
+       /* Check if request voltage is outside of physically settable range. */
+       lim_min_uV = vreg->set_points->range[0].set_point_min_uV;
+       lim_max_uV =
+         vreg->set_points->range[vreg->set_points->count - 1].set_point_max_uV;
+
+       if (uV < lim_min_uV && max_uV >= lim_min_uV)
+               uV = lim_min_uV;
+
+       if (uV < lim_min_uV || uV > lim_max_uV) {
+               dev_err(vreg->dev,
+                       "request v=[%d, %d] is outside possible v=[%d, %d]\n",
+                        min_uV, max_uV, lim_min_uV, lim_max_uV);
+               return -EINVAL;
+       }
+
+       /* Find the range which uV is inside of. */
+       for (i = vreg->set_points->count - 1; i > 0; i--) {
+               range_max_uV = vreg->set_points->range[i - 1].set_point_max_uV;
+               if (uV > range_max_uV && range_max_uV > 0)
+                       break;
+       }
+
+       range_id = i;
+       range = &vreg->set_points->range[range_id];
+       *range_sel = range->range_sel;
+
+       /*
+        * Force uV to be an allowed set point by applying a ceiling function to
+        * the uV value.
+        */
+       *voltage_sel = (uV - range->min_uV + range->step_uV - 1)
+                       / range->step_uV;
+       uV = *voltage_sel * range->step_uV + range->min_uV;
+
+       if (uV > max_uV) {
+               dev_err(vreg->dev,
+                       "request v=[%d, %d] cannot be met by any set point; "
+                       "next set point: %d\n",
+                       min_uV, max_uV, uV);
+               return -EINVAL;
+       }
+
+       *selector = 0;
+       for (i = 0; i < range_id; i++)
+               *selector += vreg->set_points->range[i].n_voltages;
+       *selector += (uV - range->set_point_min_uV) / range->step_uV;
+
+       return 0;
+}
+
+static const struct spmi_voltage_range *
+spmi_regulator_find_range(struct spmi_regulator *vreg)
+{
+       u8 range_sel;
+       const struct spmi_voltage_range *range, *end;
+
+       range = vreg->set_points->range;
+       end = range + vreg->set_points->count;
+
+       spmi_vreg_read(vreg, SPMI_COMMON_REG_VOLTAGE_RANGE, &range_sel, 1);
+
+       for (; range < end; range++)
+               if (range->range_sel == range_sel)
+                       return range;
+
+       return NULL;
+}
+
+static int spmi_regulator_select_voltage_same_range(struct spmi_regulator *vreg,
+               int min_uV, int max_uV, u8 *range_sel, u8 *voltage_sel,
+               unsigned *selector)
+{
+       const struct spmi_voltage_range *range;
+       int uV = min_uV;
+       int i;
+
+       range = spmi_regulator_find_range(vreg);
+       if (!range)
+               goto different_range;
+
+       if (uV < range->min_uV && max_uV >= range->min_uV)
+               uV = range->min_uV;
+
+       if (uV < range->min_uV || uV > range->max_uV) {
+               /* Current range doesn't support the requested voltage. */
+               goto different_range;
+       }
+
+       /*
+        * Force uV to be an allowed set point by applying a ceiling function to
+        * the uV value.
+        */
+       *voltage_sel = DIV_ROUND_UP(uV - range->min_uV, range->step_uV);
+       uV = *voltage_sel * range->step_uV + range->min_uV;
+
+       if (uV > max_uV) {
+               /*
+                * No set point in the current voltage range is within the
+                * requested min_uV to max_uV range.
+                */
+               goto different_range;
+       }
+
+       *selector = 0;
+       for (i = 0; i < vreg->set_points->count; i++) {
+               if (uV >= vreg->set_points->range[i].set_point_min_uV
+                   && uV <= vreg->set_points->range[i].set_point_max_uV) {
+                       *selector +=
+                           (uV - vreg->set_points->range[i].set_point_min_uV)
+                               / vreg->set_points->range[i].step_uV;
+                       break;
+               }
+
+               *selector += vreg->set_points->range[i].n_voltages;
+       }
+
+       if (*selector >= vreg->set_points->n_voltages)
+               goto different_range;
+
+       return 0;
+
+different_range:
+       return spmi_regulator_select_voltage(vreg, min_uV, max_uV,
+                       range_sel, voltage_sel, selector);
+}
+
+static int spmi_regulator_common_set_voltage(struct regulator_dev *rdev,
+               int min_uV, int max_uV, unsigned *selector)
+{
+       struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+       int ret;
+       u8 buf[2];
+       u8 range_sel, voltage_sel;
+
+       /*
+        * Favor staying in the current voltage range if possible.  This avoids
+        * voltage spikes that occur when changing the voltage range.
+        */
+       ret = spmi_regulator_select_voltage_same_range(vreg, min_uV, max_uV,
+               &range_sel, &voltage_sel, selector);
+       if (ret)
+               return ret;
+
+       buf[0] = range_sel;
+       buf[1] = voltage_sel;
+       return spmi_vreg_write(vreg, SPMI_COMMON_REG_VOLTAGE_RANGE, buf, 2);
+}
+
+static int spmi_regulator_set_voltage_time_sel(struct regulator_dev *rdev,
+               unsigned int old_selector, unsigned int new_selector)
+{
+       struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+       const struct spmi_voltage_range *range;
+       int diff_uV;
+
+       range = spmi_regulator_find_range(vreg);
+       if (!range)
+               return -EINVAL;
+
+       diff_uV = abs(new_selector - old_selector) * range->step_uV;
+
+       return DIV_ROUND_UP(diff_uV, vreg->slew_rate);
+}
+
+static int spmi_regulator_common_get_voltage(struct regulator_dev *rdev)
+{
+       struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+       const struct spmi_voltage_range *range;
+       u8 voltage_sel;
+
+       spmi_vreg_read(vreg, SPMI_COMMON_REG_VOLTAGE_SET, &voltage_sel, 1);
+
+       range = spmi_regulator_find_range(vreg);
+       if (!range)
+               return VOLTAGE_UNKNOWN;
+
+       return range->step_uV * voltage_sel + range->min_uV;
+}
+
+static int spmi_regulator_single_range_set_voltage(struct regulator_dev *rdev,
+               int min_uV, int max_uV, unsigned *selector)
+{
+       struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+       int ret;
+       u8 range_sel, sel;
+
+       ret = spmi_regulator_select_voltage(vreg, min_uV, max_uV, &range_sel,
+               &sel, selector);
+       if (ret) {
+               dev_err(vreg->dev, "could not set voltage, ret=%d\n", ret);
+               return ret;
+       }
+
+       /*
+        * Certain types of regulators do not have a range select register so
+        * only voltage set register needs to be written.
+        */
+       return spmi_vreg_write(vreg, SPMI_COMMON_REG_VOLTAGE_SET, &sel, 1);
+}
+
+static int spmi_regulator_single_range_get_voltage(struct regulator_dev *rdev)
+{
+       struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+       const struct spmi_voltage_range *range = vreg->set_points->range;
+       u8 voltage_sel;
+
+       spmi_vreg_read(vreg, SPMI_COMMON_REG_VOLTAGE_SET, &voltage_sel, 1);
+
+       return range->step_uV * voltage_sel + range->min_uV;
+}
+
+static int spmi_regulator_ult_lo_smps_set_voltage(struct regulator_dev *rdev,
+               int min_uV, int max_uV, unsigned *selector)
+{
+       struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+       int ret;
+       u8 range_sel, voltage_sel;
+
+       /*
+        * Favor staying in the current voltage range if possible. This avoids
+        * voltage spikes that occur when changing the voltage range.
+        */
+       ret = spmi_regulator_select_voltage_same_range(vreg, min_uV, max_uV,
+               &range_sel, &voltage_sel, selector);
+       if (ret)
+               return ret;
+
+       /*
+        * Calculate VSET based on range
+        * In case of range 0: voltage_sel is a 7 bit value, can be written
+        *                      witout any modification.
+        * In case of range 1: voltage_sel is a 5 bit value, bits[7-5] set to
+        *                      [011].
+        */
+       if (range_sel == 1)
+               voltage_sel |= ULT_SMPS_RANGE_SPLIT;
+
+       return spmi_vreg_update_bits(vreg, SPMI_COMMON_REG_VOLTAGE_SET,
+              voltage_sel, 0xff);
+}
+
+static int spmi_regulator_ult_lo_smps_get_voltage(struct regulator_dev *rdev)
+{
+       struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+       const struct spmi_voltage_range *range;
+       u8 voltage_sel;
+
+       spmi_vreg_read(vreg, SPMI_COMMON_REG_VOLTAGE_SET, &voltage_sel, 1);
+
+       range = spmi_regulator_find_range(vreg);
+       if (!range)
+               return VOLTAGE_UNKNOWN;
+
+       if (range->range_sel == 1)
+               voltage_sel &= ~ULT_SMPS_RANGE_SPLIT;
+
+       return range->step_uV * voltage_sel + range->min_uV;
+}
+
+static int spmi_regulator_common_list_voltage(struct regulator_dev *rdev,
+                       unsigned selector)
+{
+       struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+       int uV = 0;
+       int i;
+
+       if (selector >= vreg->set_points->n_voltages)
+               return 0;
+
+       for (i = 0; i < vreg->set_points->count; i++) {
+               if (selector < vreg->set_points->range[i].n_voltages) {
+                       uV = selector * vreg->set_points->range[i].step_uV
+                               + vreg->set_points->range[i].set_point_min_uV;
+                       break;
+               }
+
+               selector -= vreg->set_points->range[i].n_voltages;
+       }
+
+       return uV;
+}
+
+static int
+spmi_regulator_common_set_bypass(struct regulator_dev *rdev, bool enable)
+{
+       struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+       u8 mask = SPMI_COMMON_MODE_BYPASS_MASK;
+       u8 val = 0;
+
+       if (enable)
+               val = mask;
+
+       return spmi_vreg_update_bits(vreg, SPMI_COMMON_REG_MODE, val, mask);
+}
+
+static int
+spmi_regulator_common_get_bypass(struct regulator_dev *rdev, bool *enable)
+{
+       struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+       u8 val;
+       int ret;
+
+       ret = spmi_vreg_read(vreg, SPMI_COMMON_REG_MODE, &val, 1);
+       *enable = val & SPMI_COMMON_MODE_BYPASS_MASK;
+
+       return ret;
+}
+
+static unsigned int spmi_regulator_common_get_mode(struct regulator_dev *rdev)
+{
+       struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+       u8 reg;
+
+       spmi_vreg_read(vreg, SPMI_COMMON_REG_MODE, &reg, 1);
+
+       if (reg & SPMI_COMMON_MODE_HPM_MASK)
+               return REGULATOR_MODE_NORMAL;
+
+       return REGULATOR_MODE_IDLE;
+}
+
+static int
+spmi_regulator_common_set_mode(struct regulator_dev *rdev, unsigned int mode)
+{
+       struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+       u8 mask = SPMI_COMMON_MODE_HPM_MASK;
+       u8 val = 0;
+
+       if (mode == REGULATOR_MODE_NORMAL)
+               val = mask;
+
+       return spmi_vreg_update_bits(vreg, SPMI_COMMON_REG_MODE, val, mask);
+}
+
+static int
+spmi_regulator_common_set_load(struct regulator_dev *rdev, int load_uA)
+{
+       struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+       unsigned int mode;
+
+       if (load_uA >= vreg->hpm_min_load)
+               mode = REGULATOR_MODE_NORMAL;
+       else
+               mode = REGULATOR_MODE_IDLE;
+
+       return spmi_regulator_common_set_mode(rdev, mode);
+}
+
+static int spmi_regulator_common_set_pull_down(struct regulator_dev *rdev)
+{
+       struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+       unsigned int mask = SPMI_COMMON_PULL_DOWN_ENABLE_MASK;
+
+       return spmi_vreg_update_bits(vreg, SPMI_COMMON_REG_PULL_DOWN,
+                                    mask, mask);
+}
+
+static int spmi_regulator_common_set_soft_start(struct regulator_dev *rdev)
+{
+       struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+       unsigned int mask = SPMI_LDO_SOFT_START_ENABLE_MASK;
+
+       return spmi_vreg_update_bits(vreg, SPMI_COMMON_REG_SOFT_START,
+                                    mask, mask);
+}
+
+static int spmi_regulator_set_ilim(struct regulator_dev *rdev, int ilim_uA)
+{
+       struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+       enum spmi_regulator_logical_type type = vreg->logical_type;
+       unsigned int current_reg;
+       u8 reg;
+       u8 mask = SPMI_BOOST_CURRENT_LIMIT_MASK |
+                 SPMI_BOOST_CURRENT_LIMIT_ENABLE_MASK;
+       int max = (SPMI_BOOST_CURRENT_LIMIT_MASK + 1) * 500;
+
+       if (type == SPMI_REGULATOR_LOGICAL_TYPE_BOOST)
+               current_reg = SPMI_BOOST_REG_CURRENT_LIMIT;
+       else
+               current_reg = SPMI_BOOST_BYP_REG_CURRENT_LIMIT;
+
+       if (ilim_uA > max || ilim_uA <= 0)
+               return -EINVAL;
+
+       reg = (ilim_uA - 1) / 500;
+       reg |= SPMI_BOOST_CURRENT_LIMIT_ENABLE_MASK;
+
+       return spmi_vreg_update_bits(vreg, current_reg, reg, mask);
+}
+
+static int spmi_regulator_vs_clear_ocp(struct spmi_regulator *vreg)
+{
+       int ret;
+
+       ret = spmi_vreg_update_bits(vreg, SPMI_COMMON_REG_ENABLE,
+               SPMI_COMMON_DISABLE, SPMI_COMMON_ENABLE_MASK);
+
+       vreg->vs_enable_time = ktime_get();
+
+       ret = spmi_vreg_update_bits(vreg, SPMI_COMMON_REG_ENABLE,
+               SPMI_COMMON_ENABLE, SPMI_COMMON_ENABLE_MASK);
+
+       return ret;
+}
+
+static void spmi_regulator_vs_ocp_work(struct work_struct *work)
+{
+       struct delayed_work *dwork = to_delayed_work(work);
+       struct spmi_regulator *vreg
+               = container_of(dwork, struct spmi_regulator, ocp_work);
+
+       spmi_regulator_vs_clear_ocp(vreg);
+}
+
+static irqreturn_t spmi_regulator_vs_ocp_isr(int irq, void *data)
+{
+       struct spmi_regulator *vreg = data;
+       ktime_t ocp_irq_time;
+       s64 ocp_trigger_delay_us;
+
+       ocp_irq_time = ktime_get();
+       ocp_trigger_delay_us = ktime_us_delta(ocp_irq_time,
+                                               vreg->vs_enable_time);
+
+       /*
+        * Reset the OCP count if there is a large delay between switch enable
+        * and when OCP triggers.  This is indicative of a hotplug event as
+        * opposed to a fault.
+        */
+       if (ocp_trigger_delay_us > SPMI_VS_OCP_FAULT_DELAY_US)
+               vreg->ocp_count = 0;
+
+       /* Wait for switch output to settle back to 0 V after OCP triggered. */
+       udelay(SPMI_VS_OCP_FALL_DELAY_US);
+
+       vreg->ocp_count++;
+
+       if (vreg->ocp_count == 1) {
+               /* Immediately clear the over current condition. */
+               spmi_regulator_vs_clear_ocp(vreg);
+       } else if (vreg->ocp_count <= vreg->ocp_max_retries) {
+               /* Schedule the over current clear task to run later. */
+               schedule_delayed_work(&vreg->ocp_work,
+                       msecs_to_jiffies(vreg->ocp_retry_delay_ms) + 1);
+       } else {
+               dev_err(vreg->dev,
+                       "OCP triggered %d times; no further retries\n",
+                       vreg->ocp_count);
+       }
+
+       return IRQ_HANDLED;
+}
+
+static struct regulator_ops spmi_smps_ops = {
+       .enable                 = spmi_regulator_common_enable,
+       .disable                = spmi_regulator_common_disable,
+       .is_enabled             = spmi_regulator_common_is_enabled,
+       .set_voltage            = spmi_regulator_common_set_voltage,
+       .get_voltage            = spmi_regulator_common_get_voltage,
+       .list_voltage           = spmi_regulator_common_list_voltage,
+       .set_mode               = spmi_regulator_common_set_mode,
+       .get_mode               = spmi_regulator_common_get_mode,
+       .set_load               = spmi_regulator_common_set_load,
+       .set_pull_down          = spmi_regulator_common_set_pull_down,
+};
+
+static struct regulator_ops spmi_ldo_ops = {
+       .enable                 = spmi_regulator_common_enable,
+       .disable                = spmi_regulator_common_disable,
+       .is_enabled             = spmi_regulator_common_is_enabled,
+       .set_voltage            = spmi_regulator_common_set_voltage,
+       .get_voltage            = spmi_regulator_common_get_voltage,
+       .list_voltage           = spmi_regulator_common_list_voltage,
+       .set_mode               = spmi_regulator_common_set_mode,
+       .get_mode               = spmi_regulator_common_get_mode,
+       .set_load               = spmi_regulator_common_set_load,
+       .set_bypass             = spmi_regulator_common_set_bypass,
+       .get_bypass             = spmi_regulator_common_get_bypass,
+       .set_pull_down          = spmi_regulator_common_set_pull_down,
+       .set_soft_start         = spmi_regulator_common_set_soft_start,
+};
+
+static struct regulator_ops spmi_ln_ldo_ops = {
+       .enable                 = spmi_regulator_common_enable,
+       .disable                = spmi_regulator_common_disable,
+       .is_enabled             = spmi_regulator_common_is_enabled,
+       .set_voltage            = spmi_regulator_common_set_voltage,
+       .get_voltage            = spmi_regulator_common_get_voltage,
+       .list_voltage           = spmi_regulator_common_list_voltage,
+       .set_bypass             = spmi_regulator_common_set_bypass,
+       .get_bypass             = spmi_regulator_common_get_bypass,
+};
+
+static struct regulator_ops spmi_vs_ops = {
+       .enable                 = spmi_regulator_vs_enable,
+       .disable                = spmi_regulator_common_disable,
+       .is_enabled             = spmi_regulator_common_is_enabled,
+       .set_pull_down          = spmi_regulator_common_set_pull_down,
+       .set_soft_start         = spmi_regulator_common_set_soft_start,
+};
+
+static struct regulator_ops spmi_boost_ops = {
+       .enable                 = spmi_regulator_common_enable,
+       .disable                = spmi_regulator_common_disable,
+       .is_enabled             = spmi_regulator_common_is_enabled,
+       .set_voltage            = spmi_regulator_single_range_set_voltage,
+       .get_voltage            = spmi_regulator_single_range_get_voltage,
+       .list_voltage           = spmi_regulator_common_list_voltage,
+       .set_input_current_limit = spmi_regulator_set_ilim,
+};
+
+static struct regulator_ops spmi_ftsmps_ops = {
+       .enable                 = spmi_regulator_common_enable,
+       .disable                = spmi_regulator_common_disable,
+       .is_enabled             = spmi_regulator_common_is_enabled,
+       .set_voltage            = spmi_regulator_common_set_voltage,
+       .set_voltage_time_sel   = spmi_regulator_set_voltage_time_sel,
+       .get_voltage            = spmi_regulator_common_get_voltage,
+       .list_voltage           = spmi_regulator_common_list_voltage,
+       .set_mode               = spmi_regulator_common_set_mode,
+       .get_mode               = spmi_regulator_common_get_mode,
+       .set_load               = spmi_regulator_common_set_load,
+       .set_pull_down          = spmi_regulator_common_set_pull_down,
+};
+
+static struct regulator_ops spmi_ult_lo_smps_ops = {
+       .enable                 = spmi_regulator_common_enable,
+       .disable                = spmi_regulator_common_disable,
+       .is_enabled             = spmi_regulator_common_is_enabled,
+       .set_voltage            = spmi_regulator_ult_lo_smps_set_voltage,
+       .get_voltage            = spmi_regulator_ult_lo_smps_get_voltage,
+       .list_voltage           = spmi_regulator_common_list_voltage,
+       .set_mode               = spmi_regulator_common_set_mode,
+       .get_mode               = spmi_regulator_common_get_mode,
+       .set_load               = spmi_regulator_common_set_load,
+       .set_pull_down          = spmi_regulator_common_set_pull_down,
+};
+
+static struct regulator_ops spmi_ult_ho_smps_ops = {
+       .enable                 = spmi_regulator_common_enable,
+       .disable                = spmi_regulator_common_disable,
+       .is_enabled             = spmi_regulator_common_is_enabled,
+       .set_voltage            = spmi_regulator_single_range_set_voltage,
+       .get_voltage            = spmi_regulator_single_range_get_voltage,
+       .list_voltage           = spmi_regulator_common_list_voltage,
+       .set_mode               = spmi_regulator_common_set_mode,
+       .get_mode               = spmi_regulator_common_get_mode,
+       .set_load               = spmi_regulator_common_set_load,
+       .set_pull_down          = spmi_regulator_common_set_pull_down,
+};
+
+static struct regulator_ops spmi_ult_ldo_ops = {
+       .enable                 = spmi_regulator_common_enable,
+       .disable                = spmi_regulator_common_disable,
+       .is_enabled             = spmi_regulator_common_is_enabled,
+       .set_voltage            = spmi_regulator_single_range_set_voltage,
+       .get_voltage            = spmi_regulator_single_range_get_voltage,
+       .list_voltage           = spmi_regulator_common_list_voltage,
+       .set_mode               = spmi_regulator_common_set_mode,
+       .get_mode               = spmi_regulator_common_get_mode,
+       .set_load               = spmi_regulator_common_set_load,
+       .set_bypass             = spmi_regulator_common_set_bypass,
+       .get_bypass             = spmi_regulator_common_get_bypass,
+       .set_pull_down          = spmi_regulator_common_set_pull_down,
+       .set_soft_start         = spmi_regulator_common_set_soft_start,
+};
+
+/* Maximum possible digital major revision value */
+#define INF 0xFF
+
+static const struct spmi_regulator_mapping supported_regulators[] = {
+       /*           type subtype dig_min dig_max ltype ops setpoints hpm_min */
+       SPMI_VREG(BUCK,  GP_CTL,   0, INF, SMPS,   smps,   smps,   100000),
+       SPMI_VREG(LDO,   N300,     0, INF, LDO,    ldo,    nldo1,   10000),
+       SPMI_VREG(LDO,   N600,     0,   0, LDO,    ldo,    nldo2,   10000),
+       SPMI_VREG(LDO,   N1200,    0,   0, LDO,    ldo,    nldo2,   10000),
+       SPMI_VREG(LDO,   N600,     1, INF, LDO,    ldo,    nldo3,   10000),
+       SPMI_VREG(LDO,   N1200,    1, INF, LDO,    ldo,    nldo3,   10000),
+       SPMI_VREG(LDO,   N600_ST,  0,   0, LDO,    ldo,    nldo2,   10000),
+       SPMI_VREG(LDO,   N1200_ST, 0,   0, LDO,    ldo,    nldo2,   10000),
+       SPMI_VREG(LDO,   N600_ST,  1, INF, LDO,    ldo,    nldo3,   10000),
+       SPMI_VREG(LDO,   N1200_ST, 1, INF, LDO,    ldo,    nldo3,   10000),
+       SPMI_VREG(LDO,   P50,      0, INF, LDO,    ldo,    pldo,     5000),
+       SPMI_VREG(LDO,   P150,     0, INF, LDO,    ldo,    pldo,    10000),
+       SPMI_VREG(LDO,   P300,     0, INF, LDO,    ldo,    pldo,    10000),
+       SPMI_VREG(LDO,   P600,     0, INF, LDO,    ldo,    pldo,    10000),
+       SPMI_VREG(LDO,   P1200,    0, INF, LDO,    ldo,    pldo,    10000),
+       SPMI_VREG(LDO,   LN,       0, INF, LN_LDO, ln_ldo, ln_ldo,      0),
+       SPMI_VREG(LDO,   LV_P50,   0, INF, LDO,    ldo,    pldo,     5000),
+       SPMI_VREG(LDO,   LV_P150,  0, INF, LDO,    ldo,    pldo,    10000),
+       SPMI_VREG(LDO,   LV_P300,  0, INF, LDO,    ldo,    pldo,    10000),
+       SPMI_VREG(LDO,   LV_P600,  0, INF, LDO,    ldo,    pldo,    10000),
+       SPMI_VREG(LDO,   LV_P1200, 0, INF, LDO,    ldo,    pldo,    10000),
+       SPMI_VREG_VS(LV100,        0, INF),
+       SPMI_VREG_VS(LV300,        0, INF),
+       SPMI_VREG_VS(MV300,        0, INF),
+       SPMI_VREG_VS(MV500,        0, INF),
+       SPMI_VREG_VS(HDMI,         0, INF),
+       SPMI_VREG_VS(OTG,          0, INF),
+       SPMI_VREG(BOOST, 5V_BOOST, 0, INF, BOOST,  boost,  boost,       0),
+       SPMI_VREG(FTS,   FTS_CTL,  0, INF, FTSMPS, ftsmps, ftsmps, 100000),
+       SPMI_VREG(FTS, FTS2p5_CTL, 0, INF, FTSMPS, ftsmps, ftsmps2p5, 100000),
+       SPMI_VREG(BOOST_BYP, BB_2A, 0, INF, BOOST_BYP, boost, boost_byp, 0),
+       SPMI_VREG(ULT_BUCK, ULT_HF_CTL1, 0, INF, ULT_LO_SMPS, ult_lo_smps,
+                                               ult_lo_smps,   100000),
+       SPMI_VREG(ULT_BUCK, ULT_HF_CTL2, 0, INF, ULT_LO_SMPS, ult_lo_smps,
+                                               ult_lo_smps,   100000),
+       SPMI_VREG(ULT_BUCK, ULT_HF_CTL3, 0, INF, ULT_LO_SMPS, ult_lo_smps,
+                                               ult_lo_smps,   100000),
+       SPMI_VREG(ULT_BUCK, ULT_HF_CTL4, 0, INF, ULT_HO_SMPS, ult_ho_smps,
+                                               ult_ho_smps,   100000),
+       SPMI_VREG(ULT_LDO, N300_ST, 0, INF, ULT_LDO, ult_ldo, ult_nldo, 10000),
+       SPMI_VREG(ULT_LDO, N600_ST, 0, INF, ULT_LDO, ult_ldo, ult_nldo, 10000),
+       SPMI_VREG(ULT_LDO, N900_ST, 0, INF, ULT_LDO, ult_ldo, ult_nldo, 10000),
+       SPMI_VREG(ULT_LDO, N1200_ST, 0, INF, ULT_LDO, ult_ldo, ult_nldo, 10000),
+       SPMI_VREG(ULT_LDO, LV_P150,  0, INF, ULT_LDO, ult_ldo, ult_pldo, 10000),
+       SPMI_VREG(ULT_LDO, LV_P300,  0, INF, ULT_LDO, ult_ldo, ult_pldo, 10000),
+       SPMI_VREG(ULT_LDO, LV_P450,  0, INF, ULT_LDO, ult_ldo, ult_pldo, 10000),
+       SPMI_VREG(ULT_LDO, P600,     0, INF, ULT_LDO, ult_ldo, ult_pldo, 10000),
+       SPMI_VREG(ULT_LDO, P150,     0, INF, ULT_LDO, ult_ldo, ult_pldo, 10000),
+       SPMI_VREG(ULT_LDO, P50,     0, INF, ULT_LDO, ult_ldo, ult_pldo, 5000),
+};
+
+static void spmi_calculate_num_voltages(struct spmi_voltage_set_points *points)
+{
+       unsigned int n;
+       struct spmi_voltage_range *range = points->range;
+
+       for (; range < points->range + points->count; range++) {
+               n = 0;
+               if (range->set_point_max_uV) {
+                       n = range->set_point_max_uV - range->set_point_min_uV;
+                       n = (n / range->step_uV) + 1;
+               }
+               range->n_voltages = n;
+               points->n_voltages += n;
+       }
+}
+
+static int spmi_regulator_match(struct spmi_regulator *vreg, u16 force_type)
+{
+       const struct spmi_regulator_mapping *mapping;
+       int ret, i;
+       u32 dig_major_rev;
+       u8 version[SPMI_COMMON_REG_SUBTYPE - SPMI_COMMON_REG_DIG_MAJOR_REV + 1];
+       u8 type, subtype;
+
+       ret = spmi_vreg_read(vreg, SPMI_COMMON_REG_DIG_MAJOR_REV, version,
+               ARRAY_SIZE(version));
+       if (ret) {
+               dev_err(vreg->dev, "could not read version registers\n");
+               return ret;
+       }
+       dig_major_rev   = version[SPMI_COMMON_REG_DIG_MAJOR_REV
+                                       - SPMI_COMMON_REG_DIG_MAJOR_REV];
+       if (!force_type) {
+               type            = version[SPMI_COMMON_REG_TYPE -
+                                         SPMI_COMMON_REG_DIG_MAJOR_REV];
+               subtype         = version[SPMI_COMMON_REG_SUBTYPE -
+                                         SPMI_COMMON_REG_DIG_MAJOR_REV];
+       } else {
+               type = force_type >> 8;
+               subtype = force_type;
+       }
+
+       for (i = 0; i < ARRAY_SIZE(supported_regulators); i++) {
+               mapping = &supported_regulators[i];
+               if (mapping->type == type && mapping->subtype == subtype
+                   && mapping->revision_min <= dig_major_rev
+                   && mapping->revision_max >= dig_major_rev)
+                       goto found;
+       }
+
+       dev_err(vreg->dev,
+               "unsupported regulator: name=%s type=0x%02X, subtype=0x%02X, dig major rev=0x%02X\n",
+               vreg->desc.name, type, subtype, dig_major_rev);
+
+       return -ENODEV;
+
+found:
+       vreg->logical_type      = mapping->logical_type;
+       vreg->set_points        = mapping->set_points;
+       vreg->hpm_min_load      = mapping->hpm_min_load;
+       vreg->desc.ops          = mapping->ops;
+
+       if (mapping->set_points) {
+               if (!mapping->set_points->n_voltages)
+                       spmi_calculate_num_voltages(mapping->set_points);
+               vreg->desc.n_voltages = mapping->set_points->n_voltages;
+       }
+
+       return 0;
+}
+
+static int spmi_regulator_ftsmps_init_slew_rate(struct spmi_regulator *vreg)
+{
+       int ret;
+       u8 reg = 0;
+       int step, delay, slew_rate;
+       const struct spmi_voltage_range *range;
+
+       ret = spmi_vreg_read(vreg, SPMI_COMMON_REG_STEP_CTRL, &reg, 1);
+       if (ret) {
+               dev_err(vreg->dev, "spmi read failed, ret=%d\n", ret);
+               return ret;
+       }
+
+       range = spmi_regulator_find_range(vreg);
+       if (!range)
+               return -EINVAL;
+
+       step = reg & SPMI_FTSMPS_STEP_CTRL_STEP_MASK;
+       step >>= SPMI_FTSMPS_STEP_CTRL_STEP_SHIFT;
+
+       delay = reg & SPMI_FTSMPS_STEP_CTRL_DELAY_MASK;
+       delay >>= SPMI_FTSMPS_STEP_CTRL_DELAY_SHIFT;
+
+       /* slew_rate has units of uV/us */
+       slew_rate = SPMI_FTSMPS_CLOCK_RATE * range->step_uV * (1 << step);
+       slew_rate /= 1000 * (SPMI_FTSMPS_STEP_DELAY << delay);
+       slew_rate *= SPMI_FTSMPS_STEP_MARGIN_NUM;
+       slew_rate /= SPMI_FTSMPS_STEP_MARGIN_DEN;
+
+       /* Ensure that the slew rate is greater than 0 */
+       vreg->slew_rate = max(slew_rate, 1);
+
+       return ret;
+}
+
+static unsigned int spmi_regulator_of_map_mode(unsigned int mode)
+{
+       if (mode)
+               return REGULATOR_MODE_NORMAL;
+
+       return REGULATOR_MODE_IDLE;
+}
+
+static int spmi_regulator_of_parse(struct device_node *node,
+                           const struct regulator_desc *desc,
+                           struct regulator_config *config)
+{
+       struct spmi_regulator *vreg = config->driver_data;
+       struct device *dev = config->dev;
+       int ret;
+
+       vreg->ocp_max_retries = SPMI_VS_OCP_DEFAULT_MAX_RETRIES;
+       vreg->ocp_retry_delay_ms = SPMI_VS_OCP_DEFAULT_RETRY_DELAY_MS;
+
+       if (vreg->logical_type == SPMI_REGULATOR_LOGICAL_TYPE_FTSMPS) {
+               ret = spmi_regulator_ftsmps_init_slew_rate(vreg);
+               if (ret)
+                       return ret;
+       }
+
+       if (vreg->logical_type != SPMI_REGULATOR_LOGICAL_TYPE_VS)
+               vreg->ocp_irq = 0;
+
+       if (vreg->ocp_irq) {
+               ret = devm_request_irq(dev, vreg->ocp_irq,
+                       spmi_regulator_vs_ocp_isr, IRQF_TRIGGER_RISING, "ocp",
+                       vreg);
+               if (ret < 0) {
+                       dev_err(dev, "failed to request irq %d, ret=%d\n",
+                               vreg->ocp_irq, ret);
+                       return ret;
+               }
+
+               INIT_DELAYED_WORK(&vreg->ocp_work, spmi_regulator_vs_ocp_work);
+       }
+
+       return 0;
+}
+
+static const struct spmi_regulator_data pm8941_regulators[] = {
+       { "s1", 0x1400, "vdd_s1", },
+       { "s2", 0x1700, "vdd_s2", },
+       { "s3", 0x1a00, "vdd_s3", },
+       { "l1", 0x4000, "vdd_l1_l3", },
+       { "l2", 0x4100, "vdd_l2_lvs_1_2_3", },
+       { "l3", 0x4200, "vdd_l1_l3", },
+       { "l4", 0x4300, "vdd_l4_l11", },
+       { "l5", 0x4400, "vdd_l5_l7", NULL, 0x0410 },
+       { "l6", 0x4500, "vdd_l6_l12_l14_l15", },
+       { "l7", 0x4600, "vdd_l5_l7", NULL, 0x0410 },
+       { "l8", 0x4700, "vdd_l8_l16_l18_19", },
+       { "l9", 0x4800, "vdd_l9_l10_l17_l22", },
+       { "l10", 0x4900, "vdd_l9_l10_l17_l22", },
+       { "l11", 0x4a00, "vdd_l4_l11", },
+       { "l12", 0x4b00, "vdd_l6_l12_l14_l15", },
+       { "l13", 0x4c00, "vdd_l13_l20_l23_l24", },
+       { "l14", 0x4d00, "vdd_l6_l12_l14_l15", },
+       { "l15", 0x4e00, "vdd_l6_l12_l14_l15", },
+       { "l16", 0x4f00, "vdd_l8_l16_l18_19", },
+       { "l17", 0x5000, "vdd_l9_l10_l17_l22", },
+       { "l18", 0x5100, "vdd_l8_l16_l18_19", },
+       { "l19", 0x5200, "vdd_l8_l16_l18_19", },
+       { "l20", 0x5300, "vdd_l13_l20_l23_l24", },
+       { "l21", 0x5400, "vdd_l21", },
+       { "l22", 0x5500, "vdd_l9_l10_l17_l22", },
+       { "l23", 0x5600, "vdd_l13_l20_l23_l24", },
+       { "l24", 0x5700, "vdd_l13_l20_l23_l24", },
+       { "lvs1", 0x8000, "vdd_l2_lvs_1_2_3", },
+       { "lvs2", 0x8100, "vdd_l2_lvs_1_2_3", },
+       { "lvs3", 0x8200, "vdd_l2_lvs_1_2_3", },
+       { "mvs1", 0x8300, "vin_5vs", },
+       { "mvs2", 0x8400, "vin_5vs", },
+       { }
+};
+
+static const struct spmi_regulator_data pm8841_regulators[] = {
+       { "s1", 0x1400, "vdd_s1", },
+       { "s2", 0x1700, "vdd_s2", NULL, 0x1c08 },
+       { "s3", 0x1a00, "vdd_s3", },
+       { "s4", 0x1d00, "vdd_s4", NULL, 0x1c08 },
+       { "s5", 0x2000, "vdd_s5", NULL, 0x1c08 },
+       { "s6", 0x2300, "vdd_s6", NULL, 0x1c08 },
+       { "s7", 0x2600, "vdd_s7", NULL, 0x1c08 },
+       { "s8", 0x2900, "vdd_s8", NULL, 0x1c08 },
+       { }
+};
+
+static const struct spmi_regulator_data pm8916_regulators[] = {
+       { "s1", 0x1400, "vdd_s1", },
+       { "s2", 0x1700, "vdd_s2", },
+       { "s3", 0x1a00, "vdd_s3", },
+       { "s4", 0x1d00, "vdd_s4", },
+       { "l1", 0x4000, "vdd_l1_l3", },
+       { "l2", 0x4100, "vdd_l2", },
+       { "l3", 0x4200, "vdd_l1_l3", },
+       { "l4", 0x4300, "vdd_l4_l5_l6", },
+       { "l5", 0x4400, "vdd_l4_l5_l6", },
+       { "l6", 0x4500, "vdd_l4_l5_l6", },
+       { "l7", 0x4600, "vdd_l7", },
+       { "l8", 0x4700, "vdd_l8_l11_l14_l15_l16", },
+       { "l9", 0x4800, "vdd_l9_l10_l12_l13_l17_l18", },
+       { "l10", 0x4900, "vdd_l9_l10_l12_l13_l17_l18", },
+       { "l11", 0x4a00, "vdd_l8_l11_l14_l15_l16", },
+       { "l12", 0x4b00, "vdd_l9_l10_l12_l13_l17_l18", },
+       { "l13", 0x4c00, "vdd_l9_l10_l12_l13_l17_l18", },
+       { "l14", 0x4d00, "vdd_l8_l11_l14_l15_l16", },
+       { "l15", 0x4e00, "vdd_l8_l11_l14_l15_l16", },
+       { "l16", 0x4f00, "vdd_l8_l11_l14_l15_l16", },
+       { "l17", 0x5000, "vdd_l9_l10_l12_l13_l17_l18", },
+       { "l18", 0x5100, "vdd_l9_l10_l12_l13_l17_l18", },
+       { }
+};
+
+static const struct of_device_id qcom_spmi_regulator_match[] = {
+       { .compatible = "qcom,pm8841-regulators", .data = &pm8841_regulators },
+       { .compatible = "qcom,pm8916-regulators", .data = &pm8916_regulators },
+       { .compatible = "qcom,pm8941-regulators", .data = &pm8941_regulators },
+       { }
+};
+MODULE_DEVICE_TABLE(of, qcom_spmi_regulator_match);
+
+static int qcom_spmi_regulator_probe(struct platform_device *pdev)
+{
+       const struct spmi_regulator_data *reg;
+       const struct of_device_id *match;
+       struct regulator_config config = { };
+       struct regulator_dev *rdev;
+       struct spmi_regulator *vreg;
+       struct regmap *regmap;
+       const char *name;
+       struct device *dev = &pdev->dev;
+       int ret;
+       struct list_head *vreg_list;
+
+       vreg_list = devm_kzalloc(dev, sizeof(*vreg_list), GFP_KERNEL);
+       if (!vreg_list)
+               return -ENOMEM;
+       INIT_LIST_HEAD(vreg_list);
+       platform_set_drvdata(pdev, vreg_list);
+
+       regmap = dev_get_regmap(dev->parent, NULL);
+       if (!regmap)
+               return -ENODEV;
+
+       match = of_match_device(qcom_spmi_regulator_match, &pdev->dev);
+       if (!match)
+               return -ENODEV;
+
+       for (reg = match->data; reg->name; reg++) {
+               vreg = devm_kzalloc(dev, sizeof(*vreg), GFP_KERNEL);
+               if (!vreg)
+                       return -ENOMEM;
+
+               vreg->dev = dev;
+               vreg->base = reg->base;
+               vreg->regmap = regmap;
+
+               if (reg->ocp) {
+                       vreg->ocp_irq = platform_get_irq_byname(pdev, reg->ocp);
+                       if (vreg->ocp_irq < 0) {
+                               ret = vreg->ocp_irq;
+                               goto err;
+                       }
+               }
+
+               vreg->desc.id = -1;
+               vreg->desc.owner = THIS_MODULE;
+               vreg->desc.type = REGULATOR_VOLTAGE;
+               vreg->desc.name = name = reg->name;
+               vreg->desc.supply_name = reg->supply;
+               vreg->desc.of_match = reg->name;
+               vreg->desc.of_parse_cb = spmi_regulator_of_parse;
+               vreg->desc.of_map_mode = spmi_regulator_of_map_mode;
+
+               ret = spmi_regulator_match(vreg, reg->force_type);
+               if (ret)
+                       goto err;
+
+               config.dev = dev;
+               config.driver_data = vreg;
+               rdev = devm_regulator_register(dev, &vreg->desc, &config);
+               if (IS_ERR(rdev)) {
+                       dev_err(dev, "failed to register %s\n", name);
+                       ret = PTR_ERR(rdev);
+                       goto err;
+               }
+
+               INIT_LIST_HEAD(&vreg->node);
+               list_add(&vreg->node, vreg_list);
+       }
+
+       return 0;
+
+err:
+       list_for_each_entry(vreg, vreg_list, node)
+               if (vreg->ocp_irq)
+                       cancel_delayed_work_sync(&vreg->ocp_work);
+       return ret;
+}
+
+static int qcom_spmi_regulator_remove(struct platform_device *pdev)
+{
+       struct spmi_regulator *vreg;
+       struct list_head *vreg_list = platform_get_drvdata(pdev);
+
+       list_for_each_entry(vreg, vreg_list, node)
+               if (vreg->ocp_irq)
+                       cancel_delayed_work_sync(&vreg->ocp_work);
+
+       return 0;
+}
+
+static struct platform_driver qcom_spmi_regulator_driver = {
+       .driver         = {
+               .name   = "qcom-spmi-regulator",
+               .of_match_table = qcom_spmi_regulator_match,
+       },
+       .probe          = qcom_spmi_regulator_probe,
+       .remove         = qcom_spmi_regulator_remove,
+};
+module_platform_driver(qcom_spmi_regulator_driver);
+
+MODULE_DESCRIPTION("Qualcomm SPMI PMIC regulator driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:qcom-spmi-regulator");
index ff828117798fd3f4775cd5cc6c8e86d8fbe33a00..326ffb55337117d6dc2a96d7fce9cea773e18a14 100644 (file)
@@ -515,7 +515,7 @@ static int s2mps14_regulator_enable(struct regulator_dev *rdev)
                break;
        default:
                return -EINVAL;
-       };
+       }
 
        return regmap_update_bits(rdev->regmap, rdev->desc->enable_reg,
                        rdev->desc->enable_mask, val);
@@ -538,7 +538,7 @@ static int s2mps14_regulator_set_suspend_disable(struct regulator_dev *rdev)
                default:
                        state = S2MPS14_ENABLE_SUSPEND;
                        break;
-               };
+               }
                break;
        case S2MPU02:
                switch (rdev_id) {
@@ -552,11 +552,11 @@ static int s2mps14_regulator_set_suspend_disable(struct regulator_dev *rdev)
                default:
                        state = S2MPU02_ENABLE_SUSPEND;
                        break;
-               };
+               }
                break;
        default:
                return -EINVAL;
-       };
+       }
 
        ret = regmap_read(rdev->regmap, rdev->desc->enable_reg, &val);
        if (ret < 0)
@@ -977,7 +977,7 @@ static int s2mps11_pmic_probe(struct platform_device *pdev)
                dev_err(&pdev->dev, "Invalid device type: %u\n",
                                    s2mps11->dev_type);
                return -EINVAL;
-       };
+       }
 
        s2mps11->ext_control_gpio = devm_kmalloc(&pdev->dev,
                        sizeof(*s2mps11->ext_control_gpio) * s2mps11->rdev_num,
index 0d7e164a5e763939a2d15d3e0d6a160beabd23c7..8cbb82ceec40521a7486b4f89b03e1c42499c041 100644 (file)
@@ -533,7 +533,8 @@ static int wm831x_buckv_probe(struct platform_device *pdev)
        irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "UV"));
        ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
                                        wm831x_dcdc_uv_irq,
-                                       IRQF_TRIGGER_RISING, dcdc->name, dcdc);
+                                       IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+                                       dcdc->name, dcdc);
        if (ret != 0) {
                dev_err(&pdev->dev, "Failed to request UV IRQ %d: %d\n",
                        irq, ret);
@@ -543,7 +544,8 @@ static int wm831x_buckv_probe(struct platform_device *pdev)
        irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "HC"));
        ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
                                        wm831x_dcdc_oc_irq,
-                                       IRQF_TRIGGER_RISING, dcdc->name, dcdc);
+                                       IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+                                       dcdc->name, dcdc);
        if (ret != 0) {
                dev_err(&pdev->dev, "Failed to request HC IRQ %d: %d\n",
                        irq, ret);
@@ -669,7 +671,8 @@ static int wm831x_buckp_probe(struct platform_device *pdev)
        irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "UV"));
        ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
                                        wm831x_dcdc_uv_irq,
-                                       IRQF_TRIGGER_RISING, dcdc->name, dcdc);
+                                       IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+                                       dcdc->name, dcdc);
        if (ret != 0) {
                dev_err(&pdev->dev, "Failed to request UV IRQ %d: %d\n",
                        irq, ret);
@@ -785,7 +788,8 @@ static int wm831x_boostp_probe(struct platform_device *pdev)
        irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "UV"));
        ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
                                        wm831x_dcdc_uv_irq,
-                                       IRQF_TRIGGER_RISING, dcdc->name,
+                                       IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+                                       dcdc->name,
                                        dcdc);
        if (ret != 0) {
                dev_err(&pdev->dev, "Failed to request UV IRQ %d: %d\n",
index 1e88391a162849c0c529e2f76e28f95216e576f8..1442828fcd9afbe44e2788646f4ed992b903d7ea 100644 (file)
@@ -204,7 +204,8 @@ static int wm831x_isink_probe(struct platform_device *pdev)
        irq = wm831x_irq(wm831x, platform_get_irq(pdev, 0));
        ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
                                        wm831x_isink_irq,
-                                       IRQF_TRIGGER_RISING, isink->name,
+                                       IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+                                       isink->name,
                                        isink);
        if (ret != 0) {
                dev_err(&pdev->dev, "Failed to request ISINK IRQ %d: %d\n",
index 7ae2dc82f636b601c2a976674dc26c1a9aae02a6..5a7b65e8a529c496edfdcf15ade002e82e4d5d6d 100644 (file)
@@ -287,7 +287,8 @@ static int wm831x_gp_ldo_probe(struct platform_device *pdev)
        irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "UV"));
        ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
                                        wm831x_ldo_uv_irq,
-                                       IRQF_TRIGGER_RISING, ldo->name,
+                                       IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+                                       ldo->name,
                                        ldo);
        if (ret != 0) {
                dev_err(&pdev->dev, "Failed to request UV IRQ %d: %d\n",
@@ -496,7 +497,8 @@ static int wm831x_aldo_probe(struct platform_device *pdev)
        irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "UV"));
        ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
                                        wm831x_ldo_uv_irq,
-                                       IRQF_TRIGGER_RISING, ldo->name, ldo);
+                                       IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+                                       ldo->name, ldo);
        if (ret != 0) {
                dev_err(&pdev->dev, "Failed to request UV IRQ %d: %d\n",
                        irq, ret);
index 0fe4ad8826b2cda45044d8699696486cbd4bd1eb..5e963df9e5656df9ec15fe97e9c614fb78806ecd 100644 (file)
@@ -1520,6 +1520,17 @@ config RTC_DRV_SIRFSOC
          Say "yes" here to support the real time clock on SiRF SOC chips.
          This driver can also be built as a module called rtc-sirfsoc.
 
+config RTC_DRV_ST_LPC
+       tristate "STMicroelectronics LPC RTC"
+       depends on ARCH_STI
+       depends on OF
+       help
+         Say Y here to include STMicroelectronics Low Power Controller
+         (LPC) based RTC support.
+
+         To compile this driver as a module, choose M here: the
+         module will be called rtc-st-lpc.
+
 config RTC_DRV_MOXART
        tristate "MOXA ART RTC"
        depends on ARCH_MOXART || COMPILE_TEST
index 2b82e2b0311bd9da719b037e5d217fdee9efc652..ebe2c085d01c77f2d45375d46e22747b3652a1df 100644 (file)
@@ -154,4 +154,5 @@ obj-$(CONFIG_RTC_DRV_WM8350)        += rtc-wm8350.o
 obj-$(CONFIG_RTC_DRV_X1205)    += rtc-x1205.o
 obj-$(CONFIG_RTC_DRV_XGENE)    += rtc-xgene.o
 obj-$(CONFIG_RTC_DRV_SIRFSOC)  += rtc-sirfsoc.o
+obj-$(CONFIG_RTC_DRV_ST_LPC)   += rtc-st-lpc.o
 obj-$(CONFIG_RTC_DRV_MOXART)   += rtc-moxart.o
diff --git a/drivers/rtc/rtc-st-lpc.c b/drivers/rtc/rtc-st-lpc.c
new file mode 100644 (file)
index 0000000..3f9d0ac
--- /dev/null
@@ -0,0 +1,354 @@
+/*
+ * rtc-st-lpc.c - ST's LPC RTC, powered by the Low Power Timer
+ *
+ * Copyright (C) 2014 STMicroelectronics Limited
+ *
+ * Author: David Paris <david.paris@st.com> for STMicroelectronics
+ *         Lee Jones <lee.jones@linaro.org> for STMicroelectronics
+ *
+ * Based on the original driver written by Stuart Menefy.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+
+#include <dt-bindings/mfd/st-lpc.h>
+
+/* Low Power Timer */
+#define LPC_LPT_LSB_OFF                0x400
+#define LPC_LPT_MSB_OFF                0x404
+#define LPC_LPT_START_OFF      0x408
+
+/* Low Power Alarm */
+#define LPC_LPA_LSB_OFF                0x410
+#define LPC_LPA_MSB_OFF                0x414
+#define LPC_LPA_START_OFF      0x418
+
+/* LPC as WDT */
+#define LPC_WDT_OFF            0x510
+#define LPC_WDT_FLAG_OFF       0x514
+
+struct st_rtc {
+       struct rtc_device *rtc_dev;
+       struct rtc_wkalrm alarm;
+       struct resource *res;
+       struct clk *clk;
+       unsigned long clkrate;
+       void __iomem *ioaddr;
+       bool irq_enabled:1;
+       spinlock_t lock;
+       short irq;
+};
+
+static void st_rtc_set_hw_alarm(struct st_rtc *rtc,
+                               unsigned long msb, unsigned long  lsb)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&rtc->lock, flags);
+
+       writel_relaxed(1, rtc->ioaddr + LPC_WDT_OFF);
+
+       writel_relaxed(msb, rtc->ioaddr + LPC_LPA_MSB_OFF);
+       writel_relaxed(lsb, rtc->ioaddr + LPC_LPA_LSB_OFF);
+       writel_relaxed(1, rtc->ioaddr + LPC_LPA_START_OFF);
+
+       writel_relaxed(0, rtc->ioaddr + LPC_WDT_OFF);
+
+       spin_unlock_irqrestore(&rtc->lock, flags);
+}
+
+static irqreturn_t st_rtc_handler(int this_irq, void *data)
+{
+       struct st_rtc *rtc = (struct st_rtc *)data;
+
+       rtc_update_irq(rtc->rtc_dev, 1, RTC_AF);
+
+       return IRQ_HANDLED;
+}
+
+static int st_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+       struct st_rtc *rtc = dev_get_drvdata(dev);
+       unsigned long lpt_lsb, lpt_msb;
+       unsigned long long lpt;
+       unsigned long flags;
+
+       spin_lock_irqsave(&rtc->lock, flags);
+
+       do {
+               lpt_msb = readl_relaxed(rtc->ioaddr + LPC_LPT_MSB_OFF);
+               lpt_lsb = readl_relaxed(rtc->ioaddr + LPC_LPT_LSB_OFF);
+       } while (readl_relaxed(rtc->ioaddr + LPC_LPT_MSB_OFF) != lpt_msb);
+
+       spin_unlock_irqrestore(&rtc->lock, flags);
+
+       lpt = ((unsigned long long)lpt_msb << 32) | lpt_lsb;
+       do_div(lpt, rtc->clkrate);
+       rtc_time_to_tm(lpt, tm);
+
+       return 0;
+}
+
+static int st_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+       struct st_rtc *rtc = dev_get_drvdata(dev);
+       unsigned long long lpt;
+       unsigned long secs, flags;
+       int ret;
+
+       ret = rtc_tm_to_time(tm, &secs);
+       if (ret)
+               return ret;
+
+       lpt = (unsigned long long)secs * rtc->clkrate;
+
+       spin_lock_irqsave(&rtc->lock, flags);
+
+       writel_relaxed(lpt >> 32, rtc->ioaddr + LPC_LPT_MSB_OFF);
+       writel_relaxed(lpt, rtc->ioaddr + LPC_LPT_LSB_OFF);
+       writel_relaxed(1, rtc->ioaddr + LPC_LPT_START_OFF);
+
+       spin_unlock_irqrestore(&rtc->lock, flags);
+
+       return 0;
+}
+
+static int st_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *wkalrm)
+{
+       struct st_rtc *rtc = dev_get_drvdata(dev);
+       unsigned long flags;
+
+       spin_lock_irqsave(&rtc->lock, flags);
+
+       memcpy(wkalrm, &rtc->alarm, sizeof(struct rtc_wkalrm));
+
+       spin_unlock_irqrestore(&rtc->lock, flags);
+
+       return 0;
+}
+
+static int st_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+       struct st_rtc *rtc = dev_get_drvdata(dev);
+
+       if (enabled && !rtc->irq_enabled) {
+               enable_irq(rtc->irq);
+               rtc->irq_enabled = true;
+       } else if (!enabled && rtc->irq_enabled) {
+               disable_irq(rtc->irq);
+               rtc->irq_enabled = false;
+       }
+
+       return 0;
+}
+
+static int st_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *t)
+{
+       struct st_rtc *rtc = dev_get_drvdata(dev);
+       struct rtc_time now;
+       unsigned long now_secs;
+       unsigned long alarm_secs;
+       unsigned long long lpa;
+
+       st_rtc_read_time(dev, &now);
+       rtc_tm_to_time(&now, &now_secs);
+       rtc_tm_to_time(&t->time, &alarm_secs);
+
+       /* Invalid alarm time */
+       if (now_secs > alarm_secs)
+               return -EINVAL;
+
+       memcpy(&rtc->alarm, t, sizeof(struct rtc_wkalrm));
+
+       /* Now many secs to fire */
+       alarm_secs -= now_secs;
+       lpa = (unsigned long long)alarm_secs * rtc->clkrate;
+
+       st_rtc_set_hw_alarm(rtc, lpa >> 32, lpa);
+       st_rtc_alarm_irq_enable(dev, t->enabled);
+
+       return 0;
+}
+
+static struct rtc_class_ops st_rtc_ops = {
+       .read_time              = st_rtc_read_time,
+       .set_time               = st_rtc_set_time,
+       .read_alarm             = st_rtc_read_alarm,
+       .set_alarm              = st_rtc_set_alarm,
+       .alarm_irq_enable       = st_rtc_alarm_irq_enable,
+};
+
+static int st_rtc_probe(struct platform_device *pdev)
+{
+       struct device_node *np = pdev->dev.of_node;
+       struct st_rtc *rtc;
+       struct resource *res;
+       struct rtc_time tm_check;
+       uint32_t mode;
+       int ret = 0;
+
+       ret = of_property_read_u32(np, "st,lpc-mode", &mode);
+       if (ret) {
+               dev_err(&pdev->dev, "An LPC mode must be provided\n");
+               return -EINVAL;
+       }
+
+       /* LPC can either run in RTC or WDT mode */
+       if (mode != ST_LPC_MODE_RTC)
+               return -ENODEV;
+
+       rtc = devm_kzalloc(&pdev->dev, sizeof(struct st_rtc), GFP_KERNEL);
+       if (!rtc)
+               return -ENOMEM;
+
+       spin_lock_init(&rtc->lock);
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       rtc->ioaddr = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(rtc->ioaddr))
+               return PTR_ERR(rtc->ioaddr);
+
+       rtc->irq = irq_of_parse_and_map(np, 0);
+       if (!rtc->irq) {
+               dev_err(&pdev->dev, "IRQ missing or invalid\n");
+               return -EINVAL;
+       }
+
+       ret = devm_request_irq(&pdev->dev, rtc->irq, st_rtc_handler, 0,
+                              pdev->name, rtc);
+       if (ret) {
+               dev_err(&pdev->dev, "Failed to request irq %i\n", rtc->irq);
+               return ret;
+       }
+
+       enable_irq_wake(rtc->irq);
+       disable_irq(rtc->irq);
+
+       rtc->clk = clk_get(&pdev->dev, NULL);
+       if (IS_ERR(rtc->clk)) {
+               dev_err(&pdev->dev, "Unable to request clock\n");
+               return PTR_ERR(rtc->clk);
+       }
+
+       clk_prepare_enable(rtc->clk);
+
+       rtc->clkrate = clk_get_rate(rtc->clk);
+       if (!rtc->clkrate) {
+               dev_err(&pdev->dev, "Unable to fetch clock rate\n");
+               return -EINVAL;
+       }
+
+       device_set_wakeup_capable(&pdev->dev, 1);
+
+       platform_set_drvdata(pdev, rtc);
+
+       /*
+        * The RTC-LPC is able to manage date.year > 2038
+        * but currently the kernel can not manage this date!
+        * If the RTC-LPC has a date.year > 2038 then
+        * it's set to the epoch "Jan 1st 2000"
+        */
+       st_rtc_read_time(&pdev->dev, &tm_check);
+
+       if (tm_check.tm_year >=  (2038 - 1900)) {
+               memset(&tm_check, 0, sizeof(tm_check));
+               tm_check.tm_year = 100;
+               tm_check.tm_mday = 1;
+               st_rtc_set_time(&pdev->dev, &tm_check);
+       }
+
+       rtc->rtc_dev = rtc_device_register("st-lpc-rtc", &pdev->dev,
+                                          &st_rtc_ops, THIS_MODULE);
+       if (IS_ERR(rtc->rtc_dev)) {
+               clk_disable_unprepare(rtc->clk);
+               return PTR_ERR(rtc->rtc_dev);
+       }
+
+       return 0;
+}
+
+static int st_rtc_remove(struct platform_device *pdev)
+{
+       struct st_rtc *rtc = platform_get_drvdata(pdev);
+
+       if (likely(rtc->rtc_dev))
+               rtc_device_unregister(rtc->rtc_dev);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int st_rtc_suspend(struct device *dev)
+{
+       struct st_rtc *rtc = dev_get_drvdata(dev);
+
+       if (device_may_wakeup(dev))
+               return 0;
+
+       writel_relaxed(1, rtc->ioaddr + LPC_WDT_OFF);
+       writel_relaxed(0, rtc->ioaddr + LPC_LPA_START_OFF);
+       writel_relaxed(0, rtc->ioaddr + LPC_WDT_OFF);
+
+       return 0;
+}
+
+static int st_rtc_resume(struct device *dev)
+{
+       struct st_rtc *rtc = dev_get_drvdata(dev);
+
+       rtc_alarm_irq_enable(rtc->rtc_dev, 0);
+
+       /*
+        * clean 'rtc->alarm' to allow a new
+        * .set_alarm to the upper RTC layer
+        */
+       memset(&rtc->alarm, 0, sizeof(struct rtc_wkalrm));
+
+       writel_relaxed(0, rtc->ioaddr + LPC_LPA_MSB_OFF);
+       writel_relaxed(0, rtc->ioaddr + LPC_LPA_LSB_OFF);
+       writel_relaxed(1, rtc->ioaddr + LPC_WDT_OFF);
+       writel_relaxed(1, rtc->ioaddr + LPC_LPA_START_OFF);
+       writel_relaxed(0, rtc->ioaddr + LPC_WDT_OFF);
+
+       return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(st_rtc_pm_ops, st_rtc_suspend, st_rtc_resume);
+
+static const struct of_device_id st_rtc_match[] = {
+       { .compatible = "st,stih407-lpc" },
+       {}
+};
+MODULE_DEVICE_TABLE(of, st_rtc_match);
+
+static struct platform_driver st_rtc_platform_driver = {
+       .driver = {
+               .name = "st-lpc-rtc",
+               .pm = &st_rtc_pm_ops,
+               .of_match_table = st_rtc_match,
+       },
+       .probe = st_rtc_probe,
+       .remove = st_rtc_remove,
+};
+
+module_platform_driver(st_rtc_platform_driver);
+
+MODULE_DESCRIPTION("STMicroelectronics LPC RTC driver");
+MODULE_AUTHOR("David Paris <david.paris@st.com>");
+MODULE_LICENSE("GPL");
index 57fd66357b959fadaccfa3ecf269dda848bc7166..1aec8ff0b58743214ff2ad520dea009d0a1f3f96 100644 (file)
@@ -38,6 +38,8 @@
  */
 #define DASD_CHANQ_MAX_SIZE 4
 
+#define DASD_DIAG_MOD          "dasd_diag_mod"
+
 /*
  * SECTION: exported variables of dasd.c
  */
@@ -3300,6 +3302,21 @@ int dasd_generic_set_online(struct ccw_device *cdev,
        discipline = base_discipline;
        if (device->features & DASD_FEATURE_USEDIAG) {
                if (!dasd_diag_discipline_pointer) {
+                       /* Try to load the required module. */
+                       rc = request_module(DASD_DIAG_MOD);
+                       if (rc) {
+                               pr_warn("%s Setting the DASD online failed "
+                                       "because the required module %s "
+                                       "could not be loaded (rc=%d)\n",
+                                       dev_name(&cdev->dev), DASD_DIAG_MOD,
+                                       rc);
+                               dasd_delete_device(device);
+                               return -ENODEV;
+                       }
+               }
+               /* Module init could have failed, so check again here after
+                * request_module(). */
+               if (!dasd_diag_discipline_pointer) {
                        pr_warn("%s Setting the DASD online failed because of missing DIAG discipline\n",
                                dev_name(&cdev->dev));
                        dasd_delete_device(device);
index 01463b052ae740aa56cd6ea816102a34961ed668..ef04a9f7a70494bea94032b7b4eb896eb11553a7 100644 (file)
@@ -433,20 +433,23 @@ do_kdgkb_ioctl(struct kbd_data *kbd, struct kbsentry __user *u_kbs,
        case KDSKBSENT:
                if (!perm)
                        return -EPERM;
-               len = strnlen_user(u_kbs->kb_string,
-                                  sizeof(u_kbs->kb_string) - 1);
+               len = strnlen_user(u_kbs->kb_string, sizeof(u_kbs->kb_string));
                if (!len)
                        return -EFAULT;
-               if (len > sizeof(u_kbs->kb_string) - 1)
+               if (len > sizeof(u_kbs->kb_string))
                        return -EINVAL;
-               p = kmalloc(len + 1, GFP_KERNEL);
+               p = kmalloc(len, GFP_KERNEL);
                if (!p)
                        return -ENOMEM;
                if (copy_from_user(p, u_kbs->kb_string, len)) {
                        kfree(p);
                        return -EFAULT;
                }
-               p[len] = 0;
+               /*
+                * Make sure the string is terminated by 0. User could have
+                * modified it between us running strnlen_user() and copying it.
+                */
+               p[len - 1] = 0;
                kfree(kbd->func_table[kb_func]);
                kbd->func_table[kb_func] = p;
                break;
index 41ba56d2e75218f6126ce61cd8c1f1443a5a9ada..5e20513c0587e82ba38eefa7604769e257036665 100644 (file)
@@ -665,7 +665,7 @@ sclp_state_change_cb(struct evbuf_header *evbuf)
                sclp_send_mask = scbuf->sclp_send_mask;
        spin_unlock_irqrestore(&sclp_lock, flags);
        if (scbuf->validity_sclp_active_facility_mask)
-               sclp_facilities = scbuf->sclp_active_facility_mask;
+               sclp.facilities = scbuf->sclp_active_facility_mask;
        sclp_dispatch_state_change();
 }
 
index a88069f8c677e437d4248a757ee33b659aac295a..026e3899095220fff6d18239975ae5db1f8adbe4 100644 (file)
@@ -100,13 +100,11 @@ struct init_sccb {
        sccb_mask_t sclp_send_mask;
 } __attribute__((packed));
 
-extern u64 sclp_facilities;
-
-#define SCLP_HAS_CHP_INFO      (sclp_facilities & 0x8000000000000000ULL)
-#define SCLP_HAS_CHP_RECONFIG  (sclp_facilities & 0x2000000000000000ULL)
-#define SCLP_HAS_CPU_INFO      (sclp_facilities & 0x0800000000000000ULL)
-#define SCLP_HAS_CPU_RECONFIG  (sclp_facilities & 0x0400000000000000ULL)
-#define SCLP_HAS_PCI_RECONFIG  (sclp_facilities & 0x0000000040000000ULL)
+#define SCLP_HAS_CHP_INFO      (sclp.facilities & 0x8000000000000000ULL)
+#define SCLP_HAS_CHP_RECONFIG  (sclp.facilities & 0x2000000000000000ULL)
+#define SCLP_HAS_CPU_INFO      (sclp.facilities & 0x0800000000000000ULL)
+#define SCLP_HAS_CPU_RECONFIG  (sclp.facilities & 0x0400000000000000ULL)
+#define SCLP_HAS_PCI_RECONFIG  (sclp.facilities & 0x0000000040000000ULL)
 
 
 struct gds_subvector {
@@ -191,9 +189,6 @@ void sclp_sdias_exit(void);
 extern int sclp_console_pages;
 extern int sclp_console_drop;
 extern unsigned long sclp_console_full;
-extern u8 sclp_fac84;
-extern unsigned long long sclp_rzm;
-extern unsigned long long sclp_rnmax;
 
 /* useful inlines */
 
index 7be782116dab0273641ea7a9cc7bc7d419944702..f74c040d5c10f5b21f505dfa7b0131c82ab02c83 100644 (file)
@@ -101,7 +101,7 @@ static void sclp_fill_cpu_info(struct sclp_cpu_info *info,
        info->configured = sccb->nr_configured;
        info->standby = sccb->nr_standby;
        info->combined = sccb->nr_configured + sccb->nr_standby;
-       info->has_cpu_type = sclp_fac84 & 0x1;
+       info->has_cpu_type = sclp.has_cpu_type;
        memcpy(&info->cpu, page + sccb->offset_configured,
               info->combined * sizeof(struct sclp_cpu_entry));
 }
@@ -186,7 +186,7 @@ int sclp_cpu_deconfigure(u8 cpu)
 static DEFINE_MUTEX(sclp_mem_mutex);
 static LIST_HEAD(sclp_mem_list);
 static u8 sclp_max_storage_id;
-static unsigned long sclp_storage_ids[256 / BITS_PER_LONG];
+static DECLARE_BITMAP(sclp_storage_ids, 256);
 static int sclp_mem_state_changed;
 
 struct memory_increment {
@@ -202,14 +202,14 @@ struct assign_storage_sccb {
 
 int arch_get_memory_phys_device(unsigned long start_pfn)
 {
-       if (!sclp_rzm)
+       if (!sclp.rzm)
                return 0;
-       return PFN_PHYS(start_pfn) >> ilog2(sclp_rzm);
+       return PFN_PHYS(start_pfn) >> ilog2(sclp.rzm);
 }
 
 static unsigned long long rn2addr(u16 rn)
 {
-       return (unsigned long long) (rn - 1) * sclp_rzm;
+       return (unsigned long long) (rn - 1) * sclp.rzm;
 }
 
 static int do_assign_storage(sclp_cmdw_t cmd, u16 rn)
@@ -250,7 +250,7 @@ static int sclp_assign_storage(u16 rn)
        if (rc)
                return rc;
        start = rn2addr(rn);
-       storage_key_init_range(start, start + sclp_rzm);
+       storage_key_init_range(start, start + sclp.rzm);
        return 0;
 }
 
@@ -309,7 +309,7 @@ static int sclp_mem_change_state(unsigned long start, unsigned long size,
                istart = rn2addr(incr->rn);
                if (start + size - 1 < istart)
                        break;
-               if (start > istart + sclp_rzm - 1)
+               if (start > istart + sclp.rzm - 1)
                        continue;
                if (online)
                        rc |= sclp_assign_storage(incr->rn);
@@ -330,7 +330,7 @@ static bool contains_standby_increment(unsigned long start, unsigned long end)
                istart = rn2addr(incr->rn);
                if (end - 1 < istart)
                        continue;
-               if (start > istart + sclp_rzm - 1)
+               if (start > istart + sclp.rzm - 1)
                        continue;
                if (incr->standby)
                        return true;
@@ -415,7 +415,7 @@ static void __init add_memory_merged(u16 rn)
        if (!first_rn)
                goto skip_add;
        start = rn2addr(first_rn);
-       size = (unsigned long long) num * sclp_rzm;
+       size = (unsigned long long) num * sclp.rzm;
        if (start >= VMEM_MAX_PHYS)
                goto skip_add;
        if (start + size > VMEM_MAX_PHYS)
@@ -465,7 +465,7 @@ static void __init insert_increment(u16 rn, int standby, int assigned)
        }
        if (!assigned)
                new_incr->rn = last_rn + 1;
-       if (new_incr->rn > sclp_rnmax) {
+       if (new_incr->rn > sclp.rnmax) {
                kfree(new_incr);
                return;
        }
@@ -508,7 +508,7 @@ static int __init sclp_detect_standby_memory(void)
 
        if (OLDMEM_BASE) /* No standby memory in kdump mode */
                return 0;
-       if ((sclp_facilities & 0xe00000000000ULL) != 0xe00000000000ULL)
+       if ((sclp.facilities & 0xe00000000000ULL) != 0xe00000000000ULL)
                return 0;
        rc = -ENOMEM;
        sccb = (void *) __get_free_page(GFP_KERNEL | GFP_DMA);
@@ -550,7 +550,7 @@ static int __init sclp_detect_standby_memory(void)
        }
        if (rc || list_empty(&sclp_mem_list))
                goto out;
-       for (i = 1; i <= sclp_rnmax - assigned; i++)
+       for (i = 1; i <= sclp.rnmax - assigned; i++)
                insert_increment(0, 1, 0);
        rc = register_memory_notifier(&sclp_mem_nb);
        if (rc)
@@ -753,8 +753,3 @@ out:
        free_page((unsigned long) sccb);
        return rc;
 }
-
-bool sclp_has_sprp(void)
-{
-       return !!(sclp_fac84 & 0x2);
-}
index 1efa4fdb7fe212daca1f6562366f2a0d25019a8e..d7f696d95597734568bb4cfb6f35f577a7bc6b30 100644 (file)
@@ -48,23 +48,10 @@ struct read_info_sccb {
 } __packed __aligned(PAGE_SIZE);
 
 static char sccb_early[PAGE_SIZE] __aligned(PAGE_SIZE) __initdata;
-static unsigned int sclp_con_has_vt220 __initdata;
-static unsigned int sclp_con_has_linemode __initdata;
-static unsigned long sclp_hsa_size;
-static unsigned int sclp_max_cpu;
 static struct sclp_ipl_info sclp_ipl_info;
-static unsigned char sclp_siif;
-static unsigned char sclp_sigpif;
-static u32 sclp_ibc;
-static unsigned int sclp_mtid;
-static unsigned int sclp_mtid_cp;
-static unsigned int sclp_mtid_max;
-static unsigned int sclp_mtid_prev;
-
-u64 sclp_facilities;
-u8 sclp_fac84;
-unsigned long long sclp_rzm;
-unsigned long long sclp_rnmax;
+
+struct sclp_info sclp;
+EXPORT_SYMBOL(sclp);
 
 static int __init sclp_cmd_sync_early(sclp_cmdw_t cmd, void *sccb)
 {
@@ -117,22 +104,23 @@ static void __init sclp_facilities_detect(struct read_info_sccb *sccb)
        if (sclp_read_info_early(sccb))
                return;
 
-       sclp_facilities = sccb->facilities;
-       sclp_fac84 = sccb->fac84;
+       sclp.facilities = sccb->facilities;
+       sclp.has_sprp = !!(sccb->fac84 & 0x02);
+       sclp.has_cpu_type = !!(sccb->fac84 & 0x01);
        if (sccb->fac85 & 0x02)
                S390_lowcore.machine_flags |= MACHINE_FLAG_ESOP;
-       sclp_rnmax = sccb->rnmax ? sccb->rnmax : sccb->rnmax2;
-       sclp_rzm = sccb->rnsize ? sccb->rnsize : sccb->rnsize2;
-       sclp_rzm <<= 20;
-       sclp_ibc = sccb->ibc;
+       sclp.rnmax = sccb->rnmax ? sccb->rnmax : sccb->rnmax2;
+       sclp.rzm = sccb->rnsize ? sccb->rnsize : sccb->rnsize2;
+       sclp.rzm <<= 20;
+       sclp.ibc = sccb->ibc;
 
        if (!sccb->hcpua) {
                if (MACHINE_IS_VM)
-                       sclp_max_cpu = 64;
+                       sclp.max_cpu = 64;
                else
-                       sclp_max_cpu = sccb->ncpurl;
+                       sclp.max_cpu = sccb->ncpurl;
        } else {
-               sclp_max_cpu = sccb->hcpua + 1;
+               sclp.max_cpu = sccb->hcpua + 1;
        }
 
        boot_cpu_address = stap();
@@ -140,8 +128,8 @@ static void __init sclp_facilities_detect(struct read_info_sccb *sccb)
        for (cpu = 0; cpu < sccb->ncpurl; cpue++, cpu++) {
                if (boot_cpu_address != cpue->core_id)
                        continue;
-               sclp_siif = cpue->siif;
-               sclp_sigpif = cpue->sigpif;
+               sclp.has_siif = cpue->siif;
+               sclp.has_sigpif = cpue->sigpif;
                break;
        }
 
@@ -151,68 +139,9 @@ static void __init sclp_facilities_detect(struct read_info_sccb *sccb)
                sclp_ipl_info.has_dump = 1;
        memcpy(&sclp_ipl_info.loadparm, &sccb->loadparm, LOADPARM_LEN);
 
-       sclp_mtid = (sccb->fac42 & 0x80) ? (sccb->fac42 & 31) : 0;
-       sclp_mtid_cp = (sccb->fac42 & 0x80) ? (sccb->fac43 & 31) : 0;
-       sclp_mtid_max = max(sclp_mtid, sclp_mtid_cp);
-       sclp_mtid_prev = (sccb->fac42 & 0x80) ? (sccb->fac66 & 31) : 0;
-}
-
-bool __init sclp_has_linemode(void)
-{
-       return !!sclp_con_has_linemode;
-}
-
-bool __init sclp_has_vt220(void)
-{
-       return !!sclp_con_has_vt220;
-}
-
-unsigned long long sclp_get_rnmax(void)
-{
-       return sclp_rnmax;
-}
-
-unsigned long long sclp_get_rzm(void)
-{
-       return sclp_rzm;
-}
-
-unsigned int sclp_get_max_cpu(void)
-{
-       return sclp_max_cpu;
-}
-
-int sclp_has_siif(void)
-{
-       return sclp_siif;
-}
-EXPORT_SYMBOL(sclp_has_siif);
-
-int sclp_has_sigpif(void)
-{
-       return sclp_sigpif;
-}
-EXPORT_SYMBOL(sclp_has_sigpif);
-
-unsigned int sclp_get_ibc(void)
-{
-       return sclp_ibc;
-}
-EXPORT_SYMBOL(sclp_get_ibc);
-
-unsigned int sclp_get_mtid(u8 cpu_type)
-{
-       return cpu_type ? sclp_mtid : sclp_mtid_cp;
-}
-
-unsigned int sclp_get_mtid_max(void)
-{
-       return sclp_mtid_max;
-}
-
-unsigned int sclp_get_mtid_prev(void)
-{
-       return sclp_mtid_prev;
+       sclp.mtid = (sccb->fac42 & 0x80) ? (sccb->fac42 & 31) : 0;
+       sclp.mtid_cp = (sccb->fac42 & 0x80) ? (sccb->fac43 & 31) : 0;
+       sclp.mtid_prev = (sccb->fac42 & 0x80) ? (sccb->fac66 & 31) : 0;
 }
 
 /*
@@ -286,11 +215,6 @@ static long __init sclp_hsa_copy_wait(struct sccb_header *sccb)
        return (((struct sdias_sccb *) sccb)->evbuf.blk_cnt - 1) * PAGE_SIZE;
 }
 
-unsigned long sclp_get_hsa_size(void)
-{
-       return sclp_hsa_size;
-}
-
 static void __init sclp_hsa_size_detect(void *sccb)
 {
        long size;
@@ -313,7 +237,7 @@ static void __init sclp_hsa_size_detect(void *sccb)
        if (size < 0)
                return;
 out:
-       sclp_hsa_size = size;
+       sclp.hsa_size = size;
 }
 
 static unsigned int __init sclp_con_check_linemode(struct init_sccb *sccb)
@@ -331,10 +255,10 @@ static void __init sclp_console_detect(struct init_sccb *sccb)
                return;
 
        if (sccb->sclp_send_mask & EVTYP_VT220MSG_MASK)
-               sclp_con_has_vt220 = 1;
+               sclp.has_vt220 = 1;
 
        if (sclp_con_check_linemode(sccb))
-               sclp_con_has_linemode = 1;
+               sclp.has_linemode = 1;
 }
 
 void __init sclp_early_detect(void)
index eb7cb076c00130c388ed1e395ca04182c220ee7c..7cdd13dd7be15a8d7aadd8f6baa5439d404269b0 100644 (file)
@@ -21,7 +21,6 @@
 #define TRACE(x...) debug_sprintf_event(sdias_dbf, 1, x)
 
 #define SDIAS_RETRIES 300
-#define SDIAS_SLEEP_TICKS 50
 
 static struct debug_info *sdias_dbf;
 
@@ -68,7 +67,7 @@ static int sdias_sclp_send(struct sclp_req *req)
                        /* not initiated, wait some time and retry */
                        set_current_state(TASK_INTERRUPTIBLE);
                        TRACE("add request failed: rc = %i\n",rc);
-                       schedule_timeout(SDIAS_SLEEP_TICKS);
+                       schedule_timeout(msecs_to_jiffies(500));
                        continue;
                }
                /* initiated, wait for completion of service call */
index a68fcfd1d48cb79928620a6acac6455e8c6fa79c..9a3dd95029cc8a7ce8d5a43e8b5cd659ca035d95 100644 (file)
@@ -330,9 +330,9 @@ static ssize_t zcore_read(struct file *file, char __user *buf, size_t count,
        mem_offs = 0;
 
        /* Copy from HSA data */
-       if (*ppos < sclp_get_hsa_size() + HEADER_SIZE) {
+       if (*ppos < sclp.hsa_size + HEADER_SIZE) {
                size = min((count - hdr_count),
-                          (size_t) (sclp_get_hsa_size() - mem_start));
+                          (size_t) (sclp.hsa_size - mem_start));
                rc = memcpy_hsa_user(buf + hdr_count, mem_start, size);
                if (rc)
                        goto fail;
@@ -483,7 +483,7 @@ static ssize_t zcore_hsa_read(struct file *filp, char __user *buf,
        static char str[18];
 
        if (hsa_available)
-               snprintf(str, sizeof(str), "%lx\n", sclp_get_hsa_size());
+               snprintf(str, sizeof(str), "%lx\n", sclp.hsa_size);
        else
                snprintf(str, sizeof(str), "0\n");
        return simple_read_from_buffer(buf, count, ppos, str, strlen(str));
@@ -558,7 +558,7 @@ static int __init sys_info_init(enum arch_id arch, unsigned long mem_end)
 
 static int __init check_sdias(void)
 {
-       if (!sclp_get_hsa_size()) {
+       if (!sclp.hsa_size) {
                TRACE("Could not determine HSA size\n");
                return -ENODEV;
        }
@@ -619,7 +619,7 @@ static int __init zcore_reipl_init(void)
        ipl_block = (void *) __get_free_page(GFP_KERNEL);
        if (!ipl_block)
                return -ENOMEM;
-       if (ipib_info.ipib < sclp_get_hsa_size())
+       if (ipib_info.ipib < sclp.hsa_size)
                rc = memcpy_hsa_kernel(ipl_block, ipib_info.ipib, PAGE_SIZE);
        else
                rc = memcpy_real(ipl_block, (void *) ipib_info.ipib, PAGE_SIZE);
index 4d14c04b746e14ebe656ae08bc14843aee093a45..9f18876f058b9b33a5c4f17cbd771fa534875cae 100644 (file)
@@ -98,11 +98,11 @@ static struct ap_driver zcrypt_pcicc_driver = {
  * - VUD block
  */
 static struct CPRB static_cprb = {
-       .cprb_len       = __constant_cpu_to_le16(0x0070),
+       .cprb_len       = cpu_to_le16(0x0070),
        .cprb_ver_id    =  0x41,
        .func_id        = {0x54,0x32},
        .checkpoint_flag=  0x01,
-       .svr_namel      = __constant_cpu_to_le16(0x0008),
+       .svr_namel      = cpu_to_le16(0x0008),
        .svr_name       = {'I','C','S','F',' ',' ',' ',' '}
 };
 
@@ -164,7 +164,7 @@ static int ICAMEX_msg_to_type6MEX_msg(struct zcrypt_device *zdev,
        };
        static struct function_and_rules_block static_pke_function_and_rules ={
                .function_code  = {'P','K'},
-               .ulen           = __constant_cpu_to_le16(10),
+               .ulen           = cpu_to_le16(10),
                .only_rule      = {'P','K','C','S','-','1','.','2'}
        };
        struct {
@@ -251,7 +251,7 @@ static int ICACRT_msg_to_type6CRT_msg(struct zcrypt_device *zdev,
        };
        static struct function_and_rules_block static_pkd_function_and_rules ={
                .function_code  = {'P','D'},
-               .ulen           = __constant_cpu_to_le16(10),
+               .ulen           = cpu_to_le16(10),
                .only_rule      = {'P','K','C','S','-','1','.','2'}
        };
        struct {
index dd65c8b4c7fe983336a7ff1596c7655837d851b7..53fb975c404b19b3f1d50f1f226cd0155accd683 100644 (file)
@@ -450,7 +450,7 @@ static int __init test_devices_support(unsigned long addr)
 static int __init kvm_devices_init(void)
 {
        int rc;
-       unsigned long total_memory_size = sclp_get_rzm() * sclp_get_rnmax();
+       unsigned long total_memory_size = sclp.rzm * sclp.rnmax;
 
        if (!MACHINE_IS_KVM)
                return -ENODEV;
@@ -497,7 +497,7 @@ static __init int early_put_chars(u32 vtermno, const char *buf, int count)
 
 static int __init s390_virtio_console_init(void)
 {
-       if (sclp_has_vt220() || sclp_has_linemode())
+       if (sclp.has_vt220 || sclp.has_linemode)
                return -ENODEV;
        return virtio_cons_early_init(early_put_chars);
 }
index 75f4bfc2b98ad21570588844140bf6546701cc50..b3c6ff49103b851f6467da7ff1fcb361865e2698 100644 (file)
@@ -297,7 +297,6 @@ static struct scsi_host_template zfcp_scsi_host_template = {
                                     * ZFCP_QDIO_MAX_SBALS_PER_REQ) - 2) * 8,
                                   /* GCD, adjusted later */
        .dma_boundary            = ZFCP_QDIO_SBALE_LEN - 1,
-       .cmd_per_lun             = 1,
        .use_clustering          = 1,
        .shost_attrs             = zfcp_sysfs_shost_attrs,
        .sdev_attrs              = zfcp_sysfs_sdev_attrs,
index b021bcb885375445ca5d4bf2820b6e29a1f7152c..456e1567841c5b0f7b103564b1cace5101d8a1e2 100644 (file)
@@ -52,7 +52,7 @@ config SCSI_MQ_DEFAULT
          This option enables the new blk-mq based I/O path for SCSI
          devices by default.  With the option the scsi_mod.use_blk_mq
          module/boot option defaults to Y, without it to N, but it can
-         still be overriden either way.
+         still be overridden either way.
 
          If unsure say N.
 
@@ -503,7 +503,7 @@ config SCSI_DPT_I2O
 
 config SCSI_ADVANSYS
        tristate "AdvanSys SCSI support"
-       depends on SCSI && VIRT_TO_BUS && !ARM
+       depends on SCSI
        depends on ISA || EISA || PCI
        help
          This is a driver for all SCSI host adapters manufactured by
@@ -634,6 +634,23 @@ config FCOE_FNIC
          <file:Documentation/scsi/scsi.txt>.
          The module will be called fnic.
 
+config SCSI_SNIC
+       tristate "Cisco SNIC Driver"
+       depends on PCI && SCSI
+       help
+         This is support for the Cisco PCI-Express SCSI HBA.
+
+         To compile this driver as a module, choose M here and read
+         <file:Documentation/scsi/scsi.txt>.
+         The module will be called snic.
+
+config SCSI_SNIC_DEBUG_FS
+       bool "Cisco SNIC Driver Debugfs Support"
+       depends on SCSI_SNIC && DEBUG_FS
+       help
+         This enables to list debugging information from SNIC Driver
+         available via debugfs file system
+
 config SCSI_DMX3191D
        tristate "DMX3191D SCSI support"
        depends on PCI && SCSI
@@ -1743,7 +1760,6 @@ config SCSI_BFA_FC
 config SCSI_VIRTIO
        tristate "virtio-scsi support"
        depends on VIRTIO
-       select BLK_DEV_INTEGRITY
        help
           This is the virtual HBA driver for virtio.  If the kernel will
           be used in a virtual machine, say Y or M.
index dee160a4f163a6b55ae105d8e8a517ea4ce93eb3..91209e3d27e3e18c0ef77da7e87f6fc990d2564f 100644 (file)
@@ -39,6 +39,7 @@ obj-$(CONFIG_LIBFC)           += libfc/
 obj-$(CONFIG_LIBFCOE)          += fcoe/
 obj-$(CONFIG_FCOE)             += fcoe/
 obj-$(CONFIG_FCOE_FNIC)                += fnic/
+obj-$(CONFIG_SCSI_SNIC)                += snic/
 obj-$(CONFIG_SCSI_BNX2X_FCOE)  += libfc/ fcoe/ bnx2fc/
 obj-$(CONFIG_ISCSI_TCP)        += libiscsi.o   libiscsi_tcp.o iscsi_tcp.o
 obj-$(CONFIG_INFINIBAND_ISER)  += libiscsi.o
@@ -161,6 +162,7 @@ obj-$(CONFIG_SCSI_OSD_INITIATOR) += osd/
 obj-$(CONFIG_SCSI_DEBUG)       += scsi_debug.o
 scsi_mod-y                     += scsi.o hosts.o scsi_ioctl.o \
                                   scsicam.o scsi_error.o scsi_lib.o
+scsi_mod-y                     += scsi_common.o
 scsi_mod-$(CONFIG_SCSI_CONSTANTS) += constants.o
 scsi_mod-$(CONFIG_SCSI_DMA)    += scsi_lib_dma.o
 scsi_mod-y                     += scsi_scan.o scsi_sysfs.o scsi_devinfo.o
index 42c7161474f7b31ca4622f4ffe91ca4da333fb1e..6e110c630d2ce272d8b13480f91c4599af6774df 100644 (file)
@@ -1064,7 +1064,6 @@ static struct scsi_host_template driver_template =
      .can_queue                = 1                     /* can_queue */,        
      .this_id                  = 7                     /* SCSI ID of the chip */,
      .sg_tablesize             = 32                    /*SG_ALL*/ /*SG_NONE*/, 
-     .cmd_per_lun              = 1                     /* commands per lun */, 
      .unchecked_isa_dma        = 1                     /* unchecked_isa_dma */,
      .use_clustering           = ENABLE_CLUSTERING,
 };
index 7e33a61c1ba45ed91db2b0fe86d4385eaac1e762..cac6b37d7b1b77baf9225b5ee8fb441480a0704c 100644 (file)
@@ -1078,7 +1078,6 @@ static struct scsi_host_template inia100_template = {
        .can_queue              = 1,
        .this_id                = 1,
        .sg_tablesize           = SG_ALL,
-       .cmd_per_lun            = 1,
        .use_clustering         = ENABLE_CLUSTERING,
 };
 
index 4596e9dd757c0a68556356e697900accc1af335f..e63cf9f22f36a40f2e4bf1320cbc695e5a40812e 100644 (file)
@@ -46,7 +46,7 @@
 
 static int aac_src_get_sync_status(struct aac_dev *dev);
 
-irqreturn_t aac_src_intr_message(int irq, void *dev_id)
+static irqreturn_t aac_src_intr_message(int irq, void *dev_id)
 {
        struct aac_msix_ctx *ctx;
        struct aac_dev *dev;
index ae95e347f37d6a505eee3ba89006bb9bc17dd13b..4305178e4e0147ba9a8f8e04a452e77947f2cddb 100644 (file)
@@ -1,12 +1,10 @@
-#define DRV_NAME "advansys"
-#define ASC_VERSION "3.4"      /* AdvanSys Driver Version */
-
 /*
  * advansys.c - Linux Host Driver for AdvanSys SCSI Adapters
  *
  * Copyright (c) 1995-2000 Advanced System Products, Inc.
  * Copyright (c) 2000-2001 ConnectCom Solutions, Inc.
  * Copyright (c) 2007 Matthew Wilcox <matthew@wil.cx>
+ * Copyright (c) 2014 Hannes Reinecke <hare@suse.de>
  * All Rights Reserved.
  *
  * This program is free software; you can redistribute it and/or modify
@@ -39,6 +37,7 @@
 #include <linux/spinlock.h>
 #include <linux/dma-mapping.h>
 #include <linux/firmware.h>
+#include <linux/dmapool.h>
 
 #include <asm/io.h>
 #include <asm/dma.h>
 #include <scsi/scsi.h>
 #include <scsi/scsi_host.h>
 
+#define DRV_NAME "advansys"
+#define ASC_VERSION "3.5"      /* AdvanSys Driver Version */
+
 /* FIXME:
  *
- *  1. Although all of the necessary command mapping places have the
- *     appropriate dma_map.. APIs, the driver still processes its internal
- *     queue using bus_to_virt() and virt_to_bus() which are illegal under
- *     the API.  The entire queue processing structure will need to be
- *     altered to fix this.
- *  2. Need to add memory mapping workaround. Test the memory mapping.
- *     If it doesn't work revert to I/O port access. Can a test be done
- *     safely?
- *  3. Handle an interrupt not working. Keep an interrupt counter in
- *     the interrupt handler. In the timeout function if the interrupt
- *     has not occurred then print a message and run in polled mode.
- *  4. Need to add support for target mode commands, cf. CAM XPT.
- *  5. check DMA mapping functions for failure
- *  6. Use scsi_transport_spi
- *  7. advansys_info is not safe against multiple simultaneous callers
- *  8. Add module_param to override ISA/VLB ioport array
+ *  1. Use scsi_transport_spi
+ *  2. advansys_info is not safe against multiple simultaneous callers
+ *  3. Add module_param to override ISA/VLB ioport array
  */
-#warning this driver is still not properly converted to the DMA API
 
 /* Enable driver /proc statistics. */
 #define ADVANSYS_STATS
 /* Enable driver tracing. */
 #undef ADVANSYS_DEBUG
 
-/*
- * Portable Data Types
- *
- * Any instance where a 32-bit long or pointer type is assumed
- * for precision or HW defined structures, the following define
- * types must be used. In Linux the char, short, and int types
- * are all consistent at 8, 16, and 32 bits respectively. Pointers
- * and long types are 64 bits on Alpha and UltraSPARC.
- */
-#define ASC_PADDR __u32                /* Physical/Bus address data type. */
-#define ASC_VADDR __u32                /* Virtual address data type. */
-#define ASC_DCNT  __u32                /* Unsigned Data count type. */
-#define ASC_SDCNT __s32                /* Signed Data count type. */
-
 typedef unsigned char uchar;
 
-#ifndef TRUE
-#define TRUE     (1)
-#endif
-#ifndef FALSE
-#define FALSE    (0)
-#endif
-
-#define ERR      (-1)
-#define UW_ERR   (uint)(0xFFFF)
 #define isodd_word(val)   ((((uint)val) & (uint)0x0001) != 0)
 
 #define PCI_VENDOR_ID_ASP              0x10cd
@@ -111,15 +76,6 @@ typedef unsigned char uchar;
 #define PCI_DEVICE_ID_38C0800_REV1     0x2500
 #define PCI_DEVICE_ID_38C1600_REV1     0x2700
 
-/*
- * Enable CC_VERY_LONG_SG_LIST to support up to 64K element SG lists.
- * The SRB structure will have to be changed and the ASC_SRB2SCSIQ()
- * macro re-defined to be able to obtain a ASC_SCSI_Q pointer from the
- * SRB structure.
- */
-#define CC_VERY_LONG_SG_LIST 0
-#define ASC_SRB2SCSIQ(srb_ptr)  (srb_ptr)
-
 #define PortAddr                 unsigned int  /* port address size  */
 #define inp(port)                inb(port)
 #define outp(port, byte)         outb((byte), (port))
@@ -307,15 +263,15 @@ typedef struct asc_scsiq_1 {
        uchar sg_queue_cnt;
        uchar target_id;
        uchar target_lun;
-       ASC_PADDR data_addr;
-       ASC_DCNT data_cnt;
-       ASC_PADDR sense_addr;
+       __le32 data_addr;
+       __le32 data_cnt;
+       __le32 sense_addr;
        uchar sense_len;
        uchar extra_bytes;
 } ASC_SCSIQ_1;
 
 typedef struct asc_scsiq_2 {
-       ASC_VADDR srb_ptr;
+       u32 srb_tag;
        uchar target_ix;
        uchar flag;
        uchar cdb_len;
@@ -338,8 +294,8 @@ typedef struct asc_scsiq_4 {
        uchar y_res;
        ushort x_req_count;
        ushort x_reconnect_rtn;
-       ASC_PADDR x_saved_data_addr;
-       ASC_DCNT x_saved_data_cnt;
+       __le32 x_saved_data_addr;
+       __le32 x_saved_data_cnt;
 } ASC_SCSIQ_4;
 
 typedef struct asc_q_done_info {
@@ -351,12 +307,12 @@ typedef struct asc_q_done_info {
        uchar sense_len;
        uchar extra_bytes;
        uchar res;
-       ASC_DCNT remain_bytes;
+       u32 remain_bytes;
 } ASC_QDONE_INFO;
 
 typedef struct asc_sg_list {
-       ASC_PADDR addr;
-       ASC_DCNT bytes;
+       __le32 addr;
+       __le32 bytes;
 } ASC_SG_LIST;
 
 typedef struct asc_sg_head {
@@ -376,17 +332,6 @@ typedef struct asc_scsi_q {
        ushort next_sg_index;
 } ASC_SCSI_Q;
 
-typedef struct asc_scsi_req_q {
-       ASC_SCSIQ_1 r1;
-       ASC_SCSIQ_2 r2;
-       uchar *cdbptr;
-       ASC_SG_HEAD *sg_head;
-       uchar *sense_ptr;
-       ASC_SCSIQ_3 r3;
-       uchar cdb[ASC_MAX_CDB_LEN];
-       uchar sense[ASC_MIN_SENSE_LEN];
-} ASC_SCSI_REQ_Q;
-
 typedef struct asc_scsi_bios_req_q {
        ASC_SCSIQ_1 r1;
        ASC_SCSIQ_2 r2;
@@ -570,7 +515,7 @@ typedef struct asc_dvc_var {
        dma_addr_t overrun_dma;
        uchar scsi_reset_wait;
        uchar chip_no;
-       char is_in_int;
+       bool is_in_int;
        uchar max_total_qng;
        uchar cur_total_qng;
        uchar in_critical_cnt;
@@ -586,15 +531,13 @@ typedef struct asc_dvc_var {
        char redo_scam;
        ushort res2;
        uchar dos_int13_table[ASC_MAX_TID + 1];
-       ASC_DCNT max_dma_count;
+       unsigned int max_dma_count;
        ASC_SCSI_BIT_ID_TYPE no_scam;
        ASC_SCSI_BIT_ID_TYPE pci_fix_asyn_xfer;
        uchar min_sdtr_index;
        uchar max_sdtr_index;
        struct asc_board *drv_ptr;
-       int ptr_map_count;
-       void **ptr_map;
-       ASC_DCNT uc_break;
+       unsigned int uc_break;
 } ASC_DVC_VAR;
 
 typedef struct asc_dvc_inq_info {
@@ -602,8 +545,8 @@ typedef struct asc_dvc_inq_info {
 } ASC_DVC_INQ_INFO;
 
 typedef struct asc_cap_info {
-       ASC_DCNT lba;
-       ASC_DCNT blk_size;
+       u32 lba;
+       u32 blk_size;
 } ASC_CAP_INFO;
 
 typedef struct asc_cap_info_array {
@@ -929,31 +872,6 @@ typedef struct asc_mc_saved {
 #define AscReadChipDvcID(port)            (uchar)inp((port)+IOP_REG_ID)
 #define AscWriteChipDvcID(port, data)     outp((port)+IOP_REG_ID, data)
 
-/*
- * Portable Data Types
- *
- * Any instance where a 32-bit long or pointer type is assumed
- * for precision or HW defined structures, the following define
- * types must be used. In Linux the char, short, and int types
- * are all consistent at 8, 16, and 32 bits respectively. Pointers
- * and long types are 64 bits on Alpha and UltraSPARC.
- */
-#define ADV_PADDR __u32                /* Physical address data type. */
-#define ADV_VADDR __u32                /* Virtual address data type. */
-#define ADV_DCNT  __u32                /* Unsigned Data count type. */
-#define ADV_SDCNT __s32                /* Signed Data count type. */
-
-/*
- * These macros are used to convert a virtual address to a
- * 32-bit value. This currently can be used on Linux Alpha
- * which uses 64-bit virtual address but a 32-bit bus address.
- * This is likely to break in the future, but doing this now
- * will give us time to change the HW and FW to handle 64-bit
- * addresses.
- */
-#define ADV_VADDR_TO_U32   virt_to_bus
-#define ADV_U32_TO_VADDR   bus_to_virt
-
 #define AdvPortAddr  void __iomem *    /* Virtual memory address size */
 
 /*
@@ -965,8 +883,6 @@ typedef struct asc_mc_saved {
 #define ADV_MEM_WRITEW(addr, word) writew(word, addr)
 #define ADV_MEM_WRITEDW(addr, dword) writel(dword, addr)
 
-#define ADV_CARRIER_COUNT (ASC_DEF_MAX_HOST_QNG + 15)
-
 /*
  * Define total number of simultaneous maximum element scatter-gather
  * request blocks per wide adapter. ASC_DEF_MAX_HOST_QNG (253) is the
@@ -1747,44 +1663,37 @@ typedef struct adveep_38C1600_config {
  * little-endian.
  */
 typedef struct adv_carr_t {
-       ADV_VADDR carr_va;      /* Carrier Virtual Address */
-       ADV_PADDR carr_pa;      /* Carrier Physical Address */
-       ADV_VADDR areq_vpa;     /* ASC_SCSI_REQ_Q Virtual or Physical Address */
+       __le32 carr_va; /* Carrier Virtual Address */
+       __le32 carr_pa; /* Carrier Physical Address */
+       __le32 areq_vpa;        /* ADV_SCSI_REQ_Q Virtual or Physical Address */
        /*
         * next_vpa [31:4]            Carrier Virtual or Physical Next Pointer
         *
         * next_vpa [3:1]             Reserved Bits
         * next_vpa [0]               Done Flag set in Response Queue.
         */
-       ADV_VADDR next_vpa;
+       __le32 next_vpa;
 } ADV_CARR_T;
 
 /*
  * Mask used to eliminate low 4 bits of carrier 'next_vpa' field.
  */
-#define ASC_NEXT_VPA_MASK       0xFFFFFFF0
-
-#define ASC_RQ_DONE             0x00000001
-#define ASC_RQ_GOOD             0x00000002
-#define ASC_CQ_STOPPER          0x00000000
+#define ADV_NEXT_VPA_MASK       0xFFFFFFF0
 
-#define ASC_GET_CARRP(carrp) ((carrp) & ASC_NEXT_VPA_MASK)
+#define ADV_RQ_DONE             0x00000001
+#define ADV_RQ_GOOD             0x00000002
+#define ADV_CQ_STOPPER          0x00000000
 
-#define ADV_CARRIER_NUM_PAGE_CROSSING \
-    (((ADV_CARRIER_COUNT * sizeof(ADV_CARR_T)) + (PAGE_SIZE - 1))/PAGE_SIZE)
-
-#define ADV_CARRIER_BUFSIZE \
-    ((ADV_CARRIER_COUNT + ADV_CARRIER_NUM_PAGE_CROSSING) * sizeof(ADV_CARR_T))
+#define ADV_GET_CARRP(carrp) ((carrp) & ADV_NEXT_VPA_MASK)
 
 /*
- * ASC_SCSI_REQ_Q 'a_flag' definitions
- *
- * The Adv Library should limit use to the lower nibble (4 bits) of
- * a_flag. Drivers are free to use the upper nibble (4 bits) of a_flag.
+ * Each carrier is 64 bytes, and we need three additional
+ * carrier for icq, irq, and the termination carrier.
  */
-#define ADV_POLL_REQUEST                0x01   /* poll for request completion */
-#define ADV_SCSIQ_DONE                  0x02   /* request done */
-#define ADV_DONT_RETRY                  0x08   /* don't do retry */
+#define ADV_CARRIER_COUNT (ASC_DEF_MAX_HOST_QNG + 3)
+
+#define ADV_CARRIER_BUFSIZE \
+       (ADV_CARRIER_COUNT * sizeof(ADV_CARR_T))
 
 #define ADV_CHIP_ASC3550          0x01 /* Ultra-Wide IC */
 #define ADV_CHIP_ASC38C0800       0x02 /* Ultra2-Wide/LVD IC */
@@ -1816,15 +1725,15 @@ typedef struct adv_dvc_cfg {
 struct adv_dvc_var;
 struct adv_scsi_req_q;
 
-typedef struct asc_sg_block {
+typedef struct adv_sg_block {
        uchar reserved1;
        uchar reserved2;
        uchar reserved3;
        uchar sg_cnt;           /* Valid entries in block. */
-       ADV_PADDR sg_ptr;       /* Pointer to next sg block. */
+       __le32 sg_ptr;  /* Pointer to next sg block. */
        struct {
-               ADV_PADDR sg_addr;      /* SG element address. */
-               ADV_DCNT sg_count;      /* SG element count. */
+               __le32 sg_addr; /* SG element address. */
+               __le32 sg_count;        /* SG element count. */
        } sg_list[NO_OF_SG_PER_BLOCK];
 } ADV_SG_BLOCK;
 
@@ -1844,10 +1753,10 @@ typedef struct adv_scsi_req_q {
        uchar target_cmd;
        uchar target_id;        /* Device target identifier. */
        uchar target_lun;       /* Device target logical unit number. */
-       ADV_PADDR data_addr;    /* Data buffer physical address. */
-       ADV_DCNT data_cnt;      /* Data count. Ucode sets to residual. */
-       ADV_PADDR sense_addr;
-       ADV_PADDR carr_pa;
+       __le32 data_addr;       /* Data buffer physical address. */
+       __le32 data_cnt;        /* Data count. Ucode sets to residual. */
+       __le32 sense_addr;
+       __le32 carr_pa;
        uchar mflag;
        uchar sense_len;
        uchar cdb_len;          /* SCSI CDB length. Must <= 16 bytes. */
@@ -1857,29 +1766,26 @@ typedef struct adv_scsi_req_q {
        uchar host_status;      /* Ucode host status. */
        uchar sg_working_ix;
        uchar cdb[12];          /* SCSI CDB bytes 0-11. */
-       ADV_PADDR sg_real_addr; /* SG list physical address. */
-       ADV_PADDR scsiq_rptr;
+       __le32 sg_real_addr;    /* SG list physical address. */
+       __le32 scsiq_rptr;
        uchar cdb16[4];         /* SCSI CDB bytes 12-15. */
-       ADV_VADDR scsiq_ptr;
-       ADV_VADDR carr_va;
+       __le32 scsiq_ptr;
+       __le32 carr_va;
        /*
         * End of microcode structure - 60 bytes. The rest of the structure
         * is used by the Adv Library and ignored by the microcode.
         */
-       ADV_VADDR srb_ptr;
+       u32 srb_tag;
        ADV_SG_BLOCK *sg_list_ptr;      /* SG list virtual address. */
-       char *vdata_addr;       /* Data buffer virtual address. */
-       uchar a_flag;
-       uchar pad[2];           /* Pad out to a word boundary. */
 } ADV_SCSI_REQ_Q;
 
 /*
  * The following two structures are used to process Wide Board requests.
  *
  * The ADV_SCSI_REQ_Q structure in adv_req_t is passed to the Adv Library
- * and microcode with the ADV_SCSI_REQ_Q field 'srb_ptr' pointing to the
- * adv_req_t. The adv_req_t structure 'cmndp' field in turn points to the
- * Mid-Level SCSI request structure.
+ * and microcode with the ADV_SCSI_REQ_Q field 'srb_tag' set to the
+ * SCSI request tag. The adv_req_t structure 'cmndp' field in turn points
+ * to the Mid-Level SCSI request structure.
  *
  * Zero or more ADV_SG_BLOCK are used with each ADV_SCSI_REQ_Q. Each
  * ADV_SG_BLOCK structure holds 15 scatter-gather elements. Under Linux
@@ -1890,17 +1796,17 @@ typedef struct adv_scsi_req_q {
  */
 typedef struct adv_sgblk {
        ADV_SG_BLOCK sg_block;  /* Sgblock structure. */
-       uchar align[32];        /* Sgblock structure padding. */
+       dma_addr_t sg_addr;     /* Physical address */
        struct adv_sgblk *next_sgblkp;  /* Next scatter-gather structure. */
 } adv_sgblk_t;
 
 typedef struct adv_req {
        ADV_SCSI_REQ_Q scsi_req_q;      /* Adv Library request structure. */
-       uchar align[32];        /* Request structure padding. */
+       uchar align[24];        /* Request structure padding. */
        struct scsi_cmnd *cmndp;        /* Mid-Level SCSI command pointer. */
+       dma_addr_t req_addr;
        adv_sgblk_t *sgblkp;    /* Adv Library scatter-gather pointer. */
-       struct adv_req *next_reqp;      /* Next Request Structure. */
-} adv_req_t;
+} adv_req_t __aligned(32);
 
 /*
  * Adapter operation variable structure.
@@ -1937,12 +1843,12 @@ typedef struct adv_dvc_var {
        uchar chip_scsi_id;     /* chip SCSI target ID */
        uchar chip_type;
        uchar bist_err_code;
-       ADV_CARR_T *carrier_buf;
+       ADV_CARR_T *carrier;
        ADV_CARR_T *carr_freelist;      /* Carrier free list. */
+       dma_addr_t carrier_addr;
        ADV_CARR_T *icq_sp;     /* Initiator command queue stopper pointer. */
        ADV_CARR_T *irq_sp;     /* Initiator response queue stopper pointer. */
        ushort carr_pending_cnt;        /* Count of pending carriers. */
-       struct adv_req *orig_reqp;      /* adv_req_t memory block. */
        /*
         * Note: The following fields will not be used after initialization. The
         * driver may discard the buffer after initialization is done.
@@ -2068,8 +1974,8 @@ do { \
     AdvReadByteRegister((iop_base), IOPB_CHIP_TYPE_REV)
 
 /*
- * Abort an SRB in the chip's RISC Memory. The 'srb_ptr' argument must
- * match the ASC_SCSI_REQ_Q 'srb_ptr' field.
+ * Abort an SRB in the chip's RISC Memory. The 'srb_tag' argument must
+ * match the ADV_SCSI_REQ_Q 'srb_tag' field.
  *
  * If the request has not yet been sent to the device it will simply be
  * aborted from RISC memory. If the request is disconnected it will be
@@ -2079,9 +1985,9 @@ do { \
  *      ADV_TRUE(1) - Queue was successfully aborted.
  *      ADV_FALSE(0) - Queue was not found on the active queue list.
  */
-#define AdvAbortQueue(asc_dvc, scsiq) \
-        AdvSendIdleCmd((asc_dvc), (ushort) IDLE_CMD_ABORT, \
-                       (ADV_DCNT) (scsiq))
+#define AdvAbortQueue(asc_dvc, srb_tag) \
+     AdvSendIdleCmd((asc_dvc), (ushort) IDLE_CMD_ABORT, \
+                   (ADV_DCNT) (srb_tag))
 
 /*
  * Send a Bus Device Reset Message to the specified target ID.
@@ -2095,8 +2001,8 @@ do { \
  *                     are not purged.
  */
 #define AdvResetDevice(asc_dvc, target_id) \
-        AdvSendIdleCmd((asc_dvc), (ushort) IDLE_CMD_DEVICE_RESET, \
-                    (ADV_DCNT) (target_id))
+     AdvSendIdleCmd((asc_dvc), (ushort) IDLE_CMD_DEVICE_RESET, \
+                   (ADV_DCNT) (target_id))
 
 /*
  * SCSI Wide Type definition.
@@ -2115,7 +2021,7 @@ do { \
 #define ADV_TID_TO_TIDMASK(tid)   (0x01 << ((tid) & ADV_MAX_TID))
 
 /*
- * ASC_SCSI_REQ_Q 'done_status' and 'host_status' return values.
+ * ADV_SCSI_REQ_Q 'done_status' and 'host_status' return values.
  */
 
 #define QD_NO_STATUS         0x00      /* Request not completed yet. */
@@ -2153,8 +2059,6 @@ do { \
 #define QHSTA_M_SGBACKUP_ERROR      0x47       /* Scatter-Gather backup error */
 
 /* Return the address that is aligned at the next doubleword >= to 'addr'. */
-#define ADV_8BALIGN(addr)      (((ulong) (addr) + 0x7) & ~0x7)
-#define ADV_16BALIGN(addr)     (((ulong) (addr) + 0xF) & ~0xF)
 #define ADV_32BALIGN(addr)     (((ulong) (addr) + 0x1F) & ~0x1F)
 
 /*
@@ -2315,24 +2219,24 @@ do { \
 /* Per board statistics structure */
 struct asc_stats {
        /* Driver Entrypoint Statistics */
-       ADV_DCNT queuecommand;  /* # calls to advansys_queuecommand() */
-       ADV_DCNT reset;         /* # calls to advansys_eh_bus_reset() */
-       ADV_DCNT biosparam;     /* # calls to advansys_biosparam() */
-       ADV_DCNT interrupt;     /* # advansys_interrupt() calls */
-       ADV_DCNT callback;      /* # calls to asc/adv_isr_callback() */
-       ADV_DCNT done;          /* # calls to request's scsi_done function */
-       ADV_DCNT build_error;   /* # asc/adv_build_req() ASC_ERROR returns. */
-       ADV_DCNT adv_build_noreq;       /* # adv_build_req() adv_req_t alloc. fail. */
-       ADV_DCNT adv_build_nosg;        /* # adv_build_req() adv_sgblk_t alloc. fail. */
+       unsigned int queuecommand;      /* # calls to advansys_queuecommand() */
+       unsigned int reset;             /* # calls to advansys_eh_bus_reset() */
+       unsigned int biosparam; /* # calls to advansys_biosparam() */
+       unsigned int interrupt; /* # advansys_interrupt() calls */
+       unsigned int callback;  /* # calls to asc/adv_isr_callback() */
+       unsigned int done;              /* # calls to request's scsi_done function */
+       unsigned int build_error;       /* # asc/adv_build_req() ASC_ERROR returns. */
+       unsigned int adv_build_noreq;   /* # adv_build_req() adv_req_t alloc. fail. */
+       unsigned int adv_build_nosg;    /* # adv_build_req() adv_sgblk_t alloc. fail. */
        /* AscExeScsiQueue()/AdvExeScsiQueue() Statistics */
-       ADV_DCNT exe_noerror;   /* # ASC_NOERROR returns. */
-       ADV_DCNT exe_busy;      /* # ASC_BUSY returns. */
-       ADV_DCNT exe_error;     /* # ASC_ERROR returns. */
-       ADV_DCNT exe_unknown;   /* # unknown returns. */
+       unsigned int exe_noerror;       /* # ASC_NOERROR returns. */
+       unsigned int exe_busy;  /* # ASC_BUSY returns. */
+       unsigned int exe_error; /* # ASC_ERROR returns. */
+       unsigned int exe_unknown;       /* # unknown returns. */
        /* Data Transfer Statistics */
-       ADV_DCNT xfer_cnt;      /* # I/O requests received */
-       ADV_DCNT xfer_elem;     /* # scatter-gather elements */
-       ADV_DCNT xfer_sect;     /* # 512-byte blocks */
+       unsigned int xfer_cnt;  /* # I/O requests received */
+       unsigned int xfer_elem; /* # scatter-gather elements */
+       unsigned int xfer_sect; /* # 512-byte blocks */
 };
 #endif /* ADVANSYS_STATS */
 
@@ -2345,6 +2249,7 @@ struct asc_stats {
  */
 struct asc_board {
        struct device *dev;
+       struct Scsi_Host *shost;
        uint flags;             /* Board flags */
        unsigned int irq;
        union {
@@ -2366,7 +2271,6 @@ struct asc_board {
                ADVEEP_38C0800_CONFIG adv_38C0800_eep;  /* 38C0800 EEPROM config. */
                ADVEEP_38C1600_CONFIG adv_38C1600_eep;  /* 38C1600 EEPROM config. */
        } eep_config;
-       ulong last_reset;       /* Saved last reset time */
        /* /proc/scsi/advansys/[0...] */
 #ifdef ADVANSYS_STATS
        struct asc_stats asc_stats;     /* Board statistics */
@@ -2381,7 +2285,9 @@ struct asc_board {
        void __iomem *ioremap_addr;     /* I/O Memory remap address. */
        ushort ioport;          /* I/O Port address. */
        adv_req_t *adv_reqp;    /* Request structures. */
-       adv_sgblk_t *adv_sgblkp;        /* Scatter-gather structures. */
+       dma_addr_t adv_reqp_addr;
+       size_t adv_reqp_size;
+       struct dma_pool *adv_sgblk_pool;        /* Scatter-gather structures. */
        ushort bios_signature;  /* BIOS Signature. */
        ushort bios_version;    /* BIOS Version. */
        ushort bios_codeseg;    /* BIOS Code Segment. */
@@ -2470,12 +2376,11 @@ static void asc_prt_adv_dvc_var(ADV_DVC_VAR *h)
        printk("  start_motor 0x%x, scsi_reset_wait 0x%x\n",
               (unsigned)h->start_motor, (unsigned)h->scsi_reset_wait);
 
-       printk("  max_host_qng %u, max_dvc_qng %u, carr_freelist 0x%lxn\n",
+       printk("  max_host_qng %u, max_dvc_qng %u, carr_freelist 0x%p\n",
               (unsigned)h->max_host_qng, (unsigned)h->max_dvc_qng,
-              (ulong)h->carr_freelist);
+              h->carr_freelist);
 
-       printk("  icq_sp 0x%lx, irq_sp 0x%lx\n",
-              (ulong)h->icq_sp, (ulong)h->irq_sp);
+       printk("  icq_sp 0x%p, irq_sp 0x%p\n", h->icq_sp, h->irq_sp);
 
        printk("  no_scam 0x%x, tagqng_able 0x%x\n",
               (unsigned)h->no_scam, (unsigned)h->tagqng_able);
@@ -2600,8 +2505,8 @@ static void asc_prt_asc_scsi_q(ASC_SCSI_Q *q)
        printk("ASC_SCSI_Q at addr 0x%lx\n", (ulong)q);
 
        printk
-           (" target_ix 0x%x, target_lun %u, srb_ptr 0x%lx, tag_code 0x%x,\n",
-            q->q2.target_ix, q->q1.target_lun, (ulong)q->q2.srb_ptr,
+           (" target_ix 0x%x, target_lun %u, srb_tag 0x%x, tag_code 0x%x,\n",
+            q->q2.target_ix, q->q1.target_lun, q->q2.srb_tag,
             q->q2.tag_code);
 
        printk
@@ -2634,8 +2539,8 @@ static void asc_prt_asc_scsi_q(ASC_SCSI_Q *q)
 static void asc_prt_asc_qdone_info(ASC_QDONE_INFO *q)
 {
        printk("ASC_QDONE_INFO at addr 0x%lx\n", (ulong)q);
-       printk(" srb_ptr 0x%lx, target_ix %u, cdb_len %u, tag_code %u,\n",
-              (ulong)q->d2.srb_ptr, q->d2.target_ix, q->d2.cdb_len,
+       printk(" srb_tag 0x%x, target_ix %u, cdb_len %u, tag_code %u,\n",
+              q->d2.srb_tag, q->d2.target_ix, q->d2.cdb_len,
               q->d2.tag_code);
        printk
            (" done_stat 0x%x, host_stat 0x%x, scsi_stat 0x%x, scsi_msg 0x%x\n",
@@ -2651,17 +2556,17 @@ static void asc_prt_adv_sgblock(int sgblockno, ADV_SG_BLOCK *b)
 {
        int i;
 
-       printk(" ASC_SG_BLOCK at addr 0x%lx (sgblockno %d)\n",
+       printk(" ADV_SG_BLOCK at addr 0x%lx (sgblockno %d)\n",
               (ulong)b, sgblockno);
-       printk("  sg_cnt %u, sg_ptr 0x%lx\n",
-              b->sg_cnt, (ulong)le32_to_cpu(b->sg_ptr));
+       printk("  sg_cnt %u, sg_ptr 0x%x\n",
+              b->sg_cnt, (u32)le32_to_cpu(b->sg_ptr));
        BUG_ON(b->sg_cnt > NO_OF_SG_PER_BLOCK);
        if (b->sg_ptr != 0)
                BUG_ON(b->sg_cnt != NO_OF_SG_PER_BLOCK);
        for (i = 0; i < b->sg_cnt; i++) {
-               printk("  [%u]: sg_addr 0x%lx, sg_count 0x%lx\n",
-                      i, (ulong)b->sg_list[i].sg_addr,
-                      (ulong)b->sg_list[i].sg_count);
+               printk("  [%u]: sg_addr 0x%x, sg_count 0x%x\n",
+                      i, (u32)le32_to_cpu(b->sg_list[i].sg_addr),
+                      (u32)le32_to_cpu(b->sg_list[i].sg_count));
        }
 }
 
@@ -2673,15 +2578,16 @@ static void asc_prt_adv_sgblock(int sgblockno, ADV_SG_BLOCK *b)
 static void asc_prt_adv_scsi_req_q(ADV_SCSI_REQ_Q *q)
 {
        int sg_blk_cnt;
-       struct asc_sg_block *sg_ptr;
+       struct adv_sg_block *sg_ptr;
+       adv_sgblk_t *sgblkp;
 
        printk("ADV_SCSI_REQ_Q at addr 0x%lx\n", (ulong)q);
 
-       printk("  target_id %u, target_lun %u, srb_ptr 0x%lx, a_flag 0x%x\n",
-              q->target_id, q->target_lun, (ulong)q->srb_ptr, q->a_flag);
+       printk("  target_id %u, target_lun %u, srb_tag 0x%x\n",
+              q->target_id, q->target_lun, q->srb_tag);
 
-       printk("  cntl 0x%x, data_addr 0x%lx, vdata_addr 0x%lx\n",
-              q->cntl, (ulong)le32_to_cpu(q->data_addr), (ulong)q->vdata_addr);
+       printk("  cntl 0x%x, data_addr 0x%lx\n",
+              q->cntl, (ulong)le32_to_cpu(q->data_addr));
 
        printk("  data_cnt %lu, sense_addr 0x%lx, sense_len %u,\n",
               (ulong)le32_to_cpu(q->data_cnt),
@@ -2700,80 +2606,21 @@ static void asc_prt_adv_scsi_req_q(ADV_SCSI_REQ_Q *q)
 
        /* Display the request's ADV_SG_BLOCK structures. */
        if (q->sg_list_ptr != NULL) {
+               sgblkp = container_of(q->sg_list_ptr, adv_sgblk_t, sg_block);
                sg_blk_cnt = 0;
-               while (1) {
-                       /*
-                        * 'sg_ptr' is a physical address. Convert it to a virtual
-                        * address by indexing 'sg_blk_cnt' into the virtual address
-                        * array 'sg_list_ptr'.
-                        *
-                        * XXX - Assumes all SG physical blocks are virtually contiguous.
-                        */
-                       sg_ptr =
-                           &(((ADV_SG_BLOCK *)(q->sg_list_ptr))[sg_blk_cnt]);
+               while (sgblkp) {
+                       sg_ptr = &sgblkp->sg_block;
                        asc_prt_adv_sgblock(sg_blk_cnt, sg_ptr);
                        if (sg_ptr->sg_ptr == 0) {
                                break;
                        }
+                       sgblkp = sgblkp->next_sgblkp;
                        sg_blk_cnt++;
                }
        }
 }
 #endif /* ADVANSYS_DEBUG */
 
-/*
- * The advansys chip/microcode contains a 32-bit identifier for each command
- * known as the 'srb'.  I don't know what it stands for.  The driver used
- * to encode the scsi_cmnd pointer by calling virt_to_bus and retrieve it
- * with bus_to_virt.  Now the driver keeps a per-host map of integers to
- * pointers.  It auto-expands when full, unless it can't allocate memory.
- * Note that an srb of 0 is treated specially by the chip/firmware, hence
- * the return of i+1 in this routine, and the corresponding subtraction in
- * the inverse routine.
- */
-#define BAD_SRB 0
-static u32 advansys_ptr_to_srb(struct asc_dvc_var *asc_dvc, void *ptr)
-{
-       int i;
-       void **new_ptr;
-
-       for (i = 0; i < asc_dvc->ptr_map_count; i++) {
-               if (!asc_dvc->ptr_map[i])
-                       goto out;
-       }
-
-       if (asc_dvc->ptr_map_count == 0)
-               asc_dvc->ptr_map_count = 1;
-       else
-               asc_dvc->ptr_map_count *= 2;
-
-       new_ptr = krealloc(asc_dvc->ptr_map,
-                       asc_dvc->ptr_map_count * sizeof(void *), GFP_ATOMIC);
-       if (!new_ptr)
-               return BAD_SRB;
-       asc_dvc->ptr_map = new_ptr;
- out:
-       ASC_DBG(3, "Putting ptr %p into array offset %d\n", ptr, i);
-       asc_dvc->ptr_map[i] = ptr;
-       return i + 1;
-}
-
-static void * advansys_srb_to_ptr(struct asc_dvc_var *asc_dvc, u32 srb)
-{
-       void *ptr;
-
-       srb--;
-       if (srb >= asc_dvc->ptr_map_count) {
-               printk("advansys: bad SRB %u, max %u\n", srb,
-                                                       asc_dvc->ptr_map_count);
-               return NULL;
-       }
-       ptr = asc_dvc->ptr_map[srb];
-       asc_dvc->ptr_map[srb] = NULL;
-       ASC_DBG(3, "Returning ptr %p from array offset %d\n", ptr, srb);
-       return ptr;
-}
-
 /*
  * advansys_info()
  *
@@ -3350,7 +3197,7 @@ static void asc_prt_driver_conf(struct seq_file *m, struct Scsi_Host *shost)
 
        seq_printf(m,
                   " flags 0x%x, last_reset 0x%lx, jiffies 0x%lx, asc_n_io_port 0x%x\n",
-                  boardp->flags, boardp->last_reset, jiffies,
+                  boardp->flags, shost->last_reset, jiffies,
                   boardp->asc_n_io_port);
 
        seq_printf(m, " io_port 0x%lx\n", shost->io_port);
@@ -3844,7 +3691,7 @@ static int AscStartChip(PortAddr iop_base)
        return (1);
 }
 
-static int AscStopChip(PortAddr iop_base)
+static bool AscStopChip(PortAddr iop_base)
 {
        uchar cc_val;
 
@@ -3855,22 +3702,22 @@ static int AscStopChip(PortAddr iop_base)
        AscSetChipIH(iop_base, INS_HALT);
        AscSetChipIH(iop_base, INS_RFLAG_WTM);
        if ((AscGetChipStatus(iop_base) & CSW_HALTED) == 0) {
-               return (0);
+               return false;
        }
-       return (1);
+       return true;
 }
 
-static int AscIsChipHalted(PortAddr iop_base)
+static bool AscIsChipHalted(PortAddr iop_base)
 {
        if ((AscGetChipStatus(iop_base) & CSW_HALTED) != 0) {
                if ((AscGetChipControl(iop_base) & CC_HALT) != 0) {
-                       return (1);
+                       return true;
                }
        }
-       return (0);
+       return false;
 }
 
-static int AscResetChipAndScsiBus(ASC_DVC_VAR *asc_dvc)
+static bool AscResetChipAndScsiBus(ASC_DVC_VAR *asc_dvc)
 {
        PortAddr iop_base;
        int i = 10;
@@ -3953,20 +3800,6 @@ static ushort AscReadLramWord(PortAddr iop_base, ushort addr)
        return (word_data);
 }
 
-#if CC_VERY_LONG_SG_LIST
-static ASC_DCNT AscReadLramDWord(PortAddr iop_base, ushort addr)
-{
-       ushort val_low, val_high;
-       ASC_DCNT dword_data;
-
-       AscSetChipLramAddr(iop_base, addr);
-       val_low = AscGetChipLramData(iop_base);
-       val_high = AscGetChipLramData(iop_base);
-       dword_data = ((ASC_DCNT) val_high << 16) | (ASC_DCNT) val_low;
-       return (dword_data);
-}
-#endif /* CC_VERY_LONG_SG_LIST */
-
 static void
 AscMemWordSetLram(PortAddr iop_base, ushort s_addr, ushort set_wval, int words)
 {
@@ -4068,27 +3901,24 @@ AscMemWordCopyPtrFromLram(PortAddr iop_base,
        }
 }
 
-static ASC_DCNT AscMemSumLramWord(PortAddr iop_base, ushort s_addr, int words)
+static u32 AscMemSumLramWord(PortAddr iop_base, ushort s_addr, int words)
 {
-       ASC_DCNT sum;
+       u32 sum = 0;
        int i;
 
-       sum = 0L;
        for (i = 0; i < words; i++, s_addr += 2) {
                sum += AscReadLramWord(iop_base, s_addr);
        }
        return (sum);
 }
 
-static ushort AscInitLram(ASC_DVC_VAR *asc_dvc)
+static void AscInitLram(ASC_DVC_VAR *asc_dvc)
 {
        uchar i;
        ushort s_addr;
        PortAddr iop_base;
-       ushort warn_code;
 
        iop_base = asc_dvc->iop_base;
-       warn_code = 0;
        AscMemWordSetLram(iop_base, ASC_QADR_BEG, 0,
                          (ushort)(((int)(asc_dvc->max_total_qng + 2 + 1) *
                                    64) >> 1));
@@ -4127,14 +3957,13 @@ static ushort AscInitLram(ASC_DVC_VAR *asc_dvc)
                AscWriteLramByte(iop_base,
                                 (ushort)(s_addr + (ushort)ASC_SCSIQ_B_QNO), i);
        }
-       return warn_code;
 }
 
-static ASC_DCNT
+static u32
 AscLoadMicroCode(PortAddr iop_base, ushort s_addr,
                 const uchar *mcode_buf, ushort mcode_size)
 {
-       ASC_DCNT chksum;
+       u32 chksum;
        ushort mcode_word_size;
        ushort mcode_chksum;
 
@@ -4186,13 +4015,13 @@ static void AscInitQLinkVar(ASC_DVC_VAR *asc_dvc)
        }
 }
 
-static ushort AscInitMicroCodeVar(ASC_DVC_VAR *asc_dvc)
+static int AscInitMicroCodeVar(ASC_DVC_VAR *asc_dvc)
 {
        int i;
-       ushort warn_code;
+       int warn_code;
        PortAddr iop_base;
-       ASC_PADDR phy_addr;
-       ASC_DCNT phy_size;
+       __le32 phy_addr;
+       __le32 phy_size;
        struct asc_board *board = asc_dvc_to_board(asc_dvc);
 
        iop_base = asc_dvc->iop_base;
@@ -4231,12 +4060,12 @@ static ushort AscInitMicroCodeVar(ASC_DVC_VAR *asc_dvc)
        AscSetPCAddr(iop_base, ASC_MCODE_START_ADDR);
        if (AscGetPCAddr(iop_base) != ASC_MCODE_START_ADDR) {
                asc_dvc->err_code |= ASC_IERR_SET_PC_ADDR;
-               warn_code = UW_ERR;
+               warn_code = -EINVAL;
                goto err_mcode_start;
        }
        if (AscStartChip(iop_base) != 1) {
                asc_dvc->err_code |= ASC_IERR_START_STOP_CHIP;
-               warn_code = UW_ERR;
+               warn_code = -EIO;
                goto err_mcode_start;
        }
 
@@ -4250,13 +4079,13 @@ err_dma_map:
        return warn_code;
 }
 
-static ushort AscInitAsc1000Driver(ASC_DVC_VAR *asc_dvc)
+static int AscInitAsc1000Driver(ASC_DVC_VAR *asc_dvc)
 {
        const struct firmware *fw;
        const char fwname[] = "advansys/mcode.bin";
        int err;
        unsigned long chksum;
-       ushort warn_code;
+       int warn_code;
        PortAddr iop_base;
 
        iop_base = asc_dvc->iop_base;
@@ -4268,15 +4097,13 @@ static ushort AscInitAsc1000Driver(ASC_DVC_VAR *asc_dvc)
        }
        asc_dvc->init_state |= ASC_INIT_STATE_BEG_LOAD_MC;
        if (asc_dvc->err_code != 0)
-               return UW_ERR;
+               return ASC_ERROR;
        if (!AscFindSignature(asc_dvc->iop_base)) {
                asc_dvc->err_code = ASC_IERR_BAD_SIGNATURE;
                return warn_code;
        }
        AscDisableInterrupt(iop_base);
-       warn_code |= AscInitLram(asc_dvc);
-       if (asc_dvc->err_code != 0)
-               return UW_ERR;
+       AscInitLram(asc_dvc);
 
        err = request_firmware(&fw, fwname, asc_dvc->drv_ptr->dev);
        if (err) {
@@ -4336,7 +4163,7 @@ static int AdvLoadMicrocode(AdvPortAddr iop_base, const unsigned char *buf,
                            int size, int memsize, int chksum)
 {
        int i, j, end, len = 0;
-       ADV_DCNT sum;
+       u32 sum;
 
        AdvWriteWordRegister(iop_base, IOPW_RAM_ADDR, 0);
 
@@ -4382,38 +4209,72 @@ static int AdvLoadMicrocode(AdvPortAddr iop_base, const unsigned char *buf,
        return 0;
 }
 
-static void AdvBuildCarrierFreelist(struct adv_dvc_var *asc_dvc)
+static void AdvBuildCarrierFreelist(struct adv_dvc_var *adv_dvc)
 {
-       ADV_CARR_T *carrp;
-       ADV_SDCNT buf_size;
-       ADV_PADDR carr_paddr;
+       off_t carr_offset = 0, next_offset;
+       dma_addr_t carr_paddr;
+       int carr_num = ADV_CARRIER_BUFSIZE / sizeof(ADV_CARR_T), i;
 
-       carrp = (ADV_CARR_T *) ADV_16BALIGN(asc_dvc->carrier_buf);
-       asc_dvc->carr_freelist = NULL;
-       if (carrp == asc_dvc->carrier_buf) {
-               buf_size = ADV_CARRIER_BUFSIZE;
-       } else {
-               buf_size = ADV_CARRIER_BUFSIZE - sizeof(ADV_CARR_T);
+       for (i = 0; i < carr_num; i++) {
+               carr_offset = i * sizeof(ADV_CARR_T);
+               /* Get physical address of the carrier 'carrp'. */
+               carr_paddr = adv_dvc->carrier_addr + carr_offset;
+
+               adv_dvc->carrier[i].carr_pa = cpu_to_le32(carr_paddr);
+               adv_dvc->carrier[i].carr_va = cpu_to_le32(carr_offset);
+               adv_dvc->carrier[i].areq_vpa = 0;
+               next_offset = carr_offset + sizeof(ADV_CARR_T);
+               if (i == carr_num)
+                       next_offset = ~0;
+               adv_dvc->carrier[i].next_vpa = cpu_to_le32(next_offset);
        }
+       /*
+        * We cannot have a carrier with 'carr_va' of '0', as
+        * a reference to this carrier would be interpreted as
+        * list termination.
+        * So start at carrier 1 with the freelist.
+        */
+       adv_dvc->carr_freelist = &adv_dvc->carrier[1];
+}
 
-       do {
-               /* Get physical address of the carrier 'carrp'. */
-               carr_paddr = cpu_to_le32(virt_to_bus(carrp));
+static ADV_CARR_T *adv_get_carrier(struct adv_dvc_var *adv_dvc, u32 offset)
+{
+       int index;
 
-               buf_size -= sizeof(ADV_CARR_T);
+       BUG_ON(offset > ADV_CARRIER_BUFSIZE);
 
-               carrp->carr_pa = carr_paddr;
-               carrp->carr_va = cpu_to_le32(ADV_VADDR_TO_U32(carrp));
+       index = offset / sizeof(ADV_CARR_T);
+       return &adv_dvc->carrier[index];
+}
 
-               /*
-                * Insert the carrier at the beginning of the freelist.
-                */
-               carrp->next_vpa =
-                       cpu_to_le32(ADV_VADDR_TO_U32(asc_dvc->carr_freelist));
-               asc_dvc->carr_freelist = carrp;
+static ADV_CARR_T *adv_get_next_carrier(struct adv_dvc_var *adv_dvc)
+{
+       ADV_CARR_T *carrp = adv_dvc->carr_freelist;
+       u32 next_vpa = le32_to_cpu(carrp->next_vpa);
+
+       if (next_vpa == 0 || next_vpa == ~0) {
+               ASC_DBG(1, "invalid vpa offset 0x%x\n", next_vpa);
+               return NULL;
+       }
+
+       adv_dvc->carr_freelist = adv_get_carrier(adv_dvc, next_vpa);
+       /*
+        * insert stopper carrier to terminate list
+        */
+       carrp->next_vpa = cpu_to_le32(ADV_CQ_STOPPER);
+
+       return carrp;
+}
+
+/*
+ * 'offset' is the index in the request pointer array
+ */
+static adv_req_t * adv_get_reqp(struct adv_dvc_var *adv_dvc, u32 offset)
+{
+       struct asc_board *boardp = adv_dvc->drv_ptr;
 
-               carrp++;
-       } while (buf_size > 0);
+       BUG_ON(offset > adv_dvc->max_host_qng);
+       return &boardp->adv_reqp[offset];
 }
 
 /*
@@ -4432,10 +4293,9 @@ static void AdvBuildCarrierFreelist(struct adv_dvc_var *asc_dvc)
  */
 static int
 AdvSendIdleCmd(ADV_DVC_VAR *asc_dvc,
-              ushort idle_cmd, ADV_DCNT idle_cmd_parameter)
+              ushort idle_cmd, u32 idle_cmd_parameter)
 {
-       int result;
-       ADV_DCNT i, j;
+       int result, i, j;
        AdvPortAddr iop_base;
 
        iop_base = asc_dvc->iop_base;
@@ -4902,17 +4762,11 @@ static int AdvInitAsc3550Driver(ADV_DVC_VAR *asc_dvc)
         * Set-up the Host->RISC Initiator Command Queue (ICQ).
         */
 
-       if ((asc_dvc->icq_sp = asc_dvc->carr_freelist) == NULL) {
+       asc_dvc->icq_sp = adv_get_next_carrier(asc_dvc);
+       if (!asc_dvc->icq_sp) {
                asc_dvc->err_code |= ASC_IERR_NO_CARRIER;
                return ADV_ERROR;
        }
-       asc_dvc->carr_freelist = (ADV_CARR_T *)
-           ADV_U32_TO_VADDR(le32_to_cpu(asc_dvc->icq_sp->next_vpa));
-
-       /*
-        * The first command issued will be placed in the stopper carrier.
-        */
-       asc_dvc->icq_sp->next_vpa = cpu_to_le32(ASC_CQ_STOPPER);
 
        /*
         * Set RISC ICQ physical address start value.
@@ -4922,21 +4776,11 @@ static int AdvInitAsc3550Driver(ADV_DVC_VAR *asc_dvc)
        /*
         * Set-up the RISC->Host Initiator Response Queue (IRQ).
         */
-       if ((asc_dvc->irq_sp = asc_dvc->carr_freelist) == NULL) {
+       asc_dvc->irq_sp = adv_get_next_carrier(asc_dvc);
+       if (!asc_dvc->irq_sp) {
                asc_dvc->err_code |= ASC_IERR_NO_CARRIER;
                return ADV_ERROR;
        }
-       asc_dvc->carr_freelist = (ADV_CARR_T *)
-           ADV_U32_TO_VADDR(le32_to_cpu(asc_dvc->irq_sp->next_vpa));
-
-       /*
-        * The first command completed by the RISC will be placed in
-        * the stopper.
-        *
-        * Note: Set 'next_vpa' to ASC_CQ_STOPPER. When the request is
-        * completed the RISC will set the ASC_RQ_STOPPER bit.
-        */
-       asc_dvc->irq_sp->next_vpa = cpu_to_le32(ASC_CQ_STOPPER);
 
        /*
         * Set RISC IRQ physical address start value.
@@ -5399,17 +5243,12 @@ static int AdvInitAsc38C0800Driver(ADV_DVC_VAR *asc_dvc)
         * Set-up the Host->RISC Initiator Command Queue (ICQ).
         */
 
-       if ((asc_dvc->icq_sp = asc_dvc->carr_freelist) == NULL) {
+       asc_dvc->icq_sp = adv_get_next_carrier(asc_dvc);
+       if (!asc_dvc->icq_sp) {
+               ASC_DBG(0, "Failed to get ICQ carrier\n");
                asc_dvc->err_code |= ASC_IERR_NO_CARRIER;
                return ADV_ERROR;
        }
-       asc_dvc->carr_freelist = (ADV_CARR_T *)
-           ADV_U32_TO_VADDR(le32_to_cpu(asc_dvc->icq_sp->next_vpa));
-
-       /*
-        * The first command issued will be placed in the stopper carrier.
-        */
-       asc_dvc->icq_sp->next_vpa = cpu_to_le32(ASC_CQ_STOPPER);
 
        /*
         * Set RISC ICQ physical address start value.
@@ -5420,21 +5259,12 @@ static int AdvInitAsc38C0800Driver(ADV_DVC_VAR *asc_dvc)
        /*
         * Set-up the RISC->Host Initiator Response Queue (IRQ).
         */
-       if ((asc_dvc->irq_sp = asc_dvc->carr_freelist) == NULL) {
+       asc_dvc->irq_sp = adv_get_next_carrier(asc_dvc);
+       if (!asc_dvc->irq_sp) {
+               ASC_DBG(0, "Failed to get IRQ carrier\n");
                asc_dvc->err_code |= ASC_IERR_NO_CARRIER;
                return ADV_ERROR;
        }
-       asc_dvc->carr_freelist = (ADV_CARR_T *)
-           ADV_U32_TO_VADDR(le32_to_cpu(asc_dvc->irq_sp->next_vpa));
-
-       /*
-        * The first command completed by the RISC will be placed in
-        * the stopper.
-        *
-        * Note: Set 'next_vpa' to ASC_CQ_STOPPER. When the request is
-        * completed the RISC will set the ASC_RQ_STOPPER bit.
-        */
-       asc_dvc->irq_sp->next_vpa = cpu_to_le32(ASC_CQ_STOPPER);
 
        /*
         * Set RISC IRQ physical address start value.
@@ -5909,17 +5739,11 @@ static int AdvInitAsc38C1600Driver(ADV_DVC_VAR *asc_dvc)
        /*
         * Set-up the Host->RISC Initiator Command Queue (ICQ).
         */
-       if ((asc_dvc->icq_sp = asc_dvc->carr_freelist) == NULL) {
+       asc_dvc->icq_sp = adv_get_next_carrier(asc_dvc);
+       if (!asc_dvc->icq_sp) {
                asc_dvc->err_code |= ASC_IERR_NO_CARRIER;
                return ADV_ERROR;
        }
-       asc_dvc->carr_freelist = (ADV_CARR_T *)
-           ADV_U32_TO_VADDR(le32_to_cpu(asc_dvc->icq_sp->next_vpa));
-
-       /*
-        * The first command issued will be placed in the stopper carrier.
-        */
-       asc_dvc->icq_sp->next_vpa = cpu_to_le32(ASC_CQ_STOPPER);
 
        /*
         * Set RISC ICQ physical address start value. Initialize the
@@ -5933,21 +5757,11 @@ static int AdvInitAsc38C1600Driver(ADV_DVC_VAR *asc_dvc)
        /*
         * Set-up the RISC->Host Initiator Response Queue (IRQ).
         */
-       if ((asc_dvc->irq_sp = asc_dvc->carr_freelist) == NULL) {
+       asc_dvc->irq_sp = adv_get_next_carrier(asc_dvc);
+       if (!asc_dvc->irq_sp) {
                asc_dvc->err_code |= ASC_IERR_NO_CARRIER;
                return ADV_ERROR;
        }
-       asc_dvc->carr_freelist = (ADV_CARR_T *)
-           ADV_U32_TO_VADDR(le32_to_cpu(asc_dvc->irq_sp->next_vpa));
-
-       /*
-        * The first command completed by the RISC will be placed in
-        * the stopper.
-        *
-        * Note: Set 'next_vpa' to ASC_CQ_STOPPER. When the request is
-        * completed the RISC will set the ASC_RQ_STOPPER bit.
-        */
-       asc_dvc->irq_sp->next_vpa = cpu_to_le32(ASC_CQ_STOPPER);
 
        /*
         * Set RISC IRQ physical address start value.
@@ -6134,15 +5948,16 @@ static void adv_async_callback(ADV_DVC_VAR *adv_dvc_varp, uchar code)
  */
 static void adv_isr_callback(ADV_DVC_VAR *adv_dvc_varp, ADV_SCSI_REQ_Q *scsiqp)
 {
-       struct asc_board *boardp;
+       struct asc_board *boardp = adv_dvc_varp->drv_ptr;
+       u32 srb_tag;
        adv_req_t *reqp;
        adv_sgblk_t *sgblkp;
        struct scsi_cmnd *scp;
-       struct Scsi_Host *shost;
-       ADV_DCNT resid_cnt;
+       u32 resid_cnt;
+       dma_addr_t sense_addr;
 
-       ASC_DBG(1, "adv_dvc_varp 0x%lx, scsiqp 0x%lx\n",
-                (ulong)adv_dvc_varp, (ulong)scsiqp);
+       ASC_DBG(1, "adv_dvc_varp 0x%p, scsiqp 0x%p\n",
+               adv_dvc_varp, scsiqp);
        ASC_DBG_PRT_ADV_SCSI_REQ_Q(2, scsiqp);
 
        /*
@@ -6150,22 +5965,9 @@ static void adv_isr_callback(ADV_DVC_VAR *adv_dvc_varp, ADV_SCSI_REQ_Q *scsiqp)
         * completed. The adv_req_t structure actually contains the
         * completed ADV_SCSI_REQ_Q structure.
         */
-       reqp = (adv_req_t *)ADV_U32_TO_VADDR(scsiqp->srb_ptr);
-       ASC_DBG(1, "reqp 0x%lx\n", (ulong)reqp);
-       if (reqp == NULL) {
-               ASC_PRINT("adv_isr_callback: reqp is NULL\n");
-               return;
-       }
+       srb_tag = le32_to_cpu(scsiqp->srb_tag);
+       scp = scsi_host_find_tag(boardp->shost, scsiqp->srb_tag);
 
-       /*
-        * Get the struct scsi_cmnd structure and Scsi_Host structure for the
-        * command that has been completed.
-        *
-        * Note: The adv_req_t request structure and adv_sgblk_t structure,
-        * if any, are dropped, because a board structure pointer can not be
-        * determined.
-        */
-       scp = reqp->cmndp;
        ASC_DBG(1, "scp 0x%p\n", scp);
        if (scp == NULL) {
                ASC_PRINT
@@ -6174,12 +5976,25 @@ static void adv_isr_callback(ADV_DVC_VAR *adv_dvc_varp, ADV_SCSI_REQ_Q *scsiqp)
        }
        ASC_DBG_PRT_CDB(2, scp->cmnd, scp->cmd_len);
 
-       shost = scp->device->host;
-       ASC_STATS(shost, callback);
-       ASC_DBG(1, "shost 0x%p\n", shost);
+       reqp = (adv_req_t *)scp->host_scribble;
+       ASC_DBG(1, "reqp 0x%lx\n", (ulong)reqp);
+       if (reqp == NULL) {
+               ASC_PRINT("adv_isr_callback: reqp is NULL\n");
+               return;
+       }
+       /*
+        * Remove backreferences to avoid duplicate
+        * command completions.
+        */
+       scp->host_scribble = NULL;
+       reqp->cmndp = NULL;
+
+       ASC_STATS(boardp->shost, callback);
+       ASC_DBG(1, "shost 0x%p\n", boardp->shost);
 
-       boardp = shost_priv(shost);
-       BUG_ON(adv_dvc_varp != &boardp->dvc_var.adv_dvc_var);
+       sense_addr = le32_to_cpu(scsiqp->sense_addr);
+       dma_unmap_single(boardp->dev, sense_addr,
+                        SCSI_SENSE_BUFFERSIZE, DMA_FROM_DEVICE);
 
        /*
         * 'done_status' contains the command's ending status.
@@ -6272,18 +6087,10 @@ static void adv_isr_callback(ADV_DVC_VAR *adv_dvc_varp, ADV_SCSI_REQ_Q *scsiqp)
                /* Remove 'sgblkp' from the request list. */
                reqp->sgblkp = sgblkp->next_sgblkp;
 
-               /* Add 'sgblkp' to the board free list. */
-               sgblkp->next_sgblkp = boardp->adv_sgblkp;
-               boardp->adv_sgblkp = sgblkp;
+               dma_pool_free(boardp->adv_sgblk_pool, sgblkp,
+                             sgblkp->sg_addr);
        }
 
-       /*
-        * Free the adv_req_t structure used with the command by adding
-        * it back to the board free list.
-        */
-       reqp->next_reqp = boardp->adv_reqp;
-       boardp->adv_reqp = reqp;
-
        ASC_DBG(1, "done\n");
 }
 
@@ -6312,8 +6119,9 @@ static int AdvISR(ADV_DVC_VAR *asc_dvc)
        uchar int_stat;
        ushort target_bit;
        ADV_CARR_T *free_carrp;
-       ADV_VADDR irq_next_vpa;
+       __le32 irq_next_vpa;
        ADV_SCSI_REQ_Q *scsiq;
+       adv_req_t *reqp;
 
        iop_base = asc_dvc->iop_base;
 
@@ -6356,25 +6164,28 @@ static int AdvISR(ADV_DVC_VAR *asc_dvc)
         * Check if the IRQ stopper carrier contains a completed request.
         */
        while (((irq_next_vpa =
-                le32_to_cpu(asc_dvc->irq_sp->next_vpa)) & ASC_RQ_DONE) != 0) {
+                le32_to_cpu(asc_dvc->irq_sp->next_vpa)) & ADV_RQ_DONE) != 0) {
                /*
                 * Get a pointer to the newly completed ADV_SCSI_REQ_Q structure.
                 * The RISC will have set 'areq_vpa' to a virtual address.
                 *
-                * The firmware will have copied the ASC_SCSI_REQ_Q.scsiq_ptr
+                * The firmware will have copied the ADV_SCSI_REQ_Q.scsiq_ptr
                 * field to the carrier ADV_CARR_T.areq_vpa field. The conversion
-                * below complements the conversion of ASC_SCSI_REQ_Q.scsiq_ptr'
+                * below complements the conversion of ADV_SCSI_REQ_Q.scsiq_ptr'
                 * in AdvExeScsiQueue().
                 */
-               scsiq = (ADV_SCSI_REQ_Q *)
-                   ADV_U32_TO_VADDR(le32_to_cpu(asc_dvc->irq_sp->areq_vpa));
+               u32 pa_offset = le32_to_cpu(asc_dvc->irq_sp->areq_vpa);
+               ASC_DBG(1, "irq_sp %p areq_vpa %u\n",
+                       asc_dvc->irq_sp, pa_offset);
+               reqp = adv_get_reqp(asc_dvc, pa_offset);
+               scsiq = &reqp->scsi_req_q;
 
                /*
                 * Request finished with good status and the queue was not
                 * DMAed to host memory by the firmware. Set all status fields
                 * to indicate good status.
                 */
-               if ((irq_next_vpa & ASC_RQ_GOOD) != 0) {
+               if ((irq_next_vpa & ADV_RQ_GOOD) != 0) {
                        scsiq->done_status = QD_NO_ERROR;
                        scsiq->host_status = scsiq->scsi_status = 0;
                        scsiq->data_cnt = 0L;
@@ -6386,11 +6197,10 @@ static int AdvISR(ADV_DVC_VAR *asc_dvc)
                 * stopper carrier.
                 */
                free_carrp = asc_dvc->irq_sp;
-               asc_dvc->irq_sp = (ADV_CARR_T *)
-                   ADV_U32_TO_VADDR(ASC_GET_CARRP(irq_next_vpa));
+               asc_dvc->irq_sp = adv_get_carrier(asc_dvc,
+                                                 ADV_GET_CARRP(irq_next_vpa));
 
-               free_carrp->next_vpa =
-                   cpu_to_le32(ADV_VADDR_TO_U32(asc_dvc->carr_freelist));
+               free_carrp->next_vpa = asc_dvc->carr_freelist->carr_va;
                asc_dvc->carr_freelist = free_carrp;
                asc_dvc->carr_pending_cnt--;
 
@@ -6405,7 +6215,6 @@ static int AdvISR(ADV_DVC_VAR *asc_dvc)
                 * Notify the driver of the completed request by passing
                 * the ADV_SCSI_REQ_Q pointer to its callback function.
                 */
-               scsiq->a_flag |= ADV_SCSIQ_DONE;
                adv_isr_callback(asc_dvc, scsiq);
                /*
                 * Note: After the driver callback function is called, 'scsiq'
@@ -6521,11 +6330,11 @@ AscCalSDTRData(ASC_DVC_VAR *asc_dvc, uchar sdtr_period, uchar syn_offset)
        return byte;
 }
 
-static int AscSetChipSynRegAtID(PortAddr iop_base, uchar id, uchar sdtr_data)
+static bool AscSetChipSynRegAtID(PortAddr iop_base, uchar id, uchar sdtr_data)
 {
        ASC_SCSI_BIT_ID_TYPE org_id;
        int i;
-       int sta = TRUE;
+       bool sta = true;
 
        AscSetBank(iop_base, 1);
        org_id = AscReadChipDvcID(iop_base);
@@ -6539,10 +6348,10 @@ static int AscSetChipSynRegAtID(PortAddr iop_base, uchar id, uchar sdtr_data)
                AscSetBank(iop_base, 0);
                AscSetChipSyn(iop_base, sdtr_data);
                if (AscGetChipSyn(iop_base) != sdtr_data) {
-                       sta = FALSE;
+                       sta = false;
                }
        } else {
-               sta = FALSE;
+               sta = false;
        }
        AscSetBank(iop_base, 1);
        AscWriteChipDvcID(iop_base, org_id);
@@ -6556,12 +6365,12 @@ static void AscSetChipSDTR(PortAddr iop_base, uchar sdtr_data, uchar tid_no)
        AscPutMCodeSDTRDoneAtID(iop_base, tid_no, sdtr_data);
 }
 
-static int AscIsrChipHalted(ASC_DVC_VAR *asc_dvc)
+static void AscIsrChipHalted(ASC_DVC_VAR *asc_dvc)
 {
        EXT_MSG ext_msg;
        EXT_MSG out_msg;
        ushort halt_q_addr;
-       int sdtr_accept;
+       bool sdtr_accept;
        ushort int_halt_code;
        ASC_SCSI_BIT_ID_TYPE scsi_busy;
        ASC_SCSI_BIT_ID_TYPE target_id;
@@ -6603,14 +6412,14 @@ static int AscIsrChipHalted(ASC_DVC_VAR *asc_dvc)
                        boardp->sdtr_data[tid_no] = 0;
                }
                AscWriteLramWord(iop_base, ASCV_HALTCODE_W, 0);
-               return (0);
+               return;
        } else if (int_halt_code == ASC_HALT_ENABLE_ASYN_USE_SYN_FIX) {
                if (asc_dvc->pci_fix_asyn_xfer & target_id) {
                        AscSetChipSDTR(iop_base, asyn_sdtr, tid_no);
                        boardp->sdtr_data[tid_no] = asyn_sdtr;
                }
                AscWriteLramWord(iop_base, ASCV_HALTCODE_W, 0);
-               return (0);
+               return;
        } else if (int_halt_code == ASC_HALT_EXTMSG_IN) {
                AscMemWordCopyPtrFromLram(iop_base,
                                          ASCV_MSGIN_BEG,
@@ -6620,10 +6429,10 @@ static int AscIsrChipHalted(ASC_DVC_VAR *asc_dvc)
                if (ext_msg.msg_type == EXTENDED_MESSAGE &&
                    ext_msg.msg_req == EXTENDED_SDTR &&
                    ext_msg.msg_len == MS_SDTR_LEN) {
-                       sdtr_accept = TRUE;
+                       sdtr_accept = true;
                        if ((ext_msg.req_ack_offset > ASC_SYN_MAX_OFFSET)) {
 
-                               sdtr_accept = FALSE;
+                               sdtr_accept = false;
                                ext_msg.req_ack_offset = ASC_SYN_MAX_OFFSET;
                        }
                        if ((ext_msg.xfer_period <
@@ -6631,7 +6440,7 @@ static int AscIsrChipHalted(ASC_DVC_VAR *asc_dvc)
                            || (ext_msg.xfer_period >
                                asc_dvc->sdtr_period_tbl[asc_dvc->
                                                         max_sdtr_index])) {
-                               sdtr_accept = FALSE;
+                               sdtr_accept = false;
                                ext_msg.xfer_period =
                                    asc_dvc->sdtr_period_tbl[asc_dvc->
                                                             min_sdtr_index];
@@ -6696,7 +6505,7 @@ static int AscIsrChipHalted(ASC_DVC_VAR *asc_dvc)
                                                  (ushort)ASC_SCSIQ_B_CNTL),
                                         q_cntl);
                        AscWriteLramWord(iop_base, ASCV_HALTCODE_W, 0);
-                       return (0);
+                       return;
                } else if (ext_msg.msg_type == EXTENDED_MESSAGE &&
                           ext_msg.msg_req == EXTENDED_WDTR &&
                           ext_msg.msg_len == MS_WDTR_LEN) {
@@ -6712,7 +6521,7 @@ static int AscIsrChipHalted(ASC_DVC_VAR *asc_dvc)
                                                  (ushort)ASC_SCSIQ_B_CNTL),
                                         q_cntl);
                        AscWriteLramWord(iop_base, ASCV_HALTCODE_W, 0);
-                       return (0);
+                       return;
                } else {
 
                        ext_msg.msg_type = MESSAGE_REJECT;
@@ -6726,7 +6535,7 @@ static int AscIsrChipHalted(ASC_DVC_VAR *asc_dvc)
                                                  (ushort)ASC_SCSIQ_B_CNTL),
                                         q_cntl);
                        AscWriteLramWord(iop_base, ASCV_HALTCODE_W, 0);
-                       return (0);
+                       return;
                }
        } else if (int_halt_code == ASC_HALT_CHK_CONDITION) {
 
@@ -6783,7 +6592,7 @@ static int AscIsrChipHalted(ASC_DVC_VAR *asc_dvc)
                AscWriteLramByte(iop_base, (ushort)ASCV_SCSIBUSY_B, scsi_busy);
 
                AscWriteLramWord(iop_base, ASCV_HALTCODE_W, 0);
-               return (0);
+               return;
        } else if (int_halt_code == ASC_HALT_SDTR_REJECTED) {
 
                AscMemWordCopyPtrFromLram(iop_base,
@@ -6805,7 +6614,7 @@ static int AscIsrChipHalted(ASC_DVC_VAR *asc_dvc)
                                 (ushort)(halt_q_addr +
                                          (ushort)ASC_SCSIQ_B_CNTL), q_cntl);
                AscWriteLramWord(iop_base, ASCV_HALTCODE_W, 0);
-               return (0);
+               return;
        } else if (int_halt_code == ASC_HALT_SS_QUEUE_FULL) {
 
                scsi_status = AscReadLramByte(iop_base,
@@ -6850,166 +6659,9 @@ static int AscIsrChipHalted(ASC_DVC_VAR *asc_dvc)
                        }
                }
                AscWriteLramWord(iop_base, ASCV_HALTCODE_W, 0);
-               return (0);
-       }
-#if CC_VERY_LONG_SG_LIST
-       else if (int_halt_code == ASC_HALT_HOST_COPY_SG_LIST_TO_RISC) {
-               uchar q_no;
-               ushort q_addr;
-               uchar sg_wk_q_no;
-               uchar first_sg_wk_q_no;
-               ASC_SCSI_Q *scsiq;      /* Ptr to driver request. */
-               ASC_SG_HEAD *sg_head;   /* Ptr to driver SG request. */
-               ASC_SG_LIST_Q scsi_sg_q;        /* Structure written to queue. */
-               ushort sg_list_dwords;
-               ushort sg_entry_cnt;
-               uchar next_qp;
-               int i;
-
-               q_no = AscReadLramByte(iop_base, (ushort)ASCV_REQ_SG_LIST_QP);
-               if (q_no == ASC_QLINK_END)
-                       return 0;
-
-               q_addr = ASC_QNO_TO_QADDR(q_no);
-
-               /*
-                * Convert the request's SRB pointer to a host ASC_SCSI_REQ
-                * structure pointer using a macro provided by the driver.
-                * The ASC_SCSI_REQ pointer provides a pointer to the
-                * host ASC_SG_HEAD structure.
-                */
-               /* Read request's SRB pointer. */
-               scsiq = (ASC_SCSI_Q *)
-                   ASC_SRB2SCSIQ(ASC_U32_TO_VADDR(AscReadLramDWord(iop_base,
-                                                                   (ushort)
-                                                                   (q_addr +
-                                                                    ASC_SCSIQ_D_SRBPTR))));
-
-               /*
-                * Get request's first and working SG queue.
-                */
-               sg_wk_q_no = AscReadLramByte(iop_base,
-                                            (ushort)(q_addr +
-                                                     ASC_SCSIQ_B_SG_WK_QP));
-
-               first_sg_wk_q_no = AscReadLramByte(iop_base,
-                                                  (ushort)(q_addr +
-                                                           ASC_SCSIQ_B_FIRST_SG_WK_QP));
-
-               /*
-                * Reset request's working SG queue back to the
-                * first SG queue.
-                */
-               AscWriteLramByte(iop_base,
-                                (ushort)(q_addr +
-                                         (ushort)ASC_SCSIQ_B_SG_WK_QP),
-                                first_sg_wk_q_no);
-
-               sg_head = scsiq->sg_head;
-
-               /*
-                * Set sg_entry_cnt to the number of SG elements
-                * that will be completed on this interrupt.
-                *
-                * Note: The allocated SG queues contain ASC_MAX_SG_LIST - 1
-                * SG elements. The data_cnt and data_addr fields which
-                * add 1 to the SG element capacity are not used when
-                * restarting SG handling after a halt.
-                */
-               if (scsiq->remain_sg_entry_cnt > (ASC_MAX_SG_LIST - 1)) {
-                       sg_entry_cnt = ASC_MAX_SG_LIST - 1;
-
-                       /*
-                        * Keep track of remaining number of SG elements that
-                        * will need to be handled on the next interrupt.
-                        */
-                       scsiq->remain_sg_entry_cnt -= (ASC_MAX_SG_LIST - 1);
-               } else {
-                       sg_entry_cnt = scsiq->remain_sg_entry_cnt;
-                       scsiq->remain_sg_entry_cnt = 0;
-               }
-
-               /*
-                * Copy SG elements into the list of allocated SG queues.
-                *
-                * Last index completed is saved in scsiq->next_sg_index.
-                */
-               next_qp = first_sg_wk_q_no;
-               q_addr = ASC_QNO_TO_QADDR(next_qp);
-               scsi_sg_q.sg_head_qp = q_no;
-               scsi_sg_q.cntl = QCSG_SG_XFER_LIST;
-               for (i = 0; i < sg_head->queue_cnt; i++) {
-                       scsi_sg_q.seq_no = i + 1;
-                       if (sg_entry_cnt > ASC_SG_LIST_PER_Q) {
-                               sg_list_dwords = (uchar)(ASC_SG_LIST_PER_Q * 2);
-                               sg_entry_cnt -= ASC_SG_LIST_PER_Q;
-                               /*
-                                * After very first SG queue RISC FW uses next
-                                * SG queue first element then checks sg_list_cnt
-                                * against zero and then decrements, so set
-                                * sg_list_cnt 1 less than number of SG elements
-                                * in each SG queue.
-                                */
-                               scsi_sg_q.sg_list_cnt = ASC_SG_LIST_PER_Q - 1;
-                               scsi_sg_q.sg_cur_list_cnt =
-                                   ASC_SG_LIST_PER_Q - 1;
-                       } else {
-                               /*
-                                * This is the last SG queue in the list of
-                                * allocated SG queues. If there are more
-                                * SG elements than will fit in the allocated
-                                * queues, then set the QCSG_SG_XFER_MORE flag.
-                                */
-                               if (scsiq->remain_sg_entry_cnt != 0) {
-                                       scsi_sg_q.cntl |= QCSG_SG_XFER_MORE;
-                               } else {
-                                       scsi_sg_q.cntl |= QCSG_SG_XFER_END;
-                               }
-                               /* equals sg_entry_cnt * 2 */
-                               sg_list_dwords = sg_entry_cnt << 1;
-                               scsi_sg_q.sg_list_cnt = sg_entry_cnt - 1;
-                               scsi_sg_q.sg_cur_list_cnt = sg_entry_cnt - 1;
-                               sg_entry_cnt = 0;
-                       }
-
-                       scsi_sg_q.q_no = next_qp;
-                       AscMemWordCopyPtrToLram(iop_base,
-                                               q_addr + ASC_SCSIQ_SGHD_CPY_BEG,
-                                               (uchar *)&scsi_sg_q,
-                                               sizeof(ASC_SG_LIST_Q) >> 1);
-
-                       AscMemDWordCopyPtrToLram(iop_base,
-                                                q_addr + ASC_SGQ_LIST_BEG,
-                                                (uchar *)&sg_head->
-                                                sg_list[scsiq->next_sg_index],
-                                                sg_list_dwords);
-
-                       scsiq->next_sg_index += ASC_SG_LIST_PER_Q;
-
-                       /*
-                        * If the just completed SG queue contained the
-                        * last SG element, then no more SG queues need
-                        * to be written.
-                        */
-                       if (scsi_sg_q.cntl & QCSG_SG_XFER_END) {
-                               break;
-                       }
-
-                       next_qp = AscReadLramByte(iop_base,
-                                                 (ushort)(q_addr +
-                                                          ASC_SCSIQ_B_FWD));
-                       q_addr = ASC_QNO_TO_QADDR(next_qp);
-               }
-
-               /*
-                * Clear the halt condition so the RISC will be restarted
-                * after the return.
-                */
-               AscWriteLramWord(iop_base, ASCV_HALTCODE_W, 0);
-               return (0);
+               return;
        }
-#endif /* CC_VERY_LONG_SG_LIST */
-       return (0);
+       return;
 }
 
 /*
@@ -7043,7 +6695,7 @@ DvcGetQinfo(PortAddr iop_base, ushort s_addr, uchar *inbuf, int words)
 static uchar
 _AscCopyLramScsiDoneQ(PortAddr iop_base,
                      ushort q_addr,
-                     ASC_QDONE_INFO *scsiq, ASC_DCNT max_dma_count)
+                     ASC_QDONE_INFO *scsiq, unsigned int max_dma_count)
 {
        ushort _val;
        uchar sg_queue_cnt;
@@ -7070,10 +6722,10 @@ _AscCopyLramScsiDoneQ(PortAddr iop_base,
        /*
         * Read high word of remain bytes from alternate location.
         */
-       scsiq->remain_bytes = (((ADV_DCNT)AscReadLramWord(iop_base,
-                                                         (ushort)(q_addr +
-                                                                  (ushort)
-                                                                  ASC_SCSIQ_W_ALT_DC1)))
+       scsiq->remain_bytes = (((u32)AscReadLramWord(iop_base,
+                                                    (ushort)(q_addr +
+                                                             (ushort)
+                                                             ASC_SCSIQ_W_ALT_DC1)))
                               << 16);
        /*
         * Read low word of remain bytes from original location.
@@ -7093,25 +6745,24 @@ _AscCopyLramScsiDoneQ(PortAddr iop_base,
  */
 static void asc_isr_callback(ASC_DVC_VAR *asc_dvc_varp, ASC_QDONE_INFO *qdonep)
 {
-       struct asc_board *boardp;
+       struct asc_board *boardp = asc_dvc_varp->drv_ptr;
+       u32 srb_tag;
        struct scsi_cmnd *scp;
-       struct Scsi_Host *shost;
 
        ASC_DBG(1, "asc_dvc_varp 0x%p, qdonep 0x%p\n", asc_dvc_varp, qdonep);
        ASC_DBG_PRT_ASC_QDONE_INFO(2, qdonep);
 
-       scp = advansys_srb_to_ptr(asc_dvc_varp, qdonep->d2.srb_ptr);
+       /*
+        * Decrease the srb_tag by 1 to find the SCSI command
+        */
+       srb_tag = qdonep->d2.srb_tag - 1;
+       scp = scsi_host_find_tag(boardp->shost, srb_tag);
        if (!scp)
                return;
 
        ASC_DBG_PRT_CDB(2, scp->cmnd, scp->cmd_len);
 
-       shost = scp->device->host;
-       ASC_STATS(shost, callback);
-       ASC_DBG(1, "shost 0x%p\n", shost);
-
-       boardp = shost_priv(shost);
-       BUG_ON(asc_dvc_varp != &boardp->dvc_var.asc_dvc_var);
+       ASC_STATS(boardp->shost, callback);
 
        dma_unmap_single(boardp->dev, scp->SCp.dma_handle,
                         SCSI_SENSE_BUFFERSIZE, DMA_FROM_DEVICE);
@@ -7220,7 +6871,7 @@ static int AscIsrQDone(ASC_DVC_VAR *asc_dvc)
        uchar cur_target_qng;
        ASC_QDONE_INFO scsiq_buf;
        ASC_QDONE_INFO *scsiq;
-       int false_overrun;
+       bool false_overrun;
 
        iop_base = asc_dvc->iop_base;
        n_q_used = 1;
@@ -7294,14 +6945,17 @@ static int AscIsrQDone(ASC_DVC_VAR *asc_dvc)
                        scsiq->d3.done_stat = QD_WITH_ERROR;
                        goto FATAL_ERR_QDONE;
                }
-               if ((scsiq->d2.srb_ptr == 0UL) ||
+               if ((scsiq->d2.srb_tag == 0UL) ||
                    ((scsiq->q_status & QS_ABORTED) != 0)) {
                        return (0x11);
                } else if (scsiq->q_status == QS_DONE) {
-                       false_overrun = FALSE;
+                       /*
+                        * This is also curious.
+                        * false_overrun will _always_ be set to 'false'
+                        */
+                       false_overrun = false;
                        if (scsiq->extra_bytes != 0) {
-                               scsiq->remain_bytes +=
-                                   (ADV_DCNT)scsiq->extra_bytes;
+                               scsiq->remain_bytes += scsiq->extra_bytes;
                        }
                        if (scsiq->d3.done_stat == QD_WITH_ERROR) {
                                if (scsiq->d3.host_stat ==
@@ -7372,23 +7026,23 @@ static int AscISR(ASC_DVC_VAR *asc_dvc)
        uchar host_flag;
 
        iop_base = asc_dvc->iop_base;
-       int_pending = FALSE;
+       int_pending = ASC_FALSE;
 
        if (AscIsIntPending(iop_base) == 0)
                return int_pending;
 
        if ((asc_dvc->init_state & ASC_INIT_STATE_END_LOAD_MC) == 0) {
-               return ERR;
+               return ASC_ERROR;
        }
        if (asc_dvc->in_critical_cnt != 0) {
                AscSetLibErrorCode(asc_dvc, ASCQ_ERR_ISR_ON_CRITICAL);
-               return ERR;
+               return ASC_ERROR;
        }
        if (asc_dvc->is_in_int) {
                AscSetLibErrorCode(asc_dvc, ASCQ_ERR_ISR_RE_ENTRY);
-               return ERR;
+               return ASC_ERROR;
        }
-       asc_dvc->is_in_int = TRUE;
+       asc_dvc->is_in_int = true;
        ctrl_reg = AscGetChipControl(iop_base);
        saved_ctrl_reg = ctrl_reg & (~(CC_SCSI_RESET | CC_CHIP_RESET |
                                       CC_SINGLE_STEP | CC_DIAG | CC_TEST));
@@ -7396,7 +7050,7 @@ static int AscISR(ASC_DVC_VAR *asc_dvc)
        if (chipstat & CSW_SCSI_RESET_LATCH) {
                if (!(asc_dvc->bus_type & (ASC_IS_VL | ASC_IS_EISA))) {
                        int i = 10;
-                       int_pending = TRUE;
+                       int_pending = ASC_TRUE;
                        asc_dvc->sdtr_done = 0;
                        saved_ctrl_reg &= (uchar)(~CC_HALT);
                        while ((AscGetChipStatus(iop_base) &
@@ -7418,15 +7072,11 @@ static int AscISR(ASC_DVC_VAR *asc_dvc)
                         (uchar)(host_flag | (uchar)ASC_HOST_FLAG_IN_ISR));
        if ((chipstat & CSW_INT_PENDING) || (int_pending)) {
                AscAckInterrupt(iop_base);
-               int_pending = TRUE;
+               int_pending = ASC_TRUE;
                if ((chipstat & CSW_HALTED) && (ctrl_reg & CC_SINGLE_STEP)) {
-                       if (AscIsrChipHalted(asc_dvc) == ERR) {
-                               goto ISR_REPORT_QDONE_FATAL_ERROR;
-                       } else {
-                               saved_ctrl_reg &= (uchar)(~CC_HALT);
-                       }
+                       AscIsrChipHalted(asc_dvc);
+                       saved_ctrl_reg &= (uchar)(~CC_HALT);
                } else {
- ISR_REPORT_QDONE_FATAL_ERROR:
                        if ((asc_dvc->dvc_cntl & ASC_CNTL_INT_MULTI_Q) != 0) {
                                while (((status =
                                         AscIsrQDone(asc_dvc)) & 0x01) != 0) {
@@ -7440,20 +7090,20 @@ static int AscISR(ASC_DVC_VAR *asc_dvc)
                                } while (status == 0x11);
                        }
                        if ((status & 0x80) != 0)
-                               int_pending = ERR;
+                               int_pending = ASC_ERROR;
                }
        }
        AscWriteLramByte(iop_base, ASCV_HOST_FLAG_B, host_flag);
        AscSetChipLramAddr(iop_base, saved_ram_addr);
        AscSetChipControl(iop_base, saved_ctrl_reg);
-       asc_dvc->is_in_int = FALSE;
+       asc_dvc->is_in_int = false;
        return int_pending;
 }
 
 /*
  * advansys_reset()
  *
- * Reset the bus associated with the command 'scp'.
+ * Reset the host associated with the command 'scp'.
  *
  * This function runs its own thread. Interrupts must be blocked but
  * sleeping is allowed and no locking other than for host structures is
@@ -7471,7 +7121,7 @@ static int advansys_reset(struct scsi_cmnd *scp)
 
        ASC_STATS(shost, reset);
 
-       scmd_printk(KERN_INFO, scp, "SCSI bus reset started...\n");
+       scmd_printk(KERN_INFO, scp, "SCSI host reset started...\n");
 
        if (ASC_NARROW_BOARD(boardp)) {
                ASC_DVC_VAR *asc_dvc = &boardp->dvc_var.asc_dvc_var;
@@ -7482,20 +7132,19 @@ static int advansys_reset(struct scsi_cmnd *scp)
 
                /* Refer to ASC_IERR_* definitions for meaning of 'err_code'. */
                if (asc_dvc->err_code || !asc_dvc->overrun_dma) {
-                       scmd_printk(KERN_INFO, scp, "SCSI bus reset error: "
+                       scmd_printk(KERN_INFO, scp, "SCSI host reset error: "
                                    "0x%x, status: 0x%x\n", asc_dvc->err_code,
                                    status);
                        ret = FAILED;
                } else if (status) {
-                       scmd_printk(KERN_INFO, scp, "SCSI bus reset warning: "
+                       scmd_printk(KERN_INFO, scp, "SCSI host reset warning: "
                                    "0x%x\n", status);
                } else {
-                       scmd_printk(KERN_INFO, scp, "SCSI bus reset "
+                       scmd_printk(KERN_INFO, scp, "SCSI host reset "
                                    "successful\n");
                }
 
                ASC_DBG(1, "after AscInitAsc1000Driver()\n");
-               spin_lock_irqsave(shost->host_lock, flags);
        } else {
                /*
                 * If the suggest reset bus flags are set, then reset the bus.
@@ -7504,28 +7153,25 @@ static int advansys_reset(struct scsi_cmnd *scp)
                ADV_DVC_VAR *adv_dvc = &boardp->dvc_var.adv_dvc_var;
 
                /*
-                * Reset the target's SCSI bus.
+                * Reset the chip and SCSI bus.
                 */
                ASC_DBG(1, "before AdvResetChipAndSB()\n");
                switch (AdvResetChipAndSB(adv_dvc)) {
                case ASC_TRUE:
-                       scmd_printk(KERN_INFO, scp, "SCSI bus reset "
+                       scmd_printk(KERN_INFO, scp, "SCSI host reset "
                                    "successful\n");
                        break;
                case ASC_FALSE:
                default:
-                       scmd_printk(KERN_INFO, scp, "SCSI bus reset error\n");
+                       scmd_printk(KERN_INFO, scp, "SCSI host reset error\n");
                        ret = FAILED;
                        break;
                }
                spin_lock_irqsave(shost->host_lock, flags);
                AdvISR(adv_dvc);
+               spin_unlock_irqrestore(shost->host_lock, flags);
        }
 
-       /* Save the time of the most recently completed reset. */
-       boardp->last_reset = jiffies;
-       spin_unlock_irqrestore(shost->host_lock, flags);
-
        ASC_DBG(1, "ret %d\n", ret);
 
        return ret;
@@ -7584,9 +7230,10 @@ static irqreturn_t advansys_interrupt(int irq, void *dev_id)
        struct Scsi_Host *shost = dev_id;
        struct asc_board *boardp = shost_priv(shost);
        irqreturn_t result = IRQ_NONE;
+       unsigned long flags;
 
        ASC_DBG(2, "boardp 0x%p\n", boardp);
-       spin_lock(shost->host_lock);
+       spin_lock_irqsave(shost->host_lock, flags);
        if (ASC_NARROW_BOARD(boardp)) {
                if (AscIsIntPending(shost->io_port)) {
                        result = IRQ_HANDLED;
@@ -7601,38 +7248,38 @@ static irqreturn_t advansys_interrupt(int irq, void *dev_id)
                        ASC_STATS(shost, interrupt);
                }
        }
-       spin_unlock(shost->host_lock);
+       spin_unlock_irqrestore(shost->host_lock, flags);
 
        ASC_DBG(1, "end\n");
        return result;
 }
 
-static int AscHostReqRiscHalt(PortAddr iop_base)
+static bool AscHostReqRiscHalt(PortAddr iop_base)
 {
        int count = 0;
-       int sta = 0;
+       bool sta = false;
        uchar saved_stop_code;
 
        if (AscIsChipHalted(iop_base))
-               return (1);
+               return true;
        saved_stop_code = AscReadLramByte(iop_base, ASCV_STOP_CODE_B);
        AscWriteLramByte(iop_base, ASCV_STOP_CODE_B,
                         ASC_STOP_HOST_REQ_RISC_HALT | ASC_STOP_REQ_RISC_STOP);
        do {
                if (AscIsChipHalted(iop_base)) {
-                       sta = 1;
+                       sta = true;
                        break;
                }
                mdelay(100);
        } while (count++ < 20);
        AscWriteLramByte(iop_base, ASCV_STOP_CODE_B, saved_stop_code);
-       return (sta);
+       return sta;
 }
 
-static int
+static bool
 AscSetRunChipSynRegAtID(PortAddr iop_base, uchar tid_no, uchar sdtr_data)
 {
-       int sta = FALSE;
+       bool sta = false;
 
        if (AscHostReqRiscHalt(iop_base)) {
                sta = AscSetChipSynRegAtID(iop_base, tid_no, sdtr_data);
@@ -7851,13 +7498,17 @@ static int advansys_slave_configure(struct scsi_device *sdev)
        return 0;
 }
 
-static __le32 advansys_get_sense_buffer_dma(struct scsi_cmnd *scp)
+static __le32 asc_get_sense_buffer_dma(struct scsi_cmnd *scp)
 {
        struct asc_board *board = shost_priv(scp->device->host);
+
        scp->SCp.dma_handle = dma_map_single(board->dev, scp->sense_buffer,
-                                            SCSI_SENSE_BUFFERSIZE, DMA_FROM_DEVICE);
-       dma_cache_sync(board->dev, scp->sense_buffer,
-                      SCSI_SENSE_BUFFERSIZE, DMA_FROM_DEVICE);
+                                            SCSI_SENSE_BUFFERSIZE,
+                                            DMA_FROM_DEVICE);
+       if (dma_mapping_error(board->dev, scp->SCp.dma_handle)) {
+               ASC_DBG(1, "failed to map sense buffer\n");
+               return 0;
+       }
        return cpu_to_le32(scp->SCp.dma_handle);
 }
 
@@ -7866,17 +7517,16 @@ static int asc_build_req(struct asc_board *boardp, struct scsi_cmnd *scp,
 {
        struct asc_dvc_var *asc_dvc = &boardp->dvc_var.asc_dvc_var;
        int use_sg;
+       u32 srb_tag;
 
        memset(asc_scsi_q, 0, sizeof(*asc_scsi_q));
 
        /*
-        * Point the ASC_SCSI_Q to the 'struct scsi_cmnd'.
+        * Set the srb_tag to the command tag + 1, as
+        * srb_tag '0' is used internally by the chip.
         */
-       asc_scsi_q->q2.srb_ptr = advansys_ptr_to_srb(asc_dvc, scp);
-       if (asc_scsi_q->q2.srb_ptr == BAD_SRB) {
-               scp->result = HOST_BYTE(DID_SOFT_ERROR);
-               return ASC_ERROR;
-       }
+       srb_tag = scp->request->tag + 1;
+       asc_scsi_q->q2.srb_tag = srb_tag;
 
        /*
         * Build the ASC_SCSI_Q request.
@@ -7887,8 +7537,10 @@ static int asc_build_req(struct asc_board *boardp, struct scsi_cmnd *scp,
        asc_scsi_q->q1.target_lun = scp->device->lun;
        asc_scsi_q->q2.target_ix =
            ASC_TIDLUN_TO_IX(scp->device->id, scp->device->lun);
-       asc_scsi_q->q1.sense_addr = advansys_get_sense_buffer_dma(scp);
+       asc_scsi_q->q1.sense_addr = asc_get_sense_buffer_dma(scp);
        asc_scsi_q->q1.sense_len = SCSI_SENSE_BUFFERSIZE;
+       if (!asc_scsi_q->q1.sense_addr)
+               return ASC_BUSY;
 
        /*
         * If there are any outstanding requests for the current target,
@@ -7910,7 +7562,10 @@ static int asc_build_req(struct asc_board *boardp, struct scsi_cmnd *scp,
 
        /* Build ASC_SCSI_Q */
        use_sg = scsi_dma_map(scp);
-       if (use_sg != 0) {
+       if (use_sg < 0) {
+               ASC_DBG(1, "failed to map sglist\n");
+               return ASC_BUSY;
+       } else if (use_sg > 0) {
                int sgcnt;
                struct scatterlist *slp;
                struct asc_sg_head *asc_sg_head;
@@ -7975,20 +7630,19 @@ static int asc_build_req(struct asc_board *boardp, struct scsi_cmnd *scp,
  *      ADV_ERROR(-1) - SG List creation failed
  */
 static int
-adv_get_sglist(struct asc_board *boardp, adv_req_t *reqp, struct scsi_cmnd *scp,
-              int use_sg)
+adv_get_sglist(struct asc_board *boardp, adv_req_t *reqp,
+              ADV_SCSI_REQ_Q *scsiqp, struct scsi_cmnd *scp, int use_sg)
 {
-       adv_sgblk_t *sgblkp;
-       ADV_SCSI_REQ_Q *scsiqp;
+       adv_sgblk_t *sgblkp, *prev_sgblkp;
        struct scatterlist *slp;
        int sg_elem_cnt;
        ADV_SG_BLOCK *sg_block, *prev_sg_block;
-       ADV_PADDR sg_block_paddr;
+       dma_addr_t sgblk_paddr;
        int i;
 
-       scsiqp = (ADV_SCSI_REQ_Q *)ADV_32BALIGN(&reqp->scsi_req_q);
        slp = scsi_sglist(scp);
        sg_elem_cnt = use_sg;
+       prev_sgblkp = NULL;
        prev_sg_block = NULL;
        reqp->sgblkp = NULL;
 
@@ -7998,7 +7652,9 @@ adv_get_sglist(struct asc_board *boardp, adv_req_t *reqp, struct scsi_cmnd *scp,
                 * list. One 'adv_sgblk_t' structure holds NO_OF_SG_PER_BLOCK
                 * (15) scatter-gather elements.
                 */
-               if ((sgblkp = boardp->adv_sgblkp) == NULL) {
+               sgblkp = dma_pool_alloc(boardp->adv_sgblk_pool, GFP_ATOMIC,
+                                       &sgblk_paddr);
+               if (!sgblkp) {
                        ASC_DBG(1, "no free adv_sgblk_t\n");
                        ASC_STATS(scp->device->host, adv_build_nosg);
 
@@ -8009,24 +7665,16 @@ adv_get_sglist(struct asc_board *boardp, adv_req_t *reqp, struct scsi_cmnd *scp,
                        while ((sgblkp = reqp->sgblkp) != NULL) {
                                /* Remove 'sgblkp' from the request list. */
                                reqp->sgblkp = sgblkp->next_sgblkp;
-
-                               /* Add 'sgblkp' to the board free list. */
-                               sgblkp->next_sgblkp = boardp->adv_sgblkp;
-                               boardp->adv_sgblkp = sgblkp;
+                               sgblkp->next_sgblkp = NULL;
+                               dma_pool_free(boardp->adv_sgblk_pool, sgblkp,
+                                             sgblkp->sg_addr);
                        }
                        return ASC_BUSY;
                }
-
                /* Complete 'adv_sgblk_t' board allocation. */
-               boardp->adv_sgblkp = sgblkp->next_sgblkp;
+               sgblkp->sg_addr = sgblk_paddr;
                sgblkp->next_sgblkp = NULL;
-
-               /*
-                * Get 8 byte aligned virtual and physical addresses
-                * for the allocated ADV_SG_BLOCK structure.
-                */
-               sg_block = (ADV_SG_BLOCK *)ADV_8BALIGN(&sgblkp->sg_block);
-               sg_block_paddr = virt_to_bus(sg_block);
+               sg_block = &sgblkp->sg_block;
 
                /*
                 * Check if this is the first 'adv_sgblk_t' for the
@@ -8041,17 +7689,16 @@ adv_get_sglist(struct asc_board *boardp, adv_req_t *reqp, struct scsi_cmnd *scp,
                         * address pointers.
                         */
                        scsiqp->sg_list_ptr = sg_block;
-                       scsiqp->sg_real_addr = cpu_to_le32(sg_block_paddr);
+                       scsiqp->sg_real_addr = cpu_to_le32(sgblk_paddr);
                } else {
                        /* Request's second or later scatter-gather block. */
-                       sgblkp->next_sgblkp = reqp->sgblkp;
-                       reqp->sgblkp = sgblkp;
+                       prev_sgblkp->next_sgblkp = sgblkp;
 
                        /*
                         * Point the previous ADV_SG_BLOCK structure to
                         * the newly allocated ADV_SG_BLOCK structure.
                         */
-                       prev_sg_block->sg_ptr = cpu_to_le32(sg_block_paddr);
+                       prev_sg_block->sg_ptr = cpu_to_le32(sgblk_paddr);
                }
 
                for (i = 0; i < NO_OF_SG_PER_BLOCK; i++) {
@@ -8062,15 +7709,19 @@ adv_get_sglist(struct asc_board *boardp, adv_req_t *reqp, struct scsi_cmnd *scp,
                        ASC_STATS_ADD(scp->device->host, xfer_sect,
                                      DIV_ROUND_UP(sg_dma_len(slp), 512));
 
-                       if (--sg_elem_cnt == 0) {       /* Last ADV_SG_BLOCK and scatter-gather entry. */
+                       if (--sg_elem_cnt == 0) {
+                               /*
+                                * Last ADV_SG_BLOCK and scatter-gather entry.
+                                */
                                sg_block->sg_cnt = i + 1;
-                               sg_block->sg_ptr = 0L;  /* Last ADV_SG_BLOCK in list. */
+                               sg_block->sg_ptr = 0L; /* Last ADV_SG_BLOCK in list. */
                                return ADV_SUCCESS;
                        }
                        slp++;
                }
                sg_block->sg_cnt = NO_OF_SG_PER_BLOCK;
                prev_sg_block = sg_block;
+               prev_sgblkp = sgblkp;
        }
 }
 
@@ -8080,38 +7731,35 @@ adv_get_sglist(struct asc_board *boardp, adv_req_t *reqp, struct scsi_cmnd *scp,
  * If an adv_req_t can not be allocated to issue the request,
  * then return ASC_BUSY. If an error occurs, then return ASC_ERROR.
  *
- * Multi-byte fields in the ASC_SCSI_REQ_Q that are used by the
+ * Multi-byte fields in the ADV_SCSI_REQ_Q that are used by the
  * microcode for DMA addresses or math operations are byte swapped
  * to little-endian order.
  */
 static int
 adv_build_req(struct asc_board *boardp, struct scsi_cmnd *scp,
-             ADV_SCSI_REQ_Q **adv_scsiqpp)
+             adv_req_t **adv_reqpp)
 {
+       u32 srb_tag = scp->request->tag;
        adv_req_t *reqp;
        ADV_SCSI_REQ_Q *scsiqp;
-       int i;
        int ret;
        int use_sg;
+       dma_addr_t sense_addr;
 
        /*
         * Allocate an adv_req_t structure from the board to execute
         * the command.
         */
-       if (boardp->adv_reqp == NULL) {
+       reqp = &boardp->adv_reqp[srb_tag];
+       if (reqp->cmndp && reqp->cmndp != scp ) {
                ASC_DBG(1, "no free adv_req_t\n");
                ASC_STATS(scp->device->host, adv_build_noreq);
                return ASC_BUSY;
-       } else {
-               reqp = boardp->adv_reqp;
-               boardp->adv_reqp = reqp->next_reqp;
-               reqp->next_reqp = NULL;
        }
 
-       /*
-        * Get 32-byte aligned ADV_SCSI_REQ_Q and ADV_SG_BLOCK pointers.
-        */
-       scsiqp = (ADV_SCSI_REQ_Q *)ADV_32BALIGN(&reqp->scsi_req_q);
+       reqp->req_addr = boardp->adv_reqp_addr + (srb_tag * sizeof(adv_req_t));
+
+       scsiqp = &reqp->scsi_req_q;
 
        /*
         * Initialize the structure.
@@ -8119,14 +7767,15 @@ adv_build_req(struct asc_board *boardp, struct scsi_cmnd *scp,
        scsiqp->cntl = scsiqp->scsi_cntl = scsiqp->done_status = 0;
 
        /*
-        * Set the ADV_SCSI_REQ_Q 'srb_ptr' to point to the adv_req_t structure.
+        * Set the srb_tag to the command tag.
         */
-       scsiqp->srb_ptr = ADV_VADDR_TO_U32(reqp);
+       scsiqp->srb_tag = srb_tag;
 
        /*
-        * Set the adv_req_t 'cmndp' to point to the struct scsi_cmnd structure.
+        * Set 'host_scribble' to point to the adv_req_t structure.
         */
        reqp->cmndp = scp;
+       scp->host_scribble = (void *)reqp;
 
        /*
         * Build the ADV_SCSI_REQ_Q request.
@@ -8135,28 +7784,38 @@ adv_build_req(struct asc_board *boardp, struct scsi_cmnd *scp,
        /* Set CDB length and copy it to the request structure.  */
        scsiqp->cdb_len = scp->cmd_len;
        /* Copy first 12 CDB bytes to cdb[]. */
-       for (i = 0; i < scp->cmd_len && i < 12; i++) {
-               scsiqp->cdb[i] = scp->cmnd[i];
-       }
+       memcpy(scsiqp->cdb, scp->cmnd, scp->cmd_len < 12 ? scp->cmd_len : 12);
        /* Copy last 4 CDB bytes, if present, to cdb16[]. */
-       for (; i < scp->cmd_len; i++) {
-               scsiqp->cdb16[i - 12] = scp->cmnd[i];
+       if (scp->cmd_len > 12) {
+               int cdb16_len = scp->cmd_len - 12;
+
+               memcpy(scsiqp->cdb16, &scp->cmnd[12], cdb16_len);
        }
 
        scsiqp->target_id = scp->device->id;
        scsiqp->target_lun = scp->device->lun;
 
-       scsiqp->sense_addr = cpu_to_le32(virt_to_bus(&scp->sense_buffer[0]));
-       scsiqp->sense_len = SCSI_SENSE_BUFFERSIZE;
+       sense_addr = dma_map_single(boardp->dev, scp->sense_buffer,
+                                   SCSI_SENSE_BUFFERSIZE, DMA_FROM_DEVICE);
+       if (dma_mapping_error(boardp->dev, sense_addr)) {
+               ASC_DBG(1, "failed to map sense buffer\n");
+               ASC_STATS(scp->device->host, adv_build_noreq);
+               return ASC_BUSY;
+       }
+       scsiqp->sense_addr = cpu_to_le32(sense_addr);
+       scsiqp->sense_len = cpu_to_le32(SCSI_SENSE_BUFFERSIZE);
 
        /* Build ADV_SCSI_REQ_Q */
 
        use_sg = scsi_dma_map(scp);
-       if (use_sg == 0) {
+       if (use_sg < 0) {
+               ASC_DBG(1, "failed to map SG list\n");
+               ASC_STATS(scp->device->host, adv_build_noreq);
+               return ASC_BUSY;
+       } else if (use_sg == 0) {
                /* Zero-length transfer */
                reqp->sgblkp = NULL;
                scsiqp->data_cnt = 0;
-               scsiqp->vdata_addr = NULL;
 
                scsiqp->data_addr = 0;
                scsiqp->sg_list_ptr = NULL;
@@ -8168,27 +7827,20 @@ adv_build_req(struct asc_board *boardp, struct scsi_cmnd *scp,
                                   scp->device->host->sg_tablesize);
                        scsi_dma_unmap(scp);
                        scp->result = HOST_BYTE(DID_ERROR);
-
-                       /*
-                        * Free the 'adv_req_t' structure by adding it back
-                        * to the board free list.
-                        */
-                       reqp->next_reqp = boardp->adv_reqp;
-                       boardp->adv_reqp = reqp;
+                       reqp->cmndp = NULL;
+                       scp->host_scribble = NULL;
 
                        return ASC_ERROR;
                }
 
                scsiqp->data_cnt = cpu_to_le32(scsi_bufflen(scp));
 
-               ret = adv_get_sglist(boardp, reqp, scp, use_sg);
+               ret = adv_get_sglist(boardp, reqp, scsiqp, scp, use_sg);
                if (ret != ADV_SUCCESS) {
-                       /*
-                        * Free the adv_req_t structure by adding it back to
-                        * the board free list.
-                        */
-                       reqp->next_reqp = boardp->adv_reqp;
-                       boardp->adv_reqp = reqp;
+                       scsi_dma_unmap(scp);
+                       scp->result = HOST_BYTE(DID_ERROR);
+                       reqp->cmndp = NULL;
+                       scp->host_scribble = NULL;
 
                        return ret;
                }
@@ -8201,7 +7853,7 @@ adv_build_req(struct asc_board *boardp, struct scsi_cmnd *scp,
        ASC_DBG_PRT_ADV_SCSI_REQ_Q(2, scsiqp);
        ASC_DBG_PRT_CDB(1, scp->cmnd, scp->cmd_len);
 
-       *adv_scsiqpp = scsiqp;
+       *adv_reqpp = reqp;
 
        return ASC_NOERROR;
 }
@@ -8358,8 +8010,8 @@ AscPutReadySgListQueue(ASC_DVC_VAR *asc_dvc, ASC_SCSI_Q *scsiq, uchar q_no)
        int i;
        ASC_SG_HEAD *sg_head;
        ASC_SG_LIST_Q scsi_sg_q;
-       ASC_DCNT saved_data_addr;
-       ASC_DCNT saved_data_cnt;
+       __le32 saved_data_addr;
+       __le32 saved_data_cnt;
        PortAddr iop_base;
        ushort sg_list_dwords;
        ushort sg_index;
@@ -8371,42 +8023,15 @@ AscPutReadySgListQueue(ASC_DVC_VAR *asc_dvc, ASC_SCSI_Q *scsiq, uchar q_no)
        sg_head = scsiq->sg_head;
        saved_data_addr = scsiq->q1.data_addr;
        saved_data_cnt = scsiq->q1.data_cnt;
-       scsiq->q1.data_addr = (ASC_PADDR) sg_head->sg_list[0].addr;
-       scsiq->q1.data_cnt = (ASC_DCNT) sg_head->sg_list[0].bytes;
-#if CC_VERY_LONG_SG_LIST
+       scsiq->q1.data_addr = cpu_to_le32(sg_head->sg_list[0].addr);
+       scsiq->q1.data_cnt = cpu_to_le32(sg_head->sg_list[0].bytes);
        /*
-        * If sg_head->entry_cnt is greater than ASC_MAX_SG_LIST
-        * then not all SG elements will fit in the allocated queues.
-        * The rest of the SG elements will be copied when the RISC
-        * completes the SG elements that fit and halts.
+        * Set sg_entry_cnt to be the number of SG elements that
+        * will fit in the allocated SG queues. It is minus 1, because
+        * the first SG element is handled above.
         */
-       if (sg_head->entry_cnt > ASC_MAX_SG_LIST) {
-               /*
-                * Set sg_entry_cnt to be the number of SG elements that
-                * will fit in the allocated SG queues. It is minus 1, because
-                * the first SG element is handled above. ASC_MAX_SG_LIST is
-                * already inflated by 1 to account for this. For example it
-                * may be 50 which is 1 + 7 queues * 7 SG elements.
-                */
-               sg_entry_cnt = ASC_MAX_SG_LIST - 1;
+       sg_entry_cnt = sg_head->entry_cnt - 1;
 
-               /*
-                * Keep track of remaining number of SG elements that will
-                * need to be handled from a_isr.c.
-                */
-               scsiq->remain_sg_entry_cnt =
-                   sg_head->entry_cnt - ASC_MAX_SG_LIST;
-       } else {
-#endif /* CC_VERY_LONG_SG_LIST */
-               /*
-                * Set sg_entry_cnt to be the number of SG elements that
-                * will fit in the allocated SG queues. It is minus 1, because
-                * the first SG element is handled above.
-                */
-               sg_entry_cnt = sg_head->entry_cnt - 1;
-#if CC_VERY_LONG_SG_LIST
-       }
-#endif /* CC_VERY_LONG_SG_LIST */
        if (sg_entry_cnt != 0) {
                scsiq->q1.cntl |= QC_SG_HEAD;
                q_addr = ASC_QNO_TO_QADDR(q_no);
@@ -8431,21 +8056,7 @@ AscPutReadySgListQueue(ASC_DVC_VAR *asc_dvc, ASC_SCSI_Q *scsiq, uchar q_no)
                                            ASC_SG_LIST_PER_Q - 1;
                                }
                        } else {
-#if CC_VERY_LONG_SG_LIST
-                               /*
-                                * This is the last SG queue in the list of
-                                * allocated SG queues. If there are more
-                                * SG elements than will fit in the allocated
-                                * queues, then set the QCSG_SG_XFER_MORE flag.
-                                */
-                               if (sg_head->entry_cnt > ASC_MAX_SG_LIST) {
-                                       scsi_sg_q.cntl |= QCSG_SG_XFER_MORE;
-                               } else {
-#endif /* CC_VERY_LONG_SG_LIST */
-                                       scsi_sg_q.cntl |= QCSG_SG_XFER_END;
-#if CC_VERY_LONG_SG_LIST
-                               }
-#endif /* CC_VERY_LONG_SG_LIST */
+                               scsi_sg_q.cntl |= QCSG_SG_XFER_END;
                                sg_list_dwords = sg_entry_cnt << 1;
                                if (i == 0) {
                                        scsi_sg_q.sg_list_cnt = sg_entry_cnt;
@@ -8550,9 +8161,9 @@ static int AscExeScsiQueue(ASC_DVC_VAR *asc_dvc, ASC_SCSI_Q *scsiq)
        PortAddr iop_base;
        int sta;
        int n_q_required;
-       int disable_syn_offset_one_fix;
+       bool disable_syn_offset_one_fix;
        int i;
-       ASC_PADDR addr;
+       u32 addr;
        ushort sg_entry_cnt = 0;
        ushort sg_entry_cnt_minus_one = 0;
        uchar target_ix;
@@ -8562,12 +8173,12 @@ static int AscExeScsiQueue(ASC_DVC_VAR *asc_dvc, ASC_SCSI_Q *scsiq)
        uchar scsi_cmd;
        uchar disable_cmd;
        ASC_SG_HEAD *sg_head;
-       ASC_DCNT data_cnt;
+       unsigned long data_cnt;
 
        iop_base = asc_dvc->iop_base;
        sg_head = scsiq->sg_head;
        if (asc_dvc->err_code != 0)
-               return (ERR);
+               return ASC_ERROR;
        scsiq->q1.q_no = 0;
        if ((scsiq->q2.tag_code & ASC_TAG_FLAG_EXTRA_BYTES) == 0) {
                scsiq->q1.extra_bytes = 0;
@@ -8593,46 +8204,41 @@ static int AscExeScsiQueue(ASC_DVC_VAR *asc_dvc, ASC_SCSI_Q *scsiq)
        }
        if (asc_dvc->in_critical_cnt != 0) {
                AscSetLibErrorCode(asc_dvc, ASCQ_ERR_CRITICAL_RE_ENTRY);
-               return (ERR);
+               return ASC_ERROR;
        }
        asc_dvc->in_critical_cnt++;
        if ((scsiq->q1.cntl & QC_SG_HEAD) != 0) {
                if ((sg_entry_cnt = sg_head->entry_cnt) == 0) {
                        asc_dvc->in_critical_cnt--;
-                       return (ERR);
+                       return ASC_ERROR;
                }
-#if !CC_VERY_LONG_SG_LIST
                if (sg_entry_cnt > ASC_MAX_SG_LIST) {
                        asc_dvc->in_critical_cnt--;
-                       return (ERR);
+                       return ASC_ERROR;
                }
-#endif /* !CC_VERY_LONG_SG_LIST */
                if (sg_entry_cnt == 1) {
-                       scsiq->q1.data_addr =
-                           (ADV_PADDR)sg_head->sg_list[0].addr;
-                       scsiq->q1.data_cnt =
-                           (ADV_DCNT)sg_head->sg_list[0].bytes;
+                       scsiq->q1.data_addr = cpu_to_le32(sg_head->sg_list[0].addr);
+                       scsiq->q1.data_cnt = cpu_to_le32(sg_head->sg_list[0].bytes);
                        scsiq->q1.cntl &= ~(QC_SG_HEAD | QC_SG_SWAP_QUEUE);
                }
                sg_entry_cnt_minus_one = sg_entry_cnt - 1;
        }
        scsi_cmd = scsiq->cdbptr[0];
-       disable_syn_offset_one_fix = FALSE;
+       disable_syn_offset_one_fix = false;
        if ((asc_dvc->pci_fix_asyn_xfer & scsiq->q1.target_id) &&
            !(asc_dvc->pci_fix_asyn_xfer_always & scsiq->q1.target_id)) {
                if (scsiq->q1.cntl & QC_SG_HEAD) {
                        data_cnt = 0;
                        for (i = 0; i < sg_entry_cnt; i++) {
-                               data_cnt +=
-                                   (ADV_DCNT)le32_to_cpu(sg_head->sg_list[i].
-                                                         bytes);
+                               data_cnt += le32_to_cpu(sg_head->sg_list[i].
+                                                       bytes);
                        }
                } else {
                        data_cnt = le32_to_cpu(scsiq->q1.data_cnt);
                }
                if (data_cnt != 0UL) {
                        if (data_cnt < 512UL) {
-                               disable_syn_offset_one_fix = TRUE;
+                               disable_syn_offset_one_fix = true;
                        } else {
                                for (i = 0; i < ASC_SYN_OFFSET_ONE_DISABLE_LIST;
                                     i++) {
@@ -8643,7 +8249,7 @@ static int AscExeScsiQueue(ASC_DVC_VAR *asc_dvc, ASC_SCSI_Q *scsiq)
                                        }
                                        if (scsi_cmd == disable_cmd) {
                                                disable_syn_offset_one_fix =
-                                                   TRUE;
+                                                   true;
                                                break;
                                        }
                                }
@@ -8662,12 +8268,11 @@ static int AscExeScsiQueue(ASC_DVC_VAR *asc_dvc, ASC_SCSI_Q *scsiq)
                        if (asc_dvc->bug_fix_cntl & ASC_BUG_FIX_IF_NOT_DWB) {
                                if ((scsi_cmd == READ_6) ||
                                    (scsi_cmd == READ_10)) {
-                                       addr =
-                                           (ADV_PADDR)le32_to_cpu(sg_head->
+                                       addr = le32_to_cpu(sg_head->
                                                                   sg_list
                                                                   [sg_entry_cnt_minus_one].
                                                                   addr) +
-                                           (ADV_DCNT)le32_to_cpu(sg_head->
+                                               le32_to_cpu(sg_head->
                                                                  sg_list
                                                                  [sg_entry_cnt_minus_one].
                                                                  bytes);
@@ -8688,8 +8293,7 @@ static int AscExeScsiQueue(ASC_DVC_VAR *asc_dvc, ASC_SCSI_Q *scsiq)
                                                                sg_list
                                                                [sg_entry_cnt_minus_one].
                                                                bytes);
-                                               data_cnt -=
-                                                   (ASC_DCNT) extra_bytes;
+                                               data_cnt -= extra_bytes;
                                                sg_head->
                                                    sg_list
                                                    [sg_entry_cnt_minus_one].
@@ -8700,16 +8304,6 @@ static int AscExeScsiQueue(ASC_DVC_VAR *asc_dvc, ASC_SCSI_Q *scsiq)
                        }
                }
                sg_head->entry_to_copy = sg_head->entry_cnt;
-#if CC_VERY_LONG_SG_LIST
-               /*
-                * Set the sg_entry_cnt to the maximum possible. The rest of
-                * the SG elements will be copied when the RISC completes the
-                * SG elements that fit and halts.
-                */
-               if (sg_entry_cnt > ASC_MAX_SG_LIST) {
-                       sg_entry_cnt = ASC_MAX_SG_LIST;
-               }
-#endif /* CC_VERY_LONG_SG_LIST */
                n_q_required = AscSgListToQueue(sg_entry_cnt);
                if ((AscGetNumOfFreeQueue(asc_dvc, target_ix, n_q_required) >=
                     (uint) n_q_required)
@@ -8744,8 +8338,7 @@ static int AscExeScsiQueue(ASC_DVC_VAR *asc_dvc, ASC_SCSI_Q *scsiq)
                                                    == 0) {
                                                        scsiq->q2.tag_code |=
                                                            ASC_TAG_FLAG_EXTRA_BYTES;
-                                                       data_cnt -= (ASC_DCNT)
-                                                           extra_bytes;
+                                                       data_cnt -= extra_bytes;
                                                        scsiq->q1.data_cnt =
                                                            cpu_to_le32
                                                            (data_cnt);
@@ -8780,7 +8373,7 @@ static int AscExeScsiQueue(ASC_DVC_VAR *asc_dvc, ASC_SCSI_Q *scsiq)
  * If 'done_status' is not set to QD_DO_RETRY, then 'error_retry' will be
  * set to SCSI_MAX_RETRY.
  *
- * Multi-byte fields in the ASC_SCSI_REQ_Q that are used by the microcode
+ * Multi-byte fields in the ADV_SCSI_REQ_Q that are used by the microcode
  * for DMA addresses or math operations are byte swapped to little-endian
  * order.
  *
@@ -8791,11 +8384,11 @@ static int AscExeScsiQueue(ASC_DVC_VAR *asc_dvc, ASC_SCSI_Q *scsiq)
  *      ADV_ERROR(-1) -  Invalid ADV_SCSI_REQ_Q request structure
  *                       host IC error.
  */
-static int AdvExeScsiQueue(ADV_DVC_VAR *asc_dvc, ADV_SCSI_REQ_Q *scsiq)
+static int AdvExeScsiQueue(ADV_DVC_VAR *asc_dvc, adv_req_t *reqp)
 {
        AdvPortAddr iop_base;
-       ADV_PADDR req_paddr;
        ADV_CARR_T *new_carrp;
+       ADV_SCSI_REQ_Q *scsiq = &reqp->scsi_req_q;
 
        /*
         * The ADV_SCSI_REQ_Q 'target_id' field should never exceed ADV_MAX_TID.
@@ -8812,39 +8405,19 @@ static int AdvExeScsiQueue(ADV_DVC_VAR *asc_dvc, ADV_SCSI_REQ_Q *scsiq)
         * Allocate a carrier ensuring at least one carrier always
         * remains on the freelist and initialize fields.
         */
-       if ((new_carrp = asc_dvc->carr_freelist) == NULL) {
+       new_carrp = adv_get_next_carrier(asc_dvc);
+       if (!new_carrp) {
+               ASC_DBG(1, "No free carriers\n");
                return ADV_BUSY;
        }
-       asc_dvc->carr_freelist = (ADV_CARR_T *)
-           ADV_U32_TO_VADDR(le32_to_cpu(new_carrp->next_vpa));
-       asc_dvc->carr_pending_cnt++;
-
-       /*
-        * Set the carrier to be a stopper by setting 'next_vpa'
-        * to the stopper value. The current stopper will be changed
-        * below to point to the new stopper.
-        */
-       new_carrp->next_vpa = cpu_to_le32(ASC_CQ_STOPPER);
 
-       /*
-        * Clear the ADV_SCSI_REQ_Q done flag.
-        */
-       scsiq->a_flag &= ~ADV_SCSIQ_DONE;
-
-       req_paddr = virt_to_bus(scsiq);
-       BUG_ON(req_paddr & 31);
-       /* Wait for assertion before making little-endian */
-       req_paddr = cpu_to_le32(req_paddr);
+       asc_dvc->carr_pending_cnt++;
 
        /* Save virtual and physical address of ADV_SCSI_REQ_Q and carrier. */
-       scsiq->scsiq_ptr = cpu_to_le32(ADV_VADDR_TO_U32(scsiq));
-       scsiq->scsiq_rptr = req_paddr;
+       scsiq->scsiq_ptr = cpu_to_le32(scsiq->srb_tag);
+       scsiq->scsiq_rptr = cpu_to_le32(reqp->req_addr);
 
-       scsiq->carr_va = cpu_to_le32(ADV_VADDR_TO_U32(asc_dvc->icq_sp));
-       /*
-        * Every ADV_CARR_T.carr_pa is byte swapped to little-endian
-        * order during initialization.
-        */
+       scsiq->carr_va = asc_dvc->icq_sp->carr_va;
        scsiq->carr_pa = asc_dvc->icq_sp->carr_pa;
 
        /*
@@ -8852,7 +8425,7 @@ static int AdvExeScsiQueue(ADV_DVC_VAR *asc_dvc, ADV_SCSI_REQ_Q *scsiq)
         * the microcode. The newly allocated stopper will become the new
         * stopper.
         */
-       asc_dvc->icq_sp->areq_vpa = req_paddr;
+       asc_dvc->icq_sp->areq_vpa = scsiq->scsiq_rptr;
 
        /*
         * Set the 'next_vpa' pointer for the old stopper to be the
@@ -8907,11 +8480,10 @@ static int asc_execute_scsi_cmnd(struct scsi_cmnd *scp)
                ASC_DVC_VAR *asc_dvc = &boardp->dvc_var.asc_dvc_var;
                struct asc_scsi_q asc_scsi_q;
 
-               /* asc_build_req() can not return ASC_BUSY. */
                ret = asc_build_req(boardp, scp, &asc_scsi_q);
-               if (ret == ASC_ERROR) {
+               if (ret != ASC_NOERROR) {
                        ASC_STATS(scp->device->host, build_error);
-                       return ASC_ERROR;
+                       return ret;
                }
 
                ret = AscExeScsiQueue(asc_dvc, &asc_scsi_q);
@@ -8919,9 +8491,9 @@ static int asc_execute_scsi_cmnd(struct scsi_cmnd *scp)
                err_code = asc_dvc->err_code;
        } else {
                ADV_DVC_VAR *adv_dvc = &boardp->dvc_var.adv_dvc_var;
-               ADV_SCSI_REQ_Q *adv_scsiqp;
+               adv_req_t *adv_reqp;
 
-               switch (adv_build_req(boardp, scp, &adv_scsiqp)) {
+               switch (adv_build_req(boardp, scp, &adv_reqp)) {
                case ASC_NOERROR:
                        ASC_DBG(3, "adv_build_req ASC_NOERROR\n");
                        break;
@@ -8941,7 +8513,7 @@ static int asc_execute_scsi_cmnd(struct scsi_cmnd *scp)
                        return ASC_ERROR;
                }
 
-               ret = AdvExeScsiQueue(adv_dvc, adv_scsiqp);
+               ret = AdvExeScsiQueue(adv_dvc, adv_reqp);
                err_code = adv_dvc->err_code;
        }
 
@@ -8956,6 +8528,7 @@ static int asc_execute_scsi_cmnd(struct scsi_cmnd *scp)
                ASC_DBG(1, "ExeScsiQueue() ASC_NOERROR\n");
                break;
        case ASC_BUSY:
+               ASC_DBG(1, "ExeScsiQueue() ASC_BUSY\n");
                ASC_STATS(scp->device->host, exe_busy);
                break;
        case ASC_ERROR:
@@ -9122,7 +8695,7 @@ static int AscStopQueueExe(PortAddr iop_base)
        return (0);
 }
 
-static ASC_DCNT AscGetMaxDmaCount(ushort bus_type)
+static unsigned int AscGetMaxDmaCount(ushort bus_type)
 {
        if (bus_type & ASC_IS_ISA)
                return ASC_MAX_ISA_DMA_COUNT;
@@ -9183,15 +8756,13 @@ static uchar AscSetIsaDmaSpeed(PortAddr iop_base, uchar speed_value)
 }
 #endif /* CONFIG_ISA */
 
-static ushort AscInitAscDvcVar(ASC_DVC_VAR *asc_dvc)
+static void AscInitAscDvcVar(ASC_DVC_VAR *asc_dvc)
 {
        int i;
        PortAddr iop_base;
-       ushort warn_code;
        uchar chip_version;
 
        iop_base = asc_dvc->iop_base;
-       warn_code = 0;
        asc_dvc->err_code = 0;
        if ((asc_dvc->bus_type &
             (ASC_IS_ISA | ASC_IS_PCI | ASC_IS_EISA | ASC_IS_VL)) == 0) {
@@ -9205,7 +8776,7 @@ static ushort AscInitAscDvcVar(ASC_DVC_VAR *asc_dvc)
        /* asc_dvc->init_state initialized in AscInitGetConfig(). */
        asc_dvc->sdtr_done = 0;
        asc_dvc->cur_total_qng = 0;
-       asc_dvc->is_in_int = 0;
+       asc_dvc->is_in_int = false;
        asc_dvc->in_critical_cnt = 0;
        asc_dvc->last_q_shortage = 0;
        asc_dvc->use_tagged_qng = 0;
@@ -9267,7 +8838,6 @@ static ushort AscInitAscDvcVar(ASC_DVC_VAR *asc_dvc)
                asc_dvc->scsiq_busy_tail[i] = (ASC_SCSI_Q *)0L;
                asc_dvc->cfg->max_tag_qng[i] = ASC_MAX_INRAM_TAG_QNG;
        }
-       return warn_code;
 }
 
 static int AscWriteEEPCmdReg(PortAddr iop_base, uchar cmd_reg)
@@ -9385,7 +8955,7 @@ static int AscWriteEEPDataReg(PortAddr iop_base, ushort data_reg)
        int retry;
 
        retry = 0;
-       while (TRUE) {
+       while (true) {
                AscSetChipEEPData(iop_base, data_reg);
                mdelay(1);
                read_back = AscGetChipEEPData(iop_base);
@@ -9521,7 +9091,7 @@ static int AscSetEEPConfig(PortAddr iop_base, ASCEEP_CONFIG *cfg_buf,
        int n_error;
 
        retry = 0;
-       while (TRUE) {
+       while (true) {
                if ((n_error = AscSetEEPConfigOnce(iop_base, cfg_buf,
                                                   bus_type)) == 0) {
                        break;
@@ -9533,7 +9103,7 @@ static int AscSetEEPConfig(PortAddr iop_base, ASCEEP_CONFIG *cfg_buf,
        return n_error;
 }
 
-static ushort AscInitFromEEP(ASC_DVC_VAR *asc_dvc)
+static int AscInitFromEEP(ASC_DVC_VAR *asc_dvc)
 {
        ASCEEP_CONFIG eep_config_buf;
        ASCEEP_CONFIG *eep_config;
@@ -9548,13 +9118,13 @@ static ushort AscInitFromEEP(ASC_DVC_VAR *asc_dvc)
        warn_code = 0;
        AscWriteLramWord(iop_base, ASCV_HALTCODE_W, 0x00FE);
        AscStopQueueExe(iop_base);
-       if ((AscStopChip(iop_base) == FALSE) ||
+       if ((AscStopChip(iop_base)) ||
            (AscGetChipScsiCtrl(iop_base) != 0)) {
                asc_dvc->init_state |= ASC_INIT_RESET_SCSI_DONE;
                AscResetChipAndScsiBus(asc_dvc);
                mdelay(asc_dvc->scsi_reset_wait * 1000); /* XXX: msleep? */
        }
-       if (AscIsChipHalted(iop_base) == FALSE) {
+       if (!AscIsChipHalted(iop_base)) {
                asc_dvc->err_code |= ASC_IERR_START_STOP_CHIP;
                return (warn_code);
        }
@@ -9709,8 +9279,8 @@ static int AscInitGetConfig(struct Scsi_Host *shost)
                return asc_dvc->err_code;
 
        if (AscFindSignature(asc_dvc->iop_base)) {
-               warn_code |= AscInitAscDvcVar(asc_dvc);
-               warn_code |= AscInitFromEEP(asc_dvc);
+               AscInitAscDvcVar(asc_dvc);
+               warn_code = AscInitFromEEP(asc_dvc);
                asc_dvc->init_state |= ASC_INIT_STATE_END_GET_CFG;
                if (asc_dvc->scsi_reset_wait > ASC_MAX_SCSI_RESET_WAIT)
                        asc_dvc->scsi_reset_wait = ASC_MAX_SCSI_RESET_WAIT;
@@ -9866,6 +9436,7 @@ static int AscInitSetConfig(struct pci_dev *pdev, struct Scsi_Host *shost)
  * on big-endian platforms so char fields read as words are actually being
  * unswapped on big-endian platforms.
  */
+#ifdef CONFIG_PCI
 static ADVEEP_3550_CONFIG Default_3550_EEPROM_Config = {
        ADV_EEPROM_BIOS_ENABLE, /* cfg_lsw */
        0x0000,                 /* cfg_msw */
@@ -10202,7 +9773,6 @@ static ADVEEP_38C1600_CONFIG ADVEEP_38C1600_Config_Field_IsChar = {
        0                       /* 63 reserved */
 };
 
-#ifdef CONFIG_PCI
 /*
  * Wait for EEPROM command to complete
  */
@@ -11232,7 +10802,7 @@ static struct scsi_host_template advansys_template = {
        .name = DRV_NAME,
        .info = advansys_info,
        .queuecommand = advansys_queuecommand,
-       .eh_bus_reset_handler = advansys_reset,
+       .eh_host_reset_handler = advansys_reset,
        .bios_param = advansys_biosparam,
        .slave_configure = advansys_slave_configure,
        /*
@@ -11240,7 +10810,7 @@ static struct scsi_host_template advansys_template = {
         * must be set. The flag will be cleared in advansys_board_found
         * for non-ISA adapters.
         */
-       .unchecked_isa_dma = 1,
+       .unchecked_isa_dma = true,
        /*
         * All adapters controlled by this driver are capable of large
         * scatter-gather lists. According to the mid-level SCSI documentation
@@ -11249,26 +10819,25 @@ static struct scsi_host_template advansys_template = {
         * by enabling clustering, I/O throughput increases as well.
         */
        .use_clustering = ENABLE_CLUSTERING,
+       .use_blk_tags = 1,
 };
 
 static int advansys_wide_init_chip(struct Scsi_Host *shost)
 {
        struct asc_board *board = shost_priv(shost);
        struct adv_dvc_var *adv_dvc = &board->dvc_var.adv_dvc_var;
-       int req_cnt = 0;
-       adv_req_t *reqp = NULL;
-       int sg_cnt = 0;
-       adv_sgblk_t *sgp;
+       size_t sgblk_pool_size;
        int warn_code, err_code;
 
        /*
         * Allocate buffer carrier structures. The total size
-        * is about 4 KB, so allocate all at once.
+        * is about 8 KB, so allocate all at once.
         */
-       adv_dvc->carrier_buf = kmalloc(ADV_CARRIER_BUFSIZE, GFP_KERNEL);
-       ASC_DBG(1, "carrier_buf 0x%p\n", adv_dvc->carrier_buf);
+       adv_dvc->carrier = dma_alloc_coherent(board->dev,
+               ADV_CARRIER_BUFSIZE, &adv_dvc->carrier_addr, GFP_KERNEL);
+       ASC_DBG(1, "carrier 0x%p\n", adv_dvc->carrier);
 
-       if (!adv_dvc->carrier_buf)
+       if (!adv_dvc->carrier)
                goto kmalloc_failed;
 
        /*
@@ -11276,54 +10845,34 @@ static int advansys_wide_init_chip(struct Scsi_Host *shost)
         * board. The total size is about 16 KB, so allocate all at once.
         * If the allocation fails decrement and try again.
         */
-       for (req_cnt = adv_dvc->max_host_qng; req_cnt > 0; req_cnt--) {
-               reqp = kmalloc(sizeof(adv_req_t) * req_cnt, GFP_KERNEL);
-
-               ASC_DBG(1, "reqp 0x%p, req_cnt %d, bytes %lu\n", reqp, req_cnt,
-                        (ulong)sizeof(adv_req_t) * req_cnt);
-
-               if (reqp)
-                       break;
+       board->adv_reqp_size = adv_dvc->max_host_qng * sizeof(adv_req_t);
+       if (board->adv_reqp_size & 0x1f) {
+               ASC_DBG(1, "unaligned reqp %lu bytes\n", sizeof(adv_req_t));
+               board->adv_reqp_size = ADV_32BALIGN(board->adv_reqp_size);
        }
+       board->adv_reqp = dma_alloc_coherent(board->dev, board->adv_reqp_size,
+               &board->adv_reqp_addr, GFP_KERNEL);
 
-       if (!reqp)
+       if (!board->adv_reqp)
                goto kmalloc_failed;
 
-       adv_dvc->orig_reqp = reqp;
+       ASC_DBG(1, "reqp 0x%p, req_cnt %d, bytes %lu\n", board->adv_reqp,
+               adv_dvc->max_host_qng, board->adv_reqp_size);
 
        /*
         * Allocate up to ADV_TOT_SG_BLOCK request structures for
         * the Wide board. Each structure is about 136 bytes.
         */
-       board->adv_sgblkp = NULL;
-       for (sg_cnt = 0; sg_cnt < ADV_TOT_SG_BLOCK; sg_cnt++) {
-               sgp = kmalloc(sizeof(adv_sgblk_t), GFP_KERNEL);
+       sgblk_pool_size = sizeof(adv_sgblk_t) * ADV_TOT_SG_BLOCK;
+       board->adv_sgblk_pool = dma_pool_create("adv_sgblk", board->dev,
+                                               sgblk_pool_size, 32, 0);
 
-               if (!sgp)
-                       break;
-
-               sgp->next_sgblkp = board->adv_sgblkp;
-               board->adv_sgblkp = sgp;
-
-       }
-
-       ASC_DBG(1, "sg_cnt %d * %lu = %lu bytes\n", sg_cnt, sizeof(adv_sgblk_t),
-                sizeof(adv_sgblk_t) * sg_cnt);
+       ASC_DBG(1, "sg_cnt %d * %lu = %lu bytes\n", ADV_TOT_SG_BLOCK,
+               sizeof(adv_sgblk_t), sgblk_pool_size);
 
-       if (!board->adv_sgblkp)
+       if (!board->adv_sgblk_pool)
                goto kmalloc_failed;
 
-       /*
-        * Point 'adv_reqp' to the request structures and
-        * link them together.
-        */
-       req_cnt--;
-       reqp[req_cnt].next_reqp = NULL;
-       for (; req_cnt > 0; req_cnt--) {
-               reqp[req_cnt - 1].next_reqp = &reqp[req_cnt];
-       }
-       board->adv_reqp = &reqp[0];
-
        if (adv_dvc->chip_type == ADV_CHIP_ASC3550) {
                ASC_DBG(2, "AdvInitAsc3550Driver()\n");
                warn_code = AdvInitAsc3550Driver(adv_dvc);
@@ -11353,14 +10902,20 @@ static int advansys_wide_init_chip(struct Scsi_Host *shost)
 static void advansys_wide_free_mem(struct asc_board *board)
 {
        struct adv_dvc_var *adv_dvc = &board->dvc_var.adv_dvc_var;
-       kfree(adv_dvc->carrier_buf);
-       adv_dvc->carrier_buf = NULL;
-       kfree(adv_dvc->orig_reqp);
-       adv_dvc->orig_reqp = board->adv_reqp = NULL;
-       while (board->adv_sgblkp) {
-               adv_sgblk_t *sgp = board->adv_sgblkp;
-               board->adv_sgblkp = sgp->next_sgblkp;
-               kfree(sgp);
+
+       if (adv_dvc->carrier) {
+               dma_free_coherent(board->dev, ADV_CARRIER_BUFSIZE,
+                                 adv_dvc->carrier, adv_dvc->carrier_addr);
+               adv_dvc->carrier = NULL;
+       }
+       if (board->adv_reqp) {
+               dma_free_coherent(board->dev, board->adv_reqp_size,
+                                 board->adv_reqp, board->adv_reqp_addr);
+               board->adv_reqp = NULL;
+       }
+       if (board->adv_sgblk_pool) {
+               dma_pool_destroy(board->adv_sgblk_pool);
+               board->adv_sgblk_pool = NULL;
        }
 }
 
@@ -11431,28 +10986,28 @@ static int advansys_board_found(struct Scsi_Host *shost, unsigned int iop,
                switch (asc_dvc_varp->bus_type) {
 #ifdef CONFIG_ISA
                case ASC_IS_ISA:
-                       shost->unchecked_isa_dma = TRUE;
+                       shost->unchecked_isa_dma = true;
                        share_irq = 0;
                        break;
                case ASC_IS_VL:
-                       shost->unchecked_isa_dma = FALSE;
+                       shost->unchecked_isa_dma = false;
                        share_irq = 0;
                        break;
                case ASC_IS_EISA:
-                       shost->unchecked_isa_dma = FALSE;
+                       shost->unchecked_isa_dma = false;
                        share_irq = IRQF_SHARED;
                        break;
 #endif /* CONFIG_ISA */
 #ifdef CONFIG_PCI
                case ASC_IS_PCI:
-                       shost->unchecked_isa_dma = FALSE;
+                       shost->unchecked_isa_dma = false;
                        share_irq = IRQF_SHARED;
                        break;
 #endif /* CONFIG_PCI */
                default:
                        shost_printk(KERN_ERR, shost, "unknown adapter type: "
                                        "%d\n", asc_dvc_varp->bus_type);
-                       shost->unchecked_isa_dma = TRUE;
+                       shost->unchecked_isa_dma = false;
                        share_irq = 0;
                        break;
                }
@@ -11471,7 +11026,7 @@ static int advansys_board_found(struct Scsi_Host *shost, unsigned int iop,
                 * For Wide boards set PCI information before calling
                 * AdvInitGetConfig().
                 */
-               shost->unchecked_isa_dma = FALSE;
+               shost->unchecked_isa_dma = false;
                share_irq = IRQF_SHARED;
                ASC_DBG(2, "AdvInitGetConfig()\n");
 
@@ -11656,24 +11211,11 @@ static int advansys_board_found(struct Scsi_Host *shost, unsigned int iop,
                /* Set maximum number of queues the adapter can handle. */
                shost->can_queue = adv_dvc_varp->max_host_qng;
        }
-
-       /*
-        * Following v1.3.89, 'cmd_per_lun' is no longer needed
-        * and should be set to zero.
-        *
-        * But because of a bug introduced in v1.3.89 if the driver is
-        * compiled as a module and 'cmd_per_lun' is zero, the Mid-Level
-        * SCSI function 'allocate_device' will panic. To allow the driver
-        * to work as a module in these kernels set 'cmd_per_lun' to 1.
-        *
-        * Note: This is wrong.  cmd_per_lun should be set to the depth
-        * you want on untagged devices always.
-        #ifdef MODULE
-        */
-       shost->cmd_per_lun = 1;
-/* #else
-            shost->cmd_per_lun = 0;
-#endif */
+       ret = scsi_init_shared_tag_map(shost, shost->can_queue);
+       if (ret) {
+               shost_printk(KERN_ERR, shost, "init tag map failed\n");
+               goto err_free_dma;
+       }
 
        /*
         * Set the maximum number of scatter-gather elements the
@@ -11844,7 +11386,9 @@ static int advansys_board_found(struct Scsi_Host *shost, unsigned int iop,
  err_unmap:
        if (boardp->ioremap_addr)
                iounmap(boardp->ioremap_addr);
+#ifdef CONFIG_PCI
  err_shost:
+#endif
        return ret;
 }
 
@@ -11927,6 +11471,7 @@ static int advansys_isa_probe(struct device *dev, unsigned int id)
        board = shost_priv(shost);
        board->irq = advansys_isa_irq_no(iop_base);
        board->dev = dev;
+       board->shost = shost;
 
        err = advansys_board_found(shost, iop_base, ASC_IS_ISA);
        if (err)
@@ -12009,6 +11554,7 @@ static int advansys_vlb_probe(struct device *dev, unsigned int id)
        board = shost_priv(shost);
        board->irq = advansys_vlb_irq_no(iop_base);
        board->dev = dev;
+       board->shost = shost;
 
        err = advansys_board_found(shost, iop_base, ASC_IS_VL);
        if (err)
@@ -12116,6 +11662,7 @@ static int advansys_eisa_probe(struct device *dev)
                board = shost_priv(shost);
                board->irq = irq;
                board->dev = dev;
+               board->shost = shost;
 
                err = advansys_board_found(shost, ioport, ASC_IS_EISA);
                if (!err) {
@@ -12232,6 +11779,7 @@ static int advansys_pci_probe(struct pci_dev *pdev,
        board = shost_priv(shost);
        board->irq = pdev->irq;
        board->dev = &pdev->dev;
+       board->shost = shost;
 
        if (pdev->device == PCI_DEVICE_ID_ASP_ABP940UW ||
            pdev->device == PCI_DEVICE_ID_38C0800_REV1 ||
index e31c460a1335527e74816945dcb6ac9e35b6125b..f44d0487236e34d20622ff9840af68f5170b537c 100644 (file)
@@ -2922,7 +2922,6 @@ static struct scsi_host_template aha152x_driver_template = {
        .can_queue                      = 1,
        .this_id                        = 7,
        .sg_tablesize                   = SG_ALL,
-       .cmd_per_lun                    = 1,
        .use_clustering                 = DISABLE_CLUSTERING,
        .slave_alloc                    = aha152x_adjust_queue,
 };
index b95d2779f4679cba20908b86a92502b32ec33a06..5b8b2937a3fea85e6d69f02526158c659a182584 100644 (file)
@@ -950,7 +950,6 @@ static struct scsi_host_template driver_template = {
        .can_queue              = AHA1542_MAILBOXES, 
        .this_id                = 7,
        .sg_tablesize           = 16,
-       .cmd_per_lun            = 1,
        .unchecked_isa_dma      = 1, 
        .use_clustering         = ENABLE_CLUSTERING,
 };
index 31ace4bef8fe98492d47a7134f1192b2d17b6647..bad35ffc015d0e0d1364eb4a72e7eb5ccf284b8d 100644 (file)
@@ -544,7 +544,6 @@ static struct scsi_host_template aha1740_template = {
        .can_queue        = AHA1740_ECBS,
        .this_id          = 7,
        .sg_tablesize     = AHA1740_SCATTER,
-       .cmd_per_lun      = AHA1740_CMDLUN,
        .use_clustering   = ENABLE_CLUSTERING,
        .eh_abort_handler = aha1740_eh_abort_handler,
 };
index af23fd6bd7952575aedd7feacb1bf8392a1261ed..b0c5603461ca252d254f6deff18b9e45eba8c479 100644 (file)
@@ -149,6 +149,5 @@ struct ecb {                        /* Enhanced Control Block 6.1 */
 
 #define AHA1740_ECBS 32
 #define AHA1740_SCATTER 16
-#define AHA1740_CMDLUN 1
 
 #endif
index 02a2512b76a8ee540e0454f5384f37afbcf20dde..4b135cca42a1282c08c43791fab4767374f6b148 100644 (file)
@@ -65,7 +65,6 @@ static struct scsi_host_template aic94xx_sht = {
        .change_queue_depth     = sas_change_queue_depth,
        .bios_param             = sas_bios_param,
        .can_queue              = 1,
-       .cmd_per_lun            = 1,
        .this_id                = -1,
        .sg_tablesize           = SG_ALL,
        .max_sectors            = SCSI_DEFAULT_MAX_SECTORS,
index 32d23212de48be5bbee9d6f5989f05f2fe0cf9be..3110736fd337096116e558334fc05660c8fd45d3 100644 (file)
@@ -245,7 +245,6 @@ static struct scsi_host_template arxescsi_template = {
        .can_queue                      = 0,
        .this_id                        = 7,
        .sg_tablesize                   = SG_ALL,
-       .cmd_per_lun                    = 1,
        .use_clustering                 = DISABLE_CLUSTERING,
        .proc_name                      = "arxescsi",
 };
index abc66f5263ec08988338f23b22d0e432529dd57e..faa1bee07c8ac0b08dfc9fa6c6a8b2a6bd9d8493 100644 (file)
@@ -367,7 +367,6 @@ static struct scsi_host_template cumanascsi2_template = {
        .this_id                        = 7,
        .sg_tablesize                   = SCSI_MAX_SG_CHAIN_SEGMENTS,
        .dma_boundary                   = IOMD_DMA_BOUNDARY,
-       .cmd_per_lun                    = 1,
        .use_clustering                 = DISABLE_CLUSTERING,
        .proc_name                      = "cumanascsi2",
 };
index 5bf3c0d134b47e3ed0b309e33b60192490ba820a..a8ad6880dd9145734a15dbd6ff0d9bd73d4b4213 100644 (file)
@@ -486,7 +486,6 @@ static struct scsi_host_template eesox_template = {
        .this_id                        = 7,
        .sg_tablesize                   = SCSI_MAX_SG_CHAIN_SEGMENTS,
        .dma_boundary                   = IOMD_DMA_BOUNDARY,
-       .cmd_per_lun                    = 1,
        .use_clustering                 = DISABLE_CLUSTERING,
        .proc_name                      = "eesox",
 };
index 0836433e3a2d5cc761edfb9f9bc2b39a3849459e..05301bc752eee7e4bd2580e4ba58717bb9622fb2 100644 (file)
@@ -3158,7 +3158,6 @@ static struct scsi_host_template atp870u_template = {
      .can_queue                = qcnt                  /* can_queue */,
      .this_id                  = 7                     /* SCSI ID */,
      .sg_tablesize             = ATP870U_SCATTER       /*SG_ALL*/ /*SG_NONE*/,
-     .cmd_per_lun              = ATP870U_CMDLUN                /* commands per lun */,
      .use_clustering           = ENABLE_CLUSTERING,
      .max_sectors              = ATP870U_MAX_SECTORS,
 };
index 62bae64a01c15d687afa5f043b78a117c7d84241..5cf62566ad4282422f4288605d8f6ac7a2469859 100644 (file)
@@ -10,7 +10,6 @@
 #define MAX_SENSE      14
 #define qcnt           32
 #define ATP870U_SCATTER        128
-#define ATP870U_CMDLUN         1
 
 #define MAX_ADAPTER    8
 #define MAX_SCSI_ID    16
index 447cf7ce606ec6e3cf8b0540ba4f455c41aec2d5..185391a64d4ba50c4d925ef48c6e266e927a2e10 100644 (file)
@@ -452,6 +452,7 @@ void beiscsi_async_link_state_process(struct beiscsi_hba *phba,
                    ((evt->port_link_status & ASYNC_EVENT_LOGICAL) &&
                     (evt->port_fault == BEISCSI_PHY_LINK_FAULT_NONE))) {
                phba->state = BE_ADAPTER_LINK_UP | BE_ADAPTER_CHECK_BOOT;
+               phba->get_boot = BE_GET_BOOT_RETRIES;
 
                beiscsi_log(phba, KERN_ERR,
                            BEISCSI_LOG_CONFIG | BEISCSI_LOG_INIT,
@@ -480,6 +481,7 @@ int beiscsi_process_mcc(struct beiscsi_hba *phba)
                                case ASYNC_EVENT_NEW_ISCSI_CONN:
                                case ASYNC_EVENT_NEW_TCP_CONN:
                                        phba->state |= BE_ADAPTER_CHECK_BOOT;
+                                       phba->get_boot = BE_GET_BOOT_RETRIES;
                                        beiscsi_log(phba, KERN_ERR,
                                                    BEISCSI_LOG_CONFIG |
                                                    BEISCSI_LOG_MBOX,
@@ -488,6 +490,8 @@ int beiscsi_process_mcc(struct beiscsi_hba *phba)
                                                    compl->flags);
                                        break;
                                default:
+                                       phba->state |= BE_ADAPTER_CHECK_BOOT;
+                                       phba->get_boot = BE_GET_BOOT_RETRIES;
                                        beiscsi_log(phba, KERN_ERR,
                                                    BEISCSI_LOG_CONFIG |
                                                    BEISCSI_LOG_MBOX,
index f11d325fe6963f191424b52d6f0c16d69ddef671..cdfbc5c19cf4536b1540a20082f5b73535167fbe 100644 (file)
@@ -304,6 +304,17 @@ struct mgmt_auth_method_format {
        struct  mgmt_chap_format chap;
 } __packed;
 
+struct be_cmd_req_logout_fw_sess {
+       struct be_cmd_req_hdr hdr;      /* dw[4] */
+       uint32_t session_handle;
+} __packed;
+
+struct be_cmd_resp_logout_fw_sess {
+       struct be_cmd_resp_hdr hdr;     /* dw[4] */
+#define BEISCSI_MGMT_SESSION_CLOSE 0x20
+       uint32_t session_status;
+} __packed;
+
 struct mgmt_conn_login_options {
        u8 flags;
        u8 header_digest;
@@ -1136,6 +1147,7 @@ struct be_cmd_get_all_if_id_req {
 #define OPCODE_ISCSI_INI_CFG_GET_HBA_NAME      6
 #define OPCODE_ISCSI_INI_CFG_SET_HBA_NAME      7
 #define OPCODE_ISCSI_INI_SESSION_GET_A_SESSION  14
+#define OPCODE_ISCSI_INI_SESSION_LOGOUT_TARGET  24
 #define OPCODE_ISCSI_INI_DRIVER_REOPEN_ALL_SESSIONS 36
 #define OPCODE_ISCSI_INI_DRIVER_OFFLOAD_SESSION 41
 #define OPCODE_ISCSI_INI_DRIVER_INVALIDATE_CONNECTION 42
index 1f74760ce86cb27db2308f4dbe1d9ba25f10bcaa..7a6dbfbccec90dd99cb766579b9472bce2d5a1e4 100644 (file)
@@ -668,14 +668,20 @@ static int beiscsi_enable_pci(struct pci_dev *pcidev)
                return ret;
        }
 
+       ret = pci_request_regions(pcidev, DRV_NAME);
+       if (ret) {
+               dev_err(&pcidev->dev,
+                               "beiscsi_enable_pci - request region failed\n");
+               goto pci_dev_disable;
+       }
+
        pci_set_master(pcidev);
        ret = pci_set_dma_mask(pcidev, DMA_BIT_MASK(64));
        if (ret) {
                ret = pci_set_dma_mask(pcidev, DMA_BIT_MASK(32));
                if (ret) {
                        dev_err(&pcidev->dev, "Could not set PCI DMA Mask\n");
-                       pci_disable_device(pcidev);
-                       return ret;
+                       goto pci_region_release;
                } else {
                        ret = pci_set_consistent_dma_mask(pcidev,
                                                          DMA_BIT_MASK(32));
@@ -684,11 +690,17 @@ static int beiscsi_enable_pci(struct pci_dev *pcidev)
                ret = pci_set_consistent_dma_mask(pcidev, DMA_BIT_MASK(64));
                if (ret) {
                        dev_err(&pcidev->dev, "Could not set PCI DMA Mask\n");
-                       pci_disable_device(pcidev);
-                       return ret;
+                       goto pci_region_release;
                }
        }
        return 0;
+
+pci_region_release:
+       pci_release_regions(pcidev);
+pci_dev_disable:
+       pci_disable_device(pcidev);
+
+       return ret;
 }
 
 static int be_ctrl_init(struct beiscsi_hba *phba, struct pci_dev *pdev)
@@ -1356,8 +1368,10 @@ be_complete_io(struct beiscsi_conn *beiscsi_conn,
        if (io_task->cmd_bhs->iscsi_hdr.flags & ISCSI_FLAG_CMD_READ)
                conn->rxdata_octets += resid;
 unmap:
-       scsi_dma_unmap(io_task->scsi_cmnd);
-       io_task->scsi_cmnd = NULL;
+       if (io_task->scsi_cmnd) {
+               scsi_dma_unmap(io_task->scsi_cmnd);
+               io_task->scsi_cmnd = NULL;
+       }
        iscsi_complete_scsi_task(task, exp_cmdsn, max_cmdsn);
 }
 
@@ -2037,11 +2051,16 @@ static void  beiscsi_process_mcc_isr(struct beiscsi_hba *phba)
                                /* Interpret compl as a async link evt */
                                beiscsi_async_link_state_process(phba,
                                (struct be_async_event_link_state *) mcc_compl);
-                       else
+                       else {
                                beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_MBOX,
                                            "BM_%d :  Unsupported Async Event, flags"
                                            " = 0x%08x\n",
                                            mcc_compl->flags);
+                               if (phba->state & BE_ADAPTER_LINK_UP) {
+                                       phba->state |= BE_ADAPTER_CHECK_BOOT;
+                                       phba->get_boot = BE_GET_BOOT_RETRIES;
+                               }
+                       }
                } else if (mcc_compl->flags & CQE_FLAGS_COMPLETED_MASK) {
                        be_mcc_compl_process_isr(&phba->ctrl, mcc_compl);
                        atomic_dec(&phba->ctrl.mcc_obj.q.used);
@@ -3678,14 +3697,16 @@ static void be_mcc_queues_destroy(struct beiscsi_hba *phba)
        struct be_ctrl_info *ctrl = &phba->ctrl;
 
        q = &phba->ctrl.mcc_obj.q;
-       if (q->created)
+       if (q->created) {
                beiscsi_cmd_q_destroy(ctrl, q, QTYPE_MCCQ);
-       be_queue_free(phba, q);
+               be_queue_free(phba, q);
+       }
 
        q = &phba->ctrl.mcc_obj.cq;
-       if (q->created)
+       if (q->created) {
                beiscsi_cmd_q_destroy(ctrl, q, QTYPE_CQ);
-       be_queue_free(phba, q);
+               be_queue_free(phba, q);
+       }
 }
 
 static void hwi_cleanup(struct beiscsi_hba *phba)
@@ -3729,8 +3750,10 @@ static void hwi_cleanup(struct beiscsi_hba *phba)
 
        for (i = 0; i < (phba->num_cpus); i++) {
                q = &phwi_context->be_cq[i];
-               if (q->created)
+               if (q->created) {
+                       be_queue_free(phba, q);
                        beiscsi_cmd_q_destroy(ctrl, q, QTYPE_CQ);
+               }
        }
 
        be_mcc_queues_destroy(phba);
@@ -3740,8 +3763,10 @@ static void hwi_cleanup(struct beiscsi_hba *phba)
                eq_for_mcc = 0;
        for (i = 0; i < (phba->num_cpus + eq_for_mcc); i++) {
                q = &phwi_context->be_eq[i].q;
-               if (q->created)
+               if (q->created) {
+                       be_queue_free(phba, q);
                        beiscsi_cmd_q_destroy(ctrl, q, QTYPE_EQ);
+               }
        }
        be_cmd_fw_uninit(ctrl);
 }
@@ -4328,8 +4353,14 @@ static int beiscsi_get_boot_info(struct beiscsi_hba *phba)
                beiscsi_log(phba, KERN_ERR,
                            BEISCSI_LOG_INIT | BEISCSI_LOG_CONFIG,
                            "BM_%d : No boot session\n");
+
+               if (ret == -ENXIO)
+                       phba->get_boot = 0;
+
+
                return ret;
        }
+       phba->get_boot = 0;
        nonemb_cmd.va = pci_zalloc_consistent(phba->ctrl.pdev,
                                              sizeof(*session_resp),
                                              &nonemb_cmd.dma);
@@ -4369,6 +4400,9 @@ static int beiscsi_get_boot_info(struct beiscsi_hba *phba)
 
        memcpy(&phba->boot_sess, &session_resp->session_info,
               sizeof(struct mgmt_session_info));
+
+        beiscsi_logout_fw_sess(phba,
+                               phba->boot_sess.session_handle);
        ret = 0;
 
 boot_freemem:
@@ -4580,11 +4614,13 @@ beiscsi_free_mgmt_task_handles(struct beiscsi_conn *beiscsi_conn,
                spin_unlock_bh(&phba->mgmt_sgl_lock);
        }
 
-       if (io_task->mtask_addr)
+       if (io_task->mtask_addr) {
                pci_unmap_single(phba->pcidev,
                                 io_task->mtask_addr,
                                 io_task->mtask_data_count,
                                 PCI_DMA_TODEVICE);
+               io_task->mtask_addr = 0;
+       }
 }
 
 /**
@@ -5264,6 +5300,7 @@ static void beiscsi_remove(struct pci_dev *pcidev)
        iscsi_host_free(phba->shost);
        pci_disable_pcie_error_reporting(pcidev);
        pci_set_drvdata(pcidev, NULL);
+       pci_release_regions(pcidev);
        pci_disable_device(pcidev);
 }
 
@@ -5374,8 +5411,14 @@ beiscsi_hw_health_check(struct work_struct *work)
        be_eqd_update(phba);
 
        if (phba->state & BE_ADAPTER_CHECK_BOOT) {
-               phba->state &= ~BE_ADAPTER_CHECK_BOOT;
-               be_check_boot_session(phba);
+               if ((phba->get_boot > 0) && (!phba->boot_kset)) {
+                       phba->get_boot--;
+                       if (!(phba->get_boot % BE_GET_BOOT_TO))
+                               be_check_boot_session(phba);
+               } else {
+                       phba->state &= ~BE_ADAPTER_CHECK_BOOT;
+                       phba->get_boot = 0;
+               }
        }
 
        beiscsi_ue_detect(phba);
@@ -5738,6 +5781,7 @@ hba_free:
        iscsi_host_free(phba->shost);
        pci_set_drvdata(pcidev, NULL);
 disable_pci:
+       pci_release_regions(pcidev);
        pci_disable_device(pcidev);
        return ret;
 }
index e70ea26bbc2b0fff8a82c540edd4aceab371da45..b8c0c7819cb1100f1a68f32fb2513e0b37f3fad9 100644 (file)
@@ -36,7 +36,7 @@
 #include <scsi/scsi_transport_iscsi.h>
 
 #define DRV_NAME               "be2iscsi"
-#define BUILD_STR              "10.4.114.0"
+#define BUILD_STR              "10.6.0.0"
 #define BE_NAME                        "Avago Technologies OneConnect" \
                                "Open-iSCSI Driver version" BUILD_STR
 #define DRV_DESC               BE_NAME " " "Driver"
 
 #define BEISCSI_CLEAN_UNLOAD   0x01
 #define BEISCSI_EEH_UNLOAD     0x02
+
+#define BE_GET_BOOT_RETRIES    45
+#define BE_GET_BOOT_TO         20
 /**
  * hardware needs the async PDU buffers to be posted in multiples of 8
  * So have atleast 8 of them by default
@@ -413,6 +416,7 @@ struct beiscsi_hba {
        } fw_config;
 
        unsigned int state;
+       int get_boot;
        bool fw_timeout;
        bool ue_detected;
        struct delayed_work beiscsi_hw_check_task;
index c2c4d6975fb7b22b2a4db35adf6ca69d2ea06547..ca4016f20e76decc0c3731a692fc8030cedf4577 100644 (file)
@@ -1707,3 +1707,72 @@ void beiscsi_offload_cxn_v2(struct beiscsi_offload_params *params,
                     (params->dw[offsetof(struct amap_beiscsi_offload_params,
                      exp_statsn) / 32] + 1));
 }
+
+/**
+ * beiscsi_logout_fw_sess()- Firmware Session Logout
+ * @phba: Device priv structure instance
+ * @fw_sess_handle: FW session handle
+ *
+ * Logout from the FW established sessions.
+ * returns
+ *  Success: 0
+ *  Failure: Non-Zero Value
+ *
+ */
+int beiscsi_logout_fw_sess(struct beiscsi_hba *phba,
+               uint32_t fw_sess_handle)
+{
+       struct be_ctrl_info *ctrl = &phba->ctrl;
+       struct be_mcc_wrb *wrb;
+       struct be_cmd_req_logout_fw_sess *req;
+       struct be_cmd_resp_logout_fw_sess *resp;
+       unsigned int tag;
+       int rc;
+
+       beiscsi_log(phba, KERN_INFO,
+                   BEISCSI_LOG_CONFIG | BEISCSI_LOG_MBOX,
+                   "BG_%d : In bescsi_logout_fwboot_sess\n");
+
+       spin_lock(&ctrl->mbox_lock);
+       tag = alloc_mcc_tag(phba);
+       if (!tag) {
+               spin_unlock(&ctrl->mbox_lock);
+               beiscsi_log(phba, KERN_INFO,
+                           BEISCSI_LOG_CONFIG | BEISCSI_LOG_MBOX,
+                           "BG_%d : MBX Tag Failure\n");
+               return -EINVAL;
+       }
+
+       wrb = wrb_from_mccq(phba);
+       req = embedded_payload(wrb);
+       wrb->tag0 |= tag;
+       be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0);
+       be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ISCSI_INI,
+                          OPCODE_ISCSI_INI_SESSION_LOGOUT_TARGET,
+                          sizeof(struct be_cmd_req_logout_fw_sess));
+
+       /* Set the session handle */
+       req->session_handle = fw_sess_handle;
+       be_mcc_notify(phba);
+       spin_unlock(&ctrl->mbox_lock);
+
+       rc = beiscsi_mccq_compl(phba, tag, &wrb, NULL);
+       if (rc) {
+               beiscsi_log(phba, KERN_ERR,
+                           BEISCSI_LOG_INIT | BEISCSI_LOG_CONFIG,
+                           "BG_%d : MBX CMD FW_SESSION_LOGOUT_TARGET Failed\n");
+               return -EBUSY;
+       }
+
+       resp = embedded_payload(wrb);
+       if (resp->session_status !=
+               BEISCSI_MGMT_SESSION_CLOSE) {
+               beiscsi_log(phba, KERN_ERR,
+                           BEISCSI_LOG_INIT | BEISCSI_LOG_CONFIG,
+                           "BG_%d : FW_SESSION_LOGOUT_TARGET resp : 0x%x\n",
+                           resp->session_status);
+               rc = -EINVAL;
+       }
+
+       return rc;
+}
index 9356b9a86b66fcc7d640361aab6670d64315244f..b58a7decbd94aa437ab0c5c6f99b8287ccecffac 100644 (file)
@@ -338,4 +338,7 @@ void beiscsi_ue_detect(struct beiscsi_hba *phba);
 int be_cmd_modify_eq_delay(struct beiscsi_hba *phba,
                         struct be_set_eqd *, int num);
 
+int beiscsi_logout_fw_sess(struct beiscsi_hba *phba,
+                           uint32_t fw_sess_handle);
+
 #endif
index e53078d033099ce8a3942e1151e9b9044fea0c8d..72894378ffcf1c911a9b8ee9b55ee0cfc289a4e4 100644 (file)
@@ -1173,8 +1173,10 @@ static void bnx2i_cleanup_task(struct iscsi_task *task)
                bnx2i_send_cmd_cleanup_req(hba, task->dd_data);
 
                spin_unlock_bh(&conn->session->back_lock);
+               spin_unlock_bh(&conn->session->frwd_lock);
                wait_for_completion_timeout(&bnx2i_conn->cmd_cleanup_cmpl,
                                msecs_to_jiffies(ISCSI_CMD_CLEANUP_TIMEOUT));
+               spin_lock_bh(&conn->session->frwd_lock);
                spin_lock_bh(&conn->session->back_lock);
        }
        bnx2i_iscsi_unmap_sg_list(task->dd_data);
@@ -2093,7 +2095,8 @@ int bnx2i_hw_ep_disconnect(struct bnx2i_endpoint *bnx2i_ep)
        else
                /* wait for option-2 conn teardown */
                wait_event_interruptible(bnx2i_ep->ofld_wait,
-                                bnx2i_ep->state != EP_STATE_DISCONN_START);
+                               ((bnx2i_ep->state != EP_STATE_DISCONN_START)
+                               && (bnx2i_ep->state != EP_STATE_TCP_FIN_RCVD)));
 
        if (signal_pending(current))
                flush_signals(current);
index 2e66f34ebb79c9caba0b40027e412b17d85dcce6..622bdabc88941430f18ed65b0d70fd9fb0478b9b 100644 (file)
@@ -3928,6 +3928,7 @@ csio_hw_init(struct csio_hw *hw)
 
                evt_entry = kzalloc(sizeof(struct csio_evt_msg), GFP_KERNEL);
                if (!evt_entry) {
+                       rv = -ENOMEM;
                        csio_err(hw, "Failed to initialize eventq");
                        goto err_evtq_cleanup;
                }
index 3db4c63978c55c1e7e623d199b6c623c6cea3597..0e2bee937fe81b345abee5ca20beec65f098f175 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * cxgb3i_offload.c: Chelsio S3xx iscsi offloaded tcp connection management
  *
- * Copyright (C) 2003-2008 Chelsio Communications.  All rights reserved.
+ * Copyright (C) 2003-2015 Chelsio Communications.  All rights reserved.
  *
  * This program is distributed in the hope that it will be useful, but WITHOUT
  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
@@ -32,8 +32,8 @@ static unsigned int dbg_level;
 
 #define DRV_MODULE_NAME         "cxgb3i"
 #define DRV_MODULE_DESC         "Chelsio T3 iSCSI Driver"
-#define DRV_MODULE_VERSION     "2.0.0"
-#define DRV_MODULE_RELDATE     "Jun. 2010"
+#define DRV_MODULE_VERSION     "2.0.1-ko"
+#define DRV_MODULE_RELDATE     "Apr. 2015"
 
 static char version[] =
        DRV_MODULE_DESC " " DRV_MODULE_NAME
@@ -156,7 +156,7 @@ static int push_tx_frames(struct cxgbi_sock *csk, int req_completion);
 static void send_act_open_req(struct cxgbi_sock *csk, struct sk_buff *skb,
                              const struct l2t_entry *e)
 {
-       unsigned int wscale = cxgbi_sock_compute_wscale(cxgb3i_rcv_win);
+       unsigned int wscale = cxgbi_sock_compute_wscale(csk->rcv_win);
        struct cpl_act_open_req *req = (struct cpl_act_open_req *)skb->head;
 
        skb->priority = CPL_PRIORITY_SETUP;
@@ -172,7 +172,7 @@ static void send_act_open_req(struct cxgbi_sock *csk, struct sk_buff *skb,
                        V_WND_SCALE(wscale) | V_MSS_IDX(csk->mss_idx) |
                        V_L2T_IDX(e->idx) | V_TX_CHANNEL(e->smt_idx));
        req->opt0l = htonl(V_ULP_MODE(ULP2_MODE_ISCSI) |
-                       V_RCV_BUFSIZ(cxgb3i_rcv_win>>10));
+                       V_RCV_BUFSIZ(csk->rcv_win >> 10));
 
        log_debug(1 << CXGBI_DBG_TOE | 1 << CXGBI_DBG_SOCK,
                "csk 0x%p,%u,0x%lx,%u, %pI4:%u-%pI4:%u, %u,%u,%u.\n",
@@ -369,7 +369,7 @@ static inline void make_tx_data_wr(struct cxgbi_sock *csk, struct sk_buff *skb,
                req->flags |= htonl(V_TX_ACK_PAGES(2) | F_TX_INIT |
                                    V_TX_CPU_IDX(csk->rss_qid));
                /* sendbuffer is in units of 32KB. */
-               req->param |= htonl(V_TX_SNDBUF(cxgb3i_snd_win >> 15));
+               req->param |= htonl(V_TX_SNDBUF(csk->snd_win >> 15));
                cxgbi_sock_set_flag(csk, CTPF_TX_DATA_SENT);
        }
 }
@@ -503,8 +503,8 @@ static int do_act_establish(struct t3cdev *tdev, struct sk_buff *skb, void *ctx)
                        csk, csk->state, csk->flags, csk->tid);
 
        csk->copied_seq = csk->rcv_wup = csk->rcv_nxt = rcv_isn;
-       if (cxgb3i_rcv_win > (M_RCV_BUFSIZ << 10))
-               csk->rcv_wup -= cxgb3i_rcv_win - (M_RCV_BUFSIZ << 10);
+       if (csk->rcv_win > (M_RCV_BUFSIZ << 10))
+               csk->rcv_wup -= csk->rcv_win - (M_RCV_BUFSIZ << 10);
 
        cxgbi_sock_established(csk, ntohl(req->snd_isn), ntohs(req->tcp_opt));
 
@@ -988,6 +988,8 @@ static int init_act_open(struct cxgbi_sock *csk)
                goto rel_resource;
        skb->sk = (struct sock *)csk;
        set_arp_failure_handler(skb, act_open_arp_failure);
+       csk->snd_win = cxgb3i_snd_win;
+       csk->rcv_win = cxgb3i_rcv_win;
 
        csk->wr_max_cred = csk->wr_cred = T3C_DATA(t3dev)->max_wrs - 1;
        csk->wr_una_cred = 0;
@@ -1320,8 +1322,6 @@ static void cxgb3i_dev_open(struct t3cdev *t3dev)
        cdev->nports = adapter->params.nports;
        cdev->mtus = adapter->params.mtus;
        cdev->nmtus = NMTUS;
-       cdev->snd_win = cxgb3i_snd_win;
-       cdev->rcv_win = cxgb3i_rcv_win;
        cdev->rx_credit_thres = cxgb3i_rx_credit_thres;
        cdev->skb_tx_rsvd = CXGB3I_TX_HEADER_LEN;
        cdev->skb_rx_extra = sizeof(struct cpl_iscsi_hdr_norss);
index 20593fd69d8f2061285384d01c3387be923caedb..b0430c9359e7d75fb79e7cb57abaa4dbb764f03e 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * cxgb3i.h: Chelsio S3xx iSCSI driver.
  *
- * Copyright (c) 2008 Chelsio Communications, Inc.
+ * Copyright (c) 2008-2015 Chelsio Communications, 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
index dd00e5fe4a5e75b2d5a8599af63d8d18aa3fff02..de6feb8964c912e8f844010f9e6af32760481826 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * cxgb4i.c: Chelsio T4 iSCSI driver.
  *
- * Copyright (c) 2010 Chelsio Communications, Inc.
+ * Copyright (c) 2010-2015 Chelsio Communications, 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
@@ -36,11 +36,12 @@ static unsigned int dbg_level;
 
 #define        DRV_MODULE_NAME         "cxgb4i"
 #define DRV_MODULE_DESC                "Chelsio T4/T5 iSCSI Driver"
-#define        DRV_MODULE_VERSION      "0.9.4"
+#define        DRV_MODULE_VERSION      "0.9.5-ko"
+#define DRV_MODULE_RELDATE     "Apr. 2015"
 
 static char version[] =
        DRV_MODULE_DESC " " DRV_MODULE_NAME
-       " v" DRV_MODULE_VERSION "\n";
+       " v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n";
 
 MODULE_AUTHOR("Chelsio Communications, Inc.");
 MODULE_DESCRIPTION(DRV_MODULE_DESC);
@@ -50,11 +51,13 @@ MODULE_LICENSE("GPL");
 module_param(dbg_level, uint, 0644);
 MODULE_PARM_DESC(dbg_level, "Debug flag (default=0)");
 
-static int cxgb4i_rcv_win = 256 * 1024;
+#define CXGB4I_DEFAULT_10G_RCV_WIN (256 * 1024)
+static int cxgb4i_rcv_win = -1;
 module_param(cxgb4i_rcv_win, int, 0644);
 MODULE_PARM_DESC(cxgb4i_rcv_win, "TCP reveive window in bytes");
 
-static int cxgb4i_snd_win = 128 * 1024;
+#define CXGB4I_DEFAULT_10G_SND_WIN (128 * 1024)
+static int cxgb4i_snd_win = -1;
 module_param(cxgb4i_snd_win, int, 0644);
 MODULE_PARM_DESC(cxgb4i_snd_win, "TCP send window in bytes");
 
@@ -196,10 +199,10 @@ static void send_act_open_req(struct cxgbi_sock *csk, struct sk_buff *skb,
                TX_CHAN_V(csk->tx_chan) |
                SMAC_SEL_V(csk->smac_idx) |
                ULP_MODE_V(ULP_MODE_ISCSI) |
-               RCV_BUFSIZ_V(cxgb4i_rcv_win >> 10);
+               RCV_BUFSIZ_V(csk->rcv_win >> 10);
+
        opt2 = RX_CHANNEL_V(0) |
                RSS_QUEUE_VALID_F |
-               (RX_FC_DISABLE_F) |
                RSS_QUEUE_V(csk->rss_qid);
 
        if (is_t4(lldi->adapter_type)) {
@@ -228,6 +231,7 @@ static void send_act_open_req(struct cxgbi_sock *csk, struct sk_buff *skb,
        } else {
                struct cpl_t5_act_open_req *req =
                                (struct cpl_t5_act_open_req *)skb->head;
+               u32 isn = (prandom_u32() & ~7UL) - 1;
 
                INIT_TP_WR(req, 0);
                OPCODE_TID(req) = cpu_to_be32(MK_OPCODE_TID(CPL_ACT_OPEN_REQ,
@@ -241,7 +245,10 @@ static void send_act_open_req(struct cxgbi_sock *csk, struct sk_buff *skb,
                                cxgb4_select_ntuple(
                                        csk->cdev->ports[csk->port_id],
                                        csk->l2t)));
-               opt2 |= 1 << 31;
+               req->rsvd = cpu_to_be32(isn);
+               opt2 |= T5_ISS_VALID;
+               opt2 |= T5_OPT_2_VALID_F;
+
                req->opt2 = cpu_to_be32(opt2);
 
                log_debug(1 << CXGBI_DBG_TOE | 1 << CXGBI_DBG_SOCK,
@@ -279,7 +286,7 @@ static void send_act_open_req6(struct cxgbi_sock *csk, struct sk_buff *skb,
                TX_CHAN_V(csk->tx_chan) |
                SMAC_SEL_V(csk->smac_idx) |
                ULP_MODE_V(ULP_MODE_ISCSI) |
-               RCV_BUFSIZ_V(cxgb4i_rcv_win >> 10);
+               RCV_BUFSIZ_V(csk->rcv_win >> 10);
 
        opt2 = RX_CHANNEL_V(0) |
                RSS_QUEUE_VALID_F |
@@ -544,7 +551,7 @@ static inline int send_tx_flowc_wr(struct cxgbi_sock *csk)
        flowc->mnemval[5].mnemonic = FW_FLOWC_MNEM_RCVNXT;
        flowc->mnemval[5].val = htonl(csk->rcv_nxt);
        flowc->mnemval[6].mnemonic = FW_FLOWC_MNEM_SNDBUF;
-       flowc->mnemval[6].val = htonl(cxgb4i_snd_win);
+       flowc->mnemval[6].val = htonl(csk->snd_win);
        flowc->mnemval[7].mnemonic = FW_FLOWC_MNEM_MSS;
        flowc->mnemval[7].val = htonl(csk->advmss);
        flowc->mnemval[8].mnemonic = 0;
@@ -557,7 +564,7 @@ static inline int send_tx_flowc_wr(struct cxgbi_sock *csk)
        log_debug(1 << CXGBI_DBG_TOE | 1 << CXGBI_DBG_SOCK,
                "csk 0x%p, tid 0x%x, %u,%u,%u,%u,%u,%u,%u.\n",
                csk, csk->tid, 0, csk->tx_chan, csk->rss_qid,
-               csk->snd_nxt, csk->rcv_nxt, cxgb4i_snd_win,
+               csk->snd_nxt, csk->rcv_nxt, csk->snd_win,
                csk->advmss);
 
        cxgb4_ofld_send(csk->cdev->ports[csk->port_id], skb);
@@ -750,8 +757,8 @@ static void do_act_establish(struct cxgbi_device *cdev, struct sk_buff *skb)
         * Causes the first RX_DATA_ACK to supply any Rx credits we couldn't
         * pass through opt0.
         */
-       if (cxgb4i_rcv_win > (RCV_BUFSIZ_MASK << 10))
-               csk->rcv_wup -= cxgb4i_rcv_win - (RCV_BUFSIZ_MASK << 10);
+       if (csk->rcv_win > (RCV_BUFSIZ_MASK << 10))
+               csk->rcv_wup -= csk->rcv_win - (RCV_BUFSIZ_MASK << 10);
 
        csk->advmss = lldi->mtus[TCPOPT_MSS_G(tcp_opt)] - 40;
        if (TCPOPT_TSTAMP_G(tcp_opt))
@@ -1367,6 +1374,8 @@ static int init_act_open(struct cxgbi_sock *csk)
        unsigned int step;
        unsigned int size, size6;
        int t4 = is_t4(lldi->adapter_type);
+       unsigned int linkspeed;
+       unsigned int rcv_winf, snd_winf;
 
        log_debug(1 << CXGBI_DBG_TOE | 1 << CXGBI_DBG_SOCK,
                "csk 0x%p,%u,0x%lx,%u.\n",
@@ -1440,6 +1449,21 @@ static int init_act_open(struct cxgbi_sock *csk)
        csk->txq_idx = cxgb4_port_idx(ndev) * step;
        step = lldi->nrxq / lldi->nchan;
        csk->rss_qid = lldi->rxq_ids[cxgb4_port_idx(ndev) * step];
+       linkspeed = ((struct port_info *)netdev_priv(ndev))->link_cfg.speed;
+       csk->snd_win = cxgb4i_snd_win;
+       csk->rcv_win = cxgb4i_rcv_win;
+       if (cxgb4i_rcv_win <= 0) {
+               csk->rcv_win = CXGB4I_DEFAULT_10G_RCV_WIN;
+               rcv_winf = linkspeed / SPEED_10000;
+               if (rcv_winf)
+                       csk->rcv_win *= rcv_winf;
+       }
+       if (cxgb4i_snd_win <= 0) {
+               csk->snd_win = CXGB4I_DEFAULT_10G_SND_WIN;
+               snd_winf = linkspeed / SPEED_10000;
+               if (snd_winf)
+                       csk->snd_win *= snd_winf;
+       }
        csk->wr_cred = lldi->wr_cred -
                       DIV_ROUND_UP(sizeof(struct cpl_abort_req), 16);
        csk->wr_max_cred = csk->wr_cred;
@@ -1758,8 +1782,6 @@ static void *t4_uld_add(const struct cxgb4_lld_info *lldi)
        cdev->nports = lldi->nports;
        cdev->mtus = lldi->mtus;
        cdev->nmtus = NMTUS;
-       cdev->snd_win = cxgb4i_snd_win;
-       cdev->rcv_win = cxgb4i_rcv_win;
        cdev->rx_credit_thres = cxgb4i_rx_credit_thres;
        cdev->skb_tx_rsvd = CXGB4I_TX_HEADER_LEN;
        cdev->skb_rx_extra = sizeof(struct cpl_iscsi_hdr);
index 1096026ba241ad2bd41f0ee643d18f091982422d..22dd8d670e4a04d5dc16c4827e7e48e6346e5498 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * cxgb4i.h: Chelsio T4 iSCSI driver.
  *
- * Copyright (c) 2010 Chelsio Communications, Inc.
+ * Copyright (c) 2010-2015 Chelsio Communications, 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
@@ -23,6 +23,8 @@
 #define CXGB4I_TX_HEADER_LEN \
        (sizeof(struct fw_ofld_tx_data_wr) + sizeof(struct sge_opaque_hdr))
 
+#define T5_ISS_VALID           (1 << 18)
+
 struct ulptx_idata {
        __be32 cmd_more;
        __be32 len;
index eb58afcfb73b4c7bbff39a33da272ab9d52e1c39..1d42e4f88b96807408ba1cf12d4c7ae2724b304c 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * libcxgbi.c: Chelsio common library for T3/T4 iSCSI driver.
  *
- * Copyright (c) 2010 Chelsio Communications, Inc.
+ * Copyright (c) 2010-2015 Chelsio Communications, 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
@@ -38,8 +38,12 @@ static unsigned int dbg_level;
 
 #define DRV_MODULE_NAME                "libcxgbi"
 #define DRV_MODULE_DESC                "Chelsio iSCSI driver library"
-#define DRV_MODULE_VERSION     "0.9.0"
-#define DRV_MODULE_RELDATE     "Jun. 2010"
+#define DRV_MODULE_VERSION     "0.9.1-ko"
+#define DRV_MODULE_RELDATE     "Apr. 2015"
+
+static char version[] =
+       DRV_MODULE_DESC " " DRV_MODULE_NAME
+       " v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n";
 
 MODULE_AUTHOR("Chelsio Communications, Inc.");
 MODULE_DESCRIPTION(DRV_MODULE_DESC);
@@ -1126,11 +1130,11 @@ static int cxgbi_sock_send_pdus(struct cxgbi_sock *csk, struct sk_buff *skb)
                goto out_err;
        }
 
-       if (csk->write_seq - csk->snd_una >= cdev->snd_win) {
+       if (csk->write_seq - csk->snd_una >= csk->snd_win) {
                log_debug(1 << CXGBI_DBG_PDU_TX,
                        "csk 0x%p,%u,0x%lx,%u, FULL %u-%u >= %u.\n",
                        csk, csk->state, csk->flags, csk->tid, csk->write_seq,
-                       csk->snd_una, cdev->snd_win);
+                       csk->snd_una, csk->snd_win);
                err = -ENOBUFS;
                goto out_err;
        }
@@ -1885,7 +1889,7 @@ static void csk_return_rx_credits(struct cxgbi_sock *csk, int copied)
                "csk 0x%p,%u,0x%lx,%u, seq %u, wup %u, thre %u, %u.\n",
                csk, csk->state, csk->flags, csk->tid, csk->copied_seq,
                csk->rcv_wup, cdev->rx_credit_thres,
-               cdev->rcv_win);
+               csk->rcv_win);
 
        if (csk->state != CTP_ESTABLISHED)
                return;
@@ -1896,7 +1900,7 @@ static void csk_return_rx_credits(struct cxgbi_sock *csk, int copied)
        if (unlikely(cdev->rx_credit_thres == 0))
                return;
 
-       must_send = credits + 16384 >= cdev->rcv_win;
+       must_send = credits + 16384 >= csk->rcv_win;
        if (must_send || credits >= cdev->rx_credit_thres)
                csk->rcv_wup += cdev->csk_send_rx_credits(csk, credits);
 }
@@ -2913,6 +2917,8 @@ static int __init libcxgbi_init_module(void)
        sw_tag_idx_bits = (__ilog2_u32(ISCSI_ITT_MASK)) + 1;
        sw_tag_age_bits = (__ilog2_u32(ISCSI_AGE_MASK)) + 1;
 
+       pr_info("%s", version);
+
        pr_info("tag itt 0x%x, %u bits, age 0x%x, %u bits.\n",
                ISCSI_ITT_MASK, sw_tag_idx_bits,
                ISCSI_AGE_MASK, sw_tag_age_bits);
index aba1af720df653548a6c1b03934780d804c05500..b3e5bd1d5d9cd3f6577bf0a12ddfe44e8548bb31 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * libcxgbi.h: Chelsio common library for T3/T4 iSCSI driver.
  *
- * Copyright (c) 2010 Chelsio Communications, Inc.
+ * Copyright (c) 2010-2015 Chelsio Communications, 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
@@ -234,6 +234,8 @@ struct cxgbi_sock {
        u32 snd_nxt;
        u32 snd_una;
        u32 write_seq;
+       u32 snd_win;
+       u32 rcv_win;
 };
 
 /*
@@ -540,8 +542,6 @@ struct cxgbi_device {
        struct iscsi_transport *itp;
 
        unsigned int pfvf;
-       unsigned int snd_win;
-       unsigned int rcv_win;
        unsigned int rx_credit_thres;
        unsigned int skb_tx_rsvd;
        unsigned int skb_rx_extra;      /* for msg coalesced mode */
index 2806cfbec2b9c885b90a18905d8edb9339ed421b..f35ed53adaac23ca14e82d886ce239cff0f63f50 100644 (file)
@@ -3562,7 +3562,6 @@ static struct scsi_host_template driver_template = {
        .slave_configure        = adpt_slave_configure,
        .can_queue              = MAX_TO_IOP_MESSAGES,
        .this_id                = 7,
-       .cmd_per_lun            = 1,
        .use_clustering         = ENABLE_CLUSTERING,
 };
 
index fff682976c56202de49122628de392b35cfa737d..eefe14d453db077f487bc92c36d79f9843ae5cb4 100644 (file)
@@ -1764,7 +1764,6 @@ struct scsi_host_template fdomain_driver_template = {
        .can_queue              = 1,
        .this_id                = 6,
        .sg_tablesize           = 64,
-       .cmd_per_lun            = 1,
        .use_clustering         = DISABLE_CLUSTERING,
 };
 
index 5980c10c734d27f702b59a7dd443771fa740d75f..d6498fabe6282fe354bedb9214652c370900c1b0 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/module.h>
 #include <linux/errno.h>
 #include <linux/debugfs.h>
+#include <linux/vmalloc.h>
 #include "fnic.h"
 
 static struct dentry *fnic_trace_debugfs_root;
index 65a9bde26974bd9a5b98b33d7f456b16c9b5c10b..4e15c4bf079578afc8d944b0daeaca2264178fb2 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/spinlock.h>
 #include <linux/kallsyms.h>
 #include <linux/time.h>
+#include <linux/vmalloc.h>
 #include "fnic_io.h"
 #include "fnic.h"
 
index 8eab107b53fbab2641a5545b964873ad57677072..1dafeb43333b873d4433e4a2955328a88d3a6448 100644 (file)
@@ -43,6 +43,8 @@
 #include <scsi/scsi_device.h>
 #include <scsi/scsi_host.h>
 #include <scsi/scsi_tcq.h>
+#include <scsi/scsi_eh.h>
+#include <scsi/scsi_dbg.h>
 #include <linux/cciss_ioctl.h>
 #include <linux/string.h>
 #include <linux/bitmap.h>
@@ -56,7 +58,7 @@
 #include "hpsa.h"
 
 /* HPSA_DRIVER_VERSION must be 3 byte values (0-255) separated by '.' */
-#define HPSA_DRIVER_VERSION "3.4.4-1"
+#define HPSA_DRIVER_VERSION "3.4.10-0"
 #define DRIVER_NAME "HP HPSA Driver (v " HPSA_DRIVER_VERSION ")"
 #define HPSA "hpsa"
 
@@ -129,6 +131,7 @@ static const struct pci_device_id hpsa_pci_device_id[] = {
        {PCI_VENDOR_ID_HP,     PCI_DEVICE_ID_HP_CISSI,     0x103C, 0x21CC},
        {PCI_VENDOR_ID_HP,     PCI_DEVICE_ID_HP_CISSI,     0x103C, 0x21CD},
        {PCI_VENDOR_ID_HP,     PCI_DEVICE_ID_HP_CISSI,     0x103C, 0x21CE},
+       {PCI_VENDOR_ID_ADAPTEC2, 0x0290, 0x9005, 0x0580},
        {PCI_VENDOR_ID_HP_3PAR, 0x0075, 0x1590, 0x0076},
        {PCI_VENDOR_ID_HP_3PAR, 0x0075, 0x1590, 0x0087},
        {PCI_VENDOR_ID_HP_3PAR, 0x0075, 0x1590, 0x007D},
@@ -186,6 +189,7 @@ static struct board_type products[] = {
        {0x21CC103C, "Smart Array", &SA5_access},
        {0x21CD103C, "Smart Array", &SA5_access},
        {0x21CE103C, "Smart HBA", &SA5_access},
+       {0x05809005, "SmartHBA-SA", &SA5_access},
        {0x00761590, "HP Storage P1224 Array Controller", &SA5_access},
        {0x00871590, "HP Storage P1224e Array Controller", &SA5_access},
        {0x007D1590, "HP Storage P1228 Array Controller", &SA5_access},
@@ -194,6 +198,10 @@ static struct board_type products[] = {
        {0xFFFF103C, "Unknown Smart Array", &SA5_access},
 };
 
+#define SCSI_CMD_BUSY ((struct scsi_cmnd *)&hpsa_cmd_busy)
+static const struct scsi_cmnd hpsa_cmd_busy;
+#define SCSI_CMD_IDLE ((struct scsi_cmnd *)&hpsa_cmd_idle)
+static const struct scsi_cmnd hpsa_cmd_idle;
 static int number_of_controllers;
 
 static irqreturn_t do_hpsa_intr_intx(int irq, void *dev_id);
@@ -207,6 +215,9 @@ static int hpsa_compat_ioctl(struct scsi_device *dev, int cmd,
 
 static void cmd_free(struct ctlr_info *h, struct CommandList *c);
 static struct CommandList *cmd_alloc(struct ctlr_info *h);
+static void cmd_tagged_free(struct ctlr_info *h, struct CommandList *c);
+static struct CommandList *cmd_tagged_alloc(struct ctlr_info *h,
+                                           struct scsi_cmnd *scmd);
 static int fill_cmd(struct CommandList *c, u8 cmd, struct ctlr_info *h,
        void *buff, size_t size, u16 page_code, unsigned char *scsi3addr,
        int cmd_type);
@@ -222,6 +233,7 @@ static int hpsa_change_queue_depth(struct scsi_device *sdev, int qdepth);
 static int hpsa_eh_device_reset_handler(struct scsi_cmnd *scsicmd);
 static int hpsa_eh_abort_handler(struct scsi_cmnd *scsicmd);
 static int hpsa_slave_alloc(struct scsi_device *sdev);
+static int hpsa_slave_configure(struct scsi_device *sdev);
 static void hpsa_slave_destroy(struct scsi_device *sdev);
 
 static void hpsa_update_scsi_devices(struct ctlr_info *h, int hostno);
@@ -232,7 +244,8 @@ static void check_ioctl_unit_attention(struct ctlr_info *h,
 /* performant mode helper functions */
 static void calc_bucket_map(int *bucket, int num_buckets,
        int nsgs, int min_blocks, u32 *bucket_map);
-static void hpsa_put_ctlr_into_performant_mode(struct ctlr_info *h);
+static void hpsa_free_performant_mode(struct ctlr_info *h);
+static int hpsa_put_ctlr_into_performant_mode(struct ctlr_info *h);
 static inline u32 next_command(struct ctlr_info *h, u8 q);
 static int hpsa_find_cfg_addrs(struct pci_dev *pdev, void __iomem *vaddr,
                               u32 *cfg_base_addr, u64 *cfg_base_addr_index,
@@ -252,6 +265,8 @@ static int hpsa_scsi_ioaccel_queue_command(struct ctlr_info *h,
        struct CommandList *c, u32 ioaccel_handle, u8 *cdb, int cdb_len,
        u8 *scsi3addr, struct hpsa_scsi_dev_t *phys_disk);
 static void hpsa_command_resubmit_worker(struct work_struct *work);
+static u32 lockup_detected(struct ctlr_info *h);
+static int detect_controller_lockup(struct ctlr_info *h);
 
 static inline struct ctlr_info *sdev_to_hba(struct scsi_device *sdev)
 {
@@ -265,40 +280,86 @@ static inline struct ctlr_info *shost_to_hba(struct Scsi_Host *sh)
        return (struct ctlr_info *) *priv;
 }
 
+static inline bool hpsa_is_cmd_idle(struct CommandList *c)
+{
+       return c->scsi_cmd == SCSI_CMD_IDLE;
+}
+
+static inline bool hpsa_is_pending_event(struct CommandList *c)
+{
+       return c->abort_pending || c->reset_pending;
+}
+
+/* extract sense key, asc, and ascq from sense data.  -1 means invalid. */
+static void decode_sense_data(const u8 *sense_data, int sense_data_len,
+                       u8 *sense_key, u8 *asc, u8 *ascq)
+{
+       struct scsi_sense_hdr sshdr;
+       bool rc;
+
+       *sense_key = -1;
+       *asc = -1;
+       *ascq = -1;
+
+       if (sense_data_len < 1)
+               return;
+
+       rc = scsi_normalize_sense(sense_data, sense_data_len, &sshdr);
+       if (rc) {
+               *sense_key = sshdr.sense_key;
+               *asc = sshdr.asc;
+               *ascq = sshdr.ascq;
+       }
+}
+
 static int check_for_unit_attention(struct ctlr_info *h,
        struct CommandList *c)
 {
-       if (c->err_info->SenseInfo[2] != UNIT_ATTENTION)
+       u8 sense_key, asc, ascq;
+       int sense_len;
+
+       if (c->err_info->SenseLen > sizeof(c->err_info->SenseInfo))
+               sense_len = sizeof(c->err_info->SenseInfo);
+       else
+               sense_len = c->err_info->SenseLen;
+
+       decode_sense_data(c->err_info->SenseInfo, sense_len,
+                               &sense_key, &asc, &ascq);
+       if (sense_key != UNIT_ATTENTION || asc == -1)
                return 0;
 
-       switch (c->err_info->SenseInfo[12]) {
+       switch (asc) {
        case STATE_CHANGED:
-               dev_warn(&h->pdev->dev, HPSA "%d: a state change "
-                       "detected, command retried\n", h->ctlr);
+               dev_warn(&h->pdev->dev,
+                       "%s: a state change detected, command retried\n",
+                       h->devname);
                break;
        case LUN_FAILED:
                dev_warn(&h->pdev->dev,
-                       HPSA "%d: LUN failure detected\n", h->ctlr);
+                       "%s: LUN failure detected\n", h->devname);
                break;
        case REPORT_LUNS_CHANGED:
                dev_warn(&h->pdev->dev,
-                       HPSA "%d: report LUN data changed\n", h->ctlr);
+                       "%s: report LUN data changed\n", h->devname);
        /*
         * Note: this REPORT_LUNS_CHANGED condition only occurs on the external
         * target (array) devices.
         */
                break;
        case POWER_OR_RESET:
-               dev_warn(&h->pdev->dev, HPSA "%d: a power on "
-                       "or device reset detected\n", h->ctlr);
+               dev_warn(&h->pdev->dev,
+                       "%s: a power on or device reset detected\n",
+                       h->devname);
                break;
        case UNIT_ATTENTION_CLEARED:
-               dev_warn(&h->pdev->dev, HPSA "%d: unit attention "
-                   "cleared by another initiator\n", h->ctlr);
+               dev_warn(&h->pdev->dev,
+                       "%s: unit attention cleared by another initiator\n",
+                       h->devname);
                break;
        default:
-               dev_warn(&h->pdev->dev, HPSA "%d: unknown "
-                       "unit attention detected\n", h->ctlr);
+               dev_warn(&h->pdev->dev,
+                       "%s: unknown unit attention detected\n",
+                       h->devname);
                break;
        }
        return 1;
@@ -314,6 +375,20 @@ static int check_for_busy(struct ctlr_info *h, struct CommandList *c)
        return 1;
 }
 
+static u32 lockup_detected(struct ctlr_info *h);
+static ssize_t host_show_lockup_detected(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       int ld;
+       struct ctlr_info *h;
+       struct Scsi_Host *shost = class_to_shost(dev);
+
+       h = shost_to_hba(shost);
+       ld = lockup_detected(h);
+
+       return sprintf(buf, "ld=%d\n", ld);
+}
+
 static ssize_t host_store_hp_ssd_smart_path_status(struct device *dev,
                                         struct device_attribute *attr,
                                         const char *buf, size_t count)
@@ -425,7 +500,7 @@ static ssize_t host_show_hp_ssd_smart_path_status(struct device *dev,
 /* List of controllers which cannot be hard reset on kexec with reset_devices */
 static u32 unresettable_controller[] = {
        0x324a103C, /* Smart Array P712m */
-       0x324b103C, /* SmartArray P711m */
+       0x324b103C, /* Smart Array P711m */
        0x3223103C, /* Smart Array P800 */
        0x3234103C, /* Smart Array P400 */
        0x3235103C, /* Smart Array P400i */
@@ -467,24 +542,32 @@ static u32 soft_unresettable_controller[] = {
        0x409D0E11, /* Smart Array 6400 EM */
 };
 
-static int ctlr_is_hard_resettable(u32 board_id)
+static u32 needs_abort_tags_swizzled[] = {
+       0x323D103C, /* Smart Array P700m */
+       0x324a103C, /* Smart Array P712m */
+       0x324b103C, /* SmartArray P711m */
+};
+
+static int board_id_in_array(u32 a[], int nelems, u32 board_id)
 {
        int i;
 
-       for (i = 0; i < ARRAY_SIZE(unresettable_controller); i++)
-               if (unresettable_controller[i] == board_id)
-                       return 0;
-       return 1;
+       for (i = 0; i < nelems; i++)
+               if (a[i] == board_id)
+                       return 1;
+       return 0;
 }
 
-static int ctlr_is_soft_resettable(u32 board_id)
+static int ctlr_is_hard_resettable(u32 board_id)
 {
-       int i;
+       return !board_id_in_array(unresettable_controller,
+                       ARRAY_SIZE(unresettable_controller), board_id);
+}
 
-       for (i = 0; i < ARRAY_SIZE(soft_unresettable_controller); i++)
-               if (soft_unresettable_controller[i] == board_id)
-                       return 0;
-       return 1;
+static int ctlr_is_soft_resettable(u32 board_id)
+{
+       return !board_id_in_array(soft_unresettable_controller,
+                       ARRAY_SIZE(soft_unresettable_controller), board_id);
 }
 
 static int ctlr_is_resettable(u32 board_id)
@@ -493,6 +576,12 @@ static int ctlr_is_resettable(u32 board_id)
                ctlr_is_soft_resettable(board_id);
 }
 
+static int ctlr_needs_abort_tags_swizzled(u32 board_id)
+{
+       return board_id_in_array(needs_abort_tags_swizzled,
+                       ARRAY_SIZE(needs_abort_tags_swizzled), board_id);
+}
+
 static ssize_t host_show_resettable(struct device *dev,
        struct device_attribute *attr, char *buf)
 {
@@ -647,12 +736,15 @@ static DEVICE_ATTR(transport_mode, S_IRUGO,
        host_show_transport_mode, NULL);
 static DEVICE_ATTR(resettable, S_IRUGO,
        host_show_resettable, NULL);
+static DEVICE_ATTR(lockup_detected, S_IRUGO,
+       host_show_lockup_detected, NULL);
 
 static struct device_attribute *hpsa_sdev_attrs[] = {
        &dev_attr_raid_level,
        &dev_attr_lunid,
        &dev_attr_unique_id,
        &dev_attr_hp_ssd_smart_path_enabled,
+       &dev_attr_lockup_detected,
        NULL,
 };
 
@@ -667,6 +759,9 @@ static struct device_attribute *hpsa_shost_attrs[] = {
        NULL,
 };
 
+#define HPSA_NRESERVED_CMDS    (HPSA_CMDS_RESERVED_FOR_ABORTS + \
+               HPSA_CMDS_RESERVED_FOR_DRIVER + HPSA_MAX_CONCURRENT_PASSTHRUS)
+
 static struct scsi_host_template hpsa_driver_template = {
        .module                 = THIS_MODULE,
        .name                   = HPSA,
@@ -681,6 +776,7 @@ static struct scsi_host_template hpsa_driver_template = {
        .eh_device_reset_handler = hpsa_eh_device_reset_handler,
        .ioctl                  = hpsa_ioctl,
        .slave_alloc            = hpsa_slave_alloc,
+       .slave_configure        = hpsa_slave_configure,
        .slave_destroy          = hpsa_slave_destroy,
 #ifdef CONFIG_COMPAT
        .compat_ioctl           = hpsa_compat_ioctl,
@@ -743,30 +839,43 @@ static inline u32 next_command(struct ctlr_info *h, u8 q)
  * a separate special register for submitting commands.
  */
 
-/* set_performant_mode: Modify the tag for cciss performant
+/*
+ * set_performant_mode: Modify the tag for cciss performant
  * set bit 0 for pull model, bits 3-1 for block fetch
  * register number
  */
-static void set_performant_mode(struct ctlr_info *h, struct CommandList *c)
+#define DEFAULT_REPLY_QUEUE (-1)
+static void set_performant_mode(struct ctlr_info *h, struct CommandList *c,
+                                       int reply_queue)
 {
        if (likely(h->transMethod & CFGTBL_Trans_Performant)) {
                c->busaddr |= 1 | (h->blockFetchTable[c->Header.SGList] << 1);
-               if (likely(h->msix_vector > 0))
+               if (unlikely(!h->msix_vector))
+                       return;
+               if (likely(reply_queue == DEFAULT_REPLY_QUEUE))
                        c->Header.ReplyQueue =
                                raw_smp_processor_id() % h->nreply_queues;
+               else
+                       c->Header.ReplyQueue = reply_queue % h->nreply_queues;
        }
 }
 
 static void set_ioaccel1_performant_mode(struct ctlr_info *h,
-                                               struct CommandList *c)
+                                               struct CommandList *c,
+                                               int reply_queue)
 {
        struct io_accel1_cmd *cp = &h->ioaccel_cmd_pool[c->cmdindex];
 
-       /* Tell the controller to post the reply to the queue for this
+       /*
+        * Tell the controller to post the reply to the queue for this
         * processor.  This seems to give the best I/O throughput.
         */
-       cp->ReplyQueue = smp_processor_id() % h->nreply_queues;
-       /* Set the bits in the address sent down to include:
+       if (likely(reply_queue == DEFAULT_REPLY_QUEUE))
+               cp->ReplyQueue = smp_processor_id() % h->nreply_queues;
+       else
+               cp->ReplyQueue = reply_queue % h->nreply_queues;
+       /*
+        * Set the bits in the address sent down to include:
         *  - performant mode bit (bit 0)
         *  - pull count (bits 1-3)
         *  - command type (bits 4-6)
@@ -775,20 +884,48 @@ static void set_ioaccel1_performant_mode(struct ctlr_info *h,
                                        IOACCEL1_BUSADDR_CMDTYPE;
 }
 
-static void set_ioaccel2_performant_mode(struct ctlr_info *h,
-                                               struct CommandList *c)
+static void set_ioaccel2_tmf_performant_mode(struct ctlr_info *h,
+                                               struct CommandList *c,
+                                               int reply_queue)
 {
-       struct io_accel2_cmd *cp = &h->ioaccel2_cmd_pool[c->cmdindex];
+       struct hpsa_tmf_struct *cp = (struct hpsa_tmf_struct *)
+               &h->ioaccel2_cmd_pool[c->cmdindex];
 
        /* Tell the controller to post the reply to the queue for this
         * processor.  This seems to give the best I/O throughput.
         */
-       cp->reply_queue = smp_processor_id() % h->nreply_queues;
+       if (likely(reply_queue == DEFAULT_REPLY_QUEUE))
+               cp->reply_queue = smp_processor_id() % h->nreply_queues;
+       else
+               cp->reply_queue = reply_queue % h->nreply_queues;
        /* Set the bits in the address sent down to include:
         *  - performant mode bit not used in ioaccel mode 2
         *  - pull count (bits 0-3)
         *  - command type isn't needed for ioaccel2
         */
+       c->busaddr |= h->ioaccel2_blockFetchTable[0];
+}
+
+static void set_ioaccel2_performant_mode(struct ctlr_info *h,
+                                               struct CommandList *c,
+                                               int reply_queue)
+{
+       struct io_accel2_cmd *cp = &h->ioaccel2_cmd_pool[c->cmdindex];
+
+       /*
+        * Tell the controller to post the reply to the queue for this
+        * processor.  This seems to give the best I/O throughput.
+        */
+       if (likely(reply_queue == DEFAULT_REPLY_QUEUE))
+               cp->reply_queue = smp_processor_id() % h->nreply_queues;
+       else
+               cp->reply_queue = reply_queue % h->nreply_queues;
+       /*
+        * Set the bits in the address sent down to include:
+        *  - performant mode bit not used in ioaccel mode 2
+        *  - pull count (bits 0-3)
+        *  - command type isn't needed for ioaccel2
+        */
        c->busaddr |= (h->ioaccel2_blockFetchTable[cp->sg_count]);
 }
 
@@ -821,26 +958,38 @@ static void dial_up_lockup_detection_on_fw_flash_complete(struct ctlr_info *h,
                h->heartbeat_sample_interval = HEARTBEAT_SAMPLE_INTERVAL;
 }
 
-static void enqueue_cmd_and_start_io(struct ctlr_info *h,
-       struct CommandList *c)
+static void __enqueue_cmd_and_start_io(struct ctlr_info *h,
+       struct CommandList *c, int reply_queue)
 {
        dial_down_lockup_detection_during_fw_flash(h, c);
        atomic_inc(&h->commands_outstanding);
        switch (c->cmd_type) {
        case CMD_IOACCEL1:
-               set_ioaccel1_performant_mode(h, c);
+               set_ioaccel1_performant_mode(h, c, reply_queue);
                writel(c->busaddr, h->vaddr + SA5_REQUEST_PORT_OFFSET);
                break;
        case CMD_IOACCEL2:
-               set_ioaccel2_performant_mode(h, c);
+               set_ioaccel2_performant_mode(h, c, reply_queue);
+               writel(c->busaddr, h->vaddr + IOACCEL2_INBOUND_POSTQ_32);
+               break;
+       case IOACCEL2_TMF:
+               set_ioaccel2_tmf_performant_mode(h, c, reply_queue);
                writel(c->busaddr, h->vaddr + IOACCEL2_INBOUND_POSTQ_32);
                break;
        default:
-               set_performant_mode(h, c);
+               set_performant_mode(h, c, reply_queue);
                h->access.submit_command(h, c);
        }
 }
 
+static void enqueue_cmd_and_start_io(struct ctlr_info *h, struct CommandList *c)
+{
+       if (unlikely(hpsa_is_pending_event(c)))
+               return finish_cmd(c);
+
+       __enqueue_cmd_and_start_io(h, c, DEFAULT_REPLY_QUEUE);
+}
+
 static inline int is_hba_lunid(unsigned char scsi3addr[])
 {
        return memcmp(scsi3addr, RAID_CTLR_LUNID, 8) == 0;
@@ -881,6 +1030,23 @@ static int hpsa_find_target_lun(struct ctlr_info *h,
        return !found;
 }
 
+static inline void hpsa_show_dev_msg(const char *level, struct ctlr_info *h,
+       struct hpsa_scsi_dev_t *dev, char *description)
+{
+       dev_printk(level, &h->pdev->dev,
+                       "scsi %d:%d:%d:%d: %s %s %.8s %.16s RAID-%s SSDSmartPathCap%c En%c Exp=%d\n",
+                       h->scsi_host->host_no, dev->bus, dev->target, dev->lun,
+                       description,
+                       scsi_device_type(dev->devtype),
+                       dev->vendor,
+                       dev->model,
+                       dev->raid_level > RAID_UNKNOWN ?
+                               "RAID-?" : raid_label[dev->raid_level],
+                       dev->offload_config ? '+' : '-',
+                       dev->offload_enabled ? '+' : '-',
+                       dev->expose_state);
+}
+
 /* Add an entry into h->dev[] array. */
 static int hpsa_scsi_add_entry(struct ctlr_info *h, int hostno,
                struct hpsa_scsi_dev_t *device,
@@ -948,15 +1114,10 @@ lun_assigned:
        h->ndevices++;
        added[*nadded] = device;
        (*nadded)++;
-
-       /* initially, (before registering with scsi layer) we don't
-        * know our hostno and we don't want to print anything first
-        * time anyway (the scsi layer's inquiries will show that info)
-        */
-       /* if (hostno != -1) */
-               dev_info(&h->pdev->dev, "%s device c%db%dt%dl%d added.\n",
-                       scsi_device_type(device->devtype), hostno,
-                       device->bus, device->target, device->lun);
+       hpsa_show_dev_msg(KERN_INFO, h, device,
+               device->expose_state & HPSA_SCSI_ADD ? "added" : "masked");
+       device->offload_to_be_enabled = device->offload_enabled;
+       device->offload_enabled = 0;
        return 0;
 }
 
@@ -964,6 +1125,7 @@ lun_assigned:
 static void hpsa_scsi_update_entry(struct ctlr_info *h, int hostno,
        int entry, struct hpsa_scsi_dev_t *new_entry)
 {
+       int offload_enabled;
        /* assumes h->devlock is held */
        BUG_ON(entry < 0 || entry >= HPSA_MAX_DEVICES);
 
@@ -982,16 +1144,29 @@ static void hpsa_scsi_update_entry(struct ctlr_info *h, int hostno,
                 */
                h->dev[entry]->raid_map = new_entry->raid_map;
                h->dev[entry]->ioaccel_handle = new_entry->ioaccel_handle;
-               wmb(); /* ensure raid map updated prior to ->offload_enabled */
        }
+       if (new_entry->hba_ioaccel_enabled) {
+               h->dev[entry]->ioaccel_handle = new_entry->ioaccel_handle;
+               wmb(); /* set ioaccel_handle *before* hba_ioaccel_enabled */
+       }
+       h->dev[entry]->hba_ioaccel_enabled = new_entry->hba_ioaccel_enabled;
        h->dev[entry]->offload_config = new_entry->offload_config;
        h->dev[entry]->offload_to_mirror = new_entry->offload_to_mirror;
-       h->dev[entry]->offload_enabled = new_entry->offload_enabled;
        h->dev[entry]->queue_depth = new_entry->queue_depth;
 
-       dev_info(&h->pdev->dev, "%s device c%db%dt%dl%d updated.\n",
-               scsi_device_type(new_entry->devtype), hostno, new_entry->bus,
-               new_entry->target, new_entry->lun);
+       /*
+        * We can turn off ioaccel offload now, but need to delay turning
+        * it on until we can update h->dev[entry]->phys_disk[], but we
+        * can't do that until all the devices are updated.
+        */
+       h->dev[entry]->offload_to_be_enabled = new_entry->offload_enabled;
+       if (!new_entry->offload_enabled)
+               h->dev[entry]->offload_enabled = 0;
+
+       offload_enabled = h->dev[entry]->offload_enabled;
+       h->dev[entry]->offload_enabled = h->dev[entry]->offload_to_be_enabled;
+       hpsa_show_dev_msg(KERN_INFO, h, h->dev[entry], "updated");
+       h->dev[entry]->offload_enabled = offload_enabled;
 }
 
 /* Replace an entry from h->dev[] array. */
@@ -1017,9 +1192,9 @@ static void hpsa_scsi_replace_entry(struct ctlr_info *h, int hostno,
        h->dev[entry] = new_entry;
        added[*nadded] = new_entry;
        (*nadded)++;
-       dev_info(&h->pdev->dev, "%s device c%db%dt%dl%d changed.\n",
-               scsi_device_type(new_entry->devtype), hostno, new_entry->bus,
-                       new_entry->target, new_entry->lun);
+       hpsa_show_dev_msg(KERN_INFO, h, new_entry, "replaced");
+       new_entry->offload_to_be_enabled = new_entry->offload_enabled;
+       new_entry->offload_enabled = 0;
 }
 
 /* Remove an entry from h->dev[] array. */
@@ -1039,9 +1214,7 @@ static void hpsa_scsi_remove_entry(struct ctlr_info *h, int hostno, int entry,
        for (i = entry; i < h->ndevices-1; i++)
                h->dev[i] = h->dev[i+1];
        h->ndevices--;
-       dev_info(&h->pdev->dev, "%s device c%db%dt%dl%d removed.\n",
-               scsi_device_type(sd->devtype), hostno, sd->bus, sd->target,
-               sd->lun);
+       hpsa_show_dev_msg(KERN_INFO, h, sd, "removed");
 }
 
 #define SCSI3ADDR_EQ(a, b) ( \
@@ -1283,6 +1456,8 @@ static void hpsa_figure_phys_disk_ptrs(struct ctlr_info *h,
        if (nraid_map_entries > RAID_MAP_MAX_ENTRIES)
                nraid_map_entries = RAID_MAP_MAX_ENTRIES;
 
+       logical_drive->nphysical_disks = nraid_map_entries;
+
        qdepth = 0;
        for (i = 0; i < nraid_map_entries; i++) {
                logical_drive->phys_disk[i] = NULL;
@@ -1312,7 +1487,8 @@ static void hpsa_figure_phys_disk_ptrs(struct ctlr_info *h,
                 */
                if (!logical_drive->phys_disk[i]) {
                        logical_drive->offload_enabled = 0;
-                       logical_drive->queue_depth = h->nr_cmds;
+                       logical_drive->offload_to_be_enabled = 0;
+                       logical_drive->queue_depth = 8;
                }
        }
        if (nraid_map_entries)
@@ -1335,6 +1511,16 @@ static void hpsa_update_log_drive_phys_drive_ptrs(struct ctlr_info *h,
                        continue;
                if (!is_logical_dev_addr_mode(dev[i]->scsi3addr))
                        continue;
+
+               /*
+                * If offload is currently enabled, the RAID map and
+                * phys_disk[] assignment *better* not be changing
+                * and since it isn't changing, we do not need to
+                * update it.
+                */
+               if (dev[i]->offload_enabled)
+                       continue;
+
                hpsa_figure_phys_disk_ptrs(h, dev, ndevices, dev[i]);
        }
 }
@@ -1411,9 +1597,7 @@ static void adjust_hpsa_scsi_table(struct ctlr_info *h, int hostno,
                 */
                if (sd[i]->volume_offline) {
                        hpsa_show_volume_status(h, sd[i]);
-                       dev_info(&h->pdev->dev, "c%db%dt%dl%d: temporarily offline\n",
-                               h->scsi_host->host_no,
-                               sd[i]->bus, sd[i]->target, sd[i]->lun);
+                       hpsa_show_dev_msg(KERN_INFO, h, sd[i], "offline");
                        continue;
                }
 
@@ -1433,6 +1617,14 @@ static void adjust_hpsa_scsi_table(struct ctlr_info *h, int hostno,
                        /* but if it does happen, we just ignore that device */
                }
        }
+       hpsa_update_log_drive_phys_drive_ptrs(h, h->dev, h->ndevices);
+
+       /* Now that h->dev[]->phys_disk[] is coherent, we can enable
+        * any logical drives that need it enabled.
+        */
+       for (i = 0; i < h->ndevices; i++)
+               h->dev[i]->offload_enabled = h->dev[i]->offload_to_be_enabled;
+
        spin_unlock_irqrestore(&h->devlock, flags);
 
        /* Monitor devices which are in one of several NOT READY states to be
@@ -1456,20 +1648,22 @@ static void adjust_hpsa_scsi_table(struct ctlr_info *h, int hostno,
        sh = h->scsi_host;
        /* Notify scsi mid layer of any removed devices */
        for (i = 0; i < nremoved; i++) {
-               struct scsi_device *sdev =
-                       scsi_device_lookup(sh, removed[i]->bus,
-                               removed[i]->target, removed[i]->lun);
-               if (sdev != NULL) {
-                       scsi_remove_device(sdev);
-                       scsi_device_put(sdev);
-               } else {
-                       /* We don't expect to get here.
-                        * future cmds to this device will get selection
-                        * timeout as if the device was gone.
-                        */
-                       dev_warn(&h->pdev->dev, "didn't find c%db%dt%dl%d "
-                               " for removal.", hostno, removed[i]->bus,
-                               removed[i]->target, removed[i]->lun);
+               if (removed[i]->expose_state & HPSA_SCSI_ADD) {
+                       struct scsi_device *sdev =
+                               scsi_device_lookup(sh, removed[i]->bus,
+                                       removed[i]->target, removed[i]->lun);
+                       if (sdev != NULL) {
+                               scsi_remove_device(sdev);
+                               scsi_device_put(sdev);
+                       } else {
+                               /*
+                                * We don't expect to get here.
+                                * future cmds to this device will get selection
+                                * timeout as if the device was gone.
+                                */
+                               hpsa_show_dev_msg(KERN_WARNING, h, removed[i],
+                                       "didn't find device for removal.");
+                       }
                }
                kfree(removed[i]);
                removed[i] = NULL;
@@ -1477,16 +1671,18 @@ static void adjust_hpsa_scsi_table(struct ctlr_info *h, int hostno,
 
        /* Notify scsi mid layer of any added devices */
        for (i = 0; i < nadded; i++) {
+               if (!(added[i]->expose_state & HPSA_SCSI_ADD))
+                       continue;
                if (scsi_add_device(sh, added[i]->bus,
                        added[i]->target, added[i]->lun) == 0)
                        continue;
-               dev_warn(&h->pdev->dev, "scsi_add_device c%db%dt%dl%d failed, "
-                       "device not added.\n", hostno, added[i]->bus,
-                       added[i]->target, added[i]->lun);
+               hpsa_show_dev_msg(KERN_WARNING, h, added[i],
+                                       "addition failed, device not added.");
                /* now we have to remove it from h->dev,
                 * since it didn't get added to scsi mid layer
                 */
                fixup_botched_add(h, added[i]);
+               added[i] = NULL;
        }
 
 free_and_out:
@@ -1512,7 +1708,6 @@ static struct hpsa_scsi_dev_t *lookup_hpsa_scsi_dev(struct ctlr_info *h,
        return NULL;
 }
 
-/* link sdev->hostdata to our per-device structure. */
 static int hpsa_slave_alloc(struct scsi_device *sdev)
 {
        struct hpsa_scsi_dev_t *sd;
@@ -1523,21 +1718,80 @@ static int hpsa_slave_alloc(struct scsi_device *sdev)
        spin_lock_irqsave(&h->devlock, flags);
        sd = lookup_hpsa_scsi_dev(h, sdev_channel(sdev),
                sdev_id(sdev), sdev->lun);
-       if (sd != NULL) {
-               sdev->hostdata = sd;
-               if (sd->queue_depth)
-                       scsi_change_queue_depth(sdev, sd->queue_depth);
+       if (likely(sd)) {
                atomic_set(&sd->ioaccel_cmds_out, 0);
-       }
+               sdev->hostdata = (sd->expose_state & HPSA_SCSI_ADD) ? sd : NULL;
+       } else
+               sdev->hostdata = NULL;
        spin_unlock_irqrestore(&h->devlock, flags);
        return 0;
 }
 
+/* configure scsi device based on internal per-device structure */
+static int hpsa_slave_configure(struct scsi_device *sdev)
+{
+       struct hpsa_scsi_dev_t *sd;
+       int queue_depth;
+
+       sd = sdev->hostdata;
+       sdev->no_uld_attach = !sd || !(sd->expose_state & HPSA_ULD_ATTACH);
+
+       if (sd)
+               queue_depth = sd->queue_depth != 0 ?
+                       sd->queue_depth : sdev->host->can_queue;
+       else
+               queue_depth = sdev->host->can_queue;
+
+       scsi_change_queue_depth(sdev, queue_depth);
+
+       return 0;
+}
+
 static void hpsa_slave_destroy(struct scsi_device *sdev)
 {
        /* nothing to do. */
 }
 
+static void hpsa_free_ioaccel2_sg_chain_blocks(struct ctlr_info *h)
+{
+       int i;
+
+       if (!h->ioaccel2_cmd_sg_list)
+               return;
+       for (i = 0; i < h->nr_cmds; i++) {
+               kfree(h->ioaccel2_cmd_sg_list[i]);
+               h->ioaccel2_cmd_sg_list[i] = NULL;
+       }
+       kfree(h->ioaccel2_cmd_sg_list);
+       h->ioaccel2_cmd_sg_list = NULL;
+}
+
+static int hpsa_allocate_ioaccel2_sg_chain_blocks(struct ctlr_info *h)
+{
+       int i;
+
+       if (h->chainsize <= 0)
+               return 0;
+
+       h->ioaccel2_cmd_sg_list =
+               kzalloc(sizeof(*h->ioaccel2_cmd_sg_list) * h->nr_cmds,
+                                       GFP_KERNEL);
+       if (!h->ioaccel2_cmd_sg_list)
+               return -ENOMEM;
+       for (i = 0; i < h->nr_cmds; i++) {
+               h->ioaccel2_cmd_sg_list[i] =
+                       kmalloc(sizeof(*h->ioaccel2_cmd_sg_list[i]) *
+                                       h->maxsgentries, GFP_KERNEL);
+               if (!h->ioaccel2_cmd_sg_list[i])
+                       goto clean;
+       }
+       return 0;
+
+clean:
+       hpsa_free_ioaccel2_sg_chain_blocks(h);
+       return -ENOMEM;
+}
+
 static void hpsa_free_sg_chain_blocks(struct ctlr_info *h)
 {
        int i;
@@ -1552,7 +1806,7 @@ static void hpsa_free_sg_chain_blocks(struct ctlr_info *h)
        h->cmd_sg_list = NULL;
 }
 
-static int hpsa_allocate_sg_chain_blocks(struct ctlr_info *h)
+static int hpsa_alloc_sg_chain_blocks(struct ctlr_info *h)
 {
        int i;
 
@@ -1580,6 +1834,39 @@ clean:
        return -ENOMEM;
 }
 
+static int hpsa_map_ioaccel2_sg_chain_block(struct ctlr_info *h,
+       struct io_accel2_cmd *cp, struct CommandList *c)
+{
+       struct ioaccel2_sg_element *chain_block;
+       u64 temp64;
+       u32 chain_size;
+
+       chain_block = h->ioaccel2_cmd_sg_list[c->cmdindex];
+       chain_size = le32_to_cpu(cp->data_len);
+       temp64 = pci_map_single(h->pdev, chain_block, chain_size,
+                               PCI_DMA_TODEVICE);
+       if (dma_mapping_error(&h->pdev->dev, temp64)) {
+               /* prevent subsequent unmapping */
+               cp->sg->address = 0;
+               return -1;
+       }
+       cp->sg->address = cpu_to_le64(temp64);
+       return 0;
+}
+
+static void hpsa_unmap_ioaccel2_sg_chain_block(struct ctlr_info *h,
+       struct io_accel2_cmd *cp)
+{
+       struct ioaccel2_sg_element *chain_sg;
+       u64 temp64;
+       u32 chain_size;
+
+       chain_sg = cp->sg;
+       temp64 = le64_to_cpu(chain_sg->address);
+       chain_size = le32_to_cpu(cp->data_len);
+       pci_unmap_single(h->pdev, temp64, chain_size, PCI_DMA_TODEVICE);
+}
+
 static int hpsa_map_sg_chain_block(struct ctlr_info *h,
        struct CommandList *c)
 {
@@ -1629,6 +1916,7 @@ static int handle_ioaccel_mode2_error(struct ctlr_info *h,
 {
        int data_len;
        int retry = 0;
+       u32 ioaccel2_resid = 0;
 
        switch (c2->error_data.serv_response) {
        case IOACCEL2_SERV_RESPONSE_COMPLETE:
@@ -1636,9 +1924,6 @@ static int handle_ioaccel_mode2_error(struct ctlr_info *h,
                case IOACCEL2_STATUS_SR_TASK_COMP_GOOD:
                        break;
                case IOACCEL2_STATUS_SR_TASK_COMP_CHK_COND:
-                       dev_warn(&h->pdev->dev,
-                               "%s: task complete with check condition.\n",
-                               "HP SSD Smart Path");
                        cmd->result |= SAM_STAT_CHECK_CONDITION;
                        if (c2->error_data.data_present !=
                                        IOACCEL2_SENSE_DATA_PRESENT) {
@@ -1658,58 +1943,56 @@ static int handle_ioaccel_mode2_error(struct ctlr_info *h,
                        retry = 1;
                        break;
                case IOACCEL2_STATUS_SR_TASK_COMP_BUSY:
-                       dev_warn(&h->pdev->dev,
-                               "%s: task complete with BUSY status.\n",
-                               "HP SSD Smart Path");
                        retry = 1;
                        break;
                case IOACCEL2_STATUS_SR_TASK_COMP_RES_CON:
-                       dev_warn(&h->pdev->dev,
-                               "%s: task complete with reservation conflict.\n",
-                               "HP SSD Smart Path");
                        retry = 1;
                        break;
                case IOACCEL2_STATUS_SR_TASK_COMP_SET_FULL:
-                       /* Make scsi midlayer do unlimited retries */
-                       cmd->result = DID_IMM_RETRY << 16;
+                       retry = 1;
                        break;
                case IOACCEL2_STATUS_SR_TASK_COMP_ABORTED:
-                       dev_warn(&h->pdev->dev,
-                               "%s: task complete with aborted status.\n",
-                               "HP SSD Smart Path");
                        retry = 1;
                        break;
                default:
-                       dev_warn(&h->pdev->dev,
-                               "%s: task complete with unrecognized status: 0x%02x\n",
-                               "HP SSD Smart Path", c2->error_data.status);
                        retry = 1;
                        break;
                }
                break;
        case IOACCEL2_SERV_RESPONSE_FAILURE:
-               /* don't expect to get here. */
-               dev_warn(&h->pdev->dev,
-                       "unexpected delivery or target failure, status = 0x%02x\n",
-                       c2->error_data.status);
-               retry = 1;
+               switch (c2->error_data.status) {
+               case IOACCEL2_STATUS_SR_IO_ERROR:
+               case IOACCEL2_STATUS_SR_IO_ABORTED:
+               case IOACCEL2_STATUS_SR_OVERRUN:
+                       retry = 1;
+                       break;
+               case IOACCEL2_STATUS_SR_UNDERRUN:
+                       cmd->result = (DID_OK << 16);           /* host byte */
+                       cmd->result |= (COMMAND_COMPLETE << 8); /* msg byte */
+                       ioaccel2_resid = get_unaligned_le32(
+                                               &c2->error_data.resid_cnt[0]);
+                       scsi_set_resid(cmd, ioaccel2_resid);
+                       break;
+               case IOACCEL2_STATUS_SR_NO_PATH_TO_DEVICE:
+               case IOACCEL2_STATUS_SR_INVALID_DEVICE:
+               case IOACCEL2_STATUS_SR_IOACCEL_DISABLED:
+                       /* We will get an event from ctlr to trigger rescan */
+                       retry = 1;
+                       break;
+               default:
+                       retry = 1;
+               }
                break;
        case IOACCEL2_SERV_RESPONSE_TMF_COMPLETE:
                break;
        case IOACCEL2_SERV_RESPONSE_TMF_SUCCESS:
                break;
        case IOACCEL2_SERV_RESPONSE_TMF_REJECTED:
-               dev_warn(&h->pdev->dev, "task management function rejected.\n");
                retry = 1;
                break;
        case IOACCEL2_SERV_RESPONSE_TMF_WRONG_LUN:
-               dev_warn(&h->pdev->dev, "task management function invalid LUN\n");
                break;
        default:
-               dev_warn(&h->pdev->dev,
-                       "%s: Unrecognized server response: 0x%02x\n",
-                       "HP SSD Smart Path",
-                       c2->error_data.serv_response);
                retry = 1;
                break;
        }
@@ -1717,6 +2000,87 @@ static int handle_ioaccel_mode2_error(struct ctlr_info *h,
        return retry;   /* retry on raid path? */
 }
 
+static void hpsa_cmd_resolve_events(struct ctlr_info *h,
+               struct CommandList *c)
+{
+       bool do_wake = false;
+
+       /*
+        * Prevent the following race in the abort handler:
+        *
+        * 1. LLD is requested to abort a SCSI command
+        * 2. The SCSI command completes
+        * 3. The struct CommandList associated with step 2 is made available
+        * 4. New I/O request to LLD to another LUN re-uses struct CommandList
+        * 5. Abort handler follows scsi_cmnd->host_scribble and
+        *    finds struct CommandList and tries to aborts it
+        * Now we have aborted the wrong command.
+        *
+        * Reset c->scsi_cmd here so that the abort or reset handler will know
+        * this command has completed.  Then, check to see if the handler is
+        * waiting for this command, and, if so, wake it.
+        */
+       c->scsi_cmd = SCSI_CMD_IDLE;
+       mb();   /* Declare command idle before checking for pending events. */
+       if (c->abort_pending) {
+               do_wake = true;
+               c->abort_pending = false;
+       }
+       if (c->reset_pending) {
+               unsigned long flags;
+               struct hpsa_scsi_dev_t *dev;
+
+               /*
+                * There appears to be a reset pending; lock the lock and
+                * reconfirm.  If so, then decrement the count of outstanding
+                * commands and wake the reset command if this is the last one.
+                */
+               spin_lock_irqsave(&h->lock, flags);
+               dev = c->reset_pending;         /* Re-fetch under the lock. */
+               if (dev && atomic_dec_and_test(&dev->reset_cmds_out))
+                       do_wake = true;
+               c->reset_pending = NULL;
+               spin_unlock_irqrestore(&h->lock, flags);
+       }
+
+       if (do_wake)
+               wake_up_all(&h->event_sync_wait_queue);
+}
+
+static void hpsa_cmd_resolve_and_free(struct ctlr_info *h,
+                                     struct CommandList *c)
+{
+       hpsa_cmd_resolve_events(h, c);
+       cmd_tagged_free(h, c);
+}
+
+static void hpsa_cmd_free_and_done(struct ctlr_info *h,
+               struct CommandList *c, struct scsi_cmnd *cmd)
+{
+       hpsa_cmd_resolve_and_free(h, c);
+       cmd->scsi_done(cmd);
+}
+
+static void hpsa_retry_cmd(struct ctlr_info *h, struct CommandList *c)
+{
+       INIT_WORK(&c->work, hpsa_command_resubmit_worker);
+       queue_work_on(raw_smp_processor_id(), h->resubmit_wq, &c->work);
+}
+
+static void hpsa_set_scsi_cmd_aborted(struct scsi_cmnd *cmd)
+{
+       cmd->result = DID_ABORT << 16;
+}
+
+static void hpsa_cmd_abort_and_free(struct ctlr_info *h, struct CommandList *c,
+                                   struct scsi_cmnd *cmd)
+{
+       hpsa_set_scsi_cmd_aborted(cmd);
+       dev_warn(&h->pdev->dev, "CDB %16phN was aborted with status 0x%x\n",
+                        c->Request.CDB, c->err_info->ScsiStatus);
+       hpsa_cmd_resolve_and_free(h, c);
+}
+
 static void process_ioaccel2_completion(struct ctlr_info *h,
                struct CommandList *c, struct scsi_cmnd *cmd,
                struct hpsa_scsi_dev_t *dev)
@@ -1725,13 +2089,11 @@ static void process_ioaccel2_completion(struct ctlr_info *h,
 
        /* check for good status */
        if (likely(c2->error_data.serv_response == 0 &&
-                       c2->error_data.status == 0)) {
-               cmd_free(h, c);
-               cmd->scsi_done(cmd);
-               return;
-       }
+                       c2->error_data.status == 0))
+               return hpsa_cmd_free_and_done(h, c, cmd);
 
-       /* Any RAID offload error results in retry which will use
+       /*
+        * Any RAID offload error results in retry which will use
         * the normal I/O path so the controller can handle whatever's
         * wrong.
         */
@@ -1741,19 +2103,42 @@ static void process_ioaccel2_completion(struct ctlr_info *h,
                if (c2->error_data.status ==
                        IOACCEL2_STATUS_SR_IOACCEL_DISABLED)
                        dev->offload_enabled = 0;
-               goto retry_cmd;
+
+               return hpsa_retry_cmd(h, c);
        }
 
        if (handle_ioaccel_mode2_error(h, c, cmd, c2))
-               goto retry_cmd;
+               return hpsa_retry_cmd(h, c);
 
-       cmd_free(h, c);
-       cmd->scsi_done(cmd);
-       return;
+       return hpsa_cmd_free_and_done(h, c, cmd);
+}
 
-retry_cmd:
-       INIT_WORK(&c->work, hpsa_command_resubmit_worker);
-       queue_work_on(raw_smp_processor_id(), h->resubmit_wq, &c->work);
+/* Returns 0 on success, < 0 otherwise. */
+static int hpsa_evaluate_tmf_status(struct ctlr_info *h,
+                                       struct CommandList *cp)
+{
+       u8 tmf_status = cp->err_info->ScsiStatus;
+
+       switch (tmf_status) {
+       case CISS_TMF_COMPLETE:
+               /*
+                * CISS_TMF_COMPLETE never happens, instead,
+                * ei->CommandStatus == 0 for this case.
+                */
+       case CISS_TMF_SUCCESS:
+               return 0;
+       case CISS_TMF_INVALID_FRAME:
+       case CISS_TMF_NOT_SUPPORTED:
+       case CISS_TMF_FAILED:
+       case CISS_TMF_WRONG_LUN:
+       case CISS_TMF_OVERLAPPED_TAG:
+               break;
+       default:
+               dev_warn(&h->pdev->dev, "Unknown TMF status: 0x%02x\n",
+                               tmf_status);
+               break;
+       }
+       return -tmf_status;
 }
 
 static void complete_scsi_command(struct CommandList *cp)
@@ -1762,51 +2147,58 @@ static void complete_scsi_command(struct CommandList *cp)
        struct ctlr_info *h;
        struct ErrorInfo *ei;
        struct hpsa_scsi_dev_t *dev;
+       struct io_accel2_cmd *c2;
 
-       unsigned char sense_key;
-       unsigned char asc;      /* additional sense code */
-       unsigned char ascq;     /* additional sense code qualifier */
+       u8 sense_key;
+       u8 asc;      /* additional sense code */
+       u8 ascq;     /* additional sense code qualifier */
        unsigned long sense_data_size;
 
        ei = cp->err_info;
        cmd = cp->scsi_cmd;
        h = cp->h;
        dev = cmd->device->hostdata;
+       c2 = &h->ioaccel2_cmd_pool[cp->cmdindex];
 
        scsi_dma_unmap(cmd); /* undo the DMA mappings */
        if ((cp->cmd_type == CMD_SCSI) &&
                (le16_to_cpu(cp->Header.SGTotal) > h->max_cmd_sg_entries))
                hpsa_unmap_sg_chain_block(h, cp);
 
+       if ((cp->cmd_type == CMD_IOACCEL2) &&
+               (c2->sg[0].chain_indicator == IOACCEL2_CHAIN))
+               hpsa_unmap_ioaccel2_sg_chain_block(h, c2);
+
        cmd->result = (DID_OK << 16);           /* host byte */
        cmd->result |= (COMMAND_COMPLETE << 8); /* msg byte */
 
        if (cp->cmd_type == CMD_IOACCEL2 || cp->cmd_type == CMD_IOACCEL1)
                atomic_dec(&cp->phys_disk->ioaccel_cmds_out);
 
-       if (cp->cmd_type == CMD_IOACCEL2)
-               return process_ioaccel2_completion(h, cp, cmd, dev);
-
-       cmd->result |= ei->ScsiStatus;
+       /*
+        * We check for lockup status here as it may be set for
+        * CMD_SCSI, CMD_IOACCEL1 and CMD_IOACCEL2 commands by
+        * fail_all_oustanding_cmds()
+        */
+       if (unlikely(ei->CommandStatus == CMD_CTLR_LOCKUP)) {
+               /* DID_NO_CONNECT will prevent a retry */
+               cmd->result = DID_NO_CONNECT << 16;
+               return hpsa_cmd_free_and_done(h, cp, cmd);
+       }
 
-       scsi_set_resid(cmd, ei->ResidualCnt);
-       if (ei->CommandStatus == 0) {
-               if (cp->cmd_type == CMD_IOACCEL1)
-                       atomic_dec(&cp->phys_disk->ioaccel_cmds_out);
-               cmd_free(h, cp);
-               cmd->scsi_done(cmd);
-               return;
+       if ((unlikely(hpsa_is_pending_event(cp)))) {
+               if (cp->reset_pending)
+                       return hpsa_cmd_resolve_and_free(h, cp);
+               if (cp->abort_pending)
+                       return hpsa_cmd_abort_and_free(h, cp, cmd);
        }
 
-       /* copy the sense data */
-       if (SCSI_SENSE_BUFFERSIZE < sizeof(ei->SenseInfo))
-               sense_data_size = SCSI_SENSE_BUFFERSIZE;
-       else
-               sense_data_size = sizeof(ei->SenseInfo);
-       if (ei->SenseLen < sense_data_size)
-               sense_data_size = ei->SenseLen;
+       if (cp->cmd_type == CMD_IOACCEL2)
+               return process_ioaccel2_completion(h, cp, cmd, dev);
 
-       memcpy(cmd->sense_buffer, ei->SenseInfo, sense_data_size);
+       scsi_set_resid(cmd, ei->ResidualCnt);
+       if (ei->CommandStatus == 0)
+               return hpsa_cmd_free_and_done(h, cp, cmd);
 
        /* For I/O accelerator commands, copy over some fields to the normal
         * CISS header used below for error handling.
@@ -1828,10 +2220,7 @@ static void complete_scsi_command(struct CommandList *cp)
                if (is_logical_dev_addr_mode(dev->scsi3addr)) {
                        if (ei->CommandStatus == CMD_IOACCEL_DISABLED)
                                dev->offload_enabled = 0;
-                       INIT_WORK(&cp->work, hpsa_command_resubmit_worker);
-                       queue_work_on(raw_smp_processor_id(),
-                                       h->resubmit_wq, &cp->work);
-                       return;
+                       return hpsa_retry_cmd(h, cp);
                }
        }
 
@@ -1839,14 +2228,18 @@ static void complete_scsi_command(struct CommandList *cp)
        switch (ei->CommandStatus) {
 
        case CMD_TARGET_STATUS:
-               if (ei->ScsiStatus) {
-                       /* Get sense key */
-                       sense_key = 0xf & ei->SenseInfo[2];
-                       /* Get additional sense code */
-                       asc = ei->SenseInfo[12];
-                       /* Get addition sense code qualifier */
-                       ascq = ei->SenseInfo[13];
-               }
+               cmd->result |= ei->ScsiStatus;
+               /* copy the sense data */
+               if (SCSI_SENSE_BUFFERSIZE < sizeof(ei->SenseInfo))
+                       sense_data_size = SCSI_SENSE_BUFFERSIZE;
+               else
+                       sense_data_size = sizeof(ei->SenseInfo);
+               if (ei->SenseLen < sense_data_size)
+                       sense_data_size = ei->SenseLen;
+               memcpy(cmd->sense_buffer, ei->SenseInfo, sense_data_size);
+               if (ei->ScsiStatus)
+                       decode_sense_data(ei->SenseInfo, sense_data_size,
+                               &sense_key, &asc, &ascq);
                if (ei->ScsiStatus == SAM_STAT_CHECK_CONDITION) {
                        if (sense_key == ABORTED_COMMAND) {
                                cmd->result |= DID_SOFT_ERROR << 16;
@@ -1918,10 +2311,8 @@ static void complete_scsi_command(struct CommandList *cp)
                        cp->Request.CDB);
                break;
        case CMD_ABORTED:
-               cmd->result = DID_ABORT << 16;
-               dev_warn(&h->pdev->dev, "CDB %16phN was aborted with status 0x%x\n",
-                               cp->Request.CDB, ei->ScsiStatus);
-               break;
+               /* Return now to avoid calling scsi_done(). */
+               return hpsa_cmd_abort_and_free(h, cp, cmd);
        case CMD_ABORT_FAILED:
                cmd->result = DID_ERROR << 16;
                dev_warn(&h->pdev->dev, "CDB %16phN : abort failed\n",
@@ -1941,6 +2332,10 @@ static void complete_scsi_command(struct CommandList *cp)
                cmd->result = DID_ERROR << 16;
                dev_warn(&h->pdev->dev, "Command unabortable\n");
                break;
+       case CMD_TMF_STATUS:
+               if (hpsa_evaluate_tmf_status(h, cp)) /* TMF failed? */
+                       cmd->result = DID_ERROR << 16;
+               break;
        case CMD_IOACCEL_DISABLED:
                /* This only handles the direct pass-through case since RAID
                 * offload is handled above.  Just attempt a retry.
@@ -1954,8 +2349,8 @@ static void complete_scsi_command(struct CommandList *cp)
                dev_warn(&h->pdev->dev, "cp %p returned unknown status %x\n",
                                cp, ei->CommandStatus);
        }
-       cmd_free(h, cp);
-       cmd->scsi_done(cmd);
+
+       return hpsa_cmd_free_and_done(h, cp, cmd);
 }
 
 static void hpsa_pci_unmap(struct pci_dev *pdev,
@@ -1998,14 +2393,36 @@ static int hpsa_map_one(struct pci_dev *pdev,
        return 0;
 }
 
-static inline void hpsa_scsi_do_simple_cmd_core(struct ctlr_info *h,
-       struct CommandList *c)
+#define NO_TIMEOUT ((unsigned long) -1)
+#define DEFAULT_TIMEOUT 30000 /* milliseconds */
+static int hpsa_scsi_do_simple_cmd_core(struct ctlr_info *h,
+       struct CommandList *c, int reply_queue, unsigned long timeout_msecs)
 {
        DECLARE_COMPLETION_ONSTACK(wait);
 
        c->waiting = &wait;
-       enqueue_cmd_and_start_io(h, c);
-       wait_for_completion(&wait);
+       __enqueue_cmd_and_start_io(h, c, reply_queue);
+       if (timeout_msecs == NO_TIMEOUT) {
+               /* TODO: get rid of this no-timeout thing */
+               wait_for_completion_io(&wait);
+               return IO_OK;
+       }
+       if (!wait_for_completion_io_timeout(&wait,
+                                       msecs_to_jiffies(timeout_msecs))) {
+               dev_warn(&h->pdev->dev, "Command timed out.\n");
+               return -ETIMEDOUT;
+       }
+       return IO_OK;
+}
+
+static int hpsa_scsi_do_simple_cmd(struct ctlr_info *h, struct CommandList *c,
+                                  int reply_queue, unsigned long timeout_msecs)
+{
+       if (unlikely(lockup_detected(h))) {
+               c->err_info->CommandStatus = CMD_CTLR_LOCKUP;
+               return IO_OK;
+       }
+       return hpsa_scsi_do_simple_cmd_core(h, c, reply_queue, timeout_msecs);
 }
 
 static u32 lockup_detected(struct ctlr_info *h)
@@ -2020,25 +2437,19 @@ static u32 lockup_detected(struct ctlr_info *h)
        return rc;
 }
 
-static void hpsa_scsi_do_simple_cmd_core_if_no_lockup(struct ctlr_info *h,
-       struct CommandList *c)
-{
-       /* If controller lockup detected, fake a hardware error. */
-       if (unlikely(lockup_detected(h)))
-               c->err_info->CommandStatus = CMD_HARDWARE_ERR;
-       else
-               hpsa_scsi_do_simple_cmd_core(h, c);
-}
-
 #define MAX_DRIVER_CMD_RETRIES 25
-static void hpsa_scsi_do_simple_cmd_with_retry(struct ctlr_info *h,
-       struct CommandList *c, int data_direction)
+static int hpsa_scsi_do_simple_cmd_with_retry(struct ctlr_info *h,
+       struct CommandList *c, int data_direction, unsigned long timeout_msecs)
 {
        int backoff_time = 10, retry_count = 0;
+       int rc;
 
        do {
                memset(c->err_info, 0, sizeof(*c->err_info));
-               hpsa_scsi_do_simple_cmd_core(h, c);
+               rc = hpsa_scsi_do_simple_cmd(h, c, DEFAULT_REPLY_QUEUE,
+                                                 timeout_msecs);
+               if (rc)
+                       break;
                retry_count++;
                if (retry_count > 3) {
                        msleep(backoff_time);
@@ -2049,6 +2460,9 @@ static void hpsa_scsi_do_simple_cmd_with_retry(struct ctlr_info *h,
                        check_for_busy(h, c)) &&
                        retry_count <= MAX_DRIVER_CMD_RETRIES);
        hpsa_pci_unmap(h->pdev, c, 1, data_direction);
+       if (retry_count > MAX_DRIVER_CMD_RETRIES)
+               rc = -EIO;
+       return rc;
 }
 
 static void hpsa_print_cmd(struct ctlr_info *h, char *txt,
@@ -2072,16 +2486,23 @@ static void hpsa_scsi_interpret_error(struct ctlr_info *h,
 {
        const struct ErrorInfo *ei = cp->err_info;
        struct device *d = &cp->h->pdev->dev;
-       const u8 *sd = ei->SenseInfo;
+       u8 sense_key, asc, ascq;
+       int sense_len;
 
        switch (ei->CommandStatus) {
        case CMD_TARGET_STATUS:
+               if (ei->SenseLen > sizeof(ei->SenseInfo))
+                       sense_len = sizeof(ei->SenseInfo);
+               else
+                       sense_len = ei->SenseLen;
+               decode_sense_data(ei->SenseInfo, sense_len,
+                                       &sense_key, &asc, &ascq);
                hpsa_print_cmd(h, "SCSI status", cp);
                if (ei->ScsiStatus == SAM_STAT_CHECK_CONDITION)
-                       dev_warn(d, "SCSI Status = 02, Sense key = %02x, ASC = %02x, ASCQ = %02x\n",
-                               sd[2] & 0x0f, sd[12], sd[13]);
+                       dev_warn(d, "SCSI Status = 02, Sense key = 0x%02x, ASC = 0x%02x, ASCQ = 0x%02x\n",
+                               sense_key, asc, ascq);
                else
-                       dev_warn(d, "SCSI Status = %02x\n", ei->ScsiStatus);
+                       dev_warn(d, "SCSI Status = 0x%02x\n", ei->ScsiStatus);
                if (ei->ScsiStatus == 0)
                        dev_warn(d, "SCSI status is abnormally zero.  "
                        "(probably indicates selection timeout "
@@ -2125,6 +2546,9 @@ static void hpsa_scsi_interpret_error(struct ctlr_info *h,
        case CMD_UNABORTABLE:
                hpsa_print_cmd(h, "unabortable", cp);
                break;
+       case CMD_CTLR_LOCKUP:
+               hpsa_print_cmd(h, "controller lockup detected", cp);
+               break;
        default:
                hpsa_print_cmd(h, "unknown status", cp);
                dev_warn(d, "Unknown command status %x\n",
@@ -2142,17 +2566,15 @@ static int hpsa_scsi_do_inquiry(struct ctlr_info *h, unsigned char *scsi3addr,
 
        c = cmd_alloc(h);
 
-       if (c == NULL) {
-               dev_warn(&h->pdev->dev, "cmd_alloc returned NULL!\n");
-               return -ENOMEM;
-       }
-
        if (fill_cmd(c, HPSA_INQUIRY, h, buf, bufsize,
                        page, scsi3addr, TYPE_CMD)) {
                rc = -1;
                goto out;
        }
-       hpsa_scsi_do_simple_cmd_with_retry(h, c, PCI_DMA_FROMDEVICE);
+       rc = hpsa_scsi_do_simple_cmd_with_retry(h, c,
+                                       PCI_DMA_FROMDEVICE, NO_TIMEOUT);
+       if (rc)
+               goto out;
        ei = c->err_info;
        if (ei->CommandStatus != 0 && ei->CommandStatus != CMD_DATA_UNDERRUN) {
                hpsa_scsi_interpret_error(h, c);
@@ -2172,17 +2594,15 @@ static int hpsa_bmic_ctrl_mode_sense(struct ctlr_info *h,
        struct ErrorInfo *ei;
 
        c = cmd_alloc(h);
-       if (c == NULL) {                        /* trouble... */
-               dev_warn(&h->pdev->dev, "cmd_alloc returned NULL!\n");
-               return -ENOMEM;
-       }
-
        if (fill_cmd(c, BMIC_SENSE_CONTROLLER_PARAMETERS, h, buf, bufsize,
                        page, scsi3addr, TYPE_CMD)) {
                rc = -1;
                goto out;
        }
-       hpsa_scsi_do_simple_cmd_with_retry(h, c, PCI_DMA_FROMDEVICE);
+       rc = hpsa_scsi_do_simple_cmd_with_retry(h, c,
+                       PCI_DMA_FROMDEVICE, NO_TIMEOUT);
+       if (rc)
+               goto out;
        ei = c->err_info;
        if (ei->CommandStatus != 0 && ei->CommandStatus != CMD_DATA_UNDERRUN) {
                hpsa_scsi_interpret_error(h, c);
@@ -2191,10 +2611,10 @@ static int hpsa_bmic_ctrl_mode_sense(struct ctlr_info *h,
 out:
        cmd_free(h, c);
        return rc;
-       }
+}
 
 static int hpsa_send_reset(struct ctlr_info *h, unsigned char *scsi3addr,
-       u8 reset_type)
+       u8 reset_type, int reply_queue)
 {
        int rc = IO_OK;
        struct CommandList *c;
@@ -2202,16 +2622,16 @@ static int hpsa_send_reset(struct ctlr_info *h, unsigned char *scsi3addr,
 
        c = cmd_alloc(h);
 
-       if (c == NULL) {                        /* trouble... */
-               dev_warn(&h->pdev->dev, "cmd_alloc returned NULL!\n");
-               return -ENOMEM;
-       }
 
        /* fill_cmd can't fail here, no data buffer to map. */
        (void) fill_cmd(c, HPSA_DEVICE_RESET_MSG, h, NULL, 0, 0,
                        scsi3addr, TYPE_MSG);
        c->Request.CDB[1] = reset_type; /* fill_cmd defaults to LUN reset */
-       hpsa_scsi_do_simple_cmd_core(h, c);
+       rc = hpsa_scsi_do_simple_cmd(h, c, reply_queue, NO_TIMEOUT);
+       if (rc) {
+               dev_warn(&h->pdev->dev, "Failed to send reset command\n");
+               goto out;
+       }
        /* no unmap needed here because no data xfer. */
 
        ei = c->err_info;
@@ -2219,10 +2639,129 @@ static int hpsa_send_reset(struct ctlr_info *h, unsigned char *scsi3addr,
                hpsa_scsi_interpret_error(h, c);
                rc = -1;
        }
+out:
        cmd_free(h, c);
        return rc;
 }
 
+static bool hpsa_cmd_dev_match(struct ctlr_info *h, struct CommandList *c,
+                              struct hpsa_scsi_dev_t *dev,
+                              unsigned char *scsi3addr)
+{
+       int i;
+       bool match = false;
+       struct io_accel2_cmd *c2 = &h->ioaccel2_cmd_pool[c->cmdindex];
+       struct hpsa_tmf_struct *ac = (struct hpsa_tmf_struct *) c2;
+
+       if (hpsa_is_cmd_idle(c))
+               return false;
+
+       switch (c->cmd_type) {
+       case CMD_SCSI:
+       case CMD_IOCTL_PEND:
+               match = !memcmp(scsi3addr, &c->Header.LUN.LunAddrBytes,
+                               sizeof(c->Header.LUN.LunAddrBytes));
+               break;
+
+       case CMD_IOACCEL1:
+       case CMD_IOACCEL2:
+               if (c->phys_disk == dev) {
+                       /* HBA mode match */
+                       match = true;
+               } else {
+                       /* Possible RAID mode -- check each phys dev. */
+                       /* FIXME:  Do we need to take out a lock here?  If
+                        * so, we could just call hpsa_get_pdisk_of_ioaccel2()
+                        * instead. */
+                       for (i = 0; i < dev->nphysical_disks && !match; i++) {
+                               /* FIXME: an alternate test might be
+                                *
+                                * match = dev->phys_disk[i]->ioaccel_handle
+                                *              == c2->scsi_nexus;      */
+                               match = dev->phys_disk[i] == c->phys_disk;
+                       }
+               }
+               break;
+
+       case IOACCEL2_TMF:
+               for (i = 0; i < dev->nphysical_disks && !match; i++) {
+                       match = dev->phys_disk[i]->ioaccel_handle ==
+                                       le32_to_cpu(ac->it_nexus);
+               }
+               break;
+
+       case 0:         /* The command is in the middle of being initialized. */
+               match = false;
+               break;
+
+       default:
+               dev_err(&h->pdev->dev, "unexpected cmd_type: %d\n",
+                       c->cmd_type);
+               BUG();
+       }
+
+       return match;
+}
+
+static int hpsa_do_reset(struct ctlr_info *h, struct hpsa_scsi_dev_t *dev,
+       unsigned char *scsi3addr, u8 reset_type, int reply_queue)
+{
+       int i;
+       int rc = 0;
+
+       /* We can really only handle one reset at a time */
+       if (mutex_lock_interruptible(&h->reset_mutex) == -EINTR) {
+               dev_warn(&h->pdev->dev, "concurrent reset wait interrupted.\n");
+               return -EINTR;
+       }
+
+       BUG_ON(atomic_read(&dev->reset_cmds_out) != 0);
+
+       for (i = 0; i < h->nr_cmds; i++) {
+               struct CommandList *c = h->cmd_pool + i;
+               int refcount = atomic_inc_return(&c->refcount);
+
+               if (refcount > 1 && hpsa_cmd_dev_match(h, c, dev, scsi3addr)) {
+                       unsigned long flags;
+
+                       /*
+                        * Mark the target command as having a reset pending,
+                        * then lock a lock so that the command cannot complete
+                        * while we're considering it.  If the command is not
+                        * idle then count it; otherwise revoke the event.
+                        */
+                       c->reset_pending = dev;
+                       spin_lock_irqsave(&h->lock, flags);     /* Implied MB */
+                       if (!hpsa_is_cmd_idle(c))
+                               atomic_inc(&dev->reset_cmds_out);
+                       else
+                               c->reset_pending = NULL;
+                       spin_unlock_irqrestore(&h->lock, flags);
+               }
+
+               cmd_free(h, c);
+       }
+
+       rc = hpsa_send_reset(h, scsi3addr, reset_type, reply_queue);
+       if (!rc)
+               wait_event(h->event_sync_wait_queue,
+                       atomic_read(&dev->reset_cmds_out) == 0 ||
+                       lockup_detected(h));
+
+       if (unlikely(lockup_detected(h))) {
+                       dev_warn(&h->pdev->dev,
+                                "Controller lockup detected during reset wait\n");
+                       mutex_unlock(&h->reset_mutex);
+                       rc = -ENODEV;
+               }
+
+       if (unlikely(rc))
+               atomic_set(&dev->reset_cmds_out, 0);
+
+       mutex_unlock(&h->reset_mutex);
+       return rc;
+}
+
 static void hpsa_get_raid_level(struct ctlr_info *h,
        unsigned char *scsi3addr, unsigned char *raid_level)
 {
@@ -2328,23 +2867,23 @@ static int hpsa_get_raid_map(struct ctlr_info *h,
        struct ErrorInfo *ei;
 
        c = cmd_alloc(h);
-       if (c == NULL) {
-               dev_warn(&h->pdev->dev, "cmd_alloc returned NULL!\n");
-               return -ENOMEM;
-       }
+
        if (fill_cmd(c, HPSA_GET_RAID_MAP, h, &this_device->raid_map,
                        sizeof(this_device->raid_map), 0,
                        scsi3addr, TYPE_CMD)) {
-               dev_warn(&h->pdev->dev, "Out of memory in hpsa_get_raid_map()\n");
+               dev_warn(&h->pdev->dev, "hpsa_get_raid_map fill_cmd failed\n");
                cmd_free(h, c);
-               return -ENOMEM;
+               return -1;
        }
-       hpsa_scsi_do_simple_cmd_with_retry(h, c, PCI_DMA_FROMDEVICE);
+       rc = hpsa_scsi_do_simple_cmd_with_retry(h, c,
+                                       PCI_DMA_FROMDEVICE, NO_TIMEOUT);
+       if (rc)
+               goto out;
        ei = c->err_info;
        if (ei->CommandStatus != 0 && ei->CommandStatus != CMD_DATA_UNDERRUN) {
                hpsa_scsi_interpret_error(h, c);
-               cmd_free(h, c);
-               return -1;
+               rc = -1;
+               goto out;
        }
        cmd_free(h, c);
 
@@ -2356,6 +2895,9 @@ static int hpsa_get_raid_map(struct ctlr_info *h,
        }
        hpsa_debug_map_buff(h, rc, &this_device->raid_map);
        return rc;
+out:
+       cmd_free(h, c);
+       return rc;
 }
 
 static int hpsa_bmic_id_physical_device(struct ctlr_info *h,
@@ -2375,7 +2917,8 @@ static int hpsa_bmic_id_physical_device(struct ctlr_info *h,
        c->Request.CDB[2] = bmic_device_index & 0xff;
        c->Request.CDB[9] = (bmic_device_index >> 8) & 0xff;
 
-       hpsa_scsi_do_simple_cmd_with_retry(h, c, PCI_DMA_FROMDEVICE);
+       hpsa_scsi_do_simple_cmd_with_retry(h, c, PCI_DMA_FROMDEVICE,
+                                               NO_TIMEOUT);
        ei = c->err_info;
        if (ei->CommandStatus != 0 && ei->CommandStatus != CMD_DATA_UNDERRUN) {
                hpsa_scsi_interpret_error(h, c);
@@ -2438,6 +2981,7 @@ static void hpsa_get_ioaccel_status(struct ctlr_info *h,
 
        this_device->offload_config = 0;
        this_device->offload_enabled = 0;
+       this_device->offload_to_be_enabled = 0;
 
        buf = kzalloc(64, GFP_KERNEL);
        if (!buf)
@@ -2461,6 +3005,7 @@ static void hpsa_get_ioaccel_status(struct ctlr_info *h,
                if (hpsa_get_raid_map(h, scsi3addr, this_device))
                        this_device->offload_enabled = 0;
        }
+       this_device->offload_to_be_enabled = this_device->offload_enabled;
 out:
        kfree(buf);
        return;
@@ -2495,10 +3040,7 @@ static int hpsa_scsi_do_report_luns(struct ctlr_info *h, int logical,
        struct ErrorInfo *ei;
 
        c = cmd_alloc(h);
-       if (c == NULL) {                        /* trouble... */
-               dev_err(&h->pdev->dev, "cmd_alloc returned NULL!\n");
-               return -1;
-       }
+
        /* address the controller */
        memset(scsi3addr, 0, sizeof(scsi3addr));
        if (fill_cmd(c, logical ? HPSA_REPORT_LOG : HPSA_REPORT_PHYS, h,
@@ -2508,7 +3050,10 @@ static int hpsa_scsi_do_report_luns(struct ctlr_info *h, int logical,
        }
        if (extended_response)
                c->Request.CDB[1] = extended_response;
-       hpsa_scsi_do_simple_cmd_with_retry(h, c, PCI_DMA_FROMDEVICE);
+       rc = hpsa_scsi_do_simple_cmd_with_retry(h, c,
+                                       PCI_DMA_FROMDEVICE, NO_TIMEOUT);
+       if (rc)
+               goto out;
        ei = c->err_info;
        if (ei->CommandStatus != 0 &&
            ei->CommandStatus != CMD_DATA_UNDERRUN) {
@@ -2600,8 +3145,10 @@ static int hpsa_volume_offline(struct ctlr_info *h,
                                        unsigned char scsi3addr[])
 {
        struct CommandList *c;
-       unsigned char *sense, sense_key, asc, ascq;
-       int ldstat = 0;
+       unsigned char *sense;
+       u8 sense_key, asc, ascq;
+       int sense_len;
+       int rc, ldstat = 0;
        u16 cmd_status;
        u8 scsi_status;
 #define ASC_LUN_NOT_READY 0x04
@@ -2609,14 +3156,19 @@ static int hpsa_volume_offline(struct ctlr_info *h,
 #define ASCQ_LUN_NOT_READY_INITIALIZING_CMD_REQ 0x02
 
        c = cmd_alloc(h);
-       if (!c)
-               return 0;
+
        (void) fill_cmd(c, TEST_UNIT_READY, h, NULL, 0, 0, scsi3addr, TYPE_CMD);
-       hpsa_scsi_do_simple_cmd_core(h, c);
+       rc = hpsa_scsi_do_simple_cmd(h, c, DEFAULT_REPLY_QUEUE, NO_TIMEOUT);
+       if (rc) {
+               cmd_free(h, c);
+               return 0;
+       }
        sense = c->err_info->SenseInfo;
-       sense_key = sense[2];
-       asc = sense[12];
-       ascq = sense[13];
+       if (c->err_info->SenseLen > sizeof(c->err_info->SenseInfo))
+               sense_len = sizeof(c->err_info->SenseInfo);
+       else
+               sense_len = c->err_info->SenseLen;
+       decode_sense_data(sense, sense_len, &sense_key, &asc, &ascq);
        cmd_status = c->err_info->CommandStatus;
        scsi_status = c->err_info->ScsiStatus;
        cmd_free(h, c);
@@ -2656,6 +3208,52 @@ static int hpsa_volume_offline(struct ctlr_info *h,
        return 0;
 }
 
+/*
+ * Find out if a logical device supports aborts by simply trying one.
+ * Smart Array may claim not to support aborts on logical drives, but
+ * if a MSA2000 * is connected, the drives on that will be presented
+ * by the Smart Array as logical drives, and aborts may be sent to
+ * those devices successfully.  So the simplest way to find out is
+ * to simply try an abort and see how the device responds.
+ */
+static int hpsa_device_supports_aborts(struct ctlr_info *h,
+                                       unsigned char *scsi3addr)
+{
+       struct CommandList *c;
+       struct ErrorInfo *ei;
+       int rc = 0;
+
+       u64 tag = (u64) -1; /* bogus tag */
+
+       /* Assume that physical devices support aborts */
+       if (!is_logical_dev_addr_mode(scsi3addr))
+               return 1;
+
+       c = cmd_alloc(h);
+
+       (void) fill_cmd(c, HPSA_ABORT_MSG, h, &tag, 0, 0, scsi3addr, TYPE_MSG);
+       (void) hpsa_scsi_do_simple_cmd(h, c, DEFAULT_REPLY_QUEUE, NO_TIMEOUT);
+       /* no unmap needed here because no data xfer. */
+       ei = c->err_info;
+       switch (ei->CommandStatus) {
+       case CMD_INVALID:
+               rc = 0;
+               break;
+       case CMD_UNABORTABLE:
+       case CMD_ABORT_FAILED:
+               rc = 1;
+               break;
+       case CMD_TMF_STATUS:
+               rc = hpsa_evaluate_tmf_status(h, c);
+               break;
+       default:
+               rc = 0;
+               break;
+       }
+       cmd_free(h, c);
+       return rc;
+}
+
 static int hpsa_update_device_info(struct ctlr_info *h,
        unsigned char scsi3addr[], struct hpsa_scsi_dev_t *this_device,
        unsigned char *is_OBDR_device)
@@ -2708,6 +3306,8 @@ static int hpsa_update_device_info(struct ctlr_info *h,
                this_device->raid_level = RAID_UNKNOWN;
                this_device->offload_config = 0;
                this_device->offload_enabled = 0;
+               this_device->offload_to_be_enabled = 0;
+               this_device->hba_ioaccel_enabled = 0;
                this_device->volume_offline = 0;
                this_device->queue_depth = h->nr_cmds;
        }
@@ -2721,7 +3321,6 @@ static int hpsa_update_device_info(struct ctlr_info *h,
                                        strncmp(obdr_sig, OBDR_TAPE_SIG,
                                                OBDR_SIG_LEN) == 0);
        }
-
        kfree(inq_buff);
        return 0;
 
@@ -2730,6 +3329,31 @@ bail_out:
        return 1;
 }
 
+static void hpsa_update_device_supports_aborts(struct ctlr_info *h,
+                       struct hpsa_scsi_dev_t *dev, u8 *scsi3addr)
+{
+       unsigned long flags;
+       int rc, entry;
+       /*
+        * See if this device supports aborts.  If we already know
+        * the device, we already know if it supports aborts, otherwise
+        * we have to find out if it supports aborts by trying one.
+        */
+       spin_lock_irqsave(&h->devlock, flags);
+       rc = hpsa_scsi_find_entry(dev, h->dev, h->ndevices, &entry);
+       if ((rc == DEVICE_SAME || rc == DEVICE_UPDATED) &&
+               entry >= 0 && entry < h->ndevices) {
+               dev->supports_aborts = h->dev[entry]->supports_aborts;
+               spin_unlock_irqrestore(&h->devlock, flags);
+       } else {
+               spin_unlock_irqrestore(&h->devlock, flags);
+               dev->supports_aborts =
+                               hpsa_device_supports_aborts(h, scsi3addr);
+               if (dev->supports_aborts < 0)
+                       dev->supports_aborts = 0;
+       }
+}
+
 static unsigned char *ext_target_model[] = {
        "MSA2012",
        "MSA2024",
@@ -2835,6 +3459,7 @@ static int add_ext_target_dev(struct ctlr_info *h,
        (*n_ext_target_devs)++;
        hpsa_set_bus_target_lun(this_device,
                                tmpdevice->bus, tmpdevice->target, 0);
+       hpsa_update_device_supports_aborts(h, this_device, scsi3addr);
        set_bit(tmpdevice->target, lunzerobits);
        return 1;
 }
@@ -2850,88 +3475,23 @@ static int add_ext_target_dev(struct ctlr_info *h,
 static int hpsa_get_pdisk_of_ioaccel2(struct ctlr_info *h,
        struct CommandList *ioaccel2_cmd_to_abort, unsigned char *scsi3addr)
 {
-       struct ReportExtendedLUNdata *physicals = NULL;
-       int responsesize = 24;  /* size of physical extended response */
-       int reportsize = sizeof(*physicals) + HPSA_MAX_PHYS_LUN * responsesize;
-       u32 nphysicals = 0;     /* number of reported physical devs */
-       int found = 0;          /* found match (1) or not (0) */
-       u32 find;               /* handle we need to match */
+       struct io_accel2_cmd *c2 =
+                       &h->ioaccel2_cmd_pool[ioaccel2_cmd_to_abort->cmdindex];
+       unsigned long flags;
        int i;
-       struct scsi_cmnd *scmd; /* scsi command within request being aborted */
-       struct hpsa_scsi_dev_t *d; /* device of request being aborted */
-       struct io_accel2_cmd *c2a; /* ioaccel2 command to abort */
-       __le32 it_nexus;        /* 4 byte device handle for the ioaccel2 cmd */
-       __le32 scsi_nexus;      /* 4 byte device handle for the ioaccel2 cmd */
-
-       if (ioaccel2_cmd_to_abort->cmd_type != CMD_IOACCEL2)
-               return 0; /* no match */
-
-       /* point to the ioaccel2 device handle */
-       c2a = &h->ioaccel2_cmd_pool[ioaccel2_cmd_to_abort->cmdindex];
-       if (c2a == NULL)
-               return 0; /* no match */
-
-       scmd = (struct scsi_cmnd *) ioaccel2_cmd_to_abort->scsi_cmd;
-       if (scmd == NULL)
-               return 0; /* no match */
-
-       d = scmd->device->hostdata;
-       if (d == NULL)
-               return 0; /* no match */
-
-       it_nexus = cpu_to_le32(d->ioaccel_handle);
-       scsi_nexus = c2a->scsi_nexus;
-       find = le32_to_cpu(c2a->scsi_nexus);
-
-       if (h->raid_offload_debug > 0)
-               dev_info(&h->pdev->dev,
-                       "%s: scsi_nexus:0x%08x device id: 0x%02x%02x%02x%02x %02x%02x%02x%02x %02x%02x%02x%02x %02x%02x%02x%02x\n",
-                       __func__, scsi_nexus,
-                       d->device_id[0], d->device_id[1], d->device_id[2],
-                       d->device_id[3], d->device_id[4], d->device_id[5],
-                       d->device_id[6], d->device_id[7], d->device_id[8],
-                       d->device_id[9], d->device_id[10], d->device_id[11],
-                       d->device_id[12], d->device_id[13], d->device_id[14],
-                       d->device_id[15]);
-
-       /* Get the list of physical devices */
-       physicals = kzalloc(reportsize, GFP_KERNEL);
-       if (physicals == NULL)
-               return 0;
-       if (hpsa_scsi_do_report_phys_luns(h, physicals, reportsize)) {
-               dev_err(&h->pdev->dev,
-                       "Can't lookup %s device handle: report physical LUNs failed.\n",
-                       "HP SSD Smart Path");
-               kfree(physicals);
-               return 0;
-       }
-       nphysicals = be32_to_cpu(*((__be32 *)physicals->LUNListLength)) /
-                                                       responsesize;
-
-       /* find ioaccel2 handle in list of physicals: */
-       for (i = 0; i < nphysicals; i++) {
-               struct ext_report_lun_entry *entry = &physicals->LUN[i];
-
-               /* handle is in bytes 28-31 of each lun */
-               if (entry->ioaccel_handle != find)
-                       continue; /* didn't match */
-               found = 1;
-               memcpy(scsi3addr, entry->lunid, 8);
-               if (h->raid_offload_debug > 0)
-                       dev_info(&h->pdev->dev,
-                               "%s: Searched h=0x%08x, Found h=0x%08x, scsiaddr 0x%8phN\n",
-                               __func__, find,
-                               entry->ioaccel_handle, scsi3addr);
-               break; /* found it */
-       }
-
-       kfree(physicals);
-       if (found)
-               return 1;
-       else
-               return 0;
 
+       spin_lock_irqsave(&h->devlock, flags);
+       for (i = 0; i < h->ndevices; i++)
+               if (h->dev[i]->ioaccel_handle == le32_to_cpu(c2->scsi_nexus)) {
+                       memcpy(scsi3addr, h->dev[i]->scsi3addr,
+                               sizeof(h->dev[i]->scsi3addr));
+                       spin_unlock_irqrestore(&h->devlock, flags);
+                       return 1;
+               }
+       spin_unlock_irqrestore(&h->devlock, flags);
+       return 0;
 }
+
 /*
  * Do CISS_REPORT_PHYS and CISS_REPORT_LOG.  Data is returned in physdev,
  * logdev.  The number of luns in physdev and logdev are returned in
@@ -3036,6 +3596,8 @@ static void hpsa_get_ioaccel_drive_info(struct ctlr_info *h,
                (struct ext_report_lun_entry *) lunaddrbytes;
 
        dev->ioaccel_handle = rle->ioaccel_handle;
+       if (PHYS_IOACCEL(lunaddrbytes) && dev->ioaccel_handle)
+               dev->hba_ioaccel_enabled = 1;
        memset(id_phys, 0, sizeof(*id_phys));
        rc = hpsa_bmic_id_physical_device(h, lunaddrbytes,
                        GET_BMIC_DRIVE_NUMBER(lunaddrbytes), id_phys,
@@ -3050,6 +3612,7 @@ static void hpsa_get_ioaccel_drive_info(struct ctlr_info *h,
        else
                dev->queue_depth = DRIVE_QUEUE_DEPTH; /* conservative */
        atomic_set(&dev->ioaccel_cmds_out, 0);
+       atomic_set(&dev->reset_cmds_out, 0);
 }
 
 static void hpsa_update_scsi_devices(struct ctlr_info *h, int hostno)
@@ -3142,16 +3705,19 @@ static void hpsa_update_scsi_devices(struct ctlr_info *h, int hostno)
                /* Figure out where the LUN ID info is coming from */
                lunaddrbytes = figure_lunaddrbytes(h, raid_ctlr_position,
                        i, nphysicals, nlogicals, physdev_list, logdev_list);
-               /* skip masked physical devices. */
-               if (lunaddrbytes[3] & 0xC0 &&
-                       i < nphysicals + (raid_ctlr_position == 0))
-                       continue;
+
+               /* skip masked non-disk devices */
+               if (MASKED_DEVICE(lunaddrbytes))
+                       if (i < nphysicals + (raid_ctlr_position == 0) &&
+                               NON_DISK_PHYS_DEV(lunaddrbytes))
+                               continue;
 
                /* Get device type, vendor, model, device id */
                if (hpsa_update_device_info(h, lunaddrbytes, tmpdevice,
                                                        &is_OBDR))
                        continue; /* skip it if we can't talk to it. */
                figure_bus_target_lun(h, lunaddrbytes, tmpdevice);
+               hpsa_update_device_supports_aborts(h, tmpdevice, lunaddrbytes);
                this_device = currentsd[ncurrent];
 
                /*
@@ -3170,6 +3736,18 @@ static void hpsa_update_scsi_devices(struct ctlr_info *h, int hostno)
 
                *this_device = *tmpdevice;
 
+               /* do not expose masked devices */
+               if (MASKED_DEVICE(lunaddrbytes) &&
+                       i < nphysicals + (raid_ctlr_position == 0)) {
+                       if (h->hba_mode_enabled)
+                               dev_warn(&h->pdev->dev,
+                                       "Masked physical device detected\n");
+                       this_device->expose_state = HPSA_DO_NOT_EXPOSE;
+               } else {
+                       this_device->expose_state =
+                                       HPSA_SG_ATTACH | HPSA_ULD_ATTACH;
+               }
+
                switch (this_device->devtype) {
                case TYPE_ROM:
                        /* We don't *really* support actual CD-ROM devices,
@@ -3183,34 +3761,31 @@ static void hpsa_update_scsi_devices(struct ctlr_info *h, int hostno)
                                ncurrent++;
                        break;
                case TYPE_DISK:
-                       if (h->hba_mode_enabled) {
-                               /* never use raid mapper in HBA mode */
-                               this_device->offload_enabled = 0;
-                               ncurrent++;
-                               break;
-                       } else if (h->acciopath_status) {
-                               if (i >= nphysicals) {
-                                       ncurrent++;
-                                       break;
-                               }
-                       } else {
-                               if (i < nphysicals)
-                                       break;
+                       if (i >= nphysicals) {
                                ncurrent++;
                                break;
                        }
-                       if (h->transMethod & CFGTBL_Trans_io_accel1 ||
-                               h->transMethod & CFGTBL_Trans_io_accel2) {
-                               hpsa_get_ioaccel_drive_info(h, this_device,
-                                                       lunaddrbytes, id_phys);
-                               atomic_set(&this_device->ioaccel_cmds_out, 0);
-                               ncurrent++;
-                       }
+
+                       if (h->hba_mode_enabled)
+                               /* never use raid mapper in HBA mode */
+                               this_device->offload_enabled = 0;
+                       else if (!(h->transMethod & CFGTBL_Trans_io_accel1 ||
+                               h->transMethod & CFGTBL_Trans_io_accel2))
+                               break;
+
+                       hpsa_get_ioaccel_drive_info(h, this_device,
+                                               lunaddrbytes, id_phys);
+                       atomic_set(&this_device->ioaccel_cmds_out, 0);
+                       ncurrent++;
                        break;
                case TYPE_TAPE:
                case TYPE_MEDIUM_CHANGER:
                        ncurrent++;
                        break;
+               case TYPE_ENCLOSURE:
+                       if (h->hba_mode_enabled)
+                               ncurrent++;
+                       break;
                case TYPE_RAID:
                        /* Only present the Smartarray HBA as a RAID controller.
                         * If it's a RAID controller other than the HBA itself
@@ -3227,7 +3802,6 @@ static void hpsa_update_scsi_devices(struct ctlr_info *h, int hostno)
                if (ncurrent >= HPSA_MAX_DEVICES)
                        break;
        }
-       hpsa_update_log_drive_phys_drive_ptrs(h, currentsd, ncurrent);
        adjust_hpsa_scsi_table(h, hostno, currentsd, ncurrent);
 out:
        kfree(tmpdevice);
@@ -3260,7 +3834,7 @@ static int hpsa_scatter_gather(struct ctlr_info *h,
                struct scsi_cmnd *cmd)
 {
        struct scatterlist *sg;
-       int use_sg, i, sg_index, chained;
+       int use_sg, i, sg_limit, chained, last_sg;
        struct SGDescriptor *curr_sg;
 
        BUG_ON(scsi_sg_count(cmd) > h->maxsgentries);
@@ -3272,22 +3846,39 @@ static int hpsa_scatter_gather(struct ctlr_info *h,
        if (!use_sg)
                goto sglist_finished;
 
+       /*
+        * If the number of entries is greater than the max for a single list,
+        * then we have a chained list; we will set up all but one entry in the
+        * first list (the last entry is saved for link information);
+        * otherwise, we don't have a chained list and we'll set up at each of
+        * the entries in the one list.
+        */
        curr_sg = cp->SG;
-       chained = 0;
-       sg_index = 0;
-       scsi_for_each_sg(cmd, sg, use_sg, i) {
-               if (i == h->max_cmd_sg_entries - 1 &&
-                       use_sg > h->max_cmd_sg_entries) {
-                       chained = 1;
-                       curr_sg = h->cmd_sg_list[cp->cmdindex];
-                       sg_index = 0;
-               }
+       chained = use_sg > h->max_cmd_sg_entries;
+       sg_limit = chained ? h->max_cmd_sg_entries - 1 : use_sg;
+       last_sg = scsi_sg_count(cmd) - 1;
+       scsi_for_each_sg(cmd, sg, sg_limit, i) {
                hpsa_set_sg_descriptor(curr_sg, sg);
                curr_sg++;
        }
 
+       if (chained) {
+               /*
+                * Continue with the chained list.  Set curr_sg to the chained
+                * list.  Modify the limit to the total count less the entries
+                * we've already set up.  Resume the scan at the list entry
+                * where the previous loop left off.
+                */
+               curr_sg = h->cmd_sg_list[cp->cmdindex];
+               sg_limit = use_sg - sg_limit;
+               for_each_sg(sg, sg, sg_limit, i) {
+                       hpsa_set_sg_descriptor(curr_sg, sg);
+                       curr_sg++;
+               }
+       }
+
        /* Back the pointer up to the last entry and mark it as "last". */
-       (--curr_sg)->Ext = cpu_to_le32(HPSA_SG_LAST);
+       (curr_sg - 1)->Ext = cpu_to_le32(HPSA_SG_LAST);
 
        if (use_sg + chained > h->maxSG)
                h->maxSG = use_sg + chained;
@@ -3530,10 +4121,7 @@ static int hpsa_scsi_ioaccel2_queue_command(struct ctlr_info *h,
        u32 len;
        u32 total_len = 0;
 
-       if (scsi_sg_count(cmd) > h->ioaccel_maxsg) {
-               atomic_dec(&phys_disk->ioaccel_cmds_out);
-               return IO_ACCEL_INELIGIBLE;
-       }
+       BUG_ON(scsi_sg_count(cmd) > h->maxsgentries);
 
        if (fixup_ioaccel_cdb(cdb, &cdb_len)) {
                atomic_dec(&phys_disk->ioaccel_cmds_out);
@@ -3556,8 +4144,19 @@ static int hpsa_scsi_ioaccel2_queue_command(struct ctlr_info *h,
        }
 
        if (use_sg) {
-               BUG_ON(use_sg > IOACCEL2_MAXSGENTRIES);
                curr_sg = cp->sg;
+               if (use_sg > h->ioaccel_maxsg) {
+                       addr64 = le64_to_cpu(
+                               h->ioaccel2_cmd_sg_list[c->cmdindex]->address);
+                       curr_sg->address = cpu_to_le64(addr64);
+                       curr_sg->length = 0;
+                       curr_sg->reserved[0] = 0;
+                       curr_sg->reserved[1] = 0;
+                       curr_sg->reserved[2] = 0;
+                       curr_sg->chain_indicator = 0x80;
+
+                       curr_sg = h->ioaccel2_cmd_sg_list[c->cmdindex];
+               }
                scsi_for_each_sg(cmd, sg, use_sg, i) {
                        addr64 = (u64) sg_dma_address(sg);
                        len  = sg_dma_len(sg);
@@ -3602,14 +4201,22 @@ static int hpsa_scsi_ioaccel2_queue_command(struct ctlr_info *h,
        cp->Tag = cpu_to_le32(c->cmdindex << DIRECT_LOOKUP_SHIFT);
        memcpy(cp->cdb, cdb, sizeof(cp->cdb));
 
-       /* fill in sg elements */
-       cp->sg_count = (u8) use_sg;
-
        cp->data_len = cpu_to_le32(total_len);
        cp->err_ptr = cpu_to_le64(c->busaddr +
                        offsetof(struct io_accel2_cmd, error_data));
        cp->err_len = cpu_to_le32(sizeof(cp->error_data));
 
+       /* fill in sg elements */
+       if (use_sg > h->ioaccel_maxsg) {
+               cp->sg_count = 1;
+               if (hpsa_map_ioaccel2_sg_chain_block(h, cp, c)) {
+                       atomic_dec(&phys_disk->ioaccel_cmds_out);
+                       scsi_dma_unmap(cmd);
+                       return -1;
+               }
+       } else
+               cp->sg_count = (u8) use_sg;
+
        enqueue_cmd_and_start_io(h, c);
        return 0;
 }
@@ -3992,7 +4599,11 @@ static int hpsa_scsi_ioaccel_raid_map(struct ctlr_info *h,
                                                dev->phys_disk[map_index]);
 }
 
-/* Submit commands down the "normal" RAID stack path */
+/*
+ * Submit commands down the "normal" RAID stack path
+ * All callers to hpsa_ciss_submit must check lockup_detected
+ * beforehand, before (opt.) and after calling cmd_alloc
+ */
 static int hpsa_ciss_submit(struct ctlr_info *h,
        struct CommandList *c, struct scsi_cmnd *cmd,
        unsigned char scsi3addr[])
@@ -4007,7 +4618,6 @@ static int hpsa_ciss_submit(struct ctlr_info *h,
        /* Fill in the request block... */
 
        c->Request.Timeout = 0;
-       memset(c->Request.CDB, 0, sizeof(c->Request.CDB));
        BUG_ON(cmd->cmd_len > sizeof(c->Request.CDB));
        c->Request.CDBLen = cmd->cmd_len;
        memcpy(c->Request.CDB, cmd->cmnd, cmd->cmd_len);
@@ -4050,7 +4660,7 @@ static int hpsa_ciss_submit(struct ctlr_info *h,
        }
 
        if (hpsa_scatter_gather(h, c, cmd) < 0) { /* Fill SG list */
-               cmd_free(h, c);
+               hpsa_cmd_resolve_and_free(h, c);
                return SCSI_MLQUEUE_HOST_BUSY;
        }
        enqueue_cmd_and_start_io(h, c);
@@ -4058,25 +4668,125 @@ static int hpsa_ciss_submit(struct ctlr_info *h,
        return 0;
 }
 
-static void hpsa_command_resubmit_worker(struct work_struct *work)
+static void hpsa_cmd_init(struct ctlr_info *h, int index,
+                               struct CommandList *c)
 {
-       struct scsi_cmnd *cmd;
-       struct hpsa_scsi_dev_t *dev;
-       struct CommandList *c =
-                       container_of(work, struct CommandList, work);
+       dma_addr_t cmd_dma_handle, err_dma_handle;
 
-       cmd = c->scsi_cmd;
-       dev = cmd->device->hostdata;
-       if (!dev) {
-               cmd->result = DID_NO_CONNECT << 16;
-               cmd->scsi_done(cmd);
-               return;
+       /* Zero out all of commandlist except the last field, refcount */
+       memset(c, 0, offsetof(struct CommandList, refcount));
+       c->Header.tag = cpu_to_le64((u64) (index << DIRECT_LOOKUP_SHIFT));
+       cmd_dma_handle = h->cmd_pool_dhandle + index * sizeof(*c);
+       c->err_info = h->errinfo_pool + index;
+       memset(c->err_info, 0, sizeof(*c->err_info));
+       err_dma_handle = h->errinfo_pool_dhandle
+           + index * sizeof(*c->err_info);
+       c->cmdindex = index;
+       c->busaddr = (u32) cmd_dma_handle;
+       c->ErrDesc.Addr = cpu_to_le64((u64) err_dma_handle);
+       c->ErrDesc.Len = cpu_to_le32((u32) sizeof(*c->err_info));
+       c->h = h;
+       c->scsi_cmd = SCSI_CMD_IDLE;
+}
+
+static void hpsa_preinitialize_commands(struct ctlr_info *h)
+{
+       int i;
+
+       for (i = 0; i < h->nr_cmds; i++) {
+               struct CommandList *c = h->cmd_pool + i;
+
+               hpsa_cmd_init(h, i, c);
+               atomic_set(&c->refcount, 0);
+       }
+}
+
+static inline void hpsa_cmd_partial_init(struct ctlr_info *h, int index,
+                               struct CommandList *c)
+{
+       dma_addr_t cmd_dma_handle = h->cmd_pool_dhandle + index * sizeof(*c);
+
+       BUG_ON(c->cmdindex != index);
+
+       memset(c->Request.CDB, 0, sizeof(c->Request.CDB));
+       memset(c->err_info, 0, sizeof(*c->err_info));
+       c->busaddr = (u32) cmd_dma_handle;
+}
+
+static int hpsa_ioaccel_submit(struct ctlr_info *h,
+               struct CommandList *c, struct scsi_cmnd *cmd,
+               unsigned char *scsi3addr)
+{
+       struct hpsa_scsi_dev_t *dev = cmd->device->hostdata;
+       int rc = IO_ACCEL_INELIGIBLE;
+
+       cmd->host_scribble = (unsigned char *) c;
+
+       if (dev->offload_enabled) {
+               hpsa_cmd_init(h, c->cmdindex, c);
+               c->cmd_type = CMD_SCSI;
+               c->scsi_cmd = cmd;
+               rc = hpsa_scsi_ioaccel_raid_map(h, c);
+               if (rc < 0)     /* scsi_dma_map failed. */
+                       rc = SCSI_MLQUEUE_HOST_BUSY;
+       } else if (dev->hba_ioaccel_enabled) {
+               hpsa_cmd_init(h, c->cmdindex, c);
+               c->cmd_type = CMD_SCSI;
+               c->scsi_cmd = cmd;
+               rc = hpsa_scsi_ioaccel_direct_map(h, c);
+               if (rc < 0)     /* scsi_dma_map failed. */
+                       rc = SCSI_MLQUEUE_HOST_BUSY;
+       }
+       return rc;
+}
+
+static void hpsa_command_resubmit_worker(struct work_struct *work)
+{
+       struct scsi_cmnd *cmd;
+       struct hpsa_scsi_dev_t *dev;
+       struct CommandList *c = container_of(work, struct CommandList, work);
+
+       cmd = c->scsi_cmd;
+       dev = cmd->device->hostdata;
+       if (!dev) {
+               cmd->result = DID_NO_CONNECT << 16;
+               return hpsa_cmd_free_and_done(c->h, c, cmd);
+       }
+       if (c->reset_pending)
+               return hpsa_cmd_resolve_and_free(c->h, c);
+       if (c->abort_pending)
+               return hpsa_cmd_abort_and_free(c->h, c, cmd);
+       if (c->cmd_type == CMD_IOACCEL2) {
+               struct ctlr_info *h = c->h;
+               struct io_accel2_cmd *c2 = &h->ioaccel2_cmd_pool[c->cmdindex];
+               int rc;
+
+               if (c2->error_data.serv_response ==
+                               IOACCEL2_STATUS_SR_TASK_COMP_SET_FULL) {
+                       rc = hpsa_ioaccel_submit(h, c, cmd, dev->scsi3addr);
+                       if (rc == 0)
+                               return;
+                       if (rc == SCSI_MLQUEUE_HOST_BUSY) {
+                               /*
+                                * If we get here, it means dma mapping failed.
+                                * Try again via scsi mid layer, which will
+                                * then get SCSI_MLQUEUE_HOST_BUSY.
+                                */
+                               cmd->result = DID_IMM_RETRY << 16;
+                               return hpsa_cmd_free_and_done(h, c, cmd);
+                       }
+                       /* else, fall thru and resubmit down CISS path */
+               }
        }
+       hpsa_cmd_partial_init(c->h, c->cmdindex, c);
        if (hpsa_ciss_submit(c->h, c, cmd, dev->scsi3addr)) {
                /*
                 * If we get here, it means dma mapping failed. Try
                 * again via scsi mid layer, which will then get
                 * SCSI_MLQUEUE_HOST_BUSY.
+                *
+                * hpsa_ciss_submit will have already freed c
+                * if it encountered a dma mapping failure.
                 */
                cmd->result = DID_IMM_RETRY << 16;
                cmd->scsi_done(cmd);
@@ -4094,30 +4804,24 @@ static int hpsa_scsi_queue_command(struct Scsi_Host *sh, struct scsi_cmnd *cmd)
 
        /* Get the ptr to our adapter structure out of cmd->host. */
        h = sdev_to_hba(cmd->device);
+
+       BUG_ON(cmd->request->tag < 0);
+
        dev = cmd->device->hostdata;
        if (!dev) {
                cmd->result = DID_NO_CONNECT << 16;
                cmd->scsi_done(cmd);
                return 0;
        }
+
        memcpy(scsi3addr, dev->scsi3addr, sizeof(scsi3addr));
 
        if (unlikely(lockup_detected(h))) {
-               cmd->result = DID_ERROR << 16;
-               cmd->scsi_done(cmd);
-               return 0;
-       }
-       c = cmd_alloc(h);
-       if (c == NULL) {                        /* trouble... */
-               dev_err(&h->pdev->dev, "cmd_alloc returned NULL!\n");
-               return SCSI_MLQUEUE_HOST_BUSY;
-       }
-       if (unlikely(lockup_detected(h))) {
-               cmd->result = DID_ERROR << 16;
-               cmd_free(h, c);
+               cmd->result = DID_NO_CONNECT << 16;
                cmd->scsi_done(cmd);
                return 0;
        }
+       c = cmd_tagged_alloc(h, cmd);
 
        /*
         * Call alternate submit routine for I/O accelerated commands.
@@ -4126,27 +4830,12 @@ static int hpsa_scsi_queue_command(struct Scsi_Host *sh, struct scsi_cmnd *cmd)
        if (likely(cmd->retries == 0 &&
                cmd->request->cmd_type == REQ_TYPE_FS &&
                h->acciopath_status)) {
-
-               cmd->host_scribble = (unsigned char *) c;
-               c->cmd_type = CMD_SCSI;
-               c->scsi_cmd = cmd;
-
-               if (dev->offload_enabled) {
-                       rc = hpsa_scsi_ioaccel_raid_map(h, c);
-                       if (rc == 0)
-                               return 0; /* Sent on ioaccel path */
-                       if (rc < 0) {   /* scsi_dma_map failed. */
-                               cmd_free(h, c);
-                               return SCSI_MLQUEUE_HOST_BUSY;
-                       }
-               } else if (dev->ioaccel_handle) {
-                       rc = hpsa_scsi_ioaccel_direct_map(h, c);
-                       if (rc == 0)
-                               return 0; /* Sent on direct map path */
-                       if (rc < 0) {   /* scsi_dma_map failed. */
-                               cmd_free(h, c);
-                               return SCSI_MLQUEUE_HOST_BUSY;
-                       }
+               rc = hpsa_ioaccel_submit(h, c, cmd, scsi3addr);
+               if (rc == 0)
+                       return 0;
+               if (rc == SCSI_MLQUEUE_HOST_BUSY) {
+                       hpsa_cmd_resolve_and_free(h, c);
+                       return SCSI_MLQUEUE_HOST_BUSY;
                }
        }
        return hpsa_ciss_submit(h, c, cmd, scsi3addr);
@@ -4228,22 +4917,16 @@ static int hpsa_scan_finished(struct Scsi_Host *sh,
        return finished;
 }
 
-static void hpsa_unregister_scsi(struct ctlr_info *h)
-{
-       /* we are being forcibly unloaded, and may not refuse. */
-       scsi_remove_host(h->scsi_host);
-       scsi_host_put(h->scsi_host);
-       h->scsi_host = NULL;
-}
-
-static int hpsa_register_scsi(struct ctlr_info *h)
+static int hpsa_scsi_host_alloc(struct ctlr_info *h)
 {
        struct Scsi_Host *sh;
        int error;
 
        sh = scsi_host_alloc(&hpsa_driver_template, sizeof(h));
-       if (sh == NULL)
-               goto fail;
+       if (sh == NULL) {
+               dev_err(&h->pdev->dev, "scsi_host_alloc failed\n");
+               return -ENOMEM;
+       }
 
        sh->io_port = 0;
        sh->n_io_port = 0;
@@ -4252,80 +4935,156 @@ static int hpsa_register_scsi(struct ctlr_info *h)
        sh->max_cmd_len = MAX_COMMAND_SIZE;
        sh->max_lun = HPSA_MAX_LUN;
        sh->max_id = HPSA_MAX_LUN;
-       sh->can_queue = h->nr_cmds -
-                       HPSA_CMDS_RESERVED_FOR_ABORTS -
-                       HPSA_CMDS_RESERVED_FOR_DRIVER -
-                       HPSA_MAX_CONCURRENT_PASSTHRUS;
+       sh->can_queue = h->nr_cmds - HPSA_NRESERVED_CMDS;
        sh->cmd_per_lun = sh->can_queue;
        sh->sg_tablesize = h->maxsgentries;
-       h->scsi_host = sh;
        sh->hostdata[0] = (unsigned long) h;
        sh->irq = h->intr[h->intr_mode];
        sh->unique_id = sh->irq;
-       error = scsi_add_host(sh, &h->pdev->dev);
-       if (error)
-               goto fail_host_put;
-       scsi_scan_host(sh);
+       error = scsi_init_shared_tag_map(sh, sh->can_queue);
+       if (error) {
+               dev_err(&h->pdev->dev,
+                       "%s: scsi_init_shared_tag_map failed for controller %d\n",
+                       __func__, h->ctlr);
+                       scsi_host_put(sh);
+                       return error;
+       }
+       h->scsi_host = sh;
        return 0;
+}
 
- fail_host_put:
-       dev_err(&h->pdev->dev, "%s: scsi_add_host"
-               " failed for controller %d\n", __func__, h->ctlr);
-       scsi_host_put(sh);
-       return error;
- fail:
-       dev_err(&h->pdev->dev, "%s: scsi_host_alloc"
-               " failed for controller %d\n", __func__, h->ctlr);
-       return -ENOMEM;
+static int hpsa_scsi_add_host(struct ctlr_info *h)
+{
+       int rv;
+
+       rv = scsi_add_host(h->scsi_host, &h->pdev->dev);
+       if (rv) {
+               dev_err(&h->pdev->dev, "scsi_add_host failed\n");
+               return rv;
+       }
+       scsi_scan_host(h->scsi_host);
+       return 0;
 }
 
-static int wait_for_device_to_become_ready(struct ctlr_info *h,
-       unsigned char lunaddr[])
+/*
+ * The block layer has already gone to the trouble of picking out a unique,
+ * small-integer tag for this request.  We use an offset from that value as
+ * an index to select our command block.  (The offset allows us to reserve the
+ * low-numbered entries for our own uses.)
+ */
+static int hpsa_get_cmd_index(struct scsi_cmnd *scmd)
+{
+       int idx = scmd->request->tag;
+
+       if (idx < 0)
+               return idx;
+
+       /* Offset to leave space for internal cmds. */
+       return idx += HPSA_NRESERVED_CMDS;
+}
+
+/*
+ * Send a TEST_UNIT_READY command to the specified LUN using the specified
+ * reply queue; returns zero if the unit is ready, and non-zero otherwise.
+ */
+static int hpsa_send_test_unit_ready(struct ctlr_info *h,
+                               struct CommandList *c, unsigned char lunaddr[],
+                               int reply_queue)
+{
+       int rc;
+
+       /* Send the Test Unit Ready, fill_cmd can't fail, no mapping */
+       (void) fill_cmd(c, TEST_UNIT_READY, h,
+                       NULL, 0, 0, lunaddr, TYPE_CMD);
+       rc = hpsa_scsi_do_simple_cmd(h, c, reply_queue, NO_TIMEOUT);
+       if (rc)
+               return rc;
+       /* no unmap needed here because no data xfer. */
+
+       /* Check if the unit is already ready. */
+       if (c->err_info->CommandStatus == CMD_SUCCESS)
+               return 0;
+
+       /*
+        * The first command sent after reset will receive "unit attention" to
+        * indicate that the LUN has been reset...this is actually what we're
+        * looking for (but, success is good too).
+        */
+       if (c->err_info->CommandStatus == CMD_TARGET_STATUS &&
+               c->err_info->ScsiStatus == SAM_STAT_CHECK_CONDITION &&
+                       (c->err_info->SenseInfo[2] == NO_SENSE ||
+                        c->err_info->SenseInfo[2] == UNIT_ATTENTION))
+               return 0;
+
+       return 1;
+}
+
+/*
+ * Wait for a TEST_UNIT_READY command to complete, retrying as necessary;
+ * returns zero when the unit is ready, and non-zero when giving up.
+ */
+static int hpsa_wait_for_test_unit_ready(struct ctlr_info *h,
+                               struct CommandList *c,
+                               unsigned char lunaddr[], int reply_queue)
 {
        int rc;
        int count = 0;
        int waittime = 1; /* seconds */
-       struct CommandList *c;
-
-       c = cmd_alloc(h);
-       if (!c) {
-               dev_warn(&h->pdev->dev, "out of memory in "
-                       "wait_for_device_to_become_ready.\n");
-               return IO_ERROR;
-       }
 
        /* Send test unit ready until device ready, or give up. */
-       while (count < HPSA_TUR_RETRY_LIMIT) {
+       for (count = 0; count < HPSA_TUR_RETRY_LIMIT; count++) {
 
-               /* Wait for a bit.  do this first, because if we send
+               /*
+                * Wait for a bit.  do this first, because if we send
                 * the TUR right away, the reset will just abort it.
                 */
                msleep(1000 * waittime);
-               count++;
-               rc = 0; /* Device ready. */
+
+               rc = hpsa_send_test_unit_ready(h, c, lunaddr, reply_queue);
+               if (!rc)
+                       break;
 
                /* Increase wait time with each try, up to a point. */
                if (waittime < HPSA_MAX_WAIT_INTERVAL_SECS)
-                       waittime = waittime * 2;
+                       waittime *= 2;
 
-               /* Send the Test Unit Ready, fill_cmd can't fail, no mapping */
-               (void) fill_cmd(c, TEST_UNIT_READY, h,
-                               NULL, 0, 0, lunaddr, TYPE_CMD);
-               hpsa_scsi_do_simple_cmd_core(h, c);
-               /* no unmap needed here because no data xfer. */
+               dev_warn(&h->pdev->dev,
+                        "waiting %d secs for device to become ready.\n",
+                        waittime);
+       }
 
-               if (c->err_info->CommandStatus == CMD_SUCCESS)
-                       break;
+       return rc;
+}
 
-               if (c->err_info->CommandStatus == CMD_TARGET_STATUS &&
-                       c->err_info->ScsiStatus == SAM_STAT_CHECK_CONDITION &&
-                       (c->err_info->SenseInfo[2] == NO_SENSE ||
-                       c->err_info->SenseInfo[2] == UNIT_ATTENTION))
-                       break;
+static int wait_for_device_to_become_ready(struct ctlr_info *h,
+                                          unsigned char lunaddr[],
+                                          int reply_queue)
+{
+       int first_queue;
+       int last_queue;
+       int rq;
+       int rc = 0;
+       struct CommandList *c;
+
+       c = cmd_alloc(h);
 
-               dev_warn(&h->pdev->dev, "waiting %d secs "
-                       "for device to become ready.\n", waittime);
-               rc = 1; /* device not ready. */
+       /*
+        * If no specific reply queue was requested, then send the TUR
+        * repeatedly, requesting a reply on each reply queue; otherwise execute
+        * the loop exactly once using only the specified queue.
+        */
+       if (reply_queue == DEFAULT_REPLY_QUEUE) {
+               first_queue = 0;
+               last_queue = h->nreply_queues - 1;
+       } else {
+               first_queue = reply_queue;
+               last_queue = reply_queue;
+       }
+
+       for (rq = first_queue; rq <= last_queue; rq++) {
+               rc = hpsa_wait_for_test_unit_ready(h, c, lunaddr, rq);
+               if (rc)
+                       break;
        }
 
        if (rc)
@@ -4345,6 +5104,7 @@ static int hpsa_eh_device_reset_handler(struct scsi_cmnd *scsicmd)
        int rc;
        struct ctlr_info *h;
        struct hpsa_scsi_dev_t *dev;
+       char msg[40];
 
        /* find the controller to which the command to be aborted was sent */
        h = sdev_to_hba(scsicmd->device);
@@ -4356,19 +5116,38 @@ static int hpsa_eh_device_reset_handler(struct scsi_cmnd *scsicmd)
 
        dev = scsicmd->device->hostdata;
        if (!dev) {
-               dev_err(&h->pdev->dev, "hpsa_eh_device_reset_handler: "
-                       "device lookup failed.\n");
+               dev_err(&h->pdev->dev, "%s: device lookup failed\n", __func__);
                return FAILED;
        }
-       dev_warn(&h->pdev->dev, "resetting device %d:%d:%d:%d\n",
-               h->scsi_host->host_no, dev->bus, dev->target, dev->lun);
-       /* send a reset to the SCSI LUN which the command was sent to */
-       rc = hpsa_send_reset(h, dev->scsi3addr, HPSA_RESET_TYPE_LUN);
-       if (rc == 0 && wait_for_device_to_become_ready(h, dev->scsi3addr) == 0)
+
+       /* if controller locked up, we can guarantee command won't complete */
+       if (lockup_detected(h)) {
+               sprintf(msg, "cmd %d RESET FAILED, lockup detected",
+                               hpsa_get_cmd_index(scsicmd));
+               hpsa_show_dev_msg(KERN_WARNING, h, dev, msg);
+               return FAILED;
+       }
+
+       /* this reset request might be the result of a lockup; check */
+       if (detect_controller_lockup(h)) {
+               sprintf(msg, "cmd %d RESET FAILED, new lockup detected",
+                               hpsa_get_cmd_index(scsicmd));
+               hpsa_show_dev_msg(KERN_WARNING, h, dev, msg);
+               return FAILED;
+       }
+
+       /* Do not attempt on controller */
+       if (is_hba_lunid(dev->scsi3addr))
                return SUCCESS;
 
-       dev_warn(&h->pdev->dev, "resetting device failed.\n");
-       return FAILED;
+       hpsa_show_dev_msg(KERN_WARNING, h, dev, "resetting");
+
+       /* send a reset to the SCSI LUN which the command was sent to */
+       rc = hpsa_do_reset(h, dev, dev->scsi3addr, HPSA_RESET_TYPE_LUN,
+                          DEFAULT_REPLY_QUEUE);
+       sprintf(msg, "reset %s", rc == 0 ? "completed successfully" : "failed");
+       hpsa_show_dev_msg(KERN_WARNING, h, dev, msg);
+       return rc == 0 ? SUCCESS : FAILED;
 }
 
 static void swizzle_abort_tag(u8 *tag)
@@ -4412,7 +5191,7 @@ static void hpsa_get_tag(struct ctlr_info *h,
 }
 
 static int hpsa_send_abort(struct ctlr_info *h, unsigned char *scsi3addr,
-       struct CommandList *abort, int swizzle)
+       struct CommandList *abort, int reply_queue)
 {
        int rc = IO_OK;
        struct CommandList *c;
@@ -4420,19 +5199,15 @@ static int hpsa_send_abort(struct ctlr_info *h, unsigned char *scsi3addr,
        __le32 tagupper, taglower;
 
        c = cmd_alloc(h);
-       if (c == NULL) {        /* trouble... */
-               dev_warn(&h->pdev->dev, "cmd_alloc returned NULL!\n");
-               return -ENOMEM;
-       }
 
        /* fill_cmd can't fail here, no buffer to map */
-       (void) fill_cmd(c, HPSA_ABORT_MSG, h, abort,
+       (void) fill_cmd(c, HPSA_ABORT_MSG, h, &abort->Header.tag,
                0, 0, scsi3addr, TYPE_MSG);
-       if (swizzle)
+       if (h->needs_abort_tags_swizzled)
                swizzle_abort_tag(&c->Request.CDB[4]);
-       hpsa_scsi_do_simple_cmd_core(h, c);
+       (void) hpsa_scsi_do_simple_cmd(h, c, reply_queue, NO_TIMEOUT);
        hpsa_get_tag(h, abort, &taglower, &tagupper);
-       dev_dbg(&h->pdev->dev, "%s: Tag:0x%08x:%08x: do_simple_cmd_core completed.\n",
+       dev_dbg(&h->pdev->dev, "%s: Tag:0x%08x:%08x: do_simple_cmd(abort) completed.\n",
                __func__, tagupper, taglower);
        /* no unmap needed here because no data xfer. */
 
@@ -4440,6 +5215,9 @@ static int hpsa_send_abort(struct ctlr_info *h, unsigned char *scsi3addr,
        switch (ei->CommandStatus) {
        case CMD_SUCCESS:
                break;
+       case CMD_TMF_STATUS:
+               rc = hpsa_evaluate_tmf_status(h, c);
+               break;
        case CMD_UNABORTABLE: /* Very common, don't make noise. */
                rc = -1;
                break;
@@ -4456,6 +5234,48 @@ static int hpsa_send_abort(struct ctlr_info *h, unsigned char *scsi3addr,
        return rc;
 }
 
+static void setup_ioaccel2_abort_cmd(struct CommandList *c, struct ctlr_info *h,
+       struct CommandList *command_to_abort, int reply_queue)
+{
+       struct io_accel2_cmd *c2 = &h->ioaccel2_cmd_pool[c->cmdindex];
+       struct hpsa_tmf_struct *ac = (struct hpsa_tmf_struct *) c2;
+       struct io_accel2_cmd *c2a =
+               &h->ioaccel2_cmd_pool[command_to_abort->cmdindex];
+       struct scsi_cmnd *scmd = command_to_abort->scsi_cmd;
+       struct hpsa_scsi_dev_t *dev = scmd->device->hostdata;
+
+       /*
+        * We're overlaying struct hpsa_tmf_struct on top of something which
+        * was allocated as a struct io_accel2_cmd, so we better be sure it
+        * actually fits, and doesn't overrun the error info space.
+        */
+       BUILD_BUG_ON(sizeof(struct hpsa_tmf_struct) >
+                       sizeof(struct io_accel2_cmd));
+       BUG_ON(offsetof(struct io_accel2_cmd, error_data) <
+                       offsetof(struct hpsa_tmf_struct, error_len) +
+                               sizeof(ac->error_len));
+
+       c->cmd_type = IOACCEL2_TMF;
+       c->scsi_cmd = SCSI_CMD_BUSY;
+
+       /* Adjust the DMA address to point to the accelerated command buffer */
+       c->busaddr = (u32) h->ioaccel2_cmd_pool_dhandle +
+                               (c->cmdindex * sizeof(struct io_accel2_cmd));
+       BUG_ON(c->busaddr & 0x0000007F);
+
+       memset(ac, 0, sizeof(*c2)); /* yes this is correct */
+       ac->iu_type = IOACCEL2_IU_TMF_TYPE;
+       ac->reply_queue = reply_queue;
+       ac->tmf = IOACCEL2_TMF_ABORT;
+       ac->it_nexus = cpu_to_le32(dev->ioaccel_handle);
+       memset(ac->lun_id, 0, sizeof(ac->lun_id));
+       ac->tag = cpu_to_le64(c->cmdindex << DIRECT_LOOKUP_SHIFT);
+       ac->abort_tag = cpu_to_le64(le32_to_cpu(c2a->Tag));
+       ac->error_ptr = cpu_to_le64(c->busaddr +
+                       offsetof(struct io_accel2_cmd, error_data));
+       ac->error_len = cpu_to_le32(sizeof(c2->error_data));
+}
+
 /* ioaccel2 path firmware cannot handle abort task requests.
  * Change abort requests to physical target reset, and send to the
  * address of the physical disk used for the ioaccel 2 command.
@@ -4464,7 +5284,7 @@ static int hpsa_send_abort(struct ctlr_info *h, unsigned char *scsi3addr,
  */
 
 static int hpsa_send_reset_as_abort_ioaccel2(struct ctlr_info *h,
-       unsigned char *scsi3addr, struct CommandList *abort)
+       unsigned char *scsi3addr, struct CommandList *abort, int reply_queue)
 {
        int rc = IO_OK;
        struct scsi_cmnd *scmd; /* scsi command within request being aborted */
@@ -4483,8 +5303,9 @@ static int hpsa_send_reset_as_abort_ioaccel2(struct ctlr_info *h,
 
        if (h->raid_offload_debug > 0)
                dev_info(&h->pdev->dev,
-                       "Reset as abort: Abort requested on C%d:B%d:T%d:L%d scsi3addr 0x%02x%02x%02x%02x%02x%02x%02x%02x\n",
+                       "scsi %d:%d:%d:%d %s scsi3addr 0x%02x%02x%02x%02x%02x%02x%02x%02x\n",
                        h->scsi_host->host_no, dev->bus, dev->target, dev->lun,
+                       "Reset as abort",
                        scsi3addr[0], scsi3addr[1], scsi3addr[2], scsi3addr[3],
                        scsi3addr[4], scsi3addr[5], scsi3addr[6], scsi3addr[7]);
 
@@ -4506,7 +5327,7 @@ static int hpsa_send_reset_as_abort_ioaccel2(struct ctlr_info *h,
                        "Reset as abort: Resetting physical device at scsi3addr 0x%02x%02x%02x%02x%02x%02x%02x%02x\n",
                        psa[0], psa[1], psa[2], psa[3],
                        psa[4], psa[5], psa[6], psa[7]);
-       rc = hpsa_send_reset(h, psa, HPSA_RESET_TYPE_TARGET);
+       rc = hpsa_do_reset(h, dev, psa, HPSA_RESET_TYPE_TARGET, reply_queue);
        if (rc != 0) {
                dev_warn(&h->pdev->dev,
                        "Reset as abort: Failed on physical device at scsi3addr 0x%02x%02x%02x%02x%02x%02x%02x%02x\n",
@@ -4516,7 +5337,7 @@ static int hpsa_send_reset_as_abort_ioaccel2(struct ctlr_info *h,
        }
 
        /* wait for device to recover */
-       if (wait_for_device_to_become_ready(h, psa) != 0) {
+       if (wait_for_device_to_become_ready(h, psa, reply_queue) != 0) {
                dev_warn(&h->pdev->dev,
                        "Reset as abort: Failed: Device never recovered from reset: 0x%02x%02x%02x%02x%02x%02x%02x%02x\n",
                        psa[0], psa[1], psa[2], psa[3],
@@ -4533,25 +5354,94 @@ static int hpsa_send_reset_as_abort_ioaccel2(struct ctlr_info *h,
        return rc; /* success */
 }
 
-/* Some Smart Arrays need the abort tag swizzled, and some don't.  It's hard to
- * tell which kind we're dealing with, so we send the abort both ways.  There
- * shouldn't be any collisions between swizzled and unswizzled tags due to the
- * way we construct our tags but we check anyway in case the assumptions which
- * make this true someday become false.
- */
+static int hpsa_send_abort_ioaccel2(struct ctlr_info *h,
+       struct CommandList *abort, int reply_queue)
+{
+       int rc = IO_OK;
+       struct CommandList *c;
+       __le32 taglower, tagupper;
+       struct hpsa_scsi_dev_t *dev;
+       struct io_accel2_cmd *c2;
+
+       dev = abort->scsi_cmd->device->hostdata;
+       if (!dev->offload_enabled && !dev->hba_ioaccel_enabled)
+               return -1;
+
+       c = cmd_alloc(h);
+       setup_ioaccel2_abort_cmd(c, h, abort, reply_queue);
+       c2 = &h->ioaccel2_cmd_pool[c->cmdindex];
+       (void) hpsa_scsi_do_simple_cmd(h, c, reply_queue, NO_TIMEOUT);
+       hpsa_get_tag(h, abort, &taglower, &tagupper);
+       dev_dbg(&h->pdev->dev,
+               "%s: Tag:0x%08x:%08x: do_simple_cmd(ioaccel2 abort) completed.\n",
+               __func__, tagupper, taglower);
+       /* no unmap needed here because no data xfer. */
+
+       dev_dbg(&h->pdev->dev,
+               "%s: Tag:0x%08x:%08x: abort service response = 0x%02x.\n",
+               __func__, tagupper, taglower, c2->error_data.serv_response);
+       switch (c2->error_data.serv_response) {
+       case IOACCEL2_SERV_RESPONSE_TMF_COMPLETE:
+       case IOACCEL2_SERV_RESPONSE_TMF_SUCCESS:
+               rc = 0;
+               break;
+       case IOACCEL2_SERV_RESPONSE_TMF_REJECTED:
+       case IOACCEL2_SERV_RESPONSE_FAILURE:
+       case IOACCEL2_SERV_RESPONSE_TMF_WRONG_LUN:
+               rc = -1;
+               break;
+       default:
+               dev_warn(&h->pdev->dev,
+                       "%s: Tag:0x%08x:%08x: unknown abort service response 0x%02x\n",
+                       __func__, tagupper, taglower,
+                       c2->error_data.serv_response);
+               rc = -1;
+       }
+       cmd_free(h, c);
+       dev_dbg(&h->pdev->dev, "%s: Tag:0x%08x:%08x: Finished.\n", __func__,
+               tagupper, taglower);
+       return rc;
+}
+
 static int hpsa_send_abort_both_ways(struct ctlr_info *h,
-       unsigned char *scsi3addr, struct CommandList *abort)
+       unsigned char *scsi3addr, struct CommandList *abort, int reply_queue)
 {
-       /* ioccelerator mode 2 commands should be aborted via the
+       /*
+        * ioccelerator mode 2 commands should be aborted via the
         * accelerated path, since RAID path is unaware of these commands,
-        * but underlying firmware can't handle abort TMF.
-        * Change abort to physical device reset.
+        * but not all underlying firmware can handle abort TMF.
+        * Change abort to physical device reset when abort TMF is unsupported.
         */
-       if (abort->cmd_type == CMD_IOACCEL2)
-               return hpsa_send_reset_as_abort_ioaccel2(h, scsi3addr, abort);
+       if (abort->cmd_type == CMD_IOACCEL2) {
+               if (HPSATMF_IOACCEL_ENABLED & h->TMFSupportFlags)
+                       return hpsa_send_abort_ioaccel2(h, abort,
+                                               reply_queue);
+               else
+                       return hpsa_send_reset_as_abort_ioaccel2(h, scsi3addr,
+                                                       abort, reply_queue);
+       }
+       return hpsa_send_abort(h, scsi3addr, abort, reply_queue);
+}
 
-       return hpsa_send_abort(h, scsi3addr, abort, 0) &&
-                       hpsa_send_abort(h, scsi3addr, abort, 1);
+/* Find out which reply queue a command was meant to return on */
+static int hpsa_extract_reply_queue(struct ctlr_info *h,
+                                       struct CommandList *c)
+{
+       if (c->cmd_type == CMD_IOACCEL2)
+               return h->ioaccel2_cmd_pool[c->cmdindex].reply_queue;
+       return c->Header.ReplyQueue;
+}
+
+/*
+ * Limit concurrency of abort commands to prevent
+ * over-subscription of commands
+ */
+static inline int wait_for_available_abort_cmd(struct ctlr_info *h)
+{
+#define ABORT_CMD_WAIT_MSECS 5000
+       return !wait_event_timeout(h->abort_cmd_wait_queue,
+                       atomic_dec_if_positive(&h->abort_cmds_available) >= 0,
+                       msecs_to_jiffies(ABORT_CMD_WAIT_MSECS));
 }
 
 /* Send an abort for the specified command.
@@ -4561,7 +5451,7 @@ static int hpsa_send_abort_both_ways(struct ctlr_info *h,
 static int hpsa_eh_abort_handler(struct scsi_cmnd *sc)
 {
 
-       int i, rc;
+       int rc;
        struct ctlr_info *h;
        struct hpsa_scsi_dev_t *dev;
        struct CommandList *abort; /* pointer to command to be aborted */
@@ -4569,27 +5459,19 @@ static int hpsa_eh_abort_handler(struct scsi_cmnd *sc)
        char msg[256];          /* For debug messaging. */
        int ml = 0;
        __le32 tagupper, taglower;
-       int refcount;
+       int refcount, reply_queue;
 
-       /* Find the controller of the command to be aborted */
-       h = sdev_to_hba(sc->device);
-       if (WARN(h == NULL,
-                       "ABORT REQUEST FAILED, Controller lookup failed.\n"))
+       if (sc == NULL)
                return FAILED;
 
-       if (lockup_detected(h))
+       if (sc->device == NULL)
                return FAILED;
 
-       /* Check that controller supports some kind of task abort */
-       if (!(HPSATMF_PHYS_TASK_ABORT & h->TMFSupportFlags) &&
-               !(HPSATMF_LOG_TASK_ABORT & h->TMFSupportFlags))
+       /* Find the controller of the command to be aborted */
+       h = sdev_to_hba(sc->device);
+       if (h == NULL)
                return FAILED;
 
-       memset(msg, 0, sizeof(msg));
-       ml += sprintf(msg+ml, "ABORT REQUEST on C%d:B%d:T%d:L%llu ",
-               h->scsi_host->host_no, sc->device->channel,
-               sc->device->id, sc->device->lun);
-
        /* Find the device of the command to be aborted */
        dev = sc->device->hostdata;
        if (!dev) {
@@ -4598,6 +5480,31 @@ static int hpsa_eh_abort_handler(struct scsi_cmnd *sc)
                return FAILED;
        }
 
+       /* If controller locked up, we can guarantee command won't complete */
+       if (lockup_detected(h)) {
+               hpsa_show_dev_msg(KERN_WARNING, h, dev,
+                                       "ABORT FAILED, lockup detected");
+               return FAILED;
+       }
+
+       /* This is a good time to check if controller lockup has occurred */
+       if (detect_controller_lockup(h)) {
+               hpsa_show_dev_msg(KERN_WARNING, h, dev,
+                                       "ABORT FAILED, new lockup detected");
+               return FAILED;
+       }
+
+       /* Check that controller supports some kind of task abort */
+       if (!(HPSATMF_PHYS_TASK_ABORT & h->TMFSupportFlags) &&
+               !(HPSATMF_LOG_TASK_ABORT & h->TMFSupportFlags))
+               return FAILED;
+
+       memset(msg, 0, sizeof(msg));
+       ml += sprintf(msg+ml, "scsi %d:%d:%d:%llu %s %p",
+               h->scsi_host->host_no, sc->device->channel,
+               sc->device->id, sc->device->lun,
+               "Aborting command", sc);
+
        /* Get SCSI command to be aborted */
        abort = (struct CommandList *) sc->host_scribble;
        if (abort == NULL) {
@@ -4609,50 +5516,115 @@ static int hpsa_eh_abort_handler(struct scsi_cmnd *sc)
                cmd_free(h, abort);
                return SUCCESS;
        }
+
+       /* Don't bother trying the abort if we know it won't work. */
+       if (abort->cmd_type != CMD_IOACCEL2 &&
+               abort->cmd_type != CMD_IOACCEL1 && !dev->supports_aborts) {
+               cmd_free(h, abort);
+               return FAILED;
+       }
+
+       /*
+        * Check that we're aborting the right command.
+        * It's possible the CommandList already completed and got re-used.
+        */
+       if (abort->scsi_cmd != sc) {
+               cmd_free(h, abort);
+               return SUCCESS;
+       }
+
+       abort->abort_pending = true;
        hpsa_get_tag(h, abort, &taglower, &tagupper);
+       reply_queue = hpsa_extract_reply_queue(h, abort);
        ml += sprintf(msg+ml, "Tag:0x%08x:%08x ", tagupper, taglower);
        as  = abort->scsi_cmd;
        if (as != NULL)
-               ml += sprintf(msg+ml, "Command:0x%x SN:0x%lx ",
-                       as->cmnd[0], as->serial_number);
-       dev_dbg(&h->pdev->dev, "%s\n", msg);
-       dev_warn(&h->pdev->dev, "Abort request on C%d:B%d:T%d:L%d\n",
-               h->scsi_host->host_no, dev->bus, dev->target, dev->lun);
+               ml += sprintf(msg+ml,
+                       "CDBLen: %d CDB: 0x%02x%02x... SN: 0x%lx ",
+                       as->cmd_len, as->cmnd[0], as->cmnd[1],
+                       as->serial_number);
+       dev_warn(&h->pdev->dev, "%s BEING SENT\n", msg);
+       hpsa_show_dev_msg(KERN_WARNING, h, dev, "Aborting command");
+
        /*
         * Command is in flight, or possibly already completed
         * by the firmware (but not to the scsi mid layer) but we can't
         * distinguish which.  Send the abort down.
         */
-       rc = hpsa_send_abort_both_ways(h, dev->scsi3addr, abort);
+       if (wait_for_available_abort_cmd(h)) {
+               dev_warn(&h->pdev->dev,
+                       "%s FAILED, timeout waiting for an abort command to become available.\n",
+                       msg);
+               cmd_free(h, abort);
+               return FAILED;
+       }
+       rc = hpsa_send_abort_both_ways(h, dev->scsi3addr, abort, reply_queue);
+       atomic_inc(&h->abort_cmds_available);
+       wake_up_all(&h->abort_cmd_wait_queue);
        if (rc != 0) {
-               dev_dbg(&h->pdev->dev, "%s Request FAILED.\n", msg);
-               dev_warn(&h->pdev->dev, "FAILED abort on device C%d:B%d:T%d:L%d\n",
-                       h->scsi_host->host_no,
-                       dev->bus, dev->target, dev->lun);
+               dev_warn(&h->pdev->dev, "%s SENT, FAILED\n", msg);
+               hpsa_show_dev_msg(KERN_WARNING, h, dev,
+                               "FAILED to abort command");
                cmd_free(h, abort);
                return FAILED;
        }
-       dev_info(&h->pdev->dev, "%s REQUEST SUCCEEDED.\n", msg);
+       dev_info(&h->pdev->dev, "%s SENT, SUCCESS\n", msg);
+       wait_event(h->event_sync_wait_queue,
+                  abort->scsi_cmd != sc || lockup_detected(h));
+       cmd_free(h, abort);
+       return !lockup_detected(h) ? SUCCESS : FAILED;
+}
 
-       /* If the abort(s) above completed and actually aborted the
-        * command, then the command to be aborted should already be
-        * completed.  If not, wait around a bit more to see if they
-        * manage to complete normally.
-        */
-#define ABORT_COMPLETE_WAIT_SECS 30
-       for (i = 0; i < ABORT_COMPLETE_WAIT_SECS * 10; i++) {
-               refcount = atomic_read(&abort->refcount);
-               if (refcount < 2) {
-                       cmd_free(h, abort);
-                       return SUCCESS;
-               } else {
-                       msleep(100);
-               }
+/*
+ * For operations with an associated SCSI command, a command block is allocated
+ * at init, and managed by cmd_tagged_alloc() and cmd_tagged_free() using the
+ * block request tag as an index into a table of entries.  cmd_tagged_free() is
+ * the complement, although cmd_free() may be called instead.
+ */
+static struct CommandList *cmd_tagged_alloc(struct ctlr_info *h,
+                                           struct scsi_cmnd *scmd)
+{
+       int idx = hpsa_get_cmd_index(scmd);
+       struct CommandList *c = h->cmd_pool + idx;
+
+       if (idx < HPSA_NRESERVED_CMDS || idx >= h->nr_cmds) {
+               dev_err(&h->pdev->dev, "Bad block tag: %d not in [%d..%d]\n",
+                       idx, HPSA_NRESERVED_CMDS, h->nr_cmds - 1);
+               /* The index value comes from the block layer, so if it's out of
+                * bounds, it's probably not our bug.
+                */
+               BUG();
        }
-       dev_warn(&h->pdev->dev, "%s FAILED. Aborted command has not completed after %d seconds.\n",
-               msg, ABORT_COMPLETE_WAIT_SECS);
-       cmd_free(h, abort);
-       return FAILED;
+
+       atomic_inc(&c->refcount);
+       if (unlikely(!hpsa_is_cmd_idle(c))) {
+               /*
+                * We expect that the SCSI layer will hand us a unique tag
+                * value.  Thus, there should never be a collision here between
+                * two requests...because if the selected command isn't idle
+                * then someone is going to be very disappointed.
+                */
+               dev_err(&h->pdev->dev,
+                       "tag collision (tag=%d) in cmd_tagged_alloc().\n",
+                       idx);
+               if (c->scsi_cmd != NULL)
+                       scsi_print_command(c->scsi_cmd);
+               scsi_print_command(scmd);
+       }
+
+       hpsa_cmd_partial_init(h, idx, c);
+       return c;
+}
+
+static void cmd_tagged_free(struct ctlr_info *h, struct CommandList *c)
+{
+       /*
+        * Release our reference to the block.  We don't need to do anything
+        * else to free it, because it is accessed by index.  (There's no point
+        * in checking the result of the decrement, since we cannot guarantee
+        * that there isn't a concurrent abort which is also accessing it.)
+        */
+       (void)atomic_dec(&c->refcount);
 }
 
 /*
@@ -4660,16 +5632,15 @@ static int hpsa_eh_abort_handler(struct scsi_cmnd *sc)
  * and managed by cmd_alloc() and cmd_free() using a simple bitmap to track
  * which ones are free or in use.  Lock must be held when calling this.
  * cmd_free() is the complement.
+ * This function never gives up and returns NULL.  If it hangs,
+ * another thread must call cmd_free() to free some tags.
  */
 
 static struct CommandList *cmd_alloc(struct ctlr_info *h)
 {
        struct CommandList *c;
-       int i;
-       union u64bit temp64;
-       dma_addr_t cmd_dma_handle, err_dma_handle;
-       int refcount;
-       unsigned long offset;
+       int refcount, i;
+       int offset = 0;
 
        /*
         * There is some *extremely* small but non-zero chance that that
@@ -4681,12 +5652,20 @@ static struct CommandList *cmd_alloc(struct ctlr_info *h)
         * very unlucky thread might be starved anyway, never able to
         * beat the other threads.  In reality, this happens so
         * infrequently as to be indistinguishable from never.
+        *
+        * Note that we start allocating commands before the SCSI host structure
+        * is initialized.  Since the search starts at bit zero, this
+        * all works, since we have at least one command structure available;
+        * however, it means that the structures with the low indexes have to be
+        * reserved for driver-initiated requests, while requests from the block
+        * layer will use the higher indexes.
         */
 
-       offset = h->last_allocation; /* benignly racy */
        for (;;) {
-               i = find_next_zero_bit(h->cmd_pool_bits, h->nr_cmds, offset);
-               if (unlikely(i == h->nr_cmds)) {
+               i = find_next_zero_bit(h->cmd_pool_bits,
+                                       HPSA_NRESERVED_CMDS,
+                                       offset);
+               if (unlikely(i >= HPSA_NRESERVED_CMDS)) {
                        offset = 0;
                        continue;
                }
@@ -4694,35 +5673,23 @@ static struct CommandList *cmd_alloc(struct ctlr_info *h)
                refcount = atomic_inc_return(&c->refcount);
                if (unlikely(refcount > 1)) {
                        cmd_free(h, c); /* already in use */
-                       offset = (i + 1) % h->nr_cmds;
+                       offset = (i + 1) % HPSA_NRESERVED_CMDS;
                        continue;
                }
                set_bit(i & (BITS_PER_LONG - 1),
                        h->cmd_pool_bits + (i / BITS_PER_LONG));
                break; /* it's ours now. */
        }
-       h->last_allocation = i; /* benignly racy */
-
-       /* Zero out all of commandlist except the last field, refcount */
-       memset(c, 0, offsetof(struct CommandList, refcount));
-       c->Header.tag = cpu_to_le64((u64) (i << DIRECT_LOOKUP_SHIFT));
-       cmd_dma_handle = h->cmd_pool_dhandle + i * sizeof(*c);
-       c->err_info = h->errinfo_pool + i;
-       memset(c->err_info, 0, sizeof(*c->err_info));
-       err_dma_handle = h->errinfo_pool_dhandle
-           + i * sizeof(*c->err_info);
-
-       c->cmdindex = i;
-
-       c->busaddr = (u32) cmd_dma_handle;
-       temp64.val = (u64) err_dma_handle;
-       c->ErrDesc.Addr = cpu_to_le64((u64) err_dma_handle);
-       c->ErrDesc.Len = cpu_to_le32((u32) sizeof(*c->err_info));
-
-       c->h = h;
+       hpsa_cmd_partial_init(h, i, c);
        return c;
 }
 
+/*
+ * This is the complementary operation to cmd_alloc().  Note, however, in some
+ * corner cases it may also be used to free blocks allocated by
+ * cmd_tagged_alloc() in which case the ref-count decrement does the trick and
+ * the clear-bit is harmless.
+ */
 static void cmd_free(struct ctlr_info *h, struct CommandList *c)
 {
        if (atomic_dec_and_test(&c->refcount)) {
@@ -4900,7 +5867,7 @@ static int hpsa_passthru_ioctl(struct ctlr_info *h, void __user *argp)
        if (iocommand.buf_size > 0) {
                buff = kmalloc(iocommand.buf_size, GFP_KERNEL);
                if (buff == NULL)
-                       return -EFAULT;
+                       return -ENOMEM;
                if (iocommand.Request.Type.Direction & XFER_WRITE) {
                        /* Copy the data into the buffer we created */
                        if (copy_from_user(buff, iocommand.buf,
@@ -4913,12 +5880,10 @@ static int hpsa_passthru_ioctl(struct ctlr_info *h, void __user *argp)
                }
        }
        c = cmd_alloc(h);
-       if (c == NULL) {
-               rc = -ENOMEM;
-               goto out_kfree;
-       }
+
        /* Fill in the command type */
        c->cmd_type = CMD_IOCTL_PEND;
+       c->scsi_cmd = SCSI_CMD_BUSY;
        /* Fill in Command Header */
        c->Header.ReplyQueue = 0; /* unused in simple mode */
        if (iocommand.buf_size > 0) {   /* buffer to fill */
@@ -4948,10 +5913,14 @@ static int hpsa_passthru_ioctl(struct ctlr_info *h, void __user *argp)
                c->SG[0].Len = cpu_to_le32(iocommand.buf_size);
                c->SG[0].Ext = cpu_to_le32(HPSA_SG_LAST); /* not chaining */
        }
-       hpsa_scsi_do_simple_cmd_core_if_no_lockup(h, c);
+       rc = hpsa_scsi_do_simple_cmd(h, c, DEFAULT_REPLY_QUEUE, NO_TIMEOUT);
        if (iocommand.buf_size > 0)
                hpsa_pci_unmap(h->pdev, c, 1, PCI_DMA_BIDIRECTIONAL);
        check_ioctl_unit_attention(h, c);
+       if (rc) {
+               rc = -EIO;
+               goto out;
+       }
 
        /* Copy the error information out */
        memcpy(&iocommand.error_info, c->err_info,
@@ -5048,11 +6017,9 @@ static int hpsa_big_passthru_ioctl(struct ctlr_info *h, void __user *argp)
                sg_used++;
        }
        c = cmd_alloc(h);
-       if (c == NULL) {
-               status = -ENOMEM;
-               goto cleanup1;
-       }
+
        c->cmd_type = CMD_IOCTL_PEND;
+       c->scsi_cmd = SCSI_CMD_BUSY;
        c->Header.ReplyQueue = 0;
        c->Header.SGList = (u8) sg_used;
        c->Header.SGTotal = cpu_to_le16(sg_used);
@@ -5078,10 +6045,15 @@ static int hpsa_big_passthru_ioctl(struct ctlr_info *h, void __user *argp)
                }
                c->SG[--i].Ext = cpu_to_le32(HPSA_SG_LAST);
        }
-       hpsa_scsi_do_simple_cmd_core_if_no_lockup(h, c);
+       status = hpsa_scsi_do_simple_cmd(h, c, DEFAULT_REPLY_QUEUE, NO_TIMEOUT);
        if (sg_used)
                hpsa_pci_unmap(h->pdev, c, sg_used, PCI_DMA_BIDIRECTIONAL);
        check_ioctl_unit_attention(h, c);
+       if (status) {
+               status = -EIO;
+               goto cleanup0;
+       }
+
        /* Copy the error information out */
        memcpy(&ioc->error_info, c->err_info, sizeof(ioc->error_info));
        if (copy_to_user(argp, ioc, sizeof(*ioc))) {
@@ -5163,14 +6135,13 @@ static int hpsa_ioctl(struct scsi_device *dev, int cmd, void __user *arg)
        }
 }
 
-static int hpsa_send_host_reset(struct ctlr_info *h, unsigned char *scsi3addr,
+static void hpsa_send_host_reset(struct ctlr_info *h, unsigned char *scsi3addr,
                                u8 reset_type)
 {
        struct CommandList *c;
 
        c = cmd_alloc(h);
-       if (!c)
-               return -ENOMEM;
+
        /* fill_cmd can't fail here, no data buffer to map */
        (void) fill_cmd(c, HPSA_DEVICE_RESET_MSG, h, NULL, 0, 0,
                RAID_CTLR_LUNID, TYPE_MSG);
@@ -5181,7 +6152,7 @@ static int hpsa_send_host_reset(struct ctlr_info *h, unsigned char *scsi3addr,
         * the command either.  This is the last command we will send before
         * re-initializing everything, so it doesn't matter and won't leak.
         */
-       return 0;
+       return;
 }
 
 static int fill_cmd(struct CommandList *c, u8 cmd, struct ctlr_info *h,
@@ -5189,9 +6160,10 @@ static int fill_cmd(struct CommandList *c, u8 cmd, struct ctlr_info *h,
        int cmd_type)
 {
        int pci_dir = XFER_NONE;
-       struct CommandList *a; /* for commands to be aborted */
+       u64 tag; /* for commands to be aborted */
 
        c->cmd_type = CMD_IOCTL_PEND;
+       c->scsi_cmd = SCSI_CMD_BUSY;
        c->Header.ReplyQueue = 0;
        if (buff != NULL && size > 0) {
                c->Header.SGList = 1;
@@ -5305,10 +6277,10 @@ static int fill_cmd(struct CommandList *c, u8 cmd, struct ctlr_info *h,
                        c->Request.CDB[7] = 0x00;
                        break;
                case  HPSA_ABORT_MSG:
-                       a = buff;       /* point to command to be aborted */
+                       memcpy(&tag, buff, sizeof(tag));
                        dev_dbg(&h->pdev->dev,
-                               "Abort Tag:0x%016llx request Tag:0x%016llx",
-                               a->Header.tag, c->Header.tag);
+                               "Abort Tag:0x%016llx using rqst Tag:0x%016llx",
+                               tag, c->Header.tag);
                        c->Request.CDBLen = 16;
                        c->Request.type_attr_dir =
                                        TYPE_ATTR_DIR(cmd_type,
@@ -5319,8 +6291,7 @@ static int fill_cmd(struct CommandList *c, u8 cmd, struct ctlr_info *h,
                        c->Request.CDB[2] = 0x00; /* reserved */
                        c->Request.CDB[3] = 0x00; /* reserved */
                        /* Tag to abort goes in CDB[4]-CDB[11] */
-                       memcpy(&c->Request.CDB[4], &a->Header.tag,
-                               sizeof(a->Header.tag));
+                       memcpy(&c->Request.CDB[4], &tag, sizeof(tag));
                        c->Request.CDB[12] = 0x00; /* reserved */
                        c->Request.CDB[13] = 0x00; /* reserved */
                        c->Request.CDB[14] = 0x00; /* reserved */
@@ -5399,7 +6370,7 @@ static inline void finish_cmd(struct CommandList *c)
        if (likely(c->cmd_type == CMD_IOACCEL1 || c->cmd_type == CMD_SCSI
                        || c->cmd_type == CMD_IOACCEL2))
                complete_scsi_command(c);
-       else if (c->cmd_type == CMD_IOCTL_PEND)
+       else if (c->cmd_type == CMD_IOCTL_PEND || c->cmd_type == IOACCEL2_TMF)
                complete(c->waiting);
 }
 
@@ -5733,7 +6704,7 @@ static int controller_reset_failed(struct CfgTable __iomem *cfgtable)
 /* This does a hard reset of the controller using PCI power management
  * states or the using the doorbell register.
  */
-static int hpsa_kdump_hard_reset_controller(struct pci_dev *pdev)
+static int hpsa_kdump_hard_reset_controller(struct pci_dev *pdev, u32 board_id)
 {
        u64 cfg_offset;
        u32 cfg_base_addr;
@@ -5744,7 +6715,6 @@ static int hpsa_kdump_hard_reset_controller(struct pci_dev *pdev)
        int rc;
        struct CfgTable __iomem *cfgtable;
        u32 use_doorbell;
-       u32 board_id;
        u16 command_register;
 
        /* For controllers as old as the P600, this is very nearly
@@ -5760,11 +6730,6 @@ static int hpsa_kdump_hard_reset_controller(struct pci_dev *pdev)
         * using the doorbell register.
         */
 
-       rc = hpsa_lookup_board_id(pdev, &board_id);
-       if (rc < 0) {
-               dev_warn(&pdev->dev, "Board ID not found\n");
-               return rc;
-       }
        if (!ctlr_is_resettable(board_id)) {
                dev_warn(&pdev->dev, "Controller not resettable\n");
                return -ENODEV;
@@ -5930,10 +6895,22 @@ static int find_PCI_BAR_index(struct pci_dev *pdev, unsigned long pci_bar_addr)
        return -1;
 }
 
+static void hpsa_disable_interrupt_mode(struct ctlr_info *h)
+{
+       if (h->msix_vector) {
+               if (h->pdev->msix_enabled)
+                       pci_disable_msix(h->pdev);
+               h->msix_vector = 0;
+       } else if (h->msi_vector) {
+               if (h->pdev->msi_enabled)
+                       pci_disable_msi(h->pdev);
+               h->msi_vector = 0;
+       }
+}
+
 /* If MSI/MSI-X is supported by the kernel we will try to enable it on
  * controllers that are capable. If not, we use legacy INTx mode.
  */
-
 static void hpsa_interrupt_mode(struct ctlr_info *h)
 {
 #ifdef CONFIG_PCI_MSI
@@ -6064,6 +7041,21 @@ static int hpsa_find_cfg_addrs(struct pci_dev *pdev, void __iomem *vaddr,
        return 0;
 }
 
+static void hpsa_free_cfgtables(struct ctlr_info *h)
+{
+       if (h->transtable) {
+               iounmap(h->transtable);
+               h->transtable = NULL;
+       }
+       if (h->cfgtable) {
+               iounmap(h->cfgtable);
+               h->cfgtable = NULL;
+       }
+}
+
+/* Find and map CISS config table and transfer table
++ * several items must be unmapped (freed) later
++ * */
 static int hpsa_find_cfgtables(struct ctlr_info *h)
 {
        u64 cfg_offset;
@@ -6090,25 +7082,31 @@ static int hpsa_find_cfgtables(struct ctlr_info *h)
        h->transtable = remap_pci_mem(pci_resource_start(h->pdev,
                                cfg_base_addr_index)+cfg_offset+trans_offset,
                                sizeof(*h->transtable));
-       if (!h->transtable)
+       if (!h->transtable) {
+               dev_err(&h->pdev->dev, "Failed mapping transfer table\n");
+               hpsa_free_cfgtables(h);
                return -ENOMEM;
+       }
        return 0;
 }
 
 static void hpsa_get_max_perf_mode_cmds(struct ctlr_info *h)
 {
-       h->max_commands = readl(&(h->cfgtable->MaxPerformantModeCommands));
+#define MIN_MAX_COMMANDS 16
+       BUILD_BUG_ON(MIN_MAX_COMMANDS <= HPSA_NRESERVED_CMDS);
+
+       h->max_commands = readl(&h->cfgtable->MaxPerformantModeCommands);
 
        /* Limit commands in memory limited kdump scenario. */
        if (reset_devices && h->max_commands > 32)
                h->max_commands = 32;
 
-       if (h->max_commands < 16) {
-               dev_warn(&h->pdev->dev, "Controller reports "
-                       "max supported commands of %d, an obvious lie. "
-                       "Using 16.  Ensure that firmware is up to date.\n",
-                       h->max_commands);
-               h->max_commands = 16;
+       if (h->max_commands < MIN_MAX_COMMANDS) {
+               dev_warn(&h->pdev->dev,
+                       "Controller reports max supported commands of %d Using %d instead. Ensure that firmware is up to date.\n",
+                       h->max_commands,
+                       MIN_MAX_COMMANDS);
+               h->max_commands = MIN_MAX_COMMANDS;
        }
 }
 
@@ -6153,6 +7151,8 @@ static void hpsa_find_board_params(struct ctlr_info *h)
                dev_warn(&h->pdev->dev, "Physical aborts not supported\n");
        if (!(HPSATMF_LOG_TASK_ABORT & h->TMFSupportFlags))
                dev_warn(&h->pdev->dev, "Logical aborts not supported\n");
+       if (!(HPSATMF_IOACCEL_ENABLED & h->TMFSupportFlags))
+               dev_warn(&h->pdev->dev, "HP SSD Smart Path aborts not supported\n");
 }
 
 static inline bool hpsa_CISS_signature_present(struct ctlr_info *h)
@@ -6222,6 +7222,8 @@ static int hpsa_wait_for_mode_change_ack(struct ctlr_info *h)
         * as we enter this code.)
         */
        for (i = 0; i < MAX_MODE_CHANGE_WAIT; i++) {
+               if (h->remove_in_progress)
+                       goto done;
                spin_lock_irqsave(&h->lock, flags);
                doorbell_value = readl(h->vaddr + SA5_DOORBELL);
                spin_unlock_irqrestore(&h->lock, flags);
@@ -6262,6 +7264,22 @@ error:
        return -ENODEV;
 }
 
+/* free items allocated or mapped by hpsa_pci_init */
+static void hpsa_free_pci_init(struct ctlr_info *h)
+{
+       hpsa_free_cfgtables(h);                 /* pci_init 4 */
+       iounmap(h->vaddr);                      /* pci_init 3 */
+       h->vaddr = NULL;
+       hpsa_disable_interrupt_mode(h);         /* pci_init 2 */
+       /*
+        * call pci_disable_device before pci_release_regions per
+        * Documentation/PCI/pci.txt
+        */
+       pci_disable_device(h->pdev);            /* pci_init 1 */
+       pci_release_regions(h->pdev);           /* pci_init 2 */
+}
+
+/* several items must be freed later */
 static int hpsa_pci_init(struct ctlr_info *h)
 {
        int prod_index, err;
@@ -6272,19 +7290,24 @@ static int hpsa_pci_init(struct ctlr_info *h)
        h->product_name = products[prod_index].product_name;
        h->access = *(products[prod_index].access);
 
+       h->needs_abort_tags_swizzled =
+               ctlr_needs_abort_tags_swizzled(h->board_id);
+
        pci_disable_link_state(h->pdev, PCIE_LINK_STATE_L0S |
                               PCIE_LINK_STATE_L1 | PCIE_LINK_STATE_CLKPM);
 
        err = pci_enable_device(h->pdev);
        if (err) {
-               dev_warn(&h->pdev->dev, "unable to enable PCI device\n");
+               dev_err(&h->pdev->dev, "failed to enable PCI device\n");
+               pci_disable_device(h->pdev);
                return err;
        }
 
        err = pci_request_regions(h->pdev, HPSA);
        if (err) {
                dev_err(&h->pdev->dev,
-                       "cannot obtain PCI resources, aborting\n");
+                       "failed to obtain PCI resources\n");
+               pci_disable_device(h->pdev);
                return err;
        }
 
@@ -6293,38 +7316,43 @@ static int hpsa_pci_init(struct ctlr_info *h)
        hpsa_interrupt_mode(h);
        err = hpsa_pci_find_memory_BAR(h->pdev, &h->paddr);
        if (err)
-               goto err_out_free_res;
+               goto clean2;    /* intmode+region, pci */
        h->vaddr = remap_pci_mem(h->paddr, 0x250);
        if (!h->vaddr) {
+               dev_err(&h->pdev->dev, "failed to remap PCI mem\n");
                err = -ENOMEM;
-               goto err_out_free_res;
+               goto clean2;    /* intmode+region, pci */
        }
        err = hpsa_wait_for_board_state(h->pdev, h->vaddr, BOARD_READY);
        if (err)
-               goto err_out_free_res;
+               goto clean3;    /* vaddr, intmode+region, pci */
        err = hpsa_find_cfgtables(h);
        if (err)
-               goto err_out_free_res;
+               goto clean3;    /* vaddr, intmode+region, pci */
        hpsa_find_board_params(h);
 
        if (!hpsa_CISS_signature_present(h)) {
                err = -ENODEV;
-               goto err_out_free_res;
+               goto clean4;    /* cfgtables, vaddr, intmode+region, pci */
        }
        hpsa_set_driver_support_bits(h);
        hpsa_p600_dma_prefetch_quirk(h);
        err = hpsa_enter_simple_mode(h);
        if (err)
-               goto err_out_free_res;
+               goto clean4;    /* cfgtables, vaddr, intmode+region, pci */
        return 0;
 
-err_out_free_res:
-       if (h->transtable)
-               iounmap(h->transtable);
-       if (h->cfgtable)
-               iounmap(h->cfgtable);
-       if (h->vaddr)
-               iounmap(h->vaddr);
+clean4:        /* cfgtables, vaddr, intmode+region, pci */
+       hpsa_free_cfgtables(h);
+clean3:        /* vaddr, intmode+region, pci */
+       iounmap(h->vaddr);
+       h->vaddr = NULL;
+clean2:        /* intmode+region, pci */
+       hpsa_disable_interrupt_mode(h);
+       /*
+        * call pci_disable_device before pci_release_regions per
+        * Documentation/PCI/pci.txt
+        */
        pci_disable_device(h->pdev);
        pci_release_regions(h->pdev);
        return err;
@@ -6346,7 +7374,7 @@ static void hpsa_hba_inquiry(struct ctlr_info *h)
        }
 }
 
-static int hpsa_init_reset_devices(struct pci_dev *pdev)
+static int hpsa_init_reset_devices(struct pci_dev *pdev, u32 board_id)
 {
        int rc, i;
        void __iomem *vaddr;
@@ -6382,7 +7410,7 @@ static int hpsa_init_reset_devices(struct pci_dev *pdev)
        iounmap(vaddr);
 
        /* Reset the controller with a PCI power-cycle or via doorbell */
-       rc = hpsa_kdump_hard_reset_controller(pdev);
+       rc = hpsa_kdump_hard_reset_controller(pdev, board_id);
 
        /* -ENOTSUPP here means we cannot reset the controller
         * but it's already (and still) up and running in
@@ -6408,7 +7436,29 @@ out_disable:
        return rc;
 }
 
-static int hpsa_allocate_cmd_pool(struct ctlr_info *h)
+static void hpsa_free_cmd_pool(struct ctlr_info *h)
+{
+       kfree(h->cmd_pool_bits);
+       h->cmd_pool_bits = NULL;
+       if (h->cmd_pool) {
+               pci_free_consistent(h->pdev,
+                               h->nr_cmds * sizeof(struct CommandList),
+                               h->cmd_pool,
+                               h->cmd_pool_dhandle);
+               h->cmd_pool = NULL;
+               h->cmd_pool_dhandle = 0;
+       }
+       if (h->errinfo_pool) {
+               pci_free_consistent(h->pdev,
+                               h->nr_cmds * sizeof(struct ErrorInfo),
+                               h->errinfo_pool,
+                               h->errinfo_pool_dhandle);
+               h->errinfo_pool = NULL;
+               h->errinfo_pool_dhandle = 0;
+       }
+}
+
+static int hpsa_alloc_cmd_pool(struct ctlr_info *h)
 {
        h->cmd_pool_bits = kzalloc(
                DIV_ROUND_UP(h->nr_cmds, BITS_PER_LONG) *
@@ -6425,34 +7475,13 @@ static int hpsa_allocate_cmd_pool(struct ctlr_info *h)
                dev_err(&h->pdev->dev, "out of memory in %s", __func__);
                goto clean_up;
        }
+       hpsa_preinitialize_commands(h);
        return 0;
 clean_up:
        hpsa_free_cmd_pool(h);
        return -ENOMEM;
 }
 
-static void hpsa_free_cmd_pool(struct ctlr_info *h)
-{
-       kfree(h->cmd_pool_bits);
-       if (h->cmd_pool)
-               pci_free_consistent(h->pdev,
-                           h->nr_cmds * sizeof(struct CommandList),
-                           h->cmd_pool, h->cmd_pool_dhandle);
-       if (h->ioaccel2_cmd_pool)
-               pci_free_consistent(h->pdev,
-                       h->nr_cmds * sizeof(*h->ioaccel2_cmd_pool),
-                       h->ioaccel2_cmd_pool, h->ioaccel2_cmd_pool_dhandle);
-       if (h->errinfo_pool)
-               pci_free_consistent(h->pdev,
-                           h->nr_cmds * sizeof(struct ErrorInfo),
-                           h->errinfo_pool,
-                           h->errinfo_pool_dhandle);
-       if (h->ioaccel_cmd_pool)
-               pci_free_consistent(h->pdev,
-                       h->nr_cmds * sizeof(struct io_accel1_cmd),
-                       h->ioaccel_cmd_pool, h->ioaccel_cmd_pool_dhandle);
-}
-
 static void hpsa_irq_affinity_hints(struct ctlr_info *h)
 {
        int i, cpu;
@@ -6474,12 +7503,14 @@ static void hpsa_free_irqs(struct ctlr_info *h)
                i = h->intr_mode;
                irq_set_affinity_hint(h->intr[i], NULL);
                free_irq(h->intr[i], &h->q[i]);
+               h->q[i] = 0;
                return;
        }
 
        for (i = 0; i < h->msix_vector; i++) {
                irq_set_affinity_hint(h->intr[i], NULL);
                free_irq(h->intr[i], &h->q[i]);
+               h->q[i] = 0;
        }
        for (; i < MAX_REPLY_QUEUES; i++)
                h->q[i] = 0;
@@ -6502,8 +7533,9 @@ static int hpsa_request_irqs(struct ctlr_info *h,
        if (h->intr_mode == PERF_MODE_INT && h->msix_vector > 0) {
                /* If performant mode and MSI-X, use multiple reply queues */
                for (i = 0; i < h->msix_vector; i++) {
+                       sprintf(h->intrname[i], "%s-msix%d", h->devname, i);
                        rc = request_irq(h->intr[i], msixhandler,
-                                       0, h->devname,
+                                       0, h->intrname[i],
                                        &h->q[i]);
                        if (rc) {
                                int j;
@@ -6524,18 +7556,30 @@ static int hpsa_request_irqs(struct ctlr_info *h,
        } else {
                /* Use single reply pool */
                if (h->msix_vector > 0 || h->msi_vector) {
+                       if (h->msix_vector)
+                               sprintf(h->intrname[h->intr_mode],
+                                       "%s-msix", h->devname);
+                       else
+                               sprintf(h->intrname[h->intr_mode],
+                                       "%s-msi", h->devname);
                        rc = request_irq(h->intr[h->intr_mode],
-                               msixhandler, 0, h->devname,
+                               msixhandler, 0,
+                               h->intrname[h->intr_mode],
                                &h->q[h->intr_mode]);
                } else {
+                       sprintf(h->intrname[h->intr_mode],
+                               "%s-intx", h->devname);
                        rc = request_irq(h->intr[h->intr_mode],
-                               intxhandler, IRQF_SHARED, h->devname,
+                               intxhandler, IRQF_SHARED,
+                               h->intrname[h->intr_mode],
                                &h->q[h->intr_mode]);
                }
+               irq_set_affinity_hint(h->intr[h->intr_mode], NULL);
        }
        if (rc) {
-               dev_err(&h->pdev->dev, "unable to get irq %d for %s\n",
+               dev_err(&h->pdev->dev, "failed to get irq %d for %s\n",
                       h->intr[h->intr_mode], h->devname);
+               hpsa_free_irqs(h);
                return -ENODEV;
        }
        return 0;
@@ -6543,42 +7587,27 @@ static int hpsa_request_irqs(struct ctlr_info *h,
 
 static int hpsa_kdump_soft_reset(struct ctlr_info *h)
 {
-       if (hpsa_send_host_reset(h, RAID_CTLR_LUNID,
-               HPSA_RESET_TYPE_CONTROLLER)) {
-               dev_warn(&h->pdev->dev, "Resetting array controller failed.\n");
-               return -EIO;
-       }
+       int rc;
+       hpsa_send_host_reset(h, RAID_CTLR_LUNID, HPSA_RESET_TYPE_CONTROLLER);
 
        dev_info(&h->pdev->dev, "Waiting for board to soft reset.\n");
-       if (hpsa_wait_for_board_state(h->pdev, h->vaddr, BOARD_NOT_READY)) {
+       rc = hpsa_wait_for_board_state(h->pdev, h->vaddr, BOARD_NOT_READY);
+       if (rc) {
                dev_warn(&h->pdev->dev, "Soft reset had no effect.\n");
-               return -1;
+               return rc;
        }
 
        dev_info(&h->pdev->dev, "Board reset, awaiting READY status.\n");
-       if (hpsa_wait_for_board_state(h->pdev, h->vaddr, BOARD_READY)) {
+       rc = hpsa_wait_for_board_state(h->pdev, h->vaddr, BOARD_READY);
+       if (rc) {
                dev_warn(&h->pdev->dev, "Board failed to become ready "
                        "after soft reset.\n");
-               return -1;
+               return rc;
        }
 
        return 0;
 }
 
-static void hpsa_free_irqs_and_disable_msix(struct ctlr_info *h)
-{
-       hpsa_free_irqs(h);
-#ifdef CONFIG_PCI_MSI
-       if (h->msix_vector) {
-               if (h->pdev->msix_enabled)
-                       pci_disable_msix(h->pdev);
-       } else if (h->msi_vector) {
-               if (h->pdev->msi_enabled)
-                       pci_disable_msi(h->pdev);
-       }
-#endif /* CONFIG_PCI_MSI */
-}
-
 static void hpsa_free_reply_queues(struct ctlr_info *h)
 {
        int i;
@@ -6586,30 +7615,36 @@ static void hpsa_free_reply_queues(struct ctlr_info *h)
        for (i = 0; i < h->nreply_queues; i++) {
                if (!h->reply_queue[i].head)
                        continue;
-               pci_free_consistent(h->pdev, h->reply_queue_size,
-                       h->reply_queue[i].head, h->reply_queue[i].busaddr);
+               pci_free_consistent(h->pdev,
+                                       h->reply_queue_size,
+                                       h->reply_queue[i].head,
+                                       h->reply_queue[i].busaddr);
                h->reply_queue[i].head = NULL;
                h->reply_queue[i].busaddr = 0;
        }
+       h->reply_queue_size = 0;
 }
 
 static void hpsa_undo_allocations_after_kdump_soft_reset(struct ctlr_info *h)
 {
-       hpsa_free_irqs_and_disable_msix(h);
-       hpsa_free_sg_chain_blocks(h);
-       hpsa_free_cmd_pool(h);
-       kfree(h->ioaccel1_blockFetchTable);
-       kfree(h->blockFetchTable);
-       hpsa_free_reply_queues(h);
-       if (h->vaddr)
-               iounmap(h->vaddr);
-       if (h->transtable)
-               iounmap(h->transtable);
-       if (h->cfgtable)
-               iounmap(h->cfgtable);
-       pci_disable_device(h->pdev);
-       pci_release_regions(h->pdev);
-       kfree(h);
+       hpsa_free_performant_mode(h);           /* init_one 7 */
+       hpsa_free_sg_chain_blocks(h);           /* init_one 6 */
+       hpsa_free_cmd_pool(h);                  /* init_one 5 */
+       hpsa_free_irqs(h);                      /* init_one 4 */
+       scsi_host_put(h->scsi_host);            /* init_one 3 */
+       h->scsi_host = NULL;                    /* init_one 3 */
+       hpsa_free_pci_init(h);                  /* init_one 2_5 */
+       free_percpu(h->lockup_detected);        /* init_one 2 */
+       h->lockup_detected = NULL;              /* init_one 2 */
+       if (h->resubmit_wq) {
+               destroy_workqueue(h->resubmit_wq);      /* init_one 1 */
+               h->resubmit_wq = NULL;
+       }
+       if (h->rescan_ctlr_wq) {
+               destroy_workqueue(h->rescan_ctlr_wq);
+               h->rescan_ctlr_wq = NULL;
+       }
+       kfree(h);                               /* init_one 1 */
 }
 
 /* Called when controller lockup detected. */
@@ -6617,17 +7652,22 @@ static void fail_all_outstanding_cmds(struct ctlr_info *h)
 {
        int i, refcount;
        struct CommandList *c;
+       int failcount = 0;
 
        flush_workqueue(h->resubmit_wq); /* ensure all cmds are fully built */
        for (i = 0; i < h->nr_cmds; i++) {
                c = h->cmd_pool + i;
                refcount = atomic_inc_return(&c->refcount);
                if (refcount > 1) {
-                       c->err_info->CommandStatus = CMD_HARDWARE_ERR;
+                       c->err_info->CommandStatus = CMD_CTLR_LOCKUP;
                        finish_cmd(c);
+                       atomic_dec(&h->commands_outstanding);
+                       failcount++;
                }
                cmd_free(h, c);
        }
+       dev_warn(&h->pdev->dev,
+               "failed %d commands in fail_all\n", failcount);
 }
 
 static void set_lockup_detected_for_all_cpus(struct ctlr_info *h, u32 value)
@@ -6653,18 +7693,19 @@ static void controller_lockup_detected(struct ctlr_info *h)
        if (!lockup_detected) {
                /* no heartbeat, but controller gave us a zero. */
                dev_warn(&h->pdev->dev,
-                       "lockup detected but scratchpad register is zero\n");
+                       "lockup detected after %d but scratchpad register is zero\n",
+                       h->heartbeat_sample_interval / HZ);
                lockup_detected = 0xffffffff;
        }
        set_lockup_detected_for_all_cpus(h, lockup_detected);
        spin_unlock_irqrestore(&h->lock, flags);
-       dev_warn(&h->pdev->dev, "Controller lockup detected: 0x%08x\n",
-                       lockup_detected);
+       dev_warn(&h->pdev->dev, "Controller lockup detected: 0x%08x after %d\n",
+                       lockup_detected, h->heartbeat_sample_interval / HZ);
        pci_disable_device(h->pdev);
        fail_all_outstanding_cmds(h);
 }
 
-static void detect_controller_lockup(struct ctlr_info *h)
+static int detect_controller_lockup(struct ctlr_info *h)
 {
        u64 now;
        u32 heartbeat;
@@ -6674,7 +7715,7 @@ static void detect_controller_lockup(struct ctlr_info *h)
        /* If we've received an interrupt recently, we're ok. */
        if (time_after64(h->last_intr_timestamp +
                                (h->heartbeat_sample_interval), now))
-               return;
+               return false;
 
        /*
         * If we've already checked the heartbeat recently, we're ok.
@@ -6683,7 +7724,7 @@ static void detect_controller_lockup(struct ctlr_info *h)
         */
        if (time_after64(h->last_heartbeat_timestamp +
                                (h->heartbeat_sample_interval), now))
-               return;
+               return false;
 
        /* If heartbeat has not changed since we last looked, we're not ok. */
        spin_lock_irqsave(&h->lock, flags);
@@ -6691,12 +7732,13 @@ static void detect_controller_lockup(struct ctlr_info *h)
        spin_unlock_irqrestore(&h->lock, flags);
        if (h->last_heartbeat == heartbeat) {
                controller_lockup_detected(h);
-               return;
+               return true;
        }
 
        /* We're ok. */
        h->last_heartbeat = heartbeat;
        h->last_heartbeat_timestamp = now;
+       return false;
 }
 
 static void hpsa_ack_ctlr_events(struct ctlr_info *h)
@@ -6843,11 +7885,18 @@ static int hpsa_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
        struct ctlr_info *h;
        int try_soft_reset = 0;
        unsigned long flags;
+       u32 board_id;
 
        if (number_of_controllers == 0)
                printk(KERN_INFO DRIVER_NAME "\n");
 
-       rc = hpsa_init_reset_devices(pdev);
+       rc = hpsa_lookup_board_id(pdev, &board_id);
+       if (rc < 0) {
+               dev_warn(&pdev->dev, "Board ID not found\n");
+               return rc;
+       }
+
+       rc = hpsa_init_reset_devices(pdev, board_id);
        if (rc) {
                if (rc != -ENOTSUPP)
                        return rc;
@@ -6868,42 +7917,41 @@ reinit_after_soft_reset:
         */
        BUILD_BUG_ON(sizeof(struct CommandList) % COMMANDLIST_ALIGNMENT);
        h = kzalloc(sizeof(*h), GFP_KERNEL);
-       if (!h)
+       if (!h) {
+               dev_err(&pdev->dev, "Failed to allocate controller head\n");
                return -ENOMEM;
+       }
 
        h->pdev = pdev;
+
        h->intr_mode = hpsa_simple_mode ? SIMPLE_MODE_INT : PERF_MODE_INT;
        INIT_LIST_HEAD(&h->offline_device_list);
        spin_lock_init(&h->lock);
        spin_lock_init(&h->offline_device_lock);
        spin_lock_init(&h->scan_lock);
        atomic_set(&h->passthru_cmds_avail, HPSA_MAX_CONCURRENT_PASSTHRUS);
-
-       h->rescan_ctlr_wq = hpsa_create_controller_wq(h, "rescan");
-       if (!h->rescan_ctlr_wq) {
-               rc = -ENOMEM;
-               goto clean1;
-       }
-
-       h->resubmit_wq = hpsa_create_controller_wq(h, "resubmit");
-       if (!h->resubmit_wq) {
-               rc = -ENOMEM;
-               goto clean1;
-       }
+       atomic_set(&h->abort_cmds_available, HPSA_CMDS_RESERVED_FOR_ABORTS);
 
        /* Allocate and clear per-cpu variable lockup_detected */
        h->lockup_detected = alloc_percpu(u32);
        if (!h->lockup_detected) {
+               dev_err(&h->pdev->dev, "Failed to allocate lockup detector\n");
                rc = -ENOMEM;
-               goto clean1;
+               goto clean1;    /* aer/h */
        }
        set_lockup_detected_for_all_cpus(h, 0);
 
        rc = hpsa_pci_init(h);
-       if (rc != 0)
-               goto clean1;
+       if (rc)
+               goto clean2;    /* lu, aer/h */
+
+       /* relies on h-> settings made by hpsa_pci_init, including
+        * interrupt_mode h->intr */
+       rc = hpsa_scsi_host_alloc(h);
+       if (rc)
+               goto clean2_5;  /* pci, lu, aer/h */
 
-       sprintf(h->devname, HPSA "%d", number_of_controllers);
+       sprintf(h->devname, HPSA "%d", h->scsi_host->host_no);
        h->ctlr = number_of_controllers;
        number_of_controllers++;
 
@@ -6917,34 +7965,57 @@ reinit_after_soft_reset:
                        dac = 0;
                } else {
                        dev_err(&pdev->dev, "no suitable DMA available\n");
-                       goto clean1;
+                       goto clean3;    /* shost, pci, lu, aer/h */
                }
        }
 
        /* make sure the board interrupts are off */
        h->access.set_intr_mask(h, HPSA_INTR_OFF);
 
-       if (hpsa_request_irqs(h, do_hpsa_intr_msi, do_hpsa_intr_intx))
-               goto clean2;
-       dev_info(&pdev->dev, "%s: <0x%x> at IRQ %d%s using DAC\n",
-              h->devname, pdev->device,
-              h->intr[h->intr_mode], dac ? "" : " not");
-       rc = hpsa_allocate_cmd_pool(h);
+       rc = hpsa_request_irqs(h, do_hpsa_intr_msi, do_hpsa_intr_intx);
        if (rc)
-               goto clean2_and_free_irqs;
-       if (hpsa_allocate_sg_chain_blocks(h))
-               goto clean4;
+               goto clean3;    /* shost, pci, lu, aer/h */
+       rc = hpsa_alloc_cmd_pool(h);
+       if (rc)
+               goto clean4;    /* irq, shost, pci, lu, aer/h */
+       rc = hpsa_alloc_sg_chain_blocks(h);
+       if (rc)
+               goto clean5;    /* cmd, irq, shost, pci, lu, aer/h */
        init_waitqueue_head(&h->scan_wait_queue);
+       init_waitqueue_head(&h->abort_cmd_wait_queue);
+       init_waitqueue_head(&h->event_sync_wait_queue);
+       mutex_init(&h->reset_mutex);
        h->scan_finished = 1; /* no scan currently in progress */
 
        pci_set_drvdata(pdev, h);
        h->ndevices = 0;
        h->hba_mode_enabled = 0;
-       h->scsi_host = NULL;
+
        spin_lock_init(&h->devlock);
-       hpsa_put_ctlr_into_performant_mode(h);
+       rc = hpsa_put_ctlr_into_performant_mode(h);
+       if (rc)
+               goto clean6; /* sg, cmd, irq, shost, pci, lu, aer/h */
+
+       /* hook into SCSI subsystem */
+       rc = hpsa_scsi_add_host(h);
+       if (rc)
+               goto clean7; /* perf, sg, cmd, irq, shost, pci, lu, aer/h */
+
+       /* create the resubmit workqueue */
+       h->rescan_ctlr_wq = hpsa_create_controller_wq(h, "rescan");
+       if (!h->rescan_ctlr_wq) {
+               rc = -ENOMEM;
+               goto clean7;
+       }
+
+       h->resubmit_wq = hpsa_create_controller_wq(h, "resubmit");
+       if (!h->resubmit_wq) {
+               rc = -ENOMEM;
+               goto clean7;    /* aer/h */
+       }
 
-       /* At this point, the controller is ready to take commands.
+       /*
+        * At this point, the controller is ready to take commands.
         * Now, if reset_devices and the hard reset didn't work, try
         * the soft reset and see if that works.
         */
@@ -6966,13 +8037,24 @@ reinit_after_soft_reset:
                if (rc) {
                        dev_warn(&h->pdev->dev,
                                "Failed to request_irq after soft reset.\n");
-                       goto clean4;
+                       /*
+                        * cannot goto clean7 or free_irqs will be called
+                        * again. Instead, do its work
+                        */
+                       hpsa_free_performant_mode(h);   /* clean7 */
+                       hpsa_free_sg_chain_blocks(h);   /* clean6 */
+                       hpsa_free_cmd_pool(h);          /* clean5 */
+                       /*
+                        * skip hpsa_free_irqs(h) clean4 since that
+                        * was just called before request_irqs failed
+                        */
+                       goto clean3;
                }
 
                rc = hpsa_kdump_soft_reset(h);
                if (rc)
                        /* Neither hard nor soft reset worked, we're hosed. */
-                       goto clean4;
+                       goto clean9;
 
                dev_info(&h->pdev->dev, "Board READY.\n");
                dev_info(&h->pdev->dev,
@@ -6993,21 +8075,20 @@ reinit_after_soft_reset:
                hpsa_undo_allocations_after_kdump_soft_reset(h);
                try_soft_reset = 0;
                if (rc)
-                       /* don't go to clean4, we already unallocated */
+                       /* don't goto clean, we already unallocated */
                        return -ENODEV;
 
                goto reinit_after_soft_reset;
        }
 
-               /* Enable Accelerated IO path at driver layer */
-               h->acciopath_status = 1;
+       /* Enable Accelerated IO path at driver layer */
+       h->acciopath_status = 1;
 
 
        /* Turn the interrupts on so we can service requests */
        h->access.set_intr_mask(h, HPSA_INTR_ON);
 
        hpsa_hba_inquiry(h);
-       hpsa_register_scsi(h);  /* hook ourselves into SCSI subsystem */
 
        /* Monitor the controller for firmware lockups */
        h->heartbeat_sample_interval = HEARTBEAT_SAMPLE_INTERVAL;
@@ -7019,19 +8100,36 @@ reinit_after_soft_reset:
                                h->heartbeat_sample_interval);
        return 0;
 
-clean4:
+clean9: /* wq, sh, perf, sg, cmd, irq, shost, pci, lu, aer/h */
+       kfree(h->hba_inquiry_data);
+clean7: /* perf, sg, cmd, irq, shost, pci, lu, aer/h */
+       hpsa_free_performant_mode(h);
+       h->access.set_intr_mask(h, HPSA_INTR_OFF);
+clean6: /* sg, cmd, irq, pci, lockup, wq/aer/h */
        hpsa_free_sg_chain_blocks(h);
+clean5: /* cmd, irq, shost, pci, lu, aer/h */
        hpsa_free_cmd_pool(h);
-clean2_and_free_irqs:
+clean4: /* irq, shost, pci, lu, aer/h */
        hpsa_free_irqs(h);
-clean2:
-clean1:
-       if (h->resubmit_wq)
+clean3: /* shost, pci, lu, aer/h */
+       scsi_host_put(h->scsi_host);
+       h->scsi_host = NULL;
+clean2_5: /* pci, lu, aer/h */
+       hpsa_free_pci_init(h);
+clean2: /* lu, aer/h */
+       if (h->lockup_detected) {
+               free_percpu(h->lockup_detected);
+               h->lockup_detected = NULL;
+       }
+clean1:        /* wq/aer/h */
+       if (h->resubmit_wq) {
                destroy_workqueue(h->resubmit_wq);
-       if (h->rescan_ctlr_wq)
+               h->resubmit_wq = NULL;
+       }
+       if (h->rescan_ctlr_wq) {
                destroy_workqueue(h->rescan_ctlr_wq);
-       if (h->lockup_detected)
-               free_percpu(h->lockup_detected);
+               h->rescan_ctlr_wq = NULL;
+       }
        kfree(h);
        return rc;
 }
@@ -7040,8 +8138,8 @@ static void hpsa_flush_cache(struct ctlr_info *h)
 {
        char *flush_buf;
        struct CommandList *c;
+       int rc;
 
-       /* Don't bother trying to flush the cache if locked up */
        if (unlikely(lockup_detected(h)))
                return;
        flush_buf = kzalloc(4, GFP_KERNEL);
@@ -7049,21 +8147,20 @@ static void hpsa_flush_cache(struct ctlr_info *h)
                return;
 
        c = cmd_alloc(h);
-       if (!c) {
-               dev_warn(&h->pdev->dev, "cmd_alloc returned NULL!\n");
-               goto out_of_memory;
-       }
+
        if (fill_cmd(c, HPSA_CACHE_FLUSH, h, flush_buf, 4, 0,
                RAID_CTLR_LUNID, TYPE_CMD)) {
                goto out;
        }
-       hpsa_scsi_do_simple_cmd_with_retry(h, c, PCI_DMA_TODEVICE);
+       rc = hpsa_scsi_do_simple_cmd_with_retry(h, c,
+                                       PCI_DMA_TODEVICE, NO_TIMEOUT);
+       if (rc)
+               goto out;
        if (c->err_info->CommandStatus != 0)
 out:
                dev_warn(&h->pdev->dev,
                        "error flushing cache on controller\n");
        cmd_free(h, c);
-out_of_memory:
        kfree(flush_buf);
 }
 
@@ -7078,15 +8175,18 @@ static void hpsa_shutdown(struct pci_dev *pdev)
         */
        hpsa_flush_cache(h);
        h->access.set_intr_mask(h, HPSA_INTR_OFF);
-       hpsa_free_irqs_and_disable_msix(h);
+       hpsa_free_irqs(h);                      /* init_one 4 */
+       hpsa_disable_interrupt_mode(h);         /* pci_init 2 */
 }
 
 static void hpsa_free_device_info(struct ctlr_info *h)
 {
        int i;
 
-       for (i = 0; i < h->ndevices; i++)
+       for (i = 0; i < h->ndevices; i++) {
                kfree(h->dev[i]);
+               h->dev[i] = NULL;
+       }
 }
 
 static void hpsa_remove_one(struct pci_dev *pdev)
@@ -7108,29 +8208,34 @@ static void hpsa_remove_one(struct pci_dev *pdev)
        cancel_delayed_work_sync(&h->rescan_ctlr_work);
        destroy_workqueue(h->rescan_ctlr_wq);
        destroy_workqueue(h->resubmit_wq);
-       hpsa_unregister_scsi(h);        /* unhook from SCSI subsystem */
+
+       /* includes hpsa_free_irqs - init_one 4 */
+       /* includes hpsa_disable_interrupt_mode - pci_init 2 */
        hpsa_shutdown(pdev);
-       iounmap(h->vaddr);
-       iounmap(h->transtable);
-       iounmap(h->cfgtable);
-       hpsa_free_device_info(h);
-       hpsa_free_sg_chain_blocks(h);
-       pci_free_consistent(h->pdev,
-               h->nr_cmds * sizeof(struct CommandList),
-               h->cmd_pool, h->cmd_pool_dhandle);
-       pci_free_consistent(h->pdev,
-               h->nr_cmds * sizeof(struct ErrorInfo),
-               h->errinfo_pool, h->errinfo_pool_dhandle);
-       hpsa_free_reply_queues(h);
-       kfree(h->cmd_pool_bits);
-       kfree(h->blockFetchTable);
-       kfree(h->ioaccel1_blockFetchTable);
-       kfree(h->ioaccel2_blockFetchTable);
-       kfree(h->hba_inquiry_data);
-       pci_disable_device(pdev);
-       pci_release_regions(pdev);
-       free_percpu(h->lockup_detected);
-       kfree(h);
+
+       hpsa_free_device_info(h);               /* scan */
+
+       kfree(h->hba_inquiry_data);                     /* init_one 10 */
+       h->hba_inquiry_data = NULL;                     /* init_one 10 */
+       if (h->scsi_host)
+               scsi_remove_host(h->scsi_host);         /* init_one 8 */
+       hpsa_free_ioaccel2_sg_chain_blocks(h);
+       hpsa_free_performant_mode(h);                   /* init_one 7 */
+       hpsa_free_sg_chain_blocks(h);                   /* init_one 6 */
+       hpsa_free_cmd_pool(h);                          /* init_one 5 */
+
+       /* hpsa_free_irqs already called via hpsa_shutdown init_one 4 */
+
+       scsi_host_put(h->scsi_host);                    /* init_one 3 */
+       h->scsi_host = NULL;                            /* init_one 3 */
+
+       /* includes hpsa_disable_interrupt_mode - pci_init 2 */
+       hpsa_free_pci_init(h);                          /* init_one 2.5 */
+
+       free_percpu(h->lockup_detected);                /* init_one 2 */
+       h->lockup_detected = NULL;                      /* init_one 2 */
+       /* (void) pci_disable_pcie_error_reporting(pdev); */    /* init_one 1 */
+       kfree(h);                                       /* init_one 1 */
 }
 
 static int hpsa_suspend(__attribute__((unused)) struct pci_dev *pdev,
@@ -7188,7 +8293,10 @@ static void  calc_bucket_map(int bucket[], int num_buckets,
        }
 }
 
-/* return -ENODEV or other reason on error, 0 on success */
+/*
+ * return -ENODEV on err, 0 on success (or no action)
+ * allocates numerous items that must be freed later
+ */
 static int hpsa_enter_performant_mode(struct ctlr_info *h, u32 trans_support)
 {
        int i;
@@ -7370,7 +8478,23 @@ static int hpsa_enter_performant_mode(struct ctlr_info *h, u32 trans_support)
        return 0;
 }
 
-static int hpsa_alloc_ioaccel_cmd_and_bft(struct ctlr_info *h)
+/* Free ioaccel1 mode command blocks and block fetch table */
+static void hpsa_free_ioaccel1_cmd_and_bft(struct ctlr_info *h)
+{
+       if (h->ioaccel_cmd_pool) {
+               pci_free_consistent(h->pdev,
+                       h->nr_cmds * sizeof(*h->ioaccel_cmd_pool),
+                       h->ioaccel_cmd_pool,
+                       h->ioaccel_cmd_pool_dhandle);
+               h->ioaccel_cmd_pool = NULL;
+               h->ioaccel_cmd_pool_dhandle = 0;
+       }
+       kfree(h->ioaccel1_blockFetchTable);
+       h->ioaccel1_blockFetchTable = NULL;
+}
+
+/* Allocate ioaccel1 mode command blocks and block fetch table */
+static int hpsa_alloc_ioaccel1_cmd_and_bft(struct ctlr_info *h)
 {
        h->ioaccel_maxsg =
                readl(&(h->cfgtable->io_accel_max_embedded_sg_count));
@@ -7401,16 +8525,32 @@ static int hpsa_alloc_ioaccel_cmd_and_bft(struct ctlr_info *h)
        return 0;
 
 clean_up:
-       if (h->ioaccel_cmd_pool)
+       hpsa_free_ioaccel1_cmd_and_bft(h);
+       return -ENOMEM;
+}
+
+/* Free ioaccel2 mode command blocks and block fetch table */
+static void hpsa_free_ioaccel2_cmd_and_bft(struct ctlr_info *h)
+{
+       hpsa_free_ioaccel2_sg_chain_blocks(h);
+
+       if (h->ioaccel2_cmd_pool) {
                pci_free_consistent(h->pdev,
-                       h->nr_cmds * sizeof(*h->ioaccel_cmd_pool),
-                       h->ioaccel_cmd_pool, h->ioaccel_cmd_pool_dhandle);
-       kfree(h->ioaccel1_blockFetchTable);
-       return 1;
+                       h->nr_cmds * sizeof(*h->ioaccel2_cmd_pool),
+                       h->ioaccel2_cmd_pool,
+                       h->ioaccel2_cmd_pool_dhandle);
+               h->ioaccel2_cmd_pool = NULL;
+               h->ioaccel2_cmd_pool_dhandle = 0;
+       }
+       kfree(h->ioaccel2_blockFetchTable);
+       h->ioaccel2_blockFetchTable = NULL;
 }
 
-static int ioaccel2_alloc_cmds_and_bft(struct ctlr_info *h)
+/* Allocate ioaccel2 mode command blocks and block fetch table */
+static int hpsa_alloc_ioaccel2_cmd_and_bft(struct ctlr_info *h)
 {
+       int rc;
+
        /* Allocate ioaccel2 mode command blocks and block fetch table */
 
        h->ioaccel_maxsg =
@@ -7430,7 +8570,13 @@ static int ioaccel2_alloc_cmds_and_bft(struct ctlr_info *h)
                                sizeof(u32)), GFP_KERNEL);
 
        if ((h->ioaccel2_cmd_pool == NULL) ||
-               (h->ioaccel2_blockFetchTable == NULL))
+               (h->ioaccel2_blockFetchTable == NULL)) {
+               rc = -ENOMEM;
+               goto clean_up;
+       }
+
+       rc = hpsa_allocate_ioaccel2_sg_chain_blocks(h);
+       if (rc)
                goto clean_up;
 
        memset(h->ioaccel2_cmd_pool, 0,
@@ -7438,41 +8584,50 @@ static int ioaccel2_alloc_cmds_and_bft(struct ctlr_info *h)
        return 0;
 
 clean_up:
-       if (h->ioaccel2_cmd_pool)
-               pci_free_consistent(h->pdev,
-                       h->nr_cmds * sizeof(*h->ioaccel2_cmd_pool),
-                       h->ioaccel2_cmd_pool, h->ioaccel2_cmd_pool_dhandle);
-       kfree(h->ioaccel2_blockFetchTable);
-       return 1;
+       hpsa_free_ioaccel2_cmd_and_bft(h);
+       return rc;
+}
+
+/* Free items allocated by hpsa_put_ctlr_into_performant_mode */
+static void hpsa_free_performant_mode(struct ctlr_info *h)
+{
+       kfree(h->blockFetchTable);
+       h->blockFetchTable = NULL;
+       hpsa_free_reply_queues(h);
+       hpsa_free_ioaccel1_cmd_and_bft(h);
+       hpsa_free_ioaccel2_cmd_and_bft(h);
 }
 
-static void hpsa_put_ctlr_into_performant_mode(struct ctlr_info *h)
+/* return -ENODEV on error, 0 on success (or no action)
+ * allocates numerous items that must be freed later
+ */
+static int hpsa_put_ctlr_into_performant_mode(struct ctlr_info *h)
 {
        u32 trans_support;
        unsigned long transMethod = CFGTBL_Trans_Performant |
                                        CFGTBL_Trans_use_short_tags;
-       int i;
+       int i, rc;
 
        if (hpsa_simple_mode)
-               return;
+               return 0;
 
        trans_support = readl(&(h->cfgtable->TransportSupport));
        if (!(trans_support & PERFORMANT_MODE))
-               return;
+               return 0;
 
        /* Check for I/O accelerator mode support */
        if (trans_support & CFGTBL_Trans_io_accel1) {
                transMethod |= CFGTBL_Trans_io_accel1 |
                                CFGTBL_Trans_enable_directed_msix;
-               if (hpsa_alloc_ioaccel_cmd_and_bft(h))
-                       goto clean_up;
-       } else {
-               if (trans_support & CFGTBL_Trans_io_accel2) {
-                               transMethod |= CFGTBL_Trans_io_accel2 |
+               rc = hpsa_alloc_ioaccel1_cmd_and_bft(h);
+               if (rc)
+                       return rc;
+       } else if (trans_support & CFGTBL_Trans_io_accel2) {
+               transMethod |= CFGTBL_Trans_io_accel2 |
                                CFGTBL_Trans_enable_directed_msix;
-               if (ioaccel2_alloc_cmds_and_bft(h))
-                       goto clean_up;
-               }
+               rc = hpsa_alloc_ioaccel2_cmd_and_bft(h);
+               if (rc)
+                       return rc;
        }
 
        h->nreply_queues = h->msix_vector > 0 ? h->msix_vector : 1;
@@ -7484,8 +8639,10 @@ static void hpsa_put_ctlr_into_performant_mode(struct ctlr_info *h)
                h->reply_queue[i].head = pci_alloc_consistent(h->pdev,
                                                h->reply_queue_size,
                                                &(h->reply_queue[i].busaddr));
-               if (!h->reply_queue[i].head)
-                       goto clean_up;
+               if (!h->reply_queue[i].head) {
+                       rc = -ENOMEM;
+                       goto clean1;    /* rq, ioaccel */
+               }
                h->reply_queue[i].size = h->max_commands;
                h->reply_queue[i].wraparound = 1;  /* spec: init to 1 */
                h->reply_queue[i].current_entry = 0;
@@ -7494,15 +8651,24 @@ static void hpsa_put_ctlr_into_performant_mode(struct ctlr_info *h)
        /* Need a block fetch table for performant mode */
        h->blockFetchTable = kmalloc(((SG_ENTRIES_IN_CMD + 1) *
                                sizeof(u32)), GFP_KERNEL);
-       if (!h->blockFetchTable)
-               goto clean_up;
+       if (!h->blockFetchTable) {
+               rc = -ENOMEM;
+               goto clean1;    /* rq, ioaccel */
+       }
 
-       hpsa_enter_performant_mode(h, trans_support);
-       return;
+       rc = hpsa_enter_performant_mode(h, trans_support);
+       if (rc)
+               goto clean2;    /* bft, rq, ioaccel */
+       return 0;
 
-clean_up:
-       hpsa_free_reply_queues(h);
+clean2:        /* bft, rq, ioaccel */
        kfree(h->blockFetchTable);
+       h->blockFetchTable = NULL;
+clean1:        /* rq, ioaccel */
+       hpsa_free_reply_queues(h);
+       hpsa_free_ioaccel1_cmd_and_bft(h);
+       hpsa_free_ioaccel2_cmd_and_bft(h);
+       return rc;
 }
 
 static int is_accelerated_cmd(struct CommandList *c)
index 6577130503490d950c6700251cc3115034f2f80f..6ee4da6b115366eec5b46ed10e4ce1ecb6add12d 100644 (file)
@@ -47,6 +47,7 @@ struct hpsa_scsi_dev_t {
        unsigned char raid_level;       /* from inquiry page 0xC1 */
        unsigned char volume_offline;   /* discovered via TUR or VPD */
        u16 queue_depth;                /* max queue_depth for this device */
+       atomic_t reset_cmds_out;        /* Count of commands to-be affected */
        atomic_t ioaccel_cmds_out;      /* Only used for physical devices
                                         * counts commands sent to physical
                                         * device via "ioaccel" path.
@@ -54,6 +55,8 @@ struct hpsa_scsi_dev_t {
        u32 ioaccel_handle;
        int offload_config;             /* I/O accel RAID offload configured */
        int offload_enabled;            /* I/O accel RAID offload enabled */
+       int offload_to_be_enabled;
+       int hba_ioaccel_enabled;
        int offload_to_mirror;          /* Send next I/O accelerator RAID
                                         * offload request to mirror drive
                                         */
@@ -68,6 +71,13 @@ struct hpsa_scsi_dev_t {
         * devices in order to honor physical device queue depth limits.
         */
        struct hpsa_scsi_dev_t *phys_disk[RAID_MAP_MAX_ENTRIES];
+       int nphysical_disks;
+       int supports_aborts;
+#define HPSA_DO_NOT_EXPOSE     0x0
+#define HPSA_SG_ATTACH         0x1
+#define HPSA_ULD_ATTACH                0x2
+#define HPSA_SCSI_ADD          (HPSA_SG_ATTACH | HPSA_ULD_ATTACH)
+       u8 expose_state;
 };
 
 struct reply_queue_buffer {
@@ -133,7 +143,6 @@ struct ctlr_info {
        struct CfgTable __iomem *cfgtable;
        int     interrupts_enabled;
        int     max_commands;
-       int last_allocation;
        atomic_t commands_outstanding;
 #      define PERF_MODE_INT    0
 #      define DOORBELL_INT     1
@@ -154,6 +163,7 @@ struct ctlr_info {
        u8 max_cmd_sg_entries;
        int chainsize;
        struct SGDescriptor **cmd_sg_list;
+       struct ioaccel2_sg_element **ioaccel2_cmd_sg_list;
 
        /* pointers to command and error info pool */
        struct CommandList      *cmd_pool;
@@ -211,6 +221,7 @@ struct ctlr_info {
        int remove_in_progress;
        /* Address of h->q[x] is passed to intr handler to know which queue */
        u8 q[MAX_REPLY_QUEUES];
+       char intrname[MAX_REPLY_QUEUES][16];    /* "hpsa0-msix00" names */
        u32 TMFSupportFlags; /* cache what task mgmt funcs are supported. */
 #define HPSATMF_BITS_SUPPORTED  (1 << 0)
 #define HPSATMF_PHYS_LUN_RESET  (1 << 1)
@@ -222,6 +233,7 @@ struct ctlr_info {
 #define HPSATMF_PHYS_QRY_TASK   (1 << 7)
 #define HPSATMF_PHYS_QRY_TSET   (1 << 8)
 #define HPSATMF_PHYS_QRY_ASYNC  (1 << 9)
+#define HPSATMF_IOACCEL_ENABLED (1 << 15)
 #define HPSATMF_MASK_SUPPORTED  (1 << 16)
 #define HPSATMF_LOG_LUN_RESET   (1 << 17)
 #define HPSATMF_LOG_NEX_RESET   (1 << 18)
@@ -251,8 +263,13 @@ struct ctlr_info {
        struct list_head offline_device_list;
        int     acciopath_status;
        int     raid_offload_debug;
+       int     needs_abort_tags_swizzled;
        struct workqueue_struct *resubmit_wq;
        struct workqueue_struct *rescan_ctlr_wq;
+       atomic_t abort_cmds_available;
+       wait_queue_head_t abort_cmd_wait_queue;
+       wait_queue_head_t event_sync_wait_queue;
+       struct mutex reset_mutex;
 };
 
 struct offline_device_entry {
index 3a621c74b76f17f95e9c5ce5a465741d41f7a144..c601622cc98e59664db6102e65650ec5ea1c98a3 100644 (file)
 #define CMD_UNSOLICITED_ABORT   0x000A
 #define CMD_TIMEOUT             0x000B
 #define CMD_UNABORTABLE                0x000C
+#define CMD_TMF_STATUS         0x000D
 #define CMD_IOACCEL_DISABLED   0x000E
+#define CMD_CTLR_LOCKUP                0xffff
+/* Note: CMD_CTLR_LOCKUP is not a value defined by the CISS spec
+ * it is a value defined by the driver that commands can be marked
+ * with when a controller lockup has been detected by the driver
+ */
 
+/* TMF function status values */
+#define CISS_TMF_COMPLETE      0x00
+#define CISS_TMF_INVALID_FRAME 0x02
+#define CISS_TMF_NOT_SUPPORTED 0x04
+#define CISS_TMF_FAILED                0x05
+#define CISS_TMF_SUCCESS       0x08
+#define CISS_TMF_WRONG_LUN     0x09
+#define CISS_TMF_OVERLAPPED_TAG 0x0a
 
 /* Unit Attentions ASC's as defined for the MSA2012sa */
 #define POWER_OR_RESET                 0x29
@@ -240,6 +254,7 @@ struct ReportLUNdata {
 
 struct ext_report_lun_entry {
        u8 lunid[8];
+#define MASKED_DEVICE(x) ((x)[3] & 0xC0)
 #define GET_BMIC_BUS(lunid) ((lunid)[7] & 0x3F)
 #define GET_BMIC_LEVEL_TWO_TARGET(lunid) ((lunid)[6])
 #define GET_BMIC_DRIVE_NUMBER(lunid) (((GET_BMIC_BUS((lunid)) - 1) << 8) + \
@@ -247,6 +262,8 @@ struct ext_report_lun_entry {
        u8 wwid[8];
        u8 device_type;
        u8 device_flags;
+#define NON_DISK_PHYS_DEV(x) ((x)[17] & 0x01)
+#define PHYS_IOACCEL(x) ((x)[17] & 0x08)
        u8 lun_count; /* multi-lun device, how many luns */
        u8 redundant_paths;
        u32 ioaccel_handle; /* ioaccel1 only uses lower 16 bits */
@@ -379,6 +396,7 @@ struct ErrorInfo {
 #define CMD_SCSI       0x03
 #define CMD_IOACCEL1   0x04
 #define CMD_IOACCEL2   0x05
+#define IOACCEL2_TMF   0x06
 
 #define DIRECT_LOOKUP_SHIFT 4
 #define DIRECT_LOOKUP_MASK (~((1 << DIRECT_LOOKUP_SHIFT) - 1))
@@ -421,7 +439,10 @@ struct CommandList {
         * not used.
         */
        struct hpsa_scsi_dev_t *phys_disk;
-       atomic_t refcount; /* Must be last to avoid memset in cmd_alloc */
+
+       int abort_pending;
+       struct hpsa_scsi_dev_t *reset_pending;
+       atomic_t refcount; /* Must be last to avoid memset in hpsa_cmd_init() */
 } __aligned(COMMANDLIST_ALIGNMENT);
 
 /* Max S/G elements in I/O accelerator command */
@@ -515,6 +536,12 @@ struct io_accel2_scsi_response {
 #define IOACCEL2_STATUS_SR_TASK_COMP_SET_FULL  0x28
 #define IOACCEL2_STATUS_SR_TASK_COMP_ABORTED   0x40
 #define IOACCEL2_STATUS_SR_IOACCEL_DISABLED    0x0E
+#define IOACCEL2_STATUS_SR_IO_ERROR            0x01
+#define IOACCEL2_STATUS_SR_IO_ABORTED          0x02
+#define IOACCEL2_STATUS_SR_NO_PATH_TO_DEVICE   0x03
+#define IOACCEL2_STATUS_SR_INVALID_DEVICE      0x04
+#define IOACCEL2_STATUS_SR_UNDERRUN            0x51
+#define IOACCEL2_STATUS_SR_OVERRUN             0x75
        u8 data_present;                /* low 2 bits */
 #define IOACCEL2_NO_DATAPRESENT                0x000
 #define IOACCEL2_RESPONSE_DATAPRESENT  0x001
@@ -567,6 +594,7 @@ struct io_accel2_cmd {
 #define IOACCEL2_DIR_NO_DATA   0x00
 #define IOACCEL2_DIR_DATA_IN   0x01
 #define IOACCEL2_DIR_DATA_OUT  0x02
+#define IOACCEL2_TMF_ABORT     0x01
 /*
  * SCSI Task Management Request format for Accelerator Mode 2
  */
@@ -575,13 +603,13 @@ struct hpsa_tmf_struct {
        u8 reply_queue;         /* Reply Queue ID */
        u8 tmf;                 /* Task Management Function */
        u8 reserved1;           /* byte 3 Reserved */
-       u32 it_nexus;           /* SCSI I-T Nexus */
+       __le32 it_nexus;        /* SCSI I-T Nexus */
        u8 lun_id[8];           /* LUN ID for TMF request */
        __le64 tag;             /* cciss tag associated w/ request */
        __le64 abort_tag;       /* cciss tag of SCSI cmd or TMF to abort */
        __le64 error_ptr;               /* Error Pointer */
        __le32 error_len;               /* Error Length */
-};
+} __aligned(IOACCEL2_COMMANDLIST_ALIGNMENT);
 
 /* Configuration Table Structure */
 struct HostWrite {
index acea5d6eebd0ddf1bbd40b12814479c9382a8168..6a41c36b16b0d5dfe5edc663988388dbf71e24cd 100644 (file)
@@ -1053,7 +1053,7 @@ static int ibmvscsi_queuecommand_lck(struct scsi_cmnd *cmnd,
        memset(srp_cmd, 0x00, SRP_MAX_IU_LEN);
        srp_cmd->opcode = SRP_CMD;
        memcpy(srp_cmd->cdb, cmnd->cmnd, sizeof(srp_cmd->cdb));
-       srp_cmd->lun = cpu_to_be64(((u64)lun) << 48);
+       int_to_scsilun(lun, &srp_cmd->lun);
 
        if (!map_data_for_srp_cmd(cmnd, evt_struct, srp_cmd, hostdata->dev)) {
                if (!firmware_has_feature(FW_FEATURE_CMO))
@@ -1529,7 +1529,7 @@ static int ibmvscsi_eh_abort_handler(struct scsi_cmnd *cmd)
                /* Set up an abort SRP command */
                memset(tsk_mgmt, 0x00, sizeof(*tsk_mgmt));
                tsk_mgmt->opcode = SRP_TSK_MGMT;
-               tsk_mgmt->lun = cpu_to_be64(((u64) lun) << 48);
+               int_to_scsilun(lun, &tsk_mgmt->lun);
                tsk_mgmt->tsk_mgmt_func = SRP_TSK_ABORT_TASK;
                tsk_mgmt->task_tag = (u64) found_evt;
 
@@ -1652,7 +1652,7 @@ static int ibmvscsi_eh_device_reset_handler(struct scsi_cmnd *cmd)
                /* Set up a lun reset SRP command */
                memset(tsk_mgmt, 0x00, sizeof(*tsk_mgmt));
                tsk_mgmt->opcode = SRP_TSK_MGMT;
-               tsk_mgmt->lun = cpu_to_be64(((u64) lun) << 48);
+               int_to_scsilun(lun, &tsk_mgmt->lun);
                tsk_mgmt->tsk_mgmt_func = SRP_TSK_LUN_RESET;
 
                evt->sync_srp = &srp_rsp;
index 89a8266560d0ebc2ecf44bb33f136b8cebb64ae9..4e1a632ccf162ea52365b84e71aab19b44619280 100644 (file)
@@ -1109,7 +1109,6 @@ static struct scsi_host_template imm_template = {
        .bios_param             = imm_biosparam,
        .this_id                = 7,
        .sg_tablesize           = SG_ALL,
-       .cmd_per_lun            = 1,
        .use_clustering         = ENABLE_CLUSTERING,
        .can_queue              = 1,
        .slave_alloc            = imm_adjust_queue,
index e5dae7b54d9a8c9bf093110b7cf39b187e5d8ddb..6a926bae76b265cea23e1f5386cfc32fecdb6fef 100644 (file)
@@ -2833,7 +2833,6 @@ static struct scsi_host_template initio_template = {
        .can_queue              = MAX_TARGETS * i91u_MAXQUEUE,
        .this_id                = 1,
        .sg_tablesize           = SG_ALL,
-       .cmd_per_lun            = 1,
        .use_clustering         = ENABLE_CLUSTERING,
 };
 
index 47412cf4eaac598ef8acb1f6a874c5a8b1c2e3c7..73790a1d096902fbda79f39ca7bd5c1bd43a1119 100644 (file)
 #define IPR_RUNTIME_RESET                              0x40000000
 
 #define IPR_IPL_INIT_MIN_STAGE_TIME                    5
-#define IPR_IPL_INIT_DEFAULT_STAGE_TIME                 15
+#define IPR_IPL_INIT_DEFAULT_STAGE_TIME                 30
 #define IPR_IPL_INIT_STAGE_UNKNOWN                     0x0
 #define IPR_IPL_INIT_STAGE_TRANSOP                     0xB0000000
 #define IPR_IPL_INIT_STAGE_MASK                                0xff000000
index 7542f11d3fcdb35c798714ed38501cb39086178e..02cb76fd442084cbc7ba108a00002f513c119f86 100644 (file)
@@ -206,10 +206,6 @@ module_param(ips, charp, 0);
 #define IPS_VERSION_HIGH        IPS_VER_MAJOR_STRING "." IPS_VER_MINOR_STRING
 #define IPS_VERSION_LOW         "." IPS_VER_BUILD_STRING " "
 
-#if !defined(__i386__) && !defined(__ia64__) && !defined(__x86_64__)
-#warning "This driver has only been tested on the x86/ia64/x86_64 platforms"
-#endif
-
 #define IPS_DMA_DIR(scb) ((!scb->scsi_cmd || ips_is_passthru(scb->scsi_cmd) || \
                          DMA_NONE == scb->scsi_cmd->sc_data_direction) ? \
                          PCI_DMA_BIDIRECTIONAL : \
@@ -6788,6 +6784,11 @@ ips_remove_device(struct pci_dev *pci_dev)
 static int __init
 ips_module_init(void)
 {
+#if !defined(__i386__) && !defined(__ia64__) && !defined(__x86_64__)
+       printk(KERN_ERR "ips: This driver has only been tested on the x86/ia64/x86_64 platforms\n");
+       add_taint(TAINT_CPU_OUT_OF_SPEC, LOCKDEP_STILL_OK);
+#endif
+
        if (pci_register_driver(&ips_pci_driver) < 0)
                return -ENODEV;
        ips_driver_template.module = THIS_MODULE;
index cd41b63a2f10f3c943b3463efd9e633999708501..0dfcabe3ca7c2c7862b3712a391f2cd4ba19015a 100644 (file)
@@ -160,7 +160,6 @@ static struct scsi_host_template isci_sht = {
        .change_queue_depth             = sas_change_queue_depth,
        .bios_param                     = sas_bios_param,
        .can_queue                      = ISCI_CAN_QUEUE_VAL,
-       .cmd_per_lun                    = 1,
        .this_id                        = -1,
        .sg_tablesize                   = SG_ALL,
        .max_sectors                    = SCSI_DEFAULT_MAX_SECTORS,
index 9b81a34d7449b3dc5e6c7cba78e0144cf5106460..a5a56fa31e70bc47bcca113ce6bfb4d58a6472c5 100644 (file)
@@ -230,6 +230,8 @@ struct lpfc_stats {
        uint32_t elsRcvRRQ;
        uint32_t elsRcvRTV;
        uint32_t elsRcvECHO;
+       uint32_t elsRcvLCB;
+       uint32_t elsRcvRDP;
        uint32_t elsXmitFLOGI;
        uint32_t elsXmitFDISC;
        uint32_t elsXmitPLOGI;
index 587e3e962f2b87279d7b9ae25c3d943427021292..b0e6fe46448d000f13efc41bdeffd509ee8395de 100644 (file)
@@ -498,3 +498,5 @@ bool lpfc_disable_oas_lun(struct lpfc_hba *, struct lpfc_name *,
 bool lpfc_find_next_oas_lun(struct lpfc_hba *, struct lpfc_name *,
                            struct lpfc_name *, uint64_t *, struct lpfc_name *,
                            struct lpfc_name *, uint64_t *, uint32_t *);
+int lpfc_sli4_dump_page_a0(struct lpfc_hba *phba, struct lpfcMboxq *mbox);
+void lpfc_mbx_cmpl_rdp_page_a0(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb);
index 513edcb0c2dae3794b302282b62a730eeda0c6f5..25aa9b98d53aa3452fb670b55f3efe810062e27a 100644 (file)
@@ -710,7 +710,7 @@ lpfc_debugfs_slow_ring_trc(struct lpfc_hba *phba, char *fmt,
  * returns a pointer to that log in the private_data field in @file.
  *
  * Returns:
- * This function returns zero if successful. On error it will return an negative
+ * This function returns zero if successful. On error it will return a negative
  * error value.
  **/
 static int
@@ -760,7 +760,7 @@ out:
  * returns a pointer to that log in the private_data field in @file.
  *
  * Returns:
- * This function returns zero if successful. On error it will return an negative
+ * This function returns zero if successful. On error it will return a negative
  * error value.
  **/
 static int
@@ -810,7 +810,7 @@ out:
  * returns a pointer to that log in the private_data field in @file.
  *
  * Returns:
- * This function returns zero if successful. On error it will return an negative
+ * This function returns zero if successful. On error it will return a negative
  * error value.
  **/
 static int
@@ -852,7 +852,7 @@ out:
  * returns a pointer to that log in the private_data field in @file.
  *
  * Returns:
- * This function returns zero if successful. On error it will return an negative
+ * This function returns zero if successful. On error it will return a negative
  * error value.
  **/
 static int
@@ -894,7 +894,7 @@ out:
  * returns a pointer to that log in the private_data field in @file.
  *
  * Returns:
- * This function returns zero if successful. On error it will return an negative
+ * This function returns zero if successful. On error it will return a negative
  * error value.
  **/
 static int
@@ -1115,7 +1115,7 @@ lpfc_debugfs_dif_err_release(struct inode *inode, struct file *file)
  * returns a pointer to that log in the private_data field in @file.
  *
  * Returns:
- * This function returns zero if successful. On error it will return an negative
+ * This function returns zero if successful. On error it will return a negative
  * error value.
  **/
 static int
index 6977027979beb6be433adad4cf9211c7787ba214..361f5b3d9d936bcdb075caf9405231e70c97016b 100644 (file)
@@ -79,7 +79,6 @@ struct lpfc_nodelist {
        struct lpfc_name nlp_portname;
        struct lpfc_name nlp_nodename;
        uint32_t         nlp_flag;              /* entry flags */
-       uint32_t         nlp_add_flag;          /* additional flags */
        uint32_t         nlp_DID;               /* FC D_ID of entry */
        uint32_t         nlp_last_elscmd;       /* Last ELS cmd sent */
        uint16_t         nlp_type;
@@ -147,6 +146,7 @@ struct lpfc_node_rrq {
 #define NLP_LOGO_ACC       0x00100000  /* Process LOGO after ACC completes */
 #define NLP_TGT_NO_SCSIID  0x00200000  /* good PRLI but no binding for scsid */
 #define NLP_ISSUE_LOGO     0x00400000  /* waiting to issue a LOGO */
+#define NLP_IN_DEV_LOSS    0x00800000  /* devloss in progress */
 #define NLP_ACC_REGLOGIN   0x01000000  /* Issue Reg Login after successful
                                           ACC */
 #define NLP_NPR_ADISC      0x02000000  /* Issue ADISC when dq'ed from
@@ -158,8 +158,6 @@ struct lpfc_node_rrq {
 #define NLP_FIRSTBURST     0x40000000  /* Target supports FirstBurst */
 #define NLP_RPI_REGISTERED 0x80000000  /* nlp_rpi is valid */
 
-/* Defines for nlp_add_flag (uint32) */
-#define NLP_IN_DEV_LOSS  0x00000001    /* Dev Loss processing in progress */
 
 /* ndlp usage management macros */
 #define NLP_CHK_NODE_ACT(ndlp)         (((ndlp)->nlp_usg_map \
index 851e8efe364e066ad1889ef421a02040d645e20c..36bf58ba750ad3d3b13770acda31f9047d5708ad 100644 (file)
@@ -1509,12 +1509,14 @@ lpfc_plogi_confirm_nport(struct lpfc_hba *phba, uint32_t *prsp,
                         struct lpfc_nodelist *ndlp)
 {
        struct lpfc_vport    *vport = ndlp->vport;
+       struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
        struct lpfc_nodelist *new_ndlp;
        struct lpfc_rport_data *rdata;
        struct fc_rport *rport;
        struct serv_parm *sp;
        uint8_t  name[sizeof(struct lpfc_name)];
-       uint32_t rc, keepDID = 0;
+       uint32_t rc, keepDID = 0, keep_nlp_flag = 0;
+       uint16_t keep_nlp_state;
        int  put_node;
        int  put_rport;
        unsigned long *active_rrqs_xri_bitmap = NULL;
@@ -1603,11 +1605,14 @@ lpfc_plogi_confirm_nport(struct lpfc_hba *phba, uint32_t *prsp,
                       ndlp->active_rrqs_xri_bitmap,
                       phba->cfg_rrq_xri_bitmap_sz);
 
-       if (ndlp->nlp_flag & NLP_NPR_2B_DISC)
-               new_ndlp->nlp_flag |= NLP_NPR_2B_DISC;
-       ndlp->nlp_flag &= ~NLP_NPR_2B_DISC;
+       spin_lock_irq(shost->host_lock);
+       keep_nlp_flag = new_ndlp->nlp_flag;
+       new_ndlp->nlp_flag = ndlp->nlp_flag;
+       ndlp->nlp_flag = keep_nlp_flag;
+       spin_unlock_irq(shost->host_lock);
 
-       /* Set state will put new_ndlp on to node list if not already done */
+       /* Set nlp_states accordingly */
+       keep_nlp_state = new_ndlp->nlp_state;
        lpfc_nlp_set_state(vport, new_ndlp, ndlp->nlp_state);
 
        /* Move this back to NPR state */
@@ -1624,8 +1629,9 @@ lpfc_plogi_confirm_nport(struct lpfc_hba *phba, uint32_t *prsp,
                if (rport) {
                        rdata = rport->dd_data;
                        if (rdata->pnode == ndlp) {
-                               lpfc_nlp_put(ndlp);
+                               /* break the link before dropping the ref */
                                ndlp->rport = NULL;
+                               lpfc_nlp_put(ndlp);
                                rdata->pnode = lpfc_nlp_get(new_ndlp);
                                new_ndlp->rport = rport;
                        }
@@ -1648,7 +1654,9 @@ lpfc_plogi_confirm_nport(struct lpfc_hba *phba, uint32_t *prsp,
                        memcpy(ndlp->active_rrqs_xri_bitmap,
                               active_rrqs_xri_bitmap,
                               phba->cfg_rrq_xri_bitmap_sz);
-               lpfc_drop_node(vport, ndlp);
+
+               if (!NLP_CHK_NODE_ACT(ndlp))
+                       lpfc_drop_node(vport, ndlp);
        }
        else {
                lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
@@ -1665,20 +1673,13 @@ lpfc_plogi_confirm_nport(struct lpfc_hba *phba, uint32_t *prsp,
                               active_rrqs_xri_bitmap,
                               phba->cfg_rrq_xri_bitmap_sz);
 
-               /* Since we are swapping the ndlp passed in with the new one
-                * and the did has already been swapped, copy over state.
-                * The new WWNs are already in new_ndlp since thats what
-                * we looked it up by in the begining of this routine.
-                */
-               new_ndlp->nlp_state = ndlp->nlp_state;
-
-               /* Since we are switching over to the new_ndlp, the old
-                * ndlp should be put in the NPR state, unless we have
-                * already started re-discovery on it.
+               /* Since we are switching over to the new_ndlp,
+                * reset the old ndlp state
                 */
                if ((ndlp->nlp_state == NLP_STE_UNMAPPED_NODE) ||
                    (ndlp->nlp_state == NLP_STE_MAPPED_NODE))
-                       lpfc_nlp_set_state(vport, ndlp, NLP_STE_NPR_NODE);
+                       keep_nlp_state = NLP_STE_NPR_NODE;
+               lpfc_nlp_set_state(vport, ndlp, keep_nlp_state);
 
                /* Fix up the rport accordingly */
                rport = ndlp->rport;
@@ -3667,15 +3668,6 @@ lpfc_cmpl_els_logo_acc(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
         * At this point, the driver is done so release the IOCB
         */
        lpfc_els_free_iocb(phba, cmdiocb);
-
-       /*
-        * Remove the ndlp reference if it's a fabric node that has
-        * sent us an unsolicted LOGO.
-        */
-       if (ndlp->nlp_type & NLP_FABRIC)
-               lpfc_nlp_put(ndlp);
-
-       return;
 }
 
 /**
@@ -4020,7 +4012,9 @@ lpfc_els_rsp_acc(struct lpfc_vport *vport, uint32_t flag,
                         ndlp->nlp_rpi, vport->fc_flag);
        if (ndlp->nlp_flag & NLP_LOGO_ACC) {
                spin_lock_irq(shost->host_lock);
-               ndlp->nlp_flag &= ~NLP_LOGO_ACC;
+               if (!(ndlp->nlp_flag & NLP_RPI_REGISTERED ||
+                       ndlp->nlp_flag & NLP_REG_LOGIN_SEND))
+                       ndlp->nlp_flag &= ~NLP_LOGO_ACC;
                spin_unlock_irq(shost->host_lock);
                elsiocb->iocb_cmpl = lpfc_cmpl_els_logo_acc;
        } else {
@@ -4587,16 +4581,16 @@ lpfc_els_disc_plogi(struct lpfc_vport *vport)
                if (!NLP_CHK_NODE_ACT(ndlp))
                        continue;
                if (ndlp->nlp_state == NLP_STE_NPR_NODE &&
-                   (ndlp->nlp_flag & NLP_NPR_2B_DISC) != 0 &&
-                   (ndlp->nlp_flag & NLP_DELAY_TMO) == 0 &&
-                   (ndlp->nlp_flag & NLP_NPR_ADISC) == 0) {
+                               (ndlp->nlp_flag & NLP_NPR_2B_DISC) != 0 &&
+                               (ndlp->nlp_flag & NLP_DELAY_TMO) == 0 &&
+                               (ndlp->nlp_flag & NLP_NPR_ADISC) == 0) {
                        ndlp->nlp_prev_state = ndlp->nlp_state;
                        lpfc_nlp_set_state(vport, ndlp, NLP_STE_PLOGI_ISSUE);
                        lpfc_issue_els_plogi(vport, ndlp->nlp_DID, 0);
                        sentplogi++;
                        vport->num_disc_nodes++;
                        if (vport->num_disc_nodes >=
-                           vport->cfg_discovery_threads) {
+                                       vport->cfg_discovery_threads) {
                                spin_lock_irq(shost->host_lock);
                                vport->fc_flag |= FC_NLP_MORE;
                                spin_unlock_irq(shost->host_lock);
@@ -4615,6 +4609,660 @@ lpfc_els_disc_plogi(struct lpfc_vport *vport)
        return sentplogi;
 }
 
+void
+lpfc_rdp_res_link_service(struct fc_rdp_link_service_desc *desc,
+               uint32_t word0)
+{
+
+       desc->tag = cpu_to_be32(RDP_LINK_SERVICE_DESC_TAG);
+       desc->payload.els_req = word0;
+       desc->length = cpu_to_be32(sizeof(desc->payload));
+}
+
+void
+lpfc_rdp_res_sfp_desc(struct fc_rdp_sfp_desc *desc,
+               uint8_t *page_a0, uint8_t *page_a2)
+{
+       uint16_t wavelength;
+       uint16_t temperature;
+       uint16_t rx_power;
+       uint16_t tx_bias;
+       uint16_t tx_power;
+       uint16_t vcc;
+       uint16_t flag = 0;
+       struct sff_trasnceiver_codes_byte4 *trasn_code_byte4;
+       struct sff_trasnceiver_codes_byte5 *trasn_code_byte5;
+
+       desc->tag = cpu_to_be32(RDP_SFP_DESC_TAG);
+
+       trasn_code_byte4 = (struct sff_trasnceiver_codes_byte4 *)
+                       &page_a0[SSF_TRANSCEIVER_CODE_B4];
+       trasn_code_byte5 = (struct sff_trasnceiver_codes_byte5 *)
+                       &page_a0[SSF_TRANSCEIVER_CODE_B5];
+
+       if ((trasn_code_byte4->fc_sw_laser) ||
+           (trasn_code_byte5->fc_sw_laser_sl) ||
+           (trasn_code_byte5->fc_sw_laser_sn)) {  /* check if its short WL */
+               flag |= (SFP_FLAG_PT_SWLASER << SFP_FLAG_PT_SHIFT);
+       } else if (trasn_code_byte4->fc_lw_laser) {
+               wavelength = (page_a0[SSF_WAVELENGTH_B1] << 8) |
+                       page_a0[SSF_WAVELENGTH_B0];
+               if (wavelength == SFP_WAVELENGTH_LC1310)
+                       flag |= SFP_FLAG_PT_LWLASER_LC1310 << SFP_FLAG_PT_SHIFT;
+               if (wavelength == SFP_WAVELENGTH_LL1550)
+                       flag |= SFP_FLAG_PT_LWLASER_LL1550 << SFP_FLAG_PT_SHIFT;
+       }
+       /* check if its SFP+ */
+       flag |= ((page_a0[SSF_IDENTIFIER] == SFF_PG0_IDENT_SFP) ?
+                       SFP_FLAG_CT_SFP_PLUS : SFP_FLAG_CT_UNKNOWN)
+                                       << SFP_FLAG_CT_SHIFT;
+
+       /* check if its OPTICAL */
+       flag |= ((page_a0[SSF_CONNECTOR] == SFF_PG0_CONNECTOR_LC) ?
+                       SFP_FLAG_IS_OPTICAL_PORT : 0)
+                                       << SFP_FLAG_IS_OPTICAL_SHIFT;
+
+       temperature = (page_a2[SFF_TEMPERATURE_B1] << 8 |
+               page_a2[SFF_TEMPERATURE_B0]);
+       vcc = (page_a2[SFF_VCC_B1] << 8 |
+               page_a2[SFF_VCC_B0]);
+       tx_power = (page_a2[SFF_TXPOWER_B1] << 8 |
+               page_a2[SFF_TXPOWER_B0]);
+       tx_bias = (page_a2[SFF_TX_BIAS_CURRENT_B1] << 8 |
+               page_a2[SFF_TX_BIAS_CURRENT_B0]);
+       rx_power = (page_a2[SFF_RXPOWER_B1] << 8 |
+               page_a2[SFF_RXPOWER_B0]);
+       desc->sfp_info.temperature = cpu_to_be16(temperature);
+       desc->sfp_info.rx_power = cpu_to_be16(rx_power);
+       desc->sfp_info.tx_bias = cpu_to_be16(tx_bias);
+       desc->sfp_info.tx_power = cpu_to_be16(tx_power);
+       desc->sfp_info.vcc = cpu_to_be16(vcc);
+
+       desc->sfp_info.flags = cpu_to_be16(flag);
+       desc->length = cpu_to_be32(sizeof(desc->sfp_info));
+}
+
+void
+lpfc_rdp_res_link_error(struct fc_rdp_link_error_status_desc *desc,
+               READ_LNK_VAR *stat)
+{
+       uint32_t type;
+
+       desc->tag = cpu_to_be32(RDP_LINK_ERROR_STATUS_DESC_TAG);
+
+       type = VN_PT_PHY_PF_PORT << VN_PT_PHY_SHIFT;
+
+       desc->info.port_type = cpu_to_be32(type);
+
+       desc->info.link_status.link_failure_cnt =
+               cpu_to_be32(stat->linkFailureCnt);
+       desc->info.link_status.loss_of_synch_cnt =
+               cpu_to_be32(stat->lossSyncCnt);
+       desc->info.link_status.loss_of_signal_cnt =
+               cpu_to_be32(stat->lossSignalCnt);
+       desc->info.link_status.primitive_seq_proto_err =
+               cpu_to_be32(stat->primSeqErrCnt);
+       desc->info.link_status.invalid_trans_word =
+               cpu_to_be32(stat->invalidXmitWord);
+       desc->info.link_status.invalid_crc_cnt = cpu_to_be32(stat->crcCnt);
+
+       desc->length = cpu_to_be32(sizeof(desc->info));
+}
+
+void
+lpfc_rdp_res_speed(struct fc_rdp_port_speed_desc *desc, struct lpfc_hba *phba)
+{
+       uint16_t rdp_cap = 0;
+       uint16_t rdp_speed;
+
+       desc->tag = cpu_to_be32(RDP_PORT_SPEED_DESC_TAG);
+
+       switch (phba->sli4_hba.link_state.speed) {
+       case LPFC_FC_LA_SPEED_1G:
+               rdp_speed = RDP_PS_1GB;
+               break;
+       case LPFC_FC_LA_SPEED_2G:
+               rdp_speed = RDP_PS_2GB;
+               break;
+       case LPFC_FC_LA_SPEED_4G:
+               rdp_speed = RDP_PS_4GB;
+               break;
+       case LPFC_FC_LA_SPEED_8G:
+               rdp_speed = RDP_PS_8GB;
+               break;
+       case LPFC_FC_LA_SPEED_10G:
+               rdp_speed = RDP_PS_10GB;
+               break;
+       case LPFC_FC_LA_SPEED_16G:
+               rdp_speed = RDP_PS_16GB;
+               break;
+       case LPFC_FC_LA_SPEED_32G:
+               rdp_speed = RDP_PS_32GB;
+               break;
+       default:
+               rdp_speed = RDP_PS_UNKNOWN;
+               break;
+       }
+
+       desc->info.port_speed.speed = cpu_to_be16(rdp_speed);
+
+       if (phba->lmt & LMT_16Gb)
+               rdp_cap |= RDP_PS_16GB;
+       if (phba->lmt & LMT_10Gb)
+               rdp_cap |= RDP_PS_10GB;
+       if (phba->lmt & LMT_8Gb)
+               rdp_cap |= RDP_PS_8GB;
+       if (phba->lmt & LMT_4Gb)
+               rdp_cap |= RDP_PS_4GB;
+       if (phba->lmt & LMT_2Gb)
+               rdp_cap |= RDP_PS_2GB;
+       if (phba->lmt & LMT_1Gb)
+               rdp_cap |= RDP_PS_1GB;
+
+       if (rdp_cap == 0)
+               rdp_cap = RDP_CAP_UNKNOWN;
+
+       desc->info.port_speed.capabilities = cpu_to_be16(rdp_cap);
+       desc->length = cpu_to_be32(sizeof(desc->info));
+}
+
+void
+lpfc_rdp_res_diag_port_names(struct fc_rdp_port_name_desc *desc,
+               struct lpfc_hba *phba)
+{
+
+       desc->tag = cpu_to_be32(RDP_PORT_NAMES_DESC_TAG);
+
+       memcpy(desc->port_names.wwnn, phba->wwnn,
+                       sizeof(desc->port_names.wwnn));
+
+       memcpy(desc->port_names.wwpn, &phba->wwpn,
+                       sizeof(desc->port_names.wwpn));
+
+       desc->length = cpu_to_be32(sizeof(desc->port_names));
+}
+
+void
+lpfc_rdp_res_attach_port_names(struct fc_rdp_port_name_desc *desc,
+               struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
+{
+
+       desc->tag = cpu_to_be32(RDP_PORT_NAMES_DESC_TAG);
+       if (vport->fc_flag & FC_FABRIC) {
+               memcpy(desc->port_names.wwnn, &vport->fabric_nodename,
+                               sizeof(desc->port_names.wwnn));
+
+               memcpy(desc->port_names.wwpn, &vport->fabric_portname,
+                               sizeof(desc->port_names.wwpn));
+       } else {  /* Point to Point */
+               memcpy(desc->port_names.wwnn, &ndlp->nlp_nodename,
+                               sizeof(desc->port_names.wwnn));
+
+               memcpy(desc->port_names.wwnn, &ndlp->nlp_portname,
+                               sizeof(desc->port_names.wwpn));
+       }
+
+       desc->length = cpu_to_be32(sizeof(desc->port_names));
+}
+
+void
+lpfc_els_rdp_cmpl(struct lpfc_hba *phba, struct lpfc_rdp_context *rdp_context,
+               int status)
+{
+       struct lpfc_nodelist *ndlp = rdp_context->ndlp;
+       struct lpfc_vport *vport = ndlp->vport;
+       struct lpfc_iocbq *elsiocb;
+       IOCB_t *icmd;
+       uint8_t *pcmd;
+       struct ls_rjt *stat;
+       struct fc_rdp_res_frame *rdp_res;
+       uint32_t cmdsize;
+       int rc;
+
+       if (status != SUCCESS)
+               goto error;
+       cmdsize = sizeof(struct fc_rdp_res_frame);
+
+       elsiocb = lpfc_prep_els_iocb(vport, 0, cmdsize,
+                       lpfc_max_els_tries, rdp_context->ndlp,
+                       rdp_context->ndlp->nlp_DID, ELS_CMD_ACC);
+       lpfc_nlp_put(ndlp);
+       if (!elsiocb)
+               goto free_rdp_context;
+
+       icmd = &elsiocb->iocb;
+       icmd->ulpContext = rdp_context->rx_id;
+       icmd->unsli3.rcvsli3.ox_id = rdp_context->ox_id;
+
+       lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
+                       "2171 Xmit RDP response tag x%x xri x%x, "
+                       "did x%x, nlp_flag x%x, nlp_state x%x, rpi x%x",
+                       elsiocb->iotag, elsiocb->iocb.ulpContext,
+                       ndlp->nlp_DID, ndlp->nlp_flag, ndlp->nlp_state,
+                       ndlp->nlp_rpi);
+       rdp_res = (struct fc_rdp_res_frame *)
+               (((struct lpfc_dmabuf *) elsiocb->context2)->virt);
+       pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt);
+       memset(pcmd, 0, sizeof(struct fc_rdp_res_frame));
+       *((uint32_t *) (pcmd)) = ELS_CMD_ACC;
+
+       /* For RDP payload */
+       lpfc_rdp_res_link_service(&rdp_res->link_service_desc, ELS_CMD_RDP);
+
+       lpfc_rdp_res_sfp_desc(&rdp_res->sfp_desc,
+                       rdp_context->page_a0, rdp_context->page_a2);
+       lpfc_rdp_res_speed(&rdp_res->portspeed_desc, phba);
+       lpfc_rdp_res_link_error(&rdp_res->link_error_desc,
+                       &rdp_context->link_stat);
+       lpfc_rdp_res_diag_port_names(&rdp_res->diag_port_names_desc, phba);
+       lpfc_rdp_res_attach_port_names(&rdp_res->attached_port_names_desc,
+                       vport, ndlp);
+       rdp_res->length = cpu_to_be32(RDP_DESC_PAYLOAD_SIZE);
+
+       elsiocb->iocb_cmpl = lpfc_cmpl_els_rsp;
+
+       phba->fc_stat.elsXmitACC++;
+       rc = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 0);
+       if (rc == IOCB_ERROR)
+               lpfc_els_free_iocb(phba, elsiocb);
+
+       kfree(rdp_context);
+
+       return;
+error:
+       cmdsize = 2 * sizeof(uint32_t);
+       elsiocb = lpfc_prep_els_iocb(vport, 0, cmdsize, lpfc_max_els_tries,
+                       ndlp, ndlp->nlp_DID, ELS_CMD_LS_RJT);
+       lpfc_nlp_put(ndlp);
+       if (!elsiocb)
+               goto free_rdp_context;
+
+       icmd = &elsiocb->iocb;
+       icmd->ulpContext = rdp_context->rx_id;
+       icmd->unsli3.rcvsli3.ox_id = rdp_context->ox_id;
+       pcmd = (uint8_t *) (((struct lpfc_dmabuf *) elsiocb->context2)->virt);
+
+       *((uint32_t *) (pcmd)) = ELS_CMD_LS_RJT;
+       stat = (struct ls_rjt *)(pcmd + sizeof(uint32_t));
+       stat->un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC;
+
+       phba->fc_stat.elsXmitLSRJT++;
+       elsiocb->iocb_cmpl = lpfc_cmpl_els_rsp;
+       rc = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 0);
+
+       if (rc == IOCB_ERROR)
+               lpfc_els_free_iocb(phba, elsiocb);
+free_rdp_context:
+       kfree(rdp_context);
+}
+
+int
+lpfc_get_rdp_info(struct lpfc_hba *phba, struct lpfc_rdp_context *rdp_context)
+{
+       LPFC_MBOXQ_t *mbox = NULL;
+       int rc;
+
+       mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+       if (!mbox) {
+               lpfc_printf_log(phba, KERN_WARNING, LOG_MBOX | LOG_ELS,
+                               "7105 failed to allocate mailbox memory");
+               return 1;
+       }
+
+       if (lpfc_sli4_dump_page_a0(phba, mbox))
+               goto prep_mbox_fail;
+       mbox->vport = rdp_context->ndlp->vport;
+       mbox->mbox_cmpl = lpfc_mbx_cmpl_rdp_page_a0;
+       mbox->context2 = (struct lpfc_rdp_context *) rdp_context;
+       rc = lpfc_sli_issue_mbox(phba, mbox, MBX_NOWAIT);
+       if (rc == MBX_NOT_FINISHED)
+               goto issue_mbox_fail;
+
+       return 0;
+
+prep_mbox_fail:
+issue_mbox_fail:
+       mempool_free(mbox, phba->mbox_mem_pool);
+       return 1;
+}
+
+/*
+ * lpfc_els_rcv_rdp - Process an unsolicited RDP ELS.
+ * @vport: pointer to a host virtual N_Port data structure.
+ * @cmdiocb: pointer to lpfc command iocb data structure.
+ * @ndlp: pointer to a node-list data structure.
+ *
+ * This routine processes an unsolicited RDP(Read Diagnostic Parameters)
+ * IOCB. First, the payload of the unsolicited RDP is checked.
+ * Then it will (1) send MBX_DUMP_MEMORY, Embedded DMP_LMSD sub command TYPE-3
+ * for Page A0, (2) send MBX_DUMP_MEMORY, DMP_LMSD for Page A2,
+ * (3) send MBX_READ_LNK_STAT to get link stat, (4) Call lpfc_els_rdp_cmpl
+ * gather all data and send RDP response.
+ *
+ * Return code
+ *   0 - Sent the acc response
+ *   1 - Sent the reject response.
+ */
+static int
+lpfc_els_rcv_rdp(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,
+               struct lpfc_nodelist *ndlp)
+{
+       struct lpfc_hba *phba = vport->phba;
+       struct lpfc_dmabuf *pcmd;
+       uint8_t rjt_err, rjt_expl = LSEXP_NOTHING_MORE;
+       struct fc_rdp_req_frame *rdp_req;
+       struct lpfc_rdp_context *rdp_context;
+       IOCB_t *cmd = NULL;
+       struct ls_rjt stat;
+
+       if (phba->sli_rev < LPFC_SLI_REV4 ||
+                       (bf_get(lpfc_sli_intf_if_type,
+                               &phba->sli4_hba.sli_intf) !=
+                                               LPFC_SLI_INTF_IF_TYPE_2)) {
+               rjt_err = LSRJT_UNABLE_TPC;
+               rjt_expl = LSEXP_REQ_UNSUPPORTED;
+               goto error;
+       }
+
+       if (phba->sli_rev < LPFC_SLI_REV4 || (phba->hba_flag & HBA_FCOE_MODE)) {
+               rjt_err = LSRJT_UNABLE_TPC;
+               rjt_expl = LSEXP_REQ_UNSUPPORTED;
+               goto error;
+       }
+
+       pcmd = (struct lpfc_dmabuf *) cmdiocb->context2;
+       rdp_req = (struct fc_rdp_req_frame *) pcmd->virt;
+
+
+       lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
+                        "2422 ELS RDP Request "
+                        "dec len %d tag x%x port_id %d len %d\n",
+                        be32_to_cpu(rdp_req->rdp_des_length),
+                        be32_to_cpu(rdp_req->nport_id_desc.tag),
+                        be32_to_cpu(rdp_req->nport_id_desc.nport_id),
+                        be32_to_cpu(rdp_req->nport_id_desc.length));
+
+       if (sizeof(struct fc_rdp_nport_desc) !=
+                       be32_to_cpu(rdp_req->rdp_des_length))
+               goto rjt_logerr;
+       if (RDP_N_PORT_DESC_TAG != be32_to_cpu(rdp_req->nport_id_desc.tag))
+               goto rjt_logerr;
+       if (RDP_NPORT_ID_SIZE !=
+                       be32_to_cpu(rdp_req->nport_id_desc.length))
+               goto rjt_logerr;
+       rdp_context = kmalloc(sizeof(struct lpfc_rdp_context), GFP_KERNEL);
+       if (!rdp_context) {
+               rjt_err = LSRJT_UNABLE_TPC;
+               goto error;
+       }
+
+       memset(rdp_context, 0, sizeof(struct lpfc_rdp_context));
+       cmd = &cmdiocb->iocb;
+       rdp_context->ndlp = lpfc_nlp_get(ndlp);
+       rdp_context->ox_id = cmd->unsli3.rcvsli3.ox_id;
+       rdp_context->rx_id = cmd->ulpContext;
+       rdp_context->cmpl = lpfc_els_rdp_cmpl;
+       if (lpfc_get_rdp_info(phba, rdp_context)) {
+               lpfc_printf_vlog(ndlp->vport, KERN_WARNING, LOG_ELS,
+                                "2423 Unable to send mailbox");
+               kfree(rdp_context);
+               rjt_err = LSRJT_UNABLE_TPC;
+               lpfc_nlp_put(ndlp);
+               goto error;
+       }
+
+       return 0;
+
+rjt_logerr:
+       rjt_err = LSRJT_LOGICAL_ERR;
+
+error:
+       memset(&stat, 0, sizeof(stat));
+       stat.un.b.lsRjtRsnCode = rjt_err;
+       stat.un.b.lsRjtRsnCodeExp = rjt_expl;
+       lpfc_els_rsp_reject(vport, stat.un.lsRjtError, cmdiocb, ndlp, NULL);
+       return 1;
+}
+
+
+static void
+lpfc_els_lcb_rsp(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
+{
+       MAILBOX_t *mb;
+       IOCB_t *icmd;
+       uint8_t *pcmd;
+       struct lpfc_iocbq *elsiocb;
+       struct lpfc_nodelist *ndlp;
+       struct ls_rjt *stat;
+       union lpfc_sli4_cfg_shdr *shdr;
+       struct lpfc_lcb_context *lcb_context;
+       struct fc_lcb_res_frame *lcb_res;
+       uint32_t cmdsize, shdr_status, shdr_add_status;
+       int rc;
+
+       mb = &pmb->u.mb;
+       lcb_context = (struct lpfc_lcb_context *)pmb->context1;
+       ndlp = lcb_context->ndlp;
+       pmb->context1 = NULL;
+       pmb->context2 = NULL;
+
+       shdr = (union lpfc_sli4_cfg_shdr *)
+                       &pmb->u.mqe.un.beacon_config.header.cfg_shdr;
+       shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response);
+       shdr_add_status = bf_get(lpfc_mbox_hdr_add_status, &shdr->response);
+
+       lpfc_printf_log(phba, KERN_INFO, LOG_MBOX,
+                               "0194 SET_BEACON_CONFIG mailbox "
+                               "completed with status x%x add_status x%x,"
+                               " mbx status x%x\n",
+                               shdr_status, shdr_add_status, mb->mbxStatus);
+
+       if (mb->mbxStatus && !(shdr_status &&
+               shdr_add_status == ADD_STATUS_OPERATION_ALREADY_ACTIVE)) {
+               mempool_free(pmb, phba->mbox_mem_pool);
+               goto error;
+       }
+
+       mempool_free(pmb, phba->mbox_mem_pool);
+       cmdsize = sizeof(struct fc_lcb_res_frame);
+       elsiocb = lpfc_prep_els_iocb(phba->pport, 0, cmdsize,
+                       lpfc_max_els_tries, ndlp,
+                       ndlp->nlp_DID, ELS_CMD_ACC);
+
+       /* Decrement the ndlp reference count from previous mbox command */
+       lpfc_nlp_put(ndlp);
+
+       if (!elsiocb)
+               goto free_lcb_context;
+
+       lcb_res = (struct fc_lcb_res_frame *)
+               (((struct lpfc_dmabuf *)elsiocb->context2)->virt);
+
+       icmd = &elsiocb->iocb;
+       icmd->ulpContext = lcb_context->rx_id;
+       icmd->unsli3.rcvsli3.ox_id = lcb_context->ox_id;
+
+       pcmd = (uint8_t *)(((struct lpfc_dmabuf *)elsiocb->context2)->virt);
+       *((uint32_t *)(pcmd)) = ELS_CMD_ACC;
+       lcb_res->lcb_sub_command = lcb_context->sub_command;
+       lcb_res->lcb_type = lcb_context->type;
+       lcb_res->lcb_frequency = lcb_context->frequency;
+       elsiocb->iocb_cmpl = lpfc_cmpl_els_rsp;
+       phba->fc_stat.elsXmitACC++;
+       rc = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 0);
+       if (rc == IOCB_ERROR)
+               lpfc_els_free_iocb(phba, elsiocb);
+
+       kfree(lcb_context);
+       return;
+
+error:
+       cmdsize = sizeof(struct fc_lcb_res_frame);
+       elsiocb = lpfc_prep_els_iocb(phba->pport, 0, cmdsize,
+                       lpfc_max_els_tries, ndlp,
+                       ndlp->nlp_DID, ELS_CMD_LS_RJT);
+       lpfc_nlp_put(ndlp);
+       if (!elsiocb)
+               goto free_lcb_context;
+
+       icmd = &elsiocb->iocb;
+       icmd->ulpContext = lcb_context->rx_id;
+       icmd->unsli3.rcvsli3.ox_id = lcb_context->ox_id;
+       pcmd = (uint8_t *)(((struct lpfc_dmabuf *)elsiocb->context2)->virt);
+
+       *((uint32_t *)(pcmd)) = ELS_CMD_LS_RJT;
+       stat = (struct ls_rjt *)(pcmd + sizeof(uint32_t));
+       stat->un.b.lsRjtRsnCode = LSRJT_UNABLE_TPC;
+
+       elsiocb->iocb_cmpl = lpfc_cmpl_els_rsp;
+       phba->fc_stat.elsXmitLSRJT++;
+       rc = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 0);
+       if (rc == IOCB_ERROR)
+               lpfc_els_free_iocb(phba, elsiocb);
+free_lcb_context:
+       kfree(lcb_context);
+}
+
+static int
+lpfc_sli4_set_beacon(struct lpfc_vport *vport,
+                    struct lpfc_lcb_context *lcb_context,
+                    uint32_t beacon_state)
+{
+       struct lpfc_hba *phba = vport->phba;
+       LPFC_MBOXQ_t *mbox = NULL;
+       uint32_t len;
+       int rc;
+
+       mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+       if (!mbox)
+               return 1;
+
+       len = sizeof(struct lpfc_mbx_set_beacon_config) -
+               sizeof(struct lpfc_sli4_cfg_mhdr);
+       lpfc_sli4_config(phba, mbox, LPFC_MBOX_SUBSYSTEM_COMMON,
+                        LPFC_MBOX_OPCODE_SET_BEACON_CONFIG, len,
+                        LPFC_SLI4_MBX_EMBED);
+       mbox->context1 = (void *)lcb_context;
+       mbox->vport = phba->pport;
+       mbox->mbox_cmpl = lpfc_els_lcb_rsp;
+       bf_set(lpfc_mbx_set_beacon_port_num, &mbox->u.mqe.un.beacon_config,
+              phba->sli4_hba.physical_port);
+       bf_set(lpfc_mbx_set_beacon_state, &mbox->u.mqe.un.beacon_config,
+              beacon_state);
+       bf_set(lpfc_mbx_set_beacon_port_type, &mbox->u.mqe.un.beacon_config, 1);
+       bf_set(lpfc_mbx_set_beacon_duration, &mbox->u.mqe.un.beacon_config, 0);
+       rc = lpfc_sli_issue_mbox(phba, mbox, MBX_NOWAIT);
+       if (rc == MBX_NOT_FINISHED) {
+               mempool_free(mbox, phba->mbox_mem_pool);
+               return 1;
+       }
+
+       return 0;
+}
+
+
+/**
+ * lpfc_els_rcv_lcb - Process an unsolicited LCB
+ * @vport: pointer to a host virtual N_Port data structure.
+ * @cmdiocb: pointer to lpfc command iocb data structure.
+ * @ndlp: pointer to a node-list data structure.
+ *
+ * This routine processes an unsolicited LCB(LINK CABLE BEACON) IOCB.
+ * First, the payload of the unsolicited LCB is checked.
+ * Then based on Subcommand beacon will either turn on or off.
+ *
+ * Return code
+ * 0 - Sent the acc response
+ * 1 - Sent the reject response.
+ **/
+static int
+lpfc_els_rcv_lcb(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,
+                struct lpfc_nodelist *ndlp)
+{
+       struct lpfc_hba *phba = vport->phba;
+       struct lpfc_dmabuf *pcmd;
+       IOCB_t *icmd;
+       uint8_t *lp;
+       struct fc_lcb_request_frame *beacon;
+       struct lpfc_lcb_context *lcb_context;
+       uint8_t state, rjt_err;
+       struct ls_rjt stat;
+
+       icmd = &cmdiocb->iocb;
+       pcmd = (struct lpfc_dmabuf *)cmdiocb->context2;
+       lp = (uint8_t *)pcmd->virt;
+       beacon = (struct fc_lcb_request_frame *)pcmd->virt;
+
+       lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
+                       "0192 ELS LCB Data x%x x%x x%x x%x sub x%x "
+                       "type x%x frequency %x duration x%x\n",
+                       lp[0], lp[1], lp[2],
+                       beacon->lcb_command,
+                       beacon->lcb_sub_command,
+                       beacon->lcb_type,
+                       beacon->lcb_frequency,
+                       be16_to_cpu(beacon->lcb_duration));
+
+       if (phba->sli_rev < LPFC_SLI_REV4 ||
+           (bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) !=
+           LPFC_SLI_INTF_IF_TYPE_2)) {
+               rjt_err = LSRJT_CMD_UNSUPPORTED;
+               goto rjt;
+       }
+       lcb_context = kmalloc(sizeof(struct lpfc_lcb_context), GFP_KERNEL);
+
+       if (phba->hba_flag & HBA_FCOE_MODE) {
+               rjt_err = LSRJT_CMD_UNSUPPORTED;
+               goto rjt;
+       }
+       if (beacon->lcb_frequency == 0) {
+               rjt_err = LSRJT_CMD_UNSUPPORTED;
+               goto rjt;
+       }
+       if ((beacon->lcb_type != LPFC_LCB_GREEN) &&
+           (beacon->lcb_type != LPFC_LCB_AMBER)) {
+               rjt_err = LSRJT_CMD_UNSUPPORTED;
+               goto rjt;
+       }
+       if ((beacon->lcb_sub_command != LPFC_LCB_ON) &&
+           (beacon->lcb_sub_command != LPFC_LCB_OFF)) {
+               rjt_err = LSRJT_CMD_UNSUPPORTED;
+               goto rjt;
+       }
+       if ((beacon->lcb_sub_command == LPFC_LCB_ON) &&
+           (beacon->lcb_type != LPFC_LCB_GREEN) &&
+           (beacon->lcb_type != LPFC_LCB_AMBER)) {
+               rjt_err = LSRJT_CMD_UNSUPPORTED;
+               goto rjt;
+       }
+       if (be16_to_cpu(beacon->lcb_duration) != 0) {
+               rjt_err = LSRJT_CMD_UNSUPPORTED;
+               goto rjt;
+       }
+
+       state = (beacon->lcb_sub_command == LPFC_LCB_ON) ? 1 : 0;
+       lcb_context->sub_command = beacon->lcb_sub_command;
+       lcb_context->type = beacon->lcb_type;
+       lcb_context->frequency = beacon->lcb_frequency;
+       lcb_context->ox_id = cmdiocb->iocb.unsli3.rcvsli3.ox_id;
+       lcb_context->rx_id = cmdiocb->iocb.ulpContext;
+       lcb_context->ndlp = lpfc_nlp_get(ndlp);
+       if (lpfc_sli4_set_beacon(vport, lcb_context, state)) {
+               lpfc_printf_vlog(ndlp->vport, KERN_ERR,
+                                LOG_ELS, "0193 failed to send mail box");
+               lpfc_nlp_put(ndlp);
+               rjt_err = LSRJT_UNABLE_TPC;
+               goto rjt;
+       }
+       return 0;
+rjt:
+       memset(&stat, 0, sizeof(stat));
+       stat.un.b.lsRjtRsnCode = rjt_err;
+       lpfc_els_rsp_reject(vport, stat.un.lsRjtError, cmdiocb, ndlp, NULL);
+       return 1;
+}
+
+
 /**
  * lpfc_els_flush_rscn - Clean up any rscn activities with a vport
  * @vport: pointer to a host virtual N_Port data structure.
@@ -6706,8 +7354,13 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
         * Do not process any unsolicited ELS commands
         * if the ndlp is in DEV_LOSS
         */
-       if (ndlp->nlp_add_flag & NLP_IN_DEV_LOSS)
+       shost = lpfc_shost_from_vport(vport);
+       spin_lock_irq(shost->host_lock);
+       if (ndlp->nlp_flag & NLP_IN_DEV_LOSS) {
+               spin_unlock_irq(shost->host_lock);
                goto dropit;
+       }
+       spin_unlock_irq(shost->host_lock);
 
        elsiocb->context1 = lpfc_nlp_get(ndlp);
        elsiocb->vport = vport;
@@ -6751,7 +7404,6 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
                        rjt_exp = LSEXP_NOTHING_MORE;
                        break;
                }
-               shost = lpfc_shost_from_vport(vport);
                if (vport->port_state < LPFC_DISC_AUTH) {
                        if (!(phba->pport->fc_flag & FC_PT2PT) ||
                                (phba->pport->fc_flag & FC_PT2PT_PLOGI)) {
@@ -6821,6 +7473,14 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
                }
                lpfc_disc_state_machine(vport, ndlp, elsiocb, NLP_EVT_RCV_PRLO);
                break;
+       case ELS_CMD_LCB:
+               phba->fc_stat.elsRcvLCB++;
+               lpfc_els_rcv_lcb(vport, elsiocb, ndlp);
+               break;
+       case ELS_CMD_RDP:
+               phba->fc_stat.elsRcvRDP++;
+               lpfc_els_rcv_rdp(vport, elsiocb, ndlp);
+               break;
        case ELS_CMD_RSCN:
                phba->fc_stat.elsRcvRSCN++;
                lpfc_els_rcv_rscn(vport, elsiocb, ndlp);
@@ -7586,7 +8246,8 @@ lpfc_cmpl_els_fdisc(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
                lpfc_do_scr_ns_plogi(phba, vport);
        goto out;
 fdisc_failed:
-       lpfc_vport_set_state(vport, FC_VPORT_FAILED);
+       if (vport->fc_vport->vport_state != FC_VPORT_NO_FABRIC_RSCS)
+               lpfc_vport_set_state(vport, FC_VPORT_FAILED);
        /* Cancel discovery timer */
        lpfc_can_disctmo(vport);
        lpfc_nlp_put(ndlp);
@@ -7739,8 +8400,10 @@ lpfc_cmpl_els_npiv_logo(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
 
        if (irsp->ulpStatus == IOSTAT_SUCCESS) {
                spin_lock_irq(shost->host_lock);
+               vport->fc_flag &= ~FC_NDISC_ACTIVE;
                vport->fc_flag &= ~FC_FABRIC;
                spin_unlock_irq(shost->host_lock);
+               lpfc_can_disctmo(vport);
        }
 }
 
index 2500f15d437ff61a331f265b5dbc9c6d72e8baa1..ce96d5bf8ae7d8a3c2b3b2fc538e38c7a8689d0d 100644 (file)
@@ -106,6 +106,7 @@ lpfc_dev_loss_tmo_callbk(struct fc_rport *rport)
        struct lpfc_rport_data *rdata;
        struct lpfc_nodelist * ndlp;
        struct lpfc_vport *vport;
+       struct Scsi_Host *shost;
        struct lpfc_hba   *phba;
        struct lpfc_work_evt *evtp;
        int  put_node;
@@ -146,48 +147,32 @@ lpfc_dev_loss_tmo_callbk(struct fc_rport *rport)
        if (ndlp->nlp_state == NLP_STE_MAPPED_NODE)
                return;
 
-       if (ndlp->nlp_type & NLP_FABRIC) {
-
-               /* If the WWPN of the rport and ndlp don't match, ignore it */
-               if (rport->port_name != wwn_to_u64(ndlp->nlp_portname.u.wwn)) {
-                       lpfc_printf_vlog(vport, KERN_ERR, LOG_NODE,
-                               "6789 rport name %lx != node port name %lx",
-                               (unsigned long)rport->port_name,
-                               (unsigned long)wwn_to_u64(
-                                               ndlp->nlp_portname.u.wwn));
-                       put_node = rdata->pnode != NULL;
-                       put_rport = ndlp->rport != NULL;
-                       rdata->pnode = NULL;
-                       ndlp->rport = NULL;
-                       if (put_node)
-                               lpfc_nlp_put(ndlp);
-                       put_device(&rport->dev);
-                       return;
-               }
-
-               put_node = rdata->pnode != NULL;
-               put_rport = ndlp->rport != NULL;
-               rdata->pnode = NULL;
-               ndlp->rport = NULL;
-               if (put_node)
-                       lpfc_nlp_put(ndlp);
-               if (put_rport)
-                       put_device(&rport->dev);
-               return;
-       }
+       if (rport->port_name != wwn_to_u64(ndlp->nlp_portname.u.wwn))
+               lpfc_printf_vlog(vport, KERN_ERR, LOG_NODE,
+                               "6789 rport name %llx != node port name %llx",
+                               rport->port_name,
+                               wwn_to_u64(ndlp->nlp_portname.u.wwn));
 
        evtp = &ndlp->dev_loss_evt;
 
-       if (!list_empty(&evtp->evt_listp))
+       if (!list_empty(&evtp->evt_listp)) {
+               lpfc_printf_vlog(vport, KERN_ERR, LOG_NODE,
+                               "6790 rport name %llx dev_loss_evt pending",
+                               rport->port_name);
                return;
+       }
 
-       evtp->evt_arg1  = lpfc_nlp_get(ndlp);
-       ndlp->nlp_add_flag |= NLP_IN_DEV_LOSS;
+       shost = lpfc_shost_from_vport(vport);
+       spin_lock_irq(shost->host_lock);
+       ndlp->nlp_flag |= NLP_IN_DEV_LOSS;
+       spin_unlock_irq(shost->host_lock);
 
-       spin_lock_irq(&phba->hbalock);
        /* We need to hold the node by incrementing the reference
         * count until this queued work is done
         */
+       evtp->evt_arg1  = lpfc_nlp_get(ndlp);
+
+       spin_lock_irq(&phba->hbalock);
        if (evtp->evt_arg1) {
                evtp->evt = LPFC_EVT_DEV_LOSS;
                list_add_tail(&evtp->evt_listp, &phba->work_list);
@@ -215,22 +200,24 @@ lpfc_dev_loss_tmo_handler(struct lpfc_nodelist *ndlp)
        struct fc_rport   *rport;
        struct lpfc_vport *vport;
        struct lpfc_hba   *phba;
+       struct Scsi_Host  *shost;
        uint8_t *name;
        int  put_node;
-       int  put_rport;
        int warn_on = 0;
        int fcf_inuse = 0;
 
        rport = ndlp->rport;
+       vport = ndlp->vport;
+       shost = lpfc_shost_from_vport(vport);
+
+       spin_lock_irq(shost->host_lock);
+       ndlp->nlp_flag &= ~NLP_IN_DEV_LOSS;
+       spin_unlock_irq(shost->host_lock);
 
-       if (!rport) {
-               ndlp->nlp_add_flag &= ~NLP_IN_DEV_LOSS;
+       if (!rport)
                return fcf_inuse;
-       }
 
-       rdata = rport->dd_data;
        name = (uint8_t *) &ndlp->nlp_portname;
-       vport = ndlp->vport;
        phba  = vport->phba;
 
        if (phba->sli_rev == LPFC_SLI_REV4)
@@ -244,6 +231,13 @@ lpfc_dev_loss_tmo_handler(struct lpfc_nodelist *ndlp)
                         "3182 dev_loss_tmo_handler x%06x, rport %p flg x%x\n",
                         ndlp->nlp_DID, ndlp->rport, ndlp->nlp_flag);
 
+       /*
+        * lpfc_nlp_remove if reached with dangling rport drops the
+        * reference. To make sure that does not happen clear rport
+        * pointer in ndlp before lpfc_nlp_put.
+        */
+       rdata = rport->dd_data;
+
        /* Don't defer this if we are in the process of deleting the vport
         * or unloading the driver. The unload will cleanup the node
         * appropriately we just need to cleanup the ndlp rport info here.
@@ -256,14 +250,12 @@ lpfc_dev_loss_tmo_handler(struct lpfc_nodelist *ndlp)
                                        ndlp->nlp_sid, 0, LPFC_CTX_TGT);
                }
                put_node = rdata->pnode != NULL;
-               put_rport = ndlp->rport != NULL;
                rdata->pnode = NULL;
                ndlp->rport = NULL;
-               ndlp->nlp_add_flag &= ~NLP_IN_DEV_LOSS;
                if (put_node)
                        lpfc_nlp_put(ndlp);
-               if (put_rport)
-                       put_device(&rport->dev);
+               put_device(&rport->dev);
+
                return fcf_inuse;
        }
 
@@ -275,28 +267,21 @@ lpfc_dev_loss_tmo_handler(struct lpfc_nodelist *ndlp)
                                 *name, *(name+1), *(name+2), *(name+3),
                                 *(name+4), *(name+5), *(name+6), *(name+7),
                                 ndlp->nlp_DID);
-               ndlp->nlp_add_flag &= ~NLP_IN_DEV_LOSS;
                return fcf_inuse;
        }
 
-       if (ndlp->nlp_type & NLP_FABRIC) {
-               /* We will clean up these Nodes in linkup */
-               put_node = rdata->pnode != NULL;
-               put_rport = ndlp->rport != NULL;
-               rdata->pnode = NULL;
-               ndlp->rport = NULL;
-               ndlp->nlp_add_flag &= ~NLP_IN_DEV_LOSS;
-               if (put_node)
-                       lpfc_nlp_put(ndlp);
-               if (put_rport)
-                       put_device(&rport->dev);
+       put_node = rdata->pnode != NULL;
+       rdata->pnode = NULL;
+       ndlp->rport = NULL;
+       if (put_node)
+               lpfc_nlp_put(ndlp);
+       put_device(&rport->dev);
+
+       if (ndlp->nlp_type & NLP_FABRIC)
                return fcf_inuse;
-       }
 
        if (ndlp->nlp_sid != NLP_NO_SID) {
                warn_on = 1;
-               /* flush the target */
-               ndlp->nlp_add_flag &= ~NLP_IN_DEV_LOSS;
                lpfc_sli_abort_iocb(vport, &phba->sli.ring[phba->sli.fcp_ring],
                                    ndlp->nlp_sid, 0, LPFC_CTX_TGT);
        }
@@ -321,16 +306,6 @@ lpfc_dev_loss_tmo_handler(struct lpfc_nodelist *ndlp)
                                 ndlp->nlp_state, ndlp->nlp_rpi);
        }
 
-       put_node = rdata->pnode != NULL;
-       put_rport = ndlp->rport != NULL;
-       rdata->pnode = NULL;
-       ndlp->rport = NULL;
-       ndlp->nlp_add_flag &= ~NLP_IN_DEV_LOSS;
-       if (put_node)
-               lpfc_nlp_put(ndlp);
-       if (put_rport)
-               put_device(&rport->dev);
-
        if (!(vport->load_flag & FC_UNLOADING) &&
            !(ndlp->nlp_flag & NLP_DELAY_TMO) &&
            !(ndlp->nlp_flag & NLP_NPR_2B_DISC) &&
@@ -1802,7 +1777,7 @@ lpfc_sli4_fcf_rec_mbox_parse(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq,
        dma_addr_t phys_addr;
        struct lpfc_mbx_sge sge;
        struct lpfc_mbx_read_fcf_tbl *read_fcf;
-       uint32_t shdr_status, shdr_add_status;
+       uint32_t shdr_status, shdr_add_status, if_type;
        union lpfc_sli4_cfg_shdr *shdr;
        struct fcf_record *new_fcf_record;
 
@@ -1823,9 +1798,11 @@ lpfc_sli4_fcf_rec_mbox_parse(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq,
        lpfc_sli_pcimem_bcopy(shdr, shdr,
                              sizeof(union lpfc_sli4_cfg_shdr));
        shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response);
+       if_type = bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf);
        shdr_add_status = bf_get(lpfc_mbox_hdr_add_status, &shdr->response);
        if (shdr_status || shdr_add_status) {
-               if (shdr_status == STATUS_FCF_TABLE_EMPTY)
+               if (shdr_status == STATUS_FCF_TABLE_EMPTY ||
+                                       if_type == LPFC_SLI_INTF_IF_TYPE_2)
                        lpfc_printf_log(phba, KERN_ERR, LOG_FIP,
                                        "2726 READ_FCF_RECORD Indicates empty "
                                        "FCF table.\n");
@@ -3868,11 +3845,11 @@ out:
 
        if (vport->port_state < LPFC_VPORT_READY) {
                /* Link up discovery requires Fabric registration. */
-               lpfc_ns_cmd(vport, SLI_CTNS_RFF_ID, 0, 0); /* Do this first! */
                lpfc_ns_cmd(vport, SLI_CTNS_RNN_ID, 0, 0);
                lpfc_ns_cmd(vport, SLI_CTNS_RSNN_NN, 0, 0);
                lpfc_ns_cmd(vport, SLI_CTNS_RSPN_ID, 0, 0);
                lpfc_ns_cmd(vport, SLI_CTNS_RFT_ID, 0, 0);
+               lpfc_ns_cmd(vport, SLI_CTNS_RFF_ID, 0, 0);
 
                /* Issue SCR just before NameServer GID_FT Query */
                lpfc_issue_els_scr(vport, SCR_DID, 0);
@@ -3918,9 +3895,17 @@ lpfc_register_remote_port(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
         * registered port, drop the reference that we took the last time we
         * registered the port.
         */
-       if (ndlp->rport && ndlp->rport->dd_data &&
-           ((struct lpfc_rport_data *) ndlp->rport->dd_data)->pnode == ndlp)
-               lpfc_nlp_put(ndlp);
+       rport = ndlp->rport;
+       if (rport) {
+               rdata = rport->dd_data;
+               /* break the link before dropping the ref */
+               ndlp->rport = NULL;
+               if (rdata && rdata->pnode == ndlp)
+                       lpfc_nlp_put(ndlp);
+               rdata->pnode = NULL;
+               /* drop reference for earlier registeration */
+               put_device(&rport->dev);
+       }
 
        lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_RPORT,
                "rport add:       did:x%x flg:x%x type x%x",
@@ -4296,9 +4281,9 @@ lpfc_drop_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
        if (vport->phba->sli_rev == LPFC_SLI_REV4) {
                lpfc_cleanup_vports_rrqs(vport, ndlp);
                lpfc_unreg_rpi(vport, ndlp);
-       } else {
-               lpfc_nlp_put(ndlp);
        }
+
+       lpfc_nlp_put(ndlp);
        return;
 }
 
@@ -4510,7 +4495,7 @@ lpfc_unreg_rpi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
 {
        struct lpfc_hba *phba = vport->phba;
        LPFC_MBOXQ_t    *mbox;
-       int rc;
+       int rc, acc_plogi = 1;
        uint16_t rpi;
 
        if (ndlp->nlp_flag & NLP_RPI_REGISTERED ||
@@ -4543,14 +4528,20 @@ lpfc_unreg_rpi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
                                        mbox->context1 = lpfc_nlp_get(ndlp);
                                        mbox->mbox_cmpl =
                                                lpfc_sli4_unreg_rpi_cmpl_clr;
+                                       /*
+                                        * accept PLOGIs after unreg_rpi_cmpl
+                                        */
+                                       acc_plogi = 0;
                                } else
                                        mbox->mbox_cmpl =
                                                lpfc_sli_def_mbox_cmpl;
                        }
 
                        rc = lpfc_sli_issue_mbox(phba, mbox, MBX_NOWAIT);
-                       if (rc == MBX_NOT_FINISHED)
+                       if (rc == MBX_NOT_FINISHED) {
                                mempool_free(mbox, phba->mbox_mem_pool);
+                               acc_plogi = 1;
+                       }
                }
                lpfc_no_rpi(phba, ndlp);
 
@@ -4558,8 +4549,11 @@ lpfc_unreg_rpi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
                        ndlp->nlp_rpi = 0;
                ndlp->nlp_flag &= ~NLP_RPI_REGISTERED;
                ndlp->nlp_flag &= ~NLP_NPR_ADISC;
+               if (acc_plogi)
+                       ndlp->nlp_flag &= ~NLP_LOGO_ACC;
                return 1;
        }
+       ndlp->nlp_flag &= ~NLP_LOGO_ACC;
        return 0;
 }
 
@@ -4761,6 +4755,7 @@ lpfc_nlp_remove(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
 {
        struct lpfc_hba  *phba = vport->phba;
        struct lpfc_rport_data *rdata;
+       struct fc_rport *rport;
        LPFC_MBOXQ_t *mbox;
        int rc;
 
@@ -4798,14 +4793,24 @@ lpfc_nlp_remove(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
        lpfc_cleanup_node(vport, ndlp);
 
        /*
-        * We can get here with a non-NULL ndlp->rport because when we
-        * unregister a rport we don't break the rport/node linkage.  So if we
-        * do, make sure we don't leaving any dangling pointers behind.
+        * ndlp->rport must be set to NULL before it reaches here
+        * i.e. break rport/node link before doing lpfc_nlp_put for
+        * registered rport and then drop the reference of rport.
         */
        if (ndlp->rport) {
-               rdata = ndlp->rport->dd_data;
+               /*
+                * extra lpfc_nlp_put dropped the reference of ndlp
+                * for registered rport so need to cleanup rport
+                */
+               lpfc_printf_vlog(vport, KERN_WARNING, LOG_NODE,
+                               "0940 removed node x%p DID x%x "
+                               " rport not null %p\n",
+                               ndlp, ndlp->nlp_DID, ndlp->rport);
+               rport = ndlp->rport;
+               rdata = rport->dd_data;
                rdata->pnode = NULL;
                ndlp->rport = NULL;
+               put_device(&rport->dev);
        }
 }
 
@@ -4833,9 +4838,19 @@ lpfc_matchdid(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
        if (matchdid.un.b.id == ndlpdid.un.b.id) {
                if ((mydid.un.b.domain == matchdid.un.b.domain) &&
                    (mydid.un.b.area == matchdid.un.b.area)) {
+                       /* This code is supposed to match the ID
+                        * for a private loop device that is
+                        * connect to fl_port. But we need to
+                        * check that the port did not just go
+                        * from pt2pt to fabric or we could end
+                        * up matching ndlp->nlp_DID 000001 to
+                        * fabric DID 0x20101
+                        */
                        if ((ndlpdid.un.b.domain == 0) &&
                            (ndlpdid.un.b.area == 0)) {
-                               if (ndlpdid.un.b.id)
+                               if (ndlpdid.un.b.id &&
+                                   vport->phba->fc_topology ==
+                                   LPFC_TOPOLOGY_LOOP)
                                        return 1;
                        }
                        return 0;
index 37beb9dc1311d4c5ff2a5f58b1687056d5fd3b84..892c5257d87cdca4f843de8d87260ed786672185 100644 (file)
@@ -543,6 +543,7 @@ struct fc_vft_header {
 #define ELS_CMD_TEST      0x11000000
 #define ELS_CMD_RRQ       0x12000000
 #define ELS_CMD_REC       0x13000000
+#define ELS_CMD_RDP       0x18000000
 #define ELS_CMD_PRLI      0x20100014
 #define ELS_CMD_PRLO      0x21100014
 #define ELS_CMD_PRLO_ACC  0x02100014
@@ -558,6 +559,7 @@ struct fc_vft_header {
 #define ELS_CMD_SCR       0x62000000
 #define ELS_CMD_RNID      0x78000000
 #define ELS_CMD_LIRR      0x7A000000
+#define ELS_CMD_LCB      0x81000000
 #else  /*  __LITTLE_ENDIAN_BITFIELD */
 #define ELS_CMD_MASK      0xffff
 #define ELS_RSP_MASK      0xff
@@ -580,6 +582,7 @@ struct fc_vft_header {
 #define ELS_CMD_TEST      0x11
 #define ELS_CMD_RRQ       0x12
 #define ELS_CMD_REC       0x13
+#define ELS_CMD_RDP      0x18
 #define ELS_CMD_PRLI      0x14001020
 #define ELS_CMD_PRLO      0x14001021
 #define ELS_CMD_PRLO_ACC  0x14001002
@@ -595,6 +598,7 @@ struct fc_vft_header {
 #define ELS_CMD_SCR       0x62
 #define ELS_CMD_RNID      0x78
 #define ELS_CMD_LIRR      0x7A
+#define ELS_CMD_LCB      0x81
 #endif
 
 /*
@@ -1010,6 +1014,198 @@ typedef struct _ELS_PKT {       /* Structure is in Big Endian format */
        } un;
 } ELS_PKT;
 
+/*
+ * Link Cable Beacon (LCB) ELS Frame
+ */
+
+struct fc_lcb_request_frame {
+       uint32_t      lcb_command;      /* ELS command opcode (0x81)     */
+       uint8_t       lcb_sub_command;/* LCB Payload Word 1, bit 24:31 */
+#define LPFC_LCB_ON    0x1
+#define LPFC_LCB_OFF   0x2
+       uint8_t       reserved[3];
+
+       uint8_t       lcb_type; /* LCB Payload Word 2, bit 24:31 */
+#define LPFC_LCB_GREEN 0x1
+#define LPFC_LCB_AMBER 0x2
+       uint8_t       lcb_frequency;    /* LCB Payload Word 2, bit 16:23 */
+       uint16_t      lcb_duration;     /* LCB Payload Word 2, bit 15:0  */
+};
+
+/*
+ * Link Cable Beacon (LCB) ELS Response Frame
+ */
+struct fc_lcb_res_frame {
+       uint32_t      lcb_ls_acc;       /* Acceptance of LCB request (0x02) */
+       uint8_t       lcb_sub_command;/* LCB Payload Word 1, bit 24:31 */
+       uint8_t       reserved[3];
+       uint8_t       lcb_type; /* LCB Payload Word 2, bit 24:31 */
+       uint8_t       lcb_frequency;    /* LCB Payload Word 2, bit 16:23 */
+       uint16_t      lcb_duration;     /* LCB Payload Word 2, bit 15:0  */
+};
+
+/*
+ * Read Diagnostic Parameters (RDP) ELS frame.
+ */
+#define SFF_PG0_IDENT_SFP              0x3
+
+#define SFP_FLAG_PT_OPTICAL            0x0
+#define SFP_FLAG_PT_SWLASER            0x01
+#define SFP_FLAG_PT_LWLASER_LC1310     0x02
+#define SFP_FLAG_PT_LWLASER_LL1550     0x03
+#define SFP_FLAG_PT_MASK               0x0F
+#define SFP_FLAG_PT_SHIFT              0
+
+#define SFP_FLAG_IS_OPTICAL_PORT       0x01
+#define SFP_FLAG_IS_OPTICAL_MASK       0x010
+#define SFP_FLAG_IS_OPTICAL_SHIFT      4
+
+#define SFP_FLAG_IS_DESC_VALID         0x01
+#define SFP_FLAG_IS_DESC_VALID_MASK    0x020
+#define SFP_FLAG_IS_DESC_VALID_SHIFT   5
+
+#define SFP_FLAG_CT_UNKNOWN            0x0
+#define SFP_FLAG_CT_SFP_PLUS           0x01
+#define SFP_FLAG_CT_MASK               0x3C
+#define SFP_FLAG_CT_SHIFT              6
+
+struct fc_rdp_port_name_info {
+       uint8_t wwnn[8];
+       uint8_t wwpn[8];
+};
+
+
+/*
+ * Link Error Status Block Structure (FC-FS-3) for RDP
+ * This similar to RPS ELS
+ */
+struct fc_link_status {
+       uint32_t      link_failure_cnt;
+       uint32_t      loss_of_synch_cnt;
+       uint32_t      loss_of_signal_cnt;
+       uint32_t      primitive_seq_proto_err;
+       uint32_t      invalid_trans_word;
+       uint32_t      invalid_crc_cnt;
+
+};
+
+#define RDP_PORT_NAMES_DESC_TAG  0x00010003
+struct fc_rdp_port_name_desc {
+       uint32_t        tag;     /* 0001 0003h */
+       uint32_t        length;  /* set to size of payload struct */
+       struct fc_rdp_port_name_info  port_names;
+};
+
+
+struct fc_rdp_link_error_status_payload_info {
+       struct fc_link_status link_status; /* 24 bytes */
+       uint32_t  port_type;             /* bits 31-30 only */
+};
+
+#define RDP_LINK_ERROR_STATUS_DESC_TAG  0x00010002
+struct fc_rdp_link_error_status_desc {
+       uint32_t         tag;     /* 0001 0002h */
+       uint32_t         length;  /* set to size of payload struct */
+       struct fc_rdp_link_error_status_payload_info info;
+};
+
+#define VN_PT_PHY_UNKNOWN      0x00
+#define VN_PT_PHY_PF_PORT      0x01
+#define VN_PT_PHY_ETH_MAC      0x10
+#define VN_PT_PHY_SHIFT                30
+
+#define RDP_PS_1GB             0x8000
+#define RDP_PS_2GB             0x4000
+#define RDP_PS_4GB             0x2000
+#define RDP_PS_10GB            0x1000
+#define RDP_PS_8GB             0x0800
+#define RDP_PS_16GB            0x0400
+#define RDP_PS_32GB            0x0200
+
+#define RDP_CAP_UNKNOWN        0x0001
+#define RDP_PS_UNKNOWN         0x0002
+#define RDP_PS_NOT_ESTABLISHED 0x0001
+
+struct fc_rdp_port_speed {
+       uint16_t   capabilities;
+       uint16_t   speed;
+};
+
+struct fc_rdp_port_speed_info {
+       struct fc_rdp_port_speed   port_speed;
+};
+
+#define RDP_PORT_SPEED_DESC_TAG  0x00010001
+struct fc_rdp_port_speed_desc {
+       uint32_t         tag;            /* 00010001h */
+       uint32_t         length;         /* set to size of payload struct */
+       struct fc_rdp_port_speed_info info;
+};
+
+#define RDP_NPORT_ID_SIZE      4
+#define RDP_N_PORT_DESC_TAG    0x00000003
+struct fc_rdp_nport_desc {
+       uint32_t         tag;          /* 0000 0003h, big endian */
+       uint32_t         length;       /* size of RDP_N_PORT_ID struct */
+       uint32_t         nport_id : 12;
+       uint32_t         reserved : 8;
+};
+
+
+struct fc_rdp_link_service_info {
+       uint32_t         els_req;    /* Request payload word 0 value.*/
+};
+
+#define RDP_LINK_SERVICE_DESC_TAG  0x00000001
+struct fc_rdp_link_service_desc {
+       uint32_t         tag;     /* Descriptor tag  1 */
+       uint32_t         length;  /* set to size of payload struct. */
+       struct fc_rdp_link_service_info  payload;
+                                 /* must be ELS req Word 0(0x18) */
+};
+
+struct fc_rdp_sfp_info {
+       uint16_t        temperature;
+       uint16_t        vcc;
+       uint16_t        tx_bias;
+       uint16_t        tx_power;
+       uint16_t        rx_power;
+       uint16_t        flags;
+};
+
+#define RDP_SFP_DESC_TAG  0x00010000
+struct fc_rdp_sfp_desc {
+       uint32_t         tag;
+       uint32_t         length;  /* set to size of sfp_info struct */
+       struct fc_rdp_sfp_info sfp_info;
+};
+
+struct fc_rdp_req_frame {
+       uint32_t         rdp_command;           /* ELS command opcode (0x18)*/
+       uint32_t         rdp_des_length;        /* RDP Payload Word 1 */
+       struct fc_rdp_nport_desc nport_id_desc; /* RDP Payload Word 2 - 4 */
+};
+
+
+struct fc_rdp_res_frame {
+       uint32_t        reply_sequence;         /* FC word0 LS_ACC or LS_RJT */
+       uint32_t        length;                 /* FC Word 1      */
+       struct fc_rdp_link_service_desc link_service_desc;    /* Word 2 -4  */
+       struct fc_rdp_sfp_desc sfp_desc;                      /* Word 5 -9  */
+       struct fc_rdp_port_speed_desc portspeed_desc;         /* Word 10-12 */
+       struct fc_rdp_link_error_status_desc link_error_desc; /* Word 13-21 */
+       struct fc_rdp_port_name_desc diag_port_names_desc;    /* Word 22-27 */
+       struct fc_rdp_port_name_desc attached_port_names_desc;/* Word 28-33 */
+};
+
+
+#define RDP_DESC_PAYLOAD_SIZE (sizeof(struct fc_rdp_link_service_desc) \
+                       + sizeof(struct fc_rdp_sfp_desc) \
+                       + sizeof(struct fc_rdp_port_speed_desc) \
+                       + sizeof(struct fc_rdp_link_error_status_desc) \
+                       + (sizeof(struct fc_rdp_port_name_desc) * 2))
+
+
 /******** FDMI ********/
 
 /* lpfc_sli_ct_request defines the CT_IU preamble for FDMI commands */
@@ -1586,6 +1782,11 @@ typedef struct {         /* FireFly BIU registers */
 
 #define TEMPERATURE_OFFSET 0xB0        /* Slim offset for critical temperature event */
 
+/*
+ * return code Fail
+ */
+#define FAILURE 1
+
 /*
  *    Begin Structure Definitions for Mailbox Commands
  */
index 1813c45946f47a517dba98dedf8cfc5b7c7a351f..33ec4fa39ccb46e147802a75b080a91a5aa65ea2 100644 (file)
@@ -291,7 +291,7 @@ struct sli4_bls_rsp {
 struct lpfc_eqe {
        uint32_t word0;
 #define lpfc_eqe_resource_id_SHIFT     16
-#define lpfc_eqe_resource_id_MASK      0x000000FF
+#define lpfc_eqe_resource_id_MASK      0x0000FFFF
 #define lpfc_eqe_resource_id_WORD      word0
 #define lpfc_eqe_minor_code_SHIFT      4
 #define lpfc_eqe_minor_code_MASK       0x00000FFF
@@ -914,6 +914,8 @@ struct mbox_header {
 #define LPFC_MBOX_OPCODE_FUNCTION_RESET                        0x3D
 #define LPFC_MBOX_OPCODE_SET_PHYSICAL_LINK_CONFIG      0x3E
 #define LPFC_MBOX_OPCODE_SET_BOOT_CONFIG               0x43
+#define LPFC_MBOX_OPCODE_SET_BEACON_CONFIG              0x45
+#define LPFC_MBOX_OPCODE_GET_BEACON_CONFIG              0x46
 #define LPFC_MBOX_OPCODE_GET_PORT_NAME                 0x4D
 #define LPFC_MBOX_OPCODE_MQ_CREATE_EXT                 0x5A
 #define LPFC_MBOX_OPCODE_GET_VPD_DATA                  0x5B
@@ -1479,6 +1481,26 @@ struct lpfc_mbx_query_fw_config {
        } rsp;
 };
 
+struct lpfc_mbx_set_beacon_config {
+       struct mbox_header header;
+       uint32_t word4;
+#define lpfc_mbx_set_beacon_port_num_SHIFT             0
+#define lpfc_mbx_set_beacon_port_num_MASK              0x0000003F
+#define lpfc_mbx_set_beacon_port_num_WORD              word4
+#define lpfc_mbx_set_beacon_port_type_SHIFT            6
+#define lpfc_mbx_set_beacon_port_type_MASK             0x00000003
+#define lpfc_mbx_set_beacon_port_type_WORD             word4
+#define lpfc_mbx_set_beacon_state_SHIFT                        8
+#define lpfc_mbx_set_beacon_state_MASK                 0x000000FF
+#define lpfc_mbx_set_beacon_state_WORD                 word4
+#define lpfc_mbx_set_beacon_duration_SHIFT             16
+#define lpfc_mbx_set_beacon_duration_MASK              0x000000FF
+#define lpfc_mbx_set_beacon_duration_WORD              word4
+#define lpfc_mbx_set_beacon_status_duration_SHIFT      24
+#define lpfc_mbx_set_beacon_status_duration_MASK       0x000000FF
+#define lpfc_mbx_set_beacon_status_duration_WORD       word4
+};
+
 struct lpfc_id_range {
        uint32_t word5;
 #define lpfc_mbx_rsrc_id_word4_0_SHIFT 0
@@ -1921,6 +1943,12 @@ struct lpfc_mbx_redisc_fcf_tbl {
 #define STATUS_FCF_IN_USE                              0x3a
 #define STATUS_FCF_TABLE_EMPTY                         0x43
 
+/*
+ * Additional status field for embedded SLI_CONFIG mailbox
+ * command.
+ */
+#define ADD_STATUS_OPERATION_ALREADY_ACTIVE            0x67
+
 struct lpfc_mbx_sli4_config {
        struct mbox_header header;
 };
@@ -2433,6 +2461,205 @@ struct lpfc_mbx_supp_pages {
 #define LPFC_SLI4_PARAMETERS           2
 };
 
+struct lpfc_mbx_memory_dump_type3 {
+       uint32_t word1;
+#define lpfc_mbx_memory_dump_type3_type_SHIFT    0
+#define lpfc_mbx_memory_dump_type3_type_MASK     0x0000000f
+#define lpfc_mbx_memory_dump_type3_type_WORD     word1
+#define lpfc_mbx_memory_dump_type3_link_SHIFT    24
+#define lpfc_mbx_memory_dump_type3_link_MASK     0x000000ff
+#define lpfc_mbx_memory_dump_type3_link_WORD     word1
+       uint32_t word2;
+#define lpfc_mbx_memory_dump_type3_page_no_SHIFT  0
+#define lpfc_mbx_memory_dump_type3_page_no_MASK   0x0000ffff
+#define lpfc_mbx_memory_dump_type3_page_no_WORD   word2
+#define lpfc_mbx_memory_dump_type3_offset_SHIFT   16
+#define lpfc_mbx_memory_dump_type3_offset_MASK    0x0000ffff
+#define lpfc_mbx_memory_dump_type3_offset_WORD    word2
+       uint32_t word3;
+#define lpfc_mbx_memory_dump_type3_length_SHIFT  0
+#define lpfc_mbx_memory_dump_type3_length_MASK   0x00ffffff
+#define lpfc_mbx_memory_dump_type3_length_WORD   word3
+       uint32_t addr_lo;
+       uint32_t addr_hi;
+       uint32_t return_len;
+};
+
+#define DMP_PAGE_A0             0xa0
+#define DMP_PAGE_A2             0xa2
+#define DMP_SFF_PAGE_A0_SIZE   256
+#define DMP_SFF_PAGE_A2_SIZE   256
+
+#define SFP_WAVELENGTH_LC1310  1310
+#define SFP_WAVELENGTH_LL1550  1550
+
+
+/*
+ *  * SFF-8472 TABLE 3.4
+ *   */
+#define  SFF_PG0_CONNECTOR_UNKNOWN    0x00   /* Unknown  */
+#define  SFF_PG0_CONNECTOR_SC         0x01   /* SC       */
+#define  SFF_PG0_CONNECTOR_FC_COPPER1 0x02   /* FC style 1 copper connector */
+#define  SFF_PG0_CONNECTOR_FC_COPPER2 0x03   /* FC style 2 copper connector */
+#define  SFF_PG0_CONNECTOR_BNC        0x04   /* BNC / TNC */
+#define  SFF_PG0_CONNECTOR__FC_COAX   0x05   /* FC coaxial headers */
+#define  SFF_PG0_CONNECTOR_FIBERJACK  0x06   /* FiberJack */
+#define  SFF_PG0_CONNECTOR_LC         0x07   /* LC        */
+#define  SFF_PG0_CONNECTOR_MT         0x08   /* MT - RJ   */
+#define  SFF_PG0_CONNECTOR_MU         0x09   /* MU        */
+#define  SFF_PG0_CONNECTOR_SF         0x0A   /* SG        */
+#define  SFF_PG0_CONNECTOR_OPTICAL_PIGTAIL 0x0B /* Optical pigtail */
+#define  SFF_PG0_CONNECTOR_OPTICAL_PARALLEL 0x0C /* MPO Parallel Optic */
+#define  SFF_PG0_CONNECTOR_HSSDC_II   0x20   /* HSSDC II */
+#define  SFF_PG0_CONNECTOR_COPPER_PIGTAIL 0x21 /* Copper pigtail */
+#define  SFF_PG0_CONNECTOR_RJ45       0x22  /* RJ45 */
+
+/* SFF-8472 Table 3.1 Diagnostics: Data Fields Address/Page A0 */
+
+#define SSF_IDENTIFIER                 0
+#define SSF_EXT_IDENTIFIER             1
+#define SSF_CONNECTOR                  2
+#define SSF_TRANSCEIVER_CODE_B0                3
+#define SSF_TRANSCEIVER_CODE_B1                4
+#define SSF_TRANSCEIVER_CODE_B2                5
+#define SSF_TRANSCEIVER_CODE_B3                6
+#define SSF_TRANSCEIVER_CODE_B4                7
+#define SSF_TRANSCEIVER_CODE_B5                8
+#define SSF_TRANSCEIVER_CODE_B6                9
+#define SSF_TRANSCEIVER_CODE_B7                10
+#define SSF_ENCODING                   11
+#define SSF_BR_NOMINAL                 12
+#define SSF_RATE_IDENTIFIER            13
+#define SSF_LENGTH_9UM_KM              14
+#define SSF_LENGTH_9UM                 15
+#define SSF_LENGTH_50UM_OM2            16
+#define SSF_LENGTH_62UM_OM1            17
+#define SFF_LENGTH_COPPER              18
+#define SSF_LENGTH_50UM_OM3            19
+#define SSF_VENDOR_NAME                        20
+#define SSF_VENDOR_OUI                 36
+#define SSF_VENDOR_PN                  40
+#define SSF_VENDOR_REV                 56
+#define SSF_WAVELENGTH_B1              60
+#define SSF_WAVELENGTH_B0              61
+#define SSF_CC_BASE                    63
+#define SSF_OPTIONS_B1                 64
+#define SSF_OPTIONS_B0                 65
+#define SSF_BR_MAX                     66
+#define SSF_BR_MIN                     67
+#define SSF_VENDOR_SN                  68
+#define SSF_DATE_CODE                  84
+#define SSF_MONITORING_TYPEDIAGNOSTIC  92
+#define SSF_ENHANCED_OPTIONS           93
+#define SFF_8472_COMPLIANCE            94
+#define SSF_CC_EXT                     95
+#define SSF_A0_VENDOR_SPECIFIC         96
+
+/* SFF-8472 Table 3.1a Diagnostics: Data Fields Address/Page A2 */
+
+#define SSF_AW_THRESHOLDS              0
+#define SSF_EXT_CAL_CONSTANTS          56
+#define SSF_CC_DMI                     95
+#define SFF_TEMPERATURE_B1             96
+#define SFF_TEMPERATURE_B0             97
+#define SFF_VCC_B1                     98
+#define SFF_VCC_B0                     99
+#define SFF_TX_BIAS_CURRENT_B1         100
+#define SFF_TX_BIAS_CURRENT_B0         101
+#define SFF_TXPOWER_B1                 102
+#define SFF_TXPOWER_B0                 103
+#define SFF_RXPOWER_B1                 104
+#define SFF_RXPOWER_B0                 105
+#define SSF_STATUS_CONTROL             110
+#define SSF_ALARM_FLAGS_B1             112
+#define SSF_ALARM_FLAGS_B0             113
+#define SSF_WARNING_FLAGS_B1           116
+#define SSF_WARNING_FLAGS_B0           117
+#define SSF_EXT_TATUS_CONTROL_B1       118
+#define SSF_EXT_TATUS_CONTROL_B0       119
+#define SSF_A2_VENDOR_SPECIFIC         120
+#define SSF_USER_EEPROM                        128
+#define SSF_VENDOR_CONTROL             148
+
+
+/*
+ * Tranceiver codes Fibre Channel SFF-8472
+ * Table 3.5.
+ */
+
+struct sff_trasnceiver_codes_byte0 {
+       uint8_t inifiband:4;
+       uint8_t teng_ethernet:4;
+};
+
+struct sff_trasnceiver_codes_byte1 {
+       uint8_t  sonet:6;
+       uint8_t  escon:2;
+};
+
+struct sff_trasnceiver_codes_byte2 {
+       uint8_t  soNet:8;
+};
+
+struct sff_trasnceiver_codes_byte3 {
+       uint8_t ethernet:8;
+};
+
+struct sff_trasnceiver_codes_byte4 {
+       uint8_t fc_el_lo:1;
+       uint8_t fc_lw_laser:1;
+       uint8_t fc_sw_laser:1;
+       uint8_t fc_md_distance:1;
+       uint8_t fc_lg_distance:1;
+       uint8_t fc_int_distance:1;
+       uint8_t fc_short_distance:1;
+       uint8_t fc_vld_distance:1;
+};
+
+struct sff_trasnceiver_codes_byte5 {
+       uint8_t reserved1:1;
+       uint8_t reserved2:1;
+       uint8_t fc_sfp_active:1;  /* Active cable   */
+       uint8_t fc_sfp_passive:1; /* Passive cable  */
+       uint8_t fc_lw_laser:1;     /* Longwave laser */
+       uint8_t fc_sw_laser_sl:1;
+       uint8_t fc_sw_laser_sn:1;
+       uint8_t fc_el_hi:1;        /* Electrical enclosure high bit */
+};
+
+struct sff_trasnceiver_codes_byte6 {
+       uint8_t fc_tm_sm:1;      /* Single Mode */
+       uint8_t reserved:1;
+       uint8_t fc_tm_m6:1;       /* Multimode, 62.5um (M6) */
+       uint8_t fc_tm_tv:1;      /* Video Coax (TV) */
+       uint8_t fc_tm_mi:1;      /* Miniature Coax (MI) */
+       uint8_t fc_tm_tp:1;      /* Twisted Pair (TP) */
+       uint8_t fc_tm_tw:1;      /* Twin Axial Pair  */
+};
+
+struct sff_trasnceiver_codes_byte7 {
+       uint8_t fc_sp_100MB:1;   /*  100 MB/sec */
+       uint8_t reserve:1;
+       uint8_t fc_sp_200mb:1;   /*  200 MB/sec */
+       uint8_t fc_sp_3200MB:1;  /* 3200 MB/sec */
+       uint8_t fc_sp_400MB:1;   /*  400 MB/sec */
+       uint8_t fc_sp_1600MB:1;  /* 1600 MB/sec */
+       uint8_t fc_sp_800MB:1;   /*  800 MB/sec */
+       uint8_t fc_sp_1200MB:1;  /* 1200 MB/sec */
+};
+
+/* User writable non-volatile memory, SFF-8472 Table 3.20 */
+struct user_eeprom {
+       uint8_t vendor_name[16];
+       uint8_t vendor_oui[3];
+       uint8_t vendor_pn[816];
+       uint8_t vendor_rev[4];
+       uint8_t vendor_sn[16];
+       uint8_t datecode[6];
+       uint8_t lot_code[2];
+       uint8_t reserved191[57];
+};
+
 struct lpfc_mbx_pc_sli4_params {
        uint32_t word1;
 #define qs_SHIFT                               0
@@ -3021,6 +3248,7 @@ struct lpfc_mqe {
                struct lpfc_mbx_request_features req_ftrs;
                struct lpfc_mbx_post_hdr_tmpl hdr_tmpl;
                struct lpfc_mbx_query_fw_config query_fw_cfg;
+               struct lpfc_mbx_set_beacon_config beacon_config;
                struct lpfc_mbx_supp_pages supp_pages;
                struct lpfc_mbx_pc_sli4_params sli4_params;
                struct lpfc_mbx_get_sli4_parameters get_sli4_parameters;
@@ -3031,6 +3259,7 @@ struct lpfc_mqe {
                struct lpfc_mbx_get_prof_cfg get_prof_cfg;
                struct lpfc_mbx_wr_object wr_object;
                struct lpfc_mbx_get_port_name get_port_name;
+               struct lpfc_mbx_memory_dump_type3 mem_dump_type3;
                struct lpfc_mbx_nop nop;
        } un;
 };
@@ -3041,8 +3270,8 @@ struct lpfc_mcqe {
 #define lpfc_mcqe_status_MASK          0x0000FFFF
 #define lpfc_mcqe_status_WORD          word0
 #define lpfc_mcqe_ext_status_SHIFT     16
-#define lpfc_mcqe_ext_status_MASK      0x0000FFFF
-#define lpfc_mcqe_ext_status_WORD      word0
+#define lpfc_mcqe_ext_status_MASK      0x0000FFFF
+#define lpfc_mcqe_ext_status_WORD      word0
        uint32_t mcqe_tag0;
        uint32_t mcqe_tag1;
        uint32_t trailer;
@@ -3176,6 +3405,7 @@ struct lpfc_acqe_fc_la {
 #define LPFC_FC_LA_SPEED_8G            0x8
 #define LPFC_FC_LA_SPEED_10G           0xA
 #define LPFC_FC_LA_SPEED_16G           0x10
+#define LPFC_FC_LA_SPEED_32G            0x20
 #define lpfc_acqe_fc_la_topology_SHIFT         16
 #define lpfc_acqe_fc_la_topology_MASK          0x000000FF
 #define lpfc_acqe_fc_la_topology_WORD          word0
index e8c8c1ecc1f54dbf7f1003199397284600eefd46..f962118da8eda9b70d7771771ad8a6537dc41f0f 100644 (file)
@@ -3303,6 +3303,7 @@ lpfc_create_port(struct lpfc_hba *phba, int instance, struct device *dev)
        shost->max_lun = vport->cfg_max_luns;
        shost->this_id = -1;
        shost->max_cmd_len = 16;
+       shost->nr_hw_queues = phba->cfg_fcp_io_channel;
        if (phba->sli_rev == LPFC_SLI_REV4) {
                shost->dma_boundary =
                        phba->sli4_hba.pc_sli4_params.sge_supp_len-1;
@@ -4483,7 +4484,13 @@ lpfc_sli4_async_fip_evt(struct lpfc_hba *phba,
                        lpfc_destroy_vport_work_array(phba, vports);
                }
 
-               if (active_vlink_present) {
+               /*
+                * Don't re-instantiate if vport is marked for deletion.
+                * If we are here first then vport_delete is going to wait
+                * for discovery to complete.
+                */
+               if (!(vport->load_flag & FC_UNLOADING) &&
+                                       active_vlink_present) {
                        /*
                         * If there are other active VLinks present,
                         * re-instantiate the Vlink using FDISC.
@@ -7500,6 +7507,8 @@ lpfc_sli4_queue_setup(struct lpfc_hba *phba)
                        mboxq->u.mqe.un.query_fw_cfg.rsp.function_mode;
        phba->sli4_hba.ulp0_mode = mboxq->u.mqe.un.query_fw_cfg.rsp.ulp0_mode;
        phba->sli4_hba.ulp1_mode = mboxq->u.mqe.un.query_fw_cfg.rsp.ulp1_mode;
+       phba->sli4_hba.physical_port =
+                       mboxq->u.mqe.un.query_fw_cfg.rsp.physical_port;
        lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
                        "3251 QUERY_FW_CFG: func_mode:x%x, ulp0_mode:x%x, "
                        "ulp1_mode:x%x\n", phba->sli4_hba.fw_func_mode,
@@ -8367,7 +8376,7 @@ lpfc_sli_enable_msix(struct lpfc_hba *phba)
 
        /* vector-0 is associated to slow-path handler */
        rc = request_irq(phba->msix_entries[0].vector,
-                        &lpfc_sli_sp_intr_handler, IRQF_SHARED,
+                        &lpfc_sli_sp_intr_handler, 0,
                         LPFC_SP_DRIVER_HANDLER_NAME, phba);
        if (rc) {
                lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
@@ -8378,7 +8387,7 @@ lpfc_sli_enable_msix(struct lpfc_hba *phba)
 
        /* vector-1 is associated to fast-path handler */
        rc = request_irq(phba->msix_entries[1].vector,
-                        &lpfc_sli_fp_intr_handler, IRQF_SHARED,
+                        &lpfc_sli_fp_intr_handler, 0,
                         LPFC_FP_DRIVER_HANDLER_NAME, phba);
 
        if (rc) {
@@ -8487,7 +8496,7 @@ lpfc_sli_enable_msi(struct lpfc_hba *phba)
        }
 
        rc = request_irq(phba->pcidev->irq, lpfc_sli_intr_handler,
-                        IRQF_SHARED, LPFC_DRIVER_NAME, phba);
+                        0, LPFC_DRIVER_NAME, phba);
        if (rc) {
                pci_disable_msi(phba->pcidev);
                lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
@@ -8944,13 +8953,13 @@ lpfc_sli4_enable_msix(struct lpfc_hba *phba)
                if (phba->cfg_fof && (index == (vectors - 1)))
                        rc = request_irq(
                                phba->sli4_hba.msix_entries[index].vector,
-                                &lpfc_sli4_fof_intr_handler, IRQF_SHARED,
+                                &lpfc_sli4_fof_intr_handler, 0,
                                 (char *)&phba->sli4_hba.handler_name[index],
                                 &phba->sli4_hba.fcp_eq_hdl[index]);
                else
                        rc = request_irq(
                                phba->sli4_hba.msix_entries[index].vector,
-                                &lpfc_sli4_hba_intr_handler, IRQF_SHARED,
+                                &lpfc_sli4_hba_intr_handler, 0,
                                 (char *)&phba->sli4_hba.handler_name[index],
                                 &phba->sli4_hba.fcp_eq_hdl[index]);
                if (rc) {
@@ -8972,7 +8981,8 @@ lpfc_sli4_enable_msix(struct lpfc_hba *phba)
                phba->cfg_fcp_io_channel = vectors;
        }
 
-       lpfc_sli4_set_affinity(phba, vectors);
+       if (!shost_use_blk_mq(lpfc_shost_from_vport(phba->pport)))
+               lpfc_sli4_set_affinity(phba, vectors);
        return rc;
 
 cfg_fail_out:
@@ -9050,7 +9060,7 @@ lpfc_sli4_enable_msi(struct lpfc_hba *phba)
        }
 
        rc = request_irq(phba->pcidev->irq, lpfc_sli4_intr_handler,
-                        IRQF_SHARED, LPFC_DRIVER_NAME, phba);
+                        0, LPFC_DRIVER_NAME, phba);
        if (rc) {
                pci_disable_msi(phba->pcidev);
                lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
index 816f596cda605ce87b1bbd0e770a76ad7829482e..eb627724417e14432ca122d2e29feed1014ce04f 100644 (file)
@@ -2255,6 +2255,158 @@ lpfc_sli4_dump_cfg_rg23(struct lpfc_hba *phba, struct lpfcMboxq *mbox)
        return 0;
 }
 
+void
+lpfc_mbx_cmpl_rdp_link_stat(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
+{
+       MAILBOX_t *mb;
+       int rc = FAILURE;
+       struct lpfc_rdp_context *rdp_context =
+                       (struct lpfc_rdp_context *)(mboxq->context2);
+
+       mb = &mboxq->u.mb;
+       if (mb->mbxStatus)
+               goto mbx_failed;
+
+       memcpy(&rdp_context->link_stat, &mb->un.varRdLnk, sizeof(READ_LNK_VAR));
+
+       rc = SUCCESS;
+
+mbx_failed:
+       lpfc_sli4_mbox_cmd_free(phba, mboxq);
+       rdp_context->cmpl(phba, rdp_context, rc);
+}
+
+void
+lpfc_mbx_cmpl_rdp_page_a2(struct lpfc_hba *phba, LPFC_MBOXQ_t *mbox)
+{
+       struct lpfc_dmabuf *mp = (struct lpfc_dmabuf *) mbox->context1;
+       struct lpfc_rdp_context *rdp_context =
+                       (struct lpfc_rdp_context *)(mbox->context2);
+
+       if (bf_get(lpfc_mqe_status, &mbox->u.mqe))
+               goto error;
+
+       lpfc_sli_bemem_bcopy(mp->virt, &rdp_context->page_a2,
+                               DMP_SFF_PAGE_A2_SIZE);
+
+       /* We don't need dma buffer for link stat. */
+       lpfc_mbuf_free(phba, mp->virt, mp->phys);
+       kfree(mp);
+
+       memset(mbox, 0, sizeof(*mbox));
+       lpfc_read_lnk_stat(phba, mbox);
+       mbox->vport = rdp_context->ndlp->vport;
+       mbox->mbox_cmpl = lpfc_mbx_cmpl_rdp_link_stat;
+       mbox->context2 = (struct lpfc_rdp_context *) rdp_context;
+       if (lpfc_sli_issue_mbox(phba, mbox, MBX_NOWAIT) == MBX_NOT_FINISHED)
+               goto error;
+
+       return;
+
+error:
+       lpfc_mbuf_free(phba, mp->virt, mp->phys);
+       kfree(mp);
+       lpfc_sli4_mbox_cmd_free(phba, mbox);
+       rdp_context->cmpl(phba, rdp_context, FAILURE);
+}
+
+void
+lpfc_mbx_cmpl_rdp_page_a0(struct lpfc_hba *phba, LPFC_MBOXQ_t *mbox)
+{
+       int rc;
+       struct lpfc_dmabuf *mp = (struct lpfc_dmabuf *) (mbox->context1);
+       struct lpfc_rdp_context *rdp_context =
+                       (struct lpfc_rdp_context *)(mbox->context2);
+
+       if (bf_get(lpfc_mqe_status, &mbox->u.mqe))
+               goto error;
+
+       lpfc_sli_bemem_bcopy(mp->virt, &rdp_context->page_a0,
+                               DMP_SFF_PAGE_A0_SIZE);
+
+       memset(mbox, 0, sizeof(*mbox));
+
+       memset(mp->virt, 0, DMP_SFF_PAGE_A2_SIZE);
+       INIT_LIST_HEAD(&mp->list);
+
+       /* save address for completion */
+       mbox->context1 = mp;
+       mbox->vport = rdp_context->ndlp->vport;
+
+       bf_set(lpfc_mqe_command, &mbox->u.mqe, MBX_DUMP_MEMORY);
+       bf_set(lpfc_mbx_memory_dump_type3_type,
+               &mbox->u.mqe.un.mem_dump_type3, DMP_LMSD);
+       bf_set(lpfc_mbx_memory_dump_type3_link,
+               &mbox->u.mqe.un.mem_dump_type3, phba->sli4_hba.physical_port);
+       bf_set(lpfc_mbx_memory_dump_type3_page_no,
+               &mbox->u.mqe.un.mem_dump_type3, DMP_PAGE_A2);
+       bf_set(lpfc_mbx_memory_dump_type3_length,
+               &mbox->u.mqe.un.mem_dump_type3, DMP_SFF_PAGE_A2_SIZE);
+       mbox->u.mqe.un.mem_dump_type3.addr_lo = putPaddrLow(mp->phys);
+       mbox->u.mqe.un.mem_dump_type3.addr_hi = putPaddrHigh(mp->phys);
+
+       mbox->mbox_cmpl = lpfc_mbx_cmpl_rdp_page_a2;
+       mbox->context2 = (struct lpfc_rdp_context *) rdp_context;
+       rc = lpfc_sli_issue_mbox(phba, mbox, MBX_NOWAIT);
+       if (rc == MBX_NOT_FINISHED)
+               goto error;
+
+       return;
+
+error:
+       lpfc_mbuf_free(phba, mp->virt, mp->phys);
+       kfree(mp);
+       lpfc_sli4_mbox_cmd_free(phba, mbox);
+       rdp_context->cmpl(phba, rdp_context, FAILURE);
+}
+
+
+/*
+ * lpfc_sli4_dump_sfp_pagea0 - Dump sli4 read SFP Diagnostic.
+ * @phba: pointer to the hba structure containing.
+ * @mbox: pointer to lpfc mbox command to initialize.
+ *
+ * This function create a SLI4 dump mailbox command to dump configure
+ * type 3 page 0xA0.
+ */
+int
+lpfc_sli4_dump_page_a0(struct lpfc_hba *phba, struct lpfcMboxq *mbox)
+{
+       struct lpfc_dmabuf *mp = NULL;
+
+       memset(mbox, 0, sizeof(*mbox));
+
+       mp = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL);
+       if (mp)
+               mp->virt = lpfc_mbuf_alloc(phba, 0, &mp->phys);
+       if (!mp || !mp->virt) {
+               kfree(mp);
+               lpfc_printf_log(phba, KERN_WARNING, LOG_MBOX,
+                       "3569 dump type 3 page 0xA0 allocation failed\n");
+               return 1;
+       }
+
+       memset(mp->virt, 0, LPFC_BPL_SIZE);
+       INIT_LIST_HEAD(&mp->list);
+
+       bf_set(lpfc_mqe_command, &mbox->u.mqe, MBX_DUMP_MEMORY);
+       /* save address for completion */
+       mbox->context1 = mp;
+
+       bf_set(lpfc_mbx_memory_dump_type3_type,
+               &mbox->u.mqe.un.mem_dump_type3, DMP_LMSD);
+       bf_set(lpfc_mbx_memory_dump_type3_link,
+               &mbox->u.mqe.un.mem_dump_type3, phba->sli4_hba.physical_port);
+       bf_set(lpfc_mbx_memory_dump_type3_page_no,
+               &mbox->u.mqe.un.mem_dump_type3, DMP_PAGE_A0);
+       bf_set(lpfc_mbx_memory_dump_type3_length,
+               &mbox->u.mqe.un.mem_dump_type3, DMP_SFF_PAGE_A0_SIZE);
+       mbox->u.mqe.un.mem_dump_type3.addr_lo = putPaddrLow(mp->phys);
+       mbox->u.mqe.un.mem_dump_type3.addr_hi = putPaddrHigh(mp->phys);
+
+       return 0;
+}
+
 /**
  * lpfc_reg_fcfi - Initialize the REG_FCFI mailbox command
  * @phba: pointer to the hba structure containing the FCF index and RQ ID.
index 4cb9882af15723aca8cbb34d611a722400c40935..af3b38aba65e75e5bb56ef52b987154be610bc65 100644 (file)
@@ -661,7 +661,13 @@ lpfc_rcv_logo(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
                        lpfc_destroy_vport_work_array(phba, vports);
                }
 
-               if (active_vlink_present) {
+               /*
+                * Don't re-instantiate if vport is marked for deletion.
+                * If we are here first then vport_delete is going to wait
+                * for discovery to complete.
+                */
+               if (!(vport->load_flag & FC_UNLOADING) &&
+                                       active_vlink_present) {
                        /*
                         * If there are other active VLinks present,
                         * re-instantiate the Vlink using FDISC.
@@ -1868,7 +1874,7 @@ lpfc_rcv_logo_logo_issue(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
        struct lpfc_iocbq *cmdiocb = (struct lpfc_iocbq *)arg;
 
        spin_lock_irq(shost->host_lock);
-       ndlp->nlp_flag &= NLP_LOGO_ACC;
+       ndlp->nlp_flag |= NLP_LOGO_ACC;
        spin_unlock_irq(shost->host_lock);
        lpfc_els_rsp_acc(vport, ELS_CMD_ACC, cmdiocb, ndlp, NULL);
        return ndlp->nlp_state;
index c140f99772caa5963b15a67456b32d9194373927..e5eb40d2c5128fd64b2efa54ff4a6a9921664cba 100644 (file)
@@ -3257,7 +3257,7 @@ lpfc_scsi_prep_dma_buf_s4(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd)
                 */
 
                nseg = scsi_dma_map(scsi_cmnd);
-               if (unlikely(!nseg))
+               if (unlikely(nseg <= 0))
                        return 1;
                sgl += 1;
                /* clear the last flag in the fcp_rsp map entry */
@@ -3845,6 +3845,49 @@ lpfc_handle_fcp_err(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd,
        lpfc_send_scsi_error_event(vport->phba, vport, lpfc_cmd, rsp_iocb);
 }
 
+/**
+ * lpfc_sli4_scmd_to_wqidx_distr - scsi command to SLI4 WQ index distribution
+ * @phba: Pointer to HBA context object.
+ *
+ * This routine performs a roundrobin SCSI command to SLI4 FCP WQ index
+ * distribution.  This is called by __lpfc_sli_issue_iocb_s4() with the hbalock
+ * held.
+ * If scsi-mq is enabled, get the default block layer mapping of software queues
+ * to hardware queues. This information is saved in request tag.
+ *
+ * Return: index into SLI4 fast-path FCP queue index.
+ **/
+int lpfc_sli4_scmd_to_wqidx_distr(struct lpfc_hba *phba,
+                                 struct lpfc_scsi_buf *lpfc_cmd)
+{
+       struct scsi_cmnd *cmnd = lpfc_cmd->pCmd;
+       struct lpfc_vector_map_info *cpup;
+       int chann, cpu;
+       uint32_t tag;
+       uint16_t hwq;
+
+       if (shost_use_blk_mq(cmnd->device->host)) {
+               tag = blk_mq_unique_tag(cmnd->request);
+               hwq = blk_mq_unique_tag_to_hwq(tag);
+
+               return hwq;
+       }
+
+       if (phba->cfg_fcp_io_sched == LPFC_FCP_SCHED_BY_CPU
+           && phba->cfg_fcp_io_channel > 1) {
+               cpu = smp_processor_id();
+               if (cpu < phba->sli4_hba.num_present_cpu) {
+                       cpup = phba->sli4_hba.cpu_map;
+                       cpup += cpu;
+                       return cpup->channel_id;
+               }
+       }
+       chann = atomic_add_return(1, &phba->fcp_qidx);
+       chann = (chann % phba->cfg_fcp_io_channel);
+       return chann;
+}
+
+
 /**
  * lpfc_scsi_cmd_iocb_cmpl - Scsi cmnd IOCB completion routine
  * @phba: The Hba for which this call is being executed.
@@ -4537,7 +4580,7 @@ lpfc_queuecommand(struct Scsi_Host *shost, struct scsi_cmnd *cmnd)
        if (lpfc_cmd == NULL) {
                lpfc_rampdown_queue_depth(phba);
 
-               lpfc_printf_vlog(vport, KERN_INFO, LOG_FCP,
+               lpfc_printf_vlog(vport, KERN_INFO, LOG_MISC,
                                 "0707 driver's buffer pool is empty, "
                                 "IO busied\n");
                goto out_host_busy;
@@ -4968,13 +5011,16 @@ lpfc_send_taskmgmt(struct lpfc_vport *vport, struct lpfc_rport_data *rdata,
                                          iocbq, iocbqrsp, lpfc_cmd->timeout);
        if ((status != IOCB_SUCCESS) ||
            (iocbqrsp->iocb.ulpStatus != IOSTAT_SUCCESS)) {
-               lpfc_printf_vlog(vport, KERN_ERR, LOG_FCP,
-                        "0727 TMF %s to TGT %d LUN %llu failed (%d, %d) "
-                        "iocb_flag x%x\n",
-                        lpfc_taskmgmt_name(task_mgmt_cmd),
-                        tgt_id, lun_id, iocbqrsp->iocb.ulpStatus,
-                        iocbqrsp->iocb.un.ulpWord[4],
-                        iocbq->iocb_flag);
+               if (status != IOCB_SUCCESS ||
+                   iocbqrsp->iocb.ulpStatus != IOSTAT_FCP_RSP_ERROR)
+                       lpfc_printf_vlog(vport, KERN_ERR, LOG_FCP,
+                                        "0727 TMF %s to TGT %d LUN %llu "
+                                        "failed (%d, %d) iocb_flag x%x\n",
+                                        lpfc_taskmgmt_name(task_mgmt_cmd),
+                                        tgt_id, lun_id,
+                                        iocbqrsp->iocb.ulpStatus,
+                                        iocbqrsp->iocb.un.ulpWord[4],
+                                        iocbq->iocb_flag);
                /* if ulpStatus != IOCB_SUCCESS, then status == IOCB_SUCCESS */
                if (status == IOCB_SUCCESS) {
                        if (iocbqrsp->iocb.ulpStatus == IOSTAT_FCP_RSP_ERROR)
@@ -4988,7 +5034,6 @@ lpfc_send_taskmgmt(struct lpfc_vport *vport, struct lpfc_rport_data *rdata,
                } else {
                        ret = FAILED;
                }
-               lpfc_cmd->status = IOSTAT_DRIVER_REJECT;
        } else
                ret = SUCCESS;
 
index 474e30cdee6e37d48b78fdcbfb462c6fc1e93b56..18b9260ccfac2f19e0eccaac866dffc785d37195 100644 (file)
@@ -184,3 +184,6 @@ struct lpfc_scsi_buf {
 #define FIND_FIRST_OAS_LUN              0
 #define NO_MORE_OAS_LUN                        -1
 #define NOT_OAS_ENABLED_LUN            NO_MORE_OAS_LUN
+
+int lpfc_sli4_scmd_to_wqidx_distr(struct lpfc_hba *phba,
+                                 struct lpfc_scsi_buf *lpfc_cmd);
index 56f73682d4bd571f792faba2859e8266df30c834..4feb9312a44745787aa7cd0763991f864c81c04f 100644 (file)
@@ -2249,7 +2249,7 @@ lpfc_sli4_unreg_rpi_cmpl_clr(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
                                                 vport->vpi, ndlp->nlp_rpi,
                                                 ndlp->nlp_DID,
                                                 ndlp->nlp_usg_map, ndlp);
-
+                               ndlp->nlp_flag &= ~NLP_LOGO_ACC;
                                lpfc_nlp_put(ndlp);
                        }
                }
@@ -8137,36 +8137,6 @@ lpfc_sli4_bpl2sgl(struct lpfc_hba *phba, struct lpfc_iocbq *piocbq,
        return sglq->sli4_xritag;
 }
 
-/**
- * lpfc_sli4_scmd_to_wqidx_distr - scsi command to SLI4 WQ index distribution
- * @phba: Pointer to HBA context object.
- *
- * This routine performs a roundrobin SCSI command to SLI4 FCP WQ index
- * distribution.  This is called by __lpfc_sli_issue_iocb_s4() with the hbalock
- * held.
- *
- * Return: index into SLI4 fast-path FCP queue index.
- **/
-static inline int
-lpfc_sli4_scmd_to_wqidx_distr(struct lpfc_hba *phba)
-{
-       struct lpfc_vector_map_info *cpup;
-       int chann, cpu;
-
-       if (phba->cfg_fcp_io_sched == LPFC_FCP_SCHED_BY_CPU
-           && phba->cfg_fcp_io_channel > 1) {
-               cpu = smp_processor_id();
-               if (cpu < phba->sli4_hba.num_present_cpu) {
-                       cpup = phba->sli4_hba.cpu_map;
-                       cpup += cpu;
-                       return cpup->channel_id;
-               }
-       }
-       chann = atomic_add_return(1, &phba->fcp_qidx);
-       chann = (chann % phba->cfg_fcp_io_channel);
-       return chann;
-}
-
 /**
  * lpfc_sli_iocb2wqe - Convert the IOCB to a work queue entry.
  * @phba: Pointer to HBA context object.
@@ -8792,32 +8762,44 @@ lpfc_sli_api_table_setup(struct lpfc_hba *phba, uint8_t dev_grp)
        return 0;
 }
 
+/**
+ * lpfc_sli_calc_ring - Calculates which ring to use
+ * @phba: Pointer to HBA context object.
+ * @ring_number: Initial ring
+ * @piocb: Pointer to command iocb.
+ *
+ * For SLI4, FCP IO can deferred to one fo many WQs, based on
+ * fcp_wqidx, thus we need to calculate the corresponding ring.
+ * Since ABORTS must go on the same WQ of the command they are
+ * aborting, we use command's fcp_wqidx.
+ */
 int
 lpfc_sli_calc_ring(struct lpfc_hba *phba, uint32_t ring_number,
                    struct lpfc_iocbq *piocb)
 {
-       uint32_t idx;
+       if (phba->sli_rev < LPFC_SLI_REV4)
+               return ring_number;
 
-       if (phba->sli_rev == LPFC_SLI_REV4) {
-               if (piocb->iocb_flag &  (LPFC_IO_FCP | LPFC_USE_FCPWQIDX)) {
+       if (piocb->iocb_flag &  (LPFC_IO_FCP | LPFC_USE_FCPWQIDX)) {
+               if (!(phba->cfg_fof) ||
+                               (!(piocb->iocb_flag & LPFC_IO_FOF))) {
+                       if (unlikely(!phba->sli4_hba.fcp_wq))
+                               return LPFC_HBA_ERROR;
                        /*
-                        * fcp_wqidx should already be setup based on what
-                        * completion queue we want to use.
+                        * for abort iocb fcp_wqidx should already
+                        * be setup based on what work queue we used.
                         */
-                       if (!(phba->cfg_fof) ||
-                           (!(piocb->iocb_flag & LPFC_IO_FOF))) {
-                               if (unlikely(!phba->sli4_hba.fcp_wq))
-                                       return LPFC_HBA_ERROR;
-                               idx = lpfc_sli4_scmd_to_wqidx_distr(phba);
-                               piocb->fcp_wqidx = idx;
-                               ring_number = MAX_SLI3_CONFIGURED_RINGS + idx;
-                       } else {
-                               if (unlikely(!phba->sli4_hba.oas_wq))
-                                       return LPFC_HBA_ERROR;
-                               idx = 0;
-                               piocb->fcp_wqidx = idx;
-                               ring_number =  LPFC_FCP_OAS_RING;
-                       }
+                       if (!(piocb->iocb_flag & LPFC_USE_FCPWQIDX))
+                               piocb->fcp_wqidx =
+                                       lpfc_sli4_scmd_to_wqidx_distr(phba,
+                                                             piocb->context1);
+                       ring_number = MAX_SLI3_CONFIGURED_RINGS +
+                               piocb->fcp_wqidx;
+               } else {
+                       if (unlikely(!phba->sli4_hba.oas_wq))
+                               return LPFC_HBA_ERROR;
+                       piocb->fcp_wqidx = 0;
+                       ring_number =  LPFC_FCP_OAS_RING;
                }
        }
        return ring_number;
index 6eca3b8124d3c3abdb06f573b13e43506341ac11..d1a5b057c6f38c91f2367a885d875248357522b7 100644 (file)
@@ -602,6 +602,7 @@ struct lpfc_sli4_hba {
        struct lpfc_iov iov;
        spinlock_t abts_scsi_buf_list_lock; /* list of aborted SCSI IOs */
        spinlock_t abts_sgl_list_lock; /* list of aborted els IOs */
+       uint32_t physical_port;
 
        /* CPU to vector mapping information */
        struct lpfc_vector_map_info *cpu_map;
@@ -651,6 +652,26 @@ struct lpfc_rsrc_blks {
        uint16_t rsrc_used;
 };
 
+struct lpfc_rdp_context {
+       struct lpfc_nodelist *ndlp;
+       uint16_t ox_id;
+       uint16_t rx_id;
+       READ_LNK_VAR link_stat;
+       uint8_t page_a0[DMP_SFF_PAGE_A0_SIZE];
+       uint8_t page_a2[DMP_SFF_PAGE_A2_SIZE];
+       void (*cmpl)(struct lpfc_hba *, struct lpfc_rdp_context*, int);
+};
+
+struct lpfc_lcb_context {
+       uint8_t  sub_command;
+       uint8_t  type;
+       uint8_t  frequency;
+       uint16_t ox_id;
+       uint16_t rx_id;
+       struct lpfc_nodelist *ndlp;
+};
+
+
 /*
  * SLI4 specific function prototypes
  */
index c37bb9f91c3be8c0f022a3a00c6485a447172428..6258d3d7722a07689a8cdf0e9483f6bda3240d0d 100644 (file)
@@ -18,7 +18,7 @@
  * included with this package.                                     *
  *******************************************************************/
 
-#define LPFC_DRIVER_VERSION "10.5.0.0."
+#define LPFC_DRIVER_VERSION "10.7.0.0."
 #define LPFC_DRIVER_NAME               "lpfc"
 
 /* Used for SLI 2/3 */
index a87ee33f4f2a6619b9b2f92a35c6e571a7a8d37e..769012663a8f53c2590234009fec7d7127e15f85 100644 (file)
@@ -567,8 +567,8 @@ int
 lpfc_vport_delete(struct fc_vport *fc_vport)
 {
        struct lpfc_nodelist *ndlp = NULL;
-       struct Scsi_Host *shost = (struct Scsi_Host *) fc_vport->shost;
        struct lpfc_vport *vport = *(struct lpfc_vport **)fc_vport->dd_data;
+       struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
        struct lpfc_hba   *phba = vport->phba;
        long timeout;
        bool ns_ndlp_referenced = false;
@@ -645,8 +645,8 @@ lpfc_vport_delete(struct fc_vport *fc_vport)
        }
 
        /* Remove FC host and then SCSI host with the vport */
-       fc_remove_host(lpfc_shost_from_vport(vport));
-       scsi_remove_host(lpfc_shost_from_vport(vport));
+       fc_remove_host(shost);
+       scsi_remove_host(shost);
 
        ndlp = lpfc_findnode_did(phba->pport, Fabric_DID);
 
@@ -772,7 +772,8 @@ skip_logo:
                 * Completion of unreg_vpi (lpfc_mbx_cmpl_unreg_vpi)
                 * does the scsi_host_put() to release the vport.
                 */
-               if (lpfc_mbx_unreg_vpi(vport))
+               if (!(vport->vpi_state & LPFC_VPI_REGISTERED) ||
+                               lpfc_mbx_unreg_vpi(vport))
                        scsi_host_put(shost);
        } else
                scsi_host_put(shost);
index 0adb2e015597a68eb2d430d00bfd0bbb1a558a01..1412266314292dfa59bf473cf2ed3fdb2edebe65 100644 (file)
@@ -403,7 +403,6 @@ static struct scsi_host_template mac53c94_template = {
        .can_queue      = 1,
        .this_id        = 7,
        .sg_tablesize   = SG_ALL,
-       .cmd_per_lun    = 1,
        .use_clustering = DISABLE_CLUSTERING,
 };
 
index 14e5c7cea929246b8c0caaffef675e83101bc4b7..20c37541963f4acd04ed6836647469f3d46cdabe 100644 (file)
@@ -35,7 +35,8 @@
 /*
  * MegaRAID SAS Driver meta data
  */
-#define MEGASAS_VERSION                                "06.806.08.00-rc1"
+#define MEGASAS_VERSION                                "06.807.10.00-rc1"
+#define MEGASAS_RELDATE                                "March 6, 2015"
 
 /*
  * Device IDs
 #define MFI_FRAME_DIR_BOTH                     0x0018
 #define MFI_FRAME_IEEE                          0x0020
 
+/* Driver internal */
+#define DRV_DCMD_POLLED_MODE           0x1
+
 /*
  * Definition for cmd_status
  */
@@ -408,7 +412,7 @@ enum MR_PD_STATE {
  * defines the physical drive address structure
  */
 struct MR_PD_ADDRESS {
-       u16     deviceId;
+       __le16  deviceId;
        u16     enclDeviceId;
 
        union {
@@ -433,8 +437,8 @@ struct MR_PD_ADDRESS {
  * defines the physical drive list structure
  */
 struct MR_PD_LIST {
-       u32             size;
-       u32             count;
+       __le32          size;
+       __le32          count;
        struct MR_PD_ADDRESS   addr[1];
 } __packed;
 
@@ -451,28 +455,28 @@ union  MR_LD_REF {
        struct {
                u8      targetId;
                u8      reserved;
-               u16     seqNum;
+               __le16     seqNum;
        };
-       u32     ref;
+       __le32     ref;
 } __packed;
 
 /*
  * defines the logical drive list structure
  */
 struct MR_LD_LIST {
-       u32     ldCount;
-       u32     reserved;
+       __le32     ldCount;
+       __le32     reserved;
        struct {
                union MR_LD_REF   ref;
                u8          state;
                u8          reserved[3];
-               u64         size;
+               __le64          size;
        } ldList[MAX_LOGICAL_DRIVES_EXT];
 } __packed;
 
 struct MR_LD_TARGETID_LIST {
-       u32     size;
-       u32     count;
+       __le32  size;
+       __le32  count;
        u8      pad[3];
        u8      targetId[MAX_LOGICAL_DRIVES_EXT];
 };
@@ -553,7 +557,7 @@ struct megasas_ctrl_prop {
        } OnOffProperties;
        u8 autoSnapVDSpace;
        u8 viewSpace;
-       u16 spinDownTime;
+       __le16 spinDownTime;
        u8  reserved[24];
 } __packed;
 
@@ -567,10 +571,10 @@ struct megasas_ctrl_info {
         */
        struct {
 
-               u16 vendor_id;
-               u16 device_id;
-               u16 sub_vendor_id;
-               u16 sub_device_id;
+               __le16 vendor_id;
+               __le16 device_id;
+               __le16 sub_vendor_id;
+               __le16 sub_device_id;
                u8 reserved[24];
 
        } __attribute__ ((packed)) pci;
@@ -611,8 +615,8 @@ struct megasas_ctrl_info {
        /*
         * List of components residing in flash. All str are null terminated
         */
-       u32 image_check_word;
-       u32 image_component_count;
+       __le32 image_check_word;
+       __le32 image_component_count;
 
        struct {
 
@@ -629,7 +633,7 @@ struct megasas_ctrl_info {
         * empty if a flash operation has not occurred. All stings are null
         * terminated
         */
-       u32 pending_image_component_count;
+       __le32 pending_image_component_count;
 
        struct {
 
@@ -662,39 +666,39 @@ struct megasas_ctrl_info {
 
        } __attribute__ ((packed)) hw_present;
 
-       u32 current_fw_time;
+       __le32 current_fw_time;
 
        /*
         * Maximum data transfer sizes
         */
-       u16 max_concurrent_cmds;
-       u16 max_sge_count;
-       u32 max_request_size;
+       __le16 max_concurrent_cmds;
+       __le16 max_sge_count;
+       __le32 max_request_size;
 
        /*
         * Logical and physical device counts
         */
-       u16 ld_present_count;
-       u16 ld_degraded_count;
-       u16 ld_offline_count;
+       __le16 ld_present_count;
+       __le16 ld_degraded_count;
+       __le16 ld_offline_count;
 
-       u16 pd_present_count;
-       u16 pd_disk_present_count;
-       u16 pd_disk_pred_failure_count;
-       u16 pd_disk_failed_count;
+       __le16 pd_present_count;
+       __le16 pd_disk_present_count;
+       __le16 pd_disk_pred_failure_count;
+       __le16 pd_disk_failed_count;
 
        /*
         * Memory size information
         */
-       u16 nvram_size;
-       u16 memory_size;
-       u16 flash_size;
+       __le16 nvram_size;
+       __le16 memory_size;
+       __le16 flash_size;
 
        /*
         * Error counters
         */
-       u16 mem_correctable_error_count;
-       u16 mem_uncorrectable_error_count;
+       __le16 mem_correctable_error_count;
+       __le16 mem_uncorrectable_error_count;
 
        /*
         * Cluster information
@@ -705,7 +709,7 @@ struct megasas_ctrl_info {
        /*
         * Additional max data transfer sizes
         */
-       u16 max_strips_per_io;
+       __le16 max_strips_per_io;
 
        /*
         * Controller capabilities structures
@@ -805,7 +809,7 @@ struct megasas_ctrl_info {
        * deviceInterface.portAddr, and the rest shall be
        * populated in deviceInterfacePortAddr2.
        */
-       u64         deviceInterfacePortAddr2[8]; /*6a0h */
+       __le64      deviceInterfacePortAddr2[8]; /*6a0h */
        u8          reserved3[128];              /*6e0h */
 
        struct {                                /*760h */
@@ -842,26 +846,26 @@ struct megasas_ctrl_info {
                u16 reserved[6];
        } pdsForRaidLevels;
 
-       u16 maxPds;                             /*780h */
-       u16 maxDedHSPs;                         /*782h */
-       u16 maxGlobalHSPs;                      /*784h */
-       u16 ddfSize;                            /*786h */
+       __le16 maxPds;                          /*780h */
+       __le16 maxDedHSPs;                      /*782h */
+       __le16 maxGlobalHSP;                    /*784h */
+       __le16 ddfSize;                         /*786h */
        u8  maxLdsPerArray;                     /*788h */
        u8  partitionsInDDF;                    /*789h */
        u8  lockKeyBinding;                     /*78ah */
        u8  maxPITsPerLd;                       /*78bh */
        u8  maxViewsPerLd;                      /*78ch */
        u8  maxTargetId;                        /*78dh */
-       u16 maxBvlVdSize;                       /*78eh */
+       __le16 maxBvlVdSize;                    /*78eh */
 
-       u16 maxConfigurableSSCSize;             /*790h */
-       u16 currentSSCsize;                     /*792h */
+       __le16 maxConfigurableSSCSize;          /*790h */
+       __le16 currentSSCsize;                  /*792h */
 
        char    expanderFwVersion[12];          /*794h */
 
-       u16 PFKTrialTimeRemaining;              /*7A0h */
+       __le16 PFKTrialTimeRemaining;           /*7A0h */
 
-       u16 cacheMemorySize;                    /*7A2h */
+       __le16 cacheMemorySize;                 /*7A2h */
 
        struct {                                /*7A4h */
 #if   defined(__BIG_ENDIAN_BITFIELD)
@@ -931,7 +935,7 @@ struct megasas_ctrl_info {
        u8  temperatureROC;                     /*7C9h */
        u8  temperatureCtrl;                    /*7CAh */
        u8  reserved4;                          /*7CBh */
-       u16 maxConfigurablePds;                 /*7CCh */
+       __le16 maxConfigurablePds;              /*7CCh */
 
 
        u8  reserved5[2];                       /*0x7CDh */
@@ -1042,11 +1046,6 @@ struct megasas_ctrl_info {
 
 #define VD_EXT_DEBUG 0
 
-enum MR_MFI_MPT_PTHR_FLAGS {
-       MFI_MPT_DETACHED = 0,
-       MFI_LIST_ADDED = 1,
-       MFI_MPT_ATTACHED = 2,
-};
 
 enum MR_SCSI_CMD_TYPE {
        READ_WRITE_LDIO = 0,
@@ -1084,6 +1083,7 @@ enum MR_SCSI_CMD_TYPE {
 #define MEGASAS_SKINNY_INT_CMDS                        5
 #define MEGASAS_FUSION_INTERNAL_CMDS           5
 #define MEGASAS_FUSION_IOCTL_CMDS              3
+#define MEGASAS_MFI_IOCTL_CMDS                 27
 
 #define MEGASAS_MAX_MSIX_QUEUES                        128
 /*
@@ -1172,22 +1172,22 @@ struct megasas_register_set {
 
 struct megasas_sge32 {
 
-       u32 phys_addr;
-       u32 length;
+       __le32 phys_addr;
+       __le32 length;
 
 } __attribute__ ((packed));
 
 struct megasas_sge64 {
 
-       u64 phys_addr;
-       u32 length;
+       __le64 phys_addr;
+       __le32 length;
 
 } __attribute__ ((packed));
 
 struct megasas_sge_skinny {
-       u64 phys_addr;
-       u32 length;
-       u32 flag;
+       __le64 phys_addr;
+       __le32 length;
+       __le32 flag;
 } __packed;
 
 union megasas_sgl {
@@ -1210,12 +1210,12 @@ struct megasas_header {
        u8 cdb_len;             /*06h */
        u8 sge_count;           /*07h */
 
-       u32 context;            /*08h */
-       u32 pad_0;              /*0Ch */
+       __le32 context;         /*08h */
+       __le32 pad_0;           /*0Ch */
 
-       u16 flags;              /*10h */
-       u16 timeout;            /*12h */
-       u32 data_xferlen;       /*14h */
+       __le16 flags;           /*10h */
+       __le16 timeout;         /*12h */
+       __le32 data_xferlen;    /*14h */
 
 } __attribute__ ((packed));
 
@@ -1248,7 +1248,7 @@ typedef union _MFI_CAPABILITIES {
                u32     reserved:25;
 #endif
        } mfi_capabilities;
-       u32     reg;
+       __le32          reg;
 } MFI_CAPABILITIES;
 
 struct megasas_init_frame {
@@ -1260,33 +1260,35 @@ struct megasas_init_frame {
        u8 reserved_1;          /*03h */
        MFI_CAPABILITIES driver_operations; /*04h*/
 
-       u32 context;            /*08h */
-       u32 pad_0;              /*0Ch */
-
-       u16 flags;              /*10h */
-       u16 reserved_3;         /*12h */
-       u32 data_xfer_len;      /*14h */
+       __le32 context;         /*08h */
+       __le32 pad_0;           /*0Ch */
 
-       u32 queue_info_new_phys_addr_lo;        /*18h */
-       u32 queue_info_new_phys_addr_hi;        /*1Ch */
-       u32 queue_info_old_phys_addr_lo;        /*20h */
-       u32 queue_info_old_phys_addr_hi;        /*24h */
+       __le16 flags;           /*10h */
+       __le16 reserved_3;              /*12h */
+       __le32 data_xfer_len;   /*14h */
 
-       u32 reserved_4[6];      /*28h */
+       __le32 queue_info_new_phys_addr_lo;     /*18h */
+       __le32 queue_info_new_phys_addr_hi;     /*1Ch */
+       __le32 queue_info_old_phys_addr_lo;     /*20h */
+       __le32 queue_info_old_phys_addr_hi;     /*24h */
+       __le32 reserved_4[2];   /*28h */
+       __le32 system_info_lo;      /*30h */
+       __le32 system_info_hi;      /*34h */
+       __le32 reserved_5[2];   /*38h */
 
 } __attribute__ ((packed));
 
 struct megasas_init_queue_info {
 
-       u32 init_flags;         /*00h */
-       u32 reply_queue_entries;        /*04h */
+       __le32 init_flags;              /*00h */
+       __le32 reply_queue_entries;     /*04h */
 
-       u32 reply_queue_start_phys_addr_lo;     /*08h */
-       u32 reply_queue_start_phys_addr_hi;     /*0Ch */
-       u32 producer_index_phys_addr_lo;        /*10h */
-       u32 producer_index_phys_addr_hi;        /*14h */
-       u32 consumer_index_phys_addr_lo;        /*18h */
-       u32 consumer_index_phys_addr_hi;        /*1Ch */
+       __le32 reply_queue_start_phys_addr_lo;  /*08h */
+       __le32 reply_queue_start_phys_addr_hi;  /*0Ch */
+       __le32 producer_index_phys_addr_lo;     /*10h */
+       __le32 producer_index_phys_addr_hi;     /*14h */
+       __le32 consumer_index_phys_addr_lo;     /*18h */
+       __le32 consumer_index_phys_addr_hi;     /*1Ch */
 
 } __attribute__ ((packed));
 
@@ -1302,18 +1304,18 @@ struct megasas_io_frame {
        u8 reserved_0;          /*06h */
        u8 sge_count;           /*07h */
 
-       u32 context;            /*08h */
-       u32 pad_0;              /*0Ch */
+       __le32 context;         /*08h */
+       __le32 pad_0;           /*0Ch */
 
-       u16 flags;              /*10h */
-       u16 timeout;            /*12h */
-       u32 lba_count;          /*14h */
+       __le16 flags;           /*10h */
+       __le16 timeout;         /*12h */
+       __le32 lba_count;       /*14h */
 
-       u32 sense_buf_phys_addr_lo;     /*18h */
-       u32 sense_buf_phys_addr_hi;     /*1Ch */
+       __le32 sense_buf_phys_addr_lo;  /*18h */
+       __le32 sense_buf_phys_addr_hi;  /*1Ch */
 
-       u32 start_lba_lo;       /*20h */
-       u32 start_lba_hi;       /*24h */
+       __le32 start_lba_lo;    /*20h */
+       __le32 start_lba_hi;    /*24h */
 
        union megasas_sgl sgl;  /*28h */
 
@@ -1331,15 +1333,15 @@ struct megasas_pthru_frame {
        u8 cdb_len;             /*06h */
        u8 sge_count;           /*07h */
 
-       u32 context;            /*08h */
-       u32 pad_0;              /*0Ch */
+       __le32 context;         /*08h */
+       __le32 pad_0;           /*0Ch */
 
-       u16 flags;              /*10h */
-       u16 timeout;            /*12h */
-       u32 data_xfer_len;      /*14h */
+       __le16 flags;           /*10h */
+       __le16 timeout;         /*12h */
+       __le32 data_xfer_len;   /*14h */
 
-       u32 sense_buf_phys_addr_lo;     /*18h */
-       u32 sense_buf_phys_addr_hi;     /*1Ch */
+       __le32 sense_buf_phys_addr_lo;  /*18h */
+       __le32 sense_buf_phys_addr_hi;  /*1Ch */
 
        u8 cdb[16];             /*20h */
        union megasas_sgl sgl;  /*30h */
@@ -1354,19 +1356,19 @@ struct megasas_dcmd_frame {
        u8 reserved_1[4];       /*03h */
        u8 sge_count;           /*07h */
 
-       u32 context;            /*08h */
-       u32 pad_0;              /*0Ch */
+       __le32 context;         /*08h */
+       __le32 pad_0;           /*0Ch */
 
-       u16 flags;              /*10h */
-       u16 timeout;            /*12h */
+       __le16 flags;           /*10h */
+       __le16 timeout;         /*12h */
 
-       u32 data_xfer_len;      /*14h */
-       u32 opcode;             /*18h */
+       __le32 data_xfer_len;   /*14h */
+       __le32 opcode;          /*18h */
 
        union {                 /*1Ch */
                u8 b[12];
-               u16 s[6];
-               u32 w[3];
+               __le16 s[6];
+               __le32 w[3];
        } mbox;
 
        union megasas_sgl sgl;  /*28h */
@@ -1380,22 +1382,22 @@ struct megasas_abort_frame {
        u8 cmd_status;          /*02h */
 
        u8 reserved_1;          /*03h */
-       u32 reserved_2;         /*04h */
+       __le32 reserved_2;      /*04h */
 
-       u32 context;            /*08h */
-       u32 pad_0;              /*0Ch */
+       __le32 context;         /*08h */
+       __le32 pad_0;           /*0Ch */
 
-       u16 flags;              /*10h */
-       u16 reserved_3;         /*12h */
-       u32 reserved_4;         /*14h */
+       __le16 flags;           /*10h */
+       __le16 reserved_3;      /*12h */
+       __le32 reserved_4;      /*14h */
 
-       u32 abort_context;      /*18h */
-       u32 pad_1;              /*1Ch */
+       __le32 abort_context;   /*18h */
+       __le32 pad_1;           /*1Ch */
 
-       u32 abort_mfi_phys_addr_lo;     /*20h */
-       u32 abort_mfi_phys_addr_hi;     /*24h */
+       __le32 abort_mfi_phys_addr_lo;  /*20h */
+       __le32 abort_mfi_phys_addr_hi;  /*24h */
 
-       u32 reserved_5[6];      /*28h */
+       __le32 reserved_5[6];   /*28h */
 
 } __attribute__ ((packed));
 
@@ -1409,14 +1411,14 @@ struct megasas_smp_frame {
        u8 reserved_2[3];       /*04h */
        u8 sge_count;           /*07h */
 
-       u32 context;            /*08h */
-       u32 pad_0;              /*0Ch */
+       __le32 context;         /*08h */
+       __le32 pad_0;           /*0Ch */
 
-       u16 flags;              /*10h */
-       u16 timeout;            /*12h */
+       __le16 flags;           /*10h */
+       __le16 timeout;         /*12h */
 
-       u32 data_xfer_len;      /*14h */
-       u64 sas_addr;           /*18h */
+       __le32 data_xfer_len;   /*14h */
+       __le64 sas_addr;        /*18h */
 
        union {
                struct megasas_sge32 sge32[2];  /* [0]: resp [1]: req */
@@ -1436,16 +1438,16 @@ struct megasas_stp_frame {
        u8 reserved_3[2];       /*05h */
        u8 sge_count;           /*07h */
 
-       u32 context;            /*08h */
-       u32 pad_0;              /*0Ch */
+       __le32 context;         /*08h */
+       __le32 pad_0;           /*0Ch */
 
-       u16 flags;              /*10h */
-       u16 timeout;            /*12h */
+       __le16 flags;           /*10h */
+       __le16 timeout;         /*12h */
 
-       u32 data_xfer_len;      /*14h */
+       __le32 data_xfer_len;   /*14h */
 
-       u16 fis[10];            /*18h */
-       u32 stp_flags;
+       __le16 fis[10];         /*18h */
+       __le32 stp_flags;
 
        union {
                struct megasas_sge32 sge32[2];  /* [0]: resp [1]: data */
@@ -1489,18 +1491,18 @@ union megasas_evt_class_locale {
 } __attribute__ ((packed));
 
 struct megasas_evt_log_info {
-       u32 newest_seq_num;
-       u32 oldest_seq_num;
-       u32 clear_seq_num;
-       u32 shutdown_seq_num;
-       u32 boot_seq_num;
+       __le32 newest_seq_num;
+       __le32 oldest_seq_num;
+       __le32 clear_seq_num;
+       __le32 shutdown_seq_num;
+       __le32 boot_seq_num;
 
 } __attribute__ ((packed));
 
 struct megasas_progress {
 
-       u16 progress;
-       u16 elapsed_seconds;
+       __le16 progress;
+       __le16 elapsed_seconds;
 
 } __attribute__ ((packed));
 
@@ -1521,9 +1523,9 @@ struct megasas_evtarg_pd {
 
 struct megasas_evt_detail {
 
-       u32 seq_num;
-       u32 time_stamp;
-       u32 code;
+       __le32 seq_num;
+       __le32 time_stamp;
+       __le32 code;
        union megasas_evt_class_locale cl;
        u8 arg_type;
        u8 reserved1[15];
@@ -1542,18 +1544,18 @@ struct megasas_evt_detail {
 
                struct {
                        struct megasas_evtarg_ld ld;
-                       u64 count;
+                       __le64 count;
                } __attribute__ ((packed)) ld_count;
 
                struct {
-                       u64 lba;
+                       __le64 lba;
                        struct megasas_evtarg_ld ld;
                } __attribute__ ((packed)) ld_lba;
 
                struct {
                        struct megasas_evtarg_ld ld;
-                       u32 prevOwner;
-                       u32 newOwner;
+                       __le32 prevOwner;
+                       __le32 newOwner;
                } __attribute__ ((packed)) ld_owner;
 
                struct {
@@ -1610,7 +1612,7 @@ struct megasas_evt_detail {
 
                struct {
                        u16 vendorId;
-                       u16 deviceId;
+                       __le16 deviceId;
                        u16 subVendorId;
                        u16 subDeviceId;
                } __attribute__ ((packed)) pci;
@@ -1630,9 +1632,9 @@ struct megasas_evt_detail {
                } __attribute__ ((packed)) ecc;
 
                u8 b[96];
-               u16 s[48];
-               u32 w[24];
-               u64 d[12];
+               __le16 s[48];
+               __le32 w[24];
+               __le64 d[12];
        } args;
 
        char description[128];
@@ -1649,12 +1651,22 @@ struct megasas_irq_context {
        u32 MSIxIndex;
 };
 
+struct MR_DRV_SYSTEM_INFO {
+       u8      infoVersion;
+       u8      systemIdLength;
+       u16     reserved0;
+       u8      systemId[64];
+       u8      reserved[1980];
+};
+
 struct megasas_instance {
 
-       u32 *producer;
+       __le32 *producer;
        dma_addr_t producer_h;
-       u32 *consumer;
+       __le32 *consumer;
        dma_addr_t consumer_h;
+       struct MR_DRV_SYSTEM_INFO *system_info_buf;
+       dma_addr_t system_info_h;
        struct MR_LD_VF_AFFILIATION *vf_affiliation;
        dma_addr_t vf_affiliation_h;
        struct MR_LD_VF_AFFILIATION_111 *vf_affiliation_111;
@@ -1662,7 +1674,7 @@ struct megasas_instance {
        struct MR_CTRL_HB_HOST_MEM *hb_host_mem;
        dma_addr_t hb_host_mem_h;
 
-       u32 *reply_queue;
+       __le32 *reply_queue;
        dma_addr_t reply_queue_h;
 
        u32 *crash_dump_buf;
@@ -1681,7 +1693,7 @@ struct megasas_instance {
        spinlock_t crashdump_lock;
 
        struct megasas_register_set __iomem *reg_set;
-       u32 *reply_post_host_index_addr[MR_MAX_MSIX_REG_ARRAY];
+       u32 __iomem *reply_post_host_index_addr[MR_MAX_MSIX_REG_ARRAY];
        struct megasas_pd_list          pd_list[MEGASAS_MAX_PD];
        struct megasas_pd_list          local_pd_list[MEGASAS_MAX_PD];
        u8 ld_ids[MEGASAS_MAX_LD_IDS];
@@ -1769,6 +1781,7 @@ struct megasas_instance {
        u16 throttlequeuedepth;
        u8 mask_interrupts;
        u8 is_imr;
+       bool dev_handle;
 };
 struct MR_LD_VF_MAP {
        u32 size;
@@ -1864,9 +1877,13 @@ struct megasas_instance_template {
 #define MEGASAS_IS_LOGICAL(scp)                                                \
        (scp->device->channel < MEGASAS_MAX_PD_CHANNELS) ? 0 : 1
 
-#define MEGASAS_DEV_INDEX(inst, scp)                                   \
-       ((scp->device->channel % 2) * MEGASAS_MAX_DEV_PER_CHANNEL) +    \
-       scp->device->id
+#define MEGASAS_DEV_INDEX(scp)                                         \
+       (((scp->device->channel % 2) * MEGASAS_MAX_DEV_PER_CHANNEL) +   \
+       scp->device->id)
+
+#define MEGASAS_PD_INDEX(scp)                                          \
+       ((scp->device->channel * MEGASAS_MAX_DEV_PER_CHANNEL) +         \
+       scp->device->id)
 
 struct megasas_cmd {
 
@@ -1877,17 +1894,14 @@ struct megasas_cmd {
 
        u32 index;
        u8 sync_cmd;
-       u8 cmd_status;
+       u8 cmd_status_drv;
        u8 abort_aen;
        u8 retry_for_fw_reset;
 
 
        struct list_head list;
        struct scsi_cmnd *scmd;
-
-       void *mpt_pthr_cmd_blocked;
-       atomic_t mfi_mpt_pthr;
-       u8 is_wait_event;
+       u8 flags;
 
        struct megasas_instance *instance;
        union {
@@ -1963,10 +1977,10 @@ u8 MR_TargetIdToLdGet(u32 ldTgtId, struct MR_DRV_RAID_MAP_ALL *map);
 struct MR_LD_RAID *MR_LdRaidGet(u32 ld, struct MR_DRV_RAID_MAP_ALL *map);
 u16 MR_ArPdGet(u32 ar, u32 arm, struct MR_DRV_RAID_MAP_ALL *map);
 u16 MR_LdSpanArrayGet(u32 ld, u32 span, struct MR_DRV_RAID_MAP_ALL *map);
-u16 MR_PdDevHandleGet(u32 pd, struct MR_DRV_RAID_MAP_ALL *map);
+__le16 MR_PdDevHandleGet(u32 pd, struct MR_DRV_RAID_MAP_ALL *map);
 u16 MR_GetLDTgtId(u32 ld, struct MR_DRV_RAID_MAP_ALL *map);
 
-u16 get_updated_dev_handle(struct megasas_instance *instance,
+__le16 get_updated_dev_handle(struct megasas_instance *instance,
        struct LD_LOAD_BALANCE_INFO *lbInfo, struct IO_REQUEST_INFO *in_info);
 void mr_update_load_balance_params(struct MR_DRV_RAID_MAP_ALL *map,
        struct LD_LOAD_BALANCE_INFO *lbInfo);
index 890637fdd61e3535da093d0f9b41e4efc95eca13..71b884dae27c7e1413c0aa300b9ce1e30a556d83 100644 (file)
@@ -94,8 +94,8 @@ MODULE_PARM_DESC(smp_affinity_enable, "SMP affinity feature enable/disbale Defau
 
 MODULE_LICENSE("GPL");
 MODULE_VERSION(MEGASAS_VERSION);
-MODULE_AUTHOR("megaraidlinux@lsi.com");
-MODULE_DESCRIPTION("LSI MegaRAID SAS Driver");
+MODULE_AUTHOR("megaraidlinux.pdl@avagotech.com");
+MODULE_DESCRIPTION("Avago MegaRAID SAS Driver");
 
 int megasas_transition_to_ready(struct megasas_instance *instance, int ocr);
 static int megasas_get_pd_list(struct megasas_instance *instance);
@@ -215,7 +215,6 @@ struct megasas_cmd *megasas_get_cmd(struct megasas_instance
                cmd = list_entry((&instance->cmd_pool)->next,
                                 struct megasas_cmd, list);
                list_del_init(&cmd->list);
-               atomic_set(&cmd->mfi_mpt_pthr, MFI_MPT_DETACHED);
        } else {
                printk(KERN_ERR "megasas: Command pool empty!\n");
        }
@@ -225,52 +224,41 @@ struct megasas_cmd *megasas_get_cmd(struct megasas_instance
 }
 
 /**
- * __megasas_return_cmd -      Return a cmd to free command pool
+ * megasas_return_cmd -        Return a cmd to free command pool
  * @instance:          Adapter soft state
  * @cmd:               Command packet to be returned to free command pool
  */
 inline void
-__megasas_return_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd)
+megasas_return_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd)
 {
-       /*
-        * Don't go ahead and free the MFI frame, if corresponding
-        * MPT frame is not freed(valid for only fusion adapters).
-        * In case of MFI adapters, anyways for any allocated MFI
-        * frame will have cmd->mfi_mpt_mpthr set to MFI_MPT_DETACHED
+       unsigned long flags;
+       u32 blk_tags;
+       struct megasas_cmd_fusion *cmd_fusion;
+       struct fusion_context *fusion = instance->ctrl_context;
+
+       /* This flag is used only for fusion adapter.
+        * Wait for Interrupt for Polled mode DCMD
         */
-       if (atomic_read(&cmd->mfi_mpt_pthr) != MFI_MPT_DETACHED)
+       if (cmd->flags & DRV_DCMD_POLLED_MODE)
                return;
 
+       spin_lock_irqsave(&instance->mfi_pool_lock, flags);
+
+       if (fusion) {
+               blk_tags = instance->max_scsi_cmds + cmd->index;
+               cmd_fusion = fusion->cmd_list[blk_tags];
+               megasas_return_cmd_fusion(instance, cmd_fusion);
+       }
        cmd->scmd = NULL;
        cmd->frame_count = 0;
-       cmd->is_wait_event = 0;
-       cmd->mpt_pthr_cmd_blocked = NULL;
-
-       if ((instance->pdev->device != PCI_DEVICE_ID_LSI_FUSION) &&
-           (instance->pdev->device != PCI_DEVICE_ID_LSI_INVADER) &&
-           (instance->pdev->device != PCI_DEVICE_ID_LSI_FURY) &&
-           (reset_devices))
+       cmd->flags = 0;
+       if (!fusion && reset_devices)
                cmd->frame->hdr.cmd = MFI_CMD_INVALID;
-
-       atomic_set(&cmd->mfi_mpt_pthr, MFI_LIST_ADDED);
        list_add(&cmd->list, (&instance->cmd_pool)->next);
-}
 
-/**
- * megasas_return_cmd -        Return a cmd to free command pool
- * @instance:          Adapter soft state
- * @cmd:               Command packet to be returned to free command pool
- */
-inline void
-megasas_return_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd)
-{
-       unsigned long flags;
-
-       spin_lock_irqsave(&instance->mfi_pool_lock, flags);
-       __megasas_return_cmd(instance, cmd);
        spin_unlock_irqrestore(&instance->mfi_pool_lock, flags);
-}
 
+}
 
 /**
 *      The following functions are defined for xscale
@@ -814,8 +802,8 @@ megasas_adp_reset_gen2(struct megasas_instance *instance,
 {
        u32                     retry = 0 ;
        u32                     HostDiag;
-       u32                     *seq_offset = &reg_set->seq_offset;
-       u32                     *hostdiag_offset = &reg_set->host_diag;
+       u32 __iomem             *seq_offset = &reg_set->seq_offset;
+       u32 __iomem             *hostdiag_offset = &reg_set->host_diag;
 
        if (instance->instancet == &megasas_instance_template_skinny) {
                seq_offset = &reg_set->fusion_seq_offset;
@@ -910,7 +898,7 @@ extern struct megasas_instance_template megasas_instance_template_fusion;
  * @instance:                  Adapter soft state
  * @cmd:                       Command packet to be issued
  *
- * For polling, MFI requires the cmd_status to be set to 0xFF before posting.
+ * For polling, MFI requires the cmd_status to be set to MFI_STAT_INVALID_STATUS before posting.
  */
 int
 megasas_issue_polled(struct megasas_instance *instance, struct megasas_cmd *cmd)
@@ -952,20 +940,20 @@ megasas_issue_blocked_cmd(struct megasas_instance *instance,
                          struct megasas_cmd *cmd, int timeout)
 {
        int ret = 0;
-       cmd->cmd_status = ENODATA;
+       cmd->cmd_status_drv = MFI_STAT_INVALID_STATUS;
 
-       cmd->is_wait_event = 1;
        instance->instancet->issue_dcmd(instance, cmd);
        if (timeout) {
                ret = wait_event_timeout(instance->int_cmd_wait_q,
-                               cmd->cmd_status != ENODATA, timeout * HZ);
+                               cmd->cmd_status_drv != MFI_STAT_INVALID_STATUS, timeout * HZ);
                if (!ret)
                        return 1;
        } else
                wait_event(instance->int_cmd_wait_q,
-                               cmd->cmd_status != ENODATA);
+                               cmd->cmd_status_drv != MFI_STAT_INVALID_STATUS);
 
-       return 0;
+       return (cmd->cmd_status_drv == MFI_STAT_OK) ?
+               0 : 1;
 }
 
 /**
@@ -998,7 +986,7 @@ megasas_issue_blocked_abort_cmd(struct megasas_instance *instance,
         * Prepare and issue the abort frame
         */
        abort_fr->cmd = MFI_CMD_ABORT;
-       abort_fr->cmd_status = 0xFF;
+       abort_fr->cmd_status = MFI_STAT_INVALID_STATUS;
        abort_fr->flags = cpu_to_le16(0);
        abort_fr->abort_context = cpu_to_le32(cmd_to_abort->index);
        abort_fr->abort_mfi_phys_addr_lo =
@@ -1007,13 +995,13 @@ megasas_issue_blocked_abort_cmd(struct megasas_instance *instance,
                cpu_to_le32(upper_32_bits(cmd_to_abort->frame_phys_addr));
 
        cmd->sync_cmd = 1;
-       cmd->cmd_status = ENODATA;
+       cmd->cmd_status_drv = MFI_STAT_INVALID_STATUS;
 
        instance->instancet->issue_dcmd(instance, cmd);
 
        if (timeout) {
                ret = wait_event_timeout(instance->abort_cmd_wait_q,
-                               cmd->cmd_status != ENODATA, timeout * HZ);
+                               cmd->cmd_status_drv != MFI_STAT_INVALID_STATUS, timeout * HZ);
                if (!ret) {
                        dev_err(&instance->pdev->dev, "Command timedout"
                                "from %s\n", __func__);
@@ -1021,7 +1009,7 @@ megasas_issue_blocked_abort_cmd(struct megasas_instance *instance,
                }
        } else
                wait_event(instance->abort_cmd_wait_q,
-                               cmd->cmd_status != ENODATA);
+                               cmd->cmd_status_drv != MFI_STAT_INVALID_STATUS);
 
        cmd->sync_cmd = 0;
 
@@ -1196,7 +1184,7 @@ megasas_build_dcdb(struct megasas_instance *instance, struct scsi_cmnd *scp,
        struct megasas_pthru_frame *pthru;
 
        is_logical = MEGASAS_IS_LOGICAL(scp);
-       device_id = MEGASAS_DEV_INDEX(instance, scp);
+       device_id = MEGASAS_DEV_INDEX(scp);
        pthru = (struct megasas_pthru_frame *)cmd->frame;
 
        if (scp->sc_data_direction == PCI_DMA_TODEVICE)
@@ -1232,7 +1220,7 @@ megasas_build_dcdb(struct megasas_instance *instance, struct scsi_cmnd *scp,
        */
        if (scp->device->type == TYPE_TAPE) {
                if ((scp->request->timeout / HZ) > 0xFFFF)
-                       pthru->timeout = 0xFFFF;
+                       pthru->timeout = cpu_to_le16(0xFFFF);
                else
                        pthru->timeout = cpu_to_le16(scp->request->timeout / HZ);
        }
@@ -1294,7 +1282,7 @@ megasas_build_ldio(struct megasas_instance *instance, struct scsi_cmnd *scp,
        u16 flags = 0;
        struct megasas_io_frame *ldio;
 
-       device_id = MEGASAS_DEV_INDEX(instance, scp);
+       device_id = MEGASAS_DEV_INDEX(scp);
        ldio = (struct megasas_io_frame *)cmd->frame;
 
        if (scp->sc_data_direction == PCI_DMA_TODEVICE)
@@ -1698,7 +1686,7 @@ static int megasas_slave_alloc(struct scsi_device *sdev)
 * @instance:                           Adapter soft state
 *
 */
-void megasas_complete_outstanding_ioctls(struct megasas_instance *instance)
+static void megasas_complete_outstanding_ioctls(struct megasas_instance *instance)
 {
        int i;
        struct megasas_cmd *cmd_mfi;
@@ -1922,22 +1910,24 @@ static int megasas_get_ld_vf_affiliation_111(struct megasas_instance *instance,
        memset(dcmd->mbox.b, 0, MFI_MBOX_SIZE);
 
        dcmd->cmd = MFI_CMD_DCMD;
-       dcmd->cmd_status = 0xFF;
+       dcmd->cmd_status = MFI_STAT_INVALID_STATUS;
        dcmd->sge_count = 1;
-       dcmd->flags = MFI_FRAME_DIR_BOTH;
+       dcmd->flags = cpu_to_le16(MFI_FRAME_DIR_BOTH);
        dcmd->timeout = 0;
        dcmd->pad_0 = 0;
-       dcmd->data_xfer_len = sizeof(struct MR_LD_VF_AFFILIATION_111);
-       dcmd->opcode = MR_DCMD_LD_VF_MAP_GET_ALL_LDS_111;
+       dcmd->data_xfer_len =
+               cpu_to_le32(sizeof(struct MR_LD_VF_AFFILIATION_111));
+       dcmd->opcode = cpu_to_le32(MR_DCMD_LD_VF_MAP_GET_ALL_LDS_111);
 
        if (initial)
                dcmd->sgl.sge32[0].phys_addr =
-                       instance->vf_affiliation_111_h;
+                       cpu_to_le32(instance->vf_affiliation_111_h);
        else
-               dcmd->sgl.sge32[0].phys_addr = new_affiliation_111_h;
+               dcmd->sgl.sge32[0].phys_addr =
+                       cpu_to_le32(new_affiliation_111_h);
 
-       dcmd->sgl.sge32[0].length =
-               sizeof(struct MR_LD_VF_AFFILIATION_111);
+       dcmd->sgl.sge32[0].length = cpu_to_le32(
+               sizeof(struct MR_LD_VF_AFFILIATION_111));
 
        printk(KERN_WARNING "megasas: SR-IOV: Getting LD/VF affiliation for "
               "scsi%d\n", instance->host->host_no);
@@ -1976,11 +1966,7 @@ out:
                                    new_affiliation_111_h);
        }
 
-       if (instance->ctrl_context && cmd->mpt_pthr_cmd_blocked)
-               megasas_return_mfi_mpt_pthr(instance, cmd,
-                       cmd->mpt_pthr_cmd_blocked);
-       else
-               megasas_return_cmd(instance, cmd);
+       megasas_return_cmd(instance, cmd);
 
        return retval;
 }
@@ -2037,22 +2023,24 @@ static int megasas_get_ld_vf_affiliation_12(struct megasas_instance *instance,
        memset(dcmd->mbox.b, 0, MFI_MBOX_SIZE);
 
        dcmd->cmd = MFI_CMD_DCMD;
-       dcmd->cmd_status = 0xFF;
+       dcmd->cmd_status = MFI_STAT_INVALID_STATUS;
        dcmd->sge_count = 1;
-       dcmd->flags = MFI_FRAME_DIR_BOTH;
+       dcmd->flags = cpu_to_le16(MFI_FRAME_DIR_BOTH);
        dcmd->timeout = 0;
        dcmd->pad_0 = 0;
-       dcmd->data_xfer_len = (MAX_LOGICAL_DRIVES + 1) *
-               sizeof(struct MR_LD_VF_AFFILIATION);
-       dcmd->opcode = MR_DCMD_LD_VF_MAP_GET_ALL_LDS;
+       dcmd->data_xfer_len = cpu_to_le32((MAX_LOGICAL_DRIVES + 1) *
+               sizeof(struct MR_LD_VF_AFFILIATION));
+       dcmd->opcode = cpu_to_le32(MR_DCMD_LD_VF_MAP_GET_ALL_LDS);
 
        if (initial)
-               dcmd->sgl.sge32[0].phys_addr = instance->vf_affiliation_h;
+               dcmd->sgl.sge32[0].phys_addr =
+                       cpu_to_le32(instance->vf_affiliation_h);
        else
-               dcmd->sgl.sge32[0].phys_addr = new_affiliation_h;
+               dcmd->sgl.sge32[0].phys_addr =
+                       cpu_to_le32(new_affiliation_h);
 
-       dcmd->sgl.sge32[0].length = (MAX_LOGICAL_DRIVES + 1) *
-               sizeof(struct MR_LD_VF_AFFILIATION);
+       dcmd->sgl.sge32[0].length = cpu_to_le32((MAX_LOGICAL_DRIVES + 1) *
+               sizeof(struct MR_LD_VF_AFFILIATION));
 
        printk(KERN_WARNING "megasas: SR-IOV: Getting LD/VF affiliation for "
               "scsi%d\n", instance->host->host_no);
@@ -2147,11 +2135,7 @@ out:
                                    (MAX_LOGICAL_DRIVES + 1) *
                                    sizeof(struct MR_LD_VF_AFFILIATION),
                                    new_affiliation, new_affiliation_h);
-       if (instance->ctrl_context && cmd->mpt_pthr_cmd_blocked)
-               megasas_return_mfi_mpt_pthr(instance, cmd,
-                       cmd->mpt_pthr_cmd_blocked);
-       else
-               megasas_return_cmd(instance, cmd);
+       megasas_return_cmd(instance, cmd);
 
        return retval;
 }
@@ -2204,39 +2188,33 @@ int megasas_sriov_start_heartbeat(struct megasas_instance *instance,
 
        memset(dcmd->mbox.b, 0, MFI_MBOX_SIZE);
 
-       dcmd->mbox.s[0] = sizeof(struct MR_CTRL_HB_HOST_MEM);
+       dcmd->mbox.s[0] = cpu_to_le16(sizeof(struct MR_CTRL_HB_HOST_MEM));
        dcmd->cmd = MFI_CMD_DCMD;
-       dcmd->cmd_status = 0xFF;
+       dcmd->cmd_status = MFI_STAT_INVALID_STATUS;
        dcmd->sge_count = 1;
-       dcmd->flags = MFI_FRAME_DIR_BOTH;
+       dcmd->flags = cpu_to_le16(MFI_FRAME_DIR_BOTH);
        dcmd->timeout = 0;
        dcmd->pad_0 = 0;
-       dcmd->data_xfer_len = sizeof(struct MR_CTRL_HB_HOST_MEM);
-       dcmd->opcode = MR_DCMD_CTRL_SHARED_HOST_MEM_ALLOC;
-       dcmd->sgl.sge32[0].phys_addr = instance->hb_host_mem_h;
-       dcmd->sgl.sge32[0].length = sizeof(struct MR_CTRL_HB_HOST_MEM);
+       dcmd->data_xfer_len = cpu_to_le32(sizeof(struct MR_CTRL_HB_HOST_MEM));
+       dcmd->opcode = cpu_to_le32(MR_DCMD_CTRL_SHARED_HOST_MEM_ALLOC);
+       dcmd->sgl.sge32[0].phys_addr = cpu_to_le32(instance->hb_host_mem_h);
+       dcmd->sgl.sge32[0].length = cpu_to_le32(sizeof(struct MR_CTRL_HB_HOST_MEM));
 
        printk(KERN_WARNING "megasas: SR-IOV: Starting heartbeat for scsi%d\n",
               instance->host->host_no);
 
-       if (!megasas_issue_polled(instance, cmd)) {
-               retval = 0;
-       } else {
-               printk(KERN_WARNING "megasas: SR-IOV: MR_DCMD_CTRL_SHARED_HOST"
-                      "_MEM_ALLOC DCMD timed out for scsi%d\n",
-                      instance->host->host_no);
-               retval = 1;
-               goto out;
-       }
-
+       if (instance->ctrl_context && !instance->mask_interrupts)
+               retval = megasas_issue_blocked_cmd(instance, cmd,
+                       MEGASAS_ROUTINE_WAIT_TIME_VF);
+       else
+               retval = megasas_issue_polled(instance, cmd);
 
-       if (dcmd->cmd_status) {
-               printk(KERN_WARNING "megasas: SR-IOV: MR_DCMD_CTRL_SHARED_HOST"
-                      "_MEM_ALLOC DCMD failed with status 0x%x for scsi%d\n",
-                      dcmd->cmd_status,
-                      instance->host->host_no);
+       if (retval) {
+               dev_warn(&instance->pdev->dev, "SR-IOV: MR_DCMD_CTRL_SHARED_HOST"
+                       "_MEM_ALLOC DCMD %s for scsi%d\n",
+                       (dcmd->cmd_status == MFI_STAT_INVALID_STATUS) ?
+                       "timed out" : "failed", instance->host->host_no);
                retval = 1;
-               goto out;
        }
 
 out:
@@ -2332,7 +2310,7 @@ static int megasas_wait_for_outstanding(struct megasas_instance *instance)
                                                "reset queue\n",
                                                reset_cmd);
 
-                               reset_cmd->cmd_status = ENODATA;
+                               reset_cmd->cmd_status_drv = MFI_STAT_INVALID_STATUS;
                                instance->instancet->fire_cmd(instance,
                                                reset_cmd->frame_phys_addr,
                                                0, instance->reg_set);
@@ -2612,11 +2590,7 @@ megasas_service_aen(struct megasas_instance *instance, struct megasas_cmd *cmd)
 
        instance->aen_cmd = NULL;
 
-       if (instance->ctrl_context && cmd->mpt_pthr_cmd_blocked)
-               megasas_return_mfi_mpt_pthr(instance, cmd,
-                       cmd->mpt_pthr_cmd_blocked);
-       else
-               megasas_return_cmd(instance, cmd);
+       megasas_return_cmd(instance, cmd);
 
        if ((instance->unload == 0) &&
                ((instance->issuepend_done == 1))) {
@@ -2786,7 +2760,7 @@ struct device_attribute *megaraid_host_attrs[] = {
 static struct scsi_host_template megasas_template = {
 
        .module = THIS_MODULE,
-       .name = "LSI SAS based MegaRAID driver",
+       .name = "Avago SAS based MegaRAID driver",
        .proc_name = "megaraid_sas",
        .slave_configure = megasas_slave_configure,
        .slave_alloc = megasas_slave_alloc,
@@ -2815,11 +2789,7 @@ static void
 megasas_complete_int_cmd(struct megasas_instance *instance,
                         struct megasas_cmd *cmd)
 {
-       cmd->cmd_status = cmd->frame->io.cmd_status;
-
-       if (cmd->cmd_status == ENODATA) {
-               cmd->cmd_status = 0;
-       }
+       cmd->cmd_status_drv = cmd->frame->io.cmd_status;
        wake_up(&instance->int_cmd_wait_q);
 }
 
@@ -2838,7 +2808,7 @@ megasas_complete_abort(struct megasas_instance *instance,
 {
        if (cmd->sync_cmd) {
                cmd->sync_cmd = 0;
-               cmd->cmd_status = 0;
+               cmd->cmd_status_drv = 0;
                wake_up(&instance->abort_cmd_wait_q);
        }
 
@@ -2978,8 +2948,7 @@ megasas_complete_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd,
                                               "failed, status = 0x%x.\n",
                                               cmd->frame->hdr.cmd_status);
                                else {
-                                       megasas_return_mfi_mpt_pthr(instance,
-                                               cmd, cmd->mpt_pthr_cmd_blocked);
+                                       megasas_return_cmd(instance, cmd);
                                        spin_unlock_irqrestore(
                                                instance->host->host_lock,
                                                flags);
@@ -2987,8 +2956,7 @@ megasas_complete_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd,
                                }
                        } else
                                instance->map_id++;
-                       megasas_return_mfi_mpt_pthr(instance, cmd,
-                               cmd->mpt_pthr_cmd_blocked);
+                       megasas_return_cmd(instance, cmd);
 
                        /*
                         * Set fast path IO to ZERO.
@@ -3086,7 +3054,7 @@ megasas_issue_pending_cmds_again(struct megasas_instance *instance)
                        printk(KERN_NOTICE "megasas: %p synchronous cmd"
                                                "on the internal reset queue,"
                                                "issue it again.\n", cmd);
-                       cmd->cmd_status = ENODATA;
+                       cmd->cmd_status_drv = MFI_STAT_INVALID_STATUS;
                        instance->instancet->fire_cmd(instance,
                                                        cmd->frame_phys_addr ,
                                                        0, instance->reg_set);
@@ -3766,7 +3734,6 @@ int megasas_alloc_cmds(struct megasas_instance *instance)
                cmd = instance->cmd_list[i];
                memset(cmd, 0, sizeof(struct megasas_cmd));
                cmd->index = i;
-               atomic_set(&cmd->mfi_mpt_pthr, MFI_LIST_ADDED);
                cmd->scmd = NULL;
                cmd->instance = instance;
 
@@ -3827,7 +3794,7 @@ megasas_get_pd_list(struct megasas_instance *instance)
        dcmd->mbox.b[0] = MR_PD_QUERY_TYPE_EXPOSED_TO_HOST;
        dcmd->mbox.b[1] = 0;
        dcmd->cmd = MFI_CMD_DCMD;
-       dcmd->cmd_status = 0xFF;
+       dcmd->cmd_status = MFI_STAT_INVALID_STATUS;
        dcmd->sge_count = 1;
        dcmd->flags = cpu_to_le16(MFI_FRAME_DIR_READ);
        dcmd->timeout = 0;
@@ -3874,11 +3841,7 @@ megasas_get_pd_list(struct megasas_instance *instance)
                                MEGASAS_MAX_PD * sizeof(struct MR_PD_LIST),
                                ci, ci_h);
 
-       if (instance->ctrl_context && cmd->mpt_pthr_cmd_blocked)
-               megasas_return_mfi_mpt_pthr(instance, cmd,
-                       cmd->mpt_pthr_cmd_blocked);
-       else
-               megasas_return_cmd(instance, cmd);
+       megasas_return_cmd(instance, cmd);
 
        return ret;
 }
@@ -3927,7 +3890,7 @@ megasas_get_ld_list(struct megasas_instance *instance)
        if (instance->supportmax256vd)
                dcmd->mbox.b[0] = 1;
        dcmd->cmd = MFI_CMD_DCMD;
-       dcmd->cmd_status = 0xFF;
+       dcmd->cmd_status = MFI_STAT_INVALID_STATUS;
        dcmd->sge_count = 1;
        dcmd->flags = cpu_to_le16(MFI_FRAME_DIR_READ);
        dcmd->timeout = 0;
@@ -3965,11 +3928,7 @@ megasas_get_ld_list(struct megasas_instance *instance)
                                ci,
                                ci_h);
 
-       if (instance->ctrl_context && cmd->mpt_pthr_cmd_blocked)
-               megasas_return_mfi_mpt_pthr(instance, cmd,
-                       cmd->mpt_pthr_cmd_blocked);
-       else
-               megasas_return_cmd(instance, cmd);
+       megasas_return_cmd(instance, cmd);
        return ret;
 }
 
@@ -4020,7 +3979,7 @@ megasas_ld_list_query(struct megasas_instance *instance, u8 query_type)
                dcmd->mbox.b[2] = 1;
 
        dcmd->cmd = MFI_CMD_DCMD;
-       dcmd->cmd_status = 0xFF;
+       dcmd->cmd_status = MFI_STAT_INVALID_STATUS;
        dcmd->sge_count = 1;
        dcmd->flags = cpu_to_le16(MFI_FRAME_DIR_READ);
        dcmd->timeout = 0;
@@ -4050,11 +4009,7 @@ megasas_ld_list_query(struct megasas_instance *instance, u8 query_type)
        pci_free_consistent(instance->pdev, sizeof(struct MR_LD_TARGETID_LIST),
                            ci, ci_h);
 
-       if (instance->ctrl_context && cmd->mpt_pthr_cmd_blocked)
-               megasas_return_mfi_mpt_pthr(instance, cmd,
-                       cmd->mpt_pthr_cmd_blocked);
-       else
-               megasas_return_cmd(instance, cmd);
+       megasas_return_cmd(instance, cmd);
 
        return ret;
 }
@@ -4091,12 +4046,11 @@ static void megasas_update_ext_vd_details(struct megasas_instance *instance)
                instance->fw_supported_vd_count = MAX_LOGICAL_DRIVES;
                instance->fw_supported_pd_count = MAX_PHYSICAL_DEVICES;
        }
-       dev_info(&instance->pdev->dev, "Firmware supports %d VD %d PD\n",
-               instance->fw_supported_vd_count,
-               instance->fw_supported_pd_count);
-       dev_info(&instance->pdev->dev, "Driver supports %d VD  %d PD\n",
-               instance->drv_supported_vd_count,
-               instance->drv_supported_pd_count);
+
+       dev_info(&instance->pdev->dev,
+               "firmware type\t: %s\n",
+               instance->supportmax256vd ? "Extended VD(240 VD)firmware" :
+               "Legacy(64 VD) firmware");
 
        old_map_sz =  sizeof(struct MR_FW_RAID_MAP) +
                                (sizeof(struct MR_LD_SPAN_MAP) *
@@ -4158,7 +4112,7 @@ megasas_get_ctrl_info(struct megasas_instance *instance)
        memset(dcmd->mbox.b, 0, MFI_MBOX_SIZE);
 
        dcmd->cmd = MFI_CMD_DCMD;
-       dcmd->cmd_status = 0xFF;
+       dcmd->cmd_status = MFI_STAT_INVALID_STATUS;
        dcmd->sge_count = 1;
        dcmd->flags = cpu_to_le16(MFI_FRAME_DIR_READ);
        dcmd->timeout = 0;
@@ -4181,16 +4135,17 @@ megasas_get_ctrl_info(struct megasas_instance *instance)
                le32_to_cpus((u32 *)&ctrl_info->adapterOperations2);
                le32_to_cpus((u32 *)&ctrl_info->adapterOperations3);
                megasas_update_ext_vd_details(instance);
+               instance->is_imr = (ctrl_info->memory_size ? 0 : 1);
+               dev_info(&instance->pdev->dev,
+                               "controller type\t: %s(%dMB)\n",
+                               instance->is_imr ? "iMR" : "MR",
+                               le16_to_cpu(ctrl_info->memory_size));
        }
 
        pci_free_consistent(instance->pdev, sizeof(struct megasas_ctrl_info),
                            ci, ci_h);
 
-       if (instance->ctrl_context && cmd->mpt_pthr_cmd_blocked)
-               megasas_return_mfi_mpt_pthr(instance, cmd,
-                       cmd->mpt_pthr_cmd_blocked);
-       else
-               megasas_return_cmd(instance, cmd);
+       megasas_return_cmd(instance, cmd);
        return ret;
 }
 
@@ -4229,7 +4184,7 @@ int megasas_set_crash_dump_params(struct megasas_instance *instance,
        memset(dcmd->mbox.b, 0, MFI_MBOX_SIZE);
        dcmd->mbox.b[0] = crash_buf_state;
        dcmd->cmd = MFI_CMD_DCMD;
-       dcmd->cmd_status = 0xFF;
+       dcmd->cmd_status = MFI_STAT_INVALID_STATUS;
        dcmd->sge_count = 1;
        dcmd->flags = cpu_to_le16(MFI_FRAME_DIR_NONE);
        dcmd->timeout = 0;
@@ -4245,11 +4200,7 @@ int megasas_set_crash_dump_params(struct megasas_instance *instance,
        else
                ret = megasas_issue_polled(instance, cmd);
 
-       if (instance->ctrl_context && cmd->mpt_pthr_cmd_blocked)
-               megasas_return_mfi_mpt_pthr(instance, cmd,
-                       cmd->mpt_pthr_cmd_blocked);
-       else
-               megasas_return_cmd(instance, cmd);
+       megasas_return_cmd(instance, cmd);
        return ret;
 }
 
@@ -4262,7 +4213,7 @@ int megasas_set_crash_dump_params(struct megasas_instance *instance,
 static int
 megasas_issue_init_mfi(struct megasas_instance *instance)
 {
-       u32 context;
+       __le32 context;
 
        struct megasas_cmd *cmd;
 
@@ -4300,7 +4251,7 @@ megasas_issue_init_mfi(struct megasas_instance *instance)
        initq_info->consumer_index_phys_addr_lo = cpu_to_le32(instance->consumer_h);
 
        init_frame->cmd = MFI_CMD_INIT;
-       init_frame->cmd_status = 0xFF;
+       init_frame->cmd_status = MFI_STAT_INVALID_STATUS;
        init_frame->queue_info_new_phys_addr_lo =
                cpu_to_le32(lower_32_bits(initq_info_h));
        init_frame->queue_info_new_phys_addr_hi =
@@ -4353,6 +4304,21 @@ megasas_init_adapter_mfi(struct megasas_instance *instance)
        instance->max_mfi_cmds = instance->max_fw_cmds;
        instance->max_num_sge = (instance->instancet->read_fw_status_reg(reg_set) & 0xFF0000) >>
                                        0x10;
+       /*
+        * For MFI skinny adapters, MEGASAS_SKINNY_INT_CMDS commands
+        * are reserved for IOCTL + driver's internal DCMDs.
+        */
+       if ((instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0073SKINNY) ||
+               (instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0071SKINNY)) {
+               instance->max_scsi_cmds = (instance->max_fw_cmds -
+                       MEGASAS_SKINNY_INT_CMDS);
+               sema_init(&instance->ioctl_sem, MEGASAS_SKINNY_INT_CMDS);
+       } else {
+               instance->max_scsi_cmds = (instance->max_fw_cmds -
+                       MEGASAS_INT_CMDS);
+               sema_init(&instance->ioctl_sem, (MEGASAS_MFI_IOCTL_CMDS));
+       }
+
        /*
         * Create a pool of commands
         */
@@ -4414,6 +4380,107 @@ fail_alloc_cmds:
        return 1;
 }
 
+/*
+ * megasas_setup_irqs_msix -           register legacy interrupts.
+ * @instance:                          Adapter soft state
+ *
+ * Do not enable interrupt, only setup ISRs.
+ *
+ * Return 0 on success.
+ */
+static int
+megasas_setup_irqs_ioapic(struct megasas_instance *instance)
+{
+       struct pci_dev *pdev;
+
+       pdev = instance->pdev;
+       instance->irq_context[0].instance = instance;
+       instance->irq_context[0].MSIxIndex = 0;
+       if (request_irq(pdev->irq, instance->instancet->service_isr,
+               IRQF_SHARED, "megasas", &instance->irq_context[0])) {
+               dev_err(&instance->pdev->dev,
+                               "Failed to register IRQ from %s %d\n",
+                               __func__, __LINE__);
+               return -1;
+       }
+       return 0;
+}
+
+/**
+ * megasas_setup_irqs_msix -           register MSI-x interrupts.
+ * @instance:                          Adapter soft state
+ * @is_probe:                          Driver probe check
+ *
+ * Do not enable interrupt, only setup ISRs.
+ *
+ * Return 0 on success.
+ */
+static int
+megasas_setup_irqs_msix(struct megasas_instance *instance, u8 is_probe)
+{
+       int i, j, cpu;
+       struct pci_dev *pdev;
+
+       pdev = instance->pdev;
+
+       /* Try MSI-x */
+       cpu = cpumask_first(cpu_online_mask);
+       for (i = 0; i < instance->msix_vectors; i++) {
+               instance->irq_context[i].instance = instance;
+               instance->irq_context[i].MSIxIndex = i;
+               if (request_irq(instance->msixentry[i].vector,
+                       instance->instancet->service_isr, 0, "megasas",
+                       &instance->irq_context[i])) {
+                       dev_err(&instance->pdev->dev,
+                               "Failed to register IRQ for vector %d.\n", i);
+                       for (j = 0; j < i; j++) {
+                               if (smp_affinity_enable)
+                                       irq_set_affinity_hint(
+                                               instance->msixentry[j].vector, NULL);
+                               free_irq(instance->msixentry[j].vector,
+                                       &instance->irq_context[j]);
+                       }
+                       /* Retry irq register for IO_APIC*/
+                       instance->msix_vectors = 0;
+                       if (is_probe)
+                               return megasas_setup_irqs_ioapic(instance);
+                       else
+                               return -1;
+               }
+               if (smp_affinity_enable) {
+                       if (irq_set_affinity_hint(instance->msixentry[i].vector,
+                               get_cpu_mask(cpu)))
+                               dev_err(&instance->pdev->dev,
+                                       "Failed to set affinity hint"
+                                       " for cpu %d\n", cpu);
+                       cpu = cpumask_next(cpu, cpu_online_mask);
+               }
+       }
+       return 0;
+}
+
+/*
+ * megasas_destroy_irqs-               unregister interrupts.
+ * @instance:                          Adapter soft state
+ * return:                             void
+ */
+static void
+megasas_destroy_irqs(struct megasas_instance *instance) {
+
+       int i;
+
+       if (instance->msix_vectors)
+               for (i = 0; i < instance->msix_vectors; i++) {
+                       if (smp_affinity_enable)
+                               irq_set_affinity_hint(
+                                       instance->msixentry[i].vector, NULL);
+                       free_irq(instance->msixentry[i].vector,
+                                &instance->irq_context[i]);
+               }
+       else
+               free_irq(instance->pdev->irq, &instance->irq_context[0]);
+}
+
 /**
  * megasas_init_fw -   Initializes the FW
  * @instance:          Adapter soft state
@@ -4499,7 +4566,7 @@ static int megasas_init_fw(struct megasas_instance *instance)
         * It is used for all MPT based Adapters.
         */
        instance->reply_post_host_index_addr[0] =
-               (u32 *)((u8 *)instance->reg_set +
+               (u32 __iomem *)((u8 __iomem *)instance->reg_set +
                MPI2_REPLY_POST_HOST_INDEX_OFFSET);
 
        /* Check if MSI-X is supported while in ready state */
@@ -4531,7 +4598,8 @@ static int megasas_init_fw(struct megasas_instance *instance)
                         */
                        for (loop = 1; loop < MR_MAX_MSIX_REG_ARRAY; loop++) {
                                instance->reply_post_host_index_addr[loop] =
-                                       (u32 *)((u8 *)instance->reg_set +
+                                       (u32 __iomem *)
+                                       ((u8 __iomem *)instance->reg_set +
                                        MPI2_SUP_REPLY_POST_HOST_INDEX_OFFSET
                                        + (loop * 0x10));
                        }
@@ -4551,14 +4619,19 @@ static int megasas_init_fw(struct megasas_instance *instance)
                        instance->msix_vectors = i;
                else
                        instance->msix_vectors = 0;
-
-               dev_info(&instance->pdev->dev, "[scsi%d]: FW supports"
-                       "<%d> MSIX vector,Online CPUs: <%d>,"
-                       "Current MSIX <%d>\n", instance->host->host_no,
-                       fw_msix_count, (unsigned int)num_online_cpus(),
-                       instance->msix_vectors);
        }
 
+       dev_info(&instance->pdev->dev,
+               "firmware supports msix\t: (%d)", fw_msix_count);
+       dev_info(&instance->pdev->dev,
+               "current msix/online cpus\t: (%d/%d)\n",
+               instance->msix_vectors, (unsigned int)num_online_cpus());
+
+       if (instance->msix_vectors ?
+               megasas_setup_irqs_msix(instance, 1) :
+               megasas_setup_irqs_ioapic(instance))
+               goto fail_setup_irqs;
+
        instance->ctrl_info = kzalloc(sizeof(struct megasas_ctrl_info),
                                GFP_KERNEL);
        if (instance->ctrl_info == NULL)
@@ -4574,6 +4647,11 @@ static int megasas_init_fw(struct megasas_instance *instance)
        if (instance->instancet->init_adapter(instance))
                goto fail_init_adapter;
 
+       tasklet_init(&instance->isr_tasklet, instance->instancet->tasklet,
+               (unsigned long)instance);
+
+       instance->instancet->enable_intr(instance);
+
        printk(KERN_ERR "megasas: INIT adapter done\n");
 
        /** for passthrough
@@ -4584,7 +4662,7 @@ static int megasas_init_fw(struct megasas_instance *instance)
                (MEGASAS_MAX_PD * sizeof(struct megasas_pd_list)));
        if (megasas_get_pd_list(instance) < 0) {
                printk(KERN_ERR "megasas: failed to get PD list\n");
-               goto fail_init_adapter;
+               goto fail_get_pd_list;
        }
 
        memset(instance->ld_ids, 0xff, MEGASAS_MAX_LD_IDS);
@@ -4610,17 +4688,6 @@ static int megasas_init_fw(struct megasas_instance *instance)
 
        tmp_sectors = min_t(u32, max_sectors_1 , max_sectors_2);
 
-       /*Check whether controller is iMR or MR */
-       if (ctrl_info->memory_size) {
-               instance->is_imr = 0;
-               dev_info(&instance->pdev->dev, "Controller type: MR,"
-                       "Memory size is: %dMB\n",
-                       le16_to_cpu(ctrl_info->memory_size));
-       } else {
-               instance->is_imr = 1;
-               dev_info(&instance->pdev->dev,
-                       "Controller type: iMR\n");
-       }
        instance->disableOnlineCtrlReset =
        ctrl_info->properties.OnOffProperties.disableOnlineCtrlReset;
        instance->mpio = ctrl_info->adapterOperations2.mpio;
@@ -4628,9 +4695,6 @@ static int megasas_init_fw(struct megasas_instance *instance)
                ctrl_info->adapterOperations2.supportUnevenSpans;
        if (instance->UnevenSpanSupport) {
                struct fusion_context *fusion = instance->ctrl_context;
-
-               dev_info(&instance->pdev->dev, "FW supports: "
-               "UnevenSpanSupport=%x\n", instance->UnevenSpanSupport);
                if (MR_ValidateMapInfo(instance))
                        fusion->fast_path_io = 1;
                else
@@ -4657,13 +4721,11 @@ static int megasas_init_fw(struct megasas_instance *instance)
        instance->crash_dump_drv_support =
                (instance->crash_dump_fw_support &&
                instance->crash_dump_buf);
-       if (instance->crash_dump_drv_support) {
-               dev_info(&instance->pdev->dev, "Firmware Crash dump "
-                       "feature is supported\n");
+       if (instance->crash_dump_drv_support)
                megasas_set_crash_dump_params(instance,
                        MR_CRASH_BUF_TURN_OFF);
 
-       else {
+       else {
                if (instance->crash_dump_buf)
                        pci_free_consistent(instance->pdev,
                                CRASH_DMA_BUF_SIZE,
@@ -4674,37 +4736,28 @@ static int megasas_init_fw(struct megasas_instance *instance)
 
        instance->secure_jbod_support =
                ctrl_info->adapterOperations3.supportSecurityonJBOD;
-       if (instance->secure_jbod_support)
-               dev_info(&instance->pdev->dev, "Firmware supports Secure JBOD\n");
+
+       dev_info(&instance->pdev->dev,
+               "pci id\t\t: (0x%04x)/(0x%04x)/(0x%04x)/(0x%04x)\n",
+               le16_to_cpu(ctrl_info->pci.vendor_id),
+               le16_to_cpu(ctrl_info->pci.device_id),
+               le16_to_cpu(ctrl_info->pci.sub_vendor_id),
+               le16_to_cpu(ctrl_info->pci.sub_device_id));
+       dev_info(&instance->pdev->dev, "unevenspan support      : %s\n",
+               instance->UnevenSpanSupport ? "yes" : "no");
+       dev_info(&instance->pdev->dev, "disable ocr             : %s\n",
+               instance->disableOnlineCtrlReset ? "yes" : "no");
+       dev_info(&instance->pdev->dev, "firmware crash dump     : %s\n",
+               instance->crash_dump_drv_support ? "yes" : "no");
+       dev_info(&instance->pdev->dev, "secure jbod             : %s\n",
+               instance->secure_jbod_support ? "yes" : "no");
+
+
        instance->max_sectors_per_req = instance->max_num_sge *
                                                PAGE_SIZE / 512;
        if (tmp_sectors && (instance->max_sectors_per_req > tmp_sectors))
                instance->max_sectors_per_req = tmp_sectors;
 
-       /*
-        * 1. For fusion adapters, 3 commands for IOCTL and 5 commands
-        *    for driver's internal DCMDs.
-        * 2. For MFI skinny adapters, 5 commands for IOCTL + driver's
-        *    internal DCMDs.
-        * 3. For rest of MFI adapters, 27 commands reserved for IOCTLs
-        *    and 5 commands for drivers's internal DCMD.
-        */
-       if (instance->ctrl_context) {
-               instance->max_scsi_cmds = instance->max_fw_cmds -
-                                       (MEGASAS_FUSION_INTERNAL_CMDS +
-                                       MEGASAS_FUSION_IOCTL_CMDS);
-               sema_init(&instance->ioctl_sem, MEGASAS_FUSION_IOCTL_CMDS);
-       } else if ((instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0073SKINNY) ||
-               (instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0071SKINNY)) {
-               instance->max_scsi_cmds = instance->max_fw_cmds -
-                                               MEGASAS_SKINNY_INT_CMDS;
-               sema_init(&instance->ioctl_sem, MEGASAS_SKINNY_INT_CMDS);
-       } else {
-               instance->max_scsi_cmds = instance->max_fw_cmds -
-                                               MEGASAS_INT_CMDS;
-               sema_init(&instance->ioctl_sem, (MEGASAS_INT_CMDS - 5));
-       }
-
        /* Check for valid throttlequeuedepth module parameter */
        if (throttlequeuedepth &&
                        throttlequeuedepth <= instance->max_scsi_cmds)
@@ -4713,12 +4766,6 @@ static int megasas_init_fw(struct megasas_instance *instance)
                instance->throttlequeuedepth =
                                MEGASAS_THROTTLE_QUEUE_DEPTH;
 
-        /*
-       * Setup tasklet for cmd completion
-       */
-
-       tasklet_init(&instance->isr_tasklet, instance->instancet->tasklet,
-               (unsigned long)instance);
 
        /* Launch SR-IOV heartbeat timer */
        if (instance->requestorId) {
@@ -4733,7 +4780,14 @@ static int megasas_init_fw(struct megasas_instance *instance)
 
        return 0;
 
+fail_get_pd_list:
+       instance->instancet->disable_intr(instance);
 fail_init_adapter:
+       megasas_destroy_irqs(instance);
+fail_setup_irqs:
+       if (instance->msix_vectors)
+               pci_disable_msix(instance->pdev);
+       instance->msix_vectors = 0;
 fail_ready_state:
        kfree(instance->ctrl_info);
        instance->ctrl_info = NULL;
@@ -4747,7 +4801,7 @@ fail_ready_state:
 
 /**
  * megasas_release_mfi -       Reverses the FW initialization
- * @intance:                   Adapter soft state
+ * @instance:                  Adapter soft state
  */
 static void megasas_release_mfi(struct megasas_instance *instance)
 {
@@ -4822,21 +4876,17 @@ megasas_get_seq_num(struct megasas_instance *instance,
                /*
                 * Copy the data back into callers buffer
                 */
-               eli->newest_seq_num = le32_to_cpu(el_info->newest_seq_num);
-               eli->oldest_seq_num = le32_to_cpu(el_info->oldest_seq_num);
-               eli->clear_seq_num = le32_to_cpu(el_info->clear_seq_num);
-               eli->shutdown_seq_num = le32_to_cpu(el_info->shutdown_seq_num);
-               eli->boot_seq_num = le32_to_cpu(el_info->boot_seq_num);
+               eli->newest_seq_num = el_info->newest_seq_num;
+               eli->oldest_seq_num = el_info->oldest_seq_num;
+               eli->clear_seq_num = el_info->clear_seq_num;
+               eli->shutdown_seq_num = el_info->shutdown_seq_num;
+               eli->boot_seq_num = el_info->boot_seq_num;
        }
 
        pci_free_consistent(instance->pdev, sizeof(struct megasas_evt_log_info),
                            el_info, el_info_h);
 
-       if (instance->ctrl_context && cmd->mpt_pthr_cmd_blocked)
-               megasas_return_mfi_mpt_pthr(instance, cmd,
-                       cmd->mpt_pthr_cmd_blocked);
-       else
-               megasas_return_cmd(instance, cmd);
+       megasas_return_cmd(instance, cmd);
 
        return 0;
 }
@@ -4877,8 +4927,8 @@ megasas_register_aen(struct megasas_instance *instance, u32 seq_num,
 
        if (instance->aen_cmd) {
 
-               prev_aen.word = instance->aen_cmd->frame->dcmd.mbox.w[1];
-               prev_aen.members.locale = le16_to_cpu(prev_aen.members.locale);
+               prev_aen.word =
+                       le32_to_cpu(instance->aen_cmd->frame->dcmd.mbox.w[1]);
 
                /*
                 * A class whose enum value is smaller is inclusive of all
@@ -4990,7 +5040,7 @@ static int megasas_start_aen(struct megasas_instance *instance)
        class_locale.members.class = MR_EVT_CLASS_DEBUG;
 
        return megasas_register_aen(instance,
-                       eli.newest_seq_num + 1,
+                       le32_to_cpu(eli.newest_seq_num) + 1,
                        class_locale.word);
 }
 
@@ -5001,6 +5051,7 @@ static int megasas_start_aen(struct megasas_instance *instance)
 static int megasas_io_attach(struct megasas_instance *instance)
 {
        struct Scsi_Host *host = instance->host;
+       u32             error;
 
        /*
         * Export parameters required by SCSI mid-layer
@@ -5050,12 +5101,21 @@ static int megasas_io_attach(struct megasas_instance *instance)
                host->hostt->eh_device_reset_handler = NULL;
                host->hostt->eh_bus_reset_handler = NULL;
        }
+       error = scsi_init_shared_tag_map(host, host->can_queue);
+       if (error) {
+               dev_err(&instance->pdev->dev,
+                       "Failed to shared tag from %s %d\n",
+                       __func__, __LINE__);
+               return -ENODEV;
+       }
 
        /*
         * Notify the mid-layer about the new controller
         */
        if (scsi_add_host(host, &instance->pdev->dev)) {
-               printk(KERN_DEBUG "megasas: scsi_add_host failed\n");
+               dev_err(&instance->pdev->dev,
+                       "Failed to add host from %s %d\n",
+                       __func__, __LINE__);
                return -ENODEV;
        }
 
@@ -5106,7 +5166,7 @@ fail_set_dma_mask:
 static int megasas_probe_one(struct pci_dev *pdev,
                             const struct pci_device_id *id)
 {
-       int rval, pos, i, j, cpu;
+       int rval, pos;
        struct Scsi_Host *host;
        struct megasas_instance *instance;
        u16 control = 0;
@@ -5128,16 +5188,6 @@ static int megasas_probe_one(struct pci_dev *pdev,
                }
        }
 
-       /*
-        * Announce PCI information
-        */
-       printk(KERN_INFO "megasas: %#4.04x:%#4.04x:%#4.04x:%#4.04x: ",
-              pdev->vendor, pdev->device, pdev->subsystem_vendor,
-              pdev->subsystem_device);
-
-       printk("bus %d:slot %d:func %d\n",
-              pdev->bus->number, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));
-
        /*
         * PCI prepping: enable device set bus mastering and dma mask
         */
@@ -5183,8 +5233,6 @@ static int megasas_probe_one(struct pci_dev *pdev,
                fusion = instance->ctrl_context;
                memset(fusion, 0,
                        ((1 << PAGE_SHIFT) << instance->ctrl_context_pages));
-               INIT_LIST_HEAD(&fusion->cmd_pool);
-               spin_lock_init(&fusion->mpt_pool_lock);
        }
        break;
        default: /* For all other supported controllers */
@@ -5207,6 +5255,13 @@ static int megasas_probe_one(struct pci_dev *pdev,
                break;
        }
 
+       instance->system_info_buf = pci_zalloc_consistent(pdev,
+                                       sizeof(struct MR_DRV_SYSTEM_INFO),
+                                       &instance->system_info_h);
+
+       if (!instance->system_info_buf)
+               dev_info(&instance->pdev->dev, "Can't allocate system info buffer\n");
+
        /* Crash dump feature related initialisation*/
        instance->drv_buf_index = 0;
        instance->drv_buf_alloc = 0;
@@ -5315,55 +5370,6 @@ static int megasas_probe_one(struct pci_dev *pdev,
                }
        }
 
-retry_irq_register:
-       /*
-        * Register IRQ
-        */
-       if (instance->msix_vectors) {
-               cpu = cpumask_first(cpu_online_mask);
-               for (i = 0; i < instance->msix_vectors; i++) {
-                       instance->irq_context[i].instance = instance;
-                       instance->irq_context[i].MSIxIndex = i;
-                       if (request_irq(instance->msixentry[i].vector,
-                                       instance->instancet->service_isr, 0,
-                                       "megasas",
-                                       &instance->irq_context[i])) {
-                               printk(KERN_DEBUG "megasas: Failed to "
-                                      "register IRQ for vector %d.\n", i);
-                               for (j = 0; j < i; j++) {
-                                       if (smp_affinity_enable)
-                                               irq_set_affinity_hint(
-                                                       instance->msixentry[j].vector, NULL);
-                                       free_irq(
-                                               instance->msixentry[j].vector,
-                                               &instance->irq_context[j]);
-                               }
-                               /* Retry irq register for IO_APIC */
-                               instance->msix_vectors = 0;
-                               goto retry_irq_register;
-                       }
-                       if (smp_affinity_enable) {
-                               if (irq_set_affinity_hint(instance->msixentry[i].vector,
-                                       get_cpu_mask(cpu)))
-                                       dev_err(&instance->pdev->dev,
-                                               "Error setting affinity hint "
-                                               "for cpu %d\n", cpu);
-                               cpu = cpumask_next(cpu, cpu_online_mask);
-                       }
-               }
-       } else {
-               instance->irq_context[0].instance = instance;
-               instance->irq_context[0].MSIxIndex = 0;
-               if (request_irq(pdev->irq, instance->instancet->service_isr,
-                               IRQF_SHARED, "megasas",
-                               &instance->irq_context[0])) {
-                       printk(KERN_DEBUG "megasas: Failed to register IRQ\n");
-                       goto fail_irq;
-               }
-       }
-
-       instance->instancet->enable_intr(instance);
-
        /*
         * Store instance in PCI softstate
         */
@@ -5410,17 +5416,8 @@ retry_irq_register:
        megasas_mgmt_info.max_index--;
 
        instance->instancet->disable_intr(instance);
-       if (instance->msix_vectors)
-               for (i = 0; i < instance->msix_vectors; i++) {
-                       if (smp_affinity_enable)
-                               irq_set_affinity_hint(
-                                       instance->msixentry[i].vector, NULL);
-                       free_irq(instance->msixentry[i].vector,
-                                &instance->irq_context[i]);
-               }
-       else
-               free_irq(instance->pdev->irq, &instance->irq_context[0]);
-fail_irq:
+       megasas_destroy_irqs(instance);
+
        if ((instance->pdev->device == PCI_DEVICE_ID_LSI_FUSION) ||
            (instance->pdev->device == PCI_DEVICE_ID_LSI_PLASMA) ||
            (instance->pdev->device == PCI_DEVICE_ID_LSI_INVADER) ||
@@ -5428,9 +5425,9 @@ fail_irq:
                megasas_release_fusion(instance);
        else
                megasas_release_mfi(instance);
-      fail_init_mfi:
        if (instance->msix_vectors)
                pci_disable_msix(instance->pdev);
+fail_init_mfi:
       fail_alloc_dma_buf:
        if (instance->evt_detail)
                pci_free_consistent(pdev, sizeof(struct megasas_evt_detail),
@@ -5487,11 +5484,7 @@ static void megasas_flush_cache(struct megasas_instance *instance)
                dev_err(&instance->pdev->dev, "Command timedout"
                        " from %s\n", __func__);
 
-       if (instance->ctrl_context && cmd->mpt_pthr_cmd_blocked)
-               megasas_return_mfi_mpt_pthr(instance, cmd,
-                       cmd->mpt_pthr_cmd_blocked);
-       else
-               megasas_return_cmd(instance, cmd);
+       megasas_return_cmd(instance, cmd);
 
        return;
 }
@@ -5538,11 +5531,7 @@ static void megasas_shutdown_controller(struct megasas_instance *instance,
                dev_err(&instance->pdev->dev, "Command timedout"
                        "from %s\n", __func__);
 
-       if (instance->ctrl_context && cmd->mpt_pthr_cmd_blocked)
-               megasas_return_mfi_mpt_pthr(instance, cmd,
-                       cmd->mpt_pthr_cmd_blocked);
-       else
-               megasas_return_cmd(instance, cmd);
+       megasas_return_cmd(instance, cmd);
 
        return;
 }
@@ -5558,7 +5547,6 @@ megasas_suspend(struct pci_dev *pdev, pm_message_t state)
 {
        struct Scsi_Host *host;
        struct megasas_instance *instance;
-       int i;
 
        instance = pci_get_drvdata(pdev);
        host = instance->host;
@@ -5583,16 +5571,8 @@ megasas_suspend(struct pci_dev *pdev, pm_message_t state)
        pci_set_drvdata(instance->pdev, instance);
        instance->instancet->disable_intr(instance);
 
-       if (instance->msix_vectors)
-               for (i = 0; i < instance->msix_vectors; i++) {
-                       if (smp_affinity_enable)
-                               irq_set_affinity_hint(
-                                       instance->msixentry[i].vector, NULL);
-                       free_irq(instance->msixentry[i].vector,
-                                &instance->irq_context[i]);
-               }
-       else
-               free_irq(instance->pdev->irq, &instance->irq_context[0]);
+       megasas_destroy_irqs(instance);
+
        if (instance->msix_vectors)
                pci_disable_msix(instance->pdev);
 
@@ -5611,7 +5591,7 @@ megasas_suspend(struct pci_dev *pdev, pm_message_t state)
 static int
 megasas_resume(struct pci_dev *pdev)
 {
-       int rval, i, j, cpu;
+       int rval;
        struct Scsi_Host *host;
        struct megasas_instance *instance;
 
@@ -5681,50 +5661,10 @@ megasas_resume(struct pci_dev *pdev)
        tasklet_init(&instance->isr_tasklet, instance->instancet->tasklet,
                     (unsigned long)instance);
 
-       /*
-        * Register IRQ
-        */
-       if (instance->msix_vectors) {
-               cpu = cpumask_first(cpu_online_mask);
-               for (i = 0 ; i < instance->msix_vectors; i++) {
-                       instance->irq_context[i].instance = instance;
-                       instance->irq_context[i].MSIxIndex = i;
-                       if (request_irq(instance->msixentry[i].vector,
-                                       instance->instancet->service_isr, 0,
-                                       "megasas",
-                                       &instance->irq_context[i])) {
-                               printk(KERN_DEBUG "megasas: Failed to "
-                                      "register IRQ for vector %d.\n", i);
-                               for (j = 0; j < i; j++) {
-                                       if (smp_affinity_enable)
-                                               irq_set_affinity_hint(
-                                                       instance->msixentry[j].vector, NULL);
-                                       free_irq(
-                                               instance->msixentry[j].vector,
-                                               &instance->irq_context[j]);
-                               }
-                               goto fail_irq;
-                       }
-
-                       if (smp_affinity_enable) {
-                               if (irq_set_affinity_hint(instance->msixentry[i].vector,
-                                       get_cpu_mask(cpu)))
-                                       dev_err(&instance->pdev->dev, "Error "
-                                               "setting affinity hint for cpu "
-                                               "%d\n", cpu);
-                               cpu = cpumask_next(cpu, cpu_online_mask);
-                       }
-               }
-       } else {
-               instance->irq_context[0].instance = instance;
-               instance->irq_context[0].MSIxIndex = 0;
-               if (request_irq(pdev->irq, instance->instancet->service_isr,
-                               IRQF_SHARED, "megasas",
-                               &instance->irq_context[0])) {
-                       printk(KERN_DEBUG "megasas: Failed to register IRQ\n");
-                       goto fail_irq;
-               }
-       }
+       if (instance->msix_vectors ?
+                       megasas_setup_irqs_msix(instance, 0) :
+                       megasas_setup_irqs_ioapic(instance))
+               goto fail_init_mfi;
 
        /* Re-launch SR-IOV heartbeat timer */
        if (instance->requestorId) {
@@ -5733,8 +5673,10 @@ megasas_resume(struct pci_dev *pdev)
                                            &instance->sriov_heartbeat_timer,
                                            megasas_sriov_heartbeat_handler,
                                            MEGASAS_SRIOV_HEARTBEAT_INTERVAL_VF);
-               else
+               else {
                        instance->skip_heartbeat_timer_del = 1;
+                       goto fail_init_mfi;
+               }
        }
 
        instance->instancet->enable_intr(instance);
@@ -5748,7 +5690,6 @@ megasas_resume(struct pci_dev *pdev)
 
        return 0;
 
-fail_irq:
 fail_init_mfi:
        if (instance->evt_detail)
                pci_free_consistent(pdev, sizeof(struct megasas_evt_detail),
@@ -5829,16 +5770,8 @@ static void megasas_detach_one(struct pci_dev *pdev)
 
        instance->instancet->disable_intr(instance);
 
-       if (instance->msix_vectors)
-               for (i = 0; i < instance->msix_vectors; i++) {
-                       if (smp_affinity_enable)
-                               irq_set_affinity_hint(
-                                       instance->msixentry[i].vector, NULL);
-                       free_irq(instance->msixentry[i].vector,
-                                &instance->irq_context[i]);
-               }
-       else
-               free_irq(instance->pdev->irq, &instance->irq_context[0]);
+       megasas_destroy_irqs(instance);
+
        if (instance->msix_vectors)
                pci_disable_msix(instance->pdev);
 
@@ -5899,6 +5832,10 @@ static void megasas_detach_one(struct pci_dev *pdev)
                pci_free_consistent(pdev, CRASH_DMA_BUF_SIZE,
                            instance->crash_dump_buf, instance->crash_dump_h);
 
+       if (instance->system_info_buf)
+               pci_free_consistent(pdev, sizeof(struct MR_DRV_SYSTEM_INFO),
+                                   instance->system_info_buf, instance->system_info_h);
+
        scsi_host_put(host);
 
        pci_disable_device(pdev);
@@ -5912,23 +5849,14 @@ static void megasas_detach_one(struct pci_dev *pdev)
  */
 static void megasas_shutdown(struct pci_dev *pdev)
 {
-       int i;
        struct megasas_instance *instance = pci_get_drvdata(pdev);
 
        instance->unload = 1;
        megasas_flush_cache(instance);
        megasas_shutdown_controller(instance, MR_DCMD_CTRL_SHUTDOWN);
        instance->instancet->disable_intr(instance);
-       if (instance->msix_vectors)
-               for (i = 0; i < instance->msix_vectors; i++) {
-                       if (smp_affinity_enable)
-                               irq_set_affinity_hint(
-                                       instance->msixentry[i].vector, NULL);
-                       free_irq(instance->msixentry[i].vector,
-                                &instance->irq_context[i]);
-               }
-       else
-               free_irq(instance->pdev->irq, &instance->irq_context[0]);
+       megasas_destroy_irqs(instance);
+
        if (instance->msix_vectors)
                pci_disable_msix(instance->pdev);
 }
@@ -6211,11 +6139,7 @@ megasas_mgmt_fw_ioctl(struct megasas_instance *instance,
                        kbuff_arr[i] = NULL;
        }
 
-       if (instance->ctrl_context && cmd->mpt_pthr_cmd_blocked)
-               megasas_return_mfi_mpt_pthr(instance, cmd,
-                       cmd->mpt_pthr_cmd_blocked);
-       else
-               megasas_return_cmd(instance, cmd);
+       megasas_return_cmd(instance, cmd);
        return error;
 }
 
@@ -6501,6 +6425,15 @@ static ssize_t megasas_sysfs_show_version(struct device_driver *dd, char *buf)
 
 static DRIVER_ATTR(version, S_IRUGO, megasas_sysfs_show_version, NULL);
 
+static ssize_t
+megasas_sysfs_show_release_date(struct device_driver *dd, char *buf)
+{
+       return snprintf(buf, strlen(MEGASAS_RELDATE) + 2, "%s\n",
+               MEGASAS_RELDATE);
+}
+
+static DRIVER_ATTR(release_date, S_IRUGO, megasas_sysfs_show_release_date, NULL);
+
 static ssize_t
 megasas_sysfs_show_support_poll_for_event(struct device_driver *dd, char *buf)
 {
@@ -6840,6 +6773,11 @@ static int __init megasas_init(void)
        if (rval)
                goto err_dcf_attr_ver;
 
+       rval = driver_create_file(&megasas_pci_driver.driver,
+                                 &driver_attr_release_date);
+       if (rval)
+               goto err_dcf_rel_date;
+
        rval = driver_create_file(&megasas_pci_driver.driver,
                                &driver_attr_support_poll_for_event);
        if (rval)
@@ -6863,6 +6801,9 @@ err_dcf_dbg_lvl:
        driver_remove_file(&megasas_pci_driver.driver,
                        &driver_attr_support_poll_for_event);
 err_dcf_support_poll_for_event:
+       driver_remove_file(&megasas_pci_driver.driver,
+                          &driver_attr_release_date);
+err_dcf_rel_date:
        driver_remove_file(&megasas_pci_driver.driver, &driver_attr_version);
 err_dcf_attr_ver:
        pci_unregister_driver(&megasas_pci_driver);
@@ -6882,6 +6823,8 @@ static void __exit megasas_exit(void)
                        &driver_attr_support_poll_for_event);
        driver_remove_file(&megasas_pci_driver.driver,
                        &driver_attr_support_device_change);
+       driver_remove_file(&megasas_pci_driver.driver,
+                          &driver_attr_release_date);
        driver_remove_file(&megasas_pci_driver.driver, &driver_attr_version);
 
        pci_unregister_driver(&megasas_pci_driver);
index 4f72287860eeea45005301d6fde70507ba4427be..be57b18675a4b1b96243e067a71cd2db2a80c1c5 100644 (file)
@@ -66,7 +66,15 @@ MODULE_PARM_DESC(lb_pending_cmds, "Change raid-1 load balancing outstanding "
 
 #define ABS_DIFF(a, b)   (((a) > (b)) ? ((a) - (b)) : ((b) - (a)))
 #define MR_LD_STATE_OPTIMAL 3
+
+#ifdef FALSE
+#undef FALSE
+#endif
 #define FALSE 0
+
+#ifdef TRUE
+#undef TRUE
+#endif
 #define TRUE 1
 
 #define SPAN_DEBUG 0
@@ -142,7 +150,7 @@ u16 MR_LdSpanArrayGet(u32 ld, u32 span, struct MR_DRV_RAID_MAP_ALL *map)
        return le16_to_cpu(map->raidMap.ldSpanMap[ld].spanBlock[span].span.arrayRef);
 }
 
-u16 MR_PdDevHandleGet(u32 pd, struct MR_DRV_RAID_MAP_ALL *map)
+__le16 MR_PdDevHandleGet(u32 pd, struct MR_DRV_RAID_MAP_ALL *map)
 {
        return map->raidMap.devHndlInfo[pd].curDevHdl;
 }
@@ -735,7 +743,7 @@ static u8 mr_spanset_get_phy_params(struct megasas_instance *instance, u32 ld,
        u8      retval = TRUE;
        u8      do_invader = 0;
        u64     *pdBlock = &io_info->pdBlock;
-       u16     *pDevHandle = &io_info->devHandle;
+       __le16  *pDevHandle = &io_info->devHandle;
        u32     logArm, rowMod, armQ, arm;
 
        if ((instance->pdev->device == PCI_DEVICE_ID_LSI_INVADER ||
@@ -769,7 +777,7 @@ static u8 mr_spanset_get_phy_params(struct megasas_instance *instance, u32 ld,
        if (pd != MR_PD_INVALID)
                *pDevHandle = MR_PdDevHandleGet(pd, map);
        else {
-               *pDevHandle = MR_PD_INVALID;
+               *pDevHandle = cpu_to_le16(MR_PD_INVALID);
                if ((raid->level >= 5) &&
                        (!do_invader  || (do_invader &&
                        (raid->regTypeReqOnRead != REGION_TYPE_UNUSED))))
@@ -817,7 +825,7 @@ u8 MR_GetPhyParams(struct megasas_instance *instance, u32 ld, u64 stripRow,
        u8          retval = TRUE;
        u8          do_invader = 0;
        u64         *pdBlock = &io_info->pdBlock;
-       u16         *pDevHandle = &io_info->devHandle;
+       __le16      *pDevHandle = &io_info->devHandle;
 
        if ((instance->pdev->device == PCI_DEVICE_ID_LSI_INVADER ||
                instance->pdev->device == PCI_DEVICE_ID_LSI_FURY))
@@ -864,7 +872,8 @@ u8 MR_GetPhyParams(struct megasas_instance *instance, u32 ld, u64 stripRow,
                /* Get dev handle from Pd. */
                *pDevHandle = MR_PdDevHandleGet(pd, map);
        else {
-               *pDevHandle = MR_PD_INVALID; /* set dev handle as invalid. */
+               /* set dev handle as invalid. */
+               *pDevHandle = cpu_to_le16(MR_PD_INVALID);
                if ((raid->level >= 5) &&
                        (!do_invader  || (do_invader &&
                        (raid->regTypeReqOnRead != REGION_TYPE_UNUSED))))
@@ -1109,7 +1118,7 @@ MR_BuildRaidContext(struct megasas_instance *instance,
                                        ref_in_start_stripe, io_info,
                                        pRAID_Context, map);
                /* If IO on an invalid Pd, then FP is not possible.*/
-               if (io_info->devHandle == MR_PD_INVALID)
+               if (io_info->devHandle == cpu_to_le16(MR_PD_INVALID))
                        io_info->fpOkForIo = FALSE;
                return retval;
        } else if (isRead) {
@@ -1341,11 +1350,11 @@ u8 megasas_get_best_arm_pd(struct megasas_instance *instance,
        return io_info->pd_after_lb;
 }
 
-u16 get_updated_dev_handle(struct megasas_instance *instance,
+__le16 get_updated_dev_handle(struct megasas_instance *instance,
        struct LD_LOAD_BALANCE_INFO *lbInfo, struct IO_REQUEST_INFO *io_info)
 {
        u8 arm_pd;
-       u16 devHandle;
+       __le16 devHandle;
        struct fusion_context *fusion;
        struct MR_DRV_RAID_MAP_ALL *drv_map;
 
index 5a0800d19970d4fedff5332a73ef397e611f7914..46a0f8f4f677eeeac6ad18e28cf3810e1554165d 100644 (file)
 #include <scsi/scsi_device.h>
 #include <scsi/scsi_host.h>
 #include <scsi/scsi_dbg.h>
+#include <linux/dmi.h>
 
 #include "megaraid_sas_fusion.h"
 #include "megaraid_sas.h"
 
+
 extern void megasas_free_cmds(struct megasas_instance *instance);
 extern struct megasas_cmd *megasas_get_cmd(struct megasas_instance
                                           *instance);
@@ -156,28 +158,15 @@ megasas_clear_intr_fusion(struct megasas_register_set __iomem *regs)
  * megasas_get_cmd_fusion -    Get a command from the free pool
  * @instance:          Adapter soft state
  *
- * Returns a free command from the pool
+ * Returns a blk_tag indexed mpt frame
  */
-struct megasas_cmd_fusion *megasas_get_cmd_fusion(struct megasas_instance
-                                                 *instance)
+inline struct megasas_cmd_fusion *megasas_get_cmd_fusion(struct megasas_instance
+                                                 *instance, u32 blk_tag)
 {
-       unsigned long flags;
-       struct fusion_context *fusion =
-               (struct fusion_context *)instance->ctrl_context;
-       struct megasas_cmd_fusion *cmd = NULL;
-
-       spin_lock_irqsave(&fusion->mpt_pool_lock, flags);
-
-       if (!list_empty(&fusion->cmd_pool)) {
-               cmd = list_entry((&fusion->cmd_pool)->next,
-                                struct megasas_cmd_fusion, list);
-               list_del_init(&cmd->list);
-       } else {
-               printk(KERN_ERR "megasas: Command pool (fusion) empty!\n");
-       }
+       struct fusion_context *fusion;
 
-       spin_unlock_irqrestore(&fusion->mpt_pool_lock, flags);
-       return cmd;
+       fusion = instance->ctrl_context;
+       return fusion->cmd_list[blk_tag];
 }
 
 /**
@@ -188,47 +177,35 @@ struct megasas_cmd_fusion *megasas_get_cmd_fusion(struct megasas_instance
 inline void megasas_return_cmd_fusion(struct megasas_instance *instance,
        struct megasas_cmd_fusion *cmd)
 {
-       unsigned long flags;
-       struct fusion_context *fusion =
-               (struct fusion_context *)instance->ctrl_context;
-
-       spin_lock_irqsave(&fusion->mpt_pool_lock, flags);
-
        cmd->scmd = NULL;
-       cmd->sync_cmd_idx = (u32)ULONG_MAX;
        memset(cmd->io_request, 0, sizeof(struct MPI2_RAID_SCSI_IO_REQUEST));
-       list_add(&cmd->list, (&fusion->cmd_pool)->next);
-
-       spin_unlock_irqrestore(&fusion->mpt_pool_lock, flags);
 }
 
 /**
- * megasas_return_mfi_mpt_pthr - Return a mfi and mpt to free command pool
- * @instance:          Adapter soft state
- * @cmd_mfi:           MFI Command packet to be returned to free command pool
- * @cmd_mpt:           MPT Command packet to be returned to free command pool
+ * megasas_fire_cmd_fusion -   Sends command to the FW
  */
-inline void megasas_return_mfi_mpt_pthr(struct megasas_instance *instance,
-               struct megasas_cmd *cmd_mfi,
-               struct megasas_cmd_fusion *cmd_fusion)
+static void
+megasas_fire_cmd_fusion(struct megasas_instance *instance,
+               union MEGASAS_REQUEST_DESCRIPTOR_UNION *req_desc)
 {
+#if defined(writeq) && defined(CONFIG_64BIT)
+       u64 req_data = (((u64)le32_to_cpu(req_desc->u.high) << 32) |
+                       le32_to_cpu(req_desc->u.low));
+
+       writeq(req_data, &instance->reg_set->inbound_low_queue_port);
+#else
        unsigned long flags;
 
-       /*
-        * TO DO: optimize this code and use only one lock instead of two
-        * locks being used currently- mpt_pool_lock is acquired
-        * inside mfi_pool_lock
-        */
-       spin_lock_irqsave(&instance->mfi_pool_lock, flags);
-       megasas_return_cmd_fusion(instance, cmd_fusion);
-       if (atomic_read(&cmd_mfi->mfi_mpt_pthr) != MFI_MPT_ATTACHED)
-               dev_err(&instance->pdev->dev, "Possible bug from %s %d\n",
-                       __func__, __LINE__);
-       atomic_set(&cmd_mfi->mfi_mpt_pthr, MFI_MPT_DETACHED);
-       __megasas_return_cmd(instance, cmd_mfi);
-       spin_unlock_irqrestore(&instance->mfi_pool_lock, flags);
+       spin_lock_irqsave(&instance->hba_lock, flags);
+       writel(le32_to_cpu(req_desc->u.low),
+               &instance->reg_set->inbound_low_queue_port);
+       writel(le32_to_cpu(req_desc->u.high),
+               &instance->reg_set->inbound_high_queue_port);
+       spin_unlock_irqrestore(&instance->hba_lock, flags);
+#endif
 }
 
+
 /**
  * megasas_teardown_frame_pool_fusion -        Destroy the cmd frame DMA pool
  * @instance:                          Adapter soft state
@@ -326,7 +303,6 @@ megasas_free_cmds_fusion(struct megasas_instance *instance)
        kfree(fusion->cmd_list);
        fusion->cmd_list = NULL;
 
-       INIT_LIST_HEAD(&fusion->cmd_pool);
 }
 
 /**
@@ -464,7 +440,7 @@ megasas_alloc_cmds_fusion(struct megasas_instance *instance)
 
        reply_desc = fusion->reply_frames_desc;
        for (i = 0; i < fusion->reply_q_depth * count; i++, reply_desc++)
-               reply_desc->Words = ULLONG_MAX;
+               reply_desc->Words = cpu_to_le64(ULLONG_MAX);
 
        io_frames_sz = fusion->io_frames_alloc_sz;
 
@@ -535,7 +511,9 @@ megasas_alloc_cmds_fusion(struct megasas_instance *instance)
                memset(cmd, 0, sizeof(struct megasas_cmd_fusion));
                cmd->index = i + 1;
                cmd->scmd = NULL;
-               cmd->sync_cmd_idx = (u32)ULONG_MAX; /* Set to Invalid */
+               cmd->sync_cmd_idx = (i >= instance->max_scsi_cmds) ?
+                               (i - instance->max_scsi_cmds) :
+                               (u32)ULONG_MAX; /* Set to Invalid */
                cmd->instance = instance;
                cmd->io_request =
                        (struct MPI2_RAID_SCSI_IO_REQUEST *)
@@ -543,8 +521,6 @@ megasas_alloc_cmds_fusion(struct megasas_instance *instance)
                memset(cmd->io_request, 0,
                       sizeof(struct MPI2_RAID_SCSI_IO_REQUEST));
                cmd->io_request_phys_addr = io_req_base_phys + offset;
-
-               list_add_tail(&cmd->list, &fusion->cmd_pool);
        }
 
        /*
@@ -605,14 +581,11 @@ wait_and_poll(struct megasas_instance *instance, struct megasas_cmd *cmd,
                msleep(20);
        }
 
-       if (frame_hdr->cmd_status == 0xff) {
-               if (fusion)
-                       megasas_return_mfi_mpt_pthr(instance, cmd,
-                               cmd->mpt_pthr_cmd_blocked);
+       if (frame_hdr->cmd_status == 0xff)
                return -ETIME;
-       }
 
-       return 0;
+       return (frame_hdr->cmd_status == MFI_STAT_OK) ?
+               0 : 1;
 }
 
 /**
@@ -633,6 +606,7 @@ megasas_ioc_init_fusion(struct megasas_instance *instance)
        union MEGASAS_REQUEST_DESCRIPTOR_UNION req_desc;
        int i;
        struct megasas_header *frame_hdr;
+       const char *sys_info;
 
        fusion = instance->ctrl_context;
 
@@ -673,7 +647,9 @@ megasas_ioc_init_fusion(struct megasas_instance *instance)
 
        frame_hdr = &cmd->frame->hdr;
        frame_hdr->cmd_status = 0xFF;
-       frame_hdr->flags |= cpu_to_le16(MFI_FRAME_DONT_POST_IN_REPLY_QUEUE);
+       frame_hdr->flags = cpu_to_le16(
+               le16_to_cpu(frame_hdr->flags) |
+               MFI_FRAME_DONT_POST_IN_REPLY_QUEUE);
 
        init_frame->cmd = MFI_CMD_INIT;
        init_frame->cmd_status = 0xFF;
@@ -695,6 +671,16 @@ megasas_ioc_init_fusion(struct megasas_instance *instance)
        /* Convert capability to LE32 */
        cpu_to_le32s((u32 *)&init_frame->driver_operations.mfi_capabilities);
 
+       sys_info = dmi_get_system_info(DMI_PRODUCT_UUID);
+       if (instance->system_info_buf && sys_info) {
+               memcpy(instance->system_info_buf->systemId, sys_info,
+                       strlen(sys_info) > 64 ? 64 : strlen(sys_info));
+               instance->system_info_buf->systemIdLength =
+                       strlen(sys_info) > 64 ? 64 : strlen(sys_info);
+               init_frame->system_info_lo = instance->system_info_h;
+               init_frame->system_info_hi = 0;
+       }
+
        init_frame->queue_info_new_phys_addr_hi =
                cpu_to_le32(upper_32_bits(ioc_init_handle));
        init_frame->queue_info_new_phys_addr_lo =
@@ -719,8 +705,7 @@ megasas_ioc_init_fusion(struct megasas_instance *instance)
                        break;
        }
 
-       instance->instancet->fire_cmd(instance, req_desc.u.low,
-                                     req_desc.u.high, instance->reg_set);
+       megasas_fire_cmd_fusion(instance, &req_desc);
 
        wait_and_poll(instance, cmd, MFI_POLL_TIMEOUT_SECS);
 
@@ -820,11 +805,7 @@ megasas_get_ld_map_info(struct megasas_instance *instance)
        else
                ret = megasas_issue_polled(instance, cmd);
 
-       if (instance->ctrl_context && cmd->mpt_pthr_cmd_blocked)
-               megasas_return_mfi_mpt_pthr(instance, cmd,
-                       cmd->mpt_pthr_cmd_blocked);
-       else
-               megasas_return_cmd(instance, cmd);
+       megasas_return_cmd(instance, cmd);
 
        return ret;
 }
@@ -1060,6 +1041,15 @@ megasas_init_adapter_fusion(struct megasas_instance *instance)
        for (i = 0 ; i < count; i++)
                fusion->last_reply_idx[i] = 0;
 
+       /*
+        * For fusion adapters, 3 commands for IOCTL and 5 commands
+        * for driver's internal DCMDs.
+        */
+       instance->max_scsi_cmds = instance->max_fw_cmds -
+                               (MEGASAS_FUSION_INTERNAL_CMDS +
+                               MEGASAS_FUSION_IOCTL_CMDS);
+       sema_init(&instance->ioctl_sem, MEGASAS_FUSION_IOCTL_CMDS);
+
        /*
         * Allocate memory for descriptors
         * Create a pool of commands
@@ -1130,34 +1120,6 @@ fail_alloc_mfi_cmds:
        return 1;
 }
 
-/**
- * megasas_fire_cmd_fusion -   Sends command to the FW
- * @frame_phys_addr :          Physical address of cmd
- * @frame_count :              Number of frames for the command
- * @regs :                     MFI register set
- */
-void
-megasas_fire_cmd_fusion(struct megasas_instance *instance,
-                       dma_addr_t req_desc_lo,
-                       u32 req_desc_hi,
-                       struct megasas_register_set __iomem *regs)
-{
-#if defined(writeq) && defined(CONFIG_64BIT)
-       u64 req_data = (((u64)le32_to_cpu(req_desc_hi) << 32) |
-                       le32_to_cpu(req_desc_lo));
-
-       writeq(req_data, &(regs)->inbound_low_queue_port);
-#else
-       unsigned long flags;
-
-       spin_lock_irqsave(&instance->hba_lock, flags);
-
-       writel(le32_to_cpu(req_desc_lo), &(regs)->inbound_low_queue_port);
-       writel(le32_to_cpu(req_desc_hi), &(regs)->inbound_high_queue_port);
-       spin_unlock_irqrestore(&instance->hba_lock, flags);
-#endif
-}
-
 /**
  * map_cmd_status -    Maps FW cmd status to OS cmd status
  * @cmd :              Pointer to cmd
@@ -1497,7 +1459,7 @@ megasas_build_ldio_fusion(struct megasas_instance *instance,
        struct MR_DRV_RAID_MAP_ALL *local_map_ptr;
        u8 *raidLUN;
 
-       device_id = MEGASAS_DEV_INDEX(instance, scp);
+       device_id = MEGASAS_DEV_INDEX(scp);
 
        fusion = instance->ctrl_context;
 
@@ -1621,6 +1583,14 @@ megasas_build_ldio_fusion(struct megasas_instance *instance,
                        cmd->pd_r1_lb = io_info.pd_after_lb;
                } else
                        scp->SCp.Status &= ~MEGASAS_LOAD_BALANCE_FLAG;
+
+               if ((raidLUN[0] == 1) &&
+                       (local_map_ptr->raidMap.devHndlInfo[io_info.pd_after_lb].validHandles > 2)) {
+                       instance->dev_handle = !(instance->dev_handle);
+                       io_info.devHandle =
+                               local_map_ptr->raidMap.devHndlInfo[io_info.pd_after_lb].devHandle[instance->dev_handle];
+               }
+
                cmd->request_desc->SCSIIO.DevHandle = io_info.devHandle;
                io_request->DevHandle = io_info.devHandle;
                /* populate the LUN field */
@@ -1650,121 +1620,68 @@ megasas_build_ldio_fusion(struct megasas_instance *instance,
 }
 
 /**
- * megasas_build_dcdb_fusion - Prepares IOs to devices
+ * megasas_build_ld_nonrw_fusion - prepares non rw ios for virtual disk
  * @instance:          Adapter soft state
  * @scp:               SCSI command
  * @cmd:               Command to be prepared
  *
- * Prepares the io_request frame for non-io cmds
+ * Prepares the io_request frame for non-rw io cmds for vd.
  */
-static void
-megasas_build_dcdb_fusion(struct megasas_instance *instance,
-                         struct scsi_cmnd *scmd,
-                         struct megasas_cmd_fusion *cmd)
+static void megasas_build_ld_nonrw_fusion(struct megasas_instance *instance,
+                         struct scsi_cmnd *scmd, struct megasas_cmd_fusion *cmd)
 {
        u32 device_id;
        struct MPI2_RAID_SCSI_IO_REQUEST *io_request;
        u16 pd_index = 0;
-       u16 os_timeout_value;
-       u16 timeout_limit;
        struct MR_DRV_RAID_MAP_ALL *local_map_ptr;
        struct fusion_context *fusion = instance->ctrl_context;
        u8                          span, physArm;
-       u16                         devHandle;
+       __le16                      devHandle;
        u32                         ld, arRef, pd;
        struct MR_LD_RAID                  *raid;
        struct RAID_CONTEXT                *pRAID_Context;
+       u8 fp_possible = 1;
 
        io_request = cmd->io_request;
-       device_id = MEGASAS_DEV_INDEX(instance, scmd);
-       pd_index = (scmd->device->channel * MEGASAS_MAX_DEV_PER_CHANNEL)
-               +scmd->device->id;
+       device_id = MEGASAS_DEV_INDEX(scmd);
+       pd_index = MEGASAS_PD_INDEX(scmd);
        local_map_ptr = fusion->ld_drv_map[(instance->map_id & 1)];
-
        io_request->DataLength = cpu_to_le32(scsi_bufflen(scmd));
+       /* get RAID_Context pointer */
+       pRAID_Context = &io_request->RaidContext;
+       /* Check with FW team */
+       pRAID_Context->VirtualDiskTgtId = cpu_to_le16(device_id);
+       pRAID_Context->regLockRowLBA    = 0;
+       pRAID_Context->regLockLength    = 0;
 
-       if (scmd->device->channel < MEGASAS_MAX_PD_CHANNELS &&
-           instance->pd_list[pd_index].driveState == MR_PD_STATE_SYSTEM) {
-               if (fusion->fast_path_io)
-                       io_request->DevHandle =
-                       local_map_ptr->raidMap.devHndlInfo[device_id].curDevHdl;
-               io_request->RaidContext.RAIDFlags =
-                       MR_RAID_FLAGS_IO_SUB_TYPE_SYSTEM_PD
-                       << MR_RAID_CTX_RAID_FLAGS_IO_SUB_TYPE_SHIFT;
-               cmd->request_desc->SCSIIO.DevHandle = io_request->DevHandle;
-               cmd->request_desc->SCSIIO.MSIxIndex =
-                       instance->msix_vectors ?
-                               raw_smp_processor_id() %
-                                       instance->msix_vectors :
-                               0;
-               os_timeout_value = scmd->request->timeout / HZ;
-
-               if (instance->secure_jbod_support &&
-                       (megasas_cmd_type(scmd) == NON_READ_WRITE_SYSPDIO)) {
-                       /* system pd firmware path */
-                       io_request->Function  =
-                               MEGASAS_MPI2_FUNCTION_LD_IO_REQUEST;
-                       cmd->request_desc->SCSIIO.RequestFlags =
-                               (MPI2_REQ_DESCRIPT_FLAGS_SCSI_IO <<
-                               MEGASAS_REQ_DESCRIPT_FLAGS_TYPE_SHIFT);
-                       io_request->RaidContext.timeoutValue =
-                               cpu_to_le16(os_timeout_value);
-               } else {
-                       /* system pd Fast Path */
-                       io_request->Function = MPI2_FUNCTION_SCSI_IO_REQUEST;
-                       io_request->RaidContext.regLockFlags = 0;
-                       io_request->RaidContext.regLockRowLBA = 0;
-                       io_request->RaidContext.regLockLength = 0;
-                       timeout_limit = (scmd->device->type == TYPE_DISK) ?
-                                       255 : 0xFFFF;
-                       io_request->RaidContext.timeoutValue =
-                               cpu_to_le16((os_timeout_value > timeout_limit) ?
-                               timeout_limit : os_timeout_value);
-               if ((instance->pdev->device == PCI_DEVICE_ID_LSI_INVADER) ||
-                       (instance->pdev->device == PCI_DEVICE_ID_LSI_FURY))
-                       io_request->IoFlags |=
-                       cpu_to_le16(MPI25_SAS_DEVICE0_FLAGS_ENABLED_FAST_PATH);
-
-                       cmd->request_desc->SCSIIO.RequestFlags =
-                               (MPI2_REQ_DESCRIPT_FLAGS_HIGH_PRIORITY <<
-                               MEGASAS_REQ_DESCRIPT_FLAGS_TYPE_SHIFT);
-               }
-       } else {
-               if (scmd->device->channel < MEGASAS_MAX_PD_CHANNELS)
-                       goto NonFastPath;
-
-               /*
-                * For older firmware, Driver should not access ldTgtIdToLd
-                * beyond index 127 and for Extended VD firmware, ldTgtIdToLd
-                * should not go beyond 255.
-                */
-
-               if ((!fusion->fast_path_io) ||
-                       (device_id >= instance->fw_supported_vd_count))
-                       goto NonFastPath;
+       if (fusion->fast_path_io && (
+               device_id < instance->fw_supported_vd_count)) {
 
                ld = MR_TargetIdToLdGet(device_id, local_map_ptr);
-
                if (ld >= instance->fw_supported_vd_count)
-                       goto NonFastPath;
+                       fp_possible = 0;
 
                raid = MR_LdRaidGet(ld, local_map_ptr);
-
-               /* check if this LD is FP capable */
                if (!(raid->capability.fpNonRWCapable))
-                       /* not FP capable, send as non-FP */
-                       goto NonFastPath;
+                       fp_possible = 0;
+       } else
+               fp_possible = 0;
 
-               /* get RAID_Context pointer */
-               pRAID_Context = &io_request->RaidContext;
+       if (!fp_possible) {
+               io_request->Function  = MEGASAS_MPI2_FUNCTION_LD_IO_REQUEST;
+               io_request->DevHandle = cpu_to_le16(device_id);
+               io_request->LUN[1] = scmd->device->lun;
+               pRAID_Context->timeoutValue =
+                       cpu_to_le16 (scmd->request->timeout / HZ);
+               cmd->request_desc->SCSIIO.RequestFlags =
+                       (MPI2_REQ_DESCRIPT_FLAGS_SCSI_IO <<
+                       MEGASAS_REQ_DESCRIPT_FLAGS_TYPE_SHIFT);
+       } else {
 
                /* set RAID context values */
-               pRAID_Context->regLockFlags     = REGION_TYPE_SHARED_READ;
-               pRAID_Context->timeoutValue     = cpu_to_le16(raid->fpIoTimeoutForLd);
-               pRAID_Context->VirtualDiskTgtId = cpu_to_le16(device_id);
-               pRAID_Context->regLockRowLBA    = 0;
-               pRAID_Context->regLockLength    = 0;
-               pRAID_Context->configSeqNum     = raid->seqNum;
+               pRAID_Context->configSeqNum = raid->seqNum;
+               pRAID_Context->regLockFlags = REGION_TYPE_SHARED_READ;
+               pRAID_Context->timeoutValue = cpu_to_le16(raid->fpIoTimeoutForLd);
 
                /* get the DevHandle for the PD (since this is
                   fpNonRWCapable, this is a single disk RAID0) */
@@ -1776,7 +1693,7 @@ megasas_build_dcdb_fusion(struct megasas_instance *instance,
                /* build request descriptor */
                cmd->request_desc->SCSIIO.RequestFlags =
                        (MPI2_REQ_DESCRIPT_FLAGS_HIGH_PRIORITY <<
-                        MEGASAS_REQ_DESCRIPT_FLAGS_TYPE_SHIFT);
+                       MEGASAS_REQ_DESCRIPT_FLAGS_TYPE_SHIFT);
                cmd->request_desc->SCSIIO.DevHandle = devHandle;
 
                /* populate the LUN field */
@@ -1785,18 +1702,87 @@ megasas_build_dcdb_fusion(struct megasas_instance *instance,
                /* build the raidScsiIO structure */
                io_request->Function = MPI2_FUNCTION_SCSI_IO_REQUEST;
                io_request->DevHandle = devHandle;
+       }
+}
 
-               return;
+/**
+ * megasas_build_syspd_fusion - prepares rw/non-rw ios for syspd
+ * @instance:          Adapter soft state
+ * @scp:               SCSI command
+ * @cmd:               Command to be prepared
+ * @fp_possible:       parameter to detect fast path or firmware path io.
+ *
+ * Prepares the io_request frame for rw/non-rw io cmds for syspds
+ */
+static void
+megasas_build_syspd_fusion(struct megasas_instance *instance,
+       struct scsi_cmnd *scmd, struct megasas_cmd_fusion *cmd, u8 fp_possible)
+{
+       u32 device_id;
+       struct MPI2_RAID_SCSI_IO_REQUEST *io_request;
+       u16 pd_index = 0;
+       u16 os_timeout_value;
+       u16 timeout_limit;
+       struct MR_DRV_RAID_MAP_ALL *local_map_ptr;
+       struct RAID_CONTEXT     *pRAID_Context;
+       struct fusion_context *fusion = instance->ctrl_context;
+
+       device_id = MEGASAS_DEV_INDEX(scmd);
+       pd_index = MEGASAS_PD_INDEX(scmd);
+       os_timeout_value = scmd->request->timeout / HZ;
 
-NonFastPath:
+       io_request = cmd->io_request;
+       /* get RAID_Context pointer */
+       pRAID_Context = &io_request->RaidContext;
+       io_request->DataLength = cpu_to_le32(scsi_bufflen(scmd));
+       io_request->LUN[1] = scmd->device->lun;
+       pRAID_Context->RAIDFlags = MR_RAID_FLAGS_IO_SUB_TYPE_SYSTEM_PD
+               << MR_RAID_CTX_RAID_FLAGS_IO_SUB_TYPE_SHIFT;
+
+       pRAID_Context->VirtualDiskTgtId = cpu_to_le16(device_id);
+       pRAID_Context->configSeqNum = 0;
+       local_map_ptr = fusion->ld_drv_map[(instance->map_id & 1)];
+       io_request->DevHandle =
+               local_map_ptr->raidMap.devHndlInfo[device_id].curDevHdl;
+
+       cmd->request_desc->SCSIIO.DevHandle = io_request->DevHandle;
+       cmd->request_desc->SCSIIO.MSIxIndex =
+               instance->msix_vectors ?
+               (raw_smp_processor_id() % instance->msix_vectors) : 0;
+
+
+       if (!fp_possible) {
+               /* system pd firmware path */
                io_request->Function  = MEGASAS_MPI2_FUNCTION_LD_IO_REQUEST;
-               io_request->DevHandle = cpu_to_le16(device_id);
                cmd->request_desc->SCSIIO.RequestFlags =
                        (MPI2_REQ_DESCRIPT_FLAGS_SCSI_IO <<
-                        MEGASAS_REQ_DESCRIPT_FLAGS_TYPE_SHIFT);
+                               MEGASAS_REQ_DESCRIPT_FLAGS_TYPE_SHIFT);
+               pRAID_Context->timeoutValue = cpu_to_le16(os_timeout_value);
+       } else {
+               /* system pd Fast Path */
+               io_request->Function = MPI2_FUNCTION_SCSI_IO_REQUEST;
+               pRAID_Context->regLockFlags = 0;
+               pRAID_Context->regLockRowLBA = 0;
+               pRAID_Context->regLockLength = 0;
+               timeout_limit = (scmd->device->type == TYPE_DISK) ?
+                               255 : 0xFFFF;
+               pRAID_Context->timeoutValue =
+                       cpu_to_le16((os_timeout_value > timeout_limit) ?
+                       timeout_limit : os_timeout_value);
+               if ((instance->pdev->device == PCI_DEVICE_ID_LSI_INVADER) ||
+                       (instance->pdev->device == PCI_DEVICE_ID_LSI_FURY)) {
+                       cmd->request_desc->SCSIIO.RequestFlags |=
+                               (MEGASAS_REQ_DESCRIPT_FLAGS_NO_LOCK <<
+                               MEGASAS_REQ_DESCRIPT_FLAGS_TYPE_SHIFT);
+                       pRAID_Context->Type = MPI2_TYPE_CUDA;
+                       pRAID_Context->nseg = 0x1;
+                       io_request->IoFlags |=
+                               cpu_to_le16(MPI25_SAS_DEVICE0_FLAGS_ENABLED_FAST_PATH);
+               }
+               cmd->request_desc->SCSIIO.RequestFlags =
+                       (MPI2_REQ_DESCRIPT_FLAGS_HIGH_PRIORITY <<
+                               MEGASAS_REQ_DESCRIPT_FLAGS_TYPE_SHIFT);
        }
-       io_request->RaidContext.VirtualDiskTgtId = cpu_to_le16(device_id);
-       int_to_scsilun(scmd->device->lun, (struct scsi_lun *)io_request->LUN);
 }
 
 /**
@@ -1813,11 +1799,10 @@ megasas_build_io_fusion(struct megasas_instance *instance,
                        struct scsi_cmnd *scp,
                        struct megasas_cmd_fusion *cmd)
 {
-       u32 device_id, sge_count;
+       u32 sge_count;
+       u8  cmd_type;
        struct MPI2_RAID_SCSI_IO_REQUEST *io_request = cmd->io_request;
 
-       device_id = MEGASAS_DEV_INDEX(instance, scp);
-
        /* Zero out some fields so they don't get reused */
        memset(io_request->LUN, 0x0, 8);
        io_request->CDB.EEDP32.PrimaryReferenceTag = 0;
@@ -1837,10 +1822,24 @@ megasas_build_io_fusion(struct megasas_instance *instance,
         */
        io_request->IoFlags = cpu_to_le16(scp->cmd_len);
 
-       if (megasas_cmd_type(scp) == READ_WRITE_LDIO)
+       switch (cmd_type = megasas_cmd_type(scp)) {
+       case READ_WRITE_LDIO:
                megasas_build_ldio_fusion(instance, scp, cmd);
-       else
-               megasas_build_dcdb_fusion(instance, scp, cmd);
+               break;
+       case NON_READ_WRITE_LDIO:
+               megasas_build_ld_nonrw_fusion(instance, scp, cmd);
+               break;
+       case READ_WRITE_SYSPDIO:
+       case NON_READ_WRITE_SYSPDIO:
+               if (instance->secure_jbod_support &&
+                       (cmd_type == NON_READ_WRITE_SYSPDIO))
+                       megasas_build_syspd_fusion(instance, scp, cmd, 0);
+               else
+                       megasas_build_syspd_fusion(instance, scp, cmd, 1);
+               break;
+       default:
+               break;
+       }
 
        /*
         * Construct SGL
@@ -1915,9 +1914,7 @@ megasas_build_and_issue_cmd_fusion(struct megasas_instance *instance,
 
        fusion = instance->ctrl_context;
 
-       cmd = megasas_get_cmd_fusion(instance);
-       if (!cmd)
-               return SCSI_MLQUEUE_HOST_BUSY;
+       cmd = megasas_get_cmd_fusion(instance, scmd->request->tag);
 
        index = cmd->index;
 
@@ -1948,9 +1945,7 @@ megasas_build_and_issue_cmd_fusion(struct megasas_instance *instance,
         */
        atomic_inc(&instance->fw_outstanding);
 
-       instance->instancet->fire_cmd(instance,
-                                     req_desc->u.low, req_desc->u.high,
-                                     instance->reg_set);
+       megasas_fire_cmd_fusion(instance, req_desc);
 
        return 0;
 }
@@ -1975,6 +1970,7 @@ complete_cmd_fusion(struct megasas_instance *instance, u32 MSIxIndex)
        union desc_value d_val;
        struct LD_LOAD_BALANCE_INFO *lbinfo;
        int threshold_reply_count = 0;
+       struct scsi_cmnd *scmd_local = NULL;
 
        fusion = instance->ctrl_context;
 
@@ -1998,7 +1994,8 @@ complete_cmd_fusion(struct megasas_instance *instance, u32 MSIxIndex)
 
        num_completed = 0;
 
-       while ((d_val.u.low != UINT_MAX) && (d_val.u.high != UINT_MAX)) {
+       while (d_val.u.low != cpu_to_le32(UINT_MAX) &&
+              d_val.u.high != cpu_to_le32(UINT_MAX)) {
                smid = le16_to_cpu(reply_desc->SMID);
 
                cmd_fusion = fusion->cmd_list[smid - 1];
@@ -2010,14 +2007,14 @@ complete_cmd_fusion(struct megasas_instance *instance, u32 MSIxIndex)
                if (cmd_fusion->scmd)
                        cmd_fusion->scmd->SCp.ptr = NULL;
 
+               scmd_local = cmd_fusion->scmd;
                status = scsi_io_req->RaidContext.status;
                extStatus = scsi_io_req->RaidContext.exStatus;
 
                switch (scsi_io_req->Function) {
                case MPI2_FUNCTION_SCSI_IO_REQUEST:  /*Fast Path IO.*/
                        /* Update load balancing info */
-                       device_id = MEGASAS_DEV_INDEX(instance,
-                                                     cmd_fusion->scmd);
+                       device_id = MEGASAS_DEV_INDEX(scmd_local);
                        lbinfo = &fusion->load_balance_info[device_id];
                        if (cmd_fusion->scmd->SCp.Status &
                            MEGASAS_LOAD_BALANCE_FLAG) {
@@ -2035,29 +2032,25 @@ complete_cmd_fusion(struct megasas_instance *instance, u32 MSIxIndex)
                case MEGASAS_MPI2_FUNCTION_LD_IO_REQUEST: /* LD-IO Path */
                        /* Map the FW Cmd Status */
                        map_cmd_status(cmd_fusion, status, extStatus);
-                       scsi_dma_unmap(cmd_fusion->scmd);
-                       cmd_fusion->scmd->scsi_done(cmd_fusion->scmd);
                        scsi_io_req->RaidContext.status = 0;
                        scsi_io_req->RaidContext.exStatus = 0;
                        megasas_return_cmd_fusion(instance, cmd_fusion);
+                       scsi_dma_unmap(scmd_local);
+                       scmd_local->scsi_done(scmd_local);
                        atomic_dec(&instance->fw_outstanding);
 
                        break;
                case MEGASAS_MPI2_FUNCTION_PASSTHRU_IO_REQUEST: /*MFI command */
                        cmd_mfi = instance->cmd_list[cmd_fusion->sync_cmd_idx];
 
-                       if (!cmd_mfi->mpt_pthr_cmd_blocked) {
-                               if (megasas_dbg_lvl == 5)
-                                       dev_info(&instance->pdev->dev,
-                                               "freeing mfi/mpt pass-through "
-                                               "from %s %d\n",
-                                                __func__, __LINE__);
-                               megasas_return_mfi_mpt_pthr(instance, cmd_mfi,
-                                       cmd_fusion);
-                       }
-
-                       megasas_complete_cmd(instance, cmd_mfi, DID_OK);
-                       cmd_fusion->flags = 0;
+                       /* Poll mode. Dummy free.
+                        * In case of Interrupt mode, caller has reverse check.
+                        */
+                       if (cmd_mfi->flags & DRV_DCMD_POLLED_MODE) {
+                               cmd_mfi->flags &= ~DRV_DCMD_POLLED_MODE;
+                               megasas_return_cmd(instance, cmd_mfi);
+                       } else
+                               megasas_complete_cmd(instance, cmd_mfi, DID_OK);
                        break;
                }
 
@@ -2066,7 +2059,7 @@ complete_cmd_fusion(struct megasas_instance *instance, u32 MSIxIndex)
                    fusion->reply_q_depth)
                        fusion->last_reply_idx[MSIxIndex] = 0;
 
-               desc->Words = ULLONG_MAX;
+               desc->Words = cpu_to_le64(ULLONG_MAX);
                num_completed++;
                threshold_reply_count++;
 
@@ -2217,27 +2210,14 @@ build_mpt_mfi_pass_thru(struct megasas_instance *instance,
        struct megasas_cmd_fusion *cmd;
        struct fusion_context *fusion;
        struct megasas_header *frame_hdr = &mfi_cmd->frame->hdr;
-       u32 opcode;
 
-       cmd = megasas_get_cmd_fusion(instance);
-       if (!cmd)
-               return 1;
+       fusion = instance->ctrl_context;
+
+       cmd = megasas_get_cmd_fusion(instance,
+                       instance->max_scsi_cmds + mfi_cmd->index);
 
        /*  Save the smid. To be used for returning the cmd */
        mfi_cmd->context.smid = cmd->index;
-       cmd->sync_cmd_idx = mfi_cmd->index;
-
-       /* Set this only for Blocked commands */
-       opcode = le32_to_cpu(mfi_cmd->frame->dcmd.opcode);
-       if ((opcode == MR_DCMD_LD_MAP_GET_INFO)
-               && (mfi_cmd->frame->dcmd.mbox.b[1] == 1))
-               mfi_cmd->is_wait_event = 1;
-
-       if (opcode == MR_DCMD_CTRL_EVENT_WAIT)
-               mfi_cmd->is_wait_event = 1;
-
-       if (mfi_cmd->is_wait_event)
-               mfi_cmd->mpt_pthr_cmd_blocked = cmd;
 
        /*
         * For cmds where the flag is set, store the flag and check
@@ -2246,9 +2226,8 @@ build_mpt_mfi_pass_thru(struct megasas_instance *instance,
         */
 
        if (frame_hdr->flags & cpu_to_le16(MFI_FRAME_DONT_POST_IN_REPLY_QUEUE))
-               cmd->flags = MFI_FRAME_DONT_POST_IN_REPLY_QUEUE;
+               mfi_cmd->flags |= DRV_DCMD_POLLED_MODE;
 
-       fusion = instance->ctrl_context;
        io_req = cmd->io_request;
 
        if ((instance->pdev->device == PCI_DEVICE_ID_LSI_INVADER) ||
@@ -2327,14 +2306,12 @@ megasas_issue_dcmd_fusion(struct megasas_instance *instance,
                printk(KERN_ERR "Couldn't issue MFI pass thru cmd\n");
                return;
        }
-       atomic_set(&cmd->mfi_mpt_pthr, MFI_MPT_ATTACHED);
-       instance->instancet->fire_cmd(instance, req_desc->u.low,
-                                     req_desc->u.high, instance->reg_set);
+       megasas_fire_cmd_fusion(instance, req_desc);
 }
 
 /**
  * megasas_release_fusion -    Reverses the FW initialization
- * @intance:                   Adapter soft state
+ * @instance:                  Adapter soft state
  */
 void
 megasas_release_fusion(struct megasas_instance *instance)
@@ -2508,7 +2485,42 @@ void  megasas_reset_reply_desc(struct megasas_instance *instance)
                fusion->last_reply_idx[i] = 0;
        reply_desc = fusion->reply_frames_desc;
        for (i = 0 ; i < fusion->reply_q_depth * count; i++, reply_desc++)
-               reply_desc->Words = ULLONG_MAX;
+               reply_desc->Words = cpu_to_le64(ULLONG_MAX);
+}
+
+/*
+ * megasas_refire_mgmt_cmd :   Re-fire management commands
+ * @instance:                          Controller's soft instance
+*/
+void megasas_refire_mgmt_cmd(struct megasas_instance *instance)
+{
+       int j;
+       struct megasas_cmd_fusion *cmd_fusion;
+       struct fusion_context *fusion;
+       struct megasas_cmd *cmd_mfi;
+       union MEGASAS_REQUEST_DESCRIPTOR_UNION *req_desc;
+       u16 smid;
+
+       fusion = instance->ctrl_context;
+
+       /* Re-fire management commands.
+        * Do not traverse complet MPT frame pool. Start from max_scsi_cmds.
+        */
+       for (j = instance->max_scsi_cmds ; j < instance->max_fw_cmds; j++) {
+               cmd_fusion = fusion->cmd_list[j];
+               cmd_mfi = instance->cmd_list[cmd_fusion->sync_cmd_idx];
+               smid = le16_to_cpu(cmd_mfi->context.smid);
+
+               if (!smid)
+                       continue;
+               req_desc = megasas_get_request_descriptor
+                                       (instance, smid - 1);
+               if (req_desc && (cmd_mfi->frame->dcmd.opcode !=
+                               cpu_to_le32(MR_DCMD_LD_MAP_GET_INFO)))
+                       megasas_fire_cmd_fusion(instance, req_desc);
+               else
+                       megasas_return_cmd(instance, cmd_mfi);
+       }
 }
 
 /* Check for a second path that is currently UP */
@@ -2538,14 +2550,13 @@ out:
 /* Core fusion reset function */
 int megasas_reset_fusion(struct Scsi_Host *shost, int iotimeout)
 {
-       int retval = SUCCESS, i, j, retry = 0, convert = 0;
+       int retval = SUCCESS, i, retry = 0, convert = 0;
        struct megasas_instance *instance;
        struct megasas_cmd_fusion *cmd_fusion;
        struct fusion_context *fusion;
-       struct megasas_cmd *cmd_mfi;
-       union MEGASAS_REQUEST_DESCRIPTOR_UNION *req_desc;
        u32 host_diag, abs_state, status_reg, reset_adapter;
        u32 io_timeout_in_crash_mode = 0;
+       struct scsi_cmnd *scmd_local = NULL;
 
        instance = (struct megasas_instance *)shost->hostdata;
        fusion = instance->ctrl_context;
@@ -2613,15 +2624,16 @@ int megasas_reset_fusion(struct Scsi_Host *shost, int iotimeout)
                        iotimeout = 0;
 
                /* Now return commands back to the OS */
-               for (i = 0 ; i < instance->max_fw_cmds; i++) {
+               for (i = 0 ; i < instance->max_scsi_cmds; i++) {
                        cmd_fusion = fusion->cmd_list[i];
+                       scmd_local = cmd_fusion->scmd;
                        if (cmd_fusion->scmd) {
-                               scsi_dma_unmap(cmd_fusion->scmd);
-                               cmd_fusion->scmd->result =
+                               scmd_local->result =
                                        megasas_check_mpio_paths(instance,
-                                                                cmd_fusion->scmd);
-                               cmd_fusion->scmd->scsi_done(cmd_fusion->scmd);
+                                                       scmd_local);
                                megasas_return_cmd_fusion(instance, cmd_fusion);
+                               scsi_dma_unmap(scmd_local);
+                               scmd_local->scsi_done(scmd_local);
                                atomic_dec(&instance->fw_outstanding);
                        }
                }
@@ -2790,44 +2802,7 @@ int megasas_reset_fusion(struct Scsi_Host *shost, int iotimeout)
                                continue;
                        }
 
-                       /* Re-fire management commands */
-                       for (j = 0 ; j < instance->max_fw_cmds; j++) {
-                               cmd_fusion = fusion->cmd_list[j];
-                               if (cmd_fusion->sync_cmd_idx !=
-                                   (u32)ULONG_MAX) {
-                                       cmd_mfi =
-                                       instance->
-                                       cmd_list[cmd_fusion->sync_cmd_idx];
-                                       if (cmd_mfi->frame->dcmd.opcode ==
-                                           cpu_to_le32(MR_DCMD_LD_MAP_GET_INFO)) {
-                                               megasas_return_mfi_mpt_pthr(instance, cmd_mfi, cmd_fusion);
-                                       } else  {
-                                               req_desc =
-                                               megasas_get_request_descriptor(
-                                                       instance,
-                                                       cmd_mfi->context.smid
-                                                       -1);
-                                               if (!req_desc) {
-                                                       printk(KERN_WARNING
-                                                              "req_desc NULL"
-                                                              " for scsi%d\n",
-                                                               instance->host->host_no);
-                                                       /* Return leaked MPT
-                                                          frame */
-                                                       megasas_return_cmd_fusion(instance, cmd_fusion);
-                                               } else {
-                                                       instance->instancet->
-                                                       fire_cmd(instance,
-                                                                req_desc->
-                                                                u.low,
-                                                                req_desc->
-                                                                u.high,
-                                                                instance->
-                                                                reg_set);
-                                               }
-                                       }
-                               }
-                       }
+                       megasas_refire_mgmt_cmd(instance);
 
                        if (megasas_get_ctrl_info(instance)) {
                                dev_info(&instance->pdev->dev,
@@ -2978,7 +2953,6 @@ void megasas_fusion_ocr_wq(struct work_struct *work)
 }
 
 struct megasas_instance_template megasas_instance_template_fusion = {
-       .fire_cmd = megasas_fire_cmd_fusion,
        .enable_intr = megasas_enable_intr_fusion,
        .disable_intr = megasas_disable_intr_fusion,
        .clear_intr = megasas_clear_intr_fusion,
index 56e6db2d5874279ab6f6e000485fc6add3542054..ced6dc0cf8e8ab7bf6e00ec1710ee24a5e802a4e 100644 (file)
@@ -104,18 +104,18 @@ struct RAID_CONTEXT {
        u8      nseg:4;
 #endif
        u8      resvd0;
-       u16     timeoutValue;
+       __le16  timeoutValue;
        u8      regLockFlags;
        u8      resvd1;
-       u16     VirtualDiskTgtId;
-       u64     regLockRowLBA;
-       u32     regLockLength;
-       u16     nextLMId;
+       __le16  VirtualDiskTgtId;
+       __le64  regLockRowLBA;
+       __le32  regLockLength;
+       __le16  nextLMId;
        u8      exStatus;
        u8      status;
        u8      RAIDFlags;
        u8      numSGE;
-       u16     configSeqNum;
+       __le16  configSeqNum;
        u8      spanArm;
        u8      resvd2[3];
 };
@@ -182,61 +182,61 @@ enum REGION_TYPE {
 #define MPI2_WRSEQ_6TH_KEY_VALUE                (0xD)
 
 struct MPI25_IEEE_SGE_CHAIN64 {
-       u64                     Address;
-       u32                     Length;
-       u16                     Reserved1;
+       __le64                  Address;
+       __le32                  Length;
+       __le16                  Reserved1;
        u8                      NextChainOffset;
        u8                      Flags;
 };
 
 struct MPI2_SGE_SIMPLE_UNION {
-       u32                     FlagsLength;
+       __le32                     FlagsLength;
        union {
-               u32                 Address32;
-               u64                 Address64;
+               __le32                 Address32;
+               __le64                 Address64;
        } u;
 };
 
 struct MPI2_SCSI_IO_CDB_EEDP32 {
        u8                      CDB[20];                    /* 0x00 */
-       u32                     PrimaryReferenceTag;        /* 0x14 */
-       u16                     PrimaryApplicationTag;      /* 0x18 */
-       u16                     PrimaryApplicationTagMask;  /* 0x1A */
-       u32                     TransferLength;             /* 0x1C */
+       __be32                  PrimaryReferenceTag;        /* 0x14 */
+       __be16                  PrimaryApplicationTag;      /* 0x18 */
+       __be16                  PrimaryApplicationTagMask;  /* 0x1A */
+       __le32                  TransferLength;             /* 0x1C */
 };
 
 struct MPI2_SGE_CHAIN_UNION {
-       u16                     Length;
+       __le16                  Length;
        u8                      NextChainOffset;
        u8                      Flags;
        union {
-               u32                 Address32;
-               u64                 Address64;
+               __le32          Address32;
+               __le64          Address64;
        } u;
 };
 
 struct MPI2_IEEE_SGE_SIMPLE32 {
-       u32                     Address;
-       u32                     FlagsLength;
+       __le32                  Address;
+       __le32                  FlagsLength;
 };
 
 struct MPI2_IEEE_SGE_CHAIN32 {
-       u32                     Address;
-       u32                     FlagsLength;
+       __le32                  Address;
+       __le32                  FlagsLength;
 };
 
 struct MPI2_IEEE_SGE_SIMPLE64 {
-       u64                     Address;
-       u32                     Length;
-       u16                     Reserved1;
+       __le64                  Address;
+       __le32                  Length;
+       __le16                  Reserved1;
        u8                      Reserved2;
        u8                      Flags;
 };
 
 struct MPI2_IEEE_SGE_CHAIN64 {
-       u64                     Address;
-       u32                     Length;
-       u16                     Reserved1;
+       __le64                  Address;
+       __le32                  Length;
+       __le16                  Reserved1;
        u8                      Reserved2;
        u8                      Flags;
 };
@@ -269,34 +269,34 @@ union MPI2_SCSI_IO_CDB_UNION {
  * Total SGE count will be one less than  _MPI2_SCSI_IO_REQUEST
  */
 struct MPI2_RAID_SCSI_IO_REQUEST {
-       u16                     DevHandle;                      /* 0x00 */
+       __le16                  DevHandle;                      /* 0x00 */
        u8                      ChainOffset;                    /* 0x02 */
        u8                      Function;                       /* 0x03 */
-       u16                     Reserved1;                      /* 0x04 */
+       __le16                  Reserved1;                      /* 0x04 */
        u8                      Reserved2;                      /* 0x06 */
        u8                      MsgFlags;                       /* 0x07 */
        u8                      VP_ID;                          /* 0x08 */
        u8                      VF_ID;                          /* 0x09 */
-       u16                     Reserved3;                      /* 0x0A */
-       u32                     SenseBufferLowAddress;          /* 0x0C */
-       u16                     SGLFlags;                       /* 0x10 */
+       __le16                  Reserved3;                      /* 0x0A */
+       __le32                  SenseBufferLowAddress;          /* 0x0C */
+       __le16                  SGLFlags;                       /* 0x10 */
        u8                      SenseBufferLength;              /* 0x12 */
        u8                      Reserved4;                      /* 0x13 */
        u8                      SGLOffset0;                     /* 0x14 */
        u8                      SGLOffset1;                     /* 0x15 */
        u8                      SGLOffset2;                     /* 0x16 */
        u8                      SGLOffset3;                     /* 0x17 */
-       u32                     SkipCount;                      /* 0x18 */
-       u32                     DataLength;                     /* 0x1C */
-       u32                     BidirectionalDataLength;        /* 0x20 */
-       u16                     IoFlags;                        /* 0x24 */
-       u16                     EEDPFlags;                      /* 0x26 */
-       u32                     EEDPBlockSize;                  /* 0x28 */
-       u32                     SecondaryReferenceTag;          /* 0x2C */
-       u16                     SecondaryApplicationTag;        /* 0x30 */
-       u16                     ApplicationTagTranslationMask;  /* 0x32 */
+       __le32                  SkipCount;                      /* 0x18 */
+       __le32                  DataLength;                     /* 0x1C */
+       __le32                  BidirectionalDataLength;        /* 0x20 */
+       __le16                  IoFlags;                        /* 0x24 */
+       __le16                  EEDPFlags;                      /* 0x26 */
+       __le32                  EEDPBlockSize;                  /* 0x28 */
+       __le32                  SecondaryReferenceTag;          /* 0x2C */
+       __le16                  SecondaryApplicationTag;        /* 0x30 */
+       __le16                  ApplicationTagTranslationMask;  /* 0x32 */
        u8                      LUN[8];                         /* 0x34 */
-       u32                     Control;                        /* 0x3C */
+       __le32                  Control;                        /* 0x3C */
        union MPI2_SCSI_IO_CDB_UNION  CDB;                      /* 0x40 */
        struct RAID_CONTEXT     RaidContext;                    /* 0x60 */
        union MPI2_SGE_IO_UNION       SGL;                      /* 0x80 */
@@ -315,45 +315,45 @@ struct MEGASAS_RAID_MFA_IO_REQUEST_DESCRIPTOR {
 struct MPI2_DEFAULT_REQUEST_DESCRIPTOR {
        u8              RequestFlags;               /* 0x00 */
        u8              MSIxIndex;                  /* 0x01 */
-       u16             SMID;                       /* 0x02 */
-       u16             LMID;                       /* 0x04 */
-       u16             DescriptorTypeDependent;    /* 0x06 */
+       __le16          SMID;                       /* 0x02 */
+       __le16          LMID;                       /* 0x04 */
+       __le16          DescriptorTypeDependent;    /* 0x06 */
 };
 
 /* High Priority Request Descriptor */
 struct MPI2_HIGH_PRIORITY_REQUEST_DESCRIPTOR {
        u8              RequestFlags;               /* 0x00 */
        u8              MSIxIndex;                  /* 0x01 */
-       u16             SMID;                       /* 0x02 */
-       u16             LMID;                       /* 0x04 */
-       u16             Reserved1;                  /* 0x06 */
+       __le16          SMID;                       /* 0x02 */
+       __le16          LMID;                       /* 0x04 */
+       __le16          Reserved1;                  /* 0x06 */
 };
 
 /* SCSI IO Request Descriptor */
 struct MPI2_SCSI_IO_REQUEST_DESCRIPTOR {
        u8              RequestFlags;               /* 0x00 */
        u8              MSIxIndex;                  /* 0x01 */
-       u16             SMID;                       /* 0x02 */
-       u16             LMID;                       /* 0x04 */
-       u16             DevHandle;                  /* 0x06 */
+       __le16          SMID;                       /* 0x02 */
+       __le16          LMID;                       /* 0x04 */
+       __le16          DevHandle;                  /* 0x06 */
 };
 
 /* SCSI Target Request Descriptor */
 struct MPI2_SCSI_TARGET_REQUEST_DESCRIPTOR {
        u8              RequestFlags;               /* 0x00 */
        u8              MSIxIndex;                  /* 0x01 */
-       u16             SMID;                       /* 0x02 */
-       u16             LMID;                       /* 0x04 */
-       u16             IoIndex;                    /* 0x06 */
+       __le16          SMID;                       /* 0x02 */
+       __le16          LMID;                       /* 0x04 */
+       __le16          IoIndex;                    /* 0x06 */
 };
 
 /* RAID Accelerator Request Descriptor */
 struct MPI2_RAID_ACCEL_REQUEST_DESCRIPTOR {
        u8              RequestFlags;               /* 0x00 */
        u8              MSIxIndex;                  /* 0x01 */
-       u16             SMID;                       /* 0x02 */
-       u16             LMID;                       /* 0x04 */
-       u16             Reserved;                   /* 0x06 */
+       __le16          SMID;                       /* 0x02 */
+       __le16          LMID;                       /* 0x04 */
+       __le16          Reserved;                   /* 0x06 */
 };
 
 /* union of Request Descriptors */
@@ -366,10 +366,10 @@ union MEGASAS_REQUEST_DESCRIPTOR_UNION {
        struct MEGASAS_RAID_MFA_IO_REQUEST_DESCRIPTOR      MFAIo;
        union {
                struct {
-                       u32 low;
-                       u32 high;
+                       __le32 low;
+                       __le32 high;
                } u;
-               u64 Words;
+               __le64 Words;
        };
 };
 
@@ -377,35 +377,35 @@ union MEGASAS_REQUEST_DESCRIPTOR_UNION {
 struct MPI2_DEFAULT_REPLY_DESCRIPTOR {
        u8              ReplyFlags;                 /* 0x00 */
        u8              MSIxIndex;                  /* 0x01 */
-       u16             DescriptorTypeDependent1;   /* 0x02 */
-       u32             DescriptorTypeDependent2;   /* 0x04 */
+       __le16          DescriptorTypeDependent1;   /* 0x02 */
+       __le32          DescriptorTypeDependent2;   /* 0x04 */
 };
 
 /* Address Reply Descriptor */
 struct MPI2_ADDRESS_REPLY_DESCRIPTOR {
        u8              ReplyFlags;                 /* 0x00 */
        u8              MSIxIndex;                  /* 0x01 */
-       u16             SMID;                       /* 0x02 */
-       u32             ReplyFrameAddress;          /* 0x04 */
+       __le16          SMID;                       /* 0x02 */
+       __le32          ReplyFrameAddress;          /* 0x04 */
 };
 
 /* SCSI IO Success Reply Descriptor */
 struct MPI2_SCSI_IO_SUCCESS_REPLY_DESCRIPTOR {
        u8              ReplyFlags;                 /* 0x00 */
        u8              MSIxIndex;                  /* 0x01 */
-       u16             SMID;                       /* 0x02 */
-       u16             TaskTag;                    /* 0x04 */
-       u16             Reserved1;                  /* 0x06 */
+       __le16          SMID;                       /* 0x02 */
+       __le16          TaskTag;                    /* 0x04 */
+       __le16          Reserved1;                  /* 0x06 */
 };
 
 /* TargetAssist Success Reply Descriptor */
 struct MPI2_TARGETASSIST_SUCCESS_REPLY_DESCRIPTOR {
        u8              ReplyFlags;                 /* 0x00 */
        u8              MSIxIndex;                  /* 0x01 */
-       u16             SMID;                       /* 0x02 */
+       __le16          SMID;                       /* 0x02 */
        u8              SequenceNumber;             /* 0x04 */
        u8              Reserved1;                  /* 0x05 */
-       u16             IoIndex;                    /* 0x06 */
+       __le16          IoIndex;                    /* 0x06 */
 };
 
 /* Target Command Buffer Reply Descriptor */
@@ -414,16 +414,16 @@ struct MPI2_TARGET_COMMAND_BUFFER_REPLY_DESCRIPTOR {
        u8              MSIxIndex;                  /* 0x01 */
        u8              VP_ID;                      /* 0x02 */
        u8              Flags;                      /* 0x03 */
-       u16             InitiatorDevHandle;         /* 0x04 */
-       u16             IoIndex;                    /* 0x06 */
+       __le16          InitiatorDevHandle;         /* 0x04 */
+       __le16          IoIndex;                    /* 0x06 */
 };
 
 /* RAID Accelerator Success Reply Descriptor */
 struct MPI2_RAID_ACCELERATOR_SUCCESS_REPLY_DESCRIPTOR {
        u8              ReplyFlags;                 /* 0x00 */
        u8              MSIxIndex;                  /* 0x01 */
-       u16             SMID;                       /* 0x02 */
-       u32             Reserved;                   /* 0x04 */
+       __le16          SMID;                       /* 0x02 */
+       __le32          Reserved;                   /* 0x04 */
 };
 
 /* union of Reply Descriptors */
@@ -435,7 +435,7 @@ union MPI2_REPLY_DESCRIPTORS_UNION {
        struct MPI2_TARGET_COMMAND_BUFFER_REPLY_DESCRIPTOR TargetCommandBuffer;
        struct MPI2_RAID_ACCELERATOR_SUCCESS_REPLY_DESCRIPTOR
        RAIDAcceleratorSuccess;
-       u64                                             Words;
+       __le64                                             Words;
 };
 
 /* IOCInit Request message */
@@ -444,28 +444,28 @@ struct MPI2_IOC_INIT_REQUEST {
        u8                      Reserved1;                      /* 0x01 */
        u8                      ChainOffset;                    /* 0x02 */
        u8                      Function;                       /* 0x03 */
-       u16                     Reserved2;                      /* 0x04 */
+       __le16                  Reserved2;                      /* 0x04 */
        u8                      Reserved3;                      /* 0x06 */
        u8                      MsgFlags;                       /* 0x07 */
        u8                      VP_ID;                          /* 0x08 */
        u8                      VF_ID;                          /* 0x09 */
-       u16                     Reserved4;                      /* 0x0A */
-       u16                     MsgVersion;                     /* 0x0C */
-       u16                     HeaderVersion;                  /* 0x0E */
+       __le16                  Reserved4;                      /* 0x0A */
+       __le16                  MsgVersion;                     /* 0x0C */
+       __le16                  HeaderVersion;                  /* 0x0E */
        u32                     Reserved5;                      /* 0x10 */
-       u16                     Reserved6;                      /* 0x14 */
+       __le16                  Reserved6;                      /* 0x14 */
        u8                      Reserved7;                      /* 0x16 */
        u8                      HostMSIxVectors;                /* 0x17 */
-       u16                     Reserved8;                      /* 0x18 */
-       u16                     SystemRequestFrameSize;         /* 0x1A */
-       u16                     ReplyDescriptorPostQueueDepth;  /* 0x1C */
-       u16                     ReplyFreeQueueDepth;            /* 0x1E */
-       u32                     SenseBufferAddressHigh;         /* 0x20 */
-       u32                     SystemReplyAddressHigh;         /* 0x24 */
-       u64                     SystemRequestFrameBaseAddress;  /* 0x28 */
-       u64                     ReplyDescriptorPostQueueAddress;/* 0x30 */
-       u64                     ReplyFreeQueueAddress;          /* 0x38 */
-       u64                     TimeStamp;                      /* 0x40 */
+       __le16                  Reserved8;                      /* 0x18 */
+       __le16                  SystemRequestFrameSize;         /* 0x1A */
+       __le16                  ReplyDescriptorPostQueueDepth;  /* 0x1C */
+       __le16                  ReplyFreeQueueDepth;            /* 0x1E */
+       __le32                  SenseBufferAddressHigh;         /* 0x20 */
+       __le32                  SystemReplyAddressHigh;         /* 0x24 */
+       __le64                  SystemRequestFrameBaseAddress;  /* 0x28 */
+       __le64                  ReplyDescriptorPostQueueAddress;/* 0x30 */
+       __le64                  ReplyFreeQueueAddress;          /* 0x38 */
+       __le64                  TimeStamp;                      /* 0x40 */
 };
 
 /* mrpriv defines */
@@ -491,41 +491,41 @@ struct MPI2_IOC_INIT_REQUEST {
 #define MR_DCMD_LD_VF_MAP_GET_ALL_LDS       0x03150200
 
 struct MR_DEV_HANDLE_INFO {
-       u16     curDevHdl;
+       __le16  curDevHdl;
        u8      validHandles;
        u8      reserved;
-       u16     devHandle[2];
+       __le16  devHandle[2];
 };
 
 struct MR_ARRAY_INFO {
-       u16      pd[MAX_RAIDMAP_ROW_SIZE];
+       __le16  pd[MAX_RAIDMAP_ROW_SIZE];
 };
 
 struct MR_QUAD_ELEMENT {
-       u64     logStart;
-       u64     logEnd;
-       u64     offsetInSpan;
-       u32     diff;
-       u32     reserved1;
+       __le64     logStart;
+       __le64     logEnd;
+       __le64     offsetInSpan;
+       __le32     diff;
+       __le32     reserved1;
 };
 
 struct MR_SPAN_INFO {
-       u32             noElements;
-       u32             reserved1;
+       __le32             noElements;
+       __le32             reserved1;
        struct MR_QUAD_ELEMENT quad[MAX_RAIDMAP_SPAN_DEPTH];
 };
 
 struct MR_LD_SPAN {
-       u64      startBlk;
-       u64      numBlks;
-       u16      arrayRef;
+       __le64   startBlk;
+       __le64   numBlks;
+       __le16   arrayRef;
        u8       spanRowSize;
        u8       spanRowDataSize;
        u8       reserved[4];
 };
 
 struct MR_SPAN_BLOCK_INFO {
-       u64          num_rows;
+       __le64          num_rows;
        struct MR_LD_SPAN   span;
        struct MR_SPAN_INFO block_span_info;
 };
@@ -558,8 +558,8 @@ struct MR_LD_RAID {
                u32     reserved4:7;
 #endif
        } capability;
-       u32     reserved6;
-       u64     size;
+       __le32     reserved6;
+       __le64     size;
        u8      spanDepth;
        u8      level;
        u8      stripeShift;
@@ -568,12 +568,12 @@ struct MR_LD_RAID {
        u8      writeMode;
        u8      PRL;
        u8      SRL;
-       u16     targetId;
+       __le16     targetId;
        u8      ldState;
        u8      regTypeReqOnWrite;
        u8      modFactor;
        u8      regTypeReqOnRead;
-       u16     seqNum;
+       __le16     seqNum;
 
        struct {
                u32 ldSyncRequired:1;
@@ -592,20 +592,20 @@ struct MR_LD_SPAN_MAP {
 };
 
 struct MR_FW_RAID_MAP {
-       u32                 totalSize;
+       __le32                 totalSize;
        union {
                struct {
-                       u32         maxLd;
-                       u32         maxSpanDepth;
-                       u32         maxRowSize;
-                       u32         maxPdCount;
-                       u32         maxArrays;
+                       __le32         maxLd;
+                       __le32         maxSpanDepth;
+                       __le32         maxRowSize;
+                       __le32         maxPdCount;
+                       __le32         maxArrays;
                } validationInfo;
-               u32             version[5];
+               __le32             version[5];
        };
 
-       u32                 ldCount;
-       u32                 Reserved1;
+       __le32                 ldCount;
+       __le32                 Reserved1;
        u8                  ldTgtIdToLd[MAX_RAIDMAP_LOGICAL_DRIVES+
                                        MAX_RAIDMAP_VIEWS];
        u8                  fpPdIoTimeoutSec;
@@ -620,7 +620,7 @@ struct IO_REQUEST_INFO {
        u32 numBlocks;
        u16 ldTgtId;
        u8 isRead;
-       u16 devHandle;
+       __le16 devHandle;
        u64 pdBlock;
        u8 fpOkForIo;
        u8 IoforUnevenSpan;
@@ -634,7 +634,7 @@ struct IO_REQUEST_INFO {
 struct MR_LD_TARGET_SYNC {
        u8  targetId;
        u8  reserved;
-       u16 seqNum;
+       __le16 seqNum;
 };
 
 #define IEEE_SGE_FLAGS_ADDR_MASK            (0x03)
@@ -679,7 +679,6 @@ struct megasas_cmd_fusion {
         */
        u32 sync_cmd_idx;
        u32 index;
-       u8 flags;
        u8 pd_r1_lb;
 };
 
@@ -720,27 +719,27 @@ struct MR_DRV_RAID_MAP {
         * This feild will be manupulated by driver for ext raid map,
         * else pick the value from firmware raid map.
         */
-       u32                 totalSize;
+       __le32                 totalSize;
 
        union {
        struct {
-               u32         maxLd;
-               u32         maxSpanDepth;
-               u32         maxRowSize;
-               u32         maxPdCount;
-               u32         maxArrays;
+               __le32         maxLd;
+               __le32         maxSpanDepth;
+               __le32         maxRowSize;
+               __le32         maxPdCount;
+               __le32         maxArrays;
        } validationInfo;
-       u32             version[5];
+       __le32             version[5];
        };
 
        /* timeout value used by driver in FP IOs*/
        u8                  fpPdIoTimeoutSec;
        u8                  reserved2[7];
 
-       u16                 ldCount;
-       u16                 arCount;
-       u16                 spanCount;
-       u16                 reserve3;
+       __le16                 ldCount;
+       __le16                 arCount;
+       __le16                 spanCount;
+       __le16                 reserve3;
 
        struct MR_DEV_HANDLE_INFO  devHndlInfo[MAX_RAIDMAP_PHYSICAL_DEVICES];
        u8                  ldTgtIdToLd[MAX_LOGICAL_DRIVES_EXT];
@@ -779,10 +778,10 @@ struct MR_FW_RAID_MAP_EXT {
        u8                  fpPdIoTimeoutSec;
        u8                  reserved2[7];
 
-       u16                 ldCount;
-       u16                 arCount;
-       u16                 spanCount;
-       u16                 reserve3;
+       __le16                 ldCount;
+       __le16                 arCount;
+       __le16                 spanCount;
+       __le16                 reserve3;
 
        struct MR_DEV_HANDLE_INFO  devHndlInfo[MAX_RAIDMAP_PHYSICAL_DEVICES];
        u8                  ldTgtIdToLd[MAX_LOGICAL_DRIVES_EXT];
@@ -792,10 +791,6 @@ struct MR_FW_RAID_MAP_EXT {
 
 struct fusion_context {
        struct megasas_cmd_fusion **cmd_list;
-       struct list_head cmd_pool;
-
-       spinlock_t mpt_pool_lock;
-
        dma_addr_t req_frames_desc_phys;
        u8 *req_frames_desc;
 
@@ -839,10 +834,10 @@ struct fusion_context {
 };
 
 union desc_value {
-       u64 word;
+       __le64 word;
        struct {
-               u32 low;
-               u32 high;
+               __le32 low;
+               __le32 high;
        } u;
 };
 
index 53030b0e8015d833719b276adad1d8898cb89780..d40d734aa53a522e2e115d70e24f6d50804238c7 100644 (file)
@@ -56,7 +56,6 @@ static struct scsi_host_template mvs_sht = {
        .change_queue_depth     = sas_change_queue_depth,
        .bios_param             = sas_bios_param,
        .can_queue              = 1,
-       .cmd_per_lun            = 1,
        .this_id                = -1,
        .sg_tablesize           = SG_ALL,
        .max_sectors            = SCSI_DEFAULT_MAX_SECTORS,
index c6077cefbeca30d6422d6c4ac6b47d5e9caedbd2..53c84771f0e8939f53e561cad198c40ac337c2f2 100644 (file)
@@ -274,7 +274,6 @@ static struct scsi_host_template nsp32_template = {
        .can_queue                      = 1,
        .sg_tablesize                   = NSP32_SG_SIZE,
        .max_sectors                    = 128,
-       .cmd_per_lun                    = 1,
        .this_id                        = NSP32_HOST_SCSIID,
        .use_clustering                 = DISABLE_CLUSTERING,
        .eh_abort_handler               = nsp32_eh_abort,
index 1b6c8833a304e645e034f91c894f4e7e0b486f13..5fb6eefc65413c8063c29541b422b31588d2bded 100644 (file)
@@ -86,7 +86,6 @@ static struct scsi_host_template nsp_driver_template = {
        .can_queue               = 1,
        .this_id                 = NSP_INITIATOR_ID,
        .sg_tablesize            = SG_ALL,
-       .cmd_per_lun             = 1,
        .use_clustering          = DISABLE_CLUSTERING,
 };
 
index bcaf89fe0c9ed886099925a53d1f2cc27b46f412..c670dc704c74eb09ca90be4f2cc49e3ede651926 100644 (file)
@@ -72,7 +72,6 @@ static struct scsi_host_template qlogicfas_driver_template = {
        .can_queue              = 1,
        .this_id                = -1,
        .sg_tablesize           = SG_ALL,
-       .cmd_per_lun            = 1,
        .use_clustering         = DISABLE_CLUSTERING,
 };
 
index 155f9573021f08af0d733b9abcf91db2c151361e..20011c8afbb5c5b00155c61f5c2f5b84c7cc69f4 100644 (file)
@@ -680,7 +680,6 @@ static struct scsi_host_template sym53c500_driver_template = {
      .can_queue                        = 1,
      .this_id                  = 7,
      .sg_tablesize             = 32,
-     .cmd_per_lun              = 1,
      .use_clustering           = ENABLE_CLUSTERING,
      .shost_attrs              = SYM53C500_shost_attrs
 };
index 65555916d3b84e89fefa34817375276f0831342e..a132f2664d2f22e607d934bb51469852ab93bb88 100644 (file)
@@ -78,7 +78,6 @@ static struct scsi_host_template pm8001_sht = {
        .change_queue_depth     = sas_change_queue_depth,
        .bios_param             = sas_bios_param,
        .can_queue              = 1,
-       .cmd_per_lun            = 1,
        .this_id                = -1,
        .sg_tablesize           = SG_ALL,
        .max_sectors            = SCSI_DEFAULT_MAX_SECTORS,
index 1db8b26063b4b93eb2372f960ae04e72d91f7524..ee00e27ba39602264769445e7fb44d6be637b344 100644 (file)
@@ -974,7 +974,6 @@ static struct scsi_host_template ppa_template = {
        .bios_param             = ppa_biosparam,
        .this_id                = -1,
        .sg_tablesize           = SG_ALL,
-       .cmd_per_lun            = 1,
        .use_clustering         = ENABLE_CLUSTERING,
        .can_queue              = 1,
        .slave_alloc            = ppa_adjust_queue,
index 5298def3373339bb3e0b917b756447395c9692bf..4924424d20fe8c691f221f3e23bb4dcb9b734a21 100644 (file)
@@ -347,7 +347,6 @@ static struct scsi_host_template ps3rom_host_template = {
        .can_queue =            1,
        .this_id =              7,
        .sg_tablesize =         SG_ALL,
-       .cmd_per_lun =          1,
        .emulated =             1,              /* only sg driver uses this */
        .max_sectors =          PS3ROM_MAX_SECTORS,
        .use_clustering =       ENABLE_CLUSTERING,
index c68a66e8cfc195ec1e78e2022efed4d9d87f6845..5d0ec42a9317d6a08a2b99fd424339c4554f4787 100644 (file)
@@ -4217,7 +4217,6 @@ static struct scsi_host_template qla1280_driver_template = {
        .can_queue              = 0xfffff,
        .this_id                = -1,
        .sg_tablesize           = SG_ALL,
-       .cmd_per_lun            = 1,
        .use_clustering         = ENABLE_CLUSTERING,
 };
 
index 285cb204f30051897ad08582c4e329eb2f359fa1..664013115c9da7d7912d0b22fa34ddf91e7b977b 100644 (file)
@@ -708,7 +708,7 @@ qla2x00_initialize_adapter(scsi_qla_host_t *vha)
                if (rval != QLA_SUCCESS) {
                        ql_log(ql_log_warn, vha, 0x00d4,
                            "Unable to initialize ISP84XX.\n");
-               qla84xx_put_chip(vha);
+                       qla84xx_put_chip(vha);
                }
        }
 
index a1ab25fca8742d2fcef03ca2a8646990799810cb..36fbd4c7af8f50e52cd0289bc31d9ccfca8bbe24 100644 (file)
@@ -2797,10 +2797,10 @@ qla2x00_start_bidir(srb_t *sp, struct scsi_qla_host *vha, uint32_t tot_dsds)
        handle = req->current_outstanding_cmd;
        for (index = 1; index < req->num_outstanding_cmds; index++) {
                handle++;
-       if (handle == req->num_outstanding_cmds)
-               handle = 1;
-       if (!req->outstanding_cmds[handle])
-               break;
+               if (handle == req->num_outstanding_cmds)
+                       handle = 1;
+               if (!req->outstanding_cmds[handle])
+                       break;
        }
 
        if (index == req->num_outstanding_cmds) {
index 6dc14cd782b2a5d7d6b96ff3eb1167cfde261893..5559d5e75bbf99bf98765636486d38d945336081 100644 (file)
@@ -1580,7 +1580,7 @@ qla24xx_tm_iocb_entry(scsi_qla_host_t *vha, struct req_que *req, void *tsk)
                        ql_log(ql_log_warn, fcport->vha, 0x503c,
                            "Async-%s error - hdl=%x response(%x).\n",
                            type, sp->handle, sts->data[3]);
-               iocb->u.tmf.data = QLA_FUNCTION_FAILED;
+                       iocb->u.tmf.data = QLA_FUNCTION_FAILED;
                }
        }
 
@@ -1979,7 +1979,7 @@ qla25xx_process_bidir_status_iocb(scsi_qla_host_t *vha, void *pkt,
                rval = EXT_STATUS_ERR;
                break;
        }
-               bsg_job->reply->reply_payload_rcv_len = 0;
+       bsg_job->reply->reply_payload_rcv_len = 0;
 
 done:
        /* Return the vendor specific reply to API */
index 7d2b18f2675ce2ef28b41db6cce442e5ba3ad390..1620b0ec977baab575cb79de5388a027c5d40af6 100644 (file)
@@ -1843,7 +1843,7 @@ qla82xx_set_product_offset(struct qla_hw_data *ha)
 
        ptab_desc = qla82xx_get_table_desc(unirom,
                 QLA82XX_URI_DIR_SECT_PRODUCT_TBL);
-       if (!ptab_desc)
+       if (!ptab_desc)
                return -1;
 
        entries = cpu_to_le32(ptab_desc->num_entries);
index ed4d6b6b53e3613b3a7f35e03f5f341655b2ab7c..000c57e4d03389768bfb31f901c1600a82f95dfc 100644 (file)
@@ -397,11 +397,11 @@ qla8044_idc_lock(struct qla_hw_data *ha)
                                 * has the lock, wait for 2secs
                                 * and retry
                                 */
-                                ql_dbg(ql_dbg_p3p, vha, 0xb08a,
-                                    "%s: IDC lock Recovery by %d "
-                                    "failed, Retrying timeout\n", __func__,
-                                    ha->portnum);
-                                timeout = 0;
+                               ql_dbg(ql_dbg_p3p, vha, 0xb08a,
+                                      "%s: IDC lock Recovery by %d "
+                                      "failed, Retrying timeout\n", __func__,
+                                      ha->portnum);
+                               timeout = 0;
                        }
                }
                msleep(QLA8044_DRV_LOCK_MSLEEP);
@@ -3141,8 +3141,7 @@ qla8044_minidump_process_rdmdio(struct scsi_qla_host *vha,
                        goto error;
 
                addr7 = addr2 - (4 * stride1);
-                       data = qla8044_ipmdio_rd_reg(vha, addr1, addr3,
-                           mask, addr7);
+               data = qla8044_ipmdio_rd_reg(vha, addr1, addr3, mask, addr7);
                if (data == -1)
                        goto error;
 
index 7462dd70b1506b35ea9a440012cb26340c25a527..a28815b8276f090901498dafaf39d12c97db42eb 100644 (file)
@@ -4418,7 +4418,10 @@ retry_lock2:
 void
 qla83xx_idc_unlock(scsi_qla_host_t *base_vha, uint16_t requester_id)
 {
-       uint16_t options = (requester_id << 15) | BIT_7, retry;
+#if 0
+       uint16_t options = (requester_id << 15) | BIT_7;
+#endif
+       uint16_t retry;
        uint32_t data;
        struct qla_hw_data *ha = base_vha->hw;
 
@@ -4454,6 +4457,7 @@ retry_unlock:
 
        return;
 
+#if 0
        /* XXX: IDC-unlock implementation using access-control mbx */
        retry = 0;
 retry_unlock2:
@@ -4469,6 +4473,7 @@ retry_unlock2:
        }
 
        return;
+#endif
 }
 
 int
index fe8a8d157e225df6018f101ec0f3a0be3d3e786f..4a484d60be0d4db6449bde1e77af21fe58c3639b 100644 (file)
@@ -3712,6 +3712,14 @@ static int qlt_24xx_handle_els(struct scsi_qla_host *vha,
 
 static int qlt_set_data_offset(struct qla_tgt_cmd *cmd, uint32_t offset)
 {
+#if 1
+       /*
+        * FIXME: Reject non zero SRR relative offset until we can test
+        * this code properly.
+        */
+       pr_debug("Rejecting non zero SRR rel_offs: %u\n", offset);
+       return -1;
+#else
        struct scatterlist *sg, *sgp, *sg_srr, *sg_srr_start = NULL;
        size_t first_offset = 0, rem_offset = offset, tmp = 0;
        int i, sg_srr_cnt, bufflen = 0;
@@ -3721,13 +3729,6 @@ static int qlt_set_data_offset(struct qla_tgt_cmd *cmd, uint32_t offset)
            "cmd->sg_cnt: %u, direction: %d\n",
            cmd, cmd->sg, cmd->sg_cnt, cmd->dma_data_direction);
 
-       /*
-        * FIXME: Reject non zero SRR relative offset until we can test
-        * this code properly.
-        */
-       pr_debug("Rejecting non zero SRR rel_offs: %u\n", offset);
-       return -1;
-
        if (!cmd->sg || !cmd->sg_cnt) {
                ql_dbg(ql_dbg_tgt, cmd->vha, 0xe055,
                    "Missing cmd->sg or zero cmd->sg_cnt in"
@@ -3810,6 +3811,7 @@ static int qlt_set_data_offset(struct qla_tgt_cmd *cmd, uint32_t offset)
                BUG();
 
        return 0;
+#endif
 }
 
 static inline int qlt_srr_adjust_data(struct qla_tgt_cmd *cmd,
index 556c1525f881650261187b01e60165a36ce81c00..5d4f8e67fb2569bce0b787243b05e27ea95a9170 100644 (file)
@@ -828,7 +828,7 @@ void qla4_83xx_read_reset_template(struct scsi_qla_host *ha)
        ret_val = qla4_83xx_flash_read_u32(ha, addr, p_buff,
                                           tmplt_hdr_def_size);
        if (ret_val != QLA_SUCCESS) {
-               ql4_printk(KERN_ERR, ha, "%s: Failed to read reset tempelate\n",
+               ql4_printk(KERN_ERR, ha, "%s: Failed to read reset template\n",
                           __func__);
                goto exit_read_template_error;
        }
index 9f92cbf964775d273f80319eb772fc9e7ba15432..415ee5eb3fc7e79518c667263629141ac5c43031 100644 (file)
@@ -571,7 +571,7 @@ static int qla4_83xx_pre_loopback_config(struct scsi_qla_host *ha,
 
        if ((config & ENABLE_INTERNAL_LOOPBACK) ||
            (config & ENABLE_EXTERNAL_LOOPBACK)) {
-               ql4_printk(KERN_INFO, ha, "%s: Loopback diagnostics already in progress. Invalid requiest\n",
+               ql4_printk(KERN_INFO, ha, "%s: Loopback diagnostics already in progress. Invalid request\n",
                           __func__);
                goto exit_pre_loopback_config;
        }
index a22bb1b40ce2a12d344b741524ee75cee2668d58..61cac87fb86fd4323033834f6c2836ed05b9c0bf 100644 (file)
@@ -193,7 +193,6 @@ static struct scsi_host_template qlogicfas_driver_template = {
        .can_queue              = 1,
        .this_id                = -1,
        .sg_tablesize           = SG_ALL,
-       .cmd_per_lun            = 1,
        .use_clustering         = DISABLE_CLUSTERING,
 };
 
index fe122700cad811f017c48052d45f31f5fb8018dd..676385ff28efb0d4dfc392f8dddf7a2c54c32aef 100644 (file)
@@ -1287,7 +1287,6 @@ static struct scsi_host_template qpti_template = {
        .can_queue              = QLOGICPTI_REQ_QUEUE_LEN,
        .this_id                = 7,
        .sg_tablesize           = QLOGICPTI_MAX_SG(QLOGICPTI_REQ_QUEUE_LEN),
-       .cmd_per_lun            = 1,
        .use_clustering         = ENABLE_CLUSTERING,
 };
 
index 3833bf59fb66a5b143eb7d9fa7145ac817095b6c..207d6a7a1bd0bce29ca748c8f3a4cd2aee6ad97a 100644 (file)
@@ -98,52 +98,6 @@ EXPORT_SYMBOL(scsi_sd_probe_domain);
 ASYNC_DOMAIN_EXCLUSIVE(scsi_sd_pm_domain);
 EXPORT_SYMBOL(scsi_sd_pm_domain);
 
-/* NB: These are exposed through /proc/scsi/scsi and form part of the ABI.
- * You may not alter any existing entry (although adding new ones is
- * encouraged once assigned by ANSI/INCITS T10
- */
-static const char *const scsi_device_types[] = {
-       "Direct-Access    ",
-       "Sequential-Access",
-       "Printer          ",
-       "Processor        ",
-       "WORM             ",
-       "CD-ROM           ",
-       "Scanner          ",
-       "Optical Device   ",
-       "Medium Changer   ",
-       "Communications   ",
-       "ASC IT8          ",
-       "ASC IT8          ",
-       "RAID             ",
-       "Enclosure        ",
-       "Direct-Access-RBC",
-       "Optical card     ",
-       "Bridge controller",
-       "Object storage   ",
-       "Automation/Drive ",
-       "Security Manager ",
-       "Direct-Access-ZBC",
-};
-
-/**
- * scsi_device_type - Return 17 char string indicating device type.
- * @type: type number to look up
- */
-
-const char * scsi_device_type(unsigned type)
-{
-       if (type == 0x1e)
-               return "Well-known LUN   ";
-       if (type == 0x1f)
-               return "No Device        ";
-       if (type >= ARRAY_SIZE(scsi_device_types))
-               return "Unknown          ";
-       return scsi_device_types[type];
-}
-
-EXPORT_SYMBOL(scsi_device_type);
-
 struct scsi_host_cmd_pool {
        struct kmem_cache       *cmd_slab;
        struct kmem_cache       *sense_slab;
diff --git a/drivers/scsi/scsi_common.c b/drivers/scsi/scsi_common.c
new file mode 100644 (file)
index 0000000..2ff0922
--- /dev/null
@@ -0,0 +1,178 @@
+/*
+ * SCSI functions used by both the initiator and the target code.
+ */
+
+#include <linux/bug.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <scsi/scsi_common.h>
+
+/* NB: These are exposed through /proc/scsi/scsi and form part of the ABI.
+ * You may not alter any existing entry (although adding new ones is
+ * encouraged once assigned by ANSI/INCITS T10
+ */
+static const char *const scsi_device_types[] = {
+       "Direct-Access    ",
+       "Sequential-Access",
+       "Printer          ",
+       "Processor        ",
+       "WORM             ",
+       "CD-ROM           ",
+       "Scanner          ",
+       "Optical Device   ",
+       "Medium Changer   ",
+       "Communications   ",
+       "ASC IT8          ",
+       "ASC IT8          ",
+       "RAID             ",
+       "Enclosure        ",
+       "Direct-Access-RBC",
+       "Optical card     ",
+       "Bridge controller",
+       "Object storage   ",
+       "Automation/Drive ",
+       "Security Manager ",
+       "Direct-Access-ZBC",
+};
+
+/**
+ * scsi_device_type - Return 17 char string indicating device type.
+ * @type: type number to look up
+ */
+const char *scsi_device_type(unsigned type)
+{
+       if (type == 0x1e)
+               return "Well-known LUN   ";
+       if (type == 0x1f)
+               return "No Device        ";
+       if (type >= ARRAY_SIZE(scsi_device_types))
+               return "Unknown          ";
+       return scsi_device_types[type];
+}
+EXPORT_SYMBOL(scsi_device_type);
+
+/**
+ * scsilun_to_int - convert a scsi_lun to an int
+ * @scsilun:   struct scsi_lun to be converted.
+ *
+ * Description:
+ *     Convert @scsilun from a struct scsi_lun to a four byte host byte-ordered
+ *     integer, and return the result. The caller must check for
+ *     truncation before using this function.
+ *
+ * Notes:
+ *     For a description of the LUN format, post SCSI-3 see the SCSI
+ *     Architecture Model, for SCSI-3 see the SCSI Controller Commands.
+ *
+ *     Given a struct scsi_lun of: d2 04 0b 03 00 00 00 00, this function
+ *     returns the integer: 0x0b03d204
+ *
+ *     This encoding will return a standard integer LUN for LUNs smaller
+ *     than 256, which typically use a single level LUN structure with
+ *     addressing method 0.
+ */
+u64 scsilun_to_int(struct scsi_lun *scsilun)
+{
+       int i;
+       u64 lun;
+
+       lun = 0;
+       for (i = 0; i < sizeof(lun); i += 2)
+               lun = lun | (((u64)scsilun->scsi_lun[i] << ((i + 1) * 8)) |
+                            ((u64)scsilun->scsi_lun[i + 1] << (i * 8)));
+       return lun;
+}
+EXPORT_SYMBOL(scsilun_to_int);
+
+/**
+ * int_to_scsilun - reverts an int into a scsi_lun
+ * @lun:        integer to be reverted
+ * @scsilun:   struct scsi_lun to be set.
+ *
+ * Description:
+ *     Reverts the functionality of the scsilun_to_int, which packed
+ *     an 8-byte lun value into an int. This routine unpacks the int
+ *     back into the lun value.
+ *
+ * Notes:
+ *     Given an integer : 0x0b03d204,  this function returns a
+ *     struct scsi_lun of: d2 04 0b 03 00 00 00 00
+ *
+ */
+void int_to_scsilun(u64 lun, struct scsi_lun *scsilun)
+{
+       int i;
+
+       memset(scsilun->scsi_lun, 0, sizeof(scsilun->scsi_lun));
+
+       for (i = 0; i < sizeof(lun); i += 2) {
+               scsilun->scsi_lun[i] = (lun >> 8) & 0xFF;
+               scsilun->scsi_lun[i+1] = lun & 0xFF;
+               lun = lun >> 16;
+       }
+}
+EXPORT_SYMBOL(int_to_scsilun);
+
+/**
+ * scsi_normalize_sense - normalize main elements from either fixed or
+ *                     descriptor sense data format into a common format.
+ *
+ * @sense_buffer:      byte array containing sense data returned by device
+ * @sb_len:            number of valid bytes in sense_buffer
+ * @sshdr:             pointer to instance of structure that common
+ *                     elements are written to.
+ *
+ * Notes:
+ *     The "main elements" from sense data are: response_code, sense_key,
+ *     asc, ascq and additional_length (only for descriptor format).
+ *
+ *     Typically this function can be called after a device has
+ *     responded to a SCSI command with the CHECK_CONDITION status.
+ *
+ * Return value:
+ *     true if valid sense data information found, else false;
+ */
+bool scsi_normalize_sense(const u8 *sense_buffer, int sb_len,
+                         struct scsi_sense_hdr *sshdr)
+{
+       if (!sense_buffer || !sb_len)
+               return false;
+
+       memset(sshdr, 0, sizeof(struct scsi_sense_hdr));
+
+       sshdr->response_code = (sense_buffer[0] & 0x7f);
+
+       if (!scsi_sense_valid(sshdr))
+               return false;
+
+       if (sshdr->response_code >= 0x72) {
+               /*
+                * descriptor format
+                */
+               if (sb_len > 1)
+                       sshdr->sense_key = (sense_buffer[1] & 0xf);
+               if (sb_len > 2)
+                       sshdr->asc = sense_buffer[2];
+               if (sb_len > 3)
+                       sshdr->ascq = sense_buffer[3];
+               if (sb_len > 7)
+                       sshdr->additional_length = sense_buffer[7];
+       } else {
+               /*
+                * fixed format
+                */
+               if (sb_len > 2)
+                       sshdr->sense_key = (sense_buffer[2] & 0xf);
+               if (sb_len > 7) {
+                       sb_len = (sb_len < (sense_buffer[7] + 8)) ?
+                                        sb_len : (sense_buffer[7] + 8);
+                       if (sb_len > 12)
+                               sshdr->asc = sense_buffer[12];
+                       if (sb_len > 13)
+                               sshdr->ascq = sense_buffer[13];
+               }
+       }
+
+       return true;
+}
+EXPORT_SYMBOL(scsi_normalize_sense);
index c95a4e943fc6843b78d8120955318d76547a956e..106884a5444e1cb6f61057c29f813c4d713760fa 100644 (file)
@@ -2399,70 +2399,6 @@ out_put_autopm_host:
 }
 EXPORT_SYMBOL(scsi_ioctl_reset);
 
-/**
- * scsi_normalize_sense - normalize main elements from either fixed or
- *                     descriptor sense data format into a common format.
- *
- * @sense_buffer:      byte array containing sense data returned by device
- * @sb_len:            number of valid bytes in sense_buffer
- * @sshdr:             pointer to instance of structure that common
- *                     elements are written to.
- *
- * Notes:
- *     The "main elements" from sense data are: response_code, sense_key,
- *     asc, ascq and additional_length (only for descriptor format).
- *
- *     Typically this function can be called after a device has
- *     responded to a SCSI command with the CHECK_CONDITION status.
- *
- * Return value:
- *     true if valid sense data information found, else false;
- */
-bool scsi_normalize_sense(const u8 *sense_buffer, int sb_len,
-                         struct scsi_sense_hdr *sshdr)
-{
-       if (!sense_buffer || !sb_len)
-               return false;
-
-       memset(sshdr, 0, sizeof(struct scsi_sense_hdr));
-
-       sshdr->response_code = (sense_buffer[0] & 0x7f);
-
-       if (!scsi_sense_valid(sshdr))
-               return false;
-
-       if (sshdr->response_code >= 0x72) {
-               /*
-                * descriptor format
-                */
-               if (sb_len > 1)
-                       sshdr->sense_key = (sense_buffer[1] & 0xf);
-               if (sb_len > 2)
-                       sshdr->asc = sense_buffer[2];
-               if (sb_len > 3)
-                       sshdr->ascq = sense_buffer[3];
-               if (sb_len > 7)
-                       sshdr->additional_length = sense_buffer[7];
-       } else {
-               /*
-                * fixed format
-                */
-               if (sb_len > 2)
-                       sshdr->sense_key = (sense_buffer[2] & 0xf);
-               if (sb_len > 7) {
-                       sb_len = (sb_len < (sense_buffer[7] + 8)) ?
-                                        sb_len : (sense_buffer[7] + 8);
-                       if (sb_len > 12)
-                               sshdr->asc = sense_buffer[12];
-                       if (sb_len > 13)
-                               sshdr->ascq = sense_buffer[13];
-               }
-       }
-
-       return true;
-}
-EXPORT_SYMBOL(scsi_normalize_sense);
-
 bool scsi_command_normalize_sense(const struct scsi_cmnd *cmd,
                                  struct scsi_sense_hdr *sshdr)
 {
index 6efab1c455e158a71a2792c07d9b6d3fadc7595c..f9f3f8203d42709d9957aa27b1be471acb612046 100644 (file)
@@ -280,7 +280,8 @@ static struct scsi_device *scsi_alloc_sdev(struct scsi_target *starget,
                                    sdev->host->cmd_per_lun, shost->bqt,
                                    shost->hostt->tag_alloc_policy);
        }
-       scsi_change_queue_depth(sdev, sdev->host->cmd_per_lun);
+       scsi_change_queue_depth(sdev, sdev->host->cmd_per_lun ?
+                                       sdev->host->cmd_per_lun : 1);
 
        scsi_sysfs_device_initialize(sdev);
 
@@ -1268,68 +1269,6 @@ static void scsi_sequential_lun_scan(struct scsi_target *starget,
                        return;
 }
 
-/**
- * scsilun_to_int - convert a scsi_lun to an int
- * @scsilun:   struct scsi_lun to be converted.
- *
- * Description:
- *     Convert @scsilun from a struct scsi_lun to a four byte host byte-ordered
- *     integer, and return the result. The caller must check for
- *     truncation before using this function.
- *
- * Notes:
- *     For a description of the LUN format, post SCSI-3 see the SCSI
- *     Architecture Model, for SCSI-3 see the SCSI Controller Commands.
- *
- *     Given a struct scsi_lun of: d2 04 0b 03 00 00 00 00, this function
- *     returns the integer: 0x0b03d204
- *
- *     This encoding will return a standard integer LUN for LUNs smaller
- *     than 256, which typically use a single level LUN structure with
- *     addressing method 0.
- **/
-u64 scsilun_to_int(struct scsi_lun *scsilun)
-{
-       int i;
-       u64 lun;
-
-       lun = 0;
-       for (i = 0; i < sizeof(lun); i += 2)
-               lun = lun | (((u64)scsilun->scsi_lun[i] << ((i + 1) * 8)) |
-                            ((u64)scsilun->scsi_lun[i + 1] << (i * 8)));
-       return lun;
-}
-EXPORT_SYMBOL(scsilun_to_int);
-
-/**
- * int_to_scsilun - reverts an int into a scsi_lun
- * @lun:        integer to be reverted
- * @scsilun:   struct scsi_lun to be set.
- *
- * Description:
- *     Reverts the functionality of the scsilun_to_int, which packed
- *     an 8-byte lun value into an int. This routine unpacks the int
- *     back into the lun value.
- *
- * Notes:
- *     Given an integer : 0x0b03d204,  this function returns a
- *     struct scsi_lun of: d2 04 0b 03 00 00 00 00
- *
- **/
-void int_to_scsilun(u64 lun, struct scsi_lun *scsilun)
-{
-       int i;
-
-       memset(scsilun->scsi_lun, 0, sizeof(scsilun->scsi_lun));
-
-       for (i = 0; i < sizeof(lun); i += 2) {
-               scsilun->scsi_lun[i] = (lun >> 8) & 0xFF;
-               scsilun->scsi_lun[i+1] = lun & 0xFF;
-               lun = lun >> 16;
-       }
-}
-EXPORT_SYMBOL(int_to_scsilun);
-
 /**
  * scsi_report_lun_scan - Scan using SCSI REPORT LUN results
  * @starget: which target
index 67d43e35693df9e9e119bb6a4c2faac56f1bc260..55647aae065cf45795d13326b6056c43f19b4dc6 100644 (file)
@@ -204,6 +204,8 @@ iscsi_create_endpoint(int dd_size)
                                        iscsi_match_epid);
                if (!dev)
                        break;
+               else
+                       put_device(dev);
        }
        if (id == ISCSI_MAX_EPID) {
                printk(KERN_ERR "Too many connections. Max supported %u\n",
index ae45bd99baed72662b1c528aef97f3b49e9aeb6a..a85292b1d09d090261f89f2f6eed018f6277abb9 100644 (file)
@@ -61,6 +61,11 @@ static inline struct Scsi_Host *rport_to_shost(struct srp_rport *r)
        return dev_to_shost(r->dev.parent);
 }
 
+static inline struct srp_rport *shost_to_rport(struct Scsi_Host *shost)
+{
+       return transport_class_to_srp_rport(&shost->shost_gendev);
+}
+
 /**
  * srp_tmo_valid() - check timeout combination validity
  * @reconnect_delay: Reconnect delay in seconds.
@@ -396,6 +401,36 @@ static void srp_reconnect_work(struct work_struct *work)
        }
 }
 
+/**
+ * scsi_request_fn_active() - number of kernel threads inside scsi_request_fn()
+ * @shost: SCSI host for which to count the number of scsi_request_fn() callers.
+ *
+ * To do: add support for scsi-mq in this function.
+ */
+static int scsi_request_fn_active(struct Scsi_Host *shost)
+{
+       struct scsi_device *sdev;
+       struct request_queue *q;
+       int request_fn_active = 0;
+
+       shost_for_each_device(sdev, shost) {
+               q = sdev->request_queue;
+
+               spin_lock_irq(q->queue_lock);
+               request_fn_active += q->request_fn_active;
+               spin_unlock_irq(q->queue_lock);
+       }
+
+       return request_fn_active;
+}
+
+/* Wait until ongoing shost->hostt->queuecommand() calls have finished. */
+static void srp_wait_for_queuecommand(struct Scsi_Host *shost)
+{
+       while (scsi_request_fn_active(shost))
+               msleep(20);
+}
+
 static void __rport_fail_io_fast(struct srp_rport *rport)
 {
        struct Scsi_Host *shost = rport_to_shost(rport);
@@ -409,8 +444,10 @@ static void __rport_fail_io_fast(struct srp_rport *rport)
 
        /* Involve the LLD if possible to terminate all I/O on the rport. */
        i = to_srp_internal(shost->transportt);
-       if (i->f->terminate_rport_io)
+       if (i->f->terminate_rport_io) {
+               srp_wait_for_queuecommand(shost);
                i->f->terminate_rport_io(rport);
+       }
 }
 
 /**
@@ -503,27 +540,6 @@ void srp_start_tl_fail_timers(struct srp_rport *rport)
 }
 EXPORT_SYMBOL(srp_start_tl_fail_timers);
 
-/**
- * scsi_request_fn_active() - number of kernel threads inside scsi_request_fn()
- * @shost: SCSI host for which to count the number of scsi_request_fn() callers.
- */
-static int scsi_request_fn_active(struct Scsi_Host *shost)
-{
-       struct scsi_device *sdev;
-       struct request_queue *q;
-       int request_fn_active = 0;
-
-       shost_for_each_device(sdev, shost) {
-               q = sdev->request_queue;
-
-               spin_lock_irq(q->queue_lock);
-               request_fn_active += q->request_fn_active;
-               spin_unlock_irq(q->queue_lock);
-       }
-
-       return request_fn_active;
-}
-
 /**
  * srp_reconnect_rport() - reconnect to an SRP target port
  * @rport: SRP target port.
@@ -559,8 +575,7 @@ int srp_reconnect_rport(struct srp_rport *rport)
        if (res)
                goto out;
        scsi_target_block(&shost->shost_gendev);
-       while (scsi_request_fn_active(shost))
-               msleep(20);
+       srp_wait_for_queuecommand(shost);
        res = rport->state != SRP_RPORT_LOST ? i->f->reconnect(rport) : -ENODEV;
        pr_debug("%s (state %d): transport.reconnect() returned %d\n",
                 dev_name(&shost->shost_gendev), rport->state, res);
@@ -618,9 +633,11 @@ static enum blk_eh_timer_return srp_timed_out(struct scsi_cmnd *scmd)
        struct scsi_device *sdev = scmd->device;
        struct Scsi_Host *shost = sdev->host;
        struct srp_internal *i = to_srp_internal(shost->transportt);
+       struct srp_rport *rport = shost_to_rport(shost);
 
        pr_debug("timeout for sdev %s\n", dev_name(&sdev->sdev_gendev));
-       return i->f->reset_timer_if_blocked && scsi_device_blocked(sdev) ?
+       return rport->fast_io_fail_tmo < 0 && rport->dev_loss_tmo < 0 &&
+               i->f->reset_timer_if_blocked && scsi_device_blocked(sdev) ?
                BLK_EH_RESET_TIMER : BLK_EH_NOT_HANDLED;
 }
 
index 7f9d65fe4fd9a441c1aa00f602f3dac14c866563..3b2fcb4fada0491c4500b42555fdef542b4399d2 100644 (file)
@@ -2988,7 +2988,8 @@ static int sd_probe(struct device *dev)
        sdkp->dev.class = &sd_disk_class;
        dev_set_name(&sdkp->dev, "%s", dev_name(dev));
 
-       if (device_add(&sdkp->dev))
+       error = device_add(&sdkp->dev);
+       if (error)
                goto out_free_index;
 
        get_device(dev);
diff --git a/drivers/scsi/snic/Makefile b/drivers/scsi/snic/Makefile
new file mode 100644 (file)
index 0000000..ef7c0dd
--- /dev/null
@@ -0,0 +1,17 @@
+obj-$(CONFIG_SCSI_SNIC) += snic.o
+
+snic-y := \
+       snic_attrs.o \
+       snic_main.o \
+       snic_res.o \
+       snic_isr.o \
+       snic_ctl.o \
+       snic_io.o \
+       snic_scsi.o \
+       snic_disc.o \
+       vnic_cq.o \
+       vnic_intr.o \
+       vnic_dev.o \
+       vnic_wq.o
+
+snic-$(CONFIG_SCSI_SNIC_DEBUG_FS) += snic_debugfs.o snic_trc.o
diff --git a/drivers/scsi/snic/cq_desc.h b/drivers/scsi/snic/cq_desc.h
new file mode 100644 (file)
index 0000000..a529056
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2014 Cisco Systems, Inc.  All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * 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 _CQ_DESC_H_
+#define _CQ_DESC_H_
+
+/*
+ * Completion queue descriptor types
+ */
+enum cq_desc_types {
+       CQ_DESC_TYPE_WQ_ENET = 0,
+       CQ_DESC_TYPE_DESC_COPY = 1,
+       CQ_DESC_TYPE_WQ_EXCH = 2,
+       CQ_DESC_TYPE_RQ_ENET = 3,
+       CQ_DESC_TYPE_RQ_FCP = 4,
+};
+
+/* Completion queue descriptor: 16B
+ *
+ * All completion queues have this basic layout.  The
+ * type_specific area is unique for each completion
+ * queue type.
+ */
+struct cq_desc {
+       __le16 completed_index;
+       __le16 q_number;
+       u8 type_specific[11];
+       u8 type_color;
+};
+
+#define CQ_DESC_TYPE_BITS        4
+#define CQ_DESC_TYPE_MASK        ((1 << CQ_DESC_TYPE_BITS) - 1)
+#define CQ_DESC_COLOR_MASK       1
+#define CQ_DESC_COLOR_SHIFT      7
+#define CQ_DESC_Q_NUM_BITS       10
+#define CQ_DESC_Q_NUM_MASK       ((1 << CQ_DESC_Q_NUM_BITS) - 1)
+#define CQ_DESC_COMP_NDX_BITS    12
+#define CQ_DESC_COMP_NDX_MASK    ((1 << CQ_DESC_COMP_NDX_BITS) - 1)
+
+static inline void cq_desc_dec(const struct cq_desc *desc_arg,
+       u8 *type, u8 *color, u16 *q_number, u16 *completed_index)
+{
+       const struct cq_desc *desc = desc_arg;
+       const u8 type_color = desc->type_color;
+
+       *color = (type_color >> CQ_DESC_COLOR_SHIFT) & CQ_DESC_COLOR_MASK;
+
+       /*
+        * Make sure color bit is read from desc *before* other fields
+        * are read from desc.  Hardware guarantees color bit is last
+        * bit (byte) written.  Adding the rmb() prevents the compiler
+        * and/or CPU from reordering the reads which would potentially
+        * result in reading stale values.
+        */
+       rmb();
+
+       *type = type_color & CQ_DESC_TYPE_MASK;
+       *q_number = le16_to_cpu(desc->q_number) & CQ_DESC_Q_NUM_MASK;
+       *completed_index = le16_to_cpu(desc->completed_index) &
+               CQ_DESC_COMP_NDX_MASK;
+}
+
+#endif /* _CQ_DESC_H_ */
diff --git a/drivers/scsi/snic/cq_enet_desc.h b/drivers/scsi/snic/cq_enet_desc.h
new file mode 100644 (file)
index 0000000..0a1be2e
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2014 Cisco Systems, Inc.  All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * 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 _CQ_ENET_DESC_H_
+#define _CQ_ENET_DESC_H_
+
+#include "cq_desc.h"
+
+/* Ethernet completion queue descriptor: 16B */
+struct cq_enet_wq_desc {
+       __le16 completed_index;
+       __le16 q_number;
+       u8 reserved[11];
+       u8 type_color;
+};
+
+static inline void cq_enet_wq_desc_dec(struct cq_enet_wq_desc *desc,
+       u8 *type, u8 *color, u16 *q_number, u16 *completed_index)
+{
+       cq_desc_dec((struct cq_desc *)desc, type,
+               color, q_number, completed_index);
+}
+
+#endif /* _CQ_ENET_DESC_H_ */
diff --git a/drivers/scsi/snic/snic.h b/drivers/scsi/snic/snic.h
new file mode 100644 (file)
index 0000000..d7f5ba6
--- /dev/null
@@ -0,0 +1,414 @@
+/*
+ * Copyright 2014 Cisco Systems, Inc.  All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * 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 _SNIC_H_
+#define _SNIC_H_
+
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/workqueue.h>
+#include <linux/bitops.h>
+#include <linux/mempool.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_host.h>
+
+#include "snic_disc.h"
+#include "snic_io.h"
+#include "snic_res.h"
+#include "snic_trc.h"
+#include "snic_stats.h"
+#include "vnic_dev.h"
+#include "vnic_wq.h"
+#include "vnic_cq.h"
+#include "vnic_intr.h"
+#include "vnic_stats.h"
+#include "vnic_snic.h"
+
+#define SNIC_DRV_NAME          "snic"
+#define SNIC_DRV_DESCRIPTION   "Cisco SCSI NIC Driver"
+#define SNIC_DRV_VERSION       "0.0.1.18"
+#define PFX                    SNIC_DRV_NAME ":"
+#define DFX                    SNIC_DRV_NAME "%d: "
+
+#define DESC_CLEAN_LOW_WATERMARK       8
+#define SNIC_UCSM_DFLT_THROTTLE_CNT_BLD 16 /* UCSM default throttle count */
+#define SNIC_MAX_IO_REQ                        50 /* scsi_cmnd tag map entries */
+#define SNIC_MIN_IO_REQ                        8  /* Min IO throttle count */
+#define SNIC_IO_LOCKS                  64 /* IO locks: power of 2 */
+#define SNIC_DFLT_QUEUE_DEPTH          32 /* Default Queue Depth */
+#define SNIC_MAX_QUEUE_DEPTH           64 /* Max Queue Depth */
+#define SNIC_DFLT_CMD_TIMEOUT          90 /* Extended tmo for FW */
+
+/*
+ * Tag bits used for special requests.
+ */
+#define SNIC_TAG_ABORT         BIT(30)         /* Tag indicating abort */
+#define SNIC_TAG_DEV_RST       BIT(29)         /* Tag for device reset */
+#define SNIC_TAG_IOCTL_DEV_RST BIT(28)         /* Tag for User Device Reset */
+#define SNIC_TAG_MASK          (BIT(24) - 1)   /* Mask for lookup */
+#define SNIC_NO_TAG            -1
+
+/*
+ * Command flags to identify the type of command and for other future use
+ */
+#define SNIC_NO_FLAGS                  0
+#define SNIC_IO_INITIALIZED            BIT(0)
+#define SNIC_IO_ISSUED                 BIT(1)
+#define SNIC_IO_DONE                   BIT(2)
+#define SNIC_IO_REQ_NULL               BIT(3)
+#define SNIC_IO_ABTS_PENDING           BIT(4)
+#define SNIC_IO_ABORTED                        BIT(5)
+#define SNIC_IO_ABTS_ISSUED            BIT(6)
+#define SNIC_IO_TERM_ISSUED            BIT(7)
+#define SNIC_IO_ABTS_TIMEDOUT          BIT(8)
+#define SNIC_IO_ABTS_TERM_DONE         BIT(9)
+#define SNIC_IO_ABTS_TERM_REQ_NULL     BIT(10)
+#define SNIC_IO_ABTS_TERM_TIMEDOUT     BIT(11)
+#define SNIC_IO_INTERNAL_TERM_PENDING  BIT(12)
+#define SNIC_IO_INTERNAL_TERM_ISSUED   BIT(13)
+#define SNIC_DEVICE_RESET              BIT(14)
+#define SNIC_DEV_RST_ISSUED            BIT(15)
+#define SNIC_DEV_RST_TIMEDOUT          BIT(16)
+#define SNIC_DEV_RST_ABTS_ISSUED       BIT(17)
+#define SNIC_DEV_RST_TERM_ISSUED       BIT(18)
+#define SNIC_DEV_RST_DONE              BIT(19)
+#define SNIC_DEV_RST_REQ_NULL          BIT(20)
+#define SNIC_DEV_RST_ABTS_DONE         BIT(21)
+#define SNIC_DEV_RST_TERM_DONE         BIT(22)
+#define SNIC_DEV_RST_ABTS_PENDING      BIT(23)
+#define SNIC_DEV_RST_PENDING           BIT(24)
+#define SNIC_DEV_RST_NOTSUP            BIT(25)
+#define SNIC_SCSI_CLEANUP              BIT(26)
+#define SNIC_HOST_RESET_ISSUED         BIT(27)
+
+#define SNIC_ABTS_TIMEOUT              30000           /* msec */
+#define SNIC_LUN_RESET_TIMEOUT         30000           /* msec */
+#define SNIC_HOST_RESET_TIMEOUT                30000           /* msec */
+
+
+/*
+ * These are protected by the hashed req_lock.
+ */
+#define CMD_SP(Cmnd)           \
+       (((struct snic_internal_io_state *)scsi_cmd_priv(Cmnd))->rqi)
+#define CMD_STATE(Cmnd)                \
+       (((struct snic_internal_io_state *)scsi_cmd_priv(Cmnd))->state)
+#define CMD_ABTS_STATUS(Cmnd)  \
+       (((struct snic_internal_io_state *)scsi_cmd_priv(Cmnd))->abts_status)
+#define CMD_LR_STATUS(Cmnd)    \
+       (((struct snic_internal_io_state *)scsi_cmd_priv(Cmnd))->lr_status)
+#define CMD_FLAGS(Cmnd)        \
+       (((struct snic_internal_io_state *)scsi_cmd_priv(Cmnd))->flags)
+
+#define SNIC_INVALID_CODE 0x100        /* Hdr Status val unused by firmware */
+
+#define SNIC_MAX_TARGET                        256
+#define SNIC_FLAGS_NONE                        (0)
+
+/* snic module params */
+extern unsigned int snic_max_qdepth;
+
+/* snic debugging */
+extern unsigned int snic_log_level;
+
+#define SNIC_MAIN_LOGGING      0x1
+#define SNIC_SCSI_LOGGING      0x2
+#define SNIC_ISR_LOGGING       0x8
+#define SNIC_DESC_LOGGING      0x10
+
+#define SNIC_CHECK_LOGGING(LEVEL, CMD)         \
+do {                                           \
+       if (unlikely(snic_log_level & LEVEL))   \
+               do {                            \
+                       CMD;                    \
+               } while (0);                    \
+} while (0)
+
+#define SNIC_MAIN_DBG(host, fmt, args...)      \
+       SNIC_CHECK_LOGGING(SNIC_MAIN_LOGGING,           \
+               shost_printk(KERN_INFO, host, fmt, ## args);)
+
+#define SNIC_SCSI_DBG(host, fmt, args...)      \
+       SNIC_CHECK_LOGGING(SNIC_SCSI_LOGGING,           \
+               shost_printk(KERN_INFO, host, fmt, ##args);)
+
+#define SNIC_DISC_DBG(host, fmt, args...)      \
+       SNIC_CHECK_LOGGING(SNIC_SCSI_LOGGING,           \
+               shost_printk(KERN_INFO, host, fmt, ##args);)
+
+#define SNIC_ISR_DBG(host, fmt, args...)       \
+       SNIC_CHECK_LOGGING(SNIC_ISR_LOGGING,            \
+               shost_printk(KERN_INFO, host, fmt, ##args);)
+
+#define SNIC_HOST_ERR(host, fmt, args...)              \
+       shost_printk(KERN_ERR, host, fmt, ##args)
+
+#define SNIC_HOST_INFO(host, fmt, args...)             \
+       shost_printk(KERN_INFO, host, fmt, ##args)
+
+#define SNIC_INFO(fmt, args...)                                \
+       pr_info(PFX fmt, ## args)
+
+#define SNIC_DBG(fmt, args...)                         \
+       pr_info(PFX fmt, ## args)
+
+#define SNIC_ERR(fmt, args...)                         \
+       pr_err(PFX fmt, ## args)
+
+#ifdef DEBUG
+#define SNIC_BUG_ON(EXPR) \
+       ({ \
+               if (EXPR) { \
+                       SNIC_ERR("SNIC BUG(%s)\n", #EXPR); \
+                       BUG_ON(EXPR); \
+               } \
+       })
+#else
+#define SNIC_BUG_ON(EXPR) \
+       ({ \
+               if (EXPR) { \
+                       SNIC_ERR("SNIC BUG(%s) at %s : %d\n", \
+                                #EXPR, __func__, __LINE__); \
+                       WARN_ON_ONCE(EXPR); \
+               } \
+       })
+#endif
+
+/* Soft assert */
+#define SNIC_ASSERT_NOT_IMPL(EXPR) \
+       ({ \
+               if (EXPR) {\
+                       SNIC_INFO("Functionality not impl'ed at %s:%d\n", \
+                                 __func__, __LINE__); \
+                       WARN_ON_ONCE(EXPR); \
+               } \
+        })
+
+
+extern const char *snic_state_str[];
+
+enum snic_intx_intr_index {
+       SNIC_INTX_WQ_RQ_COPYWQ,
+       SNIC_INTX_ERR,
+       SNIC_INTX_NOTIFY,
+       SNIC_INTX_INTR_MAX,
+};
+
+enum snic_msix_intr_index {
+       SNIC_MSIX_WQ,
+       SNIC_MSIX_IO_CMPL,
+       SNIC_MSIX_ERR_NOTIFY,
+       SNIC_MSIX_INTR_MAX,
+};
+
+struct snic_msix_entry {
+       int requested;
+       char devname[IFNAMSIZ];
+       irqreturn_t (*isr)(int, void *);
+       void *devid;
+};
+
+enum snic_state {
+       SNIC_INIT = 0,
+       SNIC_ERROR,
+       SNIC_ONLINE,
+       SNIC_OFFLINE,
+       SNIC_FWRESET,
+};
+
+#define SNIC_WQ_MAX            1
+#define SNIC_CQ_IO_CMPL_MAX    1
+#define SNIC_CQ_MAX            (SNIC_WQ_MAX + SNIC_CQ_IO_CMPL_MAX)
+
+/* firmware version information */
+struct snic_fw_info {
+       u32     fw_ver;
+       u32     hid;                    /* u16 hid | u16 vnic id */
+       u32     max_concur_ios;         /* max concurrent ios */
+       u32     max_sgs_per_cmd;        /* max sgls per IO */
+       u32     max_io_sz;              /* max io size supported */
+       u32     hba_cap;                /* hba capabilities */
+       u32     max_tgts;               /* max tgts supported */
+       u16     io_tmo;                 /* FW Extended timeout */
+       struct completion *wait;        /* protected by snic lock*/
+};
+
+/*
+ * snic_work item : defined to process asynchronous events
+ */
+struct snic_work {
+       struct work_struct work;
+       u16     ev_id;
+       u64     *ev_data;
+};
+
+/*
+ * snic structure to represent SCSI vNIC
+ */
+struct snic {
+       /* snic specific members */
+       struct list_head list;
+       char name[IFNAMSIZ];
+       atomic_t state;
+       spinlock_t snic_lock;
+       struct completion *remove_wait;
+       bool in_remove;
+       bool stop_link_events;          /* stop processing link events */
+
+       /* discovery related */
+       struct snic_disc disc;
+
+       /* Scsi Host info */
+       struct Scsi_Host *shost;
+
+       /* vnic related structures */
+       struct vnic_dev_bar bar0;
+
+       struct vnic_stats *stats;
+       unsigned long stats_time;
+       unsigned long stats_reset_time;
+
+       struct vnic_dev *vdev;
+
+       /* hw resource info */
+       unsigned int wq_count;
+       unsigned int cq_count;
+       unsigned int intr_count;
+       unsigned int err_intr_offset;
+
+       int link_status; /* retrieved from svnic_dev_link_status() */
+       u32 link_down_cnt;
+
+       /* pci related */
+       struct pci_dev *pdev;
+       struct msix_entry msix_entry[SNIC_MSIX_INTR_MAX];
+       struct snic_msix_entry msix[SNIC_MSIX_INTR_MAX];
+
+       /* io related info */
+       mempool_t *req_pool[SNIC_REQ_MAX_CACHES]; /* (??) */
+       ____cacheline_aligned spinlock_t io_req_lock[SNIC_IO_LOCKS];
+
+       /* Maintain snic specific commands, cmds with no tag in spl_cmd_list */
+       ____cacheline_aligned spinlock_t spl_cmd_lock;
+       struct list_head spl_cmd_list;
+
+       unsigned int max_tag_id;
+       atomic_t ios_inflight;          /* io in flight counter */
+
+       struct vnic_snic_config config;
+
+       struct work_struct link_work;
+
+       /* firmware information */
+       struct snic_fw_info fwinfo;
+
+       /* Work for processing Target related work */
+       struct work_struct tgt_work;
+
+       /* Work for processing Discovery */
+       struct work_struct disc_work;
+
+       /* stats related */
+       unsigned int reset_stats;
+       atomic64_t io_cmpl_skip;
+       struct snic_stats s_stats;      /* Per SNIC driver stats */
+
+       /* platform specific */
+#ifdef CONFIG_SCSI_SNIC_DEBUG_FS
+       struct dentry *stats_host;      /* Per snic debugfs root */
+       struct dentry *stats_file;      /* Per snic debugfs file */
+       struct dentry *reset_stats_file;/* Per snic reset stats file */
+#endif
+
+       /* completion queue cache line section */
+       ____cacheline_aligned struct vnic_cq cq[SNIC_CQ_MAX];
+
+       /* work queue cache line section */
+       ____cacheline_aligned struct vnic_wq wq[SNIC_WQ_MAX];
+       spinlock_t wq_lock[SNIC_WQ_MAX];
+
+       /* interrupt resource cache line section */
+       ____cacheline_aligned struct vnic_intr intr[SNIC_MSIX_INTR_MAX];
+}; /* end of snic structure */
+
+/*
+ * SNIC Driver's Global Data
+ */
+struct snic_global {
+       struct list_head snic_list;
+       spinlock_t snic_list_lock;
+
+       struct kmem_cache *req_cache[SNIC_REQ_MAX_CACHES];
+
+       struct workqueue_struct *event_q;
+
+#ifdef CONFIG_SCSI_SNIC_DEBUG_FS
+       /* debugfs related global data */
+       struct dentry *trc_root;
+       struct dentry *stats_root;
+
+       struct snic_trc trc ____cacheline_aligned;
+#endif
+};
+
+extern struct snic_global *snic_glob;
+
+int snic_glob_init(void);
+void snic_glob_cleanup(void);
+
+extern struct workqueue_struct *snic_event_queue;
+extern struct device_attribute *snic_attrs[];
+
+int snic_queuecommand(struct Scsi_Host *, struct scsi_cmnd *);
+int snic_abort_cmd(struct scsi_cmnd *);
+int snic_device_reset(struct scsi_cmnd *);
+int snic_host_reset(struct scsi_cmnd *);
+int snic_reset(struct Scsi_Host *, struct scsi_cmnd *);
+void snic_shutdown_scsi_cleanup(struct snic *);
+
+
+int snic_request_intr(struct snic *);
+void snic_free_intr(struct snic *);
+int snic_set_intr_mode(struct snic *);
+void snic_clear_intr_mode(struct snic *);
+
+int snic_fwcq_cmpl_handler(struct snic *, int);
+int snic_wq_cmpl_handler(struct snic *, int);
+void snic_free_wq_buf(struct vnic_wq *, struct vnic_wq_buf *);
+
+
+void snic_log_q_error(struct snic *);
+void snic_handle_link_event(struct snic *);
+void snic_handle_link(struct work_struct *);
+
+int snic_queue_exch_ver_req(struct snic *);
+int snic_io_exch_ver_cmpl_handler(struct snic *, struct snic_fw_req *);
+
+int snic_queue_wq_desc(struct snic *, void *os_buf, u16 len);
+
+void snic_handle_untagged_req(struct snic *, struct snic_req_info *);
+void snic_release_untagged_req(struct snic *, struct snic_req_info *);
+void snic_free_all_untagged_reqs(struct snic *);
+int snic_get_conf(struct snic *);
+void snic_set_state(struct snic *, enum snic_state);
+int snic_get_state(struct snic *);
+const char *snic_state_to_str(unsigned int);
+void snic_hex_dump(char *, char *, int);
+void snic_print_desc(const char *fn, char *os_buf, int len);
+const char *show_opcode_name(int val);
+#endif /* _SNIC_H */
diff --git a/drivers/scsi/snic/snic_attrs.c b/drivers/scsi/snic/snic_attrs.c
new file mode 100644 (file)
index 0000000..32d5d55
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2014 Cisco Systems, Inc.  All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * 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/string.h>
+#include <linux/device.h>
+
+#include "snic.h"
+
+static ssize_t
+snic_show_sym_name(struct device *dev,
+               struct device_attribute *attr,
+               char *buf)
+{
+       struct snic *snic = shost_priv(class_to_shost(dev));
+
+       return snprintf(buf, PAGE_SIZE, "%s\n", snic->name);
+}
+
+static ssize_t
+snic_show_state(struct device *dev,
+               struct device_attribute *attr,
+               char *buf)
+{
+       struct snic *snic = shost_priv(class_to_shost(dev));
+
+       return snprintf(buf, PAGE_SIZE, "%s\n",
+                       snic_state_str[snic_get_state(snic)]);
+}
+
+static ssize_t
+snic_show_drv_version(struct device *dev,
+                     struct device_attribute *attr,
+                     char *buf)
+{
+       return snprintf(buf, PAGE_SIZE, "%s\n", SNIC_DRV_VERSION);
+}
+
+static ssize_t
+snic_show_link_state(struct device *dev,
+                    struct device_attribute *attr,
+                    char *buf)
+{
+       struct snic *snic = shost_priv(class_to_shost(dev));
+
+       if (snic->config.xpt_type == SNIC_DAS)
+               snic->link_status = svnic_dev_link_status(snic->vdev);
+
+       return snprintf(buf, PAGE_SIZE, "%s\n",
+                       (snic->link_status) ? "Link Up" : "Link Down");
+}
+
+static DEVICE_ATTR(snic_sym_name, S_IRUGO, snic_show_sym_name, NULL);
+static DEVICE_ATTR(snic_state, S_IRUGO, snic_show_state, NULL);
+static DEVICE_ATTR(drv_version, S_IRUGO, snic_show_drv_version, NULL);
+static DEVICE_ATTR(link_state, S_IRUGO, snic_show_link_state, NULL);
+
+struct device_attribute *snic_attrs[] = {
+       &dev_attr_snic_sym_name,
+       &dev_attr_snic_state,
+       &dev_attr_drv_version,
+       &dev_attr_link_state,
+       NULL,
+};
diff --git a/drivers/scsi/snic/snic_ctl.c b/drivers/scsi/snic/snic_ctl.c
new file mode 100644 (file)
index 0000000..aebe753
--- /dev/null
@@ -0,0 +1,279 @@
+/*
+ * Copyright 2014 Cisco Systems, Inc.  All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * 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/errno.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/spinlock.h>
+#include <linux/mempool.h>
+#include <scsi/scsi_tcq.h>
+#include <linux/ctype.h>
+
+#include "snic_io.h"
+#include "snic.h"
+#include "cq_enet_desc.h"
+#include "snic_fwint.h"
+
+/*
+ * snic_handle_link : Handles link flaps.
+ */
+void
+snic_handle_link(struct work_struct *work)
+{
+       struct snic *snic = container_of(work, struct snic, link_work);
+
+       if (snic->config.xpt_type != SNIC_DAS) {
+               SNIC_HOST_INFO(snic->shost, "Link Event Received.\n");
+               SNIC_ASSERT_NOT_IMPL(1);
+
+               return;
+       }
+
+       snic->link_status = svnic_dev_link_status(snic->vdev);
+       snic->link_down_cnt = svnic_dev_link_down_cnt(snic->vdev);
+       SNIC_HOST_INFO(snic->shost, "Link Event: Link %s.\n",
+                      ((snic->link_status) ? "Up" : "Down"));
+}
+
+
+/*
+ * snic_ver_enc : Encodes version str to int
+ * version string is similar to netmask string
+ */
+static int
+snic_ver_enc(const char *s)
+{
+       int v[4] = {0};
+       int  i = 0, x = 0;
+       char c;
+       const char *p = s;
+
+       /* validate version string */
+       if ((strlen(s) > 15) || (strlen(s) < 7))
+               goto end;
+
+       while ((c = *p++)) {
+               if (c == '.') {
+                       i++;
+                       continue;
+               }
+
+               if (i > 4 || !isdigit(c))
+                       goto end;
+
+               v[i] = v[i] * 10 + (c - '0');
+       }
+
+       /* validate sub version numbers */
+       for (i = 3; i >= 0; i--)
+               if (v[i] > 0xff)
+                       goto end;
+
+       x |= (v[0] << 24) | v[1] << 16 | v[2] << 8 | v[3];
+
+end:
+       if (x == 0) {
+               SNIC_ERR("Invalid version string [%s].\n", s);
+
+               return -1;
+       }
+
+       return x;
+} /* end of snic_ver_enc */
+
+/*
+ * snic_qeueue_exch_ver_req :
+ *
+ * Queues Exchange Version Request, to communicate host information
+ * in return, it gets firmware version details
+ */
+int
+snic_queue_exch_ver_req(struct snic *snic)
+{
+       struct snic_req_info *rqi = NULL;
+       struct snic_host_req *req = NULL;
+       u32 ver = 0;
+       int ret = 0;
+
+       SNIC_HOST_INFO(snic->shost, "Exch Ver Req Preparing...\n");
+
+       rqi = snic_req_init(snic, 0);
+       if (!rqi) {
+               SNIC_HOST_ERR(snic->shost,
+                             "Queuing Exch Ver Req failed, err = %d\n",
+                             ret);
+
+               ret = -ENOMEM;
+               goto error;
+       }
+
+       req = rqi_to_req(rqi);
+
+       /* Initialize snic_host_req */
+       snic_io_hdr_enc(&req->hdr, SNIC_REQ_EXCH_VER, 0, SCSI_NO_TAG,
+                       snic->config.hid, 0, (ulong)rqi);
+       ver = snic_ver_enc(SNIC_DRV_VERSION);
+       req->u.exch_ver.drvr_ver = cpu_to_le32(ver);
+       req->u.exch_ver.os_type = cpu_to_le32(SNIC_OS_LINUX);
+
+       snic_handle_untagged_req(snic, rqi);
+
+       ret = snic_queue_wq_desc(snic, req, sizeof(*req));
+       if (ret) {
+               snic_release_untagged_req(snic, rqi);
+               SNIC_HOST_ERR(snic->shost,
+                             "Queuing Exch Ver Req failed, err = %d\n",
+                             ret);
+               goto error;
+       }
+
+       SNIC_HOST_INFO(snic->shost, "Exch Ver Req is issued. ret = %d\n", ret);
+
+error:
+       return ret;
+} /* end of snic_queue_exch_ver_req */
+
+/*
+ * snic_io_exch_ver_cmpl_handler
+ */
+int
+snic_io_exch_ver_cmpl_handler(struct snic *snic, struct snic_fw_req *fwreq)
+{
+       struct snic_req_info *rqi = NULL;
+       struct snic_exch_ver_rsp *exv_cmpl = &fwreq->u.exch_ver_cmpl;
+       u8 typ, hdr_stat;
+       u32 cmnd_id, hid, max_sgs;
+       ulong ctx = 0;
+       unsigned long flags;
+       int ret = 0;
+
+       SNIC_HOST_INFO(snic->shost, "Exch Ver Compl Received.\n");
+       snic_io_hdr_dec(&fwreq->hdr, &typ, &hdr_stat, &cmnd_id, &hid, &ctx);
+       SNIC_BUG_ON(snic->config.hid != hid);
+       rqi = (struct snic_req_info *) ctx;
+
+       if (hdr_stat) {
+               SNIC_HOST_ERR(snic->shost,
+                             "Exch Ver Completed w/ err status %d\n",
+                             hdr_stat);
+
+               goto exch_cmpl_end;
+       }
+
+       spin_lock_irqsave(&snic->snic_lock, flags);
+       snic->fwinfo.fw_ver = le32_to_cpu(exv_cmpl->version);
+       snic->fwinfo.hid = le32_to_cpu(exv_cmpl->hid);
+       snic->fwinfo.max_concur_ios = le32_to_cpu(exv_cmpl->max_concur_ios);
+       snic->fwinfo.max_sgs_per_cmd = le32_to_cpu(exv_cmpl->max_sgs_per_cmd);
+       snic->fwinfo.max_io_sz = le32_to_cpu(exv_cmpl->max_io_sz);
+       snic->fwinfo.max_tgts = le32_to_cpu(exv_cmpl->max_tgts);
+       snic->fwinfo.io_tmo = le16_to_cpu(exv_cmpl->io_timeout);
+
+       SNIC_HOST_INFO(snic->shost,
+                      "vers %u hid %u max_concur_ios %u max_sgs_per_cmd %u max_io_sz %u max_tgts %u fw tmo %u\n",
+                      snic->fwinfo.fw_ver,
+                      snic->fwinfo.hid,
+                      snic->fwinfo.max_concur_ios,
+                      snic->fwinfo.max_sgs_per_cmd,
+                      snic->fwinfo.max_io_sz,
+                      snic->fwinfo.max_tgts,
+                      snic->fwinfo.io_tmo);
+
+       SNIC_HOST_INFO(snic->shost,
+                      "HBA Capabilities = 0x%x\n",
+                      le32_to_cpu(exv_cmpl->hba_cap));
+
+       /* Updating SGList size */
+       max_sgs = snic->fwinfo.max_sgs_per_cmd;
+       if (max_sgs && max_sgs < SNIC_MAX_SG_DESC_CNT) {
+               snic->shost->sg_tablesize = max_sgs;
+               SNIC_HOST_INFO(snic->shost, "Max SGs set to %d\n",
+                              snic->shost->sg_tablesize);
+       } else if (max_sgs > snic->shost->sg_tablesize) {
+               SNIC_HOST_INFO(snic->shost,
+                              "Target type %d Supports Larger Max SGList %d than driver's Max SG List %d.\n",
+                              snic->config.xpt_type, max_sgs,
+                              snic->shost->sg_tablesize);
+       }
+
+       if (snic->shost->can_queue > snic->fwinfo.max_concur_ios)
+               snic->shost->can_queue = snic->fwinfo.max_concur_ios;
+
+       snic->shost->max_sectors = snic->fwinfo.max_io_sz >> 9;
+       if (snic->fwinfo.wait)
+               complete(snic->fwinfo.wait);
+
+       spin_unlock_irqrestore(&snic->snic_lock, flags);
+
+exch_cmpl_end:
+       snic_release_untagged_req(snic, rqi);
+
+       SNIC_HOST_INFO(snic->shost, "Exch_cmpl Done, hdr_stat %d.\n", hdr_stat);
+
+       return ret;
+} /* end of snic_io_exch_ver_cmpl_handler */
+
+/*
+ * snic_get_conf
+ *
+ * Synchronous call, and Retrieves snic params.
+ */
+int
+snic_get_conf(struct snic *snic)
+{
+       DECLARE_COMPLETION_ONSTACK(wait);
+       unsigned long flags;
+       int ret;
+       int nr_retries = 3;
+
+       SNIC_HOST_INFO(snic->shost, "Retrieving snic params.\n");
+       spin_lock_irqsave(&snic->snic_lock, flags);
+       memset(&snic->fwinfo, 0, sizeof(snic->fwinfo));
+       snic->fwinfo.wait = &wait;
+       spin_unlock_irqrestore(&snic->snic_lock, flags);
+
+       /* Additional delay to handle HW Resource initialization. */
+       msleep(50);
+
+       /*
+        * Exch ver req can be ignored by FW, if HW Resource initialization
+        * is in progress, Hence retry.
+        */
+       do {
+               ret = snic_queue_exch_ver_req(snic);
+               if (ret)
+                       return ret;
+
+               wait_for_completion_timeout(&wait, msecs_to_jiffies(2000));
+               spin_lock_irqsave(&snic->snic_lock, flags);
+               ret = (snic->fwinfo.fw_ver != 0) ? 0 : -ETIMEDOUT;
+               if (ret)
+                       SNIC_HOST_ERR(snic->shost,
+                                     "Failed to retrieve snic params,\n");
+
+               /* Unset fwinfo.wait, on success or on last retry */
+               if (ret == 0 || nr_retries == 1)
+                       snic->fwinfo.wait = NULL;
+
+               spin_unlock_irqrestore(&snic->snic_lock, flags);
+       } while (ret && --nr_retries);
+
+       return ret;
+} /* end of snic_get_info */
diff --git a/drivers/scsi/snic/snic_debugfs.c b/drivers/scsi/snic/snic_debugfs.c
new file mode 100644 (file)
index 0000000..1686f01
--- /dev/null
@@ -0,0 +1,560 @@
+/*
+ * Copyright 2014 Cisco Systems, Inc.  All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * 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 <linux/errno.h>
+#include <linux/debugfs.h>
+
+#include "snic.h"
+
+/*
+ * snic_debugfs_init - Initialize debugfs for snic debug logging
+ *
+ * Description:
+ * When Debugfs is configured this routine sets up fnic debugfs
+ * filesystem. If not already created. this routine will crate the
+ * fnic directory and statistics directory for trace buffer and
+ * stats logging
+ */
+
+int
+snic_debugfs_init(void)
+{
+       int rc = -1;
+       struct dentry *de = NULL;
+
+       de = debugfs_create_dir("snic", NULL);
+       if (!de) {
+               SNIC_DBG("Cannot create debugfs root\n");
+
+               return rc;
+       }
+       snic_glob->trc_root = de;
+
+       de = debugfs_create_dir("statistics", snic_glob->trc_root);
+       if (!de) {
+               SNIC_DBG("Cannot create Statistics directory\n");
+
+               return rc;
+       }
+       snic_glob->stats_root = de;
+
+       rc = 0;
+
+       return rc;
+} /* end of snic_debugfs_init */
+
+/*
+ * snic_debugfs_term - Tear down debugfs intrastructure
+ *
+ * Description:
+ * When Debufs is configured this routine removes debugfs file system
+ * elements that are specific to snic
+ */
+void
+snic_debugfs_term(void)
+{
+       debugfs_remove(snic_glob->stats_root);
+       snic_glob->stats_root = NULL;
+
+       debugfs_remove(snic_glob->trc_root);
+       snic_glob->trc_root = NULL;
+}
+
+/*
+ * snic_reset_stats_open - Open the reset_stats file
+ */
+static int
+snic_reset_stats_open(struct inode *inode, struct file *filp)
+{
+       SNIC_BUG_ON(!inode->i_private);
+       filp->private_data = inode->i_private;
+
+       return 0;
+}
+
+/*
+ * snic_reset_stats_read - Read a reset_stats debugfs file
+ * @filp: The file pointer to read from.
+ * @ubuf: The buffer tocopy the data to.
+ * @cnt: The number of bytes to read.
+ * @ppos: The position in the file to start reading frm.
+ *
+ * Description:
+ * This routine reads value of variable reset_stats
+ * and stores into local @buf. It will start reading file @ppos and
+ * copy up to @cnt of data to @ubuf from @buf.
+ *
+ * Returns:
+ * This function returns the amount of data that was read.
+ */
+static ssize_t
+snic_reset_stats_read(struct file *filp,
+                     char __user *ubuf,
+                     size_t cnt,
+                     loff_t *ppos)
+{
+       struct snic *snic = (struct snic *) filp->private_data;
+       char buf[64];
+       int len;
+
+       len = sprintf(buf, "%u\n", snic->reset_stats);
+
+       return simple_read_from_buffer(ubuf, cnt, ppos, buf, len);
+}
+
+/*
+ * snic_reset_stats_write - Write to reset_stats debugfs file
+ * @filp: The file pointer to write from
+ * @ubuf: The buffer to copy the data from.
+ * @cnt: The number of bytes to write.
+ * @ppos: The position in the file to start writing to.
+ *
+ * Description:
+ * This routine writes data from user buffer @ubuf to buffer @buf and
+ * resets cumulative stats of snic.
+ *
+ * Returns:
+ * This function returns the amount of data that was written.
+ */
+static ssize_t
+snic_reset_stats_write(struct file *filp,
+                      const char __user *ubuf,
+                      size_t cnt,
+                      loff_t *ppos)
+{
+       struct snic *snic = (struct snic *) filp->private_data;
+       struct snic_stats *stats = &snic->s_stats;
+       u64 *io_stats_p = (u64 *) &stats->io;
+       u64 *fw_stats_p = (u64 *) &stats->fw;
+       char buf[64];
+       unsigned long val;
+       int ret;
+
+       if (cnt >= sizeof(buf))
+               return -EINVAL;
+
+       if (copy_from_user(&buf, ubuf, cnt))
+               return -EFAULT;
+
+       buf[cnt] = '\0';
+
+       ret = kstrtoul(buf, 10, &val);
+       if (ret < 0)
+               return ret;
+
+       snic->reset_stats = val;
+
+       if (snic->reset_stats) {
+               /* Skip variable is used to avoid descrepancies to Num IOs
+                * and IO Completions stats. Skip incrementing No IO Compls
+                * for pending active IOs after reset_stats
+                */
+               atomic64_set(&snic->io_cmpl_skip,
+                            atomic64_read(&stats->io.active));
+               memset(&stats->abts, 0, sizeof(struct snic_abort_stats));
+               memset(&stats->reset, 0, sizeof(struct snic_reset_stats));
+               memset(&stats->misc, 0, sizeof(struct snic_misc_stats));
+               memset(io_stats_p+1,
+                       0,
+                       sizeof(struct snic_io_stats) - sizeof(u64));
+               memset(fw_stats_p+1,
+                       0,
+                       sizeof(struct snic_fw_stats) - sizeof(u64));
+       }
+
+       (*ppos)++;
+
+       SNIC_HOST_INFO(snic->shost, "Reset Op: Driver statistics.\n");
+
+       return cnt;
+}
+
+static int
+snic_reset_stats_release(struct inode *inode, struct file *filp)
+{
+       filp->private_data = NULL;
+
+       return 0;
+}
+
+/*
+ * snic_stats_show - Formats and prints per host specific driver stats.
+ */
+static int
+snic_stats_show(struct seq_file *sfp, void *data)
+{
+       struct snic *snic = (struct snic *) sfp->private;
+       struct snic_stats *stats = &snic->s_stats;
+       struct timespec last_isr_tms, last_ack_tms;
+       u64 maxio_tm;
+       int i;
+
+       /* Dump IO Stats */
+       seq_printf(sfp,
+                  "------------------------------------------\n"
+                  "\t\t IO Statistics\n"
+                  "------------------------------------------\n");
+
+       maxio_tm = (u64) atomic64_read(&stats->io.max_time);
+       seq_printf(sfp,
+                  "Active IOs                  : %lld\n"
+                  "Max Active IOs              : %lld\n"
+                  "Total IOs                   : %lld\n"
+                  "IOs Completed               : %lld\n"
+                  "IOs Failed                  : %lld\n"
+                  "IOs Not Found               : %lld\n"
+                  "Memory Alloc Failures       : %lld\n"
+                  "REQs Null                   : %lld\n"
+                  "SCSI Cmd Pointers Null      : %lld\n"
+                  "Max SGL for any IO          : %lld\n"
+                  "Max IO Size                 : %lld Sectors\n"
+                  "Max Queuing Time            : %lld\n"
+                  "Max Completion Time         : %lld\n"
+                  "Max IO Process Time(FW)     : %lld (%u msec)\n",
+                  (u64) atomic64_read(&stats->io.active),
+                  (u64) atomic64_read(&stats->io.max_active),
+                  (u64) atomic64_read(&stats->io.num_ios),
+                  (u64) atomic64_read(&stats->io.compl),
+                  (u64) atomic64_read(&stats->io.fail),
+                  (u64) atomic64_read(&stats->io.io_not_found),
+                  (u64) atomic64_read(&stats->io.alloc_fail),
+                  (u64) atomic64_read(&stats->io.req_null),
+                  (u64) atomic64_read(&stats->io.sc_null),
+                  (u64) atomic64_read(&stats->io.max_sgl),
+                  (u64) atomic64_read(&stats->io.max_io_sz),
+                  (u64) atomic64_read(&stats->io.max_qtime),
+                  (u64) atomic64_read(&stats->io.max_cmpl_time),
+                  maxio_tm,
+                  jiffies_to_msecs(maxio_tm));
+
+       seq_puts(sfp, "\nSGL Counters\n");
+
+       for (i = 0; i < SNIC_MAX_SG_DESC_CNT; i++) {
+               seq_printf(sfp,
+                          "%10lld ",
+                          (u64) atomic64_read(&stats->io.sgl_cnt[i]));
+
+               if ((i + 1) % 8 == 0)
+                       seq_puts(sfp, "\n");
+       }
+
+       /* Dump Abort Stats */
+       seq_printf(sfp,
+                  "\n-------------------------------------------\n"
+                  "\t\t Abort Statistics\n"
+                  "---------------------------------------------\n");
+
+       seq_printf(sfp,
+                  "Aborts                      : %lld\n"
+                  "Aborts Fail                 : %lld\n"
+                  "Aborts Driver Timeout       : %lld\n"
+                  "Abort FW Timeout            : %lld\n"
+                  "Abort IO NOT Found          : %lld\n",
+                  (u64) atomic64_read(&stats->abts.num),
+                  (u64) atomic64_read(&stats->abts.fail),
+                  (u64) atomic64_read(&stats->abts.drv_tmo),
+                  (u64) atomic64_read(&stats->abts.fw_tmo),
+                  (u64) atomic64_read(&stats->abts.io_not_found));
+
+       /* Dump Reset Stats */
+       seq_printf(sfp,
+                  "\n-------------------------------------------\n"
+                  "\t\t Reset Statistics\n"
+                  "---------------------------------------------\n");
+
+       seq_printf(sfp,
+                  "HBA Resets                  : %lld\n"
+                  "HBA Reset Cmpls             : %lld\n"
+                  "HBA Reset Fail              : %lld\n",
+                  (u64) atomic64_read(&stats->reset.hba_resets),
+                  (u64) atomic64_read(&stats->reset.hba_reset_cmpl),
+                  (u64) atomic64_read(&stats->reset.hba_reset_fail));
+
+       /* Dump Firmware Stats */
+       seq_printf(sfp,
+                  "\n-------------------------------------------\n"
+                  "\t\t Firmware Statistics\n"
+                  "---------------------------------------------\n");
+
+       seq_printf(sfp,
+               "Active FW Requests             : %lld\n"
+               "Max FW Requests                : %lld\n"
+               "FW Out Of Resource Errs        : %lld\n"
+               "FW IO Errors                   : %lld\n"
+               "FW SCSI Errors                 : %lld\n",
+               (u64) atomic64_read(&stats->fw.actv_reqs),
+               (u64) atomic64_read(&stats->fw.max_actv_reqs),
+               (u64) atomic64_read(&stats->fw.out_of_res),
+               (u64) atomic64_read(&stats->fw.io_errs),
+               (u64) atomic64_read(&stats->fw.scsi_errs));
+
+
+       /* Dump Miscellenous Stats */
+       seq_printf(sfp,
+                  "\n---------------------------------------------\n"
+                  "\t\t Other Statistics\n"
+                  "\n---------------------------------------------\n");
+
+       jiffies_to_timespec(stats->misc.last_isr_time, &last_isr_tms);
+       jiffies_to_timespec(stats->misc.last_ack_time, &last_ack_tms);
+
+       seq_printf(sfp,
+                  "Last ISR Time               : %llu (%8lu.%8lu)\n"
+                  "Last Ack Time               : %llu (%8lu.%8lu)\n"
+                  "ISRs                        : %llu\n"
+                  "Max CQ Entries              : %lld\n"
+                  "Data Count Mismatch         : %lld\n"
+                  "IOs w/ Timeout Status       : %lld\n"
+                  "IOs w/ Aborted Status       : %lld\n"
+                  "IOs w/ SGL Invalid Stat     : %lld\n"
+                  "WQ Desc Alloc Fail          : %lld\n"
+                  "Queue Full                  : %lld\n"
+                  "Target Not Ready            : %lld\n",
+                  (u64) stats->misc.last_isr_time,
+                  last_isr_tms.tv_sec, last_isr_tms.tv_nsec,
+                  (u64)stats->misc.last_ack_time,
+                  last_ack_tms.tv_sec, last_ack_tms.tv_nsec,
+                  (u64) atomic64_read(&stats->misc.isr_cnt),
+                  (u64) atomic64_read(&stats->misc.max_cq_ents),
+                  (u64) atomic64_read(&stats->misc.data_cnt_mismat),
+                  (u64) atomic64_read(&stats->misc.io_tmo),
+                  (u64) atomic64_read(&stats->misc.io_aborted),
+                  (u64) atomic64_read(&stats->misc.sgl_inval),
+                  (u64) atomic64_read(&stats->misc.wq_alloc_fail),
+                  (u64) atomic64_read(&stats->misc.qfull),
+                  (u64) atomic64_read(&stats->misc.tgt_not_rdy));
+
+       return 0;
+}
+
+/*
+ * snic_stats_open - Open the stats file for specific host
+ *
+ * Description:
+ * This routine opens a debugfs file stats of specific host
+ */
+static int
+snic_stats_open(struct inode *inode, struct file *filp)
+{
+       return single_open(filp, snic_stats_show, inode->i_private);
+}
+
+static const struct file_operations snic_stats_fops = {
+       .owner  = THIS_MODULE,
+       .open   = snic_stats_open,
+       .read   = seq_read,
+       .llseek = seq_lseek,
+       .release = single_release,
+};
+
+static const struct file_operations snic_reset_stats_fops = {
+       .owner = THIS_MODULE,
+       .open = snic_reset_stats_open,
+       .read = snic_reset_stats_read,
+       .write = snic_reset_stats_write,
+       .release = snic_reset_stats_release,
+};
+
+/*
+ * snic_stats_init - Initialize stats struct and create stats file
+ * per snic
+ *
+ * Description:
+ * When debugfs is cofigured this routine sets up the stats file per snic
+ * It will create file stats and reset_stats under statistics/host# directory
+ * to log per snic stats
+ */
+int
+snic_stats_debugfs_init(struct snic *snic)
+{
+       int rc = -1;
+       char name[16];
+       struct dentry *de = NULL;
+
+       snprintf(name, sizeof(name), "host%d", snic->shost->host_no);
+       if (!snic_glob->stats_root) {
+               SNIC_DBG("snic_stats root doesn't exist\n");
+
+               return rc;
+       }
+
+       de = debugfs_create_dir(name, snic_glob->stats_root);
+       if (!de) {
+               SNIC_DBG("Cannot create host directory\n");
+
+               return rc;
+       }
+       snic->stats_host = de;
+
+       de = debugfs_create_file("stats",
+                               S_IFREG|S_IRUGO,
+                               snic->stats_host,
+                               snic,
+                               &snic_stats_fops);
+       if (!de) {
+               SNIC_DBG("Cannot create host's stats file\n");
+
+               return rc;
+       }
+       snic->stats_file = de;
+
+       de = debugfs_create_file("reset_stats",
+                               S_IFREG|S_IRUGO|S_IWUSR,
+                               snic->stats_host,
+                               snic,
+                               &snic_reset_stats_fops);
+
+       if (!de) {
+               SNIC_DBG("Cannot create host's reset_stats file\n");
+
+               return rc;
+       }
+       snic->reset_stats_file = de;
+       rc = 0;
+
+       return rc;
+} /* end of snic_stats_debugfs_init */
+
+/*
+ * snic_stats_debugfs_remove - Tear down debugfs infrastructure of stats
+ *
+ * Description:
+ * When Debufs is configured this routine removes debugfs file system
+ * elements that are specific to to snic stats
+ */
+void
+snic_stats_debugfs_remove(struct snic *snic)
+{
+       debugfs_remove(snic->stats_file);
+       snic->stats_file = NULL;
+
+       debugfs_remove(snic->reset_stats_file);
+       snic->reset_stats_file = NULL;
+
+       debugfs_remove(snic->stats_host);
+       snic->stats_host = NULL;
+}
+
+/* Trace Facility related API */
+static void *
+snic_trc_seq_start(struct seq_file *sfp, loff_t *pos)
+{
+       return &snic_glob->trc;
+}
+
+static void *
+snic_trc_seq_next(struct seq_file *sfp, void *data, loff_t *pos)
+{
+       return NULL;
+}
+
+static void
+snic_trc_seq_stop(struct seq_file *sfp, void *data)
+{
+}
+
+#define SNIC_TRC_PBLEN 256
+static int
+snic_trc_seq_show(struct seq_file *sfp, void *data)
+{
+       char buf[SNIC_TRC_PBLEN];
+
+       if (snic_get_trc_data(buf, SNIC_TRC_PBLEN) > 0)
+               seq_printf(sfp, "%s\n", buf);
+
+       return 0;
+}
+
+static const struct seq_operations snic_trc_seq_ops = {
+       .start  = snic_trc_seq_start,
+       .next   = snic_trc_seq_next,
+       .stop   = snic_trc_seq_stop,
+       .show   = snic_trc_seq_show,
+};
+
+static int
+snic_trc_open(struct inode *inode, struct file *filp)
+{
+       return seq_open(filp, &snic_trc_seq_ops);
+}
+
+static const struct file_operations snic_trc_fops = {
+       .owner  = THIS_MODULE,
+       .open   = snic_trc_open,
+       .read   = seq_read,
+       .llseek = seq_lseek,
+       .release = seq_release,
+};
+
+/*
+ * snic_trc_debugfs_init : creates trace/tracing_enable files for trace
+ * under debugfs
+ */
+int
+snic_trc_debugfs_init(void)
+{
+       struct dentry *de = NULL;
+       int ret = -1;
+
+       if (!snic_glob->trc_root) {
+               SNIC_ERR("Debugfs root directory for snic doesn't exist.\n");
+
+               return ret;
+       }
+
+       de = debugfs_create_bool("tracing_enable",
+                                S_IFREG | S_IRUGO | S_IWUSR,
+                                snic_glob->trc_root,
+                                &snic_glob->trc.enable);
+
+       if (!de) {
+               SNIC_ERR("Can't create trace_enable file.\n");
+
+               return ret;
+       }
+       snic_glob->trc.trc_enable = de;
+
+       de = debugfs_create_file("trace",
+                                S_IFREG | S_IRUGO | S_IWUSR,
+                                snic_glob->trc_root,
+                                NULL,
+                                &snic_trc_fops);
+
+       if (!de) {
+               SNIC_ERR("Cann't create trace file.\n");
+
+               return ret;
+       }
+       snic_glob->trc.trc_file = de;
+       ret = 0;
+
+       return ret;
+} /* end of snic_trc_debugfs_init */
+
+/*
+ * snic_trc_debugfs_term : cleans up the files created for trace under debugfs
+ */
+void
+snic_trc_debugfs_term(void)
+{
+       debugfs_remove(snic_glob->trc.trc_file);
+       snic_glob->trc.trc_file = NULL;
+
+       debugfs_remove(snic_glob->trc.trc_enable);
+       snic_glob->trc.trc_enable = NULL;
+}
diff --git a/drivers/scsi/snic/snic_disc.c b/drivers/scsi/snic/snic_disc.c
new file mode 100644 (file)
index 0000000..5f63217
--- /dev/null
@@ -0,0 +1,551 @@
+/*
+ * Copyright 2014 Cisco Systems, Inc.  All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * 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/errno.h>
+#include <linux/mempool.h>
+
+#include <scsi/scsi_tcq.h>
+
+#include "snic_disc.h"
+#include "snic.h"
+#include "snic_io.h"
+
+
+/* snic target types */
+static const char * const snic_tgt_type_str[] = {
+       [SNIC_TGT_DAS] = "DAS",
+       [SNIC_TGT_SAN] = "SAN",
+};
+
+static inline const char *
+snic_tgt_type_to_str(int typ)
+{
+       return ((typ > SNIC_TGT_NONE && typ <= SNIC_TGT_SAN) ?
+                snic_tgt_type_str[typ] : "Unknown");
+}
+
+static const char * const snic_tgt_state_str[] = {
+       [SNIC_TGT_STAT_INIT]    = "INIT",
+       [SNIC_TGT_STAT_ONLINE]  = "ONLINE",
+       [SNIC_TGT_STAT_OFFLINE] = "OFFLINE",
+       [SNIC_TGT_STAT_DEL]     = "DELETION IN PROGRESS",
+};
+
+const char *
+snic_tgt_state_to_str(int state)
+{
+       return ((state >= SNIC_TGT_STAT_INIT && state <= SNIC_TGT_STAT_DEL) ?
+               snic_tgt_state_str[state] : "UNKNOWN");
+}
+
+/*
+ * Initiate report_tgt req desc
+ */
+static void
+snic_report_tgt_init(struct snic_host_req *req, u32 hid, u8 *buf, u32 len,
+                    dma_addr_t rsp_buf_pa, ulong ctx)
+{
+       struct snic_sg_desc *sgd = NULL;
+
+
+       snic_io_hdr_enc(&req->hdr, SNIC_REQ_REPORT_TGTS, 0, SCSI_NO_TAG, hid,
+                       1, ctx);
+
+       req->u.rpt_tgts.sg_cnt = cpu_to_le16(1);
+       sgd = req_to_sgl(req);
+       sgd[0].addr = cpu_to_le64(rsp_buf_pa);
+       sgd[0].len = cpu_to_le32(len);
+       sgd[0]._resvd = 0;
+       req->u.rpt_tgts.sg_addr = cpu_to_le64((ulong)sgd);
+}
+
+/*
+ * snic_queue_report_tgt_req: Queues report target request.
+ */
+static int
+snic_queue_report_tgt_req(struct snic *snic)
+{
+       struct snic_req_info *rqi = NULL;
+       u32 ntgts, buf_len = 0;
+       u8 *buf = NULL;
+       dma_addr_t pa = 0;
+       int ret = 0;
+
+       rqi = snic_req_init(snic, 1);
+       if (!rqi) {
+               ret = -ENOMEM;
+               goto error;
+       }
+
+       if (snic->fwinfo.max_tgts)
+               ntgts = min_t(u32, snic->fwinfo.max_tgts, snic->shost->max_id);
+       else
+               ntgts = snic->shost->max_id;
+
+       /* Allocate Response Buffer */
+       SNIC_BUG_ON(ntgts == 0);
+       buf_len = ntgts * sizeof(struct snic_tgt_id) + SNIC_SG_DESC_ALIGN;
+
+       buf = kzalloc(buf_len, GFP_KERNEL|GFP_DMA);
+       if (!buf) {
+               snic_req_free(snic, rqi);
+               SNIC_HOST_ERR(snic->shost, "Resp Buf Alloc Failed.\n");
+
+               ret = -ENOMEM;
+               goto error;
+       }
+
+       SNIC_BUG_ON((((unsigned long)buf) % SNIC_SG_DESC_ALIGN) != 0);
+
+       pa = pci_map_single(snic->pdev, buf, buf_len, PCI_DMA_FROMDEVICE);
+       if (pci_dma_mapping_error(snic->pdev, pa)) {
+               kfree(buf);
+               snic_req_free(snic, rqi);
+               SNIC_HOST_ERR(snic->shost,
+                             "Rpt-tgt rspbuf %p: PCI DMA Mapping Failed\n",
+                             buf);
+               ret = -EINVAL;
+
+               goto error;
+       }
+
+
+       SNIC_BUG_ON(pa == 0);
+       rqi->sge_va = (ulong) buf;
+
+       snic_report_tgt_init(rqi->req,
+                            snic->config.hid,
+                            buf,
+                            buf_len,
+                            pa,
+                            (ulong)rqi);
+
+       snic_handle_untagged_req(snic, rqi);
+
+       ret = snic_queue_wq_desc(snic, rqi->req, rqi->req_len);
+       if (ret) {
+               pci_unmap_single(snic->pdev, pa, buf_len, PCI_DMA_FROMDEVICE);
+               kfree(buf);
+               rqi->sge_va = 0;
+               snic_release_untagged_req(snic, rqi);
+               SNIC_HOST_ERR(snic->shost, "Queuing Report Tgts Failed.\n");
+
+               goto error;
+       }
+
+       SNIC_DISC_DBG(snic->shost, "Report Targets Issued.\n");
+
+       return ret;
+
+error:
+       SNIC_HOST_ERR(snic->shost,
+                     "Queuing Report Targets Failed, err = %d\n",
+                     ret);
+       return ret;
+} /* end of snic_queue_report_tgt_req */
+
+/* call into SML */
+static void
+snic_scsi_scan_tgt(struct work_struct *work)
+{
+       struct snic_tgt *tgt = container_of(work, struct snic_tgt, scan_work);
+       struct Scsi_Host *shost = dev_to_shost(&tgt->dev);
+       unsigned long flags;
+
+       SNIC_HOST_INFO(shost, "Scanning Target id 0x%x\n", tgt->id);
+       scsi_scan_target(&tgt->dev,
+                        tgt->channel,
+                        tgt->scsi_tgt_id,
+                        SCAN_WILD_CARD,
+                        1);
+
+       spin_lock_irqsave(shost->host_lock, flags);
+       tgt->flags &= ~SNIC_TGT_SCAN_PENDING;
+       spin_unlock_irqrestore(shost->host_lock, flags);
+} /* end of snic_scsi_scan_tgt */
+
+/*
+ * snic_tgt_lookup :
+ */
+static struct snic_tgt *
+snic_tgt_lookup(struct snic *snic, struct snic_tgt_id *tgtid)
+{
+       struct list_head *cur, *nxt;
+       struct snic_tgt *tgt = NULL;
+
+       list_for_each_safe(cur, nxt, &snic->disc.tgt_list) {
+               tgt = list_entry(cur, struct snic_tgt, list);
+               if (tgt->id == le32_to_cpu(tgtid->tgt_id))
+                       return tgt;
+               tgt = NULL;
+       }
+
+       return tgt;
+} /* end of snic_tgt_lookup */
+
+/*
+ * snic_tgt_dev_release : Called on dropping last ref for snic_tgt object
+ */
+void
+snic_tgt_dev_release(struct device *dev)
+{
+       struct snic_tgt *tgt = dev_to_tgt(dev);
+
+       SNIC_HOST_INFO(snic_tgt_to_shost(tgt),
+                      "Target Device ID %d (%s) Permanently Deleted.\n",
+                      tgt->id,
+                      dev_name(dev));
+
+       SNIC_BUG_ON(!list_empty(&tgt->list));
+       kfree(tgt);
+}
+
+/*
+ * snic_tgt_del : work function to delete snic_tgt
+ */
+static void
+snic_tgt_del(struct work_struct *work)
+{
+       struct snic_tgt *tgt = container_of(work, struct snic_tgt, del_work);
+       struct Scsi_Host *shost = snic_tgt_to_shost(tgt);
+
+       if (tgt->flags & SNIC_TGT_SCAN_PENDING)
+               scsi_flush_work(shost);
+
+       /* Block IOs on child devices, stops new IOs */
+       scsi_target_block(&tgt->dev);
+
+       /* Cleanup IOs */
+       snic_tgt_scsi_abort_io(tgt);
+
+       /* Unblock IOs now, to flush if there are any. */
+       scsi_target_unblock(&tgt->dev, SDEV_TRANSPORT_OFFLINE);
+
+       /* Delete SCSI Target and sdevs */
+       scsi_remove_target(&tgt->dev);  /* ?? */
+       device_del(&tgt->dev);
+       put_device(&tgt->dev);
+} /* end of snic_tgt_del */
+
+/* snic_tgt_create: checks for existence of snic_tgt, if it doesn't
+ * it creates one.
+ */
+static struct snic_tgt *
+snic_tgt_create(struct snic *snic, struct snic_tgt_id *tgtid)
+{
+       struct snic_tgt *tgt = NULL;
+       unsigned long flags;
+       int ret;
+
+       tgt = snic_tgt_lookup(snic, tgtid);
+       if (tgt) {
+               /* update the information if required */
+               return tgt;
+       }
+
+       tgt = kzalloc(sizeof(*tgt), GFP_KERNEL);
+       if (!tgt) {
+               SNIC_HOST_ERR(snic->shost, "Failure to allocate snic_tgt.\n");
+               ret = -ENOMEM;
+
+               return tgt;
+       }
+
+       INIT_LIST_HEAD(&tgt->list);
+       tgt->id = le32_to_cpu(tgtid->tgt_id);
+       tgt->channel = 0;
+
+       SNIC_BUG_ON(le16_to_cpu(tgtid->tgt_type) > SNIC_TGT_SAN);
+       tgt->tdata.typ = le16_to_cpu(tgtid->tgt_type);
+
+       /*
+        * Plugging into SML Device Tree
+        */
+       tgt->tdata.disc_id = 0;
+       tgt->state = SNIC_TGT_STAT_INIT;
+       device_initialize(&tgt->dev);
+       tgt->dev.parent = get_device(&snic->shost->shost_gendev);
+       tgt->dev.release = snic_tgt_dev_release;
+       INIT_WORK(&tgt->scan_work, snic_scsi_scan_tgt);
+       INIT_WORK(&tgt->del_work, snic_tgt_del);
+       switch (tgt->tdata.typ) {
+       case SNIC_TGT_DAS:
+               dev_set_name(&tgt->dev, "snic_das_tgt:%d:%d-%d",
+                            snic->shost->host_no, tgt->channel, tgt->id);
+               break;
+
+       case SNIC_TGT_SAN:
+               dev_set_name(&tgt->dev, "snic_san_tgt:%d:%d-%d",
+                            snic->shost->host_no, tgt->channel, tgt->id);
+               break;
+
+       default:
+               SNIC_HOST_INFO(snic->shost, "Target type Unknown Detected.\n");
+               dev_set_name(&tgt->dev, "snic_das_tgt:%d:%d-%d",
+                            snic->shost->host_no, tgt->channel, tgt->id);
+               break;
+       }
+
+       spin_lock_irqsave(snic->shost->host_lock, flags);
+       list_add_tail(&tgt->list, &snic->disc.tgt_list);
+       tgt->scsi_tgt_id = snic->disc.nxt_tgt_id++;
+       tgt->state = SNIC_TGT_STAT_ONLINE;
+       spin_unlock_irqrestore(snic->shost->host_lock, flags);
+
+       SNIC_HOST_INFO(snic->shost,
+                      "Tgt %d, type = %s detected. Adding..\n",
+                      tgt->id, snic_tgt_type_to_str(tgt->tdata.typ));
+
+       ret = device_add(&tgt->dev);
+       if (ret) {
+               SNIC_HOST_ERR(snic->shost,
+                             "Snic Tgt: device_add, with err = %d\n",
+                             ret);
+
+               put_device(&snic->shost->shost_gendev);
+               kfree(tgt);
+               tgt = NULL;
+
+               return tgt;
+       }
+
+       SNIC_HOST_INFO(snic->shost, "Scanning %s.\n", dev_name(&tgt->dev));
+
+       scsi_queue_work(snic->shost, &tgt->scan_work);
+
+       return tgt;
+} /* end of snic_tgt_create */
+
+/* Handler for discovery */
+void
+snic_handle_tgt_disc(struct work_struct *work)
+{
+       struct snic *snic = container_of(work, struct snic, tgt_work);
+       struct snic_tgt_id *tgtid = NULL;
+       struct snic_tgt *tgt = NULL;
+       unsigned long flags;
+       int i;
+
+       spin_lock_irqsave(&snic->snic_lock, flags);
+       if (snic->in_remove) {
+               spin_unlock_irqrestore(&snic->snic_lock, flags);
+               kfree(snic->disc.rtgt_info);
+
+               return;
+       }
+       spin_unlock_irqrestore(&snic->snic_lock, flags);
+
+       mutex_lock(&snic->disc.mutex);
+       /* Discover triggered during disc in progress */
+       if (snic->disc.req_cnt) {
+               snic->disc.state = SNIC_DISC_DONE;
+               snic->disc.req_cnt = 0;
+               mutex_unlock(&snic->disc.mutex);
+               kfree(snic->disc.rtgt_info);
+               snic->disc.rtgt_info = NULL;
+
+               SNIC_HOST_INFO(snic->shost, "tgt_disc: Discovery restart.\n");
+               /* Start Discovery Again */
+               snic_disc_start(snic);
+
+               return;
+       }
+
+       tgtid = (struct snic_tgt_id *)snic->disc.rtgt_info;
+
+       SNIC_BUG_ON(snic->disc.rtgt_cnt == 0 || tgtid == NULL);
+
+       for (i = 0; i < snic->disc.rtgt_cnt; i++) {
+               tgt = snic_tgt_create(snic, &tgtid[i]);
+               if (!tgt) {
+                       int buf_sz = snic->disc.rtgt_cnt * sizeof(*tgtid);
+
+                       SNIC_HOST_ERR(snic->shost, "Failed to create tgt.\n");
+                       snic_hex_dump("rpt_tgt_rsp", (char *)tgtid, buf_sz);
+                       break;
+               }
+       }
+
+       snic->disc.rtgt_info = NULL;
+       snic->disc.state = SNIC_DISC_DONE;
+       mutex_unlock(&snic->disc.mutex);
+
+       SNIC_HOST_INFO(snic->shost, "Discovery Completed.\n");
+
+       kfree(tgtid);
+} /* end of snic_handle_tgt_disc */
+
+
+int
+snic_report_tgt_cmpl_handler(struct snic *snic, struct snic_fw_req *fwreq)
+{
+
+       u8 typ, cmpl_stat;
+       u32 cmnd_id, hid, tgt_cnt = 0;
+       ulong ctx;
+       struct snic_req_info *rqi = NULL;
+       struct snic_tgt_id *tgtid;
+       int i, ret = 0;
+
+       snic_io_hdr_dec(&fwreq->hdr, &typ, &cmpl_stat, &cmnd_id, &hid, &ctx);
+       rqi = (struct snic_req_info *) ctx;
+       tgtid = (struct snic_tgt_id *) rqi->sge_va;
+
+       tgt_cnt = le32_to_cpu(fwreq->u.rpt_tgts_cmpl.tgt_cnt);
+       if (tgt_cnt == 0) {
+               SNIC_HOST_ERR(snic->shost, "No Targets Found on this host.\n");
+               ret = 1;
+
+               goto end;
+       }
+
+       /* printing list of targets here */
+       SNIC_HOST_INFO(snic->shost, "Target Count = %d\n", tgt_cnt);
+
+       SNIC_BUG_ON(tgt_cnt > snic->fwinfo.max_tgts);
+
+       for (i = 0; i < tgt_cnt; i++)
+               SNIC_HOST_INFO(snic->shost,
+                              "Tgt id = 0x%x\n",
+                              le32_to_cpu(tgtid[i].tgt_id));
+
+       /*
+        * Queue work for further processing,
+        * Response Buffer Memory is freed after creating targets
+        */
+       snic->disc.rtgt_cnt = tgt_cnt;
+       snic->disc.rtgt_info = (u8 *) tgtid;
+       queue_work(snic_glob->event_q, &snic->tgt_work);
+       ret = 0;
+
+end:
+       /* Unmap Response Buffer */
+       snic_pci_unmap_rsp_buf(snic, rqi);
+       if (ret)
+               kfree(tgtid);
+
+       rqi->sge_va = 0;
+       snic_release_untagged_req(snic, rqi);
+
+       return ret;
+} /* end of snic_report_tgt_cmpl_handler */
+
+/* Discovery init fn */
+void
+snic_disc_init(struct snic_disc *disc)
+{
+       INIT_LIST_HEAD(&disc->tgt_list);
+       mutex_init(&disc->mutex);
+       disc->disc_id = 0;
+       disc->nxt_tgt_id = 0;
+       disc->state = SNIC_DISC_INIT;
+       disc->req_cnt = 0;
+       disc->rtgt_cnt = 0;
+       disc->rtgt_info = NULL;
+       disc->cb = NULL;
+} /* end of snic_disc_init */
+
+/* Discovery, uninit fn */
+void
+snic_disc_term(struct snic *snic)
+{
+       struct snic_disc *disc = &snic->disc;
+
+       mutex_lock(&disc->mutex);
+       if (disc->req_cnt) {
+               disc->req_cnt = 0;
+               SNIC_SCSI_DBG(snic->shost, "Terminating Discovery.\n");
+       }
+       mutex_unlock(&disc->mutex);
+}
+
+/*
+ * snic_disc_start: Discovery Start ...
+ */
+int
+snic_disc_start(struct snic *snic)
+{
+       struct snic_disc *disc = &snic->disc;
+       int ret = 0;
+
+       SNIC_SCSI_DBG(snic->shost, "Discovery Start.\n");
+
+       mutex_lock(&disc->mutex);
+       if (disc->state == SNIC_DISC_PENDING) {
+               disc->req_cnt++;
+               mutex_unlock(&disc->mutex);
+
+               return ret;
+       }
+       disc->state = SNIC_DISC_PENDING;
+       mutex_unlock(&disc->mutex);
+
+       ret = snic_queue_report_tgt_req(snic);
+       if (ret)
+               SNIC_HOST_INFO(snic->shost, "Discovery Failed, err=%d.\n", ret);
+
+       return ret;
+} /* end of snic_disc_start */
+
+/*
+ * snic_disc_work :
+ */
+void
+snic_handle_disc(struct work_struct *work)
+{
+       struct snic *snic = container_of(work, struct snic, disc_work);
+       int ret = 0;
+
+       SNIC_HOST_INFO(snic->shost, "disc_work: Discovery\n");
+
+       ret = snic_disc_start(snic);
+       if (ret)
+               goto disc_err;
+
+disc_err:
+       SNIC_HOST_ERR(snic->shost,
+                     "disc_work: Discovery Failed w/ err = %d\n",
+                     ret);
+} /* end of snic_disc_work */
+
+/*
+ * snic_tgt_del_all : cleanup all snic targets
+ * Called on unbinding the interface
+ */
+void
+snic_tgt_del_all(struct snic *snic)
+{
+       struct snic_tgt *tgt = NULL;
+       struct list_head *cur, *nxt;
+       unsigned long flags;
+
+       mutex_lock(&snic->disc.mutex);
+       spin_lock_irqsave(snic->shost->host_lock, flags);
+
+       list_for_each_safe(cur, nxt, &snic->disc.tgt_list) {
+               tgt = list_entry(cur, struct snic_tgt, list);
+               tgt->state = SNIC_TGT_STAT_DEL;
+               list_del_init(&tgt->list);
+               SNIC_HOST_INFO(snic->shost, "Tgt %d q'ing for del\n", tgt->id);
+               queue_work(snic_glob->event_q, &tgt->del_work);
+               tgt = NULL;
+       }
+       spin_unlock_irqrestore(snic->shost->host_lock, flags);
+
+       scsi_flush_work(snic->shost);
+       mutex_unlock(&snic->disc.mutex);
+} /* end of snic_tgt_del_all */
diff --git a/drivers/scsi/snic/snic_disc.h b/drivers/scsi/snic/snic_disc.h
new file mode 100644 (file)
index 0000000..97fa3f5
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2014 Cisco Systems, Inc.  All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * 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 __SNIC_DISC_H
+#define __SNIC_DISC_H
+
+#include "snic_fwint.h"
+
+enum snic_disc_state {
+       SNIC_DISC_NONE,
+       SNIC_DISC_INIT,
+       SNIC_DISC_PENDING,
+       SNIC_DISC_DONE
+};
+
+struct snic;
+struct snic_disc {
+       struct list_head tgt_list;
+       enum snic_disc_state state;
+       struct mutex mutex;
+       u16     disc_id;
+       u8      req_cnt;
+       u32     nxt_tgt_id;
+       u32     rtgt_cnt;
+       u8      *rtgt_info;
+       struct delayed_work disc_timeout;
+       void (*cb)(struct snic *);
+};
+
+#define SNIC_TGT_NAM_LEN       16
+
+enum snic_tgt_state {
+       SNIC_TGT_STAT_NONE,
+       SNIC_TGT_STAT_INIT,
+       SNIC_TGT_STAT_ONLINE,   /* Target is Online */
+       SNIC_TGT_STAT_OFFLINE,  /* Target is Offline */
+       SNIC_TGT_STAT_DEL,
+};
+
+struct snic_tgt_priv {
+       struct list_head list;
+       enum snic_tgt_type typ;
+       u16 disc_id;
+       char *name[SNIC_TGT_NAM_LEN];
+
+       union {
+               /*DAS Target specific info */
+               /*SAN Target specific info */
+               u8 dummmy;
+       } u;
+};
+
+/* snic tgt flags */
+#define SNIC_TGT_SCAN_PENDING  0x01
+
+struct snic_tgt {
+       struct list_head list;
+       u16     id;
+       u16     channel;
+       u32     flags;
+       u32     scsi_tgt_id;
+       enum snic_tgt_state state;
+       struct device dev;
+       struct work_struct scan_work;
+       struct work_struct del_work;
+       struct snic_tgt_priv tdata;
+};
+
+
+struct snic_fw_req;
+
+void snic_disc_init(struct snic_disc *);
+int snic_disc_start(struct snic *);
+void snic_disc_term(struct snic *);
+int snic_report_tgt_cmpl_handler(struct snic *, struct snic_fw_req *);
+int snic_tgtinfo_cmpl_handler(struct snic *snic, struct snic_fw_req *fwreq);
+void snic_process_report_tgts_rsp(struct work_struct *);
+void snic_handle_tgt_disc(struct work_struct *);
+void snic_handle_disc(struct work_struct *);
+void snic_tgt_dev_release(struct device *);
+void snic_tgt_del_all(struct snic *);
+
+#define dev_to_tgt(d) \
+       container_of(d, struct snic_tgt, dev)
+
+static inline int
+is_snic_target(struct device *dev)
+{
+       return dev->release == snic_tgt_dev_release;
+}
+
+#define starget_to_tgt(st)     \
+       (is_snic_target(((struct scsi_target *) st)->dev.parent) ? \
+               dev_to_tgt(st->dev.parent) : NULL)
+
+#define snic_tgt_to_shost(t)   \
+       dev_to_shost(t->dev.parent)
+
+static inline int
+snic_tgt_chkready(struct snic_tgt *tgt)
+{
+       if (tgt->state == SNIC_TGT_STAT_ONLINE)
+               return 0;
+       else
+               return DID_NO_CONNECT << 16;
+}
+
+const char *snic_tgt_state_to_str(int);
+int snic_tgt_scsi_abort_io(struct snic_tgt *);
+#endif /* end of  __SNIC_DISC_H */
diff --git a/drivers/scsi/snic/snic_fwint.h b/drivers/scsi/snic/snic_fwint.h
new file mode 100644 (file)
index 0000000..2cfaf2d
--- /dev/null
@@ -0,0 +1,525 @@
+/*
+ * Copyright 2014 Cisco Systems, Inc.  All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * 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 __SNIC_FWINT_H
+#define __SNIC_FWINT_H
+
+#define SNIC_CDB_LEN   32      /* SCSI CDB size 32, can be used for 16 bytes */
+#define LUN_ADDR_LEN   8
+
+/*
+ * Command entry type
+ */
+enum snic_io_type {
+       /*
+        * Initiator request types
+        */
+       SNIC_REQ_REPORT_TGTS = 0x2,     /* Report Targets */
+       SNIC_REQ_ICMND,                 /* Initiator command for SCSI IO */
+       SNIC_REQ_ITMF,                  /* Initiator command for Task Mgmt */
+       SNIC_REQ_HBA_RESET,             /* SNIC Reset */
+       SNIC_REQ_EXCH_VER,              /* Exchange Version Information */
+       SNIC_REQ_TGT_INFO,              /* Backend/Target Information */
+       SNIC_REQ_BOOT_LUNS,
+
+       /*
+        * Response type
+        */
+       SNIC_RSP_REPORT_TGTS_CMPL = 0x12,/* Report Targets Completion */
+       SNIC_RSP_ICMND_CMPL,            /* SCSI IO Completion */
+       SNIC_RSP_ITMF_CMPL,             /* Task Management Completion */
+       SNIC_RSP_HBA_RESET_CMPL,        /* SNIC Reset Completion */
+       SNIC_RSP_EXCH_VER_CMPL,         /* Exchange Version Completion*/
+       SNIC_RSP_BOOT_LUNS_CMPL,
+
+       /*
+        * Misc Request types
+        */
+       SNIC_MSG_ACK = 0x80,            /* Ack: snic_notify_msg */
+       SNIC_MSG_ASYNC_EVNOTIFY,        /* Asynchronous Event Notification */
+}; /* end of enum snic_io_type */
+
+
+/*
+ * Header status codes from firmware
+ */
+enum snic_io_status {
+       SNIC_STAT_IO_SUCCESS = 0,       /* request was successful */
+
+       /*
+        * If a request to the fw is rejected, the original request header
+        * will be returned with the status set to one of the following:
+        */
+       SNIC_STAT_INVALID_HDR,  /* header contains invalid data */
+       SNIC_STAT_OUT_OF_RES,   /* out of resources to complete request */
+       SNIC_STAT_INVALID_PARM, /* some parameter in request is not valid */
+       SNIC_STAT_REQ_NOT_SUP,  /* req type is not supported */
+       SNIC_STAT_IO_NOT_FOUND, /* requested IO was not found */
+
+       /*
+        * Once a request is processed, the fw will usually return
+        * a cmpl message type. In cases where errors occurred,
+        * the header status would be filled in with one of the following:
+        */
+       SNIC_STAT_ABORTED,              /* req was aborted */
+       SNIC_STAT_TIMEOUT,              /* req was timed out */
+       SNIC_STAT_SGL_INVALID,          /* req was aborted due to sgl error */
+       SNIC_STAT_DATA_CNT_MISMATCH,    /*recv/sent more/less data than expec */
+       SNIC_STAT_FW_ERR,               /* req was terminated due to fw error */
+       SNIC_STAT_ITMF_REJECT,          /* itmf req was rejected by target */
+       SNIC_STAT_ITMF_FAIL,            /* itmf req was failed */
+       SNIC_STAT_ITMF_INCORRECT_LUN,   /* itmf req has incorrect LUN id*/
+       SNIC_STAT_CMND_REJECT,          /* req was invalid and rejected */
+       SNIC_STAT_DEV_OFFLINE,          /* req sent to offline device */
+       SNIC_STAT_NO_BOOTLUN,
+       SNIC_STAT_SCSI_ERR,             /* SCSI error returned by Target. */
+       SNIC_STAT_NOT_READY,            /* sNIC Subsystem is not ready */
+       SNIC_STAT_FATAL_ERROR,          /* sNIC is in unrecoverable state */
+}; /* end of enum snic_io_status */
+
+/*
+ * snic_io_hdr : host <--> firmare
+ *
+ * for any other message that will be queued to firmware should
+ *  have the following request header
+ */
+struct snic_io_hdr {
+       __le32  hid;
+       __le32  cmnd_id;        /* tag here */
+       ulong   init_ctx;       /* initiator context */
+       u8      type;           /* request/response type */
+       u8      status;         /* header status entry */
+       u8      protocol;       /* Protocol specific, may needed for RoCE*/
+       u8      flags;
+       __le16  sg_cnt;
+       u16     resvd;
+};
+
+/* auxillary funciton for encoding the snic_io_hdr */
+static inline void
+snic_io_hdr_enc(struct snic_io_hdr *hdr, u8 typ, u8 status, u32 id, u32 hid,
+               u16 sg_cnt, ulong ctx)
+{
+       hdr->type = typ;
+       hdr->status = status;
+       hdr->protocol = 0;
+       hdr->hid = cpu_to_le32(hid);
+       hdr->cmnd_id = cpu_to_le32(id);
+       hdr->sg_cnt = cpu_to_le16(sg_cnt);
+       hdr->init_ctx = ctx;
+       hdr->flags = 0;
+}
+
+/* auxillary funciton for decoding the snic_io_hdr */
+static inline void
+snic_io_hdr_dec(struct snic_io_hdr *hdr, u8 *typ, u8 *stat, u32 *cmnd_id,
+               u32 *hid, ulong *ctx)
+{
+       *typ = hdr->type;
+       *stat = hdr->status;
+       *hid = le32_to_cpu(hdr->hid);
+       *cmnd_id = le32_to_cpu(hdr->cmnd_id);
+       *ctx = hdr->init_ctx;
+}
+
+/*
+ * snic_host_info: host -> firmware
+ *
+ * Used for sending host information to firmware, and request fw version
+ */
+struct snic_exch_ver_req {
+       __le32  drvr_ver;       /* for debugging, when fw dump captured */
+       __le32  os_type;        /* for OS specific features */
+};
+
+/*
+ * os_type flags
+ * Bit 0-7 : OS information
+ * Bit 8-31: Feature/Capability Information
+ */
+#define SNIC_OS_LINUX  0x1
+#define SNIC_OS_WIN    0x2
+#define SNIC_OS_ESX    0x3
+
+/*
+ * HBA Capabilities
+ * Bit 1: Reserved.
+ * Bit 2: Dynamic Discovery of LUNs.
+ * Bit 3: Async event notifications on on tgt online/offline events.
+ * Bit 4: IO timeout support in FW.
+ * Bit 5-31: Reserved.
+ */
+#define SNIC_HBA_CAP_DDL       0x02    /* Supports Dynamic Discovery of LUNs */
+#define SNIC_HBA_CAP_AEN       0x04    /* Supports Async Event Noitifcation */
+#define SNIC_HBA_CAP_TMO       0x08    /* Supports IO timeout in FW */
+
+/*
+ * snic_exch_ver_rsp : firmware -> host
+ *
+ * Used by firmware to send response to version request
+ */
+struct snic_exch_ver_rsp {
+       __le32  version;
+       __le32  hid;
+       __le32  max_concur_ios;         /* max concurrent ios */
+       __le32  max_sgs_per_cmd;        /* max sgls per IO */
+       __le32  max_io_sz;              /* max io size supported */
+       __le32  hba_cap;                /* hba capabilities */
+       __le32  max_tgts;               /* max tgts supported */
+       __le16  io_timeout;             /* FW extended timeout */
+       u16     rsvd;
+};
+
+
+/*
+ * snic_report_tgts : host -> firmware request
+ *
+ * Used by the host to request list of targets
+ */
+struct snic_report_tgts {
+       __le16  sg_cnt;
+       __le16  flags;          /* specific flags from fw */
+       u8      _resvd[4];
+       __le64  sg_addr;        /* Points to SGL */
+       __le64  sense_addr;
+};
+
+enum snic_type {
+       SNIC_NONE = 0x0,
+       SNIC_DAS,
+       SNIC_SAN,
+};
+
+
+/* Report Target Response */
+enum snic_tgt_type {
+       SNIC_TGT_NONE = 0x0,
+       SNIC_TGT_DAS,   /* DAS Target */
+       SNIC_TGT_SAN,   /* SAN Target */
+};
+
+/* target id format */
+struct snic_tgt_id {
+       __le32  tgt_id;         /* target id */
+       __le16  tgt_type;       /* tgt type */
+       __le16  vnic_id;        /* corresponding vnic id */
+};
+
+/*
+ * snic_report_tgts_cmpl : firmware -> host response
+ *
+ * Used by firmware to send response to Report Targets request
+ */
+struct snic_report_tgts_cmpl {
+       __le32  tgt_cnt;        /* Number of Targets accessible */
+       u32     _resvd;
+};
+
+/*
+ * Command flags
+ *
+ * Bit 0: Read flags
+ * Bit 1: Write flag
+ * Bit 2: ESGL - sg/esg array contains extended sg
+ *       ESGE - is a host buffer contains sg elements
+ * Bit 3-4: Task Attributes
+ *             00b - simple
+ *             01b - head of queue
+ *             10b - ordered
+ * Bit 5-7: Priority - future use
+ * Bit 8-15: Reserved
+ */
+
+#define SNIC_ICMND_WR          0x01    /* write command */
+#define SNIC_ICMND_RD          0x02    /* read command */
+#define SNIC_ICMND_ESGL                0x04    /* SGE/ESGE array contains valid data*/
+
+/*
+ * Priority/Task Attribute settings
+ */
+#define SNIC_ICMND_TSK_SHIFT           2       /* task attr starts at bit 2 */
+#define SNIC_ICMND_TSK_MASK(x)         ((x>>SNIC_ICMND_TSK_SHIFT) & ~(0xffff))
+#define SNIC_ICMND_TSK_SIMPLE          0       /* simple task attr */
+#define SNIC_ICMND_TSK_HEAD_OF_QUEUE   1       /* head of qeuue task attr */
+#define SNIC_ICMND_TSK_ORDERED         2       /* ordered task attr */
+
+#define SNIC_ICMND_PRI_SHIFT           5       /* prio val starts at bit 5 */
+
+/*
+ * snic_icmnd : host-> firmware request
+ *
+ * used for sending out an initiator SCSI 16/32-byte command
+ */
+struct snic_icmnd {
+       __le16  sg_cnt;         /* Number of SG Elements */
+       __le16  flags;          /* flags */
+       __le32  sense_len;      /* Sense buffer length */
+       __le64  tgt_id;         /* Destination Target ID */
+       __le64  lun_id;         /* Destination LUN ID */
+       u8      cdb_len;
+       u8      _resvd;
+       __le16  time_out;       /* ms time for Res allocations fw to handle io*/
+       __le32  data_len;       /* Total number of bytes to be transferred */
+       u8      cdb[SNIC_CDB_LEN];
+       __le64  sg_addr;        /* Points to SG List */
+       __le64  sense_addr;     /* Sense buffer address */
+};
+
+
+/* Response flags */
+/* Bit 0: Under run
+ * Bit 1: Over Run
+ * Bit 2-7: Reserved
+ */
+#define SNIC_ICMND_CMPL_UNDR_RUN       0x01    /* resid under and valid */
+#define SNIC_ICMND_CMPL_OVER_RUN       0x02    /* resid over and valid */
+
+/*
+ * snic_icmnd_cmpl: firmware -> host response
+ *
+ * Used for sending the host a response to an icmnd (initiator command)
+ */
+struct snic_icmnd_cmpl {
+       u8      scsi_status;    /* value as per SAM */
+       u8      flags;
+       __le16  sense_len;      /* Sense Length */
+       __le32  resid;          /* Residue : # bytes under or over run */
+};
+
+/*
+ * snic_itmf: host->firmware request
+ *
+ * used for requesting the firmware to abort a request and/or send out
+ * a task management function
+ *
+ * the req_id field is valid in case of abort task and clear task
+ */
+struct snic_itmf {
+       u8      tm_type;        /* SCSI Task Management request */
+       u8      resvd;
+       __le16  flags;          /* flags */
+       __le32  req_id;         /* Command id of snic req to be aborted */
+       __le64  tgt_id;         /* Target ID */
+       __le64  lun_id;         /* Destination LUN ID */
+       __le16  timeout;        /* in sec */
+};
+
+/*
+ * Task Management Request
+ */
+enum snic_itmf_tm_type {
+       SNIC_ITMF_ABTS_TASK = 0x01,     /* Abort Task */
+       SNIC_ITMF_ABTS_TASK_SET,        /* Abort Task Set */
+       SNIC_ITMF_CLR_TASK,             /* Clear Task */
+       SNIC_ITMF_CLR_TASKSET,          /* Clear Task Set */
+       SNIC_ITMF_LUN_RESET,            /* Lun Reset */
+       SNIC_ITMF_ABTS_TASK_TERM,       /* Supported for SAN Targets */
+};
+
+/*
+ * snic_itmf_cmpl: firmware -> host resposne
+ *
+ * used for sending the host a response for a itmf request
+ */
+struct snic_itmf_cmpl {
+       __le32  nterminated;    /* # IOs terminated as a result of tmf */
+       u8      flags;          /* flags */
+       u8      _resvd[3];
+};
+
+/*
+ * itmfl_cmpl flags
+ * Bit 0 : 1 - Num terminated field valid
+ * Bit 1 - 7 : Reserved
+ */
+#define SNIC_NUM_TERM_VALID    0x01    /* Number of IOs terminated */
+
+/*
+ * snic_hba_reset: host -> firmware request
+ *
+ * used for requesting firmware to reset snic
+ */
+struct snic_hba_reset {
+       __le16  flags;          /* flags */
+       u8      _resvd[6];
+};
+
+/*
+ * snic_hba_reset_cmpl: firmware -> host response
+ *
+ * Used by firmware to respond to the host's hba reset request
+ */
+struct snic_hba_reset_cmpl {
+       u8      flags;          /* flags : more info needs to be added*/
+       u8      _resvd[7];
+};
+
+/*
+ * snic_notify_msg: firmware -> host response
+ *
+ * Used by firmware to notify host of the last work queue entry received
+ */
+struct snic_notify_msg {
+       __le32  wqe_num;        /* wq entry number */
+       u8      flags;          /* flags, macros */
+       u8      _resvd[4];
+};
+
+
+#define SNIC_EVDATA_LEN                24      /* in bytes */
+/* snic_async_evnotify: firmware -> host notification
+ *
+ * Used by firmware to notify the host about configuration/state changes
+ */
+struct snic_async_evnotify {
+       u8      FLS_EVENT_DESC;
+       u8      vnic;                   /* vnic id */
+       u8      _resvd[2];
+       __le32  ev_id;                  /* Event ID */
+       u8      ev_data[SNIC_EVDATA_LEN]; /* Event Data */
+       u8      _resvd2[4];
+};
+
+/* async event flags */
+enum snic_ev_type {
+       SNIC_EV_TGT_OFFLINE = 0x01, /* Target Offline, PL contains TGT ID */
+       SNIC_EV_TGT_ONLINE,     /* Target Online, PL contains TGT ID */
+       SNIC_EV_LUN_OFFLINE,    /* LUN Offline, PL contains LUN ID */
+       SNIC_EV_LUN_ONLINE,     /* LUN Online, PL contains LUN ID */
+       SNIC_EV_CONF_CHG,       /* Dev Config/Attr Change Event */
+       SNIC_EV_TGT_ADDED,      /* Target Added */
+       SNIC_EV_TGT_DELTD,      /* Target Del'd, PL contains TGT ID */
+       SNIC_EV_LUN_ADDED,      /* LUN Added */
+       SNIC_EV_LUN_DELTD,      /* LUN Del'd, PL cont. TGT & LUN ID */
+
+       SNIC_EV_DISC_CMPL = 0x10, /* Discovery Completed Event */
+};
+
+
+#define SNIC_HOST_REQ_LEN      128     /*Exp length of host req, wq desc sz*/
+/* Payload 88 bytes = 128 - 24 - 16 */
+#define SNIC_HOST_REQ_PAYLOAD  ((int)(SNIC_HOST_REQ_LEN -              \
+                                       sizeof(struct snic_io_hdr) -    \
+                                       (2 * sizeof(u64))))
+
+/*
+ * snic_host_req: host -> firmware request
+ *
+ * Basic structure for all snic requests that are sent from the host to
+ * firmware. They are 128 bytes in size.
+ */
+struct snic_host_req {
+       u64     ctrl_data[2];   /*16 bytes - Control Data */
+       struct snic_io_hdr hdr;
+       union {
+               /*
+                * Entry specific space, last byte contains color
+                */
+               u8      buf[SNIC_HOST_REQ_PAYLOAD];
+
+               /*
+                * Exchange firmware version
+                */
+               struct snic_exch_ver_req        exch_ver;
+
+               /* report targets */
+               struct snic_report_tgts         rpt_tgts;
+
+               /* io request */
+               struct snic_icmnd               icmnd;
+
+               /* task management request */
+               struct snic_itmf                itmf;
+
+               /* hba reset */
+               struct snic_hba_reset           reset;
+       } u;
+}; /* end of snic_host_req structure */
+
+
+#define SNIC_FW_REQ_LEN                64 /* Expected length of fw req */
+struct snic_fw_req {
+       struct snic_io_hdr hdr;
+       union {
+               /*
+                * Entry specific space, last byte contains color
+                */
+               u8      buf[SNIC_FW_REQ_LEN - sizeof(struct snic_io_hdr)];
+
+               /* Exchange Version Response */
+               struct snic_exch_ver_rsp        exch_ver_cmpl;
+
+               /* Report Targets Response */
+               struct snic_report_tgts_cmpl    rpt_tgts_cmpl;
+
+               /* scsi response */
+               struct snic_icmnd_cmpl          icmnd_cmpl;
+
+               /* task management response */
+               struct snic_itmf_cmpl           itmf_cmpl;
+
+               /* hba reset response */
+               struct snic_hba_reset_cmpl      reset_cmpl;
+
+               /* notify message */
+               struct snic_notify_msg          ack;
+
+               /* async notification event */
+               struct snic_async_evnotify      async_ev;
+
+       } u;
+}; /* end of snic_fw_req structure */
+
+/*
+ * Auxillary macro to verify specific snic req/cmpl structures
+ * to ensure that it will be aligned to 64 bit, and not using
+ * color bit field
+ */
+#define VERIFY_REQ_SZ(x)
+#define VERIFY_CMPL_SZ(x)
+
+/*
+ * Access routines to encode and decode the color bit, which is the most
+ * significant bit of the structure.
+ */
+static inline void
+snic_color_enc(struct snic_fw_req *req, u8 color)
+{
+       u8 *c = ((u8 *) req) + sizeof(struct snic_fw_req) - 1;
+
+       if (color)
+               *c |= 0x80;
+       else
+               *c &= ~0x80;
+}
+
+static inline void
+snic_color_dec(struct snic_fw_req *req, u8 *color)
+{
+       u8 *c = ((u8 *) req) + sizeof(struct snic_fw_req) - 1;
+
+       *color = *c >> 7;
+
+       /* Make sure color bit is read from desc *before* other fields
+        * are read from desc. Hardware guarantees color bit is last
+        * bit (byte) written. Adding the rmb() prevents the compiler
+        * and/or CPU from reordering the reads which would potentially
+        * result in reading stale values.
+        */
+       rmb();
+}
+#endif /* end of __SNIC_FWINT_H */
diff --git a/drivers/scsi/snic/snic_io.c b/drivers/scsi/snic/snic_io.c
new file mode 100644 (file)
index 0000000..993db7d
--- /dev/null
@@ -0,0 +1,518 @@
+/*
+ * Copyright 2014 Cisco Systems, Inc.  All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * 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/errno.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/spinlock.h>
+#include <linux/mempool.h>
+#include <scsi/scsi_tcq.h>
+
+#include "snic_io.h"
+#include "snic.h"
+#include "cq_enet_desc.h"
+#include "snic_fwint.h"
+
+static void
+snic_wq_cmpl_frame_send(struct vnic_wq *wq,
+                           struct cq_desc *cq_desc,
+                           struct vnic_wq_buf *buf,
+                           void *opaque)
+{
+       struct snic *snic = svnic_dev_priv(wq->vdev);
+
+       SNIC_BUG_ON(buf->os_buf == NULL);
+
+       if (snic_log_level & SNIC_DESC_LOGGING)
+               SNIC_HOST_INFO(snic->shost,
+                              "Ack received for snic_host_req %p.\n",
+                              buf->os_buf);
+
+       SNIC_TRC(snic->shost->host_no, 0, 0,
+                ((ulong)(buf->os_buf) - sizeof(struct snic_req_info)), 0, 0,
+                0);
+       pci_unmap_single(snic->pdev, buf->dma_addr, buf->len, PCI_DMA_TODEVICE);
+       buf->os_buf = NULL;
+}
+
+static int
+snic_wq_cmpl_handler_cont(struct vnic_dev *vdev,
+                         struct cq_desc *cq_desc,
+                         u8 type,
+                         u16 q_num,
+                         u16 cmpl_idx,
+                         void *opaque)
+{
+       struct snic *snic = svnic_dev_priv(vdev);
+       unsigned long flags;
+
+       SNIC_BUG_ON(q_num != 0);
+
+       spin_lock_irqsave(&snic->wq_lock[q_num], flags);
+       svnic_wq_service(&snic->wq[q_num],
+                        cq_desc,
+                        cmpl_idx,
+                        snic_wq_cmpl_frame_send,
+                        NULL);
+       spin_unlock_irqrestore(&snic->wq_lock[q_num], flags);
+
+       return 0;
+} /* end of snic_cmpl_handler_cont */
+
+int
+snic_wq_cmpl_handler(struct snic *snic, int work_to_do)
+{
+       unsigned int work_done = 0;
+       unsigned int i;
+
+       snic->s_stats.misc.last_ack_time = jiffies;
+       for (i = 0; i < snic->wq_count; i++) {
+               work_done += svnic_cq_service(&snic->cq[i],
+                                             work_to_do,
+                                             snic_wq_cmpl_handler_cont,
+                                             NULL);
+       }
+
+       return work_done;
+} /* end of snic_wq_cmpl_handler */
+
+void
+snic_free_wq_buf(struct vnic_wq *wq, struct vnic_wq_buf *buf)
+{
+
+       struct snic_host_req *req = buf->os_buf;
+       struct snic *snic = svnic_dev_priv(wq->vdev);
+       struct snic_req_info *rqi = NULL;
+       unsigned long flags;
+
+       pci_unmap_single(snic->pdev, buf->dma_addr, buf->len, PCI_DMA_TODEVICE);
+
+       rqi = req_to_rqi(req);
+       spin_lock_irqsave(&snic->spl_cmd_lock, flags);
+       if (list_empty(&rqi->list)) {
+               spin_unlock_irqrestore(&snic->spl_cmd_lock, flags);
+               goto end;
+       }
+
+       SNIC_BUG_ON(rqi->list.next == NULL); /* if not added to spl_cmd_list */
+       list_del_init(&rqi->list);
+       spin_unlock_irqrestore(&snic->spl_cmd_lock, flags);
+
+       if (rqi->sge_va) {
+               snic_pci_unmap_rsp_buf(snic, rqi);
+               kfree((void *)rqi->sge_va);
+               rqi->sge_va = 0;
+       }
+       snic_req_free(snic, rqi);
+       SNIC_HOST_INFO(snic->shost, "snic_free_wq_buf .. freed.\n");
+
+end:
+       return;
+}
+
+/* Criteria to select work queue in multi queue mode */
+static int
+snic_select_wq(struct snic *snic)
+{
+       /* No multi queue support for now */
+       BUILD_BUG_ON(SNIC_WQ_MAX > 1);
+
+       return 0;
+}
+
+int
+snic_queue_wq_desc(struct snic *snic, void *os_buf, u16 len)
+{
+       dma_addr_t pa = 0;
+       unsigned long flags;
+       struct snic_fw_stats *fwstats = &snic->s_stats.fw;
+       long act_reqs;
+       int q_num = 0;
+
+       snic_print_desc(__func__, os_buf, len);
+
+       /* Map request buffer */
+       pa = pci_map_single(snic->pdev, os_buf, len, PCI_DMA_TODEVICE);
+       if (pci_dma_mapping_error(snic->pdev, pa)) {
+               SNIC_HOST_ERR(snic->shost, "qdesc: PCI DMA Mapping Fail.\n");
+
+               return -ENOMEM;
+       }
+
+       q_num = snic_select_wq(snic);
+
+       spin_lock_irqsave(&snic->wq_lock[q_num], flags);
+       if (!svnic_wq_desc_avail(snic->wq)) {
+               pci_unmap_single(snic->pdev, pa, len, PCI_DMA_TODEVICE);
+               spin_unlock_irqrestore(&snic->wq_lock[q_num], flags);
+               atomic64_inc(&snic->s_stats.misc.wq_alloc_fail);
+               SNIC_DBG("host = %d, WQ is Full\n", snic->shost->host_no);
+
+               return -ENOMEM;
+       }
+
+       snic_queue_wq_eth_desc(&snic->wq[q_num], os_buf, pa, len, 0, 0, 1);
+       spin_unlock_irqrestore(&snic->wq_lock[q_num], flags);
+
+       /* Update stats */
+       act_reqs = atomic64_inc_return(&fwstats->actv_reqs);
+       if (act_reqs > atomic64_read(&fwstats->max_actv_reqs))
+               atomic64_set(&fwstats->max_actv_reqs, act_reqs);
+
+       return 0;
+} /* end of snic_queue_wq_desc() */
+
+/*
+ * snic_handle_untagged_req: Adds snic specific requests to spl_cmd_list.
+ * Purpose : Used during driver unload to clean up the requests.
+ */
+void
+snic_handle_untagged_req(struct snic *snic, struct snic_req_info *rqi)
+{
+       unsigned long flags;
+
+       INIT_LIST_HEAD(&rqi->list);
+
+       spin_lock_irqsave(&snic->spl_cmd_lock, flags);
+       list_add_tail(&rqi->list, &snic->spl_cmd_list);
+       spin_unlock_irqrestore(&snic->spl_cmd_lock, flags);
+}
+
+/*
+ * snic_req_init:
+ * Allocates snic_req_info + snic_host_req + sgl data, and initializes.
+ */
+struct snic_req_info *
+snic_req_init(struct snic *snic, int sg_cnt)
+{
+       u8 typ;
+       struct snic_req_info *rqi = NULL;
+
+       typ = (sg_cnt <= SNIC_REQ_CACHE_DFLT_SGL) ?
+               SNIC_REQ_CACHE_DFLT_SGL : SNIC_REQ_CACHE_MAX_SGL;
+
+       rqi = mempool_alloc(snic->req_pool[typ], GFP_ATOMIC);
+       if (!rqi) {
+               atomic64_inc(&snic->s_stats.io.alloc_fail);
+               SNIC_HOST_ERR(snic->shost,
+                             "Failed to allocate memory from snic req pool id = %d\n",
+                             typ);
+               return rqi;
+       }
+
+       memset(rqi, 0, sizeof(*rqi));
+       rqi->rq_pool_type = typ;
+       rqi->start_time = jiffies;
+       rqi->req = (struct snic_host_req *) (rqi + 1);
+       rqi->req_len = sizeof(struct snic_host_req);
+       rqi->snic = snic;
+
+       rqi->req = (struct snic_host_req *)(rqi + 1);
+
+       if (sg_cnt == 0)
+               goto end;
+
+       rqi->req_len += (sg_cnt * sizeof(struct snic_sg_desc));
+
+       if (sg_cnt > atomic64_read(&snic->s_stats.io.max_sgl))
+               atomic64_set(&snic->s_stats.io.max_sgl, sg_cnt);
+
+       SNIC_BUG_ON(sg_cnt > SNIC_MAX_SG_DESC_CNT);
+       atomic64_inc(&snic->s_stats.io.sgl_cnt[sg_cnt - 1]);
+
+end:
+       memset(rqi->req, 0, rqi->req_len);
+
+       /* pre initialization of init_ctx to support req_to_rqi */
+       rqi->req->hdr.init_ctx = (ulong) rqi;
+
+       SNIC_SCSI_DBG(snic->shost, "Req_alloc:rqi = %p allocatd.\n", rqi);
+
+       return rqi;
+} /* end of snic_req_init */
+
+/*
+ * snic_abort_req_init : Inits abort request.
+ */
+struct snic_host_req *
+snic_abort_req_init(struct snic *snic, struct snic_req_info *rqi)
+{
+       struct snic_host_req *req = NULL;
+
+       SNIC_BUG_ON(!rqi);
+
+       /* If abort to be issued second time, then reuse */
+       if (rqi->abort_req)
+               return rqi->abort_req;
+
+
+       req = mempool_alloc(snic->req_pool[SNIC_REQ_TM_CACHE], GFP_ATOMIC);
+       if (!req) {
+               SNIC_HOST_ERR(snic->shost, "abts:Failed to alloc tm req.\n");
+               WARN_ON_ONCE(1);
+
+               return NULL;
+       }
+
+       rqi->abort_req = req;
+       memset(req, 0, sizeof(struct snic_host_req));
+       /* pre initialization of init_ctx to support req_to_rqi */
+       req->hdr.init_ctx = (ulong) rqi;
+
+       return req;
+} /* end of snic_abort_req_init */
+
+/*
+ * snic_dr_req_init : Inits device reset req
+ */
+struct snic_host_req *
+snic_dr_req_init(struct snic *snic, struct snic_req_info *rqi)
+{
+       struct snic_host_req *req = NULL;
+
+       SNIC_BUG_ON(!rqi);
+
+       req = mempool_alloc(snic->req_pool[SNIC_REQ_TM_CACHE], GFP_ATOMIC);
+       if (!req) {
+               SNIC_HOST_ERR(snic->shost, "dr:Failed to alloc tm req.\n");
+               WARN_ON_ONCE(1);
+
+               return NULL;
+       }
+
+       SNIC_BUG_ON(rqi->dr_req != NULL);
+       rqi->dr_req = req;
+       memset(req, 0, sizeof(struct snic_host_req));
+       /* pre initialization of init_ctx to support req_to_rqi */
+       req->hdr.init_ctx = (ulong) rqi;
+
+       return req;
+} /* end of snic_dr_req_init */
+
+/* frees snic_req_info and snic_host_req */
+void
+snic_req_free(struct snic *snic, struct snic_req_info *rqi)
+{
+       SNIC_BUG_ON(rqi->req == rqi->abort_req);
+       SNIC_BUG_ON(rqi->req == rqi->dr_req);
+       SNIC_BUG_ON(rqi->sge_va != 0);
+
+       SNIC_SCSI_DBG(snic->shost,
+                     "Req_free:rqi %p:ioreq %p:abt %p:dr %p\n",
+                     rqi, rqi->req, rqi->abort_req, rqi->dr_req);
+
+       if (rqi->abort_req)
+               mempool_free(rqi->abort_req, snic->req_pool[SNIC_REQ_TM_CACHE]);
+
+       if (rqi->dr_req)
+               mempool_free(rqi->dr_req, snic->req_pool[SNIC_REQ_TM_CACHE]);
+
+       mempool_free(rqi, snic->req_pool[rqi->rq_pool_type]);
+}
+
+void
+snic_pci_unmap_rsp_buf(struct snic *snic, struct snic_req_info *rqi)
+{
+       struct snic_sg_desc *sgd;
+
+       sgd = req_to_sgl(rqi_to_req(rqi));
+       SNIC_BUG_ON(sgd[0].addr == 0);
+       pci_unmap_single(snic->pdev,
+                        le64_to_cpu(sgd[0].addr),
+                        le32_to_cpu(sgd[0].len),
+                        PCI_DMA_FROMDEVICE);
+}
+
+/*
+ * snic_free_all_untagged_reqs: Walks through untagged reqs and frees them.
+ */
+void
+snic_free_all_untagged_reqs(struct snic *snic)
+{
+       struct snic_req_info *rqi;
+       struct list_head *cur, *nxt;
+       unsigned long flags;
+
+       spin_lock_irqsave(&snic->spl_cmd_lock, flags);
+       list_for_each_safe(cur, nxt, &snic->spl_cmd_list) {
+               rqi = list_entry(cur, struct snic_req_info, list);
+               list_del_init(&rqi->list);
+               if (rqi->sge_va) {
+                       snic_pci_unmap_rsp_buf(snic, rqi);
+                       kfree((void *)rqi->sge_va);
+                       rqi->sge_va = 0;
+               }
+
+               snic_req_free(snic, rqi);
+       }
+       spin_unlock_irqrestore(&snic->spl_cmd_lock, flags);
+}
+
+/*
+ * snic_release_untagged_req : Unlinks the untagged req and frees it.
+ */
+void
+snic_release_untagged_req(struct snic *snic, struct snic_req_info *rqi)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&snic->snic_lock, flags);
+       if (snic->in_remove) {
+               spin_unlock_irqrestore(&snic->snic_lock, flags);
+               goto end;
+       }
+       spin_unlock_irqrestore(&snic->snic_lock, flags);
+
+       spin_lock_irqsave(&snic->spl_cmd_lock, flags);
+       if (list_empty(&rqi->list)) {
+               spin_unlock_irqrestore(&snic->spl_cmd_lock, flags);
+               goto end;
+       }
+       list_del_init(&rqi->list);
+       spin_unlock_irqrestore(&snic->spl_cmd_lock, flags);
+       snic_req_free(snic, rqi);
+
+end:
+       return;
+}
+
+/* dump buf in hex fmt */
+void
+snic_hex_dump(char *pfx, char *data, int len)
+{
+       SNIC_INFO("%s Dumping Data of Len = %d\n", pfx, len);
+       print_hex_dump_bytes(pfx, DUMP_PREFIX_NONE, data, len);
+}
+
+#define        LINE_BUFSZ      128     /* for snic_print_desc fn */
+static void
+snic_dump_desc(const char *fn, char *os_buf, int len)
+{
+       struct snic_host_req *req = (struct snic_host_req *) os_buf;
+       struct snic_fw_req *fwreq = (struct snic_fw_req *) os_buf;
+       struct snic_req_info *rqi = NULL;
+       char line[LINE_BUFSZ] = { '\0' };
+       char *cmd_str = NULL;
+
+       if (req->hdr.type >= SNIC_RSP_REPORT_TGTS_CMPL)
+               rqi = (struct snic_req_info *) fwreq->hdr.init_ctx;
+       else
+               rqi = (struct snic_req_info *) req->hdr.init_ctx;
+
+       SNIC_BUG_ON(rqi == NULL || rqi->req == NULL);
+       switch (req->hdr.type) {
+       case SNIC_REQ_REPORT_TGTS:
+               cmd_str = "report-tgt : ";
+               snprintf(line, LINE_BUFSZ, "SNIC_REQ_REPORT_TGTS :");
+               break;
+
+       case SNIC_REQ_ICMND:
+               cmd_str = "icmnd : ";
+               snprintf(line, LINE_BUFSZ, "SNIC_REQ_ICMND : 0x%x :",
+                        req->u.icmnd.cdb[0]);
+               break;
+
+       case SNIC_REQ_ITMF:
+               cmd_str = "itmf : ";
+               snprintf(line, LINE_BUFSZ, "SNIC_REQ_ITMF :");
+               break;
+
+       case SNIC_REQ_HBA_RESET:
+               cmd_str = "hba reset :";
+               snprintf(line, LINE_BUFSZ, "SNIC_REQ_HBA_RESET :");
+               break;
+
+       case SNIC_REQ_EXCH_VER:
+               cmd_str = "exch ver : ";
+               snprintf(line, LINE_BUFSZ, "SNIC_REQ_EXCH_VER :");
+               break;
+
+       case SNIC_REQ_TGT_INFO:
+               cmd_str = "tgt info : ";
+               break;
+
+       case SNIC_RSP_REPORT_TGTS_CMPL:
+               cmd_str = "report tgt cmpl : ";
+               snprintf(line, LINE_BUFSZ, "SNIC_RSP_REPORT_TGTS_CMPL :");
+               break;
+
+       case SNIC_RSP_ICMND_CMPL:
+               cmd_str = "icmnd_cmpl : ";
+               snprintf(line, LINE_BUFSZ, "SNIC_RSP_ICMND_CMPL : 0x%x :",
+                        rqi->req->u.icmnd.cdb[0]);
+               break;
+
+       case SNIC_RSP_ITMF_CMPL:
+               cmd_str = "itmf_cmpl : ";
+               snprintf(line, LINE_BUFSZ, "SNIC_RSP_ITMF_CMPL :");
+               break;
+
+       case SNIC_RSP_HBA_RESET_CMPL:
+               cmd_str = "hba_reset_cmpl : ";
+               snprintf(line, LINE_BUFSZ, "SNIC_RSP_HBA_RESET_CMPL :");
+               break;
+
+       case SNIC_RSP_EXCH_VER_CMPL:
+               cmd_str = "exch_ver_cmpl : ";
+               snprintf(line, LINE_BUFSZ, "SNIC_RSP_EXCH_VER_CMPL :");
+               break;
+
+       case SNIC_MSG_ACK:
+               cmd_str = "msg ack : ";
+               snprintf(line, LINE_BUFSZ, "SNIC_MSG_ACK :");
+               break;
+
+       case SNIC_MSG_ASYNC_EVNOTIFY:
+               cmd_str = "async notify : ";
+               snprintf(line, LINE_BUFSZ, "SNIC_MSG_ASYNC_EVNOTIFY :");
+               break;
+
+       default:
+               cmd_str = "unknown : ";
+               SNIC_BUG_ON(1);
+               break;
+       }
+
+       SNIC_INFO("%s:%s >>cmndid=%x:sg_cnt = %x:status = %x:ctx = %lx.\n",
+                 fn, line, req->hdr.cmnd_id, req->hdr.sg_cnt, req->hdr.status,
+                 req->hdr.init_ctx);
+
+       /* Enable it, to dump byte stream */
+       if (snic_log_level & 0x20)
+               snic_hex_dump(cmd_str, os_buf, len);
+} /* end of __snic_print_desc */
+
+void
+snic_print_desc(const char *fn, char *os_buf, int len)
+{
+       if (snic_log_level & SNIC_DESC_LOGGING)
+               snic_dump_desc(fn, os_buf, len);
+}
+
+void
+snic_calc_io_process_time(struct snic *snic, struct snic_req_info *rqi)
+{
+       u64 duration;
+
+       duration = jiffies - rqi->start_time;
+
+       if (duration > atomic64_read(&snic->s_stats.io.max_time))
+               atomic64_set(&snic->s_stats.io.max_time, duration);
+}
diff --git a/drivers/scsi/snic/snic_io.h b/drivers/scsi/snic/snic_io.h
new file mode 100644 (file)
index 0000000..093d652
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2014 Cisco Systems, Inc.  All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * 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 _SNIC_IO_H
+#define _SNIC_IO_H
+
+#define SNIC_DFLT_SG_DESC_CNT  32      /* Default descriptors for sgl */
+#define SNIC_MAX_SG_DESC_CNT   60      /* Max descriptor for sgl */
+#define SNIC_SG_DESC_ALIGN     16      /* Descriptor address alignment */
+
+/* SG descriptor for snic */
+struct snic_sg_desc {
+       __le64 addr;
+       __le32 len;
+       u32 _resvd;
+};
+
+struct snic_dflt_sgl {
+       struct snic_sg_desc sg_desc[SNIC_DFLT_SG_DESC_CNT];
+};
+
+struct snic_max_sgl {
+       struct snic_sg_desc sg_desc[SNIC_MAX_SG_DESC_CNT];
+};
+
+enum snic_req_cache_type {
+       SNIC_REQ_CACHE_DFLT_SGL = 0,    /* cache with default size sgl */
+       SNIC_REQ_CACHE_MAX_SGL,         /* cache with max size sgl */
+       SNIC_REQ_TM_CACHE,              /* cache for task mgmt reqs contains
+                                          snic_host_req objects only*/
+       SNIC_REQ_MAX_CACHES             /* number of sgl caches */
+};
+
+/* Per IO internal state */
+struct snic_internal_io_state {
+       char    *rqi;
+       u64     flags;
+       u32     state;
+       u32     abts_status;    /* Abort completion status */
+       u32     lr_status;      /* device reset completion status */
+};
+
+/* IO state machine */
+enum snic_ioreq_state {
+       SNIC_IOREQ_NOT_INITED = 0,
+       SNIC_IOREQ_PENDING,
+       SNIC_IOREQ_ABTS_PENDING,
+       SNIC_IOREQ_ABTS_COMPLETE,
+       SNIC_IOREQ_LR_PENDING,
+       SNIC_IOREQ_LR_COMPLETE,
+       SNIC_IOREQ_COMPLETE,
+};
+
+struct snic;
+struct snic_host_req;
+
+/*
+ * snic_req_info : Contains info about IO, one per scsi command.
+ * Notes: Make sure that the structure is aligned to 16 B
+ * this helps in easy access to snic_req_info from snic_host_req
+ */
+struct snic_req_info {
+       struct list_head list;
+       struct snic_host_req *req;
+       u64     start_time;             /* start time in jiffies */
+       u16     rq_pool_type;           /* noticion of request pool type */
+       u16     req_len;                /* buf len passing to fw (req + sgl)*/
+       u32     tgt_id;
+
+       u32     tm_tag;
+       u8      io_cmpl:1;              /* sets to 1 when fw completes IO */
+       u8      resvd[3];
+       struct scsi_cmnd *sc;           /* Associated scsi cmd */
+       struct snic     *snic;          /* Associated snic */
+       ulong   sge_va;                 /* Pointer to Resp Buffer */
+       u64     snsbuf_va;
+
+       struct snic_host_req *abort_req;
+       struct completion *abts_done;
+
+       struct snic_host_req *dr_req;
+       struct completion *dr_done;
+};
+
+
+#define rqi_to_req(rqi)        \
+       ((struct snic_host_req *) (((struct snic_req_info *)rqi)->req))
+
+#define req_to_rqi(req)        \
+       ((struct snic_req_info *) (((struct snic_host_req *)req)->hdr.init_ctx))
+
+#define req_to_sgl(req)        \
+       ((struct snic_sg_desc *) (((struct snic_host_req *)req)+1))
+
+struct snic_req_info *
+snic_req_init(struct snic *, int sg_cnt);
+void snic_req_free(struct snic *, struct snic_req_info *);
+void snic_calc_io_process_time(struct snic *, struct snic_req_info *);
+void snic_pci_unmap_rsp_buf(struct snic *, struct snic_req_info *);
+struct snic_host_req *
+snic_abort_req_init(struct snic *, struct snic_req_info *);
+struct snic_host_req *
+snic_dr_req_init(struct snic *, struct snic_req_info *);
+#endif /* _SNIC_IO_H */
diff --git a/drivers/scsi/snic/snic_isr.c b/drivers/scsi/snic/snic_isr.c
new file mode 100644 (file)
index 0000000..a85fae2
--- /dev/null
@@ -0,0 +1,204 @@
+/*
+ * Copyright 2014 Cisco Systems, Inc.  All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * 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/string.h>
+#include <linux/errno.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+
+#include "vnic_dev.h"
+#include "vnic_intr.h"
+#include "vnic_stats.h"
+#include "snic_io.h"
+#include "snic.h"
+
+
+/*
+ * snic_isr_msix_wq : MSIx ISR for work queue.
+ */
+
+static irqreturn_t
+snic_isr_msix_wq(int irq, void *data)
+{
+       struct snic *snic = data;
+       unsigned long wq_work_done = 0;
+
+       snic->s_stats.misc.last_isr_time = jiffies;
+       atomic64_inc(&snic->s_stats.misc.isr_cnt);
+
+       wq_work_done = snic_wq_cmpl_handler(snic, -1);
+       svnic_intr_return_credits(&snic->intr[SNIC_MSIX_WQ],
+                                 wq_work_done,
+                                 1 /* unmask intr */,
+                                 1 /* reset intr timer */);
+
+       return IRQ_HANDLED;
+} /* end of snic_isr_msix_wq */
+
+static irqreturn_t
+snic_isr_msix_io_cmpl(int irq, void *data)
+{
+       struct snic *snic = data;
+       unsigned long iocmpl_work_done = 0;
+
+       snic->s_stats.misc.last_isr_time = jiffies;
+       atomic64_inc(&snic->s_stats.misc.isr_cnt);
+
+       iocmpl_work_done = snic_fwcq_cmpl_handler(snic, -1);
+       svnic_intr_return_credits(&snic->intr[SNIC_MSIX_IO_CMPL],
+                                 iocmpl_work_done,
+                                 1 /* unmask intr */,
+                                 1 /* reset intr timer */);
+
+       return IRQ_HANDLED;
+} /* end of snic_isr_msix_io_cmpl */
+
+static irqreturn_t
+snic_isr_msix_err_notify(int irq, void *data)
+{
+       struct snic *snic = data;
+
+       snic->s_stats.misc.last_isr_time = jiffies;
+       atomic64_inc(&snic->s_stats.misc.isr_cnt);
+
+       svnic_intr_return_all_credits(&snic->intr[SNIC_MSIX_ERR_NOTIFY]);
+       snic_log_q_error(snic);
+
+       /*Handling link events */
+       snic_handle_link_event(snic);
+
+       return IRQ_HANDLED;
+} /* end of snic_isr_msix_err_notify */
+
+
+void
+snic_free_intr(struct snic *snic)
+{
+       int i;
+
+       /* ONLY interrupt mode MSIX is supported */
+       for (i = 0; i < ARRAY_SIZE(snic->msix); i++) {
+               if (snic->msix[i].requested) {
+                       free_irq(snic->msix_entry[i].vector,
+                                snic->msix[i].devid);
+               }
+       }
+} /* end of snic_free_intr */
+
+int
+snic_request_intr(struct snic *snic)
+{
+       int ret = 0, i;
+       enum vnic_dev_intr_mode intr_mode;
+
+       intr_mode = svnic_dev_get_intr_mode(snic->vdev);
+       SNIC_BUG_ON(intr_mode != VNIC_DEV_INTR_MODE_MSIX);
+
+       /*
+        * Currently HW supports single WQ and CQ. So passing devid as snic.
+        * When hardware supports multiple WQs and CQs, one idea is
+        * to pass devid as corresponding WQ or CQ ptr and retrieve snic
+        * from queue ptr.
+        * Except for err_notify, which is always one.
+        */
+       sprintf(snic->msix[SNIC_MSIX_WQ].devname,
+               "%.11s-scsi-wq",
+               snic->name);
+       snic->msix[SNIC_MSIX_WQ].isr = snic_isr_msix_wq;
+       snic->msix[SNIC_MSIX_WQ].devid = snic;
+
+       sprintf(snic->msix[SNIC_MSIX_IO_CMPL].devname,
+               "%.11s-io-cmpl",
+               snic->name);
+       snic->msix[SNIC_MSIX_IO_CMPL].isr = snic_isr_msix_io_cmpl;
+       snic->msix[SNIC_MSIX_IO_CMPL].devid = snic;
+
+       sprintf(snic->msix[SNIC_MSIX_ERR_NOTIFY].devname,
+               "%.11s-err-notify",
+               snic->name);
+       snic->msix[SNIC_MSIX_ERR_NOTIFY].isr = snic_isr_msix_err_notify;
+       snic->msix[SNIC_MSIX_ERR_NOTIFY].devid = snic;
+
+       for (i = 0; i < ARRAY_SIZE(snic->msix); i++) {
+               ret = request_irq(snic->msix_entry[i].vector,
+                                 snic->msix[i].isr,
+                                 0,
+                                 snic->msix[i].devname,
+                                 snic->msix[i].devid);
+               if (ret) {
+                       SNIC_HOST_ERR(snic->shost,
+                                     "MSI-X: requrest_irq(%d) failed %d\n",
+                                     i,
+                                     ret);
+                       snic_free_intr(snic);
+                       break;
+               }
+               snic->msix[i].requested = 1;
+       }
+
+       return ret;
+} /* end of snic_requrest_intr */
+
+int
+snic_set_intr_mode(struct snic *snic)
+{
+       unsigned int n = ARRAY_SIZE(snic->wq);
+       unsigned int m = SNIC_CQ_IO_CMPL_MAX;
+       unsigned int i;
+
+       /*
+        * We need n WQs, m CQs, and n+m+1 INTRs
+        * (last INTR is used for WQ/CQ errors and notification area
+        */
+
+       BUILD_BUG_ON((ARRAY_SIZE(snic->wq) + SNIC_CQ_IO_CMPL_MAX) >
+                       ARRAY_SIZE(snic->intr));
+       SNIC_BUG_ON(ARRAY_SIZE(snic->msix_entry) < (n + m + 1));
+
+       for (i = 0; i < (n + m + 1); i++)
+               snic->msix_entry[i].entry = i;
+
+       if (snic->wq_count >= n && snic->cq_count >= (n + m)) {
+               if (!pci_enable_msix(snic->pdev,
+                                    snic->msix_entry,
+                                    (n + m + 1))) {
+                       snic->wq_count = n;
+                       snic->cq_count = n + m;
+                       snic->intr_count = n + m + 1;
+                       snic->err_intr_offset = SNIC_MSIX_ERR_NOTIFY;
+
+                       SNIC_ISR_DBG(snic->shost,
+                                    "Using MSI-X Interrupts\n");
+                       svnic_dev_set_intr_mode(snic->vdev,
+                                               VNIC_DEV_INTR_MODE_MSIX);
+
+                       return 0;
+               }
+       }
+
+       svnic_dev_set_intr_mode(snic->vdev, VNIC_DEV_INTR_MODE_UNKNOWN);
+
+       return -EINVAL;
+} /* end of snic_set_intr_mode */
+
+void
+snic_clear_intr_mode(struct snic *snic)
+{
+       pci_disable_msix(snic->pdev);
+
+       svnic_dev_set_intr_mode(snic->vdev, VNIC_DEV_INTR_MODE_INTX);
+}
diff --git a/drivers/scsi/snic/snic_main.c b/drivers/scsi/snic/snic_main.c
new file mode 100644 (file)
index 0000000..b2b87ce
--- /dev/null
@@ -0,0 +1,1044 @@
+/*
+ * Copyright 2014 Cisco Systems, Inc.  All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * 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 <linux/mempool.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/skbuff.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/workqueue.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_tcq.h>
+
+#include "snic.h"
+#include "snic_fwint.h"
+
+#define PCI_DEVICE_ID_CISCO_SNIC       0x0046
+
+/* Supported devices by snic module */
+static struct pci_device_id snic_id_table[] = {
+       {PCI_DEVICE(0x1137, PCI_DEVICE_ID_CISCO_SNIC) },
+       { 0, }  /* end of table */
+};
+
+unsigned int snic_log_level = 0x0;
+module_param(snic_log_level, int, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(snic_log_level, "bitmask for snic logging levels");
+
+#ifdef CONFIG_SCSI_SNIC_DEBUG_FS
+unsigned int snic_trace_max_pages = 16;
+module_param(snic_trace_max_pages, uint, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(snic_trace_max_pages,
+               "Total allocated memory pages for snic trace buffer");
+
+#endif
+unsigned int snic_max_qdepth = SNIC_DFLT_QUEUE_DEPTH;
+module_param(snic_max_qdepth, uint, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(snic_max_qdepth, "Queue depth to report for each LUN");
+
+/*
+ * snic_slave_alloc : callback function to SCSI Mid Layer, called on
+ * scsi device initialization.
+ */
+static int
+snic_slave_alloc(struct scsi_device *sdev)
+{
+       struct snic_tgt *tgt = starget_to_tgt(scsi_target(sdev));
+
+       if (!tgt || snic_tgt_chkready(tgt))
+               return -ENXIO;
+
+       return 0;
+}
+
+/*
+ * snic_slave_configure : callback function to SCSI Mid Layer, called on
+ * scsi device initialization.
+ */
+static int
+snic_slave_configure(struct scsi_device *sdev)
+{
+       struct snic *snic = shost_priv(sdev->host);
+       u32 qdepth = 0, max_ios = 0;
+       int tmo = SNIC_DFLT_CMD_TIMEOUT * HZ;
+
+       /* Set Queue Depth */
+       max_ios = snic_max_qdepth;
+       qdepth = min_t(u32, max_ios, SNIC_MAX_QUEUE_DEPTH);
+       scsi_change_queue_depth(sdev, qdepth);
+
+       if (snic->fwinfo.io_tmo > 1)
+               tmo = snic->fwinfo.io_tmo * HZ;
+
+       /* FW requires extended timeouts */
+       blk_queue_rq_timeout(sdev->request_queue, tmo);
+
+       return 0;
+}
+
+static int
+snic_change_queue_depth(struct scsi_device *sdev, int qdepth)
+{
+       int qsz = 0;
+
+       qsz = min_t(u32, qdepth, SNIC_MAX_QUEUE_DEPTH);
+       scsi_change_queue_depth(sdev, qsz);
+       SNIC_INFO("QDepth Changed to %d\n", sdev->queue_depth);
+
+       return sdev->queue_depth;
+}
+
+static struct scsi_host_template snic_host_template = {
+       .module = THIS_MODULE,
+       .name = SNIC_DRV_NAME,
+       .queuecommand = snic_queuecommand,
+       .eh_abort_handler = snic_abort_cmd,
+       .eh_device_reset_handler = snic_device_reset,
+       .eh_host_reset_handler = snic_host_reset,
+       .slave_alloc = snic_slave_alloc,
+       .slave_configure = snic_slave_configure,
+       .change_queue_depth = snic_change_queue_depth,
+       .this_id = -1,
+       .cmd_per_lun = SNIC_DFLT_QUEUE_DEPTH,
+       .can_queue = SNIC_MAX_IO_REQ,
+       .use_clustering = ENABLE_CLUSTERING,
+       .sg_tablesize = SNIC_MAX_SG_DESC_CNT,
+       .max_sectors = 0x800,
+       .shost_attrs = snic_attrs,
+       .use_blk_tags = 1,
+       .track_queue_depth = 1,
+       .cmd_size = sizeof(struct snic_internal_io_state),
+       .proc_name = "snic_scsi",
+};
+
+/*
+ * snic_handle_link_event : Handles link events such as link up/down/error
+ */
+void
+snic_handle_link_event(struct snic *snic)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&snic->snic_lock, flags);
+       if (snic->stop_link_events) {
+               spin_unlock_irqrestore(&snic->snic_lock, flags);
+
+               return;
+       }
+       spin_unlock_irqrestore(&snic->snic_lock, flags);
+
+       queue_work(snic_glob->event_q, &snic->link_work);
+} /* end of snic_handle_link_event */
+
+/*
+ * snic_notify_set : sets notification area
+ * This notification area is to receive events from fw
+ * Note: snic supports only MSIX interrupts, in which we can just call
+ *  svnic_dev_notify_set directly
+ */
+static int
+snic_notify_set(struct snic *snic)
+{
+       int ret = 0;
+       enum vnic_dev_intr_mode intr_mode;
+
+       intr_mode = svnic_dev_get_intr_mode(snic->vdev);
+
+       if (intr_mode == VNIC_DEV_INTR_MODE_MSIX) {
+               ret = svnic_dev_notify_set(snic->vdev, SNIC_MSIX_ERR_NOTIFY);
+       } else {
+               SNIC_HOST_ERR(snic->shost,
+                             "Interrupt mode should be setup before devcmd notify set %d\n",
+                             intr_mode);
+               ret = -1;
+       }
+
+       return ret;
+} /* end of snic_notify_set */
+
+/*
+ * snic_dev_wait : polls vnic open status.
+ */
+static int
+snic_dev_wait(struct vnic_dev *vdev,
+               int (*start)(struct vnic_dev *, int),
+               int (*finished)(struct vnic_dev *, int *),
+               int arg)
+{
+       unsigned long time;
+       int ret, done;
+       int retry_cnt = 0;
+
+       ret = start(vdev, arg);
+       if (ret)
+               return ret;
+
+       /*
+        * Wait for func to complete...2 seconds max.
+        *
+        * Sometimes schedule_timeout_uninterruptible take long time
+        * to wakeup, which results skipping retry. The retry counter
+        * ensures to retry at least two times.
+        */
+       time = jiffies + (HZ * 2);
+       do {
+               ret = finished(vdev, &done);
+               if (ret)
+                       return ret;
+
+               if (done)
+                       return 0;
+               schedule_timeout_uninterruptible(HZ/10);
+               ++retry_cnt;
+       } while (time_after(time, jiffies) || (retry_cnt < 3));
+
+       return -ETIMEDOUT;
+} /* end of snic_dev_wait */
+
+/*
+ * snic_cleanup: called by snic_remove
+ * Stops the snic device, masks all interrupts, Completed CQ entries are
+ * drained. Posted WQ/RQ/Copy-WQ entries are cleanup
+ */
+static int
+snic_cleanup(struct snic *snic)
+{
+       unsigned int i;
+       int ret;
+
+       svnic_dev_disable(snic->vdev);
+       for (i = 0; i < snic->intr_count; i++)
+               svnic_intr_mask(&snic->intr[i]);
+
+       for (i = 0; i < snic->wq_count; i++) {
+               ret = svnic_wq_disable(&snic->wq[i]);
+               if (ret)
+                       return ret;
+       }
+
+       /* Clean up completed IOs */
+       snic_fwcq_cmpl_handler(snic, -1);
+
+       snic_wq_cmpl_handler(snic, -1);
+
+       /* Clean up the IOs that have not completed */
+       for (i = 0; i < snic->wq_count; i++)
+               svnic_wq_clean(&snic->wq[i], snic_free_wq_buf);
+
+       for (i = 0; i < snic->cq_count; i++)
+               svnic_cq_clean(&snic->cq[i]);
+
+       for (i = 0; i < snic->intr_count; i++)
+               svnic_intr_clean(&snic->intr[i]);
+
+       /* Cleanup snic specific requests */
+       snic_free_all_untagged_reqs(snic);
+
+       /* Cleanup Pending SCSI commands */
+       snic_shutdown_scsi_cleanup(snic);
+
+       for (i = 0; i < SNIC_REQ_MAX_CACHES; i++)
+               mempool_destroy(snic->req_pool[i]);
+
+       return 0;
+} /* end of snic_cleanup */
+
+
+static void
+snic_iounmap(struct snic *snic)
+{
+       if (snic->bar0.vaddr)
+               iounmap(snic->bar0.vaddr);
+}
+
+/*
+ * snic_vdev_open_done : polls for svnic_dev_open cmd completion.
+ */
+static int
+snic_vdev_open_done(struct vnic_dev *vdev, int *done)
+{
+       struct snic *snic = svnic_dev_priv(vdev);
+       int ret;
+       int nretries = 5;
+
+       do {
+               ret = svnic_dev_open_done(vdev, done);
+               if (ret == 0)
+                       break;
+
+               SNIC_HOST_INFO(snic->shost, "VNIC_DEV_OPEN Timedout.\n");
+       } while (nretries--);
+
+       return ret;
+} /* end of snic_vdev_open_done */
+
+/*
+ * snic_add_host : registers scsi host with ML
+ */
+static int
+snic_add_host(struct Scsi_Host *shost, struct pci_dev *pdev)
+{
+       int ret = 0;
+
+       ret = scsi_add_host(shost, &pdev->dev);
+       if (ret) {
+               SNIC_HOST_ERR(shost,
+                             "snic: scsi_add_host failed. %d\n",
+                             ret);
+
+               return ret;
+       }
+
+       SNIC_BUG_ON(shost->work_q != NULL);
+       snprintf(shost->work_q_name, sizeof(shost->work_q_name), "scsi_wq_%d",
+                shost->host_no);
+       shost->work_q = create_singlethread_workqueue(shost->work_q_name);
+       if (!shost->work_q) {
+               SNIC_HOST_ERR(shost, "Failed to Create ScsiHost wq.\n");
+
+               ret = -ENOMEM;
+       }
+
+       return ret;
+} /* end of snic_add_host */
+
+static void
+snic_del_host(struct Scsi_Host *shost)
+{
+       if (!shost->work_q)
+               return;
+
+       destroy_workqueue(shost->work_q);
+       shost->work_q = NULL;
+       scsi_remove_host(shost);
+}
+
+int
+snic_get_state(struct snic *snic)
+{
+       return atomic_read(&snic->state);
+}
+
+void
+snic_set_state(struct snic *snic, enum snic_state state)
+{
+       SNIC_HOST_INFO(snic->shost, "snic state change from %s to %s\n",
+                      snic_state_to_str(snic_get_state(snic)),
+                      snic_state_to_str(state));
+
+       atomic_set(&snic->state, state);
+}
+
+/*
+ * snic_probe : Initialize the snic interface.
+ */
+static int
+snic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+       struct Scsi_Host *shost;
+       struct snic *snic;
+       mempool_t *pool;
+       unsigned long flags;
+       u32 max_ios = 0;
+       int ret, i;
+
+       /* Device Information */
+       SNIC_INFO("snic device %4x:%4x:%4x:%4x: ",
+                 pdev->vendor, pdev->device, pdev->subsystem_vendor,
+                 pdev->subsystem_device);
+
+       SNIC_INFO("snic device bus %x: slot %x: fn %x\n",
+                 pdev->bus->number, PCI_SLOT(pdev->devfn),
+                 PCI_FUNC(pdev->devfn));
+
+       /*
+        * Allocate SCSI Host and setup association between host, and snic
+        */
+       shost = scsi_host_alloc(&snic_host_template, sizeof(struct snic));
+       if (!shost) {
+               SNIC_ERR("Unable to alloc scsi_host\n");
+               ret = -ENOMEM;
+
+               goto prob_end;
+       }
+       snic = shost_priv(shost);
+       snic->shost = shost;
+
+       snprintf(snic->name, sizeof(snic->name) - 1, "%s%d", SNIC_DRV_NAME,
+                shost->host_no);
+
+       SNIC_HOST_INFO(shost,
+                      "snic%d = %p shost = %p device bus %x: slot %x: fn %x\n",
+                      shost->host_no, snic, shost, pdev->bus->number,
+                      PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));
+#ifdef CONFIG_SCSI_SNIC_DEBUG_FS
+       /* Per snic debugfs init */
+       ret = snic_stats_debugfs_init(snic);
+       if (ret) {
+               SNIC_HOST_ERR(snic->shost,
+                             "Failed to initialize debugfs stats\n");
+               snic_stats_debugfs_remove(snic);
+       }
+#endif
+
+       /* Setup PCI Resources */
+       pci_set_drvdata(pdev, snic);
+       snic->pdev = pdev;
+
+       ret = pci_enable_device(pdev);
+       if (ret) {
+               SNIC_HOST_ERR(shost,
+                             "Cannot enable PCI Resources, aborting : %d\n",
+                             ret);
+
+               goto err_free_snic;
+       }
+
+       ret = pci_request_regions(pdev, SNIC_DRV_NAME);
+       if (ret) {
+               SNIC_HOST_ERR(shost,
+                             "Cannot obtain PCI Resources, aborting : %d\n",
+                             ret);
+
+               goto err_pci_disable;
+       }
+
+       pci_set_master(pdev);
+
+       /*
+        * Query PCI Controller on system for DMA addressing
+        * limitation for the device. Try 43-bit first, and
+        * fail to 32-bit.
+        */
+       ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(43));
+       if (ret) {
+               ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
+               if (ret) {
+                       SNIC_HOST_ERR(shost,
+                                     "No Usable DMA Configuration, aborting %d\n",
+                                     ret);
+
+                       goto err_rel_regions;
+               }
+
+               ret = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
+               if (ret) {
+                       SNIC_HOST_ERR(shost,
+                                     "Unable to obtain 32-bit DMA for consistent allocations, aborting: %d\n",
+                                     ret);
+
+                       goto err_rel_regions;
+               }
+       } else {
+               ret = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(43));
+               if (ret) {
+                       SNIC_HOST_ERR(shost,
+                                     "Unable to obtain 43-bit DMA for consistent allocations. aborting: %d\n",
+                                     ret);
+
+                       goto err_rel_regions;
+               }
+       }
+
+
+       /* Map vNIC resources from BAR0 */
+       if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM)) {
+               SNIC_HOST_ERR(shost, "BAR0 not memory mappable aborting.\n");
+
+               ret = -ENODEV;
+               goto err_rel_regions;
+       }
+
+       snic->bar0.vaddr = pci_iomap(pdev, 0, 0);
+       if (!snic->bar0.vaddr) {
+               SNIC_HOST_ERR(shost,
+                             "Cannot memory map BAR0 res hdr aborting.\n");
+
+               ret = -ENODEV;
+               goto err_rel_regions;
+       }
+
+       snic->bar0.bus_addr = pci_resource_start(pdev, 0);
+       snic->bar0.len = pci_resource_len(pdev, 0);
+       SNIC_BUG_ON(snic->bar0.bus_addr == 0);
+
+       /* Devcmd2 Resource Allocation and Initialization */
+       snic->vdev = svnic_dev_alloc_discover(NULL, snic, pdev, &snic->bar0, 1);
+       if (!snic->vdev) {
+               SNIC_HOST_ERR(shost, "vNIC Resource Discovery Failed.\n");
+
+               ret = -ENODEV;
+               goto err_iounmap;
+       }
+
+       ret = svnic_dev_cmd_init(snic->vdev, 0);
+       if (ret) {
+               SNIC_HOST_INFO(shost, "Devcmd2 Init Failed. err = %d\n", ret);
+
+               goto err_vnic_unreg;
+       }
+
+       ret = snic_dev_wait(snic->vdev, svnic_dev_open, snic_vdev_open_done, 0);
+       if (ret) {
+               SNIC_HOST_ERR(shost,
+                             "vNIC dev open failed, aborting. %d\n",
+                             ret);
+
+               goto err_vnic_unreg;
+       }
+
+       ret = svnic_dev_init(snic->vdev, 0);
+       if (ret) {
+               SNIC_HOST_ERR(shost,
+                             "vNIC dev init failed. aborting. %d\n",
+                             ret);
+
+               goto err_dev_close;
+       }
+
+       /* Get vNIC information */
+       ret = snic_get_vnic_config(snic);
+       if (ret) {
+               SNIC_HOST_ERR(shost,
+                             "Get vNIC configuration failed, aborting. %d\n",
+                             ret);
+
+               goto err_dev_close;
+       }
+
+       /* Configure Maximum Outstanding IO reqs */
+       max_ios = snic->config.io_throttle_count;
+       if (max_ios != SNIC_UCSM_DFLT_THROTTLE_CNT_BLD)
+               shost->can_queue = min_t(u32, SNIC_MAX_IO_REQ,
+                                        max_t(u32, SNIC_MIN_IO_REQ, max_ios));
+
+       snic->max_tag_id = shost->can_queue;
+
+       ret = scsi_init_shared_tag_map(shost, snic->max_tag_id);
+       if (ret) {
+               SNIC_HOST_ERR(shost,
+                             "Unable to alloc shared tag map. %d\n",
+                             ret);
+
+               goto err_dev_close;
+       }
+
+       shost->max_lun = snic->config.luns_per_tgt;
+       shost->max_id = SNIC_MAX_TARGET;
+
+       shost->max_cmd_len = MAX_COMMAND_SIZE; /*defined in scsi_cmnd.h*/
+
+       snic_get_res_counts(snic);
+
+       /*
+        * Assumption: Only MSIx is supported
+        */
+       ret = snic_set_intr_mode(snic);
+       if (ret) {
+               SNIC_HOST_ERR(shost,
+                             "Failed to set intr mode aborting. %d\n",
+                             ret);
+
+               goto err_dev_close;
+       }
+
+       ret = snic_alloc_vnic_res(snic);
+       if (ret) {
+               SNIC_HOST_ERR(shost,
+                             "Failed to alloc vNIC resources aborting. %d\n",
+                             ret);
+
+               goto err_clear_intr;
+       }
+
+       /* Initialize specific lists */
+       INIT_LIST_HEAD(&snic->list);
+
+       /*
+        * spl_cmd_list for maintaining snic specific cmds
+        * such as EXCH_VER_REQ, REPORT_TARGETS etc
+        */
+       INIT_LIST_HEAD(&snic->spl_cmd_list);
+       spin_lock_init(&snic->spl_cmd_lock);
+
+       /* initialize all snic locks */
+       spin_lock_init(&snic->snic_lock);
+
+       for (i = 0; i < SNIC_WQ_MAX; i++)
+               spin_lock_init(&snic->wq_lock[i]);
+
+       for (i = 0; i < SNIC_IO_LOCKS; i++)
+               spin_lock_init(&snic->io_req_lock[i]);
+
+       pool = mempool_create_slab_pool(2,
+                               snic_glob->req_cache[SNIC_REQ_CACHE_DFLT_SGL]);
+       if (!pool) {
+               SNIC_HOST_ERR(shost, "dflt sgl pool creation failed\n");
+
+               goto err_free_res;
+       }
+
+       snic->req_pool[SNIC_REQ_CACHE_DFLT_SGL] = pool;
+
+       pool = mempool_create_slab_pool(2,
+                               snic_glob->req_cache[SNIC_REQ_CACHE_MAX_SGL]);
+       if (!pool) {
+               SNIC_HOST_ERR(shost, "max sgl pool creation failed\n");
+
+               goto err_free_dflt_sgl_pool;
+       }
+
+       snic->req_pool[SNIC_REQ_CACHE_MAX_SGL] = pool;
+
+       pool = mempool_create_slab_pool(2,
+                               snic_glob->req_cache[SNIC_REQ_TM_CACHE]);
+       if (!pool) {
+               SNIC_HOST_ERR(shost, "snic tmreq info pool creation failed.\n");
+
+               goto err_free_max_sgl_pool;
+       }
+
+       snic->req_pool[SNIC_REQ_TM_CACHE] = pool;
+
+       /* Initialize snic state */
+       atomic_set(&snic->state, SNIC_INIT);
+
+       atomic_set(&snic->ios_inflight, 0);
+
+       /* Setup notification buffer area */
+       ret = snic_notify_set(snic);
+       if (ret) {
+               SNIC_HOST_ERR(shost,
+                             "Failed to alloc notify buffer aborting. %d\n",
+                             ret);
+
+               goto err_free_tmreq_pool;
+       }
+
+       /*
+        * Initialization done with PCI system, hardware, firmware.
+        * Add shost to SCSI
+        */
+       ret = snic_add_host(shost, pdev);
+       if (ret) {
+               SNIC_HOST_ERR(shost,
+                             "Adding scsi host Failed ... exiting. %d\n",
+                             ret);
+
+               goto err_notify_unset;
+       }
+
+       spin_lock_irqsave(&snic_glob->snic_list_lock, flags);
+       list_add_tail(&snic->list, &snic_glob->snic_list);
+       spin_unlock_irqrestore(&snic_glob->snic_list_lock, flags);
+
+       snic_disc_init(&snic->disc);
+       INIT_WORK(&snic->tgt_work, snic_handle_tgt_disc);
+       INIT_WORK(&snic->disc_work, snic_handle_disc);
+       INIT_WORK(&snic->link_work, snic_handle_link);
+
+       /* Enable all queues */
+       for (i = 0; i < snic->wq_count; i++)
+               svnic_wq_enable(&snic->wq[i]);
+
+       ret = svnic_dev_enable_wait(snic->vdev);
+       if (ret) {
+               SNIC_HOST_ERR(shost,
+                             "vNIC dev enable failed w/ error %d\n",
+                             ret);
+
+               goto err_vdev_enable;
+       }
+
+       ret = snic_request_intr(snic);
+       if (ret) {
+               SNIC_HOST_ERR(shost, "Unable to request irq. %d\n", ret);
+
+               goto err_req_intr;
+       }
+
+       for (i = 0; i < snic->intr_count; i++)
+               svnic_intr_unmask(&snic->intr[i]);
+
+       snic_set_state(snic, SNIC_ONLINE);
+
+       /* Get snic params */
+       ret = snic_get_conf(snic);
+       if (ret) {
+               SNIC_HOST_ERR(shost,
+                             "Failed to get snic io config from FW w err %d\n",
+                             ret);
+
+               goto err_get_conf;
+       }
+
+       ret = snic_disc_start(snic);
+       if (ret) {
+               SNIC_HOST_ERR(shost, "snic_probe:Discovery Failed w err = %d\n",
+                             ret);
+
+               goto err_get_conf;
+       }
+
+       SNIC_HOST_INFO(shost, "SNIC Device Probe Successful.\n");
+
+       return 0;
+
+err_get_conf:
+       snic_free_all_untagged_reqs(snic);
+
+       for (i = 0; i < snic->intr_count; i++)
+               svnic_intr_mask(&snic->intr[i]);
+
+       snic_free_intr(snic);
+
+err_req_intr:
+       svnic_dev_disable(snic->vdev);
+
+err_vdev_enable:
+       for (i = 0; i < snic->wq_count; i++) {
+               int rc = 0;
+
+               rc = svnic_wq_disable(&snic->wq[i]);
+               if (rc) {
+                       SNIC_HOST_ERR(shost,
+                                     "WQ Disable Failed w/ err = %d\n", rc);
+
+                        break;
+               }
+       }
+       snic_del_host(snic->shost);
+
+err_notify_unset:
+       svnic_dev_notify_unset(snic->vdev);
+
+err_free_tmreq_pool:
+       mempool_destroy(snic->req_pool[SNIC_REQ_TM_CACHE]);
+
+err_free_max_sgl_pool:
+       mempool_destroy(snic->req_pool[SNIC_REQ_CACHE_MAX_SGL]);
+
+err_free_dflt_sgl_pool:
+       mempool_destroy(snic->req_pool[SNIC_REQ_CACHE_DFLT_SGL]);
+
+err_free_res:
+       snic_free_vnic_res(snic);
+
+err_clear_intr:
+       snic_clear_intr_mode(snic);
+
+err_dev_close:
+       svnic_dev_close(snic->vdev);
+
+err_vnic_unreg:
+       svnic_dev_unregister(snic->vdev);
+
+err_iounmap:
+       snic_iounmap(snic);
+
+err_rel_regions:
+       pci_release_regions(pdev);
+
+err_pci_disable:
+       pci_disable_device(pdev);
+
+err_free_snic:
+#ifdef CONFIG_SCSI_SNIC_DEBUG_FS
+       snic_stats_debugfs_remove(snic);
+#endif
+       scsi_host_put(shost);
+       pci_set_drvdata(pdev, NULL);
+
+prob_end:
+       SNIC_INFO("sNIC device : bus %d: slot %d: fn %d Registration Failed.\n",
+                 pdev->bus->number, PCI_SLOT(pdev->devfn),
+                 PCI_FUNC(pdev->devfn));
+
+       return ret;
+} /* end of snic_probe */
+
+
+/*
+ * snic_remove : invoked on unbinding the interface to cleanup the
+ * resources allocated in snic_probe on initialization.
+ */
+static void
+snic_remove(struct pci_dev *pdev)
+{
+       struct snic *snic = pci_get_drvdata(pdev);
+       unsigned long flags;
+
+       if (!snic) {
+               SNIC_INFO("sNIC dev: bus %d slot %d fn %d snic inst is null.\n",
+                         pdev->bus->number, PCI_SLOT(pdev->devfn),
+                         PCI_FUNC(pdev->devfn));
+
+               return;
+       }
+
+       /*
+        * Mark state so that the workqueue thread stops forwarding
+        * received frames and link events. ISR and other threads
+        * that can queue work items will also stop creating work
+        * items on the snic workqueue
+        */
+       snic_set_state(snic, SNIC_OFFLINE);
+       spin_lock_irqsave(&snic->snic_lock, flags);
+       snic->stop_link_events = 1;
+       spin_unlock_irqrestore(&snic->snic_lock, flags);
+
+       flush_workqueue(snic_glob->event_q);
+       snic_disc_term(snic);
+
+       spin_lock_irqsave(&snic->snic_lock, flags);
+       snic->in_remove = 1;
+       spin_unlock_irqrestore(&snic->snic_lock, flags);
+
+       /*
+        * This stops the snic device, masks all interrupts, Completed
+        * CQ entries are drained. Posted WQ/RQ/Copy-WQ entries are
+        * cleanup
+        */
+       snic_cleanup(snic);
+
+       spin_lock_irqsave(&snic_glob->snic_list_lock, flags);
+       list_del(&snic->list);
+       spin_unlock_irqrestore(&snic_glob->snic_list_lock, flags);
+
+       snic_tgt_del_all(snic);
+#ifdef CONFIG_SCSI_SNIC_DEBUG_FS
+       snic_stats_debugfs_remove(snic);
+#endif
+       snic_del_host(snic->shost);
+
+       svnic_dev_notify_unset(snic->vdev);
+       snic_free_intr(snic);
+       snic_free_vnic_res(snic);
+       snic_clear_intr_mode(snic);
+       svnic_dev_close(snic->vdev);
+       svnic_dev_unregister(snic->vdev);
+       snic_iounmap(snic);
+       pci_release_regions(pdev);
+       pci_disable_device(pdev);
+       pci_set_drvdata(pdev, NULL);
+
+       /* this frees Scsi_Host and snic memory (continuous chunk) */
+       scsi_host_put(snic->shost);
+} /* end of snic_remove */
+
+
+struct snic_global *snic_glob;
+
+/*
+ * snic_global_data_init: Initialize SNIC Global Data
+ * Notes: All the global lists, variables should be part of global data
+ * this helps in debugging.
+ */
+static int
+snic_global_data_init(void)
+{
+       int ret = 0;
+       struct kmem_cache *cachep;
+       ssize_t len = 0;
+
+       snic_glob = kzalloc(sizeof(*snic_glob), GFP_KERNEL);
+
+       if (!snic_glob) {
+               SNIC_ERR("Failed to allocate Global Context.\n");
+
+               ret = -ENOMEM;
+               goto gdi_end;
+       }
+
+#ifdef CONFIG_SCSI_SNIC_DEBUG_FS
+       /* Debugfs related Initialization */
+       /* Create debugfs entries for snic */
+       ret = snic_debugfs_init();
+       if (ret < 0) {
+               SNIC_ERR("Failed to create sysfs dir for tracing and stats.\n");
+               snic_debugfs_term();
+               /* continue even if it fails */
+       }
+
+       /* Trace related Initialization */
+       /* Allocate memory for trace buffer */
+       ret = snic_trc_init();
+       if (ret < 0) {
+               SNIC_ERR("Trace buffer init failed, SNIC tracing disabled\n");
+               snic_trc_free();
+               /* continue even if it fails */
+       }
+
+#endif
+       INIT_LIST_HEAD(&snic_glob->snic_list);
+       spin_lock_init(&snic_glob->snic_list_lock);
+
+       /* Create a cache for allocation of snic_host_req+default size ESGLs */
+       len = sizeof(struct snic_req_info);
+       len += sizeof(struct snic_host_req) + sizeof(struct snic_dflt_sgl);
+       cachep = kmem_cache_create("snic_req_dfltsgl", len, SNIC_SG_DESC_ALIGN,
+                                  SLAB_HWCACHE_ALIGN, NULL);
+       if (!cachep) {
+               SNIC_ERR("Failed to create snic default sgl slab\n");
+               ret = -ENOMEM;
+
+               goto err_dflt_req_slab;
+       }
+       snic_glob->req_cache[SNIC_REQ_CACHE_DFLT_SGL] = cachep;
+
+       /* Create a cache for allocation of max size Extended SGLs */
+       len = sizeof(struct snic_req_info);
+       len += sizeof(struct snic_host_req) + sizeof(struct snic_max_sgl);
+       cachep = kmem_cache_create("snic_req_maxsgl", len, SNIC_SG_DESC_ALIGN,
+                                  SLAB_HWCACHE_ALIGN, NULL);
+       if (!cachep) {
+               SNIC_ERR("Failed to create snic max sgl slab\n");
+               ret = -ENOMEM;
+
+               goto err_max_req_slab;
+       }
+       snic_glob->req_cache[SNIC_REQ_CACHE_MAX_SGL] = cachep;
+
+       len = sizeof(struct snic_host_req);
+       cachep = kmem_cache_create("snic_req_maxsgl", len, SNIC_SG_DESC_ALIGN,
+                                  SLAB_HWCACHE_ALIGN, NULL);
+       if (!cachep) {
+               SNIC_ERR("Failed to create snic tm req slab\n");
+               ret = -ENOMEM;
+
+               goto err_tmreq_slab;
+       }
+       snic_glob->req_cache[SNIC_REQ_TM_CACHE] = cachep;
+
+       /* snic_event queue */
+       snic_glob->event_q = create_singlethread_workqueue("snic_event_wq");
+       if (!snic_glob->event_q) {
+               SNIC_ERR("snic event queue create failed\n");
+               ret = -ENOMEM;
+
+               goto err_eventq;
+       }
+
+       return ret;
+
+err_eventq:
+       kmem_cache_destroy(snic_glob->req_cache[SNIC_REQ_TM_CACHE]);
+
+err_tmreq_slab:
+       kmem_cache_destroy(snic_glob->req_cache[SNIC_REQ_CACHE_MAX_SGL]);
+
+err_max_req_slab:
+       kmem_cache_destroy(snic_glob->req_cache[SNIC_REQ_CACHE_DFLT_SGL]);
+
+err_dflt_req_slab:
+#ifdef CONFIG_SCSI_SNIC_DEBUG_FS
+       snic_trc_free();
+       snic_debugfs_term();
+#endif
+       kfree(snic_glob);
+       snic_glob = NULL;
+
+gdi_end:
+       return ret;
+} /* end of snic_glob_init */
+
+/*
+ * snic_global_data_cleanup : Frees SNIC Global Data
+ */
+static void
+snic_global_data_cleanup(void)
+{
+       SNIC_BUG_ON(snic_glob == NULL);
+
+       destroy_workqueue(snic_glob->event_q);
+       kmem_cache_destroy(snic_glob->req_cache[SNIC_REQ_TM_CACHE]);
+       kmem_cache_destroy(snic_glob->req_cache[SNIC_REQ_CACHE_MAX_SGL]);
+       kmem_cache_destroy(snic_glob->req_cache[SNIC_REQ_CACHE_DFLT_SGL]);
+
+#ifdef CONFIG_SCSI_SNIC_DEBUG_FS
+       /* Freeing Trace Resources */
+       snic_trc_free();
+
+       /* Freeing Debugfs Resources */
+       snic_debugfs_term();
+#endif
+       kfree(snic_glob);
+       snic_glob = NULL;
+} /* end of snic_glob_cleanup */
+
+static struct pci_driver snic_driver = {
+       .name = SNIC_DRV_NAME,
+       .id_table = snic_id_table,
+       .probe = snic_probe,
+       .remove = snic_remove,
+};
+
+static int __init
+snic_init_module(void)
+{
+       int ret = 0;
+
+#ifndef __x86_64__
+       SNIC_INFO("SNIC Driver is supported only for x86_64 platforms!\n");
+       add_taint(TAINT_CPU_OUT_OF_SPEC, LOCKDEP_STILL_OK);
+#endif
+
+       SNIC_INFO("%s, ver %s\n", SNIC_DRV_DESCRIPTION, SNIC_DRV_VERSION);
+
+       ret = snic_global_data_init();
+       if (ret) {
+               SNIC_ERR("Failed to Initialize Global Data.\n");
+
+               return ret;
+       }
+
+       ret = pci_register_driver(&snic_driver);
+       if (ret < 0) {
+               SNIC_ERR("PCI driver register error\n");
+
+               goto err_pci_reg;
+       }
+
+       return ret;
+
+err_pci_reg:
+       snic_global_data_cleanup();
+
+       return ret;
+}
+
+static void __exit
+snic_cleanup_module(void)
+{
+       pci_unregister_driver(&snic_driver);
+       snic_global_data_cleanup();
+}
+
+module_init(snic_init_module);
+module_exit(snic_cleanup_module);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION(SNIC_DRV_DESCRIPTION);
+MODULE_VERSION(SNIC_DRV_VERSION);
+MODULE_DEVICE_TABLE(pci, snic_id_table);
+MODULE_AUTHOR("Narsimhulu Musini <nmusini@cisco.com>, "
+             "Sesidhar Baddela <sebaddel@cisco.com>");
diff --git a/drivers/scsi/snic/snic_res.c b/drivers/scsi/snic/snic_res.c
new file mode 100644 (file)
index 0000000..b54912c
--- /dev/null
@@ -0,0 +1,295 @@
+/*
+ * Copyright 2014 Cisco Systems, Inc.  All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * 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/errno.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+
+#include "wq_enet_desc.h"
+#include "cq_enet_desc.h"
+#include "vnic_resource.h"
+#include "vnic_dev.h"
+#include "vnic_wq.h"
+#include "vnic_cq.h"
+#include "vnic_intr.h"
+#include "vnic_stats.h"
+#include "snic.h"
+
+int
+snic_get_vnic_config(struct snic *snic)
+{
+       struct vnic_snic_config *c = &snic->config;
+       int ret;
+
+#define GET_CONFIG(m) \
+       do { \
+               ret = svnic_dev_spec(snic->vdev, \
+                                    offsetof(struct vnic_snic_config, m), \
+                                    sizeof(c->m), \
+                                    &c->m); \
+               if (ret) { \
+                       SNIC_HOST_ERR(snic->shost, \
+                                     "Error getting %s, %d\n", #m, ret); \
+                       return ret; \
+               } \
+       } while (0)
+
+       GET_CONFIG(wq_enet_desc_count);
+       GET_CONFIG(maxdatafieldsize);
+       GET_CONFIG(intr_timer);
+       GET_CONFIG(intr_timer_type);
+       GET_CONFIG(flags);
+       GET_CONFIG(io_throttle_count);
+       GET_CONFIG(port_down_timeout);
+       GET_CONFIG(port_down_io_retries);
+       GET_CONFIG(luns_per_tgt);
+       GET_CONFIG(xpt_type);
+       GET_CONFIG(hid);
+
+       c->wq_enet_desc_count = min_t(u32,
+                                     VNIC_SNIC_WQ_DESCS_MAX,
+                                     max_t(u32,
+                                           VNIC_SNIC_WQ_DESCS_MIN,
+                                           c->wq_enet_desc_count));
+
+       c->wq_enet_desc_count = ALIGN(c->wq_enet_desc_count, 16);
+
+       c->maxdatafieldsize = min_t(u32,
+                                   VNIC_SNIC_MAXDATAFIELDSIZE_MAX,
+                                   max_t(u32,
+                                         VNIC_SNIC_MAXDATAFIELDSIZE_MIN,
+                                         c->maxdatafieldsize));
+
+       c->io_throttle_count = min_t(u32,
+                                    VNIC_SNIC_IO_THROTTLE_COUNT_MAX,
+                                    max_t(u32,
+                                          VNIC_SNIC_IO_THROTTLE_COUNT_MIN,
+                                          c->io_throttle_count));
+
+       c->port_down_timeout = min_t(u32,
+                                    VNIC_SNIC_PORT_DOWN_TIMEOUT_MAX,
+                                    c->port_down_timeout);
+
+       c->port_down_io_retries = min_t(u32,
+                                    VNIC_SNIC_PORT_DOWN_IO_RETRIES_MAX,
+                                    c->port_down_io_retries);
+
+       c->luns_per_tgt = min_t(u32,
+                               VNIC_SNIC_LUNS_PER_TARGET_MAX,
+                               max_t(u32,
+                                     VNIC_SNIC_LUNS_PER_TARGET_MIN,
+                                     c->luns_per_tgt));
+
+       c->intr_timer = min_t(u32, VNIC_INTR_TIMER_MAX, c->intr_timer);
+
+       SNIC_INFO("vNIC resources wq %d\n", c->wq_enet_desc_count);
+       SNIC_INFO("vNIC mtu %d intr timer %d\n",
+                 c->maxdatafieldsize,
+                 c->intr_timer);
+
+       SNIC_INFO("vNIC flags 0x%x luns per tgt %d\n",
+                 c->flags,
+                 c->luns_per_tgt);
+
+       SNIC_INFO("vNIC io throttle count %d\n", c->io_throttle_count);
+       SNIC_INFO("vNIC port down timeout %d port down io retries %d\n",
+                 c->port_down_timeout,
+                 c->port_down_io_retries);
+
+       SNIC_INFO("vNIC back end type = %d\n", c->xpt_type);
+       SNIC_INFO("vNIC hid = %d\n", c->hid);
+
+       return 0;
+}
+
+void
+snic_get_res_counts(struct snic *snic)
+{
+       snic->wq_count = svnic_dev_get_res_count(snic->vdev, RES_TYPE_WQ);
+       SNIC_BUG_ON(snic->wq_count == 0);
+       snic->cq_count = svnic_dev_get_res_count(snic->vdev, RES_TYPE_CQ);
+       SNIC_BUG_ON(snic->cq_count == 0);
+       snic->intr_count = svnic_dev_get_res_count(snic->vdev,
+                                                 RES_TYPE_INTR_CTRL);
+       SNIC_BUG_ON(snic->intr_count == 0);
+}
+
+void
+snic_free_vnic_res(struct snic *snic)
+{
+       unsigned int i;
+
+       for (i = 0; i < snic->wq_count; i++)
+               svnic_wq_free(&snic->wq[i]);
+
+       for (i = 0; i < snic->cq_count; i++)
+               svnic_cq_free(&snic->cq[i]);
+
+       for (i = 0; i < snic->intr_count; i++)
+               svnic_intr_free(&snic->intr[i]);
+}
+
+int
+snic_alloc_vnic_res(struct snic *snic)
+{
+       enum vnic_dev_intr_mode intr_mode;
+       unsigned int mask_on_assertion;
+       unsigned int intr_offset;
+       unsigned int err_intr_enable;
+       unsigned int err_intr_offset;
+       unsigned int i;
+       int ret;
+
+       intr_mode = svnic_dev_get_intr_mode(snic->vdev);
+
+       SNIC_INFO("vNIC interrupt mode: %s\n",
+                 ((intr_mode == VNIC_DEV_INTR_MODE_INTX) ?
+                  "Legacy PCI INTx" :
+                  ((intr_mode == VNIC_DEV_INTR_MODE_MSI) ?
+                   "MSI" :
+                   ((intr_mode == VNIC_DEV_INTR_MODE_MSIX) ?
+                    "MSI-X" : "Unknown"))));
+
+       /* only MSI-X is supported */
+       SNIC_BUG_ON(intr_mode != VNIC_DEV_INTR_MODE_MSIX);
+
+       SNIC_INFO("wq %d cq %d intr %d\n", snic->wq_count,
+                 snic->cq_count,
+                 snic->intr_count);
+
+
+       /* Allocate WQs used for SCSI IOs */
+       for (i = 0; i < snic->wq_count; i++) {
+               ret = svnic_wq_alloc(snic->vdev,
+                                    &snic->wq[i],
+                                    i,
+                                    snic->config.wq_enet_desc_count,
+                                    sizeof(struct wq_enet_desc));
+               if (ret)
+                       goto error_cleanup;
+       }
+
+       /* CQ for each WQ */
+       for (i = 0; i < snic->wq_count; i++) {
+               ret = svnic_cq_alloc(snic->vdev,
+                                    &snic->cq[i],
+                                    i,
+                                    snic->config.wq_enet_desc_count,
+                                    sizeof(struct cq_enet_wq_desc));
+               if (ret)
+                       goto error_cleanup;
+       }
+
+       SNIC_BUG_ON(snic->cq_count != 2 * snic->wq_count);
+       /* CQ for FW TO host */
+       for (i = snic->wq_count; i < snic->cq_count; i++) {
+               ret = svnic_cq_alloc(snic->vdev,
+                                    &snic->cq[i],
+                                    i,
+                                    (snic->config.wq_enet_desc_count * 3),
+                                    sizeof(struct snic_fw_req));
+               if (ret)
+                       goto error_cleanup;
+       }
+
+       for (i = 0; i < snic->intr_count; i++) {
+               ret = svnic_intr_alloc(snic->vdev, &snic->intr[i], i);
+               if (ret)
+                       goto error_cleanup;
+       }
+
+       /*
+        * Init WQ Resources.
+        * WQ[0 to n] points to CQ[0 to n-1]
+        * firmware to host comm points to CQ[n to m+1]
+        */
+       err_intr_enable = 1;
+       err_intr_offset = snic->err_intr_offset;
+
+       for (i = 0; i < snic->wq_count; i++) {
+               svnic_wq_init(&snic->wq[i],
+                             i,
+                             err_intr_enable,
+                             err_intr_offset);
+       }
+
+       for (i = 0; i < snic->cq_count; i++) {
+               intr_offset = i;
+
+               svnic_cq_init(&snic->cq[i],
+                             0 /* flow_control_enable */,
+                             1 /* color_enable */,
+                             0 /* cq_head */,
+                             0 /* cq_tail */,
+                             1 /* cq_tail_color */,
+                             1 /* interrupt_enable */,
+                             1 /* cq_entry_enable */,
+                             0 /* cq_message_enable */,
+                             intr_offset,
+                             0 /* cq_message_addr */);
+       }
+
+       /*
+        * Init INTR resources
+        * Assumption : snic is always in MSI-X mode
+        */
+       SNIC_BUG_ON(intr_mode != VNIC_DEV_INTR_MODE_MSIX);
+       mask_on_assertion = 1;
+
+       for (i = 0; i < snic->intr_count; i++) {
+               svnic_intr_init(&snic->intr[i],
+                               snic->config.intr_timer,
+                               snic->config.intr_timer_type,
+                               mask_on_assertion);
+       }
+
+       /* init the stats memory by making the first call here */
+       ret = svnic_dev_stats_dump(snic->vdev, &snic->stats);
+       if (ret) {
+               SNIC_HOST_ERR(snic->shost,
+                             "svnic_dev_stats_dump failed - x%x\n",
+                             ret);
+               goto error_cleanup;
+       }
+
+       /* Clear LIF stats */
+       svnic_dev_stats_clear(snic->vdev);
+       ret = 0;
+
+       return ret;
+
+error_cleanup:
+       snic_free_vnic_res(snic);
+
+       return ret;
+}
+
+void
+snic_log_q_error(struct snic *snic)
+{
+       unsigned int i;
+       u32 err_status;
+
+       for (i = 0; i < snic->wq_count; i++) {
+               err_status = ioread32(&snic->wq[i].ctrl->error_status);
+               if (err_status)
+                       SNIC_HOST_ERR(snic->shost,
+                                     "WQ[%d] error status %d\n",
+                                     i,
+                                     err_status);
+       }
+} /* end of snic_log_q_error */
diff --git a/drivers/scsi/snic/snic_res.h b/drivers/scsi/snic/snic_res.h
new file mode 100644 (file)
index 0000000..273f72f
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2014 Cisco Systems, Inc.  All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * 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 __SNIC_RES_H
+#define __SNIC_RES_H
+
+#include "snic_io.h"
+#include "wq_enet_desc.h"
+#include "vnic_wq.h"
+#include "snic_fwint.h"
+#include "vnic_cq_fw.h"
+
+static inline void
+snic_icmnd_init(struct snic_host_req *req, u32 cmnd_id, u32 host_id, u64 ctx,
+               u16 flags, u64 tgt_id, u8 *lun, u8 *scsi_cdb, u8 cdb_len,
+               u32 data_len, u16 sg_cnt, ulong sgl_addr,
+               dma_addr_t sns_addr_pa, u32 sense_len)
+{
+       snic_io_hdr_enc(&req->hdr, SNIC_REQ_ICMND, 0, cmnd_id, host_id, sg_cnt,
+                       ctx);
+
+       req->u.icmnd.flags = cpu_to_le16(flags);
+       req->u.icmnd.tgt_id = cpu_to_le64(tgt_id);
+       memcpy(&req->u.icmnd.lun_id, lun, LUN_ADDR_LEN);
+       req->u.icmnd.cdb_len = cdb_len;
+       memset(req->u.icmnd.cdb, 0, SNIC_CDB_LEN);
+       memcpy(req->u.icmnd.cdb, scsi_cdb, cdb_len);
+       req->u.icmnd.data_len = cpu_to_le32(data_len);
+       req->u.icmnd.sg_addr = cpu_to_le64(sgl_addr);
+       req->u.icmnd.sense_len = cpu_to_le32(sense_len);
+       req->u.icmnd.sense_addr = cpu_to_le64(sns_addr_pa);
+}
+
+static inline void
+snic_itmf_init(struct snic_host_req *req, u32 cmnd_id, u32 host_id, ulong ctx,
+              u16 flags, u32 req_id, u64 tgt_id, u8 *lun, u8 tm_type)
+{
+       snic_io_hdr_enc(&req->hdr, SNIC_REQ_ITMF, 0, cmnd_id, host_id, 0, ctx);
+
+       req->u.itmf.tm_type = tm_type;
+       req->u.itmf.flags = cpu_to_le16(flags);
+       /* req_id valid only in abort, clear task */
+       req->u.itmf.req_id = cpu_to_le32(req_id);
+       req->u.itmf.tgt_id = cpu_to_le64(tgt_id);
+       memcpy(&req->u.itmf.lun_id, lun, LUN_ADDR_LEN);
+}
+
+static inline void
+snic_queue_wq_eth_desc(struct vnic_wq *wq,
+                      void *os_buf,
+                      dma_addr_t dma_addr,
+                      unsigned int len,
+                      int vlan_tag_insert,
+                      unsigned int vlan_tag,
+                      int cq_entry)
+{
+       struct wq_enet_desc *desc = svnic_wq_next_desc(wq);
+
+       wq_enet_desc_enc(desc,
+                       (u64)dma_addr | VNIC_PADDR_TARGET,
+                       (u16)len,
+                       0, /* mss_or_csum_offset */
+                       0, /* fc_eof */
+                       0, /* offload mode */
+                       1, /* eop */
+                       (u8)cq_entry,
+                       0, /* fcoe_encap */
+                       (u8)vlan_tag_insert,
+                       (u16)vlan_tag,
+                       0 /* loopback */);
+
+       svnic_wq_post(wq, os_buf, dma_addr, len, 1, 1);
+}
+
+struct snic;
+
+int snic_get_vnic_config(struct snic *);
+int snic_alloc_vnic_res(struct snic *);
+void snic_free_vnic_res(struct snic *);
+void snic_get_res_counts(struct snic *);
+void snic_log_q_error(struct snic *);
+int snic_get_vnic_resources_size(struct snic *);
+#endif /* __SNIC_RES_H */
diff --git a/drivers/scsi/snic/snic_scsi.c b/drivers/scsi/snic/snic_scsi.c
new file mode 100644 (file)
index 0000000..2c7b4c3
--- /dev/null
@@ -0,0 +1,2632 @@
+/*
+ * Copyright 2014 Cisco Systems, Inc.  All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * 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/mempool.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/workqueue.h>
+#include <linux/pci.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <linux/gfp.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_tcq.h>
+#include <scsi/scsi_dbg.h>
+
+#include "snic_io.h"
+#include "snic.h"
+
+#define snic_cmd_tag(sc)       (((struct scsi_cmnd *) sc)->request->tag)
+
+const char *snic_state_str[] = {
+       [SNIC_INIT]     = "SNIC_INIT",
+       [SNIC_ERROR]    = "SNIC_ERROR",
+       [SNIC_ONLINE]   = "SNIC_ONLINE",
+       [SNIC_OFFLINE]  = "SNIC_OFFLINE",
+       [SNIC_FWRESET]  = "SNIC_FWRESET",
+};
+
+static const char * const snic_req_state_str[] = {
+       [SNIC_IOREQ_NOT_INITED] = "SNIC_IOREQ_NOT_INITED",
+       [SNIC_IOREQ_PENDING]    = "SNIC_IOREQ_PENDING",
+       [SNIC_IOREQ_ABTS_PENDING] = "SNIC_IOREQ_ABTS_PENDING",
+       [SNIC_IOREQ_ABTS_COMPLETE] = "SNIC_IOREQ_ABTS_COMPELTE",
+       [SNIC_IOREQ_LR_PENDING] = "SNIC_IOREQ_LR_PENDING",
+       [SNIC_IOREQ_LR_COMPLETE] = "SNIC_IOREQ_LR_COMPELTE",
+       [SNIC_IOREQ_COMPLETE]   = "SNIC_IOREQ_CMD_COMPELTE",
+};
+
+/* snic cmd status strings */
+static const char * const snic_io_status_str[] = {
+       [SNIC_STAT_IO_SUCCESS]  = "SNIC_STAT_IO_SUCCESS", /* 0x0 */
+       [SNIC_STAT_INVALID_HDR] = "SNIC_STAT_INVALID_HDR",
+       [SNIC_STAT_OUT_OF_RES]  = "SNIC_STAT_OUT_OF_RES",
+       [SNIC_STAT_INVALID_PARM] = "SNIC_STAT_INVALID_PARM",
+       [SNIC_STAT_REQ_NOT_SUP] = "SNIC_STAT_REQ_NOT_SUP",
+       [SNIC_STAT_IO_NOT_FOUND] = "SNIC_STAT_IO_NOT_FOUND",
+       [SNIC_STAT_ABORTED]     = "SNIC_STAT_ABORTED",
+       [SNIC_STAT_TIMEOUT]     = "SNIC_STAT_TIMEOUT",
+       [SNIC_STAT_SGL_INVALID] = "SNIC_STAT_SGL_INVALID",
+       [SNIC_STAT_DATA_CNT_MISMATCH] = "SNIC_STAT_DATA_CNT_MISMATCH",
+       [SNIC_STAT_FW_ERR]      = "SNIC_STAT_FW_ERR",
+       [SNIC_STAT_ITMF_REJECT] = "SNIC_STAT_ITMF_REJECT",
+       [SNIC_STAT_ITMF_FAIL]   = "SNIC_STAT_ITMF_FAIL",
+       [SNIC_STAT_ITMF_INCORRECT_LUN] = "SNIC_STAT_ITMF_INCORRECT_LUN",
+       [SNIC_STAT_CMND_REJECT] = "SNIC_STAT_CMND_REJECT",
+       [SNIC_STAT_DEV_OFFLINE] = "SNIC_STAT_DEV_OFFLINE",
+       [SNIC_STAT_NO_BOOTLUN]  = "SNIC_STAT_NO_BOOTLUN",
+       [SNIC_STAT_SCSI_ERR]    = "SNIC_STAT_SCSI_ERR",
+       [SNIC_STAT_NOT_READY]   = "SNIC_STAT_NOT_READY",
+       [SNIC_STAT_FATAL_ERROR] = "SNIC_STAT_FATAL_ERROR",
+};
+
+static void snic_scsi_cleanup(struct snic *, int);
+
+const char *
+snic_state_to_str(unsigned int state)
+{
+       if (state >= ARRAY_SIZE(snic_state_str) || !snic_state_str[state])
+               return "Unknown";
+
+       return snic_state_str[state];
+}
+
+static const char *
+snic_io_status_to_str(unsigned int state)
+{
+       if ((state >= ARRAY_SIZE(snic_io_status_str)) ||
+            (!snic_io_status_str[state]))
+               return "Unknown";
+
+       return snic_io_status_str[state];
+}
+
+static const char *
+snic_ioreq_state_to_str(unsigned int state)
+{
+       if (state >= ARRAY_SIZE(snic_req_state_str) ||
+                       !snic_req_state_str[state])
+               return "Unknown";
+
+       return snic_req_state_str[state];
+}
+
+static inline spinlock_t *
+snic_io_lock_hash(struct snic *snic, struct scsi_cmnd *sc)
+{
+       u32 hash = snic_cmd_tag(sc) & (SNIC_IO_LOCKS - 1);
+
+       return &snic->io_req_lock[hash];
+}
+
+static inline spinlock_t *
+snic_io_lock_tag(struct snic *snic, int tag)
+{
+       return &snic->io_req_lock[tag & (SNIC_IO_LOCKS - 1)];
+}
+
+/* snic_release_req_buf : Releases snic_req_info */
+static void
+snic_release_req_buf(struct snic *snic,
+                  struct snic_req_info *rqi,
+                  struct scsi_cmnd *sc)
+{
+       struct snic_host_req *req = rqi_to_req(rqi);
+
+       /* Freeing cmd without marking completion, not okay */
+       SNIC_BUG_ON(!((CMD_STATE(sc) == SNIC_IOREQ_COMPLETE) ||
+                     (CMD_STATE(sc) == SNIC_IOREQ_ABTS_COMPLETE) ||
+                     (CMD_FLAGS(sc) & SNIC_DEV_RST_NOTSUP) ||
+                     (CMD_FLAGS(sc) & SNIC_IO_INTERNAL_TERM_ISSUED) ||
+                     (CMD_FLAGS(sc) & SNIC_DEV_RST_TERM_ISSUED) ||
+                     (CMD_FLAGS(sc) & SNIC_SCSI_CLEANUP) ||
+                     (CMD_STATE(sc) == SNIC_IOREQ_LR_COMPLETE)));
+
+       SNIC_SCSI_DBG(snic->shost,
+                     "Rel_req:sc %p:tag %x:rqi %p:ioreq %p:abt %p:dr %p: state %s:flags 0x%llx\n",
+                     sc, snic_cmd_tag(sc), rqi, rqi->req, rqi->abort_req,
+                     rqi->dr_req, snic_ioreq_state_to_str(CMD_STATE(sc)),
+                     CMD_FLAGS(sc));
+
+       if (req->u.icmnd.sense_addr)
+               pci_unmap_single(snic->pdev,
+                                le64_to_cpu(req->u.icmnd.sense_addr),
+                                SCSI_SENSE_BUFFERSIZE,
+                                PCI_DMA_FROMDEVICE);
+
+       scsi_dma_unmap(sc);
+
+       snic_req_free(snic, rqi);
+} /* end of snic_release_req_buf */
+
+/*
+ * snic_queue_icmnd_req : Queues snic_icmnd request
+ */
+static int
+snic_queue_icmnd_req(struct snic *snic,
+                    struct snic_req_info *rqi,
+                    struct scsi_cmnd *sc,
+                    int sg_cnt)
+{
+       struct scatterlist *sg;
+       struct snic_sg_desc *sgd;
+       dma_addr_t pa = 0;
+       struct scsi_lun lun;
+       u16 flags = 0;
+       int ret = 0;
+       unsigned int i;
+
+       if (sg_cnt) {
+               flags = SNIC_ICMND_ESGL;
+               sgd = (struct snic_sg_desc *) req_to_sgl(rqi->req);
+
+               for_each_sg(scsi_sglist(sc), sg, sg_cnt, i) {
+                       sgd->addr = cpu_to_le64(sg_dma_address(sg));
+                       sgd->len = cpu_to_le32(sg_dma_len(sg));
+                       sgd->_resvd = 0;
+                       sgd++;
+               }
+       }
+
+       pa = pci_map_single(snic->pdev,
+                           sc->sense_buffer,
+                           SCSI_SENSE_BUFFERSIZE,
+                           PCI_DMA_FROMDEVICE);
+
+       if (pci_dma_mapping_error(snic->pdev, pa)) {
+               SNIC_HOST_ERR(snic->shost,
+                             "QIcmnd:PCI Map Failed for sns buf %p tag %x\n",
+                             sc->sense_buffer, snic_cmd_tag(sc));
+               ret = -ENOMEM;
+
+               return ret;
+       }
+
+       int_to_scsilun(sc->device->lun, &lun);
+       if (sc->sc_data_direction == DMA_FROM_DEVICE)
+               flags |= SNIC_ICMND_RD;
+       if (sc->sc_data_direction == DMA_TO_DEVICE)
+               flags |= SNIC_ICMND_WR;
+
+       /* Initialize icmnd */
+       snic_icmnd_init(rqi->req,
+                       snic_cmd_tag(sc),
+                       snic->config.hid, /* hid */
+                       (ulong) rqi,
+                       flags, /* command flags */
+                       rqi->tgt_id,
+                       lun.scsi_lun,
+                       sc->cmnd,
+                       sc->cmd_len,
+                       scsi_bufflen(sc),
+                       sg_cnt,
+                       (ulong) req_to_sgl(rqi->req),
+                       pa, /* sense buffer pa */
+                       SCSI_SENSE_BUFFERSIZE);
+
+       ret = snic_queue_wq_desc(snic, rqi->req, rqi->req_len);
+       if (ret)
+               SNIC_HOST_ERR(snic->shost,
+                             "QIcmnd: Queuing Icmnd Failed. ret = %d\n",
+                             ret);
+
+       return ret;
+} /* end of snic_queue_icmnd_req */
+
+/*
+ * snic_issue_scsi_req : Prepares IO request and Issues to FW.
+ */
+static int
+snic_issue_scsi_req(struct snic *snic,
+                     struct snic_tgt *tgt,
+                     struct scsi_cmnd *sc)
+{
+       struct snic_req_info *rqi = NULL;
+       int sg_cnt = 0;
+       int ret = 0;
+       u32 tag = snic_cmd_tag(sc);
+       u64 cmd_trc = 0, cmd_st_flags = 0;
+       spinlock_t *io_lock = NULL;
+       unsigned long flags;
+
+       CMD_STATE(sc) = SNIC_IOREQ_NOT_INITED;
+       CMD_FLAGS(sc) = SNIC_NO_FLAGS;
+       sg_cnt = scsi_dma_map(sc);
+       if (sg_cnt < 0) {
+               SNIC_TRC((u16)snic->shost->host_no, tag, (ulong) sc, 0,
+                        sc->cmnd[0], sg_cnt, CMD_STATE(sc));
+
+               SNIC_HOST_ERR(snic->shost, "issue_sc:Failed to map SG List.\n");
+               ret = -ENOMEM;
+
+               goto issue_sc_end;
+       }
+
+       rqi = snic_req_init(snic, sg_cnt);
+       if (!rqi) {
+               scsi_dma_unmap(sc);
+               ret = -ENOMEM;
+
+               goto issue_sc_end;
+       }
+
+       rqi->tgt_id = tgt->id;
+       rqi->sc = sc;
+
+       CMD_STATE(sc) = SNIC_IOREQ_PENDING;
+       CMD_SP(sc) = (char *) rqi;
+       cmd_trc = SNIC_TRC_CMD(sc);
+       CMD_FLAGS(sc) |= (SNIC_IO_INITIALIZED | SNIC_IO_ISSUED);
+       cmd_st_flags = SNIC_TRC_CMD_STATE_FLAGS(sc);
+       io_lock = snic_io_lock_hash(snic, sc);
+
+       /* create wq desc and enqueue it */
+       ret = snic_queue_icmnd_req(snic, rqi, sc, sg_cnt);
+       if (ret) {
+               SNIC_HOST_ERR(snic->shost,
+                             "issue_sc: icmnd qing Failed for sc %p, err %d\n",
+                             sc, ret);
+
+               spin_lock_irqsave(io_lock, flags);
+               rqi = (struct snic_req_info *) CMD_SP(sc);
+               CMD_SP(sc) = NULL;
+               CMD_STATE(sc) = SNIC_IOREQ_COMPLETE;
+               CMD_FLAGS(sc) &= ~SNIC_IO_ISSUED; /* turn off the flag */
+               spin_unlock_irqrestore(io_lock, flags);
+
+               if (rqi)
+                       snic_release_req_buf(snic, rqi, sc);
+
+               SNIC_TRC(snic->shost->host_no, tag, (ulong) sc, 0, 0, 0,
+                        SNIC_TRC_CMD_STATE_FLAGS(sc));
+       } else {
+               u32 io_sz = scsi_bufflen(sc) >> 9;
+               u32 qtime = jiffies - rqi->start_time;
+               struct snic_io_stats *iostats = &snic->s_stats.io;
+
+               if (io_sz > atomic64_read(&iostats->max_io_sz))
+                       atomic64_set(&iostats->max_io_sz, io_sz);
+
+               if (qtime > atomic64_read(&iostats->max_qtime))
+                       atomic64_set(&iostats->max_qtime, qtime);
+
+               SNIC_SCSI_DBG(snic->shost,
+                             "issue_sc:sc %p, tag %d queued to WQ.\n",
+                             sc, tag);
+
+               SNIC_TRC(snic->shost->host_no, tag, (ulong) sc, (ulong) rqi,
+                        sg_cnt, cmd_trc, cmd_st_flags);
+       }
+
+issue_sc_end:
+
+       return ret;
+} /* end of snic_issue_scsi_req */
+
+
+/*
+ * snic_queuecommand
+ * Routine to send a scsi cdb to LLD
+ * Called with host_lock held and interrupts disabled
+ */
+int
+snic_queuecommand(struct Scsi_Host *shost, struct scsi_cmnd *sc)
+{
+       struct snic_tgt *tgt = NULL;
+       struct snic *snic = shost_priv(shost);
+       int ret;
+
+       tgt = starget_to_tgt(scsi_target(sc->device));
+       ret = snic_tgt_chkready(tgt);
+       if (ret) {
+               SNIC_HOST_ERR(shost, "Tgt %p id %d Not Ready.\n", tgt, tgt->id);
+               atomic64_inc(&snic->s_stats.misc.tgt_not_rdy);
+               sc->result = ret;
+               sc->scsi_done(sc);
+
+               return 0;
+       }
+
+       if (snic_get_state(snic) != SNIC_ONLINE) {
+               SNIC_HOST_ERR(shost, "snic state is %s\n",
+                             snic_state_str[snic_get_state(snic)]);
+
+               return SCSI_MLQUEUE_HOST_BUSY;
+       }
+       atomic_inc(&snic->ios_inflight);
+
+       SNIC_SCSI_DBG(shost, "sc %p Tag %d (sc %0x) lun %lld in snic_qcmd\n",
+                     sc, snic_cmd_tag(sc), sc->cmnd[0], sc->device->lun);
+
+       memset(scsi_cmd_priv(sc), 0, sizeof(struct snic_internal_io_state));
+
+       ret = snic_issue_scsi_req(snic, tgt, sc);
+       if (ret) {
+               SNIC_HOST_ERR(shost, "Failed to Q, Scsi Req w/ err %d.\n", ret);
+               ret = SCSI_MLQUEUE_HOST_BUSY;
+       } else
+               snic_stats_update_active_ios(&snic->s_stats);
+
+       atomic_dec(&snic->ios_inflight);
+
+       return ret;
+} /* end of snic_queuecommand */
+
+/*
+ * snic_process_abts_pending_state:
+ * caller should hold IO lock
+ */
+static void
+snic_proc_tmreq_pending_state(struct snic *snic,
+                             struct scsi_cmnd *sc,
+                             u8 cmpl_status)
+{
+       int state = CMD_STATE(sc);
+
+       if (state == SNIC_IOREQ_ABTS_PENDING)
+               CMD_FLAGS(sc) |= SNIC_IO_ABTS_PENDING;
+       else if (state == SNIC_IOREQ_LR_PENDING)
+               CMD_FLAGS(sc) |= SNIC_DEV_RST_PENDING;
+       else
+               SNIC_BUG_ON(1);
+
+       switch (cmpl_status) {
+       case SNIC_STAT_IO_SUCCESS:
+               CMD_FLAGS(sc) |= SNIC_IO_DONE;
+               break;
+
+       case SNIC_STAT_ABORTED:
+               CMD_FLAGS(sc) |= SNIC_IO_ABORTED;
+               break;
+
+       default:
+               SNIC_BUG_ON(1);
+       }
+}
+
+/*
+ * snic_process_io_failed_state:
+ * Processes IO's error states
+ */
+static void
+snic_process_io_failed_state(struct snic *snic,
+                            struct snic_icmnd_cmpl *icmnd_cmpl,
+                            struct scsi_cmnd *sc,
+                            u8 cmpl_stat)
+{
+       int res = 0;
+
+       switch (cmpl_stat) {
+       case SNIC_STAT_TIMEOUT:         /* Req was timedout */
+               atomic64_inc(&snic->s_stats.misc.io_tmo);
+               res = DID_TIME_OUT;
+               break;
+
+       case SNIC_STAT_ABORTED:         /* Req was aborted */
+               atomic64_inc(&snic->s_stats.misc.io_aborted);
+               res = DID_ABORT;
+               break;
+
+       case SNIC_STAT_DATA_CNT_MISMATCH:/* Recv/Sent more/less data than exp */
+               atomic64_inc(&snic->s_stats.misc.data_cnt_mismat);
+               scsi_set_resid(sc, le32_to_cpu(icmnd_cmpl->resid));
+               res = DID_ERROR;
+               break;
+
+       case SNIC_STAT_OUT_OF_RES: /* Out of resources to complete request */
+               atomic64_inc(&snic->s_stats.fw.out_of_res);
+               res = DID_REQUEUE;
+               break;
+
+       case SNIC_STAT_IO_NOT_FOUND:    /* Requested I/O was not found */
+               atomic64_inc(&snic->s_stats.io.io_not_found);
+               res = DID_ERROR;
+               break;
+
+       case SNIC_STAT_SGL_INVALID:     /* Req was aborted to due to sgl error*/
+               atomic64_inc(&snic->s_stats.misc.sgl_inval);
+               res = DID_ERROR;
+               break;
+
+       case SNIC_STAT_FW_ERR:          /* Req terminated due to FW Error */
+               atomic64_inc(&snic->s_stats.fw.io_errs);
+               res = DID_ERROR;
+               break;
+
+       case SNIC_STAT_SCSI_ERR:        /* FW hits SCSI Error */
+               atomic64_inc(&snic->s_stats.fw.scsi_errs);
+               break;
+
+       case SNIC_STAT_NOT_READY:       /* XPT yet to initialize */
+       case SNIC_STAT_DEV_OFFLINE:     /* Device offline */
+               res = DID_NO_CONNECT;
+               break;
+
+       case SNIC_STAT_INVALID_HDR:     /* Hdr contains invalid data */
+       case SNIC_STAT_INVALID_PARM:    /* Some param in req is invalid */
+       case SNIC_STAT_REQ_NOT_SUP:     /* Req type is not supported */
+       case SNIC_STAT_CMND_REJECT:     /* Req rejected */
+       case SNIC_STAT_FATAL_ERROR:     /* XPT Error */
+       default:
+               SNIC_SCSI_DBG(snic->shost,
+                             "Invalid Hdr/Param or Req Not Supported or Cmnd Rejected or Device Offline. or Unknown\n");
+               res = DID_ERROR;
+               break;
+       }
+
+       SNIC_HOST_ERR(snic->shost, "fw returns failed status %s flags 0x%llx\n",
+                     snic_io_status_to_str(cmpl_stat), CMD_FLAGS(sc));
+
+       /* Set sc->result */
+       sc->result = (res << 16) | icmnd_cmpl->scsi_status;
+} /* end of snic_process_io_failed_state */
+
+/*
+ * snic_tmreq_pending : is task management in progress.
+ */
+static int
+snic_tmreq_pending(struct scsi_cmnd *sc)
+{
+       int state = CMD_STATE(sc);
+
+       return ((state == SNIC_IOREQ_ABTS_PENDING) ||
+                       (state == SNIC_IOREQ_LR_PENDING));
+}
+
+/*
+ * snic_process_icmnd_cmpl_status:
+ * Caller should hold io_lock
+ */
+static int
+snic_process_icmnd_cmpl_status(struct snic *snic,
+                              struct snic_icmnd_cmpl *icmnd_cmpl,
+                              u8 cmpl_stat,
+                              struct scsi_cmnd *sc)
+{
+       u8 scsi_stat = icmnd_cmpl->scsi_status;
+       u64 xfer_len = 0;
+       int ret = 0;
+
+       /* Mark the IO as complete */
+       CMD_STATE(sc) = SNIC_IOREQ_COMPLETE;
+
+       if (likely(cmpl_stat == SNIC_STAT_IO_SUCCESS)) {
+               sc->result = (DID_OK << 16) | scsi_stat;
+
+               xfer_len = scsi_bufflen(sc);
+
+               /* Update SCSI Cmd with resid value */
+               scsi_set_resid(sc, le32_to_cpu(icmnd_cmpl->resid));
+
+               if (icmnd_cmpl->flags & SNIC_ICMND_CMPL_UNDR_RUN) {
+                       xfer_len -= le32_to_cpu(icmnd_cmpl->resid);
+                       atomic64_inc(&snic->s_stats.misc.io_under_run);
+               }
+
+               if (icmnd_cmpl->scsi_status == SAM_STAT_TASK_SET_FULL)
+                       atomic64_inc(&snic->s_stats.misc.qfull);
+
+               ret = 0;
+       } else {
+               snic_process_io_failed_state(snic, icmnd_cmpl, sc, cmpl_stat);
+               atomic64_inc(&snic->s_stats.io.fail);
+               SNIC_HOST_ERR(snic->shost,
+                             "icmnd_cmpl: IO Failed : Hdr Status %s flags 0x%llx\n",
+                             snic_io_status_to_str(cmpl_stat), CMD_FLAGS(sc));
+               ret = 1;
+       }
+
+       return ret;
+} /* end of snic_process_icmnd_cmpl_status */
+
+
+/*
+ * snic_icmnd_cmpl_handler
+ * Routine to handle icmnd completions
+ */
+static void
+snic_icmnd_cmpl_handler(struct snic *snic, struct snic_fw_req *fwreq)
+{
+       u8 typ, hdr_stat;
+       u32 cmnd_id, hid;
+       ulong ctx;
+       struct scsi_cmnd *sc = NULL;
+       struct snic_icmnd_cmpl *icmnd_cmpl = NULL;
+       struct snic_host_req *req = NULL;
+       struct snic_req_info *rqi = NULL;
+       unsigned long flags, start_time;
+       spinlock_t *io_lock;
+       u8 sc_stat = 0;
+
+       snic_io_hdr_dec(&fwreq->hdr, &typ, &hdr_stat, &cmnd_id, &hid, &ctx);
+       icmnd_cmpl = &fwreq->u.icmnd_cmpl;
+       sc_stat = icmnd_cmpl->scsi_status;
+
+       SNIC_SCSI_DBG(snic->shost,
+                     "Icmnd_cmpl: type = %x, hdr_stat = %x, cmnd_id = %x, hid = %x,i ctx = %lx\n",
+                     typ, hdr_stat, cmnd_id, hid, ctx);
+
+       if (cmnd_id >= snic->max_tag_id) {
+               SNIC_HOST_ERR(snic->shost,
+                             "Icmnd_cmpl:Tag Error:Out of Range Tag %d, hdr status = %s\n",
+                             cmnd_id, snic_io_status_to_str(hdr_stat));
+               return;
+       }
+
+       sc = scsi_host_find_tag(snic->shost, cmnd_id);
+       WARN_ON_ONCE(!sc);
+
+       if (!sc) {
+               atomic64_inc(&snic->s_stats.io.sc_null);
+               SNIC_HOST_ERR(snic->shost,
+                             "Icmnd_cmpl: Scsi Cmnd Not found, sc = NULL Hdr Status = %s tag = 0x%x fwreq = 0x%p\n",
+                             snic_io_status_to_str(hdr_stat),
+                             cmnd_id,
+                             fwreq);
+
+               SNIC_TRC(snic->shost->host_no, cmnd_id, 0,
+                        ((u64)hdr_stat << 16 |
+                         (u64)sc_stat << 8 | (u64)icmnd_cmpl->flags),
+                        (ulong) fwreq, le32_to_cpu(icmnd_cmpl->resid), ctx);
+
+               return;
+       }
+
+       io_lock = snic_io_lock_hash(snic, sc);
+
+       spin_lock_irqsave(io_lock, flags);
+       rqi = (struct snic_req_info *) CMD_SP(sc);
+       SNIC_SCSI_DBG(snic->shost,
+                     "Icmnd_cmpl:lun %lld sc %p cmd %xtag %d flags 0x%llx rqi %p\n",
+                     sc->device->lun, sc, sc->cmnd[0], snic_cmd_tag(sc),
+                     CMD_FLAGS(sc), rqi);
+
+       SNIC_BUG_ON(rqi != (struct snic_req_info *)ctx);
+       WARN_ON_ONCE(req);
+       if (!rqi) {
+               atomic64_inc(&snic->s_stats.io.req_null);
+               CMD_FLAGS(sc) |= SNIC_IO_REQ_NULL;
+               spin_unlock_irqrestore(io_lock, flags);
+
+               SNIC_HOST_ERR(snic->shost,
+                             "Icmnd_cmpl:Host Req Not Found(null), Hdr Status %s, Tag 0x%x, sc 0x%p flags 0x%llx\n",
+                             snic_io_status_to_str(hdr_stat),
+                             cmnd_id, sc, CMD_FLAGS(sc));
+               return;
+       }
+
+       rqi = (struct snic_req_info *) ctx;
+       start_time = rqi->start_time;
+
+       /* firmware completed the io */
+       rqi->io_cmpl = 1;
+
+       /*
+        * if SCSI-ML has already issued abort on this command,
+        * ignore completion of the IO. The abts path will clean it up
+        */
+       if (unlikely(snic_tmreq_pending(sc))) {
+               snic_proc_tmreq_pending_state(snic, sc, hdr_stat);
+               spin_unlock_irqrestore(io_lock, flags);
+
+               snic_stats_update_io_cmpl(&snic->s_stats);
+
+               /* Expected value is SNIC_STAT_ABORTED */
+               if (likely(hdr_stat == SNIC_STAT_ABORTED))
+                       return;
+
+               SNIC_SCSI_DBG(snic->shost,
+                             "icmnd_cmpl:TM Req Pending(%s), Hdr Status %s sc 0x%p scsi status %x resid %d flags 0x%llx\n",
+                             snic_ioreq_state_to_str(CMD_STATE(sc)),
+                             snic_io_status_to_str(hdr_stat),
+                             sc, sc_stat, le32_to_cpu(icmnd_cmpl->resid),
+                             CMD_FLAGS(sc));
+
+               SNIC_TRC(snic->shost->host_no, cmnd_id, (ulong) sc,
+                        jiffies_to_msecs(jiffies - start_time), (ulong) fwreq,
+                        SNIC_TRC_CMD(sc), SNIC_TRC_CMD_STATE_FLAGS(sc));
+
+               return;
+       }
+
+       if (snic_process_icmnd_cmpl_status(snic, icmnd_cmpl, hdr_stat, sc)) {
+               scsi_print_command(sc);
+               SNIC_HOST_ERR(snic->shost,
+                             "icmnd_cmpl:IO Failed, sc 0x%p Tag %d Cmd %x Hdr Status %s flags 0x%llx\n",
+                             sc, sc->cmnd[0], cmnd_id,
+                             snic_io_status_to_str(hdr_stat), CMD_FLAGS(sc));
+       }
+
+       /* Break link with the SCSI Command */
+       CMD_SP(sc) = NULL;
+       CMD_FLAGS(sc) |= SNIC_IO_DONE;
+
+       spin_unlock_irqrestore(io_lock, flags);
+
+       /* For now, consider only successful IO. */
+       snic_calc_io_process_time(snic, rqi);
+
+       snic_release_req_buf(snic, rqi, sc);
+
+       SNIC_TRC(snic->shost->host_no, cmnd_id, (ulong) sc,
+                jiffies_to_msecs(jiffies - start_time), (ulong) fwreq,
+                SNIC_TRC_CMD(sc), SNIC_TRC_CMD_STATE_FLAGS(sc));
+
+
+       if (sc->scsi_done)
+               sc->scsi_done(sc);
+
+       snic_stats_update_io_cmpl(&snic->s_stats);
+} /* end of snic_icmnd_cmpl_handler */
+
+static void
+snic_proc_dr_cmpl_locked(struct snic *snic,
+                        struct snic_fw_req *fwreq,
+                        u8 cmpl_stat,
+                        u32 cmnd_id,
+                        struct scsi_cmnd *sc)
+{
+       struct snic_req_info *rqi = (struct snic_req_info *) CMD_SP(sc);
+       u32 start_time = rqi->start_time;
+
+       CMD_LR_STATUS(sc) = cmpl_stat;
+
+       SNIC_SCSI_DBG(snic->shost, "itmf_cmpl: Cmd State = %s\n",
+                     snic_ioreq_state_to_str(CMD_STATE(sc)));
+
+       if (CMD_STATE(sc) == SNIC_IOREQ_ABTS_PENDING) {
+               CMD_FLAGS(sc) |= SNIC_DEV_RST_ABTS_PENDING;
+
+               SNIC_TRC(snic->shost->host_no, cmnd_id, (ulong) sc,
+                        jiffies_to_msecs(jiffies - start_time),
+                        (ulong) fwreq, 0, SNIC_TRC_CMD_STATE_FLAGS(sc));
+
+               SNIC_SCSI_DBG(snic->shost,
+                             "itmf_cmpl: Terminate Pending Dev Reset Cmpl Recvd.id %x, status %s flags 0x%llx\n",
+                             (int)(cmnd_id & SNIC_TAG_MASK),
+                             snic_io_status_to_str(cmpl_stat),
+                             CMD_FLAGS(sc));
+
+               return;
+       }
+
+
+       if (CMD_FLAGS(sc) & SNIC_DEV_RST_TIMEDOUT) {
+               SNIC_TRC(snic->shost->host_no, cmnd_id, (ulong) sc,
+                        jiffies_to_msecs(jiffies - start_time),
+                        (ulong) fwreq, 0, SNIC_TRC_CMD_STATE_FLAGS(sc));
+
+               SNIC_SCSI_DBG(snic->shost,
+                             "itmf_cmpl:Dev Reset Completion Received after timeout. id %d cmpl status %s flags 0x%llx\n",
+                             (int)(cmnd_id & SNIC_TAG_MASK),
+                             snic_io_status_to_str(cmpl_stat),
+                             CMD_FLAGS(sc));
+
+               return;
+       }
+
+       CMD_STATE(sc) = SNIC_IOREQ_LR_COMPLETE;
+       CMD_FLAGS(sc) |= SNIC_DEV_RST_DONE;
+
+       SNIC_SCSI_DBG(snic->shost,
+                     "itmf_cmpl:Dev Reset Cmpl Recvd id %d cmpl status %s flags 0x%llx\n",
+                     (int)(cmnd_id & SNIC_TAG_MASK),
+                     snic_io_status_to_str(cmpl_stat),
+                     CMD_FLAGS(sc));
+
+       if (rqi->dr_done)
+               complete(rqi->dr_done);
+} /* end of snic_proc_dr_cmpl_locked */
+
+/*
+ * snic_update_abort_stats : Updates abort stats based on completion status.
+ */
+static void
+snic_update_abort_stats(struct snic *snic, u8 cmpl_stat)
+{
+       struct snic_abort_stats *abt_stats = &snic->s_stats.abts;
+
+       SNIC_SCSI_DBG(snic->shost, "Updating Abort stats.\n");
+
+       switch (cmpl_stat) {
+       case  SNIC_STAT_IO_SUCCESS:
+               break;
+
+       case SNIC_STAT_TIMEOUT:
+               atomic64_inc(&abt_stats->fw_tmo);
+               break;
+
+       case SNIC_STAT_IO_NOT_FOUND:
+               atomic64_inc(&abt_stats->io_not_found);
+               break;
+
+       default:
+               atomic64_inc(&abt_stats->fail);
+               break;
+       }
+}
+
+static int
+snic_process_itmf_cmpl(struct snic *snic,
+                      struct snic_fw_req *fwreq,
+                      u32 cmnd_id,
+                      u8 cmpl_stat,
+                      struct scsi_cmnd *sc)
+{
+       struct snic_req_info *rqi = NULL;
+       u32 tm_tags = 0;
+       spinlock_t *io_lock = NULL;
+       unsigned long flags;
+       u32 start_time = 0;
+       int ret = 0;
+
+       io_lock = snic_io_lock_hash(snic, sc);
+       spin_lock_irqsave(io_lock, flags);
+       rqi = (struct snic_req_info *) CMD_SP(sc);
+       WARN_ON_ONCE(!rqi);
+
+       if (!rqi) {
+               atomic64_inc(&snic->s_stats.io.req_null);
+               spin_unlock_irqrestore(io_lock, flags);
+               CMD_FLAGS(sc) |= SNIC_IO_ABTS_TERM_REQ_NULL;
+               SNIC_HOST_ERR(snic->shost,
+                             "itmf_cmpl: rqi is null,Hdr stat = %s Tag = 0x%x sc = 0x%p flags 0x%llx\n",
+                             snic_io_status_to_str(cmpl_stat), cmnd_id, sc,
+                             CMD_FLAGS(sc));
+
+               return ret;
+       }
+
+       /* Extract task management flags */
+       tm_tags = cmnd_id & ~(SNIC_TAG_MASK);
+
+       start_time = rqi->start_time;
+       cmnd_id &= (SNIC_TAG_MASK);
+
+       switch (tm_tags) {
+       case SNIC_TAG_ABORT:
+               /* Abort only issued on cmd */
+               snic_update_abort_stats(snic, cmpl_stat);
+
+               if (CMD_STATE(sc) != SNIC_IOREQ_ABTS_PENDING) {
+                       /* This is a late completion. Ignore it. */
+                       ret = -1;
+                       spin_unlock_irqrestore(io_lock, flags);
+                       break;
+               }
+
+               CMD_STATE(sc) = SNIC_IOREQ_ABTS_COMPLETE;
+               CMD_ABTS_STATUS(sc) = cmpl_stat;
+               CMD_FLAGS(sc) |= SNIC_IO_ABTS_TERM_DONE;
+
+               SNIC_SCSI_DBG(snic->shost,
+                             "itmf_cmpl:Abort Cmpl Recvd.Tag 0x%x Status %s flags 0x%llx\n",
+                             cmnd_id,
+                             snic_io_status_to_str(cmpl_stat),
+                             CMD_FLAGS(sc));
+
+               /*
+                * If scsi_eh thread is blocked waiting for abts complete,
+                * signal completion to it. IO will be cleaned in the thread,
+                * else clean it in this context.
+                */
+               if (rqi->abts_done) {
+                       complete(rqi->abts_done);
+                       spin_unlock_irqrestore(io_lock, flags);
+
+                       break; /* jump out */
+               }
+
+               CMD_SP(sc) = NULL;
+               sc->result = (DID_ERROR << 16);
+               SNIC_SCSI_DBG(snic->shost,
+                             "itmf_cmpl: Completing IO. sc %p flags 0x%llx\n",
+                             sc, CMD_FLAGS(sc));
+
+               spin_unlock_irqrestore(io_lock, flags);
+
+               snic_release_req_buf(snic, rqi, sc);
+
+               if (sc->scsi_done) {
+                       SNIC_TRC(snic->shost->host_no, cmnd_id, (ulong) sc,
+                                jiffies_to_msecs(jiffies - start_time),
+                                (ulong) fwreq, SNIC_TRC_CMD(sc),
+                                SNIC_TRC_CMD_STATE_FLAGS(sc));
+
+                       sc->scsi_done(sc);
+               }
+
+               break;
+
+       case SNIC_TAG_DEV_RST:
+       case SNIC_TAG_DEV_RST | SNIC_TAG_IOCTL_DEV_RST:
+               snic_proc_dr_cmpl_locked(snic, fwreq, cmpl_stat, cmnd_id, sc);
+               spin_unlock_irqrestore(io_lock, flags);
+               ret = 0;
+
+               break;
+
+       case SNIC_TAG_ABORT | SNIC_TAG_DEV_RST:
+               /* Abort and terminate completion of device reset req */
+
+               CMD_STATE(sc) = SNIC_IOREQ_ABTS_COMPLETE;
+               CMD_ABTS_STATUS(sc) = cmpl_stat;
+               CMD_FLAGS(sc) |= SNIC_DEV_RST_DONE;
+
+               SNIC_SCSI_DBG(snic->shost,
+                             "itmf_cmpl:dev reset abts cmpl recvd. id %d status %s flags 0x%llx\n",
+                             cmnd_id, snic_io_status_to_str(cmpl_stat),
+                             CMD_FLAGS(sc));
+
+               if (rqi->abts_done)
+                       complete(rqi->abts_done);
+
+               spin_unlock_irqrestore(io_lock, flags);
+
+               break;
+
+       default:
+               spin_unlock_irqrestore(io_lock, flags);
+               SNIC_HOST_ERR(snic->shost,
+                             "itmf_cmpl: Unknown TM tag bit 0x%x\n", tm_tags);
+
+               SNIC_HOST_ERR(snic->shost,
+                             "itmf_cmpl:Unexpected itmf io stat %s Tag = 0x%x flags 0x%llx\n",
+                             snic_ioreq_state_to_str(CMD_STATE(sc)),
+                             cmnd_id,
+                             CMD_FLAGS(sc));
+               ret = -1;
+               SNIC_BUG_ON(1);
+
+               break;
+       }
+
+       return ret;
+} /* end of snic_process_itmf_cmpl_status */
+
+/*
+ * snic_itmf_cmpl_handler.
+ * Routine to handle itmf completions.
+ */
+static void
+snic_itmf_cmpl_handler(struct snic *snic, struct snic_fw_req *fwreq)
+{
+       struct scsi_cmnd  *sc = NULL;
+       struct snic_req_info *rqi = NULL;
+       struct snic_itmf_cmpl *itmf_cmpl = NULL;
+       ulong ctx;
+       u32 cmnd_id;
+       u32 hid;
+       u8 typ;
+       u8 hdr_stat;
+
+       snic_io_hdr_dec(&fwreq->hdr, &typ, &hdr_stat, &cmnd_id, &hid, &ctx);
+       SNIC_SCSI_DBG(snic->shost,
+                     "Itmf_cmpl: %s: type = %x, hdr_stat = %x, cmnd_id = %x, hid = %x,ctx = %lx\n",
+                     __func__, typ, hdr_stat, cmnd_id, hid, ctx);
+
+       itmf_cmpl = &fwreq->u.itmf_cmpl;
+       SNIC_SCSI_DBG(snic->shost,
+                     "Itmf_cmpl: nterm %u , flags 0x%x\n",
+                     le32_to_cpu(itmf_cmpl->nterminated), itmf_cmpl->flags);
+
+       /* spl case, dev reset issued through ioctl */
+       if (cmnd_id & SNIC_TAG_IOCTL_DEV_RST) {
+               rqi = (struct snic_req_info *) ctx;
+               sc = rqi->sc;
+
+               goto ioctl_dev_rst;
+       }
+
+       if ((cmnd_id & SNIC_TAG_MASK) >= snic->max_tag_id) {
+               SNIC_HOST_ERR(snic->shost,
+                             "Itmf_cmpl: Tag 0x%x out of Range,HdrStat %s\n",
+                             cmnd_id, snic_io_status_to_str(hdr_stat));
+               SNIC_BUG_ON(1);
+
+               return;
+       }
+
+       sc = scsi_host_find_tag(snic->shost, cmnd_id & SNIC_TAG_MASK);
+       WARN_ON_ONCE(!sc);
+
+ioctl_dev_rst:
+       if (!sc) {
+               atomic64_inc(&snic->s_stats.io.sc_null);
+               SNIC_HOST_ERR(snic->shost,
+                             "Itmf_cmpl: sc is NULL - Hdr Stat %s Tag 0x%x\n",
+                             snic_io_status_to_str(hdr_stat), cmnd_id);
+
+               return;
+       }
+
+       snic_process_itmf_cmpl(snic, fwreq, cmnd_id, hdr_stat, sc);
+} /* end of snic_itmf_cmpl_handler */
+
+
+
+static void
+snic_hba_reset_scsi_cleanup(struct snic *snic, struct scsi_cmnd *sc)
+{
+       struct snic_stats *st = &snic->s_stats;
+       long act_ios = 0, act_fwreqs = 0;
+
+       SNIC_SCSI_DBG(snic->shost, "HBA Reset scsi cleanup.\n");
+       snic_scsi_cleanup(snic, snic_cmd_tag(sc));
+
+       /* Update stats on pending IOs */
+       act_ios = atomic64_read(&st->io.active);
+       atomic64_add(act_ios, &st->io.compl);
+       atomic64_sub(act_ios, &st->io.active);
+
+       act_fwreqs = atomic64_read(&st->fw.actv_reqs);
+       atomic64_sub(act_fwreqs, &st->fw.actv_reqs);
+}
+
+/*
+ * snic_hba_reset_cmpl_handler :
+ *
+ * Notes :
+ * 1. Cleanup all the scsi cmds, release all snic specific cmds
+ * 2. Issue Report Targets in case of SAN targets
+ */
+static int
+snic_hba_reset_cmpl_handler(struct snic *snic, struct snic_fw_req *fwreq)
+{
+       ulong ctx;
+       u32 cmnd_id;
+       u32 hid;
+       u8 typ;
+       u8 hdr_stat;
+       struct scsi_cmnd *sc = NULL;
+       struct snic_req_info *rqi = NULL;
+       spinlock_t *io_lock = NULL;
+       unsigned long flags, gflags;
+       int ret = 0;
+
+       SNIC_HOST_INFO(snic->shost,
+                      "reset_cmpl:HBA Reset Completion received.\n");
+
+       snic_io_hdr_dec(&fwreq->hdr, &typ, &hdr_stat, &cmnd_id, &hid, &ctx);
+       SNIC_SCSI_DBG(snic->shost,
+                     "reset_cmpl: type = %x, hdr_stat = %x, cmnd_id = %x, hid = %x, ctx = %lx\n",
+                     typ, hdr_stat, cmnd_id, hid, ctx);
+
+       /* spl case, host reset issued through ioctl */
+       if (cmnd_id == SCSI_NO_TAG) {
+               rqi = (struct snic_req_info *) ctx;
+               sc = rqi->sc;
+
+               goto ioctl_hba_rst;
+       }
+
+       if (cmnd_id >= snic->max_tag_id) {
+               SNIC_HOST_ERR(snic->shost,
+                             "reset_cmpl: Tag 0x%x out of Range,HdrStat %s\n",
+                             cmnd_id, snic_io_status_to_str(hdr_stat));
+               SNIC_BUG_ON(1);
+
+               return 1;
+       }
+
+       sc = scsi_host_find_tag(snic->shost, cmnd_id);
+ioctl_hba_rst:
+       if (!sc) {
+               atomic64_inc(&snic->s_stats.io.sc_null);
+               SNIC_HOST_ERR(snic->shost,
+                             "reset_cmpl: sc is NULL - Hdr Stat %s Tag 0x%x\n",
+                             snic_io_status_to_str(hdr_stat), cmnd_id);
+               ret = 1;
+
+               return ret;
+       }
+
+       io_lock = snic_io_lock_hash(snic, sc);
+       spin_lock_irqsave(io_lock, flags);
+
+       if (!snic->remove_wait) {
+               spin_unlock_irqrestore(io_lock, flags);
+               SNIC_HOST_ERR(snic->shost,
+                             "reset_cmpl:host reset completed after timout\n");
+               ret = 1;
+
+               return ret;
+       }
+
+       rqi = (struct snic_req_info *) CMD_SP(sc);
+       WARN_ON_ONCE(!rqi);
+
+       if (!rqi) {
+               atomic64_inc(&snic->s_stats.io.req_null);
+               spin_unlock_irqrestore(io_lock, flags);
+               CMD_FLAGS(sc) |= SNIC_IO_ABTS_TERM_REQ_NULL;
+               SNIC_HOST_ERR(snic->shost,
+                             "reset_cmpl: rqi is null,Hdr stat %s Tag 0x%x sc 0x%p flags 0x%llx\n",
+                             snic_io_status_to_str(hdr_stat), cmnd_id, sc,
+                             CMD_FLAGS(sc));
+
+               ret = 1;
+
+               return ret;
+       }
+       /* stats */
+       spin_unlock_irqrestore(io_lock, flags);
+
+       /* scsi cleanup */
+       snic_hba_reset_scsi_cleanup(snic, sc);
+
+       SNIC_BUG_ON(snic_get_state(snic) != SNIC_OFFLINE &&
+                   snic_get_state(snic) != SNIC_FWRESET);
+
+       /* Careful locking between snic_lock and io lock */
+       spin_lock_irqsave(io_lock, flags);
+       spin_lock_irqsave(&snic->snic_lock, gflags);
+       if (snic_get_state(snic) == SNIC_FWRESET)
+               snic_set_state(snic, SNIC_ONLINE);
+       spin_unlock_irqrestore(&snic->snic_lock, gflags);
+
+       if (snic->remove_wait)
+               complete(snic->remove_wait);
+
+       spin_unlock_irqrestore(io_lock, flags);
+       atomic64_inc(&snic->s_stats.reset.hba_reset_cmpl);
+
+       ret = 0;
+       /* Rediscovery is for SAN */
+       if (snic->config.xpt_type == SNIC_DAS)
+                       return ret;
+
+       SNIC_SCSI_DBG(snic->shost, "reset_cmpl: Queuing discovery work.\n");
+       queue_work(snic_glob->event_q, &snic->disc_work);
+
+       return ret;
+}
+
+static void
+snic_msg_ack_handler(struct snic *snic, struct snic_fw_req *fwreq)
+{
+       SNIC_HOST_INFO(snic->shost, "Message Ack Received.\n");
+
+       SNIC_ASSERT_NOT_IMPL(1);
+}
+
+static void
+snic_aen_handler(struct snic *snic, struct snic_fw_req *fwreq)
+{
+       u8 typ, hdr_stat;
+       u32 cmnd_id, hid;
+       ulong ctx;
+       struct snic_async_evnotify *aen = &fwreq->u.async_ev;
+       u32 event_id = 0;
+
+       snic_io_hdr_dec(&fwreq->hdr, &typ, &hdr_stat, &cmnd_id, &hid, &ctx);
+       SNIC_SCSI_DBG(snic->shost,
+                     "aen: type = %x, hdr_stat = %x, cmnd_id = %x, hid = %x, ctx = %lx\n",
+                     typ, hdr_stat, cmnd_id, hid, ctx);
+
+       event_id = le32_to_cpu(aen->ev_id);
+
+       switch (event_id) {
+       case SNIC_EV_TGT_OFFLINE:
+               SNIC_HOST_INFO(snic->shost, "aen:TGT_OFFLINE Event Recvd.\n");
+               break;
+
+       case SNIC_EV_TGT_ONLINE:
+               SNIC_HOST_INFO(snic->shost, "aen:TGT_ONLINE Event Recvd.\n");
+               break;
+
+       case SNIC_EV_LUN_OFFLINE:
+               SNIC_HOST_INFO(snic->shost, "aen:LUN_OFFLINE Event Recvd.\n");
+               break;
+
+       case SNIC_EV_LUN_ONLINE:
+               SNIC_HOST_INFO(snic->shost, "aen:LUN_ONLINE Event Recvd.\n");
+               break;
+
+       case SNIC_EV_CONF_CHG:
+               SNIC_HOST_INFO(snic->shost, "aen:Config Change Event Recvd.\n");
+               break;
+
+       case SNIC_EV_TGT_ADDED:
+               SNIC_HOST_INFO(snic->shost, "aen:TGT_ADD Event Recvd.\n");
+               break;
+
+       case SNIC_EV_TGT_DELTD:
+               SNIC_HOST_INFO(snic->shost, "aen:TGT_DEL Event Recvd.\n");
+               break;
+
+       case SNIC_EV_LUN_ADDED:
+               SNIC_HOST_INFO(snic->shost, "aen:LUN_ADD Event Recvd.\n");
+               break;
+
+       case SNIC_EV_LUN_DELTD:
+               SNIC_HOST_INFO(snic->shost, "aen:LUN_DEL Event Recvd.\n");
+               break;
+
+       case SNIC_EV_DISC_CMPL:
+               SNIC_HOST_INFO(snic->shost, "aen:DISC_CMPL Event Recvd.\n");
+               break;
+
+       default:
+               SNIC_HOST_INFO(snic->shost, "aen:Unknown Event Recvd.\n");
+               SNIC_BUG_ON(1);
+               break;
+       }
+
+       SNIC_ASSERT_NOT_IMPL(1);
+} /* end of snic_aen_handler */
+
+/*
+ * snic_io_cmpl_handler
+ * Routine to process CQ entries(IO Completions) posted by fw.
+ */
+static int
+snic_io_cmpl_handler(struct vnic_dev *vdev,
+                    unsigned int cq_idx,
+                    struct snic_fw_req *fwreq)
+{
+       struct snic *snic = svnic_dev_priv(vdev);
+       u64 start = jiffies, cmpl_time;
+
+       snic_print_desc(__func__, (char *)fwreq, sizeof(*fwreq));
+
+       /* Update FW Stats */
+       if ((fwreq->hdr.type >= SNIC_RSP_REPORT_TGTS_CMPL) &&
+               (fwreq->hdr.type <= SNIC_RSP_BOOT_LUNS_CMPL))
+               atomic64_dec(&snic->s_stats.fw.actv_reqs);
+
+       SNIC_BUG_ON((fwreq->hdr.type > SNIC_RSP_BOOT_LUNS_CMPL) &&
+                   (fwreq->hdr.type < SNIC_MSG_ASYNC_EVNOTIFY));
+
+       /* Check for snic subsys errors */
+       switch (fwreq->hdr.status) {
+       case SNIC_STAT_NOT_READY:       /* XPT yet to initialize */
+               SNIC_HOST_ERR(snic->shost,
+                             "sNIC SubSystem is NOT Ready.\n");
+               break;
+
+       case SNIC_STAT_FATAL_ERROR:     /* XPT Error */
+               SNIC_HOST_ERR(snic->shost,
+                             "sNIC SubSystem in Unrecoverable State.\n");
+               break;
+       }
+
+       switch (fwreq->hdr.type) {
+       case SNIC_RSP_EXCH_VER_CMPL:
+               snic_io_exch_ver_cmpl_handler(snic, fwreq);
+               break;
+
+       case SNIC_RSP_REPORT_TGTS_CMPL:
+               snic_report_tgt_cmpl_handler(snic, fwreq);
+               break;
+
+       case SNIC_RSP_ICMND_CMPL:
+               snic_icmnd_cmpl_handler(snic, fwreq);
+               break;
+
+       case SNIC_RSP_ITMF_CMPL:
+               snic_itmf_cmpl_handler(snic, fwreq);
+               break;
+
+       case SNIC_RSP_HBA_RESET_CMPL:
+               snic_hba_reset_cmpl_handler(snic, fwreq);
+               break;
+
+       case SNIC_MSG_ACK:
+               snic_msg_ack_handler(snic, fwreq);
+               break;
+
+       case SNIC_MSG_ASYNC_EVNOTIFY:
+               snic_aen_handler(snic, fwreq);
+               break;
+
+       default:
+               SNIC_BUG_ON(1);
+               SNIC_SCSI_DBG(snic->shost,
+                             "Unknown Firmwqre completion request type %d\n",
+                             fwreq->hdr.type);
+               break;
+       }
+
+       /* Update Stats */
+       cmpl_time = jiffies - start;
+       if (cmpl_time > atomic64_read(&snic->s_stats.io.max_cmpl_time))
+               atomic64_set(&snic->s_stats.io.max_cmpl_time, cmpl_time);
+
+       return 0;
+} /* end of snic_io_cmpl_handler */
+
+/*
+ * snic_fwcq_cmpl_handler
+ * Routine to process fwCQ
+ * This CQ is independent, and not associated with wq/rq/wq_copy queues
+ */
+int
+snic_fwcq_cmpl_handler(struct snic *snic, int io_cmpl_work)
+{
+       unsigned int num_ent = 0;       /* number cq entries processed */
+       unsigned int cq_idx;
+       unsigned int nent_per_cq;
+       struct snic_misc_stats *misc_stats = &snic->s_stats.misc;
+
+       for (cq_idx = snic->wq_count; cq_idx < snic->cq_count; cq_idx++) {
+               nent_per_cq = vnic_cq_fw_service(&snic->cq[cq_idx],
+                                                snic_io_cmpl_handler,
+                                                io_cmpl_work);
+               num_ent += nent_per_cq;
+
+               if (nent_per_cq > atomic64_read(&misc_stats->max_cq_ents))
+                       atomic64_set(&misc_stats->max_cq_ents, nent_per_cq);
+       }
+
+       return num_ent;
+} /* end of snic_fwcq_cmpl_handler */
+
+/*
+ * snic_queue_itmf_req: Common API to queue Task Management requests.
+ * Use rqi->tm_tag for passing special tags.
+ * @req_id : aborted request's tag, -1 for lun reset.
+ */
+static int
+snic_queue_itmf_req(struct snic *snic,
+                   struct snic_host_req *tmreq,
+                   struct scsi_cmnd *sc,
+                   u32 tmf,
+                   u32 req_id)
+{
+       struct snic_req_info *rqi = req_to_rqi(tmreq);
+       struct scsi_lun lun;
+       int tm_tag = snic_cmd_tag(sc) | rqi->tm_tag;
+       int ret = 0;
+
+       SNIC_BUG_ON(!rqi);
+       SNIC_BUG_ON(!rqi->tm_tag);
+
+       /* fill in lun info */
+       int_to_scsilun(sc->device->lun, &lun);
+
+       /* Initialize snic_host_req: itmf */
+       snic_itmf_init(tmreq,
+                      tm_tag,
+                      snic->config.hid,
+                      (ulong) rqi,
+                      0 /* flags */,
+                      req_id, /* Command to be aborted. */
+                      rqi->tgt_id,
+                      lun.scsi_lun,
+                      tmf);
+
+       /*
+        * In case of multiple aborts on same cmd,
+        * use try_wait_for_completion and completion_done() to check
+        * whether it queues aborts even after completion of abort issued
+        * prior.SNIC_BUG_ON(completion_done(&rqi->done));
+        */
+
+       ret = snic_queue_wq_desc(snic, tmreq, sizeof(*tmreq));
+       if (ret)
+               SNIC_HOST_ERR(snic->shost,
+                             "qitmf:Queuing ITMF(%d) Req sc %p, rqi %p, req_id %d tag %d Failed, ret = %d\n",
+                             tmf, sc, rqi, req_id, snic_cmd_tag(sc), ret);
+       else
+               SNIC_SCSI_DBG(snic->shost,
+                             "qitmf:Queuing ITMF(%d) Req sc %p, rqi %p, req_id %d, tag %d (req_id)- Success.",
+                             tmf, sc, rqi, req_id, snic_cmd_tag(sc));
+
+       return ret;
+} /* end of snic_queue_itmf_req */
+
+static int
+snic_issue_tm_req(struct snic *snic,
+                   struct snic_req_info *rqi,
+                   struct scsi_cmnd *sc,
+                   int tmf)
+{
+       struct snic_host_req *tmreq = NULL;
+       int req_id = 0, tag = snic_cmd_tag(sc);
+       int ret = 0;
+
+       if (snic_get_state(snic) == SNIC_FWRESET)
+               return -EBUSY;
+
+       atomic_inc(&snic->ios_inflight);
+
+       SNIC_SCSI_DBG(snic->shost,
+                     "issu_tmreq: Task mgmt req %d. rqi %p w/ tag %x\n",
+                     tmf, rqi, tag);
+
+
+       if (tmf == SNIC_ITMF_LUN_RESET) {
+               tmreq = snic_dr_req_init(snic, rqi);
+               req_id = SCSI_NO_TAG;
+       } else {
+               tmreq = snic_abort_req_init(snic, rqi);
+               req_id = tag;
+       }
+
+       if (!tmreq) {
+               ret = -ENOMEM;
+
+               goto tmreq_err;
+       }
+
+       ret = snic_queue_itmf_req(snic, tmreq, sc, tmf, req_id);
+       if (ret)
+               goto tmreq_err;
+
+       ret = 0;
+
+tmreq_err:
+       if (ret) {
+               SNIC_HOST_ERR(snic->shost,
+                             "issu_tmreq: Queing ITMF(%d) Req, sc %p rqi %p req_id %d tag %x fails err = %d\n",
+                             tmf, sc, rqi, req_id, tag, ret);
+       } else {
+               SNIC_SCSI_DBG(snic->shost,
+                             "issu_tmreq: Queuing ITMF(%d) Req, sc %p, rqi %p, req_id %d tag %x - Success.\n",
+                             tmf, sc, rqi, req_id, tag);
+       }
+
+       atomic_dec(&snic->ios_inflight);
+
+       return ret;
+}
+
+/*
+ * snic_queue_abort_req : Queues abort req to WQ
+ */
+static int
+snic_queue_abort_req(struct snic *snic,
+                    struct snic_req_info *rqi,
+                    struct scsi_cmnd *sc,
+                    int tmf)
+{
+       SNIC_SCSI_DBG(snic->shost, "q_abtreq: sc %p, rqi %p, tag %x, tmf %d\n",
+                     sc, rqi, snic_cmd_tag(sc), tmf);
+
+       /* Add special tag for abort */
+       rqi->tm_tag |= SNIC_TAG_ABORT;
+
+       return snic_issue_tm_req(snic, rqi, sc, tmf);
+}
+
+/*
+ * snic_abort_finish : called by snic_abort_cmd on queuing abort successfully.
+ */
+static int
+snic_abort_finish(struct snic *snic, struct scsi_cmnd *sc)
+{
+       struct snic_req_info *rqi = NULL;
+       spinlock_t *io_lock = NULL;
+       unsigned long flags;
+       int ret = 0, tag = snic_cmd_tag(sc);
+
+       io_lock = snic_io_lock_hash(snic, sc);
+       spin_lock_irqsave(io_lock, flags);
+       rqi = (struct snic_req_info *) CMD_SP(sc);
+       if (!rqi) {
+               atomic64_inc(&snic->s_stats.io.req_null);
+               CMD_FLAGS(sc) |= SNIC_IO_ABTS_TERM_REQ_NULL;
+
+               SNIC_SCSI_DBG(snic->shost,
+                             "abt_fini:req info is null tag 0x%x, sc 0x%p flags 0x%llx\n",
+                             tag, sc, CMD_FLAGS(sc));
+               ret = FAILED;
+
+               goto abort_fail;
+       }
+
+       rqi->abts_done = NULL;
+
+       ret = FAILED;
+
+       /* Check the abort status. */
+       switch (CMD_ABTS_STATUS(sc)) {
+       case SNIC_INVALID_CODE:
+               /* Firmware didn't complete abort req, timedout */
+               CMD_FLAGS(sc) |= SNIC_IO_ABTS_TIMEDOUT;
+               atomic64_inc(&snic->s_stats.abts.drv_tmo);
+               SNIC_SCSI_DBG(snic->shost,
+                             "abt_fini:sc %p Tag %x Driver Timeout.flags 0x%llx\n",
+                             sc, snic_cmd_tag(sc), CMD_FLAGS(sc));
+               /* do not release snic request in timedout case */
+               rqi = NULL;
+
+               goto abort_fail;
+
+       case SNIC_STAT_IO_SUCCESS:
+       case SNIC_STAT_IO_NOT_FOUND:
+               ret = SUCCESS;
+               break;
+
+       default:
+               /* Firmware completed abort with error */
+               ret = FAILED;
+               break;
+       }
+
+       CMD_SP(sc) = NULL;
+       SNIC_HOST_INFO(snic->shost,
+                      "abt_fini: Tag %x, Cmpl Status %s flags 0x%llx\n",
+                      tag, snic_io_status_to_str(CMD_ABTS_STATUS(sc)),
+                      CMD_FLAGS(sc));
+
+abort_fail:
+       spin_unlock_irqrestore(io_lock, flags);
+       if (rqi)
+               snic_release_req_buf(snic, rqi, sc);
+
+       return ret;
+} /* end of snic_abort_finish */
+
+/*
+ * snic_send_abort_and_wait : Issues Abort, and Waits
+ */
+static int
+snic_send_abort_and_wait(struct snic *snic, struct scsi_cmnd *sc)
+{
+       struct snic_req_info *rqi = NULL;
+       enum snic_ioreq_state sv_state;
+       struct snic_tgt *tgt = NULL;
+       spinlock_t *io_lock = NULL;
+       DECLARE_COMPLETION_ONSTACK(tm_done);
+       unsigned long flags;
+       int ret = 0, tmf = 0, tag = snic_cmd_tag(sc);
+
+       tgt = starget_to_tgt(scsi_target(sc->device));
+       if ((snic_tgt_chkready(tgt) != 0) && (tgt->tdata.typ == SNIC_TGT_SAN))
+               tmf = SNIC_ITMF_ABTS_TASK_TERM;
+       else
+               tmf = SNIC_ITMF_ABTS_TASK;
+
+       /* stats */
+
+       io_lock = snic_io_lock_hash(snic, sc);
+
+       /*
+        * Avoid a race between SCSI issuing the abort and the device
+        * completing the command.
+        *
+        * If the command is already completed by fw_cmpl code,
+        * we just return SUCCESS from here. This means that the abort
+        * succeeded. In the SCSI ML, since the timeout for command has
+        * happend, the completion wont actually complete the command
+        * and it will be considered as an aborted command
+        *
+        * The CMD_SP will not be cleared except while holding io_lock
+        */
+       spin_lock_irqsave(io_lock, flags);
+       rqi = (struct snic_req_info *) CMD_SP(sc);
+       if (!rqi) {
+               spin_unlock_irqrestore(io_lock, flags);
+
+               SNIC_HOST_ERR(snic->shost,
+                             "abt_cmd: rqi is null. Tag %d flags 0x%llx\n",
+                             tag, CMD_FLAGS(sc));
+
+               ret = SUCCESS;
+
+               goto send_abts_end;
+       }
+
+       rqi->abts_done = &tm_done;
+       if (CMD_STATE(sc) == SNIC_IOREQ_ABTS_PENDING) {
+               spin_unlock_irqrestore(io_lock, flags);
+
+               ret = 0;
+               goto abts_pending;
+       }
+       SNIC_BUG_ON(!rqi->abts_done);
+
+       /* Save Command State, should be restored on failed to Queue. */
+       sv_state = CMD_STATE(sc);
+
+       /*
+        * Command is still pending, need to abort it
+        * If the fw completes the command after this point,
+        * the completion won't be done till mid-layer, since abot
+        * has already started.
+        */
+       CMD_STATE(sc) = SNIC_IOREQ_ABTS_PENDING;
+       CMD_ABTS_STATUS(sc) = SNIC_INVALID_CODE;
+
+       SNIC_SCSI_DBG(snic->shost, "send_abt_cmd: TAG 0x%x\n", tag);
+
+       spin_unlock_irqrestore(io_lock, flags);
+
+       /* Now Queue the abort command to firmware */
+       ret = snic_queue_abort_req(snic, rqi, sc, tmf);
+       if (ret) {
+               SNIC_HOST_ERR(snic->shost,
+                             "send_abt_cmd: IO w/ Tag 0x%x fail w/ err %d flags 0x%llx\n",
+                             tag, ret, CMD_FLAGS(sc));
+
+               spin_lock_irqsave(io_lock, flags);
+               /* Restore Command's previous state */
+               CMD_STATE(sc) = sv_state;
+               rqi = (struct snic_req_info *) CMD_SP(sc);
+               if (rqi)
+                       rqi->abts_done = NULL;
+               spin_unlock_irqrestore(io_lock, flags);
+               ret = FAILED;
+
+               goto send_abts_end;
+       }
+
+       spin_lock_irqsave(io_lock, flags);
+       if (tmf == SNIC_ITMF_ABTS_TASK) {
+               CMD_FLAGS(sc) |= SNIC_IO_ABTS_ISSUED;
+               atomic64_inc(&snic->s_stats.abts.num);
+       } else {
+               /* term stats */
+               CMD_FLAGS(sc) |= SNIC_IO_TERM_ISSUED;
+       }
+       spin_unlock_irqrestore(io_lock, flags);
+
+       SNIC_SCSI_DBG(snic->shost,
+                     "send_abt_cmd: sc %p Tag %x flags 0x%llx\n",
+                     sc, tag, CMD_FLAGS(sc));
+
+
+       ret = 0;
+
+abts_pending:
+       /*
+        * Queued an abort IO, wait for its completion.
+        * Once the fw completes the abort command, it will
+        * wakeup this thread.
+        */
+       wait_for_completion_timeout(&tm_done, SNIC_ABTS_TIMEOUT);
+
+send_abts_end:
+       return ret;
+} /* end of snic_send_abort_and_wait */
+
+/*
+ * This function is exported to SCSI for sending abort cmnds.
+ * A SCSI IO is represent by snic_ioreq in the driver.
+ * The snic_ioreq is linked to the SCSI Cmd, thus a link with the ULP'S IO
+ */
+int
+snic_abort_cmd(struct scsi_cmnd *sc)
+{
+       struct snic *snic = shost_priv(sc->device->host);
+       int ret = SUCCESS, tag = snic_cmd_tag(sc);
+       u32 start_time = jiffies;
+
+       SNIC_SCSI_DBG(snic->shost, "abt_cmd:sc %p :0x%x :req = %p :tag = %d\n",
+                      sc, sc->cmnd[0], sc->request, tag);
+
+       if (unlikely(snic_get_state(snic) != SNIC_ONLINE)) {
+               SNIC_HOST_ERR(snic->shost,
+                             "abt_cmd: tag %x Parent Devs are not rdy\n",
+                             tag);
+               ret = FAST_IO_FAIL;
+
+               goto abort_end;
+       }
+
+
+       ret = snic_send_abort_and_wait(snic, sc);
+       if (ret)
+               goto abort_end;
+
+       ret = snic_abort_finish(snic, sc);
+
+abort_end:
+       SNIC_TRC(snic->shost->host_no, tag, (ulong) sc,
+                jiffies_to_msecs(jiffies - start_time), 0,
+                SNIC_TRC_CMD(sc), SNIC_TRC_CMD_STATE_FLAGS(sc));
+
+       SNIC_SCSI_DBG(snic->shost,
+                     "abts: Abort Req Status = %s\n",
+                     (ret == SUCCESS) ? "SUCCESS" :
+                      ((ret == FAST_IO_FAIL) ? "FAST_IO_FAIL" : "FAILED"));
+
+       return ret;
+}
+
+
+
+static int
+snic_is_abts_pending(struct snic *snic, struct scsi_cmnd *lr_sc)
+{
+       struct snic_req_info *rqi = NULL;
+       struct scsi_cmnd *sc = NULL;
+       struct scsi_device *lr_sdev = NULL;
+       spinlock_t *io_lock = NULL;
+       u32 tag;
+       unsigned long flags;
+
+       if (lr_sc)
+               lr_sdev = lr_sc->device;
+
+       /* walk through the tag map, an dcheck if IOs are still pending in fw*/
+       for (tag = 0; tag < snic->max_tag_id; tag++) {
+               io_lock = snic_io_lock_tag(snic, tag);
+
+               spin_lock_irqsave(io_lock, flags);
+               sc = scsi_host_find_tag(snic->shost, tag);
+
+               if (!sc || (lr_sc && (sc->device != lr_sdev || sc == lr_sc))) {
+                       spin_unlock_irqrestore(io_lock, flags);
+
+                       continue;
+               }
+
+               rqi = (struct snic_req_info *) CMD_SP(sc);
+               if (!rqi) {
+                       spin_unlock_irqrestore(io_lock, flags);
+
+                       continue;
+               }
+
+               /*
+                * Found IO that is still pending w/ firmware and belongs to
+                * the LUN that is under reset, if lr_sc != NULL
+                */
+               SNIC_SCSI_DBG(snic->shost, "Found IO in %s on LUN\n",
+                             snic_ioreq_state_to_str(CMD_STATE(sc)));
+
+               if (CMD_STATE(sc) == SNIC_IOREQ_ABTS_PENDING) {
+                       spin_unlock_irqrestore(io_lock, flags);
+
+                       return 1;
+               }
+
+               spin_unlock_irqrestore(io_lock, flags);
+       }
+
+       return 0;
+} /* end of snic_is_abts_pending */
+
+static int
+snic_dr_clean_single_req(struct snic *snic,
+                        u32 tag,
+                        struct scsi_device *lr_sdev)
+{
+       struct snic_req_info *rqi = NULL;
+       struct snic_tgt *tgt = NULL;
+       struct scsi_cmnd *sc = NULL;
+       spinlock_t *io_lock = NULL;
+       u32 sv_state = 0, tmf = 0;
+       DECLARE_COMPLETION_ONSTACK(tm_done);
+       unsigned long flags;
+       int ret = 0;
+
+       io_lock = snic_io_lock_tag(snic, tag);
+       spin_lock_irqsave(io_lock, flags);
+       sc = scsi_host_find_tag(snic->shost, tag);
+
+       /* Ignore Cmd that don't belong to Lun Reset device */
+       if (!sc || sc->device != lr_sdev)
+               goto skip_clean;
+
+       rqi = (struct snic_req_info *) CMD_SP(sc);
+
+       if (!rqi)
+               goto skip_clean;
+
+
+       if (CMD_STATE(sc) == SNIC_IOREQ_ABTS_PENDING)
+               goto skip_clean;
+
+
+       if ((CMD_FLAGS(sc) & SNIC_DEVICE_RESET) &&
+                       (!(CMD_FLAGS(sc) & SNIC_DEV_RST_ISSUED))) {
+
+               SNIC_SCSI_DBG(snic->shost,
+                             "clean_single_req: devrst is not pending sc 0x%p\n",
+                             sc);
+
+               goto skip_clean;
+       }
+
+       SNIC_SCSI_DBG(snic->shost,
+               "clean_single_req: Found IO in %s on lun\n",
+               snic_ioreq_state_to_str(CMD_STATE(sc)));
+
+       /* Save Command State */
+       sv_state = CMD_STATE(sc);
+
+       /*
+        * Any pending IO issued prior to reset is expected to be
+        * in abts pending state, if not we need to set SNIC_IOREQ_ABTS_PENDING
+        * to indicate the IO is abort pending.
+        * When IO is completed, the IO will be handed over and handled
+        * in this function.
+        */
+
+       CMD_STATE(sc) = SNIC_IOREQ_ABTS_PENDING;
+       SNIC_BUG_ON(rqi->abts_done);
+
+       if (CMD_FLAGS(sc) & SNIC_DEVICE_RESET) {
+               rqi->tm_tag = SNIC_TAG_DEV_RST;
+
+               SNIC_SCSI_DBG(snic->shost,
+                             "clean_single_req:devrst sc 0x%p\n", sc);
+       }
+
+       CMD_ABTS_STATUS(sc) = SNIC_INVALID_CODE;
+       rqi->abts_done = &tm_done;
+       spin_unlock_irqrestore(io_lock, flags);
+
+       tgt = starget_to_tgt(scsi_target(sc->device));
+       if ((snic_tgt_chkready(tgt) != 0) && (tgt->tdata.typ == SNIC_TGT_SAN))
+               tmf = SNIC_ITMF_ABTS_TASK_TERM;
+       else
+               tmf = SNIC_ITMF_ABTS_TASK;
+
+       /* Now queue the abort command to firmware */
+       ret = snic_queue_abort_req(snic, rqi, sc, tmf);
+       if (ret) {
+               SNIC_HOST_ERR(snic->shost,
+                             "clean_single_req_err:sc %p, tag %d abt failed. tm_tag %d flags 0x%llx\n",
+                             sc, tag, rqi->tm_tag, CMD_FLAGS(sc));
+
+               spin_lock_irqsave(io_lock, flags);
+               rqi = (struct snic_req_info *) CMD_SP(sc);
+               if (rqi)
+                       rqi->abts_done = NULL;
+
+               /* Restore Command State */
+               if (CMD_STATE(sc) == SNIC_IOREQ_ABTS_PENDING)
+                       CMD_STATE(sc) = sv_state;
+
+               ret = 1;
+               goto skip_clean;
+       }
+
+       spin_lock_irqsave(io_lock, flags);
+       if (CMD_FLAGS(sc) & SNIC_DEVICE_RESET)
+               CMD_FLAGS(sc) |= SNIC_DEV_RST_TERM_ISSUED;
+
+       CMD_FLAGS(sc) |= SNIC_IO_INTERNAL_TERM_ISSUED;
+       spin_unlock_irqrestore(io_lock, flags);
+
+       wait_for_completion_timeout(&tm_done, SNIC_ABTS_TIMEOUT);
+
+       /* Recheck cmd state to check if it now aborted. */
+       spin_lock_irqsave(io_lock, flags);
+       rqi = (struct snic_req_info *) CMD_SP(sc);
+       if (!rqi) {
+               CMD_FLAGS(sc) |= SNIC_IO_ABTS_TERM_REQ_NULL;
+               goto skip_clean;
+       }
+       rqi->abts_done = NULL;
+
+       /* if abort is still pending w/ fw, fail */
+       if (CMD_ABTS_STATUS(sc) == SNIC_INVALID_CODE) {
+               SNIC_HOST_ERR(snic->shost,
+                             "clean_single_req_err:sc %p tag %d abt still pending w/ fw, tm_tag %d flags 0x%llx\n",
+                             sc, tag, rqi->tm_tag, CMD_FLAGS(sc));
+
+               CMD_FLAGS(sc) |= SNIC_IO_ABTS_TERM_DONE;
+               ret = 1;
+
+               goto skip_clean;
+       }
+
+       CMD_STATE(sc) = SNIC_IOREQ_ABTS_COMPLETE;
+       CMD_SP(sc) = NULL;
+       spin_unlock_irqrestore(io_lock, flags);
+
+       snic_release_req_buf(snic, rqi, sc);
+
+       ret = 0;
+
+       return ret;
+
+skip_clean:
+       spin_unlock_irqrestore(io_lock, flags);
+
+       return ret;
+} /* end of snic_dr_clean_single_req */
+
+static int
+snic_dr_clean_pending_req(struct snic *snic, struct scsi_cmnd *lr_sc)
+{
+       struct scsi_device *lr_sdev = lr_sc->device;
+       u32 tag = 0;
+       int ret = FAILED;
+
+       for (tag = 0; tag < snic->max_tag_id; tag++) {
+               if (tag == snic_cmd_tag(lr_sc))
+                       continue;
+
+               ret = snic_dr_clean_single_req(snic, tag, lr_sdev);
+               if (ret) {
+                       SNIC_HOST_ERR(snic->shost, "clean_err:tag = %d\n", tag);
+
+                       goto clean_err;
+               }
+       }
+
+       schedule_timeout(msecs_to_jiffies(100));
+
+       /* Walk through all the cmds and check abts status. */
+       if (snic_is_abts_pending(snic, lr_sc)) {
+               ret = FAILED;
+
+               goto clean_err;
+       }
+
+       ret = 0;
+       SNIC_SCSI_DBG(snic->shost, "clean_pending_req: Success.\n");
+
+       return ret;
+
+clean_err:
+       ret = FAILED;
+       SNIC_HOST_ERR(snic->shost,
+                     "Failed to Clean Pending IOs on %s device.\n",
+                     dev_name(&lr_sdev->sdev_gendev));
+
+       return ret;
+
+} /* end of snic_dr_clean_pending_req */
+
+/*
+ * snic_dr_finish : Called by snic_device_reset
+ */
+static int
+snic_dr_finish(struct snic *snic, struct scsi_cmnd *sc)
+{
+       struct snic_req_info *rqi = NULL;
+       spinlock_t *io_lock = NULL;
+       unsigned long flags;
+       int lr_res = 0;
+       int ret = FAILED;
+
+       io_lock = snic_io_lock_hash(snic, sc);
+       spin_lock_irqsave(io_lock, flags);
+       rqi = (struct snic_req_info *) CMD_SP(sc);
+       if (!rqi) {
+               spin_unlock_irqrestore(io_lock, flags);
+               SNIC_SCSI_DBG(snic->shost,
+                             "dr_fini: rqi is null tag 0x%x sc 0x%p flags 0x%llx\n",
+                             snic_cmd_tag(sc), sc, CMD_FLAGS(sc));
+
+               ret = FAILED;
+               goto dr_fini_end;
+       }
+
+       rqi->dr_done = NULL;
+
+       lr_res = CMD_LR_STATUS(sc);
+
+       switch (lr_res) {
+       case SNIC_INVALID_CODE:
+               /* stats */
+               SNIC_SCSI_DBG(snic->shost,
+                             "dr_fini: Tag %x Dev Reset Timedout. flags 0x%llx\n",
+                             snic_cmd_tag(sc), CMD_FLAGS(sc));
+
+               CMD_FLAGS(sc) |= SNIC_DEV_RST_TIMEDOUT;
+               ret = FAILED;
+
+               goto dr_failed;
+
+       case SNIC_STAT_IO_SUCCESS:
+               SNIC_SCSI_DBG(snic->shost,
+                             "dr_fini: Tag %x Dev Reset cmpl\n",
+                             snic_cmd_tag(sc));
+               ret = 0;
+               break;
+
+       default:
+               SNIC_HOST_ERR(snic->shost,
+                             "dr_fini:Device Reset completed& failed.Tag = %x lr_status %s flags 0x%llx\n",
+                             snic_cmd_tag(sc),
+                             snic_io_status_to_str(lr_res), CMD_FLAGS(sc));
+               ret = FAILED;
+               goto dr_failed;
+       }
+       spin_unlock_irqrestore(io_lock, flags);
+
+       /*
+        * Cleanup any IOs on this LUN that have still not completed.
+        * If any of these fail, then LUN Reset fails.
+        * Cleanup cleans all commands on this LUN except
+        * the lun reset command. If all cmds get cleaned, the LUN Reset
+        * succeeds.
+        */
+
+       ret = snic_dr_clean_pending_req(snic, sc);
+       if (ret) {
+               spin_lock_irqsave(io_lock, flags);
+               SNIC_SCSI_DBG(snic->shost,
+                             "dr_fini: Device Reset Failed since could not abort all IOs. Tag = %x.\n",
+                             snic_cmd_tag(sc));
+               rqi = (struct snic_req_info *) CMD_SP(sc);
+
+               goto dr_failed;
+       } else {
+               /* Cleanup LUN Reset Command */
+               spin_lock_irqsave(io_lock, flags);
+               rqi = (struct snic_req_info *) CMD_SP(sc);
+               if (rqi)
+                       ret = SUCCESS; /* Completed Successfully */
+               else
+                       ret = FAILED;
+       }
+
+dr_failed:
+       SNIC_BUG_ON(!spin_is_locked(io_lock));
+       if (rqi)
+               CMD_SP(sc) = NULL;
+       spin_unlock_irqrestore(io_lock, flags);
+
+       if (rqi)
+               snic_release_req_buf(snic, rqi, sc);
+
+dr_fini_end:
+       return ret;
+} /* end of snic_dr_finish */
+
+static int
+snic_queue_dr_req(struct snic *snic,
+                 struct snic_req_info *rqi,
+                 struct scsi_cmnd *sc)
+{
+       /* Add special tag for device reset */
+       rqi->tm_tag |= SNIC_TAG_DEV_RST;
+
+       return snic_issue_tm_req(snic, rqi, sc, SNIC_ITMF_LUN_RESET);
+}
+
+static int
+snic_send_dr_and_wait(struct snic *snic, struct scsi_cmnd *sc)
+{
+       struct snic_req_info *rqi = NULL;
+       enum snic_ioreq_state sv_state;
+       spinlock_t *io_lock = NULL;
+       unsigned long flags;
+       DECLARE_COMPLETION_ONSTACK(tm_done);
+       int ret = FAILED, tag = snic_cmd_tag(sc);
+
+       io_lock = snic_io_lock_hash(snic, sc);
+       spin_lock_irqsave(io_lock, flags);
+       CMD_FLAGS(sc) |= SNIC_DEVICE_RESET;
+       rqi = (struct snic_req_info *) CMD_SP(sc);
+       if (!rqi) {
+               SNIC_HOST_ERR(snic->shost,
+                             "send_dr: rqi is null, Tag 0x%x flags 0x%llx\n",
+                             tag, CMD_FLAGS(sc));
+               spin_unlock_irqrestore(io_lock, flags);
+
+               ret = FAILED;
+               goto send_dr_end;
+       }
+
+       /* Save Command state to restore in case Queuing failed. */
+       sv_state = CMD_STATE(sc);
+
+       CMD_STATE(sc) = SNIC_IOREQ_LR_PENDING;
+       CMD_LR_STATUS(sc) = SNIC_INVALID_CODE;
+
+       SNIC_SCSI_DBG(snic->shost, "dr: TAG = %x\n", tag);
+
+       rqi->dr_done = &tm_done;
+       SNIC_BUG_ON(!rqi->dr_done);
+
+       spin_unlock_irqrestore(io_lock, flags);
+       /*
+        * The Command state is changed to IOREQ_PENDING,
+        * in this case, if the command is completed, the icmnd_cmpl will
+        * mark the cmd as completed.
+        * This logic still makes LUN Reset is inevitable.
+        */
+
+       ret = snic_queue_dr_req(snic, rqi, sc);
+       if (ret) {
+               SNIC_HOST_ERR(snic->shost,
+                             "send_dr: IO w/ Tag 0x%x Failed err = %d. flags 0x%llx\n",
+                             tag, ret, CMD_FLAGS(sc));
+
+               spin_lock_irqsave(io_lock, flags);
+               /* Restore State */
+               CMD_STATE(sc) = sv_state;
+               rqi = (struct snic_req_info *) CMD_SP(sc);
+               if (rqi)
+                       rqi->dr_done = NULL;
+               /* rqi is freed in caller. */
+               spin_unlock_irqrestore(io_lock, flags);
+               ret = FAILED;
+
+               goto send_dr_end;
+       }
+
+       spin_lock_irqsave(io_lock, flags);
+       CMD_FLAGS(sc) |= SNIC_DEV_RST_ISSUED;
+       spin_unlock_irqrestore(io_lock, flags);
+
+       ret = 0;
+
+       wait_for_completion_timeout(&tm_done, SNIC_LUN_RESET_TIMEOUT);
+
+send_dr_end:
+       return ret;
+}
+
+/*
+ * auxillary funciton to check lun reset op is supported or not
+ * Not supported if returns 0
+ */
+static int
+snic_dev_reset_supported(struct scsi_device *sdev)
+{
+       struct snic_tgt *tgt = starget_to_tgt(scsi_target(sdev));
+
+       if (tgt->tdata.typ == SNIC_TGT_DAS)
+               return 0;
+
+       return 1;
+}
+
+static void
+snic_unlink_and_release_req(struct snic *snic, struct scsi_cmnd *sc, int flag)
+{
+       struct snic_req_info *rqi = NULL;
+       spinlock_t *io_lock = NULL;
+       unsigned long flags;
+       u32 start_time = jiffies;
+
+       io_lock = snic_io_lock_hash(snic, sc);
+       spin_lock_irqsave(io_lock, flags);
+       rqi = (struct snic_req_info *) CMD_SP(sc);
+       if (rqi) {
+               start_time = rqi->start_time;
+               CMD_SP(sc) = NULL;
+       }
+
+       CMD_FLAGS(sc) |= flag;
+       spin_unlock_irqrestore(io_lock, flags);
+
+       if (rqi)
+               snic_release_req_buf(snic, rqi, sc);
+
+       SNIC_TRC(snic->shost->host_no, snic_cmd_tag(sc), (ulong) sc,
+                jiffies_to_msecs(jiffies - start_time), (ulong) rqi,
+                SNIC_TRC_CMD(sc), SNIC_TRC_CMD_STATE_FLAGS(sc));
+}
+
+/*
+ * SCSI Eh thread issues a LUN Reset when one or more commands on a LUN
+ * fail to get aborted. It calls driver's eh_device_reset with a SCSI
+ * command on the LUN.
+ */
+int
+snic_device_reset(struct scsi_cmnd *sc)
+{
+       struct Scsi_Host *shost = sc->device->host;
+       struct snic *snic = shost_priv(shost);
+       struct snic_req_info *rqi = NULL;
+       int tag = snic_cmd_tag(sc);
+       int start_time = jiffies;
+       int ret = FAILED;
+       int dr_supp = 0;
+
+       SNIC_SCSI_DBG(shost, "dev_reset:sc %p :0x%x :req = %p :tag = %d\n",
+                     sc, sc->cmnd[0], sc->request,
+                     snic_cmd_tag(sc));
+       dr_supp = snic_dev_reset_supported(sc->device);
+       if (!dr_supp) {
+               /* device reset op is not supported */
+               SNIC_HOST_INFO(shost, "LUN Reset Op not supported.\n");
+               snic_unlink_and_release_req(snic, sc, SNIC_DEV_RST_NOTSUP);
+
+               goto dev_rst_end;
+       }
+
+       if (unlikely(snic_get_state(snic) != SNIC_ONLINE)) {
+               snic_unlink_and_release_req(snic, sc, 0);
+               SNIC_HOST_ERR(shost, "Devrst: Parent Devs are not online.\n");
+
+               goto dev_rst_end;
+       }
+
+       /* There is no tag when lun reset is issue through ioctl. */
+       if (unlikely(tag <= SNIC_NO_TAG)) {
+               SNIC_HOST_INFO(snic->shost,
+                              "Devrst: LUN Reset Recvd thru IOCTL.\n");
+
+               rqi = snic_req_init(snic, 0);
+               if (!rqi)
+                       goto dev_rst_end;
+
+               memset(scsi_cmd_priv(sc), 0,
+                       sizeof(struct snic_internal_io_state));
+               CMD_SP(sc) = (char *)rqi;
+               CMD_FLAGS(sc) = SNIC_NO_FLAGS;
+
+               /* Add special tag for dr coming from user spc */
+               rqi->tm_tag = SNIC_TAG_IOCTL_DEV_RST;
+               rqi->sc = sc;
+       }
+
+       ret = snic_send_dr_and_wait(snic, sc);
+       if (ret) {
+               SNIC_HOST_ERR(snic->shost,
+                             "Devrst: IO w/ Tag %x Failed w/ err = %d\n",
+                             tag, ret);
+
+               snic_unlink_and_release_req(snic, sc, 0);
+
+               goto dev_rst_end;
+       }
+
+       ret = snic_dr_finish(snic, sc);
+
+dev_rst_end:
+       SNIC_TRC(snic->shost->host_no, tag, (ulong) sc,
+                jiffies_to_msecs(jiffies - start_time),
+                0, SNIC_TRC_CMD(sc), SNIC_TRC_CMD_STATE_FLAGS(sc));
+
+       SNIC_SCSI_DBG(snic->shost,
+                     "Devrst: Returning from Device Reset : %s\n",
+                     (ret == SUCCESS) ? "SUCCESS" : "FAILED");
+
+       return ret;
+} /* end of snic_device_reset */
+
+/*
+ * SCSI Error handling calls driver's eh_host_reset if all prior
+ * error handling levels return FAILED.
+ *
+ * Host Reset is the highest level of error recovery. If this fails, then
+ * host is offlined by SCSI.
+ */
+/*
+ * snic_issue_hba_reset : Queues FW Reset Request.
+ */
+static int
+snic_issue_hba_reset(struct snic *snic, struct scsi_cmnd *sc)
+{
+       struct snic_req_info *rqi = NULL;
+       struct snic_host_req *req = NULL;
+       spinlock_t *io_lock = NULL;
+       DECLARE_COMPLETION_ONSTACK(wait);
+       unsigned long flags;
+       int ret = -ENOMEM;
+
+       rqi = snic_req_init(snic, 0);
+       if (!rqi) {
+               ret = -ENOMEM;
+
+               goto hba_rst_end;
+       }
+
+       if (snic_cmd_tag(sc) == SCSI_NO_TAG) {
+               memset(scsi_cmd_priv(sc), 0,
+                       sizeof(struct snic_internal_io_state));
+               SNIC_HOST_INFO(snic->shost, "issu_hr:Host reset thru ioctl.\n");
+               rqi->sc = sc;
+       }
+
+       req = rqi_to_req(rqi);
+
+       io_lock = snic_io_lock_hash(snic, sc);
+       spin_lock_irqsave(io_lock, flags);
+       SNIC_BUG_ON(CMD_SP(sc) != NULL);
+       CMD_STATE(sc) = SNIC_IOREQ_PENDING;
+       CMD_SP(sc) = (char *) rqi;
+       CMD_FLAGS(sc) |= SNIC_IO_INITIALIZED;
+       snic->remove_wait = &wait;
+       spin_unlock_irqrestore(io_lock, flags);
+
+       /* Initialize Request */
+       snic_io_hdr_enc(&req->hdr, SNIC_REQ_HBA_RESET, 0, snic_cmd_tag(sc),
+                       snic->config.hid, 0, (ulong) rqi);
+
+       req->u.reset.flags = 0;
+
+       ret = snic_queue_wq_desc(snic, req, sizeof(*req));
+       if (ret) {
+               SNIC_HOST_ERR(snic->shost,
+                             "issu_hr:Queuing HBA Reset Failed. w err %d\n",
+                             ret);
+
+               goto hba_rst_err;
+       }
+
+       spin_lock_irqsave(io_lock, flags);
+       CMD_FLAGS(sc) |= SNIC_HOST_RESET_ISSUED;
+       spin_unlock_irqrestore(io_lock, flags);
+       atomic64_inc(&snic->s_stats.reset.hba_resets);
+       SNIC_HOST_INFO(snic->shost, "Queued HBA Reset Successfully.\n");
+
+       wait_for_completion_timeout(snic->remove_wait,
+                                   SNIC_HOST_RESET_TIMEOUT);
+
+       if (snic_get_state(snic) == SNIC_FWRESET) {
+               SNIC_HOST_ERR(snic->shost, "reset_cmpl: Reset Timedout.\n");
+               ret = -ETIMEDOUT;
+
+               goto hba_rst_err;
+       }
+
+       spin_lock_irqsave(io_lock, flags);
+       snic->remove_wait = NULL;
+       rqi = (struct snic_req_info *) CMD_SP(sc);
+       CMD_SP(sc) = NULL;
+       spin_unlock_irqrestore(io_lock, flags);
+
+       if (rqi)
+               snic_req_free(snic, rqi);
+
+       ret = 0;
+
+       return ret;
+
+hba_rst_err:
+       spin_lock_irqsave(io_lock, flags);
+       snic->remove_wait = NULL;
+       rqi = (struct snic_req_info *) CMD_SP(sc);
+       CMD_SP(sc) = NULL;
+       spin_unlock_irqrestore(io_lock, flags);
+
+       if (rqi)
+               snic_req_free(snic, rqi);
+
+hba_rst_end:
+       SNIC_HOST_ERR(snic->shost,
+                     "reset:HBA Reset Failed w/ err = %d.\n",
+                     ret);
+
+       return ret;
+} /* end of snic_issue_hba_reset */
+
+int
+snic_reset(struct Scsi_Host *shost, struct scsi_cmnd *sc)
+{
+       struct snic *snic = shost_priv(shost);
+       enum snic_state sv_state;
+       unsigned long flags;
+       int ret = FAILED;
+
+       /* Set snic state as SNIC_FWRESET*/
+       sv_state = snic_get_state(snic);
+
+       spin_lock_irqsave(&snic->snic_lock, flags);
+       if (snic_get_state(snic) == SNIC_FWRESET) {
+               spin_unlock_irqrestore(&snic->snic_lock, flags);
+               SNIC_HOST_INFO(shost, "reset:prev reset is in progres\n");
+
+               msleep(SNIC_HOST_RESET_TIMEOUT);
+               ret = SUCCESS;
+
+               goto reset_end;
+       }
+
+       snic_set_state(snic, SNIC_FWRESET);
+       spin_unlock_irqrestore(&snic->snic_lock, flags);
+
+
+       /* Wait for all the IOs that are entered in Qcmd */
+       while (atomic_read(&snic->ios_inflight))
+               schedule_timeout(msecs_to_jiffies(1));
+
+       ret = snic_issue_hba_reset(snic, sc);
+       if (ret) {
+               SNIC_HOST_ERR(shost,
+                             "reset:Host Reset Failed w/ err %d.\n",
+                             ret);
+               spin_lock_irqsave(&snic->snic_lock, flags);
+               snic_set_state(snic, sv_state);
+               spin_unlock_irqrestore(&snic->snic_lock, flags);
+               atomic64_inc(&snic->s_stats.reset.hba_reset_fail);
+               ret = FAILED;
+
+               goto reset_end;
+       }
+
+       ret = SUCCESS;
+
+reset_end:
+       return ret;
+} /* end of snic_reset */
+
+/*
+ * SCSI Error handling calls driver's eh_host_reset if all prior
+ * error handling levels return FAILED.
+ *
+ * Host Reset is the highest level of error recovery. If this fails, then
+ * host is offlined by SCSI.
+ */
+int
+snic_host_reset(struct scsi_cmnd *sc)
+{
+       struct Scsi_Host *shost = sc->device->host;
+       u32 start_time  = jiffies;
+       int ret = FAILED;
+
+       SNIC_SCSI_DBG(shost,
+                     "host reset:sc %p sc_cmd 0x%x req %p tag %d flags 0x%llx\n",
+                     sc, sc->cmnd[0], sc->request,
+                     snic_cmd_tag(sc), CMD_FLAGS(sc));
+
+       ret = snic_reset(shost, sc);
+
+       SNIC_TRC(shost->host_no, snic_cmd_tag(sc), (ulong) sc,
+                jiffies_to_msecs(jiffies - start_time),
+                0, SNIC_TRC_CMD(sc), SNIC_TRC_CMD_STATE_FLAGS(sc));
+
+       return ret;
+} /* end of snic_host_reset */
+
+/*
+ * snic_cmpl_pending_tmreq : Caller should hold io_lock
+ */
+static void
+snic_cmpl_pending_tmreq(struct snic *snic, struct scsi_cmnd *sc)
+{
+       struct snic_req_info *rqi = NULL;
+
+       SNIC_SCSI_DBG(snic->shost,
+                     "Completing Pending TM Req sc %p, state %s flags 0x%llx\n",
+                     sc, snic_io_status_to_str(CMD_STATE(sc)), CMD_FLAGS(sc));
+
+       rqi = (struct snic_req_info *) CMD_SP(sc);
+       if (!rqi)
+               return;
+
+       if (rqi->dr_done)
+               complete(rqi->dr_done);
+       else if (rqi->abts_done)
+               complete(rqi->abts_done);
+}
+
+/*
+ * snic_scsi_cleanup: Walks through tag map and releases the reqs
+ */
+static void
+snic_scsi_cleanup(struct snic *snic, int ex_tag)
+{
+       struct snic_req_info *rqi = NULL;
+       struct scsi_cmnd *sc = NULL;
+       spinlock_t *io_lock = NULL;
+       unsigned long flags;
+       int tag;
+       u64 st_time = 0;
+
+       SNIC_SCSI_DBG(snic->shost, "sc_clean: scsi cleanup.\n");
+
+       for (tag = 0; tag < snic->max_tag_id; tag++) {
+               /* Skip ex_tag */
+               if (tag == ex_tag)
+                       continue;
+
+               io_lock = snic_io_lock_tag(snic, tag);
+               spin_lock_irqsave(io_lock, flags);
+               sc = scsi_host_find_tag(snic->shost, tag);
+               if (!sc) {
+                       spin_unlock_irqrestore(io_lock, flags);
+
+                       continue;
+               }
+
+               if (unlikely(snic_tmreq_pending(sc))) {
+                       /*
+                        * When FW Completes reset w/o sending completions
+                        * for outstanding ios.
+                        */
+                       snic_cmpl_pending_tmreq(snic, sc);
+                       spin_unlock_irqrestore(io_lock, flags);
+
+                       continue;
+               }
+
+               rqi = (struct snic_req_info *) CMD_SP(sc);
+               if (!rqi) {
+                       spin_unlock_irqrestore(io_lock, flags);
+
+                       goto cleanup;
+               }
+
+               SNIC_SCSI_DBG(snic->shost,
+                             "sc_clean: sc %p, rqi %p, tag %d flags 0x%llx\n",
+                             sc, rqi, tag, CMD_FLAGS(sc));
+
+               CMD_SP(sc) = NULL;
+               CMD_FLAGS(sc) |= SNIC_SCSI_CLEANUP;
+               spin_unlock_irqrestore(io_lock, flags);
+               st_time = rqi->start_time;
+
+               SNIC_HOST_INFO(snic->shost,
+                              "sc_clean: Releasing rqi %p : flags 0x%llx\n",
+                              rqi, CMD_FLAGS(sc));
+
+               snic_release_req_buf(snic, rqi, sc);
+
+cleanup:
+               sc->result = DID_TRANSPORT_DISRUPTED << 16;
+               SNIC_HOST_INFO(snic->shost,
+                              "sc_clean: DID_TRANSPORT_DISRUPTED for sc %p. rqi %p duration %llu msecs\n",
+                              sc, rqi, (jiffies - st_time));
+
+               /* Update IO stats */
+               snic_stats_update_io_cmpl(&snic->s_stats);
+
+               if (sc->scsi_done) {
+                       SNIC_TRC(snic->shost->host_no, tag, (ulong) sc,
+                                jiffies_to_msecs(jiffies - st_time), 0,
+                                SNIC_TRC_CMD(sc),
+                                SNIC_TRC_CMD_STATE_FLAGS(sc));
+
+                       sc->scsi_done(sc);
+               }
+       }
+} /* end of snic_scsi_cleanup */
+
+void
+snic_shutdown_scsi_cleanup(struct snic *snic)
+{
+       SNIC_HOST_INFO(snic->shost, "Shutdown time SCSI Cleanup.\n");
+
+       snic_scsi_cleanup(snic, SCSI_NO_TAG);
+} /* end of snic_shutdown_scsi_cleanup */
+
+/*
+ * snic_internal_abort_io
+ * called by : snic_tgt_scsi_abort_io
+ */
+static int
+snic_internal_abort_io(struct snic *snic, struct scsi_cmnd *sc, int tmf)
+{
+       struct snic_req_info *rqi = NULL;
+       spinlock_t *io_lock = NULL;
+       unsigned long flags;
+       u32 sv_state = 0;
+       int ret = 0;
+
+       io_lock = snic_io_lock_hash(snic, sc);
+       spin_lock_irqsave(io_lock, flags);
+       rqi = (struct snic_req_info *) CMD_SP(sc);
+       if (!rqi)
+               goto skip_internal_abts;
+
+       if (CMD_STATE(sc) == SNIC_IOREQ_ABTS_PENDING)
+               goto skip_internal_abts;
+
+       if ((CMD_FLAGS(sc) & SNIC_DEVICE_RESET) &&
+               (!(CMD_FLAGS(sc) & SNIC_DEV_RST_ISSUED))) {
+
+               SNIC_SCSI_DBG(snic->shost,
+                             "internal_abts: dev rst not pending sc 0x%p\n",
+                             sc);
+
+               goto skip_internal_abts;
+       }
+
+
+       if (!(CMD_FLAGS(sc) & SNIC_IO_ISSUED)) {
+               SNIC_SCSI_DBG(snic->shost,
+                       "internal_abts: IO not yet issued sc 0x%p tag 0x%x flags 0x%llx state %d\n",
+                       sc, snic_cmd_tag(sc), CMD_FLAGS(sc), CMD_STATE(sc));
+
+               goto skip_internal_abts;
+       }
+
+       sv_state = CMD_STATE(sc);
+       CMD_STATE(sc) = SNIC_IOREQ_ABTS_PENDING;
+       CMD_ABTS_STATUS(sc) = SNIC_INVALID_CODE;
+       CMD_FLAGS(sc) |= SNIC_IO_INTERNAL_TERM_PENDING;
+
+       if (CMD_FLAGS(sc) & SNIC_DEVICE_RESET) {
+               /* stats */
+               rqi->tm_tag = SNIC_TAG_DEV_RST;
+               SNIC_SCSI_DBG(snic->shost, "internal_abts:dev rst sc %p\n", sc);
+       }
+
+       SNIC_SCSI_DBG(snic->shost, "internal_abts: Issuing abts tag %x\n",
+                     snic_cmd_tag(sc));
+       SNIC_BUG_ON(rqi->abts_done);
+       spin_unlock_irqrestore(io_lock, flags);
+
+       ret = snic_queue_abort_req(snic, rqi, sc, tmf);
+       if (ret) {
+               SNIC_HOST_ERR(snic->shost,
+                             "internal_abts: Tag = %x , Failed w/ err = %d\n",
+                             snic_cmd_tag(sc), ret);
+
+               spin_lock_irqsave(io_lock, flags);
+
+               if (CMD_STATE(sc) == SNIC_IOREQ_ABTS_PENDING)
+                       CMD_STATE(sc) = sv_state;
+
+               goto skip_internal_abts;
+       }
+
+       spin_lock_irqsave(io_lock, flags);
+       if (CMD_FLAGS(sc) & SNIC_DEVICE_RESET)
+               CMD_FLAGS(sc) |= SNIC_DEV_RST_TERM_ISSUED;
+       else
+               CMD_FLAGS(sc) |= SNIC_IO_INTERNAL_TERM_ISSUED;
+
+       ret = SUCCESS;
+
+skip_internal_abts:
+       SNIC_BUG_ON(!spin_is_locked(io_lock));
+       spin_unlock_irqrestore(io_lock, flags);
+
+       return ret;
+} /* end of snic_internal_abort_io */
+
+/*
+ * snic_tgt_scsi_abort_io : called by snic_tgt_del
+ */
+int
+snic_tgt_scsi_abort_io(struct snic_tgt *tgt)
+{
+       struct snic *snic = NULL;
+       struct scsi_cmnd *sc = NULL;
+       struct snic_tgt *sc_tgt = NULL;
+       spinlock_t *io_lock = NULL;
+       unsigned long flags;
+       int ret = 0, tag, abt_cnt = 0, tmf = 0;
+
+       if (!tgt)
+               return -1;
+
+       snic = shost_priv(snic_tgt_to_shost(tgt));
+       SNIC_SCSI_DBG(snic->shost, "tgt_abt_io: Cleaning Pending IOs.\n");
+
+       if (tgt->tdata.typ == SNIC_TGT_DAS)
+               tmf = SNIC_ITMF_ABTS_TASK;
+       else
+               tmf = SNIC_ITMF_ABTS_TASK_TERM;
+
+       for (tag = 0; tag < snic->max_tag_id; tag++) {
+               io_lock = snic_io_lock_tag(snic, tag);
+
+               spin_lock_irqsave(io_lock, flags);
+               sc = scsi_host_find_tag(snic->shost, tag);
+               if (!sc) {
+                       spin_unlock_irqrestore(io_lock, flags);
+
+                       continue;
+               }
+
+               sc_tgt = starget_to_tgt(scsi_target(sc->device));
+               if (sc_tgt != tgt) {
+                       spin_unlock_irqrestore(io_lock, flags);
+
+                       continue;
+               }
+               spin_unlock_irqrestore(io_lock, flags);
+
+               ret = snic_internal_abort_io(snic, sc, tmf);
+               if (ret < 0) {
+                       SNIC_HOST_ERR(snic->shost,
+                                     "tgt_abt_io: Tag %x, Failed w err = %d\n",
+                                     tag, ret);
+
+                       continue;
+               }
+
+               if (ret == SUCCESS)
+                       abt_cnt++;
+       }
+
+       SNIC_SCSI_DBG(snic->shost, "tgt_abt_io: abt_cnt = %d\n", abt_cnt);
+
+       return 0;
+} /* end of snic_tgt_scsi_abort_io */
diff --git a/drivers/scsi/snic/snic_stats.h b/drivers/scsi/snic/snic_stats.h
new file mode 100644 (file)
index 0000000..11e6148
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2014 Cisco Systems, Inc.  All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * 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 __SNIC_STATS_H
+#define __SNIC_STATS_H
+
+struct snic_io_stats {
+       atomic64_t active;              /* Active IOs */
+       atomic64_t max_active;          /* Max # active IOs */
+       atomic64_t max_sgl;             /* Max # SGLs for any IO */
+       atomic64_t max_time;            /* Max time to process IO */
+       atomic64_t max_qtime;           /* Max time to Queue the IO */
+       atomic64_t max_cmpl_time;       /* Max time to complete the IO */
+       atomic64_t sgl_cnt[SNIC_MAX_SG_DESC_CNT]; /* SGL Counters */
+       atomic64_t max_io_sz;           /* Max IO Size */
+       atomic64_t compl;               /* IO Completions */
+       atomic64_t fail;                /* IO Failures */
+       atomic64_t req_null;            /* req or req info is NULL */
+       atomic64_t alloc_fail;          /* Alloc Failures */
+       atomic64_t sc_null;
+       atomic64_t io_not_found;        /* IO Not Found */
+       atomic64_t num_ios;             /* Number of IOs */
+};
+
+struct snic_abort_stats {
+       atomic64_t num;         /* Abort counter */
+       atomic64_t fail;        /* Abort Failure Counter */
+       atomic64_t drv_tmo;     /* Abort Driver Timeouts */
+       atomic64_t fw_tmo;      /* Abort Firmware Timeouts */
+       atomic64_t io_not_found;/* Abort IO Not Found */
+};
+
+struct snic_reset_stats {
+       atomic64_t dev_resets;          /* Device Reset Counter */
+       atomic64_t dev_reset_fail;      /* Device Reset Failures */
+       atomic64_t dev_reset_aborts;    /* Device Reset Aborts */
+       atomic64_t dev_reset_tmo;       /* Device Reset Timeout */
+       atomic64_t dev_reset_terms;     /* Device Reset terminate */
+       atomic64_t hba_resets;          /* hba/firmware resets */
+       atomic64_t hba_reset_cmpl;      /* hba/firmware reset completions */
+       atomic64_t hba_reset_fail;      /* hba/firmware failures */
+       atomic64_t snic_resets;         /* snic resets */
+       atomic64_t snic_reset_compl;    /* snic reset completions */
+       atomic64_t snic_reset_fail;     /* snic reset failures */
+};
+
+struct snic_fw_stats {
+       atomic64_t actv_reqs;           /* Active Requests */
+       atomic64_t max_actv_reqs;       /* Max Active Requests */
+       atomic64_t out_of_res;          /* Firmware Out Of Resources */
+       atomic64_t io_errs;             /* Firmware IO Firmware Errors */
+       atomic64_t scsi_errs;           /* Target hits check condition */
+};
+
+struct snic_misc_stats {
+       u64     last_isr_time;
+       u64     last_ack_time;
+       atomic64_t isr_cnt;
+       atomic64_t max_cq_ents;         /* Max CQ Entries */
+       atomic64_t data_cnt_mismat;     /* Data Count Mismatch */
+       atomic64_t io_tmo;
+       atomic64_t io_aborted;
+       atomic64_t sgl_inval;           /* SGL Invalid */
+       atomic64_t abts_wq_alloc_fail;  /* Abort Path WQ desc alloc failure */
+       atomic64_t devrst_wq_alloc_fail;/* Device Reset - WQ desc alloc fail */
+       atomic64_t wq_alloc_fail;       /* IO WQ desc alloc failure */
+       atomic64_t no_icmnd_itmf_cmpls;
+       atomic64_t io_under_run;
+       atomic64_t qfull;
+       atomic64_t tgt_not_rdy;
+};
+
+struct snic_stats {
+       struct snic_io_stats io;
+       struct snic_abort_stats abts;
+       struct snic_reset_stats reset;
+       struct snic_fw_stats fw;
+       struct snic_misc_stats misc;
+       atomic64_t io_cmpl_skip;
+};
+
+int snic_stats_debugfs_init(struct snic *);
+void snic_stats_debugfs_remove(struct snic *);
+
+/* Auxillary function to update active IO counter */
+static inline void
+snic_stats_update_active_ios(struct snic_stats *s_stats)
+{
+       struct snic_io_stats *io = &s_stats->io;
+       u32 nr_active_ios;
+
+       nr_active_ios = atomic64_inc_return(&io->active);
+       if (atomic64_read(&io->max_active) < nr_active_ios)
+               atomic64_set(&io->max_active, nr_active_ios);
+
+       atomic64_inc(&io->num_ios);
+}
+
+/* Auxillary function to update IO completion counter */
+static inline void
+snic_stats_update_io_cmpl(struct snic_stats *s_stats)
+{
+       atomic64_dec(&s_stats->io.active);
+       if (unlikely(atomic64_read(&s_stats->io_cmpl_skip)))
+               atomic64_dec(&s_stats->io_cmpl_skip);
+       else
+               atomic64_inc(&s_stats->io.compl);
+}
+#endif /* __SNIC_STATS_H */
diff --git a/drivers/scsi/snic/snic_trc.c b/drivers/scsi/snic/snic_trc.c
new file mode 100644 (file)
index 0000000..28a40a7
--- /dev/null
@@ -0,0 +1,181 @@
+/*
+ * Copyright 2014 Cisco Systems, Inc.  All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * 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 <linux/mempool.h>
+#include <linux/errno.h>
+#include <linux/vmalloc.h>
+
+#include "snic_io.h"
+#include "snic.h"
+
+/*
+ * snic_get_trc_buf : Allocates a trace record and returns.
+ */
+struct snic_trc_data *
+snic_get_trc_buf(void)
+{
+       struct snic_trc *trc = &snic_glob->trc;
+       struct snic_trc_data *td = NULL;
+       unsigned long flags;
+
+       spin_lock_irqsave(&trc->lock, flags);
+       td = &trc->buf[trc->wr_idx];
+       trc->wr_idx++;
+
+       if (trc->wr_idx == trc->max_idx)
+               trc->wr_idx = 0;
+
+       if (trc->wr_idx != trc->rd_idx) {
+               spin_unlock_irqrestore(&trc->lock, flags);
+
+               goto end;
+       }
+
+       trc->rd_idx++;
+       if (trc->rd_idx == trc->max_idx)
+               trc->rd_idx = 0;
+
+       td->ts = 0;     /* Marker for checking the record, for complete data*/
+       spin_unlock_irqrestore(&trc->lock, flags);
+
+end:
+
+       return td;
+} /* end of snic_get_trc_buf */
+
+/*
+ * snic_fmt_trc_data : Formats trace data for printing.
+ */
+static int
+snic_fmt_trc_data(struct snic_trc_data *td, char *buf, int buf_sz)
+{
+       int len = 0;
+       struct timespec tmspec;
+
+       jiffies_to_timespec(td->ts, &tmspec);
+
+       len += snprintf(buf, buf_sz,
+                       "%lu.%10lu %-25s %3d %4x %16llx %16llx %16llx %16llx %16llx\n",
+                       tmspec.tv_sec,
+                       tmspec.tv_nsec,
+                       td->fn,
+                       td->hno,
+                       td->tag,
+                       td->data[0], td->data[1], td->data[2], td->data[3],
+                       td->data[4]);
+
+       return len;
+} /* end of snic_fmt_trc_data */
+
+/*
+ * snic_get_trc_data : Returns a formatted trace buffer.
+ */
+int
+snic_get_trc_data(char *buf, int buf_sz)
+{
+       struct snic_trc_data *td = NULL;
+       struct snic_trc *trc = &snic_glob->trc;
+       unsigned long flags;
+
+       spin_lock_irqsave(&trc->lock, flags);
+       if (trc->rd_idx == trc->wr_idx) {
+               spin_unlock_irqrestore(&trc->lock, flags);
+
+               return -1;
+       }
+       td = &trc->buf[trc->rd_idx];
+
+       if (td->ts == 0) {
+               /* write in progress. */
+               spin_unlock_irqrestore(&trc->lock, flags);
+
+               return -1;
+       }
+
+       trc->rd_idx++;
+       if (trc->rd_idx == trc->max_idx)
+               trc->rd_idx = 0;
+       spin_unlock_irqrestore(&trc->lock, flags);
+
+       return snic_fmt_trc_data(td, buf, buf_sz);
+} /* end of snic_get_trc_data */
+
+/*
+ * snic_trc_init() : Configures Trace Functionality for snic.
+ */
+int
+snic_trc_init(void)
+{
+       struct snic_trc *trc = &snic_glob->trc;
+       void *tbuf = NULL;
+       int tbuf_sz = 0, ret;
+
+       tbuf_sz = (snic_trace_max_pages * PAGE_SIZE);
+       tbuf = vmalloc(tbuf_sz);
+       if (!tbuf) {
+               SNIC_ERR("Failed to Allocate Trace Buffer Size. %d\n", tbuf_sz);
+               SNIC_ERR("Trace Facility not enabled.\n");
+               ret = -ENOMEM;
+
+               return ret;
+       }
+
+       memset(tbuf, 0, tbuf_sz);
+       trc->buf = (struct snic_trc_data *) tbuf;
+       spin_lock_init(&trc->lock);
+
+       ret = snic_trc_debugfs_init();
+       if (ret) {
+               SNIC_ERR("Failed to create Debugfs Files.\n");
+
+               goto error;
+       }
+
+       trc->max_idx = (tbuf_sz / SNIC_TRC_ENTRY_SZ);
+       trc->rd_idx = trc->wr_idx = 0;
+       trc->enable = 1;
+       SNIC_INFO("Trace Facility Enabled.\n Trace Buffer SZ %lu Pages.\n",
+                 tbuf_sz / PAGE_SIZE);
+       ret = 0;
+
+       return ret;
+
+error:
+       snic_trc_free();
+
+       return ret;
+} /* end of snic_trc_init */
+
+/*
+ * snic_trc_free : Releases the trace buffer and disables the tracing.
+ */
+void
+snic_trc_free(void)
+{
+       struct snic_trc *trc = &snic_glob->trc;
+
+       trc->enable = 0;
+       snic_trc_debugfs_term();
+
+       if (trc->buf) {
+               vfree(trc->buf);
+               trc->buf = NULL;
+       }
+
+       SNIC_INFO("Trace Facility Disabled.\n");
+} /* end of snic_trc_free */
diff --git a/drivers/scsi/snic/snic_trc.h b/drivers/scsi/snic/snic_trc.h
new file mode 100644 (file)
index 0000000..427faee
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ * Copyright 2014 Cisco Systems, Inc.  All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * 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 __SNIC_TRC_H
+#define __SNIC_TRC_H
+
+#ifdef CONFIG_SCSI_SNIC_DEBUG_FS
+
+extern ssize_t simple_read_from_buffer(void __user *to,
+                                       size_t count,
+                                       loff_t *ppos,
+                                       const void *from,
+                                       size_t available);
+
+extern unsigned int snic_trace_max_pages;
+
+/* Global Data structure for trace to manage trace functionality */
+struct snic_trc_data {
+       u64     ts;             /* Time Stamp */
+       char    *fn;            /* Ptr to Function Name */
+       u32     hno;            /* SCSI Host ID */
+       u32     tag;            /* Command Tag */
+       u64 data[5];
+} __attribute__((__packed__));
+
+#define SNIC_TRC_ENTRY_SZ  64  /* in Bytes */
+
+struct snic_trc {
+       spinlock_t lock;
+       struct snic_trc_data *buf;      /* Trace Buffer */
+       u32     max_idx;                /* Max Index into trace buffer */
+       u32     rd_idx;
+       u32     wr_idx;
+       u32     enable;                 /* Control Variable for Tracing */
+
+       struct dentry *trc_enable;      /* debugfs file object */
+       struct dentry *trc_file;
+};
+
+int snic_trc_init(void);
+void snic_trc_free(void);
+int snic_trc_debugfs_init(void);
+void snic_trc_debugfs_term(void);
+struct snic_trc_data *snic_get_trc_buf(void);
+int snic_get_trc_data(char *buf, int buf_sz);
+
+int snic_debugfs_init(void);
+void snic_debugfs_term(void);
+
+static inline void
+snic_trace(char *fn, u16 hno, u32 tag, u64 d1, u64 d2, u64 d3, u64 d4, u64 d5)
+{
+       struct snic_trc_data *tr_rec = snic_get_trc_buf();
+
+       if (!tr_rec)
+               return;
+
+       tr_rec->fn = (char *)fn;
+       tr_rec->hno = hno;
+       tr_rec->tag = tag;
+       tr_rec->data[0] = d1;
+       tr_rec->data[1] = d2;
+       tr_rec->data[2] = d3;
+       tr_rec->data[3] = d4;
+       tr_rec->data[4] = d5;
+       tr_rec->ts = jiffies; /* Update time stamp at last */
+}
+
+#define SNIC_TRC(_hno, _tag, d1, d2, d3, d4, d5)                       \
+       do {                                                            \
+               if (unlikely(snic_glob->trc.enable))                    \
+                       snic_trace((char *)__func__,                    \
+                                  (u16)(_hno),                         \
+                                  (u32)(_tag),                         \
+                                  (u64)(d1),                           \
+                                  (u64)(d2),                           \
+                                  (u64)(d3),                           \
+                                  (u64)(d4),                           \
+                                  (u64)(d5));                          \
+       } while (0)
+#else
+
+#define SNIC_TRC(_hno, _tag, d1, d2, d3, d4, d5)       \
+       do {                                            \
+               if (unlikely(snic_log_level & 0x2))     \
+                       SNIC_DBG("SnicTrace: %s %2u %2u %llx %llx %llx %llx %llx", \
+                                (char *)__func__,      \
+                                (u16)(_hno),           \
+                                (u32)(_tag),           \
+                                (u64)(d1),             \
+                                (u64)(d2),             \
+                                (u64)(d3),             \
+                                (u64)(d4),             \
+                                (u64)(d5));            \
+       } while (0)
+#endif /* end of CONFIG_SCSI_SNIC_DEBUG_FS */
+
+#define SNIC_TRC_CMD(sc)       \
+       ((u64)sc->cmnd[0] << 56 | (u64)sc->cmnd[7] << 40 |      \
+        (u64)sc->cmnd[8] << 32 | (u64)sc->cmnd[2] << 24 |      \
+        (u64)sc->cmnd[3] << 16 | (u64)sc->cmnd[4] << 8 |       \
+        (u64)sc->cmnd[5])
+
+#define SNIC_TRC_CMD_STATE_FLAGS(sc)   \
+       ((u64) CMD_FLAGS(sc) << 32 | CMD_STATE(sc))
+
+#endif /* end of __SNIC_TRC_H */
diff --git a/drivers/scsi/snic/vnic_cq.c b/drivers/scsi/snic/vnic_cq.c
new file mode 100644 (file)
index 0000000..4c8e64e
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2014 Cisco Systems, Inc.  All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * 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/errno.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include "vnic_dev.h"
+#include "vnic_cq.h"
+
+void svnic_cq_free(struct vnic_cq *cq)
+{
+       svnic_dev_free_desc_ring(cq->vdev, &cq->ring);
+
+       cq->ctrl = NULL;
+}
+
+int svnic_cq_alloc(struct vnic_dev *vdev, struct vnic_cq *cq,
+       unsigned int index, unsigned int desc_count, unsigned int desc_size)
+{
+       int err;
+
+       cq->index = index;
+       cq->vdev = vdev;
+
+       cq->ctrl = svnic_dev_get_res(vdev, RES_TYPE_CQ, index);
+       if (!cq->ctrl) {
+               pr_err("Failed to hook CQ[%d] resource\n", index);
+
+               return -EINVAL;
+       }
+
+       err = svnic_dev_alloc_desc_ring(vdev, &cq->ring, desc_count, desc_size);
+       if (err)
+               return err;
+
+       return 0;
+}
+
+void svnic_cq_init(struct vnic_cq *cq, unsigned int flow_control_enable,
+       unsigned int color_enable, unsigned int cq_head, unsigned int cq_tail,
+       unsigned int cq_tail_color, unsigned int interrupt_enable,
+       unsigned int cq_entry_enable, unsigned int cq_message_enable,
+       unsigned int interrupt_offset, u64 cq_message_addr)
+{
+       u64 paddr;
+
+       paddr = (u64)cq->ring.base_addr | VNIC_PADDR_TARGET;
+       writeq(paddr, &cq->ctrl->ring_base);
+       iowrite32(cq->ring.desc_count, &cq->ctrl->ring_size);
+       iowrite32(flow_control_enable, &cq->ctrl->flow_control_enable);
+       iowrite32(color_enable, &cq->ctrl->color_enable);
+       iowrite32(cq_head, &cq->ctrl->cq_head);
+       iowrite32(cq_tail, &cq->ctrl->cq_tail);
+       iowrite32(cq_tail_color, &cq->ctrl->cq_tail_color);
+       iowrite32(interrupt_enable, &cq->ctrl->interrupt_enable);
+       iowrite32(cq_entry_enable, &cq->ctrl->cq_entry_enable);
+       iowrite32(cq_message_enable, &cq->ctrl->cq_message_enable);
+       iowrite32(interrupt_offset, &cq->ctrl->interrupt_offset);
+       writeq(cq_message_addr, &cq->ctrl->cq_message_addr);
+}
+
+void svnic_cq_clean(struct vnic_cq *cq)
+{
+       cq->to_clean = 0;
+       cq->last_color = 0;
+
+       iowrite32(0, &cq->ctrl->cq_head);
+       iowrite32(0, &cq->ctrl->cq_tail);
+       iowrite32(1, &cq->ctrl->cq_tail_color);
+
+       svnic_dev_clear_desc_ring(&cq->ring);
+}
diff --git a/drivers/scsi/snic/vnic_cq.h b/drivers/scsi/snic/vnic_cq.h
new file mode 100644 (file)
index 0000000..6e651c3
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2014 Cisco Systems, Inc.  All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * 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 _VNIC_CQ_H_
+#define _VNIC_CQ_H_
+
+#include "cq_desc.h"
+#include "vnic_dev.h"
+
+/* Completion queue control */
+struct vnic_cq_ctrl {
+       u64 ring_base;                  /* 0x00 */
+       u32 ring_size;                  /* 0x08 */
+       u32 pad0;
+       u32 flow_control_enable;        /* 0x10 */
+       u32 pad1;
+       u32 color_enable;               /* 0x18 */
+       u32 pad2;
+       u32 cq_head;                    /* 0x20 */
+       u32 pad3;
+       u32 cq_tail;                    /* 0x28 */
+       u32 pad4;
+       u32 cq_tail_color;              /* 0x30 */
+       u32 pad5;
+       u32 interrupt_enable;           /* 0x38 */
+       u32 pad6;
+       u32 cq_entry_enable;            /* 0x40 */
+       u32 pad7;
+       u32 cq_message_enable;          /* 0x48 */
+       u32 pad8;
+       u32 interrupt_offset;           /* 0x50 */
+       u32 pad9;
+       u64 cq_message_addr;            /* 0x58 */
+       u32 pad10;
+};
+
+struct vnic_cq {
+       unsigned int index;
+       struct vnic_dev *vdev;
+       struct vnic_cq_ctrl __iomem *ctrl;      /* memory-mapped */
+       struct vnic_dev_ring ring;
+       unsigned int to_clean;
+       unsigned int last_color;
+};
+
+static inline unsigned int svnic_cq_service(struct vnic_cq *cq,
+       unsigned int work_to_do,
+       int (*q_service)(struct vnic_dev *vdev, struct cq_desc *cq_desc,
+       u8 type, u16 q_number, u16 completed_index, void *opaque),
+       void *opaque)
+{
+       struct cq_desc *cq_desc;
+       unsigned int work_done = 0;
+       u16 q_number, completed_index;
+       u8 type, color;
+
+       cq_desc = (struct cq_desc *)((u8 *)cq->ring.descs +
+               cq->ring.desc_size * cq->to_clean);
+       cq_desc_dec(cq_desc, &type, &color,
+               &q_number, &completed_index);
+
+       while (color != cq->last_color) {
+
+               if ((*q_service)(cq->vdev, cq_desc, type,
+                       q_number, completed_index, opaque))
+                       break;
+
+               cq->to_clean++;
+               if (cq->to_clean == cq->ring.desc_count) {
+                       cq->to_clean = 0;
+                       cq->last_color = cq->last_color ? 0 : 1;
+               }
+
+               cq_desc = (struct cq_desc *)((u8 *)cq->ring.descs +
+                       cq->ring.desc_size * cq->to_clean);
+               cq_desc_dec(cq_desc, &type, &color,
+                       &q_number, &completed_index);
+
+               work_done++;
+               if (work_done >= work_to_do)
+                       break;
+       }
+
+       return work_done;
+}
+
+void svnic_cq_free(struct vnic_cq *cq);
+int svnic_cq_alloc(struct vnic_dev *vdev, struct vnic_cq *cq,
+       unsigned int index, unsigned int desc_count, unsigned int desc_size);
+void svnic_cq_init(struct vnic_cq *cq, unsigned int flow_control_enable,
+       unsigned int color_enable, unsigned int cq_head, unsigned int cq_tail,
+       unsigned int cq_tail_color, unsigned int interrupt_enable,
+       unsigned int cq_entry_enable, unsigned int message_enable,
+       unsigned int interrupt_offset, u64 message_addr);
+void svnic_cq_clean(struct vnic_cq *cq);
+#endif /* _VNIC_CQ_H_ */
diff --git a/drivers/scsi/snic/vnic_cq_fw.h b/drivers/scsi/snic/vnic_cq_fw.h
new file mode 100644 (file)
index 0000000..c2d1bbd
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2014 Cisco Systems, Inc.  All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * 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 _VNIC_CQ_FW_H_
+#define _VNIC_CQ_FW_H_
+
+#include "snic_fwint.h"
+
+static inline unsigned int
+vnic_cq_fw_service(struct vnic_cq *cq,
+                  int (*q_service)(struct vnic_dev *vdev,
+                                   unsigned int index,
+                                   struct snic_fw_req *desc),
+                  unsigned int work_to_do)
+
+{
+       struct snic_fw_req *desc;
+       unsigned int work_done = 0;
+       u8 color;
+
+       desc = (struct snic_fw_req *)((u8 *)cq->ring.descs +
+               cq->ring.desc_size * cq->to_clean);
+       snic_color_dec(desc, &color);
+
+       while (color != cq->last_color) {
+
+               if ((*q_service)(cq->vdev, cq->index, desc))
+                       break;
+
+               cq->to_clean++;
+               if (cq->to_clean == cq->ring.desc_count) {
+                       cq->to_clean = 0;
+                       cq->last_color = cq->last_color ? 0 : 1;
+               }
+
+               desc = (struct snic_fw_req *)((u8 *)cq->ring.descs +
+                       cq->ring.desc_size * cq->to_clean);
+               snic_color_dec(desc, &color);
+
+               work_done++;
+               if (work_done >= work_to_do)
+                       break;
+       }
+
+       return work_done;
+}
+
+#endif /* _VNIC_CQ_FW_H_ */
diff --git a/drivers/scsi/snic/vnic_dev.c b/drivers/scsi/snic/vnic_dev.c
new file mode 100644 (file)
index 0000000..e0b5549
--- /dev/null
@@ -0,0 +1,748 @@
+/*
+ * Copyright 2014 Cisco Systems, Inc.  All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * 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/kernel.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/if_ether.h>
+#include <linux/slab.h>
+#include "vnic_resource.h"
+#include "vnic_devcmd.h"
+#include "vnic_dev.h"
+#include "vnic_stats.h"
+#include "vnic_wq.h"
+
+#define VNIC_DVCMD_TMO 10000   /* Devcmd Timeout value */
+#define VNIC_NOTIFY_INTR_MASK 0x0000ffff00000000ULL
+
+struct devcmd2_controller {
+       struct vnic_wq_ctrl __iomem *wq_ctrl;
+       struct vnic_dev_ring results_ring;
+       struct vnic_wq wq;
+       struct vnic_devcmd2 *cmd_ring;
+       struct devcmd2_result *result;
+       u16 next_result;
+       u16 result_size;
+       int color;
+};
+
+struct vnic_res {
+       void __iomem *vaddr;
+       unsigned int count;
+};
+
+struct vnic_dev {
+       void *priv;
+       struct pci_dev *pdev;
+       struct vnic_res res[RES_TYPE_MAX];
+       enum vnic_dev_intr_mode intr_mode;
+       struct vnic_devcmd __iomem *devcmd;
+       struct vnic_devcmd_notify *notify;
+       struct vnic_devcmd_notify notify_copy;
+       dma_addr_t notify_pa;
+       u32 *linkstatus;
+       dma_addr_t linkstatus_pa;
+       struct vnic_stats *stats;
+       dma_addr_t stats_pa;
+       struct vnic_devcmd_fw_info *fw_info;
+       dma_addr_t fw_info_pa;
+       u64 args[VNIC_DEVCMD_NARGS];
+       struct devcmd2_controller *devcmd2;
+
+       int (*devcmd_rtn)(struct vnic_dev *vdev, enum vnic_devcmd_cmd cmd,
+                         int wait);
+};
+
+#define VNIC_MAX_RES_HDR_SIZE \
+       (sizeof(struct vnic_resource_header) + \
+       sizeof(struct vnic_resource) * RES_TYPE_MAX)
+#define VNIC_RES_STRIDE        128
+
+void *svnic_dev_priv(struct vnic_dev *vdev)
+{
+       return vdev->priv;
+}
+
+static int vnic_dev_discover_res(struct vnic_dev *vdev,
+       struct vnic_dev_bar *bar, unsigned int num_bars)
+{
+       struct vnic_resource_header __iomem *rh;
+       struct vnic_resource __iomem *r;
+       u8 type;
+
+       if (num_bars == 0)
+               return -EINVAL;
+
+       if (bar->len < VNIC_MAX_RES_HDR_SIZE) {
+               pr_err("vNIC BAR0 res hdr length error\n");
+
+               return -EINVAL;
+       }
+
+       rh = bar->vaddr;
+       if (!rh) {
+               pr_err("vNIC BAR0 res hdr not mem-mapped\n");
+
+               return -EINVAL;
+       }
+
+       if (ioread32(&rh->magic) != VNIC_RES_MAGIC ||
+           ioread32(&rh->version) != VNIC_RES_VERSION) {
+               pr_err("vNIC BAR0 res magic/version error exp (%lx/%lx) curr (%x/%x)\n",
+                       VNIC_RES_MAGIC, VNIC_RES_VERSION,
+                       ioread32(&rh->magic), ioread32(&rh->version));
+
+               return -EINVAL;
+       }
+
+       r = (struct vnic_resource __iomem *)(rh + 1);
+
+       while ((type = ioread8(&r->type)) != RES_TYPE_EOL) {
+
+               u8 bar_num = ioread8(&r->bar);
+               u32 bar_offset = ioread32(&r->bar_offset);
+               u32 count = ioread32(&r->count);
+               u32 len;
+
+               r++;
+
+               if (bar_num >= num_bars)
+                       continue;
+
+               if (!bar[bar_num].len || !bar[bar_num].vaddr)
+                       continue;
+
+               switch (type) {
+               case RES_TYPE_WQ:
+               case RES_TYPE_RQ:
+               case RES_TYPE_CQ:
+               case RES_TYPE_INTR_CTRL:
+                       /* each count is stride bytes long */
+                       len = count * VNIC_RES_STRIDE;
+                       if (len + bar_offset > bar->len) {
+                               pr_err("vNIC BAR0 resource %d out-of-bounds, offset 0x%x + size 0x%x > bar len 0x%lx\n",
+                                       type, bar_offset,
+                                       len,
+                                       bar->len);
+
+                               return -EINVAL;
+                       }
+                       break;
+
+               case RES_TYPE_INTR_PBA_LEGACY:
+               case RES_TYPE_DEVCMD:
+               case RES_TYPE_DEVCMD2:
+                       len = count;
+                       break;
+
+               default:
+                       continue;
+               }
+
+               vdev->res[type].count = count;
+               vdev->res[type].vaddr = (char __iomem *)bar->vaddr + bar_offset;
+       }
+
+       return 0;
+}
+
+unsigned int svnic_dev_get_res_count(struct vnic_dev *vdev,
+       enum vnic_res_type type)
+{
+       return vdev->res[type].count;
+}
+
+void __iomem *svnic_dev_get_res(struct vnic_dev *vdev, enum vnic_res_type type,
+       unsigned int index)
+{
+       if (!vdev->res[type].vaddr)
+               return NULL;
+
+       switch (type) {
+       case RES_TYPE_WQ:
+       case RES_TYPE_RQ:
+       case RES_TYPE_CQ:
+       case RES_TYPE_INTR_CTRL:
+               return (char __iomem *)vdev->res[type].vaddr +
+                                       index * VNIC_RES_STRIDE;
+
+       default:
+               return (char __iomem *)vdev->res[type].vaddr;
+       }
+}
+
+unsigned int svnic_dev_desc_ring_size(struct vnic_dev_ring *ring,
+                                     unsigned int desc_count,
+                                     unsigned int desc_size)
+{
+       /* The base address of the desc rings must be 512 byte aligned.
+        * Descriptor count is aligned to groups of 32 descriptors.  A
+        * count of 0 means the maximum 4096 descriptors.  Descriptor
+        * size is aligned to 16 bytes.
+        */
+
+       unsigned int count_align = 32;
+       unsigned int desc_align = 16;
+
+       ring->base_align = 512;
+
+       if (desc_count == 0)
+               desc_count = 4096;
+
+       ring->desc_count = ALIGN(desc_count, count_align);
+
+       ring->desc_size = ALIGN(desc_size, desc_align);
+
+       ring->size = ring->desc_count * ring->desc_size;
+       ring->size_unaligned = ring->size + ring->base_align;
+
+       return ring->size_unaligned;
+}
+
+void svnic_dev_clear_desc_ring(struct vnic_dev_ring *ring)
+{
+       memset(ring->descs, 0, ring->size);
+}
+
+int svnic_dev_alloc_desc_ring(struct vnic_dev *vdev, struct vnic_dev_ring *ring,
+       unsigned int desc_count, unsigned int desc_size)
+{
+       svnic_dev_desc_ring_size(ring, desc_count, desc_size);
+
+       ring->descs_unaligned = pci_alloc_consistent(vdev->pdev,
+               ring->size_unaligned,
+               &ring->base_addr_unaligned);
+
+       if (!ring->descs_unaligned) {
+               pr_err("Failed to allocate ring (size=%d), aborting\n",
+                       (int)ring->size);
+
+               return -ENOMEM;
+       }
+
+       ring->base_addr = ALIGN(ring->base_addr_unaligned,
+               ring->base_align);
+       ring->descs = (u8 *)ring->descs_unaligned +
+               (ring->base_addr - ring->base_addr_unaligned);
+
+       svnic_dev_clear_desc_ring(ring);
+
+       ring->desc_avail = ring->desc_count - 1;
+
+       return 0;
+}
+
+void svnic_dev_free_desc_ring(struct vnic_dev *vdev, struct vnic_dev_ring *ring)
+{
+       if (ring->descs) {
+               pci_free_consistent(vdev->pdev,
+                       ring->size_unaligned,
+                       ring->descs_unaligned,
+                       ring->base_addr_unaligned);
+               ring->descs = NULL;
+       }
+}
+
+static int _svnic_dev_cmd2(struct vnic_dev *vdev, enum vnic_devcmd_cmd cmd,
+       int wait)
+{
+       struct devcmd2_controller *dc2c = vdev->devcmd2;
+       struct devcmd2_result *result = dc2c->result + dc2c->next_result;
+       unsigned int i;
+       int delay;
+       int err;
+       u32 posted;
+       u32 new_posted;
+
+       posted = ioread32(&dc2c->wq_ctrl->posted_index);
+
+       if (posted == 0xFFFFFFFF) { /* check for hardware gone  */
+               /* Hardware surprise removal: return error */
+               return -ENODEV;
+       }
+
+       new_posted = (posted + 1) % DEVCMD2_RING_SIZE;
+       dc2c->cmd_ring[posted].cmd = cmd;
+       dc2c->cmd_ring[posted].flags = 0;
+
+       if ((_CMD_FLAGS(cmd) & _CMD_FLAGS_NOWAIT))
+               dc2c->cmd_ring[posted].flags |= DEVCMD2_FNORESULT;
+
+       if (_CMD_DIR(cmd) & _CMD_DIR_WRITE) {
+               for (i = 0; i < VNIC_DEVCMD_NARGS; i++)
+                       dc2c->cmd_ring[posted].args[i] = vdev->args[i];
+       }
+       /* Adding write memory barrier prevents compiler and/or CPU
+        * reordering, thus avoiding descriptor posting before
+        * descriptor is initialized. Otherwise, hardware can read
+        * stale descriptor fields.
+        */
+       wmb();
+       iowrite32(new_posted, &dc2c->wq_ctrl->posted_index);
+
+       if (dc2c->cmd_ring[posted].flags & DEVCMD2_FNORESULT)
+               return 0;
+
+       for (delay = 0; delay < wait; delay++) {
+               udelay(100);
+               if (result->color == dc2c->color) {
+                       dc2c->next_result++;
+                       if (dc2c->next_result == dc2c->result_size) {
+                               dc2c->next_result = 0;
+                               dc2c->color = dc2c->color ? 0 : 1;
+                       }
+                       if (result->error) {
+                               err = (int) result->error;
+                               if (err != ERR_ECMDUNKNOWN ||
+                                   cmd != CMD_CAPABILITY)
+                                       pr_err("Error %d devcmd %d\n",
+                                               err, _CMD_N(cmd));
+
+                               return err;
+                       }
+                       if (_CMD_DIR(cmd) & _CMD_DIR_READ) {
+                               /*
+                                * Adding the rmb() prevents the compiler
+                                * and/or CPU from reordering the reads which
+                                * would potentially result in reading stale
+                                * values.
+                                */
+                               rmb();
+                               for (i = 0; i < VNIC_DEVCMD_NARGS; i++)
+                                       vdev->args[i] = result->results[i];
+                       }
+
+                       return 0;
+               }
+       }
+
+       pr_err("Timed out devcmd %d\n", _CMD_N(cmd));
+
+       return -ETIMEDOUT;
+}
+
+static int svnic_dev_init_devcmd2(struct vnic_dev *vdev)
+{
+       struct devcmd2_controller *dc2c = NULL;
+       unsigned int fetch_idx;
+       int ret;
+       void __iomem *p;
+
+       if (vdev->devcmd2)
+               return 0;
+
+       p = svnic_dev_get_res(vdev, RES_TYPE_DEVCMD2, 0);
+       if (!p)
+               return -ENODEV;
+
+       dc2c = kzalloc(sizeof(*dc2c), GFP_ATOMIC);
+       if (!dc2c)
+               return -ENOMEM;
+
+       vdev->devcmd2 = dc2c;
+
+       dc2c->color = 1;
+       dc2c->result_size = DEVCMD2_RING_SIZE;
+
+       ret  = vnic_wq_devcmd2_alloc(vdev,
+                                    &dc2c->wq,
+                                    DEVCMD2_RING_SIZE,
+                                    DEVCMD2_DESC_SIZE);
+       if (ret)
+               goto err_free_devcmd2;
+
+       fetch_idx = ioread32(&dc2c->wq.ctrl->fetch_index);
+       if (fetch_idx == 0xFFFFFFFF) { /* check for hardware gone  */
+               /* Hardware surprise removal: reset fetch_index */
+               fetch_idx = 0;
+       }
+
+       /*
+        * Don't change fetch_index ever and
+        * set posted_index same as fetch_index
+        * when setting up the WQ for devcmd2.
+        */
+       vnic_wq_init_start(&dc2c->wq, 0, fetch_idx, fetch_idx, 0, 0);
+       svnic_wq_enable(&dc2c->wq);
+       ret = svnic_dev_alloc_desc_ring(vdev,
+                                       &dc2c->results_ring,
+                                       DEVCMD2_RING_SIZE,
+                                       DEVCMD2_DESC_SIZE);
+       if (ret)
+               goto err_free_wq;
+
+       dc2c->result = (struct devcmd2_result *) dc2c->results_ring.descs;
+       dc2c->cmd_ring = (struct vnic_devcmd2 *) dc2c->wq.ring.descs;
+       dc2c->wq_ctrl = dc2c->wq.ctrl;
+       vdev->args[0] = (u64) dc2c->results_ring.base_addr | VNIC_PADDR_TARGET;
+       vdev->args[1] = DEVCMD2_RING_SIZE;
+
+       ret = _svnic_dev_cmd2(vdev, CMD_INITIALIZE_DEVCMD2, VNIC_DVCMD_TMO);
+       if (ret < 0)
+               goto err_free_desc_ring;
+
+       vdev->devcmd_rtn = &_svnic_dev_cmd2;
+       pr_info("DEVCMD2 Initialized.\n");
+
+       return ret;
+
+err_free_desc_ring:
+       svnic_dev_free_desc_ring(vdev, &dc2c->results_ring);
+
+err_free_wq:
+       svnic_wq_disable(&dc2c->wq);
+       svnic_wq_free(&dc2c->wq);
+
+err_free_devcmd2:
+       kfree(dc2c);
+       vdev->devcmd2 = NULL;
+
+       return ret;
+} /* end of svnic_dev_init_devcmd2 */
+
+static void vnic_dev_deinit_devcmd2(struct vnic_dev *vdev)
+{
+       struct devcmd2_controller *dc2c = vdev->devcmd2;
+
+       vdev->devcmd2 = NULL;
+       vdev->devcmd_rtn = NULL;
+
+       svnic_dev_free_desc_ring(vdev, &dc2c->results_ring);
+       svnic_wq_disable(&dc2c->wq);
+       svnic_wq_free(&dc2c->wq);
+       kfree(dc2c);
+}
+
+int svnic_dev_cmd(struct vnic_dev *vdev, enum vnic_devcmd_cmd cmd,
+       u64 *a0, u64 *a1, int wait)
+{
+       int err;
+
+       memset(vdev->args, 0, sizeof(vdev->args));
+       vdev->args[0] = *a0;
+       vdev->args[1] = *a1;
+
+       err = (*vdev->devcmd_rtn)(vdev, cmd, wait);
+
+       *a0 = vdev->args[0];
+       *a1 = vdev->args[1];
+
+       return  err;
+}
+
+int svnic_dev_fw_info(struct vnic_dev *vdev,
+       struct vnic_devcmd_fw_info **fw_info)
+{
+       u64 a0, a1 = 0;
+       int wait = VNIC_DVCMD_TMO;
+       int err = 0;
+
+       if (!vdev->fw_info) {
+               vdev->fw_info = pci_alloc_consistent(vdev->pdev,
+                       sizeof(struct vnic_devcmd_fw_info),
+                       &vdev->fw_info_pa);
+               if (!vdev->fw_info)
+                       return -ENOMEM;
+
+               a0 = vdev->fw_info_pa;
+
+               /* only get fw_info once and cache it */
+               err = svnic_dev_cmd(vdev, CMD_MCPU_FW_INFO, &a0, &a1, wait);
+       }
+
+       *fw_info = vdev->fw_info;
+
+       return err;
+}
+
+int svnic_dev_spec(struct vnic_dev *vdev, unsigned int offset,
+       unsigned int size, void *value)
+{
+       u64 a0, a1;
+       int wait = VNIC_DVCMD_TMO;
+       int err;
+
+       a0 = offset;
+       a1 = size;
+
+       err = svnic_dev_cmd(vdev, CMD_DEV_SPEC, &a0, &a1, wait);
+
+       switch (size) {
+       case 1:
+               *(u8 *)value = (u8)a0;
+               break;
+       case 2:
+               *(u16 *)value = (u16)a0;
+               break;
+       case 4:
+               *(u32 *)value = (u32)a0;
+               break;
+       case 8:
+               *(u64 *)value = a0;
+               break;
+       default:
+               BUG();
+               break;
+       }
+
+       return err;
+}
+
+int svnic_dev_stats_clear(struct vnic_dev *vdev)
+{
+       u64 a0 = 0, a1 = 0;
+       int wait = VNIC_DVCMD_TMO;
+
+       return svnic_dev_cmd(vdev, CMD_STATS_CLEAR, &a0, &a1, wait);
+}
+
+int svnic_dev_stats_dump(struct vnic_dev *vdev, struct vnic_stats **stats)
+{
+       u64 a0, a1;
+       int wait = VNIC_DVCMD_TMO;
+
+       if (!vdev->stats) {
+               vdev->stats = pci_alloc_consistent(vdev->pdev,
+                       sizeof(struct vnic_stats), &vdev->stats_pa);
+               if (!vdev->stats)
+                       return -ENOMEM;
+       }
+
+       *stats = vdev->stats;
+       a0 = vdev->stats_pa;
+       a1 = sizeof(struct vnic_stats);
+
+       return svnic_dev_cmd(vdev, CMD_STATS_DUMP, &a0, &a1, wait);
+}
+
+int svnic_dev_close(struct vnic_dev *vdev)
+{
+       u64 a0 = 0, a1 = 0;
+       int wait = VNIC_DVCMD_TMO;
+
+       return svnic_dev_cmd(vdev, CMD_CLOSE, &a0, &a1, wait);
+}
+
+int svnic_dev_enable_wait(struct vnic_dev *vdev)
+{
+       u64 a0 = 0, a1 = 0;
+       int wait = VNIC_DVCMD_TMO;
+       int err = 0;
+
+       err = svnic_dev_cmd(vdev, CMD_ENABLE_WAIT, &a0, &a1, wait);
+       if (err == ERR_ECMDUNKNOWN)
+               return svnic_dev_cmd(vdev, CMD_ENABLE, &a0, &a1, wait);
+
+       return err;
+}
+
+int svnic_dev_disable(struct vnic_dev *vdev)
+{
+       u64 a0 = 0, a1 = 0;
+       int wait = VNIC_DVCMD_TMO;
+
+       return svnic_dev_cmd(vdev, CMD_DISABLE, &a0, &a1, wait);
+}
+
+int svnic_dev_open(struct vnic_dev *vdev, int arg)
+{
+       u64 a0 = (u32)arg, a1 = 0;
+       int wait = VNIC_DVCMD_TMO;
+
+       return svnic_dev_cmd(vdev, CMD_OPEN, &a0, &a1, wait);
+}
+
+int svnic_dev_open_done(struct vnic_dev *vdev, int *done)
+{
+       u64 a0 = 0, a1 = 0;
+       int wait = VNIC_DVCMD_TMO;
+       int err;
+
+       *done = 0;
+
+       err = svnic_dev_cmd(vdev, CMD_OPEN_STATUS, &a0, &a1, wait);
+       if (err)
+               return err;
+
+       *done = (a0 == 0);
+
+       return 0;
+}
+
+int svnic_dev_notify_set(struct vnic_dev *vdev, u16 intr)
+{
+       u64 a0, a1;
+       int wait = VNIC_DVCMD_TMO;
+
+       if (!vdev->notify) {
+               vdev->notify = pci_alloc_consistent(vdev->pdev,
+                       sizeof(struct vnic_devcmd_notify),
+                       &vdev->notify_pa);
+               if (!vdev->notify)
+                       return -ENOMEM;
+       }
+
+       a0 = vdev->notify_pa;
+       a1 = ((u64)intr << 32) & VNIC_NOTIFY_INTR_MASK;
+       a1 += sizeof(struct vnic_devcmd_notify);
+
+       return svnic_dev_cmd(vdev, CMD_NOTIFY, &a0, &a1, wait);
+}
+
+void svnic_dev_notify_unset(struct vnic_dev *vdev)
+{
+       u64 a0, a1;
+       int wait = VNIC_DVCMD_TMO;
+
+       a0 = 0;  /* paddr = 0 to unset notify buffer */
+       a1 = VNIC_NOTIFY_INTR_MASK; /* intr num = -1 to unreg for intr */
+       a1 += sizeof(struct vnic_devcmd_notify);
+
+       svnic_dev_cmd(vdev, CMD_NOTIFY, &a0, &a1, wait);
+}
+
+static int vnic_dev_notify_ready(struct vnic_dev *vdev)
+{
+       u32 *words;
+       unsigned int nwords = sizeof(struct vnic_devcmd_notify) / 4;
+       unsigned int i;
+       u32 csum;
+
+       if (!vdev->notify)
+               return 0;
+
+       do {
+               csum = 0;
+               memcpy(&vdev->notify_copy, vdev->notify,
+                       sizeof(struct vnic_devcmd_notify));
+               words = (u32 *)&vdev->notify_copy;
+               for (i = 1; i < nwords; i++)
+                       csum += words[i];
+       } while (csum != words[0]);
+
+       return 1;
+}
+
+int svnic_dev_init(struct vnic_dev *vdev, int arg)
+{
+       u64 a0 = (u32)arg, a1 = 0;
+       int wait = VNIC_DVCMD_TMO;
+
+       return svnic_dev_cmd(vdev, CMD_INIT, &a0, &a1, wait);
+}
+
+int svnic_dev_link_status(struct vnic_dev *vdev)
+{
+       if (vdev->linkstatus)
+               return *vdev->linkstatus;
+
+       if (!vnic_dev_notify_ready(vdev))
+               return 0;
+
+       return vdev->notify_copy.link_state;
+}
+
+u32 svnic_dev_link_down_cnt(struct vnic_dev *vdev)
+{
+       if (!vnic_dev_notify_ready(vdev))
+               return 0;
+
+       return vdev->notify_copy.link_down_cnt;
+}
+
+void svnic_dev_set_intr_mode(struct vnic_dev *vdev,
+       enum vnic_dev_intr_mode intr_mode)
+{
+       vdev->intr_mode = intr_mode;
+}
+
+enum vnic_dev_intr_mode svnic_dev_get_intr_mode(struct vnic_dev *vdev)
+{
+       return vdev->intr_mode;
+}
+
+void svnic_dev_unregister(struct vnic_dev *vdev)
+{
+       if (vdev) {
+               if (vdev->notify)
+                       pci_free_consistent(vdev->pdev,
+                               sizeof(struct vnic_devcmd_notify),
+                               vdev->notify,
+                               vdev->notify_pa);
+               if (vdev->linkstatus)
+                       pci_free_consistent(vdev->pdev,
+                               sizeof(u32),
+                               vdev->linkstatus,
+                               vdev->linkstatus_pa);
+               if (vdev->stats)
+                       pci_free_consistent(vdev->pdev,
+                               sizeof(struct vnic_stats),
+                               vdev->stats, vdev->stats_pa);
+               if (vdev->fw_info)
+                       pci_free_consistent(vdev->pdev,
+                               sizeof(struct vnic_devcmd_fw_info),
+                               vdev->fw_info, vdev->fw_info_pa);
+               if (vdev->devcmd2)
+                       vnic_dev_deinit_devcmd2(vdev);
+               kfree(vdev);
+       }
+}
+
+struct vnic_dev *svnic_dev_alloc_discover(struct vnic_dev *vdev,
+                                         void *priv,
+                                         struct pci_dev *pdev,
+                                         struct vnic_dev_bar *bar,
+                                         unsigned int num_bars)
+{
+       if (!vdev) {
+               vdev = kzalloc(sizeof(struct vnic_dev), GFP_ATOMIC);
+               if (!vdev)
+                       return NULL;
+       }
+
+       vdev->priv = priv;
+       vdev->pdev = pdev;
+
+       if (vnic_dev_discover_res(vdev, bar, num_bars))
+               goto err_out;
+
+       return vdev;
+
+err_out:
+       svnic_dev_unregister(vdev);
+
+       return NULL;
+} /* end of svnic_dev_alloc_discover */
+
+/*
+ * fallback option is left to keep the interface common for other vnics.
+ */
+int svnic_dev_cmd_init(struct vnic_dev *vdev, int fallback)
+{
+       int err = -ENODEV;
+       void __iomem *p;
+
+       p = svnic_dev_get_res(vdev, RES_TYPE_DEVCMD2, 0);
+       if (p)
+               err = svnic_dev_init_devcmd2(vdev);
+       else
+               pr_err("DEVCMD2 resource not found.\n");
+
+       return err;
+} /* end of svnic_dev_cmd_init */
diff --git a/drivers/scsi/snic/vnic_dev.h b/drivers/scsi/snic/vnic_dev.h
new file mode 100644 (file)
index 0000000..e65726d
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2014 Cisco Systems, Inc.  All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * 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 _VNIC_DEV_H_
+#define _VNIC_DEV_H_
+
+#include "vnic_resource.h"
+#include "vnic_devcmd.h"
+
+#ifndef VNIC_PADDR_TARGET
+#define VNIC_PADDR_TARGET      0x0000000000000000ULL
+#endif
+
+#ifndef readq
+static inline u64 readq(void __iomem *reg)
+{
+       return ((u64)readl(reg + 0x4UL) << 32) | (u64)readl(reg);
+}
+
+static inline void writeq(u64 val, void __iomem *reg)
+{
+       writel(lower_32_bits(val), reg);
+       writel(upper_32_bits(val), reg + 0x4UL);
+}
+#endif
+
+enum vnic_dev_intr_mode {
+       VNIC_DEV_INTR_MODE_UNKNOWN,
+       VNIC_DEV_INTR_MODE_INTX,
+       VNIC_DEV_INTR_MODE_MSI,
+       VNIC_DEV_INTR_MODE_MSIX,
+};
+
+struct vnic_dev_bar {
+       void __iomem *vaddr;
+       dma_addr_t bus_addr;
+       unsigned long len;
+};
+
+struct vnic_dev_ring {
+       void *descs;
+       size_t size;
+       dma_addr_t base_addr;
+       size_t base_align;
+       void *descs_unaligned;
+       size_t size_unaligned;
+       dma_addr_t base_addr_unaligned;
+       unsigned int desc_size;
+       unsigned int desc_count;
+       unsigned int desc_avail;
+};
+
+struct vnic_dev;
+struct vnic_stats;
+
+void *svnic_dev_priv(struct vnic_dev *vdev);
+unsigned int svnic_dev_get_res_count(struct vnic_dev *vdev,
+                                   enum vnic_res_type type);
+void __iomem *svnic_dev_get_res(struct vnic_dev *vdev, enum vnic_res_type type,
+                              unsigned int index);
+unsigned int svnic_dev_desc_ring_size(struct vnic_dev_ring *ring,
+                                    unsigned int desc_count,
+                                    unsigned int desc_size);
+void svnic_dev_clear_desc_ring(struct vnic_dev_ring *ring);
+int svnic_dev_alloc_desc_ring(struct vnic_dev *vdev, struct vnic_dev_ring *ring,
+                            unsigned int desc_count, unsigned int desc_size);
+void svnic_dev_free_desc_ring(struct vnic_dev *vdev,
+                            struct vnic_dev_ring *ring);
+int svnic_dev_cmd(struct vnic_dev *vdev, enum vnic_devcmd_cmd cmd,
+                u64 *a0, u64 *a1, int wait);
+int svnic_dev_fw_info(struct vnic_dev *vdev,
+                    struct vnic_devcmd_fw_info **fw_info);
+int svnic_dev_spec(struct vnic_dev *vdev, unsigned int offset,
+                 unsigned int size, void *value);
+int svnic_dev_stats_clear(struct vnic_dev *vdev);
+int svnic_dev_stats_dump(struct vnic_dev *vdev, struct vnic_stats **stats);
+int svnic_dev_notify_set(struct vnic_dev *vdev, u16 intr);
+void svnic_dev_notify_unset(struct vnic_dev *vdev);
+int svnic_dev_link_status(struct vnic_dev *vdev);
+u32 svnic_dev_link_down_cnt(struct vnic_dev *vdev);
+int svnic_dev_close(struct vnic_dev *vdev);
+int svnic_dev_enable_wait(struct vnic_dev *vdev);
+int svnic_dev_disable(struct vnic_dev *vdev);
+int svnic_dev_open(struct vnic_dev *vdev, int arg);
+int svnic_dev_open_done(struct vnic_dev *vdev, int *done);
+int svnic_dev_init(struct vnic_dev *vdev, int arg);
+struct vnic_dev *svnic_dev_alloc_discover(struct vnic_dev *vdev,
+                                        void *priv, struct pci_dev *pdev,
+                                        struct vnic_dev_bar *bar,
+                                        unsigned int num_bars);
+void svnic_dev_set_intr_mode(struct vnic_dev *vdev,
+                           enum vnic_dev_intr_mode intr_mode);
+enum vnic_dev_intr_mode svnic_dev_get_intr_mode(struct vnic_dev *vdev);
+void svnic_dev_unregister(struct vnic_dev *vdev);
+int svnic_dev_cmd_init(struct vnic_dev *vdev, int fallback);
+#endif /* _VNIC_DEV_H_ */
diff --git a/drivers/scsi/snic/vnic_devcmd.h b/drivers/scsi/snic/vnic_devcmd.h
new file mode 100644 (file)
index 0000000..d81b4f0
--- /dev/null
@@ -0,0 +1,270 @@
+/*
+ * Copyright 2014 Cisco Systems, Inc.  All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * 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 _VNIC_DEVCMD_H_
+#define _VNIC_DEVCMD_H_
+
+#define _CMD_NBITS      14
+#define _CMD_VTYPEBITS 10
+#define _CMD_FLAGSBITS  6
+#define _CMD_DIRBITS   2
+
+#define _CMD_NMASK      ((1 << _CMD_NBITS)-1)
+#define _CMD_VTYPEMASK  ((1 << _CMD_VTYPEBITS)-1)
+#define _CMD_FLAGSMASK  ((1 << _CMD_FLAGSBITS)-1)
+#define _CMD_DIRMASK    ((1 << _CMD_DIRBITS)-1)
+
+#define _CMD_NSHIFT     0
+#define _CMD_VTYPESHIFT (_CMD_NSHIFT+_CMD_NBITS)
+#define _CMD_FLAGSSHIFT (_CMD_VTYPESHIFT+_CMD_VTYPEBITS)
+#define _CMD_DIRSHIFT   (_CMD_FLAGSSHIFT+_CMD_FLAGSBITS)
+
+/*
+ * Direction bits (from host perspective).
+ */
+#define _CMD_DIR_NONE   0U
+#define _CMD_DIR_WRITE  1U
+#define _CMD_DIR_READ   2U
+#define _CMD_DIR_RW     (_CMD_DIR_WRITE | _CMD_DIR_READ)
+
+/*
+ * Flag bits.
+ */
+#define _CMD_FLAGS_NONE 0U
+#define _CMD_FLAGS_NOWAIT 1U
+
+/*
+ * vNIC type bits.
+ */
+#define _CMD_VTYPE_NONE  0U
+#define _CMD_VTYPE_ENET  1U
+#define _CMD_VTYPE_FC    2U
+#define _CMD_VTYPE_SCSI  4U
+#define _CMD_VTYPE_ALL   (_CMD_VTYPE_ENET | _CMD_VTYPE_FC | _CMD_VTYPE_SCSI)
+
+/*
+ * Used to create cmds..
+*/
+#define _CMDCF(dir, flags, vtype, nr)  \
+       (((dir)   << _CMD_DIRSHIFT) | \
+       ((flags) << _CMD_FLAGSSHIFT) | \
+       ((vtype) << _CMD_VTYPESHIFT) | \
+       ((nr)    << _CMD_NSHIFT))
+#define _CMDC(dir, vtype, nr)    _CMDCF(dir, 0, vtype, nr)
+#define _CMDCNW(dir, vtype, nr)  _CMDCF(dir, _CMD_FLAGS_NOWAIT, vtype, nr)
+
+/*
+ * Used to decode cmds..
+*/
+#define _CMD_DIR(cmd)            (((cmd) >> _CMD_DIRSHIFT) & _CMD_DIRMASK)
+#define _CMD_FLAGS(cmd)          (((cmd) >> _CMD_FLAGSSHIFT) & _CMD_FLAGSMASK)
+#define _CMD_VTYPE(cmd)          (((cmd) >> _CMD_VTYPESHIFT) & _CMD_VTYPEMASK)
+#define _CMD_N(cmd)              (((cmd) >> _CMD_NSHIFT) & _CMD_NMASK)
+
+enum vnic_devcmd_cmd {
+       CMD_NONE                = _CMDC(_CMD_DIR_NONE, _CMD_VTYPE_NONE, 0),
+
+       /* mcpu fw info in mem: (u64)a0=paddr to struct vnic_devcmd_fw_info */
+       CMD_MCPU_FW_INFO        = _CMDC(_CMD_DIR_WRITE, _CMD_VTYPE_ALL, 1),
+
+       /* dev-specific block member:
+        *    in: (u16)a0=offset,(u8)a1=size
+        *    out: a0=value */
+       CMD_DEV_SPEC            = _CMDC(_CMD_DIR_RW, _CMD_VTYPE_ALL, 2),
+
+       /* stats clear */
+       CMD_STATS_CLEAR         = _CMDCNW(_CMD_DIR_NONE, _CMD_VTYPE_ALL, 3),
+
+       /* stats dump in mem: (u64)a0=paddr to stats area,
+        *                    (u16)a1=sizeof stats area */
+       CMD_STATS_DUMP          = _CMDC(_CMD_DIR_WRITE, _CMD_VTYPE_ALL, 4),
+
+       /* nic_cfg in (u32)a0 */
+       CMD_NIC_CFG             = _CMDCNW(_CMD_DIR_WRITE, _CMD_VTYPE_ALL, 16),
+
+       /* set struct vnic_devcmd_notify buffer in mem:
+        * in:
+        *   (u64)a0=paddr to notify (set paddr=0 to unset)
+        *   (u32)a1 & 0x00000000ffffffff=sizeof(struct vnic_devcmd_notify)
+        *   (u16)a1 & 0x0000ffff00000000=intr num (-1 for no intr)
+        * out:
+        *   (u32)a1 = effective size
+        */
+       CMD_NOTIFY              = _CMDC(_CMD_DIR_RW, _CMD_VTYPE_ALL, 21),
+
+       /* initiate open sequence (u32)a0=flags (see CMD_OPENF_*) */
+       CMD_OPEN                = _CMDCNW(_CMD_DIR_WRITE, _CMD_VTYPE_ALL, 23),
+
+       /* open status:
+        *    out: a0=0 open complete, a0=1 open in progress */
+       CMD_OPEN_STATUS         = _CMDC(_CMD_DIR_READ, _CMD_VTYPE_ALL, 24),
+
+       /* close vnic */
+       CMD_CLOSE               = _CMDC(_CMD_DIR_NONE, _CMD_VTYPE_ALL, 25),
+
+       /* initialize virtual link: (u32)a0=flags (see CMD_INITF_*) */
+       CMD_INIT                = _CMDCNW(_CMD_DIR_READ, _CMD_VTYPE_ALL, 26),
+
+       /* enable virtual link */
+       CMD_ENABLE              = _CMDCNW(_CMD_DIR_WRITE, _CMD_VTYPE_ALL, 28),
+
+       /* enable virtual link, waiting variant. */
+       CMD_ENABLE_WAIT         = _CMDC(_CMD_DIR_WRITE, _CMD_VTYPE_ALL, 28),
+
+       /* disable virtual link */
+       CMD_DISABLE             = _CMDC(_CMD_DIR_NONE, _CMD_VTYPE_ALL, 29),
+
+       /* stats dump all vnics on uplink in mem: (u64)a0=paddr (u32)a1=uif */
+       CMD_STATS_DUMP_ALL      = _CMDC(_CMD_DIR_WRITE, _CMD_VTYPE_ALL, 30),
+
+       /* init status:
+        *    out: a0=0 init complete, a0=1 init in progress
+        *         if a0=0, a1=errno */
+       CMD_INIT_STATUS         = _CMDC(_CMD_DIR_READ, _CMD_VTYPE_ALL, 31),
+
+       /* undo initialize of virtual link */
+       CMD_DEINIT              = _CMDCNW(_CMD_DIR_NONE, _CMD_VTYPE_ALL, 34),
+
+       /* check fw capability of a cmd:
+        * in:  (u32)a0=cmd
+        * out: (u32)a0=errno, 0:valid cmd, a1=supported VNIC_STF_* bits */
+       CMD_CAPABILITY      = _CMDC(_CMD_DIR_RW, _CMD_VTYPE_ALL, 36),
+
+       /*
+        * Initialization for the devcmd2 interface.
+        * in: (u64) a0=host result buffer physical address
+        * in: (u16) a1=number of entries in result buffer
+        */
+       CMD_INITIALIZE_DEVCMD2 = _CMDC(_CMD_DIR_WRITE, _CMD_VTYPE_ALL, 57)
+};
+
+/* flags for CMD_OPEN */
+#define CMD_OPENF_OPROM                0x1     /* open coming from option rom */
+
+/* flags for CMD_INIT */
+#define CMD_INITF_DEFAULT_MAC  0x1     /* init with default mac addr */
+
+/* flags for CMD_PACKET_FILTER */
+#define CMD_PFILTER_DIRECTED           0x01
+#define CMD_PFILTER_MULTICAST          0x02
+#define CMD_PFILTER_BROADCAST          0x04
+#define CMD_PFILTER_PROMISCUOUS                0x08
+#define CMD_PFILTER_ALL_MULTICAST      0x10
+
+enum vnic_devcmd_status {
+       STAT_NONE = 0,
+       STAT_BUSY = 1 << 0,     /* cmd in progress */
+       STAT_ERROR = 1 << 1,    /* last cmd caused error (code in a0) */
+};
+
+enum vnic_devcmd_error {
+       ERR_SUCCESS = 0,
+       ERR_EINVAL = 1,
+       ERR_EFAULT = 2,
+       ERR_EPERM = 3,
+       ERR_EBUSY = 4,
+       ERR_ECMDUNKNOWN = 5,
+       ERR_EBADSTATE = 6,
+       ERR_ENOMEM = 7,
+       ERR_ETIMEDOUT = 8,
+       ERR_ELINKDOWN = 9,
+};
+
+struct vnic_devcmd_fw_info {
+       char fw_version[32];
+       char fw_build[32];
+       char hw_version[32];
+       char hw_serial_number[32];
+};
+
+struct vnic_devcmd_notify {
+       u32 csum;               /* checksum over following words */
+
+       u32 link_state;         /* link up == 1 */
+       u32 port_speed;         /* effective port speed (rate limit) */
+       u32 mtu;                /* MTU */
+       u32 msglvl;             /* requested driver msg lvl */
+       u32 uif;                /* uplink interface */
+       u32 status;             /* status bits (see VNIC_STF_*) */
+       u32 error;              /* error code (see ERR_*) for first ERR */
+       u32 link_down_cnt;      /* running count of link down transitions */
+};
+#define VNIC_STF_FATAL_ERR     0x0001  /* fatal fw error */
+
+struct vnic_devcmd_provinfo {
+       u8 oui[3];
+       u8 type;
+       u8 data[0];
+};
+
+/*
+ * Writing cmd register causes STAT_BUSY to get set in status register.
+ * When cmd completes, STAT_BUSY will be cleared.
+ *
+ * If cmd completed successfully STAT_ERROR will be clear
+ * and args registers contain cmd-specific results.
+ *
+ * If cmd error, STAT_ERROR will be set and args[0] contains error code.
+ *
+ * status register is read-only.  While STAT_BUSY is set,
+ * all other register contents are read-only.
+ */
+
+/* Make sizeof(vnic_devcmd) a power-of-2 for I/O BAR. */
+#define VNIC_DEVCMD_NARGS 15
+struct vnic_devcmd {
+       u32 status;                     /* RO */
+       u32 cmd;                        /* RW */
+       u64 args[VNIC_DEVCMD_NARGS];    /* RW cmd args (little-endian) */
+};
+
+
+/*
+ * Version 2 of the interface.
+ *
+ * Some things are carried over, notably the vnic_devcmd_cmd enum.
+ */
+
+/*
+ * Flags for vnic_devcmd2.flags
+ */
+
+#define DEVCMD2_FNORESULT       0x1     /* Don't copy result to host */
+
+#define VNIC_DEVCMD2_NARGS      VNIC_DEVCMD_NARGS
+struct vnic_devcmd2 {
+       u16 pad;
+       u16 flags;
+       u32 cmd;                /* same command #defines as original */
+       u64 args[VNIC_DEVCMD2_NARGS];
+};
+
+#define VNIC_DEVCMD2_NRESULTS   VNIC_DEVCMD_NARGS
+struct devcmd2_result {
+       u64 results[VNIC_DEVCMD2_NRESULTS];
+       u32 pad;
+       u16 completed_index;    /* into copy WQ */
+       u8  error;              /* same error codes as original */
+       u8  color;              /* 0 or 1 as with completion queues */
+};
+
+#define DEVCMD2_RING_SIZE   32
+#define DEVCMD2_DESC_SIZE   128
+
+#define DEVCMD2_RESULTS_SIZE_MAX   ((1 << 16) - 1)
+
+#endif /* _VNIC_DEVCMD_H_ */
diff --git a/drivers/scsi/snic/vnic_intr.c b/drivers/scsi/snic/vnic_intr.c
new file mode 100644 (file)
index 0000000..a7d5480
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2014 Cisco Systems, Inc.  All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * 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/kernel.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include "vnic_dev.h"
+#include "vnic_intr.h"
+
+void svnic_intr_free(struct vnic_intr *intr)
+{
+       intr->ctrl = NULL;
+}
+
+int svnic_intr_alloc(struct vnic_dev *vdev, struct vnic_intr *intr,
+       unsigned int index)
+{
+       intr->index = index;
+       intr->vdev = vdev;
+
+       intr->ctrl = svnic_dev_get_res(vdev, RES_TYPE_INTR_CTRL, index);
+       if (!intr->ctrl) {
+               pr_err("Failed to hook INTR[%d].ctrl resource\n",
+                       index);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+void svnic_intr_init(struct vnic_intr *intr, unsigned int coalescing_timer,
+       unsigned int coalescing_type, unsigned int mask_on_assertion)
+{
+       iowrite32(coalescing_timer, &intr->ctrl->coalescing_timer);
+       iowrite32(coalescing_type, &intr->ctrl->coalescing_type);
+       iowrite32(mask_on_assertion, &intr->ctrl->mask_on_assertion);
+       iowrite32(0, &intr->ctrl->int_credits);
+}
+
+void svnic_intr_clean(struct vnic_intr *intr)
+{
+       iowrite32(0, &intr->ctrl->int_credits);
+}
diff --git a/drivers/scsi/snic/vnic_intr.h b/drivers/scsi/snic/vnic_intr.h
new file mode 100644 (file)
index 0000000..4547f60
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2014 Cisco Systems, Inc.  All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * 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 _VNIC_INTR_H_
+#define _VNIC_INTR_H_
+
+#include <linux/pci.h>
+#include "vnic_dev.h"
+
+#define VNIC_INTR_TIMER_MAX            0xffff
+
+#define VNIC_INTR_TIMER_TYPE_ABS       0
+#define VNIC_INTR_TIMER_TYPE_QUIET     1
+
+/* Interrupt control */
+struct vnic_intr_ctrl {
+       u32 coalescing_timer;           /* 0x00 */
+       u32 pad0;
+       u32 coalescing_value;           /* 0x08 */
+       u32 pad1;
+       u32 coalescing_type;            /* 0x10 */
+       u32 pad2;
+       u32 mask_on_assertion;          /* 0x18 */
+       u32 pad3;
+       u32 mask;                       /* 0x20 */
+       u32 pad4;
+       u32 int_credits;                /* 0x28 */
+       u32 pad5;
+       u32 int_credit_return;          /* 0x30 */
+       u32 pad6;
+};
+
+struct vnic_intr {
+       unsigned int index;
+       struct vnic_dev *vdev;
+       struct vnic_intr_ctrl __iomem *ctrl;    /* memory-mapped */
+};
+
+static inline void
+svnic_intr_unmask(struct vnic_intr *intr)
+{
+       iowrite32(0, &intr->ctrl->mask);
+}
+
+static inline void
+svnic_intr_mask(struct vnic_intr *intr)
+{
+       iowrite32(1, &intr->ctrl->mask);
+}
+
+static inline void
+svnic_intr_return_credits(struct vnic_intr *intr,
+                         unsigned int credits,
+                         int unmask,
+                         int reset_timer)
+{
+#define VNIC_INTR_UNMASK_SHIFT         16
+#define VNIC_INTR_RESET_TIMER_SHIFT    17
+
+       u32 int_credit_return = (credits & 0xffff) |
+               (unmask ? (1 << VNIC_INTR_UNMASK_SHIFT) : 0) |
+               (reset_timer ? (1 << VNIC_INTR_RESET_TIMER_SHIFT) : 0);
+
+       iowrite32(int_credit_return, &intr->ctrl->int_credit_return);
+}
+
+static inline unsigned int
+svnic_intr_credits(struct vnic_intr *intr)
+{
+       return ioread32(&intr->ctrl->int_credits);
+}
+
+static inline void
+svnic_intr_return_all_credits(struct vnic_intr *intr)
+{
+       unsigned int credits = svnic_intr_credits(intr);
+       int unmask = 1;
+       int reset_timer = 1;
+
+       svnic_intr_return_credits(intr, credits, unmask, reset_timer);
+}
+
+void svnic_intr_free(struct vnic_intr *);
+int svnic_intr_alloc(struct vnic_dev *, struct vnic_intr *, unsigned int);
+void svnic_intr_init(struct vnic_intr *intr,
+                    unsigned int coalescing_timer,
+                    unsigned int coalescing_type,
+                    unsigned int mask_on_assertion);
+void svnic_intr_clean(struct vnic_intr *);
+
+#endif /* _VNIC_INTR_H_ */
diff --git a/drivers/scsi/snic/vnic_resource.h b/drivers/scsi/snic/vnic_resource.h
new file mode 100644 (file)
index 0000000..9713d68
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2014 Cisco Systems, Inc.  All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * 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 _VNIC_RESOURCE_H_
+#define _VNIC_RESOURCE_H_
+
+#define VNIC_RES_MAGIC         0x766E6963L     /* 'vnic' */
+#define VNIC_RES_VERSION       0x00000000L
+
+/* vNIC resource types */
+enum vnic_res_type {
+       RES_TYPE_EOL,                   /* End-of-list */
+       RES_TYPE_WQ,                    /* Work queues */
+       RES_TYPE_RQ,                    /* Receive queues */
+       RES_TYPE_CQ,                    /* Completion queues */
+       RES_TYPE_RSVD1,
+       RES_TYPE_NIC_CFG,               /* Enet NIC config registers */
+       RES_TYPE_RSVD2,
+       RES_TYPE_RSVD3,
+       RES_TYPE_RSVD4,
+       RES_TYPE_RSVD5,
+       RES_TYPE_INTR_CTRL,             /* Interrupt ctrl table */
+       RES_TYPE_INTR_TABLE,            /* MSI/MSI-X Interrupt table */
+       RES_TYPE_INTR_PBA,              /* MSI/MSI-X PBA table */
+       RES_TYPE_INTR_PBA_LEGACY,       /* Legacy intr status */
+       RES_TYPE_RSVD6,
+       RES_TYPE_RSVD7,
+       RES_TYPE_DEVCMD,                /* Device command region */
+       RES_TYPE_PASS_THRU_PAGE,        /* Pass-thru page */
+       RES_TYPE_SUBVNIC,               /* subvnic resource type */
+       RES_TYPE_MQ_WQ,                 /* MQ Work queues */
+       RES_TYPE_MQ_RQ,                 /* MQ Receive queues */
+       RES_TYPE_MQ_CQ,                 /* MQ Completion queues */
+       RES_TYPE_DEPRECATED1,           /* Old version of devcmd 2 */
+       RES_TYPE_DEPRECATED2,           /* Old version of devcmd 2 */
+       RES_TYPE_DEVCMD2,               /* Device control region */
+
+       RES_TYPE_MAX,                   /* Count of resource types */
+};
+
+struct vnic_resource_header {
+       u32 magic;
+       u32 version;
+};
+
+struct vnic_resource {
+       u8 type;
+       u8 bar;
+       u8 pad[2];
+       u32 bar_offset;
+       u32 count;
+};
+
+#endif /* _VNIC_RESOURCE_H_ */
diff --git a/drivers/scsi/snic/vnic_snic.h b/drivers/scsi/snic/vnic_snic.h
new file mode 100644 (file)
index 0000000..514d39f
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2014 Cisco Systems, Inc.  All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * 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 _VNIC_SNIC_H_
+#define _VNIC_SNIC_H_
+
+#define VNIC_SNIC_WQ_DESCS_MIN              64
+#define VNIC_SNIC_WQ_DESCS_MAX              1024
+
+#define VNIC_SNIC_MAXDATAFIELDSIZE_MIN      256
+#define VNIC_SNIC_MAXDATAFIELDSIZE_MAX      2112
+
+#define VNIC_SNIC_IO_THROTTLE_COUNT_MIN     1
+#define VNIC_SNIC_IO_THROTTLE_COUNT_MAX     1024
+
+#define VNIC_SNIC_PORT_DOWN_TIMEOUT_MIN     0
+#define VNIC_SNIC_PORT_DOWN_TIMEOUT_MAX     240000
+
+#define VNIC_SNIC_PORT_DOWN_IO_RETRIES_MIN  0
+#define VNIC_SNIC_PORT_DOWN_IO_RETRIES_MAX  255
+
+#define VNIC_SNIC_LUNS_PER_TARGET_MIN       1
+#define VNIC_SNIC_LUNS_PER_TARGET_MAX       1024
+
+/* Device-specific region: scsi configuration */
+struct vnic_snic_config {
+       u32 flags;
+       u32 wq_enet_desc_count;
+       u32 io_throttle_count;
+       u32 port_down_timeout;
+       u32 port_down_io_retries;
+       u32 luns_per_tgt;
+       u16 maxdatafieldsize;
+       u16 intr_timer;
+       u8 intr_timer_type;
+       u8 _resvd2;
+       u8 xpt_type;
+       u8 hid;
+};
+#endif /* _VNIC_SNIC_H_ */
diff --git a/drivers/scsi/snic/vnic_stats.h b/drivers/scsi/snic/vnic_stats.h
new file mode 100644 (file)
index 0000000..370a37c
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2014 Cisco Systems, Inc.  All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * 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 _VNIC_STATS_H_
+#define _VNIC_STATS_H_
+
+/* Tx statistics */
+struct vnic_tx_stats {
+       u64 tx_frames_ok;
+       u64 tx_unicast_frames_ok;
+       u64 tx_multicast_frames_ok;
+       u64 tx_broadcast_frames_ok;
+       u64 tx_bytes_ok;
+       u64 tx_unicast_bytes_ok;
+       u64 tx_multicast_bytes_ok;
+       u64 tx_broadcast_bytes_ok;
+       u64 tx_drops;
+       u64 tx_errors;
+       u64 tx_tso;
+       u64 rsvd[16];
+};
+
+/* Rx statistics */
+struct vnic_rx_stats {
+       u64 rx_frames_ok;
+       u64 rx_frames_total;
+       u64 rx_unicast_frames_ok;
+       u64 rx_multicast_frames_ok;
+       u64 rx_broadcast_frames_ok;
+       u64 rx_bytes_ok;
+       u64 rx_unicast_bytes_ok;
+       u64 rx_multicast_bytes_ok;
+       u64 rx_broadcast_bytes_ok;
+       u64 rx_drop;
+       u64 rx_no_bufs;
+       u64 rx_errors;
+       u64 rx_rss;
+       u64 rx_crc_errors;
+       u64 rx_frames_64;
+       u64 rx_frames_127;
+       u64 rx_frames_255;
+       u64 rx_frames_511;
+       u64 rx_frames_1023;
+       u64 rx_frames_1518;
+       u64 rx_frames_to_max;
+       u64 rsvd[16];
+};
+
+struct vnic_stats {
+       struct vnic_tx_stats tx;
+       struct vnic_rx_stats rx;
+};
+
+#endif /* _VNIC_STATS_H_ */
diff --git a/drivers/scsi/snic/vnic_wq.c b/drivers/scsi/snic/vnic_wq.c
new file mode 100644 (file)
index 0000000..1e91d43
--- /dev/null
@@ -0,0 +1,237 @@
+/*
+ * Copyright 2014 Cisco Systems, Inc.  All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * 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/errno.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include "vnic_dev.h"
+#include "vnic_wq.h"
+
+static inline int vnic_wq_get_ctrl(struct vnic_dev *vdev, struct vnic_wq *wq,
+       unsigned int index, enum vnic_res_type res_type)
+{
+       wq->ctrl = svnic_dev_get_res(vdev, res_type, index);
+       if (!wq->ctrl)
+               return -EINVAL;
+
+       return 0;
+}
+
+static inline int vnic_wq_alloc_ring(struct vnic_dev *vdev, struct vnic_wq *wq,
+       unsigned int index, unsigned int desc_count, unsigned int desc_size)
+{
+       return svnic_dev_alloc_desc_ring(vdev, &wq->ring, desc_count,
+                                        desc_size);
+}
+
+static int vnic_wq_alloc_bufs(struct vnic_wq *wq)
+{
+       struct vnic_wq_buf *buf;
+       unsigned int i, j, count = wq->ring.desc_count;
+       unsigned int blks = VNIC_WQ_BUF_BLKS_NEEDED(count);
+
+       for (i = 0; i < blks; i++) {
+               wq->bufs[i] = kzalloc(VNIC_WQ_BUF_BLK_SZ, GFP_ATOMIC);
+               if (!wq->bufs[i]) {
+                       pr_err("Failed to alloc wq_bufs\n");
+
+                       return -ENOMEM;
+               }
+       }
+
+       for (i = 0; i < blks; i++) {
+               buf = wq->bufs[i];
+               for (j = 0; j < VNIC_WQ_BUF_DFLT_BLK_ENTRIES; j++) {
+                       buf->index = i * VNIC_WQ_BUF_DFLT_BLK_ENTRIES + j;
+                       buf->desc = (u8 *)wq->ring.descs +
+                               wq->ring.desc_size * buf->index;
+                       if (buf->index + 1 == count) {
+                               buf->next = wq->bufs[0];
+                               break;
+                       } else if (j + 1 == VNIC_WQ_BUF_DFLT_BLK_ENTRIES) {
+                               buf->next = wq->bufs[i + 1];
+                       } else {
+                               buf->next = buf + 1;
+                               buf++;
+                       }
+               }
+       }
+
+       wq->to_use = wq->to_clean = wq->bufs[0];
+
+       return 0;
+}
+
+void svnic_wq_free(struct vnic_wq *wq)
+{
+       struct vnic_dev *vdev;
+       unsigned int i;
+
+       vdev = wq->vdev;
+
+       svnic_dev_free_desc_ring(vdev, &wq->ring);
+
+       for (i = 0; i < VNIC_WQ_BUF_BLKS_MAX; i++) {
+               kfree(wq->bufs[i]);
+               wq->bufs[i] = NULL;
+       }
+
+       wq->ctrl = NULL;
+
+}
+
+int vnic_wq_devcmd2_alloc(struct vnic_dev *vdev, struct vnic_wq *wq,
+       unsigned int desc_count, unsigned int desc_size)
+{
+       int err;
+
+       wq->index = 0;
+       wq->vdev = vdev;
+
+       err = vnic_wq_get_ctrl(vdev, wq, 0, RES_TYPE_DEVCMD2);
+       if (err) {
+               pr_err("Failed to get devcmd2 resource\n");
+
+               return err;
+       }
+
+       svnic_wq_disable(wq);
+
+       err = vnic_wq_alloc_ring(vdev, wq, 0, desc_count, desc_size);
+       if (err)
+               return err;
+
+       return 0;
+}
+
+int svnic_wq_alloc(struct vnic_dev *vdev, struct vnic_wq *wq,
+       unsigned int index, unsigned int desc_count, unsigned int desc_size)
+{
+       int err;
+
+       wq->index = index;
+       wq->vdev = vdev;
+
+       err = vnic_wq_get_ctrl(vdev, wq, index, RES_TYPE_WQ);
+       if (err) {
+               pr_err("Failed to hook WQ[%d] resource\n", index);
+
+               return err;
+       }
+
+       svnic_wq_disable(wq);
+
+       err = vnic_wq_alloc_ring(vdev, wq, index, desc_count, desc_size);
+       if (err)
+               return err;
+
+       err = vnic_wq_alloc_bufs(wq);
+       if (err) {
+               svnic_wq_free(wq);
+
+               return err;
+       }
+
+       return 0;
+}
+
+void vnic_wq_init_start(struct vnic_wq *wq, unsigned int cq_index,
+       unsigned int fetch_index, unsigned int posted_index,
+       unsigned int error_interrupt_enable,
+       unsigned int error_interrupt_offset)
+{
+       u64 paddr;
+       unsigned int count = wq->ring.desc_count;
+
+       paddr = (u64)wq->ring.base_addr | VNIC_PADDR_TARGET;
+       writeq(paddr, &wq->ctrl->ring_base);
+       iowrite32(count, &wq->ctrl->ring_size);
+       iowrite32(fetch_index, &wq->ctrl->fetch_index);
+       iowrite32(posted_index, &wq->ctrl->posted_index);
+       iowrite32(cq_index, &wq->ctrl->cq_index);
+       iowrite32(error_interrupt_enable, &wq->ctrl->error_interrupt_enable);
+       iowrite32(error_interrupt_offset, &wq->ctrl->error_interrupt_offset);
+       iowrite32(0, &wq->ctrl->error_status);
+
+       wq->to_use = wq->to_clean =
+               &wq->bufs[fetch_index / VNIC_WQ_BUF_BLK_ENTRIES(count)]
+                       [fetch_index % VNIC_WQ_BUF_BLK_ENTRIES(count)];
+}
+
+void svnic_wq_init(struct vnic_wq *wq, unsigned int cq_index,
+       unsigned int error_interrupt_enable,
+       unsigned int error_interrupt_offset)
+{
+       vnic_wq_init_start(wq, cq_index, 0, 0, error_interrupt_enable,
+                          error_interrupt_offset);
+}
+
+unsigned int svnic_wq_error_status(struct vnic_wq *wq)
+{
+       return ioread32(&wq->ctrl->error_status);
+}
+
+void svnic_wq_enable(struct vnic_wq *wq)
+{
+       iowrite32(1, &wq->ctrl->enable);
+}
+
+int svnic_wq_disable(struct vnic_wq *wq)
+{
+       unsigned int wait;
+
+       iowrite32(0, &wq->ctrl->enable);
+
+       /* Wait for HW to ACK disable request */
+       for (wait = 0; wait < 100; wait++) {
+               if (!(ioread32(&wq->ctrl->running)))
+                       return 0;
+               udelay(1);
+       }
+
+       pr_err("Failed to disable WQ[%d]\n", wq->index);
+
+       return -ETIMEDOUT;
+}
+
+void svnic_wq_clean(struct vnic_wq *wq,
+       void (*buf_clean)(struct vnic_wq *wq, struct vnic_wq_buf *buf))
+{
+       struct vnic_wq_buf *buf;
+
+       BUG_ON(ioread32(&wq->ctrl->enable));
+
+       buf = wq->to_clean;
+
+       while (svnic_wq_desc_used(wq) > 0) {
+
+               (*buf_clean)(wq, buf);
+
+               buf = wq->to_clean = buf->next;
+               wq->ring.desc_avail++;
+       }
+
+       wq->to_use = wq->to_clean = wq->bufs[0];
+
+       iowrite32(0, &wq->ctrl->fetch_index);
+       iowrite32(0, &wq->ctrl->posted_index);
+       iowrite32(0, &wq->ctrl->error_status);
+
+       svnic_dev_clear_desc_ring(&wq->ring);
+}
diff --git a/drivers/scsi/snic/vnic_wq.h b/drivers/scsi/snic/vnic_wq.h
new file mode 100644 (file)
index 0000000..7cc031c
--- /dev/null
@@ -0,0 +1,170 @@
+/*
+ * Copyright 2014 Cisco Systems, Inc.  All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * 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 _VNIC_WQ_H_
+#define _VNIC_WQ_H_
+
+#include <linux/pci.h>
+#include "vnic_dev.h"
+#include "vnic_cq.h"
+
+/* Work queue control */
+struct vnic_wq_ctrl {
+       u64 ring_base;                  /* 0x00 */
+       u32 ring_size;                  /* 0x08 */
+       u32 pad0;
+       u32 posted_index;               /* 0x10 */
+       u32 pad1;
+       u32 cq_index;                   /* 0x18 */
+       u32 pad2;
+       u32 enable;                     /* 0x20 */
+       u32 pad3;
+       u32 running;                    /* 0x28 */
+       u32 pad4;
+       u32 fetch_index;                /* 0x30 */
+       u32 pad5;
+       u32 dca_value;                  /* 0x38 */
+       u32 pad6;
+       u32 error_interrupt_enable;     /* 0x40 */
+       u32 pad7;
+       u32 error_interrupt_offset;     /* 0x48 */
+       u32 pad8;
+       u32 error_status;               /* 0x50 */
+       u32 pad9;
+};
+
+struct vnic_wq_buf {
+       struct vnic_wq_buf *next;
+       dma_addr_t dma_addr;
+       void *os_buf;
+       unsigned int len;
+       unsigned int index;
+       int sop;
+       void *desc;
+};
+
+/* Break the vnic_wq_buf allocations into blocks of 64 entries */
+#define VNIC_WQ_BUF_MIN_BLK_ENTRIES 32
+#define VNIC_WQ_BUF_DFLT_BLK_ENTRIES 64
+#define VNIC_WQ_BUF_BLK_ENTRIES(entries) \
+       ((unsigned int)(entries < VNIC_WQ_BUF_DFLT_BLK_ENTRIES) ? \
+               VNIC_WQ_BUF_MIN_BLK_ENTRIES : VNIC_WQ_BUF_DFLT_BLK_ENTRIES)
+#define VNIC_WQ_BUF_BLK_SZ \
+       (VNIC_WQ_BUF_DFLT_BLK_ENTRIES * sizeof(struct vnic_wq_buf))
+#define VNIC_WQ_BUF_BLKS_NEEDED(entries) \
+       DIV_ROUND_UP(entries, VNIC_WQ_BUF_DFLT_BLK_ENTRIES)
+#define VNIC_WQ_BUF_BLKS_NEEDED(entries) \
+       DIV_ROUND_UP(entries, VNIC_WQ_BUF_DFLT_BLK_ENTRIES)
+#define VNIC_WQ_BUF_BLKS_MAX VNIC_WQ_BUF_BLKS_NEEDED(4096)
+
+struct vnic_wq {
+       unsigned int index;
+       struct vnic_dev *vdev;
+       struct vnic_wq_ctrl __iomem *ctrl;      /* memory-mapped */
+       struct vnic_dev_ring ring;
+       struct vnic_wq_buf *bufs[VNIC_WQ_BUF_BLKS_MAX];
+       struct vnic_wq_buf *to_use;
+       struct vnic_wq_buf *to_clean;
+       unsigned int pkts_outstanding;
+};
+
+static inline unsigned int svnic_wq_desc_avail(struct vnic_wq *wq)
+{
+       /* how many does SW own? */
+       return wq->ring.desc_avail;
+}
+
+static inline unsigned int svnic_wq_desc_used(struct vnic_wq *wq)
+{
+       /* how many does HW own? */
+       return wq->ring.desc_count - wq->ring.desc_avail - 1;
+}
+
+static inline void *svnic_wq_next_desc(struct vnic_wq *wq)
+{
+       return wq->to_use->desc;
+}
+
+static inline void svnic_wq_post(struct vnic_wq *wq,
+       void *os_buf, dma_addr_t dma_addr,
+       unsigned int len, int sop, int eop)
+{
+       struct vnic_wq_buf *buf = wq->to_use;
+
+       buf->sop = sop;
+       buf->os_buf = eop ? os_buf : NULL;
+       buf->dma_addr = dma_addr;
+       buf->len = len;
+
+       buf = buf->next;
+       if (eop) {
+               /* Adding write memory barrier prevents compiler and/or CPU
+                * reordering, thus avoiding descriptor posting before
+                * descriptor is initialized. Otherwise, hardware can read
+                * stale descriptor fields.
+                */
+               wmb();
+               iowrite32(buf->index, &wq->ctrl->posted_index);
+       }
+       wq->to_use = buf;
+
+       wq->ring.desc_avail--;
+}
+
+static inline void svnic_wq_service(struct vnic_wq *wq,
+       struct cq_desc *cq_desc, u16 completed_index,
+       void (*buf_service)(struct vnic_wq *wq,
+       struct cq_desc *cq_desc, struct vnic_wq_buf *buf, void *opaque),
+       void *opaque)
+{
+       struct vnic_wq_buf *buf;
+
+       buf = wq->to_clean;
+       while (1) {
+
+               (*buf_service)(wq, cq_desc, buf, opaque);
+
+               wq->ring.desc_avail++;
+
+               wq->to_clean = buf->next;
+
+               if (buf->index == completed_index)
+                       break;
+
+               buf = wq->to_clean;
+       }
+}
+
+void svnic_wq_free(struct vnic_wq *wq);
+int svnic_wq_alloc(struct vnic_dev *vdev, struct vnic_wq *wq,
+       unsigned int index, unsigned int desc_count, unsigned int desc_size);
+int vnic_wq_devcmd2_alloc(struct vnic_dev *vdev, struct vnic_wq *wq,
+               unsigned int desc_count, unsigned int desc_size);
+void vnic_wq_init_start(struct vnic_wq *wq, unsigned int cq_index,
+               unsigned int fetch_index, unsigned int post_index,
+               unsigned int error_interrupt_enable,
+               unsigned int error_interrupt_offset);
+
+void svnic_wq_init(struct vnic_wq *wq, unsigned int cq_index,
+       unsigned int error_interrupt_enable,
+       unsigned int error_interrupt_offset);
+unsigned int svnic_wq_error_status(struct vnic_wq *wq);
+void svnic_wq_enable(struct vnic_wq *wq);
+int svnic_wq_disable(struct vnic_wq *wq);
+void svnic_wq_clean(struct vnic_wq *wq,
+       void (*buf_clean)(struct vnic_wq *wq, struct vnic_wq_buf *buf));
+#endif /* _VNIC_WQ_H_ */
diff --git a/drivers/scsi/snic/wq_enet_desc.h b/drivers/scsi/snic/wq_enet_desc.h
new file mode 100644 (file)
index 0000000..68f62b6
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2014 Cisco Systems, Inc.  All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * 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 _WQ_ENET_DESC_H_
+#define _WQ_ENET_DESC_H_
+
+/* Ethernet work queue descriptor: 16B */
+struct wq_enet_desc {
+       __le64 address;
+       __le16 length;
+       __le16 mss_loopback;
+       __le16 header_length_flags;
+       __le16 vlan_tag;
+};
+
+#define WQ_ENET_ADDR_BITS              64
+#define WQ_ENET_LEN_BITS               14
+#define WQ_ENET_LEN_MASK               ((1 << WQ_ENET_LEN_BITS) - 1)
+#define WQ_ENET_MSS_BITS               14
+#define WQ_ENET_MSS_MASK               ((1 << WQ_ENET_MSS_BITS) - 1)
+#define WQ_ENET_MSS_SHIFT              2
+#define WQ_ENET_LOOPBACK_SHIFT         1
+#define WQ_ENET_HDRLEN_BITS            10
+#define WQ_ENET_HDRLEN_MASK            ((1 << WQ_ENET_HDRLEN_BITS) - 1)
+#define WQ_ENET_FLAGS_OM_BITS          2
+#define WQ_ENET_FLAGS_OM_MASK          ((1 << WQ_ENET_FLAGS_OM_BITS) - 1)
+#define WQ_ENET_FLAGS_EOP_SHIFT                12
+#define WQ_ENET_FLAGS_CQ_ENTRY_SHIFT   13
+#define WQ_ENET_FLAGS_FCOE_ENCAP_SHIFT 14
+#define WQ_ENET_FLAGS_VLAN_TAG_INSERT_SHIFT    15
+
+#define WQ_ENET_OFFLOAD_MODE_CSUM      0
+#define WQ_ENET_OFFLOAD_MODE_RESERVED  1
+#define WQ_ENET_OFFLOAD_MODE_CSUM_L4   2
+#define WQ_ENET_OFFLOAD_MODE_TSO       3
+
+static inline void wq_enet_desc_enc(struct wq_enet_desc *desc,
+       u64 address, u16 length, u16 mss, u16 header_length,
+       u8 offload_mode, u8 eop, u8 cq_entry, u8 fcoe_encap,
+       u8 vlan_tag_insert, u16 vlan_tag, u8 loopback)
+{
+       desc->address = cpu_to_le64(address);
+       desc->length = cpu_to_le16(length & WQ_ENET_LEN_MASK);
+       desc->mss_loopback = cpu_to_le16((mss & WQ_ENET_MSS_MASK) <<
+               WQ_ENET_MSS_SHIFT | (loopback & 1) << WQ_ENET_LOOPBACK_SHIFT);
+       desc->header_length_flags = cpu_to_le16(
+               (header_length & WQ_ENET_HDRLEN_MASK) |
+               (offload_mode & WQ_ENET_FLAGS_OM_MASK) << WQ_ENET_HDRLEN_BITS |
+               (eop & 1) << WQ_ENET_FLAGS_EOP_SHIFT |
+               (cq_entry & 1) << WQ_ENET_FLAGS_CQ_ENTRY_SHIFT |
+               (fcoe_encap & 1) << WQ_ENET_FLAGS_FCOE_ENCAP_SHIFT |
+               (vlan_tag_insert & 1) << WQ_ENET_FLAGS_VLAN_TAG_INSERT_SHIFT);
+       desc->vlan_tag = cpu_to_le16(vlan_tag);
+}
+
+static inline void wq_enet_desc_dec(struct wq_enet_desc *desc,
+       u64 *address, u16 *length, u16 *mss, u16 *header_length,
+       u8 *offload_mode, u8 *eop, u8 *cq_entry, u8 *fcoe_encap,
+       u8 *vlan_tag_insert, u16 *vlan_tag, u8 *loopback)
+{
+       *address = le64_to_cpu(desc->address);
+       *length = le16_to_cpu(desc->length) & WQ_ENET_LEN_MASK;
+       *mss = (le16_to_cpu(desc->mss_loopback) >> WQ_ENET_MSS_SHIFT) &
+               WQ_ENET_MSS_MASK;
+       *loopback = (u8)((le16_to_cpu(desc->mss_loopback) >>
+               WQ_ENET_LOOPBACK_SHIFT) & 1);
+       *header_length = le16_to_cpu(desc->header_length_flags) &
+               WQ_ENET_HDRLEN_MASK;
+       *offload_mode = (u8)((le16_to_cpu(desc->header_length_flags) >>
+               WQ_ENET_HDRLEN_BITS) & WQ_ENET_FLAGS_OM_MASK);
+       *eop = (u8)((le16_to_cpu(desc->header_length_flags) >>
+               WQ_ENET_FLAGS_EOP_SHIFT) & 1);
+       *cq_entry = (u8)((le16_to_cpu(desc->header_length_flags) >>
+               WQ_ENET_FLAGS_CQ_ENTRY_SHIFT) & 1);
+       *fcoe_encap = (u8)((le16_to_cpu(desc->header_length_flags) >>
+               WQ_ENET_FLAGS_FCOE_ENCAP_SHIFT) & 1);
+       *vlan_tag_insert = (u8)((le16_to_cpu(desc->header_length_flags) >>
+               WQ_ENET_FLAGS_VLAN_TAG_INSERT_SHIFT) & 1);
+       *vlan_tag = le16_to_cpu(desc->vlan_tag);
+}
+
+#endif /* _WQ_ENET_DESC_H_ */
index 9a1c34205254f62b0f9e1c99d99db3eb29852ce7..3f25b8fa921d69bff68ff6aab6e4c0b1f6ac65d7 100644 (file)
@@ -471,6 +471,47 @@ static void st_release_request(struct st_request *streq)
        kfree(streq);
 }
 
+static void st_do_stats(struct scsi_tape *STp, struct request *req)
+{
+       ktime_t now;
+
+       now = ktime_get();
+       if (req->cmd[0] == WRITE_6) {
+               now = ktime_sub(now, STp->stats->write_time);
+               atomic64_add(ktime_to_ns(now), &STp->stats->tot_write_time);
+               atomic64_add(ktime_to_ns(now), &STp->stats->tot_io_time);
+               atomic64_inc(&STp->stats->write_cnt);
+               if (req->errors) {
+                       atomic64_add(atomic_read(&STp->stats->last_write_size)
+                               - STp->buffer->cmdstat.residual,
+                               &STp->stats->write_byte_cnt);
+                       if (STp->buffer->cmdstat.residual > 0)
+                               atomic64_inc(&STp->stats->resid_cnt);
+               } else
+                       atomic64_add(atomic_read(&STp->stats->last_write_size),
+                               &STp->stats->write_byte_cnt);
+       } else if (req->cmd[0] == READ_6) {
+               now = ktime_sub(now, STp->stats->read_time);
+               atomic64_add(ktime_to_ns(now), &STp->stats->tot_read_time);
+               atomic64_add(ktime_to_ns(now), &STp->stats->tot_io_time);
+               atomic64_inc(&STp->stats->read_cnt);
+               if (req->errors) {
+                       atomic64_add(atomic_read(&STp->stats->last_read_size)
+                               - STp->buffer->cmdstat.residual,
+                               &STp->stats->read_byte_cnt);
+                       if (STp->buffer->cmdstat.residual > 0)
+                               atomic64_inc(&STp->stats->resid_cnt);
+               } else
+                       atomic64_add(atomic_read(&STp->stats->last_read_size),
+                               &STp->stats->read_byte_cnt);
+       } else {
+               now = ktime_sub(now, STp->stats->other_time);
+               atomic64_add(ktime_to_ns(now), &STp->stats->tot_io_time);
+               atomic64_inc(&STp->stats->other_cnt);
+       }
+       atomic64_dec(&STp->stats->in_flight);
+}
+
 static void st_scsi_execute_end(struct request *req, int uptodate)
 {
        struct st_request *SRpnt = req->end_io_data;
@@ -480,6 +521,8 @@ static void st_scsi_execute_end(struct request *req, int uptodate)
        STp->buffer->cmdstat.midlevel_result = SRpnt->result = req->errors;
        STp->buffer->cmdstat.residual = req->resid_len;
 
+       st_do_stats(STp, req);
+
        tmp = SRpnt->bio;
        if (SRpnt->waiting)
                complete(SRpnt->waiting);
@@ -496,6 +539,7 @@ static int st_scsi_execute(struct st_request *SRpnt, const unsigned char *cmd,
        struct rq_map_data *mdata = &SRpnt->stp->buffer->map_data;
        int err = 0;
        int write = (data_direction == DMA_TO_DEVICE);
+       struct scsi_tape *STp = SRpnt->stp;
 
        req = blk_get_request(SRpnt->stp->device->request_queue, write,
                              GFP_KERNEL);
@@ -516,6 +560,17 @@ static int st_scsi_execute(struct st_request *SRpnt, const unsigned char *cmd,
                }
        }
 
+       atomic64_inc(&STp->stats->in_flight);
+       if (cmd[0] == WRITE_6) {
+               atomic_set(&STp->stats->last_write_size, bufflen);
+               STp->stats->write_time = ktime_get();
+       } else if (cmd[0] == READ_6) {
+               atomic_set(&STp->stats->last_read_size, bufflen);
+               STp->stats->read_time = ktime_get();
+       } else {
+               STp->stats->other_time = ktime_get();
+       }
+
        SRpnt->bio = req->bio;
        req->cmd_len = COMMAND_SIZE(cmd[0]);
        memset(req->cmd, 0, BLK_MAX_CDB);
@@ -4222,6 +4277,12 @@ static int st_probe(struct device *dev)
        }
        tpnt->index = error;
        sprintf(disk->disk_name, "st%d", tpnt->index);
+       tpnt->stats = kzalloc(sizeof(struct scsi_tape_stats), GFP_KERNEL);
+       if (tpnt->stats == NULL) {
+               sdev_printk(KERN_ERR, SDp,
+                           "st: Can't allocate statistics.\n");
+               goto out_idr_remove;
+       }
 
        dev_set_drvdata(dev, tpnt);
 
@@ -4241,6 +4302,8 @@ static int st_probe(struct device *dev)
 
 out_remove_devs:
        remove_cdevs(tpnt);
+       kfree(tpnt->stats);
+out_idr_remove:
        spin_lock(&st_index_lock);
        idr_remove(&st_index_idr, tpnt->index);
        spin_unlock(&st_index_lock);
@@ -4298,6 +4361,7 @@ static void scsi_tape_release(struct kref *kref)
 
        disk->private_data = NULL;
        put_disk(disk);
+       kfree(tpnt->stats);
        kfree(tpnt);
        return;
 }
@@ -4513,6 +4577,184 @@ options_show(struct device *dev, struct device_attribute *attr, char *buf)
 }
 static DEVICE_ATTR_RO(options);
 
+/* Support for tape stats */
+
+/**
+ * read_cnt_show - return read count - count of reads made from tape drive
+ * @dev: struct device
+ * @attr: attribute structure
+ * @buf: buffer to return formatted data in
+ */
+static ssize_t read_cnt_show(struct device *dev,
+       struct device_attribute *attr, char *buf)
+{
+       struct st_modedef *STm = dev_get_drvdata(dev);
+
+       return sprintf(buf, "%lld",
+                      (long long)atomic64_read(&STm->tape->stats->read_cnt));
+}
+static DEVICE_ATTR_RO(read_cnt);
+
+/**
+ * read_byte_cnt_show - return read byte count - tape drives
+ * may use blocks less than 512 bytes this gives the raw byte count of
+ * of data read from the tape drive.
+ * @dev: struct device
+ * @attr: attribute structure
+ * @buf: buffer to return formatted data in
+ */
+static ssize_t read_byte_cnt_show(struct device *dev,
+       struct device_attribute *attr, char *buf)
+{
+       struct st_modedef *STm = dev_get_drvdata(dev);
+
+       return sprintf(buf, "%lld",
+                      (long long)atomic64_read(&STm->tape->stats->read_byte_cnt));
+}
+static DEVICE_ATTR_RO(read_byte_cnt);
+
+/**
+ * read_us_show - return read us - overall time spent waiting on reads in ns.
+ * @dev: struct device
+ * @attr: attribute structure
+ * @buf: buffer to return formatted data in
+ */
+static ssize_t read_ns_show(struct device *dev,
+       struct device_attribute *attr, char *buf)
+{
+       struct st_modedef *STm = dev_get_drvdata(dev);
+
+       return sprintf(buf, "%lld",
+                      (long long)atomic64_read(&STm->tape->stats->tot_read_time));
+}
+static DEVICE_ATTR_RO(read_ns);
+
+/**
+ * write_cnt_show - write count - number of user calls
+ * to write(2) that have written data to tape.
+ * @dev: struct device
+ * @attr: attribute structure
+ * @buf: buffer to return formatted data in
+ */
+static ssize_t write_cnt_show(struct device *dev,
+       struct device_attribute *attr, char *buf)
+{
+       struct st_modedef *STm = dev_get_drvdata(dev);
+
+       return sprintf(buf, "%lld",
+                      (long long)atomic64_read(&STm->tape->stats->write_cnt));
+}
+static DEVICE_ATTR_RO(write_cnt);
+
+/**
+ * write_byte_cnt_show - write byte count - raw count of
+ * bytes written to tape.
+ * @dev: struct device
+ * @attr: attribute structure
+ * @buf: buffer to return formatted data in
+ */
+static ssize_t write_byte_cnt_show(struct device *dev,
+       struct device_attribute *attr, char *buf)
+{
+       struct st_modedef *STm = dev_get_drvdata(dev);
+
+       return sprintf(buf, "%lld",
+                      (long long)atomic64_read(&STm->tape->stats->write_byte_cnt));
+}
+static DEVICE_ATTR_RO(write_byte_cnt);
+
+/**
+ * write_ns_show - write ns - number of nanoseconds waiting on write
+ * requests to complete.
+ * @dev: struct device
+ * @attr: attribute structure
+ * @buf: buffer to return formatted data in
+ */
+static ssize_t write_ns_show(struct device *dev,
+       struct device_attribute *attr, char *buf)
+{
+       struct st_modedef *STm = dev_get_drvdata(dev);
+
+       return sprintf(buf, "%lld",
+                      (long long)atomic64_read(&STm->tape->stats->tot_write_time));
+}
+static DEVICE_ATTR_RO(write_ns);
+
+/**
+ * in_flight_show - number of I/Os currently in flight -
+ * in most cases this will be either 0 or 1. It may be higher if someone
+ * has also issued other SCSI commands such as via an ioctl.
+ * @dev: struct device
+ * @attr: attribute structure
+ * @buf: buffer to return formatted data in
+ */
+static ssize_t in_flight_show(struct device *dev,
+       struct device_attribute *attr, char *buf)
+{
+       struct st_modedef *STm = dev_get_drvdata(dev);
+
+       return sprintf(buf, "%lld",
+                      (long long)atomic64_read(&STm->tape->stats->in_flight));
+}
+static DEVICE_ATTR_RO(in_flight);
+
+/**
+ * io_ns_show - io wait ns - this is the number of ns spent
+ * waiting on all I/O to complete. This includes tape movement commands
+ * such as rewinding, seeking to end of file or tape, it also includes
+ * read and write. To determine the time spent on tape movement
+ * subtract the read and write ns from this value.
+ * @dev: struct device
+ * @attr: attribute structure
+ * @buf: buffer to return formatted data in
+ */
+static ssize_t io_ns_show(struct device *dev,
+       struct device_attribute *attr, char *buf)
+{
+       struct st_modedef *STm = dev_get_drvdata(dev);
+
+       return sprintf(buf, "%lld",
+                      (long long)atomic64_read(&STm->tape->stats->tot_io_time));
+}
+static DEVICE_ATTR_RO(io_ns);
+
+/**
+ * other_cnt_show - other io count - this is the number of
+ * I/O requests other than read and write requests.
+ * Typically these are tape movement requests but will include driver
+ * tape movement. This includes only requests issued by the st driver.
+ * @dev: struct device
+ * @attr: attribute structure
+ * @buf: buffer to return formatted data in
+ */
+static ssize_t other_cnt_show(struct device *dev,
+       struct device_attribute *attr, char *buf)
+{
+       struct st_modedef *STm = dev_get_drvdata(dev);
+
+       return sprintf(buf, "%lld",
+                      (long long)atomic64_read(&STm->tape->stats->other_cnt));
+}
+static DEVICE_ATTR_RO(other_cnt);
+
+/**
+ * resid_cnt_show - A count of the number of times we get a residual
+ * count - this should indicate someone issuing reads larger than the
+ * block size on tape.
+ * @dev: struct device
+ * @attr: attribute structure
+ * @buf: buffer to return formatted data in
+ */
+static ssize_t resid_cnt_show(struct device *dev,
+       struct device_attribute *attr, char *buf)
+{
+       struct st_modedef *STm = dev_get_drvdata(dev);
+
+       return sprintf(buf, "%lld",
+                      (long long)atomic64_read(&STm->tape->stats->resid_cnt));
+}
+static DEVICE_ATTR_RO(resid_cnt);
+
 static struct attribute *st_dev_attrs[] = {
        &dev_attr_defined.attr,
        &dev_attr_default_blksize.attr,
@@ -4521,7 +4763,35 @@ static struct attribute *st_dev_attrs[] = {
        &dev_attr_options.attr,
        NULL,
 };
-ATTRIBUTE_GROUPS(st_dev);
+
+static struct attribute *st_stats_attrs[] = {
+       &dev_attr_read_cnt.attr,
+       &dev_attr_read_byte_cnt.attr,
+       &dev_attr_read_ns.attr,
+       &dev_attr_write_cnt.attr,
+       &dev_attr_write_byte_cnt.attr,
+       &dev_attr_write_ns.attr,
+       &dev_attr_in_flight.attr,
+       &dev_attr_io_ns.attr,
+       &dev_attr_other_cnt.attr,
+       &dev_attr_resid_cnt.attr,
+       NULL,
+};
+
+static struct attribute_group stats_group = {
+       .name = "stats",
+       .attrs = st_stats_attrs,
+};
+
+static struct attribute_group st_group = {
+       .attrs = st_dev_attrs,
+};
+
+static const struct attribute_group *st_dev_groups[] = {
+       &st_group,
+       &stats_group,
+       NULL,
+};
 
 /* The following functions may be useful for a larger audience. */
 static int sgl_map_user_pages(struct st_buffer *STbp,
index f3eee0f9f40c01631ad3e62cab4d7c4c5c557f56..b6486b5d8681122759ae9bd3d5ca8e59542231a9 100644 (file)
@@ -92,6 +92,27 @@ struct st_partstat {
        int drv_file;
 };
 
+/* Tape statistics */
+struct scsi_tape_stats {
+       atomic64_t read_byte_cnt;  /* bytes read */
+       atomic64_t write_byte_cnt; /* bytes written */
+       atomic64_t in_flight;      /* Number of I/Os in flight */
+       atomic64_t read_cnt;       /* Count of read requests */
+       atomic64_t write_cnt;      /* Count of write requests */
+       atomic64_t other_cnt;      /* Count of other requests either
+                                   * implicit or from user space
+                                   * ioctl. */
+       atomic64_t resid_cnt;      /* Count of resid_len > 0 */
+       atomic64_t tot_read_time;  /* ktime spent completing reads */
+       atomic64_t tot_write_time; /* ktime spent completing writes */
+       atomic64_t tot_io_time;    /* ktime spent doing any I/O */
+       ktime_t read_time;         /* holds ktime request was queued */
+       ktime_t write_time;        /* holds ktime request was queued */
+       ktime_t other_time;        /* holds ktime request was queued */
+       atomic_t last_read_size;   /* Number of bytes issued for last read */
+       atomic_t last_write_size;  /* Number of bytes issued for last write */
+};
+
 #define ST_NBR_PARTITIONS 4
 
 /* The tape drive descriptor */
@@ -171,6 +192,7 @@ struct scsi_tape {
 #endif
        struct gendisk *disk;
        struct kref     kref;
+       struct scsi_tape_stats *stats;
 };
 
 /* Bit masks for use_pf */
index 0b7819f3e09b1736288a1827b5410c39efcdc437..5bdcbe8fa958dabb0785615eab99b3e107bf124b 100644 (file)
@@ -838,7 +838,6 @@ static struct scsi_host_template driver_template = {
        .can_queue =            1,
        .this_id =              SYM53C416_SCSI_ID,
        .sg_tablesize =         32,
-       .cmd_per_lun =          1,
        .unchecked_isa_dma =    1,
        .use_clustering =       ENABLE_CLUSTERING,
 };
index 8a1f4b355416ed693f04ca19fdfbe55fe847bdc8..e94538362536e4234b180201c7a70ae21bde7af1 100644 (file)
@@ -73,7 +73,7 @@ config SCSI_UFSHCD_PLATFORM
 
 config SCSI_UFS_QCOM
        bool "QCOM specific hooks to UFS controller platform driver"
-       depends on SCSI_UFSHCD_PLATFORM && ARCH_MSM
+       depends on SCSI_UFSHCD_PLATFORM && ARCH_QCOM
        select PHY_QCOM_UFS
        help
          This selects the QCOM specific additions to UFSHCD platform driver.
index 6652a8171de60c41e07299559d3e993cb3d5163e..4cdffa46d401a5ea6120ba5d350e453158166a31 100644 (file)
@@ -307,6 +307,7 @@ static int ufs_qcom_hce_enable_notify(struct ufs_hba *hba, bool status)
 static unsigned long
 ufs_qcom_cfg_timers(struct ufs_hba *hba, u32 gear, u32 hs, u32 rate)
 {
+       struct ufs_qcom_host *host = hba->priv;
        struct ufs_clk_info *clki;
        u32 core_clk_period_in_ns;
        u32 tx_clk_cycles_per_us = 0;
@@ -330,6 +331,16 @@ ufs_qcom_cfg_timers(struct ufs_hba *hba, u32 gear, u32 hs, u32 rate)
                {UFS_HS_G2, 0x49},
        };
 
+       /*
+        * The Qunipro controller does not use following registers:
+        * SYS1CLK_1US_REG, TX_SYMBOL_CLK_1US_REG, CLK_NS_REG &
+        * UFS_REG_PA_LINK_STARTUP_TIMER
+        * But UTP controller uses SYS1CLK_1US_REG register for Interrupt
+        * Aggregation logic.
+       */
+       if (ufs_qcom_cap_qunipro(host) && !ufshcd_is_intr_aggr_allowed(hba))
+               goto out;
+
        if (gear == 0) {
                dev_err(hba->dev, "%s: invalid gear = %d\n", __func__, gear);
                goto out_error;
@@ -683,6 +694,16 @@ out:
        return ret;
 }
 
+static u32 ufs_qcom_get_ufs_hci_version(struct ufs_hba *hba)
+{
+       struct ufs_qcom_host *host = hba->priv;
+
+       if (host->hw_ver.major == 0x1)
+               return UFSHCI_VERSION_11;
+       else
+               return UFSHCI_VERSION_20;
+}
+
 /**
  * ufs_qcom_advertise_quirks - advertise the known QCOM UFS controller quirks
  * @hba: host controller instance
@@ -696,13 +717,24 @@ static void ufs_qcom_advertise_quirks(struct ufs_hba *hba)
 {
        struct ufs_qcom_host *host = hba->priv;
 
-       if (host->hw_ver.major == 0x1)
-               hba->quirks |= UFSHCD_QUIRK_DELAY_BEFORE_DME_CMDS;
+       if (host->hw_ver.major == 0x01) {
+               hba->quirks |= UFSHCD_QUIRK_DELAY_BEFORE_DME_CMDS
+                           | UFSHCD_QUIRK_BROKEN_PA_RXHSUNTERMCAP
+                           | UFSHCD_QUIRK_DME_PEER_ACCESS_AUTO_MODE;
+
+               if (host->hw_ver.minor == 0x0001 && host->hw_ver.step == 0x0001)
+                       hba->quirks |= UFSHCD_QUIRK_BROKEN_INTR_AGGR;
+       }
 
        if (host->hw_ver.major >= 0x2) {
+               hba->quirks |= UFSHCD_QUIRK_BROKEN_LCC;
+               hba->quirks |= UFSHCD_QUIRK_BROKEN_UFS_HCI_VERSION;
+
                if (!ufs_qcom_cap_qunipro(host))
                        /* Legacy UniPro mode still need following quirks */
-                       hba->quirks |= UFSHCD_QUIRK_DELAY_BEFORE_DME_CMDS;
+                       hba->quirks |= (UFSHCD_QUIRK_DELAY_BEFORE_DME_CMDS
+                               | UFSHCD_QUIRK_DME_PEER_ACCESS_AUTO_MODE
+                               | UFSHCD_QUIRK_BROKEN_PA_RXHSUNTERMCAP);
        }
 }
 
@@ -1005,6 +1037,7 @@ static const struct ufs_hba_variant_ops ufs_hba_qcom_vops = {
        .name                   = "qcom",
        .init                   = ufs_qcom_init,
        .exit                   = ufs_qcom_exit,
+       .get_ufs_hci_version    = ufs_qcom_get_ufs_hci_version,
        .clk_scale_notify       = ufs_qcom_clk_scale_notify,
        .setup_clocks           = ufs_qcom_setup_clocks,
        .hce_enable_notify      = ufs_qcom_hce_enable_notify,
index 648a446758801ab6ff338d1dd9e5b5ab052df0da..b0ade73f8c6a66a6a5f4d32fcbc4ac266b43d7eb 100644 (file)
@@ -188,6 +188,8 @@ static int ufshcd_host_reset_and_restore(struct ufs_hba *hba);
 static irqreturn_t ufshcd_intr(int irq, void *__hba);
 static int ufshcd_config_pwr_mode(struct ufs_hba *hba,
                struct ufs_pa_layer_attr *desired_pwr_mode);
+static int ufshcd_change_power_mode(struct ufs_hba *hba,
+                            struct ufs_pa_layer_attr *pwr_mode);
 
 static inline int ufshcd_enable_irq(struct ufs_hba *hba)
 {
@@ -269,6 +271,11 @@ static inline u32 ufshcd_get_intr_mask(struct ufs_hba *hba)
  */
 static inline u32 ufshcd_get_ufs_version(struct ufs_hba *hba)
 {
+       if (hba->quirks & UFSHCD_QUIRK_BROKEN_UFS_HCI_VERSION) {
+               if (hba->vops && hba->vops->get_ufs_hci_version)
+                       return hba->vops->get_ufs_hci_version(hba);
+       }
+
        return ufshcd_readl(hba, REG_UFS_VERSION);
 }
 
@@ -480,6 +487,15 @@ ufshcd_config_intr_aggr(struct ufs_hba *hba, u8 cnt, u8 tmout)
                      REG_UTP_TRANSFER_REQ_INT_AGG_CONTROL);
 }
 
+/**
+ * ufshcd_disable_intr_aggr - Disables interrupt aggregation.
+ * @hba: per adapter instance
+ */
+static inline void ufshcd_disable_intr_aggr(struct ufs_hba *hba)
+{
+       ufshcd_writel(hba, 0, REG_UTP_TRANSFER_REQ_INT_AGG_CONTROL);
+}
+
 /**
  * ufshcd_enable_run_stop_reg - Enable run-stop registers,
  *                     When run-stop registers are set to 1, it indicates the
@@ -1326,7 +1342,7 @@ static int ufshcd_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd)
        lrbp->sense_buffer = cmd->sense_buffer;
        lrbp->task_tag = tag;
        lrbp->lun = ufshcd_scsi_to_upiu_lun(cmd->device->lun);
-       lrbp->intr_cmd = false;
+       lrbp->intr_cmd = !ufshcd_is_intr_aggr_allowed(hba) ? true : false;
        lrbp->command_type = UTP_CMD_TYPE_SCSI;
 
        /* form UPIU before issuing the command */
@@ -2147,6 +2163,31 @@ int ufshcd_dme_get_attr(struct ufs_hba *hba, u32 attr_sel,
        };
        const char *get = action[!!peer];
        int ret;
+       struct ufs_pa_layer_attr orig_pwr_info;
+       struct ufs_pa_layer_attr temp_pwr_info;
+       bool pwr_mode_change = false;
+
+       if (peer && (hba->quirks & UFSHCD_QUIRK_DME_PEER_ACCESS_AUTO_MODE)) {
+               orig_pwr_info = hba->pwr_info;
+               temp_pwr_info = orig_pwr_info;
+
+               if (orig_pwr_info.pwr_tx == FAST_MODE ||
+                   orig_pwr_info.pwr_rx == FAST_MODE) {
+                       temp_pwr_info.pwr_tx = FASTAUTO_MODE;
+                       temp_pwr_info.pwr_rx = FASTAUTO_MODE;
+                       pwr_mode_change = true;
+               } else if (orig_pwr_info.pwr_tx == SLOW_MODE ||
+                   orig_pwr_info.pwr_rx == SLOW_MODE) {
+                       temp_pwr_info.pwr_tx = SLOWAUTO_MODE;
+                       temp_pwr_info.pwr_rx = SLOWAUTO_MODE;
+                       pwr_mode_change = true;
+               }
+               if (pwr_mode_change) {
+                       ret = ufshcd_change_power_mode(hba, &temp_pwr_info);
+                       if (ret)
+                               goto out;
+               }
+       }
 
        uic_cmd.command = peer ?
                UIC_CMD_DME_PEER_GET : UIC_CMD_DME_GET;
@@ -2161,6 +2202,10 @@ int ufshcd_dme_get_attr(struct ufs_hba *hba, u32 attr_sel,
 
        if (mib_val)
                *mib_val = uic_cmd.argument3;
+
+       if (peer && (hba->quirks & UFSHCD_QUIRK_DME_PEER_ACCESS_AUTO_MODE)
+           && pwr_mode_change)
+               ufshcd_change_power_mode(hba, &orig_pwr_info);
 out:
        return ret;
 }
@@ -2249,6 +2294,16 @@ static int ufshcd_uic_change_pwr_mode(struct ufs_hba *hba, u8 mode)
        struct uic_command uic_cmd = {0};
        int ret;
 
+       if (hba->quirks & UFSHCD_QUIRK_BROKEN_PA_RXHSUNTERMCAP) {
+               ret = ufshcd_dme_set(hba,
+                               UIC_ARG_MIB_SEL(PA_RXHSUNTERMCAP, 0), 1);
+               if (ret) {
+                       dev_err(hba->dev, "%s: failed to enable PA_RXHSUNTERMCAP ret %d\n",
+                                               __func__, ret);
+                       goto out;
+               }
+       }
+
        uic_cmd.command = UIC_CMD_DME_SET;
        uic_cmd.argument1 = UIC_ARG_MIB(PA_PWRMODE);
        uic_cmd.argument3 = mode;
@@ -2256,6 +2311,7 @@ static int ufshcd_uic_change_pwr_mode(struct ufs_hba *hba, u8 mode)
        ret = ufshcd_uic_pwr_ctrl(hba, &uic_cmd);
        ufshcd_release(hba);
 
+out:
        return ret;
 }
 
@@ -2522,7 +2578,10 @@ static int ufshcd_make_hba_operational(struct ufs_hba *hba)
        ufshcd_enable_intr(hba, UFSHCD_ENABLE_INTRS);
 
        /* Configure interrupt aggregation */
-       ufshcd_config_intr_aggr(hba, hba->nutrs - 1, INT_AGGR_DEF_TO);
+       if (ufshcd_is_intr_aggr_allowed(hba))
+               ufshcd_config_intr_aggr(hba, hba->nutrs - 1, INT_AGGR_DEF_TO);
+       else
+               ufshcd_disable_intr_aggr(hba);
 
        /* Configure UTRL and UTMRL base address registers */
        ufshcd_writel(hba, lower_32_bits(hba->utrdl_dma_addr),
@@ -2628,6 +2687,42 @@ static int ufshcd_hba_enable(struct ufs_hba *hba)
        return 0;
 }
 
+static int ufshcd_disable_tx_lcc(struct ufs_hba *hba, bool peer)
+{
+       int tx_lanes, i, err = 0;
+
+       if (!peer)
+               ufshcd_dme_get(hba, UIC_ARG_MIB(PA_CONNECTEDTXDATALANES),
+                              &tx_lanes);
+       else
+               ufshcd_dme_peer_get(hba, UIC_ARG_MIB(PA_CONNECTEDTXDATALANES),
+                                   &tx_lanes);
+       for (i = 0; i < tx_lanes; i++) {
+               if (!peer)
+                       err = ufshcd_dme_set(hba,
+                               UIC_ARG_MIB_SEL(TX_LCC_ENABLE,
+                                       UIC_ARG_MPHY_TX_GEN_SEL_INDEX(i)),
+                                       0);
+               else
+                       err = ufshcd_dme_peer_set(hba,
+                               UIC_ARG_MIB_SEL(TX_LCC_ENABLE,
+                                       UIC_ARG_MPHY_TX_GEN_SEL_INDEX(i)),
+                                       0);
+               if (err) {
+                       dev_err(hba->dev, "%s: TX LCC Disable failed, peer = %d, lane = %d, err = %d",
+                               __func__, peer, i, err);
+                       break;
+               }
+       }
+
+       return err;
+}
+
+static inline int ufshcd_disable_device_tx_lcc(struct ufs_hba *hba)
+{
+       return ufshcd_disable_tx_lcc(hba, true);
+}
+
 /**
  * ufshcd_link_startup - Initialize unipro link startup
  * @hba: per adapter instance
@@ -2665,6 +2760,12 @@ static int ufshcd_link_startup(struct ufs_hba *hba)
                /* failed to get the link up... retire */
                goto out;
 
+       if (hba->quirks & UFSHCD_QUIRK_BROKEN_LCC) {
+               ret = ufshcd_disable_device_tx_lcc(hba);
+               if (ret)
+                       goto out;
+       }
+
        /* Include any host controller configuration via UIC commands */
        if (hba->vops && hba->vops->link_startup_notify) {
                ret = hba->vops->link_startup_notify(hba, POST_CHANGE);
@@ -3073,7 +3174,8 @@ static void ufshcd_transfer_req_compl(struct ufs_hba *hba)
         * false interrupt if device completes another request after resetting
         * aggregation and before reading the DB.
         */
-       ufshcd_reset_intr_aggr(hba);
+       if (ufshcd_is_intr_aggr_allowed(hba))
+               ufshcd_reset_intr_aggr(hba);
 
        tr_doorbell = ufshcd_readl(hba, REG_UTP_TRANSFER_REQ_DOOR_BELL);
        completed_reqs = tr_doorbell ^ hba->outstanding_reqs;
index b47ff07698e80aa9119633568324e009da5217be..c40a0e78a6c4298cf437dc312f0a136dd78abfa9 100644 (file)
@@ -246,6 +246,7 @@ struct ufs_pwr_mode_info {
  * @name: variant name
  * @init: called when the driver is initialized
  * @exit: called to cleanup everything done in init
+ * @get_ufs_hci_version: called to get UFS HCI version
  * @clk_scale_notify: notifies that clks are scaled up/down
  * @setup_clocks: called before touching any of the controller registers
  * @setup_regulators: called before accessing the host controller
@@ -263,6 +264,7 @@ struct ufs_hba_variant_ops {
        const char *name;
        int     (*init)(struct ufs_hba *);
        void    (*exit)(struct ufs_hba *);
+       u32     (*get_ufs_hci_version)(struct ufs_hba *);
        void    (*clk_scale_notify)(struct ufs_hba *);
        int     (*setup_clocks)(struct ufs_hba *, bool);
        int     (*setup_regulators)(struct ufs_hba *, bool);
@@ -417,11 +419,45 @@ struct ufs_hba {
        unsigned int irq;
        bool is_irq_enabled;
 
+       /* Interrupt aggregation support is broken */
+       #define UFSHCD_QUIRK_BROKEN_INTR_AGGR                   UFS_BIT(0)
+
        /*
         * delay before each dme command is required as the unipro
         * layer has shown instabilities
         */
-       #define UFSHCD_QUIRK_DELAY_BEFORE_DME_CMDS              UFS_BIT(0)
+       #define UFSHCD_QUIRK_DELAY_BEFORE_DME_CMDS              UFS_BIT(1)
+
+       /*
+        * If UFS host controller is having issue in processing LCC (Line
+        * Control Command) coming from device then enable this quirk.
+        * When this quirk is enabled, host controller driver should disable
+        * the LCC transmission on UFS device (by clearing TX_LCC_ENABLE
+        * attribute of device to 0).
+        */
+       #define UFSHCD_QUIRK_BROKEN_LCC                         UFS_BIT(2)
+
+       /*
+        * The attribute PA_RXHSUNTERMCAP specifies whether or not the
+        * inbound Link supports unterminated line in HS mode. Setting this
+        * attribute to 1 fixes moving to HS gear.
+        */
+       #define UFSHCD_QUIRK_BROKEN_PA_RXHSUNTERMCAP            UFS_BIT(3)
+
+       /*
+        * This quirk needs to be enabled if the host contoller only allows
+        * accessing the peer dme attributes in AUTO mode (FAST AUTO or
+        * SLOW AUTO).
+        */
+       #define UFSHCD_QUIRK_DME_PEER_ACCESS_AUTO_MODE          UFS_BIT(4)
+
+       /*
+        * This quirk needs to be enabled if the host contoller doesn't
+        * advertise the correct version in UFS_VER register. If this quirk
+        * is enabled, standard UFS host driver will call the vendor specific
+        * ops (get_ufs_hci_version) to get the correct version.
+        */
+       #define UFSHCD_QUIRK_BROKEN_UFS_HCI_VERSION             UFS_BIT(5)
 
        unsigned int quirks;    /* Deviations from standard UFSHCI spec. */
 
@@ -478,6 +514,12 @@ struct ufs_hba {
 #define UFSHCD_CAP_CLK_SCALING (1 << 2)
        /* Allow auto bkops to enabled during runtime suspend */
 #define UFSHCD_CAP_AUTO_BKOPS_SUSPEND (1 << 3)
+       /*
+        * This capability allows host controller driver to use the UFS HCI's
+        * interrupt aggregation capability.
+        * CAUTION: Enabling this might reduce overall UFS throughput.
+        */
+#define UFSHCD_CAP_INTR_AGGR (1 << 4)
 
        struct devfreq *devfreq;
        struct ufs_clk_scaling clk_scaling;
@@ -502,6 +544,15 @@ static inline bool ufshcd_can_autobkops_during_suspend(struct ufs_hba *hba)
        return hba->caps & UFSHCD_CAP_AUTO_BKOPS_SUSPEND;
 }
 
+static inline bool ufshcd_is_intr_aggr_allowed(struct ufs_hba *hba)
+{
+       if ((hba->caps & UFSHCD_CAP_INTR_AGGR) &&
+           !(hba->quirks & UFSHCD_QUIRK_BROKEN_INTR_AGGR))
+               return true;
+       else
+               return false;
+}
+
 #define ufshcd_writel(hba, val, reg)   \
        writel((val), (hba)->mmio_base + (reg))
 #define ufshcd_readl(hba, reg) \
index d5721199e9cc2a5fd2fd9d979869d5e49cb167ac..0ae0967aaed8c651830d138cc7e3adc40a6f826b 100644 (file)
@@ -89,8 +89,9 @@ enum {
 
 /* Controller UFSHCI version */
 enum {
-       UFSHCI_VERSION_10 = 0x00010000,
-       UFSHCI_VERSION_11 = 0x00010100,
+       UFSHCI_VERSION_10 = 0x00010000, /* 1.0 */
+       UFSHCI_VERSION_11 = 0x00010100, /* 1.1 */
+       UFSHCI_VERSION_20 = 0x00000200, /* 2.0 */
 };
 
 /*
@@ -206,6 +207,9 @@ enum {
 #define CONFIG_RESULT_CODE_MASK                0xFF
 #define GENERIC_ERROR_CODE_MASK                0xFF
 
+/* GenSelectorIndex calculation macros for M-PHY attributes */
+#define UIC_ARG_MPHY_TX_GEN_SEL_INDEX(lane) (lane)
+
 #define UIC_ARG_MIB_SEL(attr, sel)     ((((attr) & 0xFFFF) << 16) |\
                                         ((sel) & 0xFFFF))
 #define UIC_ARG_MIB(attr)              UIC_ARG_MIB_SEL(attr, 0)
index 3fc3e21b746b225276742e9e7403e0c71f6cbdb4..816a8a46efb888c3decd0b29877c9d8e6a0cecc4 100644 (file)
@@ -198,6 +198,14 @@ enum ufs_hs_gear_tag {
 #define T_TC0TXMAXSDUSIZE      0x4060
 #define T_TC1TXMAXSDUSIZE      0x4061
 
+#ifdef FALSE
+#undef FALSE
+#endif
+
+#ifdef TRUE
+#undef TRUE
+#endif
+
 /* Boolean attribute values */
 enum {
        FALSE = 0,
index f164f24a4a556cfe19c91f9031682a8ff6d9f0c5..285f77544c36391a89b243001c3fc93d0703f46f 100644 (file)
@@ -501,6 +501,7 @@ static void virtio_scsi_init_hdr(struct virtio_device *vdev,
        cmd->crn = 0;
 }
 
+#ifdef CONFIG_BLK_DEV_INTEGRITY
 static void virtio_scsi_init_hdr_pi(struct virtio_device *vdev,
                                    struct virtio_scsi_cmd_req_pi *cmd_pi,
                                    struct scsi_cmnd *sc)
@@ -524,6 +525,7 @@ static void virtio_scsi_init_hdr_pi(struct virtio_device *vdev,
                                                       blk_rq_sectors(rq) *
                                                       bi->tuple_size);
 }
+#endif
 
 static int virtscsi_queuecommand(struct virtio_scsi *vscsi,
                                 struct virtio_scsi_vq *req_vq,
@@ -546,11 +548,14 @@ static int virtscsi_queuecommand(struct virtio_scsi *vscsi,
 
        BUG_ON(sc->cmd_len > VIRTIO_SCSI_CDB_SIZE);
 
+#ifdef CONFIG_BLK_DEV_INTEGRITY
        if (virtio_has_feature(vscsi->vdev, VIRTIO_SCSI_F_T10_PI)) {
                virtio_scsi_init_hdr_pi(vscsi->vdev, &cmd->req.cmd_pi, sc);
                memcpy(cmd->req.cmd_pi.cdb, sc->cmnd, sc->cmd_len);
                req_size = sizeof(cmd->req.cmd_pi);
-       } else {
+       } else
+#endif
+       {
                virtio_scsi_init_hdr(vscsi->vdev, &cmd->req.cmd, sc);
                memcpy(cmd->req.cmd.cdb, sc->cmnd, sc->cmd_len);
                req_size = sizeof(cmd->req.cmd);
@@ -1002,6 +1007,7 @@ static int virtscsi_probe(struct virtio_device *vdev)
        shost->max_cmd_len = VIRTIO_SCSI_CDB_SIZE;
        shost->nr_hw_queues = num_queues;
 
+#ifdef CONFIG_BLK_DEV_INTEGRITY
        if (virtio_has_feature(vdev, VIRTIO_SCSI_F_T10_PI)) {
                host_prot = SHOST_DIF_TYPE1_PROTECTION | SHOST_DIF_TYPE2_PROTECTION |
                            SHOST_DIF_TYPE3_PROTECTION | SHOST_DIX_TYPE1_PROTECTION |
@@ -1010,6 +1016,7 @@ static int virtscsi_probe(struct virtio_device *vdev)
                scsi_host_set_prot(shost, host_prot);
                scsi_host_set_guard(shost, SHOST_DIX_GUARD_CRC);
        }
+#endif
 
        err = scsi_add_host(shost, &vdev->dev);
        if (err)
@@ -1090,7 +1097,9 @@ static struct virtio_device_id id_table[] = {
 static unsigned int features[] = {
        VIRTIO_SCSI_F_HOTPLUG,
        VIRTIO_SCSI_F_CHANGE,
+#ifdef CONFIG_BLK_DEV_INTEGRITY
        VIRTIO_SCSI_F_T10_PI,
+#endif
 };
 
 static struct virtio_driver virtio_scsi_driver = {
index 289ad016d92504e9d4ebbc47cf8d244ab6cb68b5..61346aa731785b417cf52a44e6a55003d7d9e7ac 100644 (file)
@@ -882,7 +882,6 @@ static struct scsi_host_template wd719x_template = {
        .can_queue                      = 255,
        .this_id                        = 7,
        .sg_tablesize                   = WD719X_SG,
-       .cmd_per_lun                    = WD719X_CMD_PER_LUN,
        .use_clustering                 = ENABLE_CLUSTERING,
 };
 
index 185e30e4eb93fb597d102e7e112a45b0ece2632e..9c6dd45f95f56f26fb83b6a18a023fa73aaaa009 100644 (file)
@@ -2,8 +2,6 @@
 #define _WD719X_H_
 
 #define WD719X_SG 255          /* Scatter/gather size */
-#define WD719X_CMD_PER_LUN 1   /* We should be able to do linked commands, but
-                                * this is 1 for now to be safe. */
 
 struct wd719x_sglist {
        __le32 ptr;
index fe8875f0d7be1155883655150ec57c3115356bbe..d3d1891cda3cf9a891b1714e240e73036109f2ac 100644 (file)
 #include <linux/bitmap.h>
 #include <linux/slab.h>
 
-#ifdef CONFIG_PM
-static int sh_pm_runtime_suspend(struct device *dev)
-{
-       int ret;
-
-       ret = pm_generic_runtime_suspend(dev);
-       if (ret) {
-               dev_err(dev, "failed to suspend device\n");
-               return ret;
-       }
-
-       ret = pm_clk_suspend(dev);
-       if (ret) {
-               dev_err(dev, "failed to suspend clock\n");
-               pm_generic_runtime_resume(dev);
-               return ret;
-       }
-
-       return 0;
-}
-
-static int sh_pm_runtime_resume(struct device *dev)
-{
-       int ret;
-
-       ret = pm_clk_resume(dev);
-       if (ret) {
-               dev_err(dev, "failed to resume clock\n");
-               return ret;
-       }
-
-       return pm_generic_runtime_resume(dev);
-}
-
 static struct dev_pm_domain default_pm_domain = {
        .ops = {
-               .runtime_suspend = sh_pm_runtime_suspend,
-               .runtime_resume = sh_pm_runtime_resume,
+               USE_PM_CLK_RUNTIME_OPS
                USE_PLATFORM_PM_SLEEP_OPS
        },
 };
 
-#define DEFAULT_PM_DOMAIN_PTR  (&default_pm_domain)
-
-#else
-
-#define DEFAULT_PM_DOMAIN_PTR  NULL
-
-#endif /* CONFIG_PM */
-
 static struct pm_clk_notifier_block platform_bus_notifier = {
-       .pm_domain = DEFAULT_PM_DOMAIN_PTR,
+       .pm_domain = &default_pm_domain,
        .con_ids = { NULL, },
 };
 
index bcdb22d5e215c9a393ccabe58f4f94ef132e3516..3c1850332a90212798ab5030554bc8fad39d9796 100644 (file)
@@ -4,6 +4,7 @@
 config MTK_PMIC_WRAP
        tristate "MediaTek PMIC Wrapper Support"
        depends on ARCH_MEDIATEK
+       depends on RESET_CONTROLLER
        select REGMAP
        help
          Say yes here to add support for MediaTek PMIC Wrapper found
index db5be1eec54c8db3977ea810e13c5470f416aaa7..f432291feee91e4b7c7b5ce3cc84f3b130933309 100644 (file)
@@ -443,11 +443,6 @@ static int pwrap_wait_for_state(struct pmic_wrapper *wrp,
 static int pwrap_write(struct pmic_wrapper *wrp, u32 adr, u32 wdata)
 {
        int ret;
-       u32 val;
-
-       val = pwrap_readl(wrp, PWRAP_WACS2_RDATA);
-       if (PWRAP_GET_WACS_FSM(val) == PWRAP_WACS_FSM_WFVLDCLR)
-               pwrap_writel(wrp, 1, PWRAP_WACS2_VLDCLR);
 
        ret = pwrap_wait_for_state(wrp, pwrap_is_fsm_idle);
        if (ret)
@@ -462,11 +457,6 @@ static int pwrap_write(struct pmic_wrapper *wrp, u32 adr, u32 wdata)
 static int pwrap_read(struct pmic_wrapper *wrp, u32 adr, u32 *rdata)
 {
        int ret;
-       u32 val;
-
-       val = pwrap_readl(wrp, PWRAP_WACS2_RDATA);
-       if (PWRAP_GET_WACS_FSM(val) == PWRAP_WACS_FSM_WFVLDCLR)
-               pwrap_writel(wrp, 1, PWRAP_WACS2_VLDCLR);
 
        ret = pwrap_wait_for_state(wrp, pwrap_is_fsm_idle);
        if (ret)
@@ -480,6 +470,8 @@ static int pwrap_read(struct pmic_wrapper *wrp, u32 adr, u32 *rdata)
 
        *rdata = PWRAP_GET_WACS_RDATA(pwrap_readl(wrp, PWRAP_WACS2_RDATA));
 
+       pwrap_writel(wrp, 1, PWRAP_WACS2_VLDCLR);
+
        return 0;
 }
 
@@ -563,45 +555,17 @@ static int pwrap_init_sidly(struct pmic_wrapper *wrp)
 
 static int pwrap_init_reg_clock(struct pmic_wrapper *wrp)
 {
-       unsigned long rate_spi;
-       int ck_mhz;
-
-       rate_spi = clk_get_rate(wrp->clk_spi);
-
-       if (rate_spi > 26000000)
-               ck_mhz = 26;
-       else if (rate_spi > 18000000)
-               ck_mhz = 18;
-       else
-               ck_mhz = 0;
-
-       switch (ck_mhz) {
-       case 18:
-               if (pwrap_is_mt8135(wrp))
-                       pwrap_writel(wrp, 0xc, PWRAP_CSHEXT);
-               pwrap_writel(wrp, 0x4, PWRAP_CSHEXT_WRITE);
-               pwrap_writel(wrp, 0xc, PWRAP_CSHEXT_READ);
-               pwrap_writel(wrp, 0x0, PWRAP_CSLEXT_START);
-               pwrap_writel(wrp, 0x0, PWRAP_CSLEXT_END);
-               break;
-       case 26:
-               if (pwrap_is_mt8135(wrp))
-                       pwrap_writel(wrp, 0x4, PWRAP_CSHEXT);
+       if (pwrap_is_mt8135(wrp)) {
+               pwrap_writel(wrp, 0x4, PWRAP_CSHEXT);
                pwrap_writel(wrp, 0x0, PWRAP_CSHEXT_WRITE);
                pwrap_writel(wrp, 0x4, PWRAP_CSHEXT_READ);
                pwrap_writel(wrp, 0x0, PWRAP_CSLEXT_START);
                pwrap_writel(wrp, 0x0, PWRAP_CSLEXT_END);
-               break;
-       case 0:
-               if (pwrap_is_mt8135(wrp))
-                       pwrap_writel(wrp, 0xf, PWRAP_CSHEXT);
-               pwrap_writel(wrp, 0xf, PWRAP_CSHEXT_WRITE);
-               pwrap_writel(wrp, 0xf, PWRAP_CSHEXT_READ);
-               pwrap_writel(wrp, 0xf, PWRAP_CSLEXT_START);
-               pwrap_writel(wrp, 0xf, PWRAP_CSLEXT_END);
-               break;
-       default:
-               return -EINVAL;
+       } else {
+               pwrap_writel(wrp, 0x0, PWRAP_CSHEXT_WRITE);
+               pwrap_writel(wrp, 0x4, PWRAP_CSHEXT_READ);
+               pwrap_writel(wrp, 0x2, PWRAP_CSLEXT_START);
+               pwrap_writel(wrp, 0x2, PWRAP_CSLEXT_END);
        }
 
        return 0;
index 72b059081559356100aa68dea2c021b056b492e5..0cae1694014dfbbaba171187fdbd4fc835dc1546 100644 (file)
@@ -77,6 +77,7 @@ config SPI_ATMEL
 
 config SPI_BCM2835
        tristate "BCM2835 SPI controller"
+       depends on GPIOLIB
        depends on ARCH_BCM2835 || COMPILE_TEST
        depends on GPIOLIB
        help
@@ -221,7 +222,7 @@ config SPI_FALCON
 
 config SPI_GPIO
        tristate "GPIO-based bitbanging SPI Master"
-       depends on GPIOLIB
+       depends on GPIOLIB || COMPILE_TEST
        select SPI_BITBANG
        help
          This simple GPIO bitbanging SPI master uses the arch-neutral GPIO
@@ -327,7 +328,7 @@ config SPI_MESON_SPIFC
 
 config SPI_OC_TINY
        tristate "OpenCores tiny SPI"
-       depends on GPIOLIB
+       depends on GPIOLIB || COMPILE_TEST
        select SPI_BITBANG
        help
          This is the driver for OpenCores tiny SPI master controller.
@@ -394,16 +395,9 @@ config SPI_PPC4xx
        help
          This selects a driver for the PPC4xx SPI Controller.
 
-config SPI_PXA2XX_PXADMA
-       bool "PXA2xx SSP legacy PXA DMA API support"
-       depends on SPI_PXA2XX && ARCH_PXA
-       help
-         Enable PXA private legacy DMA API support. Note that this is
-         deprecated in favor of generic DMA engine API.
-
 config SPI_PXA2XX_DMA
        def_bool y
-       depends on SPI_PXA2XX && !SPI_PXA2XX_PXADMA
+       depends on SPI_PXA2XX
 
 config SPI_PXA2XX
        tristate "PXA2xx SSP SPI master"
@@ -429,6 +423,12 @@ config SPI_ROCKCHIP
          The main usecase of this controller is to use spi flash as boot
          device.
 
+config SPI_RB4XX
+       tristate "Mikrotik RB4XX SPI master"
+       depends on SPI_MASTER && ATH79
+       help
+         SPI controller driver for the Mikrotik RB4xx series boards.
+
 config SPI_RSPI
        tristate "Renesas RSPI/QSPI controller"
        depends on SUPERH || ARCH_SHMOBILE || COMPILE_TEST
@@ -610,6 +610,12 @@ config SPI_XTENSA_XTFPGA
          16 bit words in SPI mode 0, automatically asserting CS on transfer
          start and deasserting on end.
 
+config SPI_ZYNQMP_GQSPI
+       tristate "Xilinx ZynqMP GQSPI controller"
+       depends on SPI_MASTER
+       help
+         Enables Xilinx GQSPI controller driver for Zynq UltraScale+ MPSoC.
+
 config SPI_NUC900
        tristate "Nuvoton NUC900 series SPI"
        depends on ARCH_W90X900
index d8cbf654976b5296aaa7561eeef6ba480631eb4f..1154dbac8f2c184b165f7f4f9c2392a2820f6c97 100644 (file)
@@ -60,12 +60,12 @@ obj-$(CONFIG_SPI_ORION)                     += spi-orion.o
 obj-$(CONFIG_SPI_PL022)                        += spi-pl022.o
 obj-$(CONFIG_SPI_PPC4xx)               += spi-ppc4xx.o
 spi-pxa2xx-platform-objs               := spi-pxa2xx.o
-spi-pxa2xx-platform-$(CONFIG_SPI_PXA2XX_PXADMA)        += spi-pxa2xx-pxadma.o
 spi-pxa2xx-platform-$(CONFIG_SPI_PXA2XX_DMA)   += spi-pxa2xx-dma.o
 obj-$(CONFIG_SPI_PXA2XX)               += spi-pxa2xx-platform.o
 obj-$(CONFIG_SPI_PXA2XX_PCI)           += spi-pxa2xx-pci.o
 obj-$(CONFIG_SPI_QUP)                  += spi-qup.o
 obj-$(CONFIG_SPI_ROCKCHIP)             += spi-rockchip.o
+obj-$(CONFIG_SPI_RB4XX)                        += spi-rb4xx.o
 obj-$(CONFIG_SPI_RSPI)                 += spi-rspi.o
 obj-$(CONFIG_SPI_S3C24XX)              += spi-s3c24xx-hw.o
 spi-s3c24xx-hw-y                       := spi-s3c24xx.o
@@ -89,3 +89,4 @@ obj-$(CONFIG_SPI_TXX9)                        += spi-txx9.o
 obj-$(CONFIG_SPI_XCOMM)                += spi-xcomm.o
 obj-$(CONFIG_SPI_XILINX)               += spi-xilinx.o
 obj-$(CONFIG_SPI_XTENSA_XTFPGA)                += spi-xtensa-xtfpga.o
+obj-$(CONFIG_SPI_ZYNQMP_GQSPI)         += spi-zynqmp-gqspi.o
index b02eb4ac02180e6cf4633d91590f99b6dbccc2c6..bf1f9b32c597b502f0d1c51b7f33f64a4996fb25 100644 (file)
@@ -79,10 +79,8 @@ static void ath79_spi_chipselect(struct spi_device *spi, int is_active)
        }
 
        if (spi->chip_select) {
-               struct ath79_spi_controller_data *cdata = spi->controller_data;
-
                /* SPI is normally active-low */
-               gpio_set_value(cdata->gpio, cs_high);
+               gpio_set_value(spi->cs_gpio, cs_high);
        } else {
                if (cs_high)
                        sp->ioc_base |= AR71XX_SPI_IOC_CS0;
@@ -117,11 +115,10 @@ static void ath79_spi_disable(struct ath79_spi *sp)
 
 static int ath79_spi_setup_cs(struct spi_device *spi)
 {
-       struct ath79_spi_controller_data *cdata;
+       struct ath79_spi *sp = ath79_spidev_to_sp(spi);
        int status;
 
-       cdata = spi->controller_data;
-       if (spi->chip_select && !cdata)
+       if (spi->chip_select && !gpio_is_valid(spi->cs_gpio))
                return -EINVAL;
 
        status = 0;
@@ -134,8 +131,15 @@ static int ath79_spi_setup_cs(struct spi_device *spi)
                else
                        flags |= GPIOF_INIT_HIGH;
 
-               status = gpio_request_one(cdata->gpio, flags,
+               status = gpio_request_one(spi->cs_gpio, flags,
                                          dev_name(&spi->dev));
+       } else {
+               if (spi->mode & SPI_CS_HIGH)
+                       sp->ioc_base &= ~AR71XX_SPI_IOC_CS0;
+               else
+                       sp->ioc_base |= AR71XX_SPI_IOC_CS0;
+
+               ath79_spi_wr(sp, AR71XX_SPI_REG_IOC, sp->ioc_base);
        }
 
        return status;
@@ -144,8 +148,7 @@ static int ath79_spi_setup_cs(struct spi_device *spi)
 static void ath79_spi_cleanup_cs(struct spi_device *spi)
 {
        if (spi->chip_select) {
-               struct ath79_spi_controller_data *cdata = spi->controller_data;
-               gpio_free(cdata->gpio);
+               gpio_free(spi->cs_gpio);
        }
 }
 
@@ -217,6 +220,7 @@ static int ath79_spi_probe(struct platform_device *pdev)
        }
 
        sp = spi_master_get_devdata(master);
+       master->dev.of_node = pdev->dev.of_node;
        platform_set_drvdata(pdev, sp);
 
        pdata = dev_get_platdata(&pdev->dev);
@@ -253,7 +257,7 @@ static int ath79_spi_probe(struct platform_device *pdev)
                goto err_put_master;
        }
 
-       ret = clk_enable(sp->clk);
+       ret = clk_prepare_enable(sp->clk);
        if (ret)
                goto err_put_master;
 
@@ -277,7 +281,7 @@ static int ath79_spi_probe(struct platform_device *pdev)
 err_disable:
        ath79_spi_disable(sp);
 err_clk_disable:
-       clk_disable(sp->clk);
+       clk_disable_unprepare(sp->clk);
 err_put_master:
        spi_master_put(sp->bitbang.master);
 
@@ -290,7 +294,7 @@ static int ath79_spi_remove(struct platform_device *pdev)
 
        spi_bitbang_stop(&sp->bitbang);
        ath79_spi_disable(sp);
-       clk_disable(sp->clk);
+       clk_disable_unprepare(sp->clk);
        spi_master_put(sp->bitbang.master);
 
        return 0;
@@ -301,12 +305,18 @@ static void ath79_spi_shutdown(struct platform_device *pdev)
        ath79_spi_remove(pdev);
 }
 
+static const struct of_device_id ath79_spi_of_match[] = {
+       { .compatible = "qca,ar7100-spi", },
+       { },
+};
+
 static struct platform_driver ath79_spi_driver = {
        .probe          = ath79_spi_probe,
        .remove         = ath79_spi_remove,
        .shutdown       = ath79_spi_shutdown,
        .driver         = {
                .name   = DRV_NAME,
+               .of_match_table = ath79_spi_of_match,
        },
 };
 module_platform_driver(ath79_spi_driver);
index a2f40b1b222500eb53d14c681ab58f3d3c445af8..c9eca347787db7ba5f85f6b1c4e10ad66e856abb 100644 (file)
@@ -41,6 +41,8 @@
 #define SPI_CSR1                               0x0034
 #define SPI_CSR2                               0x0038
 #define SPI_CSR3                               0x003c
+#define SPI_FMR                                        0x0040
+#define SPI_FLR                                        0x0044
 #define SPI_VERSION                            0x00fc
 #define SPI_RPR                                        0x0100
 #define SPI_RCR                                        0x0104
 #define SPI_SWRST_SIZE                         1
 #define SPI_LASTXFER_OFFSET                    24
 #define SPI_LASTXFER_SIZE                      1
+#define SPI_TXFCLR_OFFSET                      16
+#define SPI_TXFCLR_SIZE                                1
+#define SPI_RXFCLR_OFFSET                      17
+#define SPI_RXFCLR_SIZE                                1
+#define SPI_FIFOEN_OFFSET                      30
+#define SPI_FIFOEN_SIZE                                1
+#define SPI_FIFODIS_OFFSET                     31
+#define SPI_FIFODIS_SIZE                       1
 
 /* Bitfields in MR */
 #define SPI_MSTR_OFFSET                                0
 #define SPI_TXEMPTY_SIZE                       1
 #define SPI_SPIENS_OFFSET                      16
 #define SPI_SPIENS_SIZE                                1
+#define SPI_TXFEF_OFFSET                       24
+#define SPI_TXFEF_SIZE                         1
+#define SPI_TXFFF_OFFSET                       25
+#define SPI_TXFFF_SIZE                         1
+#define SPI_TXFTHF_OFFSET                      26
+#define SPI_TXFTHF_SIZE                                1
+#define SPI_RXFEF_OFFSET                       27
+#define SPI_RXFEF_SIZE                         1
+#define SPI_RXFFF_OFFSET                       28
+#define SPI_RXFFF_SIZE                         1
+#define SPI_RXFTHF_OFFSET                      29
+#define SPI_RXFTHF_SIZE                                1
+#define SPI_TXFPTEF_OFFSET                     30
+#define SPI_TXFPTEF_SIZE                       1
+#define SPI_RXFPTEF_OFFSET                     31
+#define SPI_RXFPTEF_SIZE                       1
 
 /* Bitfields in CSR0 */
 #define SPI_CPOL_OFFSET                                0
 #define SPI_TXTDIS_OFFSET                      9
 #define SPI_TXTDIS_SIZE                                1
 
+/* Bitfields in FMR */
+#define SPI_TXRDYM_OFFSET                      0
+#define SPI_TXRDYM_SIZE                                2
+#define SPI_RXRDYM_OFFSET                      4
+#define SPI_RXRDYM_SIZE                                2
+#define SPI_TXFTHRES_OFFSET                    16
+#define SPI_TXFTHRES_SIZE                      6
+#define SPI_RXFTHRES_OFFSET                    24
+#define SPI_RXFTHRES_SIZE                      6
+
+/* Bitfields in FLR */
+#define SPI_TXFL_OFFSET                                0
+#define SPI_TXFL_SIZE                          6
+#define SPI_RXFL_OFFSET                                16
+#define SPI_RXFL_SIZE                          6
+
 /* Constants for BITS */
 #define SPI_BITS_8_BPT                         0
 #define SPI_BITS_9_BPT                         1
 #define SPI_BITS_14_BPT                                6
 #define SPI_BITS_15_BPT                                7
 #define SPI_BITS_16_BPT                                8
+#define SPI_ONE_DATA                           0
+#define SPI_TWO_DATA                           1
+#define SPI_FOUR_DATA                          2
 
 /* Bit manipulation macros */
 #define SPI_BIT(name) \
        __raw_readl((port)->regs + SPI_##reg)
 #define spi_writel(port, reg, value) \
        __raw_writel((value), (port)->regs + SPI_##reg)
+
+#define spi_readw(port, reg) \
+       __raw_readw((port)->regs + SPI_##reg)
+#define spi_writew(port, reg, value) \
+       __raw_writew((value), (port)->regs + SPI_##reg)
+
+#define spi_readb(port, reg) \
+       __raw_readb((port)->regs + SPI_##reg)
+#define spi_writeb(port, reg, value) \
+       __raw_writeb((value), (port)->regs + SPI_##reg)
 #else
 #define spi_readl(port, reg) \
        readl_relaxed((port)->regs + SPI_##reg)
 #define spi_writel(port, reg, value) \
        writel_relaxed((value), (port)->regs + SPI_##reg)
+
+#define spi_readw(port, reg) \
+       readw_relaxed((port)->regs + SPI_##reg)
+#define spi_writew(port, reg, value) \
+       writew_relaxed((value), (port)->regs + SPI_##reg)
+
+#define spi_readb(port, reg) \
+       readb_relaxed((port)->regs + SPI_##reg)
+#define spi_writeb(port, reg, value) \
+       writeb_relaxed((value), (port)->regs + SPI_##reg)
 #endif
 /* use PIO for small transfers, avoiding DMA setup/teardown overhead and
  * cache operations; better heuristics consider wordsize and bitrate.
@@ -246,11 +311,14 @@ struct atmel_spi {
 
        bool                    use_dma;
        bool                    use_pdc;
+       bool                    use_cs_gpios;
        /* dmaengine data */
        struct atmel_spi_dma    dma;
 
        bool                    keep_cs;
        bool                    cs_active;
+
+       u32                     fifo_size;
 };
 
 /* Controller-specific per-slave state */
@@ -321,7 +389,8 @@ static void cs_activate(struct atmel_spi *as, struct spi_device *spi)
                }
 
                mr = spi_readl(as, MR);
-               gpio_set_value(asd->npcs_pin, active);
+               if (as->use_cs_gpios)
+                       gpio_set_value(asd->npcs_pin, active);
        } else {
                u32 cpol = (spi->mode & SPI_CPOL) ? SPI_BIT(CPOL) : 0;
                int i;
@@ -337,7 +406,7 @@ static void cs_activate(struct atmel_spi *as, struct spi_device *spi)
 
                mr = spi_readl(as, MR);
                mr = SPI_BFINS(PCS, ~(1 << spi->chip_select), mr);
-               if (spi->chip_select != 0)
+               if (as->use_cs_gpios && spi->chip_select != 0)
                        gpio_set_value(asd->npcs_pin, active);
                spi_writel(as, MR, mr);
        }
@@ -366,7 +435,9 @@ static void cs_deactivate(struct atmel_spi *as, struct spi_device *spi)
                        asd->npcs_pin, active ? " (low)" : "",
                        mr);
 
-       if (atmel_spi_is_v2(as) || spi->chip_select != 0)
+       if (!as->use_cs_gpios)
+               spi_writel(as, CR, SPI_BIT(LASTXFER));
+       else if (atmel_spi_is_v2(as) || spi->chip_select != 0)
                gpio_set_value(asd->npcs_pin, !active);
 }
 
@@ -406,6 +477,20 @@ static int atmel_spi_dma_slave_config(struct atmel_spi *as,
        slave_config->dst_maxburst = 1;
        slave_config->device_fc = false;
 
+       /*
+        * This driver uses fixed peripheral select mode (PS bit set to '0' in
+        * the Mode Register).
+        * So according to the datasheet, when FIFOs are available (and
+        * enabled), the Transmit FIFO operates in Multiple Data Mode.
+        * In this mode, up to 2 data, not 4, can be written into the Transmit
+        * Data Register in a single access.
+        * However, the first data has to be written into the lowest 16 bits and
+        * the second data into the highest 16 bits of the Transmit
+        * Data Register. For 8bit data (the most frequent case), it would
+        * require to rework tx_buf so each data would actualy fit 16 bits.
+        * So we'd rather write only one data at the time. Hence the transmit
+        * path works the same whether FIFOs are available (and enabled) or not.
+        */
        slave_config->direction = DMA_MEM_TO_DEV;
        if (dmaengine_slave_config(as->dma.chan_tx, slave_config)) {
                dev_err(&as->pdev->dev,
@@ -413,6 +498,14 @@ static int atmel_spi_dma_slave_config(struct atmel_spi *as,
                err = -EINVAL;
        }
 
+       /*
+        * This driver configures the spi controller for master mode (MSTR bit
+        * set to '1' in the Mode Register).
+        * So according to the datasheet, when FIFOs are available (and
+        * enabled), the Receive FIFO operates in Single Data Mode.
+        * So the receive path works the same whether FIFOs are available (and
+        * enabled) or not.
+        */
        slave_config->direction = DMA_DEV_TO_MEM;
        if (dmaengine_slave_config(as->dma.chan_rx, slave_config)) {
                dev_err(&as->pdev->dev,
@@ -502,10 +595,10 @@ static void dma_callback(void *data)
 }
 
 /*
- * Next transfer using PIO.
+ * Next transfer using PIO without FIFO.
  */
-static void atmel_spi_next_xfer_pio(struct spi_master *master,
-                               struct spi_transfer *xfer)
+static void atmel_spi_next_xfer_single(struct spi_master *master,
+                                      struct spi_transfer *xfer)
 {
        struct atmel_spi        *as = spi_master_get_devdata(master);
        unsigned long xfer_pos = xfer->len - as->current_remaining_bytes;
@@ -537,6 +630,99 @@ static void atmel_spi_next_xfer_pio(struct spi_master *master,
        spi_writel(as, IER, SPI_BIT(RDRF) | SPI_BIT(OVRES));
 }
 
+/*
+ * Next transfer using PIO with FIFO.
+ */
+static void atmel_spi_next_xfer_fifo(struct spi_master *master,
+                                    struct spi_transfer *xfer)
+{
+       struct atmel_spi *as = spi_master_get_devdata(master);
+       u32 current_remaining_data, num_data;
+       u32 offset = xfer->len - as->current_remaining_bytes;
+       const u16 *words = (const u16 *)((u8 *)xfer->tx_buf + offset);
+       const u8  *bytes = (const u8  *)((u8 *)xfer->tx_buf + offset);
+       u16 td0, td1;
+       u32 fifomr;
+
+       dev_vdbg(master->dev.parent, "atmel_spi_next_xfer_fifo\n");
+
+       /* Compute the number of data to transfer in the current iteration */
+       current_remaining_data = ((xfer->bits_per_word > 8) ?
+                                 ((u32)as->current_remaining_bytes >> 1) :
+                                 (u32)as->current_remaining_bytes);
+       num_data = min(current_remaining_data, as->fifo_size);
+
+       /* Flush RX and TX FIFOs */
+       spi_writel(as, CR, SPI_BIT(RXFCLR) | SPI_BIT(TXFCLR));
+       while (spi_readl(as, FLR))
+               cpu_relax();
+
+       /* Set RX FIFO Threshold to the number of data to transfer */
+       fifomr = spi_readl(as, FMR);
+       spi_writel(as, FMR, SPI_BFINS(RXFTHRES, num_data, fifomr));
+
+       /* Clear FIFO flags in the Status Register, especially RXFTHF */
+       (void)spi_readl(as, SR);
+
+       /* Fill TX FIFO */
+       while (num_data >= 2) {
+               if (xfer->tx_buf) {
+                       if (xfer->bits_per_word > 8) {
+                               td0 = *words++;
+                               td1 = *words++;
+                       } else {
+                               td0 = *bytes++;
+                               td1 = *bytes++;
+                       }
+               } else {
+                       td0 = 0;
+                       td1 = 0;
+               }
+
+               spi_writel(as, TDR, (td1 << 16) | td0);
+               num_data -= 2;
+       }
+
+       if (num_data) {
+               if (xfer->tx_buf) {
+                       if (xfer->bits_per_word > 8)
+                               td0 = *words++;
+                       else
+                               td0 = *bytes++;
+               } else {
+                       td0 = 0;
+               }
+
+               spi_writew(as, TDR, td0);
+               num_data--;
+       }
+
+       dev_dbg(master->dev.parent,
+               "  start fifo xfer %p: len %u tx %p rx %p bitpw %d\n",
+               xfer, xfer->len, xfer->tx_buf, xfer->rx_buf,
+               xfer->bits_per_word);
+
+       /*
+        * Enable RX FIFO Threshold Flag interrupt to be notified about
+        * transfer completion.
+        */
+       spi_writel(as, IER, SPI_BIT(RXFTHF) | SPI_BIT(OVRES));
+}
+
+/*
+ * Next transfer using PIO.
+ */
+static void atmel_spi_next_xfer_pio(struct spi_master *master,
+                                   struct spi_transfer *xfer)
+{
+       struct atmel_spi *as = spi_master_get_devdata(master);
+
+       if (as->fifo_size)
+               atmel_spi_next_xfer_fifo(master, xfer);
+       else
+               atmel_spi_next_xfer_single(master, xfer);
+}
+
 /*
  * Submit next transfer for DMA.
  */
@@ -839,13 +1025,8 @@ static void atmel_spi_disable_pdc_transfer(struct atmel_spi *as)
        spi_writel(as, PTCR, SPI_BIT(RXTDIS) | SPI_BIT(TXTDIS));
 }
 
-/* Called from IRQ
- *
- * Must update "current_remaining_bytes" to keep track of data
- * to transfer.
- */
 static void
-atmel_spi_pump_pio_data(struct atmel_spi *as, struct spi_transfer *xfer)
+atmel_spi_pump_single_data(struct atmel_spi *as, struct spi_transfer *xfer)
 {
        u8              *rxp;
        u16             *rxp16;
@@ -872,6 +1053,57 @@ atmel_spi_pump_pio_data(struct atmel_spi *as, struct spi_transfer *xfer)
        }
 }
 
+static void
+atmel_spi_pump_fifo_data(struct atmel_spi *as, struct spi_transfer *xfer)
+{
+       u32 fifolr = spi_readl(as, FLR);
+       u32 num_bytes, num_data = SPI_BFEXT(RXFL, fifolr);
+       u32 offset = xfer->len - as->current_remaining_bytes;
+       u16 *words = (u16 *)((u8 *)xfer->rx_buf + offset);
+       u8  *bytes = (u8  *)((u8 *)xfer->rx_buf + offset);
+       u16 rd; /* RD field is the lowest 16 bits of RDR */
+
+       /* Update the number of remaining bytes to transfer */
+       num_bytes = ((xfer->bits_per_word > 8) ?
+                    (num_data << 1) :
+                    num_data);
+
+       if (as->current_remaining_bytes > num_bytes)
+               as->current_remaining_bytes -= num_bytes;
+       else
+               as->current_remaining_bytes = 0;
+
+       /* Handle odd number of bytes when data are more than 8bit width */
+       if (xfer->bits_per_word > 8)
+               as->current_remaining_bytes &= ~0x1;
+
+       /* Read data */
+       while (num_data) {
+               rd = spi_readl(as, RDR);
+               if (xfer->rx_buf) {
+                       if (xfer->bits_per_word > 8)
+                               *words++ = rd;
+                       else
+                               *bytes++ = rd;
+               }
+               num_data--;
+       }
+}
+
+/* Called from IRQ
+ *
+ * Must update "current_remaining_bytes" to keep track of data
+ * to transfer.
+ */
+static void
+atmel_spi_pump_pio_data(struct atmel_spi *as, struct spi_transfer *xfer)
+{
+       if (as->fifo_size)
+               atmel_spi_pump_fifo_data(as, xfer);
+       else
+               atmel_spi_pump_single_data(as, xfer);
+}
+
 /* Interrupt
  *
  * No need for locking in this Interrupt handler: done_status is the
@@ -912,7 +1144,7 @@ atmel_spi_pio_interrupt(int irq, void *dev_id)
 
                complete(&as->xfer_completion);
 
-       } else if (pending & SPI_BIT(RDRF)) {
+       } else if (pending & (SPI_BIT(RDRF) | SPI_BIT(RXFTHF))) {
                atmel_spi_lock(as);
 
                if (as->current_remaining_bytes) {
@@ -996,6 +1228,8 @@ static int atmel_spi_setup(struct spi_device *spi)
                csr |= SPI_BIT(CPOL);
        if (!(spi->mode & SPI_CPHA))
                csr |= SPI_BIT(NCPHA);
+       if (!as->use_cs_gpios)
+               csr |= SPI_BIT(CSAAT);
 
        /* DLYBS is mostly irrelevant since we manage chipselect using GPIOs.
         *
@@ -1009,7 +1243,9 @@ static int atmel_spi_setup(struct spi_device *spi)
        /* chipselect must have been muxed as GPIO (e.g. in board setup) */
        npcs_pin = (unsigned long)spi->controller_data;
 
-       if (gpio_is_valid(spi->cs_gpio))
+       if (!as->use_cs_gpios)
+               npcs_pin = spi->chip_select;
+       else if (gpio_is_valid(spi->cs_gpio))
                npcs_pin = spi->cs_gpio;
 
        asd = spi->controller_state;
@@ -1018,15 +1254,19 @@ static int atmel_spi_setup(struct spi_device *spi)
                if (!asd)
                        return -ENOMEM;
 
-               ret = gpio_request(npcs_pin, dev_name(&spi->dev));
-               if (ret) {
-                       kfree(asd);
-                       return ret;
+               if (as->use_cs_gpios) {
+                       ret = gpio_request(npcs_pin, dev_name(&spi->dev));
+                       if (ret) {
+                               kfree(asd);
+                               return ret;
+                       }
+
+                       gpio_direction_output(npcs_pin,
+                                             !(spi->mode & SPI_CS_HIGH));
                }
 
                asd->npcs_pin = npcs_pin;
                spi->controller_state = asd;
-               gpio_direction_output(npcs_pin, !(spi->mode & SPI_CS_HIGH));
        }
 
        asd->csr = csr;
@@ -1338,6 +1578,13 @@ static int atmel_spi_probe(struct platform_device *pdev)
 
        atmel_get_caps(as);
 
+       as->use_cs_gpios = true;
+       if (atmel_spi_is_v2(as) &&
+           !of_get_property(pdev->dev.of_node, "cs-gpios", NULL)) {
+               as->use_cs_gpios = false;
+               master->num_chipselect = 4;
+       }
+
        as->use_dma = false;
        as->use_pdc = false;
        if (as->caps.has_dma_support) {
@@ -1380,6 +1627,13 @@ static int atmel_spi_probe(struct platform_device *pdev)
                spi_writel(as, PTCR, SPI_BIT(RXTDIS) | SPI_BIT(TXTDIS));
        spi_writel(as, CR, SPI_BIT(SPIEN));
 
+       as->fifo_size = 0;
+       if (!of_property_read_u32(pdev->dev.of_node, "atmel,fifo-size",
+                                 &as->fifo_size)) {
+               dev_info(&pdev->dev, "Using FIFO (%u data)\n", as->fifo_size);
+               spi_writel(as, CR, SPI_BIT(FIFOEN));
+       }
+
        /* go! */
        dev_info(&pdev->dev, "Atmel SPI Controller at 0x%08lx (irq %d)\n",
                        (unsigned long)regs->start, irq);
index 37875cf942f7b928c5d31f44345b5c060f182b36..59705ab23577494b68a136ca0f3c377b3371ca2b 100644 (file)
  * GNU General Public License for more details.
  */
 
+#include <asm/page.h>
 #include <linux/clk.h>
 #include <linux/completion.h>
 #include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
 #include <linux/err.h>
 #include <linux/interrupt.h>
 #include <linux/io.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/of.h>
-#include <linux/of_irq.h>
-#include <linux/of_gpio.h>
+#include <linux/of_address.h>
 #include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/of_irq.h>
 #include <linux/spi/spi.h>
 
 /* SPI register offsets */
@@ -69,7 +73,8 @@
 #define BCM2835_SPI_CS_CS_01           0x00000001
 
 #define BCM2835_SPI_POLLING_LIMIT_US   30
-#define BCM2835_SPI_TIMEOUT_MS         30000
+#define BCM2835_SPI_POLLING_JIFFIES    2
+#define BCM2835_SPI_DMA_MIN_LENGTH     96
 #define BCM2835_SPI_MODE_BITS  (SPI_CPOL | SPI_CPHA | SPI_CS_HIGH \
                                | SPI_NO_CS | SPI_3WIRE)
 
@@ -83,6 +88,7 @@ struct bcm2835_spi {
        u8 *rx_buf;
        int tx_len;
        int rx_len;
+       bool dma_pending;
 };
 
 static inline u32 bcm2835_rd(struct bcm2835_spi *bs, unsigned reg)
@@ -128,12 +134,15 @@ static void bcm2835_spi_reset_hw(struct spi_master *master)
        /* Disable SPI interrupts and transfer */
        cs &= ~(BCM2835_SPI_CS_INTR |
                BCM2835_SPI_CS_INTD |
+               BCM2835_SPI_CS_DMAEN |
                BCM2835_SPI_CS_TA);
        /* and reset RX/TX FIFOS */
        cs |= BCM2835_SPI_CS_CLEAR_RX | BCM2835_SPI_CS_CLEAR_TX;
 
        /* and reset the SPI_HW */
        bcm2835_wr(bs, BCM2835_SPI_CS, cs);
+       /* as well as DLEN */
+       bcm2835_wr(bs, BCM2835_SPI_DLEN, 0);
 }
 
 static irqreturn_t bcm2835_spi_interrupt(int irq, void *dev_id)
@@ -157,42 +166,6 @@ static irqreturn_t bcm2835_spi_interrupt(int irq, void *dev_id)
        return IRQ_HANDLED;
 }
 
-static int bcm2835_spi_transfer_one_poll(struct spi_master *master,
-                                        struct spi_device *spi,
-                                        struct spi_transfer *tfr,
-                                        u32 cs,
-                                        unsigned long xfer_time_us)
-{
-       struct bcm2835_spi *bs = spi_master_get_devdata(master);
-       /* set timeout to 1 second of maximum polling */
-       unsigned long timeout = jiffies + HZ;
-
-       /* enable HW block without interrupts */
-       bcm2835_wr(bs, BCM2835_SPI_CS, cs | BCM2835_SPI_CS_TA);
-
-       /* loop until finished the transfer */
-       while (bs->rx_len) {
-               /* read from fifo as much as possible */
-               bcm2835_rd_fifo(bs);
-               /* fill in tx fifo as much as possible */
-               bcm2835_wr_fifo(bs);
-               /* if we still expect some data after the read,
-                * check for a possible timeout
-                */
-               if (bs->rx_len && time_after(jiffies, timeout)) {
-                       /* Transfer complete - reset SPI HW */
-                       bcm2835_spi_reset_hw(master);
-                       /* and return timeout */
-                       return -ETIMEDOUT;
-               }
-       }
-
-       /* Transfer complete - reset SPI HW */
-       bcm2835_spi_reset_hw(master);
-       /* and return without waiting for completion */
-       return 0;
-}
-
 static int bcm2835_spi_transfer_one_irq(struct spi_master *master,
                                        struct spi_device *spi,
                                        struct spi_transfer *tfr,
@@ -229,6 +202,329 @@ static int bcm2835_spi_transfer_one_irq(struct spi_master *master,
        return 1;
 }
 
+/*
+ * DMA support
+ *
+ * this implementation has currently a few issues in so far as it does
+ * not work arrount limitations of the HW.
+ *
+ * the main one being that DMA transfers are limited to 16 bit
+ * (so 0 to 65535 bytes) by the SPI HW due to BCM2835_SPI_DLEN
+ *
+ * also we currently assume that the scatter-gather fragments are
+ * all multiple of 4 (except the last) - otherwise we would need
+ * to reset the FIFO before subsequent transfers...
+ * this also means that tx/rx transfers sg's need to be of equal size!
+ *
+ * there may be a few more border-cases we may need to address as well
+ * but unfortunately this would mean splitting up the scatter-gather
+ * list making it slightly unpractical...
+ */
+static void bcm2835_spi_dma_done(void *data)
+{
+       struct spi_master *master = data;
+       struct bcm2835_spi *bs = spi_master_get_devdata(master);
+
+       /* reset fifo and HW */
+       bcm2835_spi_reset_hw(master);
+
+       /* and terminate tx-dma as we do not have an irq for it
+        * because when the rx dma will terminate and this callback
+        * is called the tx-dma must have finished - can't get to this
+        * situation otherwise...
+        */
+       dmaengine_terminate_all(master->dma_tx);
+
+       /* mark as no longer pending */
+       bs->dma_pending = 0;
+
+       /* and mark as completed */;
+       complete(&master->xfer_completion);
+}
+
+static int bcm2835_spi_prepare_sg(struct spi_master *master,
+                                 struct spi_transfer *tfr,
+                                 bool is_tx)
+{
+       struct dma_chan *chan;
+       struct scatterlist *sgl;
+       unsigned int nents;
+       enum dma_transfer_direction dir;
+       unsigned long flags;
+
+       struct dma_async_tx_descriptor *desc;
+       dma_cookie_t cookie;
+
+       if (is_tx) {
+               dir   = DMA_MEM_TO_DEV;
+               chan  = master->dma_tx;
+               nents = tfr->tx_sg.nents;
+               sgl   = tfr->tx_sg.sgl;
+               flags = 0 /* no  tx interrupt */;
+
+       } else {
+               dir   = DMA_DEV_TO_MEM;
+               chan  = master->dma_rx;
+               nents = tfr->rx_sg.nents;
+               sgl   = tfr->rx_sg.sgl;
+               flags = DMA_PREP_INTERRUPT;
+       }
+       /* prepare the channel */
+       desc = dmaengine_prep_slave_sg(chan, sgl, nents, dir, flags);
+       if (!desc)
+               return -EINVAL;
+
+       /* set callback for rx */
+       if (!is_tx) {
+               desc->callback = bcm2835_spi_dma_done;
+               desc->callback_param = master;
+       }
+
+       /* submit it to DMA-engine */
+       cookie = dmaengine_submit(desc);
+
+       return dma_submit_error(cookie);
+}
+
+static inline int bcm2835_check_sg_length(struct sg_table *sgt)
+{
+       int i;
+       struct scatterlist *sgl;
+
+       /* check that the sg entries are word-sized (except for last) */
+       for_each_sg(sgt->sgl, sgl, (int)sgt->nents - 1, i) {
+               if (sg_dma_len(sgl) % 4)
+                       return -EFAULT;
+       }
+
+       return 0;
+}
+
+static int bcm2835_spi_transfer_one_dma(struct spi_master *master,
+                                       struct spi_device *spi,
+                                       struct spi_transfer *tfr,
+                                       u32 cs)
+{
+       struct bcm2835_spi *bs = spi_master_get_devdata(master);
+       int ret;
+
+       /* check that the scatter gather segments are all a multiple of 4 */
+       if (bcm2835_check_sg_length(&tfr->tx_sg) ||
+           bcm2835_check_sg_length(&tfr->rx_sg)) {
+               dev_warn_once(&spi->dev,
+                             "scatter gather segment length is not a multiple of 4 - falling back to interrupt mode\n");
+               return bcm2835_spi_transfer_one_irq(master, spi, tfr, cs);
+       }
+
+       /* setup tx-DMA */
+       ret = bcm2835_spi_prepare_sg(master, tfr, true);
+       if (ret)
+               return ret;
+
+       /* start TX early */
+       dma_async_issue_pending(master->dma_tx);
+
+       /* mark as dma pending */
+       bs->dma_pending = 1;
+
+       /* set the DMA length */
+       bcm2835_wr(bs, BCM2835_SPI_DLEN, tfr->len);
+
+       /* start the HW */
+       bcm2835_wr(bs, BCM2835_SPI_CS,
+                  cs | BCM2835_SPI_CS_TA | BCM2835_SPI_CS_DMAEN);
+
+       /* setup rx-DMA late - to run transfers while
+        * mapping of the rx buffers still takes place
+        * this saves 10us or more.
+        */
+       ret = bcm2835_spi_prepare_sg(master, tfr, false);
+       if (ret) {
+               /* need to reset on errors */
+               dmaengine_terminate_all(master->dma_tx);
+               bcm2835_spi_reset_hw(master);
+               return ret;
+       }
+
+       /* start rx dma late */
+       dma_async_issue_pending(master->dma_rx);
+
+       /* wait for wakeup in framework */
+       return 1;
+}
+
+static bool bcm2835_spi_can_dma(struct spi_master *master,
+                               struct spi_device *spi,
+                               struct spi_transfer *tfr)
+{
+       /* only run for gpio_cs */
+       if (!gpio_is_valid(spi->cs_gpio))
+               return false;
+
+       /* we start DMA efforts only on bigger transfers */
+       if (tfr->len < BCM2835_SPI_DMA_MIN_LENGTH)
+               return false;
+
+       /* BCM2835_SPI_DLEN has defined a max transfer size as
+        * 16 bit, so max is 65535
+        * we can revisit this by using an alternative transfer
+        * method - ideally this would get done without any more
+        * interaction...
+        */
+       if (tfr->len > 65535) {
+               dev_warn_once(&spi->dev,
+                             "transfer size of %d too big for dma-transfer\n",
+                             tfr->len);
+               return false;
+       }
+
+       /* if we run rx/tx_buf with word aligned addresses then we are OK */
+       if ((((size_t)tfr->rx_buf & 3) == 0) &&
+           (((size_t)tfr->tx_buf & 3) == 0))
+               return true;
+
+       /* otherwise we only allow transfers within the same page
+        * to avoid wasting time on dma_mapping when it is not practical
+        */
+       if (((size_t)tfr->tx_buf & PAGE_MASK) + tfr->len > PAGE_SIZE) {
+               dev_warn_once(&spi->dev,
+                             "Unaligned spi tx-transfer bridging page\n");
+               return false;
+       }
+       if (((size_t)tfr->rx_buf & PAGE_MASK) + tfr->len > PAGE_SIZE) {
+               dev_warn_once(&spi->dev,
+                             "Unaligned spi tx-transfer bridging page\n");
+               return false;
+       }
+
+       /* return OK */
+       return true;
+}
+
+static void bcm2835_dma_release(struct spi_master *master)
+{
+       if (master->dma_tx) {
+               dmaengine_terminate_all(master->dma_tx);
+               dma_release_channel(master->dma_tx);
+               master->dma_tx = NULL;
+       }
+       if (master->dma_rx) {
+               dmaengine_terminate_all(master->dma_rx);
+               dma_release_channel(master->dma_rx);
+               master->dma_rx = NULL;
+       }
+}
+
+static void bcm2835_dma_init(struct spi_master *master, struct device *dev)
+{
+       struct dma_slave_config slave_config;
+       const __be32 *addr;
+       dma_addr_t dma_reg_base;
+       int ret;
+
+       /* base address in dma-space */
+       addr = of_get_address(master->dev.of_node, 0, NULL, NULL);
+       if (!addr) {
+               dev_err(dev, "could not get DMA-register address - not using dma mode\n");
+               goto err;
+       }
+       dma_reg_base = be32_to_cpup(addr);
+
+       /* get tx/rx dma */
+       master->dma_tx = dma_request_slave_channel(dev, "tx");
+       if (!master->dma_tx) {
+               dev_err(dev, "no tx-dma configuration found - not using dma mode\n");
+               goto err;
+       }
+       master->dma_rx = dma_request_slave_channel(dev, "rx");
+       if (!master->dma_rx) {
+               dev_err(dev, "no rx-dma configuration found - not using dma mode\n");
+               goto err_release;
+       }
+
+       /* configure DMAs */
+       slave_config.direction = DMA_MEM_TO_DEV;
+       slave_config.dst_addr = (u32)(dma_reg_base + BCM2835_SPI_FIFO);
+       slave_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+
+       ret = dmaengine_slave_config(master->dma_tx, &slave_config);
+       if (ret)
+               goto err_config;
+
+       slave_config.direction = DMA_DEV_TO_MEM;
+       slave_config.src_addr = (u32)(dma_reg_base + BCM2835_SPI_FIFO);
+       slave_config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+
+       ret = dmaengine_slave_config(master->dma_rx, &slave_config);
+       if (ret)
+               goto err_config;
+
+       /* all went well, so set can_dma */
+       master->can_dma = bcm2835_spi_can_dma;
+       master->max_dma_len = 65535; /* limitation by BCM2835_SPI_DLEN */
+       /* need to do TX AND RX DMA, so we need dummy buffers */
+       master->flags = SPI_MASTER_MUST_RX | SPI_MASTER_MUST_TX;
+
+       return;
+
+err_config:
+       dev_err(dev, "issue configuring dma: %d - not using DMA mode\n",
+               ret);
+err_release:
+       bcm2835_dma_release(master);
+err:
+       return;
+}
+
+static int bcm2835_spi_transfer_one_poll(struct spi_master *master,
+                                        struct spi_device *spi,
+                                        struct spi_transfer *tfr,
+                                        u32 cs,
+                                        unsigned long xfer_time_us)
+{
+       struct bcm2835_spi *bs = spi_master_get_devdata(master);
+       unsigned long timeout;
+
+       /* enable HW block without interrupts */
+       bcm2835_wr(bs, BCM2835_SPI_CS, cs | BCM2835_SPI_CS_TA);
+
+       /* fill in the fifo before timeout calculations
+        * if we are interrupted here, then the data is
+        * getting transferred by the HW while we are interrupted
+        */
+       bcm2835_wr_fifo(bs);
+
+       /* set the timeout */
+       timeout = jiffies + BCM2835_SPI_POLLING_JIFFIES;
+
+       /* loop until finished the transfer */
+       while (bs->rx_len) {
+               /* fill in tx fifo with remaining data */
+               bcm2835_wr_fifo(bs);
+
+               /* read from fifo as much as possible */
+               bcm2835_rd_fifo(bs);
+
+               /* if there is still data pending to read
+                * then check the timeout
+                */
+               if (bs->rx_len && time_after(jiffies, timeout)) {
+                       dev_dbg_ratelimited(&spi->dev,
+                                           "timeout period reached: jiffies: %lu remaining tx/rx: %d/%d - falling back to interrupt mode\n",
+                                           jiffies - timeout,
+                                           bs->tx_len, bs->rx_len);
+                       /* fall back to interrupt mode */
+                       return bcm2835_spi_transfer_one_irq(master, spi,
+                                                           tfr, cs);
+               }
+       }
+
+       /* Transfer complete - reset SPI HW */
+       bcm2835_spi_reset_hw(master);
+       /* and return without waiting for completion */
+       return 0;
+}
+
 static int bcm2835_spi_transfer_one(struct spi_master *master,
                                    struct spi_device *spi,
                                    struct spi_transfer *tfr)
@@ -288,12 +584,26 @@ static int bcm2835_spi_transfer_one(struct spi_master *master,
                return bcm2835_spi_transfer_one_poll(master, spi, tfr,
                                                     cs, xfer_time_us);
 
+       /* run in dma mode if conditions are right */
+       if (master->can_dma && bcm2835_spi_can_dma(master, spi, tfr))
+               return bcm2835_spi_transfer_one_dma(master, spi, tfr, cs);
+
+       /* run in interrupt-mode */
        return bcm2835_spi_transfer_one_irq(master, spi, tfr, cs);
 }
 
 static void bcm2835_spi_handle_err(struct spi_master *master,
                                   struct spi_message *msg)
 {
+       struct bcm2835_spi *bs = spi_master_get_devdata(master);
+
+       /* if an error occurred and we have an active dma, then terminate */
+       if (bs->dma_pending) {
+               dmaengine_terminate_all(master->dma_tx);
+               dmaengine_terminate_all(master->dma_rx);
+               bs->dma_pending = 0;
+       }
+       /* and reset */
        bcm2835_spi_reset_hw(master);
 }
 
@@ -463,6 +773,8 @@ static int bcm2835_spi_probe(struct platform_device *pdev)
                goto out_clk_disable;
        }
 
+       bcm2835_dma_init(master, &pdev->dev);
+
        /* initialise the hardware with the default polarities */
        bcm2835_wr(bs, BCM2835_SPI_CS,
                   BCM2835_SPI_CS_CLEAR_RX | BCM2835_SPI_CS_CLEAR_TX);
@@ -493,6 +805,8 @@ static int bcm2835_spi_remove(struct platform_device *pdev)
 
        clk_disable_unprepare(bs->clk);
 
+       bcm2835_dma_release(master);
+
        return 0;
 }
 
index 5e991065f5b0166437aceb3f313ed7bd65f9bcbd..987afebea09379507f2c8505e7f4c14acbde079c 100644 (file)
@@ -265,7 +265,7 @@ static inline int davinci_spi_get_prescale(struct davinci_spi *dspi,
 
        ret = DIV_ROUND_UP(clk_get_rate(dspi->clk), max_speed_hz);
 
-       if (ret < 3 || ret > 256)
+       if (ret < 1 || ret > 256)
                return -EINVAL;
 
        return ret - 1;
index 5fe54cda309f5e7523c39f44245e6374c134fb08..86bcdd68c1fe09f1640fc352cc2d94f0aa53566d 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/module.h>
 #include <linux/of.h>
 #include <linux/of_device.h>
+#include <linux/pinctrl/consumer.h>
 #include <linux/platform_device.h>
 #include <linux/pm_runtime.h>
 #include <linux/regmap.h>
@@ -47,6 +48,7 @@
 #define SPI_MCR_CLR_RXF        (1 << 10)
 
 #define SPI_TCR                        0x08
+#define SPI_TCR_GET_TCNT(x)    (((x) & 0xffff0000) >> 16)
 
 #define SPI_CTAR(x)            (0x0c + (((x) & 0x3) * 4))
 #define SPI_CTAR_FMSZ(x)       (((x) & 0x0000000f) << 27)
 
 #define SPI_SR                 0x2c
 #define SPI_SR_EOQF            0x10000000
+#define SPI_SR_TCFQF           0x80000000
 
 #define SPI_RSER               0x30
 #define SPI_RSER_EOQFE         0x10000000
+#define SPI_RSER_TCFQE         0x80000000
 
 #define SPI_PUSHR              0x34
 #define SPI_PUSHR_CONT         (1 << 31)
 #define SPI_CS_ASSERT          0x02
 #define SPI_CS_DROP            0x04
 
+#define SPI_TCR_TCNT_MAX       0x10000
+
 struct chip_data {
        u32 mcr_val;
        u32 ctar_val;
        u16 void_write_data;
 };
 
+enum dspi_trans_mode {
+       DSPI_EOQ_MODE = 0,
+       DSPI_TCFQ_MODE,
+};
+
+struct fsl_dspi_devtype_data {
+       enum dspi_trans_mode trans_mode;
+};
+
+static const struct fsl_dspi_devtype_data vf610_data = {
+       .trans_mode = DSPI_EOQ_MODE,
+};
+
+static const struct fsl_dspi_devtype_data ls1021a_v1_data = {
+       .trans_mode = DSPI_TCFQ_MODE,
+};
+
+static const struct fsl_dspi_devtype_data ls2085a_data = {
+       .trans_mode = DSPI_TCFQ_MODE,
+};
+
 struct fsl_dspi {
        struct spi_master       *master;
        struct platform_device  *pdev;
@@ -128,9 +155,12 @@ struct fsl_dspi {
        u8                      cs;
        u16                     void_write_data;
        u32                     cs_change;
+       struct fsl_dspi_devtype_data *devtype_data;
 
        wait_queue_head_t       waitq;
        u32                     waitflags;
+
+       u32                     spi_tcnt;
 };
 
 static inline int is_double_byte_mode(struct fsl_dspi *dspi)
@@ -213,63 +243,60 @@ static void ns_delay_scale(char *psc, char *sc, int delay_ns,
        }
 }
 
-static int dspi_transfer_write(struct fsl_dspi *dspi)
+static u32 dspi_data_to_pushr(struct fsl_dspi *dspi, int tx_word)
 {
-       int tx_count = 0;
-       int tx_word;
        u16 d16;
-       u8  d8;
-       u32 dspi_pushr = 0;
-       int first = 1;
 
-       tx_word = is_double_byte_mode(dspi);
+       if (!(dspi->dataflags & TRAN_STATE_TX_VOID))
+               d16 = tx_word ? *(u16 *)dspi->tx : *(u8 *)dspi->tx;
+       else
+               d16 = dspi->void_write_data;
 
-       /* If we are in word mode, but only have a single byte to transfer
-        * then switch to byte mode temporarily.  Will switch back at the
-        * end of the transfer.
-        */
-       if (tx_word && (dspi->len == 1)) {
-               dspi->dataflags |= TRAN_STATE_WORD_ODD_NUM;
-               regmap_update_bits(dspi->regmap, SPI_CTAR(dspi->cs),
-                               SPI_FRAME_BITS_MASK, SPI_FRAME_BITS(8));
-               tx_word = 0;
-       }
+       dspi->tx += tx_word + 1;
+       dspi->len -= tx_word + 1;
 
-       while (dspi->len && (tx_count < DSPI_FIFO_SIZE)) {
-               if (tx_word) {
-                       if (dspi->len == 1)
-                               break;
+       return  SPI_PUSHR_TXDATA(d16) |
+               SPI_PUSHR_PCS(dspi->cs) |
+               SPI_PUSHR_CTAS(dspi->cs) |
+               SPI_PUSHR_CONT;
+}
 
-                       if (!(dspi->dataflags & TRAN_STATE_TX_VOID)) {
-                               d16 = *(u16 *)dspi->tx;
-                               dspi->tx += 2;
-                       } else {
-                               d16 = dspi->void_write_data;
-                       }
+static void dspi_data_from_popr(struct fsl_dspi *dspi, int rx_word)
+{
+       u16 d;
+       unsigned int val;
 
-                       dspi_pushr = SPI_PUSHR_TXDATA(d16) |
-                               SPI_PUSHR_PCS(dspi->cs) |
-                               SPI_PUSHR_CTAS(dspi->cs) |
-                               SPI_PUSHR_CONT;
+       regmap_read(dspi->regmap, SPI_POPR, &val);
+       d = SPI_POPR_RXDATA(val);
 
-                       dspi->len -= 2;
-               } else {
-                       if (!(dspi->dataflags & TRAN_STATE_TX_VOID)) {
+       if (!(dspi->dataflags & TRAN_STATE_RX_VOID))
+               rx_word ? (*(u16 *)dspi->rx = d) : (*(u8 *)dspi->rx = d);
 
-                               d8 = *(u8 *)dspi->tx;
-                               dspi->tx++;
-                       } else {
-                               d8 = (u8)dspi->void_write_data;
-                       }
+       dspi->rx += rx_word + 1;
+}
 
-                       dspi_pushr = SPI_PUSHR_TXDATA(d8) |
-                               SPI_PUSHR_PCS(dspi->cs) |
-                               SPI_PUSHR_CTAS(dspi->cs) |
-                               SPI_PUSHR_CONT;
+static int dspi_eoq_write(struct fsl_dspi *dspi)
+{
+       int tx_count = 0;
+       int tx_word;
+       u32 dspi_pushr = 0;
+
+       tx_word = is_double_byte_mode(dspi);
 
-                       dspi->len--;
+       while (dspi->len && (tx_count < DSPI_FIFO_SIZE)) {
+               /* If we are in word mode, only have a single byte to transfer
+                * switch to byte mode temporarily.  Will switch back at the
+                * end of the transfer.
+                */
+               if (tx_word && (dspi->len == 1)) {
+                       dspi->dataflags |= TRAN_STATE_WORD_ODD_NUM;
+                       regmap_update_bits(dspi->regmap, SPI_CTAR(dspi->cs),
+                                       SPI_FRAME_BITS_MASK, SPI_FRAME_BITS(8));
+                       tx_word = 0;
                }
 
+               dspi_pushr = dspi_data_to_pushr(dspi, tx_word);
+
                if (dspi->len == 0 || tx_count == DSPI_FIFO_SIZE - 1) {
                        /* last transfer in the transfer */
                        dspi_pushr |= SPI_PUSHR_EOQ;
@@ -278,11 +305,6 @@ static int dspi_transfer_write(struct fsl_dspi *dspi)
                } else if (tx_word && (dspi->len == 1))
                        dspi_pushr |= SPI_PUSHR_EOQ;
 
-               if (first) {
-                       first = 0;
-                       dspi_pushr |= SPI_PUSHR_CTCNT; /* clear counter */
-               }
-
                regmap_write(dspi->regmap, SPI_PUSHR, dspi_pushr);
 
                tx_count++;
@@ -291,40 +313,55 @@ static int dspi_transfer_write(struct fsl_dspi *dspi)
        return tx_count * (tx_word + 1);
 }
 
-static int dspi_transfer_read(struct fsl_dspi *dspi)
+static int dspi_eoq_read(struct fsl_dspi *dspi)
 {
        int rx_count = 0;
        int rx_word = is_double_byte_mode(dspi);
-       u16 d;
 
        while ((dspi->rx < dspi->rx_end)
                        && (rx_count < DSPI_FIFO_SIZE)) {
-               if (rx_word) {
-                       unsigned int val;
+               if (rx_word && (dspi->rx_end - dspi->rx) == 1)
+                       rx_word = 0;
 
-                       if ((dspi->rx_end - dspi->rx) == 1)
-                               break;
+               dspi_data_from_popr(dspi, rx_word);
+               rx_count++;
+       }
 
-                       regmap_read(dspi->regmap, SPI_POPR, &val);
-                       d = SPI_POPR_RXDATA(val);
+       return rx_count;
+}
 
-                       if (!(dspi->dataflags & TRAN_STATE_RX_VOID))
-                               *(u16 *)dspi->rx = d;
-                       dspi->rx += 2;
+static int dspi_tcfq_write(struct fsl_dspi *dspi)
+{
+       int tx_word;
+       u32 dspi_pushr = 0;
 
-               } else {
-                       unsigned int val;
+       tx_word = is_double_byte_mode(dspi);
 
-                       regmap_read(dspi->regmap, SPI_POPR, &val);
-                       d = SPI_POPR_RXDATA(val);
-                       if (!(dspi->dataflags & TRAN_STATE_RX_VOID))
-                               *(u8 *)dspi->rx = d;
-                       dspi->rx++;
-               }
-               rx_count++;
+       if (tx_word && (dspi->len == 1)) {
+               dspi->dataflags |= TRAN_STATE_WORD_ODD_NUM;
+               regmap_update_bits(dspi->regmap, SPI_CTAR(dspi->cs),
+                               SPI_FRAME_BITS_MASK, SPI_FRAME_BITS(8));
+               tx_word = 0;
        }
 
-       return rx_count;
+       dspi_pushr = dspi_data_to_pushr(dspi, tx_word);
+
+       if ((dspi->cs_change) && (!dspi->len))
+               dspi_pushr &= ~SPI_PUSHR_CONT;
+
+       regmap_write(dspi->regmap, SPI_PUSHR, dspi_pushr);
+
+       return tx_word + 1;
+}
+
+static void dspi_tcfq_read(struct fsl_dspi *dspi)
+{
+       int rx_word = is_double_byte_mode(dspi);
+
+       if (rx_word && (dspi->rx_end - dspi->rx) == 1)
+               rx_word = 0;
+
+       dspi_data_from_popr(dspi, rx_word);
 }
 
 static int dspi_transfer_one_message(struct spi_master *master,
@@ -334,6 +371,12 @@ static int dspi_transfer_one_message(struct spi_master *master,
        struct spi_device *spi = message->spi;
        struct spi_transfer *transfer;
        int status = 0;
+       enum dspi_trans_mode trans_mode;
+       u32 spi_tcr;
+
+       regmap_read(dspi->regmap, SPI_TCR, &spi_tcr);
+       dspi->spi_tcnt = SPI_TCR_GET_TCNT(spi_tcr);
+
        message->actual_length = 0;
 
        list_for_each_entry(transfer, &message->transfers, transfer_list) {
@@ -341,10 +384,10 @@ static int dspi_transfer_one_message(struct spi_master *master,
                dspi->cur_msg = message;
                dspi->cur_chip = spi_get_ctldata(spi);
                dspi->cs = spi->chip_select;
+               dspi->cs_change = 0;
                if (dspi->cur_transfer->transfer_list.next
                                == &dspi->cur_msg->transfers)
-                       transfer->cs_change = 1;
-               dspi->cs_change = transfer->cs_change;
+                       dspi->cs_change = 1;
                dspi->void_write_data = dspi->cur_chip->void_write_data;
 
                dspi->dataflags = 0;
@@ -370,8 +413,22 @@ static int dspi_transfer_one_message(struct spi_master *master,
                        regmap_write(dspi->regmap, SPI_CTAR(dspi->cs),
                                        dspi->cur_chip->ctar_val);
 
-               regmap_write(dspi->regmap, SPI_RSER, SPI_RSER_EOQFE);
-               message->actual_length += dspi_transfer_write(dspi);
+               trans_mode = dspi->devtype_data->trans_mode;
+               switch (trans_mode) {
+               case DSPI_EOQ_MODE:
+                       regmap_write(dspi->regmap, SPI_RSER, SPI_RSER_EOQFE);
+                       dspi_eoq_write(dspi);
+                       break;
+               case DSPI_TCFQ_MODE:
+                       regmap_write(dspi->regmap, SPI_RSER, SPI_RSER_TCFQE);
+                       dspi_tcfq_write(dspi);
+                       break;
+               default:
+                       dev_err(&dspi->pdev->dev, "unsupported trans_mode %u\n",
+                               trans_mode);
+                       status = -EINVAL;
+                       goto out;
+               }
 
                if (wait_event_interruptible(dspi->waitq, dspi->waitflags))
                        dev_err(&dspi->pdev->dev, "wait transfer complete fail!\n");
@@ -381,6 +438,7 @@ static int dspi_transfer_one_message(struct spi_master *master,
                        udelay(transfer->delay_usecs);
        }
 
+out:
        message->status = status;
        spi_finalize_current_message(master);
 
@@ -460,27 +518,89 @@ static void dspi_cleanup(struct spi_device *spi)
 static irqreturn_t dspi_interrupt(int irq, void *dev_id)
 {
        struct fsl_dspi *dspi = (struct fsl_dspi *)dev_id;
-
        struct spi_message *msg = dspi->cur_msg;
+       enum dspi_trans_mode trans_mode;
+       u32 spi_sr, spi_tcr;
+       u32 spi_tcnt, tcnt_diff;
+       int tx_word;
 
-       regmap_write(dspi->regmap, SPI_SR, SPI_SR_EOQF);
-       dspi_transfer_read(dspi);
-
-       if (!dspi->len) {
+       regmap_read(dspi->regmap, SPI_SR, &spi_sr);
+       regmap_write(dspi->regmap, SPI_SR, spi_sr);
+
+
+       if (spi_sr & (SPI_SR_EOQF | SPI_SR_TCFQF)) {
+               tx_word = is_double_byte_mode(dspi);
+
+               regmap_read(dspi->regmap, SPI_TCR, &spi_tcr);
+               spi_tcnt = SPI_TCR_GET_TCNT(spi_tcr);
+               /*
+                * The width of SPI Transfer Counter in SPI_TCR is 16bits,
+                * so the max couner is 65535. When the counter reach 65535,
+                * it will wrap around, counter reset to zero.
+                * spi_tcnt my be less than dspi->spi_tcnt, it means the
+                * counter already wrapped around.
+                * SPI Transfer Counter is a counter of transmitted frames.
+                * The size of frame maybe two bytes.
+                */
+               tcnt_diff = ((spi_tcnt + SPI_TCR_TCNT_MAX) - dspi->spi_tcnt)
+                       % SPI_TCR_TCNT_MAX;
+               tcnt_diff *= (tx_word + 1);
                if (dspi->dataflags & TRAN_STATE_WORD_ODD_NUM)
-                       regmap_update_bits(dspi->regmap, SPI_CTAR(dspi->cs),
-                       SPI_FRAME_BITS_MASK, SPI_FRAME_BITS(16));
+                       tcnt_diff--;
+
+               msg->actual_length += tcnt_diff;
+
+               dspi->spi_tcnt = spi_tcnt;
+
+               trans_mode = dspi->devtype_data->trans_mode;
+               switch (trans_mode) {
+               case DSPI_EOQ_MODE:
+                       dspi_eoq_read(dspi);
+                       break;
+               case DSPI_TCFQ_MODE:
+                       dspi_tcfq_read(dspi);
+                       break;
+               default:
+                       dev_err(&dspi->pdev->dev, "unsupported trans_mode %u\n",
+                               trans_mode);
+                               return IRQ_HANDLED;
+               }
 
-               dspi->waitflags = 1;
-               wake_up_interruptible(&dspi->waitq);
-       } else
-               msg->actual_length += dspi_transfer_write(dspi);
+               if (!dspi->len) {
+                       if (dspi->dataflags & TRAN_STATE_WORD_ODD_NUM) {
+                               regmap_update_bits(dspi->regmap,
+                                                  SPI_CTAR(dspi->cs),
+                                                  SPI_FRAME_BITS_MASK,
+                                                  SPI_FRAME_BITS(16));
+                               dspi->dataflags &= ~TRAN_STATE_WORD_ODD_NUM;
+                       }
+
+                       dspi->waitflags = 1;
+                       wake_up_interruptible(&dspi->waitq);
+               } else {
+                       switch (trans_mode) {
+                       case DSPI_EOQ_MODE:
+                               dspi_eoq_write(dspi);
+                               break;
+                       case DSPI_TCFQ_MODE:
+                               dspi_tcfq_write(dspi);
+                               break;
+                       default:
+                               dev_err(&dspi->pdev->dev,
+                                       "unsupported trans_mode %u\n",
+                                       trans_mode);
+                       }
+               }
+       }
 
        return IRQ_HANDLED;
 }
 
 static const struct of_device_id fsl_dspi_dt_ids[] = {
-       { .compatible = "fsl,vf610-dspi", .data = NULL, },
+       { .compatible = "fsl,vf610-dspi", .data = (void *)&vf610_data, },
+       { .compatible = "fsl,ls1021a-v1.0-dspi",
+               .data = (void *)&ls1021a_v1_data, },
+       { .compatible = "fsl,ls2085a-dspi", .data = (void *)&ls2085a_data, },
        { /* sentinel */ }
 };
 MODULE_DEVICE_TABLE(of, fsl_dspi_dt_ids);
@@ -494,6 +614,8 @@ static int dspi_suspend(struct device *dev)
        spi_master_suspend(master);
        clk_disable_unprepare(dspi->clk);
 
+       pinctrl_pm_select_sleep_state(dev);
+
        return 0;
 }
 
@@ -502,6 +624,8 @@ static int dspi_resume(struct device *dev)
        struct spi_master *master = dev_get_drvdata(dev);
        struct fsl_dspi *dspi = spi_master_get_devdata(master);
 
+       pinctrl_pm_select_default_state(dev);
+
        clk_prepare_enable(dspi->clk);
        spi_master_resume(master);
 
@@ -526,6 +650,8 @@ static int dspi_probe(struct platform_device *pdev)
        struct resource *res;
        void __iomem *base;
        int ret = 0, cs_num, bus_num;
+       const struct of_device_id *of_id =
+                       of_match_device(fsl_dspi_dt_ids, &pdev->dev);
 
        master = spi_alloc_master(&pdev->dev, sizeof(struct fsl_dspi));
        if (!master)
@@ -559,6 +685,13 @@ static int dspi_probe(struct platform_device *pdev)
        }
        master->bus_num = bus_num;
 
+       dspi->devtype_data = (struct fsl_dspi_devtype_data *)of_id->data;
+       if (!dspi->devtype_data) {
+               dev_err(&pdev->dev, "can't get devtype_data\n");
+               ret = -EFAULT;
+               goto out_master_put;
+       }
+
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        base = devm_ioremap_resource(&pdev->dev, res);
        if (IS_ERR(base)) {
@@ -566,7 +699,7 @@ static int dspi_probe(struct platform_device *pdev)
                goto out_master_put;
        }
 
-       dspi->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "dspi", base,
+       dspi->regmap = devm_regmap_init_mmio_clk(&pdev->dev, NULL, base,
                                                &dspi_regmap_config);
        if (IS_ERR(dspi->regmap)) {
                dev_err(&pdev->dev, "failed to init regmap: %ld\n",
index 80d245ac846fa366abf8d2b7511a1fb4ac25c03e..d3f05a0525a428194e0cb792604eed8ac481bd00 100644 (file)
@@ -561,9 +561,13 @@ void fsl_espi_cpu_irq(struct mpc8xxx_spi *mspi, u32 events)
 
                /* spin until TX is done */
                ret = spin_event_timeout(((events = mpc8xxx_spi_read_reg(
-                               &reg_base->event)) & SPIE_NF) == 0, 1000, 0);
+                               &reg_base->event)) & SPIE_NF), 1000, 0);
                if (!ret) {
                        dev_err(mspi->dev, "tired waiting for SPIE_NF\n");
+
+                       /* Clear the SPIE bits */
+                       mpc8xxx_spi_write_reg(&reg_base->event, events);
+                       complete(&mspi->done);
                        return;
                }
        }
index f08e812b29847bd3ba5f44ab1c5b02aae8437258..eb7d3a6fb14c0694b55e5286933256c5962043ce 100644 (file)
@@ -674,7 +674,7 @@ static struct spi_imx_devtype_data imx51_ecspi_devtype_data = {
        .devtype = IMX51_ECSPI,
 };
 
-static struct platform_device_id spi_imx_devtype[] = {
+static const struct platform_device_id spi_imx_devtype[] = {
        {
                .name = "imx1-cspi",
                .driver_data = (kernel_ulong_t) &imx1_cspi_devtype_data,
index d1a5b9fc3eba22edaafd6155a5292ee108d97ac7..58673841286c335adc123d5200c1a0f128ecab04 100644 (file)
@@ -35,6 +35,7 @@
 #include <linux/gcd.h>
 
 #include <linux/spi/spi.h>
+#include <linux/gpio.h>
 
 #include <linux/platform_data/spi-omap2-mcspi.h>
 
@@ -242,17 +243,27 @@ static void omap2_mcspi_set_enable(const struct spi_device *spi, int enable)
        mcspi_read_cs_reg(spi, OMAP2_MCSPI_CHCTRL0);
 }
 
-static void omap2_mcspi_force_cs(struct spi_device *spi, int cs_active)
+static void omap2_mcspi_set_cs(struct spi_device *spi, bool enable)
 {
        u32 l;
 
-       l = mcspi_cached_chconf0(spi);
-       if (cs_active)
-               l |= OMAP2_MCSPI_CHCONF_FORCE;
-       else
-               l &= ~OMAP2_MCSPI_CHCONF_FORCE;
+       /* The controller handles the inverted chip selects
+        * using the OMAP2_MCSPI_CHCONF_EPOL bit so revert
+        * the inversion from the core spi_set_cs function.
+        */
+       if (spi->mode & SPI_CS_HIGH)
+               enable = !enable;
 
-       mcspi_write_chconf0(spi, l);
+       if (spi->controller_state) {
+               l = mcspi_cached_chconf0(spi);
+
+               if (enable)
+                       l &= ~OMAP2_MCSPI_CHCONF_FORCE;
+               else
+                       l |= OMAP2_MCSPI_CHCONF_FORCE;
+
+               mcspi_write_chconf0(spi, l);
+       }
 }
 
 static void omap2_mcspi_set_master_mode(struct spi_master *master)
@@ -1011,6 +1022,15 @@ static int omap2_mcspi_setup(struct spi_device *spi)
                        return ret;
        }
 
+       if (gpio_is_valid(spi->cs_gpio)) {
+               ret = gpio_request(spi->cs_gpio, dev_name(&spi->dev));
+               if (ret) {
+                       dev_err(&spi->dev, "failed to request gpio\n");
+                       return ret;
+               }
+               gpio_direction_output(spi->cs_gpio, !(spi->mode & SPI_CS_HIGH));
+       }
+
        ret = pm_runtime_get_sync(mcspi->dev);
        if (ret < 0)
                return ret;
@@ -1050,9 +1070,13 @@ static void omap2_mcspi_cleanup(struct spi_device *spi)
                        mcspi_dma->dma_tx = NULL;
                }
        }
+
+       if (gpio_is_valid(spi->cs_gpio))
+               gpio_free(spi->cs_gpio);
 }
 
-static void omap2_mcspi_work(struct omap2_mcspi *mcspi, struct spi_message *m)
+static int omap2_mcspi_work_one(struct omap2_mcspi *mcspi,
+               struct spi_device *spi, struct spi_transfer *t)
 {
 
        /* We only enable one channel at a time -- the one whose message is
@@ -1062,18 +1086,14 @@ static void omap2_mcspi_work(struct omap2_mcspi *mcspi, struct spi_message *m)
         * chipselect with the FORCE bit ... CS != channel enable.
         */
 
-       struct spi_device               *spi;
-       struct spi_transfer             *t = NULL;
        struct spi_master               *master;
        struct omap2_mcspi_dma          *mcspi_dma;
-       int                             cs_active = 0;
        struct omap2_mcspi_cs           *cs;
        struct omap2_mcspi_device_config *cd;
        int                             par_override = 0;
        int                             status = 0;
        u32                             chconf;
 
-       spi = m->spi;
        master = spi->master;
        mcspi_dma = mcspi->dma_channels + spi->chip_select;
        cs = spi->controller_state;
@@ -1090,103 +1110,84 @@ static void omap2_mcspi_work(struct omap2_mcspi *mcspi, struct spi_message *m)
                par_override = 1;
 
        omap2_mcspi_set_enable(spi, 0);
-       list_for_each_entry(t, &m->transfers, transfer_list) {
-               if (t->tx_buf == NULL && t->rx_buf == NULL && t->len) {
-                       status = -EINVAL;
-                       break;
-               }
-               if (par_override ||
-                   (t->speed_hz != spi->max_speed_hz) ||
-                   (t->bits_per_word != spi->bits_per_word)) {
-                       par_override = 1;
-                       status = omap2_mcspi_setup_transfer(spi, t);
-                       if (status < 0)
-                               break;
-                       if (t->speed_hz == spi->max_speed_hz &&
-                           t->bits_per_word == spi->bits_per_word)
-                               par_override = 0;
-               }
-               if (cd && cd->cs_per_word) {
-                       chconf = mcspi->ctx.modulctrl;
-                       chconf &= ~OMAP2_MCSPI_MODULCTRL_SINGLE;
-                       mcspi_write_reg(master, OMAP2_MCSPI_MODULCTRL, chconf);
-                       mcspi->ctx.modulctrl =
-                               mcspi_read_cs_reg(spi, OMAP2_MCSPI_MODULCTRL);
-               }
 
+       if (gpio_is_valid(spi->cs_gpio))
+               omap2_mcspi_set_cs(spi, spi->mode & SPI_CS_HIGH);
 
-               if (!cs_active) {
-                       omap2_mcspi_force_cs(spi, 1);
-                       cs_active = 1;
-               }
-
-               chconf = mcspi_cached_chconf0(spi);
-               chconf &= ~OMAP2_MCSPI_CHCONF_TRM_MASK;
-               chconf &= ~OMAP2_MCSPI_CHCONF_TURBO;
+       if (par_override ||
+           (t->speed_hz != spi->max_speed_hz) ||
+           (t->bits_per_word != spi->bits_per_word)) {
+               par_override = 1;
+               status = omap2_mcspi_setup_transfer(spi, t);
+               if (status < 0)
+                       goto out;
+               if (t->speed_hz == spi->max_speed_hz &&
+                   t->bits_per_word == spi->bits_per_word)
+                       par_override = 0;
+       }
+       if (cd && cd->cs_per_word) {
+               chconf = mcspi->ctx.modulctrl;
+               chconf &= ~OMAP2_MCSPI_MODULCTRL_SINGLE;
+               mcspi_write_reg(master, OMAP2_MCSPI_MODULCTRL, chconf);
+               mcspi->ctx.modulctrl =
+                       mcspi_read_cs_reg(spi, OMAP2_MCSPI_MODULCTRL);
+       }
 
-               if (t->tx_buf == NULL)
-                       chconf |= OMAP2_MCSPI_CHCONF_TRM_RX_ONLY;
-               else if (t->rx_buf == NULL)
-                       chconf |= OMAP2_MCSPI_CHCONF_TRM_TX_ONLY;
-
-               if (cd && cd->turbo_mode && t->tx_buf == NULL) {
-                       /* Turbo mode is for more than one word */
-                       if (t->len > ((cs->word_len + 7) >> 3))
-                               chconf |= OMAP2_MCSPI_CHCONF_TURBO;
-               }
+       chconf = mcspi_cached_chconf0(spi);
+       chconf &= ~OMAP2_MCSPI_CHCONF_TRM_MASK;
+       chconf &= ~OMAP2_MCSPI_CHCONF_TURBO;
+
+       if (t->tx_buf == NULL)
+               chconf |= OMAP2_MCSPI_CHCONF_TRM_RX_ONLY;
+       else if (t->rx_buf == NULL)
+               chconf |= OMAP2_MCSPI_CHCONF_TRM_TX_ONLY;
+
+       if (cd && cd->turbo_mode && t->tx_buf == NULL) {
+               /* Turbo mode is for more than one word */
+               if (t->len > ((cs->word_len + 7) >> 3))
+                       chconf |= OMAP2_MCSPI_CHCONF_TURBO;
+       }
 
-               mcspi_write_chconf0(spi, chconf);
+       mcspi_write_chconf0(spi, chconf);
 
-               if (t->len) {
-                       unsigned        count;
+       if (t->len) {
+               unsigned        count;
 
-                       if ((mcspi_dma->dma_rx && mcspi_dma->dma_tx) &&
-                           (m->is_dma_mapped || t->len >= DMA_MIN_BYTES))
-                               omap2_mcspi_set_fifo(spi, t, 1);
+               if ((mcspi_dma->dma_rx && mcspi_dma->dma_tx) &&
+                   (t->len >= DMA_MIN_BYTES))
+                       omap2_mcspi_set_fifo(spi, t, 1);
 
-                       omap2_mcspi_set_enable(spi, 1);
+               omap2_mcspi_set_enable(spi, 1);
 
-                       /* RX_ONLY mode needs dummy data in TX reg */
-                       if (t->tx_buf == NULL)
-                               writel_relaxed(0, cs->base
-                                               + OMAP2_MCSPI_TX0);
+               /* RX_ONLY mode needs dummy data in TX reg */
+               if (t->tx_buf == NULL)
+                       writel_relaxed(0, cs->base
+                                       + OMAP2_MCSPI_TX0);
 
-                       if ((mcspi_dma->dma_rx && mcspi_dma->dma_tx) &&
-                           (m->is_dma_mapped || t->len >= DMA_MIN_BYTES))
-                               count = omap2_mcspi_txrx_dma(spi, t);
-                       else
-                               count = omap2_mcspi_txrx_pio(spi, t);
-                       m->actual_length += count;
+               if ((mcspi_dma->dma_rx && mcspi_dma->dma_tx) &&
+                   (t->len >= DMA_MIN_BYTES))
+                       count = omap2_mcspi_txrx_dma(spi, t);
+               else
+                       count = omap2_mcspi_txrx_pio(spi, t);
 
-                       if (count != t->len) {
-                               status = -EIO;
-                               break;
-                       }
+               if (count != t->len) {
+                       status = -EIO;
+                       goto out;
                }
+       }
 
-               if (t->delay_usecs)
-                       udelay(t->delay_usecs);
-
-               /* ignore the "leave it on after last xfer" hint */
-               if (t->cs_change) {
-                       omap2_mcspi_force_cs(spi, 0);
-                       cs_active = 0;
-               }
+       omap2_mcspi_set_enable(spi, 0);
 
-               omap2_mcspi_set_enable(spi, 0);
+       if (mcspi->fifo_depth > 0)
+               omap2_mcspi_set_fifo(spi, t, 0);
 
-               if (mcspi->fifo_depth > 0)
-                       omap2_mcspi_set_fifo(spi, t, 0);
-       }
+out:
        /* Restore defaults if they were overriden */
        if (par_override) {
                par_override = 0;
                status = omap2_mcspi_setup_transfer(spi, NULL);
        }
 
-       if (cs_active)
-               omap2_mcspi_force_cs(spi, 0);
-
        if (cd && cd->cs_per_word) {
                chconf = mcspi->ctx.modulctrl;
                chconf |= OMAP2_MCSPI_MODULCTRL_SINGLE;
@@ -1197,78 +1198,64 @@ static void omap2_mcspi_work(struct omap2_mcspi *mcspi, struct spi_message *m)
 
        omap2_mcspi_set_enable(spi, 0);
 
+       if (gpio_is_valid(spi->cs_gpio))
+               omap2_mcspi_set_cs(spi, !(spi->mode & SPI_CS_HIGH));
+
        if (mcspi->fifo_depth > 0 && t)
                omap2_mcspi_set_fifo(spi, t, 0);
 
-       m->status = status;
+       return status;
 }
 
-static int omap2_mcspi_transfer_one_message(struct spi_master *master,
-               struct spi_message *m)
+static int omap2_mcspi_transfer_one(struct spi_master *master,
+               struct spi_device *spi, struct spi_transfer *t)
 {
-       struct spi_device       *spi;
        struct omap2_mcspi      *mcspi;
        struct omap2_mcspi_dma  *mcspi_dma;
-       struct spi_transfer     *t;
-       int status;
+       const void      *tx_buf = t->tx_buf;
+       void            *rx_buf = t->rx_buf;
+       unsigned        len = t->len;
 
-       spi = m->spi;
        mcspi = spi_master_get_devdata(master);
        mcspi_dma = mcspi->dma_channels + spi->chip_select;
-       m->actual_length = 0;
-       m->status = 0;
-
-       list_for_each_entry(t, &m->transfers, transfer_list) {
-               const void      *tx_buf = t->tx_buf;
-               void            *rx_buf = t->rx_buf;
-               unsigned        len = t->len;
-
-               if ((len && !(rx_buf || tx_buf))) {
-                       dev_dbg(mcspi->dev, "transfer: %d Hz, %d %s%s, %d bpw\n",
-                                       t->speed_hz,
-                                       len,
-                                       tx_buf ? "tx" : "",
-                                       rx_buf ? "rx" : "",
-                                       t->bits_per_word);
-                       status = -EINVAL;
-                       goto out;
-               }
 
-               if (m->is_dma_mapped || len < DMA_MIN_BYTES)
-                       continue;
-
-               if (mcspi_dma->dma_tx && tx_buf != NULL) {
-                       t->tx_dma = dma_map_single(mcspi->dev, (void *) tx_buf,
-                                       len, DMA_TO_DEVICE);
-                       if (dma_mapping_error(mcspi->dev, t->tx_dma)) {
-                               dev_dbg(mcspi->dev, "dma %cX %d bytes error\n",
-                                               'T', len);
-                               status = -EINVAL;
-                               goto out;
-                       }
+       if ((len && !(rx_buf || tx_buf))) {
+               dev_dbg(mcspi->dev, "transfer: %d Hz, %d %s%s, %d bpw\n",
+                               t->speed_hz,
+                               len,
+                               tx_buf ? "tx" : "",
+                               rx_buf ? "rx" : "",
+                               t->bits_per_word);
+               return -EINVAL;
+       }
+
+       if (len < DMA_MIN_BYTES)
+               goto skip_dma_map;
+
+       if (mcspi_dma->dma_tx && tx_buf != NULL) {
+               t->tx_dma = dma_map_single(mcspi->dev, (void *) tx_buf,
+                               len, DMA_TO_DEVICE);
+               if (dma_mapping_error(mcspi->dev, t->tx_dma)) {
+                       dev_dbg(mcspi->dev, "dma %cX %d bytes error\n",
+                                       'T', len);
+                       return -EINVAL;
                }
-               if (mcspi_dma->dma_rx && rx_buf != NULL) {
-                       t->rx_dma = dma_map_single(mcspi->dev, rx_buf, t->len,
-                                       DMA_FROM_DEVICE);
-                       if (dma_mapping_error(mcspi->dev, t->rx_dma)) {
-                               dev_dbg(mcspi->dev, "dma %cX %d bytes error\n",
-                                               'R', len);
-                               if (tx_buf != NULL)
-                                       dma_unmap_single(mcspi->dev, t->tx_dma,
-                                                       len, DMA_TO_DEVICE);
-                               status = -EINVAL;
-                               goto out;
-                       }
+       }
+       if (mcspi_dma->dma_rx && rx_buf != NULL) {
+               t->rx_dma = dma_map_single(mcspi->dev, rx_buf, t->len,
+                               DMA_FROM_DEVICE);
+               if (dma_mapping_error(mcspi->dev, t->rx_dma)) {
+                       dev_dbg(mcspi->dev, "dma %cX %d bytes error\n",
+                                       'R', len);
+                       if (tx_buf != NULL)
+                               dma_unmap_single(mcspi->dev, t->tx_dma,
+                                               len, DMA_TO_DEVICE);
+                       return -EINVAL;
                }
        }
 
-       omap2_mcspi_work(mcspi, m);
-       /* spi_finalize_current_message() changes the status inside the
-        * spi_message, save the status here. */
-       status = m->status;
-out:
-       spi_finalize_current_message(master);
-       return status;
+skip_dma_map:
+       return omap2_mcspi_work_one(mcspi, spi, t);
 }
 
 static int omap2_mcspi_master_setup(struct omap2_mcspi *mcspi)
@@ -1347,7 +1334,8 @@ static int omap2_mcspi_probe(struct platform_device *pdev)
        master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32);
        master->setup = omap2_mcspi_setup;
        master->auto_runtime_pm = true;
-       master->transfer_one_message = omap2_mcspi_transfer_one_message;
+       master->transfer_one = omap2_mcspi_transfer_one;
+       master->set_cs = omap2_mcspi_set_cs;
        master->cleanup = omap2_mcspi_cleanup;
        master->dev.of_node = node;
        master->max_speed_hz = OMAP2_MCSPI_MAX_FREQ;
index 861664776672cfab25200b22dba1227d28af08d6..8cad107a5b3f7e6d3b77f79fbf56f4b49f944267 100644 (file)
@@ -61,6 +61,12 @@ enum orion_spi_type {
 
 struct orion_spi_dev {
        enum orion_spi_type     typ;
+       /*
+        * min_divisor and max_hz should be exclusive, the only we can
+        * have both is for managing the armada-370-spi case with old
+        * device tree
+        */
+       unsigned long           max_hz;
        unsigned int            min_divisor;
        unsigned int            max_divisor;
        u32                     prescale_mask;
@@ -385,16 +391,54 @@ static const struct orion_spi_dev orion_spi_dev_data = {
        .prescale_mask = ORION_SPI_CLK_PRESCALE_MASK,
 };
 
-static const struct orion_spi_dev armada_spi_dev_data = {
+static const struct orion_spi_dev armada_370_spi_dev_data = {
        .typ = ARMADA_SPI,
-       .min_divisor = 1,
+       .min_divisor = 4,
+       .max_divisor = 1920,
+       .max_hz = 50000000,
+       .prescale_mask = ARMADA_SPI_CLK_PRESCALE_MASK,
+};
+
+static const struct orion_spi_dev armada_xp_spi_dev_data = {
+       .typ = ARMADA_SPI,
+       .max_hz = 50000000,
+       .max_divisor = 1920,
+       .prescale_mask = ARMADA_SPI_CLK_PRESCALE_MASK,
+};
+
+static const struct orion_spi_dev armada_375_spi_dev_data = {
+       .typ = ARMADA_SPI,
+       .min_divisor = 15,
        .max_divisor = 1920,
        .prescale_mask = ARMADA_SPI_CLK_PRESCALE_MASK,
 };
 
 static const struct of_device_id orion_spi_of_match_table[] = {
-       { .compatible = "marvell,orion-spi", .data = &orion_spi_dev_data, },
-       { .compatible = "marvell,armada-370-spi", .data = &armada_spi_dev_data, },
+       {
+               .compatible = "marvell,orion-spi",
+               .data = &orion_spi_dev_data,
+       },
+       {
+               .compatible = "marvell,armada-370-spi",
+               .data = &armada_370_spi_dev_data,
+       },
+       {
+               .compatible = "marvell,armada-375-spi",
+               .data = &armada_375_spi_dev_data,
+       },
+       {
+               .compatible = "marvell,armada-380-spi",
+               .data = &armada_xp_spi_dev_data,
+       },
+       {
+               .compatible = "marvell,armada-390-spi",
+               .data = &armada_xp_spi_dev_data,
+       },
+       {
+               .compatible = "marvell,armada-xp-spi",
+               .data = &armada_xp_spi_dev_data,
+       },
+
        {}
 };
 MODULE_DEVICE_TABLE(of, orion_spi_of_match_table);
@@ -454,7 +498,23 @@ static int orion_spi_probe(struct platform_device *pdev)
                goto out;
 
        tclk_hz = clk_get_rate(spi->clk);
-       master->max_speed_hz = DIV_ROUND_UP(tclk_hz, devdata->min_divisor);
+
+       /*
+        * With old device tree, armada-370-spi could be used with
+        * Armada XP, however for this SoC the maximum frequency is
+        * 50MHz instead of tclk/4. On Armada 370, tclk cannot be
+        * higher than 200MHz. So, in order to be able to handle both
+        * SoCs, we can take the minimum of 50MHz and tclk/4.
+        */
+       if (of_device_is_compatible(pdev->dev.of_node,
+                                       "marvell,armada-370-spi"))
+               master->max_speed_hz = min(devdata->max_hz,
+                               DIV_ROUND_UP(tclk_hz, devdata->min_divisor));
+       else if (devdata->min_divisor)
+               master->max_speed_hz =
+                       DIV_ROUND_UP(tclk_hz, devdata->min_divisor);
+       else
+               master->max_speed_hz = devdata->max_hz;
        master->min_speed_hz = DIV_ROUND_UP(tclk_hz, devdata->max_divisor);
 
        r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
index fa7399e84bbb8f745354bb0822afd681f77ad057..3cfd4357489aba1a5904da3f39d0d12346d2a908 100644 (file)
@@ -62,7 +62,7 @@ static struct pxa_spi_info spi_info_configs[] = {
                .max_clk_rate = 3686400,
        },
        [PORT_BYT] = {
-               .type = LPSS_SSP,
+               .type = LPSS_BYT_SSP,
                .port_id = 0,
                .num_chipselect = 1,
                .max_clk_rate = 50000000,
@@ -70,7 +70,7 @@ static struct pxa_spi_info spi_info_configs[] = {
                .rx_param = &byt_rx_param,
        },
        [PORT_BSW0] = {
-               .type = LPSS_SSP,
+               .type = LPSS_BYT_SSP,
                .port_id = 0,
                .num_chipselect = 1,
                .max_clk_rate = 50000000,
@@ -78,7 +78,7 @@ static struct pxa_spi_info spi_info_configs[] = {
                .rx_param = &bsw0_rx_param,
        },
        [PORT_BSW1] = {
-               .type = LPSS_SSP,
+               .type = LPSS_BYT_SSP,
                .port_id = 1,
                .num_chipselect = 1,
                .max_clk_rate = 50000000,
@@ -86,7 +86,7 @@ static struct pxa_spi_info spi_info_configs[] = {
                .rx_param = &bsw1_rx_param,
        },
        [PORT_BSW2] = {
-               .type = LPSS_SSP,
+               .type = LPSS_BYT_SSP,
                .port_id = 2,
                .num_chipselect = 1,
                .max_clk_rate = 50000000,
diff --git a/drivers/spi/spi-pxa2xx-pxadma.c b/drivers/spi/spi-pxa2xx-pxadma.c
deleted file mode 100644 (file)
index 2e0796a..0000000
+++ /dev/null
@@ -1,487 +0,0 @@
-/*
- * PXA2xx SPI private DMA support.
- *
- * Copyright (C) 2005 Stephen Street / StreetFire Sound Labs
- *
- * 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/delay.h>
-#include <linux/device.h>
-#include <linux/dma-mapping.h>
-#include <linux/pxa2xx_ssp.h>
-#include <linux/spi/spi.h>
-#include <linux/spi/pxa2xx_spi.h>
-
-#include <mach/dma.h>
-#include "spi-pxa2xx.h"
-
-#define DMA_INT_MASK           (DCSR_ENDINTR | DCSR_STARTINTR | DCSR_BUSERR)
-#define RESET_DMA_CHANNEL      (DCSR_NODESC | DMA_INT_MASK)
-
-bool pxa2xx_spi_dma_is_possible(size_t len)
-{
-       /* Try to map dma buffer and do a dma transfer if successful, but
-        * only if the length is non-zero and less than MAX_DMA_LEN.
-        *
-        * Zero-length non-descriptor DMA is illegal on PXA2xx; force use
-        * of PIO instead.  Care is needed above because the transfer may
-        * have have been passed with buffers that are already dma mapped.
-        * A zero-length transfer in PIO mode will not try to write/read
-        * to/from the buffers
-        *
-        * REVISIT large transfers are exactly where we most want to be
-        * using DMA.  If this happens much, split those transfers into
-        * multiple DMA segments rather than forcing PIO.
-        */
-       return len > 0 && len <= MAX_DMA_LEN;
-}
-
-int pxa2xx_spi_map_dma_buffers(struct driver_data *drv_data)
-{
-       struct spi_message *msg = drv_data->cur_msg;
-       struct device *dev = &msg->spi->dev;
-
-       if (!drv_data->cur_chip->enable_dma)
-               return 0;
-
-       if (msg->is_dma_mapped)
-               return  drv_data->rx_dma && drv_data->tx_dma;
-
-       if (!IS_DMA_ALIGNED(drv_data->rx) || !IS_DMA_ALIGNED(drv_data->tx))
-               return 0;
-
-       /* Modify setup if rx buffer is null */
-       if (drv_data->rx == NULL) {
-               *drv_data->null_dma_buf = 0;
-               drv_data->rx = drv_data->null_dma_buf;
-               drv_data->rx_map_len = 4;
-       } else
-               drv_data->rx_map_len = drv_data->len;
-
-
-       /* Modify setup if tx buffer is null */
-       if (drv_data->tx == NULL) {
-               *drv_data->null_dma_buf = 0;
-               drv_data->tx = drv_data->null_dma_buf;
-               drv_data->tx_map_len = 4;
-       } else
-               drv_data->tx_map_len = drv_data->len;
-
-       /* Stream map the tx buffer. Always do DMA_TO_DEVICE first
-        * so we flush the cache *before* invalidating it, in case
-        * the tx and rx buffers overlap.
-        */
-       drv_data->tx_dma = dma_map_single(dev, drv_data->tx,
-                                       drv_data->tx_map_len, DMA_TO_DEVICE);
-       if (dma_mapping_error(dev, drv_data->tx_dma))
-               return 0;
-
-       /* Stream map the rx buffer */
-       drv_data->rx_dma = dma_map_single(dev, drv_data->rx,
-                                       drv_data->rx_map_len, DMA_FROM_DEVICE);
-       if (dma_mapping_error(dev, drv_data->rx_dma)) {
-               dma_unmap_single(dev, drv_data->tx_dma,
-                                       drv_data->tx_map_len, DMA_TO_DEVICE);
-               return 0;
-       }
-
-       return 1;
-}
-
-static void pxa2xx_spi_unmap_dma_buffers(struct driver_data *drv_data)
-{
-       struct device *dev;
-
-       if (!drv_data->dma_mapped)
-               return;
-
-       if (!drv_data->cur_msg->is_dma_mapped) {
-               dev = &drv_data->cur_msg->spi->dev;
-               dma_unmap_single(dev, drv_data->rx_dma,
-                                       drv_data->rx_map_len, DMA_FROM_DEVICE);
-               dma_unmap_single(dev, drv_data->tx_dma,
-                                       drv_data->tx_map_len, DMA_TO_DEVICE);
-       }
-
-       drv_data->dma_mapped = 0;
-}
-
-static int wait_ssp_rx_stall(struct driver_data *drv_data)
-{
-       unsigned long limit = loops_per_jiffy << 1;
-
-       while ((pxa2xx_spi_read(drv_data, SSSR) & SSSR_BSY) && --limit)
-               cpu_relax();
-
-       return limit;
-}
-
-static int wait_dma_channel_stop(int channel)
-{
-       unsigned long limit = loops_per_jiffy << 1;
-
-       while (!(DCSR(channel) & DCSR_STOPSTATE) && --limit)
-               cpu_relax();
-
-       return limit;
-}
-
-static void pxa2xx_spi_dma_error_stop(struct driver_data *drv_data,
-                                     const char *msg)
-{
-       /* Stop and reset */
-       DCSR(drv_data->rx_channel) = RESET_DMA_CHANNEL;
-       DCSR(drv_data->tx_channel) = RESET_DMA_CHANNEL;
-       write_SSSR_CS(drv_data, drv_data->clear_sr);
-       pxa2xx_spi_write(drv_data, SSCR1,
-                        pxa2xx_spi_read(drv_data, SSCR1)
-                        & ~drv_data->dma_cr1);
-       if (!pxa25x_ssp_comp(drv_data))
-               pxa2xx_spi_write(drv_data, SSTO, 0);
-       pxa2xx_spi_flush(drv_data);
-       pxa2xx_spi_write(drv_data, SSCR0,
-                        pxa2xx_spi_read(drv_data, SSCR0) & ~SSCR0_SSE);
-
-       pxa2xx_spi_unmap_dma_buffers(drv_data);
-
-       dev_err(&drv_data->pdev->dev, "%s\n", msg);
-
-       drv_data->cur_msg->state = ERROR_STATE;
-       tasklet_schedule(&drv_data->pump_transfers);
-}
-
-static void pxa2xx_spi_dma_transfer_complete(struct driver_data *drv_data)
-{
-       struct spi_message *msg = drv_data->cur_msg;
-
-       /* Clear and disable interrupts on SSP and DMA channels*/
-       pxa2xx_spi_write(drv_data, SSCR1,
-                        pxa2xx_spi_read(drv_data, SSCR1)
-                        & ~drv_data->dma_cr1);
-       write_SSSR_CS(drv_data, drv_data->clear_sr);
-       DCSR(drv_data->tx_channel) = RESET_DMA_CHANNEL;
-       DCSR(drv_data->rx_channel) = RESET_DMA_CHANNEL;
-
-       if (wait_dma_channel_stop(drv_data->rx_channel) == 0)
-               dev_err(&drv_data->pdev->dev,
-                       "dma_handler: dma rx channel stop failed\n");
-
-       if (wait_ssp_rx_stall(drv_data->ioaddr) == 0)
-               dev_err(&drv_data->pdev->dev,
-                       "dma_transfer: ssp rx stall failed\n");
-
-       pxa2xx_spi_unmap_dma_buffers(drv_data);
-
-       /* update the buffer pointer for the amount completed in dma */
-       drv_data->rx += drv_data->len -
-                       (DCMD(drv_data->rx_channel) & DCMD_LENGTH);
-
-       /* read trailing data from fifo, it does not matter how many
-        * bytes are in the fifo just read until buffer is full
-        * or fifo is empty, which ever occurs first */
-       drv_data->read(drv_data);
-
-       /* return count of what was actually read */
-       msg->actual_length += drv_data->len -
-                               (drv_data->rx_end - drv_data->rx);
-
-       /* Transfer delays and chip select release are
-        * handled in pump_transfers or giveback
-        */
-
-       /* Move to next transfer */
-       msg->state = pxa2xx_spi_next_transfer(drv_data);
-
-       /* Schedule transfer tasklet */
-       tasklet_schedule(&drv_data->pump_transfers);
-}
-
-void pxa2xx_spi_dma_handler(int channel, void *data)
-{
-       struct driver_data *drv_data = data;
-       u32 irq_status = DCSR(channel) & DMA_INT_MASK;
-
-       if (irq_status & DCSR_BUSERR) {
-
-               if (channel == drv_data->tx_channel)
-                       pxa2xx_spi_dma_error_stop(drv_data,
-                               "dma_handler: bad bus address on tx channel");
-               else
-                       pxa2xx_spi_dma_error_stop(drv_data,
-                               "dma_handler: bad bus address on rx channel");
-               return;
-       }
-
-       /* PXA255x_SSP has no timeout interrupt, wait for tailing bytes */
-       if ((channel == drv_data->tx_channel)
-               && (irq_status & DCSR_ENDINTR)
-               && (drv_data->ssp_type == PXA25x_SSP)) {
-
-               /* Wait for rx to stall */
-               if (wait_ssp_rx_stall(drv_data) == 0)
-                       dev_err(&drv_data->pdev->dev,
-                               "dma_handler: ssp rx stall failed\n");
-
-               /* finish this transfer, start the next */
-               pxa2xx_spi_dma_transfer_complete(drv_data);
-       }
-}
-
-irqreturn_t pxa2xx_spi_dma_transfer(struct driver_data *drv_data)
-{
-       u32 irq_status;
-
-       irq_status = pxa2xx_spi_read(drv_data, SSSR) & drv_data->mask_sr;
-       if (irq_status & SSSR_ROR) {
-               pxa2xx_spi_dma_error_stop(drv_data,
-                                         "dma_transfer: fifo overrun");
-               return IRQ_HANDLED;
-       }
-
-       /* Check for false positive timeout */
-       if ((irq_status & SSSR_TINT)
-               && (DCSR(drv_data->tx_channel) & DCSR_RUN)) {
-               pxa2xx_spi_write(drv_data, SSSR, SSSR_TINT);
-               return IRQ_HANDLED;
-       }
-
-       if (irq_status & SSSR_TINT || drv_data->rx == drv_data->rx_end) {
-
-               /* Clear and disable timeout interrupt, do the rest in
-                * dma_transfer_complete */
-               if (!pxa25x_ssp_comp(drv_data))
-                       pxa2xx_spi_write(drv_data, SSTO, 0);
-
-               /* finish this transfer, start the next */
-               pxa2xx_spi_dma_transfer_complete(drv_data);
-
-               return IRQ_HANDLED;
-       }
-
-       /* Opps problem detected */
-       return IRQ_NONE;
-}
-
-int pxa2xx_spi_dma_prepare(struct driver_data *drv_data, u32 dma_burst)
-{
-       u32 dma_width;
-
-       switch (drv_data->n_bytes) {
-       case 1:
-               dma_width = DCMD_WIDTH1;
-               break;
-       case 2:
-               dma_width = DCMD_WIDTH2;
-               break;
-       default:
-               dma_width = DCMD_WIDTH4;
-               break;
-       }
-
-       /* Setup rx DMA Channel */
-       DCSR(drv_data->rx_channel) = RESET_DMA_CHANNEL;
-       DSADR(drv_data->rx_channel) = drv_data->ssdr_physical;
-       DTADR(drv_data->rx_channel) = drv_data->rx_dma;
-       if (drv_data->rx == drv_data->null_dma_buf)
-               /* No target address increment */
-               DCMD(drv_data->rx_channel) = DCMD_FLOWSRC
-                                               | dma_width
-                                               | dma_burst
-                                               | drv_data->len;
-       else
-               DCMD(drv_data->rx_channel) = DCMD_INCTRGADDR
-                                               | DCMD_FLOWSRC
-                                               | dma_width
-                                               | dma_burst
-                                               | drv_data->len;
-
-       /* Setup tx DMA Channel */
-       DCSR(drv_data->tx_channel) = RESET_DMA_CHANNEL;
-       DSADR(drv_data->tx_channel) = drv_data->tx_dma;
-       DTADR(drv_data->tx_channel) = drv_data->ssdr_physical;
-       if (drv_data->tx == drv_data->null_dma_buf)
-               /* No source address increment */
-               DCMD(drv_data->tx_channel) = DCMD_FLOWTRG
-                                               | dma_width
-                                               | dma_burst
-                                               | drv_data->len;
-       else
-               DCMD(drv_data->tx_channel) = DCMD_INCSRCADDR
-                                               | DCMD_FLOWTRG
-                                               | dma_width
-                                               | dma_burst
-                                               | drv_data->len;
-
-       /* Enable dma end irqs on SSP to detect end of transfer */
-       if (drv_data->ssp_type == PXA25x_SSP)
-               DCMD(drv_data->tx_channel) |= DCMD_ENDIRQEN;
-
-       return 0;
-}
-
-void pxa2xx_spi_dma_start(struct driver_data *drv_data)
-{
-       DCSR(drv_data->rx_channel) |= DCSR_RUN;
-       DCSR(drv_data->tx_channel) |= DCSR_RUN;
-}
-
-int pxa2xx_spi_dma_setup(struct driver_data *drv_data)
-{
-       struct device *dev = &drv_data->pdev->dev;
-       struct ssp_device *ssp = drv_data->ssp;
-
-       /* Get two DMA channels (rx and tx) */
-       drv_data->rx_channel = pxa_request_dma("pxa2xx_spi_ssp_rx",
-                                               DMA_PRIO_HIGH,
-                                               pxa2xx_spi_dma_handler,
-                                               drv_data);
-       if (drv_data->rx_channel < 0) {
-               dev_err(dev, "problem (%d) requesting rx channel\n",
-                       drv_data->rx_channel);
-               return -ENODEV;
-       }
-       drv_data->tx_channel = pxa_request_dma("pxa2xx_spi_ssp_tx",
-                                               DMA_PRIO_MEDIUM,
-                                               pxa2xx_spi_dma_handler,
-                                               drv_data);
-       if (drv_data->tx_channel < 0) {
-               dev_err(dev, "problem (%d) requesting tx channel\n",
-                       drv_data->tx_channel);
-               pxa_free_dma(drv_data->rx_channel);
-               return -ENODEV;
-       }
-
-       DRCMR(ssp->drcmr_rx) = DRCMR_MAPVLD | drv_data->rx_channel;
-       DRCMR(ssp->drcmr_tx) = DRCMR_MAPVLD | drv_data->tx_channel;
-
-       return 0;
-}
-
-void pxa2xx_spi_dma_release(struct driver_data *drv_data)
-{
-       struct ssp_device *ssp = drv_data->ssp;
-
-       DRCMR(ssp->drcmr_rx) = 0;
-       DRCMR(ssp->drcmr_tx) = 0;
-
-       if (drv_data->tx_channel != 0)
-               pxa_free_dma(drv_data->tx_channel);
-       if (drv_data->rx_channel != 0)
-               pxa_free_dma(drv_data->rx_channel);
-}
-
-void pxa2xx_spi_dma_resume(struct driver_data *drv_data)
-{
-       if (drv_data->rx_channel != -1)
-               DRCMR(drv_data->ssp->drcmr_rx) =
-                       DRCMR_MAPVLD | drv_data->rx_channel;
-       if (drv_data->tx_channel != -1)
-               DRCMR(drv_data->ssp->drcmr_tx) =
-                       DRCMR_MAPVLD | drv_data->tx_channel;
-}
-
-int pxa2xx_spi_set_dma_burst_and_threshold(struct chip_data *chip,
-                                          struct spi_device *spi,
-                                          u8 bits_per_word, u32 *burst_code,
-                                          u32 *threshold)
-{
-       struct pxa2xx_spi_chip *chip_info =
-                       (struct pxa2xx_spi_chip *)spi->controller_data;
-       int bytes_per_word;
-       int burst_bytes;
-       int thresh_words;
-       int req_burst_size;
-       int retval = 0;
-
-       /* Set the threshold (in registers) to equal the same amount of data
-        * as represented by burst size (in bytes).  The computation below
-        * is (burst_size rounded up to nearest 8 byte, word or long word)
-        * divided by (bytes/register); the tx threshold is the inverse of
-        * the rx, so that there will always be enough data in the rx fifo
-        * to satisfy a burst, and there will always be enough space in the
-        * tx fifo to accept a burst (a tx burst will overwrite the fifo if
-        * there is not enough space), there must always remain enough empty
-        * space in the rx fifo for any data loaded to the tx fifo.
-        * Whenever burst_size (in bytes) equals bits/word, the fifo threshold
-        * will be 8, or half the fifo;
-        * The threshold can only be set to 2, 4 or 8, but not 16, because
-        * to burst 16 to the tx fifo, the fifo would have to be empty;
-        * however, the minimum fifo trigger level is 1, and the tx will
-        * request service when the fifo is at this level, with only 15 spaces.
-        */
-
-       /* find bytes/word */
-       if (bits_per_word <= 8)
-               bytes_per_word = 1;
-       else if (bits_per_word <= 16)
-               bytes_per_word = 2;
-       else
-               bytes_per_word = 4;
-
-       /* use struct pxa2xx_spi_chip->dma_burst_size if available */
-       if (chip_info)
-               req_burst_size = chip_info->dma_burst_size;
-       else {
-               switch (chip->dma_burst_size) {
-               default:
-                       /* if the default burst size is not set,
-                        * do it now */
-                       chip->dma_burst_size = DCMD_BURST8;
-               case DCMD_BURST8:
-                       req_burst_size = 8;
-                       break;
-               case DCMD_BURST16:
-                       req_burst_size = 16;
-                       break;
-               case DCMD_BURST32:
-                       req_burst_size = 32;
-                       break;
-               }
-       }
-       if (req_burst_size <= 8) {
-               *burst_code = DCMD_BURST8;
-               burst_bytes = 8;
-       } else if (req_burst_size <= 16) {
-               if (bytes_per_word == 1) {
-                       /* don't burst more than 1/2 the fifo */
-                       *burst_code = DCMD_BURST8;
-                       burst_bytes = 8;
-                       retval = 1;
-               } else {
-                       *burst_code = DCMD_BURST16;
-                       burst_bytes = 16;
-               }
-       } else {
-               if (bytes_per_word == 1) {
-                       /* don't burst more than 1/2 the fifo */
-                       *burst_code = DCMD_BURST8;
-                       burst_bytes = 8;
-                       retval = 1;
-               } else if (bytes_per_word == 2) {
-                       /* don't burst more than 1/2 the fifo */
-                       *burst_code = DCMD_BURST16;
-                       burst_bytes = 16;
-                       retval = 1;
-               } else {
-                       *burst_code = DCMD_BURST32;
-                       burst_bytes = 32;
-               }
-       }
-
-       thresh_words = burst_bytes / bytes_per_word;
-
-       /* thresh_words will be between 2 and 8 */
-       *threshold = (SSCR1_RxTresh(thresh_words) & SSCR1_RFT)
-                       | (SSCR1_TxTresh(16-thresh_words) & SSCR1_TFT);
-
-       return retval;
-}
index e3223ac75a7c57d55e1c56e90dfac2aea877f8d5..7293d6d875c5c692184d1c45aea151f063bb6cd0 100644 (file)
@@ -60,21 +60,60 @@ MODULE_ALIAS("platform:pxa2xx-spi");
                                | QUARK_X1000_SSCR1_TFT         \
                                | SSCR1_SPH | SSCR1_SPO | SSCR1_LBM)
 
-#define LPSS_RX_THRESH_DFLT    64
-#define LPSS_TX_LOTHRESH_DFLT  160
-#define LPSS_TX_HITHRESH_DFLT  224
-
-/* Offset from drv_data->lpss_base */
-#define GENERAL_REG            0x08
 #define GENERAL_REG_RXTO_HOLDOFF_DISABLE BIT(24)
-#define SSP_REG                        0x0c
-#define SPI_CS_CONTROL         0x18
 #define SPI_CS_CONTROL_SW_MODE BIT(0)
 #define SPI_CS_CONTROL_CS_HIGH BIT(1)
 
+struct lpss_config {
+       /* LPSS offset from drv_data->ioaddr */
+       unsigned offset;
+       /* Register offsets from drv_data->lpss_base or -1 */
+       int reg_general;
+       int reg_ssp;
+       int reg_cs_ctrl;
+       /* FIFO thresholds */
+       u32 rx_threshold;
+       u32 tx_threshold_lo;
+       u32 tx_threshold_hi;
+};
+
+/* Keep these sorted with enum pxa_ssp_type */
+static const struct lpss_config lpss_platforms[] = {
+       {       /* LPSS_LPT_SSP */
+               .offset = 0x800,
+               .reg_general = 0x08,
+               .reg_ssp = 0x0c,
+               .reg_cs_ctrl = 0x18,
+               .rx_threshold = 64,
+               .tx_threshold_lo = 160,
+               .tx_threshold_hi = 224,
+       },
+       {       /* LPSS_BYT_SSP */
+               .offset = 0x400,
+               .reg_general = 0x08,
+               .reg_ssp = 0x0c,
+               .reg_cs_ctrl = 0x18,
+               .rx_threshold = 64,
+               .tx_threshold_lo = 160,
+               .tx_threshold_hi = 224,
+       },
+};
+
+static inline const struct lpss_config
+*lpss_get_config(const struct driver_data *drv_data)
+{
+       return &lpss_platforms[drv_data->ssp_type - LPSS_LPT_SSP];
+}
+
 static bool is_lpss_ssp(const struct driver_data *drv_data)
 {
-       return drv_data->ssp_type == LPSS_SSP;
+       switch (drv_data->ssp_type) {
+       case LPSS_LPT_SSP:
+       case LPSS_BYT_SSP:
+               return true;
+       default:
+               return false;
+       }
 }
 
 static bool is_quark_x1000_ssp(const struct driver_data *drv_data)
@@ -192,63 +231,43 @@ static void __lpss_ssp_write_priv(struct driver_data *drv_data,
  */
 static void lpss_ssp_setup(struct driver_data *drv_data)
 {
-       unsigned offset = 0x400;
-       u32 value, orig;
-
-       /*
-        * Perform auto-detection of the LPSS SSP private registers. They
-        * can be either at 1k or 2k offset from the base address.
-        */
-       orig = readl(drv_data->ioaddr + offset + SPI_CS_CONTROL);
-
-       /* Test SPI_CS_CONTROL_SW_MODE bit enabling */
-       value = orig | SPI_CS_CONTROL_SW_MODE;
-       writel(value, drv_data->ioaddr + offset + SPI_CS_CONTROL);
-       value = readl(drv_data->ioaddr + offset + SPI_CS_CONTROL);
-       if (value != (orig | SPI_CS_CONTROL_SW_MODE)) {
-               offset = 0x800;
-               goto detection_done;
-       }
-
-       orig = readl(drv_data->ioaddr + offset + SPI_CS_CONTROL);
-
-       /* Test SPI_CS_CONTROL_SW_MODE bit disabling */
-       value = orig & ~SPI_CS_CONTROL_SW_MODE;
-       writel(value, drv_data->ioaddr + offset + SPI_CS_CONTROL);
-       value = readl(drv_data->ioaddr + offset + SPI_CS_CONTROL);
-       if (value != (orig & ~SPI_CS_CONTROL_SW_MODE)) {
-               offset = 0x800;
-               goto detection_done;
-       }
+       const struct lpss_config *config;
+       u32 value;
 
-detection_done:
-       /* Now set the LPSS base */
-       drv_data->lpss_base = drv_data->ioaddr + offset;
+       config = lpss_get_config(drv_data);
+       drv_data->lpss_base = drv_data->ioaddr + config->offset;
 
        /* Enable software chip select control */
        value = SPI_CS_CONTROL_SW_MODE | SPI_CS_CONTROL_CS_HIGH;
-       __lpss_ssp_write_priv(drv_data, SPI_CS_CONTROL, value);
+       __lpss_ssp_write_priv(drv_data, config->reg_cs_ctrl, value);
 
        /* Enable multiblock DMA transfers */
        if (drv_data->master_info->enable_dma) {
-               __lpss_ssp_write_priv(drv_data, SSP_REG, 1);
-
-               value = __lpss_ssp_read_priv(drv_data, GENERAL_REG);
-               value |= GENERAL_REG_RXTO_HOLDOFF_DISABLE;
-               __lpss_ssp_write_priv(drv_data, GENERAL_REG, value);
+               __lpss_ssp_write_priv(drv_data, config->reg_ssp, 1);
+
+               if (config->reg_general >= 0) {
+                       value = __lpss_ssp_read_priv(drv_data,
+                                                    config->reg_general);
+                       value |= GENERAL_REG_RXTO_HOLDOFF_DISABLE;
+                       __lpss_ssp_write_priv(drv_data,
+                                             config->reg_general, value);
+               }
        }
 }
 
 static void lpss_ssp_cs_control(struct driver_data *drv_data, bool enable)
 {
+       const struct lpss_config *config;
        u32 value;
 
-       value = __lpss_ssp_read_priv(drv_data, SPI_CS_CONTROL);
+       config = lpss_get_config(drv_data);
+
+       value = __lpss_ssp_read_priv(drv_data, config->reg_cs_ctrl);
        if (enable)
                value &= ~SPI_CS_CONTROL_CS_HIGH;
        else
                value |= SPI_CS_CONTROL_CS_HIGH;
-       __lpss_ssp_write_priv(drv_data, SPI_CS_CONTROL, value);
+       __lpss_ssp_write_priv(drv_data, config->reg_cs_ctrl, value);
 }
 
 static void cs_assert(struct driver_data *drv_data)
@@ -1075,6 +1094,7 @@ static int setup(struct spi_device *spi)
 {
        struct pxa2xx_spi_chip *chip_info = NULL;
        struct chip_data *chip;
+       const struct lpss_config *config;
        struct driver_data *drv_data = spi_master_get_devdata(spi->master);
        unsigned int clk_div;
        uint tx_thres, tx_hi_thres, rx_thres;
@@ -1085,10 +1105,12 @@ static int setup(struct spi_device *spi)
                tx_hi_thres = 0;
                rx_thres = RX_THRESH_QUARK_X1000_DFLT;
                break;
-       case LPSS_SSP:
-               tx_thres = LPSS_TX_LOTHRESH_DFLT;
-               tx_hi_thres = LPSS_TX_HITHRESH_DFLT;
-               rx_thres = LPSS_RX_THRESH_DFLT;
+       case LPSS_LPT_SSP:
+       case LPSS_BYT_SSP:
+               config = lpss_get_config(drv_data);
+               tx_thres = config->tx_threshold_lo;
+               tx_hi_thres = config->tx_threshold_hi;
+               rx_thres = config->rx_threshold;
                break;
        default:
                tx_thres = TX_THRESH_DFLT;
@@ -1242,6 +1264,18 @@ static void cleanup(struct spi_device *spi)
 }
 
 #ifdef CONFIG_ACPI
+
+static const struct acpi_device_id pxa2xx_spi_acpi_match[] = {
+       { "INT33C0", LPSS_LPT_SSP },
+       { "INT33C1", LPSS_LPT_SSP },
+       { "INT3430", LPSS_LPT_SSP },
+       { "INT3431", LPSS_LPT_SSP },
+       { "80860F0E", LPSS_BYT_SSP },
+       { "8086228E", LPSS_BYT_SSP },
+       { },
+};
+MODULE_DEVICE_TABLE(acpi, pxa2xx_spi_acpi_match);
+
 static struct pxa2xx_spi_master *
 pxa2xx_spi_acpi_get_pdata(struct platform_device *pdev)
 {
@@ -1249,12 +1283,19 @@ pxa2xx_spi_acpi_get_pdata(struct platform_device *pdev)
        struct acpi_device *adev;
        struct ssp_device *ssp;
        struct resource *res;
-       int devid;
+       const struct acpi_device_id *id;
+       int devid, type;
 
        if (!ACPI_HANDLE(&pdev->dev) ||
            acpi_bus_get_device(ACPI_HANDLE(&pdev->dev), &adev))
                return NULL;
 
+       id = acpi_match_device(pdev->dev.driver->acpi_match_table, &pdev->dev);
+       if (id)
+               type = (int)id->driver_data;
+       else
+               return NULL;
+
        pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
        if (!pdata)
                return NULL;
@@ -1272,7 +1313,7 @@ pxa2xx_spi_acpi_get_pdata(struct platform_device *pdev)
 
        ssp->clk = devm_clk_get(&pdev->dev, NULL);
        ssp->irq = platform_get_irq(pdev, 0);
-       ssp->type = LPSS_SSP;
+       ssp->type = type;
        ssp->pdev = pdev;
 
        ssp->port_id = -1;
@@ -1285,16 +1326,6 @@ pxa2xx_spi_acpi_get_pdata(struct platform_device *pdev)
        return pdata;
 }
 
-static struct acpi_device_id pxa2xx_spi_acpi_match[] = {
-       { "INT33C0", 0 },
-       { "INT33C1", 0 },
-       { "INT3430", 0 },
-       { "INT3431", 0 },
-       { "80860F0E", 0 },
-       { "8086228E", 0 },
-       { },
-};
-MODULE_DEVICE_TABLE(acpi, pxa2xx_spi_acpi_match);
 #else
 static inline struct pxa2xx_spi_master *
 pxa2xx_spi_acpi_get_pdata(struct platform_device *pdev)
index 85a58c9068694fa7a8b80629a455f3b3125efe12..9f01e9c9aa753c913d791f9930c4d2649ed7ccdc 100644 (file)
@@ -162,11 +162,7 @@ extern void *pxa2xx_spi_next_transfer(struct driver_data *drv_data);
 /*
  * Select the right DMA implementation.
  */
-#if defined(CONFIG_SPI_PXA2XX_PXADMA)
-#define SPI_PXA2XX_USE_DMA     1
-#define MAX_DMA_LEN            8191
-#define DEFAULT_DMA_CR1                (SSCR1_TSRE | SSCR1_RSRE | SSCR1_TINTE)
-#elif defined(CONFIG_SPI_PXA2XX_DMA)
+#if defined(CONFIG_SPI_PXA2XX_DMA)
 #define SPI_PXA2XX_USE_DMA     1
 #define MAX_DMA_LEN            SZ_64K
 #define DEFAULT_DMA_CR1                (SSCR1_TSRE | SSCR1_RSRE | SSCR1_TRAIL)
diff --git a/drivers/spi/spi-rb4xx.c b/drivers/spi/spi-rb4xx.c
new file mode 100644 (file)
index 0000000..3641d0e
--- /dev/null
@@ -0,0 +1,210 @@
+/*
+ * SPI controller driver for the Mikrotik RB4xx boards
+ *
+ * Copyright (C) 2010 Gabor Juhos <juhosg@openwrt.org>
+ * Copyright (C) 2015 Bert Vermeulen <bert@biot.com>
+ *
+ * This file was based on the patches for Linux 2.6.27.39 published by
+ * MikroTik for their RouterBoard 4xx series devices.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/spi/spi.h>
+
+#include <asm/mach-ath79/ar71xx_regs.h>
+
+struct rb4xx_spi {
+       void __iomem *base;
+       struct clk *clk;
+};
+
+static inline u32 rb4xx_read(struct rb4xx_spi *rbspi, u32 reg)
+{
+       return __raw_readl(rbspi->base + reg);
+}
+
+static inline void rb4xx_write(struct rb4xx_spi *rbspi, u32 reg, u32 value)
+{
+       __raw_writel(value, rbspi->base + reg);
+}
+
+static inline void do_spi_clk(struct rb4xx_spi *rbspi, u32 spi_ioc, int value)
+{
+       u32 regval;
+
+       regval = spi_ioc;
+       if (value & BIT(0))
+               regval |= AR71XX_SPI_IOC_DO;
+
+       rb4xx_write(rbspi, AR71XX_SPI_REG_IOC, regval);
+       rb4xx_write(rbspi, AR71XX_SPI_REG_IOC, regval | AR71XX_SPI_IOC_CLK);
+}
+
+static void do_spi_byte(struct rb4xx_spi *rbspi, u32 spi_ioc, u8 byte)
+{
+       int i;
+
+       for (i = 7; i >= 0; i--)
+               do_spi_clk(rbspi, spi_ioc, byte >> i);
+}
+
+/* The CS2 pin is used to clock in a second bit per clock cycle. */
+static inline void do_spi_clk_two(struct rb4xx_spi *rbspi, u32 spi_ioc,
+                                  u8 value)
+{
+       u32 regval;
+
+       regval = spi_ioc;
+       if (value & BIT(1))
+               regval |= AR71XX_SPI_IOC_DO;
+       if (value & BIT(0))
+               regval |= AR71XX_SPI_IOC_CS2;
+
+       rb4xx_write(rbspi, AR71XX_SPI_REG_IOC, regval);
+       rb4xx_write(rbspi, AR71XX_SPI_REG_IOC, regval | AR71XX_SPI_IOC_CLK);
+}
+
+/* Two bits at a time, msb first */
+static void do_spi_byte_two(struct rb4xx_spi *rbspi, u32 spi_ioc, u8 byte)
+{
+       do_spi_clk_two(rbspi, spi_ioc, byte >> 6);
+       do_spi_clk_two(rbspi, spi_ioc, byte >> 4);
+       do_spi_clk_two(rbspi, spi_ioc, byte >> 2);
+       do_spi_clk_two(rbspi, spi_ioc, byte >> 0);
+}
+
+static void rb4xx_set_cs(struct spi_device *spi, bool enable)
+{
+       struct rb4xx_spi *rbspi = spi_master_get_devdata(spi->master);
+
+       /*
+        * Setting CS is done along with bitbanging the actual values,
+        * since it's all on the same hardware register. However the
+        * CPLD needs CS deselected after every command.
+        */
+       if (enable)
+               rb4xx_write(rbspi, AR71XX_SPI_REG_IOC,
+                           AR71XX_SPI_IOC_CS0 | AR71XX_SPI_IOC_CS1);
+}
+
+static int rb4xx_transfer_one(struct spi_master *master,
+                             struct spi_device *spi, struct spi_transfer *t)
+{
+       struct rb4xx_spi *rbspi = spi_master_get_devdata(master);
+       int i;
+       u32 spi_ioc;
+       u8 *rx_buf;
+       const u8 *tx_buf;
+
+       /*
+        * Prime the SPI register with the SPI device selected. The m25p80 boot
+        * flash and CPLD share the CS0 pin. This works because the CPLD's
+        * command set was designed to almost not clash with that of the
+        * boot flash.
+        */
+       if (spi->chip_select == 2)
+               /* MMC */
+               spi_ioc = AR71XX_SPI_IOC_CS0;
+       else
+               /* Boot flash and CPLD */
+               spi_ioc = AR71XX_SPI_IOC_CS1;
+
+       tx_buf = t->tx_buf;
+       rx_buf = t->rx_buf;
+       for (i = 0; i < t->len; ++i) {
+               if (t->tx_nbits == SPI_NBITS_DUAL)
+                       /* CPLD can use two-wire transfers */
+                       do_spi_byte_two(rbspi, spi_ioc, tx_buf[i]);
+               else
+                       do_spi_byte(rbspi, spi_ioc, tx_buf[i]);
+               if (!rx_buf)
+                       continue;
+               rx_buf[i] = rb4xx_read(rbspi, AR71XX_SPI_REG_RDS);
+       }
+       spi_finalize_current_transfer(master);
+
+       return 0;
+}
+
+static int rb4xx_spi_probe(struct platform_device *pdev)
+{
+       struct spi_master *master;
+       struct clk *ahb_clk;
+       struct rb4xx_spi *rbspi;
+       struct resource *r;
+       int err;
+       void __iomem *spi_base;
+
+       r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       spi_base = devm_ioremap_resource(&pdev->dev, r);
+       if (IS_ERR(spi_base))
+               return PTR_ERR(spi_base);
+
+       master = spi_alloc_master(&pdev->dev, sizeof(*rbspi));
+       if (!master)
+               return -ENOMEM;
+
+       ahb_clk = devm_clk_get(&pdev->dev, "ahb");
+       if (IS_ERR(ahb_clk))
+               return PTR_ERR(ahb_clk);
+
+       master->bus_num = 0;
+       master->num_chipselect = 3;
+       master->mode_bits = SPI_TX_DUAL;
+       master->bits_per_word_mask = BIT(7);
+       master->flags = SPI_MASTER_MUST_TX;
+       master->transfer_one = rb4xx_transfer_one;
+       master->set_cs = rb4xx_set_cs;
+
+       err = devm_spi_register_master(&pdev->dev, master);
+       if (err) {
+               dev_err(&pdev->dev, "failed to register SPI master\n");
+               return err;
+       }
+
+       err = clk_prepare_enable(ahb_clk);
+       if (err)
+               return err;
+
+       rbspi = spi_master_get_devdata(master);
+       rbspi->base = spi_base;
+       rbspi->clk = ahb_clk;
+       platform_set_drvdata(pdev, rbspi);
+
+       /* Enable SPI */
+       rb4xx_write(rbspi, AR71XX_SPI_REG_FS, AR71XX_SPI_FS_GPIO);
+
+       return 0;
+}
+
+static int rb4xx_spi_remove(struct platform_device *pdev)
+{
+       struct rb4xx_spi *rbspi = platform_get_drvdata(pdev);
+
+       clk_disable_unprepare(rbspi->clk);
+
+       return 0;
+}
+
+static struct platform_driver rb4xx_spi_drv = {
+       .probe = rb4xx_spi_probe,
+       .remove = rb4xx_spi_remove,
+       .driver = {
+               .name = "rb4xx-spi",
+       },
+};
+
+module_platform_driver(rb4xx_spi_drv);
+
+MODULE_DESCRIPTION("Mikrotik RB4xx SPI controller driver");
+MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>");
+MODULE_AUTHOR("Bert Vermeulen <bert@biot.com>");
+MODULE_LICENSE("GPL v2");
index f6bac9e77d067390f29584595b2a219b69e749e4..f9189a0c8cec81f243c7e2ad4587356d13cf27a6 100644 (file)
@@ -665,15 +665,12 @@ static bool rspi_can_dma(struct spi_master *master, struct spi_device *spi,
 static int rspi_dma_check_then_transfer(struct rspi_data *rspi,
                                         struct spi_transfer *xfer)
 {
-       if (rspi->master->can_dma && __rspi_can_dma(rspi, xfer)) {
-               /* rx_buf can be NULL on RSPI on SH in TX-only Mode */
-               int ret = rspi_dma_transfer(rspi, &xfer->tx_sg,
-                                       xfer->rx_buf ? &xfer->rx_sg : NULL);
-               if (ret != -EAGAIN)
-                       return 0;
-       }
+       if (!rspi->master->can_dma || !__rspi_can_dma(rspi, xfer))
+               return -EAGAIN;
 
-       return -EAGAIN;
+       /* rx_buf can be NULL on RSPI on SH in TX-only Mode */
+       return rspi_dma_transfer(rspi, &xfer->tx_sg,
+                               xfer->rx_buf ? &xfer->rx_sg : NULL);
 }
 
 static int rspi_common_transfer(struct rspi_data *rspi,
@@ -724,7 +721,7 @@ static int rspi_rz_transfer_one(struct spi_master *master,
        return rspi_common_transfer(rspi, xfer);
 }
 
-static int qspi_trigger_transfer_out_int(struct rspi_data *rspi, const u8 *tx,
+static int qspi_trigger_transfer_out_in(struct rspi_data *rspi, const u8 *tx,
                                        u8 *rx, unsigned int len)
 {
        int i, n, ret;
@@ -771,12 +768,8 @@ static int qspi_transfer_out_in(struct rspi_data *rspi,
        if (ret != -EAGAIN)
                return ret;
 
-       ret = qspi_trigger_transfer_out_int(rspi, xfer->tx_buf,
+       return qspi_trigger_transfer_out_in(rspi, xfer->tx_buf,
                                            xfer->rx_buf, xfer->len);
-       if (ret < 0)
-               return ret;
-
-       return 0;
 }
 
 static int qspi_transfer_out(struct rspi_data *rspi, struct spi_transfer *xfer)
@@ -1300,7 +1293,7 @@ error1:
        return ret;
 }
 
-static struct platform_device_id spi_driver_ids[] = {
+static const struct platform_device_id spi_driver_ids[] = {
        { "rspi",       (kernel_ulong_t)&rspi_ops },
        { "rspi-rz",    (kernel_ulong_t)&rspi_rz_ops },
        { "qspi",       (kernel_ulong_t)&qspi_ops },
index b1c6731fbf2755199669fe204747330b985b4733..2a8c513c4d07808d7ceaa46827e57a2d6267f0e4 100644 (file)
@@ -1347,7 +1347,7 @@ static struct s3c64xx_spi_port_config exynos7_spi_port_config = {
        .quirks         = S3C64XX_SPI_QUIRK_CS_AUTO,
 };
 
-static struct platform_device_id s3c64xx_spi_driver_ids[] = {
+static const struct platform_device_id s3c64xx_spi_driver_ids[] = {
        {
                .name           = "s3c2443-spi",
                .driver_data    = (kernel_ulong_t)&s3c2443_spi_port_config,
index bcc7c635d8e7dfe628d4ad5a4cf5fce80c6e4f09..d3370a612d8432cf48b6f4d5d885e696833b4349 100644 (file)
@@ -1263,7 +1263,7 @@ static int sh_msiof_spi_remove(struct platform_device *pdev)
        return 0;
 }
 
-static struct platform_device_id spi_driver_ids[] = {
+static const struct platform_device_id spi_driver_ids[] = {
        { "spi_sh_msiof",       (kernel_ulong_t)&sh_data },
        { "spi_r8a7790_msiof",  (kernel_ulong_t)&r8a779x_data },
        { "spi_r8a7791_msiof",  (kernel_ulong_t)&r8a779x_data },
index f5715c9f68b0e0cb3dd2f7f568fed38798820796..7072276ad354b8498c50734a9b333f7952a2e022 100644 (file)
 #include <linux/reset.h>
 
 #define DRIVER_NAME "sirfsoc_spi"
-
-#define SIRFSOC_SPI_CTRL               0x0000
-#define SIRFSOC_SPI_CMD                        0x0004
-#define SIRFSOC_SPI_TX_RX_EN           0x0008
-#define SIRFSOC_SPI_INT_EN             0x000C
-#define SIRFSOC_SPI_INT_STATUS         0x0010
-#define SIRFSOC_SPI_TX_DMA_IO_CTRL     0x0100
-#define SIRFSOC_SPI_TX_DMA_IO_LEN      0x0104
-#define SIRFSOC_SPI_TXFIFO_CTRL                0x0108
-#define SIRFSOC_SPI_TXFIFO_LEVEL_CHK   0x010C
-#define SIRFSOC_SPI_TXFIFO_OP          0x0110
-#define SIRFSOC_SPI_TXFIFO_STATUS      0x0114
-#define SIRFSOC_SPI_TXFIFO_DATA                0x0118
-#define SIRFSOC_SPI_RX_DMA_IO_CTRL     0x0120
-#define SIRFSOC_SPI_RX_DMA_IO_LEN      0x0124
-#define SIRFSOC_SPI_RXFIFO_CTRL                0x0128
-#define SIRFSOC_SPI_RXFIFO_LEVEL_CHK   0x012C
-#define SIRFSOC_SPI_RXFIFO_OP          0x0130
-#define SIRFSOC_SPI_RXFIFO_STATUS      0x0134
-#define SIRFSOC_SPI_RXFIFO_DATA                0x0138
-#define SIRFSOC_SPI_DUMMY_DELAY_CTL    0x0144
-
 /* SPI CTRL register defines */
 #define SIRFSOC_SPI_SLV_MODE           BIT(16)
 #define SIRFSOC_SPI_CMD_MODE           BIT(17)
@@ -80,8 +58,6 @@
 #define SIRFSOC_SPI_TXFIFO_THD_INT_EN  BIT(9)
 #define SIRFSOC_SPI_FRM_END_INT_EN     BIT(10)
 
-#define SIRFSOC_SPI_INT_MASK_ALL       0x1FFF
-
 /* Interrupt status */
 #define SIRFSOC_SPI_RX_DONE            BIT(0)
 #define SIRFSOC_SPI_TX_DONE            BIT(1)
 #define SIRFSOC_SPI_FIFO_WIDTH_BYTE    (0 << 0)
 #define SIRFSOC_SPI_FIFO_WIDTH_WORD    (1 << 0)
 #define SIRFSOC_SPI_FIFO_WIDTH_DWORD   (2 << 0)
-
-/* FIFO Status */
-#define        SIRFSOC_SPI_FIFO_LEVEL_MASK     0xFF
-#define SIRFSOC_SPI_FIFO_FULL          BIT(8)
-#define SIRFSOC_SPI_FIFO_EMPTY         BIT(9)
-
-/* 256 bytes rx/tx FIFO */
-#define SIRFSOC_SPI_FIFO_SIZE          256
-#define SIRFSOC_SPI_DAT_FRM_LEN_MAX    (64 * 1024)
-
-#define SIRFSOC_SPI_FIFO_SC(x)         ((x) & 0x3F)
-#define SIRFSOC_SPI_FIFO_LC(x)         (((x) & 0x3F) << 10)
-#define SIRFSOC_SPI_FIFO_HC(x)         (((x) & 0x3F) << 20)
-#define SIRFSOC_SPI_FIFO_THD(x)                (((x) & 0xFF) << 2)
+/* USP related */
+#define SIRFSOC_USP_SYNC_MODE          BIT(0)
+#define SIRFSOC_USP_SLV_MODE           BIT(1)
+#define SIRFSOC_USP_LSB                        BIT(4)
+#define SIRFSOC_USP_EN                 BIT(5)
+#define SIRFSOC_USP_RXD_FALLING_EDGE   BIT(6)
+#define SIRFSOC_USP_TXD_FALLING_EDGE   BIT(7)
+#define SIRFSOC_USP_CS_HIGH_VALID      BIT(9)
+#define SIRFSOC_USP_SCLK_IDLE_STAT     BIT(11)
+#define SIRFSOC_USP_TFS_IO_MODE                BIT(14)
+#define SIRFSOC_USP_TFS_IO_INPUT       BIT(19)
+
+#define SIRFSOC_USP_RXD_DELAY_LEN_MASK 0xFF
+#define SIRFSOC_USP_TXD_DELAY_LEN_MASK 0xFF
+#define SIRFSOC_USP_RXD_DELAY_OFFSET   0
+#define SIRFSOC_USP_TXD_DELAY_OFFSET   8
+#define SIRFSOC_USP_RXD_DELAY_LEN      1
+#define SIRFSOC_USP_TXD_DELAY_LEN      1
+#define SIRFSOC_USP_CLK_DIVISOR_OFFSET 21
+#define SIRFSOC_USP_CLK_DIVISOR_MASK   0x3FF
+#define SIRFSOC_USP_CLK_10_11_MASK     0x3
+#define SIRFSOC_USP_CLK_10_11_OFFSET   30
+#define SIRFSOC_USP_CLK_12_15_MASK     0xF
+#define SIRFSOC_USP_CLK_12_15_OFFSET   24
+
+#define SIRFSOC_USP_TX_DATA_OFFSET     0
+#define SIRFSOC_USP_TX_SYNC_OFFSET     8
+#define SIRFSOC_USP_TX_FRAME_OFFSET    16
+#define SIRFSOC_USP_TX_SHIFTER_OFFSET  24
+
+#define SIRFSOC_USP_TX_DATA_MASK       0xFF
+#define SIRFSOC_USP_TX_SYNC_MASK       0xFF
+#define SIRFSOC_USP_TX_FRAME_MASK      0xFF
+#define SIRFSOC_USP_TX_SHIFTER_MASK    0x1F
+
+#define SIRFSOC_USP_RX_DATA_OFFSET     0
+#define SIRFSOC_USP_RX_FRAME_OFFSET    8
+#define SIRFSOC_USP_RX_SHIFTER_OFFSET  16
+
+#define SIRFSOC_USP_RX_DATA_MASK       0xFF
+#define SIRFSOC_USP_RX_FRAME_MASK      0xFF
+#define SIRFSOC_USP_RX_SHIFTER_MASK    0x1F
+#define SIRFSOC_USP_CS_HIGH_VALUE      BIT(1)
+
+#define SIRFSOC_SPI_FIFO_SC_OFFSET     0
+#define SIRFSOC_SPI_FIFO_LC_OFFSET     10
+#define SIRFSOC_SPI_FIFO_HC_OFFSET     20
+
+#define SIRFSOC_SPI_FIFO_FULL_MASK(s)  (1 << ((s)->fifo_full_offset))
+#define SIRFSOC_SPI_FIFO_EMPTY_MASK(s) (1 << ((s)->fifo_full_offset + 1))
+#define SIRFSOC_SPI_FIFO_THD_MASK(s)   ((s)->fifo_size - 1)
+#define SIRFSOC_SPI_FIFO_THD_OFFSET    2
+#define SIRFSOC_SPI_FIFO_LEVEL_CHK_MASK(s, val)        \
+       ((val) & (s)->fifo_level_chk_mask)
+
+enum sirf_spi_type {
+       SIRF_REAL_SPI,
+       SIRF_USP_SPI_P2,
+       SIRF_USP_SPI_A7,
+};
 
 /*
  * only if the rx/tx buffer and transfer size are 4-bytes aligned, we use dma
 #define SIRFSOC_MAX_CMD_BYTES  4
 #define SIRFSOC_SPI_DEFAULT_FRQ 1000000
 
+struct sirf_spi_register {
+       /*SPI and USP-SPI common*/
+       u32 tx_rx_en;
+       u32 int_en;
+       u32 int_st;
+       u32 tx_dma_io_ctrl;
+       u32 tx_dma_io_len;
+       u32 txfifo_ctrl;
+       u32 txfifo_level_chk;
+       u32 txfifo_op;
+       u32 txfifo_st;
+       u32 txfifo_data;
+       u32 rx_dma_io_ctrl;
+       u32 rx_dma_io_len;
+       u32 rxfifo_ctrl;
+       u32 rxfifo_level_chk;
+       u32 rxfifo_op;
+       u32 rxfifo_st;
+       u32 rxfifo_data;
+       /*SPI self*/
+       u32 spi_ctrl;
+       u32 spi_cmd;
+       u32 spi_dummy_delay_ctrl;
+       /*USP-SPI self*/
+       u32 usp_mode1;
+       u32 usp_mode2;
+       u32 usp_tx_frame_ctrl;
+       u32 usp_rx_frame_ctrl;
+       u32 usp_pin_io_data;
+       u32 usp_risc_dsp_mode;
+       u32 usp_async_param_reg;
+       u32 usp_irda_x_mode_div;
+       u32 usp_sm_cfg;
+       u32 usp_int_en_clr;
+};
+
+static const struct sirf_spi_register real_spi_register = {
+       .tx_rx_en               = 0x8,
+       .int_en         = 0xc,
+       .int_st         = 0x10,
+       .tx_dma_io_ctrl = 0x100,
+       .tx_dma_io_len  = 0x104,
+       .txfifo_ctrl    = 0x108,
+       .txfifo_level_chk       = 0x10c,
+       .txfifo_op              = 0x110,
+       .txfifo_st              = 0x114,
+       .txfifo_data    = 0x118,
+       .rx_dma_io_ctrl = 0x120,
+       .rx_dma_io_len  = 0x124,
+       .rxfifo_ctrl    = 0x128,
+       .rxfifo_level_chk       = 0x12c,
+       .rxfifo_op              = 0x130,
+       .rxfifo_st              = 0x134,
+       .rxfifo_data    = 0x138,
+       .spi_ctrl               = 0x0,
+       .spi_cmd                = 0x4,
+       .spi_dummy_delay_ctrl   = 0x144,
+};
+
+static const struct sirf_spi_register usp_spi_register = {
+       .tx_rx_en               = 0x10,
+       .int_en         = 0x14,
+       .int_st         = 0x18,
+       .tx_dma_io_ctrl = 0x100,
+       .tx_dma_io_len  = 0x104,
+       .txfifo_ctrl    = 0x108,
+       .txfifo_level_chk       = 0x10c,
+       .txfifo_op              = 0x110,
+       .txfifo_st              = 0x114,
+       .txfifo_data    = 0x118,
+       .rx_dma_io_ctrl = 0x120,
+       .rx_dma_io_len  = 0x124,
+       .rxfifo_ctrl    = 0x128,
+       .rxfifo_level_chk       = 0x12c,
+       .rxfifo_op              = 0x130,
+       .rxfifo_st              = 0x134,
+       .rxfifo_data    = 0x138,
+       .usp_mode1              = 0x0,
+       .usp_mode2              = 0x4,
+       .usp_tx_frame_ctrl      = 0x8,
+       .usp_rx_frame_ctrl      = 0xc,
+       .usp_pin_io_data        = 0x1c,
+       .usp_risc_dsp_mode      = 0x20,
+       .usp_async_param_reg    = 0x24,
+       .usp_irda_x_mode_div    = 0x28,
+       .usp_sm_cfg             = 0x2c,
+       .usp_int_en_clr         = 0x140,
+};
+
 struct sirfsoc_spi {
        struct spi_bitbang bitbang;
        struct completion rx_done;
@@ -164,7 +275,6 @@ struct sirfsoc_spi {
        struct dma_chan *tx_chan;
        dma_addr_t src_start;
        dma_addr_t dst_start;
-       void *dummypage;
        int word_width; /* in bytes */
 
        /*
@@ -173,14 +283,39 @@ struct sirfsoc_spi {
         */
        bool    tx_by_cmd;
        bool    hw_cs;
+       enum sirf_spi_type type;
+       const struct sirf_spi_register *regs;
+       unsigned int fifo_size;
+       /* fifo empty offset is (fifo full offset + 1)*/
+       unsigned int fifo_full_offset;
+       /* fifo_level_chk_mask is (fifo_size/4 - 1) */
+       unsigned int fifo_level_chk_mask;
+       unsigned int dat_max_frm_len;
+};
+
+struct sirf_spi_comp_data {
+       const struct sirf_spi_register *regs;
+       enum sirf_spi_type type;
+       unsigned int dat_max_frm_len;
+       unsigned int fifo_size;
+       void (*hwinit)(struct sirfsoc_spi *sspi);
 };
 
+static void sirfsoc_usp_hwinit(struct sirfsoc_spi *sspi)
+{
+       /* reset USP and let USP can operate */
+       writel(readl(sspi->base + sspi->regs->usp_mode1) &
+               ~SIRFSOC_USP_EN, sspi->base + sspi->regs->usp_mode1);
+       writel(readl(sspi->base + sspi->regs->usp_mode1) |
+               SIRFSOC_USP_EN, sspi->base + sspi->regs->usp_mode1);
+}
+
 static void spi_sirfsoc_rx_word_u8(struct sirfsoc_spi *sspi)
 {
        u32 data;
        u8 *rx = sspi->rx;
 
-       data = readl(sspi->base + SIRFSOC_SPI_RXFIFO_DATA);
+       data = readl(sspi->base + sspi->regs->rxfifo_data);
 
        if (rx) {
                *rx++ = (u8) data;
@@ -199,8 +334,7 @@ static void spi_sirfsoc_tx_word_u8(struct sirfsoc_spi *sspi)
                data = *tx++;
                sspi->tx = tx;
        }
-
-       writel(data, sspi->base + SIRFSOC_SPI_TXFIFO_DATA);
+       writel(data, sspi->base + sspi->regs->txfifo_data);
        sspi->left_tx_word--;
 }
 
@@ -209,7 +343,7 @@ static void spi_sirfsoc_rx_word_u16(struct sirfsoc_spi *sspi)
        u32 data;
        u16 *rx = sspi->rx;
 
-       data = readl(sspi->base + SIRFSOC_SPI_RXFIFO_DATA);
+       data = readl(sspi->base + sspi->regs->rxfifo_data);
 
        if (rx) {
                *rx++ = (u16) data;
@@ -229,7 +363,7 @@ static void spi_sirfsoc_tx_word_u16(struct sirfsoc_spi *sspi)
                sspi->tx = tx;
        }
 
-       writel(data, sspi->base + SIRFSOC_SPI_TXFIFO_DATA);
+       writel(data, sspi->base + sspi->regs->txfifo_data);
        sspi->left_tx_word--;
 }
 
@@ -238,7 +372,7 @@ static void spi_sirfsoc_rx_word_u32(struct sirfsoc_spi *sspi)
        u32 data;
        u32 *rx = sspi->rx;
 
-       data = readl(sspi->base + SIRFSOC_SPI_RXFIFO_DATA);
+       data = readl(sspi->base + sspi->regs->rxfifo_data);
 
        if (rx) {
                *rx++ = (u32) data;
@@ -259,41 +393,59 @@ static void spi_sirfsoc_tx_word_u32(struct sirfsoc_spi *sspi)
                sspi->tx = tx;
        }
 
-       writel(data, sspi->base + SIRFSOC_SPI_TXFIFO_DATA);
+       writel(data, sspi->base + sspi->regs->txfifo_data);
        sspi->left_tx_word--;
 }
 
 static irqreturn_t spi_sirfsoc_irq(int irq, void *dev_id)
 {
        struct sirfsoc_spi *sspi = dev_id;
-       u32 spi_stat = readl(sspi->base + SIRFSOC_SPI_INT_STATUS);
-       if (sspi->tx_by_cmd && (spi_stat & SIRFSOC_SPI_FRM_END)) {
+       u32 spi_stat;
+
+       spi_stat = readl(sspi->base + sspi->regs->int_st);
+       if (sspi->tx_by_cmd && sspi->type == SIRF_REAL_SPI
+               && (spi_stat & SIRFSOC_SPI_FRM_END)) {
                complete(&sspi->tx_done);
-               writel(0x0, sspi->base + SIRFSOC_SPI_INT_EN);
-               writel(SIRFSOC_SPI_INT_MASK_ALL,
-                               sspi->base + SIRFSOC_SPI_INT_STATUS);
+               writel(0x0, sspi->base + sspi->regs->int_en);
+               writel(readl(sspi->base + sspi->regs->int_st),
+                               sspi->base + sspi->regs->int_st);
                return IRQ_HANDLED;
        }
-
        /* Error Conditions */
        if (spi_stat & SIRFSOC_SPI_RX_OFLOW ||
                        spi_stat & SIRFSOC_SPI_TX_UFLOW) {
                complete(&sspi->tx_done);
                complete(&sspi->rx_done);
-               writel(0x0, sspi->base + SIRFSOC_SPI_INT_EN);
-               writel(SIRFSOC_SPI_INT_MASK_ALL,
-                               sspi->base + SIRFSOC_SPI_INT_STATUS);
+               switch (sspi->type) {
+               case SIRF_REAL_SPI:
+               case SIRF_USP_SPI_P2:
+                       writel(0x0, sspi->base + sspi->regs->int_en);
+                       break;
+               case SIRF_USP_SPI_A7:
+                       writel(~0UL, sspi->base + sspi->regs->usp_int_en_clr);
+                       break;
+               }
+               writel(readl(sspi->base + sspi->regs->int_st),
+                               sspi->base + sspi->regs->int_st);
                return IRQ_HANDLED;
        }
        if (spi_stat & SIRFSOC_SPI_TXFIFO_EMPTY)
                complete(&sspi->tx_done);
-       while (!(readl(sspi->base + SIRFSOC_SPI_INT_STATUS) &
+       while (!(readl(sspi->base + sspi->regs->int_st) &
                SIRFSOC_SPI_RX_IO_DMA))
                cpu_relax();
        complete(&sspi->rx_done);
-       writel(0x0, sspi->base + SIRFSOC_SPI_INT_EN);
-       writel(SIRFSOC_SPI_INT_MASK_ALL,
-                       sspi->base + SIRFSOC_SPI_INT_STATUS);
+       switch (sspi->type) {
+       case SIRF_REAL_SPI:
+       case SIRF_USP_SPI_P2:
+               writel(0x0, sspi->base + sspi->regs->int_en);
+               break;
+       case SIRF_USP_SPI_A7:
+               writel(~0UL, sspi->base + sspi->regs->usp_int_en_clr);
+               break;
+       }
+       writel(readl(sspi->base + sspi->regs->int_st),
+                       sspi->base + sspi->regs->int_st);
 
        return IRQ_HANDLED;
 }
@@ -313,8 +465,8 @@ static void spi_sirfsoc_cmd_transfer(struct spi_device *spi,
        u32 cmd;
 
        sspi = spi_master_get_devdata(spi->master);
-       writel(SIRFSOC_SPI_FIFO_RESET, sspi->base + SIRFSOC_SPI_TXFIFO_OP);
-       writel(SIRFSOC_SPI_FIFO_START, sspi->base + SIRFSOC_SPI_TXFIFO_OP);
+       writel(SIRFSOC_SPI_FIFO_RESET, sspi->base + sspi->regs->txfifo_op);
+       writel(SIRFSOC_SPI_FIFO_START, sspi->base + sspi->regs->txfifo_op);
        memcpy(&cmd, sspi->tx, t->len);
        if (sspi->word_width == 1 && !(spi->mode & SPI_LSB_FIRST))
                cmd = cpu_to_be32(cmd) >>
@@ -322,11 +474,11 @@ static void spi_sirfsoc_cmd_transfer(struct spi_device *spi,
        if (sspi->word_width == 2 && t->len == 4 &&
                        (!(spi->mode & SPI_LSB_FIRST)))
                cmd = ((cmd & 0xffff) << 16) | (cmd >> 16);
-       writel(cmd, sspi->base + SIRFSOC_SPI_CMD);
+       writel(cmd, sspi->base + sspi->regs->spi_cmd);
        writel(SIRFSOC_SPI_FRM_END_INT_EN,
-               sspi->base + SIRFSOC_SPI_INT_EN);
+               sspi->base + sspi->regs->int_en);
        writel(SIRFSOC_SPI_CMD_TX_EN,
-               sspi->base + SIRFSOC_SPI_TX_RX_EN);
+               sspi->base + sspi->regs->tx_rx_en);
        if (wait_for_completion_timeout(&sspi->tx_done, timeout) == 0) {
                dev_err(&spi->dev, "cmd transfer timeout\n");
                return;
@@ -342,25 +494,56 @@ static void spi_sirfsoc_dma_transfer(struct spi_device *spi,
        int timeout = t->len * 10;
 
        sspi = spi_master_get_devdata(spi->master);
-       writel(SIRFSOC_SPI_FIFO_RESET, sspi->base + SIRFSOC_SPI_RXFIFO_OP);
-       writel(SIRFSOC_SPI_FIFO_RESET, sspi->base + SIRFSOC_SPI_TXFIFO_OP);
-       writel(SIRFSOC_SPI_FIFO_START, sspi->base + SIRFSOC_SPI_RXFIFO_OP);
-       writel(SIRFSOC_SPI_FIFO_START, sspi->base + SIRFSOC_SPI_TXFIFO_OP);
-       writel(0, sspi->base + SIRFSOC_SPI_INT_EN);
-       writel(SIRFSOC_SPI_INT_MASK_ALL, sspi->base + SIRFSOC_SPI_INT_STATUS);
-       if (sspi->left_tx_word < SIRFSOC_SPI_DAT_FRM_LEN_MAX) {
-               writel(readl(sspi->base + SIRFSOC_SPI_CTRL) |
-                       SIRFSOC_SPI_ENA_AUTO_CLR | SIRFSOC_SPI_MUL_DAT_MODE,
-                       sspi->base + SIRFSOC_SPI_CTRL);
-               writel(sspi->left_tx_word - 1,
-                               sspi->base + SIRFSOC_SPI_TX_DMA_IO_LEN);
-               writel(sspi->left_tx_word - 1,
-                               sspi->base + SIRFSOC_SPI_RX_DMA_IO_LEN);
+       writel(SIRFSOC_SPI_FIFO_RESET, sspi->base + sspi->regs->rxfifo_op);
+       writel(SIRFSOC_SPI_FIFO_RESET, sspi->base + sspi->regs->txfifo_op);
+       switch (sspi->type) {
+       case SIRF_REAL_SPI:
+               writel(SIRFSOC_SPI_FIFO_START,
+                       sspi->base + sspi->regs->rxfifo_op);
+               writel(SIRFSOC_SPI_FIFO_START,
+                       sspi->base + sspi->regs->txfifo_op);
+               writel(0, sspi->base + sspi->regs->int_en);
+               break;
+       case SIRF_USP_SPI_P2:
+               writel(0x0, sspi->base + sspi->regs->rxfifo_op);
+               writel(0x0, sspi->base + sspi->regs->txfifo_op);
+               writel(0, sspi->base + sspi->regs->int_en);
+               break;
+       case SIRF_USP_SPI_A7:
+               writel(0x0, sspi->base + sspi->regs->rxfifo_op);
+               writel(0x0, sspi->base + sspi->regs->txfifo_op);
+               writel(~0UL, sspi->base + sspi->regs->usp_int_en_clr);
+               break;
+       }
+       writel(readl(sspi->base + sspi->regs->int_st),
+               sspi->base + sspi->regs->int_st);
+       if (sspi->left_tx_word < sspi->dat_max_frm_len) {
+               switch (sspi->type) {
+               case SIRF_REAL_SPI:
+                       writel(readl(sspi->base + sspi->regs->spi_ctrl) |
+                               SIRFSOC_SPI_ENA_AUTO_CLR |
+                               SIRFSOC_SPI_MUL_DAT_MODE,
+                               sspi->base + sspi->regs->spi_ctrl);
+                       writel(sspi->left_tx_word - 1,
+                               sspi->base + sspi->regs->tx_dma_io_len);
+                       writel(sspi->left_tx_word - 1,
+                               sspi->base + sspi->regs->rx_dma_io_len);
+                       break;
+               case SIRF_USP_SPI_P2:
+               case SIRF_USP_SPI_A7:
+                       /*USP simulate SPI, tx/rx_dma_io_len indicates bytes*/
+                       writel(sspi->left_tx_word * sspi->word_width,
+                               sspi->base + sspi->regs->tx_dma_io_len);
+                       writel(sspi->left_tx_word * sspi->word_width,
+                               sspi->base + sspi->regs->rx_dma_io_len);
+                       break;
+               }
        } else {
-               writel(readl(sspi->base + SIRFSOC_SPI_CTRL),
-                       sspi->base + SIRFSOC_SPI_CTRL);
-               writel(0, sspi->base + SIRFSOC_SPI_TX_DMA_IO_LEN);
-               writel(0, sspi->base + SIRFSOC_SPI_RX_DMA_IO_LEN);
+               if (sspi->type == SIRF_REAL_SPI)
+                       writel(readl(sspi->base + sspi->regs->spi_ctrl),
+                               sspi->base + sspi->regs->spi_ctrl);
+               writel(0, sspi->base + sspi->regs->tx_dma_io_len);
+               writel(0, sspi->base + sspi->regs->rx_dma_io_len);
        }
        sspi->dst_start = dma_map_single(&spi->dev, sspi->rx, t->len,
                                        (t->tx_buf != t->rx_buf) ?
@@ -385,7 +568,14 @@ static void spi_sirfsoc_dma_transfer(struct spi_device *spi,
        dma_async_issue_pending(sspi->tx_chan);
        dma_async_issue_pending(sspi->rx_chan);
        writel(SIRFSOC_SPI_RX_EN | SIRFSOC_SPI_TX_EN,
-                       sspi->base + SIRFSOC_SPI_TX_RX_EN);
+                       sspi->base + sspi->regs->tx_rx_en);
+       if (sspi->type == SIRF_USP_SPI_P2 ||
+               sspi->type == SIRF_USP_SPI_A7) {
+               writel(SIRFSOC_SPI_FIFO_START,
+                       sspi->base + sspi->regs->rxfifo_op);
+               writel(SIRFSOC_SPI_FIFO_START,
+                       sspi->base + sspi->regs->txfifo_op);
+       }
        if (wait_for_completion_timeout(&sspi->rx_done, timeout) == 0) {
                dev_err(&spi->dev, "transfer timeout\n");
                dmaengine_terminate_all(sspi->rx_chan);
@@ -398,15 +588,21 @@ static void spi_sirfsoc_dma_transfer(struct spi_device *spi,
         */
        if (wait_for_completion_timeout(&sspi->tx_done, timeout) == 0) {
                dev_err(&spi->dev, "transfer timeout\n");
+               if (sspi->type == SIRF_USP_SPI_P2 ||
+                       sspi->type == SIRF_USP_SPI_A7)
+                       writel(0, sspi->base + sspi->regs->tx_rx_en);
                dmaengine_terminate_all(sspi->tx_chan);
        }
        dma_unmap_single(&spi->dev, sspi->src_start, t->len, DMA_TO_DEVICE);
        dma_unmap_single(&spi->dev, sspi->dst_start, t->len, DMA_FROM_DEVICE);
        /* TX, RX FIFO stop */
-       writel(0, sspi->base + SIRFSOC_SPI_RXFIFO_OP);
-       writel(0, sspi->base + SIRFSOC_SPI_TXFIFO_OP);
-       if (sspi->left_tx_word >= SIRFSOC_SPI_DAT_FRM_LEN_MAX)
-               writel(0, sspi->base + SIRFSOC_SPI_TX_RX_EN);
+       writel(0, sspi->base + sspi->regs->rxfifo_op);
+       writel(0, sspi->base + sspi->regs->txfifo_op);
+       if (sspi->left_tx_word >= sspi->dat_max_frm_len)
+               writel(0, sspi->base + sspi->regs->tx_rx_en);
+       if (sspi->type == SIRF_USP_SPI_P2 ||
+               sspi->type == SIRF_USP_SPI_A7)
+               writel(0, sspi->base + sspi->regs->tx_rx_en);
 }
 
 static void spi_sirfsoc_pio_transfer(struct spi_device *spi,
@@ -414,57 +610,105 @@ static void spi_sirfsoc_pio_transfer(struct spi_device *spi,
 {
        struct sirfsoc_spi *sspi;
        int timeout = t->len * 10;
+       unsigned int data_units;
 
        sspi = spi_master_get_devdata(spi->master);
        do {
                writel(SIRFSOC_SPI_FIFO_RESET,
-                       sspi->base + SIRFSOC_SPI_RXFIFO_OP);
+                       sspi->base + sspi->regs->rxfifo_op);
                writel(SIRFSOC_SPI_FIFO_RESET,
-                       sspi->base + SIRFSOC_SPI_TXFIFO_OP);
-               writel(SIRFSOC_SPI_FIFO_START,
-                       sspi->base + SIRFSOC_SPI_RXFIFO_OP);
-               writel(SIRFSOC_SPI_FIFO_START,
-                       sspi->base + SIRFSOC_SPI_TXFIFO_OP);
-               writel(0, sspi->base + SIRFSOC_SPI_INT_EN);
-               writel(SIRFSOC_SPI_INT_MASK_ALL,
-                       sspi->base + SIRFSOC_SPI_INT_STATUS);
-               writel(readl(sspi->base + SIRFSOC_SPI_CTRL) |
-                       SIRFSOC_SPI_MUL_DAT_MODE | SIRFSOC_SPI_ENA_AUTO_CLR,
-                       sspi->base + SIRFSOC_SPI_CTRL);
-               writel(min(sspi->left_tx_word, (u32)(256 / sspi->word_width))
-                               - 1, sspi->base + SIRFSOC_SPI_TX_DMA_IO_LEN);
-               writel(min(sspi->left_rx_word, (u32)(256 / sspi->word_width))
-                               - 1, sspi->base + SIRFSOC_SPI_RX_DMA_IO_LEN);
-               while (!((readl(sspi->base + SIRFSOC_SPI_TXFIFO_STATUS)
-                       & SIRFSOC_SPI_FIFO_FULL)) && sspi->left_tx_word)
+                       sspi->base + sspi->regs->txfifo_op);
+               switch (sspi->type) {
+               case SIRF_USP_SPI_P2:
+                       writel(0x0, sspi->base + sspi->regs->rxfifo_op);
+                       writel(0x0, sspi->base + sspi->regs->txfifo_op);
+                       writel(0, sspi->base + sspi->regs->int_en);
+                       writel(readl(sspi->base + sspi->regs->int_st),
+                               sspi->base + sspi->regs->int_st);
+                       writel(min((sspi->left_tx_word * sspi->word_width),
+                               sspi->fifo_size),
+                               sspi->base + sspi->regs->tx_dma_io_len);
+                       writel(min((sspi->left_rx_word * sspi->word_width),
+                               sspi->fifo_size),
+                               sspi->base + sspi->regs->rx_dma_io_len);
+                       break;
+               case SIRF_USP_SPI_A7:
+                       writel(0x0, sspi->base + sspi->regs->rxfifo_op);
+                       writel(0x0, sspi->base + sspi->regs->txfifo_op);
+                       writel(~0UL, sspi->base + sspi->regs->usp_int_en_clr);
+                       writel(readl(sspi->base + sspi->regs->int_st),
+                               sspi->base + sspi->regs->int_st);
+                       writel(min((sspi->left_tx_word * sspi->word_width),
+                               sspi->fifo_size),
+                               sspi->base + sspi->regs->tx_dma_io_len);
+                       writel(min((sspi->left_rx_word * sspi->word_width),
+                               sspi->fifo_size),
+                               sspi->base + sspi->regs->rx_dma_io_len);
+                       break;
+               case SIRF_REAL_SPI:
+                       writel(SIRFSOC_SPI_FIFO_START,
+                               sspi->base + sspi->regs->rxfifo_op);
+                       writel(SIRFSOC_SPI_FIFO_START,
+                               sspi->base + sspi->regs->txfifo_op);
+                       writel(0, sspi->base + sspi->regs->int_en);
+                       writel(readl(sspi->base + sspi->regs->int_st),
+                               sspi->base + sspi->regs->int_st);
+                       writel(readl(sspi->base + sspi->regs->spi_ctrl) |
+                               SIRFSOC_SPI_MUL_DAT_MODE |
+                               SIRFSOC_SPI_ENA_AUTO_CLR,
+                               sspi->base + sspi->regs->spi_ctrl);
+                       data_units = sspi->fifo_size / sspi->word_width;
+                       writel(min(sspi->left_tx_word, data_units) - 1,
+                               sspi->base + sspi->regs->tx_dma_io_len);
+                       writel(min(sspi->left_rx_word, data_units) - 1,
+                               sspi->base + sspi->regs->rx_dma_io_len);
+                       break;
+               }
+               while (!((readl(sspi->base + sspi->regs->txfifo_st)
+                       & SIRFSOC_SPI_FIFO_FULL_MASK(sspi))) &&
+                       sspi->left_tx_word)
                        sspi->tx_word(sspi);
                writel(SIRFSOC_SPI_TXFIFO_EMPTY_INT_EN |
                        SIRFSOC_SPI_TX_UFLOW_INT_EN |
                        SIRFSOC_SPI_RX_OFLOW_INT_EN |
                        SIRFSOC_SPI_RX_IO_DMA_INT_EN,
-                       sspi->base + SIRFSOC_SPI_INT_EN);
+                       sspi->base + sspi->regs->int_en);
                writel(SIRFSOC_SPI_RX_EN | SIRFSOC_SPI_TX_EN,
-                       sspi->base + SIRFSOC_SPI_TX_RX_EN);
+                       sspi->base + sspi->regs->tx_rx_en);
+               if (sspi->type == SIRF_USP_SPI_P2 ||
+                       sspi->type == SIRF_USP_SPI_A7) {
+                       writel(SIRFSOC_SPI_FIFO_START,
+                               sspi->base + sspi->regs->rxfifo_op);
+                       writel(SIRFSOC_SPI_FIFO_START,
+                               sspi->base + sspi->regs->txfifo_op);
+               }
                if (!wait_for_completion_timeout(&sspi->tx_done, timeout) ||
                        !wait_for_completion_timeout(&sspi->rx_done, timeout)) {
                        dev_err(&spi->dev, "transfer timeout\n");
+                       if (sspi->type == SIRF_USP_SPI_P2 ||
+                               sspi->type == SIRF_USP_SPI_A7)
+                               writel(0, sspi->base + sspi->regs->tx_rx_en);
                        break;
                }
-               while (!((readl(sspi->base + SIRFSOC_SPI_RXFIFO_STATUS)
-                       & SIRFSOC_SPI_FIFO_EMPTY)) && sspi->left_rx_word)
+               while (!((readl(sspi->base + sspi->regs->rxfifo_st)
+                       & SIRFSOC_SPI_FIFO_EMPTY_MASK(sspi))) &&
+                       sspi->left_rx_word)
                        sspi->rx_word(sspi);
-               writel(0, sspi->base + SIRFSOC_SPI_RXFIFO_OP);
-               writel(0, sspi->base + SIRFSOC_SPI_TXFIFO_OP);
+               if (sspi->type == SIRF_USP_SPI_P2 ||
+                       sspi->type == SIRF_USP_SPI_A7)
+                       writel(0, sspi->base + sspi->regs->tx_rx_en);
+               writel(0, sspi->base + sspi->regs->rxfifo_op);
+               writel(0, sspi->base + sspi->regs->txfifo_op);
        } while (sspi->left_tx_word != 0 || sspi->left_rx_word != 0);
 }
 
 static int spi_sirfsoc_transfer(struct spi_device *spi, struct spi_transfer *t)
 {
        struct sirfsoc_spi *sspi;
-       sspi = spi_master_get_devdata(spi->master);
 
-       sspi->tx = t->tx_buf ? t->tx_buf : sspi->dummypage;
-       sspi->rx = t->rx_buf ? t->rx_buf : sspi->dummypage;
+       sspi = spi_master_get_devdata(spi->master);
+       sspi->tx = t->tx_buf;
+       sspi->rx = t->rx_buf;
        sspi->left_tx_word = sspi->left_rx_word = t->len / sspi->word_width;
        reinit_completion(&sspi->rx_done);
        reinit_completion(&sspi->tx_done);
@@ -473,7 +717,7 @@ static int spi_sirfsoc_transfer(struct spi_device *spi, struct spi_transfer *t)
         * null, just fill command data into command register and wait for its
         * completion.
         */
-       if (sspi->tx_by_cmd)
+       if (sspi->type == SIRF_REAL_SPI && sspi->tx_by_cmd)
                spi_sirfsoc_cmd_transfer(spi, t);
        else if (IS_DMA_VALID(t))
                spi_sirfsoc_dma_transfer(spi, t);
@@ -488,22 +732,49 @@ static void spi_sirfsoc_chipselect(struct spi_device *spi, int value)
        struct sirfsoc_spi *sspi = spi_master_get_devdata(spi->master);
 
        if (sspi->hw_cs) {
-               u32 regval = readl(sspi->base + SIRFSOC_SPI_CTRL);
-               switch (value) {
-               case BITBANG_CS_ACTIVE:
-                       if (spi->mode & SPI_CS_HIGH)
-                               regval |= SIRFSOC_SPI_CS_IO_OUT;
-                       else
-                               regval &= ~SIRFSOC_SPI_CS_IO_OUT;
+               u32 regval;
+
+               switch (sspi->type) {
+               case SIRF_REAL_SPI:
+                       regval = readl(sspi->base + sspi->regs->spi_ctrl);
+                       switch (value) {
+                       case BITBANG_CS_ACTIVE:
+                               if (spi->mode & SPI_CS_HIGH)
+                                       regval |= SIRFSOC_SPI_CS_IO_OUT;
+                               else
+                                       regval &= ~SIRFSOC_SPI_CS_IO_OUT;
+                               break;
+                       case BITBANG_CS_INACTIVE:
+                               if (spi->mode & SPI_CS_HIGH)
+                                       regval &= ~SIRFSOC_SPI_CS_IO_OUT;
+                               else
+                                       regval |= SIRFSOC_SPI_CS_IO_OUT;
+                               break;
+                       }
+                       writel(regval, sspi->base + sspi->regs->spi_ctrl);
                        break;
-               case BITBANG_CS_INACTIVE:
-                       if (spi->mode & SPI_CS_HIGH)
-                               regval &= ~SIRFSOC_SPI_CS_IO_OUT;
-                       else
-                               regval |= SIRFSOC_SPI_CS_IO_OUT;
+               case SIRF_USP_SPI_P2:
+               case SIRF_USP_SPI_A7:
+                       regval = readl(sspi->base +
+                                       sspi->regs->usp_pin_io_data);
+                       switch (value) {
+                       case BITBANG_CS_ACTIVE:
+                               if (spi->mode & SPI_CS_HIGH)
+                                       regval |= SIRFSOC_USP_CS_HIGH_VALUE;
+                               else
+                                       regval &= ~(SIRFSOC_USP_CS_HIGH_VALUE);
+                               break;
+                       case BITBANG_CS_INACTIVE:
+                               if (spi->mode & SPI_CS_HIGH)
+                                       regval &= ~(SIRFSOC_USP_CS_HIGH_VALUE);
+                               else
+                                       regval |= SIRFSOC_USP_CS_HIGH_VALUE;
+                               break;
+                       }
+                       writel(regval,
+                               sspi->base + sspi->regs->usp_pin_io_data);
                        break;
                }
-               writel(regval, sspi->base + SIRFSOC_SPI_CTRL);
        } else {
                switch (value) {
                case BITBANG_CS_ACTIVE:
@@ -518,27 +789,102 @@ static void spi_sirfsoc_chipselect(struct spi_device *spi, int value)
        }
 }
 
+static int spi_sirfsoc_config_mode(struct spi_device *spi)
+{
+       struct sirfsoc_spi *sspi;
+       u32 regval, usp_mode1;
+
+       sspi = spi_master_get_devdata(spi->master);
+       regval = readl(sspi->base + sspi->regs->spi_ctrl);
+       usp_mode1 = readl(sspi->base + sspi->regs->usp_mode1);
+       if (!(spi->mode & SPI_CS_HIGH)) {
+               regval |= SIRFSOC_SPI_CS_IDLE_STAT;
+               usp_mode1 &= ~SIRFSOC_USP_CS_HIGH_VALID;
+       } else {
+               regval &= ~SIRFSOC_SPI_CS_IDLE_STAT;
+               usp_mode1 |= SIRFSOC_USP_CS_HIGH_VALID;
+       }
+       if (!(spi->mode & SPI_LSB_FIRST)) {
+               regval |= SIRFSOC_SPI_TRAN_MSB;
+               usp_mode1 &= ~SIRFSOC_USP_LSB;
+       } else {
+               regval &= ~SIRFSOC_SPI_TRAN_MSB;
+               usp_mode1 |= SIRFSOC_USP_LSB;
+       }
+       if (spi->mode & SPI_CPOL) {
+               regval |= SIRFSOC_SPI_CLK_IDLE_STAT;
+               usp_mode1 |= SIRFSOC_USP_SCLK_IDLE_STAT;
+       } else {
+               regval &= ~SIRFSOC_SPI_CLK_IDLE_STAT;
+               usp_mode1 &= ~SIRFSOC_USP_SCLK_IDLE_STAT;
+       }
+       /*
+        * Data should be driven at least 1/2 cycle before the fetch edge
+        * to make sure that data gets stable at the fetch edge.
+        */
+       if (((spi->mode & SPI_CPOL) && (spi->mode & SPI_CPHA)) ||
+           (!(spi->mode & SPI_CPOL) && !(spi->mode & SPI_CPHA))) {
+               regval &= ~SIRFSOC_SPI_DRV_POS_EDGE;
+               usp_mode1 |= (SIRFSOC_USP_TXD_FALLING_EDGE |
+                               SIRFSOC_USP_RXD_FALLING_EDGE);
+       } else {
+               regval |= SIRFSOC_SPI_DRV_POS_EDGE;
+               usp_mode1 &= ~(SIRFSOC_USP_RXD_FALLING_EDGE |
+                               SIRFSOC_USP_TXD_FALLING_EDGE);
+       }
+       writel((SIRFSOC_SPI_FIFO_LEVEL_CHK_MASK(sspi, sspi->fifo_size - 2) <<
+               SIRFSOC_SPI_FIFO_SC_OFFSET) |
+               (SIRFSOC_SPI_FIFO_LEVEL_CHK_MASK(sspi, sspi->fifo_size / 2) <<
+               SIRFSOC_SPI_FIFO_LC_OFFSET) |
+               (SIRFSOC_SPI_FIFO_LEVEL_CHK_MASK(sspi, 2) <<
+               SIRFSOC_SPI_FIFO_HC_OFFSET),
+               sspi->base + sspi->regs->txfifo_level_chk);
+       writel((SIRFSOC_SPI_FIFO_LEVEL_CHK_MASK(sspi, 2) <<
+               SIRFSOC_SPI_FIFO_SC_OFFSET) |
+               (SIRFSOC_SPI_FIFO_LEVEL_CHK_MASK(sspi, sspi->fifo_size / 2) <<
+               SIRFSOC_SPI_FIFO_LC_OFFSET) |
+               (SIRFSOC_SPI_FIFO_LEVEL_CHK_MASK(sspi, sspi->fifo_size - 2) <<
+               SIRFSOC_SPI_FIFO_HC_OFFSET),
+               sspi->base + sspi->regs->rxfifo_level_chk);
+       /*
+        * it should never set to hardware cs mode because in hardware cs mode,
+        * cs signal can't controlled by driver.
+        */
+       switch (sspi->type) {
+       case SIRF_REAL_SPI:
+               regval |= SIRFSOC_SPI_CS_IO_MODE;
+               writel(regval, sspi->base + sspi->regs->spi_ctrl);
+               break;
+       case SIRF_USP_SPI_P2:
+       case SIRF_USP_SPI_A7:
+               usp_mode1 |= SIRFSOC_USP_SYNC_MODE;
+               usp_mode1 |= SIRFSOC_USP_TFS_IO_MODE;
+               usp_mode1 &= ~SIRFSOC_USP_TFS_IO_INPUT;
+               writel(usp_mode1, sspi->base + sspi->regs->usp_mode1);
+               break;
+       }
+
+       return 0;
+}
+
 static int
 spi_sirfsoc_setup_transfer(struct spi_device *spi, struct spi_transfer *t)
 {
        struct sirfsoc_spi *sspi;
        u8 bits_per_word = 0;
        int hz = 0;
-       u32 regval;
-       u32 txfifo_ctrl, rxfifo_ctrl;
-       u32 fifo_size = SIRFSOC_SPI_FIFO_SIZE / 4;
+       u32 regval, txfifo_ctrl, rxfifo_ctrl, tx_frm_ctl, rx_frm_ctl, usp_mode2;
 
        sspi = spi_master_get_devdata(spi->master);
 
        bits_per_word = (t) ? t->bits_per_word : spi->bits_per_word;
        hz = t && t->speed_hz ? t->speed_hz : spi->max_speed_hz;
 
-       regval = (sspi->ctrl_freq / (2 * hz)) - 1;
+       usp_mode2 = regval = (sspi->ctrl_freq / (2 * hz)) - 1;
        if (regval > 0xFFFF || regval < 0) {
                dev_err(&spi->dev, "Speed %d not supported\n", hz);
                return -EINVAL;
        }
-
        switch (bits_per_word) {
        case 8:
                regval |= SIRFSOC_SPI_TRAN_DAT_FORMAT_8;
@@ -559,94 +905,177 @@ spi_sirfsoc_setup_transfer(struct spi_device *spi, struct spi_transfer *t)
                sspi->tx_word = spi_sirfsoc_tx_word_u32;
                break;
        default:
-               BUG();
+               dev_err(&spi->dev, "bpw %d not supported\n", bits_per_word);
+               return -EINVAL;
        }
-
        sspi->word_width = DIV_ROUND_UP(bits_per_word, 8);
-       txfifo_ctrl = SIRFSOC_SPI_FIFO_THD(SIRFSOC_SPI_FIFO_SIZE / 2) |
-                                          (sspi->word_width >> 1);
-       rxfifo_ctrl = SIRFSOC_SPI_FIFO_THD(SIRFSOC_SPI_FIFO_SIZE / 2) |
-                                          (sspi->word_width >> 1);
-
-       if (!(spi->mode & SPI_CS_HIGH))
-               regval |= SIRFSOC_SPI_CS_IDLE_STAT;
-       if (!(spi->mode & SPI_LSB_FIRST))
-               regval |= SIRFSOC_SPI_TRAN_MSB;
-       if (spi->mode & SPI_CPOL)
-               regval |= SIRFSOC_SPI_CLK_IDLE_STAT;
-
-       /*
-        * Data should be driven at least 1/2 cycle before the fetch edge
-        * to make sure that data gets stable at the fetch edge.
-        */
-       if (((spi->mode & SPI_CPOL) && (spi->mode & SPI_CPHA)) ||
-           (!(spi->mode & SPI_CPOL) && !(spi->mode & SPI_CPHA)))
-               regval &= ~SIRFSOC_SPI_DRV_POS_EDGE;
-       else
-               regval |= SIRFSOC_SPI_DRV_POS_EDGE;
-
-       writel(SIRFSOC_SPI_FIFO_SC(fifo_size - 2) |
-                       SIRFSOC_SPI_FIFO_LC(fifo_size / 2) |
-                       SIRFSOC_SPI_FIFO_HC(2),
-               sspi->base + SIRFSOC_SPI_TXFIFO_LEVEL_CHK);
-       writel(SIRFSOC_SPI_FIFO_SC(2) |
-                       SIRFSOC_SPI_FIFO_LC(fifo_size / 2) |
-                       SIRFSOC_SPI_FIFO_HC(fifo_size - 2),
-               sspi->base + SIRFSOC_SPI_RXFIFO_LEVEL_CHK);
-       writel(txfifo_ctrl, sspi->base + SIRFSOC_SPI_TXFIFO_CTRL);
-       writel(rxfifo_ctrl, sspi->base + SIRFSOC_SPI_RXFIFO_CTRL);
-
-       if (t && t->tx_buf && !t->rx_buf && (t->len <= SIRFSOC_MAX_CMD_BYTES)) {
-               regval |= (SIRFSOC_SPI_CMD_BYTE_NUM((t->len - 1)) |
-                               SIRFSOC_SPI_CMD_MODE);
-               sspi->tx_by_cmd = true;
-       } else {
-               regval &= ~SIRFSOC_SPI_CMD_MODE;
-               sspi->tx_by_cmd = false;
+       txfifo_ctrl = (((sspi->fifo_size / 2) &
+                       SIRFSOC_SPI_FIFO_THD_MASK(sspi))
+                       << SIRFSOC_SPI_FIFO_THD_OFFSET) |
+                       (sspi->word_width >> 1);
+       rxfifo_ctrl = (((sspi->fifo_size / 2) &
+                       SIRFSOC_SPI_FIFO_THD_MASK(sspi))
+                       << SIRFSOC_SPI_FIFO_THD_OFFSET) |
+                       (sspi->word_width >> 1);
+       writel(txfifo_ctrl, sspi->base + sspi->regs->txfifo_ctrl);
+       writel(rxfifo_ctrl, sspi->base + sspi->regs->rxfifo_ctrl);
+       if (sspi->type == SIRF_USP_SPI_P2 ||
+               sspi->type == SIRF_USP_SPI_A7) {
+               tx_frm_ctl = 0;
+               tx_frm_ctl |= ((bits_per_word - 1) & SIRFSOC_USP_TX_DATA_MASK)
+                               << SIRFSOC_USP_TX_DATA_OFFSET;
+               tx_frm_ctl |= ((bits_per_word + 1 + SIRFSOC_USP_TXD_DELAY_LEN
+                               - 1) & SIRFSOC_USP_TX_SYNC_MASK) <<
+                               SIRFSOC_USP_TX_SYNC_OFFSET;
+               tx_frm_ctl |= ((bits_per_word + 1 + SIRFSOC_USP_TXD_DELAY_LEN
+                               + 2 - 1) & SIRFSOC_USP_TX_FRAME_MASK) <<
+                               SIRFSOC_USP_TX_FRAME_OFFSET;
+               tx_frm_ctl |= ((bits_per_word - 1) &
+                               SIRFSOC_USP_TX_SHIFTER_MASK) <<
+                               SIRFSOC_USP_TX_SHIFTER_OFFSET;
+               rx_frm_ctl = 0;
+               rx_frm_ctl |= ((bits_per_word - 1) & SIRFSOC_USP_RX_DATA_MASK)
+                               << SIRFSOC_USP_RX_DATA_OFFSET;
+               rx_frm_ctl |= ((bits_per_word + 1 + SIRFSOC_USP_RXD_DELAY_LEN
+                               + 2 - 1) & SIRFSOC_USP_RX_FRAME_MASK) <<
+                               SIRFSOC_USP_RX_FRAME_OFFSET;
+               rx_frm_ctl |= ((bits_per_word - 1)
+                               & SIRFSOC_USP_RX_SHIFTER_MASK) <<
+                               SIRFSOC_USP_RX_SHIFTER_OFFSET;
+               writel(tx_frm_ctl | (((usp_mode2 >> 10) &
+                       SIRFSOC_USP_CLK_10_11_MASK) <<
+                       SIRFSOC_USP_CLK_10_11_OFFSET),
+                       sspi->base + sspi->regs->usp_tx_frame_ctrl);
+               writel(rx_frm_ctl | (((usp_mode2 >> 12) &
+                       SIRFSOC_USP_CLK_12_15_MASK) <<
+                       SIRFSOC_USP_CLK_12_15_OFFSET),
+                       sspi->base + sspi->regs->usp_rx_frame_ctrl);
+               writel(readl(sspi->base + sspi->regs->usp_mode2) |
+                       ((usp_mode2 & SIRFSOC_USP_CLK_DIVISOR_MASK) <<
+                       SIRFSOC_USP_CLK_DIVISOR_OFFSET) |
+                       (SIRFSOC_USP_RXD_DELAY_LEN <<
+                        SIRFSOC_USP_RXD_DELAY_OFFSET) |
+                       (SIRFSOC_USP_TXD_DELAY_LEN <<
+                        SIRFSOC_USP_TXD_DELAY_OFFSET),
+                       sspi->base + sspi->regs->usp_mode2);
+       }
+       if (sspi->type == SIRF_REAL_SPI)
+               writel(regval, sspi->base + sspi->regs->spi_ctrl);
+       spi_sirfsoc_config_mode(spi);
+       if (sspi->type == SIRF_REAL_SPI) {
+               if (t && t->tx_buf && !t->rx_buf &&
+                       (t->len <= SIRFSOC_MAX_CMD_BYTES)) {
+                       sspi->tx_by_cmd = true;
+                       writel(readl(sspi->base + sspi->regs->spi_ctrl) |
+                               (SIRFSOC_SPI_CMD_BYTE_NUM((t->len - 1)) |
+                               SIRFSOC_SPI_CMD_MODE),
+                               sspi->base + sspi->regs->spi_ctrl);
+               } else {
+                       sspi->tx_by_cmd = false;
+                       writel(readl(sspi->base + sspi->regs->spi_ctrl) &
+                               ~SIRFSOC_SPI_CMD_MODE,
+                               sspi->base + sspi->regs->spi_ctrl);
+               }
        }
-       /*
-        * it should never set to hardware cs mode because in hardware cs mode,
-        * cs signal can't controlled by driver.
-        */
-       regval |= SIRFSOC_SPI_CS_IO_MODE;
-       writel(regval, sspi->base + SIRFSOC_SPI_CTRL);
-
        if (IS_DMA_VALID(t)) {
                /* Enable DMA mode for RX, TX */
-               writel(0, sspi->base + SIRFSOC_SPI_TX_DMA_IO_CTRL);
+               writel(0, sspi->base + sspi->regs->tx_dma_io_ctrl);
                writel(SIRFSOC_SPI_RX_DMA_FLUSH,
-                       sspi->base + SIRFSOC_SPI_RX_DMA_IO_CTRL);
+                       sspi->base + sspi->regs->rx_dma_io_ctrl);
        } else {
                /* Enable IO mode for RX, TX */
                writel(SIRFSOC_SPI_IO_MODE_SEL,
-                       sspi->base + SIRFSOC_SPI_TX_DMA_IO_CTRL);
+                       sspi->base + sspi->regs->tx_dma_io_ctrl);
                writel(SIRFSOC_SPI_IO_MODE_SEL,
-                       sspi->base + SIRFSOC_SPI_RX_DMA_IO_CTRL);
+                       sspi->base + sspi->regs->rx_dma_io_ctrl);
        }
-
        return 0;
 }
 
 static int spi_sirfsoc_setup(struct spi_device *spi)
 {
        struct sirfsoc_spi *sspi;
+       int ret = 0;
 
        sspi = spi_master_get_devdata(spi->master);
-
        if (spi->cs_gpio == -ENOENT)
                sspi->hw_cs = true;
-       else
+       else {
                sspi->hw_cs = false;
-       return spi_sirfsoc_setup_transfer(spi, NULL);
+               if (!spi_get_ctldata(spi)) {
+                       void *cs = kmalloc(sizeof(int), GFP_KERNEL);
+                       if (!cs) {
+                               ret = -ENOMEM;
+                               goto exit;
+                       }
+                       ret = gpio_is_valid(spi->cs_gpio);
+                       if (!ret) {
+                               dev_err(&spi->dev, "no valid gpio\n");
+                               ret = -ENOENT;
+                               goto exit;
+                       }
+                       ret = gpio_request(spi->cs_gpio, DRIVER_NAME);
+                       if (ret) {
+                               dev_err(&spi->dev, "failed to request gpio\n");
+                               goto exit;
+                       }
+                       spi_set_ctldata(spi, cs);
+               }
+       }
+       spi_sirfsoc_config_mode(spi);
+       spi_sirfsoc_chipselect(spi, BITBANG_CS_INACTIVE);
+exit:
+       return ret;
+}
+
+static void spi_sirfsoc_cleanup(struct spi_device *spi)
+{
+       if (spi_get_ctldata(spi)) {
+               gpio_free(spi->cs_gpio);
+               kfree(spi_get_ctldata(spi));
+       }
 }
 
+static const struct sirf_spi_comp_data sirf_real_spi = {
+       .regs = &real_spi_register,
+       .type = SIRF_REAL_SPI,
+       .dat_max_frm_len = 64 * 1024,
+       .fifo_size = 256,
+};
+
+static const struct sirf_spi_comp_data sirf_usp_spi_p2 = {
+       .regs = &usp_spi_register,
+       .type = SIRF_USP_SPI_P2,
+       .dat_max_frm_len = 1024 * 1024,
+       .fifo_size = 128,
+       .hwinit = sirfsoc_usp_hwinit,
+};
+
+static const struct sirf_spi_comp_data sirf_usp_spi_a7 = {
+       .regs = &usp_spi_register,
+       .type = SIRF_USP_SPI_A7,
+       .dat_max_frm_len = 1024 * 1024,
+       .fifo_size = 512,
+       .hwinit = sirfsoc_usp_hwinit,
+};
+
+static const struct of_device_id spi_sirfsoc_of_match[] = {
+       { .compatible = "sirf,prima2-spi", .data = &sirf_real_spi},
+       { .compatible = "sirf,prima2-usp-spi", .data = &sirf_usp_spi_p2},
+       { .compatible = "sirf,atlas7-usp-spi", .data = &sirf_usp_spi_a7},
+       {}
+};
+MODULE_DEVICE_TABLE(of, spi_sirfsoc_of_match);
+
 static int spi_sirfsoc_probe(struct platform_device *pdev)
 {
        struct sirfsoc_spi *sspi;
        struct spi_master *master;
        struct resource *mem_res;
+       struct sirf_spi_comp_data *spi_comp_data;
        int irq;
-       int i, ret;
+       int ret;
+       const struct of_device_id *match;
 
        ret = device_reset(&pdev->dev);
        if (ret) {
@@ -659,16 +1088,22 @@ static int spi_sirfsoc_probe(struct platform_device *pdev)
                dev_err(&pdev->dev, "Unable to allocate SPI master\n");
                return -ENOMEM;
        }
+       match = of_match_node(spi_sirfsoc_of_match, pdev->dev.of_node);
        platform_set_drvdata(pdev, master);
        sspi = spi_master_get_devdata(master);
-
+       sspi->fifo_full_offset = ilog2(sspi->fifo_size);
+       spi_comp_data = (struct sirf_spi_comp_data *)match->data;
+       sspi->regs = spi_comp_data->regs;
+       sspi->type = spi_comp_data->type;
+       sspi->fifo_level_chk_mask = (sspi->fifo_size / 4) - 1;
+       sspi->dat_max_frm_len = spi_comp_data->dat_max_frm_len;
+       sspi->fifo_size = spi_comp_data->fifo_size;
        mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        sspi->base = devm_ioremap_resource(&pdev->dev, mem_res);
        if (IS_ERR(sspi->base)) {
                ret = PTR_ERR(sspi->base);
                goto free_master;
        }
-
        irq = platform_get_irq(pdev, 0);
        if (irq < 0) {
                ret = -ENXIO;
@@ -684,11 +1119,13 @@ static int spi_sirfsoc_probe(struct platform_device *pdev)
        sspi->bitbang.setup_transfer = spi_sirfsoc_setup_transfer;
        sspi->bitbang.txrx_bufs = spi_sirfsoc_transfer;
        sspi->bitbang.master->setup = spi_sirfsoc_setup;
+       sspi->bitbang.master->cleanup = spi_sirfsoc_cleanup;
        master->bus_num = pdev->id;
        master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST | SPI_CS_HIGH;
        master->bits_per_word_mask = SPI_BPW_MASK(8) | SPI_BPW_MASK(12) |
                                        SPI_BPW_MASK(16) | SPI_BPW_MASK(32);
        master->max_speed_hz = SIRFSOC_SPI_DEFAULT_FRQ;
+       master->flags = SPI_MASTER_MUST_RX | SPI_MASTER_MUST_TX;
        sspi->bitbang.master->dev.of_node = pdev->dev.of_node;
 
        /* request DMA channels */
@@ -711,47 +1148,19 @@ static int spi_sirfsoc_probe(struct platform_device *pdev)
                goto free_tx_dma;
        }
        clk_prepare_enable(sspi->clk);
+       if (spi_comp_data->hwinit)
+               spi_comp_data->hwinit(sspi);
        sspi->ctrl_freq = clk_get_rate(sspi->clk);
 
        init_completion(&sspi->rx_done);
        init_completion(&sspi->tx_done);
 
-       writel(SIRFSOC_SPI_FIFO_RESET, sspi->base + SIRFSOC_SPI_RXFIFO_OP);
-       writel(SIRFSOC_SPI_FIFO_RESET, sspi->base + SIRFSOC_SPI_TXFIFO_OP);
-       writel(SIRFSOC_SPI_FIFO_START, sspi->base + SIRFSOC_SPI_RXFIFO_OP);
-       writel(SIRFSOC_SPI_FIFO_START, sspi->base + SIRFSOC_SPI_TXFIFO_OP);
-       /* We are not using dummy delay between command and data */
-       writel(0, sspi->base + SIRFSOC_SPI_DUMMY_DELAY_CTL);
-
-       sspi->dummypage = kmalloc(2 * PAGE_SIZE, GFP_KERNEL);
-       if (!sspi->dummypage) {
-               ret = -ENOMEM;
-               goto free_clk;
-       }
-
        ret = spi_bitbang_start(&sspi->bitbang);
        if (ret)
-               goto free_dummypage;
-       for (i = 0; master->cs_gpios && i < master->num_chipselect; i++) {
-               if (master->cs_gpios[i] == -ENOENT)
-                       continue;
-               if (!gpio_is_valid(master->cs_gpios[i])) {
-                       dev_err(&pdev->dev, "no valid gpio\n");
-                       ret = -EINVAL;
-                       goto free_dummypage;
-               }
-               ret = devm_gpio_request(&pdev->dev,
-                               master->cs_gpios[i], DRIVER_NAME);
-               if (ret) {
-                       dev_err(&pdev->dev, "failed to request gpio\n");
-                       goto free_dummypage;
-               }
-       }
+               goto free_clk;
        dev_info(&pdev->dev, "registerred, bus number = %d\n", master->bus_num);
 
        return 0;
-free_dummypage:
-       kfree(sspi->dummypage);
 free_clk:
        clk_disable_unprepare(sspi->clk);
        clk_put(sspi->clk);
@@ -772,9 +1181,7 @@ static int  spi_sirfsoc_remove(struct platform_device *pdev)
 
        master = platform_get_drvdata(pdev);
        sspi = spi_master_get_devdata(master);
-
        spi_bitbang_stop(&sspi->bitbang);
-       kfree(sspi->dummypage);
        clk_disable_unprepare(sspi->clk);
        clk_put(sspi->clk);
        dma_release_channel(sspi->rx_chan);
@@ -804,24 +1211,17 @@ static int spi_sirfsoc_resume(struct device *dev)
        struct sirfsoc_spi *sspi = spi_master_get_devdata(master);
 
        clk_enable(sspi->clk);
-       writel(SIRFSOC_SPI_FIFO_RESET, sspi->base + SIRFSOC_SPI_RXFIFO_OP);
-       writel(SIRFSOC_SPI_FIFO_RESET, sspi->base + SIRFSOC_SPI_TXFIFO_OP);
-       writel(SIRFSOC_SPI_FIFO_START, sspi->base + SIRFSOC_SPI_RXFIFO_OP);
-       writel(SIRFSOC_SPI_FIFO_START, sspi->base + SIRFSOC_SPI_TXFIFO_OP);
-
-       return spi_master_resume(master);
+       writel(SIRFSOC_SPI_FIFO_RESET, sspi->base + sspi->regs->txfifo_op);
+       writel(SIRFSOC_SPI_FIFO_RESET, sspi->base + sspi->regs->rxfifo_op);
+       writel(SIRFSOC_SPI_FIFO_START, sspi->base + sspi->regs->txfifo_op);
+       writel(SIRFSOC_SPI_FIFO_START, sspi->base + sspi->regs->rxfifo_op);
+       return 0;
 }
 #endif
 
 static SIMPLE_DEV_PM_OPS(spi_sirfsoc_pm_ops, spi_sirfsoc_suspend,
                         spi_sirfsoc_resume);
 
-static const struct of_device_id spi_sirfsoc_of_match[] = {
-       { .compatible = "sirf,prima2-spi", },
-       {}
-};
-MODULE_DEVICE_TABLE(of, spi_sirfsoc_of_match);
-
 static struct platform_driver spi_sirfsoc_driver = {
        .driver = {
                .name = DRIVER_NAME,
@@ -835,4 +1235,5 @@ module_platform_driver(spi_sirfsoc_driver);
 MODULE_DESCRIPTION("SiRF SoC SPI master driver");
 MODULE_AUTHOR("Zhiwu Song <Zhiwu.Song@csr.com>");
 MODULE_AUTHOR("Barry Song <Baohua.Song@csr.com>");
+MODULE_AUTHOR("Qipan Li <Qipan.Li@csr.com>");
 MODULE_LICENSE("GPL v2");
diff --git a/drivers/spi/spi-zynqmp-gqspi.c b/drivers/spi/spi-zynqmp-gqspi.c
new file mode 100644 (file)
index 0000000..87b20a5
--- /dev/null
@@ -0,0 +1,1122 @@
+/*
+ * Xilinx Zynq UltraScale+ MPSoC Quad-SPI (QSPI) controller driver
+ * (master mode only)
+ *
+ * Copyright (C) 2009 - 2015 Xilinx, Inc.
+ *
+ * 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; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_irq.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <linux/spinlock.h>
+#include <linux/workqueue.h>
+
+/* Generic QSPI register offsets */
+#define GQSPI_CONFIG_OFST              0x00000100
+#define GQSPI_ISR_OFST                 0x00000104
+#define GQSPI_IDR_OFST                 0x0000010C
+#define GQSPI_IER_OFST                 0x00000108
+#define GQSPI_IMASK_OFST               0x00000110
+#define GQSPI_EN_OFST                  0x00000114
+#define GQSPI_TXD_OFST                 0x0000011C
+#define GQSPI_RXD_OFST                 0x00000120
+#define GQSPI_TX_THRESHOLD_OFST                0x00000128
+#define GQSPI_RX_THRESHOLD_OFST                0x0000012C
+#define GQSPI_LPBK_DLY_ADJ_OFST                0x00000138
+#define GQSPI_GEN_FIFO_OFST            0x00000140
+#define GQSPI_SEL_OFST                 0x00000144
+#define GQSPI_GF_THRESHOLD_OFST                0x00000150
+#define GQSPI_FIFO_CTRL_OFST           0x0000014C
+#define GQSPI_QSPIDMA_DST_CTRL_OFST    0x0000080C
+#define GQSPI_QSPIDMA_DST_SIZE_OFST    0x00000804
+#define GQSPI_QSPIDMA_DST_STS_OFST     0x00000808
+#define GQSPI_QSPIDMA_DST_I_STS_OFST   0x00000814
+#define GQSPI_QSPIDMA_DST_I_EN_OFST    0x00000818
+#define GQSPI_QSPIDMA_DST_I_DIS_OFST   0x0000081C
+#define GQSPI_QSPIDMA_DST_I_MASK_OFST  0x00000820
+#define GQSPI_QSPIDMA_DST_ADDR_OFST    0x00000800
+#define GQSPI_QSPIDMA_DST_ADDR_MSB_OFST 0x00000828
+
+/* GQSPI register bit masks */
+#define GQSPI_SEL_MASK                         0x00000001
+#define GQSPI_EN_MASK                          0x00000001
+#define GQSPI_LPBK_DLY_ADJ_USE_LPBK_MASK       0x00000020
+#define GQSPI_ISR_WR_TO_CLR_MASK               0x00000002
+#define GQSPI_IDR_ALL_MASK                     0x00000FBE
+#define GQSPI_CFG_MODE_EN_MASK                 0xC0000000
+#define GQSPI_CFG_GEN_FIFO_START_MODE_MASK     0x20000000
+#define GQSPI_CFG_ENDIAN_MASK                  0x04000000
+#define GQSPI_CFG_EN_POLL_TO_MASK              0x00100000
+#define GQSPI_CFG_WP_HOLD_MASK                 0x00080000
+#define GQSPI_CFG_BAUD_RATE_DIV_MASK           0x00000038
+#define GQSPI_CFG_CLK_PHA_MASK                 0x00000004
+#define GQSPI_CFG_CLK_POL_MASK                 0x00000002
+#define GQSPI_CFG_START_GEN_FIFO_MASK          0x10000000
+#define GQSPI_GENFIFO_IMM_DATA_MASK            0x000000FF
+#define GQSPI_GENFIFO_DATA_XFER                        0x00000100
+#define GQSPI_GENFIFO_EXP                      0x00000200
+#define GQSPI_GENFIFO_MODE_SPI                 0x00000400
+#define GQSPI_GENFIFO_MODE_DUALSPI             0x00000800
+#define GQSPI_GENFIFO_MODE_QUADSPI             0x00000C00
+#define GQSPI_GENFIFO_MODE_MASK                        0x00000C00
+#define GQSPI_GENFIFO_CS_LOWER                 0x00001000
+#define GQSPI_GENFIFO_CS_UPPER                 0x00002000
+#define GQSPI_GENFIFO_BUS_LOWER                        0x00004000
+#define GQSPI_GENFIFO_BUS_UPPER                        0x00008000
+#define GQSPI_GENFIFO_BUS_BOTH                 0x0000C000
+#define GQSPI_GENFIFO_BUS_MASK                 0x0000C000
+#define GQSPI_GENFIFO_TX                       0x00010000
+#define GQSPI_GENFIFO_RX                       0x00020000
+#define GQSPI_GENFIFO_STRIPE                   0x00040000
+#define GQSPI_GENFIFO_POLL                     0x00080000
+#define GQSPI_GENFIFO_EXP_START                        0x00000100
+#define GQSPI_FIFO_CTRL_RST_RX_FIFO_MASK       0x00000004
+#define GQSPI_FIFO_CTRL_RST_TX_FIFO_MASK       0x00000002
+#define GQSPI_FIFO_CTRL_RST_GEN_FIFO_MASK      0x00000001
+#define GQSPI_ISR_RXEMPTY_MASK                 0x00000800
+#define GQSPI_ISR_GENFIFOFULL_MASK             0x00000400
+#define GQSPI_ISR_GENFIFONOT_FULL_MASK         0x00000200
+#define GQSPI_ISR_TXEMPTY_MASK                 0x00000100
+#define GQSPI_ISR_GENFIFOEMPTY_MASK            0x00000080
+#define GQSPI_ISR_RXFULL_MASK                  0x00000020
+#define GQSPI_ISR_RXNEMPTY_MASK                        0x00000010
+#define GQSPI_ISR_TXFULL_MASK                  0x00000008
+#define GQSPI_ISR_TXNOT_FULL_MASK              0x00000004
+#define GQSPI_ISR_POLL_TIME_EXPIRE_MASK                0x00000002
+#define GQSPI_IER_TXNOT_FULL_MASK              0x00000004
+#define GQSPI_IER_RXEMPTY_MASK                 0x00000800
+#define GQSPI_IER_POLL_TIME_EXPIRE_MASK                0x00000002
+#define GQSPI_IER_RXNEMPTY_MASK                        0x00000010
+#define GQSPI_IER_GENFIFOEMPTY_MASK            0x00000080
+#define GQSPI_IER_TXEMPTY_MASK                 0x00000100
+#define GQSPI_QSPIDMA_DST_INTR_ALL_MASK                0x000000FE
+#define GQSPI_QSPIDMA_DST_STS_WTC              0x0000E000
+#define GQSPI_CFG_MODE_EN_DMA_MASK             0x80000000
+#define GQSPI_ISR_IDR_MASK                     0x00000994
+#define GQSPI_QSPIDMA_DST_I_EN_DONE_MASK       0x00000002
+#define GQSPI_QSPIDMA_DST_I_STS_DONE_MASK      0x00000002
+#define GQSPI_IRQ_MASK                         0x00000980
+
+#define GQSPI_CFG_BAUD_RATE_DIV_SHIFT          3
+#define GQSPI_GENFIFO_CS_SETUP                 0x4
+#define GQSPI_GENFIFO_CS_HOLD                  0x3
+#define GQSPI_TXD_DEPTH                                64
+#define GQSPI_RX_FIFO_THRESHOLD                        32
+#define GQSPI_RX_FIFO_FILL     (GQSPI_RX_FIFO_THRESHOLD * 4)
+#define GQSPI_TX_FIFO_THRESHOLD_RESET_VAL      32
+#define GQSPI_TX_FIFO_FILL     (GQSPI_TXD_DEPTH -\
+                               GQSPI_TX_FIFO_THRESHOLD_RESET_VAL)
+#define GQSPI_GEN_FIFO_THRESHOLD_RESET_VAL     0X10
+#define GQSPI_QSPIDMA_DST_CTRL_RESET_VAL       0x803FFA00
+#define GQSPI_SELECT_FLASH_CS_LOWER            0x1
+#define GQSPI_SELECT_FLASH_CS_UPPER            0x2
+#define GQSPI_SELECT_FLASH_CS_BOTH             0x3
+#define GQSPI_SELECT_FLASH_BUS_LOWER           0x1
+#define GQSPI_SELECT_FLASH_BUS_UPPER           0x2
+#define GQSPI_SELECT_FLASH_BUS_BOTH            0x3
+#define GQSPI_BAUD_DIV_MAX     7       /* Baud rate divisor maximum */
+#define GQSPI_BAUD_DIV_SHIFT   2       /* Baud rate divisor shift */
+#define GQSPI_SELECT_MODE_SPI          0x1
+#define GQSPI_SELECT_MODE_DUALSPI      0x2
+#define GQSPI_SELECT_MODE_QUADSPI      0x4
+#define GQSPI_DMA_UNALIGN              0x3
+#define GQSPI_DEFAULT_NUM_CS   1       /* Default number of chip selects */
+
+enum mode_type {GQSPI_MODE_IO, GQSPI_MODE_DMA};
+
+/**
+ * struct zynqmp_qspi - Defines qspi driver instance
+ * @regs:              Virtual address of the QSPI controller registers
+ * @refclk:            Pointer to the peripheral clock
+ * @pclk:              Pointer to the APB clock
+ * @irq:               IRQ number
+ * @dev:               Pointer to struct device
+ * @txbuf:             Pointer to the TX buffer
+ * @rxbuf:             Pointer to the RX buffer
+ * @bytes_to_transfer: Number of bytes left to transfer
+ * @bytes_to_receive:  Number of bytes left to receive
+ * @genfifocs:         Used for chip select
+ * @genfifobus:                Used to select the upper or lower bus
+ * @dma_rx_bytes:      Remaining bytes to receive by DMA mode
+ * @dma_addr:          DMA address after mapping the kernel buffer
+ * @genfifoentry:      Used for storing the genfifoentry instruction.
+ * @mode:              Defines the mode in which QSPI is operating
+ */
+struct zynqmp_qspi {
+       void __iomem *regs;
+       struct clk *refclk;
+       struct clk *pclk;
+       int irq;
+       struct device *dev;
+       const void *txbuf;
+       void *rxbuf;
+       int bytes_to_transfer;
+       int bytes_to_receive;
+       u32 genfifocs;
+       u32 genfifobus;
+       u32 dma_rx_bytes;
+       dma_addr_t dma_addr;
+       u32 genfifoentry;
+       enum mode_type mode;
+};
+
+/**
+ * zynqmp_gqspi_read:  For GQSPI controller read operation
+ * @xqspi:     Pointer to the zynqmp_qspi structure
+ * @offset:    Offset from where to read
+ */
+static u32 zynqmp_gqspi_read(struct zynqmp_qspi *xqspi, u32 offset)
+{
+       return readl_relaxed(xqspi->regs + offset);
+}
+
+/**
+ * zynqmp_gqspi_write: For GQSPI controller write operation
+ * @xqspi:     Pointer to the zynqmp_qspi structure
+ * @offset:    Offset where to write
+ * @val:       Value to be written
+ */
+static inline void zynqmp_gqspi_write(struct zynqmp_qspi *xqspi, u32 offset,
+                                     u32 val)
+{
+       writel_relaxed(val, (xqspi->regs + offset));
+}
+
+/**
+ * zynqmp_gqspi_selectslave:   For selection of slave device
+ * @instanceptr:       Pointer to the zynqmp_qspi structure
+ * @flashcs:   For chip select
+ * @flashbus:  To check which bus is selected- upper or lower
+ */
+static void zynqmp_gqspi_selectslave(struct zynqmp_qspi *instanceptr,
+                                    u8 slavecs, u8 slavebus)
+{
+       /*
+        * Bus and CS lines selected here will be updated in the instance and
+        * used for subsequent GENFIFO entries during transfer.
+        */
+
+       /* Choose slave select line */
+       switch (slavecs) {
+       case GQSPI_SELECT_FLASH_CS_BOTH:
+               instanceptr->genfifocs = GQSPI_GENFIFO_CS_LOWER |
+                       GQSPI_GENFIFO_CS_UPPER;
+       case GQSPI_SELECT_FLASH_CS_UPPER:
+               instanceptr->genfifocs = GQSPI_GENFIFO_CS_UPPER;
+               break;
+       case GQSPI_SELECT_FLASH_CS_LOWER:
+               instanceptr->genfifocs = GQSPI_GENFIFO_CS_LOWER;
+               break;
+       default:
+               dev_warn(instanceptr->dev, "Invalid slave select\n");
+       }
+
+       /* Choose the bus */
+       switch (slavebus) {
+       case GQSPI_SELECT_FLASH_BUS_BOTH:
+               instanceptr->genfifobus = GQSPI_GENFIFO_BUS_LOWER |
+                       GQSPI_GENFIFO_BUS_UPPER;
+               break;
+       case GQSPI_SELECT_FLASH_BUS_UPPER:
+               instanceptr->genfifobus = GQSPI_GENFIFO_BUS_UPPER;
+               break;
+       case GQSPI_SELECT_FLASH_BUS_LOWER:
+               instanceptr->genfifobus = GQSPI_GENFIFO_BUS_LOWER;
+               break;
+       default:
+               dev_warn(instanceptr->dev, "Invalid slave bus\n");
+       }
+}
+
+/**
+ * zynqmp_qspi_init_hw:        Initialize the hardware
+ * @xqspi:     Pointer to the zynqmp_qspi structure
+ *
+ * The default settings of the QSPI controller's configurable parameters on
+ * reset are
+ *     - Master mode
+ *     - TX threshold set to 1
+ *     - RX threshold set to 1
+ *     - Flash memory interface mode enabled
+ * This function performs the following actions
+ *     - Disable and clear all the interrupts
+ *     - Enable manual slave select
+ *     - Enable manual start
+ *     - Deselect all the chip select lines
+ *     - Set the little endian mode of TX FIFO and
+ *     - Enable the QSPI controller
+ */
+static void zynqmp_qspi_init_hw(struct zynqmp_qspi *xqspi)
+{
+       u32 config_reg;
+
+       /* Select the GQSPI mode */
+       zynqmp_gqspi_write(xqspi, GQSPI_SEL_OFST, GQSPI_SEL_MASK);
+       /* Clear and disable interrupts */
+       zynqmp_gqspi_write(xqspi, GQSPI_ISR_OFST,
+                          zynqmp_gqspi_read(xqspi, GQSPI_ISR_OFST) |
+                          GQSPI_ISR_WR_TO_CLR_MASK);
+       /* Clear the DMA STS */
+       zynqmp_gqspi_write(xqspi, GQSPI_QSPIDMA_DST_I_STS_OFST,
+                          zynqmp_gqspi_read(xqspi,
+                                            GQSPI_QSPIDMA_DST_I_STS_OFST));
+       zynqmp_gqspi_write(xqspi, GQSPI_QSPIDMA_DST_STS_OFST,
+                          zynqmp_gqspi_read(xqspi,
+                                            GQSPI_QSPIDMA_DST_STS_OFST) |
+                                            GQSPI_QSPIDMA_DST_STS_WTC);
+       zynqmp_gqspi_write(xqspi, GQSPI_IDR_OFST, GQSPI_IDR_ALL_MASK);
+       zynqmp_gqspi_write(xqspi,
+                          GQSPI_QSPIDMA_DST_I_DIS_OFST,
+                          GQSPI_QSPIDMA_DST_INTR_ALL_MASK);
+       /* Disable the GQSPI */
+       zynqmp_gqspi_write(xqspi, GQSPI_EN_OFST, 0x0);
+       config_reg = zynqmp_gqspi_read(xqspi, GQSPI_CONFIG_OFST);
+       config_reg &= ~GQSPI_CFG_MODE_EN_MASK;
+       /* Manual start */
+       config_reg |= GQSPI_CFG_GEN_FIFO_START_MODE_MASK;
+       /* Little endian by default */
+       config_reg &= ~GQSPI_CFG_ENDIAN_MASK;
+       /* Disable poll time out */
+       config_reg &= ~GQSPI_CFG_EN_POLL_TO_MASK;
+       /* Set hold bit */
+       config_reg |= GQSPI_CFG_WP_HOLD_MASK;
+       /* Clear pre-scalar by default */
+       config_reg &= ~GQSPI_CFG_BAUD_RATE_DIV_MASK;
+       /* CPHA 0 */
+       config_reg &= ~GQSPI_CFG_CLK_PHA_MASK;
+       /* CPOL 0 */
+       config_reg &= ~GQSPI_CFG_CLK_POL_MASK;
+       zynqmp_gqspi_write(xqspi, GQSPI_CONFIG_OFST, config_reg);
+
+       /* Clear the TX and RX FIFO */
+       zynqmp_gqspi_write(xqspi, GQSPI_FIFO_CTRL_OFST,
+                          GQSPI_FIFO_CTRL_RST_RX_FIFO_MASK |
+                          GQSPI_FIFO_CTRL_RST_TX_FIFO_MASK |
+                          GQSPI_FIFO_CTRL_RST_GEN_FIFO_MASK);
+       /* Set by default to allow for high frequencies */
+       zynqmp_gqspi_write(xqspi, GQSPI_LPBK_DLY_ADJ_OFST,
+                          zynqmp_gqspi_read(xqspi, GQSPI_LPBK_DLY_ADJ_OFST) |
+                          GQSPI_LPBK_DLY_ADJ_USE_LPBK_MASK);
+       /* Reset thresholds */
+       zynqmp_gqspi_write(xqspi, GQSPI_TX_THRESHOLD_OFST,
+                          GQSPI_TX_FIFO_THRESHOLD_RESET_VAL);
+       zynqmp_gqspi_write(xqspi, GQSPI_RX_THRESHOLD_OFST,
+                          GQSPI_RX_FIFO_THRESHOLD);
+       zynqmp_gqspi_write(xqspi, GQSPI_GF_THRESHOLD_OFST,
+                          GQSPI_GEN_FIFO_THRESHOLD_RESET_VAL);
+       zynqmp_gqspi_selectslave(xqspi,
+                                GQSPI_SELECT_FLASH_CS_LOWER,
+                                GQSPI_SELECT_FLASH_BUS_LOWER);
+       /* Initialize DMA */
+       zynqmp_gqspi_write(xqspi,
+                       GQSPI_QSPIDMA_DST_CTRL_OFST,
+                       GQSPI_QSPIDMA_DST_CTRL_RESET_VAL);
+
+       /* Enable the GQSPI */
+       zynqmp_gqspi_write(xqspi, GQSPI_EN_OFST, GQSPI_EN_MASK);
+}
+
+/**
+ * zynqmp_qspi_copy_read_data: Copy data to RX buffer
+ * @xqspi:     Pointer to the zynqmp_qspi structure
+ * @data:      The variable where data is stored
+ * @size:      Number of bytes to be copied from data to RX buffer
+ */
+static void zynqmp_qspi_copy_read_data(struct zynqmp_qspi *xqspi,
+                                      ulong data, u8 size)
+{
+       memcpy(xqspi->rxbuf, &data, size);
+       xqspi->rxbuf += size;
+       xqspi->bytes_to_receive -= size;
+}
+
+/**
+ * zynqmp_prepare_transfer_hardware:   Prepares hardware for transfer.
+ * @master:    Pointer to the spi_master structure which provides
+ *             information about the controller.
+ *
+ * This function enables SPI master controller.
+ *
+ * Return:     0 on success; error value otherwise
+ */
+static int zynqmp_prepare_transfer_hardware(struct spi_master *master)
+{
+       struct zynqmp_qspi *xqspi = spi_master_get_devdata(master);
+       int ret;
+
+       ret = clk_enable(xqspi->refclk);
+       if (ret)
+               goto clk_err;
+
+       ret = clk_enable(xqspi->pclk);
+       if (ret)
+               goto clk_err;
+
+       zynqmp_gqspi_write(xqspi, GQSPI_EN_OFST, GQSPI_EN_MASK);
+       return 0;
+clk_err:
+       return ret;
+}
+
+/**
+ * zynqmp_unprepare_transfer_hardware: Relaxes hardware after transfer
+ * @master:    Pointer to the spi_master structure which provides
+ *             information about the controller.
+ *
+ * This function disables the SPI master controller.
+ *
+ * Return:     Always 0
+ */
+static int zynqmp_unprepare_transfer_hardware(struct spi_master *master)
+{
+       struct zynqmp_qspi *xqspi = spi_master_get_devdata(master);
+
+       zynqmp_gqspi_write(xqspi, GQSPI_EN_OFST, 0x0);
+       clk_disable(xqspi->refclk);
+       clk_disable(xqspi->pclk);
+       return 0;
+}
+
+/**
+ * zynqmp_qspi_chipselect:     Select or deselect the chip select line
+ * @qspi:      Pointer to the spi_device structure
+ * @is_high:   Select(0) or deselect (1) the chip select line
+ */
+static void zynqmp_qspi_chipselect(struct spi_device *qspi, bool is_high)
+{
+       struct zynqmp_qspi *xqspi = spi_master_get_devdata(qspi->master);
+       ulong timeout;
+       u32 genfifoentry = 0x0, statusreg;
+
+       genfifoentry |= GQSPI_GENFIFO_MODE_SPI;
+       genfifoentry |= xqspi->genfifobus;
+
+       if (!is_high) {
+               genfifoentry |= xqspi->genfifocs;
+               genfifoentry |= GQSPI_GENFIFO_CS_SETUP;
+       } else {
+               genfifoentry |= GQSPI_GENFIFO_CS_HOLD;
+       }
+
+       zynqmp_gqspi_write(xqspi, GQSPI_GEN_FIFO_OFST, genfifoentry);
+
+       /* Dummy generic FIFO entry */
+       zynqmp_gqspi_write(xqspi, GQSPI_GEN_FIFO_OFST, 0x0);
+
+       /* Manually start the generic FIFO command */
+       zynqmp_gqspi_write(xqspi, GQSPI_CONFIG_OFST,
+                       zynqmp_gqspi_read(xqspi, GQSPI_CONFIG_OFST) |
+                       GQSPI_CFG_START_GEN_FIFO_MASK);
+
+       timeout = jiffies + msecs_to_jiffies(1000);
+
+       /* Wait until the generic FIFO command is empty */
+       do {
+               statusreg = zynqmp_gqspi_read(xqspi, GQSPI_ISR_OFST);
+
+               if ((statusreg & GQSPI_ISR_GENFIFOEMPTY_MASK) &&
+                       (statusreg & GQSPI_ISR_TXEMPTY_MASK))
+                       break;
+               else
+                       cpu_relax();
+       } while (!time_after_eq(jiffies, timeout));
+
+       if (time_after_eq(jiffies, timeout))
+               dev_err(xqspi->dev, "Chip select timed out\n");
+}
+
+/**
+ * zynqmp_qspi_setup_transfer: Configure QSPI controller for specified
+ *                             transfer
+ * @qspi:      Pointer to the spi_device structure
+ * @transfer:  Pointer to the spi_transfer structure which provides
+ *             information about next transfer setup parameters
+ *
+ * Sets the operational mode of QSPI controller for the next QSPI transfer and
+ * sets the requested clock frequency.
+ *
+ * Return:     Always 0
+ *
+ * Note:
+ *     If the requested frequency is not an exact match with what can be
+ *     obtained using the pre-scalar value, the driver sets the clock
+ *     frequency which is lower than the requested frequency (maximum lower)
+ *     for the transfer.
+ *
+ *     If the requested frequency is higher or lower than that is supported
+ *     by the QSPI controller the driver will set the highest or lowest
+ *     frequency supported by controller.
+ */
+static int zynqmp_qspi_setup_transfer(struct spi_device *qspi,
+                                     struct spi_transfer *transfer)
+{
+       struct zynqmp_qspi *xqspi = spi_master_get_devdata(qspi->master);
+       ulong clk_rate;
+       u32 config_reg, req_hz, baud_rate_val = 0;
+
+       if (transfer)
+               req_hz = transfer->speed_hz;
+       else
+               req_hz = qspi->max_speed_hz;
+
+       /* Set the clock frequency */
+       /* If req_hz == 0, default to lowest speed */
+       clk_rate = clk_get_rate(xqspi->refclk);
+
+       while ((baud_rate_val < GQSPI_BAUD_DIV_MAX) &&
+              (clk_rate /
+               (GQSPI_BAUD_DIV_SHIFT << baud_rate_val)) > req_hz)
+               baud_rate_val++;
+
+       config_reg = zynqmp_gqspi_read(xqspi, GQSPI_CONFIG_OFST);
+
+       /* Set the QSPI clock phase and clock polarity */
+       config_reg &= (~GQSPI_CFG_CLK_PHA_MASK) & (~GQSPI_CFG_CLK_POL_MASK);
+
+       if (qspi->mode & SPI_CPHA)
+               config_reg |= GQSPI_CFG_CLK_PHA_MASK;
+       if (qspi->mode & SPI_CPOL)
+               config_reg |= GQSPI_CFG_CLK_POL_MASK;
+
+       config_reg &= ~GQSPI_CFG_BAUD_RATE_DIV_MASK;
+       config_reg |= (baud_rate_val << GQSPI_CFG_BAUD_RATE_DIV_SHIFT);
+       zynqmp_gqspi_write(xqspi, GQSPI_CONFIG_OFST, config_reg);
+       return 0;
+}
+
+/**
+ * zynqmp_qspi_setup:  Configure the QSPI controller
+ * @qspi:      Pointer to the spi_device structure
+ *
+ * Sets the operational mode of QSPI controller for the next QSPI transfer,
+ * baud rate and divisor value to setup the requested qspi clock.
+ *
+ * Return:     0 on success; error value otherwise.
+ */
+static int zynqmp_qspi_setup(struct spi_device *qspi)
+{
+       if (qspi->master->busy)
+               return -EBUSY;
+       return 0;
+}
+
+/**
+ * zynqmp_qspi_filltxfifo:     Fills the TX FIFO as long as there is room in
+ *                             the FIFO or the bytes required to be
+ *                             transmitted.
+ * @xqspi:     Pointer to the zynqmp_qspi structure
+ * @size:      Number of bytes to be copied from TX buffer to TX FIFO
+ */
+static void zynqmp_qspi_filltxfifo(struct zynqmp_qspi *xqspi, int size)
+{
+       u32 count = 0, intermediate;
+
+       while ((xqspi->bytes_to_transfer > 0) && (count < size)) {
+               memcpy(&intermediate, xqspi->txbuf, 4);
+               zynqmp_gqspi_write(xqspi, GQSPI_TXD_OFST, intermediate);
+
+               if (xqspi->bytes_to_transfer >= 4) {
+                       xqspi->txbuf += 4;
+                       xqspi->bytes_to_transfer -= 4;
+               } else {
+                       xqspi->txbuf += xqspi->bytes_to_transfer;
+                       xqspi->bytes_to_transfer = 0;
+               }
+               count++;
+       }
+}
+
+/**
+ * zynqmp_qspi_readrxfifo:     Fills the RX FIFO as long as there is room in
+ *                             the FIFO.
+ * @xqspi:     Pointer to the zynqmp_qspi structure
+ * @size:      Number of bytes to be copied from RX buffer to RX FIFO
+ */
+static void zynqmp_qspi_readrxfifo(struct zynqmp_qspi *xqspi, u32 size)
+{
+       ulong data;
+       int count = 0;
+
+       while ((count < size) && (xqspi->bytes_to_receive > 0)) {
+               if (xqspi->bytes_to_receive >= 4) {
+                       (*(u32 *) xqspi->rxbuf) =
+                       zynqmp_gqspi_read(xqspi, GQSPI_RXD_OFST);
+                       xqspi->rxbuf += 4;
+                       xqspi->bytes_to_receive -= 4;
+                       count += 4;
+               } else {
+                       data = zynqmp_gqspi_read(xqspi, GQSPI_RXD_OFST);
+                       count += xqspi->bytes_to_receive;
+                       zynqmp_qspi_copy_read_data(xqspi, data,
+                                                  xqspi->bytes_to_receive);
+                       xqspi->bytes_to_receive = 0;
+               }
+       }
+}
+
+/**
+ * zynqmp_process_dma_irq:     Handler for DMA done interrupt of QSPI
+ *                             controller
+ * @xqspi:     zynqmp_qspi instance pointer
+ *
+ * This function handles DMA interrupt only.
+ */
+static void zynqmp_process_dma_irq(struct zynqmp_qspi *xqspi)
+{
+       u32 config_reg, genfifoentry;
+
+       dma_unmap_single(xqspi->dev, xqspi->dma_addr,
+                               xqspi->dma_rx_bytes, DMA_FROM_DEVICE);
+       xqspi->rxbuf += xqspi->dma_rx_bytes;
+       xqspi->bytes_to_receive -= xqspi->dma_rx_bytes;
+       xqspi->dma_rx_bytes = 0;
+
+       /* Disabling the DMA interrupts */
+       zynqmp_gqspi_write(xqspi, GQSPI_QSPIDMA_DST_I_DIS_OFST,
+                                       GQSPI_QSPIDMA_DST_I_EN_DONE_MASK);
+
+       if (xqspi->bytes_to_receive > 0) {
+               /* Switch to IO mode,for remaining bytes to receive */
+               config_reg = zynqmp_gqspi_read(xqspi, GQSPI_CONFIG_OFST);
+               config_reg &= ~GQSPI_CFG_MODE_EN_MASK;
+               zynqmp_gqspi_write(xqspi, GQSPI_CONFIG_OFST, config_reg);
+
+               /* Initiate the transfer of remaining bytes */
+               genfifoentry = xqspi->genfifoentry;
+               genfifoentry |= xqspi->bytes_to_receive;
+               zynqmp_gqspi_write(xqspi, GQSPI_GEN_FIFO_OFST, genfifoentry);
+
+               /* Dummy generic FIFO entry */
+               zynqmp_gqspi_write(xqspi, GQSPI_GEN_FIFO_OFST, 0x0);
+
+               /* Manual start */
+               zynqmp_gqspi_write(xqspi, GQSPI_CONFIG_OFST,
+                       (zynqmp_gqspi_read(xqspi, GQSPI_CONFIG_OFST) |
+                       GQSPI_CFG_START_GEN_FIFO_MASK));
+
+               /* Enable the RX interrupts for IO mode */
+               zynqmp_gqspi_write(xqspi, GQSPI_IER_OFST,
+                               GQSPI_IER_GENFIFOEMPTY_MASK |
+                               GQSPI_IER_RXNEMPTY_MASK |
+                               GQSPI_IER_RXEMPTY_MASK);
+       }
+}
+
+/**
+ * zynqmp_qspi_irq:    Interrupt service routine of the QSPI controller
+ * @irq:       IRQ number
+ * @dev_id:    Pointer to the xqspi structure
+ *
+ * This function handles TX empty only.
+ * On TX empty interrupt this function reads the received data from RX FIFO
+ * and fills the TX FIFO if there is any data remaining to be transferred.
+ *
+ * Return:     IRQ_HANDLED when interrupt is handled
+ *             IRQ_NONE otherwise.
+ */
+static irqreturn_t zynqmp_qspi_irq(int irq, void *dev_id)
+{
+       struct spi_master *master = dev_id;
+       struct zynqmp_qspi *xqspi = spi_master_get_devdata(master);
+       int ret = IRQ_NONE;
+       u32 status, mask, dma_status = 0;
+
+       status = zynqmp_gqspi_read(xqspi, GQSPI_ISR_OFST);
+       zynqmp_gqspi_write(xqspi, GQSPI_ISR_OFST, status);
+       mask = (status & ~(zynqmp_gqspi_read(xqspi, GQSPI_IMASK_OFST)));
+
+       /* Read and clear DMA status */
+       if (xqspi->mode == GQSPI_MODE_DMA) {
+               dma_status =
+                       zynqmp_gqspi_read(xqspi, GQSPI_QSPIDMA_DST_I_STS_OFST);
+               zynqmp_gqspi_write(xqspi, GQSPI_QSPIDMA_DST_I_STS_OFST,
+                                                               dma_status);
+       }
+
+       if (mask & GQSPI_ISR_TXNOT_FULL_MASK) {
+               zynqmp_qspi_filltxfifo(xqspi, GQSPI_TX_FIFO_FILL);
+               ret = IRQ_HANDLED;
+       }
+
+       if (dma_status & GQSPI_QSPIDMA_DST_I_STS_DONE_MASK) {
+               zynqmp_process_dma_irq(xqspi);
+               ret = IRQ_HANDLED;
+       } else if (!(mask & GQSPI_IER_RXEMPTY_MASK) &&
+                       (mask & GQSPI_IER_GENFIFOEMPTY_MASK)) {
+               zynqmp_qspi_readrxfifo(xqspi, GQSPI_RX_FIFO_FILL);
+               ret = IRQ_HANDLED;
+       }
+
+       if ((xqspi->bytes_to_receive == 0) && (xqspi->bytes_to_transfer == 0)
+                       && ((status & GQSPI_IRQ_MASK) == GQSPI_IRQ_MASK)) {
+               zynqmp_gqspi_write(xqspi, GQSPI_IDR_OFST, GQSPI_ISR_IDR_MASK);
+               spi_finalize_current_transfer(master);
+               ret = IRQ_HANDLED;
+       }
+       return ret;
+}
+
+/**
+ * zynqmp_qspi_selectspimode:  Selects SPI mode - x1 or x2 or x4.
+ * @xqspi:     xqspi is a pointer to the GQSPI instance
+ * @spimode:   spimode - SPI or DUAL or QUAD.
+ * Return:     Mask to set desired SPI mode in GENFIFO entry.
+ */
+static inline u32 zynqmp_qspi_selectspimode(struct zynqmp_qspi *xqspi,
+                                               u8 spimode)
+{
+       u32 mask = 0;
+
+       switch (spimode) {
+       case GQSPI_SELECT_MODE_DUALSPI:
+               mask = GQSPI_GENFIFO_MODE_DUALSPI;
+               break;
+       case GQSPI_SELECT_MODE_QUADSPI:
+               mask = GQSPI_GENFIFO_MODE_QUADSPI;
+               break;
+       case GQSPI_SELECT_MODE_SPI:
+               mask = GQSPI_GENFIFO_MODE_SPI;
+               break;
+       default:
+               dev_warn(xqspi->dev, "Invalid SPI mode\n");
+       }
+
+       return mask;
+}
+
+/**
+ * zynq_qspi_setuprxdma:       This function sets up the RX DMA operation
+ * @xqspi:     xqspi is a pointer to the GQSPI instance.
+ */
+static void zynq_qspi_setuprxdma(struct zynqmp_qspi *xqspi)
+{
+       u32 rx_bytes, rx_rem, config_reg;
+       dma_addr_t addr;
+       u64 dma_align =  (u64)(uintptr_t)xqspi->rxbuf;
+
+       if ((xqspi->bytes_to_receive < 8) ||
+               ((dma_align & GQSPI_DMA_UNALIGN) != 0x0)) {
+               /* Setting to IO mode */
+               config_reg = zynqmp_gqspi_read(xqspi, GQSPI_CONFIG_OFST);
+               config_reg &= ~GQSPI_CFG_MODE_EN_MASK;
+               zynqmp_gqspi_write(xqspi, GQSPI_CONFIG_OFST, config_reg);
+               xqspi->mode = GQSPI_MODE_IO;
+               xqspi->dma_rx_bytes = 0;
+               return;
+       }
+
+       rx_rem = xqspi->bytes_to_receive % 4;
+       rx_bytes = (xqspi->bytes_to_receive - rx_rem);
+
+       addr = dma_map_single(xqspi->dev, (void *)xqspi->rxbuf,
+                                               rx_bytes, DMA_FROM_DEVICE);
+       if (dma_mapping_error(xqspi->dev, addr))
+               dev_err(xqspi->dev, "ERR:rxdma:memory not mapped\n");
+
+       xqspi->dma_rx_bytes = rx_bytes;
+       xqspi->dma_addr = addr;
+       zynqmp_gqspi_write(xqspi, GQSPI_QSPIDMA_DST_ADDR_OFST,
+                               (u32)(addr & 0xffffffff));
+       addr = ((addr >> 16) >> 16);
+       zynqmp_gqspi_write(xqspi, GQSPI_QSPIDMA_DST_ADDR_MSB_OFST,
+                               ((u32)addr) & 0xfff);
+
+       /* Enabling the DMA mode */
+       config_reg = zynqmp_gqspi_read(xqspi, GQSPI_CONFIG_OFST);
+       config_reg &= ~GQSPI_CFG_MODE_EN_MASK;
+       config_reg |= GQSPI_CFG_MODE_EN_DMA_MASK;
+       zynqmp_gqspi_write(xqspi, GQSPI_CONFIG_OFST, config_reg);
+
+       /* Switch to DMA mode */
+       xqspi->mode = GQSPI_MODE_DMA;
+
+       /* Write the number of bytes to transfer */
+       zynqmp_gqspi_write(xqspi, GQSPI_QSPIDMA_DST_SIZE_OFST, rx_bytes);
+}
+
+/**
+ * zynqmp_qspi_txrxsetup:      This function checks the TX/RX buffers in
+ *                             the transfer and sets up the GENFIFO entries,
+ *                             TX FIFO as required.
+ * @xqspi:     xqspi is a pointer to the GQSPI instance.
+ * @transfer:  It is a pointer to the structure containing transfer data.
+ * @genfifoentry:      genfifoentry is pointer to the variable in which
+ *                     GENFIFO mask is returned to calling function
+ */
+static void zynqmp_qspi_txrxsetup(struct zynqmp_qspi *xqspi,
+                                 struct spi_transfer *transfer,
+                                 u32 *genfifoentry)
+{
+       u32 config_reg;
+
+       /* Transmit */
+       if ((xqspi->txbuf != NULL) && (xqspi->rxbuf == NULL)) {
+               /* Setup data to be TXed */
+               *genfifoentry &= ~GQSPI_GENFIFO_RX;
+               *genfifoentry |= GQSPI_GENFIFO_DATA_XFER;
+               *genfifoentry |= GQSPI_GENFIFO_TX;
+               *genfifoentry |=
+                       zynqmp_qspi_selectspimode(xqspi, transfer->tx_nbits);
+               xqspi->bytes_to_transfer = transfer->len;
+               if (xqspi->mode == GQSPI_MODE_DMA) {
+                       config_reg = zynqmp_gqspi_read(xqspi,
+                                                       GQSPI_CONFIG_OFST);
+                       config_reg &= ~GQSPI_CFG_MODE_EN_MASK;
+                       zynqmp_gqspi_write(xqspi, GQSPI_CONFIG_OFST,
+                                                               config_reg);
+                       xqspi->mode = GQSPI_MODE_IO;
+               }
+               zynqmp_qspi_filltxfifo(xqspi, GQSPI_TXD_DEPTH);
+               /* Discard RX data */
+               xqspi->bytes_to_receive = 0;
+       } else if ((xqspi->txbuf == NULL) && (xqspi->rxbuf != NULL)) {
+               /* Receive */
+
+               /* TX auto fill */
+               *genfifoentry &= ~GQSPI_GENFIFO_TX;
+               /* Setup RX */
+               *genfifoentry |= GQSPI_GENFIFO_DATA_XFER;
+               *genfifoentry |= GQSPI_GENFIFO_RX;
+               *genfifoentry |=
+                       zynqmp_qspi_selectspimode(xqspi, transfer->rx_nbits);
+               xqspi->bytes_to_transfer = 0;
+               xqspi->bytes_to_receive = transfer->len;
+               zynq_qspi_setuprxdma(xqspi);
+       }
+}
+
+/**
+ * zynqmp_qspi_start_transfer: Initiates the QSPI transfer
+ * @master:    Pointer to the spi_master structure which provides
+ *             information about the controller.
+ * @qspi:      Pointer to the spi_device structure
+ * @transfer:  Pointer to the spi_transfer structure which provide information
+ *             about next transfer parameters
+ *
+ * This function fills the TX FIFO, starts the QSPI transfer, and waits for the
+ * transfer to be completed.
+ *
+ * Return:     Number of bytes transferred in the last transfer
+ */
+static int zynqmp_qspi_start_transfer(struct spi_master *master,
+                                     struct spi_device *qspi,
+                                     struct spi_transfer *transfer)
+{
+       struct zynqmp_qspi *xqspi = spi_master_get_devdata(master);
+       u32 genfifoentry = 0x0, transfer_len;
+
+       xqspi->txbuf = transfer->tx_buf;
+       xqspi->rxbuf = transfer->rx_buf;
+
+       zynqmp_qspi_setup_transfer(qspi, transfer);
+
+       genfifoentry |= xqspi->genfifocs;
+       genfifoentry |= xqspi->genfifobus;
+
+       zynqmp_qspi_txrxsetup(xqspi, transfer, &genfifoentry);
+
+       if (xqspi->mode == GQSPI_MODE_DMA)
+               transfer_len = xqspi->dma_rx_bytes;
+       else
+               transfer_len = transfer->len;
+
+       xqspi->genfifoentry = genfifoentry;
+       if ((transfer_len) < GQSPI_GENFIFO_IMM_DATA_MASK) {
+               genfifoentry &= ~GQSPI_GENFIFO_IMM_DATA_MASK;
+               genfifoentry |= transfer_len;
+               zynqmp_gqspi_write(xqspi, GQSPI_GEN_FIFO_OFST, genfifoentry);
+       } else {
+               int tempcount = transfer_len;
+               u32 exponent = 8;       /* 2^8 = 256 */
+               u8 imm_data = tempcount & 0xFF;
+
+               tempcount &= ~(tempcount & 0xFF);
+               /* Immediate entry */
+               if (tempcount != 0) {
+                       /* Exponent entries */
+                       genfifoentry |= GQSPI_GENFIFO_EXP;
+                       while (tempcount != 0) {
+                               if (tempcount & GQSPI_GENFIFO_EXP_START) {
+                                       genfifoentry &=
+                                           ~GQSPI_GENFIFO_IMM_DATA_MASK;
+                                       genfifoentry |= exponent;
+                                       zynqmp_gqspi_write(xqspi,
+                                                          GQSPI_GEN_FIFO_OFST,
+                                                          genfifoentry);
+                               }
+                               tempcount = tempcount >> 1;
+                               exponent++;
+                       }
+               }
+               if (imm_data != 0) {
+                       genfifoentry &= ~GQSPI_GENFIFO_EXP;
+                       genfifoentry &= ~GQSPI_GENFIFO_IMM_DATA_MASK;
+                       genfifoentry |= (u8) (imm_data & 0xFF);
+                       zynqmp_gqspi_write(xqspi,
+                                          GQSPI_GEN_FIFO_OFST, genfifoentry);
+               }
+       }
+
+       if ((xqspi->mode == GQSPI_MODE_IO) &&
+                       (xqspi->rxbuf != NULL)) {
+               /* Dummy generic FIFO entry */
+               zynqmp_gqspi_write(xqspi, GQSPI_GEN_FIFO_OFST, 0x0);
+       }
+
+       /* Since we are using manual mode */
+       zynqmp_gqspi_write(xqspi, GQSPI_CONFIG_OFST,
+                          zynqmp_gqspi_read(xqspi, GQSPI_CONFIG_OFST) |
+                          GQSPI_CFG_START_GEN_FIFO_MASK);
+
+       if (xqspi->txbuf != NULL)
+               /* Enable interrupts for TX */
+               zynqmp_gqspi_write(xqspi, GQSPI_IER_OFST,
+                                  GQSPI_IER_TXEMPTY_MASK |
+                                       GQSPI_IER_GENFIFOEMPTY_MASK |
+                                       GQSPI_IER_TXNOT_FULL_MASK);
+
+       if (xqspi->rxbuf != NULL) {
+               /* Enable interrupts for RX */
+               if (xqspi->mode == GQSPI_MODE_DMA) {
+                       /* Enable DMA interrupts */
+                       zynqmp_gqspi_write(xqspi,
+                                       GQSPI_QSPIDMA_DST_I_EN_OFST,
+                                       GQSPI_QSPIDMA_DST_I_EN_DONE_MASK);
+               } else {
+                       zynqmp_gqspi_write(xqspi, GQSPI_IER_OFST,
+                                       GQSPI_IER_GENFIFOEMPTY_MASK |
+                                       GQSPI_IER_RXNEMPTY_MASK |
+                                       GQSPI_IER_RXEMPTY_MASK);
+               }
+       }
+
+       return transfer->len;
+}
+
+/**
+ * zynqmp_qspi_suspend:        Suspend method for the QSPI driver
+ * @_dev:      Address of the platform_device structure
+ *
+ * This function stops the QSPI driver queue and disables the QSPI controller
+ *
+ * Return:     Always 0
+ */
+static int __maybe_unused zynqmp_qspi_suspend(struct device *dev)
+{
+       struct platform_device *pdev = container_of(dev,
+                                                   struct platform_device,
+                                                   dev);
+       struct spi_master *master = platform_get_drvdata(pdev);
+
+       spi_master_suspend(master);
+
+       zynqmp_unprepare_transfer_hardware(master);
+
+       return 0;
+}
+
+/**
+ * zynqmp_qspi_resume: Resume method for the QSPI driver
+ * @dev:       Address of the platform_device structure
+ *
+ * The function starts the QSPI driver queue and initializes the QSPI
+ * controller
+ *
+ * Return:     0 on success; error value otherwise
+ */
+static int __maybe_unused zynqmp_qspi_resume(struct device *dev)
+{
+       struct platform_device *pdev = container_of(dev,
+                                                   struct platform_device,
+                                                   dev);
+       struct spi_master *master = platform_get_drvdata(pdev);
+       struct zynqmp_qspi *xqspi = spi_master_get_devdata(master);
+       int ret = 0;
+
+       ret = clk_enable(xqspi->pclk);
+       if (ret) {
+               dev_err(dev, "Cannot enable APB clock.\n");
+               return ret;
+       }
+
+       ret = clk_enable(xqspi->refclk);
+       if (ret) {
+               dev_err(dev, "Cannot enable device clock.\n");
+               clk_disable(xqspi->pclk);
+               return ret;
+       }
+
+       spi_master_resume(master);
+
+       return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(zynqmp_qspi_dev_pm_ops, zynqmp_qspi_suspend,
+                        zynqmp_qspi_resume);
+
+/**
+ * zynqmp_qspi_probe:  Probe method for the QSPI driver
+ * @pdev:      Pointer to the platform_device structure
+ *
+ * This function initializes the driver data structures and the hardware.
+ *
+ * Return:     0 on success; error value otherwise
+ */
+static int zynqmp_qspi_probe(struct platform_device *pdev)
+{
+       int ret = 0;
+       struct spi_master *master;
+       struct zynqmp_qspi *xqspi;
+       struct resource *res;
+       struct device *dev = &pdev->dev;
+
+       master = spi_alloc_master(&pdev->dev, sizeof(*xqspi));
+       if (!master)
+               return -ENOMEM;
+
+       xqspi = spi_master_get_devdata(master);
+       master->dev.of_node = pdev->dev.of_node;
+       platform_set_drvdata(pdev, master);
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       xqspi->regs = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(xqspi->regs)) {
+               ret = PTR_ERR(xqspi->regs);
+               goto remove_master;
+       }
+
+       xqspi->dev = dev;
+       xqspi->pclk = devm_clk_get(&pdev->dev, "pclk");
+       if (IS_ERR(xqspi->pclk)) {
+               dev_err(dev, "pclk clock not found.\n");
+               ret = PTR_ERR(xqspi->pclk);
+               goto remove_master;
+       }
+
+       ret = clk_prepare_enable(xqspi->pclk);
+       if (ret) {
+               dev_err(dev, "Unable to enable APB clock.\n");
+               goto remove_master;
+       }
+
+       xqspi->refclk = devm_clk_get(&pdev->dev, "ref_clk");
+       if (IS_ERR(xqspi->refclk)) {
+               dev_err(dev, "ref_clk clock not found.\n");
+               ret = PTR_ERR(xqspi->refclk);
+               goto clk_dis_pclk;
+       }
+
+       ret = clk_prepare_enable(xqspi->refclk);
+       if (ret) {
+               dev_err(dev, "Unable to enable device clock.\n");
+               goto clk_dis_pclk;
+       }
+
+       /* QSPI controller initializations */
+       zynqmp_qspi_init_hw(xqspi);
+
+       xqspi->irq = platform_get_irq(pdev, 0);
+       if (xqspi->irq <= 0) {
+               ret = -ENXIO;
+               dev_err(dev, "irq resource not found\n");
+               goto clk_dis_all;
+       }
+       ret = devm_request_irq(&pdev->dev, xqspi->irq, zynqmp_qspi_irq,
+                              0, pdev->name, master);
+       if (ret != 0) {
+               ret = -ENXIO;
+               dev_err(dev, "request_irq failed\n");
+               goto clk_dis_all;
+       }
+
+       master->num_chipselect = GQSPI_DEFAULT_NUM_CS;
+
+       master->setup = zynqmp_qspi_setup;
+       master->set_cs = zynqmp_qspi_chipselect;
+       master->transfer_one = zynqmp_qspi_start_transfer;
+       master->prepare_transfer_hardware = zynqmp_prepare_transfer_hardware;
+       master->unprepare_transfer_hardware =
+                                       zynqmp_unprepare_transfer_hardware;
+       master->max_speed_hz = clk_get_rate(xqspi->refclk) / 2;
+       master->bits_per_word_mask = SPI_BPW_MASK(8);
+       master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_RX_DUAL | SPI_RX_QUAD |
+                           SPI_TX_DUAL | SPI_TX_QUAD;
+
+       if (master->dev.parent == NULL)
+               master->dev.parent = &master->dev;
+
+       ret = spi_register_master(master);
+       if (ret)
+               goto clk_dis_all;
+
+       return 0;
+
+clk_dis_all:
+       clk_disable_unprepare(xqspi->refclk);
+clk_dis_pclk:
+       clk_disable_unprepare(xqspi->pclk);
+remove_master:
+       spi_master_put(master);
+
+       return ret;
+}
+
+/**
+ * zynqmp_qspi_remove: Remove method for the QSPI driver
+ * @pdev:      Pointer to the platform_device structure
+ *
+ * This function is called if a device is physically removed from the system or
+ * if the driver module is being unloaded. It frees all resources allocated to
+ * the device.
+ *
+ * Return:     0 Always
+ */
+static int zynqmp_qspi_remove(struct platform_device *pdev)
+{
+       struct spi_master *master = platform_get_drvdata(pdev);
+       struct zynqmp_qspi *xqspi = spi_master_get_devdata(master);
+
+       zynqmp_gqspi_write(xqspi, GQSPI_EN_OFST, 0x0);
+       clk_disable_unprepare(xqspi->refclk);
+       clk_disable_unprepare(xqspi->pclk);
+
+       spi_unregister_master(master);
+
+       return 0;
+}
+
+static const struct of_device_id zynqmp_qspi_of_match[] = {
+       { .compatible = "xlnx,zynqmp-qspi-1.0", },
+       { /* End of table */ }
+};
+
+MODULE_DEVICE_TABLE(of, zynqmp_qspi_of_match);
+
+static struct platform_driver zynqmp_qspi_driver = {
+       .probe = zynqmp_qspi_probe,
+       .remove = zynqmp_qspi_remove,
+       .driver = {
+               .name = "zynqmp-qspi",
+               .of_match_table = zynqmp_qspi_of_match,
+               .pm = &zynqmp_qspi_dev_pm_ops,
+       },
+};
+
+module_platform_driver(zynqmp_qspi_driver);
+
+MODULE_AUTHOR("Xilinx, Inc.");
+MODULE_DESCRIPTION("Xilinx Zynqmp QSPI driver");
+MODULE_LICENSE("GPL");
index 50910d85df5af28d6f281485cc79d4b3e15bc7e5..cf8b91b23a763d60f8253e3ef1e8ccd420e52a0c 100644 (file)
@@ -571,7 +571,7 @@ static int __spi_map_msg(struct spi_master *master, struct spi_message *msg)
        return 0;
 }
 
-static int spi_unmap_msg(struct spi_master *master, struct spi_message *msg)
+static int __spi_unmap_msg(struct spi_master *master, struct spi_message *msg)
 {
        struct spi_transfer *xfer;
        struct device *tx_dev, *rx_dev;
@@ -583,15 +583,6 @@ static int spi_unmap_msg(struct spi_master *master, struct spi_message *msg)
        rx_dev = master->dma_rx->device->dev;
 
        list_for_each_entry(xfer, &msg->transfers, transfer_list) {
-               /*
-                * Restore the original value of tx_buf or rx_buf if they are
-                * NULL.
-                */
-               if (xfer->tx_buf == master->dummy_tx)
-                       xfer->tx_buf = NULL;
-               if (xfer->rx_buf == master->dummy_rx)
-                       xfer->rx_buf = NULL;
-
                if (!master->can_dma(master, msg->spi, xfer))
                        continue;
 
@@ -608,13 +599,32 @@ static inline int __spi_map_msg(struct spi_master *master,
        return 0;
 }
 
-static inline int spi_unmap_msg(struct spi_master *master,
-                               struct spi_message *msg)
+static inline int __spi_unmap_msg(struct spi_master *master,
+                                 struct spi_message *msg)
 {
        return 0;
 }
 #endif /* !CONFIG_HAS_DMA */
 
+static inline int spi_unmap_msg(struct spi_master *master,
+                               struct spi_message *msg)
+{
+       struct spi_transfer *xfer;
+
+       list_for_each_entry(xfer, &msg->transfers, transfer_list) {
+               /*
+                * Restore the original value of tx_buf or rx_buf if they are
+                * NULL.
+                */
+               if (xfer->tx_buf == master->dummy_tx)
+                       xfer->tx_buf = NULL;
+               if (xfer->rx_buf == master->dummy_rx)
+                       xfer->rx_buf = NULL;
+       }
+
+       return __spi_unmap_msg(master, msg);
+}
+
 static int spi_map_msg(struct spi_master *master, struct spi_message *msg)
 {
        struct spi_transfer *xfer;
@@ -988,9 +998,6 @@ void spi_finalize_current_message(struct spi_master *master)
 
        spin_lock_irqsave(&master->queue_lock, flags);
        mesg = master->cur_msg;
-       master->cur_msg = NULL;
-
-       queue_kthread_work(&master->kworker, &master->pump_messages);
        spin_unlock_irqrestore(&master->queue_lock, flags);
 
        spi_unmap_msg(master, mesg);
@@ -1003,9 +1010,13 @@ void spi_finalize_current_message(struct spi_master *master)
                }
        }
 
-       trace_spi_message_done(mesg);
-
+       spin_lock_irqsave(&master->queue_lock, flags);
+       master->cur_msg = NULL;
        master->cur_msg_prepared = false;
+       queue_kthread_work(&master->kworker, &master->pump_messages);
+       spin_unlock_irqrestore(&master->queue_lock, flags);
+
+       trace_spi_message_done(mesg);
 
        mesg->state = NULL;
        if (mesg->complete)
index 92c909eed6b504b01086e4775143128069f33261..dd616ff0ffc52542c3c8f8aaaf7c7d30bb9fd219 100644 (file)
@@ -95,37 +95,25 @@ MODULE_PARM_DESC(bufsiz, "data bytes in biggest supported SPI message");
 
 /*-------------------------------------------------------------------------*/
 
-/*
- * We can't use the standard synchronous wrappers for file I/O; we
- * need to protect against async removal of the underlying spi_device.
- */
-static void spidev_complete(void *arg)
-{
-       complete(arg);
-}
-
 static ssize_t
 spidev_sync(struct spidev_data *spidev, struct spi_message *message)
 {
        DECLARE_COMPLETION_ONSTACK(done);
        int status;
-
-       message->complete = spidev_complete;
-       message->context = &done;
+       struct spi_device *spi;
 
        spin_lock_irq(&spidev->spi_lock);
-       if (spidev->spi == NULL)
+       spi = spidev->spi;
+       spin_unlock_irq(&spidev->spi_lock);
+
+       if (spi == NULL)
                status = -ESHUTDOWN;
        else
-               status = spi_async(spidev->spi, message);
-       spin_unlock_irq(&spidev->spi_lock);
+               status = spi_sync(spi, message);
+
+       if (status == 0)
+               status = message->actual_length;
 
-       if (status == 0) {
-               wait_for_completion(&done);
-               status = message->status;
-               if (status == 0)
-                       status = message->actual_length;
-       }
        return status;
 }
 
@@ -647,7 +635,6 @@ err_find_dev:
 static int spidev_release(struct inode *inode, struct file *filp)
 {
        struct spidev_data      *spidev;
-       int                     status = 0;
 
        mutex_lock(&device_list_lock);
        spidev = filp->private_data;
@@ -676,7 +663,7 @@ static int spidev_release(struct inode *inode, struct file *filp)
        }
        mutex_unlock(&device_list_lock);
 
-       return status;
+       return 0;
 }
 
 static const struct file_operations spidev_fops = {
index 09428412139e399979537da2e6272eda827a8757..c5352ea4821ea0df593c7043ac911ee891f103b0 100644 (file)
@@ -621,8 +621,8 @@ static u32 ssb_pmu_get_alp_clock_clk0(struct ssb_chipcommon *cc)
        u32 crystalfreq;
        const struct pmu0_plltab_entry *e = NULL;
 
-       crystalfreq = chipco_read32(cc, SSB_CHIPCO_PMU_CTL) &
-                     SSB_CHIPCO_PMU_CTL_XTALFREQ >> SSB_CHIPCO_PMU_CTL_XTALFREQ_SHIFT;
+       crystalfreq = (chipco_read32(cc, SSB_CHIPCO_PMU_CTL) &
+                      SSB_CHIPCO_PMU_CTL_XTALFREQ)  >> SSB_CHIPCO_PMU_CTL_XTALFREQ_SHIFT;
        e = pmu0_plltab_find_entry(crystalfreq);
        BUG_ON(!e);
        return e->freq * 1000;
@@ -634,7 +634,7 @@ u32 ssb_pmu_get_alp_clock(struct ssb_chipcommon *cc)
 
        switch (bus->chip_id) {
        case 0x5354:
-               ssb_pmu_get_alp_clock_clk0(cc);
+               return ssb_pmu_get_alp_clock_clk0(cc);
        default:
                ssb_err("ERROR: PMU alp clock unknown for device %04X\n",
                        bus->chip_id);
index 3bad441de8dc90df646caecb64e40e54b83c15bc..c41b5575df05e38d7c5d5cdd3503d136affaabab 100644 (file)
@@ -647,6 +647,7 @@ kib_conn_t *kiblnd_create_conn(kib_peer_t *peer, struct rdma_cm_id *cmid,
        kib_dev_t             *dev;
        struct ib_qp_init_attr *init_qp_attr;
        struct kib_sched_info   *sched;
+       struct ib_cq_init_attr  cq_attr = {};
        kib_conn_t              *conn;
        struct ib_cq            *cq;
        unsigned long           flags;
@@ -742,10 +743,11 @@ kib_conn_t *kiblnd_create_conn(kib_peer_t *peer, struct rdma_cm_id *cmid,
 
        kiblnd_map_rx_descs(conn);
 
+       cq_attr.cqe = IBLND_CQ_ENTRIES(version);
+       cq_attr.comp_vector = kiblnd_get_completion_vector(conn, cpt);
        cq = ib_create_cq(cmid->device,
                          kiblnd_cq_completion, kiblnd_cq_event, conn,
-                         IBLND_CQ_ENTRIES(version),
-                         kiblnd_get_completion_vector(conn, cpt));
+                         &cq_attr);
        if (IS_ERR(cq)) {
                CERROR("Can't create CQ: %ld, cqe: %d\n",
                       PTR_ERR(cq), IBLND_CQ_ENTRIES(version));
index 3925db160650ca5d96df0880331717188a2ce23f..513c81f43d6e87926a2d368fc023525a6a37b338 100644 (file)
@@ -189,22 +189,7 @@ static inline int ll_quota_off(struct super_block *sb, int off, int remount)
 #endif
 
 
-
-/*
- * After 3.1, kernel's nameidata.intent.open.flags is different
- * with lustre's lookup_intent.it_flags, as lustre's it_flags'
- * lower bits equal to FMODE_xxx while kernel doesn't transliterate
- * lower bits of nameidata.intent.open.flags to FMODE_xxx.
- * */
 #include <linux/version.h>
-static inline int ll_namei_to_lookup_intent_flag(int flag)
-{
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 1, 0)
-       flag = (flag & ~O_ACCMODE) | OPEN_FMODE(flag);
-#endif
-       return flag;
-}
-
 #include <linux/fs.h>
 
 # define ll_umode_t    umode_t
index 5f918e3c4683ddac82c864d1d45f7c5700d20282..528af9011653d199f77dddb9926168acbfa96fef 100644 (file)
 #define VM_FAULT_RETRY 0
 #endif
 
-/* Kernel 3.1 kills LOOKUP_CONTINUE, LOOKUP_PARENT is equivalent to it.
- * seem kernel commit 49084c3bb2055c401f3493c13edae14d49128ca0 */
-#ifndef LOOKUP_CONTINUE
-#define LOOKUP_CONTINUE LOOKUP_PARENT
-#endif
-
 /** Only used on client-side for indicating the tail of dir hash/offset. */
 #define LL_DIR_END_OFF   0x7fffffffffffffffULL
 #define LL_DIR_END_OFF_32BIT    0x7fffffffUL
index 3711e671a4dfaa21af87d19857416f8f0f631734..69b203651905e93f77149754a5b9d6a021b6bf32 100644 (file)
@@ -118,7 +118,7 @@ failed:
        return rc;
 }
 
-static void *ll_follow_link(struct dentry *dentry, struct nameidata *nd)
+static const char *ll_follow_link(struct dentry *dentry, void **cookie)
 {
        struct inode *inode = d_inode(dentry);
        struct ptlrpc_request *request = NULL;
@@ -126,32 +126,22 @@ static void *ll_follow_link(struct dentry *dentry, struct nameidata *nd)
        char *symname = NULL;
 
        CDEBUG(D_VFSTRACE, "VFS Op\n");
-       /* Limit the recursive symlink depth to 5 instead of default
-        * 8 links when kernel has 4k stack to prevent stack overflow.
-        * For 8k stacks we need to limit it to 7 for local servers. */
-       if (THREAD_SIZE < 8192 && current->link_count >= 6) {
-               rc = -ELOOP;
-       } else if (THREAD_SIZE == 8192 && current->link_count >= 8) {
-               rc = -ELOOP;
-       } else {
-               ll_inode_size_lock(inode);
-               rc = ll_readlink_internal(inode, &request, &symname);
-               ll_inode_size_unlock(inode);
-       }
+       ll_inode_size_lock(inode);
+       rc = ll_readlink_internal(inode, &request, &symname);
+       ll_inode_size_unlock(inode);
        if (rc) {
                ptlrpc_req_finished(request);
-               request = NULL;
-               symname = ERR_PTR(rc);
+               return ERR_PTR(rc);
        }
 
-       nd_set_link(nd, symname);
        /* symname may contain a pointer to the request message buffer,
         * we delay request releasing until ll_put_link then.
         */
-       return request;
+       *cookie = request;
+       return symname;
 }
 
-static void ll_put_link(struct dentry *dentry, struct nameidata *nd, void *cookie)
+static void ll_put_link(struct inode *unused, void *cookie)
 {
        ptlrpc_req_finished(cookie);
 }
index 5ff4716b72c311485084005b9e09a36021157530..784b5ecfa8493ba07d8ba90cde1b11b2b6a4b6b7 100644 (file)
@@ -746,8 +746,8 @@ void oz_hcd_pd_reset(void *hpd, void *hport)
 /*
  * Context: softirq
  */
-void oz_hcd_get_desc_cnf(void *hport, u8 req_id, int status, const u8 *desc,
-                       int length, int offset, int total_size)
+void oz_hcd_get_desc_cnf(void *hport, u8 req_id, u8 status, const u8 *desc,
+                       u8 length, u16 offset, u16 total_size)
 {
        struct oz_port *port = hport;
        struct urb *urb;
@@ -759,8 +759,8 @@ void oz_hcd_get_desc_cnf(void *hport, u8 req_id, int status, const u8 *desc,
        if (!urb)
                return;
        if (status == 0) {
-               int copy_len;
-               int required_size = urb->transfer_buffer_length;
+               unsigned int copy_len;
+               unsigned int required_size = urb->transfer_buffer_length;
 
                if (required_size > total_size)
                        required_size = total_size;
index 4249fa37401289c4caf1f4cae4d46dba321f276b..d2a6085345bec8c2e927115389efc46bfbad3019 100644 (file)
@@ -29,8 +29,8 @@ void oz_usb_request_heartbeat(void *hpd);
 
 /* Confirmation functions.
  */
-void oz_hcd_get_desc_cnf(void *hport, u8 req_id, int status,
-       const u8 *desc, int length, int offset, int total_size);
+void oz_hcd_get_desc_cnf(void *hport, u8 req_id, u8 status,
+       const u8 *desc, u8 length, u16 offset, u16 total_size);
 void oz_hcd_control_cnf(void *hport, u8 req_id, u8 rcode,
        const u8 *data, int data_len);
 
index d434d8c6fff67c04b58d6cac5c76a6832bae5bc3..f660bb198c65534a6cbe8183d3f5d0a30a532eb1 100644 (file)
@@ -326,7 +326,11 @@ static void oz_usb_handle_ep_data(struct oz_usb_ctx *usb_ctx,
                        struct oz_multiple_fixed *body =
                                (struct oz_multiple_fixed *)data_hdr;
                        u8 *data = body->data;
-                       int n = (len - sizeof(struct oz_multiple_fixed)+1)
+                       unsigned int n;
+                       if (!body->unit_size ||
+                               len < sizeof(struct oz_multiple_fixed) - 1)
+                               break;
+                       n = (len - (sizeof(struct oz_multiple_fixed) - 1))
                                / body->unit_size;
                        while (n--) {
                                oz_hcd_data_ind(usb_ctx->hport, body->endpoint,
@@ -390,10 +394,15 @@ void oz_usb_rx(struct oz_pd *pd, struct oz_elt *elt)
        case OZ_GET_DESC_RSP: {
                        struct oz_get_desc_rsp *body =
                                (struct oz_get_desc_rsp *)usb_hdr;
-                       int data_len = elt->length -
-                                       sizeof(struct oz_get_desc_rsp) + 1;
-                       u16 offs = le16_to_cpu(get_unaligned(&body->offset));
-                       u16 total_size =
+                       u16 offs, total_size;
+                       u8 data_len;
+
+                       if (elt->length < sizeof(struct oz_get_desc_rsp) - 1)
+                               break;
+                       data_len = elt->length -
+                                       (sizeof(struct oz_get_desc_rsp) - 1);
+                       offs = le16_to_cpu(get_unaligned(&body->offset));
+                       total_size =
                                le16_to_cpu(get_unaligned(&body->total_size));
                        oz_dbg(ON, "USB_REQ_GET_DESCRIPTOR - cnf\n");
                        oz_hcd_get_desc_cnf(usb_ctx->hport, body->req_id,
index f1d47a0676c3e3ba29ea974754c77e8a32a3f950..ada8d5dafd492e97a1b4d9457d25e4a485e67556 100644 (file)
@@ -898,11 +898,11 @@ static void SwLedControlMode1(struct _adapter *padapter,
                          IS_LED_WPS_BLINKING(pLed))
                                return;
                        if (pLed->bLedLinkBlinkInProgress == true) {
-                               del_timer_sync(&pLed->BlinkTimer);
+                               del_timer(&pLed->BlinkTimer);
                                pLed->bLedLinkBlinkInProgress = false;
                        }
                        if (pLed->bLedBlinkInProgress == true) {
-                               del_timer_sync(&pLed->BlinkTimer);
+                               del_timer(&pLed->BlinkTimer);
                                pLed->bLedBlinkInProgress = false;
                        }
                        pLed->bLedNoLinkBlinkInProgress = true;
@@ -921,11 +921,11 @@ static void SwLedControlMode1(struct _adapter *padapter,
                            IS_LED_WPS_BLINKING(pLed))
                                return;
                        if (pLed->bLedNoLinkBlinkInProgress == true) {
-                               del_timer_sync(&pLed->BlinkTimer);
+                               del_timer(&pLed->BlinkTimer);
                                pLed->bLedNoLinkBlinkInProgress = false;
                        }
                        if (pLed->bLedBlinkInProgress == true) {
-                               del_timer_sync(&pLed->BlinkTimer);
+                               del_timer(&pLed->BlinkTimer);
                                pLed->bLedBlinkInProgress = false;
                        }
                        pLed->bLedLinkBlinkInProgress = true;
@@ -946,15 +946,15 @@ static void SwLedControlMode1(struct _adapter *padapter,
                        if (IS_LED_WPS_BLINKING(pLed))
                                return;
                        if (pLed->bLedNoLinkBlinkInProgress == true) {
-                               del_timer_sync(&pLed->BlinkTimer);
+                               del_timer(&pLed->BlinkTimer);
                                pLed->bLedNoLinkBlinkInProgress = false;
                        }
                        if (pLed->bLedLinkBlinkInProgress == true) {
-                               del_timer_sync(&pLed->BlinkTimer);
+                               del_timer(&pLed->BlinkTimer);
                                 pLed->bLedLinkBlinkInProgress = false;
                        }
                        if (pLed->bLedBlinkInProgress == true) {
-                               del_timer_sync(&pLed->BlinkTimer);
+                               del_timer(&pLed->BlinkTimer);
                                pLed->bLedBlinkInProgress = false;
                        }
                        pLed->bLedScanBlinkInProgress = true;
@@ -975,11 +975,11 @@ static void SwLedControlMode1(struct _adapter *padapter,
                            IS_LED_WPS_BLINKING(pLed))
                                return;
                        if (pLed->bLedNoLinkBlinkInProgress == true) {
-                               del_timer_sync(&pLed->BlinkTimer);
+                               del_timer(&pLed->BlinkTimer);
                                pLed->bLedNoLinkBlinkInProgress = false;
                        }
                        if (pLed->bLedLinkBlinkInProgress == true) {
-                               del_timer_sync(&pLed->BlinkTimer);
+                               del_timer(&pLed->BlinkTimer);
                                pLed->bLedLinkBlinkInProgress = false;
                        }
                        pLed->bLedBlinkInProgress = true;
@@ -998,19 +998,19 @@ static void SwLedControlMode1(struct _adapter *padapter,
        case LED_CTL_START_WPS_BOTTON:
                 if (pLed->bLedWPSBlinkInProgress == false) {
                        if (pLed->bLedNoLinkBlinkInProgress == true) {
-                               del_timer_sync(&pLed->BlinkTimer);
+                               del_timer(&pLed->BlinkTimer);
                                pLed->bLedNoLinkBlinkInProgress = false;
                        }
                        if (pLed->bLedLinkBlinkInProgress == true) {
-                               del_timer_sync(&pLed->BlinkTimer);
+                               del_timer(&pLed->BlinkTimer);
                                 pLed->bLedLinkBlinkInProgress = false;
                        }
                        if (pLed->bLedBlinkInProgress == true) {
-                               del_timer_sync(&pLed->BlinkTimer);
+                               del_timer(&pLed->BlinkTimer);
                                pLed->bLedBlinkInProgress = false;
                        }
                        if (pLed->bLedScanBlinkInProgress == true) {
-                               del_timer_sync(&pLed->BlinkTimer);
+                               del_timer(&pLed->BlinkTimer);
                                pLed->bLedScanBlinkInProgress = false;
                        }
                        pLed->bLedWPSBlinkInProgress = true;
@@ -1025,23 +1025,23 @@ static void SwLedControlMode1(struct _adapter *padapter,
                break;
        case LED_CTL_STOP_WPS:
                if (pLed->bLedNoLinkBlinkInProgress == true) {
-                       del_timer_sync(&pLed->BlinkTimer);
+                       del_timer(&pLed->BlinkTimer);
                        pLed->bLedNoLinkBlinkInProgress = false;
                }
                if (pLed->bLedLinkBlinkInProgress == true) {
-                       del_timer_sync(&pLed->BlinkTimer);
+                       del_timer(&pLed->BlinkTimer);
                         pLed->bLedLinkBlinkInProgress = false;
                }
                if (pLed->bLedBlinkInProgress == true) {
-                       del_timer_sync(&pLed->BlinkTimer);
+                       del_timer(&pLed->BlinkTimer);
                        pLed->bLedBlinkInProgress = false;
                }
                if (pLed->bLedScanBlinkInProgress == true) {
-                       del_timer_sync(&pLed->BlinkTimer);
+                       del_timer(&pLed->BlinkTimer);
                        pLed->bLedScanBlinkInProgress = false;
                }
                if (pLed->bLedWPSBlinkInProgress)
-                       del_timer_sync(&pLed->BlinkTimer);
+                       del_timer(&pLed->BlinkTimer);
                else
                        pLed->bLedWPSBlinkInProgress = true;
                pLed->CurrLedState = LED_BLINK_WPS_STOP;
@@ -1057,7 +1057,7 @@ static void SwLedControlMode1(struct _adapter *padapter,
                break;
        case LED_CTL_STOP_WPS_FAIL:
                if (pLed->bLedWPSBlinkInProgress) {
-                       del_timer_sync(&pLed->BlinkTimer);
+                       del_timer(&pLed->BlinkTimer);
                        pLed->bLedWPSBlinkInProgress = false;
                }
                pLed->bLedNoLinkBlinkInProgress = true;
@@ -1073,23 +1073,23 @@ static void SwLedControlMode1(struct _adapter *padapter,
                pLed->CurrLedState = LED_OFF;
                pLed->BlinkingLedState = LED_OFF;
                if (pLed->bLedNoLinkBlinkInProgress) {
-                       del_timer_sync(&pLed->BlinkTimer);
+                       del_timer(&pLed->BlinkTimer);
                        pLed->bLedNoLinkBlinkInProgress = false;
                }
                if (pLed->bLedLinkBlinkInProgress) {
-                       del_timer_sync(&pLed->BlinkTimer);
+                       del_timer(&pLed->BlinkTimer);
                        pLed->bLedLinkBlinkInProgress = false;
                }
                if (pLed->bLedBlinkInProgress) {
-                       del_timer_sync(&pLed->BlinkTimer);
+                       del_timer(&pLed->BlinkTimer);
                        pLed->bLedBlinkInProgress = false;
                }
                if (pLed->bLedWPSBlinkInProgress) {
-                       del_timer_sync(&pLed->BlinkTimer);
+                       del_timer(&pLed->BlinkTimer);
                        pLed->bLedWPSBlinkInProgress = false;
                }
                if (pLed->bLedScanBlinkInProgress) {
-                       del_timer_sync(&pLed->BlinkTimer);
+                       del_timer(&pLed->BlinkTimer);
                        pLed->bLedScanBlinkInProgress = false;
                }
                mod_timer(&pLed->BlinkTimer,
@@ -1116,7 +1116,7 @@ static void SwLedControlMode2(struct _adapter *padapter,
                                return;
 
                        if (pLed->bLedBlinkInProgress == true) {
-                               del_timer_sync(&pLed->BlinkTimer);
+                               del_timer(&pLed->BlinkTimer);
                                pLed->bLedBlinkInProgress = false;
                        }
                        pLed->bLedScanBlinkInProgress = true;
@@ -1154,11 +1154,11 @@ static void SwLedControlMode2(struct _adapter *padapter,
                pLed->CurrLedState = LED_ON;
                pLed->BlinkingLedState = LED_ON;
                if (pLed->bLedBlinkInProgress) {
-                       del_timer_sync(&pLed->BlinkTimer);
+                       del_timer(&pLed->BlinkTimer);
                        pLed->bLedBlinkInProgress = false;
                }
                if (pLed->bLedScanBlinkInProgress) {
-                       del_timer_sync(&pLed->BlinkTimer);
+                       del_timer(&pLed->BlinkTimer);
                        pLed->bLedScanBlinkInProgress = false;
                }
 
@@ -1170,11 +1170,11 @@ static void SwLedControlMode2(struct _adapter *padapter,
        case LED_CTL_START_WPS_BOTTON:
                if (pLed->bLedWPSBlinkInProgress == false) {
                        if (pLed->bLedBlinkInProgress == true) {
-                               del_timer_sync(&pLed->BlinkTimer);
+                               del_timer(&pLed->BlinkTimer);
                                pLed->bLedBlinkInProgress = false;
                        }
                        if (pLed->bLedScanBlinkInProgress == true) {
-                               del_timer_sync(&pLed->BlinkTimer);
+                               del_timer(&pLed->BlinkTimer);
                                pLed->bLedScanBlinkInProgress = false;
                        }
                        pLed->bLedWPSBlinkInProgress = true;
@@ -1214,15 +1214,15 @@ static void SwLedControlMode2(struct _adapter *padapter,
                pLed->CurrLedState = LED_OFF;
                pLed->BlinkingLedState = LED_OFF;
                if (pLed->bLedBlinkInProgress) {
-                       del_timer_sync(&pLed->BlinkTimer);
+                       del_timer(&pLed->BlinkTimer);
                        pLed->bLedBlinkInProgress = false;
                }
                if (pLed->bLedScanBlinkInProgress) {
-                       del_timer_sync(&pLed->BlinkTimer);
+                       del_timer(&pLed->BlinkTimer);
                        pLed->bLedScanBlinkInProgress = false;
                }
                if (pLed->bLedWPSBlinkInProgress) {
-                       del_timer_sync(&pLed->BlinkTimer);
+                       del_timer(&pLed->BlinkTimer);
                        pLed->bLedWPSBlinkInProgress = false;
                }
                mod_timer(&pLed->BlinkTimer,
@@ -1248,7 +1248,7 @@ static void SwLedControlMode3(struct _adapter *padapter,
                        if (IS_LED_WPS_BLINKING(pLed))
                                return;
                        if (pLed->bLedBlinkInProgress == true) {
-                               del_timer_sync(&pLed->BlinkTimer);
+                               del_timer(&pLed->BlinkTimer);
                                pLed->bLedBlinkInProgress = false;
                        }
                        pLed->bLedScanBlinkInProgress = true;
@@ -1286,11 +1286,11 @@ static void SwLedControlMode3(struct _adapter *padapter,
                pLed->CurrLedState = LED_ON;
                pLed->BlinkingLedState = LED_ON;
                if (pLed->bLedBlinkInProgress) {
-                       del_timer_sync(&pLed->BlinkTimer);
+                       del_timer(&pLed->BlinkTimer);
                        pLed->bLedBlinkInProgress = false;
                }
                if (pLed->bLedScanBlinkInProgress) {
-                       del_timer_sync(&pLed->BlinkTimer);
+                       del_timer(&pLed->BlinkTimer);
                        pLed->bLedScanBlinkInProgress = false;
                }
                mod_timer(&pLed->BlinkTimer,
@@ -1300,11 +1300,11 @@ static void SwLedControlMode3(struct _adapter *padapter,
        case LED_CTL_START_WPS_BOTTON:
                if (pLed->bLedWPSBlinkInProgress == false) {
                        if (pLed->bLedBlinkInProgress == true) {
-                               del_timer_sync(&pLed->BlinkTimer);
+                               del_timer(&pLed->BlinkTimer);
                                pLed->bLedBlinkInProgress = false;
                        }
                        if (pLed->bLedScanBlinkInProgress == true) {
-                               del_timer_sync(&pLed->BlinkTimer);
+                               del_timer(&pLed->BlinkTimer);
                                pLed->bLedScanBlinkInProgress = false;
                        }
                        pLed->bLedWPSBlinkInProgress = true;
@@ -1319,7 +1319,7 @@ static void SwLedControlMode3(struct _adapter *padapter,
                break;
        case LED_CTL_STOP_WPS:
                if (pLed->bLedWPSBlinkInProgress) {
-                       del_timer_sync(&(pLed->BlinkTimer));
+                       del_timer(&pLed->BlinkTimer);
                        pLed->bLedWPSBlinkInProgress = false;
                } else
                        pLed->bLedWPSBlinkInProgress = true;
@@ -1336,7 +1336,7 @@ static void SwLedControlMode3(struct _adapter *padapter,
                break;
        case LED_CTL_STOP_WPS_FAIL:
                if (pLed->bLedWPSBlinkInProgress) {
-                       del_timer_sync(&pLed->BlinkTimer);
+                       del_timer(&pLed->BlinkTimer);
                        pLed->bLedWPSBlinkInProgress = false;
                }
                pLed->CurrLedState = LED_OFF;
@@ -1357,15 +1357,15 @@ static void SwLedControlMode3(struct _adapter *padapter,
                pLed->CurrLedState = LED_OFF;
                pLed->BlinkingLedState = LED_OFF;
                if (pLed->bLedBlinkInProgress) {
-                       del_timer_sync(&pLed->BlinkTimer);
+                       del_timer(&pLed->BlinkTimer);
                        pLed->bLedBlinkInProgress = false;
                }
                if (pLed->bLedScanBlinkInProgress) {
-                       del_timer_sync(&pLed->BlinkTimer);
+                       del_timer(&pLed->BlinkTimer);
                        pLed->bLedScanBlinkInProgress = false;
                }
                if (pLed->bLedWPSBlinkInProgress) {
-                       del_timer_sync(&pLed->BlinkTimer);
+                       del_timer(&pLed->BlinkTimer);
                        pLed->bLedWPSBlinkInProgress = false;
                }
                mod_timer(&pLed->BlinkTimer,
@@ -1388,7 +1388,7 @@ static void SwLedControlMode4(struct _adapter *padapter,
        case LED_CTL_START_TO_LINK:
                if (pLed1->bLedWPSBlinkInProgress) {
                        pLed1->bLedWPSBlinkInProgress = false;
-                       del_timer_sync(&pLed1->BlinkTimer);
+                       del_timer(&pLed1->BlinkTimer);
                        pLed1->BlinkingLedState = LED_OFF;
                        pLed1->CurrLedState = LED_OFF;
                        if (pLed1->bLedOn)
@@ -1400,11 +1400,11 @@ static void SwLedControlMode4(struct _adapter *padapter,
                            IS_LED_WPS_BLINKING(pLed))
                                return;
                        if (pLed->bLedBlinkInProgress == true) {
-                               del_timer_sync(&pLed->BlinkTimer);
+                               del_timer(&pLed->BlinkTimer);
                                pLed->bLedBlinkInProgress = false;
                        }
                        if (pLed->bLedNoLinkBlinkInProgress == true) {
-                               del_timer_sync(&pLed->BlinkTimer);
+                               del_timer(&pLed->BlinkTimer);
                                pLed->bLedNoLinkBlinkInProgress = false;
                        }
                        pLed->bLedStartToLinkBlinkInProgress = true;
@@ -1426,7 +1426,7 @@ static void SwLedControlMode4(struct _adapter *padapter,
                if (LedAction == LED_CTL_LINK) {
                        if (pLed1->bLedWPSBlinkInProgress) {
                                pLed1->bLedWPSBlinkInProgress = false;
-                               del_timer_sync(&pLed1->BlinkTimer);
+                               del_timer(&pLed1->BlinkTimer);
                                pLed1->BlinkingLedState = LED_OFF;
                                pLed1->CurrLedState = LED_OFF;
                                if (pLed1->bLedOn)
@@ -1439,7 +1439,7 @@ static void SwLedControlMode4(struct _adapter *padapter,
                            IS_LED_WPS_BLINKING(pLed))
                                return;
                        if (pLed->bLedBlinkInProgress == true) {
-                               del_timer_sync(&pLed->BlinkTimer);
+                               del_timer(&pLed->BlinkTimer);
                                pLed->bLedBlinkInProgress = false;
                        }
                        pLed->bLedNoLinkBlinkInProgress = true;
@@ -1460,11 +1460,11 @@ static void SwLedControlMode4(struct _adapter *padapter,
                        if (IS_LED_WPS_BLINKING(pLed))
                                return;
                        if (pLed->bLedNoLinkBlinkInProgress == true) {
-                               del_timer_sync(&pLed->BlinkTimer);
+                               del_timer(&pLed->BlinkTimer);
                                pLed->bLedNoLinkBlinkInProgress = false;
                        }
                        if (pLed->bLedBlinkInProgress == true) {
-                               del_timer_sync(&pLed->BlinkTimer);
+                               del_timer(&pLed->BlinkTimer);
                                pLed->bLedBlinkInProgress = false;
                        }
                        pLed->bLedScanBlinkInProgress = true;
@@ -1485,7 +1485,7 @@ static void SwLedControlMode4(struct _adapter *padapter,
                            IS_LED_WPS_BLINKING(pLed))
                                return;
                        if (pLed->bLedNoLinkBlinkInProgress == true) {
-                               del_timer_sync(&pLed->BlinkTimer);
+                               del_timer(&pLed->BlinkTimer);
                                pLed->bLedNoLinkBlinkInProgress = false;
                        }
                        pLed->bLedBlinkInProgress = true;
@@ -1503,7 +1503,7 @@ static void SwLedControlMode4(struct _adapter *padapter,
        case LED_CTL_START_WPS_BOTTON:
                if (pLed1->bLedWPSBlinkInProgress) {
                        pLed1->bLedWPSBlinkInProgress = false;
-                       del_timer_sync(&(pLed1->BlinkTimer));
+                       del_timer(&pLed1->BlinkTimer);
                        pLed1->BlinkingLedState = LED_OFF;
                        pLed1->CurrLedState = LED_OFF;
                        if (pLed1->bLedOn)
@@ -1512,15 +1512,15 @@ static void SwLedControlMode4(struct _adapter *padapter,
                }
                if (pLed->bLedWPSBlinkInProgress == false) {
                        if (pLed->bLedNoLinkBlinkInProgress == true) {
-                               del_timer_sync(&pLed->BlinkTimer);
+                               del_timer(&pLed->BlinkTimer);
                                pLed->bLedNoLinkBlinkInProgress = false;
                        }
                        if (pLed->bLedBlinkInProgress == true) {
-                               del_timer_sync(&pLed->BlinkTimer);
+                               del_timer(&pLed->BlinkTimer);
                                pLed->bLedBlinkInProgress = false;
                        }
                        if (pLed->bLedScanBlinkInProgress == true) {
-                               del_timer_sync(&pLed->BlinkTimer);
+                               del_timer(&pLed->BlinkTimer);
                                pLed->bLedScanBlinkInProgress = false;
                        }
                        pLed->bLedWPSBlinkInProgress = true;
@@ -1538,7 +1538,7 @@ static void SwLedControlMode4(struct _adapter *padapter,
                break;
        case LED_CTL_STOP_WPS:  /*WPS connect success*/
                if (pLed->bLedWPSBlinkInProgress) {
-                       del_timer_sync(&pLed->BlinkTimer);
+                       del_timer(&pLed->BlinkTimer);
                        pLed->bLedWPSBlinkInProgress = false;
                }
                pLed->bLedNoLinkBlinkInProgress = true;
@@ -1552,7 +1552,7 @@ static void SwLedControlMode4(struct _adapter *padapter,
                break;
        case LED_CTL_STOP_WPS_FAIL:     /*WPS authentication fail*/
                if (pLed->bLedWPSBlinkInProgress) {
-                       del_timer_sync(&pLed->BlinkTimer);
+                       del_timer(&pLed->BlinkTimer);
                        pLed->bLedWPSBlinkInProgress = false;
                }
                pLed->bLedNoLinkBlinkInProgress = true;
@@ -1565,7 +1565,7 @@ static void SwLedControlMode4(struct _adapter *padapter,
                          msecs_to_jiffies(LED_BLINK_NO_LINK_INTERVAL_ALPHA));
                /*LED1 settings*/
                if (pLed1->bLedWPSBlinkInProgress)
-                       del_timer_sync(&pLed1->BlinkTimer);
+                       del_timer(&pLed1->BlinkTimer);
                else
                        pLed1->bLedWPSBlinkInProgress = true;
                pLed1->CurrLedState = LED_BLINK_WPS_STOP;
@@ -1578,7 +1578,7 @@ static void SwLedControlMode4(struct _adapter *padapter,
                break;
        case LED_CTL_STOP_WPS_FAIL_OVERLAP:     /*WPS session overlap*/
                if (pLed->bLedWPSBlinkInProgress) {
-                       del_timer_sync(&pLed->BlinkTimer);
+                       del_timer(&pLed->BlinkTimer);
                        pLed->bLedWPSBlinkInProgress = false;
                }
                pLed->bLedNoLinkBlinkInProgress = true;
@@ -1591,7 +1591,7 @@ static void SwLedControlMode4(struct _adapter *padapter,
                          msecs_to_jiffies(LED_BLINK_NO_LINK_INTERVAL_ALPHA));
                /*LED1 settings*/
                if (pLed1->bLedWPSBlinkInProgress)
-                       del_timer_sync(&pLed1->BlinkTimer);
+                       del_timer(&pLed1->BlinkTimer);
                else
                        pLed1->bLedWPSBlinkInProgress = true;
                pLed1->CurrLedState = LED_BLINK_WPS_STOP_OVERLAP;
@@ -1607,31 +1607,31 @@ static void SwLedControlMode4(struct _adapter *padapter,
                pLed->CurrLedState = LED_OFF;
                pLed->BlinkingLedState = LED_OFF;
                if (pLed->bLedNoLinkBlinkInProgress) {
-                       del_timer_sync(&pLed->BlinkTimer);
+                       del_timer(&pLed->BlinkTimer);
                        pLed->bLedNoLinkBlinkInProgress = false;
                }
                if (pLed->bLedLinkBlinkInProgress) {
-                       del_timer_sync(&pLed->BlinkTimer);
+                       del_timer(&pLed->BlinkTimer);
                        pLed->bLedLinkBlinkInProgress = false;
                }
                if (pLed->bLedBlinkInProgress) {
-                       del_timer_sync(&pLed->BlinkTimer);
+                       del_timer(&pLed->BlinkTimer);
                        pLed->bLedBlinkInProgress = false;
                }
                if (pLed->bLedWPSBlinkInProgress) {
-                       del_timer_sync(&pLed->BlinkTimer);
+                       del_timer(&pLed->BlinkTimer);
                        pLed->bLedWPSBlinkInProgress = false;
                }
                if (pLed->bLedScanBlinkInProgress) {
-                       del_timer_sync(&pLed->BlinkTimer);
+                       del_timer(&pLed->BlinkTimer);
                        pLed->bLedScanBlinkInProgress = false;
                }
                if (pLed->bLedStartToLinkBlinkInProgress) {
-                       del_timer_sync(&pLed->BlinkTimer);
+                       del_timer(&pLed->BlinkTimer);
                        pLed->bLedStartToLinkBlinkInProgress = false;
                }
                if (pLed1->bLedWPSBlinkInProgress) {
-                       del_timer_sync(&pLed1->BlinkTimer);
+                       del_timer(&pLed1->BlinkTimer);
                        pLed1->bLedWPSBlinkInProgress = false;
                }
                pLed1->BlinkingLedState = LED_UNKNOWN;
@@ -1671,7 +1671,7 @@ static void SwLedControlMode5(struct _adapter *padapter,
                        ; /* dummy branch */
                else if (pLed->bLedScanBlinkInProgress == false) {
                        if (pLed->bLedBlinkInProgress == true) {
-                               del_timer_sync(&pLed->BlinkTimer);
+                               del_timer(&pLed->BlinkTimer);
                                pLed->bLedBlinkInProgress = false;
                        }
                        pLed->bLedScanBlinkInProgress = true;
@@ -1705,7 +1705,7 @@ static void SwLedControlMode5(struct _adapter *padapter,
                pLed->CurrLedState = LED_OFF;
                pLed->BlinkingLedState = LED_OFF;
                if (pLed->bLedBlinkInProgress) {
-                       del_timer_sync(&pLed->BlinkTimer);
+                       del_timer(&pLed->BlinkTimer);
                        pLed->bLedBlinkInProgress = false;
                }
                SwLedOff(padapter, pLed);
@@ -1756,7 +1756,7 @@ static void SwLedControlMode6(struct _adapter *padapter,
        case LED_CTL_START_WPS_BOTTON:
                if (pLed->bLedWPSBlinkInProgress == false) {
                        if (pLed->bLedBlinkInProgress == true) {
-                               del_timer_sync(&pLed->BlinkTimer);
+                               del_timer(&pLed->BlinkTimer);
                                pLed->bLedBlinkInProgress = false;
                        }
                        pLed->bLedWPSBlinkInProgress = true;
@@ -1772,7 +1772,7 @@ static void SwLedControlMode6(struct _adapter *padapter,
        case LED_CTL_STOP_WPS_FAIL:
        case LED_CTL_STOP_WPS:
                if (pLed->bLedWPSBlinkInProgress) {
-                       del_timer_sync(&pLed->BlinkTimer);
+                       del_timer(&pLed->BlinkTimer);
                        pLed->bLedWPSBlinkInProgress = false;
                }
                pLed->CurrLedState = LED_ON;
@@ -1784,11 +1784,11 @@ static void SwLedControlMode6(struct _adapter *padapter,
                pLed->CurrLedState = LED_OFF;
                pLed->BlinkingLedState = LED_OFF;
                if (pLed->bLedBlinkInProgress) {
-                       del_timer_sync(&pLed->BlinkTimer);
+                       del_timer(&pLed->BlinkTimer);
                        pLed->bLedBlinkInProgress = false;
                }
                if (pLed->bLedWPSBlinkInProgress) {
-                       del_timer_sync(&pLed->BlinkTimer);
+                       del_timer(&pLed->BlinkTimer);
                        pLed->bLedWPSBlinkInProgress = false;
                }
                SwLedOff(padapter, pLed);
index 1a1c38f885d6b191d5a62b5fb1aae26713dd6cb3..e35854d28f90ed96aa3ff149f39175c9e46b1373 100644 (file)
@@ -910,7 +910,7 @@ void r8712_createbss_cmd_callback(struct _adapter *padapter,
        if (pcmd->res != H2C_SUCCESS)
                mod_timer(&pmlmepriv->assoc_timer,
                          jiffies + msecs_to_jiffies(1));
-       del_timer_sync(&pmlmepriv->assoc_timer);
+       del_timer(&pmlmepriv->assoc_timer);
 #ifdef __BIG_ENDIAN
        /* endian_convert */
        pnetwork->Length = le32_to_cpu(pnetwork->Length);
index fb2b195b90af0d1690552dfccb6ec93b13960fdf..c044b0e55ba93d0c989031d52ce99f4008ae0630 100644 (file)
@@ -582,7 +582,7 @@ void r8712_surveydone_event_callback(struct _adapter *adapter, u8 *pbuf)
        spin_lock_irqsave(&pmlmepriv->lock, irqL);
 
        if (check_fwstate(pmlmepriv, _FW_UNDER_SURVEY) == true) {
-               del_timer_sync(&pmlmepriv->scan_to_timer);
+               del_timer(&pmlmepriv->scan_to_timer);
 
                _clr_fwstate_(pmlmepriv, _FW_UNDER_SURVEY);
        }
@@ -696,7 +696,7 @@ void r8712_ind_disconnect(struct _adapter *padapter)
        }
        if (padapter->pwrctrlpriv.pwr_mode !=
            padapter->registrypriv.power_mgnt) {
-               del_timer_sync(&pmlmepriv->dhcp_timer);
+               del_timer(&pmlmepriv->dhcp_timer);
                r8712_set_ps_mode(padapter, padapter->registrypriv.power_mgnt,
                                  padapter->registrypriv.smart_ps);
        }
@@ -910,7 +910,7 @@ void r8712_joinbss_event_callback(struct _adapter *adapter, u8 *pbuf)
                        if (check_fwstate(pmlmepriv, WIFI_STATION_STATE)
                                == true)
                                r8712_indicate_connect(adapter);
-                       del_timer_sync(&pmlmepriv->assoc_timer);
+                       del_timer(&pmlmepriv->assoc_timer);
                } else
                        goto ignore_joinbss_callback;
        } else {
index aaa584435c87d25d3efb3bbbe794da6cf2096c24..9bc04f474d18d7c79311c8bd6fc80b48015a6550 100644 (file)
@@ -103,7 +103,7 @@ void r8712_cpwm_int_hdl(struct _adapter *padapter,
 
        if (pwrpriv->cpwm_tog == ((preportpwrstate->state) & 0x80))
                return;
-       del_timer_sync(&padapter->pwrctrlpriv.rpwm_check_timer);
+       del_timer(&padapter->pwrctrlpriv.rpwm_check_timer);
        _enter_pwrlock(&pwrpriv->lock);
        pwrpriv->cpwm = (preportpwrstate->state) & 0xf;
        if (pwrpriv->cpwm >= PS_STATE_S2) {
index 7bb96c47f1883dad0c62e8618b2e98ac773fca27..a9b93d0f6f566b83bb00271de37f68dc1716586c 100644 (file)
@@ -198,7 +198,7 @@ void r8712_free_stainfo(struct _adapter *padapter, struct sta_info *psta)
         * cancel reordering_ctrl_timer */
        for (i = 0; i < 16; i++) {
                preorder_ctrl = &psta->recvreorder_ctrl[i];
-               del_timer_sync(&preorder_ctrl->reordering_ctrl_timer);
+               del_timer(&preorder_ctrl->reordering_ctrl_timer);
        }
        spin_lock(&(pfree_sta_queue->lock));
        /* insert into free_sta_queue; 20061114 */
index d64b6ed9c0c967eb725a8911bc04138e6d25430d..aed49bf762b4174c0c76e4851d81c7a8a7af5d41 100644 (file)
@@ -230,7 +230,6 @@ static struct scsi_host_template rtsx_host_template = {
 
        /* queue commands only, only one command per LUN */
        .can_queue =                    1,
-       .cmd_per_lun =                  1,
 
        /* unknown initiator id */
        .this_id =                      -1,
index 74e6114ff18f9343e3012cf21c7faadbdf5c6f61..4672bb1a24d0ac4c9af46c53faa36cd44844c4c1 100644 (file)
@@ -23,7 +23,7 @@
 #include <linux/module.h>
 #include <linux/idr.h>
 #include <asm/unaligned.h>
-#include <scsi/scsi_device.h>
+#include <scsi/scsi_proto.h>
 #include <scsi/iscsi_proto.h>
 #include <scsi/scsi_tcq.h>
 #include <target/target_core_base.h>
index 34c3cd1b05ce8a40d9911da3815bc4a44b8a479c..5fabcd3d623f27fe9cd1f97b9d4c311157ade876 100644 (file)
@@ -17,7 +17,6 @@
  * GNU General Public License for more details.
  ******************************************************************************/
 
-#include <scsi/scsi_device.h>
 #include <target/target_core_base.h>
 #include <target/target_core_fabric.h>
 
index b0224a77e26d3aff2549b71dd4b8c6a816213557..fe9a582ca6af4f4e83410dab6ecd22e28d74ff96 100644 (file)
@@ -17,7 +17,7 @@
  ******************************************************************************/
 
 #include <asm/unaligned.h>
-#include <scsi/scsi_device.h>
+#include <scsi/scsi_proto.h>
 #include <scsi/iscsi_proto.h>
 #include <target/target_core_base.h>
 #include <target/target_core_fabric.h>
index 18b0f9703ff282a7c63229e822b6f2b8a01b26e2..ce81f17ad1ba5f97595ada370d8de40429eb9e0c 100644 (file)
@@ -30,7 +30,7 @@
 #include <linux/ctype.h>
 #include <linux/firewire.h>
 #include <linux/firewire-constants.h>
-#include <scsi/scsi.h>
+#include <scsi/scsi_proto.h>
 #include <scsi/scsi_tcq.h>
 #include <target/target_core_base.h>
 #include <target/target_core_backend.h>
index 4f8d4d459aa4f936a09438cc076fc998a75aa783..8ca3737742765a9aca58c2890a9d11a0cfe50237 100644 (file)
@@ -28,8 +28,7 @@
 #include <linux/configfs.h>
 #include <linux/export.h>
 #include <linux/file.h>
-#include <scsi/scsi.h>
-#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_proto.h>
 #include <asm/unaligned.h>
 
 #include <target/target_core_base.h>
index ce5f768181ff6593a7afac365214c77b0f0aceab..417f88b498c72585570398f6f783850c5473bd67 100644 (file)
@@ -36,8 +36,8 @@
 #include <asm/unaligned.h>
 #include <net/sock.h>
 #include <net/tcp.h>
-#include <scsi/scsi.h>
-#include <scsi/scsi_device.h>
+#include <scsi/scsi_common.h>
+#include <scsi/scsi_proto.h>
 
 #include <target/target_core_base.h>
 #include <target/target_core_backend.h>
index 35bfe77160d86828b6511034787862f74788addc..41f4f270f91982746e8ce237f22bd1680d51722e 100644 (file)
@@ -29,8 +29,8 @@
 #include <linux/ctype.h>
 #include <linux/spinlock.h>
 #include <linux/export.h>
-#include <scsi/scsi.h>
-#include <scsi/scsi_cmnd.h>
+
+#include <scsi/scsi_proto.h>
 
 #include <target/target_core_base.h>
 #include <target/target_core_fabric.h>
index 3f27bfd816d87201c5f3cec3ad7857ead488191b..d48379a258c74bb34e49d998da5c1fec8b91a389 100644 (file)
@@ -31,8 +31,7 @@
 #include <linux/spinlock.h>
 #include <linux/module.h>
 #include <linux/falloc.h>
-#include <scsi/scsi.h>
-#include <scsi/scsi_host.h>
+#include <scsi/scsi_proto.h>
 #include <asm/unaligned.h>
 
 #include <target/target_core_base.h>
index 8c965683789f9e141233edac76593e156a58bd2f..972ed1781ae2f0ad08c5671d7f8cc535cf12cde9 100644 (file)
@@ -35,8 +35,7 @@
 #include <linux/genhd.h>
 #include <linux/file.h>
 #include <linux/module.h>
-#include <scsi/scsi.h>
-#include <scsi/scsi_host.h>
+#include <scsi/scsi_proto.h>
 #include <asm/unaligned.h>
 
 #include <target/target_core_base.h>
index a15411c79ae99649041c216439e938f52a7c071a..7ca642361f9c27279f39577557ca4788151eda66 100644 (file)
@@ -28,8 +28,7 @@
 #include <linux/spinlock.h>
 #include <linux/list.h>
 #include <linux/file.h>
-#include <scsi/scsi.h>
-#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_proto.h>
 #include <asm/unaligned.h>
 
 #include <target/target_core_base.h>
index ecc5eaef13d6c38956a213a784ae66e1b4e0411b..26581e2151415563996f5d2ceaedb96c789749d1 100644 (file)
@@ -36,9 +36,7 @@
 #include <linux/module.h>
 #include <asm/unaligned.h>
 
-#include <scsi/scsi.h>
 #include <scsi/scsi_device.h>
-#include <scsi/scsi_cmnd.h>
 #include <scsi/scsi_host.h>
 #include <scsi/scsi_tcq.h>
 
index 820d3052b775caf438912d703402ebb4172d7f31..6d2007e35df65919f1687341499b26076ba25ed1 100644 (file)
 #define PS_TIMEOUT_OTHER       (500*HZ)
 
 #include <linux/device.h>
-#include <scsi/scsi_driver.h>
-#include <scsi/scsi_device.h>
 #include <linux/kref.h>
 #include <linux/kobject.h>
 
+struct scsi_device;
+
 struct pscsi_plugin_task {
-       unsigned char pscsi_sense[SCSI_SENSE_BUFFERSIZE];
+       unsigned char pscsi_sense[TRANSPORT_SENSE_BUFFER];
        int     pscsi_direction;
        int     pscsi_result;
        u32     pscsi_resid;
index d16489b6a1a4767ef4a8ba9445998a7bff2845d8..b2d8f6f9163336a7f17ce3107e447476c4729994 100644 (file)
@@ -29,8 +29,7 @@
 #include <linux/timer.h>
 #include <linux/slab.h>
 #include <linux/spinlock.h>
-#include <scsi/scsi.h>
-#include <scsi/scsi_host.h>
+#include <scsi/scsi_proto.h>
 
 #include <target/target_core_base.h>
 #include <target/target_core_backend.h>
index 733824e3825f4845e9035b9f00a7d553b9d59d6e..43719b393ca9e607912693ee5b14525ad8830266 100644 (file)
@@ -25,7 +25,7 @@
 #include <linux/ratelimit.h>
 #include <linux/crc-t10dif.h>
 #include <asm/unaligned.h>
-#include <scsi/scsi.h>
+#include <scsi/scsi_proto.h>
 #include <scsi/scsi_tcq.h>
 
 #include <target/target_core_base.h>
index 7912aa1243857a8968dcff5fa0f643b8e249dd6e..52ea640274f4fde0dd387866842c3aac3fcf77a3 100644 (file)
@@ -24,7 +24,8 @@
 #include <linux/module.h>
 #include <asm/unaligned.h>
 
-#include <scsi/scsi.h>
+#include <scsi/scsi_proto.h>
+#include <scsi/scsi_common.h>
 #include <scsi/scsi_tcq.h>
 
 #include <target/target_core_base.h>
index 03538994d2f7e1ee817d5b594fd33174079abaf3..40f6c13780414894a636cc43ef5b7eb6e0fea0d8 100644 (file)
@@ -33,9 +33,6 @@
 #include <linux/proc_fs.h>
 #include <linux/seq_file.h>
 #include <linux/configfs.h>
-#include <scsi/scsi.h>
-#include <scsi/scsi_device.h>
-#include <scsi/scsi_host.h>
 
 #include <target/target_core_base.h>
 #include <target/target_core_backend.h>
index 315ec3458eebc03a15a1715a065eee3dd72b9a2d..a5bb0c46e57e5c6398903141dcab6075ede4a1f8 100644 (file)
@@ -27,8 +27,6 @@
 #include <linux/spinlock.h>
 #include <linux/list.h>
 #include <linux/export.h>
-#include <scsi/scsi.h>
-#include <scsi/scsi_cmnd.h>
 
 #include <target/target_core_base.h>
 #include <target/target_core_backend.h>
index 47f064415bf661fc2bb39b27762f870c4acabc56..84de757bd4580b04b1519934e71eb24606bbd3a6 100644 (file)
@@ -32,8 +32,7 @@
 #include <linux/export.h>
 #include <net/sock.h>
 #include <net/tcp.h>
-#include <scsi/scsi.h>
-#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_proto.h>
 
 #include <target/target_core_base.h>
 #include <target/target_core_backend.h>
index 675f2d9d1f14c69142d63179afa38e5b74255243..fdf867230e188d7767e4af3e611fe71a839a4699 100644 (file)
@@ -37,9 +37,7 @@
 #include <asm/unaligned.h>
 #include <net/sock.h>
 #include <net/tcp.h>
-#include <scsi/scsi.h>
-#include <scsi/scsi_cmnd.h>
-#include <scsi/scsi_tcq.h>
+#include <scsi/scsi_proto.h>
 
 #include <target/target_core_base.h>
 #include <target/target_core_backend.h>
index 1738b164698877d984d83239250b717aba83b2d7..e44cc94b12cb1b10ae3670f053fc0456255ac515 100644 (file)
@@ -25,8 +25,7 @@
 
 #include <linux/slab.h>
 #include <linux/spinlock.h>
-#include <scsi/scsi.h>
-#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_proto.h>
 
 #include <target/target_core_base.h>
 #include <target/target_core_fabric.h>
index 07d2996d8c1fe922334ee57dfe4d27fd9d7685f8..5efef9a2a3d308a0c2e09c7372d720216b7a3226 100644 (file)
 #include <linux/spinlock.h>
 #include <linux/module.h>
 #include <linux/idr.h>
+#include <linux/kernel.h>
 #include <linux/timer.h>
 #include <linux/parser.h>
-#include <scsi/scsi.h>
-#include <scsi/scsi_host.h>
 #include <linux/uio_driver.h>
 #include <net/genetlink.h>
+#include <scsi/scsi_common.h>
+#include <scsi/scsi_proto.h>
 #include <target/target_core_base.h>
 #include <target/target_core_fabric.h>
 #include <target/target_core_backend.h>
index 8fd680ac941bde49cd7803134da5beb77c7092b0..5ec0d00edaa3412591eebd4fae6a0bd435005a1a 100644 (file)
@@ -25,8 +25,7 @@
 #include <linux/spinlock.h>
 #include <linux/list.h>
 #include <linux/configfs.h>
-#include <scsi/scsi.h>
-#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_proto.h>
 #include <asm/unaligned.h>
 
 #include <target/target_core_base.h>
index edcafa4490c0850bfc01115357ce4e817773ee72..1bf78e7c994c8ef7ec3ecc58cbd7b6a2e3dfa782 100644 (file)
 #include <linux/hash.h>
 #include <linux/percpu_ida.h>
 #include <asm/unaligned.h>
-#include <scsi/scsi.h>
-#include <scsi/scsi_host.h>
-#include <scsi/scsi_device.h>
-#include <scsi/scsi_cmnd.h>
 #include <scsi/scsi_tcq.h>
 #include <scsi/libfc.h>
 #include <scsi/fc_encode.h>
index 65dce1345966e392fe57c6bf7eadd80dc86351cd..86b699b94c7b615836ac38383554e1c83ff27af2 100644 (file)
 #include <linux/kernel.h>
 #include <linux/ctype.h>
 #include <asm/unaligned.h>
-#include <scsi/scsi.h>
-#include <scsi/scsi_host.h>
-#include <scsi/scsi_device.h>
-#include <scsi/scsi_cmnd.h>
 #include <scsi/libfc.h>
 
 #include <target/target_core_base.h>
index 583e755d809177d59953b991a9c8dd9068649205..fe585d1cce231c246150d5d639837fab68bbb09f 100644 (file)
 #include <linux/hash.h>
 #include <linux/ratelimit.h>
 #include <asm/unaligned.h>
-#include <scsi/scsi.h>
-#include <scsi/scsi_host.h>
-#include <scsi/scsi_device.h>
-#include <scsi/scsi_cmnd.h>
 #include <scsi/libfc.h>
 #include <scsi/fc_encode.h>
 
index ccee7e332a4d4900af26527d4e6d4141a679206d..f2a616d4f2c481ef568abfa5faff1a0063ebf36e 100644 (file)
 #include <linux/rculist.h>
 #include <linux/kref.h>
 #include <asm/unaligned.h>
-#include <scsi/scsi.h>
-#include <scsi/scsi_host.h>
-#include <scsi/scsi_device.h>
-#include <scsi/scsi_cmnd.h>
 #include <scsi/libfc.h>
 
 #include <target/target_core_base.h>
index 543b234e70fc33bf728f6b30b66e0ef3048e4e1d..47b54c6aefd2688bfee7c48a506f01f9a5546ec1 100644 (file)
@@ -29,6 +29,7 @@
 #include <linux/of.h>
 #include <linux/of_platform.h>
 #include <linux/export.h>
+#include <linux/interrupt.h>
 
 #include <asm/hvconsole.h>
 #include <asm/prom.h>
@@ -61,7 +62,6 @@ static struct hvc_opal_priv *hvc_opal_privs[MAX_NR_HVC_CONSOLES];
 /* For early boot console */
 static struct hvc_opal_priv hvc_opal_boot_priv;
 static u32 hvc_opal_boot_termno;
-static bool hvc_opal_event_registered;
 
 static const struct hv_ops hvc_opal_raw_ops = {
        .get_chars = opal_get_chars,
@@ -162,28 +162,15 @@ static const struct hv_ops hvc_opal_hvsi_ops = {
        .tiocmset = hvc_opal_hvsi_tiocmset,
 };
 
-static int hvc_opal_console_event(struct notifier_block *nb,
-                                 unsigned long events, void *change)
-{
-       if (events & OPAL_EVENT_CONSOLE_INPUT)
-               hvc_kick();
-       return 0;
-}
-
-static struct notifier_block hvc_opal_console_nb = {
-       .notifier_call  = hvc_opal_console_event,
-};
-
 static int hvc_opal_probe(struct platform_device *dev)
 {
        const struct hv_ops *ops;
        struct hvc_struct *hp;
        struct hvc_opal_priv *pv;
        hv_protocol_t proto;
-       unsigned int termno, boot = 0;
+       unsigned int termno, irq, boot = 0;
        const __be32 *reg;
 
-
        if (of_device_is_compatible(dev->dev.of_node, "ibm,opal-console-raw")) {
                proto = HV_PROTOCOL_RAW;
                ops = &hvc_opal_raw_ops;
@@ -227,18 +214,18 @@ static int hvc_opal_probe(struct platform_device *dev)
                dev->dev.of_node->full_name,
                boot ? " (boot console)" : "");
 
-       /* We don't do IRQ ... */
-       hp = hvc_alloc(termno, 0, ops, MAX_VIO_PUT_CHARS);
+       irq = opal_event_request(ilog2(OPAL_EVENT_CONSOLE_INPUT));
+       if (!irq) {
+               pr_err("hvc_opal: Unable to map interrupt for device %s\n",
+                       dev->dev.of_node->full_name);
+               return irq;
+       }
+
+       hp = hvc_alloc(termno, irq, ops, MAX_VIO_PUT_CHARS);
        if (IS_ERR(hp))
                return PTR_ERR(hp);
        dev_set_drvdata(&dev->dev, hp);
 
-       /* ...  but we use OPAL event to kick the console */
-       if (!hvc_opal_event_registered) {
-               opal_notifier_register(&hvc_opal_console_nb);
-               hvc_opal_event_registered = true;
-       }
-
        return 0;
 }
 
index cc57a3a6b02b348df95c827fd2c770e59ffca155..396344cb011fd1fafab05c3ddeeff1841e13e055 100644 (file)
@@ -162,6 +162,17 @@ static inline int tty_put_user(struct tty_struct *tty, unsigned char x,
        return put_user(x, ptr);
 }
 
+static inline int tty_copy_to_user(struct tty_struct *tty,
+                                       void __user *to,
+                                       const void *from,
+                                       unsigned long n)
+{
+       struct n_tty_data *ldata = tty->disc_data;
+
+       tty_audit_add_data(tty, to, n, ldata->icanon);
+       return copy_to_user(to, from, n);
+}
+
 /**
  *     n_tty_kick_worker - start input worker (if required)
  *     @tty: terminal
@@ -2070,8 +2081,8 @@ static int canon_copy_from_read_buf(struct tty_struct *tty,
 
        size = N_TTY_BUF_SIZE - tail;
        n = eol - tail;
-       if (n > 4096)
-               n += 4096;
+       if (n > N_TTY_BUF_SIZE)
+               n += N_TTY_BUF_SIZE;
        n += found;
        c = n;
 
@@ -2084,12 +2095,12 @@ static int canon_copy_from_read_buf(struct tty_struct *tty,
                    __func__, eol, found, n, c, size, more);
 
        if (n > size) {
-               ret = copy_to_user(*b, read_buf_addr(ldata, tail), size);
+               ret = tty_copy_to_user(tty, *b, read_buf_addr(ldata, tail), size);
                if (ret)
                        return -EFAULT;
-               ret = copy_to_user(*b + size, ldata->read_buf, n - size);
+               ret = tty_copy_to_user(tty, *b + size, ldata->read_buf, n - size);
        } else
-               ret = copy_to_user(*b, read_buf_addr(ldata, tail), n);
+               ret = tty_copy_to_user(tty, *b, read_buf_addr(ldata, tail), n);
 
        if (ret)
                return -EFAULT;
index 9289999cb7c62bb05b2a4b758fa76d5ce9413316..dce1a23706e86531d3caa86ba4b03c36b03bf3cf 100644 (file)
@@ -562,12 +562,36 @@ static irqreturn_t omap_wake_irq(int irq, void *dev_id)
        return IRQ_NONE;
 }
 
+#ifdef CONFIG_SERIAL_8250_DMA
+static int omap_8250_dma_handle_irq(struct uart_port *port);
+#endif
+
+static irqreturn_t omap8250_irq(int irq, void *dev_id)
+{
+       struct uart_port *port = dev_id;
+       struct uart_8250_port *up = up_to_u8250p(port);
+       unsigned int iir;
+       int ret;
+
+#ifdef CONFIG_SERIAL_8250_DMA
+       if (up->dma) {
+               ret = omap_8250_dma_handle_irq(port);
+               return IRQ_RETVAL(ret);
+       }
+#endif
+
+       serial8250_rpm_get(up);
+       iir = serial_port_in(port, UART_IIR);
+       ret = serial8250_handle_irq(port, iir);
+       serial8250_rpm_put(up);
+
+       return IRQ_RETVAL(ret);
+}
+
 static int omap_8250_startup(struct uart_port *port)
 {
-       struct uart_8250_port *up =
-               container_of(port, struct uart_8250_port, port);
+       struct uart_8250_port *up = up_to_u8250p(port);
        struct omap8250_priv *priv = port->private_data;
-
        int ret;
 
        if (priv->wakeirq) {
@@ -580,10 +604,31 @@ static int omap_8250_startup(struct uart_port *port)
 
        pm_runtime_get_sync(port->dev);
 
-       ret = serial8250_do_startup(port);
-       if (ret)
+       up->mcr = 0;
+       serial_out(up, UART_FCR, UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
+
+       serial_out(up, UART_LCR, UART_LCR_WLEN8);
+
+       up->lsr_saved_flags = 0;
+       up->msr_saved_flags = 0;
+
+       if (up->dma) {
+               ret = serial8250_request_dma(up);
+               if (ret) {
+                       dev_warn_ratelimited(port->dev,
+                                            "failed to request DMA\n");
+                       up->dma = NULL;
+               }
+       }
+
+       ret = request_irq(port->irq, omap8250_irq, IRQF_SHARED,
+                         dev_name(port->dev), port);
+       if (ret < 0)
                goto err;
 
+       up->ier = UART_IER_RLSI | UART_IER_RDI;
+       serial_out(up, UART_IER, up->ier);
+
 #ifdef CONFIG_PM
        up->capabilities |= UART_CAP_RPM;
 #endif
@@ -610,8 +655,7 @@ err:
 
 static void omap_8250_shutdown(struct uart_port *port)
 {
-       struct uart_8250_port *up =
-               container_of(port, struct uart_8250_port, port);
+       struct uart_8250_port *up = up_to_u8250p(port);
        struct omap8250_priv *priv = port->private_data;
 
        flush_work(&priv->qos_work);
@@ -621,11 +665,24 @@ static void omap_8250_shutdown(struct uart_port *port)
        pm_runtime_get_sync(port->dev);
 
        serial_out(up, UART_OMAP_WER, 0);
-       serial8250_do_shutdown(port);
+
+       up->ier = 0;
+       serial_out(up, UART_IER, 0);
+
+       if (up->dma)
+               serial8250_release_dma(up);
+
+       /*
+        * Disable break condition and FIFOs
+        */
+       if (up->lcr & UART_LCR_SBC)
+               serial_out(up, UART_LCR, up->lcr & ~UART_LCR_SBC);
+       serial_out(up, UART_FCR, UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
 
        pm_runtime_mark_last_busy(port->dev);
        pm_runtime_put_autosuspend(port->dev);
 
+       free_irq(port->irq, port);
        if (priv->wakeirq)
                free_irq(priv->wakeirq, port);
 }
@@ -974,6 +1031,13 @@ static inline int omap_8250_rx_dma(struct uart_8250_port *p, unsigned int iir)
 }
 #endif
 
+static int omap8250_no_handle_irq(struct uart_port *port)
+{
+       /* IRQ has not been requested but handling irq? */
+       WARN_ONCE(1, "Unexpected irq handling before port startup\n");
+       return 0;
+}
+
 static int omap8250_probe(struct platform_device *pdev)
 {
        struct resource *regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -1075,6 +1139,7 @@ static int omap8250_probe(struct platform_device *pdev)
        pm_runtime_get_sync(&pdev->dev);
 
        omap_serial_fill_features_erratas(&up, priv);
+       up.port.handle_irq = omap8250_no_handle_irq;
 #ifdef CONFIG_SERIAL_8250_DMA
        if (pdev->dev.of_node) {
                /*
@@ -1088,7 +1153,6 @@ static int omap8250_probe(struct platform_device *pdev)
                ret = of_property_count_strings(pdev->dev.of_node, "dma-names");
                if (ret == 2) {
                        up.dma = &priv->omap8250_dma;
-                       up.port.handle_irq = omap_8250_dma_handle_irq;
                        priv->omap8250_dma.fn = the_no_dma_filter_fn;
                        priv->omap8250_dma.tx_dma = omap_8250_tx_dma;
                        priv->omap8250_dma.rx_dma = omap_8250_rx_dma;
index 6f5a0720a8c8eead6c23f37c359c516730013cef..763eb20fe3213b6cfda04dc2624bcd1b8638f324 100644 (file)
@@ -1249,20 +1249,19 @@ __acquires(&uap->port.lock)
 
 /*
  * Transmit a character
- * There must be at least one free entry in the TX FIFO to accept the char.
  *
- * Returns true if the FIFO might have space in it afterwards;
- * returns false if the FIFO definitely became full.
+ * Returns true if the character was successfully queued to the FIFO.
+ * Returns false otherwise.
  */
 static bool pl011_tx_char(struct uart_amba_port *uap, unsigned char c)
 {
+       if (readw(uap->port.membase + UART01x_FR) & UART01x_FR_TXFF)
+               return false; /* unable to transmit character */
+
        writew(c, uap->port.membase + UART01x_DR);
        uap->port.icount.tx++;
 
-       if (likely(uap->tx_irq_seen > 1))
-               return true;
-
-       return !(readw(uap->port.membase + UART01x_FR) & UART01x_FR_TXFF);
+       return true;
 }
 
 static bool pl011_tx_chars(struct uart_amba_port *uap)
@@ -1296,7 +1295,8 @@ static bool pl011_tx_chars(struct uart_amba_port *uap)
                return false;
 
        if (uap->port.x_char) {
-               pl011_tx_char(uap, uap->port.x_char);
+               if (!pl011_tx_char(uap, uap->port.x_char))
+                       goto done;
                uap->port.x_char = 0;
                --count;
        }
index c8cfa06371280af6abfd63bd379ee5c121523ad7..88250395b0ce96486a2dac5e2e9162fb7f4eae43 100644 (file)
@@ -911,6 +911,14 @@ static void dma_rx_callback(void *data)
 
        status = dmaengine_tx_status(chan, (dma_cookie_t)0, &state);
        count = RX_BUF_SIZE - state.residue;
+
+       if (readl(sport->port.membase + USR2) & USR2_IDLE) {
+               /* In condition [3] the SDMA counted up too early */
+               count--;
+
+               writel(USR2_IDLE, sport->port.membase + USR2);
+       }
+
        dev_dbg(sport->port.dev, "We get %d bytes.\n", count);
 
        if (count) {
index 0ec756c62bcf1f859a7692c9cf2fe580cff8b56f..d7b846d416309a00401d396d1a6e5f7870fca975 100644 (file)
@@ -55,7 +55,7 @@ void mctrl_gpio_set(struct mctrl_gpios *gpios, unsigned int mctrl)
                        value_array[count] = !!(mctrl & mctrl_gpios_desc[i].mctrl);
                        count++;
                }
-       gpiod_set_array(count, desc_array, value_array);
+       gpiod_set_array_value(count, desc_array, value_array);
 }
 EXPORT_SYMBOL_GPL(mctrl_gpio_set);
 
index fdab715a063119d6e696a8f66ea26d4a1613e983..c0eafa6fd40314086474f5b7cab8f63361c73d64 100644 (file)
 #define DWC3_DGCMD_SET_ENDPOINT_NRDY   0x0c
 #define DWC3_DGCMD_RUN_SOC_BUS_LOOPBACK        0x10
 
-#define DWC3_DGCMD_STATUS(n)           (((n) >> 15) & 1)
+#define DWC3_DGCMD_STATUS(n)           (((n) >> 12) & 0x0F)
 #define DWC3_DGCMD_CMDACT              (1 << 10)
 #define DWC3_DGCMD_CMDIOC              (1 << 8)
 
 #define DWC3_DEPCMD_PARAM_SHIFT                16
 #define DWC3_DEPCMD_PARAM(x)           ((x) << DWC3_DEPCMD_PARAM_SHIFT)
 #define DWC3_DEPCMD_GET_RSC_IDX(x)     (((x) >> DWC3_DEPCMD_PARAM_SHIFT) & 0x7f)
-#define DWC3_DEPCMD_STATUS(x)          (((x) >> 15) & 1)
+#define DWC3_DEPCMD_STATUS(x)          (((x) >> 12) & 0x0F)
 #define DWC3_DEPCMD_HIPRI_FORCERM      (1 << 11)
 #define DWC3_DEPCMD_CMDACT             (1 << 10)
 #define DWC3_DEPCMD_CMDIOC             (1 << 8)
index 6bdb5706904497ca9eccb7fd5d979c67824d8600..3507f880eb74294c76ddbc43c3aa153528478f53 100644 (file)
@@ -315,7 +315,6 @@ static ssize_t ffs_ep0_write(struct file *file, const char __user *buf,
                                return ret;
                        }
 
-                       set_bit(FFS_FL_CALL_CLOSED_CALLBACK, &ffs->flags);
                        return len;
                }
                break;
@@ -847,7 +846,7 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data)
                                ret = ep->status;
                                if (io_data->read && ret > 0) {
                                        ret = copy_to_iter(data, ret, &io_data->data);
-                                       if (unlikely(iov_iter_count(&io_data->data)))
+                                       if (!ret)
                                                ret = -EFAULT;
                                }
                        }
@@ -1463,8 +1462,7 @@ static void ffs_data_clear(struct ffs_data *ffs)
 {
        ENTER();
 
-       if (test_and_clear_bit(FFS_FL_CALL_CLOSED_CALLBACK, &ffs->flags))
-               ffs_closed(ffs);
+       ffs_closed(ffs);
 
        BUG_ON(ffs->gadget);
 
@@ -3422,9 +3420,13 @@ static int ffs_ready(struct ffs_data *ffs)
        ffs_obj->desc_ready = true;
        ffs_obj->ffs_data = ffs;
 
-       if (ffs_obj->ffs_ready_callback)
+       if (ffs_obj->ffs_ready_callback) {
                ret = ffs_obj->ffs_ready_callback(ffs);
+               if (ret)
+                       goto done;
+       }
 
+       set_bit(FFS_FL_CALL_CLOSED_CALLBACK, &ffs->flags);
 done:
        ffs_dev_unlock();
        return ret;
@@ -3443,7 +3445,8 @@ static void ffs_closed(struct ffs_data *ffs)
 
        ffs_obj->desc_ready = false;
 
-       if (ffs_obj->ffs_closed_callback)
+       if (test_and_clear_bit(FFS_FL_CALL_CLOSED_CALLBACK, &ffs->flags) &&
+           ffs_obj->ffs_closed_callback)
                ffs_obj->ffs_closed_callback(ffs);
 
        if (!ffs_obj->opts || ffs_obj->opts->no_configfs
index 259b656c0b3ec7bde9e119488f46ded351bb7300..6316aa5b1c4947a6df2e08b4c45856dc77b94374 100644 (file)
@@ -973,7 +973,13 @@ static ssize_t f_midi_opts_id_show(struct f_midi_opts *opts, char *page)
        int result;
 
        mutex_lock(&opts->lock);
-       result = strlcpy(page, opts->id, PAGE_SIZE);
+       if (opts->id) {
+               result = strlcpy(page, opts->id, PAGE_SIZE);
+       } else {
+               page[0] = 0;
+               result = 0;
+       }
+
        mutex_unlock(&opts->lock);
 
        return result;
index 9719abfb61455ca91ec5d1721e53622d4b76f1ef..7856b3394494b7d4250637277dd1f42f45d7a1ea 100644 (file)
@@ -588,7 +588,10 @@ static int f_audio_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
 
        if (intf == 1) {
                if (alt == 1) {
-                       config_ep_by_speed(cdev->gadget, f, out_ep);
+                       err = config_ep_by_speed(cdev->gadget, f, out_ep);
+                       if (err)
+                               return err;
+
                        usb_ep_enable(out_ep);
                        out_ep->driver_data = audio;
                        audio->copy_buf = f_audio_buffer_alloc(audio_buf_size);
index 7b9ef7e257d236dd442226203301a59bbd59ef47..e821931c965cd9203a8011358ffeb16844dc7eed 100644 (file)
@@ -304,8 +304,10 @@ static int functionfs_ready_callback(struct ffs_data *ffs)
        gfs_registered = true;
 
        ret = usb_composite_probe(&gfs_driver);
-       if (unlikely(ret < 0))
+       if (unlikely(ret < 0)) {
+               ++missing_funcs;
                gfs_registered = false;
+       }
        
        return ret;
 }
index f9b4882fce528f7cd6fa04ec66d90109a3a12047..6ce932f90ef84ee961eee50dd95ec4cc83bfacad 100644 (file)
@@ -16,7 +16,6 @@
 #include <linux/usb/composite.h>
 #include <linux/usb/gadget.h>
 #include <linux/usb/storage.h>
-#include <scsi/scsi.h>
 #include <scsi/scsi_tcq.h>
 #include <target/target_core_base.h>
 #include <target/target_core_fabric.h>
index 8289219925b87dd7b99bedd3f5c2b4205f401862..9fb3544cc80f088d547d264945944043508418e3 100644 (file)
@@ -6,7 +6,6 @@
 #include <linux/usb/composite.h>
 #include <linux/usb/uas.h>
 #include <linux/usb/storage.h>
-#include <scsi/scsi.h>
 #include <target/target_core_base.h>
 #include <target/target_core_fabric.h>
 
index b808951491ccbfcdd949d8e78f7c1cc2b4c55f47..99fd9a5667dfd4997092d982c0beae28b578a17c 100644 (file)
@@ -1487,7 +1487,7 @@ static int s3c2410_udc_pullup(struct usb_gadget *gadget, int is_on)
 
        dprintk(DEBUG_NORMAL, "%s()\n", __func__);
 
-       s3c2410_udc_set_pullup(udc, is_on ? 0 : 1);
+       s3c2410_udc_set_pullup(udc, is_on);
        return 0;
 }
 
index ec8ac16748547a2ac87bf9aa225ed0a36c0bf7df..36bf089b708fe5219258d46305719b7a999a23f6 100644 (file)
@@ -3682,18 +3682,21 @@ int xhci_alloc_dev(struct usb_hcd *hcd, struct usb_device *udev)
 {
        struct xhci_hcd *xhci = hcd_to_xhci(hcd);
        unsigned long flags;
-       int ret;
+       int ret, slot_id;
        struct xhci_command *command;
 
        command = xhci_alloc_command(xhci, false, false, GFP_KERNEL);
        if (!command)
                return 0;
 
+       /* xhci->slot_id and xhci->addr_dev are not thread-safe */
+       mutex_lock(&xhci->mutex);
        spin_lock_irqsave(&xhci->lock, flags);
        command->completion = &xhci->addr_dev;
        ret = xhci_queue_slot_control(xhci, command, TRB_ENABLE_SLOT, 0);
        if (ret) {
                spin_unlock_irqrestore(&xhci->lock, flags);
+               mutex_unlock(&xhci->mutex);
                xhci_dbg(xhci, "FIXME: allocate a command ring segment\n");
                kfree(command);
                return 0;
@@ -3702,8 +3705,10 @@ int xhci_alloc_dev(struct usb_hcd *hcd, struct usb_device *udev)
        spin_unlock_irqrestore(&xhci->lock, flags);
 
        wait_for_completion(command->completion);
+       slot_id = xhci->slot_id;
+       mutex_unlock(&xhci->mutex);
 
-       if (!xhci->slot_id || command->status != COMP_SUCCESS) {
+       if (!slot_id || command->status != COMP_SUCCESS) {
                xhci_err(xhci, "Error while assigning device slot ID\n");
                xhci_err(xhci, "Max number of devices this xHCI host supports is %u.\n",
                                HCS_MAX_SLOTS(
@@ -3728,11 +3733,11 @@ int xhci_alloc_dev(struct usb_hcd *hcd, struct usb_device *udev)
         * xhci_discover_or_reset_device(), which may be called as part of
         * mass storage driver error handling.
         */
-       if (!xhci_alloc_virt_device(xhci, xhci->slot_id, udev, GFP_NOIO)) {
+       if (!xhci_alloc_virt_device(xhci, slot_id, udev, GFP_NOIO)) {
                xhci_warn(xhci, "Could not allocate xHCI USB device data structures\n");
                goto disable_slot;
        }
-       udev->slot_id = xhci->slot_id;
+       udev->slot_id = slot_id;
 
 #ifndef CONFIG_USB_DEFAULT_PERSIST
        /*
@@ -3778,12 +3783,15 @@ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev,
        struct xhci_slot_ctx *slot_ctx;
        struct xhci_input_control_ctx *ctrl_ctx;
        u64 temp_64;
-       struct xhci_command *command;
+       struct xhci_command *command = NULL;
+
+       mutex_lock(&xhci->mutex);
 
        if (!udev->slot_id) {
                xhci_dbg_trace(xhci, trace_xhci_dbg_address,
                                "Bad Slot ID %d", udev->slot_id);
-               return -EINVAL;
+               ret = -EINVAL;
+               goto out;
        }
 
        virt_dev = xhci->devs[udev->slot_id];
@@ -3796,7 +3804,8 @@ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev,
                 */
                xhci_warn(xhci, "Virt dev invalid for slot_id 0x%x!\n",
                        udev->slot_id);
-               return -EINVAL;
+               ret = -EINVAL;
+               goto out;
        }
 
        if (setup == SETUP_CONTEXT_ONLY) {
@@ -3804,13 +3813,15 @@ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev,
                if (GET_SLOT_STATE(le32_to_cpu(slot_ctx->dev_state)) ==
                    SLOT_STATE_DEFAULT) {
                        xhci_dbg(xhci, "Slot already in default state\n");
-                       return 0;
+                       goto out;
                }
        }
 
        command = xhci_alloc_command(xhci, false, false, GFP_KERNEL);
-       if (!command)
-               return -ENOMEM;
+       if (!command) {
+               ret = -ENOMEM;
+               goto out;
+       }
 
        command->in_ctx = virt_dev->in_ctx;
        command->completion = &xhci->addr_dev;
@@ -3820,8 +3831,8 @@ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev,
        if (!ctrl_ctx) {
                xhci_warn(xhci, "%s: Could not get input context, bad type.\n",
                                __func__);
-               kfree(command);
-               return -EINVAL;
+               ret = -EINVAL;
+               goto out;
        }
        /*
         * If this is the first Set Address since device plug-in or
@@ -3848,8 +3859,7 @@ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev,
                spin_unlock_irqrestore(&xhci->lock, flags);
                xhci_dbg_trace(xhci, trace_xhci_dbg_address,
                                "FIXME: allocate a command ring segment");
-               kfree(command);
-               return ret;
+               goto out;
        }
        xhci_ring_cmd_db(xhci);
        spin_unlock_irqrestore(&xhci->lock, flags);
@@ -3896,10 +3906,8 @@ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev,
                ret = -EINVAL;
                break;
        }
-       if (ret) {
-               kfree(command);
-               return ret;
-       }
+       if (ret)
+               goto out;
        temp_64 = xhci_read_64(xhci, &xhci->op_regs->dcbaa_ptr);
        xhci_dbg_trace(xhci, trace_xhci_dbg_address,
                        "Op regs DCBAA ptr = %#016llx", temp_64);
@@ -3932,8 +3940,10 @@ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev,
        xhci_dbg_trace(xhci, trace_xhci_dbg_address,
                       "Internal device address = %d",
                       le32_to_cpu(slot_ctx->dev_state) & DEV_ADDR_MASK);
+out:
+       mutex_unlock(&xhci->mutex);
        kfree(command);
-       return 0;
+       return ret;
 }
 
 int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev)
@@ -4855,6 +4865,7 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks)
                return 0;
        }
 
+       mutex_init(&xhci->mutex);
        xhci->cap_regs = hcd->regs;
        xhci->op_regs = hcd->regs +
                HC_LENGTH(readl(&xhci->cap_regs->hc_capbase));
@@ -5011,4 +5022,12 @@ static int __init xhci_hcd_init(void)
        BUILD_BUG_ON(sizeof(struct xhci_run_regs) != (8+8*128)*32/8);
        return 0;
 }
+
+/*
+ * If an init function is provided, an exit function must also be provided
+ * to allow module unload.
+ */
+static void __exit xhci_hcd_fini(void) { }
+
 module_init(xhci_hcd_init);
+module_exit(xhci_hcd_fini);
index ea75e8ccd3c11d397dc7a6a2ff45e78ae829fd81..6977f8491fa7ced6ea317bf75354a0eb7703670e 100644 (file)
@@ -1497,6 +1497,8 @@ struct xhci_hcd {
        struct list_head        lpm_failed_devs;
 
        /* slot enabling and address device helpers */
+       /* these are not thread safe so use mutex */
+       struct mutex mutex;
        struct completion       addr_dev;
        int slot_id;
        /* For USB 3.0 LPM enable/disable. */
index 6431d08c8d9dea8d401400fe21ccf9cf5ce41e14..a4dbb0cd80da191b2d5e7e541c007f2fd1b23b76 100644 (file)
@@ -635,7 +635,6 @@ static struct scsi_host_template mts_scsi_host_template = {
        .sg_tablesize =         SG_ALL,
        .can_queue =            1,
        .this_id =              -1,
-       .cmd_per_lun =          1,
        .use_clustering =       1,
        .emulated =             1,
        .slave_alloc =          mts_slave_alloc,
index 82503a7ff6c826e6044696ea8645d9e4a9bd4580..cce22ff1c2eb4249ac02f011bca5be154a8ad927 100644 (file)
 #define USB_DEVICE_ID_LD_HYBRID                0x2090  /* USB Product ID of Automotive Hybrid */
 #define USB_DEVICE_ID_LD_HEATCONTROL   0x20A0  /* USB Product ID of Heat control */
 
-#define USB_VENDOR_ID_VERNIER          0x08f7
-#define USB_DEVICE_ID_VERNIER_GOTEMP   0x0002
-#define USB_DEVICE_ID_VERNIER_SKIP     0x0003
-#define USB_DEVICE_ID_VERNIER_CYCLOPS  0x0004
-#define USB_DEVICE_ID_VERNIER_LCSPEC   0x0006
-
 #ifdef CONFIG_USB_DYNAMIC_MINORS
 #define USB_LD_MINOR_BASE      0
 #else
@@ -115,10 +109,6 @@ static const struct usb_device_id ld_usb_table[] = {
        { USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MCT) },
        { USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_HYBRID) },
        { USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_HEATCONTROL) },
-       { USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_GOTEMP) },
-       { USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_SKIP) },
-       { USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_CYCLOPS) },
-       { USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_LCSPEC) },
        { }                                     /* Terminating entry */
 };
 MODULE_DEVICE_TABLE(usb, ld_usb_table);
index 3789b08ef67b037781e278c41c0d4b2f2d33e5d9..6dca3d794ced6e1948dd5cbb180e708893f7ba83 100644 (file)
@@ -2021,13 +2021,7 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl)
        if (musb->ops->quirks)
                musb->io.quirks = musb->ops->quirks;
 
-       /* At least tusb6010 has it's own offsets.. */
-       if (musb->ops->ep_offset)
-               musb->io.ep_offset = musb->ops->ep_offset;
-       if (musb->ops->ep_select)
-               musb->io.ep_select = musb->ops->ep_select;
-
-       /* ..and some devices use indexed offset or flat offset */
+       /* Most devices use indexed offset or flat offset */
        if (musb->io.quirks & MUSB_INDEXED_EP) {
                musb->io.ep_offset = musb_indexed_ep_offset;
                musb->io.ep_select = musb_indexed_ep_select;
@@ -2036,6 +2030,12 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl)
                musb->io.ep_select = musb_flat_ep_select;
        }
 
+       /* At least tusb6010 has its own offsets */
+       if (musb->ops->ep_offset)
+               musb->io.ep_offset = musb->ops->ep_offset;
+       if (musb->ops->ep_select)
+               musb->io.ep_select = musb->ops->ep_select;
+
        if (musb->ops->fifo_mode)
                fifo_mode = musb->ops->fifo_mode;
        else
index 7225d526df0446ff26fd69ef65268265737d8c66..03ab0c699f74dd1768f2b769ca823eb7904132ab 100644 (file)
@@ -1179,7 +1179,7 @@ static int ab8500_usb_irq_setup(struct platform_device *pdev,
                }
                err = devm_request_threaded_irq(&pdev->dev, irq, NULL,
                                ab8500_usb_link_status_irq,
-                               IRQF_NO_SUSPEND | IRQF_SHARED,
+                               IRQF_NO_SUSPEND | IRQF_SHARED | IRQF_ONESHOT,
                                "usb-link-status", ab);
                if (err < 0) {
                        dev_err(ab->dev, "request_irq failed for link status irq\n");
@@ -1195,7 +1195,7 @@ static int ab8500_usb_irq_setup(struct platform_device *pdev,
                }
                err = devm_request_threaded_irq(&pdev->dev, irq, NULL,
                                ab8500_usb_disconnect_irq,
-                               IRQF_NO_SUSPEND | IRQF_SHARED,
+                               IRQF_NO_SUSPEND | IRQF_SHARED | IRQF_ONESHOT,
                                "usb-id-fall", ab);
                if (err < 0) {
                        dev_err(ab->dev, "request_irq failed for ID fall irq\n");
@@ -1211,7 +1211,7 @@ static int ab8500_usb_irq_setup(struct platform_device *pdev,
                }
                err = devm_request_threaded_irq(&pdev->dev, irq, NULL,
                                ab8500_usb_disconnect_irq,
-                               IRQF_NO_SUSPEND | IRQF_SHARED,
+                               IRQF_NO_SUSPEND | IRQF_SHARED | IRQF_ONESHOT,
                                "usb-vbus-fall", ab);
                if (err < 0) {
                        dev_err(ab->dev, "request_irq failed for Vbus fall irq\n");
index 845f658276b106342907c7606a078dbfa47d06d1..2b28443d07b92daed26660f1d80f0bd390937992 100644 (file)
@@ -401,7 +401,8 @@ static int tahvo_usb_probe(struct platform_device *pdev)
        dev_set_drvdata(&pdev->dev, tu);
 
        tu->irq = platform_get_irq(pdev, 0);
-       ret = request_threaded_irq(tu->irq, NULL, tahvo_usb_vbus_interrupt, 0,
+       ret = request_threaded_irq(tu->irq, NULL, tahvo_usb_vbus_interrupt,
+                                  IRQF_ONESHOT,
                                   "tahvo-vbus", tu);
        if (ret) {
                dev_err(&pdev->dev, "could not register tahvo-vbus irq: %d\n",
index 8597cf9cfceb7715883738ac8cf1c0380e9a00b1..c0f5c652d272c8959f5b3d59461e1af139d6f7fd 100644 (file)
@@ -611,6 +611,8 @@ struct usbhs_pkt_handle usbhs_fifo_pio_push_handler = {
 static int usbhsf_prepare_pop(struct usbhs_pkt *pkt, int *is_done)
 {
        struct usbhs_pipe *pipe = pkt->pipe;
+       struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
+       struct usbhs_fifo *fifo = usbhsf_get_cfifo(priv);
 
        if (usbhs_pipe_is_busy(pipe))
                return 0;
@@ -624,6 +626,9 @@ static int usbhsf_prepare_pop(struct usbhs_pkt *pkt, int *is_done)
        usbhs_pipe_data_sequence(pipe, pkt->sequence);
        pkt->sequence = -1; /* -1 sequence will be ignored */
 
+       if (usbhs_pipe_is_dcp(pipe))
+               usbhsf_fifo_clear(pipe, fifo);
+
        usbhs_pipe_set_trans_count_if_bulk(pipe, pkt->length);
        usbhs_pipe_enable(pipe);
        usbhs_pipe_running(pipe, 1);
@@ -673,7 +678,14 @@ static int usbhsf_pio_try_pop(struct usbhs_pkt *pkt, int *is_done)
                *is_done = 1;
                usbhsf_rx_irq_ctrl(pipe, 0);
                usbhs_pipe_running(pipe, 0);
-               usbhs_pipe_disable(pipe);       /* disable pipe first */
+               /*
+                * If function mode, since this controller is possible to enter
+                * Control Write status stage at this timing, this driver
+                * should not disable the pipe. If such a case happens, this
+                * controller is not able to complete the status stage.
+                */
+               if (!usbhs_mod_is_host(priv) && !usbhs_pipe_is_dcp(pipe))
+                       usbhs_pipe_disable(pipe);       /* disable pipe first */
        }
 
        /*
@@ -1227,15 +1239,21 @@ static void usbhsf_dma_init_dt(struct device *dev, struct usbhs_fifo *fifo,
 {
        char name[16];
 
-       snprintf(name, sizeof(name), "tx%d", channel);
-       fifo->tx_chan = dma_request_slave_channel_reason(dev, name);
-       if (IS_ERR(fifo->tx_chan))
-               fifo->tx_chan = NULL;
-
-       snprintf(name, sizeof(name), "rx%d", channel);
-       fifo->rx_chan = dma_request_slave_channel_reason(dev, name);
-       if (IS_ERR(fifo->rx_chan))
-               fifo->rx_chan = NULL;
+       /*
+        * To avoid complex handing for DnFIFOs, the driver uses each
+        * DnFIFO as TX or RX direction (not bi-direction).
+        * So, the driver uses odd channels for TX, even channels for RX.
+        */
+       snprintf(name, sizeof(name), "ch%d", channel);
+       if (channel & 1) {
+               fifo->tx_chan = dma_request_slave_channel_reason(dev, name);
+               if (IS_ERR(fifo->tx_chan))
+                       fifo->tx_chan = NULL;
+       } else {
+               fifo->rx_chan = dma_request_slave_channel_reason(dev, name);
+               if (IS_ERR(fifo->rx_chan))
+                       fifo->rx_chan = NULL;
+       }
 }
 
 static void usbhsf_dma_init(struct usbhs_priv *priv, struct usbhs_fifo *fifo,
index b7cf1982d1d94055b5eade551dcc59fe6e353aaf..56ecb8b5115dd66b8d0db52849e9738e127a2afa 100644 (file)
@@ -171,7 +171,7 @@ config USB_SERIAL_FTDI_SIO
        ---help---
          Say Y here if you want to use a FTDI SIO single port USB to serial
          converter device. The implementation I have is called the USC-1000.
-         This driver has also be tested with the 245 and 232 devices.
+         This driver has also been tested with the 245 and 232 devices.
 
          See <http://ftdi-usb-sio.sourceforge.net/> for more
          information on this driver and the device.
index 9031750e7404a566d3c08c30e0366c424dcd0b06..ffd739e31bfc193b058628560e86ea6f9b96f375 100644 (file)
@@ -128,6 +128,7 @@ static const struct usb_device_id id_table[] = {
        { USB_DEVICE(0x10C4, 0x8946) }, /* Ketra N1 Wireless Interface */
        { USB_DEVICE(0x10C4, 0x8977) }, /* CEL MeshWorks DevKit Device */
        { USB_DEVICE(0x10C4, 0x8998) }, /* KCF Technologies PRN */
+       { USB_DEVICE(0x10C4, 0x8A2A) }, /* HubZ dual ZigBee and Z-Wave dongle */
        { USB_DEVICE(0x10C4, 0xEA60) }, /* Silicon Labs factory default */
        { USB_DEVICE(0x10C4, 0xEA61) }, /* Silicon Labs factory default */
        { USB_DEVICE(0x10C4, 0xEA70) }, /* Silicon Labs factory default */
index 8eb68a31cab6c4021617ca555cd58b086872c112..4c8b3b82103d6318ea1d46250ad708bb3f722260 100644 (file)
@@ -699,6 +699,7 @@ static const struct usb_device_id id_table_combined[] = {
        { USB_DEVICE(XSENS_VID, XSENS_AWINDA_DONGLE_PID) },
        { USB_DEVICE(XSENS_VID, XSENS_AWINDA_STATION_PID) },
        { USB_DEVICE(XSENS_VID, XSENS_CONVERTER_PID) },
+       { USB_DEVICE(XSENS_VID, XSENS_MTDEVBOARD_PID) },
        { USB_DEVICE(XSENS_VID, XSENS_MTW_PID) },
        { USB_DEVICE(FTDI_VID, FTDI_OMNI1509) },
        { USB_DEVICE(MOBILITY_VID, MOBILITY_USB_SERIAL_PID) },
index 4e4f46f3c89c025670d42860756f39b2bb62ae24..792e054126de51402711814f5962945f7742e188 100644 (file)
 #define XSENS_AWINDA_STATION_PID 0x0101
 #define XSENS_AWINDA_DONGLE_PID 0x0102
 #define XSENS_MTW_PID          0x0200  /* Xsens MTw */
+#define XSENS_MTDEVBOARD_PID   0x0300  /* Motion Tracker Development Board */
 #define XSENS_CONVERTER_PID    0xD00D  /* Xsens USB-serial converter */
 
 /* Xsens devices using FTDI VID */
index 0e400f382f3a8aea1f56f568e623b7fecfbf7de9..996ef1e882d386d24f5ef526b31a360269eeeec1 100644 (file)
@@ -558,7 +558,6 @@ struct scsi_host_template usb_stor_host_template = {
 
        /* queue commands only, only one command per LUN */
        .can_queue =                    1,
-       .cmd_per_lun =                  1,
 
        /* unknown initiator id */
        .this_id =                      -1,
index 6d3122afeed33e9cfe1b571c3acaf19dac967da4..f689219095526a76076010eee2d1e3df82fa8e73 100644 (file)
@@ -811,7 +811,6 @@ static struct scsi_host_template uas_host_template = {
        .can_queue = 65536,     /* Is there a limit on the _host_ ? */
        .this_id = -1,
        .sg_tablesize = SG_NONE,
-       .cmd_per_lun = 1,       /* until we override it */
        .skip_settle_delay = 1,
        .use_blk_tags = 1,
 };
index 7d092ddc81195814ca0409fdcc19770832f35086..454017928ed030a776d2aefd84ac661f829b05b9 100644 (file)
@@ -21,7 +21,7 @@ config VFIO_VIRQFD
 menuconfig VFIO
        tristate "VFIO Non-Privileged userspace driver framework"
        depends on IOMMU_API
-       select VFIO_IOMMU_TYPE1 if (X86 || S390 || ARM_SMMU)
+       select VFIO_IOMMU_TYPE1 if (X86 || S390 || ARM_SMMU || ARM_SMMU_V3)
        select VFIO_IOMMU_SPAPR_TCE if (PPC_POWERNV || PPC_PSERIES)
        select VFIO_SPAPR_EEH if (PPC_POWERNV || PPC_PSERIES)
        select ANON_INODES
index 730b4ef3e0cc3543382930456f923135858cb181..0582b72ef3772cf1257cc330dcf5e32246fe72bb 100644 (file)
 #include <linux/uaccess.h>
 #include <linux/err.h>
 #include <linux/vfio.h>
+#include <linux/vmalloc.h>
 #include <asm/iommu.h>
 #include <asm/tce.h>
+#include <asm/mmu_context.h>
 
 #define DRIVER_VERSION  "0.1"
 #define DRIVER_AUTHOR   "aik@ozlabs.ru"
 static void tce_iommu_detach_group(void *iommu_data,
                struct iommu_group *iommu_group);
 
+static long try_increment_locked_vm(long npages)
+{
+       long ret = 0, locked, lock_limit;
+
+       if (!current || !current->mm)
+               return -ESRCH; /* process exited */
+
+       if (!npages)
+               return 0;
+
+       down_write(&current->mm->mmap_sem);
+       locked = current->mm->locked_vm + npages;
+       lock_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT;
+       if (locked > lock_limit && !capable(CAP_IPC_LOCK))
+               ret = -ENOMEM;
+       else
+               current->mm->locked_vm += npages;
+
+       pr_debug("[%d] RLIMIT_MEMLOCK +%ld %ld/%ld%s\n", current->pid,
+                       npages << PAGE_SHIFT,
+                       current->mm->locked_vm << PAGE_SHIFT,
+                       rlimit(RLIMIT_MEMLOCK),
+                       ret ? " - exceeded" : "");
+
+       up_write(&current->mm->mmap_sem);
+
+       return ret;
+}
+
+static void decrement_locked_vm(long npages)
+{
+       if (!current || !current->mm || !npages)
+               return; /* process exited */
+
+       down_write(&current->mm->mmap_sem);
+       if (WARN_ON_ONCE(npages > current->mm->locked_vm))
+               npages = current->mm->locked_vm;
+       current->mm->locked_vm -= npages;
+       pr_debug("[%d] RLIMIT_MEMLOCK -%ld %ld/%ld\n", current->pid,
+                       npages << PAGE_SHIFT,
+                       current->mm->locked_vm << PAGE_SHIFT,
+                       rlimit(RLIMIT_MEMLOCK));
+       up_write(&current->mm->mmap_sem);
+}
+
 /*
  * VFIO IOMMU fd for SPAPR_TCE IOMMU implementation
  *
@@ -36,6 +83,11 @@ static void tce_iommu_detach_group(void *iommu_data,
  * into DMA'ble space using the IOMMU
  */
 
+struct tce_iommu_group {
+       struct list_head next;
+       struct iommu_group *grp;
+};
+
 /*
  * The container descriptor supports only a single group per container.
  * Required by the API as the container is not supplied with the IOMMU group
@@ -43,18 +95,140 @@ static void tce_iommu_detach_group(void *iommu_data,
  */
 struct tce_container {
        struct mutex lock;
-       struct iommu_table *tbl;
        bool enabled;
+       bool v2;
+       unsigned long locked_pages;
+       struct iommu_table *tables[IOMMU_TABLE_GROUP_MAX_TABLES];
+       struct list_head group_list;
 };
 
+static long tce_iommu_unregister_pages(struct tce_container *container,
+               __u64 vaddr, __u64 size)
+{
+       struct mm_iommu_table_group_mem_t *mem;
+
+       if ((vaddr & ~PAGE_MASK) || (size & ~PAGE_MASK))
+               return -EINVAL;
+
+       mem = mm_iommu_find(vaddr, size >> PAGE_SHIFT);
+       if (!mem)
+               return -ENOENT;
+
+       return mm_iommu_put(mem);
+}
+
+static long tce_iommu_register_pages(struct tce_container *container,
+               __u64 vaddr, __u64 size)
+{
+       long ret = 0;
+       struct mm_iommu_table_group_mem_t *mem = NULL;
+       unsigned long entries = size >> PAGE_SHIFT;
+
+       if ((vaddr & ~PAGE_MASK) || (size & ~PAGE_MASK) ||
+                       ((vaddr + size) < vaddr))
+               return -EINVAL;
+
+       ret = mm_iommu_get(vaddr, entries, &mem);
+       if (ret)
+               return ret;
+
+       container->enabled = true;
+
+       return 0;
+}
+
+static long tce_iommu_userspace_view_alloc(struct iommu_table *tbl)
+{
+       unsigned long cb = _ALIGN_UP(sizeof(tbl->it_userspace[0]) *
+                       tbl->it_size, PAGE_SIZE);
+       unsigned long *uas;
+       long ret;
+
+       BUG_ON(tbl->it_userspace);
+
+       ret = try_increment_locked_vm(cb >> PAGE_SHIFT);
+       if (ret)
+               return ret;
+
+       uas = vzalloc(cb);
+       if (!uas) {
+               decrement_locked_vm(cb >> PAGE_SHIFT);
+               return -ENOMEM;
+       }
+       tbl->it_userspace = uas;
+
+       return 0;
+}
+
+static void tce_iommu_userspace_view_free(struct iommu_table *tbl)
+{
+       unsigned long cb = _ALIGN_UP(sizeof(tbl->it_userspace[0]) *
+                       tbl->it_size, PAGE_SIZE);
+
+       if (!tbl->it_userspace)
+               return;
+
+       vfree(tbl->it_userspace);
+       tbl->it_userspace = NULL;
+       decrement_locked_vm(cb >> PAGE_SHIFT);
+}
+
+static bool tce_page_is_contained(struct page *page, unsigned page_shift)
+{
+       /*
+        * Check that the TCE table granularity is not bigger than the size of
+        * a page we just found. Otherwise the hardware can get access to
+        * a bigger memory chunk that it should.
+        */
+       return (PAGE_SHIFT + compound_order(compound_head(page))) >= page_shift;
+}
+
+static inline bool tce_groups_attached(struct tce_container *container)
+{
+       return !list_empty(&container->group_list);
+}
+
+static long tce_iommu_find_table(struct tce_container *container,
+               phys_addr_t ioba, struct iommu_table **ptbl)
+{
+       long i;
+
+       for (i = 0; i < IOMMU_TABLE_GROUP_MAX_TABLES; ++i) {
+               struct iommu_table *tbl = container->tables[i];
+
+               if (tbl) {
+                       unsigned long entry = ioba >> tbl->it_page_shift;
+                       unsigned long start = tbl->it_offset;
+                       unsigned long end = start + tbl->it_size;
+
+                       if ((start <= entry) && (entry < end)) {
+                               *ptbl = tbl;
+                               return i;
+                       }
+               }
+       }
+
+       return -1;
+}
+
+static int tce_iommu_find_free_table(struct tce_container *container)
+{
+       int i;
+
+       for (i = 0; i < IOMMU_TABLE_GROUP_MAX_TABLES; ++i) {
+               if (!container->tables[i])
+                       return i;
+       }
+
+       return -ENOSPC;
+}
+
 static int tce_iommu_enable(struct tce_container *container)
 {
        int ret = 0;
-       unsigned long locked, lock_limit, npages;
-       struct iommu_table *tbl = container->tbl;
-
-       if (!container->tbl)
-               return -ENXIO;
+       unsigned long locked;
+       struct iommu_table_group *table_group;
+       struct tce_iommu_group *tcegrp;
 
        if (!current->mm)
                return -ESRCH; /* process exited */
@@ -79,21 +253,38 @@ static int tce_iommu_enable(struct tce_container *container)
         * Also we don't have a nice way to fail on H_PUT_TCE due to ulimits,
         * that would effectively kill the guest at random points, much better
         * enforcing the limit based on the max that the guest can map.
+        *
+        * Unfortunately at the moment it counts whole tables, no matter how
+        * much memory the guest has. I.e. for 4GB guest and 4 IOMMU groups
+        * each with 2GB DMA window, 8GB will be counted here. The reason for
+        * this is that we cannot tell here the amount of RAM used by the guest
+        * as this information is only available from KVM and VFIO is
+        * KVM agnostic.
+        *
+        * So we do not allow enabling a container without a group attached
+        * as there is no way to know how much we should increment
+        * the locked_vm counter.
         */
-       down_write(&current->mm->mmap_sem);
-       npages = (tbl->it_size << IOMMU_PAGE_SHIFT_4K) >> PAGE_SHIFT;
-       locked = current->mm->locked_vm + npages;
-       lock_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT;
-       if (locked > lock_limit && !capable(CAP_IPC_LOCK)) {
-               pr_warn("RLIMIT_MEMLOCK (%ld) exceeded\n",
-                               rlimit(RLIMIT_MEMLOCK));
-               ret = -ENOMEM;
-       } else {
+       if (!tce_groups_attached(container))
+               return -ENODEV;
 
-               current->mm->locked_vm += npages;
-               container->enabled = true;
-       }
-       up_write(&current->mm->mmap_sem);
+       tcegrp = list_first_entry(&container->group_list,
+                       struct tce_iommu_group, next);
+       table_group = iommu_group_get_iommudata(tcegrp->grp);
+       if (!table_group)
+               return -ENODEV;
+
+       if (!table_group->tce32_size)
+               return -EPERM;
+
+       locked = table_group->tce32_size >> PAGE_SHIFT;
+       ret = try_increment_locked_vm(locked);
+       if (ret)
+               return ret;
+
+       container->locked_pages = locked;
+
+       container->enabled = true;
 
        return ret;
 }
@@ -105,20 +296,17 @@ static void tce_iommu_disable(struct tce_container *container)
 
        container->enabled = false;
 
-       if (!container->tbl || !current->mm)
+       if (!current->mm)
                return;
 
-       down_write(&current->mm->mmap_sem);
-       current->mm->locked_vm -= (container->tbl->it_size <<
-                       IOMMU_PAGE_SHIFT_4K) >> PAGE_SHIFT;
-       up_write(&current->mm->mmap_sem);
+       decrement_locked_vm(container->locked_pages);
 }
 
 static void *tce_iommu_open(unsigned long arg)
 {
        struct tce_container *container;
 
-       if (arg != VFIO_SPAPR_TCE_IOMMU) {
+       if ((arg != VFIO_SPAPR_TCE_IOMMU) && (arg != VFIO_SPAPR_TCE_v2_IOMMU)) {
                pr_err("tce_vfio: Wrong IOMMU type\n");
                return ERR_PTR(-EINVAL);
        }
@@ -128,36 +316,411 @@ static void *tce_iommu_open(unsigned long arg)
                return ERR_PTR(-ENOMEM);
 
        mutex_init(&container->lock);
+       INIT_LIST_HEAD_RCU(&container->group_list);
+
+       container->v2 = arg == VFIO_SPAPR_TCE_v2_IOMMU;
 
        return container;
 }
 
+static int tce_iommu_clear(struct tce_container *container,
+               struct iommu_table *tbl,
+               unsigned long entry, unsigned long pages);
+static void tce_iommu_free_table(struct iommu_table *tbl);
+
 static void tce_iommu_release(void *iommu_data)
 {
        struct tce_container *container = iommu_data;
+       struct iommu_table_group *table_group;
+       struct tce_iommu_group *tcegrp;
+       long i;
+
+       while (tce_groups_attached(container)) {
+               tcegrp = list_first_entry(&container->group_list,
+                               struct tce_iommu_group, next);
+               table_group = iommu_group_get_iommudata(tcegrp->grp);
+               tce_iommu_detach_group(iommu_data, tcegrp->grp);
+       }
 
-       WARN_ON(container->tbl && !container->tbl->it_group);
-       tce_iommu_disable(container);
+       /*
+        * If VFIO created a table, it was not disposed
+        * by tce_iommu_detach_group() so do it now.
+        */
+       for (i = 0; i < IOMMU_TABLE_GROUP_MAX_TABLES; ++i) {
+               struct iommu_table *tbl = container->tables[i];
+
+               if (!tbl)
+                       continue;
 
-       if (container->tbl && container->tbl->it_group)
-               tce_iommu_detach_group(iommu_data, container->tbl->it_group);
+               tce_iommu_clear(container, tbl, tbl->it_offset, tbl->it_size);
+               tce_iommu_free_table(tbl);
+       }
 
+       tce_iommu_disable(container);
        mutex_destroy(&container->lock);
 
        kfree(container);
 }
 
+static void tce_iommu_unuse_page(struct tce_container *container,
+               unsigned long hpa)
+{
+       struct page *page;
+
+       page = pfn_to_page(hpa >> PAGE_SHIFT);
+       put_page(page);
+}
+
+static int tce_iommu_prereg_ua_to_hpa(unsigned long tce, unsigned long size,
+               unsigned long *phpa, struct mm_iommu_table_group_mem_t **pmem)
+{
+       long ret = 0;
+       struct mm_iommu_table_group_mem_t *mem;
+
+       mem = mm_iommu_lookup(tce, size);
+       if (!mem)
+               return -EINVAL;
+
+       ret = mm_iommu_ua_to_hpa(mem, tce, phpa);
+       if (ret)
+               return -EINVAL;
+
+       *pmem = mem;
+
+       return 0;
+}
+
+static void tce_iommu_unuse_page_v2(struct iommu_table *tbl,
+               unsigned long entry)
+{
+       struct mm_iommu_table_group_mem_t *mem = NULL;
+       int ret;
+       unsigned long hpa = 0;
+       unsigned long *pua = IOMMU_TABLE_USERSPACE_ENTRY(tbl, entry);
+
+       if (!pua || !current || !current->mm)
+               return;
+
+       ret = tce_iommu_prereg_ua_to_hpa(*pua, IOMMU_PAGE_SIZE(tbl),
+                       &hpa, &mem);
+       if (ret)
+               pr_debug("%s: tce %lx at #%lx was not cached, ret=%d\n",
+                               __func__, *pua, entry, ret);
+       if (mem)
+               mm_iommu_mapped_dec(mem);
+
+       *pua = 0;
+}
+
+static int tce_iommu_clear(struct tce_container *container,
+               struct iommu_table *tbl,
+               unsigned long entry, unsigned long pages)
+{
+       unsigned long oldhpa;
+       long ret;
+       enum dma_data_direction direction;
+
+       for ( ; pages; --pages, ++entry) {
+               direction = DMA_NONE;
+               oldhpa = 0;
+               ret = iommu_tce_xchg(tbl, entry, &oldhpa, &direction);
+               if (ret)
+                       continue;
+
+               if (direction == DMA_NONE)
+                       continue;
+
+               if (container->v2) {
+                       tce_iommu_unuse_page_v2(tbl, entry);
+                       continue;
+               }
+
+               tce_iommu_unuse_page(container, oldhpa);
+       }
+
+       return 0;
+}
+
+static int tce_iommu_use_page(unsigned long tce, unsigned long *hpa)
+{
+       struct page *page = NULL;
+       enum dma_data_direction direction = iommu_tce_direction(tce);
+
+       if (get_user_pages_fast(tce & PAGE_MASK, 1,
+                       direction != DMA_TO_DEVICE, &page) != 1)
+               return -EFAULT;
+
+       *hpa = __pa((unsigned long) page_address(page));
+
+       return 0;
+}
+
+static long tce_iommu_build(struct tce_container *container,
+               struct iommu_table *tbl,
+               unsigned long entry, unsigned long tce, unsigned long pages,
+               enum dma_data_direction direction)
+{
+       long i, ret = 0;
+       struct page *page;
+       unsigned long hpa;
+       enum dma_data_direction dirtmp;
+
+       for (i = 0; i < pages; ++i) {
+               unsigned long offset = tce & IOMMU_PAGE_MASK(tbl) & ~PAGE_MASK;
+
+               ret = tce_iommu_use_page(tce, &hpa);
+               if (ret)
+                       break;
+
+               page = pfn_to_page(hpa >> PAGE_SHIFT);
+               if (!tce_page_is_contained(page, tbl->it_page_shift)) {
+                       ret = -EPERM;
+                       break;
+               }
+
+               hpa |= offset;
+               dirtmp = direction;
+               ret = iommu_tce_xchg(tbl, entry + i, &hpa, &dirtmp);
+               if (ret) {
+                       tce_iommu_unuse_page(container, hpa);
+                       pr_err("iommu_tce: %s failed ioba=%lx, tce=%lx, ret=%ld\n",
+                                       __func__, entry << tbl->it_page_shift,
+                                       tce, ret);
+                       break;
+               }
+
+               if (dirtmp != DMA_NONE)
+                       tce_iommu_unuse_page(container, hpa);
+
+               tce += IOMMU_PAGE_SIZE(tbl);
+       }
+
+       if (ret)
+               tce_iommu_clear(container, tbl, entry, i);
+
+       return ret;
+}
+
+static long tce_iommu_build_v2(struct tce_container *container,
+               struct iommu_table *tbl,
+               unsigned long entry, unsigned long tce, unsigned long pages,
+               enum dma_data_direction direction)
+{
+       long i, ret = 0;
+       struct page *page;
+       unsigned long hpa;
+       enum dma_data_direction dirtmp;
+
+       for (i = 0; i < pages; ++i) {
+               struct mm_iommu_table_group_mem_t *mem = NULL;
+               unsigned long *pua = IOMMU_TABLE_USERSPACE_ENTRY(tbl,
+                               entry + i);
+
+               ret = tce_iommu_prereg_ua_to_hpa(tce, IOMMU_PAGE_SIZE(tbl),
+                               &hpa, &mem);
+               if (ret)
+                       break;
+
+               page = pfn_to_page(hpa >> PAGE_SHIFT);
+               if (!tce_page_is_contained(page, tbl->it_page_shift)) {
+                       ret = -EPERM;
+                       break;
+               }
+
+               /* Preserve offset within IOMMU page */
+               hpa |= tce & IOMMU_PAGE_MASK(tbl) & ~PAGE_MASK;
+               dirtmp = direction;
+
+               /* The registered region is being unregistered */
+               if (mm_iommu_mapped_inc(mem))
+                       break;
+
+               ret = iommu_tce_xchg(tbl, entry + i, &hpa, &dirtmp);
+               if (ret) {
+                       /* dirtmp cannot be DMA_NONE here */
+                       tce_iommu_unuse_page_v2(tbl, entry + i);
+                       pr_err("iommu_tce: %s failed ioba=%lx, tce=%lx, ret=%ld\n",
+                                       __func__, entry << tbl->it_page_shift,
+                                       tce, ret);
+                       break;
+               }
+
+               if (dirtmp != DMA_NONE)
+                       tce_iommu_unuse_page_v2(tbl, entry + i);
+
+               *pua = tce;
+
+               tce += IOMMU_PAGE_SIZE(tbl);
+       }
+
+       if (ret)
+               tce_iommu_clear(container, tbl, entry, i);
+
+       return ret;
+}
+
+static long tce_iommu_create_table(struct tce_container *container,
+                       struct iommu_table_group *table_group,
+                       int num,
+                       __u32 page_shift,
+                       __u64 window_size,
+                       __u32 levels,
+                       struct iommu_table **ptbl)
+{
+       long ret, table_size;
+
+       table_size = table_group->ops->get_table_size(page_shift, window_size,
+                       levels);
+       if (!table_size)
+               return -EINVAL;
+
+       ret = try_increment_locked_vm(table_size >> PAGE_SHIFT);
+       if (ret)
+               return ret;
+
+       ret = table_group->ops->create_table(table_group, num,
+                       page_shift, window_size, levels, ptbl);
+
+       WARN_ON(!ret && !(*ptbl)->it_ops->free);
+       WARN_ON(!ret && ((*ptbl)->it_allocated_size != table_size));
+
+       if (!ret && container->v2) {
+               ret = tce_iommu_userspace_view_alloc(*ptbl);
+               if (ret)
+                       (*ptbl)->it_ops->free(*ptbl);
+       }
+
+       if (ret)
+               decrement_locked_vm(table_size >> PAGE_SHIFT);
+
+       return ret;
+}
+
+static void tce_iommu_free_table(struct iommu_table *tbl)
+{
+       unsigned long pages = tbl->it_allocated_size >> PAGE_SHIFT;
+
+       tce_iommu_userspace_view_free(tbl);
+       tbl->it_ops->free(tbl);
+       decrement_locked_vm(pages);
+}
+
+static long tce_iommu_create_window(struct tce_container *container,
+               __u32 page_shift, __u64 window_size, __u32 levels,
+               __u64 *start_addr)
+{
+       struct tce_iommu_group *tcegrp;
+       struct iommu_table_group *table_group;
+       struct iommu_table *tbl = NULL;
+       long ret, num;
+
+       num = tce_iommu_find_free_table(container);
+       if (num < 0)
+               return num;
+
+       /* Get the first group for ops::create_table */
+       tcegrp = list_first_entry(&container->group_list,
+                       struct tce_iommu_group, next);
+       table_group = iommu_group_get_iommudata(tcegrp->grp);
+       if (!table_group)
+               return -EFAULT;
+
+       if (!(table_group->pgsizes & (1ULL << page_shift)))
+               return -EINVAL;
+
+       if (!table_group->ops->set_window || !table_group->ops->unset_window ||
+                       !table_group->ops->get_table_size ||
+                       !table_group->ops->create_table)
+               return -EPERM;
+
+       /* Create TCE table */
+       ret = tce_iommu_create_table(container, table_group, num,
+                       page_shift, window_size, levels, &tbl);
+       if (ret)
+               return ret;
+
+       BUG_ON(!tbl->it_ops->free);
+
+       /*
+        * Program the table to every group.
+        * Groups have been tested for compatibility at the attach time.
+        */
+       list_for_each_entry(tcegrp, &container->group_list, next) {
+               table_group = iommu_group_get_iommudata(tcegrp->grp);
+
+               ret = table_group->ops->set_window(table_group, num, tbl);
+               if (ret)
+                       goto unset_exit;
+       }
+
+       container->tables[num] = tbl;
+
+       /* Return start address assigned by platform in create_table() */
+       *start_addr = tbl->it_offset << tbl->it_page_shift;
+
+       return 0;
+
+unset_exit:
+       list_for_each_entry(tcegrp, &container->group_list, next) {
+               table_group = iommu_group_get_iommudata(tcegrp->grp);
+               table_group->ops->unset_window(table_group, num);
+       }
+       tce_iommu_free_table(tbl);
+
+       return ret;
+}
+
+static long tce_iommu_remove_window(struct tce_container *container,
+               __u64 start_addr)
+{
+       struct iommu_table_group *table_group = NULL;
+       struct iommu_table *tbl;
+       struct tce_iommu_group *tcegrp;
+       int num;
+
+       num = tce_iommu_find_table(container, start_addr, &tbl);
+       if (num < 0)
+               return -EINVAL;
+
+       BUG_ON(!tbl->it_size);
+
+       /* Detach groups from IOMMUs */
+       list_for_each_entry(tcegrp, &container->group_list, next) {
+               table_group = iommu_group_get_iommudata(tcegrp->grp);
+
+               /*
+                * SPAPR TCE IOMMU exposes the default DMA window to
+                * the guest via dma32_window_start/size of
+                * VFIO_IOMMU_SPAPR_TCE_GET_INFO. Some platforms allow
+                * the userspace to remove this window, some do not so
+                * here we check for the platform capability.
+                */
+               if (!table_group->ops || !table_group->ops->unset_window)
+                       return -EPERM;
+
+               table_group->ops->unset_window(table_group, num);
+       }
+
+       /* Free table */
+       tce_iommu_clear(container, tbl, tbl->it_offset, tbl->it_size);
+       tce_iommu_free_table(tbl);
+       container->tables[num] = NULL;
+
+       return 0;
+}
+
 static long tce_iommu_ioctl(void *iommu_data,
                                 unsigned int cmd, unsigned long arg)
 {
        struct tce_container *container = iommu_data;
-       unsigned long minsz;
+       unsigned long minsz, ddwsz;
        long ret;
 
        switch (cmd) {
        case VFIO_CHECK_EXTENSION:
                switch (arg) {
                case VFIO_SPAPR_TCE_IOMMU:
+               case VFIO_SPAPR_TCE_v2_IOMMU:
                        ret = 1;
                        break;
                default:
@@ -169,9 +732,17 @@ static long tce_iommu_ioctl(void *iommu_data,
 
        case VFIO_IOMMU_SPAPR_TCE_GET_INFO: {
                struct vfio_iommu_spapr_tce_info info;
-               struct iommu_table *tbl = container->tbl;
+               struct tce_iommu_group *tcegrp;
+               struct iommu_table_group *table_group;
+
+               if (!tce_groups_attached(container))
+                       return -ENXIO;
+
+               tcegrp = list_first_entry(&container->group_list,
+                               struct tce_iommu_group, next);
+               table_group = iommu_group_get_iommudata(tcegrp->grp);
 
-               if (WARN_ON(!tbl))
+               if (!table_group)
                        return -ENXIO;
 
                minsz = offsetofend(struct vfio_iommu_spapr_tce_info,
@@ -183,9 +754,24 @@ static long tce_iommu_ioctl(void *iommu_data,
                if (info.argsz < minsz)
                        return -EINVAL;
 
-               info.dma32_window_start = tbl->it_offset << IOMMU_PAGE_SHIFT_4K;
-               info.dma32_window_size = tbl->it_size << IOMMU_PAGE_SHIFT_4K;
+               info.dma32_window_start = table_group->tce32_start;
+               info.dma32_window_size = table_group->tce32_size;
                info.flags = 0;
+               memset(&info.ddw, 0, sizeof(info.ddw));
+
+               if (table_group->max_dynamic_windows_supported &&
+                               container->v2) {
+                       info.flags |= VFIO_IOMMU_SPAPR_INFO_DDW;
+                       info.ddw.pgsizes = table_group->pgsizes;
+                       info.ddw.max_dynamic_windows_supported =
+                               table_group->max_dynamic_windows_supported;
+                       info.ddw.levels = table_group->max_levels;
+               }
+
+               ddwsz = offsetofend(struct vfio_iommu_spapr_tce_info, ddw);
+
+               if (info.argsz >= ddwsz)
+                       minsz = ddwsz;
 
                if (copy_to_user((void __user *)arg, &info, minsz))
                        return -EFAULT;
@@ -194,13 +780,12 @@ static long tce_iommu_ioctl(void *iommu_data,
        }
        case VFIO_IOMMU_MAP_DMA: {
                struct vfio_iommu_type1_dma_map param;
-               struct iommu_table *tbl = container->tbl;
-               unsigned long tce, i;
+               struct iommu_table *tbl = NULL;
+               long num;
+               enum dma_data_direction direction;
 
-               if (!tbl)
-                       return -ENXIO;
-
-               BUG_ON(!tbl->it_group);
+               if (!container->enabled)
+                       return -EPERM;
 
                minsz = offsetofend(struct vfio_iommu_type1_dma_map, size);
 
@@ -214,32 +799,43 @@ static long tce_iommu_ioctl(void *iommu_data,
                                VFIO_DMA_MAP_FLAG_WRITE))
                        return -EINVAL;
 
-               if ((param.size & ~IOMMU_PAGE_MASK_4K) ||
-                               (param.vaddr & ~IOMMU_PAGE_MASK_4K))
+               num = tce_iommu_find_table(container, param.iova, &tbl);
+               if (num < 0)
+                       return -ENXIO;
+
+               if ((param.size & ~IOMMU_PAGE_MASK(tbl)) ||
+                               (param.vaddr & ~IOMMU_PAGE_MASK(tbl)))
                        return -EINVAL;
 
                /* iova is checked by the IOMMU API */
-               tce = param.vaddr;
-               if (param.flags & VFIO_DMA_MAP_FLAG_READ)
-                       tce |= TCE_PCI_READ;
-               if (param.flags & VFIO_DMA_MAP_FLAG_WRITE)
-                       tce |= TCE_PCI_WRITE;
+               if (param.flags & VFIO_DMA_MAP_FLAG_READ) {
+                       if (param.flags & VFIO_DMA_MAP_FLAG_WRITE)
+                               direction = DMA_BIDIRECTIONAL;
+                       else
+                               direction = DMA_TO_DEVICE;
+               } else {
+                       if (param.flags & VFIO_DMA_MAP_FLAG_WRITE)
+                               direction = DMA_FROM_DEVICE;
+                       else
+                               return -EINVAL;
+               }
 
-               ret = iommu_tce_put_param_check(tbl, param.iova, tce);
+               ret = iommu_tce_put_param_check(tbl, param.iova, param.vaddr);
                if (ret)
                        return ret;
 
-               for (i = 0; i < (param.size >> IOMMU_PAGE_SHIFT_4K); ++i) {
-                       ret = iommu_put_tce_user_mode(tbl,
-                                       (param.iova >> IOMMU_PAGE_SHIFT_4K) + i,
-                                       tce);
-                       if (ret)
-                               break;
-                       tce += IOMMU_PAGE_SIZE_4K;
-               }
-               if (ret)
-                       iommu_clear_tces_and_put_pages(tbl,
-                                       param.iova >> IOMMU_PAGE_SHIFT_4K, i);
+               if (container->v2)
+                       ret = tce_iommu_build_v2(container, tbl,
+                                       param.iova >> tbl->it_page_shift,
+                                       param.vaddr,
+                                       param.size >> tbl->it_page_shift,
+                                       direction);
+               else
+                       ret = tce_iommu_build(container, tbl,
+                                       param.iova >> tbl->it_page_shift,
+                                       param.vaddr,
+                                       param.size >> tbl->it_page_shift,
+                                       direction);
 
                iommu_flush_tce(tbl);
 
@@ -247,10 +843,11 @@ static long tce_iommu_ioctl(void *iommu_data,
        }
        case VFIO_IOMMU_UNMAP_DMA: {
                struct vfio_iommu_type1_dma_unmap param;
-               struct iommu_table *tbl = container->tbl;
+               struct iommu_table *tbl = NULL;
+               long num;
 
-               if (WARN_ON(!tbl))
-                       return -ENXIO;
+               if (!container->enabled)
+                       return -EPERM;
 
                minsz = offsetofend(struct vfio_iommu_type1_dma_unmap,
                                size);
@@ -265,22 +862,81 @@ static long tce_iommu_ioctl(void *iommu_data,
                if (param.flags)
                        return -EINVAL;
 
-               if (param.size & ~IOMMU_PAGE_MASK_4K)
+               num = tce_iommu_find_table(container, param.iova, &tbl);
+               if (num < 0)
+                       return -ENXIO;
+
+               if (param.size & ~IOMMU_PAGE_MASK(tbl))
                        return -EINVAL;
 
                ret = iommu_tce_clear_param_check(tbl, param.iova, 0,
-                               param.size >> IOMMU_PAGE_SHIFT_4K);
+                               param.size >> tbl->it_page_shift);
                if (ret)
                        return ret;
 
-               ret = iommu_clear_tces_and_put_pages(tbl,
-                               param.iova >> IOMMU_PAGE_SHIFT_4K,
-                               param.size >> IOMMU_PAGE_SHIFT_4K);
+               ret = tce_iommu_clear(container, tbl,
+                               param.iova >> tbl->it_page_shift,
+                               param.size >> tbl->it_page_shift);
                iommu_flush_tce(tbl);
 
                return ret;
        }
+       case VFIO_IOMMU_SPAPR_REGISTER_MEMORY: {
+               struct vfio_iommu_spapr_register_memory param;
+
+               if (!container->v2)
+                       break;
+
+               minsz = offsetofend(struct vfio_iommu_spapr_register_memory,
+                               size);
+
+               if (copy_from_user(&param, (void __user *)arg, minsz))
+                       return -EFAULT;
+
+               if (param.argsz < minsz)
+                       return -EINVAL;
+
+               /* No flag is supported now */
+               if (param.flags)
+                       return -EINVAL;
+
+               mutex_lock(&container->lock);
+               ret = tce_iommu_register_pages(container, param.vaddr,
+                               param.size);
+               mutex_unlock(&container->lock);
+
+               return ret;
+       }
+       case VFIO_IOMMU_SPAPR_UNREGISTER_MEMORY: {
+               struct vfio_iommu_spapr_register_memory param;
+
+               if (!container->v2)
+                       break;
+
+               minsz = offsetofend(struct vfio_iommu_spapr_register_memory,
+                               size);
+
+               if (copy_from_user(&param, (void __user *)arg, minsz))
+                       return -EFAULT;
+
+               if (param.argsz < minsz)
+                       return -EINVAL;
+
+               /* No flag is supported now */
+               if (param.flags)
+                       return -EINVAL;
+
+               mutex_lock(&container->lock);
+               ret = tce_iommu_unregister_pages(container, param.vaddr,
+                               param.size);
+               mutex_unlock(&container->lock);
+
+               return ret;
+       }
        case VFIO_IOMMU_ENABLE:
+               if (container->v2)
+                       break;
+
                mutex_lock(&container->lock);
                ret = tce_iommu_enable(container);
                mutex_unlock(&container->lock);
@@ -288,48 +944,280 @@ static long tce_iommu_ioctl(void *iommu_data,
 
 
        case VFIO_IOMMU_DISABLE:
+               if (container->v2)
+                       break;
+
                mutex_lock(&container->lock);
                tce_iommu_disable(container);
                mutex_unlock(&container->lock);
                return 0;
-       case VFIO_EEH_PE_OP:
-               if (!container->tbl || !container->tbl->it_group)
-                       return -ENODEV;
 
-               return vfio_spapr_iommu_eeh_ioctl(container->tbl->it_group,
-                                                 cmd, arg);
+       case VFIO_EEH_PE_OP: {
+               struct tce_iommu_group *tcegrp;
+
+               ret = 0;
+               list_for_each_entry(tcegrp, &container->group_list, next) {
+                       ret = vfio_spapr_iommu_eeh_ioctl(tcegrp->grp,
+                                       cmd, arg);
+                       if (ret)
+                               return ret;
+               }
+               return ret;
+       }
+
+       case VFIO_IOMMU_SPAPR_TCE_CREATE: {
+               struct vfio_iommu_spapr_tce_create create;
+
+               if (!container->v2)
+                       break;
+
+               if (!tce_groups_attached(container))
+                       return -ENXIO;
+
+               minsz = offsetofend(struct vfio_iommu_spapr_tce_create,
+                               start_addr);
+
+               if (copy_from_user(&create, (void __user *)arg, minsz))
+                       return -EFAULT;
+
+               if (create.argsz < minsz)
+                       return -EINVAL;
+
+               if (create.flags)
+                       return -EINVAL;
+
+               mutex_lock(&container->lock);
+
+               ret = tce_iommu_create_window(container, create.page_shift,
+                               create.window_size, create.levels,
+                               &create.start_addr);
+
+               mutex_unlock(&container->lock);
+
+               if (!ret && copy_to_user((void __user *)arg, &create, minsz))
+                       ret = -EFAULT;
+
+               return ret;
+       }
+       case VFIO_IOMMU_SPAPR_TCE_REMOVE: {
+               struct vfio_iommu_spapr_tce_remove remove;
+
+               if (!container->v2)
+                       break;
+
+               if (!tce_groups_attached(container))
+                       return -ENXIO;
+
+               minsz = offsetofend(struct vfio_iommu_spapr_tce_remove,
+                               start_addr);
+
+               if (copy_from_user(&remove, (void __user *)arg, minsz))
+                       return -EFAULT;
+
+               if (remove.argsz < minsz)
+                       return -EINVAL;
+
+               if (remove.flags)
+                       return -EINVAL;
+
+               mutex_lock(&container->lock);
+
+               ret = tce_iommu_remove_window(container, remove.start_addr);
+
+               mutex_unlock(&container->lock);
+
+               return ret;
+       }
        }
 
        return -ENOTTY;
 }
 
+static void tce_iommu_release_ownership(struct tce_container *container,
+               struct iommu_table_group *table_group)
+{
+       int i;
+
+       for (i = 0; i < IOMMU_TABLE_GROUP_MAX_TABLES; ++i) {
+               struct iommu_table *tbl = container->tables[i];
+
+               if (!tbl)
+                       continue;
+
+               tce_iommu_clear(container, tbl, tbl->it_offset, tbl->it_size);
+               tce_iommu_userspace_view_free(tbl);
+               if (tbl->it_map)
+                       iommu_release_ownership(tbl);
+
+               container->tables[i] = NULL;
+       }
+}
+
+static int tce_iommu_take_ownership(struct tce_container *container,
+               struct iommu_table_group *table_group)
+{
+       int i, j, rc = 0;
+
+       for (i = 0; i < IOMMU_TABLE_GROUP_MAX_TABLES; ++i) {
+               struct iommu_table *tbl = table_group->tables[i];
+
+               if (!tbl || !tbl->it_map)
+                       continue;
+
+               rc = tce_iommu_userspace_view_alloc(tbl);
+               if (!rc)
+                       rc = iommu_take_ownership(tbl);
+
+               if (rc) {
+                       for (j = 0; j < i; ++j)
+                               iommu_release_ownership(
+                                               table_group->tables[j]);
+
+                       return rc;
+               }
+       }
+
+       for (i = 0; i < IOMMU_TABLE_GROUP_MAX_TABLES; ++i)
+               container->tables[i] = table_group->tables[i];
+
+       return 0;
+}
+
+static void tce_iommu_release_ownership_ddw(struct tce_container *container,
+               struct iommu_table_group *table_group)
+{
+       long i;
+
+       if (!table_group->ops->unset_window) {
+               WARN_ON_ONCE(1);
+               return;
+       }
+
+       for (i = 0; i < IOMMU_TABLE_GROUP_MAX_TABLES; ++i)
+               table_group->ops->unset_window(table_group, i);
+
+       table_group->ops->release_ownership(table_group);
+}
+
+static long tce_iommu_take_ownership_ddw(struct tce_container *container,
+               struct iommu_table_group *table_group)
+{
+       long i, ret = 0;
+       struct iommu_table *tbl = NULL;
+
+       if (!table_group->ops->create_table || !table_group->ops->set_window ||
+                       !table_group->ops->release_ownership) {
+               WARN_ON_ONCE(1);
+               return -EFAULT;
+       }
+
+       table_group->ops->take_ownership(table_group);
+
+       /*
+        * If it the first group attached, check if there is
+        * a default DMA window and create one if none as
+        * the userspace expects it to exist.
+        */
+       if (!tce_groups_attached(container) && !container->tables[0]) {
+               ret = tce_iommu_create_table(container,
+                               table_group,
+                               0, /* window number */
+                               IOMMU_PAGE_SHIFT_4K,
+                               table_group->tce32_size,
+                               1, /* default levels */
+                               &tbl);
+               if (ret)
+                       goto release_exit;
+               else
+                       container->tables[0] = tbl;
+       }
+
+       /* Set all windows to the new group */
+       for (i = 0; i < IOMMU_TABLE_GROUP_MAX_TABLES; ++i) {
+               tbl = container->tables[i];
+
+               if (!tbl)
+                       continue;
+
+               /* Set the default window to a new group */
+               ret = table_group->ops->set_window(table_group, i, tbl);
+               if (ret)
+                       goto release_exit;
+       }
+
+       return 0;
+
+release_exit:
+       for (i = 0; i < IOMMU_TABLE_GROUP_MAX_TABLES; ++i)
+               table_group->ops->unset_window(table_group, i);
+
+       table_group->ops->release_ownership(table_group);
+
+       return ret;
+}
+
 static int tce_iommu_attach_group(void *iommu_data,
                struct iommu_group *iommu_group)
 {
        int ret;
        struct tce_container *container = iommu_data;
-       struct iommu_table *tbl = iommu_group_get_iommudata(iommu_group);
+       struct iommu_table_group *table_group;
+       struct tce_iommu_group *tcegrp = NULL;
 
-       BUG_ON(!tbl);
        mutex_lock(&container->lock);
 
        /* pr_debug("tce_vfio: Attaching group #%u to iommu %p\n",
                        iommu_group_id(iommu_group), iommu_group); */
-       if (container->tbl) {
-               pr_warn("tce_vfio: Only one group per IOMMU container is allowed, existing id=%d, attaching id=%d\n",
-                               iommu_group_id(container->tbl->it_group),
-                               iommu_group_id(iommu_group));
-               ret = -EBUSY;
-       } else if (container->enabled) {
-               pr_err("tce_vfio: attaching group #%u to enabled container\n",
-                               iommu_group_id(iommu_group));
+       table_group = iommu_group_get_iommudata(iommu_group);
+
+       if (tce_groups_attached(container) && (!table_group->ops ||
+                       !table_group->ops->take_ownership ||
+                       !table_group->ops->release_ownership)) {
                ret = -EBUSY;
-       } else {
-               ret = iommu_take_ownership(tbl);
-               if (!ret)
-                       container->tbl = tbl;
+               goto unlock_exit;
+       }
+
+       /* Check if new group has the same iommu_ops (i.e. compatible) */
+       list_for_each_entry(tcegrp, &container->group_list, next) {
+               struct iommu_table_group *table_group_tmp;
+
+               if (tcegrp->grp == iommu_group) {
+                       pr_warn("tce_vfio: Group %d is already attached\n",
+                                       iommu_group_id(iommu_group));
+                       ret = -EBUSY;
+                       goto unlock_exit;
+               }
+               table_group_tmp = iommu_group_get_iommudata(tcegrp->grp);
+               if (table_group_tmp->ops != table_group->ops) {
+                       pr_warn("tce_vfio: Group %d is incompatible with group %d\n",
+                                       iommu_group_id(iommu_group),
+                                       iommu_group_id(tcegrp->grp));
+                       ret = -EPERM;
+                       goto unlock_exit;
+               }
+       }
+
+       tcegrp = kzalloc(sizeof(*tcegrp), GFP_KERNEL);
+       if (!tcegrp) {
+               ret = -ENOMEM;
+               goto unlock_exit;
        }
 
+       if (!table_group->ops || !table_group->ops->take_ownership ||
+                       !table_group->ops->release_ownership)
+               ret = tce_iommu_take_ownership(container, table_group);
+       else
+               ret = tce_iommu_take_ownership_ddw(container, table_group);
+
+       if (!ret) {
+               tcegrp->grp = iommu_group;
+               list_add(&tcegrp->next, &container->group_list);
+       }
+
+unlock_exit:
+       if (ret && tcegrp)
+               kfree(tcegrp);
+
        mutex_unlock(&container->lock);
 
        return ret;
@@ -339,26 +1227,37 @@ static void tce_iommu_detach_group(void *iommu_data,
                struct iommu_group *iommu_group)
 {
        struct tce_container *container = iommu_data;
-       struct iommu_table *tbl = iommu_group_get_iommudata(iommu_group);
+       struct iommu_table_group *table_group;
+       bool found = false;
+       struct tce_iommu_group *tcegrp;
 
-       BUG_ON(!tbl);
        mutex_lock(&container->lock);
-       if (tbl != container->tbl) {
-               pr_warn("tce_vfio: detaching group #%u, expected group is #%u\n",
-                               iommu_group_id(iommu_group),
-                               iommu_group_id(tbl->it_group));
-       } else {
-               if (container->enabled) {
-                       pr_warn("tce_vfio: detaching group #%u from enabled container, forcing disable\n",
-                                       iommu_group_id(tbl->it_group));
-                       tce_iommu_disable(container);
+
+       list_for_each_entry(tcegrp, &container->group_list, next) {
+               if (tcegrp->grp == iommu_group) {
+                       found = true;
+                       break;
                }
+       }
 
-               /* pr_debug("tce_vfio: detaching group #%u from iommu %p\n",
-                               iommu_group_id(iommu_group), iommu_group); */
-               container->tbl = NULL;
-               iommu_release_ownership(tbl);
+       if (!found) {
+               pr_warn("tce_vfio: detaching unattached group #%u\n",
+                               iommu_group_id(iommu_group));
+               goto unlock_exit;
        }
+
+       list_del(&tcegrp->next);
+       kfree(tcegrp);
+
+       table_group = iommu_group_get_iommudata(iommu_group);
+       BUG_ON(!table_group);
+
+       if (!table_group->ops || !table_group->ops->release_ownership)
+               tce_iommu_release_ownership(container, table_group);
+       else
+               tce_iommu_release_ownership_ddw(container, table_group);
+
+unlock_exit:
        mutex_unlock(&container->lock);
 }
 
index 5fa42db769ee8e5d9d88edd8dbccc71102290ad1..38edeb4729a9d475445bffab51c25ead9c6ae5bb 100644 (file)
@@ -85,6 +85,16 @@ long vfio_spapr_iommu_eeh_ioctl(struct iommu_group *group,
                case VFIO_EEH_PE_CONFIGURE:
                        ret = eeh_pe_configure(pe);
                        break;
+               case VFIO_EEH_PE_INJECT_ERR:
+                       minsz = offsetofend(struct vfio_eeh_pe_op, err.mask);
+                       if (op.argsz < minsz)
+                               return -EINVAL;
+                       if (copy_from_user(&op, (void __user *)arg, minsz))
+                               return -EFAULT;
+
+                       ret = eeh_pe_inject_err(pe, op.err.type, op.err.func,
+                                               op.err.addr, op.err.mask);
+                       break;
                default:
                        ret = -EINVAL;
                }
index ea32b386797f5d52b70ee6f4028f5e8df43f3a8f..5b30d27dab507a998d30f4341f824358c67f4a10 100644 (file)
@@ -37,7 +37,8 @@
 #include <linux/fs.h>
 #include <linux/miscdevice.h>
 #include <asm/unaligned.h>
-#include <scsi/scsi.h>
+#include <scsi/scsi_common.h>
+#include <scsi/scsi_proto.h>
 #include <target/target_core_base.h>
 #include <target/target_core_fabric.h>
 #include <target/target_core_fabric_configfs.h>
index a6ab9299813c9558dcf9e2cb2ec629e3b557e398..bb4e96255974af27d090b0c4d25a1269976d2d76 100644 (file)
@@ -687,7 +687,7 @@ static int newport_scroll(struct vc_data *vc, int t, int b, int dir,
 static void newport_bmove(struct vc_data *vc, int sy, int sx, int dy,
                          int dx, int h, int w)
 {
-       short xs, ys, xe, ye, xoffs, yoffs, tmp;
+       short xs, ys, xe, ye, xoffs, yoffs;
 
        xs = sx << 3;
        xe = ((sx + w) << 3) - 1;
@@ -701,9 +701,7 @@ static void newport_bmove(struct vc_data *vc, int sy, int sx, int dy,
        yoffs = (dy - sy) << 4;
        if (xoffs > 0) {
                /* move to the right, exchange starting points */
-               tmp = xe;
-               xe = xs;
-               xs = tmp;
+               swap(xe, xs);
        }
        newport_wait(npregs);
        npregs->set.drawmode0 = (NPORT_DMODE0_S2S | NPORT_DMODE0_BLOCK |
index 1094623030879dcdc7c0b898b3b8853d42a54282..2d98de535e0f7374804474c58de752ffb2848aa4 100644 (file)
@@ -2326,13 +2326,6 @@ config FB_PRE_INIT_FB
          Select this option if display contents should be inherited as set by
          the bootloader.
 
-config FB_MSM
-       tristate "MSM Framebuffer support"
-       depends on FB && ARCH_MSM
-       select FB_CFB_FILLRECT
-       select FB_CFB_COPYAREA
-       select FB_CFB_IMAGEBLIT
-
 config FB_MX3
        tristate "MX3 Framebuffer support"
        depends on FB && MX3_IPU
@@ -2478,6 +2471,7 @@ config FB_SSD1307
        select FB_SYS_IMAGEBLIT
        select FB_DEFERRED_IO
        select PWM
+       select FB_BACKLIGHT
        help
          This driver implements support for the Solomon SSD1307
          OLED controller over I2C.
index 1979afffccfe18f568252b3bf351525d20e61b08..cecea5063a802afbf3d211dce5e831d3d31e8cd5 100644 (file)
@@ -126,7 +126,6 @@ obj-y                             += omap2/
 obj-$(CONFIG_XEN_FBDEV_FRONTEND)  += xen-fbfront.o
 obj-$(CONFIG_FB_CARMINE)          += carminefb.o
 obj-$(CONFIG_FB_MB862XX)         += mb862xx/
-obj-$(CONFIG_FB_MSM)              += msm/
 obj-$(CONFIG_FB_NUC900)           += nuc900fb.o
 obj-$(CONFIG_FB_JZ4740)                  += jz4740_fb.o
 obj-$(CONFIG_FB_PUV3_UNIGFX)      += fb-puv3.o
index 35f7900a057347566bf566392315e2d26fa82ebb..1d702e13aaff08771f873d705f1fdeb79ad2a3ee 100644 (file)
@@ -2052,7 +2052,7 @@ static void ami_set_sprite(const struct amifb_par *par)
 {
        copins *copl, *cops;
        u_short hs, vs, ve;
-       u_long pl, ps, pt;
+       u_long pl, ps;
        short mx, my;
 
        cops = copdisplay.list[currentcop][0];
@@ -2078,7 +2078,7 @@ static void ami_set_sprite(const struct amifb_par *par)
                        if (mod2(vs)) {
                                lofsprite[1 << par->crsr.fmode] = spr2hw_ctl(vs, hs, ve);
                                shfsprite[1 << par->crsr.fmode] = spr2hw_ctl(vs + 1, hs, ve + 1);
-                               pt = pl; pl = ps; ps = pt;
+                               swap(pl, ps);
                        } else {
                                lofsprite[1 << par->crsr.fmode] = spr2hw_ctl(vs, hs, ve + 1);
                                shfsprite[1 << par->crsr.fmode] = spr2hw_ctl(vs + 1, hs, ve);
@@ -3705,8 +3705,8 @@ default_chipset:
         * access the videomem with writethrough cache
         */
        info->fix.smem_start = (u_long)ZTWO_PADDR(videomemory);
-       videomemory = (u_long)ioremap_writethrough(info->fix.smem_start,
-                                                  info->fix.smem_len);
+       videomemory = (u_long)ioremap_wt(info->fix.smem_start,
+                                        info->fix.smem_len);
        if (!videomemory) {
                dev_warn(&pdev->dev,
                         "Unable to map videomem cached writethrough\n");
index cb9ee25568506a5f3efeca661a4309634f255937..d6ce613e12adea198fa55dcf35a934a4e8dc66a7 100644 (file)
@@ -3185,8 +3185,7 @@ int __init atafb_init(void)
                /* Map the video memory (physical address given) to somewhere
                 * in the kernel address space.
                 */
-               external_screen_base = ioremap_writethrough(external_addr,
-                                                    external_len);
+               external_screen_base = ioremap_wt(external_addr, external_len);
                if (external_vgaiobase)
                        external_vgaiobase =
                          (unsigned long)ioremap(external_vgaiobase, 0x10000);
index 94a8d04e60f94679c9f7ae5846133363b553c274..abadc490fa1f58cdc3b129108248c3871fe40c17 100644 (file)
@@ -1266,7 +1266,8 @@ static int __init atmel_lcdfb_probe(struct platform_device *pdev)
                        goto stop_clk;
                }
 
-               info->screen_base = ioremap(info->fix.smem_start, info->fix.smem_len);
+               info->screen_base = ioremap_wc(info->fix.smem_start,
+                                              info->fix.smem_len);
                if (!info->screen_base) {
                        ret = -ENOMEM;
                        goto release_intmem;
index 0156954bf34097547a381341385b85dc6c8021de..c42ce2fdfd441de6a88f7e3ecbc46fa2514028d7 100644 (file)
 #include <asm/btext.h>
 #endif /* CONFIG_BOOTX_TEXT */
 
-#ifdef CONFIG_MTRR
-#include <asm/mtrr.h>
-#endif
-
 #include <video/aty128.h>
 
 /* Debug flag */
@@ -399,10 +395,7 @@ static int default_cmode = CMODE_8;
 
 static int default_crt_on = 0;
 static int default_lcd_on = 1;
-
-#ifdef CONFIG_MTRR
 static bool mtrr = true;
-#endif
 
 #ifdef CONFIG_FB_ATY128_BACKLIGHT
 #ifdef CONFIG_PMAC_BACKLIGHT
@@ -456,9 +449,7 @@ struct aty128fb_par {
        u32 vram_size;                      /* onboard video ram   */
        int chip_gen;
        const struct aty128_meminfo *mem;   /* onboard mem info    */
-#ifdef CONFIG_MTRR
-       struct { int vram; int vram_valid; } mtrr;
-#endif
+       int wc_cookie;
        int blitter_may_be_busy;
        int fifo_slots;                 /* free slots in FIFO (64 max) */
 
@@ -1725,12 +1716,10 @@ static int aty128fb_setup(char *options)
 #endif
                        continue;
                }
-#ifdef CONFIG_MTRR
                if(!strncmp(this_opt, "nomtrr", 6)) {
                        mtrr = 0;
                        continue;
                }
-#endif
 #ifdef CONFIG_PPC_PMAC
                /* vmode and cmode deprecated */
                if (!strncmp(this_opt, "vmode:", 6)) {
@@ -2133,7 +2122,7 @@ static int aty128_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        par->vram_size = aty_ld_le32(CNFG_MEMSIZE) & 0x03FFFFFF;
 
        /* Virtualize the framebuffer */
-       info->screen_base = ioremap(fb_addr, par->vram_size);
+       info->screen_base = ioremap_wc(fb_addr, par->vram_size);
        if (!info->screen_base)
                goto err_unmap_out;
 
@@ -2170,15 +2159,9 @@ static int aty128_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        if (!aty128_init(pdev, ent))
                goto err_out;
 
-#ifdef CONFIG_MTRR
-       if (mtrr) {
-               par->mtrr.vram = mtrr_add(info->fix.smem_start,
-                               par->vram_size, MTRR_TYPE_WRCOMB, 1);
-               par->mtrr.vram_valid = 1;
-               /* let there be speed */
-               printk(KERN_INFO "aty128fb: Rage128 MTRR set to ON\n");
-       }
-#endif /* CONFIG_MTRR */
+       if (mtrr)
+               par->wc_cookie = arch_phys_wc_add(info->fix.smem_start,
+                                                 par->vram_size);
        return 0;
 
 err_out:
@@ -2212,11 +2195,7 @@ static void aty128_remove(struct pci_dev *pdev)
        aty128_bl_exit(info->bl_dev);
 #endif
 
-#ifdef CONFIG_MTRR
-       if (par->mtrr.vram_valid)
-               mtrr_del(par->mtrr.vram, info->fix.smem_start,
-                        par->vram_size);
-#endif /* CONFIG_MTRR */
+       arch_phys_wc_del(par->wc_cookie);
        iounmap(par->regbase);
        iounmap(info->screen_base);
 
@@ -2625,8 +2604,5 @@ MODULE_DESCRIPTION("FBDev driver for ATI Rage128 / Pro cards");
 MODULE_LICENSE("GPL");
 module_param(mode_option, charp, 0);
 MODULE_PARM_DESC(mode_option, "Specify resolution as \"<xres>x<yres>[-<bpp>][@<refresh>]\" ");
-#ifdef CONFIG_MTRR
 module_param_named(nomtrr, mtrr, invbool, 0);
 MODULE_PARM_DESC(nomtrr, "bool: Disable MTRR support (0 or 1=disabled) (default=0)");
-#endif
-
index 01237c8fcdc6b64b54bca91d1ef11cf64338ecde..2bdb070707e47851ee24e941e66a02bbdb8bc028 100644 (file)
 
 #endif /* CONFIG_PPC */
 
-#ifdef CONFIG_MTRR
-#include <asm/mtrr.h>
-#endif
-
 #include <video/radeon.h>
 #include <linux/radeonfb.h>
 
@@ -271,9 +267,7 @@ static bool mirror = 0;
 static int panel_yres = 0;
 static bool force_dfp = 0;
 static bool force_measure_pll = 0;
-#ifdef CONFIG_MTRR
 static bool nomtrr = 0;
-#endif
 static bool force_sleep;
 static bool ignore_devlist;
 #ifdef CONFIG_PMAC_BACKLIGHT
@@ -2260,8 +2254,8 @@ static int radeonfb_pci_register(struct pci_dev *pdev,
        rinfo->mapped_vram = min_t(unsigned long, MAX_MAPPED_VRAM, rinfo->video_ram);
 
        do {
-               rinfo->fb_base = ioremap (rinfo->fb_base_phys,
-                                         rinfo->mapped_vram);
+               rinfo->fb_base = ioremap_wc(rinfo->fb_base_phys,
+                                           rinfo->mapped_vram);
        } while (rinfo->fb_base == NULL &&
                 ((rinfo->mapped_vram /= 2) >= MIN_MAPPED_VRAM));
 
@@ -2359,11 +2353,9 @@ static int radeonfb_pci_register(struct pci_dev *pdev,
                goto err_unmap_fb;
        }
 
-#ifdef CONFIG_MTRR
-       rinfo->mtrr_hdl = nomtrr ? -1 : mtrr_add(rinfo->fb_base_phys,
-                                                rinfo->video_ram,
-                                                MTRR_TYPE_WRCOMB, 1);
-#endif
+       if (!nomtrr)
+               rinfo->wc_cookie = arch_phys_wc_add(rinfo->fb_base_phys,
+                                                   rinfo->video_ram);
 
        if (backlight)
                radeonfb_bl_init(rinfo);
@@ -2428,12 +2420,7 @@ static void radeonfb_pci_unregister(struct pci_dev *pdev)
  #endif
 
        del_timer_sync(&rinfo->lvds_timer);
-
-#ifdef CONFIG_MTRR
-       if (rinfo->mtrr_hdl >= 0)
-               mtrr_del(rinfo->mtrr_hdl, 0, 0);
-#endif
-
+       arch_phys_wc_del(rinfo->wc_cookie);
         unregister_framebuffer(info);
 
         radeonfb_bl_exit(rinfo);
@@ -2489,10 +2476,8 @@ static int __init radeonfb_setup (char *options)
                        panel_yres = simple_strtoul((this_opt+11), NULL, 0);
                } else if (!strncmp(this_opt, "backlight:", 10)) {
                        backlight = simple_strtoul(this_opt+10, NULL, 0);
-#ifdef CONFIG_MTRR
                } else if (!strncmp(this_opt, "nomtrr", 6)) {
                        nomtrr = 1;
-#endif
                } else if (!strncmp(this_opt, "nomodeset", 9)) {
                        nomodeset = 1;
                } else if (!strncmp(this_opt, "force_measure_pll", 17)) {
@@ -2552,10 +2537,8 @@ module_param(monitor_layout, charp, 0);
 MODULE_PARM_DESC(monitor_layout, "Specify monitor mapping (like XFree86)");
 module_param(force_measure_pll, bool, 0);
 MODULE_PARM_DESC(force_measure_pll, "Force measurement of PLL (debug)");
-#ifdef CONFIG_MTRR
 module_param(nomtrr, bool, 0);
 MODULE_PARM_DESC(nomtrr, "bool: disable use of MTRR registers");
-#endif
 module_param(panel_yres, int, 0);
 MODULE_PARM_DESC(panel_yres, "int: set panel yres");
 module_param(mode_option, charp, 0);
index 039def41c92004f0815b2324c9f96aa8b202eb07..5bc1944ea1a910b79fe25167b37d86496314df16 100644 (file)
@@ -340,7 +340,7 @@ struct radeonfb_info {
 
        struct pll_info         pll;
 
-       int                     mtrr_hdl;
+       int                     wc_cookie;
 
        u32                     save_regs[100];
        int                     asleep;
index 67f28e20a89256c0b11a23d9a81bf8defa98c1e5..23d86a8b7d7b3d0264da4a6c7fdb05cea28768fa 100644 (file)
@@ -3,6 +3,7 @@ obj-$(CONFIG_FB_CMDLINE)          += fb_cmdline.o
 obj-$(CONFIG_FB)                  += fb.o
 fb-y                              := fbmem.o fbmon.o fbcmap.o fbsysfs.o \
                                      modedb.o fbcvt.o
+fb-$(CONFIG_FB_DEFERRED_IO)       += fb_defio.o
 fb-objs                           := $(fb-y)
 
 obj-$(CONFIG_FB_CFB_FILLRECT)  += cfbfillrect.o
@@ -14,4 +15,3 @@ obj-$(CONFIG_FB_SYS_IMAGEBLIT) += sysimgblt.o
 obj-$(CONFIG_FB_SYS_FOPS)      += fb_sys_fops.o
 obj-$(CONFIG_FB_SVGALIB)       += svgalib.o
 obj-$(CONFIG_FB_DDC)           += fb_ddc.o
-obj-$(CONFIG_FB_DEFERRED_IO)   += fb_defio.o
index d6cab1fd9a4795da2fe2348c2882bc3e04a6cf30..3fc63c208d08ba9fa801570d830220106aa24f21 100644 (file)
@@ -242,5 +242,3 @@ void fb_deferred_io_cleanup(struct fb_info *info)
        mutex_destroy(&fbdefio->lock);
 }
 EXPORT_SYMBOL_GPL(fb_deferred_io_cleanup);
-
-MODULE_LICENSE("GPL");
index 01ef1b953390d186a02770e08f3cb954f8d22a72..d787533d9c8b0b0e589fe4eaa217b29debbb6845 100644 (file)
@@ -1475,7 +1475,9 @@ int of_get_fb_videomode(struct device_node *np, struct fb_videomode *fb,
        if (ret)
                return ret;
 
-       fb_videomode_from_videomode(&vm, fb);
+       ret = fb_videomode_from_videomode(&vm, fb);
+       if (ret)
+               return ret;
 
        pr_debug("%s: got %dx%d display mode from %s\n",
                of_node_full_name(np), vm.hactive, vm.vactive, np->name);
index 6d9ef39810c8eed2c8efe34b3087ab95971595a1..b63d55f481fae421cbea5cb71466401835e23a3d 100644 (file)
@@ -22,9 +22,6 @@
 #include <linux/module.h>
 #include <linux/io.h>
 
-#ifdef CONFIG_X86
-#include <asm/mtrr.h>
-#endif
 #ifdef CONFIG_MIPS
 #include <asm/addrspace.h>
 #endif
@@ -38,6 +35,7 @@ static struct sgi_gbe *gbe;
 struct gbefb_par {
        struct fb_var_screeninfo var;
        struct gbe_timing_info timing;
+       int wc_cookie;
        int valid;
 };
 
@@ -1175,8 +1173,8 @@ static int gbefb_probe(struct platform_device *p_dev)
 
        if (gbe_mem_phys) {
                /* memory was allocated at boot time */
-               gbe_mem = devm_ioremap_nocache(&p_dev->dev, gbe_mem_phys,
-                                              gbe_mem_size);
+               gbe_mem = devm_ioremap_wc(&p_dev->dev, gbe_mem_phys,
+                                         gbe_mem_size);
                if (!gbe_mem) {
                        printk(KERN_ERR "gbefb: couldn't map framebuffer\n");
                        ret = -ENOMEM;
@@ -1187,8 +1185,8 @@ static int gbefb_probe(struct platform_device *p_dev)
        } else {
                /* try to allocate memory with the classical allocator
                 * this has high chance to fail on low memory machines */
-               gbe_mem = dma_alloc_coherent(NULL, gbe_mem_size, &gbe_dma_addr,
-                                            GFP_KERNEL);
+               gbe_mem = dma_alloc_writecombine(NULL, gbe_mem_size,
+                                                &gbe_dma_addr, GFP_KERNEL);
                if (!gbe_mem) {
                        printk(KERN_ERR "gbefb: couldn't allocate framebuffer memory\n");
                        ret = -ENOMEM;
@@ -1198,9 +1196,8 @@ static int gbefb_probe(struct platform_device *p_dev)
                gbe_mem_phys = (unsigned long) gbe_dma_addr;
        }
 
-#ifdef CONFIG_X86
-       mtrr_add(gbe_mem_phys, gbe_mem_size, MTRR_TYPE_WRCOMB, 1);
-#endif
+       par = info->par;
+       par->wc_cookie = arch_phys_wc_add(gbe_mem_phys, gbe_mem_size);
 
        /* map framebuffer memory into tiles table */
        for (i = 0; i < (gbe_mem_size >> TILE_SHIFT); i++)
@@ -1215,7 +1212,6 @@ static int gbefb_probe(struct platform_device *p_dev)
        /* reset GBE */
        gbe_reset();
 
-       par = info->par;
        /* turn on default video mode */
        if (fb_find_mode(&par->var, info, mode_option, NULL, 0,
                         default_mode, 8) == 0)
@@ -1240,8 +1236,9 @@ static int gbefb_probe(struct platform_device *p_dev)
        return 0;
 
 out_gbe_unmap:
+       arch_phys_wc_del(par->wc_cookie);
        if (gbe_dma_addr)
-               dma_free_coherent(NULL, gbe_mem_size, gbe_mem, gbe_mem_phys);
+               dma_free_writecombine(NULL, gbe_mem_size, gbe_mem, gbe_mem_phys);
 out_tiles_free:
        dma_free_coherent(NULL, GBE_TLB_SIZE * sizeof(uint16_t),
                          (void *)gbe_tiles.cpu, gbe_tiles.dma);
@@ -1256,11 +1253,13 @@ out_release_framebuffer:
 static int gbefb_remove(struct platform_device* p_dev)
 {
        struct fb_info *info = platform_get_drvdata(p_dev);
+       struct gbefb_par *par = info->par;
 
        unregister_framebuffer(info);
        gbe_turn_off();
+       arch_phys_wc_del(par->wc_cookie);
        if (gbe_dma_addr)
-               dma_free_coherent(NULL, gbe_mem_size, gbe_mem, gbe_mem_phys);
+               dma_free_writecombine(NULL, gbe_mem_size, gbe_mem, gbe_mem_phys);
        dma_free_coherent(NULL, GBE_TLB_SIZE * sizeof(uint16_t),
                          (void *)gbe_tiles.cpu, gbe_tiles.dma);
        release_mem_region(GBE_BASE, sizeof(struct sgi_gbe));
index 124d7c7e2d1482c3a01b419c11afa7572f864479..ec9fc9ac23decf320408e66f15ae5e5cf0bb8bea 100644 (file)
@@ -263,7 +263,8 @@ static int gxfb_map_video_memory(struct fb_info *info, struct pci_dev *dev)
 
        info->fix.smem_start = pci_resource_start(dev, 0);
        info->fix.smem_len = vram ? vram : gx_frame_buffer_size();
-       info->screen_base = ioremap(info->fix.smem_start, info->fix.smem_len);
+       info->screen_base = ioremap_wc(info->fix.smem_start,
+                                      info->fix.smem_len);
        if (!info->screen_base)
                return -ENOMEM;
 
index a1b7e5fa9b099f7df0011672bde9afeb8d6405e8..9476d196f510c2c93e9275befc652bda0b7412f7 100644 (file)
@@ -241,8 +241,8 @@ static int hpfb_init_one(unsigned long phys_base, unsigned long virt_base)
        fb_info.fix.line_length = fb_width;
        fb_height = (in_8(fb_regs + HPFB_FBHMSB) << 8) | in_8(fb_regs + HPFB_FBHLSB);
        fb_info.fix.smem_len = fb_width * fb_height;
-       fb_start = (unsigned long)ioremap_writethrough(fb_info.fix.smem_start,
-                                                      fb_info.fix.smem_len);
+       fb_start = (unsigned long)ioremap_wt(fb_info.fix.smem_start,
+                                            fb_info.fix.smem_len);
        hpfb_defined.xres = (in_8(fb_regs + HPFB_DWMSB) << 8) | in_8(fb_regs + HPFB_DWLSB);
        hpfb_defined.yres = (in_8(fb_regs + HPFB_DHMSB) << 8) | in_8(fb_regs + HPFB_DHLSB);
        hpfb_defined.xres_virtual = hpfb_defined.xres;
index 1414b73ac55b67f0ffbdde5da0f8923572252169..7b1c002bfb0878b85bbb8502b343937b5f0002af 100644 (file)
 #define HAS_FONTCACHE               8 
 
 /* driver flags */
-#define HAS_MTRR                    1
 #define HAS_ACCELERATION            2
 #define ALWAYS_SYNC                 4
 #define LOCKUP                      8
@@ -281,7 +280,7 @@ struct i810fb_par {
        u32 ovract;
        u32 cur_state;
        u32 ddc_num;
-       int mtrr_reg;
+       int wc_cookie;
        u16 bltcntl;
        u8 interlace;
 };
index bb674e431741cea2f4c53089ab97169364206946..025b882a4826dee272abc930897c6fd98fd09e71 100644 (file)
@@ -41,6 +41,7 @@
 #include <linux/resource.h>
 #include <linux/unistd.h>
 #include <linux/console.h>
+#include <linux/io.h>
 
 #include <asm/io.h>
 #include <asm/div64.h>
@@ -1816,7 +1817,9 @@ static void i810_init_device(struct i810fb_par *par)
        u8 reg;
        u8 __iomem *mmio = par->mmio_start_virtual;
 
-       if (mtrr) set_mtrr(par);
+       if (mtrr)
+               par->wc_cookie= arch_phys_wc_add((u32) par->aperture.physical,
+                                                par->aperture.size);
 
        i810_init_cursor(par);
 
@@ -1865,8 +1868,8 @@ static int i810_allocate_pci_resource(struct i810fb_par *par,
        }
        par->res_flags |= FRAMEBUFFER_REQ;
 
-       par->aperture.virtual = ioremap_nocache(par->aperture.physical, 
-                                       par->aperture.size);
+       par->aperture.virtual = ioremap_wc(par->aperture.physical,
+                                          par->aperture.size);
        if (!par->aperture.virtual) {
                printk("i810fb_init: cannot remap framebuffer region\n");
                return -ENODEV;
@@ -2096,7 +2099,7 @@ static void i810fb_release_resource(struct fb_info *info,
                                    struct i810fb_par *par)
 {
        struct gtt_data *gtt = &par->i810_gtt;
-       unset_mtrr(par);
+       arch_phys_wc_del(par->wc_cookie);
 
        i810_delete_i2c_busses(par);
 
index a25afaa534baa3264925b86a503eb2fb39f0d41a..7bfaaad1d0fae4b39f00809f400b73809da08021 100644 (file)
@@ -60,32 +60,6 @@ static inline void flush_cache(void)
 #define flush_cache() do { } while(0)
 #endif 
 
-#ifdef CONFIG_MTRR
-
-#include <asm/mtrr.h>
-
-static inline void set_mtrr(struct i810fb_par *par)
-{
-       par->mtrr_reg = mtrr_add((u32) par->aperture.physical, 
-                par->aperture.size, MTRR_TYPE_WRCOMB, 1);
-       if (par->mtrr_reg < 0) {
-               printk(KERN_ERR "set_mtrr: unable to set MTRR\n");
-               return;
-       }
-       par->dev_flags |= HAS_MTRR;
-}
-static inline void unset_mtrr(struct i810fb_par *par)
-{
-       if (par->dev_flags & HAS_MTRR) 
-               mtrr_del(par->mtrr_reg, (u32) par->aperture.physical, 
-                        par->aperture.size); 
-}
-#else
-#define set_mtrr(x) printk("set_mtrr: MTRR is disabled in the kernel\n")
-
-#define unset_mtrr(x) do { } while (0)
-#endif /* CONFIG_MTRR */
-
 #ifdef CONFIG_FB_I810_GTF
 #define IS_DVT (0)
 #else
index 84d1d29e532cb6ceed4c698b16e249dc69476c36..cee88603efc9e40195430fe1a745eebefb97ef0b 100644 (file)
@@ -170,7 +170,7 @@ struct imxfb_info {
        struct regulator        *lcd_pwr;
 };
 
-static struct platform_device_id imxfb_devtype[] = {
+static const struct platform_device_id imxfb_devtype[] = {
        {
                .name = "imx1-fb",
                .driver_data = IMX1_FB,
index 6b51175629c741edb36ef98a890020eafe0091c4..37f8339ea88c831b7bd3211513e953134c505694 100644 (file)
@@ -285,9 +285,7 @@ struct intelfb_info {
        /* use a gart reserved fb mem */
        u8 fbmem_gart;
 
-       /* mtrr support */
-       int mtrr_reg;
-       u32 has_mtrr;
+       int wc_cookie;
 
        /* heap data */
        struct intelfb_heap_data aperture;
index b847d530471a2f6a421ec3c8ff08822ea95ecbef..bbec737eef30241ce56fd72be62c6003215b3251 100644 (file)
 
 #include <asm/io.h>
 
-#ifdef CONFIG_MTRR
-#include <asm/mtrr.h>
-#endif
-
 #include "intelfb.h"
 #include "intelfbhw.h"
 #include "../edid.h"
@@ -410,33 +406,6 @@ static void __exit intelfb_exit(void)
 module_init(intelfb_init);
 module_exit(intelfb_exit);
 
-/***************************************************************
- *                     mtrr support functions                  *
- ***************************************************************/
-
-#ifdef CONFIG_MTRR
-static inline void set_mtrr(struct intelfb_info *dinfo)
-{
-       dinfo->mtrr_reg = mtrr_add(dinfo->aperture.physical,
-                                  dinfo->aperture.size, MTRR_TYPE_WRCOMB, 1);
-       if (dinfo->mtrr_reg < 0) {
-               ERR_MSG("unable to set MTRR\n");
-               return;
-       }
-       dinfo->has_mtrr = 1;
-}
-static inline void unset_mtrr(struct intelfb_info *dinfo)
-{
-       if (dinfo->has_mtrr)
-               mtrr_del(dinfo->mtrr_reg, dinfo->aperture.physical,
-                        dinfo->aperture.size);
-}
-#else
-#define set_mtrr(x) WRN_MSG("MTRR is disabled in the kernel\n")
-
-#define unset_mtrr(x) do { } while (0)
-#endif /* CONFIG_MTRR */
-
 /***************************************************************
  *                        driver init / cleanup                *
  ***************************************************************/
@@ -456,7 +425,7 @@ static void cleanup(struct intelfb_info *dinfo)
        if (dinfo->registered)
                unregister_framebuffer(dinfo->info);
 
-       unset_mtrr(dinfo);
+       arch_phys_wc_del(dinfo->wc_cookie);
 
        if (dinfo->fbmem_gart && dinfo->gtt_fb_mem) {
                agp_unbind_memory(dinfo->gtt_fb_mem);
@@ -675,7 +644,7 @@ static int intelfb_pci_register(struct pci_dev *pdev,
        /* Allocate memories (which aren't stolen) */
        /* Map the fb and MMIO regions */
        /* ioremap only up to the end of used aperture */
-       dinfo->aperture.virtual = (u8 __iomem *)ioremap_nocache
+       dinfo->aperture.virtual = (u8 __iomem *)ioremap_wc
                (dinfo->aperture.physical, ((offset + dinfo->fb.offset) << 12)
                 + dinfo->fb.size);
        if (!dinfo->aperture.virtual) {
@@ -772,7 +741,8 @@ static int intelfb_pci_register(struct pci_dev *pdev,
        agp_backend_release(bridge);
 
        if (mtrr)
-               set_mtrr(dinfo);
+               dinfo->wc_cookie = arch_phys_wc_add(dinfo->aperture.physical,
+                                                   dinfo->aperture.size);
 
        DBG_MSG("fb: 0x%x(+ 0x%x)/0x%x (0x%p)\n",
                dinfo->fb.physical, dinfo->fb.offset, dinfo->fb.size,
index 62539ca1cfa909e1ffc3e74b4e9b74a60e26be1f..11eb094396aea32f206b60ab2b99df90c3c8d2c4 100644 (file)
@@ -370,12 +370,9 @@ static void matroxfb_remove(struct matrox_fb_info *minfo, int dummy)
        matroxfb_unregister_device(minfo);
        unregister_framebuffer(&minfo->fbcon);
        matroxfb_g450_shutdown(minfo);
-#ifdef CONFIG_MTRR
-       if (minfo->mtrr.vram_valid)
-               mtrr_del(minfo->mtrr.vram, minfo->video.base, minfo->video.len);
-#endif
-       mga_iounmap(minfo->mmio.vbase);
-       mga_iounmap(minfo->video.vbase);
+       arch_phys_wc_del(minfo->wc_cookie);
+       iounmap(minfo->mmio.vbase.vaddr);
+       iounmap(minfo->video.vbase.vaddr);
        release_mem_region(minfo->video.base, minfo->video.len_maximum);
        release_mem_region(minfo->mmio.base, 16384);
        kfree(minfo);
@@ -591,12 +588,8 @@ static int matroxfb_decode_var(const struct matrox_fb_info *minfo,
                        unsigned int max_yres;
 
                        while (m1) {
-                               int t;
-
                                while (m2 >= m1) m2 -= m1;
-                               t = m1;
-                               m1 = m2;
-                               m2 = t;
+                               swap(m1, m2);
                        }
                        m2 = linelen * PAGE_SIZE / m2;
                        *ydstorg = m2 = 0x400000 % m2;
@@ -1256,9 +1249,7 @@ static int nobios;                        /* "matroxfb:nobios" */
 static int noinit = 1;                 /* "matroxfb:init" */
 static int inverse;                    /* "matroxfb:inverse" */
 static int sgram;                      /* "matroxfb:sgram" */
-#ifdef CONFIG_MTRR
 static int mtrr = 1;                   /* "matroxfb:nomtrr" */
-#endif
 static int grayscale;                  /* "matroxfb:grayscale" */
 static int dev = -1;                   /* "matroxfb:dev:xxxxx" */
 static unsigned int vesa = ~0;         /* "matroxfb:vesa:xxxxx" */
@@ -1717,14 +1708,17 @@ static int initMatrox2(struct matrox_fb_info *minfo, struct board *b)
        if (mem && (mem < memsize))
                memsize = mem;
        err = -ENOMEM;
-       if (mga_ioremap(ctrlptr_phys, 16384, MGA_IOREMAP_MMIO, &minfo->mmio.vbase)) {
+
+       minfo->mmio.vbase.vaddr = ioremap_nocache(ctrlptr_phys, 16384);
+       if (!minfo->mmio.vbase.vaddr) {
                printk(KERN_ERR "matroxfb: cannot ioremap(%lX, 16384), matroxfb disabled\n", ctrlptr_phys);
                goto failVideoMR;
        }
        minfo->mmio.base = ctrlptr_phys;
        minfo->mmio.len = 16384;
        minfo->video.base = video_base_phys;
-       if (mga_ioremap(video_base_phys, memsize, MGA_IOREMAP_FB, &minfo->video.vbase)) {
+       minfo->video.vbase.vaddr = ioremap_wc(video_base_phys, memsize);
+       if (!minfo->video.vbase.vaddr) {
                printk(KERN_ERR "matroxfb: cannot ioremap(%lX, %d), matroxfb disabled\n",
                        video_base_phys, memsize);
                goto failCtrlIO;
@@ -1772,13 +1766,9 @@ static int initMatrox2(struct matrox_fb_info *minfo, struct board *b)
        minfo->video.len_usable = minfo->video.len;
        if (minfo->video.len_usable > b->base->maxdisplayable)
                minfo->video.len_usable = b->base->maxdisplayable;
-#ifdef CONFIG_MTRR
-       if (mtrr) {
-               minfo->mtrr.vram = mtrr_add(video_base_phys, minfo->video.len, MTRR_TYPE_WRCOMB, 1);
-               minfo->mtrr.vram_valid = 1;
-               printk(KERN_INFO "matroxfb: MTRR's turned on\n");
-       }
-#endif /* CONFIG_MTRR */
+       if (mtrr)
+               minfo->wc_cookie = arch_phys_wc_add(video_base_phys,
+                                                   minfo->video.len);
 
        if (!minfo->devflags.novga)
                request_region(0x3C0, 32, "matrox");
@@ -1947,9 +1937,9 @@ static int initMatrox2(struct matrox_fb_info *minfo, struct board *b)
        return 0;
 failVideoIO:;
        matroxfb_g450_shutdown(minfo);
-       mga_iounmap(minfo->video.vbase);
+       iounmap(minfo->video.vbase.vaddr);
 failCtrlIO:;
-       mga_iounmap(minfo->mmio.vbase);
+       iounmap(minfo->mmio.vbase.vaddr);
 failVideoMR:;
        release_mem_region(video_base_phys, minfo->video.len_maximum);
 failCtrlMR:;
@@ -2443,10 +2433,8 @@ static int __init matroxfb_setup(char *options) {
                                nobios = !value;
                        else if (!strcmp(this_opt, "init"))
                                noinit = !value;
-#ifdef CONFIG_MTRR
                        else if (!strcmp(this_opt, "mtrr"))
                                mtrr = value;
-#endif
                        else if (!strcmp(this_opt, "inv24"))
                                inv24 = value;
                        else if (!strcmp(this_opt, "cross4MB"))
@@ -2515,10 +2503,8 @@ module_param(noinit, int, 0);
 MODULE_PARM_DESC(noinit, "Disables W/SG/SD-RAM and bus interface initialization (0 or 1=do not initialize) (default=0)");
 module_param(memtype, int, 0);
 MODULE_PARM_DESC(memtype, "Memory type for G200/G400 (see Documentation/fb/matroxfb.txt for explanation) (default=3 for G200, 0 for G400)");
-#ifdef CONFIG_MTRR
 module_param(mtrr, int, 0);
 MODULE_PARM_DESC(mtrr, "This speeds up video memory accesses (0=disabled or 1) (default=1)");
-#endif
 module_param(sgram, int, 0);
 MODULE_PARM_DESC(sgram, "Indicates that G100/G200/G400 has SGRAM memory (0=SDRAM, 1=SGRAM) (default=0)");
 module_param(inv24, int, 0);
index 89a8a89a5eb297273c86b111af4cd8ff9e32615e..09b02cd1eb0e270f7dc654116167101202b00a27 100644 (file)
@@ -44,9 +44,6 @@
 
 #include <asm/io.h>
 #include <asm/unaligned.h>
-#ifdef CONFIG_MTRR
-#include <asm/mtrr.h>
-#endif
 
 #if defined(CONFIG_PPC_PMAC)
 #include <asm/prom.h>
@@ -187,23 +184,6 @@ static inline void __iomem* vaddr_va(vaddr_t va) {
        return va.vaddr;
 }
 
-#define MGA_IOREMAP_NORMAL     0
-#define MGA_IOREMAP_NOCACHE    1
-
-#define MGA_IOREMAP_FB         MGA_IOREMAP_NOCACHE
-#define MGA_IOREMAP_MMIO       MGA_IOREMAP_NOCACHE
-static inline int mga_ioremap(unsigned long phys, unsigned long size, int flags, vaddr_t* virt) {
-       if (flags & MGA_IOREMAP_NOCACHE)
-               virt->vaddr = ioremap_nocache(phys, size);
-       else
-               virt->vaddr = ioremap(phys, size);
-       return (virt->vaddr == NULL); /* 0, !0... 0, error_code in future */
-}
-
-static inline void mga_iounmap(vaddr_t va) {
-       iounmap(va.vaddr);
-}
-
 struct my_timming {
        unsigned int pixclock;
        int mnp;
@@ -449,12 +429,7 @@ struct matrox_fb_info {
                int             plnwt;
                int             srcorg;
                              } capable;
-#ifdef CONFIG_MTRR
-       struct {
-               int             vram;
-               int             vram_valid;
-                             } mtrr;
-#endif
+       int                     wc_cookie;
        struct {
                int             precise_width;
                int             mga_24bpp_fix;
diff --git a/drivers/video/fbdev/msm/Makefile b/drivers/video/fbdev/msm/Makefile
deleted file mode 100644 (file)
index 802d6ae..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-
-# core framebuffer
-#
-obj-y := msm_fb.o
-
-# MDP DMA/PPP engine
-#
-obj-y += mdp.o mdp_scale_tables.o mdp_ppp.o
-
-# MDDI interface
-#
-obj-y += mddi.o
-
-# MDDI client/panel drivers
-#
-obj-y += mddi_client_dummy.o
-obj-y += mddi_client_toshiba.o
-obj-y += mddi_client_nt35399.o
-
diff --git a/drivers/video/fbdev/msm/mddi.c b/drivers/video/fbdev/msm/mddi.c
deleted file mode 100644 (file)
index e0f8011..0000000
+++ /dev/null
@@ -1,821 +0,0 @@
-/*
- * MSM MDDI Transport
- *
- * Copyright (C) 2007 Google Incorporated
- * Copyright (C) 2007 QUALCOMM Incorporated
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.         See the
- * GNU General Public License for more details.
- *
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/dma-mapping.h>
-#include <linux/interrupt.h>
-#include <linux/platform_device.h>
-#include <linux/delay.h>
-#include <linux/gfp.h>
-#include <linux/spinlock.h>
-#include <linux/clk.h>
-#include <linux/io.h>
-#include <linux/sched.h>
-#include <linux/platform_data/video-msm_fb.h>
-#include "mddi_hw.h"
-
-#define FLAG_DISABLE_HIBERNATION 0x0001
-#define FLAG_HAVE_CAPS          0x0002
-#define FLAG_HAS_VSYNC_IRQ      0x0004
-#define FLAG_HAVE_STATUS        0x0008
-
-#define CMD_GET_CLIENT_CAP     0x0601
-#define CMD_GET_CLIENT_STATUS  0x0602
-
-union mddi_rev {
-       unsigned char raw[MDDI_REV_BUFFER_SIZE];
-       struct mddi_rev_packet hdr;
-       struct mddi_client_status status;
-       struct mddi_client_caps caps;
-       struct mddi_register_access reg;
-};
-
-struct reg_read_info {
-       struct completion done;
-       uint32_t reg;
-       uint32_t status;
-       uint32_t result;
-};
-
-struct mddi_info {
-       uint16_t flags;
-       uint16_t version;
-       char __iomem *base;
-       int irq;
-       struct clk *clk;
-       struct msm_mddi_client_data client_data;
-
-       /* buffer for rev encap packets */
-       void *rev_data;
-       dma_addr_t rev_addr;
-       struct mddi_llentry *reg_write_data;
-       dma_addr_t reg_write_addr;
-       struct mddi_llentry *reg_read_data;
-       dma_addr_t reg_read_addr;
-       size_t rev_data_curr;
-
-       spinlock_t int_lock;
-       uint32_t int_enable;
-       uint32_t got_int;
-       wait_queue_head_t int_wait;
-
-       struct mutex reg_write_lock;
-       struct mutex reg_read_lock;
-       struct reg_read_info *reg_read;
-
-       struct mddi_client_caps caps;
-       struct mddi_client_status status;
-
-       void (*power_client)(struct msm_mddi_client_data *, int);
-
-       /* client device published to bind us to the
-        * appropriate mddi_client driver
-        */
-       char client_name[20];
-
-       struct platform_device client_pdev;
-};
-
-static void mddi_init_rev_encap(struct mddi_info *mddi);
-
-#define mddi_readl(r) readl(mddi->base + (MDDI_##r))
-#define mddi_writel(v, r) writel((v), mddi->base + (MDDI_##r))
-
-void mddi_activate_link(struct msm_mddi_client_data *cdata)
-{
-       struct mddi_info *mddi = container_of(cdata, struct mddi_info,
-                                             client_data);
-
-       mddi_writel(MDDI_CMD_LINK_ACTIVE, CMD);
-}
-
-static void mddi_handle_link_list_done(struct mddi_info *mddi)
-{
-}
-
-static void mddi_reset_rev_encap_ptr(struct mddi_info *mddi)
-{
-       printk(KERN_INFO "mddi: resetting rev ptr\n");
-       mddi->rev_data_curr = 0;
-       mddi_writel(mddi->rev_addr, REV_PTR);
-       mddi_writel(mddi->rev_addr, REV_PTR);
-       mddi_writel(MDDI_CMD_FORCE_NEW_REV_PTR, CMD);
-}
-
-static void mddi_handle_rev_data(struct mddi_info *mddi, union mddi_rev *rev)
-{
-       int i;
-       struct reg_read_info *ri;
-
-       if ((rev->hdr.length <= MDDI_REV_BUFFER_SIZE - 2) &&
-          (rev->hdr.length >= sizeof(struct mddi_rev_packet) - 2)) {
-
-               switch (rev->hdr.type) {
-               case TYPE_CLIENT_CAPS:
-                       memcpy(&mddi->caps, &rev->caps,
-                              sizeof(struct mddi_client_caps));
-                       mddi->flags |= FLAG_HAVE_CAPS;
-                       wake_up(&mddi->int_wait);
-                       break;
-               case TYPE_CLIENT_STATUS:
-                       memcpy(&mddi->status, &rev->status,
-                              sizeof(struct mddi_client_status));
-                       mddi->flags |= FLAG_HAVE_STATUS;
-                       wake_up(&mddi->int_wait);
-                       break;
-               case TYPE_REGISTER_ACCESS:
-                       ri = mddi->reg_read;
-                       if (ri == 0) {
-                               printk(KERN_INFO "rev: got reg %x = %x without "
-                                                " pending read\n",
-                                      rev->reg.register_address,
-                                      rev->reg.register_data_list);
-                               break;
-                       }
-                       if (ri->reg != rev->reg.register_address) {
-                               printk(KERN_INFO "rev: got reg %x = %x for "
-                                                "wrong register, expected "
-                                                "%x\n",
-                                      rev->reg.register_address,
-                                      rev->reg.register_data_list, ri->reg);
-                               break;
-                       }
-                       mddi->reg_read = NULL;
-                       ri->status = 0;
-                       ri->result = rev->reg.register_data_list;
-                       complete(&ri->done);
-                       break;
-               default:
-                       printk(KERN_INFO "rev: unknown reverse packet: "
-                                        "len=%04x type=%04x CURR_REV_PTR=%x\n",
-                              rev->hdr.length, rev->hdr.type,
-                              mddi_readl(CURR_REV_PTR));
-                       for (i = 0; i < rev->hdr.length + 2; i++) {
-                               if ((i % 16) == 0)
-                                       printk(KERN_INFO "\n");
-                               printk(KERN_INFO " %02x", rev->raw[i]);
-                       }
-                       printk(KERN_INFO "\n");
-                       mddi_reset_rev_encap_ptr(mddi);
-               }
-       } else {
-               printk(KERN_INFO "bad rev length, %d, CURR_REV_PTR %x\n",
-                      rev->hdr.length, mddi_readl(CURR_REV_PTR));
-               mddi_reset_rev_encap_ptr(mddi);
-       }
-}
-
-static void mddi_wait_interrupt(struct mddi_info *mddi, uint32_t intmask);
-
-static void mddi_handle_rev_data_avail(struct mddi_info *mddi)
-{
-       uint32_t rev_data_count;
-       uint32_t rev_crc_err_count;
-       struct reg_read_info *ri;
-       size_t prev_offset;
-       uint16_t length;
-
-       union mddi_rev *crev = mddi->rev_data + mddi->rev_data_curr;
-
-       /* clear the interrupt */
-       mddi_writel(MDDI_INT_REV_DATA_AVAIL, INT);
-       rev_data_count = mddi_readl(REV_PKT_CNT);
-       rev_crc_err_count = mddi_readl(REV_CRC_ERR);
-       if (rev_data_count > 1)
-               printk(KERN_INFO "rev_data_count %d\n", rev_data_count);
-
-       if (rev_crc_err_count) {
-               printk(KERN_INFO "rev_crc_err_count %d, INT %x\n",
-                      rev_crc_err_count,  mddi_readl(INT));
-               ri = mddi->reg_read;
-               if (ri == 0) {
-                       printk(KERN_INFO "rev: got crc error without pending "
-                              "read\n");
-               } else {
-                       mddi->reg_read = NULL;
-                       ri->status = -EIO;
-                       ri->result = -1;
-                       complete(&ri->done);
-               }
-       }
-
-       if (rev_data_count == 0)
-               return;
-
-       prev_offset = mddi->rev_data_curr;
-
-       length = *((uint8_t *)mddi->rev_data + mddi->rev_data_curr);
-       mddi->rev_data_curr++;
-       if (mddi->rev_data_curr == MDDI_REV_BUFFER_SIZE)
-               mddi->rev_data_curr = 0;
-       length += *((uint8_t *)mddi->rev_data + mddi->rev_data_curr) << 8;
-       mddi->rev_data_curr += 1 + length;
-       if (mddi->rev_data_curr >= MDDI_REV_BUFFER_SIZE)
-               mddi->rev_data_curr =
-                       mddi->rev_data_curr % MDDI_REV_BUFFER_SIZE;
-
-       if (length > MDDI_REV_BUFFER_SIZE - 2) {
-               printk(KERN_INFO "mddi: rev data length greater than buffer"
-                       "size\n");
-               mddi_reset_rev_encap_ptr(mddi);
-               return;
-       }
-
-       if (prev_offset + 2 + length >= MDDI_REV_BUFFER_SIZE) {
-               union mddi_rev tmprev;
-               size_t rem = MDDI_REV_BUFFER_SIZE - prev_offset;
-               memcpy(&tmprev.raw[0], mddi->rev_data + prev_offset, rem);
-               memcpy(&tmprev.raw[rem], mddi->rev_data, 2 + length - rem);
-               mddi_handle_rev_data(mddi, &tmprev);
-       } else {
-               mddi_handle_rev_data(mddi, crev);
-       }
-
-       if (prev_offset < MDDI_REV_BUFFER_SIZE / 2 &&
-           mddi->rev_data_curr >= MDDI_REV_BUFFER_SIZE / 2) {
-               mddi_writel(mddi->rev_addr, REV_PTR);
-       }
-}
-
-static irqreturn_t mddi_isr(int irq, void *data)
-{
-       struct msm_mddi_client_data *cdata = data;
-       struct mddi_info *mddi = container_of(cdata, struct mddi_info,
-                                             client_data);
-       uint32_t active, status;
-
-       spin_lock(&mddi->int_lock);
-
-       active = mddi_readl(INT);
-       status = mddi_readl(STAT);
-
-       mddi_writel(active, INT);
-
-       /* ignore any interrupts we have disabled */
-       active &= mddi->int_enable;
-
-       mddi->got_int |= active;
-       wake_up(&mddi->int_wait);
-
-       if (active & MDDI_INT_PRI_LINK_LIST_DONE) {
-               mddi->int_enable &= (~MDDI_INT_PRI_LINK_LIST_DONE);
-               mddi_handle_link_list_done(mddi);
-       }
-       if (active & MDDI_INT_REV_DATA_AVAIL)
-               mddi_handle_rev_data_avail(mddi);
-
-       if (active & ~MDDI_INT_NEED_CLEAR)
-               mddi->int_enable &= ~(active & ~MDDI_INT_NEED_CLEAR);
-
-       if (active & MDDI_INT_LINK_ACTIVE) {
-               mddi->int_enable &= (~MDDI_INT_LINK_ACTIVE);
-               mddi->int_enable |= MDDI_INT_IN_HIBERNATION;
-       }
-
-       if (active & MDDI_INT_IN_HIBERNATION) {
-               mddi->int_enable &= (~MDDI_INT_IN_HIBERNATION);
-               mddi->int_enable |= MDDI_INT_LINK_ACTIVE;
-       }
-
-       mddi_writel(mddi->int_enable, INTEN);
-       spin_unlock(&mddi->int_lock);
-
-       return IRQ_HANDLED;
-}
-
-static long mddi_wait_interrupt_timeout(struct mddi_info *mddi,
-                                       uint32_t intmask, int timeout)
-{
-       unsigned long irq_flags;
-
-       spin_lock_irqsave(&mddi->int_lock, irq_flags);
-       mddi->got_int &= ~intmask;
-       mddi->int_enable |= intmask;
-       mddi_writel(mddi->int_enable, INTEN);
-       spin_unlock_irqrestore(&mddi->int_lock, irq_flags);
-       return wait_event_timeout(mddi->int_wait, mddi->got_int & intmask,
-                                 timeout);
-}
-
-static void mddi_wait_interrupt(struct mddi_info *mddi, uint32_t intmask)
-{
-       if (mddi_wait_interrupt_timeout(mddi, intmask, HZ/10) == 0)
-               printk(KERN_INFO "mddi_wait_interrupt %d, timeout "
-                      "waiting for %x, INT = %x, STAT = %x gotint = %x\n",
-                      current->pid, intmask, mddi_readl(INT), mddi_readl(STAT),
-                      mddi->got_int);
-}
-
-static void mddi_init_rev_encap(struct mddi_info *mddi)
-{
-       memset(mddi->rev_data, 0xee, MDDI_REV_BUFFER_SIZE);
-       mddi_writel(mddi->rev_addr, REV_PTR);
-       mddi_writel(MDDI_CMD_FORCE_NEW_REV_PTR, CMD);
-       mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND);
-}
-
-void mddi_set_auto_hibernate(struct msm_mddi_client_data *cdata, int on)
-{
-       struct mddi_info *mddi = container_of(cdata, struct mddi_info,
-                                             client_data);
-       mddi_writel(MDDI_CMD_POWERDOWN, CMD);
-       mddi_wait_interrupt(mddi, MDDI_INT_IN_HIBERNATION);
-       mddi_writel(MDDI_CMD_HIBERNATE | !!on, CMD);
-       mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND);
-}
-
-
-static uint16_t mddi_init_registers(struct mddi_info *mddi)
-{
-       mddi_writel(0x0001, VERSION);
-       mddi_writel(MDDI_HOST_BYTES_PER_SUBFRAME, BPS);
-       mddi_writel(0x0003, SPM); /* subframes per media */
-       mddi_writel(0x0005, TA1_LEN);
-       mddi_writel(MDDI_HOST_TA2_LEN, TA2_LEN);
-       mddi_writel(0x0096, DRIVE_HI);
-       /* 0x32 normal, 0x50 for Toshiba display */
-       mddi_writel(0x0050, DRIVE_LO);
-       mddi_writel(0x003C, DISP_WAKE); /* wakeup counter */
-       mddi_writel(MDDI_HOST_REV_RATE_DIV, REV_RATE_DIV);
-
-       mddi_writel(MDDI_REV_BUFFER_SIZE, REV_SIZE);
-       mddi_writel(MDDI_MAX_REV_PKT_SIZE, REV_ENCAP_SZ);
-
-       /* disable periodic rev encap */
-       mddi_writel(MDDI_CMD_PERIODIC_REV_ENCAP, CMD);
-       mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND);
-
-       if (mddi_readl(PAD_CTL) == 0) {
-               /* If we are turning on band gap, need to wait 5us before
-                * turning on the rest of the PAD */
-               mddi_writel(0x08000, PAD_CTL);
-               udelay(5);
-       }
-
-       /* Recommendation from PAD hw team */
-       mddi_writel(0xa850f, PAD_CTL);
-
-
-       /* Need an even number for counts */
-       mddi_writel(0x60006, DRIVER_START_CNT);
-
-       mddi_set_auto_hibernate(&mddi->client_data, 0);
-
-       mddi_writel(MDDI_CMD_DISP_IGNORE, CMD);
-       mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND);
-
-       mddi_init_rev_encap(mddi);
-       return mddi_readl(CORE_VER) & 0xffff;
-}
-
-static void mddi_suspend(struct msm_mddi_client_data *cdata)
-{
-       struct mddi_info *mddi = container_of(cdata, struct mddi_info,
-                                             client_data);
-       /* turn off the client */
-       if (mddi->power_client)
-               mddi->power_client(&mddi->client_data, 0);
-       /* turn off the link */
-       mddi_writel(MDDI_CMD_RESET, CMD);
-       mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND);
-       /* turn off the clock */
-       clk_disable(mddi->clk);
-}
-
-static void mddi_resume(struct msm_mddi_client_data *cdata)
-{
-       struct mddi_info *mddi = container_of(cdata, struct mddi_info,
-                                             client_data);
-       mddi_set_auto_hibernate(&mddi->client_data, 0);
-       /* turn on the client */
-       if (mddi->power_client)
-               mddi->power_client(&mddi->client_data, 1);
-       /* turn on the clock */
-       clk_enable(mddi->clk);
-       /* set up the local registers */
-       mddi->rev_data_curr = 0;
-       mddi_init_registers(mddi);
-       mddi_writel(mddi->int_enable, INTEN);
-       mddi_writel(MDDI_CMD_LINK_ACTIVE, CMD);
-       mddi_writel(MDDI_CMD_SEND_RTD, CMD);
-       mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND);
-       mddi_set_auto_hibernate(&mddi->client_data, 1);
-}
-
-static int mddi_get_client_caps(struct mddi_info *mddi)
-{
-       int i, j;
-
-       /* clear any stale interrupts */
-       mddi_writel(0xffffffff, INT);
-
-       mddi->int_enable = MDDI_INT_LINK_ACTIVE |
-                          MDDI_INT_IN_HIBERNATION |
-                          MDDI_INT_PRI_LINK_LIST_DONE |
-                          MDDI_INT_REV_DATA_AVAIL |
-                          MDDI_INT_REV_OVERFLOW |
-                          MDDI_INT_REV_OVERWRITE |
-                          MDDI_INT_RTD_FAILURE;
-       mddi_writel(mddi->int_enable, INTEN);
-
-       mddi_writel(MDDI_CMD_LINK_ACTIVE, CMD);
-       mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND);
-
-       for (j = 0; j < 3; j++) {
-               /* the toshiba vga panel does not respond to get
-                * caps unless you SEND_RTD, but the first SEND_RTD
-                * will fail...
-                */
-               for (i = 0; i < 4; i++) {
-                       uint32_t stat;
-
-                       mddi_writel(MDDI_CMD_SEND_RTD, CMD);
-                       mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND);
-                       stat = mddi_readl(STAT);
-                       printk(KERN_INFO "mddi cmd send rtd: int %x, stat %x, "
-                                       "rtd val %x\n", mddi_readl(INT), stat,
-                                       mddi_readl(RTD_VAL));
-                       if ((stat & MDDI_STAT_RTD_MEAS_FAIL) == 0)
-                               break;
-                       msleep(1);
-               }
-
-               mddi_writel(CMD_GET_CLIENT_CAP, CMD);
-               mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND);
-               wait_event_timeout(mddi->int_wait, mddi->flags & FLAG_HAVE_CAPS,
-                                  HZ / 100);
-
-               if (mddi->flags & FLAG_HAVE_CAPS)
-                       break;
-               printk(KERN_INFO "mddi_init, timeout waiting for caps\n");
-       }
-       return mddi->flags & FLAG_HAVE_CAPS;
-}
-
-/* link must be active when this is called */
-int mddi_check_status(struct mddi_info *mddi)
-{
-       int ret = -1, retry = 3;
-       mutex_lock(&mddi->reg_read_lock);
-       mddi_writel(MDDI_CMD_PERIODIC_REV_ENCAP | 1, CMD);
-       mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND);
-
-       do {
-               mddi->flags &= ~FLAG_HAVE_STATUS;
-               mddi_writel(CMD_GET_CLIENT_STATUS, CMD);
-               mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND);
-               wait_event_timeout(mddi->int_wait,
-                                  mddi->flags & FLAG_HAVE_STATUS,
-                                  HZ / 100);
-
-               if (mddi->flags & FLAG_HAVE_STATUS) {
-                       if (mddi->status.crc_error_count)
-                               printk(KERN_INFO "mddi status: crc_error "
-                                       "count: %d\n",
-                                       mddi->status.crc_error_count);
-                       else
-                               ret = 0;
-                       break;
-               } else
-                       printk(KERN_INFO "mddi status: failed to get client "
-                               "status\n");
-               mddi_writel(MDDI_CMD_SEND_RTD, CMD);
-               mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND);
-       } while (--retry);
-
-       mddi_writel(MDDI_CMD_PERIODIC_REV_ENCAP | 0, CMD);
-       mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND);
-       mutex_unlock(&mddi->reg_read_lock);
-       return ret;
-}
-
-
-void mddi_remote_write(struct msm_mddi_client_data *cdata, uint32_t val,
-                      uint32_t reg)
-{
-       struct mddi_info *mddi = container_of(cdata, struct mddi_info,
-                                             client_data);
-       struct mddi_llentry *ll;
-       struct mddi_register_access *ra;
-
-       mutex_lock(&mddi->reg_write_lock);
-
-       ll = mddi->reg_write_data;
-
-       ra = &(ll->u.r);
-       ra->length = 14 + 4;
-       ra->type = TYPE_REGISTER_ACCESS;
-       ra->client_id = 0;
-       ra->read_write_info = MDDI_WRITE | 1;
-       ra->crc16 = 0;
-
-       ra->register_address = reg;
-       ra->register_data_list = val;
-
-       ll->flags = 1;
-       ll->header_count = 14;
-       ll->data_count = 4;
-       ll->data = mddi->reg_write_addr + offsetof(struct mddi_llentry,
-                                                  u.r.register_data_list);
-       ll->next = 0;
-       ll->reserved = 0;
-
-       mddi_writel(mddi->reg_write_addr, PRI_PTR);
-
-       mddi_wait_interrupt(mddi, MDDI_INT_PRI_LINK_LIST_DONE);
-       mutex_unlock(&mddi->reg_write_lock);
-}
-
-uint32_t mddi_remote_read(struct msm_mddi_client_data *cdata, uint32_t reg)
-{
-       struct mddi_info *mddi = container_of(cdata, struct mddi_info,
-                                             client_data);
-       struct mddi_llentry *ll;
-       struct mddi_register_access *ra;
-       struct reg_read_info ri;
-       unsigned s;
-       int retry_count = 2;
-       unsigned long irq_flags;
-
-       mutex_lock(&mddi->reg_read_lock);
-
-       ll = mddi->reg_read_data;
-
-       ra = &(ll->u.r);
-       ra->length = 14;
-       ra->type = TYPE_REGISTER_ACCESS;
-       ra->client_id = 0;
-       ra->read_write_info = MDDI_READ | 1;
-       ra->crc16 = 0;
-
-       ra->register_address = reg;
-
-       ll->flags = 0x11;
-       ll->header_count = 14;
-       ll->data_count = 0;
-       ll->data = 0;
-       ll->next = 0;
-       ll->reserved = 0;
-
-       s = mddi_readl(STAT);
-
-       ri.reg = reg;
-       ri.status = -1;
-
-       do {
-               init_completion(&ri.done);
-               mddi->reg_read = &ri;
-               mddi_writel(mddi->reg_read_addr, PRI_PTR);
-
-               mddi_wait_interrupt(mddi, MDDI_INT_PRI_LINK_LIST_DONE);
-
-               /* Enable Periodic Reverse Encapsulation. */
-               mddi_writel(MDDI_CMD_PERIODIC_REV_ENCAP | 1, CMD);
-               mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND);
-               if (wait_for_completion_timeout(&ri.done, HZ/10) == 0 &&
-                   !ri.done.done) {
-                       printk(KERN_INFO "mddi_remote_read(%x) timeout "
-                                        "(%d %d %d)\n",
-                              reg, ri.status, ri.result, ri.done.done);
-                       spin_lock_irqsave(&mddi->int_lock, irq_flags);
-                       mddi->reg_read = NULL;
-                       spin_unlock_irqrestore(&mddi->int_lock, irq_flags);
-                       ri.status = -1;
-                       ri.result = -1;
-               }
-               if (ri.status == 0)
-                       break;
-
-               mddi_writel(MDDI_CMD_SEND_RTD, CMD);
-               mddi_writel(MDDI_CMD_LINK_ACTIVE, CMD);
-               mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND);
-               printk(KERN_INFO "mddi_remote_read: failed, sent "
-                      "MDDI_CMD_SEND_RTD: int %x, stat %x, rtd val %x "
-                      "curr_rev_ptr %x\n", mddi_readl(INT), mddi_readl(STAT),
-                      mddi_readl(RTD_VAL), mddi_readl(CURR_REV_PTR));
-       } while (retry_count-- > 0);
-       /* Disable Periodic Reverse Encapsulation. */
-       mddi_writel(MDDI_CMD_PERIODIC_REV_ENCAP | 0, CMD);
-       mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND);
-       mddi->reg_read = NULL;
-       mutex_unlock(&mddi->reg_read_lock);
-       return ri.result;
-}
-
-static struct mddi_info mddi_info[2];
-
-static int mddi_clk_setup(struct platform_device *pdev, struct mddi_info *mddi,
-                         unsigned long clk_rate)
-{
-       int ret;
-
-       /* set up the clocks */
-       mddi->clk = clk_get(&pdev->dev, "mddi_clk");
-       if (IS_ERR(mddi->clk)) {
-               printk(KERN_INFO "mddi: failed to get clock\n");
-               return PTR_ERR(mddi->clk);
-       }
-       ret =  clk_enable(mddi->clk);
-       if (ret)
-               goto fail;
-       ret = clk_set_rate(mddi->clk, clk_rate);
-       if (ret)
-               goto fail;
-       return 0;
-
-fail:
-       clk_put(mddi->clk);
-       return ret;
-}
-
-static int __init mddi_rev_data_setup(struct mddi_info *mddi)
-{
-       void *dma;
-       dma_addr_t dma_addr;
-
-       /* set up dma buffer */
-       dma = dma_alloc_coherent(NULL, 0x1000, &dma_addr, GFP_KERNEL);
-       if (dma == 0)
-               return -ENOMEM;
-       mddi->rev_data = dma;
-       mddi->rev_data_curr = 0;
-       mddi->rev_addr = dma_addr;
-       mddi->reg_write_data = dma + MDDI_REV_BUFFER_SIZE;
-       mddi->reg_write_addr = dma_addr + MDDI_REV_BUFFER_SIZE;
-       mddi->reg_read_data = mddi->reg_write_data + 1;
-       mddi->reg_read_addr = mddi->reg_write_addr +
-                             sizeof(*mddi->reg_write_data);
-       return 0;
-}
-
-static int mddi_probe(struct platform_device *pdev)
-{
-       struct msm_mddi_platform_data *pdata = pdev->dev.platform_data;
-       struct mddi_info *mddi = &mddi_info[pdev->id];
-       struct resource *resource;
-       int ret, i;
-
-       resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       if (!resource) {
-               printk(KERN_ERR "mddi: no associated mem resource!\n");
-               return -ENOMEM;
-       }
-       mddi->base = ioremap(resource->start, resource_size(resource));
-       if (!mddi->base) {
-               printk(KERN_ERR "mddi: failed to remap base!\n");
-               ret = -EINVAL;
-               goto error_ioremap;
-       }
-       resource = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
-       if (!resource) {
-               printk(KERN_ERR "mddi: no associated irq resource!\n");
-               ret = -EINVAL;
-               goto error_get_irq_resource;
-       }
-       mddi->irq = resource->start;
-       printk(KERN_INFO "mddi: init() base=0x%p irq=%d\n", mddi->base,
-              mddi->irq);
-       mddi->power_client = pdata->power_client;
-
-       mutex_init(&mddi->reg_write_lock);
-       mutex_init(&mddi->reg_read_lock);
-       spin_lock_init(&mddi->int_lock);
-       init_waitqueue_head(&mddi->int_wait);
-
-       ret = mddi_clk_setup(pdev, mddi, pdata->clk_rate);
-       if (ret) {
-               printk(KERN_ERR "mddi: failed to setup clock!\n");
-               goto error_clk_setup;
-       }
-
-       ret = mddi_rev_data_setup(mddi);
-       if (ret) {
-               printk(KERN_ERR "mddi: failed to setup rev data!\n");
-               goto error_rev_data;
-       }
-
-       mddi->int_enable = 0;
-       mddi_writel(mddi->int_enable, INTEN);
-       ret = request_irq(mddi->irq, mddi_isr, 0, "mddi",
-                         &mddi->client_data);
-       if (ret) {
-               printk(KERN_ERR "mddi: failed to request enable irq!\n");
-               goto error_request_irq;
-       }
-
-       /* turn on the mddi client bridge chip */
-       if (mddi->power_client)
-               mddi->power_client(&mddi->client_data, 1);
-
-       /* initialize the mddi registers */
-       mddi_set_auto_hibernate(&mddi->client_data, 0);
-       mddi_writel(MDDI_CMD_RESET, CMD);
-       mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND);
-       mddi->version = mddi_init_registers(mddi);
-       if (mddi->version < 0x20) {
-               printk(KERN_ERR "mddi: unsupported version 0x%x\n",
-                      mddi->version);
-               ret = -ENODEV;
-               goto error_mddi_version;
-       }
-
-       /* read the capabilities off the client */
-       if (!mddi_get_client_caps(mddi)) {
-               printk(KERN_INFO "mddi: no client found\n");
-               /* power down the panel */
-               mddi_writel(MDDI_CMD_POWERDOWN, CMD);
-               printk(KERN_INFO "mddi powerdown: stat %x\n", mddi_readl(STAT));
-               msleep(100);
-               printk(KERN_INFO "mddi powerdown: stat %x\n", mddi_readl(STAT));
-               return 0;
-       }
-       mddi_set_auto_hibernate(&mddi->client_data, 1);
-
-       if (mddi->caps.Mfr_Name == 0 && mddi->caps.Product_Code == 0)
-               pdata->fixup(&mddi->caps.Mfr_Name, &mddi->caps.Product_Code);
-
-       mddi->client_pdev.id = 0;
-       for (i = 0; i < pdata->num_clients; i++) {
-               if (pdata->client_platform_data[i].product_id ==
-                   (mddi->caps.Mfr_Name << 16 | mddi->caps.Product_Code)) {
-                       mddi->client_data.private_client_data =
-                               pdata->client_platform_data[i].client_data;
-                       mddi->client_pdev.name =
-                               pdata->client_platform_data[i].name;
-                       mddi->client_pdev.id =
-                               pdata->client_platform_data[i].id;
-                       /* XXX: possibly set clock */
-                       break;
-               }
-       }
-
-       if (i >= pdata->num_clients)
-               mddi->client_pdev.name = "mddi_c_dummy";
-       printk(KERN_INFO "mddi: registering panel %s\n",
-               mddi->client_pdev.name);
-
-       mddi->client_data.suspend = mddi_suspend;
-       mddi->client_data.resume = mddi_resume;
-       mddi->client_data.activate_link = mddi_activate_link;
-       mddi->client_data.remote_write = mddi_remote_write;
-       mddi->client_data.remote_read = mddi_remote_read;
-       mddi->client_data.auto_hibernate = mddi_set_auto_hibernate;
-       mddi->client_data.fb_resource = pdata->fb_resource;
-       if (pdev->id == 0)
-               mddi->client_data.interface_type = MSM_MDDI_PMDH_INTERFACE;
-       else if (pdev->id == 1)
-               mddi->client_data.interface_type = MSM_MDDI_EMDH_INTERFACE;
-       else {
-               printk(KERN_ERR "mddi: can not determine interface %d!\n",
-                      pdev->id);
-               ret = -EINVAL;
-               goto error_mddi_interface;
-       }
-
-       mddi->client_pdev.dev.platform_data = &mddi->client_data;
-       printk(KERN_INFO "mddi: publish: %s\n", mddi->client_name);
-       platform_device_register(&mddi->client_pdev);
-       return 0;
-
-error_mddi_interface:
-error_mddi_version:
-       free_irq(mddi->irq, 0);
-error_request_irq:
-       dma_free_coherent(NULL, 0x1000, mddi->rev_data, mddi->rev_addr);
-error_rev_data:
-error_clk_setup:
-error_get_irq_resource:
-       iounmap(mddi->base);
-error_ioremap:
-
-       printk(KERN_INFO "mddi: mddi_init() failed (%d)\n", ret);
-       return ret;
-}
-
-
-static struct platform_driver mddi_driver = {
-       .probe = mddi_probe,
-       .driver = { .name = "msm_mddi" },
-};
-
-static int __init _mddi_init(void)
-{
-       return platform_driver_register(&mddi_driver);
-}
-
-module_init(_mddi_init);
diff --git a/drivers/video/fbdev/msm/mddi_client_dummy.c b/drivers/video/fbdev/msm/mddi_client_dummy.c
deleted file mode 100644 (file)
index cdb8f69..0000000
+++ /dev/null
@@ -1,85 +0,0 @@
-/* drivers/video/msm_fb/mddi_client_dummy.c
- *
- * Support for "dummy" mddi client devices which require no
- * special initialization code.
- *
- * Copyright (C) 2007 Google Incorporated
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- */
-
-#include <linux/device.h>
-#include <linux/slab.h>
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/platform_device.h>
-
-#include <linux/platform_data/video-msm_fb.h>
-
-struct panel_info {
-       struct platform_device pdev;
-       struct msm_panel_data panel_data;
-};
-
-static int mddi_dummy_suspend(struct msm_panel_data *panel_data)
-{
-       return 0;
-}
-
-static int mddi_dummy_resume(struct msm_panel_data *panel_data)
-{
-       return 0;
-}
-
-static int mddi_dummy_blank(struct msm_panel_data *panel_data)
-{
-       return 0;
-}
-
-static int mddi_dummy_unblank(struct msm_panel_data *panel_data)
-{
-       return 0;
-}
-
-static int mddi_dummy_probe(struct platform_device *pdev)
-{
-       struct msm_mddi_client_data *client_data = pdev->dev.platform_data;
-       struct panel_info *panel =
-               devm_kzalloc(&pdev->dev, sizeof(struct panel_info), GFP_KERNEL);
-       if (!panel)
-               return -ENOMEM;
-       platform_set_drvdata(pdev, panel);
-       panel->panel_data.suspend = mddi_dummy_suspend;
-       panel->panel_data.resume = mddi_dummy_resume;
-       panel->panel_data.blank = mddi_dummy_blank;
-       panel->panel_data.unblank = mddi_dummy_unblank;
-       panel->panel_data.caps = MSMFB_CAP_PARTIAL_UPDATES;
-       panel->pdev.name = "msm_panel";
-       panel->pdev.id = pdev->id;
-       platform_device_add_resources(&panel->pdev,
-                                     client_data->fb_resource, 1);
-       panel->panel_data.fb_data = client_data->private_client_data;
-       panel->pdev.dev.platform_data = &panel->panel_data;
-       return platform_device_register(&panel->pdev);
-}
-
-static struct platform_driver mddi_client_dummy = {
-       .probe = mddi_dummy_probe,
-       .driver = { .name = "mddi_c_dummy" },
-};
-
-static int __init mddi_client_dummy_init(void)
-{
-       platform_driver_register(&mddi_client_dummy);
-       return 0;
-}
-
-module_init(mddi_client_dummy_init);
-
diff --git a/drivers/video/fbdev/msm/mddi_client_nt35399.c b/drivers/video/fbdev/msm/mddi_client_nt35399.c
deleted file mode 100644 (file)
index f96df32..0000000
+++ /dev/null
@@ -1,252 +0,0 @@
-/* drivers/video/msm_fb/mddi_client_nt35399.c
- *
- * Support for Novatek NT35399 MDDI client of Sapphire
- *
- * Copyright (C) 2008 HTC Incorporated
- * Author: Solomon Chiu (solomon_chiu@htc.com)
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/platform_device.h>
-#include <linux/interrupt.h>
-#include <linux/sched.h>
-#include <linux/gpio.h>
-#include <linux/slab.h>
-#include <linux/platform_data/video-msm_fb.h>
-
-static DECLARE_WAIT_QUEUE_HEAD(nt35399_vsync_wait);
-
-struct panel_info {
-       struct msm_mddi_client_data *client_data;
-       struct platform_device pdev;
-       struct msm_panel_data panel_data;
-       struct msmfb_callback *fb_callback;
-       struct work_struct panel_work;
-       struct workqueue_struct *fb_wq;
-       int nt35399_got_int;
-};
-
-static void
-nt35399_request_vsync(struct msm_panel_data *panel_data,
-                     struct msmfb_callback *callback)
-{
-       struct panel_info *panel = container_of(panel_data, struct panel_info,
-                                               panel_data);
-       struct msm_mddi_client_data *client_data = panel->client_data;
-
-       panel->fb_callback = callback;
-       if (panel->nt35399_got_int) {
-               panel->nt35399_got_int = 0;
-               client_data->activate_link(client_data); /* clears interrupt */
-       }
-}
-
-static void nt35399_wait_vsync(struct msm_panel_data *panel_data)
-{
-       struct panel_info *panel = container_of(panel_data, struct panel_info,
-                                               panel_data);
-       struct msm_mddi_client_data *client_data = panel->client_data;
-
-       if (panel->nt35399_got_int) {
-               panel->nt35399_got_int = 0;
-               client_data->activate_link(client_data); /* clears interrupt */
-       }
-
-       if (wait_event_timeout(nt35399_vsync_wait, panel->nt35399_got_int,
-                               HZ/2) == 0)
-               printk(KERN_ERR "timeout waiting for VSYNC\n");
-
-       panel->nt35399_got_int = 0;
-       /* interrupt clears when screen dma starts */
-}
-
-static int nt35399_suspend(struct msm_panel_data *panel_data)
-{
-       struct panel_info *panel = container_of(panel_data, struct panel_info,
-                                               panel_data);
-       struct msm_mddi_client_data *client_data = panel->client_data;
-
-       struct msm_mddi_bridge_platform_data *bridge_data =
-               client_data->private_client_data;
-       int ret;
-
-       ret = bridge_data->uninit(bridge_data, client_data);
-       if (ret) {
-               printk(KERN_INFO "mddi nt35399 client: non zero return from "
-                       "uninit\n");
-               return ret;
-       }
-       client_data->suspend(client_data);
-       return 0;
-}
-
-static int nt35399_resume(struct msm_panel_data *panel_data)
-{
-       struct panel_info *panel = container_of(panel_data, struct panel_info,
-                                               panel_data);
-       struct msm_mddi_client_data *client_data = panel->client_data;
-
-       struct msm_mddi_bridge_platform_data *bridge_data =
-               client_data->private_client_data;
-       int ret;
-
-       client_data->resume(client_data);
-       ret = bridge_data->init(bridge_data, client_data);
-       if (ret)
-               return ret;
-       return 0;
-}
-
-static int nt35399_blank(struct msm_panel_data *panel_data)
-{
-       struct panel_info *panel = container_of(panel_data, struct panel_info,
-                                               panel_data);
-       struct msm_mddi_client_data *client_data = panel->client_data;
-       struct msm_mddi_bridge_platform_data *bridge_data =
-               client_data->private_client_data;
-
-       return bridge_data->blank(bridge_data, client_data);
-}
-
-static int nt35399_unblank(struct msm_panel_data *panel_data)
-{
-       struct panel_info *panel = container_of(panel_data, struct panel_info,
-                                               panel_data);
-       struct msm_mddi_client_data *client_data = panel->client_data;
-       struct msm_mddi_bridge_platform_data *bridge_data =
-               client_data->private_client_data;
-
-       return bridge_data->unblank(bridge_data, client_data);
-}
-
-irqreturn_t nt35399_vsync_interrupt(int irq, void *data)
-{
-       struct panel_info *panel = data;
-
-       panel->nt35399_got_int = 1;
-
-       if (panel->fb_callback) {
-               panel->fb_callback->func(panel->fb_callback);
-               panel->fb_callback = NULL;
-       }
-
-       wake_up(&nt35399_vsync_wait);
-
-       return IRQ_HANDLED;
-}
-
-static int setup_vsync(struct panel_info *panel, int init)
-{
-       int ret;
-       int gpio = 97;
-       unsigned int irq;
-
-       if (!init) {
-               ret = 0;
-               goto uninit;
-       }
-       ret = gpio_request_one(gpio, GPIOF_IN, "vsync");
-       if (ret)
-               goto err_request_gpio_failed;
-
-       ret = irq = gpio_to_irq(gpio);
-       if (ret < 0)
-               goto err_get_irq_num_failed;
-
-       ret = request_irq(irq, nt35399_vsync_interrupt, IRQF_TRIGGER_RISING,
-                         "vsync", panel);
-       if (ret)
-               goto err_request_irq_failed;
-
-       printk(KERN_INFO "vsync on gpio %d now %d\n",
-              gpio, gpio_get_value(gpio));
-       return 0;
-
-uninit:
-       free_irq(gpio_to_irq(gpio), panel->client_data);
-err_request_irq_failed:
-err_get_irq_num_failed:
-       gpio_free(gpio);
-err_request_gpio_failed:
-       return ret;
-}
-
-static int mddi_nt35399_probe(struct platform_device *pdev)
-{
-       struct msm_mddi_client_data *client_data = pdev->dev.platform_data;
-       struct msm_mddi_bridge_platform_data *bridge_data =
-               client_data->private_client_data;
-
-       int ret;
-
-       struct panel_info *panel = devm_kzalloc(&pdev->dev,
-                                               sizeof(struct panel_info),
-                                               GFP_KERNEL);
-
-       printk(KERN_DEBUG "%s: enter.\n", __func__);
-
-       if (!panel)
-               return -ENOMEM;
-       platform_set_drvdata(pdev, panel);
-
-       ret = setup_vsync(panel, 1);
-       if (ret) {
-               dev_err(&pdev->dev, "mddi_nt35399_setup_vsync failed\n");
-               return ret;
-       }
-
-       panel->client_data = client_data;
-       panel->panel_data.suspend = nt35399_suspend;
-       panel->panel_data.resume = nt35399_resume;
-       panel->panel_data.wait_vsync = nt35399_wait_vsync;
-       panel->panel_data.request_vsync = nt35399_request_vsync;
-       panel->panel_data.blank = nt35399_blank;
-       panel->panel_data.unblank = nt35399_unblank;
-       panel->panel_data.fb_data = &bridge_data->fb_data;
-       panel->panel_data.caps = 0;
-
-       panel->pdev.name = "msm_panel";
-       panel->pdev.id = pdev->id;
-       panel->pdev.resource = client_data->fb_resource;
-       panel->pdev.num_resources = 1;
-       panel->pdev.dev.platform_data = &panel->panel_data;
-
-       if (bridge_data->init)
-               bridge_data->init(bridge_data, client_data);
-
-       platform_device_register(&panel->pdev);
-
-       return 0;
-}
-
-static int mddi_nt35399_remove(struct platform_device *pdev)
-{
-       struct panel_info *panel = platform_get_drvdata(pdev);
-
-       setup_vsync(panel, 0);
-       return 0;
-}
-
-static struct platform_driver mddi_client_0bda_8a47 = {
-       .probe = mddi_nt35399_probe,
-       .remove = mddi_nt35399_remove,
-       .driver = { .name = "mddi_c_0bda_8a47" },
-};
-
-static int __init mddi_client_nt35399_init(void)
-{
-       return platform_driver_register(&mddi_client_0bda_8a47);
-}
-
-module_init(mddi_client_nt35399_init);
-
diff --git a/drivers/video/fbdev/msm/mddi_client_toshiba.c b/drivers/video/fbdev/msm/mddi_client_toshiba.c
deleted file mode 100644 (file)
index 061d7df..0000000
+++ /dev/null
@@ -1,280 +0,0 @@
-/* drivers/video/msm_fb/mddi_client_toshiba.c
- *
- * Support for Toshiba TC358720XBG mddi client devices which require no
- * special initialization code.
- *
- * Copyright (C) 2007 Google Incorporated
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/platform_device.h>
-#include <linux/interrupt.h>
-#include <linux/gpio.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
-#include <linux/platform_data/video-msm_fb.h>
-
-
-#define LCD_CONTROL_BLOCK_BASE 0x110000
-#define CMN         (LCD_CONTROL_BLOCK_BASE|0x10)
-#define INTFLG      (LCD_CONTROL_BLOCK_BASE|0x18)
-#define HCYCLE      (LCD_CONTROL_BLOCK_BASE|0x34)
-#define HDE_START   (LCD_CONTROL_BLOCK_BASE|0x3C)
-#define VPOS        (LCD_CONTROL_BLOCK_BASE|0xC0)
-#define MPLFBUF     (LCD_CONTROL_BLOCK_BASE|0x20)
-#define WAKEUP      (LCD_CONTROL_BLOCK_BASE|0x54)
-#define WSYN_DLY    (LCD_CONTROL_BLOCK_BASE|0x58)
-#define REGENB      (LCD_CONTROL_BLOCK_BASE|0x5C)
-
-#define BASE5 0x150000
-#define BASE6 0x160000
-#define BASE7 0x170000
-
-#define GPIOIEV     (BASE5 + 0x10)
-#define GPIOIE      (BASE5 + 0x14)
-#define GPIORIS     (BASE5 + 0x18)
-#define GPIOMIS     (BASE5 + 0x1C)
-#define GPIOIC      (BASE5 + 0x20)
-
-#define INTMASK     (BASE6 + 0x0C)
-#define INTMASK_VWAKEOUT (1U << 0)
-#define INTMASK_VWAKEOUT_ACTIVE_LOW (1U << 8)
-#define GPIOSEL     (BASE7 + 0x00)
-#define GPIOSEL_VWAKEINT (1U << 0)
-
-static DECLARE_WAIT_QUEUE_HEAD(toshiba_vsync_wait);
-
-struct panel_info {
-       struct msm_mddi_client_data *client_data;
-       struct platform_device pdev;
-       struct msm_panel_data panel_data;
-       struct msmfb_callback *toshiba_callback;
-       int toshiba_got_int;
-};
-
-
-static void toshiba_request_vsync(struct msm_panel_data *panel_data,
-                                 struct msmfb_callback *callback)
-{
-       struct panel_info *panel = container_of(panel_data, struct panel_info,
-                                               panel_data);
-       struct msm_mddi_client_data *client_data = panel->client_data;
-
-       panel->toshiba_callback = callback;
-       if (panel->toshiba_got_int) {
-               panel->toshiba_got_int = 0;
-               client_data->activate_link(client_data);
-       }
-}
-
-static void toshiba_clear_vsync(struct msm_panel_data *panel_data)
-{
-       struct panel_info *panel = container_of(panel_data, struct panel_info,
-                                               panel_data);
-       struct msm_mddi_client_data *client_data = panel->client_data;
-
-       client_data->activate_link(client_data);
-}
-
-static void toshiba_wait_vsync(struct msm_panel_data *panel_data)
-{
-       struct panel_info *panel = container_of(panel_data, struct panel_info,
-                                               panel_data);
-       struct msm_mddi_client_data *client_data = panel->client_data;
-
-       if (panel->toshiba_got_int) {
-               panel->toshiba_got_int = 0;
-               client_data->activate_link(client_data); /* clears interrupt */
-       }
-       if (wait_event_timeout(toshiba_vsync_wait, panel->toshiba_got_int,
-                               HZ/2) == 0)
-               printk(KERN_ERR "timeout waiting for VSYNC\n");
-       panel->toshiba_got_int = 0;
-       /* interrupt clears when screen dma starts */
-}
-
-static int toshiba_suspend(struct msm_panel_data *panel_data)
-{
-       struct panel_info *panel = container_of(panel_data, struct panel_info,
-                                               panel_data);
-       struct msm_mddi_client_data *client_data = panel->client_data;
-
-       struct msm_mddi_bridge_platform_data *bridge_data =
-               client_data->private_client_data;
-       int ret;
-
-       ret = bridge_data->uninit(bridge_data, client_data);
-       if (ret) {
-               printk(KERN_INFO "mddi toshiba client: non zero return from "
-                       "uninit\n");
-               return ret;
-       }
-       client_data->suspend(client_data);
-       return 0;
-}
-
-static int toshiba_resume(struct msm_panel_data *panel_data)
-{
-       struct panel_info *panel = container_of(panel_data, struct panel_info,
-                                               panel_data);
-       struct msm_mddi_client_data *client_data = panel->client_data;
-
-       struct msm_mddi_bridge_platform_data *bridge_data =
-               client_data->private_client_data;
-       int ret;
-
-       client_data->resume(client_data);
-       ret = bridge_data->init(bridge_data, client_data);
-       if (ret)
-               return ret;
-       return 0;
-}
-
-static int toshiba_blank(struct msm_panel_data *panel_data)
-{
-       struct panel_info *panel = container_of(panel_data, struct panel_info,
-                                               panel_data);
-       struct msm_mddi_client_data *client_data = panel->client_data;
-       struct msm_mddi_bridge_platform_data *bridge_data =
-               client_data->private_client_data;
-
-       return bridge_data->blank(bridge_data, client_data);
-}
-
-static int toshiba_unblank(struct msm_panel_data *panel_data)
-{
-       struct panel_info *panel = container_of(panel_data, struct panel_info,
-                                               panel_data);
-       struct msm_mddi_client_data *client_data = panel->client_data;
-       struct msm_mddi_bridge_platform_data *bridge_data =
-               client_data->private_client_data;
-
-       return bridge_data->unblank(bridge_data, client_data);
-}
-
-irqreturn_t toshiba_vsync_interrupt(int irq, void *data)
-{
-       struct panel_info *panel = data;
-
-       panel->toshiba_got_int = 1;
-       if (panel->toshiba_callback) {
-               panel->toshiba_callback->func(panel->toshiba_callback);
-               panel->toshiba_callback = 0;
-       }
-       wake_up(&toshiba_vsync_wait);
-       return IRQ_HANDLED;
-}
-
-static int setup_vsync(struct panel_info *panel,
-                      int init)
-{
-       int ret;
-       int gpio = 97;
-       unsigned int irq;
-
-       if (!init) {
-               ret = 0;
-               goto uninit;
-       }
-       ret = gpio_request_one(gpio, GPIOF_IN, "vsync");
-       if (ret)
-               goto err_request_gpio_failed;
-
-       ret = irq = gpio_to_irq(gpio);
-       if (ret < 0)
-               goto err_get_irq_num_failed;
-
-       ret = request_irq(irq, toshiba_vsync_interrupt, IRQF_TRIGGER_RISING,
-                         "vsync", panel);
-       if (ret)
-               goto err_request_irq_failed;
-       printk(KERN_INFO "vsync on gpio %d now %d\n",
-              gpio, gpio_get_value(gpio));
-       return 0;
-
-uninit:
-       free_irq(gpio_to_irq(gpio), panel);
-err_request_irq_failed:
-err_get_irq_num_failed:
-       gpio_free(gpio);
-err_request_gpio_failed:
-       return ret;
-}
-
-static int mddi_toshiba_probe(struct platform_device *pdev)
-{
-       int ret;
-       struct msm_mddi_client_data *client_data = pdev->dev.platform_data;
-       struct msm_mddi_bridge_platform_data *bridge_data =
-               client_data->private_client_data;
-       struct panel_info *panel =
-               kzalloc(sizeof(struct panel_info), GFP_KERNEL);
-       if (!panel)
-               return -ENOMEM;
-       platform_set_drvdata(pdev, panel);
-
-       /* mddi_remote_write(mddi, 0, WAKEUP); */
-       client_data->remote_write(client_data, GPIOSEL_VWAKEINT, GPIOSEL);
-       client_data->remote_write(client_data, INTMASK_VWAKEOUT, INTMASK);
-
-       ret = setup_vsync(panel, 1);
-       if (ret) {
-               dev_err(&pdev->dev, "mddi_bridge_setup_vsync failed\n");
-               return ret;
-       }
-
-       panel->client_data = client_data;
-       panel->panel_data.suspend = toshiba_suspend;
-       panel->panel_data.resume = toshiba_resume;
-       panel->panel_data.wait_vsync = toshiba_wait_vsync;
-       panel->panel_data.request_vsync = toshiba_request_vsync;
-       panel->panel_data.clear_vsync = toshiba_clear_vsync;
-       panel->panel_data.blank = toshiba_blank;
-       panel->panel_data.unblank = toshiba_unblank;
-       panel->panel_data.fb_data =  &bridge_data->fb_data;
-       panel->panel_data.caps = MSMFB_CAP_PARTIAL_UPDATES;
-
-       panel->pdev.name = "msm_panel";
-       panel->pdev.id = pdev->id;
-       panel->pdev.resource = client_data->fb_resource;
-       panel->pdev.num_resources = 1;
-       panel->pdev.dev.platform_data = &panel->panel_data;
-       bridge_data->init(bridge_data, client_data);
-       platform_device_register(&panel->pdev);
-
-       return 0;
-}
-
-static int mddi_toshiba_remove(struct platform_device *pdev)
-{
-       struct panel_info *panel = platform_get_drvdata(pdev);
-
-       setup_vsync(panel, 0);
-       kfree(panel);
-       return 0;
-}
-
-static struct platform_driver mddi_client_d263_0000 = {
-       .probe = mddi_toshiba_probe,
-       .remove = mddi_toshiba_remove,
-       .driver = { .name = "mddi_c_d263_0000" },
-};
-
-static int __init mddi_client_toshiba_init(void)
-{
-       platform_driver_register(&mddi_client_d263_0000);
-       return 0;
-}
-
-module_init(mddi_client_toshiba_init);
-
diff --git a/drivers/video/fbdev/msm/mddi_hw.h b/drivers/video/fbdev/msm/mddi_hw.h
deleted file mode 100644 (file)
index 45cc01f..0000000
+++ /dev/null
@@ -1,305 +0,0 @@
-/* drivers/video/msm_fb/mddi_hw.h
- *
- * MSM MDDI Hardware Registers and Structures
- *
- * Copyright (C) 2007 QUALCOMM Incorporated
- * Copyright (C) 2007 Google Incorporated
- *
- * 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 _MDDI_HW_H_
-#define _MDDI_HW_H_
-
-#include <linux/types.h>
-
-#define MDDI_CMD                0x0000
-#define MDDI_VERSION            0x0004
-#define MDDI_PRI_PTR            0x0008
-#define MDDI_SEC_PTR            0x000c
-#define MDDI_BPS                0x0010
-#define MDDI_SPM                0x0014
-#define MDDI_INT                0x0018
-#define MDDI_INTEN              0x001c
-#define MDDI_REV_PTR            0x0020
-#define MDDI_REV_SIZE           0x0024
-#define MDDI_STAT               0x0028
-#define MDDI_REV_RATE_DIV       0x002c
-#define MDDI_REV_CRC_ERR        0x0030
-#define MDDI_TA1_LEN            0x0034
-#define MDDI_TA2_LEN            0x0038
-#define MDDI_TEST_BUS           0x003c
-#define MDDI_TEST               0x0040
-#define MDDI_REV_PKT_CNT        0x0044
-#define MDDI_DRIVE_HI           0x0048
-#define MDDI_DRIVE_LO           0x004c
-#define MDDI_DISP_WAKE          0x0050
-#define MDDI_REV_ENCAP_SZ       0x0054
-#define MDDI_RTD_VAL            0x0058
-#define MDDI_PAD_CTL            0x0068
-#define MDDI_DRIVER_START_CNT   0x006c
-#define MDDI_NEXT_PRI_PTR       0x0070
-#define MDDI_NEXT_SEC_PTR       0x0074
-#define MDDI_MISR_CTL           0x0078
-#define MDDI_MISR_DATA          0x007c
-#define MDDI_SF_CNT             0x0080
-#define MDDI_MF_CNT             0x0084
-#define MDDI_CURR_REV_PTR       0x0088
-#define MDDI_CORE_VER           0x008c
-
-#define MDDI_INT_PRI_PTR_READ       0x0001
-#define MDDI_INT_SEC_PTR_READ       0x0002
-#define MDDI_INT_REV_DATA_AVAIL     0x0004
-#define MDDI_INT_DISP_REQ           0x0008
-#define MDDI_INT_PRI_UNDERFLOW      0x0010
-#define MDDI_INT_SEC_UNDERFLOW      0x0020
-#define MDDI_INT_REV_OVERFLOW       0x0040
-#define MDDI_INT_CRC_ERROR          0x0080
-#define MDDI_INT_MDDI_IN            0x0100
-#define MDDI_INT_PRI_OVERWRITE      0x0200
-#define MDDI_INT_SEC_OVERWRITE      0x0400
-#define MDDI_INT_REV_OVERWRITE      0x0800
-#define MDDI_INT_DMA_FAILURE        0x1000
-#define MDDI_INT_LINK_ACTIVE        0x2000
-#define MDDI_INT_IN_HIBERNATION     0x4000
-#define MDDI_INT_PRI_LINK_LIST_DONE 0x8000
-#define MDDI_INT_SEC_LINK_LIST_DONE 0x10000
-#define MDDI_INT_NO_CMD_PKTS_PEND   0x20000
-#define MDDI_INT_RTD_FAILURE        0x40000
-#define MDDI_INT_REV_PKT_RECEIVED   0x80000
-#define MDDI_INT_REV_PKTS_AVAIL     0x100000
-
-#define MDDI_INT_NEED_CLEAR ( \
-       MDDI_INT_REV_DATA_AVAIL | \
-       MDDI_INT_PRI_UNDERFLOW | \
-       MDDI_INT_SEC_UNDERFLOW | \
-       MDDI_INT_REV_OVERFLOW | \
-       MDDI_INT_CRC_ERROR | \
-       MDDI_INT_REV_PKT_RECEIVED)
-
-
-#define MDDI_STAT_LINK_ACTIVE        0x0001
-#define MDDI_STAT_NEW_REV_PTR        0x0002
-#define MDDI_STAT_NEW_PRI_PTR        0x0004
-#define MDDI_STAT_NEW_SEC_PTR        0x0008
-#define MDDI_STAT_IN_HIBERNATION     0x0010
-#define MDDI_STAT_PRI_LINK_LIST_DONE 0x0020
-#define MDDI_STAT_SEC_LINK_LIST_DONE 0x0040
-#define MDDI_STAT_PENDING_TIMING_PKT 0x0080
-#define MDDI_STAT_PENDING_REV_ENCAP  0x0100
-#define MDDI_STAT_PENDING_POWERDOWN  0x0200
-#define MDDI_STAT_RTD_MEAS_FAIL      0x0800
-#define MDDI_STAT_CLIENT_WAKEUP_REQ  0x1000
-
-
-#define MDDI_CMD_POWERDOWN           0x0100
-#define MDDI_CMD_POWERUP             0x0200
-#define MDDI_CMD_HIBERNATE           0x0300
-#define MDDI_CMD_RESET               0x0400
-#define MDDI_CMD_DISP_IGNORE         0x0501
-#define MDDI_CMD_DISP_LISTEN         0x0500
-#define MDDI_CMD_SEND_REV_ENCAP      0x0600
-#define MDDI_CMD_GET_CLIENT_CAP      0x0601
-#define MDDI_CMD_GET_CLIENT_STATUS   0x0602
-#define MDDI_CMD_SEND_RTD            0x0700
-#define MDDI_CMD_LINK_ACTIVE         0x0900
-#define MDDI_CMD_PERIODIC_REV_ENCAP  0x0A00
-#define MDDI_CMD_FORCE_NEW_REV_PTR   0x0C00
-
-
-
-#define MDDI_VIDEO_REV_PKT_SIZE              0x40
-#define MDDI_CLIENT_CAPABILITY_REV_PKT_SIZE  0x60
-#define MDDI_MAX_REV_PKT_SIZE                0x60
-
-/* #define MDDI_REV_BUFFER_SIZE 128 */
-#define MDDI_REV_BUFFER_SIZE (MDDI_MAX_REV_PKT_SIZE * 4)
-
-/* MDP sends 256 pixel packets, so lower value hibernates more without
- * significantly increasing latency of waiting for next subframe */
-#define MDDI_HOST_BYTES_PER_SUBFRAME  0x3C00
-#define MDDI_HOST_TA2_LEN       0x000c
-#define MDDI_HOST_REV_RATE_DIV  0x0002
-
-
-struct __attribute__((packed)) mddi_rev_packet {
-       uint16_t length;
-       uint16_t type;
-       uint16_t client_id;
-};
-
-struct __attribute__((packed)) mddi_client_status {
-       uint16_t length;
-       uint16_t type;
-       uint16_t client_id;
-       uint16_t reverse_link_request;  /* bytes needed in rev encap message */
-       uint8_t  crc_error_count;
-       uint8_t  capability_change;
-       uint16_t graphics_busy_flags;
-       uint16_t crc16;
-};
-
-struct __attribute__((packed)) mddi_client_caps {
-       uint16_t length; /* length, exclusive of this field */
-       uint16_t type; /* 66 */
-       uint16_t client_id;
-
-       uint16_t Protocol_Version;
-       uint16_t Minimum_Protocol_Version;
-       uint16_t Data_Rate_Capability;
-       uint8_t  Interface_Type_Capability;
-       uint8_t  Number_of_Alt_Displays;
-       uint16_t PostCal_Data_Rate;
-       uint16_t Bitmap_Width;
-       uint16_t Bitmap_Height;
-       uint16_t Display_Window_Width;
-       uint16_t Display_Window_Height;
-       uint32_t Color_Map_Size;
-       uint16_t Color_Map_RGB_Width;
-       uint16_t RGB_Capability;
-       uint8_t  Monochrome_Capability;
-       uint8_t  Reserved_1;
-       uint16_t Y_Cb_Cr_Capability;
-       uint16_t Bayer_Capability;
-       uint16_t Alpha_Cursor_Image_Planes;
-       uint32_t Client_Feature_Capability_Indicators;
-       uint8_t  Maximum_Video_Frame_Rate_Capability;
-       uint8_t  Minimum_Video_Frame_Rate_Capability;
-       uint16_t Minimum_Sub_frame_Rate;
-       uint16_t Audio_Buffer_Depth;
-       uint16_t Audio_Channel_Capability;
-       uint16_t Audio_Sample_Rate_Capability;
-       uint8_t  Audio_Sample_Resolution;
-       uint8_t  Mic_Audio_Sample_Resolution;
-       uint16_t Mic_Sample_Rate_Capability;
-       uint8_t  Keyboard_Data_Format;
-       uint8_t  pointing_device_data_format;
-       uint16_t content_protection_type;
-       uint16_t Mfr_Name;
-       uint16_t Product_Code;
-       uint16_t Reserved_3;
-       uint32_t Serial_Number;
-       uint8_t  Week_of_Manufacture;
-       uint8_t  Year_of_Manufacture;
-
-       uint16_t crc16;
-} mddi_client_capability_type;
-
-
-struct __attribute__((packed)) mddi_video_stream {
-       uint16_t length;
-       uint16_t type; /* 16 */
-       uint16_t client_id; /* 0 */
-
-       uint16_t video_data_format_descriptor;
-/* format of each pixel in the Pixel Data in the present stream in the
- * present packet.
- * If bits [15:13] = 000 monochrome
- * If bits [15:13] = 001 color pixels (palette).
- * If bits [15:13] = 010 color pixels in raw RGB
- * If bits [15:13] = 011 data in 4:2:2 Y Cb Cr format
- * If bits [15:13] = 100 Bayer pixels
- */
-
-       uint16_t pixel_data_attributes;
-/* interpreted as follows:
- * Bits [1:0] = 11  pixel data is displayed to both eyes
- * Bits [1:0] = 10  pixel data is routed to the left eye only.
- * Bits [1:0] = 01  pixel data is routed to the right eye only.
- * Bits [1:0] = 00  pixel data is routed to the alternate display.
- * Bit 2 is 0  Pixel Data is in the standard progressive format.
- * Bit 2 is 1  Pixel Data is in interlace format.
- * Bit 3 is 0  Pixel Data is in the standard progressive format.
- * Bit 3 is 1  Pixel Data is in alternate pixel format.
- * Bit 4 is 0  Pixel Data is to or from the display frame buffer.
- * Bit 4 is 1  Pixel Data is to or from the camera.
- * Bit 5 is 0  pixel data contains the next consecutive row of pixels.
- * Bit 5 is 1  X Left Edge, Y Top Edge, X Right Edge, Y Bottom Edge,
- *             X Start, and Y Start parameters are not defined and
- *             shall be ignored by the client.
- * Bits [7:6] = 01  Pixel data is written to the offline image buffer.
- * Bits [7:6] = 00  Pixel data is written to the buffer to refresh display.
- * Bits [7:6] = 11  Pixel data is written to all image buffers.
- * Bits [7:6] = 10  Invalid. Reserved for future use.
- * Bits 8 through 11 alternate display number.
- * Bits 12 through 14 are reserved for future use and shall be set to zero.
- * Bit 15 is 1 the row of pixels is the last row of pixels in a frame.
- */
-
-       uint16_t x_left_edge;
-       uint16_t y_top_edge;
-       /* X,Y coordinate of the top left edge of the screen window */
-
-       uint16_t x_right_edge;
-       uint16_t y_bottom_edge;
-       /* X,Y coordinate of the bottom right edge of the window being
-        * updated. */
-
-       uint16_t x_start;
-       uint16_t y_start;
-       /* (X Start, Y Start) is the first pixel in the Pixel Data field
-        * below. */
-
-       uint16_t pixel_count;
-       /* number of pixels in the Pixel Data field below. */
-
-       uint16_t parameter_CRC;
-       /* 16-bit CRC of all bytes from the Packet Length to the Pixel Count. */
-
-       uint16_t reserved;
-       /* 16-bit variable to make structure align on 4 byte boundary */
-};
-
-#define TYPE_VIDEO_STREAM      16
-#define TYPE_CLIENT_CAPS       66
-#define TYPE_REGISTER_ACCESS   146
-#define TYPE_CLIENT_STATUS     70
-
-struct __attribute__((packed)) mddi_register_access {
-       uint16_t length;
-       uint16_t type; /* 146 */
-       uint16_t client_id;
-
-       uint16_t read_write_info;
-       /* Bits 13:0  a 14-bit unsigned integer that specifies the number of
-        *            32-bit Register Data List items to be transferred in the
-        *            Register Data List field.
-        * Bits[15:14] = 00  Write to register(s);
-        * Bits[15:14] = 10  Read from register(s);
-        * Bits[15:14] = 11  Response to a Read.
-        * Bits[15:14] = 01  this value is reserved for future use. */
-#define MDDI_WRITE     (0 << 14)
-#define MDDI_READ      (2 << 14)
-#define MDDI_READ_RESP (3 << 14)
-
-       uint32_t register_address;
-       /* the register address that is to be written to or read from. */
-
-       uint16_t crc16;
-
-       uint32_t register_data_list;
-       /* list of 4-byte register data values for/from client registers */
-};
-
-struct __attribute__((packed)) mddi_llentry {
-       uint16_t flags;
-       uint16_t header_count;
-       uint16_t data_count;
-       dma_addr_t data; /* 32 bit */
-       struct mddi_llentry *next;
-       uint16_t reserved;
-       union {
-               struct mddi_video_stream v;
-               struct mddi_register_access r;
-               uint32_t _[12];
-       } u;
-};
-
-#endif
diff --git a/drivers/video/fbdev/msm/mdp.c b/drivers/video/fbdev/msm/mdp.c
deleted file mode 100644 (file)
index 113c787..0000000
+++ /dev/null
@@ -1,520 +0,0 @@
-/* drivers/video/msm_fb/mdp.c
- *
- * MSM MDP Interface (used by framebuffer core)
- *
- * Copyright (C) 2007 QUALCOMM Incorporated
- * Copyright (C) 2007 Google Incorporated
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- */
-
-#include <linux/kernel.h>
-#include <linux/fb.h>
-#include <linux/msm_mdp.h>
-#include <linux/interrupt.h>
-#include <linux/wait.h>
-#include <linux/clk.h>
-#include <linux/file.h>
-#include <linux/major.h>
-#include <linux/slab.h>
-
-#include <linux/platform_data/video-msm_fb.h>
-#include <linux/platform_device.h>
-#include <linux/export.h>
-
-#include "mdp_hw.h"
-
-struct class *mdp_class;
-
-#define MDP_CMD_DEBUG_ACCESS_BASE (0x10000)
-
-static uint16_t mdp_default_ccs[] = {
-       0x254, 0x000, 0x331, 0x254, 0xF38, 0xE61, 0x254, 0x409, 0x000,
-       0x010, 0x080, 0x080
-};
-
-static DECLARE_WAIT_QUEUE_HEAD(mdp_dma2_waitqueue);
-static DECLARE_WAIT_QUEUE_HEAD(mdp_ppp_waitqueue);
-static struct msmfb_callback *dma_callback;
-static struct clk *clk;
-static unsigned int mdp_irq_mask;
-static DEFINE_SPINLOCK(mdp_lock);
-DEFINE_MUTEX(mdp_mutex);
-
-static int enable_mdp_irq(struct mdp_info *mdp, uint32_t mask)
-{
-       unsigned long irq_flags;
-       int ret = 0;
-
-       BUG_ON(!mask);
-
-       spin_lock_irqsave(&mdp_lock, irq_flags);
-       /* if the mask bits are already set return an error, this interrupt
-        * is already enabled */
-       if (mdp_irq_mask & mask) {
-               printk(KERN_ERR "mdp irq already on already on %x %x\n",
-                      mdp_irq_mask, mask);
-               ret = -1;
-       }
-       /* if the mdp irq is not already enabled enable it */
-       if (!mdp_irq_mask) {
-               if (clk)
-                       clk_enable(clk);
-               enable_irq(mdp->irq);
-       }
-
-       /* update the irq mask to reflect the fact that the interrupt is
-        * enabled */
-       mdp_irq_mask |= mask;
-       spin_unlock_irqrestore(&mdp_lock, irq_flags);
-       return ret;
-}
-
-static int locked_disable_mdp_irq(struct mdp_info *mdp, uint32_t mask)
-{
-       /* this interrupt is already disabled! */
-       if (!(mdp_irq_mask & mask)) {
-               printk(KERN_ERR "mdp irq already off %x %x\n",
-                      mdp_irq_mask, mask);
-               return -1;
-       }
-       /* update the irq mask to reflect the fact that the interrupt is
-        * disabled */
-       mdp_irq_mask &= ~(mask);
-       /* if no one is waiting on the interrupt, disable it */
-       if (!mdp_irq_mask) {
-               disable_irq_nosync(mdp->irq);
-               if (clk)
-                       clk_disable(clk);
-       }
-       return 0;
-}
-
-static int disable_mdp_irq(struct mdp_info *mdp, uint32_t mask)
-{
-       unsigned long irq_flags;
-       int ret;
-
-       spin_lock_irqsave(&mdp_lock, irq_flags);
-       ret = locked_disable_mdp_irq(mdp, mask);
-       spin_unlock_irqrestore(&mdp_lock, irq_flags);
-       return ret;
-}
-
-static irqreturn_t mdp_isr(int irq, void *data)
-{
-       uint32_t status;
-       unsigned long irq_flags;
-       struct mdp_info *mdp = data;
-
-       spin_lock_irqsave(&mdp_lock, irq_flags);
-
-       status = mdp_readl(mdp, MDP_INTR_STATUS);
-       mdp_writel(mdp, status, MDP_INTR_CLEAR);
-
-       status &= mdp_irq_mask;
-       if (status & DL0_DMA2_TERM_DONE) {
-               if (dma_callback) {
-                       dma_callback->func(dma_callback);
-                       dma_callback = NULL;
-               }
-               wake_up(&mdp_dma2_waitqueue);
-       }
-
-       if (status & DL0_ROI_DONE)
-               wake_up(&mdp_ppp_waitqueue);
-
-       if (status)
-               locked_disable_mdp_irq(mdp, status);
-
-       spin_unlock_irqrestore(&mdp_lock, irq_flags);
-       return IRQ_HANDLED;
-}
-
-static uint32_t mdp_check_mask(uint32_t mask)
-{
-       uint32_t ret;
-       unsigned long irq_flags;
-
-       spin_lock_irqsave(&mdp_lock, irq_flags);
-       ret = mdp_irq_mask & mask;
-       spin_unlock_irqrestore(&mdp_lock, irq_flags);
-       return ret;
-}
-
-static int mdp_wait(struct mdp_info *mdp, uint32_t mask, wait_queue_head_t *wq)
-{
-       int ret = 0;
-       unsigned long irq_flags;
-
-       wait_event_timeout(*wq, !mdp_check_mask(mask), HZ);
-
-       spin_lock_irqsave(&mdp_lock, irq_flags);
-       if (mdp_irq_mask & mask) {
-               locked_disable_mdp_irq(mdp, mask);
-               printk(KERN_WARNING "timeout waiting for mdp to complete %x\n",
-                      mask);
-               ret = -ETIMEDOUT;
-       }
-       spin_unlock_irqrestore(&mdp_lock, irq_flags);
-
-       return ret;
-}
-
-void mdp_dma_wait(struct mdp_device *mdp_dev)
-{
-#define MDP_MAX_TIMEOUTS 20
-       static int timeout_count;
-       struct mdp_info *mdp = container_of(mdp_dev, struct mdp_info, mdp_dev);
-
-       if (mdp_wait(mdp, DL0_DMA2_TERM_DONE, &mdp_dma2_waitqueue) == -ETIMEDOUT)
-               timeout_count++;
-       else
-               timeout_count = 0;
-
-       if (timeout_count > MDP_MAX_TIMEOUTS) {
-               printk(KERN_ERR "mdp: dma failed %d times, somethings wrong!\n",
-                      MDP_MAX_TIMEOUTS);
-               BUG();
-       }
-}
-
-static int mdp_ppp_wait(struct mdp_info *mdp)
-{
-       return mdp_wait(mdp, DL0_ROI_DONE, &mdp_ppp_waitqueue);
-}
-
-void mdp_dma_to_mddi(struct mdp_info *mdp, uint32_t addr, uint32_t stride,
-                    uint32_t width, uint32_t height, uint32_t x, uint32_t y,
-                    struct msmfb_callback *callback)
-{
-       uint32_t dma2_cfg;
-       uint16_t ld_param = 0; /* 0=PRIM, 1=SECD, 2=EXT */
-
-       if (enable_mdp_irq(mdp, DL0_DMA2_TERM_DONE)) {
-               printk(KERN_ERR "mdp_dma_to_mddi: busy\n");
-               return;
-       }
-
-       dma_callback = callback;
-
-       dma2_cfg = DMA_PACK_TIGHT |
-               DMA_PACK_ALIGN_LSB |
-               DMA_PACK_PATTERN_RGB |
-               DMA_OUT_SEL_AHB |
-               DMA_IBUF_NONCONTIGUOUS;
-
-       dma2_cfg |= DMA_IBUF_FORMAT_RGB565;
-
-       dma2_cfg |= DMA_OUT_SEL_MDDI;
-
-       dma2_cfg |= DMA_MDDI_DMAOUT_LCD_SEL_PRIMARY;
-
-       dma2_cfg |= DMA_DITHER_EN;
-
-       /* setup size, address, and stride */
-       mdp_writel(mdp, (height << 16) | (width),
-                  MDP_CMD_DEBUG_ACCESS_BASE + 0x0184);
-       mdp_writel(mdp, addr, MDP_CMD_DEBUG_ACCESS_BASE + 0x0188);
-       mdp_writel(mdp, stride, MDP_CMD_DEBUG_ACCESS_BASE + 0x018C);
-
-       /* 666 18BPP */
-       dma2_cfg |= DMA_DSTC0G_6BITS | DMA_DSTC1B_6BITS | DMA_DSTC2R_6BITS;
-
-       /* set y & x offset and MDDI transaction parameters */
-       mdp_writel(mdp, (y << 16) | (x), MDP_CMD_DEBUG_ACCESS_BASE + 0x0194);
-       mdp_writel(mdp, ld_param, MDP_CMD_DEBUG_ACCESS_BASE + 0x01a0);
-       mdp_writel(mdp, (MDDI_VDO_PACKET_DESC << 16) | MDDI_VDO_PACKET_PRIM,
-                  MDP_CMD_DEBUG_ACCESS_BASE + 0x01a4);
-
-       mdp_writel(mdp, dma2_cfg, MDP_CMD_DEBUG_ACCESS_BASE + 0x0180);
-
-       /* start DMA2 */
-       mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0044);
-}
-
-void mdp_dma(struct mdp_device *mdp_dev, uint32_t addr, uint32_t stride,
-            uint32_t width, uint32_t height, uint32_t x, uint32_t y,
-            struct msmfb_callback *callback, int interface)
-{
-       struct mdp_info *mdp = container_of(mdp_dev, struct mdp_info, mdp_dev);
-
-       if (interface == MSM_MDDI_PMDH_INTERFACE) {
-               mdp_dma_to_mddi(mdp, addr, stride, width, height, x, y,
-                               callback);
-       }
-}
-
-int get_img(struct mdp_img *img, struct fb_info *info,
-           unsigned long *start, unsigned long *len,
-           struct file **filep)
-{
-       int ret = 0;
-       struct fd f = fdget(img->memory_id);
-       if (f.file == NULL)
-               return -1;
-
-       if (MAJOR(file_inode(f.file)->i_rdev) == FB_MAJOR) {
-               *start = info->fix.smem_start;
-               *len = info->fix.smem_len;
-       } else
-               ret = -1;
-       fdput(f);
-
-       return ret;
-}
-
-void put_img(struct file *src_file, struct file *dst_file)
-{
-}
-
-int mdp_blit(struct mdp_device *mdp_dev, struct fb_info *fb,
-            struct mdp_blit_req *req)
-{
-       int ret;
-       unsigned long src_start = 0, src_len = 0, dst_start = 0, dst_len = 0;
-       struct mdp_info *mdp = container_of(mdp_dev, struct mdp_info, mdp_dev);
-       struct file *src_file = 0, *dst_file = 0;
-
-       /* WORKAROUND FOR HARDWARE BUG IN BG TILE FETCH */
-       if (unlikely(req->src_rect.h == 0 ||
-                    req->src_rect.w == 0)) {
-               printk(KERN_ERR "mpd_ppp: src img of zero size!\n");
-               return -EINVAL;
-       }
-       if (unlikely(req->dst_rect.h == 0 ||
-                    req->dst_rect.w == 0))
-               return -EINVAL;
-
-       /* do this first so that if this fails, the caller can always
-        * safely call put_img */
-       if (unlikely(get_img(&req->src, fb, &src_start, &src_len, &src_file))) {
-               printk(KERN_ERR "mpd_ppp: could not retrieve src image from "
-                               "memory\n");
-               return -EINVAL;
-       }
-
-       if (unlikely(get_img(&req->dst, fb, &dst_start, &dst_len, &dst_file))) {
-               printk(KERN_ERR "mpd_ppp: could not retrieve dst image from "
-                               "memory\n");
-               return -EINVAL;
-       }
-       mutex_lock(&mdp_mutex);
-
-       /* transp_masking unimplemented */
-       req->transp_mask = MDP_TRANSP_NOP;
-       if (unlikely((req->transp_mask != MDP_TRANSP_NOP ||
-                     req->alpha != MDP_ALPHA_NOP ||
-                     HAS_ALPHA(req->src.format)) &&
-                    (req->flags & MDP_ROT_90 &&
-                     req->dst_rect.w <= 16 && req->dst_rect.h >= 16))) {
-               int i;
-               unsigned int tiles = req->dst_rect.h / 16;
-               unsigned int remainder = req->dst_rect.h % 16;
-               req->src_rect.w = 16*req->src_rect.w / req->dst_rect.h;
-               req->dst_rect.h = 16;
-               for (i = 0; i < tiles; i++) {
-                       enable_mdp_irq(mdp, DL0_ROI_DONE);
-                       ret = mdp_ppp_blit(mdp, req, src_file, src_start,
-                                          src_len, dst_file, dst_start,
-                                          dst_len);
-                       if (ret)
-                               goto err_bad_blit;
-                       ret = mdp_ppp_wait(mdp);
-                       if (ret)
-                               goto err_wait_failed;
-                       req->dst_rect.y += 16;
-                       req->src_rect.x += req->src_rect.w;
-               }
-               if (!remainder)
-                       goto end;
-               req->src_rect.w = remainder*req->src_rect.w / req->dst_rect.h;
-               req->dst_rect.h = remainder;
-       }
-       enable_mdp_irq(mdp, DL0_ROI_DONE);
-       ret = mdp_ppp_blit(mdp, req, src_file, src_start, src_len, dst_file,
-                          dst_start,
-                          dst_len);
-       if (ret)
-               goto err_bad_blit;
-       ret = mdp_ppp_wait(mdp);
-       if (ret)
-               goto err_wait_failed;
-end:
-       put_img(src_file, dst_file);
-       mutex_unlock(&mdp_mutex);
-       return 0;
-err_bad_blit:
-       disable_mdp_irq(mdp, DL0_ROI_DONE);
-err_wait_failed:
-       put_img(src_file, dst_file);
-       mutex_unlock(&mdp_mutex);
-       return ret;
-}
-
-void mdp_set_grp_disp(struct mdp_device *mdp_dev, unsigned disp_id)
-{
-       struct mdp_info *mdp = container_of(mdp_dev, struct mdp_info, mdp_dev);
-
-       disp_id &= 0xf;
-       mdp_writel(mdp, disp_id, MDP_FULL_BYPASS_WORD43);
-}
-
-int register_mdp_client(struct class_interface *cint)
-{
-       if (!mdp_class) {
-               pr_err("mdp: no mdp_class when registering mdp client\n");
-               return -ENODEV;
-       }
-       cint->class = mdp_class;
-       return class_interface_register(cint);
-}
-
-#include "mdp_csc_table.h"
-#include "mdp_scale_tables.h"
-
-int mdp_probe(struct platform_device *pdev)
-{
-       struct resource *resource;
-       int ret;
-       int n;
-       struct mdp_info *mdp;
-
-       resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       if (!resource) {
-               pr_err("mdp: can not get mdp mem resource!\n");
-               return -ENOMEM;
-       }
-
-       mdp = kzalloc(sizeof(struct mdp_info), GFP_KERNEL);
-       if (!mdp)
-               return -ENOMEM;
-
-       mdp->irq = platform_get_irq(pdev, 0);
-       if (mdp->irq < 0) {
-               pr_err("mdp: can not get mdp irq\n");
-               ret = mdp->irq;
-               goto error_get_irq;
-       }
-
-       mdp->base = ioremap(resource->start, resource_size(resource));
-       if (mdp->base == 0) {
-               printk(KERN_ERR "msmfb: cannot allocate mdp regs!\n");
-               ret = -ENOMEM;
-               goto error_ioremap;
-       }
-
-       mdp->mdp_dev.dma = mdp_dma;
-       mdp->mdp_dev.dma_wait = mdp_dma_wait;
-       mdp->mdp_dev.blit = mdp_blit;
-       mdp->mdp_dev.set_grp_disp = mdp_set_grp_disp;
-
-       clk = clk_get(&pdev->dev, "mdp_clk");
-       if (IS_ERR(clk)) {
-               printk(KERN_INFO "mdp: failed to get mdp clk");
-               ret = PTR_ERR(clk);
-               goto error_get_clk;
-       }
-
-       ret = request_irq(mdp->irq, mdp_isr, 0, "msm_mdp", mdp);
-       if (ret)
-               goto error_request_irq;
-       disable_irq(mdp->irq);
-       mdp_irq_mask = 0;
-
-       /* debug interface write access */
-       mdp_writel(mdp, 1, 0x60);
-
-       mdp_writel(mdp, MDP_ANY_INTR_MASK, MDP_INTR_ENABLE);
-       mdp_writel(mdp, 1, MDP_EBI2_PORTMAP_MODE);
-
-       mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x01f8);
-       mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x01fc);
-
-       for (n = 0; n < ARRAY_SIZE(csc_table); n++)
-               mdp_writel(mdp, csc_table[n].val, csc_table[n].reg);
-
-       /* clear up unused fg/main registers */
-       /* comp.plane 2&3 ystride */
-       mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0120);
-
-       /* unpacked pattern */
-       mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x012c);
-       mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0130);
-       mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0134);
-       mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0158);
-       mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x015c);
-       mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0160);
-       mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0170);
-       mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0174);
-       mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x017c);
-
-       /* comp.plane 2 & 3 */
-       mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0114);
-       mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0118);
-
-       /* clear unused bg registers */
-       mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x01c8);
-       mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x01d0);
-       mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x01dc);
-       mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x01e0);
-       mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x01e4);
-
-       for (n = 0; n < ARRAY_SIZE(mdp_upscale_table); n++)
-               mdp_writel(mdp, mdp_upscale_table[n].val,
-                      mdp_upscale_table[n].reg);
-
-       for (n = 0; n < 9; n++)
-               mdp_writel(mdp, mdp_default_ccs[n], 0x40440 + 4 * n);
-       mdp_writel(mdp, mdp_default_ccs[9], 0x40500 + 4 * 0);
-       mdp_writel(mdp, mdp_default_ccs[10], 0x40500 + 4 * 0);
-       mdp_writel(mdp, mdp_default_ccs[11], 0x40500 + 4 * 0);
-
-       /* register mdp device */
-       mdp->mdp_dev.dev.parent = &pdev->dev;
-       mdp->mdp_dev.dev.class = mdp_class;
-       dev_set_name(&mdp->mdp_dev.dev, "mdp%d", pdev->id);
-
-       /* if you can remove the platform device you'd have to implement
-        * this:
-       mdp_dev.release = mdp_class; */
-
-       ret = device_register(&mdp->mdp_dev.dev);
-       if (ret)
-               goto error_device_register;
-       return 0;
-
-error_device_register:
-       free_irq(mdp->irq, mdp);
-error_request_irq:
-error_get_clk:
-       iounmap(mdp->base);
-error_get_irq:
-error_ioremap:
-       kfree(mdp);
-       return ret;
-}
-
-static struct platform_driver msm_mdp_driver = {
-       .probe = mdp_probe,
-       .driver = {.name = "msm_mdp"},
-};
-
-static int __init mdp_init(void)
-{
-       mdp_class = class_create(THIS_MODULE, "msm_mdp");
-       if (IS_ERR(mdp_class)) {
-               printk(KERN_ERR "Error creating mdp class\n");
-               return PTR_ERR(mdp_class);
-       }
-       return platform_driver_register(&msm_mdp_driver);
-}
-
-subsys_initcall(mdp_init);
diff --git a/drivers/video/fbdev/msm/mdp_csc_table.h b/drivers/video/fbdev/msm/mdp_csc_table.h
deleted file mode 100644 (file)
index d1cde30..0000000
+++ /dev/null
@@ -1,582 +0,0 @@
-/* drivers/video/msm_fb/mdp_csc_table.h
- *
- * Copyright (C) 2007 QUALCOMM Incorporated
- * Copyright (C) 2007 Google Incorporated
- *
- * 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.
- */
-
-static struct {
-       uint32_t reg;
-       uint32_t val;
-} csc_table[] = {
-       { 0x40400, 0x83 },
-       { 0x40404, 0x102 },
-       { 0x40408, 0x32 },
-       { 0x4040c, 0xffffffb5 },
-       { 0x40410, 0xffffff6c },
-       { 0x40414, 0xe1 },
-       { 0x40418, 0xe1 },
-       { 0x4041c, 0xffffff45 },
-       { 0x40420, 0xffffffdc },
-       { 0x40440, 0x254 },
-       { 0x40444, 0x0 },
-       { 0x40448, 0x331 },
-       { 0x4044c, 0x254 },
-       { 0x40450, 0xffffff38 },
-       { 0x40454, 0xfffffe61 },
-       { 0x40458, 0x254 },
-       { 0x4045c, 0x409 },
-       { 0x40460, 0x0 },
-       { 0x40480, 0x5d },
-       { 0x40484, 0x13a },
-       { 0x40488, 0x20 },
-       { 0x4048c, 0xffffffcd },
-       { 0x40490, 0xffffff54 },
-       { 0x40494, 0xe1 },
-       { 0x40498, 0xe1 },
-       { 0x4049c, 0xffffff35 },
-       { 0x404a0, 0xffffffec },
-       { 0x404c0, 0x254 },
-       { 0x404c4, 0x0 },
-       { 0x404c8, 0x396 },
-       { 0x404cc, 0x254 },
-       { 0x404d0, 0xffffff94 },
-       { 0x404d4, 0xfffffef0 },
-       { 0x404d8, 0x254 },
-       { 0x404dc, 0x43a },
-       { 0x404e0, 0x0 },
-       { 0x40500, 0x10 },
-       { 0x40504, 0x80 },
-       { 0x40508, 0x80 },
-       { 0x40540, 0x10 },
-       { 0x40544, 0x80 },
-       { 0x40548, 0x80 },
-       { 0x40580, 0x10 },
-       { 0x40584, 0xeb },
-       { 0x40588, 0x10 },
-       { 0x4058c, 0xf0 },
-       { 0x405c0, 0x10 },
-       { 0x405c4, 0xeb },
-       { 0x405c8, 0x10 },
-       { 0x405cc, 0xf0 },
-       { 0x40800, 0x0 },
-       { 0x40804, 0x151515 },
-       { 0x40808, 0x1d1d1d },
-       { 0x4080c, 0x232323 },
-       { 0x40810, 0x272727 },
-       { 0x40814, 0x2b2b2b },
-       { 0x40818, 0x2f2f2f },
-       { 0x4081c, 0x333333 },
-       { 0x40820, 0x363636 },
-       { 0x40824, 0x393939 },
-       { 0x40828, 0x3b3b3b },
-       { 0x4082c, 0x3e3e3e },
-       { 0x40830, 0x404040 },
-       { 0x40834, 0x434343 },
-       { 0x40838, 0x454545 },
-       { 0x4083c, 0x474747 },
-       { 0x40840, 0x494949 },
-       { 0x40844, 0x4b4b4b },
-       { 0x40848, 0x4d4d4d },
-       { 0x4084c, 0x4f4f4f },
-       { 0x40850, 0x515151 },
-       { 0x40854, 0x535353 },
-       { 0x40858, 0x555555 },
-       { 0x4085c, 0x565656 },
-       { 0x40860, 0x585858 },
-       { 0x40864, 0x5a5a5a },
-       { 0x40868, 0x5b5b5b },
-       { 0x4086c, 0x5d5d5d },
-       { 0x40870, 0x5e5e5e },
-       { 0x40874, 0x606060 },
-       { 0x40878, 0x616161 },
-       { 0x4087c, 0x636363 },
-       { 0x40880, 0x646464 },
-       { 0x40884, 0x666666 },
-       { 0x40888, 0x676767 },
-       { 0x4088c, 0x686868 },
-       { 0x40890, 0x6a6a6a },
-       { 0x40894, 0x6b6b6b },
-       { 0x40898, 0x6c6c6c },
-       { 0x4089c, 0x6e6e6e },
-       { 0x408a0, 0x6f6f6f },
-       { 0x408a4, 0x707070 },
-       { 0x408a8, 0x717171 },
-       { 0x408ac, 0x727272 },
-       { 0x408b0, 0x747474 },
-       { 0x408b4, 0x757575 },
-       { 0x408b8, 0x767676 },
-       { 0x408bc, 0x777777 },
-       { 0x408c0, 0x787878 },
-       { 0x408c4, 0x797979 },
-       { 0x408c8, 0x7a7a7a },
-       { 0x408cc, 0x7c7c7c },
-       { 0x408d0, 0x7d7d7d },
-       { 0x408d4, 0x7e7e7e },
-       { 0x408d8, 0x7f7f7f },
-       { 0x408dc, 0x808080 },
-       { 0x408e0, 0x818181 },
-       { 0x408e4, 0x828282 },
-       { 0x408e8, 0x838383 },
-       { 0x408ec, 0x848484 },
-       { 0x408f0, 0x858585 },
-       { 0x408f4, 0x868686 },
-       { 0x408f8, 0x878787 },
-       { 0x408fc, 0x888888 },
-       { 0x40900, 0x898989 },
-       { 0x40904, 0x8a8a8a },
-       { 0x40908, 0x8b8b8b },
-       { 0x4090c, 0x8c8c8c },
-       { 0x40910, 0x8d8d8d },
-       { 0x40914, 0x8e8e8e },
-       { 0x40918, 0x8f8f8f },
-       { 0x4091c, 0x8f8f8f },
-       { 0x40920, 0x909090 },
-       { 0x40924, 0x919191 },
-       { 0x40928, 0x929292 },
-       { 0x4092c, 0x939393 },
-       { 0x40930, 0x949494 },
-       { 0x40934, 0x959595 },
-       { 0x40938, 0x969696 },
-       { 0x4093c, 0x969696 },
-       { 0x40940, 0x979797 },
-       { 0x40944, 0x989898 },
-       { 0x40948, 0x999999 },
-       { 0x4094c, 0x9a9a9a },
-       { 0x40950, 0x9b9b9b },
-       { 0x40954, 0x9c9c9c },
-       { 0x40958, 0x9c9c9c },
-       { 0x4095c, 0x9d9d9d },
-       { 0x40960, 0x9e9e9e },
-       { 0x40964, 0x9f9f9f },
-       { 0x40968, 0xa0a0a0 },
-       { 0x4096c, 0xa0a0a0 },
-       { 0x40970, 0xa1a1a1 },
-       { 0x40974, 0xa2a2a2 },
-       { 0x40978, 0xa3a3a3 },
-       { 0x4097c, 0xa4a4a4 },
-       { 0x40980, 0xa4a4a4 },
-       { 0x40984, 0xa5a5a5 },
-       { 0x40988, 0xa6a6a6 },
-       { 0x4098c, 0xa7a7a7 },
-       { 0x40990, 0xa7a7a7 },
-       { 0x40994, 0xa8a8a8 },
-       { 0x40998, 0xa9a9a9 },
-       { 0x4099c, 0xaaaaaa },
-       { 0x409a0, 0xaaaaaa },
-       { 0x409a4, 0xababab },
-       { 0x409a8, 0xacacac },
-       { 0x409ac, 0xadadad },
-       { 0x409b0, 0xadadad },
-       { 0x409b4, 0xaeaeae },
-       { 0x409b8, 0xafafaf },
-       { 0x409bc, 0xafafaf },
-       { 0x409c0, 0xb0b0b0 },
-       { 0x409c4, 0xb1b1b1 },
-       { 0x409c8, 0xb2b2b2 },
-       { 0x409cc, 0xb2b2b2 },
-       { 0x409d0, 0xb3b3b3 },
-       { 0x409d4, 0xb4b4b4 },
-       { 0x409d8, 0xb4b4b4 },
-       { 0x409dc, 0xb5b5b5 },
-       { 0x409e0, 0xb6b6b6 },
-       { 0x409e4, 0xb6b6b6 },
-       { 0x409e8, 0xb7b7b7 },
-       { 0x409ec, 0xb8b8b8 },
-       { 0x409f0, 0xb8b8b8 },
-       { 0x409f4, 0xb9b9b9 },
-       { 0x409f8, 0xbababa },
-       { 0x409fc, 0xbababa },
-       { 0x40a00, 0xbbbbbb },
-       { 0x40a04, 0xbcbcbc },
-       { 0x40a08, 0xbcbcbc },
-       { 0x40a0c, 0xbdbdbd },
-       { 0x40a10, 0xbebebe },
-       { 0x40a14, 0xbebebe },
-       { 0x40a18, 0xbfbfbf },
-       { 0x40a1c, 0xc0c0c0 },
-       { 0x40a20, 0xc0c0c0 },
-       { 0x40a24, 0xc1c1c1 },
-       { 0x40a28, 0xc1c1c1 },
-       { 0x40a2c, 0xc2c2c2 },
-       { 0x40a30, 0xc3c3c3 },
-       { 0x40a34, 0xc3c3c3 },
-       { 0x40a38, 0xc4c4c4 },
-       { 0x40a3c, 0xc5c5c5 },
-       { 0x40a40, 0xc5c5c5 },
-       { 0x40a44, 0xc6c6c6 },
-       { 0x40a48, 0xc6c6c6 },
-       { 0x40a4c, 0xc7c7c7 },
-       { 0x40a50, 0xc8c8c8 },
-       { 0x40a54, 0xc8c8c8 },
-       { 0x40a58, 0xc9c9c9 },
-       { 0x40a5c, 0xc9c9c9 },
-       { 0x40a60, 0xcacaca },
-       { 0x40a64, 0xcbcbcb },
-       { 0x40a68, 0xcbcbcb },
-       { 0x40a6c, 0xcccccc },
-       { 0x40a70, 0xcccccc },
-       { 0x40a74, 0xcdcdcd },
-       { 0x40a78, 0xcecece },
-       { 0x40a7c, 0xcecece },
-       { 0x40a80, 0xcfcfcf },
-       { 0x40a84, 0xcfcfcf },
-       { 0x40a88, 0xd0d0d0 },
-       { 0x40a8c, 0xd0d0d0 },
-       { 0x40a90, 0xd1d1d1 },
-       { 0x40a94, 0xd2d2d2 },
-       { 0x40a98, 0xd2d2d2 },
-       { 0x40a9c, 0xd3d3d3 },
-       { 0x40aa0, 0xd3d3d3 },
-       { 0x40aa4, 0xd4d4d4 },
-       { 0x40aa8, 0xd4d4d4 },
-       { 0x40aac, 0xd5d5d5 },
-       { 0x40ab0, 0xd6d6d6 },
-       { 0x40ab4, 0xd6d6d6 },
-       { 0x40ab8, 0xd7d7d7 },
-       { 0x40abc, 0xd7d7d7 },
-       { 0x40ac0, 0xd8d8d8 },
-       { 0x40ac4, 0xd8d8d8 },
-       { 0x40ac8, 0xd9d9d9 },
-       { 0x40acc, 0xd9d9d9 },
-       { 0x40ad0, 0xdadada },
-       { 0x40ad4, 0xdbdbdb },
-       { 0x40ad8, 0xdbdbdb },
-       { 0x40adc, 0xdcdcdc },
-       { 0x40ae0, 0xdcdcdc },
-       { 0x40ae4, 0xdddddd },
-       { 0x40ae8, 0xdddddd },
-       { 0x40aec, 0xdedede },
-       { 0x40af0, 0xdedede },
-       { 0x40af4, 0xdfdfdf },
-       { 0x40af8, 0xdfdfdf },
-       { 0x40afc, 0xe0e0e0 },
-       { 0x40b00, 0xe0e0e0 },
-       { 0x40b04, 0xe1e1e1 },
-       { 0x40b08, 0xe1e1e1 },
-       { 0x40b0c, 0xe2e2e2 },
-       { 0x40b10, 0xe3e3e3 },
-       { 0x40b14, 0xe3e3e3 },
-       { 0x40b18, 0xe4e4e4 },
-       { 0x40b1c, 0xe4e4e4 },
-       { 0x40b20, 0xe5e5e5 },
-       { 0x40b24, 0xe5e5e5 },
-       { 0x40b28, 0xe6e6e6 },
-       { 0x40b2c, 0xe6e6e6 },
-       { 0x40b30, 0xe7e7e7 },
-       { 0x40b34, 0xe7e7e7 },
-       { 0x40b38, 0xe8e8e8 },
-       { 0x40b3c, 0xe8e8e8 },
-       { 0x40b40, 0xe9e9e9 },
-       { 0x40b44, 0xe9e9e9 },
-       { 0x40b48, 0xeaeaea },
-       { 0x40b4c, 0xeaeaea },
-       { 0x40b50, 0xebebeb },
-       { 0x40b54, 0xebebeb },
-       { 0x40b58, 0xececec },
-       { 0x40b5c, 0xececec },
-       { 0x40b60, 0xededed },
-       { 0x40b64, 0xededed },
-       { 0x40b68, 0xeeeeee },
-       { 0x40b6c, 0xeeeeee },
-       { 0x40b70, 0xefefef },
-       { 0x40b74, 0xefefef },
-       { 0x40b78, 0xf0f0f0 },
-       { 0x40b7c, 0xf0f0f0 },
-       { 0x40b80, 0xf1f1f1 },
-       { 0x40b84, 0xf1f1f1 },
-       { 0x40b88, 0xf2f2f2 },
-       { 0x40b8c, 0xf2f2f2 },
-       { 0x40b90, 0xf2f2f2 },
-       { 0x40b94, 0xf3f3f3 },
-       { 0x40b98, 0xf3f3f3 },
-       { 0x40b9c, 0xf4f4f4 },
-       { 0x40ba0, 0xf4f4f4 },
-       { 0x40ba4, 0xf5f5f5 },
-       { 0x40ba8, 0xf5f5f5 },
-       { 0x40bac, 0xf6f6f6 },
-       { 0x40bb0, 0xf6f6f6 },
-       { 0x40bb4, 0xf7f7f7 },
-       { 0x40bb8, 0xf7f7f7 },
-       { 0x40bbc, 0xf8f8f8 },
-       { 0x40bc0, 0xf8f8f8 },
-       { 0x40bc4, 0xf9f9f9 },
-       { 0x40bc8, 0xf9f9f9 },
-       { 0x40bcc, 0xfafafa },
-       { 0x40bd0, 0xfafafa },
-       { 0x40bd4, 0xfafafa },
-       { 0x40bd8, 0xfbfbfb },
-       { 0x40bdc, 0xfbfbfb },
-       { 0x40be0, 0xfcfcfc },
-       { 0x40be4, 0xfcfcfc },
-       { 0x40be8, 0xfdfdfd },
-       { 0x40bec, 0xfdfdfd },
-       { 0x40bf0, 0xfefefe },
-       { 0x40bf4, 0xfefefe },
-       { 0x40bf8, 0xffffff },
-       { 0x40bfc, 0xffffff },
-       { 0x40c00, 0x0 },
-       { 0x40c04, 0x0 },
-       { 0x40c08, 0x0 },
-       { 0x40c0c, 0x0 },
-       { 0x40c10, 0x0 },
-       { 0x40c14, 0x0 },
-       { 0x40c18, 0x0 },
-       { 0x40c1c, 0x0 },
-       { 0x40c20, 0x0 },
-       { 0x40c24, 0x0 },
-       { 0x40c28, 0x0 },
-       { 0x40c2c, 0x0 },
-       { 0x40c30, 0x0 },
-       { 0x40c34, 0x0 },
-       { 0x40c38, 0x0 },
-       { 0x40c3c, 0x0 },
-       { 0x40c40, 0x10101 },
-       { 0x40c44, 0x10101 },
-       { 0x40c48, 0x10101 },
-       { 0x40c4c, 0x10101 },
-       { 0x40c50, 0x10101 },
-       { 0x40c54, 0x10101 },
-       { 0x40c58, 0x10101 },
-       { 0x40c5c, 0x10101 },
-       { 0x40c60, 0x10101 },
-       { 0x40c64, 0x10101 },
-       { 0x40c68, 0x20202 },
-       { 0x40c6c, 0x20202 },
-       { 0x40c70, 0x20202 },
-       { 0x40c74, 0x20202 },
-       { 0x40c78, 0x20202 },
-       { 0x40c7c, 0x20202 },
-       { 0x40c80, 0x30303 },
-       { 0x40c84, 0x30303 },
-       { 0x40c88, 0x30303 },
-       { 0x40c8c, 0x30303 },
-       { 0x40c90, 0x30303 },
-       { 0x40c94, 0x40404 },
-       { 0x40c98, 0x40404 },
-       { 0x40c9c, 0x40404 },
-       { 0x40ca0, 0x40404 },
-       { 0x40ca4, 0x40404 },
-       { 0x40ca8, 0x50505 },
-       { 0x40cac, 0x50505 },
-       { 0x40cb0, 0x50505 },
-       { 0x40cb4, 0x50505 },
-       { 0x40cb8, 0x60606 },
-       { 0x40cbc, 0x60606 },
-       { 0x40cc0, 0x60606 },
-       { 0x40cc4, 0x70707 },
-       { 0x40cc8, 0x70707 },
-       { 0x40ccc, 0x70707 },
-       { 0x40cd0, 0x70707 },
-       { 0x40cd4, 0x80808 },
-       { 0x40cd8, 0x80808 },
-       { 0x40cdc, 0x80808 },
-       { 0x40ce0, 0x90909 },
-       { 0x40ce4, 0x90909 },
-       { 0x40ce8, 0xa0a0a },
-       { 0x40cec, 0xa0a0a },
-       { 0x40cf0, 0xa0a0a },
-       { 0x40cf4, 0xb0b0b },
-       { 0x40cf8, 0xb0b0b },
-       { 0x40cfc, 0xb0b0b },
-       { 0x40d00, 0xc0c0c },
-       { 0x40d04, 0xc0c0c },
-       { 0x40d08, 0xd0d0d },
-       { 0x40d0c, 0xd0d0d },
-       { 0x40d10, 0xe0e0e },
-       { 0x40d14, 0xe0e0e },
-       { 0x40d18, 0xe0e0e },
-       { 0x40d1c, 0xf0f0f },
-       { 0x40d20, 0xf0f0f },
-       { 0x40d24, 0x101010 },
-       { 0x40d28, 0x101010 },
-       { 0x40d2c, 0x111111 },
-       { 0x40d30, 0x111111 },
-       { 0x40d34, 0x121212 },
-       { 0x40d38, 0x121212 },
-       { 0x40d3c, 0x131313 },
-       { 0x40d40, 0x131313 },
-       { 0x40d44, 0x141414 },
-       { 0x40d48, 0x151515 },
-       { 0x40d4c, 0x151515 },
-       { 0x40d50, 0x161616 },
-       { 0x40d54, 0x161616 },
-       { 0x40d58, 0x171717 },
-       { 0x40d5c, 0x171717 },
-       { 0x40d60, 0x181818 },
-       { 0x40d64, 0x191919 },
-       { 0x40d68, 0x191919 },
-       { 0x40d6c, 0x1a1a1a },
-       { 0x40d70, 0x1b1b1b },
-       { 0x40d74, 0x1b1b1b },
-       { 0x40d78, 0x1c1c1c },
-       { 0x40d7c, 0x1c1c1c },
-       { 0x40d80, 0x1d1d1d },
-       { 0x40d84, 0x1e1e1e },
-       { 0x40d88, 0x1f1f1f },
-       { 0x40d8c, 0x1f1f1f },
-       { 0x40d90, 0x202020 },
-       { 0x40d94, 0x212121 },
-       { 0x40d98, 0x212121 },
-       { 0x40d9c, 0x222222 },
-       { 0x40da0, 0x232323 },
-       { 0x40da4, 0x242424 },
-       { 0x40da8, 0x242424 },
-       { 0x40dac, 0x252525 },
-       { 0x40db0, 0x262626 },
-       { 0x40db4, 0x272727 },
-       { 0x40db8, 0x272727 },
-       { 0x40dbc, 0x282828 },
-       { 0x40dc0, 0x292929 },
-       { 0x40dc4, 0x2a2a2a },
-       { 0x40dc8, 0x2b2b2b },
-       { 0x40dcc, 0x2c2c2c },
-       { 0x40dd0, 0x2c2c2c },
-       { 0x40dd4, 0x2d2d2d },
-       { 0x40dd8, 0x2e2e2e },
-       { 0x40ddc, 0x2f2f2f },
-       { 0x40de0, 0x303030 },
-       { 0x40de4, 0x313131 },
-       { 0x40de8, 0x323232 },
-       { 0x40dec, 0x333333 },
-       { 0x40df0, 0x333333 },
-       { 0x40df4, 0x343434 },
-       { 0x40df8, 0x353535 },
-       { 0x40dfc, 0x363636 },
-       { 0x40e00, 0x373737 },
-       { 0x40e04, 0x383838 },
-       { 0x40e08, 0x393939 },
-       { 0x40e0c, 0x3a3a3a },
-       { 0x40e10, 0x3b3b3b },
-       { 0x40e14, 0x3c3c3c },
-       { 0x40e18, 0x3d3d3d },
-       { 0x40e1c, 0x3e3e3e },
-       { 0x40e20, 0x3f3f3f },
-       { 0x40e24, 0x404040 },
-       { 0x40e28, 0x414141 },
-       { 0x40e2c, 0x424242 },
-       { 0x40e30, 0x434343 },
-       { 0x40e34, 0x444444 },
-       { 0x40e38, 0x464646 },
-       { 0x40e3c, 0x474747 },
-       { 0x40e40, 0x484848 },
-       { 0x40e44, 0x494949 },
-       { 0x40e48, 0x4a4a4a },
-       { 0x40e4c, 0x4b4b4b },
-       { 0x40e50, 0x4c4c4c },
-       { 0x40e54, 0x4d4d4d },
-       { 0x40e58, 0x4f4f4f },
-       { 0x40e5c, 0x505050 },
-       { 0x40e60, 0x515151 },
-       { 0x40e64, 0x525252 },
-       { 0x40e68, 0x535353 },
-       { 0x40e6c, 0x545454 },
-       { 0x40e70, 0x565656 },
-       { 0x40e74, 0x575757 },
-       { 0x40e78, 0x585858 },
-       { 0x40e7c, 0x595959 },
-       { 0x40e80, 0x5b5b5b },
-       { 0x40e84, 0x5c5c5c },
-       { 0x40e88, 0x5d5d5d },
-       { 0x40e8c, 0x5e5e5e },
-       { 0x40e90, 0x606060 },
-       { 0x40e94, 0x616161 },
-       { 0x40e98, 0x626262 },
-       { 0x40e9c, 0x646464 },
-       { 0x40ea0, 0x656565 },
-       { 0x40ea4, 0x666666 },
-       { 0x40ea8, 0x686868 },
-       { 0x40eac, 0x696969 },
-       { 0x40eb0, 0x6a6a6a },
-       { 0x40eb4, 0x6c6c6c },
-       { 0x40eb8, 0x6d6d6d },
-       { 0x40ebc, 0x6f6f6f },
-       { 0x40ec0, 0x707070 },
-       { 0x40ec4, 0x717171 },
-       { 0x40ec8, 0x737373 },
-       { 0x40ecc, 0x747474 },
-       { 0x40ed0, 0x767676 },
-       { 0x40ed4, 0x777777 },
-       { 0x40ed8, 0x797979 },
-       { 0x40edc, 0x7a7a7a },
-       { 0x40ee0, 0x7c7c7c },
-       { 0x40ee4, 0x7d7d7d },
-       { 0x40ee8, 0x7f7f7f },
-       { 0x40eec, 0x808080 },
-       { 0x40ef0, 0x828282 },
-       { 0x40ef4, 0x838383 },
-       { 0x40ef8, 0x858585 },
-       { 0x40efc, 0x868686 },
-       { 0x40f00, 0x888888 },
-       { 0x40f04, 0x898989 },
-       { 0x40f08, 0x8b8b8b },
-       { 0x40f0c, 0x8d8d8d },
-       { 0x40f10, 0x8e8e8e },
-       { 0x40f14, 0x909090 },
-       { 0x40f18, 0x919191 },
-       { 0x40f1c, 0x939393 },
-       { 0x40f20, 0x959595 },
-       { 0x40f24, 0x969696 },
-       { 0x40f28, 0x989898 },
-       { 0x40f2c, 0x9a9a9a },
-       { 0x40f30, 0x9b9b9b },
-       { 0x40f34, 0x9d9d9d },
-       { 0x40f38, 0x9f9f9f },
-       { 0x40f3c, 0xa1a1a1 },
-       { 0x40f40, 0xa2a2a2 },
-       { 0x40f44, 0xa4a4a4 },
-       { 0x40f48, 0xa6a6a6 },
-       { 0x40f4c, 0xa7a7a7 },
-       { 0x40f50, 0xa9a9a9 },
-       { 0x40f54, 0xababab },
-       { 0x40f58, 0xadadad },
-       { 0x40f5c, 0xafafaf },
-       { 0x40f60, 0xb0b0b0 },
-       { 0x40f64, 0xb2b2b2 },
-       { 0x40f68, 0xb4b4b4 },
-       { 0x40f6c, 0xb6b6b6 },
-       { 0x40f70, 0xb8b8b8 },
-       { 0x40f74, 0xbababa },
-       { 0x40f78, 0xbbbbbb },
-       { 0x40f7c, 0xbdbdbd },
-       { 0x40f80, 0xbfbfbf },
-       { 0x40f84, 0xc1c1c1 },
-       { 0x40f88, 0xc3c3c3 },
-       { 0x40f8c, 0xc5c5c5 },
-       { 0x40f90, 0xc7c7c7 },
-       { 0x40f94, 0xc9c9c9 },
-       { 0x40f98, 0xcbcbcb },
-       { 0x40f9c, 0xcdcdcd },
-       { 0x40fa0, 0xcfcfcf },
-       { 0x40fa4, 0xd1d1d1 },
-       { 0x40fa8, 0xd3d3d3 },
-       { 0x40fac, 0xd5d5d5 },
-       { 0x40fb0, 0xd7d7d7 },
-       { 0x40fb4, 0xd9d9d9 },
-       { 0x40fb8, 0xdbdbdb },
-       { 0x40fbc, 0xdddddd },
-       { 0x40fc0, 0xdfdfdf },
-       { 0x40fc4, 0xe1e1e1 },
-       { 0x40fc8, 0xe3e3e3 },
-       { 0x40fcc, 0xe5e5e5 },
-       { 0x40fd0, 0xe7e7e7 },
-       { 0x40fd4, 0xe9e9e9 },
-       { 0x40fd8, 0xebebeb },
-       { 0x40fdc, 0xeeeeee },
-       { 0x40fe0, 0xf0f0f0 },
-       { 0x40fe4, 0xf2f2f2 },
-       { 0x40fe8, 0xf4f4f4 },
-       { 0x40fec, 0xf6f6f6 },
-       { 0x40ff0, 0xf8f8f8 },
-       { 0x40ff4, 0xfbfbfb },
-       { 0x40ff8, 0xfdfdfd },
-       { 0x40ffc, 0xffffff },
-};
diff --git a/drivers/video/fbdev/msm/mdp_hw.h b/drivers/video/fbdev/msm/mdp_hw.h
deleted file mode 100644 (file)
index 35848d7..0000000
+++ /dev/null
@@ -1,627 +0,0 @@
-/* drivers/video/msm_fb/mdp_hw.h
- *
- * Copyright (C) 2007 QUALCOMM Incorporated
- * Copyright (C) 2007 Google Incorporated
- *
- * 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 _MDP_HW_H_
-#define _MDP_HW_H_
-
-#include <linux/platform_data/video-msm_fb.h>
-
-struct mdp_info {
-       struct mdp_device mdp_dev;
-       char * __iomem base;
-       int irq;
-};
-struct mdp_blit_req;
-struct mdp_device;
-int mdp_ppp_blit(const struct mdp_info *mdp, struct mdp_blit_req *req,
-                struct file *src_file, unsigned long src_start,
-                unsigned long src_len, struct file *dst_file,
-                unsigned long dst_start, unsigned long dst_len);
-#define mdp_writel(mdp, value, offset) writel(value, mdp->base + offset)
-#define mdp_readl(mdp, offset) readl(mdp->base + offset)
-
-#define MDP_SYNC_CONFIG_0                (0x00000)
-#define MDP_SYNC_CONFIG_1                (0x00004)
-#define MDP_SYNC_CONFIG_2                (0x00008)
-#define MDP_SYNC_STATUS_0                (0x0000c)
-#define MDP_SYNC_STATUS_1                (0x00010)
-#define MDP_SYNC_STATUS_2                (0x00014)
-#define MDP_SYNC_THRESH_0                (0x00018)
-#define MDP_SYNC_THRESH_1                (0x0001c)
-#define MDP_INTR_ENABLE                  (0x00020)
-#define MDP_INTR_STATUS                  (0x00024)
-#define MDP_INTR_CLEAR                   (0x00028)
-#define MDP_DISPLAY0_START               (0x00030)
-#define MDP_DISPLAY1_START               (0x00034)
-#define MDP_DISPLAY_STATUS               (0x00038)
-#define MDP_EBI2_LCD0                    (0x0003c)
-#define MDP_EBI2_LCD1                    (0x00040)
-#define MDP_DISPLAY0_ADDR                (0x00054)
-#define MDP_DISPLAY1_ADDR                (0x00058)
-#define MDP_EBI2_PORTMAP_MODE            (0x0005c)
-#define MDP_MODE                         (0x00060)
-#define MDP_TV_OUT_STATUS                (0x00064)
-#define MDP_HW_VERSION                   (0x00070)
-#define MDP_SW_RESET                     (0x00074)
-#define MDP_AXI_ERROR_MASTER_STOP        (0x00078)
-#define MDP_SEL_CLK_OR_HCLK_TEST_BUS     (0x0007c)
-#define MDP_PRIMARY_VSYNC_OUT_CTRL       (0x00080)
-#define MDP_SECONDARY_VSYNC_OUT_CTRL     (0x00084)
-#define MDP_EXTERNAL_VSYNC_OUT_CTRL      (0x00088)
-#define MDP_VSYNC_CTRL                   (0x0008c)
-#define MDP_CGC_EN                       (0x00100)
-#define MDP_CMD_STATUS                   (0x10008)
-#define MDP_PROFILE_EN                   (0x10010)
-#define MDP_PROFILE_COUNT                (0x10014)
-#define MDP_DMA_START                    (0x10044)
-#define MDP_FULL_BYPASS_WORD0            (0x10100)
-#define MDP_FULL_BYPASS_WORD1            (0x10104)
-#define MDP_COMMAND_CONFIG               (0x10104)
-#define MDP_FULL_BYPASS_WORD2            (0x10108)
-#define MDP_FULL_BYPASS_WORD3            (0x1010c)
-#define MDP_FULL_BYPASS_WORD4            (0x10110)
-#define MDP_FULL_BYPASS_WORD6            (0x10118)
-#define MDP_FULL_BYPASS_WORD7            (0x1011c)
-#define MDP_FULL_BYPASS_WORD8            (0x10120)
-#define MDP_FULL_BYPASS_WORD9            (0x10124)
-#define MDP_PPP_SOURCE_CONFIG            (0x10124)
-#define MDP_FULL_BYPASS_WORD10           (0x10128)
-#define MDP_FULL_BYPASS_WORD11           (0x1012c)
-#define MDP_FULL_BYPASS_WORD12           (0x10130)
-#define MDP_FULL_BYPASS_WORD13           (0x10134)
-#define MDP_FULL_BYPASS_WORD14           (0x10138)
-#define MDP_PPP_OPERATION_CONFIG         (0x10138)
-#define MDP_FULL_BYPASS_WORD15           (0x1013c)
-#define MDP_FULL_BYPASS_WORD16           (0x10140)
-#define MDP_FULL_BYPASS_WORD17           (0x10144)
-#define MDP_FULL_BYPASS_WORD18           (0x10148)
-#define MDP_FULL_BYPASS_WORD19           (0x1014c)
-#define MDP_FULL_BYPASS_WORD20           (0x10150)
-#define MDP_PPP_DESTINATION_CONFIG       (0x10150)
-#define MDP_FULL_BYPASS_WORD21           (0x10154)
-#define MDP_FULL_BYPASS_WORD22           (0x10158)
-#define MDP_FULL_BYPASS_WORD23           (0x1015c)
-#define MDP_FULL_BYPASS_WORD24           (0x10160)
-#define MDP_FULL_BYPASS_WORD25           (0x10164)
-#define MDP_FULL_BYPASS_WORD26           (0x10168)
-#define MDP_FULL_BYPASS_WORD27           (0x1016c)
-#define MDP_FULL_BYPASS_WORD29           (0x10174)
-#define MDP_FULL_BYPASS_WORD30           (0x10178)
-#define MDP_FULL_BYPASS_WORD31           (0x1017c)
-#define MDP_FULL_BYPASS_WORD32           (0x10180)
-#define MDP_DMA_CONFIG                   (0x10180)
-#define MDP_FULL_BYPASS_WORD33           (0x10184)
-#define MDP_FULL_BYPASS_WORD34           (0x10188)
-#define MDP_FULL_BYPASS_WORD35           (0x1018c)
-#define MDP_FULL_BYPASS_WORD37           (0x10194)
-#define MDP_FULL_BYPASS_WORD39           (0x1019c)
-#define MDP_FULL_BYPASS_WORD40           (0x101a0)
-#define MDP_FULL_BYPASS_WORD41           (0x101a4)
-#define MDP_FULL_BYPASS_WORD43           (0x101ac)
-#define MDP_FULL_BYPASS_WORD46           (0x101b8)
-#define MDP_FULL_BYPASS_WORD47           (0x101bc)
-#define MDP_FULL_BYPASS_WORD48           (0x101c0)
-#define MDP_FULL_BYPASS_WORD49           (0x101c4)
-#define MDP_FULL_BYPASS_WORD50           (0x101c8)
-#define MDP_FULL_BYPASS_WORD51           (0x101cc)
-#define MDP_FULL_BYPASS_WORD52           (0x101d0)
-#define MDP_FULL_BYPASS_WORD53           (0x101d4)
-#define MDP_FULL_BYPASS_WORD54           (0x101d8)
-#define MDP_FULL_BYPASS_WORD55           (0x101dc)
-#define MDP_FULL_BYPASS_WORD56           (0x101e0)
-#define MDP_FULL_BYPASS_WORD57           (0x101e4)
-#define MDP_FULL_BYPASS_WORD58           (0x101e8)
-#define MDP_FULL_BYPASS_WORD59           (0x101ec)
-#define MDP_FULL_BYPASS_WORD60           (0x101f0)
-#define MDP_VSYNC_THRESHOLD              (0x101f0)
-#define MDP_FULL_BYPASS_WORD61           (0x101f4)
-#define MDP_FULL_BYPASS_WORD62           (0x101f8)
-#define MDP_FULL_BYPASS_WORD63           (0x101fc)
-#define MDP_TFETCH_TEST_MODE             (0x20004)
-#define MDP_TFETCH_STATUS                (0x20008)
-#define MDP_TFETCH_TILE_COUNT            (0x20010)
-#define MDP_TFETCH_FETCH_COUNT           (0x20014)
-#define MDP_TFETCH_CONSTANT_COLOR        (0x20040)
-#define MDP_CSC_BYPASS                   (0x40004)
-#define MDP_SCALE_COEFF_LSB              (0x5fffc)
-#define MDP_TV_OUT_CTL                   (0xc0000)
-#define MDP_TV_OUT_FIR_COEFF             (0xc0004)
-#define MDP_TV_OUT_BUF_ADDR              (0xc0008)
-#define MDP_TV_OUT_CC_DATA               (0xc000c)
-#define MDP_TV_OUT_SOBEL                 (0xc0010)
-#define MDP_TV_OUT_Y_CLAMP               (0xc0018)
-#define MDP_TV_OUT_CB_CLAMP              (0xc001c)
-#define MDP_TV_OUT_CR_CLAMP              (0xc0020)
-#define MDP_TEST_MODE_CLK                (0xd0000)
-#define MDP_TEST_MISR_RESET_CLK          (0xd0004)
-#define MDP_TEST_EXPORT_MISR_CLK         (0xd0008)
-#define MDP_TEST_MISR_CURR_VAL_CLK       (0xd000c)
-#define MDP_TEST_MODE_HCLK               (0xd0100)
-#define MDP_TEST_MISR_RESET_HCLK         (0xd0104)
-#define MDP_TEST_EXPORT_MISR_HCLK        (0xd0108)
-#define MDP_TEST_MISR_CURR_VAL_HCLK      (0xd010c)
-#define MDP_TEST_MODE_DCLK               (0xd0200)
-#define MDP_TEST_MISR_RESET_DCLK         (0xd0204)
-#define MDP_TEST_EXPORT_MISR_DCLK        (0xd0208)
-#define MDP_TEST_MISR_CURR_VAL_DCLK      (0xd020c)
-#define MDP_TEST_CAPTURED_DCLK           (0xd0210)
-#define MDP_TEST_MISR_CAPT_VAL_DCLK      (0xd0214)
-#define MDP_LCDC_CTL                     (0xe0000)
-#define MDP_LCDC_HSYNC_CTL               (0xe0004)
-#define MDP_LCDC_VSYNC_CTL               (0xe0008)
-#define MDP_LCDC_ACTIVE_HCTL             (0xe000c)
-#define MDP_LCDC_ACTIVE_VCTL             (0xe0010)
-#define MDP_LCDC_BORDER_CLR              (0xe0014)
-#define MDP_LCDC_H_BLANK                 (0xe0018)
-#define MDP_LCDC_V_BLANK                 (0xe001c)
-#define MDP_LCDC_UNDERFLOW_CLR           (0xe0020)
-#define MDP_LCDC_HSYNC_SKEW              (0xe0024)
-#define MDP_LCDC_TEST_CTL                (0xe0028)
-#define MDP_LCDC_LINE_IRQ                (0xe002c)
-#define MDP_LCDC_CTL_POLARITY            (0xe0030)
-#define MDP_LCDC_DMA_CONFIG              (0xe1000)
-#define MDP_LCDC_DMA_SIZE                (0xe1004)
-#define MDP_LCDC_DMA_IBUF_ADDR           (0xe1008)
-#define MDP_LCDC_DMA_IBUF_Y_STRIDE       (0xe100c)
-
-
-#define MDP_DMA2_TERM 0x1
-#define MDP_DMA3_TERM 0x2
-#define MDP_PPP_TERM 0x3
-
-/* MDP_INTR_ENABLE */
-#define DL0_ROI_DONE           (1<<0)
-#define DL1_ROI_DONE           (1<<1)
-#define DL0_DMA2_TERM_DONE     (1<<2)
-#define DL1_DMA2_TERM_DONE     (1<<3)
-#define DL0_PPP_TERM_DONE      (1<<4)
-#define DL1_PPP_TERM_DONE      (1<<5)
-#define TV_OUT_DMA3_DONE       (1<<6)
-#define TV_ENC_UNDERRUN        (1<<7)
-#define DL0_FETCH_DONE         (1<<11)
-#define DL1_FETCH_DONE         (1<<12)
-
-#define MDP_PPP_BUSY_STATUS (DL0_ROI_DONE| \
-                          DL1_ROI_DONE| \
-                          DL0_PPP_TERM_DONE| \
-                          DL1_PPP_TERM_DONE)
-
-#define MDP_ANY_INTR_MASK (DL0_ROI_DONE| \
-                          DL1_ROI_DONE| \
-                          DL0_DMA2_TERM_DONE| \
-                          DL1_DMA2_TERM_DONE| \
-                          DL0_PPP_TERM_DONE| \
-                          DL1_PPP_TERM_DONE| \
-                          DL0_FETCH_DONE| \
-                          DL1_FETCH_DONE| \
-                          TV_ENC_UNDERRUN)
-
-#define MDP_TOP_LUMA       16
-#define MDP_TOP_CHROMA     0
-#define MDP_BOTTOM_LUMA    19
-#define MDP_BOTTOM_CHROMA  3
-#define MDP_LEFT_LUMA      22
-#define MDP_LEFT_CHROMA    6
-#define MDP_RIGHT_LUMA     25
-#define MDP_RIGHT_CHROMA   9
-
-#define CLR_G 0x0
-#define CLR_B 0x1
-#define CLR_R 0x2
-#define CLR_ALPHA 0x3
-
-#define CLR_Y  CLR_G
-#define CLR_CB CLR_B
-#define CLR_CR CLR_R
-
-/* from lsb to msb */
-#define MDP_GET_PACK_PATTERN(a, x, y, z, bit) \
-       (((a)<<(bit*3))|((x)<<(bit*2))|((y)<<bit)|(z))
-
-/* MDP_SYNC_CONFIG_0/1/2 */
-#define MDP_SYNCFG_HGT_LOC 22
-#define MDP_SYNCFG_VSYNC_EXT_EN (1<<21)
-#define MDP_SYNCFG_VSYNC_INT_EN (1<<20)
-
-/* MDP_SYNC_THRESH_0 */
-#define MDP_PRIM_BELOW_LOC 0
-#define MDP_PRIM_ABOVE_LOC 8
-
-/* MDP_{PRIMARY,SECONDARY,EXTERNAL}_VSYNC_OUT_CRL */
-#define VSYNC_PULSE_EN (1<<31)
-#define VSYNC_PULSE_INV (1<<30)
-
-/* MDP_VSYNC_CTRL */
-#define DISP0_VSYNC_MAP_VSYNC0 0
-#define DISP0_VSYNC_MAP_VSYNC1 (1<<0)
-#define DISP0_VSYNC_MAP_VSYNC2 ((1<<0)|(1<<1))
-
-#define DISP1_VSYNC_MAP_VSYNC0 0
-#define DISP1_VSYNC_MAP_VSYNC1 (1<<2)
-#define DISP1_VSYNC_MAP_VSYNC2 ((1<<2)|(1<<3))
-
-#define PRIMARY_LCD_SYNC_EN (1<<4)
-#define PRIMARY_LCD_SYNC_DISABLE 0
-
-#define SECONDARY_LCD_SYNC_EN (1<<5)
-#define SECONDARY_LCD_SYNC_DISABLE 0
-
-#define EXTERNAL_LCD_SYNC_EN (1<<6)
-#define EXTERNAL_LCD_SYNC_DISABLE 0
-
-/* MDP_VSYNC_THRESHOLD / MDP_FULL_BYPASS_WORD60 */
-#define VSYNC_THRESHOLD_ABOVE_LOC 0
-#define VSYNC_THRESHOLD_BELOW_LOC 16
-#define VSYNC_ANTI_TEAR_EN (1<<31)
-
-/* MDP_COMMAND_CONFIG / MDP_FULL_BYPASS_WORD1 */
-#define MDP_CMD_DBGBUS_EN (1<<0)
-
-/* MDP_PPP_SOURCE_CONFIG / MDP_FULL_BYPASS_WORD9&53 */
-#define PPP_SRC_C0G_8BIT ((1<<1)|(1<<0))
-#define PPP_SRC_C1B_8BIT ((1<<3)|(1<<2))
-#define PPP_SRC_C2R_8BIT ((1<<5)|(1<<4))
-#define PPP_SRC_C3A_8BIT ((1<<7)|(1<<6))
-
-#define PPP_SRC_C0G_6BIT (1<<1)
-#define PPP_SRC_C1B_6BIT (1<<3)
-#define PPP_SRC_C2R_6BIT (1<<5)
-
-#define PPP_SRC_C0G_5BIT (1<<0)
-#define PPP_SRC_C1B_5BIT (1<<2)
-#define PPP_SRC_C2R_5BIT (1<<4)
-
-#define PPP_SRC_C3ALPHA_EN (1<<8)
-
-#define PPP_SRC_BPP_1BYTES 0
-#define PPP_SRC_BPP_2BYTES (1<<9)
-#define PPP_SRC_BPP_3BYTES (1<<10)
-#define PPP_SRC_BPP_4BYTES ((1<<10)|(1<<9))
-
-#define PPP_SRC_BPP_ROI_ODD_X (1<<11)
-#define PPP_SRC_BPP_ROI_ODD_Y (1<<12)
-#define PPP_SRC_INTERLVD_2COMPONENTS (1<<13)
-#define PPP_SRC_INTERLVD_3COMPONENTS (1<<14)
-#define PPP_SRC_INTERLVD_4COMPONENTS ((1<<14)|(1<<13))
-
-
-/* RGB666 unpack format
-** TIGHT means R6+G6+B6 together
-** LOOSE means R6+2 +G6+2+ B6+2 (with MSB)
-**          or 2+R6 +2+G6 +2+B6 (with LSB)
-*/
-#define PPP_SRC_PACK_TIGHT (1<<17)
-#define PPP_SRC_PACK_LOOSE 0
-#define PPP_SRC_PACK_ALIGN_LSB 0
-#define PPP_SRC_PACK_ALIGN_MSB (1<<18)
-
-#define PPP_SRC_PLANE_INTERLVD 0
-#define PPP_SRC_PLANE_PSEUDOPLNR (1<<20)
-
-#define PPP_SRC_WMV9_MODE (1<<21)
-
-/* MDP_PPP_OPERATION_CONFIG / MDP_FULL_BYPASS_WORD14 */
-#define PPP_OP_SCALE_X_ON (1<<0)
-#define PPP_OP_SCALE_Y_ON (1<<1)
-
-#define PPP_OP_CONVERT_RGB2YCBCR 0
-#define PPP_OP_CONVERT_YCBCR2RGB (1<<2)
-#define PPP_OP_CONVERT_ON (1<<3)
-
-#define PPP_OP_CONVERT_MATRIX_PRIMARY 0
-#define PPP_OP_CONVERT_MATRIX_SECONDARY (1<<4)
-
-#define PPP_OP_LUT_C0_ON (1<<5)
-#define PPP_OP_LUT_C1_ON (1<<6)
-#define PPP_OP_LUT_C2_ON (1<<7)
-
-/* rotate or blend enable */
-#define PPP_OP_ROT_ON (1<<8)
-
-#define PPP_OP_ROT_90 (1<<9)
-#define PPP_OP_FLIP_LR (1<<10)
-#define PPP_OP_FLIP_UD (1<<11)
-
-#define PPP_OP_BLEND_ON (1<<12)
-
-#define PPP_OP_BLEND_SRCPIXEL_ALPHA 0
-#define PPP_OP_BLEND_DSTPIXEL_ALPHA (1<<13)
-#define PPP_OP_BLEND_CONSTANT_ALPHA (1<<14)
-#define PPP_OP_BLEND_SRCPIXEL_TRANSP ((1<<13)|(1<<14))
-
-#define PPP_OP_BLEND_ALPHA_BLEND_NORMAL 0
-#define PPP_OP_BLEND_ALPHA_BLEND_REVERSE (1<<15)
-
-#define PPP_OP_DITHER_EN (1<<16)
-
-#define PPP_OP_COLOR_SPACE_RGB 0
-#define PPP_OP_COLOR_SPACE_YCBCR (1<<17)
-
-#define PPP_OP_SRC_CHROMA_RGB 0
-#define PPP_OP_SRC_CHROMA_H2V1 (1<<18)
-#define PPP_OP_SRC_CHROMA_H1V2 (1<<19)
-#define PPP_OP_SRC_CHROMA_420 ((1<<18)|(1<<19))
-#define PPP_OP_SRC_CHROMA_COSITE 0
-#define PPP_OP_SRC_CHROMA_OFFSITE (1<<20)
-
-#define PPP_OP_DST_CHROMA_RGB 0
-#define PPP_OP_DST_CHROMA_H2V1 (1<<21)
-#define PPP_OP_DST_CHROMA_H1V2 (1<<22)
-#define PPP_OP_DST_CHROMA_420 ((1<<21)|(1<<22))
-#define PPP_OP_DST_CHROMA_COSITE 0
-#define PPP_OP_DST_CHROMA_OFFSITE (1<<23)
-
-#define PPP_BLEND_ALPHA_TRANSP (1<<24)
-
-#define PPP_OP_BG_CHROMA_RGB 0
-#define PPP_OP_BG_CHROMA_H2V1 (1<<25)
-#define PPP_OP_BG_CHROMA_H1V2 (1<<26)
-#define PPP_OP_BG_CHROMA_420 ((1<<25)|(1<<26))
-#define PPP_OP_BG_CHROMA_SITE_COSITE 0
-#define PPP_OP_BG_CHROMA_SITE_OFFSITE (1<<27)
-
-/* MDP_PPP_DESTINATION_CONFIG / MDP_FULL_BYPASS_WORD20 */
-#define PPP_DST_C0G_8BIT ((1<<0)|(1<<1))
-#define PPP_DST_C1B_8BIT ((1<<3)|(1<<2))
-#define PPP_DST_C2R_8BIT ((1<<5)|(1<<4))
-#define PPP_DST_C3A_8BIT ((1<<7)|(1<<6))
-
-#define PPP_DST_C0G_6BIT (1<<1)
-#define PPP_DST_C1B_6BIT (1<<3)
-#define PPP_DST_C2R_6BIT (1<<5)
-
-#define PPP_DST_C0G_5BIT (1<<0)
-#define PPP_DST_C1B_5BIT (1<<2)
-#define PPP_DST_C2R_5BIT (1<<4)
-
-#define PPP_DST_C3A_8BIT ((1<<7)|(1<<6))
-#define PPP_DST_C3ALPHA_EN (1<<8)
-
-#define PPP_DST_INTERLVD_2COMPONENTS (1<<9)
-#define PPP_DST_INTERLVD_3COMPONENTS (1<<10)
-#define PPP_DST_INTERLVD_4COMPONENTS ((1<<10)|(1<<9))
-#define PPP_DST_INTERLVD_6COMPONENTS ((1<<11)|(1<<9))
-
-#define PPP_DST_PACK_LOOSE 0
-#define PPP_DST_PACK_TIGHT (1<<13)
-#define PPP_DST_PACK_ALIGN_LSB 0
-#define PPP_DST_PACK_ALIGN_MSB (1<<14)
-
-#define PPP_DST_OUT_SEL_AXI 0
-#define PPP_DST_OUT_SEL_MDDI (1<<15)
-
-#define PPP_DST_BPP_2BYTES (1<<16)
-#define PPP_DST_BPP_3BYTES (1<<17)
-#define PPP_DST_BPP_4BYTES ((1<<17)|(1<<16))
-
-#define PPP_DST_PLANE_INTERLVD 0
-#define PPP_DST_PLANE_PLANAR (1<<18)
-#define PPP_DST_PLANE_PSEUDOPLNR (1<<19)
-
-#define PPP_DST_TO_TV (1<<20)
-
-#define PPP_DST_MDDI_PRIMARY 0
-#define PPP_DST_MDDI_SECONDARY (1<<21)
-#define PPP_DST_MDDI_EXTERNAL (1<<22)
-
-/* image configurations by image type */
-#define PPP_CFG_MDP_RGB_565(dir)       (PPP_##dir##_C2R_5BIT | \
-                                       PPP_##dir##_C0G_6BIT | \
-                                       PPP_##dir##_C1B_5BIT | \
-                                       PPP_##dir##_BPP_2BYTES | \
-                                       PPP_##dir##_INTERLVD_3COMPONENTS | \
-                                       PPP_##dir##_PACK_TIGHT | \
-                                       PPP_##dir##_PACK_ALIGN_LSB | \
-                                       PPP_##dir##_PLANE_INTERLVD)
-
-#define PPP_CFG_MDP_RGB_888(dir)       (PPP_##dir##_C2R_8BIT | \
-                                       PPP_##dir##_C0G_8BIT | \
-                                       PPP_##dir##_C1B_8BIT | \
-                                       PPP_##dir##_BPP_3BYTES | \
-                                       PPP_##dir##_INTERLVD_3COMPONENTS | \
-                                       PPP_##dir##_PACK_TIGHT | \
-                                       PPP_##dir##_PACK_ALIGN_LSB | \
-                                       PPP_##dir##_PLANE_INTERLVD)
-
-#define PPP_CFG_MDP_ARGB_8888(dir)     (PPP_##dir##_C2R_8BIT | \
-                                       PPP_##dir##_C0G_8BIT | \
-                                       PPP_##dir##_C1B_8BIT | \
-                                       PPP_##dir##_C3A_8BIT | \
-                                       PPP_##dir##_C3ALPHA_EN | \
-                                       PPP_##dir##_BPP_4BYTES | \
-                                       PPP_##dir##_INTERLVD_4COMPONENTS | \
-                                       PPP_##dir##_PACK_TIGHT | \
-                                       PPP_##dir##_PACK_ALIGN_LSB | \
-                                       PPP_##dir##_PLANE_INTERLVD)
-
-#define PPP_CFG_MDP_XRGB_8888(dir) PPP_CFG_MDP_ARGB_8888(dir)
-#define PPP_CFG_MDP_RGBA_8888(dir) PPP_CFG_MDP_ARGB_8888(dir)
-#define PPP_CFG_MDP_BGRA_8888(dir) PPP_CFG_MDP_ARGB_8888(dir)
-#define PPP_CFG_MDP_RGBX_8888(dir) PPP_CFG_MDP_ARGB_8888(dir)
-
-#define PPP_CFG_MDP_Y_CBCR_H2V2(dir)   (PPP_##dir##_C2R_8BIT | \
-                                       PPP_##dir##_C0G_8BIT | \
-                                       PPP_##dir##_C1B_8BIT | \
-                                       PPP_##dir##_C3A_8BIT | \
-                                       PPP_##dir##_BPP_2BYTES | \
-                                       PPP_##dir##_INTERLVD_2COMPONENTS | \
-                                       PPP_##dir##_PACK_TIGHT | \
-                                       PPP_##dir##_PACK_ALIGN_LSB | \
-                                       PPP_##dir##_PLANE_PSEUDOPLNR)
-
-#define PPP_CFG_MDP_Y_CRCB_H2V2(dir)   PPP_CFG_MDP_Y_CBCR_H2V2(dir)
-
-#define PPP_CFG_MDP_YCRYCB_H2V1(dir)   (PPP_##dir##_C2R_8BIT | \
-                                       PPP_##dir##_C0G_8BIT | \
-                                       PPP_##dir##_C1B_8BIT | \
-                                       PPP_##dir##_C3A_8BIT | \
-                                       PPP_##dir##_BPP_2BYTES | \
-                                       PPP_##dir##_INTERLVD_4COMPONENTS | \
-                                       PPP_##dir##_PACK_TIGHT | \
-                                       PPP_##dir##_PACK_ALIGN_LSB |\
-                                       PPP_##dir##_PLANE_INTERLVD)
-
-#define PPP_CFG_MDP_Y_CBCR_H2V1(dir)   (PPP_##dir##_C2R_8BIT | \
-                                       PPP_##dir##_C0G_8BIT | \
-                                       PPP_##dir##_C1B_8BIT | \
-                                       PPP_##dir##_C3A_8BIT | \
-                                       PPP_##dir##_BPP_2BYTES |   \
-                                       PPP_##dir##_INTERLVD_2COMPONENTS |  \
-                                       PPP_##dir##_PACK_TIGHT | \
-                                       PPP_##dir##_PACK_ALIGN_LSB | \
-                                       PPP_##dir##_PLANE_PSEUDOPLNR)
-
-#define PPP_CFG_MDP_Y_CRCB_H2V1(dir)   PPP_CFG_MDP_Y_CBCR_H2V1(dir)
-
-#define PPP_PACK_PATTERN_MDP_RGB_565 \
-       MDP_GET_PACK_PATTERN(0, CLR_R, CLR_G, CLR_B, 8)
-#define PPP_PACK_PATTERN_MDP_RGB_888 PPP_PACK_PATTERN_MDP_RGB_565
-#define PPP_PACK_PATTERN_MDP_XRGB_8888 \
-       MDP_GET_PACK_PATTERN(CLR_B, CLR_G, CLR_R, CLR_ALPHA, 8)
-#define PPP_PACK_PATTERN_MDP_ARGB_8888 PPP_PACK_PATTERN_MDP_XRGB_8888
-#define PPP_PACK_PATTERN_MDP_RGBA_8888 \
-       MDP_GET_PACK_PATTERN(CLR_ALPHA, CLR_B, CLR_G, CLR_R, 8)
-#define PPP_PACK_PATTERN_MDP_BGRA_8888 \
-       MDP_GET_PACK_PATTERN(CLR_ALPHA, CLR_R, CLR_G, CLR_B, 8)
-#define PPP_PACK_PATTERN_MDP_RGBX_8888 \
-       MDP_GET_PACK_PATTERN(CLR_ALPHA, CLR_B, CLR_G, CLR_R, 8)
-#define PPP_PACK_PATTERN_MDP_Y_CBCR_H2V1 \
-       MDP_GET_PACK_PATTERN(0, 0, CLR_CB, CLR_CR, 8)
-#define PPP_PACK_PATTERN_MDP_Y_CBCR_H2V2 PPP_PACK_PATTERN_MDP_Y_CBCR_H2V1
-#define PPP_PACK_PATTERN_MDP_Y_CRCB_H2V1 \
-       MDP_GET_PACK_PATTERN(0, 0, CLR_CR, CLR_CB, 8)
-#define PPP_PACK_PATTERN_MDP_Y_CRCB_H2V2 PPP_PACK_PATTERN_MDP_Y_CRCB_H2V1
-#define PPP_PACK_PATTERN_MDP_YCRYCB_H2V1 \
-       MDP_GET_PACK_PATTERN(CLR_Y, CLR_R, CLR_Y, CLR_B, 8)
-
-#define PPP_CHROMA_SAMP_MDP_RGB_565(dir) PPP_OP_##dir##_CHROMA_RGB
-#define PPP_CHROMA_SAMP_MDP_RGB_888(dir) PPP_OP_##dir##_CHROMA_RGB
-#define PPP_CHROMA_SAMP_MDP_XRGB_8888(dir) PPP_OP_##dir##_CHROMA_RGB
-#define PPP_CHROMA_SAMP_MDP_ARGB_8888(dir) PPP_OP_##dir##_CHROMA_RGB
-#define PPP_CHROMA_SAMP_MDP_RGBA_8888(dir) PPP_OP_##dir##_CHROMA_RGB
-#define PPP_CHROMA_SAMP_MDP_BGRA_8888(dir) PPP_OP_##dir##_CHROMA_RGB
-#define PPP_CHROMA_SAMP_MDP_RGBX_8888(dir) PPP_OP_##dir##_CHROMA_RGB
-#define PPP_CHROMA_SAMP_MDP_Y_CBCR_H2V1(dir) PPP_OP_##dir##_CHROMA_H2V1
-#define PPP_CHROMA_SAMP_MDP_Y_CBCR_H2V2(dir) PPP_OP_##dir##_CHROMA_420
-#define PPP_CHROMA_SAMP_MDP_Y_CRCB_H2V1(dir) PPP_OP_##dir##_CHROMA_H2V1
-#define PPP_CHROMA_SAMP_MDP_Y_CRCB_H2V2(dir) PPP_OP_##dir##_CHROMA_420
-#define PPP_CHROMA_SAMP_MDP_YCRYCB_H2V1(dir) PPP_OP_##dir##_CHROMA_H2V1
-
-/* Helpful array generation macros */
-#define PPP_ARRAY0(name) \
-       [MDP_RGB_565] = PPP_##name##_MDP_RGB_565,\
-       [MDP_RGB_888] = PPP_##name##_MDP_RGB_888,\
-       [MDP_XRGB_8888] = PPP_##name##_MDP_XRGB_8888,\
-       [MDP_ARGB_8888] = PPP_##name##_MDP_ARGB_8888,\
-       [MDP_RGBA_8888] = PPP_##name##_MDP_RGBA_8888,\
-       [MDP_BGRA_8888] = PPP_##name##_MDP_BGRA_8888,\
-       [MDP_RGBX_8888] = PPP_##name##_MDP_RGBX_8888,\
-       [MDP_Y_CBCR_H2V1] = PPP_##name##_MDP_Y_CBCR_H2V1,\
-       [MDP_Y_CBCR_H2V2] = PPP_##name##_MDP_Y_CBCR_H2V2,\
-       [MDP_Y_CRCB_H2V1] = PPP_##name##_MDP_Y_CRCB_H2V1,\
-       [MDP_Y_CRCB_H2V2] = PPP_##name##_MDP_Y_CRCB_H2V2,\
-       [MDP_YCRYCB_H2V1] = PPP_##name##_MDP_YCRYCB_H2V1
-
-#define PPP_ARRAY1(name, dir) \
-       [MDP_RGB_565] = PPP_##name##_MDP_RGB_565(dir),\
-       [MDP_RGB_888] = PPP_##name##_MDP_RGB_888(dir),\
-       [MDP_XRGB_8888] = PPP_##name##_MDP_XRGB_8888(dir),\
-       [MDP_ARGB_8888] = PPP_##name##_MDP_ARGB_8888(dir),\
-       [MDP_RGBA_8888] = PPP_##name##_MDP_RGBA_8888(dir),\
-       [MDP_BGRA_8888] = PPP_##name##_MDP_BGRA_8888(dir),\
-       [MDP_RGBX_8888] = PPP_##name##_MDP_RGBX_8888(dir),\
-       [MDP_Y_CBCR_H2V1] = PPP_##name##_MDP_Y_CBCR_H2V1(dir),\
-       [MDP_Y_CBCR_H2V2] = PPP_##name##_MDP_Y_CBCR_H2V2(dir),\
-       [MDP_Y_CRCB_H2V1] = PPP_##name##_MDP_Y_CRCB_H2V1(dir),\
-       [MDP_Y_CRCB_H2V2] = PPP_##name##_MDP_Y_CRCB_H2V2(dir),\
-       [MDP_YCRYCB_H2V1] = PPP_##name##_MDP_YCRYCB_H2V1(dir)
-
-#define IS_YCRCB(img) ((img == MDP_Y_CRCB_H2V2) | (img == MDP_Y_CBCR_H2V2) | \
-                      (img == MDP_Y_CRCB_H2V1) | (img == MDP_Y_CBCR_H2V1) | \
-                      (img == MDP_YCRYCB_H2V1))
-#define IS_RGB(img) ((img == MDP_RGB_565) | (img == MDP_RGB_888) | \
-                    (img == MDP_ARGB_8888) | (img == MDP_RGBA_8888) | \
-                    (img == MDP_XRGB_8888) | (img == MDP_BGRA_8888) | \
-                    (img == MDP_RGBX_8888))
-#define HAS_ALPHA(img) ((img == MDP_ARGB_8888) | (img == MDP_RGBA_8888) | \
-                       (img == MDP_BGRA_8888))
-
-#define IS_PSEUDOPLNR(img) ((img == MDP_Y_CRCB_H2V2) | \
-                           (img == MDP_Y_CBCR_H2V2) | \
-                           (img == MDP_Y_CRCB_H2V1) | \
-                           (img == MDP_Y_CBCR_H2V1))
-
-/* Mappings from addr to purpose */
-#define PPP_ADDR_SRC_ROI               MDP_FULL_BYPASS_WORD2
-#define PPP_ADDR_SRC0                  MDP_FULL_BYPASS_WORD3
-#define PPP_ADDR_SRC1                  MDP_FULL_BYPASS_WORD4
-#define PPP_ADDR_SRC_YSTRIDE           MDP_FULL_BYPASS_WORD7
-#define PPP_ADDR_SRC_CFG               MDP_FULL_BYPASS_WORD9
-#define PPP_ADDR_SRC_PACK_PATTERN      MDP_FULL_BYPASS_WORD10
-#define PPP_ADDR_OPERATION             MDP_FULL_BYPASS_WORD14
-#define PPP_ADDR_PHASEX_INIT           MDP_FULL_BYPASS_WORD15
-#define PPP_ADDR_PHASEY_INIT           MDP_FULL_BYPASS_WORD16
-#define PPP_ADDR_PHASEX_STEP           MDP_FULL_BYPASS_WORD17
-#define PPP_ADDR_PHASEY_STEP           MDP_FULL_BYPASS_WORD18
-#define PPP_ADDR_ALPHA_TRANSP          MDP_FULL_BYPASS_WORD19
-#define PPP_ADDR_DST_CFG               MDP_FULL_BYPASS_WORD20
-#define PPP_ADDR_DST_PACK_PATTERN      MDP_FULL_BYPASS_WORD21
-#define PPP_ADDR_DST_ROI               MDP_FULL_BYPASS_WORD25
-#define PPP_ADDR_DST0                  MDP_FULL_BYPASS_WORD26
-#define PPP_ADDR_DST1                  MDP_FULL_BYPASS_WORD27
-#define PPP_ADDR_DST_YSTRIDE           MDP_FULL_BYPASS_WORD30
-#define PPP_ADDR_EDGE                  MDP_FULL_BYPASS_WORD46
-#define PPP_ADDR_BG0                   MDP_FULL_BYPASS_WORD48
-#define PPP_ADDR_BG1                   MDP_FULL_BYPASS_WORD49
-#define PPP_ADDR_BG_YSTRIDE            MDP_FULL_BYPASS_WORD51
-#define PPP_ADDR_BG_CFG                        MDP_FULL_BYPASS_WORD53
-#define PPP_ADDR_BG_PACK_PATTERN       MDP_FULL_BYPASS_WORD54
-
-/* MDP_DMA_CONFIG / MDP_FULL_BYPASS_WORD32 */
-#define DMA_DSTC0G_6BITS (1<<1)
-#define DMA_DSTC1B_6BITS (1<<3)
-#define DMA_DSTC2R_6BITS (1<<5)
-#define DMA_DSTC0G_5BITS (1<<0)
-#define DMA_DSTC1B_5BITS (1<<2)
-#define DMA_DSTC2R_5BITS (1<<4)
-
-#define DMA_PACK_TIGHT (1<<6)
-#define DMA_PACK_LOOSE 0
-#define DMA_PACK_ALIGN_LSB 0
-#define DMA_PACK_ALIGN_MSB (1<<7)
-#define DMA_PACK_PATTERN_RGB \
-       (MDP_GET_PACK_PATTERN(0, CLR_R, CLR_G, CLR_B, 2)<<8)
-
-#define DMA_OUT_SEL_AHB  0
-#define DMA_OUT_SEL_MDDI (1<<14)
-#define DMA_AHBM_LCD_SEL_PRIMARY 0
-#define DMA_AHBM_LCD_SEL_SECONDARY (1<<15)
-#define DMA_IBUF_C3ALPHA_EN (1<<16)
-#define DMA_DITHER_EN (1<<17)
-
-#define DMA_MDDI_DMAOUT_LCD_SEL_PRIMARY 0
-#define DMA_MDDI_DMAOUT_LCD_SEL_SECONDARY (1<<18)
-#define DMA_MDDI_DMAOUT_LCD_SEL_EXTERNAL (1<<19)
-
-#define DMA_IBUF_FORMAT_RGB565 (1<<20)
-#define DMA_IBUF_FORMAT_RGB888_OR_ARGB8888 0
-
-#define DMA_IBUF_NONCONTIGUOUS (1<<21)
-
-/* MDDI REGISTER ? */
-#define MDDI_VDO_PACKET_DESC  0x5666
-#define MDDI_VDO_PACKET_PRIM  0xC3
-#define MDDI_VDO_PACKET_SECD  0xC0
-
-#endif
diff --git a/drivers/video/fbdev/msm/mdp_ppp.c b/drivers/video/fbdev/msm/mdp_ppp.c
deleted file mode 100644 (file)
index be6079c..0000000
+++ /dev/null
@@ -1,731 +0,0 @@
-/* drivers/video/msm/mdp_ppp.c
- *
- * Copyright (C) 2007 QUALCOMM Incorporated
- * Copyright (C) 2007 Google Incorporated
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- */
-#include <linux/fb.h>
-#include <linux/file.h>
-#include <linux/delay.h>
-#include <linux/msm_mdp.h>
-#include <linux/platform_data/video-msm_fb.h>
-
-#include "mdp_hw.h"
-#include "mdp_scale_tables.h"
-
-#define DLOG(x...) do {} while (0)
-
-#define MDP_DOWNSCALE_BLUR (MDP_DOWNSCALE_MAX + 1)
-static int downscale_y_table = MDP_DOWNSCALE_MAX;
-static int downscale_x_table = MDP_DOWNSCALE_MAX;
-
-struct mdp_regs {
-       uint32_t src0;
-       uint32_t src1;
-       uint32_t dst0;
-       uint32_t dst1;
-       uint32_t src_cfg;
-       uint32_t dst_cfg;
-       uint32_t src_pack;
-       uint32_t dst_pack;
-       uint32_t src_rect;
-       uint32_t dst_rect;
-       uint32_t src_ystride;
-       uint32_t dst_ystride;
-       uint32_t op;
-       uint32_t src_bpp;
-       uint32_t dst_bpp;
-       uint32_t edge;
-       uint32_t phasex_init;
-       uint32_t phasey_init;
-       uint32_t phasex_step;
-       uint32_t phasey_step;
-};
-
-static uint32_t pack_pattern[] = {
-       PPP_ARRAY0(PACK_PATTERN)
-};
-
-static uint32_t src_img_cfg[] = {
-       PPP_ARRAY1(CFG, SRC)
-};
-
-static uint32_t dst_img_cfg[] = {
-       PPP_ARRAY1(CFG, DST)
-};
-
-static uint32_t bytes_per_pixel[] = {
-       [MDP_RGB_565] = 2,
-       [MDP_RGB_888] = 3,
-       [MDP_XRGB_8888] = 4,
-       [MDP_ARGB_8888] = 4,
-       [MDP_RGBA_8888] = 4,
-       [MDP_BGRA_8888] = 4,
-       [MDP_RGBX_8888] = 4,
-       [MDP_Y_CBCR_H2V1] = 1,
-       [MDP_Y_CBCR_H2V2] = 1,
-       [MDP_Y_CRCB_H2V1] = 1,
-       [MDP_Y_CRCB_H2V2] = 1,
-       [MDP_YCRYCB_H2V1] = 2
-};
-
-static uint32_t dst_op_chroma[] = {
-       PPP_ARRAY1(CHROMA_SAMP, DST)
-};
-
-static uint32_t src_op_chroma[] = {
-       PPP_ARRAY1(CHROMA_SAMP, SRC)
-};
-
-static uint32_t bg_op_chroma[] = {
-       PPP_ARRAY1(CHROMA_SAMP, BG)
-};
-
-static void rotate_dst_addr_x(struct mdp_blit_req *req, struct mdp_regs *regs)
-{
-       regs->dst0 += (req->dst_rect.w -
-                      min((uint32_t)16, req->dst_rect.w)) * regs->dst_bpp;
-       regs->dst1 += (req->dst_rect.w -
-                      min((uint32_t)16, req->dst_rect.w)) * regs->dst_bpp;
-}
-
-static void rotate_dst_addr_y(struct mdp_blit_req *req, struct mdp_regs *regs)
-{
-       regs->dst0 += (req->dst_rect.h -
-                      min((uint32_t)16, req->dst_rect.h)) *
-                      regs->dst_ystride;
-       regs->dst1 += (req->dst_rect.h -
-                      min((uint32_t)16, req->dst_rect.h)) *
-                      regs->dst_ystride;
-}
-
-static void blit_rotate(struct mdp_blit_req *req,
-                       struct mdp_regs *regs)
-{
-       if (req->flags == MDP_ROT_NOP)
-               return;
-
-       regs->op |= PPP_OP_ROT_ON;
-       if ((req->flags & MDP_ROT_90 || req->flags & MDP_FLIP_LR) &&
-           !(req->flags & MDP_ROT_90 && req->flags & MDP_FLIP_LR))
-               rotate_dst_addr_x(req, regs);
-       if (req->flags & MDP_ROT_90)
-               regs->op |= PPP_OP_ROT_90;
-       if (req->flags & MDP_FLIP_UD) {
-               regs->op |= PPP_OP_FLIP_UD;
-               rotate_dst_addr_y(req, regs);
-       }
-       if (req->flags & MDP_FLIP_LR)
-               regs->op |= PPP_OP_FLIP_LR;
-}
-
-static void blit_convert(struct mdp_blit_req *req, struct mdp_regs *regs)
-{
-       if (req->src.format == req->dst.format)
-               return;
-       if (IS_RGB(req->src.format) && IS_YCRCB(req->dst.format)) {
-               regs->op |= PPP_OP_CONVERT_RGB2YCBCR | PPP_OP_CONVERT_ON;
-       } else if (IS_YCRCB(req->src.format) && IS_RGB(req->dst.format)) {
-               regs->op |= PPP_OP_CONVERT_YCBCR2RGB | PPP_OP_CONVERT_ON;
-               if (req->dst.format == MDP_RGB_565)
-                       regs->op |= PPP_OP_CONVERT_MATRIX_SECONDARY;
-       }
-}
-
-#define GET_BIT_RANGE(value, high, low) \
-       (((1 << (high - low + 1)) - 1) & (value >> low))
-static uint32_t transp_convert(struct mdp_blit_req *req)
-{
-       uint32_t transp = 0;
-       if (req->src.format == MDP_RGB_565) {
-               /* pad each value to 8 bits by copying the high bits into the
-                * low end, convert RGB to RBG by switching low 2 components */
-               transp |= ((GET_BIT_RANGE(req->transp_mask, 15, 11) << 3) |
-                          (GET_BIT_RANGE(req->transp_mask, 15, 13))) << 16;
-
-               transp |= ((GET_BIT_RANGE(req->transp_mask, 4, 0) << 3) |
-                          (GET_BIT_RANGE(req->transp_mask, 4, 2))) << 8;
-
-               transp |= (GET_BIT_RANGE(req->transp_mask, 10, 5) << 2) |
-                         (GET_BIT_RANGE(req->transp_mask, 10, 9));
-       } else {
-               /* convert RGB to RBG */
-               transp |= (GET_BIT_RANGE(req->transp_mask, 15, 8)) |
-                         (GET_BIT_RANGE(req->transp_mask, 23, 16) << 16) |
-                         (GET_BIT_RANGE(req->transp_mask, 7, 0) << 8);
-       }
-       return transp;
-}
-#undef GET_BIT_RANGE
-
-static void blit_blend(struct mdp_blit_req *req, struct mdp_regs *regs)
-{
-       /* TRANSP BLEND */
-       if (req->transp_mask != MDP_TRANSP_NOP) {
-               req->transp_mask = transp_convert(req);
-               if (req->alpha != MDP_ALPHA_NOP) {
-                       /* use blended transparancy mode
-                        * pixel = (src == transp) ? dst : blend
-                        * blend is combo of blend_eq_sel and
-                        * blend_alpha_sel */
-                       regs->op |= PPP_OP_ROT_ON | PPP_OP_BLEND_ON |
-                               PPP_OP_BLEND_ALPHA_BLEND_NORMAL |
-                               PPP_OP_BLEND_CONSTANT_ALPHA |
-                               PPP_BLEND_ALPHA_TRANSP;
-               } else {
-                       /* simple transparancy mode
-                        * pixel = (src == transp) ? dst : src */
-                       regs->op |= PPP_OP_ROT_ON | PPP_OP_BLEND_ON |
-                               PPP_OP_BLEND_SRCPIXEL_TRANSP;
-               }
-       }
-
-       req->alpha &= 0xff;
-       /* ALPHA BLEND */
-       if (HAS_ALPHA(req->src.format)) {
-               regs->op |= PPP_OP_ROT_ON | PPP_OP_BLEND_ON |
-                       PPP_OP_BLEND_SRCPIXEL_ALPHA;
-       } else if (req->alpha < MDP_ALPHA_NOP) {
-               /* just blend by alpha */
-               regs->op |= PPP_OP_ROT_ON | PPP_OP_BLEND_ON |
-                       PPP_OP_BLEND_ALPHA_BLEND_NORMAL |
-                       PPP_OP_BLEND_CONSTANT_ALPHA;
-       }
-
-       regs->op |= bg_op_chroma[req->dst.format];
-}
-
-#define ONE_HALF       (1LL << 32)
-#define ONE            (1LL << 33)
-#define TWO            (2LL << 33)
-#define THREE          (3LL << 33)
-#define FRAC_MASK (ONE - 1)
-#define INT_MASK (~FRAC_MASK)
-
-static int scale_params(uint32_t dim_in, uint32_t dim_out, uint32_t origin,
-                       uint32_t *phase_init, uint32_t *phase_step)
-{
-       /* to improve precicsion calculations are done in U31.33 and converted
-        * to U3.29 at the end */
-       int64_t k1, k2, k3, k4, tmp;
-       uint64_t n, d, os, os_p, od, od_p, oreq;
-       unsigned rpa = 0;
-       int64_t ip64, delta;
-
-       if (dim_out % 3 == 0)
-               rpa = !(dim_in % (dim_out / 3));
-
-       n = ((uint64_t)dim_out) << 34;
-       d = dim_in;
-       if (!d)
-               return -1;
-       do_div(n, d);
-       k3 = (n + 1) >> 1;
-       if ((k3 >> 4) < (1LL << 27) || (k3 >> 4) > (1LL << 31)) {
-               DLOG("crap bad scale\n");
-               return -1;
-       }
-       n = ((uint64_t)dim_in) << 34;
-       d = (uint64_t)dim_out;
-       if (!d)
-               return -1;
-       do_div(n, d);
-       k1 = (n + 1) >> 1;
-       k2 = (k1 - ONE) >> 1;
-
-       *phase_init = (int)(k2 >> 4);
-       k4 = (k3 - ONE) >> 1;
-
-       if (rpa) {
-               os = ((uint64_t)origin << 33) - ONE_HALF;
-               tmp = (dim_out * os) + ONE_HALF;
-               if (!dim_in)
-                       return -1;
-               do_div(tmp, dim_in);
-               od = tmp - ONE_HALF;
-       } else {
-               os = ((uint64_t)origin << 1) - 1;
-               od = (((k3 * os) >> 1) + k4);
-       }
-
-       od_p = od & INT_MASK;
-       if (od_p != od)
-               od_p += ONE;
-
-       if (rpa) {
-               tmp = (dim_in * od_p) + ONE_HALF;
-               if (!dim_in)
-                       return -1;
-               do_div(tmp, dim_in);
-               os_p = tmp - ONE_HALF;
-       } else {
-               os_p = ((k1 * (od_p >> 33)) + k2);
-       }
-
-       oreq = (os_p & INT_MASK) - ONE;
-
-       ip64 = os_p - oreq;
-       delta = ((int64_t)(origin) << 33) - oreq;
-       ip64 -= delta;
-       /* limit to valid range before the left shift */
-       delta = (ip64 & (1LL << 63)) ? 4 : -4;
-       delta <<= 33;
-       while (abs((int)(ip64 >> 33)) > 4)
-               ip64 += delta;
-       *phase_init = (int)(ip64 >> 4);
-       *phase_step = (uint32_t)(k1 >> 4);
-       return 0;
-}
-
-static void load_scale_table(const struct mdp_info *mdp,
-                            struct mdp_table_entry *table, int len)
-{
-       int i;
-       for (i = 0; i < len; i++)
-               mdp_writel(mdp, table[i].val, table[i].reg);
-}
-
-enum {
-IMG_LEFT,
-IMG_RIGHT,
-IMG_TOP,
-IMG_BOTTOM,
-};
-
-static void get_edge_info(uint32_t src, uint32_t src_coord, uint32_t dst,
-                         uint32_t *interp1, uint32_t *interp2,
-                         uint32_t *repeat1, uint32_t *repeat2) {
-       if (src > 3 * dst) {
-               *interp1 = 0;
-               *interp2 = src - 1;
-               *repeat1 = 0;
-               *repeat2 = 0;
-       } else if (src == 3 * dst) {
-               *interp1 = 0;
-               *interp2 = src;
-               *repeat1 = 0;
-               *repeat2 = 1;
-       } else if (src > dst && src < 3 * dst) {
-               *interp1 = -1;
-               *interp2 = src;
-               *repeat1 = 1;
-               *repeat2 = 1;
-       } else if (src == dst) {
-               *interp1 = -1;
-               *interp2 = src + 1;
-               *repeat1 = 1;
-               *repeat2 = 2;
-       } else {
-               *interp1 = -2;
-               *interp2 = src + 1;
-               *repeat1 = 2;
-               *repeat2 = 2;
-       }
-       *interp1 += src_coord;
-       *interp2 += src_coord;
-}
-
-static int get_edge_cond(struct mdp_blit_req *req, struct mdp_regs *regs)
-{
-       int32_t luma_interp[4];
-       int32_t luma_repeat[4];
-       int32_t chroma_interp[4];
-       int32_t chroma_bound[4];
-       int32_t chroma_repeat[4];
-       uint32_t dst_w, dst_h;
-
-       memset(&luma_interp, 0, sizeof(int32_t) * 4);
-       memset(&luma_repeat, 0, sizeof(int32_t) * 4);
-       memset(&chroma_interp, 0, sizeof(int32_t) * 4);
-       memset(&chroma_bound, 0, sizeof(int32_t) * 4);
-       memset(&chroma_repeat, 0, sizeof(int32_t) * 4);
-       regs->edge = 0;
-
-       if (req->flags & MDP_ROT_90) {
-               dst_w = req->dst_rect.h;
-               dst_h = req->dst_rect.w;
-       } else {
-               dst_w = req->dst_rect.w;
-               dst_h = req->dst_rect.h;
-       }
-
-       if (regs->op & (PPP_OP_SCALE_Y_ON | PPP_OP_SCALE_X_ON)) {
-               get_edge_info(req->src_rect.h, req->src_rect.y, dst_h,
-                             &luma_interp[IMG_TOP], &luma_interp[IMG_BOTTOM],
-                             &luma_repeat[IMG_TOP], &luma_repeat[IMG_BOTTOM]);
-               get_edge_info(req->src_rect.w, req->src_rect.x, dst_w,
-                             &luma_interp[IMG_LEFT], &luma_interp[IMG_RIGHT],
-                             &luma_repeat[IMG_LEFT], &luma_repeat[IMG_RIGHT]);
-       } else {
-               luma_interp[IMG_LEFT] = req->src_rect.x;
-               luma_interp[IMG_RIGHT] = req->src_rect.x + req->src_rect.w - 1;
-               luma_interp[IMG_TOP] = req->src_rect.y;
-               luma_interp[IMG_BOTTOM] = req->src_rect.y + req->src_rect.h - 1;
-               luma_repeat[IMG_LEFT] = 0;
-               luma_repeat[IMG_TOP] = 0;
-               luma_repeat[IMG_RIGHT] = 0;
-               luma_repeat[IMG_BOTTOM] = 0;
-       }
-
-       chroma_interp[IMG_LEFT] = luma_interp[IMG_LEFT];
-       chroma_interp[IMG_RIGHT] = luma_interp[IMG_RIGHT];
-       chroma_interp[IMG_TOP] = luma_interp[IMG_TOP];
-       chroma_interp[IMG_BOTTOM] = luma_interp[IMG_BOTTOM];
-
-       chroma_bound[IMG_LEFT] = req->src_rect.x;
-       chroma_bound[IMG_RIGHT] = req->src_rect.x + req->src_rect.w - 1;
-       chroma_bound[IMG_TOP] = req->src_rect.y;
-       chroma_bound[IMG_BOTTOM] = req->src_rect.y + req->src_rect.h - 1;
-
-       if (IS_YCRCB(req->src.format)) {
-               chroma_interp[IMG_LEFT] = chroma_interp[IMG_LEFT] >> 1;
-               chroma_interp[IMG_RIGHT] = (chroma_interp[IMG_RIGHT] + 1) >> 1;
-
-               chroma_bound[IMG_LEFT] = chroma_bound[IMG_LEFT] >> 1;
-               chroma_bound[IMG_RIGHT] = chroma_bound[IMG_RIGHT] >> 1;
-       }
-
-       if (req->src.format == MDP_Y_CBCR_H2V2 ||
-           req->src.format == MDP_Y_CRCB_H2V2) {
-               chroma_interp[IMG_TOP] = (chroma_interp[IMG_TOP] - 1) >> 1;
-               chroma_interp[IMG_BOTTOM] = (chroma_interp[IMG_BOTTOM] + 1)
-                                           >> 1;
-               chroma_bound[IMG_TOP] = (chroma_bound[IMG_TOP] + 1) >> 1;
-               chroma_bound[IMG_BOTTOM] = chroma_bound[IMG_BOTTOM] >> 1;
-       }
-
-       chroma_repeat[IMG_LEFT] = chroma_bound[IMG_LEFT] -
-                                 chroma_interp[IMG_LEFT];
-       chroma_repeat[IMG_RIGHT] = chroma_interp[IMG_RIGHT] -
-                                 chroma_bound[IMG_RIGHT];
-       chroma_repeat[IMG_TOP] = chroma_bound[IMG_TOP] -
-                                 chroma_interp[IMG_TOP];
-       chroma_repeat[IMG_BOTTOM] = chroma_interp[IMG_BOTTOM] -
-                                 chroma_bound[IMG_BOTTOM];
-
-       if (chroma_repeat[IMG_LEFT] < 0 || chroma_repeat[IMG_LEFT] > 3 ||
-           chroma_repeat[IMG_RIGHT] < 0 || chroma_repeat[IMG_RIGHT] > 3 ||
-           chroma_repeat[IMG_TOP] < 0 || chroma_repeat[IMG_TOP] > 3 ||
-           chroma_repeat[IMG_BOTTOM] < 0 || chroma_repeat[IMG_BOTTOM] > 3 ||
-           luma_repeat[IMG_LEFT] < 0 || luma_repeat[IMG_LEFT] > 3 ||
-           luma_repeat[IMG_RIGHT] < 0 || luma_repeat[IMG_RIGHT] > 3 ||
-           luma_repeat[IMG_TOP] < 0 || luma_repeat[IMG_TOP] > 3 ||
-           luma_repeat[IMG_BOTTOM] < 0 || luma_repeat[IMG_BOTTOM] > 3)
-               return -1;
-
-       regs->edge |= (chroma_repeat[IMG_LEFT] & 3) << MDP_LEFT_CHROMA;
-       regs->edge |= (chroma_repeat[IMG_RIGHT] & 3) << MDP_RIGHT_CHROMA;
-       regs->edge |= (chroma_repeat[IMG_TOP] & 3) << MDP_TOP_CHROMA;
-       regs->edge |= (chroma_repeat[IMG_BOTTOM] & 3) << MDP_BOTTOM_CHROMA;
-       regs->edge |= (luma_repeat[IMG_LEFT] & 3) << MDP_LEFT_LUMA;
-       regs->edge |= (luma_repeat[IMG_RIGHT] & 3) << MDP_RIGHT_LUMA;
-       regs->edge |= (luma_repeat[IMG_TOP] & 3) << MDP_TOP_LUMA;
-       regs->edge |= (luma_repeat[IMG_BOTTOM] & 3) << MDP_BOTTOM_LUMA;
-       return 0;
-}
-
-static int blit_scale(const struct mdp_info *mdp, struct mdp_blit_req *req,
-                     struct mdp_regs *regs)
-{
-       uint32_t phase_init_x, phase_init_y, phase_step_x, phase_step_y;
-       uint32_t scale_factor_x, scale_factor_y;
-       uint32_t downscale;
-       uint32_t dst_w, dst_h;
-
-       if (req->flags & MDP_ROT_90) {
-               dst_w = req->dst_rect.h;
-               dst_h = req->dst_rect.w;
-       } else {
-               dst_w = req->dst_rect.w;
-               dst_h = req->dst_rect.h;
-       }
-       if ((req->src_rect.w == dst_w)  && (req->src_rect.h == dst_h) &&
-           !(req->flags & MDP_BLUR)) {
-               regs->phasex_init = 0;
-               regs->phasey_init = 0;
-               regs->phasex_step = 0;
-               regs->phasey_step = 0;
-               return 0;
-       }
-
-       if (scale_params(req->src_rect.w, dst_w, 1, &phase_init_x,
-                        &phase_step_x) ||
-           scale_params(req->src_rect.h, dst_h, 1, &phase_init_y,
-                        &phase_step_y))
-               return -1;
-
-       scale_factor_x = (dst_w * 10) / req->src_rect.w;
-       scale_factor_y = (dst_h * 10) / req->src_rect.h;
-
-       if (scale_factor_x > 8)
-               downscale = MDP_DOWNSCALE_PT8TO1;
-       else if (scale_factor_x > 6)
-               downscale = MDP_DOWNSCALE_PT6TOPT8;
-       else if (scale_factor_x > 4)
-               downscale = MDP_DOWNSCALE_PT4TOPT6;
-       else
-               downscale = MDP_DOWNSCALE_PT2TOPT4;
-       if (downscale != downscale_x_table) {
-               load_scale_table(mdp, mdp_downscale_x_table[downscale], 64);
-               downscale_x_table = downscale;
-       }
-
-       if (scale_factor_y > 8)
-               downscale = MDP_DOWNSCALE_PT8TO1;
-       else if (scale_factor_y > 6)
-               downscale = MDP_DOWNSCALE_PT6TOPT8;
-       else if (scale_factor_y > 4)
-               downscale = MDP_DOWNSCALE_PT4TOPT6;
-       else
-               downscale = MDP_DOWNSCALE_PT2TOPT4;
-       if (downscale != downscale_y_table) {
-               load_scale_table(mdp, mdp_downscale_y_table[downscale], 64);
-               downscale_y_table = downscale;
-       }
-
-       regs->phasex_init = phase_init_x;
-       regs->phasey_init = phase_init_y;
-       regs->phasex_step = phase_step_x;
-       regs->phasey_step = phase_step_y;
-       regs->op |= (PPP_OP_SCALE_Y_ON | PPP_OP_SCALE_X_ON);
-       return 0;
-
-}
-
-static void blit_blur(const struct mdp_info *mdp, struct mdp_blit_req *req,
-                     struct mdp_regs *regs)
-{
-       if (!(req->flags & MDP_BLUR))
-               return;
-
-       if (!(downscale_x_table == MDP_DOWNSCALE_BLUR &&
-             downscale_y_table == MDP_DOWNSCALE_BLUR)) {
-               load_scale_table(mdp, mdp_gaussian_blur_table, 128);
-               downscale_x_table = MDP_DOWNSCALE_BLUR;
-               downscale_y_table = MDP_DOWNSCALE_BLUR;
-       }
-
-       regs->op |= (PPP_OP_SCALE_Y_ON | PPP_OP_SCALE_X_ON);
-}
-
-
-#define IMG_LEN(rect_h, w, rect_w, bpp) (((rect_h) * w) * bpp)
-
-#define Y_TO_CRCB_RATIO(format) \
-       ((format == MDP_Y_CBCR_H2V2 || format == MDP_Y_CRCB_H2V2) ?  2 :\
-        (format == MDP_Y_CBCR_H2V1 || format == MDP_Y_CRCB_H2V1) ?  1 : 1)
-
-static void get_len(struct mdp_img *img, struct mdp_rect *rect, uint32_t bpp,
-                   uint32_t *len0, uint32_t *len1)
-{
-       *len0 = IMG_LEN(rect->h, img->width, rect->w, bpp);
-       if (IS_PSEUDOPLNR(img->format))
-               *len1 = *len0/Y_TO_CRCB_RATIO(img->format);
-       else
-               *len1 = 0;
-}
-
-static int valid_src_dst(unsigned long src_start, unsigned long src_len,
-                        unsigned long dst_start, unsigned long dst_len,
-                        struct mdp_blit_req *req, struct mdp_regs *regs)
-{
-       unsigned long src_min_ok = src_start;
-       unsigned long src_max_ok = src_start + src_len;
-       unsigned long dst_min_ok = dst_start;
-       unsigned long dst_max_ok = dst_start + dst_len;
-       uint32_t src0_len, src1_len, dst0_len, dst1_len;
-       get_len(&req->src, &req->src_rect, regs->src_bpp, &src0_len,
-                &src1_len);
-       get_len(&req->dst, &req->dst_rect, regs->dst_bpp, &dst0_len,
-                &dst1_len);
-
-       if (regs->src0 < src_min_ok || regs->src0 > src_max_ok ||
-           regs->src0 + src0_len > src_max_ok) {
-               DLOG("invalid_src %x %x %lx %lx\n", regs->src0,
-                     src0_len, src_min_ok, src_max_ok);
-               return 0;
-       }
-       if (regs->src_cfg & PPP_SRC_PLANE_PSEUDOPLNR) {
-               if (regs->src1 < src_min_ok || regs->src1 > src_max_ok ||
-                   regs->src1 + src1_len > src_max_ok) {
-                       DLOG("invalid_src1");
-                       return 0;
-               }
-       }
-       if (regs->dst0 < dst_min_ok || regs->dst0 > dst_max_ok ||
-           regs->dst0 + dst0_len > dst_max_ok) {
-               DLOG("invalid_dst");
-               return 0;
-       }
-       if (regs->dst_cfg & PPP_SRC_PLANE_PSEUDOPLNR) {
-               if (regs->dst1 < dst_min_ok || regs->dst1 > dst_max_ok ||
-                   regs->dst1 + dst1_len > dst_max_ok) {
-                       DLOG("invalid_dst1");
-                       return 0;
-               }
-       }
-       return 1;
-}
-
-
-static void flush_imgs(struct mdp_blit_req *req, struct mdp_regs *regs,
-                      struct file *src_file, struct file *dst_file)
-{
-}
-
-static void get_chroma_addr(struct mdp_img *img, struct mdp_rect *rect,
-                           uint32_t base, uint32_t bpp, uint32_t cfg,
-                           uint32_t *addr, uint32_t *ystride)
-{
-       uint32_t compress_v = Y_TO_CRCB_RATIO(img->format);
-       uint32_t compress_h = 2;
-       uint32_t  offset;
-
-       if (IS_PSEUDOPLNR(img->format)) {
-               offset = (rect->x / compress_h) * compress_h;
-               offset += rect->y == 0 ? 0 :
-                         ((rect->y + 1) / compress_v) * img->width;
-               *addr = base + (img->width * img->height * bpp);
-               *addr += offset * bpp;
-               *ystride |= *ystride << 16;
-       } else {
-               *addr = 0;
-       }
-}
-
-static int send_blit(const struct mdp_info *mdp, struct mdp_blit_req *req,
-                    struct mdp_regs *regs, struct file *src_file,
-                    struct file *dst_file)
-{
-       mdp_writel(mdp, 1, 0x060);
-       mdp_writel(mdp, regs->src_rect, PPP_ADDR_SRC_ROI);
-       mdp_writel(mdp, regs->src0, PPP_ADDR_SRC0);
-       mdp_writel(mdp, regs->src1, PPP_ADDR_SRC1);
-       mdp_writel(mdp, regs->src_ystride, PPP_ADDR_SRC_YSTRIDE);
-       mdp_writel(mdp, regs->src_cfg, PPP_ADDR_SRC_CFG);
-       mdp_writel(mdp, regs->src_pack, PPP_ADDR_SRC_PACK_PATTERN);
-
-       mdp_writel(mdp, regs->op, PPP_ADDR_OPERATION);
-       mdp_writel(mdp, regs->phasex_init, PPP_ADDR_PHASEX_INIT);
-       mdp_writel(mdp, regs->phasey_init, PPP_ADDR_PHASEY_INIT);
-       mdp_writel(mdp, regs->phasex_step, PPP_ADDR_PHASEX_STEP);
-       mdp_writel(mdp, regs->phasey_step, PPP_ADDR_PHASEY_STEP);
-
-       mdp_writel(mdp, (req->alpha << 24) | (req->transp_mask & 0xffffff),
-              PPP_ADDR_ALPHA_TRANSP);
-
-       mdp_writel(mdp, regs->dst_cfg, PPP_ADDR_DST_CFG);
-       mdp_writel(mdp, regs->dst_pack, PPP_ADDR_DST_PACK_PATTERN);
-       mdp_writel(mdp, regs->dst_rect, PPP_ADDR_DST_ROI);
-       mdp_writel(mdp, regs->dst0, PPP_ADDR_DST0);
-       mdp_writel(mdp, regs->dst1, PPP_ADDR_DST1);
-       mdp_writel(mdp, regs->dst_ystride, PPP_ADDR_DST_YSTRIDE);
-
-       mdp_writel(mdp, regs->edge, PPP_ADDR_EDGE);
-       if (regs->op & PPP_OP_BLEND_ON) {
-               mdp_writel(mdp, regs->dst0, PPP_ADDR_BG0);
-               mdp_writel(mdp, regs->dst1, PPP_ADDR_BG1);
-               mdp_writel(mdp, regs->dst_ystride, PPP_ADDR_BG_YSTRIDE);
-               mdp_writel(mdp, src_img_cfg[req->dst.format], PPP_ADDR_BG_CFG);
-               mdp_writel(mdp, pack_pattern[req->dst.format],
-                          PPP_ADDR_BG_PACK_PATTERN);
-       }
-       flush_imgs(req, regs, src_file, dst_file);
-       mdp_writel(mdp, 0x1000, MDP_DISPLAY0_START);
-       return 0;
-}
-
-int mdp_ppp_blit(const struct mdp_info *mdp, struct mdp_blit_req *req,
-                struct file *src_file, unsigned long src_start, unsigned long src_len,
-                struct file *dst_file, unsigned long dst_start, unsigned long dst_len)
-{
-       struct mdp_regs regs = {0};
-
-       if (unlikely(req->src.format >= MDP_IMGTYPE_LIMIT ||
-                    req->dst.format >= MDP_IMGTYPE_LIMIT)) {
-               printk(KERN_ERR "mpd_ppp: img is of wrong format\n");
-               return -EINVAL;
-       }
-
-       if (unlikely(req->src_rect.x > req->src.width ||
-                    req->src_rect.y > req->src.height ||
-                    req->dst_rect.x > req->dst.width ||
-                    req->dst_rect.y > req->dst.height)) {
-               printk(KERN_ERR "mpd_ppp: img rect is outside of img!\n");
-               return -EINVAL;
-       }
-
-       /* set the src image configuration */
-       regs.src_cfg = src_img_cfg[req->src.format];
-       regs.src_cfg |= (req->src_rect.x & 0x1) ? PPP_SRC_BPP_ROI_ODD_X : 0;
-       regs.src_cfg |= (req->src_rect.y & 0x1) ? PPP_SRC_BPP_ROI_ODD_Y : 0;
-       regs.src_rect = (req->src_rect.h << 16) | req->src_rect.w;
-       regs.src_pack = pack_pattern[req->src.format];
-
-       /* set the dest image configuration */
-       regs.dst_cfg = dst_img_cfg[req->dst.format] | PPP_DST_OUT_SEL_AXI;
-       regs.dst_rect = (req->dst_rect.h << 16) | req->dst_rect.w;
-       regs.dst_pack = pack_pattern[req->dst.format];
-
-       /* set src, bpp, start pixel and ystride */
-       regs.src_bpp = bytes_per_pixel[req->src.format];
-       regs.src0 = src_start + req->src.offset;
-       regs.src_ystride = req->src.width * regs.src_bpp;
-       get_chroma_addr(&req->src, &req->src_rect, regs.src0, regs.src_bpp,
-                       regs.src_cfg, &regs.src1, &regs.src_ystride);
-       regs.src0 += (req->src_rect.x + (req->src_rect.y * req->src.width)) *
-                     regs.src_bpp;
-
-       /* set dst, bpp, start pixel and ystride */
-       regs.dst_bpp = bytes_per_pixel[req->dst.format];
-       regs.dst0 = dst_start + req->dst.offset;
-       regs.dst_ystride = req->dst.width * regs.dst_bpp;
-       get_chroma_addr(&req->dst, &req->dst_rect, regs.dst0, regs.dst_bpp,
-                       regs.dst_cfg, &regs.dst1, &regs.dst_ystride);
-       regs.dst0 += (req->dst_rect.x + (req->dst_rect.y * req->dst.width)) *
-                     regs.dst_bpp;
-
-       if (!valid_src_dst(src_start, src_len, dst_start, dst_len, req,
-                          &regs)) {
-               printk(KERN_ERR "mpd_ppp: final src or dst location is "
-                       "invalid, are you trying to make an image too large "
-                       "or to place it outside the screen?\n");
-               return -EINVAL;
-       }
-
-       /* set up operation register */
-       regs.op = 0;
-       blit_rotate(req, &regs);
-       blit_convert(req, &regs);
-       if (req->flags & MDP_DITHER)
-               regs.op |= PPP_OP_DITHER_EN;
-       blit_blend(req, &regs);
-       if (blit_scale(mdp, req, &regs)) {
-               printk(KERN_ERR "mpd_ppp: error computing scale for img.\n");
-               return -EINVAL;
-       }
-       blit_blur(mdp, req, &regs);
-       regs.op |= dst_op_chroma[req->dst.format] |
-                  src_op_chroma[req->src.format];
-
-       /* if the image is YCRYCB, the x and w must be even */
-       if (unlikely(req->src.format == MDP_YCRYCB_H2V1)) {
-               req->src_rect.x = req->src_rect.x & (~0x1);
-               req->src_rect.w = req->src_rect.w & (~0x1);
-               req->dst_rect.x = req->dst_rect.x & (~0x1);
-               req->dst_rect.w = req->dst_rect.w & (~0x1);
-       }
-       if (get_edge_cond(req, &regs))
-               return -EINVAL;
-
-       send_blit(mdp, req, &regs, src_file, dst_file);
-       return 0;
-}
diff --git a/drivers/video/fbdev/msm/mdp_scale_tables.c b/drivers/video/fbdev/msm/mdp_scale_tables.c
deleted file mode 100644 (file)
index 604783b..0000000
+++ /dev/null
@@ -1,766 +0,0 @@
-/* drivers/video/msm_fb/mdp_scale_tables.c
- *
- * Copyright (C) 2007 QUALCOMM Incorporated
- * Copyright (C) 2007 Google Incorporated
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- */
-
-#include "mdp_scale_tables.h"
-#include "mdp_hw.h"
-
-struct mdp_table_entry mdp_upscale_table[] = {
-       { 0x5fffc, 0x0 },
-       { 0x50200, 0x7fc00000 },
-       { 0x5fffc, 0xff80000d },
-       { 0x50204, 0x7ec003f9 },
-       { 0x5fffc, 0xfec0001c },
-       { 0x50208, 0x7d4003f3 },
-       { 0x5fffc, 0xfe40002b },
-       { 0x5020c, 0x7b8003ed },
-       { 0x5fffc, 0xfd80003c },
-       { 0x50210, 0x794003e8 },
-       { 0x5fffc, 0xfcc0004d },
-       { 0x50214, 0x76c003e4 },
-       { 0x5fffc, 0xfc40005f },
-       { 0x50218, 0x73c003e0 },
-       { 0x5fffc, 0xfb800071 },
-       { 0x5021c, 0x708003de },
-       { 0x5fffc, 0xfac00085 },
-       { 0x50220, 0x6d0003db },
-       { 0x5fffc, 0xfa000098 },
-       { 0x50224, 0x698003d9 },
-       { 0x5fffc, 0xf98000ac },
-       { 0x50228, 0x654003d8 },
-       { 0x5fffc, 0xf8c000c1 },
-       { 0x5022c, 0x610003d7 },
-       { 0x5fffc, 0xf84000d5 },
-       { 0x50230, 0x5c8003d7 },
-       { 0x5fffc, 0xf7c000e9 },
-       { 0x50234, 0x580003d7 },
-       { 0x5fffc, 0xf74000fd },
-       { 0x50238, 0x534003d8 },
-       { 0x5fffc, 0xf6c00112 },
-       { 0x5023c, 0x4e8003d8 },
-       { 0x5fffc, 0xf6800126 },
-       { 0x50240, 0x494003da },
-       { 0x5fffc, 0xf600013a },
-       { 0x50244, 0x448003db },
-       { 0x5fffc, 0xf600014d },
-       { 0x50248, 0x3f4003dd },
-       { 0x5fffc, 0xf5c00160 },
-       { 0x5024c, 0x3a4003df },
-       { 0x5fffc, 0xf5c00172 },
-       { 0x50250, 0x354003e1 },
-       { 0x5fffc, 0xf5c00184 },
-       { 0x50254, 0x304003e3 },
-       { 0x5fffc, 0xf6000195 },
-       { 0x50258, 0x2b0003e6 },
-       { 0x5fffc, 0xf64001a6 },
-       { 0x5025c, 0x260003e8 },
-       { 0x5fffc, 0xf6c001b4 },
-       { 0x50260, 0x214003eb },
-       { 0x5fffc, 0xf78001c2 },
-       { 0x50264, 0x1c4003ee },
-       { 0x5fffc, 0xf80001cf },
-       { 0x50268, 0x17c003f1 },
-       { 0x5fffc, 0xf90001db },
-       { 0x5026c, 0x134003f3 },
-       { 0x5fffc, 0xfa0001e5 },
-       { 0x50270, 0xf0003f6 },
-       { 0x5fffc, 0xfb4001ee },
-       { 0x50274, 0xac003f9 },
-       { 0x5fffc, 0xfcc001f5 },
-       { 0x50278, 0x70003fb },
-       { 0x5fffc, 0xfe4001fb },
-       { 0x5027c, 0x34003fe },
-};
-
-static struct mdp_table_entry mdp_downscale_x_table_PT2TOPT4[] = {
-       { 0x5fffc, 0x740008c },
-       { 0x50280, 0x33800088 },
-       { 0x5fffc, 0x800008e },
-       { 0x50284, 0x33400084 },
-       { 0x5fffc, 0x8400092 },
-       { 0x50288, 0x33000080 },
-       { 0x5fffc, 0x9000094 },
-       { 0x5028c, 0x3300007b },
-       { 0x5fffc, 0x9c00098 },
-       { 0x50290, 0x32400077 },
-       { 0x5fffc, 0xa40009b },
-       { 0x50294, 0x32000073 },
-       { 0x5fffc, 0xb00009d },
-       { 0x50298,  0x31c0006f },
-       { 0x5fffc,  0xbc000a0 },
-       { 0x5029c,  0x3140006b },
-       { 0x5fffc,  0xc8000a2 },
-       { 0x502a0,  0x31000067 },
-       { 0x5fffc,  0xd8000a5 },
-       { 0x502a4,  0x30800062 },
-       { 0x5fffc,  0xe4000a8 },
-       { 0x502a8,  0x2fc0005f },
-       { 0x5fffc,  0xec000aa },
-       { 0x502ac,  0x2fc0005b },
-       { 0x5fffc,  0xf8000ad },
-       { 0x502b0,  0x2f400057 },
-       { 0x5fffc,  0x108000b0 },
-       { 0x502b4,  0x2e400054 },
-       { 0x5fffc,  0x114000b2 },
-       { 0x502b8,  0x2e000050 },
-       { 0x5fffc,  0x124000b4 },
-       { 0x502bc,  0x2d80004c },
-       { 0x5fffc,  0x130000b6 },
-       { 0x502c0,  0x2d000049 },
-       { 0x5fffc,  0x140000b8 },
-       { 0x502c4,  0x2c800045 },
-       { 0x5fffc,  0x150000b9 },
-       { 0x502c8,  0x2c000042 },
-       { 0x5fffc,  0x15c000bd },
-       { 0x502cc,  0x2b40003e },
-       { 0x5fffc,  0x16c000bf },
-       { 0x502d0,  0x2a80003b },
-       { 0x5fffc,  0x17c000bf },
-       { 0x502d4,  0x2a000039 },
-       { 0x5fffc,  0x188000c2 },
-       { 0x502d8,  0x29400036 },
-       { 0x5fffc,  0x19c000c4 },
-       { 0x502dc,  0x28800032 },
-       { 0x5fffc,  0x1ac000c5 },
-       { 0x502e0,  0x2800002f },
-       { 0x5fffc,  0x1bc000c7 },
-       { 0x502e4,  0x2740002c },
-       { 0x5fffc,  0x1cc000c8 },
-       { 0x502e8,  0x26c00029 },
-       { 0x5fffc,  0x1dc000c9 },
-       { 0x502ec,  0x26000027 },
-       { 0x5fffc,  0x1ec000cc },
-       { 0x502f0,  0x25000024 },
-       { 0x5fffc,  0x200000cc },
-       { 0x502f4,  0x24800021 },
-       { 0x5fffc,  0x210000cd },
-       { 0x502f8,  0x23800020 },
-       { 0x5fffc,  0x220000ce },
-       { 0x502fc,  0x2300001d },
-};
-
-static struct mdp_table_entry mdp_downscale_x_table_PT4TOPT6[] = {
-       { 0x5fffc,  0x740008c },
-       { 0x50280,  0x33800088 },
-       { 0x5fffc,  0x800008e },
-       { 0x50284,  0x33400084 },
-       { 0x5fffc,  0x8400092 },
-       { 0x50288,  0x33000080 },
-       { 0x5fffc,  0x9000094 },
-       { 0x5028c,  0x3300007b },
-       { 0x5fffc,  0x9c00098 },
-       { 0x50290,  0x32400077 },
-       { 0x5fffc,  0xa40009b },
-       { 0x50294,  0x32000073 },
-       { 0x5fffc,  0xb00009d },
-       { 0x50298,  0x31c0006f },
-       { 0x5fffc,  0xbc000a0 },
-       { 0x5029c,  0x3140006b },
-       { 0x5fffc,  0xc8000a2 },
-       { 0x502a0,  0x31000067 },
-       { 0x5fffc,  0xd8000a5 },
-       { 0x502a4,  0x30800062 },
-       { 0x5fffc,  0xe4000a8 },
-       { 0x502a8,  0x2fc0005f },
-       { 0x5fffc,  0xec000aa },
-       { 0x502ac,  0x2fc0005b },
-       { 0x5fffc,  0xf8000ad },
-       { 0x502b0,  0x2f400057 },
-       { 0x5fffc,  0x108000b0 },
-       { 0x502b4,  0x2e400054 },
-       { 0x5fffc,  0x114000b2 },
-       { 0x502b8,  0x2e000050 },
-       { 0x5fffc,  0x124000b4 },
-       { 0x502bc,  0x2d80004c },
-       { 0x5fffc,  0x130000b6 },
-       { 0x502c0,  0x2d000049 },
-       { 0x5fffc,  0x140000b8 },
-       { 0x502c4,  0x2c800045 },
-       { 0x5fffc,  0x150000b9 },
-       { 0x502c8,  0x2c000042 },
-       { 0x5fffc,  0x15c000bd },
-       { 0x502cc,  0x2b40003e },
-       { 0x5fffc,  0x16c000bf },
-       { 0x502d0,  0x2a80003b },
-       { 0x5fffc,  0x17c000bf },
-       { 0x502d4,  0x2a000039 },
-       { 0x5fffc,  0x188000c2 },
-       { 0x502d8,  0x29400036 },
-       { 0x5fffc,  0x19c000c4 },
-       { 0x502dc,  0x28800032 },
-       { 0x5fffc,  0x1ac000c5 },
-       { 0x502e0,  0x2800002f },
-       { 0x5fffc,  0x1bc000c7 },
-       { 0x502e4,  0x2740002c },
-       { 0x5fffc,  0x1cc000c8 },
-       { 0x502e8,  0x26c00029 },
-       { 0x5fffc,  0x1dc000c9 },
-       { 0x502ec,  0x26000027 },
-       { 0x5fffc,  0x1ec000cc },
-       { 0x502f0,  0x25000024 },
-       { 0x5fffc,  0x200000cc },
-       { 0x502f4,  0x24800021 },
-       { 0x5fffc,  0x210000cd },
-       { 0x502f8,  0x23800020 },
-       { 0x5fffc,  0x220000ce },
-       { 0x502fc,  0x2300001d },
-};
-
-static struct mdp_table_entry mdp_downscale_x_table_PT6TOPT8[] = {
-       { 0x5fffc,  0xfe000070 },
-       { 0x50280,  0x4bc00068 },
-       { 0x5fffc,  0xfe000078 },
-       { 0x50284,  0x4bc00060 },
-       { 0x5fffc,  0xfe000080 },
-       { 0x50288,  0x4b800059 },
-       { 0x5fffc,  0xfe000089 },
-       { 0x5028c,  0x4b000052 },
-       { 0x5fffc,  0xfe400091 },
-       { 0x50290,  0x4a80004b },
-       { 0x5fffc,  0xfe40009a },
-       { 0x50294,  0x4a000044 },
-       { 0x5fffc,  0xfe8000a3 },
-       { 0x50298,  0x4940003d },
-       { 0x5fffc,  0xfec000ac },
-       { 0x5029c,  0x48400037 },
-       { 0x5fffc,  0xff0000b4 },
-       { 0x502a0,  0x47800031 },
-       { 0x5fffc,  0xff8000bd },
-       { 0x502a4,  0x4640002b },
-       { 0x5fffc,  0xc5 },
-       { 0x502a8,  0x45000026 },
-       { 0x5fffc,  0x8000ce },
-       { 0x502ac,  0x43800021 },
-       { 0x5fffc,  0x10000d6 },
-       { 0x502b0,  0x4240001c },
-       { 0x5fffc,  0x18000df },
-       { 0x502b4,  0x40800018 },
-       { 0x5fffc,  0x24000e6 },
-       { 0x502b8,  0x3f000014 },
-       { 0x5fffc,  0x30000ee },
-       { 0x502bc,  0x3d400010 },
-       { 0x5fffc,  0x40000f5 },
-       { 0x502c0,  0x3b80000c },
-       { 0x5fffc,  0x50000fc },
-       { 0x502c4,  0x39800009 },
-       { 0x5fffc,  0x6000102 },
-       { 0x502c8,  0x37c00006 },
-       { 0x5fffc,  0x7000109 },
-       { 0x502cc,  0x35800004 },
-       { 0x5fffc,  0x840010e },
-       { 0x502d0,  0x33800002 },
-       { 0x5fffc,  0x9800114 },
-       { 0x502d4,  0x31400000 },
-       { 0x5fffc,  0xac00119 },
-       { 0x502d8,  0x2f4003fe },
-       { 0x5fffc,  0xc40011e },
-       { 0x502dc,  0x2d0003fc },
-       { 0x5fffc,  0xdc00121 },
-       { 0x502e0,  0x2b0003fb },
-       { 0x5fffc,  0xf400125 },
-       { 0x502e4,  0x28c003fa },
-       { 0x5fffc,  0x11000128 },
-       { 0x502e8,  0x268003f9 },
-       { 0x5fffc,  0x12c0012a },
-       { 0x502ec,  0x244003f9 },
-       { 0x5fffc,  0x1480012c },
-       { 0x502f0,  0x224003f8 },
-       { 0x5fffc,  0x1640012e },
-       { 0x502f4,  0x200003f8 },
-       { 0x5fffc,  0x1800012f },
-       { 0x502f8,  0x1e0003f8 },
-       { 0x5fffc,  0x1a00012f },
-       { 0x502fc,  0x1c0003f8 },
-};
-
-static struct mdp_table_entry mdp_downscale_x_table_PT8TO1[] = {
-       { 0x5fffc,  0x0 },
-       { 0x50280,  0x7fc00000 },
-       { 0x5fffc,  0xff80000d },
-       { 0x50284,  0x7ec003f9 },
-       { 0x5fffc,  0xfec0001c },
-       { 0x50288,  0x7d4003f3 },
-       { 0x5fffc,  0xfe40002b },
-       { 0x5028c,  0x7b8003ed },
-       { 0x5fffc,  0xfd80003c },
-       { 0x50290,  0x794003e8 },
-       { 0x5fffc,  0xfcc0004d },
-       { 0x50294,  0x76c003e4 },
-       { 0x5fffc,  0xfc40005f },
-       { 0x50298,  0x73c003e0 },
-       { 0x5fffc,  0xfb800071 },
-       { 0x5029c,  0x708003de },
-       { 0x5fffc,  0xfac00085 },
-       { 0x502a0,  0x6d0003db },
-       { 0x5fffc,  0xfa000098 },
-       { 0x502a4,  0x698003d9 },
-       { 0x5fffc,  0xf98000ac },
-       { 0x502a8,  0x654003d8 },
-       { 0x5fffc,  0xf8c000c1 },
-       { 0x502ac,  0x610003d7 },
-       { 0x5fffc,  0xf84000d5 },
-       { 0x502b0,  0x5c8003d7 },
-       { 0x5fffc,  0xf7c000e9 },
-       { 0x502b4,  0x580003d7 },
-       { 0x5fffc,  0xf74000fd },
-       { 0x502b8,  0x534003d8 },
-       { 0x5fffc,  0xf6c00112 },
-       { 0x502bc,  0x4e8003d8 },
-       { 0x5fffc,  0xf6800126 },
-       { 0x502c0,  0x494003da },
-       { 0x5fffc,  0xf600013a },
-       { 0x502c4,  0x448003db },
-       { 0x5fffc,  0xf600014d },
-       { 0x502c8,  0x3f4003dd },
-       { 0x5fffc,  0xf5c00160 },
-       { 0x502cc,  0x3a4003df },
-       { 0x5fffc,  0xf5c00172 },
-       { 0x502d0,  0x354003e1 },
-       { 0x5fffc,  0xf5c00184 },
-       { 0x502d4,  0x304003e3 },
-       { 0x5fffc,  0xf6000195 },
-       { 0x502d8,  0x2b0003e6 },
-       { 0x5fffc,  0xf64001a6 },
-       { 0x502dc,  0x260003e8 },
-       { 0x5fffc,  0xf6c001b4 },
-       { 0x502e0,  0x214003eb },
-       { 0x5fffc,  0xf78001c2 },
-       { 0x502e4,  0x1c4003ee },
-       { 0x5fffc,  0xf80001cf },
-       { 0x502e8,  0x17c003f1 },
-       { 0x5fffc,  0xf90001db },
-       { 0x502ec,  0x134003f3 },
-       { 0x5fffc,  0xfa0001e5 },
-       { 0x502f0,  0xf0003f6 },
-       { 0x5fffc,  0xfb4001ee },
-       { 0x502f4,  0xac003f9 },
-       { 0x5fffc,  0xfcc001f5 },
-       { 0x502f8,  0x70003fb },
-       { 0x5fffc,  0xfe4001fb },
-       { 0x502fc,  0x34003fe },
-};
-
-struct mdp_table_entry *mdp_downscale_x_table[MDP_DOWNSCALE_MAX] = {
-       [MDP_DOWNSCALE_PT2TOPT4] = mdp_downscale_x_table_PT2TOPT4,
-       [MDP_DOWNSCALE_PT4TOPT6] = mdp_downscale_x_table_PT4TOPT6,
-       [MDP_DOWNSCALE_PT6TOPT8] = mdp_downscale_x_table_PT6TOPT8,
-       [MDP_DOWNSCALE_PT8TO1]  = mdp_downscale_x_table_PT8TO1,
-};
-
-static struct mdp_table_entry mdp_downscale_y_table_PT2TOPT4[] = {
-       { 0x5fffc,  0x740008c },
-       { 0x50300,  0x33800088 },
-       { 0x5fffc,  0x800008e },
-       { 0x50304,  0x33400084 },
-       { 0x5fffc,  0x8400092 },
-       { 0x50308,  0x33000080 },
-       { 0x5fffc,  0x9000094 },
-       { 0x5030c,  0x3300007b },
-       { 0x5fffc,  0x9c00098 },
-       { 0x50310,  0x32400077 },
-       { 0x5fffc,  0xa40009b },
-       { 0x50314,  0x32000073 },
-       { 0x5fffc,  0xb00009d },
-       { 0x50318,  0x31c0006f },
-       { 0x5fffc,  0xbc000a0 },
-       { 0x5031c,  0x3140006b },
-       { 0x5fffc,  0xc8000a2 },
-       { 0x50320,  0x31000067 },
-       { 0x5fffc,  0xd8000a5 },
-       { 0x50324,  0x30800062 },
-       { 0x5fffc,  0xe4000a8 },
-       { 0x50328,  0x2fc0005f },
-       { 0x5fffc,  0xec000aa },
-       { 0x5032c,  0x2fc0005b },
-       { 0x5fffc,  0xf8000ad },
-       { 0x50330,  0x2f400057 },
-       { 0x5fffc,  0x108000b0 },
-       { 0x50334,  0x2e400054 },
-       { 0x5fffc,  0x114000b2 },
-       { 0x50338,  0x2e000050 },
-       { 0x5fffc,  0x124000b4 },
-       { 0x5033c,  0x2d80004c },
-       { 0x5fffc,  0x130000b6 },
-       { 0x50340,  0x2d000049 },
-       { 0x5fffc,  0x140000b8 },
-       { 0x50344,  0x2c800045 },
-       { 0x5fffc,  0x150000b9 },
-       { 0x50348,  0x2c000042 },
-       { 0x5fffc,  0x15c000bd },
-       { 0x5034c,  0x2b40003e },
-       { 0x5fffc,  0x16c000bf },
-       { 0x50350,  0x2a80003b },
-       { 0x5fffc,  0x17c000bf },
-       { 0x50354,  0x2a000039 },
-       { 0x5fffc,  0x188000c2 },
-       { 0x50358,  0x29400036 },
-       { 0x5fffc,  0x19c000c4 },
-       { 0x5035c,  0x28800032 },
-       { 0x5fffc,  0x1ac000c5 },
-       { 0x50360,  0x2800002f },
-       { 0x5fffc,  0x1bc000c7 },
-       { 0x50364,  0x2740002c },
-       { 0x5fffc,  0x1cc000c8 },
-       { 0x50368,  0x26c00029 },
-       { 0x5fffc,  0x1dc000c9 },
-       { 0x5036c,  0x26000027 },
-       { 0x5fffc,  0x1ec000cc },
-       { 0x50370,  0x25000024 },
-       { 0x5fffc,  0x200000cc },
-       { 0x50374,  0x24800021 },
-       { 0x5fffc,  0x210000cd },
-       { 0x50378,  0x23800020 },
-       { 0x5fffc,  0x220000ce },
-       { 0x5037c,  0x2300001d },
-};
-
-static struct mdp_table_entry mdp_downscale_y_table_PT4TOPT6[] = {
-       { 0x5fffc,  0x740008c },
-       { 0x50300,  0x33800088 },
-       { 0x5fffc,  0x800008e },
-       { 0x50304,  0x33400084 },
-       { 0x5fffc,  0x8400092 },
-       { 0x50308,  0x33000080 },
-       { 0x5fffc,  0x9000094 },
-       { 0x5030c,  0x3300007b },
-       { 0x5fffc,  0x9c00098 },
-       { 0x50310,  0x32400077 },
-       { 0x5fffc,  0xa40009b },
-       { 0x50314,  0x32000073 },
-       { 0x5fffc,  0xb00009d },
-       { 0x50318,  0x31c0006f },
-       { 0x5fffc,  0xbc000a0 },
-       { 0x5031c,  0x3140006b },
-       { 0x5fffc,  0xc8000a2 },
-       { 0x50320,  0x31000067 },
-       { 0x5fffc,  0xd8000a5 },
-       { 0x50324,  0x30800062 },
-       { 0x5fffc,  0xe4000a8 },
-       { 0x50328,  0x2fc0005f },
-       { 0x5fffc,  0xec000aa },
-       { 0x5032c,  0x2fc0005b },
-       { 0x5fffc,  0xf8000ad },
-       { 0x50330,  0x2f400057 },
-       { 0x5fffc,  0x108000b0 },
-       { 0x50334,  0x2e400054 },
-       { 0x5fffc,  0x114000b2 },
-       { 0x50338,  0x2e000050 },
-       { 0x5fffc,  0x124000b4 },
-       { 0x5033c,  0x2d80004c },
-       { 0x5fffc,  0x130000b6 },
-       { 0x50340,  0x2d000049 },
-       { 0x5fffc,  0x140000b8 },
-       { 0x50344,  0x2c800045 },
-       { 0x5fffc,  0x150000b9 },
-       { 0x50348,  0x2c000042 },
-       { 0x5fffc,  0x15c000bd },
-       { 0x5034c,  0x2b40003e },
-       { 0x5fffc,  0x16c000bf },
-       { 0x50350,  0x2a80003b },
-       { 0x5fffc,  0x17c000bf },
-       { 0x50354,  0x2a000039 },
-       { 0x5fffc,  0x188000c2 },
-       { 0x50358,  0x29400036 },
-       { 0x5fffc,  0x19c000c4 },
-       { 0x5035c,  0x28800032 },
-       { 0x5fffc,  0x1ac000c5 },
-       { 0x50360,  0x2800002f },
-       { 0x5fffc,  0x1bc000c7 },
-       { 0x50364,  0x2740002c },
-       { 0x5fffc,  0x1cc000c8 },
-       { 0x50368,  0x26c00029 },
-       { 0x5fffc,  0x1dc000c9 },
-       { 0x5036c,  0x26000027 },
-       { 0x5fffc,  0x1ec000cc },
-       { 0x50370,  0x25000024 },
-       { 0x5fffc,  0x200000cc },
-       { 0x50374,  0x24800021 },
-       { 0x5fffc,  0x210000cd },
-       { 0x50378,  0x23800020 },
-       { 0x5fffc,  0x220000ce },
-       { 0x5037c,  0x2300001d },
-};
-
-static struct mdp_table_entry mdp_downscale_y_table_PT6TOPT8[] = {
-       { 0x5fffc,  0xfe000070 },
-       { 0x50300,  0x4bc00068 },
-       { 0x5fffc,  0xfe000078 },
-       { 0x50304,  0x4bc00060 },
-       { 0x5fffc,  0xfe000080 },
-       { 0x50308,  0x4b800059 },
-       { 0x5fffc,  0xfe000089 },
-       { 0x5030c,  0x4b000052 },
-       { 0x5fffc,  0xfe400091 },
-       { 0x50310,  0x4a80004b },
-       { 0x5fffc,  0xfe40009a },
-       { 0x50314,  0x4a000044 },
-       { 0x5fffc,  0xfe8000a3 },
-       { 0x50318,  0x4940003d },
-       { 0x5fffc,  0xfec000ac },
-       { 0x5031c,  0x48400037 },
-       { 0x5fffc,  0xff0000b4 },
-       { 0x50320,  0x47800031 },
-       { 0x5fffc,  0xff8000bd },
-       { 0x50324,  0x4640002b },
-       { 0x5fffc,  0xc5 },
-       { 0x50328,  0x45000026 },
-       { 0x5fffc,  0x8000ce },
-       { 0x5032c,  0x43800021 },
-       { 0x5fffc,  0x10000d6 },
-       { 0x50330,  0x4240001c },
-       { 0x5fffc,  0x18000df },
-       { 0x50334,  0x40800018 },
-       { 0x5fffc,  0x24000e6 },
-       { 0x50338,  0x3f000014 },
-       { 0x5fffc,  0x30000ee },
-       { 0x5033c,  0x3d400010 },
-       { 0x5fffc,  0x40000f5 },
-       { 0x50340,  0x3b80000c },
-       { 0x5fffc,  0x50000fc },
-       { 0x50344,  0x39800009 },
-       { 0x5fffc,  0x6000102 },
-       { 0x50348,  0x37c00006 },
-       { 0x5fffc,  0x7000109 },
-       { 0x5034c,  0x35800004 },
-       { 0x5fffc,  0x840010e },
-       { 0x50350,  0x33800002 },
-       { 0x5fffc,  0x9800114 },
-       { 0x50354,  0x31400000 },
-       { 0x5fffc,  0xac00119 },
-       { 0x50358,  0x2f4003fe },
-       { 0x5fffc,  0xc40011e },
-       { 0x5035c,  0x2d0003fc },
-       { 0x5fffc,  0xdc00121 },
-       { 0x50360,  0x2b0003fb },
-       { 0x5fffc,  0xf400125 },
-       { 0x50364,  0x28c003fa },
-       { 0x5fffc,  0x11000128 },
-       { 0x50368,  0x268003f9 },
-       { 0x5fffc,  0x12c0012a },
-       { 0x5036c,  0x244003f9 },
-       { 0x5fffc,  0x1480012c },
-       { 0x50370,  0x224003f8 },
-       { 0x5fffc,  0x1640012e },
-       { 0x50374,  0x200003f8 },
-       { 0x5fffc,  0x1800012f },
-       { 0x50378,  0x1e0003f8 },
-       { 0x5fffc,  0x1a00012f },
-       { 0x5037c,  0x1c0003f8 },
-};
-
-static struct mdp_table_entry mdp_downscale_y_table_PT8TO1[] = {
-       { 0x5fffc,  0x0 },
-       { 0x50300,  0x7fc00000 },
-       { 0x5fffc,  0xff80000d },
-       { 0x50304,  0x7ec003f9 },
-       { 0x5fffc,  0xfec0001c },
-       { 0x50308,  0x7d4003f3 },
-       { 0x5fffc,  0xfe40002b },
-       { 0x5030c,  0x7b8003ed },
-       { 0x5fffc,  0xfd80003c },
-       { 0x50310,  0x794003e8 },
-       { 0x5fffc,  0xfcc0004d },
-       { 0x50314,  0x76c003e4 },
-       { 0x5fffc,  0xfc40005f },
-       { 0x50318,  0x73c003e0 },
-       { 0x5fffc,  0xfb800071 },
-       { 0x5031c,  0x708003de },
-       { 0x5fffc,  0xfac00085 },
-       { 0x50320,  0x6d0003db },
-       { 0x5fffc,  0xfa000098 },
-       { 0x50324,  0x698003d9 },
-       { 0x5fffc,  0xf98000ac },
-       { 0x50328,  0x654003d8 },
-       { 0x5fffc,  0xf8c000c1 },
-       { 0x5032c,  0x610003d7 },
-       { 0x5fffc,  0xf84000d5 },
-       { 0x50330,  0x5c8003d7 },
-       { 0x5fffc,  0xf7c000e9 },
-       { 0x50334,  0x580003d7 },
-       { 0x5fffc,  0xf74000fd },
-       { 0x50338,  0x534003d8 },
-       { 0x5fffc,  0xf6c00112 },
-       { 0x5033c,  0x4e8003d8 },
-       { 0x5fffc,  0xf6800126 },
-       { 0x50340,  0x494003da },
-       { 0x5fffc,  0xf600013a },
-       { 0x50344,  0x448003db },
-       { 0x5fffc,  0xf600014d },
-       { 0x50348,  0x3f4003dd },
-       { 0x5fffc,  0xf5c00160 },
-       { 0x5034c,  0x3a4003df },
-       { 0x5fffc,  0xf5c00172 },
-       { 0x50350,  0x354003e1 },
-       { 0x5fffc,  0xf5c00184 },
-       { 0x50354,  0x304003e3 },
-       { 0x5fffc,  0xf6000195 },
-       { 0x50358,  0x2b0003e6 },
-       { 0x5fffc,  0xf64001a6 },
-       { 0x5035c,  0x260003e8 },
-       { 0x5fffc,  0xf6c001b4 },
-       { 0x50360,  0x214003eb },
-       { 0x5fffc,  0xf78001c2 },
-       { 0x50364,  0x1c4003ee },
-       { 0x5fffc,  0xf80001cf },
-       { 0x50368,  0x17c003f1 },
-       { 0x5fffc,  0xf90001db },
-       { 0x5036c,  0x134003f3 },
-       { 0x5fffc,  0xfa0001e5 },
-       { 0x50370,  0xf0003f6 },
-       { 0x5fffc,  0xfb4001ee },
-       { 0x50374,  0xac003f9 },
-       { 0x5fffc,  0xfcc001f5 },
-       { 0x50378,  0x70003fb },
-       { 0x5fffc,  0xfe4001fb },
-       { 0x5037c,  0x34003fe },
-};
-
-struct mdp_table_entry *mdp_downscale_y_table[MDP_DOWNSCALE_MAX] = {
-       [MDP_DOWNSCALE_PT2TOPT4] = mdp_downscale_y_table_PT2TOPT4,
-       [MDP_DOWNSCALE_PT4TOPT6] = mdp_downscale_y_table_PT4TOPT6,
-       [MDP_DOWNSCALE_PT6TOPT8] = mdp_downscale_y_table_PT6TOPT8,
-       [MDP_DOWNSCALE_PT8TO1]  = mdp_downscale_y_table_PT8TO1,
-};
-
-struct mdp_table_entry mdp_gaussian_blur_table[] = {
-       /* max variance */
-       { 0x5fffc, 0x20000080 },
-       { 0x50280, 0x20000080 },
-       { 0x5fffc, 0x20000080 },
-       { 0x50284, 0x20000080 },
-       { 0x5fffc, 0x20000080 },
-       { 0x50288, 0x20000080 },
-       { 0x5fffc, 0x20000080 },
-       { 0x5028c, 0x20000080 },
-       { 0x5fffc, 0x20000080 },
-       { 0x50290, 0x20000080 },
-       { 0x5fffc, 0x20000080 },
-       { 0x50294, 0x20000080 },
-       { 0x5fffc, 0x20000080 },
-       { 0x50298, 0x20000080 },
-       { 0x5fffc, 0x20000080 },
-       { 0x5029c, 0x20000080 },
-       { 0x5fffc, 0x20000080 },
-       { 0x502a0, 0x20000080 },
-       { 0x5fffc, 0x20000080 },
-       { 0x502a4, 0x20000080 },
-       { 0x5fffc, 0x20000080 },
-       { 0x502a8, 0x20000080 },
-       { 0x5fffc, 0x20000080 },
-       { 0x502ac, 0x20000080 },
-       { 0x5fffc, 0x20000080 },
-       { 0x502b0, 0x20000080 },
-       { 0x5fffc, 0x20000080 },
-       { 0x502b4, 0x20000080 },
-       { 0x5fffc, 0x20000080 },
-       { 0x502b8, 0x20000080 },
-       { 0x5fffc, 0x20000080 },
-       { 0x502bc, 0x20000080 },
-       { 0x5fffc, 0x20000080 },
-       { 0x502c0, 0x20000080 },
-       { 0x5fffc, 0x20000080 },
-       { 0x502c4, 0x20000080 },
-       { 0x5fffc, 0x20000080 },
-       { 0x502c8, 0x20000080 },
-       { 0x5fffc, 0x20000080 },
-       { 0x502cc, 0x20000080 },
-       { 0x5fffc, 0x20000080 },
-       { 0x502d0, 0x20000080 },
-       { 0x5fffc, 0x20000080 },
-       { 0x502d4, 0x20000080 },
-       { 0x5fffc, 0x20000080 },
-       { 0x502d8, 0x20000080 },
-       { 0x5fffc, 0x20000080 },
-       { 0x502dc, 0x20000080 },
-       { 0x5fffc, 0x20000080 },
-       { 0x502e0, 0x20000080 },
-       { 0x5fffc, 0x20000080 },
-       { 0x502e4, 0x20000080 },
-       { 0x5fffc, 0x20000080 },
-       { 0x502e8, 0x20000080 },
-       { 0x5fffc, 0x20000080 },
-       { 0x502ec, 0x20000080 },
-       { 0x5fffc, 0x20000080 },
-       { 0x502f0, 0x20000080 },
-       { 0x5fffc, 0x20000080 },
-       { 0x502f4, 0x20000080 },
-       { 0x5fffc, 0x20000080 },
-       { 0x502f8, 0x20000080 },
-       { 0x5fffc, 0x20000080 },
-       { 0x502fc, 0x20000080 },
-       { 0x5fffc, 0x20000080 },
-       { 0x50300, 0x20000080 },
-       { 0x5fffc, 0x20000080 },
-       { 0x50304, 0x20000080 },
-       { 0x5fffc, 0x20000080 },
-       { 0x50308, 0x20000080 },
-       { 0x5fffc, 0x20000080 },
-       { 0x5030c, 0x20000080 },
-       { 0x5fffc, 0x20000080 },
-       { 0x50310, 0x20000080 },
-       { 0x5fffc, 0x20000080 },
-       { 0x50314, 0x20000080 },
-       { 0x5fffc, 0x20000080 },
-       { 0x50318, 0x20000080 },
-       { 0x5fffc, 0x20000080 },
-       { 0x5031c, 0x20000080 },
-       { 0x5fffc, 0x20000080 },
-       { 0x50320, 0x20000080 },
-       { 0x5fffc, 0x20000080 },
-       { 0x50324, 0x20000080 },
-       { 0x5fffc, 0x20000080 },
-       { 0x50328, 0x20000080 },
-       { 0x5fffc, 0x20000080 },
-       { 0x5032c, 0x20000080 },
-       { 0x5fffc, 0x20000080 },
-       { 0x50330, 0x20000080 },
-       { 0x5fffc, 0x20000080 },
-       { 0x50334, 0x20000080 },
-       { 0x5fffc, 0x20000080 },
-       { 0x50338, 0x20000080 },
-       { 0x5fffc, 0x20000080 },
-       { 0x5033c, 0x20000080 },
-       { 0x5fffc, 0x20000080 },
-       { 0x50340, 0x20000080 },
-       { 0x5fffc, 0x20000080 },
-       { 0x50344, 0x20000080 },
-       { 0x5fffc, 0x20000080 },
-       { 0x50348, 0x20000080 },
-       { 0x5fffc, 0x20000080 },
-       { 0x5034c, 0x20000080 },
-       { 0x5fffc, 0x20000080 },
-       { 0x50350, 0x20000080 },
-       { 0x5fffc, 0x20000080 },
-       { 0x50354, 0x20000080 },
-       { 0x5fffc, 0x20000080 },
-       { 0x50358, 0x20000080 },
-       { 0x5fffc, 0x20000080 },
-       { 0x5035c, 0x20000080 },
-       { 0x5fffc, 0x20000080 },
-       { 0x50360, 0x20000080 },
-       { 0x5fffc, 0x20000080 },
-       { 0x50364, 0x20000080 },
-       { 0x5fffc, 0x20000080 },
-       { 0x50368, 0x20000080 },
-       { 0x5fffc, 0x20000080 },
-       { 0x5036c, 0x20000080 },
-       { 0x5fffc, 0x20000080 },
-       { 0x50370, 0x20000080 },
-       { 0x5fffc, 0x20000080 },
-       { 0x50374, 0x20000080 },
-       { 0x5fffc, 0x20000080 },
-       { 0x50378, 0x20000080 },
-       { 0x5fffc, 0x20000080 },
-       { 0x5037c, 0x20000080 },
-};
diff --git a/drivers/video/fbdev/msm/mdp_scale_tables.h b/drivers/video/fbdev/msm/mdp_scale_tables.h
deleted file mode 100644 (file)
index 34077b1..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-/* drivers/video/msm_fb/mdp_scale_tables.h
- *
- * Copyright (C) 2007 QUALCOMM Incorporated
- * Copyright (C) 2007 Google Incorporated
- *
- * 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 _MDP_SCALE_TABLES_H_
-#define _MDP_SCALE_TABLES_H_
-
-#include <linux/types.h>
-struct mdp_table_entry {
-       uint32_t reg;
-       uint32_t val;
-};
-
-extern struct mdp_table_entry mdp_upscale_table[64];
-
-enum {
-       MDP_DOWNSCALE_PT2TOPT4,
-       MDP_DOWNSCALE_PT4TOPT6,
-       MDP_DOWNSCALE_PT6TOPT8,
-       MDP_DOWNSCALE_PT8TO1,
-       MDP_DOWNSCALE_MAX,
-};
-
-extern struct mdp_table_entry *mdp_downscale_x_table[MDP_DOWNSCALE_MAX];
-extern struct mdp_table_entry *mdp_downscale_y_table[MDP_DOWNSCALE_MAX];
-extern struct mdp_table_entry mdp_gaussian_blur_table[];
-
-#endif
diff --git a/drivers/video/fbdev/msm/msm_fb.c b/drivers/video/fbdev/msm/msm_fb.c
deleted file mode 100644 (file)
index 2979d7e..0000000
+++ /dev/null
@@ -1,659 +0,0 @@
-/* drivers/video/msm/msm_fb.c
- *
- * Core MSM framebuffer driver.
- *
- * Copyright (C) 2007 Google Incorporated
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- */
-
-#include <linux/platform_device.h>
-#include <linux/module.h>
-#include <linux/fb.h>
-#include <linux/slab.h>
-#include <linux/delay.h>
-
-#include <linux/freezer.h>
-#include <linux/wait.h>
-#include <linux/msm_mdp.h>
-#include <linux/io.h>
-#include <linux/uaccess.h>
-#include <linux/platform_data/video-msm_fb.h>
-#include <linux/workqueue.h>
-#include <linux/clk.h>
-#include <linux/debugfs.h>
-#include <linux/dma-mapping.h>
-
-#define PRINT_FPS 0
-#define PRINT_BLIT_TIME 0
-
-#define SLEEPING 0x4
-#define UPDATING 0x3
-#define FULL_UPDATE_DONE 0x2
-#define WAKING 0x1
-#define AWAKE 0x0
-
-#define NONE 0
-#define SUSPEND_RESUME 0x1
-#define FPS 0x2
-#define BLIT_TIME 0x4
-#define SHOW_UPDATES 0x8
-
-#define DLOG(mask, fmt, args...) \
-do { \
-       if (msmfb_debug_mask & mask) \
-               printk(KERN_INFO "msmfb: "fmt, ##args); \
-} while (0)
-
-static int msmfb_debug_mask;
-module_param_named(msmfb_debug_mask, msmfb_debug_mask, int,
-                  S_IRUGO | S_IWUSR | S_IWGRP);
-
-struct mdp_device *mdp;
-
-struct msmfb_info {
-       struct fb_info *fb;
-       struct msm_panel_data *panel;
-       int xres;
-       int yres;
-       unsigned output_format;
-       unsigned yoffset;
-       unsigned frame_requested;
-       unsigned frame_done;
-       int sleeping;
-       unsigned update_frame;
-       struct {
-               int left;
-               int top;
-               int eright; /* exclusive */
-               int ebottom; /* exclusive */
-       } update_info;
-       char *black;
-
-       spinlock_t update_lock;
-       struct mutex panel_init_lock;
-       wait_queue_head_t frame_wq;
-       struct work_struct resume_work;
-       struct msmfb_callback dma_callback;
-       struct msmfb_callback vsync_callback;
-       struct hrtimer fake_vsync;
-       ktime_t vsync_request_time;
-};
-
-static int msmfb_open(struct fb_info *info, int user)
-{
-       return 0;
-}
-
-static int msmfb_release(struct fb_info *info, int user)
-{
-       return 0;
-}
-
-/* Called from dma interrupt handler, must not sleep */
-static void msmfb_handle_dma_interrupt(struct msmfb_callback *callback)
-{
-       unsigned long irq_flags;
-       struct msmfb_info *msmfb  = container_of(callback, struct msmfb_info,
-                                              dma_callback);
-
-       spin_lock_irqsave(&msmfb->update_lock, irq_flags);
-       msmfb->frame_done = msmfb->frame_requested;
-       if (msmfb->sleeping == UPDATING &&
-           msmfb->frame_done == msmfb->update_frame) {
-               DLOG(SUSPEND_RESUME, "full update completed\n");
-               schedule_work(&msmfb->resume_work);
-       }
-       spin_unlock_irqrestore(&msmfb->update_lock, irq_flags);
-       wake_up(&msmfb->frame_wq);
-}
-
-static int msmfb_start_dma(struct msmfb_info *msmfb)
-{
-       uint32_t x, y, w, h;
-       unsigned addr;
-       unsigned long irq_flags;
-       uint32_t yoffset;
-       s64 time_since_request;
-       struct msm_panel_data *panel = msmfb->panel;
-
-       spin_lock_irqsave(&msmfb->update_lock, irq_flags);
-       time_since_request = ktime_to_ns(ktime_sub(ktime_get(),
-                            msmfb->vsync_request_time));
-       if (time_since_request > 20 * NSEC_PER_MSEC) {
-               uint32_t us;
-               us = do_div(time_since_request, NSEC_PER_MSEC) / NSEC_PER_USEC;
-               printk(KERN_WARNING "msmfb_start_dma %lld.%03u ms after vsync "
-                       "request\n", time_since_request, us);
-       }
-       if (msmfb->frame_done == msmfb->frame_requested) {
-               spin_unlock_irqrestore(&msmfb->update_lock, irq_flags);
-               return -1;
-       }
-       if (msmfb->sleeping == SLEEPING) {
-               DLOG(SUSPEND_RESUME, "tried to start dma while asleep\n");
-               spin_unlock_irqrestore(&msmfb->update_lock, irq_flags);
-               return -1;
-       }
-       x = msmfb->update_info.left;
-       y = msmfb->update_info.top;
-       w = msmfb->update_info.eright - x;
-       h = msmfb->update_info.ebottom - y;
-       yoffset = msmfb->yoffset;
-       msmfb->update_info.left = msmfb->xres + 1;
-       msmfb->update_info.top = msmfb->yres + 1;
-       msmfb->update_info.eright = 0;
-       msmfb->update_info.ebottom = 0;
-       if (unlikely(w > msmfb->xres || h > msmfb->yres ||
-                    w == 0 || h == 0)) {
-               printk(KERN_INFO "invalid update: %d %d %d "
-                               "%d\n", x, y, w, h);
-               msmfb->frame_done = msmfb->frame_requested;
-               goto error;
-       }
-       spin_unlock_irqrestore(&msmfb->update_lock, irq_flags);
-
-       addr = ((msmfb->xres * (yoffset + y) + x) * 2);
-       mdp->dma(mdp, addr + msmfb->fb->fix.smem_start,
-                msmfb->xres * 2, w, h, x, y, &msmfb->dma_callback,
-                panel->interface_type);
-       return 0;
-error:
-       spin_unlock_irqrestore(&msmfb->update_lock, irq_flags);
-       /* some clients need to clear their vsync interrupt */
-       if (panel->clear_vsync)
-               panel->clear_vsync(panel);
-       wake_up(&msmfb->frame_wq);
-       return 0;
-}
-
-/* Called from esync interrupt handler, must not sleep */
-static void msmfb_handle_vsync_interrupt(struct msmfb_callback *callback)
-{
-       struct msmfb_info *msmfb = container_of(callback, struct msmfb_info,
-                                              vsync_callback);
-       msmfb_start_dma(msmfb);
-}
-
-static enum hrtimer_restart msmfb_fake_vsync(struct hrtimer *timer)
-{
-       struct msmfb_info *msmfb  = container_of(timer, struct msmfb_info,
-                                              fake_vsync);
-       msmfb_start_dma(msmfb);
-       return HRTIMER_NORESTART;
-}
-
-static void msmfb_pan_update(struct fb_info *info, uint32_t left, uint32_t top,
-                            uint32_t eright, uint32_t ebottom,
-                            uint32_t yoffset, int pan_display)
-{
-       struct msmfb_info *msmfb = info->par;
-       struct msm_panel_data *panel = msmfb->panel;
-       unsigned long irq_flags;
-       int sleeping;
-       int retry = 1;
-
-       DLOG(SHOW_UPDATES, "update %d %d %d %d %d %d\n",
-               left, top, eright, ebottom, yoffset, pan_display);
-restart:
-       spin_lock_irqsave(&msmfb->update_lock, irq_flags);
-
-       /* if we are sleeping, on a pan_display wait 10ms (to throttle back
-        * drawing otherwise return */
-       if (msmfb->sleeping == SLEEPING) {
-               DLOG(SUSPEND_RESUME, "drawing while asleep\n");
-               spin_unlock_irqrestore(&msmfb->update_lock, irq_flags);
-               if (pan_display)
-                       wait_event_interruptible_timeout(msmfb->frame_wq,
-                               msmfb->sleeping != SLEEPING, HZ/10);
-               return;
-       }
-
-       sleeping = msmfb->sleeping;
-       /* on a full update, if the last frame has not completed, wait for it */
-       if ((pan_display && msmfb->frame_requested != msmfb->frame_done) ||
-                           sleeping == UPDATING) {
-               int ret;
-               spin_unlock_irqrestore(&msmfb->update_lock, irq_flags);
-               ret = wait_event_interruptible_timeout(msmfb->frame_wq,
-                       msmfb->frame_done == msmfb->frame_requested &&
-                       msmfb->sleeping != UPDATING, 5 * HZ);
-               if (ret <= 0 && (msmfb->frame_requested != msmfb->frame_done ||
-                                msmfb->sleeping == UPDATING)) {
-                       if (retry && panel->request_vsync &&
-                           (sleeping == AWAKE)) {
-                               panel->request_vsync(panel,
-                                       &msmfb->vsync_callback);
-                               retry = 0;
-                               printk(KERN_WARNING "msmfb_pan_display timeout "
-                                       "rerequest vsync\n");
-                       } else {
-                               printk(KERN_WARNING "msmfb_pan_display timeout "
-                                       "waiting for frame start, %d %d\n",
-                                       msmfb->frame_requested,
-                                       msmfb->frame_done);
-                               return;
-                       }
-               }
-               goto restart;
-       }
-
-
-       msmfb->frame_requested++;
-       /* if necessary, update the y offset, if this is the
-        * first full update on resume, set the sleeping state */
-       if (pan_display) {
-               msmfb->yoffset = yoffset;
-               if (left == 0 && top == 0 && eright == info->var.xres &&
-                   ebottom == info->var.yres) {
-                       if (sleeping == WAKING) {
-                               msmfb->update_frame = msmfb->frame_requested;
-                               DLOG(SUSPEND_RESUME, "full update starting\n");
-                               msmfb->sleeping = UPDATING;
-                       }
-               }
-       }
-
-       /* set the update request */
-       if (left < msmfb->update_info.left)
-               msmfb->update_info.left = left;
-       if (top < msmfb->update_info.top)
-               msmfb->update_info.top = top;
-       if (eright > msmfb->update_info.eright)
-               msmfb->update_info.eright = eright;
-       if (ebottom > msmfb->update_info.ebottom)
-               msmfb->update_info.ebottom = ebottom;
-       DLOG(SHOW_UPDATES, "update queued %d %d %d %d %d\n",
-               msmfb->update_info.left, msmfb->update_info.top,
-               msmfb->update_info.eright, msmfb->update_info.ebottom,
-               msmfb->yoffset);
-       spin_unlock_irqrestore(&msmfb->update_lock, irq_flags);
-
-       /* if the panel is all the way on wait for vsync, otherwise sleep
-        * for 16 ms (long enough for the dma to panel) and then begin dma */
-       msmfb->vsync_request_time = ktime_get();
-       if (panel->request_vsync && (sleeping == AWAKE)) {
-               panel->request_vsync(panel, &msmfb->vsync_callback);
-       } else {
-               if (!hrtimer_active(&msmfb->fake_vsync)) {
-                       hrtimer_start(&msmfb->fake_vsync,
-                                     ktime_set(0, NSEC_PER_SEC/60),
-                                     HRTIMER_MODE_REL);
-               }
-       }
-}
-
-static void msmfb_update(struct fb_info *info, uint32_t left, uint32_t top,
-                        uint32_t eright, uint32_t ebottom)
-{
-       msmfb_pan_update(info, left, top, eright, ebottom, 0, 0);
-}
-
-static void power_on_panel(struct work_struct *work)
-{
-       struct msmfb_info *msmfb =
-               container_of(work, struct msmfb_info, resume_work);
-       struct msm_panel_data *panel = msmfb->panel;
-       unsigned long irq_flags;
-
-       mutex_lock(&msmfb->panel_init_lock);
-       DLOG(SUSPEND_RESUME, "turning on panel\n");
-       if (msmfb->sleeping == UPDATING) {
-               if (panel->unblank(panel)) {
-                       printk(KERN_INFO "msmfb: panel unblank failed,"
-                              "not starting drawing\n");
-                       goto error;
-               }
-               spin_lock_irqsave(&msmfb->update_lock, irq_flags);
-               msmfb->sleeping = AWAKE;
-               wake_up(&msmfb->frame_wq);
-               spin_unlock_irqrestore(&msmfb->update_lock, irq_flags);
-       }
-error:
-       mutex_unlock(&msmfb->panel_init_lock);
-}
-
-
-static int msmfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
-{
-       if ((var->xres != info->var.xres) ||
-           (var->yres != info->var.yres) ||
-           (var->xres_virtual != info->var.xres_virtual) ||
-           (var->yres_virtual != info->var.yres_virtual) ||
-           (var->xoffset != info->var.xoffset) ||
-           (var->bits_per_pixel != info->var.bits_per_pixel) ||
-           (var->grayscale != info->var.grayscale))
-                return -EINVAL;
-       return 0;
-}
-
-int msmfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
-{
-       struct msmfb_info *msmfb = info->par;
-       struct msm_panel_data *panel = msmfb->panel;
-
-       /* "UPDT" */
-       if ((panel->caps & MSMFB_CAP_PARTIAL_UPDATES) &&
-           (var->reserved[0] == 0x54445055)) {
-               msmfb_pan_update(info, var->reserved[1] & 0xffff,
-                                var->reserved[1] >> 16,
-                                var->reserved[2] & 0xffff,
-                                var->reserved[2] >> 16, var->yoffset, 1);
-       } else {
-               msmfb_pan_update(info, 0, 0, info->var.xres, info->var.yres,
-                                var->yoffset, 1);
-       }
-       return 0;
-}
-
-static void msmfb_fillrect(struct fb_info *p, const struct fb_fillrect *rect)
-{
-       cfb_fillrect(p, rect);
-       msmfb_update(p, rect->dx, rect->dy, rect->dx + rect->width,
-                    rect->dy + rect->height);
-}
-
-static void msmfb_copyarea(struct fb_info *p, const struct fb_copyarea *area)
-{
-       cfb_copyarea(p, area);
-       msmfb_update(p, area->dx, area->dy, area->dx + area->width,
-                    area->dy + area->height);
-}
-
-static void msmfb_imageblit(struct fb_info *p, const struct fb_image *image)
-{
-       cfb_imageblit(p, image);
-       msmfb_update(p, image->dx, image->dy, image->dx + image->width,
-                    image->dy + image->height);
-}
-
-
-static int msmfb_blit(struct fb_info *info,
-                     void __user *p)
-{
-       struct mdp_blit_req req;
-       struct mdp_blit_req_list req_list;
-       int i;
-       int ret;
-
-       if (copy_from_user(&req_list, p, sizeof(req_list)))
-               return -EFAULT;
-
-       for (i = 0; i < req_list.count; i++) {
-               struct mdp_blit_req_list *list =
-                       (struct mdp_blit_req_list *)p;
-               if (copy_from_user(&req, &list->req[i], sizeof(req)))
-                       return -EFAULT;
-               ret = mdp->blit(mdp, info, &req);
-               if (ret)
-                       return ret;
-       }
-       return 0;
-}
-
-
-DEFINE_MUTEX(mdp_ppp_lock);
-
-static int msmfb_ioctl(struct fb_info *p, unsigned int cmd, unsigned long arg)
-{
-       void __user *argp = (void __user *)arg;
-       int ret;
-
-       switch (cmd) {
-       case MSMFB_GRP_DISP:
-               mdp->set_grp_disp(mdp, arg);
-               break;
-       case MSMFB_BLIT:
-               ret = msmfb_blit(p, argp);
-               if (ret)
-                       return ret;
-               break;
-       default:
-                       printk(KERN_INFO "msmfb unknown ioctl: %d\n", cmd);
-                       return -EINVAL;
-       }
-       return 0;
-}
-
-static struct fb_ops msmfb_ops = {
-       .owner = THIS_MODULE,
-       .fb_open = msmfb_open,
-       .fb_release = msmfb_release,
-       .fb_check_var = msmfb_check_var,
-       .fb_pan_display = msmfb_pan_display,
-       .fb_fillrect = msmfb_fillrect,
-       .fb_copyarea = msmfb_copyarea,
-       .fb_imageblit = msmfb_imageblit,
-       .fb_ioctl = msmfb_ioctl,
-};
-
-static unsigned PP[16];
-
-
-
-#define BITS_PER_PIXEL 16
-
-static void setup_fb_info(struct msmfb_info *msmfb)
-{
-       struct fb_info *fb_info = msmfb->fb;
-       int r;
-
-       /* finish setting up the fb_info struct */
-       strncpy(fb_info->fix.id, "msmfb", 16);
-       fb_info->fix.ypanstep = 1;
-
-       fb_info->fbops = &msmfb_ops;
-       fb_info->flags = FBINFO_DEFAULT;
-
-       fb_info->fix.type = FB_TYPE_PACKED_PIXELS;
-       fb_info->fix.visual = FB_VISUAL_TRUECOLOR;
-       fb_info->fix.line_length = msmfb->xres * 2;
-
-       fb_info->var.xres = msmfb->xres;
-       fb_info->var.yres = msmfb->yres;
-       fb_info->var.width = msmfb->panel->fb_data->width;
-       fb_info->var.height = msmfb->panel->fb_data->height;
-       fb_info->var.xres_virtual = msmfb->xres;
-       fb_info->var.yres_virtual = msmfb->yres * 2;
-       fb_info->var.bits_per_pixel = BITS_PER_PIXEL;
-       fb_info->var.accel_flags = 0;
-
-       fb_info->var.yoffset = 0;
-
-       if (msmfb->panel->caps & MSMFB_CAP_PARTIAL_UPDATES) {
-               /*
-                * Set the param in the fixed screen, so userspace can't
-                * change it. This will be used to check for the
-                * capability.
-                */
-               fb_info->fix.reserved[0] = 0x5444;
-               fb_info->fix.reserved[1] = 0x5055;
-
-               /*
-                * This preloads the value so that if userspace doesn't
-                * change it, it will be a full update
-                */
-               fb_info->var.reserved[0] = 0x54445055;
-               fb_info->var.reserved[1] = 0;
-               fb_info->var.reserved[2] = (uint16_t)msmfb->xres |
-                                          ((uint32_t)msmfb->yres << 16);
-       }
-
-       fb_info->var.red.offset = 11;
-       fb_info->var.red.length = 5;
-       fb_info->var.red.msb_right = 0;
-       fb_info->var.green.offset = 5;
-       fb_info->var.green.length = 6;
-       fb_info->var.green.msb_right = 0;
-       fb_info->var.blue.offset = 0;
-       fb_info->var.blue.length = 5;
-       fb_info->var.blue.msb_right = 0;
-
-       r = fb_alloc_cmap(&fb_info->cmap, 16, 0);
-       fb_info->pseudo_palette = PP;
-
-       PP[0] = 0;
-       for (r = 1; r < 16; r++)
-               PP[r] = 0xffffffff;
-}
-
-static int setup_fbmem(struct msmfb_info *msmfb, struct platform_device *pdev)
-{
-       struct fb_info *fb = msmfb->fb;
-       struct resource *resource;
-       unsigned long size = msmfb->xres * msmfb->yres *
-                            (BITS_PER_PIXEL >> 3) * 2;
-       unsigned char *fbram;
-
-       /* board file might have attached a resource describing an fb */
-       resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       if (!resource)
-               return -EINVAL;
-
-       /* check the resource is large enough to fit the fb */
-       if (resource->end - resource->start < size) {
-               printk(KERN_ERR "allocated resource is too small for "
-                               "fb\n");
-               return -ENOMEM;
-       }
-       fb->fix.smem_start = resource->start;
-       fb->fix.smem_len = resource_size(resource);
-       fbram = ioremap(resource->start, resource_size(resource));
-       if (fbram == NULL) {
-               printk(KERN_ERR "msmfb: cannot allocate fbram!\n");
-               return -ENOMEM;
-       }
-       fb->screen_base = fbram;
-       return 0;
-}
-
-static int msmfb_probe(struct platform_device *pdev)
-{
-       struct fb_info *fb;
-       struct msmfb_info *msmfb;
-       struct msm_panel_data *panel = pdev->dev.platform_data;
-       int ret;
-
-       if (!panel) {
-               pr_err("msmfb_probe: no platform data\n");
-               return -EINVAL;
-       }
-       if (!panel->fb_data) {
-               pr_err("msmfb_probe: no fb_data\n");
-               return -EINVAL;
-       }
-
-       fb = framebuffer_alloc(sizeof(struct msmfb_info), &pdev->dev);
-       if (!fb)
-               return -ENOMEM;
-       msmfb = fb->par;
-       msmfb->fb = fb;
-       msmfb->panel = panel;
-       msmfb->xres = panel->fb_data->xres;
-       msmfb->yres = panel->fb_data->yres;
-
-       ret = setup_fbmem(msmfb, pdev);
-       if (ret)
-               goto error_setup_fbmem;
-
-       setup_fb_info(msmfb);
-
-       spin_lock_init(&msmfb->update_lock);
-       mutex_init(&msmfb->panel_init_lock);
-       init_waitqueue_head(&msmfb->frame_wq);
-       INIT_WORK(&msmfb->resume_work, power_on_panel);
-       msmfb->black = devm_kzalloc(&pdev->dev,
-                                   msmfb->fb->var.bits_per_pixel*msmfb->xres,
-                                   GFP_KERNEL);
-       if (!msmfb->black) {
-               ret = -ENOMEM;
-               goto error_register_framebuffer;
-       }
-
-       printk(KERN_INFO "msmfb_probe() installing %d x %d panel\n",
-              msmfb->xres, msmfb->yres);
-
-       msmfb->dma_callback.func = msmfb_handle_dma_interrupt;
-       msmfb->vsync_callback.func = msmfb_handle_vsync_interrupt;
-       hrtimer_init(&msmfb->fake_vsync, CLOCK_MONOTONIC,
-                    HRTIMER_MODE_REL);
-
-
-       msmfb->fake_vsync.function = msmfb_fake_vsync;
-
-       ret = register_framebuffer(fb);
-       if (ret)
-               goto error_register_framebuffer;
-
-       msmfb->sleeping = WAKING;
-
-       platform_set_drvdata(pdev, msmfb);
-
-       return 0;
-
-error_register_framebuffer:
-       iounmap(fb->screen_base);
-error_setup_fbmem:
-       framebuffer_release(msmfb->fb);
-       return ret;
-}
-
-static int msmfb_remove(struct platform_device *pdev)
-{
-       struct msmfb_info *msmfb;
-
-       msmfb = platform_get_drvdata(pdev);
-
-       unregister_framebuffer(msmfb->fb);
-       iounmap(msmfb->fb->screen_base);
-       framebuffer_release(msmfb->fb);
-
-       return 0;
-}
-
-static struct platform_driver msm_panel_driver = {
-       /* need to write remove */
-       .probe = msmfb_probe,
-       .remove = msmfb_remove,
-       .driver = {.name = "msm_panel"},
-};
-
-
-static int msmfb_add_mdp_device(struct device *dev,
-                               struct class_interface *class_intf)
-{
-       /* might need locking if mulitple mdp devices */
-       if (mdp)
-               return 0;
-       mdp = container_of(dev, struct mdp_device, dev);
-       return platform_driver_register(&msm_panel_driver);
-}
-
-static void msmfb_remove_mdp_device(struct device *dev,
-                               struct class_interface *class_intf)
-{
-       /* might need locking if mulitple mdp devices */
-       if (dev != &mdp->dev)
-               return;
-       platform_driver_unregister(&msm_panel_driver);
-       mdp = NULL;
-}
-
-static struct class_interface msm_fb_interface = {
-       .add_dev = &msmfb_add_mdp_device,
-       .remove_dev = &msmfb_remove_mdp_device,
-};
-
-static int __init msmfb_init(void)
-{
-       return register_mdp_client(&msm_fb_interface);
-}
-
-module_init(msmfb_init);
index f8ac4a452f26d5346613dc981e4eb25ed3992cef..4e6608ceac09c20f8d85d844386d22c1207e8230 100644 (file)
@@ -316,6 +316,18 @@ static int mxsfb_check_var(struct fb_var_screeninfo *var,
        return 0;
 }
 
+static inline void mxsfb_enable_axi_clk(struct mxsfb_info *host)
+{
+       if (host->clk_axi)
+               clk_prepare_enable(host->clk_axi);
+}
+
+static inline void mxsfb_disable_axi_clk(struct mxsfb_info *host)
+{
+       if (host->clk_axi)
+               clk_disable_unprepare(host->clk_axi);
+}
+
 static void mxsfb_enable_controller(struct fb_info *fb_info)
 {
        struct mxsfb_info *host = to_imxfb_host(fb_info);
@@ -333,14 +345,13 @@ static void mxsfb_enable_controller(struct fb_info *fb_info)
                }
        }
 
-       if (host->clk_axi)
-               clk_prepare_enable(host->clk_axi);
-
        if (host->clk_disp_axi)
                clk_prepare_enable(host->clk_disp_axi);
        clk_prepare_enable(host->clk);
        clk_set_rate(host->clk, PICOS2KHZ(fb_info->var.pixclock) * 1000U);
 
+       mxsfb_enable_axi_clk(host);
+
        /* if it was disabled, re-enable the mode again */
        writel(CTRL_DOTCLK_MODE, host->base + LCDC_CTRL + REG_SET);
 
@@ -380,11 +391,11 @@ static void mxsfb_disable_controller(struct fb_info *fb_info)
        reg = readl(host->base + LCDC_VDCTRL4);
        writel(reg & ~VDCTRL4_SYNC_SIGNALS_ON, host->base + LCDC_VDCTRL4);
 
+       mxsfb_disable_axi_clk(host);
+
        clk_disable_unprepare(host->clk);
        if (host->clk_disp_axi)
                clk_disable_unprepare(host->clk_disp_axi);
-       if (host->clk_axi)
-               clk_disable_unprepare(host->clk_axi);
 
        host->enabled = 0;
 
@@ -421,6 +432,8 @@ static int mxsfb_set_par(struct fb_info *fb_info)
                mxsfb_disable_controller(fb_info);
        }
 
+       mxsfb_enable_axi_clk(host);
+
        /* clear the FIFOs */
        writel(CTRL1_FIFO_CLEAR, host->base + LCDC_CTRL1 + REG_SET);
 
@@ -438,6 +451,7 @@ static int mxsfb_set_par(struct fb_info *fb_info)
                ctrl |= CTRL_SET_WORD_LENGTH(3);
                switch (host->ld_intf_width) {
                case STMLCDIF_8BIT:
+                       mxsfb_disable_axi_clk(host);
                        dev_err(&host->pdev->dev,
                                        "Unsupported LCD bus width mapping\n");
                        return -EINVAL;
@@ -451,6 +465,7 @@ static int mxsfb_set_par(struct fb_info *fb_info)
                writel(CTRL1_SET_BYTE_PACKAGING(0x7), host->base + LCDC_CTRL1);
                break;
        default:
+               mxsfb_disable_axi_clk(host);
                dev_err(&host->pdev->dev, "Unhandled color depth of %u\n",
                                fb_info->var.bits_per_pixel);
                return -EINVAL;
@@ -504,6 +519,8 @@ static int mxsfb_set_par(struct fb_info *fb_info)
                        fb_info->fix.line_length * fb_info->var.yoffset,
                        host->base + host->devdata->next_buf);
 
+       mxsfb_disable_axi_clk(host);
+
        if (reenable)
                mxsfb_enable_controller(fb_info);
 
@@ -582,10 +599,14 @@ static int mxsfb_pan_display(struct fb_var_screeninfo *var,
 
        offset = fb_info->fix.line_length * var->yoffset;
 
+       mxsfb_enable_axi_clk(host);
+
        /* update on next VSYNC */
        writel(fb_info->fix.smem_start + offset,
                        host->base + host->devdata->next_buf);
 
+       mxsfb_disable_axi_clk(host);
+
        return 0;
 }
 
@@ -608,13 +629,17 @@ static int mxsfb_restore_mode(struct mxsfb_info *host,
        unsigned line_count;
        unsigned period;
        unsigned long pa, fbsize;
-       int bits_per_pixel, ofs;
+       int bits_per_pixel, ofs, ret = 0;
        u32 transfer_count, vdctrl0, vdctrl2, vdctrl3, vdctrl4, ctrl;
 
+       mxsfb_enable_axi_clk(host);
+
        /* Only restore the mode when the controller is running */
        ctrl = readl(host->base + LCDC_CTRL);
-       if (!(ctrl & CTRL_RUN))
-               return -EINVAL;
+       if (!(ctrl & CTRL_RUN)) {
+               ret = -EINVAL;
+               goto err;
+       }
 
        vdctrl0 = readl(host->base + LCDC_VDCTRL0);
        vdctrl2 = readl(host->base + LCDC_VDCTRL2);
@@ -635,7 +660,8 @@ static int mxsfb_restore_mode(struct mxsfb_info *host,
                break;
        case 1:
        default:
-               return -EINVAL;
+               ret = -EINVAL;
+               goto err;
        }
 
        fb_info->var.bits_per_pixel = bits_per_pixel;
@@ -673,10 +699,14 @@ static int mxsfb_restore_mode(struct mxsfb_info *host,
 
        pa = readl(host->base + host->devdata->cur_buf);
        fbsize = fb_info->fix.line_length * vmode->yres;
-       if (pa < fb_info->fix.smem_start)
-               return -EINVAL;
-       if (pa + fbsize > fb_info->fix.smem_start + fb_info->fix.smem_len)
-               return -EINVAL;
+       if (pa < fb_info->fix.smem_start) {
+               ret = -EINVAL;
+               goto err;
+       }
+       if (pa + fbsize > fb_info->fix.smem_start + fb_info->fix.smem_len) {
+               ret = -EINVAL;
+               goto err;
+       }
        ofs = pa - fb_info->fix.smem_start;
        if (ofs) {
                memmove(fb_info->screen_base, fb_info->screen_base + ofs, fbsize);
@@ -689,7 +719,11 @@ static int mxsfb_restore_mode(struct mxsfb_info *host,
        clk_prepare_enable(host->clk);
        host->enabled = 1;
 
-       return 0;
+err:
+       if (ret)
+               mxsfb_disable_axi_clk(host);
+
+       return ret;
 }
 
 static int mxsfb_init_fbinfo_dt(struct mxsfb_info *host,
@@ -814,7 +848,7 @@ static void mxsfb_free_videomem(struct mxsfb_info *host)
        free_pages_exact(fb_info->screen_base, fb_info->fix.smem_len);
 }
 
-static struct platform_device_id mxsfb_devtype[] = {
+static const struct platform_device_id mxsfb_devtype[] = {
        {
                .name = "imx23-fb",
                .driver_data = MXSFB_V3,
@@ -915,7 +949,9 @@ static int mxsfb_probe(struct platform_device *pdev)
        }
 
        if (!host->enabled) {
+               mxsfb_enable_axi_clk(host);
                writel(0, host->base + LCDC_CTRL);
+               mxsfb_disable_axi_clk(host);
                mxsfb_set_par(fb_info);
                mxsfb_enable_controller(fb_info);
        }
@@ -954,11 +990,15 @@ static void mxsfb_shutdown(struct platform_device *pdev)
        struct fb_info *fb_info = platform_get_drvdata(pdev);
        struct mxsfb_info *host = to_imxfb_host(fb_info);
 
+       mxsfb_enable_axi_clk(host);
+
        /*
         * Force stop the LCD controller as keeping it running during reboot
         * might interfere with the BootROM's boot mode pads sampling.
         */
        writel(CTRL_RUN, host->base + LCDC_CTRL + REG_CLR);
+
+       mxsfb_disable_axi_clk(host);
 }
 
 static struct platform_driver mxsfb_driver = {
index 44f99a60bb9b768a9bcef3e8f3520dd0636d6222..db023a97d1eaedca6dc1d1c8929330c4a993f390 100644 (file)
 #include <asm/io.h>
 #include <asm/irq.h>
 #include <asm/pgtable.h>
-
-#ifdef CONFIG_MTRR
-#include <asm/mtrr.h>
-#endif
-
 #include <video/vga.h>
 #include <video/neomagic.h>
 
@@ -1710,6 +1705,7 @@ static int neo_map_video(struct fb_info *info, struct pci_dev *dev,
                         int video_len)
 {
        //unsigned long addr;
+       struct neofb_par *par = info->par;
 
        DBG("neo_map_video");
 
@@ -1723,7 +1719,7 @@ static int neo_map_video(struct fb_info *info, struct pci_dev *dev,
        }
 
        info->screen_base =
-           ioremap(info->fix.smem_start, info->fix.smem_len);
+           ioremap_wc(info->fix.smem_start, info->fix.smem_len);
        if (!info->screen_base) {
                printk("neofb: unable to map screen memory\n");
                release_mem_region(info->fix.smem_start,
@@ -1733,11 +1729,8 @@ static int neo_map_video(struct fb_info *info, struct pci_dev *dev,
                printk(KERN_INFO "neofb: mapped framebuffer at %p\n",
                       info->screen_base);
 
-#ifdef CONFIG_MTRR
-       ((struct neofb_par *)(info->par))->mtrr =
-               mtrr_add(info->fix.smem_start, pci_resource_len(dev, 0),
-                               MTRR_TYPE_WRCOMB, 1);
-#endif
+       par->wc_cookie = arch_phys_wc_add(info->fix.smem_start,
+                                         pci_resource_len(dev, 0));
 
        /* Clear framebuffer, it's all white in memory after boot */
        memset_io(info->screen_base, 0, info->fix.smem_len);
@@ -1754,16 +1747,11 @@ static int neo_map_video(struct fb_info *info, struct pci_dev *dev,
 
 static void neo_unmap_video(struct fb_info *info)
 {
-       DBG("neo_unmap_video");
+       struct neofb_par *par = info->par;
 
-#ifdef CONFIG_MTRR
-       {
-               struct neofb_par *par = info->par;
+       DBG("neo_unmap_video");
 
-               mtrr_del(par->mtrr, info->fix.smem_start,
-                        info->fix.smem_len);
-       }
-#endif
+       arch_phys_wc_del(par->wc_cookie);
        iounmap(info->screen_base);
        info->screen_base = NULL;
 
index c03f7f55c76d47199909dd89d6d520ac07f68f1f..6ff321a3681328c014f39295b3c5b5cd8a8295bc 100644 (file)
@@ -148,12 +148,7 @@ struct nvidia_par {
        u32 forceCRTC;
        u32 open_count;
        u8 DDCBase;
-#ifdef CONFIG_MTRR
-       struct {
-               int vram;
-               int vram_valid;
-       } mtrr;
-#endif
+       int wc_cookie;
        struct nvidia_i2c_chan chan[3];
 
        volatile u32 __iomem *REGS;
index 4273c6ee8cf6baf166fa5d38121c1bf6f3ca735c..ce7dab7299fe2e66846171bdc60e073da57c0581 100644 (file)
@@ -21,9 +21,6 @@
 #include <linux/pci.h>
 #include <linux/console.h>
 #include <linux/backlight.h>
-#ifdef CONFIG_MTRR
-#include <asm/mtrr.h>
-#endif
 #ifdef CONFIG_BOOTX_TEXT
 #include <asm/btext.h>
 #endif
@@ -76,9 +73,7 @@ static int paneltweak = 0;
 static int vram = 0;
 static int bpp = 8;
 static int reverse_i2c;
-#ifdef CONFIG_MTRR
 static bool nomtrr = false;
-#endif
 #ifdef CONFIG_PMAC_BACKLIGHT
 static int backlight = 1;
 #else
@@ -1361,7 +1356,8 @@ static int nvidiafb_probe(struct pci_dev *pd, const struct pci_device_id *ent)
        par->ScratchBufferStart = par->FbUsableSize - par->ScratchBufferSize;
        par->CursorStart = par->FbUsableSize + (32 * 1024);
 
-       info->screen_base = ioremap(nvidiafb_fix.smem_start, par->FbMapSize);
+       info->screen_base = ioremap_wc(nvidiafb_fix.smem_start,
+                                      par->FbMapSize);
        info->screen_size = par->FbUsableSize;
        nvidiafb_fix.smem_len = par->RamAmountKBytes * 1024;
 
@@ -1372,20 +1368,9 @@ static int nvidiafb_probe(struct pci_dev *pd, const struct pci_device_id *ent)
 
        par->FbStart = info->screen_base;
 
-#ifdef CONFIG_MTRR
-       if (!nomtrr) {
-               par->mtrr.vram = mtrr_add(nvidiafb_fix.smem_start,
-                                         par->RamAmountKBytes * 1024,
-                                         MTRR_TYPE_WRCOMB, 1);
-               if (par->mtrr.vram < 0) {
-                       printk(KERN_ERR PFX "unable to setup MTRR\n");
-               } else {
-                       par->mtrr.vram_valid = 1;
-                       /* let there be speed */
-                       printk(KERN_INFO PFX "MTRR set to ON\n");
-               }
-       }
-#endif                         /* CONFIG_MTRR */
+       if (!nomtrr)
+               par->wc_cookie = arch_phys_wc_add(nvidiafb_fix.smem_start,
+                                                 par->RamAmountKBytes * 1024);
 
        info->fbops = &nvidia_fb_ops;
        info->fix = nvidiafb_fix;
@@ -1443,13 +1428,7 @@ static void nvidiafb_remove(struct pci_dev *pd)
        unregister_framebuffer(info);
 
        nvidia_bl_exit(par);
-
-#ifdef CONFIG_MTRR
-       if (par->mtrr.vram_valid)
-               mtrr_del(par->mtrr.vram, info->fix.smem_start,
-                        info->fix.smem_len);
-#endif                         /* CONFIG_MTRR */
-
+       arch_phys_wc_del(par->wc_cookie);
        iounmap(info->screen_base);
        fb_destroy_modedb(info->monspecs.modedb);
        nvidia_delete_i2c_busses(par);
@@ -1501,10 +1480,8 @@ static int nvidiafb_setup(char *options)
                        vram = simple_strtoul(this_opt+5, NULL, 0);
                } else if (!strncmp(this_opt, "backlight:", 10)) {
                        backlight = simple_strtoul(this_opt+10, NULL, 0);
-#ifdef CONFIG_MTRR
                } else if (!strncmp(this_opt, "nomtrr", 6)) {
                        nomtrr = true;
-#endif
                } else if (!strncmp(this_opt, "fpdither:", 9)) {
                        fpdither = simple_strtol(this_opt+9, NULL, 0);
                } else if (!strncmp(this_opt, "bpp:", 4)) {
@@ -1592,11 +1569,9 @@ MODULE_PARM_DESC(bpp, "pixel width in bits"
                 "(default=8)");
 module_param(reverse_i2c, int, 0);
 MODULE_PARM_DESC(reverse_i2c, "reverse port assignment of the i2c bus");
-#ifdef CONFIG_MTRR
 module_param(nomtrr, bool, false);
 MODULE_PARM_DESC(nomtrr, "Disables MTRR support (0 or 1=disabled) "
                 "(default=0)");
-#endif
 
 MODULE_AUTHOR("Antonino Daplas");
 MODULE_DESCRIPTION("Framebuffer driver for nVidia graphics chipset");
index 18c4cb0d569078bf99133b555ca9d1c280c31bcd..29d250da8a3e5f976a7fd39341e4540d43a7f35d 100644 (file)
@@ -42,7 +42,7 @@ config FB_OMAP_LCD_MIPID
 config FB_OMAP_LCD_H3
        bool "TPS65010 LCD controller on OMAP-H3"
        depends on MACH_OMAP_H3
-       depends on TPS65010
+       depends on TPS65010=y
        default y
        help
          Say Y here if you want to have support for the LCD on the
index 84a6b3367124edd4c877d39107d863843f50fe15..a14d993f719dddd4a80a3effc45f71566c6c8666 100644 (file)
@@ -201,15 +201,9 @@ static int opa362_probe(struct platform_device *pdev)
 
        platform_set_drvdata(pdev, ddata);
 
-       gpio = devm_gpiod_get(&pdev->dev, "enable");
-       if (IS_ERR(gpio)) {
-               if (PTR_ERR(gpio) != -ENOENT)
-                       return PTR_ERR(gpio);
-
-               gpio = NULL;
-       } else {
-               gpiod_direction_output(gpio, 0);
-       }
+       gpio = devm_gpiod_get_optional(&pdev->dev, "enable", GPIOD_OUT_LOW);
+       if (IS_ERR(gpio))
+               return PTR_ERR(gpio);
 
        ddata->enable_gpio = gpio;
 
index eb8fd8140ad03ec8b972ee0ffda854120bdc35e6..f7be3489f74436d6a11258fd9402d8a49c81dda2 100644 (file)
@@ -209,16 +209,9 @@ static int panel_dpi_probe_of(struct platform_device *pdev)
        struct videomode vm;
        struct gpio_desc *gpio;
 
-       gpio = devm_gpiod_get(&pdev->dev, "enable");
-
-       if (IS_ERR(gpio)) {
-               if (PTR_ERR(gpio) != -ENOENT)
-                       return PTR_ERR(gpio);
-               else
-                       gpio = NULL;
-       } else {
-               gpiod_direction_output(gpio, 0);
-       }
+       gpio = devm_gpiod_get_optional(&pdev->dev, "enable", GPIOD_OUT_LOW);
+       if (IS_ERR(gpio))
+               return PTR_ERR(gpio);
 
        ddata->enable_gpio = gpio;
 
index 9974a37a11af9455a508ffd9b7010296bc465275..6a1b6a89a92868994c1adcffd1644db3063572ae 100644 (file)
@@ -285,15 +285,14 @@ static int lb035q02_probe_of(struct spi_device *spi)
        struct omap_dss_device *in;
        struct gpio_desc *gpio;
 
-       gpio = devm_gpiod_get(&spi->dev, "enable");
+       gpio = devm_gpiod_get(&spi->dev, "enable", GPIOD_OUT_LOW);
        if (IS_ERR(gpio)) {
                dev_err(&spi->dev, "failed to parse enable gpio\n");
                return PTR_ERR(gpio);
-       } else {
-               gpiod_direction_output(gpio, 0);
-               ddata->enable_gpio = gpio;
        }
 
+       ddata->enable_gpio = gpio;
+
        ddata->backlight_gpio = -ENOENT;
 
        in = omapdss_of_find_source_for_first_ep(node);
index eae263702964a7e78f22f922acc4b0b451e6b8ec..abfd1f6e33275b6d3b192df0c504669ab771c5e7 100644 (file)
@@ -268,17 +268,12 @@ static  int sharp_ls_get_gpio_of(struct device *dev, int index, int val,
        const char *desc, struct gpio_desc **gpiod)
 {
        struct gpio_desc *gd;
-       int r;
 
        *gpiod = NULL;
 
-       gd = devm_gpiod_get_index(dev, desc, index);
+       gd = devm_gpiod_get_index(dev, desc, index, GPIOD_OUT_LOW);
        if (IS_ERR(gd))
-               return PTR_ERR(gd) == -ENOENT ? 0 : PTR_ERR(gd);
-
-       r = gpiod_direction_output(gd, val);
-       if (r)
-               return r;
+               return PTR_ERR(gd);
 
        *gpiod = gd;
        return 0;
index 16751755d43364c02c6b0d4814ea7b091cad474b..54eeb507f9b379c4c494a4f4a5b5dcbe7e9b6ea7 100644 (file)
@@ -50,8 +50,6 @@ static char *def_disp_name;
 module_param_named(def_disp, def_disp_name, charp, 0);
 MODULE_PARM_DESC(def_disp, "default display name");
 
-static bool dss_initialized;
-
 const char *omapdss_get_default_display_name(void)
 {
        return core.default_display_name;
@@ -65,12 +63,6 @@ enum omapdss_version omapdss_get_version(void)
 }
 EXPORT_SYMBOL(omapdss_get_version);
 
-bool omapdss_is_initialized(void)
-{
-       return dss_initialized;
-}
-EXPORT_SYMBOL(omapdss_is_initialized);
-
 struct platform_device *dss_get_core_pdev(void)
 {
        return core.pdev;
@@ -253,6 +245,8 @@ static struct platform_driver omap_dss_driver = {
 
 /* INIT */
 static int (*dss_output_drv_reg_funcs[])(void) __initdata = {
+       dss_init_platform_driver,
+       dispc_init_platform_driver,
 #ifdef CONFIG_OMAP2_DSS_DSI
        dsi_init_platform_driver,
 #endif
@@ -276,32 +270,32 @@ static int (*dss_output_drv_reg_funcs[])(void) __initdata = {
 #endif
 };
 
-static void (*dss_output_drv_unreg_funcs[])(void) __exitdata = {
-#ifdef CONFIG_OMAP2_DSS_DSI
-       dsi_uninit_platform_driver,
+static void (*dss_output_drv_unreg_funcs[])(void) = {
+#ifdef CONFIG_OMAP5_DSS_HDMI
+       hdmi5_uninit_platform_driver,
 #endif
-#ifdef CONFIG_OMAP2_DSS_DPI
-       dpi_uninit_platform_driver,
+#ifdef CONFIG_OMAP4_DSS_HDMI
+       hdmi4_uninit_platform_driver,
 #endif
-#ifdef CONFIG_OMAP2_DSS_SDI
-       sdi_uninit_platform_driver,
+#ifdef CONFIG_OMAP2_DSS_VENC
+       venc_uninit_platform_driver,
 #endif
 #ifdef CONFIG_OMAP2_DSS_RFBI
        rfbi_uninit_platform_driver,
 #endif
-#ifdef CONFIG_OMAP2_DSS_VENC
-       venc_uninit_platform_driver,
+#ifdef CONFIG_OMAP2_DSS_SDI
+       sdi_uninit_platform_driver,
 #endif
-#ifdef CONFIG_OMAP4_DSS_HDMI
-       hdmi4_uninit_platform_driver,
+#ifdef CONFIG_OMAP2_DSS_DPI
+       dpi_uninit_platform_driver,
 #endif
-#ifdef CONFIG_OMAP5_DSS_HDMI
-       hdmi5_uninit_platform_driver,
+#ifdef CONFIG_OMAP2_DSS_DSI
+       dsi_uninit_platform_driver,
 #endif
+       dispc_uninit_platform_driver,
+       dss_uninit_platform_driver,
 };
 
-static bool dss_output_drv_loaded[ARRAY_SIZE(dss_output_drv_reg_funcs)];
-
 static int __init omap_dss_init(void)
 {
        int r;
@@ -311,35 +305,20 @@ static int __init omap_dss_init(void)
        if (r)
                return r;
 
-       r = dss_init_platform_driver();
-       if (r) {
-               DSSERR("Failed to initialize DSS platform driver\n");
-               goto err_dss;
-       }
-
-       r = dispc_init_platform_driver();
-       if (r) {
-               DSSERR("Failed to initialize dispc platform driver\n");
-               goto err_dispc;
-       }
-
-       /*
-        * It's ok if the output-driver register fails. It happens, for example,
-        * when there is no output-device (e.g. SDI for OMAP4).
-        */
        for (i = 0; i < ARRAY_SIZE(dss_output_drv_reg_funcs); ++i) {
                r = dss_output_drv_reg_funcs[i]();
-               if (r == 0)
-                       dss_output_drv_loaded[i] = true;
+               if (r)
+                       goto err_reg;
        }
 
-       dss_initialized = true;
-
        return 0;
 
-err_dispc:
-       dss_uninit_platform_driver();
-err_dss:
+err_reg:
+       for (i = ARRAY_SIZE(dss_output_drv_reg_funcs) - i;
+                       i < ARRAY_SIZE(dss_output_drv_reg_funcs);
+                       ++i)
+               dss_output_drv_unreg_funcs[i]();
+
        platform_driver_unregister(&omap_dss_driver);
 
        return r;
@@ -349,13 +328,8 @@ static void __exit omap_dss_exit(void)
 {
        int i;
 
-       for (i = 0; i < ARRAY_SIZE(dss_output_drv_unreg_funcs); ++i) {
-               if (dss_output_drv_loaded[i])
-                       dss_output_drv_unreg_funcs[i]();
-       }
-
-       dispc_uninit_platform_driver();
-       dss_uninit_platform_driver();
+       for (i = 0; i < ARRAY_SIZE(dss_output_drv_unreg_funcs); ++i)
+               dss_output_drv_unreg_funcs[i]();
 
        platform_driver_unregister(&omap_dss_driver);
 }
index f4fc77d9d3bfcb02f807f8781142d43d22840630..be716c9ffb88a8bc998cc07ba42de950d55a2ac0 100644 (file)
@@ -39,6 +39,7 @@
 #include <linux/mfd/syscon.h>
 #include <linux/regmap.h>
 #include <linux/of.h>
+#include <linux/component.h>
 
 #include <video/omapdss.h>
 
@@ -95,6 +96,9 @@ struct dispc_features {
        bool mstandby_workaround:1;
 
        bool set_max_preload:1;
+
+       /* PIXEL_INC is not added to the last pixel of a line */
+       bool last_pixel_inc_missing:1;
 };
 
 #define DISPC_MAX_NR_FIFOS 5
@@ -1741,6 +1745,15 @@ static void dispc_ovl_set_rotation_attrs(enum omap_plane plane, u8 rotation,
                        row_repeat = false;
        }
 
+       /*
+        * OMAP4/5 Errata i631:
+        * NV12 in 1D mode must use ROTATION=1. Otherwise DSS will fetch extra
+        * rows beyond the framebuffer, which may cause OCP error.
+        */
+       if (color_mode == OMAP_DSS_COLOR_NV12 &&
+                       rotation_type != OMAP_DSS_ROT_TILER)
+               vidrot = 1;
+
        REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), vidrot, 13, 12);
        if (dss_has_feature(FEAT_ROWREPEATENABLE))
                REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane),
@@ -2154,7 +2167,7 @@ static unsigned long calc_core_clk_five_taps(unsigned long pclk,
        if (height > out_height) {
                unsigned int ppl = mgr_timings->x_res;
 
-               tmp = pclk * height * out_width;
+               tmp = (u64)pclk * height * out_width;
                do_div(tmp, 2 * out_height * ppl);
                core_clk = tmp;
 
@@ -2162,14 +2175,14 @@ static unsigned long calc_core_clk_five_taps(unsigned long pclk,
                        if (ppl == out_width)
                                return 0;
 
-                       tmp = pclk * (height - 2 * out_height) * out_width;
+                       tmp = (u64)pclk * (height - 2 * out_height) * out_width;
                        do_div(tmp, 2 * out_height * (ppl - out_width));
                        core_clk = max_t(u32, core_clk, tmp);
                }
        }
 
        if (width > out_width) {
-               tmp = pclk * width;
+               tmp = (u64)pclk * width;
                do_div(tmp, out_width);
                core_clk = max_t(u32, core_clk, tmp);
 
@@ -2267,6 +2280,11 @@ static int dispc_ovl_calc_scaling_24xx(unsigned long pclk, unsigned long lclk,
                }
        } while (*decim_x <= *x_predecim && *decim_y <= *y_predecim && error);
 
+       if (error) {
+               DSSERR("failed to find scaling settings\n");
+               return -EINVAL;
+       }
+
        if (in_width > maxsinglelinewidth) {
                DSSERR("Cannot scale max input width exceeded");
                return -EINVAL;
@@ -2283,7 +2301,6 @@ static int dispc_ovl_calc_scaling_34xx(unsigned long pclk, unsigned long lclk,
 {
        int error;
        u16 in_width, in_height;
-       int min_factor = min(*decim_x, *decim_y);
        const int maxsinglelinewidth =
                        dss_feat_get_param_max(FEAT_PARAM_LINEWIDTH);
 
@@ -2317,20 +2334,32 @@ again:
                error = (error || in_width > maxsinglelinewidth * 2 ||
                        (in_width > maxsinglelinewidth && *five_taps) ||
                        !*core_clk || *core_clk > dispc_core_clk_rate());
-               if (error) {
-                       if (*decim_x == *decim_y) {
-                               *decim_x = min_factor;
-                               ++*decim_y;
+
+               if (!error) {
+                       /* verify that we're inside the limits of scaler */
+                       if (in_width / 4 > out_width)
+                                       error = 1;
+
+                       if (*five_taps) {
+                               if (in_height / 4 > out_height)
+                                       error = 1;
                        } else {
-                               swap(*decim_x, *decim_y);
-                               if (*decim_x < *decim_y)
-                                       ++*decim_x;
+                               if (in_height / 2 > out_height)
+                                       error = 1;
                        }
                }
+
+               if (error)
+                       ++*decim_y;
        } while (*decim_x <= *x_predecim && *decim_y <= *y_predecim && error);
 
-       if (check_horiz_timing_omap3(pclk, lclk, mgr_timings, pos_x, width,
-                               height, out_width, out_height, *five_taps)) {
+       if (error) {
+               DSSERR("failed to find scaling settings\n");
+               return -EINVAL;
+       }
+
+       if (check_horiz_timing_omap3(pclk, lclk, mgr_timings, pos_x, in_width,
+                               in_height, out_width, out_height, *five_taps)) {
                        DSSERR("horizontal timing too tight\n");
                        return -EINVAL;
        }
@@ -2390,6 +2419,9 @@ static int dispc_ovl_calc_scaling_44xx(unsigned long pclk, unsigned long lclk,
        return 0;
 }
 
+#define DIV_FRAC(dividend, divisor) \
+       ((dividend) * 100 / (divisor) - ((dividend) / (divisor) * 100))
+
 static int dispc_ovl_calc_scaling(unsigned long pclk, unsigned long lclk,
                enum omap_overlay_caps caps,
                const struct omap_video_timings *mgr_timings,
@@ -2449,8 +2481,19 @@ static int dispc_ovl_calc_scaling(unsigned long pclk, unsigned long lclk,
        if (ret)
                return ret;
 
-       DSSDBG("required core clk rate = %lu Hz\n", core_clk);
-       DSSDBG("current core clk rate = %lu Hz\n", dispc_core_clk_rate());
+       DSSDBG("%dx%d -> %dx%d (%d.%02d x %d.%02d), decim %dx%d %dx%d (%d.%02d x %d.%02d), taps %d, req clk %lu, cur clk %lu\n",
+               width, height,
+               out_width, out_height,
+               out_width / width, DIV_FRAC(out_width, width),
+               out_height / height, DIV_FRAC(out_height, height),
+
+               decim_x, decim_y,
+               width / decim_x, height / decim_y,
+               out_width / (width / decim_x), DIV_FRAC(out_width, width / decim_x),
+               out_height / (height / decim_y), DIV_FRAC(out_height, height / decim_y),
+
+               *five_taps ? 5 : 3,
+               core_clk, dispc_core_clk_rate());
 
        if (!core_clk || core_clk > dispc_core_clk_rate()) {
                DSSERR("failed to set up scaling, "
@@ -2533,6 +2576,21 @@ static int dispc_ovl_setup_common(enum omap_plane plane,
        if (paddr == 0 && rotation_type != OMAP_DSS_ROT_TILER)
                return -EINVAL;
 
+       switch (color_mode) {
+       case OMAP_DSS_COLOR_YUV2:
+       case OMAP_DSS_COLOR_UYVY:
+       case OMAP_DSS_COLOR_NV12:
+               if (in_width & 1) {
+                       DSSERR("input width %d is not even for YUV format\n",
+                               in_width);
+                       return -EINVAL;
+               }
+               break;
+
+       default:
+               break;
+       }
+
        out_width = out_width == 0 ? width : out_width;
        out_height = out_height == 0 ? height : out_height;
 
@@ -2563,6 +2621,27 @@ static int dispc_ovl_setup_common(enum omap_plane plane,
        in_width = in_width / x_predecim;
        in_height = in_height / y_predecim;
 
+       if (x_predecim > 1 || y_predecim > 1)
+               DSSDBG("predecimation %d x %x, new input size %d x %d\n",
+                       x_predecim, y_predecim, in_width, in_height);
+
+       switch (color_mode) {
+       case OMAP_DSS_COLOR_YUV2:
+       case OMAP_DSS_COLOR_UYVY:
+       case OMAP_DSS_COLOR_NV12:
+               if (in_width & 1) {
+                       DSSDBG("predecimated input width is not even for YUV format\n");
+                       DSSDBG("adjusting input width %d -> %d\n",
+                               in_width, in_width & ~1);
+
+                       in_width &= ~1;
+               }
+               break;
+
+       default:
+               break;
+       }
+
        if (color_mode == OMAP_DSS_COLOR_YUV2 ||
                        color_mode == OMAP_DSS_COLOR_UYVY ||
                        color_mode == OMAP_DSS_COLOR_NV12)
@@ -2632,6 +2711,9 @@ static int dispc_ovl_setup_common(enum omap_plane plane,
                dispc_ovl_set_ba1_uv(plane, p_uv_addr + offset1);
        }
 
+       if (dispc.feat->last_pixel_inc_missing)
+               row_inc += pix_inc - 1;
+
        dispc_ovl_set_row_inc(plane, row_inc);
        dispc_ovl_set_pix_inc(plane, pix_inc);
 
@@ -3692,7 +3774,7 @@ static void _omap_dispc_initial_config(void)
                dispc_init_mflag();
 }
 
-static const struct dispc_features omap24xx_dispc_feats __initconst = {
+static const struct dispc_features omap24xx_dispc_feats = {
        .sw_start               =       5,
        .fp_start               =       15,
        .bp_start               =       27,
@@ -3709,9 +3791,10 @@ static const struct dispc_features omap24xx_dispc_feats __initconst = {
        .num_fifos              =       3,
        .no_framedone_tv        =       true,
        .set_max_preload        =       false,
+       .last_pixel_inc_missing =       true,
 };
 
-static const struct dispc_features omap34xx_rev1_0_dispc_feats __initconst = {
+static const struct dispc_features omap34xx_rev1_0_dispc_feats = {
        .sw_start               =       5,
        .fp_start               =       15,
        .bp_start               =       27,
@@ -3729,9 +3812,10 @@ static const struct dispc_features omap34xx_rev1_0_dispc_feats __initconst = {
        .num_fifos              =       3,
        .no_framedone_tv        =       true,
        .set_max_preload        =       false,
+       .last_pixel_inc_missing =       true,
 };
 
-static const struct dispc_features omap34xx_rev3_0_dispc_feats __initconst = {
+static const struct dispc_features omap34xx_rev3_0_dispc_feats = {
        .sw_start               =       7,
        .fp_start               =       19,
        .bp_start               =       31,
@@ -3749,9 +3833,10 @@ static const struct dispc_features omap34xx_rev3_0_dispc_feats __initconst = {
        .num_fifos              =       3,
        .no_framedone_tv        =       true,
        .set_max_preload        =       false,
+       .last_pixel_inc_missing =       true,
 };
 
-static const struct dispc_features omap44xx_dispc_feats __initconst = {
+static const struct dispc_features omap44xx_dispc_feats = {
        .sw_start               =       7,
        .fp_start               =       19,
        .bp_start               =       31,
@@ -3771,7 +3856,7 @@ static const struct dispc_features omap44xx_dispc_feats __initconst = {
        .set_max_preload        =       true,
 };
 
-static const struct dispc_features omap54xx_dispc_feats __initconst = {
+static const struct dispc_features omap54xx_dispc_feats = {
        .sw_start               =       7,
        .fp_start               =       19,
        .bp_start               =       31,
@@ -3792,7 +3877,7 @@ static const struct dispc_features omap54xx_dispc_feats __initconst = {
        .set_max_preload        =       true,
 };
 
-static int __init dispc_init_features(struct platform_device *pdev)
+static int dispc_init_features(struct platform_device *pdev)
 {
        const struct dispc_features *src;
        struct dispc_features *dst;
@@ -3882,8 +3967,9 @@ void dispc_free_irq(void *dev_id)
 EXPORT_SYMBOL(dispc_free_irq);
 
 /* DISPC HW IP initialisation */
-static int __init omap_dispchw_probe(struct platform_device *pdev)
+static int dispc_bind(struct device *dev, struct device *master, void *data)
 {
+       struct platform_device *pdev = to_platform_device(dev);
        u32 rev;
        int r = 0;
        struct resource *dispc_mem;
@@ -3955,12 +4041,27 @@ err_runtime_get:
        return r;
 }
 
-static int __exit omap_dispchw_remove(struct platform_device *pdev)
+static void dispc_unbind(struct device *dev, struct device *master,
+                              void *data)
 {
-       pm_runtime_disable(&pdev->dev);
+       pm_runtime_disable(dev);
 
        dss_uninit_overlay_managers();
+}
+
+static const struct component_ops dispc_component_ops = {
+       .bind   = dispc_bind,
+       .unbind = dispc_unbind,
+};
 
+static int dispc_probe(struct platform_device *pdev)
+{
+       return component_add(&pdev->dev, &dispc_component_ops);
+}
+
+static int dispc_remove(struct platform_device *pdev)
+{
+       component_del(&pdev->dev, &dispc_component_ops);
        return 0;
 }
 
@@ -4013,7 +4114,8 @@ static const struct of_device_id dispc_of_match[] = {
 };
 
 static struct platform_driver omap_dispchw_driver = {
-       .remove         = __exit_p(omap_dispchw_remove),
+       .probe          = dispc_probe,
+       .remove         = dispc_remove,
        .driver         = {
                .name   = "omapdss_dispc",
                .pm     = &dispc_pm_ops,
@@ -4024,10 +4126,10 @@ static struct platform_driver omap_dispchw_driver = {
 
 int __init dispc_init_platform_driver(void)
 {
-       return platform_driver_probe(&omap_dispchw_driver, omap_dispchw_probe);
+       return platform_driver_register(&omap_dispchw_driver);
 }
 
-void __exit dispc_uninit_platform_driver(void)
+void dispc_uninit_platform_driver(void)
 {
        platform_driver_unregister(&omap_dispchw_driver);
 }
index 12186557a9d4d5030d23dd91453029a3d2cc8a3c..6ad0991f8259e16b3c2d2f2c3fdac9df43683a5a 100644 (file)
@@ -324,7 +324,7 @@ int display_init_sysfs(struct platform_device *pdev)
 
        for_each_dss_dev(dssdev) {
                r = kobject_init_and_add(&dssdev->kobj, &display_ktype,
-                       &pdev->dev.kobj, dssdev->alias);
+                       &pdev->dev.kobj, "%s", dssdev->alias);
                if (r) {
                        DSSERR("failed to create sysfs files\n");
                        omap_dss_put_device(dssdev);
index f83e7b030249dd49727e0f081b732cf28a62ee9f..fb45b6432968e0ab73277497392729f5b5c3cdad 100644 (file)
@@ -32,6 +32,7 @@
 #include <linux/string.h>
 #include <linux/of.h>
 #include <linux/clk.h>
+#include <linux/component.h>
 
 #include <video/omapdss.h>
 
@@ -731,7 +732,7 @@ static void dpi_init_output(struct platform_device *pdev)
        omapdss_register_output(out);
 }
 
-static void __exit dpi_uninit_output(struct platform_device *pdev)
+static void dpi_uninit_output(struct platform_device *pdev)
 {
        struct dpi_data *dpi = dpi_get_data_from_pdev(pdev);
        struct omap_dss_device *out = &dpi->output;
@@ -775,7 +776,7 @@ static void dpi_init_output_port(struct platform_device *pdev,
        omapdss_register_output(out);
 }
 
-static void __exit dpi_uninit_output_port(struct device_node *port)
+static void dpi_uninit_output_port(struct device_node *port)
 {
        struct dpi_data *dpi = port->data;
        struct omap_dss_device *out = &dpi->output;
@@ -783,8 +784,9 @@ static void __exit dpi_uninit_output_port(struct device_node *port)
        omapdss_unregister_output(out);
 }
 
-static int omap_dpi_probe(struct platform_device *pdev)
+static int dpi_bind(struct device *dev, struct device *master, void *data)
 {
+       struct platform_device *pdev = to_platform_device(dev);
        struct dpi_data *dpi;
 
        dpi = devm_kzalloc(&pdev->dev, sizeof(*dpi), GFP_KERNEL);
@@ -802,16 +804,32 @@ static int omap_dpi_probe(struct platform_device *pdev)
        return 0;
 }
 
-static int __exit omap_dpi_remove(struct platform_device *pdev)
+static void dpi_unbind(struct device *dev, struct device *master, void *data)
 {
+       struct platform_device *pdev = to_platform_device(dev);
+
        dpi_uninit_output(pdev);
+}
+
+static const struct component_ops dpi_component_ops = {
+       .bind   = dpi_bind,
+       .unbind = dpi_unbind,
+};
 
+static int dpi_probe(struct platform_device *pdev)
+{
+       return component_add(&pdev->dev, &dpi_component_ops);
+}
+
+static int dpi_remove(struct platform_device *pdev)
+{
+       component_del(&pdev->dev, &dpi_component_ops);
        return 0;
 }
 
 static struct platform_driver omap_dpi_driver = {
-       .probe          = omap_dpi_probe,
-       .remove         = __exit_p(omap_dpi_remove),
+       .probe          = dpi_probe,
+       .remove         = dpi_remove,
        .driver         = {
                .name   = "omapdss_dpi",
                .suppress_bind_attrs = true,
@@ -823,12 +841,12 @@ int __init dpi_init_platform_driver(void)
        return platform_driver_register(&omap_dpi_driver);
 }
 
-void __exit dpi_uninit_platform_driver(void)
+void dpi_uninit_platform_driver(void)
 {
        platform_driver_unregister(&omap_dpi_driver);
 }
 
-int __init dpi_init_port(struct platform_device *pdev, struct device_node *port)
+int dpi_init_port(struct platform_device *pdev, struct device_node *port)
 {
        struct dpi_data *dpi;
        struct device_node *ep;
@@ -870,7 +888,7 @@ err_datalines:
        return r;
 }
 
-void __exit dpi_uninit_port(struct device_node *port)
+void dpi_uninit_port(struct device_node *port)
 {
        struct dpi_data *dpi = port->data;
 
index 28b0bc11669d59d42c16a7e51d731af87dc7a2f3..b3606def5b7ba7e853397ce223cedfb5d481027a 100644 (file)
@@ -40,6 +40,7 @@
 #include <linux/pm_runtime.h>
 #include <linux/of.h>
 #include <linux/of_platform.h>
+#include <linux/component.h>
 
 #include <video/omapdss.h>
 #include <video/mipi_display.h>
@@ -5274,8 +5275,9 @@ static int dsi_init_pll_data(struct platform_device *dsidev)
 }
 
 /* DSI1 HW IP initialisation */
-static int omap_dsihw_probe(struct platform_device *dsidev)
+static int dsi_bind(struct device *dev, struct device *master, void *data)
 {
+       struct platform_device *dsidev = to_platform_device(dev);
        u32 rev;
        int r, i;
        struct dsi_data *dsi;
@@ -5484,8 +5486,9 @@ err_runtime_get:
        return r;
 }
 
-static int __exit omap_dsihw_remove(struct platform_device *dsidev)
+static void dsi_unbind(struct device *dev, struct device *master, void *data)
 {
+       struct platform_device *dsidev = to_platform_device(dev);
        struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
 
        of_platform_depopulate(&dsidev->dev);
@@ -5502,7 +5505,21 @@ static int __exit omap_dsihw_remove(struct platform_device *dsidev)
                regulator_disable(dsi->vdds_dsi_reg);
                dsi->vdds_dsi_enabled = false;
        }
+}
+
+static const struct component_ops dsi_component_ops = {
+       .bind   = dsi_bind,
+       .unbind = dsi_unbind,
+};
 
+static int dsi_probe(struct platform_device *pdev)
+{
+       return component_add(&pdev->dev, &dsi_component_ops);
+}
+
+static int dsi_remove(struct platform_device *pdev)
+{
+       component_del(&pdev->dev, &dsi_component_ops);
        return 0;
 }
 
@@ -5569,8 +5586,8 @@ static const struct of_device_id dsi_of_match[] = {
 };
 
 static struct platform_driver omap_dsihw_driver = {
-       .probe          = omap_dsihw_probe,
-       .remove         = __exit_p(omap_dsihw_remove),
+       .probe          = dsi_probe,
+       .remove         = dsi_remove,
        .driver         = {
                .name   = "omapdss_dsi",
                .pm     = &dsi_pm_ops,
@@ -5584,7 +5601,7 @@ int __init dsi_init_platform_driver(void)
        return platform_driver_register(&omap_dsihw_driver);
 }
 
-void __exit dsi_uninit_platform_driver(void)
+void dsi_uninit_platform_driver(void)
 {
        platform_driver_unregister(&omap_dsihw_driver);
 }
index 7f978b6a34e8022c1f47c5ccc53bbfe972c8fb9b..612b093831d52be7040bcefeb279eaf9f4dd9700 100644 (file)
@@ -39,6 +39,7 @@
 #include <linux/of.h>
 #include <linux/regulator/consumer.h>
 #include <linux/suspend.h>
+#include <linux/component.h>
 
 #include <video/omapdss.h>
 
@@ -111,6 +112,14 @@ static const char * const dss_generic_clk_source_names[] = {
        [OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DSI]   = "DSI_PLL2_HSDIV_DSI",
 };
 
+static bool dss_initialized;
+
+bool omapdss_is_initialized(void)
+{
+       return dss_initialized;
+}
+EXPORT_SYMBOL(omapdss_is_initialized);
+
 static inline void dss_write_reg(const struct dss_reg idx, u32 val)
 {
        __raw_writel(val, dss.base + idx.idx);
@@ -811,7 +820,7 @@ static const enum omap_display_type dra7xx_ports[] = {
        OMAP_DISPLAY_TYPE_DPI,
 };
 
-static const struct dss_features omap24xx_dss_feats __initconst = {
+static const struct dss_features omap24xx_dss_feats = {
        /*
         * fck div max is really 16, but the divider range has gaps. The range
         * from 1 to 6 has no gaps, so let's use that as a max.
@@ -824,7 +833,7 @@ static const struct dss_features omap24xx_dss_feats __initconst = {
        .num_ports              =       ARRAY_SIZE(omap2plus_ports),
 };
 
-static const struct dss_features omap34xx_dss_feats __initconst = {
+static const struct dss_features omap34xx_dss_feats = {
        .fck_div_max            =       16,
        .dss_fck_multiplier     =       2,
        .parent_clk_name        =       "dpll4_ck",
@@ -833,7 +842,7 @@ static const struct dss_features omap34xx_dss_feats __initconst = {
        .num_ports              =       ARRAY_SIZE(omap34xx_ports),
 };
 
-static const struct dss_features omap3630_dss_feats __initconst = {
+static const struct dss_features omap3630_dss_feats = {
        .fck_div_max            =       32,
        .dss_fck_multiplier     =       1,
        .parent_clk_name        =       "dpll4_ck",
@@ -842,7 +851,7 @@ static const struct dss_features omap3630_dss_feats __initconst = {
        .num_ports              =       ARRAY_SIZE(omap2plus_ports),
 };
 
-static const struct dss_features omap44xx_dss_feats __initconst = {
+static const struct dss_features omap44xx_dss_feats = {
        .fck_div_max            =       32,
        .dss_fck_multiplier     =       1,
        .parent_clk_name        =       "dpll_per_x2_ck",
@@ -851,7 +860,7 @@ static const struct dss_features omap44xx_dss_feats __initconst = {
        .num_ports              =       ARRAY_SIZE(omap2plus_ports),
 };
 
-static const struct dss_features omap54xx_dss_feats __initconst = {
+static const struct dss_features omap54xx_dss_feats = {
        .fck_div_max            =       64,
        .dss_fck_multiplier     =       1,
        .parent_clk_name        =       "dpll_per_x2_ck",
@@ -860,7 +869,7 @@ static const struct dss_features omap54xx_dss_feats __initconst = {
        .num_ports              =       ARRAY_SIZE(omap2plus_ports),
 };
 
-static const struct dss_features am43xx_dss_feats __initconst = {
+static const struct dss_features am43xx_dss_feats = {
        .fck_div_max            =       0,
        .dss_fck_multiplier     =       0,
        .parent_clk_name        =       NULL,
@@ -869,7 +878,7 @@ static const struct dss_features am43xx_dss_feats __initconst = {
        .num_ports              =       ARRAY_SIZE(omap2plus_ports),
 };
 
-static const struct dss_features dra7xx_dss_feats __initconst = {
+static const struct dss_features dra7xx_dss_feats = {
        .fck_div_max            =       64,
        .dss_fck_multiplier     =       1,
        .parent_clk_name        =       "dpll_per_x2_ck",
@@ -878,7 +887,7 @@ static const struct dss_features dra7xx_dss_feats __initconst = {
        .num_ports              =       ARRAY_SIZE(dra7xx_ports),
 };
 
-static int __init dss_init_features(struct platform_device *pdev)
+static int dss_init_features(struct platform_device *pdev)
 {
        const struct dss_features *src;
        struct dss_features *dst;
@@ -932,7 +941,7 @@ static int __init dss_init_features(struct platform_device *pdev)
        return 0;
 }
 
-static int __init dss_init_ports(struct platform_device *pdev)
+static int dss_init_ports(struct platform_device *pdev)
 {
        struct device_node *parent = pdev->dev.of_node;
        struct device_node *port;
@@ -976,7 +985,7 @@ static int __init dss_init_ports(struct platform_device *pdev)
        return 0;
 }
 
-static void __exit dss_uninit_ports(struct platform_device *pdev)
+static void dss_uninit_ports(struct platform_device *pdev)
 {
        struct device_node *parent = pdev->dev.of_node;
        struct device_node *port;
@@ -1018,14 +1027,74 @@ static void __exit dss_uninit_ports(struct platform_device *pdev)
        } while ((port = omapdss_of_get_next_port(parent, port)) != NULL);
 }
 
+static int dss_video_pll_probe(struct platform_device *pdev)
+{
+       struct device_node *np = pdev->dev.of_node;
+       struct regulator *pll_regulator;
+       int r;
+
+       if (!np)
+               return 0;
+
+       if (of_property_read_bool(np, "syscon-pll-ctrl")) {
+               dss.syscon_pll_ctrl = syscon_regmap_lookup_by_phandle(np,
+                       "syscon-pll-ctrl");
+               if (IS_ERR(dss.syscon_pll_ctrl)) {
+                       dev_err(&pdev->dev,
+                               "failed to get syscon-pll-ctrl regmap\n");
+                       return PTR_ERR(dss.syscon_pll_ctrl);
+               }
+
+               if (of_property_read_u32_index(np, "syscon-pll-ctrl", 1,
+                               &dss.syscon_pll_ctrl_offset)) {
+                       dev_err(&pdev->dev,
+                               "failed to get syscon-pll-ctrl offset\n");
+                       return -EINVAL;
+               }
+       }
+
+       pll_regulator = devm_regulator_get(&pdev->dev, "vdda_video");
+       if (IS_ERR(pll_regulator)) {
+               r = PTR_ERR(pll_regulator);
+
+               switch (r) {
+               case -ENOENT:
+                       pll_regulator = NULL;
+                       break;
+
+               case -EPROBE_DEFER:
+                       return -EPROBE_DEFER;
+
+               default:
+                       DSSERR("can't get DPLL VDDA regulator\n");
+                       return r;
+               }
+       }
+
+       if (of_property_match_string(np, "reg-names", "pll1") >= 0) {
+               dss.video1_pll = dss_video_pll_init(pdev, 0, pll_regulator);
+               if (IS_ERR(dss.video1_pll))
+                       return PTR_ERR(dss.video1_pll);
+       }
+
+       if (of_property_match_string(np, "reg-names", "pll2") >= 0) {
+               dss.video2_pll = dss_video_pll_init(pdev, 1, pll_regulator);
+               if (IS_ERR(dss.video2_pll)) {
+                       dss_video_pll_uninit(dss.video1_pll);
+                       return PTR_ERR(dss.video2_pll);
+               }
+       }
+
+       return 0;
+}
+
 /* DSS HW IP initialisation */
-static int __init omap_dsshw_probe(struct platform_device *pdev)
+static int dss_bind(struct device *dev)
 {
+       struct platform_device *pdev = to_platform_device(dev);
        struct resource *dss_mem;
-       struct device_node *np = pdev->dev.of_node;
        u32 rev;
        int r;
-       struct regulator *pll_regulator;
 
        dss.pdev = pdev;
 
@@ -1054,6 +1123,14 @@ static int __init omap_dsshw_probe(struct platform_device *pdev)
        if (r)
                goto err_setup_clocks;
 
+       r = dss_video_pll_probe(pdev);
+       if (r)
+               goto err_pll_init;
+
+       r = dss_init_ports(pdev);
+       if (r)
+               goto err_init_ports;
+
        pm_runtime_enable(&pdev->dev);
 
        r = dss_runtime_get();
@@ -1078,86 +1155,48 @@ static int __init omap_dsshw_probe(struct platform_device *pdev)
        dss.lcd_clk_source[0] = OMAP_DSS_CLK_SRC_FCK;
        dss.lcd_clk_source[1] = OMAP_DSS_CLK_SRC_FCK;
 
-       dss_init_ports(pdev);
-
-       if (np && of_property_read_bool(np, "syscon-pll-ctrl")) {
-               dss.syscon_pll_ctrl = syscon_regmap_lookup_by_phandle(np,
-                       "syscon-pll-ctrl");
-               if (IS_ERR(dss.syscon_pll_ctrl)) {
-                       dev_err(&pdev->dev,
-                               "failed to get syscon-pll-ctrl regmap\n");
-                       return PTR_ERR(dss.syscon_pll_ctrl);
-               }
-
-               if (of_property_read_u32_index(np, "syscon-pll-ctrl", 1,
-                               &dss.syscon_pll_ctrl_offset)) {
-                       dev_err(&pdev->dev,
-                               "failed to get syscon-pll-ctrl offset\n");
-                       return -EINVAL;
-               }
-       }
-
-       pll_regulator = devm_regulator_get(&pdev->dev, "vdda_video");
-       if (IS_ERR(pll_regulator)) {
-               r = PTR_ERR(pll_regulator);
-
-               switch (r) {
-               case -ENOENT:
-                       pll_regulator = NULL;
-                       break;
-
-               case -EPROBE_DEFER:
-                       return -EPROBE_DEFER;
-
-               default:
-                       DSSERR("can't get DPLL VDDA regulator\n");
-                       return r;
-               }
-       }
-
-       if (of_property_match_string(np, "reg-names", "pll1") >= 0) {
-               dss.video1_pll = dss_video_pll_init(pdev, 0, pll_regulator);
-               if (IS_ERR(dss.video1_pll)) {
-                       r = PTR_ERR(dss.video1_pll);
-                       goto err_pll_init;
-               }
-       }
-
-       if (of_property_match_string(np, "reg-names", "pll2") >= 0) {
-               dss.video2_pll = dss_video_pll_init(pdev, 1, pll_regulator);
-               if (IS_ERR(dss.video2_pll)) {
-                       r = PTR_ERR(dss.video2_pll);
-                       goto err_pll_init;
-               }
-       }
-
        rev = dss_read_reg(DSS_REVISION);
        printk(KERN_INFO "OMAP DSS rev %d.%d\n",
                        FLD_GET(rev, 7, 4), FLD_GET(rev, 3, 0));
 
        dss_runtime_put();
 
+       r = component_bind_all(&pdev->dev, NULL);
+       if (r)
+               goto err_component;
+
        dss_debugfs_create_file("dss", dss_dump_regs);
 
        pm_set_vt_switch(0);
 
+       dss_initialized = true;
+
        return 0;
 
-err_pll_init:
+err_component:
+err_runtime_get:
+       pm_runtime_disable(&pdev->dev);
+       dss_uninit_ports(pdev);
+err_init_ports:
        if (dss.video1_pll)
                dss_video_pll_uninit(dss.video1_pll);
 
        if (dss.video2_pll)
                dss_video_pll_uninit(dss.video2_pll);
-err_runtime_get:
-       pm_runtime_disable(&pdev->dev);
+err_pll_init:
 err_setup_clocks:
        dss_put_clocks();
        return r;
 }
 
-static int __exit omap_dsshw_remove(struct platform_device *pdev)
+static void dss_unbind(struct device *dev)
 {
+       struct platform_device *pdev = to_platform_device(dev);
+
+       dss_initialized = false;
+
+       component_unbind_all(&pdev->dev, NULL);
+
        if (dss.video1_pll)
                dss_video_pll_uninit(dss.video1_pll);
 
@@ -1169,10 +1208,49 @@ static int __exit omap_dsshw_remove(struct platform_device *pdev)
        pm_runtime_disable(&pdev->dev);
 
        dss_put_clocks();
+}
+
+static const struct component_master_ops dss_component_ops = {
+       .bind = dss_bind,
+       .unbind = dss_unbind,
+};
+
+static int dss_component_compare(struct device *dev, void *data)
+{
+       struct device *child = data;
+       return dev == child;
+}
+
+static int dss_add_child_component(struct device *dev, void *data)
+{
+       struct component_match **match = data;
+
+       component_match_add(dev->parent, match, dss_component_compare, dev);
+
+       return 0;
+}
+
+static int dss_probe(struct platform_device *pdev)
+{
+       struct component_match *match = NULL;
+       int r;
+
+       /* add all the child devices as components */
+       device_for_each_child(&pdev->dev, &match, dss_add_child_component);
+
+       r = component_master_add_with_match(&pdev->dev, &dss_component_ops, match);
+       if (r)
+               return r;
 
        return 0;
 }
 
+static int dss_remove(struct platform_device *pdev)
+{
+       component_master_del(&pdev->dev, &dss_component_ops);
+       return 0;
+}
+
 static int dss_runtime_suspend(struct device *dev)
 {
        dss_save_context();
@@ -1215,7 +1293,8 @@ static const struct of_device_id dss_of_match[] = {
 MODULE_DEVICE_TABLE(of, dss_of_match);
 
 static struct platform_driver omap_dsshw_driver = {
-       .remove         = __exit_p(omap_dsshw_remove),
+       .probe          = dss_probe,
+       .remove         = dss_remove,
        .driver         = {
                .name   = "omapdss_dss",
                .pm     = &dss_pm_ops,
@@ -1226,7 +1305,7 @@ static struct platform_driver omap_dsshw_driver = {
 
 int __init dss_init_platform_driver(void)
 {
-       return platform_driver_probe(&omap_dsshw_driver, omap_dsshw_probe);
+       return platform_driver_register(&omap_dsshw_driver);
 }
 
 void dss_uninit_platform_driver(void)
index 4812eee2622a5dcdb07285533b7758db7cb5b013..2406bcdb831a3db1f20bb25f927bfee6fbaed241 100644 (file)
@@ -309,18 +309,18 @@ bool dss_div_calc(unsigned long pck, unsigned long fck_min,
 
 /* SDI */
 int sdi_init_platform_driver(void) __init;
-void sdi_uninit_platform_driver(void) __exit;
+void sdi_uninit_platform_driver(void);
 
 #ifdef CONFIG_OMAP2_DSS_SDI
-int sdi_init_port(struct platform_device *pdev, struct device_node *port) __init;
-void sdi_uninit_port(struct device_node *port) __exit;
+int sdi_init_port(struct platform_device *pdev, struct device_node *port);
+void sdi_uninit_port(struct device_node *port);
 #else
-static inline int __init sdi_init_port(struct platform_device *pdev,
+static inline int sdi_init_port(struct platform_device *pdev,
                struct device_node *port)
 {
        return 0;
 }
-static inline void __exit sdi_uninit_port(struct device_node *port)
+static inline void sdi_uninit_port(struct device_node *port)
 {
 }
 #endif
@@ -333,7 +333,7 @@ struct dentry;
 struct file_operations;
 
 int dsi_init_platform_driver(void) __init;
-void dsi_uninit_platform_driver(void) __exit;
+void dsi_uninit_platform_driver(void);
 
 void dsi_dump_clocks(struct seq_file *s);
 
@@ -350,25 +350,25 @@ static inline u8 dsi_get_pixel_size(enum omap_dss_dsi_pixel_format fmt)
 
 /* DPI */
 int dpi_init_platform_driver(void) __init;
-void dpi_uninit_platform_driver(void) __exit;
+void dpi_uninit_platform_driver(void);
 
 #ifdef CONFIG_OMAP2_DSS_DPI
-int dpi_init_port(struct platform_device *pdev, struct device_node *port) __init;
-void dpi_uninit_port(struct device_node *port) __exit;
+int dpi_init_port(struct platform_device *pdev, struct device_node *port);
+void dpi_uninit_port(struct device_node *port);
 #else
-static inline int __init dpi_init_port(struct platform_device *pdev,
+static inline int dpi_init_port(struct platform_device *pdev,
                struct device_node *port)
 {
        return 0;
 }
-static inline void __exit dpi_uninit_port(struct device_node *port)
+static inline void dpi_uninit_port(struct device_node *port)
 {
 }
 #endif
 
 /* DISPC */
 int dispc_init_platform_driver(void) __init;
-void dispc_uninit_platform_driver(void) __exit;
+void dispc_uninit_platform_driver(void);
 void dispc_dump_clocks(struct seq_file *s);
 
 void dispc_enable_sidle(void);
@@ -418,18 +418,18 @@ int dispc_wb_setup(const struct omap_dss_writeback_info *wi,
 
 /* VENC */
 int venc_init_platform_driver(void) __init;
-void venc_uninit_platform_driver(void) __exit;
+void venc_uninit_platform_driver(void);
 
 /* HDMI */
 int hdmi4_init_platform_driver(void) __init;
-void hdmi4_uninit_platform_driver(void) __exit;
+void hdmi4_uninit_platform_driver(void);
 
 int hdmi5_init_platform_driver(void) __init;
-void hdmi5_uninit_platform_driver(void) __exit;
+void hdmi5_uninit_platform_driver(void);
 
 /* RFBI */
 int rfbi_init_platform_driver(void) __init;
-void rfbi_uninit_platform_driver(void) __exit;
+void rfbi_uninit_platform_driver(void);
 
 
 #ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS
index 916d47978f4118f95e76b337e2fb1df8d0298f1c..6d3aa3f51c205704a41e0506149ec9f81154b074 100644 (file)
@@ -32,6 +32,7 @@
 #include <linux/clk.h>
 #include <linux/gpio.h>
 #include <linux/regulator/consumer.h>
+#include <linux/component.h>
 #include <video/omapdss.h>
 #include <sound/omap-hdmi-audio.h>
 
@@ -229,9 +230,9 @@ static int hdmi_power_on_full(struct omap_dss_device *dssdev)
 err_mgr_enable:
        hdmi_wp_video_stop(&hdmi.wp);
 err_vid_enable:
-err_phy_cfg:
        hdmi_wp_set_phy_pwr(&hdmi.wp, HDMI_PHYPWRCMD_OFF);
 err_phy_pwr:
+err_phy_cfg:
 err_pll_cfg:
        dss_pll_disable(&hdmi.pll.pll);
 err_pll_enable:
@@ -646,8 +647,9 @@ static int hdmi_audio_register(struct device *dev)
 }
 
 /* HDMI HW IP initialisation */
-static int omapdss_hdmihw_probe(struct platform_device *pdev)
+static int hdmi4_bind(struct device *dev, struct device *master, void *data)
 {
+       struct platform_device *pdev = to_platform_device(dev);
        int r;
        int irq;
 
@@ -713,8 +715,10 @@ err:
        return r;
 }
 
-static int __exit omapdss_hdmihw_remove(struct platform_device *pdev)
+static void hdmi4_unbind(struct device *dev, struct device *master, void *data)
 {
+       struct platform_device *pdev = to_platform_device(dev);
+
        if (hdmi.audio_pdev)
                platform_device_unregister(hdmi.audio_pdev);
 
@@ -723,7 +727,21 @@ static int __exit omapdss_hdmihw_remove(struct platform_device *pdev)
        hdmi_pll_uninit(&hdmi.pll);
 
        pm_runtime_disable(&pdev->dev);
+}
+
+static const struct component_ops hdmi4_component_ops = {
+       .bind   = hdmi4_bind,
+       .unbind = hdmi4_unbind,
+};
 
+static int hdmi4_probe(struct platform_device *pdev)
+{
+       return component_add(&pdev->dev, &hdmi4_component_ops);
+}
+
+static int hdmi4_remove(struct platform_device *pdev)
+{
+       component_del(&pdev->dev, &hdmi4_component_ops);
        return 0;
 }
 
@@ -756,8 +774,8 @@ static const struct of_device_id hdmi_of_match[] = {
 };
 
 static struct platform_driver omapdss_hdmihw_driver = {
-       .probe          = omapdss_hdmihw_probe,
-       .remove         = __exit_p(omapdss_hdmihw_remove),
+       .probe          = hdmi4_probe,
+       .remove         = hdmi4_remove,
        .driver         = {
                .name   = "omapdss_hdmi",
                .pm     = &hdmi_pm_ops,
@@ -771,7 +789,7 @@ int __init hdmi4_init_platform_driver(void)
        return platform_driver_register(&omapdss_hdmihw_driver);
 }
 
-void __exit hdmi4_uninit_platform_driver(void)
+void hdmi4_uninit_platform_driver(void)
 {
        platform_driver_unregister(&omapdss_hdmihw_driver);
 }
index 7eafea5b8e190c15f03a51e12ca28ad830acc647..fa72e735dad2f995c127a265cd6b82a43bf9c73c 100644 (file)
@@ -654,6 +654,13 @@ static void hdmi_core_audio_infoframe_cfg(struct hdmi_core_data *core,
        hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_DBYTE(2), info_aud->db3);
        sum += info_aud->db3;
 
+       /*
+        * The OMAP HDMI IP requires to use the 8-channel channel code when
+        * transmitting more than two channels.
+        */
+       if (info_aud->db4_ca != 0x00)
+               info_aud->db4_ca = 0x13;
+
        hdmi_write_reg(av_base, HDMI_CORE_AV_AUD_DBYTE(3), info_aud->db4_ca);
        sum += info_aud->db4_ca;
 
@@ -795,7 +802,9 @@ int hdmi4_audio_config(struct hdmi_core_data *core, struct hdmi_wp_data *wp,
 
        /*
         * the HDMI IP needs to enable four stereo channels when transmitting
-        * more than 2 audio channels
+        * more than 2 audio channels.  Similarly, the channel count in the
+        * Audio InfoFrame has to match the sample_present bits (some channels
+        * are padded with zeroes)
         */
        if (channel_count == 2) {
                audio_format.stereo_channels = HDMI_AUDIO_STEREO_ONECHANNEL;
@@ -807,6 +816,7 @@ int hdmi4_audio_config(struct hdmi_core_data *core, struct hdmi_wp_data *wp,
                                HDMI_AUDIO_I2S_SD1_EN | HDMI_AUDIO_I2S_SD2_EN |
                                HDMI_AUDIO_I2S_SD3_EN;
                acore.layout = HDMI_AUDIO_LAYOUT_8CH;
+               audio->cea->db1_ct_cc = 7;
        }
 
        acore.en_spdif = false;
index 3f0b34a7031adc85bea15eb87fde380645d5f498..7f875788edbc9f97e0fec729fb7122e2164dcfa3 100644 (file)
@@ -37,6 +37,7 @@
 #include <linux/clk.h>
 #include <linux/gpio.h>
 #include <linux/regulator/consumer.h>
+#include <linux/component.h>
 #include <video/omapdss.h>
 #include <sound/omap-hdmi-audio.h>
 
@@ -681,8 +682,9 @@ static int hdmi_audio_register(struct device *dev)
 }
 
 /* HDMI HW IP initialisation */
-static int omapdss_hdmihw_probe(struct platform_device *pdev)
+static int hdmi5_bind(struct device *dev, struct device *master, void *data)
 {
+       struct platform_device *pdev = to_platform_device(dev);
        int r;
        int irq;
 
@@ -748,8 +750,10 @@ err:
        return r;
 }
 
-static int __exit omapdss_hdmihw_remove(struct platform_device *pdev)
+static void hdmi5_unbind(struct device *dev, struct device *master, void *data)
 {
+       struct platform_device *pdev = to_platform_device(dev);
+
        if (hdmi.audio_pdev)
                platform_device_unregister(hdmi.audio_pdev);
 
@@ -758,7 +762,21 @@ static int __exit omapdss_hdmihw_remove(struct platform_device *pdev)
        hdmi_pll_uninit(&hdmi.pll);
 
        pm_runtime_disable(&pdev->dev);
+}
+
+static const struct component_ops hdmi5_component_ops = {
+       .bind   = hdmi5_bind,
+       .unbind = hdmi5_unbind,
+};
 
+static int hdmi5_probe(struct platform_device *pdev)
+{
+       return component_add(&pdev->dev, &hdmi5_component_ops);
+}
+
+static int hdmi5_remove(struct platform_device *pdev)
+{
+       component_del(&pdev->dev, &hdmi5_component_ops);
        return 0;
 }
 
@@ -792,8 +810,8 @@ static const struct of_device_id hdmi_of_match[] = {
 };
 
 static struct platform_driver omapdss_hdmihw_driver = {
-       .probe          = omapdss_hdmihw_probe,
-       .remove         = __exit_p(omapdss_hdmihw_remove),
+       .probe          = hdmi5_probe,
+       .remove         = hdmi5_remove,
        .driver         = {
                .name   = "omapdss_hdmi5",
                .pm     = &hdmi_pm_ops,
@@ -807,7 +825,7 @@ int __init hdmi5_init_platform_driver(void)
        return platform_driver_register(&omapdss_hdmihw_driver);
 }
 
-void __exit hdmi5_uninit_platform_driver(void)
+void hdmi5_uninit_platform_driver(void)
 {
        platform_driver_unregister(&omapdss_hdmihw_driver);
 }
index bfc0c4c297d69b9c16bcb16eba56d7d734f8ccf0..8ea531d2652c4cd96fcefab1f6fec938494ebc71 100644 (file)
@@ -790,7 +790,9 @@ static void hdmi5_core_audio_infoframe_cfg(struct hdmi_core_data *core,
 
        hdmi_write_reg(base, HDMI_CORE_FC_AUDICONF1, info_aud->db2_sf_ss);
        hdmi_write_reg(base, HDMI_CORE_FC_AUDICONF2, info_aud->db4_ca);
-       hdmi_write_reg(base, HDMI_CORE_FC_AUDICONF3, info_aud->db5_dminh_lsv);
+       hdmi_write_reg(base, HDMI_CORE_FC_AUDICONF3,
+         (info_aud->db5_dminh_lsv & CEA861_AUDIO_INFOFRAME_DB5_DM_INH) >> 3 |
+         (info_aud->db5_dminh_lsv & CEA861_AUDIO_INFOFRAME_DB5_LSV));
 }
 
 int hdmi5_audio_config(struct hdmi_core_data *core, struct hdmi_wp_data *wp,
@@ -870,6 +872,7 @@ int hdmi5_audio_config(struct hdmi_core_data *core, struct hdmi_wp_data *wp,
        audio_format.samples_per_word = HDMI_AUDIO_ONEWORD_TWOSAMPLES;
        audio_format.sample_size = HDMI_AUDIO_SAMPLE_16BITS;
        audio_format.justification = HDMI_AUDIO_JUSTIFY_LEFT;
+       audio_format.sample_order = HDMI_AUDIO_SAMPLE_LEFT_FIRST;
 
        /* only LPCM atm */
        audio_format.type = HDMI_AUDIO_TYPE_LPCM;
index c15377e242cc44244b90f517c63c78fcf9412c62..7c544bc56fb5d4400b4d7c32a96809ce6a2dd2a1 100644 (file)
@@ -110,7 +110,23 @@ int hdmi_wp_video_start(struct hdmi_wp_data *wp)
 
 void hdmi_wp_video_stop(struct hdmi_wp_data *wp)
 {
+       int i;
+
+       hdmi_write_reg(wp->base, HDMI_WP_IRQSTATUS, HDMI_IRQ_VIDEO_FRAME_DONE);
+
        REG_FLD_MOD(wp->base, HDMI_WP_VIDEO_CFG, false, 31, 31);
+
+       for (i = 0; i < 50; ++i) {
+               u32 v;
+
+               msleep(20);
+
+               v = hdmi_read_reg(wp->base, HDMI_WP_IRQSTATUS_RAW);
+               if (v & HDMI_IRQ_VIDEO_FRAME_DONE)
+                       return;
+       }
+
+       DSSERR("no HDMI FRAMEDONE when disabling output\n");
 }
 
 void hdmi_wp_video_config_format(struct hdmi_wp_data *wp,
index 065effca9236b2845658b8ed007143fc4f1b2367..1525a494d057a552ba86b900c43afbd253c0f883 100644 (file)
@@ -36,6 +36,7 @@
 #include <linux/semaphore.h>
 #include <linux/platform_device.h>
 #include <linux/pm_runtime.h>
+#include <linux/component.h>
 
 #include <video/omapdss.h>
 #include "dss.h"
@@ -938,7 +939,7 @@ static void rfbi_init_output(struct platform_device *pdev)
        omapdss_register_output(out);
 }
 
-static void __exit rfbi_uninit_output(struct platform_device *pdev)
+static void rfbi_uninit_output(struct platform_device *pdev)
 {
        struct omap_dss_device *out = &rfbi.output;
 
@@ -946,8 +947,9 @@ static void __exit rfbi_uninit_output(struct platform_device *pdev)
 }
 
 /* RFBI HW IP initialisation */
-static int omap_rfbihw_probe(struct platform_device *pdev)
+static int rfbi_bind(struct device *dev, struct device *master, void *data)
 {
+       struct platform_device *pdev = to_platform_device(dev);
        u32 rev;
        struct resource *rfbi_mem;
        struct clk *clk;
@@ -1005,8 +1007,10 @@ err_runtime_get:
        return r;
 }
 
-static int __exit omap_rfbihw_remove(struct platform_device *pdev)
+static void rfbi_unbind(struct device *dev, struct device *master, void *data)
 {
+       struct platform_device *pdev = to_platform_device(dev);
+
        rfbi_uninit_output(pdev);
 
        pm_runtime_disable(&pdev->dev);
@@ -1014,6 +1018,22 @@ static int __exit omap_rfbihw_remove(struct platform_device *pdev)
        return 0;
 }
 
+static const struct component_ops rfbi_component_ops = {
+       .bind   = rfbi_bind,
+       .unbind = rfbi_unbind,
+};
+
+static int rfbi_probe(struct platform_device *pdev)
+{
+       return component_add(&pdev->dev, &rfbi_component_ops);
+}
+
+static int rfbi_remove(struct platform_device *pdev)
+{
+       component_del(&pdev->dev, &rfbi_component_ops);
+       return 0;
+}
+
 static int rfbi_runtime_suspend(struct device *dev)
 {
        dispc_runtime_put();
@@ -1038,8 +1058,8 @@ static const struct dev_pm_ops rfbi_pm_ops = {
 };
 
 static struct platform_driver omap_rfbihw_driver = {
-       .probe          = omap_rfbihw_probe,
-       .remove         = __exit_p(omap_rfbihw_remove),
+       .probe          = rfbi_probe,
+       .remove         = rfbi_remove,
        .driver         = {
                .name   = "omapdss_rfbi",
                .pm     = &rfbi_pm_ops,
@@ -1052,7 +1072,7 @@ int __init rfbi_init_platform_driver(void)
        return platform_driver_register(&omap_rfbihw_driver);
 }
 
-void __exit rfbi_uninit_platform_driver(void)
+void rfbi_uninit_platform_driver(void)
 {
        platform_driver_unregister(&omap_rfbihw_driver);
 }
index 5c2ccab5a958f6d41a68cbab3a48e78e8514cbda..5843580a1deb89c115c4a44939069776e8349f2f 100644 (file)
@@ -27,6 +27,7 @@
 #include <linux/platform_device.h>
 #include <linux/string.h>
 #include <linux/of.h>
+#include <linux/component.h>
 
 #include <video/omapdss.h>
 #include "dss.h"
@@ -350,15 +351,17 @@ static void sdi_init_output(struct platform_device *pdev)
        omapdss_register_output(out);
 }
 
-static void __exit sdi_uninit_output(struct platform_device *pdev)
+static void sdi_uninit_output(struct platform_device *pdev)
 {
        struct omap_dss_device *out = &sdi.output;
 
        omapdss_unregister_output(out);
 }
 
-static int omap_sdi_probe(struct platform_device *pdev)
+static int sdi_bind(struct device *dev, struct device *master, void *data)
 {
+       struct platform_device *pdev = to_platform_device(dev);
+
        sdi.pdev = pdev;
 
        sdi_init_output(pdev);
@@ -366,16 +369,32 @@ static int omap_sdi_probe(struct platform_device *pdev)
        return 0;
 }
 
-static int __exit omap_sdi_remove(struct platform_device *pdev)
+static void sdi_unbind(struct device *dev, struct device *master, void *data)
 {
+       struct platform_device *pdev = to_platform_device(dev);
+
        sdi_uninit_output(pdev);
+}
+
+static const struct component_ops sdi_component_ops = {
+       .bind   = sdi_bind,
+       .unbind = sdi_unbind,
+};
 
+static int sdi_probe(struct platform_device *pdev)
+{
+       return component_add(&pdev->dev, &sdi_component_ops);
+}
+
+static int sdi_remove(struct platform_device *pdev)
+{
+       component_del(&pdev->dev, &sdi_component_ops);
        return 0;
 }
 
 static struct platform_driver omap_sdi_driver = {
-       .probe          = omap_sdi_probe,
-       .remove         = __exit_p(omap_sdi_remove),
+       .probe          = sdi_probe,
+       .remove         = sdi_remove,
        .driver         = {
                .name   = "omapdss_sdi",
                .suppress_bind_attrs = true,
@@ -387,12 +406,12 @@ int __init sdi_init_platform_driver(void)
        return platform_driver_register(&omap_sdi_driver);
 }
 
-void __exit sdi_uninit_platform_driver(void)
+void sdi_uninit_platform_driver(void)
 {
        platform_driver_unregister(&omap_sdi_driver);
 }
 
-int __init sdi_init_port(struct platform_device *pdev, struct device_node *port)
+int sdi_init_port(struct platform_device *pdev, struct device_node *port)
 {
        struct device_node *ep;
        u32 datapairs;
@@ -426,7 +445,7 @@ err_datapairs:
        return r;
 }
 
-void __exit sdi_uninit_port(struct device_node *port)
+void sdi_uninit_port(struct device_node *port)
 {
        if (!sdi.port_initialized)
                return;
index ef7fd925e7f290645b69fbd6ec1538fa8602ee22..99ca268c1cddf24332308b58fbeb051d565b8dca 100644 (file)
@@ -35,6 +35,7 @@
 #include <linux/regulator/consumer.h>
 #include <linux/pm_runtime.h>
 #include <linux/of.h>
+#include <linux/component.h>
 
 #include <video/omapdss.h>
 
@@ -802,7 +803,7 @@ static void venc_init_output(struct platform_device *pdev)
        omapdss_register_output(out);
 }
 
-static void __exit venc_uninit_output(struct platform_device *pdev)
+static void venc_uninit_output(struct platform_device *pdev)
 {
        struct omap_dss_device *out = &venc.output;
 
@@ -852,8 +853,9 @@ err:
 }
 
 /* VENC HW IP initialisation */
-static int omap_venchw_probe(struct platform_device *pdev)
+static int venc_bind(struct device *dev, struct device *master, void *data)
 {
+       struct platform_device *pdev = to_platform_device(dev);
        u8 rev_id;
        struct resource *venc_mem;
        int r;
@@ -912,12 +914,28 @@ err_runtime_get:
        return r;
 }
 
-static int __exit omap_venchw_remove(struct platform_device *pdev)
+static void venc_unbind(struct device *dev, struct device *master, void *data)
 {
+       struct platform_device *pdev = to_platform_device(dev);
+
        venc_uninit_output(pdev);
 
        pm_runtime_disable(&pdev->dev);
+}
 
+static const struct component_ops venc_component_ops = {
+       .bind   = venc_bind,
+       .unbind = venc_unbind,
+};
+
+static int venc_probe(struct platform_device *pdev)
+{
+       return component_add(&pdev->dev, &venc_component_ops);
+}
+
+static int venc_remove(struct platform_device *pdev)
+{
+       component_del(&pdev->dev, &venc_component_ops);
        return 0;
 }
 
@@ -950,7 +968,6 @@ static const struct dev_pm_ops venc_pm_ops = {
        .runtime_resume = venc_runtime_resume,
 };
 
-
 static const struct of_device_id venc_of_match[] = {
        { .compatible = "ti,omap2-venc", },
        { .compatible = "ti,omap3-venc", },
@@ -959,8 +976,8 @@ static const struct of_device_id venc_of_match[] = {
 };
 
 static struct platform_driver omap_venchw_driver = {
-       .probe          = omap_venchw_probe,
-       .remove         = __exit_p(omap_venchw_remove),
+       .probe          = venc_probe,
+       .remove         = venc_remove,
        .driver         = {
                .name   = "omapdss_venc",
                .pm     = &venc_pm_ops,
@@ -974,7 +991,7 @@ int __init venc_init_platform_driver(void)
        return platform_driver_register(&omap_venchw_driver);
 }
 
-void __exit venc_uninit_platform_driver(void)
+void venc_uninit_platform_driver(void)
 {
        platform_driver_unregister(&omap_venchw_driver);
 }
index 3b85b647bc10f97e004974ab37f360c63d50ba2d..aa8d28880912dc571cdfe7763a8edd2223a28065 100644 (file)
 #include <linux/fb.h>
 #include <linux/init.h>
 #include <linux/pci.h>
-#ifdef CONFIG_MTRR
-#include <asm/mtrr.h>
-#endif
-
 #include <video/permedia2.h>
 #include <video/cvisionppc.h>
 
@@ -81,10 +77,7 @@ static char *mode_option;
 static bool lowhsync;
 static bool lowvsync;
 static bool noaccel;
-/* mtrr option */
-#ifdef CONFIG_MTRR
 static bool nomtrr;
-#endif
 
 /*
  * The hardware state of the graphics card that isn't part of the
@@ -100,7 +93,7 @@ struct pm2fb_par
        u32             mem_control;    /* MemControl reg at probe */
        u32             boot_address;   /* BootAddress reg at probe */
        u32             palette[16];
-       int             mtrr_handle;
+       int             wc_cookie;
 };
 
 /*
@@ -1637,21 +1630,16 @@ static int pm2fb_probe(struct pci_dev *pdev, const struct pci_device_id *id)
                goto err_exit_mmio;
        }
        info->screen_base =
-               ioremap_nocache(pm2fb_fix.smem_start, pm2fb_fix.smem_len);
+               ioremap_wc(pm2fb_fix.smem_start, pm2fb_fix.smem_len);
        if (!info->screen_base) {
                printk(KERN_WARNING "pm2fb: Can't ioremap smem area.\n");
                release_mem_region(pm2fb_fix.smem_start, pm2fb_fix.smem_len);
                goto err_exit_mmio;
        }
 
-#ifdef CONFIG_MTRR
-       default_par->mtrr_handle = -1;
        if (!nomtrr)
-               default_par->mtrr_handle =
-                       mtrr_add(pm2fb_fix.smem_start,
-                                pm2fb_fix.smem_len,
-                                MTRR_TYPE_WRCOMB, 1);
-#endif
+               default_par->wc_cookie = arch_phys_wc_add(pm2fb_fix.smem_start,
+                                                         pm2fb_fix.smem_len);
 
        info->fbops             = &pm2fb_ops;
        info->fix               = pm2fb_fix;
@@ -1733,12 +1721,7 @@ static void pm2fb_remove(struct pci_dev *pdev)
        struct pm2fb_par *par = info->par;
 
        unregister_framebuffer(info);
-
-#ifdef CONFIG_MTRR
-       if (par->mtrr_handle >= 0)
-               mtrr_del(par->mtrr_handle, info->fix.smem_start,
-                        info->fix.smem_len);
-#endif /* CONFIG_MTRR */
+       arch_phys_wc_del(par->wc_cookie);
        iounmap(info->screen_base);
        release_mem_region(fix->smem_start, fix->smem_len);
        iounmap(par->v_regs);
@@ -1791,10 +1774,8 @@ static int __init pm2fb_setup(char *options)
                        lowvsync = 1;
                else if (!strncmp(this_opt, "hwcursor=", 9))
                        hwcursor = simple_strtoul(this_opt + 9, NULL, 0);
-#ifdef CONFIG_MTRR
                else if (!strncmp(this_opt, "nomtrr", 6))
                        nomtrr = 1;
-#endif
                else if (!strncmp(this_opt, "noaccel", 7))
                        noaccel = 1;
                else
@@ -1847,10 +1828,8 @@ MODULE_PARM_DESC(noaccel, "Disable acceleration");
 module_param(hwcursor, int, 0644);
 MODULE_PARM_DESC(hwcursor, "Enable hardware cursor "
                        "(1=enable, 0=disable, default=1)");
-#ifdef CONFIG_MTRR
 module_param(nomtrr, bool, 0);
 MODULE_PARM_DESC(nomtrr, "Disable MTRR support (0 or 1=disabled) (default=0)");
-#endif
 
 MODULE_AUTHOR("Jim Hague <jim.hague@acm.org>");
 MODULE_DESCRIPTION("Permedia2 framebuffer device driver");
index 77b99ed39ad0935856398a1921b626e64e069a50..6ff5077a2e15ba2d5da0732dca85d259c22d23f3 100644 (file)
@@ -32,9 +32,6 @@
 #include <linux/fb.h>
 #include <linux/init.h>
 #include <linux/pci.h>
-#ifdef CONFIG_MTRR
-#include <asm/mtrr.h>
-#endif
 
 #include <video/pm3fb.h>
 
 static int hwcursor = 1;
 static char *mode_option;
 static bool noaccel;
-
-/* mtrr option */
-#ifdef CONFIG_MTRR
 static bool nomtrr;
-#endif
 
 /*
  * This structure defines the hardware state of the graphics card. Normally
@@ -76,7 +69,7 @@ struct pm3_par {
        u32             video;          /* video flags before blanking */
        u32             base;           /* screen base in 128 bits unit */
        u32             palette[16];
-       int             mtrr_handle;
+       int             wc_cookie;
 };
 
 /*
@@ -1374,8 +1367,8 @@ static int pm3fb_probe(struct pci_dev *dev, const struct pci_device_id *ent)
                printk(KERN_WARNING "pm3fb: Can't reserve smem.\n");
                goto err_exit_mmio;
        }
-       info->screen_base =
-               ioremap_nocache(pm3fb_fix.smem_start, pm3fb_fix.smem_len);
+       info->screen_base = ioremap_wc(pm3fb_fix.smem_start,
+                                      pm3fb_fix.smem_len);
        if (!info->screen_base) {
                printk(KERN_WARNING "pm3fb: Can't ioremap smem area.\n");
                release_mem_region(pm3fb_fix.smem_start, pm3fb_fix.smem_len);
@@ -1383,12 +1376,9 @@ static int pm3fb_probe(struct pci_dev *dev, const struct pci_device_id *ent)
        }
        info->screen_size = pm3fb_fix.smem_len;
 
-#ifdef CONFIG_MTRR
        if (!nomtrr)
-               par->mtrr_handle = mtrr_add(pm3fb_fix.smem_start,
-                                               pm3fb_fix.smem_len,
-                                               MTRR_TYPE_WRCOMB, 1);
-#endif
+               par->wc_cookie = arch_phys_wc_add(pm3fb_fix.smem_start,
+                                                 pm3fb_fix.smem_len);
        info->fbops = &pm3fb_ops;
 
        par->video = PM3_READ_REG(par, PM3VideoControl);
@@ -1478,11 +1468,7 @@ static void pm3fb_remove(struct pci_dev *dev)
                unregister_framebuffer(info);
                fb_dealloc_cmap(&info->cmap);
 
-#ifdef CONFIG_MTRR
-               if (par->mtrr_handle >= 0)
-                       mtrr_del(par->mtrr_handle, info->fix.smem_start,
-                                info->fix.smem_len);
-#endif /* CONFIG_MTRR */
+               arch_phys_wc_del(par->wc_cookie);
                iounmap(info->screen_base);
                release_mem_region(fix->smem_start, fix->smem_len);
                iounmap(par->v_regs);
@@ -1533,10 +1519,8 @@ static int __init pm3fb_setup(char *options)
                        noaccel = 1;
                else if (!strncmp(this_opt, "hwcursor=", 9))
                        hwcursor = simple_strtoul(this_opt + 9, NULL, 0);
-#ifdef CONFIG_MTRR
                else if (!strncmp(this_opt, "nomtrr", 6))
                        nomtrr = 1;
-#endif
                else
                        mode_option = this_opt;
        }
@@ -1577,10 +1561,8 @@ MODULE_PARM_DESC(noaccel, "Disable acceleration");
 module_param(hwcursor, int, 0644);
 MODULE_PARM_DESC(hwcursor, "Enable hardware cursor "
                        "(1=enable, 0=disable, default=1)");
-#ifdef CONFIG_MTRR
 module_param(nomtrr, bool, 0);
 MODULE_PARM_DESC(nomtrr, "Disable MTRR support (0 or 1=disabled) (default=0)");
-#endif
 
 MODULE_DESCRIPTION("Permedia3 framebuffer device driver");
 MODULE_LICENSE("GPL");
index 294a80908c8cd48f7fe9fe5f93e2723bcb817bb1..f1ad2747064bf9c70f410d99e552efa3a513c54c 100644 (file)
@@ -41,9 +41,6 @@
 #include <linux/pci.h>
 #include <linux/backlight.h>
 #include <linux/bitrev.h>
-#ifdef CONFIG_MTRR
-#include <asm/mtrr.h>
-#endif
 #ifdef CONFIG_PMAC_BACKLIGHT
 #include <asm/machdep.h>
 #include <asm/backlight.h>
@@ -204,9 +201,7 @@ MODULE_DEVICE_TABLE(pci, rivafb_pci_tbl);
 static int flatpanel = -1; /* Autodetect later */
 static int forceCRTC = -1;
 static bool noaccel  = 0;
-#ifdef CONFIG_MTRR
 static bool nomtrr = 0;
-#endif
 #ifdef CONFIG_PMAC_BACKLIGHT
 static int backlight = 1;
 #else
@@ -2010,28 +2005,18 @@ static int rivafb_probe(struct pci_dev *pd, const struct pci_device_id *ent)
 
        rivafb_fix.smem_len = riva_get_memlen(default_par) * 1024;
        default_par->dclk_max = riva_get_maxdclk(default_par) * 1000;
-       info->screen_base = ioremap(rivafb_fix.smem_start,
-                                   rivafb_fix.smem_len);
+       info->screen_base = ioremap_wc(rivafb_fix.smem_start,
+                                      rivafb_fix.smem_len);
        if (!info->screen_base) {
                printk(KERN_ERR PFX "cannot ioremap FB base\n");
                ret = -EIO;
                goto err_iounmap_pramin;
        }
 
-#ifdef CONFIG_MTRR
-       if (!nomtrr) {
-               default_par->mtrr.vram = mtrr_add(rivafb_fix.smem_start,
-                                                 rivafb_fix.smem_len,
-                                                 MTRR_TYPE_WRCOMB, 1);
-               if (default_par->mtrr.vram < 0) {
-                       printk(KERN_ERR PFX "unable to setup MTRR\n");
-               } else {
-                       default_par->mtrr.vram_valid = 1;
-                       /* let there be speed */
-                       printk(KERN_INFO PFX "RIVA MTRR set to ON\n");
-               }
-       }
-#endif /* CONFIG_MTRR */
+       if (!nomtrr)
+               default_par->wc_cookie =
+                       arch_phys_wc_add(rivafb_fix.smem_start,
+                                        rivafb_fix.smem_len);
 
        info->fbops = &riva_fb_ops;
        info->fix = rivafb_fix;
@@ -2105,13 +2090,7 @@ static void rivafb_remove(struct pci_dev *pd)
        unregister_framebuffer(info);
 
        riva_bl_exit(info);
-
-#ifdef CONFIG_MTRR
-       if (par->mtrr.vram_valid)
-               mtrr_del(par->mtrr.vram, info->fix.smem_start,
-                        info->fix.smem_len);
-#endif /* CONFIG_MTRR */
-
+       arch_phys_wc_del(par->wc_cookie);
        iounmap(par->ctrl_base);
        iounmap(info->screen_base);
        if (par->riva.Architecture == NV_ARCH_03)
@@ -2150,10 +2129,8 @@ static int rivafb_setup(char *options)
                        flatpanel = 1;
                } else if (!strncmp(this_opt, "backlight:", 10)) {
                        backlight = simple_strtoul(this_opt+10, NULL, 0);
-#ifdef CONFIG_MTRR
                } else if (!strncmp(this_opt, "nomtrr", 6)) {
                        nomtrr = 1;
-#endif
                } else if (!strncmp(this_opt, "strictmode", 10)) {
                        strictmode = 1;
                } else if (!strncmp(this_opt, "noaccel", 7)) {
@@ -2209,10 +2186,8 @@ module_param(flatpanel, int, 0);
 MODULE_PARM_DESC(flatpanel, "Enables experimental flat panel support for some chipsets. (0 or 1=enabled) (default=0)");
 module_param(forceCRTC, int, 0);
 MODULE_PARM_DESC(forceCRTC, "Forces usage of a particular CRTC in case autodetection fails. (0 or 1) (default=autodetect)");
-#ifdef CONFIG_MTRR
 module_param(nomtrr, bool, 0);
 MODULE_PARM_DESC(nomtrr, "Disables MTRR support (0 or 1=disabled) (default=0)");
-#endif
 module_param(strictmode, bool, 0);
 MODULE_PARM_DESC(strictmode, "Only use video modes from EDID");
 
index d9f107b704c6296feb52ee2ea83aa2aa11a1b9a4..61fd37ca490af9b6e8f42731c6a8691412eb69b9 100644 (file)
@@ -61,9 +61,7 @@ struct riva_par {
        int FlatPanel;
        struct pci_dev *pdev;
        int cursor_reset;
-#ifdef CONFIG_MTRR
-       struct { int vram; int vram_valid; } mtrr;
-#endif
+       int wc_cookie;
        struct riva_i2c_chan chan[3];
 };
 
index 8ff4ab1cb69b0f40143b17426c2702c52c420bf2..aba04afe712d02afdddc3c2dc7d1b736e2a7ae2e 100644 (file)
@@ -213,9 +213,7 @@ struct savagefb_par {
                void   __iomem *vbase;
                u32    pbase;
                u32    len;
-#ifdef CONFIG_MTRR
-               int    mtrr;
-#endif
+               int    wc_cookie;
        } video;
 
        struct {
index 4dbf45f3b21a3d6b9bc7287d23bf305f27a0de16..6c77ab09b0b202db95718c67cc2ecbd415f8f1bc 100644 (file)
 #include <asm/irq.h>
 #include <asm/pgtable.h>
 
-#ifdef CONFIG_MTRR
-#include <asm/mtrr.h>
-#endif
-
 #include "savagefb.h"
 
 
@@ -1775,7 +1771,7 @@ static int savage_map_video(struct fb_info *info, int video_len)
 
        par->video.pbase = pci_resource_start(par->pcidev, resource);
        par->video.len   = video_len;
-       par->video.vbase = ioremap(par->video.pbase, par->video.len);
+       par->video.vbase = ioremap_wc(par->video.pbase, par->video.len);
 
        if (!par->video.vbase) {
                printk("savagefb: unable to map screen memory\n");
@@ -1787,11 +1783,7 @@ static int savage_map_video(struct fb_info *info, int video_len)
        info->fix.smem_start = par->video.pbase;
        info->fix.smem_len   = par->video.len - par->cob_size;
        info->screen_base    = par->video.vbase;
-
-#ifdef CONFIG_MTRR
-       par->video.mtrr = mtrr_add(par->video.pbase, video_len,
-                                  MTRR_TYPE_WRCOMB, 1);
-#endif
+       par->video.wc_cookie = arch_phys_wc_add(par->video.pbase, video_len);
 
        /* Clear framebuffer, it's all white in memory after boot */
        memset_io(par->video.vbase, 0, par->video.len);
@@ -1806,10 +1798,7 @@ static void savage_unmap_video(struct fb_info *info)
        DBG("savage_unmap_video");
 
        if (par->video.vbase) {
-#ifdef CONFIG_MTRR
-               mtrr_del(par->video.mtrr, par->video.pbase, par->video.len);
-#endif
-
+               arch_phys_wc_del(par->video.wc_cookie);
                iounmap(par->video.vbase);
                par->video.vbase = NULL;
                info->screen_base = NULL;
index 1987f1b7212f40732c2c28315d3791b3d08d2652..ea1d1c9640bfbf187e5b7c1fd97300b70962846f 100644 (file)
@@ -458,7 +458,7 @@ struct sis_video_info {
 
        unsigned char   *bios_abase;
 
-       int             mtrr;
+       int             wc_cookie;
 
        u32             sisfb_mem;
 
index fcf610edf217b83a18f0216deba34fb4c416c835..e92303823a4b083987090920011c79bb7b45c001 100644 (file)
@@ -53,9 +53,6 @@
 #include <linux/types.h>
 #include <linux/uaccess.h>
 #include <asm/io.h>
-#ifdef CONFIG_MTRR
-#include <asm/mtrr.h>
-#endif
 
 #include "sis.h"
 #include "sis_main.h"
@@ -4130,13 +4127,13 @@ static void sisfb_post_map_vram(struct sis_video_info *ivideo,
        if (*mapsize < (min << 20))
                return;
 
-       ivideo->video_vbase = ioremap(ivideo->video_base, (*mapsize));
+       ivideo->video_vbase = ioremap_wc(ivideo->video_base, (*mapsize));
 
        if(!ivideo->video_vbase) {
                printk(KERN_ERR
                        "sisfb: Unable to map maximum video RAM for size detection\n");
                (*mapsize) >>= 1;
-               while((!(ivideo->video_vbase = ioremap(ivideo->video_base, (*mapsize))))) {
+               while((!(ivideo->video_vbase = ioremap_wc(ivideo->video_base, (*mapsize))))) {
                        (*mapsize) >>= 1;
                        if((*mapsize) < (min << 20))
                                break;
@@ -6186,7 +6183,7 @@ static int sisfb_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
                goto error_2;
        }
 
-       ivideo->video_vbase = ioremap(ivideo->video_base, ivideo->video_size);
+       ivideo->video_vbase = ioremap_wc(ivideo->video_base, ivideo->video_size);
        ivideo->SiS_Pr.VideoMemoryAddress = ivideo->video_vbase;
        if(!ivideo->video_vbase) {
                printk(KERN_ERR "sisfb: Fatal error: Unable to map framebuffer memory\n");
@@ -6254,8 +6251,6 @@ error_3:  vfree(ivideo->bios_abase);
        ivideo->SiS_Pr.VideoMemoryAddress += ivideo->video_offset;
        ivideo->SiS_Pr.VideoMemorySize = ivideo->sisfb_mem;
 
-       ivideo->mtrr = -1;
-
        ivideo->vbflags = 0;
        ivideo->lcddefmodeidx = DEFAULT_LCDMODE;
        ivideo->tvdefmodeidx  = DEFAULT_TVMODE;
@@ -6443,14 +6438,8 @@ error_3: vfree(ivideo->bios_abase);
 
                printk(KERN_DEBUG "sisfb: Initial vbflags 0x%x\n", (int)ivideo->vbflags);
 
-#ifdef CONFIG_MTRR
-               ivideo->mtrr = mtrr_add(ivideo->video_base, ivideo->video_size,
-                                       MTRR_TYPE_WRCOMB, 1);
-               if(ivideo->mtrr < 0) {
-                       printk(KERN_DEBUG "sisfb: Failed to add MTRRs\n");
-               }
-#endif
-
+               ivideo->wc_cookie = arch_phys_wc_add(ivideo->video_base,
+                                                    ivideo->video_size);
                if(register_framebuffer(sis_fb_info) < 0) {
                        printk(KERN_ERR "sisfb: Fatal error: Failed to register framebuffer\n");
                        ret = -EINVAL;
@@ -6507,11 +6496,7 @@ static void sisfb_remove(struct pci_dev *pdev)
 
        pci_dev_put(ivideo->nbridge);
 
-#ifdef CONFIG_MTRR
-       /* Release MTRR region */
-       if(ivideo->mtrr >= 0)
-               mtrr_del(ivideo->mtrr, ivideo->video_base, ivideo->video_size);
-#endif
+       arch_phys_wc_del(ivideo->wc_cookie);
 
        /* If device was disabled when starting, disable
         * it when quitting.
index f7ed6d9016f7779a575626485990dc6accf3c7d4..3e153c06131ad94adc2dac710dcd1c53539b75e8 100644 (file)
@@ -7,6 +7,7 @@
  */
 
 #include <linux/module.h>
+#include <linux/backlight.h>
 #include <linux/kernel.h>
 #include <linux/i2c.h>
 #include <linux/fb.h>
 #define        SSD1307FB_SET_COM_PINS_CONFIG   0xda
 #define        SSD1307FB_SET_VCOMH             0xdb
 
+#define MAX_CONTRAST 255
+
+#define REFRESHRATE 1
+
+static u_int refreshrate = REFRESHRATE;
+module_param(refreshrate, uint, 0);
+
 struct ssd1307fb_par;
 
-struct ssd1307fb_ops {
-       int (*init)(struct ssd1307fb_par *);
-       int (*remove)(struct ssd1307fb_par *);
+struct ssd1307fb_deviceinfo {
+       u32 default_vcomh;
+       u32 default_dclk_div;
+       u32 default_dclk_frq;
+       int need_pwm;
+       int need_chargepump;
 };
 
 struct ssd1307fb_par {
+       u32 com_invdir;
+       u32 com_lrremap;
+       u32 com_offset;
+       u32 com_seq;
+       u32 contrast;
+       u32 dclk_div;
+       u32 dclk_frq;
+       struct ssd1307fb_deviceinfo *device_info;
        struct i2c_client *client;
        u32 height;
        struct fb_info *info;
-       struct ssd1307fb_ops *ops;
        u32 page_offset;
+       u32 prechargep1;
+       u32 prechargep2;
        struct pwm_device *pwm;
        u32 pwm_period;
        int reset;
+       u32 seg_remap;
+       u32 vcomh;
        u32 width;
 };
 
@@ -213,6 +235,16 @@ static ssize_t ssd1307fb_write(struct fb_info *info, const char __user *buf,
        return count;
 }
 
+static int ssd1307fb_blank(int blank_mode, struct fb_info *info)
+{
+       struct ssd1307fb_par *par = info->par;
+
+       if (blank_mode != FB_BLANK_UNBLANK)
+               return ssd1307fb_write_cmd(par->client, SSD1307FB_DISPLAY_OFF);
+       else
+               return ssd1307fb_write_cmd(par->client, SSD1307FB_DISPLAY_ON);
+}
+
 static void ssd1307fb_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
 {
        struct ssd1307fb_par *par = info->par;
@@ -238,6 +270,7 @@ static struct fb_ops ssd1307fb_ops = {
        .owner          = THIS_MODULE,
        .fb_read        = fb_sys_read,
        .fb_write       = ssd1307fb_write,
+       .fb_blank       = ssd1307fb_blank,
        .fb_fillrect    = ssd1307fb_fillrect,
        .fb_copyarea    = ssd1307fb_copyarea,
        .fb_imageblit   = ssd1307fb_imageblit,
@@ -249,74 +282,46 @@ static void ssd1307fb_deferred_io(struct fb_info *info,
        ssd1307fb_update_display(info->par);
 }
 
-static struct fb_deferred_io ssd1307fb_defio = {
-       .delay          = HZ,
-       .deferred_io    = ssd1307fb_deferred_io,
-};
-
-static int ssd1307fb_ssd1307_init(struct ssd1307fb_par *par)
+static int ssd1307fb_init(struct ssd1307fb_par *par)
 {
        int ret;
+       u32 precharge, dclk, com_invdir, compins;
 
-       par->pwm = pwm_get(&par->client->dev, NULL);
-       if (IS_ERR(par->pwm)) {
-               dev_err(&par->client->dev, "Could not get PWM from device tree!\n");
-               return PTR_ERR(par->pwm);
-       }
-
-       par->pwm_period = pwm_get_period(par->pwm);
-       /* Enable the PWM */
-       pwm_config(par->pwm, par->pwm_period / 2, par->pwm_period);
-       pwm_enable(par->pwm);
-
-       dev_dbg(&par->client->dev, "Using PWM%d with a %dns period.\n",
-               par->pwm->pwm, par->pwm_period);
-
-       /* Map column 127 of the OLED to segment 0 */
-       ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SEG_REMAP_ON);
-       if (ret < 0)
-               return ret;
-
-       /* Turn on the display */
-       ret = ssd1307fb_write_cmd(par->client, SSD1307FB_DISPLAY_ON);
-       if (ret < 0)
-               return ret;
-
-       return 0;
-}
-
-static int ssd1307fb_ssd1307_remove(struct ssd1307fb_par *par)
-{
-       pwm_disable(par->pwm);
-       pwm_put(par->pwm);
-       return 0;
-}
+       if (par->device_info->need_pwm) {
+               par->pwm = pwm_get(&par->client->dev, NULL);
+               if (IS_ERR(par->pwm)) {
+                       dev_err(&par->client->dev, "Could not get PWM from device tree!\n");
+                       return PTR_ERR(par->pwm);
+               }
 
-static struct ssd1307fb_ops ssd1307fb_ssd1307_ops = {
-       .init   = ssd1307fb_ssd1307_init,
-       .remove = ssd1307fb_ssd1307_remove,
-};
+               par->pwm_period = pwm_get_period(par->pwm);
+               /* Enable the PWM */
+               pwm_config(par->pwm, par->pwm_period / 2, par->pwm_period);
+               pwm_enable(par->pwm);
 
-static int ssd1307fb_ssd1306_init(struct ssd1307fb_par *par)
-{
-       int ret;
+               dev_dbg(&par->client->dev, "Using PWM%d with a %dns period.\n",
+                       par->pwm->pwm, par->pwm_period);
+       };
 
        /* Set initial contrast */
        ret = ssd1307fb_write_cmd(par->client, SSD1307FB_CONTRAST);
        if (ret < 0)
                return ret;
 
-       ret = ssd1307fb_write_cmd(par->client, 0x7f);
-       if (ret < 0)
-               return ret;
-
-       /* Set COM direction */
-       ret = ssd1307fb_write_cmd(par->client, 0xc8);
+       ret = ssd1307fb_write_cmd(par->client, par->contrast);
        if (ret < 0)
                return ret;
 
        /* Set segment re-map */
-       ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SEG_REMAP_ON);
+       if (par->seg_remap) {
+               ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SEG_REMAP_ON);
+               if (ret < 0)
+                       return ret;
+       };
+
+       /* Set COM direction */
+       com_invdir = 0xc0 | (par->com_invdir & 0x1) << 3;
+       ret = ssd1307fb_write_cmd(par->client,  com_invdir);
        if (ret < 0)
                return ret;
 
@@ -334,7 +339,7 @@ static int ssd1307fb_ssd1306_init(struct ssd1307fb_par *par)
        if (ret < 0)
                return ret;
 
-       ret = ssd1307fb_write_cmd(par->client, 0x20);
+       ret = ssd1307fb_write_cmd(par->client, par->com_offset);
        if (ret < 0)
                return ret;
 
@@ -343,7 +348,8 @@ static int ssd1307fb_ssd1306_init(struct ssd1307fb_par *par)
        if (ret < 0)
                return ret;
 
-       ret = ssd1307fb_write_cmd(par->client, 0xf0);
+       dclk = ((par->dclk_div - 1) & 0xf) | (par->dclk_frq & 0xf) << 4;
+       ret = ssd1307fb_write_cmd(par->client, dclk);
        if (ret < 0)
                return ret;
 
@@ -352,7 +358,8 @@ static int ssd1307fb_ssd1306_init(struct ssd1307fb_par *par)
        if (ret < 0)
                return ret;
 
-       ret = ssd1307fb_write_cmd(par->client, 0x22);
+       precharge = (par->prechargep1 & 0xf) | (par->prechargep2 & 0xf) << 4;
+       ret = ssd1307fb_write_cmd(par->client, precharge);
        if (ret < 0)
                return ret;
 
@@ -361,7 +368,9 @@ static int ssd1307fb_ssd1306_init(struct ssd1307fb_par *par)
        if (ret < 0)
                return ret;
 
-       ret = ssd1307fb_write_cmd(par->client, 0x22);
+       compins = 0x02 | !(par->com_seq & 0x1) << 4
+                                  | (par->com_lrremap & 0x1) << 5;
+       ret = ssd1307fb_write_cmd(par->client, compins);
        if (ret < 0)
                return ret;
 
@@ -370,7 +379,7 @@ static int ssd1307fb_ssd1306_init(struct ssd1307fb_par *par)
        if (ret < 0)
                return ret;
 
-       ret = ssd1307fb_write_cmd(par->client, 0x49);
+       ret = ssd1307fb_write_cmd(par->client, par->vcomh);
        if (ret < 0)
                return ret;
 
@@ -379,7 +388,8 @@ static int ssd1307fb_ssd1306_init(struct ssd1307fb_par *par)
        if (ret < 0)
                return ret;
 
-       ret = ssd1307fb_write_cmd(par->client, 0x14);
+       ret = ssd1307fb_write_cmd(par->client,
+               (par->device_info->need_chargepump & 0x1 << 2) & 0x14);
        if (ret < 0)
                return ret;
 
@@ -393,6 +403,7 @@ static int ssd1307fb_ssd1306_init(struct ssd1307fb_par *par)
        if (ret < 0)
                return ret;
 
+       /* Set column range */
        ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_COL_RANGE);
        if (ret < 0)
                return ret;
@@ -405,6 +416,7 @@ static int ssd1307fb_ssd1306_init(struct ssd1307fb_par *par)
        if (ret < 0)
                return ret;
 
+       /* Set page range */
        ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_PAGE_RANGE);
        if (ret < 0)
                return ret;
@@ -426,18 +438,75 @@ static int ssd1307fb_ssd1306_init(struct ssd1307fb_par *par)
        return 0;
 }
 
-static struct ssd1307fb_ops ssd1307fb_ssd1306_ops = {
-       .init   = ssd1307fb_ssd1306_init,
+static int ssd1307fb_update_bl(struct backlight_device *bdev)
+{
+       struct ssd1307fb_par *par = bl_get_data(bdev);
+       int ret;
+       int brightness = bdev->props.brightness;
+
+       par->contrast = brightness;
+
+       ret = ssd1307fb_write_cmd(par->client, SSD1307FB_CONTRAST);
+       if (ret < 0)
+               return ret;
+       ret = ssd1307fb_write_cmd(par->client, par->contrast);
+       if (ret < 0)
+               return ret;
+       return 0;
+}
+
+static int ssd1307fb_get_brightness(struct backlight_device *bdev)
+{
+       struct ssd1307fb_par *par = bl_get_data(bdev);
+
+       return par->contrast;
+}
+
+static int ssd1307fb_check_fb(struct backlight_device *bdev,
+                                  struct fb_info *info)
+{
+       return (info->bl_dev == bdev);
+}
+
+static const struct backlight_ops ssd1307fb_bl_ops = {
+       .options        = BL_CORE_SUSPENDRESUME,
+       .update_status  = ssd1307fb_update_bl,
+       .get_brightness = ssd1307fb_get_brightness,
+       .check_fb       = ssd1307fb_check_fb,
+};
+
+static struct ssd1307fb_deviceinfo ssd1307fb_ssd1305_deviceinfo = {
+       .default_vcomh = 0x34,
+       .default_dclk_div = 1,
+       .default_dclk_frq = 7,
+};
+
+static struct ssd1307fb_deviceinfo ssd1307fb_ssd1306_deviceinfo = {
+       .default_vcomh = 0x20,
+       .default_dclk_div = 1,
+       .default_dclk_frq = 8,
+       .need_chargepump = 1,
+};
+
+static struct ssd1307fb_deviceinfo ssd1307fb_ssd1307_deviceinfo = {
+       .default_vcomh = 0x20,
+       .default_dclk_div = 2,
+       .default_dclk_frq = 12,
+       .need_pwm = 1,
 };
 
 static const struct of_device_id ssd1307fb_of_match[] = {
+       {
+               .compatible = "solomon,ssd1305fb-i2c",
+               .data = (void *)&ssd1307fb_ssd1305_deviceinfo,
+       },
        {
                .compatible = "solomon,ssd1306fb-i2c",
-               .data = (void *)&ssd1307fb_ssd1306_ops,
+               .data = (void *)&ssd1307fb_ssd1306_deviceinfo,
        },
        {
                .compatible = "solomon,ssd1307fb-i2c",
-               .data = (void *)&ssd1307fb_ssd1307_ops,
+               .data = (void *)&ssd1307fb_ssd1307_deviceinfo,
        },
        {},
 };
@@ -446,8 +515,11 @@ MODULE_DEVICE_TABLE(of, ssd1307fb_of_match);
 static int ssd1307fb_probe(struct i2c_client *client,
                           const struct i2c_device_id *id)
 {
+       struct backlight_device *bl;
+       char bl_name[12];
        struct fb_info *info;
        struct device_node *node = client->dev.of_node;
+       struct fb_deferred_io *ssd1307fb_defio;
        u32 vmem_size;
        struct ssd1307fb_par *par;
        u8 *vmem;
@@ -468,8 +540,8 @@ static int ssd1307fb_probe(struct i2c_client *client,
        par->info = info;
        par->client = client;
 
-       par->ops = (struct ssd1307fb_ops *)of_match_device(ssd1307fb_of_match,
-                                                          &client->dev)->data;
+       par->device_info = (struct ssd1307fb_deviceinfo *)of_match_device(
+                       ssd1307fb_of_match, &client->dev)->data;
 
        par->reset = of_get_named_gpio(client->dev.of_node,
                                         "reset-gpios", 0);
@@ -487,19 +559,51 @@ static int ssd1307fb_probe(struct i2c_client *client,
        if (of_property_read_u32(node, "solomon,page-offset", &par->page_offset))
                par->page_offset = 1;
 
+       if (of_property_read_u32(node, "solomon,com-offset", &par->com_offset))
+               par->com_offset = 0;
+
+       if (of_property_read_u32(node, "solomon,prechargep1", &par->prechargep1))
+               par->prechargep1 = 2;
+
+       if (of_property_read_u32(node, "solomon,prechargep2", &par->prechargep2))
+               par->prechargep2 = 2;
+
+       par->seg_remap = !of_property_read_bool(node, "solomon,segment-no-remap");
+       par->com_seq = of_property_read_bool(node, "solomon,com-seq");
+       par->com_lrremap = of_property_read_bool(node, "solomon,com-lrremap");
+       par->com_invdir = of_property_read_bool(node, "solomon,com-invdir");
+
+       par->contrast = 127;
+       par->vcomh = par->device_info->default_vcomh;
+
+       /* Setup display timing */
+       par->dclk_div = par->device_info->default_dclk_div;
+       par->dclk_frq = par->device_info->default_dclk_frq;
+
        vmem_size = par->width * par->height / 8;
 
-       vmem = devm_kzalloc(&client->dev, vmem_size, GFP_KERNEL);
+       vmem = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO,
+                                       get_order(vmem_size));
        if (!vmem) {
                dev_err(&client->dev, "Couldn't allocate graphical memory.\n");
                ret = -ENOMEM;
                goto fb_alloc_error;
        }
 
+       ssd1307fb_defio = devm_kzalloc(&client->dev, sizeof(struct fb_deferred_io), GFP_KERNEL);
+       if (!ssd1307fb_defio) {
+               dev_err(&client->dev, "Couldn't allocate deferred io.\n");
+               ret = -ENOMEM;
+               goto fb_alloc_error;
+       }
+
+       ssd1307fb_defio->delay = HZ / refreshrate;
+       ssd1307fb_defio->deferred_io = ssd1307fb_deferred_io;
+
        info->fbops = &ssd1307fb_ops;
        info->fix = ssd1307fb_fix;
        info->fix.line_length = par->width / 8;
-       info->fbdefio = &ssd1307fb_defio;
+       info->fbdefio = ssd1307fb_defio;
 
        info->var = ssd1307fb_var;
        info->var.xres = par->width;
@@ -515,7 +619,7 @@ static int ssd1307fb_probe(struct i2c_client *client,
        info->var.blue.offset = 0;
 
        info->screen_base = (u8 __force __iomem *)vmem;
-       info->fix.smem_start = (unsigned long)vmem;
+       info->fix.smem_start = __pa(vmem);
        info->fix.smem_len = vmem_size;
 
        fb_deferred_io_init(info);
@@ -538,11 +642,9 @@ static int ssd1307fb_probe(struct i2c_client *client,
        gpio_set_value(par->reset, 1);
        udelay(4);
 
-       if (par->ops->init) {
-               ret = par->ops->init(par);
-               if (ret)
-                       goto reset_oled_error;
-       }
+       ret = ssd1307fb_init(par);
+       if (ret)
+               goto reset_oled_error;
 
        ret = register_framebuffer(info);
        if (ret) {
@@ -550,13 +652,30 @@ static int ssd1307fb_probe(struct i2c_client *client,
                goto panel_init_error;
        }
 
+       snprintf(bl_name, sizeof(bl_name), "ssd1307fb%d", info->node);
+       bl = backlight_device_register(bl_name, &client->dev, par,
+                                      &ssd1307fb_bl_ops, NULL);
+       if (IS_ERR(bl)) {
+               dev_err(&client->dev, "unable to register backlight device: %ld\n",
+                       PTR_ERR(bl));
+               goto bl_init_error;
+       }
+
+       bl->props.brightness = par->contrast;
+       bl->props.max_brightness = MAX_CONTRAST;
+       info->bl_dev = bl;
+
        dev_info(&client->dev, "fb%d: %s framebuffer device registered, using %d bytes of video memory\n", info->node, info->fix.id, vmem_size);
 
        return 0;
 
+bl_init_error:
+       unregister_framebuffer(info);
 panel_init_error:
-       if (par->ops->remove)
-               par->ops->remove(par);
+       if (par->device_info->need_pwm) {
+               pwm_disable(par->pwm);
+               pwm_put(par->pwm);
+       };
 reset_oled_error:
        fb_deferred_io_cleanup(info);
 fb_alloc_error:
@@ -569,16 +688,24 @@ static int ssd1307fb_remove(struct i2c_client *client)
        struct fb_info *info = i2c_get_clientdata(client);
        struct ssd1307fb_par *par = info->par;
 
+       ssd1307fb_write_cmd(par->client, SSD1307FB_DISPLAY_OFF);
+
+       backlight_device_unregister(info->bl_dev);
+
        unregister_framebuffer(info);
-       if (par->ops->remove)
-               par->ops->remove(par);
+       if (par->device_info->need_pwm) {
+               pwm_disable(par->pwm);
+               pwm_put(par->pwm);
+       };
        fb_deferred_io_cleanup(info);
+       __free_pages(__va(info->fix.smem_start), get_order(info->fix.smem_len));
        framebuffer_release(info);
 
        return 0;
 }
 
 static const struct i2c_device_id ssd1307fb_i2c_id[] = {
+       { "ssd1305fb", 0 },
        { "ssd1306fb", 0 },
        { "ssd1307fb", 0 },
        { }
index f761fe375f5b265122817242fb3668feaa495ebe..621fa441a6db225755145cd859fd51d48d62482f 100644 (file)
 
 #define DPRINTK(a, b...) pr_debug("fb: %s: " a, __func__ , ## b)
 
-#ifdef CONFIG_MTRR
-#include <asm/mtrr.h>
-#else
-/* duplicate asm/mtrr.h defines to work on archs without mtrr */
-#define MTRR_TYPE_WRCOMB     1
-
-static inline int mtrr_add(unsigned long base, unsigned long size,
-                               unsigned int type, char increment)
-{
-    return -ENODEV;
-}
-static inline int mtrr_del(int reg, unsigned long base,
-                               unsigned long size)
-{
-    return -ENODEV;
-}
-#endif
-
 #define BANSHEE_MAX_PIXCLOCK 270000
 #define VOODOO3_MAX_PIXCLOCK 300000
 #define VOODOO5_MAX_PIXCLOCK 350000
@@ -167,7 +149,6 @@ static int nopan;
 static int nowrap = 1;      /* not implemented (yet) */
 static int hwcursor = 1;
 static char *mode_option;
-/* mtrr option */
 static bool nomtrr;
 
 /* -------------------------------------------------------------------------
@@ -1454,8 +1435,8 @@ static int tdfxfb_probe(struct pci_dev *pdev, const struct pci_device_id *id)
                goto out_err_regbase;
        }
 
-       info->screen_base = ioremap_nocache(info->fix.smem_start,
-                                           info->fix.smem_len);
+       info->screen_base = ioremap_wc(info->fix.smem_start,
+                                      info->fix.smem_len);
        if (!info->screen_base) {
                printk(KERN_ERR "fb: Can't remap %s framebuffer.\n",
                                info->fix.id);
@@ -1473,11 +1454,9 @@ static int tdfxfb_probe(struct pci_dev *pdev, const struct pci_device_id *id)
        printk(KERN_INFO "fb: %s memory = %dK\n", info->fix.id,
                        info->fix.smem_len >> 10);
 
-       default_par->mtrr_handle = -1;
        if (!nomtrr)
-               default_par->mtrr_handle =
-                       mtrr_add(info->fix.smem_start, info->fix.smem_len,
-                                MTRR_TYPE_WRCOMB, 1);
+               default_par->wc_cookie= arch_phys_wc_add(info->fix.smem_start,
+                                                        info->fix.smem_len);
 
        info->fix.ypanstep      = nopan ? 0 : 1;
        info->fix.ywrapstep     = nowrap ? 0 : 1;
@@ -1566,9 +1545,7 @@ out_err_iobase:
 #ifdef CONFIG_FB_3DFX_I2C
        tdfxfb_delete_i2c_busses(default_par);
 #endif
-       if (default_par->mtrr_handle >= 0)
-               mtrr_del(default_par->mtrr_handle, info->fix.smem_start,
-                        info->fix.smem_len);
+       arch_phys_wc_del(default_par->wc_cookie);
        release_region(pci_resource_start(pdev, 2),
                       pci_resource_len(pdev, 2));
 out_err_screenbase:
@@ -1604,10 +1581,8 @@ static void __init tdfxfb_setup(char *options)
                        nowrap = 1;
                } else if (!strncmp(this_opt, "hwcursor=", 9)) {
                        hwcursor = simple_strtoul(this_opt + 9, NULL, 0);
-#ifdef CONFIG_MTRR
                } else if (!strncmp(this_opt, "nomtrr", 6)) {
                        nomtrr = 1;
-#endif
                } else {
                        mode_option = this_opt;
                }
@@ -1633,9 +1608,7 @@ static void tdfxfb_remove(struct pci_dev *pdev)
 #ifdef CONFIG_FB_3DFX_I2C
        tdfxfb_delete_i2c_busses(par);
 #endif
-       if (par->mtrr_handle >= 0)
-               mtrr_del(par->mtrr_handle, info->fix.smem_start,
-                        info->fix.smem_len);
+       arch_phys_wc_del(par->wc_cookie);
        iounmap(par->regbase_virt);
        iounmap(info->screen_base);
 
@@ -1677,10 +1650,8 @@ MODULE_PARM_DESC(hwcursor, "Enable hardware cursor "
                        "(1=enable, 0=disable, default=1)");
 module_param(mode_option, charp, 0);
 MODULE_PARM_DESC(mode_option, "Initial video mode e.g. '648x480-8@60'");
-#ifdef CONFIG_MTRR
 module_param(nomtrr, bool, 0);
 MODULE_PARM_DESC(nomtrr, "Disable MTRR support (default: enabled)");
-#endif
 
 module_init(tdfxfb_init);
 module_exit(tdfxfb_exit);
index d79a0ac49fc7d17449b358174464847cdbc5b4a0..528fe917dd49473ecd64d81a97b2b1987ac3d913 100644 (file)
 #include <linux/init.h>
 #include <linux/platform_device.h>
 #include <linux/screen_info.h>
+#include <linux/io.h>
 
 #include <video/vga.h>
-#include <asm/io.h>
-#include <asm/mtrr.h>
 
 #define dac_reg        (0x3c8)
 #define dac_val        (0x3c9)
 
 /* --------------------------------------------------------------------- */
 
+struct vesafb_par {
+       u32 pseudo_palette[256];
+       int wc_cookie;
+};
+
 static struct fb_var_screeninfo vesafb_defined = {
        .activate       = FB_ACTIVATE_NOW,
        .height         = -1,
@@ -175,7 +179,10 @@ static int vesafb_setcolreg(unsigned regno, unsigned red, unsigned green,
 
 static void vesafb_destroy(struct fb_info *info)
 {
+       struct vesafb_par *par = info->par;
+
        fb_dealloc_cmap(&info->cmap);
+       arch_phys_wc_del(par->wc_cookie);
        if (info->screen_base)
                iounmap(info->screen_base);
        release_mem_region(info->apertures->ranges[0].base, info->apertures->ranges[0].size);
@@ -228,6 +235,7 @@ static int vesafb_setup(char *options)
 static int vesafb_probe(struct platform_device *dev)
 {
        struct fb_info *info;
+       struct vesafb_par *par;
        int i, err;
        unsigned int size_vmode;
        unsigned int size_remap;
@@ -291,14 +299,14 @@ static int vesafb_probe(struct platform_device *dev)
                   spaces our resource handlers simply don't know about */
        }
 
-       info = framebuffer_alloc(sizeof(u32) * 256, &dev->dev);
+       info = framebuffer_alloc(sizeof(struct vesafb_par), &dev->dev);
        if (!info) {
                release_mem_region(vesafb_fix.smem_start, size_total);
                return -ENOMEM;
        }
        platform_set_drvdata(dev, info);
-       info->pseudo_palette = info->par;
-       info->par = NULL;
+       par = info->par;
+       info->pseudo_palette = par->pseudo_palette;
 
        /* set vesafb aperture size for generic probing */
        info->apertures = alloc_apertures(1);
@@ -404,60 +412,27 @@ static int vesafb_probe(struct platform_device *dev)
         * region already (FIXME) */
        request_region(0x3c0, 32, "vesafb");
 
-#ifdef CONFIG_MTRR
-       if (mtrr) {
+       if (mtrr == 3) {
                unsigned int temp_size = size_total;
-               unsigned int type = 0;
-
-               switch (mtrr) {
-               case 1:
-                       type = MTRR_TYPE_UNCACHABLE;
-                       break;
-               case 2:
-                       type = MTRR_TYPE_WRBACK;
-                       break;
-               case 3:
-                       type = MTRR_TYPE_WRCOMB;
-                       break;
-               case 4:
-                       type = MTRR_TYPE_WRTHROUGH;
-                       break;
-               default:
-                       type = 0;
-                       break;
-               }
 
-               if (type) {
-                       int rc;
+               /* Find the largest power-of-two */
+               temp_size = roundup_pow_of_two(temp_size);
 
-                       /* Find the largest power-of-two */
-                       temp_size = roundup_pow_of_two(temp_size);
+               /* Try and find a power of two to add */
+               do {
+                       par->wc_cookie =
+                               arch_phys_wc_add(vesafb_fix.smem_start,
+                                                temp_size);
+                       temp_size >>= 1;
+               } while (temp_size >= PAGE_SIZE && par->wc_cookie < 0);
 
-                       /* Try and find a power of two to add */
-                       do {
-                               rc = mtrr_add(vesafb_fix.smem_start, temp_size,
-                                             type, 1);
-                               temp_size >>= 1;
-                       } while (temp_size >= PAGE_SIZE && rc == -EINVAL);
-               }
-       }
-#endif
-       
-       switch (mtrr) {
-       case 1: /* uncachable */
-               info->screen_base = ioremap_nocache(vesafb_fix.smem_start, vesafb_fix.smem_len);
-               break;
-       case 2: /* write-back */
-               info->screen_base = ioremap_cache(vesafb_fix.smem_start, vesafb_fix.smem_len);
-               break;
-       case 3: /* write-combining */
                info->screen_base = ioremap_wc(vesafb_fix.smem_start, vesafb_fix.smem_len);
-               break;
-       case 4: /* write-through */
-       default:
+       } else {
+               if (mtrr && mtrr != 3)
+                       WARN_ONCE(1, "Only MTRR_TYPE_WRCOMB (3) make sense\n");
                info->screen_base = ioremap(vesafb_fix.smem_start, vesafb_fix.smem_len);
-               break;
        }
+
        if (!info->screen_base) {
                printk(KERN_ERR
                       "vesafb: abort, cannot ioremap video memory 0x%x @ 0x%lx\n",
@@ -492,6 +467,7 @@ static int vesafb_probe(struct platform_device *dev)
        fb_info(info, "%s frame buffer device\n", info->fix.id);
        return 0;
 err:
+       arch_phys_wc_del(par->wc_cookie);
        if (info->screen_base)
                iounmap(info->screen_base);
        framebuffer_release(info);
index e894eb278d8336d018d3e6e8c29556dc9b5f3cb5..5447b818633232937d7cb87ddb21424da1281010 100644 (file)
@@ -423,6 +423,7 @@ int vp_set_vq_affinity(struct virtqueue *vq, int cpu)
                if (cpu == -1)
                        irq_set_affinity_hint(irq, NULL);
                else {
+                       cpumask_clear(mask);
                        cpumask_set_cpu(cpu, mask);
                        irq_set_affinity_hint(irq, mask);
                }
@@ -501,9 +502,6 @@ static int virtio_pci_probe(struct pci_dev *pci_dev,
        INIT_LIST_HEAD(&vp_dev->virtqueues);
        spin_lock_init(&vp_dev->lock);
 
-       /* Disable MSI/MSIX to bring device to a known good state. */
-       pci_msi_off(pci_dev);
-
        /* enable the device */
        rc = pci_enable_device(pci_dev);
        if (rc)
index e5e7c5505de7dc8f289f3891013b0eac25399747..262647bbc61449931624821533e887d4b748b18e 100644 (file)
@@ -470,6 +470,18 @@ config SIRFSOC_WATCHDOG
          Support for CSR SiRFprimaII and SiRFatlasVI watchdog. When
          the watchdog triggers the system will be reset.
 
+config ST_LPC_WATCHDOG
+       tristate "STMicroelectronics LPC Watchdog"
+       depends on ARCH_STI
+       depends on OF
+       select WATCHDOG_CORE
+       help
+         Say Y here to include STMicroelectronics Low Power Controller
+         (LPC) based Watchdog timer support.
+
+         To compile this driver as a module, choose M here: the
+         module will be called st_lpc_wdt.
+
 config TEGRA_WATCHDOG
        tristate "Tegra watchdog"
        depends on (ARCH_TEGRA || COMPILE_TEST) && HAS_IOMEM
index 5c19294d1c3015acaf14e4a52734f0c82ae798db..d98768c7d928cb5c7b54b6f339f0efc4d88c2db4 100644 (file)
@@ -59,6 +59,7 @@ obj-$(CONFIG_RETU_WATCHDOG) += retu_wdt.o
 obj-$(CONFIG_BCM2835_WDT) += bcm2835_wdt.o
 obj-$(CONFIG_MOXART_WDT) += moxart_wdt.o
 obj-$(CONFIG_SIRFSOC_WATCHDOG) += sirfsoc_wdt.o
+obj-$(CONFIG_ST_LPC_WATCHDOG) += st_lpc_wdt.o
 obj-$(CONFIG_QCOM_WDT) += qcom-wdt.o
 obj-$(CONFIG_BCM_KONA_WDT) += bcm_kona_wdt.o
 obj-$(CONFIG_TEGRA_WATCHDOG) += tegra_wdt.o
diff --git a/drivers/watchdog/st_lpc_wdt.c b/drivers/watchdog/st_lpc_wdt.c
new file mode 100644 (file)
index 0000000..f32be15
--- /dev/null
@@ -0,0 +1,344 @@
+/*
+ * ST's LPC Watchdog
+ *
+ * Copyright (C) 2014 STMicroelectronics -- All Rights Reserved
+ *
+ * Author: David Paris <david.paris@st.com> for STMicroelectronics
+ *         Lee Jones <lee.jones@linaro.org> for STMicroelectronics
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#include <linux/clk.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/watchdog.h>
+
+#include <dt-bindings/mfd/st-lpc.h>
+
+/* Low Power Alarm */
+#define LPC_LPA_LSB_OFF                        0x410
+#define LPC_LPA_START_OFF              0x418
+
+/* LPC as WDT */
+#define LPC_WDT_OFF                    0x510
+
+static struct watchdog_device st_wdog_dev;
+
+struct st_wdog_syscfg {
+       unsigned int reset_type_reg;
+       unsigned int reset_type_mask;
+       unsigned int enable_reg;
+       unsigned int enable_mask;
+};
+
+struct st_wdog {
+       void __iomem *base;
+       struct device *dev;
+       struct regmap *regmap;
+       struct st_wdog_syscfg *syscfg;
+       struct clk *clk;
+       unsigned long clkrate;
+       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),
+};
+
+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);
+
+static void st_wdog_setup(struct st_wdog *st_wdog, bool enable)
+{
+       /* Type of watchdog reset - 0: Cold 1: Warm */
+       if (st_wdog->syscfg->reset_type_reg)
+               regmap_update_bits(st_wdog->regmap,
+                                  st_wdog->syscfg->reset_type_reg,
+                                  st_wdog->syscfg->reset_type_mask,
+                                  st_wdog->warm_reset);
+
+       /* Mask/unmask watchdog reset */
+       regmap_update_bits(st_wdog->regmap,
+                          st_wdog->syscfg->enable_reg,
+                          st_wdog->syscfg->enable_mask,
+                          enable ? 0 : st_wdog->syscfg->enable_mask);
+}
+
+static void st_wdog_load_timer(struct st_wdog *st_wdog, unsigned int timeout)
+{
+       unsigned long clkrate = st_wdog->clkrate;
+
+       writel_relaxed(timeout * clkrate, st_wdog->base + LPC_LPA_LSB_OFF);
+       writel_relaxed(1, st_wdog->base + LPC_LPA_START_OFF);
+}
+
+static int st_wdog_start(struct watchdog_device *wdd)
+{
+       struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
+
+       writel_relaxed(1, st_wdog->base + LPC_WDT_OFF);
+
+       return 0;
+}
+
+static int st_wdog_stop(struct watchdog_device *wdd)
+{
+       struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
+
+       writel_relaxed(0, st_wdog->base + LPC_WDT_OFF);
+
+       return 0;
+}
+
+static int st_wdog_set_timeout(struct watchdog_device *wdd,
+                              unsigned int timeout)
+{
+       struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
+
+       wdd->timeout = timeout;
+       st_wdog_load_timer(st_wdog, timeout);
+
+       return 0;
+}
+
+static int st_wdog_keepalive(struct watchdog_device *wdd)
+{
+       struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
+
+       st_wdog_load_timer(st_wdog, wdd->timeout);
+
+       return 0;
+}
+
+static const struct watchdog_info st_wdog_info = {
+       .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
+       .identity = "ST LPC WDT",
+};
+
+static const struct watchdog_ops st_wdog_ops = {
+       .owner          = THIS_MODULE,
+       .start          = st_wdog_start,
+       .stop           = st_wdog_stop,
+       .ping           = st_wdog_keepalive,
+       .set_timeout    = st_wdog_set_timeout,
+};
+
+static struct watchdog_device st_wdog_dev = {
+       .info           = &st_wdog_info,
+       .ops            = &st_wdog_ops,
+};
+
+static int st_wdog_probe(struct platform_device *pdev)
+{
+       const struct of_device_id *match;
+       struct device_node *np = pdev->dev.of_node;
+       struct st_wdog *st_wdog;
+       struct regmap *regmap;
+       struct resource *res;
+       struct clk *clk;
+       void __iomem *base;
+       uint32_t mode;
+       int ret;
+
+       ret = of_property_read_u32(np, "st,lpc-mode", &mode);
+       if (ret) {
+               dev_err(&pdev->dev, "An LPC mode must be provided\n");
+               return -EINVAL;
+       }
+
+       /* LPC can either run in RTC or WDT mode */
+       if (mode != ST_LPC_MODE_WDT)
+               return -ENODEV;
+
+       st_wdog = devm_kzalloc(&pdev->dev, sizeof(*st_wdog), GFP_KERNEL);
+       if (!st_wdog)
+               return -ENOMEM;
+
+       match = of_match_device(st_wdog_match, &pdev->dev);
+       if (!match) {
+               dev_err(&pdev->dev, "Couldn't match device\n");
+               return -ENODEV;
+       }
+       st_wdog->syscfg = (struct st_wdog_syscfg *)match->data;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       base = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(base))
+               return PTR_ERR(base);
+
+       regmap = syscon_regmap_lookup_by_phandle(np, "st,syscfg");
+       if (IS_ERR(regmap)) {
+               dev_err(&pdev->dev, "No syscfg phandle specified\n");
+               return PTR_ERR(regmap);
+       }
+
+       clk = devm_clk_get(&pdev->dev, NULL);
+       if (IS_ERR(clk)) {
+               dev_err(&pdev->dev, "Unable to request clock\n");
+               return PTR_ERR(clk);
+       }
+
+       st_wdog->dev            = &pdev->dev;
+       st_wdog->base           = base;
+       st_wdog->clk            = clk;
+       st_wdog->regmap         = regmap;
+       st_wdog->warm_reset     = of_property_read_bool(np, "st,warm_reset");
+       st_wdog->clkrate        = clk_get_rate(st_wdog->clk);
+
+       if (!st_wdog->clkrate) {
+               dev_err(&pdev->dev, "Unable to fetch clock rate\n");
+               return -EINVAL;
+       }
+       st_wdog_dev.max_timeout = 0xFFFFFFFF / st_wdog->clkrate;
+
+       ret = clk_prepare_enable(clk);
+       if (ret) {
+               dev_err(&pdev->dev, "Unable to enable clock\n");
+               return ret;
+       }
+
+       watchdog_set_drvdata(&st_wdog_dev, st_wdog);
+       watchdog_set_nowayout(&st_wdog_dev, WATCHDOG_NOWAYOUT);
+
+       /* Init Watchdog timeout with value in DT */
+       ret = watchdog_init_timeout(&st_wdog_dev, 0, &pdev->dev);
+       if (ret) {
+               dev_err(&pdev->dev, "Unable to initialise watchdog timeout\n");
+               clk_disable_unprepare(clk);
+               return ret;
+       }
+
+       ret = watchdog_register_device(&st_wdog_dev);
+       if (ret) {
+               dev_err(&pdev->dev, "Unable to register watchdog\n");
+               clk_disable_unprepare(clk);
+               return ret;
+       }
+
+       st_wdog_setup(st_wdog, true);
+
+       dev_info(&pdev->dev, "LPC Watchdog driver registered, reset type is %s",
+                st_wdog->warm_reset ? "warm" : "cold");
+
+       return ret;
+}
+
+static int st_wdog_remove(struct platform_device *pdev)
+{
+       struct st_wdog *st_wdog = watchdog_get_drvdata(&st_wdog_dev);
+
+       st_wdog_setup(st_wdog, false);
+       watchdog_unregister_device(&st_wdog_dev);
+       clk_disable_unprepare(st_wdog->clk);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int st_wdog_suspend(struct device *dev)
+{
+       struct st_wdog *st_wdog = watchdog_get_drvdata(&st_wdog_dev);
+
+       if (watchdog_active(&st_wdog_dev))
+               st_wdog_stop(&st_wdog_dev);
+
+       st_wdog_setup(st_wdog, false);
+
+       clk_disable(st_wdog->clk);
+
+       return 0;
+}
+
+static int st_wdog_resume(struct device *dev)
+{
+       struct st_wdog *st_wdog = watchdog_get_drvdata(&st_wdog_dev);
+       int ret;
+
+       ret = clk_enable(st_wdog->clk);
+       if (ret) {
+               dev_err(dev, "Unable to re-enable clock\n");
+               watchdog_unregister_device(&st_wdog_dev);
+               clk_unprepare(st_wdog->clk);
+               return ret;
+       }
+
+       st_wdog_setup(st_wdog, true);
+
+       if (watchdog_active(&st_wdog_dev)) {
+               st_wdog_load_timer(st_wdog, st_wdog_dev.timeout);
+               st_wdog_start(&st_wdog_dev);
+       }
+
+       return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(st_wdog_pm_ops,
+                        st_wdog_suspend,
+                        st_wdog_resume);
+
+static struct platform_driver st_wdog_driver = {
+       .driver = {
+               .name = "st-lpc-wdt",
+               .pm = &st_wdog_pm_ops,
+               .of_match_table = st_wdog_match,
+       },
+       .probe = st_wdog_probe,
+       .remove = st_wdog_remove,
+};
+module_platform_driver(st_wdog_driver);
+
+MODULE_AUTHOR("David Paris <david.paris@st.com>");
+MODULE_DESCRIPTION("ST LPC Watchdog Driver");
+MODULE_LICENSE("GPL");
index 3e62ee4b3b6641208e6833b639e25e5203f75ec9..f4a3694295533d3111c74bd0223c5f64f2e7d327 100644 (file)
@@ -46,13 +46,7 @@ static int xen_acpi_processor_enable(struct acpi_device *device)
        unsigned long long value;
        union acpi_object object = { 0 };
        struct acpi_buffer buffer = { sizeof(union acpi_object), &object };
-       struct acpi_processor *pr;
-
-       pr = acpi_driver_data(device);
-       if (!pr) {
-               pr_err(PREFIX "Cannot find driver data\n");
-               return -EINVAL;
-       }
+       struct acpi_processor *pr = acpi_driver_data(device);
 
        if (!strcmp(acpi_device_hid(device), ACPI_PROCESSOR_OBJECT_HID)) {
                /* Declared with "Processor" statement; match ProcessorID */
@@ -77,7 +71,7 @@ static int xen_acpi_processor_enable(struct acpi_device *device)
 
        pr->id = xen_pcpu_id(pr->acpi_id);
 
-       if ((int)pr->id < 0)
+       if (invalid_logical_cpuid(pr->id))
                /* This cpu is not presented at hypervisor, try to hotadd it */
                if (ACPI_FAILURE(xen_acpi_cpu_hotadd(pr))) {
                        pr_err(PREFIX "Hotadd CPU (acpi_id = %d) failed.\n",
@@ -226,7 +220,7 @@ static acpi_status xen_acpi_cpu_hotadd(struct acpi_processor *pr)
                return AE_ERROR;
 
        pr->id = xen_hotadd_cpu(pr);
-       if ((int)pr->id < 0)
+       if (invalid_logical_cpuid(pr->id))
                return AE_ERROR;
 
        /*
index b7f51504f85adc1b5d19d33e48d066bdd60684c5..39223c3e99ad5277a3bc525647b7a58f7df668f8 100644 (file)
 
 #include <generated/utsrelease.h>
 
-#include <scsi/scsi.h>
-#include <scsi/scsi_dbg.h>
-#include <scsi/scsi_eh.h>
-#include <scsi/scsi_tcq.h>
+#include <scsi/scsi_host.h> /* SG_ALL */
 
 #include <target/target_core_base.h>
 #include <target/target_core_fabric.h>
index fb9ffcb432779b55620909090b825ead7ddbcede..0923f2cf3c80aa2fb95a7385276de6f497ea46fe 100644 (file)
@@ -149,8 +149,6 @@ extern int v9fs_vfs_unlink(struct inode *i, struct dentry *d);
 extern int v9fs_vfs_rmdir(struct inode *i, struct dentry *d);
 extern int v9fs_vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
                        struct inode *new_dir, struct dentry *new_dentry);
-extern void v9fs_vfs_put_link(struct dentry *dentry, struct nameidata *nd,
-                       void *p);
 extern struct inode *v9fs_inode_from_fid(struct v9fs_session_info *v9ses,
                                         struct p9_fid *fid,
                                         struct super_block *sb, int new);
index 703342e309f57af329085db6d8f0b1b4814793dd..510040b04c964dbdeab4ab47c29b789fef5c4b5f 100644 (file)
@@ -1224,100 +1224,43 @@ ino_t v9fs_qid2ino(struct p9_qid *qid)
 }
 
 /**
- * v9fs_readlink - read a symlink's location (internal version)
+ * v9fs_vfs_follow_link - follow a symlink path
  * @dentry: dentry for symlink
- * @buffer: buffer to load symlink location into
- * @buflen: length of buffer
- *
+ * @cookie: place to pass the data to put_link()
  */
 
-static int v9fs_readlink(struct dentry *dentry, char *buffer, int buflen)
+static const char *v9fs_vfs_follow_link(struct dentry *dentry, void **cookie)
 {
-       int retval;
-
-       struct v9fs_session_info *v9ses;
-       struct p9_fid *fid;
+       struct v9fs_session_info *v9ses = v9fs_dentry2v9ses(dentry);
+       struct p9_fid *fid = v9fs_fid_lookup(dentry);
        struct p9_wstat *st;
+       char *res;
+
+       p9_debug(P9_DEBUG_VFS, "%pd\n", dentry);
 
-       p9_debug(P9_DEBUG_VFS, " %pd\n", dentry);
-       retval = -EPERM;
-       v9ses = v9fs_dentry2v9ses(dentry);
-       fid = v9fs_fid_lookup(dentry);
        if (IS_ERR(fid))
-               return PTR_ERR(fid);
+               return ERR_CAST(fid);
 
        if (!v9fs_proto_dotu(v9ses))
-               return -EBADF;
+               return ERR_PTR(-EBADF);
 
        st = p9_client_stat(fid);
        if (IS_ERR(st))
-               return PTR_ERR(st);
+               return ERR_CAST(st);
 
        if (!(st->mode & P9_DMSYMLINK)) {
-               retval = -EINVAL;
-               goto done;
+               p9stat_free(st);
+               kfree(st);
+               return ERR_PTR(-EINVAL);
        }
+       res = st->extension;
+       st->extension = NULL;
+       if (strlen(res) >= PATH_MAX)
+               res[PATH_MAX - 1] = '\0';
 
-       /* copy extension buffer into buffer */
-       retval = min(strlen(st->extension)+1, (size_t)buflen);
-       memcpy(buffer, st->extension, retval);
-
-       p9_debug(P9_DEBUG_VFS, "%pd -> %s (%.*s)\n",
-                dentry, st->extension, buflen, buffer);
-
-done:
        p9stat_free(st);
        kfree(st);
-       return retval;
-}
-
-/**
- * v9fs_vfs_follow_link - follow a symlink path
- * @dentry: dentry for symlink
- * @nd: nameidata
- *
- */
-
-static void *v9fs_vfs_follow_link(struct dentry *dentry, struct nameidata *nd)
-{
-       int len = 0;
-       char *link = __getname();
-
-       p9_debug(P9_DEBUG_VFS, "%pd\n", dentry);
-
-       if (!link)
-               link = ERR_PTR(-ENOMEM);
-       else {
-               len = v9fs_readlink(dentry, link, PATH_MAX);
-
-               if (len < 0) {
-                       __putname(link);
-                       link = ERR_PTR(len);
-               } else
-                       link[min(len, PATH_MAX-1)] = 0;
-       }
-       nd_set_link(nd, link);
-
-       return NULL;
-}
-
-/**
- * v9fs_vfs_put_link - release a symlink path
- * @dentry: dentry for symlink
- * @nd: nameidata
- * @p: unused
- *
- */
-
-void
-v9fs_vfs_put_link(struct dentry *dentry, struct nameidata *nd, void *p)
-{
-       char *s = nd_get_link(nd);
-
-       p9_debug(P9_DEBUG_VFS, " %pd %s\n",
-                dentry, IS_ERR(s) ? "<error>" : s);
-       if (!IS_ERR(s))
-               __putname(s);
+       return *cookie = res;
 }
 
 /**
@@ -1370,6 +1313,8 @@ v9fs_vfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname)
        return v9fs_vfs_mkspecial(dir, dentry, P9_DMSYMLINK, symname);
 }
 
+#define U32_MAX_DIGITS 10
+
 /**
  * v9fs_vfs_link - create a hardlink
  * @old_dentry: dentry for file to link to
@@ -1383,7 +1328,7 @@ v9fs_vfs_link(struct dentry *old_dentry, struct inode *dir,
              struct dentry *dentry)
 {
        int retval;
-       char *name;
+       char name[1 + U32_MAX_DIGITS + 2]; /* sign + number + \n + \0 */
        struct p9_fid *oldfid;
 
        p9_debug(P9_DEBUG_VFS, " %lu,%pd,%pd\n",
@@ -1393,20 +1338,12 @@ v9fs_vfs_link(struct dentry *old_dentry, struct inode *dir,
        if (IS_ERR(oldfid))
                return PTR_ERR(oldfid);
 
-       name = __getname();
-       if (unlikely(!name)) {
-               retval = -ENOMEM;
-               goto clunk_fid;
-       }
-
        sprintf(name, "%d\n", oldfid->fid);
        retval = v9fs_vfs_mkspecial(dir, dentry, P9_DMLINK, name);
-       __putname(name);
        if (!retval) {
                v9fs_refresh_inode(oldfid, d_inode(old_dentry));
                v9fs_invalidate_inode_attr(dir);
        }
-clunk_fid:
        p9_client_clunk(oldfid);
        return retval;
 }
@@ -1425,7 +1362,7 @@ v9fs_vfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t rde
 {
        struct v9fs_session_info *v9ses = v9fs_inode2v9ses(dir);
        int retval;
-       char *name;
+       char name[2 + U32_MAX_DIGITS + 1 + U32_MAX_DIGITS + 1];
        u32 perm;
 
        p9_debug(P9_DEBUG_VFS, " %lu,%pd mode: %hx MAJOR: %u MINOR: %u\n",
@@ -1435,26 +1372,16 @@ v9fs_vfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t rde
        if (!new_valid_dev(rdev))
                return -EINVAL;
 
-       name = __getname();
-       if (!name)
-               return -ENOMEM;
        /* build extension */
        if (S_ISBLK(mode))
                sprintf(name, "b %u %u", MAJOR(rdev), MINOR(rdev));
        else if (S_ISCHR(mode))
                sprintf(name, "c %u %u", MAJOR(rdev), MINOR(rdev));
-       else if (S_ISFIFO(mode))
-               *name = 0;
-       else if (S_ISSOCK(mode))
+       else
                *name = 0;
-       else {
-               __putname(name);
-               return -EINVAL;
-       }
 
        perm = unixmode2p9mode(v9ses, mode);
        retval = v9fs_vfs_mkspecial(dir, dentry, perm, name);
-       __putname(name);
 
        return retval;
 }
@@ -1530,7 +1457,7 @@ static const struct inode_operations v9fs_file_inode_operations = {
 static const struct inode_operations v9fs_symlink_inode_operations = {
        .readlink = generic_readlink,
        .follow_link = v9fs_vfs_follow_link,
-       .put_link = v9fs_vfs_put_link,
+       .put_link = kfree_put_link,
        .getattr = v9fs_vfs_getattr,
        .setattr = v9fs_vfs_setattr,
 };
index 9861c7c951a6dbd293e78d84e190f0908b4a1921..09e4433717b8795c2c7c8c76452887ec27a91be4 100644 (file)
@@ -905,41 +905,24 @@ error:
 /**
  * v9fs_vfs_follow_link_dotl - follow a symlink path
  * @dentry: dentry for symlink
- * @nd: nameidata
- *
+ * @cookie: place to pass the data to put_link()
  */
 
-static void *
-v9fs_vfs_follow_link_dotl(struct dentry *dentry, struct nameidata *nd)
+static const char *
+v9fs_vfs_follow_link_dotl(struct dentry *dentry, void **cookie)
 {
-       int retval;
-       struct p9_fid *fid;
-       char *link = __getname();
+       struct p9_fid *fid = v9fs_fid_lookup(dentry);
        char *target;
+       int retval;
 
        p9_debug(P9_DEBUG_VFS, "%pd\n", dentry);
 
-       if (!link) {
-               link = ERR_PTR(-ENOMEM);
-               goto ndset;
-       }
-       fid = v9fs_fid_lookup(dentry);
-       if (IS_ERR(fid)) {
-               __putname(link);
-               link = ERR_CAST(fid);
-               goto ndset;
-       }
+       if (IS_ERR(fid))
+               return ERR_CAST(fid);
        retval = p9_client_readlink(fid, &target);
-       if (!retval) {
-               strcpy(link, target);
-               kfree(target);
-               goto ndset;
-       }
-       __putname(link);
-       link = ERR_PTR(retval);
-ndset:
-       nd_set_link(nd, link);
-       return NULL;
+       if (retval)
+               return ERR_PTR(retval);
+       return *cookie = target;
 }
 
 int v9fs_refresh_inode_dotl(struct p9_fid *fid, struct inode *inode)
@@ -1006,7 +989,7 @@ const struct inode_operations v9fs_file_inode_operations_dotl = {
 const struct inode_operations v9fs_symlink_inode_operations_dotl = {
        .readlink = generic_readlink,
        .follow_link = v9fs_vfs_follow_link_dotl,
-       .put_link = v9fs_vfs_put_link,
+       .put_link = kfree_put_link,
        .getattr = v9fs_vfs_getattr_dotl,
        .setattr = v9fs_vfs_setattr_dotl,
        .setxattr = generic_setxattr,
index de58cc7b8076178605cea8f776031945d7d6da62..da0c33481bc0387788bcf4ce1792b38e141804e4 100644 (file)
 
 #include "autofs_i.h"
 
-static void *autofs4_follow_link(struct dentry *dentry, struct nameidata *nd)
+static const char *autofs4_follow_link(struct dentry *dentry, void **cookie)
 {
        struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb);
        struct autofs_info *ino = autofs4_dentry_ino(dentry);
        if (ino && !autofs4_oz_mode(sbi))
                ino->last_used = jiffies;
-       nd_set_link(nd, d_inode(dentry)->i_private);
-       return NULL;
+       return d_inode(dentry)->i_private;
 }
 
 const struct inode_operations autofs4_symlink_inode_operations = {
index 7943533c386802dc880051cc7ac2b078cf5d3ccb..46aedacfa6a8d4131563a83a8402daf89d590ddb 100644 (file)
@@ -42,8 +42,7 @@ static struct inode *befs_iget(struct super_block *, unsigned long);
 static struct inode *befs_alloc_inode(struct super_block *sb);
 static void befs_destroy_inode(struct inode *inode);
 static void befs_destroy_inodecache(void);
-static void *befs_follow_link(struct dentry *, struct nameidata *);
-static void *befs_fast_follow_link(struct dentry *, struct nameidata *);
+static const char *befs_follow_link(struct dentry *, void **);
 static int befs_utf2nls(struct super_block *sb, const char *in, int in_len,
                        char **out, int *out_len);
 static int befs_nls2utf(struct super_block *sb, const char *in, int in_len,
@@ -80,11 +79,6 @@ static const struct address_space_operations befs_aops = {
        .bmap           = befs_bmap,
 };
 
-static const struct inode_operations befs_fast_symlink_inode_operations = {
-       .readlink       = generic_readlink,
-       .follow_link    = befs_fast_follow_link,
-};
-
 static const struct inode_operations befs_symlink_inode_operations = {
        .readlink       = generic_readlink,
        .follow_link    = befs_follow_link,
@@ -403,10 +397,12 @@ static struct inode *befs_iget(struct super_block *sb, unsigned long ino)
                inode->i_op = &befs_dir_inode_operations;
                inode->i_fop = &befs_dir_operations;
        } else if (S_ISLNK(inode->i_mode)) {
-               if (befs_ino->i_flags & BEFS_LONG_SYMLINK)
+               if (befs_ino->i_flags & BEFS_LONG_SYMLINK) {
                        inode->i_op = &befs_symlink_inode_operations;
-               else
-                       inode->i_op = &befs_fast_symlink_inode_operations;
+               } else {
+                       inode->i_link = befs_ino->i_data.symlink;
+                       inode->i_op = &simple_symlink_inode_operations;
+               }
        } else {
                befs_error(sb, "Inode %lu is not a regular file, "
                           "directory or symlink. THAT IS WRONG! BeFS has no "
@@ -467,8 +463,8 @@ befs_destroy_inodecache(void)
  * The data stream become link name. Unless the LONG_SYMLINK
  * flag is set.
  */
-static void *
-befs_follow_link(struct dentry *dentry, struct nameidata *nd)
+static const char *
+befs_follow_link(struct dentry *dentry, void **cookie)
 {
        struct super_block *sb = dentry->d_sb;
        struct befs_inode_info *befs_ino = BEFS_I(d_inode(dentry));
@@ -478,33 +474,20 @@ befs_follow_link(struct dentry *dentry, struct nameidata *nd)
 
        if (len == 0) {
                befs_error(sb, "Long symlink with illegal length");
-               link = ERR_PTR(-EIO);
-       } else {
-               befs_debug(sb, "Follow long symlink");
-
-               link = kmalloc(len, GFP_NOFS);
-               if (!link) {
-                       link = ERR_PTR(-ENOMEM);
-               } else if (befs_read_lsymlink(sb, data, link, len) != len) {
-                       kfree(link);
-                       befs_error(sb, "Failed to read entire long symlink");
-                       link = ERR_PTR(-EIO);
-               } else {
-                       link[len - 1] = '\0';
-               }
+               return ERR_PTR(-EIO);
        }
-       nd_set_link(nd, link);
-       return NULL;
-}
-
-
-static void *
-befs_fast_follow_link(struct dentry *dentry, struct nameidata *nd)
-{
-       struct befs_inode_info *befs_ino = BEFS_I(d_inode(dentry));
+       befs_debug(sb, "Follow long symlink");
 
-       nd_set_link(nd, befs_ino->i_data.symlink);
-       return NULL;
+       link = kmalloc(len, GFP_NOFS);
+       if (!link)
+               return ERR_PTR(-ENOMEM);
+       if (befs_read_lsymlink(sb, data, link, len) != len) {
+               kfree(link);
+               befs_error(sb, "Failed to read entire long symlink");
+               return ERR_PTR(-EIO);
+       }
+       link[len - 1] = '\0';
+       return *cookie = link;
 }
 
 /*
index e876e1944519a330a2cc1f44e33a031139a35438..571acd88606cfcec3d01fc4a6ef453f0b49e9713 100644 (file)
@@ -6,7 +6,6 @@
 #include <linux/string.h>
 #include <linux/uaccess.h>
 #include <linux/kernel.h>
-#include <linux/namei.h>
 #include <linux/writeback.h>
 #include <linux/vmalloc.h>
 #include <linux/posix_acl.h>
@@ -819,6 +818,7 @@ static int fill_inode(struct inode *inode, struct page *locked_page,
                        else
                                kfree(sym); /* lost a race */
                }
+               inode->i_link = ci->i_symlink;
                break;
        case S_IFDIR:
                inode->i_op = &ceph_dir_iops;
@@ -1691,16 +1691,9 @@ retry:
 /*
  * symlinks
  */
-static void *ceph_sym_follow_link(struct dentry *dentry, struct nameidata *nd)
-{
-       struct ceph_inode_info *ci = ceph_inode(d_inode(dentry));
-       nd_set_link(nd, ci->i_symlink);
-       return NULL;
-}
-
 static const struct inode_operations ceph_symlink_iops = {
        .readlink = generic_readlink,
-       .follow_link = ceph_sym_follow_link,
+       .follow_link = simple_follow_link,
        .setattr = ceph_setattr,
        .getattr = ceph_getattr,
        .setxattr = ceph_setxattr,
index 252f5c15806bc2f18f5c1c10ff7c2bde0aedba6d..a782b22904e40b71387d844a6a7879bab8191a88 100644 (file)
@@ -120,7 +120,7 @@ extern struct vfsmount *cifs_dfs_d_automount(struct path *path);
 #endif
 
 /* Functions related to symlinks */
-extern void *cifs_follow_link(struct dentry *direntry, struct nameidata *nd);
+extern const char *cifs_follow_link(struct dentry *direntry, void **cookie);
 extern int cifs_readlink(struct dentry *direntry, char __user *buffer,
                         int buflen);
 extern int cifs_symlink(struct inode *inode, struct dentry *direntry,
index e6c707cc62b39b445b4b374eeec51a5be4fe07f4..e3548f73bdeaa980ef1c282246688e1e3f5f21e8 100644 (file)
@@ -626,8 +626,8 @@ cifs_hl_exit:
        return rc;
 }
 
-void *
-cifs_follow_link(struct dentry *direntry, struct nameidata *nd)
+const char *
+cifs_follow_link(struct dentry *direntry, void **cookie)
 {
        struct inode *inode = d_inode(direntry);
        int rc = -ENOMEM;
@@ -643,16 +643,18 @@ cifs_follow_link(struct dentry *direntry, struct nameidata *nd)
 
        tlink = cifs_sb_tlink(cifs_sb);
        if (IS_ERR(tlink)) {
-               rc = PTR_ERR(tlink);
-               tlink = NULL;
-               goto out;
+               free_xid(xid);
+               return ERR_CAST(tlink);
        }
        tcon = tlink_tcon(tlink);
        server = tcon->ses->server;
 
        full_path = build_path_from_dentry(direntry);
-       if (!full_path)
-               goto out;
+       if (!full_path) {
+               free_xid(xid);
+               cifs_put_tlink(tlink);
+               return ERR_PTR(-ENOMEM);
+       }
 
        cifs_dbg(FYI, "Full path: %s inode = 0x%p\n", full_path, inode);
 
@@ -670,17 +672,13 @@ cifs_follow_link(struct dentry *direntry, struct nameidata *nd)
                                                &target_path, cifs_sb);
 
        kfree(full_path);
-out:
+       free_xid(xid);
+       cifs_put_tlink(tlink);
        if (rc != 0) {
                kfree(target_path);
-               target_path = ERR_PTR(rc);
+               return ERR_PTR(rc);
        }
-
-       free_xid(xid);
-       if (tlink)
-               cifs_put_tlink(tlink);
-       nd_set_link(nd, target_path);
-       return NULL;
+       return *cookie = target_path;
 }
 
 int
index cc9f2546ea4a041273654b6076d1fbc774d447e4..ec5c8325b503d1a1602863769ae43c067d13a047 100644 (file)
@@ -279,36 +279,27 @@ static int configfs_getlink(struct dentry *dentry, char * path)
 
 }
 
-static void *configfs_follow_link(struct dentry *dentry, struct nameidata *nd)
+static const char *configfs_follow_link(struct dentry *dentry, void **cookie)
 {
-       int error = -ENOMEM;
        unsigned long page = get_zeroed_page(GFP_KERNEL);
+       int error;
 
-       if (page) {
-               error = configfs_getlink(dentry, (char *)page);
-               if (!error) {
-                       nd_set_link(nd, (char *)page);
-                       return (void *)page;
-               }
-       }
-
-       nd_set_link(nd, ERR_PTR(error));
-       return NULL;
-}
+       if (!page)
+               return ERR_PTR(-ENOMEM);
 
-static void configfs_put_link(struct dentry *dentry, struct nameidata *nd,
-                             void *cookie)
-{
-       if (cookie) {
-               unsigned long page = (unsigned long)cookie;
-               free_page(page);
+       error = configfs_getlink(dentry, (char *)page);
+       if (!error) {
+               return *cookie = (void *)page;
        }
+
+       free_page(page);
+       return ERR_PTR(error);
 }
 
 const struct inode_operations configfs_symlink_inode_operations = {
        .follow_link = configfs_follow_link,
        .readlink = generic_readlink,
-       .put_link = configfs_put_link,
+       .put_link = free_page_put_link,
        .setattr = configfs_setattr,
 };
 
index 37b5afdaf6989e211151cc55a7fa656a6addd364..592c4b582495b515c52a2aa3458be3422953c111 100644 (file)
@@ -322,17 +322,17 @@ static void dentry_free(struct dentry *dentry)
 }
 
 /**
- * dentry_rcuwalk_barrier - invalidate in-progress rcu-walk lookups
+ * dentry_rcuwalk_invalidate - invalidate in-progress rcu-walk lookups
  * @dentry: the target dentry
  * After this call, in-progress rcu-walk path lookup will fail. This
  * should be called after unhashing, and after changing d_inode (if
  * the dentry has not already been unhashed).
  */
-static inline void dentry_rcuwalk_barrier(struct dentry *dentry)
+static inline void dentry_rcuwalk_invalidate(struct dentry *dentry)
 {
-       assert_spin_locked(&dentry->d_lock);
-       /* Go through a barrier */
-       write_seqcount_barrier(&dentry->d_seq);
+       lockdep_assert_held(&dentry->d_lock);
+       /* Go through am invalidation barrier */
+       write_seqcount_invalidate(&dentry->d_seq);
 }
 
 /*
@@ -372,7 +372,7 @@ static void dentry_unlink_inode(struct dentry * dentry)
        struct inode *inode = dentry->d_inode;
        __d_clear_type_and_inode(dentry);
        hlist_del_init(&dentry->d_u.d_alias);
-       dentry_rcuwalk_barrier(dentry);
+       dentry_rcuwalk_invalidate(dentry);
        spin_unlock(&dentry->d_lock);
        spin_unlock(&inode->i_lock);
        if (!inode->i_nlink)
@@ -494,7 +494,7 @@ void __d_drop(struct dentry *dentry)
                __hlist_bl_del(&dentry->d_hash);
                dentry->d_hash.pprev = NULL;
                hlist_bl_unlock(b);
-               dentry_rcuwalk_barrier(dentry);
+               dentry_rcuwalk_invalidate(dentry);
        }
 }
 EXPORT_SYMBOL(__d_drop);
@@ -1752,7 +1752,7 @@ static void __d_instantiate(struct dentry *dentry, struct inode *inode)
        if (inode)
                hlist_add_head(&dentry->d_u.d_alias, &inode->i_dentry);
        __d_set_inode_and_type(dentry, inode, add_flags);
-       dentry_rcuwalk_barrier(dentry);
+       dentry_rcuwalk_invalidate(dentry);
        spin_unlock(&dentry->d_lock);
        fsnotify_d_instantiate(dentry, inode);
 }
index 830a7e76f5c64067e46fad8fd368e9112ddae7a9..284f9aa0028b8dd46b9897ababc5e826c185c608 100644 (file)
@@ -17,7 +17,6 @@
 #include <linux/fs.h>
 #include <linux/seq_file.h>
 #include <linux/pagemap.h>
-#include <linux/namei.h>
 #include <linux/debugfs.h>
 #include <linux/io.h>
 #include <linux/slab.h>
@@ -43,17 +42,6 @@ const struct file_operations debugfs_file_operations = {
        .llseek =       noop_llseek,
 };
 
-static void *debugfs_follow_link(struct dentry *dentry, struct nameidata *nd)
-{
-       nd_set_link(nd, d_inode(dentry)->i_private);
-       return NULL;
-}
-
-const struct inode_operations debugfs_link_operations = {
-       .readlink       = generic_readlink,
-       .follow_link    = debugfs_follow_link,
-};
-
 static int debugfs_u8_set(void *data, u64 val)
 {
        *(u8 *)data = val;
index c1e7ffb0dab658ecd21c449bf36467b14e0b75d6..7eaec88ea970d1a6ea8422465857deaefd2b7052 100644 (file)
@@ -174,7 +174,7 @@ static void debugfs_evict_inode(struct inode *inode)
        truncate_inode_pages_final(&inode->i_data);
        clear_inode(inode);
        if (S_ISLNK(inode->i_mode))
-               kfree(inode->i_private);
+               kfree(inode->i_link);
 }
 
 static const struct super_operations debugfs_super_operations = {
@@ -511,8 +511,8 @@ struct dentry *debugfs_create_symlink(const char *name, struct dentry *parent,
                return failed_creating(dentry);
        }
        inode->i_mode = S_IFLNK | S_IRWXUGO;
-       inode->i_op = &debugfs_link_operations;
-       inode->i_private = link;
+       inode->i_op = &simple_symlink_inode_operations;
+       inode->i_link = link;
        d_instantiate(dentry, inode);
        return end_creating(dentry);
 }
index fc850b55db67a27a99663596e1e8c711c8d71237..3c4db1172d222840b8cf0fcd50a61437ebf5f4c7 100644 (file)
@@ -170,7 +170,6 @@ out_unlock:
  * @directory_inode: inode of the new file's dentry's parent in ecryptfs
  * @ecryptfs_dentry: New file's dentry in ecryptfs
  * @mode: The mode of the new file
- * @nd: nameidata of ecryptfs' parent's dentry & vfsmount
  *
  * Creates the underlying file and the eCryptfs inode which will link to
  * it. It will also update the eCryptfs directory inode to mimic the
@@ -384,7 +383,7 @@ static int ecryptfs_lookup_interpose(struct dentry *dentry,
  * ecryptfs_lookup
  * @ecryptfs_dir_inode: The eCryptfs directory inode
  * @ecryptfs_dentry: The eCryptfs dentry that we are looking up
- * @ecryptfs_nd: nameidata; may be NULL
+ * @flags: lookup flags
  *
  * Find a file on disk. If the file does not exist, then we'll add it to the
  * dentry cache and continue on to read it from the disk.
@@ -675,18 +674,16 @@ out:
        return rc ? ERR_PTR(rc) : buf;
 }
 
-static void *ecryptfs_follow_link(struct dentry *dentry, struct nameidata *nd)
+static const char *ecryptfs_follow_link(struct dentry *dentry, void **cookie)
 {
        size_t len;
        char *buf = ecryptfs_readlink_lower(dentry, &len);
        if (IS_ERR(buf))
-               goto out;
+               return buf;
        fsstack_copy_attr_atime(d_inode(dentry),
                                d_inode(ecryptfs_dentry_to_lower(dentry)));
        buf[len] = '\0';
-out:
-       nd_set_link(nd, buf);
-       return NULL;
+       return *cookie = buf;
 }
 
 /**
index b47c7b8dc275429e87b1b35fe0a36af2e820cdf3..a364fd0965ec6a35e27c386b22916a20de9469b3 100644 (file)
@@ -16,5 +16,5 @@
 libore-y := ore.o ore_raid.o
 obj-$(CONFIG_ORE) += libore.o
 
-exofs-y := inode.o file.o symlink.o namei.o dir.o super.o sys.o
+exofs-y := inode.o file.o namei.o dir.o super.o sys.o
 obj-$(CONFIG_EXOFS_FS) += exofs.o
index ad9cac670a470d163001c7aa2227db46afc607d3..2e86086bc9403efe99a25ab0df439db0c714eb4e 100644 (file)
@@ -207,10 +207,6 @@ extern const struct address_space_operations exofs_aops;
 extern const struct inode_operations exofs_dir_inode_operations;
 extern const struct inode_operations exofs_special_inode_operations;
 
-/* symlink.c         */
-extern const struct inode_operations exofs_symlink_inode_operations;
-extern const struct inode_operations exofs_fast_symlink_inode_operations;
-
 /* exofs_init_comps will initialize an ore_components device array
  * pointing to a single ore_comp struct, and a round-robin view
  * of the device table.
index 786e4cc8c889cc8903f7d734ee3e255c0aeb24f8..73c64daa0f5517b4ff8271bd17e25740bb83d506 100644 (file)
@@ -1222,10 +1222,11 @@ struct inode *exofs_iget(struct super_block *sb, unsigned long ino)
                inode->i_fop = &exofs_dir_operations;
                inode->i_mapping->a_ops = &exofs_aops;
        } else if (S_ISLNK(inode->i_mode)) {
-               if (exofs_inode_is_fast_symlink(inode))
-                       inode->i_op = &exofs_fast_symlink_inode_operations;
-               else {
-                       inode->i_op = &exofs_symlink_inode_operations;
+               if (exofs_inode_is_fast_symlink(inode)) {
+                       inode->i_op = &simple_symlink_inode_operations;
+                       inode->i_link = (char *)oi->i_data;
+               } else {
+                       inode->i_op = &page_symlink_inode_operations;
                        inode->i_mapping->a_ops = &exofs_aops;
                }
        } else {
index 5ae25e43119185e04d19a287e534921cc61bbc1c..09a6bb1ad63c840b91ef56114871753454046292 100644 (file)
@@ -113,7 +113,7 @@ static int exofs_symlink(struct inode *dir, struct dentry *dentry,
        oi = exofs_i(inode);
        if (l > sizeof(oi->i_data)) {
                /* slow symlink */
-               inode->i_op = &exofs_symlink_inode_operations;
+               inode->i_op = &page_symlink_inode_operations;
                inode->i_mapping->a_ops = &exofs_aops;
                memset(oi->i_data, 0, sizeof(oi->i_data));
 
@@ -122,7 +122,8 @@ static int exofs_symlink(struct inode *dir, struct dentry *dentry,
                        goto out_fail;
        } else {
                /* fast symlink */
-               inode->i_op = &exofs_fast_symlink_inode_operations;
+               inode->i_op = &simple_symlink_inode_operations;
+               inode->i_link = (char *)oi->i_data;
                memcpy(oi->i_data, symname, l);
                inode->i_size = l-1;
        }
diff --git a/fs/exofs/symlink.c b/fs/exofs/symlink.c
deleted file mode 100644 (file)
index 6f6f3a4..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2005, 2006
- * Avishay Traeger (avishay@gmail.com)
- * Copyright (C) 2008, 2009
- * Boaz Harrosh <ooo@electrozaur.com>
- *
- * Copyrights for code taken from ext2:
- *     Copyright (C) 1992, 1993, 1994, 1995
- *     Remy Card (card@masi.ibp.fr)
- *     Laboratoire MASI - Institut Blaise Pascal
- *     Universite Pierre et Marie Curie (Paris VI)
- *     from
- *     linux/fs/minix/inode.c
- *     Copyright (C) 1991, 1992  Linus Torvalds
- *
- * This file is part of exofs.
- *
- * exofs 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.  Since it is based on ext2, and the only
- * valid version of GPL for the Linux kernel is version 2, the only valid
- * version of GPL for exofs is version 2.
- *
- * exofs 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 exofs; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
- */
-
-#include <linux/namei.h>
-
-#include "exofs.h"
-
-static void *exofs_follow_link(struct dentry *dentry, struct nameidata *nd)
-{
-       struct exofs_i_info *oi = exofs_i(d_inode(dentry));
-
-       nd_set_link(nd, (char *)oi->i_data);
-       return NULL;
-}
-
-const struct inode_operations exofs_symlink_inode_operations = {
-       .readlink       = generic_readlink,
-       .follow_link    = page_follow_link_light,
-       .put_link       = page_put_link,
-};
-
-const struct inode_operations exofs_fast_symlink_inode_operations = {
-       .readlink       = generic_readlink,
-       .follow_link    = exofs_follow_link,
-};
index f460ae36d5b78addfd9cc1a6eb6c4287887a946e..5c09776d347fc363c4f456862eb2361d717e46bd 100644 (file)
@@ -1403,6 +1403,7 @@ struct inode *ext2_iget (struct super_block *sb, unsigned long ino)
                        inode->i_mapping->a_ops = &ext2_aops;
        } else if (S_ISLNK(inode->i_mode)) {
                if (ext2_inode_is_fast_symlink(inode)) {
+                       inode->i_link = (char *)ei->i_data;
                        inode->i_op = &ext2_fast_symlink_inode_operations;
                        nd_terminate_link(ei->i_data, inode->i_size,
                                sizeof(ei->i_data) - 1);
index 3e074a9ccbe6dd048c288ae8162229b0af26d176..13ec54a99c962a85bc628732102fdb554f750d1c 100644 (file)
@@ -189,7 +189,8 @@ static int ext2_symlink (struct inode * dir, struct dentry * dentry,
        } else {
                /* fast symlink */
                inode->i_op = &ext2_fast_symlink_inode_operations;
-               memcpy((char*)(EXT2_I(inode)->i_data),symname,l);
+               inode->i_link = (char*)EXT2_I(inode)->i_data;
+               memcpy(inode->i_link, symname, l);
                inode->i_size = l-1;
        }
        mark_inode_dirty(inode);
index 20608f17c2e5144ed6283e6afe7f23dae6f51031..ae17179f3810b2dd635c81203643a14f8f4c0c10 100644 (file)
 
 #include "ext2.h"
 #include "xattr.h"
-#include <linux/namei.h>
-
-static void *ext2_follow_link(struct dentry *dentry, struct nameidata *nd)
-{
-       struct ext2_inode_info *ei = EXT2_I(d_inode(dentry));
-       nd_set_link(nd, (char *)ei->i_data);
-       return NULL;
-}
 
 const struct inode_operations ext2_symlink_inode_operations = {
        .readlink       = generic_readlink,
@@ -43,7 +35,7 @@ const struct inode_operations ext2_symlink_inode_operations = {
  
 const struct inode_operations ext2_fast_symlink_inode_operations = {
        .readlink       = generic_readlink,
-       .follow_link    = ext2_follow_link,
+       .follow_link    = simple_follow_link,
        .setattr        = ext2_setattr,
 #ifdef CONFIG_EXT2_FS_XATTR
        .setxattr       = generic_setxattr,
index 2ee2dc4351d1630b375da3b9aaa062840ce1afc9..6c7e5468a2f807d68e48b7c43b13a0626a5f4aee 100644 (file)
@@ -2999,6 +2999,7 @@ struct inode *ext3_iget(struct super_block *sb, unsigned long ino)
                        inode->i_op = &ext3_fast_symlink_inode_operations;
                        nd_terminate_link(ei->i_data, inode->i_size,
                                sizeof(ei->i_data) - 1);
+                       inode->i_link = (char *)ei->i_data;
                } else {
                        inode->i_op = &ext3_symlink_inode_operations;
                        ext3_set_aops(inode);
index 4264b9bd0002f199593308feaf7906292311e42f..c9e767cd4b67991e3ecf4bebc77b8eaa6be2f752 100644 (file)
@@ -2308,7 +2308,8 @@ retry:
                }
        } else {
                inode->i_op = &ext3_fast_symlink_inode_operations;
-               memcpy((char*)&EXT3_I(inode)->i_data,symname,l);
+               inode->i_link = (char*)&EXT3_I(inode)->i_data;
+               memcpy(inode->i_link, symname, l);
                inode->i_size = l-1;
        }
        EXT3_I(inode)->i_disksize = inode->i_size;
index ea96df3c58db199915e6e9b60b1d7beb9b931150..c08c59094ae61f3172c58e237477a7cb7518e50f 100644 (file)
  *  ext3 symlink handling code
  */
 
-#include <linux/namei.h>
 #include "ext3.h"
 #include "xattr.h"
 
-static void * ext3_follow_link(struct dentry *dentry, struct nameidata *nd)
-{
-       struct ext3_inode_info *ei = EXT3_I(d_inode(dentry));
-       nd_set_link(nd, (char*)ei->i_data);
-       return NULL;
-}
-
 const struct inode_operations ext3_symlink_inode_operations = {
        .readlink       = generic_readlink,
        .follow_link    = page_follow_link_light,
@@ -43,7 +35,7 @@ const struct inode_operations ext3_symlink_inode_operations = {
 
 const struct inode_operations ext3_fast_symlink_inode_operations = {
        .readlink       = generic_readlink,
-       .follow_link    = ext3_follow_link,
+       .follow_link    = simple_follow_link,
        .setattr        = ext3_setattr,
 #ifdef CONFIG_EXT3_FS_XATTR
        .setxattr       = generic_setxattr,
index 9a83f149ac85525b821a4d24ba5387b93b3229d6..0a3b72d1d458bd68834465a70fff3d323959f997 100644 (file)
@@ -2847,6 +2847,7 @@ extern int ext4_mpage_readpages(struct address_space *mapping,
                                unsigned nr_pages);
 
 /* symlink.c */
+extern const struct inode_operations ext4_encrypted_symlink_inode_operations;
 extern const struct inode_operations ext4_symlink_inode_operations;
 extern const struct inode_operations ext4_fast_symlink_inode_operations;
 
index 0554b0b5957bb5db223534f2116d5eea18eae2d4..5168c9b568809d81f66cd4813bc25cba5d669fc9 100644 (file)
@@ -4213,8 +4213,11 @@ struct inode *ext4_iget(struct super_block *sb, unsigned long ino)
                inode->i_op = &ext4_dir_inode_operations;
                inode->i_fop = &ext4_dir_operations;
        } else if (S_ISLNK(inode->i_mode)) {
-               if (ext4_inode_is_fast_symlink(inode) &&
-                   !ext4_encrypted_inode(inode)) {
+               if (ext4_encrypted_inode(inode)) {
+                       inode->i_op = &ext4_encrypted_symlink_inode_operations;
+                       ext4_set_aops(inode);
+               } else if (ext4_inode_is_fast_symlink(inode)) {
+                       inode->i_link = (char *)ei->i_data;
                        inode->i_op = &ext4_fast_symlink_inode_operations;
                        nd_terminate_link(ei->i_data, inode->i_size,
                                sizeof(ei->i_data) - 1);
index 814f3beb436965f116b7555ee8cf9ac30c3f0165..5fdb9f6aa869445ca9893751393e4822b2e0ed63 100644 (file)
@@ -3206,10 +3206,12 @@ static int ext4_symlink(struct inode *dir,
                        goto err_drop_inode;
                sd->len = cpu_to_le16(ostr.len);
                disk_link.name = (char *) sd;
+               inode->i_op = &ext4_encrypted_symlink_inode_operations;
        }
 
        if ((disk_link.len > EXT4_N_BLOCKS * 4)) {
-               inode->i_op = &ext4_symlink_inode_operations;
+               if (!encryption_required)
+                       inode->i_op = &ext4_symlink_inode_operations;
                ext4_set_aops(inode);
                /*
                 * We cannot call page_symlink() with transaction started
@@ -3249,9 +3251,10 @@ static int ext4_symlink(struct inode *dir,
        } else {
                /* clear the extent format for fast symlink */
                ext4_clear_inode_flag(inode, EXT4_INODE_EXTENTS);
-               inode->i_op = encryption_required ?
-                       &ext4_symlink_inode_operations :
-                       &ext4_fast_symlink_inode_operations;
+               if (!encryption_required) {
+                       inode->i_op = &ext4_fast_symlink_inode_operations;
+                       inode->i_link = (char *)&EXT4_I(inode)->i_data;
+               }
                memcpy((char *)&EXT4_I(inode)->i_data, disk_link.name,
                       disk_link.len);
                inode->i_size = disk_link.len - 1;
index 187b789203142d6b444b264acd44798427626b41..ba5bd18a9825242fdfc9e8ee4cb05f1cc7cd05f7 100644 (file)
@@ -23,7 +23,7 @@
 #include "xattr.h"
 
 #ifdef CONFIG_EXT4_FS_ENCRYPTION
-static void *ext4_follow_link(struct dentry *dentry, struct nameidata *nd)
+static const char *ext4_follow_link(struct dentry *dentry, void **cookie)
 {
        struct page *cpage = NULL;
        char *caddr, *paddr = NULL;
@@ -35,12 +35,9 @@ static void *ext4_follow_link(struct dentry *dentry, struct nameidata *nd)
        int res;
        u32 plen, max_size = inode->i_sb->s_blocksize;
 
-       if (!ext4_encrypted_inode(inode))
-               return page_follow_link_light(dentry, nd);
-
        ctx = ext4_get_fname_crypto_ctx(inode, inode->i_sb->s_blocksize);
        if (IS_ERR(ctx))
-               return ctx;
+               return ERR_CAST(ctx);
 
        if (ext4_inode_is_fast_symlink(inode)) {
                caddr = (char *) EXT4_I(inode)->i_data;
@@ -49,7 +46,7 @@ static void *ext4_follow_link(struct dentry *dentry, struct nameidata *nd)
                cpage = read_mapping_page(inode->i_mapping, 0, NULL);
                if (IS_ERR(cpage)) {
                        ext4_put_fname_crypto_ctx(&ctx);
-                       return cpage;
+                       return ERR_CAST(cpage);
                }
                caddr = kmap(cpage);
                caddr[size] = 0;
@@ -80,13 +77,12 @@ static void *ext4_follow_link(struct dentry *dentry, struct nameidata *nd)
        /* Null-terminate the name */
        if (res <= plen)
                paddr[res] = '\0';
-       nd_set_link(nd, paddr);
        ext4_put_fname_crypto_ctx(&ctx);
        if (cpage) {
                kunmap(cpage);
                page_cache_release(cpage);
        }
-       return NULL;
+       return *cookie = paddr;
 errout:
        ext4_put_fname_crypto_ctx(&ctx);
        if (cpage) {
@@ -97,36 +93,22 @@ errout:
        return ERR_PTR(res);
 }
 
-static void ext4_put_link(struct dentry *dentry, struct nameidata *nd,
-                         void *cookie)
-{
-       struct page *page = cookie;
-
-       if (!page) {
-               kfree(nd_get_link(nd));
-       } else {
-               kunmap(page);
-               page_cache_release(page);
-       }
-}
+const struct inode_operations ext4_encrypted_symlink_inode_operations = {
+       .readlink       = generic_readlink,
+       .follow_link    = ext4_follow_link,
+       .put_link       = kfree_put_link,
+       .setattr        = ext4_setattr,
+       .setxattr       = generic_setxattr,
+       .getxattr       = generic_getxattr,
+       .listxattr      = ext4_listxattr,
+       .removexattr    = generic_removexattr,
+};
 #endif
 
-static void *ext4_follow_fast_link(struct dentry *dentry, struct nameidata *nd)
-{
-       struct ext4_inode_info *ei = EXT4_I(d_inode(dentry));
-       nd_set_link(nd, (char *) ei->i_data);
-       return NULL;
-}
-
 const struct inode_operations ext4_symlink_inode_operations = {
        .readlink       = generic_readlink,
-#ifdef CONFIG_EXT4_FS_ENCRYPTION
-       .follow_link    = ext4_follow_link,
-       .put_link       = ext4_put_link,
-#else
        .follow_link    = page_follow_link_light,
        .put_link       = page_put_link,
-#endif
        .setattr        = ext4_setattr,
        .setxattr       = generic_setxattr,
        .getxattr       = generic_getxattr,
@@ -136,7 +118,7 @@ const struct inode_operations ext4_symlink_inode_operations = {
 
 const struct inode_operations ext4_fast_symlink_inode_operations = {
        .readlink       = generic_readlink,
-       .follow_link    = ext4_follow_fast_link,
+       .follow_link    = simple_follow_link,
        .setattr        = ext4_setattr,
        .setxattr       = generic_setxattr,
        .getxattr       = generic_getxattr,
index 658e8079aaf9b9020068bd30aad723d168899309..71765d062914a515fc7603843cabd2b456d5a189 100644 (file)
@@ -296,19 +296,15 @@ fail:
        return err;
 }
 
-static void *f2fs_follow_link(struct dentry *dentry, struct nameidata *nd)
+static const char *f2fs_follow_link(struct dentry *dentry, void **cookie)
 {
-       struct page *page = page_follow_link_light(dentry, nd);
-
-       if (IS_ERR_OR_NULL(page))
-               return page;
-
-       /* this is broken symlink case */
-       if (*nd_get_link(nd) == 0) {
-               page_put_link(dentry, nd, page);
-               return ERR_PTR(-ENOENT);
+       const char *link = page_follow_link_light(dentry, cookie);
+       if (!IS_ERR(link) && !*link) {
+               /* this is broken symlink case */
+               page_put_link(NULL, *cookie);
+               link = ERR_PTR(-ENOENT);
        }
-       return page;
+       return link;
 }
 
 static int f2fs_symlink(struct inode *dir, struct dentry *dentry,
index 999ff5c3cab0edacd585447132180d5c35554e3c..d59712dfa3e701e86ff53609308e813cf8acf69e 100644 (file)
@@ -195,8 +195,9 @@ static int handle_to_path(int mountdirfd, struct file_handle __user *ufh,
                goto out_err;
        }
        /* copy the full handle */
-       if (copy_from_user(handle, ufh,
-                          sizeof(struct file_handle) +
+       *handle = f_handle;
+       if (copy_from_user(&handle->f_handle,
+                          &ufh->f_handle,
                           f_handle.handle_bytes)) {
                retval = -EFAULT;
                goto out_handle;
index 881aa3d217f007a76361ff1c23f77499bdab851e..e3dcb4467d92752af6980549fb740f97d11d1f47 100644 (file)
@@ -50,9 +50,6 @@ extern daddr_t                        vxfs_bmap1(struct inode *, long);
 /* vxfs_fshead.c */
 extern int                     vxfs_read_fshead(struct super_block *);
 
-/* vxfs_immed.c */
-extern const struct inode_operations vxfs_immed_symlink_iops;
-
 /* vxfs_inode.c */
 extern const struct address_space_operations vxfs_immed_aops;
 extern struct kmem_cache       *vxfs_inode_cachep;
index 8b9229e2ca5cb572976a839ca20f58e0514b6726..cb84f0fcc72a468c1366f498554ab4c79ac26830 100644 (file)
  */
 #include <linux/fs.h>
 #include <linux/pagemap.h>
-#include <linux/namei.h>
 
 #include "vxfs.h"
 #include "vxfs_extern.h"
 #include "vxfs_inode.h"
 
 
-static void *  vxfs_immed_follow_link(struct dentry *, struct nameidata *);
-
 static int     vxfs_immed_readpage(struct file *, struct page *);
 
-/*
- * Inode operations for immed symlinks.
- *
- * Unliked all other operations we do not go through the pagecache,
- * but do all work directly on the inode.
- */
-const struct inode_operations vxfs_immed_symlink_iops = {
-       .readlink =             generic_readlink,
-       .follow_link =          vxfs_immed_follow_link,
-};
-
 /*
  * Address space operations for immed files and directories.
  */
@@ -61,26 +47,6 @@ const struct address_space_operations vxfs_immed_aops = {
        .readpage =             vxfs_immed_readpage,
 };
 
-/**
- * vxfs_immed_follow_link - follow immed symlink
- * @dp:                dentry for the link
- * @np:                pathname lookup data for the current path walk
- *
- * Description:
- *   vxfs_immed_follow_link restarts the pathname lookup with
- *   the data obtained from @dp.
- *
- * Returns:
- *   Zero on success, else a negative error code.
- */
-static void *
-vxfs_immed_follow_link(struct dentry *dp, struct nameidata *np)
-{
-       struct vxfs_inode_info          *vip = VXFS_INO(d_inode(dp));
-       nd_set_link(np, vip->vii_immed.vi_immed);
-       return NULL;
-}
-
 /**
  * vxfs_immed_readpage - read part of an immed inode into pagecache
  * @file:      file context (unused)
index 363e3ae25f6b42c775f6c09f6251786555adeaa2..ef73ed674a27162917845b0507269bdf86b273da 100644 (file)
@@ -35,6 +35,7 @@
 #include <linux/pagemap.h>
 #include <linux/kernel.h>
 #include <linux/slab.h>
+#include <linux/namei.h>
 
 #include "vxfs.h"
 #include "vxfs_inode.h"
@@ -327,8 +328,10 @@ vxfs_iget(struct super_block *sbp, ino_t ino)
                        ip->i_op = &page_symlink_inode_operations;
                        ip->i_mapping->a_ops = &vxfs_aops;
                } else {
-                       ip->i_op = &vxfs_immed_symlink_iops;
-                       vip->vii_immed.vi_immed[ip->i_size] = '\0';
+                       ip->i_op = &simple_symlink_inode_operations;
+                       ip->i_link = vip->vii_immed.vi_immed;
+                       nd_terminate_link(ip->i_link, ip->i_size,
+                                         sizeof(vip->vii_immed.vi_immed) - 1);
                }
        } else
                init_special_inode(ip, ip->i_mode, old_decode_dev(vip->vii_rdev));
index 0572bca49f1546b3d9cd3b00fb7fc21f0369308b..5e2e08712d3ba614a46687d5688fc2f01cd835be 100644 (file)
@@ -1365,7 +1365,7 @@ static int fuse_readdir(struct file *file, struct dir_context *ctx)
        return err;
 }
 
-static char *read_link(struct dentry *dentry)
+static const char *fuse_follow_link(struct dentry *dentry, void **cookie)
 {
        struct inode *inode = d_inode(dentry);
        struct fuse_conn *fc = get_fuse_conn(inode);
@@ -1389,28 +1389,12 @@ static char *read_link(struct dentry *dentry)
                link = ERR_PTR(ret);
        } else {
                link[ret] = '\0';
+               *cookie = link;
        }
        fuse_invalidate_atime(inode);
        return link;
 }
 
-static void free_link(char *link)
-{
-       if (!IS_ERR(link))
-               free_page((unsigned long) link);
-}
-
-static void *fuse_follow_link(struct dentry *dentry, struct nameidata *nd)
-{
-       nd_set_link(nd, read_link(dentry));
-       return NULL;
-}
-
-static void fuse_put_link(struct dentry *dentry, struct nameidata *nd, void *c)
-{
-       free_link(nd_get_link(nd));
-}
-
 static int fuse_dir_open(struct inode *inode, struct file *file)
 {
        return fuse_open_common(inode, file, true);
@@ -1926,7 +1910,7 @@ static const struct inode_operations fuse_common_inode_operations = {
 static const struct inode_operations fuse_symlink_inode_operations = {
        .setattr        = fuse_setattr,
        .follow_link    = fuse_follow_link,
-       .put_link       = fuse_put_link,
+       .put_link       = free_page_put_link,
        .readlink       = generic_readlink,
        .getattr        = fuse_getattr,
        .setxattr       = fuse_setxattr,
index 1b3ca7a2e3fcfb807d4505d81bace6b698272bff..3a1461de1551d5765b7334747e69d2909ffb8d00 100644 (file)
@@ -1548,7 +1548,7 @@ out:
  * Returns: 0 on success or error code
  */
 
-static void *gfs2_follow_link(struct dentry *dentry, struct nameidata *nd)
+static const char *gfs2_follow_link(struct dentry *dentry, void **cookie)
 {
        struct gfs2_inode *ip = GFS2_I(d_inode(dentry));
        struct gfs2_holder i_gh;
@@ -1561,8 +1561,7 @@ static void *gfs2_follow_link(struct dentry *dentry, struct nameidata *nd)
        error = gfs2_glock_nq(&i_gh);
        if (error) {
                gfs2_holder_uninit(&i_gh);
-               nd_set_link(nd, ERR_PTR(error));
-               return NULL;
+               return ERR_PTR(error);
        }
 
        size = (unsigned int)i_size_read(&ip->i_inode);
@@ -1586,8 +1585,9 @@ static void *gfs2_follow_link(struct dentry *dentry, struct nameidata *nd)
        brelse(dibh);
 out:
        gfs2_glock_dq_uninit(&i_gh);
-       nd_set_link(nd, buf);
-       return NULL;
+       if (!IS_ERR(buf))
+               *cookie = buf;
+       return buf;
 }
 
 /**
index 07d8d8f52faf50d027f2f90a1699c7afa274aa34..059597b23f677b0959d8264b83cf4c4a2cec34b7 100644 (file)
@@ -892,7 +892,7 @@ static const struct inode_operations hostfs_dir_iops = {
        .setattr        = hostfs_setattr,
 };
 
-static void *hostfs_follow_link(struct dentry *dentry, struct nameidata *nd)
+static const char *hostfs_follow_link(struct dentry *dentry, void **cookie)
 {
        char *link = __getname();
        if (link) {
@@ -906,21 +906,18 @@ static void *hostfs_follow_link(struct dentry *dentry, struct nameidata *nd)
                }
                if (err < 0) {
                        __putname(link);
-                       link = ERR_PTR(err);
+                       return ERR_PTR(err);
                }
        } else {
-               link = ERR_PTR(-ENOMEM);
+               return ERR_PTR(-ENOMEM);
        }
 
-       nd_set_link(nd, link);
-       return NULL;
+       return *cookie = link;
 }
 
-static void hostfs_put_link(struct dentry *dentry, struct nameidata *nd, void *cookie)
+static void hostfs_put_link(struct inode *unused, void *cookie)
 {
-       char *s = nd_get_link(nd);
-       if (!IS_ERR(s))
-               __putname(s);
+       __putname(cookie);
 }
 
 static const struct inode_operations hostfs_link_iops = {
index fa2bd5366ecf1f4c3d9b81c2d80336a621831dcf..2867837909a91ba005af78ea3ba4b5191e13c1d5 100644 (file)
@@ -642,20 +642,19 @@ static int hppfs_readlink(struct dentry *dentry, char __user *buffer,
                                                    buflen);
 }
 
-static void *hppfs_follow_link(struct dentry *dentry, struct nameidata *nd)
+static const char *hppfs_follow_link(struct dentry *dentry, void **cookie)
 {
        struct dentry *proc_dentry = HPPFS_I(d_inode(dentry))->proc_dentry;
 
-       return d_inode(proc_dentry)->i_op->follow_link(proc_dentry, nd);
+       return d_inode(proc_dentry)->i_op->follow_link(proc_dentry, cookie);
 }
 
-static void hppfs_put_link(struct dentry *dentry, struct nameidata *nd,
-                          void *cookie)
+static void hppfs_put_link(struct inode *inode, void *cookie)
 {
-       struct dentry *proc_dentry = HPPFS_I(d_inode(dentry))->proc_dentry;
+       struct inode *proc_inode = d_inode(HPPFS_I(inode)->proc_dentry);
 
-       if (d_inode(proc_dentry)->i_op->put_link)
-               d_inode(proc_dentry)->i_op->put_link(proc_dentry, nd, cookie);
+       if (proc_inode->i_op->put_link)
+               proc_inode->i_op->put_link(proc_inode, cookie);
 }
 
 static const struct inode_operations hppfs_dir_iops = {
index ea37cd17b53f0c98b47e2e626b9ff2a9b6e5699b..e8d62688ed9181e511e2a0e8c6a5f36840cdbe94 100644 (file)
@@ -152,6 +152,7 @@ int inode_init_always(struct super_block *sb, struct inode *inode)
        inode->i_pipe = NULL;
        inode->i_bdev = NULL;
        inode->i_cdev = NULL;
+       inode->i_link = NULL;
        inode->i_rdev = 0;
        inode->dirtied_when = 0;
 
@@ -1584,36 +1585,47 @@ static int update_time(struct inode *inode, struct timespec *time, int flags)
  *     This function automatically handles read only file systems and media,
  *     as well as the "noatime" flag and inode specific "noatime" markers.
  */
-void touch_atime(const struct path *path)
+bool atime_needs_update(const struct path *path, struct inode *inode)
 {
        struct vfsmount *mnt = path->mnt;
-       struct inode *inode = d_inode(path->dentry);
        struct timespec now;
 
        if (inode->i_flags & S_NOATIME)
-               return;
+               return false;
        if (IS_NOATIME(inode))
-               return;
+               return false;
        if ((inode->i_sb->s_flags & MS_NODIRATIME) && S_ISDIR(inode->i_mode))
-               return;
+               return false;
 
        if (mnt->mnt_flags & MNT_NOATIME)
-               return;
+               return false;
        if ((mnt->mnt_flags & MNT_NODIRATIME) && S_ISDIR(inode->i_mode))
-               return;
+               return false;
 
        now = current_fs_time(inode->i_sb);
 
        if (!relatime_need_update(mnt, inode, now))
-               return;
+               return false;
 
        if (timespec_equal(&inode->i_atime, &now))
+               return false;
+
+       return true;
+}
+
+void touch_atime(const struct path *path)
+{
+       struct vfsmount *mnt = path->mnt;
+       struct inode *inode = d_inode(path->dentry);
+       struct timespec now;
+
+       if (!atime_needs_update(path, inode))
                return;
 
        if (!sb_start_write_trylock(inode->i_sb))
                return;
 
-       if (__mnt_want_write(mnt))
+       if (__mnt_want_write(mnt) != 0)
                goto skip_update;
        /*
         * File systems can error out when updating inodes if they need to
@@ -1624,6 +1636,7 @@ void touch_atime(const struct path *path)
         * We may also fail on filesystems that have the ability to make parts
         * of the fs read only, e.g. subvolumes in Btrfs.
         */
+       now = current_fs_time(inode->i_sb);
        update_time(inode, &now, S_ATIME);
        __mnt_drop_write(mnt);
 skip_update:
index 1ba5c97943b8751f0210870a7bb51636dd4e5ecd..81180022923fbd8ccd499d0b74af05b59695c302 100644 (file)
@@ -354,6 +354,7 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char
                ret = -ENOMEM;
                goto fail;
        }
+       inode->i_link = f->target;
 
        jffs2_dbg(1, "%s(): symlink's target '%s' cached\n",
                  __func__, (char *)f->target);
index fe5ea080b4ec810f29589b257f605389067dadf4..2caf1682036dc1914aaa887565b0a54edbe0ed35 100644 (file)
@@ -272,12 +272,9 @@ struct inode *jffs2_iget(struct super_block *sb, unsigned long ino)
        mutex_lock(&f->sem);
 
        ret = jffs2_do_read_inode(c, f, inode->i_ino, &latest_node);
+       if (ret)
+               goto error;
 
-       if (ret) {
-               mutex_unlock(&f->sem);
-               iget_failed(inode);
-               return ERR_PTR(ret);
-       }
        inode->i_mode = jemode_to_cpu(latest_node.mode);
        i_uid_write(inode, je16_to_cpu(latest_node.uid));
        i_gid_write(inode, je16_to_cpu(latest_node.gid));
@@ -294,6 +291,7 @@ struct inode *jffs2_iget(struct super_block *sb, unsigned long ino)
 
        case S_IFLNK:
                inode->i_op = &jffs2_symlink_inode_operations;
+               inode->i_link = f->target;
                break;
 
        case S_IFDIR:
index dddbde4f56f4e32a4c43caff1c4ab231c7106ce6..28e0aab42bc34f6a26f67b82bc3f8c94a9bf4232 100644 (file)
@@ -1203,17 +1203,13 @@ static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c,
                JFFS2_ERROR("failed to read from flash: error %d, %zd of %zd bytes read\n",
                        ret, retlen, sizeof(*latest_node));
                /* FIXME: If this fails, there seems to be a memory leak. Find it. */
-               mutex_unlock(&f->sem);
-               jffs2_do_clear_inode(c, f);
-               return ret?ret:-EIO;
+               return ret ? ret : -EIO;
        }
 
        crc = crc32(0, latest_node, sizeof(*latest_node)-8);
        if (crc != je32_to_cpu(latest_node->node_crc)) {
                JFFS2_ERROR("CRC failed for read_inode of inode %u at physical location 0x%x\n",
                        f->inocache->ino, ref_offset(rii.latest_ref));
-               mutex_unlock(&f->sem);
-               jffs2_do_clear_inode(c, f);
                return -EIO;
        }
 
@@ -1250,16 +1246,11 @@ static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c,
                         * keep in RAM to facilitate quick follow symlink
                         * operation. */
                        uint32_t csize = je32_to_cpu(latest_node->csize);
-                       if (csize > JFFS2_MAX_NAME_LEN) {
-                               mutex_unlock(&f->sem);
-                               jffs2_do_clear_inode(c, f);
+                       if (csize > JFFS2_MAX_NAME_LEN)
                                return -ENAMETOOLONG;
-                       }
                        f->target = kmalloc(csize + 1, GFP_KERNEL);
                        if (!f->target) {
                                JFFS2_ERROR("can't allocate %u bytes of memory for the symlink target path cache\n", csize);
-                               mutex_unlock(&f->sem);
-                               jffs2_do_clear_inode(c, f);
                                return -ENOMEM;
                        }
 
@@ -1271,8 +1262,6 @@ static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c,
                                        ret = -EIO;
                                kfree(f->target);
                                f->target = NULL;
-                               mutex_unlock(&f->sem);
-                               jffs2_do_clear_inode(c, f);
                                return ret;
                        }
 
@@ -1289,15 +1278,11 @@ static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c,
                if (f->metadata) {
                        JFFS2_ERROR("Argh. Special inode #%u with mode 0%o had metadata node\n",
                               f->inocache->ino, jemode_to_cpu(latest_node->mode));
-                       mutex_unlock(&f->sem);
-                       jffs2_do_clear_inode(c, f);
                        return -EIO;
                }
                if (!frag_first(&f->fragtree)) {
                        JFFS2_ERROR("Argh. Special inode #%u with mode 0%o has no fragments\n",
                               f->inocache->ino, jemode_to_cpu(latest_node->mode));
-                       mutex_unlock(&f->sem);
-                       jffs2_do_clear_inode(c, f);
                        return -EIO;
                }
                /* ASSERT: f->fraglist != NULL */
@@ -1305,8 +1290,6 @@ static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c,
                        JFFS2_ERROR("Argh. Special inode #%u with mode 0x%x had more than one node\n",
                               f->inocache->ino, jemode_to_cpu(latest_node->mode));
                        /* FIXME: Deal with it - check crc32, check for duplicate node, check times and discard the older one */
-                       mutex_unlock(&f->sem);
-                       jffs2_do_clear_inode(c, f);
                        return -EIO;
                }
                /* OK. We're happy */
@@ -1400,10 +1383,8 @@ int jffs2_do_crccheck_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *i
        f->inocache = ic;
 
        ret = jffs2_do_read_inode_internal(c, f, &n);
-       if (!ret) {
-               mutex_unlock(&f->sem);
-               jffs2_do_clear_inode(c, f);
-       }
+       mutex_unlock(&f->sem);
+       jffs2_do_clear_inode(c, f);
        jffs2_xattr_do_crccheck_inode(c, ic);
        kfree (f);
        return ret;
index 1fefa25d0fa586a14caae06efea7363794be38fc..8ce2f240125b39803b4ebf2d681b6a95d40c33f5 100644 (file)
@@ -9,58 +9,15 @@
  *
  */
 
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/kernel.h>
-#include <linux/fs.h>
-#include <linux/namei.h>
 #include "nodelist.h"
 
-static void *jffs2_follow_link(struct dentry *dentry, struct nameidata *nd);
-
 const struct inode_operations jffs2_symlink_inode_operations =
 {
        .readlink =     generic_readlink,
-       .follow_link =  jffs2_follow_link,
+       .follow_link =  simple_follow_link,
        .setattr =      jffs2_setattr,
        .setxattr =     jffs2_setxattr,
        .getxattr =     jffs2_getxattr,
        .listxattr =    jffs2_listxattr,
        .removexattr =  jffs2_removexattr
 };
-
-static void *jffs2_follow_link(struct dentry *dentry, struct nameidata *nd)
-{
-       struct jffs2_inode_info *f = JFFS2_INODE_INFO(d_inode(dentry));
-       char *p = (char *)f->target;
-
-       /*
-        * We don't acquire the f->sem mutex here since the only data we
-        * use is f->target.
-        *
-        * 1. If we are here the inode has already built and f->target has
-        * to point to the target path.
-        * 2. Nobody uses f->target (if the inode is symlink's inode). The
-        * exception is inode freeing function which frees f->target. But
-        * it can't be called while we are here and before VFS has
-        * stopped using our f->target string which we provide by means of
-        * nd_set_link() call.
-        */
-
-       if (!p) {
-               pr_err("%s(): can't find symlink target\n", __func__);
-               p = ERR_PTR(-EIO);
-       }
-       jffs2_dbg(1, "%s(): target path is '%s'\n",
-                 __func__, (char *)f->target);
-
-       nd_set_link(nd, p);
-
-       /*
-        * We will unlock the f->sem mutex but VFS will use the f->target string. This is safe
-        * since the only way that may cause f->target to be changed is iput() operation.
-        * But VFS will not use f->target after iput() has been called.
-        */
-       return NULL;
-}
-
index 070dc4b335449423091e67dd74c0f1c34617b041..6f1cb2b5ee285dd50622f719296fe71284d5f826 100644 (file)
@@ -63,11 +63,12 @@ struct inode *jfs_iget(struct super_block *sb, unsigned long ino)
                        inode->i_mapping->a_ops = &jfs_aops;
                } else {
                        inode->i_op = &jfs_fast_symlink_inode_operations;
+                       inode->i_link = JFS_IP(inode)->i_inline;
                        /*
                         * The inline data should be null-terminated, but
                         * don't let on-disk corruption crash the kernel
                         */
-                       JFS_IP(inode)->i_inline[inode->i_size] = '\0';
+                       inode->i_link[inode->i_size] = '\0';
                }
        } else {
                inode->i_op = &jfs_file_inode_operations;
index 66db7bc0ed1096050c2b2f93c3aa9e16a0ba4726..e33be921aa41b5ae56a4054605aaeea5b8065987 100644 (file)
@@ -880,7 +880,6 @@ static int jfs_symlink(struct inode *dip, struct dentry *dentry,
        int ssize;              /* source pathname size */
        struct btstack btstack;
        struct inode *ip = d_inode(dentry);
-       unchar *i_fastsymlink;
        s64 xlen = 0;
        int bmask = 0, xsize;
        s64 xaddr;
@@ -946,8 +945,8 @@ static int jfs_symlink(struct inode *dip, struct dentry *dentry,
        if (ssize <= IDATASIZE) {
                ip->i_op = &jfs_fast_symlink_inode_operations;
 
-               i_fastsymlink = JFS_IP(ip)->i_inline;
-               memcpy(i_fastsymlink, name, ssize);
+               ip->i_link = JFS_IP(ip)->i_inline;
+               memcpy(ip->i_link, name, ssize);
                ip->i_size = ssize - 1;
 
                /*
index 80f42bcc4ef1295669de10ea1087384f1c98c95b..5929e2363cb85eddc0d54bf3a04754383cb395db 100644 (file)
  */
 
 #include <linux/fs.h>
-#include <linux/namei.h>
 #include "jfs_incore.h"
 #include "jfs_inode.h"
 #include "jfs_xattr.h"
 
-static void *jfs_follow_link(struct dentry *dentry, struct nameidata *nd)
-{
-       char *s = JFS_IP(d_inode(dentry))->i_inline;
-       nd_set_link(nd, s);
-       return NULL;
-}
-
 const struct inode_operations jfs_fast_symlink_inode_operations = {
        .readlink       = generic_readlink,
-       .follow_link    = jfs_follow_link,
+       .follow_link    = simple_follow_link,
        .setattr        = jfs_setattr,
        .setxattr       = jfs_setxattr,
        .getxattr       = jfs_getxattr,
index 8a198898e39afd3ffde994cee7d732dcdfa8bdcd..db272528ab5bb01c192b5502650f29e0784663ce 100644 (file)
@@ -112,25 +112,18 @@ static int kernfs_getlink(struct dentry *dentry, char *path)
        return error;
 }
 
-static void *kernfs_iop_follow_link(struct dentry *dentry, struct nameidata *nd)
+static const char *kernfs_iop_follow_link(struct dentry *dentry, void **cookie)
 {
        int error = -ENOMEM;
        unsigned long page = get_zeroed_page(GFP_KERNEL);
-       if (page) {
-               error = kernfs_getlink(dentry, (char *) page);
-               if (error < 0)
-                       free_page((unsigned long)page);
-       }
-       nd_set_link(nd, error ? ERR_PTR(error) : (char *)page);
-       return NULL;
-}
-
-static void kernfs_iop_put_link(struct dentry *dentry, struct nameidata *nd,
-                               void *cookie)
-{
-       char *page = nd_get_link(nd);
-       if (!IS_ERR(page))
+       if (!page)
+               return ERR_PTR(-ENOMEM);
+       error = kernfs_getlink(dentry, (char *)page);
+       if (unlikely(error < 0)) {
                free_page((unsigned long)page);
+               return ERR_PTR(error);
+       }
+       return *cookie = (char *)page;
 }
 
 const struct inode_operations kernfs_symlink_iops = {
@@ -140,7 +133,7 @@ const struct inode_operations kernfs_symlink_iops = {
        .listxattr      = kernfs_iop_listxattr,
        .readlink       = generic_readlink,
        .follow_link    = kernfs_iop_follow_link,
-       .put_link       = kernfs_iop_put_link,
+       .put_link       = free_page_put_link,
        .setattr        = kernfs_iop_setattr,
        .getattr        = kernfs_iop_getattr,
        .permission     = kernfs_iop_permission,
index cb1fb4b9b6377b09b669b833cc3437b259b622f6..65e1feca8b982c55bff37e5a85529f8cb0d4121e 100644 (file)
@@ -1024,15 +1024,18 @@ int noop_fsync(struct file *file, loff_t start, loff_t end, int datasync)
 }
 EXPORT_SYMBOL(noop_fsync);
 
-void kfree_put_link(struct dentry *dentry, struct nameidata *nd,
-                               void *cookie)
+void kfree_put_link(struct inode *unused, void *cookie)
 {
-       char *s = nd_get_link(nd);
-       if (!IS_ERR(s))
-               kfree(s);
+       kfree(cookie);
 }
 EXPORT_SYMBOL(kfree_put_link);
 
+void free_page_put_link(struct inode *unused, void *cookie)
+{
+       free_page((unsigned long) cookie);
+}
+EXPORT_SYMBOL(free_page_put_link);
+
 /*
  * nop .set_page_dirty method so that people can use .page_mkwrite on
  * anon inodes.
@@ -1093,3 +1096,15 @@ simple_nosetlease(struct file *filp, long arg, struct file_lock **flp,
        return -EINVAL;
 }
 EXPORT_SYMBOL(simple_nosetlease);
+
+const char *simple_follow_link(struct dentry *dentry, void **cookie)
+{
+       return d_inode(dentry)->i_link;
+}
+EXPORT_SYMBOL(simple_follow_link);
+
+const struct inode_operations simple_symlink_inode_operations = {
+       .follow_link = simple_follow_link,
+       .readlink = generic_readlink
+};
+EXPORT_SYMBOL(simple_symlink_inode_operations);
index 4cf38f1185494115c0dcb8625f07b6593ae15fd5..f9b45d46d4c483ea0be1ceca4c35b9b3075b56b9 100644 (file)
@@ -779,6 +779,7 @@ fail:
 const struct inode_operations logfs_symlink_iops = {
        .readlink       = generic_readlink,
        .follow_link    = page_follow_link_light,
+       .put_link       = page_put_link,
 };
 
 const struct inode_operations logfs_dir_iops = {
index 6a61c2b3e385cfabcf53cdf13ed8ae9afa99ce1d..b5b8082bfa4208086a7ee06741cb76670d51bab9 100644 (file)
@@ -88,6 +88,7 @@ static inline int is_mounted(struct vfsmount *mnt)
 extern struct mount *__lookup_mnt(struct vfsmount *, struct dentry *);
 extern struct mount *__lookup_mnt_last(struct vfsmount *, struct dentry *);
 
+extern int __legitimize_mnt(struct vfsmount *, unsigned);
 extern bool legitimize_mnt(struct vfsmount *, unsigned);
 
 extern void __detach_mounts(struct dentry *dentry);
index fe30d3be43a8b381d3b9ac3016b28531996f91b7..2dad0eaf91d34d8f47d3cc525eafd45107d429bd 100644 (file)
@@ -492,6 +492,7 @@ void path_put(const struct path *path)
 }
 EXPORT_SYMBOL(path_put);
 
+#define EMBEDDED_LEVELS 2
 struct nameidata {
        struct path     path;
        struct qstr     last;
@@ -501,10 +502,139 @@ struct nameidata {
        unsigned        seq, m_seq;
        int             last_type;
        unsigned        depth;
-       struct file     *base;
-       char *saved_names[MAX_NESTED_LINKS + 1];
+       int             total_link_count;
+       struct saved {
+               struct path link;
+               void *cookie;
+               const char *name;
+               struct inode *inode;
+               unsigned seq;
+       } *stack, internal[EMBEDDED_LEVELS];
+       struct filename *name;
+       struct nameidata *saved;
+       unsigned        root_seq;
+       int             dfd;
 };
 
+static void set_nameidata(struct nameidata *p, int dfd, struct filename *name)
+{
+       struct nameidata *old = current->nameidata;
+       p->stack = p->internal;
+       p->dfd = dfd;
+       p->name = name;
+       p->total_link_count = old ? old->total_link_count : 0;
+       p->saved = old;
+       current->nameidata = p;
+}
+
+static void restore_nameidata(void)
+{
+       struct nameidata *now = current->nameidata, *old = now->saved;
+
+       current->nameidata = old;
+       if (old)
+               old->total_link_count = now->total_link_count;
+       if (now->stack != now->internal) {
+               kfree(now->stack);
+               now->stack = now->internal;
+       }
+}
+
+static int __nd_alloc_stack(struct nameidata *nd)
+{
+       struct saved *p;
+
+       if (nd->flags & LOOKUP_RCU) {
+               p= kmalloc(MAXSYMLINKS * sizeof(struct saved),
+                                 GFP_ATOMIC);
+               if (unlikely(!p))
+                       return -ECHILD;
+       } else {
+               p= kmalloc(MAXSYMLINKS * sizeof(struct saved),
+                                 GFP_KERNEL);
+               if (unlikely(!p))
+                       return -ENOMEM;
+       }
+       memcpy(p, nd->internal, sizeof(nd->internal));
+       nd->stack = p;
+       return 0;
+}
+
+static inline int nd_alloc_stack(struct nameidata *nd)
+{
+       if (likely(nd->depth != EMBEDDED_LEVELS))
+               return 0;
+       if (likely(nd->stack != nd->internal))
+               return 0;
+       return __nd_alloc_stack(nd);
+}
+
+static void drop_links(struct nameidata *nd)
+{
+       int i = nd->depth;
+       while (i--) {
+               struct saved *last = nd->stack + i;
+               struct inode *inode = last->inode;
+               if (last->cookie && inode->i_op->put_link) {
+                       inode->i_op->put_link(inode, last->cookie);
+                       last->cookie = NULL;
+               }
+       }
+}
+
+static void terminate_walk(struct nameidata *nd)
+{
+       drop_links(nd);
+       if (!(nd->flags & LOOKUP_RCU)) {
+               int i;
+               path_put(&nd->path);
+               for (i = 0; i < nd->depth; i++)
+                       path_put(&nd->stack[i].link);
+               if (nd->root.mnt && !(nd->flags & LOOKUP_ROOT)) {
+                       path_put(&nd->root);
+                       nd->root.mnt = NULL;
+               }
+       } else {
+               nd->flags &= ~LOOKUP_RCU;
+               if (!(nd->flags & LOOKUP_ROOT))
+                       nd->root.mnt = NULL;
+               rcu_read_unlock();
+       }
+       nd->depth = 0;
+}
+
+/* path_put is needed afterwards regardless of success or failure */
+static bool legitimize_path(struct nameidata *nd,
+                           struct path *path, unsigned seq)
+{
+       int res = __legitimize_mnt(path->mnt, nd->m_seq);
+       if (unlikely(res)) {
+               if (res > 0)
+                       path->mnt = NULL;
+               path->dentry = NULL;
+               return false;
+       }
+       if (unlikely(!lockref_get_not_dead(&path->dentry->d_lockref))) {
+               path->dentry = NULL;
+               return false;
+       }
+       return !read_seqcount_retry(&path->dentry->d_seq, seq);
+}
+
+static bool legitimize_links(struct nameidata *nd)
+{
+       int i;
+       for (i = 0; i < nd->depth; i++) {
+               struct saved *last = nd->stack + i;
+               if (unlikely(!legitimize_path(nd, &last->link, last->seq))) {
+                       drop_links(nd);
+                       nd->depth = i + 1;
+                       return false;
+               }
+       }
+       return true;
+}
+
 /*
  * Path walking has 2 modes, rcu-walk and ref-walk (see
  * Documentation/filesystems/path-lookup.txt).  In situations when we can't
@@ -520,35 +650,28 @@ struct nameidata {
  * unlazy_walk - try to switch to ref-walk mode.
  * @nd: nameidata pathwalk data
  * @dentry: child of nd->path.dentry or NULL
+ * @seq: seq number to check dentry against
  * Returns: 0 on success, -ECHILD on failure
  *
  * unlazy_walk attempts to legitimize the current nd->path, nd->root and dentry
  * for ref-walk mode.  @dentry must be a path found by a do_lookup call on
  * @nd or NULL.  Must be called from rcu-walk context.
+ * Nothing should touch nameidata between unlazy_walk() failure and
+ * terminate_walk().
  */
-static int unlazy_walk(struct nameidata *nd, struct dentry *dentry)
+static int unlazy_walk(struct nameidata *nd, struct dentry *dentry, unsigned seq)
 {
-       struct fs_struct *fs = current->fs;
        struct dentry *parent = nd->path.dentry;
 
        BUG_ON(!(nd->flags & LOOKUP_RCU));
 
-       /*
-        * After legitimizing the bastards, terminate_walk()
-        * will do the right thing for non-RCU mode, and all our
-        * subsequent exit cases should rcu_read_unlock()
-        * before returning.  Do vfsmount first; if dentry
-        * can't be legitimized, just set nd->path.dentry to NULL
-        * and rely on dput(NULL) being a no-op.
-        */
-       if (!legitimize_mnt(nd->path.mnt, nd->m_seq))
-               return -ECHILD;
        nd->flags &= ~LOOKUP_RCU;
-
-       if (!lockref_get_not_dead(&parent->d_lockref)) {
-               nd->path.dentry = NULL; 
-               goto out;
-       }
+       if (unlikely(!legitimize_links(nd)))
+               goto out2;
+       if (unlikely(!legitimize_mnt(nd->path.mnt, nd->m_seq)))
+               goto out2;
+       if (unlikely(!lockref_get_not_dead(&parent->d_lockref)))
+               goto out1;
 
        /*
         * For a negative lookup, the lookup sequence point is the parents
@@ -568,7 +691,7 @@ static int unlazy_walk(struct nameidata *nd, struct dentry *dentry)
        } else {
                if (!lockref_get_not_dead(&dentry->d_lockref))
                        goto out;
-               if (read_seqcount_retry(&dentry->d_seq, nd->seq))
+               if (read_seqcount_retry(&dentry->d_seq, seq))
                        goto drop_dentry;
        }
 
@@ -577,22 +700,24 @@ static int unlazy_walk(struct nameidata *nd, struct dentry *dentry)
         * still valid and get it if required.
         */
        if (nd->root.mnt && !(nd->flags & LOOKUP_ROOT)) {
-               spin_lock(&fs->lock);
-               if (nd->root.mnt != fs->root.mnt || nd->root.dentry != fs->root.dentry)
-                       goto unlock_and_drop_dentry;
-               path_get(&nd->root);
-               spin_unlock(&fs->lock);
+               if (unlikely(!legitimize_path(nd, &nd->root, nd->root_seq))) {
+                       rcu_read_unlock();
+                       dput(dentry);
+                       return -ECHILD;
+               }
        }
 
        rcu_read_unlock();
        return 0;
 
-unlock_and_drop_dentry:
-       spin_unlock(&fs->lock);
 drop_dentry:
        rcu_read_unlock();
        dput(dentry);
        goto drop_root_mnt;
+out2:
+       nd->path.mnt = NULL;
+out1:
+       nd->path.dentry = NULL;
 out:
        rcu_read_unlock();
 drop_root_mnt:
@@ -601,6 +726,24 @@ drop_root_mnt:
        return -ECHILD;
 }
 
+static int unlazy_link(struct nameidata *nd, struct path *link, unsigned seq)
+{
+       if (unlikely(!legitimize_path(nd, link, seq))) {
+               drop_links(nd);
+               nd->depth = 0;
+               nd->flags &= ~LOOKUP_RCU;
+               nd->path.mnt = NULL;
+               nd->path.dentry = NULL;
+               if (!(nd->flags & LOOKUP_ROOT))
+                       nd->root.mnt = NULL;
+               rcu_read_unlock();
+       } else if (likely(unlazy_walk(nd, NULL, 0)) == 0) {
+               return 0;
+       }
+       path_put(link);
+       return -ECHILD;
+}
+
 static inline int d_revalidate(struct dentry *dentry, unsigned int flags)
 {
        return dentry->d_op->d_revalidate(dentry, flags);
@@ -622,26 +765,10 @@ static int complete_walk(struct nameidata *nd)
        int status;
 
        if (nd->flags & LOOKUP_RCU) {
-               nd->flags &= ~LOOKUP_RCU;
                if (!(nd->flags & LOOKUP_ROOT))
                        nd->root.mnt = NULL;
-
-               if (!legitimize_mnt(nd->path.mnt, nd->m_seq)) {
-                       rcu_read_unlock();
-                       return -ECHILD;
-               }
-               if (unlikely(!lockref_get_not_dead(&dentry->d_lockref))) {
-                       rcu_read_unlock();
-                       mntput(nd->path.mnt);
+               if (unlikely(unlazy_walk(nd, NULL, 0)))
                        return -ECHILD;
-               }
-               if (read_seqcount_retry(&dentry->d_seq, nd->seq)) {
-                       rcu_read_unlock();
-                       dput(dentry);
-                       mntput(nd->path.mnt);
-                       return -ECHILD;
-               }
-               rcu_read_unlock();
        }
 
        if (likely(!(nd->flags & LOOKUP_JUMPED)))
@@ -657,28 +784,25 @@ static int complete_walk(struct nameidata *nd)
        if (!status)
                status = -ESTALE;
 
-       path_put(&nd->path);
        return status;
 }
 
-static __always_inline void set_root(struct nameidata *nd)
+static void set_root(struct nameidata *nd)
 {
        get_fs_root(current->fs, &nd->root);
 }
 
-static int link_path_walk(const char *, struct nameidata *);
-
-static __always_inline unsigned set_root_rcu(struct nameidata *nd)
+static unsigned set_root_rcu(struct nameidata *nd)
 {
        struct fs_struct *fs = current->fs;
-       unsigned seq, res;
+       unsigned seq;
 
        do {
                seq = read_seqcount_begin(&fs->seq);
                nd->root = fs->root;
-               res = __read_seqcount_begin(&nd->root.dentry->d_seq);
+               nd->root_seq = __read_seqcount_begin(&nd->root.dentry->d_seq);
        } while (read_seqcount_retry(&fs->seq, seq));
-       return res;
+       return nd->root_seq;
 }
 
 static void path_put_conditional(struct path *path, struct nameidata *nd)
@@ -704,8 +828,9 @@ static inline void path_to_nameidata(const struct path *path,
  * Helper to directly jump to a known parsed path from ->follow_link,
  * caller must have taken a reference to path beforehand.
  */
-void nd_jump_link(struct nameidata *nd, struct path *path)
+void nd_jump_link(struct path *path)
 {
+       struct nameidata *nd = current->nameidata;
        path_put(&nd->path);
 
        nd->path = *path;
@@ -713,24 +838,14 @@ void nd_jump_link(struct nameidata *nd, struct path *path)
        nd->flags |= LOOKUP_JUMPED;
 }
 
-void nd_set_link(struct nameidata *nd, char *path)
-{
-       nd->saved_names[nd->depth] = path;
-}
-EXPORT_SYMBOL(nd_set_link);
-
-char *nd_get_link(struct nameidata *nd)
-{
-       return nd->saved_names[nd->depth];
-}
-EXPORT_SYMBOL(nd_get_link);
-
-static inline void put_link(struct nameidata *nd, struct path *link, void *cookie)
+static inline void put_link(struct nameidata *nd)
 {
-       struct inode *inode = link->dentry->d_inode;
-       if (inode->i_op->put_link)
-               inode->i_op->put_link(link->dentry, nd, cookie);
-       path_put(link);
+       struct saved *last = nd->stack + --nd->depth;
+       struct inode *inode = last->inode;
+       if (last->cookie && inode->i_op->put_link)
+               inode->i_op->put_link(inode, last->cookie);
+       if (!(nd->flags & LOOKUP_RCU))
+               path_put(&last->link);
 }
 
 int sysctl_protected_symlinks __read_mostly = 0;
@@ -738,7 +853,6 @@ int sysctl_protected_hardlinks __read_mostly = 0;
 
 /**
  * may_follow_link - Check symlink following for unsafe situations
- * @link: The path of the symlink
  * @nd: nameidata pathwalk data
  *
  * In the case of the sysctl_protected_symlinks sysctl being enabled,
@@ -752,7 +866,7 @@ int sysctl_protected_hardlinks __read_mostly = 0;
  *
  * Returns 0 if following the symlink is allowed, -ve on error.
  */
-static inline int may_follow_link(struct path *link, struct nameidata *nd)
+static inline int may_follow_link(struct nameidata *nd)
 {
        const struct inode *inode;
        const struct inode *parent;
@@ -761,7 +875,7 @@ static inline int may_follow_link(struct path *link, struct nameidata *nd)
                return 0;
 
        /* Allowed if owner and follower match. */
-       inode = link->dentry->d_inode;
+       inode = nd->stack[0].inode;
        if (uid_eq(current_cred()->fsuid, inode->i_uid))
                return 0;
 
@@ -774,9 +888,10 @@ static inline int may_follow_link(struct path *link, struct nameidata *nd)
        if (uid_eq(parent->i_uid, inode->i_uid))
                return 0;
 
-       audit_log_link_denied("follow_link", link);
-       path_put_conditional(link, nd);
-       path_put(&nd->path);
+       if (nd->flags & LOOKUP_RCU)
+               return -ECHILD;
+
+       audit_log_link_denied("follow_link", &nd->stack[0].link);
        return -EACCES;
 }
 
@@ -849,82 +964,68 @@ static int may_linkat(struct path *link)
        return -EPERM;
 }
 
-static __always_inline int
-follow_link(struct path *link, struct nameidata *nd, void **p)
+static __always_inline
+const char *get_link(struct nameidata *nd)
 {
-       struct dentry *dentry = link->dentry;
+       struct saved *last = nd->stack + nd->depth - 1;
+       struct dentry *dentry = last->link.dentry;
+       struct inode *inode = last->inode;
        int error;
-       char *s;
+       const char *res;
 
-       BUG_ON(nd->flags & LOOKUP_RCU);
-
-       if (link->mnt == nd->path.mnt)
-               mntget(link->mnt);
-
-       error = -ELOOP;
-       if (unlikely(current->total_link_count >= 40))
-               goto out_put_nd_path;
-
-       cond_resched();
-       current->total_link_count++;
-
-       touch_atime(link);
-       nd_set_link(nd, NULL);
+       if (!(nd->flags & LOOKUP_RCU)) {
+               touch_atime(&last->link);
+               cond_resched();
+       } else if (atime_needs_update(&last->link, inode)) {
+               if (unlikely(unlazy_walk(nd, NULL, 0)))
+                       return ERR_PTR(-ECHILD);
+               touch_atime(&last->link);
+       }
 
-       error = security_inode_follow_link(link->dentry, nd);
-       if (error)
-               goto out_put_nd_path;
+       error = security_inode_follow_link(dentry, inode,
+                                          nd->flags & LOOKUP_RCU);
+       if (unlikely(error))
+               return ERR_PTR(error);
 
        nd->last_type = LAST_BIND;
-       *p = dentry->d_inode->i_op->follow_link(dentry, nd);
-       error = PTR_ERR(*p);
-       if (IS_ERR(*p))
-               goto out_put_nd_path;
-
-       error = 0;
-       s = nd_get_link(nd);
-       if (s) {
-               if (unlikely(IS_ERR(s))) {
-                       path_put(&nd->path);
-                       put_link(nd, link, *p);
-                       return PTR_ERR(s);
+       res = inode->i_link;
+       if (!res) {
+               if (nd->flags & LOOKUP_RCU) {
+                       if (unlikely(unlazy_walk(nd, NULL, 0)))
+                               return ERR_PTR(-ECHILD);
                }
-               if (*s == '/') {
+               res = inode->i_op->follow_link(dentry, &last->cookie);
+               if (IS_ERR_OR_NULL(res)) {
+                       last->cookie = NULL;
+                       return res;
+               }
+       }
+       if (*res == '/') {
+               if (nd->flags & LOOKUP_RCU) {
+                       struct dentry *d;
+                       if (!nd->root.mnt)
+                               set_root_rcu(nd);
+                       nd->path = nd->root;
+                       d = nd->path.dentry;
+                       nd->inode = d->d_inode;
+                       nd->seq = nd->root_seq;
+                       if (unlikely(read_seqcount_retry(&d->d_seq, nd->seq)))
+                               return ERR_PTR(-ECHILD);
+               } else {
                        if (!nd->root.mnt)
                                set_root(nd);
                        path_put(&nd->path);
                        nd->path = nd->root;
                        path_get(&nd->root);
-                       nd->flags |= LOOKUP_JUMPED;
+                       nd->inode = nd->path.dentry->d_inode;
                }
-               nd->inode = nd->path.dentry->d_inode;
-               error = link_path_walk(s, nd);
-               if (unlikely(error))
-                       put_link(nd, link, *p);
+               nd->flags |= LOOKUP_JUMPED;
+               while (unlikely(*++res == '/'))
+                       ;
        }
-
-       return error;
-
-out_put_nd_path:
-       *p = NULL;
-       path_put(&nd->path);
-       path_put(link);
-       return error;
-}
-
-static int follow_up_rcu(struct path *path)
-{
-       struct mount *mnt = real_mount(path->mnt);
-       struct mount *parent;
-       struct dentry *mountpoint;
-
-       parent = mnt->mnt_parent;
-       if (&parent->mnt == path->mnt)
-               return 0;
-       mountpoint = mnt->mnt_mountpoint;
-       path->dentry = mountpoint;
-       path->mnt = &parent->mnt;
-       return 1;
+       if (!*res)
+               res = NULL;
+       return res;
 }
 
 /*
@@ -965,7 +1066,7 @@ EXPORT_SYMBOL(follow_up);
  * - return -EISDIR to tell follow_managed() to stop and return the path we
  *   were called with.
  */
-static int follow_automount(struct path *path, unsigned flags,
+static int follow_automount(struct path *path, struct nameidata *nd,
                            bool *need_mntput)
 {
        struct vfsmount *mnt;
@@ -985,13 +1086,13 @@ static int follow_automount(struct path *path, unsigned flags,
         * as being automount points.  These will need the attentions
         * of the daemon to instantiate them before they can be used.
         */
-       if (!(flags & (LOOKUP_PARENT | LOOKUP_DIRECTORY |
-                    LOOKUP_OPEN | LOOKUP_CREATE | LOOKUP_AUTOMOUNT)) &&
+       if (!(nd->flags & (LOOKUP_PARENT | LOOKUP_DIRECTORY |
+                          LOOKUP_OPEN | LOOKUP_CREATE | LOOKUP_AUTOMOUNT)) &&
            path->dentry->d_inode)
                return -EISDIR;
 
-       current->total_link_count++;
-       if (current->total_link_count >= 40)
+       nd->total_link_count++;
+       if (nd->total_link_count >= 40)
                return -ELOOP;
 
        mnt = path->dentry->d_op->d_automount(path);
@@ -1005,7 +1106,7 @@ static int follow_automount(struct path *path, unsigned flags,
                 * the path being looked up; if it wasn't then the remainder of
                 * the path is inaccessible and we should say so.
                 */
-               if (PTR_ERR(mnt) == -EISDIR && (flags & LOOKUP_PARENT))
+               if (PTR_ERR(mnt) == -EISDIR && (nd->flags & LOOKUP_PARENT))
                        return -EREMOTE;
                return PTR_ERR(mnt);
        }
@@ -1045,7 +1146,7 @@ static int follow_automount(struct path *path, unsigned flags,
  *
  * Serialization is taken care of in namespace.c
  */
-static int follow_managed(struct path *path, unsigned flags)
+static int follow_managed(struct path *path, struct nameidata *nd)
 {
        struct vfsmount *mnt = path->mnt; /* held by caller, must be left alone */
        unsigned managed;
@@ -1089,7 +1190,7 @@ static int follow_managed(struct path *path, unsigned flags)
 
                /* Handle an automount point */
                if (managed & DCACHE_NEED_AUTOMOUNT) {
-                       ret = follow_automount(path, flags, &need_mntput);
+                       ret = follow_automount(path, nd, &need_mntput);
                        if (ret < 0)
                                break;
                        continue;
@@ -1103,7 +1204,11 @@ static int follow_managed(struct path *path, unsigned flags)
                mntput(path->mnt);
        if (ret == -EISDIR)
                ret = 0;
-       return ret < 0 ? ret : need_mntput;
+       if (need_mntput)
+               nd->flags |= LOOKUP_JUMPED;
+       if (unlikely(ret < 0))
+               path_put_conditional(path, nd);
+       return ret;
 }
 
 int follow_down_one(struct path *path)
@@ -1133,7 +1238,7 @@ static inline int managed_dentry_rcu(struct dentry *dentry)
  * we meet a managed dentry that would need blocking.
  */
 static bool __follow_mount_rcu(struct nameidata *nd, struct path *path,
-                              struct inode **inode)
+                              struct inode **inode, unsigned *seqp)
 {
        for (;;) {
                struct mount *mounted;
@@ -1160,7 +1265,7 @@ static bool __follow_mount_rcu(struct nameidata *nd, struct path *path,
                path->mnt = &mounted->mnt;
                path->dentry = mounted->mnt.mnt_root;
                nd->flags |= LOOKUP_JUMPED;
-               nd->seq = read_seqcount_begin(&path->dentry->d_seq);
+               *seqp = read_seqcount_begin(&path->dentry->d_seq);
                /*
                 * Update the inode too. We don't need to re-check the
                 * dentry sequence number here after this d_inode read,
@@ -1179,10 +1284,8 @@ static int follow_dotdot_rcu(struct nameidata *nd)
                set_root_rcu(nd);
 
        while (1) {
-               if (nd->path.dentry == nd->root.dentry &&
-                   nd->path.mnt == nd->root.mnt) {
+               if (path_equal(&nd->path, &nd->root))
                        break;
-               }
                if (nd->path.dentry != nd->path.mnt->mnt_root) {
                        struct dentry *old = nd->path.dentry;
                        struct dentry *parent = old->d_parent;
@@ -1190,38 +1293,42 @@ static int follow_dotdot_rcu(struct nameidata *nd)
 
                        inode = parent->d_inode;
                        seq = read_seqcount_begin(&parent->d_seq);
-                       if (read_seqcount_retry(&old->d_seq, nd->seq))
-                               goto failed;
+                       if (unlikely(read_seqcount_retry(&old->d_seq, nd->seq)))
+                               return -ECHILD;
                        nd->path.dentry = parent;
                        nd->seq = seq;
                        break;
+               } else {
+                       struct mount *mnt = real_mount(nd->path.mnt);
+                       struct mount *mparent = mnt->mnt_parent;
+                       struct dentry *mountpoint = mnt->mnt_mountpoint;
+                       struct inode *inode2 = mountpoint->d_inode;
+                       unsigned seq = read_seqcount_begin(&mountpoint->d_seq);
+                       if (unlikely(read_seqretry(&mount_lock, nd->m_seq)))
+                               return -ECHILD;
+                       if (&mparent->mnt == nd->path.mnt)
+                               break;
+                       /* we know that mountpoint was pinned */
+                       nd->path.dentry = mountpoint;
+                       nd->path.mnt = &mparent->mnt;
+                       inode = inode2;
+                       nd->seq = seq;
                }
-               if (!follow_up_rcu(&nd->path))
-                       break;
-               inode = nd->path.dentry->d_inode;
-               nd->seq = read_seqcount_begin(&nd->path.dentry->d_seq);
        }
-       while (d_mountpoint(nd->path.dentry)) {
+       while (unlikely(d_mountpoint(nd->path.dentry))) {
                struct mount *mounted;
                mounted = __lookup_mnt(nd->path.mnt, nd->path.dentry);
+               if (unlikely(read_seqretry(&mount_lock, nd->m_seq)))
+                       return -ECHILD;
                if (!mounted)
                        break;
                nd->path.mnt = &mounted->mnt;
                nd->path.dentry = mounted->mnt.mnt_root;
                inode = nd->path.dentry->d_inode;
                nd->seq = read_seqcount_begin(&nd->path.dentry->d_seq);
-               if (read_seqretry(&mount_lock, nd->m_seq))
-                       goto failed;
        }
        nd->inode = inode;
        return 0;
-
-failed:
-       nd->flags &= ~LOOKUP_RCU;
-       if (!(nd->flags & LOOKUP_ROOT))
-               nd->root.mnt = NULL;
-       rcu_read_unlock();
-       return -ECHILD;
 }
 
 /*
@@ -1400,7 +1507,8 @@ static struct dentry *__lookup_hash(struct qstr *name,
  *  It _is_ time-critical.
  */
 static int lookup_fast(struct nameidata *nd,
-                      struct path *path, struct inode **inode)
+                      struct path *path, struct inode **inode,
+                      unsigned *seqp)
 {
        struct vfsmount *mnt = nd->path.mnt;
        struct dentry *dentry, *parent = nd->path.dentry;
@@ -1424,7 +1532,7 @@ static int lookup_fast(struct nameidata *nd,
                 * This sequence count validates that the inode matches
                 * the dentry name information from lookup.
                 */
-               *inode = dentry->d_inode;
+               *inode = d_backing_inode(dentry);
                negative = d_is_negative(dentry);
                if (read_seqcount_retry(&dentry->d_seq, seq))
                        return -ECHILD;
@@ -1440,8 +1548,8 @@ static int lookup_fast(struct nameidata *nd,
                 */
                if (__read_seqcount_retry(&parent->d_seq, nd->seq))
                        return -ECHILD;
-               nd->seq = seq;
 
+               *seqp = seq;
                if (unlikely(dentry->d_flags & DCACHE_OP_REVALIDATE)) {
                        status = d_revalidate(dentry, nd->flags);
                        if (unlikely(status <= 0)) {
@@ -1452,10 +1560,10 @@ static int lookup_fast(struct nameidata *nd,
                }
                path->mnt = mnt;
                path->dentry = dentry;
-               if (likely(__follow_mount_rcu(nd, path, inode)))
+               if (likely(__follow_mount_rcu(nd, path, inode, seqp)))
                        return 0;
 unlazy:
-               if (unlazy_walk(nd, dentry))
+               if (unlazy_walk(nd, dentry, seq))
                        return -ECHILD;
        } else {
                dentry = __d_lookup(parent, &nd->last);
@@ -1482,15 +1590,10 @@ unlazy:
        }
        path->mnt = mnt;
        path->dentry = dentry;
-       err = follow_managed(path, nd->flags);
-       if (unlikely(err < 0)) {
-               path_put_conditional(path, nd);
-               return err;
-       }
-       if (err)
-               nd->flags |= LOOKUP_JUMPED;
-       *inode = path->dentry->d_inode;
-       return 0;
+       err = follow_managed(path, nd);
+       if (likely(!err))
+               *inode = d_backing_inode(path->dentry);
+       return err;
 
 need_lookup:
        return 1;
@@ -1500,7 +1603,6 @@ need_lookup:
 static int lookup_slow(struct nameidata *nd, struct path *path)
 {
        struct dentry *dentry, *parent;
-       int err;
 
        parent = nd->path.dentry;
        BUG_ON(nd->inode != parent->d_inode);
@@ -1512,14 +1614,7 @@ static int lookup_slow(struct nameidata *nd, struct path *path)
                return PTR_ERR(dentry);
        path->mnt = nd->path.mnt;
        path->dentry = dentry;
-       err = follow_managed(path, nd->flags);
-       if (unlikely(err < 0)) {
-               path_put_conditional(path, nd);
-               return err;
-       }
-       if (err)
-               nd->flags |= LOOKUP_JUMPED;
-       return 0;
+       return follow_managed(path, nd);
 }
 
 static inline int may_lookup(struct nameidata *nd)
@@ -1528,7 +1623,7 @@ static inline int may_lookup(struct nameidata *nd)
                int err = inode_permission(nd->inode, MAY_EXEC|MAY_NOT_BLOCK);
                if (err != -ECHILD)
                        return err;
-               if (unlazy_walk(nd, NULL))
+               if (unlazy_walk(nd, NULL, 0))
                        return -ECHILD;
        }
        return inode_permission(nd->inode, MAY_EXEC);
@@ -1538,24 +1633,45 @@ static inline int handle_dots(struct nameidata *nd, int type)
 {
        if (type == LAST_DOTDOT) {
                if (nd->flags & LOOKUP_RCU) {
-                       if (follow_dotdot_rcu(nd))
-                               return -ECHILD;
+                       return follow_dotdot_rcu(nd);
                } else
                        follow_dotdot(nd);
        }
        return 0;
 }
 
-static void terminate_walk(struct nameidata *nd)
+static int pick_link(struct nameidata *nd, struct path *link,
+                    struct inode *inode, unsigned seq)
 {
+       int error;
+       struct saved *last;
+       if (unlikely(nd->total_link_count++ >= MAXSYMLINKS)) {
+               path_to_nameidata(link, nd);
+               return -ELOOP;
+       }
        if (!(nd->flags & LOOKUP_RCU)) {
-               path_put(&nd->path);
-       } else {
-               nd->flags &= ~LOOKUP_RCU;
-               if (!(nd->flags & LOOKUP_ROOT))
-                       nd->root.mnt = NULL;
-               rcu_read_unlock();
+               if (link->mnt == nd->path.mnt)
+                       mntget(link->mnt);
+       }
+       error = nd_alloc_stack(nd);
+       if (unlikely(error)) {
+               if (error == -ECHILD) {
+                       if (unlikely(unlazy_link(nd, link, seq)))
+                               return -ECHILD;
+                       error = nd_alloc_stack(nd);
+               }
+               if (error) {
+                       path_put(link);
+                       return error;
+               }
        }
+
+       last = nd->stack + nd->depth++;
+       last->link = *link;
+       last->cookie = NULL;
+       last->inode = inode;
+       last->seq = seq;
+       return 1;
 }
 
 /*
@@ -1564,97 +1680,67 @@ static void terminate_walk(struct nameidata *nd)
  * so we keep a cache of "no, this doesn't need follow_link"
  * for the common case.
  */
-static inline int should_follow_link(struct dentry *dentry, int follow)
+static inline int should_follow_link(struct nameidata *nd, struct path *link,
+                                    int follow,
+                                    struct inode *inode, unsigned seq)
 {
-       return unlikely(d_is_symlink(dentry)) ? follow : 0;
+       if (likely(!d_is_symlink(link->dentry)))
+               return 0;
+       if (!follow)
+               return 0;
+       return pick_link(nd, link, inode, seq);
 }
 
-static inline int walk_component(struct nameidata *nd, struct path *path,
-               int follow)
+enum {WALK_GET = 1, WALK_PUT = 2};
+
+static int walk_component(struct nameidata *nd, int flags)
 {
+       struct path path;
        struct inode *inode;
+       unsigned seq;
        int err;
        /*
         * "." and ".." are special - ".." especially so because it has
         * to be able to know about the current root directory and
         * parent relationships.
         */
-       if (unlikely(nd->last_type != LAST_NORM))
-               return handle_dots(nd, nd->last_type);
-       err = lookup_fast(nd, path, &inode);
+       if (unlikely(nd->last_type != LAST_NORM)) {
+               err = handle_dots(nd, nd->last_type);
+               if (flags & WALK_PUT)
+                       put_link(nd);
+               return err;
+       }
+       err = lookup_fast(nd, &path, &inode, &seq);
        if (unlikely(err)) {
                if (err < 0)
-                       goto out_err;
+                       return err;
 
-               err = lookup_slow(nd, path);
+               err = lookup_slow(nd, &path);
                if (err < 0)
-                       goto out_err;
+                       return err;
 
-               inode = path->dentry->d_inode;
+               inode = d_backing_inode(path.dentry);
+               seq = 0;        /* we are already out of RCU mode */
                err = -ENOENT;
-               if (d_is_negative(path->dentry))
+               if (d_is_negative(path.dentry))
                        goto out_path_put;
        }
 
-       if (should_follow_link(path->dentry, follow)) {
-               if (nd->flags & LOOKUP_RCU) {
-                       if (unlikely(nd->path.mnt != path->mnt ||
-                                    unlazy_walk(nd, path->dentry))) {
-                               err = -ECHILD;
-                               goto out_err;
-                       }
-               }
-               BUG_ON(inode != path->dentry->d_inode);
-               return 1;
-       }
-       path_to_nameidata(path, nd);
+       if (flags & WALK_PUT)
+               put_link(nd);
+       err = should_follow_link(nd, &path, flags & WALK_GET, inode, seq);
+       if (unlikely(err))
+               return err;
+       path_to_nameidata(&path, nd);
        nd->inode = inode;
+       nd->seq = seq;
        return 0;
 
 out_path_put:
-       path_to_nameidata(path, nd);
-out_err:
-       terminate_walk(nd);
+       path_to_nameidata(&path, nd);
        return err;
 }
 
-/*
- * This limits recursive symlink follows to 8, while
- * limiting consecutive symlinks to 40.
- *
- * Without that kind of total limit, nasty chains of consecutive
- * symlinks can cause almost arbitrarily long lookups.
- */
-static inline int nested_symlink(struct path *path, struct nameidata *nd)
-{
-       int res;
-
-       if (unlikely(current->link_count >= MAX_NESTED_LINKS)) {
-               path_put_conditional(path, nd);
-               path_put(&nd->path);
-               return -ELOOP;
-       }
-       BUG_ON(nd->depth >= MAX_NESTED_LINKS);
-
-       nd->depth++;
-       current->link_count++;
-
-       do {
-               struct path link = *path;
-               void *cookie;
-
-               res = follow_link(&link, nd, &cookie);
-               if (res)
-                       break;
-               res = walk_component(nd, path, LOOKUP_FOLLOW);
-               put_link(nd, &link, cookie);
-       } while (res > 0);
-
-       current->link_count--;
-       nd->depth--;
-       return res;
-}
-
 /*
  * We can do the critical dentry name comparison and hashing
  * operations one word at a time, but we are limited to:
@@ -1781,9 +1867,8 @@ static inline u64 hash_name(const char *name)
  */
 static int link_path_walk(const char *name, struct nameidata *nd)
 {
-       struct path next;
        int err;
-       
+
        while (*name=='/')
                name++;
        if (!*name)
@@ -1796,7 +1881,7 @@ static int link_path_walk(const char *name, struct nameidata *nd)
 
                err = may_lookup(nd);
                if (err)
-                       break;
+                       return err;
 
                hash_len = hash_name(name);
 
@@ -1818,7 +1903,7 @@ static int link_path_walk(const char *name, struct nameidata *nd)
                                struct qstr this = { { .hash_len = hash_len }, .name = name };
                                err = parent->d_op->d_hash(parent, &this);
                                if (err < 0)
-                                       break;
+                                       return err;
                                hash_len = this.hash_len;
                                name = this.name;
                        }
@@ -1830,7 +1915,7 @@ static int link_path_walk(const char *name, struct nameidata *nd)
 
                name += hashlen_len(hash_len);
                if (!*name)
-                       return 0;
+                       goto OK;
                /*
                 * If it wasn't NUL, we know it was '/'. Skip that
                 * slash, and continue until no more slashes.
@@ -1838,57 +1923,73 @@ static int link_path_walk(const char *name, struct nameidata *nd)
                do {
                        name++;
                } while (unlikely(*name == '/'));
-               if (!*name)
-                       return 0;
-
-               err = walk_component(nd, &next, LOOKUP_FOLLOW);
+               if (unlikely(!*name)) {
+OK:
+                       /* pathname body, done */
+                       if (!nd->depth)
+                               return 0;
+                       name = nd->stack[nd->depth - 1].name;
+                       /* trailing symlink, done */
+                       if (!name)
+                               return 0;
+                       /* last component of nested symlink */
+                       err = walk_component(nd, WALK_GET | WALK_PUT);
+               } else {
+                       err = walk_component(nd, WALK_GET);
+               }
                if (err < 0)
                        return err;
 
                if (err) {
-                       err = nested_symlink(&next, nd);
-                       if (err)
-                               return err;
-               }
-               if (!d_can_lookup(nd->path.dentry)) {
-                       err = -ENOTDIR; 
-                       break;
+                       const char *s = get_link(nd);
+
+                       if (unlikely(IS_ERR(s)))
+                               return PTR_ERR(s);
+                       err = 0;
+                       if (unlikely(!s)) {
+                               /* jumped */
+                               put_link(nd);
+                       } else {
+                               nd->stack[nd->depth - 1].name = name;
+                               name = s;
+                               continue;
+                       }
                }
+               if (unlikely(!d_can_lookup(nd->path.dentry)))
+                       return -ENOTDIR;
        }
-       terminate_walk(nd);
-       return err;
 }
 
-static int path_init(int dfd, const struct filename *name, unsigned int flags,
-                    struct nameidata *nd)
+static const char *path_init(struct nameidata *nd, unsigned flags)
 {
        int retval = 0;
-       const char *s = name->name;
+       const char *s = nd->name->name;
 
        nd->last_type = LAST_ROOT; /* if there are only slashes... */
        nd->flags = flags | LOOKUP_JUMPED | LOOKUP_PARENT;
        nd->depth = 0;
-       nd->base = NULL;
+       nd->total_link_count = 0;
        if (flags & LOOKUP_ROOT) {
                struct dentry *root = nd->root.dentry;
                struct inode *inode = root->d_inode;
                if (*s) {
                        if (!d_can_lookup(root))
-                               return -ENOTDIR;
+                               return ERR_PTR(-ENOTDIR);
                        retval = inode_permission(inode, MAY_EXEC);
                        if (retval)
-                               return retval;
+                               return ERR_PTR(retval);
                }
                nd->path = nd->root;
                nd->inode = inode;
                if (flags & LOOKUP_RCU) {
                        rcu_read_lock();
                        nd->seq = __read_seqcount_begin(&nd->path.dentry->d_seq);
+                       nd->root_seq = nd->seq;
                        nd->m_seq = read_seqbegin(&mount_lock);
                } else {
                        path_get(&nd->path);
                }
-               goto done;
+               return s;
        }
 
        nd->root.mnt = NULL;
@@ -1903,7 +2004,7 @@ static int path_init(int dfd, const struct filename *name, unsigned int flags,
                        path_get(&nd->root);
                }
                nd->path = nd->root;
-       } else if (dfd == AT_FDCWD) {
+       } else if (nd->dfd == AT_FDCWD) {
                if (flags & LOOKUP_RCU) {
                        struct fs_struct *fs = current->fs;
                        unsigned seq;
@@ -1920,180 +2021,205 @@ static int path_init(int dfd, const struct filename *name, unsigned int flags,
                }
        } else {
                /* Caller must check execute permissions on the starting path component */
-               struct fd f = fdget_raw(dfd);
+               struct fd f = fdget_raw(nd->dfd);
                struct dentry *dentry;
 
                if (!f.file)
-                       return -EBADF;
+                       return ERR_PTR(-EBADF);
 
                dentry = f.file->f_path.dentry;
 
                if (*s) {
                        if (!d_can_lookup(dentry)) {
                                fdput(f);
-                               return -ENOTDIR;
+                               return ERR_PTR(-ENOTDIR);
                        }
                }
 
                nd->path = f.file->f_path;
                if (flags & LOOKUP_RCU) {
-                       if (f.flags & FDPUT_FPUT)
-                               nd->base = f.file;
-                       nd->seq = __read_seqcount_begin(&nd->path.dentry->d_seq);
                        rcu_read_lock();
+                       nd->inode = nd->path.dentry->d_inode;
+                       nd->seq = read_seqcount_begin(&nd->path.dentry->d_seq);
                } else {
                        path_get(&nd->path);
-                       fdput(f);
+                       nd->inode = nd->path.dentry->d_inode;
                }
+               fdput(f);
+               return s;
        }
 
        nd->inode = nd->path.dentry->d_inode;
        if (!(flags & LOOKUP_RCU))
-               goto done;
+               return s;
        if (likely(!read_seqcount_retry(&nd->path.dentry->d_seq, nd->seq)))
-               goto done;
+               return s;
        if (!(nd->flags & LOOKUP_ROOT))
                nd->root.mnt = NULL;
        rcu_read_unlock();
-       return -ECHILD;
-done:
-       current->total_link_count = 0;
-       return link_path_walk(s, nd);
+       return ERR_PTR(-ECHILD);
 }
 
-static void path_cleanup(struct nameidata *nd)
+static const char *trailing_symlink(struct nameidata *nd)
 {
-       if (nd->root.mnt && !(nd->flags & LOOKUP_ROOT)) {
-               path_put(&nd->root);
-               nd->root.mnt = NULL;
-       }
-       if (unlikely(nd->base))
-               fput(nd->base);
+       const char *s;
+       int error = may_follow_link(nd);
+       if (unlikely(error))
+               return ERR_PTR(error);
+       nd->flags |= LOOKUP_PARENT;
+       nd->stack[0].name = NULL;
+       s = get_link(nd);
+       return s ? s : "";
 }
 
-static inline int lookup_last(struct nameidata *nd, struct path *path)
+static inline int lookup_last(struct nameidata *nd)
 {
        if (nd->last_type == LAST_NORM && nd->last.name[nd->last.len])
                nd->flags |= LOOKUP_FOLLOW | LOOKUP_DIRECTORY;
 
        nd->flags &= ~LOOKUP_PARENT;
-       return walk_component(nd, path, nd->flags & LOOKUP_FOLLOW);
+       return walk_component(nd,
+                       nd->flags & LOOKUP_FOLLOW
+                               ? nd->depth
+                                       ? WALK_PUT | WALK_GET
+                                       : WALK_GET
+                               : 0);
 }
 
 /* Returns 0 and nd will be valid on success; Retuns error, otherwise. */
-static int path_lookupat(int dfd, const struct filename *name,
-                               unsigned int flags, struct nameidata *nd)
+static int path_lookupat(struct nameidata *nd, unsigned flags, struct path *path)
 {
-       struct path path;
+       const char *s = path_init(nd, flags);
        int err;
 
-       /*
-        * Path walking is largely split up into 2 different synchronisation
-        * schemes, rcu-walk and ref-walk (explained in
-        * Documentation/filesystems/path-lookup.txt). These share much of the
-        * path walk code, but some things particularly setup, cleanup, and
-        * following mounts are sufficiently divergent that functions are
-        * duplicated. Typically there is a function foo(), and its RCU
-        * analogue, foo_rcu().
-        *
-        * -ECHILD is the error number of choice (just to avoid clashes) that
-        * is returned if some aspect of an rcu-walk fails. Such an error must
-        * be handled by restarting a traditional ref-walk (which will always
-        * be able to complete).
-        */
-       err = path_init(dfd, name, flags, nd);
-       if (!err && !(flags & LOOKUP_PARENT)) {
-               err = lookup_last(nd, &path);
-               while (err > 0) {
-                       void *cookie;
-                       struct path link = path;
-                       err = may_follow_link(&link, nd);
-                       if (unlikely(err))
-                               break;
-                       nd->flags |= LOOKUP_PARENT;
-                       err = follow_link(&link, nd, &cookie);
-                       if (err)
-                               break;
-                       err = lookup_last(nd, &path);
-                       put_link(nd, &link, cookie);
+       if (IS_ERR(s))
+               return PTR_ERR(s);
+       while (!(err = link_path_walk(s, nd))
+               && ((err = lookup_last(nd)) > 0)) {
+               s = trailing_symlink(nd);
+               if (IS_ERR(s)) {
+                       err = PTR_ERR(s);
+                       break;
                }
        }
-
        if (!err)
                err = complete_walk(nd);
 
-       if (!err && nd->flags & LOOKUP_DIRECTORY) {
-               if (!d_can_lookup(nd->path.dentry)) {
-                       path_put(&nd->path);
+       if (!err && nd->flags & LOOKUP_DIRECTORY)
+               if (!d_can_lookup(nd->path.dentry))
                        err = -ENOTDIR;
-               }
+       if (!err) {
+               *path = nd->path;
+               nd->path.mnt = NULL;
+               nd->path.dentry = NULL;
        }
-
-       path_cleanup(nd);
+       terminate_walk(nd);
        return err;
 }
 
-static int filename_lookup(int dfd, struct filename *name,
-                               unsigned int flags, struct nameidata *nd)
+static int filename_lookup(int dfd, struct filename *name, unsigned flags,
+                          struct path *path, struct path *root)
 {
-       int retval = path_lookupat(dfd, name, flags | LOOKUP_RCU, nd);
+       int retval;
+       struct nameidata nd;
+       if (IS_ERR(name))
+               return PTR_ERR(name);
+       if (unlikely(root)) {
+               nd.root = *root;
+               flags |= LOOKUP_ROOT;
+       }
+       set_nameidata(&nd, dfd, name);
+       retval = path_lookupat(&nd, flags | LOOKUP_RCU, path);
        if (unlikely(retval == -ECHILD))
-               retval = path_lookupat(dfd, name, flags, nd);
+               retval = path_lookupat(&nd, flags, path);
        if (unlikely(retval == -ESTALE))
-               retval = path_lookupat(dfd, name, flags | LOOKUP_REVAL, nd);
+               retval = path_lookupat(&nd, flags | LOOKUP_REVAL, path);
 
        if (likely(!retval))
-               audit_inode(name, nd->path.dentry, flags & LOOKUP_PARENT);
+               audit_inode(name, path->dentry, flags & LOOKUP_PARENT);
+       restore_nameidata();
+       putname(name);
        return retval;
 }
 
+/* Returns 0 and nd will be valid on success; Retuns error, otherwise. */
+static int path_parentat(struct nameidata *nd, unsigned flags,
+                               struct path *parent)
+{
+       const char *s = path_init(nd, flags);
+       int err;
+       if (IS_ERR(s))
+               return PTR_ERR(s);
+       err = link_path_walk(s, nd);
+       if (!err)
+               err = complete_walk(nd);
+       if (!err) {
+               *parent = nd->path;
+               nd->path.mnt = NULL;
+               nd->path.dentry = NULL;
+       }
+       terminate_walk(nd);
+       return err;
+}
+
+static struct filename *filename_parentat(int dfd, struct filename *name,
+                               unsigned int flags, struct path *parent,
+                               struct qstr *last, int *type)
+{
+       int retval;
+       struct nameidata nd;
+
+       if (IS_ERR(name))
+               return name;
+       set_nameidata(&nd, dfd, name);
+       retval = path_parentat(&nd, flags | LOOKUP_RCU, parent);
+       if (unlikely(retval == -ECHILD))
+               retval = path_parentat(&nd, flags, parent);
+       if (unlikely(retval == -ESTALE))
+               retval = path_parentat(&nd, flags | LOOKUP_REVAL, parent);
+       if (likely(!retval)) {
+               *last = nd.last;
+               *type = nd.last_type;
+               audit_inode(name, parent->dentry, LOOKUP_PARENT);
+       } else {
+               putname(name);
+               name = ERR_PTR(retval);
+       }
+       restore_nameidata();
+       return name;
+}
+
 /* does lookup, returns the object with parent locked */
 struct dentry *kern_path_locked(const char *name, struct path *path)
 {
-       struct filename *filename = getname_kernel(name);
-       struct nameidata nd;
+       struct filename *filename;
        struct dentry *d;
-       int err;
+       struct qstr last;
+       int type;
 
+       filename = filename_parentat(AT_FDCWD, getname_kernel(name), 0, path,
+                                   &last, &type);
        if (IS_ERR(filename))
                return ERR_CAST(filename);
-
-       err = filename_lookup(AT_FDCWD, filename, LOOKUP_PARENT, &nd);
-       if (err) {
-               d = ERR_PTR(err);
-               goto out;
-       }
-       if (nd.last_type != LAST_NORM) {
-               path_put(&nd.path);
-               d = ERR_PTR(-EINVAL);
-               goto out;
+       if (unlikely(type != LAST_NORM)) {
+               path_put(path);
+               putname(filename);
+               return ERR_PTR(-EINVAL);
        }
-       mutex_lock_nested(&nd.path.dentry->d_inode->i_mutex, I_MUTEX_PARENT);
-       d = __lookup_hash(&nd.last, nd.path.dentry, 0);
+       mutex_lock_nested(&path->dentry->d_inode->i_mutex, I_MUTEX_PARENT);
+       d = __lookup_hash(&last, path->dentry, 0);
        if (IS_ERR(d)) {
-               mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
-               path_put(&nd.path);
-               goto out;
+               mutex_unlock(&path->dentry->d_inode->i_mutex);
+               path_put(path);
        }
-       *path = nd.path;
-out:
        putname(filename);
        return d;
 }
 
 int kern_path(const char *name, unsigned int flags, struct path *path)
 {
-       struct nameidata nd;
-       struct filename *filename = getname_kernel(name);
-       int res = PTR_ERR(filename);
-
-       if (!IS_ERR(filename)) {
-               res = filename_lookup(AT_FDCWD, filename, flags, &nd);
-               putname(filename);
-               if (!res)
-                       *path = nd.path;
-       }
-       return res;
+       return filename_lookup(AT_FDCWD, getname_kernel(name),
+                              flags, path, NULL);
 }
 EXPORT_SYMBOL(kern_path);
 
@@ -2109,36 +2235,13 @@ int vfs_path_lookup(struct dentry *dentry, struct vfsmount *mnt,
                    const char *name, unsigned int flags,
                    struct path *path)
 {
-       struct filename *filename = getname_kernel(name);
-       int err = PTR_ERR(filename);
-
-       BUG_ON(flags & LOOKUP_PARENT);
-
-       /* the first argument of filename_lookup() is ignored with LOOKUP_ROOT */
-       if (!IS_ERR(filename)) {
-               struct nameidata nd;
-               nd.root.dentry = dentry;
-               nd.root.mnt = mnt;
-               err = filename_lookup(AT_FDCWD, filename,
-                                     flags | LOOKUP_ROOT, &nd);
-               if (!err)
-                       *path = nd.path;
-               putname(filename);
-       }
-       return err;
+       struct path root = {.mnt = mnt, .dentry = dentry};
+       /* the first argument of filename_lookup() is ignored with root */
+       return filename_lookup(AT_FDCWD, getname_kernel(name),
+                              flags , path, &root);
 }
 EXPORT_SYMBOL(vfs_path_lookup);
 
-/*
- * Restricted form of lookup. Doesn't follow links, single-component only,
- * needs parent already locked. Doesn't follow mounts.
- * SMP-safe.
- */
-static struct dentry *lookup_hash(struct nameidata *nd)
-{
-       return __lookup_hash(&nd->last, nd->path.dentry, nd->flags);
-}
-
 /**
  * lookup_one_len - filesystem helper to lookup single pathname component
  * @name:      pathname component to lookup
@@ -2193,27 +2296,10 @@ EXPORT_SYMBOL(lookup_one_len);
 int user_path_at_empty(int dfd, const char __user *name, unsigned flags,
                 struct path *path, int *empty)
 {
-       struct nameidata nd;
-       struct filename *tmp = getname_flags(name, flags, empty);
-       int err = PTR_ERR(tmp);
-       if (!IS_ERR(tmp)) {
-
-               BUG_ON(flags & LOOKUP_PARENT);
-
-               err = filename_lookup(dfd, tmp, flags, &nd);
-               putname(tmp);
-               if (!err)
-                       *path = nd.path;
-       }
-       return err;
-}
-
-int user_path_at(int dfd, const char __user *name, unsigned flags,
-                struct path *path)
-{
-       return user_path_at_empty(dfd, name, flags, path, NULL);
+       return filename_lookup(dfd, getname_flags(name, flags, empty),
+                              flags, path, NULL);
 }
-EXPORT_SYMBOL(user_path_at);
+EXPORT_SYMBOL(user_path_at_empty);
 
 /*
  * NB: most callers don't do anything directly with the reference to the
@@ -2221,26 +2307,16 @@ EXPORT_SYMBOL(user_path_at);
  *     allocated by getname. So we must hold the reference to it until all
  *     path-walking is complete.
  */
-static struct filename *
-user_path_parent(int dfd, const char __user *path, struct nameidata *nd,
+static inline struct filename *
+user_path_parent(int dfd, const char __user *path,
+                struct path *parent,
+                struct qstr *last,
+                int *type,
                 unsigned int flags)
 {
-       struct filename *s = getname(path);
-       int error;
-
        /* only LOOKUP_REVAL is allowed in extra flags */
-       flags &= LOOKUP_REVAL;
-
-       if (IS_ERR(s))
-               return s;
-
-       error = filename_lookup(dfd, s, flags | LOOKUP_PARENT, nd);
-       if (error) {
-               putname(s);
-               return ERR_PTR(error);
-       }
-
-       return s;
+       return filename_parentat(dfd, getname(path), flags & LOOKUP_REVAL,
+                                parent, last, type);
 }
 
 /**
@@ -2279,10 +2355,8 @@ mountpoint_last(struct nameidata *nd, struct path *path)
 
        /* If we're in rcuwalk, drop out of it to handle last component */
        if (nd->flags & LOOKUP_RCU) {
-               if (unlazy_walk(nd, NULL)) {
-                       error = -ECHILD;
-                       goto out;
-               }
+               if (unlazy_walk(nd, NULL, 0))
+                       return -ECHILD;
        }
 
        nd->flags &= ~LOOKUP_PARENT;
@@ -2290,7 +2364,7 @@ mountpoint_last(struct nameidata *nd, struct path *path)
        if (unlikely(nd->last_type != LAST_NORM)) {
                error = handle_dots(nd, nd->last_type);
                if (error)
-                       goto out;
+                       return error;
                dentry = dget(nd->path.dentry);
                goto done;
        }
@@ -2305,74 +2379,60 @@ mountpoint_last(struct nameidata *nd, struct path *path)
                 */
                dentry = d_alloc(dir, &nd->last);
                if (!dentry) {
-                       error = -ENOMEM;
                        mutex_unlock(&dir->d_inode->i_mutex);
-                       goto out;
+                       return -ENOMEM;
                }
                dentry = lookup_real(dir->d_inode, dentry, nd->flags);
-               error = PTR_ERR(dentry);
                if (IS_ERR(dentry)) {
                        mutex_unlock(&dir->d_inode->i_mutex);
-                       goto out;
+                       return PTR_ERR(dentry);
                }
        }
        mutex_unlock(&dir->d_inode->i_mutex);
 
 done:
        if (d_is_negative(dentry)) {
-               error = -ENOENT;
                dput(dentry);
-               goto out;
+               return -ENOENT;
        }
+       if (nd->depth)
+               put_link(nd);
        path->dentry = dentry;
        path->mnt = nd->path.mnt;
-       if (should_follow_link(dentry, nd->flags & LOOKUP_FOLLOW))
-               return 1;
+       error = should_follow_link(nd, path, nd->flags & LOOKUP_FOLLOW,
+                                  d_backing_inode(dentry), 0);
+       if (unlikely(error))
+               return error;
        mntget(path->mnt);
        follow_mount(path);
-       error = 0;
-out:
-       terminate_walk(nd);
-       return error;
+       return 0;
 }
 
 /**
  * path_mountpoint - look up a path to be umounted
- * @dfd:       directory file descriptor to start walk from
- * @name:      full pathname to walk
- * @path:      pointer to container for result
+ * @nameidata: lookup context
  * @flags:     lookup flags
+ * @path:      pointer to container for result
  *
  * Look up the given name, but don't attempt to revalidate the last component.
  * Returns 0 and "path" will be valid on success; Returns error otherwise.
  */
 static int
-path_mountpoint(int dfd, const struct filename *name, struct path *path,
-               unsigned int flags)
+path_mountpoint(struct nameidata *nd, unsigned flags, struct path *path)
 {
-       struct nameidata nd;
+       const char *s = path_init(nd, flags);
        int err;
-
-       err = path_init(dfd, name, flags, &nd);
-       if (unlikely(err))
-               goto out;
-
-       err = mountpoint_last(&nd, path);
-       while (err > 0) {
-               void *cookie;
-               struct path link = *path;
-               err = may_follow_link(&link, &nd);
-               if (unlikely(err))
-                       break;
-               nd.flags |= LOOKUP_PARENT;
-               err = follow_link(&link, &nd, &cookie);
-               if (err)
+       if (IS_ERR(s))
+               return PTR_ERR(s);
+       while (!(err = link_path_walk(s, nd)) &&
+               (err = mountpoint_last(nd, path)) > 0) {
+               s = trailing_symlink(nd);
+               if (IS_ERR(s)) {
+                       err = PTR_ERR(s);
                        break;
-               err = mountpoint_last(&nd, path);
-               put_link(&nd, &link, cookie);
+               }
        }
-out:
-       path_cleanup(&nd);
+       terminate_walk(nd);
        return err;
 }
 
@@ -2380,16 +2440,19 @@ static int
 filename_mountpoint(int dfd, struct filename *name, struct path *path,
                        unsigned int flags)
 {
+       struct nameidata nd;
        int error;
        if (IS_ERR(name))
                return PTR_ERR(name);
-       error = path_mountpoint(dfd, name, path, flags | LOOKUP_RCU);
+       set_nameidata(&nd, dfd, name);
+       error = path_mountpoint(&nd, flags | LOOKUP_RCU, path);
        if (unlikely(error == -ECHILD))
-               error = path_mountpoint(dfd, name, path, flags);
+               error = path_mountpoint(&nd, flags, path);
        if (unlikely(error == -ESTALE))
-               error = path_mountpoint(dfd, name, path, flags | LOOKUP_REVAL);
+               error = path_mountpoint(&nd, flags | LOOKUP_REVAL, path);
        if (likely(!error))
                audit_inode(name, path->dentry, 0);
+       restore_nameidata();
        putname(name);
        return error;
 }
@@ -2456,7 +2519,7 @@ EXPORT_SYMBOL(__check_sticky);
  */
 static int may_delete(struct inode *dir, struct dentry *victim, bool isdir)
 {
-       struct inode *inode = victim->d_inode;
+       struct inode *inode = d_backing_inode(victim);
        int error;
 
        if (d_is_negative(victim))
@@ -2922,18 +2985,19 @@ out_dput:
 /*
  * Handle the last step of open()
  */
-static int do_last(struct nameidata *nd, struct path *path,
+static int do_last(struct nameidata *nd,
                   struct file *file, const struct open_flags *op,
-                  int *opened, struct filename *name)
+                  int *opened)
 {
        struct dentry *dir = nd->path.dentry;
        int open_flag = op->open_flag;
        bool will_truncate = (open_flag & O_TRUNC) != 0;
        bool got_write = false;
        int acc_mode = op->acc_mode;
+       unsigned seq;
        struct inode *inode;
-       bool symlink_ok = false;
        struct path save_parent = { .dentry = NULL, .mnt = NULL };
+       struct path path;
        bool retried = false;
        int error;
 
@@ -2942,7 +3006,7 @@ static int do_last(struct nameidata *nd, struct path *path,
 
        if (nd->last_type != LAST_NORM) {
                error = handle_dots(nd, nd->last_type);
-               if (error)
+               if (unlikely(error))
                        return error;
                goto finish_open;
        }
@@ -2950,15 +3014,13 @@ static int do_last(struct nameidata *nd, struct path *path,
        if (!(open_flag & O_CREAT)) {
                if (nd->last.name[nd->last.len])
                        nd->flags |= LOOKUP_FOLLOW | LOOKUP_DIRECTORY;
-               if (open_flag & O_PATH && !(nd->flags & LOOKUP_FOLLOW))
-                       symlink_ok = true;
                /* we _can_ be in RCU mode here */
-               error = lookup_fast(nd, path, &inode);
+               error = lookup_fast(nd, &path, &inode, &seq);
                if (likely(!error))
                        goto finish_lookup;
 
                if (error < 0)
-                       goto out;
+                       return error;
 
                BUG_ON(nd->inode != dir->d_inode);
        } else {
@@ -2972,11 +3034,10 @@ static int do_last(struct nameidata *nd, struct path *path,
                if (error)
                        return error;
 
-               audit_inode(name, dir, LOOKUP_PARENT);
-               error = -EISDIR;
+               audit_inode(nd->name, dir, LOOKUP_PARENT);
                /* trailing slashes? */
-               if (nd->last.name[nd->last.len])
-                       goto out;
+               if (unlikely(nd->last.name[nd->last.len]))
+                       return -EISDIR;
        }
 
 retry_lookup:
@@ -2991,7 +3052,7 @@ retry_lookup:
                 */
        }
        mutex_lock(&dir->d_inode->i_mutex);
-       error = lookup_open(nd, path, file, op, got_write, opened);
+       error = lookup_open(nd, &path, file, op, got_write, opened);
        mutex_unlock(&dir->d_inode->i_mutex);
 
        if (error <= 0) {
@@ -3002,7 +3063,7 @@ retry_lookup:
                    !S_ISREG(file_inode(file)->i_mode))
                        will_truncate = false;
 
-               audit_inode(name, file->f_path.dentry, 0);
+               audit_inode(nd->name, file->f_path.dentry, 0);
                goto opened;
        }
 
@@ -3011,15 +3072,15 @@ retry_lookup:
                open_flag &= ~O_TRUNC;
                will_truncate = false;
                acc_mode = MAY_OPEN;
-               path_to_nameidata(path, nd);
+               path_to_nameidata(&path, nd);
                goto finish_open_created;
        }
 
        /*
         * create/update audit record if it already exists.
         */
-       if (d_is_positive(path->dentry))
-               audit_inode(name, path->dentry, 0);
+       if (d_is_positive(path.dentry))
+               audit_inode(nd->name, path.dentry, 0);
 
        /*
         * If atomic_open() acquired write access it is dropped now due to
@@ -3031,47 +3092,45 @@ retry_lookup:
                got_write = false;
        }
 
-       error = -EEXIST;
-       if ((open_flag & (O_EXCL | O_CREAT)) == (O_EXCL | O_CREAT))
-               goto exit_dput;
-
-       error = follow_managed(path, nd->flags);
-       if (error < 0)
-               goto exit_dput;
+       if (unlikely((open_flag & (O_EXCL | O_CREAT)) == (O_EXCL | O_CREAT))) {
+               path_to_nameidata(&path, nd);
+               return -EEXIST;
+       }
 
-       if (error)
-               nd->flags |= LOOKUP_JUMPED;
+       error = follow_managed(&path, nd);
+       if (unlikely(error < 0))
+               return error;
 
        BUG_ON(nd->flags & LOOKUP_RCU);
-       inode = path->dentry->d_inode;
-       error = -ENOENT;
-       if (d_is_negative(path->dentry)) {
-               path_to_nameidata(path, nd);
-               goto out;
+       inode = d_backing_inode(path.dentry);
+       seq = 0;        /* out of RCU mode, so the value doesn't matter */
+       if (unlikely(d_is_negative(path.dentry))) {
+               path_to_nameidata(&path, nd);
+               return -ENOENT;
        }
 finish_lookup:
-       /* we _can_ be in RCU mode here */
-       if (should_follow_link(path->dentry, !symlink_ok)) {
-               if (nd->flags & LOOKUP_RCU) {
-                       if (unlikely(nd->path.mnt != path->mnt ||
-                                    unlazy_walk(nd, path->dentry))) {
-                               error = -ECHILD;
-                               goto out;
-                       }
-               }
-               BUG_ON(inode != path->dentry->d_inode);
-               return 1;
+       if (nd->depth)
+               put_link(nd);
+       error = should_follow_link(nd, &path, nd->flags & LOOKUP_FOLLOW,
+                                  inode, seq);
+       if (unlikely(error))
+               return error;
+
+       if (unlikely(d_is_symlink(path.dentry)) && !(open_flag & O_PATH)) {
+               path_to_nameidata(&path, nd);
+               return -ELOOP;
        }
 
-       if ((nd->flags & LOOKUP_RCU) || nd->path.mnt != path->mnt) {
-               path_to_nameidata(path, nd);
+       if ((nd->flags & LOOKUP_RCU) || nd->path.mnt != path.mnt) {
+               path_to_nameidata(&path, nd);
        } else {
                save_parent.dentry = nd->path.dentry;
-               save_parent.mnt = mntget(path->mnt);
-               nd->path.dentry = path->dentry;
+               save_parent.mnt = mntget(path.mnt);
+               nd->path.dentry = path.dentry;
 
        }
        nd->inode = inode;
+       nd->seq = seq;
        /* Why this, you ask?  _Now_ we might have grown LOOKUP_JUMPED... */
 finish_open:
        error = complete_walk(nd);
@@ -3079,7 +3138,7 @@ finish_open:
                path_put(&save_parent);
                return error;
        }
-       audit_inode(name, nd->path.dentry, 0);
+       audit_inode(nd->name, nd->path.dentry, 0);
        error = -EISDIR;
        if ((open_flag & O_CREAT) && d_is_dir(nd->path.dentry))
                goto out;
@@ -3126,12 +3185,8 @@ out:
        if (got_write)
                mnt_drop_write(nd->path.mnt);
        path_put(&save_parent);
-       terminate_walk(nd);
        return error;
 
-exit_dput:
-       path_put_conditional(path, nd);
-       goto out;
 exit_fput:
        fput(file);
        goto out;
@@ -3155,50 +3210,46 @@ stale_open:
        goto retry_lookup;
 }
 
-static int do_tmpfile(int dfd, struct filename *pathname,
-               struct nameidata *nd, int flags,
+static int do_tmpfile(struct nameidata *nd, unsigned flags,
                const struct open_flags *op,
                struct file *file, int *opened)
 {
        static const struct qstr name = QSTR_INIT("/", 1);
-       struct dentry *dentry, *child;
+       struct dentry *child;
        struct inode *dir;
-       int error = path_lookupat(dfd, pathname,
-                                 flags | LOOKUP_DIRECTORY, nd);
+       struct path path;
+       int error = path_lookupat(nd, flags | LOOKUP_DIRECTORY, &path);
        if (unlikely(error))
                return error;
-       error = mnt_want_write(nd->path.mnt);
+       error = mnt_want_write(path.mnt);
        if (unlikely(error))
                goto out;
+       dir = path.dentry->d_inode;
        /* we want directory to be writable */
-       error = inode_permission(nd->inode, MAY_WRITE | MAY_EXEC);
+       error = inode_permission(dir, MAY_WRITE | MAY_EXEC);
        if (error)
                goto out2;
-       dentry = nd->path.dentry;
-       dir = dentry->d_inode;
        if (!dir->i_op->tmpfile) {
                error = -EOPNOTSUPP;
                goto out2;
        }
-       child = d_alloc(dentry, &name);
+       child = d_alloc(path.dentry, &name);
        if (unlikely(!child)) {
                error = -ENOMEM;
                goto out2;
        }
-       nd->flags &= ~LOOKUP_DIRECTORY;
-       nd->flags |= op->intent;
-       dput(nd->path.dentry);
-       nd->path.dentry = child;
-       error = dir->i_op->tmpfile(dir, nd->path.dentry, op->mode);
+       dput(path.dentry);
+       path.dentry = child;
+       error = dir->i_op->tmpfile(dir, child, op->mode);
        if (error)
                goto out2;
-       audit_inode(pathname, nd->path.dentry, 0);
+       audit_inode(nd->name, child, 0);
        /* Don't check for other permissions, the inode was just created */
-       error = may_open(&nd->path, MAY_OPEN, op->open_flag);
+       error = may_open(&path, MAY_OPEN, op->open_flag);
        if (error)
                goto out2;
-       file->f_path.mnt = nd->path.mnt;
-       error = finish_open(file, nd->path.dentry, NULL, opened);
+       file->f_path.mnt = path.mnt;
+       error = finish_open(file, child, NULL, opened);
        if (error)
                goto out2;
        error = open_check_o_direct(file);
@@ -3211,17 +3262,17 @@ static int do_tmpfile(int dfd, struct filename *pathname,
                spin_unlock(&inode->i_lock);
        }
 out2:
-       mnt_drop_write(nd->path.mnt);
+       mnt_drop_write(path.mnt);
 out:
-       path_put(&nd->path);
+       path_put(&path);
        return error;
 }
 
-static struct file *path_openat(int dfd, struct filename *pathname,
-               struct nameidata *nd, const struct open_flags *op, int flags)
+static struct file *path_openat(struct nameidata *nd,
+                       const struct open_flags *op, unsigned flags)
 {
+       const char *s;
        struct file *file;
-       struct path path;
        int opened = 0;
        int error;
 
@@ -3232,37 +3283,25 @@ static struct file *path_openat(int dfd, struct filename *pathname,
        file->f_flags = op->open_flag;
 
        if (unlikely(file->f_flags & __O_TMPFILE)) {
-               error = do_tmpfile(dfd, pathname, nd, flags, op, file, &opened);
+               error = do_tmpfile(nd, flags, op, file, &opened);
                goto out2;
        }
 
-       error = path_init(dfd, pathname, flags, nd);
-       if (unlikely(error))
-               goto out;
-
-       error = do_last(nd, &path, file, op, &opened, pathname);
-       while (unlikely(error > 0)) { /* trailing symlink */
-               struct path link = path;
-               void *cookie;
-               if (!(nd->flags & LOOKUP_FOLLOW)) {
-                       path_put_conditional(&path, nd);
-                       path_put(&nd->path);
-                       error = -ELOOP;
-                       break;
-               }
-               error = may_follow_link(&link, nd);
-               if (unlikely(error))
-                       break;
-               nd->flags |= LOOKUP_PARENT;
+       s = path_init(nd, flags);
+       if (IS_ERR(s)) {
+               put_filp(file);
+               return ERR_CAST(s);
+       }
+       while (!(error = link_path_walk(s, nd)) &&
+               (error = do_last(nd, file, op, &opened)) > 0) {
                nd->flags &= ~(LOOKUP_OPEN|LOOKUP_CREATE|LOOKUP_EXCL);
-               error = follow_link(&link, nd, &cookie);
-               if (unlikely(error))
+               s = trailing_symlink(nd);
+               if (IS_ERR(s)) {
+                       error = PTR_ERR(s);
                        break;
-               error = do_last(nd, &path, file, op, &opened, pathname);
-               put_link(nd, &link, cookie);
+               }
        }
-out:
-       path_cleanup(nd);
+       terminate_walk(nd);
 out2:
        if (!(opened & FILE_OPENED)) {
                BUG_ON(!error);
@@ -3287,11 +3326,13 @@ struct file *do_filp_open(int dfd, struct filename *pathname,
        int flags = op->lookup_flags;
        struct file *filp;
 
-       filp = path_openat(dfd, pathname, &nd, op, flags | LOOKUP_RCU);
+       set_nameidata(&nd, dfd, pathname);
+       filp = path_openat(&nd, op, flags | LOOKUP_RCU);
        if (unlikely(filp == ERR_PTR(-ECHILD)))
-               filp = path_openat(dfd, pathname, &nd, op, flags);
+               filp = path_openat(&nd, op, flags);
        if (unlikely(filp == ERR_PTR(-ESTALE)))
-               filp = path_openat(dfd, pathname, &nd, op, flags | LOOKUP_REVAL);
+               filp = path_openat(&nd, op, flags | LOOKUP_REVAL);
+       restore_nameidata();
        return filp;
 }
 
@@ -3313,11 +3354,13 @@ struct file *do_file_open_root(struct dentry *dentry, struct vfsmount *mnt,
        if (unlikely(IS_ERR(filename)))
                return ERR_CAST(filename);
 
-       file = path_openat(-1, filename, &nd, op, flags | LOOKUP_RCU);
+       set_nameidata(&nd, -1, filename);
+       file = path_openat(&nd, op, flags | LOOKUP_RCU);
        if (unlikely(file == ERR_PTR(-ECHILD)))
-               file = path_openat(-1, filename, &nd, op, flags);
+               file = path_openat(&nd, op, flags);
        if (unlikely(file == ERR_PTR(-ESTALE)))
-               file = path_openat(-1, filename, &nd, op, flags | LOOKUP_REVAL);
+               file = path_openat(&nd, op, flags | LOOKUP_REVAL);
+       restore_nameidata();
        putname(filename);
        return file;
 }
@@ -3326,7 +3369,8 @@ static struct dentry *filename_create(int dfd, struct filename *name,
                                struct path *path, unsigned int lookup_flags)
 {
        struct dentry *dentry = ERR_PTR(-EEXIST);
-       struct nameidata nd;
+       struct qstr last;
+       int type;
        int err2;
        int error;
        bool is_dir = (lookup_flags & LOOKUP_DIRECTORY);
@@ -3337,26 +3381,25 @@ static struct dentry *filename_create(int dfd, struct filename *name,
         */
        lookup_flags &= LOOKUP_REVAL;
 
-       error = filename_lookup(dfd, name, LOOKUP_PARENT|lookup_flags, &nd);
-       if (error)
-               return ERR_PTR(error);
+       name = filename_parentat(dfd, name, lookup_flags, path, &last, &type);
+       if (IS_ERR(name))
+               return ERR_CAST(name);
 
        /*
         * Yucky last component or no last component at all?
         * (foo/., foo/.., /////)
         */
-       if (nd.last_type != LAST_NORM)
+       if (unlikely(type != LAST_NORM))
                goto out;
-       nd.flags &= ~LOOKUP_PARENT;
-       nd.flags |= LOOKUP_CREATE | LOOKUP_EXCL;
 
        /* don't fail immediately if it's r/o, at least try to report other errors */
-       err2 = mnt_want_write(nd.path.mnt);
+       err2 = mnt_want_write(path->mnt);
        /*
         * Do the final lookup.
         */
-       mutex_lock_nested(&nd.path.dentry->d_inode->i_mutex, I_MUTEX_PARENT);
-       dentry = lookup_hash(&nd);
+       lookup_flags |= LOOKUP_CREATE | LOOKUP_EXCL;
+       mutex_lock_nested(&path->dentry->d_inode->i_mutex, I_MUTEX_PARENT);
+       dentry = __lookup_hash(&last, path->dentry, lookup_flags);
        if (IS_ERR(dentry))
                goto unlock;
 
@@ -3370,7 +3413,7 @@ static struct dentry *filename_create(int dfd, struct filename *name,
         * all is fine. Let's be bastards - you had / on the end, you've
         * been asking for (non-existent) directory. -ENOENT for you.
         */
-       if (unlikely(!is_dir && nd.last.name[nd.last.len])) {
+       if (unlikely(!is_dir && last.name[last.len])) {
                error = -ENOENT;
                goto fail;
        }
@@ -3378,31 +3421,26 @@ static struct dentry *filename_create(int dfd, struct filename *name,
                error = err2;
                goto fail;
        }
-       *path = nd.path;
+       putname(name);
        return dentry;
 fail:
        dput(dentry);
        dentry = ERR_PTR(error);
 unlock:
-       mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
+       mutex_unlock(&path->dentry->d_inode->i_mutex);
        if (!err2)
-               mnt_drop_write(nd.path.mnt);
+               mnt_drop_write(path->mnt);
 out:
-       path_put(&nd.path);
+       path_put(path);
+       putname(name);
        return dentry;
 }
 
 struct dentry *kern_path_create(int dfd, const char *pathname,
                                struct path *path, unsigned int lookup_flags)
 {
-       struct filename *filename = getname_kernel(pathname);
-       struct dentry *res;
-
-       if (IS_ERR(filename))
-               return ERR_CAST(filename);
-       res = filename_create(dfd, filename, path, lookup_flags);
-       putname(filename);
-       return res;
+       return filename_create(dfd, getname_kernel(pathname),
+                               path, lookup_flags);
 }
 EXPORT_SYMBOL(kern_path_create);
 
@@ -3415,16 +3453,10 @@ void done_path_create(struct path *path, struct dentry *dentry)
 }
 EXPORT_SYMBOL(done_path_create);
 
-struct dentry *user_path_create(int dfd, const char __user *pathname,
+inline struct dentry *user_path_create(int dfd, const char __user *pathname,
                                struct path *path, unsigned int lookup_flags)
 {
-       struct filename *tmp = getname(pathname);
-       struct dentry *res;
-       if (IS_ERR(tmp))
-               return ERR_CAST(tmp);
-       res = filename_create(dfd, tmp, path, lookup_flags);
-       putname(tmp);
-       return res;
+       return filename_create(dfd, getname(pathname), path, lookup_flags);
 }
 EXPORT_SYMBOL(user_path_create);
 
@@ -3645,14 +3677,17 @@ static long do_rmdir(int dfd, const char __user *pathname)
        int error = 0;
        struct filename *name;
        struct dentry *dentry;
-       struct nameidata nd;
+       struct path path;
+       struct qstr last;
+       int type;
        unsigned int lookup_flags = 0;
 retry:
-       name = user_path_parent(dfd, pathname, &nd, lookup_flags);
+       name = user_path_parent(dfd, pathname,
+                               &path, &last, &type, lookup_flags);
        if (IS_ERR(name))
                return PTR_ERR(name);
 
-       switch(nd.last_type) {
+       switch (type) {
        case LAST_DOTDOT:
                error = -ENOTEMPTY;
                goto exit1;
@@ -3664,13 +3699,12 @@ retry:
                goto exit1;
        }
 
-       nd.flags &= ~LOOKUP_PARENT;
-       error = mnt_want_write(nd.path.mnt);
+       error = mnt_want_write(path.mnt);
        if (error)
                goto exit1;
 
-       mutex_lock_nested(&nd.path.dentry->d_inode->i_mutex, I_MUTEX_PARENT);
-       dentry = lookup_hash(&nd);
+       mutex_lock_nested(&path.dentry->d_inode->i_mutex, I_MUTEX_PARENT);
+       dentry = __lookup_hash(&last, path.dentry, lookup_flags);
        error = PTR_ERR(dentry);
        if (IS_ERR(dentry))
                goto exit2;
@@ -3678,17 +3712,17 @@ retry:
                error = -ENOENT;
                goto exit3;
        }
-       error = security_path_rmdir(&nd.path, dentry);
+       error = security_path_rmdir(&path, dentry);
        if (error)
                goto exit3;
-       error = vfs_rmdir(nd.path.dentry->d_inode, dentry);
+       error = vfs_rmdir(path.dentry->d_inode, dentry);
 exit3:
        dput(dentry);
 exit2:
-       mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
-       mnt_drop_write(nd.path.mnt);
+       mutex_unlock(&path.dentry->d_inode->i_mutex);
+       mnt_drop_write(path.mnt);
 exit1:
-       path_put(&nd.path);
+       path_put(&path);
        putname(name);
        if (retry_estale(error, lookup_flags)) {
                lookup_flags |= LOOKUP_REVAL;
@@ -3771,43 +3805,45 @@ static long do_unlinkat(int dfd, const char __user *pathname)
        int error;
        struct filename *name;
        struct dentry *dentry;
-       struct nameidata nd;
+       struct path path;
+       struct qstr last;
+       int type;
        struct inode *inode = NULL;
        struct inode *delegated_inode = NULL;
        unsigned int lookup_flags = 0;
 retry:
-       name = user_path_parent(dfd, pathname, &nd, lookup_flags);
+       name = user_path_parent(dfd, pathname,
+                               &path, &last, &type, lookup_flags);
        if (IS_ERR(name))
                return PTR_ERR(name);
 
        error = -EISDIR;
-       if (nd.last_type != LAST_NORM)
+       if (type != LAST_NORM)
                goto exit1;
 
-       nd.flags &= ~LOOKUP_PARENT;
-       error = mnt_want_write(nd.path.mnt);
+       error = mnt_want_write(path.mnt);
        if (error)
                goto exit1;
 retry_deleg:
-       mutex_lock_nested(&nd.path.dentry->d_inode->i_mutex, I_MUTEX_PARENT);
-       dentry = lookup_hash(&nd);
+       mutex_lock_nested(&path.dentry->d_inode->i_mutex, I_MUTEX_PARENT);
+       dentry = __lookup_hash(&last, path.dentry, lookup_flags);
        error = PTR_ERR(dentry);
        if (!IS_ERR(dentry)) {
                /* Why not before? Because we want correct error value */
-               if (nd.last.name[nd.last.len])
+               if (last.name[last.len])
                        goto slashes;
                inode = dentry->d_inode;
                if (d_is_negative(dentry))
                        goto slashes;
                ihold(inode);
-               error = security_path_unlink(&nd.path, dentry);
+               error = security_path_unlink(&path, dentry);
                if (error)
                        goto exit2;
-               error = vfs_unlink(nd.path.dentry->d_inode, dentry, &delegated_inode);
+               error = vfs_unlink(path.dentry->d_inode, dentry, &delegated_inode);
 exit2:
                dput(dentry);
        }
-       mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
+       mutex_unlock(&path.dentry->d_inode->i_mutex);
        if (inode)
                iput(inode);    /* truncate the inode here */
        inode = NULL;
@@ -3816,9 +3852,9 @@ exit2:
                if (!error)
                        goto retry_deleg;
        }
-       mnt_drop_write(nd.path.mnt);
+       mnt_drop_write(path.mnt);
 exit1:
-       path_put(&nd.path);
+       path_put(&path);
        putname(name);
        if (retry_estale(error, lookup_flags)) {
                lookup_flags |= LOOKUP_REVAL;
@@ -4248,14 +4284,15 @@ EXPORT_SYMBOL(vfs_rename);
 SYSCALL_DEFINE5(renameat2, int, olddfd, const char __user *, oldname,
                int, newdfd, const char __user *, newname, unsigned int, flags)
 {
-       struct dentry *old_dir, *new_dir;
        struct dentry *old_dentry, *new_dentry;
        struct dentry *trap;
-       struct nameidata oldnd, newnd;
+       struct path old_path, new_path;
+       struct qstr old_last, new_last;
+       int old_type, new_type;
        struct inode *delegated_inode = NULL;
        struct filename *from;
        struct filename *to;
-       unsigned int lookup_flags = 0;
+       unsigned int lookup_flags = 0, target_flags = LOOKUP_RENAME_TARGET;
        bool should_retry = false;
        int error;
 
@@ -4269,47 +4306,45 @@ SYSCALL_DEFINE5(renameat2, int, olddfd, const char __user *, oldname,
        if ((flags & RENAME_WHITEOUT) && !capable(CAP_MKNOD))
                return -EPERM;
 
+       if (flags & RENAME_EXCHANGE)
+               target_flags = 0;
+
 retry:
-       from = user_path_parent(olddfd, oldname, &oldnd, lookup_flags);
+       from = user_path_parent(olddfd, oldname,
+                               &old_path, &old_last, &old_type, lookup_flags);
        if (IS_ERR(from)) {
                error = PTR_ERR(from);
                goto exit;
        }
 
-       to = user_path_parent(newdfd, newname, &newnd, lookup_flags);
+       to = user_path_parent(newdfd, newname,
+                               &new_path, &new_last, &new_type, lookup_flags);
        if (IS_ERR(to)) {
                error = PTR_ERR(to);
                goto exit1;
        }
 
        error = -EXDEV;
-       if (oldnd.path.mnt != newnd.path.mnt)
+       if (old_path.mnt != new_path.mnt)
                goto exit2;
 
-       old_dir = oldnd.path.dentry;
        error = -EBUSY;
-       if (oldnd.last_type != LAST_NORM)
+       if (old_type != LAST_NORM)
                goto exit2;
 
-       new_dir = newnd.path.dentry;
        if (flags & RENAME_NOREPLACE)
                error = -EEXIST;
-       if (newnd.last_type != LAST_NORM)
+       if (new_type != LAST_NORM)
                goto exit2;
 
-       error = mnt_want_write(oldnd.path.mnt);
+       error = mnt_want_write(old_path.mnt);
        if (error)
                goto exit2;
 
-       oldnd.flags &= ~LOOKUP_PARENT;
-       newnd.flags &= ~LOOKUP_PARENT;
-       if (!(flags & RENAME_EXCHANGE))
-               newnd.flags |= LOOKUP_RENAME_TARGET;
-
 retry_deleg:
-       trap = lock_rename(new_dir, old_dir);
+       trap = lock_rename(new_path.dentry, old_path.dentry);
 
-       old_dentry = lookup_hash(&oldnd);
+       old_dentry = __lookup_hash(&old_last, old_path.dentry, lookup_flags);
        error = PTR_ERR(old_dentry);
        if (IS_ERR(old_dentry))
                goto exit3;
@@ -4317,7 +4352,7 @@ retry_deleg:
        error = -ENOENT;
        if (d_is_negative(old_dentry))
                goto exit4;
-       new_dentry = lookup_hash(&newnd);
+       new_dentry = __lookup_hash(&new_last, new_path.dentry, lookup_flags | target_flags);
        error = PTR_ERR(new_dentry);
        if (IS_ERR(new_dentry))
                goto exit4;
@@ -4331,16 +4366,16 @@ retry_deleg:
 
                if (!d_is_dir(new_dentry)) {
                        error = -ENOTDIR;
-                       if (newnd.last.name[newnd.last.len])
+                       if (new_last.name[new_last.len])
                                goto exit5;
                }
        }
        /* unless the source is a directory trailing slashes give -ENOTDIR */
        if (!d_is_dir(old_dentry)) {
                error = -ENOTDIR;
-               if (oldnd.last.name[oldnd.last.len])
+               if (old_last.name[old_last.len])
                        goto exit5;
-               if (!(flags & RENAME_EXCHANGE) && newnd.last.name[newnd.last.len])
+               if (!(flags & RENAME_EXCHANGE) && new_last.name[new_last.len])
                        goto exit5;
        }
        /* source should not be ancestor of target */
@@ -4353,32 +4388,32 @@ retry_deleg:
        if (new_dentry == trap)
                goto exit5;
 
-       error = security_path_rename(&oldnd.path, old_dentry,
-                                    &newnd.path, new_dentry, flags);
+       error = security_path_rename(&old_path, old_dentry,
+                                    &new_path, new_dentry, flags);
        if (error)
                goto exit5;
-       error = vfs_rename(old_dir->d_inode, old_dentry,
-                          new_dir->d_inode, new_dentry,
+       error = vfs_rename(old_path.dentry->d_inode, old_dentry,
+                          new_path.dentry->d_inode, new_dentry,
                           &delegated_inode, flags);
 exit5:
        dput(new_dentry);
 exit4:
        dput(old_dentry);
 exit3:
-       unlock_rename(new_dir, old_dir);
+       unlock_rename(new_path.dentry, old_path.dentry);
        if (delegated_inode) {
                error = break_deleg_wait(&delegated_inode);
                if (!error)
                        goto retry_deleg;
        }
-       mnt_drop_write(oldnd.path.mnt);
+       mnt_drop_write(old_path.mnt);
 exit2:
        if (retry_estale(error, lookup_flags))
                should_retry = true;
-       path_put(&newnd.path);
+       path_put(&new_path);
        putname(to);
 exit1:
-       path_put(&oldnd.path);
+       path_put(&old_path);
        putname(from);
        if (should_retry) {
                should_retry = false;
@@ -4437,18 +4472,19 @@ EXPORT_SYMBOL(readlink_copy);
  */
 int generic_readlink(struct dentry *dentry, char __user *buffer, int buflen)
 {
-       struct nameidata nd;
        void *cookie;
+       struct inode *inode = d_inode(dentry);
+       const char *link = inode->i_link;
        int res;
 
-       nd.depth = 0;
-       cookie = dentry->d_inode->i_op->follow_link(dentry, &nd);
-       if (IS_ERR(cookie))
-               return PTR_ERR(cookie);
-
-       res = readlink_copy(buffer, buflen, nd_get_link(&nd));
-       if (dentry->d_inode->i_op->put_link)
-               dentry->d_inode->i_op->put_link(dentry, &nd, cookie);
+       if (!link) {
+               link = inode->i_op->follow_link(dentry, &cookie);
+               if (IS_ERR(link))
+                       return PTR_ERR(link);
+       }
+       res = readlink_copy(buffer, buflen, link);
+       if (inode->i_op->put_link)
+               inode->i_op->put_link(inode, cookie);
        return res;
 }
 EXPORT_SYMBOL(generic_readlink);
@@ -4480,22 +4516,21 @@ int page_readlink(struct dentry *dentry, char __user *buffer, int buflen)
 }
 EXPORT_SYMBOL(page_readlink);
 
-void *page_follow_link_light(struct dentry *dentry, struct nameidata *nd)
+const char *page_follow_link_light(struct dentry *dentry, void **cookie)
 {
        struct page *page = NULL;
-       nd_set_link(nd, page_getlink(dentry, &page));
-       return page;
+       char *res = page_getlink(dentry, &page);
+       if (!IS_ERR(res))
+               *cookie = page;
+       return res;
 }
 EXPORT_SYMBOL(page_follow_link_light);
 
-void page_put_link(struct dentry *dentry, struct nameidata *nd, void *cookie)
+void page_put_link(struct inode *unused, void *cookie)
 {
        struct page *page = cookie;
-
-       if (page) {
-               kunmap(page);
-               page_cache_release(page);
-       }
+       kunmap(page);
+       page_cache_release(page);
 }
 EXPORT_SYMBOL(page_put_link);
 
index 1b9e11167baedc310b8f5b6585ac019abd78b915..9c1c43d0d4f10112bcf711068873a70e2a057200 100644 (file)
@@ -590,24 +590,35 @@ static void delayed_free_vfsmnt(struct rcu_head *head)
 }
 
 /* call under rcu_read_lock */
-bool legitimize_mnt(struct vfsmount *bastard, unsigned seq)
+int __legitimize_mnt(struct vfsmount *bastard, unsigned seq)
 {
        struct mount *mnt;
        if (read_seqretry(&mount_lock, seq))
-               return false;
+               return 1;
        if (bastard == NULL)
-               return true;
+               return 0;
        mnt = real_mount(bastard);
        mnt_add_count(mnt, 1);
        if (likely(!read_seqretry(&mount_lock, seq)))
-               return true;
+               return 0;
        if (bastard->mnt_flags & MNT_SYNC_UMOUNT) {
                mnt_add_count(mnt, -1);
-               return false;
+               return 1;
+       }
+       return -1;
+}
+
+/* call under rcu_read_lock */
+bool legitimize_mnt(struct vfsmount *bastard, unsigned seq)
+{
+       int res = __legitimize_mnt(bastard, seq);
+       if (likely(!res))
+               return true;
+       if (unlikely(res < 0)) {
+               rcu_read_unlock();
+               mntput(bastard);
+               rcu_read_lock();
        }
-       rcu_read_unlock();
-       mntput(bastard);
-       rcu_read_lock();
        return false;
 }
 
index 2d56200655fe600ae73d11c382af5815285fab48..b6de433da5db14ab788ba358ce94b5952d5c601f 100644 (file)
@@ -20,7 +20,6 @@
 #include <linux/stat.h>
 #include <linux/mm.h>
 #include <linux/string.h>
-#include <linux/namei.h>
 
 /* Symlink caching in the page cache is even more simplistic
  * and straight-forward than readdir caching.
@@ -43,7 +42,7 @@ error:
        return -EIO;
 }
 
-static void *nfs_follow_link(struct dentry *dentry, struct nameidata *nd)
+static const char *nfs_follow_link(struct dentry *dentry, void **cookie)
 {
        struct inode *inode = d_inode(dentry);
        struct page *page;
@@ -51,19 +50,13 @@ static void *nfs_follow_link(struct dentry *dentry, struct nameidata *nd)
 
        err = ERR_PTR(nfs_revalidate_mapping(inode, inode->i_mapping));
        if (err)
-               goto read_failed;
+               return err;
        page = read_cache_page(&inode->i_data, 0,
                                (filler_t *)nfs_symlink_filler, inode);
-       if (IS_ERR(page)) {
-               err = page;
-               goto read_failed;
-       }
-       nd_set_link(nd, kmap(page));
-       return page;
-
-read_failed:
-       nd_set_link(nd, err);
-       return NULL;
+       if (IS_ERR(page))
+               return ERR_CAST(page);
+       *cookie = page;
+       return kmap(page);
 }
 
 /*
index 0f35b80d17fe019cdae356ecaba8359ac9f9a9a2..443abecf01b7d45cfb19be0ee63032b156ae718b 100644 (file)
@@ -35,7 +35,7 @@
  * ntfs_lookup - find the inode represented by a dentry in a directory inode
  * @dir_ino:   directory inode in which to look for the inode
  * @dent:      dentry representing the inode to look for
- * @nd:                lookup nameidata
+ * @flags:     lookup flags
  *
  * In short, ntfs_lookup() looks for the inode represented by the dentry @dent
  * in the directory inode @dir_ino and if found attaches the inode to the
index 98e5a52dc68c9503136b8ff6a89b44dacd5478bf..e0250bdcc44005db6510ac516a53923282db5d12 100644 (file)
--- a/fs/open.c
+++ b/fs/open.c
@@ -367,7 +367,7 @@ retry:
        if (res)
                goto out;
 
-       inode = path.dentry->d_inode;
+       inode = d_backing_inode(path.dentry);
 
        if ((mode & MAY_EXEC) && S_ISREG(inode->i_mode)) {
                /*
index 04f1248846877d019625c861b474702177b38ae5..308379b2d0b2cb82b6ad505755484a19b4042fa0 100644 (file)
@@ -140,11 +140,12 @@ struct ovl_link_data {
        void *cookie;
 };
 
-static void *ovl_follow_link(struct dentry *dentry, struct nameidata *nd)
+static const char *ovl_follow_link(struct dentry *dentry, void **cookie)
 {
-       void *ret;
        struct dentry *realdentry;
        struct inode *realinode;
+       struct ovl_link_data *data = NULL;
+       const char *ret;
 
        realdentry = ovl_dentry_real(dentry);
        realinode = realdentry->d_inode;
@@ -152,28 +153,28 @@ static void *ovl_follow_link(struct dentry *dentry, struct nameidata *nd)
        if (WARN_ON(!realinode->i_op->follow_link))
                return ERR_PTR(-EPERM);
 
-       ret = realinode->i_op->follow_link(realdentry, nd);
-       if (IS_ERR(ret))
-               return ret;
-
        if (realinode->i_op->put_link) {
-               struct ovl_link_data *data;
-
                data = kmalloc(sizeof(struct ovl_link_data), GFP_KERNEL);
-               if (!data) {
-                       realinode->i_op->put_link(realdentry, nd, ret);
+               if (!data)
                        return ERR_PTR(-ENOMEM);
-               }
                data->realdentry = realdentry;
-               data->cookie = ret;
+       }
 
-               return data;
-       } else {
-               return NULL;
+       ret = realinode->i_op->follow_link(realdentry, cookie);
+       if (IS_ERR_OR_NULL(ret)) {
+               kfree(data);
+               return ret;
        }
+
+       if (data)
+               data->cookie = *cookie;
+
+       *cookie = data;
+
+       return ret;
 }
 
-static void ovl_put_link(struct dentry *dentry, struct nameidata *nd, void *c)
+static void ovl_put_link(struct inode *unused, void *c)
 {
        struct inode *realinode;
        struct ovl_link_data *data = c;
@@ -182,7 +183,7 @@ static void ovl_put_link(struct dentry *dentry, struct nameidata *nd, void *c)
                return;
 
        realinode = data->realdentry->d_inode;
-       realinode->i_op->put_link(data->realdentry, nd, data->cookie);
+       realinode->i_op->put_link(realinode, data->cookie);
        kfree(data);
 }
 
index 093ca14f570154f5de1cc6db10c7995e1b7cba02..286a422f440e9ed8a6c4b9c54ad7b7c85d28d6f2 100644 (file)
@@ -1380,7 +1380,7 @@ static int proc_exe_link(struct dentry *dentry, struct path *exe_path)
                return -ENOENT;
 }
 
-static void *proc_pid_follow_link(struct dentry *dentry, struct nameidata *nd)
+static const char *proc_pid_follow_link(struct dentry *dentry, void **cookie)
 {
        struct inode *inode = d_inode(dentry);
        struct path path;
@@ -1394,7 +1394,7 @@ static void *proc_pid_follow_link(struct dentry *dentry, struct nameidata *nd)
        if (error)
                goto out;
 
-       nd_jump_link(nd, &path);
+       nd_jump_link(&path);
        return NULL;
 out:
        return ERR_PTR(error);
index 8272aaba1bb06fd4b65416979155f18b590291a4..afe232b9df6e5b6c83779712c8cd068169992a55 100644 (file)
@@ -23,7 +23,6 @@
 #include <linux/slab.h>
 #include <linux/mount.h>
 #include <linux/magic.h>
-#include <linux/namei.h>
 
 #include <asm/uaccess.h>
 
@@ -394,16 +393,16 @@ static const struct file_operations proc_reg_file_ops_no_compat = {
 };
 #endif
 
-static void *proc_follow_link(struct dentry *dentry, struct nameidata *nd)
+static const char *proc_follow_link(struct dentry *dentry, void **cookie)
 {
        struct proc_dir_entry *pde = PDE(d_inode(dentry));
        if (unlikely(!use_pde(pde)))
                return ERR_PTR(-EINVAL);
-       nd_set_link(nd, pde->data);
-       return pde;
+       *cookie = pde;
+       return pde->data;
 }
 
-static void proc_put_link(struct dentry *dentry, struct nameidata *nd, void *p)
+static void proc_put_link(struct inode *unused, void *p)
 {
        unuse_pde(p);
 }
index e512642dbbdcb3cfe37f97b586770d695e2a755f..f6e8354b8cea20a936f6a4f8ae0335fd7fa36bd4 100644 (file)
@@ -30,7 +30,7 @@ static const struct proc_ns_operations *ns_entries[] = {
        &mntns_operations,
 };
 
-static void *proc_ns_follow_link(struct dentry *dentry, struct nameidata *nd)
+static const char *proc_ns_follow_link(struct dentry *dentry, void **cookie)
 {
        struct inode *inode = d_inode(dentry);
        const struct proc_ns_operations *ns_ops = PROC_I(inode)->ns_ops;
@@ -45,7 +45,7 @@ static void *proc_ns_follow_link(struct dentry *dentry, struct nameidata *nd)
        if (ptrace_may_access(task, PTRACE_MODE_READ)) {
                error = ns_get_path(&ns_path, task, ns_ops);
                if (!error)
-                       nd_jump_link(nd, &ns_path);
+                       nd_jump_link(&ns_path);
        }
        put_task_struct(task);
        return error;
index 6195b4a7c3b17f8c7feb09542e7e435b2cd59356..113b8d061fc023858ab152a5033e029d085f27a6 100644 (file)
@@ -1,5 +1,4 @@
 #include <linux/sched.h>
-#include <linux/namei.h>
 #include <linux/slab.h>
 #include <linux/pid_namespace.h>
 #include "internal.h"
@@ -19,21 +18,20 @@ static int proc_self_readlink(struct dentry *dentry, char __user *buffer,
        return readlink_copy(buffer, buflen, tmp);
 }
 
-static void *proc_self_follow_link(struct dentry *dentry, struct nameidata *nd)
+static const char *proc_self_follow_link(struct dentry *dentry, void **cookie)
 {
        struct pid_namespace *ns = dentry->d_sb->s_fs_info;
        pid_t tgid = task_tgid_nr_ns(current, ns);
-       char *name = ERR_PTR(-ENOENT);
-       if (tgid) {
-               /* 11 for max length of signed int in decimal + NULL term */
-               name = kmalloc(12, GFP_KERNEL);
-               if (!name)
-                       name = ERR_PTR(-ENOMEM);
-               else
-                       sprintf(name, "%d", tgid);
-       }
-       nd_set_link(nd, name);
-       return NULL;
+       char *name;
+
+       if (!tgid)
+               return ERR_PTR(-ENOENT);
+       /* 11 for max length of signed int in decimal + NULL term */
+       name = kmalloc(12, GFP_KERNEL);
+       if (!name)
+               return ERR_PTR(-ENOMEM);
+       sprintf(name, "%d", tgid);
+       return *cookie = name;
 }
 
 static const struct inode_operations proc_self_inode_operations = {
index a8371993b4fb7822b865cd4b2a02bc63d9642fa7..947b0f4fd0a194057334762bafeff3548c276568 100644 (file)
@@ -1,5 +1,4 @@
 #include <linux/sched.h>
-#include <linux/namei.h>
 #include <linux/slab.h>
 #include <linux/pid_namespace.h>
 #include "internal.h"
@@ -20,21 +19,20 @@ static int proc_thread_self_readlink(struct dentry *dentry, char __user *buffer,
        return readlink_copy(buffer, buflen, tmp);
 }
 
-static void *proc_thread_self_follow_link(struct dentry *dentry, struct nameidata *nd)
+static const char *proc_thread_self_follow_link(struct dentry *dentry, void **cookie)
 {
        struct pid_namespace *ns = dentry->d_sb->s_fs_info;
        pid_t tgid = task_tgid_nr_ns(current, ns);
        pid_t pid = task_pid_nr_ns(current, ns);
-       char *name = ERR_PTR(-ENOENT);
-       if (pid) {
-               name = kmalloc(PROC_NUMBUF + 6 + PROC_NUMBUF, GFP_KERNEL);
-               if (!name)
-                       name = ERR_PTR(-ENOMEM);
-               else
-                       sprintf(name, "%d/task/%d", tgid, pid);
-       }
-       nd_set_link(nd, name);
-       return NULL;
+       char *name;
+
+       if (!pid)
+               return ERR_PTR(-ENOENT);
+       name = kmalloc(PROC_NUMBUF + 6 + PROC_NUMBUF, GFP_KERNEL);
+       if (!name)
+               return ERR_PTR(-ENOMEM);
+       sprintf(name, "%d/task/%d", tgid, pid);
+       return *cookie = name;
 }
 
 static const struct inode_operations proc_thread_self_inode_operations = {
index f684c750e08a549283733c1340420a61733b4b61..015547330e88699dccb37fe1d4509e1dc07394f7 100644 (file)
@@ -189,7 +189,7 @@ static int __pollwake(wait_queue_t *wait, unsigned mode, int sync, void *key)
         * doesn't imply write barrier and the users expect write
         * barrier semantics on wakeup functions.  The following
         * smp_wmb() is equivalent to smp_wmb() in try_to_wake_up()
-        * and is paired with set_mb() in poll_schedule_timeout.
+        * and is paired with smp_store_mb() in poll_schedule_timeout.
         */
        smp_wmb();
        pwq->triggered = 1;
@@ -244,7 +244,7 @@ int poll_schedule_timeout(struct poll_wqueues *pwq, int state,
        /*
         * Prepare for the next iteration.
         *
-        * The following set_mb() serves two purposes.  First, it's
+        * The following smp_store_mb() serves two purposes.  First, it's
         * the counterpart rmb of the wmb in pollwake() such that data
         * written before wake up is always visible after wake up.
         * Second, the full barrier guarantees that triggered clearing
@@ -252,7 +252,7 @@ int poll_schedule_timeout(struct poll_wqueues *pwq, int state,
         * this problem doesn't exist for the first iteration as
         * add_wait_queue() has full barrier semantics.
         */
-       set_mb(pwq->triggered, 0);
+       smp_store_mb(pwq->triggered, 0);
 
        return rc;
 }
index 3591f9d7a48a4a38948d30f7b523103471b85889..7a75e70a4b61b9d9fdc9fdd5d087be105304d62e 100644 (file)
@@ -5,4 +5,4 @@
 obj-$(CONFIG_SYSV_FS) += sysv.o
 
 sysv-objs := ialloc.o balloc.o inode.o itree.o file.o dir.o \
-            namei.o super.o symlink.o
+            namei.o super.o
index 88956309cc86ab6d6d614315651ced6778647347..590ad9206e3f4e761d2c2ad95cfaedfa76b47e16 100644 (file)
@@ -166,8 +166,9 @@ void sysv_set_inode(struct inode *inode, dev_t rdev)
                        inode->i_op = &sysv_symlink_inode_operations;
                        inode->i_mapping->a_ops = &sysv_aops;
                } else {
-                       inode->i_op = &sysv_fast_symlink_inode_operations;
-                       nd_terminate_link(SYSV_I(inode)->i_data, inode->i_size,
+                       inode->i_op = &simple_symlink_inode_operations;
+                       inode->i_link = (char *)SYSV_I(inode)->i_data;
+                       nd_terminate_link(inode->i_link, inode->i_size,
                                sizeof(SYSV_I(inode)->i_data) - 1);
                }
        } else
diff --git a/fs/sysv/symlink.c b/fs/sysv/symlink.c
deleted file mode 100644 (file)
index d3fa0d7..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- *  linux/fs/sysv/symlink.c
- *
- *  Handling of System V filesystem fast symlinks extensions.
- *  Aug 2001, Christoph Hellwig (hch@infradead.org)
- */
-
-#include "sysv.h"
-#include <linux/namei.h>
-
-static void *sysv_follow_link(struct dentry *dentry, struct nameidata *nd)
-{
-       nd_set_link(nd, (char *)SYSV_I(d_inode(dentry))->i_data);
-       return NULL;
-}
-
-const struct inode_operations sysv_fast_symlink_inode_operations = {
-       .readlink       = generic_readlink,
-       .follow_link    = sysv_follow_link,
-};
index 69d488986cce4923860c6d1f4c7ab325370fc5ce..2c13525131cd8146dd19d6a07086a70afc97b25a 100644 (file)
@@ -161,7 +161,6 @@ extern ino_t sysv_inode_by_name(struct dentry *);
 
 extern const struct inode_operations sysv_file_inode_operations;
 extern const struct inode_operations sysv_dir_inode_operations;
-extern const struct inode_operations sysv_fast_symlink_inode_operations;
 extern const struct file_operations sysv_file_operations;
 extern const struct file_operations sysv_dir_operations;
 extern const struct address_space_operations sysv_aops;
index 27060fc855d42549b2bbe7d3cbe6329858e3fb2c..5c27c66c224af38618ec4f92b453bd8e65d24608 100644 (file)
@@ -889,6 +889,7 @@ static int ubifs_symlink(struct inode *dir, struct dentry *dentry,
 
        memcpy(ui->data, symname, len);
        ((char *)ui->data)[len] = '\0';
+       inode->i_link = ui->data;
        /*
         * The terminating zero byte is not written to the flash media and it
         * is put just to make later in-memory string processing simpler. Thus,
index 35efc103c39c102215cebb0792b6b4f24d49b90d..a3dfe2ae79f28592a0ba01feb6d0b1889345957c 100644 (file)
@@ -51,7 +51,6 @@
 
 #include "ubifs.h"
 #include <linux/mount.h>
-#include <linux/namei.h>
 #include <linux/slab.h>
 
 static int read_block(struct inode *inode, void *addr, unsigned int block,
@@ -1300,14 +1299,6 @@ static void ubifs_invalidatepage(struct page *page, unsigned int offset,
        ClearPageChecked(page);
 }
 
-static void *ubifs_follow_link(struct dentry *dentry, struct nameidata *nd)
-{
-       struct ubifs_inode *ui = ubifs_inode(d_inode(dentry));
-
-       nd_set_link(nd, ui->data);
-       return NULL;
-}
-
 int ubifs_fsync(struct file *file, loff_t start, loff_t end, int datasync)
 {
        struct inode *inode = file->f_mapping->host;
@@ -1570,7 +1561,7 @@ const struct inode_operations ubifs_file_inode_operations = {
 
 const struct inode_operations ubifs_symlink_inode_operations = {
        .readlink    = generic_readlink,
-       .follow_link = ubifs_follow_link,
+       .follow_link = simple_follow_link,
        .setattr     = ubifs_setattr,
        .getattr     = ubifs_getattr,
        .setxattr    = ubifs_setxattr,
index 75e6f04bb795a9605a5d8c7cf6e4607b89a5f7cf..20f5dbd7c6a8b6bee476dc6139b803c1e14d5a47 100644 (file)
@@ -195,6 +195,7 @@ struct inode *ubifs_iget(struct super_block *sb, unsigned long inum)
                }
                memcpy(ui->data, ino->data, ui->data_len);
                ((char *)ui->data)[ui->data_len] = '\0';
+               inode->i_link = ui->data;
                break;
        case S_IFBLK:
        case S_IFCHR:
index be7d42c7d9382bf8072a7e6e9eff7e03b24bab25..99aaf5c9bf4d83f0f5ec6469d6d6cb20827750d0 100644 (file)
@@ -572,9 +572,10 @@ static void ufs_set_inode_ops(struct inode *inode)
                inode->i_fop = &ufs_dir_operations;
                inode->i_mapping->a_ops = &ufs_aops;
        } else if (S_ISLNK(inode->i_mode)) {
-               if (!inode->i_blocks)
+               if (!inode->i_blocks) {
                        inode->i_op = &ufs_fast_symlink_inode_operations;
-               else {
+                       inode->i_link = (char *)UFS_I(inode)->i_u1.i_symlink;
+               } else {
                        inode->i_op = &ufs_symlink_inode_operations;
                        inode->i_mapping->a_ops = &ufs_aops;
                }
index e491a93a7e9af14c4227ee5072fa4a9d0bc17709..f773deb1d2e3fd561b906a0dd2a050d16161a5be 100644 (file)
@@ -144,7 +144,8 @@ static int ufs_symlink (struct inode * dir, struct dentry * dentry,
        } else {
                /* fast symlink */
                inode->i_op = &ufs_fast_symlink_inode_operations;
-               memcpy(UFS_I(inode)->i_u1.i_symlink, symname, l);
+               inode->i_link = (char *)UFS_I(inode)->i_u1.i_symlink;
+               memcpy(inode->i_link, symname, l);
                inode->i_size = l-1;
        }
        mark_inode_dirty(inode);
index 5b537e2fdda385a0da9aa346e93e05e83084e1dd..874480bb43e9d08190707d7d2ed30aee02eec531 100644 (file)
  *  ext2 symlink handling code
  */
 
-#include <linux/fs.h>
-#include <linux/namei.h>
-
 #include "ufs_fs.h"
 #include "ufs.h"
 
-
-static void *ufs_follow_link(struct dentry *dentry, struct nameidata *nd)
-{
-       struct ufs_inode_info *p = UFS_I(d_inode(dentry));
-       nd_set_link(nd, (char*)p->i_u1.i_symlink);
-       return NULL;
-}
-
 const struct inode_operations ufs_fast_symlink_inode_operations = {
        .readlink       = generic_readlink,
-       .follow_link    = ufs_follow_link,
+       .follow_link    = simple_follow_link,
        .setattr        = ufs_setattr,
 };
 
index f4cd7204e23667724c01a4c4b8efe8c1d48b1cb3..7f51f39f8acc0a2fd407a7be57c1477439213a0f 100644 (file)
@@ -41,7 +41,6 @@
 
 #include <linux/capability.h>
 #include <linux/xattr.h>
-#include <linux/namei.h>
 #include <linux/posix_acl.h>
 #include <linux/security.h>
 #include <linux/fiemap.h>
@@ -414,10 +413,10 @@ xfs_vn_rename(
  * we need to be very careful about how much stack we use.
  * uio is kmalloced for this reason...
  */
-STATIC void *
+STATIC const char *
 xfs_vn_follow_link(
        struct dentry           *dentry,
-       struct nameidata        *nd)
+       void                    **cookie)
 {
        char                    *link;
        int                     error = -ENOMEM;
@@ -430,14 +429,12 @@ xfs_vn_follow_link(
        if (unlikely(error))
                goto out_kfree;
 
-       nd_set_link(nd, link);
-       return NULL;
+       return *cookie = link;
 
  out_kfree:
        kfree(link);
  out_err:
-       nd_set_link(nd, ERR_PTR(error));
-       return NULL;
+       return ERR_PTR(error);
 }
 
 STATIC int
index 8de4fa90e8c4add33967c8dbec6b7cfe2d01b2c4..b43276f339efd0114400ab6396ba7efd651a44c3 100644 (file)
@@ -208,7 +208,10 @@ struct acpi_device_flags {
        u32 visited:1;
        u32 hotplug_notify:1;
        u32 is_dock_station:1;
-       u32 reserved:23;
+       u32 of_compatible_ok:1;
+       u32 coherent_dma:1;
+       u32 cca_seen:1;
+       u32 reserved:20;
 };
 
 /* File System */
@@ -271,7 +274,6 @@ struct acpi_device_power_flags {
 struct acpi_device_power_state {
        struct {
                u8 valid:1;
-               u8 os_accessible:1;
                u8 explicit_set:1;      /* _PSx present? */
                u8 reserved:6;
        } flags;
@@ -380,6 +382,39 @@ struct acpi_device {
        void (*remove)(struct acpi_device *);
 };
 
+static inline bool acpi_check_dma(struct acpi_device *adev, bool *coherent)
+{
+       bool ret = false;
+
+       if (!adev)
+               return ret;
+
+       /**
+        * Currently, we only support _CCA=1 (i.e. coherent_dma=1)
+        * This should be equivalent to specifyig dma-coherent for
+        * a device in OF.
+        *
+        * For the case when _CCA=0 (i.e. coherent_dma=0 && cca_seen=1),
+        * There are two cases:
+        * case 1. Do not support and disable DMA.
+        * case 2. Support but rely on arch-specific cache maintenance for
+        *         non-coherence DMA operations.
+        * Currently, we implement case 1 above.
+        *
+        * For the case when _CCA is missing (i.e. cca_seen=0) and
+        * platform specifies ACPI_CCA_REQUIRED, we do not support DMA,
+        * and fallback to arch-specific default handling.
+        *
+        * See acpi_init_coherency() for more info.
+        */
+       if (adev->flags.coherent_dma) {
+               ret = true;
+               if (coherent)
+                       *coherent = adev->flags.coherent_dma;
+       }
+       return ret;
+}
+
 static inline bool is_acpi_node(struct fwnode_handle *fwnode)
 {
        return fwnode && fwnode->type == FWNODE_ACPI;
@@ -601,7 +636,7 @@ static inline bool acpi_device_can_wakeup(struct acpi_device *adev)
 
 static inline bool acpi_device_can_poweroff(struct acpi_device *adev)
 {
-       return adev->power.states[ACPI_STATE_D3_COLD].flags.os_accessible;
+       return adev->power.states[ACPI_STATE_D3_COLD].flags.valid;
 }
 
 #else  /* CONFIG_ACPI */
index 0bc78df66d4b10764c464986dd9262f69844f94d..d02df0a49d98089d4e6e5c7c3bfb4ad0445ffde1 100644 (file)
@@ -95,7 +95,7 @@ acpi_physical_address acpi_os_get_root_pointer(void);
 #ifndef ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_predefined_override
 acpi_status
 acpi_os_predefined_override(const struct acpi_predefined_names *init_val,
-                           acpi_string * new_val);
+                           char **new_val);
 #endif
 
 #ifndef ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_table_override
index 08ef57bc8d63fae510abf38680137bcda349aa0a..d68f1cd39c495f732b66c048d64cbe384162cd6d 100644 (file)
@@ -46,7 +46,7 @@
 
 /* Current ACPICA subsystem version in YYYYMMDD format */
 
-#define ACPI_CA_VERSION                 0x20150410
+#define ACPI_CA_VERSION                 0x20150515
 
 #include <acpi/acconfig.h>
 #include <acpi/actypes.h>
index d4081fef1095bded3f607c04ed26d514efefa208..cb8a6b97cedae07b239fbc5eb671e1ddfe6d38ea 100644 (file)
@@ -284,6 +284,7 @@ struct acpi_table_fadt {
        struct acpi_generic_address xgpe1_block;        /* 64-bit Extended General Purpose Event 1 Reg Blk address */
        struct acpi_generic_address sleep_control;      /* 64-bit Sleep Control register (ACPI 5.0) */
        struct acpi_generic_address sleep_status;       /* 64-bit Sleep Status register (ACPI 5.0) */
+       u64 hypervisor_id;      /* Hypervisor Vendor ID (ACPI 6.0) */
 };
 
 /* Masks for FADT IA-PC Boot Architecture Flags (boot_flags) [Vx]=Introduced in this FADT revision */
@@ -341,7 +342,7 @@ enum acpi_preferred_pm_profiles {
        PM_TABLET = 8
 };
 
-/* Values for sleep_status and sleep_control registers (V5 FADT) */
+/* Values for sleep_status and sleep_control registers (V5+ FADT) */
 
 #define ACPI_X_WAKE_STATUS          0x80
 #define ACPI_X_SLEEP_TYPE_MASK      0x1C
@@ -398,15 +399,17 @@ struct acpi_table_desc {
  * FADT is the bottom line as to what the version really is.
  *
  * For reference, the values below are as follows:
- *     FADT V1  size: 0x074
- *     FADT V2  size: 0x084
- *     FADT V3  size: 0x0F4
- *     FADT V4  size: 0x0F4
- *     FADT V5  size: 0x10C
+ *     FADT V1 size: 0x074
+ *     FADT V2 size: 0x084
+ *     FADT V3 size: 0x0F4
+ *     FADT V4 size: 0x0F4
+ *     FADT V5 size: 0x10C
+ *     FADT V6 size: 0x114
  */
 #define ACPI_FADT_V1_SIZE       (u32) (ACPI_FADT_OFFSET (flags) + 4)
 #define ACPI_FADT_V2_SIZE       (u32) (ACPI_FADT_OFFSET (minor_revision) + 1)
 #define ACPI_FADT_V3_SIZE       (u32) (ACPI_FADT_OFFSET (sleep_control))
-#define ACPI_FADT_V5_SIZE       (u32) (sizeof (struct acpi_table_fadt))
+#define ACPI_FADT_V5_SIZE       (u32) (ACPI_FADT_OFFSET (hypervisor_id))
+#define ACPI_FADT_V6_SIZE       (u32) (sizeof (struct acpi_table_fadt))
 
 #endif                         /* __ACTBL_H__ */
index b80b0e6dabc568700346a1bf941467a9eed438df..06b61f01ea599177c0cba9b3172c89672d7aa63b 100644 (file)
@@ -71,6 +71,7 @@
 #define ACPI_SIG_SBST           "SBST" /* Smart Battery Specification Table */
 #define ACPI_SIG_SLIT           "SLIT" /* System Locality Distance Information Table */
 #define ACPI_SIG_SRAT           "SRAT" /* System Resource Affinity Table */
+#define ACPI_SIG_NFIT           "NFIT" /* NVDIMM Firmware Interface Table */
 
 /*
  * All tables must be byte-packed to match the ACPI specification, since
@@ -673,7 +674,8 @@ enum acpi_madt_type {
        ACPI_MADT_TYPE_GENERIC_DISTRIBUTOR = 12,
        ACPI_MADT_TYPE_GENERIC_MSI_FRAME = 13,
        ACPI_MADT_TYPE_GENERIC_REDISTRIBUTOR = 14,
-       ACPI_MADT_TYPE_RESERVED = 15    /* 15 and greater are reserved */
+       ACPI_MADT_TYPE_GENERIC_TRANSLATOR = 15,
+       ACPI_MADT_TYPE_RESERVED = 16    /* 16 and greater are reserved */
 };
 
 /*
@@ -794,7 +796,7 @@ struct acpi_madt_local_x2apic_nmi {
        u8 reserved[3];         /* reserved - must be zero */
 };
 
-/* 11: Generic Interrupt (ACPI 5.0) */
+/* 11: Generic Interrupt (ACPI 5.0 + ACPI 6.0 changes) */
 
 struct acpi_madt_generic_interrupt {
        struct acpi_subtable_header header;
@@ -811,6 +813,8 @@ struct acpi_madt_generic_interrupt {
        u32 vgic_interrupt;
        u64 gicr_base_address;
        u64 arm_mpidr;
+       u8 efficiency_class;
+       u8 reserved2[3];
 };
 
 /* Masks for Flags field above */
@@ -819,7 +823,7 @@ struct acpi_madt_generic_interrupt {
 #define ACPI_MADT_PERFORMANCE_IRQ_MODE  (1<<1) /* 01: Performance Interrupt Mode */
 #define ACPI_MADT_VGIC_IRQ_MODE         (1<<2) /* 02: VGIC Maintenance Interrupt mode */
 
-/* 12: Generic Distributor (ACPI 5.0) */
+/* 12: Generic Distributor (ACPI 5.0 + ACPI 6.0 changes) */
 
 struct acpi_madt_generic_distributor {
        struct acpi_subtable_header header;
@@ -827,7 +831,8 @@ struct acpi_madt_generic_distributor {
        u32 gic_id;
        u64 base_address;
        u32 global_irq_base;
-       u32 reserved2;          /* reserved - must be zero */
+       u8 version;
+       u8 reserved2[3];        /* reserved - must be zero */
 };
 
 /* 13: Generic MSI Frame (ACPI 5.1) */
@@ -855,6 +860,16 @@ struct acpi_madt_generic_redistributor {
        u32 length;
 };
 
+/* 15: Generic Translator (ACPI 6.0) */
+
+struct acpi_madt_generic_translator {
+       struct acpi_subtable_header header;
+       u16 reserved;           /* reserved - must be zero */
+       u32 translation_id;
+       u64 base_address;
+       u32 reserved2;
+};
+
 /*
  * Common flags fields for MADT subtables
  */
@@ -906,6 +921,159 @@ struct acpi_msct_proximity {
        u64 memory_capacity;    /* In bytes */
 };
 
+/*******************************************************************************
+ *
+ * NFIT - NVDIMM Interface Table (ACPI 6.0)
+ *        Version 1
+ *
+ ******************************************************************************/
+
+struct acpi_table_nfit {
+       struct acpi_table_header header;        /* Common ACPI table header */
+       u32 reserved;           /* Reserved, must be zero */
+};
+
+/* Subtable header for NFIT */
+
+struct acpi_nfit_header {
+       u16 type;
+       u16 length;
+};
+
+/* Values for subtable type in struct acpi_nfit_header */
+
+enum acpi_nfit_type {
+       ACPI_NFIT_TYPE_SYSTEM_ADDRESS = 0,
+       ACPI_NFIT_TYPE_MEMORY_MAP = 1,
+       ACPI_NFIT_TYPE_INTERLEAVE = 2,
+       ACPI_NFIT_TYPE_SMBIOS = 3,
+       ACPI_NFIT_TYPE_CONTROL_REGION = 4,
+       ACPI_NFIT_TYPE_DATA_REGION = 5,
+       ACPI_NFIT_TYPE_FLUSH_ADDRESS = 6,
+       ACPI_NFIT_TYPE_RESERVED = 7     /* 7 and greater are reserved */
+};
+
+/*
+ * NFIT Subtables
+ */
+
+/* 0: System Physical Address Range Structure */
+
+struct acpi_nfit_system_address {
+       struct acpi_nfit_header header;
+       u16 range_index;
+       u16 flags;
+       u32 reserved;           /* Reseved, must be zero */
+       u32 proximity_domain;
+       u8 range_guid[16];
+       u64 address;
+       u64 length;
+       u64 memory_mapping;
+};
+
+/* Flags */
+
+#define ACPI_NFIT_ADD_ONLINE_ONLY       (1)    /* 00: Add/Online Operation Only */
+#define ACPI_NFIT_PROXIMITY_VALID       (1<<1) /* 01: Proximity Domain Valid */
+
+/* Range Type GUIDs appear in the include/acuuid.h file */
+
+/* 1: Memory Device to System Address Range Map Structure */
+
+struct acpi_nfit_memory_map {
+       struct acpi_nfit_header header;
+       u32 device_handle;
+       u16 physical_id;
+       u16 region_id;
+       u16 range_index;
+       u16 region_index;
+       u64 region_size;
+       u64 region_offset;
+       u64 address;
+       u16 interleave_index;
+       u16 interleave_ways;
+       u16 flags;
+       u16 reserved;           /* Reserved, must be zero */
+};
+
+/* Flags */
+
+#define ACPI_NFIT_MEM_SAVE_FAILED       (1)    /* 00: Last SAVE to Memory Device failed */
+#define ACPI_NFIT_MEM_RESTORE_FAILED    (1<<1) /* 01: Last RESTORE from Memory Device failed */
+#define ACPI_NFIT_MEM_FLUSH_FAILED      (1<<2) /* 02: Platform flush failed */
+#define ACPI_NFIT_MEM_ARMED             (1<<3) /* 03: Memory Device observed to be not armed */
+#define ACPI_NFIT_MEM_HEALTH_OBSERVED   (1<<4) /* 04: Memory Device observed SMART/health events */
+#define ACPI_NFIT_MEM_HEALTH_ENABLED    (1<<5) /* 05: SMART/health events enabled */
+
+/* 2: Interleave Structure */
+
+struct acpi_nfit_interleave {
+       struct acpi_nfit_header header;
+       u16 interleave_index;
+       u16 reserved;           /* Reserved, must be zero */
+       u32 line_count;
+       u32 line_size;
+       u32 line_offset[1];     /* Variable length */
+};
+
+/* 3: SMBIOS Management Information Structure */
+
+struct acpi_nfit_smbios {
+       struct acpi_nfit_header header;
+       u32 reserved;           /* Reserved, must be zero */
+       u8 data[1];             /* Variable length */
+};
+
+/* 4: NVDIMM Control Region Structure */
+
+struct acpi_nfit_control_region {
+       struct acpi_nfit_header header;
+       u16 region_index;
+       u16 vendor_id;
+       u16 device_id;
+       u16 revision_id;
+       u16 subsystem_vendor_id;
+       u16 subsystem_device_id;
+       u16 subsystem_revision_id;
+       u8 reserved[6];         /* Reserved, must be zero */
+       u32 serial_number;
+       u16 code;
+       u16 windows;
+       u64 window_size;
+       u64 command_offset;
+       u64 command_size;
+       u64 status_offset;
+       u64 status_size;
+       u16 flags;
+       u8 reserved1[6];        /* Reserved, must be zero */
+};
+
+/* Flags */
+
+#define ACPI_NFIT_CONTROL_BUFFERED      (1)    /* Block Data Windows implementation is buffered */
+
+/* 5: NVDIMM Block Data Window Region Structure */
+
+struct acpi_nfit_data_region {
+       struct acpi_nfit_header header;
+       u16 region_index;
+       u16 windows;
+       u64 offset;
+       u64 size;
+       u64 capacity;
+       u64 start_address;
+};
+
+/* 6: Flush Hint Address Structure */
+
+struct acpi_nfit_flush_address {
+       struct acpi_nfit_header header;
+       u32 device_handle;
+       u16 hint_count;
+       u8 reserved[6];         /* Reserved, must be zero */
+       u64 hint_address[1];    /* Variable length */
+};
+
 /*******************************************************************************
  *
  * SBST - Smart Battery Specification Table
index cafdeb50fbdfd3bb03b1f297484236790610d6b8..370d69d871a0d19054c6fd81bf33789670493f08 100644 (file)
@@ -69,6 +69,7 @@
 #define ACPI_SIG_DMAR           "DMAR" /* DMA Remapping table */
 #define ACPI_SIG_HPET           "HPET" /* High Precision Event Timer table */
 #define ACPI_SIG_IBFT           "IBFT" /* iSCSI Boot Firmware Table */
+#define ACPI_SIG_IORT           "IORT" /* IO Remapping Table */
 #define ACPI_SIG_IVRS           "IVRS" /* I/O Virtualization Reporting Structure */
 #define ACPI_SIG_LPIT           "LPIT" /* Low Power Idle Table */
 #define ACPI_SIG_MCFG           "MCFG" /* PCI Memory Mapped Configuration table */
@@ -648,6 +649,131 @@ struct acpi_ibft_target {
        u16 reverse_chap_secret_offset;
 };
 
+/*******************************************************************************
+ *
+ * IORT - IO Remapping Table
+ *
+ * Conforms to "IO Remapping Table System Software on ARM Platforms",
+ * Document number: ARM DEN 0049A, 2015
+ *
+ ******************************************************************************/
+
+struct acpi_table_iort {
+       struct acpi_table_header header;
+       u32 node_count;
+       u32 node_offset;
+       u32 reserved;
+};
+
+/*
+ * IORT subtables
+ */
+struct acpi_iort_node {
+       u8 type;
+       u16 length;
+       u8 revision;
+       u32 reserved;
+       u32 mapping_count;
+       u32 mapping_offset;
+       char node_data[1];
+};
+
+/* Values for subtable Type above */
+
+enum acpi_iort_node_type {
+       ACPI_IORT_NODE_ITS_GROUP = 0x00,
+       ACPI_IORT_NODE_NAMED_COMPONENT = 0x01,
+       ACPI_IORT_NODE_PCI_ROOT_COMPLEX = 0x02,
+       ACPI_IORT_NODE_SMMU = 0x03
+};
+
+struct acpi_iort_id_mapping {
+       u32 input_base;         /* Lowest value in input range */
+       u32 id_count;           /* Number of IDs */
+       u32 output_base;        /* Lowest value in output range */
+       u32 output_reference;   /* A reference to the output node */
+       u32 flags;
+};
+
+/* Masks for Flags field above for IORT subtable */
+
+#define ACPI_IORT_ID_SINGLE_MAPPING (1)
+
+struct acpi_iort_memory_access {
+       u32 cache_coherency;
+       u8 hints;
+       u16 reserved;
+       u8 memory_flags;
+};
+
+/* Values for cache_coherency field above */
+
+#define ACPI_IORT_NODE_COHERENT         0x00000001     /* The device node is fully coherent */
+#define ACPI_IORT_NODE_NOT_COHERENT     0x00000000     /* The device node is not coherent */
+
+/* Masks for Hints field above */
+
+#define ACPI_IORT_HT_TRANSIENT          (1)
+#define ACPI_IORT_HT_WRITE              (1<<1)
+#define ACPI_IORT_HT_READ               (1<<2)
+#define ACPI_IORT_HT_OVERRIDE           (1<<3)
+
+/* Masks for memory_flags field above */
+
+#define ACPI_IORT_MF_COHERENCY          (1)
+#define ACPI_IORT_MF_ATTRIBUTES         (1<<1)
+
+/*
+ * IORT node specific subtables
+ */
+struct acpi_iort_its_group {
+       u32 its_count;
+       u32 identifiers[1];     /* GIC ITS identifier arrary */
+};
+
+struct acpi_iort_named_component {
+       u32 node_flags;
+       u64 memory_properties;  /* Memory access properties */
+       u8 memory_address_limit;        /* Memory address size limit */
+       char device_name[1];    /* Path of namespace object */
+};
+
+struct acpi_iort_root_complex {
+       u64 memory_properties;  /* Memory access properties */
+       u32 ats_attribute;
+       u32 pci_segment_number;
+};
+
+/* Values for ats_attribute field above */
+
+#define ACPI_IORT_ATS_SUPPORTED         0x00000001     /* The root complex supports ATS */
+#define ACPI_IORT_ATS_UNSUPPORTED       0x00000000     /* The root complex doesn't support ATS */
+
+struct acpi_iort_smmu {
+       u64 base_address;       /* SMMU base address */
+       u64 span;               /* Length of memory range */
+       u32 model;
+       u32 flags;
+       u32 global_interrupt_offset;
+       u32 context_interrupt_count;
+       u32 context_interrupt_offset;
+       u32 pmu_interrupt_count;
+       u32 pmu_interrupt_offset;
+       u64 interrupts[1];      /* Interrupt array */
+};
+
+/* Values for Model field above */
+
+#define ACPI_IORT_SMMU_V1               0x00000000     /* Generic SMMUv1 */
+#define ACPI_IORT_SMMU_V2               0x00000001     /* Generic SMMUv2 */
+#define ACPI_IORT_SMMU_CORELINK_MMU400  0x00000002     /* ARM Corelink MMU-400 */
+#define ACPI_IORT_SMMU_CORELINK_MMU500  0x00000003     /* ARM Corelink MMU-500 */
+
+/* Masks for Flags field above */
+
+#define ACPI_IORT_SMMU_DVM_SUPPORTED    (1)
+#define ACPI_IORT_SMMU_COHERENT_WALK    (1<<1)
+
 /*******************************************************************************
  *
  * IVRS - I/O Virtualization Reporting Structure
@@ -824,7 +950,7 @@ struct acpi_ivrs_memory {
  *
  * LPIT - Low Power Idle Table
  *
- * Conforms to "ACPI Low Power Idle Table (LPIT) and _LPD Proposal (DRAFT)"
+ * Conforms to "ACPI Low Power Idle Table (LPIT)" July 2014.
  *
  ******************************************************************************/
 
@@ -846,8 +972,7 @@ struct acpi_lpit_header {
 
 enum acpi_lpit_type {
        ACPI_LPIT_TYPE_NATIVE_CSTATE = 0x00,
-       ACPI_LPIT_TYPE_SIMPLE_IO = 0x01,
-       ACPI_LPIT_TYPE_RESERVED = 0x02  /* 2 and above are reserved */
+       ACPI_LPIT_TYPE_RESERVED = 0x01  /* 1 and above are reserved */
 };
 
 /* Masks for Flags field above  */
@@ -870,21 +995,6 @@ struct acpi_lpit_native {
        u64 counter_frequency;
 };
 
-/* 0x01: Simple I/O based LPI structure */
-
-struct acpi_lpit_io {
-       struct acpi_lpit_header header;
-       struct acpi_generic_address entry_trigger;
-       u32 trigger_action;
-       u64 trigger_value;
-       u64 trigger_mask;
-       struct acpi_generic_address minimum_idle_state;
-       u32 residency;
-       u32 latency;
-       struct acpi_generic_address residency_counter;
-       u64 counter_frequency;
-};
-
 /*******************************************************************************
  *
  * MCFG - PCI Memory Mapped Configuration table and subtable
index 440ca8104b437c0b8c8a2eee31eea1afa6a7b2b3..4018986d2a2e2b96f5bf2f493078561666b59e41 100644 (file)
 #define ACPI_SIG_PCCT           "PCCT" /* Platform Communications Channel Table */
 #define ACPI_SIG_PMTT           "PMTT" /* Platform Memory Topology Table */
 #define ACPI_SIG_RASF           "RASF" /* RAS Feature table */
+#define ACPI_SIG_STAO           "STAO" /* Status Override table */
 #define ACPI_SIG_TPM2           "TPM2" /* Trusted Platform Module 2.0 H/W interface table */
+#define ACPI_SIG_WPBT           "WPBT" /* Windows Platform Binary Table */
+#define ACPI_SIG_XENV           "XENV" /* Xen Environment table */
 
 #define ACPI_SIG_S3PT           "S3PT" /* S3 Performance (sub)Table */
 #define ACPI_SIG_PCCS           "PCC"  /* PCC Shared Memory Region */
@@ -77,7 +80,6 @@
 
 #define ACPI_SIG_MATR           "MATR" /* Memory Address Translation Table */
 #define ACPI_SIG_MSDM           "MSDM" /* Microsoft Data Management Table */
-#define ACPI_SIG_WPBT           "WPBT" /* Windows Platform Binary Table */
 
 /*
  * All tables must be byte-packed to match the ACPI specification, since
@@ -117,6 +119,8 @@ struct acpi_table_bgrt {
 /*******************************************************************************
  *
  * DRTM - Dynamic Root of Trust for Measurement table
+ * Conforms to "TCG D-RTM Architecture" June 17 2013, Version 1.0.0
+ * Table version 1
  *
  ******************************************************************************/
 
@@ -133,22 +137,40 @@ struct acpi_table_drtm {
        u32 flags;
 };
 
-/* 1) Validated Tables List */
+/* Flag Definitions for above */
+
+#define ACPI_DRTM_ACCESS_ALLOWED            (1)
+#define ACPI_DRTM_ENABLE_GAP_CODE           (1<<1)
+#define ACPI_DRTM_INCOMPLETE_MEASUREMENTS   (1<<2)
+#define ACPI_DRTM_AUTHORITY_ORDER           (1<<3)
 
-struct acpi_drtm_vtl_list {
-       u32 validated_table_list_count;
+/* 1) Validated Tables List (64-bit addresses) */
+
+struct acpi_drtm_vtable_list {
+       u32 validated_table_count;
+       u64 validated_tables[1];
 };
 
-/* 2) Resources List */
+/* 2) Resources List (of Resource Descriptors) */
+
+/* Resource Descriptor */
+
+struct acpi_drtm_resource {
+       u8 size[7];
+       u8 type;
+       u64 address;
+};
 
 struct acpi_drtm_resource_list {
-       u32 resource_list_count;
+       u32 resource_count;
+       struct acpi_drtm_resource resources[1];
 };
 
 /* 3) Platform-specific Identifiers List */
 
-struct acpi_drtm_id_list {
-       u32 id_list_count;
+struct acpi_drtm_dps_id {
+       u32 dps_id_length;
+       u8 dps_id[16];
 };
 
 /*******************************************************************************
@@ -683,6 +705,21 @@ enum acpi_rasf_status {
 #define ACPI_RASF_ERROR                 (1<<2)
 #define ACPI_RASF_STATUS                (0x1F<<3)
 
+/*******************************************************************************
+ *
+ * STAO - Status Override Table (_STA override) - ACPI 6.0
+ *        Version 1
+ *
+ * Conforms to "ACPI Specification for Status Override Table"
+ * 6 January 2015
+ *
+ ******************************************************************************/
+
+struct acpi_table_stao {
+       struct acpi_table_header header;        /* Common ACPI table header */
+       u8 ignore_uart;
+};
+
 /*******************************************************************************
  *
  * TPM2 - Trusted Platform Module (TPM) 2.0 Hardware Interface Table
@@ -713,6 +750,41 @@ struct acpi_tpm2_control {
        u64 response_address;
 };
 
+/*******************************************************************************
+ *
+ * WPBT - Windows Platform Environment Table (ACPI 6.0)
+ *        Version 1
+ *
+ * Conforms to "Windows Platform Binary Table (WPBT)" 29 November 2011
+ *
+ ******************************************************************************/
+
+struct acpi_table_wpbt {
+       struct acpi_table_header header;        /* Common ACPI table header */
+       u32 handoff_size;
+       u64 handoff_address;
+       u8 layout;
+       u8 type;
+       u16 arguments_length;
+};
+
+/*******************************************************************************
+ *
+ * XENV - Xen Environment Table (ACPI 6.0)
+ *        Version 1
+ *
+ * Conforms to "ACPI Specification for Xen Environment Table" 4 January 2015
+ *
+ ******************************************************************************/
+
+struct acpi_table_xenv {
+       struct acpi_table_header header;        /* Common ACPI table header */
+       u64 grant_table_address;
+       u64 grant_table_size;
+       u32 event_interrupt;
+       u8 event_flags;
+};
+
 /* Reset to default packing */
 
 #pragma pack()
index 1c3002e1db20c8f91a31f17e8d6d323ed76ea404..63fd7f5e9fb3495198e659c8b04a16db2e489877 100644 (file)
@@ -471,11 +471,6 @@ typedef u8 acpi_owner_id;
 
 #define ACPI_INTEGER_BIT_SIZE           64
 #define ACPI_MAX_DECIMAL_DIGITS         20     /* 2^64 = 18,446,744,073,709,551,616 */
-
-#if ACPI_MACHINE_WIDTH == 64
-#define ACPI_USE_NATIVE_DIVIDE /* Use compiler native 64-bit divide */
-#endif
-
 #define ACPI_MAX64_DECIMAL_DIGITS       20
 #define ACPI_MAX32_DECIMAL_DIGITS       10
 #define ACPI_MAX16_DECIMAL_DIGITS        5
@@ -530,6 +525,7 @@ typedef u64 acpi_integer;
 #define ACPI_CAST_PTR(t, p)             ((t *) (acpi_uintptr_t) (p))
 #define ACPI_CAST_INDIRECT_PTR(t, p)    ((t **) (acpi_uintptr_t) (p))
 #define ACPI_ADD_PTR(t, a, b)           ACPI_CAST_PTR (t, (ACPI_CAST_PTR (u8, (a)) + (acpi_size)(b)))
+#define ACPI_SUB_PTR(t, a, b)           ACPI_CAST_PTR (t, (ACPI_CAST_PTR (u8, (a)) - (acpi_size)(b)))
 #define ACPI_PTR_DIFF(a, b)             (acpi_size) (ACPI_CAST_PTR (u8, (a)) - ACPI_CAST_PTR (u8, (b)))
 
 /* Pointer/Integer type conversions */
diff --git a/include/acpi/acuuid.h b/include/acpi/acuuid.h
new file mode 100644 (file)
index 0000000..80fe8cf
--- /dev/null
@@ -0,0 +1,89 @@
+/******************************************************************************
+ *
+ * Name: acuuid.h - ACPI-related UUID/GUID definitions
+ *
+ *****************************************************************************/
+
+/*
+ * Copyright (C) 2000 - 2015, Intel Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * 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 MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
+ */
+
+#ifndef __ACUUID_H__
+#define __ACUUID_H__
+
+/*
+ * Note1: UUIDs and GUIDs are defined to be identical in ACPI.
+ *
+ * Note2: This file is standalone and should remain that way.
+ */
+
+/* Controllers */
+
+#define UUID_GPIO_CONTROLLER            "4f248f40-d5e2-499f-834c-27758ea1cd3f"
+#define UUID_USB_CONTROLLER             "ce2ee385-00e6-48cb-9f05-2edb927c4899"
+#define UUID_SATA_CONTROLLER            "e4db149b-fcfe-425b-a6d8-92357d78fc7f"
+
+/* Devices */
+
+#define UUID_PCI_HOST_BRIDGE            "33db4d5b-1ff7-401c-9657-7441c03dd766"
+#define UUID_I2C_DEVICE                 "3cdff6f7-4267-4555-ad05-b30a3d8938de"
+#define UUID_POWER_BUTTON               "dfbcf3c5-e7a5-44e6-9c1f-29c76f6e059c"
+
+/* Interfaces */
+
+#define UUID_DEVICE_LABELING            "e5c937d0-3553-4d7a-9117-ea4d19c3434d"
+#define UUID_PHYSICAL_PRESENCE          "3dddfaa6-361b-4eb4-a424-8d10089d1653"
+
+/* NVDIMM - NFIT table */
+
+#define UUID_VOLATILE_MEMORY            "7305944f-fdda-44e3-b16c-3f22d252e5d0"
+#define UUID_PERSISTENT_MEMORY          "66f0d379-b4f3-4074-ac43-0d3318b78cdb"
+#define UUID_CONTROL_REGION             "92f701f6-13b4-405d-910b-299367e8234c"
+#define UUID_DATA_REGION                "91af0530-5d86-470e-a6b0-0a2db9408249"
+#define UUID_VOLATILE_VIRTUAL_DISK      "77ab535a-45fc-624b-5560-f7b281d1f96e"
+#define UUID_VOLATILE_VIRTUAL_CD        "3d5abd30-4175-87ce-6d64-d2ade523c4bb"
+#define UUID_PERSISTENT_VIRTUAL_DISK    "5cea02c9-4d07-69d3-269f-4496fbe096f9"
+#define UUID_PERSISTENT_VIRTUAL_CD      "08018188-42cd-bb48-100f-5387d53ded3d"
+
+/* Miscellaneous */
+
+#define UUID_PLATFORM_CAPABILITIES      "0811b06e-4a27-44f9-8d60-3cbbc22e7b48"
+#define UUID_DYNAMIC_ENUMERATION        "d8c1a3a6-be9b-4c9b-91bf-c3cb81fc5daf"
+#define UUID_BATTERY_THERMAL_LIMIT      "4c2067e3-887d-475c-9720-4af1d3ed602e"
+#define UUID_THERMAL_EXTENSIONS         "14d399cd-7a27-4b18-8fb4-7cb7b9f4e500"
+#define UUID_DEVICE_PROPERTIES          "daffd814-6eba-4d8c-8a91-bc9bbf4aa301"
+
+#endif                         /* __AUUID_H__ */
index ecdf9405dd3a8276abf9def3e79a8ebc0a22c6b5..073997d729e9c9710c10656ff3f34c6e30527c34 100644 (file)
 #elif defined(_APPLE) || defined(__APPLE__)
 #include "acmacosx.h"
 
+#elif defined(__DragonFly__)
+#include "acdragonfly.h"
+
 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
 #include "acfreebsd.h"
 
index 71e5ec5b07a3436b872326a4bafece104e2b7343..14dc6f68ca1854de0095e1d572d5e6de36abf7b5 100644 (file)
@@ -56,6 +56,9 @@
 #if defined(_LINUX) || defined(__linux__)
 #include <acpi/platform/aclinuxex.h>
 
+#elif defined(__DragonFly__)
+#include "acdragonflyex.h"
+
 #endif
 
 /*! [End] no source code translation !*/
index 843ef1adfbfab7ca736055f2333e2af8413ba7ef..a7d7f1043e9c124f84dbd1130259c343a754c9db 100644 (file)
@@ -16,23 +16,36 @@ struct acpi_device;
 #define ACPI_VIDEO_DISPLAY_LEGACY_PANEL   0x0110
 #define ACPI_VIDEO_DISPLAY_LEGACY_TV      0x0200
 
+enum acpi_backlight_type {
+       acpi_backlight_undef = -1,
+       acpi_backlight_none = 0,
+       acpi_backlight_video,
+       acpi_backlight_vendor,
+       acpi_backlight_native,
+};
+
 #if (defined CONFIG_ACPI_VIDEO || defined CONFIG_ACPI_VIDEO_MODULE)
 extern int acpi_video_register(void);
 extern void acpi_video_unregister(void);
-extern void acpi_video_unregister_backlight(void);
 extern int acpi_video_get_edid(struct acpi_device *device, int type,
                               int device_id, void **edid);
-extern bool acpi_video_verify_backlight_support(void);
+extern enum acpi_backlight_type acpi_video_get_backlight_type(void);
+extern void acpi_video_set_dmi_backlight_type(enum acpi_backlight_type type);
 #else
 static inline int acpi_video_register(void) { return 0; }
 static inline void acpi_video_unregister(void) { return; }
-static inline void acpi_video_unregister_backlight(void) { return; }
 static inline int acpi_video_get_edid(struct acpi_device *device, int type,
                                      int device_id, void **edid)
 {
        return -ENODEV;
 }
-static inline bool acpi_video_verify_backlight_support(void) { return false; }
+static inline enum acpi_backlight_type acpi_video_get_backlight_type(void)
+{
+       return acpi_backlight_vendor;
+}
+static void acpi_video_set_dmi_backlight_type(enum acpi_backlight_type type)
+{
+}
 #endif
 
 #endif
index f5c40b0fadc2a50be563304727db2e7ad7fe6699..e6a83d712ef6772ac7265e914837da7806e21441 100644 (file)
@@ -66,8 +66,8 @@
 #define smp_read_barrier_depends()     do { } while (0)
 #endif
 
-#ifndef set_mb
-#define set_mb(var, value)  do { (var) = (value); mb(); } while (0)
+#ifndef smp_store_mb
+#define smp_store_mb(var, value)  do { WRITE_ONCE(var, value); mb(); } while (0)
 #endif
 
 #ifndef smp_mb__before_atomic
index 811fb1e9b06131303d41f07797becb024e61d77f..3766ab34aa45afae86287a7c213c3311c88b4be2 100644 (file)
@@ -86,9 +86,6 @@ unsigned long __xchg(unsigned long x, volatile void *ptr, int size)
 
 /*
  * Atomic compare and exchange.
- *
- * Do not define __HAVE_ARCH_CMPXCHG because we want to use it to check whether
- * a cmpxchg primitive faster than repeated local irq save/restore exists.
  */
 #include <asm-generic/cmpxchg-local.h>
 
index 9bb0d11729c9cef15cf9ca1e5b6f6c18528da27d..40ec1433f05de25d15cedaeaf2388b2cc8d0298c 100644 (file)
@@ -128,11 +128,6 @@ static inline int gpio_export_link(struct device *dev, const char *name,
        return gpiod_export_link(dev, name, gpio_to_desc(gpio));
 }
 
-static inline int gpio_sysfs_set_active_low(unsigned gpio, int value)
-{
-       return gpiod_sysfs_set_active_low(gpio_to_desc(gpio), value);
-}
-
 static inline void gpio_unexport(unsigned gpio)
 {
        gpiod_unexport(gpio_to_desc(gpio));
index 9db042304df37d0c97eea09be466fa403d505603..f56094cfdeff0e0f312045212bcf19e8a99882db 100644 (file)
@@ -769,6 +769,14 @@ static inline void __iomem *ioremap_nocache(phys_addr_t offset, size_t size)
 }
 #endif
 
+#ifndef ioremap_uc
+#define ioremap_uc ioremap_uc
+static inline void __iomem *ioremap_uc(phys_addr_t offset, size_t size)
+{
+       return ioremap_nocache(offset, size);
+}
+#endif
+
 #ifndef ioremap_wc
 #define ioremap_wc ioremap_wc
 static inline void __iomem *ioremap_wc(phys_addr_t offset, size_t size)
@@ -777,8 +785,17 @@ static inline void __iomem *ioremap_wc(phys_addr_t offset, size_t size)
 }
 #endif
 
+#ifndef ioremap_wt
+#define ioremap_wt ioremap_wt
+static inline void __iomem *ioremap_wt(phys_addr_t offset, size_t size)
+{
+       return ioremap_nocache(offset, size);
+}
+#endif
+
 #ifndef iounmap
 #define iounmap iounmap
+
 static inline void iounmap(void __iomem *addr)
 {
 }
index 1b41011643a5ebb694742f7a11ba6c54bba35a16..d8f8622fa044dbaeba223bc373f57dad5dac6070 100644 (file)
@@ -66,6 +66,10 @@ extern void ioport_unmap(void __iomem *);
 #define ioremap_wc ioremap_nocache
 #endif
 
+#ifndef ARCH_HAS_IOREMAP_WT
+#define ioremap_wt ioremap_nocache
+#endif
+
 #ifdef CONFIG_PCI
 /* Destroy a virtual mapping cookie for a PCI BAR (memory or IO) */
 struct pci_dev;
index e80a0495e5b0e063fb6f8ac655981b9385d9f7bf..f24bc519bf31c63d72803d08a5969d176413aa4f 100644 (file)
@@ -6,19 +6,6 @@
 #ifndef _ASM_GENERIC_PCI_H
 #define _ASM_GENERIC_PCI_H
 
-static inline struct resource *
-pcibios_select_root(struct pci_dev *pdev, struct resource *res)
-{
-       struct resource *root = NULL;
-
-       if (res->flags & IORESOURCE_IO)
-               root = &ioport_resource;
-       if (res->flags & IORESOURCE_MEM)
-               root = &iomem_resource;
-
-       return root;
-}
-
 #ifndef HAVE_ARCH_PCI_GET_LEGACY_IDE_IRQ
 static inline int pci_get_legacy_ide_irq(struct pci_dev *dev, int channel)
 {
index 39f1d6a2b04d4292f2dbd01b59bc748484824eff..bd910ceaccfa2d5b66da77b1b5becc760c50a615 100644 (file)
@@ -262,6 +262,10 @@ static inline int pmd_same(pmd_t pmd_a, pmd_t pmd_b)
 #define pgprot_writecombine pgprot_noncached
 #endif
 
+#ifndef pgprot_writethrough
+#define pgprot_writethrough pgprot_noncached
+#endif
+
 #ifndef pgprot_device
 #define pgprot_device pgprot_noncached
 #endif
diff --git a/include/asm-generic/qspinlock.h b/include/asm-generic/qspinlock.h
new file mode 100644 (file)
index 0000000..83bfb87
--- /dev/null
@@ -0,0 +1,139 @@
+/*
+ * Queued spinlock
+ *
+ * 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.
+ *
+ * (C) Copyright 2013-2015 Hewlett-Packard Development Company, L.P.
+ *
+ * Authors: Waiman Long <waiman.long@hp.com>
+ */
+#ifndef __ASM_GENERIC_QSPINLOCK_H
+#define __ASM_GENERIC_QSPINLOCK_H
+
+#include <asm-generic/qspinlock_types.h>
+
+/**
+ * queued_spin_is_locked - is the spinlock locked?
+ * @lock: Pointer to queued spinlock structure
+ * Return: 1 if it is locked, 0 otherwise
+ */
+static __always_inline int queued_spin_is_locked(struct qspinlock *lock)
+{
+       return atomic_read(&lock->val);
+}
+
+/**
+ * queued_spin_value_unlocked - is the spinlock structure unlocked?
+ * @lock: queued spinlock structure
+ * Return: 1 if it is unlocked, 0 otherwise
+ *
+ * N.B. Whenever there are tasks waiting for the lock, it is considered
+ *      locked wrt the lockref code to avoid lock stealing by the lockref
+ *      code and change things underneath the lock. This also allows some
+ *      optimizations to be applied without conflict with lockref.
+ */
+static __always_inline int queued_spin_value_unlocked(struct qspinlock lock)
+{
+       return !atomic_read(&lock.val);
+}
+
+/**
+ * queued_spin_is_contended - check if the lock is contended
+ * @lock : Pointer to queued spinlock structure
+ * Return: 1 if lock contended, 0 otherwise
+ */
+static __always_inline int queued_spin_is_contended(struct qspinlock *lock)
+{
+       return atomic_read(&lock->val) & ~_Q_LOCKED_MASK;
+}
+/**
+ * queued_spin_trylock - try to acquire the queued spinlock
+ * @lock : Pointer to queued spinlock structure
+ * Return: 1 if lock acquired, 0 if failed
+ */
+static __always_inline int queued_spin_trylock(struct qspinlock *lock)
+{
+       if (!atomic_read(&lock->val) &&
+          (atomic_cmpxchg(&lock->val, 0, _Q_LOCKED_VAL) == 0))
+               return 1;
+       return 0;
+}
+
+extern void queued_spin_lock_slowpath(struct qspinlock *lock, u32 val);
+
+/**
+ * queued_spin_lock - acquire a queued spinlock
+ * @lock: Pointer to queued spinlock structure
+ */
+static __always_inline void queued_spin_lock(struct qspinlock *lock)
+{
+       u32 val;
+
+       val = atomic_cmpxchg(&lock->val, 0, _Q_LOCKED_VAL);
+       if (likely(val == 0))
+               return;
+       queued_spin_lock_slowpath(lock, val);
+}
+
+#ifndef queued_spin_unlock
+/**
+ * queued_spin_unlock - release a queued spinlock
+ * @lock : Pointer to queued spinlock structure
+ */
+static __always_inline void queued_spin_unlock(struct qspinlock *lock)
+{
+       /*
+        * smp_mb__before_atomic() in order to guarantee release semantics
+        */
+       smp_mb__before_atomic_dec();
+       atomic_sub(_Q_LOCKED_VAL, &lock->val);
+}
+#endif
+
+/**
+ * queued_spin_unlock_wait - wait until current lock holder releases the lock
+ * @lock : Pointer to queued spinlock structure
+ *
+ * There is a very slight possibility of live-lock if the lockers keep coming
+ * and the waiter is just unfortunate enough to not see any unlock state.
+ */
+static inline void queued_spin_unlock_wait(struct qspinlock *lock)
+{
+       while (atomic_read(&lock->val) & _Q_LOCKED_MASK)
+               cpu_relax();
+}
+
+#ifndef virt_queued_spin_lock
+static __always_inline bool virt_queued_spin_lock(struct qspinlock *lock)
+{
+       return false;
+}
+#endif
+
+/*
+ * Initializier
+ */
+#define        __ARCH_SPIN_LOCK_UNLOCKED       { ATOMIC_INIT(0) }
+
+/*
+ * Remapping spinlock architecture specific functions to the corresponding
+ * queued spinlock functions.
+ */
+#define arch_spin_is_locked(l)         queued_spin_is_locked(l)
+#define arch_spin_is_contended(l)      queued_spin_is_contended(l)
+#define arch_spin_value_unlocked(l)    queued_spin_value_unlocked(l)
+#define arch_spin_lock(l)              queued_spin_lock(l)
+#define arch_spin_trylock(l)           queued_spin_trylock(l)
+#define arch_spin_unlock(l)            queued_spin_unlock(l)
+#define arch_spin_lock_flags(l, f)     queued_spin_lock(l)
+#define arch_spin_unlock_wait(l)       queued_spin_unlock_wait(l)
+
+#endif /* __ASM_GENERIC_QSPINLOCK_H */
diff --git a/include/asm-generic/qspinlock_types.h b/include/asm-generic/qspinlock_types.h
new file mode 100644 (file)
index 0000000..85f888e
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * Queued spinlock
+ *
+ * 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.
+ *
+ * (C) Copyright 2013-2015 Hewlett-Packard Development Company, L.P.
+ *
+ * Authors: Waiman Long <waiman.long@hp.com>
+ */
+#ifndef __ASM_GENERIC_QSPINLOCK_TYPES_H
+#define __ASM_GENERIC_QSPINLOCK_TYPES_H
+
+/*
+ * Including atomic.h with PARAVIRT on will cause compilation errors because
+ * of recursive header file incluson via paravirt_types.h. So don't include
+ * it if PARAVIRT is on.
+ */
+#ifndef CONFIG_PARAVIRT
+#include <linux/types.h>
+#include <linux/atomic.h>
+#endif
+
+typedef struct qspinlock {
+       atomic_t        val;
+} arch_spinlock_t;
+
+/*
+ * Bitfields in the atomic value:
+ *
+ * When NR_CPUS < 16K
+ *  0- 7: locked byte
+ *     8: pending
+ *  9-15: not used
+ * 16-17: tail index
+ * 18-31: tail cpu (+1)
+ *
+ * When NR_CPUS >= 16K
+ *  0- 7: locked byte
+ *     8: pending
+ *  9-10: tail index
+ * 11-31: tail cpu (+1)
+ */
+#define        _Q_SET_MASK(type)       (((1U << _Q_ ## type ## _BITS) - 1)\
+                                     << _Q_ ## type ## _OFFSET)
+#define _Q_LOCKED_OFFSET       0
+#define _Q_LOCKED_BITS         8
+#define _Q_LOCKED_MASK         _Q_SET_MASK(LOCKED)
+
+#define _Q_PENDING_OFFSET      (_Q_LOCKED_OFFSET + _Q_LOCKED_BITS)
+#if CONFIG_NR_CPUS < (1U << 14)
+#define _Q_PENDING_BITS                8
+#else
+#define _Q_PENDING_BITS                1
+#endif
+#define _Q_PENDING_MASK                _Q_SET_MASK(PENDING)
+
+#define _Q_TAIL_IDX_OFFSET     (_Q_PENDING_OFFSET + _Q_PENDING_BITS)
+#define _Q_TAIL_IDX_BITS       2
+#define _Q_TAIL_IDX_MASK       _Q_SET_MASK(TAIL_IDX)
+
+#define _Q_TAIL_CPU_OFFSET     (_Q_TAIL_IDX_OFFSET + _Q_TAIL_IDX_BITS)
+#define _Q_TAIL_CPU_BITS       (32 - _Q_TAIL_CPU_OFFSET)
+#define _Q_TAIL_CPU_MASK       _Q_SET_MASK(TAIL_CPU)
+
+#define _Q_TAIL_OFFSET         _Q_TAIL_IDX_OFFSET
+#define _Q_TAIL_MASK           (_Q_TAIL_IDX_MASK | _Q_TAIL_CPU_MASK)
+
+#define _Q_LOCKED_VAL          (1U << _Q_LOCKED_OFFSET)
+#define _Q_PENDING_VAL         (1U << _Q_PENDING_OFFSET)
+
+#endif /* __ASM_GENERIC_QSPINLOCK_TYPES_H */
index 94b19be67574495c9270bcd29c55292554c8cbe9..7169ad04acc06602aa10ffaeeedcf26c061a2da2 100644 (file)
 #include <linux/kernel.h>
 #include <linux/slab.h>
 
+/**
+ * DOC: Authenticated Encryption With Associated Data (AEAD) Cipher API
+ *
+ * The AEAD cipher API is used with the ciphers of type CRYPTO_ALG_TYPE_AEAD
+ * (listed as type "aead" in /proc/crypto)
+ *
+ * The most prominent examples for this type of encryption is GCM and CCM.
+ * However, the kernel supports other types of AEAD ciphers which are defined
+ * with the following cipher string:
+ *
+ *     authenc(keyed message digest, block cipher)
+ *
+ * For example: authenc(hmac(sha256), cbc(aes))
+ *
+ * The example code provided for the asynchronous block cipher operation
+ * applies here as well. Naturally all *ablkcipher* symbols must be exchanged
+ * the *aead* pendants discussed in the following. In addition, for the AEAD
+ * operation, the aead_request_set_assoc function must be used to set the
+ * pointer to the associated data memory location before performing the
+ * encryption or decryption operation. In case of an encryption, the associated
+ * data memory is filled during the encryption operation. For decryption, the
+ * associated data memory must contain data that is used to verify the integrity
+ * of the decrypted data. Another deviation from the asynchronous block cipher
+ * operation is that the caller should explicitly check for -EBADMSG of the
+ * crypto_aead_decrypt. That error indicates an authentication error, i.e.
+ * a breach in the integrity of the message. In essence, that -EBADMSG error
+ * code is the key bonus an AEAD cipher has over "standard" block chaining
+ * modes.
+ */
+
+/**
+ *     struct aead_request - AEAD request
+ *     @base: Common attributes for async crypto requests
+ *     @old: Boolean whether the old or new AEAD API is used
+ *     @assoclen: Length in bytes of associated data for authentication
+ *     @cryptlen: Length of data to be encrypted or decrypted
+ *     @iv: Initialisation vector
+ *     @assoc: Associated data
+ *     @src: Source data
+ *     @dst: Destination data
+ *     @__ctx: Start of private context data
+ */
+struct aead_request {
+       struct crypto_async_request base;
+
+       bool old;
+
+       unsigned int assoclen;
+       unsigned int cryptlen;
+
+       u8 *iv;
+
+       struct scatterlist *assoc;
+       struct scatterlist *src;
+       struct scatterlist *dst;
+
+       void *__ctx[] CRYPTO_MINALIGN_ATTR;
+};
+
 /**
  *     struct aead_givcrypt_request - AEAD request with IV generation
  *     @seq: Sequence number for IV generation
@@ -30,6 +89,474 @@ struct aead_givcrypt_request {
        struct aead_request areq;
 };
 
+/**
+ * struct aead_alg - AEAD cipher definition
+ * @maxauthsize: Set the maximum authentication tag size supported by the
+ *              transformation. A transformation may support smaller tag sizes.
+ *              As the authentication tag is a message digest to ensure the
+ *              integrity of the encrypted data, a consumer typically wants the
+ *              largest authentication tag possible as defined by this
+ *              variable.
+ * @setauthsize: Set authentication size for the AEAD transformation. This
+ *              function is used to specify the consumer requested size of the
+ *              authentication tag to be either generated by the transformation
+ *              during encryption or the size of the authentication tag to be
+ *              supplied during the decryption operation. This function is also
+ *              responsible for checking the authentication tag size for
+ *              validity.
+ * @setkey: see struct ablkcipher_alg
+ * @encrypt: see struct ablkcipher_alg
+ * @decrypt: see struct ablkcipher_alg
+ * @geniv: see struct ablkcipher_alg
+ * @ivsize: see struct ablkcipher_alg
+ * @init: Initialize the cryptographic transformation object. This function
+ *       is used to initialize the cryptographic transformation object.
+ *       This function is called only once at the instantiation time, right
+ *       after the transformation context was allocated. In case the
+ *       cryptographic hardware has some special requirements which need to
+ *       be handled by software, this function shall check for the precise
+ *       requirement of the transformation and put any software fallbacks
+ *       in place.
+ * @exit: Deinitialize the cryptographic transformation object. This is a
+ *       counterpart to @init, used to remove various changes set in
+ *       @init.
+ *
+ * All fields except @ivsize is mandatory and must be filled.
+ */
+struct aead_alg {
+       int (*setkey)(struct crypto_aead *tfm, const u8 *key,
+                     unsigned int keylen);
+       int (*setauthsize)(struct crypto_aead *tfm, unsigned int authsize);
+       int (*encrypt)(struct aead_request *req);
+       int (*decrypt)(struct aead_request *req);
+       int (*init)(struct crypto_aead *tfm);
+       void (*exit)(struct crypto_aead *tfm);
+
+       const char *geniv;
+
+       unsigned int ivsize;
+       unsigned int maxauthsize;
+
+       struct crypto_alg base;
+};
+
+struct crypto_aead {
+       int (*setkey)(struct crypto_aead *tfm, const u8 *key,
+                     unsigned int keylen);
+       int (*setauthsize)(struct crypto_aead *tfm, unsigned int authsize);
+       int (*encrypt)(struct aead_request *req);
+       int (*decrypt)(struct aead_request *req);
+       int (*givencrypt)(struct aead_givcrypt_request *req);
+       int (*givdecrypt)(struct aead_givcrypt_request *req);
+
+       struct crypto_aead *child;
+
+       unsigned int authsize;
+       unsigned int reqsize;
+
+       struct crypto_tfm base;
+};
+
+static inline struct crypto_aead *__crypto_aead_cast(struct crypto_tfm *tfm)
+{
+       return container_of(tfm, struct crypto_aead, base);
+}
+
+/**
+ * crypto_alloc_aead() - allocate AEAD cipher handle
+ * @alg_name: is the cra_name / name or cra_driver_name / driver name of the
+ *          AEAD cipher
+ * @type: specifies the type of the cipher
+ * @mask: specifies the mask for the cipher
+ *
+ * Allocate a cipher handle for an AEAD. The returned struct
+ * crypto_aead is the cipher handle that is required for any subsequent
+ * API invocation for that AEAD.
+ *
+ * Return: allocated cipher handle in case of success; IS_ERR() is true in case
+ *        of an error, PTR_ERR() returns the error code.
+ */
+struct crypto_aead *crypto_alloc_aead(const char *alg_name, u32 type, u32 mask);
+
+static inline struct crypto_tfm *crypto_aead_tfm(struct crypto_aead *tfm)
+{
+       return &tfm->base;
+}
+
+/**
+ * crypto_free_aead() - zeroize and free aead handle
+ * @tfm: cipher handle to be freed
+ */
+static inline void crypto_free_aead(struct crypto_aead *tfm)
+{
+       crypto_destroy_tfm(tfm, crypto_aead_tfm(tfm));
+}
+
+static inline struct crypto_aead *crypto_aead_crt(struct crypto_aead *tfm)
+{
+       return tfm;
+}
+
+static inline struct old_aead_alg *crypto_old_aead_alg(struct crypto_aead *tfm)
+{
+       return &crypto_aead_tfm(tfm)->__crt_alg->cra_aead;
+}
+
+static inline struct aead_alg *crypto_aead_alg(struct crypto_aead *tfm)
+{
+       return container_of(crypto_aead_tfm(tfm)->__crt_alg,
+                           struct aead_alg, base);
+}
+
+static inline unsigned int crypto_aead_alg_ivsize(struct aead_alg *alg)
+{
+       return alg->base.cra_aead.encrypt ? alg->base.cra_aead.ivsize :
+                                           alg->ivsize;
+}
+
+/**
+ * crypto_aead_ivsize() - obtain IV size
+ * @tfm: cipher handle
+ *
+ * The size of the IV for the aead referenced by the cipher handle is
+ * returned. This IV size may be zero if the cipher does not need an IV.
+ *
+ * Return: IV size in bytes
+ */
+static inline unsigned int crypto_aead_ivsize(struct crypto_aead *tfm)
+{
+       return crypto_aead_alg_ivsize(crypto_aead_alg(tfm));
+}
+
+/**
+ * crypto_aead_authsize() - obtain maximum authentication data size
+ * @tfm: cipher handle
+ *
+ * The maximum size of the authentication data for the AEAD cipher referenced
+ * by the AEAD cipher handle is returned. The authentication data size may be
+ * zero if the cipher implements a hard-coded maximum.
+ *
+ * The authentication data may also be known as "tag value".
+ *
+ * Return: authentication data size / tag size in bytes
+ */
+static inline unsigned int crypto_aead_authsize(struct crypto_aead *tfm)
+{
+       return tfm->authsize;
+}
+
+/**
+ * crypto_aead_blocksize() - obtain block size of cipher
+ * @tfm: cipher handle
+ *
+ * The block size for the AEAD referenced with the cipher handle is returned.
+ * The caller may use that information to allocate appropriate memory for the
+ * data returned by the encryption or decryption operation
+ *
+ * Return: block size of cipher
+ */
+static inline unsigned int crypto_aead_blocksize(struct crypto_aead *tfm)
+{
+       return crypto_tfm_alg_blocksize(crypto_aead_tfm(tfm));
+}
+
+static inline unsigned int crypto_aead_alignmask(struct crypto_aead *tfm)
+{
+       return crypto_tfm_alg_alignmask(crypto_aead_tfm(tfm));
+}
+
+static inline u32 crypto_aead_get_flags(struct crypto_aead *tfm)
+{
+       return crypto_tfm_get_flags(crypto_aead_tfm(tfm));
+}
+
+static inline void crypto_aead_set_flags(struct crypto_aead *tfm, u32 flags)
+{
+       crypto_tfm_set_flags(crypto_aead_tfm(tfm), flags);
+}
+
+static inline void crypto_aead_clear_flags(struct crypto_aead *tfm, u32 flags)
+{
+       crypto_tfm_clear_flags(crypto_aead_tfm(tfm), flags);
+}
+
+/**
+ * crypto_aead_setkey() - set key for cipher
+ * @tfm: cipher handle
+ * @key: buffer holding the key
+ * @keylen: length of the key in bytes
+ *
+ * The caller provided key is set for the AEAD referenced by the cipher
+ * handle.
+ *
+ * Note, the key length determines the cipher type. Many block ciphers implement
+ * different cipher modes depending on the key size, such as AES-128 vs AES-192
+ * vs. AES-256. When providing a 16 byte key for an AES cipher handle, AES-128
+ * is performed.
+ *
+ * Return: 0 if the setting of the key was successful; < 0 if an error occurred
+ */
+int crypto_aead_setkey(struct crypto_aead *tfm,
+                      const u8 *key, unsigned int keylen);
+
+/**
+ * crypto_aead_setauthsize() - set authentication data size
+ * @tfm: cipher handle
+ * @authsize: size of the authentication data / tag in bytes
+ *
+ * Set the authentication data size / tag size. AEAD requires an authentication
+ * tag (or MAC) in addition to the associated data.
+ *
+ * Return: 0 if the setting of the key was successful; < 0 if an error occurred
+ */
+int crypto_aead_setauthsize(struct crypto_aead *tfm, unsigned int authsize);
+
+static inline struct crypto_aead *crypto_aead_reqtfm(struct aead_request *req)
+{
+       return __crypto_aead_cast(req->base.tfm);
+}
+
+/**
+ * crypto_aead_encrypt() - encrypt plaintext
+ * @req: reference to the aead_request handle that holds all information
+ *      needed to perform the cipher operation
+ *
+ * Encrypt plaintext data using the aead_request handle. That data structure
+ * and how it is filled with data is discussed with the aead_request_*
+ * functions.
+ *
+ * IMPORTANT NOTE The encryption operation creates the authentication data /
+ *               tag. That data is concatenated with the created ciphertext.
+ *               The ciphertext memory size is therefore the given number of
+ *               block cipher blocks + the size defined by the
+ *               crypto_aead_setauthsize invocation. The caller must ensure
+ *               that sufficient memory is available for the ciphertext and
+ *               the authentication tag.
+ *
+ * Return: 0 if the cipher operation was successful; < 0 if an error occurred
+ */
+static inline int crypto_aead_encrypt(struct aead_request *req)
+{
+       return crypto_aead_reqtfm(req)->encrypt(req);
+}
+
+/**
+ * crypto_aead_decrypt() - decrypt ciphertext
+ * @req: reference to the ablkcipher_request handle that holds all information
+ *      needed to perform the cipher operation
+ *
+ * Decrypt ciphertext data using the aead_request handle. That data structure
+ * and how it is filled with data is discussed with the aead_request_*
+ * functions.
+ *
+ * IMPORTANT NOTE The caller must concatenate the ciphertext followed by the
+ *               authentication data / tag. That authentication data / tag
+ *               must have the size defined by the crypto_aead_setauthsize
+ *               invocation.
+ *
+ *
+ * Return: 0 if the cipher operation was successful; -EBADMSG: The AEAD
+ *        cipher operation performs the authentication of the data during the
+ *        decryption operation. Therefore, the function returns this error if
+ *        the authentication of the ciphertext was unsuccessful (i.e. the
+ *        integrity of the ciphertext or the associated data was violated);
+ *        < 0 if an error occurred.
+ */
+static inline int crypto_aead_decrypt(struct aead_request *req)
+{
+       if (req->cryptlen < crypto_aead_authsize(crypto_aead_reqtfm(req)))
+               return -EINVAL;
+
+       return crypto_aead_reqtfm(req)->decrypt(req);
+}
+
+/**
+ * DOC: Asynchronous AEAD Request Handle
+ *
+ * The aead_request data structure contains all pointers to data required for
+ * the AEAD cipher operation. This includes the cipher handle (which can be
+ * used by multiple aead_request instances), pointer to plaintext and
+ * ciphertext, asynchronous callback function, etc. It acts as a handle to the
+ * aead_request_* API calls in a similar way as AEAD handle to the
+ * crypto_aead_* API calls.
+ */
+
+/**
+ * crypto_aead_reqsize() - obtain size of the request data structure
+ * @tfm: cipher handle
+ *
+ * Return: number of bytes
+ */
+unsigned int crypto_aead_reqsize(struct crypto_aead *tfm);
+
+/**
+ * aead_request_set_tfm() - update cipher handle reference in request
+ * @req: request handle to be modified
+ * @tfm: cipher handle that shall be added to the request handle
+ *
+ * Allow the caller to replace the existing aead handle in the request
+ * data structure with a different one.
+ */
+static inline void aead_request_set_tfm(struct aead_request *req,
+                                       struct crypto_aead *tfm)
+{
+       req->base.tfm = crypto_aead_tfm(tfm->child);
+}
+
+/**
+ * aead_request_alloc() - allocate request data structure
+ * @tfm: cipher handle to be registered with the request
+ * @gfp: memory allocation flag that is handed to kmalloc by the API call.
+ *
+ * Allocate the request data structure that must be used with the AEAD
+ * encrypt and decrypt API calls. During the allocation, the provided aead
+ * handle is registered in the request data structure.
+ *
+ * Return: allocated request handle in case of success; IS_ERR() is true in case
+ *        of an error, PTR_ERR() returns the error code.
+ */
+static inline struct aead_request *aead_request_alloc(struct crypto_aead *tfm,
+                                                     gfp_t gfp)
+{
+       struct aead_request *req;
+
+       req = kmalloc(sizeof(*req) + crypto_aead_reqsize(tfm), gfp);
+
+       if (likely(req))
+               aead_request_set_tfm(req, tfm);
+
+       return req;
+}
+
+/**
+ * aead_request_free() - zeroize and free request data structure
+ * @req: request data structure cipher handle to be freed
+ */
+static inline void aead_request_free(struct aead_request *req)
+{
+       kzfree(req);
+}
+
+/**
+ * aead_request_set_callback() - set asynchronous callback function
+ * @req: request handle
+ * @flags: specify zero or an ORing of the flags
+ *        CRYPTO_TFM_REQ_MAY_BACKLOG the request queue may back log and
+ *        increase the wait queue beyond the initial maximum size;
+ *        CRYPTO_TFM_REQ_MAY_SLEEP the request processing may sleep
+ * @compl: callback function pointer to be registered with the request handle
+ * @data: The data pointer refers to memory that is not used by the kernel
+ *       crypto API, but provided to the callback function for it to use. Here,
+ *       the caller can provide a reference to memory the callback function can
+ *       operate on. As the callback function is invoked asynchronously to the
+ *       related functionality, it may need to access data structures of the
+ *       related functionality which can be referenced using this pointer. The
+ *       callback function can access the memory via the "data" field in the
+ *       crypto_async_request data structure provided to the callback function.
+ *
+ * Setting the callback function that is triggered once the cipher operation
+ * completes
+ *
+ * The callback function is registered with the aead_request handle and
+ * must comply with the following template
+ *
+ *     void callback_function(struct crypto_async_request *req, int error)
+ */
+static inline void aead_request_set_callback(struct aead_request *req,
+                                            u32 flags,
+                                            crypto_completion_t compl,
+                                            void *data)
+{
+       req->base.complete = compl;
+       req->base.data = data;
+       req->base.flags = flags;
+}
+
+/**
+ * aead_request_set_crypt - set data buffers
+ * @req: request handle
+ * @src: source scatter / gather list
+ * @dst: destination scatter / gather list
+ * @cryptlen: number of bytes to process from @src
+ * @iv: IV for the cipher operation which must comply with the IV size defined
+ *      by crypto_aead_ivsize()
+ *
+ * Setting the source data and destination data scatter / gather lists which
+ * hold the associated data concatenated with the plaintext or ciphertext. See
+ * below for the authentication tag.
+ *
+ * For encryption, the source is treated as the plaintext and the
+ * destination is the ciphertext. For a decryption operation, the use is
+ * reversed - the source is the ciphertext and the destination is the plaintext.
+ *
+ * For both src/dst the layout is associated data, plain/cipher text,
+ * authentication tag.
+ *
+ * The content of the AD in the destination buffer after processing
+ * will either be untouched, or it will contain a copy of the AD
+ * from the source buffer.  In order to ensure that it always has
+ * a copy of the AD, the user must copy the AD over either before
+ * or after processing.  Of course this is not relevant if the user
+ * is doing in-place processing where src == dst.
+ *
+ * IMPORTANT NOTE AEAD requires an authentication tag (MAC). For decryption,
+ *               the caller must concatenate the ciphertext followed by the
+ *               authentication tag and provide the entire data stream to the
+ *               decryption operation (i.e. the data length used for the
+ *               initialization of the scatterlist and the data length for the
+ *               decryption operation is identical). For encryption, however,
+ *               the authentication tag is created while encrypting the data.
+ *               The destination buffer must hold sufficient space for the
+ *               ciphertext and the authentication tag while the encryption
+ *               invocation must only point to the plaintext data size. The
+ *               following code snippet illustrates the memory usage
+ *               buffer = kmalloc(ptbuflen + (enc ? authsize : 0));
+ *               sg_init_one(&sg, buffer, ptbuflen + (enc ? authsize : 0));
+ *               aead_request_set_crypt(req, &sg, &sg, ptbuflen, iv);
+ */
+static inline void aead_request_set_crypt(struct aead_request *req,
+                                         struct scatterlist *src,
+                                         struct scatterlist *dst,
+                                         unsigned int cryptlen, u8 *iv)
+{
+       req->src = src;
+       req->dst = dst;
+       req->cryptlen = cryptlen;
+       req->iv = iv;
+}
+
+/**
+ * aead_request_set_assoc() - set the associated data scatter / gather list
+ * @req: request handle
+ * @assoc: associated data scatter / gather list
+ * @assoclen: number of bytes to process from @assoc
+ *
+ * Obsolete, do not use.
+ */
+static inline void aead_request_set_assoc(struct aead_request *req,
+                                         struct scatterlist *assoc,
+                                         unsigned int assoclen)
+{
+       req->assoc = assoc;
+       req->assoclen = assoclen;
+       req->old = true;
+}
+
+/**
+ * aead_request_set_ad - set associated data information
+ * @req: request handle
+ * @assoclen: number of bytes in associated data
+ *
+ * Setting the AD information.  This function sets the length of
+ * the associated data.
+ */
+static inline void aead_request_set_ad(struct aead_request *req,
+                                      unsigned int assoclen)
+{
+       req->assoclen = assoclen;
+       req->old = false;
+}
+
 static inline struct crypto_aead *aead_givcrypt_reqtfm(
        struct aead_givcrypt_request *req)
 {
@@ -38,14 +565,12 @@ static inline struct crypto_aead *aead_givcrypt_reqtfm(
 
 static inline int crypto_aead_givencrypt(struct aead_givcrypt_request *req)
 {
-       struct aead_tfm *crt = crypto_aead_crt(aead_givcrypt_reqtfm(req));
-       return crt->givencrypt(req);
+       return aead_givcrypt_reqtfm(req)->givencrypt(req);
 };
 
 static inline int crypto_aead_givdecrypt(struct aead_givcrypt_request *req)
 {
-       struct aead_tfm *crt = crypto_aead_crt(aead_givcrypt_reqtfm(req));
-       return crt->givdecrypt(req);
+       return aead_givcrypt_reqtfm(req)->givdecrypt(req);
 };
 
 static inline void aead_givcrypt_set_tfm(struct aead_givcrypt_request *req,
diff --git a/include/crypto/akcipher.h b/include/crypto/akcipher.h
new file mode 100644 (file)
index 0000000..69d163e
--- /dev/null
@@ -0,0 +1,340 @@
+/*
+ * Public Key Encryption
+ *
+ * Copyright (c) 2015, Intel Corporation
+ * Authors: Tadeusz Struk <tadeusz.struk@intel.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 _CRYPTO_AKCIPHER_H
+#define _CRYPTO_AKCIPHER_H
+#include <linux/crypto.h>
+
+/**
+ * struct akcipher_request - public key request
+ *
+ * @base:      Common attributes for async crypto requests
+ * @src:       Pointer to memory containing the input parameters
+ *             The format of the parameter(s) is expeted to be Octet String
+ * @dst:       Pointer to memory whare the result will be stored
+ * @src_len:   Size of the input parameter
+ * @dst_len:   Size of the output buffer. It needs to be at leaset
+ *             as big as the expected result depending on the operation
+ *             After operation it will be updated with the acctual size of the
+ *             result. In case of error, where the dst_len was insufficient,
+ *             it will be updated to the size required for the operation.
+ * @__ctx:     Start of private context data
+ */
+struct akcipher_request {
+       struct crypto_async_request base;
+       void *src;
+       void *dst;
+       unsigned int src_len;
+       unsigned int dst_len;
+       void *__ctx[] CRYPTO_MINALIGN_ATTR;
+};
+
+/**
+ * struct crypto_akcipher - user-instantiated objects which encapsulate
+ * algorithms and core processing logic
+ *
+ * @base:      Common crypto API algorithm data structure
+ */
+struct crypto_akcipher {
+       struct crypto_tfm base;
+};
+
+/**
+ * struct akcipher_alg - generic public key algorithm
+ *
+ * @sign:      Function performs a sign operation as defined by public key
+ *             algorithm. In case of error, where the dst_len was insufficient,
+ *             the req->dst_len will be updated to the size required for the
+ *             operation
+ * @verify:    Function performs a sign operation as defined by public key
+ *             algorithm. In case of error, where the dst_len was insufficient,
+ *             the req->dst_len will be updated to the size required for the
+ *             operation
+ * @encrypt:   Function performs an encrytp operation as defined by public key
+ *             algorithm. In case of error, where the dst_len was insufficient,
+ *             the req->dst_len will be updated to the size required for the
+ *             operation
+ * @decrypt:   Function performs a decrypt operation as defined by public key
+ *             algorithm. In case of error, where the dst_len was insufficient,
+ *             the req->dst_len will be updated to the size required for the
+ *             operation
+ * @setkey:    Function invokes the algorithm specific set key function, which
+ *             knows how to decode and interpret the BER encoded key
+ * @init:      Initialize the cryptographic transformation object.
+ *             This function is used to initialize the cryptographic
+ *             transformation object. This function is called only once at
+ *             the instantiation time, right after the transformation context
+ *             was allocated. In case the cryptographic hardware has some
+ *             special requirements which need to be handled by software, this
+ *             function shall check for the precise requirement of the
+ *             transformation and put any software fallbacks in place.
+ * @exit:      Deinitialize the cryptographic transformation object. This is a
+ *             counterpart to @init, used to remove various changes set in
+ *             @init.
+ *
+ * @reqsize:   Request context size required by algorithm implementation
+ * @base:      Common crypto API algorithm data structure
+ */
+struct akcipher_alg {
+       int (*sign)(struct akcipher_request *req);
+       int (*verify)(struct akcipher_request *req);
+       int (*encrypt)(struct akcipher_request *req);
+       int (*decrypt)(struct akcipher_request *req);
+       int (*setkey)(struct crypto_akcipher *tfm, const void *key,
+                     unsigned int keylen);
+       int (*init)(struct crypto_akcipher *tfm);
+       void (*exit)(struct crypto_akcipher *tfm);
+
+       unsigned int reqsize;
+       struct crypto_alg base;
+};
+
+/**
+ * DOC: Generic Public Key API
+ *
+ * The Public Key API is used with the algorithms of type
+ * CRYPTO_ALG_TYPE_AKCIPHER (listed as type "akcipher" in /proc/crypto)
+ */
+
+/**
+ * crypto_alloc_akcipher() -- allocate AKCIPHER tfm handle
+ * @alg_name: is the cra_name / name or cra_driver_name / driver name of the
+ *           public key algorithm e.g. "rsa"
+ * @type: specifies the type of the algorithm
+ * @mask: specifies the mask for the algorithm
+ *
+ * Allocate a handle for public key algorithm. The returned struct
+ * crypto_akcipher is the handle that is required for any subsequent
+ * API invocation for the public key operations.
+ *
+ * Return: allocated handle in case of success; IS_ERR() is true in case
+ *        of an error, PTR_ERR() returns the error code.
+ */
+struct crypto_akcipher *crypto_alloc_akcipher(const char *alg_name, u32 type,
+                                             u32 mask);
+
+static inline struct crypto_tfm *crypto_akcipher_tfm(
+       struct crypto_akcipher *tfm)
+{
+       return &tfm->base;
+}
+
+static inline struct akcipher_alg *__crypto_akcipher_alg(struct crypto_alg *alg)
+{
+       return container_of(alg, struct akcipher_alg, base);
+}
+
+static inline struct crypto_akcipher *__crypto_akcipher_tfm(
+       struct crypto_tfm *tfm)
+{
+       return container_of(tfm, struct crypto_akcipher, base);
+}
+
+static inline struct akcipher_alg *crypto_akcipher_alg(
+       struct crypto_akcipher *tfm)
+{
+       return __crypto_akcipher_alg(crypto_akcipher_tfm(tfm)->__crt_alg);
+}
+
+static inline unsigned int crypto_akcipher_reqsize(struct crypto_akcipher *tfm)
+{
+       return crypto_akcipher_alg(tfm)->reqsize;
+}
+
+static inline void akcipher_request_set_tfm(struct akcipher_request *req,
+                                           struct crypto_akcipher *tfm)
+{
+       req->base.tfm = crypto_akcipher_tfm(tfm);
+}
+
+static inline struct crypto_akcipher *crypto_akcipher_reqtfm(
+       struct akcipher_request *req)
+{
+       return __crypto_akcipher_tfm(req->base.tfm);
+}
+
+/**
+ * crypto_free_akcipher() -- free AKCIPHER tfm handle
+ *
+ * @tfm: AKCIPHER tfm handle allocated with crypto_alloc_akcipher()
+ */
+static inline void crypto_free_akcipher(struct crypto_akcipher *tfm)
+{
+       crypto_destroy_tfm(tfm, crypto_akcipher_tfm(tfm));
+}
+
+/**
+ * akcipher_request_alloc() -- allocates public key request
+ *
+ * @tfm:       AKCIPHER tfm handle allocated with crypto_alloc_akcipher()
+ * @gfp:       allocation flags
+ *
+ * Return: allocated handle in case of success or NULL in case of an error.
+ */
+static inline struct akcipher_request *akcipher_request_alloc(
+       struct crypto_akcipher *tfm, gfp_t gfp)
+{
+       struct akcipher_request *req;
+
+       req = kmalloc(sizeof(*req) + crypto_akcipher_reqsize(tfm), gfp);
+       if (likely(req))
+               akcipher_request_set_tfm(req, tfm);
+
+       return req;
+}
+
+/**
+ * akcipher_request_free() -- zeroize and free public key request
+ *
+ * @req:       request to free
+ */
+static inline void akcipher_request_free(struct akcipher_request *req)
+{
+       kzfree(req);
+}
+
+/**
+ * akcipher_request_set_callback() -- Sets an asynchronous callback.
+ *
+ * Callback will be called when an asynchronous operation on a given
+ * request is finished.
+ *
+ * @req:       request that the callback will be set for
+ * @flgs:      specify for instance if the operation may backlog
+ * @cmlp:      callback which will be called
+ * @data:      private data used by the caller
+ */
+static inline void akcipher_request_set_callback(struct akcipher_request *req,
+                                                u32 flgs,
+                                                crypto_completion_t cmpl,
+                                                void *data)
+{
+       req->base.complete = cmpl;
+       req->base.data = data;
+       req->base.flags = flgs;
+}
+
+/**
+ * akcipher_request_set_crypt() -- Sets reqest parameters
+ *
+ * Sets parameters required by crypto operation
+ *
+ * @req:       public key request
+ * @src:       ptr to input parameter
+ * @dst:       ptr of output parameter
+ * @src_len:   size of the input buffer
+ * @dst_len:   size of the output buffer. It will be updated by the
+ *             implementation to reflect the acctual size of the result
+ */
+static inline void akcipher_request_set_crypt(struct akcipher_request *req,
+                                             void *src, void *dst,
+                                             unsigned int src_len,
+                                             unsigned int dst_len)
+{
+       req->src = src;
+       req->dst = dst;
+       req->src_len = src_len;
+       req->dst_len = dst_len;
+}
+
+/**
+ * crypto_akcipher_encrypt() -- Invoke public key encrypt operation
+ *
+ * Function invokes the specific public key encrypt operation for a given
+ * public key algorithm
+ *
+ * @req:       asymmetric key request
+ *
+ * Return: zero on success; error code in case of error
+ */
+static inline int crypto_akcipher_encrypt(struct akcipher_request *req)
+{
+       struct crypto_akcipher *tfm = crypto_akcipher_reqtfm(req);
+       struct akcipher_alg *alg = crypto_akcipher_alg(tfm);
+
+       return alg->encrypt(req);
+}
+
+/**
+ * crypto_akcipher_decrypt() -- Invoke public key decrypt operation
+ *
+ * Function invokes the specific public key decrypt operation for a given
+ * public key algorithm
+ *
+ * @req:       asymmetric key request
+ *
+ * Return: zero on success; error code in case of error
+ */
+static inline int crypto_akcipher_decrypt(struct akcipher_request *req)
+{
+       struct crypto_akcipher *tfm = crypto_akcipher_reqtfm(req);
+       struct akcipher_alg *alg = crypto_akcipher_alg(tfm);
+
+       return alg->decrypt(req);
+}
+
+/**
+ * crypto_akcipher_sign() -- Invoke public key sign operation
+ *
+ * Function invokes the specific public key sign operation for a given
+ * public key algorithm
+ *
+ * @req:       asymmetric key request
+ *
+ * Return: zero on success; error code in case of error
+ */
+static inline int crypto_akcipher_sign(struct akcipher_request *req)
+{
+       struct crypto_akcipher *tfm = crypto_akcipher_reqtfm(req);
+       struct akcipher_alg *alg = crypto_akcipher_alg(tfm);
+
+       return alg->sign(req);
+}
+
+/**
+ * crypto_akcipher_verify() -- Invoke public key verify operation
+ *
+ * Function invokes the specific public key verify operation for a given
+ * public key algorithm
+ *
+ * @req:       asymmetric key request
+ *
+ * Return: zero on success; error code in case of error
+ */
+static inline int crypto_akcipher_verify(struct akcipher_request *req)
+{
+       struct crypto_akcipher *tfm = crypto_akcipher_reqtfm(req);
+       struct akcipher_alg *alg = crypto_akcipher_alg(tfm);
+
+       return alg->verify(req);
+}
+
+/**
+ * crypto_akcipher_setkey() -- Invoke public key setkey operation
+ *
+ * Function invokes the algorithm specific set key function, which knows
+ * how to decode and interpret the encoded key
+ *
+ * @tfm:       tfm handle
+ * @key:       BER encoded private or public key
+ * @keylen:    length of the key
+ *
+ * Return: zero on success; error code in case of error
+ */
+static inline int crypto_akcipher_setkey(struct crypto_akcipher *tfm, void *key,
+                                        unsigned int keylen)
+{
+       struct akcipher_alg *alg = crypto_akcipher_alg(tfm);
+
+       return alg->setkey(tfm, key, keylen);
+}
+#endif
index 0ecb7688af71aa28496404ddbcdbfafdcb45ce6e..d4ebf6e9af6a536c589d55c914e56f1c6000f910 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/kernel.h>
 #include <linux/skbuff.h>
 
+struct crypto_aead;
 struct module;
 struct rtattr;
 struct seq_file;
@@ -126,7 +127,6 @@ struct ablkcipher_walk {
 };
 
 extern const struct crypto_type crypto_ablkcipher_type;
-extern const struct crypto_type crypto_aead_type;
 extern const struct crypto_type crypto_blkcipher_type;
 
 void crypto_mod_put(struct crypto_alg *alg);
@@ -144,6 +144,8 @@ int crypto_init_spawn(struct crypto_spawn *spawn, struct crypto_alg *alg,
 int crypto_init_spawn2(struct crypto_spawn *spawn, struct crypto_alg *alg,
                       struct crypto_instance *inst,
                       const struct crypto_type *frontend);
+int crypto_grab_spawn(struct crypto_spawn *spawn, const char *name,
+                     u32 type, u32 mask);
 
 void crypto_drop_spawn(struct crypto_spawn *spawn);
 struct crypto_tfm *crypto_spawn_tfm(struct crypto_spawn *spawn, u32 type,
@@ -239,22 +241,6 @@ static inline void *crypto_ablkcipher_ctx_aligned(struct crypto_ablkcipher *tfm)
        return crypto_tfm_ctx_aligned(&tfm->base);
 }
 
-static inline struct aead_alg *crypto_aead_alg(struct crypto_aead *tfm)
-{
-       return &crypto_aead_tfm(tfm)->__crt_alg->cra_aead;
-}
-
-static inline void *crypto_aead_ctx(struct crypto_aead *tfm)
-{
-       return crypto_tfm_ctx(&tfm->base);
-}
-
-static inline struct crypto_instance *crypto_aead_alg_instance(
-       struct crypto_aead *aead)
-{
-       return crypto_tfm_alg_instance(&aead->base);
-}
-
 static inline struct crypto_blkcipher *crypto_spawn_blkcipher(
        struct crypto_spawn *spawn)
 {
@@ -363,21 +349,6 @@ static inline int ablkcipher_tfm_in_queue(struct crypto_queue *queue,
        return crypto_tfm_in_queue(queue, crypto_ablkcipher_tfm(tfm));
 }
 
-static inline void *aead_request_ctx(struct aead_request *req)
-{
-       return req->__ctx;
-}
-
-static inline void aead_request_complete(struct aead_request *req, int err)
-{
-       req->base.complete(&req->base, err);
-}
-
-static inline u32 aead_request_flags(struct aead_request *req)
-{
-       return req->base.flags;
-}
-
 static inline struct crypto_alg *crypto_get_attr_alg(struct rtattr **tb,
                                                     u32 type, u32 mask)
 {
index 86163ef24219874a0a96640de84686105f2529fb..5b67af834d836eae2a6e74740f9862e0d399145b 100644 (file)
@@ -55,14 +55,14 @@ struct crypto_pcomp {
 };
 
 struct pcomp_alg {
-       int (*compress_setup)(struct crypto_pcomp *tfm, void *params,
+       int (*compress_setup)(struct crypto_pcomp *tfm, const void *params,
                              unsigned int len);
        int (*compress_init)(struct crypto_pcomp *tfm);
        int (*compress_update)(struct crypto_pcomp *tfm,
                               struct comp_request *req);
        int (*compress_final)(struct crypto_pcomp *tfm,
                              struct comp_request *req);
-       int (*decompress_setup)(struct crypto_pcomp *tfm, void *params,
+       int (*decompress_setup)(struct crypto_pcomp *tfm, const void *params,
                                unsigned int len);
        int (*decompress_init)(struct crypto_pcomp *tfm);
        int (*decompress_update)(struct crypto_pcomp *tfm,
@@ -97,7 +97,7 @@ static inline struct pcomp_alg *crypto_pcomp_alg(struct crypto_pcomp *tfm)
 }
 
 static inline int crypto_compress_setup(struct crypto_pcomp *tfm,
-                                       void *params, unsigned int len)
+                                       const void *params, unsigned int len)
 {
        return crypto_pcomp_alg(tfm)->compress_setup(tfm, params, len);
 }
@@ -120,7 +120,7 @@ static inline int crypto_compress_final(struct crypto_pcomp *tfm,
 }
 
 static inline int crypto_decompress_setup(struct crypto_pcomp *tfm,
-                                         void *params, unsigned int len)
+                                         const void *params, unsigned int len)
 {
        return crypto_pcomp_alg(tfm)->decompress_setup(tfm, params, len);
 }
index ba98918bbd9b8ec53207aa77abed034c56bc40ec..1547f540c920b3fea66a7028f0b868f75f0e4d37 100644 (file)
@@ -14,6 +14,7 @@
 
 #include <linux/crypto.h>
 #include <linux/kernel.h>
+#include <crypto/aead.h>
 #include <crypto/hash.h>
 
 struct cryptd_ablkcipher {
index 5186f750c7131ee14625fb5eb71344b6ba70d747..9756c70899d8195eb54f7349efb911e8604d6222 100644 (file)
@@ -49,8 +49,9 @@
 #include <crypto/internal/rng.h>
 #include <crypto/rng.h>
 #include <linux/fips.h>
-#include <linux/spinlock.h>
+#include <linux/mutex.h>
 #include <linux/list.h>
+#include <linux/workqueue.h>
 
 /*
  * Concatenation Helper and string operation helper
@@ -104,12 +105,13 @@ struct drbg_test_data {
 };
 
 struct drbg_state {
-       spinlock_t drbg_lock;   /* lock around DRBG */
+       struct mutex drbg_mutex;        /* lock around DRBG */
        unsigned char *V;       /* internal state 10.1.1.1 1a) */
        /* hash: static value 10.1.1.1 1b) hmac / ctr: key */
        unsigned char *C;
        /* Number of RNG requests since last reseed -- 10.1.1.1 1c) */
        size_t reseed_ctr;
+       size_t reseed_threshold;
         /* some memory the DRBG can use for its operation */
        unsigned char *scratchpad;
        void *priv_data;        /* Cipher handle */
@@ -119,9 +121,12 @@ struct drbg_state {
        bool fips_primed;       /* Continuous test primed? */
        unsigned char *prev;    /* FIPS 140-2 continuous test value */
 #endif
+       struct work_struct seed_work;   /* asynchronous seeding support */
+       struct crypto_rng *jent;
        const struct drbg_state_ops *d_ops;
        const struct drbg_core *core;
-       struct drbg_test_data *test_data;
+       struct drbg_string test_data;
+       struct random_ready_callback random_ready;
 };
 
 static inline __u8 drbg_statelen(struct drbg_state *drbg)
@@ -176,20 +181,9 @@ static inline size_t drbg_max_requests(struct drbg_state *drbg)
 #endif
 }
 
-/*
- * kernel crypto API input data structure for DRBG generate in case dlen
- * is set to 0
- */
-struct drbg_gen {
-       unsigned char *outbuf;  /* output buffer for random numbers */
-       unsigned int outlen;    /* size of output buffer */
-       struct drbg_string *addtl;      /* additional information string */
-       struct drbg_test_data *test_data;       /* test data */
-};
-
 /*
  * This is a wrapper to the kernel crypto API function of
- * crypto_rng_get_bytes() to allow the caller to provide additional data.
+ * crypto_rng_generate() to allow the caller to provide additional data.
  *
  * @drng DRBG handle -- see crypto_rng_get_bytes
  * @outbuf output buffer -- see crypto_rng_get_bytes
@@ -204,21 +198,15 @@ static inline int crypto_drbg_get_bytes_addtl(struct crypto_rng *drng,
                        unsigned char *outbuf, unsigned int outlen,
                        struct drbg_string *addtl)
 {
-       int ret;
-       struct drbg_gen genbuf;
-       genbuf.outbuf = outbuf;
-       genbuf.outlen = outlen;
-       genbuf.addtl = addtl;
-       genbuf.test_data = NULL;
-       ret = crypto_rng_get_bytes(drng, (u8 *)&genbuf, 0);
-       return ret;
+       return crypto_rng_generate(drng, addtl->buf, addtl->len,
+                                  outbuf, outlen);
 }
 
 /*
  * TEST code
  *
  * This is a wrapper to the kernel crypto API function of
- * crypto_rng_get_bytes() to allow the caller to provide additional data and
+ * crypto_rng_generate() to allow the caller to provide additional data and
  * allow furnishing of test_data
  *
  * @drng DRBG handle -- see crypto_rng_get_bytes
@@ -236,14 +224,10 @@ static inline int crypto_drbg_get_bytes_addtl_test(struct crypto_rng *drng,
                        struct drbg_string *addtl,
                        struct drbg_test_data *test_data)
 {
-       int ret;
-       struct drbg_gen genbuf;
-       genbuf.outbuf = outbuf;
-       genbuf.outlen = outlen;
-       genbuf.addtl = addtl;
-       genbuf.test_data = test_data;
-       ret = crypto_rng_get_bytes(drng, (u8 *)&genbuf, 0);
-       return ret;
+       crypto_rng_set_entropy(drng, test_data->testentropy->buf,
+                              test_data->testentropy->len);
+       return crypto_rng_generate(drng, addtl->buf, addtl->len,
+                                  outbuf, outlen);
 }
 
 /*
@@ -264,14 +248,9 @@ static inline int crypto_drbg_reset_test(struct crypto_rng *drng,
                                         struct drbg_string *pers,
                                         struct drbg_test_data *test_data)
 {
-       int ret;
-       struct drbg_gen genbuf;
-       genbuf.outbuf = NULL;
-       genbuf.outlen = 0;
-       genbuf.addtl = pers;
-       genbuf.test_data = test_data;
-       ret = crypto_rng_reset(drng, (u8 *)&genbuf, 0);
-       return ret;
+       crypto_rng_set_entropy(drng, test_data->testentropy->buf,
+                              test_data->testentropy->len);
+       return crypto_rng_reset(drng, pers->buf, pers->len);
 }
 
 /* DRBG type flags */
index 98abda9ed3aa868996e640ae7d15cff05e20539a..57c8a6ee33c27321d1a1e366559a858ae5cccdb3 100644 (file)
@@ -66,7 +66,7 @@ struct ahash_request {
 /**
  * struct ahash_alg - asynchronous message digest definition
  * @init: Initialize the transformation context. Intended only to initialize the
- *       state of the HASH transformation at the begining. This shall fill in
+ *       state of the HASH transformation at the beginning. This shall fill in
  *       the internal structures used during the entire duration of the whole
  *       transformation. No data processing happens at this point.
  * @update: Push a chunk of data into the driver for transformation. This
index 2eba340230a73a63e8a1472a20ff0ed0a451d981..4b2547186519f80e00729144dd8edafab238b5ef 100644 (file)
 
 #include <crypto/aead.h>
 #include <crypto/algapi.h>
+#include <linux/stddef.h>
 #include <linux/types.h>
 
 struct rtattr;
 
+struct aead_instance {
+       union {
+               struct {
+                       char head[offsetof(struct aead_alg, base)];
+                       struct crypto_instance base;
+               } s;
+               struct aead_alg alg;
+       };
+};
+
 struct crypto_aead_spawn {
        struct crypto_spawn base;
 };
 
+extern const struct crypto_type crypto_aead_type;
 extern const struct crypto_type crypto_nivaead_type;
 
+static inline void *crypto_aead_ctx(struct crypto_aead *tfm)
+{
+       return crypto_tfm_ctx(&tfm->base);
+}
+
+static inline struct crypto_instance *crypto_aead_alg_instance(
+       struct crypto_aead *aead)
+{
+       return crypto_tfm_alg_instance(&aead->base);
+}
+
+static inline struct crypto_instance *aead_crypto_instance(
+       struct aead_instance *inst)
+{
+       return container_of(&inst->alg.base, struct crypto_instance, alg);
+}
+
+static inline struct aead_instance *aead_instance(struct crypto_instance *inst)
+{
+       return container_of(&inst->alg, struct aead_instance, alg.base);
+}
+
+static inline struct aead_instance *aead_alg_instance(struct crypto_aead *aead)
+{
+       return aead_instance(crypto_aead_alg_instance(aead));
+}
+
+static inline void *aead_instance_ctx(struct aead_instance *inst)
+{
+       return crypto_instance_ctx(aead_crypto_instance(inst));
+}
+
+static inline void *aead_request_ctx(struct aead_request *req)
+{
+       return req->__ctx;
+}
+
+static inline void aead_request_complete(struct aead_request *req, int err)
+{
+       req->base.complete(&req->base, err);
+}
+
+static inline u32 aead_request_flags(struct aead_request *req)
+{
+       return req->base.flags;
+}
+
 static inline void crypto_set_aead_spawn(
        struct crypto_aead_spawn *spawn, struct crypto_instance *inst)
 {
@@ -47,24 +106,27 @@ static inline struct crypto_alg *crypto_aead_spawn_alg(
        return spawn->base.alg;
 }
 
+static inline struct aead_alg *crypto_spawn_aead_alg(
+       struct crypto_aead_spawn *spawn)
+{
+       return container_of(spawn->base.alg, struct aead_alg, base);
+}
+
 static inline struct crypto_aead *crypto_spawn_aead(
        struct crypto_aead_spawn *spawn)
 {
-       return __crypto_aead_cast(
-               crypto_spawn_tfm(&spawn->base, CRYPTO_ALG_TYPE_AEAD,
-                                CRYPTO_ALG_TYPE_MASK));
+       return crypto_spawn_tfm2(&spawn->base);
 }
 
-struct crypto_instance *aead_geniv_alloc(struct crypto_template *tmpl,
-                                        struct rtattr **tb, u32 type,
-                                        u32 mask);
-void aead_geniv_free(struct crypto_instance *inst);
+struct aead_instance *aead_geniv_alloc(struct crypto_template *tmpl,
+                                      struct rtattr **tb, u32 type, u32 mask);
+void aead_geniv_free(struct aead_instance *inst);
 int aead_geniv_init(struct crypto_tfm *tfm);
 void aead_geniv_exit(struct crypto_tfm *tfm);
 
 static inline struct crypto_aead *aead_geniv_base(struct crypto_aead *geniv)
 {
-       return crypto_aead_crt(geniv)->base;
+       return geniv->child;
 }
 
 static inline void *aead_givcrypt_reqctx(struct aead_givcrypt_request *req)
@@ -78,5 +140,29 @@ static inline void aead_givcrypt_complete(struct aead_givcrypt_request *req,
        aead_request_complete(&req->areq, err);
 }
 
+static inline void crypto_aead_set_reqsize(struct crypto_aead *aead,
+                                          unsigned int reqsize)
+{
+       crypto_aead_crt(aead)->reqsize = reqsize;
+}
+
+static inline unsigned int crypto_aead_alg_maxauthsize(struct aead_alg *alg)
+{
+       return alg->base.cra_aead.encrypt ? alg->base.cra_aead.maxauthsize :
+                                           alg->maxauthsize;
+}
+
+static inline unsigned int crypto_aead_maxauthsize(struct crypto_aead *aead)
+{
+       return crypto_aead_alg_maxauthsize(crypto_aead_alg(aead));
+}
+
+int crypto_register_aead(struct aead_alg *alg);
+void crypto_unregister_aead(struct aead_alg *alg);
+int crypto_register_aeads(struct aead_alg *algs, int count);
+void crypto_unregister_aeads(struct aead_alg *algs, int count);
+int aead_register_instance(struct crypto_template *tmpl,
+                          struct aead_instance *inst);
+
 #endif /* _CRYPTO_INTERNAL_AEAD_H */
 
diff --git a/include/crypto/internal/akcipher.h b/include/crypto/internal/akcipher.h
new file mode 100644 (file)
index 0000000..9a2bda1
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * Public Key Encryption
+ *
+ * Copyright (c) 2015, Intel Corporation
+ * Authors: Tadeusz Struk <tadeusz.struk@intel.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 _CRYPTO_AKCIPHER_INT_H
+#define _CRYPTO_AKCIPHER_INT_H
+#include <crypto/akcipher.h>
+
+/*
+ * Transform internal helpers.
+ */
+static inline void *akcipher_request_ctx(struct akcipher_request *req)
+{
+       return req->__ctx;
+}
+
+static inline void *akcipher_tfm_ctx(struct crypto_akcipher *tfm)
+{
+       return tfm->base.__crt_ctx;
+}
+
+static inline void akcipher_request_complete(struct akcipher_request *req,
+                                            int err)
+{
+       req->base.complete(&req->base, err);
+}
+
+static inline const char *akcipher_alg_name(struct crypto_akcipher *tfm)
+{
+       return crypto_akcipher_tfm(tfm)->__crt_alg->cra_name;
+}
+
+/**
+ * crypto_register_akcipher() -- Register public key algorithm
+ *
+ * Function registers an implementation of a public key verify algorithm
+ *
+ * @alg:       algorithm definition
+ *
+ * Return: zero on success; error code in case of error
+ */
+int crypto_register_akcipher(struct akcipher_alg *alg);
+
+/**
+ * crypto_unregister_akcipher() -- Unregister public key algorithm
+ *
+ * Function unregisters an implementation of a public key verify algorithm
+ *
+ * @alg:       algorithm definition
+ */
+void crypto_unregister_akcipher(struct akcipher_alg *alg);
+#endif
diff --git a/include/crypto/internal/geniv.h b/include/crypto/internal/geniv.h
new file mode 100644 (file)
index 0000000..9ca9b87
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * geniv: IV generation
+ *
+ * Copyright (c) 2015 Herbert Xu <herbert@gondor.apana.org.au>
+ *
+ * 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 _CRYPTO_INTERNAL_GENIV_H
+#define _CRYPTO_INTERNAL_GENIV_H
+
+#include <crypto/internal/aead.h>
+#include <linux/spinlock.h>
+
+struct aead_geniv_ctx {
+       spinlock_t lock;
+       struct crypto_aead *child;
+};
+
+#endif /* _CRYPTO_INTERNAL_GENIV_H */
index 896973369573f7f078c519cd562128206129398a..a52ef3483dd75032134c2d2b0785f76520e2cceb 100644 (file)
@@ -2,6 +2,7 @@
  * RNG: Random Number Generator  algorithms under the crypto API
  *
  * Copyright (c) 2008 Neil Horman <nhorman@tuxdriver.com>
+ * Copyright (c) 2015 Herbert Xu <herbert@gondor.apana.org.au>
  *
  * 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
 #include <crypto/algapi.h>
 #include <crypto/rng.h>
 
-extern const struct crypto_type crypto_rng_type;
+int crypto_register_rng(struct rng_alg *alg);
+void crypto_unregister_rng(struct rng_alg *alg);
+int crypto_register_rngs(struct rng_alg *algs, int count);
+void crypto_unregister_rngs(struct rng_alg *algs, int count);
+
+#if defined(CONFIG_CRYPTO_RNG) || defined(CONFIG_CRYPTO_RNG_MODULE)
+int crypto_del_default_rng(void);
+#else
+static inline int crypto_del_default_rng(void)
+{
+       return 0;
+}
+#endif
 
 static inline void *crypto_rng_ctx(struct crypto_rng *tfm)
 {
        return crypto_tfm_ctx(&tfm->base);
 }
 
+static inline void crypto_rng_set_entropy(struct crypto_rng *tfm,
+                                         const u8 *data, unsigned int len)
+{
+       crypto_rng_alg(tfm)->set_ent(tfm, data, len);
+}
+
 #endif
diff --git a/include/crypto/internal/rsa.h b/include/crypto/internal/rsa.h
new file mode 100644 (file)
index 0000000..a8c8636
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * RSA internal helpers
+ *
+ * Copyright (c) 2015, Intel Corporation
+ * Authors: Tadeusz Struk <tadeusz.struk@intel.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 _RSA_HELPER_
+#define _RSA_HELPER_
+#include <linux/mpi.h>
+
+struct rsa_key {
+       MPI n;
+       MPI e;
+       MPI d;
+};
+
+int rsa_parse_key(struct rsa_key *rsa_key, const void *key,
+                 unsigned int key_len);
+
+void rsa_free_key(struct rsa_key *rsa_key);
+#endif
index 65f299b08b0d7721f6748b0f4c67672bffe62c59..146af825eedba1813ce3bea39c2b6cbcf2c2a1e4 100644 (file)
@@ -8,6 +8,11 @@
 #define MD5_BLOCK_WORDS                16
 #define MD5_HASH_WORDS         4
 
+#define MD5_H0 0x67452301UL
+#define MD5_H1 0xefcdab89UL
+#define MD5_H2 0x98badcfeUL
+#define MD5_H3 0x10325476UL
+
 struct md5_state {
        u32 hash[MD5_HASH_WORDS];
        u32 block[MD5_BLOCK_WORDS];
index b7c864cc70df88ce773ad4945489997b25ac9372..06dc30d9f56e9035fba27c653bf61aacbdbb67b3 100644 (file)
@@ -8,4 +8,7 @@
 #define NULL_DIGEST_SIZE       0
 #define NULL_IV_SIZE           0
 
+struct crypto_blkcipher *crypto_get_default_null_skcipher(void);
+void crypto_put_default_null_skcipher(void);
+
 #endif
index 6e28ea5be9f1264d685be72e00472ef6506a65c8..b95ede354a6651ee0d3bc831fbe8962276e7f563 100644 (file)
@@ -2,6 +2,7 @@
  * RNG: Random Number Generator  algorithms under the crypto API
  *
  * Copyright (c) 2008 Neil Horman <nhorman@tuxdriver.com>
+ * Copyright (c) 2015 Herbert Xu <herbert@gondor.apana.org.au>
  *
  * 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
 
 #include <linux/crypto.h>
 
+struct crypto_rng;
+
+/**
+ * struct rng_alg - random number generator definition
+ *
+ * @generate:  The function defined by this variable obtains a
+ *             random number. The random number generator transform
+ *             must generate the random number out of the context
+ *             provided with this call, plus any additional data
+ *             if provided to the call.
+ * @seed:      Seed or reseed the random number generator.  With the
+ *             invocation of this function call, the random number
+ *             generator shall become ready for generation.  If the
+ *             random number generator requires a seed for setting
+ *             up a new state, the seed must be provided by the
+ *             consumer while invoking this function. The required
+ *             size of the seed is defined with @seedsize .
+ * @set_ent:   Set entropy that would otherwise be obtained from
+ *             entropy source.  Internal use only.
+ * @seedsize:  The seed size required for a random number generator
+ *             initialization defined with this variable. Some
+ *             random number generators does not require a seed
+ *             as the seeding is implemented internally without
+ *             the need of support by the consumer. In this case,
+ *             the seed size is set to zero.
+ * @base:      Common crypto API algorithm data structure.
+ */
+struct rng_alg {
+       int (*generate)(struct crypto_rng *tfm,
+                       const u8 *src, unsigned int slen,
+                       u8 *dst, unsigned int dlen);
+       int (*seed)(struct crypto_rng *tfm, const u8 *seed, unsigned int slen);
+       void (*set_ent)(struct crypto_rng *tfm, const u8 *data,
+                       unsigned int len);
+
+       unsigned int seedsize;
+
+       struct crypto_alg base;
+};
+
+struct crypto_rng {
+       struct crypto_tfm base;
+};
+
 extern struct crypto_rng *crypto_default_rng;
 
 int crypto_get_default_rng(void);
@@ -27,11 +72,6 @@ void crypto_put_default_rng(void);
  * CRYPTO_ALG_TYPE_RNG (listed as type "rng" in /proc/crypto)
  */
 
-static inline struct crypto_rng *__crypto_rng_cast(struct crypto_tfm *tfm)
-{
-       return (struct crypto_rng *)tfm;
-}
-
 /**
  * crypto_alloc_rng() -- allocate RNG handle
  * @alg_name: is the cra_name / name or cra_driver_name / driver name of the
@@ -52,15 +92,7 @@ static inline struct crypto_rng *__crypto_rng_cast(struct crypto_tfm *tfm)
  * Return: allocated cipher handle in case of success; IS_ERR() is true in case
  *        of an error, PTR_ERR() returns the error code.
  */
-static inline struct crypto_rng *crypto_alloc_rng(const char *alg_name,
-                                                 u32 type, u32 mask)
-{
-       type &= ~CRYPTO_ALG_TYPE_MASK;
-       type |= CRYPTO_ALG_TYPE_RNG;
-       mask |= CRYPTO_ALG_TYPE_MASK;
-
-       return __crypto_rng_cast(crypto_alloc_base(alg_name, type, mask));
-}
+struct crypto_rng *crypto_alloc_rng(const char *alg_name, u32 type, u32 mask);
 
 static inline struct crypto_tfm *crypto_rng_tfm(struct crypto_rng *tfm)
 {
@@ -77,12 +109,8 @@ static inline struct crypto_tfm *crypto_rng_tfm(struct crypto_rng *tfm)
  */
 static inline struct rng_alg *crypto_rng_alg(struct crypto_rng *tfm)
 {
-       return &crypto_rng_tfm(tfm)->__crt_alg->cra_rng;
-}
-
-static inline struct rng_tfm *crypto_rng_crt(struct crypto_rng *tfm)
-{
-       return &crypto_rng_tfm(tfm)->crt_rng;
+       return container_of(crypto_rng_tfm(tfm)->__crt_alg,
+                           struct rng_alg, base);
 }
 
 /**
@@ -91,7 +119,28 @@ static inline struct rng_tfm *crypto_rng_crt(struct crypto_rng *tfm)
  */
 static inline void crypto_free_rng(struct crypto_rng *tfm)
 {
-       crypto_free_tfm(crypto_rng_tfm(tfm));
+       crypto_destroy_tfm(tfm, crypto_rng_tfm(tfm));
+}
+
+/**
+ * crypto_rng_generate() - get random number
+ * @tfm: cipher handle
+ * @src: Input buffer holding additional data, may be NULL
+ * @slen: Length of additional data
+ * @dst: output buffer holding the random numbers
+ * @dlen: length of the output buffer
+ *
+ * This function fills the caller-allocated buffer with random
+ * numbers using the random number generator referenced by the
+ * cipher handle.
+ *
+ * Return: 0 function was successful; < 0 if an error occurred
+ */
+static inline int crypto_rng_generate(struct crypto_rng *tfm,
+                                     const u8 *src, unsigned int slen,
+                                     u8 *dst, unsigned int dlen)
+{
+       return crypto_rng_alg(tfm)->generate(tfm, src, slen, dst, dlen);
 }
 
 /**
@@ -108,7 +157,7 @@ static inline void crypto_free_rng(struct crypto_rng *tfm)
 static inline int crypto_rng_get_bytes(struct crypto_rng *tfm,
                                       u8 *rdata, unsigned int dlen)
 {
-       return crypto_rng_crt(tfm)->rng_gen_random(tfm, rdata, dlen);
+       return crypto_rng_generate(tfm, NULL, 0, rdata, dlen);
 }
 
 /**
@@ -128,11 +177,8 @@ static inline int crypto_rng_get_bytes(struct crypto_rng *tfm,
  *
  * Return: 0 if the setting of the key was successful; < 0 if an error occurred
  */
-static inline int crypto_rng_reset(struct crypto_rng *tfm,
-                                  u8 *seed, unsigned int slen)
-{
-       return crypto_rng_crt(tfm)->rng_reset(tfm, seed, slen);
-}
+int crypto_rng_reset(struct crypto_rng *tfm, const u8 *seed,
+                    unsigned int slen);
 
 /**
  * crypto_rng_seedsize() - obtain seed size of RNG
index 20e4226a2e14e68e3a5e6fc6541993ff228919d7..96670e7e7c145f741b104afccae780b95b22a41d 100644 (file)
@@ -102,4 +102,8 @@ void scatterwalk_map_and_copy(void *buf, struct scatterlist *sg,
 
 int scatterwalk_bytes_sglen(struct scatterlist *sg, int num_bytes);
 
+struct scatterlist *scatterwalk_ffwd(struct scatterlist dst[2],
+                                    struct scatterlist *src,
+                                    unsigned int len);
+
 #endif  /* _CRYPTO_SCATTERWALK_H */
index c7af7c7ef793240dc55a3b4aa0182d41a93f328d..555609910acb386a6662051969918f8d3fa06278 100644 (file)
 #define ARIZONA_INMODE_SE   1
 #define ARIZONA_INMODE_DMIC 2
 
+#define ARIZONA_MICD_TIME_CONTINUOUS                 0
+#define ARIZONA_MICD_TIME_250US                      1
+#define ARIZONA_MICD_TIME_500US                      2
+#define ARIZONA_MICD_TIME_1MS                        3
+#define ARIZONA_MICD_TIME_2MS                        4
+#define ARIZONA_MICD_TIME_4MS                        5
+#define ARIZONA_MICD_TIME_8MS                        6
+#define ARIZONA_MICD_TIME_16MS                       7
+#define ARIZONA_MICD_TIME_32MS                       8
+#define ARIZONA_MICD_TIME_64MS                       9
+#define ARIZONA_MICD_TIME_128MS                      10
+#define ARIZONA_MICD_TIME_256MS                      11
+#define ARIZONA_MICD_TIME_512MS                      12
+
 #endif
diff --git a/include/dt-bindings/mfd/st-lpc.h b/include/dt-bindings/mfd/st-lpc.h
new file mode 100644 (file)
index 0000000..e3e6c75
--- /dev/null
@@ -0,0 +1,15 @@
+/*
+ * This header provides shared DT/Driver defines for ST's LPC device
+ *
+ * Copyright (C) 2014 STMicroelectronics -- All Rights Reserved
+ *
+ * Author: Lee Jones <lee.jones@linaro.org> for STMicroelectronics
+ */
+
+#ifndef __DT_BINDINGS_ST_LPC_H__
+#define __DT_BINDINGS_ST_LPC_H__
+
+#define ST_LPC_MODE_RTC                0
+#define ST_LPC_MODE_WDT                1
+
+#endif /* __DT_BINDINGS_ST_LPC_H__ */
index e4da5e35e29cd8ee66f443ddd070276ea268e248..c187817471fb58fe7fdd59431d57d1911a4b2d5a 100644 (file)
@@ -158,6 +158,16 @@ typedef u32 phys_cpuid_t;
 #define PHYS_CPUID_INVALID (phys_cpuid_t)(-1)
 #endif
 
+static inline bool invalid_logical_cpuid(u32 cpuid)
+{
+       return (int)cpuid < 0;
+}
+
+static inline bool invalid_phys_cpuid(phys_cpuid_t phys_id)
+{
+       return phys_id == PHYS_CPUID_INVALID;
+}
+
 #ifdef CONFIG_ACPI_HOTPLUG_CPU
 /* Arch dependent functions for cpu hotplug support */
 int acpi_map_cpu(acpi_handle handle, phys_cpuid_t physid, int *pcpu);
@@ -243,50 +253,12 @@ extern bool wmi_has_guid(const char *guid);
 #define ACPI_VIDEO_OUTPUT_SWITCHING_DMI_VENDOR         0x0400
 #define ACPI_VIDEO_OUTPUT_SWITCHING_DMI_VIDEO          0x0800
 
-#if defined(CONFIG_ACPI_VIDEO) || defined(CONFIG_ACPI_VIDEO_MODULE)
-
-extern long acpi_video_get_capabilities(acpi_handle graphics_dev_handle);
+extern char acpi_video_backlight_string[];
 extern long acpi_is_video_device(acpi_handle handle);
-extern void acpi_video_dmi_promote_vendor(void);
-extern void acpi_video_dmi_demote_vendor(void);
-extern int acpi_video_backlight_support(void);
-extern int acpi_video_display_switch_support(void);
-
-#else
-
-static inline long acpi_video_get_capabilities(acpi_handle graphics_dev_handle)
-{
-       return 0;
-}
-
-static inline long acpi_is_video_device(acpi_handle handle)
-{
-       return 0;
-}
-
-static inline void acpi_video_dmi_promote_vendor(void)
-{
-}
-
-static inline void acpi_video_dmi_demote_vendor(void)
-{
-}
-
-static inline int acpi_video_backlight_support(void)
-{
-       return 0;
-}
-
-static inline int acpi_video_display_switch_support(void)
-{
-       return 0;
-}
-
-#endif /* defined(CONFIG_ACPI_VIDEO) || defined(CONFIG_ACPI_VIDEO_MODULE) */
-
 extern int acpi_blacklisted(void);
 extern void acpi_dmi_osi_linux(int enable, const struct dmi_system_id *d);
 extern void acpi_osi_setup(char *str);
+extern bool acpi_osi_is_win8(void);
 
 #ifdef CONFIG_ACPI_NUMA
 int acpi_get_node(acpi_handle handle);
@@ -332,6 +304,9 @@ int acpi_check_region(resource_size_t start, resource_size_t n,
 
 int acpi_resources_are_enforced(void);
 
+int acpi_reserve_region(u64 start, unsigned int length, u8 space_id,
+                       unsigned long flags, char *desc);
+
 #ifdef CONFIG_HIBERNATION
 void __init acpi_no_s4_hw_signature(void);
 #endif
@@ -440,6 +415,7 @@ extern acpi_status acpi_pci_osc_control_set(acpi_handle handle,
 #define ACPI_OST_SC_INSERT_NOT_SUPPORTED       0x82
 
 extern void acpi_early_init(void);
+extern void acpi_subsystem_init(void);
 
 extern int acpi_nvs_register(__u64 start, __u64 size);
 
@@ -494,6 +470,7 @@ static inline const char *acpi_dev_name(struct acpi_device *adev)
 }
 
 static inline void acpi_early_init(void) { }
+static inline void acpi_subsystem_init(void) { }
 
 static inline int early_acpi_boot_init(void)
 {
@@ -525,6 +502,13 @@ static inline int acpi_check_region(resource_size_t start, resource_size_t n,
        return 0;
 }
 
+static inline int acpi_reserve_region(u64 start, unsigned int length,
+                                     u8 space_id, unsigned long flags,
+                                     char *desc)
+{
+       return -ENXIO;
+}
+
 struct acpi_table_header;
 static inline int acpi_table_parse(char *id,
                                int (*handler)(struct acpi_table_header *))
@@ -569,6 +553,11 @@ static inline int acpi_device_modalias(struct device *dev,
        return -ENODEV;
 }
 
+static inline bool acpi_check_dma(struct acpi_device *adev, bool *coherent)
+{
+       return false;
+}
+
 #define ACPI_PTR(_ptr) (NULL)
 
 #endif /* !CONFIG_ACPI */
@@ -721,6 +710,8 @@ static inline void acpi_dev_remove_driver_gpios(struct acpi_device *adev)
        if (adev)
                adev->driver_gpios = NULL;
 }
+
+int acpi_dev_gpio_irq_get(struct acpi_device *adev, int index);
 #else
 static inline int acpi_dev_add_driver_gpios(struct acpi_device *adev,
                              const struct acpi_gpio_mapping *gpios)
@@ -728,6 +719,11 @@ static inline int acpi_dev_add_driver_gpios(struct acpi_device *adev,
        return -ENXIO;
 }
 static inline void acpi_dev_remove_driver_gpios(struct acpi_device *adev) {}
+
+static inline int acpi_dev_gpio_irq_get(struct acpi_device *adev, int index)
+{
+       return -ENXIO;
+}
 #endif
 
 /* Device properties */
index a899402a5a0e6325c0b3dce2f03c7dca06846238..52f3b7da4f2d4dbb181aa1fe5c8d73e4df8f1814 100644 (file)
@@ -43,8 +43,8 @@ struct alarm {
 
 void alarm_init(struct alarm *alarm, enum alarmtimer_type type,
                enum alarmtimer_restart (*function)(struct alarm *, ktime_t));
-int alarm_start(struct alarm *alarm, ktime_t start);
-int alarm_start_relative(struct alarm *alarm, ktime_t start);
+void alarm_start(struct alarm *alarm, ktime_t start);
+void alarm_start_relative(struct alarm *alarm, ktime_t start);
 void alarm_restart(struct alarm *alarm);
 int alarm_try_to_cancel(struct alarm *alarm);
 int alarm_cancel(struct alarm *alarm);
index aff923ae8c4b963272563759b9ac52ad55778bd0..d87d8eced06407c59c6d231f9e707bdcc398ce52 100644 (file)
@@ -116,7 +116,6 @@ __printf(3, 4)
 int bdi_register(struct backing_dev_info *bdi, struct device *parent,
                const char *fmt, ...);
 int bdi_register_dev(struct backing_dev_info *bdi, dev_t dev);
-void bdi_unregister(struct backing_dev_info *bdi);
 int __must_check bdi_setup_and_register(struct backing_dev_info *, char *);
 void bdi_start_writeback(struct backing_dev_info *bdi, long nr_pages,
                        enum wb_reason reason);
index 0e97856b2cffafd8dc10218c95d2eae64169c17a..14eea946e6401cde3880cafc4e7c11e1264d918a 100644 (file)
@@ -74,5 +74,6 @@ int bgpio_init(struct bgpio_chip *bgc, struct device *dev,
 #define BGPIOF_UNREADABLE_REG_SET      BIT(1) /* reg_set is unreadable */
 #define BGPIOF_UNREADABLE_REG_DIR      BIT(2) /* reg_dir is unreadable */
 #define BGPIOF_BIG_ENDIAN_BYTE_ORDER   BIT(3)
+#define BGPIOF_READ_OUTPUT_REG_SET     BIT(4) /* reg_set stores output value */
 
 #endif /* __BASIC_MMIO_GPIO_H */
index 68c16a6bedb36462c3cec290c9eee81abe2072f9..0df4a51e1a78d2bd74ac899c655bce9659ea50da 100644 (file)
@@ -306,6 +306,20 @@ void devm_clk_put(struct device *dev, struct clk *clk);
  * @clk: clock source
  * @rate: desired clock rate in Hz
  *
+ * This answers the question "if I were to pass @rate to clk_set_rate(),
+ * what clock rate would I end up with?" without changing the hardware
+ * in any way.  In other words:
+ *
+ *   rate = clk_round_rate(clk, r);
+ *
+ * and:
+ *
+ *   clk_set_rate(clk, r);
+ *   rate = clk_get_rate(clk);
+ *
+ * are equivalent except the former does not modify the clock hardware
+ * in any way.
+ *
  * Returns rounded clock rate in Hz, or negative errno.
  */
 long clk_round_rate(struct clk *clk, unsigned long rate);
@@ -471,19 +485,6 @@ static inline void clk_disable_unprepare(struct clk *clk)
        clk_unprepare(clk);
 }
 
-/**
- * clk_add_alias - add a new clock alias
- * @alias: name for clock alias
- * @alias_dev_name: device name
- * @id: platform specific clock name
- * @dev: device
- *
- * Allows using generic clock names for drivers by adding a new alias.
- * Assumes clkdev, see clkdev.h for more info.
- */
-int clk_add_alias(const char *alias, const char *alias_dev_name, char *id,
-                       struct device *dev);
-
 struct device_node;
 struct of_phandle_args;
 
index 94bad77eeb4a19d4fbfeef6461a103f5b7e9dbcc..a240b18e86fa4e6cfcc1aaea2b00d4d899c44bdf 100644 (file)
@@ -22,6 +22,7 @@ struct clk_lookup {
        const char              *dev_id;
        const char              *con_id;
        struct clk              *clk;
+       struct clk_hw           *clk_hw;
 };
 
 #define CLKDEV_INIT(d, n, c)   \
@@ -37,8 +38,11 @@ struct clk_lookup *clkdev_alloc(struct clk *clk, const char *con_id,
 void clkdev_add(struct clk_lookup *cl);
 void clkdev_drop(struct clk_lookup *cl);
 
+struct clk_lookup *clkdev_create(struct clk *clk, const char *con_id,
+       const char *dev_fmt, ...);
+
 void clkdev_add_table(struct clk_lookup *, size_t);
-int clk_add_alias(const char *, const char *, char *, struct device *);
+int clk_add_alias(const char *, const char *, const char *, struct device *);
 
 int clk_register_clkdev(struct clk *, const char *, const char *, ...);
 int clk_register_clkdevs(struct clk *, struct clk_lookup *, size_t);
index 96c280b2c263476c053bdd0c514aa16df3d04212..597a1e836f223762da499dc3e1ff3689610a9deb 100644 (file)
@@ -37,12 +37,15 @@ enum clock_event_mode {
  *             reached from DETACHED or SHUTDOWN.
  * ONESHOT:    Device is programmed to generate event only once. Can be reached
  *             from DETACHED or SHUTDOWN.
+ * ONESHOT_STOPPED: Device was programmed in ONESHOT mode and is temporarily
+ *                 stopped.
  */
 enum clock_event_state {
        CLOCK_EVT_STATE_DETACHED,
        CLOCK_EVT_STATE_SHUTDOWN,
        CLOCK_EVT_STATE_PERIODIC,
        CLOCK_EVT_STATE_ONESHOT,
+       CLOCK_EVT_STATE_ONESHOT_STOPPED,
 };
 
 /*
@@ -84,12 +87,13 @@ enum clock_event_state {
  * @mult:              nanosecond to cycles multiplier
  * @shift:             nanoseconds to cycles divisor (power of two)
  * @mode:              operating mode, relevant only to ->set_mode(), OBSOLETE
- * @state:             current state of the device, assigned by the core code
+ * @state_use_accessors:current state of the device, assigned by the core code
  * @features:          features
  * @retries:           number of forced programming retries
  * @set_mode:          legacy set mode function, only for modes <= CLOCK_EVT_MODE_RESUME.
  * @set_state_periodic:        switch state to periodic, if !set_mode
  * @set_state_oneshot: switch state to oneshot, if !set_mode
+ * @set_state_oneshot_stopped: switch state to oneshot_stopped, if !set_mode
  * @set_state_shutdown:        switch state to shutdown, if !set_mode
  * @tick_resume:       resume clkevt device, if !set_mode
  * @broadcast:         function to broadcast events
@@ -113,7 +117,7 @@ struct clock_event_device {
        u32                     mult;
        u32                     shift;
        enum clock_event_mode   mode;
-       enum clock_event_state  state;
+       enum clock_event_state  state_use_accessors;
        unsigned int            features;
        unsigned long           retries;
 
@@ -121,11 +125,12 @@ struct clock_event_device {
         * State transition callback(s): Only one of the two groups should be
         * defined:
         * - set_mode(), only for modes <= CLOCK_EVT_MODE_RESUME.
-        * - set_state_{shutdown|periodic|oneshot}(), tick_resume().
+        * - set_state_{shutdown|periodic|oneshot|oneshot_stopped}(), tick_resume().
         */
        void                    (*set_mode)(enum clock_event_mode mode, struct clock_event_device *);
        int                     (*set_state_periodic)(struct clock_event_device *);
        int                     (*set_state_oneshot)(struct clock_event_device *);
+       int                     (*set_state_oneshot_stopped)(struct clock_event_device *);
        int                     (*set_state_shutdown)(struct clock_event_device *);
        int                     (*tick_resume)(struct clock_event_device *);
 
@@ -144,6 +149,32 @@ struct clock_event_device {
        struct module           *owner;
 } ____cacheline_aligned;
 
+/* Helpers to verify state of a clockevent device */
+static inline bool clockevent_state_detached(struct clock_event_device *dev)
+{
+       return dev->state_use_accessors == CLOCK_EVT_STATE_DETACHED;
+}
+
+static inline bool clockevent_state_shutdown(struct clock_event_device *dev)
+{
+       return dev->state_use_accessors == CLOCK_EVT_STATE_SHUTDOWN;
+}
+
+static inline bool clockevent_state_periodic(struct clock_event_device *dev)
+{
+       return dev->state_use_accessors == CLOCK_EVT_STATE_PERIODIC;
+}
+
+static inline bool clockevent_state_oneshot(struct clock_event_device *dev)
+{
+       return dev->state_use_accessors == CLOCK_EVT_STATE_ONESHOT;
+}
+
+static inline bool clockevent_state_oneshot_stopped(struct clock_event_device *dev)
+{
+       return dev->state_use_accessors == CLOCK_EVT_STATE_ONESHOT_STOPPED;
+}
+
 /*
  * Calculate a multiplication factor for scaled math, which is used to convert
  * nanoseconds based values to clock ticks:
index d27d0152271f9e8b487a48a9f2d74f51fe9a58a5..278dd279a7a8035e8be073a9664ea88f7357984a 100644 (file)
@@ -181,7 +181,6 @@ static inline s64 clocksource_cyc2ns(cycle_t cycles, u32 mult, u32 shift)
 
 extern int clocksource_unregister(struct clocksource*);
 extern void clocksource_touch_watchdog(void);
-extern struct clocksource* clocksource_get_next(void);
 extern void clocksource_change_rating(struct clocksource *cs, int rating);
 extern void clocksource_suspend(void);
 extern void clocksource_resume(void);
index 867722591be2c7e026e1b97c241e65e27e3b9d1b..05be2352fef889663fad482f57c4d8b9d5e18df4 100644 (file)
@@ -250,7 +250,23 @@ static __always_inline void __write_once_size(volatile void *p, void *res, int s
        ({ union { typeof(x) __val; char __c[1]; } __u; __read_once_size(&(x), __u.__c, sizeof(x)); __u.__val; })
 
 #define WRITE_ONCE(x, val) \
-       ({ typeof(x) __val = (val); __write_once_size(&(x), &__val, sizeof(__val)); __val; })
+       ({ union { typeof(x) __val; char __c[1]; } __u = { .__val = (val) }; __write_once_size(&(x), __u.__c, sizeof(x)); __u.__val; })
+
+/**
+ * READ_ONCE_CTRL - Read a value heading a control dependency
+ * @x: The value to be read, heading the control dependency
+ *
+ * Control dependencies are tricky.  See Documentation/memory-barriers.txt
+ * for important information on how to use them.  Note that in many cases,
+ * use of smp_load_acquire() will be much simpler.  Control dependencies
+ * should be avoided except on the hottest of hotpaths.
+ */
+#define READ_ONCE_CTRL(x) \
+({ \
+       typeof(x) __val = READ_ONCE(x); \
+       smp_read_barrier_depends(); /* Enforce control dependency. */ \
+       __val; \
+})
 
 #endif /* __KERNEL__ */
 
@@ -450,7 +466,7 @@ static __always_inline void __write_once_size(volatile void *p, void *res, int s
  * with an explicit memory barrier or atomic instruction that provides the
  * required ordering.
  *
- * If possible use READ_ONCE/ASSIGN_ONCE instead.
+ * If possible use READ_ONCE()/WRITE_ONCE() instead.
  */
 #define __ACCESS_ONCE(x) ({ \
         __maybe_unused typeof(x) __var = (__force typeof(x)) 0; \
index 2821838256b4f728640819f9f81842954261ec03..b96bd299966f04b374e0b1e17dbc793e3efa784d 100644 (file)
@@ -14,8 +14,6 @@ extern void context_tracking_enter(enum ctx_state state);
 extern void context_tracking_exit(enum ctx_state state);
 extern void context_tracking_user_enter(void);
 extern void context_tracking_user_exit(void);
-extern void __context_tracking_task_switch(struct task_struct *prev,
-                                          struct task_struct *next);
 
 static inline void user_enter(void)
 {
@@ -51,19 +49,11 @@ static inline void exception_exit(enum ctx_state prev_ctx)
        }
 }
 
-static inline void context_tracking_task_switch(struct task_struct *prev,
-                                               struct task_struct *next)
-{
-       if (context_tracking_is_enabled())
-               __context_tracking_task_switch(prev, next);
-}
 #else
 static inline void user_enter(void) { }
 static inline void user_exit(void) { }
 static inline enum ctx_state exception_enter(void) { return 0; }
 static inline void exception_exit(enum ctx_state prev_ctx) { }
-static inline void context_tracking_task_switch(struct task_struct *prev,
-                                               struct task_struct *next) { }
 #endif /* !CONFIG_CONTEXT_TRACKING */
 
 
index 6b7b96a32b753a614ef36ea8bb6c1d5a83641b21..678ecdf90cf6606f89a0747b99aa7134e638daf7 100644 (file)
@@ -12,6 +12,7 @@ struct context_tracking {
         * may be further optimized using static keys.
         */
        bool active;
+       int recursion;
        enum ctx_state {
                CONTEXT_KERNEL = 0,
                CONTEXT_USER,
index 2ee4888c1f47f6cbdcc0c7d49e48cc74f7729e05..29ad97c34fd5cf4bcdeec373d2ad5691bb2ec6c3 100644 (file)
@@ -65,7 +65,9 @@ struct cpufreq_policy {
 
        unsigned int            shared_type; /* ACPI: ANY or ALL affected CPUs
                                                should set cpufreq */
-       unsigned int            cpu;    /* cpu nr of CPU managing this policy */
+       unsigned int            cpu;    /* cpu managing this policy, must be online */
+       unsigned int            kobj_cpu; /* cpu managing sysfs files, can be offline */
+
        struct clk              *clk;
        struct cpufreq_cpuinfo  cpuinfo;/* see above */
 
@@ -80,6 +82,7 @@ struct cpufreq_policy {
        struct cpufreq_governor *governor; /* see below */
        void                    *governor_data;
        bool                    governor_enabled; /* governor start/stop flag */
+       char                    last_governor[CPUFREQ_NAME_LEN]; /* last governor used */
 
        struct work_struct      update; /* if update_policy() needs to be
                                         * called, but you're in IRQ context */
index 9c5e892547961544eae22a53669ba600e5ad4973..d075d34279df3dbde3fadb6317ae403e9955f414 100644 (file)
@@ -151,10 +151,6 @@ extern void cpuidle_resume(void);
 extern int cpuidle_enable_device(struct cpuidle_device *dev);
 extern void cpuidle_disable_device(struct cpuidle_device *dev);
 extern int cpuidle_play_dead(void);
-extern int cpuidle_find_deepest_state(struct cpuidle_driver *drv,
-                                     struct cpuidle_device *dev);
-extern int cpuidle_enter_freeze(struct cpuidle_driver *drv,
-                               struct cpuidle_device *dev);
 
 extern struct cpuidle_driver *cpuidle_get_cpu_driver(struct cpuidle_device *dev);
 #else
@@ -190,16 +186,28 @@ static inline int cpuidle_enable_device(struct cpuidle_device *dev)
 {return -ENODEV; }
 static inline void cpuidle_disable_device(struct cpuidle_device *dev) { }
 static inline int cpuidle_play_dead(void) {return -ENODEV; }
+static inline struct cpuidle_driver *cpuidle_get_cpu_driver(
+       struct cpuidle_device *dev) {return NULL; }
+#endif
+
+#if defined(CONFIG_CPU_IDLE) && defined(CONFIG_SUSPEND)
+extern int cpuidle_find_deepest_state(struct cpuidle_driver *drv,
+                                     struct cpuidle_device *dev);
+extern int cpuidle_enter_freeze(struct cpuidle_driver *drv,
+                               struct cpuidle_device *dev);
+#else
 static inline int cpuidle_find_deepest_state(struct cpuidle_driver *drv,
                                             struct cpuidle_device *dev)
 {return -ENODEV; }
 static inline int cpuidle_enter_freeze(struct cpuidle_driver *drv,
                                       struct cpuidle_device *dev)
 {return -ENODEV; }
-static inline struct cpuidle_driver *cpuidle_get_cpu_driver(
-       struct cpuidle_device *dev) {return NULL; }
 #endif
 
+/* kernel/sched/idle.c */
+extern void sched_idle_set_state(struct cpuidle_state *idle_state);
+extern void default_idle_call(void);
+
 #ifdef CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED
 void cpuidle_coupled_parallel_barrier(struct cpuidle_device *dev, atomic_t *a);
 #else
index 84920f3cc83e0d843885117f145c7dcb301f1489..a9953c762eee62d99e382052e021aee3c38176cd 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Implements the standard CRC ITU-T V.41:
  *   Width 16
- *   Poly  0x0x1021 (x^16 + x^12 + x^15 + 1)
+ *   Poly  0x1021 (x^16 + x^12 + x^15 + 1)
  *   Init  0
  *
  * This source code is licensed under the GNU General Public License,
index 10df5d2d093a7ffa14fd02fec144a5073562e194..81ef938b0a8e9d34342ddbf8459a7a8b30b489d1 100644 (file)
@@ -53,6 +53,7 @@
 #define CRYPTO_ALG_TYPE_SHASH          0x00000009
 #define CRYPTO_ALG_TYPE_AHASH          0x0000000a
 #define CRYPTO_ALG_TYPE_RNG            0x0000000c
+#define CRYPTO_ALG_TYPE_AKCIPHER       0x0000000d
 #define CRYPTO_ALG_TYPE_PCOMPRESS      0x0000000f
 
 #define CRYPTO_ALG_TYPE_HASH_MASK      0x0000000e
  */
 #define CRYPTO_ALG_INTERNAL            0x00002000
 
+/*
+ * Temporary flag used to prevent legacy AEAD implementations from
+ * being used by user-space.
+ */
+#define CRYPTO_ALG_AEAD_NEW            0x00004000
+
 /*
  * Transform masks and values (for crt_flags).
  */
@@ -138,9 +145,9 @@ struct crypto_async_request;
 struct crypto_aead;
 struct crypto_blkcipher;
 struct crypto_hash;
-struct crypto_rng;
 struct crypto_tfm;
 struct crypto_type;
+struct aead_request;
 struct aead_givcrypt_request;
 struct skcipher_givcrypt_request;
 
@@ -175,32 +182,6 @@ struct ablkcipher_request {
        void *__ctx[] CRYPTO_MINALIGN_ATTR;
 };
 
-/**
- *     struct aead_request - AEAD request
- *     @base: Common attributes for async crypto requests
- *     @assoclen: Length in bytes of associated data for authentication
- *     @cryptlen: Length of data to be encrypted or decrypted
- *     @iv: Initialisation vector
- *     @assoc: Associated data
- *     @src: Source data
- *     @dst: Destination data
- *     @__ctx: Start of private context data
- */
-struct aead_request {
-       struct crypto_async_request base;
-
-       unsigned int assoclen;
-       unsigned int cryptlen;
-
-       u8 *iv;
-
-       struct scatterlist *assoc;
-       struct scatterlist *src;
-       struct scatterlist *dst;
-
-       void *__ctx[] CRYPTO_MINALIGN_ATTR;
-};
-
 struct blkcipher_desc {
        struct crypto_blkcipher *tfm;
        void *info;
@@ -294,7 +275,7 @@ struct ablkcipher_alg {
 };
 
 /**
- * struct aead_alg - AEAD cipher definition
+ * struct old_aead_alg - AEAD cipher definition
  * @maxauthsize: Set the maximum authentication tag size supported by the
  *              transformation. A transformation may support smaller tag sizes.
  *              As the authentication tag is a message digest to ensure the
@@ -319,7 +300,7 @@ struct ablkcipher_alg {
  * All fields except @givencrypt , @givdecrypt , @geniv and @ivsize are
  * mandatory and must be filled.
  */
-struct aead_alg {
+struct old_aead_alg {
        int (*setkey)(struct crypto_aead *tfm, const u8 *key,
                      unsigned int keylen);
        int (*setauthsize)(struct crypto_aead *tfm, unsigned int authsize);
@@ -426,40 +407,12 @@ struct compress_alg {
                              unsigned int slen, u8 *dst, unsigned int *dlen);
 };
 
-/**
- * struct rng_alg - random number generator definition
- * @rng_make_random: The function defined by this variable obtains a random
- *                  number. The random number generator transform must generate
- *                  the random number out of the context provided with this
- *                  call.
- * @rng_reset: Reset of the random number generator by clearing the entire state.
- *            With the invocation of this function call, the random number
- *             generator shall completely reinitialize its state. If the random
- *            number generator requires a seed for setting up a new state,
- *            the seed must be provided by the consumer while invoking this
- *            function. The required size of the seed is defined with
- *            @seedsize .
- * @seedsize: The seed size required for a random number generator
- *           initialization defined with this variable. Some random number
- *           generators like the SP800-90A DRBG does not require a seed as the
- *           seeding is implemented internally without the need of support by
- *           the consumer. In this case, the seed size is set to zero.
- */
-struct rng_alg {
-       int (*rng_make_random)(struct crypto_rng *tfm, u8 *rdata,
-                              unsigned int dlen);
-       int (*rng_reset)(struct crypto_rng *tfm, u8 *seed, unsigned int slen);
-
-       unsigned int seedsize;
-};
-
 
 #define cra_ablkcipher cra_u.ablkcipher
 #define cra_aead       cra_u.aead
 #define cra_blkcipher  cra_u.blkcipher
 #define cra_cipher     cra_u.cipher
 #define cra_compress   cra_u.compress
-#define cra_rng                cra_u.rng
 
 /**
  * struct crypto_alg - definition of a cryptograpic cipher algorithm
@@ -505,7 +458,7 @@ struct rng_alg {
  *                  transformation algorithm.
  * @cra_type: Type of the cryptographic transformation. This is a pointer to
  *           struct crypto_type, which implements callbacks common for all
- *           trasnformation types. There are multiple options:
+ *           transformation types. There are multiple options:
  *           &crypto_blkcipher_type, &crypto_ablkcipher_type,
  *           &crypto_ahash_type, &crypto_aead_type, &crypto_rng_type.
  *           This field might be empty. In that case, there are no common
@@ -555,11 +508,10 @@ struct crypto_alg {
 
        union {
                struct ablkcipher_alg ablkcipher;
-               struct aead_alg aead;
+               struct old_aead_alg aead;
                struct blkcipher_alg blkcipher;
                struct cipher_alg cipher;
                struct compress_alg compress;
-               struct rng_alg rng;
        } cra_u;
 
        int (*cra_init)(struct crypto_tfm *tfm);
@@ -567,7 +519,7 @@ struct crypto_alg {
        void (*cra_destroy)(struct crypto_alg *alg);
        
        struct module *cra_module;
-};
+} CRYPTO_MINALIGN_ATTR;
 
 /*
  * Algorithm registration interface.
@@ -602,21 +554,6 @@ struct ablkcipher_tfm {
        unsigned int reqsize;
 };
 
-struct aead_tfm {
-       int (*setkey)(struct crypto_aead *tfm, const u8 *key,
-                     unsigned int keylen);
-       int (*encrypt)(struct aead_request *req);
-       int (*decrypt)(struct aead_request *req);
-       int (*givencrypt)(struct aead_givcrypt_request *req);
-       int (*givdecrypt)(struct aead_givcrypt_request *req);
-
-       struct crypto_aead *base;
-
-       unsigned int ivsize;
-       unsigned int authsize;
-       unsigned int reqsize;
-};
-
 struct blkcipher_tfm {
        void *iv;
        int (*setkey)(struct crypto_tfm *tfm, const u8 *key,
@@ -655,19 +592,11 @@ struct compress_tfm {
                              u8 *dst, unsigned int *dlen);
 };
 
-struct rng_tfm {
-       int (*rng_gen_random)(struct crypto_rng *tfm, u8 *rdata,
-                             unsigned int dlen);
-       int (*rng_reset)(struct crypto_rng *tfm, u8 *seed, unsigned int slen);
-};
-
 #define crt_ablkcipher crt_u.ablkcipher
-#define crt_aead       crt_u.aead
 #define crt_blkcipher  crt_u.blkcipher
 #define crt_cipher     crt_u.cipher
 #define crt_hash       crt_u.hash
 #define crt_compress   crt_u.compress
-#define crt_rng                crt_u.rng
 
 struct crypto_tfm {
 
@@ -675,12 +604,10 @@ struct crypto_tfm {
        
        union {
                struct ablkcipher_tfm ablkcipher;
-               struct aead_tfm aead;
                struct blkcipher_tfm blkcipher;
                struct cipher_tfm cipher;
                struct hash_tfm hash;
                struct compress_tfm compress;
-               struct rng_tfm rng;
        } crt_u;
 
        void (*exit)(struct crypto_tfm *tfm);
@@ -694,10 +621,6 @@ struct crypto_ablkcipher {
        struct crypto_tfm base;
 };
 
-struct crypto_aead {
-       struct crypto_tfm base;
-};
-
 struct crypto_blkcipher {
        struct crypto_tfm base;
 };
@@ -714,10 +637,6 @@ struct crypto_hash {
        struct crypto_tfm base;
 };
 
-struct crypto_rng {
-       struct crypto_tfm base;
-};
-
 enum {
        CRYPTOA_UNSPEC,
        CRYPTOA_ALG,
@@ -1193,400 +1112,6 @@ static inline void ablkcipher_request_set_crypt(
        req->info = iv;
 }
 
-/**
- * DOC: Authenticated Encryption With Associated Data (AEAD) Cipher API
- *
- * The AEAD cipher API is used with the ciphers of type CRYPTO_ALG_TYPE_AEAD
- * (listed as type "aead" in /proc/crypto)
- *
- * The most prominent examples for this type of encryption is GCM and CCM.
- * However, the kernel supports other types of AEAD ciphers which are defined
- * with the following cipher string:
- *
- *     authenc(keyed message digest, block cipher)
- *
- * For example: authenc(hmac(sha256), cbc(aes))
- *
- * The example code provided for the asynchronous block cipher operation
- * applies here as well. Naturally all *ablkcipher* symbols must be exchanged
- * the *aead* pendants discussed in the following. In addtion, for the AEAD
- * operation, the aead_request_set_assoc function must be used to set the
- * pointer to the associated data memory location before performing the
- * encryption or decryption operation. In case of an encryption, the associated
- * data memory is filled during the encryption operation. For decryption, the
- * associated data memory must contain data that is used to verify the integrity
- * of the decrypted data. Another deviation from the asynchronous block cipher
- * operation is that the caller should explicitly check for -EBADMSG of the
- * crypto_aead_decrypt. That error indicates an authentication error, i.e.
- * a breach in the integrity of the message. In essence, that -EBADMSG error
- * code is the key bonus an AEAD cipher has over "standard" block chaining
- * modes.
- */
-
-static inline struct crypto_aead *__crypto_aead_cast(struct crypto_tfm *tfm)
-{
-       return (struct crypto_aead *)tfm;
-}
-
-/**
- * crypto_alloc_aead() - allocate AEAD cipher handle
- * @alg_name: is the cra_name / name or cra_driver_name / driver name of the
- *          AEAD cipher
- * @type: specifies the type of the cipher
- * @mask: specifies the mask for the cipher
- *
- * Allocate a cipher handle for an AEAD. The returned struct
- * crypto_aead is the cipher handle that is required for any subsequent
- * API invocation for that AEAD.
- *
- * Return: allocated cipher handle in case of success; IS_ERR() is true in case
- *        of an error, PTR_ERR() returns the error code.
- */
-struct crypto_aead *crypto_alloc_aead(const char *alg_name, u32 type, u32 mask);
-
-static inline struct crypto_tfm *crypto_aead_tfm(struct crypto_aead *tfm)
-{
-       return &tfm->base;
-}
-
-/**
- * crypto_free_aead() - zeroize and free aead handle
- * @tfm: cipher handle to be freed
- */
-static inline void crypto_free_aead(struct crypto_aead *tfm)
-{
-       crypto_free_tfm(crypto_aead_tfm(tfm));
-}
-
-static inline struct aead_tfm *crypto_aead_crt(struct crypto_aead *tfm)
-{
-       return &crypto_aead_tfm(tfm)->crt_aead;
-}
-
-/**
- * crypto_aead_ivsize() - obtain IV size
- * @tfm: cipher handle
- *
- * The size of the IV for the aead referenced by the cipher handle is
- * returned. This IV size may be zero if the cipher does not need an IV.
- *
- * Return: IV size in bytes
- */
-static inline unsigned int crypto_aead_ivsize(struct crypto_aead *tfm)
-{
-       return crypto_aead_crt(tfm)->ivsize;
-}
-
-/**
- * crypto_aead_authsize() - obtain maximum authentication data size
- * @tfm: cipher handle
- *
- * The maximum size of the authentication data for the AEAD cipher referenced
- * by the AEAD cipher handle is returned. The authentication data size may be
- * zero if the cipher implements a hard-coded maximum.
- *
- * The authentication data may also be known as "tag value".
- *
- * Return: authentication data size / tag size in bytes
- */
-static inline unsigned int crypto_aead_authsize(struct crypto_aead *tfm)
-{
-       return crypto_aead_crt(tfm)->authsize;
-}
-
-/**
- * crypto_aead_blocksize() - obtain block size of cipher
- * @tfm: cipher handle
- *
- * The block size for the AEAD referenced with the cipher handle is returned.
- * The caller may use that information to allocate appropriate memory for the
- * data returned by the encryption or decryption operation
- *
- * Return: block size of cipher
- */
-static inline unsigned int crypto_aead_blocksize(struct crypto_aead *tfm)
-{
-       return crypto_tfm_alg_blocksize(crypto_aead_tfm(tfm));
-}
-
-static inline unsigned int crypto_aead_alignmask(struct crypto_aead *tfm)
-{
-       return crypto_tfm_alg_alignmask(crypto_aead_tfm(tfm));
-}
-
-static inline u32 crypto_aead_get_flags(struct crypto_aead *tfm)
-{
-       return crypto_tfm_get_flags(crypto_aead_tfm(tfm));
-}
-
-static inline void crypto_aead_set_flags(struct crypto_aead *tfm, u32 flags)
-{
-       crypto_tfm_set_flags(crypto_aead_tfm(tfm), flags);
-}
-
-static inline void crypto_aead_clear_flags(struct crypto_aead *tfm, u32 flags)
-{
-       crypto_tfm_clear_flags(crypto_aead_tfm(tfm), flags);
-}
-
-/**
- * crypto_aead_setkey() - set key for cipher
- * @tfm: cipher handle
- * @key: buffer holding the key
- * @keylen: length of the key in bytes
- *
- * The caller provided key is set for the AEAD referenced by the cipher
- * handle.
- *
- * Note, the key length determines the cipher type. Many block ciphers implement
- * different cipher modes depending on the key size, such as AES-128 vs AES-192
- * vs. AES-256. When providing a 16 byte key for an AES cipher handle, AES-128
- * is performed.
- *
- * Return: 0 if the setting of the key was successful; < 0 if an error occurred
- */
-static inline int crypto_aead_setkey(struct crypto_aead *tfm, const u8 *key,
-                                    unsigned int keylen)
-{
-       struct aead_tfm *crt = crypto_aead_crt(tfm);
-
-       return crt->setkey(crt->base, key, keylen);
-}
-
-/**
- * crypto_aead_setauthsize() - set authentication data size
- * @tfm: cipher handle
- * @authsize: size of the authentication data / tag in bytes
- *
- * Set the authentication data size / tag size. AEAD requires an authentication
- * tag (or MAC) in addition to the associated data.
- *
- * Return: 0 if the setting of the key was successful; < 0 if an error occurred
- */
-int crypto_aead_setauthsize(struct crypto_aead *tfm, unsigned int authsize);
-
-static inline struct crypto_aead *crypto_aead_reqtfm(struct aead_request *req)
-{
-       return __crypto_aead_cast(req->base.tfm);
-}
-
-/**
- * crypto_aead_encrypt() - encrypt plaintext
- * @req: reference to the aead_request handle that holds all information
- *      needed to perform the cipher operation
- *
- * Encrypt plaintext data using the aead_request handle. That data structure
- * and how it is filled with data is discussed with the aead_request_*
- * functions.
- *
- * IMPORTANT NOTE The encryption operation creates the authentication data /
- *               tag. That data is concatenated with the created ciphertext.
- *               The ciphertext memory size is therefore the given number of
- *               block cipher blocks + the size defined by the
- *               crypto_aead_setauthsize invocation. The caller must ensure
- *               that sufficient memory is available for the ciphertext and
- *               the authentication tag.
- *
- * Return: 0 if the cipher operation was successful; < 0 if an error occurred
- */
-static inline int crypto_aead_encrypt(struct aead_request *req)
-{
-       return crypto_aead_crt(crypto_aead_reqtfm(req))->encrypt(req);
-}
-
-/**
- * crypto_aead_decrypt() - decrypt ciphertext
- * @req: reference to the ablkcipher_request handle that holds all information
- *      needed to perform the cipher operation
- *
- * Decrypt ciphertext data using the aead_request handle. That data structure
- * and how it is filled with data is discussed with the aead_request_*
- * functions.
- *
- * IMPORTANT NOTE The caller must concatenate the ciphertext followed by the
- *               authentication data / tag. That authentication data / tag
- *               must have the size defined by the crypto_aead_setauthsize
- *               invocation.
- *
- *
- * Return: 0 if the cipher operation was successful; -EBADMSG: The AEAD
- *        cipher operation performs the authentication of the data during the
- *        decryption operation. Therefore, the function returns this error if
- *        the authentication of the ciphertext was unsuccessful (i.e. the
- *        integrity of the ciphertext or the associated data was violated);
- *        < 0 if an error occurred.
- */
-static inline int crypto_aead_decrypt(struct aead_request *req)
-{
-       if (req->cryptlen < crypto_aead_authsize(crypto_aead_reqtfm(req)))
-               return -EINVAL;
-
-       return crypto_aead_crt(crypto_aead_reqtfm(req))->decrypt(req);
-}
-
-/**
- * DOC: Asynchronous AEAD Request Handle
- *
- * The aead_request data structure contains all pointers to data required for
- * the AEAD cipher operation. This includes the cipher handle (which can be
- * used by multiple aead_request instances), pointer to plaintext and
- * ciphertext, asynchronous callback function, etc. It acts as a handle to the
- * aead_request_* API calls in a similar way as AEAD handle to the
- * crypto_aead_* API calls.
- */
-
-/**
- * crypto_aead_reqsize() - obtain size of the request data structure
- * @tfm: cipher handle
- *
- * Return: number of bytes
- */
-static inline unsigned int crypto_aead_reqsize(struct crypto_aead *tfm)
-{
-       return crypto_aead_crt(tfm)->reqsize;
-}
-
-/**
- * aead_request_set_tfm() - update cipher handle reference in request
- * @req: request handle to be modified
- * @tfm: cipher handle that shall be added to the request handle
- *
- * Allow the caller to replace the existing aead handle in the request
- * data structure with a different one.
- */
-static inline void aead_request_set_tfm(struct aead_request *req,
-                                       struct crypto_aead *tfm)
-{
-       req->base.tfm = crypto_aead_tfm(crypto_aead_crt(tfm)->base);
-}
-
-/**
- * aead_request_alloc() - allocate request data structure
- * @tfm: cipher handle to be registered with the request
- * @gfp: memory allocation flag that is handed to kmalloc by the API call.
- *
- * Allocate the request data structure that must be used with the AEAD
- * encrypt and decrypt API calls. During the allocation, the provided aead
- * handle is registered in the request data structure.
- *
- * Return: allocated request handle in case of success; IS_ERR() is true in case
- *        of an error, PTR_ERR() returns the error code.
- */
-static inline struct aead_request *aead_request_alloc(struct crypto_aead *tfm,
-                                                     gfp_t gfp)
-{
-       struct aead_request *req;
-
-       req = kmalloc(sizeof(*req) + crypto_aead_reqsize(tfm), gfp);
-
-       if (likely(req))
-               aead_request_set_tfm(req, tfm);
-
-       return req;
-}
-
-/**
- * aead_request_free() - zeroize and free request data structure
- * @req: request data structure cipher handle to be freed
- */
-static inline void aead_request_free(struct aead_request *req)
-{
-       kzfree(req);
-}
-
-/**
- * aead_request_set_callback() - set asynchronous callback function
- * @req: request handle
- * @flags: specify zero or an ORing of the flags
- *        CRYPTO_TFM_REQ_MAY_BACKLOG the request queue may back log and
- *        increase the wait queue beyond the initial maximum size;
- *        CRYPTO_TFM_REQ_MAY_SLEEP the request processing may sleep
- * @compl: callback function pointer to be registered with the request handle
- * @data: The data pointer refers to memory that is not used by the kernel
- *       crypto API, but provided to the callback function for it to use. Here,
- *       the caller can provide a reference to memory the callback function can
- *       operate on. As the callback function is invoked asynchronously to the
- *       related functionality, it may need to access data structures of the
- *       related functionality which can be referenced using this pointer. The
- *       callback function can access the memory via the "data" field in the
- *       crypto_async_request data structure provided to the callback function.
- *
- * Setting the callback function that is triggered once the cipher operation
- * completes
- *
- * The callback function is registered with the aead_request handle and
- * must comply with the following template
- *
- *     void callback_function(struct crypto_async_request *req, int error)
- */
-static inline void aead_request_set_callback(struct aead_request *req,
-                                            u32 flags,
-                                            crypto_completion_t compl,
-                                            void *data)
-{
-       req->base.complete = compl;
-       req->base.data = data;
-       req->base.flags = flags;
-}
-
-/**
- * aead_request_set_crypt - set data buffers
- * @req: request handle
- * @src: source scatter / gather list
- * @dst: destination scatter / gather list
- * @cryptlen: number of bytes to process from @src
- * @iv: IV for the cipher operation which must comply with the IV size defined
- *      by crypto_aead_ivsize()
- *
- * Setting the source data and destination data scatter / gather lists.
- *
- * For encryption, the source is treated as the plaintext and the
- * destination is the ciphertext. For a decryption operation, the use is
- * reversed - the source is the ciphertext and the destination is the plaintext.
- *
- * IMPORTANT NOTE AEAD requires an authentication tag (MAC). For decryption,
- *               the caller must concatenate the ciphertext followed by the
- *               authentication tag and provide the entire data stream to the
- *               decryption operation (i.e. the data length used for the
- *               initialization of the scatterlist and the data length for the
- *               decryption operation is identical). For encryption, however,
- *               the authentication tag is created while encrypting the data.
- *               The destination buffer must hold sufficient space for the
- *               ciphertext and the authentication tag while the encryption
- *               invocation must only point to the plaintext data size. The
- *               following code snippet illustrates the memory usage
- *               buffer = kmalloc(ptbuflen + (enc ? authsize : 0));
- *               sg_init_one(&sg, buffer, ptbuflen + (enc ? authsize : 0));
- *               aead_request_set_crypt(req, &sg, &sg, ptbuflen, iv);
- */
-static inline void aead_request_set_crypt(struct aead_request *req,
-                                         struct scatterlist *src,
-                                         struct scatterlist *dst,
-                                         unsigned int cryptlen, u8 *iv)
-{
-       req->src = src;
-       req->dst = dst;
-       req->cryptlen = cryptlen;
-       req->iv = iv;
-}
-
-/**
- * aead_request_set_assoc() - set the associated data scatter / gather list
- * @req: request handle
- * @assoc: associated data scatter / gather list
- * @assoclen: number of bytes to process from @assoc
- *
- * For encryption, the memory is filled with the associated data. For
- * decryption, the memory must point to the associated data.
- */
-static inline void aead_request_set_assoc(struct aead_request *req,
-                                         struct scatterlist *assoc,
-                                         unsigned int assoclen)
-{
-       req->assoc = assoc;
-       req->assoclen = assoclen;
-}
-
 /**
  * DOC: Synchronous Block Cipher API
  *
index cb25af46105406908e5a825ca3812f0ff63d3194..420311bcee38c291cf75894ebfe4c2d1141da1a7 100644 (file)
@@ -45,7 +45,6 @@ extern struct dentry *arch_debugfs_dir;
 
 /* declared over in file.c */
 extern const struct file_operations debugfs_file_operations;
-extern const struct inode_operations debugfs_link_operations;
 
 struct dentry *debugfs_create_file(const char *name, umode_t mode,
                                   struct dentry *parent, void *data,
index 30624954dec5a9250c142d2c57d000b991a91710..e9bc9292bd3a5e8ff8ba6603564ee25a8911c7e4 100644 (file)
@@ -185,33 +185,85 @@ static inline int dmar_device_remove(void *handle)
 
 struct irte {
        union {
+               /* Shared between remapped and posted mode*/
                struct {
-                       __u64   present         : 1,
-                               fpd             : 1,
-                               dst_mode        : 1,
-                               redir_hint      : 1,
-                               trigger_mode    : 1,
-                               dlvry_mode      : 3,
-                               avail           : 4,
-                               __reserved_1    : 4,
-                               vector          : 8,
-                               __reserved_2    : 8,
-                               dest_id         : 32;
+                       __u64   present         : 1,  /*  0      */
+                               fpd             : 1,  /*  1      */
+                               __res0          : 6,  /*  2 -  6 */
+                               avail           : 4,  /*  8 - 11 */
+                               __res1          : 3,  /* 12 - 14 */
+                               pst             : 1,  /* 15      */
+                               vector          : 8,  /* 16 - 23 */
+                               __res2          : 40; /* 24 - 63 */
+               };
+
+               /* Remapped mode */
+               struct {
+                       __u64   r_present       : 1,  /*  0      */
+                               r_fpd           : 1,  /*  1      */
+                               dst_mode        : 1,  /*  2      */
+                               redir_hint      : 1,  /*  3      */
+                               trigger_mode    : 1,  /*  4      */
+                               dlvry_mode      : 3,  /*  5 -  7 */
+                               r_avail         : 4,  /*  8 - 11 */
+                               r_res0          : 4,  /* 12 - 15 */
+                               r_vector        : 8,  /* 16 - 23 */
+                               r_res1          : 8,  /* 24 - 31 */
+                               dest_id         : 32; /* 32 - 63 */
+               };
+
+               /* Posted mode */
+               struct {
+                       __u64   p_present       : 1,  /*  0      */
+                               p_fpd           : 1,  /*  1      */
+                               p_res0          : 6,  /*  2 -  7 */
+                               p_avail         : 4,  /*  8 - 11 */
+                               p_res1          : 2,  /* 12 - 13 */
+                               p_urgent        : 1,  /* 14      */
+                               p_pst           : 1,  /* 15      */
+                               p_vector        : 8,  /* 16 - 23 */
+                               p_res2          : 14, /* 24 - 37 */
+                               pda_l           : 26; /* 38 - 63 */
                };
                __u64 low;
        };
 
        union {
+               /* Shared between remapped and posted mode*/
                struct {
-                       __u64   sid             : 16,
-                               sq              : 2,
-                               svt             : 2,
-                               __reserved_3    : 44;
+                       __u64   sid             : 16,  /* 64 - 79  */
+                               sq              : 2,   /* 80 - 81  */
+                               svt             : 2,   /* 82 - 83  */
+                               __res3          : 44;  /* 84 - 127 */
+               };
+
+               /* Posted mode*/
+               struct {
+                       __u64   p_sid           : 16,  /* 64 - 79  */
+                               p_sq            : 2,   /* 80 - 81  */
+                               p_svt           : 2,   /* 82 - 83  */
+                               p_res3          : 12,  /* 84 - 95  */
+                               pda_h           : 32;  /* 96 - 127 */
                };
                __u64 high;
        };
 };
 
+static inline void dmar_copy_shared_irte(struct irte *dst, struct irte *src)
+{
+       dst->present    = src->present;
+       dst->fpd        = src->fpd;
+       dst->avail      = src->avail;
+       dst->pst        = src->pst;
+       dst->vector     = src->vector;
+       dst->sid        = src->sid;
+       dst->sq         = src->sq;
+       dst->svt        = src->svt;
+}
+
+#define PDA_LOW_BIT    26
+#define PDA_HIGH_BIT   32
+
 enum {
        IRQ_REMAP_XAPIC_MODE,
        IRQ_REMAP_X2APIC_MODE,
@@ -227,6 +279,7 @@ extern void dmar_msi_read(int irq, struct msi_msg *msg);
 extern void dmar_msi_write(int irq, struct msi_msg *msg);
 extern int dmar_set_interrupt(struct intel_iommu *iommu);
 extern irqreturn_t dmar_fault(int irq, void *dev_id);
-extern int arch_setup_dmar_msi(unsigned int irq);
+extern int dmar_alloc_hwirq(int id, int node, void *arg);
+extern void dmar_free_hwirq(int irq);
 
 #endif /* __DMAR_H__ */
index af5be0368dec26c934565e634c0dc803958ed2cc..2092965afca3994606ee8a255a97929a38df8095 100644 (file)
@@ -583,6 +583,9 @@ void efi_native_runtime_setup(void);
 #define EFI_FILE_INFO_ID \
     EFI_GUID(  0x9576e92, 0x6d3f, 0x11d2, 0x8e, 0x39, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b )
 
+#define EFI_SYSTEM_RESOURCE_TABLE_GUID \
+    EFI_GUID(  0xb122a263, 0x3661, 0x4f68, 0x99, 0x29, 0x78, 0xf8, 0xb0, 0xd6, 0x21, 0x80 )
+
 #define EFI_FILE_SYSTEM_GUID \
     EFI_GUID(  0x964e5b22, 0x6459, 0x11d2, 0x8e, 0x39, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b )
 
@@ -823,6 +826,7 @@ extern struct efi {
        unsigned long fw_vendor;        /* fw_vendor */
        unsigned long runtime;          /* runtime table */
        unsigned long config_table;     /* config tables */
+       unsigned long esrt;             /* ESRT table */
        efi_get_time_t *get_time;
        efi_set_time_t *set_time;
        efi_get_wakeup_time_t *get_wakeup_time;
@@ -875,6 +879,11 @@ static inline efi_status_t efi_query_variable_store(u32 attributes, unsigned lon
 #endif
 extern void __iomem *efi_lookup_mapped_addr(u64 phys_addr);
 extern int efi_config_init(efi_config_table_type_t *arch_tables);
+#ifdef CONFIG_EFI_ESRT
+extern void __init efi_esrt_init(void);
+#else
+static inline void efi_esrt_init(void) { }
+#endif
 extern int efi_config_parse_tables(void *config_tables, int count, int sz,
                                   efi_config_table_type_t *arch_tables);
 extern u64 efi_get_iobase (void);
@@ -882,12 +891,15 @@ extern u32 efi_mem_type (unsigned long phys_addr);
 extern u64 efi_mem_attributes (unsigned long phys_addr);
 extern u64 efi_mem_attribute (unsigned long phys_addr, unsigned long size);
 extern int __init efi_uart_console_only (void);
+extern u64 efi_mem_desc_end(efi_memory_desc_t *md);
+extern int efi_mem_desc_lookup(u64 phys_addr, efi_memory_desc_t *out_md);
 extern void efi_initialize_iomem_resources(struct resource *code_resource,
                struct resource *data_resource, struct resource *bss_resource);
 extern void efi_get_time(struct timespec *now);
 extern void efi_reserve_boot_services(void);
 extern int efi_get_fdt_params(struct efi_fdt_params *params, int verbose);
 extern struct efi_memory_map memmap;
+extern struct kobject *efi_kobj;
 
 extern int efi_reboot_quirk_mode;
 extern bool efi_poweroff_required(void);
index 35ec87e490b1a41ff0bc3ba20b06ac9d958f972a..b577e801b4af17ddd3288e28c209b644415cc63c 100644 (file)
@@ -38,7 +38,6 @@ struct backing_dev_info;
 struct export_operations;
 struct hd_geometry;
 struct iovec;
-struct nameidata;
 struct kiocb;
 struct kobject;
 struct pipe_inode_info;
@@ -656,6 +655,7 @@ struct inode {
                struct pipe_inode_info  *i_pipe;
                struct block_device     *i_bdev;
                struct cdev             *i_cdev;
+               char                    *i_link;
        };
 
        __u32                   i_generation;
@@ -1607,12 +1607,12 @@ struct file_operations {
 
 struct inode_operations {
        struct dentry * (*lookup) (struct inode *,struct dentry *, unsigned int);
-       void * (*follow_link) (struct dentry *, struct nameidata *);
+       const char * (*follow_link) (struct dentry *, void **);
        int (*permission) (struct inode *, int);
        struct posix_acl * (*get_acl)(struct inode *, int);
 
        int (*readlink) (struct dentry *, char __user *,int);
-       void (*put_link) (struct dentry *, struct nameidata *, void *);
+       void (*put_link) (struct inode *, void *);
 
        int (*create) (struct inode *,struct dentry *, umode_t, bool);
        int (*link) (struct dentry *,struct inode *,struct dentry *);
@@ -1879,6 +1879,7 @@ enum file_time_flags {
        S_VERSION = 8,
 };
 
+extern bool atime_needs_update(const struct path *, struct inode *);
 extern void touch_atime(const struct path *);
 static inline void file_accessed(struct file *file)
 {
@@ -2704,13 +2705,14 @@ extern const struct file_operations generic_ro_fops;
 
 extern int readlink_copy(char __user *, int, const char *);
 extern int page_readlink(struct dentry *, char __user *, int);
-extern void *page_follow_link_light(struct dentry *, struct nameidata *);
-extern void page_put_link(struct dentry *, struct nameidata *, void *);
+extern const char *page_follow_link_light(struct dentry *, void **);
+extern void page_put_link(struct inode *, void *);
 extern int __page_symlink(struct inode *inode, const char *symname, int len,
                int nofs);
 extern int page_symlink(struct inode *inode, const char *symname, int len);
 extern const struct inode_operations page_symlink_inode_operations;
-extern void kfree_put_link(struct dentry *, struct nameidata *, void *);
+extern void kfree_put_link(struct inode *, void *);
+extern void free_page_put_link(struct inode *, void *);
 extern int generic_readlink(struct dentry *, char __user *, int);
 extern void generic_fillattr(struct inode *, struct kstat *);
 int vfs_getattr_nosec(struct path *path, struct kstat *stat);
@@ -2721,6 +2723,8 @@ void __inode_sub_bytes(struct inode *inode, loff_t bytes);
 void inode_sub_bytes(struct inode *inode, loff_t bytes);
 loff_t inode_get_bytes(struct inode *inode);
 void inode_set_bytes(struct inode *inode, loff_t bytes);
+const char *simple_follow_link(struct dentry *, void **);
+extern const struct inode_operations simple_symlink_inode_operations;
 
 extern int iterate_dir(struct file *, struct dir_context *);
 
index ab81339a8590a8bb8f4b889b501e7eb928878cc5..d12b5d566e4b11c725aa79dae8f2301364d3b9a9 100644 (file)
@@ -196,13 +196,6 @@ static inline int gpio_export_link(struct device *dev, const char *name,
        return -EINVAL;
 }
 
-static inline int gpio_sysfs_set_active_low(unsigned gpio, int value)
-{
-       /* GPIO can never have been requested */
-       WARN_ON(1);
-       return -EINVAL;
-}
-
 static inline void gpio_unexport(unsigned gpio)
 {
        /* GPIO can never have been exported */
index 3a7c9ffd5ab930b46e7341adfdcc0c5cc446224a..fd098169fe87ed0b92aecd23b46e7a9a790bafad 100644 (file)
@@ -100,24 +100,25 @@ int gpiod_direction_output_raw(struct gpio_desc *desc, int value);
 /* Value get/set from non-sleeping context */
 int gpiod_get_value(const struct gpio_desc *desc);
 void gpiod_set_value(struct gpio_desc *desc, int value);
-void gpiod_set_array(unsigned int array_size,
-                    struct gpio_desc **desc_array, int *value_array);
+void gpiod_set_array_value(unsigned int array_size,
+                          struct gpio_desc **desc_array, int *value_array);
 int gpiod_get_raw_value(const struct gpio_desc *desc);
 void gpiod_set_raw_value(struct gpio_desc *desc, int value);
-void gpiod_set_raw_array(unsigned int array_size,
-                        struct gpio_desc **desc_array, int *value_array);
+void gpiod_set_raw_array_value(unsigned int array_size,
+                              struct gpio_desc **desc_array,
+                              int *value_array);
 
 /* Value get/set from sleeping context */
 int gpiod_get_value_cansleep(const struct gpio_desc *desc);
 void gpiod_set_value_cansleep(struct gpio_desc *desc, int value);
-void gpiod_set_array_cansleep(unsigned int array_size,
-                             struct gpio_desc **desc_array,
-                             int *value_array);
+void gpiod_set_array_value_cansleep(unsigned int array_size,
+                                   struct gpio_desc **desc_array,
+                                   int *value_array);
 int gpiod_get_raw_value_cansleep(const struct gpio_desc *desc);
 void gpiod_set_raw_value_cansleep(struct gpio_desc *desc, int value);
-void gpiod_set_raw_array_cansleep(unsigned int array_size,
-                                 struct gpio_desc **desc_array,
-                                 int *value_array);
+void gpiod_set_raw_array_value_cansleep(unsigned int array_size,
+                                       struct gpio_desc **desc_array,
+                                       int *value_array);
 
 int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce);
 
@@ -304,9 +305,9 @@ static inline void gpiod_set_value(struct gpio_desc *desc, int value)
        /* GPIO can never have been requested */
        WARN_ON(1);
 }
-static inline void gpiod_set_array(unsigned int array_size,
-                                  struct gpio_desc **desc_array,
-                                  int *value_array)
+static inline void gpiod_set_array_value(unsigned int array_size,
+                                        struct gpio_desc **desc_array,
+                                        int *value_array)
 {
        /* GPIO can never have been requested */
        WARN_ON(1);
@@ -322,9 +323,9 @@ static inline void gpiod_set_raw_value(struct gpio_desc *desc, int value)
        /* GPIO can never have been requested */
        WARN_ON(1);
 }
-static inline void gpiod_set_raw_array(unsigned int array_size,
-                                      struct gpio_desc **desc_array,
-                                      int *value_array)
+static inline void gpiod_set_raw_array_value(unsigned int array_size,
+                                            struct gpio_desc **desc_array,
+                                            int *value_array)
 {
        /* GPIO can never have been requested */
        WARN_ON(1);
@@ -341,7 +342,7 @@ static inline void gpiod_set_value_cansleep(struct gpio_desc *desc, int value)
        /* GPIO can never have been requested */
        WARN_ON(1);
 }
-static inline void gpiod_set_array_cansleep(unsigned int array_size,
+static inline void gpiod_set_array_value_cansleep(unsigned int array_size,
                                            struct gpio_desc **desc_array,
                                            int *value_array)
 {
@@ -360,7 +361,7 @@ static inline void gpiod_set_raw_value_cansleep(struct gpio_desc *desc,
        /* GPIO can never have been requested */
        WARN_ON(1);
 }
-static inline void gpiod_set_raw_array_cansleep(unsigned int array_size,
+static inline void gpiod_set_raw_array_value_cansleep(unsigned int array_size,
                                                struct gpio_desc **desc_array,
                                                int *value_array)
 {
@@ -449,7 +450,6 @@ static inline int desc_to_gpio(const struct gpio_desc *desc)
 int gpiod_export(struct gpio_desc *desc, bool direction_may_change);
 int gpiod_export_link(struct device *dev, const char *name,
                      struct gpio_desc *desc);
-int gpiod_sysfs_set_active_low(struct gpio_desc *desc, int value);
 void gpiod_unexport(struct gpio_desc *desc);
 
 #else  /* CONFIG_GPIOLIB && CONFIG_GPIO_SYSFS */
@@ -466,11 +466,6 @@ static inline int gpiod_export_link(struct device *dev, const char *name,
        return -ENOSYS;
 }
 
-static inline int gpiod_sysfs_set_active_low(struct gpio_desc *desc, int value)
-{
-       return -ENOSYS;
-}
-
 static inline void gpiod_unexport(struct gpio_desc *desc)
 {
 }
index f1b36593ec9f18f0e552a83992be2f03f9c007d5..cc7ec129b329efb9887c0482c5abeb83562b79f7 100644 (file)
@@ -20,6 +20,7 @@ struct seq_file;
  * struct gpio_chip - abstract a GPIO controller
  * @label: for diagnostics
  * @dev: optional device providing the GPIOs
+ * @cdev: class device used by sysfs interface (may be NULL)
  * @owner: helps prevent removal of modules exporting active GPIOs
  * @list: links gpio_chips together for traversal
  * @request: optional hook for chip-specific activation, such as
@@ -41,8 +42,12 @@ struct seq_file;
  * @dbg_show: optional routine to show contents in debugfs; default code
  *     will be used when this is omitted, but custom code can show extra
  *     state (such as pullup/pulldown configuration).
- * @base: identifies the first GPIO number handled by this chip; or, if
- *     negative during registration, requests dynamic ID allocation.
+ * @base: identifies the first GPIO number handled by this chip;
+ *     or, if negative during registration, requests dynamic ID allocation.
+ *     DEPRECATION: providing anything non-negative and nailing the base
+ *     base offset of GPIO chips is deprecated. Please pass -1 as base to
+ *     let gpiolib select the chip base in all possible cases. We want to
+ *     get rid of the static GPIO number space in the long run.
  * @ngpio: the number of GPIOs handled by this controller; the last GPIO
  *     handled is (base + ngpio - 1).
  * @desc: array of ngpio descriptors. Private.
@@ -57,7 +62,6 @@ struct seq_file;
  *     implies that if the chip supports IRQs, these IRQs need to be threaded
  *     as the chip access may sleep when e.g. reading out the IRQ status
  *     registers.
- * @exported: flags if the gpiochip is exported for use from sysfs. Private.
  * @irq_not_threaded: flag must be set if @can_sleep is set but the
  *     IRQs don't need to be threaded
  *
@@ -74,6 +78,7 @@ struct seq_file;
 struct gpio_chip {
        const char              *label;
        struct device           *dev;
+       struct device           *cdev;
        struct module           *owner;
        struct list_head        list;
 
@@ -109,7 +114,6 @@ struct gpio_chip {
        const char              *const *names;
        bool                    can_sleep;
        bool                    irq_not_threaded;
-       bool                    exported;
 
 #ifdef CONFIG_GPIOLIB_IRQCHIP
        /*
@@ -121,6 +125,7 @@ struct gpio_chip {
        unsigned int            irq_base;
        irq_flow_handler_t      irq_handler;
        unsigned int            irq_default_type;
+       int                     irq_parent;
 #endif
 
 #if defined(CONFIG_OF_GPIO)
index 176b43670e5da9f7de6c906a56dfb007f0b160cf..f17980de26629fbf65365047a0cfe8ce6034b53e 100644 (file)
@@ -815,6 +815,8 @@ void hid_disconnect(struct hid_device *hid);
 const struct hid_device_id *hid_match_id(struct hid_device *hdev,
                                         const struct hid_device_id *id);
 s32 hid_snto32(__u32 value, unsigned n);
+__u32 hid_field_extract(const struct hid_device *hid, __u8 *report,
+                    unsigned offset, unsigned n);
 
 /**
  * hid_device_io_start - enable HID input during probe, remove
index 05f6df1fdf5bbfc70880f188c40e61264f764cc7..76dd4f0da5ca9907572bea19f4b0d0dbe8ad06eb 100644 (file)
@@ -53,34 +53,25 @@ enum hrtimer_restart {
  *
  * 0x00                inactive
  * 0x01                enqueued into rbtree
- * 0x02                callback function running
- * 0x04                timer is migrated to another cpu
  *
- * Special cases:
- * 0x03                callback function running and enqueued
- *             (was requeued on another CPU)
- * 0x05                timer was migrated on CPU hotunplug
+ * The callback state is not part of the timer->state because clearing it would
+ * mean touching the timer after the callback, this makes it impossible to free
+ * the timer from the callback function.
  *
- * The "callback function running and enqueued" status is only possible on
- * SMP. It happens for example when a posix timer expired and the callback
+ * Therefore we track the callback state in:
+ *
+ *     timer->base->cpu_base->running == timer
+ *
+ * On SMP it is possible to have a "callback function running and enqueued"
+ * status. It happens for example when a posix timer expired and the callback
  * queued a signal. Between dropping the lock which protects the posix timer
  * and reacquiring the base lock of the hrtimer, another CPU can deliver the
- * signal and rearm the timer. We have to preserve the callback running state,
- * as otherwise the timer could be removed before the softirq code finishes the
- * the handling of the timer.
- *
- * The HRTIMER_STATE_ENQUEUED bit is always or'ed to the current state
- * to preserve the HRTIMER_STATE_CALLBACK in the above scenario. This
- * also affects HRTIMER_STATE_MIGRATE where the preservation is not
- * necessary. HRTIMER_STATE_MIGRATE is cleared after the timer is
- * enqueued on the new cpu.
+ * signal and rearm the timer.
  *
  * All state transitions are protected by cpu_base->lock.
  */
 #define HRTIMER_STATE_INACTIVE 0x00
 #define HRTIMER_STATE_ENQUEUED 0x01
-#define HRTIMER_STATE_CALLBACK 0x02
-#define HRTIMER_STATE_MIGRATE  0x04
 
 /**
  * struct hrtimer - the basic hrtimer structure
@@ -130,6 +121,12 @@ struct hrtimer_sleeper {
        struct task_struct *task;
 };
 
+#ifdef CONFIG_64BIT
+# define HRTIMER_CLOCK_BASE_ALIGN      64
+#else
+# define HRTIMER_CLOCK_BASE_ALIGN      32
+#endif
+
 /**
  * struct hrtimer_clock_base - the timer base for a specific clock
  * @cpu_base:          per cpu clock base
@@ -137,9 +134,7 @@ struct hrtimer_sleeper {
  *                     timer to a base on another cpu.
  * @clockid:           clock id for per_cpu support
  * @active:            red black tree root node for the active timers
- * @resolution:                the resolution of the clock, in nanoseconds
  * @get_time:          function to retrieve the current time of the clock
- * @softirq_time:      the time when running the hrtimer queue in the softirq
  * @offset:            offset of this clock to the monotonic base
  */
 struct hrtimer_clock_base {
@@ -147,11 +142,9 @@ struct hrtimer_clock_base {
        int                     index;
        clockid_t               clockid;
        struct timerqueue_head  active;
-       ktime_t                 resolution;
        ktime_t                 (*get_time)(void);
-       ktime_t                 softirq_time;
        ktime_t                 offset;
-};
+} __attribute__((__aligned__(HRTIMER_CLOCK_BASE_ALIGN)));
 
 enum  hrtimer_base_type {
        HRTIMER_BASE_MONOTONIC,
@@ -165,11 +158,16 @@ enum  hrtimer_base_type {
  * struct hrtimer_cpu_base - the per cpu clock bases
  * @lock:              lock protecting the base and associated clock bases
  *                     and timers
+ * @seq:               seqcount around __run_hrtimer
+ * @running:           pointer to the currently running hrtimer
  * @cpu:               cpu number
  * @active_bases:      Bitfield to mark bases with active timers
- * @clock_was_set:     Indicates that clock was set from irq context.
+ * @clock_was_set_seq: Sequence counter of clock was set events
+ * @migration_enabled: The migration of hrtimers to other cpus is enabled
+ * @nohz_active:       The nohz functionality is enabled
  * @expires_next:      absolute time of the next event which was scheduled
  *                     via clock_set_next_event()
+ * @next_timer:                Pointer to the first expiring timer
  * @in_hrtirq:         hrtimer_interrupt() is currently executing
  * @hres_active:       State of high resolution mode
  * @hang_detected:     The last hrtimer interrupt detected a hang
@@ -178,27 +176,38 @@ enum  hrtimer_base_type {
  * @nr_hangs:          Total number of hrtimer interrupt hangs
  * @max_hang_time:     Maximum time spent in hrtimer_interrupt
  * @clock_base:                array of clock bases for this cpu
+ *
+ * Note: next_timer is just an optimization for __remove_hrtimer().
+ *      Do not dereference the pointer because it is not reliable on
+ *      cross cpu removals.
  */
 struct hrtimer_cpu_base {
        raw_spinlock_t                  lock;
+       seqcount_t                      seq;
+       struct hrtimer                  *running;
        unsigned int                    cpu;
        unsigned int                    active_bases;
-       unsigned int                    clock_was_set;
+       unsigned int                    clock_was_set_seq;
+       bool                            migration_enabled;
+       bool                            nohz_active;
 #ifdef CONFIG_HIGH_RES_TIMERS
+       unsigned int                    in_hrtirq       : 1,
+                                       hres_active     : 1,
+                                       hang_detected   : 1;
        ktime_t                         expires_next;
-       int                             in_hrtirq;
-       int                             hres_active;
-       int                             hang_detected;
-       unsigned long                   nr_events;
-       unsigned long                   nr_retries;
-       unsigned long                   nr_hangs;
-       ktime_t                         max_hang_time;
+       struct hrtimer                  *next_timer;
+       unsigned int                    nr_events;
+       unsigned int                    nr_retries;
+       unsigned int                    nr_hangs;
+       unsigned int                    max_hang_time;
 #endif
        struct hrtimer_clock_base       clock_base[HRTIMER_MAX_CLOCK_BASES];
-};
+} ____cacheline_aligned;
 
 static inline void hrtimer_set_expires(struct hrtimer *timer, ktime_t time)
 {
+       BUILD_BUG_ON(sizeof(struct hrtimer_clock_base) > HRTIMER_CLOCK_BASE_ALIGN);
+
        timer->node.expires = time;
        timer->_softexpires = time;
 }
@@ -262,19 +271,16 @@ static inline ktime_t hrtimer_expires_remaining(const struct hrtimer *timer)
        return ktime_sub(timer->node.expires, timer->base->get_time());
 }
 
-#ifdef CONFIG_HIGH_RES_TIMERS
-struct clock_event_device;
-
-extern void hrtimer_interrupt(struct clock_event_device *dev);
-
-/*
- * In high resolution mode the time reference must be read accurate
- */
 static inline ktime_t hrtimer_cb_get_time(struct hrtimer *timer)
 {
        return timer->base->get_time();
 }
 
+#ifdef CONFIG_HIGH_RES_TIMERS
+struct clock_event_device;
+
+extern void hrtimer_interrupt(struct clock_event_device *dev);
+
 static inline int hrtimer_is_hres_active(struct hrtimer *timer)
 {
        return timer->base->cpu_base->hres_active;
@@ -295,21 +301,16 @@ extern void hrtimer_peek_ahead_timers(void);
 
 extern void clock_was_set_delayed(void);
 
+extern unsigned int hrtimer_resolution;
+
 #else
 
 # define MONOTONIC_RES_NSEC    LOW_RES_NSEC
 # define KTIME_MONOTONIC_RES   KTIME_LOW_RES
 
-static inline void hrtimer_peek_ahead_timers(void) { }
+#define hrtimer_resolution     (unsigned int)LOW_RES_NSEC
 
-/*
- * In non high resolution mode the time reference is taken from
- * the base softirq time variable.
- */
-static inline ktime_t hrtimer_cb_get_time(struct hrtimer *timer)
-{
-       return timer->base->softirq_time;
-}
+static inline void hrtimer_peek_ahead_timers(void) { }
 
 static inline int hrtimer_is_hres_active(struct hrtimer *timer)
 {
@@ -353,49 +354,47 @@ static inline void destroy_hrtimer_on_stack(struct hrtimer *timer) { }
 #endif
 
 /* Basic timer operations: */
-extern int hrtimer_start(struct hrtimer *timer, ktime_t tim,
-                        const enum hrtimer_mode mode);
-extern int hrtimer_start_range_ns(struct hrtimer *timer, ktime_t tim,
+extern void hrtimer_start_range_ns(struct hrtimer *timer, ktime_t tim,
                        unsigned long range_ns, const enum hrtimer_mode mode);
-extern int
-__hrtimer_start_range_ns(struct hrtimer *timer, ktime_t tim,
-                        unsigned long delta_ns,
-                        const enum hrtimer_mode mode, int wakeup);
+
+/**
+ * hrtimer_start - (re)start an hrtimer on the current CPU
+ * @timer:     the timer to be added
+ * @tim:       expiry time
+ * @mode:      expiry mode: absolute (HRTIMER_MODE_ABS) or
+ *             relative (HRTIMER_MODE_REL)
+ */
+static inline void hrtimer_start(struct hrtimer *timer, ktime_t tim,
+                                const enum hrtimer_mode mode)
+{
+       hrtimer_start_range_ns(timer, tim, 0, mode);
+}
 
 extern int hrtimer_cancel(struct hrtimer *timer);
 extern int hrtimer_try_to_cancel(struct hrtimer *timer);
 
-static inline int hrtimer_start_expires(struct hrtimer *timer,
-                                               enum hrtimer_mode mode)
+static inline void hrtimer_start_expires(struct hrtimer *timer,
+                                        enum hrtimer_mode mode)
 {
        unsigned long delta;
        ktime_t soft, hard;
        soft = hrtimer_get_softexpires(timer);
        hard = hrtimer_get_expires(timer);
        delta = ktime_to_ns(ktime_sub(hard, soft));
-       return hrtimer_start_range_ns(timer, soft, delta, mode);
+       hrtimer_start_range_ns(timer, soft, delta, mode);
 }
 
-static inline int hrtimer_restart(struct hrtimer *timer)
+static inline void hrtimer_restart(struct hrtimer *timer)
 {
-       return hrtimer_start_expires(timer, HRTIMER_MODE_ABS);
+       hrtimer_start_expires(timer, HRTIMER_MODE_ABS);
 }
 
 /* Query timers: */
 extern ktime_t hrtimer_get_remaining(const struct hrtimer *timer);
-extern int hrtimer_get_res(const clockid_t which_clock, struct timespec *tp);
 
-extern ktime_t hrtimer_get_next_event(void);
+extern u64 hrtimer_get_next_event(void);
 
-/*
- * A timer is active, when it is enqueued into the rbtree or the
- * callback function is running or it's in the state of being migrated
- * to another cpu.
- */
-static inline int hrtimer_active(const struct hrtimer *timer)
-{
-       return timer->state != HRTIMER_STATE_INACTIVE;
-}
+extern bool hrtimer_active(const struct hrtimer *timer);
 
 /*
  * Helper function to check, whether the timer is on one of the queues
@@ -411,14 +410,29 @@ static inline int hrtimer_is_queued(struct hrtimer *timer)
  */
 static inline int hrtimer_callback_running(struct hrtimer *timer)
 {
-       return timer->state & HRTIMER_STATE_CALLBACK;
+       return timer->base->cpu_base->running == timer;
 }
 
 /* Forward a hrtimer so it expires after now: */
 extern u64
 hrtimer_forward(struct hrtimer *timer, ktime_t now, ktime_t interval);
 
-/* Forward a hrtimer so it expires after the hrtimer's current now */
+/**
+ * hrtimer_forward_now - forward the timer expiry so it expires after now
+ * @timer:     hrtimer to forward
+ * @interval:  the interval to forward
+ *
+ * Forward the timer expiry so it will expire after the current time
+ * of the hrtimer clock base. Returns the number of overruns.
+ *
+ * Can be safely called from the callback function of @timer. If
+ * called from other contexts @timer must neither be enqueued nor
+ * running the callback and the caller needs to take care of
+ * serialization.
+ *
+ * Note: This only updates the timer expiry value and does not requeue
+ * the timer.
+ */
 static inline u64 hrtimer_forward_now(struct hrtimer *timer,
                                      ktime_t interval)
 {
@@ -443,7 +457,6 @@ extern int schedule_hrtimeout(ktime_t *expires, const enum hrtimer_mode mode);
 
 /* Soft interrupt function to run the hrtimer queues: */
 extern void hrtimer_run_queues(void);
-extern void hrtimer_run_pending(void);
 
 /* Bootup initialization: */
 extern void __init hrtimers_init(void);
index 70a1dbbf209350836f97743363fadfe4f6f95b36..d4a527e58434df73800eed55fbbc6750f5f2d5ee 100644 (file)
@@ -1,24 +1,38 @@
 #ifndef LINUX_HTIRQ_H
 #define LINUX_HTIRQ_H
 
+struct pci_dev;
+struct irq_data;
+
 struct ht_irq_msg {
        u32     address_lo;     /* low 32 bits of the ht irq message */
        u32     address_hi;     /* high 32 bits of the it irq message */
 };
 
+typedef void (ht_irq_update_t)(struct pci_dev *dev, int irq,
+                              struct ht_irq_msg *msg);
+
+struct ht_irq_cfg {
+       struct pci_dev *dev;
+        /* Update callback used to cope with buggy hardware */
+       ht_irq_update_t *update;
+       unsigned pos;
+       unsigned idx;
+       struct ht_irq_msg msg;
+};
+
 /* Helper functions.. */
 void fetch_ht_irq_msg(unsigned int irq, struct ht_irq_msg *msg);
 void write_ht_irq_msg(unsigned int irq, struct ht_irq_msg *msg);
-struct irq_data;
 void mask_ht_irq(struct irq_data *data);
 void unmask_ht_irq(struct irq_data *data);
 
 /* The arch hook for getting things started */
-int arch_setup_ht_irq(unsigned int irq, struct pci_dev *dev);
+int arch_setup_ht_irq(int idx, int pos, struct pci_dev *dev,
+                     ht_irq_update_t *update);
+void arch_teardown_ht_irq(unsigned int irq);
 
 /* For drivers of buggy hardware */
-typedef void (ht_irq_update_t)(struct pci_dev *dev, int irq,
-                              struct ht_irq_msg *msg);
 int __ht_create_irq(struct pci_dev *dev, int idx, ht_irq_update_t *update);
 
 #endif /* LINUX_HTIRQ_H */
index 0bc03f100d04a89f72aabeacf541734dd0d8504e..9ad7828d9d34df3675d9c5fb6781a82883211525 100644 (file)
@@ -675,6 +675,7 @@ struct twl4030_power_data {
        struct twl4030_resconfig *board_config;
 #define TWL4030_RESCONFIG_UNDEF        ((u8)-1)
        bool use_poweroff;      /* Board is wired for TWL poweroff */
+       bool ac_charger_quirk;  /* Disable AC charger on board */
 };
 
 extern int twl4030_remove_script(u8 flags);
index 796ef9645827f000cb76ce4cd8637dc6cfa4db7a..d9a366d24e3bb8736cc7acdaa4f541c9720747a6 100644 (file)
@@ -87,6 +87,7 @@ static inline void dmar_writeq(void __iomem *addr, u64 val)
 /*
  * Decoding Capability Register
  */
+#define cap_pi_support(c)      (((c) >> 59) & 1)
 #define cap_read_drain(c)      (((c) >> 55) & 1)
 #define cap_write_drain(c)     (((c) >> 54) & 1)
 #define cap_max_amask_val(c)   (((c) >> 48) & 0x3f)
@@ -115,13 +116,14 @@ static inline void dmar_writeq(void __iomem *addr, u64 val)
  * Extended Capability Register
  */
 
+#define ecap_pasid(e)          ((e >> 40) & 0x1)
 #define ecap_pss(e)            ((e >> 35) & 0x1f)
 #define ecap_eafs(e)           ((e >> 34) & 0x1)
 #define ecap_nwfs(e)           ((e >> 33) & 0x1)
 #define ecap_srs(e)            ((e >> 31) & 0x1)
 #define ecap_ers(e)            ((e >> 30) & 0x1)
 #define ecap_prs(e)            ((e >> 29) & 0x1)
-#define ecap_pasid(e)          ((e >> 28) & 0x1)
+/* PASID support used to be on bit 28 */
 #define ecap_dis(e)            ((e >> 27) & 0x1)
 #define ecap_nest(e)           ((e >> 26) & 0x1)
 #define ecap_mts(e)            ((e >> 25) & 0x1)
@@ -295,9 +297,12 @@ struct q_inval {
 /* 1MB - maximum possible interrupt remapping table size */
 #define INTR_REMAP_PAGE_ORDER  8
 #define INTR_REMAP_TABLE_REG_SIZE      0xf
+#define INTR_REMAP_TABLE_REG_SIZE_MASK  0xf
 
 #define INTR_REMAP_TABLE_ENTRIES       65536
 
+struct irq_domain;
+
 struct ir_table {
        struct irte *base;
        unsigned long *bitmap;
@@ -319,6 +324,9 @@ enum {
        MAX_SR_DMAR_REGS
 };
 
+#define VTD_FLAG_TRANS_PRE_ENABLED     (1 << 0)
+#define VTD_FLAG_IRQ_REMAP_PRE_ENABLED (1 << 1)
+
 struct intel_iommu {
        void __iomem    *reg; /* Pointer to hardware regs, virtual addr */
        u64             reg_phys; /* physical address of hw register set */
@@ -347,9 +355,12 @@ struct intel_iommu {
 
 #ifdef CONFIG_IRQ_REMAP
        struct ir_table *ir_table;      /* Interrupt remapping info */
+       struct irq_domain *ir_domain;
+       struct irq_domain *ir_msi_domain;
 #endif
        struct device   *iommu_dev; /* IOMMU-sysfs device */
        int             node;
+       u32             flags;      /* Software defined flags */
 };
 
 static inline void __iommu_flush_cache(
index 950ae45018260224c9138b043f9071ce4d1f9acc..be7e75c945e97b07d5f248ded5c1e0d9240756ad 100644 (file)
@@ -413,7 +413,8 @@ enum
        BLOCK_IOPOLL_SOFTIRQ,
        TASKLET_SOFTIRQ,
        SCHED_SOFTIRQ,
-       HRTIMER_SOFTIRQ,
+       HRTIMER_SOFTIRQ, /* Unused, but kept as tools rely on the
+                           numbering. Sigh! */
        RCU_SOFTIRQ,    /* Preferable RCU should always be the last softirq */
 
        NR_SOFTIRQS
@@ -592,10 +593,10 @@ tasklet_hrtimer_init(struct tasklet_hrtimer *ttimer,
                     clockid_t which_clock, enum hrtimer_mode mode);
 
 static inline
-int tasklet_hrtimer_start(struct tasklet_hrtimer *ttimer, ktime_t time,
-                         const enum hrtimer_mode mode)
+void tasklet_hrtimer_start(struct tasklet_hrtimer *ttimer, ktime_t time,
+                          const enum hrtimer_mode mode)
 {
-       return hrtimer_start(&ttimer->timer, time, mode);
+       hrtimer_start(&ttimer->timer, time, mode);
 }
 
 static inline
index 986f2bffea1edc95513361a76fa55df5a3e06c06..fb5a99800e77faf6481363fb1fe43bb156d8b213 100644 (file)
@@ -19,6 +19,7 @@
 #define _LINUX_IO_H
 
 #include <linux/types.h>
+#include <linux/init.h>
 #include <asm/io.h>
 #include <asm/page.h>
 
@@ -111,6 +112,13 @@ static inline void arch_phys_wc_del(int handle)
 }
 
 #define arch_phys_wc_add arch_phys_wc_add
+#ifndef arch_phys_wc_index
+static inline int arch_phys_wc_index(int handle)
+{
+       return -1;
+}
+#define arch_phys_wc_index arch_phys_wc_index
+#endif
 #endif
 
 #endif /* _LINUX_IO_H */
index 0546b8710ce308540abd841944aee07ba24fedfb..dc767f7c3704639da944153e8bdae7cf40cd804d 100644 (file)
@@ -114,6 +114,20 @@ enum iommu_attr {
        DOMAIN_ATTR_MAX,
 };
 
+/**
+ * struct iommu_dm_region - descriptor for a direct mapped memory region
+ * @list: Linked list pointers
+ * @start: System physical start address of the region
+ * @length: Length of the region in bytes
+ * @prot: IOMMU Protection flags (READ/WRITE/...)
+ */
+struct iommu_dm_region {
+       struct list_head        list;
+       phys_addr_t             start;
+       size_t                  length;
+       int                     prot;
+};
+
 #ifdef CONFIG_IOMMU_API
 
 /**
@@ -159,6 +173,10 @@ struct iommu_ops {
        int (*domain_set_attr)(struct iommu_domain *domain,
                               enum iommu_attr attr, void *data);
 
+       /* Request/Free a list of direct mapping requirements for a device */
+       void (*get_dm_regions)(struct device *dev, struct list_head *list);
+       void (*put_dm_regions)(struct device *dev, struct list_head *list);
+
        /* Window handling functions */
        int (*domain_window_enable)(struct iommu_domain *domain, u32 wnd_nr,
                                    phys_addr_t paddr, u64 size, int prot);
@@ -193,6 +211,7 @@ extern int iommu_attach_device(struct iommu_domain *domain,
                               struct device *dev);
 extern void iommu_detach_device(struct iommu_domain *domain,
                                struct device *dev);
+extern struct iommu_domain *iommu_get_domain_for_dev(struct device *dev);
 extern int iommu_map(struct iommu_domain *domain, unsigned long iova,
                     phys_addr_t paddr, size_t size, int prot);
 extern size_t iommu_unmap(struct iommu_domain *domain, unsigned long iova,
@@ -204,6 +223,10 @@ extern phys_addr_t iommu_iova_to_phys(struct iommu_domain *domain, dma_addr_t io
 extern void iommu_set_fault_handler(struct iommu_domain *domain,
                        iommu_fault_handler_t handler, void *token);
 
+extern void iommu_get_dm_regions(struct device *dev, struct list_head *list);
+extern void iommu_put_dm_regions(struct device *dev, struct list_head *list);
+extern int iommu_request_dm_for_dev(struct device *dev);
+
 extern int iommu_attach_group(struct iommu_domain *domain,
                              struct iommu_group *group);
 extern void iommu_detach_group(struct iommu_domain *domain,
@@ -227,6 +250,7 @@ extern int iommu_group_unregister_notifier(struct iommu_group *group,
                                           struct notifier_block *nb);
 extern int iommu_group_id(struct iommu_group *group);
 extern struct iommu_group *iommu_group_get_for_dev(struct device *dev);
+extern struct iommu_domain *iommu_group_default_domain(struct iommu_group *);
 
 extern int iommu_domain_get_attr(struct iommu_domain *domain, enum iommu_attr,
                                 void *data);
@@ -332,6 +356,11 @@ static inline void iommu_detach_device(struct iommu_domain *domain,
 {
 }
 
+static inline struct iommu_domain *iommu_get_domain_for_dev(struct device *dev)
+{
+       return NULL;
+}
+
 static inline int iommu_map(struct iommu_domain *domain, unsigned long iova,
                            phys_addr_t paddr, int gfp_order, int prot)
 {
@@ -373,6 +402,21 @@ static inline void iommu_set_fault_handler(struct iommu_domain *domain,
 {
 }
 
+static inline void iommu_get_dm_regions(struct device *dev,
+                                       struct list_head *list)
+{
+}
+
+static inline void iommu_put_dm_regions(struct device *dev,
+                                       struct list_head *list)
+{
+}
+
+static inline int iommu_request_dm_for_dev(struct device *dev)
+{
+       return -ENODEV;
+}
+
 static inline int iommu_attach_group(struct iommu_domain *domain,
                                     struct iommu_group *group)
 {
index 62c6901cab550d7f57039c5b7052fc08bbe0964d..812149160d3bc5829750d5f2790ae23c4791092f 100644 (file)
@@ -126,13 +126,21 @@ struct msi_desc;
 struct irq_domain;
 
 /**
- * struct irq_data - per irq and irq chip data passed down to chip functions
+ * struct irq_common_data - per irq data shared by all irqchips
+ * @state_use_accessors: status information for irq chip functions.
+ *                     Use accessor functions to deal with it
+ */
+struct irq_common_data {
+       unsigned int            state_use_accessors;
+};
+
+/**
+ * struct irq_data - per irq chip data passed down to chip functions
  * @mask:              precomputed bitmask for accessing the chip registers
  * @irq:               interrupt number
  * @hwirq:             hardware interrupt number, local to the interrupt domain
  * @node:              node index useful for balancing
- * @state_use_accessors: status information for irq chip functions.
- *                     Use accessor functions to deal with it
+ * @common:            point to data shared by all irqchips
  * @chip:              low level interrupt hardware access
  * @domain:            Interrupt translation domain; responsible for mapping
  *                     between hwirq number and linux irq number.
@@ -153,7 +161,7 @@ struct irq_data {
        unsigned int            irq;
        unsigned long           hwirq;
        unsigned int            node;
-       unsigned int            state_use_accessors;
+       struct irq_common_data  *common;
        struct irq_chip         *chip;
        struct irq_domain       *domain;
 #ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
@@ -166,7 +174,7 @@ struct irq_data {
 };
 
 /*
- * Bit masks for irq_data.state
+ * Bit masks for irq_common_data.state_use_accessors
  *
  * IRQD_TRIGGER_MASK           - Mask for the trigger type bits
  * IRQD_SETAFFINITY_PENDING    - Affinity setting is pending
@@ -198,34 +206,36 @@ enum {
        IRQD_WAKEUP_ARMED               = (1 << 19),
 };
 
+#define __irqd_to_state(d)             ((d)->common->state_use_accessors)
+
 static inline bool irqd_is_setaffinity_pending(struct irq_data *d)
 {
-       return d->state_use_accessors & IRQD_SETAFFINITY_PENDING;
+       return __irqd_to_state(d) & IRQD_SETAFFINITY_PENDING;
 }
 
 static inline bool irqd_is_per_cpu(struct irq_data *d)
 {
-       return d->state_use_accessors & IRQD_PER_CPU;
+       return __irqd_to_state(d) & IRQD_PER_CPU;
 }
 
 static inline bool irqd_can_balance(struct irq_data *d)
 {
-       return !(d->state_use_accessors & (IRQD_PER_CPU | IRQD_NO_BALANCING));
+       return !(__irqd_to_state(d) & (IRQD_PER_CPU | IRQD_NO_BALANCING));
 }
 
 static inline bool irqd_affinity_was_set(struct irq_data *d)
 {
-       return d->state_use_accessors & IRQD_AFFINITY_SET;
+       return __irqd_to_state(d) & IRQD_AFFINITY_SET;
 }
 
 static inline void irqd_mark_affinity_was_set(struct irq_data *d)
 {
-       d->state_use_accessors |= IRQD_AFFINITY_SET;
+       __irqd_to_state(d) |= IRQD_AFFINITY_SET;
 }
 
 static inline u32 irqd_get_trigger_type(struct irq_data *d)
 {
-       return d->state_use_accessors & IRQD_TRIGGER_MASK;
+       return __irqd_to_state(d) & IRQD_TRIGGER_MASK;
 }
 
 /*
@@ -233,43 +243,43 @@ static inline u32 irqd_get_trigger_type(struct irq_data *d)
  */
 static inline void irqd_set_trigger_type(struct irq_data *d, u32 type)
 {
-       d->state_use_accessors &= ~IRQD_TRIGGER_MASK;
-       d->state_use_accessors |= type & IRQD_TRIGGER_MASK;
+       __irqd_to_state(d) &= ~IRQD_TRIGGER_MASK;
+       __irqd_to_state(d) |= type & IRQD_TRIGGER_MASK;
 }
 
 static inline bool irqd_is_level_type(struct irq_data *d)
 {
-       return d->state_use_accessors & IRQD_LEVEL;
+       return __irqd_to_state(d) & IRQD_LEVEL;
 }
 
 static inline bool irqd_is_wakeup_set(struct irq_data *d)
 {
-       return d->state_use_accessors & IRQD_WAKEUP_STATE;
+       return __irqd_to_state(d) & IRQD_WAKEUP_STATE;
 }
 
 static inline bool irqd_can_move_in_process_context(struct irq_data *d)
 {
-       return d->state_use_accessors & IRQD_MOVE_PCNTXT;
+       return __irqd_to_state(d) & IRQD_MOVE_PCNTXT;
 }
 
 static inline bool irqd_irq_disabled(struct irq_data *d)
 {
-       return d->state_use_accessors & IRQD_IRQ_DISABLED;
+       return __irqd_to_state(d) & IRQD_IRQ_DISABLED;
 }
 
 static inline bool irqd_irq_masked(struct irq_data *d)
 {
-       return d->state_use_accessors & IRQD_IRQ_MASKED;
+       return __irqd_to_state(d) & IRQD_IRQ_MASKED;
 }
 
 static inline bool irqd_irq_inprogress(struct irq_data *d)
 {
-       return d->state_use_accessors & IRQD_IRQ_INPROGRESS;
+       return __irqd_to_state(d) & IRQD_IRQ_INPROGRESS;
 }
 
 static inline bool irqd_is_wakeup_armed(struct irq_data *d)
 {
-       return d->state_use_accessors & IRQD_WAKEUP_ARMED;
+       return __irqd_to_state(d) & IRQD_WAKEUP_ARMED;
 }
 
 
@@ -280,12 +290,12 @@ static inline bool irqd_is_wakeup_armed(struct irq_data *d)
  */
 static inline void irqd_set_chained_irq_inprogress(struct irq_data *d)
 {
-       d->state_use_accessors |= IRQD_IRQ_INPROGRESS;
+       __irqd_to_state(d) |= IRQD_IRQ_INPROGRESS;
 }
 
 static inline void irqd_clr_chained_irq_inprogress(struct irq_data *d)
 {
-       d->state_use_accessors &= ~IRQD_IRQ_INPROGRESS;
+       __irqd_to_state(d) &= ~IRQD_IRQ_INPROGRESS;
 }
 
 static inline irq_hw_number_t irqd_to_hwirq(struct irq_data *d)
@@ -327,6 +337,7 @@ static inline irq_hw_number_t irqd_to_hwirq(struct irq_data *d)
  * @irq_write_msi_msg: optional to write message content for MSI
  * @irq_get_irqchip_state:     return the internal state of an interrupt
  * @irq_set_irqchip_state:     set the internal state of a interrupt
+ * @irq_set_vcpu_affinity:     optional to target a vCPU in a virtual machine
  * @flags:             chip specific flags
  */
 struct irq_chip {
@@ -369,6 +380,8 @@ struct irq_chip {
        int             (*irq_get_irqchip_state)(struct irq_data *data, enum irqchip_irq_state which, bool *state);
        int             (*irq_set_irqchip_state)(struct irq_data *data, enum irqchip_irq_state which, bool state);
 
+       int             (*irq_set_vcpu_affinity)(struct irq_data *data, void *vcpu_info);
+
        unsigned long   flags;
 };
 
@@ -422,6 +435,7 @@ extern void irq_cpu_online(void);
 extern void irq_cpu_offline(void);
 extern int irq_set_affinity_locked(struct irq_data *data,
                                   const struct cpumask *cpumask, bool force);
+extern int irq_set_vcpu_affinity(unsigned int irq, void *vcpu_info);
 
 #if defined(CONFIG_SMP) && defined(CONFIG_GENERIC_PENDING_IRQ)
 void irq_move_irq(struct irq_data *data);
@@ -458,6 +472,8 @@ extern void handle_nested_irq(unsigned int irq);
 
 extern int irq_chip_compose_msi_msg(struct irq_data *data, struct msi_msg *msg);
 #ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
+extern void irq_chip_enable_parent(struct irq_data *data);
+extern void irq_chip_disable_parent(struct irq_data *data);
 extern void irq_chip_ack_parent(struct irq_data *data);
 extern int irq_chip_retrigger_hierarchy(struct irq_data *data);
 extern void irq_chip_mask_parent(struct irq_data *data);
@@ -467,6 +483,8 @@ extern int irq_chip_set_affinity_parent(struct irq_data *data,
                                        const struct cpumask *dest,
                                        bool force);
 extern int irq_chip_set_wake_parent(struct irq_data *data, unsigned int on);
+extern int irq_chip_set_vcpu_affinity_parent(struct irq_data *data,
+                                            void *vcpu_info);
 #endif
 
 /* Handling of unhandled and spurious interrupts: */
@@ -517,6 +535,15 @@ irq_set_chained_handler(unsigned int irq, irq_flow_handler_t handle)
        __irq_set_handler(irq, handle, 1, NULL);
 }
 
+/*
+ * Set a highlevel chained flow handler and its data for a given IRQ.
+ * (a chained handler is automatically enabled and set to
+ *  IRQ_NOREQUEST, IRQ_NOPROBE, and IRQ_NOTHREAD)
+ */
+void
+irq_set_chained_handler_and_data(unsigned int irq, irq_flow_handler_t handle,
+                                void *data);
+
 void irq_modify_status(unsigned int irq, unsigned long clr, unsigned long set);
 
 static inline void irq_set_status_flags(unsigned int irq, unsigned long set)
@@ -624,6 +651,23 @@ static inline u32 irq_get_trigger_type(unsigned int irq)
        return d ? irqd_get_trigger_type(d) : 0;
 }
 
+static inline int irq_data_get_node(struct irq_data *d)
+{
+       return d->node;
+}
+
+static inline struct cpumask *irq_get_affinity_mask(int irq)
+{
+       struct irq_data *d = irq_get_irq_data(irq);
+
+       return d ? d->affinity : NULL;
+}
+
+static inline struct cpumask *irq_data_get_affinity_mask(struct irq_data *d)
+{
+       return d->affinity;
+}
+
 unsigned int arch_dynirq_lower_bound(unsigned int from);
 
 int __irq_alloc_descs(int irq, unsigned int from, unsigned int cnt, int node,
index dd1109fb241e42263e5851ddbc325f469c42a87c..c52d1480f272448a18ae5ea8400328c82f0c76da 100644 (file)
@@ -17,7 +17,7 @@ struct pt_regs;
 
 /**
  * struct irq_desc - interrupt descriptor
- * @irq_data:          per irq and chip data passed down to chip functions
+ * @irq_common_data:   per irq and chip data passed down to chip functions
  * @kstat_irqs:                irq stats per cpu
  * @handle_irq:                highlevel irq-events handler
  * @preflow_handler:   handler called before the flow handler (currently used by sparc)
@@ -47,6 +47,7 @@ struct pt_regs;
  * @name:              flow handler name for /proc/interrupts output
  */
 struct irq_desc {
+       struct irq_common_data  irq_common_data;
        struct irq_data         irq_data;
        unsigned int __percpu   *kstat_irqs;
        irq_flow_handler_t      handle_irq;
@@ -93,6 +94,15 @@ struct irq_desc {
 extern struct irq_desc irq_desc[NR_IRQS];
 #endif
 
+static inline struct irq_desc *irq_data_to_desc(struct irq_data *data)
+{
+#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
+       return irq_to_desc(data->irq);
+#else
+       return container_of(data, struct irq_desc, irq_data);
+#endif
+}
+
 static inline struct irq_data *irq_desc_get_irq_data(struct irq_desc *desc)
 {
        return &desc->irq_data;
index 676d7306a3609cc9aaccb090f71000f965f0e3bd..744ac0ec98eb2c9e094f8ebdcfd71aebcdd32026 100644 (file)
@@ -258,6 +258,10 @@ int irq_domain_xlate_onetwocell(struct irq_domain *d, struct device_node *ctrlr,
 /* V2 interfaces to support hierarchy IRQ domains. */
 extern struct irq_data *irq_domain_get_irq_data(struct irq_domain *domain,
                                                unsigned int virq);
+extern void irq_domain_set_info(struct irq_domain *domain, unsigned int virq,
+                               irq_hw_number_t hwirq, struct irq_chip *chip,
+                               void *chip_data, irq_flow_handler_t handler,
+                               void *handler_data, const char *handler_name);
 #ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
 extern struct irq_domain *irq_domain_add_hierarchy(struct irq_domain *parent,
                        unsigned int flags, unsigned int size,
@@ -281,10 +285,6 @@ extern int irq_domain_set_hwirq_and_chip(struct irq_domain *domain,
                                         irq_hw_number_t hwirq,
                                         struct irq_chip *chip,
                                         void *chip_data);
-extern void irq_domain_set_info(struct irq_domain *domain, unsigned int virq,
-                               irq_hw_number_t hwirq, struct irq_chip *chip,
-                               void *chip_data, irq_flow_handler_t handler,
-                               void *handler_data, const char *handler_name);
 extern void irq_domain_reset_irq_data(struct irq_data *irq_data);
 extern void irq_domain_free_irqs_common(struct irq_domain *domain,
                                        unsigned int virq,
index c367cbdf73ab1a5b83f1af48c848be21b466167d..535fd3bb1ba889d77afc3d7ad25c093810b5bfd6 100644 (file)
@@ -7,6 +7,7 @@
 #include <linux/time.h>
 #include <linux/timex.h>
 #include <asm/param.h>                 /* for HZ */
+#include <generated/timeconst.h>
 
 /*
  * The following defines establish the engineering parameters of the PLL
@@ -288,8 +289,133 @@ static inline u64 jiffies_to_nsecs(const unsigned long j)
        return (u64)jiffies_to_usecs(j) * NSEC_PER_USEC;
 }
 
-extern unsigned long msecs_to_jiffies(const unsigned int m);
-extern unsigned long usecs_to_jiffies(const unsigned int u);
+extern unsigned long __msecs_to_jiffies(const unsigned int m);
+#if HZ <= MSEC_PER_SEC && !(MSEC_PER_SEC % HZ)
+/*
+ * HZ is equal to or smaller than 1000, and 1000 is a nice round
+ * multiple of HZ, divide with the factor between them, but round
+ * upwards:
+ */
+static inline unsigned long _msecs_to_jiffies(const unsigned int m)
+{
+       return (m + (MSEC_PER_SEC / HZ) - 1) / (MSEC_PER_SEC / HZ);
+}
+#elif HZ > MSEC_PER_SEC && !(HZ % MSEC_PER_SEC)
+/*
+ * HZ is larger than 1000, and HZ is a nice round multiple of 1000 -
+ * simply multiply with the factor between them.
+ *
+ * But first make sure the multiplication result cannot overflow:
+ */
+static inline unsigned long _msecs_to_jiffies(const unsigned int m)
+{
+       if (m > jiffies_to_msecs(MAX_JIFFY_OFFSET))
+               return MAX_JIFFY_OFFSET;
+       return m * (HZ / MSEC_PER_SEC);
+}
+#else
+/*
+ * Generic case - multiply, round and divide. But first check that if
+ * we are doing a net multiplication, that we wouldn't overflow:
+ */
+static inline unsigned long _msecs_to_jiffies(const unsigned int m)
+{
+       if (HZ > MSEC_PER_SEC && m > jiffies_to_msecs(MAX_JIFFY_OFFSET))
+               return MAX_JIFFY_OFFSET;
+
+       return (MSEC_TO_HZ_MUL32 * m + MSEC_TO_HZ_ADJ32) >> MSEC_TO_HZ_SHR32;
+}
+#endif
+/**
+ * msecs_to_jiffies: - convert milliseconds to jiffies
+ * @m: time in milliseconds
+ *
+ * conversion is done as follows:
+ *
+ * - negative values mean 'infinite timeout' (MAX_JIFFY_OFFSET)
+ *
+ * - 'too large' values [that would result in larger than
+ *   MAX_JIFFY_OFFSET values] mean 'infinite timeout' too.
+ *
+ * - all other values are converted to jiffies by either multiplying
+ *   the input value by a factor or dividing it with a factor and
+ *   handling any 32-bit overflows.
+ *   for the details see __msecs_to_jiffies()
+ *
+ * msecs_to_jiffies() checks for the passed in value being a constant
+ * via __builtin_constant_p() allowing gcc to eliminate most of the
+ * code, __msecs_to_jiffies() is called if the value passed does not
+ * allow constant folding and the actual conversion must be done at
+ * runtime.
+ * the HZ range specific helpers _msecs_to_jiffies() are called both
+ * directly here and from __msecs_to_jiffies() in the case where
+ * constant folding is not possible.
+ */
+static inline unsigned long msecs_to_jiffies(const unsigned int m)
+{
+       if (__builtin_constant_p(m)) {
+               if ((int)m < 0)
+                       return MAX_JIFFY_OFFSET;
+               return _msecs_to_jiffies(m);
+       } else {
+               return __msecs_to_jiffies(m);
+       }
+}
+
+extern unsigned long __usecs_to_jiffies(const unsigned int u);
+#if HZ <= USEC_PER_SEC && !(USEC_PER_SEC % HZ)
+static inline unsigned long _usecs_to_jiffies(const unsigned int u)
+{
+       return (u + (USEC_PER_SEC / HZ) - 1) / (USEC_PER_SEC / HZ);
+}
+#elif HZ > USEC_PER_SEC && !(HZ % USEC_PER_SEC)
+static inline unsigned long _usecs_to_jiffies(const unsigned int u)
+{
+       return u * (HZ / USEC_PER_SEC);
+}
+static inline unsigned long _usecs_to_jiffies(const unsigned int u)
+{
+#else
+static inline unsigned long _usecs_to_jiffies(const unsigned int u)
+{
+       return (USEC_TO_HZ_MUL32 * u + USEC_TO_HZ_ADJ32)
+               >> USEC_TO_HZ_SHR32;
+}
+#endif
+
+/**
+ * usecs_to_jiffies: - convert microseconds to jiffies
+ * @u: time in microseconds
+ *
+ * conversion is done as follows:
+ *
+ * - 'too large' values [that would result in larger than
+ *   MAX_JIFFY_OFFSET values] mean 'infinite timeout' too.
+ *
+ * - all other values are converted to jiffies by either multiplying
+ *   the input value by a factor or dividing it with a factor and
+ *   handling any 32-bit overflows as for msecs_to_jiffies.
+ *
+ * usecs_to_jiffies() checks for the passed in value being a constant
+ * via __builtin_constant_p() allowing gcc to eliminate most of the
+ * code, __usecs_to_jiffies() is called if the value passed does not
+ * allow constant folding and the actual conversion must be done at
+ * runtime.
+ * the HZ range specific helpers _usecs_to_jiffies() are called both
+ * directly here and from __msecs_to_jiffies() in the case where
+ * constant folding is not possible.
+ */
+static inline unsigned long usecs_to_jiffies(const unsigned int u)
+{
+       if (__builtin_constant_p(u)) {
+               if (u > jiffies_to_usecs(MAX_JIFFY_OFFSET))
+                       return MAX_JIFFY_OFFSET;
+               return _usecs_to_jiffies(u);
+       } else {
+               return __usecs_to_jiffies(u);
+       }
+}
+
 extern unsigned long timespec_to_jiffies(const struct timespec *value);
 extern void jiffies_to_timespec(const unsigned long jiffies,
                                struct timespec *value);
index ad45054309a0fcaea6d12392cf02560f3d46ab95..9564fd78c547b6128ddf8304639e2215b6190e3f 100644 (file)
 /* Two fragments for cross MMIO pages. */
 #define KVM_MAX_MMIO_FRAGMENTS 2
 
+#ifndef KVM_ADDRESS_SPACE_NUM
+#define KVM_ADDRESS_SPACE_NUM  1
+#endif
+
 /*
  * For the normal pfn, the highest 12 bits should be zero,
  * so we can mask bit 62 ~ bit 52  to indicate the error pfn,
@@ -134,6 +138,7 @@ static inline bool is_error_page(struct page *page)
 #define KVM_REQ_ENABLE_IBS        23
 #define KVM_REQ_DISABLE_IBS       24
 #define KVM_REQ_APIC_PAGE_RELOAD  25
+#define KVM_REQ_SMI               26
 
 #define KVM_USERSPACE_IRQ_SOURCE_ID            0
 #define KVM_IRQFD_RESAMPLE_IRQ_SOURCE_ID       1
@@ -230,6 +235,7 @@ struct kvm_vcpu {
 
        int fpu_active;
        int guest_fpu_loaded, guest_xcr0_loaded;
+       unsigned char fpu_counter;
        wait_queue_head_t wq;
        struct pid *pid;
        int sigset_active;
@@ -329,6 +335,13 @@ struct kvm_kernel_irq_routing_entry {
 #define KVM_MEM_SLOTS_NUM (KVM_USER_MEM_SLOTS + KVM_PRIVATE_MEM_SLOTS)
 #endif
 
+#ifndef __KVM_VCPU_MULTIPLE_ADDRESS_SPACE
+static inline int kvm_arch_vcpu_memslots_id(struct kvm_vcpu *vcpu)
+{
+       return 0;
+}
+#endif
+
 /*
  * Note:
  * memslots are not sorted by id anymore, please use id_to_memslot()
@@ -347,7 +360,7 @@ struct kvm {
        spinlock_t mmu_lock;
        struct mutex slots_lock;
        struct mm_struct *mm; /* userspace tied to this vm */
-       struct kvm_memslots *memslots;
+       struct kvm_memslots *memslots[KVM_ADDRESS_SPACE_NUM];
        struct srcu_struct srcu;
        struct srcu_struct irq_srcu;
 #ifdef CONFIG_KVM_APIC_ARCHITECTURE
@@ -462,13 +475,25 @@ void kvm_exit(void);
 void kvm_get_kvm(struct kvm *kvm);
 void kvm_put_kvm(struct kvm *kvm);
 
-static inline struct kvm_memslots *kvm_memslots(struct kvm *kvm)
+static inline struct kvm_memslots *__kvm_memslots(struct kvm *kvm, int as_id)
 {
-       return rcu_dereference_check(kvm->memslots,
+       return rcu_dereference_check(kvm->memslots[as_id],
                        srcu_read_lock_held(&kvm->srcu)
                        || lockdep_is_held(&kvm->slots_lock));
 }
 
+static inline struct kvm_memslots *kvm_memslots(struct kvm *kvm)
+{
+       return __kvm_memslots(kvm, 0);
+}
+
+static inline struct kvm_memslots *kvm_vcpu_memslots(struct kvm_vcpu *vcpu)
+{
+       int as_id = kvm_arch_vcpu_memslots_id(vcpu);
+
+       return __kvm_memslots(vcpu->kvm, as_id);
+}
+
 static inline struct kvm_memory_slot *
 id_to_memslot(struct kvm_memslots *slots, int id)
 {
@@ -500,21 +525,22 @@ enum kvm_mr_change {
 };
 
 int kvm_set_memory_region(struct kvm *kvm,
-                         struct kvm_userspace_memory_region *mem);
+                         const struct kvm_userspace_memory_region *mem);
 int __kvm_set_memory_region(struct kvm *kvm,
-                           struct kvm_userspace_memory_region *mem);
+                           const struct kvm_userspace_memory_region *mem);
 void kvm_arch_free_memslot(struct kvm *kvm, struct kvm_memory_slot *free,
                           struct kvm_memory_slot *dont);
 int kvm_arch_create_memslot(struct kvm *kvm, struct kvm_memory_slot *slot,
                            unsigned long npages);
-void kvm_arch_memslots_updated(struct kvm *kvm);
+void kvm_arch_memslots_updated(struct kvm *kvm, struct kvm_memslots *slots);
 int kvm_arch_prepare_memory_region(struct kvm *kvm,
                                struct kvm_memory_slot *memslot,
-                               struct kvm_userspace_memory_region *mem,
+                               const struct kvm_userspace_memory_region *mem,
                                enum kvm_mr_change change);
 void kvm_arch_commit_memory_region(struct kvm *kvm,
-                               struct kvm_userspace_memory_region *mem,
+                               const struct kvm_userspace_memory_region *mem,
                                const struct kvm_memory_slot *old,
+                               const struct kvm_memory_slot *new,
                                enum kvm_mr_change change);
 bool kvm_largepages_enabled(void);
 void kvm_disable_largepages(void);
@@ -524,8 +550,8 @@ void kvm_arch_flush_shadow_all(struct kvm *kvm);
 void kvm_arch_flush_shadow_memslot(struct kvm *kvm,
                                   struct kvm_memory_slot *slot);
 
-int gfn_to_page_many_atomic(struct kvm *kvm, gfn_t gfn, struct page **pages,
-                           int nr_pages);
+int gfn_to_page_many_atomic(struct kvm_memory_slot *slot, gfn_t gfn,
+                           struct page **pages, int nr_pages);
 
 struct page *gfn_to_page(struct kvm *kvm, gfn_t gfn);
 unsigned long gfn_to_hva(struct kvm *kvm, gfn_t gfn);
@@ -538,13 +564,13 @@ void kvm_release_page_dirty(struct page *page);
 void kvm_set_page_accessed(struct page *page);
 
 pfn_t gfn_to_pfn_atomic(struct kvm *kvm, gfn_t gfn);
-pfn_t gfn_to_pfn_async(struct kvm *kvm, gfn_t gfn, bool *async,
-                      bool write_fault, bool *writable);
 pfn_t gfn_to_pfn(struct kvm *kvm, gfn_t gfn);
 pfn_t gfn_to_pfn_prot(struct kvm *kvm, gfn_t gfn, bool write_fault,
                      bool *writable);
 pfn_t gfn_to_pfn_memslot(struct kvm_memory_slot *slot, gfn_t gfn);
 pfn_t gfn_to_pfn_memslot_atomic(struct kvm_memory_slot *slot, gfn_t gfn);
+pfn_t __gfn_to_pfn_memslot(struct kvm_memory_slot *slot, gfn_t gfn, bool atomic,
+                          bool *async, bool write_fault, bool *writable);
 
 void kvm_release_pfn_clean(pfn_t pfn);
 void kvm_set_pfn_dirty(pfn_t pfn);
@@ -573,6 +599,25 @@ int kvm_is_visible_gfn(struct kvm *kvm, gfn_t gfn);
 unsigned long kvm_host_page_size(struct kvm *kvm, gfn_t gfn);
 void mark_page_dirty(struct kvm *kvm, gfn_t gfn);
 
+struct kvm_memslots *kvm_vcpu_memslots(struct kvm_vcpu *vcpu);
+struct kvm_memory_slot *kvm_vcpu_gfn_to_memslot(struct kvm_vcpu *vcpu, gfn_t gfn);
+pfn_t kvm_vcpu_gfn_to_pfn_atomic(struct kvm_vcpu *vcpu, gfn_t gfn);
+pfn_t kvm_vcpu_gfn_to_pfn(struct kvm_vcpu *vcpu, gfn_t gfn);
+struct page *kvm_vcpu_gfn_to_page(struct kvm_vcpu *vcpu, gfn_t gfn);
+unsigned long kvm_vcpu_gfn_to_hva(struct kvm_vcpu *vcpu, gfn_t gfn);
+unsigned long kvm_vcpu_gfn_to_hva_prot(struct kvm_vcpu *vcpu, gfn_t gfn, bool *writable);
+int kvm_vcpu_read_guest_page(struct kvm_vcpu *vcpu, gfn_t gfn, void *data, int offset,
+                            int len);
+int kvm_vcpu_read_guest_atomic(struct kvm_vcpu *vcpu, gpa_t gpa, void *data,
+                              unsigned long len);
+int kvm_vcpu_read_guest(struct kvm_vcpu *vcpu, gpa_t gpa, void *data,
+                       unsigned long len);
+int kvm_vcpu_write_guest_page(struct kvm_vcpu *vcpu, gfn_t gfn, const void *data,
+                             int offset, int len);
+int kvm_vcpu_write_guest(struct kvm_vcpu *vcpu, gpa_t gpa, const void *data,
+                        unsigned long len);
+void kvm_vcpu_mark_page_dirty(struct kvm_vcpu *vcpu, gfn_t gfn);
+
 void kvm_vcpu_block(struct kvm_vcpu *vcpu);
 void kvm_vcpu_kick(struct kvm_vcpu *vcpu);
 int kvm_vcpu_yield_to(struct kvm_vcpu *target);
@@ -762,16 +807,10 @@ static inline void kvm_iommu_unmap_pages(struct kvm *kvm,
 }
 #endif
 
-static inline void kvm_guest_enter(void)
+/* must be called with irqs disabled */
+static inline void __kvm_guest_enter(void)
 {
-       unsigned long flags;
-
-       BUG_ON(preemptible());
-
-       local_irq_save(flags);
        guest_enter();
-       local_irq_restore(flags);
-
        /* KVM does not hold any references to rcu protected data when it
         * switches CPU into a guest mode. In fact switching to a guest mode
         * is very similar to exiting to userspace from rcu point of view. In
@@ -783,12 +822,27 @@ static inline void kvm_guest_enter(void)
                rcu_virt_note_context_switch(smp_processor_id());
 }
 
+/* must be called with irqs disabled */
+static inline void __kvm_guest_exit(void)
+{
+       guest_exit();
+}
+
+static inline void kvm_guest_enter(void)
+{
+       unsigned long flags;
+
+       local_irq_save(flags);
+       __kvm_guest_enter();
+       local_irq_restore(flags);
+}
+
 static inline void kvm_guest_exit(void)
 {
        unsigned long flags;
 
        local_irq_save(flags);
-       guest_exit();
+       __kvm_guest_exit();
        local_irq_restore(flags);
 }
 
index 931da7e917cf71deceae10a11507f89486fe9b6d..1b47a185c2f0b5a567c6cbb97f96d2d050cf79cb 100644 (file)
@@ -28,6 +28,7 @@ struct kvm_run;
 struct kvm_userspace_memory_region;
 struct kvm_vcpu;
 struct kvm_vcpu_init;
+struct kvm_memslots;
 
 enum kvm_mr_change;
 
index 0081f000e34b30f0ba0f6562274bb33a9ae0e74c..c92ebd100d9b653e25ed9accc4d1ac4569bea784 100644 (file)
@@ -52,10 +52,15 @@ struct lglock {
        static struct lglock name = { .lock = &name ## _lock }
 
 void lg_lock_init(struct lglock *lg, char *name);
+
 void lg_local_lock(struct lglock *lg);
 void lg_local_unlock(struct lglock *lg);
 void lg_local_lock_cpu(struct lglock *lg, int cpu);
 void lg_local_unlock_cpu(struct lglock *lg, int cpu);
+
+void lg_double_lock(struct lglock *lg, int cpu1, int cpu2);
+void lg_double_unlock(struct lglock *lg, int cpu1, int cpu2);
+
 void lg_global_lock(struct lglock *lg);
 void lg_global_unlock(struct lglock *lg);
 
index 28aeae46f355fdfdc96f28dd5a091dacbc85e9b8..51cb312d9bb9786682d3255f8a0cac6d9f670ce0 100644 (file)
@@ -134,7 +134,6 @@ enum {
        ATA_ALL_DEVICES         = (1 << ATA_MAX_DEVICES) - 1,
 
        ATA_SHT_EMULATED        = 1,
-       ATA_SHT_CMD_PER_LUN     = 1,
        ATA_SHT_THIS_ID         = -1,
        ATA_SHT_USE_CLUSTERING  = 1,
 
@@ -1364,7 +1363,6 @@ extern struct device_attribute *ata_common_sdev_attrs[];
        .can_queue              = ATA_DEF_QUEUE,                \
        .tag_alloc_policy       = BLK_TAG_ALLOC_RR,             \
        .this_id                = ATA_SHT_THIS_ID,              \
-       .cmd_per_lun            = ATA_SHT_CMD_PER_LUN,          \
        .emulated               = ATA_SHT_EMULATED,             \
        .use_clustering         = ATA_SHT_USE_CLUSTERING,       \
        .proc_name              = drv_name,                     \
index ee6dbb39a809811ca6aa563ea125fc9bb0af4756..31db7a05dd369094dea01c1ad109e1715f936c75 100644 (file)
@@ -99,7 +99,7 @@ struct klp_object {
        struct klp_func *funcs;
 
        /* internal */
-       struct kobject *kobj;
+       struct kobject kobj;
        struct module *mod;
        enum klp_state state;
 };
@@ -123,6 +123,12 @@ struct klp_patch {
        enum klp_state state;
 };
 
+#define klp_for_each_object(patch, obj) \
+       for (obj = patch->objs; obj->funcs; obj++)
+
+#define klp_for_each_func(obj, func) \
+       for (func = obj->funcs; func->old_name; func++)
+
 int klp_register_patch(struct klp_patch *);
 int klp_unregister_patch(struct klp_patch *);
 int klp_enable_patch(struct klp_patch *);
index 066ba4157541a94b0bfadf4b9fb2e245fc75ab8e..2722111591a398a8ad37e9001e1453d43a15cb1d 100644 (file)
@@ -130,8 +130,8 @@ enum bounce_type {
 };
 
 struct lock_class_stats {
-       unsigned long                   contention_point[4];
-       unsigned long                   contending_point[4];
+       unsigned long                   contention_point[LOCKSTAT_POINTS];
+       unsigned long                   contending_point[LOCKSTAT_POINTS];
        struct lock_time                read_waittime;
        struct lock_time                write_waittime;
        struct lock_time                read_holdtime;
index 611b69fa85941ebae8d4f61ab61221a469e1c079..1f7bc630d2252618ea940e75675cc39013b052fb 100644 (file)
@@ -54,11 +54,16 @@ struct mbus_dram_target_info
  */
 #ifdef CONFIG_PLAT_ORION
 extern const struct mbus_dram_target_info *mv_mbus_dram_info(void);
+extern const struct mbus_dram_target_info *mv_mbus_dram_info_nooverlap(void);
 #else
 static inline const struct mbus_dram_target_info *mv_mbus_dram_info(void)
 {
        return NULL;
 }
+static inline const struct mbus_dram_target_info *mv_mbus_dram_info_nooverlap(void)
+{
+       return NULL;
+}
 #endif
 
 int mvebu_mbus_save_cpu_target(u32 *store_addr);
index 16a498f48169a186d45ccd74366873e3d6878ffe..2f434f4f79a1ebd1ce06383afc0522a150f75600 100644 (file)
@@ -117,6 +117,7 @@ struct arizona {
        int num_core_supplies;
        struct regulator_bulk_data core_supplies[ARIZONA_MAX_CORE_SUPPLIES];
        struct regulator *dcvdd;
+       bool has_fully_powered_off;
 
        struct arizona_pdata pdata;
 
@@ -153,7 +154,15 @@ int arizona_request_irq(struct arizona *arizona, int irq, char *name,
 void arizona_free_irq(struct arizona *arizona, int irq, void *data);
 int arizona_set_irq_wake(struct arizona *arizona, int irq, int on);
 
+#ifdef CONFIG_MFD_WM5102
 int wm5102_patch(struct arizona *arizona);
+#else
+static inline int wm5102_patch(struct arizona *arizona)
+{
+       return 0;
+}
+#endif
+
 int wm5110_patch(struct arizona *arizona);
 int wm8997_patch(struct arizona *arizona);
 
index 1789cb0f4f170171d4e37fc20192906a44a4eb44..f6722677e6d02af7e5b2c1f057f54fe7968e41f5 100644 (file)
@@ -156,7 +156,10 @@ struct arizona_pdata {
        /** MICBIAS configurations */
        struct arizona_micbias micbias[ARIZONA_MAX_MICBIAS];
 
-       /** Mode of input structures */
+       /**
+        * Mode of input structures
+        * One of the ARIZONA_INMODE_xxx values
+        */
        int inmode[ARIZONA_MAX_INPUT];
 
        /** Mode for outputs */
index aacc10d7789c339aedb5e3e2d4d420f207c2e2ee..3499d36e60672fa3c80a8f7beba87ed0b5a37b67 100644 (file)
 #define ARIZONA_IN1_DMIC_SUP_MASK                0x1800  /* IN1_DMIC_SUP - [12:11] */
 #define ARIZONA_IN1_DMIC_SUP_SHIFT                   11  /* IN1_DMIC_SUP - [12:11] */
 #define ARIZONA_IN1_DMIC_SUP_WIDTH                    2  /* IN1_DMIC_SUP - [12:11] */
-#define ARIZONA_IN1_MODE_MASK                    0x0600  /* IN1_MODE - [10:9] */
-#define ARIZONA_IN1_MODE_SHIFT                        9  /* IN1_MODE - [10:9] */
-#define ARIZONA_IN1_MODE_WIDTH                        2  /* IN1_MODE - [10:9] */
+#define ARIZONA_IN1_MODE_MASK                    0x0400  /* IN1_MODE - [10] */
+#define ARIZONA_IN1_MODE_SHIFT                       10  /* IN1_MODE - [10] */
+#define ARIZONA_IN1_MODE_WIDTH                        1  /* IN1_MODE - [10] */
+#define ARIZONA_IN1_SINGLE_ENDED_MASK            0x0200  /* IN1_MODE - [9] */
+#define ARIZONA_IN1_SINGLE_ENDED_SHIFT                9  /* IN1_MODE - [9] */
+#define ARIZONA_IN1_SINGLE_ENDED_WIDTH                1  /* IN1_MODE - [9] */
 #define ARIZONA_IN1L_PGA_VOL_MASK                0x00FE  /* IN1L_PGA_VOL - [7:1] */
 #define ARIZONA_IN1L_PGA_VOL_SHIFT                    1  /* IN1L_PGA_VOL - [7:1] */
 #define ARIZONA_IN1L_PGA_VOL_WIDTH                    7  /* IN1L_PGA_VOL - [7:1] */
 #define ARIZONA_IN2_DMIC_SUP_MASK                0x1800  /* IN2_DMIC_SUP - [12:11] */
 #define ARIZONA_IN2_DMIC_SUP_SHIFT                   11  /* IN2_DMIC_SUP - [12:11] */
 #define ARIZONA_IN2_DMIC_SUP_WIDTH                    2  /* IN2_DMIC_SUP - [12:11] */
-#define ARIZONA_IN2_MODE_MASK                    0x0600  /* IN2_MODE - [10:9] */
-#define ARIZONA_IN2_MODE_SHIFT                        9  /* IN2_MODE - [10:9] */
-#define ARIZONA_IN2_MODE_WIDTH                        2  /* IN2_MODE - [10:9] */
+#define ARIZONA_IN2_MODE_MASK                    0x0400  /* IN2_MODE - [10] */
+#define ARIZONA_IN2_MODE_SHIFT                       10  /* IN2_MODE - [10] */
+#define ARIZONA_IN2_MODE_WIDTH                        1  /* IN2_MODE - [10] */
+#define ARIZONA_IN2_SINGLE_ENDED_MASK            0x0200  /* IN2_MODE - [9] */
+#define ARIZONA_IN2_SINGLE_ENDED_SHIFT                9  /* IN2_MODE - [9] */
+#define ARIZONA_IN2_SINGLE_ENDED_WIDTH                1  /* IN2_MODE - [9] */
 #define ARIZONA_IN2L_PGA_VOL_MASK                0x00FE  /* IN2L_PGA_VOL - [7:1] */
 #define ARIZONA_IN2L_PGA_VOL_SHIFT                    1  /* IN2L_PGA_VOL - [7:1] */
 #define ARIZONA_IN2L_PGA_VOL_WIDTH                    7  /* IN2L_PGA_VOL - [7:1] */
 #define ARIZONA_IN3_DMIC_SUP_MASK                0x1800  /* IN3_DMIC_SUP - [12:11] */
 #define ARIZONA_IN3_DMIC_SUP_SHIFT                   11  /* IN3_DMIC_SUP - [12:11] */
 #define ARIZONA_IN3_DMIC_SUP_WIDTH                    2  /* IN3_DMIC_SUP - [12:11] */
-#define ARIZONA_IN3_MODE_MASK                    0x0600  /* IN3_MODE - [10:9] */
-#define ARIZONA_IN3_MODE_SHIFT                        9  /* IN3_MODE - [10:9] */
-#define ARIZONA_IN3_MODE_WIDTH                        2  /* IN3_MODE - [10:9] */
+#define ARIZONA_IN3_MODE_MASK                    0x0400  /* IN3_MODE - [10] */
+#define ARIZONA_IN3_MODE_SHIFT                       10  /* IN3_MODE - [10] */
+#define ARIZONA_IN3_MODE_WIDTH                        1  /* IN3_MODE - [10] */
+#define ARIZONA_IN3_SINGLE_ENDED_MASK            0x0200  /* IN3_MODE - [9] */
+#define ARIZONA_IN3_SINGLE_ENDED_SHIFT                9  /* IN3_MODE - [9] */
+#define ARIZONA_IN3_SINGLE_ENDED_WIDTH                1  /* IN3_MODE - [9] */
 #define ARIZONA_IN3L_PGA_VOL_MASK                0x00FE  /* IN3L_PGA_VOL - [7:1] */
 #define ARIZONA_IN3L_PGA_VOL_SHIFT                    1  /* IN3L_PGA_VOL - [7:1] */
 #define ARIZONA_IN3L_PGA_VOL_WIDTH                    7  /* IN3L_PGA_VOL - [7:1] */
index dfabd6db7ddf7c1cba523a022aa5dcf008671652..02f97dc568aca0047f22494dd8814dd12d759595 100644 (file)
@@ -14,6 +14,7 @@
 enum {
        AXP202_ID = 0,
        AXP209_ID,
+       AXP221_ID,
        AXP288_ID,
        NR_AXP20X_VARIANTS,
 };
@@ -45,6 +46,28 @@ enum {
 #define AXP20X_V_LTF_DISCHRG           0x3c
 #define AXP20X_V_HTF_DISCHRG           0x3d
 
+#define AXP22X_PWR_OUT_CTRL1           0x10
+#define AXP22X_PWR_OUT_CTRL2           0x12
+#define AXP22X_PWR_OUT_CTRL3           0x13
+#define AXP22X_DLDO1_V_OUT             0x15
+#define AXP22X_DLDO2_V_OUT             0x16
+#define AXP22X_DLDO3_V_OUT             0x17
+#define AXP22X_DLDO4_V_OUT             0x18
+#define AXP22X_ELDO1_V_OUT             0x19
+#define AXP22X_ELDO2_V_OUT             0x1a
+#define AXP22X_ELDO3_V_OUT             0x1b
+#define AXP22X_DC5LDO_V_OUT            0x1c
+#define AXP22X_DCDC1_V_OUT             0x21
+#define AXP22X_DCDC2_V_OUT             0x22
+#define AXP22X_DCDC3_V_OUT             0x23
+#define AXP22X_DCDC4_V_OUT             0x24
+#define AXP22X_DCDC5_V_OUT             0x25
+#define AXP22X_DCDC23_V_RAMP_CTRL      0x27
+#define AXP22X_ALDO1_V_OUT             0x28
+#define AXP22X_ALDO2_V_OUT             0x29
+#define AXP22X_ALDO3_V_OUT             0x2a
+#define AXP22X_CHRG_CTRL3              0x35
+
 /* Interrupt */
 #define AXP20X_IRQ1_EN                 0x40
 #define AXP20X_IRQ2_EN                 0x41
@@ -100,6 +123,9 @@ enum {
 #define AXP20X_VBUS_MON                        0x8b
 #define AXP20X_OVER_TMP                        0x8f
 
+#define AXP22X_PWREN_CTRL1             0x8c
+#define AXP22X_PWREN_CTRL2             0x8d
+
 /* GPIO */
 #define AXP20X_GPIO0_CTRL              0x90
 #define AXP20X_LDO5_V_OUT              0x91
@@ -108,6 +134,11 @@ enum {
 #define AXP20X_GPIO20_SS               0x94
 #define AXP20X_GPIO3_CTRL              0x95
 
+#define AXP22X_LDO_IO0_V_OUT           0x91
+#define AXP22X_LDO_IO1_V_OUT           0x93
+#define AXP22X_GPIO_STATE              0x94
+#define AXP22X_GPIO_PULL_DOWN          0x95
+
 /* Battery */
 #define AXP20X_CHRG_CC_31_24           0xb0
 #define AXP20X_CHRG_CC_23_16           0xb1
@@ -120,6 +151,9 @@ enum {
 #define AXP20X_CC_CTRL                 0xb8
 #define AXP20X_FG_RES                  0xb9
 
+/* AXP22X specific registers */
+#define AXP22X_BATLOW_THRES1           0xe6
+
 /* AXP288 specific registers */
 #define AXP288_PMIC_ADC_H               0x56
 #define AXP288_PMIC_ADC_L               0x57
@@ -158,6 +192,30 @@ enum {
        AXP20X_REG_ID_MAX,
 };
 
+enum {
+       AXP22X_DCDC1 = 0,
+       AXP22X_DCDC2,
+       AXP22X_DCDC3,
+       AXP22X_DCDC4,
+       AXP22X_DCDC5,
+       AXP22X_DC1SW,
+       AXP22X_DC5LDO,
+       AXP22X_ALDO1,
+       AXP22X_ALDO2,
+       AXP22X_ALDO3,
+       AXP22X_ELDO1,
+       AXP22X_ELDO2,
+       AXP22X_ELDO3,
+       AXP22X_DLDO1,
+       AXP22X_DLDO2,
+       AXP22X_DLDO3,
+       AXP22X_DLDO4,
+       AXP22X_RTC_LDO,
+       AXP22X_LDO_IO0,
+       AXP22X_LDO_IO1,
+       AXP22X_REG_ID_MAX,
+};
+
 /* IRQs */
 enum {
        AXP20X_IRQ_ACIN_OVER_V = 1,
@@ -199,6 +257,34 @@ enum {
        AXP20X_IRQ_GPIO0_INPUT,
 };
 
+enum axp22x_irqs {
+       AXP22X_IRQ_ACIN_OVER_V = 1,
+       AXP22X_IRQ_ACIN_PLUGIN,
+       AXP22X_IRQ_ACIN_REMOVAL,
+       AXP22X_IRQ_VBUS_OVER_V,
+       AXP22X_IRQ_VBUS_PLUGIN,
+       AXP22X_IRQ_VBUS_REMOVAL,
+       AXP22X_IRQ_VBUS_V_LOW,
+       AXP22X_IRQ_BATT_PLUGIN,
+       AXP22X_IRQ_BATT_REMOVAL,
+       AXP22X_IRQ_BATT_ENT_ACT_MODE,
+       AXP22X_IRQ_BATT_EXIT_ACT_MODE,
+       AXP22X_IRQ_CHARG,
+       AXP22X_IRQ_CHARG_DONE,
+       AXP22X_IRQ_BATT_TEMP_HIGH,
+       AXP22X_IRQ_BATT_TEMP_LOW,
+       AXP22X_IRQ_DIE_TEMP_HIGH,
+       AXP22X_IRQ_PEK_SHORT,
+       AXP22X_IRQ_PEK_LONG,
+       AXP22X_IRQ_LOW_PWR_LVL1,
+       AXP22X_IRQ_LOW_PWR_LVL2,
+       AXP22X_IRQ_TIMER,
+       AXP22X_IRQ_PEK_RIS_EDGE,
+       AXP22X_IRQ_PEK_FAL_EDGE,
+       AXP22X_IRQ_GPIO1_INPUT,
+       AXP22X_IRQ_GPIO0_INPUT,
+};
+
 enum axp288_irqs {
        AXP288_IRQ_VBUS_FALL     = 2,
        AXP288_IRQ_VBUS_RISE,
@@ -275,4 +361,11 @@ struct axp20x_fg_pdata {
        int thermistor_curve[MAX_THERM_CURVE_SIZE][2];
 };
 
+struct axp20x_chrg_pdata {
+       int max_cc;
+       int max_cv;
+       int def_cc;
+       int def_cv;
+};
+
 #endif /* __LINUX_MFD_AXP20X_H */
index 324a34683971e9a01513590dd11c97aeb0e7d4a3..da72671a42fa6ed14d81a875c0fb8e6a2e66284f 100644 (file)
 #define __LINUX_MFD_CROS_EC_H
 
 #include <linux/cdev.h>
+#include <linux/device.h>
 #include <linux/notifier.h>
 #include <linux/mfd/cros_ec_commands.h>
 #include <linux/mutex.h>
 
+#define CROS_EC_DEV_NAME "cros_ec"
+#define CROS_EC_DEV_PD_NAME "cros_pd"
+
+/*
+ * The EC is unresponsive for a time after a reboot command.  Add a
+ * simple delay to make sure that the bus stays locked.
+ */
+#define EC_REBOOT_DELAY_MS             50
+
+/*
+ * Max bus-specific overhead incurred by request/responses.
+ * I2C requires 1 additional byte for requests.
+ * I2C requires 2 additional bytes for responses.
+ * */
+#define EC_PROTO_VERSION_UNKNOWN       0
+#define EC_MAX_REQUEST_OVERHEAD                1
+#define EC_MAX_RESPONSE_OVERHEAD       2
+
 /*
  * Command interface between EC and AP, for LPC, I2C and SPI interfaces.
  */
@@ -42,8 +61,7 @@ enum {
  * @outsize: Outgoing length in bytes
  * @insize: Max number of bytes to accept from EC
  * @result: EC's response to the command (separate from communication failure)
- * @outdata: Outgoing data to EC
- * @indata: Where to put the incoming data from EC
+ * @data: Where to put the incoming data from EC and outgoing data to EC
  */
 struct cros_ec_command {
        uint32_t version;
@@ -51,18 +69,14 @@ struct cros_ec_command {
        uint32_t outsize;
        uint32_t insize;
        uint32_t result;
-       uint8_t outdata[EC_PROTO2_MAX_PARAM_SIZE];
-       uint8_t indata[EC_PROTO2_MAX_PARAM_SIZE];
+       uint8_t data[0];
 };
 
 /**
  * struct cros_ec_device - Information about a ChromeOS EC device
  *
- * @ec_name: name of EC device (e.g. 'chromeos-ec')
  * @phys_name: name of physical comms layer (e.g. 'i2c-4')
  * @dev: Device pointer for physical comms device
- * @vdev: Device pointer for virtual comms device
- * @cdev: Character device structure for virtual comms device
  * @was_wake_device: true if this device was set to wake the system from
  * sleep at the last suspend
  * @cmd_readmem: direct read of the EC memory-mapped region, if supported
@@ -74,6 +88,7 @@ struct cros_ec_command {
  *
  * @priv: Private data
  * @irq: Interrupt to use
+ * @id: Device id
  * @din: input buffer (for data from EC)
  * @dout: output buffer (for data to EC)
  * \note
@@ -85,41 +100,72 @@ struct cros_ec_command {
  * to using dword.
  * @din_size: size of din buffer to allocate (zero to use static din)
  * @dout_size: size of dout buffer to allocate (zero to use static dout)
- * @parent: pointer to parent device (e.g. i2c or spi device)
  * @wake_enabled: true if this device can wake the system from sleep
  * @cmd_xfer: send command to EC and get response
  *     Returns the number of bytes received if the communication succeeded, but
  *     that doesn't mean the EC was happy with the command. The caller
  *     should check msg.result for the EC's result code.
+ * @pkt_xfer: send packet to EC and get response
  * @lock: one transaction at a time
  */
 struct cros_ec_device {
 
        /* These are used by other drivers that want to talk to the EC */
-       const char *ec_name;
        const char *phys_name;
        struct device *dev;
-       struct device *vdev;
-       struct cdev cdev;
        bool was_wake_device;
        struct class *cros_class;
        int (*cmd_readmem)(struct cros_ec_device *ec, unsigned int offset,
                           unsigned int bytes, void *dest);
 
        /* These are used to implement the platform-specific interface */
+       u16 max_request;
+       u16 max_response;
+       u16 max_passthru;
+       u16 proto_version;
        void *priv;
        int irq;
-       uint8_t *din;
-       uint8_t *dout;
+       u8 *din;
+       u8 *dout;
        int din_size;
        int dout_size;
-       struct device *parent;
        bool wake_enabled;
        int (*cmd_xfer)(struct cros_ec_device *ec,
                        struct cros_ec_command *msg);
+       int (*pkt_xfer)(struct cros_ec_device *ec,
+                       struct cros_ec_command *msg);
        struct mutex lock;
 };
 
+/* struct cros_ec_platform - ChromeOS EC platform information
+ *
+ * @ec_name: name of EC device (e.g. 'cros-ec', 'cros-pd', ...)
+ * used in /dev/ and sysfs.
+ * @cmd_offset: offset to apply for each command. Set when
+ * registering a devicde behind another one.
+ */
+struct cros_ec_platform {
+       const char *ec_name;
+       u16 cmd_offset;
+};
+
+/*
+ * struct cros_ec_dev - ChromeOS EC device entry point
+ *
+ * @class_dev: Device structure used in sysfs
+ * @cdev: Character device structure in /dev
+ * @ec_dev: cros_ec_device structure to talk to the physical device
+ * @dev: pointer to the platform device
+ * @cmd_offset: offset to apply for each command.
+ */
+struct cros_ec_dev {
+       struct device class_dev;
+       struct cdev cdev;
+       struct cros_ec_device *ec_dev;
+       struct device *dev;
+       u16 cmd_offset;
+};
+
 /**
  * cros_ec_suspend - Handle a suspend operation for the ChromeOS EC device
  *
@@ -198,4 +244,16 @@ int cros_ec_remove(struct cros_ec_device *ec_dev);
  */
 int cros_ec_register(struct cros_ec_device *ec_dev);
 
+/**
+ * cros_ec_register -  Query the protocol version supported by the ChromeOS EC
+ *
+ * @ec_dev: Device to register
+ * @return 0 if ok, -ve on error
+ */
+int cros_ec_query_all(struct cros_ec_device *ec_dev);
+
+/* sysfs stuff */
+extern struct attribute_group cros_ec_attr_group;
+extern struct attribute_group cros_ec_lightbar_attr_group;
+
 #endif /* __LINUX_MFD_CROS_EC_H */
index a49cd41feea79ca38f70ccf1e4809f6dd04dacd2..13b630c10d4c07d16e958bff61f1c6f3787142dd 100644 (file)
@@ -515,7 +515,7 @@ struct ec_host_response {
 /*
  * Notes on commands:
  *
- * Each command is an 8-byte command value.  Commands which take params or
+ * Each command is an 16-bit command value.  Commands which take params or
  * return response data specify structs for that data.  If no struct is
  * specified, the command does not input or output data, respectively.
  * Parameter/response length is implicit in the structs.  Some underlying
@@ -966,7 +966,7 @@ struct rgb_s {
 /* List of tweakable parameters. NOTE: It's __packed so it can be sent in a
  * host command, but the alignment is the same regardless. Keep it that way.
  */
-struct lightbar_params {
+struct lightbar_params_v0 {
        /* Timing */
        int32_t google_ramp_up;
        int32_t google_ramp_down;
@@ -1000,32 +1000,81 @@ struct lightbar_params {
        struct rgb_s color[8];                  /* 0-3 are Google colors */
 } __packed;
 
+struct lightbar_params_v1 {
+       /* Timing */
+       int32_t google_ramp_up;
+       int32_t google_ramp_down;
+       int32_t s3s0_ramp_up;
+       int32_t s0_tick_delay[2];               /* AC=0/1 */
+       int32_t s0a_tick_delay[2];              /* AC=0/1 */
+       int32_t s0s3_ramp_down;
+       int32_t s3_sleep_for;
+       int32_t s3_ramp_up;
+       int32_t s3_ramp_down;
+       int32_t tap_tick_delay;
+       int32_t tap_display_time;
+
+       /* Tap-for-battery params */
+       uint8_t tap_pct_red;
+       uint8_t tap_pct_green;
+       uint8_t tap_seg_min_on;
+       uint8_t tap_seg_max_on;
+       uint8_t tap_seg_osc;
+       uint8_t tap_idx[3];
+
+       /* Oscillation */
+       uint8_t osc_min[2];                     /* AC=0/1 */
+       uint8_t osc_max[2];                     /* AC=0/1 */
+       uint8_t w_ofs[2];                       /* AC=0/1 */
+
+       /* Brightness limits based on the backlight and AC. */
+       uint8_t bright_bl_off_fixed[2];         /* AC=0/1 */
+       uint8_t bright_bl_on_min[2];            /* AC=0/1 */
+       uint8_t bright_bl_on_max[2];            /* AC=0/1 */
+
+       /* Battery level thresholds */
+       uint8_t battery_threshold[LB_BATTERY_LEVELS - 1];
+
+       /* Map [AC][battery_level] to color index */
+       uint8_t s0_idx[2][LB_BATTERY_LEVELS];   /* AP is running */
+       uint8_t s3_idx[2][LB_BATTERY_LEVELS];   /* AP is sleeping */
+
+       /* Color palette */
+       struct rgb_s color[8];                  /* 0-3 are Google colors */
+} __packed;
+
 struct ec_params_lightbar {
        uint8_t cmd;                  /* Command (see enum lightbar_command) */
        union {
                struct {
                        /* no args */
-               } dump, off, on, init, get_seq, get_params, version;
+               } dump, off, on, init, get_seq, get_params_v0, get_params_v1,
+                       version, get_brightness, get_demo;
 
-               struct num {
+               struct {
                        uint8_t num;
-               } brightness, seq, demo;
+               } set_brightness, seq, demo;
 
-               struct reg {
+               struct {
                        uint8_t ctrl, reg, value;
                } reg;
 
-               struct rgb {
+               struct {
                        uint8_t led, red, green, blue;
-               } rgb;
+               } set_rgb;
+
+               struct {
+                       uint8_t led;
+               } get_rgb;
 
-               struct lightbar_params set_params;
+               struct lightbar_params_v0 set_params_v0;
+               struct lightbar_params_v1 set_params_v1;
        };
 } __packed;
 
 struct ec_response_lightbar {
        union {
-               struct dump {
+               struct {
                        struct {
                                uint8_t reg;
                                uint8_t ic0;
@@ -1033,20 +1082,26 @@ struct ec_response_lightbar {
                        } vals[23];
                } dump;
 
-               struct get_seq {
+               struct  {
                        uint8_t num;
-               } get_seq;
+               } get_seq, get_brightness, get_demo;
 
-               struct lightbar_params get_params;
+               struct lightbar_params_v0 get_params_v0;
+               struct lightbar_params_v1 get_params_v1;
 
-               struct version {
+               struct {
                        uint32_t num;
                        uint32_t flags;
                } version;
 
+               struct {
+                       uint8_t red, green, blue;
+               } get_rgb;
+
                struct {
                        /* no return params */
-               } off, on, init, brightness, seq, reg, rgb, demo, set_params;
+               } off, on, init, set_brightness, seq, reg, set_rgb,
+                       demo, set_params_v0, set_params_v1;
        };
 } __packed;
 
@@ -1056,15 +1111,20 @@ enum lightbar_command {
        LIGHTBAR_CMD_OFF = 1,
        LIGHTBAR_CMD_ON = 2,
        LIGHTBAR_CMD_INIT = 3,
-       LIGHTBAR_CMD_BRIGHTNESS = 4,
+       LIGHTBAR_CMD_SET_BRIGHTNESS = 4,
        LIGHTBAR_CMD_SEQ = 5,
        LIGHTBAR_CMD_REG = 6,
-       LIGHTBAR_CMD_RGB = 7,
+       LIGHTBAR_CMD_SET_RGB = 7,
        LIGHTBAR_CMD_GET_SEQ = 8,
        LIGHTBAR_CMD_DEMO = 9,
-       LIGHTBAR_CMD_GET_PARAMS = 10,
-       LIGHTBAR_CMD_SET_PARAMS = 11,
+       LIGHTBAR_CMD_GET_PARAMS_V0 = 10,
+       LIGHTBAR_CMD_SET_PARAMS_V0 = 11,
        LIGHTBAR_CMD_VERSION = 12,
+       LIGHTBAR_CMD_GET_BRIGHTNESS = 13,
+       LIGHTBAR_CMD_GET_RGB = 14,
+       LIGHTBAR_CMD_GET_DEMO = 15,
+       LIGHTBAR_CMD_GET_PARAMS_V1 = 16,
+       LIGHTBAR_CMD_SET_PARAMS_V1 = 17,
        LIGHTBAR_NUM_CMDS
 };
 
@@ -1421,8 +1481,40 @@ struct ec_response_rtc {
 /*****************************************************************************/
 /* Port80 log access */
 
+/* Maximum entries that can be read/written in a single command */
+#define EC_PORT80_SIZE_MAX 32
+
 /* Get last port80 code from previous boot */
 #define EC_CMD_PORT80_LAST_BOOT 0x48
+#define EC_CMD_PORT80_READ 0x48
+
+enum ec_port80_subcmd {
+       EC_PORT80_GET_INFO = 0,
+       EC_PORT80_READ_BUFFER,
+};
+
+struct ec_params_port80_read {
+       uint16_t subcmd;
+       union {
+               struct {
+                       uint32_t offset;
+                       uint32_t num_entries;
+               } read_buffer;
+       };
+} __packed;
+
+struct ec_response_port80_read {
+       union {
+               struct {
+                       uint32_t writes;
+                       uint32_t history_size;
+                       uint32_t last_boot;
+               } get_info;
+               struct {
+                       uint16_t codes[EC_PORT80_SIZE_MAX];
+               } data;
+       };
+} __packed;
 
 struct ec_response_port80_last_boot {
        uint16_t code;
@@ -1782,6 +1874,7 @@ struct ec_params_gpio_set {
 /* Get GPIO value */
 #define EC_CMD_GPIO_GET 0x93
 
+/* Version 0 of input params and response */
 struct ec_params_gpio_get {
        char name[32];
 } __packed;
@@ -1789,6 +1882,38 @@ struct ec_response_gpio_get {
        uint8_t val;
 } __packed;
 
+/* Version 1 of input params and response */
+struct ec_params_gpio_get_v1 {
+       uint8_t subcmd;
+       union {
+               struct {
+                       char name[32];
+               } get_value_by_name;
+               struct {
+                       uint8_t index;
+               } get_info;
+       };
+} __packed;
+
+struct ec_response_gpio_get_v1 {
+       union {
+               struct {
+                       uint8_t val;
+               } get_value_by_name, get_count;
+               struct {
+                       uint8_t val;
+                       char name[32];
+                       uint32_t flags;
+               } get_info;
+       };
+} __packed;
+
+enum gpio_get_subcmd {
+       EC_GPIO_GET_BY_NAME = 0,
+       EC_GPIO_GET_COUNT = 1,
+       EC_GPIO_GET_INFO = 2,
+};
+
 /*****************************************************************************/
 /* I2C commands. Only available when flash write protect is unlocked. */
 
@@ -1857,13 +1982,21 @@ struct ec_params_charge_control {
 /*****************************************************************************/
 
 /*
- * Cut off battery power output if the battery supports.
+ * Cut off battery power immediately or after the host has shut down.
  *
- * For unsupported battery, just don't implement this command and lets EC
- * return EC_RES_INVALID_COMMAND.
+ * return EC_RES_INVALID_COMMAND if unsupported by a board/battery.
+ *       EC_RES_SUCCESS if the command was successful.
+ *       EC_RES_ERROR if the cut off command failed.
  */
+
 #define EC_CMD_BATTERY_CUT_OFF 0x99
 
+#define EC_BATTERY_CUTOFF_FLAG_AT_SHUTDOWN     (1 << 0)
+
+struct ec_params_battery_cutoff {
+       uint8_t flags;
+} __packed;
+
 /*****************************************************************************/
 /* USB port mux control. */
 
@@ -2141,6 +2274,32 @@ struct ec_params_sb_wr_block {
        uint16_t data[32];
 } __packed;
 
+/*****************************************************************************/
+/* Battery vendor parameters
+ *
+ * Get or set vendor-specific parameters in the battery. Implementations may
+ * differ between boards or batteries. On a set operation, the response
+ * contains the actual value set, which may be rounded or clipped from the
+ * requested value.
+ */
+
+#define EC_CMD_BATTERY_VENDOR_PARAM 0xb4
+
+enum ec_battery_vendor_param_mode {
+       BATTERY_VENDOR_PARAM_MODE_GET = 0,
+       BATTERY_VENDOR_PARAM_MODE_SET,
+};
+
+struct ec_params_battery_vendor_param {
+       uint32_t param;
+       uint32_t value;
+       uint8_t mode;
+} __packed;
+
+struct ec_response_battery_vendor_param {
+       uint32_t value;
+} __packed;
+
 /*****************************************************************************/
 /* System commands */
 
@@ -2336,6 +2495,80 @@ struct ec_params_reboot_ec {
 
 #endif  /* !__ACPI__ */
 
+/*****************************************************************************/
+/*
+ * PD commands
+ *
+ * These commands are for PD MCU communication.
+ */
+
+/* EC to PD MCU exchange status command */
+#define EC_CMD_PD_EXCHANGE_STATUS 0x100
+
+/* Status of EC being sent to PD */
+struct ec_params_pd_status {
+       int8_t batt_soc; /* battery state of charge */
+} __packed;
+
+/* Status of PD being sent back to EC */
+struct ec_response_pd_status {
+       int8_t status;        /* PD MCU status */
+       uint32_t curr_lim_ma; /* input current limit */
+} __packed;
+
+/* Set USB type-C port role and muxes */
+#define EC_CMD_USB_PD_CONTROL 0x101
+
+enum usb_pd_control_role {
+       USB_PD_CTRL_ROLE_NO_CHANGE = 0,
+       USB_PD_CTRL_ROLE_TOGGLE_ON = 1, /* == AUTO */
+       USB_PD_CTRL_ROLE_TOGGLE_OFF = 2,
+       USB_PD_CTRL_ROLE_FORCE_SINK = 3,
+       USB_PD_CTRL_ROLE_FORCE_SOURCE = 4,
+};
+
+enum usb_pd_control_mux {
+       USB_PD_CTRL_MUX_NO_CHANGE = 0,
+       USB_PD_CTRL_MUX_NONE = 1,
+       USB_PD_CTRL_MUX_USB = 2,
+       USB_PD_CTRL_MUX_DP = 3,
+       USB_PD_CTRL_MUX_DOCK = 4,
+       USB_PD_CTRL_MUX_AUTO = 5,
+};
+
+struct ec_params_usb_pd_control {
+       uint8_t port;
+       uint8_t role;
+       uint8_t mux;
+} __packed;
+
+/*****************************************************************************/
+/*
+ * Passthru commands
+ *
+ * Some platforms have sub-processors chained to each other.  For example.
+ *
+ *     AP <--> EC <--> PD MCU
+ *
+ * The top 2 bits of the command number are used to indicate which device the
+ * command is intended for.  Device 0 is always the device receiving the
+ * command; other device mapping is board-specific.
+ *
+ * When a device receives a command to be passed to a sub-processor, it passes
+ * it on with the device number set back to 0.  This allows the sub-processor
+ * to remain blissfully unaware of whether the command originated on the next
+ * device up the chain, or was passed through from the AP.
+ *
+ * In the above example, if the AP wants to send command 0x0002 to the PD MCU,
+ *     AP sends command 0x4002 to the EC
+ *     EC sends command 0x0002 to the PD MCU
+ *     EC forwards PD MCU response back to the AP
+ */
+
+/* Offset and max command number for sub-device n */
+#define EC_CMD_PASSTHRU_OFFSET(n) (0x4000 * (n))
+#define EC_CMD_PASSTHRU_MAX(n) (EC_CMD_PASSTHRU_OFFSET(n) + 0x3fff)
+
 /*****************************************************************************/
 /*
  * Deprecated constants. These constants have been renamed for clarity. The
index 956afa445998013536198dade8910c0cad3be116..5dc743fd63a62317073b7bfca7a26ced6417b850 100644 (file)
@@ -89,6 +89,6 @@ static inline int da9055_reg_update(struct da9055 *da9055, unsigned char reg,
 int da9055_device_init(struct da9055 *da9055);
 void da9055_device_exit(struct da9055 *da9055);
 
-extern struct regmap_config da9055_regmap_config;
+extern const struct regmap_config da9055_regmap_config;
 
 #endif /* __DA9055_CORE_H */
index bb995ab9a57564be1ae635ac455688e6f2f1173b..d4b72d519115a26bf4f212e20cbab3b052a2c859 100644 (file)
@@ -125,9 +125,4 @@ enum max77686_opmode {
        MAX77686_OPMODE_STANDBY,
 };
 
-struct max77686_opmode_data {
-       int id;
-       int mode;
-};
-
 #endif /* __LINUX_MFD_MAX77686_H */
index 83e80ab9450048d121b739bd23b85ccb14a39b36..f94984fb8bb248ca5d55e8c7e7bd2161e143dbab 100644 (file)
@@ -829,6 +829,12 @@ struct mlx4_dev {
        struct mlx4_vf_dev     *dev_vfs;
 };
 
+struct mlx4_clock_params {
+       u64 offset;
+       u8 bar;
+       u8 size;
+};
+
 struct mlx4_eqe {
        u8                      reserved1;
        u8                      type;
@@ -1485,4 +1491,7 @@ int mlx4_ACCESS_PTYS_REG(struct mlx4_dev *dev,
                         enum mlx4_access_reg_method method,
                         struct mlx4_ptys_reg *ptys_reg);
 
+int mlx4_get_internal_clock_params(struct mlx4_dev *dev,
+                                  struct mlx4_clock_params *params);
+
 #endif /* MLX4_DEVICE_H */
index 9a90e7523dc24d2f7f29467023c8845cbf50cff7..9ec7c93d6fa355c1760890f1265ae1d4a2dcea44 100644 (file)
@@ -696,7 +696,7 @@ int mlx5_core_dump_fill_mkey(struct mlx5_core_dev *dev, struct mlx5_core_mr *mr,
                             u32 *mkey);
 int mlx5_core_alloc_pd(struct mlx5_core_dev *dev, u32 *pdn);
 int mlx5_core_dealloc_pd(struct mlx5_core_dev *dev, u32 pdn);
-int mlx5_core_mad_ifc(struct mlx5_core_dev *dev, void *inb, void *outb,
+int mlx5_core_mad_ifc(struct mlx5_core_dev *dev, const void *inb, void *outb,
                      u16 opmod, u8 port);
 void mlx5_pagealloc_init(struct mlx5_core_dev *dev);
 void mlx5_pagealloc_cleanup(struct mlx5_core_dev *dev);
index 19f0175c0afa68a99faf3684dda69fe7967ef5a3..4d3776d25925a14d6140ce681911dd5c1a5bc133 100644 (file)
@@ -97,6 +97,7 @@ struct mmc_ext_csd {
        u8                      raw_erased_mem_count;   /* 181 */
        u8                      raw_ext_csd_structure;  /* 194 */
        u8                      raw_card_type;          /* 196 */
+       u8                      raw_driver_strength;    /* 197 */
        u8                      out_of_int_time;        /* 198 */
        u8                      raw_pwr_cl_52_195;      /* 200 */
        u8                      raw_pwr_cl_26_195;      /* 201 */
@@ -305,6 +306,7 @@ struct mmc_card {
 
        unsigned int            sd_bus_speed;   /* Bus Speed Mode set for the card */
        unsigned int            mmc_avail_type; /* supported device type by both host and card */
+       unsigned int            drive_strength; /* for UHS-I, HS200 or HS400 */
 
        struct dentry           *debugfs_root;
        struct mmc_part part[MMC_NUM_PHY_PARTITION]; /* physical partitions */
index de722d4e9d61b9a1e30e4e80f0bb7630acec9459..258daf914c6df301330b70f00d95ac95a44ac545 100644 (file)
@@ -121,6 +121,7 @@ struct mmc_data {
        struct mmc_request      *mrq;           /* associated request */
 
        unsigned int            sg_len;         /* size of scatter list */
+       int                     sg_count;       /* mapped sg entries */
        struct scatterlist      *sg;            /* I/O scatter list */
        s32                     host_cookie;    /* host private data */
 };
index 12111993a3175ede2a43a0c061f5cbaf01b50abf..5be97676f1fa029a9e0b40418bc1c37b83105d44 100644 (file)
@@ -226,12 +226,6 @@ struct dw_mci_dma_ops {
 #define DW_MCI_QUIRK_HIGHSPEED                 BIT(2)
 /* Unreliable card detection */
 #define DW_MCI_QUIRK_BROKEN_CARD_DETECTION     BIT(3)
-/* No write protect */
-#define DW_MCI_QUIRK_NO_WRITE_PROTECT          BIT(4)
-
-/* Slot level quirks */
-/* This slot has no write protect */
-#define DW_MCI_SLOT_QUIRK_NO_WRITE_PROTECT     BIT(0)
 
 struct dma_pdata;
 
index b5bedaec6223679bc00a25af801c1ae57ff17ad2..1369e54faeb7e2ee8ef3d49481751249c70bbcbd 100644 (file)
@@ -12,6 +12,7 @@
 
 #include <linux/leds.h>
 #include <linux/mutex.h>
+#include <linux/timer.h>
 #include <linux/sched.h>
 #include <linux/device.h>
 #include <linux/fault-inject.h>
@@ -131,7 +132,9 @@ struct mmc_host_ops {
 
        /* Prepare HS400 target operating frequency depending host driver */
        int     (*prepare_hs400_tuning)(struct mmc_host *host, struct mmc_ios *ios);
-       int     (*select_drive_strength)(unsigned int max_dtr, int host_drv, int card_drv);
+       int     (*select_drive_strength)(struct mmc_card *card,
+                                        unsigned int max_dtr, int host_drv,
+                                        int card_drv, int *drv_type);
        void    (*hw_reset)(struct mmc_host *host);
        void    (*card_event)(struct mmc_host *host);
 
@@ -285,6 +288,7 @@ struct mmc_host {
                                 MMC_CAP2_HS400_1_2V)
 #define MMC_CAP2_HSX00_1_2V    (MMC_CAP2_HS200_1_2V_SDR | MMC_CAP2_HS400_1_2V)
 #define MMC_CAP2_SDIO_IRQ_NOTHREAD (1 << 17)
+#define MMC_CAP2_NO_WRITE_PROTECT (1 << 18)    /* No physical write protect pin, assume that card is always read-write */
 
        mmc_pm_flag_t           pm_caps;        /* supported pm features */
 
@@ -321,10 +325,18 @@ struct mmc_host {
 #ifdef CONFIG_MMC_DEBUG
        unsigned int            removed:1;      /* host is being removed */
 #endif
+       unsigned int            can_retune:1;   /* re-tuning can be used */
+       unsigned int            doing_retune:1; /* re-tuning in progress */
+       unsigned int            retune_now:1;   /* do re-tuning at next req */
 
        int                     rescan_disable; /* disable card detection */
        int                     rescan_entered; /* used with nonremovable devices */
 
+       int                     need_retune;    /* re-tuning is needed */
+       int                     hold_retune;    /* hold off re-tuning */
+       unsigned int            retune_period;  /* re-tuning period in secs */
+       struct timer_list       retune_timer;   /* for periodic re-tuning */
+
        bool                    trigger_card_event; /* card_event necessary */
 
        struct mmc_card         *card;          /* device attached to this host */
@@ -513,4 +525,18 @@ static inline bool mmc_card_hs400(struct mmc_card *card)
        return card->host->ios.timing == MMC_TIMING_MMC_HS400;
 }
 
+void mmc_retune_timer_stop(struct mmc_host *host);
+
+static inline void mmc_retune_needed(struct mmc_host *host)
+{
+       if (host->can_retune)
+               host->need_retune = 1;
+}
+
+static inline void mmc_retune_recheck(struct mmc_host *host)
+{
+       if (host->hold_retune <= 1)
+               host->retune_now = 1;
+}
+
 #endif /* LINUX_MMC_HOST_H */
index 124f562118b8541384f05b3093f436a95be4cf00..15f2c4a0a62cbfa3d7751d91be7ebc3bbf6ec645 100644 (file)
@@ -302,6 +302,7 @@ struct _mmc_csd {
 #define EXT_CSD_REV                    192     /* RO */
 #define EXT_CSD_STRUCTURE              194     /* RO */
 #define EXT_CSD_CARD_TYPE              196     /* RO */
+#define EXT_CSD_DRIVER_STRENGTH                197     /* RO */
 #define EXT_CSD_OUT_OF_INTERRUPT_TIME  198     /* RO */
 #define EXT_CSD_PART_SWITCH_TIME        199     /* RO */
 #define EXT_CSD_PWR_CL_52_195          200     /* RO */
@@ -390,6 +391,7 @@ struct _mmc_csd {
 #define EXT_CSD_TIMING_HS      1       /* High speed */
 #define EXT_CSD_TIMING_HS200   2       /* HS200 */
 #define EXT_CSD_TIMING_HS400   3       /* HS400 */
+#define EXT_CSD_DRV_STR_SHIFT  4       /* Driver Strength shift */
 
 #define EXT_CSD_SEC_ER_EN      BIT(0)
 #define EXT_CSD_SEC_BD_BLK_EN  BIT(2)
@@ -441,4 +443,6 @@ struct _mmc_csd {
 #define MMC_SWITCH_MODE_CLEAR_BITS     0x02    /* Clear bits which are 1 in value */
 #define MMC_SWITCH_MODE_WRITE_BYTE     0x03    /* Set target to value */
 
+#define mmc_driver_type_mask(n)                (1 << (n))
+
 #endif /* LINUX_MMC_MMC_H */
index 8959604a13d32de998db146725de039fde06a3b6..fda15b6d4135d387549e1ed628ca1285b399abb0 100644 (file)
@@ -15,4 +15,6 @@ struct sdhci_pci_data {
 extern struct sdhci_pci_data *(*sdhci_pci_get_data)(struct pci_dev *pdev,
                                int slotno);
 
+extern int sdhci_pci_spt_drive_strength;
+
 #endif
index c883b86ea9649ae62ca485dd3843e51e43edc346..1e5436042eb0322f77209586944954f650aa8b17 100644 (file)
@@ -655,4 +655,16 @@ static inline void module_bug_finalize(const Elf_Ehdr *hdr,
 static inline void module_bug_cleanup(struct module *mod) {}
 #endif /* CONFIG_GENERIC_BUG */
 
+#ifdef CONFIG_MODULE_SIG
+static inline bool module_sig_ok(struct module *module)
+{
+       return module->sig_ok;
+}
+#else  /* !CONFIG_MODULE_SIG */
+static inline bool module_sig_ok(struct module *module)
+{
+       return true;
+}
+#endif /* CONFIG_MODULE_SIG */
+
 #endif /* _LINUX_MODULE_H */
index 5af1b81def49f6f98ec5783c60fb4fabe0a62c46..641b7d6fd0966dac7f49465dd0c0be9df2a560f8 100644 (file)
@@ -81,6 +81,8 @@ MPI mpi_read_from_buffer(const void *buffer, unsigned *ret_nread);
 int mpi_fromstr(MPI val, const char *str);
 u32 mpi_get_keyid(MPI a, u32 *keyid);
 void *mpi_get_buffer(MPI a, unsigned *nbytes, int *sign);
+int mpi_read_buffer(MPI a, uint8_t *buf, unsigned buf_len, unsigned *nbytes,
+                   int *sign);
 void *mpi_get_secure_buffer(MPI a, unsigned *nbytes, int *sign);
 int mpi_set_buffer(MPI a, const void *buffer, unsigned nbytes, int sign);
 
@@ -142,4 +144,17 @@ int mpi_rshift(MPI x, MPI a, unsigned n);
 /*-- mpi-inv.c --*/
 int mpi_invm(MPI x, MPI u, MPI v);
 
+/* inline functions */
+
+/**
+ * mpi_get_size() - returns max size required to store the number
+ *
+ * @a: A multi precision integer for which we want to allocate a bufer
+ *
+ * Return: size required to store the number
+ */
+static inline unsigned int mpi_get_size(MPI a)
+{
+       return a->nlimbs * BYTES_PER_MPI_LIMB;
+}
 #endif /*G10_MPI_H */
index 299d7d31fe539df197d571b584cca3423d6a1907..9b57a9b1b081d375b2a5bf0b8f28f4ee92305b18 100644 (file)
@@ -296,183 +296,19 @@ struct cfi_private {
        struct flchip chips[0];  /* per-chip data structure for each chip */
 };
 
-/*
- * Returns the command address according to the given geometry.
- */
-static inline uint32_t cfi_build_cmd_addr(uint32_t cmd_ofs,
-                               struct map_info *map, struct cfi_private *cfi)
-{
-       unsigned bankwidth = map_bankwidth(map);
-       unsigned interleave = cfi_interleave(cfi);
-       unsigned type = cfi->device_type;
-       uint32_t addr;
-       
-       addr = (cmd_ofs * type) * interleave;
-
-       /* Modify the unlock address if we are in compatibility mode.
-        * For 16bit devices on 8 bit busses
-        * and 32bit devices on 16 bit busses
-        * set the low bit of the alternating bit sequence of the address.
-        */
-       if (((type * interleave) > bankwidth) && ((cmd_ofs & 0xff) == 0xaa))
-               addr |= (type >> 1)*interleave;
-
-       return  addr;
-}
-
-/*
- * Transforms the CFI command for the given geometry (bus width & interleave).
- * It looks too long to be inline, but in the common case it should almost all
- * get optimised away.
- */
-static inline map_word cfi_build_cmd(u_long cmd, struct map_info *map, struct cfi_private *cfi)
-{
-       map_word val = { {0} };
-       int wordwidth, words_per_bus, chip_mode, chips_per_word;
-       unsigned long onecmd;
-       int i;
-
-       /* We do it this way to give the compiler a fighting chance
-          of optimising away all the crap for 'bankwidth' larger than
-          an unsigned long, in the common case where that support is
-          disabled */
-       if (map_bankwidth_is_large(map)) {
-               wordwidth = sizeof(unsigned long);
-               words_per_bus = (map_bankwidth(map)) / wordwidth; // i.e. normally 1
-       } else {
-               wordwidth = map_bankwidth(map);
-               words_per_bus = 1;
-       }
-
-       chip_mode = map_bankwidth(map) / cfi_interleave(cfi);
-       chips_per_word = wordwidth * cfi_interleave(cfi) / map_bankwidth(map);
-
-       /* First, determine what the bit-pattern should be for a single
-          device, according to chip mode and endianness... */
-       switch (chip_mode) {
-       default: BUG();
-       case 1:
-               onecmd = cmd;
-               break;
-       case 2:
-               onecmd = cpu_to_cfi16(map, cmd);
-               break;
-       case 4:
-               onecmd = cpu_to_cfi32(map, cmd);
-               break;
-       }
-
-       /* Now replicate it across the size of an unsigned long, or
-          just to the bus width as appropriate */
-       switch (chips_per_word) {
-       default: BUG();
-#if BITS_PER_LONG >= 64
-       case 8:
-               onecmd |= (onecmd << (chip_mode * 32));
-#endif
-       case 4:
-               onecmd |= (onecmd << (chip_mode * 16));
-       case 2:
-               onecmd |= (onecmd << (chip_mode * 8));
-       case 1:
-               ;
-       }
+uint32_t cfi_build_cmd_addr(uint32_t cmd_ofs,
+                               struct map_info *map, struct cfi_private *cfi);
 
-       /* And finally, for the multi-word case, replicate it
-          in all words in the structure */
-       for (i=0; i < words_per_bus; i++) {
-               val.x[i] = onecmd;
-       }
-
-       return val;
-}
+map_word cfi_build_cmd(u_long cmd, struct map_info *map, struct cfi_private *cfi);
 #define CMD(x)  cfi_build_cmd((x), map, cfi)
 
-
-static inline unsigned long cfi_merge_status(map_word val, struct map_info *map,
-                                          struct cfi_private *cfi)
-{
-       int wordwidth, words_per_bus, chip_mode, chips_per_word;
-       unsigned long onestat, res = 0;
-       int i;
-
-       /* We do it this way to give the compiler a fighting chance
-          of optimising away all the crap for 'bankwidth' larger than
-          an unsigned long, in the common case where that support is
-          disabled */
-       if (map_bankwidth_is_large(map)) {
-               wordwidth = sizeof(unsigned long);
-               words_per_bus = (map_bankwidth(map)) / wordwidth; // i.e. normally 1
-       } else {
-               wordwidth = map_bankwidth(map);
-               words_per_bus = 1;
-       }
-
-       chip_mode = map_bankwidth(map) / cfi_interleave(cfi);
-       chips_per_word = wordwidth * cfi_interleave(cfi) / map_bankwidth(map);
-
-       onestat = val.x[0];
-       /* Or all status words together */
-       for (i=1; i < words_per_bus; i++) {
-               onestat |= val.x[i];
-       }
-
-       res = onestat;
-       switch(chips_per_word) {
-       default: BUG();
-#if BITS_PER_LONG >= 64
-       case 8:
-               res |= (onestat >> (chip_mode * 32));
-#endif
-       case 4:
-               res |= (onestat >> (chip_mode * 16));
-       case 2:
-               res |= (onestat >> (chip_mode * 8));
-       case 1:
-               ;
-       }
-
-       /* Last, determine what the bit-pattern should be for a single
-          device, according to chip mode and endianness... */
-       switch (chip_mode) {
-       case 1:
-               break;
-       case 2:
-               res = cfi16_to_cpu(map, res);
-               break;
-       case 4:
-               res = cfi32_to_cpu(map, res);
-               break;
-       default: BUG();
-       }
-       return res;
-}
-
+unsigned long cfi_merge_status(map_word val, struct map_info *map,
+                                          struct cfi_private *cfi);
 #define MERGESTATUS(x) cfi_merge_status((x), map, cfi)
 
-
-/*
- * Sends a CFI command to a bank of flash for the given geometry.
- *
- * Returns the offset in flash where the command was written.
- * If prev_val is non-null, it will be set to the value at the command address,
- * before the command was written.
- */
-static inline uint32_t cfi_send_gen_cmd(u_char cmd, uint32_t cmd_addr, uint32_t base,
+uint32_t cfi_send_gen_cmd(u_char cmd, uint32_t cmd_addr, uint32_t base,
                                struct map_info *map, struct cfi_private *cfi,
-                               int type, map_word *prev_val)
-{
-       map_word val;
-       uint32_t addr = base + cfi_build_cmd_addr(cmd_addr, map, cfi);
-       val = cfi_build_cmd(cmd, map, cfi);
-
-       if (prev_val)
-               *prev_val = map_read(map, addr);
-
-       map_write(map, val, addr);
-
-       return addr - base;
-}
+                               int type, map_word *prev_val);
 
 static inline uint8_t cfi_read_query(struct map_info *map, uint32_t addr)
 {
@@ -506,15 +342,7 @@ static inline uint16_t cfi_read_query16(struct map_info *map, uint32_t addr)
        }
 }
 
-static inline void cfi_udelay(int us)
-{
-       if (us >= 1000) {
-               msleep((us+999)/1000);
-       } else {
-               udelay(us);
-               cond_resched();
-       }
-}
+void cfi_udelay(int us);
 
 int __xipram cfi_qry_present(struct map_info *map, __u32 base,
                             struct cfi_private *cfi);
index 3d4ea7eb2b68b9681a77c161669a1944994d4431..f25e2bdd188c94643efc0f3a94c7c67de81897aa 100644 (file)
@@ -26,6 +26,8 @@
 
 struct mtd_info;
 struct nand_flash_dev;
+struct device_node;
+
 /* Scan and identify a NAND device */
 extern int nand_scan(struct mtd_info *mtd, int max_chips);
 /*
@@ -542,6 +544,7 @@ struct nand_buffers {
  *                     flash device
  * @IO_ADDR_W:         [BOARDSPECIFIC] address to write the 8 I/O lines of the
  *                     flash device.
+ * @dn:                        [BOARDSPECIFIC] device node describing this instance
  * @read_byte:         [REPLACEABLE] read one byte from the chip
  * @read_word:         [REPLACEABLE] read one word from the chip
  * @write_byte:                [REPLACEABLE] write a single byte to the chip on the
@@ -644,6 +647,8 @@ struct nand_chip {
        void __iomem *IO_ADDR_R;
        void __iomem *IO_ADDR_W;
 
+       struct device_node *dn;
+
        uint8_t (*read_byte)(struct mtd_info *mtd);
        u16 (*read_word)(struct mtd_info *mtd);
        void (*write_byte)(struct mtd_info *mtd, uint8_t byte);
@@ -833,7 +838,6 @@ struct nand_manufacturers {
 extern struct nand_flash_dev nand_flash_ids[];
 extern struct nand_manufacturers nand_manuf_ids[];
 
-extern int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd);
 extern int nand_default_bbt(struct mtd_info *mtd);
 extern int nand_markbad_bbt(struct mtd_info *mtd, loff_t offs);
 extern int nand_isreserved_bbt(struct mtd_info *mtd, loff_t offs);
index c8990779f0c33b99e552ca9406621cde03f49443..d8c6334cd15005c16162f57959e20b2e09f99d03 100644 (file)
@@ -1,16 +1,15 @@
 #ifndef _LINUX_NAMEI_H
 #define _LINUX_NAMEI_H
 
-#include <linux/dcache.h>
-#include <linux/errno.h>
-#include <linux/linkage.h>
+#include <linux/kernel.h>
 #include <linux/path.h>
-
-struct vfsmount;
-struct nameidata;
+#include <linux/fcntl.h>
+#include <linux/errno.h>
 
 enum { MAX_NESTED_LINKS = 8 };
 
+#define MAXSYMLINKS 40
+
 /*
  * Type of the last component on LOOKUP_PARENT
  */
@@ -45,13 +44,29 @@ enum {LAST_NORM, LAST_ROOT, LAST_DOT, LAST_DOTDOT, LAST_BIND};
 #define LOOKUP_ROOT            0x2000
 #define LOOKUP_EMPTY           0x4000
 
-extern int user_path_at(int, const char __user *, unsigned, struct path *);
 extern int user_path_at_empty(int, const char __user *, unsigned, struct path *, int *empty);
 
-#define user_path(name, path) user_path_at(AT_FDCWD, name, LOOKUP_FOLLOW, path)
-#define user_lpath(name, path) user_path_at(AT_FDCWD, name, 0, path)
-#define user_path_dir(name, path) \
-       user_path_at(AT_FDCWD, name, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, path)
+static inline int user_path_at(int dfd, const char __user *name, unsigned flags,
+                struct path *path)
+{
+       return user_path_at_empty(dfd, name, flags, path, NULL);
+}
+
+static inline int user_path(const char __user *name, struct path *path)
+{
+       return user_path_at_empty(AT_FDCWD, name, LOOKUP_FOLLOW, path, NULL);
+}
+
+static inline int user_lpath(const char __user *name, struct path *path)
+{
+       return user_path_at_empty(AT_FDCWD, name, 0, path, NULL);
+}
+
+static inline int user_path_dir(const char __user *name, struct path *path)
+{
+       return user_path_at_empty(AT_FDCWD, name,
+                                 LOOKUP_FOLLOW | LOOKUP_DIRECTORY, path, NULL);
+}
 
 extern int kern_path(const char *, unsigned, struct path *);
 
@@ -70,9 +85,7 @@ extern int follow_up(struct path *);
 extern struct dentry *lock_rename(struct dentry *, struct dentry *);
 extern void unlock_rename(struct dentry *, struct dentry *);
 
-extern void nd_jump_link(struct nameidata *nd, struct path *path);
-extern void nd_set_link(struct nameidata *nd, char *path);
-extern char *nd_get_link(struct nameidata *nd);
+extern void nd_jump_link(struct path *path);
 
 static inline void nd_terminate_link(void *name, size_t len, size_t maxlen)
 {
index f1bd3962e6b6d2388ff611da201bf87584f7d5b2..8ca6d6464ea31fd7fa8c9faf53c9b5aa5c58980e 100644 (file)
@@ -6,7 +6,7 @@
  *
  *  ebtables.c,v 2.0, April, 2002
  *
- *  This code is stongly inspired on the iptables code which is
+ *  This code is strongly inspired by the iptables code which is
  *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
  */
 #ifndef __LINUX_BRIDGE_EFF_H
diff --git a/include/linux/nx842.h b/include/linux/nx842.h
deleted file mode 100644 (file)
index a4d324c..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-#ifndef __NX842_H__
-#define __NX842_H__
-
-int nx842_get_workmem_size(void);
-int nx842_get_workmem_size_aligned(void);
-int nx842_compress(const unsigned char *in, unsigned int in_len,
-               unsigned char *out, unsigned int *out_len, void *wrkmem);
-int nx842_decompress(const unsigned char *in, unsigned int in_len,
-               unsigned char *out, unsigned int *out_len, void *wrkmem);
-
-#endif
index ddeaae6d2083b256b21b930f3eed8182510e26a8..b871ff9d81d7207333fa021e6a95cb6bdbcf34ac 100644 (file)
@@ -121,6 +121,8 @@ extern struct device_node *of_stdout;
 extern raw_spinlock_t devtree_lock;
 
 #ifdef CONFIG_OF
+void of_core_init(void);
+
 static inline bool is_of_node(struct fwnode_handle *fwnode)
 {
        return fwnode && fwnode->type == FWNODE_OF;
@@ -376,6 +378,10 @@ bool of_console_check(struct device_node *dn, char *name, int index);
 
 #else /* CONFIG_OF */
 
+static inline void of_core_init(void)
+{
+}
+
 static inline bool is_of_node(struct fwnode_handle *fwnode)
 {
        return false;
index 587ee507965d78a1b16112a0e3c0bba7856f1d3e..fd627a58068f3473a58240064efd7ed7171ebfad 100644 (file)
@@ -64,6 +64,7 @@ extern int early_init_dt_scan_chosen(unsigned long node, const char *uname,
 extern int early_init_dt_scan_memory(unsigned long node, const char *uname,
                                     int depth, void *data);
 extern void early_init_fdt_scan_reserved_mem(void);
+extern void early_init_fdt_reserve_self(void);
 extern void early_init_dt_add_memory_arch(u64 base, u64 size);
 extern int early_init_dt_reserve_memory_arch(phys_addr_t base, phys_addr_t size,
                                             bool no_map);
@@ -91,6 +92,7 @@ extern u64 fdt_translate_address(const void *blob, int node_offset);
 extern void of_fdt_limit_memory(int limit);
 #else /* CONFIG_OF_FLATTREE */
 static inline void early_init_fdt_scan_reserved_mem(void) {}
+static inline void early_init_fdt_reserve_self(void) {}
 static inline const char *of_flat_dt_get_machine_name(void) { return NULL; }
 static inline void unflatten_device_tree(void) {}
 static inline void unflatten_and_copy_device_tree(void) {}
index 3a6490e81b2856821ca190f438c039136a7fa207..703ea5c30a33f84bd60fcc19415b48c53084f5c7 100644 (file)
@@ -32,4 +32,9 @@ static inline void osq_lock_init(struct optimistic_spin_queue *lock)
 extern bool osq_lock(struct optimistic_spin_queue *lock);
 extern void osq_unlock(struct optimistic_spin_queue *lock);
 
+static inline bool osq_is_locked(struct optimistic_spin_queue *lock)
+{
+       return atomic_read(&lock->tail) != OSQ_UNLOCKED_VAL;
+}
+
 #endif
index 353db8dc4c6e0eca74751d4aabc8b0cb28fc4c7a..8a0321a8fb595892c5e80910d5c914c89ded63de 100644 (file)
@@ -355,6 +355,7 @@ struct pci_dev {
        unsigned int    broken_intx_masking:1;
        unsigned int    io_window_1k:1; /* Intel P2P bridge 1K I/O windows */
        unsigned int    irq_managed:1;
+       unsigned int    has_secondary_link:1;
        pci_dev_flags_t dev_flags;
        atomic_t        enable_cnt;     /* pci_enable_device has been called */
 
@@ -577,9 +578,15 @@ int raw_pci_read(unsigned int domain, unsigned int bus, unsigned int devfn,
 int raw_pci_write(unsigned int domain, unsigned int bus, unsigned int devfn,
                  int reg, int len, u32 val);
 
+#ifdef CONFIG_PCI_BUS_ADDR_T_64BIT
+typedef u64 pci_bus_addr_t;
+#else
+typedef u32 pci_bus_addr_t;
+#endif
+
 struct pci_bus_region {
-       dma_addr_t start;
-       dma_addr_t end;
+       pci_bus_addr_t start;
+       pci_bus_addr_t end;
 };
 
 struct pci_dynids {
@@ -773,8 +780,6 @@ void pcibios_bus_to_resource(struct pci_bus *bus, struct resource *res,
 void pcibios_scan_specific_bus(int busn);
 struct pci_bus *pci_find_bus(int domain, int busnr);
 void pci_bus_add_devices(const struct pci_bus *bus);
-struct pci_bus *pci_scan_bus_parented(struct device *parent, int bus,
-                                     struct pci_ops *ops, void *sysdata);
 struct pci_bus *pci_scan_bus(int bus, struct pci_ops *ops, void *sysdata);
 struct pci_bus *pci_create_root_bus(struct device *parent, int bus,
                                    struct pci_ops *ops, void *sysdata,
@@ -974,7 +979,6 @@ void pci_intx(struct pci_dev *dev, int enable);
 bool pci_intx_mask_supported(struct pci_dev *dev);
 bool pci_check_and_mask_intx(struct pci_dev *dev);
 bool pci_check_and_unmask_intx(struct pci_dev *dev);
-void pci_msi_off(struct pci_dev *dev);
 int pci_set_dma_max_seg_size(struct pci_dev *dev, unsigned int size);
 int pci_set_dma_seg_boundary(struct pci_dev *dev, unsigned long mask);
 int pci_wait_for_pending(struct pci_dev *dev, int pos, u16 mask);
@@ -1006,6 +1010,7 @@ int __must_check pci_assign_resource(struct pci_dev *dev, int i);
 int __must_check pci_reassign_resource(struct pci_dev *dev, int i, resource_size_t add_size, resource_size_t align);
 int pci_select_bars(struct pci_dev *dev, unsigned long flags);
 bool pci_device_is_present(struct pci_dev *pdev);
+void pci_ignore_hotplug(struct pci_dev *dev);
 
 /* ROM control related routines */
 int pci_enable_rom(struct pci_dev *pdev);
@@ -1043,11 +1048,6 @@ bool pci_dev_run_wake(struct pci_dev *dev);
 bool pci_check_pme_status(struct pci_dev *dev);
 void pci_pme_wakeup_bus(struct pci_bus *bus);
 
-static inline void pci_ignore_hotplug(struct pci_dev *dev)
-{
-       dev->ignore_hotplug = 1;
-}
-
 static inline int pci_enable_wake(struct pci_dev *dev, pci_power_t state,
                                  bool enable)
 {
@@ -1128,7 +1128,7 @@ int __must_check pci_bus_alloc_resource(struct pci_bus *bus,
 
 int pci_remap_iospace(const struct resource *res, phys_addr_t phys_addr);
 
-static inline dma_addr_t pci_bus_address(struct pci_dev *pdev, int bar)
+static inline pci_bus_addr_t pci_bus_address(struct pci_dev *pdev, int bar)
 {
        struct pci_bus_region region;
 
@@ -1197,15 +1197,6 @@ int pci_set_vga_state(struct pci_dev *pdev, bool decode,
 #define        pci_pool_alloc(pool, flags, handle) dma_pool_alloc(pool, flags, handle)
 #define        pci_pool_free(pool, vaddr, addr) dma_pool_free(pool, vaddr, addr)
 
-enum pci_dma_burst_strategy {
-       PCI_DMA_BURST_INFINITY, /* make bursts as large as possible,
-                                  strategy_parameter is N/A */
-       PCI_DMA_BURST_BOUNDARY, /* disconnect at every strategy_parameter
-                                  byte boundaries */
-       PCI_DMA_BURST_MULTIPLE, /* disconnect at some multiple of
-                                  strategy_parameter byte boundaries */
-};
-
 struct msix_entry {
        u32     vector; /* kernel uses to write allocated vector */
        u16     entry;  /* driver uses to specify entry, OS writes */
@@ -1430,8 +1421,6 @@ static inline int pci_request_regions(struct pci_dev *dev, const char *res_name)
 { return -EIO; }
 static inline void pci_release_regions(struct pci_dev *dev) { }
 
-#define pci_dma_burst_advice(pdev, strat, strategy_parameter) do { } while (0)
-
 static inline void pci_block_cfg_access(struct pci_dev *dev) { }
 static inline int pci_block_cfg_access_in_atomic(struct pci_dev *dev)
 { return 0; }
@@ -1905,4 +1894,15 @@ static inline bool pci_is_dev_assigned(struct pci_dev *pdev)
 {
        return (pdev->dev_flags & PCI_DEV_FLAGS_ASSIGNED) == PCI_DEV_FLAGS_ASSIGNED;
 }
+
+/**
+ * pci_ari_enabled - query ARI forwarding status
+ * @bus: the PCI bus
+ *
+ * Returns true if ARI forwarding is enabled.
+ */
+static inline bool pci_ari_enabled(struct pci_bus *bus)
+{
+       return bus->self && bus->self->ari_enabled;
+}
 #endif /* LINUX_PCI_H */
index 2f7b9a40f627ead2e04d4105ad1eaeab76fdf5bf..cb63a7b522ef4b883d0803cbb73beb752436a0a0 100644 (file)
 #define PCI_DEVICE_ID_AMD_HUDSON2_SATA_IDE     0x7800
 #define PCI_DEVICE_ID_AMD_HUDSON2_SMBUS                0x780b
 #define PCI_DEVICE_ID_AMD_HUDSON2_IDE          0x780c
+#define PCI_DEVICE_ID_AMD_KERNCZ_SMBUS  0x790b
 
 #define PCI_VENDOR_ID_TRIDENT          0x1023
 #define PCI_DEVICE_ID_TRIDENT_4DWAVE_DX        0x2000
index 61992cf2e9771699ee06595c8fbb1bd39633018a..1b82d44b0a02d278f980acb0ae2158d2462fb329 100644 (file)
@@ -92,8 +92,6 @@ struct hw_perf_event_extra {
        int             idx;    /* index in shared_regs->regs[] */
 };
 
-struct event_constraint;
-
 /**
  * struct hw_perf_event - performance event hardware details:
  */
@@ -112,8 +110,6 @@ struct hw_perf_event {
 
                        struct hw_perf_event_extra extra_reg;
                        struct hw_perf_event_extra branch_reg;
-
-                       struct event_constraint *constraint;
                };
                struct { /* software */
                        struct hrtimer  hrtimer;
@@ -124,7 +120,7 @@ struct hw_perf_event {
                };
                struct { /* intel_cqm */
                        int                     cqm_state;
-                       int                     cqm_rmid;
+                       u32                     cqm_rmid;
                        struct list_head        cqm_events_entry;
                        struct list_head        cqm_groups_entry;
                        struct list_head        cqm_group_entry;
@@ -566,8 +562,12 @@ struct perf_cpu_context {
        struct perf_event_context       *task_ctx;
        int                             active_oncpu;
        int                             exclusive;
+
+       raw_spinlock_t                  hrtimer_lock;
        struct hrtimer                  hrtimer;
        ktime_t                         hrtimer_interval;
+       unsigned int                    hrtimer_active;
+
        struct pmu                      *unique_pmu;
        struct perf_cgroup              *cgrp;
 };
@@ -734,6 +734,22 @@ extern int perf_event_overflow(struct perf_event *event,
                                 struct perf_sample_data *data,
                                 struct pt_regs *regs);
 
+extern void perf_event_output(struct perf_event *event,
+                               struct perf_sample_data *data,
+                               struct pt_regs *regs);
+
+extern void
+perf_event_header__init_id(struct perf_event_header *header,
+                          struct perf_sample_data *data,
+                          struct perf_event *event);
+extern void
+perf_event__output_id_sample(struct perf_event *event,
+                            struct perf_output_handle *handle,
+                            struct perf_sample_data *sample);
+
+extern void
+perf_log_lost_samples(struct perf_event *event, u64 lost);
+
 static inline bool is_sampling_event(struct perf_event *event)
 {
        return event->attr.sample_period != 0;
@@ -798,11 +814,33 @@ perf_sw_event_sched(u32 event_id, u64 nr, u64 addr)
 
 extern struct static_key_deferred perf_sched_events;
 
+static __always_inline bool
+perf_sw_migrate_enabled(void)
+{
+       if (static_key_false(&perf_swevent_enabled[PERF_COUNT_SW_CPU_MIGRATIONS]))
+               return true;
+       return false;
+}
+
+static inline void perf_event_task_migrate(struct task_struct *task)
+{
+       if (perf_sw_migrate_enabled())
+               task->sched_migrated = 1;
+}
+
 static inline void perf_event_task_sched_in(struct task_struct *prev,
                                            struct task_struct *task)
 {
        if (static_key_false(&perf_sched_events.key))
                __perf_event_task_sched_in(prev, task);
+
+       if (perf_sw_migrate_enabled() && task->sched_migrated) {
+               struct pt_regs *regs = this_cpu_ptr(&__perf_regs[0]);
+
+               perf_fetch_caller_regs(regs);
+               ___perf_sw_event(PERF_COUNT_SW_CPU_MIGRATIONS, 1, regs, 0);
+               task->sched_migrated = 0;
+       }
 }
 
 static inline void perf_event_task_sched_out(struct task_struct *prev,
@@ -925,6 +963,8 @@ perf_aux_output_skip(struct perf_output_handle *handle,
 static inline void *
 perf_get_aux(struct perf_output_handle *handle)                                { return NULL; }
 static inline void
+perf_event_task_migrate(struct task_struct *task)                      { }
+static inline void
 perf_event_task_sched_in(struct task_struct *prev,
                         struct task_struct *task)                      { }
 static inline void
index 5d50b25a73d71374b523ba68b6614e9deb3268a4..cb2618147c34f68278b932120daf44a10b426bd6 100644 (file)
@@ -208,9 +208,17 @@ struct omap_gpio_platform_data {
        int (*get_context_loss_count)(struct device *dev);
 };
 
+#if IS_BUILTIN(CONFIG_GPIO_OMAP)
 extern void omap2_gpio_prepare_for_idle(int off_mode);
 extern void omap2_gpio_resume_after_idle(void);
-extern void omap_set_gpio_debounce(int gpio, int enable);
-extern void omap_set_gpio_debounce_time(int gpio, int enable);
+#else
+static inline void omap2_gpio_prepare_for_idle(int off_mode)
+{
+}
+
+static inline void omap2_gpio_resume_after_idle(void)
+{
+}
+#endif
 
 #endif
diff --git a/include/linux/platform_data/irq-renesas-irqc.h b/include/linux/platform_data/irq-renesas-irqc.h
deleted file mode 100644 (file)
index 3ae17b3..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Renesas IRQC Driver
- *
- *  Copyright (C) 2013 Magnus Damm
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-#ifndef __IRQ_RENESAS_IRQC_H__
-#define __IRQ_RENESAS_IRQC_H__
-
-struct renesas_irqc_config {
-       unsigned int irq_base;
-};
-
-#endif /* __IRQ_RENESAS_IRQC_H__ */
index 0a6de4ca4930bdded25056c7d4cc36bbf0a172d0..aed170588b747c05a50eedfda9cdb5e7ea48bc86 100644 (file)
@@ -27,6 +27,7 @@ enum ntc_thermistor_type {
        TYPE_NCPXXWB473,
        TYPE_NCPXXWL333,
        TYPE_B57330V2103,
+       TYPE_NCPXXWF104,
 };
 
 struct ntc_thermistor_platform_data {
diff --git a/include/linux/platform_data/video-msm_fb.h b/include/linux/platform_data/video-msm_fb.h
deleted file mode 100644 (file)
index 31449be..0000000
+++ /dev/null
@@ -1,146 +0,0 @@
-/*
- * Internal shared definitions for various MSM framebuffer parts.
- *
- * Copyright (C) 2007 Google Incorporated
- *
- * 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 _MSM_FB_H_
-#define _MSM_FB_H_
-
-#include <linux/device.h>
-
-struct mddi_info;
-
-struct msm_fb_data {
-       int xres;       /* x resolution in pixels */
-       int yres;       /* y resolution in pixels */
-       int width;      /* disply width in mm */
-       int height;     /* display height in mm */
-       unsigned output_format;
-};
-
-struct msmfb_callback {
-       void (*func)(struct msmfb_callback *);
-};
-
-enum {
-       MSM_MDDI_PMDH_INTERFACE,
-       MSM_MDDI_EMDH_INTERFACE,
-       MSM_EBI2_INTERFACE,
-};
-
-#define MSMFB_CAP_PARTIAL_UPDATES      (1 << 0)
-
-struct msm_panel_data {
-       /* turns off the fb memory */
-       int (*suspend)(struct msm_panel_data *);
-       /* turns on the fb memory */
-       int (*resume)(struct msm_panel_data *);
-       /* turns off the panel */
-       int (*blank)(struct msm_panel_data *);
-       /* turns on the panel */
-       int (*unblank)(struct msm_panel_data *);
-       void (*wait_vsync)(struct msm_panel_data *);
-       void (*request_vsync)(struct msm_panel_data *, struct msmfb_callback *);
-       void (*clear_vsync)(struct msm_panel_data *);
-       /* from the enum above */
-       unsigned interface_type;
-       /* data to be passed to the fb driver */
-       struct msm_fb_data *fb_data;
-
-       /* capabilities supported by the panel */
-       uint32_t caps;
-};
-
-struct msm_mddi_client_data {
-       void (*suspend)(struct msm_mddi_client_data *);
-       void (*resume)(struct msm_mddi_client_data *);
-       void (*activate_link)(struct msm_mddi_client_data *);
-       void (*remote_write)(struct msm_mddi_client_data *, uint32_t val,
-                            uint32_t reg);
-       uint32_t (*remote_read)(struct msm_mddi_client_data *, uint32_t reg);
-       void (*auto_hibernate)(struct msm_mddi_client_data *, int);
-       /* custom data that needs to be passed from the board file to a 
-        * particular client */
-       void *private_client_data;
-       struct resource *fb_resource;
-       /* from the list above */
-       unsigned interface_type;
-};
-
-struct msm_mddi_platform_data {
-       unsigned int clk_rate;
-       void (*power_client)(struct msm_mddi_client_data *, int on);
-
-       /* fixup the mfr name, product id */
-       void (*fixup)(uint16_t *mfr_name, uint16_t *product_id);
-
-       struct resource *fb_resource; /*optional*/
-       /* number of clients in the list that follows */
-       int num_clients;
-       /* array of client information of clients */
-       struct {
-               unsigned product_id; /* mfr id in top 16 bits, product id
-                                     * in lower 16 bits
-                                     */
-               char *name;     /* the device name will be the platform
-                                * device name registered for the client,
-                                * it should match the name of the associated
-                                * driver
-                                */
-               unsigned id;    /* id for mddi client device node, will also
-                                * be used as device id of panel devices, if
-                                * the client device will have multiple panels
-                                * space must be left here for them
-                                */
-               void *client_data;      /* required private client data */
-               unsigned int clk_rate;  /* optional: if the client requires a
-                                       * different mddi clk rate
-                                       */
-       } client_platform_data[];
-};
-
-struct mdp_blit_req;
-struct fb_info;
-struct mdp_device {
-       struct device dev;
-       void (*dma)(struct mdp_device *mpd, uint32_t addr,
-                   uint32_t stride, uint32_t w, uint32_t h, uint32_t x,
-                   uint32_t y, struct msmfb_callback *callback, int interface);
-       void (*dma_wait)(struct mdp_device *mdp);
-       int (*blit)(struct mdp_device *mdp, struct fb_info *fb,
-                   struct mdp_blit_req *req);
-       void (*set_grp_disp)(struct mdp_device *mdp, uint32_t disp_id);
-};
-
-struct class_interface;
-int register_mdp_client(struct class_interface *class_intf);
-
-/**** private client data structs go below this line ***/
-
-struct msm_mddi_bridge_platform_data {
-       /* from board file */
-       int (*init)(struct msm_mddi_bridge_platform_data *,
-                   struct msm_mddi_client_data *);
-       int (*uninit)(struct msm_mddi_bridge_platform_data *,
-                     struct msm_mddi_client_data *);
-       /* passed to panel for use by the fb driver */
-       int (*blank)(struct msm_mddi_bridge_platform_data *,
-                    struct msm_mddi_client_data *);
-       int (*unblank)(struct msm_mddi_bridge_platform_data *,
-                      struct msm_mddi_client_data *);
-       struct msm_fb_data fb_data;
-};
-
-
-
-#endif
index 2d29c64f8fb1ee73b454ff8f428e5d67cb7bf57c..35d599e7250d2d8c29defd0373ff25fed0b03467 100644 (file)
@@ -342,6 +342,18 @@ struct dev_pm_ops {
 #define SET_LATE_SYSTEM_SLEEP_PM_OPS(suspend_fn, resume_fn)
 #endif
 
+#ifdef CONFIG_PM_SLEEP
+#define SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(suspend_fn, resume_fn) \
+       .suspend_noirq = suspend_fn, \
+       .resume_noirq = resume_fn, \
+       .freeze_noirq = suspend_fn, \
+       .thaw_noirq = resume_fn, \
+       .poweroff_noirq = suspend_fn, \
+       .restore_noirq = resume_fn,
+#else
+#define SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(suspend_fn, resume_fn)
+#endif
+
 #ifdef CONFIG_PM
 #define SET_RUNTIME_PM_OPS(suspend_fn, resume_fn, idle_fn) \
        .runtime_suspend = suspend_fn, \
@@ -529,6 +541,7 @@ enum rpm_request {
 };
 
 struct wakeup_source;
+struct wake_irq;
 struct pm_domain_data;
 
 struct pm_subsys_data {
@@ -568,6 +581,7 @@ struct dev_pm_info {
        unsigned long           timer_expires;
        struct work_struct      work;
        wait_queue_head_t       wait_queue;
+       struct wake_irq         *wakeirq;
        atomic_t                usage_count;
        atomic_t                child_count;
        unsigned int            disable_depth:3;
index 0b0039634410c158a22c3667b142856dfa4a66a1..25266c600021462c8f809a22451231820caf1b6e 100644 (file)
@@ -20,6 +20,16 @@ struct pm_clk_notifier_block {
 
 struct clk;
 
+#ifdef CONFIG_PM
+extern int pm_clk_runtime_suspend(struct device *dev);
+extern int pm_clk_runtime_resume(struct device *dev);
+#define USE_PM_CLK_RUNTIME_OPS \
+       .runtime_suspend = pm_clk_runtime_suspend, \
+       .runtime_resume = pm_clk_runtime_resume,
+#else
+#define USE_PM_CLK_RUNTIME_OPS
+#endif
+
 #ifdef CONFIG_PM_CLK
 static inline bool pm_clk_no_clocks(struct device *dev)
 {
diff --git a/include/linux/pm_wakeirq.h b/include/linux/pm_wakeirq.h
new file mode 100644 (file)
index 0000000..cd5b62d
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * pm_wakeirq.h - Device wakeirq helper functions
+ *
+ * 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 "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _LINUX_PM_WAKEIRQ_H
+#define _LINUX_PM_WAKEIRQ_H
+
+#ifdef CONFIG_PM
+
+extern int dev_pm_set_wake_irq(struct device *dev, int irq);
+extern int dev_pm_set_dedicated_wake_irq(struct device *dev,
+                                        int irq);
+extern void dev_pm_clear_wake_irq(struct device *dev);
+extern void dev_pm_enable_wake_irq(struct device *dev);
+extern void dev_pm_disable_wake_irq(struct device *dev);
+
+#else  /* !CONFIG_PM */
+
+static inline int dev_pm_set_wake_irq(struct device *dev, int irq)
+{
+       return 0;
+}
+
+static inline int dev_pm_set_dedicated_wake_irq(struct device *dev, int irq)
+{
+       return 0;
+}
+
+static inline void dev_pm_clear_wake_irq(struct device *dev)
+{
+}
+
+static inline void dev_pm_enable_wake_irq(struct device *dev)
+{
+}
+
+static inline void dev_pm_disable_wake_irq(struct device *dev)
+{
+}
+
+#endif /* CONFIG_PM */
+#endif /* _LINUX_PM_WAKEIRQ_H */
index a0f70808d7f48432967c60a8a0d686ab077d08e8..a3447932df1ff0a00f417c847052a83a407f4631 100644 (file)
 
 #include <linux/types.h>
 
+struct wake_irq;
+
 /**
  * struct wakeup_source - Representation of wakeup sources
  *
+ * @name: Name of the wakeup source
+ * @entry: Wakeup source list entry
+ * @lock: Wakeup source lock
+ * @wakeirq: Optional device specific wakeirq
+ * @timer: Wakeup timer list
+ * @timer_expires: Wakeup timer expiration
  * @total_time: Total time this wakeup source has been active.
  * @max_time: Maximum time this wakeup source has been continuously active.
  * @last_time: Monotonic clock when the wakeup source's was touched last time.
@@ -47,6 +55,7 @@ struct wakeup_source {
        const char              *name;
        struct list_head        entry;
        spinlock_t              lock;
+       struct wake_irq         *wakeirq;
        struct timer_list       timer;
        unsigned long           timer_expires;
        ktime_t total_time;
index cf112b4075c872f81ae87c972d4d823b10d36526..522757ac9cd4d9975a5fe8bbbf144efb54551cd4 100644 (file)
@@ -215,6 +215,10 @@ struct max17042_platform_data {
         * the datasheet although it can be changed by board designers.
         */
        unsigned int r_sns;
+       int         vmin;       /* in millivolts */
+       int         vmax;       /* in millivolts */
+       int         temp_min;   /* in tenths of degree Celsius */
+       int         temp_max;   /* in tenths of degree Celsius */
 };
 
 #endif /* __MAX17042_BATTERY_H_ */
index 75a1dd8dc56ee19b5dc1ae89ce771851b370d4cd..ef9f1592185d3189867b564efc4fdb8c151597a7 100644 (file)
@@ -206,6 +206,11 @@ struct power_supply_desc {
        int (*set_property)(struct power_supply *psy,
                            enum power_supply_property psp,
                            const union power_supply_propval *val);
+       /*
+        * property_is_writeable() will be called during registration
+        * of power supply. If this happens during device probe then it must
+        * not access internal data of device (because probe did not end).
+        */
        int (*property_is_writeable)(struct power_supply *psy,
                                     enum power_supply_property psp);
        void (*external_power_changed)(struct power_supply *psy);
@@ -237,6 +242,7 @@ struct power_supply {
        /* private */
        struct device dev;
        struct work_struct changed_work;
+       struct delayed_work deferred_register_work;
        spinlock_t changed_lock;
        bool changed;
        atomic_t use_cnt;
@@ -286,10 +292,15 @@ extern void power_supply_put(struct power_supply *psy);
 #ifdef CONFIG_OF
 extern struct power_supply *power_supply_get_by_phandle(struct device_node *np,
                                                        const char *property);
+extern struct power_supply *devm_power_supply_get_by_phandle(
+                                   struct device *dev, const char *property);
 #else /* !CONFIG_OF */
 static inline struct power_supply *
 power_supply_get_by_phandle(struct device_node *np, const char *property)
 { return NULL; }
+static inline struct power_supply *
+devm_power_supply_get_by_phandle(struct device *dev, const char *property)
+{ return NULL; }
 #endif /* CONFIG_OF */
 extern void power_supply_changed(struct power_supply *psy);
 extern int power_supply_am_i_supplied(struct power_supply *psy);
index de8bdf417a35adc745e37fc1df1de7453d518895..76ebde9c11d483fe3a177b049e2774550e7b2bcc 100644 (file)
@@ -164,4 +164,6 @@ struct property_set {
 
 void device_add_property_set(struct device *dev, struct property_set *pset);
 
+bool device_dma_is_coherent(struct device *dev);
+
 #endif /* _LINUX_PROPERTY_H_ */
index e90628cac8fae57e8a88ecd9f93d2a2f1342b9d5..36262d08a9dad8aca7eaea494e30348c0b26cb5b 100644 (file)
@@ -182,6 +182,8 @@ struct pwm_chip {
 int pwm_set_chip_data(struct pwm_device *pwm, void *data);
 void *pwm_get_chip_data(struct pwm_device *pwm);
 
+int pwmchip_add_with_polarity(struct pwm_chip *chip,
+                             enum pwm_polarity polarity);
 int pwmchip_add(struct pwm_chip *chip);
 int pwmchip_remove(struct pwm_chip *chip);
 struct pwm_device *pwm_request_from_chip(struct pwm_chip *chip,
@@ -217,6 +219,11 @@ static inline int pwmchip_add(struct pwm_chip *chip)
        return -EINVAL;
 }
 
+static inline int pwmchip_add_inversed(struct pwm_chip *chip)
+{
+       return -EINVAL;
+}
+
 static inline int pwmchip_remove(struct pwm_chip *chip)
 {
        return -EINVAL;
@@ -290,10 +297,15 @@ struct pwm_lookup {
 
 #if IS_ENABLED(CONFIG_PWM)
 void pwm_add_table(struct pwm_lookup *table, size_t num);
+void pwm_remove_table(struct pwm_lookup *table, size_t num);
 #else
 static inline void pwm_add_table(struct pwm_lookup *table, size_t num)
 {
 }
+
+static inline void pwm_remove_table(struct pwm_lookup *table, size_t num)
+{
+}
 #endif
 
 #ifdef CONFIG_PWM_SYSFS
index dab545bb66b3114597da2d4a851539d0ab0d89a8..0485bab061fd3936a1f53aafefbf87b659df0365 100644 (file)
@@ -194,8 +194,9 @@ enum pxa_ssp_type {
        PXA168_SSP,
        PXA910_SSP,
        CE4100_SSP,
-       LPSS_SSP,
        QUARK_X1000_SSP,
+       LPSS_LPT_SSP, /* Keep LPSS types sorted with lpss_platforms[] */
+       LPSS_BYT_SSP,
 };
 
 struct ssp_device {
index b05856e16b75be8b7dea5015a5a6ff6afcb021eb..e651874df2c9d7f7e45187900d4c5de84df03307 100644 (file)
@@ -6,14 +6,23 @@
 #ifndef _LINUX_RANDOM_H
 #define _LINUX_RANDOM_H
 
+#include <linux/list.h>
 #include <uapi/linux/random.h>
 
+struct random_ready_callback {
+       struct list_head list;
+       void (*func)(struct random_ready_callback *rdy);
+       struct module *owner;
+};
+
 extern void add_device_randomness(const void *, unsigned int);
 extern void add_input_randomness(unsigned int type, unsigned int code,
                                 unsigned int value);
 extern void add_interrupt_randomness(int irq, int irq_flags);
 
 extern void get_random_bytes(void *buf, int nbytes);
+extern int add_random_ready_callback(struct random_ready_callback *rdy);
+extern void del_random_ready_callback(struct random_ready_callback *rdy);
 extern void get_random_bytes_arch(void *buf, int nbytes);
 void generate_random_uuid(unsigned char uuid_out[16]);
 extern int random_int_secret_init(void);
index a18b16f1dc0e44f7f5a3b99ea4f43d64c67b8a0c..17c6b1f84a77d3b3073bc272fffc6a6138b322b5 100644 (file)
@@ -29,8 +29,8 @@
  */
 static inline void INIT_LIST_HEAD_RCU(struct list_head *list)
 {
-       ACCESS_ONCE(list->next) = list;
-       ACCESS_ONCE(list->prev) = list;
+       WRITE_ONCE(list->next, list);
+       WRITE_ONCE(list->prev, list);
 }
 
 /*
@@ -288,7 +288,7 @@ static inline void list_splice_init_rcu(struct list_head *list,
 #define list_first_or_null_rcu(ptr, type, member) \
 ({ \
        struct list_head *__ptr = (ptr); \
-       struct list_head *__next = ACCESS_ONCE(__ptr->next); \
+       struct list_head *__next = READ_ONCE(__ptr->next); \
        likely(__ptr != __next) ? list_entry_rcu(__next, type, member) : NULL; \
 })
 
@@ -549,8 +549,8 @@ static inline void hlist_add_behind_rcu(struct hlist_node *n,
  */
 #define hlist_for_each_entry_from_rcu(pos, member)                     \
        for (; pos;                                                     \
-            pos = hlist_entry_safe(rcu_dereference((pos)->member.next),\
-                       typeof(*(pos)), member))
+            pos = hlist_entry_safe(rcu_dereference_raw(hlist_next_rcu( \
+                       &(pos)->member)), typeof(*(pos)), member))
 
 #endif /* __KERNEL__ */
 #endif
index 573a5afd5ed884d5bdcfc4af6cf88c3b9d25214d..33a056bb886faeedeb9690faefd3a4adeeedd14b 100644 (file)
@@ -44,6 +44,8 @@
 #include <linux/debugobjects.h>
 #include <linux/bug.h>
 #include <linux/compiler.h>
+#include <linux/ktime.h>
+
 #include <asm/barrier.h>
 
 extern int rcu_expedited; /* for sysctl */
@@ -292,10 +294,6 @@ void rcu_sched_qs(void);
 void rcu_bh_qs(void);
 void rcu_check_callbacks(int user);
 struct notifier_block;
-void rcu_idle_enter(void);
-void rcu_idle_exit(void);
-void rcu_irq_enter(void);
-void rcu_irq_exit(void);
 int rcu_cpu_notify(struct notifier_block *self,
                   unsigned long action, void *hcpu);
 
@@ -364,8 +362,8 @@ extern struct srcu_struct tasks_rcu_exit_srcu;
 #define rcu_note_voluntary_context_switch(t) \
        do { \
                rcu_all_qs(); \
-               if (ACCESS_ONCE((t)->rcu_tasks_holdout)) \
-                       ACCESS_ONCE((t)->rcu_tasks_holdout) = false; \
+               if (READ_ONCE((t)->rcu_tasks_holdout)) \
+                       WRITE_ONCE((t)->rcu_tasks_holdout, false); \
        } while (0)
 #else /* #ifdef CONFIG_TASKS_RCU */
 #define TASKS_RCU(x) do { } while (0)
@@ -609,7 +607,7 @@ static inline void rcu_preempt_sleep_check(void)
 
 #define __rcu_access_pointer(p, space) \
 ({ \
-       typeof(*p) *_________p1 = (typeof(*p) *__force)ACCESS_ONCE(p); \
+       typeof(*p) *_________p1 = (typeof(*p) *__force)READ_ONCE(p); \
        rcu_dereference_sparse(p, space); \
        ((typeof(*p) __force __kernel *)(_________p1)); \
 })
@@ -628,21 +626,6 @@ static inline void rcu_preempt_sleep_check(void)
        ((typeof(*p) __force __kernel *)(p)); \
 })
 
-#define __rcu_access_index(p, space) \
-({ \
-       typeof(p) _________p1 = ACCESS_ONCE(p); \
-       rcu_dereference_sparse(p, space); \
-       (_________p1); \
-})
-#define __rcu_dereference_index_check(p, c) \
-({ \
-       /* Dependency order vs. p above. */ \
-       typeof(p) _________p1 = lockless_dereference(p); \
-       rcu_lockdep_assert(c, \
-                          "suspicious rcu_dereference_index_check() usage"); \
-       (_________p1); \
-})
-
 /**
  * RCU_INITIALIZER() - statically initialize an RCU-protected global variable
  * @v: The value to statically initialize with.
@@ -659,7 +642,7 @@ static inline void rcu_preempt_sleep_check(void)
  */
 #define lockless_dereference(p) \
 ({ \
-       typeof(p) _________p1 = ACCESS_ONCE(p); \
+       typeof(p) _________p1 = READ_ONCE(p); \
        smp_read_barrier_depends(); /* Dependency order vs. p above. */ \
        (_________p1); \
 })
@@ -702,7 +685,7 @@ static inline void rcu_preempt_sleep_check(void)
  * @p: The pointer to read
  *
  * Return the value of the specified RCU-protected pointer, but omit the
- * smp_read_barrier_depends() and keep the ACCESS_ONCE().  This is useful
+ * smp_read_barrier_depends() and keep the READ_ONCE().  This is useful
  * when the value of this pointer is accessed, but the pointer is not
  * dereferenced, for example, when testing an RCU-protected pointer against
  * NULL.  Although rcu_access_pointer() may also be used in cases where
@@ -786,48 +769,13 @@ static inline void rcu_preempt_sleep_check(void)
  */
 #define rcu_dereference_raw_notrace(p) __rcu_dereference_check((p), 1, __rcu)
 
-/**
- * rcu_access_index() - fetch RCU index with no dereferencing
- * @p: The index to read
- *
- * Return the value of the specified RCU-protected index, but omit the
- * smp_read_barrier_depends() and keep the ACCESS_ONCE().  This is useful
- * when the value of this index is accessed, but the index is not
- * dereferenced, for example, when testing an RCU-protected index against
- * -1.  Although rcu_access_index() may also be used in cases where
- * update-side locks prevent the value of the index from changing, you
- * should instead use rcu_dereference_index_protected() for this use case.
- */
-#define rcu_access_index(p) __rcu_access_index((p), __rcu)
-
-/**
- * rcu_dereference_index_check() - rcu_dereference for indices with debug checking
- * @p: The pointer to read, prior to dereferencing
- * @c: The conditions under which the dereference will take place
- *
- * Similar to rcu_dereference_check(), but omits the sparse checking.
- * This allows rcu_dereference_index_check() to be used on integers,
- * which can then be used as array indices.  Attempting to use
- * rcu_dereference_check() on an integer will give compiler warnings
- * because the sparse address-space mechanism relies on dereferencing
- * the RCU-protected pointer.  Dereferencing integers is not something
- * that even gcc will put up with.
- *
- * Note that this function does not implicitly check for RCU read-side
- * critical sections.  If this function gains lots of uses, it might
- * make sense to provide versions for each flavor of RCU, but it does
- * not make sense as of early 2010.
- */
-#define rcu_dereference_index_check(p, c) \
-       __rcu_dereference_index_check((p), (c))
-
 /**
  * rcu_dereference_protected() - fetch RCU pointer when updates prevented
  * @p: The pointer to read, prior to dereferencing
  * @c: The conditions under which the dereference will take place
  *
  * Return the value of the specified RCU-protected pointer, but omit
- * both the smp_read_barrier_depends() and the ACCESS_ONCE().  This
+ * both the smp_read_barrier_depends() and the READ_ONCE().  This
  * is useful in cases where update-side locks prevent the value of the
  * pointer from changing.  Please note that this primitive does -not-
  * prevent the compiler from repeating this reference or combining it
@@ -1153,13 +1101,13 @@ static inline notrace void rcu_read_unlock_sched_notrace(void)
 #define kfree_rcu(ptr, rcu_head)                                       \
        __kfree_rcu(&((ptr)->rcu_head), offsetof(typeof(*(ptr)), rcu_head))
 
-#if defined(CONFIG_TINY_RCU) || defined(CONFIG_RCU_NOCB_CPU_ALL)
-static inline int rcu_needs_cpu(unsigned long *delta_jiffies)
+#ifdef CONFIG_TINY_RCU
+static inline int rcu_needs_cpu(u64 basemono, u64 *nextevt)
 {
-       *delta_jiffies = ULONG_MAX;
+       *nextevt = KTIME_MAX;
        return 0;
 }
-#endif /* #if defined(CONFIG_TINY_RCU) || defined(CONFIG_RCU_NOCB_CPU_ALL) */
+#endif /* #ifdef CONFIG_TINY_RCU */
 
 #if defined(CONFIG_RCU_NOCB_CPU_ALL)
 static inline bool rcu_is_nocb_cpu(int cpu) { return true; }
index 937edaeb150deb17759a9c0c715630fd0cc9a729..3df6c1ec4e25503583cb14656474d6727495a530 100644 (file)
@@ -159,6 +159,22 @@ static inline void rcu_cpu_stall_reset(void)
 {
 }
 
+static inline void rcu_idle_enter(void)
+{
+}
+
+static inline void rcu_idle_exit(void)
+{
+}
+
+static inline void rcu_irq_enter(void)
+{
+}
+
+static inline void rcu_irq_exit(void)
+{
+}
+
 static inline void exit_rcu(void)
 {
 }
index d2e583a6aacacf09ee9dc3bf3646b6a3cff3494e..456879143f89f9db45d0f79315f728f50a9f9d0c 100644 (file)
@@ -31,9 +31,7 @@
 #define __LINUX_RCUTREE_H
 
 void rcu_note_context_switch(void);
-#ifndef CONFIG_RCU_NOCB_CPU_ALL
-int rcu_needs_cpu(unsigned long *delta_jiffies);
-#endif /* #ifndef CONFIG_RCU_NOCB_CPU_ALL */
+int rcu_needs_cpu(u64 basem, u64 *nextevt);
 void rcu_cpu_stall_reset(void);
 
 /*
@@ -93,6 +91,11 @@ void rcu_force_quiescent_state(void);
 void rcu_bh_force_quiescent_state(void);
 void rcu_sched_force_quiescent_state(void);
 
+void rcu_idle_enter(void);
+void rcu_idle_exit(void);
+void rcu_irq_enter(void);
+void rcu_irq_exit(void);
+
 void exit_rcu(void);
 
 void rcu_scheduler_starting(void);
index 116655d922691b3143b8c9d840b944fed3a70adb..59c55ea0f0b50c270d64bebe9dd6c25a717ca5ec 100644 (file)
@@ -433,6 +433,8 @@ int regmap_update_bits_check_async(struct regmap *map, unsigned int reg,
                                   unsigned int mask, unsigned int val,
                                   bool *change);
 int regmap_get_val_bytes(struct regmap *map);
+int regmap_get_max_register(struct regmap *map);
+int regmap_get_reg_stride(struct regmap *map);
 int regmap_async_complete(struct regmap *map);
 bool regmap_can_raw_write(struct regmap *map);
 
@@ -676,6 +678,18 @@ static inline int regmap_get_val_bytes(struct regmap *map)
        return -EINVAL;
 }
 
+static inline int regmap_get_max_register(struct regmap *map)
+{
+       WARN_ONCE(1, "regmap API is disabled");
+       return -EINVAL;
+}
+
+static inline int regmap_get_reg_stride(struct regmap *map)
+{
+       WARN_ONCE(1, "regmap API is disabled");
+       return -EINVAL;
+}
+
 static inline int regcache_sync(struct regmap *map)
 {
        WARN_ONCE(1, "regmap API is disabled");
index fffa688ac3a7a36c9ecc33e006c52f1c4cf3892c..4db9fbe4889d3c54f76c2c2c1e3d50ce404d0965 100644 (file)
@@ -91,6 +91,7 @@ struct regulator_linear_range {
  * @set_current_limit: Configure a limit for a current-limited regulator.
  *                     The driver should select the current closest to max_uA.
  * @get_current_limit: Get the configured limit for a current-limited regulator.
+ * @set_input_current_limit: Configure an input limit.
  *
  * @set_mode: Set the configured operating mode for the regulator.
  * @get_mode: Get the configured operating mode for the regulator.
@@ -111,6 +112,7 @@ struct regulator_linear_range {
  *               to stabilise after being set to a new value, in microseconds.
  *               The function provides the from and to voltage selector, the
  *               function should return the worst case.
+ * @set_soft_start: Enable soft start for the regulator.
  *
  * @set_suspend_voltage: Set the voltage for the regulator when the system
  *                       is suspended.
@@ -121,6 +123,9 @@ struct regulator_linear_range {
  * @set_suspend_mode: Set the operating mode for the regulator when the
  *                    system is suspended.
  *
+ * @set_pull_down: Configure the regulator to pull down when the regulator
+ *                is disabled.
+ *
  * This struct describes regulator operations which can be implemented by
  * regulator chip drivers.
  */
@@ -142,6 +147,8 @@ struct regulator_ops {
                                 int min_uA, int max_uA);
        int (*get_current_limit) (struct regulator_dev *);
 
+       int (*set_input_current_limit) (struct regulator_dev *, int lim_uA);
+
        /* enable/disable regulator */
        int (*enable) (struct regulator_dev *);
        int (*disable) (struct regulator_dev *);
@@ -158,6 +165,8 @@ struct regulator_ops {
                                     unsigned int old_selector,
                                     unsigned int new_selector);
 
+       int (*set_soft_start) (struct regulator_dev *);
+
        /* report regulator status ... most other accessors report
         * control inputs, this reports results of combining inputs
         * from Linux (and other sources) with the actual load.
@@ -187,6 +196,8 @@ struct regulator_ops {
 
        /* set regulator suspend operating mode (defined in consumer.h) */
        int (*set_suspend_mode) (struct regulator_dev *, unsigned int mode);
+
+       int (*set_pull_down) (struct regulator_dev *);
 };
 
 /*
index b07562e082c416a2b98fc08818f35111c8237288..b11be126012974ebf4877220c8949310d649ddea 100644 (file)
@@ -75,6 +75,8 @@ struct regulator_state {
  *
  * @min_uA: Smallest current consumers may set.
  * @max_uA: Largest current consumers may set.
+ * @ilim_uA: Maximum input current.
+ * @system_load: Load that isn't captured by any consumer requests.
  *
  * @valid_modes_mask: Mask of modes which may be configured by consumers.
  * @valid_ops_mask: Operations which may be performed by consumers.
@@ -86,6 +88,8 @@ struct regulator_state {
  *           applied.
  * @apply_uV: Apply the voltage constraint when initialising.
  * @ramp_disable: Disable ramp delay when initialising or when setting voltage.
+ * @soft_start: Enable soft start so that voltage ramps slowly.
+ * @pull_down: Enable pull down when regulator is disabled.
  *
  * @input_uV: Input voltage for regulator when supplied by another regulator.
  *
@@ -111,6 +115,9 @@ struct regulation_constraints {
        /* current output range (inclusive) - for current control */
        int min_uA;
        int max_uA;
+       int ilim_uA;
+
+       int system_load;
 
        /* valid regulator operating modes for this machine */
        unsigned int valid_modes_mask;
@@ -138,6 +145,8 @@ struct regulation_constraints {
        unsigned boot_on:1;     /* bootloader/firmware enabled regulator */
        unsigned apply_uV:1;    /* apply uV constraint if min == max */
        unsigned ramp_disable:1; /* disable ramp delay */
+       unsigned soft_start:1;  /* ramp voltage slowly */
+       unsigned pull_down:1;   /* pull down resistor when regulator off */
 };
 
 /**
index f8acc052e35326ebc818809bc921896c81c7fd02..f6a8a16a0d4deb27dc232f782a13453a5ba73ce0 100644 (file)
@@ -58,6 +58,9 @@
  *             control signal from EN input pin. If it is false then
  *             voltage output will be enabled/disabled through EN bit of
  *             device register.
+ * @enable_gpio: Enable GPIO. If EN pin is controlled through GPIO from host
+ *             then GPIO number can be provided. If no GPIO controlled then
+ *             it should be -1.
  * @dvs_gpio: GPIO for dvs. It should be -1 if this is tied with fixed logic.
  * @dvs_def_state: Default state of dvs. 1 if it is high else 0.
  */
@@ -65,6 +68,7 @@ struct max8973_regulator_platform_data {
        struct regulator_init_data *reg_init_data;
        unsigned long control_flags;
        bool enable_ext_control;
+       int enable_gpio;
        int dvs_gpio;
        unsigned dvs_def_state:1;
 };
index 6bda06f21930bcce8ff57ad7eaa5f3afad8bf45d..cde976e86b48aa91049734a8ae57e2b2fa22ed6e 100644 (file)
@@ -298,7 +298,7 @@ struct rio_id_table {
  * struct rio_net - RIO network info
  * @node: Node in global list of RIO networks
  * @devices: List of devices in this network
- * @switches: List of switches in this netowrk
+ * @switches: List of switches in this network
  * @mports: List of master ports accessing this network
  * @hport: Default port for accessing this network
  * @id: RIO network ID
index ed8f9e70df9bcf72358ce9baf534c082699daa86..a0edb992c9c31a76bf314eb6f443c24c230083ce 100644 (file)
@@ -221,6 +221,7 @@ static inline void *sg_virt(struct scatterlist *sg)
 }
 
 int sg_nents(struct scatterlist *sg);
+int sg_nents_for_len(struct scatterlist *sg, u64 len);
 struct scatterlist *sg_next(struct scatterlist *);
 struct scatterlist *sg_last(struct scatterlist *s, unsigned int);
 void sg_init_table(struct scatterlist *, unsigned int);
index 7de815c6fa787221bc244d1be64da7dcb3c4d781..6633e83e608ab55fea2c5b5b64ef0058b12a035b 100644 (file)
@@ -132,6 +132,7 @@ struct fs_struct;
 struct perf_event_context;
 struct blk_plug;
 struct filename;
+struct nameidata;
 
 #define VMACACHE_BITS 2
 #define VMACACHE_SIZE (1U << VMACACHE_BITS)
@@ -261,7 +262,7 @@ extern char ___assert_task_state[1 - 2*!!(
 #define set_task_state(tsk, state_value)                       \
        do {                                                    \
                (tsk)->task_state_change = _THIS_IP_;           \
-               set_mb((tsk)->state, (state_value));            \
+               smp_store_mb((tsk)->state, (state_value));              \
        } while (0)
 
 /*
@@ -283,7 +284,7 @@ extern char ___assert_task_state[1 - 2*!!(
 #define set_current_state(state_value)                         \
        do {                                                    \
                current->task_state_change = _THIS_IP_;         \
-               set_mb(current->state, (state_value));          \
+               smp_store_mb(current->state, (state_value));            \
        } while (0)
 
 #else
@@ -291,7 +292,7 @@ extern char ___assert_task_state[1 - 2*!!(
 #define __set_task_state(tsk, state_value)             \
        do { (tsk)->state = (state_value); } while (0)
 #define set_task_state(tsk, state_value)               \
-       set_mb((tsk)->state, (state_value))
+       smp_store_mb((tsk)->state, (state_value))
 
 /*
  * set_current_state() includes a barrier so that the write of current->state
@@ -307,7 +308,7 @@ extern char ___assert_task_state[1 - 2*!!(
 #define __set_current_state(state_value)               \
        do { current->state = (state_value); } while (0)
 #define set_current_state(state_value)                 \
-       set_mb(current->state, (state_value))
+       smp_store_mb(current->state, (state_value))
 
 #endif
 
@@ -344,14 +345,10 @@ extern int runqueue_is_locked(int cpu);
 #if defined(CONFIG_SMP) && defined(CONFIG_NO_HZ_COMMON)
 extern void nohz_balance_enter_idle(int cpu);
 extern void set_cpu_sd_state_idle(void);
-extern int get_nohz_timer_target(int pinned);
+extern int get_nohz_timer_target(void);
 #else
 static inline void nohz_balance_enter_idle(int cpu) { }
 static inline void set_cpu_sd_state_idle(void) { }
-static inline int get_nohz_timer_target(int pinned)
-{
-       return smp_processor_id();
-}
 #endif
 
 /*
@@ -1422,9 +1419,6 @@ struct task_struct {
 #endif
 
        struct mm_struct *mm, *active_mm;
-#ifdef CONFIG_COMPAT_BRK
-       unsigned brk_randomized:1;
-#endif
        /* per-thread vma caching */
        u32 vmacache_seqnum;
        struct vm_area_struct *vmacache[VMACACHE_SIZE];
@@ -1447,10 +1441,14 @@ struct task_struct {
        /* Revert to default priority/policy when forking */
        unsigned sched_reset_on_fork:1;
        unsigned sched_contributes_to_load:1;
+       unsigned sched_migrated:1;
 
 #ifdef CONFIG_MEMCG_KMEM
        unsigned memcg_kmem_skip_account:1;
 #endif
+#ifdef CONFIG_COMPAT_BRK
+       unsigned brk_randomized:1;
+#endif
 
        unsigned long atomic_flags; /* Flags needing atomic access. */
 
@@ -1527,7 +1525,7 @@ struct task_struct {
                                       it with task_lock())
                                     - initialized normally by setup_new_exec */
 /* file system info */
-       int link_count, total_link_count;
+       struct nameidata *nameidata;
 #ifdef CONFIG_SYSVIPC
 /* ipc stuff */
        struct sysv_sem sysvsem;
@@ -2601,6 +2599,9 @@ static inline unsigned long wait_task_inactive(struct task_struct *p,
 }
 #endif
 
+#define tasklist_empty() \
+       list_empty(&init_task.tasks)
+
 #define next_task(p) \
        list_entry_rcu((p)->tasks.next, struct task_struct, tasks)
 
index 596a0e007c62d97e57d040ee45fa3df784403880..c9e4731cf10b8e97956b160c503e447490991931 100644 (file)
@@ -57,24 +57,12 @@ extern unsigned int sysctl_numa_balancing_scan_size;
 extern unsigned int sysctl_sched_migration_cost;
 extern unsigned int sysctl_sched_nr_migrate;
 extern unsigned int sysctl_sched_time_avg;
-extern unsigned int sysctl_timer_migration;
 extern unsigned int sysctl_sched_shares_window;
 
 int sched_proc_update_handler(struct ctl_table *table, int write,
                void __user *buffer, size_t *length,
                loff_t *ppos);
 #endif
-#ifdef CONFIG_SCHED_DEBUG
-static inline unsigned int get_sysctl_timer_migration(void)
-{
-       return sysctl_timer_migration;
-}
-#else
-static inline unsigned int get_sysctl_timer_migration(void)
-{
-       return 1;
-}
-#endif
 
 /*
  *  control realtime throttling:
index 18264ea9e314153488f9726b530993658c4cea25..52febde524794f5b0201ceba920c4c695edcf8e3 100644 (file)
@@ -43,7 +43,6 @@ struct file;
 struct vfsmount;
 struct path;
 struct qstr;
-struct nameidata;
 struct iattr;
 struct fown_struct;
 struct file_operations;
@@ -477,7 +476,8 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
  * @inode_follow_link:
  *     Check permission to follow a symbolic link when looking up a pathname.
  *     @dentry contains the dentry structure for the link.
- *     @nd contains the nameidata structure for the parent directory.
+ *     @inode contains the inode, which itself is not stable in RCU-walk
+ *     @rcu indicates whether we are in RCU-walk mode.
  *     Return 0 if permission is granted.
  * @inode_permission:
  *     Check permission before accessing an inode.  This hook is called by the
@@ -1553,7 +1553,8 @@ struct security_operations {
        int (*inode_rename) (struct inode *old_dir, struct dentry *old_dentry,
                             struct inode *new_dir, struct dentry *new_dentry);
        int (*inode_readlink) (struct dentry *dentry);
-       int (*inode_follow_link) (struct dentry *dentry, struct nameidata *nd);
+       int (*inode_follow_link) (struct dentry *dentry, struct inode *inode,
+                                 bool rcu);
        int (*inode_permission) (struct inode *inode, int mask);
        int (*inode_setattr)    (struct dentry *dentry, struct iattr *attr);
        int (*inode_getattr) (const struct path *path);
@@ -1839,7 +1840,8 @@ int security_inode_rename(struct inode *old_dir, struct dentry *old_dentry,
                          struct inode *new_dir, struct dentry *new_dentry,
                          unsigned int flags);
 int security_inode_readlink(struct dentry *dentry);
-int security_inode_follow_link(struct dentry *dentry, struct nameidata *nd);
+int security_inode_follow_link(struct dentry *dentry, struct inode *inode,
+                              bool rcu);
 int security_inode_permission(struct inode *inode, int mask);
 int security_inode_setattr(struct dentry *dentry, struct iattr *attr);
 int security_inode_getattr(const struct path *path);
@@ -2242,7 +2244,8 @@ static inline int security_inode_readlink(struct dentry *dentry)
 }
 
 static inline int security_inode_follow_link(struct dentry *dentry,
-                                             struct nameidata *nd)
+                                            struct inode *inode,
+                                            bool rcu)
 {
        return 0;
 }
index 5f68d0a391cee8506f8e0d94cda72d8bd357b10f..486e685a226a82d5cb841e61fb2ebf1562c5adb7 100644 (file)
@@ -233,6 +233,47 @@ static inline void raw_write_seqcount_end(seqcount_t *s)
        s->sequence++;
 }
 
+/**
+ * raw_write_seqcount_barrier - do a seq write barrier
+ * @s: pointer to seqcount_t
+ *
+ * This can be used to provide an ordering guarantee instead of the
+ * usual consistency guarantee. It is one wmb cheaper, because we can
+ * collapse the two back-to-back wmb()s.
+ *
+ *      seqcount_t seq;
+ *      bool X = true, Y = false;
+ *
+ *      void read(void)
+ *      {
+ *              bool x, y;
+ *
+ *              do {
+ *                      int s = read_seqcount_begin(&seq);
+ *
+ *                      x = X; y = Y;
+ *
+ *              } while (read_seqcount_retry(&seq, s));
+ *
+ *              BUG_ON(!x && !y);
+ *      }
+ *
+ *      void write(void)
+ *      {
+ *              Y = true;
+ *
+ *              raw_write_seqcount_barrier(seq);
+ *
+ *              X = false;
+ *      }
+ */
+static inline void raw_write_seqcount_barrier(seqcount_t *s)
+{
+       s->sequence++;
+       smp_wmb();
+       s->sequence++;
+}
+
 /*
  * raw_write_seqcount_latch - redirect readers to even/odd copy
  * @s: pointer to seqcount_t
@@ -266,13 +307,13 @@ static inline void write_seqcount_end(seqcount_t *s)
 }
 
 /**
- * write_seqcount_barrier - invalidate in-progress read-side seq operations
+ * write_seqcount_invalidate - invalidate in-progress read-side seq operations
  * @s: pointer to seqcount_t
  *
- * After write_seqcount_barrier, no read-side seq operations will complete
+ * After write_seqcount_invalidate, no read-side seq operations will complete
  * successfully and see data older than this.
  */
-static inline void write_seqcount_barrier(seqcount_t *s)
+static inline void write_seqcount_invalidate(seqcount_t *s)
 {
        smp_wmb();
        s->sequence+=2;
index 3e18379dfa6f349ba48edfc6615232af41ccfb99..0063b24b4f36df594b3587daadfdaf8849192c7d 100644 (file)
@@ -120,7 +120,7 @@ do {                                                                \
 /*
  * Despite its name it doesn't necessarily has to be a full barrier.
  * It should only guarantee that a STORE before the critical section
- * can not be reordered with a LOAD inside this section.
+ * can not be reordered with LOADs and STOREs inside this section.
  * spin_lock() is the one-way barrier, this LOAD can not escape out
  * of the region. So the default implementation simply ensures that
  * a STORE can not move into the critical section, smp_wmb() should
diff --git a/include/linux/sw842.h b/include/linux/sw842.h
new file mode 100644 (file)
index 0000000..109ba04
--- /dev/null
@@ -0,0 +1,12 @@
+#ifndef __SW842_H__
+#define __SW842_H__
+
+#define SW842_MEM_COMPRESS     (0xf000)
+
+int sw842_compress(const u8 *src, unsigned int srclen,
+                  u8 *dst, unsigned int *destlen, void *wmem);
+
+int sw842_decompress(const u8 *src, unsigned int srclen,
+                    u8 *dst, unsigned int *destlen);
+
+#endif
index f8492da57ad32e7607e320510a24ac0165dcd8cd..3741ba1a652c9716724bbef2ae1e9c12bc82aa8a 100644 (file)
@@ -13,8 +13,6 @@
 
 #ifdef CONFIG_GENERIC_CLOCKEVENTS
 extern void __init tick_init(void);
-extern void tick_freeze(void);
-extern void tick_unfreeze(void);
 /* Should be core only, but ARM BL switcher requires it */
 extern void tick_suspend_local(void);
 /* Should be core only, but XEN resume magic and ARM BL switcher require it */
@@ -23,14 +21,20 @@ extern void tick_handover_do_timer(void);
 extern void tick_cleanup_dead_cpu(int cpu);
 #else /* CONFIG_GENERIC_CLOCKEVENTS */
 static inline void tick_init(void) { }
-static inline void tick_freeze(void) { }
-static inline void tick_unfreeze(void) { }
 static inline void tick_suspend_local(void) { }
 static inline void tick_resume_local(void) { }
 static inline void tick_handover_do_timer(void) { }
 static inline void tick_cleanup_dead_cpu(int cpu) { }
 #endif /* !CONFIG_GENERIC_CLOCKEVENTS */
 
+#if defined(CONFIG_GENERIC_CLOCKEVENTS) && defined(CONFIG_SUSPEND)
+extern void tick_freeze(void);
+extern void tick_unfreeze(void);
+#else
+static inline void tick_freeze(void) { }
+static inline void tick_unfreeze(void) { }
+#endif
+
 #ifdef CONFIG_TICK_ONESHOT
 extern void tick_irq_enter(void);
 #  ifndef arch_needs_cpu
@@ -134,6 +138,12 @@ static inline bool tick_nohz_full_cpu(int cpu)
        return cpumask_test_cpu(cpu, tick_nohz_full_mask);
 }
 
+static inline void tick_nohz_full_add_cpus_to(struct cpumask *mask)
+{
+       if (tick_nohz_full_enabled())
+               cpumask_or(mask, mask, tick_nohz_full_mask);
+}
+
 extern void __tick_nohz_full_check(void);
 extern void tick_nohz_full_kick(void);
 extern void tick_nohz_full_kick_cpu(int cpu);
@@ -142,6 +152,7 @@ extern void __tick_nohz_task_switch(struct task_struct *tsk);
 #else
 static inline bool tick_nohz_full_enabled(void) { return false; }
 static inline bool tick_nohz_full_cpu(int cpu) { return false; }
+static inline void tick_nohz_full_add_cpus_to(struct cpumask *mask) { }
 static inline void __tick_nohz_full_check(void) { }
 static inline void tick_nohz_full_kick_cpu(int cpu) { }
 static inline void tick_nohz_full_kick(void) { }
index a3831478d9cf8db848a193fc908f7e54f340ffa8..77b5df2acd2adde021954ae8275b9f39d19c7c0f 100644 (file)
@@ -2,6 +2,7 @@
 #define _LINUX_TIME64_H
 
 #include <uapi/linux/time.h>
+#include <linux/math64.h>
 
 typedef __s64 time64_t;
 
@@ -28,6 +29,7 @@ struct timespec64 {
 #define FSEC_PER_SEC   1000000000000000LL
 
 /* Located here for timespec[64]_valid_strict */
+#define TIME64_MAX                     ((s64)~((u64)1 << 63))
 #define KTIME_MAX                      ((s64)~((u64)1 << 63))
 #define KTIME_SEC_MAX                  (KTIME_MAX / NSEC_PER_SEC)
 
index fb86963859c772846dfc531fc9cc8c0825f36ac7..25247220b4b7ddf3f336077b105ab42271475a05 100644 (file)
@@ -49,6 +49,8 @@ struct tk_read_base {
  * @offs_boot:         Offset clock monotonic -> clock boottime
  * @offs_tai:          Offset clock monotonic -> clock tai
  * @tai_offset:                The current UTC to TAI offset in seconds
+ * @clock_was_set_seq: The sequence number of clock was set events
+ * @next_leap_ktime:   CLOCK_MONOTONIC time value of a pending leap-second
  * @raw_time:          Monotonic raw base time in timespec64 format
  * @cycle_interval:    Number of clock cycles in one NTP interval
  * @xtime_interval:    Number of clock shifted nano seconds in one NTP
@@ -60,6 +62,9 @@ struct tk_read_base {
  *                     shifted nano seconds.
  * @ntp_error_shift:   Shift conversion between clock shifted nano seconds and
  *                     ntp shifted nano seconds.
+ * @last_warning:      Warning ratelimiter (DEBUG_TIMEKEEPING)
+ * @underflow_seen:    Underflow warning flag (DEBUG_TIMEKEEPING)
+ * @overflow_seen:     Overflow warning flag (DEBUG_TIMEKEEPING)
  *
  * Note: For timespec(64) based interfaces wall_to_monotonic is what
  * we need to add to xtime (or xtime corrected for sub jiffie times)
@@ -85,6 +90,8 @@ struct timekeeper {
        ktime_t                 offs_boot;
        ktime_t                 offs_tai;
        s32                     tai_offset;
+       unsigned int            clock_was_set_seq;
+       ktime_t                 next_leap_ktime;
        struct timespec64       raw_time;
 
        /* The following members are for timekeeping internal use */
@@ -104,6 +111,18 @@ struct timekeeper {
        s64                     ntp_error;
        u32                     ntp_error_shift;
        u32                     ntp_err_mult;
+#ifdef CONFIG_DEBUG_TIMEKEEPING
+       long                    last_warning;
+       /*
+        * These simple flag variables are managed
+        * without locks, which is racy, but they are
+        * ok since we don't really care about being
+        * super precise about how many events were
+        * seen, just that a problem was observed.
+        */
+       int                     underflow_seen;
+       int                     overflow_seen;
+#endif
 };
 
 #ifdef CONFIG_GENERIC_TIME_VSYSCALL
index 99176af216af449563e3a190b96edc04ea1a1f9e..3aa72e64865021ef0efd15c0511fee55a520c19e 100644 (file)
@@ -163,6 +163,7 @@ extern ktime_t ktime_get(void);
 extern ktime_t ktime_get_with_offset(enum tk_offsets offs);
 extern ktime_t ktime_mono_to_any(ktime_t tmono, enum tk_offsets offs);
 extern ktime_t ktime_get_raw(void);
+extern u32 ktime_get_resolution_ns(void);
 
 /**
  * ktime_get_real - get the real (wall-) time in ktime_t format
@@ -266,7 +267,6 @@ extern int persistent_clock_is_local;
 
 extern void read_persistent_clock(struct timespec *ts);
 extern void read_persistent_clock64(struct timespec64 *ts);
-extern void read_boot_clock(struct timespec *ts);
 extern void read_boot_clock64(struct timespec64 *ts);
 extern int update_persistent_clock(struct timespec now);
 extern int update_persistent_clock64(struct timespec64 now);
index 8c5a197e1587de4c647ff205b5b591c31a0dbcc6..61aa61dc410cf5035beb63c2873a471be4c50372 100644 (file)
@@ -14,27 +14,23 @@ struct timer_list {
         * All fields that change during normal runtime grouped to the
         * same cacheline
         */
-       struct list_head entry;
-       unsigned long expires;
-       struct tvec_base *base;
-
-       void (*function)(unsigned long);
-       unsigned long data;
-
-       int slack;
+       struct hlist_node       entry;
+       unsigned long           expires;
+       void                    (*function)(unsigned long);
+       unsigned long           data;
+       u32                     flags;
+       int                     slack;
 
 #ifdef CONFIG_TIMER_STATS
-       int start_pid;
-       void *start_site;
-       char start_comm[16];
+       int                     start_pid;
+       void                    *start_site;
+       char                    start_comm[16];
 #endif
 #ifdef CONFIG_LOCKDEP
-       struct lockdep_map lockdep_map;
+       struct lockdep_map      lockdep_map;
 #endif
 };
 
-extern struct tvec_base boot_tvec_bases;
-
 #ifdef CONFIG_LOCKDEP
 /*
  * NB: because we have to copy the lockdep_map, setting the lockdep_map key
@@ -49,9 +45,6 @@ extern struct tvec_base boot_tvec_bases;
 #endif
 
 /*
- * Note that all tvec_bases are at least 4 byte aligned and lower two bits
- * of base in timer_list is guaranteed to be zero. Use them for flags.
- *
  * A deferrable timer will work normally when the system is busy, but
  * will not cause a CPU to come out of idle just to service it; instead,
  * the timer will be serviced when the CPU eventually wakes up with a
@@ -65,17 +58,18 @@ extern struct tvec_base boot_tvec_bases;
  * workqueue locking issues. It's not meant for executing random crap
  * with interrupts disabled. Abuse is monitored!
  */
-#define TIMER_DEFERRABLE               0x1LU
-#define TIMER_IRQSAFE                  0x2LU
-
-#define TIMER_FLAG_MASK                        0x3LU
+#define TIMER_CPUMASK          0x0007FFFF
+#define TIMER_MIGRATING                0x00080000
+#define TIMER_BASEMASK         (TIMER_CPUMASK | TIMER_MIGRATING)
+#define TIMER_DEFERRABLE       0x00100000
+#define TIMER_IRQSAFE          0x00200000
 
 #define __TIMER_INITIALIZER(_function, _expires, _data, _flags) { \
-               .entry = { .prev = TIMER_ENTRY_STATIC },        \
+               .entry = { .next = TIMER_ENTRY_STATIC },        \
                .function = (_function),                        \
                .expires = (_expires),                          \
                .data = (_data),                                \
-               .base = (void *)((unsigned long)&boot_tvec_bases + (_flags)), \
+               .flags = (_flags),                              \
                .slack = -1,                                    \
                __TIMER_LOCKDEP_MAP_INITIALIZER(                \
                        __FILE__ ":" __stringify(__LINE__))     \
@@ -168,7 +162,7 @@ static inline void init_timer_on_stack_key(struct timer_list *timer,
  */
 static inline int timer_pending(const struct timer_list * timer)
 {
-       return timer->entry.next != NULL;
+       return timer->entry.pprev != NULL;
 }
 
 extern void add_timer_on(struct timer_list *timer, int cpu);
@@ -187,13 +181,6 @@ extern void set_timer_slack(struct timer_list *time, int slack_hz);
  */
 #define NEXT_TIMER_MAX_DELTA   ((1UL << 30) - 1)
 
-/*
- * Return when the next timer-wheel timeout occurs (in absolute jiffies),
- * locks the timer base and does the comparison against the given
- * jiffie.
- */
-extern unsigned long get_next_timer_interrupt(unsigned long now);
-
 /*
  * Timer-statistics info:
  */
@@ -201,13 +188,10 @@ extern unsigned long get_next_timer_interrupt(unsigned long now);
 
 extern int timer_stats_active;
 
-#define TIMER_STATS_FLAG_DEFERRABLE    0x1
-
 extern void init_timer_stats(void);
 
 extern void timer_stats_update_stats(void *timer, pid_t pid, void *startf,
-                                    void *timerf, char *comm,
-                                    unsigned int timer_flag);
+                                    void *timerf, char *comm, u32 flags);
 
 extern void __timer_stats_timer_set_start_info(struct timer_list *timer,
                                               void *addr);
@@ -254,6 +238,15 @@ extern void run_local_timers(void);
 struct hrtimer;
 extern enum hrtimer_restart it_real_fn(struct hrtimer *);
 
+#if defined(CONFIG_SMP) && defined(CONFIG_NO_HZ_COMMON)
+#include <linux/sysctl.h>
+
+extern unsigned int sysctl_timer_migration;
+int timer_migration_handler(struct ctl_table *table, int write,
+                           void __user *buffer, size_t *lenp,
+                           loff_t *ppos);
+#endif
+
 unsigned long __round_jiffies(unsigned long j, int cpu);
 unsigned long __round_jiffies_relative(unsigned long j, int cpu);
 unsigned long round_jiffies(unsigned long j);
index a520fd70a59f371f40a34f79e883dcf7b32c23f7..7eec17ad7fa195bba39c06e44817ef3f3a1b0402 100644 (file)
@@ -16,10 +16,10 @@ struct timerqueue_head {
 };
 
 
-extern void timerqueue_add(struct timerqueue_head *head,
-                               struct timerqueue_node *node);
-extern void timerqueue_del(struct timerqueue_head *head,
-                               struct timerqueue_node *node);
+extern bool timerqueue_add(struct timerqueue_head *head,
+                          struct timerqueue_node *node);
+extern bool timerqueue_del(struct timerqueue_head *head,
+                          struct timerqueue_node *node);
 extern struct timerqueue_node *timerqueue_iterate_next(
                                                struct timerqueue_node *node);
 
index 59698be034908505d0695fd2eec51efbb4d28d53..8715287c3b1f636d21f01acb4ce9220a59764b08 100644 (file)
@@ -139,12 +139,20 @@ typedef unsigned long blkcnt_t;
  */
 #define pgoff_t unsigned long
 
-/* A dma_addr_t can hold any valid DMA or bus address for the platform */
+/*
+ * A dma_addr_t can hold any valid DMA address, i.e., any address returned
+ * by the DMA API.
+ *
+ * If the DMA API only uses 32-bit addresses, dma_addr_t need only be 32
+ * bits wide.  Bus addresses, e.g., PCI BARs, may be wider than 32 bits,
+ * but drivers do memory-mapped I/O to ioremapped kernel virtual addresses,
+ * so they don't care about the size of the actual bus addresses.
+ */
 #ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
 typedef u64 dma_addr_t;
 #else
 typedef u32 dma_addr_t;
-#endif /* dma_addr_t */
+#endif
 
 typedef unsigned __bitwise__ gfp_t;
 typedef unsigned __bitwise__ fmode_t;
diff --git a/include/misc/cxl-base.h b/include/misc/cxl-base.h
new file mode 100644 (file)
index 0000000..5ae9625
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2014 IBM Corp.
+ *
+ * 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 _MISC_CXL_BASE_H
+#define _MISC_CXL_BASE_H
+
+#ifdef CONFIG_CXL_BASE
+
+#define CXL_IRQ_RANGES 4
+
+struct cxl_irq_ranges {
+       irq_hw_number_t offset[CXL_IRQ_RANGES];
+       irq_hw_number_t range[CXL_IRQ_RANGES];
+};
+
+extern atomic_t cxl_use_count;
+
+static inline bool cxl_ctx_in_use(void)
+{
+       return (atomic_read(&cxl_use_count) != 0);
+}
+
+static inline void cxl_ctx_get(void)
+{
+       atomic_inc(&cxl_use_count);
+}
+
+static inline void cxl_ctx_put(void)
+{
+       atomic_dec(&cxl_use_count);
+}
+
+void cxl_slbia(struct mm_struct *mm);
+
+#else /* CONFIG_CXL_BASE */
+
+static inline bool cxl_ctx_in_use(void) { return false; }
+static inline void cxl_slbia(struct mm_struct *mm) {}
+
+#endif /* CONFIG_CXL_BASE */
+
+#endif
index 975cc7861f184afc255397b87f926c18269bcbf8..7a6c1d6cc1732e1fc83432cd46e65ed25a5fa4ef 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2014 IBM Corp.
+ * Copyright 2015 IBM Corp.
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
 #ifndef _MISC_CXL_H
 #define _MISC_CXL_H
 
-#ifdef CONFIG_CXL_BASE
+#include <linux/pci.h>
+#include <linux/poll.h>
+#include <linux/interrupt.h>
+#include <uapi/misc/cxl.h>
 
-#define CXL_IRQ_RANGES 4
+/*
+ * This documents the in kernel API for driver to use CXL. It allows kernel
+ * drivers to bind to AFUs using an AFU configuration record exposed as a PCI
+ * configuration record.
+ *
+ * This API enables control over AFU and contexts which can't be part of the
+ * generic PCI API. This API is agnostic to the actual AFU.
+ */
+
+/* Get the AFU associated with a pci_dev */
+struct cxl_afu *cxl_pci_to_afu(struct pci_dev *dev);
+
+/* Get the AFU conf record number associated with a pci_dev */
+unsigned int cxl_pci_to_cfg_record(struct pci_dev *dev);
+
+/* Get the physical device (ie. the PCIe card) which the AFU is attached */
+struct device *cxl_get_phys_dev(struct pci_dev *dev);
+
+
+/*
+ * Context lifetime overview:
+ *
+ * An AFU context may be inited and then started and stoppped multiple times
+ * before it's released. ie.
+ *    - cxl_dev_context_init()
+ *      - cxl_start_context()
+ *      - cxl_stop_context()
+ *      - cxl_start_context()
+ *      - cxl_stop_context()
+ *     ...repeat...
+ *    - cxl_release_context()
+ * Once released, a context can't be started again.
+ *
+ * One context is inited by the cxl driver for every pci_dev. This is to be
+ * used as a default kernel context. cxl_get_context() will get this
+ * context. This context will be released by PCI hot unplug, so doesn't need to
+ * be released explicitly by drivers.
+ *
+ * Additional kernel contexts may be inited using cxl_dev_context_init().
+ * These must be released using cxl_context_detach().
+ *
+ * Once a context has been inited, IRQs may be configured. Firstly these IRQs
+ * must be allocated (cxl_allocate_afu_irqs()), then individually mapped to
+ * specific handlers (cxl_map_afu_irq()).
+ *
+ * These IRQs can be unmapped (cxl_unmap_afu_irq()) and finally released
+ * (cxl_free_afu_irqs()).
+ *
+ * The AFU can be reset (cxl_afu_reset()). This will cause the PSL/AFU
+ * hardware to lose track of all contexts. It's upto the caller of
+ * cxl_afu_reset() to restart these contexts.
+ */
+
+/*
+ * On pci_enabled_device(), the cxl driver will init a single cxl context for
+ * use by the driver. It doesn't start this context (as that will likely
+ * generate DMA traffic for most AFUs).
+ *
+ * This gets the default context associated with this pci_dev.  This context
+ * doesn't need to be released as this will be done by the PCI subsystem on hot
+ * unplug.
+ */
+struct cxl_context *cxl_get_context(struct pci_dev *dev);
+/*
+ * Allocate and initalise a context associated with a AFU PCI device. This
+ * doesn't start the context in the AFU.
+ */
+struct cxl_context *cxl_dev_context_init(struct pci_dev *dev);
+/*
+ * Release and free a context. Context should be stopped before calling.
+ */
+int cxl_release_context(struct cxl_context *ctx);
 
-struct cxl_irq_ranges {
-       irq_hw_number_t offset[CXL_IRQ_RANGES];
-       irq_hw_number_t range[CXL_IRQ_RANGES];
-};
+/*
+ * Allocate AFU interrupts for this context. num=0 will allocate the default
+ * for this AFU as given in the AFU descriptor. This number doesn't include the
+ * interrupt 0 (CAIA defines AFU IRQ 0 for page faults). Each interrupt to be
+ * used must map a handler with cxl_map_afu_irq.
+ */
+int cxl_allocate_afu_irqs(struct cxl_context *cxl, int num);
+/* Free allocated interrupts */
+void cxl_free_afu_irqs(struct cxl_context *cxl);
+
+/*
+ * Map a handler for an AFU interrupt associated with a particular context. AFU
+ * IRQS numbers start from 1 (CAIA defines AFU IRQ 0 for page faults). cookie
+ * is private data is that will be provided to the interrupt handler.
+ */
+int cxl_map_afu_irq(struct cxl_context *cxl, int num,
+                   irq_handler_t handler, void *cookie, char *name);
+/* unmap mapped IRQ handlers */
+void cxl_unmap_afu_irq(struct cxl_context *cxl, int num, void *cookie);
 
-extern atomic_t cxl_use_count;
+/*
+ * Start work on the AFU. This starts an cxl context and associates it with a
+ * task. task == NULL will make it a kernel context.
+ */
+int cxl_start_context(struct cxl_context *ctx, u64 wed,
+                     struct task_struct *task);
+/*
+ * Stop a context and remove it from the PSL
+ */
+int cxl_stop_context(struct cxl_context *ctx);
 
-static inline bool cxl_ctx_in_use(void)
-{
-       return (atomic_read(&cxl_use_count) != 0);
-}
+/* Reset the AFU */
+int cxl_afu_reset(struct cxl_context *ctx);
 
-static inline void cxl_ctx_get(void)
-{
-       atomic_inc(&cxl_use_count);
-}
+/*
+ * Set a context as a master context.
+ * This sets the default problem space area mapped as the full space, rather
+ * than just the per context area (for slaves).
+ */
+void cxl_set_master(struct cxl_context *ctx);
 
-static inline void cxl_ctx_put(void)
-{
-       atomic_dec(&cxl_use_count);
-}
+/*
+ * Map and unmap the AFU Problem Space area. The amount and location mapped
+ * depends on if this context is a master or slave.
+ */
+void __iomem *cxl_psa_map(struct cxl_context *ctx);
+void cxl_psa_unmap(void __iomem *addr);
 
-void cxl_slbia(struct mm_struct *mm);
+/*  Get the process element for this context */
+int cxl_process_element(struct cxl_context *ctx);
 
-#else /* CONFIG_CXL_BASE */
 
-static inline bool cxl_ctx_in_use(void) { return false; }
-static inline void cxl_slbia(struct mm_struct *mm) {}
+/*
+ * These calls allow drivers to create their own file descriptors and make them
+ * identical to the cxl file descriptor user API. An example use case:
+ *
+ * struct file_operations cxl_my_fops = {};
+ * ......
+ *     // Init the context
+ *     ctx = cxl_dev_context_init(dev);
+ *     if (IS_ERR(ctx))
+ *             return PTR_ERR(ctx);
+ *     // Create and attach a new file descriptor to my file ops
+ *     file = cxl_get_fd(ctx, &cxl_my_fops, &fd);
+ *     // Start context
+ *     rc = cxl_start_work(ctx, &work.work);
+ *     if (rc) {
+ *             fput(file);
+ *             put_unused_fd(fd);
+ *             return -ENODEV;
+ *     }
+ *     // No error paths after installing the fd
+ *     fd_install(fd, file);
+ *     return fd;
+ *
+ * This inits a context, and gets a file descriptor and associates some file
+ * ops to that file descriptor. If the file ops are blank, the cxl driver will
+ * fill them in with the default ones that mimic the standard user API.  Once
+ * completed, the file descriptor can be installed. Once the file descriptor is
+ * installed, it's visible to the user so no errors must occur past this point.
+ *
+ * If cxl_fd_release() file op call is installed, the context will be stopped
+ * and released when the fd is released. Hence the driver won't need to manage
+ * this itself.
+ */
 
-#endif /* CONFIG_CXL_BASE */
+/*
+ * Take a context and associate it with my file ops. Returns the associated
+ * file and file descriptor. Any file ops which are blank are filled in by the
+ * cxl driver with the default ops to mimic the standard API.
+ */
+struct file *cxl_get_fd(struct cxl_context *ctx, struct file_operations *fops,
+                       int *fd);
+/* Get the context associated with this file */
+struct cxl_context *cxl_fops_get_context(struct file *file);
+/*
+ * Start a context associated a struct cxl_ioctl_start_work used by the
+ * standard cxl user API.
+ */
+int cxl_start_work(struct cxl_context *ctx,
+                  struct cxl_ioctl_start_work *work);
+/*
+ * Export all the existing fops so drivers can use them
+ */
+int cxl_fd_open(struct inode *inode, struct file *file);
+int cxl_fd_release(struct inode *inode, struct file *file);
+long cxl_fd_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
+int cxl_fd_mmap(struct file *file, struct vm_area_struct *vm);
+unsigned int cxl_fd_poll(struct file *file, struct poll_table_struct *poll);
+ssize_t cxl_fd_read(struct file *file, char __user *buf, size_t count,
+                          loff_t *off);
 
-#endif
+#endif /* _MISC_CXL_H */
index 36ac102c97c72b1b5d62f99e28bf285fbad9f8bb..f0ee97eec24d28625d9c3f714ab18a72b0b8f125 100644 (file)
@@ -168,6 +168,7 @@ struct xfrm_state {
        struct xfrm_algo        *ealg;
        struct xfrm_algo        *calg;
        struct xfrm_algo_aead   *aead;
+       const char              *geniv;
 
        /* Data for encapsulator */
        struct xfrm_encap_tmpl  *encap;
@@ -1314,6 +1315,7 @@ static inline int xfrm_id_proto_match(u8 proto, u8 userproto)
  * xfrm algorithm information
  */
 struct xfrm_algo_aead_info {
+       char *geniv;
        u16 icv_truncbits;
 };
 
@@ -1323,6 +1325,7 @@ struct xfrm_algo_auth_info {
 };
 
 struct xfrm_algo_encr_info {
+       char *geniv;
        u16 blockbits;
        u16 defkeybits;
 };
index ac54c27a2bfd39a2f5ec950afe57963c57b04a42..fde33ac6b58a1e4eccb52512055f546d3a59e2a6 100644 (file)
@@ -111,8 +111,8 @@ int rdma_copy_addr(struct rdma_dev_addr *dev_addr, struct net_device *dev,
 int rdma_addr_size(struct sockaddr *addr);
 
 int rdma_addr_find_smac_by_sgid(union ib_gid *sgid, u8 *smac, u16 *vlan_id);
-int rdma_addr_find_dmac_by_grh(union ib_gid *sgid, union ib_gid *dgid, u8 *smac,
-                              u16 *vlan_id);
+int rdma_addr_find_dmac_by_grh(const union ib_gid *sgid, const union ib_gid *dgid,
+                              u8 *smac, u16 *vlan_id);
 
 static inline u16 ib_addr_get_pkey(struct rdma_dev_addr *dev_addr)
 {
@@ -160,7 +160,7 @@ static inline int rdma_ip2gid(struct sockaddr *addr, union ib_gid *gid)
 }
 
 /* Important - sockaddr should be a union of sockaddr_in and sockaddr_in6 */
-static inline void rdma_gid2ip(struct sockaddr *out, union ib_gid *gid)
+static inline void rdma_gid2ip(struct sockaddr *out, const union ib_gid *gid)
 {
        if (ipv6_addr_v4mapped((struct in6_addr *)gid)) {
                struct sockaddr_in *out_in = (struct sockaddr_in *)out;
index ad9a3c280944ff3581ac9313a04387198f2c0fd9..bd92130f4ac5803a60e0c058bc19d26faca9f948 100644 (file)
@@ -64,10 +64,10 @@ int ib_get_cached_gid(struct ib_device    *device,
  * ib_find_cached_gid() searches for the specified GID value in
  * the local software cache.
  */
-int ib_find_cached_gid(struct ib_device *device,
-                      union ib_gid     *gid,
-                      u8               *port_num,
-                      u16              *index);
+int ib_find_cached_gid(struct ib_device   *device,
+                      const union ib_gid *gid,
+                      u8                 *port_num,
+                      u16                *index);
 
 /**
  * ib_get_cached_pkey - Returns a cached PKey table entry
index 9bb99e983f583d28fd81d978484022495f7744fc..c8422d5a5a91f2256bc59455e3d996b0bdc69d6c 100644 (file)
 #include <rdma/ib_verbs.h>
 #include <uapi/rdma/ib_user_mad.h>
 
-/* Management base version */
+/* Management base versions */
 #define IB_MGMT_BASE_VERSION                   1
+#define OPA_MGMT_BASE_VERSION                  0x80
+
+#define OPA_SMP_CLASS_VERSION                  0x80
 
 /* Management classes */
 #define IB_MGMT_CLASS_SUBN_LID_ROUTED          0x01
@@ -135,6 +138,10 @@ enum {
        IB_MGMT_SA_DATA = 200,
        IB_MGMT_DEVICE_HDR = 64,
        IB_MGMT_DEVICE_DATA = 192,
+       IB_MGMT_MAD_SIZE = IB_MGMT_MAD_HDR + IB_MGMT_MAD_DATA,
+       OPA_MGMT_MAD_DATA = 2024,
+       OPA_MGMT_RMPP_DATA = 2012,
+       OPA_MGMT_MAD_SIZE = IB_MGMT_MAD_HDR + OPA_MGMT_MAD_DATA,
 };
 
 struct ib_mad_hdr {
@@ -181,12 +188,23 @@ struct ib_mad {
        u8                      data[IB_MGMT_MAD_DATA];
 };
 
+struct opa_mad {
+       struct ib_mad_hdr       mad_hdr;
+       u8                      data[OPA_MGMT_MAD_DATA];
+};
+
 struct ib_rmpp_mad {
        struct ib_mad_hdr       mad_hdr;
        struct ib_rmpp_hdr      rmpp_hdr;
        u8                      data[IB_MGMT_RMPP_DATA];
 };
 
+struct opa_rmpp_mad {
+       struct ib_mad_hdr       mad_hdr;
+       struct ib_rmpp_hdr      rmpp_hdr;
+       u8                      data[OPA_MGMT_RMPP_DATA];
+};
+
 struct ib_sa_mad {
        struct ib_mad_hdr       mad_hdr;
        struct ib_rmpp_hdr      rmpp_hdr;
@@ -235,7 +253,10 @@ struct ib_class_port_info {
  *   includes the common MAD, RMPP, and class specific headers.
  * @data_len: Indicates the total size of user-transferred data.
  * @seg_count: The number of RMPP segments allocated for this send.
- * @seg_size: Size of each RMPP segment.
+ * @seg_size: Size of the data in each RMPP segment.  This does not include
+ *   class specific headers.
+ * @seg_rmpp_size: Size of each RMPP segment including the class specific
+ *   headers.
  * @timeout_ms: Time to wait for a response.
  * @retries: Number of times to retry a request for a response.  For MADs
  *   using RMPP, this applies per window.  On completion, returns the number
@@ -255,6 +276,7 @@ struct ib_mad_send_buf {
        int                     data_len;
        int                     seg_count;
        int                     seg_size;
+       int                     seg_rmpp_size;
        int                     timeout_ms;
        int                     retries;
 };
@@ -263,7 +285,7 @@ struct ib_mad_send_buf {
  * ib_response_mad - Returns if the specified MAD has been generated in
  *   response to a sent request or trap.
  */
-int ib_response_mad(struct ib_mad *mad);
+int ib_response_mad(const struct ib_mad_hdr *hdr);
 
 /**
  * ib_get_rmpp_resptime - Returns the RMPP response time.
@@ -401,7 +423,10 @@ struct ib_mad_send_wc {
 struct ib_mad_recv_buf {
        struct list_head        list;
        struct ib_grh           *grh;
-       struct ib_mad           *mad;
+       union {
+               struct ib_mad   *mad;
+               struct opa_mad  *opa_mad;
+       };
 };
 
 /**
@@ -410,6 +435,7 @@ struct ib_mad_recv_buf {
  * @recv_buf: Specifies the location of the received data buffer(s).
  * @rmpp_list: Specifies a list of RMPP reassembled received MAD buffers.
  * @mad_len: The length of the received MAD, without duplicated headers.
+ * @mad_seg_size: The size of individual MAD segments
  *
  * For received response, the wr_id contains a pointer to the ib_mad_send_buf
  *   for the corresponding send request.
@@ -419,6 +445,7 @@ struct ib_mad_recv_wc {
        struct ib_mad_recv_buf  recv_buf;
        struct list_head        rmpp_list;
        int                     mad_len;
+       size_t                  mad_seg_size;
 };
 
 /**
@@ -618,6 +645,7 @@ int ib_process_mad_wc(struct ib_mad_agent *mad_agent,
  *   automatically adjust the allocated buffer size to account for any
  *   additional padding that may be necessary.
  * @gfp_mask: GFP mask used for the memory allocation.
+ * @base_version: Base Version of this MAD
  *
  * This routine allocates a MAD for sending.  The returned MAD send buffer
  * will reference a data buffer usable for sending a MAD, along
@@ -633,7 +661,8 @@ struct ib_mad_send_buf *ib_create_send_mad(struct ib_mad_agent *mad_agent,
                                           u32 remote_qpn, u16 pkey_index,
                                           int rmpp_active,
                                           int hdr_len, int data_len,
-                                          gfp_t gfp_mask);
+                                          gfp_t gfp_mask,
+                                          u8 base_version);
 
 /**
  * ib_is_mad_class_rmpp - returns whether given management class
@@ -675,6 +704,6 @@ void ib_free_send_mad(struct ib_mad_send_buf *send_buf);
  * @agent: the agent in question
  * @return: true if agent is performing rmpp, false otherwise.
  */
-int ib_mad_kernel_rmpp_agent(struct ib_mad_agent *agent);
+int ib_mad_kernel_rmpp_agent(const struct ib_mad_agent *agent);
 
 #endif /* IB_MAD_H */
index 65994a19e84055e7b4f4d8cde83a07eb41c1a69a..986fddb085796035a44c69e48779ec84393415f8 100644 (file)
@@ -81,6 +81,13 @@ enum rdma_transport_type {
        RDMA_TRANSPORT_USNIC_UDP
 };
 
+enum rdma_protocol_type {
+       RDMA_PROTOCOL_IB,
+       RDMA_PROTOCOL_IBOE,
+       RDMA_PROTOCOL_IWARP,
+       RDMA_PROTOCOL_USNIC_UDP
+};
+
 __attribute_const__ enum rdma_transport_type
 rdma_node_get_transport(enum rdma_node_type node_type);
 
@@ -166,6 +173,16 @@ struct ib_odp_caps {
        } per_transport_caps;
 };
 
+enum ib_cq_creation_flags {
+       IB_CQ_FLAGS_TIMESTAMP_COMPLETION   = 1 << 0,
+};
+
+struct ib_cq_init_attr {
+       unsigned int    cqe;
+       int             comp_vector;
+       u32             flags;
+};
+
 struct ib_device_attr {
        u64                     fw_ver;
        __be64                  sys_image_guid;
@@ -210,6 +227,8 @@ struct ib_device_attr {
        int                     sig_prot_cap;
        int                     sig_guard_cap;
        struct ib_odp_caps      odp_caps;
+       uint64_t                timestamp_mask;
+       uint64_t                hca_core_clock; /* in KHZ */
 };
 
 enum ib_mtu {
@@ -346,6 +365,42 @@ union rdma_protocol_stats {
        struct iw_protocol_stats        iw;
 };
 
+/* Define bits for the various functionality this port needs to be supported by
+ * the core.
+ */
+/* Management                           0x00000FFF */
+#define RDMA_CORE_CAP_IB_MAD            0x00000001
+#define RDMA_CORE_CAP_IB_SMI            0x00000002
+#define RDMA_CORE_CAP_IB_CM             0x00000004
+#define RDMA_CORE_CAP_IW_CM             0x00000008
+#define RDMA_CORE_CAP_IB_SA             0x00000010
+#define RDMA_CORE_CAP_OPA_MAD           0x00000020
+
+/* Address format                       0x000FF000 */
+#define RDMA_CORE_CAP_AF_IB             0x00001000
+#define RDMA_CORE_CAP_ETH_AH            0x00002000
+
+/* Protocol                             0xFFF00000 */
+#define RDMA_CORE_CAP_PROT_IB           0x00100000
+#define RDMA_CORE_CAP_PROT_ROCE         0x00200000
+#define RDMA_CORE_CAP_PROT_IWARP        0x00400000
+
+#define RDMA_CORE_PORT_IBA_IB          (RDMA_CORE_CAP_PROT_IB  \
+                                       | RDMA_CORE_CAP_IB_MAD \
+                                       | RDMA_CORE_CAP_IB_SMI \
+                                       | RDMA_CORE_CAP_IB_CM  \
+                                       | RDMA_CORE_CAP_IB_SA  \
+                                       | RDMA_CORE_CAP_AF_IB)
+#define RDMA_CORE_PORT_IBA_ROCE        (RDMA_CORE_CAP_PROT_ROCE \
+                                       | RDMA_CORE_CAP_IB_MAD  \
+                                       | RDMA_CORE_CAP_IB_CM   \
+                                       | RDMA_CORE_CAP_AF_IB   \
+                                       | RDMA_CORE_CAP_ETH_AH)
+#define RDMA_CORE_PORT_IWARP           (RDMA_CORE_CAP_PROT_IWARP \
+                                       | RDMA_CORE_CAP_IW_CM)
+#define RDMA_CORE_PORT_INTEL_OPA       (RDMA_CORE_PORT_IBA_IB  \
+                                       | RDMA_CORE_CAP_OPA_MAD)
+
 struct ib_port_attr {
        enum ib_port_state      state;
        enum ib_mtu             max_mtu;
@@ -412,6 +467,8 @@ enum ib_event_type {
        IB_EVENT_GID_CHANGE,
 };
 
+__attribute_const__ const char *ib_event_msg(enum ib_event_type event);
+
 struct ib_event {
        struct ib_device        *device;
        union {
@@ -663,6 +720,8 @@ enum ib_wc_status {
        IB_WC_GENERAL_ERR
 };
 
+__attribute_const__ const char *ib_wc_status_msg(enum ib_wc_status status);
+
 enum ib_wc_opcode {
        IB_WC_SEND,
        IB_WC_RDMA_WRITE,
@@ -1407,7 +1466,7 @@ struct ib_flow {
        struct ib_uobject       *uobject;
 };
 
-struct ib_mad;
+struct ib_mad_hdr;
 struct ib_grh;
 
 enum ib_process_mad_flags {
@@ -1474,6 +1533,13 @@ struct ib_dma_mapping_ops {
 
 struct iw_cm_verbs;
 
+struct ib_port_immutable {
+       int                           pkey_tbl_len;
+       int                           gid_tbl_len;
+       u32                           core_cap_flags;
+       u32                           max_mad_size;
+};
+
 struct ib_device {
        struct device                *dma_device;
 
@@ -1487,8 +1553,10 @@ struct ib_device {
        struct list_head              client_data_list;
 
        struct ib_cache               cache;
-       int                          *pkey_tbl_len;
-       int                          *gid_tbl_len;
+       /**
+        * port_immutable is indexed by port number
+        */
+       struct ib_port_immutable     *port_immutable;
 
        int                           num_comp_vectors;
 
@@ -1497,7 +1565,8 @@ struct ib_device {
        int                        (*get_protocol_stats)(struct ib_device *device,
                                                         union rdma_protocol_stats *stats);
        int                        (*query_device)(struct ib_device *device,
-                                                  struct ib_device_attr *device_attr);
+                                                  struct ib_device_attr *device_attr,
+                                                  struct ib_udata *udata);
        int                        (*query_port)(struct ib_device *device,
                                                 u8 port_num,
                                                 struct ib_port_attr *port_attr);
@@ -1561,8 +1630,8 @@ struct ib_device {
        int                        (*post_recv)(struct ib_qp *qp,
                                                struct ib_recv_wr *recv_wr,
                                                struct ib_recv_wr **bad_recv_wr);
-       struct ib_cq *             (*create_cq)(struct ib_device *device, int cqe,
-                                               int comp_vector,
+       struct ib_cq *             (*create_cq)(struct ib_device *device,
+                                               const struct ib_cq_init_attr *attr,
                                                struct ib_ucontext *context,
                                                struct ib_udata *udata);
        int                        (*modify_cq)(struct ib_cq *cq, u16 cq_count,
@@ -1637,10 +1706,13 @@ struct ib_device {
        int                        (*process_mad)(struct ib_device *device,
                                                  int process_mad_flags,
                                                  u8 port_num,
-                                                 struct ib_wc *in_wc,
-                                                 struct ib_grh *in_grh,
-                                                 struct ib_mad *in_mad,
-                                                 struct ib_mad *out_mad);
+                                                 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);
        struct ib_xrcd *           (*alloc_xrcd)(struct ib_device *device,
                                                 struct ib_ucontext *ucontext,
                                                 struct ib_udata *udata);
@@ -1675,6 +1747,14 @@ struct ib_device {
        u32                          local_dma_lkey;
        u8                           node_type;
        u8                           phys_port_cnt;
+
+       /**
+        * The following mandatory functions are used only at device
+        * registration.  Keep functions such as these at the end of this
+        * structure to avoid cache line misses when accessing struct ib_device
+        * in fast paths.
+        */
+       int (*get_port_immutable)(struct ib_device *, u8, struct ib_port_immutable *);
 };
 
 struct ib_client {
@@ -1743,6 +1823,284 @@ int ib_query_port(struct ib_device *device,
 enum rdma_link_layer rdma_port_get_link_layer(struct ib_device *device,
                                               u8 port_num);
 
+/**
+ * rdma_start_port - Return the first valid port number for the device
+ * specified
+ *
+ * @device: Device to be checked
+ *
+ * Return start port number
+ */
+static inline u8 rdma_start_port(const struct ib_device *device)
+{
+       return (device->node_type == RDMA_NODE_IB_SWITCH) ? 0 : 1;
+}
+
+/**
+ * rdma_end_port - Return the last valid port number for the device
+ * specified
+ *
+ * @device: Device to be checked
+ *
+ * Return last port number
+ */
+static inline u8 rdma_end_port(const struct ib_device *device)
+{
+       return (device->node_type == RDMA_NODE_IB_SWITCH) ?
+               0 : device->phys_port_cnt;
+}
+
+static inline bool rdma_protocol_ib(const struct ib_device *device, u8 port_num)
+{
+       return device->port_immutable[port_num].core_cap_flags & RDMA_CORE_CAP_PROT_IB;
+}
+
+static inline bool rdma_protocol_roce(const struct ib_device *device, u8 port_num)
+{
+       return device->port_immutable[port_num].core_cap_flags & RDMA_CORE_CAP_PROT_ROCE;
+}
+
+static inline bool rdma_protocol_iwarp(const struct ib_device *device, u8 port_num)
+{
+       return device->port_immutable[port_num].core_cap_flags & RDMA_CORE_CAP_PROT_IWARP;
+}
+
+static inline bool rdma_ib_or_roce(const struct ib_device *device, u8 port_num)
+{
+       return device->port_immutable[port_num].core_cap_flags &
+               (RDMA_CORE_CAP_PROT_IB | RDMA_CORE_CAP_PROT_ROCE);
+}
+
+/**
+ * rdma_cap_ib_mad - Check if the port of a device supports Infiniband
+ * Management Datagrams.
+ * @device: Device to check
+ * @port_num: Port number to check
+ *
+ * Management Datagrams (MAD) are a required part of the InfiniBand
+ * specification and are supported on all InfiniBand devices.  A slightly
+ * extended version are also supported on OPA interfaces.
+ *
+ * Return: true if the port supports sending/receiving of MAD packets.
+ */
+static inline bool rdma_cap_ib_mad(const struct ib_device *device, u8 port_num)
+{
+       return device->port_immutable[port_num].core_cap_flags & RDMA_CORE_CAP_IB_MAD;
+}
+
+/**
+ * rdma_cap_opa_mad - Check if the port of device provides support for OPA
+ * Management Datagrams.
+ * @device: Device to check
+ * @port_num: Port number to check
+ *
+ * Intel OmniPath devices extend and/or replace the InfiniBand Management
+ * datagrams with their own versions.  These OPA MADs share many but not all of
+ * the characteristics of InfiniBand MADs.
+ *
+ * OPA MADs differ in the following ways:
+ *
+ *    1) MADs are variable size up to 2K
+ *       IBTA defined MADs remain fixed at 256 bytes
+ *    2) OPA SMPs must carry valid PKeys
+ *    3) OPA SMP packets are a different format
+ *
+ * Return: true if the port supports OPA MAD packet formats.
+ */
+static inline bool rdma_cap_opa_mad(struct ib_device *device, u8 port_num)
+{
+       return (device->port_immutable[port_num].core_cap_flags & RDMA_CORE_CAP_OPA_MAD)
+               == RDMA_CORE_CAP_OPA_MAD;
+}
+
+/**
+ * rdma_cap_ib_smi - Check if the port of a device provides an Infiniband
+ * Subnet Management Agent (SMA) on the Subnet Management Interface (SMI).
+ * @device: Device to check
+ * @port_num: Port number to check
+ *
+ * Each InfiniBand node is required to provide a Subnet Management Agent
+ * that the subnet manager can access.  Prior to the fabric being fully
+ * configured by the subnet manager, the SMA is accessed via a well known
+ * interface called the Subnet Management Interface (SMI).  This interface
+ * uses directed route packets to communicate with the SM to get around the
+ * chicken and egg problem of the SM needing to know what's on the fabric
+ * in order to configure the fabric, and needing to configure the fabric in
+ * order to send packets to the devices on the fabric.  These directed
+ * route packets do not need the fabric fully configured in order to reach
+ * their destination.  The SMI is the only method allowed to send
+ * directed route packets on an InfiniBand fabric.
+ *
+ * Return: true if the port provides an SMI.
+ */
+static inline bool rdma_cap_ib_smi(const struct ib_device *device, u8 port_num)
+{
+       return device->port_immutable[port_num].core_cap_flags & RDMA_CORE_CAP_IB_SMI;
+}
+
+/**
+ * rdma_cap_ib_cm - Check if the port of device has the capability Infiniband
+ * Communication Manager.
+ * @device: Device to check
+ * @port_num: Port number to check
+ *
+ * The InfiniBand Communication Manager is one of many pre-defined General
+ * Service Agents (GSA) that are accessed via the General Service
+ * Interface (GSI).  It's role is to facilitate establishment of connections
+ * between nodes as well as other management related tasks for established
+ * connections.
+ *
+ * Return: true if the port supports an IB CM (this does not guarantee that
+ * a CM is actually running however).
+ */
+static inline bool rdma_cap_ib_cm(const struct ib_device *device, u8 port_num)
+{
+       return device->port_immutable[port_num].core_cap_flags & RDMA_CORE_CAP_IB_CM;
+}
+
+/**
+ * rdma_cap_iw_cm - Check if the port of device has the capability IWARP
+ * Communication Manager.
+ * @device: Device to check
+ * @port_num: Port number to check
+ *
+ * Similar to above, but specific to iWARP connections which have a different
+ * managment protocol than InfiniBand.
+ *
+ * Return: true if the port supports an iWARP CM (this does not guarantee that
+ * a CM is actually running however).
+ */
+static inline bool rdma_cap_iw_cm(const struct ib_device *device, u8 port_num)
+{
+       return device->port_immutable[port_num].core_cap_flags & RDMA_CORE_CAP_IW_CM;
+}
+
+/**
+ * rdma_cap_ib_sa - Check if the port of device has the capability Infiniband
+ * Subnet Administration.
+ * @device: Device to check
+ * @port_num: Port number to check
+ *
+ * An InfiniBand Subnet Administration (SA) service is a pre-defined General
+ * Service Agent (GSA) provided by the Subnet Manager (SM).  On InfiniBand
+ * fabrics, devices should resolve routes to other hosts by contacting the
+ * SA to query the proper route.
+ *
+ * Return: true if the port should act as a client to the fabric Subnet
+ * Administration interface.  This does not imply that the SA service is
+ * running locally.
+ */
+static inline bool rdma_cap_ib_sa(const struct ib_device *device, u8 port_num)
+{
+       return device->port_immutable[port_num].core_cap_flags & RDMA_CORE_CAP_IB_SA;
+}
+
+/**
+ * rdma_cap_ib_mcast - Check if the port of device has the capability Infiniband
+ * Multicast.
+ * @device: Device to check
+ * @port_num: Port number to check
+ *
+ * InfiniBand multicast registration is more complex than normal IPv4 or
+ * IPv6 multicast registration.  Each Host Channel Adapter must register
+ * with the Subnet Manager when it wishes to join a multicast group.  It
+ * should do so only once regardless of how many queue pairs it subscribes
+ * to this group.  And it should leave the group only after all queue pairs
+ * attached to the group have been detached.
+ *
+ * Return: true if the port must undertake the additional adminstrative
+ * overhead of registering/unregistering with the SM and tracking of the
+ * total number of queue pairs attached to the multicast group.
+ */
+static inline bool rdma_cap_ib_mcast(const struct ib_device *device, u8 port_num)
+{
+       return rdma_cap_ib_sa(device, port_num);
+}
+
+/**
+ * rdma_cap_af_ib - Check if the port of device has the capability
+ * Native Infiniband Address.
+ * @device: Device to check
+ * @port_num: Port number to check
+ *
+ * InfiniBand addressing uses a port's GUID + Subnet Prefix to make a default
+ * GID.  RoCE uses a different mechanism, but still generates a GID via
+ * a prescribed mechanism and port specific data.
+ *
+ * Return: true if the port uses a GID address to identify devices on the
+ * network.
+ */
+static inline bool rdma_cap_af_ib(const struct ib_device *device, u8 port_num)
+{
+       return device->port_immutable[port_num].core_cap_flags & RDMA_CORE_CAP_AF_IB;
+}
+
+/**
+ * rdma_cap_eth_ah - Check if the port of device has the capability
+ * Ethernet Address Handle.
+ * @device: Device to check
+ * @port_num: Port number to check
+ *
+ * RoCE is InfiniBand over Ethernet, and it uses a well defined technique
+ * to fabricate GIDs over Ethernet/IP specific addresses native to the
+ * port.  Normally, packet headers are generated by the sending host
+ * adapter, but when sending connectionless datagrams, we must manually
+ * inject the proper headers for the fabric we are communicating over.
+ *
+ * Return: true if we are running as a RoCE port and must force the
+ * addition of a Global Route Header built from our Ethernet Address
+ * Handle into our header list for connectionless packets.
+ */
+static inline bool rdma_cap_eth_ah(const struct ib_device *device, u8 port_num)
+{
+       return device->port_immutable[port_num].core_cap_flags & RDMA_CORE_CAP_ETH_AH;
+}
+
+/**
+ * rdma_cap_read_multi_sge - Check if the port of device has the capability
+ * RDMA Read Multiple Scatter-Gather Entries.
+ * @device: Device to check
+ * @port_num: Port number to check
+ *
+ * iWARP has a restriction that RDMA READ requests may only have a single
+ * Scatter/Gather Entry (SGE) in the work request.
+ *
+ * NOTE: although the linux kernel currently assumes all devices are either
+ * single SGE RDMA READ devices or identical SGE maximums for RDMA READs and
+ * WRITEs, according to Tom Talpey, this is not accurate.  There are some
+ * devices out there that support more than a single SGE on RDMA READ
+ * requests, but do not support the same number of SGEs as they do on
+ * RDMA WRITE requests.  The linux kernel would need rearchitecting to
+ * support these imbalanced READ/WRITE SGEs allowed devices.  So, for now,
+ * suffice with either the device supports the same READ/WRITE SGEs, or
+ * it only gets one READ sge.
+ *
+ * Return: true for any device that allows more than one SGE in RDMA READ
+ * requests.
+ */
+static inline bool rdma_cap_read_multi_sge(struct ib_device *device,
+                                          u8 port_num)
+{
+       return !(device->port_immutable[port_num].core_cap_flags & RDMA_CORE_CAP_PROT_IWARP);
+}
+
+/**
+ * rdma_max_mad_size - Return the max MAD size required by this RDMA Port.
+ *
+ * @device: Device
+ * @port_num: Port number
+ *
+ * This MAD size includes the MAD headers and MAD payload.  No other headers
+ * are included.
+ *
+ * Return the max MAD size required by the Port.  Will return 0 if the port
+ * does not support MADs
+ */
+static inline size_t rdma_max_mad_size(const struct ib_device *device, u8 port_num)
+{
+       return device->port_immutable[port_num].max_mad_size;
+}
+
 int ib_query_gid(struct ib_device *device,
                 u8 port_num, int index, union ib_gid *gid);
 
@@ -1799,8 +2157,9 @@ struct ib_ah *ib_create_ah(struct ib_pd *pd, struct ib_ah_attr *ah_attr);
  * @ah_attr: Returned attributes that can be used when creating an address
  *   handle for replying to the message.
  */
-int ib_init_ah_from_wc(struct ib_device *device, u8 port_num, struct ib_wc *wc,
-                      struct ib_grh *grh, struct ib_ah_attr *ah_attr);
+int ib_init_ah_from_wc(struct ib_device *device, u8 port_num,
+                      const struct ib_wc *wc, const struct ib_grh *grh,
+                      struct ib_ah_attr *ah_attr);
 
 /**
  * ib_create_ah_from_wc - Creates an address handle associated with the
@@ -1814,8 +2173,8 @@ int ib_init_ah_from_wc(struct ib_device *device, u8 port_num, struct ib_wc *wc,
  * The address handle is used to reference a local or global destination
  * in all UD QP post sends.
  */
-struct ib_ah *ib_create_ah_from_wc(struct ib_pd *pd, struct ib_wc *wc,
-                                  struct ib_grh *grh, u8 port_num);
+struct ib_ah *ib_create_ah_from_wc(struct ib_pd *pd, const struct ib_wc *wc,
+                                  const struct ib_grh *grh, u8 port_num);
 
 /**
  * ib_modify_ah - Modifies the address vector associated with an address
@@ -2011,16 +2370,15 @@ static inline int ib_post_recv(struct ib_qp *qp,
  *   asynchronous event not associated with a completion occurs on the CQ.
  * @cq_context: Context associated with the CQ returned to the user via
  *   the associated completion and event handlers.
- * @cqe: The minimum size of the CQ.
- * @comp_vector - Completion vector used to signal completion events.
- *     Must be >= 0 and < context->num_comp_vectors.
+ * @cq_attr: The attributes the CQ should be created upon.
  *
  * Users can examine the cq structure to determine the actual CQ size.
  */
 struct ib_cq *ib_create_cq(struct ib_device *device,
                           ib_comp_handler comp_handler,
                           void (*event_handler)(struct ib_event *, void *),
-                          void *cq_context, int cqe, int comp_vector);
+                          void *cq_context,
+                          const struct ib_cq_init_attr *cq_attr);
 
 /**
  * ib_resize_cq - Modifies the capacity of the CQ.
index 1017e0bdf8baa75beb5ce0a13f852ddd7c683c4d..036bd277266254dba1ff5807a760e17da013667d 100644 (file)
@@ -91,6 +91,7 @@ struct iw_cm_id {
        /* Used by provider to add and remove refs on IW cm_id */
        void (*add_ref)(struct iw_cm_id *);
        void (*rem_ref)(struct iw_cm_id *);
+       u8  tos;
 };
 
 struct iw_cm_conn_param {
diff --git a/include/rdma/opa_smi.h b/include/rdma/opa_smi.h
new file mode 100644 (file)
index 0000000..29063e8
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2014 Intel Corporation.  All rights reserved.
+ *
+ * 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.
+ */
+
+#if !defined(OPA_SMI_H)
+#define OPA_SMI_H
+
+#include <rdma/ib_mad.h>
+#include <rdma/ib_smi.h>
+
+#define OPA_SMP_LID_DATA_SIZE                  2016
+#define OPA_SMP_DR_DATA_SIZE                   1872
+#define OPA_SMP_MAX_PATH_HOPS                  64
+
+#define OPA_SMI_CLASS_VERSION                  0x80
+
+#define OPA_LID_PERMISSIVE                     cpu_to_be32(0xFFFFFFFF)
+
+struct opa_smp {
+       u8      base_version;
+       u8      mgmt_class;
+       u8      class_version;
+       u8      method;
+       __be16  status;
+       u8      hop_ptr;
+       u8      hop_cnt;
+       __be64  tid;
+       __be16  attr_id;
+       __be16  resv;
+       __be32  attr_mod;
+       __be64  mkey;
+       union {
+               struct {
+                       uint8_t data[OPA_SMP_LID_DATA_SIZE];
+               } lid;
+               struct {
+                       __be32  dr_slid;
+                       __be32  dr_dlid;
+                       u8      initial_path[OPA_SMP_MAX_PATH_HOPS];
+                       u8      return_path[OPA_SMP_MAX_PATH_HOPS];
+                       u8      reserved[8];
+                       u8      data[OPA_SMP_DR_DATA_SIZE];
+               } dr;
+       } route;
+} __packed;
+
+
+static inline u8
+opa_get_smp_direction(struct opa_smp *smp)
+{
+       return ib_get_smp_direction((struct ib_smp *)smp);
+}
+
+static inline u8 *opa_get_smp_data(struct opa_smp *smp)
+{
+       if (smp->mgmt_class == IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE)
+               return smp->route.dr.data;
+
+       return smp->route.lid.data;
+}
+
+static inline size_t opa_get_smp_data_size(struct opa_smp *smp)
+{
+       if (smp->mgmt_class == IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE)
+               return sizeof(smp->route.dr.data);
+
+       return sizeof(smp->route.lid.data);
+}
+
+static inline size_t opa_get_smp_header_size(struct opa_smp *smp)
+{
+       if (smp->mgmt_class == IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE)
+               return sizeof(*smp) - sizeof(smp->route.dr.data);
+
+       return sizeof(*smp) - sizeof(smp->route.lid.data);
+}
+
+#endif /* OPA_SMI_H */
index 1ed2088dc9f5f029532f0b09765cf01a768624ba..c92522c192d26df9401d061121fb743935ef9b45 100644 (file)
@@ -62,6 +62,8 @@ enum rdma_cm_event_type {
        RDMA_CM_EVENT_TIMEWAIT_EXIT
 };
 
+__attribute_const__ const char *rdma_event_msg(enum rdma_cm_event_type event);
+
 enum rdma_port_space {
        RDMA_PS_SDP   = 0x0001,
        RDMA_PS_IPOIB = 0x0002,
index d0a66aa1868d2841f80a52843fb719e6323afe2f..e0a3398b15476e73f98f1dc4c030b7bfbaedd9a3 100644 (file)
@@ -1,9 +1,6 @@
 /*
  * This header file contains public constants and structures used by
- * the scsi code for linux.
- *
- * For documentation on the OPCODES, MESSAGES, and SENSE values,
- * please consult the SCSI standard.
+ * the SCSI initiator code.
  */
 #ifndef _SCSI_SCSI_H
 #define _SCSI_SCSI_H
@@ -11,6 +8,8 @@
 #include <linux/types.h>
 #include <linux/scatterlist.h>
 #include <linux/kernel.h>
+#include <scsi/scsi_common.h>
+#include <scsi/scsi_proto.h>
 
 struct scsi_cmnd;
 
@@ -49,187 +48,6 @@ enum scsi_timeouts {
  */
 #define SCAN_WILD_CARD ~0
 
-/*
- *      SCSI opcodes
- */
-
-#define TEST_UNIT_READY       0x00
-#define REZERO_UNIT           0x01
-#define REQUEST_SENSE         0x03
-#define FORMAT_UNIT           0x04
-#define READ_BLOCK_LIMITS     0x05
-#define REASSIGN_BLOCKS       0x07
-#define INITIALIZE_ELEMENT_STATUS 0x07
-#define READ_6                0x08
-#define WRITE_6               0x0a
-#define SEEK_6                0x0b
-#define READ_REVERSE          0x0f
-#define WRITE_FILEMARKS       0x10
-#define SPACE                 0x11
-#define INQUIRY               0x12
-#define RECOVER_BUFFERED_DATA 0x14
-#define MODE_SELECT           0x15
-#define RESERVE               0x16
-#define RELEASE               0x17
-#define COPY                  0x18
-#define ERASE                 0x19
-#define MODE_SENSE            0x1a
-#define START_STOP            0x1b
-#define RECEIVE_DIAGNOSTIC    0x1c
-#define SEND_DIAGNOSTIC       0x1d
-#define ALLOW_MEDIUM_REMOVAL  0x1e
-
-#define READ_FORMAT_CAPACITIES 0x23
-#define SET_WINDOW            0x24
-#define READ_CAPACITY         0x25
-#define READ_10               0x28
-#define WRITE_10              0x2a
-#define SEEK_10               0x2b
-#define POSITION_TO_ELEMENT   0x2b
-#define WRITE_VERIFY          0x2e
-#define VERIFY                0x2f
-#define SEARCH_HIGH           0x30
-#define SEARCH_EQUAL          0x31
-#define SEARCH_LOW            0x32
-#define SET_LIMITS            0x33
-#define PRE_FETCH             0x34
-#define READ_POSITION         0x34
-#define SYNCHRONIZE_CACHE     0x35
-#define LOCK_UNLOCK_CACHE     0x36
-#define READ_DEFECT_DATA      0x37
-#define MEDIUM_SCAN           0x38
-#define COMPARE               0x39
-#define COPY_VERIFY           0x3a
-#define WRITE_BUFFER          0x3b
-#define READ_BUFFER           0x3c
-#define UPDATE_BLOCK          0x3d
-#define READ_LONG             0x3e
-#define WRITE_LONG            0x3f
-#define CHANGE_DEFINITION     0x40
-#define WRITE_SAME            0x41
-#define UNMAP                0x42
-#define READ_TOC              0x43
-#define READ_HEADER           0x44
-#define GET_EVENT_STATUS_NOTIFICATION 0x4a
-#define LOG_SELECT            0x4c
-#define LOG_SENSE             0x4d
-#define XDWRITEREAD_10        0x53
-#define MODE_SELECT_10        0x55
-#define RESERVE_10            0x56
-#define RELEASE_10            0x57
-#define MODE_SENSE_10         0x5a
-#define PERSISTENT_RESERVE_IN 0x5e
-#define PERSISTENT_RESERVE_OUT 0x5f
-#define VARIABLE_LENGTH_CMD   0x7f
-#define REPORT_LUNS           0xa0
-#define SECURITY_PROTOCOL_IN  0xa2
-#define MAINTENANCE_IN        0xa3
-#define MAINTENANCE_OUT       0xa4
-#define MOVE_MEDIUM           0xa5
-#define EXCHANGE_MEDIUM       0xa6
-#define READ_12               0xa8
-#define SERVICE_ACTION_OUT_12 0xa9
-#define WRITE_12              0xaa
-#define READ_MEDIA_SERIAL_NUMBER 0xab /* Obsolete with SPC-2 */
-#define SERVICE_ACTION_IN_12  0xab
-#define WRITE_VERIFY_12       0xae
-#define VERIFY_12            0xaf
-#define SEARCH_HIGH_12        0xb0
-#define SEARCH_EQUAL_12       0xb1
-#define SEARCH_LOW_12         0xb2
-#define SECURITY_PROTOCOL_OUT 0xb5
-#define READ_ELEMENT_STATUS   0xb8
-#define SEND_VOLUME_TAG       0xb6
-#define WRITE_LONG_2          0xea
-#define EXTENDED_COPY         0x83
-#define RECEIVE_COPY_RESULTS  0x84
-#define ACCESS_CONTROL_IN     0x86
-#define ACCESS_CONTROL_OUT    0x87
-#define READ_16               0x88
-#define COMPARE_AND_WRITE     0x89
-#define WRITE_16              0x8a
-#define READ_ATTRIBUTE        0x8c
-#define WRITE_ATTRIBUTE              0x8d
-#define VERIFY_16            0x8f
-#define SYNCHRONIZE_CACHE_16  0x91
-#define WRITE_SAME_16        0x93
-#define SERVICE_ACTION_BIDIRECTIONAL 0x9d
-#define SERVICE_ACTION_IN_16  0x9e
-#define SERVICE_ACTION_OUT_16 0x9f
-/* values for service action in */
-#define        SAI_READ_CAPACITY_16  0x10
-#define SAI_GET_LBA_STATUS    0x12
-#define SAI_REPORT_REFERRALS  0x13
-/* values for VARIABLE_LENGTH_CMD service action codes
- * see spc4r17 Section D.3.5, table D.7 and D.8 */
-#define VLC_SA_RECEIVE_CREDENTIAL 0x1800
-/* values for maintenance in */
-#define MI_REPORT_IDENTIFYING_INFORMATION 0x05
-#define MI_REPORT_TARGET_PGS  0x0a
-#define MI_REPORT_ALIASES     0x0b
-#define MI_REPORT_SUPPORTED_OPERATION_CODES 0x0c
-#define MI_REPORT_SUPPORTED_TASK_MANAGEMENT_FUNCTIONS 0x0d
-#define MI_REPORT_PRIORITY    0x0e
-#define MI_REPORT_TIMESTAMP   0x0f
-#define MI_MANAGEMENT_PROTOCOL_IN 0x10
-/* value for MI_REPORT_TARGET_PGS ext header */
-#define MI_EXT_HDR_PARAM_FMT  0x20
-/* values for maintenance out */
-#define MO_SET_IDENTIFYING_INFORMATION 0x06
-#define MO_SET_TARGET_PGS     0x0a
-#define MO_CHANGE_ALIASES     0x0b
-#define MO_SET_PRIORITY       0x0e
-#define MO_SET_TIMESTAMP      0x0f
-#define MO_MANAGEMENT_PROTOCOL_OUT 0x10
-/* values for variable length command */
-#define XDREAD_32            0x03
-#define XDWRITE_32           0x04
-#define XPWRITE_32           0x06
-#define XDWRITEREAD_32       0x07
-#define READ_32                      0x09
-#define VERIFY_32            0x0a
-#define WRITE_32             0x0b
-#define WRITE_SAME_32        0x0d
-
-/* Values for T10/04-262r7 */
-#define        ATA_16                0x85      /* 16-byte pass-thru */
-#define        ATA_12                0xa1      /* 12-byte pass-thru */
-
-/* Vendor specific CDBs start here */
-#define VENDOR_SPECIFIC_CDB 0xc0
-
-/*
- *     SCSI command lengths
- */
-
-#define SCSI_MAX_VARLEN_CDB_SIZE 260
-
-/* defined in T10 SCSI Primary Commands-2 (SPC2) */
-struct scsi_varlen_cdb_hdr {
-       __u8 opcode;        /* opcode always == VARIABLE_LENGTH_CMD */
-       __u8 control;
-       __u8 misc[5];
-       __u8 additional_cdb_length;         /* total cdb length - 8 */
-       __be16 service_action;
-       /* service specific data follows */
-};
-
-static inline unsigned
-scsi_varlen_cdb_length(const void *hdr)
-{
-       return ((struct scsi_varlen_cdb_hdr *)hdr)->additional_cdb_length + 8;
-}
-
-extern const unsigned char scsi_command_size_tbl[8];
-#define COMMAND_SIZE(opcode) scsi_command_size_tbl[((opcode) >> 5) & 7]
-
-static inline unsigned
-scsi_command_size(const unsigned char *cmnd)
-{
-       return (cmnd[0] == VARIABLE_LENGTH_CMD) ?
-               scsi_varlen_cdb_length(cmnd) : COMMAND_SIZE(cmnd[0]);
-}
-
 #ifdef CONFIG_ACPI
 struct acpi_bus_type;
 
@@ -240,22 +58,6 @@ extern void
 scsi_unregister_acpi_bus_type(struct acpi_bus_type *bus);
 #endif
 
-/*
- *  SCSI Architecture Model (SAM) Status codes. Taken from SAM-3 draft
- *  T10/1561-D Revision 4 Draft dated 7th November 2002.
- */
-#define SAM_STAT_GOOD            0x00
-#define SAM_STAT_CHECK_CONDITION 0x02
-#define SAM_STAT_CONDITION_MET   0x04
-#define SAM_STAT_BUSY            0x08
-#define SAM_STAT_INTERMEDIATE    0x10
-#define SAM_STAT_INTERMEDIATE_CONDITION_MET 0x14
-#define SAM_STAT_RESERVATION_CONFLICT 0x18
-#define SAM_STAT_COMMAND_TERMINATED 0x22       /* obsolete in SAM-3 */
-#define SAM_STAT_TASK_SET_FULL   0x28
-#define SAM_STAT_ACA_ACTIVE      0x30
-#define SAM_STAT_TASK_ABORTED    0x40
-
 /** scsi_status_is_good - check the status return.
  *
  * @status: the status passed up from the driver (including host and
@@ -279,86 +81,6 @@ static inline int scsi_status_is_good(int status)
                (status == SAM_STAT_COMMAND_TERMINATED));
 }
 
-/*
- *  Status codes. These are deprecated as they are shifted 1 bit right
- *  from those found in the SCSI standards. This causes confusion for
- *  applications that are ported to several OSes. Prefer SAM Status codes
- *  above.
- */
-
-#define GOOD                 0x00
-#define CHECK_CONDITION      0x01
-#define CONDITION_GOOD       0x02
-#define BUSY                 0x04
-#define INTERMEDIATE_GOOD    0x08
-#define INTERMEDIATE_C_GOOD  0x0a
-#define RESERVATION_CONFLICT 0x0c
-#define COMMAND_TERMINATED   0x11
-#define QUEUE_FULL           0x14
-#define ACA_ACTIVE           0x18
-#define TASK_ABORTED         0x20
-
-#define STATUS_MASK          0xfe
-
-/*
- *  SENSE KEYS
- */
-
-#define NO_SENSE            0x00
-#define RECOVERED_ERROR     0x01
-#define NOT_READY           0x02
-#define MEDIUM_ERROR        0x03
-#define HARDWARE_ERROR      0x04
-#define ILLEGAL_REQUEST     0x05
-#define UNIT_ATTENTION      0x06
-#define DATA_PROTECT        0x07
-#define BLANK_CHECK         0x08
-#define COPY_ABORTED        0x0a
-#define ABORTED_COMMAND     0x0b
-#define VOLUME_OVERFLOW     0x0d
-#define MISCOMPARE          0x0e
-
-
-/*
- *  DEVICE TYPES
- *  Please keep them in 0x%02x format for $MODALIAS to work
- */
-
-#define TYPE_DISK           0x00
-#define TYPE_TAPE           0x01
-#define TYPE_PRINTER        0x02
-#define TYPE_PROCESSOR      0x03    /* HP scanners use this */
-#define TYPE_WORM           0x04    /* Treated as ROM by our system */
-#define TYPE_ROM            0x05
-#define TYPE_SCANNER        0x06
-#define TYPE_MOD            0x07    /* Magneto-optical disk - 
-                                    * - treated as TYPE_DISK */
-#define TYPE_MEDIUM_CHANGER 0x08
-#define TYPE_COMM           0x09    /* Communications device */
-#define TYPE_RAID           0x0c
-#define TYPE_ENCLOSURE      0x0d    /* Enclosure Services Device */
-#define TYPE_RBC           0x0e
-#define TYPE_OSD            0x11
-#define TYPE_ZBC            0x14
-#define TYPE_WLUN           0x1e    /* well-known logical unit */
-#define TYPE_NO_LUN         0x7f
-
-/* SCSI protocols; these are taken from SPC-3 section 7.5 */
-enum scsi_protocol {
-       SCSI_PROTOCOL_FCP = 0,  /* Fibre Channel */
-       SCSI_PROTOCOL_SPI = 1,  /* parallel SCSI */
-       SCSI_PROTOCOL_SSA = 2,  /* Serial Storage Architecture - Obsolete */
-       SCSI_PROTOCOL_SBP = 3,  /* firewire */
-       SCSI_PROTOCOL_SRP = 4,  /* Infiniband RDMA */
-       SCSI_PROTOCOL_ISCSI = 5,
-       SCSI_PROTOCOL_SAS = 6,
-       SCSI_PROTOCOL_ADT = 7,  /* Media Changers */
-       SCSI_PROTOCOL_ATA = 8,
-       SCSI_PROTOCOL_UNSPEC = 0xf, /* No specific protocol */
-};
-
-/* Returns a human-readable name for the device */
-extern const char * scsi_device_type(unsigned type);
 
 /*
  * standard mode-select header prepended to all mode-select commands
@@ -379,13 +101,6 @@ struct ccs_modesel_head {
        __u8 block_length_lo;
 };
 
-/*
- * ScsiLun: 8 byte LUN.
- */
-struct scsi_lun {
-       __u8 scsi_lun[8];
-};
-
 /*
  * The Well Known LUNS (SAM-3) in our int representation of a LUN
  */
diff --git a/include/scsi/scsi_common.h b/include/scsi/scsi_common.h
new file mode 100644 (file)
index 0000000..676b03b
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Functions used by both the SCSI initiator code and the SCSI target code.
+ */
+
+#ifndef _SCSI_COMMON_H_
+#define _SCSI_COMMON_H_
+
+#include <linux/types.h>
+#include <scsi/scsi_proto.h>
+
+static inline unsigned
+scsi_varlen_cdb_length(const void *hdr)
+{
+       return ((struct scsi_varlen_cdb_hdr *)hdr)->additional_cdb_length + 8;
+}
+
+extern const unsigned char scsi_command_size_tbl[8];
+#define COMMAND_SIZE(opcode) scsi_command_size_tbl[((opcode) >> 5) & 7]
+
+static inline unsigned
+scsi_command_size(const unsigned char *cmnd)
+{
+       return (cmnd[0] == VARIABLE_LENGTH_CMD) ?
+               scsi_varlen_cdb_length(cmnd) : COMMAND_SIZE(cmnd[0]);
+}
+
+/* Returns a human-readable name for the device */
+extern const char *scsi_device_type(unsigned type);
+
+extern void int_to_scsilun(u64, struct scsi_lun *);
+extern u64 scsilun_to_int(struct scsi_lun *);
+
+/*
+ * This is a slightly modified SCSI sense "descriptor" format header.
+ * The addition is to allow the 0x70 and 0x71 response codes. The idea
+ * is to place the salient data from either "fixed" or "descriptor" sense
+ * format into one structure to ease application processing.
+ *
+ * The original sense buffer should be kept around for those cases
+ * in which more information is required (e.g. the LBA of a MEDIUM ERROR).
+ */
+struct scsi_sense_hdr {                /* See SPC-3 section 4.5 */
+       u8 response_code;       /* permit: 0x0, 0x70, 0x71, 0x72, 0x73 */
+       u8 sense_key;
+       u8 asc;
+       u8 ascq;
+       u8 byte4;
+       u8 byte5;
+       u8 byte6;
+       u8 additional_length;   /* always 0 for fixed sense format */
+};
+
+static inline bool scsi_sense_valid(const struct scsi_sense_hdr *sshdr)
+{
+       if (!sshdr)
+               return false;
+
+       return (sshdr->response_code & 0x70) == 0x70;
+}
+
+extern bool scsi_normalize_sense(const u8 *sense_buffer, int sb_len,
+                                struct scsi_sense_hdr *sshdr);
+
+#endif /* _SCSI_COMMON_H_ */
index a4c9336811d17b14fe7c06e2171de329cf077b7b..ae84b2214d407f16d2a7f9ac6b73c350973c4f37 100644 (file)
@@ -413,8 +413,6 @@ extern void scsi_target_reap(struct scsi_target *);
 extern void scsi_target_block(struct device *);
 extern void scsi_target_unblock(struct device *, enum scsi_device_state);
 extern void scsi_remove_target(struct device *);
-extern void int_to_scsilun(u64, struct scsi_lun *);
-extern u64 scsilun_to_int(struct scsi_lun *);
 extern const char *scsi_device_state_name(enum scsi_device_state);
 extern int scsi_is_sdev_device(const struct device *);
 extern int scsi_is_target_device(const struct device *);
index 5a4bb5bb66b3b9e5a7411082178665e4198a2972..4942710ef720ea5716e8cc6ebf0df941e22500ba 100644 (file)
@@ -7,43 +7,12 @@
 struct scsi_device;
 struct Scsi_Host;
 
-/*
- * This is a slightly modified SCSI sense "descriptor" format header.
- * The addition is to allow the 0x70 and 0x71 response codes. The idea
- * is to place the salient data from either "fixed" or "descriptor" sense
- * format into one structure to ease application processing.
- *
- * The original sense buffer should be kept around for those cases
- * in which more information is required (e.g. the LBA of a MEDIUM ERROR).
- */
-struct scsi_sense_hdr {                /* See SPC-3 section 4.5 */
-       u8 response_code;       /* permit: 0x0, 0x70, 0x71, 0x72, 0x73 */
-       u8 sense_key;
-       u8 asc;
-       u8 ascq;
-       u8 byte4;
-       u8 byte5;
-       u8 byte6;
-       u8 additional_length;   /* always 0 for fixed sense format */
-};
-
-static inline bool scsi_sense_valid(const struct scsi_sense_hdr *sshdr)
-{
-       if (!sshdr)
-               return false;
-
-       return (sshdr->response_code & 0x70) == 0x70;
-}
-
-
 extern void scsi_eh_finish_cmd(struct scsi_cmnd *scmd,
                               struct list_head *done_q);
 extern void scsi_eh_flush_done_q(struct list_head *done_q);
 extern void scsi_report_bus_reset(struct Scsi_Host *, int);
 extern void scsi_report_device_reset(struct Scsi_Host *, int, int);
 extern int scsi_block_when_processing_errors(struct scsi_device *);
-extern bool scsi_normalize_sense(const u8 *sense_buffer, int sb_len,
-                                struct scsi_sense_hdr *sshdr);
 extern bool scsi_command_normalize_sense(const struct scsi_cmnd *cmd,
                                         struct scsi_sense_hdr *sshdr);
 
diff --git a/include/scsi/scsi_proto.h b/include/scsi/scsi_proto.h
new file mode 100644 (file)
index 0000000..a9fbf1b
--- /dev/null
@@ -0,0 +1,281 @@
+/*
+ * This header file contains public constants and structures used by
+ * both the SCSI initiator and the SCSI target code.
+ *
+ * For documentation on the OPCODES, MESSAGES, and SENSE values,
+ * please consult the SCSI standard.
+ */
+
+#ifndef _SCSI_PROTO_H_
+#define _SCSI_PROTO_H_
+
+#include <linux/types.h>
+
+/*
+ *      SCSI opcodes
+ */
+
+#define TEST_UNIT_READY       0x00
+#define REZERO_UNIT           0x01
+#define REQUEST_SENSE         0x03
+#define FORMAT_UNIT           0x04
+#define READ_BLOCK_LIMITS     0x05
+#define REASSIGN_BLOCKS       0x07
+#define INITIALIZE_ELEMENT_STATUS 0x07
+#define READ_6                0x08
+#define WRITE_6               0x0a
+#define SEEK_6                0x0b
+#define READ_REVERSE          0x0f
+#define WRITE_FILEMARKS       0x10
+#define SPACE                 0x11
+#define INQUIRY               0x12
+#define RECOVER_BUFFERED_DATA 0x14
+#define MODE_SELECT           0x15
+#define RESERVE               0x16
+#define RELEASE               0x17
+#define COPY                  0x18
+#define ERASE                 0x19
+#define MODE_SENSE            0x1a
+#define START_STOP            0x1b
+#define RECEIVE_DIAGNOSTIC    0x1c
+#define SEND_DIAGNOSTIC       0x1d
+#define ALLOW_MEDIUM_REMOVAL  0x1e
+
+#define READ_FORMAT_CAPACITIES 0x23
+#define SET_WINDOW            0x24
+#define READ_CAPACITY         0x25
+#define READ_10               0x28
+#define WRITE_10              0x2a
+#define SEEK_10               0x2b
+#define POSITION_TO_ELEMENT   0x2b
+#define WRITE_VERIFY          0x2e
+#define VERIFY                0x2f
+#define SEARCH_HIGH           0x30
+#define SEARCH_EQUAL          0x31
+#define SEARCH_LOW            0x32
+#define SET_LIMITS            0x33
+#define PRE_FETCH             0x34
+#define READ_POSITION         0x34
+#define SYNCHRONIZE_CACHE     0x35
+#define LOCK_UNLOCK_CACHE     0x36
+#define READ_DEFECT_DATA      0x37
+#define MEDIUM_SCAN           0x38
+#define COMPARE               0x39
+#define COPY_VERIFY           0x3a
+#define WRITE_BUFFER          0x3b
+#define READ_BUFFER           0x3c
+#define UPDATE_BLOCK          0x3d
+#define READ_LONG             0x3e
+#define WRITE_LONG            0x3f
+#define CHANGE_DEFINITION     0x40
+#define WRITE_SAME            0x41
+#define UNMAP                0x42
+#define READ_TOC              0x43
+#define READ_HEADER           0x44
+#define GET_EVENT_STATUS_NOTIFICATION 0x4a
+#define LOG_SELECT            0x4c
+#define LOG_SENSE             0x4d
+#define XDWRITEREAD_10        0x53
+#define MODE_SELECT_10        0x55
+#define RESERVE_10            0x56
+#define RELEASE_10            0x57
+#define MODE_SENSE_10         0x5a
+#define PERSISTENT_RESERVE_IN 0x5e
+#define PERSISTENT_RESERVE_OUT 0x5f
+#define VARIABLE_LENGTH_CMD   0x7f
+#define REPORT_LUNS           0xa0
+#define SECURITY_PROTOCOL_IN  0xa2
+#define MAINTENANCE_IN        0xa3
+#define MAINTENANCE_OUT       0xa4
+#define MOVE_MEDIUM           0xa5
+#define EXCHANGE_MEDIUM       0xa6
+#define READ_12               0xa8
+#define SERVICE_ACTION_OUT_12 0xa9
+#define WRITE_12              0xaa
+#define READ_MEDIA_SERIAL_NUMBER 0xab /* Obsolete with SPC-2 */
+#define SERVICE_ACTION_IN_12  0xab
+#define WRITE_VERIFY_12       0xae
+#define VERIFY_12            0xaf
+#define SEARCH_HIGH_12        0xb0
+#define SEARCH_EQUAL_12       0xb1
+#define SEARCH_LOW_12         0xb2
+#define SECURITY_PROTOCOL_OUT 0xb5
+#define READ_ELEMENT_STATUS   0xb8
+#define SEND_VOLUME_TAG       0xb6
+#define WRITE_LONG_2          0xea
+#define EXTENDED_COPY         0x83
+#define RECEIVE_COPY_RESULTS  0x84
+#define ACCESS_CONTROL_IN     0x86
+#define ACCESS_CONTROL_OUT    0x87
+#define READ_16               0x88
+#define COMPARE_AND_WRITE     0x89
+#define WRITE_16              0x8a
+#define READ_ATTRIBUTE        0x8c
+#define WRITE_ATTRIBUTE              0x8d
+#define VERIFY_16            0x8f
+#define SYNCHRONIZE_CACHE_16  0x91
+#define WRITE_SAME_16        0x93
+#define SERVICE_ACTION_BIDIRECTIONAL 0x9d
+#define SERVICE_ACTION_IN_16  0x9e
+#define SERVICE_ACTION_OUT_16 0x9f
+/* values for service action in */
+#define        SAI_READ_CAPACITY_16  0x10
+#define SAI_GET_LBA_STATUS    0x12
+#define SAI_REPORT_REFERRALS  0x13
+/* values for VARIABLE_LENGTH_CMD service action codes
+ * see spc4r17 Section D.3.5, table D.7 and D.8 */
+#define VLC_SA_RECEIVE_CREDENTIAL 0x1800
+/* values for maintenance in */
+#define MI_REPORT_IDENTIFYING_INFORMATION 0x05
+#define MI_REPORT_TARGET_PGS  0x0a
+#define MI_REPORT_ALIASES     0x0b
+#define MI_REPORT_SUPPORTED_OPERATION_CODES 0x0c
+#define MI_REPORT_SUPPORTED_TASK_MANAGEMENT_FUNCTIONS 0x0d
+#define MI_REPORT_PRIORITY    0x0e
+#define MI_REPORT_TIMESTAMP   0x0f
+#define MI_MANAGEMENT_PROTOCOL_IN 0x10
+/* value for MI_REPORT_TARGET_PGS ext header */
+#define MI_EXT_HDR_PARAM_FMT  0x20
+/* values for maintenance out */
+#define MO_SET_IDENTIFYING_INFORMATION 0x06
+#define MO_SET_TARGET_PGS     0x0a
+#define MO_CHANGE_ALIASES     0x0b
+#define MO_SET_PRIORITY       0x0e
+#define MO_SET_TIMESTAMP      0x0f
+#define MO_MANAGEMENT_PROTOCOL_OUT 0x10
+/* values for variable length command */
+#define XDREAD_32            0x03
+#define XDWRITE_32           0x04
+#define XPWRITE_32           0x06
+#define XDWRITEREAD_32       0x07
+#define READ_32                      0x09
+#define VERIFY_32            0x0a
+#define WRITE_32             0x0b
+#define WRITE_SAME_32        0x0d
+
+/* Values for T10/04-262r7 */
+#define        ATA_16                0x85      /* 16-byte pass-thru */
+#define        ATA_12                0xa1      /* 12-byte pass-thru */
+
+/* Vendor specific CDBs start here */
+#define VENDOR_SPECIFIC_CDB 0xc0
+
+/*
+ *     SCSI command lengths
+ */
+
+#define SCSI_MAX_VARLEN_CDB_SIZE 260
+
+/* defined in T10 SCSI Primary Commands-2 (SPC2) */
+struct scsi_varlen_cdb_hdr {
+       __u8 opcode;        /* opcode always == VARIABLE_LENGTH_CMD */
+       __u8 control;
+       __u8 misc[5];
+       __u8 additional_cdb_length;         /* total cdb length - 8 */
+       __be16 service_action;
+       /* service specific data follows */
+};
+
+/*
+ *  SCSI Architecture Model (SAM) Status codes. Taken from SAM-3 draft
+ *  T10/1561-D Revision 4 Draft dated 7th November 2002.
+ */
+#define SAM_STAT_GOOD            0x00
+#define SAM_STAT_CHECK_CONDITION 0x02
+#define SAM_STAT_CONDITION_MET   0x04
+#define SAM_STAT_BUSY            0x08
+#define SAM_STAT_INTERMEDIATE    0x10
+#define SAM_STAT_INTERMEDIATE_CONDITION_MET 0x14
+#define SAM_STAT_RESERVATION_CONFLICT 0x18
+#define SAM_STAT_COMMAND_TERMINATED 0x22       /* obsolete in SAM-3 */
+#define SAM_STAT_TASK_SET_FULL   0x28
+#define SAM_STAT_ACA_ACTIVE      0x30
+#define SAM_STAT_TASK_ABORTED    0x40
+
+/*
+ *  Status codes. These are deprecated as they are shifted 1 bit right
+ *  from those found in the SCSI standards. This causes confusion for
+ *  applications that are ported to several OSes. Prefer SAM Status codes
+ *  above.
+ */
+
+#define GOOD                 0x00
+#define CHECK_CONDITION      0x01
+#define CONDITION_GOOD       0x02
+#define BUSY                 0x04
+#define INTERMEDIATE_GOOD    0x08
+#define INTERMEDIATE_C_GOOD  0x0a
+#define RESERVATION_CONFLICT 0x0c
+#define COMMAND_TERMINATED   0x11
+#define QUEUE_FULL           0x14
+#define ACA_ACTIVE           0x18
+#define TASK_ABORTED         0x20
+
+#define STATUS_MASK          0xfe
+
+/*
+ *  SENSE KEYS
+ */
+
+#define NO_SENSE            0x00
+#define RECOVERED_ERROR     0x01
+#define NOT_READY           0x02
+#define MEDIUM_ERROR        0x03
+#define HARDWARE_ERROR      0x04
+#define ILLEGAL_REQUEST     0x05
+#define UNIT_ATTENTION      0x06
+#define DATA_PROTECT        0x07
+#define BLANK_CHECK         0x08
+#define COPY_ABORTED        0x0a
+#define ABORTED_COMMAND     0x0b
+#define VOLUME_OVERFLOW     0x0d
+#define MISCOMPARE          0x0e
+
+
+/*
+ *  DEVICE TYPES
+ *  Please keep them in 0x%02x format for $MODALIAS to work
+ */
+
+#define TYPE_DISK           0x00
+#define TYPE_TAPE           0x01
+#define TYPE_PRINTER        0x02
+#define TYPE_PROCESSOR      0x03    /* HP scanners use this */
+#define TYPE_WORM           0x04    /* Treated as ROM by our system */
+#define TYPE_ROM            0x05
+#define TYPE_SCANNER        0x06
+#define TYPE_MOD            0x07    /* Magneto-optical disk -
+                                    * - treated as TYPE_DISK */
+#define TYPE_MEDIUM_CHANGER 0x08
+#define TYPE_COMM           0x09    /* Communications device */
+#define TYPE_RAID           0x0c
+#define TYPE_ENCLOSURE      0x0d    /* Enclosure Services Device */
+#define TYPE_RBC           0x0e
+#define TYPE_OSD            0x11
+#define TYPE_ZBC            0x14
+#define TYPE_WLUN           0x1e    /* well-known logical unit */
+#define TYPE_NO_LUN         0x7f
+
+/* SCSI protocols; these are taken from SPC-3 section 7.5 */
+enum scsi_protocol {
+       SCSI_PROTOCOL_FCP = 0,  /* Fibre Channel */
+       SCSI_PROTOCOL_SPI = 1,  /* parallel SCSI */
+       SCSI_PROTOCOL_SSA = 2,  /* Serial Storage Architecture - Obsolete */
+       SCSI_PROTOCOL_SBP = 3,  /* firewire */
+       SCSI_PROTOCOL_SRP = 4,  /* Infiniband RDMA */
+       SCSI_PROTOCOL_ISCSI = 5,
+       SCSI_PROTOCOL_SAS = 6,
+       SCSI_PROTOCOL_ADT = 7,  /* Media Changers */
+       SCSI_PROTOCOL_ATA = 8,
+       SCSI_PROTOCOL_UNSPEC = 0xf, /* No specific protocol */
+};
+
+/*
+ * ScsiLun: 8 byte LUN.
+ */
+struct scsi_lun {
+       __u8 scsi_lun[8];
+};
+
+
+#endif /* _SCSI_PROTO_H_ */
index 1ae84db4c9fb6525b4937276e42f220dbe41d5e7..5be834de491a8500eea7421711d0e560aff6aa43 100644 (file)
@@ -42,6 +42,7 @@
  */
 
 #include <linux/types.h>
+#include <scsi/scsi.h>
 
 enum {
        SRP_LOGIN_REQ   = 0x00,
@@ -179,7 +180,7 @@ struct srp_tsk_mgmt {
        u8      reserved1[6];
        u64     tag;
        u8      reserved2[4];
-       __be64  lun __attribute__((packed));
+       struct scsi_lun lun;
        u8      reserved3[2];
        u8      tsk_mgmt_func;
        u8      reserved4;
@@ -200,7 +201,7 @@ struct srp_cmd {
        u8      data_in_desc_cnt;
        u64     tag;
        u8      reserved2[4];
-       __be64  lun __attribute__((packed));
+       struct scsi_lun lun;
        u8      reserved3;
        u8      task_attr;
        u8      reserved4;
@@ -265,7 +266,7 @@ struct srp_aer_req {
        __be32  req_lim_delta;
        u64     tag;
        u32     reserved2;
-       __be64  lun;
+       struct scsi_lun lun;
        __be32  sense_data_len;
        u32     reserved3;
        u8      sense_data[0];
index 53a18b3635e24a458700a21566c56a10d0704c56..df705908480aebbf754900731834162fa8097f75 100644 (file)
@@ -9,6 +9,8 @@
 #include <sound/core.h>
 #include <sound/hdaudio.h>
 
+#define AC_AMP_FAKE_MUTE       0x10    /* fake mute bit set to amp verbs */
+
 int snd_hdac_regmap_init(struct hdac_device *codec);
 void snd_hdac_regmap_exit(struct hdac_device *codec);
 int snd_hdac_regmap_add_vendor_verb(struct hdac_device *codec,
index 54e7af301888f00550a05b872e4c8a5eaf92e51c..006983b296dd6da3afa466e43f40ec0699cad36f 100644 (file)
@@ -5,7 +5,6 @@
 #include <linux/configfs.h>
 #include <net/sock.h>
 #include <net/tcp.h>
-#include <scsi/scsi_cmnd.h>
 #include <scsi/iscsi_proto.h>
 #include <target/target_core_base.h>
 
index 480e9f82dfea861a70fa5e1df45076770c719f2e..aec6f6a4477c79454af758817537dd8d3a3300b8 100644 (file)
@@ -6,7 +6,6 @@
 #include <linux/dma-mapping.h>
 #include <linux/blkdev.h>
 #include <linux/percpu_ida.h>
-#include <scsi/scsi_cmnd.h>
 #include <net/sock.h>
 #include <net/tcp.h>
 
 /* Don't raise above 511 or REPORT_LUNS needs to handle >1 page */
 #define TRANSPORT_MAX_LUNS_PER_TPG             256
 /*
- * By default we use 32-byte CDBs in TCM Core and subsystem plugin code.
- *
- * Note that both include/scsi/scsi_cmnd.h:MAX_COMMAND_SIZE and
- * include/linux/blkdev.h:BLOCK_MAX_CDB as of v2.6.36-rc4 still use
- * 16-byte CDBs by default and require an extra allocation for
- * 32-byte CDBs to because of legacy issues.
- *
- * Within TCM Core there are no such legacy limitiations, so we go ahead
- * use 32-byte CDBs by default and use include/scsi/scsi.h:scsi_command_size()
- * within all TCM Core and subsystem plugin code.
+ * Maximum size of a CDB that can be stored in se_cmd without allocating
+ * memory dynamically for the CDB.
  */
 #define TCM_MAX_COMMAND_SIZE                   32
 /*
  * From include/scsi/scsi_cmnd.h:SCSI_SENSE_BUFFERSIZE, currently
  * defined 96, but the real limit is 252 (or 260 including the header)
  */
-#define TRANSPORT_SENSE_BUFFER                 SCSI_SENSE_BUFFERSIZE
+#define TRANSPORT_SENSE_BUFFER                 96
 /* Used by transport_send_check_condition_and_sense() */
 #define SPC_SENSE_KEY_OFFSET                   2
 #define SPC_ADD_SENSE_LEN_OFFSET               7
index d19840b0cac844c8cd2fca6f4dac5d4bc62c75a3..630d1e5e4de014ffebe1b043414384e398298c67 100644 (file)
@@ -42,45 +42,54 @@ TRACE_EVENT(pstate_sample,
 
        TP_PROTO(u32 core_busy,
                u32 scaled_busy,
-               u32 state,
+               u32 from,
+               u32 to,
                u64 mperf,
                u64 aperf,
+               u64 tsc,
                u32 freq
                ),
 
        TP_ARGS(core_busy,
                scaled_busy,
-               state,
+               from,
+               to,
                mperf,
                aperf,
+               tsc,
                freq
                ),
 
        TP_STRUCT__entry(
                __field(u32, core_busy)
                __field(u32, scaled_busy)
-               __field(u32, state)
+               __field(u32, from)
+               __field(u32, to)
                __field(u64, mperf)
                __field(u64, aperf)
+               __field(u64, tsc)
                __field(u32, freq)
-
-       ),
+               ),
 
        TP_fast_assign(
                __entry->core_busy = core_busy;
                __entry->scaled_busy = scaled_busy;
-               __entry->state = state;
+               __entry->from = from;
+               __entry->to = to;
                __entry->mperf = mperf;
                __entry->aperf = aperf;
+               __entry->tsc = tsc;
                __entry->freq = freq;
                ),
 
-       TP_printk("core_busy=%lu scaled=%lu state=%lu mperf=%llu aperf=%llu freq=%lu ",
+       TP_printk("core_busy=%lu scaled=%lu from=%lu to=%lu mperf=%llu aperf=%llu tsc=%llu freq=%lu ",
                (unsigned long)__entry->core_busy,
                (unsigned long)__entry->scaled_busy,
-               (unsigned long)__entry->state,
+               (unsigned long)__entry->from,
+               (unsigned long)__entry->to,
                (unsigned long long)__entry->mperf,
                (unsigned long long)__entry->aperf,
+               (unsigned long long)__entry->tsc,
                (unsigned long)__entry->freq
                )
 
index 04c3c6efdcc22d1a1a787d94b3686440521b9ff9..50fea660c0f89cfd325f6c3c22bf108d534468de 100644 (file)
@@ -6,7 +6,7 @@
 
 #include <linux/tracepoint.h>
 #include <linux/trace_seq.h>
-#include <scsi/scsi.h>
+#include <scsi/scsi_proto.h>
 #include <scsi/scsi_tcq.h>
 #include <target/target_core_base.h>
 
index 68c2c2000f02bb639e1445245e1e460a18349f54..073b9ac245ba0315f31a51f5df9f21bcdf2e9115 100644 (file)
@@ -43,15 +43,18 @@ DEFINE_EVENT(timer_class, timer_init,
  */
 TRACE_EVENT(timer_start,
 
-       TP_PROTO(struct timer_list *timer, unsigned long expires),
+       TP_PROTO(struct timer_list *timer,
+               unsigned long expires,
+               unsigned int flags),
 
-       TP_ARGS(timer, expires),
+       TP_ARGS(timer, expires, flags),
 
        TP_STRUCT__entry(
                __field( void *,        timer           )
                __field( void *,        function        )
                __field( unsigned long, expires         )
                __field( unsigned long, now             )
+               __field( unsigned int,  flags           )
        ),
 
        TP_fast_assign(
@@ -59,11 +62,12 @@ TRACE_EVENT(timer_start,
                __entry->function       = timer->function;
                __entry->expires        = expires;
                __entry->now            = jiffies;
+               __entry->flags          = flags;
        ),
 
-       TP_printk("timer=%p function=%pf expires=%lu [timeout=%ld]",
+       TP_printk("timer=%p function=%pf expires=%lu [timeout=%ld] flags=0x%08x",
                  __entry->timer, __entry->function, __entry->expires,
-                 (long)__entry->expires - __entry->now)
+                 (long)__entry->expires - __entry->now, __entry->flags)
 );
 
 /**
index 880dd74371729939a0179ef3dfd487e1fa017838..c178d13d6f4c0cb51d441c59e7b4975a1913ed3e 100644 (file)
@@ -250,7 +250,6 @@ DEFINE_EVENT(writeback_class, name, \
 DEFINE_WRITEBACK_EVENT(writeback_nowork);
 DEFINE_WRITEBACK_EVENT(writeback_wake_background);
 DEFINE_WRITEBACK_EVENT(writeback_bdi_register);
-DEFINE_WRITEBACK_EVENT(writeback_bdi_unregister);
 
 DECLARE_EVENT_CLASS(wbc_class,
        TP_PROTO(struct writeback_control *wbc, struct backing_dev_info *bdi),
index 871e73f99a4d7aa13b4cd6f5bc8969421c2a9301..94d44ab2fda1821bcda7e1b541bcfeffb556e7c5 100644 (file)
@@ -1038,6 +1038,7 @@ struct drm_radeon_cs {
 #define RADEON_INFO_CURRENT_GPU_SCLK   0x22
 #define RADEON_INFO_CURRENT_GPU_MCLK   0x23
 #define RADEON_INFO_READ_REG           0x24
+#define RADEON_INFO_VA_UNMAP_WORKING   0x25
 
 struct drm_radeon_info {
        uint32_t                request;
similarity index 95%
rename from include/linux/cryptouser.h
rename to include/uapi/linux/cryptouser.h
index 4abf2ea6a88761dd0980cba05553737c769bf72d..2e67bb64c1da58361793a76c163d9acdce5161d8 100644 (file)
@@ -25,6 +25,7 @@ enum {
        CRYPTO_MSG_DELALG,
        CRYPTO_MSG_UPDATEALG,
        CRYPTO_MSG_GETALG,
+       CRYPTO_MSG_DELRNG,
        __CRYPTO_MSG_MAX
 };
 #define CRYPTO_MSG_MAX (__CRYPTO_MSG_MAX - 1)
@@ -43,6 +44,7 @@ enum crypto_attr_type_t {
        CRYPTOCFGA_REPORT_COMPRESS,     /* struct crypto_report_comp */
        CRYPTOCFGA_REPORT_RNG,          /* struct crypto_report_rng */
        CRYPTOCFGA_REPORT_CIPHER,       /* struct crypto_report_cipher */
+       CRYPTOCFGA_REPORT_AKCIPHER,     /* struct crypto_report_akcipher */
        __CRYPTOCFGA_MAX
 
 #define CRYPTOCFGA_MAX (__CRYPTOCFGA_MAX - 1)
@@ -101,5 +103,9 @@ struct crypto_report_rng {
        unsigned int seedsize;
 };
 
+struct crypto_report_akcipher {
+       char type[CRYPTO_MAX_NAME];
+};
+
 #define CRYPTO_REPORT_MAXSIZE (sizeof(struct crypto_user_alg) + \
                               sizeof(struct crypto_report_blkcipher))
index 4957bba57cbef2e49e65f3f92f56804dca82f37d..f153d6ea7c626b1dbba775c33a600f91e1e13e4b 100644 (file)
@@ -75,6 +75,15 @@ struct cs_buffer_config {
        __u32 reserved[4];
 };
 
+/*
+ * struct for monotonic timestamp taken when the
+ * last control command was received
+ */
+struct cs_timestamp {
+       __u32 tv_sec;  /* seconds */
+       __u32 tv_nsec; /* nanoseconds */
+};
+
 /*
  * Struct describing the layout and contents of the driver mmap area.
  * This information is meant as read-only information for the application.
@@ -91,11 +100,8 @@ struct cs_mmap_config_block {
        __u32 rx_ptr;
        __u32 rx_ptr_boundary;
        __u32 reserved3[2];
-       /*
-        * if enabled with CS_FEAT_TSTAMP_RX_CTRL, monotonic
-        * timestamp taken when the last control command was received
-        */
-       struct timespec tstamp_rx_ctrl;
+       /* enabled with CS_FEAT_TSTAMP_RX_CTRL */
+       struct cs_timestamp tstamp_rx_ctrl;
 };
 
 #define CS_IO_MAGIC            'C'
index 4b60056776d1484f4c6c43b6c4a1ef47a2592a61..716ad4ae4d4b10443f4618bd96e86a71fa521d78 100644 (file)
@@ -202,7 +202,7 @@ struct kvm_run {
        __u32 exit_reason;
        __u8 ready_for_interrupt_injection;
        __u8 if_flag;
-       __u8 padding2[2];
+       __u16 flags;
 
        /* in (pre_kvm_run), out (post_kvm_run) */
        __u64 cr8;
@@ -814,6 +814,9 @@ struct kvm_ppc_smmu_info {
 #define KVM_CAP_S390_INJECT_IRQ 113
 #define KVM_CAP_S390_IRQ_STATE 114
 #define KVM_CAP_PPC_HWRNG 115
+#define KVM_CAP_DISABLE_QUIRKS 116
+#define KVM_CAP_X86_SMM 117
+#define KVM_CAP_MULTI_ADDRESS_SPACE 118
 
 #ifdef KVM_CAP_IRQ_ROUTING
 
@@ -894,7 +897,7 @@ struct kvm_xen_hvm_config {
  *
  * KVM_IRQFD_FLAG_RESAMPLE indicates resamplefd is valid and specifies
  * the irqfd to operate in resampling mode for level triggered interrupt
- * emlation.  See Documentation/virtual/kvm/api.txt.
+ * emulation.  See Documentation/virtual/kvm/api.txt.
  */
 #define KVM_IRQFD_FLAG_RESAMPLE (1 << 1)
 
@@ -1199,6 +1202,8 @@ struct kvm_s390_ucas_mapping {
 /* Available with KVM_CAP_S390_IRQ_STATE */
 #define KVM_S390_SET_IRQ_STATE   _IOW(KVMIO, 0xb5, struct kvm_s390_irq_state)
 #define KVM_S390_GET_IRQ_STATE   _IOW(KVMIO, 0xb6, struct kvm_s390_irq_state)
+/* Available with KVM_CAP_X86_SMM */
+#define KVM_SMI                   _IO(KVMIO,   0xb7)
 
 #define KVM_DEV_ASSIGN_ENABLE_IOMMU    (1 << 0)
 #define KVM_DEV_ASSIGN_PCI_2_3         (1 << 1)
index 773dfe8924c7e76a0411e08247663203a69e5306..fd2ee501726d6949d8680a70f95a686602b3b6b2 100644 (file)
@@ -6,7 +6,7 @@
  *
  *  ebtables.c,v 2.0, April, 2002
  *
- *  This code is stongly inspired on the iptables code which is
+ *  This code is strongly inspired by the iptables code which is
  *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
  */
 
index 309211b3eb672f9449a0fc266ed643ac881196d3..d97f84c080daefb3e8789a59b98cedbbe387cc56 100644 (file)
@@ -167,6 +167,7 @@ enum perf_branch_sample_type_shift {
        PERF_SAMPLE_BRANCH_COND_SHIFT           = 10, /* conditional branches */
 
        PERF_SAMPLE_BRANCH_CALL_STACK_SHIFT     = 11, /* call/ret stack */
+       PERF_SAMPLE_BRANCH_IND_JUMP_SHIFT       = 12, /* indirect jumps */
 
        PERF_SAMPLE_BRANCH_MAX_SHIFT            /* non-ABI */
 };
@@ -186,6 +187,7 @@ enum perf_branch_sample_type {
        PERF_SAMPLE_BRANCH_COND         = 1U << PERF_SAMPLE_BRANCH_COND_SHIFT,
 
        PERF_SAMPLE_BRANCH_CALL_STACK   = 1U << PERF_SAMPLE_BRANCH_CALL_STACK_SHIFT,
+       PERF_SAMPLE_BRANCH_IND_JUMP     = 1U << PERF_SAMPLE_BRANCH_IND_JUMP_SHIFT,
 
        PERF_SAMPLE_BRANCH_MAX          = 1U << PERF_SAMPLE_BRANCH_MAX_SHIFT,
 };
@@ -563,6 +565,10 @@ struct perf_event_mmap_page {
 #define PERF_RECORD_MISC_GUEST_KERNEL          (4 << 0)
 #define PERF_RECORD_MISC_GUEST_USER            (5 << 0)
 
+/*
+ * Indicates that /proc/PID/maps parsing are truncated by time out.
+ */
+#define PERF_RECORD_MISC_PROC_MAP_PARSE_TIMEOUT        (1 << 12)
 /*
  * PERF_RECORD_MISC_MMAP_DATA and PERF_RECORD_MISC_COMM_EXEC are used on
  * different events so can reuse the same bit position.
@@ -800,6 +806,18 @@ enum perf_event_type {
         */
        PERF_RECORD_ITRACE_START                = 12,
 
+       /*
+        * Records the dropped/lost sample number.
+        *
+        * struct {
+        *      struct perf_event_header        header;
+        *
+        *      u64                             lost;
+        *      struct sample_id                sample_id;
+        * };
+        */
+       PERF_RECORD_LOST_SAMPLES                = 13,
+
        PERF_RECORD_MAX,                        /* non-ABI */
 };
 
index b57b750c222f1d5729b7b171a9131e0e1ed9ccd5..9fd7b5d8df2fa357f434a899cccfa7810f826107 100644 (file)
@@ -36,6 +36,8 @@
 /* Two-stage IOMMU */
 #define VFIO_TYPE1_NESTING_IOMMU       6       /* Implies v2 */
 
+#define VFIO_SPAPR_TCE_v2_IOMMU                7
+
 /*
  * The IOCTL interface is designed for extensibility by embedding the
  * structure length (argsz) and flags into structures passed between
@@ -442,6 +444,23 @@ struct vfio_iommu_type1_dma_unmap {
 
 /* -------- Additional API for SPAPR TCE (Server POWERPC) IOMMU -------- */
 
+/*
+ * The SPAPR TCE DDW info struct provides the information about
+ * the details of Dynamic DMA window capability.
+ *
+ * @pgsizes contains a page size bitmask, 4K/64K/16M are supported.
+ * @max_dynamic_windows_supported tells the maximum number of windows
+ * which the platform can create.
+ * @levels tells the maximum number of levels in multi-level IOMMU tables;
+ * this allows splitting a table into smaller chunks which reduces
+ * the amount of physically contiguous memory required for the table.
+ */
+struct vfio_iommu_spapr_tce_ddw_info {
+       __u64 pgsizes;                  /* Bitmap of supported page sizes */
+       __u32 max_dynamic_windows_supported;
+       __u32 levels;
+};
+
 /*
  * The SPAPR TCE info struct provides the information about the PCI bus
  * address ranges available for DMA, these values are programmed into
@@ -452,14 +471,17 @@ struct vfio_iommu_type1_dma_unmap {
  * addresses too so the window works as a filter rather than an offset
  * for IOVA addresses.
  *
- * A flag will need to be added if other page sizes are supported,
- * so as defined here, it is always 4k.
+ * Flags supported:
+ * - VFIO_IOMMU_SPAPR_INFO_DDW: informs the userspace that dynamic DMA windows
+ *   (DDW) support is present. @ddw is only supported when DDW is present.
  */
 struct vfio_iommu_spapr_tce_info {
        __u32 argsz;
-       __u32 flags;                    /* reserved for future use */
+       __u32 flags;
+#define VFIO_IOMMU_SPAPR_INFO_DDW      (1 << 0)        /* DDW supported */
        __u32 dma32_window_start;       /* 32 bit window start (bytes) */
        __u32 dma32_window_size;        /* 32 bit window size (bytes) */
+       struct vfio_iommu_spapr_tce_ddw_info ddw;
 };
 
 #define VFIO_IOMMU_SPAPR_TCE_GET_INFO  _IO(VFIO_TYPE, VFIO_BASE + 12)
@@ -470,12 +492,23 @@ struct vfio_iommu_spapr_tce_info {
  * - unfreeze IO/DMA for frozen PE;
  * - read PE state;
  * - reset PE;
- * - configure PE.
+ * - configure PE;
+ * - inject EEH error.
  */
+struct vfio_eeh_pe_err {
+       __u32 type;
+       __u32 func;
+       __u64 addr;
+       __u64 mask;
+};
+
 struct vfio_eeh_pe_op {
        __u32 argsz;
        __u32 flags;
        __u32 op;
+       union {
+               struct vfio_eeh_pe_err err;
+       };
 };
 
 #define VFIO_EEH_PE_DISABLE            0       /* Disable EEH functionality */
@@ -492,9 +525,70 @@ struct vfio_eeh_pe_op {
 #define VFIO_EEH_PE_RESET_HOT          6       /* Assert hot reset          */
 #define VFIO_EEH_PE_RESET_FUNDAMENTAL  7       /* Assert fundamental reset  */
 #define VFIO_EEH_PE_CONFIGURE          8       /* PE configuration          */
+#define VFIO_EEH_PE_INJECT_ERR         9       /* Inject EEH error          */
 
 #define VFIO_EEH_PE_OP                 _IO(VFIO_TYPE, VFIO_BASE + 21)
 
+/**
+ * VFIO_IOMMU_SPAPR_REGISTER_MEMORY - _IOW(VFIO_TYPE, VFIO_BASE + 17, struct vfio_iommu_spapr_register_memory)
+ *
+ * Registers user space memory where DMA is allowed. It pins
+ * user pages and does the locked memory accounting so
+ * subsequent VFIO_IOMMU_MAP_DMA/VFIO_IOMMU_UNMAP_DMA calls
+ * get faster.
+ */
+struct vfio_iommu_spapr_register_memory {
+       __u32   argsz;
+       __u32   flags;
+       __u64   vaddr;                          /* Process virtual address */
+       __u64   size;                           /* Size of mapping (bytes) */
+};
+#define VFIO_IOMMU_SPAPR_REGISTER_MEMORY       _IO(VFIO_TYPE, VFIO_BASE + 17)
+
+/**
+ * VFIO_IOMMU_SPAPR_UNREGISTER_MEMORY - _IOW(VFIO_TYPE, VFIO_BASE + 18, struct vfio_iommu_spapr_register_memory)
+ *
+ * Unregisters user space memory registered with
+ * VFIO_IOMMU_SPAPR_REGISTER_MEMORY.
+ * Uses vfio_iommu_spapr_register_memory for parameters.
+ */
+#define VFIO_IOMMU_SPAPR_UNREGISTER_MEMORY     _IO(VFIO_TYPE, VFIO_BASE + 18)
+
+/**
+ * VFIO_IOMMU_SPAPR_TCE_CREATE - _IOWR(VFIO_TYPE, VFIO_BASE + 19, struct vfio_iommu_spapr_tce_create)
+ *
+ * Creates an additional TCE table and programs it (sets a new DMA window)
+ * to every IOMMU group in the container. It receives page shift, window
+ * size and number of levels in the TCE table being created.
+ *
+ * It allocates and returns an offset on a PCI bus of the new DMA window.
+ */
+struct vfio_iommu_spapr_tce_create {
+       __u32 argsz;
+       __u32 flags;
+       /* in */
+       __u32 page_shift;
+       __u64 window_size;
+       __u32 levels;
+       /* out */
+       __u64 start_addr;
+};
+#define VFIO_IOMMU_SPAPR_TCE_CREATE    _IO(VFIO_TYPE, VFIO_BASE + 19)
+
+/**
+ * VFIO_IOMMU_SPAPR_TCE_REMOVE - _IOW(VFIO_TYPE, VFIO_BASE + 20, struct vfio_iommu_spapr_tce_remove)
+ *
+ * Unprograms a TCE table from all groups in the container and destroys it.
+ * It receives a PCI bus offset as a window id.
+ */
+struct vfio_iommu_spapr_tce_remove {
+       __u32 argsz;
+       __u32 flags;
+       /* in */
+       __u64 start_addr;
+};
+#define VFIO_IOMMU_SPAPR_TCE_REMOVE    _IO(VFIO_TYPE, VFIO_BASE + 20)
+
 /* ***************************************************************** */
 
 #endif /* _UAPIVFIO_H */
index cd6d789b73ec4cd15c116f2302c305db845f9385..99a8ca15fe648c9d176325eb2ed9c952a5ac7088 100644 (file)
@@ -32,10 +32,32 @@ struct cxl_ioctl_start_work {
 #define CXL_START_WORK_ALL             (CXL_START_WORK_AMR |\
                                         CXL_START_WORK_NUM_IRQS)
 
+
+/* Possible modes that an afu can be in */
+#define CXL_MODE_DEDICATED   0x1
+#define CXL_MODE_DIRECTED    0x2
+
+/* possible flags for the cxl_afu_id flags field */
+#define CXL_AFUID_FLAG_SLAVE    0x1  /* In directed-mode afu is in slave mode */
+
+struct cxl_afu_id {
+       __u64 flags;     /* One of CXL_AFUID_FLAG_X */
+       __u32 card_id;
+       __u32 afu_offset;
+       __u32 afu_mode;  /* one of the CXL_MODE_X */
+       __u32 reserved1;
+       __u64 reserved2;
+       __u64 reserved3;
+       __u64 reserved4;
+       __u64 reserved5;
+       __u64 reserved6;
+};
+
 /* ioctl numbers */
 #define CXL_MAGIC 0xCA
 #define CXL_IOCTL_START_WORK           _IOW(CXL_MAGIC, 0x00, struct cxl_ioctl_start_work)
 #define CXL_IOCTL_GET_PROCESS_ELEMENT  _IOR(CXL_MAGIC, 0x01, __u32)
+#define CXL_IOCTL_GET_AFU_ID            _IOR(CXL_MAGIC, 0x02, struct cxl_afu_id)
 
 #define CXL_READ_MIN_SIZE 0x1000 /* 4K */
 
index b513e662d8e4999401f3c7366368bfb3c7900664..978841eeaff10e1cffc5d1c6e37fbe175c3db197 100644 (file)
@@ -91,6 +91,7 @@ enum {
 
 enum {
        IB_USER_VERBS_EX_CMD_QUERY_DEVICE = IB_USER_VERBS_CMD_QUERY_DEVICE,
+       IB_USER_VERBS_EX_CMD_CREATE_CQ = IB_USER_VERBS_CMD_CREATE_CQ,
        IB_USER_VERBS_EX_CMD_CREATE_FLOW = IB_USER_VERBS_CMD_THRESHOLD,
        IB_USER_VERBS_EX_CMD_DESTROY_FLOW,
 };
@@ -222,6 +223,8 @@ struct ib_uverbs_ex_query_device_resp {
        __u32 comp_mask;
        __u32 response_length;
        struct ib_uverbs_odp_caps odp_caps;
+       __u64 timestamp_mask;
+       __u64 hca_core_clock; /* in KHZ */
 };
 
 struct ib_uverbs_query_port {
@@ -353,11 +356,27 @@ struct ib_uverbs_create_cq {
        __u64 driver_data[0];
 };
 
+struct ib_uverbs_ex_create_cq {
+       __u64 user_handle;
+       __u32 cqe;
+       __u32 comp_vector;
+       __s32 comp_channel;
+       __u32 comp_mask;
+       __u32 flags;
+       __u32 reserved;
+};
+
 struct ib_uverbs_create_cq_resp {
        __u32 cq_handle;
        __u32 cqe;
 };
 
+struct ib_uverbs_ex_create_cq_resp {
+       struct ib_uverbs_create_cq_resp base;
+       __u32 comp_mask;
+       __u32 response_length;
+};
+
 struct ib_uverbs_resize_cq {
        __u64 response;
        __u32 cq_handle;
index bc5013e8059d562cbffd46c214c7c7e485abf76d..91e225a6107d4ab000984b6634b1f43402f3d496 100644 (file)
@@ -159,10 +159,7 @@ struct neofb_par {
        unsigned char VCLK3NumeratorHigh;
        unsigned char VCLK3Denominator;
        unsigned char VerticalExt;
-
-#ifdef CONFIG_MTRR
-       int mtrr;
-#endif
+       int wc_cookie;
        u8 __iomem *mmio_vbase;
        u8 cursorOff;
        u8 *cursorPad;          /* Must die !! */
index befbaf0a92d83c84ec980c56bc1fce45d4f1c0f3..69674b94bb684b2271a599fe4d29e273d0084f4f 100644 (file)
@@ -196,7 +196,7 @@ struct tdfx_par {
        u32 palette[16];
        void __iomem *regbase_virt;
        unsigned long iobase;
-       int mtrr_handle;
+       int wc_cookie;
 #ifdef CONFIG_FB_3DFX_I2C
        struct tdfxfb_i2c_chan chan[2];
 #endif
index dc24dec6023292ac6f1bd484ce506074d3e53566..b999fa381bf9fe1f37757af5e0a454cc6adb2da9 100644 (file)
@@ -465,13 +465,9 @@ endmenu # "CPU/Task time and stats accounting"
 
 menu "RCU Subsystem"
 
-choice
-       prompt "RCU Implementation"
-       default TREE_RCU
-
 config TREE_RCU
-       bool "Tree-based hierarchical RCU"
-       depends on !PREEMPT && SMP
+       bool
+       default y if !PREEMPT && SMP
        help
          This option selects the RCU implementation that is
          designed for very large SMP system with hundreds or
@@ -479,8 +475,8 @@ config TREE_RCU
          smaller systems.
 
 config PREEMPT_RCU
-       bool "Preemptible tree-based hierarchical RCU"
-       depends on PREEMPT
+       bool
+       default y if PREEMPT
        help
          This option selects the RCU implementation that is
          designed for very large SMP systems with hundreds or
@@ -491,15 +487,28 @@ config PREEMPT_RCU
          Select this option if you are unsure.
 
 config TINY_RCU
-       bool "UP-only small-memory-footprint RCU"
-       depends on !PREEMPT && !SMP
+       bool
+       default y if !PREEMPT && !SMP
        help
          This option selects the RCU implementation that is
          designed for UP systems from which real-time response
          is not required.  This option greatly reduces the
          memory footprint of RCU.
 
-endchoice
+config RCU_EXPERT
+       bool "Make expert-level adjustments to RCU configuration"
+       default n
+       help
+         This option needs to be enabled if you wish to make
+         expert-level adjustments to RCU configuration.  By default,
+         no such adjustments can be made, which has the often-beneficial
+         side-effect of preventing "make oldconfig" from asking you all
+         sorts of detailed questions about how you would like numerous
+         obscure RCU options to be set up.
+
+         Say Y if you need to make expert-level adjustments to RCU.
+
+         Say N if you are unsure.
 
 config SRCU
        bool
@@ -509,7 +518,7 @@ config SRCU
          sections.
 
 config TASKS_RCU
-       bool "Task_based RCU implementation using voluntary context switch"
+       bool
        default n
        select SRCU
        help
@@ -517,8 +526,6 @@ config TASKS_RCU
          only voluntary context switch (not preemption!), idle, and
          user-mode execution as quiescent states.
 
-         If unsure, say N.
-
 config RCU_STALL_COMMON
        def_bool ( TREE_RCU || PREEMPT_RCU || RCU_TRACE )
        help
@@ -531,9 +538,7 @@ config CONTEXT_TRACKING
        bool
 
 config RCU_USER_QS
-       bool "Consider userspace as in RCU extended quiescent state"
-       depends on HAVE_CONTEXT_TRACKING && SMP
-       select CONTEXT_TRACKING
+       bool
        help
          This option sets hooks on kernel / userspace boundaries and
          puts RCU in extended quiescent state when the CPU runs in
@@ -541,12 +546,6 @@ config RCU_USER_QS
          excluded from the global RCU state machine and thus doesn't
          try to keep the timer tick on for RCU.
 
-         Unless you want to hack and help the development of the full
-         dynticks mode, you shouldn't enable this option.  It also
-         adds unnecessary overhead.
-
-         If unsure say N
-
 config CONTEXT_TRACKING_FORCE
        bool "Force context tracking"
        depends on CONTEXT_TRACKING
@@ -578,7 +577,7 @@ config RCU_FANOUT
        int "Tree-based hierarchical RCU fanout value"
        range 2 64 if 64BIT
        range 2 32 if !64BIT
-       depends on TREE_RCU || PREEMPT_RCU
+       depends on (TREE_RCU || PREEMPT_RCU) && RCU_EXPERT
        default 64 if 64BIT
        default 32 if !64BIT
        help
@@ -596,9 +595,9 @@ config RCU_FANOUT
 
 config RCU_FANOUT_LEAF
        int "Tree-based hierarchical RCU leaf-level fanout value"
-       range 2 RCU_FANOUT if 64BIT
-       range 2 RCU_FANOUT if !64BIT
-       depends on TREE_RCU || PREEMPT_RCU
+       range 2 64 if 64BIT
+       range 2 32 if !64BIT
+       depends on (TREE_RCU || PREEMPT_RCU) && RCU_EXPERT
        default 16
        help
          This option controls the leaf-level fanout of hierarchical
@@ -621,23 +620,9 @@ config RCU_FANOUT_LEAF
 
          Take the default if unsure.
 
-config RCU_FANOUT_EXACT
-       bool "Disable tree-based hierarchical RCU auto-balancing"
-       depends on TREE_RCU || PREEMPT_RCU
-       default n
-       help
-         This option forces use of the exact RCU_FANOUT value specified,
-         regardless of imbalances in the hierarchy.  This is useful for
-         testing RCU itself, and might one day be useful on systems with
-         strong NUMA behavior.
-
-         Without RCU_FANOUT_EXACT, the code will balance the hierarchy.
-
-         Say N if unsure.
-
 config RCU_FAST_NO_HZ
        bool "Accelerate last non-dyntick-idle CPU's grace periods"
-       depends on NO_HZ_COMMON && SMP
+       depends on NO_HZ_COMMON && SMP && RCU_EXPERT
        default n
        help
          This option permits CPUs to enter dynticks-idle state even if
@@ -663,7 +648,7 @@ config TREE_RCU_TRACE
 
 config RCU_BOOST
        bool "Enable RCU priority boosting"
-       depends on RT_MUTEXES && PREEMPT_RCU
+       depends on RT_MUTEXES && PREEMPT_RCU && RCU_EXPERT
        default n
        help
          This option boosts the priority of preempted RCU readers that
@@ -680,6 +665,7 @@ config RCU_KTHREAD_PRIO
        range 0 99 if !RCU_BOOST
        default 1 if RCU_BOOST
        default 0 if !RCU_BOOST
+       depends on RCU_EXPERT
        help
          This option specifies the SCHED_FIFO priority value that will be
          assigned to the rcuc/n and rcub/n threads and is also the value
@@ -1637,7 +1623,7 @@ config PERF_EVENTS
 config DEBUG_PERF_USE_VMALLOC
        default n
        bool "Debug: use vmalloc to back perf mmap() buffers"
-       depends on PERF_EVENTS && DEBUG_KERNEL
+       depends on PERF_EVENTS && DEBUG_KERNEL && !PPC
        select PERF_USE_VMALLOC
        help
         Use vmalloc memory to back perf mmap() buffers.
index 2115055faeac948bd164c9e8d574795f557f02a5..2a89545e0a5d690db09acaa7042c83256ea84fb1 100644 (file)
@@ -664,6 +664,7 @@ asmlinkage __visible void __init start_kernel(void)
 
        check_bugs();
 
+       acpi_subsystem_init();
        sfi_init_late();
 
        if (efi_enabled(EFI_RUNTIME_SERVICES)) {
index 08561f1acd130bd68314e03278402e629b028ba4..ebdb0043203adb339e7b917dd3bb28b47d2ee480 100644 (file)
@@ -235,9 +235,16 @@ config LOCK_SPIN_ON_OWNER
        def_bool y
        depends on MUTEX_SPIN_ON_OWNER || RWSEM_SPIN_ON_OWNER
 
-config ARCH_USE_QUEUE_RWLOCK
+config ARCH_USE_QUEUED_SPINLOCKS
        bool
 
-config QUEUE_RWLOCK
-       def_bool y if ARCH_USE_QUEUE_RWLOCK
+config QUEUED_SPINLOCKS
+       def_bool y if ARCH_USE_QUEUED_SPINLOCKS
+       depends on SMP
+
+config ARCH_USE_QUEUED_RWLOCKS
+       bool
+
+config QUEUED_RWLOCKS
+       def_bool y if ARCH_USE_QUEUED_RWLOCKS
        depends on SMP
index 24f00610c575fd5d34c40dcf9fad35b358596c22..333d364be29d9e6c8b209d9eaded9d28552a36d7 100644 (file)
@@ -912,7 +912,8 @@ long compat_get_bitmap(unsigned long *mask, const compat_ulong_t __user *umask,
                         * bitmap. We must however ensure the end of the
                         * kernel bitmap is zeroed.
                         */
-                       if (nr_compat_longs-- > 0) {
+                       if (nr_compat_longs) {
+                               nr_compat_longs--;
                                if (__get_user(um, umask))
                                        return -EFAULT;
                        } else {
@@ -954,7 +955,8 @@ long compat_put_bitmap(compat_ulong_t __user *umask, unsigned long *mask,
                         * We dont want to write past the end of the userspace
                         * bitmap.
                         */
-                       if (nr_compat_longs-- > 0) {
+                       if (nr_compat_longs) {
+                               nr_compat_longs--;
                                if (__put_user(um, umask))
                                        return -EFAULT;
                        }
index 72d59a1a6eb66f54985aa8711e0d55ed46150628..0a495ab35bc72b55d8eab13f0922e6fdb8099d9a 100644 (file)
@@ -30,12 +30,23 @@ EXPORT_SYMBOL_GPL(context_tracking_enabled);
 DEFINE_PER_CPU(struct context_tracking, context_tracking);
 EXPORT_SYMBOL_GPL(context_tracking);
 
-void context_tracking_cpu_set(int cpu)
+static bool context_tracking_recursion_enter(void)
 {
-       if (!per_cpu(context_tracking.active, cpu)) {
-               per_cpu(context_tracking.active, cpu) = true;
-               static_key_slow_inc(&context_tracking_enabled);
-       }
+       int recursion;
+
+       recursion = __this_cpu_inc_return(context_tracking.recursion);
+       if (recursion == 1)
+               return true;
+
+       WARN_ONCE((recursion < 1), "Invalid context tracking recursion value %d\n", recursion);
+       __this_cpu_dec(context_tracking.recursion);
+
+       return false;
+}
+
+static void context_tracking_recursion_exit(void)
+{
+       __this_cpu_dec(context_tracking.recursion);
 }
 
 /**
@@ -75,6 +86,9 @@ void context_tracking_enter(enum ctx_state state)
        WARN_ON_ONCE(!current->mm);
 
        local_irq_save(flags);
+       if (!context_tracking_recursion_enter())
+               goto out_irq_restore;
+
        if ( __this_cpu_read(context_tracking.state) != state) {
                if (__this_cpu_read(context_tracking.active)) {
                        /*
@@ -105,6 +119,8 @@ void context_tracking_enter(enum ctx_state state)
                 */
                __this_cpu_write(context_tracking.state, state);
        }
+       context_tracking_recursion_exit();
+out_irq_restore:
        local_irq_restore(flags);
 }
 NOKPROBE_SYMBOL(context_tracking_enter);
@@ -139,6 +155,9 @@ void context_tracking_exit(enum ctx_state state)
                return;
 
        local_irq_save(flags);
+       if (!context_tracking_recursion_enter())
+               goto out_irq_restore;
+
        if (__this_cpu_read(context_tracking.state) == state) {
                if (__this_cpu_read(context_tracking.active)) {
                        /*
@@ -153,6 +172,8 @@ void context_tracking_exit(enum ctx_state state)
                }
                __this_cpu_write(context_tracking.state, CONTEXT_KERNEL);
        }
+       context_tracking_recursion_exit();
+out_irq_restore:
        local_irq_restore(flags);
 }
 NOKPROBE_SYMBOL(context_tracking_exit);
@@ -164,24 +185,26 @@ void context_tracking_user_exit(void)
 }
 NOKPROBE_SYMBOL(context_tracking_user_exit);
 
-/**
- * __context_tracking_task_switch - context switch the syscall callbacks
- * @prev: the task that is being switched out
- * @next: the task that is being switched in
- *
- * The context tracking uses the syscall slow path to implement its user-kernel
- * boundaries probes on syscalls. This way it doesn't impact the syscall fast
- * path on CPUs that don't do context tracking.
- *
- * But we need to clear the flag on the previous task because it may later
- * migrate to some CPU that doesn't do the context tracking. As such the TIF
- * flag may not be desired there.
- */
-void __context_tracking_task_switch(struct task_struct *prev,
-                                   struct task_struct *next)
+void __init context_tracking_cpu_set(int cpu)
 {
-       clear_tsk_thread_flag(prev, TIF_NOHZ);
-       set_tsk_thread_flag(next, TIF_NOHZ);
+       static __initdata bool initialized = false;
+
+       if (!per_cpu(context_tracking.active, cpu)) {
+               per_cpu(context_tracking.active, cpu) = true;
+               static_key_slow_inc(&context_tracking_enabled);
+       }
+
+       if (initialized)
+               return;
+
+       /*
+        * Set TIF_NOHZ to init/0 and let it propagate to all tasks through fork
+        * This assumes that init is the only task at this early boot stage.
+        */
+       set_tsk_thread_flag(&init_task, TIF_NOHZ);
+       WARN_ON_ONCE(!tasklist_empty());
+
+       initialized = true;
 }
 
 #ifdef CONFIG_CONTEXT_TRACKING_FORCE
index 94bbe4695232cd2fa2e9c0def32de7fa27644971..9c9c9fab16cc3610afa76a6c467780482b35b0be 100644 (file)
@@ -398,7 +398,6 @@ static int __ref _cpu_down(unsigned int cpu, int tasks_frozen)
        err = __stop_machine(take_cpu_down, &tcd_param, cpumask_of(cpu));
        if (err) {
                /* CPU didn't die: tell everyone.  Can't complain. */
-               smpboot_unpark_threads(cpu);
                cpu_notify_nofail(CPU_DOWN_FAILED | mod, hcpu);
                goto out_release;
        }
@@ -463,6 +462,7 @@ static int smpboot_thread_call(struct notifier_block *nfb,
 
        switch (action & ~CPU_TASKS_FROZEN) {
 
+       case CPU_DOWN_FAILED:
        case CPU_ONLINE:
                smpboot_unpark_threads(cpu);
                break;
@@ -479,7 +479,7 @@ static struct notifier_block smpboot_thread_notifier = {
        .priority = CPU_PRI_SMPBOOT,
 };
 
-void __cpuinit smpboot_thread_init(void)
+void smpboot_thread_init(void)
 {
        register_cpu_notifier(&smpboot_thread_notifier);
 }
index 1a3bf48743ce1c62c26077d642084cbdc8b40d6b..8e13f3e54ec369f26d52e52081f013a6aa29fd23 100644 (file)
 
 static struct workqueue_struct *perf_wq;
 
+typedef int (*remote_function_f)(void *);
+
 struct remote_function_call {
        struct task_struct      *p;
-       int                     (*func)(void *info);
+       remote_function_f       func;
        void                    *info;
        int                     ret;
 };
@@ -86,7 +88,7 @@ static void remote_function(void *data)
  *         -EAGAIN - when the process moved away
  */
 static int
-task_function_call(struct task_struct *p, int (*func) (void *info), void *info)
+task_function_call(struct task_struct *p, remote_function_f func, void *info)
 {
        struct remote_function_call data = {
                .p      = p,
@@ -110,7 +112,7 @@ task_function_call(struct task_struct *p, int (*func) (void *info), void *info)
  *
  * returns: @func return value or -ENXIO when the cpu is offline
  */
-static int cpu_function_call(int cpu, int (*func) (void *info), void *info)
+static int cpu_function_call(int cpu, remote_function_f func, void *info)
 {
        struct remote_function_call data = {
                .p      = NULL,
@@ -747,62 +749,31 @@ perf_cgroup_mark_enabled(struct perf_event *event,
 /*
  * function must be called with interrupts disbled
  */
-static enum hrtimer_restart perf_cpu_hrtimer_handler(struct hrtimer *hr)
+static enum hrtimer_restart perf_mux_hrtimer_handler(struct hrtimer *hr)
 {
        struct perf_cpu_context *cpuctx;
-       enum hrtimer_restart ret = HRTIMER_NORESTART;
        int rotations = 0;
 
        WARN_ON(!irqs_disabled());
 
        cpuctx = container_of(hr, struct perf_cpu_context, hrtimer);
-
        rotations = perf_rotate_context(cpuctx);
 
-       /*
-        * arm timer if needed
-        */
-       if (rotations) {
+       raw_spin_lock(&cpuctx->hrtimer_lock);
+       if (rotations)
                hrtimer_forward_now(hr, cpuctx->hrtimer_interval);
-               ret = HRTIMER_RESTART;
-       }
-
-       return ret;
-}
-
-/* CPU is going down */
-void perf_cpu_hrtimer_cancel(int cpu)
-{
-       struct perf_cpu_context *cpuctx;
-       struct pmu *pmu;
-       unsigned long flags;
-
-       if (WARN_ON(cpu != smp_processor_id()))
-               return;
-
-       local_irq_save(flags);
-
-       rcu_read_lock();
-
-       list_for_each_entry_rcu(pmu, &pmus, entry) {
-               cpuctx = this_cpu_ptr(pmu->pmu_cpu_context);
-
-               if (pmu->task_ctx_nr == perf_sw_context)
-                       continue;
-
-               hrtimer_cancel(&cpuctx->hrtimer);
-       }
-
-       rcu_read_unlock();
+       else
+               cpuctx->hrtimer_active = 0;
+       raw_spin_unlock(&cpuctx->hrtimer_lock);
 
-       local_irq_restore(flags);
+       return rotations ? HRTIMER_RESTART : HRTIMER_NORESTART;
 }
 
-static void __perf_cpu_hrtimer_init(struct perf_cpu_context *cpuctx, int cpu)
+static void __perf_mux_hrtimer_init(struct perf_cpu_context *cpuctx, int cpu)
 {
-       struct hrtimer *hr = &cpuctx->hrtimer;
+       struct hrtimer *timer = &cpuctx->hrtimer;
        struct pmu *pmu = cpuctx->ctx.pmu;
-       int timer;
+       u64 interval;
 
        /* no multiplexing needed for SW PMU */
        if (pmu->task_ctx_nr == perf_sw_context)
@@ -812,31 +783,36 @@ static void __perf_cpu_hrtimer_init(struct perf_cpu_context *cpuctx, int cpu)
         * check default is sane, if not set then force to
         * default interval (1/tick)
         */
-       timer = pmu->hrtimer_interval_ms;
-       if (timer < 1)
-               timer = pmu->hrtimer_interval_ms = PERF_CPU_HRTIMER;
+       interval = pmu->hrtimer_interval_ms;
+       if (interval < 1)
+               interval = pmu->hrtimer_interval_ms = PERF_CPU_HRTIMER;
 
-       cpuctx->hrtimer_interval = ns_to_ktime(NSEC_PER_MSEC * timer);
+       cpuctx->hrtimer_interval = ns_to_ktime(NSEC_PER_MSEC * interval);
 
-       hrtimer_init(hr, CLOCK_MONOTONIC, HRTIMER_MODE_REL_PINNED);
-       hr->function = perf_cpu_hrtimer_handler;
+       raw_spin_lock_init(&cpuctx->hrtimer_lock);
+       hrtimer_init(timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS_PINNED);
+       timer->function = perf_mux_hrtimer_handler;
 }
 
-static void perf_cpu_hrtimer_restart(struct perf_cpu_context *cpuctx)
+static int perf_mux_hrtimer_restart(struct perf_cpu_context *cpuctx)
 {
-       struct hrtimer *hr = &cpuctx->hrtimer;
+       struct hrtimer *timer = &cpuctx->hrtimer;
        struct pmu *pmu = cpuctx->ctx.pmu;
+       unsigned long flags;
 
        /* not for SW PMU */
        if (pmu->task_ctx_nr == perf_sw_context)
-               return;
+               return 0;
 
-       if (hrtimer_active(hr))
-               return;
+       raw_spin_lock_irqsave(&cpuctx->hrtimer_lock, flags);
+       if (!cpuctx->hrtimer_active) {
+               cpuctx->hrtimer_active = 1;
+               hrtimer_forward_now(timer, cpuctx->hrtimer_interval);
+               hrtimer_start_expires(timer, HRTIMER_MODE_ABS_PINNED);
+       }
+       raw_spin_unlock_irqrestore(&cpuctx->hrtimer_lock, flags);
 
-       if (!hrtimer_callback_running(hr))
-               __hrtimer_start_range_ns(hr, cpuctx->hrtimer_interval,
-                                        0, HRTIMER_MODE_REL_PINNED, 0);
+       return 0;
 }
 
 void perf_pmu_disable(struct pmu *pmu)
@@ -1935,7 +1911,7 @@ group_sched_in(struct perf_event *group_event,
 
        if (event_sched_in(group_event, cpuctx, ctx)) {
                pmu->cancel_txn(pmu);
-               perf_cpu_hrtimer_restart(cpuctx);
+               perf_mux_hrtimer_restart(cpuctx);
                return -EAGAIN;
        }
 
@@ -1982,7 +1958,7 @@ group_error:
 
        pmu->cancel_txn(pmu);
 
-       perf_cpu_hrtimer_restart(cpuctx);
+       perf_mux_hrtimer_restart(cpuctx);
 
        return -EAGAIN;
 }
@@ -2255,7 +2231,7 @@ static int __perf_event_enable(void *info)
                 */
                if (leader != event) {
                        group_sched_out(leader, cpuctx, ctx);
-                       perf_cpu_hrtimer_restart(cpuctx);
+                       perf_mux_hrtimer_restart(cpuctx);
                }
                if (leader->attr.pinned) {
                        update_group_times(leader);
@@ -3442,7 +3418,6 @@ static void free_event_rcu(struct rcu_head *head)
        if (event->ns)
                put_pid_ns(event->ns);
        perf_event_free_filter(event);
-       perf_event_free_bpf_prog(event);
        kfree(event);
 }
 
@@ -3573,6 +3548,8 @@ static void __free_event(struct perf_event *event)
                        put_callchain_buffers();
        }
 
+       perf_event_free_bpf_prog(event);
+
        if (event->destroy)
                event->destroy(event);
 
@@ -4330,20 +4307,20 @@ static void ring_buffer_attach(struct perf_event *event,
                WARN_ON_ONCE(event->rcu_pending);
 
                old_rb = event->rb;
-               event->rcu_batches = get_state_synchronize_rcu();
-               event->rcu_pending = 1;
-
                spin_lock_irqsave(&old_rb->event_lock, flags);
                list_del_rcu(&event->rb_entry);
                spin_unlock_irqrestore(&old_rb->event_lock, flags);
-       }
 
-       if (event->rcu_pending && rb) {
-               cond_synchronize_rcu(event->rcu_batches);
-               event->rcu_pending = 0;
+               event->rcu_batches = get_state_synchronize_rcu();
+               event->rcu_pending = 1;
        }
 
        if (rb) {
+               if (event->rcu_pending) {
+                       cond_synchronize_rcu(event->rcu_batches);
+                       event->rcu_pending = 0;
+               }
+
                spin_lock_irqsave(&rb->event_lock, flags);
                list_add_rcu(&event->rb_entry, &rb->event_list);
                spin_unlock_irqrestore(&rb->event_lock, flags);
@@ -5380,9 +5357,9 @@ void perf_prepare_sample(struct perf_event_header *header,
        }
 }
 
-static void perf_event_output(struct perf_event *event,
-                               struct perf_sample_data *data,
-                               struct pt_regs *regs)
+void perf_event_output(struct perf_event *event,
+                       struct perf_sample_data *data,
+                       struct pt_regs *regs)
 {
        struct perf_output_handle handle;
        struct perf_event_header header;
@@ -5973,6 +5950,39 @@ void perf_event_aux_event(struct perf_event *event, unsigned long head,
        perf_output_end(&handle);
 }
 
+/*
+ * Lost/dropped samples logging
+ */
+void perf_log_lost_samples(struct perf_event *event, u64 lost)
+{
+       struct perf_output_handle handle;
+       struct perf_sample_data sample;
+       int ret;
+
+       struct {
+               struct perf_event_header        header;
+               u64                             lost;
+       } lost_samples_event = {
+               .header = {
+                       .type = PERF_RECORD_LOST_SAMPLES,
+                       .misc = 0,
+                       .size = sizeof(lost_samples_event),
+               },
+               .lost           = lost,
+       };
+
+       perf_event_header__init_id(&lost_samples_event.header, &sample, event);
+
+       ret = perf_output_begin(&handle, event,
+                               lost_samples_event.header.size);
+       if (ret)
+               return;
+
+       perf_output_put(&handle, lost_samples_event);
+       perf_event__output_id_sample(event, &handle, &sample);
+       perf_output_end(&handle);
+}
+
 /*
  * IRQ throttle logging
  */
@@ -6863,9 +6873,8 @@ static void perf_swevent_start_hrtimer(struct perf_event *event)
        } else {
                period = max_t(u64, 10000, hwc->sample_period);
        }
-       __hrtimer_start_range_ns(&hwc->hrtimer,
-                               ns_to_ktime(period), 0,
-                               HRTIMER_MODE_REL_PINNED, 0);
+       hrtimer_start(&hwc->hrtimer, ns_to_ktime(period),
+                     HRTIMER_MODE_REL_PINNED);
 }
 
 static void perf_swevent_cancel_hrtimer(struct perf_event *event)
@@ -7166,6 +7175,8 @@ perf_event_mux_interval_ms_show(struct device *dev,
        return snprintf(page, PAGE_SIZE-1, "%d\n", pmu->hrtimer_interval_ms);
 }
 
+static DEFINE_MUTEX(mux_interval_mutex);
+
 static ssize_t
 perf_event_mux_interval_ms_store(struct device *dev,
                                 struct device_attribute *attr,
@@ -7185,17 +7196,21 @@ perf_event_mux_interval_ms_store(struct device *dev,
        if (timer == pmu->hrtimer_interval_ms)
                return count;
 
+       mutex_lock(&mux_interval_mutex);
        pmu->hrtimer_interval_ms = timer;
 
        /* update all cpuctx for this PMU */
-       for_each_possible_cpu(cpu) {
+       get_online_cpus();
+       for_each_online_cpu(cpu) {
                struct perf_cpu_context *cpuctx;
                cpuctx = per_cpu_ptr(pmu->pmu_cpu_context, cpu);
                cpuctx->hrtimer_interval = ns_to_ktime(NSEC_PER_MSEC * timer);
 
-               if (hrtimer_active(&cpuctx->hrtimer))
-                       hrtimer_forward_now(&cpuctx->hrtimer, cpuctx->hrtimer_interval);
+               cpu_function_call(cpu,
+                       (remote_function_f)perf_mux_hrtimer_restart, cpuctx);
        }
+       put_online_cpus();
+       mutex_unlock(&mux_interval_mutex);
 
        return count;
 }
@@ -7300,7 +7315,7 @@ skip_type:
                lockdep_set_class(&cpuctx->ctx.lock, &cpuctx_lock);
                cpuctx->ctx.pmu = pmu;
 
-               __perf_cpu_hrtimer_init(cpuctx, cpu);
+               __perf_mux_hrtimer_init(cpuctx, cpu);
 
                cpuctx->unique_pmu = pmu;
        }
index 9f6ce9ba4a04330d689345bc312fc765f0804a31..2deb24c7a40dd979313eb1fcff58044d2d948888 100644 (file)
@@ -72,15 +72,6 @@ static inline bool rb_has_aux(struct ring_buffer *rb)
 void perf_event_aux_event(struct perf_event *event, unsigned long head,
                          unsigned long size, u64 flags);
 
-extern void
-perf_event_header__init_id(struct perf_event_header *header,
-                          struct perf_sample_data *data,
-                          struct perf_event *event);
-extern void
-perf_event__output_id_sample(struct perf_event *event,
-                            struct perf_output_handle *handle,
-                            struct perf_sample_data *sample);
-
 extern struct page *
 perf_mmap_to_page(struct ring_buffer *rb, unsigned long pgoff);
 
index 232f00f273cbe419d2738d5f83465dd96529ee17..96472824a752f76fe651ec1bfb7ab7a52411a12c 100644 (file)
@@ -141,7 +141,7 @@ int perf_output_begin(struct perf_output_handle *handle,
        perf_output_get_handle(handle);
 
        do {
-               tail = ACCESS_ONCE(rb->user_page->data_tail);
+               tail = READ_ONCE_CTRL(rb->user_page->data_tail);
                offset = head = local_read(&rb->head);
                if (!rb->overwrite &&
                    unlikely(CIRC_SPACE(head, tail, perf_data_size(rb)) < size))
@@ -493,6 +493,20 @@ int rb_alloc_aux(struct ring_buffer *rb, struct perf_event *event,
                        rb->aux_pages[rb->aux_nr_pages] = page_address(page++);
        }
 
+       /*
+        * In overwrite mode, PMUs that don't support SG may not handle more
+        * than one contiguous allocation, since they rely on PMI to do double
+        * buffering. In this case, the entire buffer has to be one contiguous
+        * chunk.
+        */
+       if ((event->pmu->capabilities & PERF_PMU_CAP_AUX_NO_SG) &&
+           overwrite) {
+               struct page *page = virt_to_page(rb->aux_pages[0]);
+
+               if (page_private(page) != max_order)
+                       goto out;
+       }
+
        rb->aux_priv = event->pmu->setup_aux(event->cpu, rb->aux_pages, nr_pages,
                                             overwrite);
        if (!rb->aux_priv)
index a0cf6fa953de0f39d391a296ea089294e0cbe023..c4a182f5357eb4372685f9524cf18b0bedfaf7ca 100644 (file)
@@ -2071,7 +2071,7 @@ static void futex_wait_queue_me(struct futex_hash_bucket *hb, struct futex_q *q,
 {
        /*
         * The task state is guaranteed to be set before another task can
-        * wake it. set_current_state() is implemented using set_mb() and
+        * wake it. set_current_state() is implemented using smp_store_mb() and
         * queue_me() calls spin_unlock() upon completion, both serializing
         * access to the hash list and forcing another memory barrier.
         */
@@ -2079,11 +2079,8 @@ static void futex_wait_queue_me(struct futex_hash_bucket *hb, struct futex_q *q,
        queue_me(q, hb);
 
        /* Arm the timer */
-       if (timeout) {
+       if (timeout)
                hrtimer_start_expires(&timeout->timer, HRTIMER_MODE_ABS);
-               if (!hrtimer_active(&timeout->timer))
-                       timeout->task = NULL;
-       }
 
        /*
         * If we have been removed from the hash list, then another task
index eb9a4ea394ab33fdde25420f11cb9021df384824..27f4332c7f84ea8b3d7ec8bb3466a64f225a8487 100644 (file)
@@ -719,15 +719,9 @@ void handle_percpu_devid_irq(unsigned int irq, struct irq_desc *desc)
 }
 
 void
-__irq_set_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained,
-                 const char *name)
+__irq_do_set_handler(struct irq_desc *desc, irq_flow_handler_t handle,
+                    int is_chained, const char *name)
 {
-       unsigned long flags;
-       struct irq_desc *desc = irq_get_desc_buslock(irq, &flags, 0);
-
-       if (!desc)
-               return;
-
        if (!handle) {
                handle = handle_bad_irq;
        } else {
@@ -749,13 +743,13 @@ __irq_set_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained,
                         * right away.
                         */
                        if (WARN_ON(is_chained))
-                               goto out;
+                               return;
                        /* Try the parent */
                        irq_data = irq_data->parent_data;
                }
 #endif
                if (WARN_ON(!irq_data || irq_data->chip == &no_irq_chip))
-                       goto out;
+                       return;
        }
 
        /* Uninstall? */
@@ -774,11 +768,40 @@ __irq_set_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained,
                irq_settings_set_nothread(desc);
                irq_startup(desc, true);
        }
-out:
+}
+
+void
+__irq_set_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained,
+                 const char *name)
+{
+       unsigned long flags;
+       struct irq_desc *desc = irq_get_desc_buslock(irq, &flags, 0);
+
+       if (!desc)
+               return;
+
+       __irq_do_set_handler(desc, handle, is_chained, name);
        irq_put_desc_busunlock(desc, flags);
 }
 EXPORT_SYMBOL_GPL(__irq_set_handler);
 
+void
+irq_set_chained_handler_and_data(unsigned int irq, irq_flow_handler_t handle,
+                                void *data)
+{
+       unsigned long flags;
+       struct irq_desc *desc = irq_get_desc_buslock(irq, &flags, 0);
+
+       if (!desc)
+               return;
+
+       __irq_do_set_handler(desc, handle, 1, NULL);
+       desc->irq_data.handler_data = data;
+
+       irq_put_desc_busunlock(desc, flags);
+}
+EXPORT_SYMBOL_GPL(irq_set_chained_handler_and_data);
+
 void
 irq_set_chip_and_handler_name(unsigned int irq, struct irq_chip *chip,
                              irq_flow_handler_t handle, const char *name)
@@ -875,6 +898,34 @@ void irq_cpu_offline(void)
 }
 
 #ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
+/**
+ * irq_chip_enable_parent - Enable the parent interrupt (defaults to unmask if
+ * NULL)
+ * @data:      Pointer to interrupt specific data
+ */
+void irq_chip_enable_parent(struct irq_data *data)
+{
+       data = data->parent_data;
+       if (data->chip->irq_enable)
+               data->chip->irq_enable(data);
+       else
+               data->chip->irq_unmask(data);
+}
+
+/**
+ * irq_chip_disable_parent - Disable the parent interrupt (defaults to mask if
+ * NULL)
+ * @data:      Pointer to interrupt specific data
+ */
+void irq_chip_disable_parent(struct irq_data *data)
+{
+       data = data->parent_data;
+       if (data->chip->irq_disable)
+               data->chip->irq_disable(data);
+       else
+               data->chip->irq_mask(data);
+}
+
 /**
  * irq_chip_ack_parent - Acknowledge the parent interrupt
  * @data:      Pointer to interrupt specific data
@@ -949,6 +1000,20 @@ int irq_chip_retrigger_hierarchy(struct irq_data *data)
        return -ENOSYS;
 }
 
+/**
+ * irq_chip_set_vcpu_affinity_parent - Set vcpu affinity on the parent interrupt
+ * @data:      Pointer to interrupt specific data
+ * @dest:      The vcpu affinity information
+ */
+int irq_chip_set_vcpu_affinity_parent(struct irq_data *data, void *vcpu_info)
+{
+       data = data->parent_data;
+       if (data->chip->irq_set_vcpu_affinity)
+               return data->chip->irq_set_vcpu_affinity(data, vcpu_info);
+
+       return -ENOSYS;
+}
+
 /**
  * irq_chip_set_wake_parent - Set/reset wake-up on the parent interrupt
  * @data:      Pointer to interrupt specific data
index d5d0f7345c54504ac7e03f14394f282c86614a92..74d90a75426881881cc7e42ff3fcdba91efd76c9 100644 (file)
@@ -104,7 +104,7 @@ int devm_request_any_context_irq(struct device *dev, unsigned int irq,
                return -ENOMEM;
 
        rc = request_any_context_irq(irq, handler, irqflags, devname, dev_id);
-       if (rc) {
+       if (rc < 0) {
                devres_free(dr);
                return rc;
        }
@@ -113,7 +113,7 @@ int devm_request_any_context_irq(struct device *dev, unsigned int irq,
        dr->dev_id = dev_id;
        devres_add(dev, dr);
 
-       return 0;
+       return rc;
 }
 EXPORT_SYMBOL(devm_request_any_context_irq);
 
index 2feb6feca0cc96dff8514c45750542386db5ec53..326a67f2410bf95c8f4299ef03de214b8d529964 100644 (file)
@@ -42,6 +42,7 @@ struct irq_chip no_irq_chip = {
        .irq_enable     = noop,
        .irq_disable    = noop,
        .irq_ack        = ack_bad,
+       .flags          = IRQCHIP_SKIP_SET_WAKE,
 };
 
 /*
index 61024e8abdeffdeff717d389ddbe8fd99110532c..15b370daf23446d3a6b5213cbeac4d9559fb095d 100644 (file)
@@ -360,7 +360,7 @@ static struct lock_class_key irq_nested_lock_class;
 int irq_map_generic_chip(struct irq_domain *d, unsigned int virq,
                         irq_hw_number_t hw_irq)
 {
-       struct irq_data *data = irq_get_irq_data(virq);
+       struct irq_data *data = irq_domain_get_irq_data(d, virq);
        struct irq_domain_chip_generic *dgc = d->gc;
        struct irq_chip_generic *gc;
        struct irq_chip_type *ct;
@@ -405,8 +405,7 @@ int irq_map_generic_chip(struct irq_domain *d, unsigned int virq,
        else
                data->mask = 1 << idx;
 
-       irq_set_chip_and_handler(virq, chip, ct->handler);
-       irq_set_chip_data(virq, gc);
+       irq_domain_set_info(d, virq, hw_irq, chip, gc, ct->handler, NULL, NULL);
        irq_modify_status(virq, dgc->irq_flags_to_clear, dgc->irq_flags_to_set);
        return 0;
 }
index df553b0af936be2aa8f5ee5e1968da0c25e88384..4834ee828c41e8ee9dd8e8e4fb15e03e66c037e1 100644 (file)
@@ -59,8 +59,6 @@ enum {
 #include "debug.h"
 #include "settings.h"
 
-#define irq_data_to_desc(data) container_of(data, struct irq_desc, irq_data)
-
 extern int __irq_set_trigger(struct irq_desc *desc, unsigned int irq,
                unsigned long flags);
 extern void __disable_irq(struct irq_desc *desc, unsigned int irq);
@@ -170,27 +168,27 @@ irq_put_desc_unlock(struct irq_desc *desc, unsigned long flags)
  */
 static inline void irqd_set_move_pending(struct irq_data *d)
 {
-       d->state_use_accessors |= IRQD_SETAFFINITY_PENDING;
+       __irqd_to_state(d) |= IRQD_SETAFFINITY_PENDING;
 }
 
 static inline void irqd_clr_move_pending(struct irq_data *d)
 {
-       d->state_use_accessors &= ~IRQD_SETAFFINITY_PENDING;
+       __irqd_to_state(d) &= ~IRQD_SETAFFINITY_PENDING;
 }
 
 static inline void irqd_clear(struct irq_data *d, unsigned int mask)
 {
-       d->state_use_accessors &= ~mask;
+       __irqd_to_state(d) &= ~mask;
 }
 
 static inline void irqd_set(struct irq_data *d, unsigned int mask)
 {
-       d->state_use_accessors |= mask;
+       __irqd_to_state(d) |= mask;
 }
 
 static inline bool irqd_has_set(struct irq_data *d, unsigned int mask)
 {
-       return d->state_use_accessors & mask;
+       return __irqd_to_state(d) & mask;
 }
 
 static inline void kstat_incr_irqs_this_cpu(unsigned int irq, struct irq_desc *desc)
@@ -199,6 +197,11 @@ static inline void kstat_incr_irqs_this_cpu(unsigned int irq, struct irq_desc *d
        __this_cpu_inc(kstat.irqs_sum);
 }
 
+static inline int irq_desc_get_node(struct irq_desc *desc)
+{
+       return irq_data_get_node(&desc->irq_data);
+}
+
 #ifdef CONFIG_PM_SLEEP
 bool irq_pm_check_wakeup(struct irq_desc *desc);
 void irq_pm_install_action(struct irq_desc *desc, struct irqaction *action);
index 99793b9b6d23a5bfb4c1b4c2794134b06cdc5132..4afc457613ddf7da1bd845733e68ec22af099ec0 100644 (file)
@@ -59,16 +59,10 @@ static void desc_smp_init(struct irq_desc *desc, int node)
 #endif
 }
 
-static inline int desc_node(struct irq_desc *desc)
-{
-       return desc->irq_data.node;
-}
-
 #else
 static inline int
 alloc_masks(struct irq_desc *desc, gfp_t gfp, int node) { return 0; }
 static inline void desc_smp_init(struct irq_desc *desc, int node) { }
-static inline int desc_node(struct irq_desc *desc) { return 0; }
 #endif
 
 static void desc_set_defaults(unsigned int irq, struct irq_desc *desc, int node,
@@ -76,6 +70,7 @@ static void desc_set_defaults(unsigned int irq, struct irq_desc *desc, int node,
 {
        int cpu;
 
+       desc->irq_data.common = &desc->irq_common_data;
        desc->irq_data.irq = irq;
        desc->irq_data.chip = &no_irq_chip;
        desc->irq_data.chip_data = NULL;
@@ -299,7 +294,7 @@ static void free_desc(unsigned int irq)
        unsigned long flags;
 
        raw_spin_lock_irqsave(&desc->lock, flags);
-       desc_set_defaults(irq, desc, desc_node(desc), NULL);
+       desc_set_defaults(irq, desc, irq_desc_get_node(desc), NULL);
        raw_spin_unlock_irqrestore(&desc->lock, flags);
 }
 
@@ -619,7 +614,7 @@ unsigned int kstat_irqs(unsigned int irq)
 {
        struct irq_desc *desc = irq_to_desc(irq);
        int cpu;
-       int sum = 0;
+       unsigned int sum = 0;
 
        if (!desc || !desc->kstat_irqs)
                return 0;
@@ -639,7 +634,7 @@ unsigned int kstat_irqs(unsigned int irq)
  */
 unsigned int kstat_irqs_usr(unsigned int irq)
 {
-       int sum;
+       unsigned int sum;
 
        irq_lock_sparse();
        sum = kstat_irqs(irq);
index 7fac311057b806e7e6ba6c4634fac7151ace347b..8c3577fef78c4ee50cd6912d1cc87202e7d17cf2 100644 (file)
@@ -830,10 +830,12 @@ static struct irq_data *irq_domain_insert_irq_data(struct irq_domain *domain,
 {
        struct irq_data *irq_data;
 
-       irq_data = kzalloc_node(sizeof(*irq_data), GFP_KERNEL, child->node);
+       irq_data = kzalloc_node(sizeof(*irq_data), GFP_KERNEL,
+                               irq_data_get_node(child));
        if (irq_data) {
                child->parent_data = irq_data;
                irq_data->irq = child->irq;
+               irq_data->common = child->common;
                irq_data->node = child->node;
                irq_data->domain = domain;
        }
@@ -1232,6 +1234,27 @@ struct irq_data *irq_domain_get_irq_data(struct irq_domain *domain,
        return (irq_data && irq_data->domain == domain) ? irq_data : NULL;
 }
 
+/**
+ * irq_domain_set_info - Set the complete data for a @virq in @domain
+ * @domain:            Interrupt domain to match
+ * @virq:              IRQ number
+ * @hwirq:             The hardware interrupt number
+ * @chip:              The associated interrupt chip
+ * @chip_data:         The associated interrupt chip data
+ * @handler:           The interrupt flow handler
+ * @handler_data:      The interrupt flow handler data
+ * @handler_name:      The interrupt handler name
+ */
+void irq_domain_set_info(struct irq_domain *domain, unsigned int virq,
+                        irq_hw_number_t hwirq, struct irq_chip *chip,
+                        void *chip_data, irq_flow_handler_t handler,
+                        void *handler_data, const char *handler_name)
+{
+       irq_set_chip_and_handler_name(virq, chip, handler, handler_name);
+       irq_set_chip_data(virq, chip_data);
+       irq_set_handler_data(virq, handler_data);
+}
+
 static void irq_domain_check_hierarchy(struct irq_domain *domain)
 {
 }
index e68932bb308e96e1c6aa184dc7d9972fba8df06e..f9744853b656976bc4fdc49e006464a08a7d5395 100644 (file)
@@ -256,6 +256,37 @@ int irq_set_affinity_hint(unsigned int irq, const struct cpumask *m)
 }
 EXPORT_SYMBOL_GPL(irq_set_affinity_hint);
 
+/**
+ *     irq_set_vcpu_affinity - Set vcpu affinity for the interrupt
+ *     @irq: interrupt number to set affinity
+ *     @vcpu_info: vCPU specific data
+ *
+ *     This function uses the vCPU specific data to set the vCPU
+ *     affinity for an irq. The vCPU specific data is passed from
+ *     outside, such as KVM. One example code path is as below:
+ *     KVM -> IOMMU -> irq_set_vcpu_affinity().
+ */
+int irq_set_vcpu_affinity(unsigned int irq, void *vcpu_info)
+{
+       unsigned long flags;
+       struct irq_desc *desc = irq_get_desc_lock(irq, &flags, 0);
+       struct irq_data *data;
+       struct irq_chip *chip;
+       int ret = -ENOSYS;
+
+       if (!desc)
+               return -EINVAL;
+
+       data = irq_desc_get_irq_data(desc);
+       chip = irq_data_get_irq_chip(data);
+       if (chip && chip->irq_set_vcpu_affinity)
+               ret = chip->irq_set_vcpu_affinity(data, vcpu_info);
+       irq_put_desc_unlock(desc, flags);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(irq_set_vcpu_affinity);
+
 static void irq_affinity_notify(struct work_struct *work)
 {
        struct irq_affinity_notify *notify =
@@ -332,7 +363,7 @@ static int
 setup_affinity(unsigned int irq, struct irq_desc *desc, struct cpumask *mask)
 {
        struct cpumask *set = irq_default_affinity;
-       int node = desc->irq_data.node;
+       int node = irq_desc_get_node(desc);
 
        /* Excludes PER_CPU and NO_BALANCE interrupts */
        if (!irq_can_set_affinity(irq))
index ca3f4aaff707db1d2aa28bee7f388be67a3317b5..37ddb7bda6517ac7cc8df9d9c63e31513d55f73e 100644 (file)
@@ -7,21 +7,21 @@
 void irq_move_masked_irq(struct irq_data *idata)
 {
        struct irq_desc *desc = irq_data_to_desc(idata);
-       struct irq_chip *chip = idata->chip;
+       struct irq_chip *chip = desc->irq_data.chip;
 
        if (likely(!irqd_is_setaffinity_pending(&desc->irq_data)))
                return;
 
+       irqd_clr_move_pending(&desc->irq_data);
+
        /*
         * Paranoia: cpu-local interrupts shouldn't be calling in here anyway.
         */
-       if (!irqd_can_balance(&desc->irq_data)) {
+       if (irqd_is_per_cpu(&desc->irq_data)) {
                WARN_ON(1);
                return;
        }
 
-       irqd_clr_move_pending(&desc->irq_data);
-
        if (unlikely(cpumask_empty(desc->pending_mask)))
                return;
 
@@ -52,6 +52,13 @@ void irq_move_irq(struct irq_data *idata)
 {
        bool masked;
 
+       /*
+        * Get top level irq_data when CONFIG_IRQ_DOMAIN_HIERARCHY is enabled,
+        * and it should be optimized away when CONFIG_IRQ_DOMAIN_HIERARCHY is
+        * disabled. So we avoid an "#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY" here.
+        */
+       idata = irq_desc_get_irq_data(irq_data_to_desc(idata));
+
        if (likely(!irqd_is_setaffinity_pending(idata)))
                return;
 
index 474de5cb394d3c2007135c788a5c40c973e34c6a..7bf1f1bbb7fa5620a270ca1d42d9d54e54b61288 100644 (file)
@@ -124,7 +124,7 @@ static void msi_domain_free(struct irq_domain *domain, unsigned int virq,
        irq_domain_free_irqs_top(domain, virq, nr_irqs);
 }
 
-static struct irq_domain_ops msi_domain_ops = {
+static const struct irq_domain_ops msi_domain_ops = {
        .alloc          = msi_domain_alloc,
        .free           = msi_domain_free,
        .activate       = msi_domain_activate,
index 5204a6d1b9854feecfc2fff678e1d2b8eef33c42..d22786a6dbde92eec5cc6b17a198cf309328b27a 100644 (file)
@@ -123,6 +123,8 @@ void suspend_device_irqs(void)
                unsigned long flags;
                bool sync;
 
+               if (irq_settings_is_nested_thread(desc))
+                       continue;
                raw_spin_lock_irqsave(&desc->lock, flags);
                sync = suspend_device_irq(desc, irq);
                raw_spin_unlock_irqrestore(&desc->lock, flags);
@@ -163,6 +165,8 @@ static void resume_irqs(bool want_early)
 
                if (!is_early && want_early)
                        continue;
+               if (irq_settings_is_nested_thread(desc))
+                       continue;
 
                raw_spin_lock_irqsave(&desc->lock, flags);
                resume_irq(desc, irq);
index df2f4642d1e7bd3892bcc4e5ecc29153a7f132af..0e97c142ce402b0492c876b909c40ac10d40c779 100644 (file)
@@ -241,7 +241,7 @@ static int irq_node_proc_show(struct seq_file *m, void *v)
 {
        struct irq_desc *desc = irq_to_desc((long) m->private);
 
-       seq_printf(m, "%d\n", desc->irq_data.node);
+       seq_printf(m, "%d\n", irq_desc_get_node(desc));
        return 0;
 }
 
index 284e2691e38073d52b00d46010e654e6ff7de9bf..c40ebcca0495fc5fb003e44c4c6549bc39fcd08d 100644 (file)
@@ -128,7 +128,7 @@ static bool klp_is_patch_registered(struct klp_patch *patch)
 
 static bool klp_initialized(void)
 {
-       return klp_root_kobj;
+       return !!klp_root_kobj;
 }
 
 struct klp_find_arg {
@@ -179,7 +179,9 @@ static int klp_find_object_symbol(const char *objname, const char *name,
                .count = 0
        };
 
+       mutex_lock(&module_mutex);
        kallsyms_on_each_symbol(klp_find_callback, &args);
+       mutex_unlock(&module_mutex);
 
        if (args.count == 0)
                pr_err("symbol '%s' not found in symbol table\n", name);
@@ -219,13 +221,19 @@ static int klp_verify_vmlinux_symbol(const char *name, unsigned long addr)
                .name = name,
                .addr = addr,
        };
+       int ret;
 
-       if (kallsyms_on_each_symbol(klp_verify_callback, &args))
-               return 0;
+       mutex_lock(&module_mutex);
+       ret = kallsyms_on_each_symbol(klp_verify_callback, &args);
+       mutex_unlock(&module_mutex);
 
-       pr_err("symbol '%s' not found at specified address 0x%016lx, kernel mismatch?\n",
-               name, addr);
-       return -EINVAL;
+       if (!ret) {
+               pr_err("symbol '%s' not found at specified address 0x%016lx, kernel mismatch?\n",
+                       name, addr);
+               return -EINVAL;
+       }
+
+       return 0;
 }
 
 static int klp_find_verify_func_addr(struct klp_object *obj,
@@ -234,8 +242,9 @@ static int klp_find_verify_func_addr(struct klp_object *obj,
        int ret;
 
 #if defined(CONFIG_RANDOMIZE_BASE)
-       /* KASLR is enabled, disregard old_addr from user */
-       func->old_addr = 0;
+       /* If KASLR has been enabled, adjust old_addr accordingly */
+       if (kaslr_enabled() && func->old_addr)
+               func->old_addr += kaslr_offset();
 #endif
 
        if (!func->old_addr || klp_is_module(obj))
@@ -422,7 +431,7 @@ static void klp_disable_object(struct klp_object *obj)
 {
        struct klp_func *func;
 
-       for (func = obj->funcs; func->old_name; func++)
+       klp_for_each_func(obj, func)
                if (func->state == KLP_ENABLED)
                        klp_disable_func(func);
 
@@ -440,7 +449,7 @@ static int klp_enable_object(struct klp_object *obj)
        if (WARN_ON(!klp_is_object_loaded(obj)))
                return -EINVAL;
 
-       for (func = obj->funcs; func->old_name; func++) {
+       klp_for_each_func(obj, func) {
                ret = klp_enable_func(func);
                if (ret) {
                        klp_disable_object(obj);
@@ -463,7 +472,7 @@ static int __klp_disable_patch(struct klp_patch *patch)
 
        pr_notice("disabling patch '%s'\n", patch->mod->name);
 
-       for (obj = patch->objs; obj->funcs; obj++) {
+       klp_for_each_object(patch, obj) {
                if (obj->state == KLP_ENABLED)
                        klp_disable_object(obj);
        }
@@ -523,7 +532,7 @@ static int __klp_enable_patch(struct klp_patch *patch)
 
        pr_notice("enabling patch '%s'\n", patch->mod->name);
 
-       for (obj = patch->objs; obj->funcs; obj++) {
+       klp_for_each_object(patch, obj) {
                if (!klp_is_object_loaded(obj))
                        continue;
 
@@ -651,6 +660,15 @@ static struct kobj_type klp_ktype_patch = {
        .default_attrs = klp_patch_attrs,
 };
 
+static void klp_kobj_release_object(struct kobject *kobj)
+{
+}
+
+static struct kobj_type klp_ktype_object = {
+       .release = klp_kobj_release_object,
+       .sysfs_ops = &kobj_sysfs_ops,
+};
+
 static void klp_kobj_release_func(struct kobject *kobj)
 {
 }
@@ -680,7 +698,7 @@ static void klp_free_object_loaded(struct klp_object *obj)
 
        obj->mod = NULL;
 
-       for (func = obj->funcs; func->old_name; func++)
+       klp_for_each_func(obj, func)
                func->old_addr = 0;
 }
 
@@ -695,7 +713,7 @@ static void klp_free_objects_limited(struct klp_patch *patch,
 
        for (obj = patch->objs; obj->funcs && obj != limit; obj++) {
                klp_free_funcs_limited(obj, NULL);
-               kobject_put(obj->kobj);
+               kobject_put(&obj->kobj);
        }
 }
 
@@ -713,7 +731,7 @@ static int klp_init_func(struct klp_object *obj, struct klp_func *func)
        func->state = KLP_DISABLED;
 
        return kobject_init_and_add(&func->kobj, &klp_ktype_func,
-                                   obj->kobj, "%s", func->old_name);
+                                   &obj->kobj, "%s", func->old_name);
 }
 
 /* parts of the initialization that is done only when the object is loaded */
@@ -729,7 +747,7 @@ static int klp_init_object_loaded(struct klp_patch *patch,
                        return ret;
        }
 
-       for (func = obj->funcs; func->old_name; func++) {
+       klp_for_each_func(obj, func) {
                ret = klp_find_verify_func_addr(obj, func);
                if (ret)
                        return ret;
@@ -753,11 +771,12 @@ static int klp_init_object(struct klp_patch *patch, struct klp_object *obj)
        klp_find_object_module(obj);
 
        name = klp_is_module(obj) ? obj->name : "vmlinux";
-       obj->kobj = kobject_create_and_add(name, &patch->kobj);
-       if (!obj->kobj)
-               return -ENOMEM;
+       ret = kobject_init_and_add(&obj->kobj, &klp_ktype_object,
+                                  &patch->kobj, "%s", name);
+       if (ret)
+               return ret;
 
-       for (func = obj->funcs; func->old_name; func++) {
+       klp_for_each_func(obj, func) {
                ret = klp_init_func(obj, func);
                if (ret)
                        goto free;
@@ -773,7 +792,7 @@ static int klp_init_object(struct klp_patch *patch, struct klp_object *obj)
 
 free:
        klp_free_funcs_limited(obj, func);
-       kobject_put(obj->kobj);
+       kobject_put(&obj->kobj);
        return ret;
 }
 
@@ -794,7 +813,7 @@ static int klp_init_patch(struct klp_patch *patch)
        if (ret)
                goto unlock;
 
-       for (obj = patch->objs; obj->funcs; obj++) {
+       klp_for_each_object(patch, obj) {
                ret = klp_init_object(patch, obj);
                if (ret)
                        goto free;
@@ -883,7 +902,7 @@ int klp_register_patch(struct klp_patch *patch)
 }
 EXPORT_SYMBOL_GPL(klp_register_patch);
 
-static void klp_module_notify_coming(struct klp_patch *patch,
+static int klp_module_notify_coming(struct klp_patch *patch,
                                     struct klp_object *obj)
 {
        struct module *pmod = patch->mod;
@@ -891,22 +910,23 @@ static void klp_module_notify_coming(struct klp_patch *patch,
        int ret;
 
        ret = klp_init_object_loaded(patch, obj);
-       if (ret)
-               goto err;
+       if (ret) {
+               pr_warn("failed to initialize patch '%s' for module '%s' (%d)\n",
+                       pmod->name, mod->name, ret);
+               return ret;
+       }
 
        if (patch->state == KLP_DISABLED)
-               return;
+               return 0;
 
        pr_notice("applying patch '%s' to loading module '%s'\n",
                  pmod->name, mod->name);
 
        ret = klp_enable_object(obj);
-       if (!ret)
-               return;
-
-err:
-       pr_warn("failed to apply patch '%s' to module '%s' (%d)\n",
-               pmod->name, mod->name, ret);
+       if (ret)
+               pr_warn("failed to apply patch '%s' to module '%s' (%d)\n",
+                       pmod->name, mod->name, ret);
+       return ret;
 }
 
 static void klp_module_notify_going(struct klp_patch *patch,
@@ -930,6 +950,7 @@ disabled:
 static int klp_module_notify(struct notifier_block *nb, unsigned long action,
                             void *data)
 {
+       int ret;
        struct module *mod = data;
        struct klp_patch *patch;
        struct klp_object *obj;
@@ -949,13 +970,18 @@ static int klp_module_notify(struct notifier_block *nb, unsigned long action,
                mod->klp_alive = false;
 
        list_for_each_entry(patch, &klp_patches, list) {
-               for (obj = patch->objs; obj->funcs; obj++) {
+               klp_for_each_object(patch, obj) {
                        if (!klp_is_module(obj) || strcmp(obj->name, mod->name))
                                continue;
 
                        if (action == MODULE_STATE_COMING) {
                                obj->mod = mod;
-                               klp_module_notify_coming(patch, obj);
+                               ret = klp_module_notify_coming(patch, obj);
+                               if (ret) {
+                                       obj->mod = NULL;
+                                       pr_warn("patch '%s' is in an inconsistent state!\n",
+                                               patch->mod->name);
+                               }
                        } else /* MODULE_STATE_GOING */
                                klp_module_notify_going(patch, obj);
 
@@ -973,7 +999,7 @@ static struct notifier_block klp_module_nb = {
        .priority = INT_MIN+1, /* called late but before ftrace notifier */
 };
 
-static int klp_init(void)
+static int __init klp_init(void)
 {
        int ret;
 
index de7a416cca2a79e537ff9fdb489aef09b4c01662..7dd5c9918e4c243df3504e2f2d9fb600e881cfed 100644 (file)
@@ -17,6 +17,7 @@ obj-$(CONFIG_SMP) += spinlock.o
 obj-$(CONFIG_LOCK_SPIN_ON_OWNER) += osq_lock.o
 obj-$(CONFIG_SMP) += lglock.o
 obj-$(CONFIG_PROVE_LOCKING) += spinlock.o
+obj-$(CONFIG_QUEUED_SPINLOCKS) += qspinlock.o
 obj-$(CONFIG_RT_MUTEXES) += rtmutex.o
 obj-$(CONFIG_DEBUG_RT_MUTEXES) += rtmutex-debug.o
 obj-$(CONFIG_RT_MUTEX_TESTER) += rtmutex-tester.o
@@ -25,5 +26,5 @@ obj-$(CONFIG_DEBUG_SPINLOCK) += spinlock_debug.o
 obj-$(CONFIG_RWSEM_GENERIC_SPINLOCK) += rwsem-spinlock.o
 obj-$(CONFIG_RWSEM_XCHGADD_ALGORITHM) += rwsem-xadd.o
 obj-$(CONFIG_PERCPU_RWSEM) += percpu-rwsem.o
-obj-$(CONFIG_QUEUE_RWLOCK) += qrwlock.o
+obj-$(CONFIG_QUEUED_RWLOCKS) += qrwlock.o
 obj-$(CONFIG_LOCK_TORTURE_TEST) += locktorture.o
index 86ae2aebf00432f4d681a413febebff79406889d..951cfcd10b4a0dc98d81f59ec61667378922269e 100644 (file)
@@ -60,6 +60,28 @@ void lg_local_unlock_cpu(struct lglock *lg, int cpu)
 }
 EXPORT_SYMBOL(lg_local_unlock_cpu);
 
+void lg_double_lock(struct lglock *lg, int cpu1, int cpu2)
+{
+       BUG_ON(cpu1 == cpu2);
+
+       /* lock in cpu order, just like lg_global_lock */
+       if (cpu2 < cpu1)
+               swap(cpu1, cpu2);
+
+       preempt_disable();
+       lock_acquire_shared(&lg->lock_dep_map, 0, 0, NULL, _RET_IP_);
+       arch_spin_lock(per_cpu_ptr(lg->lock, cpu1));
+       arch_spin_lock(per_cpu_ptr(lg->lock, cpu2));
+}
+
+void lg_double_unlock(struct lglock *lg, int cpu1, int cpu2)
+{
+       lock_release(&lg->lock_dep_map, 1, _RET_IP_);
+       arch_spin_unlock(per_cpu_ptr(lg->lock, cpu1));
+       arch_spin_unlock(per_cpu_ptr(lg->lock, cpu2));
+       preempt_enable();
+}
+
 void lg_global_lock(struct lglock *lg)
 {
        int i;
index a0831e1b99f4aabd6c80ea68cedb6350a7a9affc..456614136f1a2caed847a9d9051754e11e1a4ed3 100644 (file)
@@ -3900,7 +3900,8 @@ static void zap_class(struct lock_class *class)
        list_del_rcu(&class->hash_entry);
        list_del_rcu(&class->lock_entry);
 
-       class->key = NULL;
+       RCU_INIT_POINTER(class->key, NULL);
+       RCU_INIT_POINTER(class->name, NULL);
 }
 
 static inline int within(const void *addr, void *start, unsigned long size)
@@ -4066,8 +4067,7 @@ void __init lockdep_info(void)
 
 #ifdef CONFIG_DEBUG_LOCKDEP
        if (lockdep_init_error) {
-               printk("WARNING: lockdep init error! lock-%s was acquired"
-                       "before lockdep_init\n", lock_init_error);
+               printk("WARNING: lockdep init error: lock '%s' was acquired before lockdep_init().\n", lock_init_error);
                printk("Call stack leading to lockdep invocation was:\n");
                print_stack_trace(&lockdep_init_trace, 0);
        }
index ef43ac4bafb59b83ab979a680d49d6077749f955..d83d798bef95a042e1060a35bf4b79e7c7a6c05c 100644 (file)
@@ -426,10 +426,12 @@ static void seq_lock_time(struct seq_file *m, struct lock_time *lt)
 
 static void seq_stats(struct seq_file *m, struct lock_stat_data *data)
 {
-       char name[39];
-       struct lock_class *class;
+       struct lockdep_subclass_key *ckey;
        struct lock_class_stats *stats;
+       struct lock_class *class;
+       const char *cname;
        int i, namelen;
+       char name[39];
 
        class = data->class;
        stats = &data->stats;
@@ -440,15 +442,25 @@ static void seq_stats(struct seq_file *m, struct lock_stat_data *data)
        if (class->subclass)
                namelen -= 2;
 
-       if (!class->name) {
+       rcu_read_lock_sched();
+       cname = rcu_dereference_sched(class->name);
+       ckey  = rcu_dereference_sched(class->key);
+
+       if (!cname && !ckey) {
+               rcu_read_unlock_sched();
+               return;
+
+       } else if (!cname) {
                char str[KSYM_NAME_LEN];
                const char *key_name;
 
-               key_name = __get_key_name(class->key, str);
+               key_name = __get_key_name(ckey, str);
                snprintf(name, namelen, "%s", key_name);
        } else {
-               snprintf(name, namelen, "%s", class->name);
+               snprintf(name, namelen, "%s", cname);
        }
+       rcu_read_unlock_sched();
+
        namelen = strlen(name);
        if (class->name_version > 1) {
                snprintf(name+namelen, 3, "#%d", class->name_version);
index ec8cce259779061dd863e6a48ff1dcfa1a7a131c..32244186f1f2ae0e7a6343ad084f416aa0cda055 100644 (file)
@@ -122,12 +122,12 @@ static int torture_lock_busted_write_lock(void)
 
 static void torture_lock_busted_write_delay(struct torture_random_state *trsp)
 {
-       const unsigned long longdelay_us = 100;
+       const unsigned long longdelay_ms = 100;
 
        /* We want a long delay occasionally to force massive contention.  */
        if (!(torture_random(trsp) %
-             (cxt.nrealwriters_stress * 2000 * longdelay_us)))
-               mdelay(longdelay_us);
+             (cxt.nrealwriters_stress * 2000 * longdelay_ms)))
+               mdelay(longdelay_ms);
 #ifdef CONFIG_PREEMPT
        if (!(torture_random(trsp) % (cxt.nrealwriters_stress * 20000)))
                preempt_schedule();  /* Allow test to be preempted. */
@@ -160,14 +160,14 @@ static int torture_spin_lock_write_lock(void) __acquires(torture_spinlock)
 static void torture_spin_lock_write_delay(struct torture_random_state *trsp)
 {
        const unsigned long shortdelay_us = 2;
-       const unsigned long longdelay_us = 100;
+       const unsigned long longdelay_ms = 100;
 
        /* We want a short delay mostly to emulate likely code, and
         * we want a long delay occasionally to force massive contention.
         */
        if (!(torture_random(trsp) %
-             (cxt.nrealwriters_stress * 2000 * longdelay_us)))
-               mdelay(longdelay_us);
+             (cxt.nrealwriters_stress * 2000 * longdelay_ms)))
+               mdelay(longdelay_ms);
        if (!(torture_random(trsp) %
              (cxt.nrealwriters_stress * 2 * shortdelay_us)))
                udelay(shortdelay_us);
@@ -309,7 +309,7 @@ static int torture_rwlock_read_lock_irq(void) __acquires(torture_rwlock)
 static void torture_rwlock_read_unlock_irq(void)
 __releases(torture_rwlock)
 {
-       write_unlock_irqrestore(&torture_rwlock, cxt.cur_ops->flags);
+       read_unlock_irqrestore(&torture_rwlock, cxt.cur_ops->flags);
 }
 
 static struct lock_torture_ops rw_lock_irq_ops = {
index 75e114bdf3f26f379c4382dce2bc5c128c06b868..fd91aaa4554c8be6cf5b981d7ac3022a8046e77e 100644 (file)
@@ -17,6 +17,7 @@
 struct mcs_spinlock {
        struct mcs_spinlock *next;
        int locked; /* 1 if lock acquired */
+       int count;  /* nesting count, see qspinlock.c */
 };
 
 #ifndef arch_mcs_spin_lock_contended
index f956ede7f90df0b31210df87843f1bafc2ddc7cb..6c5da483966bde7aea3c7e7d43a42c0b55349f65 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Queue read/write lock
+ * Queued read/write locks
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
 #include <linux/hardirq.h>
 #include <asm/qrwlock.h>
 
+/*
+ * This internal data structure is used for optimizing access to some of
+ * the subfields within the atomic_t cnts.
+ */
+struct __qrwlock {
+       union {
+               atomic_t cnts;
+               struct {
+#ifdef __LITTLE_ENDIAN
+                       u8 wmode;       /* Writer mode   */
+                       u8 rcnts[3];    /* Reader counts */
+#else
+                       u8 rcnts[3];    /* Reader counts */
+                       u8 wmode;       /* Writer mode   */
+#endif
+               };
+       };
+       arch_spinlock_t lock;
+};
+
 /**
  * rspin_until_writer_unlock - inc reader count & spin until writer is gone
  * @lock  : Pointer to queue rwlock structure
@@ -107,10 +127,10 @@ void queue_write_lock_slowpath(struct qrwlock *lock)
         * or wait for a previous writer to go away.
         */
        for (;;) {
-               cnts = atomic_read(&lock->cnts);
-               if (!(cnts & _QW_WMASK) &&
-                   (atomic_cmpxchg(&lock->cnts, cnts,
-                                   cnts | _QW_WAITING) == cnts))
+               struct __qrwlock *l = (struct __qrwlock *)lock;
+
+               if (!READ_ONCE(l->wmode) &&
+                  (cmpxchg(&l->wmode, 0, _QW_WAITING) == 0))
                        break;
 
                cpu_relax_lowlatency();
diff --git a/kernel/locking/qspinlock.c b/kernel/locking/qspinlock.c
new file mode 100644 (file)
index 0000000..38c4920
--- /dev/null
@@ -0,0 +1,473 @@
+/*
+ * Queued spinlock
+ *
+ * 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.
+ *
+ * (C) Copyright 2013-2015 Hewlett-Packard Development Company, L.P.
+ * (C) Copyright 2013-2014 Red Hat, Inc.
+ * (C) Copyright 2015 Intel Corp.
+ *
+ * Authors: Waiman Long <waiman.long@hp.com>
+ *          Peter Zijlstra <peterz@infradead.org>
+ */
+
+#ifndef _GEN_PV_LOCK_SLOWPATH
+
+#include <linux/smp.h>
+#include <linux/bug.h>
+#include <linux/cpumask.h>
+#include <linux/percpu.h>
+#include <linux/hardirq.h>
+#include <linux/mutex.h>
+#include <asm/byteorder.h>
+#include <asm/qspinlock.h>
+
+/*
+ * The basic principle of a queue-based spinlock can best be understood
+ * by studying a classic queue-based spinlock implementation called the
+ * MCS lock. The paper below provides a good description for this kind
+ * of lock.
+ *
+ * http://www.cise.ufl.edu/tr/DOC/REP-1992-71.pdf
+ *
+ * This queued spinlock implementation is based on the MCS lock, however to make
+ * it fit the 4 bytes we assume spinlock_t to be, and preserve its existing
+ * API, we must modify it somehow.
+ *
+ * In particular; where the traditional MCS lock consists of a tail pointer
+ * (8 bytes) and needs the next pointer (another 8 bytes) of its own node to
+ * unlock the next pending (next->locked), we compress both these: {tail,
+ * next->locked} into a single u32 value.
+ *
+ * Since a spinlock disables recursion of its own context and there is a limit
+ * to the contexts that can nest; namely: task, softirq, hardirq, nmi. As there
+ * are at most 4 nesting levels, it can be encoded by a 2-bit number. Now
+ * we can encode the tail by combining the 2-bit nesting level with the cpu
+ * number. With one byte for the lock value and 3 bytes for the tail, only a
+ * 32-bit word is now needed. Even though we only need 1 bit for the lock,
+ * we extend it to a full byte to achieve better performance for architectures
+ * that support atomic byte write.
+ *
+ * We also change the first spinner to spin on the lock bit instead of its
+ * node; whereby avoiding the need to carry a node from lock to unlock, and
+ * preserving existing lock API. This also makes the unlock code simpler and
+ * faster.
+ *
+ * N.B. The current implementation only supports architectures that allow
+ *      atomic operations on smaller 8-bit and 16-bit data types.
+ *
+ */
+
+#include "mcs_spinlock.h"
+
+#ifdef CONFIG_PARAVIRT_SPINLOCKS
+#define MAX_NODES      8
+#else
+#define MAX_NODES      4
+#endif
+
+/*
+ * Per-CPU queue node structures; we can never have more than 4 nested
+ * contexts: task, softirq, hardirq, nmi.
+ *
+ * Exactly fits one 64-byte cacheline on a 64-bit architecture.
+ *
+ * PV doubles the storage and uses the second cacheline for PV state.
+ */
+static DEFINE_PER_CPU_ALIGNED(struct mcs_spinlock, mcs_nodes[MAX_NODES]);
+
+/*
+ * We must be able to distinguish between no-tail and the tail at 0:0,
+ * therefore increment the cpu number by one.
+ */
+
+static inline u32 encode_tail(int cpu, int idx)
+{
+       u32 tail;
+
+#ifdef CONFIG_DEBUG_SPINLOCK
+       BUG_ON(idx > 3);
+#endif
+       tail  = (cpu + 1) << _Q_TAIL_CPU_OFFSET;
+       tail |= idx << _Q_TAIL_IDX_OFFSET; /* assume < 4 */
+
+       return tail;
+}
+
+static inline struct mcs_spinlock *decode_tail(u32 tail)
+{
+       int cpu = (tail >> _Q_TAIL_CPU_OFFSET) - 1;
+       int idx = (tail &  _Q_TAIL_IDX_MASK) >> _Q_TAIL_IDX_OFFSET;
+
+       return per_cpu_ptr(&mcs_nodes[idx], cpu);
+}
+
+#define _Q_LOCKED_PENDING_MASK (_Q_LOCKED_MASK | _Q_PENDING_MASK)
+
+/*
+ * By using the whole 2nd least significant byte for the pending bit, we
+ * can allow better optimization of the lock acquisition for the pending
+ * bit holder.
+ *
+ * This internal structure is also used by the set_locked function which
+ * is not restricted to _Q_PENDING_BITS == 8.
+ */
+struct __qspinlock {
+       union {
+               atomic_t val;
+#ifdef __LITTLE_ENDIAN
+               struct {
+                       u8      locked;
+                       u8      pending;
+               };
+               struct {
+                       u16     locked_pending;
+                       u16     tail;
+               };
+#else
+               struct {
+                       u16     tail;
+                       u16     locked_pending;
+               };
+               struct {
+                       u8      reserved[2];
+                       u8      pending;
+                       u8      locked;
+               };
+#endif
+       };
+};
+
+#if _Q_PENDING_BITS == 8
+/**
+ * clear_pending_set_locked - take ownership and clear the pending bit.
+ * @lock: Pointer to queued spinlock structure
+ *
+ * *,1,0 -> *,0,1
+ *
+ * Lock stealing is not allowed if this function is used.
+ */
+static __always_inline void clear_pending_set_locked(struct qspinlock *lock)
+{
+       struct __qspinlock *l = (void *)lock;
+
+       WRITE_ONCE(l->locked_pending, _Q_LOCKED_VAL);
+}
+
+/*
+ * xchg_tail - Put in the new queue tail code word & retrieve previous one
+ * @lock : Pointer to queued spinlock structure
+ * @tail : The new queue tail code word
+ * Return: The previous queue tail code word
+ *
+ * xchg(lock, tail)
+ *
+ * p,*,* -> n,*,* ; prev = xchg(lock, node)
+ */
+static __always_inline u32 xchg_tail(struct qspinlock *lock, u32 tail)
+{
+       struct __qspinlock *l = (void *)lock;
+
+       return (u32)xchg(&l->tail, tail >> _Q_TAIL_OFFSET) << _Q_TAIL_OFFSET;
+}
+
+#else /* _Q_PENDING_BITS == 8 */
+
+/**
+ * clear_pending_set_locked - take ownership and clear the pending bit.
+ * @lock: Pointer to queued spinlock structure
+ *
+ * *,1,0 -> *,0,1
+ */
+static __always_inline void clear_pending_set_locked(struct qspinlock *lock)
+{
+       atomic_add(-_Q_PENDING_VAL + _Q_LOCKED_VAL, &lock->val);
+}
+
+/**
+ * xchg_tail - Put in the new queue tail code word & retrieve previous one
+ * @lock : Pointer to queued spinlock structure
+ * @tail : The new queue tail code word
+ * Return: The previous queue tail code word
+ *
+ * xchg(lock, tail)
+ *
+ * p,*,* -> n,*,* ; prev = xchg(lock, node)
+ */
+static __always_inline u32 xchg_tail(struct qspinlock *lock, u32 tail)
+{
+       u32 old, new, val = atomic_read(&lock->val);
+
+       for (;;) {
+               new = (val & _Q_LOCKED_PENDING_MASK) | tail;
+               old = atomic_cmpxchg(&lock->val, val, new);
+               if (old == val)
+                       break;
+
+               val = old;
+       }
+       return old;
+}
+#endif /* _Q_PENDING_BITS == 8 */
+
+/**
+ * set_locked - Set the lock bit and own the lock
+ * @lock: Pointer to queued spinlock structure
+ *
+ * *,*,0 -> *,0,1
+ */
+static __always_inline void set_locked(struct qspinlock *lock)
+{
+       struct __qspinlock *l = (void *)lock;
+
+       WRITE_ONCE(l->locked, _Q_LOCKED_VAL);
+}
+
+
+/*
+ * Generate the native code for queued_spin_unlock_slowpath(); provide NOPs for
+ * all the PV callbacks.
+ */
+
+static __always_inline void __pv_init_node(struct mcs_spinlock *node) { }
+static __always_inline void __pv_wait_node(struct mcs_spinlock *node) { }
+static __always_inline void __pv_kick_node(struct mcs_spinlock *node) { }
+
+static __always_inline void __pv_wait_head(struct qspinlock *lock,
+                                          struct mcs_spinlock *node) { }
+
+#define pv_enabled()           false
+
+#define pv_init_node           __pv_init_node
+#define pv_wait_node           __pv_wait_node
+#define pv_kick_node           __pv_kick_node
+#define pv_wait_head           __pv_wait_head
+
+#ifdef CONFIG_PARAVIRT_SPINLOCKS
+#define queued_spin_lock_slowpath      native_queued_spin_lock_slowpath
+#endif
+
+#endif /* _GEN_PV_LOCK_SLOWPATH */
+
+/**
+ * queued_spin_lock_slowpath - acquire the queued spinlock
+ * @lock: Pointer to queued spinlock structure
+ * @val: Current value of the queued spinlock 32-bit word
+ *
+ * (queue tail, pending bit, lock value)
+ *
+ *              fast     :    slow                                  :    unlock
+ *                       :                                          :
+ * uncontended  (0,0,0) -:--> (0,0,1) ------------------------------:--> (*,*,0)
+ *                       :       | ^--------.------.             /  :
+ *                       :       v           \      \            |  :
+ * pending               :    (0,1,1) +--> (0,1,0)   \           |  :
+ *                       :       | ^--'              |           |  :
+ *                       :       v                   |           |  :
+ * uncontended           :    (n,x,y) +--> (n,0,0) --'           |  :
+ *   queue               :       | ^--'                          |  :
+ *                       :       v                               |  :
+ * contended             :    (*,x,y) +--> (*,0,0) ---> (*,0,1) -'  :
+ *   queue               :         ^--'                             :
+ */
+void queued_spin_lock_slowpath(struct qspinlock *lock, u32 val)
+{
+       struct mcs_spinlock *prev, *next, *node;
+       u32 new, old, tail;
+       int idx;
+
+       BUILD_BUG_ON(CONFIG_NR_CPUS >= (1U << _Q_TAIL_CPU_BITS));
+
+       if (pv_enabled())
+               goto queue;
+
+       if (virt_queued_spin_lock(lock))
+               return;
+
+       /*
+        * wait for in-progress pending->locked hand-overs
+        *
+        * 0,1,0 -> 0,0,1
+        */
+       if (val == _Q_PENDING_VAL) {
+               while ((val = atomic_read(&lock->val)) == _Q_PENDING_VAL)
+                       cpu_relax();
+       }
+
+       /*
+        * trylock || pending
+        *
+        * 0,0,0 -> 0,0,1 ; trylock
+        * 0,0,1 -> 0,1,1 ; pending
+        */
+       for (;;) {
+               /*
+                * If we observe any contention; queue.
+                */
+               if (val & ~_Q_LOCKED_MASK)
+                       goto queue;
+
+               new = _Q_LOCKED_VAL;
+               if (val == new)
+                       new |= _Q_PENDING_VAL;
+
+               old = atomic_cmpxchg(&lock->val, val, new);
+               if (old == val)
+                       break;
+
+               val = old;
+       }
+
+       /*
+        * we won the trylock
+        */
+       if (new == _Q_LOCKED_VAL)
+               return;
+
+       /*
+        * we're pending, wait for the owner to go away.
+        *
+        * *,1,1 -> *,1,0
+        *
+        * this wait loop must be a load-acquire such that we match the
+        * store-release that clears the locked bit and create lock
+        * sequentiality; this is because not all clear_pending_set_locked()
+        * implementations imply full barriers.
+        */
+       while ((val = smp_load_acquire(&lock->val.counter)) & _Q_LOCKED_MASK)
+               cpu_relax();
+
+       /*
+        * take ownership and clear the pending bit.
+        *
+        * *,1,0 -> *,0,1
+        */
+       clear_pending_set_locked(lock);
+       return;
+
+       /*
+        * End of pending bit optimistic spinning and beginning of MCS
+        * queuing.
+        */
+queue:
+       node = this_cpu_ptr(&mcs_nodes[0]);
+       idx = node->count++;
+       tail = encode_tail(smp_processor_id(), idx);
+
+       node += idx;
+       node->locked = 0;
+       node->next = NULL;
+       pv_init_node(node);
+
+       /*
+        * We touched a (possibly) cold cacheline in the per-cpu queue node;
+        * attempt the trylock once more in the hope someone let go while we
+        * weren't watching.
+        */
+       if (queued_spin_trylock(lock))
+               goto release;
+
+       /*
+        * We have already touched the queueing cacheline; don't bother with
+        * pending stuff.
+        *
+        * p,*,* -> n,*,*
+        */
+       old = xchg_tail(lock, tail);
+
+       /*
+        * if there was a previous node; link it and wait until reaching the
+        * head of the waitqueue.
+        */
+       if (old & _Q_TAIL_MASK) {
+               prev = decode_tail(old);
+               WRITE_ONCE(prev->next, node);
+
+               pv_wait_node(node);
+               arch_mcs_spin_lock_contended(&node->locked);
+       }
+
+       /*
+        * we're at the head of the waitqueue, wait for the owner & pending to
+        * go away.
+        *
+        * *,x,y -> *,0,0
+        *
+        * this wait loop must use a load-acquire such that we match the
+        * store-release that clears the locked bit and create lock
+        * sequentiality; this is because the set_locked() function below
+        * does not imply a full barrier.
+        *
+        */
+       pv_wait_head(lock, node);
+       while ((val = smp_load_acquire(&lock->val.counter)) & _Q_LOCKED_PENDING_MASK)
+               cpu_relax();
+
+       /*
+        * claim the lock:
+        *
+        * n,0,0 -> 0,0,1 : lock, uncontended
+        * *,0,0 -> *,0,1 : lock, contended
+        *
+        * If the queue head is the only one in the queue (lock value == tail),
+        * clear the tail code and grab the lock. Otherwise, we only need
+        * to grab the lock.
+        */
+       for (;;) {
+               if (val != tail) {
+                       set_locked(lock);
+                       break;
+               }
+               old = atomic_cmpxchg(&lock->val, val, _Q_LOCKED_VAL);
+               if (old == val)
+                       goto release;   /* No contention */
+
+               val = old;
+       }
+
+       /*
+        * contended path; wait for next, release.
+        */
+       while (!(next = READ_ONCE(node->next)))
+               cpu_relax();
+
+       arch_mcs_spin_unlock_contended(&next->locked);
+       pv_kick_node(next);
+
+release:
+       /*
+        * release the node
+        */
+       this_cpu_dec(mcs_nodes[0].count);
+}
+EXPORT_SYMBOL(queued_spin_lock_slowpath);
+
+/*
+ * Generate the paravirt code for queued_spin_unlock_slowpath().
+ */
+#if !defined(_GEN_PV_LOCK_SLOWPATH) && defined(CONFIG_PARAVIRT_SPINLOCKS)
+#define _GEN_PV_LOCK_SLOWPATH
+
+#undef  pv_enabled
+#define pv_enabled()   true
+
+#undef pv_init_node
+#undef pv_wait_node
+#undef pv_kick_node
+#undef pv_wait_head
+
+#undef  queued_spin_lock_slowpath
+#define queued_spin_lock_slowpath      __pv_queued_spin_lock_slowpath
+
+#include "qspinlock_paravirt.h"
+#include "qspinlock.c"
+
+#endif
diff --git a/kernel/locking/qspinlock_paravirt.h b/kernel/locking/qspinlock_paravirt.h
new file mode 100644 (file)
index 0000000..04ab181
--- /dev/null
@@ -0,0 +1,325 @@
+#ifndef _GEN_PV_LOCK_SLOWPATH
+#error "do not include this file"
+#endif
+
+#include <linux/hash.h>
+#include <linux/bootmem.h>
+
+/*
+ * Implement paravirt qspinlocks; the general idea is to halt the vcpus instead
+ * of spinning them.
+ *
+ * This relies on the architecture to provide two paravirt hypercalls:
+ *
+ *   pv_wait(u8 *ptr, u8 val) -- suspends the vcpu if *ptr == val
+ *   pv_kick(cpu)             -- wakes a suspended vcpu
+ *
+ * Using these we implement __pv_queued_spin_lock_slowpath() and
+ * __pv_queued_spin_unlock() to replace native_queued_spin_lock_slowpath() and
+ * native_queued_spin_unlock().
+ */
+
+#define _Q_SLOW_VAL    (3U << _Q_LOCKED_OFFSET)
+
+enum vcpu_state {
+       vcpu_running = 0,
+       vcpu_halted,
+};
+
+struct pv_node {
+       struct mcs_spinlock     mcs;
+       struct mcs_spinlock     __res[3];
+
+       int                     cpu;
+       u8                      state;
+};
+
+/*
+ * Lock and MCS node addresses hash table for fast lookup
+ *
+ * Hashing is done on a per-cacheline basis to minimize the need to access
+ * more than one cacheline.
+ *
+ * Dynamically allocate a hash table big enough to hold at least 4X the
+ * number of possible cpus in the system. Allocation is done on page
+ * granularity. So the minimum number of hash buckets should be at least
+ * 256 (64-bit) or 512 (32-bit) to fully utilize a 4k page.
+ *
+ * Since we should not be holding locks from NMI context (very rare indeed) the
+ * max load factor is 0.75, which is around the point where open addressing
+ * breaks down.
+ *
+ */
+struct pv_hash_entry {
+       struct qspinlock *lock;
+       struct pv_node   *node;
+};
+
+#define PV_HE_PER_LINE (SMP_CACHE_BYTES / sizeof(struct pv_hash_entry))
+#define PV_HE_MIN      (PAGE_SIZE / sizeof(struct pv_hash_entry))
+
+static struct pv_hash_entry *pv_lock_hash;
+static unsigned int pv_lock_hash_bits __read_mostly;
+
+/*
+ * Allocate memory for the PV qspinlock hash buckets
+ *
+ * This function should be called from the paravirt spinlock initialization
+ * routine.
+ */
+void __init __pv_init_lock_hash(void)
+{
+       int pv_hash_size = ALIGN(4 * num_possible_cpus(), PV_HE_PER_LINE);
+
+       if (pv_hash_size < PV_HE_MIN)
+               pv_hash_size = PV_HE_MIN;
+
+       /*
+        * Allocate space from bootmem which should be page-size aligned
+        * and hence cacheline aligned.
+        */
+       pv_lock_hash = alloc_large_system_hash("PV qspinlock",
+                                              sizeof(struct pv_hash_entry),
+                                              pv_hash_size, 0, HASH_EARLY,
+                                              &pv_lock_hash_bits, NULL,
+                                              pv_hash_size, pv_hash_size);
+}
+
+#define for_each_hash_entry(he, offset, hash)                                          \
+       for (hash &= ~(PV_HE_PER_LINE - 1), he = &pv_lock_hash[hash], offset = 0;       \
+            offset < (1 << pv_lock_hash_bits);                                         \
+            offset++, he = &pv_lock_hash[(hash + offset) & ((1 << pv_lock_hash_bits) - 1)])
+
+static struct qspinlock **pv_hash(struct qspinlock *lock, struct pv_node *node)
+{
+       unsigned long offset, hash = hash_ptr(lock, pv_lock_hash_bits);
+       struct pv_hash_entry *he;
+
+       for_each_hash_entry(he, offset, hash) {
+               if (!cmpxchg(&he->lock, NULL, lock)) {
+                       WRITE_ONCE(he->node, node);
+                       return &he->lock;
+               }
+       }
+       /*
+        * Hard assume there is a free entry for us.
+        *
+        * This is guaranteed by ensuring every blocked lock only ever consumes
+        * a single entry, and since we only have 4 nesting levels per CPU
+        * and allocated 4*nr_possible_cpus(), this must be so.
+        *
+        * The single entry is guaranteed by having the lock owner unhash
+        * before it releases.
+        */
+       BUG();
+}
+
+static struct pv_node *pv_unhash(struct qspinlock *lock)
+{
+       unsigned long offset, hash = hash_ptr(lock, pv_lock_hash_bits);
+       struct pv_hash_entry *he;
+       struct pv_node *node;
+
+       for_each_hash_entry(he, offset, hash) {
+               if (READ_ONCE(he->lock) == lock) {
+                       node = READ_ONCE(he->node);
+                       WRITE_ONCE(he->lock, NULL);
+                       return node;
+               }
+       }
+       /*
+        * Hard assume we'll find an entry.
+        *
+        * This guarantees a limited lookup time and is itself guaranteed by
+        * having the lock owner do the unhash -- IFF the unlock sees the
+        * SLOW flag, there MUST be a hash entry.
+        */
+       BUG();
+}
+
+/*
+ * Initialize the PV part of the mcs_spinlock node.
+ */
+static void pv_init_node(struct mcs_spinlock *node)
+{
+       struct pv_node *pn = (struct pv_node *)node;
+
+       BUILD_BUG_ON(sizeof(struct pv_node) > 5*sizeof(struct mcs_spinlock));
+
+       pn->cpu = smp_processor_id();
+       pn->state = vcpu_running;
+}
+
+/*
+ * Wait for node->locked to become true, halt the vcpu after a short spin.
+ * pv_kick_node() is used to wake the vcpu again.
+ */
+static void pv_wait_node(struct mcs_spinlock *node)
+{
+       struct pv_node *pn = (struct pv_node *)node;
+       int loop;
+
+       for (;;) {
+               for (loop = SPIN_THRESHOLD; loop; loop--) {
+                       if (READ_ONCE(node->locked))
+                               return;
+                       cpu_relax();
+               }
+
+               /*
+                * Order pn->state vs pn->locked thusly:
+                *
+                * [S] pn->state = vcpu_halted    [S] next->locked = 1
+                *     MB                             MB
+                * [L] pn->locked               [RmW] pn->state = vcpu_running
+                *
+                * Matches the xchg() from pv_kick_node().
+                */
+               smp_store_mb(pn->state, vcpu_halted);
+
+               if (!READ_ONCE(node->locked))
+                       pv_wait(&pn->state, vcpu_halted);
+
+               /*
+                * Reset the vCPU state to avoid unncessary CPU kicking
+                */
+               WRITE_ONCE(pn->state, vcpu_running);
+
+               /*
+                * If the locked flag is still not set after wakeup, it is a
+                * spurious wakeup and the vCPU should wait again. However,
+                * there is a pretty high overhead for CPU halting and kicking.
+                * So it is better to spin for a while in the hope that the
+                * MCS lock will be released soon.
+                */
+       }
+       /*
+        * By now our node->locked should be 1 and our caller will not actually
+        * spin-wait for it. We do however rely on our caller to do a
+        * load-acquire for us.
+        */
+}
+
+/*
+ * Called after setting next->locked = 1, used to wake those stuck in
+ * pv_wait_node().
+ */
+static void pv_kick_node(struct mcs_spinlock *node)
+{
+       struct pv_node *pn = (struct pv_node *)node;
+
+       /*
+        * Note that because node->locked is already set, this actual
+        * mcs_spinlock entry could be re-used already.
+        *
+        * This should be fine however, kicking people for no reason is
+        * harmless.
+        *
+        * See the comment in pv_wait_node().
+        */
+       if (xchg(&pn->state, vcpu_running) == vcpu_halted)
+               pv_kick(pn->cpu);
+}
+
+/*
+ * Wait for l->locked to become clear; halt the vcpu after a short spin.
+ * __pv_queued_spin_unlock() will wake us.
+ */
+static void pv_wait_head(struct qspinlock *lock, struct mcs_spinlock *node)
+{
+       struct pv_node *pn = (struct pv_node *)node;
+       struct __qspinlock *l = (void *)lock;
+       struct qspinlock **lp = NULL;
+       int loop;
+
+       for (;;) {
+               for (loop = SPIN_THRESHOLD; loop; loop--) {
+                       if (!READ_ONCE(l->locked))
+                               return;
+                       cpu_relax();
+               }
+
+               WRITE_ONCE(pn->state, vcpu_halted);
+               if (!lp) { /* ONCE */
+                       lp = pv_hash(lock, pn);
+                       /*
+                        * lp must be set before setting _Q_SLOW_VAL
+                        *
+                        * [S] lp = lock                [RmW] l = l->locked = 0
+                        *     MB                             MB
+                        * [S] l->locked = _Q_SLOW_VAL  [L]   lp
+                        *
+                        * Matches the cmpxchg() in __pv_queued_spin_unlock().
+                        */
+                       if (!cmpxchg(&l->locked, _Q_LOCKED_VAL, _Q_SLOW_VAL)) {
+                               /*
+                                * The lock is free and _Q_SLOW_VAL has never
+                                * been set. Therefore we need to unhash before
+                                * getting the lock.
+                                */
+                               WRITE_ONCE(*lp, NULL);
+                               return;
+                       }
+               }
+               pv_wait(&l->locked, _Q_SLOW_VAL);
+
+               /*
+                * The unlocker should have freed the lock before kicking the
+                * CPU. So if the lock is still not free, it is a spurious
+                * wakeup and so the vCPU should wait again after spinning for
+                * a while.
+                */
+       }
+
+       /*
+        * Lock is unlocked now; the caller will acquire it without waiting.
+        * As with pv_wait_node() we rely on the caller to do a load-acquire
+        * for us.
+        */
+}
+
+/*
+ * PV version of the unlock function to be used in stead of
+ * queued_spin_unlock().
+ */
+__visible void __pv_queued_spin_unlock(struct qspinlock *lock)
+{
+       struct __qspinlock *l = (void *)lock;
+       struct pv_node *node;
+
+       /*
+        * We must not unlock if SLOW, because in that case we must first
+        * unhash. Otherwise it would be possible to have multiple @lock
+        * entries, which would be BAD.
+        */
+       if (likely(cmpxchg(&l->locked, _Q_LOCKED_VAL, 0) == _Q_LOCKED_VAL))
+               return;
+
+       /*
+        * Since the above failed to release, this must be the SLOW path.
+        * Therefore start by looking up the blocked node and unhashing it.
+        */
+       node = pv_unhash(lock);
+
+       /*
+        * Now that we have a reference to the (likely) blocked pv_node,
+        * release the lock.
+        */
+       smp_store_release(&l->locked, 0);
+
+       /*
+        * At this point the memory pointed at by lock can be freed/reused,
+        * however we can still use the pv_node to kick the CPU.
+        */
+       if (READ_ONCE(node->state) == vcpu_halted)
+               pv_kick(node->cpu);
+}
+/*
+ * Include the architecture specific callee-save thunk of the
+ * __pv_queued_spin_unlock(). This thunk is put together with
+ * __pv_queued_spin_unlock() near the top of the file to make sure
+ * that the callee-save thunk and the real unlock function are close
+ * to each other sharing consecutive instruction cachelines.
+ */
+#include <asm/qspinlock_paravirt.h>
+
index 86d4853d7b405c2ed53fcbbe4d42a4b0167802ab..5674b073473c2f41a0c4fec8dede633bd75e402c 100644 (file)
@@ -70,10 +70,10 @@ static void fixup_rt_mutex_waiters(struct rt_mutex *lock)
 }
 
 /*
- * We can speed up the acquire/release, if the architecture
- * supports cmpxchg and if there's no debugging state to be set up
+ * We can speed up the acquire/release, if there's no debugging state to be
+ * set up.
  */
-#if defined(__HAVE_ARCH_CMPXCHG) && !defined(CONFIG_DEBUG_RT_MUTEXES)
+#ifndef CONFIG_DEBUG_RT_MUTEXES
 # define rt_mutex_cmpxchg(l,c,n)       (cmpxchg(&l->owner, c, n) == c)
 static inline void mark_rt_mutex_waiters(struct rt_mutex *lock)
 {
@@ -1176,11 +1176,8 @@ rt_mutex_slowlock(struct rt_mutex *lock, int state,
        set_current_state(state);
 
        /* Setup the timer, when timeout != NULL */
-       if (unlikely(timeout)) {
+       if (unlikely(timeout))
                hrtimer_start_expires(&timeout->timer, HRTIMER_MODE_ABS);
-               if (!hrtimer_active(&timeout->timer))
-                       timeout->task = NULL;
-       }
 
        ret = task_blocks_on_rt_mutex(lock, &waiter, current, chwalk);
 
@@ -1451,10 +1448,17 @@ EXPORT_SYMBOL_GPL(rt_mutex_timed_lock);
  *
  * @lock:      the rt_mutex to be locked
  *
+ * This function can only be called in thread context. It's safe to
+ * call it from atomic regions, but not from hard interrupt or soft
+ * interrupt context.
+ *
  * Returns 1 on success and 0 on contention
  */
 int __sched rt_mutex_trylock(struct rt_mutex *lock)
 {
+       if (WARN_ON(in_irq() || in_nmi() || in_serving_softirq()))
+               return 0;
+
        return rt_mutex_fasttrylock(lock, rt_mutex_slowtrylock);
 }
 EXPORT_SYMBOL_GPL(rt_mutex_trylock);
index 3417d0172a5d2e7cd69460ed4ef96c02f6c578d0..0f189714e457016ba7801c788a44673907c7746b 100644 (file)
@@ -409,11 +409,24 @@ done:
        return taken;
 }
 
+/*
+ * Return true if the rwsem has active spinner
+ */
+static inline bool rwsem_has_spinner(struct rw_semaphore *sem)
+{
+       return osq_is_locked(&sem->osq);
+}
+
 #else
 static bool rwsem_optimistic_spin(struct rw_semaphore *sem)
 {
        return false;
 }
+
+static inline bool rwsem_has_spinner(struct rw_semaphore *sem)
+{
+       return false;
+}
 #endif
 
 /*
@@ -496,7 +509,38 @@ struct rw_semaphore *rwsem_wake(struct rw_semaphore *sem)
 {
        unsigned long flags;
 
+       /*
+        * If a spinner is present, it is not necessary to do the wakeup.
+        * Try to do wakeup only if the trylock succeeds to minimize
+        * spinlock contention which may introduce too much delay in the
+        * unlock operation.
+        *
+        *    spinning writer           up_write/up_read caller
+        *    ---------------           -----------------------
+        * [S]   osq_unlock()           [L]   osq
+        *       MB                           RMB
+        * [RmW] rwsem_try_write_lock() [RmW] spin_trylock(wait_lock)
+        *
+        * Here, it is important to make sure that there won't be a missed
+        * wakeup while the rwsem is free and the only spinning writer goes
+        * to sleep without taking the rwsem. Even when the spinning writer
+        * is just going to break out of the waiting loop, it will still do
+        * a trylock in rwsem_down_write_failed() before sleeping. IOW, if
+        * rwsem_has_spinner() is true, it will guarantee at least one
+        * trylock attempt on the rwsem later on.
+        */
+       if (rwsem_has_spinner(sem)) {
+               /*
+                * The smp_rmb() here is to make sure that the spinner
+                * state is consulted before reading the wait_lock.
+                */
+               smp_rmb();
+               if (!raw_spin_trylock_irqsave(&sem->wait_lock, flags))
+                       return sem;
+               goto locked;
+       }
        raw_spin_lock_irqsave(&sem->wait_lock, flags);
+locked:
 
        /* do nothing if list empty */
        if (!list_empty(&sem->wait_list))
index 86e8157a450f51c1ac592d9c8d2fa4a09e47c21f..63d395b5df9305b0033e6e294e209bf101e0937b 100644 (file)
@@ -272,7 +272,7 @@ static inline void pm_print_times_init(void)
 {
        pm_print_times_enabled = !!initcall_debug;
 }
-#else /* !CONFIG_PP_SLEEP_DEBUG */
+#else /* !CONFIG_PM_SLEEP_DEBUG */
 static inline void pm_print_times_init(void) {}
 #endif /* CONFIG_PM_SLEEP_DEBUG */
 
index 8d7a1ef7275855089957f877d6743b653dd7f076..53266b729fd9c785cc1f4461e0bc2a7c82aeb5a9 100644 (file)
@@ -366,6 +366,8 @@ static int suspend_enter(suspend_state_t state, bool *wakeup)
                        trace_suspend_resume(TPS("machine_suspend"),
                                state, false);
                        events_check_enabled = false;
+               } else if (*wakeup) {
+                       error = -EBUSY;
                }
                syscore_resume();
        }
@@ -468,7 +470,7 @@ static int enter_state(suspend_state_t state)
        if (state == PM_SUSPEND_FREEZE) {
 #ifdef CONFIG_PM_DEBUG
                if (pm_test_level != TEST_NONE && pm_test_level <= TEST_CPUS) {
-                       pr_warning("PM: Unsupported test mode for freeze state,"
+                       pr_warning("PM: Unsupported test mode for suspend to idle,"
                                   "please choose none/freezer/devices/platform.\n");
                        return -EAGAIN;
                }
@@ -488,7 +490,7 @@ static int enter_state(suspend_state_t state)
        printk("done.\n");
        trace_suspend_resume(TPS("sync_filesystems"), 0, false);
 
-       pr_debug("PM: Preparing system for %s sleep\n", pm_states[state]);
+       pr_debug("PM: Preparing system for sleep (%s)\n", pm_states[state]);
        error = suspend_prepare(state);
        if (error)
                goto Unlock;
@@ -497,7 +499,7 @@ static int enter_state(suspend_state_t state)
                goto Finish;
 
        trace_suspend_resume(TPS("suspend_enter"), state, false);
-       pr_debug("PM: Entering %s sleep\n", pm_states[state]);
+       pr_debug("PM: Suspending system (%s)\n", pm_states[state]);
        pm_restrict_gfp_mask();
        error = suspend_devices_and_enter(state);
        pm_restore_gfp_mask();
index 8dbe27611ec399e42f8912d49708ff4e20bff73f..59e32684c23b58714ecb26215856f67866cfc58e 100644 (file)
@@ -241,6 +241,7 @@ rcu_torture_free(struct rcu_torture *p)
 struct rcu_torture_ops {
        int ttype;
        void (*init)(void);
+       void (*cleanup)(void);
        int (*readlock)(void);
        void (*read_delay)(struct torture_random_state *rrsp);
        void (*readunlock)(int idx);
@@ -477,10 +478,12 @@ static struct rcu_torture_ops rcu_busted_ops = {
  */
 
 DEFINE_STATIC_SRCU(srcu_ctl);
+static struct srcu_struct srcu_ctld;
+static struct srcu_struct *srcu_ctlp = &srcu_ctl;
 
-static int srcu_torture_read_lock(void) __acquires(&srcu_ctl)
+static int srcu_torture_read_lock(void) __acquires(srcu_ctlp)
 {
-       return srcu_read_lock(&srcu_ctl);
+       return srcu_read_lock(srcu_ctlp);
 }
 
 static void srcu_read_delay(struct torture_random_state *rrsp)
@@ -499,49 +502,49 @@ static void srcu_read_delay(struct torture_random_state *rrsp)
                rcu_read_delay(rrsp);
 }
 
-static void srcu_torture_read_unlock(int idx) __releases(&srcu_ctl)
+static void srcu_torture_read_unlock(int idx) __releases(srcu_ctlp)
 {
-       srcu_read_unlock(&srcu_ctl, idx);
+       srcu_read_unlock(srcu_ctlp, idx);
 }
 
 static unsigned long srcu_torture_completed(void)
 {
-       return srcu_batches_completed(&srcu_ctl);
+       return srcu_batches_completed(srcu_ctlp);
 }
 
 static void srcu_torture_deferred_free(struct rcu_torture *rp)
 {
-       call_srcu(&srcu_ctl, &rp->rtort_rcu, rcu_torture_cb);
+       call_srcu(srcu_ctlp, &rp->rtort_rcu, rcu_torture_cb);
 }
 
 static void srcu_torture_synchronize(void)
 {
-       synchronize_srcu(&srcu_ctl);
+       synchronize_srcu(srcu_ctlp);
 }
 
 static void srcu_torture_call(struct rcu_head *head,
                              void (*func)(struct rcu_head *head))
 {
-       call_srcu(&srcu_ctl, head, func);
+       call_srcu(srcu_ctlp, head, func);
 }
 
 static void srcu_torture_barrier(void)
 {
-       srcu_barrier(&srcu_ctl);
+       srcu_barrier(srcu_ctlp);
 }
 
 static void srcu_torture_stats(void)
 {
        int cpu;
-       int idx = srcu_ctl.completed & 0x1;
+       int idx = srcu_ctlp->completed & 0x1;
 
        pr_alert("%s%s per-CPU(idx=%d):",
                 torture_type, TORTURE_FLAG, idx);
        for_each_possible_cpu(cpu) {
                long c0, c1;
 
-               c0 = (long)per_cpu_ptr(srcu_ctl.per_cpu_ref, cpu)->c[!idx];
-               c1 = (long)per_cpu_ptr(srcu_ctl.per_cpu_ref, cpu)->c[idx];
+               c0 = (long)per_cpu_ptr(srcu_ctlp->per_cpu_ref, cpu)->c[!idx];
+               c1 = (long)per_cpu_ptr(srcu_ctlp->per_cpu_ref, cpu)->c[idx];
                pr_cont(" %d(%ld,%ld)", cpu, c0, c1);
        }
        pr_cont("\n");
@@ -549,7 +552,7 @@ static void srcu_torture_stats(void)
 
 static void srcu_torture_synchronize_expedited(void)
 {
-       synchronize_srcu_expedited(&srcu_ctl);
+       synchronize_srcu_expedited(srcu_ctlp);
 }
 
 static struct rcu_torture_ops srcu_ops = {
@@ -569,6 +572,38 @@ static struct rcu_torture_ops srcu_ops = {
        .name           = "srcu"
 };
 
+static void srcu_torture_init(void)
+{
+       rcu_sync_torture_init();
+       WARN_ON(init_srcu_struct(&srcu_ctld));
+       srcu_ctlp = &srcu_ctld;
+}
+
+static void srcu_torture_cleanup(void)
+{
+       cleanup_srcu_struct(&srcu_ctld);
+       srcu_ctlp = &srcu_ctl; /* In case of a later rcutorture run. */
+}
+
+/* As above, but dynamically allocated. */
+static struct rcu_torture_ops srcud_ops = {
+       .ttype          = SRCU_FLAVOR,
+       .init           = srcu_torture_init,
+       .cleanup        = srcu_torture_cleanup,
+       .readlock       = srcu_torture_read_lock,
+       .read_delay     = srcu_read_delay,
+       .readunlock     = srcu_torture_read_unlock,
+       .started        = NULL,
+       .completed      = srcu_torture_completed,
+       .deferred_free  = srcu_torture_deferred_free,
+       .sync           = srcu_torture_synchronize,
+       .exp_sync       = srcu_torture_synchronize_expedited,
+       .call           = srcu_torture_call,
+       .cb_barrier     = srcu_torture_barrier,
+       .stats          = srcu_torture_stats,
+       .name           = "srcud"
+};
+
 /*
  * Definitions for sched torture testing.
  */
@@ -672,8 +707,8 @@ static void rcu_torture_boost_cb(struct rcu_head *head)
        struct rcu_boost_inflight *rbip =
                container_of(head, struct rcu_boost_inflight, rcu);
 
-       smp_mb(); /* Ensure RCU-core accesses precede clearing ->inflight */
-       rbip->inflight = 0;
+       /* Ensure RCU-core accesses precede clearing ->inflight */
+       smp_store_release(&rbip->inflight, 0);
 }
 
 static int rcu_torture_boost(void *arg)
@@ -710,9 +745,9 @@ static int rcu_torture_boost(void *arg)
                call_rcu_time = jiffies;
                while (ULONG_CMP_LT(jiffies, endtime)) {
                        /* If we don't have a callback in flight, post one. */
-                       if (!rbi.inflight) {
-                               smp_mb(); /* RCU core before ->inflight = 1. */
-                               rbi.inflight = 1;
+                       if (!smp_load_acquire(&rbi.inflight)) {
+                               /* RCU core before ->inflight = 1. */
+                               smp_store_release(&rbi.inflight, 1);
                                call_rcu(&rbi.rcu, rcu_torture_boost_cb);
                                if (jiffies - call_rcu_time >
                                         test_boost_duration * HZ - HZ / 2) {
@@ -751,11 +786,10 @@ checkwait:        stutter_wait("rcu_torture_boost");
        } while (!torture_must_stop());
 
        /* Clean up and exit. */
-       while (!kthread_should_stop() || rbi.inflight) {
+       while (!kthread_should_stop() || smp_load_acquire(&rbi.inflight)) {
                torture_shutdown_absorb("rcu_torture_boost");
                schedule_timeout_uninterruptible(1);
        }
-       smp_mb(); /* order accesses to ->inflight before stack-frame death. */
        destroy_rcu_head_on_stack(&rbi.rcu);
        torture_kthread_stopping("rcu_torture_boost");
        return 0;
@@ -1054,7 +1088,7 @@ static void rcu_torture_timer(unsigned long unused)
        p = rcu_dereference_check(rcu_torture_current,
                                  rcu_read_lock_bh_held() ||
                                  rcu_read_lock_sched_held() ||
-                                 srcu_read_lock_held(&srcu_ctl));
+                                 srcu_read_lock_held(srcu_ctlp));
        if (p == NULL) {
                /* Leave because rcu_torture_writer is not yet underway */
                cur_ops->readunlock(idx);
@@ -1128,7 +1162,7 @@ rcu_torture_reader(void *arg)
                p = rcu_dereference_check(rcu_torture_current,
                                          rcu_read_lock_bh_held() ||
                                          rcu_read_lock_sched_held() ||
-                                         srcu_read_lock_held(&srcu_ctl));
+                                         srcu_read_lock_held(srcu_ctlp));
                if (p == NULL) {
                        /* Wait for rcu_torture_writer to get underway */
                        cur_ops->readunlock(idx);
@@ -1413,12 +1447,15 @@ static int rcu_torture_barrier_cbs(void *arg)
        do {
                wait_event(barrier_cbs_wq[myid],
                           (newphase =
-                           ACCESS_ONCE(barrier_phase)) != lastphase ||
+                           smp_load_acquire(&barrier_phase)) != lastphase ||
                           torture_must_stop());
                lastphase = newphase;
-               smp_mb(); /* ensure barrier_phase load before ->call(). */
                if (torture_must_stop())
                        break;
+               /*
+                * The above smp_load_acquire() ensures barrier_phase load
+                * is ordered before the folloiwng ->call().
+                */
                cur_ops->call(&rcu, rcu_torture_barrier_cbf);
                if (atomic_dec_and_test(&barrier_cbs_count))
                        wake_up(&barrier_wq);
@@ -1439,8 +1476,8 @@ static int rcu_torture_barrier(void *arg)
        do {
                atomic_set(&barrier_cbs_invoked, 0);
                atomic_set(&barrier_cbs_count, n_barrier_cbs);
-               smp_mb(); /* Ensure barrier_phase after prior assignments. */
-               barrier_phase = !barrier_phase;
+               /* Ensure barrier_phase ordered after prior assignments. */
+               smp_store_release(&barrier_phase, !barrier_phase);
                for (i = 0; i < n_barrier_cbs; i++)
                        wake_up(&barrier_cbs_wq[i]);
                wait_event(barrier_wq,
@@ -1588,10 +1625,14 @@ rcu_torture_cleanup(void)
                        rcutorture_booster_cleanup(i);
        }
 
-       /* Wait for all RCU callbacks to fire.  */
-
+       /*
+        * Wait for all RCU callbacks to fire, then do flavor-specific
+        * cleanup operations.
+        */
        if (cur_ops->cb_barrier != NULL)
                cur_ops->cb_barrier();
+       if (cur_ops->cleanup != NULL)
+               cur_ops->cleanup();
 
        rcu_torture_stats_print();  /* -After- the stats thread is stopped! */
 
@@ -1668,8 +1709,8 @@ rcu_torture_init(void)
        int cpu;
        int firsterr = 0;
        static struct rcu_torture_ops *torture_ops[] = {
-               &rcu_ops, &rcu_bh_ops, &rcu_busted_ops, &srcu_ops, &sched_ops,
-               RCUTORTURE_TASKS_OPS
+               &rcu_ops, &rcu_bh_ops, &rcu_busted_ops, &srcu_ops, &srcud_ops,
+               &sched_ops, RCUTORTURE_TASKS_OPS
        };
 
        if (!torture_init_begin(torture_type, verbose, &torture_runnable))
@@ -1701,7 +1742,7 @@ rcu_torture_init(void)
        if (nreaders >= 0) {
                nrealreaders = nreaders;
        } else {
-               nrealreaders = num_online_cpus() - 1;
+               nrealreaders = num_online_cpus() - 2 - nreaders;
                if (nrealreaders <= 0)
                        nrealreaders = 1;
        }
index cad76e76b4e7def42de7c379882c878084c5394c..fb33d35ee0b7c0ecdb6df0cb0562050abcc6707b 100644 (file)
@@ -151,7 +151,7 @@ static unsigned long srcu_readers_seq_idx(struct srcu_struct *sp, int idx)
        unsigned long t;
 
        for_each_possible_cpu(cpu) {
-               t = ACCESS_ONCE(per_cpu_ptr(sp->per_cpu_ref, cpu)->seq[idx]);
+               t = READ_ONCE(per_cpu_ptr(sp->per_cpu_ref, cpu)->seq[idx]);
                sum += t;
        }
        return sum;
@@ -168,7 +168,7 @@ static unsigned long srcu_readers_active_idx(struct srcu_struct *sp, int idx)
        unsigned long t;
 
        for_each_possible_cpu(cpu) {
-               t = ACCESS_ONCE(per_cpu_ptr(sp->per_cpu_ref, cpu)->c[idx]);
+               t = READ_ONCE(per_cpu_ptr(sp->per_cpu_ref, cpu)->c[idx]);
                sum += t;
        }
        return sum;
@@ -265,8 +265,8 @@ static int srcu_readers_active(struct srcu_struct *sp)
        unsigned long sum = 0;
 
        for_each_possible_cpu(cpu) {
-               sum += ACCESS_ONCE(per_cpu_ptr(sp->per_cpu_ref, cpu)->c[0]);
-               sum += ACCESS_ONCE(per_cpu_ptr(sp->per_cpu_ref, cpu)->c[1]);
+               sum += READ_ONCE(per_cpu_ptr(sp->per_cpu_ref, cpu)->c[0]);
+               sum += READ_ONCE(per_cpu_ptr(sp->per_cpu_ref, cpu)->c[1]);
        }
        return sum;
 }
@@ -296,7 +296,7 @@ int __srcu_read_lock(struct srcu_struct *sp)
 {
        int idx;
 
-       idx = ACCESS_ONCE(sp->completed) & 0x1;
+       idx = READ_ONCE(sp->completed) & 0x1;
        preempt_disable();
        __this_cpu_inc(sp->per_cpu_ref->c[idx]);
        smp_mb(); /* B */  /* Avoid leaking the critical section. */
index 069742d61c68873a3fce7bf5d7df2e77d5a59958..591af0cb7b9f4e7a25abbab58f3a7ab1fe3cb4fb 100644 (file)
@@ -49,39 +49,6 @@ static void __call_rcu(struct rcu_head *head,
 
 #include "tiny_plugin.h"
 
-/*
- * Enter idle, which is an extended quiescent state if we have fully
- * entered that mode.
- */
-void rcu_idle_enter(void)
-{
-}
-EXPORT_SYMBOL_GPL(rcu_idle_enter);
-
-/*
- * Exit an interrupt handler towards idle.
- */
-void rcu_irq_exit(void)
-{
-}
-EXPORT_SYMBOL_GPL(rcu_irq_exit);
-
-/*
- * Exit idle, so that we are no longer in an extended quiescent state.
- */
-void rcu_idle_exit(void)
-{
-}
-EXPORT_SYMBOL_GPL(rcu_idle_exit);
-
-/*
- * Enter an interrupt handler, moving away from idle.
- */
-void rcu_irq_enter(void)
-{
-}
-EXPORT_SYMBOL_GPL(rcu_irq_enter);
-
 #if defined(CONFIG_DEBUG_LOCK_ALLOC) || defined(CONFIG_RCU_TRACE)
 
 /*
@@ -170,6 +137,11 @@ static void __rcu_process_callbacks(struct rcu_ctrlblk *rcp)
 
        /* Move the ready-to-invoke callbacks to a local list. */
        local_irq_save(flags);
+       if (rcp->donetail == &rcp->rcucblist) {
+               /* No callbacks ready, so just leave. */
+               local_irq_restore(flags);
+               return;
+       }
        RCU_TRACE(trace_rcu_batch_start(rcp->name, 0, rcp->qlen, -1));
        list = rcp->rcucblist;
        rcp->rcucblist = *rcp->donetail;
index f94e209a10d615a5a4f7d379e623918f533ce814..e492a5253e0f10c94da7056efd8f42ba2c1a394c 100644 (file)
@@ -144,16 +144,17 @@ static void check_cpu_stall(struct rcu_ctrlblk *rcp)
                return;
        rcp->ticks_this_gp++;
        j = jiffies;
-       js = ACCESS_ONCE(rcp->jiffies_stall);
+       js = READ_ONCE(rcp->jiffies_stall);
        if (rcp->rcucblist && ULONG_CMP_GE(j, js)) {
                pr_err("INFO: %s stall on CPU (%lu ticks this GP) idle=%llx (t=%lu jiffies q=%ld)\n",
                       rcp->name, rcp->ticks_this_gp, DYNTICK_TASK_EXIT_IDLE,
                       jiffies - rcp->gp_start, rcp->qlen);
                dump_stack();
-               ACCESS_ONCE(rcp->jiffies_stall) = jiffies +
-                       3 * rcu_jiffies_till_stall_check() + 3;
+               WRITE_ONCE(rcp->jiffies_stall,
+                          jiffies + 3 * rcu_jiffies_till_stall_check() + 3);
        } else if (ULONG_CMP_GE(j, js)) {
-               ACCESS_ONCE(rcp->jiffies_stall) = jiffies + rcu_jiffies_till_stall_check();
+               WRITE_ONCE(rcp->jiffies_stall,
+                          jiffies + rcu_jiffies_till_stall_check());
        }
 }
 
@@ -161,7 +162,8 @@ static void reset_cpu_stall_ticks(struct rcu_ctrlblk *rcp)
 {
        rcp->ticks_this_gp = 0;
        rcp->gp_start = jiffies;
-       ACCESS_ONCE(rcp->jiffies_stall) = jiffies + rcu_jiffies_till_stall_check();
+       WRITE_ONCE(rcp->jiffies_stall,
+                  jiffies + rcu_jiffies_till_stall_check());
 }
 
 static void check_cpu_stalls(void)
index 8cf7304b2867f5a113807afb0bd5dc0a3bd3cfc0..add042926a6608258564c72ea5a33dd204428df6 100644 (file)
@@ -91,7 +91,7 @@ static const char *tp_##sname##_varname __used __tracepoint_string = sname##_var
 
 #define RCU_STATE_INITIALIZER(sname, sabbr, cr) \
 DEFINE_RCU_TPS(sname) \
-DEFINE_PER_CPU_SHARED_ALIGNED(struct rcu_data, sname##_data); \
+static DEFINE_PER_CPU_SHARED_ALIGNED(struct rcu_data, sname##_data); \
 struct rcu_state sname##_state = { \
        .level = { &sname##_state.node[0] }, \
        .rda = &sname##_data, \
@@ -110,11 +110,18 @@ struct rcu_state sname##_state = { \
 RCU_STATE_INITIALIZER(rcu_sched, 's', call_rcu_sched);
 RCU_STATE_INITIALIZER(rcu_bh, 'b', call_rcu_bh);
 
-static struct rcu_state *rcu_state_p;
+static struct rcu_state *const rcu_state_p;
+static struct rcu_data __percpu *const rcu_data_p;
 LIST_HEAD(rcu_struct_flavors);
 
-/* Increase (but not decrease) the CONFIG_RCU_FANOUT_LEAF at boot time. */
-static int rcu_fanout_leaf = CONFIG_RCU_FANOUT_LEAF;
+/* Dump rcu_node combining tree at boot to verify correct setup. */
+static bool dump_tree;
+module_param(dump_tree, bool, 0444);
+/* Control rcu_node-tree auto-balancing at boot time. */
+static bool rcu_fanout_exact;
+module_param(rcu_fanout_exact, bool, 0444);
+/* Increase (but not decrease) the RCU_FANOUT_LEAF at boot time. */
+static int rcu_fanout_leaf = RCU_FANOUT_LEAF;
 module_param(rcu_fanout_leaf, int, 0444);
 int rcu_num_lvls __read_mostly = RCU_NUM_LVLS;
 static int num_rcu_lvl[] = {  /* Number of rcu_nodes at specified level. */
@@ -159,17 +166,46 @@ static void invoke_rcu_core(void);
 static void invoke_rcu_callbacks(struct rcu_state *rsp, struct rcu_data *rdp);
 
 /* rcuc/rcub kthread realtime priority */
+#ifdef CONFIG_RCU_KTHREAD_PRIO
 static int kthread_prio = CONFIG_RCU_KTHREAD_PRIO;
+#else /* #ifdef CONFIG_RCU_KTHREAD_PRIO */
+static int kthread_prio = IS_ENABLED(CONFIG_RCU_BOOST) ? 1 : 0;
+#endif /* #else #ifdef CONFIG_RCU_KTHREAD_PRIO */
 module_param(kthread_prio, int, 0644);
 
 /* Delay in jiffies for grace-period initialization delays, debug only. */
+
+#ifdef CONFIG_RCU_TORTURE_TEST_SLOW_PREINIT
+static int gp_preinit_delay = CONFIG_RCU_TORTURE_TEST_SLOW_PREINIT_DELAY;
+module_param(gp_preinit_delay, int, 0644);
+#else /* #ifdef CONFIG_RCU_TORTURE_TEST_SLOW_PREINIT */
+static const int gp_preinit_delay;
+#endif /* #else #ifdef CONFIG_RCU_TORTURE_TEST_SLOW_PREINIT */
+
 #ifdef CONFIG_RCU_TORTURE_TEST_SLOW_INIT
 static int gp_init_delay = CONFIG_RCU_TORTURE_TEST_SLOW_INIT_DELAY;
 module_param(gp_init_delay, int, 0644);
 #else /* #ifdef CONFIG_RCU_TORTURE_TEST_SLOW_INIT */
 static const int gp_init_delay;
 #endif /* #else #ifdef CONFIG_RCU_TORTURE_TEST_SLOW_INIT */
-#define PER_RCU_NODE_PERIOD 10 /* Number of grace periods between delays. */
+
+#ifdef CONFIG_RCU_TORTURE_TEST_SLOW_CLEANUP
+static int gp_cleanup_delay = CONFIG_RCU_TORTURE_TEST_SLOW_CLEANUP_DELAY;
+module_param(gp_cleanup_delay, int, 0644);
+#else /* #ifdef CONFIG_RCU_TORTURE_TEST_SLOW_CLEANUP */
+static const int gp_cleanup_delay;
+#endif /* #else #ifdef CONFIG_RCU_TORTURE_TEST_SLOW_CLEANUP */
+
+/*
+ * Number of grace periods between delays, normalized by the duration of
+ * the delay.  The longer the the delay, the more the grace periods between
+ * each delay.  The reason for this normalization is that it means that,
+ * for non-zero delays, the overall slowdown of grace periods is constant
+ * regardless of the duration of the delay.  This arrangement balances
+ * the need for long delays to increase some race probabilities with the
+ * need for fast grace periods to increase other race probabilities.
+ */
+#define PER_RCU_NODE_PERIOD 3  /* Number of grace periods between delays. */
 
 /*
  * Track the rcutorture test sequence number and the update version
@@ -191,17 +227,17 @@ unsigned long rcutorture_vernum;
  */
 unsigned long rcu_rnp_online_cpus(struct rcu_node *rnp)
 {
-       return ACCESS_ONCE(rnp->qsmaskinitnext);
+       return READ_ONCE(rnp->qsmaskinitnext);
 }
 
 /*
- * Return true if an RCU grace period is in progress.  The ACCESS_ONCE()s
+ * Return true if an RCU grace period is in progress.  The READ_ONCE()s
  * permit this function to be invoked without holding the root rcu_node
  * structure's ->lock, but of course results can be subject to change.
  */
 static int rcu_gp_in_progress(struct rcu_state *rsp)
 {
-       return ACCESS_ONCE(rsp->completed) != ACCESS_ONCE(rsp->gpnum);
+       return READ_ONCE(rsp->completed) != READ_ONCE(rsp->gpnum);
 }
 
 /*
@@ -278,8 +314,8 @@ static void rcu_momentary_dyntick_idle(void)
                if (!(resched_mask & rsp->flavor_mask))
                        continue;
                smp_mb(); /* rcu_sched_qs_mask before cond_resched_completed. */
-               if (ACCESS_ONCE(rdp->mynode->completed) !=
-                   ACCESS_ONCE(rdp->cond_resched_completed))
+               if (READ_ONCE(rdp->mynode->completed) !=
+                   READ_ONCE(rdp->cond_resched_completed))
                        continue;
 
                /*
@@ -491,9 +527,9 @@ void rcutorture_get_gp_data(enum rcutorture_type test_type, int *flags,
                break;
        }
        if (rsp != NULL) {
-               *flags = ACCESS_ONCE(rsp->gp_flags);
-               *gpnum = ACCESS_ONCE(rsp->gpnum);
-               *completed = ACCESS_ONCE(rsp->completed);
+               *flags = READ_ONCE(rsp->gp_flags);
+               *gpnum = READ_ONCE(rsp->gpnum);
+               *completed = READ_ONCE(rsp->completed);
                return;
        }
        *flags = 0;
@@ -539,10 +575,10 @@ static struct rcu_node *rcu_get_root(struct rcu_state *rsp)
 static int rcu_future_needs_gp(struct rcu_state *rsp)
 {
        struct rcu_node *rnp = rcu_get_root(rsp);
-       int idx = (ACCESS_ONCE(rnp->completed) + 1) & 0x1;
+       int idx = (READ_ONCE(rnp->completed) + 1) & 0x1;
        int *fp = &rnp->need_future_gp[idx];
 
-       return ACCESS_ONCE(*fp);
+       return READ_ONCE(*fp);
 }
 
 /*
@@ -565,7 +601,7 @@ cpu_needs_another_gp(struct rcu_state *rsp, struct rcu_data *rdp)
                return 1;  /* Yes, this CPU has newly registered callbacks. */
        for (i = RCU_WAIT_TAIL; i < RCU_NEXT_TAIL; i++)
                if (rdp->nxttail[i - 1] != rdp->nxttail[i] &&
-                   ULONG_CMP_LT(ACCESS_ONCE(rsp->completed),
+                   ULONG_CMP_LT(READ_ONCE(rsp->completed),
                                 rdp->nxtcompleted[i]))
                        return 1;  /* Yes, CBs for future grace period. */
        return 0; /* No grace period needed. */
@@ -585,7 +621,8 @@ static void rcu_eqs_enter_common(long long oldval, bool user)
        struct rcu_dynticks *rdtp = this_cpu_ptr(&rcu_dynticks);
 
        trace_rcu_dyntick(TPS("Start"), oldval, rdtp->dynticks_nesting);
-       if (!user && !is_idle_task(current)) {
+       if (IS_ENABLED(CONFIG_RCU_EQS_DEBUG) &&
+           !user && !is_idle_task(current)) {
                struct task_struct *idle __maybe_unused =
                        idle_task(smp_processor_id());
 
@@ -604,7 +641,8 @@ static void rcu_eqs_enter_common(long long oldval, bool user)
        smp_mb__before_atomic();  /* See above. */
        atomic_inc(&rdtp->dynticks);
        smp_mb__after_atomic();  /* Force ordering with next sojourn. */
-       WARN_ON_ONCE(atomic_read(&rdtp->dynticks) & 0x1);
+       WARN_ON_ONCE(IS_ENABLED(CONFIG_RCU_EQS_DEBUG) &&
+                    atomic_read(&rdtp->dynticks) & 0x1);
        rcu_dynticks_task_enter();
 
        /*
@@ -630,7 +668,8 @@ static void rcu_eqs_enter(bool user)
 
        rdtp = this_cpu_ptr(&rcu_dynticks);
        oldval = rdtp->dynticks_nesting;
-       WARN_ON_ONCE((oldval & DYNTICK_TASK_NEST_MASK) == 0);
+       WARN_ON_ONCE(IS_ENABLED(CONFIG_RCU_EQS_DEBUG) &&
+                    (oldval & DYNTICK_TASK_NEST_MASK) == 0);
        if ((oldval & DYNTICK_TASK_NEST_MASK) == DYNTICK_TASK_NEST_VALUE) {
                rdtp->dynticks_nesting = 0;
                rcu_eqs_enter_common(oldval, user);
@@ -703,7 +742,8 @@ void rcu_irq_exit(void)
        rdtp = this_cpu_ptr(&rcu_dynticks);
        oldval = rdtp->dynticks_nesting;
        rdtp->dynticks_nesting--;
-       WARN_ON_ONCE(rdtp->dynticks_nesting < 0);
+       WARN_ON_ONCE(IS_ENABLED(CONFIG_RCU_EQS_DEBUG) &&
+                    rdtp->dynticks_nesting < 0);
        if (rdtp->dynticks_nesting)
                trace_rcu_dyntick(TPS("--="), oldval, rdtp->dynticks_nesting);
        else
@@ -728,10 +768,12 @@ static void rcu_eqs_exit_common(long long oldval, int user)
        atomic_inc(&rdtp->dynticks);
        /* CPUs seeing atomic_inc() must see later RCU read-side crit sects */
        smp_mb__after_atomic();  /* See above. */
-       WARN_ON_ONCE(!(atomic_read(&rdtp->dynticks) & 0x1));
+       WARN_ON_ONCE(IS_ENABLED(CONFIG_RCU_EQS_DEBUG) &&
+                    !(atomic_read(&rdtp->dynticks) & 0x1));
        rcu_cleanup_after_idle();
        trace_rcu_dyntick(TPS("End"), oldval, rdtp->dynticks_nesting);
-       if (!user && !is_idle_task(current)) {
+       if (IS_ENABLED(CONFIG_RCU_EQS_DEBUG) &&
+           !user && !is_idle_task(current)) {
                struct task_struct *idle __maybe_unused =
                        idle_task(smp_processor_id());
 
@@ -755,7 +797,7 @@ static void rcu_eqs_exit(bool user)
 
        rdtp = this_cpu_ptr(&rcu_dynticks);
        oldval = rdtp->dynticks_nesting;
-       WARN_ON_ONCE(oldval < 0);
+       WARN_ON_ONCE(IS_ENABLED(CONFIG_RCU_EQS_DEBUG) && oldval < 0);
        if (oldval & DYNTICK_TASK_NEST_MASK) {
                rdtp->dynticks_nesting += DYNTICK_TASK_NEST_VALUE;
        } else {
@@ -828,7 +870,8 @@ void rcu_irq_enter(void)
        rdtp = this_cpu_ptr(&rcu_dynticks);
        oldval = rdtp->dynticks_nesting;
        rdtp->dynticks_nesting++;
-       WARN_ON_ONCE(rdtp->dynticks_nesting == 0);
+       WARN_ON_ONCE(IS_ENABLED(CONFIG_RCU_EQS_DEBUG) &&
+                    rdtp->dynticks_nesting == 0);
        if (oldval)
                trace_rcu_dyntick(TPS("++="), oldval, rdtp->dynticks_nesting);
        else
@@ -1011,9 +1054,9 @@ static int dyntick_save_progress_counter(struct rcu_data *rdp,
                trace_rcu_fqs(rdp->rsp->name, rdp->gpnum, rdp->cpu, TPS("dti"));
                return 1;
        } else {
-               if (ULONG_CMP_LT(ACCESS_ONCE(rdp->gpnum) + ULONG_MAX / 4,
+               if (ULONG_CMP_LT(READ_ONCE(rdp->gpnum) + ULONG_MAX / 4,
                                 rdp->mynode->gpnum))
-                       ACCESS_ONCE(rdp->gpwrap) = true;
+                       WRITE_ONCE(rdp->gpwrap, true);
                return 0;
        }
 }
@@ -1093,12 +1136,12 @@ static int rcu_implicit_dynticks_qs(struct rcu_data *rdp,
        if (ULONG_CMP_GE(jiffies,
                         rdp->rsp->gp_start + jiffies_till_sched_qs) ||
            ULONG_CMP_GE(jiffies, rdp->rsp->jiffies_resched)) {
-               if (!(ACCESS_ONCE(*rcrmp) & rdp->rsp->flavor_mask)) {
-                       ACCESS_ONCE(rdp->cond_resched_completed) =
-                               ACCESS_ONCE(rdp->mynode->completed);
+               if (!(READ_ONCE(*rcrmp) & rdp->rsp->flavor_mask)) {
+                       WRITE_ONCE(rdp->cond_resched_completed,
+                                  READ_ONCE(rdp->mynode->completed));
                        smp_mb(); /* ->cond_resched_completed before *rcrmp. */
-                       ACCESS_ONCE(*rcrmp) =
-                               ACCESS_ONCE(*rcrmp) + rdp->rsp->flavor_mask;
+                       WRITE_ONCE(*rcrmp,
+                                  READ_ONCE(*rcrmp) + rdp->rsp->flavor_mask);
                        resched_cpu(rdp->cpu);  /* Force CPU into scheduler. */
                        rdp->rsp->jiffies_resched += 5; /* Enable beating. */
                } else if (ULONG_CMP_GE(jiffies, rdp->rsp->jiffies_resched)) {
@@ -1119,9 +1162,9 @@ static void record_gp_stall_check_time(struct rcu_state *rsp)
        rsp->gp_start = j;
        smp_wmb(); /* Record start time before stall time. */
        j1 = rcu_jiffies_till_stall_check();
-       ACCESS_ONCE(rsp->jiffies_stall) = j + j1;
+       WRITE_ONCE(rsp->jiffies_stall, j + j1);
        rsp->jiffies_resched = j + j1 / 2;
-       rsp->n_force_qs_gpstart = ACCESS_ONCE(rsp->n_force_qs);
+       rsp->n_force_qs_gpstart = READ_ONCE(rsp->n_force_qs);
 }
 
 /*
@@ -1133,10 +1176,11 @@ static void rcu_check_gp_kthread_starvation(struct rcu_state *rsp)
        unsigned long j;
 
        j = jiffies;
-       gpa = ACCESS_ONCE(rsp->gp_activity);
+       gpa = READ_ONCE(rsp->gp_activity);
        if (j - gpa > 2 * HZ)
-               pr_err("%s kthread starved for %ld jiffies!\n",
-                      rsp->name, j - gpa);
+               pr_err("%s kthread starved for %ld jiffies! g%lu c%lu f%#x\n",
+                      rsp->name, j - gpa,
+                      rsp->gpnum, rsp->completed, rsp->gp_flags);
 }
 
 /*
@@ -1173,12 +1217,13 @@ static void print_other_cpu_stall(struct rcu_state *rsp, unsigned long gpnum)
        /* Only let one CPU complain about others per time interval. */
 
        raw_spin_lock_irqsave(&rnp->lock, flags);
-       delta = jiffies - ACCESS_ONCE(rsp->jiffies_stall);
+       delta = jiffies - READ_ONCE(rsp->jiffies_stall);
        if (delta < RCU_STALL_RAT_DELAY || !rcu_gp_in_progress(rsp)) {
                raw_spin_unlock_irqrestore(&rnp->lock, flags);
                return;
        }
-       ACCESS_ONCE(rsp->jiffies_stall) = jiffies + 3 * rcu_jiffies_till_stall_check() + 3;
+       WRITE_ONCE(rsp->jiffies_stall,
+                  jiffies + 3 * rcu_jiffies_till_stall_check() + 3);
        raw_spin_unlock_irqrestore(&rnp->lock, flags);
 
        /*
@@ -1212,12 +1257,12 @@ static void print_other_cpu_stall(struct rcu_state *rsp, unsigned long gpnum)
        if (ndetected) {
                rcu_dump_cpu_stacks(rsp);
        } else {
-               if (ACCESS_ONCE(rsp->gpnum) != gpnum ||
-                   ACCESS_ONCE(rsp->completed) == gpnum) {
+               if (READ_ONCE(rsp->gpnum) != gpnum ||
+                   READ_ONCE(rsp->completed) == gpnum) {
                        pr_err("INFO: Stall ended before state dump start\n");
                } else {
                        j = jiffies;
-                       gpa = ACCESS_ONCE(rsp->gp_activity);
+                       gpa = READ_ONCE(rsp->gp_activity);
                        pr_err("All QSes seen, last %s kthread activity %ld (%ld-%ld), jiffies_till_next_fqs=%ld, root ->qsmask %#lx\n",
                               rsp->name, j - gpa, j, gpa,
                               jiffies_till_next_fqs,
@@ -1262,9 +1307,9 @@ static void print_cpu_stall(struct rcu_state *rsp)
        rcu_dump_cpu_stacks(rsp);
 
        raw_spin_lock_irqsave(&rnp->lock, flags);
-       if (ULONG_CMP_GE(jiffies, ACCESS_ONCE(rsp->jiffies_stall)))
-               ACCESS_ONCE(rsp->jiffies_stall) = jiffies +
-                                    3 * rcu_jiffies_till_stall_check() + 3;
+       if (ULONG_CMP_GE(jiffies, READ_ONCE(rsp->jiffies_stall)))
+               WRITE_ONCE(rsp->jiffies_stall,
+                          jiffies + 3 * rcu_jiffies_till_stall_check() + 3);
        raw_spin_unlock_irqrestore(&rnp->lock, flags);
 
        /*
@@ -1307,20 +1352,20 @@ static void check_cpu_stall(struct rcu_state *rsp, struct rcu_data *rdp)
         * Given this check, comparisons of jiffies, rsp->jiffies_stall,
         * and rsp->gp_start suffice to forestall false positives.
         */
-       gpnum = ACCESS_ONCE(rsp->gpnum);
+       gpnum = READ_ONCE(rsp->gpnum);
        smp_rmb(); /* Pick up ->gpnum first... */
-       js = ACCESS_ONCE(rsp->jiffies_stall);
+       js = READ_ONCE(rsp->jiffies_stall);
        smp_rmb(); /* ...then ->jiffies_stall before the rest... */
-       gps = ACCESS_ONCE(rsp->gp_start);
+       gps = READ_ONCE(rsp->gp_start);
        smp_rmb(); /* ...and finally ->gp_start before ->completed. */
-       completed = ACCESS_ONCE(rsp->completed);
+       completed = READ_ONCE(rsp->completed);
        if (ULONG_CMP_GE(completed, gpnum) ||
            ULONG_CMP_LT(j, js) ||
            ULONG_CMP_GE(gps, js))
                return; /* No stall or GP completed since entering function. */
        rnp = rdp->mynode;
        if (rcu_gp_in_progress(rsp) &&
-           (ACCESS_ONCE(rnp->qsmask) & rdp->grpmask)) {
+           (READ_ONCE(rnp->qsmask) & rdp->grpmask)) {
 
                /* We haven't checked in, so go dump stack. */
                print_cpu_stall(rsp);
@@ -1347,7 +1392,7 @@ void rcu_cpu_stall_reset(void)
        struct rcu_state *rsp;
 
        for_each_rcu_flavor(rsp)
-               ACCESS_ONCE(rsp->jiffies_stall) = jiffies + ULONG_MAX / 2;
+               WRITE_ONCE(rsp->jiffies_stall, jiffies + ULONG_MAX / 2);
 }
 
 /*
@@ -1457,7 +1502,7 @@ rcu_start_future_gp(struct rcu_node *rnp, struct rcu_data *rdp,
         * doing some extra useless work.
         */
        if (rnp->gpnum != rnp->completed ||
-           ACCESS_ONCE(rnp_root->gpnum) != ACCESS_ONCE(rnp_root->completed)) {
+           READ_ONCE(rnp_root->gpnum) != READ_ONCE(rnp_root->completed)) {
                rnp->need_future_gp[c & 0x1]++;
                trace_rcu_future_gp(rnp, rdp, c, TPS("Startedleaf"));
                goto out;
@@ -1542,7 +1587,7 @@ static int rcu_future_gp_cleanup(struct rcu_state *rsp, struct rcu_node *rnp)
 static void rcu_gp_kthread_wake(struct rcu_state *rsp)
 {
        if (current == rsp->gp_kthread ||
-           !ACCESS_ONCE(rsp->gp_flags) ||
+           !READ_ONCE(rsp->gp_flags) ||
            !rsp->gp_kthread)
                return;
        wake_up(&rsp->gp_wq);
@@ -1677,7 +1722,7 @@ static bool __note_gp_changes(struct rcu_state *rsp, struct rcu_node *rnp,
 
        /* Handle the ends of any preceding grace periods first. */
        if (rdp->completed == rnp->completed &&
-           !unlikely(ACCESS_ONCE(rdp->gpwrap))) {
+           !unlikely(READ_ONCE(rdp->gpwrap))) {
 
                /* No grace period end, so just accelerate recent callbacks. */
                ret = rcu_accelerate_cbs(rsp, rnp, rdp);
@@ -1692,7 +1737,7 @@ static bool __note_gp_changes(struct rcu_state *rsp, struct rcu_node *rnp,
                trace_rcu_grace_period(rsp->name, rdp->gpnum, TPS("cpuend"));
        }
 
-       if (rdp->gpnum != rnp->gpnum || unlikely(ACCESS_ONCE(rdp->gpwrap))) {
+       if (rdp->gpnum != rnp->gpnum || unlikely(READ_ONCE(rdp->gpwrap))) {
                /*
                 * If the current grace period is waiting for this CPU,
                 * set up to detect a quiescent state, otherwise don't
@@ -1704,7 +1749,7 @@ static bool __note_gp_changes(struct rcu_state *rsp, struct rcu_node *rnp,
                rdp->rcu_qs_ctr_snap = __this_cpu_read(rcu_qs_ctr);
                rdp->qs_pending = !!(rnp->qsmask & rdp->grpmask);
                zero_cpu_stall_ticks(rdp);
-               ACCESS_ONCE(rdp->gpwrap) = false;
+               WRITE_ONCE(rdp->gpwrap, false);
        }
        return ret;
 }
@@ -1717,9 +1762,9 @@ static void note_gp_changes(struct rcu_state *rsp, struct rcu_data *rdp)
 
        local_irq_save(flags);
        rnp = rdp->mynode;
-       if ((rdp->gpnum == ACCESS_ONCE(rnp->gpnum) &&
-            rdp->completed == ACCESS_ONCE(rnp->completed) &&
-            !unlikely(ACCESS_ONCE(rdp->gpwrap))) || /* w/out lock. */
+       if ((rdp->gpnum == READ_ONCE(rnp->gpnum) &&
+            rdp->completed == READ_ONCE(rnp->completed) &&
+            !unlikely(READ_ONCE(rdp->gpwrap))) || /* w/out lock. */
            !raw_spin_trylock(&rnp->lock)) { /* irqs already off, so later. */
                local_irq_restore(flags);
                return;
@@ -1731,6 +1776,13 @@ static void note_gp_changes(struct rcu_state *rsp, struct rcu_data *rdp)
                rcu_gp_kthread_wake(rsp);
 }
 
+static void rcu_gp_slow(struct rcu_state *rsp, int delay)
+{
+       if (delay > 0 &&
+           !(rsp->gpnum % (rcu_num_nodes * PER_RCU_NODE_PERIOD * delay)))
+               schedule_timeout_uninterruptible(delay);
+}
+
 /*
  * Initialize a new grace period.  Return 0 if no grace period required.
  */
@@ -1740,15 +1792,15 @@ static int rcu_gp_init(struct rcu_state *rsp)
        struct rcu_data *rdp;
        struct rcu_node *rnp = rcu_get_root(rsp);
 
-       ACCESS_ONCE(rsp->gp_activity) = jiffies;
+       WRITE_ONCE(rsp->gp_activity, jiffies);
        raw_spin_lock_irq(&rnp->lock);
        smp_mb__after_unlock_lock();
-       if (!ACCESS_ONCE(rsp->gp_flags)) {
+       if (!READ_ONCE(rsp->gp_flags)) {
                /* Spurious wakeup, tell caller to go back to sleep.  */
                raw_spin_unlock_irq(&rnp->lock);
                return 0;
        }
-       ACCESS_ONCE(rsp->gp_flags) = 0; /* Clear all flags: New grace period. */
+       WRITE_ONCE(rsp->gp_flags, 0); /* Clear all flags: New grace period. */
 
        if (WARN_ON_ONCE(rcu_gp_in_progress(rsp))) {
                /*
@@ -1773,6 +1825,7 @@ static int rcu_gp_init(struct rcu_state *rsp)
         * will handle subsequent offline CPUs.
         */
        rcu_for_each_leaf_node(rsp, rnp) {
+               rcu_gp_slow(rsp, gp_preinit_delay);
                raw_spin_lock_irq(&rnp->lock);
                smp_mb__after_unlock_lock();
                if (rnp->qsmaskinit == rnp->qsmaskinitnext &&
@@ -1829,14 +1882,15 @@ static int rcu_gp_init(struct rcu_state *rsp)
         * process finishes, because this kthread handles both.
         */
        rcu_for_each_node_breadth_first(rsp, rnp) {
+               rcu_gp_slow(rsp, gp_init_delay);
                raw_spin_lock_irq(&rnp->lock);
                smp_mb__after_unlock_lock();
                rdp = this_cpu_ptr(rsp->rda);
                rcu_preempt_check_blocked_tasks(rnp);
                rnp->qsmask = rnp->qsmaskinit;
-               ACCESS_ONCE(rnp->gpnum) = rsp->gpnum;
+               WRITE_ONCE(rnp->gpnum, rsp->gpnum);
                if (WARN_ON_ONCE(rnp->completed != rsp->completed))
-                       ACCESS_ONCE(rnp->completed) = rsp->completed;
+                       WRITE_ONCE(rnp->completed, rsp->completed);
                if (rnp == rdp->mynode)
                        (void)__note_gp_changes(rsp, rnp, rdp);
                rcu_preempt_boost_start_gp(rnp);
@@ -1845,10 +1899,7 @@ static int rcu_gp_init(struct rcu_state *rsp)
                                            rnp->grphi, rnp->qsmask);
                raw_spin_unlock_irq(&rnp->lock);
                cond_resched_rcu_qs();
-               ACCESS_ONCE(rsp->gp_activity) = jiffies;
-               if (gp_init_delay > 0 &&
-                   !(rsp->gpnum % (rcu_num_nodes * PER_RCU_NODE_PERIOD)))
-                       schedule_timeout_uninterruptible(gp_init_delay);
+               WRITE_ONCE(rsp->gp_activity, jiffies);
        }
 
        return 1;
@@ -1864,7 +1915,7 @@ static int rcu_gp_fqs(struct rcu_state *rsp, int fqs_state_in)
        unsigned long maxj;
        struct rcu_node *rnp = rcu_get_root(rsp);
 
-       ACCESS_ONCE(rsp->gp_activity) = jiffies;
+       WRITE_ONCE(rsp->gp_activity, jiffies);
        rsp->n_force_qs++;
        if (fqs_state == RCU_SAVE_DYNTICK) {
                /* Collect dyntick-idle snapshots. */
@@ -1882,11 +1933,11 @@ static int rcu_gp_fqs(struct rcu_state *rsp, int fqs_state_in)
                force_qs_rnp(rsp, rcu_implicit_dynticks_qs, &isidle, &maxj);
        }
        /* Clear flag to prevent immediate re-entry. */
-       if (ACCESS_ONCE(rsp->gp_flags) & RCU_GP_FLAG_FQS) {
+       if (READ_ONCE(rsp->gp_flags) & RCU_GP_FLAG_FQS) {
                raw_spin_lock_irq(&rnp->lock);
                smp_mb__after_unlock_lock();
-               ACCESS_ONCE(rsp->gp_flags) =
-                       ACCESS_ONCE(rsp->gp_flags) & ~RCU_GP_FLAG_FQS;
+               WRITE_ONCE(rsp->gp_flags,
+                          READ_ONCE(rsp->gp_flags) & ~RCU_GP_FLAG_FQS);
                raw_spin_unlock_irq(&rnp->lock);
        }
        return fqs_state;
@@ -1903,7 +1954,7 @@ static void rcu_gp_cleanup(struct rcu_state *rsp)
        struct rcu_data *rdp;
        struct rcu_node *rnp = rcu_get_root(rsp);
 
-       ACCESS_ONCE(rsp->gp_activity) = jiffies;
+       WRITE_ONCE(rsp->gp_activity, jiffies);
        raw_spin_lock_irq(&rnp->lock);
        smp_mb__after_unlock_lock();
        gp_duration = jiffies - rsp->gp_start;
@@ -1934,7 +1985,7 @@ static void rcu_gp_cleanup(struct rcu_state *rsp)
                smp_mb__after_unlock_lock();
                WARN_ON_ONCE(rcu_preempt_blocked_readers_cgp(rnp));
                WARN_ON_ONCE(rnp->qsmask);
-               ACCESS_ONCE(rnp->completed) = rsp->gpnum;
+               WRITE_ONCE(rnp->completed, rsp->gpnum);
                rdp = this_cpu_ptr(rsp->rda);
                if (rnp == rdp->mynode)
                        needgp = __note_gp_changes(rsp, rnp, rdp) || needgp;
@@ -1942,7 +1993,8 @@ static void rcu_gp_cleanup(struct rcu_state *rsp)
                nocb += rcu_future_gp_cleanup(rsp, rnp);
                raw_spin_unlock_irq(&rnp->lock);
                cond_resched_rcu_qs();
-               ACCESS_ONCE(rsp->gp_activity) = jiffies;
+               WRITE_ONCE(rsp->gp_activity, jiffies);
+               rcu_gp_slow(rsp, gp_cleanup_delay);
        }
        rnp = rcu_get_root(rsp);
        raw_spin_lock_irq(&rnp->lock);
@@ -1950,16 +2002,16 @@ static void rcu_gp_cleanup(struct rcu_state *rsp)
        rcu_nocb_gp_set(rnp, nocb);
 
        /* Declare grace period done. */
-       ACCESS_ONCE(rsp->completed) = rsp->gpnum;
+       WRITE_ONCE(rsp->completed, rsp->gpnum);
        trace_rcu_grace_period(rsp->name, rsp->completed, TPS("end"));
        rsp->fqs_state = RCU_GP_IDLE;
        rdp = this_cpu_ptr(rsp->rda);
        /* Advance CBs to reduce false positives below. */
        needgp = rcu_advance_cbs(rsp, rnp, rdp) || needgp;
        if (needgp || cpu_needs_another_gp(rsp, rdp)) {
-               ACCESS_ONCE(rsp->gp_flags) = RCU_GP_FLAG_INIT;
+               WRITE_ONCE(rsp->gp_flags, RCU_GP_FLAG_INIT);
                trace_rcu_grace_period(rsp->name,
-                                      ACCESS_ONCE(rsp->gpnum),
+                                      READ_ONCE(rsp->gpnum),
                                       TPS("newreq"));
        }
        raw_spin_unlock_irq(&rnp->lock);
@@ -1983,20 +2035,20 @@ static int __noreturn rcu_gp_kthread(void *arg)
                /* Handle grace-period start. */
                for (;;) {
                        trace_rcu_grace_period(rsp->name,
-                                              ACCESS_ONCE(rsp->gpnum),
+                                              READ_ONCE(rsp->gpnum),
                                               TPS("reqwait"));
                        rsp->gp_state = RCU_GP_WAIT_GPS;
                        wait_event_interruptible(rsp->gp_wq,
-                                                ACCESS_ONCE(rsp->gp_flags) &
+                                                READ_ONCE(rsp->gp_flags) &
                                                 RCU_GP_FLAG_INIT);
                        /* Locking provides needed memory barrier. */
                        if (rcu_gp_init(rsp))
                                break;
                        cond_resched_rcu_qs();
-                       ACCESS_ONCE(rsp->gp_activity) = jiffies;
+                       WRITE_ONCE(rsp->gp_activity, jiffies);
                        WARN_ON(signal_pending(current));
                        trace_rcu_grace_period(rsp->name,
-                                              ACCESS_ONCE(rsp->gpnum),
+                                              READ_ONCE(rsp->gpnum),
                                               TPS("reqwaitsig"));
                }
 
@@ -2012,39 +2064,39 @@ static int __noreturn rcu_gp_kthread(void *arg)
                        if (!ret)
                                rsp->jiffies_force_qs = jiffies + j;
                        trace_rcu_grace_period(rsp->name,
-                                              ACCESS_ONCE(rsp->gpnum),
+                                              READ_ONCE(rsp->gpnum),
                                               TPS("fqswait"));
                        rsp->gp_state = RCU_GP_WAIT_FQS;
                        ret = wait_event_interruptible_timeout(rsp->gp_wq,
-                                       ((gf = ACCESS_ONCE(rsp->gp_flags)) &
+                                       ((gf = READ_ONCE(rsp->gp_flags)) &
                                         RCU_GP_FLAG_FQS) ||
-                                       (!ACCESS_ONCE(rnp->qsmask) &&
+                                       (!READ_ONCE(rnp->qsmask) &&
                                         !rcu_preempt_blocked_readers_cgp(rnp)),
                                        j);
                        /* Locking provides needed memory barriers. */
                        /* If grace period done, leave loop. */
-                       if (!ACCESS_ONCE(rnp->qsmask) &&
+                       if (!READ_ONCE(rnp->qsmask) &&
                            !rcu_preempt_blocked_readers_cgp(rnp))
                                break;
                        /* If time for quiescent-state forcing, do it. */
                        if (ULONG_CMP_GE(jiffies, rsp->jiffies_force_qs) ||
                            (gf & RCU_GP_FLAG_FQS)) {
                                trace_rcu_grace_period(rsp->name,
-                                                      ACCESS_ONCE(rsp->gpnum),
+                                                      READ_ONCE(rsp->gpnum),
                                                       TPS("fqsstart"));
                                fqs_state = rcu_gp_fqs(rsp, fqs_state);
                                trace_rcu_grace_period(rsp->name,
-                                                      ACCESS_ONCE(rsp->gpnum),
+                                                      READ_ONCE(rsp->gpnum),
                                                       TPS("fqsend"));
                                cond_resched_rcu_qs();
-                               ACCESS_ONCE(rsp->gp_activity) = jiffies;
+                               WRITE_ONCE(rsp->gp_activity, jiffies);
                        } else {
                                /* Deal with stray signal. */
                                cond_resched_rcu_qs();
-                               ACCESS_ONCE(rsp->gp_activity) = jiffies;
+                               WRITE_ONCE(rsp->gp_activity, jiffies);
                                WARN_ON(signal_pending(current));
                                trace_rcu_grace_period(rsp->name,
-                                                      ACCESS_ONCE(rsp->gpnum),
+                                                      READ_ONCE(rsp->gpnum),
                                                       TPS("fqswaitsig"));
                        }
                        j = jiffies_till_next_fqs;
@@ -2086,8 +2138,8 @@ rcu_start_gp_advanced(struct rcu_state *rsp, struct rcu_node *rnp,
                 */
                return false;
        }
-       ACCESS_ONCE(rsp->gp_flags) = RCU_GP_FLAG_INIT;
-       trace_rcu_grace_period(rsp->name, ACCESS_ONCE(rsp->gpnum),
+       WRITE_ONCE(rsp->gp_flags, RCU_GP_FLAG_INIT);
+       trace_rcu_grace_period(rsp->name, READ_ONCE(rsp->gpnum),
                               TPS("newreq"));
 
        /*
@@ -2137,6 +2189,7 @@ static void rcu_report_qs_rsp(struct rcu_state *rsp, unsigned long flags)
        __releases(rcu_get_root(rsp)->lock)
 {
        WARN_ON_ONCE(!rcu_gp_in_progress(rsp));
+       WRITE_ONCE(rsp->gp_flags, READ_ONCE(rsp->gp_flags) | RCU_GP_FLAG_FQS);
        raw_spin_unlock_irqrestore(&rcu_get_root(rsp)->lock, flags);
        rcu_gp_kthread_wake(rsp);
 }
@@ -2334,8 +2387,6 @@ rcu_check_quiescent_state(struct rcu_state *rsp, struct rcu_data *rdp)
        rcu_report_qs_rdp(rdp->cpu, rsp, rdp);
 }
 
-#ifdef CONFIG_HOTPLUG_CPU
-
 /*
  * Send the specified CPU's RCU callbacks to the orphanage.  The
  * specified CPU must be offline, and the caller must hold the
@@ -2346,7 +2397,7 @@ rcu_send_cbs_to_orphanage(int cpu, struct rcu_state *rsp,
                          struct rcu_node *rnp, struct rcu_data *rdp)
 {
        /* No-CBs CPUs do not have orphanable callbacks. */
-       if (rcu_is_nocb_cpu(rdp->cpu))
+       if (!IS_ENABLED(CONFIG_HOTPLUG_CPU) || rcu_is_nocb_cpu(rdp->cpu))
                return;
 
        /*
@@ -2359,7 +2410,7 @@ rcu_send_cbs_to_orphanage(int cpu, struct rcu_state *rsp,
                rsp->qlen += rdp->qlen;
                rdp->n_cbs_orphaned += rdp->qlen;
                rdp->qlen_lazy = 0;
-               ACCESS_ONCE(rdp->qlen) = 0;
+               WRITE_ONCE(rdp->qlen, 0);
        }
 
        /*
@@ -2405,7 +2456,8 @@ static void rcu_adopt_orphan_cbs(struct rcu_state *rsp, unsigned long flags)
        struct rcu_data *rdp = raw_cpu_ptr(rsp->rda);
 
        /* No-CBs CPUs are handled specially. */
-       if (rcu_nocb_adopt_orphan_cbs(rsp, rdp, flags))
+       if (!IS_ENABLED(CONFIG_HOTPLUG_CPU) ||
+           rcu_nocb_adopt_orphan_cbs(rsp, rdp, flags))
                return;
 
        /* Do the accounting first. */
@@ -2452,6 +2504,9 @@ static void rcu_cleanup_dying_cpu(struct rcu_state *rsp)
        RCU_TRACE(struct rcu_data *rdp = this_cpu_ptr(rsp->rda));
        RCU_TRACE(struct rcu_node *rnp = rdp->mynode);
 
+       if (!IS_ENABLED(CONFIG_HOTPLUG_CPU))
+               return;
+
        RCU_TRACE(mask = rdp->grpmask);
        trace_rcu_grace_period(rsp->name,
                               rnp->gpnum + 1 - !!(rnp->qsmask & mask),
@@ -2480,7 +2535,8 @@ static void rcu_cleanup_dead_rnp(struct rcu_node *rnp_leaf)
        long mask;
        struct rcu_node *rnp = rnp_leaf;
 
-       if (rnp->qsmaskinit || rcu_preempt_has_tasks(rnp))
+       if (!IS_ENABLED(CONFIG_HOTPLUG_CPU) ||
+           rnp->qsmaskinit || rcu_preempt_has_tasks(rnp))
                return;
        for (;;) {
                mask = rnp->grpmask;
@@ -2511,6 +2567,9 @@ static void rcu_cleanup_dying_idle_cpu(int cpu, struct rcu_state *rsp)
        struct rcu_data *rdp = per_cpu_ptr(rsp->rda, cpu);
        struct rcu_node *rnp = rdp->mynode;  /* Outgoing CPU's rdp & rnp. */
 
+       if (!IS_ENABLED(CONFIG_HOTPLUG_CPU))
+               return;
+
        /* Remove outgoing CPU from mask in the leaf rcu_node structure. */
        mask = rdp->grpmask;
        raw_spin_lock_irqsave(&rnp->lock, flags);
@@ -2532,6 +2591,9 @@ static void rcu_cleanup_dead_cpu(int cpu, struct rcu_state *rsp)
        struct rcu_data *rdp = per_cpu_ptr(rsp->rda, cpu);
        struct rcu_node *rnp = rdp->mynode;  /* Outgoing CPU's rdp & rnp. */
 
+       if (!IS_ENABLED(CONFIG_HOTPLUG_CPU))
+               return;
+
        /* Adjust any no-longer-needed kthreads. */
        rcu_boost_kthread_setaffinity(rnp, -1);
 
@@ -2546,26 +2608,6 @@ static void rcu_cleanup_dead_cpu(int cpu, struct rcu_state *rsp)
                  cpu, rdp->qlen, rdp->nxtlist);
 }
 
-#else /* #ifdef CONFIG_HOTPLUG_CPU */
-
-static void rcu_cleanup_dying_cpu(struct rcu_state *rsp)
-{
-}
-
-static void __maybe_unused rcu_cleanup_dead_rnp(struct rcu_node *rnp_leaf)
-{
-}
-
-static void rcu_cleanup_dying_idle_cpu(int cpu, struct rcu_state *rsp)
-{
-}
-
-static void rcu_cleanup_dead_cpu(int cpu, struct rcu_state *rsp)
-{
-}
-
-#endif /* #else #ifdef CONFIG_HOTPLUG_CPU */
-
 /*
  * Invoke any RCU callbacks that have made it to the end of their grace
  * period.  Thottle as specified by rdp->blimit.
@@ -2580,7 +2622,7 @@ static void rcu_do_batch(struct rcu_state *rsp, struct rcu_data *rdp)
        /* If no callbacks are ready, just return. */
        if (!cpu_has_callbacks_ready_to_invoke(rdp)) {
                trace_rcu_batch_start(rsp->name, rdp->qlen_lazy, rdp->qlen, 0);
-               trace_rcu_batch_end(rsp->name, 0, !!ACCESS_ONCE(rdp->nxtlist),
+               trace_rcu_batch_end(rsp->name, 0, !!READ_ONCE(rdp->nxtlist),
                                    need_resched(), is_idle_task(current),
                                    rcu_is_callbacks_kthread());
                return;
@@ -2636,7 +2678,7 @@ static void rcu_do_batch(struct rcu_state *rsp, struct rcu_data *rdp)
        }
        smp_mb(); /* List handling before counting for rcu_barrier(). */
        rdp->qlen_lazy -= count_lazy;
-       ACCESS_ONCE(rdp->qlen) = rdp->qlen - count;
+       WRITE_ONCE(rdp->qlen, rdp->qlen - count);
        rdp->n_cbs_invoked += count;
 
        /* Reinstate batch limit if we have worked down the excess. */
@@ -2730,10 +2772,6 @@ static void force_qs_rnp(struct rcu_state *rsp,
                mask = 0;
                raw_spin_lock_irqsave(&rnp->lock, flags);
                smp_mb__after_unlock_lock();
-               if (!rcu_gp_in_progress(rsp)) {
-                       raw_spin_unlock_irqrestore(&rnp->lock, flags);
-                       return;
-               }
                if (rnp->qsmask == 0) {
                        if (rcu_state_p == &rcu_sched_state ||
                            rsp != rcu_state_p ||
@@ -2763,8 +2801,6 @@ static void force_qs_rnp(struct rcu_state *rsp,
                bit = 1;
                for (; cpu <= rnp->grphi; cpu++, bit <<= 1) {
                        if ((rnp->qsmask & bit) != 0) {
-                               if ((rnp->qsmaskinit & bit) == 0)
-                                       *isidle = false; /* Pending hotplug. */
                                if (f(per_cpu_ptr(rsp->rda, cpu), isidle, maxj))
                                        mask |= bit;
                        }
@@ -2793,7 +2829,7 @@ static void force_quiescent_state(struct rcu_state *rsp)
        /* Funnel through hierarchy to reduce memory contention. */
        rnp = __this_cpu_read(rsp->rda->mynode);
        for (; rnp != NULL; rnp = rnp->parent) {
-               ret = (ACCESS_ONCE(rsp->gp_flags) & RCU_GP_FLAG_FQS) ||
+               ret = (READ_ONCE(rsp->gp_flags) & RCU_GP_FLAG_FQS) ||
                      !raw_spin_trylock(&rnp->fqslock);
                if (rnp_old != NULL)
                        raw_spin_unlock(&rnp_old->fqslock);
@@ -2809,13 +2845,12 @@ static void force_quiescent_state(struct rcu_state *rsp)
        raw_spin_lock_irqsave(&rnp_old->lock, flags);
        smp_mb__after_unlock_lock();
        raw_spin_unlock(&rnp_old->fqslock);
-       if (ACCESS_ONCE(rsp->gp_flags) & RCU_GP_FLAG_FQS) {
+       if (READ_ONCE(rsp->gp_flags) & RCU_GP_FLAG_FQS) {
                rsp->n_force_qs_lh++;
                raw_spin_unlock_irqrestore(&rnp_old->lock, flags);
                return;  /* Someone beat us to it. */
        }
-       ACCESS_ONCE(rsp->gp_flags) =
-               ACCESS_ONCE(rsp->gp_flags) | RCU_GP_FLAG_FQS;
+       WRITE_ONCE(rsp->gp_flags, READ_ONCE(rsp->gp_flags) | RCU_GP_FLAG_FQS);
        raw_spin_unlock_irqrestore(&rnp_old->lock, flags);
        rcu_gp_kthread_wake(rsp);
 }
@@ -2881,7 +2916,7 @@ static void rcu_process_callbacks(struct softirq_action *unused)
  */
 static void invoke_rcu_callbacks(struct rcu_state *rsp, struct rcu_data *rdp)
 {
-       if (unlikely(!ACCESS_ONCE(rcu_scheduler_fully_active)))
+       if (unlikely(!READ_ONCE(rcu_scheduler_fully_active)))
                return;
        if (likely(!rsp->boost)) {
                rcu_do_batch(rsp, rdp);
@@ -2972,7 +3007,7 @@ __call_rcu(struct rcu_head *head, void (*func)(struct rcu_head *rcu),
        WARN_ON_ONCE((unsigned long)head & 0x1); /* Misaligned rcu_head! */
        if (debug_rcu_head_queue(head)) {
                /* Probable double call_rcu(), so leak the callback. */
-               ACCESS_ONCE(head->func) = rcu_leak_callback;
+               WRITE_ONCE(head->func, rcu_leak_callback);
                WARN_ONCE(1, "__call_rcu(): Leaked duplicate callback\n");
                return;
        }
@@ -3011,7 +3046,7 @@ __call_rcu(struct rcu_head *head, void (*func)(struct rcu_head *rcu),
                if (!likely(rdp->nxtlist))
                        init_default_callback_list(rdp);
        }
-       ACCESS_ONCE(rdp->qlen) = rdp->qlen + 1;
+       WRITE_ONCE(rdp->qlen, rdp->qlen + 1);
        if (lazy)
                rdp->qlen_lazy++;
        else
@@ -3287,7 +3322,7 @@ void synchronize_sched_expedited(void)
        if (ULONG_CMP_GE((ulong)atomic_long_read(&rsp->expedited_start),
                         (ulong)atomic_long_read(&rsp->expedited_done) +
                         ULONG_MAX / 8)) {
-               synchronize_sched();
+               wait_rcu_gp(call_rcu_sched);
                atomic_long_inc(&rsp->expedited_wrap);
                return;
        }
@@ -3450,14 +3485,14 @@ static int __rcu_pending(struct rcu_state *rsp, struct rcu_data *rdp)
        }
 
        /* Has another RCU grace period completed?  */
-       if (ACCESS_ONCE(rnp->completed) != rdp->completed) { /* outside lock */
+       if (READ_ONCE(rnp->completed) != rdp->completed) { /* outside lock */
                rdp->n_rp_gp_completed++;
                return 1;
        }
 
        /* Has a new RCU grace period started? */
-       if (ACCESS_ONCE(rnp->gpnum) != rdp->gpnum ||
-           unlikely(ACCESS_ONCE(rdp->gpwrap))) { /* outside lock */
+       if (READ_ONCE(rnp->gpnum) != rdp->gpnum ||
+           unlikely(READ_ONCE(rdp->gpwrap))) { /* outside lock */
                rdp->n_rp_gp_started++;
                return 1;
        }
@@ -3493,7 +3528,7 @@ static int rcu_pending(void)
  * non-NULL, store an indication of whether all callbacks are lazy.
  * (If there are no callbacks, all of them are deemed to be lazy.)
  */
-static int __maybe_unused rcu_cpu_has_callbacks(bool *all_lazy)
+static bool __maybe_unused rcu_cpu_has_callbacks(bool *all_lazy)
 {
        bool al = true;
        bool hc = false;
@@ -3564,7 +3599,7 @@ static void _rcu_barrier(struct rcu_state *rsp)
 {
        int cpu;
        struct rcu_data *rdp;
-       unsigned long snap = ACCESS_ONCE(rsp->n_barrier_done);
+       unsigned long snap = READ_ONCE(rsp->n_barrier_done);
        unsigned long snap_done;
 
        _rcu_barrier_trace(rsp, "Begin", -1, snap);
@@ -3606,10 +3641,10 @@ static void _rcu_barrier(struct rcu_state *rsp)
 
        /*
         * Increment ->n_barrier_done to avoid duplicate work.  Use
-        * ACCESS_ONCE() to prevent the compiler from speculating
+        * WRITE_ONCE() to prevent the compiler from speculating
         * the increment to precede the early-exit check.
         */
-       ACCESS_ONCE(rsp->n_barrier_done) = rsp->n_barrier_done + 1;
+       WRITE_ONCE(rsp->n_barrier_done, rsp->n_barrier_done + 1);
        WARN_ON_ONCE((rsp->n_barrier_done & 0x1) != 1);
        _rcu_barrier_trace(rsp, "Inc1", -1, rsp->n_barrier_done);
        smp_mb(); /* Order ->n_barrier_done increment with below mechanism. */
@@ -3645,7 +3680,7 @@ static void _rcu_barrier(struct rcu_state *rsp)
                                __call_rcu(&rdp->barrier_head,
                                           rcu_barrier_callback, rsp, cpu, 0);
                        }
-               } else if (ACCESS_ONCE(rdp->qlen)) {
+               } else if (READ_ONCE(rdp->qlen)) {
                        _rcu_barrier_trace(rsp, "OnlineQ", cpu,
                                           rsp->n_barrier_done);
                        smp_call_function_single(cpu, rcu_barrier_func, rsp, 1);
@@ -3665,7 +3700,7 @@ static void _rcu_barrier(struct rcu_state *rsp)
 
        /* Increment ->n_barrier_done to prevent duplicate work. */
        smp_mb(); /* Keep increment after above mechanism. */
-       ACCESS_ONCE(rsp->n_barrier_done) = rsp->n_barrier_done + 1;
+       WRITE_ONCE(rsp->n_barrier_done, rsp->n_barrier_done + 1);
        WARN_ON_ONCE((rsp->n_barrier_done & 0x1) != 0);
        _rcu_barrier_trace(rsp, "Inc2", -1, rsp->n_barrier_done);
        smp_mb(); /* Keep increment before caller's subsequent code. */
@@ -3780,7 +3815,7 @@ rcu_init_percpu_data(int cpu, struct rcu_state *rsp)
        rdp->gpnum = rnp->completed; /* Make CPU later note any new GP. */
        rdp->completed = rnp->completed;
        rdp->passed_quiesce = false;
-       rdp->rcu_qs_ctr_snap = __this_cpu_read(rcu_qs_ctr);
+       rdp->rcu_qs_ctr_snap = per_cpu(rcu_qs_ctr, cpu);
        rdp->qs_pending = false;
        trace_rcu_grace_period(rsp->name, rdp->gpnum, TPS("cpuonl"));
        raw_spin_unlock_irqrestore(&rnp->lock, flags);
@@ -3924,16 +3959,16 @@ void rcu_scheduler_starting(void)
 
 /*
  * Compute the per-level fanout, either using the exact fanout specified
- * or balancing the tree, depending on CONFIG_RCU_FANOUT_EXACT.
+ * or balancing the tree, depending on the rcu_fanout_exact boot parameter.
  */
 static void __init rcu_init_levelspread(struct rcu_state *rsp)
 {
        int i;
 
-       if (IS_ENABLED(CONFIG_RCU_FANOUT_EXACT)) {
+       if (rcu_fanout_exact) {
                rsp->levelspread[rcu_num_lvls - 1] = rcu_fanout_leaf;
                for (i = rcu_num_lvls - 2; i >= 0; i--)
-                       rsp->levelspread[i] = CONFIG_RCU_FANOUT;
+                       rsp->levelspread[i] = RCU_FANOUT;
        } else {
                int ccur;
                int cprv;
@@ -3971,9 +4006,9 @@ static void __init rcu_init_one(struct rcu_state *rsp,
 
        BUILD_BUG_ON(MAX_RCU_LVLS > ARRAY_SIZE(buf));  /* Fix buf[] init! */
 
-       /* Silence gcc 4.8 warning about array index out of range. */
-       if (rcu_num_lvls > RCU_NUM_LVLS)
-               panic("rcu_init_one: rcu_num_lvls overflow");
+       /* Silence gcc 4.8 false positive about array index out of range. */
+       if (rcu_num_lvls <= 0 || rcu_num_lvls > RCU_NUM_LVLS)
+               panic("rcu_init_one: rcu_num_lvls out of range");
 
        /* Initialize the level-tracking arrays. */
 
@@ -4059,7 +4094,7 @@ static void __init rcu_init_geometry(void)
                jiffies_till_next_fqs = d;
 
        /* If the compile-time values are accurate, just leave. */
-       if (rcu_fanout_leaf == CONFIG_RCU_FANOUT_LEAF &&
+       if (rcu_fanout_leaf == RCU_FANOUT_LEAF &&
            nr_cpu_ids == NR_CPUS)
                return;
        pr_info("RCU: Adjusting geometry for rcu_fanout_leaf=%d, nr_cpu_ids=%d\n",
@@ -4073,7 +4108,7 @@ static void __init rcu_init_geometry(void)
        rcu_capacity[0] = 1;
        rcu_capacity[1] = rcu_fanout_leaf;
        for (i = 2; i <= MAX_RCU_LVLS; i++)
-               rcu_capacity[i] = rcu_capacity[i - 1] * CONFIG_RCU_FANOUT;
+               rcu_capacity[i] = rcu_capacity[i - 1] * RCU_FANOUT;
 
        /*
         * The boot-time rcu_fanout_leaf parameter is only permitted
@@ -4083,7 +4118,7 @@ static void __init rcu_init_geometry(void)
         * the configured number of CPUs.  Complain and fall back to the
         * compile-time values if these limits are exceeded.
         */
-       if (rcu_fanout_leaf < CONFIG_RCU_FANOUT_LEAF ||
+       if (rcu_fanout_leaf < RCU_FANOUT_LEAF ||
            rcu_fanout_leaf > sizeof(unsigned long) * 8 ||
            n > rcu_capacity[MAX_RCU_LVLS]) {
                WARN_ON(1);
@@ -4109,6 +4144,28 @@ static void __init rcu_init_geometry(void)
        rcu_num_nodes -= n;
 }
 
+/*
+ * Dump out the structure of the rcu_node combining tree associated
+ * with the rcu_state structure referenced by rsp.
+ */
+static void __init rcu_dump_rcu_node_tree(struct rcu_state *rsp)
+{
+       int level = 0;
+       struct rcu_node *rnp;
+
+       pr_info("rcu_node tree layout dump\n");
+       pr_info(" ");
+       rcu_for_each_node_breadth_first(rsp, rnp) {
+               if (rnp->level != level) {
+                       pr_cont("\n");
+                       pr_info(" ");
+                       level = rnp->level;
+               }
+               pr_cont("%d:%d ^%d  ", rnp->grplo, rnp->grphi, rnp->grpnum);
+       }
+       pr_cont("\n");
+}
+
 void __init rcu_init(void)
 {
        int cpu;
@@ -4119,6 +4176,8 @@ void __init rcu_init(void)
        rcu_init_geometry();
        rcu_init_one(&rcu_bh_state, &rcu_bh_data);
        rcu_init_one(&rcu_sched_state, &rcu_sched_data);
+       if (dump_tree)
+               rcu_dump_rcu_node_tree(&rcu_sched_state);
        __rcu_init_preempt();
        open_softirq(RCU_SOFTIRQ, rcu_process_callbacks);
 
index a69d3dab2ec4dbf9dc8c412813c84c27363f9378..4adb7ca0bf47a209067c66205ace8b6f0dbebb61 100644 (file)
  * In practice, this did work well going from three levels to four.
  * Of course, your mileage may vary.
  */
+
 #define MAX_RCU_LVLS 4
-#define RCU_FANOUT_1         (CONFIG_RCU_FANOUT_LEAF)
-#define RCU_FANOUT_2         (RCU_FANOUT_1 * CONFIG_RCU_FANOUT)
-#define RCU_FANOUT_3         (RCU_FANOUT_2 * CONFIG_RCU_FANOUT)
-#define RCU_FANOUT_4         (RCU_FANOUT_3 * CONFIG_RCU_FANOUT)
+
+#ifdef CONFIG_RCU_FANOUT
+#define RCU_FANOUT CONFIG_RCU_FANOUT
+#else /* #ifdef CONFIG_RCU_FANOUT */
+# ifdef CONFIG_64BIT
+# define RCU_FANOUT 64
+# else
+# define RCU_FANOUT 32
+# endif
+#endif /* #else #ifdef CONFIG_RCU_FANOUT */
+
+#ifdef CONFIG_RCU_FANOUT_LEAF
+#define RCU_FANOUT_LEAF CONFIG_RCU_FANOUT_LEAF
+#else /* #ifdef CONFIG_RCU_FANOUT_LEAF */
+# ifdef CONFIG_64BIT
+# define RCU_FANOUT_LEAF 64
+# else
+# define RCU_FANOUT_LEAF 32
+# endif
+#endif /* #else #ifdef CONFIG_RCU_FANOUT_LEAF */
+
+#define RCU_FANOUT_1         (RCU_FANOUT_LEAF)
+#define RCU_FANOUT_2         (RCU_FANOUT_1 * RCU_FANOUT)
+#define RCU_FANOUT_3         (RCU_FANOUT_2 * RCU_FANOUT)
+#define RCU_FANOUT_4         (RCU_FANOUT_3 * RCU_FANOUT)
 
 #if NR_CPUS <= RCU_FANOUT_1
 #  define RCU_NUM_LVLS       1
@@ -170,7 +192,6 @@ struct rcu_node {
                                /*  if there is no such task.  If there */
                                /*  is no current expedited grace period, */
                                /*  then there can cannot be any such task. */
-#ifdef CONFIG_RCU_BOOST
        struct list_head *boost_tasks;
                                /* Pointer to first task that needs to be */
                                /*  priority boosted, or NULL if no priority */
@@ -208,7 +229,6 @@ struct rcu_node {
        unsigned long n_balk_nos;
                                /* Refused to boost: not sure why, though. */
                                /*  This can happen due to race conditions. */
-#endif /* #ifdef CONFIG_RCU_BOOST */
 #ifdef CONFIG_RCU_NOCB_CPU
        wait_queue_head_t nocb_gp_wq[2];
                                /* Place for rcu_nocb_kthread() to wait GP. */
@@ -519,14 +539,11 @@ extern struct list_head rcu_struct_flavors;
  * RCU implementation internal declarations:
  */
 extern struct rcu_state rcu_sched_state;
-DECLARE_PER_CPU(struct rcu_data, rcu_sched_data);
 
 extern struct rcu_state rcu_bh_state;
-DECLARE_PER_CPU(struct rcu_data, rcu_bh_data);
 
 #ifdef CONFIG_PREEMPT_RCU
 extern struct rcu_state rcu_preempt_state;
-DECLARE_PER_CPU(struct rcu_data, rcu_preempt_data);
 #endif /* #ifdef CONFIG_PREEMPT_RCU */
 
 #ifdef CONFIG_RCU_BOOST
index 8c0ec0f5a02702f1a3c5ed5db0bdf346ac7ae140..013485fb2b06b9f499d0673a36bf8f62d5e72607 100644 (file)
@@ -43,7 +43,17 @@ DEFINE_PER_CPU(unsigned int, rcu_cpu_kthread_status);
 DEFINE_PER_CPU(unsigned int, rcu_cpu_kthread_loops);
 DEFINE_PER_CPU(char, rcu_cpu_has_work);
 
-#endif /* #ifdef CONFIG_RCU_BOOST */
+#else /* #ifdef CONFIG_RCU_BOOST */
+
+/*
+ * Some architectures do not define rt_mutexes, but if !CONFIG_RCU_BOOST,
+ * all uses are in dead code.  Provide a definition to keep the compiler
+ * happy, but add WARN_ON_ONCE() to complain if used in the wrong place.
+ * This probably needs to be excluded from -rt builds.
+ */
+#define rt_mutex_owner(a) ({ WARN_ON_ONCE(1); NULL; })
+
+#endif /* #else #ifdef CONFIG_RCU_BOOST */
 
 #ifdef CONFIG_RCU_NOCB_CPU
 static cpumask_var_t rcu_nocb_mask; /* CPUs to have callbacks offloaded. */
@@ -60,11 +70,11 @@ static void __init rcu_bootup_announce_oddness(void)
 {
        if (IS_ENABLED(CONFIG_RCU_TRACE))
                pr_info("\tRCU debugfs-based tracing is enabled.\n");
-       if ((IS_ENABLED(CONFIG_64BIT) && CONFIG_RCU_FANOUT != 64) ||
-           (!IS_ENABLED(CONFIG_64BIT) && CONFIG_RCU_FANOUT != 32))
+       if ((IS_ENABLED(CONFIG_64BIT) && RCU_FANOUT != 64) ||
+           (!IS_ENABLED(CONFIG_64BIT) && RCU_FANOUT != 32))
                pr_info("\tCONFIG_RCU_FANOUT set to non-default value of %d\n",
-                      CONFIG_RCU_FANOUT);
-       if (IS_ENABLED(CONFIG_RCU_FANOUT_EXACT))
+                      RCU_FANOUT);
+       if (rcu_fanout_exact)
                pr_info("\tHierarchical RCU autobalancing is disabled.\n");
        if (IS_ENABLED(CONFIG_RCU_FAST_NO_HZ))
                pr_info("\tRCU dyntick-idle grace-period acceleration is enabled.\n");
@@ -76,10 +86,10 @@ static void __init rcu_bootup_announce_oddness(void)
                pr_info("\tAdditional per-CPU info printed with stalls.\n");
        if (NUM_RCU_LVL_4 != 0)
                pr_info("\tFour-level hierarchy is enabled.\n");
-       if (CONFIG_RCU_FANOUT_LEAF != 16)
+       if (RCU_FANOUT_LEAF != 16)
                pr_info("\tBuild-time adjustment of leaf fanout to %d.\n",
-                       CONFIG_RCU_FANOUT_LEAF);
-       if (rcu_fanout_leaf != CONFIG_RCU_FANOUT_LEAF)
+                       RCU_FANOUT_LEAF);
+       if (rcu_fanout_leaf != RCU_FANOUT_LEAF)
                pr_info("\tBoot-time adjustment of leaf fanout to %d.\n", rcu_fanout_leaf);
        if (nr_cpu_ids != NR_CPUS)
                pr_info("\tRCU restricting CPUs from NR_CPUS=%d to nr_cpu_ids=%d.\n", NR_CPUS, nr_cpu_ids);
@@ -90,7 +100,8 @@ static void __init rcu_bootup_announce_oddness(void)
 #ifdef CONFIG_PREEMPT_RCU
 
 RCU_STATE_INITIALIZER(rcu_preempt, 'p', call_rcu);
-static struct rcu_state *rcu_state_p = &rcu_preempt_state;
+static struct rcu_state *const rcu_state_p = &rcu_preempt_state;
+static struct rcu_data __percpu *const rcu_data_p = &rcu_preempt_data;
 
 static int rcu_preempted_readers_exp(struct rcu_node *rnp);
 static void rcu_report_exp_rnp(struct rcu_state *rsp, struct rcu_node *rnp,
@@ -116,11 +127,11 @@ static void __init rcu_bootup_announce(void)
  */
 static void rcu_preempt_qs(void)
 {
-       if (!__this_cpu_read(rcu_preempt_data.passed_quiesce)) {
+       if (!__this_cpu_read(rcu_data_p->passed_quiesce)) {
                trace_rcu_grace_period(TPS("rcu_preempt"),
-                                      __this_cpu_read(rcu_preempt_data.gpnum),
+                                      __this_cpu_read(rcu_data_p->gpnum),
                                       TPS("cpuqs"));
-               __this_cpu_write(rcu_preempt_data.passed_quiesce, 1);
+               __this_cpu_write(rcu_data_p->passed_quiesce, 1);
                barrier(); /* Coordinate with rcu_preempt_check_callbacks(). */
                current->rcu_read_unlock_special.b.need_qs = false;
        }
@@ -150,7 +161,7 @@ static void rcu_preempt_note_context_switch(void)
            !t->rcu_read_unlock_special.b.blocked) {
 
                /* Possibly blocking in an RCU read-side critical section. */
-               rdp = this_cpu_ptr(rcu_preempt_state.rda);
+               rdp = this_cpu_ptr(rcu_state_p->rda);
                rnp = rdp->mynode;
                raw_spin_lock_irqsave(&rnp->lock, flags);
                smp_mb__after_unlock_lock();
@@ -180,10 +191,9 @@ static void rcu_preempt_note_context_switch(void)
                if ((rnp->qsmask & rdp->grpmask) && rnp->gp_tasks != NULL) {
                        list_add(&t->rcu_node_entry, rnp->gp_tasks->prev);
                        rnp->gp_tasks = &t->rcu_node_entry;
-#ifdef CONFIG_RCU_BOOST
-                       if (rnp->boost_tasks != NULL)
+                       if (IS_ENABLED(CONFIG_RCU_BOOST) &&
+                           rnp->boost_tasks != NULL)
                                rnp->boost_tasks = rnp->gp_tasks;
-#endif /* #ifdef CONFIG_RCU_BOOST */
                } else {
                        list_add(&t->rcu_node_entry, &rnp->blkd_tasks);
                        if (rnp->qsmask & rdp->grpmask)
@@ -263,9 +273,7 @@ void rcu_read_unlock_special(struct task_struct *t)
        bool empty_exp_now;
        unsigned long flags;
        struct list_head *np;
-#ifdef CONFIG_RCU_BOOST
        bool drop_boost_mutex = false;
-#endif /* #ifdef CONFIG_RCU_BOOST */
        struct rcu_node *rnp;
        union rcu_special special;
 
@@ -307,9 +315,11 @@ void rcu_read_unlock_special(struct task_struct *t)
                t->rcu_read_unlock_special.b.blocked = false;
 
                /*
-                * Remove this task from the list it blocked on.  The
-                * task can migrate while we acquire the lock, but at
-                * most one time.  So at most two passes through loop.
+                * Remove this task from the list it blocked on.  The task
+                * now remains queued on the rcu_node corresponding to
+                * the CPU it first blocked on, so the first attempt to
+                * acquire the task's rcu_node's ->lock will succeed.
+                * Keep the loop and add a WARN_ON() out of sheer paranoia.
                 */
                for (;;) {
                        rnp = t->rcu_blocked_node;
@@ -317,6 +327,7 @@ void rcu_read_unlock_special(struct task_struct *t)
                        smp_mb__after_unlock_lock();
                        if (rnp == t->rcu_blocked_node)
                                break;
+                       WARN_ON_ONCE(1);
                        raw_spin_unlock(&rnp->lock); /* irqs remain disabled. */
                }
                empty_norm = !rcu_preempt_blocked_readers_cgp(rnp);
@@ -331,12 +342,12 @@ void rcu_read_unlock_special(struct task_struct *t)
                        rnp->gp_tasks = np;
                if (&t->rcu_node_entry == rnp->exp_tasks)
                        rnp->exp_tasks = np;
-#ifdef CONFIG_RCU_BOOST
-               if (&t->rcu_node_entry == rnp->boost_tasks)
-                       rnp->boost_tasks = np;
-               /* Snapshot ->boost_mtx ownership with rcu_node lock held. */
-               drop_boost_mutex = rt_mutex_owner(&rnp->boost_mtx) == t;
-#endif /* #ifdef CONFIG_RCU_BOOST */
+               if (IS_ENABLED(CONFIG_RCU_BOOST)) {
+                       if (&t->rcu_node_entry == rnp->boost_tasks)
+                               rnp->boost_tasks = np;
+                       /* Snapshot ->boost_mtx ownership w/rnp->lock held. */
+                       drop_boost_mutex = rt_mutex_owner(&rnp->boost_mtx) == t;
+               }
 
                /*
                 * If this was the last task on the current list, and if
@@ -353,24 +364,21 @@ void rcu_read_unlock_special(struct task_struct *t)
                                                         rnp->grplo,
                                                         rnp->grphi,
                                                         !!rnp->gp_tasks);
-                       rcu_report_unblock_qs_rnp(&rcu_preempt_state,
-                                                 rnp, flags);
+                       rcu_report_unblock_qs_rnp(rcu_state_p, rnp, flags);
                } else {
                        raw_spin_unlock_irqrestore(&rnp->lock, flags);
                }
 
-#ifdef CONFIG_RCU_BOOST
                /* Unboost if we were boosted. */
-               if (drop_boost_mutex)
+               if (IS_ENABLED(CONFIG_RCU_BOOST) && drop_boost_mutex)
                        rt_mutex_unlock(&rnp->boost_mtx);
-#endif /* #ifdef CONFIG_RCU_BOOST */
 
                /*
                 * If this was the last task on the expedited lists,
                 * then we need to report up the rcu_node hierarchy.
                 */
                if (!empty_exp && empty_exp_now)
-                       rcu_report_exp_rnp(&rcu_preempt_state, rnp, true);
+                       rcu_report_exp_rnp(rcu_state_p, rnp, true);
        } else {
                local_irq_restore(flags);
        }
@@ -390,7 +398,7 @@ static void rcu_print_detail_task_stall_rnp(struct rcu_node *rnp)
                raw_spin_unlock_irqrestore(&rnp->lock, flags);
                return;
        }
-       t = list_entry(rnp->gp_tasks,
+       t = list_entry(rnp->gp_tasks->prev,
                       struct task_struct, rcu_node_entry);
        list_for_each_entry_continue(t, &rnp->blkd_tasks, rcu_node_entry)
                sched_show_task(t);
@@ -447,7 +455,7 @@ static int rcu_print_task_stall(struct rcu_node *rnp)
        if (!rcu_preempt_blocked_readers_cgp(rnp))
                return 0;
        rcu_print_task_stall_begin(rnp);
-       t = list_entry(rnp->gp_tasks,
+       t = list_entry(rnp->gp_tasks->prev,
                       struct task_struct, rcu_node_entry);
        list_for_each_entry_continue(t, &rnp->blkd_tasks, rcu_node_entry) {
                pr_cont(" P%d", t->pid);
@@ -491,8 +499,8 @@ static void rcu_preempt_check_callbacks(void)
                return;
        }
        if (t->rcu_read_lock_nesting > 0 &&
-           __this_cpu_read(rcu_preempt_data.qs_pending) &&
-           !__this_cpu_read(rcu_preempt_data.passed_quiesce))
+           __this_cpu_read(rcu_data_p->qs_pending) &&
+           !__this_cpu_read(rcu_data_p->passed_quiesce))
                t->rcu_read_unlock_special.b.need_qs = true;
 }
 
@@ -500,7 +508,7 @@ static void rcu_preempt_check_callbacks(void)
 
 static void rcu_preempt_do_callbacks(void)
 {
-       rcu_do_batch(&rcu_preempt_state, this_cpu_ptr(&rcu_preempt_data));
+       rcu_do_batch(rcu_state_p, this_cpu_ptr(rcu_data_p));
 }
 
 #endif /* #ifdef CONFIG_RCU_BOOST */
@@ -510,7 +518,7 @@ static void rcu_preempt_do_callbacks(void)
  */
 void call_rcu(struct rcu_head *head, void (*func)(struct rcu_head *rcu))
 {
-       __call_rcu(head, func, &rcu_preempt_state, -1, 0);
+       __call_rcu(head, func, rcu_state_p, -1, 0);
 }
 EXPORT_SYMBOL_GPL(call_rcu);
 
@@ -570,7 +578,7 @@ static int rcu_preempted_readers_exp(struct rcu_node *rnp)
 static int sync_rcu_preempt_exp_done(struct rcu_node *rnp)
 {
        return !rcu_preempted_readers_exp(rnp) &&
-              ACCESS_ONCE(rnp->expmask) == 0;
+              READ_ONCE(rnp->expmask) == 0;
 }
 
 /*
@@ -711,12 +719,12 @@ sync_rcu_preempt_exp_init2(struct rcu_state *rsp, struct rcu_node *rnp)
 void synchronize_rcu_expedited(void)
 {
        struct rcu_node *rnp;
-       struct rcu_state *rsp = &rcu_preempt_state;
+       struct rcu_state *rsp = rcu_state_p;
        unsigned long snap;
        int trycount = 0;
 
        smp_mb(); /* Caller's modifications seen first by other CPUs. */
-       snap = ACCESS_ONCE(sync_rcu_preempt_exp_count) + 1;
+       snap = READ_ONCE(sync_rcu_preempt_exp_count) + 1;
        smp_mb(); /* Above access cannot bleed into critical section. */
 
        /*
@@ -740,7 +748,7 @@ void synchronize_rcu_expedited(void)
         */
        while (!mutex_trylock(&sync_rcu_preempt_exp_mutex)) {
                if (ULONG_CMP_LT(snap,
-                   ACCESS_ONCE(sync_rcu_preempt_exp_count))) {
+                   READ_ONCE(sync_rcu_preempt_exp_count))) {
                        put_online_cpus();
                        goto mb_ret; /* Others did our work for us. */
                }
@@ -752,7 +760,7 @@ void synchronize_rcu_expedited(void)
                        return;
                }
        }
-       if (ULONG_CMP_LT(snap, ACCESS_ONCE(sync_rcu_preempt_exp_count))) {
+       if (ULONG_CMP_LT(snap, READ_ONCE(sync_rcu_preempt_exp_count))) {
                put_online_cpus();
                goto unlock_mb_ret; /* Others did our work for us. */
        }
@@ -780,8 +788,7 @@ void synchronize_rcu_expedited(void)
 
        /* Clean up and exit. */
        smp_mb(); /* ensure expedited GP seen before counter increment. */
-       ACCESS_ONCE(sync_rcu_preempt_exp_count) =
-                                       sync_rcu_preempt_exp_count + 1;
+       WRITE_ONCE(sync_rcu_preempt_exp_count, sync_rcu_preempt_exp_count + 1);
 unlock_mb_ret:
        mutex_unlock(&sync_rcu_preempt_exp_mutex);
 mb_ret:
@@ -799,7 +806,7 @@ EXPORT_SYMBOL_GPL(synchronize_rcu_expedited);
  */
 void rcu_barrier(void)
 {
-       _rcu_barrier(&rcu_preempt_state);
+       _rcu_barrier(rcu_state_p);
 }
 EXPORT_SYMBOL_GPL(rcu_barrier);
 
@@ -808,7 +815,7 @@ EXPORT_SYMBOL_GPL(rcu_barrier);
  */
 static void __init __rcu_init_preempt(void)
 {
-       rcu_init_one(&rcu_preempt_state, &rcu_preempt_data);
+       rcu_init_one(rcu_state_p, rcu_data_p);
 }
 
 /*
@@ -831,7 +838,8 @@ void exit_rcu(void)
 
 #else /* #ifdef CONFIG_PREEMPT_RCU */
 
-static struct rcu_state *rcu_state_p = &rcu_sched_state;
+static struct rcu_state *const rcu_state_p = &rcu_sched_state;
+static struct rcu_data __percpu *const rcu_data_p = &rcu_sched_data;
 
 /*
  * Tell them what RCU they are running.
@@ -994,8 +1002,8 @@ static int rcu_boost(struct rcu_node *rnp)
        struct task_struct *t;
        struct list_head *tb;
 
-       if (ACCESS_ONCE(rnp->exp_tasks) == NULL &&
-           ACCESS_ONCE(rnp->boost_tasks) == NULL)
+       if (READ_ONCE(rnp->exp_tasks) == NULL &&
+           READ_ONCE(rnp->boost_tasks) == NULL)
                return 0;  /* Nothing left to boost. */
 
        raw_spin_lock_irqsave(&rnp->lock, flags);
@@ -1048,8 +1056,8 @@ static int rcu_boost(struct rcu_node *rnp)
        rt_mutex_lock(&rnp->boost_mtx);
        rt_mutex_unlock(&rnp->boost_mtx);  /* Then keep lockdep happy. */
 
-       return ACCESS_ONCE(rnp->exp_tasks) != NULL ||
-              ACCESS_ONCE(rnp->boost_tasks) != NULL;
+       return READ_ONCE(rnp->exp_tasks) != NULL ||
+              READ_ONCE(rnp->boost_tasks) != NULL;
 }
 
 /*
@@ -1173,7 +1181,7 @@ static int rcu_spawn_one_boost_kthread(struct rcu_state *rsp,
        struct sched_param sp;
        struct task_struct *t;
 
-       if (&rcu_preempt_state != rsp)
+       if (rcu_state_p != rsp)
                return 0;
 
        if (!rcu_scheduler_fully_active || rcu_rnp_online_cpus(rnp) == 0)
@@ -1367,13 +1375,12 @@ static void rcu_prepare_kthreads(int cpu)
  * Because we not have RCU_FAST_NO_HZ, just check whether this CPU needs
  * any flavor of RCU.
  */
-#ifndef CONFIG_RCU_NOCB_CPU_ALL
-int rcu_needs_cpu(unsigned long *delta_jiffies)
+int rcu_needs_cpu(u64 basemono, u64 *nextevt)
 {
-       *delta_jiffies = ULONG_MAX;
-       return rcu_cpu_has_callbacks(NULL);
+       *nextevt = KTIME_MAX;
+       return IS_ENABLED(CONFIG_RCU_NOCB_CPU_ALL)
+              ? 0 : rcu_cpu_has_callbacks(NULL);
 }
-#endif /* #ifndef CONFIG_RCU_NOCB_CPU_ALL */
 
 /*
  * Because we do not have RCU_FAST_NO_HZ, don't bother cleaning up
@@ -1432,8 +1439,6 @@ module_param(rcu_idle_gp_delay, int, 0644);
 static int rcu_idle_lazy_gp_delay = RCU_IDLE_LAZY_GP_DELAY;
 module_param(rcu_idle_lazy_gp_delay, int, 0644);
 
-extern int tick_nohz_active;
-
 /*
  * Try to advance callbacks for all flavors of RCU on the current CPU, but
  * only if it has been awhile since the last time we did so.  Afterwards,
@@ -1462,7 +1467,7 @@ static bool __maybe_unused rcu_try_advance_all_cbs(void)
                 * callbacks not yet ready to invoke.
                 */
                if ((rdp->completed != rnp->completed ||
-                    unlikely(ACCESS_ONCE(rdp->gpwrap))) &&
+                    unlikely(READ_ONCE(rdp->gpwrap))) &&
                    rdp->nxttail[RCU_DONE_TAIL] != rdp->nxttail[RCU_NEXT_TAIL])
                        note_gp_changes(rsp, rdp);
 
@@ -1480,17 +1485,22 @@ static bool __maybe_unused rcu_try_advance_all_cbs(void)
  *
  * The caller must have disabled interrupts.
  */
-#ifndef CONFIG_RCU_NOCB_CPU_ALL
-int rcu_needs_cpu(unsigned long *dj)
+int rcu_needs_cpu(u64 basemono, u64 *nextevt)
 {
        struct rcu_dynticks *rdtp = this_cpu_ptr(&rcu_dynticks);
+       unsigned long dj;
+
+       if (IS_ENABLED(CONFIG_RCU_NOCB_CPU_ALL)) {
+               *nextevt = KTIME_MAX;
+               return 0;
+       }
 
        /* Snapshot to detect later posting of non-lazy callback. */
        rdtp->nonlazy_posted_snap = rdtp->nonlazy_posted;
 
        /* If no callbacks, RCU doesn't need the CPU. */
        if (!rcu_cpu_has_callbacks(&rdtp->all_lazy)) {
-               *dj = ULONG_MAX;
+               *nextevt = KTIME_MAX;
                return 0;
        }
 
@@ -1504,14 +1514,14 @@ int rcu_needs_cpu(unsigned long *dj)
 
        /* Request timer delay depending on laziness, and round. */
        if (!rdtp->all_lazy) {
-               *dj = round_up(rcu_idle_gp_delay + jiffies,
+               dj = round_up(rcu_idle_gp_delay + jiffies,
                               rcu_idle_gp_delay) - jiffies;
        } else {
-               *dj = round_jiffies(rcu_idle_lazy_gp_delay + jiffies) - jiffies;
+               dj = round_jiffies(rcu_idle_lazy_gp_delay + jiffies) - jiffies;
        }
+       *nextevt = basemono + dj * TICK_NSEC;
        return 0;
 }
-#endif /* #ifndef CONFIG_RCU_NOCB_CPU_ALL */
 
 /*
  * Prepare a CPU for idle from an RCU perspective.  The first major task
@@ -1525,7 +1535,6 @@ int rcu_needs_cpu(unsigned long *dj)
  */
 static void rcu_prepare_for_idle(void)
 {
-#ifndef CONFIG_RCU_NOCB_CPU_ALL
        bool needwake;
        struct rcu_data *rdp;
        struct rcu_dynticks *rdtp = this_cpu_ptr(&rcu_dynticks);
@@ -1533,8 +1542,11 @@ static void rcu_prepare_for_idle(void)
        struct rcu_state *rsp;
        int tne;
 
+       if (IS_ENABLED(CONFIG_RCU_NOCB_CPU_ALL))
+               return;
+
        /* Handle nohz enablement switches conservatively. */
-       tne = ACCESS_ONCE(tick_nohz_active);
+       tne = READ_ONCE(tick_nohz_active);
        if (tne != rdtp->tick_nohz_enabled_snap) {
                if (rcu_cpu_has_callbacks(NULL))
                        invoke_rcu_core(); /* force nohz to see update. */
@@ -1580,7 +1592,6 @@ static void rcu_prepare_for_idle(void)
                if (needwake)
                        rcu_gp_kthread_wake(rsp);
        }
-#endif /* #ifndef CONFIG_RCU_NOCB_CPU_ALL */
 }
 
 /*
@@ -1590,12 +1601,11 @@ static void rcu_prepare_for_idle(void)
  */
 static void rcu_cleanup_after_idle(void)
 {
-#ifndef CONFIG_RCU_NOCB_CPU_ALL
-       if (rcu_is_nocb_cpu(smp_processor_id()))
+       if (IS_ENABLED(CONFIG_RCU_NOCB_CPU_ALL) ||
+           rcu_is_nocb_cpu(smp_processor_id()))
                return;
        if (rcu_try_advance_all_cbs())
                invoke_rcu_core();
-#endif /* #ifndef CONFIG_RCU_NOCB_CPU_ALL */
 }
 
 /*
@@ -1760,7 +1770,7 @@ static void print_cpu_stall_info(struct rcu_state *rsp, int cpu)
               atomic_read(&rdtp->dynticks) & 0xfff,
               rdtp->dynticks_nesting, rdtp->dynticks_nmi_nesting,
               rdp->softirq_snap, kstat_softirqs_cpu(RCU_SOFTIRQ, cpu),
-              ACCESS_ONCE(rsp->n_force_qs) - rsp->n_force_qs_gpstart,
+              READ_ONCE(rsp->n_force_qs) - rsp->n_force_qs_gpstart,
               fast_no_hz);
 }
 
@@ -1898,11 +1908,11 @@ static void wake_nocb_leader(struct rcu_data *rdp, bool force)
 {
        struct rcu_data *rdp_leader = rdp->nocb_leader;
 
-       if (!ACCESS_ONCE(rdp_leader->nocb_kthread))
+       if (!READ_ONCE(rdp_leader->nocb_kthread))
                return;
-       if (ACCESS_ONCE(rdp_leader->nocb_leader_sleep) || force) {
+       if (READ_ONCE(rdp_leader->nocb_leader_sleep) || force) {
                /* Prior smp_mb__after_atomic() orders against prior enqueue. */
-               ACCESS_ONCE(rdp_leader->nocb_leader_sleep) = false;
+               WRITE_ONCE(rdp_leader->nocb_leader_sleep, false);
                wake_up(&rdp_leader->nocb_wq);
        }
 }
@@ -1934,14 +1944,14 @@ static bool rcu_nocb_cpu_needs_barrier(struct rcu_state *rsp, int cpu)
        ret = atomic_long_read(&rdp->nocb_q_count);
 
 #ifdef CONFIG_PROVE_RCU
-       rhp = ACCESS_ONCE(rdp->nocb_head);
+       rhp = READ_ONCE(rdp->nocb_head);
        if (!rhp)
-               rhp = ACCESS_ONCE(rdp->nocb_gp_head);
+               rhp = READ_ONCE(rdp->nocb_gp_head);
        if (!rhp)
-               rhp = ACCESS_ONCE(rdp->nocb_follower_head);
+               rhp = READ_ONCE(rdp->nocb_follower_head);
 
        /* Having no rcuo kthread but CBs after scheduler starts is bad! */
-       if (!ACCESS_ONCE(rdp->nocb_kthread) && rhp &&
+       if (!READ_ONCE(rdp->nocb_kthread) && rhp &&
            rcu_scheduler_fully_active) {
                /* RCU callback enqueued before CPU first came online??? */
                pr_err("RCU: Never-onlined no-CBs CPU %d has CB %p\n",
@@ -1975,12 +1985,12 @@ static void __call_rcu_nocb_enqueue(struct rcu_data *rdp,
        atomic_long_add(rhcount, &rdp->nocb_q_count);
        /* rcu_barrier() relies on ->nocb_q_count add before xchg. */
        old_rhpp = xchg(&rdp->nocb_tail, rhtp);
-       ACCESS_ONCE(*old_rhpp) = rhp;
+       WRITE_ONCE(*old_rhpp, rhp);
        atomic_long_add(rhcount_lazy, &rdp->nocb_q_count_lazy);
        smp_mb__after_atomic(); /* Store *old_rhpp before _wake test. */
 
        /* If we are not being polled and there is a kthread, awaken it ... */
-       t = ACCESS_ONCE(rdp->nocb_kthread);
+       t = READ_ONCE(rdp->nocb_kthread);
        if (rcu_nocb_poll || !t) {
                trace_rcu_nocb_wake(rdp->rsp->name, rdp->cpu,
                                    TPS("WakeNotPoll"));
@@ -2118,7 +2128,7 @@ static void rcu_nocb_wait_gp(struct rcu_data *rdp)
        for (;;) {
                wait_event_interruptible(
                        rnp->nocb_gp_wq[c & 0x1],
-                       (d = ULONG_CMP_GE(ACCESS_ONCE(rnp->completed), c)));
+                       (d = ULONG_CMP_GE(READ_ONCE(rnp->completed), c)));
                if (likely(d))
                        break;
                WARN_ON(signal_pending(current));
@@ -2145,7 +2155,7 @@ wait_again:
        if (!rcu_nocb_poll) {
                trace_rcu_nocb_wake(my_rdp->rsp->name, my_rdp->cpu, "Sleep");
                wait_event_interruptible(my_rdp->nocb_wq,
-                               !ACCESS_ONCE(my_rdp->nocb_leader_sleep));
+                               !READ_ONCE(my_rdp->nocb_leader_sleep));
                /* Memory barrier handled by smp_mb() calls below and repoll. */
        } else if (firsttime) {
                firsttime = false; /* Don't drown trace log with "Poll"! */
@@ -2159,12 +2169,12 @@ wait_again:
         */
        gotcbs = false;
        for (rdp = my_rdp; rdp; rdp = rdp->nocb_next_follower) {
-               rdp->nocb_gp_head = ACCESS_ONCE(rdp->nocb_head);
+               rdp->nocb_gp_head = READ_ONCE(rdp->nocb_head);
                if (!rdp->nocb_gp_head)
                        continue;  /* No CBs here, try next follower. */
 
                /* Move callbacks to wait-for-GP list, which is empty. */
-               ACCESS_ONCE(rdp->nocb_head) = NULL;
+               WRITE_ONCE(rdp->nocb_head, NULL);
                rdp->nocb_gp_tail = xchg(&rdp->nocb_tail, &rdp->nocb_head);
                gotcbs = true;
        }
@@ -2184,7 +2194,7 @@ wait_again:
                my_rdp->nocb_leader_sleep = true;
                smp_mb();  /* Ensure _sleep true before scan. */
                for (rdp = my_rdp; rdp; rdp = rdp->nocb_next_follower)
-                       if (ACCESS_ONCE(rdp->nocb_head)) {
+                       if (READ_ONCE(rdp->nocb_head)) {
                                /* Found CB, so short-circuit next wait. */
                                my_rdp->nocb_leader_sleep = false;
                                break;
@@ -2205,7 +2215,7 @@ wait_again:
 
        /* Each pass through the following loop wakes a follower, if needed. */
        for (rdp = my_rdp; rdp; rdp = rdp->nocb_next_follower) {
-               if (ACCESS_ONCE(rdp->nocb_head))
+               if (READ_ONCE(rdp->nocb_head))
                        my_rdp->nocb_leader_sleep = false;/* No need to sleep.*/
                if (!rdp->nocb_gp_head)
                        continue; /* No CBs, so no need to wake follower. */
@@ -2241,7 +2251,7 @@ static void nocb_follower_wait(struct rcu_data *rdp)
                        trace_rcu_nocb_wake(rdp->rsp->name, rdp->cpu,
                                            "FollowerSleep");
                        wait_event_interruptible(rdp->nocb_wq,
-                                                ACCESS_ONCE(rdp->nocb_follower_head));
+                                                READ_ONCE(rdp->nocb_follower_head));
                } else if (firsttime) {
                        /* Don't drown trace log with "Poll"! */
                        firsttime = false;
@@ -2282,10 +2292,10 @@ static int rcu_nocb_kthread(void *arg)
                        nocb_follower_wait(rdp);
 
                /* Pull the ready-to-invoke callbacks onto local list. */
-               list = ACCESS_ONCE(rdp->nocb_follower_head);
+               list = READ_ONCE(rdp->nocb_follower_head);
                BUG_ON(!list);
                trace_rcu_nocb_wake(rdp->rsp->name, rdp->cpu, "WokeNonEmpty");
-               ACCESS_ONCE(rdp->nocb_follower_head) = NULL;
+               WRITE_ONCE(rdp->nocb_follower_head, NULL);
                tail = xchg(&rdp->nocb_follower_tail, &rdp->nocb_follower_head);
 
                /* Each pass through the following loop invokes a callback. */
@@ -2324,7 +2334,7 @@ static int rcu_nocb_kthread(void *arg)
 /* Is a deferred wakeup of rcu_nocb_kthread() required? */
 static int rcu_nocb_need_deferred_wakeup(struct rcu_data *rdp)
 {
-       return ACCESS_ONCE(rdp->nocb_defer_wakeup);
+       return READ_ONCE(rdp->nocb_defer_wakeup);
 }
 
 /* Do a deferred wakeup of rcu_nocb_kthread(). */
@@ -2334,8 +2344,8 @@ static void do_nocb_deferred_wakeup(struct rcu_data *rdp)
 
        if (!rcu_nocb_need_deferred_wakeup(rdp))
                return;
-       ndw = ACCESS_ONCE(rdp->nocb_defer_wakeup);
-       ACCESS_ONCE(rdp->nocb_defer_wakeup) = RCU_NOGP_WAKE_NOT;
+       ndw = READ_ONCE(rdp->nocb_defer_wakeup);
+       WRITE_ONCE(rdp->nocb_defer_wakeup, RCU_NOGP_WAKE_NOT);
        wake_nocb_leader(rdp, ndw == RCU_NOGP_WAKE_FORCE);
        trace_rcu_nocb_wake(rdp->rsp->name, rdp->cpu, TPS("DeferredWake"));
 }
@@ -2448,7 +2458,7 @@ static void rcu_spawn_one_nocb_kthread(struct rcu_state *rsp, int cpu)
        t = kthread_run(rcu_nocb_kthread, rdp_spawn,
                        "rcuo%c/%d", rsp->abbr, cpu);
        BUG_ON(IS_ERR(t));
-       ACCESS_ONCE(rdp_spawn->nocb_kthread) = t;
+       WRITE_ONCE(rdp_spawn->nocb_kthread, t);
 }
 
 /*
@@ -2663,7 +2673,7 @@ static void rcu_sysidle_enter(int irq)
 
        /* Record start of fully idle period. */
        j = jiffies;
-       ACCESS_ONCE(rdtp->dynticks_idle_jiffies) = j;
+       WRITE_ONCE(rdtp->dynticks_idle_jiffies, j);
        smp_mb__before_atomic();
        atomic_inc(&rdtp->dynticks_idle);
        smp_mb__after_atomic();
@@ -2681,7 +2691,7 @@ static void rcu_sysidle_enter(int irq)
  */
 void rcu_sysidle_force_exit(void)
 {
-       int oldstate = ACCESS_ONCE(full_sysidle_state);
+       int oldstate = READ_ONCE(full_sysidle_state);
        int newoldstate;
 
        /*
@@ -2794,7 +2804,7 @@ static void rcu_sysidle_check_cpu(struct rcu_data *rdp, bool *isidle,
        smp_mb(); /* Read counters before timestamps. */
 
        /* Pick up timestamps. */
-       j = ACCESS_ONCE(rdtp->dynticks_idle_jiffies);
+       j = READ_ONCE(rdtp->dynticks_idle_jiffies);
        /* If this CPU entered idle more recently, update maxj timestamp. */
        if (ULONG_CMP_LT(*maxj, j))
                *maxj = j;
@@ -2831,11 +2841,11 @@ static unsigned long rcu_sysidle_delay(void)
 static void rcu_sysidle(unsigned long j)
 {
        /* Check the current state. */
-       switch (ACCESS_ONCE(full_sysidle_state)) {
+       switch (READ_ONCE(full_sysidle_state)) {
        case RCU_SYSIDLE_NOT:
 
                /* First time all are idle, so note a short idle period. */
-               ACCESS_ONCE(full_sysidle_state) = RCU_SYSIDLE_SHORT;
+               WRITE_ONCE(full_sysidle_state, RCU_SYSIDLE_SHORT);
                break;
 
        case RCU_SYSIDLE_SHORT:
@@ -2873,7 +2883,7 @@ static void rcu_sysidle_cancel(void)
 {
        smp_mb();
        if (full_sysidle_state > RCU_SYSIDLE_SHORT)
-               ACCESS_ONCE(full_sysidle_state) = RCU_SYSIDLE_NOT;
+               WRITE_ONCE(full_sysidle_state, RCU_SYSIDLE_NOT);
 }
 
 /*
@@ -2925,7 +2935,7 @@ static void rcu_sysidle_cb(struct rcu_head *rhp)
        smp_mb();  /* grace period precedes setting inuse. */
 
        rshp = container_of(rhp, struct rcu_sysidle_head, rh);
-       ACCESS_ONCE(rshp->inuse) = 0;
+       WRITE_ONCE(rshp->inuse, 0);
 }
 
 /*
@@ -2936,7 +2946,7 @@ static void rcu_sysidle_cb(struct rcu_head *rhp)
 bool rcu_sys_is_idle(void)
 {
        static struct rcu_sysidle_head rsh;
-       int rss = ACCESS_ONCE(full_sysidle_state);
+       int rss = READ_ONCE(full_sysidle_state);
 
        if (WARN_ON_ONCE(smp_processor_id() != tick_do_timer_cpu))
                return false;
@@ -2964,7 +2974,7 @@ bool rcu_sys_is_idle(void)
                        }
                        rcu_sysidle_report(rcu_state_p, isidle, maxj, false);
                        oldrss = rss;
-                       rss = ACCESS_ONCE(full_sysidle_state);
+                       rss = READ_ONCE(full_sysidle_state);
                }
        }
 
@@ -3048,10 +3058,10 @@ static bool rcu_nohz_full_cpu(struct rcu_state *rsp)
 #ifdef CONFIG_NO_HZ_FULL
        if (tick_nohz_full_cpu(smp_processor_id()) &&
            (!rcu_gp_in_progress(rsp) ||
-            ULONG_CMP_LT(jiffies, ACCESS_ONCE(rsp->gp_start) + HZ)))
-               return 1;
+            ULONG_CMP_LT(jiffies, READ_ONCE(rsp->gp_start) + HZ)))
+               return true;
 #endif /* #ifdef CONFIG_NO_HZ_FULL */
-       return 0;
+       return false;
 }
 
 /*
@@ -3077,7 +3087,7 @@ static void rcu_bind_gp_kthread(void)
 static void rcu_dynticks_task_enter(void)
 {
 #if defined(CONFIG_TASKS_RCU) && defined(CONFIG_NO_HZ_FULL)
-       ACCESS_ONCE(current->rcu_tasks_idle_cpu) = smp_processor_id();
+       WRITE_ONCE(current->rcu_tasks_idle_cpu, smp_processor_id());
 #endif /* #if defined(CONFIG_TASKS_RCU) && defined(CONFIG_NO_HZ_FULL) */
 }
 
@@ -3085,6 +3095,6 @@ static void rcu_dynticks_task_enter(void)
 static void rcu_dynticks_task_exit(void)
 {
 #if defined(CONFIG_TASKS_RCU) && defined(CONFIG_NO_HZ_FULL)
-       ACCESS_ONCE(current->rcu_tasks_idle_cpu) = -1;
+       WRITE_ONCE(current->rcu_tasks_idle_cpu, -1);
 #endif /* #if defined(CONFIG_TASKS_RCU) && defined(CONFIG_NO_HZ_FULL) */
 }
index f92361efd0f55d970d851604d34800e7d109ecc8..3ea7ffc7d5c4a75378899d87c805314864804b51 100644 (file)
@@ -277,7 +277,7 @@ static void print_one_rcu_state(struct seq_file *m, struct rcu_state *rsp)
        seq_printf(m, "nfqs=%lu/nfqsng=%lu(%lu) fqlh=%lu oqlen=%ld/%ld\n",
                   rsp->n_force_qs, rsp->n_force_qs_ngp,
                   rsp->n_force_qs - rsp->n_force_qs_ngp,
-                  ACCESS_ONCE(rsp->n_force_qs_lh), rsp->qlen_lazy, rsp->qlen);
+                  READ_ONCE(rsp->n_force_qs_lh), rsp->qlen_lazy, rsp->qlen);
        for (rnp = &rsp->node[0]; rnp - &rsp->node[0] < rcu_num_nodes; rnp++) {
                if (rnp->level != level) {
                        seq_puts(m, "\n");
@@ -323,8 +323,8 @@ static void show_one_rcugp(struct seq_file *m, struct rcu_state *rsp)
        struct rcu_node *rnp = &rsp->node[0];
 
        raw_spin_lock_irqsave(&rnp->lock, flags);
-       completed = ACCESS_ONCE(rsp->completed);
-       gpnum = ACCESS_ONCE(rsp->gpnum);
+       completed = READ_ONCE(rsp->completed);
+       gpnum = READ_ONCE(rsp->gpnum);
        if (completed == gpnum)
                gpage = 0;
        else
index 1f133350da01e360bc6048b3a458e8b8cc0bdefc..afaecb7a799af235f63afb6877050cf348e4247c 100644 (file)
@@ -150,14 +150,14 @@ void __rcu_read_unlock(void)
                barrier();  /* critical section before exit code. */
                t->rcu_read_lock_nesting = INT_MIN;
                barrier();  /* assign before ->rcu_read_unlock_special load */
-               if (unlikely(ACCESS_ONCE(t->rcu_read_unlock_special.s)))
+               if (unlikely(READ_ONCE(t->rcu_read_unlock_special.s)))
                        rcu_read_unlock_special(t);
                barrier();  /* ->rcu_read_unlock_special load before assign */
                t->rcu_read_lock_nesting = 0;
        }
 #ifdef CONFIG_PROVE_LOCKING
        {
-               int rrln = ACCESS_ONCE(t->rcu_read_lock_nesting);
+               int rrln = READ_ONCE(t->rcu_read_lock_nesting);
 
                WARN_ON_ONCE(rrln < 0 && rrln > INT_MIN / 2);
        }
@@ -389,17 +389,17 @@ module_param(rcu_cpu_stall_timeout, int, 0644);
 
 int rcu_jiffies_till_stall_check(void)
 {
-       int till_stall_check = ACCESS_ONCE(rcu_cpu_stall_timeout);
+       int till_stall_check = READ_ONCE(rcu_cpu_stall_timeout);
 
        /*
         * Limit check must be consistent with the Kconfig limits
         * for CONFIG_RCU_CPU_STALL_TIMEOUT.
         */
        if (till_stall_check < 3) {
-               ACCESS_ONCE(rcu_cpu_stall_timeout) = 3;
+               WRITE_ONCE(rcu_cpu_stall_timeout, 3);
                till_stall_check = 3;
        } else if (till_stall_check > 300) {
-               ACCESS_ONCE(rcu_cpu_stall_timeout) = 300;
+               WRITE_ONCE(rcu_cpu_stall_timeout, 300);
                till_stall_check = 300;
        }
        return till_stall_check * HZ + RCU_STALL_DELAY_DELTA;
@@ -550,12 +550,12 @@ static void check_holdout_task(struct task_struct *t,
 {
        int cpu;
 
-       if (!ACCESS_ONCE(t->rcu_tasks_holdout) ||
-           t->rcu_tasks_nvcsw != ACCESS_ONCE(t->nvcsw) ||
-           !ACCESS_ONCE(t->on_rq) ||
+       if (!READ_ONCE(t->rcu_tasks_holdout) ||
+           t->rcu_tasks_nvcsw != READ_ONCE(t->nvcsw) ||
+           !READ_ONCE(t->on_rq) ||
            (IS_ENABLED(CONFIG_NO_HZ_FULL) &&
             !is_idle_task(t) && t->rcu_tasks_idle_cpu >= 0)) {
-               ACCESS_ONCE(t->rcu_tasks_holdout) = false;
+               WRITE_ONCE(t->rcu_tasks_holdout, false);
                list_del_init(&t->rcu_tasks_holdout_list);
                put_task_struct(t);
                return;
@@ -639,11 +639,11 @@ static int __noreturn rcu_tasks_kthread(void *arg)
                 */
                rcu_read_lock();
                for_each_process_thread(g, t) {
-                       if (t != current && ACCESS_ONCE(t->on_rq) &&
+                       if (t != current && READ_ONCE(t->on_rq) &&
                            !is_idle_task(t)) {
                                get_task_struct(t);
-                               t->rcu_tasks_nvcsw = ACCESS_ONCE(t->nvcsw);
-                               ACCESS_ONCE(t->rcu_tasks_holdout) = true;
+                               t->rcu_tasks_nvcsw = READ_ONCE(t->nvcsw);
+                               WRITE_ONCE(t->rcu_tasks_holdout, true);
                                list_add(&t->rcu_tasks_holdout_list,
                                         &rcu_tasks_holdouts);
                        }
@@ -672,7 +672,7 @@ static int __noreturn rcu_tasks_kthread(void *arg)
                        struct task_struct *t1;
 
                        schedule_timeout_interruptible(HZ);
-                       rtst = ACCESS_ONCE(rcu_task_stall_timeout);
+                       rtst = READ_ONCE(rcu_task_stall_timeout);
                        needreport = rtst > 0 &&
                                     time_after(jiffies, lastreport + rtst);
                        if (needreport)
@@ -728,7 +728,7 @@ static void rcu_spawn_tasks_kthread(void)
        static struct task_struct *rcu_tasks_kthread_ptr;
        struct task_struct *t;
 
-       if (ACCESS_ONCE(rcu_tasks_kthread_ptr)) {
+       if (READ_ONCE(rcu_tasks_kthread_ptr)) {
                smp_mb(); /* Ensure caller sees full kthread. */
                return;
        }
@@ -740,7 +740,7 @@ static void rcu_spawn_tasks_kthread(void)
        t = kthread_run(rcu_tasks_kthread, NULL, "rcu_tasks_kthread");
        BUG_ON(IS_ERR(t));
        smp_mb(); /* Ensure others see full kthread. */
-       ACCESS_ONCE(rcu_tasks_kthread_ptr) = t;
+       WRITE_ONCE(rcu_tasks_kthread_ptr, t);
        mutex_unlock(&rcu_tasks_kthread_mutex);
 }
 
index af0a5a6cee9858475d2baa7057694edf71330708..c86935a7f1f813664476d311ec43d115efdd2a30 100644 (file)
 #define CREATE_TRACE_POINTS
 #include <trace/events/sched.h>
 
-void start_bandwidth_timer(struct hrtimer *period_timer, ktime_t period)
-{
-       unsigned long delta;
-       ktime_t soft, hard, now;
-
-       for (;;) {
-               if (hrtimer_active(period_timer))
-                       break;
-
-               now = hrtimer_cb_get_time(period_timer);
-               hrtimer_forward(period_timer, now, period);
-
-               soft = hrtimer_get_softexpires(period_timer);
-               hard = hrtimer_get_expires(period_timer);
-               delta = ktime_to_ns(ktime_sub(hard, soft));
-               __hrtimer_start_range_ns(period_timer, soft, delta,
-                                        HRTIMER_MODE_ABS_PINNED, 0);
-       }
-}
-
 DEFINE_MUTEX(sched_domains_mutex);
 DEFINE_PER_CPU_SHARED_ALIGNED(struct rq, runqueues);
 
@@ -355,12 +335,11 @@ static enum hrtimer_restart hrtick(struct hrtimer *timer)
 
 #ifdef CONFIG_SMP
 
-static int __hrtick_restart(struct rq *rq)
+static void __hrtick_restart(struct rq *rq)
 {
        struct hrtimer *timer = &rq->hrtick_timer;
-       ktime_t time = hrtimer_get_softexpires(timer);
 
-       return __hrtimer_start_range_ns(timer, time, 0, HRTIMER_MODE_ABS_PINNED, 0);
+       hrtimer_start_expires(timer, HRTIMER_MODE_ABS_PINNED);
 }
 
 /*
@@ -440,8 +419,8 @@ void hrtick_start(struct rq *rq, u64 delay)
         * doesn't make sense. Rely on vruntime for fairness.
         */
        delay = max_t(u64, delay, 10000LL);
-       __hrtimer_start_range_ns(&rq->hrtick_timer, ns_to_ktime(delay), 0,
-                       HRTIMER_MODE_REL_PINNED, 0);
+       hrtimer_start(&rq->hrtick_timer, ns_to_ktime(delay),
+                     HRTIMER_MODE_REL_PINNED);
 }
 
 static inline void init_hrtick(void)
@@ -639,13 +618,12 @@ void resched_cpu(int cpu)
  * selecting an idle cpu will add more delays to the timers than intended
  * (as that cpu's timer base may not be uptodate wrt jiffies etc).
  */
-int get_nohz_timer_target(int pinned)
+int get_nohz_timer_target(void)
 {
-       int cpu = smp_processor_id();
-       int i;
+       int i, cpu = smp_processor_id();
        struct sched_domain *sd;
 
-       if (pinned || !get_sysctl_timer_migration() || !idle_cpu(cpu))
+       if (!idle_cpu(cpu))
                return cpu;
 
        rcu_read_lock();
@@ -1095,7 +1073,7 @@ void set_task_cpu(struct task_struct *p, unsigned int new_cpu)
                if (p->sched_class->migrate_task_rq)
                        p->sched_class->migrate_task_rq(p, new_cpu);
                p->se.nr_migrations++;
-               perf_sw_event_sched(PERF_COUNT_SW_CPU_MIGRATIONS, 1, 0);
+               perf_event_task_migrate(p);
        }
 
        __set_task_cpu(p, new_cpu);
@@ -2151,12 +2129,15 @@ void wake_up_new_task(struct task_struct *p)
 
 #ifdef CONFIG_PREEMPT_NOTIFIERS
 
+static struct static_key preempt_notifier_key = STATIC_KEY_INIT_FALSE;
+
 /**
  * preempt_notifier_register - tell me when current is being preempted & rescheduled
  * @notifier: notifier struct to register
  */
 void preempt_notifier_register(struct preempt_notifier *notifier)
 {
+       static_key_slow_inc(&preempt_notifier_key);
        hlist_add_head(&notifier->link, &current->preempt_notifiers);
 }
 EXPORT_SYMBOL_GPL(preempt_notifier_register);
@@ -2165,15 +2146,16 @@ EXPORT_SYMBOL_GPL(preempt_notifier_register);
  * preempt_notifier_unregister - no longer interested in preemption notifications
  * @notifier: notifier struct to unregister
  *
- * This is safe to call from within a preemption notifier.
+ * This is *not* safe to call from within a preemption notifier.
  */
 void preempt_notifier_unregister(struct preempt_notifier *notifier)
 {
        hlist_del(&notifier->link);
+       static_key_slow_dec(&preempt_notifier_key);
 }
 EXPORT_SYMBOL_GPL(preempt_notifier_unregister);
 
-static void fire_sched_in_preempt_notifiers(struct task_struct *curr)
+static void __fire_sched_in_preempt_notifiers(struct task_struct *curr)
 {
        struct preempt_notifier *notifier;
 
@@ -2181,9 +2163,15 @@ static void fire_sched_in_preempt_notifiers(struct task_struct *curr)
                notifier->ops->sched_in(notifier, raw_smp_processor_id());
 }
 
+static __always_inline void fire_sched_in_preempt_notifiers(struct task_struct *curr)
+{
+       if (static_key_false(&preempt_notifier_key))
+               __fire_sched_in_preempt_notifiers(curr);
+}
+
 static void
-fire_sched_out_preempt_notifiers(struct task_struct *curr,
-                                struct task_struct *next)
+__fire_sched_out_preempt_notifiers(struct task_struct *curr,
+                                  struct task_struct *next)
 {
        struct preempt_notifier *notifier;
 
@@ -2191,13 +2179,21 @@ fire_sched_out_preempt_notifiers(struct task_struct *curr,
                notifier->ops->sched_out(notifier, next);
 }
 
+static __always_inline void
+fire_sched_out_preempt_notifiers(struct task_struct *curr,
+                                struct task_struct *next)
+{
+       if (static_key_false(&preempt_notifier_key))
+               __fire_sched_out_preempt_notifiers(curr, next);
+}
+
 #else /* !CONFIG_PREEMPT_NOTIFIERS */
 
-static void fire_sched_in_preempt_notifiers(struct task_struct *curr)
+static inline void fire_sched_in_preempt_notifiers(struct task_struct *curr)
 {
 }
 
-static void
+static inline void
 fire_sched_out_preempt_notifiers(struct task_struct *curr,
                                 struct task_struct *next)
 {
@@ -2378,7 +2374,6 @@ context_switch(struct rq *rq, struct task_struct *prev,
         */
        spin_release(&rq->lock.dep_map, 1, _THIS_IP_);
 
-       context_tracking_task_switch(prev, next);
        /* Here we just switch the register state and the stack. */
        switch_to(prev, next, prev);
        barrier();
@@ -3081,7 +3076,6 @@ void rt_mutex_setprio(struct task_struct *p, int prio)
                if (!dl_prio(p->normal_prio) ||
                    (pi_task && dl_entity_preempt(&pi_task->dl, &p->dl))) {
                        p->dl.dl_boosted = 1;
-                       p->dl.dl_throttled = 0;
                        enqueue_flag = ENQUEUE_REPLENISH;
                } else
                        p->dl.dl_boosted = 0;
@@ -7073,6 +7067,9 @@ void __init sched_init_smp(void)
        alloc_cpumask_var(&non_isolated_cpus, GFP_KERNEL);
        alloc_cpumask_var(&fallback_doms, GFP_KERNEL);
 
+       /* nohz_full won't take effect without isolating the cpus. */
+       tick_nohz_full_add_cpus_to(cpu_isolated_map);
+
        sched_init_numa();
 
        /*
@@ -7109,8 +7106,6 @@ void __init sched_init_smp(void)
 }
 #endif /* CONFIG_SMP */
 
-const_debug unsigned int sysctl_timer_migration = 1;
-
 int in_sched_functions(unsigned long addr)
 {
        return in_lock_functions(addr) ||
@@ -8146,10 +8141,8 @@ static int tg_set_cfs_bandwidth(struct task_group *tg, u64 period, u64 quota)
 
        __refill_cfs_bandwidth_runtime(cfs_b);
        /* restart the period timer (if active) to handle new period expiry */
-       if (runtime_enabled && cfs_b->timer_active) {
-               /* force a reprogram */
-               __start_cfs_bandwidth(cfs_b, true);
-       }
+       if (runtime_enabled)
+               start_cfs_bandwidth(cfs_b);
        raw_spin_unlock_irq(&cfs_b->lock);
 
        for_each_online_cpu(i) {
index 890ce951c71713ab8dead85a62e5ad8104686bc1..eac20c557a55cc83f8e9d7e62578868ba9436aff 100644 (file)
@@ -503,8 +503,6 @@ static int start_dl_timer(struct sched_dl_entity *dl_se, bool boosted)
        struct dl_rq *dl_rq = dl_rq_of_se(dl_se);
        struct rq *rq = rq_of_dl_rq(dl_rq);
        ktime_t now, act;
-       ktime_t soft, hard;
-       unsigned long range;
        s64 delta;
 
        if (boosted)
@@ -527,15 +525,9 @@ static int start_dl_timer(struct sched_dl_entity *dl_se, bool boosted)
        if (ktime_us_delta(act, now) < 0)
                return 0;
 
-       hrtimer_set_expires(&dl_se->dl_timer, act);
+       hrtimer_start(&dl_se->dl_timer, act, HRTIMER_MODE_ABS);
 
-       soft = hrtimer_get_softexpires(&dl_se->dl_timer);
-       hard = hrtimer_get_expires(&dl_se->dl_timer);
-       range = ktime_to_ns(ktime_sub(hard, soft));
-       __hrtimer_start_range_ns(&dl_se->dl_timer, soft,
-                                range, HRTIMER_MODE_ABS, 0);
-
-       return hrtimer_active(&dl_se->dl_timer);
+       return 1;
 }
 
 /*
@@ -640,7 +632,7 @@ void init_dl_task_timer(struct sched_dl_entity *dl_se)
 }
 
 static
-int dl_runtime_exceeded(struct rq *rq, struct sched_dl_entity *dl_se)
+int dl_runtime_exceeded(struct sched_dl_entity *dl_se)
 {
        return (dl_se->runtime <= 0);
 }
@@ -684,7 +676,7 @@ static void update_curr_dl(struct rq *rq)
        sched_rt_avg_update(rq, delta_exec);
 
        dl_se->runtime -= dl_se->dl_yielded ? 0 : delta_exec;
-       if (dl_runtime_exceeded(rq, dl_se)) {
+       if (dl_runtime_exceeded(dl_se)) {
                dl_se->dl_throttled = 1;
                __dequeue_task_dl(rq, curr, 0);
                if (unlikely(!start_dl_timer(dl_se, curr->dl.dl_boosted)))
@@ -1012,7 +1004,9 @@ select_task_rq_dl(struct task_struct *p, int cpu, int sd_flag, int flags)
            (p->nr_cpus_allowed > 1)) {
                int target = find_later_rq(p);
 
-               if (target != -1)
+               if (target != -1 &&
+                               dl_time_before(p->dl.deadline,
+                                       cpu_rq(target)->dl.earliest_dl.curr))
                        cpu = target;
        }
        rcu_read_unlock();
@@ -1230,6 +1224,32 @@ next_node:
        return NULL;
 }
 
+/*
+ * Return the earliest pushable rq's task, which is suitable to be executed
+ * on the CPU, NULL otherwise:
+ */
+static struct task_struct *pick_earliest_pushable_dl_task(struct rq *rq, int cpu)
+{
+       struct rb_node *next_node = rq->dl.pushable_dl_tasks_leftmost;
+       struct task_struct *p = NULL;
+
+       if (!has_pushable_dl_tasks(rq))
+               return NULL;
+
+next_node:
+       if (next_node) {
+               p = rb_entry(next_node, struct task_struct, pushable_dl_tasks);
+
+               if (pick_dl_task(rq, p, cpu))
+                       return p;
+
+               next_node = rb_next(next_node);
+               goto next_node;
+       }
+
+       return NULL;
+}
+
 static DEFINE_PER_CPU(cpumask_var_t, local_cpu_mask_dl);
 
 static int find_later_rq(struct task_struct *task)
@@ -1333,6 +1353,17 @@ static struct rq *find_lock_later_rq(struct task_struct *task, struct rq *rq)
 
                later_rq = cpu_rq(cpu);
 
+               if (!dl_time_before(task->dl.deadline,
+                                       later_rq->dl.earliest_dl.curr)) {
+                       /*
+                        * Target rq has tasks of equal or earlier deadline,
+                        * retrying does not release any lock and is unlikely
+                        * to yield a different result.
+                        */
+                       later_rq = NULL;
+                       break;
+               }
+
                /* Retry if something changed. */
                if (double_lock_balance(rq, later_rq)) {
                        if (unlikely(task_rq(task) != rq ||
@@ -1514,7 +1545,7 @@ static int pull_dl_task(struct rq *this_rq)
                if (src_rq->dl.dl_nr_running <= 1)
                        goto skip;
 
-               p = pick_next_earliest_dl_task(src_rq, this_cpu);
+               p = pick_earliest_pushable_dl_task(src_rq, this_cpu);
 
                /*
                 * We found a task to be pulled if:
@@ -1659,7 +1690,7 @@ static void rq_offline_dl(struct rq *rq)
        cpudl_clear_freecpu(&rq->rd->cpudl, rq->cpu);
 }
 
-void init_sched_dl_class(void)
+void __init init_sched_dl_class(void)
 {
        unsigned int i;
 
index a245c1fc6f0a610f17e2d13635306d681e2ef821..315c68e015d955d6227a83b6b951482cffd8a68e 100644 (file)
@@ -132,12 +132,14 @@ print_task(struct seq_file *m, struct rq *rq, struct task_struct *p)
                p->prio);
 #ifdef CONFIG_SCHEDSTATS
        SEQ_printf(m, "%9Ld.%06ld %9Ld.%06ld %9Ld.%06ld",
-               SPLIT_NS(p->se.vruntime),
+               SPLIT_NS(p->se.statistics.wait_sum),
                SPLIT_NS(p->se.sum_exec_runtime),
                SPLIT_NS(p->se.statistics.sum_sleep_runtime));
 #else
-       SEQ_printf(m, "%15Ld %15Ld %15Ld.%06ld %15Ld.%06ld %15Ld.%06ld",
-               0LL, 0LL, 0LL, 0L, 0LL, 0L, 0LL, 0L);
+       SEQ_printf(m, "%9Ld.%06ld %9Ld.%06ld %9Ld.%06ld",
+               0LL, 0L,
+               SPLIT_NS(p->se.sum_exec_runtime),
+               0LL, 0L);
 #endif
 #ifdef CONFIG_NUMA_BALANCING
        SEQ_printf(m, " %d", task_node(p));
@@ -156,7 +158,7 @@ static void print_rq(struct seq_file *m, struct rq *rq, int rq_cpu)
        SEQ_printf(m,
        "\nrunnable tasks:\n"
        "            task   PID         tree-key  switches  prio"
-       "     exec-runtime         sum-exec        sum-sleep\n"
+       "     wait-time             sum-exec        sum-sleep\n"
        "------------------------------------------------------"
        "----------------------------------------------------\n");
 
@@ -230,8 +232,6 @@ void print_cfs_rq(struct seq_file *m, int cpu, struct cfs_rq *cfs_rq)
 #endif
 #endif
 #ifdef CONFIG_CFS_BANDWIDTH
-       SEQ_printf(m, "  .%-30s: %d\n", "tg->cfs_bandwidth.timer_active",
-                       cfs_rq->tg->cfs_bandwidth.timer_active);
        SEQ_printf(m, "  .%-30s: %d\n", "throttled",
                        cfs_rq->throttled);
        SEQ_printf(m, "  .%-30s: %d\n", "throttle_count",
@@ -582,6 +582,7 @@ void proc_sched_show_task(struct task_struct *p, struct seq_file *m)
        nr_switches = p->nvcsw + p->nivcsw;
 
 #ifdef CONFIG_SCHEDSTATS
+       PN(se.statistics.sum_sleep_runtime);
        PN(se.statistics.wait_start);
        PN(se.statistics.sleep_start);
        PN(se.statistics.block_start);
index 4b6e5f63d9af0e7525a9fbf3cd32551b61eecf60..40a7fcbf491eb7d1f5735d0e9efd25c23d4d60a9 100644 (file)
@@ -2209,7 +2209,7 @@ void task_numa_work(struct callback_head *work)
        }
        for (; vma; vma = vma->vm_next) {
                if (!vma_migratable(vma) || !vma_policy_mof(vma) ||
-                       is_vm_hugetlb_page(vma)) {
+                       is_vm_hugetlb_page(vma) || (vma->vm_flags & VM_MIXEDMAP)) {
                        continue;
                }
 
@@ -3504,16 +3504,7 @@ static int assign_cfs_rq_runtime(struct cfs_rq *cfs_rq)
        if (cfs_b->quota == RUNTIME_INF)
                amount = min_amount;
        else {
-               /*
-                * If the bandwidth pool has become inactive, then at least one
-                * period must have elapsed since the last consumption.
-                * Refresh the global state and ensure bandwidth timer becomes
-                * active.
-                */
-               if (!cfs_b->timer_active) {
-                       __refill_cfs_bandwidth_runtime(cfs_b);
-                       __start_cfs_bandwidth(cfs_b, false);
-               }
+               start_cfs_bandwidth(cfs_b);
 
                if (cfs_b->runtime > 0) {
                        amount = min(cfs_b->runtime, min_amount);
@@ -3662,6 +3653,7 @@ static void throttle_cfs_rq(struct cfs_rq *cfs_rq)
        struct cfs_bandwidth *cfs_b = tg_cfs_bandwidth(cfs_rq->tg);
        struct sched_entity *se;
        long task_delta, dequeue = 1;
+       bool empty;
 
        se = cfs_rq->tg->se[cpu_of(rq_of(cfs_rq))];
 
@@ -3691,13 +3683,21 @@ static void throttle_cfs_rq(struct cfs_rq *cfs_rq)
        cfs_rq->throttled = 1;
        cfs_rq->throttled_clock = rq_clock(rq);
        raw_spin_lock(&cfs_b->lock);
+       empty = list_empty(&cfs_rq->throttled_list);
+
        /*
         * Add to the _head_ of the list, so that an already-started
         * distribute_cfs_runtime will not see us
         */
        list_add_rcu(&cfs_rq->throttled_list, &cfs_b->throttled_cfs_rq);
-       if (!cfs_b->timer_active)
-               __start_cfs_bandwidth(cfs_b, false);
+
+       /*
+        * If we're the first throttled task, make sure the bandwidth
+        * timer is running.
+        */
+       if (empty)
+               start_cfs_bandwidth(cfs_b);
+
        raw_spin_unlock(&cfs_b->lock);
 }
 
@@ -3812,13 +3812,6 @@ static int do_sched_cfs_period_timer(struct cfs_bandwidth *cfs_b, int overrun)
        if (cfs_b->idle && !throttled)
                goto out_deactivate;
 
-       /*
-        * if we have relooped after returning idle once, we need to update our
-        * status as actually running, so that other cpus doing
-        * __start_cfs_bandwidth will stop trying to cancel us.
-        */
-       cfs_b->timer_active = 1;
-
        __refill_cfs_bandwidth_runtime(cfs_b);
 
        if (!throttled) {
@@ -3863,7 +3856,6 @@ static int do_sched_cfs_period_timer(struct cfs_bandwidth *cfs_b, int overrun)
        return 0;
 
 out_deactivate:
-       cfs_b->timer_active = 0;
        return 1;
 }
 
@@ -3878,7 +3870,7 @@ static const u64 cfs_bandwidth_slack_period = 5 * NSEC_PER_MSEC;
  * Are we near the end of the current quota period?
  *
  * Requires cfs_b->lock for hrtimer_expires_remaining to be safe against the
- * hrtimer base being cleared by __hrtimer_start_range_ns. In the case of
+ * hrtimer base being cleared by hrtimer_start. In the case of
  * migrate_hrtimers, base is never cleared, so we are fine.
  */
 static int runtime_refresh_within(struct cfs_bandwidth *cfs_b, u64 min_expire)
@@ -3906,8 +3898,9 @@ static void start_cfs_slack_bandwidth(struct cfs_bandwidth *cfs_b)
        if (runtime_refresh_within(cfs_b, min_left))
                return;
 
-       start_bandwidth_timer(&cfs_b->slack_timer,
-                               ns_to_ktime(cfs_bandwidth_slack_period));
+       hrtimer_start(&cfs_b->slack_timer,
+                       ns_to_ktime(cfs_bandwidth_slack_period),
+                       HRTIMER_MODE_REL);
 }
 
 /* we know any runtime found here is valid as update_curr() precedes return */
@@ -4027,6 +4020,7 @@ static enum hrtimer_restart sched_cfs_slack_timer(struct hrtimer *timer)
 {
        struct cfs_bandwidth *cfs_b =
                container_of(timer, struct cfs_bandwidth, slack_timer);
+
        do_sched_cfs_slack_timer(cfs_b);
 
        return HRTIMER_NORESTART;
@@ -4036,20 +4030,19 @@ static enum hrtimer_restart sched_cfs_period_timer(struct hrtimer *timer)
 {
        struct cfs_bandwidth *cfs_b =
                container_of(timer, struct cfs_bandwidth, period_timer);
-       ktime_t now;
        int overrun;
        int idle = 0;
 
        raw_spin_lock(&cfs_b->lock);
        for (;;) {
-               now = hrtimer_cb_get_time(timer);
-               overrun = hrtimer_forward(timer, now, cfs_b->period);
-
+               overrun = hrtimer_forward_now(timer, cfs_b->period);
                if (!overrun)
                        break;
 
                idle = do_sched_cfs_period_timer(cfs_b, overrun);
        }
+       if (idle)
+               cfs_b->period_active = 0;
        raw_spin_unlock(&cfs_b->lock);
 
        return idle ? HRTIMER_NORESTART : HRTIMER_RESTART;
@@ -4063,7 +4056,7 @@ void init_cfs_bandwidth(struct cfs_bandwidth *cfs_b)
        cfs_b->period = ns_to_ktime(default_cfs_period());
 
        INIT_LIST_HEAD(&cfs_b->throttled_cfs_rq);
-       hrtimer_init(&cfs_b->period_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+       hrtimer_init(&cfs_b->period_timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS_PINNED);
        cfs_b->period_timer.function = sched_cfs_period_timer;
        hrtimer_init(&cfs_b->slack_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
        cfs_b->slack_timer.function = sched_cfs_slack_timer;
@@ -4075,28 +4068,15 @@ static void init_cfs_rq_runtime(struct cfs_rq *cfs_rq)
        INIT_LIST_HEAD(&cfs_rq->throttled_list);
 }
 
-/* requires cfs_b->lock, may release to reprogram timer */
-void __start_cfs_bandwidth(struct cfs_bandwidth *cfs_b, bool force)
+void start_cfs_bandwidth(struct cfs_bandwidth *cfs_b)
 {
-       /*
-        * The timer may be active because we're trying to set a new bandwidth
-        * period or because we're racing with the tear-down path
-        * (timer_active==0 becomes visible before the hrtimer call-back
-        * terminates).  In either case we ensure that it's re-programmed
-        */
-       while (unlikely(hrtimer_active(&cfs_b->period_timer)) &&
-              hrtimer_try_to_cancel(&cfs_b->period_timer) < 0) {
-               /* bounce the lock to allow do_sched_cfs_period_timer to run */
-               raw_spin_unlock(&cfs_b->lock);
-               cpu_relax();
-               raw_spin_lock(&cfs_b->lock);
-               /* if someone else restarted the timer then we're done */
-               if (!force && cfs_b->timer_active)
-                       return;
-       }
+       lockdep_assert_held(&cfs_b->lock);
 
-       cfs_b->timer_active = 1;
-       start_bandwidth_timer(&cfs_b->period_timer, cfs_b->period);
+       if (!cfs_b->period_active) {
+               cfs_b->period_active = 1;
+               hrtimer_forward_now(&cfs_b->period_timer, cfs_b->period);
+               hrtimer_start_expires(&cfs_b->period_timer, HRTIMER_MODE_ABS_PINNED);
+       }
 }
 
 static void destroy_cfs_bandwidth(struct cfs_bandwidth *cfs_b)
index fefcb1fa5160139a41e9dd1fee74b20ef39105e0..594275ed262041a9fe702c9b7e9ac82172eb0465 100644 (file)
 
 #include "sched.h"
 
+/**
+ * sched_idle_set_state - Record idle state for the current CPU.
+ * @idle_state: State to record.
+ */
+void sched_idle_set_state(struct cpuidle_state *idle_state)
+{
+       idle_set_state(this_rq(), idle_state);
+}
+
 static int __read_mostly cpu_idle_force_poll;
 
 void cpu_idle_poll_ctrl(bool enable)
@@ -67,6 +76,46 @@ void __weak arch_cpu_idle(void)
        local_irq_enable();
 }
 
+/**
+ * default_idle_call - Default CPU idle routine.
+ *
+ * To use when the cpuidle framework cannot be used.
+ */
+void default_idle_call(void)
+{
+       if (current_clr_polling_and_test())
+               local_irq_enable();
+       else
+               arch_cpu_idle();
+}
+
+static int call_cpuidle(struct cpuidle_driver *drv, struct cpuidle_device *dev,
+                     int next_state)
+{
+       /* Fall back to the default arch idle method on errors. */
+       if (next_state < 0) {
+               default_idle_call();
+               return next_state;
+       }
+
+       /*
+        * The idle task must be scheduled, it is pointless to go to idle, just
+        * update no idle residency and return.
+        */
+       if (current_clr_polling_and_test()) {
+               dev->last_residency = 0;
+               local_irq_enable();
+               return -EBUSY;
+       }
+
+       /*
+        * Enter the idle state previously returned by the governor decision.
+        * This function will block until an interrupt occurs and will take
+        * care of re-enabling the local interrupts
+        */
+       return cpuidle_enter(drv, dev, next_state);
+}
+
 /**
  * cpuidle_idle_call - the main idle function
  *
@@ -81,7 +130,6 @@ static void cpuidle_idle_call(void)
        struct cpuidle_device *dev = __this_cpu_read(cpuidle_devices);
        struct cpuidle_driver *drv = cpuidle_get_cpu_driver(dev);
        int next_state, entered_state;
-       bool reflect;
 
        /*
         * Check if the idle task must be rescheduled. If it is the
@@ -105,8 +153,10 @@ static void cpuidle_idle_call(void)
         */
        rcu_idle_enter();
 
-       if (cpuidle_not_available(drv, dev))
-               goto use_default;
+       if (cpuidle_not_available(drv, dev)) {
+               default_idle_call();
+               goto exit_idle;
+       }
 
        /*
         * Suspend-to-idle ("freeze") is a system state in which all user space
@@ -124,52 +174,19 @@ static void cpuidle_idle_call(void)
                        goto exit_idle;
                }
 
-               reflect = false;
                next_state = cpuidle_find_deepest_state(drv, dev);
+               call_cpuidle(drv, dev, next_state);
        } else {
-               reflect = true;
                /*
                 * Ask the cpuidle framework to choose a convenient idle state.
                 */
                next_state = cpuidle_select(drv, dev);
-       }
-       /* Fall back to the default arch idle method on errors. */
-       if (next_state < 0)
-               goto use_default;
-
-       /*
-        * The idle task must be scheduled, it is pointless to
-        * go to idle, just update no idle residency and get
-        * out of this function
-        */
-       if (current_clr_polling_and_test()) {
-               dev->last_residency = 0;
-               entered_state = next_state;
-               local_irq_enable();
-               goto exit_idle;
-       }
-
-       /* Take note of the planned idle state. */
-       idle_set_state(this_rq(), &drv->states[next_state]);
-
-       /*
-        * Enter the idle state previously returned by the governor decision.
-        * This function will block until an interrupt occurs and will take
-        * care of re-enabling the local interrupts
-        */
-       entered_state = cpuidle_enter(drv, dev, next_state);
-
-       /* The cpu is no longer idle or about to enter idle. */
-       idle_set_state(this_rq(), NULL);
-
-       if (entered_state == -EBUSY)
-               goto use_default;
-
-       /*
-        * Give the governor an opportunity to reflect on the outcome
-        */
-       if (reflect)
+               entered_state = call_cpuidle(drv, dev, next_state);
+               /*
+                * Give the governor an opportunity to reflect on the outcome
+                */
                cpuidle_reflect(dev, entered_state);
+       }
 
 exit_idle:
        __current_set_polling();
@@ -182,19 +199,6 @@ exit_idle:
 
        rcu_idle_exit();
        start_critical_timings();
-       return;
-
-use_default:
-       /*
-        * We can't use the cpuidle framework, let's use the default
-        * idle routine.
-        */
-       if (current_clr_polling_and_test())
-               local_irq_enable();
-       else
-               arch_cpu_idle();
-
-       goto exit_idle;
 }
 
 DEFINE_PER_CPU(bool, cpu_dead_idle);
index 560d2fa623c311c9aa5ad51ead007e1b27c6fa6c..7d7093c51f8d169cea027c78a9ca0321c8f15932 100644 (file)
@@ -18,19 +18,22 @@ static enum hrtimer_restart sched_rt_period_timer(struct hrtimer *timer)
 {
        struct rt_bandwidth *rt_b =
                container_of(timer, struct rt_bandwidth, rt_period_timer);
-       ktime_t now;
-       int overrun;
        int idle = 0;
+       int overrun;
 
+       raw_spin_lock(&rt_b->rt_runtime_lock);
        for (;;) {
-               now = hrtimer_cb_get_time(timer);
-               overrun = hrtimer_forward(timer, now, rt_b->rt_period);
-
+               overrun = hrtimer_forward_now(timer, rt_b->rt_period);
                if (!overrun)
                        break;
 
+               raw_spin_unlock(&rt_b->rt_runtime_lock);
                idle = do_sched_rt_period_timer(rt_b, overrun);
+               raw_spin_lock(&rt_b->rt_runtime_lock);
        }
+       if (idle)
+               rt_b->rt_period_active = 0;
+       raw_spin_unlock(&rt_b->rt_runtime_lock);
 
        return idle ? HRTIMER_NORESTART : HRTIMER_RESTART;
 }
@@ -52,11 +55,12 @@ static void start_rt_bandwidth(struct rt_bandwidth *rt_b)
        if (!rt_bandwidth_enabled() || rt_b->rt_runtime == RUNTIME_INF)
                return;
 
-       if (hrtimer_active(&rt_b->rt_period_timer))
-               return;
-
        raw_spin_lock(&rt_b->rt_runtime_lock);
-       start_bandwidth_timer(&rt_b->rt_period_timer, rt_b->rt_period);
+       if (!rt_b->rt_period_active) {
+               rt_b->rt_period_active = 1;
+               hrtimer_forward_now(&rt_b->rt_period_timer, rt_b->rt_period);
+               hrtimer_start_expires(&rt_b->rt_period_timer, HRTIMER_MODE_ABS_PINNED);
+       }
        raw_spin_unlock(&rt_b->rt_runtime_lock);
 }
 
index d85455539d5cd8424c281bf6487ee814e91708e3..aea7c1f393cb3c983b3fd01e7df53155e4859ef3 100644 (file)
@@ -137,6 +137,7 @@ struct rt_bandwidth {
        ktime_t                 rt_period;
        u64                     rt_runtime;
        struct hrtimer          rt_period_timer;
+       unsigned int            rt_period_active;
 };
 
 void __dl_clear_params(struct task_struct *p);
@@ -221,7 +222,7 @@ struct cfs_bandwidth {
        s64 hierarchical_quota;
        u64 runtime_expires;
 
-       int idle, timer_active;
+       int idle, period_active;
        struct hrtimer period_timer, slack_timer;
        struct list_head throttled_cfs_rq;
 
@@ -312,7 +313,7 @@ extern void init_cfs_bandwidth(struct cfs_bandwidth *cfs_b);
 extern int sched_group_set_shares(struct task_group *tg, unsigned long shares);
 
 extern void __refill_cfs_bandwidth_runtime(struct cfs_bandwidth *cfs_b);
-extern void __start_cfs_bandwidth(struct cfs_bandwidth *cfs_b, bool force);
+extern void start_cfs_bandwidth(struct cfs_bandwidth *cfs_b);
 extern void unthrottle_cfs_rq(struct cfs_rq *cfs_rq);
 
 extern void free_rt_sched_group(struct task_group *tg);
@@ -1290,7 +1291,6 @@ extern void update_max_interval(void);
 extern void init_sched_dl_class(void);
 extern void init_sched_rt_class(void);
 extern void init_sched_fair_class(void);
-extern void init_sched_dl_class(void);
 
 extern void resched_curr(struct rq *rq);
 extern void resched_cpu(int cpu);
@@ -1410,8 +1410,6 @@ static inline void sched_rt_avg_update(struct rq *rq, u64 rt_delta) { }
 static inline void sched_avg_update(struct rq *rq) { }
 #endif
 
-extern void start_bandwidth_timer(struct hrtimer *period_timer, ktime_t period);
-
 /*
  * __task_rq_lock - lock the rq @p resides on.
  */
index 2ccec988d6b7c4c11f8355bf4e5286ac422c3fcf..052e02672d12428ce1e9e1f7266c7cd754ace5af 100644 (file)
@@ -341,7 +341,7 @@ long wait_woken(wait_queue_t *wait, unsigned mode, long timeout)
         * condition being true _OR_ WQ_FLAG_WOKEN such that we will not miss
         * an event.
         */
-       set_mb(wait->flags, wait->flags & ~WQ_FLAG_WOKEN); /* B */
+       smp_store_mb(wait->flags, wait->flags & ~WQ_FLAG_WOKEN); /* B */
 
        return timeout;
 }
@@ -354,7 +354,7 @@ int woken_wake_function(wait_queue_t *wait, unsigned mode, int sync, void *key)
         * doesn't imply write barrier and the users expects write
         * barrier semantics on wakeup functions.  The following
         * smp_wmb() is equivalent to smp_wmb() in try_to_wake_up()
-        * and is paired with set_mb() in wait_woken().
+        * and is paired with smp_store_mb() in wait_woken().
         */
        smp_wmb(); /* C */
        wait->flags |= WQ_FLAG_WOKEN;
index 695f0c6cd169a307de1f216cc67b4330a83363a4..fd643d8c4b424f858e3c6d7e0996bbb919a555e7 100644 (file)
@@ -211,25 +211,6 @@ static int multi_cpu_stop(void *data)
        return err;
 }
 
-struct irq_cpu_stop_queue_work_info {
-       int cpu1;
-       int cpu2;
-       struct cpu_stop_work *work1;
-       struct cpu_stop_work *work2;
-};
-
-/*
- * This function is always run with irqs and preemption disabled.
- * This guarantees that both work1 and work2 get queued, before
- * our local migrate thread gets the chance to preempt us.
- */
-static void irq_cpu_stop_queue_work(void *arg)
-{
-       struct irq_cpu_stop_queue_work_info *info = arg;
-       cpu_stop_queue_work(info->cpu1, info->work1);
-       cpu_stop_queue_work(info->cpu2, info->work2);
-}
-
 /**
  * stop_two_cpus - stops two cpus
  * @cpu1: the cpu to stop
@@ -245,7 +226,6 @@ int stop_two_cpus(unsigned int cpu1, unsigned int cpu2, cpu_stop_fn_t fn, void *
 {
        struct cpu_stop_done done;
        struct cpu_stop_work work1, work2;
-       struct irq_cpu_stop_queue_work_info call_args;
        struct multi_stop_data msdata;
 
        preempt_disable();
@@ -262,13 +242,6 @@ int stop_two_cpus(unsigned int cpu1, unsigned int cpu2, cpu_stop_fn_t fn, void *
                .done = &done
        };
 
-       call_args = (struct irq_cpu_stop_queue_work_info){
-               .cpu1 = cpu1,
-               .cpu2 = cpu2,
-               .work1 = &work1,
-               .work2 = &work2,
-       };
-
        cpu_stop_init_done(&done, 2);
        set_state(&msdata, MULTI_STOP_PREPARE);
 
@@ -285,16 +258,11 @@ int stop_two_cpus(unsigned int cpu1, unsigned int cpu2, cpu_stop_fn_t fn, void *
                return -ENOENT;
        }
 
-       lg_local_lock(&stop_cpus_lock);
-       /*
-        * Queuing needs to be done by the lowest numbered CPU, to ensure
-        * that works are always queued in the same order on every CPU.
-        * This prevents deadlocks.
-        */
-       smp_call_function_single(min(cpu1, cpu2),
-                                &irq_cpu_stop_queue_work,
-                                &call_args, 1);
-       lg_local_unlock(&stop_cpus_lock);
+       lg_double_lock(&stop_cpus_lock, cpu1, cpu2);
+       cpu_stop_queue_work(cpu1, &work1);
+       cpu_stop_queue_work(cpu2, &work2);
+       lg_double_unlock(&stop_cpus_lock, cpu1, cpu2);
+
        preempt_enable();
 
        wait_for_completion(&done.completion);
index a4e372b798a5f29535f9120b32fea70b0489f603..8571296b7ddb9b5efffed3f6fc30c29b34c33099 100644 (file)
 # define SET_TSC_CTL(a)                (-EINVAL)
 #endif
 #ifndef MPX_ENABLE_MANAGEMENT
-# define MPX_ENABLE_MANAGEMENT(a)      (-EINVAL)
+# define MPX_ENABLE_MANAGEMENT(      (-EINVAL)
 #endif
 #ifndef MPX_DISABLE_MANAGEMENT
-# define MPX_DISABLE_MANAGEMENT(a)     (-EINVAL)
+# define MPX_DISABLE_MANAGEMENT(     (-EINVAL)
 #endif
 #ifndef GET_FP_MODE
 # define GET_FP_MODE(a)                (-EINVAL)
@@ -2230,12 +2230,12 @@ SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3,
        case PR_MPX_ENABLE_MANAGEMENT:
                if (arg2 || arg3 || arg4 || arg5)
                        return -EINVAL;
-               error = MPX_ENABLE_MANAGEMENT(me);
+               error = MPX_ENABLE_MANAGEMENT();
                break;
        case PR_MPX_DISABLE_MANAGEMENT:
                if (arg2 || arg3 || arg4 || arg5)
                        return -EINVAL;
-               error = MPX_DISABLE_MANAGEMENT(me);
+               error = MPX_DISABLE_MANAGEMENT();
                break;
        case PR_SET_FP_MODE:
                error = SET_FP_MODE(me, arg2);
index 2082b1a88fb9a451a00a759379bec8d786c3bab7..b13e9d2de302411438ba62898ca27697130d38b0 100644 (file)
@@ -349,15 +349,6 @@ static struct ctl_table kern_table[] = {
                .mode           = 0644,
                .proc_handler   = proc_dointvec,
        },
-       {
-               .procname       = "timer_migration",
-               .data           = &sysctl_timer_migration,
-               .maxlen         = sizeof(unsigned int),
-               .mode           = 0644,
-               .proc_handler   = proc_dointvec_minmax,
-               .extra1         = &zero,
-               .extra2         = &one,
-       },
 #endif /* CONFIG_SMP */
 #ifdef CONFIG_NUMA_BALANCING
        {
@@ -1132,6 +1123,15 @@ static struct ctl_table kern_table[] = {
                .extra1         = &zero,
                .extra2         = &one,
        },
+#if defined(CONFIG_SMP) && defined(CONFIG_NO_HZ_COMMON)
+       {
+               .procname       = "timer_migration",
+               .data           = &sysctl_timer_migration,
+               .maxlen         = sizeof(unsigned int),
+               .mode           = 0644,
+               .proc_handler   = timer_migration_handler,
+       },
+#endif
        { }
 };
 
index 01f0312419b3cb44d8fa455d8cfaa2ad14d5ef0d..ffc4cc3dcd47b4e277df56735e11d51abdd87f6d 100644 (file)
@@ -13,19 +13,4 @@ obj-$(CONFIG_TIMER_STATS)                    += timer_stats.o
 obj-$(CONFIG_DEBUG_FS)                         += timekeeping_debug.o
 obj-$(CONFIG_TEST_UDELAY)                      += test_udelay.o
 
-$(obj)/time.o: $(obj)/timeconst.h
-
-quiet_cmd_hzfile = HZFILE  $@
-      cmd_hzfile = echo "hz=$(CONFIG_HZ)" > $@
-
-targets += hz.bc
-$(obj)/hz.bc: $(objtree)/include/config/hz.h FORCE
-       $(call if_changed,hzfile)
-
-quiet_cmd_bc  = BC      $@
-      cmd_bc  = bc -q $(filter-out FORCE,$^) > $@
-
-targets += timeconst.h
-$(obj)/timeconst.h: $(obj)/hz.bc $(src)/timeconst.bc FORCE
-       $(call if_changed,bc)
-
+$(obj)/time.o: $(objtree)/include/config/
index 1b001ed1edb945cd5a95238fc563f93e8971041e..7fbba635a5499805c316c36e99910f1d96eb6fb6 100644 (file)
@@ -317,19 +317,16 @@ EXPORT_SYMBOL_GPL(alarm_init);
  * @alarm: ptr to alarm to set
  * @start: time to run the alarm
  */
-int alarm_start(struct alarm *alarm, ktime_t start)
+void alarm_start(struct alarm *alarm, ktime_t start)
 {
        struct alarm_base *base = &alarm_bases[alarm->type];
        unsigned long flags;
-       int ret;
 
        spin_lock_irqsave(&base->lock, flags);
        alarm->node.expires = start;
        alarmtimer_enqueue(base, alarm);
-       ret = hrtimer_start(&alarm->timer, alarm->node.expires,
-                               HRTIMER_MODE_ABS);
+       hrtimer_start(&alarm->timer, alarm->node.expires, HRTIMER_MODE_ABS);
        spin_unlock_irqrestore(&base->lock, flags);
-       return ret;
 }
 EXPORT_SYMBOL_GPL(alarm_start);
 
@@ -338,12 +335,12 @@ EXPORT_SYMBOL_GPL(alarm_start);
  * @alarm: ptr to alarm to set
  * @start: time relative to now to run the alarm
  */
-int alarm_start_relative(struct alarm *alarm, ktime_t start)
+void alarm_start_relative(struct alarm *alarm, ktime_t start)
 {
        struct alarm_base *base = &alarm_bases[alarm->type];
 
        start = ktime_add(start, base->gettime());
-       return alarm_start(alarm, start);
+       alarm_start(alarm, start);
 }
 EXPORT_SYMBOL_GPL(alarm_start_relative);
 
@@ -495,12 +492,12 @@ static enum alarmtimer_restart alarm_handle_timer(struct alarm *alarm,
  */
 static int alarm_clock_getres(const clockid_t which_clock, struct timespec *tp)
 {
-       clockid_t baseid = alarm_bases[clock2alarm(which_clock)].base_clockid;
-
        if (!alarmtimer_get_rtcdev())
                return -EINVAL;
 
-       return hrtimer_get_res(baseid, tp);
+       tp->tv_sec = 0;
+       tp->tv_nsec = hrtimer_resolution;
+       return 0;
 }
 
 /**
index 637a09461c1d9a3d30fc3d6a33cb85b76a990261..08ccc3da3ca086366eadaae89f46e0604099d661 100644 (file)
@@ -94,8 +94,8 @@ u64 clockevent_delta2ns(unsigned long latch, struct clock_event_device *evt)
 }
 EXPORT_SYMBOL_GPL(clockevent_delta2ns);
 
-static int __clockevents_set_state(struct clock_event_device *dev,
-                                  enum clock_event_state state)
+static int __clockevents_switch_state(struct clock_event_device *dev,
+                                     enum clock_event_state state)
 {
        /* Transition with legacy set_mode() callback */
        if (dev->set_mode) {
@@ -134,32 +134,44 @@ static int __clockevents_set_state(struct clock_event_device *dev,
                        return -ENOSYS;
                return dev->set_state_oneshot(dev);
 
+       case CLOCK_EVT_STATE_ONESHOT_STOPPED:
+               /* Core internal bug */
+               if (WARN_ONCE(!clockevent_state_oneshot(dev),
+                             "Current state: %d\n",
+                             clockevent_get_state(dev)))
+                       return -EINVAL;
+
+               if (dev->set_state_oneshot_stopped)
+                       return dev->set_state_oneshot_stopped(dev);
+               else
+                       return -ENOSYS;
+
        default:
                return -ENOSYS;
        }
 }
 
 /**
- * clockevents_set_state - set the operating state of a clock event device
+ * clockevents_switch_state - set the operating state of a clock event device
  * @dev:       device to modify
  * @state:     new state
  *
  * Must be called with interrupts disabled !
  */
-void clockevents_set_state(struct clock_event_device *dev,
-                          enum clock_event_state state)
+void clockevents_switch_state(struct clock_event_device *dev,
+                             enum clock_event_state state)
 {
-       if (dev->state != state) {
-               if (__clockevents_set_state(dev, state))
+       if (clockevent_get_state(dev) != state) {
+               if (__clockevents_switch_state(dev, state))
                        return;
 
-               dev->state = state;
+               clockevent_set_state(dev, state);
 
                /*
                 * A nsec2cyc multiplicator of 0 is invalid and we'd crash
                 * on it, so fix it up and emit a warning:
                 */
-               if (state == CLOCK_EVT_STATE_ONESHOT) {
+               if (clockevent_state_oneshot(dev)) {
                        if (unlikely(!dev->mult)) {
                                dev->mult = 1;
                                WARN_ON(1);
@@ -174,7 +186,7 @@ void clockevents_set_state(struct clock_event_device *dev,
  */
 void clockevents_shutdown(struct clock_event_device *dev)
 {
-       clockevents_set_state(dev, CLOCK_EVT_STATE_SHUTDOWN);
+       clockevents_switch_state(dev, CLOCK_EVT_STATE_SHUTDOWN);
        dev->next_event.tv64 = KTIME_MAX;
 }
 
@@ -248,7 +260,7 @@ static int clockevents_program_min_delta(struct clock_event_device *dev)
                delta = dev->min_delta_ns;
                dev->next_event = ktime_add_ns(ktime_get(), delta);
 
-               if (dev->state == CLOCK_EVT_STATE_SHUTDOWN)
+               if (clockevent_state_shutdown(dev))
                        return 0;
 
                dev->retries++;
@@ -285,7 +297,7 @@ static int clockevents_program_min_delta(struct clock_event_device *dev)
        delta = dev->min_delta_ns;
        dev->next_event = ktime_add_ns(ktime_get(), delta);
 
-       if (dev->state == CLOCK_EVT_STATE_SHUTDOWN)
+       if (clockevent_state_shutdown(dev))
                return 0;
 
        dev->retries++;
@@ -317,9 +329,13 @@ int clockevents_program_event(struct clock_event_device *dev, ktime_t expires,
 
        dev->next_event = expires;
 
-       if (dev->state == CLOCK_EVT_STATE_SHUTDOWN)
+       if (clockevent_state_shutdown(dev))
                return 0;
 
+       /* We must be in ONESHOT state here */
+       WARN_ONCE(!clockevent_state_oneshot(dev), "Current state: %d\n",
+                 clockevent_get_state(dev));
+
        /* Shortcut for clockevent devices that can deal with ktime. */
        if (dev->features & CLOCK_EVT_FEAT_KTIME)
                return dev->set_next_ktime(expires, dev);
@@ -362,7 +378,7 @@ static int clockevents_replace(struct clock_event_device *ced)
        struct clock_event_device *dev, *newdev = NULL;
 
        list_for_each_entry(dev, &clockevent_devices, list) {
-               if (dev == ced || dev->state != CLOCK_EVT_STATE_DETACHED)
+               if (dev == ced || !clockevent_state_detached(dev))
                        continue;
 
                if (!tick_check_replacement(newdev, dev))
@@ -388,7 +404,7 @@ static int clockevents_replace(struct clock_event_device *ced)
 static int __clockevents_try_unbind(struct clock_event_device *ced, int cpu)
 {
        /* Fast track. Device is unused */
-       if (ced->state == CLOCK_EVT_STATE_DETACHED) {
+       if (clockevent_state_detached(ced)) {
                list_del_init(&ced->list);
                return 0;
        }
@@ -445,7 +461,8 @@ static int clockevents_sanity_check(struct clock_event_device *dev)
        if (dev->set_mode) {
                /* We shouldn't be supporting new modes now */
                WARN_ON(dev->set_state_periodic || dev->set_state_oneshot ||
-                       dev->set_state_shutdown || dev->tick_resume);
+                       dev->set_state_shutdown || dev->tick_resume ||
+                       dev->set_state_oneshot_stopped);
 
                BUG_ON(dev->mode != CLOCK_EVT_MODE_UNUSED);
                return 0;
@@ -480,7 +497,7 @@ void clockevents_register_device(struct clock_event_device *dev)
        BUG_ON(clockevents_sanity_check(dev));
 
        /* Initialize state to DETACHED */
-       dev->state = CLOCK_EVT_STATE_DETACHED;
+       clockevent_set_state(dev, CLOCK_EVT_STATE_DETACHED);
 
        if (!dev->cpumask) {
                WARN_ON(num_possible_cpus() > 1);
@@ -545,11 +562,11 @@ int __clockevents_update_freq(struct clock_event_device *dev, u32 freq)
 {
        clockevents_config(dev, freq);
 
-       if (dev->state == CLOCK_EVT_STATE_ONESHOT)
+       if (clockevent_state_oneshot(dev))
                return clockevents_program_event(dev, dev->next_event, false);
 
-       if (dev->state == CLOCK_EVT_STATE_PERIODIC)
-               return __clockevents_set_state(dev, CLOCK_EVT_STATE_PERIODIC);
+       if (clockevent_state_periodic(dev))
+               return __clockevents_switch_state(dev, CLOCK_EVT_STATE_PERIODIC);
 
        return 0;
 }
@@ -603,13 +620,13 @@ void clockevents_exchange_device(struct clock_event_device *old,
         */
        if (old) {
                module_put(old->owner);
-               clockevents_set_state(old, CLOCK_EVT_STATE_DETACHED);
+               clockevents_switch_state(old, CLOCK_EVT_STATE_DETACHED);
                list_del(&old->list);
                list_add(&old->list, &clockevents_released);
        }
 
        if (new) {
-               BUG_ON(new->state != CLOCK_EVT_STATE_DETACHED);
+               BUG_ON(!clockevent_state_detached(new));
                clockevents_shutdown(new);
        }
 }
@@ -622,7 +639,7 @@ void clockevents_suspend(void)
        struct clock_event_device *dev;
 
        list_for_each_entry_reverse(dev, &clockevent_devices, list)
-               if (dev->suspend)
+               if (dev->suspend && !clockevent_state_detached(dev))
                        dev->suspend(dev);
 }
 
@@ -634,7 +651,7 @@ void clockevents_resume(void)
        struct clock_event_device *dev;
 
        list_for_each_entry(dev, &clockevent_devices, list)
-               if (dev->resume)
+               if (dev->resume && !clockevent_state_detached(dev))
                        dev->resume(dev);
 }
 
@@ -665,7 +682,7 @@ void tick_cleanup_dead_cpu(int cpu)
                if (cpumask_test_cpu(cpu, dev->cpumask) &&
                    cpumask_weight(dev->cpumask) == 1 &&
                    !tick_is_broadcast_device(dev)) {
-                       BUG_ON(dev->state != CLOCK_EVT_STATE_DETACHED);
+                       BUG_ON(!clockevent_state_detached(dev));
                        list_del(&dev->list);
                }
        }
index 15facb1b9c606c7a5fa5ea3500ea7dd4bf523477..841b72f720e88041a99ded8852381555c307fb43 100644 (file)
@@ -23,6 +23,8 @@
  *   o Allow clocksource drivers to be unregistered
  */
 
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
 #include <linux/device.h>
 #include <linux/clocksource.h>
 #include <linux/init.h>
@@ -216,10 +218,11 @@ static void clocksource_watchdog(unsigned long data)
 
                /* Check the deviation from the watchdog clocksource. */
                if ((abs(cs_nsec - wd_nsec) > WATCHDOG_THRESHOLD)) {
-                       pr_warn("timekeeping watchdog: Marking clocksource '%s' as unstable, because the skew is too large:\n", cs->name);
-                       pr_warn("       '%s' wd_now: %llx wd_last: %llx mask: %llx\n",
+                       pr_warn("timekeeping watchdog: Marking clocksource '%s' as unstable because the skew is too large:\n",
+                               cs->name);
+                       pr_warn("                      '%s' wd_now: %llx wd_last: %llx mask: %llx\n",
                                watchdog->name, wdnow, wdlast, watchdog->mask);
-                       pr_warn("       '%s' cs_now: %llx cs_last: %llx mask: %llx\n",
+                       pr_warn("                      '%s' cs_now: %llx cs_last: %llx mask: %llx\n",
                                cs->name, csnow, cslast, cs->mask);
                        __clocksource_unstable(cs);
                        continue;
@@ -567,9 +570,8 @@ static void __clocksource_select(bool skipcur)
                 */
                if (!(cs->flags & CLOCK_SOURCE_VALID_FOR_HRES) && oneshot) {
                        /* Override clocksource cannot be used. */
-                       printk(KERN_WARNING "Override clocksource %s is not "
-                              "HRT compatible. Cannot switch while in "
-                              "HRT/NOHZ mode\n", cs->name);
+                       pr_warn("Override clocksource %s is not HRT compatible - cannot switch while in HRT/NOHZ mode\n",
+                               cs->name);
                        override_name[0] = 0;
                } else
                        /* Override clocksource can be used. */
@@ -708,8 +710,8 @@ void __clocksource_update_freq_scale(struct clocksource *cs, u32 scale, u32 freq
 
        clocksource_update_max_deferment(cs);
 
-       pr_info("clocksource %s: mask: 0x%llx max_cycles: 0x%llx, max_idle_ns: %lld ns\n",
-                       cs->name, cs->mask, cs->max_cycles, cs->max_idle_ns);
+       pr_info("%s: mask: 0x%llx max_cycles: 0x%llx, max_idle_ns: %lld ns\n",
+               cs->name, cs->mask, cs->max_cycles, cs->max_idle_ns);
 }
 EXPORT_SYMBOL_GPL(__clocksource_update_freq_scale);
 
@@ -1008,12 +1010,10 @@ __setup("clocksource=", boot_override_clocksource);
 static int __init boot_override_clock(char* str)
 {
        if (!strcmp(str, "pmtmr")) {
-               printk("Warning: clock=pmtmr is deprecated. "
-                       "Use clocksource=acpi_pm.\n");
+               pr_warn("clock=pmtmr is deprecated - use clocksource=acpi_pm\n");
                return boot_override_clocksource("acpi_pm");
        }
-       printk("Warning! clock= boot option is deprecated. "
-               "Use clocksource=xyz\n");
+       pr_warn("clock= boot option is deprecated - use clocksource=xyz\n");
        return boot_override_clocksource(str);
 }
 
index 93ef7190bdeaadbf99efe07954cca3bee6399d07..5c7ae4b641c44aca69393a704507630a652381bf 100644 (file)
  */
 DEFINE_PER_CPU(struct hrtimer_cpu_base, hrtimer_bases) =
 {
-
        .lock = __RAW_SPIN_LOCK_UNLOCKED(hrtimer_bases.lock),
+       .seq = SEQCNT_ZERO(hrtimer_bases.seq),
        .clock_base =
        {
                {
                        .index = HRTIMER_BASE_MONOTONIC,
                        .clockid = CLOCK_MONOTONIC,
                        .get_time = &ktime_get,
-                       .resolution = KTIME_LOW_RES,
                },
                {
                        .index = HRTIMER_BASE_REALTIME,
                        .clockid = CLOCK_REALTIME,
                        .get_time = &ktime_get_real,
-                       .resolution = KTIME_LOW_RES,
                },
                {
                        .index = HRTIMER_BASE_BOOTTIME,
                        .clockid = CLOCK_BOOTTIME,
                        .get_time = &ktime_get_boottime,
-                       .resolution = KTIME_LOW_RES,
                },
                {
                        .index = HRTIMER_BASE_TAI,
                        .clockid = CLOCK_TAI,
                        .get_time = &ktime_get_clocktai,
-                       .resolution = KTIME_LOW_RES,
                },
        }
 };
@@ -109,33 +105,24 @@ static inline int hrtimer_clockid_to_base(clockid_t clock_id)
        return hrtimer_clock_to_base_table[clock_id];
 }
 
-
-/*
- * Get the coarse grained time at the softirq based on xtime and
- * wall_to_monotonic.
- */
-static void hrtimer_get_softirq_time(struct hrtimer_cpu_base *base)
-{
-       ktime_t xtim, mono, boot, tai;
-       ktime_t off_real, off_boot, off_tai;
-
-       mono = ktime_get_update_offsets_tick(&off_real, &off_boot, &off_tai);
-       boot = ktime_add(mono, off_boot);
-       xtim = ktime_add(mono, off_real);
-       tai = ktime_add(mono, off_tai);
-
-       base->clock_base[HRTIMER_BASE_REALTIME].softirq_time = xtim;
-       base->clock_base[HRTIMER_BASE_MONOTONIC].softirq_time = mono;
-       base->clock_base[HRTIMER_BASE_BOOTTIME].softirq_time = boot;
-       base->clock_base[HRTIMER_BASE_TAI].softirq_time = tai;
-}
-
 /*
  * Functions and macros which are different for UP/SMP systems are kept in a
  * single place
  */
 #ifdef CONFIG_SMP
 
+/*
+ * We require the migration_base for lock_hrtimer_base()/switch_hrtimer_base()
+ * such that hrtimer_callback_running() can unconditionally dereference
+ * timer->base->cpu_base
+ */
+static struct hrtimer_cpu_base migration_cpu_base = {
+       .seq = SEQCNT_ZERO(migration_cpu_base),
+       .clock_base = { { .cpu_base = &migration_cpu_base, }, },
+};
+
+#define migration_base migration_cpu_base.clock_base[0]
+
 /*
  * We are using hashed locking: holding per_cpu(hrtimer_bases)[n].lock
  * means that all timers which are tied to this base via timer->base are
@@ -145,8 +132,8 @@ static void hrtimer_get_softirq_time(struct hrtimer_cpu_base *base)
  * be found on the lists/queues.
  *
  * When the timer's base is locked, and the timer removed from list, it is
- * possible to set timer->base = NULL and drop the lock: the timer remains
- * locked.
+ * possible to set timer->base = &migration_base and drop the lock: the timer
+ * remains locked.
  */
 static
 struct hrtimer_clock_base *lock_hrtimer_base(const struct hrtimer *timer,
@@ -156,7 +143,7 @@ struct hrtimer_clock_base *lock_hrtimer_base(const struct hrtimer *timer,
 
        for (;;) {
                base = timer->base;
-               if (likely(base != NULL)) {
+               if (likely(base != &migration_base)) {
                        raw_spin_lock_irqsave(&base->cpu_base->lock, *flags);
                        if (likely(base == timer->base))
                                return base;
@@ -190,6 +177,24 @@ hrtimer_check_target(struct hrtimer *timer, struct hrtimer_clock_base *new_base)
 #endif
 }
 
+#if defined(CONFIG_SMP) && defined(CONFIG_NO_HZ_COMMON)
+static inline
+struct hrtimer_cpu_base *get_target_base(struct hrtimer_cpu_base *base,
+                                        int pinned)
+{
+       if (pinned || !base->migration_enabled)
+               return this_cpu_ptr(&hrtimer_bases);
+       return &per_cpu(hrtimer_bases, get_nohz_timer_target());
+}
+#else
+static inline
+struct hrtimer_cpu_base *get_target_base(struct hrtimer_cpu_base *base,
+                                        int pinned)
+{
+       return this_cpu_ptr(&hrtimer_bases);
+}
+#endif
+
 /*
  * Switch the timer base to the current CPU when possible.
  */
@@ -197,14 +202,13 @@ static inline struct hrtimer_clock_base *
 switch_hrtimer_base(struct hrtimer *timer, struct hrtimer_clock_base *base,
                    int pinned)
 {
+       struct hrtimer_cpu_base *new_cpu_base, *this_base;
        struct hrtimer_clock_base *new_base;
-       struct hrtimer_cpu_base *new_cpu_base;
-       int this_cpu = smp_processor_id();
-       int cpu = get_nohz_timer_target(pinned);
        int basenum = base->index;
 
+       this_base = this_cpu_ptr(&hrtimer_bases);
+       new_cpu_base = get_target_base(this_base, pinned);
 again:
-       new_cpu_base = &per_cpu(hrtimer_bases, cpu);
        new_base = &new_cpu_base->clock_base[basenum];
 
        if (base != new_base) {
@@ -220,22 +224,24 @@ again:
                if (unlikely(hrtimer_callback_running(timer)))
                        return base;
 
-               /* See the comment in lock_timer_base() */
-               timer->base = NULL;
+               /* See the comment in lock_hrtimer_base() */
+               timer->base = &migration_base;
                raw_spin_unlock(&base->cpu_base->lock);
                raw_spin_lock(&new_base->cpu_base->lock);
 
-               if (cpu != this_cpu && hrtimer_check_target(timer, new_base)) {
-                       cpu = this_cpu;
+               if (new_cpu_base != this_base &&
+                   hrtimer_check_target(timer, new_base)) {
                        raw_spin_unlock(&new_base->cpu_base->lock);
                        raw_spin_lock(&base->cpu_base->lock);
+                       new_cpu_base = this_base;
                        timer->base = base;
                        goto again;
                }
                timer->base = new_base;
        } else {
-               if (cpu != this_cpu && hrtimer_check_target(timer, new_base)) {
-                       cpu = this_cpu;
+               if (new_cpu_base != this_base &&
+                   hrtimer_check_target(timer, new_base)) {
+                       new_cpu_base = this_base;
                        goto again;
                }
        }
@@ -443,24 +449,35 @@ static inline void debug_deactivate(struct hrtimer *timer)
 }
 
 #if defined(CONFIG_NO_HZ_COMMON) || defined(CONFIG_HIGH_RES_TIMERS)
+static inline void hrtimer_update_next_timer(struct hrtimer_cpu_base *cpu_base,
+                                            struct hrtimer *timer)
+{
+#ifdef CONFIG_HIGH_RES_TIMERS
+       cpu_base->next_timer = timer;
+#endif
+}
+
 static ktime_t __hrtimer_get_next_event(struct hrtimer_cpu_base *cpu_base)
 {
        struct hrtimer_clock_base *base = cpu_base->clock_base;
        ktime_t expires, expires_next = { .tv64 = KTIME_MAX };
-       int i;
+       unsigned int active = cpu_base->active_bases;
 
-       for (i = 0; i < HRTIMER_MAX_CLOCK_BASES; i++, base++) {
+       hrtimer_update_next_timer(cpu_base, NULL);
+       for (; active; base++, active >>= 1) {
                struct timerqueue_node *next;
                struct hrtimer *timer;
 
-               next = timerqueue_getnext(&base->active);
-               if (!next)
+               if (!(active & 0x01))
                        continue;
 
+               next = timerqueue_getnext(&base->active);
                timer = container_of(next, struct hrtimer, node);
                expires = ktime_sub(hrtimer_get_expires(timer), base->offset);
-               if (expires.tv64 < expires_next.tv64)
+               if (expires.tv64 < expires_next.tv64) {
                        expires_next = expires;
+                       hrtimer_update_next_timer(cpu_base, timer);
+               }
        }
        /*
         * clock_was_set() might have changed base->offset of any of
@@ -473,6 +490,16 @@ static ktime_t __hrtimer_get_next_event(struct hrtimer_cpu_base *cpu_base)
 }
 #endif
 
+static inline ktime_t hrtimer_update_base(struct hrtimer_cpu_base *base)
+{
+       ktime_t *offs_real = &base->clock_base[HRTIMER_BASE_REALTIME].offset;
+       ktime_t *offs_boot = &base->clock_base[HRTIMER_BASE_BOOTTIME].offset;
+       ktime_t *offs_tai = &base->clock_base[HRTIMER_BASE_TAI].offset;
+
+       return ktime_get_update_offsets_now(&base->clock_was_set_seq,
+                                           offs_real, offs_boot, offs_tai);
+}
+
 /* High resolution timer related functions */
 #ifdef CONFIG_HIGH_RES_TIMERS
 
@@ -480,6 +507,8 @@ static ktime_t __hrtimer_get_next_event(struct hrtimer_cpu_base *cpu_base)
  * High resolution timer enabled ?
  */
 static int hrtimer_hres_enabled __read_mostly  = 1;
+unsigned int hrtimer_resolution __read_mostly = LOW_RES_NSEC;
+EXPORT_SYMBOL_GPL(hrtimer_resolution);
 
 /*
  * Enable / Disable high resolution mode
@@ -508,9 +537,14 @@ static inline int hrtimer_is_hres_enabled(void)
 /*
  * Is the high resolution mode active ?
  */
+static inline int __hrtimer_hres_active(struct hrtimer_cpu_base *cpu_base)
+{
+       return cpu_base->hres_active;
+}
+
 static inline int hrtimer_hres_active(void)
 {
-       return __this_cpu_read(hrtimer_bases.hres_active);
+       return __hrtimer_hres_active(this_cpu_ptr(&hrtimer_bases));
 }
 
 /*
@@ -521,7 +555,12 @@ static inline int hrtimer_hres_active(void)
 static void
 hrtimer_force_reprogram(struct hrtimer_cpu_base *cpu_base, int skip_equal)
 {
-       ktime_t expires_next = __hrtimer_get_next_event(cpu_base);
+       ktime_t expires_next;
+
+       if (!cpu_base->hres_active)
+               return;
+
+       expires_next = __hrtimer_get_next_event(cpu_base);
 
        if (skip_equal && expires_next.tv64 == cpu_base->expires_next.tv64)
                return;
@@ -545,63 +584,53 @@ hrtimer_force_reprogram(struct hrtimer_cpu_base *cpu_base, int skip_equal)
        if (cpu_base->hang_detected)
                return;
 
-       if (cpu_base->expires_next.tv64 != KTIME_MAX)
-               tick_program_event(cpu_base->expires_next, 1);
+       tick_program_event(cpu_base->expires_next, 1);
 }
 
 /*
- * Shared reprogramming for clock_realtime and clock_monotonic
- *
  * When a timer is enqueued and expires earlier than the already enqueued
  * timers, we have to check, whether it expires earlier than the timer for
  * which the clock event device was armed.
  *
- * Note, that in case the state has HRTIMER_STATE_CALLBACK set, no reprogramming
- * and no expiry check happens. The timer gets enqueued into the rbtree. The
- * reprogramming and expiry check is done in the hrtimer_interrupt or in the
- * softirq.
- *
  * Called with interrupts disabled and base->cpu_base.lock held
  */
-static int hrtimer_reprogram(struct hrtimer *timer,
-                            struct hrtimer_clock_base *base)
+static void hrtimer_reprogram(struct hrtimer *timer,
+                             struct hrtimer_clock_base *base)
 {
        struct hrtimer_cpu_base *cpu_base = this_cpu_ptr(&hrtimer_bases);
        ktime_t expires = ktime_sub(hrtimer_get_expires(timer), base->offset);
-       int res;
 
        WARN_ON_ONCE(hrtimer_get_expires_tv64(timer) < 0);
 
        /*
-        * When the callback is running, we do not reprogram the clock event
-        * device. The timer callback is either running on a different CPU or
-        * the callback is executed in the hrtimer_interrupt context. The
-        * reprogramming is handled either by the softirq, which called the
-        * callback or at the end of the hrtimer_interrupt.
+        * If the timer is not on the current cpu, we cannot reprogram
+        * the other cpus clock event device.
         */
-       if (hrtimer_callback_running(timer))
-               return 0;
+       if (base->cpu_base != cpu_base)
+               return;
+
+       /*
+        * If the hrtimer interrupt is running, then it will
+        * reevaluate the clock bases and reprogram the clock event
+        * device. The callbacks are always executed in hard interrupt
+        * context so we don't need an extra check for a running
+        * callback.
+        */
+       if (cpu_base->in_hrtirq)
+               return;
 
        /*
         * CLOCK_REALTIME timer might be requested with an absolute
-        * expiry time which is less than base->offset. Nothing wrong
-        * about that, just avoid to call into the tick code, which
-        * has now objections against negative expiry values.
+        * expiry time which is less than base->offset. Set it to 0.
         */
        if (expires.tv64 < 0)
-               return -ETIME;
+               expires.tv64 = 0;
 
        if (expires.tv64 >= cpu_base->expires_next.tv64)
-               return 0;
+               return;
 
-       /*
-        * When the target cpu of the timer is currently executing
-        * hrtimer_interrupt(), then we do not touch the clock event
-        * device. hrtimer_interrupt() will reevaluate all clock bases
-        * before reprogramming the device.
-        */
-       if (cpu_base->in_hrtirq)
-               return 0;
+       /* Update the pointer to the next expiring timer */
+       cpu_base->next_timer = timer;
 
        /*
         * If a hang was detected in the last timer interrupt then we
@@ -610,15 +639,14 @@ static int hrtimer_reprogram(struct hrtimer *timer,
         * to make progress.
         */
        if (cpu_base->hang_detected)
-               return 0;
+               return;
 
        /*
-        * Clockevents returns -ETIME, when the event was in the past.
+        * Program the timer hardware. We enforce the expiry for
+        * events which are already in the past.
         */
-       res = tick_program_event(expires, 0);
-       if (!IS_ERR_VALUE(res))
-               cpu_base->expires_next = expires;
-       return res;
+       cpu_base->expires_next = expires;
+       tick_program_event(expires, 1);
 }
 
 /*
@@ -630,15 +658,6 @@ static inline void hrtimer_init_hres(struct hrtimer_cpu_base *base)
        base->hres_active = 0;
 }
 
-static inline ktime_t hrtimer_update_base(struct hrtimer_cpu_base *base)
-{
-       ktime_t *offs_real = &base->clock_base[HRTIMER_BASE_REALTIME].offset;
-       ktime_t *offs_boot = &base->clock_base[HRTIMER_BASE_BOOTTIME].offset;
-       ktime_t *offs_tai = &base->clock_base[HRTIMER_BASE_TAI].offset;
-
-       return ktime_get_update_offsets_now(offs_real, offs_boot, offs_tai);
-}
-
 /*
  * Retrigger next event is called after clock was set
  *
@@ -648,7 +667,7 @@ static void retrigger_next_event(void *arg)
 {
        struct hrtimer_cpu_base *base = this_cpu_ptr(&hrtimer_bases);
 
-       if (!hrtimer_hres_active())
+       if (!base->hres_active)
                return;
 
        raw_spin_lock(&base->lock);
@@ -662,29 +681,19 @@ static void retrigger_next_event(void *arg)
  */
 static int hrtimer_switch_to_hres(void)
 {
-       int i, cpu = smp_processor_id();
-       struct hrtimer_cpu_base *base = &per_cpu(hrtimer_bases, cpu);
-       unsigned long flags;
-
-       if (base->hres_active)
-               return 1;
-
-       local_irq_save(flags);
+       struct hrtimer_cpu_base *base = this_cpu_ptr(&hrtimer_bases);
 
        if (tick_init_highres()) {
-               local_irq_restore(flags);
                printk(KERN_WARNING "Could not switch to high resolution "
-                                   "mode on CPU %d\n", cpu);
+                                   "mode on CPU %d\n", base->cpu);
                return 0;
        }
        base->hres_active = 1;
-       for (i = 0; i < HRTIMER_MAX_CLOCK_BASES; i++)
-               base->clock_base[i].resolution = KTIME_HIGH_RES;
+       hrtimer_resolution = HIGH_RES_NSEC;
 
        tick_setup_sched_timer();
        /* "Retrigger" the interrupt to get things going */
        retrigger_next_event(NULL);
-       local_irq_restore(flags);
        return 1;
 }
 
@@ -706,6 +715,7 @@ void clock_was_set_delayed(void)
 
 #else
 
+static inline int __hrtimer_hres_active(struct hrtimer_cpu_base *b) { return 0; }
 static inline int hrtimer_hres_active(void) { return 0; }
 static inline int hrtimer_is_hres_enabled(void) { return 0; }
 static inline int hrtimer_switch_to_hres(void) { return 0; }
@@ -803,6 +813,14 @@ void unlock_hrtimer_base(const struct hrtimer *timer, unsigned long *flags)
  *
  * Forward the timer expiry so it will expire in the future.
  * Returns the number of overruns.
+ *
+ * Can be safely called from the callback function of @timer. If
+ * called from other contexts @timer must neither be enqueued nor
+ * running the callback and the caller needs to take care of
+ * serialization.
+ *
+ * Note: This only updates the timer expiry value and does not requeue
+ * the timer.
  */
 u64 hrtimer_forward(struct hrtimer *timer, ktime_t now, ktime_t interval)
 {
@@ -814,8 +832,11 @@ u64 hrtimer_forward(struct hrtimer *timer, ktime_t now, ktime_t interval)
        if (delta.tv64 < 0)
                return 0;
 
-       if (interval.tv64 < timer->base->resolution.tv64)
-               interval.tv64 = timer->base->resolution.tv64;
+       if (WARN_ON(timer->state & HRTIMER_STATE_ENQUEUED))
+               return 0;
+
+       if (interval.tv64 < hrtimer_resolution)
+               interval.tv64 = hrtimer_resolution;
 
        if (unlikely(delta.tv64 >= interval.tv64)) {
                s64 incr = ktime_to_ns(interval);
@@ -849,16 +870,11 @@ static int enqueue_hrtimer(struct hrtimer *timer,
 {
        debug_activate(timer);
 
-       timerqueue_add(&base->active, &timer->node);
        base->cpu_base->active_bases |= 1 << base->index;
 
-       /*
-        * HRTIMER_STATE_ENQUEUED is or'ed to the current state to preserve the
-        * state of a possibly running callback.
-        */
-       timer->state |= HRTIMER_STATE_ENQUEUED;
+       timer->state = HRTIMER_STATE_ENQUEUED;
 
-       return (&timer->node == base->active.next);
+       return timerqueue_add(&base->active, &timer->node);
 }
 
 /*
@@ -875,39 +891,38 @@ static void __remove_hrtimer(struct hrtimer *timer,
                             struct hrtimer_clock_base *base,
                             unsigned long newstate, int reprogram)
 {
-       struct timerqueue_node *next_timer;
-       if (!(timer->state & HRTIMER_STATE_ENQUEUED))
-               goto out;
+       struct hrtimer_cpu_base *cpu_base = base->cpu_base;
+       unsigned int state = timer->state;
+
+       timer->state = newstate;
+       if (!(state & HRTIMER_STATE_ENQUEUED))
+               return;
+
+       if (!timerqueue_del(&base->active, &timer->node))
+               cpu_base->active_bases &= ~(1 << base->index);
 
-       next_timer = timerqueue_getnext(&base->active);
-       timerqueue_del(&base->active, &timer->node);
-       if (&timer->node == next_timer) {
 #ifdef CONFIG_HIGH_RES_TIMERS
-               /* Reprogram the clock event device. if enabled */
-               if (reprogram && hrtimer_hres_active()) {
-                       ktime_t expires;
-
-                       expires = ktime_sub(hrtimer_get_expires(timer),
-                                           base->offset);
-                       if (base->cpu_base->expires_next.tv64 == expires.tv64)
-                               hrtimer_force_reprogram(base->cpu_base, 1);
-               }
+       /*
+        * Note: If reprogram is false we do not update
+        * cpu_base->next_timer. This happens when we remove the first
+        * timer on a remote cpu. No harm as we never dereference
+        * cpu_base->next_timer. So the worst thing what can happen is
+        * an superflous call to hrtimer_force_reprogram() on the
+        * remote cpu later on if the same timer gets enqueued again.
+        */
+       if (reprogram && timer == cpu_base->next_timer)
+               hrtimer_force_reprogram(cpu_base, 1);
 #endif
-       }
-       if (!timerqueue_getnext(&base->active))
-               base->cpu_base->active_bases &= ~(1 << base->index);
-out:
-       timer->state = newstate;
 }
 
 /*
  * remove hrtimer, called with base lock held
  */
 static inline int
-remove_hrtimer(struct hrtimer *timer, struct hrtimer_clock_base *base)
+remove_hrtimer(struct hrtimer *timer, struct hrtimer_clock_base *base, bool restart)
 {
        if (hrtimer_is_queued(timer)) {
-               unsigned long state;
+               unsigned long state = timer->state;
                int reprogram;
 
                /*
@@ -921,30 +936,35 @@ remove_hrtimer(struct hrtimer *timer, struct hrtimer_clock_base *base)
                debug_deactivate(timer);
                timer_stats_hrtimer_clear_start_info(timer);
                reprogram = base->cpu_base == this_cpu_ptr(&hrtimer_bases);
-               /*
-                * We must preserve the CALLBACK state flag here,
-                * otherwise we could move the timer base in
-                * switch_hrtimer_base.
-                */
-               state = timer->state & HRTIMER_STATE_CALLBACK;
+
+               if (!restart)
+                       state = HRTIMER_STATE_INACTIVE;
+
                __remove_hrtimer(timer, base, state, reprogram);
                return 1;
        }
        return 0;
 }
 
-int __hrtimer_start_range_ns(struct hrtimer *timer, ktime_t tim,
-               unsigned long delta_ns, const enum hrtimer_mode mode,
-               int wakeup)
+/**
+ * hrtimer_start_range_ns - (re)start an hrtimer on the current CPU
+ * @timer:     the timer to be added
+ * @tim:       expiry time
+ * @delta_ns:  "slack" range for the timer
+ * @mode:      expiry mode: absolute (HRTIMER_MODE_ABS) or
+ *             relative (HRTIMER_MODE_REL)
+ */
+void hrtimer_start_range_ns(struct hrtimer *timer, ktime_t tim,
+                           unsigned long delta_ns, const enum hrtimer_mode mode)
 {
        struct hrtimer_clock_base *base, *new_base;
        unsigned long flags;
-       int ret, leftmost;
+       int leftmost;
 
        base = lock_hrtimer_base(timer, &flags);
 
        /* Remove an active timer from the queue: */
-       ret = remove_hrtimer(timer, base);
+       remove_hrtimer(timer, base, true);
 
        if (mode & HRTIMER_MODE_REL) {
                tim = ktime_add_safe(tim, base->get_time());
@@ -956,7 +976,7 @@ int __hrtimer_start_range_ns(struct hrtimer *timer, ktime_t tim,
                 * timeouts. This will go away with the GTOD framework.
                 */
 #ifdef CONFIG_TIME_LOW_RES
-               tim = ktime_add_safe(tim, base->resolution);
+               tim = ktime_add_safe(tim, ktime_set(0, hrtimer_resolution));
 #endif
        }
 
@@ -968,84 +988,24 @@ int __hrtimer_start_range_ns(struct hrtimer *timer, ktime_t tim,
        timer_stats_hrtimer_set_start_info(timer);
 
        leftmost = enqueue_hrtimer(timer, new_base);
-
-       if (!leftmost) {
-               unlock_hrtimer_base(timer, &flags);
-               return ret;
-       }
+       if (!leftmost)
+               goto unlock;
 
        if (!hrtimer_is_hres_active(timer)) {
                /*
                 * Kick to reschedule the next tick to handle the new timer
                 * on dynticks target.
                 */
-               wake_up_nohz_cpu(new_base->cpu_base->cpu);
-       } else if (new_base->cpu_base == this_cpu_ptr(&hrtimer_bases) &&
-                       hrtimer_reprogram(timer, new_base)) {
-               /*
-                * Only allow reprogramming if the new base is on this CPU.
-                * (it might still be on another CPU if the timer was pending)
-                *
-                * XXX send_remote_softirq() ?
-                */
-               if (wakeup) {
-                       /*
-                        * We need to drop cpu_base->lock to avoid a
-                        * lock ordering issue vs. rq->lock.
-                        */
-                       raw_spin_unlock(&new_base->cpu_base->lock);
-                       raise_softirq_irqoff(HRTIMER_SOFTIRQ);
-                       local_irq_restore(flags);
-                       return ret;
-               } else {
-                       __raise_softirq_irqoff(HRTIMER_SOFTIRQ);
-               }
+               if (new_base->cpu_base->nohz_active)
+                       wake_up_nohz_cpu(new_base->cpu_base->cpu);
+       } else {
+               hrtimer_reprogram(timer, new_base);
        }
-
+unlock:
        unlock_hrtimer_base(timer, &flags);
-
-       return ret;
-}
-EXPORT_SYMBOL_GPL(__hrtimer_start_range_ns);
-
-/**
- * hrtimer_start_range_ns - (re)start an hrtimer on the current CPU
- * @timer:     the timer to be added
- * @tim:       expiry time
- * @delta_ns:  "slack" range for the timer
- * @mode:      expiry mode: absolute (HRTIMER_MODE_ABS) or
- *             relative (HRTIMER_MODE_REL)
- *
- * Returns:
- *  0 on success
- *  1 when the timer was active
- */
-int hrtimer_start_range_ns(struct hrtimer *timer, ktime_t tim,
-               unsigned long delta_ns, const enum hrtimer_mode mode)
-{
-       return __hrtimer_start_range_ns(timer, tim, delta_ns, mode, 1);
 }
 EXPORT_SYMBOL_GPL(hrtimer_start_range_ns);
 
-/**
- * hrtimer_start - (re)start an hrtimer on the current CPU
- * @timer:     the timer to be added
- * @tim:       expiry time
- * @mode:      expiry mode: absolute (HRTIMER_MODE_ABS) or
- *             relative (HRTIMER_MODE_REL)
- *
- * Returns:
- *  0 on success
- *  1 when the timer was active
- */
-int
-hrtimer_start(struct hrtimer *timer, ktime_t tim, const enum hrtimer_mode mode)
-{
-       return __hrtimer_start_range_ns(timer, tim, 0, mode, 1);
-}
-EXPORT_SYMBOL_GPL(hrtimer_start);
-
-
 /**
  * hrtimer_try_to_cancel - try to deactivate a timer
  * @timer:     hrtimer to stop
@@ -1062,10 +1022,19 @@ int hrtimer_try_to_cancel(struct hrtimer *timer)
        unsigned long flags;
        int ret = -1;
 
+       /*
+        * Check lockless first. If the timer is not active (neither
+        * enqueued nor running the callback, nothing to do here.  The
+        * base lock does not serialize against a concurrent enqueue,
+        * so we can avoid taking it.
+        */
+       if (!hrtimer_active(timer))
+               return 0;
+
        base = lock_hrtimer_base(timer, &flags);
 
        if (!hrtimer_callback_running(timer))
-               ret = remove_hrtimer(timer, base);
+               ret = remove_hrtimer(timer, base, false);
 
        unlock_hrtimer_base(timer, &flags);
 
@@ -1115,26 +1084,22 @@ EXPORT_SYMBOL_GPL(hrtimer_get_remaining);
 /**
  * hrtimer_get_next_event - get the time until next expiry event
  *
- * Returns the delta to the next expiry event or KTIME_MAX if no timer
- * is pending.
+ * Returns the next expiry time or KTIME_MAX if no timer is pending.
  */
-ktime_t hrtimer_get_next_event(void)
+u64 hrtimer_get_next_event(void)
 {
        struct hrtimer_cpu_base *cpu_base = this_cpu_ptr(&hrtimer_bases);
-       ktime_t mindelta = { .tv64 = KTIME_MAX };
+       u64 expires = KTIME_MAX;
        unsigned long flags;
 
        raw_spin_lock_irqsave(&cpu_base->lock, flags);
 
-       if (!hrtimer_hres_active())
-               mindelta = ktime_sub(__hrtimer_get_next_event(cpu_base),
-                                    ktime_get());
+       if (!__hrtimer_hres_active(cpu_base))
+               expires = __hrtimer_get_next_event(cpu_base).tv64;
 
        raw_spin_unlock_irqrestore(&cpu_base->lock, flags);
 
-       if (mindelta.tv64 < 0)
-               mindelta.tv64 = 0;
-       return mindelta;
+       return expires;
 }
 #endif
 
@@ -1176,37 +1141,73 @@ void hrtimer_init(struct hrtimer *timer, clockid_t clock_id,
 }
 EXPORT_SYMBOL_GPL(hrtimer_init);
 
-/**
- * hrtimer_get_res - get the timer resolution for a clock
- * @which_clock: which clock to query
- * @tp:                 pointer to timespec variable to store the resolution
+/*
+ * A timer is active, when it is enqueued into the rbtree or the
+ * callback function is running or it's in the state of being migrated
+ * to another cpu.
  *
- * Store the resolution of the clock selected by @which_clock in the
- * variable pointed to by @tp.
+ * It is important for this function to not return a false negative.
  */
-int hrtimer_get_res(const clockid_t which_clock, struct timespec *tp)
+bool hrtimer_active(const struct hrtimer *timer)
 {
        struct hrtimer_cpu_base *cpu_base;
-       int base = hrtimer_clockid_to_base(which_clock);
+       unsigned int seq;
 
-       cpu_base = raw_cpu_ptr(&hrtimer_bases);
-       *tp = ktime_to_timespec(cpu_base->clock_base[base].resolution);
+       do {
+               cpu_base = READ_ONCE(timer->base->cpu_base);
+               seq = raw_read_seqcount_begin(&cpu_base->seq);
 
-       return 0;
+               if (timer->state != HRTIMER_STATE_INACTIVE ||
+                   cpu_base->running == timer)
+                       return true;
+
+       } while (read_seqcount_retry(&cpu_base->seq, seq) ||
+                cpu_base != READ_ONCE(timer->base->cpu_base));
+
+       return false;
 }
-EXPORT_SYMBOL_GPL(hrtimer_get_res);
+EXPORT_SYMBOL_GPL(hrtimer_active);
 
-static void __run_hrtimer(struct hrtimer *timer, ktime_t *now)
+/*
+ * The write_seqcount_barrier()s in __run_hrtimer() split the thing into 3
+ * distinct sections:
+ *
+ *  - queued:  the timer is queued
+ *  - callback:        the timer is being ran
+ *  - post:    the timer is inactive or (re)queued
+ *
+ * On the read side we ensure we observe timer->state and cpu_base->running
+ * from the same section, if anything changed while we looked at it, we retry.
+ * This includes timer->base changing because sequence numbers alone are
+ * insufficient for that.
+ *
+ * The sequence numbers are required because otherwise we could still observe
+ * a false negative if the read side got smeared over multiple consequtive
+ * __run_hrtimer() invocations.
+ */
+
+static void __run_hrtimer(struct hrtimer_cpu_base *cpu_base,
+                         struct hrtimer_clock_base *base,
+                         struct hrtimer *timer, ktime_t *now)
 {
-       struct hrtimer_clock_base *base = timer->base;
-       struct hrtimer_cpu_base *cpu_base = base->cpu_base;
        enum hrtimer_restart (*fn)(struct hrtimer *);
        int restart;
 
-       WARN_ON(!irqs_disabled());
+       lockdep_assert_held(&cpu_base->lock);
 
        debug_deactivate(timer);
-       __remove_hrtimer(timer, base, HRTIMER_STATE_CALLBACK, 0);
+       cpu_base->running = timer;
+
+       /*
+        * Separate the ->running assignment from the ->state assignment.
+        *
+        * As with a regular write barrier, this ensures the read side in
+        * hrtimer_active() cannot observe cpu_base->running == NULL &&
+        * timer->state == INACTIVE.
+        */
+       raw_write_seqcount_barrier(&cpu_base->seq);
+
+       __remove_hrtimer(timer, base, HRTIMER_STATE_INACTIVE, 0);
        timer_stats_account_hrtimer(timer);
        fn = timer->function;
 
@@ -1222,58 +1223,43 @@ static void __run_hrtimer(struct hrtimer *timer, ktime_t *now)
        raw_spin_lock(&cpu_base->lock);
 
        /*
-        * Note: We clear the CALLBACK bit after enqueue_hrtimer and
+        * Note: We clear the running state after enqueue_hrtimer and
         * we do not reprogramm the event hardware. Happens either in
         * hrtimer_start_range_ns() or in hrtimer_interrupt()
+        *
+        * Note: Because we dropped the cpu_base->lock above,
+        * hrtimer_start_range_ns() can have popped in and enqueued the timer
+        * for us already.
         */
-       if (restart != HRTIMER_NORESTART) {
-               BUG_ON(timer->state != HRTIMER_STATE_CALLBACK);
+       if (restart != HRTIMER_NORESTART &&
+           !(timer->state & HRTIMER_STATE_ENQUEUED))
                enqueue_hrtimer(timer, base);
-       }
 
-       WARN_ON_ONCE(!(timer->state & HRTIMER_STATE_CALLBACK));
+       /*
+        * Separate the ->running assignment from the ->state assignment.
+        *
+        * As with a regular write barrier, this ensures the read side in
+        * hrtimer_active() cannot observe cpu_base->running == NULL &&
+        * timer->state == INACTIVE.
+        */
+       raw_write_seqcount_barrier(&cpu_base->seq);
 
-       timer->state &= ~HRTIMER_STATE_CALLBACK;
+       WARN_ON_ONCE(cpu_base->running != timer);
+       cpu_base->running = NULL;
 }
 
-#ifdef CONFIG_HIGH_RES_TIMERS
-
-/*
- * High resolution timer interrupt
- * Called with interrupts disabled
- */
-void hrtimer_interrupt(struct clock_event_device *dev)
+static void __hrtimer_run_queues(struct hrtimer_cpu_base *cpu_base, ktime_t now)
 {
-       struct hrtimer_cpu_base *cpu_base = this_cpu_ptr(&hrtimer_bases);
-       ktime_t expires_next, now, entry_time, delta;
-       int i, retries = 0;
-
-       BUG_ON(!cpu_base->hres_active);
-       cpu_base->nr_events++;
-       dev->next_event.tv64 = KTIME_MAX;
-
-       raw_spin_lock(&cpu_base->lock);
-       entry_time = now = hrtimer_update_base(cpu_base);
-retry:
-       cpu_base->in_hrtirq = 1;
-       /*
-        * We set expires_next to KTIME_MAX here with cpu_base->lock
-        * held to prevent that a timer is enqueued in our queue via
-        * the migration code. This does not affect enqueueing of
-        * timers which run their callback and need to be requeued on
-        * this CPU.
-        */
-       cpu_base->expires_next.tv64 = KTIME_MAX;
+       struct hrtimer_clock_base *base = cpu_base->clock_base;
+       unsigned int active = cpu_base->active_bases;
 
-       for (i = 0; i < HRTIMER_MAX_CLOCK_BASES; i++) {
-               struct hrtimer_clock_base *base;
+       for (; active; base++, active >>= 1) {
                struct timerqueue_node *node;
                ktime_t basenow;
 
-               if (!(cpu_base->active_bases & (1 << i)))
+               if (!(active & 0x01))
                        continue;
 
-               base = cpu_base->clock_base + i;
                basenow = ktime_add(now, base->offset);
 
                while ((node = timerqueue_getnext(&base->active))) {
@@ -1296,9 +1282,42 @@ retry:
                        if (basenow.tv64 < hrtimer_get_softexpires_tv64(timer))
                                break;
 
-                       __run_hrtimer(timer, &basenow);
+                       __run_hrtimer(cpu_base, base, timer, &basenow);
                }
        }
+}
+
+#ifdef CONFIG_HIGH_RES_TIMERS
+
+/*
+ * High resolution timer interrupt
+ * Called with interrupts disabled
+ */
+void hrtimer_interrupt(struct clock_event_device *dev)
+{
+       struct hrtimer_cpu_base *cpu_base = this_cpu_ptr(&hrtimer_bases);
+       ktime_t expires_next, now, entry_time, delta;
+       int retries = 0;
+
+       BUG_ON(!cpu_base->hres_active);
+       cpu_base->nr_events++;
+       dev->next_event.tv64 = KTIME_MAX;
+
+       raw_spin_lock(&cpu_base->lock);
+       entry_time = now = hrtimer_update_base(cpu_base);
+retry:
+       cpu_base->in_hrtirq = 1;
+       /*
+        * We set expires_next to KTIME_MAX here with cpu_base->lock
+        * held to prevent that a timer is enqueued in our queue via
+        * the migration code. This does not affect enqueueing of
+        * timers which run their callback and need to be requeued on
+        * this CPU.
+        */
+       cpu_base->expires_next.tv64 = KTIME_MAX;
+
+       __hrtimer_run_queues(cpu_base, now);
+
        /* Reevaluate the clock bases for the next expiry */
        expires_next = __hrtimer_get_next_event(cpu_base);
        /*
@@ -1310,8 +1329,7 @@ retry:
        raw_spin_unlock(&cpu_base->lock);
 
        /* Reprogramming necessary ? */
-       if (expires_next.tv64 == KTIME_MAX ||
-           !tick_program_event(expires_next, 0)) {
+       if (!tick_program_event(expires_next, 0)) {
                cpu_base->hang_detected = 0;
                return;
        }
@@ -1344,8 +1362,8 @@ retry:
        cpu_base->hang_detected = 1;
        raw_spin_unlock(&cpu_base->lock);
        delta = ktime_sub(now, entry_time);
-       if (delta.tv64 > cpu_base->max_hang_time.tv64)
-               cpu_base->max_hang_time = delta;
+       if ((unsigned int)delta.tv64 > cpu_base->max_hang_time)
+               cpu_base->max_hang_time = (unsigned int) delta.tv64;
        /*
         * Limit it to a sensible value as we enforce a longer
         * delay. Give the CPU at least 100ms to catch up.
@@ -1363,7 +1381,7 @@ retry:
  * local version of hrtimer_peek_ahead_timers() called with interrupts
  * disabled.
  */
-static void __hrtimer_peek_ahead_timers(void)
+static inline void __hrtimer_peek_ahead_timers(void)
 {
        struct tick_device *td;
 
@@ -1375,29 +1393,6 @@ static void __hrtimer_peek_ahead_timers(void)
                hrtimer_interrupt(td->evtdev);
 }
 
-/**
- * hrtimer_peek_ahead_timers -- run soft-expired timers now
- *
- * hrtimer_peek_ahead_timers will peek at the timer queue of
- * the current cpu and check if there are any timers for which
- * the soft expires time has passed. If any such timers exist,
- * they are run immediately and then removed from the timer queue.
- *
- */
-void hrtimer_peek_ahead_timers(void)
-{
-       unsigned long flags;
-
-       local_irq_save(flags);
-       __hrtimer_peek_ahead_timers();
-       local_irq_restore(flags);
-}
-
-static void run_hrtimer_softirq(struct softirq_action *h)
-{
-       hrtimer_peek_ahead_timers();
-}
-
 #else /* CONFIG_HIGH_RES_TIMERS */
 
 static inline void __hrtimer_peek_ahead_timers(void) { }
@@ -1405,66 +1400,32 @@ static inline void __hrtimer_peek_ahead_timers(void) { }
 #endif /* !CONFIG_HIGH_RES_TIMERS */
 
 /*
- * Called from timer softirq every jiffy, expire hrtimers:
- *
- * For HRT its the fall back code to run the softirq in the timer
- * softirq context in case the hrtimer initialization failed or has
- * not been done yet.
+ * Called from run_local_timers in hardirq context every jiffy
  */
-void hrtimer_run_pending(void)
+void hrtimer_run_queues(void)
 {
-       if (hrtimer_hres_active())
+       struct hrtimer_cpu_base *cpu_base = this_cpu_ptr(&hrtimer_bases);
+       ktime_t now;
+
+       if (__hrtimer_hres_active(cpu_base))
                return;
 
        /*
-        * This _is_ ugly: We have to check in the softirq context,
-        * whether we can switch to highres and / or nohz mode. The
-        * clocksource switch happens in the timer interrupt with
-        * xtime_lock held. Notification from there only sets the
-        * check bit in the tick_oneshot code, otherwise we might
-        * deadlock vs. xtime_lock.
+        * This _is_ ugly: We have to check periodically, whether we
+        * can switch to highres and / or nohz mode. The clocksource
+        * switch happens with xtime_lock held. Notification from
+        * there only sets the check bit in the tick_oneshot code,
+        * otherwise we might deadlock vs. xtime_lock.
         */
-       if (tick_check_oneshot_change(!hrtimer_is_hres_enabled()))
+       if (tick_check_oneshot_change(!hrtimer_is_hres_enabled())) {
                hrtimer_switch_to_hres();
-}
-
-/*
- * Called from hardirq context every jiffy
- */
-void hrtimer_run_queues(void)
-{
-       struct timerqueue_node *node;
-       struct hrtimer_cpu_base *cpu_base = this_cpu_ptr(&hrtimer_bases);
-       struct hrtimer_clock_base *base;
-       int index, gettime = 1;
-
-       if (hrtimer_hres_active())
                return;
-
-       for (index = 0; index < HRTIMER_MAX_CLOCK_BASES; index++) {
-               base = &cpu_base->clock_base[index];
-               if (!timerqueue_getnext(&base->active))
-                       continue;
-
-               if (gettime) {
-                       hrtimer_get_softirq_time(cpu_base);
-                       gettime = 0;
-               }
-
-               raw_spin_lock(&cpu_base->lock);
-
-               while ((node = timerqueue_getnext(&base->active))) {
-                       struct hrtimer *timer;
-
-                       timer = container_of(node, struct hrtimer, node);
-                       if (base->softirq_time.tv64 <=
-                                       hrtimer_get_expires_tv64(timer))
-                               break;
-
-                       __run_hrtimer(timer, &base->softirq_time);
-               }
-               raw_spin_unlock(&cpu_base->lock);
        }
+
+       raw_spin_lock(&cpu_base->lock);
+       now = hrtimer_update_base(cpu_base);
+       __hrtimer_run_queues(cpu_base, now);
+       raw_spin_unlock(&cpu_base->lock);
 }
 
 /*
@@ -1497,8 +1458,6 @@ static int __sched do_nanosleep(struct hrtimer_sleeper *t, enum hrtimer_mode mod
        do {
                set_current_state(TASK_INTERRUPTIBLE);
                hrtimer_start_expires(&t->timer, mode);
-               if (!hrtimer_active(&t->timer))
-                       t->task = NULL;
 
                if (likely(t->task))
                        freezable_schedule();
@@ -1642,11 +1601,11 @@ static void migrate_hrtimer_list(struct hrtimer_clock_base *old_base,
                debug_deactivate(timer);
 
                /*
-                * Mark it as STATE_MIGRATE not INACTIVE otherwise the
+                * Mark it as ENQUEUED not INACTIVE otherwise the
                 * timer could be seen as !active and just vanish away
                 * under us on another CPU
                 */
-               __remove_hrtimer(timer, old_base, HRTIMER_STATE_MIGRATE, 0);
+               __remove_hrtimer(timer, old_base, HRTIMER_STATE_ENQUEUED, 0);
                timer->base = new_base;
                /*
                 * Enqueue the timers on the new cpu. This does not
@@ -1657,9 +1616,6 @@ static void migrate_hrtimer_list(struct hrtimer_clock_base *old_base,
                 * event device.
                 */
                enqueue_hrtimer(timer, new_base);
-
-               /* Clear the migration state bit */
-               timer->state &= ~HRTIMER_STATE_MIGRATE;
        }
 }
 
@@ -1731,9 +1687,6 @@ void __init hrtimers_init(void)
        hrtimer_cpu_notify(&hrtimers_nb, (unsigned long)CPU_UP_PREPARE,
                          (void *)(long)smp_processor_id());
        register_cpu_notifier(&hrtimers_nb);
-#ifdef CONFIG_HIGH_RES_TIMERS
-       open_softirq(HRTIMER_SOFTIRQ, run_hrtimer_softirq);
-#endif
 }
 
 /**
@@ -1772,8 +1725,6 @@ schedule_hrtimeout_range_clock(ktime_t *expires, unsigned long delta,
        hrtimer_init_sleeper(&t, current);
 
        hrtimer_start_expires(&t.timer, mode);
-       if (!hrtimer_active(&t.timer))
-               t.task = NULL;
 
        if (likely(t.task))
                schedule();
index 7a681003001c0ee75631e2c5c56e528ec0ea98df..fb4d98c7fd43e715d795d8df2c522c80b2204361 100644 (file)
@@ -35,6 +35,7 @@ unsigned long                 tick_nsec;
 static u64                     tick_length;
 static u64                     tick_length_base;
 
+#define SECS_PER_DAY           86400
 #define MAX_TICKADJ            500LL           /* usecs */
 #define MAX_TICKADJ_SCALED \
        (((MAX_TICKADJ * NSEC_PER_USEC) << NTP_SCALE_SHIFT) / NTP_INTERVAL_FREQ)
@@ -76,6 +77,9 @@ static long                   time_adjust;
 /* constant (boot-param configurable) NTP tick adjustment (upscaled)   */
 static s64                     ntp_tick_adj;
 
+/* second value of the next pending leapsecond, or TIME64_MAX if no leap */
+static time64_t                        ntp_next_leap_sec = TIME64_MAX;
+
 #ifdef CONFIG_NTP_PPS
 
 /*
@@ -349,6 +353,7 @@ void ntp_clear(void)
        tick_length     = tick_length_base;
        time_offset     = 0;
 
+       ntp_next_leap_sec = TIME64_MAX;
        /* Clear PPS state variables */
        pps_clear();
 }
@@ -359,6 +364,21 @@ u64 ntp_tick_length(void)
        return tick_length;
 }
 
+/**
+ * ntp_get_next_leap - Returns the next leapsecond in CLOCK_REALTIME ktime_t
+ *
+ * Provides the time of the next leapsecond against CLOCK_REALTIME in
+ * a ktime_t format. Returns KTIME_MAX if no leapsecond is pending.
+ */
+ktime_t ntp_get_next_leap(void)
+{
+       ktime_t ret;
+
+       if ((time_state == TIME_INS) && (time_status & STA_INS))
+               return ktime_set(ntp_next_leap_sec, 0);
+       ret.tv64 = KTIME_MAX;
+       return ret;
+}
 
 /*
  * this routine handles the overflow of the microsecond field
@@ -382,15 +402,21 @@ int second_overflow(unsigned long secs)
         */
        switch (time_state) {
        case TIME_OK:
-               if (time_status & STA_INS)
+               if (time_status & STA_INS) {
                        time_state = TIME_INS;
-               else if (time_status & STA_DEL)
+                       ntp_next_leap_sec = secs + SECS_PER_DAY -
+                                               (secs % SECS_PER_DAY);
+               } else if (time_status & STA_DEL) {
                        time_state = TIME_DEL;
+                       ntp_next_leap_sec = secs + SECS_PER_DAY -
+                                                ((secs+1) % SECS_PER_DAY);
+               }
                break;
        case TIME_INS:
-               if (!(time_status & STA_INS))
+               if (!(time_status & STA_INS)) {
+                       ntp_next_leap_sec = TIME64_MAX;
                        time_state = TIME_OK;
-               else if (secs % 86400 == 0) {
+               } else if (secs % SECS_PER_DAY == 0) {
                        leap = -1;
                        time_state = TIME_OOP;
                        printk(KERN_NOTICE
@@ -398,19 +424,21 @@ int second_overflow(unsigned long secs)
                }
                break;
        case TIME_DEL:
-               if (!(time_status & STA_DEL))
+               if (!(time_status & STA_DEL)) {
+                       ntp_next_leap_sec = TIME64_MAX;
                        time_state = TIME_OK;
-               else if ((secs + 1) % 86400 == 0) {
+               } else if ((secs + 1) % SECS_PER_DAY == 0) {
                        leap = 1;
+                       ntp_next_leap_sec = TIME64_MAX;
                        time_state = TIME_WAIT;
                        printk(KERN_NOTICE
                                "Clock: deleting leap second 23:59:59 UTC\n");
                }
                break;
        case TIME_OOP:
+               ntp_next_leap_sec = TIME64_MAX;
                time_state = TIME_WAIT;
                break;
-
        case TIME_WAIT:
                if (!(time_status & (STA_INS | STA_DEL)))
                        time_state = TIME_OK;
@@ -547,6 +575,7 @@ static inline void process_adj_status(struct timex *txc, struct timespec64 *ts)
        if ((time_status & STA_PLL) && !(txc->status & STA_PLL)) {
                time_state = TIME_OK;
                time_status = STA_UNSYNC;
+               ntp_next_leap_sec = TIME64_MAX;
                /* restart PPS frequency calibration */
                pps_reset_freq_interval();
        }
@@ -711,6 +740,24 @@ int __do_adjtimex(struct timex *txc, struct timespec64 *ts, s32 *time_tai)
        if (!(time_status & STA_NANO))
                txc->time.tv_usec /= NSEC_PER_USEC;
 
+       /* Handle leapsec adjustments */
+       if (unlikely(ts->tv_sec >= ntp_next_leap_sec)) {
+               if ((time_state == TIME_INS) && (time_status & STA_INS)) {
+                       result = TIME_OOP;
+                       txc->tai++;
+                       txc->time.tv_sec--;
+               }
+               if ((time_state == TIME_DEL) && (time_status & STA_DEL)) {
+                       result = TIME_WAIT;
+                       txc->tai--;
+                       txc->time.tv_sec++;
+               }
+               if ((time_state == TIME_OOP) &&
+                                       (ts->tv_sec == ntp_next_leap_sec)) {
+                       result = TIME_WAIT;
+               }
+       }
+
        return result;
 }
 
index bbd102ad9df7c8fcc5df253faf6970d078ba9db9..65430504ca2630c31d185429e6a2573c817b43ae 100644 (file)
@@ -5,6 +5,7 @@ extern void ntp_init(void);
 extern void ntp_clear(void);
 /* Returns how long ticks are at present, in ns / 2^NTP_SCALE_SHIFT. */
 extern u64 ntp_tick_length(void);
+extern ktime_t ntp_get_next_leap(void);
 extern int second_overflow(unsigned long secs);
 extern int ntp_validate_timex(struct timex *);
 extern int __do_adjtimex(struct timex *, struct timespec64 *, s32 *);
index 31ea01f42e1f088786a291199cc54e9bde4658c9..31d11ac9fa4739789728c44470b82115ec307d11 100644 (file)
@@ -272,13 +272,20 @@ static int posix_get_tai(clockid_t which_clock, struct timespec *tp)
        return 0;
 }
 
+static int posix_get_hrtimer_res(clockid_t which_clock, struct timespec *tp)
+{
+       tp->tv_sec = 0;
+       tp->tv_nsec = hrtimer_resolution;
+       return 0;
+}
+
 /*
  * Initialize everything, well, just everything in Posix clocks/timers ;)
  */
 static __init int init_posix_timers(void)
 {
        struct k_clock clock_realtime = {
-               .clock_getres   = hrtimer_get_res,
+               .clock_getres   = posix_get_hrtimer_res,
                .clock_get      = posix_clock_realtime_get,
                .clock_set      = posix_clock_realtime_set,
                .clock_adj      = posix_clock_realtime_adj,
@@ -290,7 +297,7 @@ static __init int init_posix_timers(void)
                .timer_del      = common_timer_del,
        };
        struct k_clock clock_monotonic = {
-               .clock_getres   = hrtimer_get_res,
+               .clock_getres   = posix_get_hrtimer_res,
                .clock_get      = posix_ktime_get_ts,
                .nsleep         = common_nsleep,
                .nsleep_restart = hrtimer_nanosleep_restart,
@@ -300,7 +307,7 @@ static __init int init_posix_timers(void)
                .timer_del      = common_timer_del,
        };
        struct k_clock clock_monotonic_raw = {
-               .clock_getres   = hrtimer_get_res,
+               .clock_getres   = posix_get_hrtimer_res,
                .clock_get      = posix_get_monotonic_raw,
        };
        struct k_clock clock_realtime_coarse = {
@@ -312,7 +319,7 @@ static __init int init_posix_timers(void)
                .clock_get      = posix_get_monotonic_coarse,
        };
        struct k_clock clock_tai = {
-               .clock_getres   = hrtimer_get_res,
+               .clock_getres   = posix_get_hrtimer_res,
                .clock_get      = posix_get_tai,
                .nsleep         = common_nsleep,
                .nsleep_restart = hrtimer_nanosleep_restart,
@@ -322,7 +329,7 @@ static __init int init_posix_timers(void)
                .timer_del      = common_timer_del,
        };
        struct k_clock clock_boottime = {
-               .clock_getres   = hrtimer_get_res,
+               .clock_getres   = posix_get_hrtimer_res,
                .clock_get      = posix_get_boottime,
                .nsleep         = common_nsleep,
                .nsleep_restart = hrtimer_nanosleep_restart,
index 6aac4beedbbe235951c0671336e52b2459a047fb..3e7db49a2381d14506a37c69b45268feeec56bef 100644 (file)
@@ -22,6 +22,7 @@ static void bc_set_mode(enum clock_event_mode mode,
                        struct clock_event_device *bc)
 {
        switch (mode) {
+       case CLOCK_EVT_MODE_UNUSED:
        case CLOCK_EVT_MODE_SHUTDOWN:
                /*
                 * Note, we cannot cancel the timer here as we might
@@ -66,9 +67,11 @@ static int bc_set_next(ktime_t expires, struct clock_event_device *bc)
         * hrtimer_{start/cancel} functions call into tracing,
         * calls to these functions must be bound within RCU_NONIDLE.
         */
-       RCU_NONIDLE(bc_moved = (hrtimer_try_to_cancel(&bctimer) >= 0) ?
-               !hrtimer_start(&bctimer, expires, HRTIMER_MODE_ABS_PINNED) :
-                       0);
+       RCU_NONIDLE({
+                       bc_moved = hrtimer_try_to_cancel(&bctimer) >= 0;
+                       if (bc_moved)
+                               hrtimer_start(&bctimer, expires,
+                                             HRTIMER_MODE_ABS_PINNED);});
        if (bc_moved) {
                /* Bind the "device" to the cpu */
                bc->bound_on = smp_processor_id();
@@ -99,10 +102,13 @@ static enum hrtimer_restart bc_handler(struct hrtimer *t)
 {
        ce_broadcast_hrtimer.event_handler(&ce_broadcast_hrtimer);
 
-       if (ce_broadcast_hrtimer.next_event.tv64 == KTIME_MAX)
+       switch (ce_broadcast_hrtimer.mode) {
+       case CLOCK_EVT_MODE_ONESHOT:
+               if (ce_broadcast_hrtimer.next_event.tv64 != KTIME_MAX)
+                       return HRTIMER_RESTART;
+       default:
                return HRTIMER_NORESTART;
-
-       return HRTIMER_RESTART;
+       }
 }
 
 void tick_setup_hrtimer_broadcast(void)
index 7e8ca4f448a88c5ad5708106bbd889e22715b3ad..d39f32cdd1b59cca97066430442e3aa054cc9eed 100644 (file)
@@ -255,18 +255,18 @@ int tick_receive_broadcast(void)
 /*
  * Broadcast the event to the cpus, which are set in the mask (mangled).
  */
-static void tick_do_broadcast(struct cpumask *mask)
+static bool tick_do_broadcast(struct cpumask *mask)
 {
        int cpu = smp_processor_id();
        struct tick_device *td;
+       bool local = false;
 
        /*
         * Check, if the current cpu is in the mask
         */
        if (cpumask_test_cpu(cpu, mask)) {
                cpumask_clear_cpu(cpu, mask);
-               td = &per_cpu(tick_cpu_device, cpu);
-               td->evtdev->event_handler(td->evtdev);
+               local = true;
        }
 
        if (!cpumask_empty(mask)) {
@@ -279,16 +279,17 @@ static void tick_do_broadcast(struct cpumask *mask)
                td = &per_cpu(tick_cpu_device, cpumask_first(mask));
                td->evtdev->broadcast(mask);
        }
+       return local;
 }
 
 /*
  * Periodic broadcast:
  * - invoke the broadcast handlers
  */
-static void tick_do_periodic_broadcast(void)
+static bool tick_do_periodic_broadcast(void)
 {
        cpumask_and(tmpmask, cpu_online_mask, tick_broadcast_mask);
-       tick_do_broadcast(tmpmask);
+       return tick_do_broadcast(tmpmask);
 }
 
 /*
@@ -296,34 +297,26 @@ static void tick_do_periodic_broadcast(void)
  */
 static void tick_handle_periodic_broadcast(struct clock_event_device *dev)
 {
-       ktime_t next;
+       struct tick_device *td = this_cpu_ptr(&tick_cpu_device);
+       bool bc_local;
 
        raw_spin_lock(&tick_broadcast_lock);
+       bc_local = tick_do_periodic_broadcast();
 
-       tick_do_periodic_broadcast();
+       if (clockevent_state_oneshot(dev)) {
+               ktime_t next = ktime_add(dev->next_event, tick_period);
 
-       /*
-        * The device is in periodic mode. No reprogramming necessary:
-        */
-       if (dev->state == CLOCK_EVT_STATE_PERIODIC)
-               goto unlock;
+               clockevents_program_event(dev, next, true);
+       }
+       raw_spin_unlock(&tick_broadcast_lock);
 
        /*
-        * Setup the next period for devices, which do not have
-        * periodic mode. We read dev->next_event first and add to it
-        * when the event already expired. clockevents_program_event()
-        * sets dev->next_event only when the event is really
-        * programmed to the device.
+        * We run the handler of the local cpu after dropping
+        * tick_broadcast_lock because the handler might deadlock when
+        * trying to switch to oneshot mode.
         */
-       for (next = dev->next_event; ;) {
-               next = ktime_add(next, tick_period);
-
-               if (!clockevents_program_event(dev, next, false))
-                       goto unlock;
-               tick_do_periodic_broadcast();
-       }
-unlock:
-       raw_spin_unlock(&tick_broadcast_lock);
+       if (bc_local)
+               td->evtdev->event_handler(td->evtdev);
 }
 
 /**
@@ -532,23 +525,19 @@ static void tick_broadcast_set_affinity(struct clock_event_device *bc,
        irq_set_affinity(bc->irq, bc->cpumask);
 }
 
-static int tick_broadcast_set_event(struct clock_event_device *bc, int cpu,
-                                   ktime_t expires, int force)
+static void tick_broadcast_set_event(struct clock_event_device *bc, int cpu,
+                                    ktime_t expires)
 {
-       int ret;
-
-       if (bc->state != CLOCK_EVT_STATE_ONESHOT)
-               clockevents_set_state(bc, CLOCK_EVT_STATE_ONESHOT);
+       if (!clockevent_state_oneshot(bc))
+               clockevents_switch_state(bc, CLOCK_EVT_STATE_ONESHOT);
 
-       ret = clockevents_program_event(bc, expires, force);
-       if (!ret)
-               tick_broadcast_set_affinity(bc, cpumask_of(cpu));
-       return ret;
+       clockevents_program_event(bc, expires, 1);
+       tick_broadcast_set_affinity(bc, cpumask_of(cpu));
 }
 
 static void tick_resume_broadcast_oneshot(struct clock_event_device *bc)
 {
-       clockevents_set_state(bc, CLOCK_EVT_STATE_ONESHOT);
+       clockevents_switch_state(bc, CLOCK_EVT_STATE_ONESHOT);
 }
 
 /*
@@ -566,7 +555,7 @@ void tick_check_oneshot_broadcast_this_cpu(void)
                 * switched over, leave the device alone.
                 */
                if (td->mode == TICKDEV_MODE_ONESHOT) {
-                       clockevents_set_state(td->evtdev,
+                       clockevents_switch_state(td->evtdev,
                                              CLOCK_EVT_STATE_ONESHOT);
                }
        }
@@ -580,9 +569,9 @@ static void tick_handle_oneshot_broadcast(struct clock_event_device *dev)
        struct tick_device *td;
        ktime_t now, next_event;
        int cpu, next_cpu = 0;
+       bool bc_local;
 
        raw_spin_lock(&tick_broadcast_lock);
-again:
        dev->next_event.tv64 = KTIME_MAX;
        next_event.tv64 = KTIME_MAX;
        cpumask_clear(tmpmask);
@@ -624,7 +613,7 @@ again:
        /*
         * Wakeup the cpus which have an expired event.
         */
-       tick_do_broadcast(tmpmask);
+       bc_local = tick_do_broadcast(tmpmask);
 
        /*
         * Two reasons for reprogram:
@@ -636,15 +625,15 @@ again:
         * - There are pending events on sleeping CPUs which were not
         * in the event mask
         */
-       if (next_event.tv64 != KTIME_MAX) {
-               /*
-                * Rearm the broadcast device. If event expired,
-                * repeat the above
-                */
-               if (tick_broadcast_set_event(dev, next_cpu, next_event, 0))
-                       goto again;
-       }
+       if (next_event.tv64 != KTIME_MAX)
+               tick_broadcast_set_event(dev, next_cpu, next_event);
+
        raw_spin_unlock(&tick_broadcast_lock);
+
+       if (bc_local) {
+               td = this_cpu_ptr(&tick_cpu_device);
+               td->evtdev->event_handler(td->evtdev);
+       }
 }
 
 static int broadcast_needs_cpu(struct clock_event_device *bc, int cpu)
@@ -670,7 +659,7 @@ static void broadcast_shutdown_local(struct clock_event_device *bc,
                if (dev->next_event.tv64 < bc->next_event.tv64)
                        return;
        }
-       clockevents_set_state(dev, CLOCK_EVT_STATE_SHUTDOWN);
+       clockevents_switch_state(dev, CLOCK_EVT_STATE_SHUTDOWN);
 }
 
 /**
@@ -726,7 +715,7 @@ int tick_broadcast_oneshot_control(enum tick_broadcast_state state)
                         */
                        if (!cpumask_test_cpu(cpu, tick_broadcast_force_mask) &&
                            dev->next_event.tv64 < bc->next_event.tv64)
-                               tick_broadcast_set_event(bc, cpu, dev->next_event, 1);
+                               tick_broadcast_set_event(bc, cpu, dev->next_event);
                }
                /*
                 * If the current CPU owns the hrtimer broadcast
@@ -740,7 +729,7 @@ int tick_broadcast_oneshot_control(enum tick_broadcast_state state)
                        cpumask_clear_cpu(cpu, tick_broadcast_oneshot_mask);
        } else {
                if (cpumask_test_and_clear_cpu(cpu, tick_broadcast_oneshot_mask)) {
-                       clockevents_set_state(dev, CLOCK_EVT_STATE_ONESHOT);
+                       clockevents_switch_state(dev, CLOCK_EVT_STATE_ONESHOT);
                        /*
                         * The cpu which was handling the broadcast
                         * timer marked this cpu in the broadcast
@@ -842,7 +831,7 @@ void tick_broadcast_setup_oneshot(struct clock_event_device *bc)
 
        /* Set it up only once ! */
        if (bc->event_handler != tick_handle_oneshot_broadcast) {
-               int was_periodic = bc->state == CLOCK_EVT_STATE_PERIODIC;
+               int was_periodic = clockevent_state_periodic(bc);
 
                bc->event_handler = tick_handle_oneshot_broadcast;
 
@@ -858,10 +847,10 @@ void tick_broadcast_setup_oneshot(struct clock_event_device *bc)
                           tick_broadcast_oneshot_mask, tmpmask);
 
                if (was_periodic && !cpumask_empty(tmpmask)) {
-                       clockevents_set_state(bc, CLOCK_EVT_STATE_ONESHOT);
+                       clockevents_switch_state(bc, CLOCK_EVT_STATE_ONESHOT);
                        tick_broadcast_init_next_event(tmpmask,
                                                       tick_next_period);
-                       tick_broadcast_set_event(bc, cpu, tick_next_period, 1);
+                       tick_broadcast_set_event(bc, cpu, tick_next_period);
                } else
                        bc->next_event.tv64 = KTIME_MAX;
        } else {
index 3ae6afa1eb98e71cc82272cd0a79a25101eff429..76446cb5dfe1a5280323f41c16299392e99efb93 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/profile.h>
 #include <linux/sched.h>
 #include <linux/module.h>
+#include <trace/events/power.h>
 
 #include <asm/irq_regs.h>
 
@@ -102,7 +103,17 @@ void tick_handle_periodic(struct clock_event_device *dev)
 
        tick_periodic(cpu);
 
-       if (dev->state != CLOCK_EVT_STATE_ONESHOT)
+#if defined(CONFIG_HIGH_RES_TIMERS) || defined(CONFIG_NO_HZ_COMMON)
+       /*
+        * The cpu might have transitioned to HIGHRES or NOHZ mode via
+        * update_process_times() -> run_local_timers() ->
+        * hrtimer_run_queues().
+        */
+       if (dev->event_handler != tick_handle_periodic)
+               return;
+#endif
+
+       if (!clockevent_state_oneshot(dev))
                return;
        for (;;) {
                /*
@@ -140,7 +151,7 @@ void tick_setup_periodic(struct clock_event_device *dev, int broadcast)
 
        if ((dev->features & CLOCK_EVT_FEAT_PERIODIC) &&
            !tick_broadcast_oneshot_active()) {
-               clockevents_set_state(dev, CLOCK_EVT_STATE_PERIODIC);
+               clockevents_switch_state(dev, CLOCK_EVT_STATE_PERIODIC);
        } else {
                unsigned long seq;
                ktime_t next;
@@ -150,7 +161,7 @@ void tick_setup_periodic(struct clock_event_device *dev, int broadcast)
                        next = tick_next_period;
                } while (read_seqretry(&jiffies_lock, seq));
 
-               clockevents_set_state(dev, CLOCK_EVT_STATE_ONESHOT);
+               clockevents_switch_state(dev, CLOCK_EVT_STATE_ONESHOT);
 
                for (;;) {
                        if (!clockevents_program_event(dev, next, false))
@@ -367,7 +378,7 @@ void tick_shutdown(unsigned int cpu)
                 * Prevent that the clock events layer tries to call
                 * the set mode function!
                 */
-               dev->state = CLOCK_EVT_STATE_DETACHED;
+               clockevent_set_state(dev, CLOCK_EVT_STATE_DETACHED);
                dev->mode = CLOCK_EVT_MODE_UNUSED;
                clockevents_exchange_device(dev, NULL);
                dev->event_handler = clockevents_handle_noop;
@@ -440,6 +451,7 @@ void tick_resume(void)
        tick_resume_local();
 }
 
+#ifdef CONFIG_SUSPEND
 static DEFINE_RAW_SPINLOCK(tick_freeze_lock);
 static unsigned int tick_freeze_depth;
 
@@ -457,10 +469,13 @@ void tick_freeze(void)
        raw_spin_lock(&tick_freeze_lock);
 
        tick_freeze_depth++;
-       if (tick_freeze_depth == num_online_cpus())
+       if (tick_freeze_depth == num_online_cpus()) {
+               trace_suspend_resume(TPS("timekeeping_freeze"),
+                                    smp_processor_id(), true);
                timekeeping_suspend();
-       else
+       } else {
                tick_suspend_local();
+       }
 
        raw_spin_unlock(&tick_freeze_lock);
 }
@@ -478,15 +493,19 @@ void tick_unfreeze(void)
 {
        raw_spin_lock(&tick_freeze_lock);
 
-       if (tick_freeze_depth == num_online_cpus())
+       if (tick_freeze_depth == num_online_cpus()) {
                timekeeping_resume();
-       else
+               trace_suspend_resume(TPS("timekeeping_freeze"),
+                                    smp_processor_id(), false);
+       } else {
                tick_resume_local();
+       }
 
        tick_freeze_depth--;
 
        raw_spin_unlock(&tick_freeze_lock);
 }
+#endif /* CONFIG_SUSPEND */
 
 /**
  * tick_init - initialize the tick control
index b64fdd8054c56b042784fdce988ebad64f2ea803..966a5a6fdd0a03c378debc87baae72672853a6fd 100644 (file)
@@ -36,11 +36,22 @@ static inline int tick_device_is_functional(struct clock_event_device *dev)
        return !(dev->features & CLOCK_EVT_FEAT_DUMMY);
 }
 
+static inline enum clock_event_state clockevent_get_state(struct clock_event_device *dev)
+{
+       return dev->state_use_accessors;
+}
+
+static inline void clockevent_set_state(struct clock_event_device *dev,
+                                       enum clock_event_state state)
+{
+       dev->state_use_accessors = state;
+}
+
 extern void clockevents_shutdown(struct clock_event_device *dev);
 extern void clockevents_exchange_device(struct clock_event_device *old,
                                        struct clock_event_device *new);
-extern void clockevents_set_state(struct clock_event_device *dev,
-                                enum clock_event_state state);
+extern void clockevents_switch_state(struct clock_event_device *dev,
+                                    enum clock_event_state state);
 extern int clockevents_program_event(struct clock_event_device *dev,
                                     ktime_t expires, bool force);
 extern void clockevents_handle_noop(struct clock_event_device *dev);
@@ -137,3 +148,19 @@ extern void tick_nohz_init(void);
 # else
 static inline void tick_nohz_init(void) { }
 #endif
+
+#ifdef CONFIG_NO_HZ_COMMON
+extern unsigned long tick_nohz_active;
+#else
+#define tick_nohz_active (0)
+#endif
+
+#if defined(CONFIG_SMP) && defined(CONFIG_NO_HZ_COMMON)
+extern void timers_update_migration(bool update_nohz);
+#else
+static inline void timers_update_migration(bool update_nohz) { }
+#endif
+
+DECLARE_PER_CPU(struct hrtimer_cpu_base, hrtimer_bases);
+
+extern u64 get_next_timer_interrupt(unsigned long basej, u64 basem);
index 67a64b1670bfdb984c7d9edec34f7eadd04800ec..b51344652330a2b12ebb4e72875515a791a8270f 100644 (file)
@@ -28,6 +28,22 @@ int tick_program_event(ktime_t expires, int force)
 {
        struct clock_event_device *dev = __this_cpu_read(tick_cpu_device.evtdev);
 
+       if (unlikely(expires.tv64 == KTIME_MAX)) {
+               /*
+                * We don't need the clock event device any more, stop it.
+                */
+               clockevents_switch_state(dev, CLOCK_EVT_STATE_ONESHOT_STOPPED);
+               return 0;
+       }
+
+       if (unlikely(clockevent_state_oneshot_stopped(dev))) {
+               /*
+                * We need the clock event again, configure it in ONESHOT mode
+                * before using it.
+                */
+               clockevents_switch_state(dev, CLOCK_EVT_STATE_ONESHOT);
+       }
+
        return clockevents_program_event(dev, expires, force);
 }
 
@@ -38,7 +54,7 @@ void tick_resume_oneshot(void)
 {
        struct clock_event_device *dev = __this_cpu_read(tick_cpu_device.evtdev);
 
-       clockevents_set_state(dev, CLOCK_EVT_STATE_ONESHOT);
+       clockevents_switch_state(dev, CLOCK_EVT_STATE_ONESHOT);
        clockevents_program_event(dev, ktime_get(), true);
 }
 
@@ -50,7 +66,7 @@ void tick_setup_oneshot(struct clock_event_device *newdev,
                        ktime_t next_event)
 {
        newdev->event_handler = handler;
-       clockevents_set_state(newdev, CLOCK_EVT_STATE_ONESHOT);
+       clockevents_switch_state(newdev, CLOCK_EVT_STATE_ONESHOT);
        clockevents_program_event(newdev, next_event, true);
 }
 
@@ -81,7 +97,7 @@ int tick_switch_to_oneshot(void (*handler)(struct clock_event_device *))
 
        td->mode = TICKDEV_MODE_ONESHOT;
        dev->event_handler = handler;
-       clockevents_set_state(dev, CLOCK_EVT_STATE_ONESHOT);
+       clockevents_switch_state(dev, CLOCK_EVT_STATE_ONESHOT);
        tick_broadcast_switch_to_oneshot();
        return 0;
 }
index 914259128145e2394e65bd36f18aaf9a81f78843..c792429e98c6de3ffb75f95f745d6cca9724585b 100644 (file)
@@ -399,7 +399,7 @@ void __init tick_nohz_init(void)
  * NO HZ enabled ?
  */
 static int tick_nohz_enabled __read_mostly  = 1;
-int tick_nohz_active  __read_mostly;
+unsigned long tick_nohz_active  __read_mostly;
 /*
  * Enable / Disable tickless mode
  */
@@ -565,156 +565,144 @@ u64 get_cpu_iowait_time_us(int cpu, u64 *last_update_time)
 }
 EXPORT_SYMBOL_GPL(get_cpu_iowait_time_us);
 
+static void tick_nohz_restart(struct tick_sched *ts, ktime_t now)
+{
+       hrtimer_cancel(&ts->sched_timer);
+       hrtimer_set_expires(&ts->sched_timer, ts->last_tick);
+
+       /* Forward the time to expire in the future */
+       hrtimer_forward(&ts->sched_timer, now, tick_period);
+
+       if (ts->nohz_mode == NOHZ_MODE_HIGHRES)
+               hrtimer_start_expires(&ts->sched_timer, HRTIMER_MODE_ABS_PINNED);
+       else
+               tick_program_event(hrtimer_get_expires(&ts->sched_timer), 1);
+}
+
 static ktime_t tick_nohz_stop_sched_tick(struct tick_sched *ts,
                                         ktime_t now, int cpu)
 {
-       unsigned long seq, last_jiffies, next_jiffies, delta_jiffies;
-       ktime_t last_update, expires, ret = { .tv64 = 0 };
-       unsigned long rcu_delta_jiffies;
        struct clock_event_device *dev = __this_cpu_read(tick_cpu_device.evtdev);
-       u64 time_delta;
-
-       time_delta = timekeeping_max_deferment();
+       u64 basemono, next_tick, next_tmr, next_rcu, delta, expires;
+       unsigned long seq, basejiff;
+       ktime_t tick;
 
        /* Read jiffies and the time when jiffies were updated last */
        do {
                seq = read_seqbegin(&jiffies_lock);
-               last_update = last_jiffies_update;
-               last_jiffies = jiffies;
+               basemono = last_jiffies_update.tv64;
+               basejiff = jiffies;
        } while (read_seqretry(&jiffies_lock, seq));
+       ts->last_jiffies = basejiff;
 
-       if (rcu_needs_cpu(&rcu_delta_jiffies) ||
+       if (rcu_needs_cpu(basemono, &next_rcu) ||
            arch_needs_cpu() || irq_work_needs_cpu()) {
-               next_jiffies = last_jiffies + 1;
-               delta_jiffies = 1;
+               next_tick = basemono + TICK_NSEC;
        } else {
-               /* Get the next timer wheel timer */
-               next_jiffies = get_next_timer_interrupt(last_jiffies);
-               delta_jiffies = next_jiffies - last_jiffies;
-               if (rcu_delta_jiffies < delta_jiffies) {
-                       next_jiffies = last_jiffies + rcu_delta_jiffies;
-                       delta_jiffies = rcu_delta_jiffies;
-               }
+               /*
+                * Get the next pending timer. If high resolution
+                * timers are enabled this only takes the timer wheel
+                * timers into account. If high resolution timers are
+                * disabled this also looks at the next expiring
+                * hrtimer.
+                */
+               next_tmr = get_next_timer_interrupt(basejiff, basemono);
+               ts->next_timer = next_tmr;
+               /* Take the next rcu event into account */
+               next_tick = next_rcu < next_tmr ? next_rcu : next_tmr;
        }
 
        /*
-        * Do not stop the tick, if we are only one off (or less)
-        * or if the cpu is required for RCU:
+        * If the tick is due in the next period, keep it ticking or
+        * restart it proper.
         */
-       if (!ts->tick_stopped && delta_jiffies <= 1)
-               goto out;
-
-       /* Schedule the tick, if we are at least one jiffie off */
-       if ((long)delta_jiffies >= 1) {
-
-               /*
-                * If this cpu is the one which updates jiffies, then
-                * give up the assignment and let it be taken by the
-                * cpu which runs the tick timer next, which might be
-                * this cpu as well. If we don't drop this here the
-                * jiffies might be stale and do_timer() never
-                * invoked. Keep track of the fact that it was the one
-                * which had the do_timer() duty last. If this cpu is
-                * the one which had the do_timer() duty last, we
-                * limit the sleep time to the timekeeping
-                * max_deferement value which we retrieved
-                * above. Otherwise we can sleep as long as we want.
-                */
-               if (cpu == tick_do_timer_cpu) {
-                       tick_do_timer_cpu = TICK_DO_TIMER_NONE;
-                       ts->do_timer_last = 1;
-               } else if (tick_do_timer_cpu != TICK_DO_TIMER_NONE) {
-                       time_delta = KTIME_MAX;
-                       ts->do_timer_last = 0;
-               } else if (!ts->do_timer_last) {
-                       time_delta = KTIME_MAX;
+       delta = next_tick - basemono;
+       if (delta <= (u64)TICK_NSEC) {
+               tick.tv64 = 0;
+               if (!ts->tick_stopped)
+                       goto out;
+               if (delta == 0) {
+                       /* Tick is stopped, but required now. Enforce it */
+                       tick_nohz_restart(ts, now);
+                       goto out;
                }
+       }
+
+       /*
+        * If this cpu is the one which updates jiffies, then give up
+        * the assignment and let it be taken by the cpu which runs
+        * the tick timer next, which might be this cpu as well. If we
+        * don't drop this here the jiffies might be stale and
+        * do_timer() never invoked. Keep track of the fact that it
+        * was the one which had the do_timer() duty last. If this cpu
+        * is the one which had the do_timer() duty last, we limit the
+        * sleep time to the timekeeping max_deferement value.
+        * Otherwise we can sleep as long as we want.
+        */
+       delta = timekeeping_max_deferment();
+       if (cpu == tick_do_timer_cpu) {
+               tick_do_timer_cpu = TICK_DO_TIMER_NONE;
+               ts->do_timer_last = 1;
+       } else if (tick_do_timer_cpu != TICK_DO_TIMER_NONE) {
+               delta = KTIME_MAX;
+               ts->do_timer_last = 0;
+       } else if (!ts->do_timer_last) {
+               delta = KTIME_MAX;
+       }
 
 #ifdef CONFIG_NO_HZ_FULL
-               if (!ts->inidle) {
-                       time_delta = min(time_delta,
-                                        scheduler_tick_max_deferment());
-               }
+       /* Limit the tick delta to the maximum scheduler deferment */
+       if (!ts->inidle)
+               delta = min(delta, scheduler_tick_max_deferment());
 #endif
 
-               /*
-                * calculate the expiry time for the next timer wheel
-                * timer. delta_jiffies >= NEXT_TIMER_MAX_DELTA signals
-                * that there is no timer pending or at least extremely
-                * far into the future (12 days for HZ=1000). In this
-                * case we set the expiry to the end of time.
-                */
-               if (likely(delta_jiffies < NEXT_TIMER_MAX_DELTA)) {
-                       /*
-                        * Calculate the time delta for the next timer event.
-                        * If the time delta exceeds the maximum time delta
-                        * permitted by the current clocksource then adjust
-                        * the time delta accordingly to ensure the
-                        * clocksource does not wrap.
-                        */
-                       time_delta = min_t(u64, time_delta,
-                                          tick_period.tv64 * delta_jiffies);
-               }
-
-               if (time_delta < KTIME_MAX)
-                       expires = ktime_add_ns(last_update, time_delta);
-               else
-                       expires.tv64 = KTIME_MAX;
-
-               /* Skip reprogram of event if its not changed */
-               if (ts->tick_stopped && ktime_equal(expires, dev->next_event))
-                       goto out;
+       /* Calculate the next expiry time */
+       if (delta < (KTIME_MAX - basemono))
+               expires = basemono + delta;
+       else
+               expires = KTIME_MAX;
 
-               ret = expires;
+       expires = min_t(u64, expires, next_tick);
+       tick.tv64 = expires;
 
-               /*
-                * nohz_stop_sched_tick can be called several times before
-                * the nohz_restart_sched_tick is called. This happens when
-                * interrupts arrive which do not cause a reschedule. In the
-                * first call we save the current tick time, so we can restart
-                * the scheduler tick in nohz_restart_sched_tick.
-                */
-               if (!ts->tick_stopped) {
-                       nohz_balance_enter_idle(cpu);
-                       calc_load_enter_idle();
+       /* Skip reprogram of event if its not changed */
+       if (ts->tick_stopped && (expires == dev->next_event.tv64))
+               goto out;
 
-                       ts->last_tick = hrtimer_get_expires(&ts->sched_timer);
-                       ts->tick_stopped = 1;
-                       trace_tick_stop(1, " ");
-               }
+       /*
+        * nohz_stop_sched_tick can be called several times before
+        * the nohz_restart_sched_tick is called. This happens when
+        * interrupts arrive which do not cause a reschedule. In the
+        * first call we save the current tick time, so we can restart
+        * the scheduler tick in nohz_restart_sched_tick.
+        */
+       if (!ts->tick_stopped) {
+               nohz_balance_enter_idle(cpu);
+               calc_load_enter_idle();
 
-               /*
-                * If the expiration time == KTIME_MAX, then
-                * in this case we simply stop the tick timer.
-                */
-                if (unlikely(expires.tv64 == KTIME_MAX)) {
-                       if (ts->nohz_mode == NOHZ_MODE_HIGHRES)
-                               hrtimer_cancel(&ts->sched_timer);
-                       goto out;
-               }
+               ts->last_tick = hrtimer_get_expires(&ts->sched_timer);
+               ts->tick_stopped = 1;
+               trace_tick_stop(1, " ");
+       }
 
-               if (ts->nohz_mode == NOHZ_MODE_HIGHRES) {
-                       hrtimer_start(&ts->sched_timer, expires,
-                                     HRTIMER_MODE_ABS_PINNED);
-                       /* Check, if the timer was already in the past */
-                       if (hrtimer_active(&ts->sched_timer))
-                               goto out;
-               } else if (!tick_program_event(expires, 0))
-                               goto out;
-               /*
-                * We are past the event already. So we crossed a
-                * jiffie boundary. Update jiffies and raise the
-                * softirq.
-                */
-               tick_do_update_jiffies64(ktime_get());
+       /*
+        * If the expiration time == KTIME_MAX, then we simply stop
+        * the tick timer.
+        */
+       if (unlikely(expires == KTIME_MAX)) {
+               if (ts->nohz_mode == NOHZ_MODE_HIGHRES)
+                       hrtimer_cancel(&ts->sched_timer);
+               goto out;
        }
-       raise_softirq_irqoff(TIMER_SOFTIRQ);
+
+       if (ts->nohz_mode == NOHZ_MODE_HIGHRES)
+               hrtimer_start(&ts->sched_timer, tick, HRTIMER_MODE_ABS_PINNED);
+       else
+               tick_program_event(tick, 1);
 out:
-       ts->next_jiffies = next_jiffies;
-       ts->last_jiffies = last_jiffies;
+       /* Update the estimated sleep length */
        ts->sleep_length = ktime_sub(dev->next_event, now);
-
-       return ret;
+       return tick;
 }
 
 static void tick_nohz_full_stop_tick(struct tick_sched *ts)
@@ -876,32 +864,6 @@ ktime_t tick_nohz_get_sleep_length(void)
        return ts->sleep_length;
 }
 
-static void tick_nohz_restart(struct tick_sched *ts, ktime_t now)
-{
-       hrtimer_cancel(&ts->sched_timer);
-       hrtimer_set_expires(&ts->sched_timer, ts->last_tick);
-
-       while (1) {
-               /* Forward the time to expire in the future */
-               hrtimer_forward(&ts->sched_timer, now, tick_period);
-
-               if (ts->nohz_mode == NOHZ_MODE_HIGHRES) {
-                       hrtimer_start_expires(&ts->sched_timer,
-                                             HRTIMER_MODE_ABS_PINNED);
-                       /* Check, if the timer was already in the past */
-                       if (hrtimer_active(&ts->sched_timer))
-                               break;
-               } else {
-                       if (!tick_program_event(
-                               hrtimer_get_expires(&ts->sched_timer), 0))
-                               break;
-               }
-               /* Reread time and update jiffies */
-               now = ktime_get();
-               tick_do_update_jiffies64(now);
-       }
-}
-
 static void tick_nohz_restart_sched_tick(struct tick_sched *ts, ktime_t now)
 {
        /* Update jiffies first */
@@ -972,12 +934,6 @@ void tick_nohz_idle_exit(void)
        local_irq_enable();
 }
 
-static int tick_nohz_reprogram(struct tick_sched *ts, ktime_t now)
-{
-       hrtimer_forward(&ts->sched_timer, now, tick_period);
-       return tick_program_event(hrtimer_get_expires(&ts->sched_timer), 0);
-}
-
 /*
  * The nohz low res interrupt handler
  */
@@ -996,10 +952,18 @@ static void tick_nohz_handler(struct clock_event_device *dev)
        if (unlikely(ts->tick_stopped))
                return;
 
-       while (tick_nohz_reprogram(ts, now)) {
-               now = ktime_get();
-               tick_do_update_jiffies64(now);
-       }
+       hrtimer_forward(&ts->sched_timer, now, tick_period);
+       tick_program_event(hrtimer_get_expires(&ts->sched_timer), 1);
+}
+
+static inline void tick_nohz_activate(struct tick_sched *ts, int mode)
+{
+       if (!tick_nohz_enabled)
+               return;
+       ts->nohz_mode = mode;
+       /* One update is enough */
+       if (!test_and_set_bit(0, &tick_nohz_active))
+               timers_update_migration(true);
 }
 
 /**
@@ -1013,13 +977,8 @@ static void tick_nohz_switch_to_nohz(void)
        if (!tick_nohz_enabled)
                return;
 
-       local_irq_disable();
-       if (tick_switch_to_oneshot(tick_nohz_handler)) {
-               local_irq_enable();
+       if (tick_switch_to_oneshot(tick_nohz_handler))
                return;
-       }
-       tick_nohz_active = 1;
-       ts->nohz_mode = NOHZ_MODE_LOWRES;
 
        /*
         * Recycle the hrtimer in ts, so we can share the
@@ -1029,13 +988,10 @@ static void tick_nohz_switch_to_nohz(void)
        /* Get the next period */
        next = tick_init_jiffy_update();
 
-       for (;;) {
-               hrtimer_set_expires(&ts->sched_timer, next);
-               if (!tick_program_event(next, 0))
-                       break;
-               next = ktime_add(next, tick_period);
-       }
-       local_irq_enable();
+       hrtimer_forward_now(&ts->sched_timer, tick_period);
+       hrtimer_set_expires(&ts->sched_timer, next);
+       tick_program_event(next, 1);
+       tick_nohz_activate(ts, NOHZ_MODE_LOWRES);
 }
 
 /*
@@ -1087,6 +1043,7 @@ static inline void tick_nohz_irq_enter(void)
 
 static inline void tick_nohz_switch_to_nohz(void) { }
 static inline void tick_nohz_irq_enter(void) { }
+static inline void tick_nohz_activate(struct tick_sched *ts, int mode) { }
 
 #endif /* CONFIG_NO_HZ_COMMON */
 
@@ -1167,22 +1124,9 @@ void tick_setup_sched_timer(void)
                hrtimer_add_expires_ns(&ts->sched_timer, offset);
        }
 
-       for (;;) {
-               hrtimer_forward(&ts->sched_timer, now, tick_period);
-               hrtimer_start_expires(&ts->sched_timer,
-                                     HRTIMER_MODE_ABS_PINNED);
-               /* Check, if the timer was already in the past */
-               if (hrtimer_active(&ts->sched_timer))
-                       break;
-               now = ktime_get();
-       }
-
-#ifdef CONFIG_NO_HZ_COMMON
-       if (tick_nohz_enabled) {
-               ts->nohz_mode = NOHZ_MODE_HIGHRES;
-               tick_nohz_active = 1;
-       }
-#endif
+       hrtimer_forward(&ts->sched_timer, now, tick_period);
+       hrtimer_start_expires(&ts->sched_timer, HRTIMER_MODE_ABS_PINNED);
+       tick_nohz_activate(ts, NOHZ_MODE_HIGHRES);
 }
 #endif /* HIGH_RES_TIMERS */
 
@@ -1227,7 +1171,7 @@ void tick_oneshot_notify(void)
  * Called cyclic from the hrtimer softirq (driven by the timer
  * softirq) allow_nohz signals, that we can switch into low-res nohz
  * mode, because high resolution timers are disabled (either compile
- * or runtime).
+ * or runtime). Called with interrupts disabled.
  */
 int tick_check_oneshot_change(int allow_nohz)
 {
index 28b5da3e1a176e62c081e965fbfc32090d3f1e74..42fdf4958bccd1c5d1593f4ebe578ea99477ddbf 100644 (file)
@@ -57,7 +57,7 @@ struct tick_sched {
        ktime_t                         iowait_sleeptime;
        ktime_t                         sleep_length;
        unsigned long                   last_jiffies;
-       unsigned long                   next_jiffies;
+       u64                             next_timer;
        ktime_t                         idle_expires;
        int                             do_timer_last;
 };
index 2c85b7724af4b0081a112e1b12cbcce4ef831117..85d5bb1d67ebc777e9dab638d3988b2fdaedf172 100644 (file)
@@ -41,7 +41,7 @@
 #include <asm/uaccess.h>
 #include <asm/unistd.h>
 
-#include "timeconst.h"
+#include <generated/timeconst.h>
 #include "timekeeping.h"
 
 /*
@@ -173,6 +173,10 @@ int do_sys_settimeofday(const struct timespec *tv, const struct timezone *tz)
                return error;
 
        if (tz) {
+               /* Verify we're witin the +-15 hrs range */
+               if (tz->tz_minuteswest > 15*60 || tz->tz_minuteswest < -15*60)
+                       return -EINVAL;
+
                sys_tz = *tz;
                update_vsyscall_tz();
                if (firsttime) {
@@ -483,9 +487,11 @@ struct timespec64 ns_to_timespec64(const s64 nsec)
 }
 EXPORT_SYMBOL(ns_to_timespec64);
 #endif
-/*
- * When we convert to jiffies then we interpret incoming values
- * the following way:
+/**
+ * msecs_to_jiffies: - convert milliseconds to jiffies
+ * @m: time in milliseconds
+ *
+ * conversion is done as follows:
  *
  * - negative values mean 'infinite timeout' (MAX_JIFFY_OFFSET)
  *
@@ -493,66 +499,36 @@ EXPORT_SYMBOL(ns_to_timespec64);
  *   MAX_JIFFY_OFFSET values] mean 'infinite timeout' too.
  *
  * - all other values are converted to jiffies by either multiplying
- *   the input value by a factor or dividing it with a factor
- *
- * We must also be careful about 32-bit overflows.
+ *   the input value by a factor or dividing it with a factor and
+ *   handling any 32-bit overflows.
+ *   for the details see __msecs_to_jiffies()
+ *
+ * msecs_to_jiffies() checks for the passed in value being a constant
+ * via __builtin_constant_p() allowing gcc to eliminate most of the
+ * code, __msecs_to_jiffies() is called if the value passed does not
+ * allow constant folding and the actual conversion must be done at
+ * runtime.
+ * the _msecs_to_jiffies helpers are the HZ dependent conversion
+ * routines found in include/linux/jiffies.h
  */
-unsigned long msecs_to_jiffies(const unsigned int m)
+unsigned long __msecs_to_jiffies(const unsigned int m)
 {
        /*
         * Negative value, means infinite timeout:
         */
        if ((int)m < 0)
                return MAX_JIFFY_OFFSET;
-
-#if HZ <= MSEC_PER_SEC && !(MSEC_PER_SEC % HZ)
-       /*
-        * HZ is equal to or smaller than 1000, and 1000 is a nice
-        * round multiple of HZ, divide with the factor between them,
-        * but round upwards:
-        */
-       return (m + (MSEC_PER_SEC / HZ) - 1) / (MSEC_PER_SEC / HZ);
-#elif HZ > MSEC_PER_SEC && !(HZ % MSEC_PER_SEC)
-       /*
-        * HZ is larger than 1000, and HZ is a nice round multiple of
-        * 1000 - simply multiply with the factor between them.
-        *
-        * But first make sure the multiplication result cannot
-        * overflow:
-        */
-       if (m > jiffies_to_msecs(MAX_JIFFY_OFFSET))
-               return MAX_JIFFY_OFFSET;
-
-       return m * (HZ / MSEC_PER_SEC);
-#else
-       /*
-        * Generic case - multiply, round and divide. But first
-        * check that if we are doing a net multiplication, that
-        * we wouldn't overflow:
-        */
-       if (HZ > MSEC_PER_SEC && m > jiffies_to_msecs(MAX_JIFFY_OFFSET))
-               return MAX_JIFFY_OFFSET;
-
-       return (MSEC_TO_HZ_MUL32 * m + MSEC_TO_HZ_ADJ32)
-               >> MSEC_TO_HZ_SHR32;
-#endif
+       return _msecs_to_jiffies(m);
 }
-EXPORT_SYMBOL(msecs_to_jiffies);
+EXPORT_SYMBOL(__msecs_to_jiffies);
 
-unsigned long usecs_to_jiffies(const unsigned int u)
+unsigned long __usecs_to_jiffies(const unsigned int u)
 {
        if (u > jiffies_to_usecs(MAX_JIFFY_OFFSET))
                return MAX_JIFFY_OFFSET;
-#if HZ <= USEC_PER_SEC && !(USEC_PER_SEC % HZ)
-       return (u + (USEC_PER_SEC / HZ) - 1) / (USEC_PER_SEC / HZ);
-#elif HZ > USEC_PER_SEC && !(HZ % USEC_PER_SEC)
-       return u * (HZ / USEC_PER_SEC);
-#else
-       return (USEC_TO_HZ_MUL32 * u + USEC_TO_HZ_ADJ32)
-               >> USEC_TO_HZ_SHR32;
-#endif
+       return _usecs_to_jiffies(u);
 }
-EXPORT_SYMBOL(usecs_to_jiffies);
+EXPORT_SYMBOL(__usecs_to_jiffies);
 
 /*
  * The TICK_NSEC - 1 rounds up the value to the next resolution.  Note
index 511bdf2cafdaa2794834ec1695ace5a587cf2022..c7388dee86358ae46967a20f39ffab6f44080d16 100644 (file)
@@ -50,7 +50,7 @@ define timeconst(hz) {
        print "#include <linux/types.h>\n\n"
 
        print "#if HZ != ", hz, "\n"
-       print "#error \qkernel/timeconst.h has the wrong HZ value!\q\n"
+       print "#error \qinclude/generated/timeconst.h has the wrong HZ value!\q\n"
        print "#endif\n\n"
 
        if (hz < 2) {
@@ -105,4 +105,5 @@ define timeconst(hz) {
        halt
 }
 
+hz = read();
 timeconst(hz)
index 946acb72179facb1c173e54592b3c1c3637f8abd..30b7a409bf1ea19001e3eeb966679137bfe74029 100644 (file)
@@ -118,18 +118,6 @@ static inline void tk_update_sleep_time(struct timekeeper *tk, ktime_t delta)
 
 #ifdef CONFIG_DEBUG_TIMEKEEPING
 #define WARNING_FREQ (HZ*300) /* 5 minute rate-limiting */
-/*
- * These simple flag variables are managed
- * without locks, which is racy, but ok since
- * we don't really care about being super
- * precise about how many events were seen,
- * just that a problem was observed.
- */
-static int timekeeping_underflow_seen;
-static int timekeeping_overflow_seen;
-
-/* last_warning is only modified under the timekeeping lock */
-static long timekeeping_last_warning;
 
 static void timekeeping_check_update(struct timekeeper *tk, cycle_t offset)
 {
@@ -149,29 +137,30 @@ static void timekeeping_check_update(struct timekeeper *tk, cycle_t offset)
                }
        }
 
-       if (timekeeping_underflow_seen) {
-               if (jiffies - timekeeping_last_warning > WARNING_FREQ) {
+       if (tk->underflow_seen) {
+               if (jiffies - tk->last_warning > WARNING_FREQ) {
                        printk_deferred("WARNING: Underflow in clocksource '%s' observed, time update ignored.\n", name);
                        printk_deferred("         Please report this, consider using a different clocksource, if possible.\n");
                        printk_deferred("         Your kernel is probably still fine.\n");
-                       timekeeping_last_warning = jiffies;
+                       tk->last_warning = jiffies;
                }
-               timekeeping_underflow_seen = 0;
+               tk->underflow_seen = 0;
        }
 
-       if (timekeeping_overflow_seen) {
-               if (jiffies - timekeeping_last_warning > WARNING_FREQ) {
+       if (tk->overflow_seen) {
+               if (jiffies - tk->last_warning > WARNING_FREQ) {
                        printk_deferred("WARNING: Overflow in clocksource '%s' observed, time update capped.\n", name);
                        printk_deferred("         Please report this, consider using a different clocksource, if possible.\n");
                        printk_deferred("         Your kernel is probably still fine.\n");
-                       timekeeping_last_warning = jiffies;
+                       tk->last_warning = jiffies;
                }
-               timekeeping_overflow_seen = 0;
+               tk->overflow_seen = 0;
        }
 }
 
 static inline cycle_t timekeeping_get_delta(struct tk_read_base *tkr)
 {
+       struct timekeeper *tk = &tk_core.timekeeper;
        cycle_t now, last, mask, max, delta;
        unsigned int seq;
 
@@ -197,13 +186,13 @@ static inline cycle_t timekeeping_get_delta(struct tk_read_base *tkr)
         * mask-relative negative values.
         */
        if (unlikely((~delta & mask) < (mask >> 3))) {
-               timekeeping_underflow_seen = 1;
+               tk->underflow_seen = 1;
                delta = 0;
        }
 
        /* Cap delta value to the max_cycles values to avoid mult overflows */
        if (unlikely(delta > max)) {
-               timekeeping_overflow_seen = 1;
+               tk->overflow_seen = 1;
                delta = tkr->clock->max_cycles;
        }
 
@@ -550,6 +539,17 @@ int pvclock_gtod_unregister_notifier(struct notifier_block *nb)
 }
 EXPORT_SYMBOL_GPL(pvclock_gtod_unregister_notifier);
 
+/*
+ * tk_update_leap_state - helper to update the next_leap_ktime
+ */
+static inline void tk_update_leap_state(struct timekeeper *tk)
+{
+       tk->next_leap_ktime = ntp_get_next_leap();
+       if (tk->next_leap_ktime.tv64 != KTIME_MAX)
+               /* Convert to monotonic time */
+               tk->next_leap_ktime = ktime_sub(tk->next_leap_ktime, tk->offs_real);
+}
+
 /*
  * Update the ktime_t based scalar nsec members of the timekeeper
  */
@@ -591,17 +591,25 @@ static void timekeeping_update(struct timekeeper *tk, unsigned int action)
                ntp_clear();
        }
 
+       tk_update_leap_state(tk);
        tk_update_ktime_data(tk);
 
        update_vsyscall(tk);
        update_pvclock_gtod(tk, action & TK_CLOCK_WAS_SET);
 
+       update_fast_timekeeper(&tk->tkr_mono, &tk_fast_mono);
+       update_fast_timekeeper(&tk->tkr_raw,  &tk_fast_raw);
+
+       if (action & TK_CLOCK_WAS_SET)
+               tk->clock_was_set_seq++;
+       /*
+        * The mirroring of the data to the shadow-timekeeper needs
+        * to happen last here to ensure we don't over-write the
+        * timekeeper structure on the next update with stale data
+        */
        if (action & TK_MIRROR)
                memcpy(&shadow_timekeeper, &tk_core.timekeeper,
                       sizeof(tk_core.timekeeper));
-
-       update_fast_timekeeper(&tk->tkr_mono, &tk_fast_mono);
-       update_fast_timekeeper(&tk->tkr_raw,  &tk_fast_raw);
 }
 
 /**
@@ -699,6 +707,23 @@ ktime_t ktime_get(void)
 }
 EXPORT_SYMBOL_GPL(ktime_get);
 
+u32 ktime_get_resolution_ns(void)
+{
+       struct timekeeper *tk = &tk_core.timekeeper;
+       unsigned int seq;
+       u32 nsecs;
+
+       WARN_ON(timekeeping_suspended);
+
+       do {
+               seq = read_seqcount_begin(&tk_core.seq);
+               nsecs = tk->tkr_mono.mult >> tk->tkr_mono.shift;
+       } while (read_seqcount_retry(&tk_core.seq, seq));
+
+       return nsecs;
+}
+EXPORT_SYMBOL_GPL(ktime_get_resolution_ns);
+
 static ktime_t *offsets[TK_OFFS_MAX] = {
        [TK_OFFS_REAL]  = &tk_core.timekeeper.offs_real,
        [TK_OFFS_BOOT]  = &tk_core.timekeeper.offs_boot,
@@ -1179,28 +1204,20 @@ void __weak read_persistent_clock64(struct timespec64 *ts64)
 }
 
 /**
- * read_boot_clock -  Return time of the system start.
+ * read_boot_clock64 -  Return time of the system start.
  *
  * Weak dummy function for arches that do not yet support it.
  * Function to read the exact time the system has been started.
- * Returns a timespec with tv_sec=0 and tv_nsec=0 if unsupported.
+ * Returns a timespec64 with tv_sec=0 and tv_nsec=0 if unsupported.
  *
  *  XXX - Do be sure to remove it once all arches implement it.
  */
-void __weak read_boot_clock(struct timespec *ts)
+void __weak read_boot_clock64(struct timespec64 *ts)
 {
        ts->tv_sec = 0;
        ts->tv_nsec = 0;
 }
 
-void __weak read_boot_clock64(struct timespec64 *ts64)
-{
-       struct timespec ts;
-
-       read_boot_clock(&ts);
-       *ts64 = timespec_to_timespec64(ts);
-}
-
 /* Flag for if timekeeping_resume() has injected sleeptime */
 static bool sleeptime_injected;
 
@@ -1836,8 +1853,9 @@ void update_wall_time(void)
         * memcpy under the tk_core.seq against one before we start
         * updating.
         */
+       timekeeping_update(tk, clock_set);
        memcpy(real_tk, tk, sizeof(*tk));
-       timekeeping_update(real_tk, clock_set);
+       /* The memcpy must come last. Do not put anything here! */
        write_seqcount_end(&tk_core.seq);
 out:
        raw_spin_unlock_irqrestore(&timekeeper_lock, flags);
@@ -1925,48 +1943,21 @@ void do_timer(unsigned long ticks)
        calc_global_load(ticks);
 }
 
-/**
- * ktime_get_update_offsets_tick - hrtimer helper
- * @offs_real: pointer to storage for monotonic -> realtime offset
- * @offs_boot: pointer to storage for monotonic -> boottime offset
- * @offs_tai:  pointer to storage for monotonic -> clock tai offset
- *
- * Returns monotonic time at last tick and various offsets
- */
-ktime_t ktime_get_update_offsets_tick(ktime_t *offs_real, ktime_t *offs_boot,
-                                                       ktime_t *offs_tai)
-{
-       struct timekeeper *tk = &tk_core.timekeeper;
-       unsigned int seq;
-       ktime_t base;
-       u64 nsecs;
-
-       do {
-               seq = read_seqcount_begin(&tk_core.seq);
-
-               base = tk->tkr_mono.base;
-               nsecs = tk->tkr_mono.xtime_nsec >> tk->tkr_mono.shift;
-
-               *offs_real = tk->offs_real;
-               *offs_boot = tk->offs_boot;
-               *offs_tai = tk->offs_tai;
-       } while (read_seqcount_retry(&tk_core.seq, seq));
-
-       return ktime_add_ns(base, nsecs);
-}
-
-#ifdef CONFIG_HIGH_RES_TIMERS
 /**
  * ktime_get_update_offsets_now - hrtimer helper
+ * @cwsseq:    pointer to check and store the clock was set sequence number
  * @offs_real: pointer to storage for monotonic -> realtime offset
  * @offs_boot: pointer to storage for monotonic -> boottime offset
  * @offs_tai:  pointer to storage for monotonic -> clock tai offset
  *
- * Returns current monotonic time and updates the offsets
+ * Returns current monotonic time and updates the offsets if the
+ * sequence number in @cwsseq and timekeeper.clock_was_set_seq are
+ * different.
+ *
  * Called from hrtimer_interrupt() or retrigger_next_event()
  */
-ktime_t ktime_get_update_offsets_now(ktime_t *offs_real, ktime_t *offs_boot,
-                                                       ktime_t *offs_tai)
+ktime_t ktime_get_update_offsets_now(unsigned int *cwsseq, ktime_t *offs_real,
+                                    ktime_t *offs_boot, ktime_t *offs_tai)
 {
        struct timekeeper *tk = &tk_core.timekeeper;
        unsigned int seq;
@@ -1978,15 +1969,23 @@ ktime_t ktime_get_update_offsets_now(ktime_t *offs_real, ktime_t *offs_boot,
 
                base = tk->tkr_mono.base;
                nsecs = timekeeping_get_ns(&tk->tkr_mono);
+               base = ktime_add_ns(base, nsecs);
+
+               if (*cwsseq != tk->clock_was_set_seq) {
+                       *cwsseq = tk->clock_was_set_seq;
+                       *offs_real = tk->offs_real;
+                       *offs_boot = tk->offs_boot;
+                       *offs_tai = tk->offs_tai;
+               }
+
+               /* Handle leapsecond insertion adjustments */
+               if (unlikely(base.tv64 >= tk->next_leap_ktime.tv64))
+                       *offs_real = ktime_sub(tk->offs_real, ktime_set(1, 0));
 
-               *offs_real = tk->offs_real;
-               *offs_boot = tk->offs_boot;
-               *offs_tai = tk->offs_tai;
        } while (read_seqcount_retry(&tk_core.seq, seq));
 
-       return ktime_add_ns(base, nsecs);
+       return base;
 }
-#endif
 
 /**
  * do_adjtimex() - Accessor function to NTP __do_adjtimex function
@@ -2027,6 +2026,8 @@ int do_adjtimex(struct timex *txc)
                __timekeeping_set_tai_offset(tk, tai);
                timekeeping_update(tk, TK_MIRROR | TK_CLOCK_WAS_SET);
        }
+       tk_update_leap_state(tk);
+
        write_seqcount_end(&tk_core.seq);
        raw_spin_unlock_irqrestore(&timekeeper_lock, flags);
 
index ead8794b9a4e470242d37684fd04079ffbd70dec..704f595ce83f03090f3f6d63a4d792703b4084a4 100644 (file)
@@ -3,19 +3,16 @@
 /*
  * Internal interfaces for kernel/time/
  */
-extern ktime_t ktime_get_update_offsets_tick(ktime_t *offs_real,
-                                               ktime_t *offs_boot,
-                                               ktime_t *offs_tai);
-extern ktime_t ktime_get_update_offsets_now(ktime_t *offs_real,
-                                               ktime_t *offs_boot,
-                                               ktime_t *offs_tai);
+extern ktime_t ktime_get_update_offsets_now(unsigned int *cwsseq,
+                                           ktime_t *offs_real,
+                                           ktime_t *offs_boot,
+                                           ktime_t *offs_tai);
 
 extern int timekeeping_valid_for_hres(void);
 extern u64 timekeeping_max_deferment(void);
 extern int timekeeping_inject_offset(struct timespec *ts);
 extern s32 timekeeping_get_tai_offset(void);
 extern void timekeeping_set_tai_offset(s32 tai_offset);
-extern void timekeeping_clocktai(struct timespec *ts);
 extern int timekeeping_suspend(void);
 extern void timekeeping_resume(void);
 
index 2ece3aa5069cade64b8c4982e920a45bea5ba232..520499dd85af42e96b2bbd8c729df36d238ad27a 100644 (file)
@@ -49,6 +49,8 @@
 #include <asm/timex.h>
 #include <asm/io.h>
 
+#include "tick-internal.h"
+
 #define CREATE_TRACE_POINTS
 #include <trace/events/timer.h>
 
@@ -68,11 +70,11 @@ EXPORT_SYMBOL(jiffies_64);
 #define MAX_TVAL ((unsigned long)((1ULL << (TVR_BITS + 4*TVN_BITS)) - 1))
 
 struct tvec {
-       struct list_head vec[TVN_SIZE];
+       struct hlist_head vec[TVN_SIZE];
 };
 
 struct tvec_root {
-       struct list_head vec[TVR_SIZE];
+       struct hlist_head vec[TVR_SIZE];
 };
 
 struct tvec_base {
@@ -83,6 +85,8 @@ struct tvec_base {
        unsigned long active_timers;
        unsigned long all_timers;
        int cpu;
+       bool migration_enabled;
+       bool nohz_active;
        struct tvec_root tv1;
        struct tvec tv2;
        struct tvec tv3;
@@ -90,43 +94,60 @@ struct tvec_base {
        struct tvec tv5;
 } ____cacheline_aligned;
 
-/*
- * __TIMER_INITIALIZER() needs to set ->base to a valid pointer (because we've
- * made NULL special, hint: lock_timer_base()) and we cannot get a compile time
- * pointer to per-cpu entries because we don't know where we'll map the section,
- * even for the boot cpu.
- *
- * And so we use boot_tvec_bases for boot CPU and per-cpu __tvec_bases for the
- * rest of them.
- */
-struct tvec_base boot_tvec_bases;
-EXPORT_SYMBOL(boot_tvec_bases);
 
-static DEFINE_PER_CPU(struct tvec_base *, tvec_bases) = &boot_tvec_bases;
+static DEFINE_PER_CPU(struct tvec_base, tvec_bases);
+
+#if defined(CONFIG_SMP) && defined(CONFIG_NO_HZ_COMMON)
+unsigned int sysctl_timer_migration = 1;
 
-/* Functions below help us manage 'deferrable' flag */
-static inline unsigned int tbase_get_deferrable(struct tvec_base *base)
+void timers_update_migration(bool update_nohz)
 {
-       return ((unsigned int)(unsigned long)base & TIMER_DEFERRABLE);
+       bool on = sysctl_timer_migration && tick_nohz_active;
+       unsigned int cpu;
+
+       /* Avoid the loop, if nothing to update */
+       if (this_cpu_read(tvec_bases.migration_enabled) == on)
+               return;
+
+       for_each_possible_cpu(cpu) {
+               per_cpu(tvec_bases.migration_enabled, cpu) = on;
+               per_cpu(hrtimer_bases.migration_enabled, cpu) = on;
+               if (!update_nohz)
+                       continue;
+               per_cpu(tvec_bases.nohz_active, cpu) = true;
+               per_cpu(hrtimer_bases.nohz_active, cpu) = true;
+       }
 }
 
-static inline unsigned int tbase_get_irqsafe(struct tvec_base *base)
+int timer_migration_handler(struct ctl_table *table, int write,
+                           void __user *buffer, size_t *lenp,
+                           loff_t *ppos)
 {
-       return ((unsigned int)(unsigned long)base & TIMER_IRQSAFE);
+       static DEFINE_MUTEX(mutex);
+       int ret;
+
+       mutex_lock(&mutex);
+       ret = proc_dointvec(table, write, buffer, lenp, ppos);
+       if (!ret && write)
+               timers_update_migration(false);
+       mutex_unlock(&mutex);
+       return ret;
 }
 
-static inline struct tvec_base *tbase_get_base(struct tvec_base *base)
+static inline struct tvec_base *get_target_base(struct tvec_base *base,
+                                               int pinned)
 {
-       return ((struct tvec_base *)((unsigned long)base & ~TIMER_FLAG_MASK));
+       if (pinned || !base->migration_enabled)
+               return this_cpu_ptr(&tvec_bases);
+       return per_cpu_ptr(&tvec_bases, get_nohz_timer_target());
 }
-
-static inline void
-timer_set_base(struct timer_list *timer, struct tvec_base *new_base)
+#else
+static inline struct tvec_base *get_target_base(struct tvec_base *base,
+                                               int pinned)
 {
-       unsigned long flags = (unsigned long)timer->base & TIMER_FLAG_MASK;
-
-       timer->base = (struct tvec_base *)((unsigned long)(new_base) | flags);
+       return this_cpu_ptr(&tvec_bases);
 }
+#endif
 
 static unsigned long round_jiffies_common(unsigned long j, int cpu,
                bool force_up)
@@ -349,26 +370,12 @@ void set_timer_slack(struct timer_list *timer, int slack_hz)
 }
 EXPORT_SYMBOL_GPL(set_timer_slack);
 
-/*
- * If the list is empty, catch up ->timer_jiffies to the current time.
- * The caller must hold the tvec_base lock.  Returns true if the list
- * was empty and therefore ->timer_jiffies was updated.
- */
-static bool catchup_timer_jiffies(struct tvec_base *base)
-{
-       if (!base->all_timers) {
-               base->timer_jiffies = jiffies;
-               return true;
-       }
-       return false;
-}
-
 static void
 __internal_add_timer(struct tvec_base *base, struct timer_list *timer)
 {
        unsigned long expires = timer->expires;
        unsigned long idx = expires - base->timer_jiffies;
-       struct list_head *vec;
+       struct hlist_head *vec;
 
        if (idx < TVR_SIZE) {
                int i = expires & TVR_MASK;
@@ -401,25 +408,25 @@ __internal_add_timer(struct tvec_base *base, struct timer_list *timer)
                i = (expires >> (TVR_BITS + 3 * TVN_BITS)) & TVN_MASK;
                vec = base->tv5.vec + i;
        }
-       /*
-        * Timers are FIFO:
-        */
-       list_add_tail(&timer->entry, vec);
+
+       hlist_add_head(&timer->entry, vec);
 }
 
 static void internal_add_timer(struct tvec_base *base, struct timer_list *timer)
 {
-       (void)catchup_timer_jiffies(base);
+       /* Advance base->jiffies, if the base is empty */
+       if (!base->all_timers++)
+               base->timer_jiffies = jiffies;
+
        __internal_add_timer(base, timer);
        /*
         * Update base->active_timers and base->next_timer
         */
-       if (!tbase_get_deferrable(timer->base)) {
+       if (!(timer->flags & TIMER_DEFERRABLE)) {
                if (!base->active_timers++ ||
                    time_before(timer->expires, base->next_timer))
                        base->next_timer = timer->expires;
        }
-       base->all_timers++;
 
        /*
         * Check whether the other CPU is in dynticks mode and needs
@@ -434,8 +441,11 @@ static void internal_add_timer(struct tvec_base *base, struct timer_list *timer)
         * require special care against races with idle_cpu(), lets deal
         * with that later.
         */
-       if (!tbase_get_deferrable(base) || tick_nohz_full_cpu(base->cpu))
-               wake_up_nohz_cpu(base->cpu);
+       if (base->nohz_active) {
+               if (!(timer->flags & TIMER_DEFERRABLE) ||
+                   tick_nohz_full_cpu(base->cpu))
+                       wake_up_nohz_cpu(base->cpu);
+       }
 }
 
 #ifdef CONFIG_TIMER_STATS
@@ -451,15 +461,12 @@ void __timer_stats_timer_set_start_info(struct timer_list *timer, void *addr)
 
 static void timer_stats_account_timer(struct timer_list *timer)
 {
-       unsigned int flag = 0;
-
        if (likely(!timer->start_site))
                return;
-       if (unlikely(tbase_get_deferrable(timer->base)))
-               flag |= TIMER_STATS_FLAG_DEFERRABLE;
 
        timer_stats_update_stats(timer, timer->start_pid, timer->start_site,
-                                timer->function, timer->start_comm, flag);
+                                timer->function, timer->start_comm,
+                                timer->flags);
 }
 
 #else
@@ -516,8 +523,8 @@ static int timer_fixup_activate(void *addr, enum debug_obj_state state)
                 * statically initialized. We just make sure that it
                 * is tracked in the object tracker.
                 */
-               if (timer->entry.next == NULL &&
-                   timer->entry.prev == TIMER_ENTRY_STATIC) {
+               if (timer->entry.pprev == NULL &&
+                   timer->entry.next == TIMER_ENTRY_STATIC) {
                        debug_object_init(timer, &timer_debug_descr);
                        debug_object_activate(timer, &timer_debug_descr);
                        return 0;
@@ -563,7 +570,7 @@ static int timer_fixup_assert_init(void *addr, enum debug_obj_state state)
 
        switch (state) {
        case ODEBUG_STATE_NOTAVAILABLE:
-               if (timer->entry.prev == TIMER_ENTRY_STATIC) {
+               if (timer->entry.next == TIMER_ENTRY_STATIC) {
                        /*
                         * This is not really a fixup. The timer was
                         * statically initialized. We just make sure that it
@@ -648,7 +655,7 @@ static inline void
 debug_activate(struct timer_list *timer, unsigned long expires)
 {
        debug_timer_activate(timer);
-       trace_timer_start(timer, expires);
+       trace_timer_start(timer, expires, timer->flags);
 }
 
 static inline void debug_deactivate(struct timer_list *timer)
@@ -665,10 +672,8 @@ static inline void debug_assert_init(struct timer_list *timer)
 static void do_init_timer(struct timer_list *timer, unsigned int flags,
                          const char *name, struct lock_class_key *key)
 {
-       struct tvec_base *base = raw_cpu_read(tvec_bases);
-
-       timer->entry.next = NULL;
-       timer->base = (void *)((unsigned long)base | flags);
+       timer->entry.pprev = NULL;
+       timer->flags = flags | raw_smp_processor_id();
        timer->slack = -1;
 #ifdef CONFIG_TIMER_STATS
        timer->start_site = NULL;
@@ -699,24 +704,23 @@ EXPORT_SYMBOL(init_timer_key);
 
 static inline void detach_timer(struct timer_list *timer, bool clear_pending)
 {
-       struct list_head *entry = &timer->entry;
+       struct hlist_node *entry = &timer->entry;
 
        debug_deactivate(timer);
 
-       __list_del(entry->prev, entry->next);
+       __hlist_del(entry);
        if (clear_pending)
-               entry->next = NULL;
-       entry->prev = LIST_POISON2;
+               entry->pprev = NULL;
+       entry->next = LIST_POISON2;
 }
 
 static inline void
 detach_expired_timer(struct timer_list *timer, struct tvec_base *base)
 {
        detach_timer(timer, true);
-       if (!tbase_get_deferrable(timer->base))
+       if (!(timer->flags & TIMER_DEFERRABLE))
                base->active_timers--;
        base->all_timers--;
-       (void)catchup_timer_jiffies(base);
 }
 
 static int detach_if_pending(struct timer_list *timer, struct tvec_base *base,
@@ -726,13 +730,14 @@ static int detach_if_pending(struct timer_list *timer, struct tvec_base *base,
                return 0;
 
        detach_timer(timer, clear_pending);
-       if (!tbase_get_deferrable(timer->base)) {
+       if (!(timer->flags & TIMER_DEFERRABLE)) {
                base->active_timers--;
                if (timer->expires == base->next_timer)
                        base->next_timer = base->timer_jiffies;
        }
-       base->all_timers--;
-       (void)catchup_timer_jiffies(base);
+       /* If this was the last timer, advance base->jiffies */
+       if (!--base->all_timers)
+               base->timer_jiffies = jiffies;
        return 1;
 }
 
@@ -744,24 +749,22 @@ static int detach_if_pending(struct timer_list *timer, struct tvec_base *base,
  * So __run_timers/migrate_timers can safely modify all timers which could
  * be found on ->tvX lists.
  *
- * When the timer's base is locked, and the timer removed from list, it is
- * possible to set timer->base = NULL and drop the lock: the timer remains
- * locked.
+ * When the timer's base is locked and removed from the list, the
+ * TIMER_MIGRATING flag is set, FIXME
  */
 static struct tvec_base *lock_timer_base(struct timer_list *timer,
                                        unsigned long *flags)
        __acquires(timer->base->lock)
 {
-       struct tvec_base *base;
-
        for (;;) {
-               struct tvec_base *prelock_base = timer->base;
-               base = tbase_get_base(prelock_base);
-               if (likely(base != NULL)) {
+               u32 tf = timer->flags;
+               struct tvec_base *base;
+
+               if (!(tf & TIMER_MIGRATING)) {
+                       base = per_cpu_ptr(&tvec_bases, tf & TIMER_CPUMASK);
                        spin_lock_irqsave(&base->lock, *flags);
-                       if (likely(prelock_base == timer->base))
+                       if (timer->flags == tf)
                                return base;
-                       /* The timer has migrated to another CPU */
                        spin_unlock_irqrestore(&base->lock, *flags);
                }
                cpu_relax();
@@ -770,11 +773,11 @@ static struct tvec_base *lock_timer_base(struct timer_list *timer,
 
 static inline int
 __mod_timer(struct timer_list *timer, unsigned long expires,
-                                               bool pending_only, int pinned)
+           bool pending_only, int pinned)
 {
        struct tvec_base *base, *new_base;
        unsigned long flags;
-       int ret = 0 , cpu;
+       int ret = 0;
 
        timer_stats_timer_set_start_info(timer);
        BUG_ON(!timer->function);
@@ -787,8 +790,7 @@ __mod_timer(struct timer_list *timer, unsigned long expires,
 
        debug_activate(timer, expires);
 
-       cpu = get_nohz_timer_target(pinned);
-       new_base = per_cpu(tvec_bases, cpu);
+       new_base = get_target_base(base, pinned);
 
        if (base != new_base) {
                /*
@@ -800,11 +802,13 @@ __mod_timer(struct timer_list *timer, unsigned long expires,
                 */
                if (likely(base->running_timer != timer)) {
                        /* See the comment in lock_timer_base() */
-                       timer_set_base(timer, NULL);
+                       timer->flags |= TIMER_MIGRATING;
+
                        spin_unlock(&base->lock);
                        base = new_base;
                        spin_lock(&base->lock);
-                       timer_set_base(timer, base);
+                       timer->flags &= ~TIMER_BASEMASK;
+                       timer->flags |= base->cpu;
                }
        }
 
@@ -966,13 +970,13 @@ EXPORT_SYMBOL(add_timer);
  */
 void add_timer_on(struct timer_list *timer, int cpu)
 {
-       struct tvec_base *base = per_cpu(tvec_bases, cpu);
+       struct tvec_base *base = per_cpu_ptr(&tvec_bases, cpu);
        unsigned long flags;
 
        timer_stats_timer_set_start_info(timer);
        BUG_ON(timer_pending(timer) || !timer->function);
        spin_lock_irqsave(&base->lock, flags);
-       timer_set_base(timer, base);
+       timer->flags = (timer->flags & ~TIMER_BASEMASK) | cpu;
        debug_activate(timer, timer->expires);
        internal_add_timer(base, timer);
        spin_unlock_irqrestore(&base->lock, flags);
@@ -1037,8 +1041,6 @@ int try_to_del_timer_sync(struct timer_list *timer)
 EXPORT_SYMBOL(try_to_del_timer_sync);
 
 #ifdef CONFIG_SMP
-static DEFINE_PER_CPU(struct tvec_base, __tvec_bases);
-
 /**
  * del_timer_sync - deactivate a timer and wait for the handler to finish.
  * @timer: the timer to be deactivated
@@ -1093,7 +1095,7 @@ int del_timer_sync(struct timer_list *timer)
         * don't use it in hardirq context, because it
         * could lead to deadlock.
         */
-       WARN_ON(in_irq() && !tbase_get_irqsafe(timer->base));
+       WARN_ON(in_irq() && !(timer->flags & TIMER_IRQSAFE));
        for (;;) {
                int ret = try_to_del_timer_sync(timer);
                if (ret >= 0)
@@ -1107,17 +1109,17 @@ EXPORT_SYMBOL(del_timer_sync);
 static int cascade(struct tvec_base *base, struct tvec *tv, int index)
 {
        /* cascade all the timers from tv up one level */
-       struct timer_list *timer, *tmp;
-       struct list_head tv_list;
+       struct timer_list *timer;
+       struct hlist_node *tmp;
+       struct hlist_head tv_list;
 
-       list_replace_init(tv->vec + index, &tv_list);
+       hlist_move_list(tv->vec + index, &tv_list);
 
        /*
         * We are removing _all_ timers from the list, so we
         * don't have to detach them individually.
         */
-       list_for_each_entry_safe(timer, tmp, &tv_list, entry) {
-               BUG_ON(tbase_get_base(timer->base) != base);
+       hlist_for_each_entry_safe(timer, tmp, &tv_list, entry) {
                /* No accounting, while moving them */
                __internal_add_timer(base, timer);
        }
@@ -1182,14 +1184,18 @@ static inline void __run_timers(struct tvec_base *base)
        struct timer_list *timer;
 
        spin_lock_irq(&base->lock);
-       if (catchup_timer_jiffies(base)) {
-               spin_unlock_irq(&base->lock);
-               return;
-       }
+
        while (time_after_eq(jiffies, base->timer_jiffies)) {
-               struct list_head work_list;
-               struct list_head *head = &work_list;
-               int index = base->timer_jiffies & TVR_MASK;
+               struct hlist_head work_list;
+               struct hlist_head *head = &work_list;
+               int index;
+
+               if (!base->all_timers) {
+                       base->timer_jiffies = jiffies;
+                       break;
+               }
+
+               index = base->timer_jiffies & TVR_MASK;
 
                /*
                 * Cascade timers:
@@ -1200,16 +1206,16 @@ static inline void __run_timers(struct tvec_base *base)
                                        !cascade(base, &base->tv4, INDEX(2)))
                        cascade(base, &base->tv5, INDEX(3));
                ++base->timer_jiffies;
-               list_replace_init(base->tv1.vec + index, head);
-               while (!list_empty(head)) {
+               hlist_move_list(base->tv1.vec + index, head);
+               while (!hlist_empty(head)) {
                        void (*fn)(unsigned long);
                        unsigned long data;
                        bool irqsafe;
 
-                       timer = list_first_entry(head, struct timer_list,entry);
+                       timer = hlist_entry(head->first, struct timer_list, entry);
                        fn = timer->function;
                        data = timer->data;
-                       irqsafe = tbase_get_irqsafe(timer->base);
+                       irqsafe = timer->flags & TIMER_IRQSAFE;
 
                        timer_stats_account_timer(timer);
 
@@ -1248,8 +1254,8 @@ static unsigned long __next_timer_interrupt(struct tvec_base *base)
        /* Look for timer events in tv1. */
        index = slot = timer_jiffies & TVR_MASK;
        do {
-               list_for_each_entry(nte, base->tv1.vec + slot, entry) {
-                       if (tbase_get_deferrable(nte->base))
+               hlist_for_each_entry(nte, base->tv1.vec + slot, entry) {
+                       if (nte->flags & TIMER_DEFERRABLE)
                                continue;
 
                        found = 1;
@@ -1279,8 +1285,8 @@ cascade:
 
                index = slot = timer_jiffies & TVN_MASK;
                do {
-                       list_for_each_entry(nte, varp->vec + slot, entry) {
-                               if (tbase_get_deferrable(nte->base))
+                       hlist_for_each_entry(nte, varp->vec + slot, entry) {
+                               if (nte->flags & TIMER_DEFERRABLE)
                                        continue;
 
                                found = 1;
@@ -1311,54 +1317,48 @@ cascade:
  * Check, if the next hrtimer event is before the next timer wheel
  * event:
  */
-static unsigned long cmp_next_hrtimer_event(unsigned long now,
-                                           unsigned long expires)
+static u64 cmp_next_hrtimer_event(u64 basem, u64 expires)
 {
-       ktime_t hr_delta = hrtimer_get_next_event();
-       struct timespec tsdelta;
-       unsigned long delta;
-
-       if (hr_delta.tv64 == KTIME_MAX)
-               return expires;
+       u64 nextevt = hrtimer_get_next_event();
 
        /*
-        * Expired timer available, let it expire in the next tick
+        * If high resolution timers are enabled
+        * hrtimer_get_next_event() returns KTIME_MAX.
         */
-       if (hr_delta.tv64 <= 0)
-               return now + 1;
-
-       tsdelta = ktime_to_timespec(hr_delta);
-       delta = timespec_to_jiffies(&tsdelta);
+       if (expires <= nextevt)
+               return expires;
 
        /*
-        * Limit the delta to the max value, which is checked in
-        * tick_nohz_stop_sched_tick():
+        * If the next timer is already expired, return the tick base
+        * time so the tick is fired immediately.
         */
-       if (delta > NEXT_TIMER_MAX_DELTA)
-               delta = NEXT_TIMER_MAX_DELTA;
+       if (nextevt <= basem)
+               return basem;
 
        /*
-        * Take rounding errors in to account and make sure, that it
-        * expires in the next tick. Otherwise we go into an endless
-        * ping pong due to tick_nohz_stop_sched_tick() retriggering
-        * the timer softirq
+        * Round up to the next jiffie. High resolution timers are
+        * off, so the hrtimers are expired in the tick and we need to
+        * make sure that this tick really expires the timer to avoid
+        * a ping pong of the nohz stop code.
+        *
+        * Use DIV_ROUND_UP_ULL to prevent gcc calling __divdi3
         */
-       if (delta < 1)
-               delta = 1;
-       now += delta;
-       if (time_before(now, expires))
-               return now;
-       return expires;
+       return DIV_ROUND_UP_ULL(nextevt, TICK_NSEC) * TICK_NSEC;
 }
 
 /**
- * get_next_timer_interrupt - return the jiffy of the next pending timer
- * @now: current time (in jiffies)
+ * get_next_timer_interrupt - return the time (clock mono) of the next timer
+ * @basej:     base time jiffies
+ * @basem:     base time clock monotonic
+ *
+ * Returns the tick aligned clock monotonic time of the next pending
+ * timer or KTIME_MAX if no timer is pending.
  */
-unsigned long get_next_timer_interrupt(unsigned long now)
+u64 get_next_timer_interrupt(unsigned long basej, u64 basem)
 {
-       struct tvec_base *base = __this_cpu_read(tvec_bases);
-       unsigned long expires = now + NEXT_TIMER_MAX_DELTA;
+       struct tvec_base *base = this_cpu_ptr(&tvec_bases);
+       u64 expires = KTIME_MAX;
+       unsigned long nextevt;
 
        /*
         * Pretend that there is no timer pending if the cpu is offline.
@@ -1371,14 +1371,15 @@ unsigned long get_next_timer_interrupt(unsigned long now)
        if (base->active_timers) {
                if (time_before_eq(base->next_timer, base->timer_jiffies))
                        base->next_timer = __next_timer_interrupt(base);
-               expires = base->next_timer;
+               nextevt = base->next_timer;
+               if (time_before_eq(nextevt, basej))
+                       expires = basem;
+               else
+                       expires = basem + (nextevt - basej) * TICK_NSEC;
        }
        spin_unlock(&base->lock);
 
-       if (time_before_eq(expires, now))
-               return now;
-
-       return cmp_next_hrtimer_event(now, expires);
+       return cmp_next_hrtimer_event(basem, expires);
 }
 #endif
 
@@ -1407,9 +1408,7 @@ void update_process_times(int user_tick)
  */
 static void run_timer_softirq(struct softirq_action *h)
 {
-       struct tvec_base *base = __this_cpu_read(tvec_bases);
-
-       hrtimer_run_pending();
+       struct tvec_base *base = this_cpu_ptr(&tvec_bases);
 
        if (time_after_eq(jiffies, base->timer_jiffies))
                __run_timers(base);
@@ -1545,15 +1544,16 @@ signed long __sched schedule_timeout_uninterruptible(signed long timeout)
 EXPORT_SYMBOL(schedule_timeout_uninterruptible);
 
 #ifdef CONFIG_HOTPLUG_CPU
-static void migrate_timer_list(struct tvec_base *new_base, struct list_head *head)
+static void migrate_timer_list(struct tvec_base *new_base, struct hlist_head *head)
 {
        struct timer_list *timer;
+       int cpu = new_base->cpu;
 
-       while (!list_empty(head)) {
-               timer = list_first_entry(head, struct timer_list, entry);
+       while (!hlist_empty(head)) {
+               timer = hlist_entry(head->first, struct timer_list, entry);
                /* We ignore the accounting on the dying cpu */
                detach_timer(timer, false);
-               timer_set_base(timer, new_base);
+               timer->flags = (timer->flags & ~TIMER_BASEMASK) | cpu;
                internal_add_timer(new_base, timer);
        }
 }
@@ -1565,8 +1565,8 @@ static void migrate_timers(int cpu)
        int i;
 
        BUG_ON(cpu_online(cpu));
-       old_base = per_cpu(tvec_bases, cpu);
-       new_base = get_cpu_var(tvec_bases);
+       old_base = per_cpu_ptr(&tvec_bases, cpu);
+       new_base = this_cpu_ptr(&tvec_bases);
        /*
         * The caller is globally serialized and nobody else
         * takes two locks at once, deadlock is not possible.
@@ -1590,7 +1590,6 @@ static void migrate_timers(int cpu)
 
        spin_unlock(&old_base->lock);
        spin_unlock_irq(&new_base->lock);
-       put_cpu_var(tvec_bases);
 }
 
 static int timer_cpu_notify(struct notifier_block *self,
@@ -1616,52 +1615,27 @@ static inline void timer_register_cpu_notifier(void)
 static inline void timer_register_cpu_notifier(void) { }
 #endif /* CONFIG_HOTPLUG_CPU */
 
-static void __init init_timer_cpu(struct tvec_base *base, int cpu)
+static void __init init_timer_cpu(int cpu)
 {
-       int j;
-
-       BUG_ON(base != tbase_get_base(base));
+       struct tvec_base *base = per_cpu_ptr(&tvec_bases, cpu);
 
        base->cpu = cpu;
-       per_cpu(tvec_bases, cpu) = base;
        spin_lock_init(&base->lock);
 
-       for (j = 0; j < TVN_SIZE; j++) {
-               INIT_LIST_HEAD(base->tv5.vec + j);
-               INIT_LIST_HEAD(base->tv4.vec + j);
-               INIT_LIST_HEAD(base->tv3.vec + j);
-               INIT_LIST_HEAD(base->tv2.vec + j);
-       }
-       for (j = 0; j < TVR_SIZE; j++)
-               INIT_LIST_HEAD(base->tv1.vec + j);
-
        base->timer_jiffies = jiffies;
        base->next_timer = base->timer_jiffies;
 }
 
 static void __init init_timer_cpus(void)
 {
-       struct tvec_base *base;
-       int local_cpu = smp_processor_id();
        int cpu;
 
-       for_each_possible_cpu(cpu) {
-               if (cpu == local_cpu)
-                       base = &boot_tvec_bases;
-#ifdef CONFIG_SMP
-               else
-                       base = per_cpu_ptr(&__tvec_bases, cpu);
-#endif
-
-               init_timer_cpu(base, cpu);
-       }
+       for_each_possible_cpu(cpu)
+               init_timer_cpu(cpu);
 }
 
 void __init init_timers(void)
 {
-       /* ensure there are enough low bits for flags in timer->base pointer */
-       BUILD_BUG_ON(__alignof__(struct tvec_base) & TIMER_FLAG_MASK);
-
        init_timer_cpus();
        init_timer_stats();
        timer_register_cpu_notifier();
@@ -1697,14 +1671,14 @@ unsigned long msleep_interruptible(unsigned int msecs)
 
 EXPORT_SYMBOL(msleep_interruptible);
 
-static int __sched do_usleep_range(unsigned long min, unsigned long max)
+static void __sched do_usleep_range(unsigned long min, unsigned long max)
 {
        ktime_t kmin;
        unsigned long delta;
 
        kmin = ktime_set(0, min * NSEC_PER_USEC);
        delta = (max - min) * NSEC_PER_USEC;
-       return schedule_hrtimeout_range(&kmin, delta, HRTIMER_MODE_REL);
+       schedule_hrtimeout_range(&kmin, delta, HRTIMER_MODE_REL);
 }
 
 /**
@@ -1712,7 +1686,7 @@ static int __sched do_usleep_range(unsigned long min, unsigned long max)
  * @min: Minimum time in usecs to sleep
  * @max: Maximum time in usecs to sleep
  */
-void usleep_range(unsigned long min, unsigned long max)
+void __sched usleep_range(unsigned long min, unsigned long max)
 {
        __set_current_state(TASK_UNINTERRUPTIBLE);
        do_usleep_range(min, max);
index e878c2e0ba45e06c4690646a8853406e11dd1a15..a4536e1e3e2ab7f0e298322e2217474a2f05579d 100644 (file)
@@ -29,19 +29,24 @@ struct timer_list_iter {
 
 typedef void (*print_fn_t)(struct seq_file *m, unsigned int *classes);
 
-DECLARE_PER_CPU(struct hrtimer_cpu_base, hrtimer_bases);
-
 /*
  * This allows printing both to /proc/timer_list and
  * to the console (on SysRq-Q):
  */
-#define SEQ_printf(m, x...)                    \
- do {                                          \
-       if (m)                                  \
-               seq_printf(m, x);               \
-       else                                    \
-               printk(x);                      \
- } while (0)
+__printf(2, 3)
+static void SEQ_printf(struct seq_file *m, const char *fmt, ...)
+{
+       va_list args;
+
+       va_start(args, fmt);
+
+       if (m)
+               seq_vprintf(m, fmt, args);
+       else
+               vprintk(fmt, args);
+
+       va_end(args);
+}
 
 static void print_name_offset(struct seq_file *m, void *sym)
 {
@@ -120,10 +125,10 @@ static void
 print_base(struct seq_file *m, struct hrtimer_clock_base *base, u64 now)
 {
        SEQ_printf(m, "  .base:       %pK\n", base);
-       SEQ_printf(m, "  .index:      %d\n",
-                       base->index);
-       SEQ_printf(m, "  .resolution: %Lu nsecs\n",
-                       (unsigned long long)ktime_to_ns(base->resolution));
+       SEQ_printf(m, "  .index:      %d\n", base->index);
+
+       SEQ_printf(m, "  .resolution: %u nsecs\n", (unsigned) hrtimer_resolution);
+
        SEQ_printf(m,   "  .get_time:   ");
        print_name_offset(m, base->get_time);
        SEQ_printf(m,   "\n");
@@ -158,7 +163,7 @@ static void print_cpu(struct seq_file *m, int cpu, u64 now)
        P(nr_events);
        P(nr_retries);
        P(nr_hangs);
-       P_ns(max_hang_time);
+       P(max_hang_time);
 #endif
 #undef P
 #undef P_ns
@@ -184,7 +189,7 @@ static void print_cpu(struct seq_file *m, int cpu, u64 now)
                P_ns(idle_sleeptime);
                P_ns(iowait_sleeptime);
                P(last_jiffies);
-               P(next_jiffies);
+               P(next_timer);
                P_ns(idle_expires);
                SEQ_printf(m, "jiffies: %Lu\n",
                           (unsigned long long)jiffies);
@@ -251,6 +256,12 @@ print_tickdevice(struct seq_file *m, struct tick_device *td, int cpu)
                        SEQ_printf(m, "\n");
                }
 
+               if (dev->set_state_oneshot_stopped) {
+                       SEQ_printf(m, " oneshot stopped: ");
+                       print_name_offset(m, dev->set_state_oneshot_stopped);
+                       SEQ_printf(m, "\n");
+               }
+
                if (dev->tick_resume) {
                        SEQ_printf(m, " resume:   ");
                        print_name_offset(m, dev->tick_resume);
@@ -269,11 +280,11 @@ static void timer_list_show_tickdevices_header(struct seq_file *m)
 {
 #ifdef CONFIG_GENERIC_CLOCKEVENTS_BROADCAST
        print_tickdevice(m, tick_get_broadcast_device(), -1);
-       SEQ_printf(m, "tick_broadcast_mask: %08lx\n",
-                  cpumask_bits(tick_get_broadcast_mask())[0]);
+       SEQ_printf(m, "tick_broadcast_mask: %*pb\n",
+                  cpumask_pr_args(tick_get_broadcast_mask()));
 #ifdef CONFIG_TICK_ONESHOT
-       SEQ_printf(m, "tick_broadcast_oneshot_mask: %08lx\n",
-                  cpumask_bits(tick_get_broadcast_oneshot_mask())[0]);
+       SEQ_printf(m, "tick_broadcast_oneshot_mask: %*pb\n",
+                  cpumask_pr_args(tick_get_broadcast_oneshot_mask()));
 #endif
        SEQ_printf(m, "\n");
 #endif
@@ -282,7 +293,7 @@ static void timer_list_show_tickdevices_header(struct seq_file *m)
 
 static inline void timer_list_header(struct seq_file *m, u64 now)
 {
-       SEQ_printf(m, "Timer List Version: v0.7\n");
+       SEQ_printf(m, "Timer List Version: v0.8\n");
        SEQ_printf(m, "HRTIMER_MAX_CLOCK_BASES: %d\n", HRTIMER_MAX_CLOCK_BASES);
        SEQ_printf(m, "now at %Ld nsecs\n", (unsigned long long)now);
        SEQ_printf(m, "\n");
index 1fb08f21302ece707ac7ea3b5210863cae8b19c2..1adecb4b87c8492e558b845914bd5c9a004fe9c0 100644 (file)
@@ -68,7 +68,7 @@ struct entry {
         * Number of timeout events:
         */
        unsigned long           count;
-       unsigned int            timer_flag;
+       u32                     flags;
 
        /*
         * We save the command-line string to preserve
@@ -227,13 +227,13 @@ static struct entry *tstat_lookup(struct entry *entry, char *comm)
  * @startf:    pointer to the function which did the timer setup
  * @timerf:    pointer to the timer callback function of the timer
  * @comm:      name of the process which set up the timer
+ * @tflags:    The flags field of the timer
  *
  * When the timer is already registered, then the event counter is
  * incremented. Otherwise the timer is registered in a free slot.
  */
 void timer_stats_update_stats(void *timer, pid_t pid, void *startf,
-                             void *timerf, char *comm,
-                             unsigned int timer_flag)
+                             void *timerf, char *comm, u32 tflags)
 {
        /*
         * It doesn't matter which lock we take:
@@ -251,7 +251,7 @@ void timer_stats_update_stats(void *timer, pid_t pid, void *startf,
        input.start_func = startf;
        input.expire_func = timerf;
        input.pid = pid;
-       input.timer_flag = timer_flag;
+       input.flags = tflags;
 
        raw_spin_lock_irqsave(lock, flags);
        if (!timer_stats_active)
@@ -306,7 +306,7 @@ static int tstats_show(struct seq_file *m, void *v)
 
        for (i = 0; i < nr_entries; i++) {
                entry = entries + i;
-               if (entry->timer_flag & TIMER_STATS_FLAG_DEFERRABLE) {
+               if (entry->flags & TIMER_DEFERRABLE) {
                        seq_printf(m, "%4luD, %5d %-16s ",
                                entry->count, entry->pid, entry->comm);
                } else {
index dd70993c266c38785510ab09f0315d1f1775d05b..3e4840633d3ee7bd926f1fe67f8b0a4b324514da 100644 (file)
@@ -409,7 +409,7 @@ static void (*torture_shutdown_hook)(void);
  */
 void torture_shutdown_absorb(const char *title)
 {
-       while (ACCESS_ONCE(fullstop) == FULLSTOP_SHUTDOWN) {
+       while (READ_ONCE(fullstop) == FULLSTOP_SHUTDOWN) {
                pr_notice("torture thread %s parking due to system shutdown\n",
                          title);
                schedule_timeout_uninterruptible(MAX_SCHEDULE_TIMEOUT);
@@ -480,9 +480,9 @@ static int torture_shutdown_notify(struct notifier_block *unused1,
                                   unsigned long unused2, void *unused3)
 {
        mutex_lock(&fullstop_mutex);
-       if (ACCESS_ONCE(fullstop) == FULLSTOP_DONTSTOP) {
+       if (READ_ONCE(fullstop) == FULLSTOP_DONTSTOP) {
                VERBOSE_TOROUT_STRING("Unscheduled system shutdown detected");
-               ACCESS_ONCE(fullstop) = FULLSTOP_SHUTDOWN;
+               WRITE_ONCE(fullstop, FULLSTOP_SHUTDOWN);
        } else {
                pr_warn("Concurrent rmmod and shutdown illegal!\n");
        }
@@ -523,13 +523,13 @@ static int stutter;
  */
 void stutter_wait(const char *title)
 {
-       while (ACCESS_ONCE(stutter_pause_test) ||
-              (torture_runnable && !ACCESS_ONCE(*torture_runnable))) {
+       while (READ_ONCE(stutter_pause_test) ||
+              (torture_runnable && !READ_ONCE(*torture_runnable))) {
                if (stutter_pause_test)
-                       if (ACCESS_ONCE(stutter_pause_test) == 1)
+                       if (READ_ONCE(stutter_pause_test) == 1)
                                schedule_timeout_interruptible(1);
                        else
-                               while (ACCESS_ONCE(stutter_pause_test))
+                               while (READ_ONCE(stutter_pause_test))
                                        cond_resched();
                else
                        schedule_timeout_interruptible(round_jiffies_relative(HZ));
@@ -549,14 +549,14 @@ static int torture_stutter(void *arg)
                if (!torture_must_stop()) {
                        if (stutter > 1) {
                                schedule_timeout_interruptible(stutter - 1);
-                               ACCESS_ONCE(stutter_pause_test) = 2;
+                               WRITE_ONCE(stutter_pause_test, 2);
                        }
                        schedule_timeout_interruptible(1);
-                       ACCESS_ONCE(stutter_pause_test) = 1;
+                       WRITE_ONCE(stutter_pause_test, 1);
                }
                if (!torture_must_stop())
                        schedule_timeout_interruptible(stutter);
-               ACCESS_ONCE(stutter_pause_test) = 0;
+               WRITE_ONCE(stutter_pause_test, 0);
                torture_shutdown_absorb("torture_stutter");
        } while (!torture_must_stop());
        torture_kthread_stopping("torture_stutter");
@@ -642,13 +642,13 @@ EXPORT_SYMBOL_GPL(torture_init_end);
 bool torture_cleanup_begin(void)
 {
        mutex_lock(&fullstop_mutex);
-       if (ACCESS_ONCE(fullstop) == FULLSTOP_SHUTDOWN) {
+       if (READ_ONCE(fullstop) == FULLSTOP_SHUTDOWN) {
                pr_warn("Concurrent rmmod and shutdown illegal!\n");
                mutex_unlock(&fullstop_mutex);
                schedule_timeout_uninterruptible(10);
                return true;
        }
-       ACCESS_ONCE(fullstop) = FULLSTOP_RMMOD;
+       WRITE_ONCE(fullstop, FULLSTOP_RMMOD);
        mutex_unlock(&fullstop_mutex);
        torture_shutdown_cleanup();
        torture_shuffle_cleanup();
@@ -681,7 +681,7 @@ EXPORT_SYMBOL_GPL(torture_must_stop);
  */
 bool torture_must_stop_irq(void)
 {
-       return ACCESS_ONCE(fullstop) != FULLSTOP_DONTSTOP;
+       return READ_ONCE(fullstop) != FULLSTOP_DONTSTOP;
 }
 EXPORT_SYMBOL_GPL(torture_must_stop_irq);
 
index 13d945c0d03f2bda5802971484b21bbe9f65301f..1b28df2d91042de97566454a80dcb36d24674a49 100644 (file)
@@ -450,7 +450,7 @@ static int __init ring_buffer_benchmark_init(void)
 
        if (producer_fifo >= 0) {
                struct sched_param param = {
-                       .sched_priority = consumer_fifo
+                       .sched_priority = producer_fifo
                };
                sched_setscheduler(producer, SCHED_FIFO, &param);
        } else
index ced69da0ff55ba08a7358cae7ceaae31546f9332..7f2e97ce71a7d12a9b2ed5e703969e635f320a57 100644 (file)
@@ -1369,19 +1369,26 @@ static int check_preds(struct filter_parse_state *ps)
 {
        int n_normal_preds = 0, n_logical_preds = 0;
        struct postfix_elt *elt;
+       int cnt = 0;
 
        list_for_each_entry(elt, &ps->postfix, list) {
-               if (elt->op == OP_NONE)
+               if (elt->op == OP_NONE) {
+                       cnt++;
                        continue;
+               }
 
                if (elt->op == OP_AND || elt->op == OP_OR) {
                        n_logical_preds++;
+                       cnt--;
                        continue;
                }
+               if (elt->op != OP_NOT)
+                       cnt--;
                n_normal_preds++;
+               WARN_ON_ONCE(cnt < 0);
        }
 
-       if (!n_normal_preds || n_logical_preds >= n_normal_preds) {
+       if (cnt != 1 || !n_normal_preds || n_logical_preds >= n_normal_preds) {
                parse_error(ps, FILT_ERR_INVALID_FILTER, 0);
                return -EINVAL;
        }
diff --git a/lib/842/842.h b/lib/842/842.h
new file mode 100644 (file)
index 0000000..7c20003
--- /dev/null
@@ -0,0 +1,127 @@
+
+#ifndef __842_H__
+#define __842_H__
+
+/* The 842 compressed format is made up of multiple blocks, each of
+ * which have the format:
+ *
+ * <template>[arg1][arg2][arg3][arg4]
+ *
+ * where there are between 0 and 4 template args, depending on the specific
+ * template operation.  For normal operations, each arg is either a specific
+ * number of data bytes to add to the output buffer, or an index pointing
+ * to a previously-written number of data bytes to copy to the output buffer.
+ *
+ * The template code is a 5-bit value.  This code indicates what to do with
+ * the following data.  Template codes from 0 to 0x19 should use the template
+ * table, the static "decomp_ops" table used in decompress.  For each template
+ * (table row), there are between 1 and 4 actions; each action corresponds to
+ * an arg following the template code bits.  Each action is either a "data"
+ * type action, or a "index" type action, and each action results in 2, 4, or 8
+ * bytes being written to the output buffer.  Each template (i.e. all actions
+ * in the table row) will add up to 8 bytes being written to the output buffer.
+ * Any row with less than 4 actions is padded with noop actions, indicated by
+ * N0 (for which there is no corresponding arg in the compressed data buffer).
+ *
+ * "Data" actions, indicated in the table by D2, D4, and D8, mean that the
+ * corresponding arg is 2, 4, or 8 bytes, respectively, in the compressed data
+ * buffer should be copied directly to the output buffer.
+ *
+ * "Index" actions, indicated in the table by I2, I4, and I8, mean the
+ * corresponding arg is an index parameter that points to, respectively, a 2,
+ * 4, or 8 byte value already in the output buffer, that should be copied to
+ * the end of the output buffer.  Essentially, the index points to a position
+ * in a ring buffer that contains the last N bytes of output buffer data.
+ * The number of bits for each index's arg are: 8 bits for I2, 9 bits for I4,
+ * and 8 bits for I8.  Since each index points to a 2, 4, or 8 byte section,
+ * this means that I2 can reference 512 bytes ((2^8 bits = 256) * 2 bytes), I4
+ * can reference 2048 bytes ((2^9 = 512) * 4 bytes), and I8 can reference 2048
+ * bytes ((2^8 = 256) * 8 bytes).  Think of it as a kind-of ring buffer for
+ * each of I2, I4, and I8 that are updated for each byte written to the output
+ * buffer.  In this implementation, the output buffer is directly used for each
+ * index; there is no additional memory required.  Note that the index is into
+ * a ring buffer, not a sliding window; for example, if there have been 260
+ * bytes written to the output buffer, an I2 index of 0 would index to byte 256
+ * in the output buffer, while an I2 index of 16 would index to byte 16 in the
+ * output buffer.
+ *
+ * There are also 3 special template codes; 0x1b for "repeat", 0x1c for
+ * "zeros", and 0x1e for "end".  The "repeat" operation is followed by a 6 bit
+ * arg N indicating how many times to repeat.  The last 8 bytes written to the
+ * output buffer are written again to the output buffer, N + 1 times.  The
+ * "zeros" operation, which has no arg bits, writes 8 zeros to the output
+ * buffer.  The "end" operation, which also has no arg bits, signals the end
+ * of the compressed data.  There may be some number of padding (don't care,
+ * but usually 0) bits after the "end" operation bits, to fill the buffer
+ * length to a specific byte multiple (usually a multiple of 8, 16, or 32
+ * bytes).
+ *
+ * This software implementation also uses one of the undefined template values,
+ * 0x1d as a special "short data" template code, to represent less than 8 bytes
+ * of uncompressed data.  It is followed by a 3 bit arg N indicating how many
+ * data bytes will follow, and then N bytes of data, which should be copied to
+ * the output buffer.  This allows the software 842 compressor to accept input
+ * buffers that are not an exact multiple of 8 bytes long.  However, those
+ * compressed buffers containing this sw-only template will be rejected by
+ * the 842 hardware decompressor, and must be decompressed with this software
+ * library.  The 842 software compression module includes a parameter to
+ * disable using this sw-only "short data" template, and instead simply
+ * reject any input buffer that is not a multiple of 8 bytes long.
+ *
+ * After all actions for each operation code are processed, another template
+ * code is in the next 5 bits.  The decompression ends once the "end" template
+ * code is detected.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/bitops.h>
+#include <asm/unaligned.h>
+
+#include <linux/sw842.h>
+
+/* special templates */
+#define OP_REPEAT      (0x1B)
+#define OP_ZEROS       (0x1C)
+#define OP_END         (0x1E)
+
+/* sw only template - this is not in the hw design; it's used only by this
+ * software compressor and decompressor, to allow input buffers that aren't
+ * a multiple of 8.
+ */
+#define OP_SHORT_DATA  (0x1D)
+
+/* additional bits of each op param */
+#define OP_BITS                (5)
+#define REPEAT_BITS    (6)
+#define SHORT_DATA_BITS        (3)
+#define I2_BITS                (8)
+#define I4_BITS                (9)
+#define I8_BITS                (8)
+
+#define REPEAT_BITS_MAX                (0x3f)
+#define SHORT_DATA_BITS_MAX    (0x7)
+
+/* Arbitrary values used to indicate action */
+#define OP_ACTION      (0x70)
+#define OP_ACTION_INDEX        (0x10)
+#define OP_ACTION_DATA (0x20)
+#define OP_ACTION_NOOP (0x40)
+#define OP_AMOUNT      (0x0f)
+#define OP_AMOUNT_0    (0x00)
+#define OP_AMOUNT_2    (0x02)
+#define OP_AMOUNT_4    (0x04)
+#define OP_AMOUNT_8    (0x08)
+
+#define D2             (OP_ACTION_DATA  | OP_AMOUNT_2)
+#define D4             (OP_ACTION_DATA  | OP_AMOUNT_4)
+#define D8             (OP_ACTION_DATA  | OP_AMOUNT_8)
+#define I2             (OP_ACTION_INDEX | OP_AMOUNT_2)
+#define I4             (OP_ACTION_INDEX | OP_AMOUNT_4)
+#define I8             (OP_ACTION_INDEX | OP_AMOUNT_8)
+#define N0             (OP_ACTION_NOOP  | OP_AMOUNT_0)
+
+/* the max of the regular templates - not including the special templates */
+#define OPS_MAX                (0x1a)
+
+#endif
diff --git a/lib/842/842_compress.c b/lib/842/842_compress.c
new file mode 100644 (file)
index 0000000..7ce6894
--- /dev/null
@@ -0,0 +1,626 @@
+/*
+ * 842 Software Compression
+ *
+ * Copyright (C) 2015 Dan Streetman, IBM Corp
+ *
+ * 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.
+ *
+ * See 842.h for details of the 842 compressed format.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#define MODULE_NAME "842_compress"
+
+#include <linux/hashtable.h>
+
+#include "842.h"
+#include "842_debugfs.h"
+
+#define SW842_HASHTABLE8_BITS  (10)
+#define SW842_HASHTABLE4_BITS  (11)
+#define SW842_HASHTABLE2_BITS  (10)
+
+/* By default, we allow compressing input buffers of any length, but we must
+ * use the non-standard "short data" template so the decompressor can correctly
+ * reproduce the uncompressed data buffer at the right length.  However the
+ * hardware 842 compressor will not recognize the "short data" template, and
+ * will fail to decompress any compressed buffer containing it (I have no idea
+ * why anyone would want to use software to compress and hardware to decompress
+ * but that's beside the point).  This parameter forces the compression
+ * function to simply reject any input buffer that isn't a multiple of 8 bytes
+ * long, instead of using the "short data" template, so that all compressed
+ * buffers produced by this function will be decompressable by the 842 hardware
+ * decompressor.  Unless you have a specific need for that, leave this disabled
+ * so that any length buffer can be compressed.
+ */
+static bool sw842_strict;
+module_param_named(strict, sw842_strict, bool, 0644);
+
+static u8 comp_ops[OPS_MAX][5] = { /* params size in bits */
+       { I8, N0, N0, N0, 0x19 }, /* 8 */
+       { I4, I4, N0, N0, 0x18 }, /* 18 */
+       { I4, I2, I2, N0, 0x17 }, /* 25 */
+       { I2, I2, I4, N0, 0x13 }, /* 25 */
+       { I2, I2, I2, I2, 0x12 }, /* 32 */
+       { I4, I2, D2, N0, 0x16 }, /* 33 */
+       { I4, D2, I2, N0, 0x15 }, /* 33 */
+       { I2, D2, I4, N0, 0x0e }, /* 33 */
+       { D2, I2, I4, N0, 0x09 }, /* 33 */
+       { I2, I2, I2, D2, 0x11 }, /* 40 */
+       { I2, I2, D2, I2, 0x10 }, /* 40 */
+       { I2, D2, I2, I2, 0x0d }, /* 40 */
+       { D2, I2, I2, I2, 0x08 }, /* 40 */
+       { I4, D4, N0, N0, 0x14 }, /* 41 */
+       { D4, I4, N0, N0, 0x04 }, /* 41 */
+       { I2, I2, D4, N0, 0x0f }, /* 48 */
+       { I2, D2, I2, D2, 0x0c }, /* 48 */
+       { I2, D4, I2, N0, 0x0b }, /* 48 */
+       { D2, I2, I2, D2, 0x07 }, /* 48 */
+       { D2, I2, D2, I2, 0x06 }, /* 48 */
+       { D4, I2, I2, N0, 0x03 }, /* 48 */
+       { I2, D2, D4, N0, 0x0a }, /* 56 */
+       { D2, I2, D4, N0, 0x05 }, /* 56 */
+       { D4, I2, D2, N0, 0x02 }, /* 56 */
+       { D4, D2, I2, N0, 0x01 }, /* 56 */
+       { D8, N0, N0, N0, 0x00 }, /* 64 */
+};
+
+struct sw842_hlist_node8 {
+       struct hlist_node node;
+       u64 data;
+       u8 index;
+};
+
+struct sw842_hlist_node4 {
+       struct hlist_node node;
+       u32 data;
+       u16 index;
+};
+
+struct sw842_hlist_node2 {
+       struct hlist_node node;
+       u16 data;
+       u8 index;
+};
+
+#define INDEX_NOT_FOUND                (-1)
+#define INDEX_NOT_CHECKED      (-2)
+
+struct sw842_param {
+       u8 *in;
+       u8 *instart;
+       u64 ilen;
+       u8 *out;
+       u64 olen;
+       u8 bit;
+       u64 data8[1];
+       u32 data4[2];
+       u16 data2[4];
+       int index8[1];
+       int index4[2];
+       int index2[4];
+       DECLARE_HASHTABLE(htable8, SW842_HASHTABLE8_BITS);
+       DECLARE_HASHTABLE(htable4, SW842_HASHTABLE4_BITS);
+       DECLARE_HASHTABLE(htable2, SW842_HASHTABLE2_BITS);
+       struct sw842_hlist_node8 node8[1 << I8_BITS];
+       struct sw842_hlist_node4 node4[1 << I4_BITS];
+       struct sw842_hlist_node2 node2[1 << I2_BITS];
+};
+
+#define get_input_data(p, o, b)                                                \
+       be##b##_to_cpu(get_unaligned((__be##b *)((p)->in + (o))))
+
+#define init_hashtable_nodes(p, b)     do {                    \
+       int _i;                                                 \
+       hash_init((p)->htable##b);                              \
+       for (_i = 0; _i < ARRAY_SIZE((p)->node##b); _i++) {     \
+               (p)->node##b[_i].index = _i;                    \
+               (p)->node##b[_i].data = 0;                      \
+               INIT_HLIST_NODE(&(p)->node##b[_i].node);        \
+       }                                                       \
+} while (0)
+
+#define find_index(p, b, n)    ({                                      \
+       struct sw842_hlist_node##b *_n;                                 \
+       p->index##b[n] = INDEX_NOT_FOUND;                               \
+       hash_for_each_possible(p->htable##b, _n, node, p->data##b[n]) { \
+               if (p->data##b[n] == _n->data) {                        \
+                       p->index##b[n] = _n->index;                     \
+                       break;                                          \
+               }                                                       \
+       }                                                               \
+       p->index##b[n] >= 0;                                            \
+})
+
+#define check_index(p, b, n)                   \
+       ((p)->index##b[n] == INDEX_NOT_CHECKED  \
+        ? find_index(p, b, n)                  \
+        : (p)->index##b[n] >= 0)
+
+#define replace_hash(p, b, i, d)       do {                            \
+       struct sw842_hlist_node##b *_n = &(p)->node##b[(i)+(d)];        \
+       hash_del(&_n->node);                                            \
+       _n->data = (p)->data##b[d];                                     \
+       pr_debug("add hash index%x %x pos %x data %lx\n", b,            \
+                (unsigned int)_n->index,                               \
+                (unsigned int)((p)->in - (p)->instart),                \
+                (unsigned long)_n->data);                              \
+       hash_add((p)->htable##b, &_n->node, _n->data);                  \
+} while (0)
+
+static u8 bmask[8] = { 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe };
+
+static int add_bits(struct sw842_param *p, u64 d, u8 n);
+
+static int __split_add_bits(struct sw842_param *p, u64 d, u8 n, u8 s)
+{
+       int ret;
+
+       if (n <= s)
+               return -EINVAL;
+
+       ret = add_bits(p, d >> s, n - s);
+       if (ret)
+               return ret;
+       return add_bits(p, d & GENMASK_ULL(s - 1, 0), s);
+}
+
+static int add_bits(struct sw842_param *p, u64 d, u8 n)
+{
+       int b = p->bit, bits = b + n, s = round_up(bits, 8) - bits;
+       u64 o;
+       u8 *out = p->out;
+
+       pr_debug("add %u bits %lx\n", (unsigned char)n, (unsigned long)d);
+
+       if (n > 64)
+               return -EINVAL;
+
+       /* split this up if writing to > 8 bytes (i.e. n == 64 && p->bit > 0),
+        * or if we're at the end of the output buffer and would write past end
+        */
+       if (bits > 64)
+               return __split_add_bits(p, d, n, 32);
+       else if (p->olen < 8 && bits > 32 && bits <= 56)
+               return __split_add_bits(p, d, n, 16);
+       else if (p->olen < 4 && bits > 16 && bits <= 24)
+               return __split_add_bits(p, d, n, 8);
+
+       if (DIV_ROUND_UP(bits, 8) > p->olen)
+               return -ENOSPC;
+
+       o = *out & bmask[b];
+       d <<= s;
+
+       if (bits <= 8)
+               *out = o | d;
+       else if (bits <= 16)
+               put_unaligned(cpu_to_be16(o << 8 | d), (__be16 *)out);
+       else if (bits <= 24)
+               put_unaligned(cpu_to_be32(o << 24 | d << 8), (__be32 *)out);
+       else if (bits <= 32)
+               put_unaligned(cpu_to_be32(o << 24 | d), (__be32 *)out);
+       else if (bits <= 40)
+               put_unaligned(cpu_to_be64(o << 56 | d << 24), (__be64 *)out);
+       else if (bits <= 48)
+               put_unaligned(cpu_to_be64(o << 56 | d << 16), (__be64 *)out);
+       else if (bits <= 56)
+               put_unaligned(cpu_to_be64(o << 56 | d << 8), (__be64 *)out);
+       else
+               put_unaligned(cpu_to_be64(o << 56 | d), (__be64 *)out);
+
+       p->bit += n;
+
+       if (p->bit > 7) {
+               p->out += p->bit / 8;
+               p->olen -= p->bit / 8;
+               p->bit %= 8;
+       }
+
+       return 0;
+}
+
+static int add_template(struct sw842_param *p, u8 c)
+{
+       int ret, i, b = 0;
+       u8 *t = comp_ops[c];
+       bool inv = false;
+
+       if (c >= OPS_MAX)
+               return -EINVAL;
+
+       pr_debug("template %x\n", t[4]);
+
+       ret = add_bits(p, t[4], OP_BITS);
+       if (ret)
+               return ret;
+
+       for (i = 0; i < 4; i++) {
+               pr_debug("op %x\n", t[i]);
+
+               switch (t[i] & OP_AMOUNT) {
+               case OP_AMOUNT_8:
+                       if (b)
+                               inv = true;
+                       else if (t[i] & OP_ACTION_INDEX)
+                               ret = add_bits(p, p->index8[0], I8_BITS);
+                       else if (t[i] & OP_ACTION_DATA)
+                               ret = add_bits(p, p->data8[0], 64);
+                       else
+                               inv = true;
+                       break;
+               case OP_AMOUNT_4:
+                       if (b == 2 && t[i] & OP_ACTION_DATA)
+                               ret = add_bits(p, get_input_data(p, 2, 32), 32);
+                       else if (b != 0 && b != 4)
+                               inv = true;
+                       else if (t[i] & OP_ACTION_INDEX)
+                               ret = add_bits(p, p->index4[b >> 2], I4_BITS);
+                       else if (t[i] & OP_ACTION_DATA)
+                               ret = add_bits(p, p->data4[b >> 2], 32);
+                       else
+                               inv = true;
+                       break;
+               case OP_AMOUNT_2:
+                       if (b != 0 && b != 2 && b != 4 && b != 6)
+                               inv = true;
+                       if (t[i] & OP_ACTION_INDEX)
+                               ret = add_bits(p, p->index2[b >> 1], I2_BITS);
+                       else if (t[i] & OP_ACTION_DATA)
+                               ret = add_bits(p, p->data2[b >> 1], 16);
+                       else
+                               inv = true;
+                       break;
+               case OP_AMOUNT_0:
+                       inv = (b != 8) || !(t[i] & OP_ACTION_NOOP);
+                       break;
+               default:
+                       inv = true;
+                       break;
+               }
+
+               if (ret)
+                       return ret;
+
+               if (inv) {
+                       pr_err("Invalid templ %x op %d : %x %x %x %x\n",
+                              c, i, t[0], t[1], t[2], t[3]);
+                       return -EINVAL;
+               }
+
+               b += t[i] & OP_AMOUNT;
+       }
+
+       if (b != 8) {
+               pr_err("Invalid template %x len %x : %x %x %x %x\n",
+                      c, b, t[0], t[1], t[2], t[3]);
+               return -EINVAL;
+       }
+
+       if (sw842_template_counts)
+               atomic_inc(&template_count[t[4]]);
+
+       return 0;
+}
+
+static int add_repeat_template(struct sw842_param *p, u8 r)
+{
+       int ret;
+
+       /* repeat param is 0-based */
+       if (!r || --r > REPEAT_BITS_MAX)
+               return -EINVAL;
+
+       ret = add_bits(p, OP_REPEAT, OP_BITS);
+       if (ret)
+               return ret;
+
+       ret = add_bits(p, r, REPEAT_BITS);
+       if (ret)
+               return ret;
+
+       if (sw842_template_counts)
+               atomic_inc(&template_repeat_count);
+
+       return 0;
+}
+
+static int add_short_data_template(struct sw842_param *p, u8 b)
+{
+       int ret, i;
+
+       if (!b || b > SHORT_DATA_BITS_MAX)
+               return -EINVAL;
+
+       ret = add_bits(p, OP_SHORT_DATA, OP_BITS);
+       if (ret)
+               return ret;
+
+       ret = add_bits(p, b, SHORT_DATA_BITS);
+       if (ret)
+               return ret;
+
+       for (i = 0; i < b; i++) {
+               ret = add_bits(p, p->in[i], 8);
+               if (ret)
+                       return ret;
+       }
+
+       if (sw842_template_counts)
+               atomic_inc(&template_short_data_count);
+
+       return 0;
+}
+
+static int add_zeros_template(struct sw842_param *p)
+{
+       int ret = add_bits(p, OP_ZEROS, OP_BITS);
+
+       if (ret)
+               return ret;
+
+       if (sw842_template_counts)
+               atomic_inc(&template_zeros_count);
+
+       return 0;
+}
+
+static int add_end_template(struct sw842_param *p)
+{
+       int ret = add_bits(p, OP_END, OP_BITS);
+
+       if (ret)
+               return ret;
+
+       if (sw842_template_counts)
+               atomic_inc(&template_end_count);
+
+       return 0;
+}
+
+static bool check_template(struct sw842_param *p, u8 c)
+{
+       u8 *t = comp_ops[c];
+       int i, match, b = 0;
+
+       if (c >= OPS_MAX)
+               return false;
+
+       for (i = 0; i < 4; i++) {
+               if (t[i] & OP_ACTION_INDEX) {
+                       if (t[i] & OP_AMOUNT_2)
+                               match = check_index(p, 2, b >> 1);
+                       else if (t[i] & OP_AMOUNT_4)
+                               match = check_index(p, 4, b >> 2);
+                       else if (t[i] & OP_AMOUNT_8)
+                               match = check_index(p, 8, 0);
+                       else
+                               return false;
+                       if (!match)
+                               return false;
+               }
+
+               b += t[i] & OP_AMOUNT;
+       }
+
+       return true;
+}
+
+static void get_next_data(struct sw842_param *p)
+{
+       p->data8[0] = get_input_data(p, 0, 64);
+       p->data4[0] = get_input_data(p, 0, 32);
+       p->data4[1] = get_input_data(p, 4, 32);
+       p->data2[0] = get_input_data(p, 0, 16);
+       p->data2[1] = get_input_data(p, 2, 16);
+       p->data2[2] = get_input_data(p, 4, 16);
+       p->data2[3] = get_input_data(p, 6, 16);
+}
+
+/* update the hashtable entries.
+ * only call this after finding/adding the current template
+ * the dataN fields for the current 8 byte block must be already updated
+ */
+static void update_hashtables(struct sw842_param *p)
+{
+       u64 pos = p->in - p->instart;
+       u64 n8 = (pos >> 3) % (1 << I8_BITS);
+       u64 n4 = (pos >> 2) % (1 << I4_BITS);
+       u64 n2 = (pos >> 1) % (1 << I2_BITS);
+
+       replace_hash(p, 8, n8, 0);
+       replace_hash(p, 4, n4, 0);
+       replace_hash(p, 4, n4, 1);
+       replace_hash(p, 2, n2, 0);
+       replace_hash(p, 2, n2, 1);
+       replace_hash(p, 2, n2, 2);
+       replace_hash(p, 2, n2, 3);
+}
+
+/* find the next template to use, and add it
+ * the p->dataN fields must already be set for the current 8 byte block
+ */
+static int process_next(struct sw842_param *p)
+{
+       int ret, i;
+
+       p->index8[0] = INDEX_NOT_CHECKED;
+       p->index4[0] = INDEX_NOT_CHECKED;
+       p->index4[1] = INDEX_NOT_CHECKED;
+       p->index2[0] = INDEX_NOT_CHECKED;
+       p->index2[1] = INDEX_NOT_CHECKED;
+       p->index2[2] = INDEX_NOT_CHECKED;
+       p->index2[3] = INDEX_NOT_CHECKED;
+
+       /* check up to OPS_MAX - 1; last op is our fallback */
+       for (i = 0; i < OPS_MAX - 1; i++) {
+               if (check_template(p, i))
+                       break;
+       }
+
+       ret = add_template(p, i);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+/**
+ * sw842_compress
+ *
+ * Compress the uncompressed buffer of length @ilen at @in to the output buffer
+ * @out, using no more than @olen bytes, using the 842 compression format.
+ *
+ * Returns: 0 on success, error on failure.  The @olen parameter
+ * will contain the number of output bytes written on success, or
+ * 0 on error.
+ */
+int sw842_compress(const u8 *in, unsigned int ilen,
+                  u8 *out, unsigned int *olen, void *wmem)
+{
+       struct sw842_param *p = (struct sw842_param *)wmem;
+       int ret;
+       u64 last, next, pad, total;
+       u8 repeat_count = 0;
+
+       BUILD_BUG_ON(sizeof(*p) > SW842_MEM_COMPRESS);
+
+       init_hashtable_nodes(p, 8);
+       init_hashtable_nodes(p, 4);
+       init_hashtable_nodes(p, 2);
+
+       p->in = (u8 *)in;
+       p->instart = p->in;
+       p->ilen = ilen;
+       p->out = out;
+       p->olen = *olen;
+       p->bit = 0;
+
+       total = p->olen;
+
+       *olen = 0;
+
+       /* if using strict mode, we can only compress a multiple of 8 */
+       if (sw842_strict && (ilen % 8)) {
+               pr_err("Using strict mode, can't compress len %d\n", ilen);
+               return -EINVAL;
+       }
+
+       /* let's compress at least 8 bytes, mkay? */
+       if (unlikely(ilen < 8))
+               goto skip_comp;
+
+       /* make initial 'last' different so we don't match the first time */
+       last = ~get_unaligned((u64 *)p->in);
+
+       while (p->ilen > 7) {
+               next = get_unaligned((u64 *)p->in);
+
+               /* must get the next data, as we need to update the hashtable
+                * entries with the new data every time
+                */
+               get_next_data(p);
+
+               /* we don't care about endianness in last or next;
+                * we're just comparing 8 bytes to another 8 bytes,
+                * they're both the same endianness
+                */
+               if (next == last) {
+                       /* repeat count bits are 0-based, so we stop at +1 */
+                       if (++repeat_count <= REPEAT_BITS_MAX)
+                               goto repeat;
+               }
+               if (repeat_count) {
+                       ret = add_repeat_template(p, repeat_count);
+                       repeat_count = 0;
+                       if (next == last) /* reached max repeat bits */
+                               goto repeat;
+               }
+
+               if (next == 0)
+                       ret = add_zeros_template(p);
+               else
+                       ret = process_next(p);
+
+               if (ret)
+                       return ret;
+
+repeat:
+               last = next;
+               update_hashtables(p);
+               p->in += 8;
+               p->ilen -= 8;
+       }
+
+       if (repeat_count) {
+               ret = add_repeat_template(p, repeat_count);
+               if (ret)
+                       return ret;
+       }
+
+skip_comp:
+       if (p->ilen > 0) {
+               ret = add_short_data_template(p, p->ilen);
+               if (ret)
+                       return ret;
+
+               p->in += p->ilen;
+               p->ilen = 0;
+       }
+
+       ret = add_end_template(p);
+       if (ret)
+               return ret;
+
+       if (p->bit) {
+               p->out++;
+               p->olen--;
+               p->bit = 0;
+       }
+
+       /* pad compressed length to multiple of 8 */
+       pad = (8 - ((total - p->olen) % 8)) % 8;
+       if (pad) {
+               if (pad > p->olen) /* we were so close! */
+                       return -ENOSPC;
+               memset(p->out, 0, pad);
+               p->out += pad;
+               p->olen -= pad;
+       }
+
+       if (unlikely((total - p->olen) > UINT_MAX))
+               return -ENOSPC;
+
+       *olen = total - p->olen;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(sw842_compress);
+
+static int __init sw842_init(void)
+{
+       if (sw842_template_counts)
+               sw842_debugfs_create();
+
+       return 0;
+}
+module_init(sw842_init);
+
+static void __exit sw842_exit(void)
+{
+       if (sw842_template_counts)
+               sw842_debugfs_remove();
+}
+module_exit(sw842_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Software 842 Compressor");
+MODULE_AUTHOR("Dan Streetman <ddstreet@ieee.org>");
diff --git a/lib/842/842_debugfs.h b/lib/842/842_debugfs.h
new file mode 100644 (file)
index 0000000..e7f3bff
--- /dev/null
@@ -0,0 +1,52 @@
+
+#ifndef __842_DEBUGFS_H__
+#define __842_DEBUGFS_H__
+
+#include <linux/debugfs.h>
+
+static bool sw842_template_counts;
+module_param_named(template_counts, sw842_template_counts, bool, 0444);
+
+static atomic_t template_count[OPS_MAX], template_repeat_count,
+       template_zeros_count, template_short_data_count, template_end_count;
+
+static struct dentry *sw842_debugfs_root;
+
+static int __init sw842_debugfs_create(void)
+{
+       umode_t m = S_IRUGO | S_IWUSR;
+       int i;
+
+       if (!debugfs_initialized())
+               return -ENODEV;
+
+       sw842_debugfs_root = debugfs_create_dir(MODULE_NAME, NULL);
+       if (IS_ERR(sw842_debugfs_root))
+               return PTR_ERR(sw842_debugfs_root);
+
+       for (i = 0; i < ARRAY_SIZE(template_count); i++) {
+               char name[32];
+
+               snprintf(name, 32, "template_%02x", i);
+               debugfs_create_atomic_t(name, m, sw842_debugfs_root,
+                                       &template_count[i]);
+       }
+       debugfs_create_atomic_t("template_repeat", m, sw842_debugfs_root,
+                               &template_repeat_count);
+       debugfs_create_atomic_t("template_zeros", m, sw842_debugfs_root,
+                               &template_zeros_count);
+       debugfs_create_atomic_t("template_short_data", m, sw842_debugfs_root,
+                               &template_short_data_count);
+       debugfs_create_atomic_t("template_end", m, sw842_debugfs_root,
+                               &template_end_count);
+
+       return 0;
+}
+
+static void __exit sw842_debugfs_remove(void)
+{
+       if (sw842_debugfs_root && !IS_ERR(sw842_debugfs_root))
+               debugfs_remove_recursive(sw842_debugfs_root);
+}
+
+#endif
diff --git a/lib/842/842_decompress.c b/lib/842/842_decompress.c
new file mode 100644 (file)
index 0000000..5446ff0
--- /dev/null
@@ -0,0 +1,405 @@
+/*
+ * 842 Software Decompression
+ *
+ * Copyright (C) 2015 Dan Streetman, IBM Corp
+ *
+ * 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.
+ *
+ * See 842.h for details of the 842 compressed format.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#define MODULE_NAME "842_decompress"
+
+#include "842.h"
+#include "842_debugfs.h"
+
+/* rolling fifo sizes */
+#define I2_FIFO_SIZE   (2 * (1 << I2_BITS))
+#define I4_FIFO_SIZE   (4 * (1 << I4_BITS))
+#define I8_FIFO_SIZE   (8 * (1 << I8_BITS))
+
+static u8 decomp_ops[OPS_MAX][4] = {
+       { D8, N0, N0, N0 },
+       { D4, D2, I2, N0 },
+       { D4, I2, D2, N0 },
+       { D4, I2, I2, N0 },
+       { D4, I4, N0, N0 },
+       { D2, I2, D4, N0 },
+       { D2, I2, D2, I2 },
+       { D2, I2, I2, D2 },
+       { D2, I2, I2, I2 },
+       { D2, I2, I4, N0 },
+       { I2, D2, D4, N0 },
+       { I2, D4, I2, N0 },
+       { I2, D2, I2, D2 },
+       { I2, D2, I2, I2 },
+       { I2, D2, I4, N0 },
+       { I2, I2, D4, N0 },
+       { I2, I2, D2, I2 },
+       { I2, I2, I2, D2 },
+       { I2, I2, I2, I2 },
+       { I2, I2, I4, N0 },
+       { I4, D4, N0, N0 },
+       { I4, D2, I2, N0 },
+       { I4, I2, D2, N0 },
+       { I4, I2, I2, N0 },
+       { I4, I4, N0, N0 },
+       { I8, N0, N0, N0 }
+};
+
+struct sw842_param {
+       u8 *in;
+       u8 bit;
+       u64 ilen;
+       u8 *out;
+       u8 *ostart;
+       u64 olen;
+};
+
+#define beN_to_cpu(d, s)                                       \
+       ((s) == 2 ? be16_to_cpu(get_unaligned((__be16 *)d)) :   \
+        (s) == 4 ? be32_to_cpu(get_unaligned((__be32 *)d)) :   \
+        (s) == 8 ? be64_to_cpu(get_unaligned((__be64 *)d)) :   \
+        WARN(1, "pr_debug param err invalid size %x\n", s))
+
+static int next_bits(struct sw842_param *p, u64 *d, u8 n);
+
+static int __split_next_bits(struct sw842_param *p, u64 *d, u8 n, u8 s)
+{
+       u64 tmp = 0;
+       int ret;
+
+       if (n <= s) {
+               pr_debug("split_next_bits invalid n %u s %u\n", n, s);
+               return -EINVAL;
+       }
+
+       ret = next_bits(p, &tmp, n - s);
+       if (ret)
+               return ret;
+       ret = next_bits(p, d, s);
+       if (ret)
+               return ret;
+       *d |= tmp << s;
+       return 0;
+}
+
+static int next_bits(struct sw842_param *p, u64 *d, u8 n)
+{
+       u8 *in = p->in, b = p->bit, bits = b + n;
+
+       if (n > 64) {
+               pr_debug("next_bits invalid n %u\n", n);
+               return -EINVAL;
+       }
+
+       /* split this up if reading > 8 bytes, or if we're at the end of
+        * the input buffer and would read past the end
+        */
+       if (bits > 64)
+               return __split_next_bits(p, d, n, 32);
+       else if (p->ilen < 8 && bits > 32 && bits <= 56)
+               return __split_next_bits(p, d, n, 16);
+       else if (p->ilen < 4 && bits > 16 && bits <= 24)
+               return __split_next_bits(p, d, n, 8);
+
+       if (DIV_ROUND_UP(bits, 8) > p->ilen)
+               return -EOVERFLOW;
+
+       if (bits <= 8)
+               *d = *in >> (8 - bits);
+       else if (bits <= 16)
+               *d = be16_to_cpu(get_unaligned((__be16 *)in)) >> (16 - bits);
+       else if (bits <= 32)
+               *d = be32_to_cpu(get_unaligned((__be32 *)in)) >> (32 - bits);
+       else
+               *d = be64_to_cpu(get_unaligned((__be64 *)in)) >> (64 - bits);
+
+       *d &= GENMASK_ULL(n - 1, 0);
+
+       p->bit += n;
+
+       if (p->bit > 7) {
+               p->in += p->bit / 8;
+               p->ilen -= p->bit / 8;
+               p->bit %= 8;
+       }
+
+       return 0;
+}
+
+static int do_data(struct sw842_param *p, u8 n)
+{
+       u64 v;
+       int ret;
+
+       if (n > p->olen)
+               return -ENOSPC;
+
+       ret = next_bits(p, &v, n * 8);
+       if (ret)
+               return ret;
+
+       switch (n) {
+       case 2:
+               put_unaligned(cpu_to_be16((u16)v), (__be16 *)p->out);
+               break;
+       case 4:
+               put_unaligned(cpu_to_be32((u32)v), (__be32 *)p->out);
+               break;
+       case 8:
+               put_unaligned(cpu_to_be64((u64)v), (__be64 *)p->out);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       p->out += n;
+       p->olen -= n;
+
+       return 0;
+}
+
+static int __do_index(struct sw842_param *p, u8 size, u8 bits, u64 fsize)
+{
+       u64 index, offset, total = round_down(p->out - p->ostart, 8);
+       int ret;
+
+       ret = next_bits(p, &index, bits);
+       if (ret)
+               return ret;
+
+       offset = index * size;
+
+       /* a ring buffer of fsize is used; correct the offset */
+       if (total > fsize) {
+               /* this is where the current fifo is */
+               u64 section = round_down(total, fsize);
+               /* the current pos in the fifo */
+               u64 pos = total - section;
+
+               /* if the offset is past/at the pos, we need to
+                * go back to the last fifo section
+                */
+               if (offset >= pos)
+                       section -= fsize;
+
+               offset += section;
+       }
+
+       if (offset + size > total) {
+               pr_debug("index%x %lx points past end %lx\n", size,
+                        (unsigned long)offset, (unsigned long)total);
+               return -EINVAL;
+       }
+
+       pr_debug("index%x to %lx off %lx adjoff %lx tot %lx data %lx\n",
+                size, (unsigned long)index, (unsigned long)(index * size),
+                (unsigned long)offset, (unsigned long)total,
+                (unsigned long)beN_to_cpu(&p->ostart[offset], size));
+
+       memcpy(p->out, &p->ostart[offset], size);
+       p->out += size;
+       p->olen -= size;
+
+       return 0;
+}
+
+static int do_index(struct sw842_param *p, u8 n)
+{
+       switch (n) {
+       case 2:
+               return __do_index(p, 2, I2_BITS, I2_FIFO_SIZE);
+       case 4:
+               return __do_index(p, 4, I4_BITS, I4_FIFO_SIZE);
+       case 8:
+               return __do_index(p, 8, I8_BITS, I8_FIFO_SIZE);
+       default:
+               return -EINVAL;
+       }
+}
+
+static int do_op(struct sw842_param *p, u8 o)
+{
+       int i, ret = 0;
+
+       if (o >= OPS_MAX)
+               return -EINVAL;
+
+       for (i = 0; i < 4; i++) {
+               u8 op = decomp_ops[o][i];
+
+               pr_debug("op is %x\n", op);
+
+               switch (op & OP_ACTION) {
+               case OP_ACTION_DATA:
+                       ret = do_data(p, op & OP_AMOUNT);
+                       break;
+               case OP_ACTION_INDEX:
+                       ret = do_index(p, op & OP_AMOUNT);
+                       break;
+               case OP_ACTION_NOOP:
+                       break;
+               default:
+                       pr_err("Interal error, invalid op %x\n", op);
+                       return -EINVAL;
+               }
+
+               if (ret)
+                       return ret;
+       }
+
+       if (sw842_template_counts)
+               atomic_inc(&template_count[o]);
+
+       return 0;
+}
+
+/**
+ * sw842_decompress
+ *
+ * Decompress the 842-compressed buffer of length @ilen at @in
+ * to the output buffer @out, using no more than @olen bytes.
+ *
+ * The compressed buffer must be only a single 842-compressed buffer,
+ * with the standard format described in the comments in 842.h
+ * Processing will stop when the 842 "END" template is detected,
+ * not the end of the buffer.
+ *
+ * Returns: 0 on success, error on failure.  The @olen parameter
+ * will contain the number of output bytes written on success, or
+ * 0 on error.
+ */
+int sw842_decompress(const u8 *in, unsigned int ilen,
+                    u8 *out, unsigned int *olen)
+{
+       struct sw842_param p;
+       int ret;
+       u64 op, rep, tmp, bytes, total;
+
+       p.in = (u8 *)in;
+       p.bit = 0;
+       p.ilen = ilen;
+       p.out = out;
+       p.ostart = out;
+       p.olen = *olen;
+
+       total = p.olen;
+
+       *olen = 0;
+
+       do {
+               ret = next_bits(&p, &op, OP_BITS);
+               if (ret)
+                       return ret;
+
+               pr_debug("template is %lx\n", (unsigned long)op);
+
+               switch (op) {
+               case OP_REPEAT:
+                       ret = next_bits(&p, &rep, REPEAT_BITS);
+                       if (ret)
+                               return ret;
+
+                       if (p.out == out) /* no previous bytes */
+                               return -EINVAL;
+
+                       /* copy rep + 1 */
+                       rep++;
+
+                       if (rep * 8 > p.olen)
+                               return -ENOSPC;
+
+                       while (rep-- > 0) {
+                               memcpy(p.out, p.out - 8, 8);
+                               p.out += 8;
+                               p.olen -= 8;
+                       }
+
+                       if (sw842_template_counts)
+                               atomic_inc(&template_repeat_count);
+
+                       break;
+               case OP_ZEROS:
+                       if (8 > p.olen)
+                               return -ENOSPC;
+
+                       memset(p.out, 0, 8);
+                       p.out += 8;
+                       p.olen -= 8;
+
+                       if (sw842_template_counts)
+                               atomic_inc(&template_zeros_count);
+
+                       break;
+               case OP_SHORT_DATA:
+                       ret = next_bits(&p, &bytes, SHORT_DATA_BITS);
+                       if (ret)
+                               return ret;
+
+                       if (!bytes || bytes > SHORT_DATA_BITS_MAX)
+                               return -EINVAL;
+
+                       while (bytes-- > 0) {
+                               ret = next_bits(&p, &tmp, 8);
+                               if (ret)
+                                       return ret;
+                               *p.out = (u8)tmp;
+                               p.out++;
+                               p.olen--;
+                       }
+
+                       if (sw842_template_counts)
+                               atomic_inc(&template_short_data_count);
+
+                       break;
+               case OP_END:
+                       if (sw842_template_counts)
+                               atomic_inc(&template_end_count);
+
+                       break;
+               default: /* use template */
+                       ret = do_op(&p, op);
+                       if (ret)
+                               return ret;
+                       break;
+               }
+       } while (op != OP_END);
+
+       if (unlikely((total - p.olen) > UINT_MAX))
+               return -ENOSPC;
+
+       *olen = total - p.olen;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(sw842_decompress);
+
+static int __init sw842_init(void)
+{
+       if (sw842_template_counts)
+               sw842_debugfs_create();
+
+       return 0;
+}
+module_init(sw842_init);
+
+static void __exit sw842_exit(void)
+{
+       if (sw842_template_counts)
+               sw842_debugfs_remove();
+}
+module_exit(sw842_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Software 842 Decompressor");
+MODULE_AUTHOR("Dan Streetman <ddstreet@ieee.org>");
diff --git a/lib/842/Makefile b/lib/842/Makefile
new file mode 100644 (file)
index 0000000..5d24c0b
--- /dev/null
@@ -0,0 +1,2 @@
+obj-$(CONFIG_842_COMPRESS) += 842_compress.o
+obj-$(CONFIG_842_DECOMPRESS) += 842_decompress.o
index 601965a948e8d40a3deff542882a772ab873e093..34e332b8d32629e075a7f3ab061454d4a532adca 100644 (file)
@@ -212,6 +212,12 @@ config RANDOM32_SELFTEST
 #
 # compression support is select'ed if needed
 #
+config 842_COMPRESS
+       tristate
+
+config 842_DECOMPRESS
+       tristate
+
 config ZLIB_INFLATE
        tristate
 
index ba2b0c87e65b196c7c1f016798e0b59ea8dba97c..b908048f8d6a8e2b33723b222e1a4a88b2841774 100644 (file)
@@ -1233,6 +1233,7 @@ config RCU_TORTURE_TEST
        depends on DEBUG_KERNEL
        select TORTURE_TEST
        select SRCU
+       select TASKS_RCU
        default n
        help
          This option provides a kernel module that runs torture tests
@@ -1261,12 +1262,38 @@ config RCU_TORTURE_TEST_RUNNABLE
          Say N here if you want the RCU torture tests to start only
          after being manually enabled via /proc.
 
+config RCU_TORTURE_TEST_SLOW_PREINIT
+       bool "Slow down RCU grace-period pre-initialization to expose races"
+       depends on RCU_TORTURE_TEST
+       help
+         This option delays grace-period pre-initialization (the
+         propagation of CPU-hotplug changes up the rcu_node combining
+         tree) for a few jiffies between initializing each pair of
+         consecutive rcu_node structures.  This helps to expose races
+         involving grace-period pre-initialization, in other words, it
+         makes your kernel less stable.  It can also greatly increase
+         grace-period latency, especially on systems with large numbers
+         of CPUs.  This is useful when torture-testing RCU, but in
+         almost no other circumstance.
+
+         Say Y here if you want your system to crash and hang more often.
+         Say N if you want a sane system.
+
+config RCU_TORTURE_TEST_SLOW_PREINIT_DELAY
+       int "How much to slow down RCU grace-period pre-initialization"
+       range 0 5
+       default 3
+       depends on RCU_TORTURE_TEST_SLOW_PREINIT
+       help
+         This option specifies the number of jiffies to wait between
+         each rcu_node structure pre-initialization step.
+
 config RCU_TORTURE_TEST_SLOW_INIT
        bool "Slow down RCU grace-period initialization to expose races"
        depends on RCU_TORTURE_TEST
        help
-         This option makes grace-period initialization block for a
-         few jiffies between initializing each pair of consecutive
+         This option delays grace-period initialization for a few
+         jiffies between initializing each pair of consecutive
          rcu_node structures.  This helps to expose races involving
          grace-period initialization, in other words, it makes your
          kernel less stable.  It can also greatly increase grace-period
@@ -1286,6 +1313,30 @@ config RCU_TORTURE_TEST_SLOW_INIT_DELAY
          This option specifies the number of jiffies to wait between
          each rcu_node structure initialization.
 
+config RCU_TORTURE_TEST_SLOW_CLEANUP
+       bool "Slow down RCU grace-period cleanup to expose races"
+       depends on RCU_TORTURE_TEST
+       help
+         This option delays grace-period cleanup for a few jiffies
+         between cleaning up each pair of consecutive rcu_node
+         structures.  This helps to expose races involving grace-period
+         cleanup, in other words, it makes your kernel less stable.
+         It can also greatly increase grace-period latency, especially
+         on systems with large numbers of CPUs.  This is useful when
+         torture-testing RCU, but in almost no other circumstance.
+
+         Say Y here if you want your system to crash and hang more often.
+         Say N if you want a sane system.
+
+config RCU_TORTURE_TEST_SLOW_CLEANUP_DELAY
+       int "How much to slow down RCU grace-period cleanup"
+       range 0 5
+       default 3
+       depends on RCU_TORTURE_TEST_SLOW_CLEANUP
+       help
+         This option specifies the number of jiffies to wait between
+         each rcu_node structure cleanup operation.
+
 config RCU_CPU_STALL_TIMEOUT
        int "RCU CPU stall timeout in seconds"
        depends on RCU_STALL_COMMON
@@ -1322,6 +1373,17 @@ config RCU_TRACE
          Say Y here if you want to enable RCU tracing
          Say N if you are unsure.
 
+config RCU_EQS_DEBUG
+       bool "Use this when adding any sort of NO_HZ support to your arch"
+       depends on DEBUG_KERNEL
+       help
+         This option provides consistency checks in RCU's handling of
+         NO_HZ.  These checks have proven quite helpful in detecting
+         bugs in arch-specific NO_HZ code.
+
+         Say N here if you need ultimate kernel/user switch latencies
+         Say Y if you are unsure
+
 endmenu # "RCU Debugging"
 
 config DEBUG_BLOCK_EXT_DEVT
index 6c37933336a0261007f73e8511936e95ff1f514c..ff37c8c2f7b24fbaa0bb6e04faa8aef5edd3dbbc 100644 (file)
@@ -78,6 +78,8 @@ obj-$(CONFIG_LIBCRC32C)       += libcrc32c.o
 obj-$(CONFIG_CRC8)     += crc8.o
 obj-$(CONFIG_GENERIC_ALLOCATOR) += genalloc.o
 
+obj-$(CONFIG_842_COMPRESS) += 842/
+obj-$(CONFIG_842_DECOMPRESS) += 842/
 obj-$(CONFIG_ZLIB_INFLATE) += zlib_inflate/
 obj-$(CONFIG_ZLIB_DEFLATE) += zlib_deflate/
 obj-$(CONFIG_REED_SOLOMON) += reed_solomon/
index 5f627084f2e998b2605016c311411d91f7016918..5a70f6196f577a071ae0a31e9da7fa0e1dd1bc68 100644 (file)
 int cpumask_next_and(int n, const struct cpumask *src1p,
                     const struct cpumask *src2p)
 {
-       struct cpumask tmp;
-
-       if (cpumask_and(&tmp, src1p, src2p))
-               return cpumask_next(n, &tmp);
-       return nr_cpu_ids;
+       while ((n = cpumask_next(n, src1p)) < nr_cpu_ids)
+               if (cpumask_test_cpu(n, src2p))
+                       break;
+       return n;
 }
 EXPORT_SYMBOL(cpumask_next_and);
 
index a63472b82416fadf00dc9831442789bc96d505dc..b3219d0abfb471558c977ae3b842485b4373a53d 100644 (file)
@@ -9,7 +9,7 @@
 #include <linux/module.h>
 #include <linux/crc-itu-t.h>
 
-/** CRC table for the CRC ITU-T V.41 0x0x1021 (x^16 + x^12 + x^15 + 1) */
+/** CRC table for the CRC ITU-T V.41 0x1021 (x^16 + x^12 + x^15 + 1) */
 const u16 crc_itu_t_table[256] = {
        0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7,
        0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef,
index aac511417ad19af5d9e3472747a983be5ed3ee4b..a89d041592c8bfa7b092c382962a4085560f5b1a 100644 (file)
@@ -639,7 +639,7 @@ do { \
        **************  MIPS  *****************
        ***************************************/
 #if defined(__mips__) && W_TYPE_SIZE == 32
-#if __GNUC__ >= 4 && __GNUC_MINOR__ >= 4
+#if (__GNUC__ >= 5) || (__GNUC__ >= 4 && __GNUC_MINOR__ >= 4)
 #define umul_ppmm(w1, w0, u, v)                        \
 do {                                           \
        UDItype __ll = (UDItype)(u) * (v);      \
@@ -671,7 +671,7 @@ do {                                                \
        **************  MIPS/64  **************
        ***************************************/
 #if (defined(__mips) && __mips >= 3) && W_TYPE_SIZE == 64
-#if __GNUC__ >= 4 && __GNUC_MINOR__ >= 4
+#if (__GNUC__ >= 5) || (__GNUC__ >= 4 && __GNUC_MINOR__ >= 4)
 #define umul_ppmm(w1, w0, u, v) \
 do {                                                                   \
        typedef unsigned int __ll_UTItype __attribute__((mode(TI)));    \
index 4cc6442733f49577f29647984db9f310d06a21c4..bc0a1da8afba2c5362fb55772e7154a8ad2ea844 100644 (file)
@@ -128,28 +128,36 @@ leave:
 }
 EXPORT_SYMBOL_GPL(mpi_read_from_buffer);
 
-/****************
- * Return an allocated buffer with the MPI (msb first).
- * NBYTES receives the length of this buffer. Caller must free the
- * return string (This function does return a 0 byte buffer with NBYTES
- * set to zero if the value of A is zero. If sign is not NULL, it will
- * be set to the sign of the A.
+/**
+ * mpi_read_buffer() - read MPI to a bufer provided by user (msb first)
+ *
+ * @a:         a multi precision integer
+ * @buf:       bufer to which the output will be written to. Needs to be at
+ *             leaset mpi_get_size(a) long.
+ * @buf_len:   size of the buf.
+ * @nbytes:    receives the actual length of the data written.
+ * @sign:      if not NULL, it will be set to the sign of a.
+ *
+ * Return:     0 on success or error code in case of error
  */
-void *mpi_get_buffer(MPI a, unsigned *nbytes, int *sign)
+int mpi_read_buffer(MPI a, uint8_t *buf, unsigned buf_len, unsigned *nbytes,
+                   int *sign)
 {
-       uint8_t *p, *buffer;
+       uint8_t *p;
        mpi_limb_t alimb;
+       unsigned int n = mpi_get_size(a);
        int i;
-       unsigned int n;
+
+       if (buf_len < n || !buf)
+               return -EINVAL;
 
        if (sign)
                *sign = a->sign;
-       *nbytes = n = a->nlimbs * BYTES_PER_MPI_LIMB;
-       if (!n)
-               n++;            /* avoid zero length allocation */
-       p = buffer = kmalloc(n, GFP_KERNEL);
-       if (!p)
-               return NULL;
+
+       if (nbytes)
+               *nbytes = n;
+
+       p = buf;
 
        for (i = a->nlimbs - 1; i >= 0; i--) {
                alimb = a->d[i];
@@ -171,15 +179,56 @@ void *mpi_get_buffer(MPI a, unsigned *nbytes, int *sign)
 #error please implement for this limb size.
 #endif
        }
+       return 0;
+}
+EXPORT_SYMBOL_GPL(mpi_read_buffer);
+
+/*
+ * mpi_get_buffer() - Returns an allocated buffer with the MPI (msb first).
+ * Caller must free the return string.
+ * This function does return a 0 byte buffer with nbytes set to zero if the
+ * value of A is zero.
+ *
+ * @a:         a multi precision integer.
+ * @nbytes:    receives the length of this buffer.
+ * @sign:      if not NULL, it will be set to the sign of the a.
+ *
+ * Return:     Pointer to MPI buffer or NULL on error
+ */
+void *mpi_get_buffer(MPI a, unsigned *nbytes, int *sign)
+{
+       uint8_t *buf, *p;
+       unsigned int n;
+       int ret;
+
+       if (!nbytes)
+               return NULL;
+
+       n = mpi_get_size(a);
+
+       if (!n)
+               n++;
+
+       buf = kmalloc(n, GFP_KERNEL);
+
+       if (!buf)
+               return NULL;
+
+       ret = mpi_read_buffer(a, buf, n, nbytes, sign);
+
+       if (ret) {
+               kfree(buf);
+               return NULL;
+       }
 
        /* this is sub-optimal but we need to do the shift operation
         * because the caller has to free the returned buffer */
-       for (p = buffer; !*p && *nbytes; p++, --*nbytes)
+       for (p = buf; !*p && *nbytes; p++, --*nbytes)
                ;
-       if (p != buffer)
-               memmove(buffer, p, *nbytes);
+       if (p != buf)
+               memmove(buf, p, *nbytes);
 
-       return buffer;
+       return buf;
 }
 EXPORT_SYMBOL_GPL(mpi_get_buffer);
 
index bf076d281d4045da0b4b780ecd19a19db3987485..314f4dfa603ee98fd89e1f1006a09e8147af6cc7 100644 (file)
@@ -69,7 +69,7 @@ void mpi_free_limb_space(mpi_ptr_t a)
        if (!a)
                return;
 
-       kfree(a);
+       kzfree(a);
 }
 
 void mpi_assign_limb_space(MPI a, mpi_ptr_t ap, unsigned nlimbs)
@@ -95,7 +95,7 @@ int mpi_resize(MPI a, unsigned nlimbs)
                if (!p)
                        return -ENOMEM;
                memcpy(p, a->d, a->alloced * sizeof(mpi_limb_t));
-               kfree(a->d);
+               kzfree(a->d);
                a->d = p;
        } else {
                a->d = kzalloc(nlimbs * sizeof(mpi_limb_t), GFP_KERNEL);
@@ -112,7 +112,7 @@ void mpi_free(MPI a)
                return;
 
        if (a->flags & 4)
-               kfree(a->d);
+               kzfree(a->d);
        else
                mpi_free_limb_space(a->d);
 
index c7dab0645554ea341590bee0ecf5b0a0139a55bd..3b10a48fa0401ff87902525095c9e1d189bfe7dd 100644 (file)
@@ -15,7 +15,7 @@ quiet_cmd_unroll = UNROLL  $@
                    < $< > $@ || ( rm -f $@ && exit 1 )
 
 ifeq ($(CONFIG_ALTIVEC),y)
-altivec_flags := -maltivec -mabi=altivec
+altivec_flags := -maltivec $(call cc-option,-mabi=altivec)
 endif
 
 # The GCC option -ffreestanding is required in order to compile code containing
index b7595484a8150c02e086f5d1a3ac41cd971bd91b..8fe9d9662abbcda7563000c00e5f516dc156f05f 100644 (file)
@@ -23,7 +23,7 @@
 
 #ifdef __KERNEL__ /* Real code */
 
-#include <asm/i387.h>
+#include <asm/fpu/api.h>
 
 #else /* Dummy code for user space testing */
 
index 4396434e471536b4772ef06efcb983f87c580889..8609378e6505123a3688e0e95a18cdde013e278a 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/random.h>
 #include <linux/rhashtable.h>
 #include <linux/err.h>
+#include <linux/export.h>
 
 #define HASH_DEFAULT_SIZE      64UL
 #define HASH_MIN_SIZE          4U
index c9f2e8c6ccc996c8a40bac6872749185170ec7f9..99fbc2f238c4976a1b95df715d22d35f739b4cc7 100644 (file)
@@ -56,6 +56,38 @@ int sg_nents(struct scatterlist *sg)
 }
 EXPORT_SYMBOL(sg_nents);
 
+/**
+ * sg_nents_for_len - return total count of entries in scatterlist
+ *                    needed to satisfy the supplied length
+ * @sg:                The scatterlist
+ * @len:       The total required length
+ *
+ * Description:
+ * Determines the number of entries in sg that are required to meet
+ * the supplied length, taking into acount chaining as well
+ *
+ * Returns:
+ *   the number of sg entries needed, negative error on failure
+ *
+ **/
+int sg_nents_for_len(struct scatterlist *sg, u64 len)
+{
+       int nents;
+       u64 total;
+
+       if (!len)
+               return 0;
+
+       for (nents = 0, total = 0; sg; sg = sg_next(sg)) {
+               nents++;
+               total += sg->length;
+               if (total >= len)
+                       return nents;
+       }
+
+       return -EINVAL;
+}
+EXPORT_SYMBOL(sg_nents_for_len);
 
 /**
  * sg_last - return the last scatterlist entry in a list
index 36c15a2889e44fc062463ef24f132d0dd7b33def..3a5f2b366d84ed209a012cf62491ca30f6a8bca8 100644 (file)
@@ -57,7 +57,8 @@ static inline long do_strnlen_user(const char __user *src, unsigned long count,
                        return res + find_zero(data) + 1 - align;
                }
                res += sizeof(unsigned long);
-               if (unlikely(max < sizeof(unsigned long)))
+               /* We already handled 'unsigned long' bytes. Did we do it all ? */
+               if (unlikely(max <= sizeof(unsigned long)))
                        break;
                max -= sizeof(unsigned long);
                if (unlikely(__get_user(c,(unsigned long __user *)(src+res))))
@@ -90,8 +91,15 @@ static inline long do_strnlen_user(const char __user *src, unsigned long count,
  * Get the size of a NUL-terminated string in user space.
  *
  * Returns the size of the string INCLUDING the terminating NUL.
- * If the string is too long, returns 'count+1'.
+ * If the string is too long, returns a number larger than @count. User
+ * has to check the return value against "> count".
  * On exception (or invalid count), returns 0.
+ *
+ * NOTE! You should basically never use this function. There is
+ * almost never any valid case for using the length of a user space
+ * string, since the string can be changed at any time by other
+ * threads. Use "strncpy_from_user()" instead to get a stable copy
+ * of the string.
  */
 long strnlen_user(const char __user *str, long count)
 {
index 4abda074ea458947390b84c36f3eaad7095a2ceb..42e192decbfd605dc4175cfbe9c93b260bbad8ef 100644 (file)
@@ -537,8 +537,9 @@ EXPORT_SYMBOL_GPL(swiotlb_tbl_map_single);
  * Allocates bounce buffer and returns its kernel virtual address.
  */
 
-phys_addr_t map_single(struct device *hwdev, phys_addr_t phys, size_t size,
-                      enum dma_data_direction dir)
+static phys_addr_t
+map_single(struct device *hwdev, phys_addr_t phys, size_t size,
+          enum dma_data_direction dir)
 {
        dma_addr_t start_dma_addr = phys_to_dma(hwdev, io_tlb_start);
 
@@ -655,7 +656,7 @@ swiotlb_alloc_coherent(struct device *hwdev, size_t size,
                 */
                phys_addr_t paddr = map_single(hwdev, 0, size, DMA_FROM_DEVICE);
                if (paddr == SWIOTLB_MAP_ERROR)
-                       return NULL;
+                       goto err_warn;
 
                ret = phys_to_virt(paddr);
                dev_addr = phys_to_dma(hwdev, paddr);
@@ -669,7 +670,7 @@ swiotlb_alloc_coherent(struct device *hwdev, size_t size,
                        /* DMA_TO_DEVICE to avoid memcpy in unmap_single */
                        swiotlb_tbl_unmap_single(hwdev, paddr,
                                                 size, DMA_TO_DEVICE);
-                       return NULL;
+                       goto err_warn;
                }
        }
 
@@ -677,6 +678,13 @@ swiotlb_alloc_coherent(struct device *hwdev, size_t size,
        memset(ret, 0, size);
 
        return ret;
+
+err_warn:
+       pr_warn("swiotlb: coherent allocation failed for device %s size=%zu\n",
+               dev_name(hwdev), size);
+       dump_stack();
+
+       return NULL;
 }
 EXPORT_SYMBOL(swiotlb_alloc_coherent);
 
index a382e4a326091691a617a7c3387a09c813c50792..782ae8ca2c06f2b3439b10592cef6a43c31223cd 100644 (file)
@@ -36,7 +36,7 @@
  * Adds the timer node to the timerqueue, sorted by the
  * node's expires value.
  */
-void timerqueue_add(struct timerqueue_head *head, struct timerqueue_node *node)
+bool timerqueue_add(struct timerqueue_head *head, struct timerqueue_node *node)
 {
        struct rb_node **p = &head->head.rb_node;
        struct rb_node *parent = NULL;
@@ -56,8 +56,11 @@ void timerqueue_add(struct timerqueue_head *head, struct timerqueue_node *node)
        rb_link_node(&node->node, parent, p);
        rb_insert_color(&node->node, &head->head);
 
-       if (!head->next || node->expires.tv64 < head->next->expires.tv64)
+       if (!head->next || node->expires.tv64 < head->next->expires.tv64) {
                head->next = node;
+               return true;
+       }
+       return false;
 }
 EXPORT_SYMBOL_GPL(timerqueue_add);
 
@@ -69,7 +72,7 @@ EXPORT_SYMBOL_GPL(timerqueue_add);
  *
  * Removes the timer node from the timerqueue.
  */
-void timerqueue_del(struct timerqueue_head *head, struct timerqueue_node *node)
+bool timerqueue_del(struct timerqueue_head *head, struct timerqueue_node *node)
 {
        WARN_ON_ONCE(RB_EMPTY_NODE(&node->node));
 
@@ -82,6 +85,7 @@ void timerqueue_del(struct timerqueue_head *head, struct timerqueue_node *node)
        }
        rb_erase(&node->node, &head->head);
        RB_CLEAR_NODE(&node->node);
+       return head->next != NULL;
 }
 EXPORT_SYMBOL_GPL(timerqueue_del);
 
index 6dc4580df2af040b10bc10a5f9c423becc3ff47e..000e7b3b9896f2a9479687befd2442c43193614e 100644 (file)
@@ -359,23 +359,6 @@ static void bdi_wb_shutdown(struct backing_dev_info *bdi)
        flush_delayed_work(&bdi->wb.dwork);
 }
 
-/*
- * Called when the device behind @bdi has been removed or ejected.
- *
- * We can't really do much here except for reducing the dirty ratio at
- * the moment.  In the future we should be able to set a flag so that
- * the filesystem can handle errors at mark_inode_dirty time instead
- * of only at writeback time.
- */
-void bdi_unregister(struct backing_dev_info *bdi)
-{
-       if (WARN_ON_ONCE(!bdi->dev))
-               return;
-
-       bdi_set_min_ratio(bdi, 0);
-}
-EXPORT_SYMBOL(bdi_unregister);
-
 static void bdi_wb_init(struct bdi_writeback *wb, struct backing_dev_info *bdi)
 {
        memset(wb, 0, sizeof(*wb));
@@ -443,6 +426,7 @@ void bdi_destroy(struct backing_dev_info *bdi)
        int i;
 
        bdi_wb_shutdown(bdi);
+       bdi_set_min_ratio(bdi, 0);
 
        WARN_ON(!list_empty(&bdi->work_list));
        WARN_ON(delayed_work_pending(&bdi->wb.dwork));
index 14c2f2017e37cc405e52cb12bc30b128997f1f8e..a04225d372ba3ab77516b970c10135b19def3ac4 100644 (file)
@@ -2323,6 +2323,8 @@ done_restock:
        css_get_many(&memcg->css, batch);
        if (batch > nr_pages)
                refill_stock(memcg, batch - nr_pages);
+       if (!(gfp_mask & __GFP_WAIT))
+               goto done;
        /*
         * If the hierarchy is above the normal consumption range,
         * make the charging task trim their excess contribution.
@@ -5833,9 +5835,7 @@ void mem_cgroup_swapout(struct page *page, swp_entry_t entry)
        if (!mem_cgroup_is_root(memcg))
                page_counter_uncharge(&memcg->memory, 1);
 
-       /* XXX: caller holds IRQ-safe mapping->tree_lock */
-       VM_BUG_ON(!irqs_disabled());
-
+       /* Caller disabled preemption with mapping->tree_lock */
        mem_cgroup_charge_statistics(memcg, page, -1);
        memcg_check_events(memcg, page);
 }
index 457bde530cbedcf0dea2f35e219466de0acf204d..9e88f749aa512395daea45f2727545fa0f281533 100644 (file)
@@ -1969,8 +1969,10 @@ void try_offline_node(int nid)
                 * wait_table may be allocated from boot memory,
                 * here only free if it's allocated by vmalloc.
                 */
-               if (is_vmalloc_addr(zone->wait_table))
+               if (is_vmalloc_addr(zone->wait_table)) {
                        vfree(zone->wait_table);
+                       zone->wait_table = NULL;
+               }
        }
 }
 EXPORT_SYMBOL(try_offline_node);
index de981370fbc5d596de3d419062c0977829602af7..3759099d8ce438f57398d84f50049b9e8821bd3a 100644 (file)
@@ -2451,6 +2451,7 @@ static int shmem_symlink(struct inode *dir, struct dentry *dentry, const char *s
                        return -ENOMEM;
                }
                inode->i_op = &shmem_short_symlink_operations;
+               inode->i_link = info->symlink;
        } else {
                error = shmem_getpage(inode, 0, &page, SGP_WRITE, NULL);
                if (error) {
@@ -2474,30 +2475,23 @@ static int shmem_symlink(struct inode *dir, struct dentry *dentry, const char *s
        return 0;
 }
 
-static void *shmem_follow_short_symlink(struct dentry *dentry, struct nameidata *nd)
-{
-       nd_set_link(nd, SHMEM_I(d_inode(dentry))->symlink);
-       return NULL;
-}
-
-static void *shmem_follow_link(struct dentry *dentry, struct nameidata *nd)
+static const char *shmem_follow_link(struct dentry *dentry, void **cookie)
 {
        struct page *page = NULL;
        int error = shmem_getpage(d_inode(dentry), 0, &page, SGP_READ, NULL);
-       nd_set_link(nd, error ? ERR_PTR(error) : kmap(page));
-       if (page)
-               unlock_page(page);
-       return page;
+       if (error)
+               return ERR_PTR(error);
+       unlock_page(page);
+       *cookie = page;
+       return kmap(page);
 }
 
-static void shmem_put_link(struct dentry *dentry, struct nameidata *nd, void *cookie)
+static void shmem_put_link(struct inode *unused, void *cookie)
 {
-       if (!IS_ERR(nd_get_link(nd))) {
-               struct page *page = cookie;
-               kunmap(page);
-               mark_page_accessed(page);
-               page_cache_release(page);
-       }
+       struct page *page = cookie;
+       kunmap(page);
+       mark_page_accessed(page);
+       page_cache_release(page);
 }
 
 #ifdef CONFIG_TMPFS_XATTR
@@ -2642,7 +2636,7 @@ static ssize_t shmem_listxattr(struct dentry *dentry, char *buffer, size_t size)
 
 static const struct inode_operations shmem_short_symlink_operations = {
        .readlink       = generic_readlink,
-       .follow_link    = shmem_follow_short_symlink,
+       .follow_link    = simple_follow_link,
 #ifdef CONFIG_TMPFS_XATTR
        .setxattr       = shmem_setxattr,
        .getxattr       = shmem_getxattr,
@@ -3401,7 +3395,13 @@ int shmem_zero_setup(struct vm_area_struct *vma)
        struct file *file;
        loff_t size = vma->vm_end - vma->vm_start;
 
-       file = shmem_file_setup("dev/zero", size, vma->vm_flags);
+       /*
+        * Cloning a new file under mmap_sem leads to a lock ordering conflict
+        * between XFS directory reading and selinux: since this file is only
+        * accessible to the user through its mapping, use S_PRIVATE flag to
+        * bypass file security, in the same way as shmem_kernel_file_setup().
+        */
+       file = __shmem_file_setup("dev/zero", size, vma->vm_flags, S_PRIVATE);
        if (IS_ERR(file))
                return PTR_ERR(file);
 
index 08bd7a3d464a9c6959a39e269d2284600e750a50..a8b5e749e84e7dbd50d325eecf84a47316145598 100644 (file)
@@ -289,7 +289,8 @@ static int create_handle_cache(struct zs_pool *pool)
 
 static void destroy_handle_cache(struct zs_pool *pool)
 {
-       kmem_cache_destroy(pool->handle_cachep);
+       if (pool->handle_cachep)
+               kmem_cache_destroy(pool->handle_cachep);
 }
 
 static unsigned long alloc_handle(struct zs_pool *pool)
index 3533d2a53ab649e634487edfa63dcc965a3ffa07..37a78d20c0f647a976ec58dbad1a06a957cc1771 100644 (file)
@@ -648,6 +648,7 @@ rdma_create_trans(struct p9_client *client, const char *addr, char *args)
        struct rdma_conn_param conn_param;
        struct ib_qp_init_attr qp_attr;
        struct ib_device_attr devattr;
+       struct ib_cq_init_attr cq_attr = {};
 
        /* Parse the transport specific mount options */
        err = parse_opts(args, &opts);
@@ -705,9 +706,10 @@ rdma_create_trans(struct p9_client *client, const char *addr, char *args)
                goto error;
 
        /* Create the Completion Queue */
+       cq_attr.cqe = opts.sq_depth + opts.rq_depth + 1;
        rdma->cq = ib_create_cq(rdma->cm_id->device, cq_comp_handler,
                                cq_event_handler, client,
-                               opts.sq_depth + opts.rq_depth + 1, 0);
+                               &cq_attr);
        if (IS_ERR(rdma->cq))
                goto error;
        ib_req_notify_cq(rdma->cq, IB_CQ_NEXT_COMP);
index e0670d7054f97c05d46b74952ee53d6fa6910776..659fb96672e41e2e6525323697ca23a41d271fbb 100644 (file)
@@ -796,9 +796,11 @@ static int __br_fdb_add(struct ndmsg *ndm, struct net_bridge_port *p,
        int err = 0;
 
        if (ndm->ndm_flags & NTF_USE) {
+               local_bh_disable();
                rcu_read_lock();
                br_fdb_update(p->br, p, addr, vid, true);
                rcu_read_unlock();
+               local_bh_enable();
        } else {
                spin_lock_bh(&p->br->hash_lock);
                err = fdb_add_entry(p, addr, ndm->ndm_state,
index 22fd0419b31455965223566f4676b46efedd8722..ff667e18b2d6313f0a806752a4ef88435e939c4d 100644 (file)
@@ -1167,6 +1167,9 @@ static void br_multicast_add_router(struct net_bridge *br,
        struct net_bridge_port *p;
        struct hlist_node *slot = NULL;
 
+       if (!hlist_unhashed(&port->rlist))
+               return;
+
        hlist_for_each_entry(p, &br->router_list, rlist) {
                if ((unsigned long) port >= (unsigned long) p)
                        break;
@@ -1194,12 +1197,8 @@ static void br_multicast_mark_router(struct net_bridge *br,
        if (port->multicast_router != 1)
                return;
 
-       if (!hlist_unhashed(&port->rlist))
-               goto timer;
-
        br_multicast_add_router(br, port);
 
-timer:
        mod_timer(&port->multicast_router_timer,
                  now + br->multicast_querier_interval);
 }
index 91180a7fc94376ea3ca7eecf274c03c3bc919590..c2cabbe6fa58170f650e00eaac4996a5f5d74818 100644 (file)
@@ -6,7 +6,7 @@
  *
  *  ebtables.c,v 2.0, July, 2002
  *
- *  This code is stongly inspired on the iptables code which is
+ *  This code is strongly inspired by the iptables code which is
  *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
  *
  *  This program is free software; you can redistribute it and/or
index 2c1c67fad64d57f3d744c89843816b2d64f5b834..aa82f9ab6a36d164769bf7c9633fcdfd5971466f 100644 (file)
@@ -1718,15 +1718,8 @@ EXPORT_SYMBOL_GPL(is_skb_forwardable);
 
 int __dev_forward_skb(struct net_device *dev, struct sk_buff *skb)
 {
-       if (skb_shinfo(skb)->tx_flags & SKBTX_DEV_ZEROCOPY) {
-               if (skb_copy_ubufs(skb, GFP_ATOMIC)) {
-                       atomic_long_inc(&dev->rx_dropped);
-                       kfree_skb(skb);
-                       return NET_RX_DROP;
-               }
-       }
-
-       if (unlikely(!is_skb_forwardable(dev, skb))) {
+       if (skb_orphan_frags(skb, GFP_ATOMIC) ||
+           unlikely(!is_skb_forwardable(dev, skb))) {
                atomic_long_inc(&dev->rx_dropped);
                kfree_skb(skb);
                return NET_RX_DROP;
index 508155b283ddcc73a967a2bc8068e67cb8cada7d..54817d365366f8f7b408f0b790d9c92628147026 100644 (file)
@@ -2212,8 +2212,6 @@ static void spin(struct pktgen_dev *pkt_dev, ktime_t spin_until)
                do {
                        set_current_state(TASK_INTERRUPTIBLE);
                        hrtimer_start_expires(&t.timer, HRTIMER_MODE_ABS);
-                       if (!hrtimer_active(&t.timer))
-                               t.task = NULL;
 
                        if (likely(t.task))
                                schedule();
index 3cfff2a3d651fb7d7cd2baaa3698c123eb7fc00f..41ec02242ea7c2ff57a6b506b685df22c62f3dcc 100644 (file)
@@ -4398,7 +4398,7 @@ struct sk_buff *alloc_skb_with_frags(unsigned long header_len,
 
                while (order) {
                        if (npages >= 1 << order) {
-                               page = alloc_pages(gfp_mask |
+                               page = alloc_pages((gfp_mask & ~__GFP_WAIT) |
                                                   __GFP_COMP |
                                                   __GFP_NOWARN |
                                                   __GFP_NORETRY,
index 292f42228bfb361b5748998bbcc538b1e16a2f22..dc30dc5bb1b892923397fee073d42e9e5ef53a7e 100644 (file)
@@ -354,15 +354,12 @@ void sk_clear_memalloc(struct sock *sk)
 
        /*
         * SOCK_MEMALLOC is allowed to ignore rmem limits to ensure forward
-        * progress of swapping. However, if SOCK_MEMALLOC is cleared while
-        * it has rmem allocations there is a risk that the user of the
-        * socket cannot make forward progress due to exceeding the rmem
-        * limits. By rights, sk_clear_memalloc() should only be called
-        * on sockets being torn down but warn and reset the accounting if
-        * that assumption breaks.
+        * progress of swapping. SOCK_MEMALLOC may be cleared while
+        * it has rmem allocations due to the last swapfile being deactivated
+        * but there is a risk that the socket is unusable due to exceeding
+        * the rmem limits. Reclaim the reserves and obey rmem limits again.
         */
-       if (WARN_ON(sk->sk_forward_alloc))
-               sk_mem_reclaim(sk);
+       sk_mem_reclaim(sk);
 }
 EXPORT_SYMBOL_GPL(sk_clear_memalloc);
 
@@ -1883,7 +1880,7 @@ bool skb_page_frag_refill(unsigned int sz, struct page_frag *pfrag, gfp_t gfp)
 
        pfrag->offset = 0;
        if (SKB_FRAG_PAGE_ORDER) {
-               pfrag->page = alloc_pages(gfp | __GFP_COMP |
+               pfrag->page = alloc_pages((gfp & ~__GFP_WAIT) | __GFP_COMP |
                                          __GFP_NOWARN | __GFP_NORETRY,
                                          SKB_FRAG_PAGE_ORDER);
                if (likely(pfrag->page)) {
index 30b544f025acc09aaad99d9adc1e5dbc1227d307..477937465a202ccdf458a808776ea3e90c853b84 100644 (file)
@@ -49,7 +49,7 @@ static void *esp_alloc_tmp(struct crypto_aead *aead, int nfrags, int seqhilen)
                len = ALIGN(len, crypto_tfm_ctx_alignment());
        }
 
-       len += sizeof(struct aead_givcrypt_request) + crypto_aead_reqsize(aead);
+       len += sizeof(struct aead_request) + crypto_aead_reqsize(aead);
        len = ALIGN(len, __alignof__(struct scatterlist));
 
        len += sizeof(struct scatterlist) * nfrags;
@@ -68,17 +68,6 @@ static inline u8 *esp_tmp_iv(struct crypto_aead *aead, void *tmp, int seqhilen)
                         crypto_aead_alignmask(aead) + 1) : tmp + seqhilen;
 }
 
-static inline struct aead_givcrypt_request *esp_tmp_givreq(
-       struct crypto_aead *aead, u8 *iv)
-{
-       struct aead_givcrypt_request *req;
-
-       req = (void *)PTR_ALIGN(iv + crypto_aead_ivsize(aead),
-                               crypto_tfm_ctx_alignment());
-       aead_givcrypt_set_tfm(req, aead);
-       return req;
-}
-
 static inline struct aead_request *esp_tmp_req(struct crypto_aead *aead, u8 *iv)
 {
        struct aead_request *req;
@@ -97,14 +86,6 @@ static inline struct scatterlist *esp_req_sg(struct crypto_aead *aead,
                             __alignof__(struct scatterlist));
 }
 
-static inline struct scatterlist *esp_givreq_sg(
-       struct crypto_aead *aead, struct aead_givcrypt_request *req)
-{
-       return (void *)ALIGN((unsigned long)(req + 1) +
-                            crypto_aead_reqsize(aead),
-                            __alignof__(struct scatterlist));
-}
-
 static void esp_output_done(struct crypto_async_request *base, int err)
 {
        struct sk_buff *skb = base->data;
@@ -113,14 +94,37 @@ static void esp_output_done(struct crypto_async_request *base, int err)
        xfrm_output_resume(skb, err);
 }
 
+/* Move ESP header back into place. */
+static void esp_restore_header(struct sk_buff *skb, unsigned int offset)
+{
+       struct ip_esp_hdr *esph = (void *)(skb->data + offset);
+       void *tmp = ESP_SKB_CB(skb)->tmp;
+       __be32 *seqhi = esp_tmp_seqhi(tmp);
+
+       esph->seq_no = esph->spi;
+       esph->spi = *seqhi;
+}
+
+static void esp_output_restore_header(struct sk_buff *skb)
+{
+       esp_restore_header(skb, skb_transport_offset(skb) - sizeof(__be32));
+}
+
+static void esp_output_done_esn(struct crypto_async_request *base, int err)
+{
+       struct sk_buff *skb = base->data;
+
+       esp_output_restore_header(skb);
+       esp_output_done(base, err);
+}
+
 static int esp_output(struct xfrm_state *x, struct sk_buff *skb)
 {
        int err;
        struct ip_esp_hdr *esph;
        struct crypto_aead *aead;
-       struct aead_givcrypt_request *req;
+       struct aead_request *req;
        struct scatterlist *sg;
-       struct scatterlist *asg;
        struct sk_buff *trailer;
        void *tmp;
        u8 *iv;
@@ -129,17 +133,19 @@ static int esp_output(struct xfrm_state *x, struct sk_buff *skb)
        int clen;
        int alen;
        int plen;
+       int ivlen;
        int tfclen;
        int nfrags;
        int assoclen;
-       int sglists;
        int seqhilen;
        __be32 *seqhi;
+       __be64 seqno;
 
        /* skb is pure payload to encrypt */
 
        aead = x->data;
        alen = crypto_aead_authsize(aead);
+       ivlen = crypto_aead_ivsize(aead);
 
        tfclen = 0;
        if (x->tfcpad) {
@@ -160,16 +166,14 @@ static int esp_output(struct xfrm_state *x, struct sk_buff *skb)
        nfrags = err;
 
        assoclen = sizeof(*esph);
-       sglists = 1;
        seqhilen = 0;
 
        if (x->props.flags & XFRM_STATE_ESN) {
-               sglists += 2;
                seqhilen += sizeof(__be32);
                assoclen += seqhilen;
        }
 
-       tmp = esp_alloc_tmp(aead, nfrags + sglists, seqhilen);
+       tmp = esp_alloc_tmp(aead, nfrags, seqhilen);
        if (!tmp) {
                err = -ENOMEM;
                goto error;
@@ -177,9 +181,8 @@ static int esp_output(struct xfrm_state *x, struct sk_buff *skb)
 
        seqhi = esp_tmp_seqhi(tmp);
        iv = esp_tmp_iv(aead, tmp, seqhilen);
-       req = esp_tmp_givreq(aead, iv);
-       asg = esp_givreq_sg(aead, req);
-       sg = asg + sglists;
+       req = esp_tmp_req(aead, iv);
+       sg = esp_req_sg(aead, req);
 
        /* Fill padding... */
        tail = skb_tail_pointer(trailer);
@@ -235,37 +238,53 @@ static int esp_output(struct xfrm_state *x, struct sk_buff *skb)
                *skb_mac_header(skb) = IPPROTO_UDP;
        }
 
-       esph->spi = x->id.spi;
        esph->seq_no = htonl(XFRM_SKB_CB(skb)->seq.output.low);
 
+       aead_request_set_callback(req, 0, esp_output_done, skb);
+
+       /* For ESN we move the header forward by 4 bytes to
+        * accomodate the high bits.  We will move it back after
+        * encryption.
+        */
+       if ((x->props.flags & XFRM_STATE_ESN)) {
+               esph = (void *)(skb_transport_header(skb) - sizeof(__be32));
+               *seqhi = esph->spi;
+               esph->seq_no = htonl(XFRM_SKB_CB(skb)->seq.output.hi);
+               aead_request_set_callback(req, 0, esp_output_done_esn, skb);
+       }
+
+       esph->spi = x->id.spi;
+
        sg_init_table(sg, nfrags);
        skb_to_sgvec(skb, sg,
-                    esph->enc_data + crypto_aead_ivsize(aead) - skb->data,
-                    clen + alen);
+                    (unsigned char *)esph - skb->data,
+                    assoclen + ivlen + clen + alen);
 
-       if ((x->props.flags & XFRM_STATE_ESN)) {
-               sg_init_table(asg, 3);
-               sg_set_buf(asg, &esph->spi, sizeof(__be32));
-               *seqhi = htonl(XFRM_SKB_CB(skb)->seq.output.hi);
-               sg_set_buf(asg + 1, seqhi, seqhilen);
-               sg_set_buf(asg + 2, &esph->seq_no, sizeof(__be32));
-       } else
-               sg_init_one(asg, esph, sizeof(*esph));
-
-       aead_givcrypt_set_callback(req, 0, esp_output_done, skb);
-       aead_givcrypt_set_crypt(req, sg, sg, clen, iv);
-       aead_givcrypt_set_assoc(req, asg, assoclen);
-       aead_givcrypt_set_giv(req, esph->enc_data,
-                             XFRM_SKB_CB(skb)->seq.output.low +
-                             ((u64)XFRM_SKB_CB(skb)->seq.output.hi << 32));
+       aead_request_set_crypt(req, sg, sg, ivlen + clen, iv);
+       aead_request_set_ad(req, assoclen);
+
+       seqno = cpu_to_be64(XFRM_SKB_CB(skb)->seq.output.low +
+                           ((u64)XFRM_SKB_CB(skb)->seq.output.hi << 32));
+
+       memset(iv, 0, ivlen);
+       memcpy(iv + ivlen - min(ivlen, 8), (u8 *)&seqno + 8 - min(ivlen, 8),
+              min(ivlen, 8));
 
        ESP_SKB_CB(skb)->tmp = tmp;
-       err = crypto_aead_givencrypt(req);
-       if (err == -EINPROGRESS)
+       err = crypto_aead_encrypt(req);
+
+       switch (err) {
+       case -EINPROGRESS:
                goto error;
 
-       if (err == -EBUSY)
+       case -EBUSY:
                err = NET_XMIT_DROP;
+               break;
+
+       case 0:
+               if ((x->props.flags & XFRM_STATE_ESN))
+                       esp_output_restore_header(skb);
+       }
 
        kfree(tmp);
 
@@ -364,6 +383,20 @@ static void esp_input_done(struct crypto_async_request *base, int err)
        xfrm_input_resume(skb, esp_input_done2(skb, err));
 }
 
+static void esp_input_restore_header(struct sk_buff *skb)
+{
+       esp_restore_header(skb, 0);
+       __skb_pull(skb, 4);
+}
+
+static void esp_input_done_esn(struct crypto_async_request *base, int err)
+{
+       struct sk_buff *skb = base->data;
+
+       esp_input_restore_header(skb);
+       esp_input_done(base, err);
+}
+
 /*
  * Note: detecting truncated vs. non-truncated authentication data is very
  * expensive, so we only support truncated data, which is the recommended
@@ -375,19 +408,18 @@ static int esp_input(struct xfrm_state *x, struct sk_buff *skb)
        struct crypto_aead *aead = x->data;
        struct aead_request *req;
        struct sk_buff *trailer;
-       int elen = skb->len - sizeof(*esph) - crypto_aead_ivsize(aead);
+       int ivlen = crypto_aead_ivsize(aead);
+       int elen = skb->len - sizeof(*esph) - ivlen;
        int nfrags;
        int assoclen;
-       int sglists;
        int seqhilen;
        __be32 *seqhi;
        void *tmp;
        u8 *iv;
        struct scatterlist *sg;
-       struct scatterlist *asg;
        int err = -EINVAL;
 
-       if (!pskb_may_pull(skb, sizeof(*esph) + crypto_aead_ivsize(aead)))
+       if (!pskb_may_pull(skb, sizeof(*esph) + ivlen))
                goto out;
 
        if (elen <= 0)
@@ -400,17 +432,15 @@ static int esp_input(struct xfrm_state *x, struct sk_buff *skb)
        nfrags = err;
 
        assoclen = sizeof(*esph);
-       sglists = 1;
        seqhilen = 0;
 
        if (x->props.flags & XFRM_STATE_ESN) {
-               sglists += 2;
                seqhilen += sizeof(__be32);
                assoclen += seqhilen;
        }
 
        err = -ENOMEM;
-       tmp = esp_alloc_tmp(aead, nfrags + sglists, seqhilen);
+       tmp = esp_alloc_tmp(aead, nfrags, seqhilen);
        if (!tmp)
                goto out;
 
@@ -418,36 +448,39 @@ static int esp_input(struct xfrm_state *x, struct sk_buff *skb)
        seqhi = esp_tmp_seqhi(tmp);
        iv = esp_tmp_iv(aead, tmp, seqhilen);
        req = esp_tmp_req(aead, iv);
-       asg = esp_req_sg(aead, req);
-       sg = asg + sglists;
+       sg = esp_req_sg(aead, req);
 
        skb->ip_summed = CHECKSUM_NONE;
 
        esph = (struct ip_esp_hdr *)skb->data;
 
-       /* Get ivec. This can be wrong, check against another impls. */
-       iv = esph->enc_data;
-
-       sg_init_table(sg, nfrags);
-       skb_to_sgvec(skb, sg, sizeof(*esph) + crypto_aead_ivsize(aead), elen);
+       aead_request_set_callback(req, 0, esp_input_done, skb);
 
+       /* For ESN we move the header forward by 4 bytes to
+        * accomodate the high bits.  We will move it back after
+        * decryption.
+        */
        if ((x->props.flags & XFRM_STATE_ESN)) {
-               sg_init_table(asg, 3);
-               sg_set_buf(asg, &esph->spi, sizeof(__be32));
-               *seqhi = XFRM_SKB_CB(skb)->seq.input.hi;
-               sg_set_buf(asg + 1, seqhi, seqhilen);
-               sg_set_buf(asg + 2, &esph->seq_no, sizeof(__be32));
-       } else
-               sg_init_one(asg, esph, sizeof(*esph));
+               esph = (void *)skb_push(skb, 4);
+               *seqhi = esph->spi;
+               esph->spi = esph->seq_no;
+               esph->seq_no = htonl(XFRM_SKB_CB(skb)->seq.input.hi);
+               aead_request_set_callback(req, 0, esp_input_done_esn, skb);
+       }
 
-       aead_request_set_callback(req, 0, esp_input_done, skb);
-       aead_request_set_crypt(req, sg, sg, elen, iv);
-       aead_request_set_assoc(req, asg, assoclen);
+       sg_init_table(sg, nfrags);
+       skb_to_sgvec(skb, sg, 0, skb->len);
+
+       aead_request_set_crypt(req, sg, sg, elen + ivlen, iv);
+       aead_request_set_ad(req, assoclen);
 
        err = crypto_aead_decrypt(req);
        if (err == -EINPROGRESS)
                goto out;
 
+       if ((x->props.flags & XFRM_STATE_ESN))
+               esp_input_restore_header(skb);
+
        err = esp_input_done2(skb, err);
 
 out:
@@ -519,10 +552,16 @@ static void esp_destroy(struct xfrm_state *x)
 
 static int esp_init_aead(struct xfrm_state *x)
 {
+       char aead_name[CRYPTO_MAX_ALG_NAME];
        struct crypto_aead *aead;
        int err;
 
-       aead = crypto_alloc_aead(x->aead->alg_name, 0, 0);
+       err = -ENAMETOOLONG;
+       if (snprintf(aead_name, CRYPTO_MAX_ALG_NAME, "%s(%s)",
+                    x->geniv, x->aead->alg_name) >= CRYPTO_MAX_ALG_NAME)
+               goto error;
+
+       aead = crypto_alloc_aead(aead_name, 0, 0);
        err = PTR_ERR(aead);
        if (IS_ERR(aead))
                goto error;
@@ -561,15 +600,19 @@ static int esp_init_authenc(struct xfrm_state *x)
 
        if ((x->props.flags & XFRM_STATE_ESN)) {
                if (snprintf(authenc_name, CRYPTO_MAX_ALG_NAME,
-                            "authencesn(%s,%s)",
+                            "%s%sauthencesn(%s,%s)%s",
+                            x->geniv ?: "", x->geniv ? "(" : "",
                             x->aalg ? x->aalg->alg_name : "digest_null",
-                            x->ealg->alg_name) >= CRYPTO_MAX_ALG_NAME)
+                            x->ealg->alg_name,
+                            x->geniv ? ")" : "") >= CRYPTO_MAX_ALG_NAME)
                        goto error;
        } else {
                if (snprintf(authenc_name, CRYPTO_MAX_ALG_NAME,
-                            "authenc(%s,%s)",
+                            "%s%sauthenc(%s,%s)%s",
+                            x->geniv ?: "", x->geniv ? "(" : "",
                             x->aalg ? x->aalg->alg_name : "digest_null",
-                            x->ealg->alg_name) >= CRYPTO_MAX_ALG_NAME)
+                            x->ealg->alg_name,
+                            x->geniv ? ")" : "") >= CRYPTO_MAX_ALG_NAME)
                        goto error;
        }
 
index 1c92ea67baefefb801d334fe60144cbcc3af63f2..83aa604f9273c332c5a0e5399253d961ef92eb9a 100644 (file)
@@ -90,6 +90,7 @@
 #include <linux/socket.h>
 #include <linux/sockios.h>
 #include <linux/igmp.h>
+#include <linux/inetdevice.h>
 #include <linux/in.h>
 #include <linux/errno.h>
 #include <linux/timer.h>
@@ -1960,6 +1961,7 @@ void udp_v4_early_demux(struct sk_buff *skb)
        struct sock *sk;
        struct dst_entry *dst;
        int dif = skb->dev->ifindex;
+       int ours;
 
        /* validate the packet */
        if (!pskb_may_pull(skb, skb_transport_offset(skb) + sizeof(struct udphdr)))
@@ -1969,14 +1971,24 @@ void udp_v4_early_demux(struct sk_buff *skb)
        uh = udp_hdr(skb);
 
        if (skb->pkt_type == PACKET_BROADCAST ||
-           skb->pkt_type == PACKET_MULTICAST)
+           skb->pkt_type == PACKET_MULTICAST) {
+               struct in_device *in_dev = __in_dev_get_rcu(skb->dev);
+
+               if (!in_dev)
+                       return;
+
+               ours = ip_check_mc_rcu(in_dev, iph->daddr, iph->saddr,
+                                      iph->protocol);
+               if (!ours)
+                       return;
                sk = __udp4_lib_mcast_demux_lookup(net, uh->dest, iph->daddr,
                                                   uh->source, iph->saddr, dif);
-       else if (skb->pkt_type == PACKET_HOST)
+       } else if (skb->pkt_type == PACKET_HOST) {
                sk = __udp4_lib_demux_lookup(net, uh->dest, iph->daddr,
                                             uh->source, iph->saddr, dif);
-       else
+       } else {
                return;
+       }
 
        if (!sk)
                return;
index d873ceea86e6c74c34e7fcd31bec41c78ce5720b..ca09bf49ac6806b399dba51399f84e47590cb9ed 100644 (file)
@@ -133,6 +133,14 @@ static void snmp6_free_dev(struct inet6_dev *idev)
        free_percpu(idev->stats.ipv6);
 }
 
+static void in6_dev_finish_destroy_rcu(struct rcu_head *head)
+{
+       struct inet6_dev *idev = container_of(head, struct inet6_dev, rcu);
+
+       snmp6_free_dev(idev);
+       kfree(idev);
+}
+
 /* Nobody refers to this device, we may destroy it. */
 
 void in6_dev_finish_destroy(struct inet6_dev *idev)
@@ -151,7 +159,6 @@ void in6_dev_finish_destroy(struct inet6_dev *idev)
                pr_warn("Freeing alive inet6 device %p\n", idev);
                return;
        }
-       snmp6_free_dev(idev);
-       kfree_rcu(idev, rcu);
+       call_rcu(&idev->rcu, in6_dev_finish_destroy_rcu);
 }
 EXPORT_SYMBOL(in6_dev_finish_destroy);
index 7c07ce36aae2a5b9cc14cb5a883327b7230b38ee..060a60b2f8a6db074167e389b56893337c887fe9 100644 (file)
@@ -76,7 +76,7 @@ static void *esp_alloc_tmp(struct crypto_aead *aead, int nfrags, int seqihlen)
                len = ALIGN(len, crypto_tfm_ctx_alignment());
        }
 
-       len += sizeof(struct aead_givcrypt_request) + crypto_aead_reqsize(aead);
+       len += sizeof(struct aead_request) + crypto_aead_reqsize(aead);
        len = ALIGN(len, __alignof__(struct scatterlist));
 
        len += sizeof(struct scatterlist) * nfrags;
@@ -96,17 +96,6 @@ static inline u8 *esp_tmp_iv(struct crypto_aead *aead, void *tmp, int seqhilen)
                         crypto_aead_alignmask(aead) + 1) : tmp + seqhilen;
 }
 
-static inline struct aead_givcrypt_request *esp_tmp_givreq(
-       struct crypto_aead *aead, u8 *iv)
-{
-       struct aead_givcrypt_request *req;
-
-       req = (void *)PTR_ALIGN(iv + crypto_aead_ivsize(aead),
-                               crypto_tfm_ctx_alignment());
-       aead_givcrypt_set_tfm(req, aead);
-       return req;
-}
-
 static inline struct aead_request *esp_tmp_req(struct crypto_aead *aead, u8 *iv)
 {
        struct aead_request *req;
@@ -125,14 +114,6 @@ static inline struct scatterlist *esp_req_sg(struct crypto_aead *aead,
                             __alignof__(struct scatterlist));
 }
 
-static inline struct scatterlist *esp_givreq_sg(
-       struct crypto_aead *aead, struct aead_givcrypt_request *req)
-{
-       return (void *)ALIGN((unsigned long)(req + 1) +
-                            crypto_aead_reqsize(aead),
-                            __alignof__(struct scatterlist));
-}
-
 static void esp_output_done(struct crypto_async_request *base, int err)
 {
        struct sk_buff *skb = base->data;
@@ -141,32 +122,57 @@ static void esp_output_done(struct crypto_async_request *base, int err)
        xfrm_output_resume(skb, err);
 }
 
+/* Move ESP header back into place. */
+static void esp_restore_header(struct sk_buff *skb, unsigned int offset)
+{
+       struct ip_esp_hdr *esph = (void *)(skb->data + offset);
+       void *tmp = ESP_SKB_CB(skb)->tmp;
+       __be32 *seqhi = esp_tmp_seqhi(tmp);
+
+       esph->seq_no = esph->spi;
+       esph->spi = *seqhi;
+}
+
+static void esp_output_restore_header(struct sk_buff *skb)
+{
+       esp_restore_header(skb, skb_transport_offset(skb) - sizeof(__be32));
+}
+
+static void esp_output_done_esn(struct crypto_async_request *base, int err)
+{
+       struct sk_buff *skb = base->data;
+
+       esp_output_restore_header(skb);
+       esp_output_done(base, err);
+}
+
 static int esp6_output(struct xfrm_state *x, struct sk_buff *skb)
 {
        int err;
        struct ip_esp_hdr *esph;
        struct crypto_aead *aead;
-       struct aead_givcrypt_request *req;
+       struct aead_request *req;
        struct scatterlist *sg;
-       struct scatterlist *asg;
        struct sk_buff *trailer;
        void *tmp;
        int blksize;
        int clen;
        int alen;
        int plen;
+       int ivlen;
        int tfclen;
        int nfrags;
        int assoclen;
-       int sglists;
        int seqhilen;
        u8 *iv;
        u8 *tail;
        __be32 *seqhi;
+       __be64 seqno;
 
        /* skb is pure payload to encrypt */
        aead = x->data;
        alen = crypto_aead_authsize(aead);
+       ivlen = crypto_aead_ivsize(aead);
 
        tfclen = 0;
        if (x->tfcpad) {
@@ -187,16 +193,14 @@ static int esp6_output(struct xfrm_state *x, struct sk_buff *skb)
        nfrags = err;
 
        assoclen = sizeof(*esph);
-       sglists = 1;
        seqhilen = 0;
 
        if (x->props.flags & XFRM_STATE_ESN) {
-               sglists += 2;
                seqhilen += sizeof(__be32);
                assoclen += seqhilen;
        }
 
-       tmp = esp_alloc_tmp(aead, nfrags + sglists, seqhilen);
+       tmp = esp_alloc_tmp(aead, nfrags, seqhilen);
        if (!tmp) {
                err = -ENOMEM;
                goto error;
@@ -204,9 +208,8 @@ static int esp6_output(struct xfrm_state *x, struct sk_buff *skb)
 
        seqhi = esp_tmp_seqhi(tmp);
        iv = esp_tmp_iv(aead, tmp, seqhilen);
-       req = esp_tmp_givreq(aead, iv);
-       asg = esp_givreq_sg(aead, req);
-       sg = asg + sglists;
+       req = esp_tmp_req(aead, iv);
+       sg = esp_req_sg(aead, req);
 
        /* Fill padding... */
        tail = skb_tail_pointer(trailer);
@@ -227,37 +230,53 @@ static int esp6_output(struct xfrm_state *x, struct sk_buff *skb)
        esph = ip_esp_hdr(skb);
        *skb_mac_header(skb) = IPPROTO_ESP;
 
-       esph->spi = x->id.spi;
        esph->seq_no = htonl(XFRM_SKB_CB(skb)->seq.output.low);
 
+       aead_request_set_callback(req, 0, esp_output_done, skb);
+
+       /* For ESN we move the header forward by 4 bytes to
+        * accomodate the high bits.  We will move it back after
+        * encryption.
+        */
+       if ((x->props.flags & XFRM_STATE_ESN)) {
+               esph = (void *)(skb_transport_header(skb) - sizeof(__be32));
+               *seqhi = esph->spi;
+               esph->seq_no = htonl(XFRM_SKB_CB(skb)->seq.output.hi);
+               aead_request_set_callback(req, 0, esp_output_done_esn, skb);
+       }
+
+       esph->spi = x->id.spi;
+
        sg_init_table(sg, nfrags);
        skb_to_sgvec(skb, sg,
-                    esph->enc_data + crypto_aead_ivsize(aead) - skb->data,
-                    clen + alen);
+                    (unsigned char *)esph - skb->data,
+                    assoclen + ivlen + clen + alen);
 
-       if ((x->props.flags & XFRM_STATE_ESN)) {
-               sg_init_table(asg, 3);
-               sg_set_buf(asg, &esph->spi, sizeof(__be32));
-               *seqhi = htonl(XFRM_SKB_CB(skb)->seq.output.hi);
-               sg_set_buf(asg + 1, seqhi, seqhilen);
-               sg_set_buf(asg + 2, &esph->seq_no, sizeof(__be32));
-       } else
-               sg_init_one(asg, esph, sizeof(*esph));
-
-       aead_givcrypt_set_callback(req, 0, esp_output_done, skb);
-       aead_givcrypt_set_crypt(req, sg, sg, clen, iv);
-       aead_givcrypt_set_assoc(req, asg, assoclen);
-       aead_givcrypt_set_giv(req, esph->enc_data,
-                             XFRM_SKB_CB(skb)->seq.output.low +
-                             ((u64)XFRM_SKB_CB(skb)->seq.output.hi << 32));
+       aead_request_set_crypt(req, sg, sg, ivlen + clen, iv);
+       aead_request_set_ad(req, assoclen);
+
+       seqno = cpu_to_be64(XFRM_SKB_CB(skb)->seq.output.low +
+                           ((u64)XFRM_SKB_CB(skb)->seq.output.hi << 32));
+
+       memset(iv, 0, ivlen);
+       memcpy(iv + ivlen - min(ivlen, 8), (u8 *)&seqno + 8 - min(ivlen, 8),
+              min(ivlen, 8));
 
        ESP_SKB_CB(skb)->tmp = tmp;
-       err = crypto_aead_givencrypt(req);
-       if (err == -EINPROGRESS)
+       err = crypto_aead_encrypt(req);
+
+       switch (err) {
+       case -EINPROGRESS:
                goto error;
 
-       if (err == -EBUSY)
+       case -EBUSY:
                err = NET_XMIT_DROP;
+               break;
+
+       case 0:
+               if ((x->props.flags & XFRM_STATE_ESN))
+                       esp_output_restore_header(skb);
+       }
 
        kfree(tmp);
 
@@ -318,25 +337,38 @@ static void esp_input_done(struct crypto_async_request *base, int err)
        xfrm_input_resume(skb, esp_input_done2(skb, err));
 }
 
+static void esp_input_restore_header(struct sk_buff *skb)
+{
+       esp_restore_header(skb, 0);
+       __skb_pull(skb, 4);
+}
+
+static void esp_input_done_esn(struct crypto_async_request *base, int err)
+{
+       struct sk_buff *skb = base->data;
+
+       esp_input_restore_header(skb);
+       esp_input_done(base, err);
+}
+
 static int esp6_input(struct xfrm_state *x, struct sk_buff *skb)
 {
        struct ip_esp_hdr *esph;
        struct crypto_aead *aead = x->data;
        struct aead_request *req;
        struct sk_buff *trailer;
-       int elen = skb->len - sizeof(*esph) - crypto_aead_ivsize(aead);
+       int ivlen = crypto_aead_ivsize(aead);
+       int elen = skb->len - sizeof(*esph) - ivlen;
        int nfrags;
        int assoclen;
-       int sglists;
        int seqhilen;
        int ret = 0;
        void *tmp;
        __be32 *seqhi;
        u8 *iv;
        struct scatterlist *sg;
-       struct scatterlist *asg;
 
-       if (!pskb_may_pull(skb, sizeof(*esph) + crypto_aead_ivsize(aead))) {
+       if (!pskb_may_pull(skb, sizeof(*esph) + ivlen)) {
                ret = -EINVAL;
                goto out;
        }
@@ -355,16 +387,14 @@ static int esp6_input(struct xfrm_state *x, struct sk_buff *skb)
        ret = -ENOMEM;
 
        assoclen = sizeof(*esph);
-       sglists = 1;
        seqhilen = 0;
 
        if (x->props.flags & XFRM_STATE_ESN) {
-               sglists += 2;
                seqhilen += sizeof(__be32);
                assoclen += seqhilen;
        }
 
-       tmp = esp_alloc_tmp(aead, nfrags + sglists, seqhilen);
+       tmp = esp_alloc_tmp(aead, nfrags, seqhilen);
        if (!tmp)
                goto out;
 
@@ -372,36 +402,39 @@ static int esp6_input(struct xfrm_state *x, struct sk_buff *skb)
        seqhi = esp_tmp_seqhi(tmp);
        iv = esp_tmp_iv(aead, tmp, seqhilen);
        req = esp_tmp_req(aead, iv);
-       asg = esp_req_sg(aead, req);
-       sg = asg + sglists;
+       sg = esp_req_sg(aead, req);
 
        skb->ip_summed = CHECKSUM_NONE;
 
        esph = (struct ip_esp_hdr *)skb->data;
 
-       /* Get ivec. This can be wrong, check against another impls. */
-       iv = esph->enc_data;
-
-       sg_init_table(sg, nfrags);
-       skb_to_sgvec(skb, sg, sizeof(*esph) + crypto_aead_ivsize(aead), elen);
+       aead_request_set_callback(req, 0, esp_input_done, skb);
 
+       /* For ESN we move the header forward by 4 bytes to
+        * accomodate the high bits.  We will move it back after
+        * decryption.
+        */
        if ((x->props.flags & XFRM_STATE_ESN)) {
-               sg_init_table(asg, 3);
-               sg_set_buf(asg, &esph->spi, sizeof(__be32));
-               *seqhi = XFRM_SKB_CB(skb)->seq.input.hi;
-               sg_set_buf(asg + 1, seqhi, seqhilen);
-               sg_set_buf(asg + 2, &esph->seq_no, sizeof(__be32));
-       } else
-               sg_init_one(asg, esph, sizeof(*esph));
+               esph = (void *)skb_push(skb, 4);
+               *seqhi = esph->spi;
+               esph->spi = esph->seq_no;
+               esph->seq_no = htonl(XFRM_SKB_CB(skb)->seq.input.hi);
+               aead_request_set_callback(req, 0, esp_input_done_esn, skb);
+       }
 
-       aead_request_set_callback(req, 0, esp_input_done, skb);
-       aead_request_set_crypt(req, sg, sg, elen, iv);
-       aead_request_set_assoc(req, asg, assoclen);
+       sg_init_table(sg, nfrags);
+       skb_to_sgvec(skb, sg, 0, skb->len);
+
+       aead_request_set_crypt(req, sg, sg, elen + ivlen, iv);
+       aead_request_set_ad(req, assoclen);
 
        ret = crypto_aead_decrypt(req);
        if (ret == -EINPROGRESS)
                goto out;
 
+       if ((x->props.flags & XFRM_STATE_ESN))
+               esp_input_restore_header(skb);
+
        ret = esp_input_done2(skb, ret);
 
 out:
@@ -461,10 +494,16 @@ static void esp6_destroy(struct xfrm_state *x)
 
 static int esp_init_aead(struct xfrm_state *x)
 {
+       char aead_name[CRYPTO_MAX_ALG_NAME];
        struct crypto_aead *aead;
        int err;
 
-       aead = crypto_alloc_aead(x->aead->alg_name, 0, 0);
+       err = -ENAMETOOLONG;
+       if (snprintf(aead_name, CRYPTO_MAX_ALG_NAME, "%s(%s)",
+                    x->geniv, x->aead->alg_name) >= CRYPTO_MAX_ALG_NAME)
+               goto error;
+
+       aead = crypto_alloc_aead(aead_name, 0, 0);
        err = PTR_ERR(aead);
        if (IS_ERR(aead))
                goto error;
@@ -503,15 +542,19 @@ static int esp_init_authenc(struct xfrm_state *x)
 
        if ((x->props.flags & XFRM_STATE_ESN)) {
                if (snprintf(authenc_name, CRYPTO_MAX_ALG_NAME,
-                            "authencesn(%s,%s)",
+                            "%s%sauthencesn(%s,%s)%s",
+                            x->geniv ?: "", x->geniv ? "(" : "",
                             x->aalg ? x->aalg->alg_name : "digest_null",
-                            x->ealg->alg_name) >= CRYPTO_MAX_ALG_NAME)
+                            x->ealg->alg_name,
+                            x->geniv ? ")" : "") >= CRYPTO_MAX_ALG_NAME)
                        goto error;
        } else {
                if (snprintf(authenc_name, CRYPTO_MAX_ALG_NAME,
-                            "authenc(%s,%s)",
+                            "%s%sauthenc(%s,%s)%s",
+                            x->geniv ?: "", x->geniv ? "(" : "",
                             x->aalg ? x->aalg->alg_name : "digest_null",
-                            x->ealg->alg_name) >= CRYPTO_MAX_ALG_NAME)
+                            x->ealg->alg_name,
+                            x->geniv ? ")" : "") >= CRYPTO_MAX_ALG_NAME)
                        goto error;
        }
 
index f0d52d721b3a4405b47f38d8f5d6c1990cbf8b5f..3c5b8ce38ef405bf07636adbe3799bfec3b673f0 100644 (file)
@@ -1190,6 +1190,7 @@ static struct xfrm_state * pfkey_msg2xfrm_state(struct net *net,
                                memcpy(x->ealg->alg_key, key+1, keysize);
                        }
                        x->props.ealgo = sa->sadb_sa_encrypt;
+                       x->geniv = a->uinfo.encr.geniv;
                }
        }
        /* x->algo.flags = sa->sadb_sa_flags; */
index 208df7c0b6eaf432343e83aef3914f26a66f251e..7663c28ba3539f230c9cc5b1bd044164f1ef561c 100644 (file)
@@ -11,9 +11,8 @@
 
 #include <linux/kernel.h>
 #include <linux/types.h>
-#include <linux/crypto.h>
 #include <linux/err.h>
-#include <crypto/aes.h>
+#include <crypto/aead.h>
 
 #include <net/mac80211.h>
 #include "key.h"
@@ -23,7 +22,7 @@ void ieee80211_aes_ccm_encrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad,
                               u8 *data, size_t data_len, u8 *mic,
                               size_t mic_len)
 {
-       struct scatterlist assoc, pt, ct[2];
+       struct scatterlist sg[3];
 
        char aead_req_data[sizeof(struct aead_request) +
                           crypto_aead_reqsize(tfm)]
@@ -32,15 +31,14 @@ void ieee80211_aes_ccm_encrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad,
 
        memset(aead_req, 0, sizeof(aead_req_data));
 
-       sg_init_one(&pt, data, data_len);
-       sg_init_one(&assoc, &aad[2], be16_to_cpup((__be16 *)aad));
-       sg_init_table(ct, 2);
-       sg_set_buf(&ct[0], data, data_len);
-       sg_set_buf(&ct[1], mic, mic_len);
+       sg_init_table(sg, 3);
+       sg_set_buf(&sg[0], &aad[2], be16_to_cpup((__be16 *)aad));
+       sg_set_buf(&sg[1], data, data_len);
+       sg_set_buf(&sg[2], mic, mic_len);
 
        aead_request_set_tfm(aead_req, tfm);
-       aead_request_set_assoc(aead_req, &assoc, assoc.length);
-       aead_request_set_crypt(aead_req, &pt, ct, data_len, b_0);
+       aead_request_set_crypt(aead_req, sg, sg, data_len, b_0);
+       aead_request_set_ad(aead_req, sg[0].length);
 
        crypto_aead_encrypt(aead_req);
 }
@@ -49,7 +47,7 @@ int ieee80211_aes_ccm_decrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad,
                              u8 *data, size_t data_len, u8 *mic,
                              size_t mic_len)
 {
-       struct scatterlist assoc, pt, ct[2];
+       struct scatterlist sg[3];
        char aead_req_data[sizeof(struct aead_request) +
                           crypto_aead_reqsize(tfm)]
                __aligned(__alignof__(struct aead_request));
@@ -60,15 +58,14 @@ int ieee80211_aes_ccm_decrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad,
 
        memset(aead_req, 0, sizeof(aead_req_data));
 
-       sg_init_one(&pt, data, data_len);
-       sg_init_one(&assoc, &aad[2], be16_to_cpup((__be16 *)aad));
-       sg_init_table(ct, 2);
-       sg_set_buf(&ct[0], data, data_len);
-       sg_set_buf(&ct[1], mic, mic_len);
+       sg_init_table(sg, 3);
+       sg_set_buf(&sg[0], &aad[2], be16_to_cpup((__be16 *)aad));
+       sg_set_buf(&sg[1], data, data_len);
+       sg_set_buf(&sg[2], mic, mic_len);
 
        aead_request_set_tfm(aead_req, tfm);
-       aead_request_set_assoc(aead_req, &assoc, assoc.length);
-       aead_request_set_crypt(aead_req, ct, &pt, data_len + mic_len, b_0);
+       aead_request_set_crypt(aead_req, sg, sg, data_len + mic_len, b_0);
+       aead_request_set_ad(aead_req, sg[0].length);
 
        return crypto_aead_decrypt(aead_req);
 }
index fd278bbe1b0db49ef825a025f11488eec7014daa..3afe361fd27ca5ef5ac1648106fbe010520fb377 100644 (file)
@@ -8,9 +8,8 @@
 
 #include <linux/kernel.h>
 #include <linux/types.h>
-#include <linux/crypto.h>
 #include <linux/err.h>
-#include <crypto/aes.h>
+#include <crypto/aead.h>
 
 #include <net/mac80211.h>
 #include "key.h"
@@ -19,7 +18,7 @@
 void ieee80211_aes_gcm_encrypt(struct crypto_aead *tfm, u8 *j_0, u8 *aad,
                               u8 *data, size_t data_len, u8 *mic)
 {
-       struct scatterlist assoc, pt, ct[2];
+       struct scatterlist sg[3];
 
        char aead_req_data[sizeof(struct aead_request) +
                           crypto_aead_reqsize(tfm)]
@@ -28,15 +27,14 @@ void ieee80211_aes_gcm_encrypt(struct crypto_aead *tfm, u8 *j_0, u8 *aad,
 
        memset(aead_req, 0, sizeof(aead_req_data));
 
-       sg_init_one(&pt, data, data_len);
-       sg_init_one(&assoc, &aad[2], be16_to_cpup((__be16 *)aad));
-       sg_init_table(ct, 2);
-       sg_set_buf(&ct[0], data, data_len);
-       sg_set_buf(&ct[1], mic, IEEE80211_GCMP_MIC_LEN);
+       sg_init_table(sg, 3);
+       sg_set_buf(&sg[0], &aad[2], be16_to_cpup((__be16 *)aad));
+       sg_set_buf(&sg[1], data, data_len);
+       sg_set_buf(&sg[2], mic, IEEE80211_GCMP_MIC_LEN);
 
        aead_request_set_tfm(aead_req, tfm);
-       aead_request_set_assoc(aead_req, &assoc, assoc.length);
-       aead_request_set_crypt(aead_req, &pt, ct, data_len, j_0);
+       aead_request_set_crypt(aead_req, sg, sg, data_len, j_0);
+       aead_request_set_ad(aead_req, sg[0].length);
 
        crypto_aead_encrypt(aead_req);
 }
@@ -44,7 +42,7 @@ void ieee80211_aes_gcm_encrypt(struct crypto_aead *tfm, u8 *j_0, u8 *aad,
 int ieee80211_aes_gcm_decrypt(struct crypto_aead *tfm, u8 *j_0, u8 *aad,
                              u8 *data, size_t data_len, u8 *mic)
 {
-       struct scatterlist assoc, pt, ct[2];
+       struct scatterlist sg[3];
        char aead_req_data[sizeof(struct aead_request) +
                           crypto_aead_reqsize(tfm)]
                __aligned(__alignof__(struct aead_request));
@@ -55,16 +53,15 @@ int ieee80211_aes_gcm_decrypt(struct crypto_aead *tfm, u8 *j_0, u8 *aad,
 
        memset(aead_req, 0, sizeof(aead_req_data));
 
-       sg_init_one(&pt, data, data_len);
-       sg_init_one(&assoc, &aad[2], be16_to_cpup((__be16 *)aad));
-       sg_init_table(ct, 2);
-       sg_set_buf(&ct[0], data, data_len);
-       sg_set_buf(&ct[1], mic, IEEE80211_GCMP_MIC_LEN);
+       sg_init_table(sg, 3);
+       sg_set_buf(&sg[0], &aad[2], be16_to_cpup((__be16 *)aad));
+       sg_set_buf(&sg[1], data, data_len);
+       sg_set_buf(&sg[2], mic, IEEE80211_GCMP_MIC_LEN);
 
        aead_request_set_tfm(aead_req, tfm);
-       aead_request_set_assoc(aead_req, &assoc, assoc.length);
-       aead_request_set_crypt(aead_req, ct, &pt,
+       aead_request_set_crypt(aead_req, sg, sg,
                               data_len + IEEE80211_GCMP_MIC_LEN, j_0);
+       aead_request_set_ad(aead_req, sg[0].length);
 
        return crypto_aead_decrypt(aead_req);
 }
index f1321b7d650675b725b3e96bdbf7611d3d0b1b42..3ddd927aaf306acf98a82651a39392a50930e339 100644 (file)
@@ -9,8 +9,8 @@
 
 #include <linux/kernel.h>
 #include <linux/types.h>
-#include <linux/crypto.h>
 #include <linux/err.h>
+#include <crypto/aead.h>
 #include <crypto/aes.h>
 
 #include <net/mac80211.h>
@@ -24,7 +24,7 @@
 int ieee80211_aes_gmac(struct crypto_aead *tfm, const u8 *aad, u8 *nonce,
                       const u8 *data, size_t data_len, u8 *mic)
 {
-       struct scatterlist sg[3], ct[1];
+       struct scatterlist sg[4];
        char aead_req_data[sizeof(struct aead_request) +
                           crypto_aead_reqsize(tfm)]
                __aligned(__alignof__(struct aead_request));
@@ -37,21 +37,19 @@ int ieee80211_aes_gmac(struct crypto_aead *tfm, const u8 *aad, u8 *nonce,
        memset(aead_req, 0, sizeof(aead_req_data));
 
        memset(zero, 0, GMAC_MIC_LEN);
-       sg_init_table(sg, 3);
+       sg_init_table(sg, 4);
        sg_set_buf(&sg[0], aad, AAD_LEN);
        sg_set_buf(&sg[1], data, data_len - GMAC_MIC_LEN);
        sg_set_buf(&sg[2], zero, GMAC_MIC_LEN);
+       sg_set_buf(&sg[3], mic, GMAC_MIC_LEN);
 
        memcpy(iv, nonce, GMAC_NONCE_LEN);
        memset(iv + GMAC_NONCE_LEN, 0, sizeof(iv) - GMAC_NONCE_LEN);
        iv[AES_BLOCK_SIZE - 1] = 0x01;
 
-       sg_init_table(ct, 1);
-       sg_set_buf(&ct[0], mic, GMAC_MIC_LEN);
-
        aead_request_set_tfm(aead_req, tfm);
-       aead_request_set_assoc(aead_req, sg, AAD_LEN + data_len);
-       aead_request_set_crypt(aead_req, NULL, ct, 0, iv);
+       aead_request_set_crypt(aead_req, sg, sg, 0, iv);
+       aead_request_set_ad(aead_req, AAD_LEN + data_len);
 
        crypto_aead_encrypt(aead_req);
 
index 5b2be12832e65fca351dec5a61619c1042de9004..985e9394e2afa97c069a9c565859a04c7a2cff51 100644 (file)
@@ -17,8 +17,9 @@
 #include <linux/err.h>
 #include <linux/bug.h>
 #include <linux/completion.h>
+#include <linux/crypto.h>
 #include <linux/ieee802154.h>
-#include <crypto/algapi.h>
+#include <crypto/aead.h>
 
 #include "ieee802154_i.h"
 #include "llsec.h"
@@ -649,7 +650,7 @@ llsec_do_encrypt_auth(struct sk_buff *skb, const struct mac802154_llsec *sec,
        u8 iv[16];
        unsigned char *data;
        int authlen, assoclen, datalen, rc;
-       struct scatterlist src, assoc[2], dst[2];
+       struct scatterlist sg;
        struct aead_request *req;
 
        authlen = ieee802154_sechdr_authtag_len(&hdr->sec);
@@ -659,30 +660,23 @@ llsec_do_encrypt_auth(struct sk_buff *skb, const struct mac802154_llsec *sec,
        if (!req)
                return -ENOMEM;
 
-       sg_init_table(assoc, 2);
-       sg_set_buf(&assoc[0], skb_mac_header(skb), skb->mac_len);
        assoclen = skb->mac_len;
 
        data = skb_mac_header(skb) + skb->mac_len;
        datalen = skb_tail_pointer(skb) - data;
 
-       if (hdr->sec.level & IEEE802154_SCF_SECLEVEL_ENC) {
-               sg_set_buf(&assoc[1], data, 0);
-       } else {
-               sg_set_buf(&assoc[1], data, datalen);
+       skb_put(skb, authlen);
+
+       sg_init_one(&sg, skb_mac_header(skb), assoclen + datalen + authlen);
+
+       if (!(hdr->sec.level & IEEE802154_SCF_SECLEVEL_ENC)) {
                assoclen += datalen;
                datalen = 0;
        }
 
-       sg_init_one(&src, data, datalen);
-
-       sg_init_table(dst, 2);
-       sg_set_buf(&dst[0], data, datalen);
-       sg_set_buf(&dst[1], skb_put(skb, authlen), authlen);
-
        aead_request_set_callback(req, 0, NULL, NULL);
-       aead_request_set_assoc(req, assoc, assoclen);
-       aead_request_set_crypt(req, &src, dst, datalen, iv);
+       aead_request_set_crypt(req, &sg, &sg, datalen, iv);
+       aead_request_set_ad(req, assoclen);
 
        rc = crypto_aead_encrypt(req);
 
@@ -858,7 +852,7 @@ llsec_do_decrypt_auth(struct sk_buff *skb, const struct mac802154_llsec *sec,
        u8 iv[16];
        unsigned char *data;
        int authlen, datalen, assoclen, rc;
-       struct scatterlist src, assoc[2];
+       struct scatterlist sg;
        struct aead_request *req;
 
        authlen = ieee802154_sechdr_authtag_len(&hdr->sec);
@@ -868,27 +862,21 @@ llsec_do_decrypt_auth(struct sk_buff *skb, const struct mac802154_llsec *sec,
        if (!req)
                return -ENOMEM;
 
-       sg_init_table(assoc, 2);
-       sg_set_buf(&assoc[0], skb_mac_header(skb), skb->mac_len);
        assoclen = skb->mac_len;
 
        data = skb_mac_header(skb) + skb->mac_len;
        datalen = skb_tail_pointer(skb) - data;
 
-       if (hdr->sec.level & IEEE802154_SCF_SECLEVEL_ENC) {
-               sg_set_buf(&assoc[1], data, 0);
-       } else {
-               sg_set_buf(&assoc[1], data, datalen - authlen);
+       sg_init_one(&sg, skb_mac_header(skb), assoclen + datalen);
+
+       if (!(hdr->sec.level & IEEE802154_SCF_SECLEVEL_ENC)) {
                assoclen += datalen - authlen;
-               data += datalen - authlen;
                datalen = authlen;
        }
 
-       sg_init_one(&src, data, datalen);
-
        aead_request_set_callback(req, 0, NULL, NULL);
-       aead_request_set_assoc(req, assoc, assoclen);
-       aead_request_set_crypt(req, &src, &src, datalen, iv);
+       aead_request_set_crypt(req, &sg, &sg, datalen, iv);
+       aead_request_set_ad(req, assoclen);
 
        rc = crypto_aead_decrypt(req);
 
index 7b3f732269e43bb33dc1a6584eaa91b74eab9b64..1f93a5978f2ad43fc81a16427e34d07ca2c0f34e 100644 (file)
@@ -541,7 +541,7 @@ static void mpls_ifdown(struct net_device *dev)
 
        RCU_INIT_POINTER(dev->mpls_ptr, NULL);
 
-       kfree(mdev);
+       kfree_rcu(mdev, rcu);
 }
 
 static int mpls_dev_notify(struct notifier_block *this, unsigned long event,
@@ -564,6 +564,17 @@ static int mpls_dev_notify(struct notifier_block *this, unsigned long event,
        case NETDEV_UNREGISTER:
                mpls_ifdown(dev);
                break;
+       case NETDEV_CHANGENAME:
+               mdev = mpls_dev_get(dev);
+               if (mdev) {
+                       int err;
+
+                       mpls_dev_sysctl_unregister(mdev);
+                       err = mpls_dev_sysctl_register(dev, mdev);
+                       if (err)
+                               return notifier_from_errno(err);
+               }
+               break;
        }
        return NOTIFY_OK;
 }
index b064c345042c17ccd9ec841535857fb29041a8a3..8cabeb5a1cb928c856c037c5994116df8547fb71 100644 (file)
@@ -16,6 +16,7 @@ struct mpls_dev {
        int                     input_enabled;
 
        struct ctl_table_header *sysctl;
+       struct rcu_head         rcu;
 };
 
 struct sk_buff;
index 4776282c64175209924740fbd87a56de8e05b609..33e6d6e2908f553516c5ca97c4b93abee7b7057b 100644 (file)
@@ -125,6 +125,7 @@ static struct vport *netdev_create(const struct vport_parms *parms)
        if (err)
                goto error_master_upper_dev_unlink;
 
+       dev_disable_lro(netdev_vport->dev);
        dev_set_promiscuity(netdev_vport->dev, 1);
        netdev_vport->dev->priv_flags |= IFF_OVS_DATAPATH;
        rtnl_unlock();
index 10443377fb9d8f5b5cb928647fa58c03001a072b..11b623c2840c67c7298544843b00ee7480ce257f 100644 (file)
 
 #include "rds.h"
 
-char *rds_str_array(char **array, size_t elements, size_t index)
-{
-       if ((index < elements) && array[index])
-               return array[index];
-       else
-               return "unknown";
-}
-EXPORT_SYMBOL(rds_str_array);
-
 /* this is just used for stats gathering :/ */
 static DEFINE_SPINLOCK(rds_sock_lock);
 static unsigned long rds_sock_count;
index c36d713229e0f5c5a1b43fe227a1e04480ba100d..333611d9e07d0460f6a0c99a6e22358f798022c4 100644 (file)
@@ -339,7 +339,6 @@ u32 rds_ib_ring_completed(struct rds_ib_work_ring *ring, u32 wr_id, u32 oldest);
 extern wait_queue_head_t rds_ib_ring_empty_wait;
 
 /* ib_send.c */
-char *rds_ib_wc_status_str(enum ib_wc_status status);
 void rds_ib_xmit_complete(struct rds_connection *conn);
 int rds_ib_xmit(struct rds_connection *conn, struct rds_message *rm,
                unsigned int hdr_off, unsigned int sg, unsigned int off);
index 8a09ee7db3c13bdd833784c4ee311e048a7c2789..0da2a45b33bd8ee5df6915e253440d3bc65081fc 100644 (file)
 #include "rds.h"
 #include "ib.h"
 
-static char *rds_ib_event_type_strings[] = {
-#define RDS_IB_EVENT_STRING(foo) \
-               [IB_EVENT_##foo] = __stringify(IB_EVENT_##foo)
-       RDS_IB_EVENT_STRING(CQ_ERR),
-       RDS_IB_EVENT_STRING(QP_FATAL),
-       RDS_IB_EVENT_STRING(QP_REQ_ERR),
-       RDS_IB_EVENT_STRING(QP_ACCESS_ERR),
-       RDS_IB_EVENT_STRING(COMM_EST),
-       RDS_IB_EVENT_STRING(SQ_DRAINED),
-       RDS_IB_EVENT_STRING(PATH_MIG),
-       RDS_IB_EVENT_STRING(PATH_MIG_ERR),
-       RDS_IB_EVENT_STRING(DEVICE_FATAL),
-       RDS_IB_EVENT_STRING(PORT_ACTIVE),
-       RDS_IB_EVENT_STRING(PORT_ERR),
-       RDS_IB_EVENT_STRING(LID_CHANGE),
-       RDS_IB_EVENT_STRING(PKEY_CHANGE),
-       RDS_IB_EVENT_STRING(SM_CHANGE),
-       RDS_IB_EVENT_STRING(SRQ_ERR),
-       RDS_IB_EVENT_STRING(SRQ_LIMIT_REACHED),
-       RDS_IB_EVENT_STRING(QP_LAST_WQE_REACHED),
-       RDS_IB_EVENT_STRING(CLIENT_REREGISTER),
-#undef RDS_IB_EVENT_STRING
-};
-
-static char *rds_ib_event_str(enum ib_event_type type)
-{
-       return rds_str_array(rds_ib_event_type_strings,
-                            ARRAY_SIZE(rds_ib_event_type_strings), type);
-};
-
 /*
  * Set the selected protocol version
  */
@@ -243,7 +213,7 @@ static void rds_ib_cm_fill_conn_param(struct rds_connection *conn,
 static void rds_ib_cq_event_handler(struct ib_event *event, void *data)
 {
        rdsdebug("event %u (%s) data %p\n",
-                event->event, rds_ib_event_str(event->event), data);
+                event->event, ib_event_msg(event->event), data);
 }
 
 static void rds_ib_qp_event_handler(struct ib_event *event, void *data)
@@ -252,7 +222,7 @@ static void rds_ib_qp_event_handler(struct ib_event *event, void *data)
        struct rds_ib_connection *ic = conn->c_transport_data;
 
        rdsdebug("conn %p ic %p event %u (%s)\n", conn, ic, event->event,
-                rds_ib_event_str(event->event));
+                ib_event_msg(event->event));
 
        switch (event->event) {
        case IB_EVENT_COMM_EST:
@@ -261,7 +231,7 @@ static void rds_ib_qp_event_handler(struct ib_event *event, void *data)
        default:
                rdsdebug("Fatal QP Event %u (%s) "
                        "- connection %pI4->%pI4, reconnecting\n",
-                       event->event, rds_ib_event_str(event->event),
+                       event->event, ib_event_msg(event->event),
                        &conn->c_laddr, &conn->c_faddr);
                rds_conn_drop(conn);
                break;
@@ -277,6 +247,7 @@ static int rds_ib_setup_qp(struct rds_connection *conn)
        struct rds_ib_connection *ic = conn->c_transport_data;
        struct ib_device *dev = ic->i_cm_id->device;
        struct ib_qp_init_attr attr;
+       struct ib_cq_init_attr cq_attr = {};
        struct rds_ib_device *rds_ibdev;
        int ret;
 
@@ -300,9 +271,10 @@ static int rds_ib_setup_qp(struct rds_connection *conn)
        ic->i_pd = rds_ibdev->pd;
        ic->i_mr = rds_ibdev->mr;
 
+       cq_attr.cqe = ic->i_send_ring.w_nr + 1;
        ic->i_send_cq = ib_create_cq(dev, rds_ib_send_cq_comp_handler,
                                     rds_ib_cq_event_handler, conn,
-                                    ic->i_send_ring.w_nr + 1, 0);
+                                    &cq_attr);
        if (IS_ERR(ic->i_send_cq)) {
                ret = PTR_ERR(ic->i_send_cq);
                ic->i_send_cq = NULL;
@@ -310,9 +282,10 @@ static int rds_ib_setup_qp(struct rds_connection *conn)
                goto out;
        }
 
+       cq_attr.cqe = ic->i_recv_ring.w_nr;
        ic->i_recv_cq = ib_create_cq(dev, rds_ib_recv_cq_comp_handler,
                                     rds_ib_cq_event_handler, conn,
-                                    ic->i_recv_ring.w_nr, 0);
+                                    &cq_attr);
        if (IS_ERR(ic->i_recv_cq)) {
                ret = PTR_ERR(ic->i_recv_cq);
                ic->i_recv_cq = NULL;
index 1b981a4e42c214d575a838b096da368a7f0316c6..cac5b4506ee387053d0baf64b1c588cbab8e0f5e 100644 (file)
@@ -956,7 +956,7 @@ static inline void rds_poll_cq(struct rds_ib_connection *ic,
        while (ib_poll_cq(ic->i_recv_cq, 1, &wc) > 0) {
                rdsdebug("wc wr_id 0x%llx status %u (%s) byte_len %u imm_data %u\n",
                         (unsigned long long)wc.wr_id, wc.status,
-                        rds_ib_wc_status_str(wc.status), wc.byte_len,
+                        ib_wc_status_msg(wc.status), wc.byte_len,
                         be32_to_cpu(wc.ex.imm_data));
                rds_ib_stats_inc(s_ib_rx_cq_event);
 
@@ -978,7 +978,7 @@ static inline void rds_poll_cq(struct rds_ib_connection *ic,
                                                  "status %u (%s), disconnecting and "
                                                  "reconnecting\n", &conn->c_faddr,
                                                  wc.status,
-                                                 rds_ib_wc_status_str(wc.status));
+                                                 ib_wc_status_msg(wc.status));
                }
 
                /*
index bd3825d38abc923bd905b6af266b4fffe706f427..5d0a704fa039f9859d588f6136a4515d98d71c8b 100644 (file)
 #include "rds.h"
 #include "ib.h"
 
-static char *rds_ib_wc_status_strings[] = {
-#define RDS_IB_WC_STATUS_STR(foo) \
-               [IB_WC_##foo] = __stringify(IB_WC_##foo)
-       RDS_IB_WC_STATUS_STR(SUCCESS),
-       RDS_IB_WC_STATUS_STR(LOC_LEN_ERR),
-       RDS_IB_WC_STATUS_STR(LOC_QP_OP_ERR),
-       RDS_IB_WC_STATUS_STR(LOC_EEC_OP_ERR),
-       RDS_IB_WC_STATUS_STR(LOC_PROT_ERR),
-       RDS_IB_WC_STATUS_STR(WR_FLUSH_ERR),
-       RDS_IB_WC_STATUS_STR(MW_BIND_ERR),
-       RDS_IB_WC_STATUS_STR(BAD_RESP_ERR),
-       RDS_IB_WC_STATUS_STR(LOC_ACCESS_ERR),
-       RDS_IB_WC_STATUS_STR(REM_INV_REQ_ERR),
-       RDS_IB_WC_STATUS_STR(REM_ACCESS_ERR),
-       RDS_IB_WC_STATUS_STR(REM_OP_ERR),
-       RDS_IB_WC_STATUS_STR(RETRY_EXC_ERR),
-       RDS_IB_WC_STATUS_STR(RNR_RETRY_EXC_ERR),
-       RDS_IB_WC_STATUS_STR(LOC_RDD_VIOL_ERR),
-       RDS_IB_WC_STATUS_STR(REM_INV_RD_REQ_ERR),
-       RDS_IB_WC_STATUS_STR(REM_ABORT_ERR),
-       RDS_IB_WC_STATUS_STR(INV_EECN_ERR),
-       RDS_IB_WC_STATUS_STR(INV_EEC_STATE_ERR),
-       RDS_IB_WC_STATUS_STR(FATAL_ERR),
-       RDS_IB_WC_STATUS_STR(RESP_TIMEOUT_ERR),
-       RDS_IB_WC_STATUS_STR(GENERAL_ERR),
-#undef RDS_IB_WC_STATUS_STR
-};
-
-char *rds_ib_wc_status_str(enum ib_wc_status status)
-{
-       return rds_str_array(rds_ib_wc_status_strings,
-                            ARRAY_SIZE(rds_ib_wc_status_strings), status);
-}
-
 /*
  * Convert IB-specific error message to RDS error message and call core
  * completion handler.
@@ -293,7 +259,7 @@ void rds_ib_send_cq_comp_handler(struct ib_cq *cq, void *context)
        while (ib_poll_cq(cq, 1, &wc) > 0) {
                rdsdebug("wc wr_id 0x%llx status %u (%s) byte_len %u imm_data %u\n",
                         (unsigned long long)wc.wr_id, wc.status,
-                        rds_ib_wc_status_str(wc.status), wc.byte_len,
+                        ib_wc_status_msg(wc.status), wc.byte_len,
                         be32_to_cpu(wc.ex.imm_data));
                rds_ib_stats_inc(s_ib_tx_cq_event);
 
@@ -344,7 +310,7 @@ void rds_ib_send_cq_comp_handler(struct ib_cq *cq, void *context)
                        rds_ib_conn_error(conn, "send completion on %pI4 had status "
                                          "%u (%s), disconnecting and reconnecting\n",
                                          &conn->c_faddr, wc.status,
-                                         rds_ib_wc_status_str(wc.status));
+                                         ib_wc_status_msg(wc.status));
                }
        }
 }
@@ -605,6 +571,8 @@ int rds_ib_xmit(struct rds_connection *conn, struct rds_message *rm,
                }
 
                rds_message_addref(rm);
+               rm->data.op_dmasg = 0;
+               rm->data.op_dmaoff = 0;
                ic->i_data_op = &rm->data;
 
                /* Finalize the header */
@@ -658,7 +626,7 @@ int rds_ib_xmit(struct rds_connection *conn, struct rds_message *rm,
        send = &ic->i_sends[pos];
        first = send;
        prev = NULL;
-       scat = &ic->i_data_op->op_sg[sg];
+       scat = &ic->i_data_op->op_sg[rm->data.op_dmasg];
        i = 0;
        do {
                unsigned int len = 0;
@@ -680,17 +648,20 @@ int rds_ib_xmit(struct rds_connection *conn, struct rds_message *rm,
                /* Set up the data, if present */
                if (i < work_alloc
                    && scat != &rm->data.op_sg[rm->data.op_count]) {
-                       len = min(RDS_FRAG_SIZE, ib_sg_dma_len(dev, scat) - off);
+                       len = min(RDS_FRAG_SIZE,
+                               ib_sg_dma_len(dev, scat) - rm->data.op_dmaoff);
                        send->s_wr.num_sge = 2;
 
-                       send->s_sge[1].addr = ib_sg_dma_address(dev, scat) + off;
+                       send->s_sge[1].addr = ib_sg_dma_address(dev, scat);
+                       send->s_sge[1].addr += rm->data.op_dmaoff;
                        send->s_sge[1].length = len;
 
                        bytes_sent += len;
-                       off += len;
-                       if (off == ib_sg_dma_len(dev, scat)) {
+                       rm->data.op_dmaoff += len;
+                       if (rm->data.op_dmaoff == ib_sg_dma_len(dev, scat)) {
                                scat++;
-                               off = 0;
+                               rm->data.op_dmasg++;
+                               rm->data.op_dmaoff = 0;
                        }
                }
 
index a6c2bea9f8f9b37b46ce381336a90fb685187083..8f486fa3207901895e5184f50eb55563b6628e30 100644 (file)
@@ -179,6 +179,7 @@ static int rds_iw_init_qp_attrs(struct ib_qp_init_attr *attr,
                void *context)
 {
        struct ib_device *dev = rds_iwdev->dev;
+       struct ib_cq_init_attr cq_attr = {};
        unsigned int send_size, recv_size;
        int ret;
 
@@ -198,9 +199,10 @@ static int rds_iw_init_qp_attrs(struct ib_qp_init_attr *attr,
        attr->sq_sig_type = IB_SIGNAL_REQ_WR;
        attr->qp_type = IB_QPT_RC;
 
+       cq_attr.cqe = send_size;
        attr->send_cq = ib_create_cq(dev, send_cq_handler,
                                     rds_iw_cq_event_handler,
-                                    context, send_size, 0);
+                                    context, &cq_attr);
        if (IS_ERR(attr->send_cq)) {
                ret = PTR_ERR(attr->send_cq);
                attr->send_cq = NULL;
@@ -208,9 +210,10 @@ static int rds_iw_init_qp_attrs(struct ib_qp_init_attr *attr,
                goto out;
        }
 
+       cq_attr.cqe = recv_size;
        attr->recv_cq = ib_create_cq(dev, recv_cq_handler,
                                     rds_iw_cq_event_handler,
-                                    context, recv_size, 0);
+                                    context, &cq_attr);
        if (IS_ERR(attr->recv_cq)) {
                ret = PTR_ERR(attr->recv_cq);
                attr->recv_cq = NULL;
index 13834780a3089e9e640e470f7b2e8b26c6334b7b..334fe98c50841fed6f448cdb05eb01a5388abaee 100644 (file)
@@ -581,6 +581,8 @@ int rds_iw_xmit(struct rds_connection *conn, struct rds_message *rm,
                ic->i_unsignaled_wrs = rds_iw_sysctl_max_unsig_wrs;
                ic->i_unsignaled_bytes = rds_iw_sysctl_max_unsig_bytes;
                rds_message_addref(rm);
+               rm->data.op_dmasg = 0;
+               rm->data.op_dmaoff = 0;
                ic->i_rm = rm;
 
                /* Finalize the header */
@@ -622,7 +624,7 @@ int rds_iw_xmit(struct rds_connection *conn, struct rds_message *rm,
        send = &ic->i_sends[pos];
        first = send;
        prev = NULL;
-       scat = &rm->data.op_sg[sg];
+       scat = &rm->data.op_sg[rm->data.op_dmasg];
        sent = 0;
        i = 0;
 
@@ -656,10 +658,11 @@ int rds_iw_xmit(struct rds_connection *conn, struct rds_message *rm,
 
                send = &ic->i_sends[pos];
 
-               len = min(RDS_FRAG_SIZE, ib_sg_dma_len(dev, scat) - off);
+               len = min(RDS_FRAG_SIZE,
+                         ib_sg_dma_len(dev, scat) - rm->data.op_dmaoff);
                rds_iw_xmit_populate_wr(ic, send, pos,
-                               ib_sg_dma_address(dev, scat) + off, len,
-                               send_flags);
+                       ib_sg_dma_address(dev, scat) + rm->data.op_dmaoff, len,
+                       send_flags);
 
                /*
                 * We want to delay signaling completions just enough to get
@@ -687,10 +690,11 @@ int rds_iw_xmit(struct rds_connection *conn, struct rds_message *rm,
                         &send->s_wr, send->s_wr.num_sge, send->s_wr.next);
 
                sent += len;
-               off += len;
-               if (off == ib_sg_dma_len(dev, scat)) {
+               rm->data.op_dmaoff += len;
+               if (rm->data.op_dmaoff == ib_sg_dma_len(dev, scat)) {
                        scat++;
-                       off = 0;
+                       rm->data.op_dmaoff = 0;
+                       rm->data.op_dmasg++;
                }
 
 add_header:
index 6cd9d1deafc395d6573b7e3b801e666106d1f257..20824083604328fe01359be1818673418490f43b 100644 (file)
 
 static struct rdma_cm_id *rds_rdma_listen_id;
 
-static char *rds_cm_event_strings[] = {
-#define RDS_CM_EVENT_STRING(foo) \
-               [RDMA_CM_EVENT_##foo] = __stringify(RDMA_CM_EVENT_##foo)
-       RDS_CM_EVENT_STRING(ADDR_RESOLVED),
-       RDS_CM_EVENT_STRING(ADDR_ERROR),
-       RDS_CM_EVENT_STRING(ROUTE_RESOLVED),
-       RDS_CM_EVENT_STRING(ROUTE_ERROR),
-       RDS_CM_EVENT_STRING(CONNECT_REQUEST),
-       RDS_CM_EVENT_STRING(CONNECT_RESPONSE),
-       RDS_CM_EVENT_STRING(CONNECT_ERROR),
-       RDS_CM_EVENT_STRING(UNREACHABLE),
-       RDS_CM_EVENT_STRING(REJECTED),
-       RDS_CM_EVENT_STRING(ESTABLISHED),
-       RDS_CM_EVENT_STRING(DISCONNECTED),
-       RDS_CM_EVENT_STRING(DEVICE_REMOVAL),
-       RDS_CM_EVENT_STRING(MULTICAST_JOIN),
-       RDS_CM_EVENT_STRING(MULTICAST_ERROR),
-       RDS_CM_EVENT_STRING(ADDR_CHANGE),
-       RDS_CM_EVENT_STRING(TIMEWAIT_EXIT),
-#undef RDS_CM_EVENT_STRING
-};
-
-static char *rds_cm_event_str(enum rdma_cm_event_type type)
-{
-       return rds_str_array(rds_cm_event_strings,
-                            ARRAY_SIZE(rds_cm_event_strings), type);
-};
-
 int rds_rdma_cm_event_handler(struct rdma_cm_id *cm_id,
                              struct rdma_cm_event *event)
 {
@@ -74,7 +46,7 @@ int rds_rdma_cm_event_handler(struct rdma_cm_id *cm_id,
        int ret = 0;
 
        rdsdebug("conn %p id %p handling event %u (%s)\n", conn, cm_id,
-                event->event, rds_cm_event_str(event->event));
+                event->event, rdma_event_msg(event->event));
 
        if (cm_id->device->node_type == RDMA_NODE_RNIC)
                trans = &rds_iw_transport;
@@ -139,7 +111,7 @@ int rds_rdma_cm_event_handler(struct rdma_cm_id *cm_id,
        default:
                /* things like device disconnect? */
                printk(KERN_ERR "RDS: unknown event %u (%s)!\n",
-                      event->event, rds_cm_event_str(event->event));
+                      event->event, rdma_event_msg(event->event));
                break;
        }
 
@@ -148,7 +120,7 @@ out:
                mutex_unlock(&conn->c_cm_lock);
 
        rdsdebug("id %p event %u (%s) handling ret %d\n", cm_id, event->event,
-                rds_cm_event_str(event->event), ret);
+                rdma_event_msg(event->event), ret);
 
        return ret;
 }
index 0d41155a2258cbbd16e19171c3daa376e3a83877..cc549858ed0e5356028dbcf1417639d0ea072e30 100644 (file)
@@ -363,6 +363,8 @@ struct rds_message {
                        unsigned int            op_active:1;
                        unsigned int            op_nents;
                        unsigned int            op_count;
+                       unsigned int            op_dmasg;
+                       unsigned int            op_dmaoff;
                        struct scatterlist      *op_sg;
                } data;
        };
@@ -575,7 +577,6 @@ struct rds_statistics {
 };
 
 /* af_rds.c */
-char *rds_str_array(char **array, size_t elements, size_t index);
 void rds_sock_addref(struct rds_sock *rs);
 void rds_sock_put(struct rds_sock *rs);
 void rds_wake_sk_sleep(struct rds_sock *rs);
index 1e1c89e51a118e79610c49412e335191fc3ba834..73a123daa2cc5c4c43c69120d1fecd273df76c17 100644 (file)
@@ -1885,13 +1885,10 @@ EXPORT_SYMBOL(tcf_destroy_chain);
 #ifdef CONFIG_PROC_FS
 static int psched_show(struct seq_file *seq, void *v)
 {
-       struct timespec ts;
-
-       hrtimer_get_res(CLOCK_MONOTONIC, &ts);
        seq_printf(seq, "%08x %08x %08x %08x\n",
                   (u32)NSEC_PER_USEC, (u32)PSCHED_TICKS2NS(1),
                   1000000,
-                  (u32)NSEC_PER_SEC/(u32)ktime_to_ns(timespec_to_ktime(ts)));
+                  (u32)NSEC_PER_SEC / hrtimer_resolution);
 
        return 0;
 }
index fb7976aee61c84f38aecdc5c5f0d8be20e577fa9..4f15b7d730e13d6aaa58ba7a28262c9831afea95 100644 (file)
@@ -381,13 +381,14 @@ nomem:
 }
 
 
-/* Public interface to creat the association shared key.
+/* Public interface to create the association shared key.
  * See code above for the algorithm.
  */
 int sctp_auth_asoc_init_active_key(struct sctp_association *asoc, gfp_t gfp)
 {
        struct sctp_auth_bytes  *secret;
        struct sctp_shared_key *ep_key;
+       struct sctp_chunk *chunk;
 
        /* If we don't support AUTH, or peer is not capable
         * we don't need to do anything.
@@ -410,6 +411,14 @@ int sctp_auth_asoc_init_active_key(struct sctp_association *asoc, gfp_t gfp)
        sctp_auth_key_put(asoc->asoc_shared_key);
        asoc->asoc_shared_key = secret;
 
+       /* Update send queue in case any chunk already in there now
+        * needs authenticating
+        */
+       list_for_each_entry(chunk, &asoc->outqueue.out_chunk_list, list) {
+               if (sctp_auth_send_cid(chunk->chunk_hdr->type, asoc))
+                       chunk->auth = 1;
+       }
+
        return 0;
 }
 
index dff0481dbcf8011dbbdf3050dd4ff9e67f0a6df3..d234521320a4bb45a9bbecd40e5e58bbb42d77d8 100644 (file)
@@ -128,8 +128,8 @@ frwr_sendcompletion(struct ib_wc *wc)
 
        /* WARNING: Only wr_id and status are reliable at this point */
        r = (struct rpcrdma_mw *)(unsigned long)wc->wr_id;
-       dprintk("RPC:       %s: frmr %p (stale), status %d\n",
-               __func__, r, wc->status);
+       dprintk("RPC:       %s: frmr %p (stale), status %s (%d)\n",
+               __func__, r, ib_wc_status_msg(wc->status), wc->status);
        r->r.frmr.fr_state = FRMR_IS_STALE;
 }
 
index f9f13a32ddb828650c10eae59a6da2a18c40026c..86b44164172b8d4b1fe2520539409a6f3e3d7fa5 100644 (file)
@@ -117,8 +117,8 @@ static void rdma_build_arg_xdr(struct svc_rqst *rqstp,
 
 static int rdma_read_max_sge(struct svcxprt_rdma *xprt, int sge_count)
 {
-       if (rdma_node_get_transport(xprt->sc_cm_id->device->node_type) ==
-            RDMA_TRANSPORT_IWARP)
+       if (!rdma_cap_read_multi_sge(xprt->sc_cm_id->device,
+                                    xprt->sc_cm_id->port_num))
                return 1;
        else
                return min_t(int, sge_count, xprt->sc_max_sge);
index f609c1c2d38ddfbad3e77718b737dbcd0391bfcd..f4cfa764d76f95063d9db606afb81236a492a337 100644 (file)
@@ -175,8 +175,8 @@ void svc_rdma_put_req_map(struct svc_rdma_req_map *map)
 static void cq_event_handler(struct ib_event *event, void *context)
 {
        struct svc_xprt *xprt = context;
-       dprintk("svcrdma: received CQ event id=%d, context=%p\n",
-               event->event, context);
+       dprintk("svcrdma: received CQ event %s (%d), context=%p\n",
+               ib_event_msg(event->event), event->event, context);
        set_bit(XPT_CLOSE, &xprt->xpt_flags);
 }
 
@@ -191,8 +191,9 @@ static void qp_event_handler(struct ib_event *event, void *context)
        case IB_EVENT_COMM_EST:
        case IB_EVENT_SQ_DRAINED:
        case IB_EVENT_QP_LAST_WQE_REACHED:
-               dprintk("svcrdma: QP event %d received for QP=%p\n",
-                       event->event, event->element.qp);
+               dprintk("svcrdma: QP event %s (%d) received for QP=%p\n",
+                       ib_event_msg(event->event), event->event,
+                       event->element.qp);
                break;
        /* These are considered fatal events */
        case IB_EVENT_PATH_MIG_ERR:
@@ -201,9 +202,10 @@ static void qp_event_handler(struct ib_event *event, void *context)
        case IB_EVENT_QP_ACCESS_ERR:
        case IB_EVENT_DEVICE_FATAL:
        default:
-               dprintk("svcrdma: QP ERROR event %d received for QP=%p, "
+               dprintk("svcrdma: QP ERROR event %s (%d) received for QP=%p, "
                        "closing transport\n",
-                       event->event, event->element.qp);
+                       ib_event_msg(event->event), event->event,
+                       event->element.qp);
                set_bit(XPT_CLOSE, &xprt->xpt_flags);
                break;
        }
@@ -402,7 +404,8 @@ static void sq_cq_reap(struct svcxprt_rdma *xprt)
                for (i = 0; i < ret; i++) {
                        wc = &wc_a[i];
                        if (wc->status != IB_WC_SUCCESS) {
-                               dprintk("svcrdma: sq wc err status %d\n",
+                               dprintk("svcrdma: sq wc err status %s (%d)\n",
+                                       ib_wc_status_msg(wc->status),
                                        wc->status);
 
                                /* Close the transport */
@@ -616,7 +619,8 @@ static int rdma_listen_handler(struct rdma_cm_id *cma_id,
        switch (event->event) {
        case RDMA_CM_EVENT_CONNECT_REQUEST:
                dprintk("svcrdma: Connect request on cma_id=%p, xprt = %p, "
-                       "event=%d\n", cma_id, cma_id->context, event->event);
+                       "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);
                break;
@@ -636,7 +640,8 @@ static int rdma_listen_handler(struct rdma_cm_id *cma_id,
 
        default:
                dprintk("svcrdma: Unexpected event on listening endpoint %p, "
-                       "event=%d\n", cma_id, event->event);
+                       "event = %s (%d)\n", cma_id,
+                       rdma_event_msg(event->event), event->event);
                break;
        }
 
@@ -669,7 +674,8 @@ static int rdma_cma_handler(struct rdma_cm_id *cma_id,
                break;
        case RDMA_CM_EVENT_DEVICE_REMOVAL:
                dprintk("svcrdma: Device removal cma_id=%p, xprt = %p, "
-                       "event=%d\n", cma_id, xprt, event->event);
+                       "event = %s (%d)\n", cma_id, xprt,
+                       rdma_event_msg(event->event), event->event);
                if (xprt) {
                        set_bit(XPT_CLOSE, &xprt->xpt_flags);
                        svc_xprt_enqueue(xprt);
@@ -677,7 +683,8 @@ static int rdma_cma_handler(struct rdma_cm_id *cma_id,
                break;
        default:
                dprintk("svcrdma: Unexpected event on DTO endpoint %p, "
-                       "event=%d\n", cma_id, event->event);
+                       "event = %s (%d)\n", cma_id,
+                       rdma_event_msg(event->event), event->event);
                break;
        }
        return 0;
@@ -848,10 +855,11 @@ 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 ib_cq_init_attr cq_attr = {};
        struct ib_qp_init_attr qp_attr;
        struct ib_device_attr devattr;
        int uninitialized_var(dma_mr_acc);
-       int need_dma_mr;
+       int need_dma_mr = 0;
        int ret;
        int i;
 
@@ -900,22 +908,22 @@ static struct svc_xprt *svc_rdma_accept(struct svc_xprt *xprt)
                dprintk("svcrdma: error creating PD for connect request\n");
                goto errout;
        }
+       cq_attr.cqe = newxprt->sc_sq_depth;
        newxprt->sc_sq_cq = ib_create_cq(newxprt->sc_cm_id->device,
                                         sq_comp_handler,
                                         cq_event_handler,
                                         newxprt,
-                                        newxprt->sc_sq_depth,
-                                        0);
+                                        &cq_attr);
        if (IS_ERR(newxprt->sc_sq_cq)) {
                dprintk("svcrdma: error creating SQ CQ for connect request\n");
                goto errout;
        }
+       cq_attr.cqe = newxprt->sc_max_requests;
        newxprt->sc_rq_cq = ib_create_cq(newxprt->sc_cm_id->device,
                                         rq_comp_handler,
                                         cq_event_handler,
                                         newxprt,
-                                        newxprt->sc_max_requests,
-                                        0);
+                                        &cq_attr);
        if (IS_ERR(newxprt->sc_rq_cq)) {
                dprintk("svcrdma: error creating RQ CQ for connect request\n");
                goto errout;
@@ -985,35 +993,26 @@ static struct svc_xprt *svc_rdma_accept(struct svc_xprt *xprt)
        /*
         * Determine if a DMA MR is required and if so, what privs are required
         */
-       switch (rdma_node_get_transport(newxprt->sc_cm_id->device->node_type)) {
-       case RDMA_TRANSPORT_IWARP:
-               newxprt->sc_dev_caps |= SVCRDMA_DEVCAP_READ_W_INV;
-               if (!(newxprt->sc_dev_caps & SVCRDMA_DEVCAP_FAST_REG)) {
-                       need_dma_mr = 1;
-                       dma_mr_acc =
-                               (IB_ACCESS_LOCAL_WRITE |
-                                IB_ACCESS_REMOTE_WRITE);
-               } else if (!(devattr.device_cap_flags & IB_DEVICE_LOCAL_DMA_LKEY)) {
-                       need_dma_mr = 1;
-                       dma_mr_acc = IB_ACCESS_LOCAL_WRITE;
-               } else
-                       need_dma_mr = 0;
-               break;
-       case RDMA_TRANSPORT_IB:
-               if (!(newxprt->sc_dev_caps & SVCRDMA_DEVCAP_FAST_REG)) {
-                       need_dma_mr = 1;
-                       dma_mr_acc = IB_ACCESS_LOCAL_WRITE;
-               } else if (!(devattr.device_cap_flags &
-                            IB_DEVICE_LOCAL_DMA_LKEY)) {
-                       need_dma_mr = 1;
-                       dma_mr_acc = IB_ACCESS_LOCAL_WRITE;
-               } else
-                       need_dma_mr = 0;
-               break;
-       default:
+       if (!rdma_protocol_iwarp(newxprt->sc_cm_id->device,
+                                newxprt->sc_cm_id->port_num) &&
+           !rdma_ib_or_roce(newxprt->sc_cm_id->device,
+                            newxprt->sc_cm_id->port_num))
                goto errout;
+
+       if (!(newxprt->sc_dev_caps & SVCRDMA_DEVCAP_FAST_REG) ||
+           !(devattr.device_cap_flags & IB_DEVICE_LOCAL_DMA_LKEY)) {
+               need_dma_mr = 1;
+               dma_mr_acc = IB_ACCESS_LOCAL_WRITE;
+               if (rdma_protocol_iwarp(newxprt->sc_cm_id->device,
+                                       newxprt->sc_cm_id->port_num) &&
+                   !(newxprt->sc_dev_caps & SVCRDMA_DEVCAP_FAST_REG))
+                       dma_mr_acc |= IB_ACCESS_REMOTE_WRITE;
        }
 
+       if (rdma_protocol_iwarp(newxprt->sc_cm_id->device,
+                               newxprt->sc_cm_id->port_num))
+               newxprt->sc_dev_caps |= SVCRDMA_DEVCAP_READ_W_INV;
+
        /* Create the DMA MR if needed, otherwise, use the DMA LKEY */
        if (need_dma_mr) {
                /* Register all of physical memory */
index 4870d272e0067140dbdc7f16a914ee99dbabe9fc..52df265b472a9b2b79574c7d9363acba26ea5d8b 100644 (file)
@@ -105,32 +105,6 @@ rpcrdma_run_tasklet(unsigned long data)
 
 static DECLARE_TASKLET(rpcrdma_tasklet_g, rpcrdma_run_tasklet, 0UL);
 
-static const char * const async_event[] = {
-       "CQ error",
-       "QP fatal error",
-       "QP request error",
-       "QP access error",
-       "communication established",
-       "send queue drained",
-       "path migration successful",
-       "path mig error",
-       "device fatal error",
-       "port active",
-       "port error",
-       "LID change",
-       "P_key change",
-       "SM change",
-       "SRQ error",
-       "SRQ limit reached",
-       "last WQE reached",
-       "client reregister",
-       "GID change",
-};
-
-#define ASYNC_MSG(status)                                      \
-       ((status) < ARRAY_SIZE(async_event) ?                   \
-               async_event[(status)] : "unknown async error")
-
 static void
 rpcrdma_schedule_tasklet(struct list_head *sched_list)
 {
@@ -148,7 +122,7 @@ rpcrdma_qp_async_error_upcall(struct ib_event *event, void *context)
        struct rpcrdma_ep *ep = context;
 
        pr_err("RPC:       %s: %s on device %s ep %p\n",
-              __func__, ASYNC_MSG(event->event),
+              __func__, ib_event_msg(event->event),
                event->device->name, context);
        if (ep->rep_connected == 1) {
                ep->rep_connected = -EIO;
@@ -163,7 +137,7 @@ rpcrdma_cq_async_error_upcall(struct ib_event *event, void *context)
        struct rpcrdma_ep *ep = context;
 
        pr_err("RPC:       %s: %s on device %s ep %p\n",
-              __func__, ASYNC_MSG(event->event),
+              __func__, ib_event_msg(event->event),
                event->device->name, context);
        if (ep->rep_connected == 1) {
                ep->rep_connected = -EIO;
@@ -172,35 +146,6 @@ rpcrdma_cq_async_error_upcall(struct ib_event *event, void *context)
        }
 }
 
-static const char * const wc_status[] = {
-       "success",
-       "local length error",
-       "local QP operation error",
-       "local EE context operation error",
-       "local protection error",
-       "WR flushed",
-       "memory management operation error",
-       "bad response error",
-       "local access error",
-       "remote invalid request error",
-       "remote access error",
-       "remote operation error",
-       "transport retry counter exceeded",
-       "RNR retry counter exceeded",
-       "local RDD violation error",
-       "remove invalid RD request",
-       "operation aborted",
-       "invalid EE context number",
-       "invalid EE context state",
-       "fatal error",
-       "response timeout error",
-       "general error",
-};
-
-#define COMPLETION_MSG(status)                                 \
-       ((status) < ARRAY_SIZE(wc_status) ?                     \
-               wc_status[(status)] : "unexpected completion error")
-
 static void
 rpcrdma_sendcq_process_wc(struct ib_wc *wc)
 {
@@ -209,7 +154,7 @@ rpcrdma_sendcq_process_wc(struct ib_wc *wc)
                if (wc->status != IB_WC_SUCCESS &&
                    wc->status != IB_WC_WR_FLUSH_ERR)
                        pr_err("RPC:       %s: SEND: %s\n",
-                              __func__, COMPLETION_MSG(wc->status));
+                              __func__, ib_wc_status_msg(wc->status));
        } else {
                struct rpcrdma_mw *r;
 
@@ -302,7 +247,7 @@ out_schedule:
 out_fail:
        if (wc->status != IB_WC_WR_FLUSH_ERR)
                pr_err("RPC:       %s: rep %p: %s\n",
-                      __func__, rep, COMPLETION_MSG(wc->status));
+                      __func__, rep, ib_wc_status_msg(wc->status));
        rep->rr_len = ~0U;
        goto out_schedule;
 }
@@ -386,31 +331,6 @@ rpcrdma_flush_cqs(struct rpcrdma_ep *ep)
                rpcrdma_sendcq_process_wc(&wc);
 }
 
-#if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
-static const char * const conn[] = {
-       "address resolved",
-       "address error",
-       "route resolved",
-       "route error",
-       "connect request",
-       "connect response",
-       "connect error",
-       "unreachable",
-       "rejected",
-       "established",
-       "disconnected",
-       "device removal",
-       "multicast join",
-       "multicast error",
-       "address change",
-       "timewait exit",
-};
-
-#define CONNECTION_MSG(status)                                         \
-       ((status) < ARRAY_SIZE(conn) ?                                  \
-               conn[(status)] : "unrecognized connection error")
-#endif
-
 static int
 rpcrdma_conn_upcall(struct rdma_cm_id *id, struct rdma_cm_event *event)
 {
@@ -476,7 +396,7 @@ connected:
        default:
                dprintk("RPC:       %s: %pIS:%u (ep 0x%p): %s\n",
                        __func__, sap, rpc_get_port(sap), ep,
-                       CONNECTION_MSG(event->event));
+                       rdma_event_msg(event->event));
                break;
        }
 
@@ -724,6 +644,7 @@ rpcrdma_ep_create(struct rpcrdma_ep *ep, struct rpcrdma_ia *ia,
 {
        struct ib_device_attr *devattr = &ia->ri_devattr;
        struct ib_cq *sendcq, *recvcq;
+       struct ib_cq_init_attr cq_attr = {};
        int rc, err;
 
        /* check provider's send/recv wr limits */
@@ -771,9 +692,9 @@ rpcrdma_ep_create(struct rpcrdma_ep *ep, struct rpcrdma_ia *ia,
        init_waitqueue_head(&ep->rep_connect_wait);
        INIT_DELAYED_WORK(&ep->rep_connect_worker, rpcrdma_connect_worker);
 
+       cq_attr.cqe = ep->rep_attr.cap.max_send_wr + 1;
        sendcq = ib_create_cq(ia->ri_id->device, rpcrdma_sendcq_upcall,
-                                 rpcrdma_cq_async_error_upcall, ep,
-                                 ep->rep_attr.cap.max_send_wr + 1, 0);
+                                 rpcrdma_cq_async_error_upcall, ep, &cq_attr);
        if (IS_ERR(sendcq)) {
                rc = PTR_ERR(sendcq);
                dprintk("RPC:       %s: failed to create send CQ: %i\n",
@@ -788,9 +709,9 @@ rpcrdma_ep_create(struct rpcrdma_ep *ep, struct rpcrdma_ia *ia,
                goto out2;
        }
 
+       cq_attr.cqe = ep->rep_attr.cap.max_recv_wr + 1;
        recvcq = ib_create_cq(ia->ri_id->device, rpcrdma_recvcq_upcall,
-                                 rpcrdma_cq_async_error_upcall, ep,
-                                 ep->rep_attr.cap.max_recv_wr + 1, 0);
+                                 rpcrdma_cq_async_error_upcall, ep, &cq_attr);
        if (IS_ERR(recvcq)) {
                rc = PTR_ERR(recvcq);
                dprintk("RPC:       %s: failed to create recv CQ: %i\n",
index 9074b5cede38b8edd75890b684a706d96b9f71ba..f485600c4507bc152cef654ae5667a03a52d990c 100644 (file)
@@ -2142,11 +2142,17 @@ static void tipc_sk_timeout(unsigned long data)
        peer_node = tsk_peer_node(tsk);
 
        if (tsk->probing_state == TIPC_CONN_PROBING) {
-               /* Previous probe not answered -> self abort */
-               skb = tipc_msg_create(TIPC_CRITICAL_IMPORTANCE,
-                                     TIPC_CONN_MSG, SHORT_H_SIZE, 0,
-                                     own_node, peer_node, tsk->portid,
-                                     peer_port, TIPC_ERR_NO_PORT);
+               if (!sock_owned_by_user(sk)) {
+                       sk->sk_socket->state = SS_DISCONNECTING;
+                       tsk->connected = 0;
+                       tipc_node_remove_conn(sock_net(sk), tsk_peer_node(tsk),
+                                             tsk_peer_port(tsk));
+                       sk->sk_state_change(sk);
+               } else {
+                       /* Try again later */
+                       sk_reset_timer(sk, &sk->sk_timer, (HZ / 20));
+               }
+
        } else {
                skb = tipc_msg_create(CONN_MANAGER, CONN_PROBE,
                                      INT_H_SIZE, 0, peer_node, own_node,
index fff1bef6ed6d916f9019a63d708652f4ab07cddf..fd682832a0e3635d52c734871d5402d270336dc3 100644 (file)
@@ -1333,6 +1333,8 @@ static struct iw_statistics *cfg80211_wireless_stats(struct net_device *dev)
        memcpy(bssid, wdev->current_bss->pub.bssid, ETH_ALEN);
        wdev_unlock(wdev);
 
+       memset(&sinfo, 0, sizeof(sinfo));
+
        if (rdev_get_station(rdev, dev, bssid, &sinfo))
                return NULL;
 
index 12e82a5e4ad5873e716014e421be8e329587e00c..42f7c76cf853697341a6e34cf522f92927b36ad4 100644 (file)
@@ -31,6 +31,7 @@ static struct xfrm_algo_desc aead_list[] = {
 
        .uinfo = {
                .aead = {
+                       .geniv = "seqniv",
                        .icv_truncbits = 64,
                }
        },
@@ -49,6 +50,7 @@ static struct xfrm_algo_desc aead_list[] = {
 
        .uinfo = {
                .aead = {
+                       .geniv = "seqniv",
                        .icv_truncbits = 96,
                }
        },
@@ -67,6 +69,7 @@ static struct xfrm_algo_desc aead_list[] = {
 
        .uinfo = {
                .aead = {
+                       .geniv = "seqniv",
                        .icv_truncbits = 128,
                }
        },
@@ -85,6 +88,7 @@ static struct xfrm_algo_desc aead_list[] = {
 
        .uinfo = {
                .aead = {
+                       .geniv = "seqniv",
                        .icv_truncbits = 64,
                }
        },
@@ -103,6 +107,7 @@ static struct xfrm_algo_desc aead_list[] = {
 
        .uinfo = {
                .aead = {
+                       .geniv = "seqniv",
                        .icv_truncbits = 96,
                }
        },
@@ -121,6 +126,7 @@ static struct xfrm_algo_desc aead_list[] = {
 
        .uinfo = {
                .aead = {
+                       .geniv = "seqniv",
                        .icv_truncbits = 128,
                }
        },
@@ -139,6 +145,7 @@ static struct xfrm_algo_desc aead_list[] = {
 
        .uinfo = {
                .aead = {
+                       .geniv = "seqiv",
                        .icv_truncbits = 128,
                }
        },
@@ -152,6 +159,18 @@ static struct xfrm_algo_desc aead_list[] = {
                .sadb_alg_maxbits = 256
        }
 },
+{
+       .name = "rfc7539esp(chacha20,poly1305)",
+
+       .uinfo = {
+               .aead = {
+                       .geniv = "seqniv",
+                       .icv_truncbits = 128,
+               }
+       },
+
+       .pfkey_supported = 0,
+},
 };
 
 static struct xfrm_algo_desc aalg_list[] = {
@@ -353,6 +372,7 @@ static struct xfrm_algo_desc ealg_list[] = {
 
        .uinfo = {
                .encr = {
+                       .geniv = "echainiv",
                        .blockbits = 64,
                        .defkeybits = 64,
                }
@@ -373,6 +393,7 @@ static struct xfrm_algo_desc ealg_list[] = {
 
        .uinfo = {
                .encr = {
+                       .geniv = "echainiv",
                        .blockbits = 64,
                        .defkeybits = 192,
                }
@@ -393,6 +414,7 @@ static struct xfrm_algo_desc ealg_list[] = {
 
        .uinfo = {
                .encr = {
+                       .geniv = "echainiv",
                        .blockbits = 64,
                        .defkeybits = 128,
                }
@@ -413,6 +435,7 @@ static struct xfrm_algo_desc ealg_list[] = {
 
        .uinfo = {
                .encr = {
+                       .geniv = "echainiv",
                        .blockbits = 64,
                        .defkeybits = 128,
                }
@@ -433,6 +456,7 @@ static struct xfrm_algo_desc ealg_list[] = {
 
        .uinfo = {
                .encr = {
+                       .geniv = "echainiv",
                        .blockbits = 128,
                        .defkeybits = 128,
                }
@@ -453,6 +477,7 @@ static struct xfrm_algo_desc ealg_list[] = {
 
        .uinfo = {
                .encr = {
+                       .geniv = "echainiv",
                        .blockbits = 128,
                        .defkeybits = 128,
                }
@@ -473,6 +498,7 @@ static struct xfrm_algo_desc ealg_list[] = {
 
        .uinfo = {
                .encr = {
+                       .geniv = "echainiv",
                        .blockbits = 128,
                        .defkeybits = 128,
                }
@@ -493,6 +519,7 @@ static struct xfrm_algo_desc ealg_list[] = {
 
        .uinfo = {
                .encr = {
+                       .geniv = "echainiv",
                        .blockbits = 128,
                        .defkeybits = 128,
                }
@@ -512,6 +539,7 @@ static struct xfrm_algo_desc ealg_list[] = {
 
        .uinfo = {
                .encr = {
+                       .geniv = "seqiv",
                        .blockbits = 128,
                        .defkeybits = 160, /* 128-bit key + 32-bit nonce */
                }
index 2091664295bae1a3a4725287e5d0bf5cfb28357c..bd16c6c7e1e7660b8ce183c72b16eb65aa51af5b 100644 (file)
@@ -289,6 +289,31 @@ static int attach_one_algo(struct xfrm_algo **algpp, u8 *props,
        return 0;
 }
 
+static int attach_crypt(struct xfrm_state *x, struct nlattr *rta)
+{
+       struct xfrm_algo *p, *ualg;
+       struct xfrm_algo_desc *algo;
+
+       if (!rta)
+               return 0;
+
+       ualg = nla_data(rta);
+
+       algo = xfrm_ealg_get_byname(ualg->alg_name, 1);
+       if (!algo)
+               return -ENOSYS;
+       x->props.ealgo = algo->desc.sadb_alg_id;
+
+       p = kmemdup(ualg, xfrm_alg_len(ualg), GFP_KERNEL);
+       if (!p)
+               return -ENOMEM;
+
+       strcpy(p->alg_name, algo->name);
+       x->ealg = p;
+       x->geniv = algo->uinfo.encr.geniv;
+       return 0;
+}
+
 static int attach_auth(struct xfrm_algo_auth **algpp, u8 *props,
                       struct nlattr *rta)
 {
@@ -349,8 +374,7 @@ static int attach_auth_trunc(struct xfrm_algo_auth **algpp, u8 *props,
        return 0;
 }
 
-static int attach_aead(struct xfrm_algo_aead **algpp, u8 *props,
-                      struct nlattr *rta)
+static int attach_aead(struct xfrm_state *x, struct nlattr *rta)
 {
        struct xfrm_algo_aead *p, *ualg;
        struct xfrm_algo_desc *algo;
@@ -363,14 +387,15 @@ static int attach_aead(struct xfrm_algo_aead **algpp, u8 *props,
        algo = xfrm_aead_get_byname(ualg->alg_name, ualg->alg_icv_len, 1);
        if (!algo)
                return -ENOSYS;
-       *props = algo->desc.sadb_alg_id;
+       x->props.ealgo = algo->desc.sadb_alg_id;
 
        p = kmemdup(ualg, aead_len(ualg), GFP_KERNEL);
        if (!p)
                return -ENOMEM;
 
        strcpy(p->alg_name, algo->name);
-       *algpp = p;
+       x->aead = p;
+       x->geniv = algo->uinfo.aead.geniv;
        return 0;
 }
 
@@ -515,8 +540,7 @@ static struct xfrm_state *xfrm_state_construct(struct net *net,
        if (attrs[XFRMA_SA_EXTRA_FLAGS])
                x->props.extra_flags = nla_get_u32(attrs[XFRMA_SA_EXTRA_FLAGS]);
 
-       if ((err = attach_aead(&x->aead, &x->props.ealgo,
-                              attrs[XFRMA_ALG_AEAD])))
+       if ((err = attach_aead(x, attrs[XFRMA_ALG_AEAD])))
                goto error;
        if ((err = attach_auth_trunc(&x->aalg, &x->props.aalgo,
                                     attrs[XFRMA_ALG_AUTH_TRUNC])))
@@ -526,9 +550,7 @@ static struct xfrm_state *xfrm_state_construct(struct net *net,
                                       attrs[XFRMA_ALG_AUTH])))
                        goto error;
        }
-       if ((err = attach_one_algo(&x->ealg, &x->props.ealgo,
-                                  xfrm_ealg_get_byname,
-                                  attrs[XFRMA_ALG_CRYPT])))
+       if ((err = attach_crypt(x, attrs[XFRMA_ALG_CRYPT])))
                goto error;
        if ((err = attach_one_algo(&x->calg, &x->props.calgo,
                                   xfrm_calg_get_byname,
index 89b1df4e72ab3423bce45011fb03f86c193f5ad4..c5ec977b9c3786097b214e1c835efd8fa337c173 100755 (executable)
@@ -3169,12 +3169,12 @@ sub process {
                }
 
 # check for global initialisers.
-               if ($line =~ /^\+(\s*$Type\s*$Ident\s*(?:\s+$Modifier))*\s*=\s*(0|NULL|false)\s*;/) {
+               if ($line =~ /^\+$Type\s*$Ident(?:\s+$Modifier)*\s*=\s*(?:0|NULL|false)\s*;/) {
                        if (ERROR("GLOBAL_INITIALISERS",
                                  "do not initialise globals to 0 or NULL\n" .
                                      $herecurr) &&
                            $fix) {
-                               $fixed[$fixlinenr] =~ s/($Type\s*$Ident\s*(?:\s+$Modifier))*\s*=\s*(0|NULL|false)\s*;/$1;/;
+                               $fixed[$fixlinenr] =~ s/(^.$Type\s*$Ident(?:\s+$Modifier)*)\s*=\s*(0|NULL|false)\s*;/$1;/;
                        }
                }
 # check for static initialisers.
index 5b3add31f9f1202610e67e4d4868598e5846e594..2c9082ba61376fd7fb5dd375ca2afac1bb44d53a 100755 (executable)
@@ -212,5 +212,5 @@ EOF
     )
 }
 
-(ignore_list && syscall_list $(dirname $0)/../arch/x86/syscalls/syscall_32.tbl) | \
+(ignore_list && syscall_list $(dirname $0)/../arch/x86/entry/syscalls/syscall_32.tbl) | \
 $* -E -x c - > /dev/null
index 0d03fcc489a49ee3221b1369ca2c1ff931c691cd..7d3f38fe02ba6ca7d75446c6d20e41c049b17b00 100644 (file)
@@ -209,8 +209,8 @@ static int cap_inode_readlink(struct dentry *dentry)
        return 0;
 }
 
-static int cap_inode_follow_link(struct dentry *dentry,
-                                struct nameidata *nameidata)
+static int cap_inode_follow_link(struct dentry *dentry, struct inode *inode,
+                                bool rcu)
 {
        return 0;
 }
index 8e9b1f4b9b45dfac98287fe969b869a3a29fb2bc..04c8feca081a3bafc8dde4f95ebc44badda4e5a5 100644 (file)
@@ -581,11 +581,12 @@ int security_inode_readlink(struct dentry *dentry)
        return security_ops->inode_readlink(dentry);
 }
 
-int security_inode_follow_link(struct dentry *dentry, struct nameidata *nd)
+int security_inode_follow_link(struct dentry *dentry, struct inode *inode,
+                              bool rcu)
 {
-       if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
+       if (unlikely(IS_PRIVATE(inode)))
                return 0;
-       return security_ops->inode_follow_link(dentry, nd);
+       return security_ops->inode_follow_link(dentry, inode, rcu);
 }
 
 int security_inode_permission(struct inode *inode, int mask)
index 3c17dda9571d4e97f7f460e162a6195bf215758b..0b122b1421a9dcc7dfd26ac6f80d00d1c6a0d55e 100644 (file)
@@ -761,7 +761,23 @@ int avc_has_perm(u32 ssid, u32 tsid, u16 tclass,
 
        rc = avc_has_perm_noaudit(ssid, tsid, tclass, requested, 0, &avd);
 
-       rc2 = avc_audit(ssid, tsid, tclass, requested, &avd, rc, auditdata);
+       rc2 = avc_audit(ssid, tsid, tclass, requested, &avd, rc, auditdata, 0);
+       if (rc2)
+               return rc2;
+       return rc;
+}
+
+int avc_has_perm_flags(u32 ssid, u32 tsid, u16 tclass,
+                      u32 requested, struct common_audit_data *auditdata,
+                      int flags)
+{
+       struct av_decision avd;
+       int rc, rc2;
+
+       rc = avc_has_perm_noaudit(ssid, tsid, tclass, requested, 0, &avd);
+
+       rc2 = avc_audit(ssid, tsid, tclass, requested, &avd, rc,
+                       auditdata, flags);
        if (rc2)
                return rc2;
        return rc;
index 7dade28affba5a0ebc0944be49dbd59dbf5c8761..ffa5a642629a1cbf16467f7bc0ebd3b20cdf02f0 100644 (file)
@@ -1564,7 +1564,7 @@ static int cred_has_capability(const struct cred *cred,
 
        rc = avc_has_perm_noaudit(sid, sid, sclass, av, 0, &avd);
        if (audit == SECURITY_CAP_AUDIT) {
-               int rc2 = avc_audit(sid, sid, sclass, av, &avd, rc, &ad);
+               int rc2 = avc_audit(sid, sid, sclass, av, &avd, rc, &ad, 0);
                if (rc2)
                        return rc2;
        }
@@ -2861,11 +2861,23 @@ static int selinux_inode_readlink(struct dentry *dentry)
        return dentry_has_perm(cred, dentry, FILE__READ);
 }
 
-static int selinux_inode_follow_link(struct dentry *dentry, struct nameidata *nameidata)
+static int selinux_inode_follow_link(struct dentry *dentry, struct inode *inode,
+                                    bool rcu)
 {
        const struct cred *cred = current_cred();
+       struct common_audit_data ad;
+       struct inode_security_struct *isec;
+       u32 sid;
 
-       return dentry_has_perm(cred, dentry, FILE__READ);
+       validate_creds(cred);
+
+       ad.type = LSM_AUDIT_DATA_DENTRY;
+       ad.u.dentry = dentry;
+       sid = cred_sid(cred);
+       isec = inode->i_security;
+
+       return avc_has_perm_flags(sid, isec->sid, isec->sclass, FILE__READ, &ad,
+                                 rcu ? MAY_NOT_BLOCK : 0);
 }
 
 static noinline int audit_inode_permission(struct inode *inode,
index ddf8eec03f211757845de5378afd1c2d5ebfe774..5973c327c54e712edba1034808defd01afa8a8a0 100644 (file)
@@ -130,7 +130,8 @@ static inline int avc_audit(u32 ssid, u32 tsid,
                            u16 tclass, u32 requested,
                            struct av_decision *avd,
                            int result,
-                           struct common_audit_data *a)
+                           struct common_audit_data *a,
+                           int flags)
 {
        u32 audited, denied;
        audited = avc_audit_required(requested, avd, result, 0, &denied);
@@ -138,7 +139,7 @@ static inline int avc_audit(u32 ssid, u32 tsid,
                return 0;
        return slow_avc_audit(ssid, tsid, tclass,
                              requested, audited, denied, result,
-                             a, 0);
+                             a, flags);
 }
 
 #define AVC_STRICT 1 /* Ignore permissive mode. */
@@ -150,6 +151,10 @@ int avc_has_perm_noaudit(u32 ssid, u32 tsid,
 int avc_has_perm(u32 ssid, u32 tsid,
                 u16 tclass, u32 requested,
                 struct common_audit_data *auditdata);
+int avc_has_perm_flags(u32 ssid, u32 tsid,
+                      u16 tclass, u32 requested,
+                      struct common_audit_data *auditdata,
+                      int flags);
 
 u32 avc_policy_seqno(void);
 
index 886be7da989d1ab52d647105ef3648a8045facbe..f845ecf7e172935f938bc52ecfe9c95c7bac4e11 100644 (file)
@@ -121,16 +121,9 @@ static struct snd_timer *mytimer;
 static int __init snd_hrtimer_init(void)
 {
        struct snd_timer *timer;
-       struct timespec tp;
        int err;
 
-       hrtimer_get_res(CLOCK_MONOTONIC, &tp);
-       if (tp.tv_sec > 0 || !tp.tv_nsec) {
-               pr_err("snd-hrtimer: Invalid resolution %u.%09u",
-                          (unsigned)tp.tv_sec, (unsigned)tp.tv_nsec);
-               return -EINVAL;
-       }
-       resolution = tp.tv_nsec;
+       resolution = hrtimer_resolution;
 
        /* Create a new timer and set up the fields */
        err = snd_timer_global_new("hrtimer", SNDRV_TIMER_GLOBAL_HRTIMER,
index d9647bd84d0f49e9b532fa1cbeb685645dfe33b9..27e25bb78c9782a5ba9fff5933d495eb4f200878 100644 (file)
@@ -42,16 +42,13 @@ struct snd_pcsp pcsp_chip;
 static int snd_pcsp_create(struct snd_card *card)
 {
        static struct snd_device_ops ops = { };
-       struct timespec tp;
-       int err;
-       int div, min_div, order;
-
-       hrtimer_get_res(CLOCK_MONOTONIC, &tp);
+       unsigned int resolution = hrtimer_resolution;
+       int err, div, min_div, order;
 
        if (!nopcm) {
-               if (tp.tv_sec || tp.tv_nsec > PCSP_MAX_PERIOD_NS) {
+               if (resolution > PCSP_MAX_PERIOD_NS) {
                        printk(KERN_ERR "PCSP: Timer resolution is not sufficient "
-                               "(%linS)\n", tp.tv_nsec);
+                               "(%unS)\n", resolution);
                        printk(KERN_ERR "PCSP: Make sure you have HPET and ACPI "
                                "enabled.\n");
                        printk(KERN_ERR "PCSP: Turned into nopcm mode.\n");
@@ -59,13 +56,13 @@ static int snd_pcsp_create(struct snd_card *card)
                }
        }
 
-       if (loops_per_jiffy >= PCSP_MIN_LPJ && tp.tv_nsec <= PCSP_MIN_PERIOD_NS)
+       if (loops_per_jiffy >= PCSP_MIN_LPJ && resolution <= PCSP_MIN_PERIOD_NS)
                min_div = MIN_DIV;
        else
                min_div = MAX_DIV;
 #if PCSP_DEBUG
-       printk(KERN_DEBUG "PCSP: lpj=%li, min_div=%i, res=%li\n",
-              loops_per_jiffy, min_div, tp.tv_nsec);
+       printk(KERN_DEBUG "PCSP: lpj=%li, min_div=%i, res=%u\n",
+              loops_per_jiffy, min_div, resolution);
 #endif
 
        div = MAX_DIV / min_div;
index 7371e0c3926f32a9104b521d0bf70f1c35f0740f..1eabcdf69457311129b766ec237d37e402f640bc 100644 (file)
@@ -246,6 +246,9 @@ static int hda_reg_read(void *context, unsigned int reg, unsigned int *val)
                return hda_reg_read_stereo_amp(codec, reg, val);
        if (verb == AC_VERB_GET_PROC_COEF)
                return hda_reg_read_coef(codec, reg, val);
+       if ((verb & 0x700) == AC_VERB_SET_AMP_GAIN_MUTE)
+               reg &= ~AC_AMP_FAKE_MUTE;
+
        err = snd_hdac_exec_verb(codec, reg, 0, val);
        if (err < 0)
                return err;
@@ -265,6 +268,9 @@ static int hda_reg_write(void *context, unsigned int reg, unsigned int val)
        unsigned int verb;
        int i, bytes, err;
 
+       if (codec->caps_overwriting)
+               return 0;
+
        reg &= ~0x00080000U; /* drop GET bit */
        reg |= (codec->addr << 28);
        verb = get_verb(reg);
@@ -280,6 +286,8 @@ static int hda_reg_write(void *context, unsigned int reg, unsigned int val)
 
        switch (verb & 0xf00) {
        case AC_VERB_SET_AMP_GAIN_MUTE:
+               if ((reg & AC_AMP_FAKE_MUTE) && (val & AC_AMP_MUTE))
+                       val = 0;
                verb = AC_VERB_SET_AMP_GAIN_MUTE;
                if (reg & AC_AMP_GET_LEFT)
                        verb |= AC_AMP_SET_LEFT >> 8;
index d2f615ab177a7ca021d9f802d61ba2b57b10a7ca..2153d31fb66312025cb6221afd8476d313261785 100644 (file)
@@ -12,12 +12,14 @@ if SND_MIPS
 config SND_SGI_O2
        tristate "SGI O2 Audio"
        depends on SGI_IP32
+       select SND_PCM
         help
                 Sound support for the SGI O2 Workstation. 
 
 config SND_SGI_HAL2
         tristate "SGI HAL2 Audio"
         depends on SGI_HAS_HAL2
+       select SND_PCM
         help
                 Sound support for the SGI Indy and Indigo2 Workstation.
 
index 6610bd096fc93560fd463c5abf5bce44840497be..d17937b92331e4c1160d1cebb1ed77398a684a01 100644 (file)
@@ -32,6 +32,7 @@
 #include <linux/pci.h>
 #include <linux/stringify.h>
 #include <linux/module.h>
+#include <linux/vmalloc.h>
 
 #ifdef MODULE_FIRMWARE
 MODULE_FIRMWARE("asihpi/dsp5000.bin");
index b49feff0a31982e7c22071c08e8d088e91a97727..5645481af3d9571b8340c963a27c34e377c405c5 100644 (file)
@@ -436,7 +436,7 @@ static unsigned int get_num_devices(struct hda_codec *codec, hda_nid_t nid)
            get_wcaps_type(wcaps) != AC_WID_PIN)
                return 0;
 
-       parm = snd_hda_param_read(codec, nid, AC_PAR_DEVLIST_LEN);
+       parm = snd_hdac_read_parm_uncached(&codec->core, nid, AC_PAR_DEVLIST_LEN);
        if (parm == -1 && codec->bus->rirb_error)
                parm = 0;
        return parm & AC_DEV_LIST_LEN_MASK;
@@ -1375,6 +1375,31 @@ int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
 }
 EXPORT_SYMBOL_GPL(snd_hda_override_amp_caps);
 
+/**
+ * snd_hda_codec_amp_update - update the AMP mono value
+ * @codec: HD-audio codec
+ * @nid: NID to read the AMP value
+ * @ch: channel to update (0 or 1)
+ * @dir: #HDA_INPUT or #HDA_OUTPUT
+ * @idx: the index value (only for input direction)
+ * @mask: bit mask to set
+ * @val: the bits value to set
+ *
+ * Update the AMP values for the given channel, direction and index.
+ */
+int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid,
+                            int ch, int dir, int idx, int mask, int val)
+{
+       unsigned int cmd = snd_hdac_regmap_encode_amp(nid, ch, dir, idx);
+
+       /* enable fake mute if no h/w mute but min=mute */
+       if ((query_amp_caps(codec, nid, dir) &
+            (AC_AMPCAP_MUTE | AC_AMPCAP_MIN_MUTE)) == AC_AMPCAP_MIN_MUTE)
+               cmd |= AC_AMP_FAKE_MUTE;
+       return snd_hdac_regmap_update_raw(&codec->core, cmd, mask, val);
+}
+EXPORT_SYMBOL_GPL(snd_hda_codec_amp_update);
+
 /**
  * snd_hda_codec_amp_stereo - update the AMP stereo values
  * @codec: HD-audio codec
index fea198c58196a41caf4b093da8d6b37355e78a32..b6db25b23dd316d0205d6a14fed365f5ae8cde4f 100644 (file)
@@ -340,6 +340,11 @@ enum {
 #define use_vga_switcheroo(chip)       0
 #endif
 
+#define CONTROLLER_IN_GPU(pci) (((pci)->device == 0x0a0c) || \
+                                       ((pci)->device == 0x0c0c) || \
+                                       ((pci)->device == 0x0d0c) || \
+                                       ((pci)->device == 0x160c))
+
 static char *driver_short_names[] = {
        [AZX_DRIVER_ICH] = "HDA Intel",
        [AZX_DRIVER_PCH] = "HDA Intel PCH",
@@ -1854,8 +1859,17 @@ static int azx_probe_continue(struct azx *chip)
        if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) {
 #ifdef CONFIG_SND_HDA_I915
                err = hda_i915_init(hda);
-               if (err < 0)
-                       goto out_free;
+               if (err < 0) {
+                       /* if the controller is bound only with HDMI/DP
+                        * (for HSW and BDW), we need to abort the probe;
+                        * for other chips, still continue probing as other
+                        * codecs can be on the same link.
+                        */
+                       if (CONTROLLER_IN_GPU(pci))
+                               goto out_free;
+                       else
+                               goto skip_i915;
+               }
                err = hda_display_power(hda, true);
                if (err < 0) {
                        dev_err(chip->card->dev,
@@ -1865,6 +1879,9 @@ static int azx_probe_continue(struct azx *chip)
 #endif
        }
 
+#ifdef CONFIG_SND_HDA_I915
+ skip_i915:
+#endif
        err = azx_first_init(chip);
        if (err < 0)
                goto out_free;
index 3b567f42296b9d6b2ca148c66c59c12b5628cb9e..bed66c3144318de3f82dcddeb2445a84ad9ce594 100644 (file)
@@ -129,8 +129,8 @@ int snd_hda_mixer_amp_switch_put_beep(struct snd_kcontrol *kcontrol,
 /* lowlevel accessor with caching; use carefully */
 #define snd_hda_codec_amp_read(codec, nid, ch, dir, idx) \
        snd_hdac_regmap_get_amp(&(codec)->core, nid, ch, dir, idx)
-#define snd_hda_codec_amp_update(codec, nid, ch, dir, idx, mask, val) \
-       snd_hdac_regmap_update_amp(&(codec)->core, nid, ch, dir, idx, mask, val)
+int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid,
+                            int ch, int dir, int idx, int mask, int val);
 int snd_hda_codec_amp_stereo(struct hda_codec *codec, hda_nid_t nid,
                             int dir, int idx, int mask, int val);
 int snd_hda_codec_amp_init(struct hda_codec *codec, hda_nid_t nid, int ch,
index 4641684264653c5ef265e6f61f6ef8c26bfa5f86..6d010452c1f5c5d131c0ac0a4ae3b2c539e56ad9 100644 (file)
@@ -2168,6 +2168,7 @@ static const struct hda_fixup alc882_fixups[] = {
 static const struct snd_pci_quirk alc882_fixup_tbl[] = {
        SND_PCI_QUIRK(0x1025, 0x006c, "Acer Aspire 9810", ALC883_FIXUP_ACER_EAPD),
        SND_PCI_QUIRK(0x1025, 0x0090, "Acer Aspire", ALC883_FIXUP_ACER_EAPD),
+       SND_PCI_QUIRK(0x1025, 0x0107, "Acer Aspire", ALC883_FIXUP_ACER_EAPD),
        SND_PCI_QUIRK(0x1025, 0x010a, "Acer Ferrari 5000", ALC883_FIXUP_ACER_EAPD),
        SND_PCI_QUIRK(0x1025, 0x0110, "Acer Aspire", ALC883_FIXUP_ACER_EAPD),
        SND_PCI_QUIRK(0x1025, 0x0112, "Acer Aspire 9303", ALC883_FIXUP_ACER_EAPD),
@@ -4514,6 +4515,8 @@ enum {
        ALC288_FIXUP_DELL_HEADSET_MODE,
        ALC288_FIXUP_DELL1_MIC_NO_PRESENCE,
        ALC288_FIXUP_DELL_XPS_13_GPIO6,
+       ALC292_FIXUP_DELL_E7X,
+       ALC292_FIXUP_DISABLE_AAMIX,
 };
 
 static const struct hda_fixup alc269_fixups[] = {
@@ -5036,6 +5039,16 @@ static const struct hda_fixup alc269_fixups[] = {
                .chained = true,
                .chain_id = ALC288_FIXUP_DELL1_MIC_NO_PRESENCE
        },
+       [ALC292_FIXUP_DISABLE_AAMIX] = {
+               .type = HDA_FIXUP_FUNC,
+               .v.func = alc_fixup_disable_aamix,
+       },
+       [ALC292_FIXUP_DELL_E7X] = {
+               .type = HDA_FIXUP_FUNC,
+               .v.func = alc_fixup_dell_xps13,
+               .chained = true,
+               .chain_id = ALC292_FIXUP_DISABLE_AAMIX
+       },
 };
 
 static const struct snd_pci_quirk alc269_fixup_tbl[] = {
@@ -5048,6 +5061,8 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
        SND_PCI_QUIRK(0x1025, 0x0775, "Acer Aspire E1-572", ALC271_FIXUP_HP_GATE_MIC_JACK_E1_572),
        SND_PCI_QUIRK(0x1025, 0x079b, "Acer Aspire V5-573G", ALC282_FIXUP_ASPIRE_V5_PINS),
        SND_PCI_QUIRK(0x1028, 0x0470, "Dell M101z", ALC269_FIXUP_DELL_M101Z),
+       SND_PCI_QUIRK(0x1028, 0x05ca, "Dell Latitude E7240", ALC292_FIXUP_DELL_E7X),
+       SND_PCI_QUIRK(0x1028, 0x05cb, "Dell Latitude E7440", ALC292_FIXUP_DELL_E7X),
        SND_PCI_QUIRK(0x1028, 0x05da, "Dell Vostro 5460", ALC290_FIXUP_SUBWOOFER),
        SND_PCI_QUIRK(0x1028, 0x05f4, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
        SND_PCI_QUIRK(0x1028, 0x05f5, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
@@ -5057,6 +5072,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
        SND_PCI_QUIRK(0x1028, 0x0638, "Dell Inspiron 5439", ALC290_FIXUP_MONO_SPEAKERS_HSJACK),
        SND_PCI_QUIRK(0x1028, 0x064a, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE),
        SND_PCI_QUIRK(0x1028, 0x064b, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE),
+       SND_PCI_QUIRK(0x1028, 0x0665, "Dell XPS 13", ALC292_FIXUP_DELL_E7X),
        SND_PCI_QUIRK(0x1028, 0x06c7, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE),
        SND_PCI_QUIRK(0x1028, 0x06d9, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE),
        SND_PCI_QUIRK(0x1028, 0x06da, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE),
@@ -5376,6 +5392,13 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = {
                {0x17, 0x40000000},
                {0x1d, 0x40700001},
                {0x21, 0x02211040}),
+       SND_HDA_PIN_QUIRK(0x10ec0256, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+               ALC255_STANDARD_PINS,
+               {0x12, 0x90a60160},
+               {0x14, 0x90170120},
+               {0x17, 0x40000000},
+               {0x1d, 0x40700001},
+               {0x21, 0x02211030}),
        SND_HDA_PIN_QUIRK(0x10ec0256, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
                ALC256_STANDARD_PINS,
                {0x13, 0x40000000}),
@@ -5629,8 +5652,7 @@ static int patch_alc269(struct hda_codec *codec)
 
        spec = codec->spec;
        spec->gen.shared_mic_vref_pin = 0x18;
-       if (codec->core.vendor_id != 0x10ec0292)
-               codec->power_save_node = 1;
+       codec->power_save_node = 1;
 
        snd_hda_pick_fixup(codec, alc269_fixup_models,
                       alc269_fixup_tbl, alc269_fixups);
index 6833c74ed6ff47f60598d6d250b8709d914590a5..6c66d7e164391b7e824e2c72b5fa2e5ae889f16d 100644 (file)
@@ -100,6 +100,7 @@ enum {
        STAC_HP_ENVY_BASS,
        STAC_HP_BNB13_EQ,
        STAC_HP_ENVY_TS_BASS,
+       STAC_HP_ENVY_TS_DAC_BIND,
        STAC_92HD83XXX_GPIO10_EAPD,
        STAC_92HD83XXX_MODELS
 };
@@ -2171,6 +2172,22 @@ static void stac92hd83xxx_fixup_gpio10_eapd(struct hda_codec *codec,
        spec->eapd_switch = 0;
 }
 
+static void hp_envy_ts_fixup_dac_bind(struct hda_codec *codec,
+                                           const struct hda_fixup *fix,
+                                           int action)
+{
+       struct sigmatel_spec *spec = codec->spec;
+       static hda_nid_t preferred_pairs[] = {
+               0xd, 0x13,
+               0
+       };
+
+       if (action != HDA_FIXUP_ACT_PRE_PROBE)
+               return;
+
+       spec->gen.preferred_dacs = preferred_pairs;
+}
+
 static const struct hda_verb hp_bnb13_eq_verbs[] = {
        /* 44.1KHz base */
        { 0x22, 0x7A6, 0x3E },
@@ -2686,6 +2703,12 @@ static const struct hda_fixup stac92hd83xxx_fixups[] = {
                        {}
                },
        },
+       [STAC_HP_ENVY_TS_DAC_BIND] = {
+               .type = HDA_FIXUP_FUNC,
+               .v.func = hp_envy_ts_fixup_dac_bind,
+               .chained = true,
+               .chain_id = STAC_HP_ENVY_TS_BASS,
+       },
        [STAC_92HD83XXX_GPIO10_EAPD] = {
                .type = HDA_FIXUP_FUNC,
                .v.func = stac92hd83xxx_fixup_gpio10_eapd,
@@ -2764,6 +2787,8 @@ static const struct snd_pci_quirk stac92hd83xxx_fixup_tbl[] = {
                          "HP bNB13", STAC_HP_BNB13_EQ),
        SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x190e,
                          "HP ENVY TS", STAC_HP_ENVY_TS_BASS),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1967,
+                         "HP ENVY TS", STAC_HP_ENVY_TS_DAC_BIND),
        SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1940,
                          "HP bNB13", STAC_HP_BNB13_EQ),
        SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1941,
index 31a95cca015d4d1c34a1facff2e226b6821a5203..bab6c04932aa050ff63f054bf172acf288f5ee5e 100644 (file)
@@ -449,6 +449,15 @@ static int via_suspend(struct hda_codec *codec)
 
        return 0;
 }
+
+static int via_resume(struct hda_codec *codec)
+{
+       /* some delay here to make jack detection working (bko#98921) */
+       msleep(10);
+       codec->patch_ops.init(codec);
+       regcache_sync(codec->core.regmap);
+       return 0;
+}
 #endif
 
 #ifdef CONFIG_PM
@@ -475,6 +484,7 @@ static const struct hda_codec_ops via_patch_ops = {
        .stream_pm = snd_hda_gen_stream_pm,
 #ifdef CONFIG_PM
        .suspend = via_suspend,
+       .resume = via_resume,
        .check_power_status = via_check_power_status,
 #endif
 };
index 4775da4c4db56529bbc0a6bd6be0b7b2a24370f0..aeef25c0cb3d9fe18256955745b5d2dc4af81e81 100644 (file)
@@ -210,16 +210,18 @@ static int hdmi_dai_hw_params(struct snd_pcm_substream *substream,
 
        cea->db3 = 0; /* not used, all zeros */
 
-       /*
-        * The OMAP HDMI IP requires to use the 8-channel channel code when
-        * transmitting more than two channels.
-        */
        if (params_channels(params) == 2)
                cea->db4_ca = 0x0;
+       else if (params_channels(params) == 6)
+               cea->db4_ca = 0xb;
        else
                cea->db4_ca = 0x13;
 
-       cea->db5_dminh_lsv = CEA861_AUDIO_INFOFRAME_DB5_DM_INH_PROHIBITED;
+       if (cea->db4_ca == 0x00)
+               cea->db5_dminh_lsv = CEA861_AUDIO_INFOFRAME_DB5_DM_INH_PERMITTED;
+       else
+               cea->db5_dminh_lsv = CEA861_AUDIO_INFOFRAME_DB5_DM_INH_PROHIBITED;
+
        /* the expression is trivial but makes clear what we are doing */
        cea->db5_dminh_lsv |= (0 & CEA861_AUDIO_INFOFRAME_DB5_LSV);
 
index 82f582344fe7e1dfce0e39af7c69289c5c4a3a3d..672bcd4c252bd3d17b3c1981be0e9b9ed5e53cc0 100644 (file)
@@ -162,12 +162,11 @@ static int __init migor_init(void)
        if (ret < 0)
                return ret;
 
-       siumckb_lookup = clkdev_alloc(&siumckb_clk, "siumckb_clk", NULL);
+       siumckb_lookup = clkdev_create(&siumckb_clk, "siumckb_clk", NULL);
        if (!siumckb_lookup) {
                ret = -ENOMEM;
                goto eclkdevalloc;
        }
-       clkdev_add(siumckb_lookup);
 
        /* Port number used on this machine: port B */
        migor_snd_device = platform_device_alloc("soc-audio", 1);
index 3e2ef61c627b831bfec65724cc7166db051f5099..8b7e391dd0b80193d49f8634bb69fa45814593f0 100644 (file)
@@ -918,6 +918,7 @@ static void volume_control_quirks(struct usb_mixer_elem_info *cval,
        case USB_ID(0x046d, 0x081d): /* HD Webcam c510 */
        case USB_ID(0x046d, 0x0825): /* HD Webcam c270 */
        case USB_ID(0x046d, 0x0826): /* HD Webcam c525 */
+       case USB_ID(0x046d, 0x08ca): /* Logitech Quickcam Fusion */
        case USB_ID(0x046d, 0x0991):
        /* Most audio usb devices lie about volume resolution.
         * Most Logitech webcams have res = 384.
@@ -1582,12 +1583,6 @@ static int parse_audio_mixer_unit(struct mixer_build *state, int unitid,
                              unitid);
                return -EINVAL;
        }
-       /* no bmControls field (e.g. Maya44) -> ignore */
-       if (desc->bLength <= 10 + input_pins) {
-               usb_audio_dbg(state->chip, "MU %d has no bmControls field\n",
-                             unitid);
-               return 0;
-       }
 
        num_ins = 0;
        ich = 0;
@@ -1595,6 +1590,9 @@ static int parse_audio_mixer_unit(struct mixer_build *state, int unitid,
                err = parse_audio_unit(state, desc->baSourceID[pin]);
                if (err < 0)
                        continue;
+               /* no bmControls field (e.g. Maya44) -> ignore */
+               if (desc->bLength <= 10 + input_pins)
+                       continue;
                err = check_input_term(state, desc->baSourceID[pin], &iterm);
                if (err < 0)
                        return err;
index b703cb3cda1993402d60efc03e9e7d840cb68f72..e5000da9e9d7093f6e287194665de2d63f046e93 100644 (file)
@@ -436,6 +436,11 @@ static struct usbmix_ctl_map usbmix_ctl_maps[] = {
                .id = USB_ID(0x200c, 0x1018),
                .map = ebox44_map,
        },
+       {
+               /* MAYA44 USB+ */
+               .id = USB_ID(0x2573, 0x0008),
+               .map = maya44_map,
+       },
        {
                /* KEF X300A */
                .id = USB_ID(0x27ac, 0x1000),
index 29175346cc4f4f9311726cb13bf1615e68aad7cf..754e689596a21b43f3b3a45b8f3062ec29b74099 100644 (file)
@@ -1120,6 +1120,7 @@ bool snd_usb_get_sample_rate_quirk(struct snd_usb_audio *chip)
        case USB_ID(0x045E, 0x0772): /* MS Lifecam Studio */
        case USB_ID(0x045E, 0x0779): /* MS Lifecam HD-3000 */
        case USB_ID(0x04D8, 0xFEEA): /* Benchmark DAC1 Pre */
+       case USB_ID(0x074D, 0x3553): /* Outlaw RR2150 (Micronas UAC3553B) */
                return true;
        }
        return false;
@@ -1266,8 +1267,9 @@ u64 snd_usb_interface_dsd_format_quirks(struct snd_usb_audio *chip,
                if (fp->altsetting == 2)
                        return SNDRV_PCM_FMTBIT_DSD_U32_BE;
                break;
-       /* DIYINHK DSD DXD 384kHz USB to I2S/DSD */
-       case USB_ID(0x20b1, 0x2009):
+
+       case USB_ID(0x20b1, 0x2009): /* DIYINHK DSD DXD 384kHz USB to I2S/DSD */
+       case USB_ID(0x20b1, 0x2023): /* JLsounds I2SoverUSB */
                if (fp->altsetting == 3)
                        return SNDRV_PCM_FMTBIT_DSD_U32_BE;
                break;
index 9a617adc6675dc06552de428c93b3c611599900b..b35102721cbbc82d1560ae657d41cebebe2fa723 100644 (file)
@@ -1,3 +1,8 @@
+# Some of the tools (perf) use same make variables
+# as in kernel build.
+export srctree=
+export objtree=
+
 include scripts/Makefile.include
 
 help:
@@ -47,11 +52,16 @@ cgroup firewire hv guest usb virtio vm net: FORCE
 liblockdep: FORCE
        $(call descend,lib/lockdep)
 
-libapikfs: FORCE
+libapi: FORCE
        $(call descend,lib/api)
 
-perf: libapikfs FORCE
-       $(call descend,$@)
+# The perf build does not follow the descend function setup,
+# invoking it via it's own make rule.
+PERF_O   = $(if $(O),$(O)/tools/perf,)
+
+perf: FORCE
+       $(Q)mkdir -p $(PERF_O) .
+       $(Q)$(MAKE) --no-print-directory -C perf O=$(PERF_O) subdir=
 
 selftests: FORCE
        $(call descend,testing/$@)
@@ -97,10 +107,10 @@ cgroup_clean hv_clean firewire_clean lguest_clean usb_clean virtio_clean vm_clea
 liblockdep_clean:
        $(call descend,lib/lockdep,clean)
 
-libapikfs_clean:
+libapi_clean:
        $(call descend,lib/api,clean)
 
-perf_clean: libapikfs_clean
+perf_clean:
        $(call descend,$(@:_clean=),clean)
 
 selftests_clean:
diff --git a/tools/arch/alpha/include/asm/barrier.h b/tools/arch/alpha/include/asm/barrier.h
new file mode 100644 (file)
index 0000000..95df19c
--- /dev/null
@@ -0,0 +1,8 @@
+#ifndef __TOOLS_LINUX_ASM_ALPHA_BARRIER_H
+#define __TOOLS_LINUX_ASM_ALPHA_BARRIER_H
+
+#define mb()   __asm__ __volatile__("mb": : :"memory")
+#define rmb()  __asm__ __volatile__("mb": : :"memory")
+#define wmb()  __asm__ __volatile__("wmb": : :"memory")
+
+#endif         /* __TOOLS_LINUX_ASM_ALPHA_BARRIER_H */
diff --git a/tools/arch/arm/include/asm/barrier.h b/tools/arch/arm/include/asm/barrier.h
new file mode 100644 (file)
index 0000000..005c618
--- /dev/null
@@ -0,0 +1,12 @@
+#ifndef _TOOLS_LINUX_ASM_ARM_BARRIER_H
+#define _TOOLS_LINUX_ASM_ARM_BARRIER_H
+
+/*
+ * Use the __kuser_memory_barrier helper in the CPU helper page. See
+ * arch/arm/kernel/entry-armv.S in the kernel source for details.
+ */
+#define mb()           ((void(*)(void))0xffff0fa0)()
+#define wmb()          ((void(*)(void))0xffff0fa0)()
+#define rmb()          ((void(*)(void))0xffff0fa0)()
+
+#endif /* _TOOLS_LINUX_ASM_ARM_BARRIER_H */
diff --git a/tools/arch/arm64/include/asm/barrier.h b/tools/arch/arm64/include/asm/barrier.h
new file mode 100644 (file)
index 0000000..a0483c8
--- /dev/null
@@ -0,0 +1,16 @@
+#ifndef _TOOLS_LINUX_ASM_AARCH64_BARRIER_H
+#define _TOOLS_LINUX_ASM_AARCH64_BARRIER_H
+
+/*
+ * From tools/perf/perf-sys.h, last modified in:
+ * f428ebd184c82a7914b2aa7e9f868918aaf7ea78 perf tools: Fix AAAAARGH64 memory barriers
+ *
+ * XXX: arch/arm64/include/asm/barrier.h in the kernel sources use dsb, is this
+ * a case like for arm32 where we do things differently in userspace?
+ */
+
+#define mb()           asm volatile("dmb ish" ::: "memory")
+#define wmb()          asm volatile("dmb ishst" ::: "memory")
+#define rmb()          asm volatile("dmb ishld" ::: "memory")
+
+#endif /* _TOOLS_LINUX_ASM_AARCH64_BARRIER_H */
diff --git a/tools/arch/ia64/include/asm/barrier.h b/tools/arch/ia64/include/asm/barrier.h
new file mode 100644 (file)
index 0000000..e4422b4
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copied from the kernel sources to tools/:
+ *
+ * Memory barrier definitions.  This is based on information published
+ * in the Processor Abstraction Layer and the System Abstraction Layer
+ * manual.
+ *
+ * Copyright (C) 1998-2003 Hewlett-Packard Co
+ *     David Mosberger-Tang <davidm@hpl.hp.com>
+ * Copyright (C) 1999 Asit Mallick <asit.k.mallick@intel.com>
+ * Copyright (C) 1999 Don Dugger <don.dugger@intel.com>
+ */
+#ifndef _TOOLS_LINUX_ASM_IA64_BARRIER_H
+#define _TOOLS_LINUX_ASM_IA64_BARRIER_H
+
+#include <linux/compiler.h>
+
+/*
+ * Macros to force memory ordering.  In these descriptions, "previous"
+ * and "subsequent" refer to program order; "visible" means that all
+ * architecturally visible effects of a memory access have occurred
+ * (at a minimum, this means the memory has been read or written).
+ *
+ *   wmb():    Guarantees that all preceding stores to memory-
+ *             like regions are visible before any subsequent
+ *             stores and that all following stores will be
+ *             visible only after all previous stores.
+ *   rmb():    Like wmb(), but for reads.
+ *   mb():     wmb()/rmb() combo, i.e., all previous memory
+ *             accesses are visible before all subsequent
+ *             accesses and vice versa.  This is also known as
+ *             a "fence."
+ *
+ * Note: "mb()" and its variants cannot be used as a fence to order
+ * accesses to memory mapped I/O registers.  For that, mf.a needs to
+ * be used.  However, we don't want to always use mf.a because (a)
+ * it's (presumably) much slower than mf and (b) mf.a is supported for
+ * sequential memory pages only.
+ */
+
+/* XXX From arch/ia64/include/uapi/asm/gcc_intrin.h */
+#define ia64_mf()       asm volatile ("mf" ::: "memory")
+
+#define mb()           ia64_mf()
+#define rmb()          mb()
+#define wmb()          mb()
+
+#endif /* _TOOLS_LINUX_ASM_IA64_BARRIER_H */
diff --git a/tools/arch/mips/include/asm/barrier.h b/tools/arch/mips/include/asm/barrier.h
new file mode 100644 (file)
index 0000000..80f96f7
--- /dev/null
@@ -0,0 +1,20 @@
+#ifndef _TOOLS_LINUX_ASM_MIPS_BARRIER_H
+#define _TOOLS_LINUX_ASM_MIPS_BARRIER_H
+/*
+ * FIXME: This came from tools/perf/perf-sys.h, where it was first introduced
+ * in c1e028ef40b8d6943b767028ba17d4f2ba020edb, more work needed to make it
+ * more closely follow the Linux kernel arch/mips/include/asm/barrier.h file.
+ * Probably when we continue work on tools/ Kconfig support to have all the
+ * CONFIG_ needed for properly doing that.
+ */
+#define mb()           asm volatile(                                   \
+                               ".set   mips2\n\t"                      \
+                               "sync\n\t"                              \
+                               ".set   mips0"                          \
+                               : /* no output */                       \
+                               : /* no input */                        \
+                               : "memory")
+#define wmb()  mb()
+#define rmb()  mb()
+
+#endif /* _TOOLS_LINUX_ASM_MIPS_BARRIER_H */
diff --git a/tools/arch/powerpc/include/asm/barrier.h b/tools/arch/powerpc/include/asm/barrier.h
new file mode 100644 (file)
index 0000000..b23aee8
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Copied from the kernel sources:
+ *
+ * Copyright (C) 1999 Cort Dougan <cort@cs.nmt.edu>
+ */
+#ifndef _TOOLS_LINUX_ASM_POWERPC_BARRIER_H
+#define _TOOLS_LINUX_ASM_POWERPC_BARRIER_H
+
+/*
+ * Memory barrier.
+ * The sync instruction guarantees that all memory accesses initiated
+ * by this processor have been performed (with respect to all other
+ * mechanisms that access memory).  The eieio instruction is a barrier
+ * providing an ordering (separately) for (a) cacheable stores and (b)
+ * loads and stores to non-cacheable memory (e.g. I/O devices).
+ *
+ * mb() prevents loads and stores being reordered across this point.
+ * rmb() prevents loads being reordered across this point.
+ * wmb() prevents stores being reordered across this point.
+ *
+ * *mb() variants without smp_ prefix must order all types of memory
+ * operations with one another. sync is the only instruction sufficient
+ * to do this.
+ */
+#define mb()   __asm__ __volatile__ ("sync" : : : "memory")
+#define rmb()  __asm__ __volatile__ ("sync" : : : "memory")
+#define wmb()  __asm__ __volatile__ ("sync" : : : "memory")
+
+#endif /* _TOOLS_LINUX_ASM_POWERPC_BARRIER_H */
diff --git a/tools/arch/s390/include/asm/barrier.h b/tools/arch/s390/include/asm/barrier.h
new file mode 100644 (file)
index 0000000..f851412
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Copied from the kernel sources:
+ *
+ * Copyright IBM Corp. 1999, 2009
+ *
+ * Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com>
+ */
+
+#ifndef __TOOLS_LINUX_ASM_BARRIER_H
+#define __TOOLS_LINUX_ASM_BARRIER_H
+
+/*
+ * Force strict CPU ordering.
+ * And yes, this is required on UP too when we're talking
+ * to devices.
+ */
+
+#ifdef CONFIG_HAVE_MARCH_Z196_FEATURES
+/* Fast-BCR without checkpoint synchronization */
+#define __ASM_BARRIER "bcr 14,0\n"
+#else
+#define __ASM_BARRIER "bcr 15,0\n"
+#endif
+
+#define mb() do {  asm volatile(__ASM_BARRIER : : : "memory"); } while (0)
+
+#define rmb()                          mb()
+#define wmb()                          mb()
+
+#endif /* __TOOLS_LIB_ASM_BARRIER_H */
diff --git a/tools/arch/sh/include/asm/barrier.h b/tools/arch/sh/include/asm/barrier.h
new file mode 100644 (file)
index 0000000..c18fd75
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copied from the kernel sources:
+ *
+ * Copyright (C) 1999, 2000  Niibe Yutaka  &  Kaz Kojima
+ * Copyright (C) 2002 Paul Mundt
+ */
+#ifndef __TOOLS_LINUX_ASM_SH_BARRIER_H
+#define __TOOLS_LINUX_ASM_SH_BARRIER_H
+
+/*
+ * A brief note on ctrl_barrier(), the control register write barrier.
+ *
+ * Legacy SH cores typically require a sequence of 8 nops after
+ * modification of a control register in order for the changes to take
+ * effect. On newer cores (like the sh4a and sh5) this is accomplished
+ * with icbi.
+ *
+ * Also note that on sh4a in the icbi case we can forego a synco for the
+ * write barrier, as it's not necessary for control registers.
+ *
+ * Historically we have only done this type of barrier for the MMUCR, but
+ * it's also necessary for the CCR, so we make it generic here instead.
+ */
+#if defined(__SH4A__) || defined(__SH5__)
+#define mb()           __asm__ __volatile__ ("synco": : :"memory")
+#define rmb()          mb()
+#define wmb()          mb()
+#endif
+
+#include <asm-generic/barrier.h>
+
+#endif /* __TOOLS_LINUX_ASM_SH_BARRIER_H */
diff --git a/tools/arch/sparc/include/asm/barrier.h b/tools/arch/sparc/include/asm/barrier.h
new file mode 100644 (file)
index 0000000..8c017b3
--- /dev/null
@@ -0,0 +1,8 @@
+#ifndef ___TOOLS_LINUX_ASM_SPARC_BARRIER_H
+#define ___TOOLS_LINUX_ASM_SPARC_BARRIER_H
+#if defined(__sparc__) && defined(__arch64__)
+#include "barrier_64.h"
+#else
+#include "barrier_32.h"
+#endif
+#endif
diff --git a/tools/arch/sparc/include/asm/barrier_32.h b/tools/arch/sparc/include/asm/barrier_32.h
new file mode 100644 (file)
index 0000000..c5eadd0
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef __TOOLS_PERF_SPARC_BARRIER_H
+#define __TOOLS_PERF_SPARC_BARRIER_H
+
+#include <asm-generic/barrier.h>
+
+#endif /* !(__TOOLS_PERF_SPARC_BARRIER_H) */
diff --git a/tools/arch/sparc/include/asm/barrier_64.h b/tools/arch/sparc/include/asm/barrier_64.h
new file mode 100644 (file)
index 0000000..9a7d732
--- /dev/null
@@ -0,0 +1,42 @@
+#ifndef __TOOLS_LINUX_SPARC64_BARRIER_H
+#define __TOOLS_LINUX_SPARC64_BARRIER_H
+
+/* Copied from the kernel sources to tools/:
+ *
+ * These are here in an effort to more fully work around Spitfire Errata
+ * #51.  Essentially, if a memory barrier occurs soon after a mispredicted
+ * branch, the chip can stop executing instructions until a trap occurs.
+ * Therefore, if interrupts are disabled, the chip can hang forever.
+ *
+ * It used to be believed that the memory barrier had to be right in the
+ * delay slot, but a case has been traced recently wherein the memory barrier
+ * was one instruction after the branch delay slot and the chip still hung.
+ * The offending sequence was the following in sym_wakeup_done() of the
+ * sym53c8xx_2 driver:
+ *
+ *     call    sym_ccb_from_dsa, 0
+ *      movge  %icc, 0, %l0
+ *     brz,pn  %o0, .LL1303
+ *      mov    %o0, %l2
+ *     membar  #LoadLoad
+ *
+ * The branch has to be mispredicted for the bug to occur.  Therefore, we put
+ * the memory barrier explicitly into a "branch always, predicted taken"
+ * delay slot to avoid the problem case.
+ */
+#define membar_safe(type) \
+do {   __asm__ __volatile__("ba,pt     %%xcc, 1f\n\t" \
+                            " membar   " type "\n" \
+                            "1:\n" \
+                            : : : "memory"); \
+} while (0)
+
+/* The kernel always executes in TSO memory model these days,
+ * and furthermore most sparc64 chips implement more stringent
+ * memory ordering than required by the specifications.
+ */
+#define mb()   membar_safe("#StoreLoad")
+#define rmb()  __asm__ __volatile__("":::"memory")
+#define wmb()  __asm__ __volatile__("":::"memory")
+
+#endif /* !(__TOOLS_LINUX_SPARC64_BARRIER_H) */
diff --git a/tools/arch/tile/include/asm/barrier.h b/tools/arch/tile/include/asm/barrier.h
new file mode 100644 (file)
index 0000000..7d3692c
--- /dev/null
@@ -0,0 +1,15 @@
+#ifndef _TOOLS_LINUX_ASM_TILE_BARRIER_H
+#define _TOOLS_LINUX_ASM_TILE_BARRIER_H
+/*
+ * FIXME: This came from tools/perf/perf-sys.h, where it was first introduced
+ * in 620830b6954913647b7c7f68920cf48eddf6ad92, more work needed to make it
+ * more closely follow the Linux kernel arch/tile/include/asm/barrier.h file.
+ * Probably when we continue work on tools/ Kconfig support to have all the
+ * CONFIG_ needed for properly doing that.
+ */
+
+#define mb()           asm volatile ("mf" ::: "memory")
+#define wmb()          mb()
+#define rmb()          mb()
+
+#endif /* _TOOLS_LINUX_ASM_TILE_BARRIER_H */
diff --git a/tools/arch/x86/include/asm/atomic.h b/tools/arch/x86/include/asm/atomic.h
new file mode 100644 (file)
index 0000000..059e33e
--- /dev/null
@@ -0,0 +1,65 @@
+#ifndef _TOOLS_LINUX_ASM_X86_ATOMIC_H
+#define _TOOLS_LINUX_ASM_X86_ATOMIC_H
+
+#include <linux/compiler.h>
+#include <linux/types.h>
+#include "rmwcc.h"
+
+#define LOCK_PREFIX "\n\tlock; "
+
+/*
+ * Atomic operations that C can't guarantee us.  Useful for
+ * resource counting etc..
+ */
+
+#define ATOMIC_INIT(i) { (i) }
+
+/**
+ * atomic_read - read atomic variable
+ * @v: pointer of type atomic_t
+ *
+ * Atomically reads the value of @v.
+ */
+static inline int atomic_read(const atomic_t *v)
+{
+       return ACCESS_ONCE((v)->counter);
+}
+
+/**
+ * atomic_set - set atomic variable
+ * @v: pointer of type atomic_t
+ * @i: required value
+ *
+ * Atomically sets the value of @v to @i.
+ */
+static inline void atomic_set(atomic_t *v, int i)
+{
+       v->counter = i;
+}
+
+/**
+ * atomic_inc - increment atomic variable
+ * @v: pointer of type atomic_t
+ *
+ * Atomically increments @v by 1.
+ */
+static inline void atomic_inc(atomic_t *v)
+{
+       asm volatile(LOCK_PREFIX "incl %0"
+                    : "+m" (v->counter));
+}
+
+/**
+ * atomic_dec_and_test - decrement and test
+ * @v: pointer of type atomic_t
+ *
+ * Atomically decrements @v by 1 and
+ * returns true if the result is 0, or false for all other
+ * cases.
+ */
+static inline int atomic_dec_and_test(atomic_t *v)
+{
+       GEN_UNARY_RMWcc(LOCK_PREFIX "decl", v->counter, "%0", "e");
+}
+
+#endif /* _TOOLS_LINUX_ASM_X86_ATOMIC_H */
diff --git a/tools/arch/x86/include/asm/barrier.h b/tools/arch/x86/include/asm/barrier.h
new file mode 100644 (file)
index 0000000..f366d8e
--- /dev/null
@@ -0,0 +1,28 @@
+#ifndef _TOOLS_LINUX_ASM_X86_BARRIER_H
+#define _TOOLS_LINUX_ASM_X86_BARRIER_H
+
+/*
+ * Copied from the Linux kernel sources, and also moving code
+ * out from tools/perf/perf-sys.h so as to make it be located
+ * in a place similar as in the kernel sources.
+ *
+ * Force strict CPU ordering.
+ * And yes, this is required on UP too when we're talking
+ * to devices.
+ */
+
+#if defined(__i386__)
+/*
+ * Some non-Intel clones support out of order store. wmb() ceases to be a
+ * nop for these.
+ */
+#define mb()   asm volatile("lock; addl $0,0(%%esp)" ::: "memory")
+#define rmb()  asm volatile("lock; addl $0,0(%%esp)" ::: "memory")
+#define wmb()  asm volatile("lock; addl $0,0(%%esp)" ::: "memory")
+#elif defined(__x86_64__)
+#define mb()   asm volatile("mfence":::"memory")
+#define rmb()  asm volatile("lfence":::"memory")
+#define wmb()  asm volatile("sfence" ::: "memory")
+#endif
+
+#endif /* _TOOLS_LINUX_ASM_X86_BARRIER_H */
diff --git a/tools/arch/x86/include/asm/rmwcc.h b/tools/arch/x86/include/asm/rmwcc.h
new file mode 100644 (file)
index 0000000..a6669bc
--- /dev/null
@@ -0,0 +1,41 @@
+#ifndef _TOOLS_LINUX_ASM_X86_RMWcc
+#define _TOOLS_LINUX_ASM_X86_RMWcc
+
+#ifdef CC_HAVE_ASM_GOTO
+
+#define __GEN_RMWcc(fullop, var, cc, ...)                              \
+do {                                                                   \
+       asm_volatile_goto (fullop "; j" cc " %l[cc_label]"              \
+                       : : "m" (var), ## __VA_ARGS__                   \
+                       : "memory" : cc_label);                         \
+       return 0;                                                       \
+cc_label:                                                              \
+       return 1;                                                       \
+} while (0)
+
+#define GEN_UNARY_RMWcc(op, var, arg0, cc)                             \
+       __GEN_RMWcc(op " " arg0, var, cc)
+
+#define GEN_BINARY_RMWcc(op, var, vcon, val, arg0, cc)                 \
+       __GEN_RMWcc(op " %1, " arg0, var, cc, vcon (val))
+
+#else /* !CC_HAVE_ASM_GOTO */
+
+#define __GEN_RMWcc(fullop, var, cc, ...)                              \
+do {                                                                   \
+       char c;                                                         \
+       asm volatile (fullop "; set" cc " %1"                           \
+                       : "+m" (var), "=qm" (c)                         \
+                       : __VA_ARGS__ : "memory");                      \
+       return c != 0;                                                  \
+} while (0)
+
+#define GEN_UNARY_RMWcc(op, var, arg0, cc)                             \
+       __GEN_RMWcc(op " " arg0, var, cc)
+
+#define GEN_BINARY_RMWcc(op, var, vcon, val, arg0, cc)                 \
+       __GEN_RMWcc(op " %2, " arg0, var, cc, vcon (val))
+
+#endif /* CC_HAVE_ASM_GOTO */
+
+#endif /* _TOOLS_LINUX_ASM_X86_RMWcc */
diff --git a/tools/arch/xtensa/include/asm/barrier.h b/tools/arch/xtensa/include/asm/barrier.h
new file mode 100644 (file)
index 0000000..583800b
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * Copied from the kernel sources to tools/:
+ *
+ * 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) 2001 - 2012 Tensilica Inc.
+ */
+
+#ifndef _TOOLS_LINUX_XTENSA_SYSTEM_H
+#define _TOOLS_LINUX_XTENSA_SYSTEM_H
+
+#define mb()  ({ __asm__ __volatile__("memw" : : : "memory"); })
+#define rmb() barrier()
+#define wmb() mb()
+
+#endif /* _TOOLS_LINUX_XTENSA_SYSTEM_H */
index 10df57237a66d26913a4baabd5949f3459f0a534..a51244a8022f91c26591b9553d16e0020c9f9d12 100644 (file)
@@ -37,7 +37,7 @@ subdir-obj-y :=
 
 # Build definitions
 build-file := $(dir)/Build
-include $(build-file)
+-include $(build-file)
 
 quiet_cmd_flex  = FLEX     $@
 quiet_cmd_bison = BISON    $@
@@ -94,12 +94,12 @@ obj-y        := $(patsubst %/, %/$(obj)-in.o, $(obj-y))
 subdir-obj-y := $(filter %/$(obj)-in.o, $(obj-y))
 
 # '$(OUTPUT)/dir' prefix to all objects
-prefix       := $(subst ./,,$(OUTPUT)$(dir)/)
-obj-y        := $(addprefix $(prefix),$(obj-y))
-subdir-obj-y := $(addprefix $(prefix),$(subdir-obj-y))
+objprefix    := $(subst ./,,$(OUTPUT)$(dir)/)
+obj-y        := $(addprefix $(objprefix),$(obj-y))
+subdir-obj-y := $(addprefix $(objprefix),$(subdir-obj-y))
 
 # Final '$(obj)-in.o' object
-in-target := $(prefix)$(obj)-in.o
+in-target := $(objprefix)$(obj)-in.o
 
 PHONY += $(subdir-y)
 
index 3a0b0ca2a28c1b0bcc6a568d6c7d3ee6ced8c627..2975632d51e2341e7e1a60286e0fa822cbec0279 100644 (file)
@@ -27,7 +27,7 @@ endef
 #   the rule that uses them - an example for that is the 'bionic'
 #   feature check. ]
 #
-FEATURE_TESTS                        \
+FEATURE_TESTS ?=                       \
        backtrace                       \
        dwarf                           \
        fortify-source                  \
@@ -53,7 +53,7 @@ FEATURE_TESTS =                       \
        zlib                            \
        lzma
 
-FEATURE_DISPLAY                      \
+FEATURE_DISPLAY ?=                     \
        dwarf                           \
        glibc                           \
        gtk2                            \
index 0e6c3e6767e6c553c34bff9ad6db7b440429d515..70d876237c5709f36a985c4f8c52a56a063167f7 100644 (file)
@@ -2,6 +2,7 @@ ex-y += ex.o
 ex-y += a.o
 ex-y += b.o
 ex-y += empty/
+ex-y += empty2/
 
 libex-y += c.o
 libex-y += d.o
diff --git a/tools/build/tests/ex/empty2/README b/tools/build/tests/ex/empty2/README
new file mode 100644 (file)
index 0000000..2107cc5
--- /dev/null
@@ -0,0 +1,2 @@
+This directory is left intentionally without Build file
+to test proper nesting into Build-less directories.
diff --git a/tools/include/asm-generic/atomic-gcc.h b/tools/include/asm-generic/atomic-gcc.h
new file mode 100644 (file)
index 0000000..2ba78c9
--- /dev/null
@@ -0,0 +1,63 @@
+#ifndef __TOOLS_ASM_GENERIC_ATOMIC_H
+#define __TOOLS_ASM_GENERIC_ATOMIC_H
+
+#include <linux/compiler.h>
+#include <linux/types.h>
+
+/*
+ * Atomic operations that C can't guarantee us.  Useful for
+ * resource counting etc..
+ *
+ * Excerpts obtained from the Linux kernel sources.
+ */
+
+#define ATOMIC_INIT(i) { (i) }
+
+/**
+ * atomic_read - read atomic variable
+ * @v: pointer of type atomic_t
+ *
+ * Atomically reads the value of @v.
+ */
+static inline int atomic_read(const atomic_t *v)
+{
+       return ACCESS_ONCE((v)->counter);
+}
+
+/**
+ * atomic_set - set atomic variable
+ * @v: pointer of type atomic_t
+ * @i: required value
+ *
+ * Atomically sets the value of @v to @i.
+ */
+static inline void atomic_set(atomic_t *v, int i)
+{
+        v->counter = i;
+}
+
+/**
+ * atomic_inc - increment atomic variable
+ * @v: pointer of type atomic_t
+ *
+ * Atomically increments @v by 1.
+ */
+static inline void atomic_inc(atomic_t *v)
+{
+       __sync_add_and_fetch(&v->counter, 1);
+}
+
+/**
+ * atomic_dec_and_test - decrement and test
+ * @v: pointer of type atomic_t
+ *
+ * Atomically decrements @v by 1 and
+ * returns true if the result is 0, or false for all other
+ * cases.
+ */
+static inline int atomic_dec_and_test(atomic_t *v)
+{
+       return __sync_sub_and_fetch(&v->counter, 1) == 0;
+}
+
+#endif /* __TOOLS_ASM_GENERIC_ATOMIC_H */
diff --git a/tools/include/asm-generic/barrier.h b/tools/include/asm-generic/barrier.h
new file mode 100644 (file)
index 0000000..47b9339
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copied from the kernel sources to tools/perf/:
+ *
+ * Generic barrier definitions, originally based on MN10300 definitions.
+ *
+ * It should be possible to use these on really simple architectures,
+ * but it serves more as a starting point for new ports.
+ *
+ * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+#ifndef __TOOLS_LINUX_ASM_GENERIC_BARRIER_H
+#define __TOOLS_LINUX_ASM_GENERIC_BARRIER_H
+
+#ifndef __ASSEMBLY__
+
+#include <linux/compiler.h>
+
+/*
+ * Force strict CPU ordering. And yes, this is required on UP too when we're
+ * talking to devices.
+ *
+ * Fall back to compiler barriers if nothing better is provided.
+ */
+
+#ifndef mb
+#define mb()   barrier()
+#endif
+
+#ifndef rmb
+#define rmb()  mb()
+#endif
+
+#ifndef wmb
+#define wmb()  mb()
+#endif
+
+#endif /* !__ASSEMBLY__ */
+#endif /* __TOOLS_LINUX_ASM_GENERIC_BARRIER_H */
diff --git a/tools/include/asm/atomic.h b/tools/include/asm/atomic.h
new file mode 100644 (file)
index 0000000..70794f5
--- /dev/null
@@ -0,0 +1,10 @@
+#ifndef __TOOLS_LINUX_ASM_ATOMIC_H
+#define __TOOLS_LINUX_ASM_ATOMIC_H
+
+#if defined(__i386__) || defined(__x86_64__)
+#include "../../arch/x86/include/asm/atomic.h"
+#else
+#include <asm-generic/atomic-gcc.h>
+#endif
+
+#endif /* __TOOLS_LINUX_ASM_ATOMIC_H */
diff --git a/tools/include/asm/barrier.h b/tools/include/asm/barrier.h
new file mode 100644 (file)
index 0000000..ac66ac5
--- /dev/null
@@ -0,0 +1,27 @@
+#if defined(__i386__) || defined(__x86_64__)
+#include "../../arch/x86/include/asm/barrier.h"
+#elif defined(__arm__)
+#include "../../arch/arm/include/asm/barrier.h"
+#elif defined(__aarch64__)
+#include "../../arch/arm64/include/asm/barrier.h"
+#elif defined(__powerpc__)
+#include "../../arch/powerpc/include/asm/barrier.h"
+#elif defined(__s390__)
+#include "../../arch/s390/include/asm/barrier.h"
+#elif defined(__sh__)
+#include "../../arch/sh/include/asm/barrier.h"
+#elif defined(__sparc__)
+#include "../../arch/sparc/include/asm/barrier.h"
+#elif defined(__tile__)
+#include "../../arch/tile/include/asm/barrier.h"
+#elif defined(__alpha__)
+#include "../../arch/alpha/include/asm/barrier.h"
+#elif defined(__mips__)
+#include "../../arch/mips/include/asm/barrier.h"
+#elif defined(__ia64__)
+#include "../../arch/ia64/include/asm/barrier.h"
+#elif defined(__xtensa__)
+#include "../../arch/xtensa/include/asm/barrier.h"
+#else
+#include <asm-generic/barrier.h>
+#endif
diff --git a/tools/include/linux/atomic.h b/tools/include/linux/atomic.h
new file mode 100644 (file)
index 0000000..4e3d3d1
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef __TOOLS_LINUX_ATOMIC_H
+#define __TOOLS_LINUX_ATOMIC_H
+
+#include <asm/atomic.h>
+
+#endif /* __TOOLS_LINUX_ATOMIC_H */
index 88461f09cc860b0d17ab87c3d4a629ee44214af7..f0e72674c52d2c9b88b46cb281db39fe8fb68d8d 100644 (file)
@@ -1,6 +1,10 @@
 #ifndef _TOOLS_LINUX_COMPILER_H_
 #define _TOOLS_LINUX_COMPILER_H_
 
+/* Optimization barrier */
+/* The "volatile" is due to gcc bugs */
+#define barrier() __asm__ __volatile__("": : :"memory")
+
 #ifndef __always_inline
 # define __always_inline       inline __attribute__((always_inline))
 #endif
similarity index 97%
rename from tools/perf/util/include/linux/kernel.h
rename to tools/include/linux/kernel.h
index 09e8e7aea7c68dc25c3ce8f2bd42adf412c54a6f..76df53539c2a4671a48da7cb64faa8dffea5dc51 100644 (file)
@@ -1,5 +1,5 @@
-#ifndef PERF_LINUX_KERNEL_H_
-#define PERF_LINUX_KERNEL_H_
+#ifndef __TOOLS_LINUX_KERNEL_H
+#define __TOOLS_LINUX_KERNEL_H
 
 #include <stdarg.h>
 #include <stdio.h>
similarity index 90%
rename from tools/perf/util/include/linux/list.h
rename to tools/include/linux/list.h
index 76ddbc72634358ff0f08ea996e1f6e3275b54280..76b014c968935aa028f2bc6cb78bfdfbd2a1def7 100644 (file)
@@ -1,10 +1,10 @@
 #include <linux/kernel.h>
 #include <linux/types.h>
 
-#include "../../../../include/linux/list.h"
+#include "../../../include/linux/list.h"
 
-#ifndef PERF_LIST_H
-#define PERF_LIST_H
+#ifndef TOOLS_LIST_H
+#define TOOLS_LIST_H
 /**
  * list_del_range - deletes range of entries from list.
  * @begin: first element in the range to delete from the list.
diff --git a/tools/include/linux/poison.h b/tools/include/linux/poison.h
new file mode 100644 (file)
index 0000000..0c27bdf
--- /dev/null
@@ -0,0 +1 @@
+#include "../../../include/linux/poison.h"
index b5cf25e05df2ecf945097b09199fbd28148a1b08..8ebf6278b2ef23d7cd45daf7b5f6f14bde675aa2 100644 (file)
@@ -60,6 +60,14 @@ typedef __u32 __bitwise __be32;
 typedef __u64 __bitwise __le64;
 typedef __u64 __bitwise __be64;
 
+typedef struct {
+       int counter;
+} atomic_t;
+
+#ifndef __aligned_u64
+# define __aligned_u64 __u64 __attribute__((aligned(8)))
+#endif
+
 struct list_head {
        struct list_head *next, *prev;
 };
index 35f56be5a4cdb9805ecb8e0b8190346ff804baf5..3c60335fe7be7c5bb35a8d5a64f59795a888e768 100644 (file)
@@ -1 +1,2 @@
 TRACEEVENT-CFLAGS
+libtraceevent-dynamic-list
index d410da335e3daeaeac8da1dbdfdabdc7e23fc38f..6daaff652affdde16240c91330781864aaedfd35 100644 (file)
@@ -23,6 +23,7 @@ endef
 # Allow setting CC and AR, or setting CROSS_COMPILE as a prefix.
 $(call allow-override,CC,$(CROSS_COMPILE)gcc)
 $(call allow-override,AR,$(CROSS_COMPILE)ar)
+$(call allow-override,NM,$(CROSS_COMPILE)nm)
 
 EXT = -std=gnu99
 INSTALL = install
@@ -34,9 +35,15 @@ INSTALL = install
 DESTDIR ?=
 DESTDIR_SQ = '$(subst ','\'',$(DESTDIR))'
 
+LP64 := $(shell echo __LP64__ | ${CC} ${CFLAGS} -E -x c - | tail -n 1)
+ifeq ($(LP64), 1)
+  libdir_relative = lib64
+else
+  libdir_relative = lib
+endif
+
 prefix ?= /usr/local
-bindir_relative = bin
-bindir = $(prefix)/$(bindir_relative)
+libdir = $(prefix)/$(libdir_relative)
 man_dir = $(prefix)/share/man
 man_dir_SQ = '$(subst ','\'',$(man_dir))'
 
@@ -58,7 +65,7 @@ ifeq ($(prefix),$(HOME))
 override plugin_dir = $(HOME)/.traceevent/plugins
 set_plugin_dir := 0
 else
-override plugin_dir = $(prefix)/lib/traceevent/plugins
+override plugin_dir = $(libdir)/traceevent/plugins
 endif
 endif
 
@@ -85,11 +92,11 @@ srctree := $(patsubst %/,%,$(dir $(srctree)))
 #$(info Determined 'srctree' to be $(srctree))
 endif
 
-export prefix bindir src obj
+export prefix libdir src obj
 
 # Shell quotes
-bindir_SQ = $(subst ','\'',$(bindir))
-bindir_relative_SQ = $(subst ','\'',$(bindir_relative))
+libdir_SQ = $(subst ','\'',$(libdir))
+libdir_relative_SQ = $(subst ','\'',$(libdir_relative))
 plugin_dir_SQ = $(subst ','\'',$(plugin_dir))
 
 LIB_FILE = libtraceevent.a libtraceevent.so
@@ -151,8 +158,9 @@ PLUGINS_IN := $(PLUGINS:.so=-in.o)
 
 TE_IN    := $(OUTPUT)libtraceevent-in.o
 LIB_FILE := $(addprefix $(OUTPUT),$(LIB_FILE))
+DYNAMIC_LIST_FILE := $(OUTPUT)libtraceevent-dynamic-list
 
-CMD_TARGETS = $(LIB_FILE) $(PLUGINS)
+CMD_TARGETS = $(LIB_FILE) $(PLUGINS) $(DYNAMIC_LIST_FILE)
 
 TARGETS = $(CMD_TARGETS)
 
@@ -169,6 +177,9 @@ $(OUTPUT)libtraceevent.so: $(TE_IN)
 $(OUTPUT)libtraceevent.a: $(TE_IN)
        $(QUIET_LINK)$(RM) $@; $(AR) rcs $@ $^
 
+$(OUTPUT)libtraceevent-dynamic-list: $(PLUGINS)
+       $(QUIET_GEN)$(call do_generate_dynamic_list_file, $(PLUGINS), $@)
+
 plugins: $(PLUGINS)
 
 __plugin_obj = $(notdir $@)
@@ -238,9 +249,16 @@ define do_install_plugins
        done
 endef
 
+define do_generate_dynamic_list_file
+       (echo '{';                                                      \
+       $(NM) -u -D $1 | awk 'NF>1 {print "\t"$$2";"}' | sort -u;       \
+       echo '};';                                                      \
+       ) > $2
+endef
+
 install_lib: all_cmd install_plugins
        $(call QUIET_INSTALL, $(LIB_FILE)) \
-               $(call do_install,$(LIB_FILE),$(bindir_SQ))
+               $(call do_install,$(LIB_FILE),$(libdir_SQ))
 
 install_plugins: $(PLUGINS)
        $(call QUIET_INSTALL, trace_plugins) \
index 29f94f6f0d9e9e2510d38471724e686a9a21167c..cc25f059ab3dfcc5368804597120f96d20dabfdf 100644 (file)
@@ -1387,7 +1387,7 @@ static int event_read_fields(struct event_format *event, struct format_field **f
                        do_warning_event(event, "%s: no type found", __func__);
                        goto fail;
                }
-               field->name = last_token;
+               field->name = field->alias = last_token;
 
                if (test_type(type, EVENT_OP))
                        goto fail;
@@ -1469,7 +1469,7 @@ static int event_read_fields(struct event_format *event, struct format_field **f
                                size_dynamic = type_size(field->name);
                                free_token(field->name);
                                strcat(field->type, brackets);
-                               field->name = token;
+                               field->name = field->alias = token;
                                type = read_token(&token);
                        } else {
                                char *new_type;
@@ -6444,6 +6444,8 @@ void pevent_ref(struct pevent *pevent)
 void pevent_free_format_field(struct format_field *field)
 {
        free(field->type);
+       if (field->alias != field->name)
+               free(field->alias);
        free(field->name);
        free(field);
 }
index 86a5839fb048e87d2f76982ab51231ef4646dfc5..063b1971eb35288ae1a8d72cab18a5df815126a4 100644 (file)
@@ -191,6 +191,7 @@ struct format_field {
        struct event_format     *event;
        char                    *type;
        char                    *name;
+       char                    *alias;
        int                     offset;
        int                     size;
        unsigned int            arraylen;
index 4592d84383188e70d1c59619a7f3b642d49f861e..ec57d0c1fbc2b679a82baf35219ea6cfa523354d 100644 (file)
@@ -4,6 +4,19 @@
 #include <endian.h>
 #include "event-parse.h"
 
+/*
+ * From glibc endian.h, for older systems where it is not present, e.g.: RHEL5,
+ * Fedora6.
+ */
+#ifndef le16toh
+# if __BYTE_ORDER == __LITTLE_ENDIAN
+#  define le16toh(x) (x)
+# else
+#  define le16toh(x) __bswap_16 (x)
+# endif
+#endif
+
+
 static unsigned long long
 process___le16_to_cpup(struct trace_seq *s, unsigned long long *args)
 {
index 812f904193e8869bf0bd179d5dd36e42d51c35c8..09db62ba5786673d2029d8c6b6f96122daa878a1 100644 (file)
@@ -28,3 +28,4 @@ config.mak.autogen
 *-flex.*
 *.pyc
 *.pyo
+.config-detected
diff --git a/tools/perf/Documentation/callchain-overhead-calculation.txt b/tools/perf/Documentation/callchain-overhead-calculation.txt
new file mode 100644 (file)
index 0000000..1a75792
--- /dev/null
@@ -0,0 +1,108 @@
+Overhead calculation
+--------------------
+The overhead can be shown in two columns as 'Children' and 'Self' when
+perf collects callchains.  The 'self' overhead is simply calculated by
+adding all period values of the entry - usually a function (symbol).
+This is the value that perf shows traditionally and sum of all the
+'self' overhead values should be 100%.
+
+The 'children' overhead is calculated by adding all period values of
+the child functions so that it can show the total overhead of the
+higher level functions even if they don't directly execute much.
+'Children' here means functions that are called from another (parent)
+function.
+
+It might be confusing that the sum of all the 'children' overhead
+values exceeds 100% since each of them is already an accumulation of
+'self' overhead of its child functions.  But with this enabled, users
+can find which function has the most overhead even if samples are
+spread over the children.
+
+Consider the following example; there are three functions like below.
+
+-----------------------
+void foo(void) {
+    /* do something */
+}
+
+void bar(void) {
+    /* do something */
+    foo();
+}
+
+int main(void) {
+    bar()
+    return 0;
+}
+-----------------------
+
+In this case 'foo' is a child of 'bar', and 'bar' is an immediate
+child of 'main' so 'foo' also is a child of 'main'.  In other words,
+'main' is a parent of 'foo' and 'bar', and 'bar' is a parent of 'foo'.
+
+Suppose all samples are recorded in 'foo' and 'bar' only.  When it's
+recorded with callchains the output will show something like below
+in the usual (self-overhead-only) output of perf report:
+
+----------------------------------
+Overhead  Symbol
+........  .....................
+  60.00%  foo
+          |
+          --- foo
+              bar
+              main
+              __libc_start_main
+
+  40.00%  bar
+          |
+          --- bar
+              main
+              __libc_start_main
+----------------------------------
+
+When the --children option is enabled, the 'self' overhead values of
+child functions (i.e. 'foo' and 'bar') are added to the parents to
+calculate the 'children' overhead.  In this case the report could be
+displayed as:
+
+-------------------------------------------
+Children      Self  Symbol
+........  ........  ....................
+ 100.00%     0.00%  __libc_start_main
+          |
+          --- __libc_start_main
+
+ 100.00%     0.00%  main
+          |
+          --- main
+              __libc_start_main
+
+ 100.00%    40.00%  bar
+          |
+          --- bar
+              main
+              __libc_start_main
+
+  60.00%    60.00%  foo
+          |
+          --- foo
+              bar
+              main
+              __libc_start_main
+-------------------------------------------
+
+In the above output, the 'self' overhead of 'foo' (60%) was add to the
+'children' overhead of 'bar', 'main' and '\_\_libc_start_main'.
+Likewise, the 'self' overhead of 'bar' (40%) was added to the
+'children' overhead of 'main' and '\_\_libc_start_main'.
+
+So '\_\_libc_start_main' and 'main' are shown first since they have
+same (100%) 'children' overhead (even though they have zero 'self'
+overhead) and they are the parents of 'foo' and 'bar'.
+
+Since v3.16 the 'children' overhead is shown by default and the output
+is sorted by its values. The 'children' overhead is disabled by
+specifying --no-children option on the command line or by adding
+'report.children = false' or 'top.children = false' in the perf config
+file.
index f6480cbf309b40fee97e86112033c1a0cf04236a..bf3d0644bf1066677ae6ced215f82ecbc55f409d 100644 (file)
@@ -210,6 +210,9 @@ Suite for evaluating hash tables.
 *wake*::
 Suite for evaluating wake calls.
 
+*wake-parallel*::
+Suite for evaluating parallel wake calls.
+
 *requeue*::
 Suite for evaluating requeue calls.
 
index dc7442cf3d7f80920b8ba36f00fb8c0da972534b..b876ae312699b9388f6565eec18e239305309f31 100644 (file)
@@ -44,6 +44,33 @@ OPTIONS
 --kallsyms=<file>::
        kallsyms pathname
 
+--itrace::
+       Decode Instruction Tracing data, replacing it with synthesized events.
+       Options are:
+
+               i       synthesize instructions events
+               b       synthesize branches events
+               c       synthesize branches events (calls only)
+               r       synthesize branches events (returns only)
+               x       synthesize transactions events
+               e       synthesize error events
+               d       create a debug log
+               g       synthesize a call chain (use with i or x)
+
+       The default is all events i.e. the same as --itrace=ibxe
+
+       In addition, the period (default 100000) for instructions events
+       can be specified in units of:
+
+               i       instructions
+               t       ticks
+               ms      milliseconds
+               us      microseconds
+               ns      nanoseconds (default)
+
+       Also the call chain size (default 16, max. 1024) for instructions or
+       transactions events can be specified.
+
 SEE ALSO
 --------
 linkperf:perf-record[1], linkperf:perf-report[1], linkperf:perf-archive[1]
index 23219c65c16f77892a30b9b85f38609f1b1dc1f5..ff0f433b3fce1ee402a375b62703d7034c468e34 100644 (file)
@@ -37,7 +37,11 @@ OPTIONS
 
 -s <key[,key2...]>::
 --sort=<key[,key2...]>::
-       Sort the output (default: frag,hit,bytes)
+       Sort the output (default: 'frag,hit,bytes' for slab and 'bytes,hit'
+       for page).  Available sort keys are 'ptr, callsite, bytes, hit,
+       pingpong, frag' for slab and 'page, callsite, bytes, hit, order,
+       migtype, gfp' for page.  This option should be preceded by one of the
+       mode selection options - i.e. --slab, --page, --alloc and/or --caller.
 
 -l <num>::
 --line=<num>::
@@ -52,6 +56,11 @@ OPTIONS
 --page::
        Analyze page allocator events
 
+--live::
+       Show live page stat.  The perf kmem shows total allocation stat by
+       default, but this option shows live (currently allocated) pages
+       instead.  (This option works with --page option only)
+
 SEE ALSO
 --------
 linkperf:perf-record[1]
index 6252e776009c022dda55970dad2aa72e13610cf4..6a5bb2b170391da59b572f8c4230209fb04199e2 100644 (file)
@@ -151,6 +151,12 @@ STAT LIVE OPTIONS
        Show events other than HLT (x86 only) or Wait state (s390 only)
        that take longer than duration usecs.
 
+--proc-map-timeout::
+       When processing pre-existing threads /proc/XXX/mmap, it may take
+       a long time, because the file may be huge. A time out is needed
+       in such cases.
+       This option sets the time out limit. The default value is 500 ms.
+
 SEE ALSO
 --------
 linkperf:perf-top[1], linkperf:perf-record[1], linkperf:perf-report[1],
index 239609c09f83a10ebde91c8254ab94c9b4d15354..3a8a9ba2b0412aba28f1796e283106f84ec63266 100644 (file)
@@ -14,11 +14,13 @@ or
 or
 'perf probe' [options] --del='[GROUP:]EVENT' [...]
 or
-'perf probe' --list
+'perf probe' --list[=[GROUP:]EVENT]
 or
 'perf probe' [options] --line='LINE'
 or
 'perf probe' [options] --vars='PROBEPOINT'
+or
+'perf probe' [options] --funcs
 
 DESCRIPTION
 -----------
@@ -64,8 +66,8 @@ OPTIONS
        classes(e.g. [a-z], [!A-Z]).
 
 -l::
---list::
-       List up current probe events.
+--list[=[GROUP:]EVENT]::
+       List up current probe events. This can also accept filtering patterns of event names.
 
 -L::
 --line=::
@@ -81,10 +83,15 @@ OPTIONS
        (Only for --vars) Show external defined variables in addition to local
        variables.
 
+--no-inlines::
+       (Only for --add) Search only for non-inlined functions. The functions
+       which do not have instances are ignored.
+
 -F::
---funcs::
+--funcs[=FILTER]::
        Show available functions in given module or kernel. With -x/--exec,
        can also list functions in a user space executable / shared library.
+       This also can accept a FILTER rule argument.
 
 --filter=FILTER::
        (Only for --vars and --funcs) Set filter. FILTER is a combination of glob
@@ -148,7 +155,7 @@ Each probe argument follows below syntax.
  [NAME=]LOCALVAR|$retval|%REG|@SYMBOL[:TYPE]
 
 'NAME' specifies the name of this argument (optional). You can use the name of local variable, local data structure member (e.g. var->field, var.field2), local array with fixed index (e.g. array[1], var->array[0], var->pointer[2]), or kprobe-tracer argument format (e.g. $retval, %ax, etc). Note that the name of this argument will be set as the last member name if you specify a local data structure member (e.g. field2 for 'var->field1.field2'.)
-'$vars' special argument is also available for NAME, it is expanded to the local variables which can access at given probe point.
+'$vars' and '$params' special arguments are also available for NAME, '$vars' is expanded to the local variables (including function parameters) which can access at given probe point. '$params' is expanded to only the function parameters.
 'TYPE' casts the type of this argument (optional). If omitted, perf probe automatically set the type based on debuginfo. You can specify 'string' type only for the local variable or structure member which is an array of or a pointer to 'char' or 'unsigned char' type.
 
 On x86 systems %REG is always the short form of the register: for example %AX. %RAX or %EAX is not valid.
index 4847a793de6516df66dc609478c36724772bb941..9b9d9d086680ae82885d2603d2b90435132234bc 100644 (file)
@@ -108,6 +108,8 @@ OPTIONS
        Number of mmap data pages (must be a power of two) or size
        specification with appended unit character - B/K/M/G. The
        size is rounded up to have nearest pages power of two value.
+       Also, by adding a comma, the number of mmap pages for AUX
+       area tracing can be specified.
 
 --group::
        Put all events in a single event group.  This precedes the --event
@@ -145,16 +147,21 @@ OPTIONS
 
 -s::
 --stat::
-       Per thread counts.
+       Record per-thread event counts.  Use it with 'perf report -T' to see
+       the values.
 
 -d::
 --data::
-       Sample addresses.
+       Record the sample addresses.
 
 -T::
 --timestamp::
-       Sample timestamps. Use it with 'perf report -D' to see the timestamps,
-       for instance.
+       Record the sample timestamps. Use it with 'perf report -D' to see the
+       timestamps, for instance.
+
+-P::
+--period::
+       Record the sample period.
 
 -n::
 --no-samples::
@@ -257,6 +264,18 @@ records. See clock_gettime(). In particular CLOCK_MONOTONIC and
 CLOCK_MONOTONIC_RAW are supported, some events might also allow
 CLOCK_BOOTTIME, CLOCK_REALTIME and CLOCK_TAI.
 
+-S::
+--snapshot::
+Select AUX area tracing Snapshot Mode. This option is valid only with an
+AUX area tracing event. Optionally the number of bytes to capture per
+snapshot can be specified. In Snapshot Mode, trace data is captured only when
+signal SIGUSR2 is received.
+
+--proc-map-timeout::
+When processing pre-existing threads /proc/XXX/mmap, it may take a long time,
+because the file may be huge. A time out is needed in such cases.
+This option sets the time out limit. The default value is 500 ms.
+
 SEE ALSO
 --------
 linkperf:perf-stat[1], linkperf:perf-list[1]
index 4879cf63882482155ffad05a3bc2abc8dff90f14..c33b69f3374fda01b6a9ee7dcca6b7bbb2f74e8c 100644 (file)
@@ -34,7 +34,8 @@ OPTIONS
 
 -T::
 --threads::
-       Show per-thread event counters
+       Show per-thread event counters.  The input data file should be recorded
+       with -s option.
 -c::
 --comms=::
        Only consider symbols in these comms. CSV that understands
@@ -193,6 +194,7 @@ OPTIONS
        Accumulate callchain of children to parent entry so that then can
        show up in the output.  The output will have a new "Children" column
        and will be sorted on the data.  It requires callchains are recorded.
+       See the `overhead calculation' section for more details.
 
 --max-stack::
        Set the stack depth limit when parsing the callchain, anything
@@ -323,6 +325,37 @@ OPTIONS
 --header-only::
        Show only perf.data header (forces --stdio).
 
+--itrace::
+       Options for decoding instruction tracing data. The options are:
+
+               i       synthesize instructions events
+               b       synthesize branches events
+               c       synthesize branches events (calls only)
+               r       synthesize branches events (returns only)
+               x       synthesize transactions events
+               e       synthesize error events
+               d       create a debug log
+               g       synthesize a call chain (use with i or x)
+
+       The default is all events i.e. the same as --itrace=ibxe
+
+       In addition, the period (default 100000) for instructions events
+       can be specified in units of:
+
+               i       instructions
+               t       ticks
+               ms      milliseconds
+               us      microseconds
+               ns      nanoseconds (default)
+
+       Also the call chain size (default 16, max. 1024) for instructions or
+       transactions events can be specified.
+
+       To disable decoding entirely, use --no-itrace.
+
+
+include::callchain-overhead-calculation.txt[]
+
 SEE ALSO
 --------
 linkperf:perf-stat[1], linkperf:perf-annotate[1]
index 79445750fcb322fb323c38b6e9a921500f5241c6..c82df572fac2ed4b4285487485d0ed33492e36c5 100644 (file)
@@ -115,7 +115,8 @@ OPTIONS
 -f::
 --fields::
         Comma separated list of fields to print. Options are:
-        comm, tid, pid, time, cpu, event, trace, ip, sym, dso, addr, symoff, srcline, period.
+        comm, tid, pid, time, cpu, event, trace, ip, sym, dso, addr, symoff,
+       srcline, period, flags.
         Field list can be prepended with the type, trace, sw or hw,
         to indicate to which event type the field list applies.
         e.g., -f sw:comm,tid,time,ip,sym  and -f trace:time,cpu,trace
@@ -165,6 +166,12 @@ OPTIONS
 
        At this point usage is displayed, and perf-script exits.
 
+       The flags field is synthesized and may have a value when Instruction
+       Trace decoding. The flags are "bcrosyiABEx" which stand for branch,
+       call, return, conditional, system, asynchronous, interrupt,
+       transaction abort, trace begin, trace end, and in transaction,
+       respectively.
+
        Finally, a user may not set fields to none for all event types.
        i.e., -f "" is not allowed.
 
@@ -221,6 +228,34 @@ OPTIONS
 --header-only
        Show only perf.data header.
 
+--itrace::
+       Options for decoding instruction tracing data. The options are:
+
+               i       synthesize instructions events
+               b       synthesize branches events
+               c       synthesize branches events (calls only)
+               r       synthesize branches events (returns only)
+               x       synthesize transactions events
+               e       synthesize error events
+               d       create a debug log
+               g       synthesize a call chain (use with i or x)
+
+       The default is all events i.e. the same as --itrace=ibxe
+
+       In addition, the period (default 100000) for instructions events
+       can be specified in units of:
+
+               i       instructions
+               t       ticks
+               ms      milliseconds
+               us      microseconds
+               ns      nanoseconds (default)
+
+       Also the call chain size (default 16, max. 1024) for instructions or
+       transactions events can be specified.
+
+       To disable decoding entirely, use --no-itrace.
+
 SEE ALSO
 --------
 linkperf:perf-record[1], linkperf:perf-script-perl[1],
index 3265b10705188027ab30256f7475d218d78f6fbb..776aec4d092771ed8ea7c68c7ce205d0b7578aaa 100644 (file)
@@ -168,7 +168,7 @@ Default is to monitor all CPUS.
        Accumulate callchain of children to parent entry so that then can
        show up in the output.  The output will have a new "Children" column
        and will be sorted on the data.  It requires -g/--call-graph option
-       enabled.
+       enabled.  See the `overhead calculation' section for more details.
 
 --max-stack::
        Set the stack depth limit when parsing the callchain, anything
@@ -201,6 +201,12 @@ Default is to monitor all CPUS.
        Force each column width to the provided list, for large terminal
        readability.  0 means no limit (default behavior).
 
+--proc-map-timeout::
+       When processing pre-existing threads /proc/XXX/mmap, it may take
+       a long time, because the file may be huge. A time out is needed
+       in such cases.
+       This option sets the time out limit. The default value is 500 ms.
+
 
 INTERACTIVE PROMPTING KEYS
 --------------------------
@@ -234,6 +240,7 @@ INTERACTIVE PROMPTING KEYS
 
 Pressing any unmapped key displays a menu, and prompts for input.
 
+include::callchain-overhead-calculation.txt[]
 
 SEE ALSO
 --------
index ba03fd5d1a5476ce218e6b5c7d36720a9ec26294..7ea078658a875029fb0ad03101bacdbe45e31564 100644 (file)
@@ -35,7 +35,7 @@ OPTIONS
 
 -e::
 --expr::
-       List of events to show, currently only syscall names.
+       List of syscalls to show, currently only syscall names.
        Prefixing with ! shows all syscalls but the ones specified.  You may
        need to escape it.
 
@@ -121,6 +121,11 @@ the thread executes on the designated CPUs. Default is to monitor all CPUs.
 --event::
        Trace other events, see 'perf list' for a complete list.
 
+--proc-map-timeout::
+       When processing pre-existing threads /proc/XXX/mmap, it may take a long time,
+       because the file may be huge. A time out is needed in such cases.
+       This option sets the time out limit. The default value is 500 ms.
+
 PAGEFAULTS
 ----------
 
index 11ccbb22ea2b8f1538f07450504c35b50c10399f..fe50a1b34aa0035dec38a07a752ae33df9ad0e5d 100644 (file)
@@ -1,12 +1,30 @@
 tools/perf
+tools/arch/alpha/include/asm/barrier.h
+tools/arch/arm/include/asm/barrier.h
+tools/arch/ia64/include/asm/barrier.h
+tools/arch/mips/include/asm/barrier.h
+tools/arch/powerpc/include/asm/barrier.h
+tools/arch/s390/include/asm/barrier.h
+tools/arch/sh/include/asm/barrier.h
+tools/arch/sparc/include/asm/barrier.h
+tools/arch/sparc/include/asm/barrier_32.h
+tools/arch/sparc/include/asm/barrier_64.h
+tools/arch/tile/include/asm/barrier.h
+tools/arch/x86/include/asm/barrier.h
+tools/arch/xtensa/include/asm/barrier.h
 tools/scripts
 tools/build
+tools/arch/x86/include/asm/atomic.h
+tools/arch/x86/include/asm/rmwcc.h
 tools/lib/traceevent
 tools/lib/api
 tools/lib/symbol/kallsyms.c
 tools/lib/symbol/kallsyms.h
 tools/lib/util/find_next_bit.c
+tools/include/asm/atomic.h
+tools/include/asm/barrier.h
 tools/include/asm/bug.h
+tools/include/asm-generic/barrier.h
 tools/include/asm-generic/bitops/arch_hweight.h
 tools/include/asm-generic/bitops/atomic.h
 tools/include/asm-generic/bitops/const_hweight.h
@@ -17,35 +35,35 @@ tools/include/asm-generic/bitops/fls64.h
 tools/include/asm-generic/bitops/fls.h
 tools/include/asm-generic/bitops/hweight.h
 tools/include/asm-generic/bitops.h
+tools/include/linux/atomic.h
 tools/include/linux/bitops.h
 tools/include/linux/compiler.h
 tools/include/linux/export.h
 tools/include/linux/hash.h
+tools/include/linux/kernel.h
+tools/include/linux/list.h
 tools/include/linux/log2.h
+tools/include/linux/poison.h
 tools/include/linux/types.h
 include/asm-generic/bitops/arch_hweight.h
 include/asm-generic/bitops/const_hweight.h
 include/asm-generic/bitops/fls64.h
 include/asm-generic/bitops/__fls.h
 include/asm-generic/bitops/fls.h
-include/linux/const.h
 include/linux/perf_event.h
 include/linux/rbtree.h
 include/linux/list.h
 include/linux/hash.h
 include/linux/stringify.h
-lib/find_next_bit.c
 lib/hweight.c
 lib/rbtree.c
 include/linux/swab.h
 arch/*/include/asm/unistd*.h
-arch/*/include/asm/perf_regs.h
 arch/*/include/uapi/asm/unistd*.h
 arch/*/include/uapi/asm/perf_regs.h
 arch/*/lib/memcpy*.S
 arch/*/lib/memset*.S
 include/linux/poison.h
-include/linux/magic.h
 include/linux/hw_breakpoint.h
 include/linux/rbtree_augmented.h
 include/uapi/linux/perf_event.h
index c43a2051759157dd6b65118b34b342b149f58861..1af0cfeb7a57824980ef64fdf4d26f643fa7ab6c 100644 (file)
@@ -73,6 +73,8 @@ include config/utilities.mak
 # for CTF data format.
 #
 # Define NO_LZMA if you do not want to support compressed (xz) kernel modules
+#
+# Define NO_AUXTRACE if you do not want AUX area tracing support
 
 ifeq ($(srctree),)
 srctree := $(patsubst %/,%,$(dir $(shell pwd)))
@@ -171,6 +173,9 @@ endif
 LIBTRACEEVENT = $(TE_PATH)libtraceevent.a
 export LIBTRACEEVENT
 
+LIBTRACEEVENT_DYNAMIC_LIST = $(TE_PATH)libtraceevent-dynamic-list
+LIBTRACEEVENT_DYNAMIC_LIST_LDFLAGS = -Xlinker --dynamic-list=$(LIBTRACEEVENT_DYNAMIC_LIST)
+
 LIBAPI = $(LIB_PATH)libapi.a
 export LIBAPI
 
@@ -185,8 +190,9 @@ python-clean := $(call QUIET_CLEAN, python) $(RM) -r $(PYTHON_EXTBUILD) $(OUTPUT
 PYTHON_EXT_SRCS := $(shell grep -v ^\# util/python-ext-sources)
 PYTHON_EXT_DEPS := util/python-ext-sources util/setup.py $(LIBTRACEEVENT) $(LIBAPI)
 
-$(OUTPUT)python/perf.so: $(PYTHON_EXT_SRCS) $(PYTHON_EXT_DEPS)
-       $(QUIET_GEN)CFLAGS='$(CFLAGS)' $(PYTHON_WORD) util/setup.py \
+$(OUTPUT)python/perf.so: $(PYTHON_EXT_SRCS) $(PYTHON_EXT_DEPS) $(LIBTRACEEVENT_DYNAMIC_LIST)
+       $(QUIET_GEN)CFLAGS='$(CFLAGS)' LDFLAGS='$(LDFLAGS) $(LIBTRACEEVENT_DYNAMIC_LIST_LDFLAGS)' \
+         $(PYTHON_WORD) util/setup.py \
          --quiet build_ext; \
        mkdir -p $(OUTPUT)python && \
        cp $(PYTHON_EXTBUILD_LIB)perf.so $(OUTPUT)python/
@@ -276,8 +282,9 @@ build := -f $(srctree)/tools/build/Makefile.build dir=. obj
 $(PERF_IN): $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)common-cmds.h FORCE
        $(Q)$(MAKE) $(build)=perf
 
-$(OUTPUT)perf: $(PERFLIBS) $(PERF_IN)
-       $(QUIET_LINK)$(CC) $(CFLAGS) $(LDFLAGS) $(PERF_IN) $(LIBS) -o $@
+$(OUTPUT)perf: $(PERFLIBS) $(PERF_IN) $(LIBTRACEEVENT_DYNAMIC_LIST)
+       $(QUIET_LINK)$(CC) $(CFLAGS) $(LDFLAGS) $(LIBTRACEEVENT_DYNAMIC_LIST_LDFLAGS) \
+               $(PERF_IN) $(LIBS) -o $@
 
 $(GTK_IN): FORCE
        $(Q)$(MAKE) $(build)=gtk
@@ -371,7 +378,13 @@ $(LIB_FILE): $(LIBPERF_IN)
 LIBTRACEEVENT_FLAGS += plugin_dir=$(plugindir_SQ)
 
 $(LIBTRACEEVENT): FORCE
-       $(Q)$(MAKE) -C $(TRACE_EVENT_DIR) $(LIBTRACEEVENT_FLAGS) O=$(OUTPUT) $(OUTPUT)libtraceevent.a plugins
+       $(Q)$(MAKE) -C $(TRACE_EVENT_DIR) $(LIBTRACEEVENT_FLAGS) O=$(OUTPUT) $(OUTPUT)libtraceevent.a
+
+libtraceevent_plugins: FORCE
+       $(Q)$(MAKE) -C $(TRACE_EVENT_DIR) $(LIBTRACEEVENT_FLAGS) O=$(OUTPUT) plugins
+
+$(LIBTRACEEVENT_DYNAMIC_LIST): libtraceevent_plugins
+       $(Q)$(MAKE) -C $(TRACE_EVENT_DIR) $(LIBTRACEEVENT_FLAGS) O=$(OUTPUT) $(OUTPUT)libtraceevent-dynamic-list
 
 $(LIBTRACEEVENT)-clean:
        $(call QUIET_CLEAN, libtraceevent)
@@ -462,7 +475,7 @@ check: $(OUTPUT)common-cmds.h
 
 install-gtk:
 
-install-bin: all install-gtk
+install-tools: all install-gtk
        $(call QUIET_INSTALL, binaries) \
                $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(bindir_SQ)'; \
                $(INSTALL) $(OUTPUT)perf '$(DESTDIR_SQ)$(bindir_SQ)'; \
@@ -500,12 +513,16 @@ endif
        $(call QUIET_INSTALL, perf_completion-script) \
                $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(sysconfdir_SQ)/bash_completion.d'; \
                $(INSTALL) perf-completion.sh '$(DESTDIR_SQ)$(sysconfdir_SQ)/bash_completion.d/perf'
+
+install-tests: all install-gtk
        $(call QUIET_INSTALL, tests) \
                $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests'; \
                $(INSTALL) tests/attr.py '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests'; \
                $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests/attr'; \
                $(INSTALL) tests/attr/* '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests/attr'
 
+install-bin: install-tools install-tests
+
 install: install-bin try-install-man install-traceevent-plugins
 
 install-python_ext:
@@ -549,4 +566,5 @@ FORCE:
 .PHONY: all install clean config-clean strip install-gtk
 .PHONY: shell_compatibility_test please_set_SHELL_PATH_to_a_more_modern_shell
 .PHONY: $(GIT-HEAD-PHONY) TAGS tags cscope FORCE single_dep
+.PHONY: libtraceevent_plugins
 
index 54afe4a467e7d9a3f505cad40650378bdcd27296..41bf61da476a4ce3150fc0184c84718d807be894 100644 (file)
@@ -1 +1,2 @@
 libperf-y += util/
+libperf-$(CONFIG_DWARF_UNWIND) += tests/
index 1d3f39c3aa564fd2e85a950fddcb4bba18f5e8db..4e5af27e3fbfa5f42d9d474d8ff7dc237722206b 100644 (file)
@@ -5,8 +5,11 @@
 #include <linux/types.h>
 #include <asm/perf_regs.h>
 
+void perf_regs_load(u64 *regs);
+
 #define PERF_REGS_MASK ((1ULL << PERF_REG_ARM64_MAX) - 1)
 #define PERF_REGS_MAX  PERF_REG_ARM64_MAX
+#define PERF_SAMPLE_REGS_ABI   PERF_SAMPLE_REGS_ABI_64
 
 #define PERF_REG_IP    PERF_REG_ARM64_PC
 #define PERF_REG_SP    PERF_REG_ARM64_SP
diff --git a/tools/perf/arch/arm64/tests/Build b/tools/perf/arch/arm64/tests/Build
new file mode 100644 (file)
index 0000000..b30eff9
--- /dev/null
@@ -0,0 +1,2 @@
+libperf-y += regs_load.o
+libperf-y += dwarf-unwind.o
diff --git a/tools/perf/arch/arm64/tests/dwarf-unwind.c b/tools/perf/arch/arm64/tests/dwarf-unwind.c
new file mode 100644 (file)
index 0000000..cf04a4c
--- /dev/null
@@ -0,0 +1,61 @@
+#include <string.h>
+#include "perf_regs.h"
+#include "thread.h"
+#include "map.h"
+#include "event.h"
+#include "debug.h"
+#include "tests/tests.h"
+
+#define STACK_SIZE 8192
+
+static int sample_ustack(struct perf_sample *sample,
+               struct thread *thread, u64 *regs)
+{
+       struct stack_dump *stack = &sample->user_stack;
+       struct map *map;
+       unsigned long sp;
+       u64 stack_size, *buf;
+
+       buf = malloc(STACK_SIZE);
+       if (!buf) {
+               pr_debug("failed to allocate sample uregs data\n");
+               return -1;
+       }
+
+       sp = (unsigned long) regs[PERF_REG_ARM64_SP];
+
+       map = map_groups__find(thread->mg, MAP__VARIABLE, (u64) sp);
+       if (!map) {
+               pr_debug("failed to get stack map\n");
+               free(buf);
+               return -1;
+       }
+
+       stack_size = map->end - sp;
+       stack_size = stack_size > STACK_SIZE ? STACK_SIZE : stack_size;
+
+       memcpy(buf, (void *) sp, stack_size);
+       stack->data = (char *) buf;
+       stack->size = stack_size;
+       return 0;
+}
+
+int test__arch_unwind_sample(struct perf_sample *sample,
+               struct thread *thread)
+{
+       struct regs_dump *regs = &sample->user_regs;
+       u64 *buf;
+
+       buf = calloc(1, sizeof(u64) * PERF_REGS_MAX);
+       if (!buf) {
+               pr_debug("failed to allocate sample uregs data\n");
+               return -1;
+       }
+
+       perf_regs_load(buf);
+       regs->abi  = PERF_SAMPLE_REGS_ABI;
+       regs->regs = buf;
+       regs->mask = PERF_REGS_MASK;
+
+       return sample_ustack(sample, thread, buf);
+}
diff --git a/tools/perf/arch/arm64/tests/regs_load.S b/tools/perf/arch/arm64/tests/regs_load.S
new file mode 100644 (file)
index 0000000..025b46e
--- /dev/null
@@ -0,0 +1,46 @@
+#include <linux/linkage.h>
+
+.text
+.type perf_regs_load,%function
+#define STR_REG(r)     str x##r, [x0, 8 * r]
+#define LDR_REG(r)     ldr x##r, [x0, 8 * r]
+#define SP     (8 * 31)
+#define PC     (8 * 32)
+ENTRY(perf_regs_load)
+       STR_REG(0)
+       STR_REG(1)
+       STR_REG(2)
+       STR_REG(3)
+       STR_REG(4)
+       STR_REG(5)
+       STR_REG(6)
+       STR_REG(7)
+       STR_REG(8)
+       STR_REG(9)
+       STR_REG(10)
+       STR_REG(11)
+       STR_REG(12)
+       STR_REG(13)
+       STR_REG(14)
+       STR_REG(15)
+       STR_REG(16)
+       STR_REG(17)
+       STR_REG(18)
+       STR_REG(19)
+       STR_REG(20)
+       STR_REG(21)
+       STR_REG(22)
+       STR_REG(23)
+       STR_REG(24)
+       STR_REG(25)
+       STR_REG(26)
+       STR_REG(27)
+       STR_REG(28)
+       STR_REG(29)
+       STR_REG(30)
+       mov x1, sp
+       str x1, [x0, #SP]
+       str x30, [x0, #PC]
+       LDR_REG(1)
+       ret
+ENDPROC(perf_regs_load)
index 49776f190abfab295920534840aa1df479a4c094..b7bb42c4469401d76527264f5e538b363b1d4bf9 100644 (file)
@@ -61,7 +61,7 @@ const char *const mips_triplets[] = {
 static bool lookup_path(char *name)
 {
        bool found = false;
-       char *path, *tmp;
+       char *path, *tmp = NULL;
        char buf[PATH_MAX];
        char *env = getenv("PATH");
 
index 0af6e9b3f72857af68ddef33862f00d884a5da1e..7b8b0d1a1b626065e0b414f42a7a998723e0e57f 100644 (file)
@@ -1,4 +1,5 @@
 libperf-y += header.o
+libperf-y += sym-handling.o
 
 libperf-$(CONFIG_DWARF) += dwarf-regs.o
 libperf-$(CONFIG_DWARF) += skip-callchain-idx.o
diff --git a/tools/perf/arch/powerpc/util/sym-handling.c b/tools/perf/arch/powerpc/util/sym-handling.c
new file mode 100644 (file)
index 0000000..bbc1a50
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * 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.
+ *
+ * Copyright (C) 2015 Naveen N. Rao, IBM Corporation
+ */
+
+#include "debug.h"
+#include "symbol.h"
+#include "map.h"
+#include "probe-event.h"
+
+#ifdef HAVE_LIBELF_SUPPORT
+bool elf__needs_adjust_symbols(GElf_Ehdr ehdr)
+{
+       return ehdr.e_type == ET_EXEC ||
+              ehdr.e_type == ET_REL ||
+              ehdr.e_type == ET_DYN;
+}
+
+#if defined(_CALL_ELF) && _CALL_ELF == 2
+void arch__elf_sym_adjust(GElf_Sym *sym)
+{
+       sym->st_value += PPC64_LOCAL_ENTRY_OFFSET(sym->st_other);
+}
+#endif
+#endif
+
+#if !defined(_CALL_ELF) || _CALL_ELF != 2
+int arch__choose_best_symbol(struct symbol *syma,
+                            struct symbol *symb __maybe_unused)
+{
+       char *sym = syma->name;
+
+       /* Skip over any initial dot */
+       if (*sym == '.')
+               sym++;
+
+       /* Avoid "SyS" kernel syscall aliases */
+       if (strlen(sym) >= 3 && !strncmp(sym, "SyS", 3))
+               return SYMBOL_B;
+       if (strlen(sym) >= 10 && !strncmp(sym, "compat_SyS", 10))
+               return SYMBOL_B;
+
+       return SYMBOL_A;
+}
+
+/* Allow matching against dot variants */
+int arch__compare_symbol_names(const char *namea, const char *nameb)
+{
+       /* Skip over initial dot */
+       if (*namea == '.')
+               namea++;
+       if (*nameb == '.')
+               nameb++;
+
+       return strcmp(namea, nameb);
+}
+#endif
+
+#if defined(_CALL_ELF) && _CALL_ELF == 2
+bool arch__prefers_symtab(void)
+{
+       return true;
+}
+
+#define PPC64LE_LEP_OFFSET     8
+
+void arch__fix_tev_from_maps(struct perf_probe_event *pev,
+                            struct probe_trace_event *tev, struct map *map)
+{
+       /*
+        * ppc64 ABIv2 local entry point is currently always 2 instructions
+        * (8 bytes) after the global entry point.
+        */
+       if (!pev->uprobes && map->dso->symtab_type == DSO_BINARY_TYPE__KALLSYMS) {
+               tev->point.address += PPC64LE_LEP_OFFSET;
+               tev->point.offset += PPC64LE_LEP_OFFSET;
+       }
+}
+#endif
index 5ce98023d518fce2d51ee11e35b0a0f2df12d8de..c3ab760e06b4d7627896723dc9fe2b15c5327a0b 100644 (file)
@@ -3,6 +3,7 @@ perf-y += sched-pipe.o
 perf-y += mem-memcpy.o
 perf-y += futex-hash.o
 perf-y += futex-wake.o
+perf-y += futex-wake-parallel.o
 perf-y += futex-requeue.o
 
 perf-$(CONFIG_X86_64) += mem-memcpy-x86-64-asm.o
index 3c4dd44d45cb7b668bd22822b6f105c1de9eaf86..70b2f718cc217976ee73bd656d66b9aa884dfded 100644 (file)
@@ -33,6 +33,8 @@ extern int bench_mem_memcpy(int argc, const char **argv,
 extern int bench_mem_memset(int argc, const char **argv, const char *prefix);
 extern int bench_futex_hash(int argc, const char **argv, const char *prefix);
 extern int bench_futex_wake(int argc, const char **argv, const char *prefix);
+extern int bench_futex_wake_parallel(int argc, const char **argv,
+                                    const char *prefix);
 extern int bench_futex_requeue(int argc, const char **argv, const char *prefix);
 
 #define BENCH_FORMAT_DEFAULT_STR       "default"
diff --git a/tools/perf/bench/futex-wake-parallel.c b/tools/perf/bench/futex-wake-parallel.c
new file mode 100644 (file)
index 0000000..6d8c9fa
--- /dev/null
@@ -0,0 +1,294 @@
+/*
+ * Copyright (C) 2015 Davidlohr Bueso.
+ *
+ * Block a bunch of threads and let parallel waker threads wakeup an
+ * equal amount of them. The program output reflects the avg latency
+ * for each individual thread to service its share of work. Ultimately
+ * it can be used to measure futex_wake() changes.
+ */
+
+#include "../perf.h"
+#include "../util/util.h"
+#include "../util/stat.h"
+#include "../util/parse-options.h"
+#include "../util/header.h"
+#include "bench.h"
+#include "futex.h"
+
+#include <err.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <pthread.h>
+
+struct thread_data {
+       pthread_t worker;
+       unsigned int nwoken;
+       struct timeval runtime;
+};
+
+static unsigned int nwakes = 1;
+
+/* all threads will block on the same futex -- hash bucket chaos ;) */
+static u_int32_t futex = 0;
+
+static pthread_t *blocked_worker;
+static bool done = false, silent = false, fshared = false;
+static unsigned int nblocked_threads = 0, nwaking_threads = 0;
+static pthread_mutex_t thread_lock;
+static pthread_cond_t thread_parent, thread_worker;
+static struct stats waketime_stats, wakeup_stats;
+static unsigned int ncpus, threads_starting;
+static int futex_flag = 0;
+
+static const struct option options[] = {
+       OPT_UINTEGER('t', "threads", &nblocked_threads, "Specify amount of threads"),
+       OPT_UINTEGER('w', "nwakers", &nwaking_threads, "Specify amount of waking threads"),
+       OPT_BOOLEAN( 's', "silent",  &silent,   "Silent mode: do not display data/details"),
+       OPT_BOOLEAN( 'S', "shared",  &fshared,  "Use shared futexes instead of private ones"),
+       OPT_END()
+};
+
+static const char * const bench_futex_wake_parallel_usage[] = {
+       "perf bench futex wake-parallel <options>",
+       NULL
+};
+
+static void *waking_workerfn(void *arg)
+{
+       struct thread_data *waker = (struct thread_data *) arg;
+       struct timeval start, end;
+
+       gettimeofday(&start, NULL);
+
+       waker->nwoken = futex_wake(&futex, nwakes, futex_flag);
+       if (waker->nwoken != nwakes)
+               warnx("couldn't wakeup all tasks (%d/%d)",
+                     waker->nwoken, nwakes);
+
+       gettimeofday(&end, NULL);
+       timersub(&end, &start, &waker->runtime);
+
+       pthread_exit(NULL);
+       return NULL;
+}
+
+static void wakeup_threads(struct thread_data *td, pthread_attr_t thread_attr)
+{
+       unsigned int i;
+
+       pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_JOINABLE);
+
+       /* create and block all threads */
+       for (i = 0; i < nwaking_threads; i++) {
+               /*
+                * Thread creation order will impact per-thread latency
+                * as it will affect the order to acquire the hb spinlock.
+                * For now let the scheduler decide.
+                */
+               if (pthread_create(&td[i].worker, &thread_attr,
+                                  waking_workerfn, (void *)&td[i]))
+                       err(EXIT_FAILURE, "pthread_create");
+       }
+
+       for (i = 0; i < nwaking_threads; i++)
+               if (pthread_join(td[i].worker, NULL))
+                       err(EXIT_FAILURE, "pthread_join");
+}
+
+static void *blocked_workerfn(void *arg __maybe_unused)
+{
+       pthread_mutex_lock(&thread_lock);
+       threads_starting--;
+       if (!threads_starting)
+               pthread_cond_signal(&thread_parent);
+       pthread_cond_wait(&thread_worker, &thread_lock);
+       pthread_mutex_unlock(&thread_lock);
+
+       while (1) { /* handle spurious wakeups */
+               if (futex_wait(&futex, 0, NULL, futex_flag) != EINTR)
+                       break;
+       }
+
+       pthread_exit(NULL);
+       return NULL;
+}
+
+static void block_threads(pthread_t *w, pthread_attr_t thread_attr)
+{
+       cpu_set_t cpu;
+       unsigned int i;
+
+       threads_starting = nblocked_threads;
+
+       /* create and block all threads */
+       for (i = 0; i < nblocked_threads; i++) {
+               CPU_ZERO(&cpu);
+               CPU_SET(i % ncpus, &cpu);
+
+               if (pthread_attr_setaffinity_np(&thread_attr, sizeof(cpu_set_t), &cpu))
+                       err(EXIT_FAILURE, "pthread_attr_setaffinity_np");
+
+               if (pthread_create(&w[i], &thread_attr, blocked_workerfn, NULL))
+                       err(EXIT_FAILURE, "pthread_create");
+       }
+}
+
+static void print_run(struct thread_data *waking_worker, unsigned int run_num)
+{
+       unsigned int i, wakeup_avg;
+       double waketime_avg, waketime_stddev;
+       struct stats __waketime_stats, __wakeup_stats;
+
+       init_stats(&__wakeup_stats);
+       init_stats(&__waketime_stats);
+
+       for (i = 0; i < nwaking_threads; i++) {
+               update_stats(&__waketime_stats, waking_worker[i].runtime.tv_usec);
+               update_stats(&__wakeup_stats, waking_worker[i].nwoken);
+       }
+
+       waketime_avg = avg_stats(&__waketime_stats);
+       waketime_stddev = stddev_stats(&__waketime_stats);
+       wakeup_avg = avg_stats(&__wakeup_stats);
+
+       printf("[Run %d]: Avg per-thread latency (waking %d/%d threads) "
+              "in %.4f ms (+-%.2f%%)\n", run_num + 1, wakeup_avg,
+              nblocked_threads, waketime_avg/1e3,
+              rel_stddev_stats(waketime_stddev, waketime_avg));
+}
+
+static void print_summary(void)
+{
+       unsigned int wakeup_avg;
+       double waketime_avg, waketime_stddev;
+
+       waketime_avg = avg_stats(&waketime_stats);
+       waketime_stddev = stddev_stats(&waketime_stats);
+       wakeup_avg = avg_stats(&wakeup_stats);
+
+       printf("Avg per-thread latency (waking %d/%d threads) in %.4f ms (+-%.2f%%)\n",
+              wakeup_avg,
+              nblocked_threads,
+              waketime_avg/1e3,
+              rel_stddev_stats(waketime_stddev, waketime_avg));
+}
+
+
+static void do_run_stats(struct thread_data *waking_worker)
+{
+       unsigned int i;
+
+       for (i = 0; i < nwaking_threads; i++) {
+               update_stats(&waketime_stats, waking_worker[i].runtime.tv_usec);
+               update_stats(&wakeup_stats, waking_worker[i].nwoken);
+       }
+
+}
+
+static void toggle_done(int sig __maybe_unused,
+                       siginfo_t *info __maybe_unused,
+                       void *uc __maybe_unused)
+{
+       done = true;
+}
+
+int bench_futex_wake_parallel(int argc, const char **argv,
+                             const char *prefix __maybe_unused)
+{
+       int ret = 0;
+       unsigned int i, j;
+       struct sigaction act;
+       pthread_attr_t thread_attr;
+       struct thread_data *waking_worker;
+
+       argc = parse_options(argc, argv, options,
+                            bench_futex_wake_parallel_usage, 0);
+       if (argc) {
+               usage_with_options(bench_futex_wake_parallel_usage, options);
+               exit(EXIT_FAILURE);
+       }
+
+       sigfillset(&act.sa_mask);
+       act.sa_sigaction = toggle_done;
+       sigaction(SIGINT, &act, NULL);
+
+       ncpus = sysconf(_SC_NPROCESSORS_ONLN);
+       if (!nblocked_threads)
+               nblocked_threads = ncpus;
+
+       /* some sanity checks */
+       if (nwaking_threads > nblocked_threads || !nwaking_threads)
+               nwaking_threads = nblocked_threads;
+
+       if (nblocked_threads % nwaking_threads)
+               errx(EXIT_FAILURE, "Must be perfectly divisible");
+       /*
+        * Each thread will wakeup nwakes tasks in
+        * a single futex_wait call.
+        */
+       nwakes = nblocked_threads/nwaking_threads;
+
+       blocked_worker = calloc(nblocked_threads, sizeof(*blocked_worker));
+       if (!blocked_worker)
+               err(EXIT_FAILURE, "calloc");
+
+       if (!fshared)
+               futex_flag = FUTEX_PRIVATE_FLAG;
+
+       printf("Run summary [PID %d]: blocking on %d threads (at [%s] "
+              "futex %p), %d threads waking up %d at a time.\n\n",
+              getpid(), nblocked_threads, fshared ? "shared":"private",
+              &futex, nwaking_threads, nwakes);
+
+       init_stats(&wakeup_stats);
+       init_stats(&waketime_stats);
+
+       pthread_attr_init(&thread_attr);
+       pthread_mutex_init(&thread_lock, NULL);
+       pthread_cond_init(&thread_parent, NULL);
+       pthread_cond_init(&thread_worker, NULL);
+
+       for (j = 0; j < bench_repeat && !done; j++) {
+               waking_worker = calloc(nwaking_threads, sizeof(*waking_worker));
+               if (!waking_worker)
+                       err(EXIT_FAILURE, "calloc");
+
+               /* create, launch & block all threads */
+               block_threads(blocked_worker, thread_attr);
+
+               /* make sure all threads are already blocked */
+               pthread_mutex_lock(&thread_lock);
+               while (threads_starting)
+                       pthread_cond_wait(&thread_parent, &thread_lock);
+               pthread_cond_broadcast(&thread_worker);
+               pthread_mutex_unlock(&thread_lock);
+
+               usleep(100000);
+
+               /* Ok, all threads are patiently blocked, start waking folks up */
+               wakeup_threads(waking_worker, thread_attr);
+
+               for (i = 0; i < nblocked_threads; i++) {
+                       ret = pthread_join(blocked_worker[i], NULL);
+                       if (ret)
+                               err(EXIT_FAILURE, "pthread_join");
+               }
+
+               do_run_stats(waking_worker);
+               if (!silent)
+                       print_run(waking_worker, j);
+
+               free(waking_worker);
+       }
+
+       /* cleanup & report results */
+       pthread_cond_destroy(&thread_parent);
+       pthread_cond_destroy(&thread_worker);
+       pthread_mutex_destroy(&thread_lock);
+       pthread_attr_destroy(&thread_attr);
+
+       print_summary();
+
+       free(blocked_worker);
+       return ret;
+}
index 929f762be47e9735058f5c57bd394c4f09360c45..e5e41d3bdce724230c16a3df2a199914b9cab29d 100644 (file)
@@ -60,7 +60,12 @@ static void *workerfn(void *arg __maybe_unused)
        pthread_cond_wait(&thread_worker, &thread_lock);
        pthread_mutex_unlock(&thread_lock);
 
-       futex_wait(&futex1, 0, NULL, futex_flag);
+       while (1) {
+               if (futex_wait(&futex1, 0, NULL, futex_flag) != EINTR)
+                       break;
+       }
+
+       pthread_exit(NULL);
        return NULL;
 }
 
index ba5efa4710b558239ff79c08b025ddc2da06efc5..870b7e665a203264c1b7b27684a860cbe147c450 100644 (file)
@@ -8,6 +8,7 @@
 #include "../builtin.h"
 #include "../util/util.h"
 #include "../util/parse-options.h"
+#include "../util/cloexec.h"
 
 #include "bench.h"
 
@@ -23,6 +24,7 @@
 #include <pthread.h>
 #include <sys/mman.h>
 #include <sys/time.h>
+#include <sys/resource.h>
 #include <sys/wait.h>
 #include <sys/prctl.h>
 #include <sys/types.h>
@@ -51,6 +53,9 @@ struct thread_data {
        unsigned int            loops_done;
        u64                     val;
        u64                     runtime_ns;
+       u64                     system_time_ns;
+       u64                     user_time_ns;
+       double                  speed_gbs;
        pthread_mutex_t         *process_lock;
 };
 
@@ -1042,6 +1047,7 @@ static void *worker_thread(void *__tdata)
        u64 bytes_done;
        long work_done;
        u32 l;
+       struct rusage rusage;
 
        bind_to_cpumask(td->bind_cpumask);
        bind_to_memnode(td->bind_node);
@@ -1194,6 +1200,13 @@ static void *worker_thread(void *__tdata)
        timersub(&stop, &start0, &diff);
        td->runtime_ns = diff.tv_sec * 1000000000ULL;
        td->runtime_ns += diff.tv_usec * 1000ULL;
+       td->speed_gbs = bytes_done / (td->runtime_ns / 1e9) / 1e9;
+
+       getrusage(RUSAGE_THREAD, &rusage);
+       td->system_time_ns = rusage.ru_stime.tv_sec * 1000000000ULL;
+       td->system_time_ns += rusage.ru_stime.tv_usec * 1000ULL;
+       td->user_time_ns = rusage.ru_utime.tv_sec * 1000000000ULL;
+       td->user_time_ns += rusage.ru_utime.tv_usec * 1000ULL;
 
        free_data(thread_data, g->p.bytes_thread);
 
@@ -1420,7 +1433,7 @@ static int __bench_numa(const char *name)
        double runtime_sec_min;
        int wait_stat;
        double bytes;
-       int i, t;
+       int i, t, p;
 
        if (init())
                return -1;
@@ -1556,6 +1569,24 @@ static int __bench_numa(const char *name)
        print_res(name, bytes / runtime_sec_max / 1e9,
                "GB/sec,", "total-speed",       "GB/sec total speed");
 
+       if (g->p.show_details >= 2) {
+               char tname[32];
+               struct thread_data *td;
+               for (p = 0; p < g->p.nr_proc; p++) {
+                       for (t = 0; t < g->p.nr_threads; t++) {
+                               memset(tname, 0, 32);
+                               td = g->threads + p*g->p.nr_threads + t;
+                               snprintf(tname, 32, "process%d:thread%d", p, t);
+                               print_res(tname, td->speed_gbs,
+                                       "GB/sec",       "thread-speed", "GB/sec/thread speed");
+                               print_res(tname, td->system_time_ns / 1e9,
+                                       "secs", "thread-system-time", "system CPU time/thread");
+                               print_res(tname, td->user_time_ns / 1e9,
+                                       "secs", "thread-user-time", "user CPU time/thread");
+                       }
+               }
+       }
+
        free(pids);
 
        deinit();
index 71bf7451c0cad1bf1f43b946631bf949eed96c3c..2c1bec39c30ea191fecb46654df14dc6229d27d5 100644 (file)
@@ -59,6 +59,10 @@ static int perf_evsel__add_sample(struct perf_evsel *evsel,
            (al->sym == NULL ||
             strcmp(ann->sym_hist_filter, al->sym->name) != 0)) {
                /* We're only interested in a symbol named sym_hist_filter */
+               /*
+                * FIXME: why isn't this done in the symbol_filter when loading
+                * the DSO?
+                */
                if (al->sym != NULL) {
                        rb_erase(&al->sym->rb_node,
                                 &al->map->dso->symbols[al->map->type]);
@@ -84,6 +88,7 @@ static int process_sample_event(struct perf_tool *tool,
 {
        struct perf_annotate *ann = container_of(tool, struct perf_annotate, tool);
        struct addr_location al;
+       int ret = 0;
 
        if (perf_event__preprocess_sample(event, machine, &al, sample) < 0) {
                pr_warning("problem processing %d event, skipping it.\n",
@@ -92,15 +97,16 @@ static int process_sample_event(struct perf_tool *tool,
        }
 
        if (ann->cpu_list && !test_bit(sample->cpu, ann->cpu_bitmap))
-               return 0;
+               goto out_put;
 
        if (!al.filtered && perf_evsel__add_sample(evsel, sample, &al, ann)) {
                pr_warning("problem incrementing symbol count, "
                           "skipping event\n");
-               return -1;
+               ret = -1;
        }
-
-       return 0;
+out_put:
+       addr_location__put(&al);
+       return ret;
 }
 
 static int hist_entry__tty_annotate(struct hist_entry *he,
@@ -283,7 +289,6 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __maybe_unused)
                },
        };
        struct perf_data_file file = {
-               .path  = input_name,
                .mode  = PERF_DATA_MODE_READ,
        };
        const struct option options[] = {
@@ -324,6 +329,8 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __maybe_unused)
                   "objdump binary to use for disassembly and annotations"),
        OPT_BOOLEAN(0, "group", &symbol_conf.event_group,
                    "Show event group information together"),
+       OPT_BOOLEAN(0, "show-total-period", &symbol_conf.show_total_period,
+                   "Show a column with the sum of periods"),
        OPT_END()
        };
        int ret = hists__init();
@@ -340,6 +347,8 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __maybe_unused)
        else if (annotate.use_gtk)
                use_browser = 2;
 
+       file.path  = input_name;
+
        setup_browser(true);
 
        annotate.session = perf_session__new(&file, false, &annotate.tool);
index b9a56fa8333065271a9242e98cf052d9c8100b87..b5314e452ec7f24a2e9e4e6b8473d39b8ed7a4a7 100644 (file)
@@ -58,6 +58,7 @@ static struct bench mem_benchmarks[] = {
 static struct bench futex_benchmarks[] = {
        { "hash",       "Benchmark for futex hash table",               bench_futex_hash        },
        { "wake",       "Benchmark for futex wake calls",               bench_futex_wake        },
+       { "wake-parallel", "Benchmark for parallel futex wake calls",   bench_futex_wake_parallel },
        { "requeue",    "Benchmark for futex requeue calls",            bench_futex_requeue     },
        { "all",        "Test all futex benchmarks",                    NULL                    },
        { NULL,         NULL,                                           NULL                    }
index feb420f74c2d9fd34778a20e93764c2e5eaf47c8..9fe93c8d4fcff11b3c2638d901d8c62da5b921d2 100644 (file)
@@ -69,6 +69,15 @@ static int perf_session__list_build_ids(bool force, bool with_hits)
        session = perf_session__new(&file, false, &build_id__mark_dso_hit_ops);
        if (session == NULL)
                return -1;
+
+       /*
+        * We take all buildids when the file contains AUX area tracing data
+        * because we do not decode the trace because it would take too long.
+        */
+       if (!perf_data_file__is_pipe(&file) &&
+           perf_header__has_feat(&session->header, HEADER_AUXTRACE))
+               with_hits = false;
+
        /*
         * in pipe-mode, the only way to get the buildids is to parse
         * the record stream. Buildids are stored as RECORD_HEADER_BUILD_ID
index df6307b4050aaa2ab7e51403d22563e0a1866279..daaa7dca9c3ba81e1e2a7c671dc7660fe34ba4a5 100644 (file)
@@ -328,6 +328,7 @@ static int diff__process_sample_event(struct perf_tool *tool __maybe_unused,
 {
        struct addr_location al;
        struct hists *hists = evsel__hists(evsel);
+       int ret = -1;
 
        if (perf_event__preprocess_sample(event, machine, &al, sample) < 0) {
                pr_warning("problem processing %d event, skipping it.\n",
@@ -338,7 +339,7 @@ static int diff__process_sample_event(struct perf_tool *tool __maybe_unused,
        if (hists__add_entry(hists, &al, sample->period,
                             sample->weight, sample->transaction)) {
                pr_warning("problem incrementing symbol period, skipping event\n");
-               return -1;
+               goto out_put;
        }
 
        /*
@@ -350,8 +351,10 @@ static int diff__process_sample_event(struct perf_tool *tool __maybe_unused,
        hists->stats.total_period += sample->period;
        if (!al.filtered)
                hists->stats.total_non_filtered_period += sample->period;
-
-       return 0;
+       ret = 0;
+out_put:
+       addr_location__put(&al);
+       return ret;
 }
 
 static struct perf_tool tool = {
index 40a33d7334cce224fb0ac8317554189f1821dfcf..52ec66b236076c46c1bd0666967d1d204d2c95c3 100644 (file)
@@ -16,6 +16,7 @@
 #include "util/debug.h"
 #include "util/build-id.h"
 #include "util/data.h"
+#include "util/auxtrace.h"
 
 #include "util/parse-options.h"
 
@@ -26,10 +27,12 @@ struct perf_inject {
        struct perf_session     *session;
        bool                    build_ids;
        bool                    sched_stat;
+       bool                    have_auxtrace;
        const char              *input_name;
        struct perf_data_file   output;
        u64                     bytes_written;
        struct list_head        samples;
+       struct itrace_synth_opts itrace_synth_opts;
 };
 
 struct event_entry {
@@ -38,14 +41,11 @@ struct event_entry {
        union perf_event event[0];
 };
 
-static int perf_event__repipe_synth(struct perf_tool *tool,
-                                   union perf_event *event)
+static int output_bytes(struct perf_inject *inject, void *buf, size_t sz)
 {
-       struct perf_inject *inject = container_of(tool, struct perf_inject, tool);
        ssize_t size;
 
-       size = perf_data_file__write(&inject->output, event,
-                                    event->header.size);
+       size = perf_data_file__write(&inject->output, buf, sz);
        if (size < 0)
                return -errno;
 
@@ -53,6 +53,15 @@ static int perf_event__repipe_synth(struct perf_tool *tool,
        return 0;
 }
 
+static int perf_event__repipe_synth(struct perf_tool *tool,
+                                   union perf_event *event)
+{
+       struct perf_inject *inject = container_of(tool, struct perf_inject,
+                                                 tool);
+
+       return output_bytes(inject, event, event->header.size);
+}
+
 static int perf_event__repipe_oe_synth(struct perf_tool *tool,
                                       union perf_event *event,
                                       struct ordered_events *oe __maybe_unused)
@@ -86,6 +95,79 @@ static int perf_event__repipe_attr(struct perf_tool *tool,
        return perf_event__repipe_synth(tool, event);
 }
 
+#ifdef HAVE_AUXTRACE_SUPPORT
+
+static int copy_bytes(struct perf_inject *inject, int fd, off_t size)
+{
+       char buf[4096];
+       ssize_t ssz;
+       int ret;
+
+       while (size > 0) {
+               ssz = read(fd, buf, min(size, (off_t)sizeof(buf)));
+               if (ssz < 0)
+                       return -errno;
+               ret = output_bytes(inject, buf, ssz);
+               if (ret)
+                       return ret;
+               size -= ssz;
+       }
+
+       return 0;
+}
+
+static s64 perf_event__repipe_auxtrace(struct perf_tool *tool,
+                                      union perf_event *event,
+                                      struct perf_session *session
+                                      __maybe_unused)
+{
+       struct perf_inject *inject = container_of(tool, struct perf_inject,
+                                                 tool);
+       int ret;
+
+       inject->have_auxtrace = true;
+
+       if (!inject->output.is_pipe) {
+               off_t offset;
+
+               offset = lseek(inject->output.fd, 0, SEEK_CUR);
+               if (offset == -1)
+                       return -errno;
+               ret = auxtrace_index__auxtrace_event(&session->auxtrace_index,
+                                                    event, offset);
+               if (ret < 0)
+                       return ret;
+       }
+
+       if (perf_data_file__is_pipe(session->file) || !session->one_mmap) {
+               ret = output_bytes(inject, event, event->header.size);
+               if (ret < 0)
+                       return ret;
+               ret = copy_bytes(inject, perf_data_file__fd(session->file),
+                                event->auxtrace.size);
+       } else {
+               ret = output_bytes(inject, event,
+                                  event->header.size + event->auxtrace.size);
+       }
+       if (ret < 0)
+               return ret;
+
+       return event->auxtrace.size;
+}
+
+#else
+
+static s64
+perf_event__repipe_auxtrace(struct perf_tool *tool __maybe_unused,
+                           union perf_event *event __maybe_unused,
+                           struct perf_session *session __maybe_unused)
+{
+       pr_err("AUX area tracing not supported\n");
+       return -EINVAL;
+}
+
+#endif
+
 static int perf_event__repipe(struct perf_tool *tool,
                              union perf_event *event,
                              struct perf_sample *sample __maybe_unused,
@@ -155,6 +237,32 @@ static int perf_event__repipe_fork(struct perf_tool *tool,
        return err;
 }
 
+static int perf_event__repipe_comm(struct perf_tool *tool,
+                                  union perf_event *event,
+                                  struct perf_sample *sample,
+                                  struct machine *machine)
+{
+       int err;
+
+       err = perf_event__process_comm(tool, event, sample, machine);
+       perf_event__repipe(tool, event, sample, machine);
+
+       return err;
+}
+
+static int perf_event__repipe_exit(struct perf_tool *tool,
+                                  union perf_event *event,
+                                  struct perf_sample *sample,
+                                  struct machine *machine)
+{
+       int err;
+
+       err = perf_event__process_exit(tool, event, sample, machine);
+       perf_event__repipe(tool, event, sample, machine);
+
+       return err;
+}
+
 static int perf_event__repipe_tracing_data(struct perf_tool *tool,
                                           union perf_event *event,
                                           struct perf_session *session)
@@ -167,6 +275,18 @@ static int perf_event__repipe_tracing_data(struct perf_tool *tool,
        return err;
 }
 
+static int perf_event__repipe_id_index(struct perf_tool *tool,
+                                      union perf_event *event,
+                                      struct perf_session *session)
+{
+       int err;
+
+       perf_event__repipe_synth(tool, event);
+       err = perf_event__process_id_index(tool, event, session);
+
+       return err;
+}
+
 static int dso__read_build_id(struct dso *dso)
 {
        if (dso->has_build_id)
@@ -245,6 +365,7 @@ static int perf_event__inject_buildid(struct perf_tool *tool,
                }
        }
 
+       thread__put(thread);
 repipe:
        perf_event__repipe(tool, event, sample, machine);
        return 0;
@@ -351,16 +472,20 @@ static int __cmd_inject(struct perf_inject *inject)
        struct perf_session *session = inject->session;
        struct perf_data_file *file_out = &inject->output;
        int fd = perf_data_file__fd(file_out);
+       u64 output_data_offset;
 
        signal(SIGINT, sig_handler);
 
-       if (inject->build_ids || inject->sched_stat) {
+       if (inject->build_ids || inject->sched_stat ||
+           inject->itrace_synth_opts.set) {
                inject->tool.mmap         = perf_event__repipe_mmap;
                inject->tool.mmap2        = perf_event__repipe_mmap2;
                inject->tool.fork         = perf_event__repipe_fork;
                inject->tool.tracing_data = perf_event__repipe_tracing_data;
        }
 
+       output_data_offset = session->header.data_offset;
+
        if (inject->build_ids) {
                inject->tool.sample = perf_event__inject_buildid;
        } else if (inject->sched_stat) {
@@ -379,17 +504,43 @@ static int __cmd_inject(struct perf_inject *inject)
                        else if (!strncmp(name, "sched:sched_stat_", 17))
                                evsel->handler = perf_inject__sched_stat;
                }
+       } else if (inject->itrace_synth_opts.set) {
+               session->itrace_synth_opts = &inject->itrace_synth_opts;
+               inject->itrace_synth_opts.inject = true;
+               inject->tool.comm           = perf_event__repipe_comm;
+               inject->tool.exit           = perf_event__repipe_exit;
+               inject->tool.id_index       = perf_event__repipe_id_index;
+               inject->tool.auxtrace_info  = perf_event__process_auxtrace_info;
+               inject->tool.auxtrace       = perf_event__process_auxtrace;
+               inject->tool.ordered_events = true;
+               inject->tool.ordering_requires_timestamps = true;
+               /* Allow space in the header for new attributes */
+               output_data_offset = 4096;
        }
 
+       if (!inject->itrace_synth_opts.set)
+               auxtrace_index__free(&session->auxtrace_index);
+
        if (!file_out->is_pipe)
-               lseek(fd, session->header.data_offset, SEEK_SET);
+               lseek(fd, output_data_offset, SEEK_SET);
 
        ret = perf_session__process_events(session);
 
        if (!file_out->is_pipe) {
-               if (inject->build_ids)
+               if (inject->build_ids) {
                        perf_header__set_feat(&session->header,
                                              HEADER_BUILD_ID);
+                       if (inject->have_auxtrace)
+                               dsos__hit_all(session);
+               }
+               /*
+                * The AUX areas have been removed and replaced with
+                * synthesized hardware events, so clear the feature flag.
+                */
+               if (inject->itrace_synth_opts.set)
+                       perf_header__clear_feat(&session->header,
+                                               HEADER_AUXTRACE);
+               session->header.data_offset = output_data_offset;
                session->header.data_size = inject->bytes_written;
                perf_session__write_header(session, session->evlist, fd, true);
        }
@@ -408,11 +559,16 @@ int cmd_inject(int argc, const char **argv, const char *prefix __maybe_unused)
                        .fork           = perf_event__repipe,
                        .exit           = perf_event__repipe,
                        .lost           = perf_event__repipe,
+                       .aux            = perf_event__repipe,
+                       .itrace_start   = perf_event__repipe,
                        .read           = perf_event__repipe_sample,
                        .throttle       = perf_event__repipe,
                        .unthrottle     = perf_event__repipe,
                        .attr           = perf_event__repipe_attr,
                        .tracing_data   = perf_event__repipe_op2_synth,
+                       .auxtrace_info  = perf_event__repipe_op2_synth,
+                       .auxtrace       = perf_event__repipe_auxtrace,
+                       .auxtrace_error = perf_event__repipe_op2_synth,
                        .finished_round = perf_event__repipe_oe_synth,
                        .build_id       = perf_event__repipe_op2_synth,
                        .id_index       = perf_event__repipe_op2_synth,
@@ -444,6 +600,9 @@ int cmd_inject(int argc, const char **argv, const char *prefix __maybe_unused)
                OPT_STRING(0, "kallsyms", &symbol_conf.kallsyms_name, "file",
                           "kallsyms pathname"),
                OPT_BOOLEAN('f', "force", &file.force, "don't complain, do it"),
+               OPT_CALLBACK_OPTARG(0, "itrace", &inject.itrace_synth_opts,
+                                   NULL, "opts", "Instruction Tracing options",
+                                   itrace_parse_synth_opts),
                OPT_END()
        };
        const char * const inject_usage[] = {
index 1634186d537cdc2eb2ee38b174891361ef13db9f..950f296dfcf7a402ebbad0df1edf16e4d62a52bd 100644 (file)
@@ -10,6 +10,7 @@
 #include "util/header.h"
 #include "util/session.h"
 #include "util/tool.h"
+#include "util/callchain.h"
 
 #include "util/parse-options.h"
 #include "util/trace-event.h"
 #include <linux/rbtree.h>
 #include <linux/string.h>
 #include <locale.h>
+#include <regex.h>
 
 static int     kmem_slab;
 static int     kmem_page;
 
 static long    kmem_page_size;
+static enum {
+       KMEM_SLAB,
+       KMEM_PAGE,
+} kmem_default = KMEM_SLAB;  /* for backward compatibility */
 
 struct alloc_stat;
-typedef int (*sort_fn_t)(struct alloc_stat *, struct alloc_stat *);
+typedef int (*sort_fn_t)(void *, void *);
 
 static int                     alloc_flag;
 static int                     caller_flag;
@@ -179,8 +185,8 @@ static int perf_evsel__process_alloc_node_event(struct perf_evsel *evsel,
        return ret;
 }
 
-static int ptr_cmp(struct alloc_stat *, struct alloc_stat *);
-static int callsite_cmp(struct alloc_stat *, struct alloc_stat *);
+static int ptr_cmp(void *, void *);
+static int slab_callsite_cmp(void *, void *);
 
 static struct alloc_stat *search_alloc_stat(unsigned long ptr,
                                            unsigned long call_site,
@@ -221,7 +227,8 @@ static int perf_evsel__process_free_event(struct perf_evsel *evsel,
                s_alloc->pingpong++;
 
                s_caller = search_alloc_stat(0, s_alloc->call_site,
-                                            &root_caller_stat, callsite_cmp);
+                                            &root_caller_stat,
+                                            slab_callsite_cmp);
                if (!s_caller)
                        return -1;
                s_caller->pingpong++;
@@ -241,6 +248,8 @@ static unsigned long nr_page_fails;
 static unsigned long nr_page_nomatch;
 
 static bool use_pfn;
+static bool live_page;
+static struct perf_session *kmem_session;
 
 #define MAX_MIGRATE_TYPES  6
 #define MAX_PAGE_ORDER     11
@@ -250,6 +259,7 @@ static int order_stats[MAX_PAGE_ORDER][MAX_MIGRATE_TYPES];
 struct page_stat {
        struct rb_node  node;
        u64             page;
+       u64             callsite;
        int             order;
        unsigned        gfp_flags;
        unsigned        migrate_type;
@@ -259,13 +269,158 @@ struct page_stat {
        int             nr_free;
 };
 
-static struct rb_root page_tree;
+static struct rb_root page_live_tree;
 static struct rb_root page_alloc_tree;
 static struct rb_root page_alloc_sorted;
+static struct rb_root page_caller_tree;
+static struct rb_root page_caller_sorted;
 
-static struct page_stat *search_page(unsigned long page, bool create)
+struct alloc_func {
+       u64 start;
+       u64 end;
+       char *name;
+};
+
+static int nr_alloc_funcs;
+static struct alloc_func *alloc_func_list;
+
+static int funcmp(const void *a, const void *b)
+{
+       const struct alloc_func *fa = a;
+       const struct alloc_func *fb = b;
+
+       if (fa->start > fb->start)
+               return 1;
+       else
+               return -1;
+}
+
+static int callcmp(const void *a, const void *b)
+{
+       const struct alloc_func *fa = a;
+       const struct alloc_func *fb = b;
+
+       if (fb->start <= fa->start && fa->end < fb->end)
+               return 0;
+
+       if (fa->start > fb->start)
+               return 1;
+       else
+               return -1;
+}
+
+static int build_alloc_func_list(void)
 {
-       struct rb_node **node = &page_tree.rb_node;
+       int ret;
+       struct map *kernel_map;
+       struct symbol *sym;
+       struct rb_node *node;
+       struct alloc_func *func;
+       struct machine *machine = &kmem_session->machines.host;
+       regex_t alloc_func_regex;
+       const char pattern[] = "^_?_?(alloc|get_free|get_zeroed)_pages?";
+
+       ret = regcomp(&alloc_func_regex, pattern, REG_EXTENDED);
+       if (ret) {
+               char err[BUFSIZ];
+
+               regerror(ret, &alloc_func_regex, err, sizeof(err));
+               pr_err("Invalid regex: %s\n%s", pattern, err);
+               return -EINVAL;
+       }
+
+       kernel_map = machine->vmlinux_maps[MAP__FUNCTION];
+       if (map__load(kernel_map, NULL) < 0) {
+               pr_err("cannot load kernel map\n");
+               return -ENOENT;
+       }
+
+       map__for_each_symbol(kernel_map, sym, node) {
+               if (regexec(&alloc_func_regex, sym->name, 0, NULL, 0))
+                       continue;
+
+               func = realloc(alloc_func_list,
+                              (nr_alloc_funcs + 1) * sizeof(*func));
+               if (func == NULL)
+                       return -ENOMEM;
+
+               pr_debug("alloc func: %s\n", sym->name);
+               func[nr_alloc_funcs].start = sym->start;
+               func[nr_alloc_funcs].end   = sym->end;
+               func[nr_alloc_funcs].name  = sym->name;
+
+               alloc_func_list = func;
+               nr_alloc_funcs++;
+       }
+
+       qsort(alloc_func_list, nr_alloc_funcs, sizeof(*func), funcmp);
+
+       regfree(&alloc_func_regex);
+       return 0;
+}
+
+/*
+ * Find first non-memory allocation function from callchain.
+ * The allocation functions are in the 'alloc_func_list'.
+ */
+static u64 find_callsite(struct perf_evsel *evsel, struct perf_sample *sample)
+{
+       struct addr_location al;
+       struct machine *machine = &kmem_session->machines.host;
+       struct callchain_cursor_node *node;
+
+       if (alloc_func_list == NULL) {
+               if (build_alloc_func_list() < 0)
+                       goto out;
+       }
+
+       al.thread = machine__findnew_thread(machine, sample->pid, sample->tid);
+       sample__resolve_callchain(sample, NULL, evsel, &al, 16);
+
+       callchain_cursor_commit(&callchain_cursor);
+       while (true) {
+               struct alloc_func key, *caller;
+               u64 addr;
+
+               node = callchain_cursor_current(&callchain_cursor);
+               if (node == NULL)
+                       break;
+
+               key.start = key.end = node->ip;
+               caller = bsearch(&key, alloc_func_list, nr_alloc_funcs,
+                                sizeof(key), callcmp);
+               if (!caller) {
+                       /* found */
+                       if (node->map)
+                               addr = map__unmap_ip(node->map, node->ip);
+                       else
+                               addr = node->ip;
+
+                       return addr;
+               } else
+                       pr_debug3("skipping alloc function: %s\n", caller->name);
+
+               callchain_cursor_advance(&callchain_cursor);
+       }
+
+out:
+       pr_debug2("unknown callsite: %"PRIx64 "\n", sample->ip);
+       return sample->ip;
+}
+
+struct sort_dimension {
+       const char              name[20];
+       sort_fn_t               cmp;
+       struct list_head        list;
+};
+
+static LIST_HEAD(page_alloc_sort_input);
+static LIST_HEAD(page_caller_sort_input);
+
+static struct page_stat *
+__page_stat__findnew_page(struct page_stat *pstat, bool create)
+{
+       struct rb_node **node = &page_live_tree.rb_node;
        struct rb_node *parent = NULL;
        struct page_stat *data;
 
@@ -275,7 +430,7 @@ static struct page_stat *search_page(unsigned long page, bool create)
                parent = *node;
                data = rb_entry(*node, struct page_stat, node);
 
-               cmp = data->page - page;
+               cmp = data->page - pstat->page;
                if (cmp < 0)
                        node = &parent->rb_left;
                else if (cmp > 0)
@@ -289,49 +444,48 @@ static struct page_stat *search_page(unsigned long page, bool create)
 
        data = zalloc(sizeof(*data));
        if (data != NULL) {
-               data->page = page;
+               data->page = pstat->page;
+               data->order = pstat->order;
+               data->gfp_flags = pstat->gfp_flags;
+               data->migrate_type = pstat->migrate_type;
 
                rb_link_node(&data->node, parent, node);
-               rb_insert_color(&data->node, &page_tree);
+               rb_insert_color(&data->node, &page_live_tree);
        }
 
        return data;
 }
 
-static int page_stat_cmp(struct page_stat *a, struct page_stat *b)
+static struct page_stat *page_stat__find_page(struct page_stat *pstat)
 {
-       if (a->page > b->page)
-               return -1;
-       if (a->page < b->page)
-               return 1;
-       if (a->order > b->order)
-               return -1;
-       if (a->order < b->order)
-               return 1;
-       if (a->migrate_type > b->migrate_type)
-               return -1;
-       if (a->migrate_type < b->migrate_type)
-               return 1;
-       if (a->gfp_flags > b->gfp_flags)
-               return -1;
-       if (a->gfp_flags < b->gfp_flags)
-               return 1;
-       return 0;
+       return __page_stat__findnew_page(pstat, false);
+}
+
+static struct page_stat *page_stat__findnew_page(struct page_stat *pstat)
+{
+       return __page_stat__findnew_page(pstat, true);
 }
 
-static struct page_stat *search_page_alloc_stat(struct page_stat *pstat, bool create)
+static struct page_stat *
+__page_stat__findnew_alloc(struct page_stat *pstat, bool create)
 {
        struct rb_node **node = &page_alloc_tree.rb_node;
        struct rb_node *parent = NULL;
        struct page_stat *data;
+       struct sort_dimension *sort;
 
        while (*node) {
-               s64 cmp;
+               int cmp = 0;
 
                parent = *node;
                data = rb_entry(*node, struct page_stat, node);
 
-               cmp = page_stat_cmp(data, pstat);
+               list_for_each_entry(sort, &page_alloc_sort_input, list) {
+                       cmp = sort->cmp(pstat, data);
+                       if (cmp)
+                               break;
+               }
+
                if (cmp < 0)
                        node = &parent->rb_left;
                else if (cmp > 0)
@@ -357,6 +511,71 @@ static struct page_stat *search_page_alloc_stat(struct page_stat *pstat, bool cr
        return data;
 }
 
+static struct page_stat *page_stat__find_alloc(struct page_stat *pstat)
+{
+       return __page_stat__findnew_alloc(pstat, false);
+}
+
+static struct page_stat *page_stat__findnew_alloc(struct page_stat *pstat)
+{
+       return __page_stat__findnew_alloc(pstat, true);
+}
+
+static struct page_stat *
+__page_stat__findnew_caller(struct page_stat *pstat, bool create)
+{
+       struct rb_node **node = &page_caller_tree.rb_node;
+       struct rb_node *parent = NULL;
+       struct page_stat *data;
+       struct sort_dimension *sort;
+
+       while (*node) {
+               int cmp = 0;
+
+               parent = *node;
+               data = rb_entry(*node, struct page_stat, node);
+
+               list_for_each_entry(sort, &page_caller_sort_input, list) {
+                       cmp = sort->cmp(pstat, data);
+                       if (cmp)
+                               break;
+               }
+
+               if (cmp < 0)
+                       node = &parent->rb_left;
+               else if (cmp > 0)
+                       node = &parent->rb_right;
+               else
+                       return data;
+       }
+
+       if (!create)
+               return NULL;
+
+       data = zalloc(sizeof(*data));
+       if (data != NULL) {
+               data->callsite = pstat->callsite;
+               data->order = pstat->order;
+               data->gfp_flags = pstat->gfp_flags;
+               data->migrate_type = pstat->migrate_type;
+
+               rb_link_node(&data->node, parent, node);
+               rb_insert_color(&data->node, &page_caller_tree);
+       }
+
+       return data;
+}
+
+static struct page_stat *page_stat__find_caller(struct page_stat *pstat)
+{
+       return __page_stat__findnew_caller(pstat, false);
+}
+
+static struct page_stat *page_stat__findnew_caller(struct page_stat *pstat)
+{
+       return __page_stat__findnew_caller(pstat, true);
+}
+
 static bool valid_page(u64 pfn_or_page)
 {
        if (use_pfn && pfn_or_page == -1UL)
@@ -366,6 +585,176 @@ static bool valid_page(u64 pfn_or_page)
        return true;
 }
 
+struct gfp_flag {
+       unsigned int flags;
+       char *compact_str;
+       char *human_readable;
+};
+
+static struct gfp_flag *gfps;
+static int nr_gfps;
+
+static int gfpcmp(const void *a, const void *b)
+{
+       const struct gfp_flag *fa = a;
+       const struct gfp_flag *fb = b;
+
+       return fa->flags - fb->flags;
+}
+
+/* see include/trace/events/gfpflags.h */
+static const struct {
+       const char *original;
+       const char *compact;
+} gfp_compact_table[] = {
+       { "GFP_TRANSHUGE",              "THP" },
+       { "GFP_HIGHUSER_MOVABLE",       "HUM" },
+       { "GFP_HIGHUSER",               "HU" },
+       { "GFP_USER",                   "U" },
+       { "GFP_TEMPORARY",              "TMP" },
+       { "GFP_KERNEL",                 "K" },
+       { "GFP_NOFS",                   "NF" },
+       { "GFP_ATOMIC",                 "A" },
+       { "GFP_NOIO",                   "NI" },
+       { "GFP_HIGH",                   "H" },
+       { "GFP_WAIT",                   "W" },
+       { "GFP_IO",                     "I" },
+       { "GFP_COLD",                   "CO" },
+       { "GFP_NOWARN",                 "NWR" },
+       { "GFP_REPEAT",                 "R" },
+       { "GFP_NOFAIL",                 "NF" },
+       { "GFP_NORETRY",                "NR" },
+       { "GFP_COMP",                   "C" },
+       { "GFP_ZERO",                   "Z" },
+       { "GFP_NOMEMALLOC",             "NMA" },
+       { "GFP_MEMALLOC",               "MA" },
+       { "GFP_HARDWALL",               "HW" },
+       { "GFP_THISNODE",               "TN" },
+       { "GFP_RECLAIMABLE",            "RC" },
+       { "GFP_MOVABLE",                "M" },
+       { "GFP_NOTRACK",                "NT" },
+       { "GFP_NO_KSWAPD",              "NK" },
+       { "GFP_OTHER_NODE",             "ON" },
+       { "GFP_NOWAIT",                 "NW" },
+};
+
+static size_t max_gfp_len;
+
+static char *compact_gfp_flags(char *gfp_flags)
+{
+       char *orig_flags = strdup(gfp_flags);
+       char *new_flags = NULL;
+       char *str, *pos = NULL;
+       size_t len = 0;
+
+       if (orig_flags == NULL)
+               return NULL;
+
+       str = strtok_r(orig_flags, "|", &pos);
+       while (str) {
+               size_t i;
+               char *new;
+               const char *cpt;
+
+               for (i = 0; i < ARRAY_SIZE(gfp_compact_table); i++) {
+                       if (strcmp(gfp_compact_table[i].original, str))
+                               continue;
+
+                       cpt = gfp_compact_table[i].compact;
+                       new = realloc(new_flags, len + strlen(cpt) + 2);
+                       if (new == NULL) {
+                               free(new_flags);
+                               return NULL;
+                       }
+
+                       new_flags = new;
+
+                       if (!len) {
+                               strcpy(new_flags, cpt);
+                       } else {
+                               strcat(new_flags, "|");
+                               strcat(new_flags, cpt);
+                               len++;
+                       }
+
+                       len += strlen(cpt);
+               }
+
+               str = strtok_r(NULL, "|", &pos);
+       }
+
+       if (max_gfp_len < len)
+               max_gfp_len = len;
+
+       free(orig_flags);
+       return new_flags;
+}
+
+static char *compact_gfp_string(unsigned long gfp_flags)
+{
+       struct gfp_flag key = {
+               .flags = gfp_flags,
+       };
+       struct gfp_flag *gfp;
+
+       gfp = bsearch(&key, gfps, nr_gfps, sizeof(*gfps), gfpcmp);
+       if (gfp)
+               return gfp->compact_str;
+
+       return NULL;
+}
+
+static int parse_gfp_flags(struct perf_evsel *evsel, struct perf_sample *sample,
+                          unsigned int gfp_flags)
+{
+       struct pevent_record record = {
+               .cpu = sample->cpu,
+               .data = sample->raw_data,
+               .size = sample->raw_size,
+       };
+       struct trace_seq seq;
+       char *str, *pos = NULL;
+
+       if (nr_gfps) {
+               struct gfp_flag key = {
+                       .flags = gfp_flags,
+               };
+
+               if (bsearch(&key, gfps, nr_gfps, sizeof(*gfps), gfpcmp))
+                       return 0;
+       }
+
+       trace_seq_init(&seq);
+       pevent_event_info(&seq, evsel->tp_format, &record);
+
+       str = strtok_r(seq.buffer, " ", &pos);
+       while (str) {
+               if (!strncmp(str, "gfp_flags=", 10)) {
+                       struct gfp_flag *new;
+
+                       new = realloc(gfps, (nr_gfps + 1) * sizeof(*gfps));
+                       if (new == NULL)
+                               return -ENOMEM;
+
+                       gfps = new;
+                       new += nr_gfps++;
+
+                       new->flags = gfp_flags;
+                       new->human_readable = strdup(str + 10);
+                       new->compact_str = compact_gfp_flags(str + 10);
+                       if (!new->human_readable || !new->compact_str)
+                               return -ENOMEM;
+
+                       qsort(gfps, nr_gfps, sizeof(*gfps), gfpcmp);
+               }
+
+               str = strtok_r(NULL, " ", &pos);
+       }
+
+       trace_seq_destroy(&seq);
+       return 0;
+}
+
 static int perf_evsel__process_page_alloc_event(struct perf_evsel *evsel,
                                                struct perf_sample *sample)
 {
@@ -375,6 +764,7 @@ static int perf_evsel__process_page_alloc_event(struct perf_evsel *evsel,
        unsigned int migrate_type = perf_evsel__intval(evsel, sample,
                                                       "migratetype");
        u64 bytes = kmem_page_size << order;
+       u64 callsite;
        struct page_stat *pstat;
        struct page_stat this = {
                .order = order,
@@ -397,20 +787,36 @@ static int perf_evsel__process_page_alloc_event(struct perf_evsel *evsel,
                return 0;
        }
 
+       if (parse_gfp_flags(evsel, sample, gfp_flags) < 0)
+               return -1;
+
+       callsite = find_callsite(evsel, sample);
+
        /*
         * This is to find the current page (with correct gfp flags and
         * migrate type) at free event.
         */
-       pstat = search_page(page, true);
+       this.page = page;
+       pstat = page_stat__findnew_page(&this);
        if (pstat == NULL)
                return -ENOMEM;
 
-       pstat->order = order;
-       pstat->gfp_flags = gfp_flags;
-       pstat->migrate_type = migrate_type;
+       pstat->nr_alloc++;
+       pstat->alloc_bytes += bytes;
+       pstat->callsite = callsite;
+
+       if (!live_page) {
+               pstat = page_stat__findnew_alloc(&this);
+               if (pstat == NULL)
+                       return -ENOMEM;
 
-       this.page = page;
-       pstat = search_page_alloc_stat(&this, true);
+               pstat->nr_alloc++;
+               pstat->alloc_bytes += bytes;
+               pstat->callsite = callsite;
+       }
+
+       this.callsite = callsite;
+       pstat = page_stat__findnew_caller(&this);
        if (pstat == NULL)
                return -ENOMEM;
 
@@ -441,7 +847,8 @@ static int perf_evsel__process_page_free_event(struct perf_evsel *evsel,
        nr_page_frees++;
        total_page_free_bytes += bytes;
 
-       pstat = search_page(page, false);
+       this.page = page;
+       pstat = page_stat__find_page(&this);
        if (pstat == NULL) {
                pr_debug2("missing free at page %"PRIx64" (order: %d)\n",
                          page, order);
@@ -452,20 +859,41 @@ static int perf_evsel__process_page_free_event(struct perf_evsel *evsel,
                return 0;
        }
 
-       this.page = page;
        this.gfp_flags = pstat->gfp_flags;
        this.migrate_type = pstat->migrate_type;
+       this.callsite = pstat->callsite;
 
-       rb_erase(&pstat->node, &page_tree);
+       rb_erase(&pstat->node, &page_live_tree);
        free(pstat);
 
-       pstat = search_page_alloc_stat(&this, false);
+       if (live_page) {
+               order_stats[this.order][this.migrate_type]--;
+       } else {
+               pstat = page_stat__find_alloc(&this);
+               if (pstat == NULL)
+                       return -ENOMEM;
+
+               pstat->nr_free++;
+               pstat->free_bytes += bytes;
+       }
+
+       pstat = page_stat__find_caller(&this);
        if (pstat == NULL)
                return -ENOENT;
 
        pstat->nr_free++;
        pstat->free_bytes += bytes;
 
+       if (live_page) {
+               pstat->nr_alloc--;
+               pstat->alloc_bytes -= bytes;
+
+               if (pstat->nr_alloc == 0) {
+                       rb_erase(&pstat->node, &page_caller_tree);
+                       free(pstat);
+               }
+       }
+
        return 0;
 }
 
@@ -478,6 +906,7 @@ static int process_sample_event(struct perf_tool *tool __maybe_unused,
                                struct perf_evsel *evsel,
                                struct machine *machine)
 {
+       int err = 0;
        struct thread *thread = machine__findnew_thread(machine, sample->pid,
                                                        sample->tid);
 
@@ -491,10 +920,12 @@ static int process_sample_event(struct perf_tool *tool __maybe_unused,
 
        if (evsel->handler != NULL) {
                tracepoint_handler f = evsel->handler;
-               return f(evsel, sample);
+               err = f(evsel, sample);
        }
 
-       return 0;
+       thread__put(thread);
+
+       return err;
 }
 
 static struct perf_tool perf_kmem = {
@@ -576,41 +1007,111 @@ static const char * const migrate_type_str[] = {
        "UNKNOWN",
 };
 
-static void __print_page_result(struct rb_root *root,
-                               struct perf_session *session __maybe_unused,
-                               int n_lines)
+static void __print_page_alloc_result(struct perf_session *session, int n_lines)
 {
-       struct rb_node *next = rb_first(root);
+       struct rb_node *next = rb_first(&page_alloc_sorted);
+       struct machine *machine = &session->machines.host;
        const char *format;
+       int gfp_len = max(strlen("GFP flags"), max_gfp_len);
 
-       printf("\n%.80s\n", graph_dotted_line);
-       printf(" %-16s | Total alloc (KB) | Hits      | Order | Mig.type | GFP flags\n",
-              use_pfn ? "PFN" : "Page");
-       printf("%.80s\n", graph_dotted_line);
+       printf("\n%.105s\n", graph_dotted_line);
+       printf(" %-16s | %5s alloc (KB) | Hits      | Order | Mig.type | %-*s | Callsite\n",
+              use_pfn ? "PFN" : "Page", live_page ? "Live" : "Total",
+              gfp_len, "GFP flags");
+       printf("%.105s\n", graph_dotted_line);
 
        if (use_pfn)
-               format = " %16llu | %'16llu | %'9d | %5d | %8s |  %08lx\n";
+               format = " %16llu | %'16llu | %'9d | %5d | %8s | %-*s | %s\n";
        else
-               format = " %016llx | %'16llu | %'9d | %5d | %8s |  %08lx\n";
+               format = " %016llx | %'16llu | %'9d | %5d | %8s | %-*s | %s\n";
 
        while (next && n_lines--) {
                struct page_stat *data;
+               struct symbol *sym;
+               struct map *map;
+               char buf[32];
+               char *caller = buf;
 
                data = rb_entry(next, struct page_stat, node);
+               sym = machine__find_kernel_function(machine, data->callsite,
+                                                   &map, NULL);
+               if (sym && sym->name)
+                       caller = sym->name;
+               else
+                       scnprintf(buf, sizeof(buf), "%"PRIx64, data->callsite);
 
                printf(format, (unsigned long long)data->page,
                       (unsigned long long)data->alloc_bytes / 1024,
                       data->nr_alloc, data->order,
                       migrate_type_str[data->migrate_type],
-                      (unsigned long)data->gfp_flags);
+                      gfp_len, compact_gfp_string(data->gfp_flags), caller);
 
                next = rb_next(next);
        }
 
-       if (n_lines == -1)
-               printf(" ...              | ...              | ...       | ...   | ...      | ...     \n");
+       if (n_lines == -1) {
+               printf(" ...              | ...              | ...       | ...   | ...      | %-*s | ...\n",
+                      gfp_len, "...");
+       }
+
+       printf("%.105s\n", graph_dotted_line);
+}
+
+static void __print_page_caller_result(struct perf_session *session, int n_lines)
+{
+       struct rb_node *next = rb_first(&page_caller_sorted);
+       struct machine *machine = &session->machines.host;
+       int gfp_len = max(strlen("GFP flags"), max_gfp_len);
+
+       printf("\n%.105s\n", graph_dotted_line);
+       printf(" %5s alloc (KB) | Hits      | Order | Mig.type | %-*s | Callsite\n",
+              live_page ? "Live" : "Total", gfp_len, "GFP flags");
+       printf("%.105s\n", graph_dotted_line);
+
+       while (next && n_lines--) {
+               struct page_stat *data;
+               struct symbol *sym;
+               struct map *map;
+               char buf[32];
+               char *caller = buf;
+
+               data = rb_entry(next, struct page_stat, node);
+               sym = machine__find_kernel_function(machine, data->callsite,
+                                                   &map, NULL);
+               if (sym && sym->name)
+                       caller = sym->name;
+               else
+                       scnprintf(buf, sizeof(buf), "%"PRIx64, data->callsite);
+
+               printf(" %'16llu | %'9d | %5d | %8s | %-*s | %s\n",
+                      (unsigned long long)data->alloc_bytes / 1024,
+                      data->nr_alloc, data->order,
+                      migrate_type_str[data->migrate_type],
+                      gfp_len, compact_gfp_string(data->gfp_flags), caller);
+
+               next = rb_next(next);
+       }
+
+       if (n_lines == -1) {
+               printf(" ...              | ...       | ...   | ...      | %-*s | ...\n",
+                      gfp_len, "...");
+       }
 
-       printf("%.80s\n", graph_dotted_line);
+       printf("%.105s\n", graph_dotted_line);
+}
+
+static void print_gfp_flags(void)
+{
+       int i;
+
+       printf("#\n");
+       printf("# GFP flags\n");
+       printf("# ---------\n");
+       for (i = 0; i < nr_gfps; i++) {
+               printf("# %08x: %*s: %s\n", gfps[i].flags,
+                      (int) max_gfp_len, gfps[i].compact_str,
+                      gfps[i].human_readable);
+       }
 }
 
 static void print_slab_summary(void)
@@ -682,8 +1183,12 @@ static void print_slab_result(struct perf_session *session)
 
 static void print_page_result(struct perf_session *session)
 {
+       if (caller_flag || alloc_flag)
+               print_gfp_flags();
+       if (caller_flag)
+               __print_page_caller_result(session, caller_lines);
        if (alloc_flag)
-               __print_page_result(&page_alloc_sorted, session, alloc_lines);
+               __print_page_alloc_result(session, alloc_lines);
        print_page_summary();
 }
 
@@ -695,14 +1200,10 @@ static void print_result(struct perf_session *session)
                print_page_result(session);
 }
 
-struct sort_dimension {
-       const char              name[20];
-       sort_fn_t               cmp;
-       struct list_head        list;
-};
-
-static LIST_HEAD(caller_sort);
-static LIST_HEAD(alloc_sort);
+static LIST_HEAD(slab_caller_sort);
+static LIST_HEAD(slab_alloc_sort);
+static LIST_HEAD(page_caller_sort);
+static LIST_HEAD(page_alloc_sort);
 
 static void sort_slab_insert(struct rb_root *root, struct alloc_stat *data,
                             struct list_head *sort_list)
@@ -751,10 +1252,12 @@ static void __sort_slab_result(struct rb_root *root, struct rb_root *root_sorted
        }
 }
 
-static void sort_page_insert(struct rb_root *root, struct page_stat *data)
+static void sort_page_insert(struct rb_root *root, struct page_stat *data,
+                            struct list_head *sort_list)
 {
        struct rb_node **new = &root->rb_node;
        struct rb_node *parent = NULL;
+       struct sort_dimension *sort;
 
        while (*new) {
                struct page_stat *this;
@@ -763,8 +1266,11 @@ static void sort_page_insert(struct rb_root *root, struct page_stat *data)
                this = rb_entry(*new, struct page_stat, node);
                parent = *new;
 
-               /* TODO: support more sort key */
-               cmp = data->alloc_bytes - this->alloc_bytes;
+               list_for_each_entry(sort, sort_list, list) {
+                       cmp = sort->cmp(data, this);
+                       if (cmp)
+                               break;
+               }
 
                if (cmp > 0)
                        new = &parent->rb_left;
@@ -776,7 +1282,8 @@ static void sort_page_insert(struct rb_root *root, struct page_stat *data)
        rb_insert_color(&data->node, root);
 }
 
-static void __sort_page_result(struct rb_root *root, struct rb_root *root_sorted)
+static void __sort_page_result(struct rb_root *root, struct rb_root *root_sorted,
+                              struct list_head *sort_list)
 {
        struct rb_node *node;
        struct page_stat *data;
@@ -788,7 +1295,7 @@ static void __sort_page_result(struct rb_root *root, struct rb_root *root_sorted
 
                rb_erase(node, root);
                data = rb_entry(node, struct page_stat, node);
-               sort_page_insert(root_sorted, data);
+               sort_page_insert(root_sorted, data, sort_list);
        }
 }
 
@@ -796,12 +1303,20 @@ static void sort_result(void)
 {
        if (kmem_slab) {
                __sort_slab_result(&root_alloc_stat, &root_alloc_sorted,
-                                  &alloc_sort);
+                                  &slab_alloc_sort);
                __sort_slab_result(&root_caller_stat, &root_caller_sorted,
-                                  &caller_sort);
+                                  &slab_caller_sort);
        }
        if (kmem_page) {
-               __sort_page_result(&page_alloc_tree, &page_alloc_sorted);
+               if (live_page)
+                       __sort_page_result(&page_live_tree, &page_alloc_sorted,
+                                          &page_alloc_sort);
+               else
+                       __sort_page_result(&page_alloc_tree, &page_alloc_sorted,
+                                          &page_alloc_sort);
+
+               __sort_page_result(&page_caller_tree, &page_caller_sorted,
+                                  &page_caller_sort);
        }
 }
 
@@ -850,8 +1365,12 @@ out:
        return err;
 }
 
-static int ptr_cmp(struct alloc_stat *l, struct alloc_stat *r)
+/* slab sort keys */
+static int ptr_cmp(void *a, void *b)
 {
+       struct alloc_stat *l = a;
+       struct alloc_stat *r = b;
+
        if (l->ptr < r->ptr)
                return -1;
        else if (l->ptr > r->ptr)
@@ -864,8 +1383,11 @@ static struct sort_dimension ptr_sort_dimension = {
        .cmp    = ptr_cmp,
 };
 
-static int callsite_cmp(struct alloc_stat *l, struct alloc_stat *r)
+static int slab_callsite_cmp(void *a, void *b)
 {
+       struct alloc_stat *l = a;
+       struct alloc_stat *r = b;
+
        if (l->call_site < r->call_site)
                return -1;
        else if (l->call_site > r->call_site)
@@ -875,11 +1397,14 @@ static int callsite_cmp(struct alloc_stat *l, struct alloc_stat *r)
 
 static struct sort_dimension callsite_sort_dimension = {
        .name   = "callsite",
-       .cmp    = callsite_cmp,
+       .cmp    = slab_callsite_cmp,
 };
 
-static int hit_cmp(struct alloc_stat *l, struct alloc_stat *r)
+static int hit_cmp(void *a, void *b)
 {
+       struct alloc_stat *l = a;
+       struct alloc_stat *r = b;
+
        if (l->hit < r->hit)
                return -1;
        else if (l->hit > r->hit)
@@ -892,8 +1417,11 @@ static struct sort_dimension hit_sort_dimension = {
        .cmp    = hit_cmp,
 };
 
-static int bytes_cmp(struct alloc_stat *l, struct alloc_stat *r)
+static int bytes_cmp(void *a, void *b)
 {
+       struct alloc_stat *l = a;
+       struct alloc_stat *r = b;
+
        if (l->bytes_alloc < r->bytes_alloc)
                return -1;
        else if (l->bytes_alloc > r->bytes_alloc)
@@ -906,9 +1434,11 @@ static struct sort_dimension bytes_sort_dimension = {
        .cmp    = bytes_cmp,
 };
 
-static int frag_cmp(struct alloc_stat *l, struct alloc_stat *r)
+static int frag_cmp(void *a, void *b)
 {
        double x, y;
+       struct alloc_stat *l = a;
+       struct alloc_stat *r = b;
 
        x = fragmentation(l->bytes_req, l->bytes_alloc);
        y = fragmentation(r->bytes_req, r->bytes_alloc);
@@ -925,8 +1455,11 @@ static struct sort_dimension frag_sort_dimension = {
        .cmp    = frag_cmp,
 };
 
-static int pingpong_cmp(struct alloc_stat *l, struct alloc_stat *r)
+static int pingpong_cmp(void *a, void *b)
 {
+       struct alloc_stat *l = a;
+       struct alloc_stat *r = b;
+
        if (l->pingpong < r->pingpong)
                return -1;
        else if (l->pingpong > r->pingpong)
@@ -939,7 +1472,135 @@ static struct sort_dimension pingpong_sort_dimension = {
        .cmp    = pingpong_cmp,
 };
 
-static struct sort_dimension *avail_sorts[] = {
+/* page sort keys */
+static int page_cmp(void *a, void *b)
+{
+       struct page_stat *l = a;
+       struct page_stat *r = b;
+
+       if (l->page < r->page)
+               return -1;
+       else if (l->page > r->page)
+               return 1;
+       return 0;
+}
+
+static struct sort_dimension page_sort_dimension = {
+       .name   = "page",
+       .cmp    = page_cmp,
+};
+
+static int page_callsite_cmp(void *a, void *b)
+{
+       struct page_stat *l = a;
+       struct page_stat *r = b;
+
+       if (l->callsite < r->callsite)
+               return -1;
+       else if (l->callsite > r->callsite)
+               return 1;
+       return 0;
+}
+
+static struct sort_dimension page_callsite_sort_dimension = {
+       .name   = "callsite",
+       .cmp    = page_callsite_cmp,
+};
+
+static int page_hit_cmp(void *a, void *b)
+{
+       struct page_stat *l = a;
+       struct page_stat *r = b;
+
+       if (l->nr_alloc < r->nr_alloc)
+               return -1;
+       else if (l->nr_alloc > r->nr_alloc)
+               return 1;
+       return 0;
+}
+
+static struct sort_dimension page_hit_sort_dimension = {
+       .name   = "hit",
+       .cmp    = page_hit_cmp,
+};
+
+static int page_bytes_cmp(void *a, void *b)
+{
+       struct page_stat *l = a;
+       struct page_stat *r = b;
+
+       if (l->alloc_bytes < r->alloc_bytes)
+               return -1;
+       else if (l->alloc_bytes > r->alloc_bytes)
+               return 1;
+       return 0;
+}
+
+static struct sort_dimension page_bytes_sort_dimension = {
+       .name   = "bytes",
+       .cmp    = page_bytes_cmp,
+};
+
+static int page_order_cmp(void *a, void *b)
+{
+       struct page_stat *l = a;
+       struct page_stat *r = b;
+
+       if (l->order < r->order)
+               return -1;
+       else if (l->order > r->order)
+               return 1;
+       return 0;
+}
+
+static struct sort_dimension page_order_sort_dimension = {
+       .name   = "order",
+       .cmp    = page_order_cmp,
+};
+
+static int migrate_type_cmp(void *a, void *b)
+{
+       struct page_stat *l = a;
+       struct page_stat *r = b;
+
+       /* for internal use to find free'd page */
+       if (l->migrate_type == -1U)
+               return 0;
+
+       if (l->migrate_type < r->migrate_type)
+               return -1;
+       else if (l->migrate_type > r->migrate_type)
+               return 1;
+       return 0;
+}
+
+static struct sort_dimension migrate_type_sort_dimension = {
+       .name   = "migtype",
+       .cmp    = migrate_type_cmp,
+};
+
+static int gfp_flags_cmp(void *a, void *b)
+{
+       struct page_stat *l = a;
+       struct page_stat *r = b;
+
+       /* for internal use to find free'd page */
+       if (l->gfp_flags == -1U)
+               return 0;
+
+       if (l->gfp_flags < r->gfp_flags)
+               return -1;
+       else if (l->gfp_flags > r->gfp_flags)
+               return 1;
+       return 0;
+}
+
+static struct sort_dimension gfp_flags_sort_dimension = {
+       .name   = "gfp",
+       .cmp    = gfp_flags_cmp,
+};
+
+static struct sort_dimension *slab_sorts[] = {
        &ptr_sort_dimension,
        &callsite_sort_dimension,
        &hit_sort_dimension,
@@ -948,16 +1609,44 @@ static struct sort_dimension *avail_sorts[] = {
        &pingpong_sort_dimension,
 };
 
-#define NUM_AVAIL_SORTS        ((int)ARRAY_SIZE(avail_sorts))
+static struct sort_dimension *page_sorts[] = {
+       &page_sort_dimension,
+       &page_callsite_sort_dimension,
+       &page_hit_sort_dimension,
+       &page_bytes_sort_dimension,
+       &page_order_sort_dimension,
+       &migrate_type_sort_dimension,
+       &gfp_flags_sort_dimension,
+};
+
+static int slab_sort_dimension__add(const char *tok, struct list_head *list)
+{
+       struct sort_dimension *sort;
+       int i;
+
+       for (i = 0; i < (int)ARRAY_SIZE(slab_sorts); i++) {
+               if (!strcmp(slab_sorts[i]->name, tok)) {
+                       sort = memdup(slab_sorts[i], sizeof(*slab_sorts[i]));
+                       if (!sort) {
+                               pr_err("%s: memdup failed\n", __func__);
+                               return -1;
+                       }
+                       list_add_tail(&sort->list, list);
+                       return 0;
+               }
+       }
+
+       return -1;
+}
 
-static int sort_dimension__add(const char *tok, struct list_head *list)
+static int page_sort_dimension__add(const char *tok, struct list_head *list)
 {
        struct sort_dimension *sort;
        int i;
 
-       for (i = 0; i < NUM_AVAIL_SORTS; i++) {
-               if (!strcmp(avail_sorts[i]->name, tok)) {
-                       sort = memdup(avail_sorts[i], sizeof(*avail_sorts[i]));
+       for (i = 0; i < (int)ARRAY_SIZE(page_sorts); i++) {
+               if (!strcmp(page_sorts[i]->name, tok)) {
+                       sort = memdup(page_sorts[i], sizeof(*page_sorts[i]));
                        if (!sort) {
                                pr_err("%s: memdup failed\n", __func__);
                                return -1;
@@ -970,7 +1659,33 @@ static int sort_dimension__add(const char *tok, struct list_head *list)
        return -1;
 }
 
-static int setup_sorting(struct list_head *sort_list, const char *arg)
+static int setup_slab_sorting(struct list_head *sort_list, const char *arg)
+{
+       char *tok;
+       char *str = strdup(arg);
+       char *pos = str;
+
+       if (!str) {
+               pr_err("%s: strdup failed\n", __func__);
+               return -1;
+       }
+
+       while (true) {
+               tok = strsep(&pos, ",");
+               if (!tok)
+                       break;
+               if (slab_sort_dimension__add(tok, sort_list) < 0) {
+                       error("Unknown slab --sort key: '%s'", tok);
+                       free(str);
+                       return -1;
+               }
+       }
+
+       free(str);
+       return 0;
+}
+
+static int setup_page_sorting(struct list_head *sort_list, const char *arg)
 {
        char *tok;
        char *str = strdup(arg);
@@ -985,8 +1700,8 @@ static int setup_sorting(struct list_head *sort_list, const char *arg)
                tok = strsep(&pos, ",");
                if (!tok)
                        break;
-               if (sort_dimension__add(tok, sort_list) < 0) {
-                       error("Unknown --sort key: '%s'", tok);
+               if (page_sort_dimension__add(tok, sort_list) < 0) {
+                       error("Unknown page --sort key: '%s'", tok);
                        free(str);
                        return -1;
                }
@@ -1002,10 +1717,18 @@ static int parse_sort_opt(const struct option *opt __maybe_unused,
        if (!arg)
                return -1;
 
-       if (caller_flag > alloc_flag)
-               return setup_sorting(&caller_sort, arg);
-       else
-               return setup_sorting(&alloc_sort, arg);
+       if (kmem_page > kmem_slab ||
+           (kmem_page == 0 && kmem_slab == 0 && kmem_default == KMEM_PAGE)) {
+               if (caller_flag > alloc_flag)
+                       return setup_page_sorting(&page_caller_sort, arg);
+               else
+                       return setup_page_sorting(&page_alloc_sort, arg);
+       } else {
+               if (caller_flag > alloc_flag)
+                       return setup_slab_sorting(&slab_caller_sort, arg);
+               else
+                       return setup_slab_sorting(&slab_alloc_sort, arg);
+       }
 
        return 0;
 }
@@ -1084,7 +1807,7 @@ static int __cmd_record(int argc, const char **argv)
        if (kmem_slab)
                rec_argc += ARRAY_SIZE(slab_events);
        if (kmem_page)
-               rec_argc += ARRAY_SIZE(page_events);
+               rec_argc += ARRAY_SIZE(page_events) + 1; /* for -g */
 
        rec_argv = calloc(rec_argc + 1, sizeof(char *));
 
@@ -1099,6 +1822,8 @@ static int __cmd_record(int argc, const char **argv)
                        rec_argv[i] = strdup(slab_events[j]);
        }
        if (kmem_page) {
+               rec_argv[i++] = strdup("-g");
+
                for (j = 0; j < ARRAY_SIZE(page_events); j++, i++)
                        rec_argv[i] = strdup(page_events[j]);
        }
@@ -1109,9 +1834,26 @@ static int __cmd_record(int argc, const char **argv)
        return cmd_record(i, rec_argv, NULL);
 }
 
+static int kmem_config(const char *var, const char *value, void *cb)
+{
+       if (!strcmp(var, "kmem.default")) {
+               if (!strcmp(value, "slab"))
+                       kmem_default = KMEM_SLAB;
+               else if (!strcmp(value, "page"))
+                       kmem_default = KMEM_PAGE;
+               else
+                       pr_err("invalid default value ('slab' or 'page' required): %s\n",
+                              value);
+               return 0;
+       }
+
+       return perf_default_config(var, value, cb);
+}
+
 int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused)
 {
-       const char * const default_sort_order = "frag,hit,bytes";
+       const char * const default_slab_sort = "frag,hit,bytes";
+       const char * const default_page_sort = "bytes,hit";
        struct perf_data_file file = {
                .mode = PERF_DATA_MODE_READ,
        };
@@ -1124,8 +1866,8 @@ int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused)
        OPT_CALLBACK_NOOPT(0, "alloc", NULL, NULL,
                           "show per-allocation statistics", parse_alloc_opt),
        OPT_CALLBACK('s', "sort", NULL, "key[,key2...]",
-                    "sort by keys: ptr, call_site, bytes, hit, pingpong, frag",
-                    parse_sort_opt),
+                    "sort by keys: ptr, callsite, bytes, hit, pingpong, frag, "
+                    "page, order, migtype, gfp", parse_sort_opt),
        OPT_CALLBACK('l', "line", NULL, "num", "show n lines", parse_line_opt),
        OPT_BOOLEAN(0, "raw-ip", &raw_ip, "show raw ip instead of symbol"),
        OPT_BOOLEAN('f', "force", &file.force, "don't complain, do it"),
@@ -1133,6 +1875,7 @@ int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused)
                           parse_slab_opt),
        OPT_CALLBACK_NOOPT(0, "page", NULL, NULL, "Analyze page allocator",
                           parse_page_opt),
+       OPT_BOOLEAN(0, "live", &live_page, "Show live page stat"),
        OPT_END()
        };
        const char *const kmem_subcommands[] = { "record", "stat", NULL };
@@ -1142,15 +1885,21 @@ int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused)
        };
        struct perf_session *session;
        int ret = -1;
+       const char errmsg[] = "No %s allocation events found.  Have you run 'perf kmem record --%s'?\n";
 
+       perf_config(kmem_config, NULL);
        argc = parse_options_subcommand(argc, argv, kmem_options,
                                        kmem_subcommands, kmem_usage, 0);
 
        if (!argc)
                usage_with_options(kmem_usage, kmem_options);
 
-       if (kmem_slab == 0 && kmem_page == 0)
-               kmem_slab = 1;  /* for backward compatibility */
+       if (kmem_slab == 0 && kmem_page == 0) {
+               if (kmem_default == KMEM_SLAB)
+                       kmem_slab = 1;
+               else
+                       kmem_page = 1;
+       }
 
        if (!strncmp(argv[0], "rec", 3)) {
                symbol__init(NULL);
@@ -1159,19 +1908,30 @@ int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused)
 
        file.path = input_name;
 
-       session = perf_session__new(&file, false, &perf_kmem);
+       kmem_session = session = perf_session__new(&file, false, &perf_kmem);
        if (session == NULL)
                return -1;
 
+       if (kmem_slab) {
+               if (!perf_evlist__find_tracepoint_by_name(session->evlist,
+                                                         "kmem:kmalloc")) {
+                       pr_err(errmsg, "slab", "slab");
+                       return -1;
+               }
+       }
+
        if (kmem_page) {
-               struct perf_evsel *evsel = perf_evlist__first(session->evlist);
+               struct perf_evsel *evsel;
 
-               if (evsel == NULL || evsel->tp_format == NULL) {
-                       pr_err("invalid event found.. aborting\n");
+               evsel = perf_evlist__find_tracepoint_by_name(session->evlist,
+                                                            "kmem:mm_page_alloc");
+               if (evsel == NULL) {
+                       pr_err(errmsg, "page", "page");
                        return -1;
                }
 
                kmem_page_size = pevent_get_page_size(evsel->tp_format->pevent);
+               symbol_conf.use_callchain = true;
        }
 
        symbol__init(&session->header.env);
@@ -1182,11 +1942,21 @@ int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused)
                if (cpu__setup_cpunode_map())
                        goto out_delete;
 
-               if (list_empty(&caller_sort))
-                       setup_sorting(&caller_sort, default_sort_order);
-               if (list_empty(&alloc_sort))
-                       setup_sorting(&alloc_sort, default_sort_order);
-
+               if (list_empty(&slab_caller_sort))
+                       setup_slab_sorting(&slab_caller_sort, default_slab_sort);
+               if (list_empty(&slab_alloc_sort))
+                       setup_slab_sorting(&slab_alloc_sort, default_slab_sort);
+               if (list_empty(&page_caller_sort))
+                       setup_page_sorting(&page_caller_sort, default_page_sort);
+               if (list_empty(&page_alloc_sort))
+                       setup_page_sorting(&page_alloc_sort, default_page_sort);
+
+               if (kmem_page) {
+                       setup_page_sorting(&page_alloc_sort_input,
+                                          "page,order,migtype,gfp");
+                       setup_page_sorting(&page_caller_sort_input,
+                                          "callsite,order,migtype,gfp");
+               }
                ret = __cmd_kmem(session);
        } else
                usage_with_options(kmem_usage, kmem_options);
index 1f9338f6109cdbe79f8f08e510c00e4a171d1f82..74878cd75078055e437396fc9a6b201603586076 100644 (file)
@@ -651,6 +651,7 @@ static int process_sample_event(struct perf_tool *tool,
                                struct perf_evsel *evsel,
                                struct machine *machine)
 {
+       int err = 0;
        struct thread *thread;
        struct perf_kvm_stat *kvm = container_of(tool, struct perf_kvm_stat,
                                                 tool);
@@ -666,9 +667,10 @@ static int process_sample_event(struct perf_tool *tool,
        }
 
        if (!handle_kvm_event(kvm, thread, evsel, sample))
-               return -1;
+               err = -1;
 
-       return 0;
+       thread__put(thread);
+       return err;
 }
 
 static int cpu_isa_config(struct perf_kvm_stat *kvm)
@@ -1309,6 +1311,8 @@ static int kvm_events_live(struct perf_kvm_stat *kvm,
                        "show events other than"
                        " HLT (x86 only) or Wait state (s390 only)"
                        " that take longer than duration usecs"),
+               OPT_UINTEGER(0, "proc-map-timeout", &kvm->opts.proc_map_timeout,
+                               "per thread proc mmap processing timeout in ms"),
                OPT_END()
        };
        const char * const live_usage[] = {
@@ -1336,6 +1340,7 @@ static int kvm_events_live(struct perf_kvm_stat *kvm,
        kvm->opts.target.uses_mmap = false;
        kvm->opts.target.uid_str = NULL;
        kvm->opts.target.uid = UINT_MAX;
+       kvm->opts.proc_map_timeout = 500;
 
        symbol__init(NULL);
        disable_buildid_cache();
@@ -1391,7 +1396,7 @@ static int kvm_events_live(struct perf_kvm_stat *kvm,
        perf_session__set_id_hdr_size(kvm->session);
        ordered_events__set_copy_on_queue(&kvm->session->ordered_events, true);
        machine__synthesize_threads(&kvm->session->machines.host, &kvm->opts.target,
-                                   kvm->evlist->threads, false);
+                                   kvm->evlist->threads, false, kvm->opts.proc_map_timeout);
        err = kvm_live_open_events(kvm);
        if (err)
                goto out;
index d49c2ab85fc2dd1e3c6560391b71f7e3269cf31e..de16aaed516e6016b2a8d887f87727a8179acf19 100644 (file)
@@ -769,6 +769,7 @@ static void dump_threads(void)
                t = perf_session__findnew(session, st->tid);
                pr_info("%10d: %s\n", st->tid, thread__comm_str(t));
                node = rb_next(node);
+               thread__put(t);
        };
 }
 
@@ -810,6 +811,7 @@ static int process_sample_event(struct perf_tool *tool __maybe_unused,
                                struct perf_evsel *evsel,
                                struct machine *machine)
 {
+       int err = 0;
        struct thread *thread = machine__findnew_thread(machine, sample->pid,
                                                        sample->tid);
 
@@ -821,10 +823,12 @@ static int process_sample_event(struct perf_tool *tool __maybe_unused,
 
        if (evsel->handler != NULL) {
                tracepoint_handler f = evsel->handler;
-               return f(evsel, sample);
+               err = f(evsel, sample);
        }
 
-       return 0;
+       thread__put(thread);
+
+       return err;
 }
 
 static void sort_result(void)
index 675216e08bfcd04baf2336ece7da328e914e21fd..da2ec06f0742dc6acf98c1c9b74d7cf45ff0fcb2 100644 (file)
@@ -74,7 +74,7 @@ dump_raw_samples(struct perf_tool *tool,
        }
 
        if (al.filtered || (mem->hide_unresolved && al.sym == NULL))
-               return 0;
+               goto out_put;
 
        if (al.map != NULL)
                al.map->dso->hit = 1;
@@ -103,7 +103,8 @@ dump_raw_samples(struct perf_tool *tool,
                symbol_conf.field_sep,
                al.map ? (al.map->dso ? al.map->dso->long_name : "???") : "???",
                al.sym ? al.sym->name : "???");
-
+out_put:
+       addr_location__put(&al);
        return 0;
 }
 
index f7b1af67e9f686d86f8bd1539b96a935b04a4559..1272559fa22d9eb60367f34594ebd18a7e76e8d3 100644 (file)
 
 #define DEFAULT_VAR_FILTER "!__k???tab_* & !__crc_*"
 #define DEFAULT_FUNC_FILTER "!_*"
+#define DEFAULT_LIST_FILTER "*:*"
 
 /* Session management structure */
 static struct {
+       int command;    /* Command short_name */
        bool list_events;
-       bool force_add;
-       bool show_lines;
-       bool show_vars;
-       bool show_ext_vars;
-       bool show_funcs;
-       bool mod_events;
        bool uprobes;
        bool quiet;
        bool target_used;
        int nevents;
        struct perf_probe_event events[MAX_PROBES];
-       struct strlist *dellist;
        struct line_range line_range;
        char *target;
-       int max_probe_points;
        struct strfilter *filter;
 } params;
 
@@ -93,6 +87,28 @@ static int parse_probe_event(const char *str)
        return ret;
 }
 
+static int params_add_filter(const char *str)
+{
+       const char *err = NULL;
+       int ret = 0;
+
+       pr_debug2("Add filter: %s\n", str);
+       if (!params.filter) {
+               params.filter = strfilter__new(str, &err);
+               if (!params.filter)
+                       ret = err ? -EINVAL : -ENOMEM;
+       } else
+               ret = strfilter__or(params.filter, str, &err);
+
+       if (ret == -EINVAL) {
+               pr_err("Filter parse error at %td.\n", err - str + 1);
+               pr_err("Source: \"%s\"\n", str);
+               pr_err("         %*c\n", (int)(err - str + 1), '^');
+       }
+
+       return ret;
+}
+
 static int set_target(const char *ptr)
 {
        int found = 0;
@@ -152,34 +168,11 @@ static int parse_probe_event_argv(int argc, const char **argv)
 
                len += sprintf(&buf[len], "%s ", argv[i]);
        }
-       params.mod_events = true;
        ret = parse_probe_event(buf);
        free(buf);
        return ret;
 }
 
-static int opt_add_probe_event(const struct option *opt __maybe_unused,
-                             const char *str, int unset __maybe_unused)
-{
-       if (str) {
-               params.mod_events = true;
-               return parse_probe_event(str);
-       } else
-               return 0;
-}
-
-static int opt_del_probe_event(const struct option *opt __maybe_unused,
-                              const char *str, int unset __maybe_unused)
-{
-       if (str) {
-               params.mod_events = true;
-               if (!params.dellist)
-                       params.dellist = strlist__new(true, NULL);
-               strlist__add(params.dellist, str);
-       }
-       return 0;
-}
-
 static int opt_set_target(const struct option *opt, const char *str,
                        int unset __maybe_unused)
 {
@@ -217,8 +210,10 @@ static int opt_set_target(const struct option *opt, const char *str,
        return ret;
 }
 
+/* Command option callbacks */
+
 #ifdef HAVE_DWARF_SUPPORT
-static int opt_show_lines(const struct option *opt __maybe_unused,
+static int opt_show_lines(const struct option *opt,
                          const char *str, int unset __maybe_unused)
 {
        int ret = 0;
@@ -226,19 +221,19 @@ static int opt_show_lines(const struct option *opt __maybe_unused,
        if (!str)
                return 0;
 
-       if (params.show_lines) {
+       if (params.command == 'L') {
                pr_warning("Warning: more than one --line options are"
                           " detected. Only the first one is valid.\n");
                return 0;
        }
 
-       params.show_lines = true;
+       params.command = opt->short_name;
        ret = parse_line_range_desc(str, &params.line_range);
 
        return ret;
 }
 
-static int opt_show_vars(const struct option *opt __maybe_unused,
+static int opt_show_vars(const struct option *opt,
                         const char *str, int unset __maybe_unused)
 {
        struct perf_probe_event *pev = &params.events[params.nevents];
@@ -252,29 +247,39 @@ static int opt_show_vars(const struct option *opt __maybe_unused,
                pr_err("  Error: '--vars' doesn't accept arguments.\n");
                return -EINVAL;
        }
-       params.show_vars = true;
+       params.command = opt->short_name;
 
        return ret;
 }
 #endif
+static int opt_add_probe_event(const struct option *opt,
+                             const char *str, int unset __maybe_unused)
+{
+       if (str) {
+               params.command = opt->short_name;
+               return parse_probe_event(str);
+       }
+
+       return 0;
+}
+
+static int opt_set_filter_with_command(const struct option *opt,
+                                      const char *str, int unset)
+{
+       if (!unset)
+               params.command = opt->short_name;
+
+       if (str)
+               return params_add_filter(str);
+
+       return 0;
+}
 
 static int opt_set_filter(const struct option *opt __maybe_unused,
                          const char *str, int unset __maybe_unused)
 {
-       const char *err;
-
-       if (str) {
-               pr_debug2("Set filter: %s\n", str);
-               if (params.filter)
-                       strfilter__delete(params.filter);
-               params.filter = strfilter__new(str, &err);
-               if (!params.filter) {
-                       pr_err("Filter parse error at %td.\n", err - str + 1);
-                       pr_err("Source: \"%s\"\n", str);
-                       pr_err("         %*c\n", (int)(err - str + 1), '^');
-                       return -EINVAL;
-               }
-       }
+       if (str)
+               return params_add_filter(str);
 
        return 0;
 }
@@ -290,8 +295,6 @@ static void cleanup_params(void)
 
        for (i = 0; i < params.nevents; i++)
                clear_perf_probe_event(params.events + i);
-       if (params.dellist)
-               strlist__delete(params.dellist);
        line_range__clear(&params.line_range);
        free(params.target);
        if (params.filter)
@@ -316,22 +319,24 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused)
                "perf probe [<options>] 'PROBEDEF' ['PROBEDEF' ...]",
                "perf probe [<options>] --add 'PROBEDEF' [--add 'PROBEDEF' ...]",
                "perf probe [<options>] --del '[GROUP:]EVENT' ...",
-               "perf probe --list",
+               "perf probe --list [GROUP:]EVENT ...",
 #ifdef HAVE_DWARF_SUPPORT
                "perf probe [<options>] --line 'LINEDESC'",
                "perf probe [<options>] --vars 'PROBEPOINT'",
 #endif
+               "perf probe [<options>] --funcs",
                NULL
-};
+       };
        struct option options[] = {
        OPT_INCR('v', "verbose", &verbose,
                    "be more verbose (show parsed arguments, etc)"),
        OPT_BOOLEAN('q', "quiet", &params.quiet,
                    "be quiet (do not show any mesages)"),
-       OPT_BOOLEAN('l', "list", &params.list_events,
-                   "list up current probe events"),
+       OPT_CALLBACK_DEFAULT('l', "list", NULL, "[GROUP:]EVENT",
+                            "list up probe events",
+                            opt_set_filter_with_command, DEFAULT_LIST_FILTER),
        OPT_CALLBACK('d', "del", NULL, "[GROUP:]EVENT", "delete a probe event.",
-               opt_del_probe_event),
+                    opt_set_filter_with_command),
        OPT_CALLBACK('a', "add", NULL,
 #ifdef HAVE_DWARF_SUPPORT
                "[EVENT=]FUNC[@SRC][+OFF|%return|:RL|;PT]|SRC:AL|SRC;PT"
@@ -356,7 +361,7 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused)
                "\t\tARG:\tProbe argument (kprobe-tracer argument format.)\n",
 #endif
                opt_add_probe_event),
-       OPT_BOOLEAN('f', "force", &params.force_add, "forcibly add events"
+       OPT_BOOLEAN('f', "force", &probe_conf.force_add, "forcibly add events"
                    " with existing name"),
 #ifdef HAVE_DWARF_SUPPORT
        OPT_CALLBACK('L', "line", NULL,
@@ -365,8 +370,10 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused)
        OPT_CALLBACK('V', "vars", NULL,
                     "FUNC[@SRC][+OFF|%return|:RL|;PT]|SRC:AL|SRC;PT",
                     "Show accessible variables on PROBEDEF", opt_show_vars),
-       OPT_BOOLEAN('\0', "externs", &params.show_ext_vars,
+       OPT_BOOLEAN('\0', "externs", &probe_conf.show_ext_vars,
                    "Show external variables too (with --vars only)"),
+       OPT_BOOLEAN('\0', "range", &probe_conf.show_location_range,
+               "Show variables location range in scope (with --vars only)"),
        OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
                   "file", "vmlinux pathname"),
        OPT_STRING('s', "source", &symbol_conf.source_prefix,
@@ -374,12 +381,15 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused)
        OPT_CALLBACK('m', "module", NULL, "modname|path",
                "target module name (for online) or path (for offline)",
                opt_set_target),
+       OPT_BOOLEAN('\0', "no-inlines", &probe_conf.no_inlines,
+               "Don't search inlined functions"),
 #endif
        OPT__DRY_RUN(&probe_event_dry_run),
-       OPT_INTEGER('\0', "max-probes", &params.max_probe_points,
+       OPT_INTEGER('\0', "max-probes", &probe_conf.max_probes,
                 "Set how many probe points can be found for a probe."),
-       OPT_BOOLEAN('F', "funcs", &params.show_funcs,
-                   "Show potential probe-able functions."),
+       OPT_CALLBACK_DEFAULT('F', "funcs", NULL, "[FILTER]",
+                            "Show potential probe-able functions.",
+                            opt_set_filter_with_command, DEFAULT_FUNC_FILTER),
        OPT_CALLBACK('\0', "filter", NULL,
                     "[!]FILTER", "Set a filter (with --vars/funcs only)\n"
                     "\t\t\t(default: \"" DEFAULT_VAR_FILTER "\" for --vars,\n"
@@ -402,6 +412,7 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused)
        set_option_flag(options, 'L', "line", PARSE_OPT_EXCLUSIVE);
        set_option_flag(options, 'V', "vars", PARSE_OPT_EXCLUSIVE);
 #endif
+       set_option_flag(options, 'F', "funcs", PARSE_OPT_EXCLUSIVE);
 
        argc = parse_options(argc, argv, options, probe_usage,
                             PARSE_OPT_STOP_AT_NON_OPTION);
@@ -410,11 +421,16 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused)
                        pr_warning("  Error: '-' is not supported.\n");
                        usage_with_options(probe_usage, options);
                }
+               if (params.command && params.command != 'a') {
+                       pr_warning("  Error: another command except --add is set.\n");
+                       usage_with_options(probe_usage, options);
+               }
                ret = parse_probe_event_argv(argc, argv);
                if (ret < 0) {
                        pr_err_with_code("  Error: Command Parse Error.", ret);
                        return ret;
                }
+               params.command = 'a';
        }
 
        if (params.quiet) {
@@ -425,89 +441,70 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused)
                verbose = -1;
        }
 
-       if (params.max_probe_points == 0)
-               params.max_probe_points = MAX_PROBES;
-
-       if ((!params.nevents && !params.dellist && !params.list_events &&
-            !params.show_lines && !params.show_funcs))
-               usage_with_options(probe_usage, options);
+       if (probe_conf.max_probes == 0)
+               probe_conf.max_probes = MAX_PROBES;
 
        /*
         * Only consider the user's kernel image path if given.
         */
        symbol_conf.try_vmlinux_path = (symbol_conf.vmlinux_name == NULL);
 
-       if (params.list_events) {
+       switch (params.command) {
+       case 'l':
                if (params.uprobes) {
                        pr_warning("  Error: Don't use --list with --exec.\n");
                        usage_with_options(probe_usage, options);
                }
-               ret = show_perf_probe_events();
+               ret = show_perf_probe_events(params.filter);
                if (ret < 0)
                        pr_err_with_code("  Error: Failed to show event list.", ret);
                return ret;
-       }
-       if (params.show_funcs) {
-               if (!params.filter)
-                       params.filter = strfilter__new(DEFAULT_FUNC_FILTER,
-                                                      NULL);
+       case 'F':
                ret = show_available_funcs(params.target, params.filter,
                                        params.uprobes);
-               strfilter__delete(params.filter);
-               params.filter = NULL;
                if (ret < 0)
                        pr_err_with_code("  Error: Failed to show functions.", ret);
                return ret;
-       }
-
 #ifdef HAVE_DWARF_SUPPORT
-       if (params.show_lines) {
+       case 'L':
                ret = show_line_range(&params.line_range, params.target,
                                      params.uprobes);
                if (ret < 0)
                        pr_err_with_code("  Error: Failed to show lines.", ret);
                return ret;
-       }
-       if (params.show_vars) {
+       case 'V':
                if (!params.filter)
                        params.filter = strfilter__new(DEFAULT_VAR_FILTER,
                                                       NULL);
 
                ret = show_available_vars(params.events, params.nevents,
-                                         params.max_probe_points,
-                                         params.target,
-                                         params.filter,
-                                         params.show_ext_vars);
-               strfilter__delete(params.filter);
-               params.filter = NULL;
+                                         params.filter);
                if (ret < 0)
                        pr_err_with_code("  Error: Failed to show vars.", ret);
                return ret;
-       }
 #endif
-
-       if (params.dellist) {
-               ret = del_perf_probe_events(params.dellist);
+       case 'd':
+               ret = del_perf_probe_events(params.filter);
                if (ret < 0) {
                        pr_err_with_code("  Error: Failed to delete events.", ret);
                        return ret;
                }
-       }
-
-       if (params.nevents) {
+               break;
+       case 'a':
                /* Ensure the last given target is used */
                if (params.target && !params.target_used) {
                        pr_warning("  Error: -x/-m must follow the probe definitions.\n");
                        usage_with_options(probe_usage, options);
                }
 
-               ret = add_perf_probe_events(params.events, params.nevents,
-                                           params.max_probe_points,
-                                           params.force_add);
+               ret = add_perf_probe_events(params.events, params.nevents);
                if (ret < 0) {
                        pr_err_with_code("  Error: Failed to add events.", ret);
                        return ret;
                }
+               break;
+       default:
+               usage_with_options(probe_usage, options);
        }
        return 0;
 }
@@ -522,5 +519,5 @@ int cmd_probe(int argc, const char **argv, const char *prefix)
                cleanup_params();
        }
 
-       return ret;
+       return ret < 0 ? ret : 0;
 }
index c3efdfb630b5b664349ed9e40c41374d3863752d..de165a1b92402ac7a6267bd0a0c5aa30a0053c92 100644 (file)
@@ -27,6 +27,8 @@
 #include "util/cpumap.h"
 #include "util/thread_map.h"
 #include "util/data.h"
+#include "util/auxtrace.h"
+#include "util/parse-branch-options.h"
 
 #include <unistd.h>
 #include <sched.h>
@@ -38,6 +40,7 @@ struct record {
        struct record_opts      opts;
        u64                     bytes_written;
        struct perf_data_file   file;
+       struct auxtrace_record  *itr;
        struct perf_evlist      *evlist;
        struct perf_session     *session;
        const char              *progname;
@@ -110,9 +113,12 @@ out:
        return rc;
 }
 
-static volatile int done = 0;
+static volatile int done;
 static volatile int signr = -1;
-static volatile int child_finished = 0;
+static volatile int child_finished;
+static volatile int auxtrace_snapshot_enabled;
+static volatile int auxtrace_snapshot_err;
+static volatile int auxtrace_record__snapshot_started;
 
 static void sig_handler(int sig)
 {
@@ -133,6 +139,133 @@ static void record__sig_exit(void)
        raise(signr);
 }
 
+#ifdef HAVE_AUXTRACE_SUPPORT
+
+static int record__process_auxtrace(struct perf_tool *tool,
+                                   union perf_event *event, void *data1,
+                                   size_t len1, void *data2, size_t len2)
+{
+       struct record *rec = container_of(tool, struct record, tool);
+       struct perf_data_file *file = &rec->file;
+       size_t padding;
+       u8 pad[8] = {0};
+
+       if (!perf_data_file__is_pipe(file)) {
+               off_t file_offset;
+               int fd = perf_data_file__fd(file);
+               int err;
+
+               file_offset = lseek(fd, 0, SEEK_CUR);
+               if (file_offset == -1)
+                       return -1;
+               err = auxtrace_index__auxtrace_event(&rec->session->auxtrace_index,
+                                                    event, file_offset);
+               if (err)
+                       return err;
+       }
+
+       /* event.auxtrace.size includes padding, see __auxtrace_mmap__read() */
+       padding = (len1 + len2) & 7;
+       if (padding)
+               padding = 8 - padding;
+
+       record__write(rec, event, event->header.size);
+       record__write(rec, data1, len1);
+       if (len2)
+               record__write(rec, data2, len2);
+       record__write(rec, &pad, padding);
+
+       return 0;
+}
+
+static int record__auxtrace_mmap_read(struct record *rec,
+                                     struct auxtrace_mmap *mm)
+{
+       int ret;
+
+       ret = auxtrace_mmap__read(mm, rec->itr, &rec->tool,
+                                 record__process_auxtrace);
+       if (ret < 0)
+               return ret;
+
+       if (ret)
+               rec->samples++;
+
+       return 0;
+}
+
+static int record__auxtrace_mmap_read_snapshot(struct record *rec,
+                                              struct auxtrace_mmap *mm)
+{
+       int ret;
+
+       ret = auxtrace_mmap__read_snapshot(mm, rec->itr, &rec->tool,
+                                          record__process_auxtrace,
+                                          rec->opts.auxtrace_snapshot_size);
+       if (ret < 0)
+               return ret;
+
+       if (ret)
+               rec->samples++;
+
+       return 0;
+}
+
+static int record__auxtrace_read_snapshot_all(struct record *rec)
+{
+       int i;
+       int rc = 0;
+
+       for (i = 0; i < rec->evlist->nr_mmaps; i++) {
+               struct auxtrace_mmap *mm =
+                               &rec->evlist->mmap[i].auxtrace_mmap;
+
+               if (!mm->base)
+                       continue;
+
+               if (record__auxtrace_mmap_read_snapshot(rec, mm) != 0) {
+                       rc = -1;
+                       goto out;
+               }
+       }
+out:
+       return rc;
+}
+
+static void record__read_auxtrace_snapshot(struct record *rec)
+{
+       pr_debug("Recording AUX area tracing snapshot\n");
+       if (record__auxtrace_read_snapshot_all(rec) < 0) {
+               auxtrace_snapshot_err = -1;
+       } else {
+               auxtrace_snapshot_err = auxtrace_record__snapshot_finish(rec->itr);
+               if (!auxtrace_snapshot_err)
+                       auxtrace_snapshot_enabled = 1;
+       }
+}
+
+#else
+
+static inline
+int record__auxtrace_mmap_read(struct record *rec __maybe_unused,
+                              struct auxtrace_mmap *mm __maybe_unused)
+{
+       return 0;
+}
+
+static inline
+void record__read_auxtrace_snapshot(struct record *rec __maybe_unused)
+{
+}
+
+static inline
+int auxtrace_record__snapshot_start(struct auxtrace_record *itr __maybe_unused)
+{
+       return 0;
+}
+
+#endif
+
 static int record__open(struct record *rec)
 {
        char msg[512];
@@ -169,13 +302,16 @@ try_again:
                goto out;
        }
 
-       if (perf_evlist__mmap(evlist, opts->mmap_pages, false) < 0) {
+       if (perf_evlist__mmap_ex(evlist, opts->mmap_pages, false,
+                                opts->auxtrace_mmap_pages,
+                                opts->auxtrace_snapshot_mode) < 0) {
                if (errno == EPERM) {
                        pr_err("Permission error mapping pages.\n"
                               "Consider increasing "
                               "/proc/sys/kernel/perf_event_mlock_kb,\n"
                               "or try again with a smaller value of -m/--mmap_pages.\n"
-                              "(current value: %u)\n", opts->mmap_pages);
+                              "(current value: %u,%u)\n",
+                              opts->mmap_pages, opts->auxtrace_mmap_pages);
                        rc = -errno;
                } else {
                        pr_err("failed to mmap with %d (%s)\n", errno,
@@ -209,12 +345,9 @@ static int process_buildids(struct record *rec)
        struct perf_data_file *file  = &rec->file;
        struct perf_session *session = rec->session;
 
-       u64 size = lseek(perf_data_file__fd(file), 0, SEEK_CUR);
-       if (size == 0)
+       if (file->size == 0)
                return 0;
 
-       file->size = size;
-
        /*
         * During this process, it'll load kernel map and replace the
         * dso->long_name to a real pathname it found.  In this case
@@ -270,12 +403,20 @@ static int record__mmap_read_all(struct record *rec)
        int rc = 0;
 
        for (i = 0; i < rec->evlist->nr_mmaps; i++) {
+               struct auxtrace_mmap *mm = &rec->evlist->mmap[i].auxtrace_mmap;
+
                if (rec->evlist->mmap[i].base) {
                        if (record__mmap_read(rec, i) != 0) {
                                rc = -1;
                                goto out;
                        }
                }
+
+               if (mm->base && !rec->opts.auxtrace_snapshot_mode &&
+                   record__auxtrace_mmap_read(rec, mm) != 0) {
+                       rc = -1;
+                       goto out;
+               }
        }
 
        /*
@@ -305,6 +446,9 @@ static void record__init_features(struct record *rec)
 
        if (!rec->opts.branch_stack)
                perf_header__clear_feat(&session->header, HEADER_BRANCH_STACK);
+
+       if (!rec->opts.full_auxtrace)
+               perf_header__clear_feat(&session->header, HEADER_AUXTRACE);
 }
 
 static volatile int workload_exec_errno;
@@ -323,6 +467,8 @@ static void workload_exec_failed_signal(int signo __maybe_unused,
        child_finished = 1;
 }
 
+static void snapshot_sig_handler(int sig);
+
 static int __cmd_record(struct record *rec, int argc, const char **argv)
 {
        int err;
@@ -343,6 +489,10 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
        signal(SIGCHLD, sig_handler);
        signal(SIGINT, sig_handler);
        signal(SIGTERM, sig_handler);
+       if (rec->opts.auxtrace_snapshot_mode)
+               signal(SIGUSR2, snapshot_sig_handler);
+       else
+               signal(SIGUSR2, SIG_IGN);
 
        session = perf_session__new(file, false, tool);
        if (session == NULL) {
@@ -421,6 +571,13 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
                }
        }
 
+       if (rec->opts.full_auxtrace) {
+               err = perf_event__synthesize_auxtrace_info(rec->itr, tool,
+                                       session, process_synthesized_event);
+               if (err)
+                       goto out_delete_session;
+       }
+
        err = perf_event__synthesize_kernel_mmap(tool, process_synthesized_event,
                                                 machine);
        if (err < 0)
@@ -441,7 +598,8 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
        }
 
        err = __machine__synthesize_threads(machine, tool, &opts->target, rec->evlist->threads,
-                                           process_synthesized_event, opts->sample_address);
+                                           process_synthesized_event, opts->sample_address,
+                                           opts->proc_map_timeout);
        if (err != 0)
                goto out_child;
 
@@ -475,14 +633,27 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
                perf_evlist__enable(rec->evlist);
        }
 
+       auxtrace_snapshot_enabled = 1;
        for (;;) {
                int hits = rec->samples;
 
                if (record__mmap_read_all(rec) < 0) {
+                       auxtrace_snapshot_enabled = 0;
                        err = -1;
                        goto out_child;
                }
 
+               if (auxtrace_record__snapshot_started) {
+                       auxtrace_record__snapshot_started = 0;
+                       if (!auxtrace_snapshot_err)
+                               record__read_auxtrace_snapshot(rec);
+                       if (auxtrace_snapshot_err) {
+                               pr_err("AUX area tracing snapshot failed\n");
+                               err = -1;
+                               goto out_child;
+                       }
+               }
+
                if (hits == rec->samples) {
                        if (done || draining)
                                break;
@@ -505,10 +676,12 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
                 * disable events in this case.
                 */
                if (done && !disabled && !target__none(&opts->target)) {
+                       auxtrace_snapshot_enabled = 0;
                        perf_evlist__disable(rec->evlist);
                        disabled = true;
                }
        }
+       auxtrace_snapshot_enabled = 0;
 
        if (forks && workload_exec_errno) {
                char msg[STRERR_BUFSIZE];
@@ -544,16 +717,25 @@ out_child:
 
        if (!err && !file->is_pipe) {
                rec->session->header.data_size += rec->bytes_written;
+               file->size = lseek(perf_data_file__fd(file), 0, SEEK_CUR);
 
-               if (!rec->no_buildid)
+               if (!rec->no_buildid) {
                        process_buildids(rec);
+                       /*
+                        * We take all buildids when the file contains
+                        * AUX area tracing data because we do not decode the
+                        * trace because it would take too long.
+                        */
+                       if (rec->opts.full_auxtrace)
+                               dsos__hit_all(rec->session);
+               }
                perf_session__write_header(rec->session, rec->evlist, fd, true);
        }
 
        if (!err && !quiet) {
                char samples[128];
 
-               if (rec->samples)
+               if (rec->samples && !rec->opts.full_auxtrace)
                        scnprintf(samples, sizeof(samples),
                                  " (%" PRIu64 " samples)", rec->samples);
                else
@@ -569,94 +751,6 @@ out_delete_session:
        return status;
 }
 
-#define BRANCH_OPT(n, m) \
-       { .name = n, .mode = (m) }
-
-#define BRANCH_END { .name = NULL }
-
-struct branch_mode {
-       const char *name;
-       int mode;
-};
-
-static const struct branch_mode branch_modes[] = {
-       BRANCH_OPT("u", PERF_SAMPLE_BRANCH_USER),
-       BRANCH_OPT("k", PERF_SAMPLE_BRANCH_KERNEL),
-       BRANCH_OPT("hv", PERF_SAMPLE_BRANCH_HV),
-       BRANCH_OPT("any", PERF_SAMPLE_BRANCH_ANY),
-       BRANCH_OPT("any_call", PERF_SAMPLE_BRANCH_ANY_CALL),
-       BRANCH_OPT("any_ret", PERF_SAMPLE_BRANCH_ANY_RETURN),
-       BRANCH_OPT("ind_call", PERF_SAMPLE_BRANCH_IND_CALL),
-       BRANCH_OPT("abort_tx", PERF_SAMPLE_BRANCH_ABORT_TX),
-       BRANCH_OPT("in_tx", PERF_SAMPLE_BRANCH_IN_TX),
-       BRANCH_OPT("no_tx", PERF_SAMPLE_BRANCH_NO_TX),
-       BRANCH_OPT("cond", PERF_SAMPLE_BRANCH_COND),
-       BRANCH_END
-};
-
-static int
-parse_branch_stack(const struct option *opt, const char *str, int unset)
-{
-#define ONLY_PLM \
-       (PERF_SAMPLE_BRANCH_USER        |\
-        PERF_SAMPLE_BRANCH_KERNEL      |\
-        PERF_SAMPLE_BRANCH_HV)
-
-       uint64_t *mode = (uint64_t *)opt->value;
-       const struct branch_mode *br;
-       char *s, *os = NULL, *p;
-       int ret = -1;
-
-       if (unset)
-               return 0;
-
-       /*
-        * cannot set it twice, -b + --branch-filter for instance
-        */
-       if (*mode)
-               return -1;
-
-       /* str may be NULL in case no arg is passed to -b */
-       if (str) {
-               /* because str is read-only */
-               s = os = strdup(str);
-               if (!s)
-                       return -1;
-
-               for (;;) {
-                       p = strchr(s, ',');
-                       if (p)
-                               *p = '\0';
-
-                       for (br = branch_modes; br->name; br++) {
-                               if (!strcasecmp(s, br->name))
-                                       break;
-                       }
-                       if (!br->name) {
-                               ui__warning("unknown branch filter %s,"
-                                           " check man page\n", s);
-                               goto error;
-                       }
-
-                       *mode |= br->mode;
-
-                       if (!p)
-                               break;
-
-                       s = p + 1;
-               }
-       }
-       ret = 0;
-
-       /* default to any branch */
-       if ((*mode & ~ONLY_PLM) == 0) {
-               *mode = PERF_SAMPLE_BRANCH_ANY;
-       }
-error:
-       free(os);
-       return ret;
-}
-
 static void callchain_debug(void)
 {
        static const char *str[CALLCHAIN_MAX] = { "NONE", "FP", "DWARF", "LBR" };
@@ -795,6 +889,49 @@ static int parse_clockid(const struct option *opt, const char *str, int unset)
        return -1;
 }
 
+static int record__parse_mmap_pages(const struct option *opt,
+                                   const char *str,
+                                   int unset __maybe_unused)
+{
+       struct record_opts *opts = opt->value;
+       char *s, *p;
+       unsigned int mmap_pages;
+       int ret;
+
+       if (!str)
+               return -EINVAL;
+
+       s = strdup(str);
+       if (!s)
+               return -ENOMEM;
+
+       p = strchr(s, ',');
+       if (p)
+               *p = '\0';
+
+       if (*s) {
+               ret = __perf_evlist__parse_mmap_pages(&mmap_pages, s);
+               if (ret)
+                       goto out_free;
+               opts->mmap_pages = mmap_pages;
+       }
+
+       if (!p) {
+               ret = 0;
+               goto out_free;
+       }
+
+       ret = __perf_evlist__parse_mmap_pages(&mmap_pages, p + 1);
+       if (ret)
+               goto out_free;
+
+       opts->auxtrace_mmap_pages = mmap_pages;
+
+out_free:
+       free(s);
+       return ret;
+}
+
 static const char * const __record_usage[] = {
        "perf record [<options>] [<command>]",
        "perf record [<options>] -- <command> [<options>]",
@@ -823,6 +960,7 @@ static struct record record = {
                        .uses_mmap   = true,
                        .default_per_cpu = true,
                },
+               .proc_map_timeout     = 500,
        },
        .tool = {
                .sample         = process_sample_event,
@@ -875,9 +1013,9 @@ struct option __record_options[] = {
                        &record.opts.no_inherit_set,
                        "child tasks do not inherit counters"),
        OPT_UINTEGER('F', "freq", &record.opts.user_freq, "profile at this frequency"),
-       OPT_CALLBACK('m', "mmap-pages", &record.opts.mmap_pages, "pages",
-                    "number of mmap data pages",
-                    perf_evlist__parse_mmap_pages),
+       OPT_CALLBACK('m', "mmap-pages", &record.opts, "pages[,pages]",
+                    "number of mmap data pages and AUX area tracing mmap pages",
+                    record__parse_mmap_pages),
        OPT_BOOLEAN(0, "group", &record.opts.group,
                    "put the counters into a counter group"),
        OPT_CALLBACK_NOOPT('g', NULL, &record.opts,
@@ -891,10 +1029,9 @@ struct option __record_options[] = {
        OPT_BOOLEAN('q', "quiet", &quiet, "don't print any message"),
        OPT_BOOLEAN('s', "stat", &record.opts.inherit_stat,
                    "per thread counts"),
-       OPT_BOOLEAN('d', "data", &record.opts.sample_address,
-                   "Sample addresses"),
-       OPT_BOOLEAN('T', "timestamp", &record.opts.sample_time, "Sample timestamps"),
-       OPT_BOOLEAN('P', "period", &record.opts.period, "Sample period"),
+       OPT_BOOLEAN('d', "data", &record.opts.sample_address, "Record the sample addresses"),
+       OPT_BOOLEAN('T', "timestamp", &record.opts.sample_time, "Record the sample timestamps"),
+       OPT_BOOLEAN('P', "period", &record.opts.period, "Record the sample period"),
        OPT_BOOLEAN('n', "no-samples", &record.opts.no_samples,
                    "don't sample"),
        OPT_BOOLEAN('N', "no-buildid-cache", &record.no_buildid_cache,
@@ -929,6 +1066,10 @@ struct option __record_options[] = {
        OPT_CALLBACK('k', "clockid", &record.opts,
        "clockid", "clockid to use for events, see clock_gettime()",
        parse_clockid),
+       OPT_STRING_OPTARG('S', "snapshot", &record.opts.auxtrace_snapshot_opts,
+                         "opts", "AUX area tracing Snapshot Mode", ""),
+       OPT_UINTEGER(0, "proc-map-timeout", &record.opts.proc_map_timeout,
+                       "per thread proc mmap processing timeout in ms"),
        OPT_END()
 };
 
@@ -936,7 +1077,7 @@ struct option *record_options = __record_options;
 
 int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused)
 {
-       int err = -ENOMEM;
+       int err;
        struct record *rec = &record;
        char errbuf[BUFSIZ];
 
@@ -957,6 +1098,19 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused)
                usage_with_options(record_usage, record_options);
        }
 
+       if (!rec->itr) {
+               rec->itr = auxtrace_record__init(rec->evlist, &err);
+               if (err)
+                       return err;
+       }
+
+       err = auxtrace_parse_snapshot_options(rec->itr, &rec->opts,
+                                             rec->opts.auxtrace_snapshot_opts);
+       if (err)
+               return err;
+
+       err = -ENOMEM;
+
        symbol__init(NULL);
 
        if (symbol_conf.kptr_restrict)
@@ -1002,6 +1156,10 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused)
        if (perf_evlist__create_maps(rec->evlist, &rec->opts.target) < 0)
                usage_with_options(record_usage, record_options);
 
+       err = auxtrace_record__options(rec->itr, rec->evlist, &rec->opts);
+       if (err)
+               goto out_symbol_exit;
+
        if (record_opts__config(&rec->opts)) {
                err = -EINVAL;
                goto out_symbol_exit;
@@ -1011,5 +1169,15 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused)
 out_symbol_exit:
        perf_evlist__delete(rec->evlist);
        symbol__exit();
+       auxtrace_record__free(rec->itr);
        return err;
 }
+
+static void snapshot_sig_handler(int sig __maybe_unused)
+{
+       if (!auxtrace_snapshot_enabled)
+               return;
+       auxtrace_snapshot_enabled = 0;
+       auxtrace_snapshot_err = auxtrace_record__snapshot_start(record.itr);
+       auxtrace_record__snapshot_started = 1;
+}
index b63aeda719be0c7604da5229e1a3a0ec33253400..32626ea3e2276b11279db88207e42c29eeed391a 100644 (file)
@@ -36,6 +36,8 @@
 #include "util/data.h"
 #include "arch/common.h"
 
+#include "util/auxtrace.h"
+
 #include <dlfcn.h>
 #include <linux/bitmap.h>
 
@@ -137,10 +139,12 @@ static int process_sample_event(struct perf_tool *tool,
        struct report *rep = container_of(tool, struct report, tool);
        struct addr_location al;
        struct hist_entry_iter iter = {
-               .hide_unresolved = rep->hide_unresolved,
-               .add_entry_cb = hist_iter__report_callback,
+               .evsel                  = evsel,
+               .sample                 = sample,
+               .hide_unresolved        = rep->hide_unresolved,
+               .add_entry_cb           = hist_iter__report_callback,
        };
-       int ret;
+       int ret = 0;
 
        if (perf_event__preprocess_sample(event, machine, &al, sample) < 0) {
                pr_debug("problem processing %d event, skipping it.\n",
@@ -149,10 +153,10 @@ static int process_sample_event(struct perf_tool *tool,
        }
 
        if (rep->hide_unresolved && al.sym == NULL)
-               return 0;
+               goto out_put;
 
        if (rep->cpu_list && !test_bit(sample->cpu, rep->cpu_bitmap))
-               return 0;
+               goto out_put;
 
        if (sort__mode == SORT_MODE__BRANCH)
                iter.ops = &hist_iter_branch;
@@ -166,11 +170,11 @@ static int process_sample_event(struct perf_tool *tool,
        if (al.map != NULL)
                al.map->dso->hit = 1;
 
-       ret = hist_entry_iter__add(&iter, &al, evsel, sample, rep->max_stack,
-                                  rep);
+       ret = hist_entry_iter__add(&iter, &al, rep->max_stack, rep);
        if (ret < 0)
                pr_debug("problem adding hist entry, skipping event\n");
-
+out_put:
+       addr_location__put(&al);
        return ret;
 }
 
@@ -316,6 +320,7 @@ static int perf_evlist__tty_browse_hists(struct perf_evlist *evlist,
 {
        struct perf_evsel *pos;
 
+       fprintf(stdout, "#\n# Total Lost Samples: %" PRIu64 "\n#\n", evlist->stats.total_lost_samples);
        evlist__for_each(evlist, pos) {
                struct hists *hists = evsel__hists(pos);
                const char *evname = perf_evsel__name(pos);
@@ -330,15 +335,14 @@ static int perf_evlist__tty_browse_hists(struct perf_evlist *evlist,
        }
 
        if (sort_order == NULL &&
-           parent_pattern == default_parent_pattern) {
+           parent_pattern == default_parent_pattern)
                fprintf(stdout, "#\n# (%s)\n#\n", help);
 
-               if (rep->show_threads) {
-                       bool style = !strcmp(rep->pretty_printing_style, "raw");
-                       perf_read_values_display(stdout, &rep->show_threads_values,
-                                                style);
-                       perf_read_values_destroy(&rep->show_threads_values);
-               }
+       if (rep->show_threads) {
+               bool style = !strcmp(rep->pretty_printing_style, "raw");
+               perf_read_values_display(stdout, &rep->show_threads_values,
+                                        style);
+               perf_read_values_destroy(&rep->show_threads_values);
        }
 
        return 0;
@@ -585,6 +589,7 @@ parse_percent_limit(const struct option *opt, const char *str,
 int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
 {
        struct perf_session *session;
+       struct itrace_synth_opts itrace_synth_opts = { .set = 0, };
        struct stat st;
        bool has_br_stack = false;
        int branch_mode = -1;
@@ -607,6 +612,9 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
                        .attr            = perf_event__process_attr,
                        .tracing_data    = perf_event__process_tracing_data,
                        .build_id        = perf_event__process_build_id,
+                       .id_index        = perf_event__process_id_index,
+                       .auxtrace_info   = perf_event__process_auxtrace_info,
+                       .auxtrace        = perf_event__process_auxtrace,
                        .ordered_events  = true,
                        .ordering_requires_timestamps = true,
                },
@@ -717,6 +725,9 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
                     "Don't show entries under that percent", parse_percent_limit),
        OPT_CALLBACK(0, "percentage", NULL, "relative|absolute",
                     "how to display percentage of filtered entries", parse_filter_percentage),
+       OPT_CALLBACK_OPTARG(0, "itrace", &itrace_synth_opts, NULL, "opts",
+                           "Instruction Tracing options",
+                           itrace_parse_synth_opts),
        OPT_END()
        };
        struct perf_data_file file = {
@@ -761,6 +772,8 @@ repeat:
                                               report.queue_size);
        }
 
+       session->itrace_synth_opts = &itrace_synth_opts;
+
        report.session = session;
 
        has_br_stack = perf_header__has_feat(&session->header,
@@ -803,8 +816,8 @@ repeat:
                goto error;
        }
 
-       /* Force tty output for header output. */
-       if (report.header || report.header_only)
+       /* Force tty output for header output and per-thread stat. */
+       if (report.header || report.header_only || report.show_threads)
                use_browser = 0;
 
        if (strcmp(input_name, "-") != 0)
index 5275bab703138cbeb9c40f1ff22174ac52ca2d13..33962612a5e9035ae42c83e15497a5713922556b 100644 (file)
@@ -95,6 +95,7 @@ struct work_atoms {
        u64                     total_lat;
        u64                     nb_atoms;
        u64                     total_runtime;
+       int                     num_merged;
 };
 
 typedef int (*sort_fn_t)(struct work_atoms *, struct work_atoms *);
@@ -168,9 +169,10 @@ struct perf_sched {
        u64              all_runtime;
        u64              all_count;
        u64              cpu_last_switched[MAX_CPUS];
-       struct rb_root   atom_root, sorted_atom_root;
+       struct rb_root   atom_root, sorted_atom_root, merged_atom_root;
        struct list_head sort_list, cmp_pid;
        bool force;
+       bool skip_merge;
 };
 
 static u64 get_nsecs(void)
@@ -770,7 +772,7 @@ static int replay_fork_event(struct perf_sched *sched,
        if (child == NULL || parent == NULL) {
                pr_debug("thread does not exist on fork event: child %p, parent %p\n",
                                 child, parent);
-               return 0;
+               goto out_put;
        }
 
        if (verbose) {
@@ -781,6 +783,9 @@ static int replay_fork_event(struct perf_sched *sched,
 
        register_pid(sched, parent->tid, thread__comm_str(parent));
        register_pid(sched, child->tid, thread__comm_str(child));
+out_put:
+       thread__put(child);
+       thread__put(parent);
        return 0;
 }
 
@@ -957,7 +962,7 @@ static int latency_switch_event(struct perf_sched *sched,
        struct work_atoms *out_events, *in_events;
        struct thread *sched_out, *sched_in;
        u64 timestamp0, timestamp = sample->time;
-       int cpu = sample->cpu;
+       int cpu = sample->cpu, err = -1;
        s64 delta;
 
        BUG_ON(cpu >= MAX_CPUS || cpu < 0);
@@ -976,15 +981,17 @@ static int latency_switch_event(struct perf_sched *sched,
 
        sched_out = machine__findnew_thread(machine, -1, prev_pid);
        sched_in = machine__findnew_thread(machine, -1, next_pid);
+       if (sched_out == NULL || sched_in == NULL)
+               goto out_put;
 
        out_events = thread_atoms_search(&sched->atom_root, sched_out, &sched->cmp_pid);
        if (!out_events) {
                if (thread_atoms_insert(sched, sched_out))
-                       return -1;
+                       goto out_put;
                out_events = thread_atoms_search(&sched->atom_root, sched_out, &sched->cmp_pid);
                if (!out_events) {
                        pr_err("out-event: Internal tree error");
-                       return -1;
+                       goto out_put;
                }
        }
        if (add_sched_out_event(out_events, sched_out_state(prev_state), timestamp))
@@ -993,22 +1000,25 @@ static int latency_switch_event(struct perf_sched *sched,
        in_events = thread_atoms_search(&sched->atom_root, sched_in, &sched->cmp_pid);
        if (!in_events) {
                if (thread_atoms_insert(sched, sched_in))
-                       return -1;
+                       goto out_put;
                in_events = thread_atoms_search(&sched->atom_root, sched_in, &sched->cmp_pid);
                if (!in_events) {
                        pr_err("in-event: Internal tree error");
-                       return -1;
+                       goto out_put;
                }
                /*
                 * Take came in we have not heard about yet,
                 * add in an initial atom in runnable state:
                 */
                if (add_sched_out_event(in_events, 'R', timestamp))
-                       return -1;
+                       goto out_put;
        }
        add_sched_in_event(in_events, timestamp);
-
-       return 0;
+       err = 0;
+out_put:
+       thread__put(sched_out);
+       thread__put(sched_in);
+       return err;
 }
 
 static int latency_runtime_event(struct perf_sched *sched,
@@ -1021,23 +1031,29 @@ static int latency_runtime_event(struct perf_sched *sched,
        struct thread *thread = machine__findnew_thread(machine, -1, pid);
        struct work_atoms *atoms = thread_atoms_search(&sched->atom_root, thread, &sched->cmp_pid);
        u64 timestamp = sample->time;
-       int cpu = sample->cpu;
+       int cpu = sample->cpu, err = -1;
+
+       if (thread == NULL)
+               return -1;
 
        BUG_ON(cpu >= MAX_CPUS || cpu < 0);
        if (!atoms) {
                if (thread_atoms_insert(sched, thread))
-                       return -1;
+                       goto out_put;
                atoms = thread_atoms_search(&sched->atom_root, thread, &sched->cmp_pid);
                if (!atoms) {
                        pr_err("in-event: Internal tree error");
-                       return -1;
+                       goto out_put;
                }
                if (add_sched_out_event(atoms, 'R', timestamp))
-                       return -1;
+                       goto out_put;
        }
 
        add_runtime_event(atoms, runtime, timestamp);
-       return 0;
+       err = 0;
+out_put:
+       thread__put(thread);
+       return err;
 }
 
 static int latency_wakeup_event(struct perf_sched *sched,
@@ -1050,19 +1066,22 @@ static int latency_wakeup_event(struct perf_sched *sched,
        struct work_atom *atom;
        struct thread *wakee;
        u64 timestamp = sample->time;
+       int err = -1;
 
        wakee = machine__findnew_thread(machine, -1, pid);
+       if (wakee == NULL)
+               return -1;
        atoms = thread_atoms_search(&sched->atom_root, wakee, &sched->cmp_pid);
        if (!atoms) {
                if (thread_atoms_insert(sched, wakee))
-                       return -1;
+                       goto out_put;
                atoms = thread_atoms_search(&sched->atom_root, wakee, &sched->cmp_pid);
                if (!atoms) {
                        pr_err("wakeup-event: Internal tree error");
-                       return -1;
+                       goto out_put;
                }
                if (add_sched_out_event(atoms, 'S', timestamp))
-                       return -1;
+                       goto out_put;
        }
 
        BUG_ON(list_empty(&atoms->work_list));
@@ -1081,17 +1100,21 @@ static int latency_wakeup_event(struct perf_sched *sched,
         * skip in this case.
         */
        if (sched->profile_cpu == -1 && atom->state != THREAD_SLEEPING)
-               return 0;
+               goto out_ok;
 
        sched->nr_timestamps++;
        if (atom->sched_out_time > timestamp) {
                sched->nr_unordered_timestamps++;
-               return 0;
+               goto out_ok;
        }
 
        atom->state = THREAD_WAIT_CPU;
        atom->wake_up_time = timestamp;
-       return 0;
+out_ok:
+       err = 0;
+out_put:
+       thread__put(wakee);
+       return err;
 }
 
 static int latency_migrate_task_event(struct perf_sched *sched,
@@ -1104,6 +1127,7 @@ static int latency_migrate_task_event(struct perf_sched *sched,
        struct work_atoms *atoms;
        struct work_atom *atom;
        struct thread *migrant;
+       int err = -1;
 
        /*
         * Only need to worry about migration when profiling one CPU.
@@ -1112,18 +1136,20 @@ static int latency_migrate_task_event(struct perf_sched *sched,
                return 0;
 
        migrant = machine__findnew_thread(machine, -1, pid);
+       if (migrant == NULL)
+               return -1;
        atoms = thread_atoms_search(&sched->atom_root, migrant, &sched->cmp_pid);
        if (!atoms) {
                if (thread_atoms_insert(sched, migrant))
-                       return -1;
+                       goto out_put;
                register_pid(sched, migrant->tid, thread__comm_str(migrant));
                atoms = thread_atoms_search(&sched->atom_root, migrant, &sched->cmp_pid);
                if (!atoms) {
                        pr_err("migration-event: Internal tree error");
-                       return -1;
+                       goto out_put;
                }
                if (add_sched_out_event(atoms, 'R', timestamp))
-                       return -1;
+                       goto out_put;
        }
 
        BUG_ON(list_empty(&atoms->work_list));
@@ -1135,8 +1161,10 @@ static int latency_migrate_task_event(struct perf_sched *sched,
 
        if (atom->sched_out_time > timestamp)
                sched->nr_unordered_timestamps++;
-
-       return 0;
+       err = 0;
+out_put:
+       thread__put(migrant);
+       return err;
 }
 
 static void output_lat_thread(struct perf_sched *sched, struct work_atoms *work_list)
@@ -1156,7 +1184,10 @@ static void output_lat_thread(struct perf_sched *sched, struct work_atoms *work_
        sched->all_runtime += work_list->total_runtime;
        sched->all_count   += work_list->nb_atoms;
 
-       ret = printf("  %s:%d ", thread__comm_str(work_list->thread), work_list->thread->tid);
+       if (work_list->num_merged > 1)
+               ret = printf("  %s:(%d) ", thread__comm_str(work_list->thread), work_list->num_merged);
+       else
+               ret = printf("  %s:%d ", thread__comm_str(work_list->thread), work_list->thread->tid);
 
        for (i = 0; i < 24 - ret; i++)
                printf(" ");
@@ -1276,17 +1307,22 @@ static int sort_dimension__add(const char *tok, struct list_head *list)
 static void perf_sched__sort_lat(struct perf_sched *sched)
 {
        struct rb_node *node;
-
+       struct rb_root *root = &sched->atom_root;
+again:
        for (;;) {
                struct work_atoms *data;
-               node = rb_first(&sched->atom_root);
+               node = rb_first(root);
                if (!node)
                        break;
 
-               rb_erase(node, &sched->atom_root);
+               rb_erase(node, root);
                data = rb_entry(node, struct work_atoms, node);
                __thread_latency_insert(&sched->sorted_atom_root, data, &sched->sort_list);
        }
+       if (root == &sched->atom_root) {
+               root = &sched->merged_atom_root;
+               goto again;
+       }
 }
 
 static int process_sched_wakeup_event(struct perf_tool *tool,
@@ -1330,8 +1366,10 @@ static int map_switch_event(struct perf_sched *sched, struct perf_evsel *evsel,
        }
 
        sched_in = machine__findnew_thread(machine, -1, next_pid);
+       if (sched_in == NULL)
+               return -1;
 
-       sched->curr_thread[this_cpu] = sched_in;
+       sched->curr_thread[this_cpu] = thread__get(sched_in);
 
        printf("  ");
 
@@ -1381,6 +1419,8 @@ static int map_switch_event(struct perf_sched *sched, struct perf_evsel *evsel,
                printf("\n");
        }
 
+       thread__put(sched_in);
+
        return 0;
 }
 
@@ -1542,6 +1582,59 @@ static void print_bad_events(struct perf_sched *sched)
        }
 }
 
+static void __merge_work_atoms(struct rb_root *root, struct work_atoms *data)
+{
+       struct rb_node **new = &(root->rb_node), *parent = NULL;
+       struct work_atoms *this;
+       const char *comm = thread__comm_str(data->thread), *this_comm;
+
+       while (*new) {
+               int cmp;
+
+               this = container_of(*new, struct work_atoms, node);
+               parent = *new;
+
+               this_comm = thread__comm_str(this->thread);
+               cmp = strcmp(comm, this_comm);
+               if (cmp > 0) {
+                       new = &((*new)->rb_left);
+               } else if (cmp < 0) {
+                       new = &((*new)->rb_right);
+               } else {
+                       this->num_merged++;
+                       this->total_runtime += data->total_runtime;
+                       this->nb_atoms += data->nb_atoms;
+                       this->total_lat += data->total_lat;
+                       list_splice(&data->work_list, &this->work_list);
+                       if (this->max_lat < data->max_lat) {
+                               this->max_lat = data->max_lat;
+                               this->max_lat_at = data->max_lat_at;
+                       }
+                       zfree(&data);
+                       return;
+               }
+       }
+
+       data->num_merged++;
+       rb_link_node(&data->node, parent, new);
+       rb_insert_color(&data->node, root);
+}
+
+static void perf_sched__merge_lat(struct perf_sched *sched)
+{
+       struct work_atoms *data;
+       struct rb_node *node;
+
+       if (sched->skip_merge)
+               return;
+
+       while ((node = rb_first(&sched->atom_root))) {
+               rb_erase(node, &sched->atom_root);
+               data = rb_entry(node, struct work_atoms, node);
+               __merge_work_atoms(&sched->merged_atom_root, data);
+       }
+}
+
 static int perf_sched__lat(struct perf_sched *sched)
 {
        struct rb_node *next;
@@ -1551,6 +1644,7 @@ static int perf_sched__lat(struct perf_sched *sched)
        if (perf_sched__read_events(sched))
                return -1;
 
+       perf_sched__merge_lat(sched);
        perf_sched__sort_lat(sched);
 
        printf("\n -----------------------------------------------------------------------------------------------------------------\n");
@@ -1702,6 +1796,7 @@ int cmd_sched(int argc, const char **argv, const char *prefix __maybe_unused)
                .profile_cpu          = -1,
                .next_shortname1      = 'A',
                .next_shortname2      = '0',
+               .skip_merge           = 0,
        };
        const struct option latency_options[] = {
        OPT_STRING('s', "sort", &sched.sort_order, "key[,key2...]",
@@ -1712,6 +1807,8 @@ int cmd_sched(int argc, const char **argv, const char *prefix __maybe_unused)
                    "CPU to profile on"),
        OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
                    "dump raw trace in ASCII"),
+       OPT_BOOLEAN('p', "pids", &sched.skip_merge,
+                   "latency stats per pid instead of per comm"),
        OPT_END()
        };
        const struct option replay_options[] = {
index 58f10b8e6ff20d51429634b8f79628188fbc2dd4..24809787369f5a1303451de5264798d0e31792b6 100644 (file)
@@ -16,6 +16,7 @@
 #include "util/evsel.h"
 #include "util/sort.h"
 #include "util/data.h"
+#include "util/auxtrace.h"
 #include <linux/bitmap.h>
 
 static char const              *script_name;
@@ -26,6 +27,7 @@ static u64                    nr_unordered;
 static bool                    no_callchain;
 static bool                    latency_format;
 static bool                    system_wide;
+static bool                    print_flags;
 static const char              *cpu_list;
 static DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS);
 
@@ -146,9 +148,10 @@ static const char *output_field2str(enum perf_output_field field)
 
 #define PRINT_FIELD(x)  (output[attr->type].fields & PERF_OUTPUT_##x)
 
-static int perf_evsel__check_stype(struct perf_evsel *evsel,
-                                  u64 sample_type, const char *sample_msg,
-                                  enum perf_output_field field)
+static int perf_evsel__do_check_stype(struct perf_evsel *evsel,
+                                     u64 sample_type, const char *sample_msg,
+                                     enum perf_output_field field,
+                                     bool allow_user_set)
 {
        struct perf_event_attr *attr = &evsel->attr;
        int type = attr->type;
@@ -158,6 +161,8 @@ static int perf_evsel__check_stype(struct perf_evsel *evsel,
                return 0;
 
        if (output[type].user_set) {
+               if (allow_user_set)
+                       return 0;
                evname = perf_evsel__name(evsel);
                pr_err("Samples for '%s' event do not have %s attribute set. "
                       "Cannot print '%s' field.\n",
@@ -175,10 +180,22 @@ static int perf_evsel__check_stype(struct perf_evsel *evsel,
        return 0;
 }
 
+static int perf_evsel__check_stype(struct perf_evsel *evsel,
+                                  u64 sample_type, const char *sample_msg,
+                                  enum perf_output_field field)
+{
+       return perf_evsel__do_check_stype(evsel, sample_type, sample_msg, field,
+                                         false);
+}
+
 static int perf_evsel__check_attr(struct perf_evsel *evsel,
                                  struct perf_session *session)
 {
        struct perf_event_attr *attr = &evsel->attr;
+       bool allow_user_set;
+
+       allow_user_set = perf_header__has_feat(&session->header,
+                                              HEADER_AUXTRACE);
 
        if (PRINT_FIELD(TRACE) &&
                !perf_session__has_traces(session, "record -R"))
@@ -191,8 +208,8 @@ static int perf_evsel__check_attr(struct perf_evsel *evsel,
        }
 
        if (PRINT_FIELD(ADDR) &&
-               perf_evsel__check_stype(evsel, PERF_SAMPLE_ADDR, "ADDR",
-                                       PERF_OUTPUT_ADDR))
+               perf_evsel__do_check_stype(evsel, PERF_SAMPLE_ADDR, "ADDR",
+                                          PERF_OUTPUT_ADDR, allow_user_set))
                return -EINVAL;
 
        if (PRINT_FIELD(SYM) && !PRINT_FIELD(IP) && !PRINT_FIELD(ADDR)) {
@@ -229,8 +246,8 @@ static int perf_evsel__check_attr(struct perf_evsel *evsel,
                return -EINVAL;
 
        if (PRINT_FIELD(CPU) &&
-               perf_evsel__check_stype(evsel, PERF_SAMPLE_CPU, "CPU",
-                                       PERF_OUTPUT_CPU))
+               perf_evsel__do_check_stype(evsel, PERF_SAMPLE_CPU, "CPU",
+                                          PERF_OUTPUT_CPU, allow_user_set))
                return -EINVAL;
 
        if (PRINT_FIELD(PERIOD) &&
@@ -445,6 +462,25 @@ static void print_sample_bts(union perf_event *event,
        printf("\n");
 }
 
+static void print_sample_flags(u32 flags)
+{
+       const char *chars = PERF_IP_FLAG_CHARS;
+       const int n = strlen(PERF_IP_FLAG_CHARS);
+       char str[33];
+       int i, pos = 0;
+
+       for (i = 0; i < n; i++, flags >>= 1) {
+               if (flags & 1)
+                       str[pos++] = chars[i];
+       }
+       for (; i < 32; i++, flags >>= 1) {
+               if (flags & 1)
+                       str[pos++] = '?';
+       }
+       str[pos] = 0;
+       printf("  %-4s ", str);
+}
+
 static void process_event(union perf_event *event, struct perf_sample *sample,
                          struct perf_evsel *evsel, struct addr_location *al)
 {
@@ -464,6 +500,9 @@ static void process_event(union perf_event *event, struct perf_sample *sample,
                printf("%s: ", evname ? evname : "[unknown]");
        }
 
+       if (print_flags)
+               print_sample_flags(sample->flags);
+
        if (is_bts_event(attr)) {
                print_sample_bts(event, sample, evsel, thread, al);
                return;
@@ -568,13 +607,14 @@ static int process_sample_event(struct perf_tool *tool __maybe_unused,
        }
 
        if (al.filtered)
-               return 0;
+               goto out_put;
 
        if (cpu_list && !test_bit(sample->cpu, cpu_bitmap))
-               return 0;
+               goto out_put;
 
        scripting_ops->process_event(event, sample, evsel, &al);
-
+out_put:
+       addr_location__put(&al);
        return 0;
 }
 
@@ -642,8 +682,8 @@ static int process_comm_event(struct perf_tool *tool,
        print_sample_start(sample, thread, evsel);
        perf_event__fprintf(event, stdout);
        ret = 0;
-
 out:
+       thread__put(thread);
        return ret;
 }
 
@@ -674,6 +714,7 @@ static int process_fork_event(struct perf_tool *tool,
        }
        print_sample_start(sample, thread, evsel);
        perf_event__fprintf(event, stdout);
+       thread__put(thread);
 
        return 0;
 }
@@ -682,6 +723,7 @@ static int process_exit_event(struct perf_tool *tool,
                              struct perf_sample *sample,
                              struct machine *machine)
 {
+       int err = 0;
        struct thread *thread;
        struct perf_script *script = container_of(tool, struct perf_script, tool);
        struct perf_session *session = script->session;
@@ -703,9 +745,10 @@ static int process_exit_event(struct perf_tool *tool,
        perf_event__fprintf(event, stdout);
 
        if (perf_event__process_exit(tool, event, sample, machine) < 0)
-               return -1;
+               err = -1;
 
-       return 0;
+       thread__put(thread);
+       return err;
 }
 
 static int process_mmap_event(struct perf_tool *tool,
@@ -735,7 +778,7 @@ static int process_mmap_event(struct perf_tool *tool,
        }
        print_sample_start(sample, thread, evsel);
        perf_event__fprintf(event, stdout);
-
+       thread__put(thread);
        return 0;
 }
 
@@ -766,7 +809,7 @@ static int process_mmap2_event(struct perf_tool *tool,
        }
        print_sample_start(sample, thread, evsel);
        perf_event__fprintf(event, stdout);
-
+       thread__put(thread);
        return 0;
 }
 
@@ -999,12 +1042,15 @@ static int parse_output_fields(const struct option *opt __maybe_unused,
                }
        }
 
-       tok = strtok(tok, ",");
-       while (tok) {
+       for (tok = strtok(tok, ","); tok; tok = strtok(NULL, ",")) {
                for (i = 0; i < imax; ++i) {
                        if (strcmp(tok, all_output_options[i].str) == 0)
                                break;
                }
+               if (i == imax && strcmp(tok, "flags") == 0) {
+                       print_flags = true;
+                       continue;
+               }
                if (i == imax) {
                        fprintf(stderr, "Invalid field requested.\n");
                        rc = -EINVAL;
@@ -1032,8 +1078,6 @@ static int parse_output_fields(const struct option *opt __maybe_unused,
                        }
                        output[type].fields |= all_output_options[i].field;
                }
-
-               tok = strtok(NULL, ",");
        }
 
        if (type >= 0) {
@@ -1497,6 +1541,7 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused)
        char *rec_script_path = NULL;
        char *rep_script_path = NULL;
        struct perf_session *session;
+       struct itrace_synth_opts itrace_synth_opts = { .set = false, };
        char *script_path = NULL;
        const char **__argv;
        int i, j, err = 0;
@@ -1511,6 +1556,10 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused)
                        .attr            = process_attr,
                        .tracing_data    = perf_event__process_tracing_data,
                        .build_id        = perf_event__process_build_id,
+                       .id_index        = perf_event__process_id_index,
+                       .auxtrace_info   = perf_event__process_auxtrace_info,
+                       .auxtrace        = perf_event__process_auxtrace,
+                       .auxtrace_error  = perf_event__process_auxtrace_error,
                        .ordered_events  = true,
                        .ordering_requires_timestamps = true,
                },
@@ -1549,7 +1598,7 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused)
                     "comma separated output fields prepend with 'type:'. "
                     "Valid types: hw,sw,trace,raw. "
                     "Fields: comm,tid,pid,time,cpu,event,trace,ip,sym,dso,"
-                    "addr,symoff,period", parse_output_fields),
+                    "addr,symoff,period,flags", parse_output_fields),
        OPT_BOOLEAN('a', "all-cpus", &system_wide,
                    "system-wide collection from all CPUs"),
        OPT_STRING('S', "symbols", &symbol_conf.sym_list_str, "symbol[,symbol...]",
@@ -1570,6 +1619,9 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused)
        OPT_BOOLEAN('\0', "show-mmap-events", &script.show_mmap_events,
                    "Show the mmap events"),
        OPT_BOOLEAN('f', "force", &file.force, "don't complain, do it"),
+       OPT_CALLBACK_OPTARG(0, "itrace", &itrace_synth_opts, NULL, "opts",
+                           "Instruction Tracing options",
+                           itrace_parse_synth_opts),
        OPT_END()
        };
        const char * const script_subcommands[] = { "record", "report", NULL };
@@ -1765,6 +1817,8 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused)
 
        script.session = session;
 
+       session->itrace_synth_opts = &itrace_synth_opts;
+
        if (cpu_list) {
                err = perf_session__cpu_bitmap(session, cpu_list, cpu_bitmap);
                if (err < 0)
index f7b8218785f6fa8911bc9c8544d7eb14ff9e2f2f..fcf99bdeb19e1cf73c54e0b6edc6d4426dfe1f48 100644 (file)
@@ -73,8 +73,8 @@ static void print_counter(struct perf_evsel *counter, char *prefix);
 static void print_aggr(char *prefix);
 
 /* Default events used for perf stat -T */
-static const char * const transaction_attrs[] = {
-       "task-clock",
+static const char *transaction_attrs = {
+       "task-clock,"
        "{"
        "instructions,"
        "cycles,"
@@ -86,8 +86,8 @@ static const char * const transaction_attrs[] = {
 };
 
 /* More limited version when the CPU does not have all events. */
-static const char * const transaction_limited_attrs[] = {
-       "task-clock",
+static const char * transaction_limited_attrs = {
+       "task-clock,"
        "{"
        "instructions,"
        "cycles,"
@@ -96,30 +96,12 @@ static const char * const transaction_limited_attrs[] = {
        "}"
 };
 
-/* must match transaction_attrs and the beginning limited_attrs */
-enum {
-       T_TASK_CLOCK,
-       T_INSTRUCTIONS,
-       T_CYCLES,
-       T_CYCLES_IN_TX,
-       T_TRANSACTION_START,
-       T_ELISION_START,
-       T_CYCLES_IN_TX_CP,
-};
-
 static struct perf_evlist      *evsel_list;
 
 static struct target target = {
        .uid    = UINT_MAX,
 };
 
-enum aggr_mode {
-       AGGR_NONE,
-       AGGR_GLOBAL,
-       AGGR_SOCKET,
-       AGGR_CORE,
-};
-
 static int                     run_count                       =  1;
 static bool                    no_inherit                      = false;
 static bool                    scale                           =  true;
@@ -147,10 +129,6 @@ static int                 (*aggr_get_id)(struct cpu_map *m, int cpu);
 
 static volatile int done = 0;
 
-struct perf_stat {
-       struct stats      res_stats[3];
-};
-
 static inline void diff_timespec(struct timespec *r, struct timespec *a,
                                 struct timespec *b)
 {
@@ -180,6 +158,8 @@ static void perf_evsel__reset_stat_priv(struct perf_evsel *evsel)
 
        for (i = 0; i < 3; i++)
                init_stats(&ps->res_stats[i]);
+
+       perf_stat_evsel_id_init(evsel);
 }
 
 static int perf_evsel__alloc_stat_priv(struct perf_evsel *evsel)
@@ -198,24 +178,19 @@ static void perf_evsel__free_stat_priv(struct perf_evsel *evsel)
 
 static int perf_evsel__alloc_prev_raw_counts(struct perf_evsel *evsel)
 {
-       void *addr;
-       size_t sz;
+       struct perf_counts *counts;
 
-       sz = sizeof(*evsel->counts) +
-            (perf_evsel__nr_cpus(evsel) * sizeof(struct perf_counts_values));
+       counts = perf_counts__new(perf_evsel__nr_cpus(evsel));
+       if (counts)
+               evsel->prev_raw_counts = counts;
 
-       addr = zalloc(sz);
-       if (!addr)
-               return -ENOMEM;
-
-       evsel->prev_raw_counts =  addr;
-
-       return 0;
+       return counts ? 0 : -ENOMEM;
 }
 
 static void perf_evsel__free_prev_raw_counts(struct perf_evsel *evsel)
 {
-       zfree(&evsel->prev_raw_counts);
+       perf_counts__delete(evsel->prev_raw_counts);
+       evsel->prev_raw_counts = NULL;
 }
 
 static void perf_evlist__free_stats(struct perf_evlist *evlist)
@@ -247,22 +222,6 @@ out_free:
        return -1;
 }
 
-static struct stats runtime_nsecs_stats[MAX_NR_CPUS];
-static struct stats runtime_cycles_stats[MAX_NR_CPUS];
-static struct stats runtime_stalled_cycles_front_stats[MAX_NR_CPUS];
-static struct stats runtime_stalled_cycles_back_stats[MAX_NR_CPUS];
-static struct stats runtime_branches_stats[MAX_NR_CPUS];
-static struct stats runtime_cacherefs_stats[MAX_NR_CPUS];
-static struct stats runtime_l1_dcache_stats[MAX_NR_CPUS];
-static struct stats runtime_l1_icache_stats[MAX_NR_CPUS];
-static struct stats runtime_ll_cache_stats[MAX_NR_CPUS];
-static struct stats runtime_itlb_cache_stats[MAX_NR_CPUS];
-static struct stats runtime_dtlb_cache_stats[MAX_NR_CPUS];
-static struct stats runtime_cycles_in_tx_stats[MAX_NR_CPUS];
-static struct stats walltime_nsecs_stats;
-static struct stats runtime_transaction_stats[MAX_NR_CPUS];
-static struct stats runtime_elision_stats[MAX_NR_CPUS];
-
 static void perf_stat__reset_stats(struct perf_evlist *evlist)
 {
        struct perf_evsel *evsel;
@@ -272,23 +231,7 @@ static void perf_stat__reset_stats(struct perf_evlist *evlist)
                perf_evsel__reset_counts(evsel, perf_evsel__nr_cpus(evsel));
        }
 
-       memset(runtime_nsecs_stats, 0, sizeof(runtime_nsecs_stats));
-       memset(runtime_cycles_stats, 0, sizeof(runtime_cycles_stats));
-       memset(runtime_stalled_cycles_front_stats, 0, sizeof(runtime_stalled_cycles_front_stats));
-       memset(runtime_stalled_cycles_back_stats, 0, sizeof(runtime_stalled_cycles_back_stats));
-       memset(runtime_branches_stats, 0, sizeof(runtime_branches_stats));
-       memset(runtime_cacherefs_stats, 0, sizeof(runtime_cacherefs_stats));
-       memset(runtime_l1_dcache_stats, 0, sizeof(runtime_l1_dcache_stats));
-       memset(runtime_l1_icache_stats, 0, sizeof(runtime_l1_icache_stats));
-       memset(runtime_ll_cache_stats, 0, sizeof(runtime_ll_cache_stats));
-       memset(runtime_itlb_cache_stats, 0, sizeof(runtime_itlb_cache_stats));
-       memset(runtime_dtlb_cache_stats, 0, sizeof(runtime_dtlb_cache_stats));
-       memset(runtime_cycles_in_tx_stats, 0,
-                       sizeof(runtime_cycles_in_tx_stats));
-       memset(runtime_transaction_stats, 0,
-               sizeof(runtime_transaction_stats));
-       memset(runtime_elision_stats, 0, sizeof(runtime_elision_stats));
-       memset(&walltime_nsecs_stats, 0, sizeof(walltime_nsecs_stats));
+       perf_stat__reset_shadow_stats();
 }
 
 static int create_perf_stat_counter(struct perf_evsel *evsel)
@@ -325,70 +268,6 @@ static inline int nsec_counter(struct perf_evsel *evsel)
        return 0;
 }
 
-static struct perf_evsel *nth_evsel(int n)
-{
-       static struct perf_evsel **array;
-       static int array_len;
-       struct perf_evsel *ev;
-       int j;
-
-       /* Assumes this only called when evsel_list does not change anymore. */
-       if (!array) {
-               evlist__for_each(evsel_list, ev)
-                       array_len++;
-               array = malloc(array_len * sizeof(void *));
-               if (!array)
-                       exit(ENOMEM);
-               j = 0;
-               evlist__for_each(evsel_list, ev)
-                       array[j++] = ev;
-       }
-       if (n < array_len)
-               return array[n];
-       return NULL;
-}
-
-/*
- * Update various tracking values we maintain to print
- * more semantic information such as miss/hit ratios,
- * instruction rates, etc:
- */
-static void update_shadow_stats(struct perf_evsel *counter, u64 *count,
-                               int cpu)
-{
-       if (perf_evsel__match(counter, SOFTWARE, SW_TASK_CLOCK))
-               update_stats(&runtime_nsecs_stats[cpu], count[0]);
-       else if (perf_evsel__match(counter, HARDWARE, HW_CPU_CYCLES))
-               update_stats(&runtime_cycles_stats[cpu], count[0]);
-       else if (transaction_run &&
-                perf_evsel__cmp(counter, nth_evsel(T_CYCLES_IN_TX)))
-               update_stats(&runtime_cycles_in_tx_stats[cpu], count[0]);
-       else if (transaction_run &&
-                perf_evsel__cmp(counter, nth_evsel(T_TRANSACTION_START)))
-               update_stats(&runtime_transaction_stats[cpu], count[0]);
-       else if (transaction_run &&
-                perf_evsel__cmp(counter, nth_evsel(T_ELISION_START)))
-               update_stats(&runtime_elision_stats[cpu], count[0]);
-       else if (perf_evsel__match(counter, HARDWARE, HW_STALLED_CYCLES_FRONTEND))
-               update_stats(&runtime_stalled_cycles_front_stats[cpu], count[0]);
-       else if (perf_evsel__match(counter, HARDWARE, HW_STALLED_CYCLES_BACKEND))
-               update_stats(&runtime_stalled_cycles_back_stats[cpu], count[0]);
-       else if (perf_evsel__match(counter, HARDWARE, HW_BRANCH_INSTRUCTIONS))
-               update_stats(&runtime_branches_stats[cpu], count[0]);
-       else if (perf_evsel__match(counter, HARDWARE, HW_CACHE_REFERENCES))
-               update_stats(&runtime_cacherefs_stats[cpu], count[0]);
-       else if (perf_evsel__match(counter, HW_CACHE, HW_CACHE_L1D))
-               update_stats(&runtime_l1_dcache_stats[cpu], count[0]);
-       else if (perf_evsel__match(counter, HW_CACHE, HW_CACHE_L1I))
-               update_stats(&runtime_l1_icache_stats[cpu], count[0]);
-       else if (perf_evsel__match(counter, HW_CACHE, HW_CACHE_LL))
-               update_stats(&runtime_ll_cache_stats[cpu], count[0]);
-       else if (perf_evsel__match(counter, HW_CACHE, HW_CACHE_DTLB))
-               update_stats(&runtime_dtlb_cache_stats[cpu], count[0]);
-       else if (perf_evsel__match(counter, HW_CACHE, HW_CACHE_ITLB))
-               update_stats(&runtime_itlb_cache_stats[cpu], count[0]);
-}
-
 static void zero_per_pkg(struct perf_evsel *counter)
 {
        if (counter->per_pkg_mask)
@@ -449,7 +328,7 @@ static int read_cb(struct perf_evsel *evsel, int cpu, int thread __maybe_unused,
                perf_counts_values__scale(count, scale, NULL);
                evsel->counts->cpu[cpu] = *count;
                if (aggr_mode == AGGR_NONE)
-                       update_shadow_stats(evsel, count->values, cpu);
+                       perf_stat__update_shadow_stats(evsel, count->values, cpu);
                break;
        case AGGR_GLOBAL:
                aggr->val += count->val;
@@ -497,7 +376,7 @@ static int read_counter_aggr(struct perf_evsel *counter)
        /*
         * Save the full runtime - to allow normalization during printout:
         */
-       update_shadow_stats(counter, count, 0);
+       perf_stat__update_shadow_stats(counter, count, 0);
 
        return 0;
 }
@@ -665,7 +544,10 @@ static int __run_perf_stat(int argc, const char **argv)
                                        ui__warning("%s event is not supported by the kernel.\n",
                                                    perf_evsel__name(counter));
                                counter->supported = false;
-                               continue;
+
+                               if ((counter->leader != counter) ||
+                                   !(counter->leader->nr_members > 1))
+                                       continue;
                        }
 
                        perf_evsel__open_strerror(counter, &target,
@@ -875,188 +757,8 @@ static void nsec_printout(int id, int nr, struct perf_evsel *evsel, double avg)
                fprintf(output, "                                   ");
 }
 
-/* used for get_ratio_color() */
-enum grc_type {
-       GRC_STALLED_CYCLES_FE,
-       GRC_STALLED_CYCLES_BE,
-       GRC_CACHE_MISSES,
-       GRC_MAX_NR
-};
-
-static const char *get_ratio_color(enum grc_type type, double ratio)
-{
-       static const double grc_table[GRC_MAX_NR][3] = {
-               [GRC_STALLED_CYCLES_FE] = { 50.0, 30.0, 10.0 },
-               [GRC_STALLED_CYCLES_BE] = { 75.0, 50.0, 20.0 },
-               [GRC_CACHE_MISSES]      = { 20.0, 10.0, 5.0 },
-       };
-       const char *color = PERF_COLOR_NORMAL;
-
-       if (ratio > grc_table[type][0])
-               color = PERF_COLOR_RED;
-       else if (ratio > grc_table[type][1])
-               color = PERF_COLOR_MAGENTA;
-       else if (ratio > grc_table[type][2])
-               color = PERF_COLOR_YELLOW;
-
-       return color;
-}
-
-static void print_stalled_cycles_frontend(int cpu,
-                                         struct perf_evsel *evsel
-                                         __maybe_unused, double avg)
-{
-       double total, ratio = 0.0;
-       const char *color;
-
-       total = avg_stats(&runtime_cycles_stats[cpu]);
-
-       if (total)
-               ratio = avg / total * 100.0;
-
-       color = get_ratio_color(GRC_STALLED_CYCLES_FE, ratio);
-
-       fprintf(output, " #  ");
-       color_fprintf(output, color, "%6.2f%%", ratio);
-       fprintf(output, " frontend cycles idle   ");
-}
-
-static void print_stalled_cycles_backend(int cpu,
-                                        struct perf_evsel *evsel
-                                        __maybe_unused, double avg)
-{
-       double total, ratio = 0.0;
-       const char *color;
-
-       total = avg_stats(&runtime_cycles_stats[cpu]);
-
-       if (total)
-               ratio = avg / total * 100.0;
-
-       color = get_ratio_color(GRC_STALLED_CYCLES_BE, ratio);
-
-       fprintf(output, " #  ");
-       color_fprintf(output, color, "%6.2f%%", ratio);
-       fprintf(output, " backend  cycles idle   ");
-}
-
-static void print_branch_misses(int cpu,
-                               struct perf_evsel *evsel __maybe_unused,
-                               double avg)
-{
-       double total, ratio = 0.0;
-       const char *color;
-
-       total = avg_stats(&runtime_branches_stats[cpu]);
-
-       if (total)
-               ratio = avg / total * 100.0;
-
-       color = get_ratio_color(GRC_CACHE_MISSES, ratio);
-
-       fprintf(output, " #  ");
-       color_fprintf(output, color, "%6.2f%%", ratio);
-       fprintf(output, " of all branches        ");
-}
-
-static void print_l1_dcache_misses(int cpu,
-                                  struct perf_evsel *evsel __maybe_unused,
-                                  double avg)
-{
-       double total, ratio = 0.0;
-       const char *color;
-
-       total = avg_stats(&runtime_l1_dcache_stats[cpu]);
-
-       if (total)
-               ratio = avg / total * 100.0;
-
-       color = get_ratio_color(GRC_CACHE_MISSES, ratio);
-
-       fprintf(output, " #  ");
-       color_fprintf(output, color, "%6.2f%%", ratio);
-       fprintf(output, " of all L1-dcache hits  ");
-}
-
-static void print_l1_icache_misses(int cpu,
-                                  struct perf_evsel *evsel __maybe_unused,
-                                  double avg)
-{
-       double total, ratio = 0.0;
-       const char *color;
-
-       total = avg_stats(&runtime_l1_icache_stats[cpu]);
-
-       if (total)
-               ratio = avg / total * 100.0;
-
-       color = get_ratio_color(GRC_CACHE_MISSES, ratio);
-
-       fprintf(output, " #  ");
-       color_fprintf(output, color, "%6.2f%%", ratio);
-       fprintf(output, " of all L1-icache hits  ");
-}
-
-static void print_dtlb_cache_misses(int cpu,
-                                   struct perf_evsel *evsel __maybe_unused,
-                                   double avg)
-{
-       double total, ratio = 0.0;
-       const char *color;
-
-       total = avg_stats(&runtime_dtlb_cache_stats[cpu]);
-
-       if (total)
-               ratio = avg / total * 100.0;
-
-       color = get_ratio_color(GRC_CACHE_MISSES, ratio);
-
-       fprintf(output, " #  ");
-       color_fprintf(output, color, "%6.2f%%", ratio);
-       fprintf(output, " of all dTLB cache hits ");
-}
-
-static void print_itlb_cache_misses(int cpu,
-                                   struct perf_evsel *evsel __maybe_unused,
-                                   double avg)
-{
-       double total, ratio = 0.0;
-       const char *color;
-
-       total = avg_stats(&runtime_itlb_cache_stats[cpu]);
-
-       if (total)
-               ratio = avg / total * 100.0;
-
-       color = get_ratio_color(GRC_CACHE_MISSES, ratio);
-
-       fprintf(output, " #  ");
-       color_fprintf(output, color, "%6.2f%%", ratio);
-       fprintf(output, " of all iTLB cache hits ");
-}
-
-static void print_ll_cache_misses(int cpu,
-                                 struct perf_evsel *evsel __maybe_unused,
-                                 double avg)
-{
-       double total, ratio = 0.0;
-       const char *color;
-
-       total = avg_stats(&runtime_ll_cache_stats[cpu]);
-
-       if (total)
-               ratio = avg / total * 100.0;
-
-       color = get_ratio_color(GRC_CACHE_MISSES, ratio);
-
-       fprintf(output, " #  ");
-       color_fprintf(output, color, "%6.2f%%", ratio);
-       fprintf(output, " of all LL-cache hits   ");
-}
-
 static void abs_printout(int id, int nr, struct perf_evsel *evsel, double avg)
 {
-       double total, ratio = 0.0, total2;
        double sc =  evsel->scale;
        const char *fmt;
        int cpu = cpu_map__id_to_cpu(id);
@@ -1090,138 +792,7 @@ static void abs_printout(int id, int nr, struct perf_evsel *evsel, double avg)
        if (csv_output || interval)
                return;
 
-       if (perf_evsel__match(evsel, HARDWARE, HW_INSTRUCTIONS)) {
-               total = avg_stats(&runtime_cycles_stats[cpu]);
-               if (total) {
-                       ratio = avg / total;
-                       fprintf(output, " #   %5.2f  insns per cycle        ", ratio);
-               } else {
-                       fprintf(output, "                                   ");
-               }
-               total = avg_stats(&runtime_stalled_cycles_front_stats[cpu]);
-               total = max(total, avg_stats(&runtime_stalled_cycles_back_stats[cpu]));
-
-               if (total && avg) {
-                       ratio = total / avg;
-                       fprintf(output, "\n");
-                       if (aggr_mode == AGGR_NONE)
-                               fprintf(output, "        ");
-                       fprintf(output, "                                                  #   %5.2f  stalled cycles per insn", ratio);
-               }
-
-       } else if (perf_evsel__match(evsel, HARDWARE, HW_BRANCH_MISSES) &&
-                       runtime_branches_stats[cpu].n != 0) {
-               print_branch_misses(cpu, evsel, avg);
-       } else if (
-               evsel->attr.type == PERF_TYPE_HW_CACHE &&
-               evsel->attr.config ==  ( PERF_COUNT_HW_CACHE_L1D |
-                                       ((PERF_COUNT_HW_CACHE_OP_READ) << 8) |
-                                       ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16)) &&
-                       runtime_l1_dcache_stats[cpu].n != 0) {
-               print_l1_dcache_misses(cpu, evsel, avg);
-       } else if (
-               evsel->attr.type == PERF_TYPE_HW_CACHE &&
-               evsel->attr.config ==  ( PERF_COUNT_HW_CACHE_L1I |
-                                       ((PERF_COUNT_HW_CACHE_OP_READ) << 8) |
-                                       ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16)) &&
-                       runtime_l1_icache_stats[cpu].n != 0) {
-               print_l1_icache_misses(cpu, evsel, avg);
-       } else if (
-               evsel->attr.type == PERF_TYPE_HW_CACHE &&
-               evsel->attr.config ==  ( PERF_COUNT_HW_CACHE_DTLB |
-                                       ((PERF_COUNT_HW_CACHE_OP_READ) << 8) |
-                                       ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16)) &&
-                       runtime_dtlb_cache_stats[cpu].n != 0) {
-               print_dtlb_cache_misses(cpu, evsel, avg);
-       } else if (
-               evsel->attr.type == PERF_TYPE_HW_CACHE &&
-               evsel->attr.config ==  ( PERF_COUNT_HW_CACHE_ITLB |
-                                       ((PERF_COUNT_HW_CACHE_OP_READ) << 8) |
-                                       ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16)) &&
-                       runtime_itlb_cache_stats[cpu].n != 0) {
-               print_itlb_cache_misses(cpu, evsel, avg);
-       } else if (
-               evsel->attr.type == PERF_TYPE_HW_CACHE &&
-               evsel->attr.config ==  ( PERF_COUNT_HW_CACHE_LL |
-                                       ((PERF_COUNT_HW_CACHE_OP_READ) << 8) |
-                                       ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16)) &&
-                       runtime_ll_cache_stats[cpu].n != 0) {
-               print_ll_cache_misses(cpu, evsel, avg);
-       } else if (perf_evsel__match(evsel, HARDWARE, HW_CACHE_MISSES) &&
-                       runtime_cacherefs_stats[cpu].n != 0) {
-               total = avg_stats(&runtime_cacherefs_stats[cpu]);
-
-               if (total)
-                       ratio = avg * 100 / total;
-
-               fprintf(output, " # %8.3f %% of all cache refs    ", ratio);
-
-       } else if (perf_evsel__match(evsel, HARDWARE, HW_STALLED_CYCLES_FRONTEND)) {
-               print_stalled_cycles_frontend(cpu, evsel, avg);
-       } else if (perf_evsel__match(evsel, HARDWARE, HW_STALLED_CYCLES_BACKEND)) {
-               print_stalled_cycles_backend(cpu, evsel, avg);
-       } else if (perf_evsel__match(evsel, HARDWARE, HW_CPU_CYCLES)) {
-               total = avg_stats(&runtime_nsecs_stats[cpu]);
-
-               if (total) {
-                       ratio = avg / total;
-                       fprintf(output, " # %8.3f GHz                    ", ratio);
-               } else {
-                       fprintf(output, "                                   ");
-               }
-       } else if (transaction_run &&
-                  perf_evsel__cmp(evsel, nth_evsel(T_CYCLES_IN_TX))) {
-               total = avg_stats(&runtime_cycles_stats[cpu]);
-               if (total)
-                       fprintf(output,
-                               " #   %5.2f%% transactional cycles   ",
-                               100.0 * (avg / total));
-       } else if (transaction_run &&
-                  perf_evsel__cmp(evsel, nth_evsel(T_CYCLES_IN_TX_CP))) {
-               total = avg_stats(&runtime_cycles_stats[cpu]);
-               total2 = avg_stats(&runtime_cycles_in_tx_stats[cpu]);
-               if (total2 < avg)
-                       total2 = avg;
-               if (total)
-                       fprintf(output,
-                               " #   %5.2f%% aborted cycles         ",
-                               100.0 * ((total2-avg) / total));
-       } else if (transaction_run &&
-                  perf_evsel__cmp(evsel, nth_evsel(T_TRANSACTION_START)) &&
-                  avg > 0 &&
-                  runtime_cycles_in_tx_stats[cpu].n != 0) {
-               total = avg_stats(&runtime_cycles_in_tx_stats[cpu]);
-
-               if (total)
-                       ratio = total / avg;
-
-               fprintf(output, " # %8.0f cycles / transaction   ", ratio);
-       } else if (transaction_run &&
-                  perf_evsel__cmp(evsel, nth_evsel(T_ELISION_START)) &&
-                  avg > 0 &&
-                  runtime_cycles_in_tx_stats[cpu].n != 0) {
-               total = avg_stats(&runtime_cycles_in_tx_stats[cpu]);
-
-               if (total)
-                       ratio = total / avg;
-
-               fprintf(output, " # %8.0f cycles / elision       ", ratio);
-       } else if (runtime_nsecs_stats[cpu].n != 0) {
-               char unit = 'M';
-
-               total = avg_stats(&runtime_nsecs_stats[cpu]);
-
-               if (total)
-                       ratio = 1000.0 * avg / total;
-               if (ratio < 0.001) {
-                       ratio *= 1000;
-                       unit = 'K';
-               }
-
-               fprintf(output, " # %8.3f %c/sec                  ", ratio, unit);
-       } else {
-               fprintf(output, "                                   ");
-       }
+       perf_stat__print_shadow_stats(output, evsel, avg, cpu, aggr_mode);
 }
 
 static void print_aggr(char *prefix)
@@ -1536,17 +1107,6 @@ static int perf_stat_init_aggr_mode(void)
        return 0;
 }
 
-static int setup_events(const char * const *attrs, unsigned len)
-{
-       unsigned i;
-
-       for (i = 0; i < len; i++) {
-               if (parse_events(evsel_list, attrs[i]))
-                       return -1;
-       }
-       return 0;
-}
-
 /*
  * Add default attributes, if there were no attributes specified or
  * if -d/--detailed, -d -d or -d -d -d is used:
@@ -1668,12 +1228,10 @@ static int add_default_attributes(void)
                int err;
                if (pmu_have_event("cpu", "cycles-ct") &&
                    pmu_have_event("cpu", "el-start"))
-                       err = setup_events(transaction_attrs,
-                                       ARRAY_SIZE(transaction_attrs));
+                       err = parse_events(evsel_list, transaction_attrs, NULL);
                else
-                       err = setup_events(transaction_limited_attrs,
-                                ARRAY_SIZE(transaction_limited_attrs));
-               if (err < 0) {
+                       err = parse_events(evsel_list, transaction_limited_attrs, NULL);
+               if (err) {
                        fprintf(stderr, "Cannot set up transaction events\n");
                        return -1;
                }
index e50fe1187b0ba2ca808f6c0a0d3effd8156b4982..30e59620179daef63c272c3119f884a8caefd12f 100644 (file)
@@ -61,13 +61,13 @@ struct timechart {
                                tasks_only,
                                with_backtrace,
                                topology;
+       bool                    force;
        /* IO related settings */
-       u64                     io_events;
        bool                    io_only,
                                skip_eagain;
+       u64                     io_events;
        u64                     min_time,
                                merge_dist;
-       bool                    force;
 };
 
 struct per_pidcomm;
@@ -523,7 +523,7 @@ static const char *cat_backtrace(union perf_event *event,
                                 * Discard all.
                                 */
                                zfree(&p);
-                               goto exit;
+                               goto exit_put;
                        }
                        continue;
                }
@@ -538,7 +538,8 @@ static const char *cat_backtrace(union perf_event *event,
                else
                        fprintf(f, "..... %016" PRIx64 "\n", ip);
        }
-
+exit_put:
+       addr_location__put(&al);
 exit:
        fclose(f);
 
index 6a4d5d41c671d0ce176deb13d318de35acee0161..619a8696fda7c939cd0e6497abab5845813bda12 100644 (file)
@@ -235,10 +235,13 @@ static void perf_top__show_details(struct perf_top *top)
 
        more = symbol__annotate_printf(symbol, he->ms.map, top->sym_evsel,
                                       0, top->sym_pcnt_filter, top->print_entries, 4);
-       if (top->zero)
-               symbol__annotate_zero_histogram(symbol, top->sym_evsel->idx);
-       else
-               symbol__annotate_decay_histogram(symbol, top->sym_evsel->idx);
+
+       if (top->evlist->enabled) {
+               if (top->zero)
+                       symbol__annotate_zero_histogram(symbol, top->sym_evsel->idx);
+               else
+                       symbol__annotate_decay_histogram(symbol, top->sym_evsel->idx);
+       }
        if (more != 0)
                printf("%d lines not displayed, maybe increase display entries [e]\n", more);
 out_unlock:
@@ -276,11 +279,13 @@ static void perf_top__print_sym_table(struct perf_top *top)
                return;
        }
 
-       if (top->zero) {
-               hists__delete_entries(hists);
-       } else {
-               hists__decay_entries(hists, top->hide_user_symbols,
-                                    top->hide_kernel_symbols);
+       if (top->evlist->enabled) {
+               if (top->zero) {
+                       hists__delete_entries(hists);
+               } else {
+                       hists__decay_entries(hists, top->hide_user_symbols,
+                                            top->hide_kernel_symbols);
+               }
        }
 
        hists__collapse_resort(hists, NULL);
@@ -545,11 +550,13 @@ static void perf_top__sort_new_samples(void *arg)
 
        hists = evsel__hists(t->sym_evsel);
 
-       if (t->zero) {
-               hists__delete_entries(hists);
-       } else {
-               hists__decay_entries(hists, t->hide_user_symbols,
-                                    t->hide_kernel_symbols);
+       if (t->evlist->enabled) {
+               if (t->zero) {
+                       hists__delete_entries(hists);
+               } else {
+                       hists__decay_entries(hists, t->hide_user_symbols,
+                                            t->hide_kernel_symbols);
+               }
        }
 
        hists__collapse_resort(hists, NULL);
@@ -579,8 +586,27 @@ static void *display_thread_tui(void *arg)
                hists->uid_filter_str = top->record_opts.target.uid_str;
        }
 
-       perf_evlist__tui_browse_hists(top->evlist, help, &hbt, top->min_percent,
-                                     &top->session->header.env);
+       while (true)  {
+               int key = perf_evlist__tui_browse_hists(top->evlist, help, &hbt,
+                                                       top->min_percent,
+                                                       &top->session->header.env);
+
+               if (key != 'f')
+                       break;
+
+               perf_evlist__toggle_enable(top->evlist);
+               /*
+                * No need to refresh, resort/decay histogram entries
+                * if we are not collecting samples:
+                */
+               if (top->evlist->enabled) {
+                       hbt.refresh = top->delay_secs;
+                       help = "Press 'f' to disable the events or 'h' to see other hotkeys";
+               } else {
+                       help = "Press 'f' again to re-enable the events";
+                       hbt.refresh = 0;
+               }
+       }
 
        done = 1;
        return NULL;
@@ -775,7 +801,9 @@ static void perf_event__process_sample(struct perf_tool *tool,
        if (al.sym == NULL || !al.sym->ignore) {
                struct hists *hists = evsel__hists(evsel);
                struct hist_entry_iter iter = {
-                       .add_entry_cb = hist_iter__top_callback,
+                       .evsel          = evsel,
+                       .sample         = sample,
+                       .add_entry_cb   = hist_iter__top_callback,
                };
 
                if (symbol_conf.cumulate_callchain)
@@ -785,15 +813,14 @@ static void perf_event__process_sample(struct perf_tool *tool,
 
                pthread_mutex_lock(&hists->lock);
 
-               err = hist_entry_iter__add(&iter, &al, evsel, sample,
-                                          top->max_stack, top);
+               err = hist_entry_iter__add(&iter, &al, top->max_stack, top);
                if (err < 0)
                        pr_err("Problem incrementing symbol period, skipping event\n");
 
                pthread_mutex_unlock(&hists->lock);
        }
 
-       return;
+       addr_location__put(&al);
 }
 
 static void perf_top__mmap_read_idx(struct perf_top *top, int idx)
@@ -950,7 +977,7 @@ static int __cmd_top(struct perf_top *top)
                goto out_delete;
 
        machine__synthesize_threads(&top->session->machines.host, &opts->target,
-                                   top->evlist->threads, false);
+                                   top->evlist->threads, false, opts->proc_map_timeout);
        ret = perf_top__start_counters(top);
        if (ret)
                goto out_delete;
@@ -1060,6 +1087,7 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused)
                        .target         = {
                                .uses_mmap   = true,
                        },
+                       .proc_map_timeout    = 500,
                },
                .max_stack           = PERF_MAX_STACK_DEPTH,
                .sym_pcnt_filter     = 5,
@@ -1159,6 +1187,8 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused)
        OPT_STRING('w', "column-widths", &symbol_conf.col_width_list_str,
                   "width[,width...]",
                   "don't try to adjust column width, use these fixed values"),
+       OPT_UINTEGER(0, "proc-map-timeout", &opts->proc_map_timeout,
+                       "per thread proc mmap processing timeout in ms"),
        OPT_END()
        };
        const char * const top_usage[] = {
index e122970361f21af6d07c321480aefa2cb90bf31d..de5d277d1ad7cb97cac2c5da67032fc8a12ffdf6 100644 (file)
@@ -16,7 +16,6 @@
 
 #include <libaudit.h>
 #include <stdlib.h>
-#include <sys/eventfd.h>
 #include <sys/mman.h>
 #include <linux/futex.h>
 
 # define EFD_SEMAPHORE         1
 #endif
 
+#ifndef EFD_NONBLOCK
+# define EFD_NONBLOCK          00004000
+#endif
+
+#ifndef EFD_CLOEXEC
+# define EFD_CLOEXEC           02000000
+#endif
+
+#ifndef O_CLOEXEC
+# define O_CLOEXEC             02000000
+#endif
+
+#ifndef SOCK_DCCP
+# define SOCK_DCCP             6
+#endif
+
+#ifndef SOCK_CLOEXEC
+# define SOCK_CLOEXEC          02000000
+#endif
+
+#ifndef SOCK_NONBLOCK
+# define SOCK_NONBLOCK         00004000
+#endif
+
+#ifndef MSG_CMSG_CLOEXEC
+# define MSG_CMSG_CLOEXEC      0x40000000
+#endif
+
+#ifndef PERF_FLAG_FD_NO_GROUP
+# define PERF_FLAG_FD_NO_GROUP         (1UL << 0)
+#endif
+
+#ifndef PERF_FLAG_FD_OUTPUT
+# define PERF_FLAG_FD_OUTPUT           (1UL << 1)
+#endif
+
+#ifndef PERF_FLAG_PID_CGROUP
+# define PERF_FLAG_PID_CGROUP          (1UL << 2) /* pid=cgroup id, per-cpu mode only */
+#endif
+
+#ifndef PERF_FLAG_FD_CLOEXEC
+# define PERF_FLAG_FD_CLOEXEC          (1UL << 3) /* O_CLOEXEC */
+#endif
+
+
 struct tp_field {
        int offset;
        union {
@@ -331,6 +375,14 @@ static size_t syscall_arg__scnprintf_hex(char *bf, size_t size,
 
 #define SCA_HEX syscall_arg__scnprintf_hex
 
+static size_t syscall_arg__scnprintf_int(char *bf, size_t size,
+                                        struct syscall_arg *arg)
+{
+       return scnprintf(bf, size, "%d", arg->val);
+}
+
+#define SCA_INT syscall_arg__scnprintf_int
+
 static size_t syscall_arg__scnprintf_mmap_prot(char *bf, size_t size,
                                               struct syscall_arg *arg)
 {
@@ -783,6 +835,34 @@ static size_t syscall_arg__scnprintf_open_flags(char *bf, size_t size,
 
 #define SCA_OPEN_FLAGS syscall_arg__scnprintf_open_flags
 
+static size_t syscall_arg__scnprintf_perf_flags(char *bf, size_t size,
+                                               struct syscall_arg *arg)
+{
+       int printed = 0, flags = arg->val;
+
+       if (flags == 0)
+               return 0;
+
+#define        P_FLAG(n) \
+       if (flags & PERF_FLAG_##n) { \
+               printed += scnprintf(bf + printed, size - printed, "%s%s", printed ? "|" : "", #n); \
+               flags &= ~PERF_FLAG_##n; \
+       }
+
+       P_FLAG(FD_NO_GROUP);
+       P_FLAG(FD_OUTPUT);
+       P_FLAG(PID_CGROUP);
+       P_FLAG(FD_CLOEXEC);
+#undef P_FLAG
+
+       if (flags)
+               printed += scnprintf(bf + printed, size - printed, "%s%#x", printed ? "|" : "", flags);
+
+       return printed;
+}
+
+#define SCA_PERF_FLAGS syscall_arg__scnprintf_perf_flags
+
 static size_t syscall_arg__scnprintf_eventfd_flags(char *bf, size_t size,
                                                   struct syscall_arg *arg)
 {
@@ -1050,6 +1130,11 @@ static struct syscall_fmt {
        { .name     = "openat",     .errmsg = true,
          .arg_scnprintf = { [0] = SCA_FDAT, /* dfd */
                             [2] = SCA_OPEN_FLAGS, /* flags */ }, },
+       { .name     = "perf_event_open", .errmsg = true,
+         .arg_scnprintf = { [1] = SCA_INT, /* pid */
+                            [2] = SCA_INT, /* cpu */
+                            [3] = SCA_FD,  /* group_fd */
+                            [4] = SCA_PERF_FLAGS,  /* flags */ }, },
        { .name     = "pipe2",      .errmsg = true,
          .arg_scnprintf = { [1] = SCA_PIPE_FLAGS, /* flags */ }, },
        { .name     = "poll",       .errmsg = true, .timeout = true, },
@@ -1433,7 +1518,8 @@ static int trace__symbols_init(struct trace *trace, struct perf_evlist *evlist)
                return -ENOMEM;
 
        err = __machine__synthesize_threads(trace->host, &trace->tool, &trace->opts.target,
-                                           evlist->threads, trace__tool_process, false);
+                                           evlist->threads, trace__tool_process, false,
+                                           trace->opts.proc_map_timeout);
        if (err)
                symbol__exit();
 
@@ -1712,7 +1798,7 @@ static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel,
        void *args;
        size_t printed = 0;
        struct thread *thread;
-       int id = perf_evsel__sc_tp_uint(evsel, id, sample);
+       int id = perf_evsel__sc_tp_uint(evsel, id, sample), err = -1;
        struct syscall *sc = trace__syscall_info(trace, evsel, id);
        struct thread_trace *ttrace;
 
@@ -1725,14 +1811,14 @@ static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel,
        thread = machine__findnew_thread(trace->host, sample->pid, sample->tid);
        ttrace = thread__trace(thread, trace->output);
        if (ttrace == NULL)
-               return -1;
+               goto out_put;
 
        args = perf_evsel__sc_tp_ptr(evsel, args, sample);
 
        if (ttrace->entry_str == NULL) {
                ttrace->entry_str = malloc(1024);
                if (!ttrace->entry_str)
-                       return -1;
+                       goto out_put;
        }
 
        if (!trace->summary_only)
@@ -1757,8 +1843,10 @@ static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel,
                thread__put(trace->current);
                trace->current = thread__get(thread);
        }
-
-       return 0;
+       err = 0;
+out_put:
+       thread__put(thread);
+       return err;
 }
 
 static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel,
@@ -1768,7 +1856,7 @@ static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel,
        long ret;
        u64 duration = 0;
        struct thread *thread;
-       int id = perf_evsel__sc_tp_uint(evsel, id, sample);
+       int id = perf_evsel__sc_tp_uint(evsel, id, sample), err = -1;
        struct syscall *sc = trace__syscall_info(trace, evsel, id);
        struct thread_trace *ttrace;
 
@@ -1781,7 +1869,7 @@ static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel,
        thread = machine__findnew_thread(trace->host, sample->pid, sample->tid);
        ttrace = thread__trace(thread, trace->output);
        if (ttrace == NULL)
-               return -1;
+               goto out_put;
 
        if (trace->summary)
                thread__update_stats(ttrace, id, sample);
@@ -1835,8 +1923,10 @@ signed_print:
        fputc('\n', trace->output);
 out:
        ttrace->entry_pending = false;
-
-       return 0;
+       err = 0;
+out_put:
+       thread__put(thread);
+       return err;
 }
 
 static int trace__vfs_getname(struct trace *trace, struct perf_evsel *evsel,
@@ -1863,6 +1953,7 @@ static int trace__sched_stat_runtime(struct trace *trace, struct perf_evsel *evs
 
        ttrace->runtime_ms += runtime_ms;
        trace->runtime_ms += runtime_ms;
+       thread__put(thread);
        return 0;
 
 out_dump:
@@ -1872,6 +1963,7 @@ out_dump:
               (pid_t)perf_evsel__intval(evsel, sample, "pid"),
               runtime,
               perf_evsel__intval(evsel, sample, "vruntime"));
+       thread__put(thread);
        return 0;
 }
 
@@ -1924,11 +2016,12 @@ static int trace__pgfault(struct trace *trace,
        struct addr_location al;
        char map_type = 'd';
        struct thread_trace *ttrace;
+       int err = -1;
 
        thread = machine__findnew_thread(trace->host, sample->pid, sample->tid);
        ttrace = thread__trace(thread, trace->output);
        if (ttrace == NULL)
-               return -1;
+               goto out_put;
 
        if (evsel->attr.config == PERF_COUNT_SW_PAGE_FAULTS_MAJ)
                ttrace->pfmaj++;
@@ -1936,7 +2029,7 @@ static int trace__pgfault(struct trace *trace,
                ttrace->pfmin++;
 
        if (trace->summary_only)
-               return 0;
+               goto out;
 
        thread__find_addr_location(thread, cpumode, MAP__FUNCTION,
                              sample->ip, &al);
@@ -1967,8 +2060,11 @@ static int trace__pgfault(struct trace *trace,
        print_location(trace->output, sample, &al, true, false);
 
        fprintf(trace->output, " (%c%c)\n", map_type, al.level);
-
-       return 0;
+out:
+       err = 0;
+out_put:
+       thread__put(thread);
+       return err;
 }
 
 static bool skip_sample(struct trace *trace, struct perf_sample *sample)
@@ -2652,6 +2748,7 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused)
                        .user_interval = ULLONG_MAX,
                        .no_buffering  = true,
                        .mmap_pages    = UINT_MAX,
+                       .proc_map_timeout  = 500,
                },
                .output = stdout,
                .show_comm = true,
@@ -2666,16 +2763,15 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused)
        OPT_BOOLEAN(0, "comm", &trace.show_comm,
                    "show the thread COMM next to its id"),
        OPT_BOOLEAN(0, "tool_stats", &trace.show_tool_stats, "show tool stats"),
-       OPT_STRING('e', "expr", &ev_qualifier_str, "expr",
-                   "list of events to trace"),
+       OPT_STRING('e', "expr", &ev_qualifier_str, "expr", "list of syscalls to trace"),
        OPT_STRING('o', "output", &output_name, "file", "output file name"),
        OPT_STRING('i', "input", &input_name, "file", "Analyze events in file"),
        OPT_STRING('p', "pid", &trace.opts.target.pid, "pid",
                    "trace events on existing process id"),
        OPT_STRING('t', "tid", &trace.opts.target.tid, "tid",
                    "trace events on existing thread id"),
-       OPT_CALLBACK(0, "filter-pids", &trace, "float",
-                    "show only events with duration > N.M ms", trace__set_filter_pids),
+       OPT_CALLBACK(0, "filter-pids", &trace, "CSV list of pids",
+                    "pids to filter (by the kernel)", trace__set_filter_pids),
        OPT_BOOLEAN('a', "all-cpus", &trace.opts.target.system_wide,
                    "system-wide collection from all CPUs"),
        OPT_STRING('C', "cpu", &trace.opts.target.cpu_list, "cpu",
@@ -2702,6 +2798,8 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused)
                     "Trace pagefaults", parse_pagefaults, "maj"),
        OPT_BOOLEAN(0, "syscalls", &trace.trace_syscalls, "Trace syscalls"),
        OPT_BOOLEAN('f', "force", &trace.force, "don't complain, do it"),
+       OPT_UINTEGER(0, "proc-map-timeout", &trace.opts.proc_map_timeout,
+                       "per thread proc mmap processing timeout in ms"),
        OPT_END()
        };
        const char * const trace_subcommands[] = { "record", NULL };
@@ -2712,11 +2810,10 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused)
        signal(SIGFPE, sighandler_dump_stack);
 
        trace.evlist = perf_evlist__new();
-       if (trace.evlist == NULL)
-               return -ENOMEM;
 
        if (trace.evlist == NULL) {
                pr_err("Not enough memory to run!\n");
+               err = -ENOMEM;
                goto out;
        }
 
index 59a98c6432403874a564753dd6e790b8632b672e..317001c946608be1430d03d485fe19586f1e0446 100644 (file)
@@ -32,7 +32,7 @@ ifeq ($(ARCH),x86)
     LIBUNWIND_LIBS = -lunwind -lunwind-x86_64
     $(call detected,CONFIG_X86_64)
   else
-    LIBUNWIND_LIBS = -lunwind -lunwind-x86
+    LIBUNWIND_LIBS = -lunwind-x86 -llzma -lunwind
   endif
   NO_PERF_REGS := 0
 endif
@@ -130,6 +130,8 @@ endif
 
 ifeq ($(DEBUG),0)
   CFLAGS += -O6
+else
+  CFLAGS += $(call cc-option,-Og,-O0)
 endif
 
 ifdef PARSER_DEBUG
@@ -268,6 +270,10 @@ else
   endif # libelf support
 endif # NO_LIBELF
 
+ifdef NO_DWARF
+  NO_LIBDW_DWARF_UNWIND := 1
+endif
+
 ifndef NO_LIBELF
   CFLAGS += -DHAVE_LIBELF_SUPPORT
   EXTLIBS += -lelf
@@ -610,6 +616,11 @@ ifdef LIBBABELTRACE
   endif
 endif
 
+ifndef NO_AUXTRACE
+  $(call detected,CONFIG_AUXTRACE)
+  CFLAGS += -DHAVE_AUXTRACE_SUPPORT
+endif
+
 # Among the variables below, these:
 #   perfexecdir
 #   template_dir
index c16ce833079c0a307642f2ae0e75f9c0d577c4d8..0ebef09c0842f89e16df8404931f884d7796c1ca 100644 (file)
@@ -177,3 +177,22 @@ $(if $($(1)),$(call _ge_attempt,$($(1)),$(1)),$(call _ge_attempt,$(2)))
 endef
 _ge_attempt = $(if $(get-executable),$(get-executable),$(call _gea_err,$(2)))
 _gea_err  = $(if $(1),$(error Please set '$(1)' appropriately))
+
+# try-run
+# Usage: option = $(call try-run, $(CC)...-o "$$TMP",option-ok,otherwise)
+# Exit code chooses option. "$$TMP" is can be used as temporary file and
+# is automatically cleaned up.
+try-run = $(shell set -e;              \
+       TMP="$(TMPOUT).$$$$.tmp";       \
+       TMPO="$(TMPOUT).$$$$.o";        \
+       if ($(1)) >/dev/null 2>&1;      \
+       then echo "$(2)";               \
+       else echo "$(3)";               \
+       fi;                             \
+       rm -f "$$TMP" "$$TMPO")
+
+# cc-option
+# Usage: cflags-y += $(call cc-option,-march=winchip-c6,-march=i586)
+
+cc-option = $(call try-run,\
+       $(CC) $(KBUILD_CPPFLAGS) $(KBUILD_CFLAGS) $(1) -c -x c /dev/null -o "$$TMP",$(1),$(2))
index 6ef68165c9db628d23bbe85b48945ac2581ec979..83a25cef82fdd2747ab0bc7f8b4fdc50c3c65b62 100644 (file)
@@ -6,11 +6,9 @@
 #include <sys/syscall.h>
 #include <linux/types.h>
 #include <linux/perf_event.h>
+#include <asm/barrier.h>
 
 #if defined(__i386__)
-#define mb()           asm volatile("lock; addl $0,0(%%esp)" ::: "memory")
-#define wmb()          asm volatile("lock; addl $0,0(%%esp)" ::: "memory")
-#define rmb()          asm volatile("lock; addl $0,0(%%esp)" ::: "memory")
 #define cpu_relax()    asm volatile("rep; nop" ::: "memory");
 #define CPUINFO_PROC   {"model name"}
 #ifndef __NR_perf_event_open
@@ -25,9 +23,6 @@
 #endif
 
 #if defined(__x86_64__)
-#define mb()           asm volatile("mfence" ::: "memory")
-#define wmb()          asm volatile("sfence" ::: "memory")
-#define rmb()          asm volatile("lfence" ::: "memory")
 #define cpu_relax()    asm volatile("rep; nop" ::: "memory");
 #define CPUINFO_PROC   {"model name"}
 #ifndef __NR_perf_event_open
 
 #ifdef __powerpc__
 #include "../../arch/powerpc/include/uapi/asm/unistd.h"
-#define mb()           asm volatile ("sync" ::: "memory")
-#define wmb()          asm volatile ("sync" ::: "memory")
-#define rmb()          asm volatile ("sync" ::: "memory")
 #define CPUINFO_PROC   {"cpu"}
 #endif
 
 #ifdef __s390__
-#define mb()           asm volatile("bcr 15,0" ::: "memory")
-#define wmb()          asm volatile("bcr 15,0" ::: "memory")
-#define rmb()          asm volatile("bcr 15,0" ::: "memory")
 #define CPUINFO_PROC   {"vendor_id"}
 #endif
 
 #ifdef __sh__
-#if defined(__SH4A__) || defined(__SH5__)
-# define mb()          asm volatile("synco" ::: "memory")
-# define wmb()         asm volatile("synco" ::: "memory")
-# define rmb()         asm volatile("synco" ::: "memory")
-#else
-# define mb()          asm volatile("" ::: "memory")
-# define wmb()         asm volatile("" ::: "memory")
-# define rmb()         asm volatile("" ::: "memory")
-#endif
 #define CPUINFO_PROC   {"cpu type"}
 #endif
 
 #ifdef __hppa__
-#define mb()           asm volatile("" ::: "memory")
-#define wmb()          asm volatile("" ::: "memory")
-#define rmb()          asm volatile("" ::: "memory")
 #define CPUINFO_PROC   {"cpu"}
 #endif
 
 #ifdef __sparc__
-#ifdef __LP64__
-#define mb()           asm volatile("ba,pt %%xcc, 1f\n"        \
-                                    "membar #StoreLoad\n"      \
-                                    "1:\n":::"memory")
-#else
-#define mb()           asm volatile("":::"memory")
-#endif
-#define wmb()          asm volatile("":::"memory")
-#define rmb()          asm volatile("":::"memory")
 #define CPUINFO_PROC   {"cpu"}
 #endif
 
 #ifdef __alpha__
-#define mb()           asm volatile("mb" ::: "memory")
-#define wmb()          asm volatile("wmb" ::: "memory")
-#define rmb()          asm volatile("mb" ::: "memory")
 #define CPUINFO_PROC   {"cpu model"}
 #endif
 
 #ifdef __ia64__
-#define mb()           asm volatile ("mf" ::: "memory")
-#define wmb()          asm volatile ("mf" ::: "memory")
-#define rmb()          asm volatile ("mf" ::: "memory")
 #define cpu_relax()    asm volatile ("hint @pause" ::: "memory")
 #define CPUINFO_PROC   {"model name"}
 #endif
 
 #ifdef __arm__
-/*
- * Use the __kuser_memory_barrier helper in the CPU helper page. See
- * arch/arm/kernel/entry-armv.S in the kernel source for details.
- */
-#define mb()           ((void(*)(void))0xffff0fa0)()
-#define wmb()          ((void(*)(void))0xffff0fa0)()
-#define rmb()          ((void(*)(void))0xffff0fa0)()
 #define CPUINFO_PROC   {"model name", "Processor"}
 #endif
 
 #ifdef __aarch64__
-#define mb()           asm volatile("dmb ish" ::: "memory")
-#define wmb()          asm volatile("dmb ishst" ::: "memory")
-#define rmb()          asm volatile("dmb ishld" ::: "memory")
 #define cpu_relax()    asm volatile("yield" ::: "memory")
 #endif
 
 #ifdef __mips__
-#define mb()           asm volatile(                                   \
-                               ".set   mips2\n\t"                      \
-                               "sync\n\t"                              \
-                               ".set   mips0"                          \
-                               : /* no output */                       \
-                               : /* no input */                        \
-                               : "memory")
-#define wmb()  mb()
-#define rmb()  mb()
 #define CPUINFO_PROC   {"cpu model"}
 #endif
 
 #ifdef __arc__
-#define mb()           asm volatile("" ::: "memory")
-#define wmb()          asm volatile("" ::: "memory")
-#define rmb()          asm volatile("" ::: "memory")
 #define CPUINFO_PROC   {"Processor"}
 #endif
 
 #ifdef __metag__
-#define mb()           asm volatile("" ::: "memory")
-#define wmb()          asm volatile("" ::: "memory")
-#define rmb()          asm volatile("" ::: "memory")
 #define CPUINFO_PROC   {"CPU"}
 #endif
 
 #ifdef __xtensa__
-#define mb()           asm volatile("memw" ::: "memory")
-#define wmb()          asm volatile("memw" ::: "memory")
-#define rmb()          asm volatile("" ::: "memory")
 #define CPUINFO_PROC   {"core ID"}
 #endif
 
 #ifdef __tile__
-#define mb()           asm volatile ("mf" ::: "memory")
-#define wmb()          asm volatile ("mf" ::: "memory")
-#define rmb()          asm volatile ("mf" ::: "memory")
 #define cpu_relax()    asm volatile ("mfspr zero, PASS" ::: "memory")
 #define CPUINFO_PROC    {"model name"}
 #endif
 
-#define barrier() asm volatile ("" ::: "memory")
-
 #ifndef cpu_relax
 #define cpu_relax() barrier()
 #endif
index e14bb637255cc351ac40850a148eba1461d55cd0..4a5827fff7993d2bbe87666af51aa939f6f9dd1d 100644 (file)
@@ -54,16 +54,22 @@ struct record_opts {
        bool         period;
        bool         sample_intr_regs;
        bool         running_time;
+       bool         full_auxtrace;
+       bool         auxtrace_snapshot_mode;
        unsigned int freq;
        unsigned int mmap_pages;
+       unsigned int auxtrace_mmap_pages;
        unsigned int user_freq;
        u64          branch_stack;
        u64          default_interval;
        u64          user_interval;
+       size_t       auxtrace_snapshot_size;
+       const char   *auxtrace_snapshot_opts;
        bool         sample_transaction;
        unsigned     initial_delay;
        bool         use_clockid;
        clockid_t    clockid;
+       unsigned int proc_map_timeout;
 };
 
 struct option;
index 6a8801b32017018a09233390e7e5856afa4ae857..ee41e705b2eba7b726e417a1fdd73a421b7a985b 100644 (file)
@@ -3,9 +3,9 @@ perf-y += parse-events.o
 perf-y += dso-data.o
 perf-y += attr.o
 perf-y += vmlinux-kallsyms.o
-perf-y += open-syscall.o
-perf-y += open-syscall-all-cpus.o
-perf-y += open-syscall-tp-fields.o
+perf-y += openat-syscall.o
+perf-y += openat-syscall-all-cpus.o
+perf-y += openat-syscall-tp-fields.o
 perf-y += mmap-basic.o
 perf-y += perf-record.o
 perf-y += rdpmc.o
@@ -34,7 +34,7 @@ perf-y += kmod-path.o
 
 perf-$(CONFIG_X86) += perf-time-to-tsc.o
 
-ifeq ($(ARCH),$(filter $(ARCH),x86 arm))
+ifeq ($(ARCH),$(filter $(ARCH),x86 arm arm64))
 perf-$(CONFIG_DWARF_UNWIND) += dwarf-unwind.o
 endif
 
index 4f409816711249a540c77eb4136ec7d24e94991c..87b9961646e4a5f08728e694549195d9eeb2ad86 100644 (file)
@@ -23,12 +23,12 @@ static struct test {
                .func = test__vmlinux_matches_kallsyms,
        },
        {
-               .desc = "detect open syscall event",
-               .func = test__open_syscall_event,
+               .desc = "detect openat syscall event",
+               .func = test__openat_syscall_event,
        },
        {
-               .desc = "detect open syscall event on all cpus",
-               .func = test__open_syscall_event_on_all_cpus,
+               .desc = "detect openat syscall event on all cpus",
+               .func = test__openat_syscall_event_on_all_cpus,
        },
        {
                .desc = "read samples using the mmap interface",
@@ -73,8 +73,8 @@ static struct test {
                .func = test__perf_evsel__tp_sched_test,
        },
        {
-               .desc = "Generate and check syscalls:sys_enter_open event fields",
-               .func = test__syscall_open_tp_fields,
+               .desc = "Generate and check syscalls:sys_enter_openat event fields",
+               .func = test__syscall_openat_tp_fields,
        },
        {
                .desc = "struct perf_event_attr setup",
@@ -126,7 +126,7 @@ static struct test {
                .desc = "Test parsing with no sample_id_all bit set",
                .func = test__parse_no_sample_id_all,
        },
-#if defined(__x86_64__) || defined(__i386__) || defined(__arm__)
+#if defined(__x86_64__) || defined(__i386__) || defined(__arm__) || defined(__aarch64__)
 #ifdef HAVE_DWARF_UNWIND_SUPPORT
        {
                .desc = "Test dwarf unwind",
@@ -219,7 +219,7 @@ static int run_test(struct test *test)
        wait(&status);
 
        if (WIFEXITED(status)) {
-               err = WEXITSTATUS(status);
+               err = (signed char)WEXITSTATUS(status);
                pr_debug("test child finished with %d\n", err);
        } else if (WIFSIGNALED(status)) {
                err = -1;
index f671ec37a7c40c1346ebe92e77a6d201bc89305e..22f8a00446e1f1b3cb6b447dbc1bc21ccfda3108 100644 (file)
@@ -248,6 +248,7 @@ static int process_sample_event(struct machine *machine,
        struct perf_sample sample;
        struct thread *thread;
        u8 cpumode;
+       int ret;
 
        if (perf_evlist__parse_sample(evlist, event, &sample)) {
                pr_debug("perf_evlist__parse_sample failed\n");
@@ -262,7 +263,9 @@ static int process_sample_event(struct machine *machine,
 
        cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
 
-       return read_object_code(sample.ip, READLEN, cpumode, thread, state);
+       ret = read_object_code(sample.ip, READLEN, cpumode, thread, state);
+       thread__put(thread);
+       return ret;
 }
 
 static int process_event(struct machine *machine, struct perf_evlist *evlist,
@@ -448,7 +451,7 @@ static int do_test_code_reading(bool try_kcore)
        }
 
        ret = perf_event__synthesize_thread_map(NULL, threads,
-                                               perf_event__process, machine, false);
+                                               perf_event__process, machine, false, 500);
        if (ret < 0) {
                pr_debug("perf_event__synthesize_thread_map failed\n");
                goto out_err;
@@ -457,13 +460,13 @@ static int do_test_code_reading(bool try_kcore)
        thread = machine__findnew_thread(machine, pid, pid);
        if (!thread) {
                pr_debug("machine__findnew_thread failed\n");
-               goto out_err;
+               goto out_put;
        }
 
        cpus = cpu_map__new(NULL);
        if (!cpus) {
                pr_debug("cpu_map__new failed\n");
-               goto out_err;
+               goto out_put;
        }
 
        while (1) {
@@ -472,7 +475,7 @@ static int do_test_code_reading(bool try_kcore)
                evlist = perf_evlist__new();
                if (!evlist) {
                        pr_debug("perf_evlist__new failed\n");
-                       goto out_err;
+                       goto out_put;
                }
 
                perf_evlist__set_maps(evlist, cpus, threads);
@@ -482,10 +485,10 @@ static int do_test_code_reading(bool try_kcore)
                else
                        str = "cycles";
                pr_debug("Parsing event '%s'\n", str);
-               ret = parse_events(evlist, str);
+               ret = parse_events(evlist, str, NULL);
                if (ret < 0) {
                        pr_debug("parse_events failed\n");
-                       goto out_err;
+                       goto out_put;
                }
 
                perf_evlist__config(evlist, &opts);
@@ -506,7 +509,7 @@ static int do_test_code_reading(bool try_kcore)
                                continue;
                        }
                        pr_debug("perf_evlist__open failed\n");
-                       goto out_err;
+                       goto out_put;
                }
                break;
        }
@@ -514,7 +517,7 @@ static int do_test_code_reading(bool try_kcore)
        ret = perf_evlist__mmap(evlist, UINT_MAX, false);
        if (ret < 0) {
                pr_debug("perf_evlist__mmap failed\n");
-               goto out_err;
+               goto out_put;
        }
 
        perf_evlist__enable(evlist);
@@ -525,7 +528,7 @@ static int do_test_code_reading(bool try_kcore)
 
        ret = process_events(machine, evlist, &state);
        if (ret < 0)
-               goto out_err;
+               goto out_put;
 
        if (!have_vmlinux && !have_kcore && !try_kcore)
                err = TEST_CODE_READING_NO_KERNEL_OBJ;
@@ -535,7 +538,10 @@ static int do_test_code_reading(bool try_kcore)
                err = TEST_CODE_READING_NO_ACCESS;
        else
                err = TEST_CODE_READING_OK;
+out_put:
+       thread__put(thread);
 out_err:
+
        if (evlist) {
                perf_evlist__delete(evlist);
        } else {
index 513e5febbe5a5016ed5d9a2564bea1e5bc92e4e8..a218aeaf56a002396bf0d0db9ef0e457a7445c9f 100644 (file)
@@ -99,6 +99,17 @@ struct test_data_offset offsets[] = {
        },
 };
 
+/* move it from util/dso.c for compatibility */
+static int dso__data_fd(struct dso *dso, struct machine *machine)
+{
+       int fd = dso__data_get_fd(dso, machine);
+
+       if (fd >= 0)
+               dso__data_put_fd(dso);
+
+       return fd;
+}
+
 int test__dso_data(void)
 {
        struct machine machine;
@@ -155,7 +166,7 @@ int test__dso_data(void)
                free(buf);
        }
 
-       dso__delete(dso);
+       dso__put(dso);
        unlink(file);
        return 0;
 }
@@ -215,7 +226,7 @@ static void dsos__delete(int cnt)
                struct dso *dso = dsos[i];
 
                unlink(dso->name);
-               dso__delete(dso);
+               dso__put(dso);
        }
 
        free(dsos);
index 0bf06bec68c7e9786668990ad399b578326726c5..40b36c4624275a360d4a0f3226eaf850d6d0e825 100644 (file)
@@ -28,7 +28,7 @@ static int init_live_machine(struct machine *machine)
        pid_t pid = getpid();
 
        return perf_event__synthesize_mmap_events(NULL, &event, pid, pid,
-                                                 mmap_handler, machine, true);
+                                                 mmap_handler, machine, true, 500);
 }
 
 #define MAX_STACK 8
@@ -170,6 +170,7 @@ int test__dwarf_unwind(void)
        }
 
        err = krava_1(thread);
+       thread__put(thread);
 
  out:
        machine__delete_threads(machine);
index b8d8341b383e7bc123c29301eff6fbe6e98be332..3fa715987a5ec2693e2bcdb31a33e3f20616c136 100644 (file)
@@ -23,7 +23,7 @@ static int perf_evsel__roundtrip_cache_name_test(void)
                        for (i = 0; i < PERF_COUNT_HW_CACHE_RESULT_MAX; i++) {
                                __perf_evsel__hw_cache_type_op_res_name(type, op, i,
                                                                        name, sizeof(name));
-                               err = parse_events(evlist, name);
+                               err = parse_events(evlist, name, NULL);
                                if (err)
                                        ret = err;
                        }
@@ -71,7 +71,7 @@ static int __perf_evsel__name_array_test(const char *names[], int nr_names)
                 return -ENOMEM;
 
        for (i = 0; i < nr_names; ++i) {
-               err = parse_events(evlist, names[i]);
+               err = parse_events(evlist, names[i], NULL);
                if (err) {
                        pr_debug("failed to parse event '%s', err %d\n",
                                 names[i], err);
index a62c091345163f70ea72ad2cb9dbc2297ae2da1a..ce80b274b097332d02b5502fb0c5b88fc6d6016a 100644 (file)
@@ -96,6 +96,7 @@ struct machine *setup_fake_machine(struct machines *machines)
                        goto out;
 
                thread__set_comm(thread, fake_threads[i].comm, 0);
+               thread__put(thread);
        }
 
        for (i = 0; i < ARRAY_SIZE(fake_mmap_info); i++) {
@@ -120,8 +121,7 @@ struct machine *setup_fake_machine(struct machines *machines)
                size_t k;
                struct dso *dso;
 
-               dso = __dsos__findnew(&machine->user_dsos,
-                                     fake_symbols[i].dso_name);
+               dso = machine__findnew_dso(machine, fake_symbols[i].dso_name);
                if (dso == NULL)
                        goto out;
 
@@ -134,11 +134,15 @@ struct machine *setup_fake_machine(struct machines *machines)
 
                        sym = symbol__new(fsym->start, fsym->length,
                                          STB_GLOBAL, fsym->name);
-                       if (sym == NULL)
+                       if (sym == NULL) {
+                               dso__put(dso);
                                goto out;
+                       }
 
                        symbols__insert(&dso->symbols[MAP__FUNCTION], sym);
                }
+
+               dso__put(dso);
        }
 
        return machine;
index 18619966454c572a0f3c0a1b2330818fc6e1ffe4..7d82c8be5e360da5f89b1ff7b569a798c5a34fe0 100644 (file)
@@ -87,6 +87,8 @@ static int add_hist_entries(struct hists *hists, struct machine *machine)
                        },
                };
                struct hist_entry_iter iter = {
+                       .evsel = evsel,
+                       .sample = &sample,
                        .hide_unresolved = false,
                };
 
@@ -104,9 +106,11 @@ static int add_hist_entries(struct hists *hists, struct machine *machine)
                                                  &sample) < 0)
                        goto out;
 
-               if (hist_entry_iter__add(&iter, &al, evsel, &sample,
-                                        PERF_MAX_STACK_DEPTH, NULL) < 0)
+               if (hist_entry_iter__add(&iter, &al, PERF_MAX_STACK_DEPTH,
+                                        NULL) < 0) {
+                       addr_location__put(&al);
                        goto out;
+               }
 
                fake_samples[i].thread = al.thread;
                fake_samples[i].map = al.map;
@@ -695,7 +699,7 @@ int test__hists_cumulate(void)
 
        TEST_ASSERT_VAL("No memory", evlist);
 
-       err = parse_events(evlist, "cpu-clock");
+       err = parse_events(evlist, "cpu-clock", NULL);
        if (err)
                goto out;
 
index 59e53db7914c0ad6100ab2e616cdf21e39efea46..ce48775e6ada13886000013183908f3f6b63f26d 100644 (file)
@@ -63,6 +63,8 @@ static int add_hist_entries(struct perf_evlist *evlist,
                                },
                        };
                        struct hist_entry_iter iter = {
+                               .evsel = evsel,
+                               .sample = &sample,
                                .ops = &hist_iter_normal,
                                .hide_unresolved = false,
                        };
@@ -81,9 +83,11 @@ static int add_hist_entries(struct perf_evlist *evlist,
                                                          &sample) < 0)
                                goto out;
 
-                       if (hist_entry_iter__add(&iter, &al, evsel, &sample,
-                                                PERF_MAX_STACK_DEPTH, NULL) < 0)
+                       if (hist_entry_iter__add(&iter, &al,
+                                                PERF_MAX_STACK_DEPTH, NULL) < 0) {
+                               addr_location__put(&al);
                                goto out;
+                       }
 
                        fake_samples[i].thread = al.thread;
                        fake_samples[i].map = al.map;
@@ -108,10 +112,10 @@ int test__hists_filter(void)
 
        TEST_ASSERT_VAL("No memory", evlist);
 
-       err = parse_events(evlist, "cpu-clock");
+       err = parse_events(evlist, "cpu-clock", NULL);
        if (err)
                goto out;
-       err = parse_events(evlist, "task-clock");
+       err = parse_events(evlist, "task-clock", NULL);
        if (err)
                goto out;
 
index 278ba8344c236d000a0388c3ef1c18b60350f554..8c102b0114249708e4ae2059c81732ab23793c4b 100644 (file)
@@ -91,8 +91,10 @@ static int add_hist_entries(struct perf_evlist *evlist, struct machine *machine)
 
                        he = __hists__add_entry(hists, &al, NULL,
                                                NULL, NULL, 1, 1, 0, true);
-                       if (he == NULL)
+                       if (he == NULL) {
+                               addr_location__put(&al);
                                goto out;
+                       }
 
                        fake_common_samples[k].thread = al.thread;
                        fake_common_samples[k].map = al.map;
@@ -115,8 +117,10 @@ static int add_hist_entries(struct perf_evlist *evlist, struct machine *machine)
 
                        he = __hists__add_entry(hists, &al, NULL,
                                                NULL, NULL, 1, 1, 0, true);
-                       if (he == NULL)
+                       if (he == NULL) {
+                               addr_location__put(&al);
                                goto out;
+                       }
 
                        fake_samples[i][k].thread = al.thread;
                        fake_samples[i][k].map = al.map;
@@ -282,10 +286,10 @@ int test__hists_link(void)
        if (evlist == NULL)
                 return -ENOMEM;
 
-       err = parse_events(evlist, "cpu-clock");
+       err = parse_events(evlist, "cpu-clock", NULL);
        if (err)
                goto out;
-       err = parse_events(evlist, "task-clock");
+       err = parse_events(evlist, "task-clock", NULL);
        if (err)
                goto out;
 
index b52c9faea22450ed4092d67acdb1eb15ce15c6a8..adbebc852cc8b58886a2618f973815f9284b7b3b 100644 (file)
@@ -57,6 +57,8 @@ static int add_hist_entries(struct hists *hists, struct machine *machine)
                        },
                };
                struct hist_entry_iter iter = {
+                       .evsel = evsel,
+                       .sample = &sample,
                        .ops = &hist_iter_normal,
                        .hide_unresolved = false,
                };
@@ -70,9 +72,11 @@ static int add_hist_entries(struct hists *hists, struct machine *machine)
                                                  &sample) < 0)
                        goto out;
 
-               if (hist_entry_iter__add(&iter, &al, evsel, &sample,
-                                        PERF_MAX_STACK_DEPTH, NULL) < 0)
+               if (hist_entry_iter__add(&iter, &al, PERF_MAX_STACK_DEPTH,
+                                        NULL) < 0) {
+                       addr_location__put(&al);
                        goto out;
+               }
 
                fake_samples[i].thread = al.thread;
                fake_samples[i].map = al.map;
@@ -590,7 +594,7 @@ int test__hists_output(void)
 
        TEST_ASSERT_VAL("No memory", evlist);
 
-       err = parse_events(evlist, "cpu-clock");
+       err = parse_events(evlist, "cpu-clock", NULL);
        if (err)
                goto out;
 
index 7a5ab7b0b8f698146794be7584af453a446f79ee..5b171d1e338bdd26bcf1343f58e8b0bdb314b71c 100644 (file)
@@ -78,8 +78,8 @@ int test__keep_tracking(void)
 
        perf_evlist__set_maps(evlist, cpus, threads);
 
-       CHECK__(parse_events(evlist, "dummy:u"));
-       CHECK__(parse_events(evlist, "cycles:u"));
+       CHECK__(parse_events(evlist, "dummy:u", NULL));
+       CHECK__(parse_events(evlist, "cycles:u", NULL));
 
        perf_evlist__config(evlist, &opts);
 
index e8d7cbb9320c58c987de7743bf4c08bcd8314361..08c433b4bf4f30c8f69307ba3fd5b0ec21802e2f 100644 (file)
@@ -34,9 +34,21 @@ static int test(const char *path, bool alloc_name, bool alloc_ext,
        return 0;
 }
 
+static int test_is_kernel_module(const char *path, int cpumode, bool expect)
+{
+       TEST_ASSERT_VAL("is_kernel_module",
+                       (!!is_kernel_module(path, cpumode)) == (!!expect));
+       pr_debug("%s (cpumode: %d) - is_kernel_module: %s\n",
+                       path, cpumode, expect ? "true" : "false");
+       return 0;
+}
+
 #define T(path, an, ae, k, c, n, e) \
        TEST_ASSERT_VAL("failed", !test(path, an, ae, k, c, n, e))
 
+#define M(path, c, e) \
+       TEST_ASSERT_VAL("failed", !test_is_kernel_module(path, c, e))
+
 int test__kmod_path__parse(void)
 {
        /* path                alloc_name  alloc_ext   kmod  comp   name     ext */
@@ -44,30 +56,90 @@ int test__kmod_path__parse(void)
        T("/xxxx/xxxx/x-x.ko", false     , true      , true, false, NULL   , NULL);
        T("/xxxx/xxxx/x-x.ko", true      , false     , true, false, "[x_x]", NULL);
        T("/xxxx/xxxx/x-x.ko", false     , false     , true, false, NULL   , NULL);
+       M("/xxxx/xxxx/x-x.ko", PERF_RECORD_MISC_CPUMODE_UNKNOWN, true);
+       M("/xxxx/xxxx/x-x.ko", PERF_RECORD_MISC_KERNEL, true);
+       M("/xxxx/xxxx/x-x.ko", PERF_RECORD_MISC_USER, false);
 
        /* path                alloc_name  alloc_ext   kmod  comp  name   ext */
        T("/xxxx/xxxx/x.ko.gz", true     , true      , true, true, "[x]", "gz");
        T("/xxxx/xxxx/x.ko.gz", false    , true      , true, true, NULL , "gz");
        T("/xxxx/xxxx/x.ko.gz", true     , false     , true, true, "[x]", NULL);
        T("/xxxx/xxxx/x.ko.gz", false    , false     , true, true, NULL , NULL);
+       M("/xxxx/xxxx/x.ko.gz", PERF_RECORD_MISC_CPUMODE_UNKNOWN, true);
+       M("/xxxx/xxxx/x.ko.gz", PERF_RECORD_MISC_KERNEL, true);
+       M("/xxxx/xxxx/x.ko.gz", PERF_RECORD_MISC_USER, false);
 
        /* path              alloc_name  alloc_ext  kmod   comp  name    ext */
        T("/xxxx/xxxx/x.gz", true      , true     , false, true, "x.gz" ,"gz");
        T("/xxxx/xxxx/x.gz", false     , true     , false, true, NULL   ,"gz");
        T("/xxxx/xxxx/x.gz", true      , false    , false, true, "x.gz" , NULL);
        T("/xxxx/xxxx/x.gz", false     , false    , false, true, NULL   , NULL);
+       M("/xxxx/xxxx/x.gz", PERF_RECORD_MISC_CPUMODE_UNKNOWN, false);
+       M("/xxxx/xxxx/x.gz", PERF_RECORD_MISC_KERNEL, false);
+       M("/xxxx/xxxx/x.gz", PERF_RECORD_MISC_USER, false);
 
        /* path   alloc_name  alloc_ext  kmod   comp  name     ext */
        T("x.gz", true      , true     , false, true, "x.gz", "gz");
        T("x.gz", false     , true     , false, true, NULL  , "gz");
        T("x.gz", true      , false    , false, true, "x.gz", NULL);
        T("x.gz", false     , false    , false, true, NULL  , NULL);
+       M("x.gz", PERF_RECORD_MISC_CPUMODE_UNKNOWN, false);
+       M("x.gz", PERF_RECORD_MISC_KERNEL, false);
+       M("x.gz", PERF_RECORD_MISC_USER, false);
 
        /* path      alloc_name  alloc_ext  kmod  comp  name  ext */
        T("x.ko.gz", true      , true     , true, true, "[x]", "gz");
        T("x.ko.gz", false     , true     , true, true, NULL , "gz");
        T("x.ko.gz", true      , false    , true, true, "[x]", NULL);
        T("x.ko.gz", false     , false    , true, true, NULL , NULL);
+       M("x.ko.gz", PERF_RECORD_MISC_CPUMODE_UNKNOWN, true);
+       M("x.ko.gz", PERF_RECORD_MISC_KERNEL, true);
+       M("x.ko.gz", PERF_RECORD_MISC_USER, false);
+
+       /* path            alloc_name  alloc_ext  kmod  comp   name             ext */
+       T("[test_module]", true      , true     , true, false, "[test_module]", NULL);
+       T("[test_module]", false     , true     , true, false, NULL           , NULL);
+       T("[test_module]", true      , false    , true, false, "[test_module]", NULL);
+       T("[test_module]", false     , false    , true, false, NULL           , NULL);
+       M("[test_module]", PERF_RECORD_MISC_CPUMODE_UNKNOWN, true);
+       M("[test_module]", PERF_RECORD_MISC_KERNEL, true);
+       M("[test_module]", PERF_RECORD_MISC_USER, false);
+
+       /* path            alloc_name  alloc_ext  kmod  comp   name             ext */
+       T("[test.module]", true      , true     , true, false, "[test.module]", NULL);
+       T("[test.module]", false     , true     , true, false, NULL           , NULL);
+       T("[test.module]", true      , false    , true, false, "[test.module]", NULL);
+       T("[test.module]", false     , false    , true, false, NULL           , NULL);
+       M("[test.module]", PERF_RECORD_MISC_CPUMODE_UNKNOWN, true);
+       M("[test.module]", PERF_RECORD_MISC_KERNEL, true);
+       M("[test.module]", PERF_RECORD_MISC_USER, false);
+
+       /* path     alloc_name  alloc_ext  kmod   comp   name      ext */
+       T("[vdso]", true      , true     , false, false, "[vdso]", NULL);
+       T("[vdso]", false     , true     , false, false, NULL    , NULL);
+       T("[vdso]", true      , false    , false, false, "[vdso]", NULL);
+       T("[vdso]", false     , false    , false, false, NULL    , NULL);
+       M("[vdso]", PERF_RECORD_MISC_CPUMODE_UNKNOWN, false);
+       M("[vdso]", PERF_RECORD_MISC_KERNEL, false);
+       M("[vdso]", PERF_RECORD_MISC_USER, false);
+
+       /* path         alloc_name  alloc_ext  kmod   comp   name          ext */
+       T("[vsyscall]", true      , true     , false, false, "[vsyscall]", NULL);
+       T("[vsyscall]", false     , true     , false, false, NULL        , NULL);
+       T("[vsyscall]", true      , false    , false, false, "[vsyscall]", NULL);
+       T("[vsyscall]", false     , false    , false, false, NULL        , NULL);
+       M("[vsyscall]", PERF_RECORD_MISC_CPUMODE_UNKNOWN, false);
+       M("[vsyscall]", PERF_RECORD_MISC_KERNEL, false);
+       M("[vsyscall]", PERF_RECORD_MISC_USER, false);
+
+       /* path                alloc_name  alloc_ext  kmod   comp   name      ext */
+       T("[kernel.kallsyms]", true      , true     , false, false, "[kernel.kallsyms]", NULL);
+       T("[kernel.kallsyms]", false     , true     , false, false, NULL               , NULL);
+       T("[kernel.kallsyms]", true      , false    , false, false, "[kernel.kallsyms]", NULL);
+       T("[kernel.kallsyms]", false     , false    , false, false, NULL               , NULL);
+       M("[kernel.kallsyms]", PERF_RECORD_MISC_CPUMODE_UNKNOWN, false);
+       M("[kernel.kallsyms]", PERF_RECORD_MISC_KERNEL, false);
+       M("[kernel.kallsyms]", PERF_RECORD_MISC_USER, false);
 
        return 0;
 }
index bff85324f799bd1eeba79413ebf1433faac0a2e1..65280d28662e4c72177a20a3cfa56f968a10c359 100644 (file)
@@ -32,6 +32,7 @@ make_no_backtrace   := NO_BACKTRACE=1
 make_no_libnuma     := NO_LIBNUMA=1
 make_no_libaudit    := NO_LIBAUDIT=1
 make_no_libbionic   := NO_LIBBIONIC=1
+make_no_auxtrace    := NO_AUXTRACE=1
 make_tags           := tags
 make_cscope         := cscope
 make_help           := help
@@ -52,7 +53,7 @@ make_static         := LDFLAGS=-static
 make_minimal        := NO_LIBPERL=1 NO_LIBPYTHON=1 NO_NEWT=1 NO_GTK2=1
 make_minimal        += NO_DEMANGLE=1 NO_LIBELF=1 NO_LIBUNWIND=1 NO_BACKTRACE=1
 make_minimal        += NO_LIBNUMA=1 NO_LIBAUDIT=1 NO_LIBBIONIC=1
-make_minimal        += NO_LIBDW_DWARF_UNWIND=1
+make_minimal        += NO_LIBDW_DWARF_UNWIND=1 NO_AUXTRACE=1
 
 # $(run) contains all available tests
 run := make_pure
@@ -74,6 +75,7 @@ run += make_no_backtrace
 run += make_no_libnuma
 run += make_no_libaudit
 run += make_no_libbionic
+run += make_no_auxtrace
 run += make_help
 run += make_doc
 run += make_perf_o
@@ -223,7 +225,19 @@ tarpkg:
        echo "- $@: $$cmd" && echo $$cmd > $@ && \
        ( eval $$cmd ) >> $@ 2>&1
 
-all: $(run) $(run_O) tarpkg
+make_kernelsrc:
+       @echo " - make -C <kernelsrc> tools/perf"
+       $(call clean); \
+       (make -C ../.. tools/perf) > $@ 2>&1 && \
+       test -x perf && rm -f $@ || (cat $@ ; false)
+
+make_kernelsrc_tools:
+       @echo " - make -C <kernelsrc>/tools perf"
+       $(call clean); \
+       (make -C ../../tools perf) > $@ 2>&1 && \
+       test -x perf && rm -f $@ || (cat $@ ; false)
+
+all: $(run) $(run_O) tarpkg make_kernelsrc make_kernelsrc_tools
        @echo OK
 
 out: $(run_O)
index 9b9622a33932dadf2e98bd249850c62f16ac66e5..5855cf47121003479ae63e859059a5ad8809c5ec 100644 (file)
@@ -23,10 +23,8 @@ int test__basic_mmap(void)
        struct cpu_map *cpus;
        struct perf_evlist *evlist;
        cpu_set_t cpu_set;
-       const char *syscall_names[] = { "getsid", "getppid", "getpgrp",
-                                       "getpgid", };
-       pid_t (*syscalls[])(void) = { (void *)getsid, getppid, getpgrp,
-                                     (void*)getpgid };
+       const char *syscall_names[] = { "getsid", "getppid", "getpgid", };
+       pid_t (*syscalls[])(void) = { (void *)getsid, getppid, (void*)getpgid };
 #define nsyscalls ARRAY_SIZE(syscall_names)
        unsigned int nr_events[nsyscalls],
                     expected_nr_events[nsyscalls], i, j;
index 2113f1c8611fb569b0fb6bc7f676477e7de0a666..7f48efa7e295f63a72f0aa083857658ce68c45cb 100644 (file)
@@ -129,7 +129,7 @@ static int synth_all(struct machine *machine)
 {
        return perf_event__synthesize_threads(NULL,
                                              perf_event__process,
-                                             machine, 0);
+                                             machine, 0, 500);
 }
 
 static int synth_process(struct machine *machine)
@@ -141,7 +141,7 @@ static int synth_process(struct machine *machine)
 
        err = perf_event__synthesize_thread_map(NULL, map,
                                                perf_event__process,
-                                               machine, 0);
+                                               machine, 0, 500);
 
        thread_map__delete(map);
        return err;
@@ -191,6 +191,8 @@ static int mmap_events(synth_cb synth)
                                      PERF_RECORD_MISC_USER, MAP__FUNCTION,
                                      (unsigned long) (td->map + 1), &al);
 
+               thread__put(thread);
+
                if (!al.map) {
                        pr_debug("failed, couldn't find map\n");
                        err = -1;
similarity index 90%
rename from tools/perf/tests/open-syscall-all-cpus.c
rename to tools/perf/tests/openat-syscall-all-cpus.c
index 3ec885c48f8fbcc77bdeaf0891fa54a2aa3935bc..9a7a116e09b8087ef5f19e63113f6f65c1e0b5bf 100644 (file)
@@ -3,13 +3,14 @@
 #include "thread_map.h"
 #include "cpumap.h"
 #include "debug.h"
+#include "stat.h"
 
-int test__open_syscall_event_on_all_cpus(void)
+int test__openat_syscall_event_on_all_cpus(void)
 {
        int err = -1, fd, cpu;
        struct cpu_map *cpus;
        struct perf_evsel *evsel;
-       unsigned int nr_open_calls = 111, i;
+       unsigned int nr_openat_calls = 111, i;
        cpu_set_t cpu_set;
        struct thread_map *threads = thread_map__new(-1, getpid(), UINT_MAX);
        char sbuf[STRERR_BUFSIZE];
@@ -27,7 +28,7 @@ int test__open_syscall_event_on_all_cpus(void)
 
        CPU_ZERO(&cpu_set);
 
-       evsel = perf_evsel__newtp("syscalls", "sys_enter_open");
+       evsel = perf_evsel__newtp("syscalls", "sys_enter_openat");
        if (evsel == NULL) {
                if (tracefs_configured())
                        pr_debug("is tracefs mounted on /sys/kernel/tracing?\n");
@@ -46,7 +47,7 @@ int test__open_syscall_event_on_all_cpus(void)
        }
 
        for (cpu = 0; cpu < cpus->nr; ++cpu) {
-               unsigned int ncalls = nr_open_calls + cpu;
+               unsigned int ncalls = nr_openat_calls + cpu;
                /*
                 * XXX eventually lift this restriction in a way that
                 * keeps perf building on older glibc installations
@@ -66,7 +67,7 @@ int test__open_syscall_event_on_all_cpus(void)
                        goto out_close_fd;
                }
                for (i = 0; i < ncalls; ++i) {
-                       fd = open("/etc/passwd", O_RDONLY);
+                       fd = openat(0, "/etc/passwd", O_RDONLY);
                        close(fd);
                }
                CPU_CLR(cpus->map[cpu], &cpu_set);
@@ -96,7 +97,7 @@ int test__open_syscall_event_on_all_cpus(void)
                        break;
                }
 
-               expected = nr_open_calls + cpu;
+               expected = nr_openat_calls + cpu;
                if (evsel->counts->cpu[cpu].val != expected) {
                        pr_debug("perf_evsel__read_on_cpu: expected to intercept %d calls on cpu %d, got %" PRIu64 "\n",
                                 expected, cpus->map[cpu], evsel->counts->cpu[cpu].val);
similarity index 94%
rename from tools/perf/tests/open-syscall-tp-fields.c
rename to tools/perf/tests/openat-syscall-tp-fields.c
index 127dcae0b76033cf457a4a4ff572da6771594e6f..6245221479d766fb9e227064bcdc42742cae65d3 100644 (file)
@@ -5,7 +5,7 @@
 #include "tests.h"
 #include "debug.h"
 
-int test__syscall_open_tp_fields(void)
+int test__syscall_openat_tp_fields(void)
 {
        struct record_opts opts = {
                .target = {
@@ -29,7 +29,7 @@ int test__syscall_open_tp_fields(void)
                goto out;
        }
 
-       evsel = perf_evsel__newtp("syscalls", "sys_enter_open");
+       evsel = perf_evsel__newtp("syscalls", "sys_enter_openat");
        if (evsel == NULL) {
                pr_debug("%s: perf_evsel__newtp\n", __func__);
                goto out_delete_evlist;
@@ -66,7 +66,7 @@ int test__syscall_open_tp_fields(void)
        /*
         * Generate the event:
         */
-       open(filename, flags);
+       openat(AT_FDCWD, filename, flags);
 
        while (1) {
                int before = nr_events;
similarity index 79%
rename from tools/perf/tests/open-syscall.c
rename to tools/perf/tests/openat-syscall.c
index 07aa319bf334f37cc63d5c17efacce2faca54676..9f9491bb8e4897faddcffed824b3d094f73b7d3d 100644 (file)
@@ -3,11 +3,11 @@
 #include "debug.h"
 #include "tests.h"
 
-int test__open_syscall_event(void)
+int test__openat_syscall_event(void)
 {
        int err = -1, fd;
        struct perf_evsel *evsel;
-       unsigned int nr_open_calls = 111, i;
+       unsigned int nr_openat_calls = 111, i;
        struct thread_map *threads = thread_map__new(-1, getpid(), UINT_MAX);
        char sbuf[STRERR_BUFSIZE];
 
@@ -16,7 +16,7 @@ int test__open_syscall_event(void)
                return -1;
        }
 
-       evsel = perf_evsel__newtp("syscalls", "sys_enter_open");
+       evsel = perf_evsel__newtp("syscalls", "sys_enter_openat");
        if (evsel == NULL) {
                if (tracefs_configured())
                        pr_debug("is tracefs mounted on /sys/kernel/tracing?\n");
@@ -34,8 +34,8 @@ int test__open_syscall_event(void)
                goto out_evsel_delete;
        }
 
-       for (i = 0; i < nr_open_calls; ++i) {
-               fd = open("/etc/passwd", O_RDONLY);
+       for (i = 0; i < nr_openat_calls; ++i) {
+               fd = openat(0, "/etc/passwd", O_RDONLY);
                close(fd);
        }
 
@@ -44,9 +44,9 @@ int test__open_syscall_event(void)
                goto out_close_fd;
        }
 
-       if (evsel->counts->cpu[0].val != nr_open_calls) {
+       if (evsel->counts->cpu[0].val != nr_openat_calls) {
                pr_debug("perf_evsel__read_on_cpu: expected to intercept %d calls, got %" PRIu64 "\n",
-                        nr_open_calls, evsel->counts->cpu[0].val);
+                        nr_openat_calls, evsel->counts->cpu[0].val);
                goto out_close_fd;
        }
 
index 3de744961739c2c1502e0c0367c357b2f39c90a6..d76963f7ad3d4a0a117af25f69bcc364d475fa11 100644 (file)
@@ -427,7 +427,7 @@ static int test__checkevent_list(struct perf_evlist *evlist)
        TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv);
        TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
 
-       /* syscalls:sys_enter_open:k */
+       /* syscalls:sys_enter_openat:k */
        evsel = perf_evsel__next(evsel);
        TEST_ASSERT_VAL("wrong type", PERF_TYPE_TRACEPOINT == evsel->attr.type);
        TEST_ASSERT_VAL("wrong sample_type",
@@ -665,7 +665,7 @@ static int test__group3(struct perf_evlist *evlist __maybe_unused)
        TEST_ASSERT_VAL("wrong number of entries", 5 == evlist->nr_entries);
        TEST_ASSERT_VAL("wrong number of groups", 2 == evlist->nr_groups);
 
-       /* group1 syscalls:sys_enter_open:H */
+       /* group1 syscalls:sys_enter_openat:H */
        evsel = leader = perf_evlist__first(evlist);
        TEST_ASSERT_VAL("wrong type", PERF_TYPE_TRACEPOINT == evsel->attr.type);
        TEST_ASSERT_VAL("wrong sample_type",
@@ -1293,7 +1293,7 @@ struct evlist_test {
 
 static struct evlist_test test__events[] = {
        {
-               .name  = "syscalls:sys_enter_open",
+               .name  = "syscalls:sys_enter_openat",
                .check = test__checkevent_tracepoint,
                .id    = 0,
        },
@@ -1353,7 +1353,7 @@ static struct evlist_test test__events[] = {
                .id    = 11,
        },
        {
-               .name  = "syscalls:sys_enter_open:k",
+               .name  = "syscalls:sys_enter_openat:k",
                .check = test__checkevent_tracepoint_modifier,
                .id    = 12,
        },
@@ -1408,7 +1408,7 @@ static struct evlist_test test__events[] = {
                .id    = 22,
        },
        {
-               .name  = "r1,syscalls:sys_enter_open:k,1:1:hp",
+               .name  = "r1,syscalls:sys_enter_openat:k,1:1:hp",
                .check = test__checkevent_list,
                .id    = 23,
        },
@@ -1443,7 +1443,7 @@ static struct evlist_test test__events[] = {
                .id    = 29,
        },
        {
-               .name  = "group1{syscalls:sys_enter_open:H,cycles:kppp},group2{cycles,1:3}:G,instructions:u",
+               .name  = "group1{syscalls:sys_enter_openat:H,cycles:kppp},group2{cycles,1:3}:G,instructions:u",
                .check = test__group3,
                .id    = 30,
        },
@@ -1571,7 +1571,7 @@ static int test_event(struct evlist_test *e)
        if (evlist == NULL)
                return -ENOMEM;
 
-       ret = parse_events(evlist, e->name);
+       ret = parse_events(evlist, e->name, NULL);
        if (ret) {
                pr_debug("failed to parse event '%s', err %d\n",
                         e->name, ret);
index f238442b238a297d11e0622f275bf847e5b7db94..5f49484f1abc03bed80d035c508501914a577caf 100644 (file)
@@ -68,7 +68,7 @@ int test__perf_time_to_tsc(void)
 
        perf_evlist__set_maps(evlist, cpus, threads);
 
-       CHECK__(parse_events(evlist, "cycles:u"));
+       CHECK__(parse_events(evlist, "cycles:u", NULL));
 
        perf_evlist__config(evlist, &opts);
 
index eeb68bb1972d44e41bafa5fc10809700e4afc630..faa04e9d5d5fc751a1ac8082522fb045f108a060 100644 (file)
@@ -152,7 +152,8 @@ int test__pmu(void)
                if (ret)
                        break;
 
-               ret = perf_pmu__config_terms(&formats, &attr, terms, false);
+               ret = perf_pmu__config_terms(&formats, &attr, terms,
+                                            false, NULL);
                if (ret)
                        break;
 
index cc68648c7c555210c17c7c3d8d6b61eb14a39e73..0d31403ea593c7d2e689056af1670a18423a39ed 100644 (file)
@@ -347,7 +347,7 @@ int test__switch_tracking(void)
        perf_evlist__set_maps(evlist, cpus, threads);
 
        /* First event */
-       err = parse_events(evlist, "cpu-clock:u");
+       err = parse_events(evlist, "cpu-clock:u", NULL);
        if (err) {
                pr_debug("Failed to parse event dummy:u\n");
                goto out_err;
@@ -356,7 +356,7 @@ int test__switch_tracking(void)
        cpu_clocks_evsel = perf_evlist__last(evlist);
 
        /* Second event */
-       err = parse_events(evlist, "cycles:u");
+       err = parse_events(evlist, "cycles:u", NULL);
        if (err) {
                pr_debug("Failed to parse event cycles:u\n");
                goto out_err;
@@ -371,7 +371,7 @@ int test__switch_tracking(void)
                goto out;
        }
 
-       err = parse_events(evlist, sched_switch);
+       err = parse_events(evlist, sched_switch, NULL);
        if (err) {
                pr_debug("Failed to parse event %s\n", sched_switch);
                goto out_err;
@@ -401,7 +401,7 @@ int test__switch_tracking(void)
        perf_evsel__set_sample_bit(cycles_evsel, TIME);
 
        /* Fourth event */
-       err = parse_events(evlist, "dummy:u");
+       err = parse_events(evlist, "dummy:u", NULL);
        if (err) {
                pr_debug("Failed to parse event dummy:u\n");
                goto out_err;
index 52758a33f64c5679bb39a2a545320d9a3a44d303..8e5038b48ba8dfe3313d9c508156ae9b4ecb8c5a 100644 (file)
@@ -9,6 +9,15 @@ do {                                                                    \
        }                                                                \
 } while (0)
 
+#define TEST_ASSERT_EQUAL(text, val, expected)                          \
+do {                                                                    \
+       if (val != expected) {                                           \
+               pr_debug("FAILED %s:%d %s (%d != %d)\n",                 \
+                        __FILE__, __LINE__, text, val, expected);       \
+               return -1;                                               \
+       }                                                                \
+} while (0)
+
 enum {
        TEST_OK   =  0,
        TEST_FAIL = -1,
@@ -17,14 +26,14 @@ enum {
 
 /* Tests */
 int test__vmlinux_matches_kallsyms(void);
-int test__open_syscall_event(void);
-int test__open_syscall_event_on_all_cpus(void);
+int test__openat_syscall_event(void);
+int test__openat_syscall_event_on_all_cpus(void);
 int test__basic_mmap(void);
 int test__PERF_RECORD(void);
 int test__rdpmc(void);
 int test__perf_evsel__roundtrip_name_test(void);
 int test__perf_evsel__tp_sched_test(void);
-int test__syscall_open_tp_fields(void);
+int test__syscall_openat_tp_fields(void);
 int test__pmu(void);
 int test__attr(void);
 int test__dso_data(void);
@@ -53,7 +62,7 @@ int test__fdarray__filter(void);
 int test__fdarray__add(void);
 int test__kmod_path__parse(void);
 
-#if defined(__x86_64__) || defined(__i386__) || defined(__arm__)
+#if defined(__x86_64__) || defined(__i386__) || defined(__arm__) || defined(__aarch64__)
 #ifdef HAVE_DWARF_UNWIND_SUPPORT
 struct thread;
 struct perf_sample;
index b028499dd3cf0f5ee7530a6c869aaa40ef802521..01fabb19d74607bb9157f0dbddfd160d21c8cebf 100644 (file)
@@ -43,7 +43,7 @@ int test__thread_mg_share(void)
                        leader && t1 && t2 && t3 && other);
 
        mg = leader->mg;
-       TEST_ASSERT_VAL("wrong refcnt", mg->refcnt == 4);
+       TEST_ASSERT_EQUAL("wrong refcnt", atomic_read(&mg->refcnt), 4);
 
        /* test the map groups pointer is shared */
        TEST_ASSERT_VAL("map groups don't match", mg == t1->mg);
@@ -58,33 +58,40 @@ int test__thread_mg_share(void)
        other_leader = machine__find_thread(machine, 4, 4);
        TEST_ASSERT_VAL("failed to find other leader", other_leader);
 
+       /*
+        * Ok, now that all the rbtree related operations were done,
+        * lets remove all of them from there so that we can do the
+        * refcounting tests.
+        */
+       machine__remove_thread(machine, leader);
+       machine__remove_thread(machine, t1);
+       machine__remove_thread(machine, t2);
+       machine__remove_thread(machine, t3);
+       machine__remove_thread(machine, other);
+       machine__remove_thread(machine, other_leader);
+
        other_mg = other->mg;
-       TEST_ASSERT_VAL("wrong refcnt", other_mg->refcnt == 2);
+       TEST_ASSERT_EQUAL("wrong refcnt", atomic_read(&other_mg->refcnt), 2);
 
        TEST_ASSERT_VAL("map groups don't match", other_mg == other_leader->mg);
 
        /* release thread group */
-       thread__delete(leader);
-       TEST_ASSERT_VAL("wrong refcnt", mg->refcnt == 3);
+       thread__put(leader);
+       TEST_ASSERT_EQUAL("wrong refcnt", atomic_read(&mg->refcnt), 3);
 
-       thread__delete(t1);
-       TEST_ASSERT_VAL("wrong refcnt", mg->refcnt == 2);
+       thread__put(t1);
+       TEST_ASSERT_EQUAL("wrong refcnt", atomic_read(&mg->refcnt), 2);
 
-       thread__delete(t2);
-       TEST_ASSERT_VAL("wrong refcnt", mg->refcnt == 1);
+       thread__put(t2);
+       TEST_ASSERT_EQUAL("wrong refcnt", atomic_read(&mg->refcnt), 1);
 
-       thread__delete(t3);
+       thread__put(t3);
 
        /* release other group  */
-       thread__delete(other_leader);
-       TEST_ASSERT_VAL("wrong refcnt", other_mg->refcnt == 1);
+       thread__put(other_leader);
+       TEST_ASSERT_EQUAL("wrong refcnt", atomic_read(&other_mg->refcnt), 1);
 
-       thread__delete(other);
-
-       /*
-        * Cannot call machine__delete_threads(machine) now,
-        * because we've already released all the threads.
-        */
+       thread__put(other);
 
        machines__exit(&machines);
        return 0;
index 3d9088003a5b6d16da0038d0abbfa1fc427ed65b..b34c5fc829ae2b0da7389bd31649dd0389e40604 100644 (file)
@@ -23,9 +23,10 @@ int test__vmlinux_matches_kallsyms(void)
        int err = -1;
        struct rb_node *nd;
        struct symbol *sym;
-       struct map *kallsyms_map, *vmlinux_map;
+       struct map *kallsyms_map, *vmlinux_map, *map;
        struct machine kallsyms, vmlinux;
        enum map_type type = MAP__FUNCTION;
+       struct maps *maps = &vmlinux.kmaps.maps[type];
        u64 mem_start, mem_end;
 
        /*
@@ -184,8 +185,8 @@ detour:
 
        pr_info("Maps only in vmlinux:\n");
 
-       for (nd = rb_first(&vmlinux.kmaps.maps[type]); nd; nd = rb_next(nd)) {
-               struct map *pos = rb_entry(nd, struct map, rb_node), *pair;
+       for (map = maps__first(maps); map; map = map__next(map)) {
+               struct map *
                /*
                 * If it is the kernel, kallsyms is always "[kernel.kallsyms]", while
                 * the kernel will have the path for the vmlinux file being used,
@@ -193,22 +194,22 @@ detour:
                 * both cases.
                 */
                pair = map_groups__find_by_name(&kallsyms.kmaps, type,
-                                               (pos->dso->kernel ?
-                                                       pos->dso->short_name :
-                                                       pos->dso->name));
+                                               (map->dso->kernel ?
+                                                       map->dso->short_name :
+                                                       map->dso->name));
                if (pair)
                        pair->priv = 1;
                else
-                       map__fprintf(pos, stderr);
+                       map__fprintf(map, stderr);
        }
 
        pr_info("Maps in vmlinux with a different name in kallsyms:\n");
 
-       for (nd = rb_first(&vmlinux.kmaps.maps[type]); nd; nd = rb_next(nd)) {
-               struct map *pos = rb_entry(nd, struct map, rb_node), *pair;
+       for (map = maps__first(maps); map; map = map__next(map)) {
+               struct map *pair;
 
-               mem_start = vmlinux_map->unmap_ip(vmlinux_map, pos->start);
-               mem_end = vmlinux_map->unmap_ip(vmlinux_map, pos->end);
+               mem_start = vmlinux_map->unmap_ip(vmlinux_map, map->start);
+               mem_end = vmlinux_map->unmap_ip(vmlinux_map, map->end);
 
                pair = map_groups__find(&kallsyms.kmaps, type, mem_start);
                if (pair == NULL || pair->priv)
@@ -217,7 +218,7 @@ detour:
                if (pair->start == mem_start) {
                        pair->priv = 1;
                        pr_info(" %" PRIx64 "-%" PRIx64 " %" PRIx64 " %s in kallsyms as",
-                               pos->start, pos->end, pos->pgoff, pos->dso->name);
+                               map->start, map->end, map->pgoff, map->dso->name);
                        if (mem_end != pair->end)
                                pr_info(":\n*%" PRIx64 "-%" PRIx64 " %" PRIx64,
                                        pair->start, pair->end, pair->pgoff);
@@ -228,12 +229,11 @@ detour:
 
        pr_info("Maps only in kallsyms:\n");
 
-       for (nd = rb_first(&kallsyms.kmaps.maps[type]);
-            nd; nd = rb_next(nd)) {
-               struct map *pos = rb_entry(nd, struct map, rb_node);
+       maps = &kallsyms.kmaps.maps[type];
 
-               if (!pos->priv)
-                       map__fprintf(pos, stderr);
+       for (map = maps__first(maps); map; map = map__next(map)) {
+               if (!map->priv)
+                       map__fprintf(map, stderr);
        }
 out:
        machine__exit(&kallsyms);
index e5250eb2dd57866b1051736767dbe598bb4c78d2..5995a8bd7c6971dc4300f9ecc508135645420e7e 100644 (file)
 #include "../../util/evsel.h"
 #include <pthread.h>
 
+struct disasm_line_samples {
+       double          percent;
+       u64             nr;
+};
+
 struct browser_disasm_line {
-       struct rb_node  rb_node;
-       u32             idx;
-       int             idx_asm;
-       int             jump_sources;
+       struct rb_node                  rb_node;
+       u32                             idx;
+       int                             idx_asm;
+       int                             jump_sources;
        /*
         * actual length of this array is saved on the nr_events field
         * of the struct annotate_browser
         */
-       double          percent[1];
+       struct disasm_line_samples      samples[1];
 };
 
 static struct annotate_browser_opt {
@@ -28,7 +33,8 @@ static struct annotate_browser_opt {
             use_offset,
             jump_arrows,
             show_linenr,
-            show_nr_jumps;
+            show_nr_jumps,
+            show_total_period;
 } annotate_browser__opts = {
        .use_offset     = true,
        .jump_arrows    = true,
@@ -105,15 +111,20 @@ static void annotate_browser__write(struct ui_browser *browser, void *entry, int
        char bf[256];
 
        for (i = 0; i < ab->nr_events; i++) {
-               if (bdl->percent[i] > percent_max)
-                       percent_max = bdl->percent[i];
+               if (bdl->samples[i].percent > percent_max)
+                       percent_max = bdl->samples[i].percent;
        }
 
        if (dl->offset != -1 && percent_max != 0.0) {
                for (i = 0; i < ab->nr_events; i++) {
-                       ui_browser__set_percent_color(browser, bdl->percent[i],
+                       ui_browser__set_percent_color(browser,
+                                                     bdl->samples[i].percent,
                                                      current_entry);
-                       slsmg_printf("%6.2f ", bdl->percent[i]);
+                       if (annotate_browser__opts.show_total_period)
+                               slsmg_printf("%6" PRIu64 " ",
+                                            bdl->samples[i].nr);
+                       else
+                               slsmg_printf("%6.2f ", bdl->samples[i].percent);
                }
        } else {
                ui_browser__set_percent_color(browser, 0, current_entry);
@@ -273,9 +284,9 @@ static int disasm__cmp(struct browser_disasm_line *a,
        int i;
 
        for (i = 0; i < nr_pcnt; i++) {
-               if (a->percent[i] == b->percent[i])
+               if (a->samples[i].percent == b->samples[i].percent)
                        continue;
-               return a->percent[i] < b->percent[i];
+               return a->samples[i].percent < b->samples[i].percent;
        }
        return 0;
 }
@@ -366,14 +377,17 @@ static void annotate_browser__calc_percent(struct annotate_browser *browser,
                next = disasm__get_next_ip_line(&notes->src->source, pos);
 
                for (i = 0; i < browser->nr_events; i++) {
-                       bpos->percent[i] = disasm__calc_percent(notes,
+                       u64 nr_samples;
+
+                       bpos->samples[i].percent = disasm__calc_percent(notes,
                                                evsel->idx + i,
                                                pos->offset,
                                                next ? next->offset : len,
-                                               &path);
+                                               &path, &nr_samples);
+                       bpos->samples[i].nr = nr_samples;
 
-                       if (max_percent < bpos->percent[i])
-                               max_percent = bpos->percent[i];
+                       if (max_percent < bpos->samples[i].percent)
+                               max_percent = bpos->samples[i].percent;
                }
 
                if (max_percent < 0.01) {
@@ -737,6 +751,7 @@ static int annotate_browser__run(struct annotate_browser *browser,
                "n             Search next string\n"
                "o             Toggle disassembler output/simplified view\n"
                "s             Toggle source code view\n"
+               "t             Toggle total period view\n"
                "/             Search string\n"
                "k             Toggle line numbers\n"
                "r             Run available scripts\n"
@@ -812,6 +827,11 @@ show_sup_ins:
                                ui_helpline__puts("Actions are only available for 'callq', 'retq' & jump instructions.");
                        }
                        continue;
+               case 't':
+                       annotate_browser__opts.show_total_period =
+                         !annotate_browser__opts.show_total_period;
+                       annotate_browser__update_addr_width(browser);
+                       continue;
                case K_LEFT:
                case K_ESC:
                case 'q':
@@ -832,12 +852,20 @@ out:
 int map_symbol__tui_annotate(struct map_symbol *ms, struct perf_evsel *evsel,
                             struct hist_browser_timer *hbt)
 {
+       /* Set default value for show_total_period.  */
+       annotate_browser__opts.show_total_period =
+         symbol_conf.show_total_period;
+
        return symbol__tui_annotate(ms->sym, ms->map, evsel, hbt);
 }
 
 int hist_entry__tui_annotate(struct hist_entry *he, struct perf_evsel *evsel,
                             struct hist_browser_timer *hbt)
 {
+       /* reset abort key so that it can get Ctrl-C as a key */
+       SLang_reset_tty();
+       SLang_init_tty(0, 0, 0);
+
        return map_symbol__tui_annotate(&he->ms, evsel, hbt);
 }
 
@@ -925,7 +953,8 @@ int symbol__tui_annotate(struct symbol *sym, struct map *map,
 
        if (perf_evsel__is_group_event(evsel)) {
                nr_pcnt = evsel->nr_members;
-               sizeof_bdl += sizeof(double) * (nr_pcnt - 1);
+               sizeof_bdl += sizeof(struct disasm_line_samples) *
+                 (nr_pcnt - 1);
        }
 
        if (symbol__annotate(sym, map, sizeof_bdl) < 0) {
@@ -1002,6 +1031,7 @@ static struct annotate_config {
        ANNOTATE_CFG(show_linenr),
        ANNOTATE_CFG(show_nr_jumps),
        ANNOTATE_CFG(use_offset),
+       ANNOTATE_CFG(show_total_period),
 };
 
 #undef ANNOTATE_CFG
index 995b7a8596b1420e9764f08f18326d319f2a0a31..c42adb6000914554bf0d109e02d9ad5cec801313 100644 (file)
@@ -25,6 +25,9 @@ struct hist_browser {
        struct hists        *hists;
        struct hist_entry   *he_selection;
        struct map_symbol   *selection;
+       struct hist_browser_timer *hbt;
+       struct pstack       *pstack;
+       struct perf_session_env *env;
        int                  print_seq;
        bool                 show_dso;
        bool                 show_headers;
@@ -60,7 +63,7 @@ static int hist_browser__get_folding(struct hist_browser *browser)
                struct hist_entry *he =
                        rb_entry(nd, struct hist_entry, rb_node);
 
-               if (he->ms.unfolded)
+               if (he->unfolded)
                        unfolded_rows += he->nr_rows;
        }
        return unfolded_rows;
@@ -136,24 +139,19 @@ static char tree__folded_sign(bool unfolded)
        return unfolded ? '-' : '+';
 }
 
-static char map_symbol__folded(const struct map_symbol *ms)
-{
-       return ms->has_children ? tree__folded_sign(ms->unfolded) : ' ';
-}
-
 static char hist_entry__folded(const struct hist_entry *he)
 {
-       return map_symbol__folded(&he->ms);
+       return he->has_children ? tree__folded_sign(he->unfolded) : ' ';
 }
 
 static char callchain_list__folded(const struct callchain_list *cl)
 {
-       return map_symbol__folded(&cl->ms);
+       return cl->has_children ? tree__folded_sign(cl->unfolded) : ' ';
 }
 
-static void map_symbol__set_folding(struct map_symbol *ms, bool unfold)
+static void callchain_list__set_folding(struct callchain_list *cl, bool unfold)
 {
-       ms->unfolded = unfold ? ms->has_children : false;
+       cl->unfolded = unfold ? cl->has_children : false;
 }
 
 static int callchain_node__count_rows_rb_tree(struct callchain_node *node)
@@ -189,7 +187,7 @@ static int callchain_node__count_rows(struct callchain_node *node)
 
        list_for_each_entry(chain, &node->val, list) {
                ++n;
-               unfolded = chain->ms.unfolded;
+               unfolded = chain->unfolded;
        }
 
        if (unfolded)
@@ -211,15 +209,27 @@ static int callchain__count_rows(struct rb_root *chain)
        return n;
 }
 
-static bool map_symbol__toggle_fold(struct map_symbol *ms)
+static bool hist_entry__toggle_fold(struct hist_entry *he)
 {
-       if (!ms)
+       if (!he)
                return false;
 
-       if (!ms->has_children)
+       if (!he->has_children)
                return false;
 
-       ms->unfolded = !ms->unfolded;
+       he->unfolded = !he->unfolded;
+       return true;
+}
+
+static bool callchain_list__toggle_fold(struct callchain_list *cl)
+{
+       if (!cl)
+               return false;
+
+       if (!cl->has_children)
+               return false;
+
+       cl->unfolded = !cl->unfolded;
        return true;
 }
 
@@ -235,10 +245,10 @@ static void callchain_node__init_have_children_rb_tree(struct callchain_node *no
                list_for_each_entry(chain, &child->val, list) {
                        if (first) {
                                first = false;
-                               chain->ms.has_children = chain->list.next != &child->val ||
+                               chain->has_children = chain->list.next != &child->val ||
                                                         !RB_EMPTY_ROOT(&child->rb_root);
                        } else
-                               chain->ms.has_children = chain->list.next == &child->val &&
+                               chain->has_children = chain->list.next == &child->val &&
                                                         !RB_EMPTY_ROOT(&child->rb_root);
                }
 
@@ -252,11 +262,11 @@ static void callchain_node__init_have_children(struct callchain_node *node,
        struct callchain_list *chain;
 
        chain = list_entry(node->val.next, struct callchain_list, list);
-       chain->ms.has_children = has_sibling;
+       chain->has_children = has_sibling;
 
        if (!list_empty(&node->val)) {
                chain = list_entry(node->val.prev, struct callchain_list, list);
-               chain->ms.has_children = !RB_EMPTY_ROOT(&node->rb_root);
+               chain->has_children = !RB_EMPTY_ROOT(&node->rb_root);
        }
 
        callchain_node__init_have_children_rb_tree(node);
@@ -276,7 +286,7 @@ static void callchain__init_have_children(struct rb_root *root)
 static void hist_entry__init_have_children(struct hist_entry *he)
 {
        if (!he->init_have_children) {
-               he->ms.has_children = !RB_EMPTY_ROOT(&he->sorted_chain);
+               he->has_children = !RB_EMPTY_ROOT(&he->sorted_chain);
                callchain__init_have_children(&he->sorted_chain);
                he->init_have_children = true;
        }
@@ -284,14 +294,22 @@ static void hist_entry__init_have_children(struct hist_entry *he)
 
 static bool hist_browser__toggle_fold(struct hist_browser *browser)
 {
-       if (map_symbol__toggle_fold(browser->selection)) {
-               struct hist_entry *he = browser->he_selection;
+       struct hist_entry *he = browser->he_selection;
+       struct map_symbol *ms = browser->selection;
+       struct callchain_list *cl = container_of(ms, struct callchain_list, ms);
+       bool has_children;
 
+       if (ms == &he->ms)
+               has_children = hist_entry__toggle_fold(he);
+       else
+               has_children = callchain_list__toggle_fold(cl);
+
+       if (has_children) {
                hist_entry__init_have_children(he);
                browser->b.nr_entries -= he->nr_rows;
                browser->nr_callchain_rows -= he->nr_rows;
 
-               if (he->ms.unfolded)
+               if (he->unfolded)
                        he->nr_rows = callchain__count_rows(&he->sorted_chain);
                else
                        he->nr_rows = 0;
@@ -318,8 +336,8 @@ static int callchain_node__set_folding_rb_tree(struct callchain_node *node, bool
 
                list_for_each_entry(chain, &child->val, list) {
                        ++n;
-                       map_symbol__set_folding(&chain->ms, unfold);
-                       has_children = chain->ms.has_children;
+                       callchain_list__set_folding(chain, unfold);
+                       has_children = chain->has_children;
                }
 
                if (has_children)
@@ -337,8 +355,8 @@ static int callchain_node__set_folding(struct callchain_node *node, bool unfold)
 
        list_for_each_entry(chain, &node->val, list) {
                ++n;
-               map_symbol__set_folding(&chain->ms, unfold);
-               has_children = chain->ms.has_children;
+               callchain_list__set_folding(chain, unfold);
+               has_children = chain->has_children;
        }
 
        if (has_children)
@@ -363,9 +381,9 @@ static int callchain__set_folding(struct rb_root *chain, bool unfold)
 static void hist_entry__set_folding(struct hist_entry *he, bool unfold)
 {
        hist_entry__init_have_children(he);
-       map_symbol__set_folding(&he->ms, unfold);
+       he->unfolded = unfold ? he->has_children : false;
 
-       if (he->ms.has_children) {
+       if (he->has_children) {
                int n = callchain__set_folding(&he->sorted_chain, unfold);
                he->nr_rows = unfold ? n : 0;
        } else
@@ -406,11 +424,11 @@ static void ui_browser__warn_lost_events(struct ui_browser *browser)
                "Or reduce the sampling frequency.");
 }
 
-static int hist_browser__run(struct hist_browser *browser,
-                            struct hist_browser_timer *hbt)
+static int hist_browser__run(struct hist_browser *browser, const char *help)
 {
        int key;
        char title[160];
+       struct hist_browser_timer *hbt = browser->hbt;
        int delay_secs = hbt ? hbt->refresh : 0;
 
        browser->b.entries = &browser->hists->entries;
@@ -418,8 +436,7 @@ static int hist_browser__run(struct hist_browser *browser,
 
        hists__browser_title(browser->hists, hbt, title, sizeof(title));
 
-       if (ui_browser__show(&browser->b, title,
-                            "Press '?' for help on key bindings") < 0)
+       if (ui_browser__show(&browser->b, title, help) < 0)
                return -1;
 
        while (1) {
@@ -1016,7 +1033,7 @@ do_offset:
        if (offset > 0) {
                do {
                        h = rb_entry(nd, struct hist_entry, rb_node);
-                       if (h->ms.unfolded) {
+                       if (h->unfolded) {
                                u16 remaining = h->nr_rows - h->row_offset;
                                if (offset > remaining) {
                                        offset -= remaining;
@@ -1037,7 +1054,7 @@ do_offset:
        } else if (offset < 0) {
                while (1) {
                        h = rb_entry(nd, struct hist_entry, rb_node);
-                       if (h->ms.unfolded) {
+                       if (h->unfolded) {
                                if (first) {
                                        if (-offset > h->row_offset) {
                                                offset += h->row_offset;
@@ -1074,7 +1091,7 @@ do_offset:
                                 * row_offset at its last entry.
                                 */
                                h = rb_entry(nd, struct hist_entry, rb_node);
-                               if (h->ms.unfolded)
+                               if (h->unfolded)
                                        h->row_offset = h->nr_rows;
                                break;
                        }
@@ -1195,7 +1212,9 @@ static int hist_browser__dump(struct hist_browser *browser)
        return 0;
 }
 
-static struct hist_browser *hist_browser__new(struct hists *hists)
+static struct hist_browser *hist_browser__new(struct hists *hists,
+                                             struct hist_browser_timer *hbt,
+                                             struct perf_session_env *env)
 {
        struct hist_browser *browser = zalloc(sizeof(*browser));
 
@@ -1206,6 +1225,8 @@ static struct hist_browser *hist_browser__new(struct hists *hists)
                browser->b.seek = ui_browser__hists_seek;
                browser->b.use_navkeypressed = true;
                browser->show_headers = symbol_conf.show_hist_headers;
+               browser->hbt = hbt;
+               browser->env = env;
        }
 
        return browser;
@@ -1395,6 +1416,257 @@ close_file_and_continue:
        return ret;
 }
 
+struct popup_action {
+       struct thread           *thread;
+       struct dso              *dso;
+       struct map_symbol       ms;
+
+       int (*fn)(struct hist_browser *browser, struct popup_action *act);
+};
+
+static int
+do_annotate(struct hist_browser *browser, struct popup_action *act)
+{
+       struct perf_evsel *evsel;
+       struct annotation *notes;
+       struct hist_entry *he;
+       int err;
+
+       if (!objdump_path && perf_session_env__lookup_objdump(browser->env))
+               return 0;
+
+       notes = symbol__annotation(act->ms.sym);
+       if (!notes->src)
+               return 0;
+
+       evsel = hists_to_evsel(browser->hists);
+       err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt);
+       he = hist_browser__selected_entry(browser);
+       /*
+        * offer option to annotate the other branch source or target
+        * (if they exists) when returning from annotate
+        */
+       if ((err == 'q' || err == CTRL('c')) && he->branch_info)
+               return 1;
+
+       ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
+       if (err)
+               ui_browser__handle_resize(&browser->b);
+       return 0;
+}
+
+static int
+add_annotate_opt(struct hist_browser *browser __maybe_unused,
+                struct popup_action *act, char **optstr,
+                struct map *map, struct symbol *sym)
+{
+       if (sym == NULL || map->dso->annotate_warned)
+               return 0;
+
+       if (asprintf(optstr, "Annotate %s", sym->name) < 0)
+               return 0;
+
+       act->ms.map = map;
+       act->ms.sym = sym;
+       act->fn = do_annotate;
+       return 1;
+}
+
+static int
+do_zoom_thread(struct hist_browser *browser, struct popup_action *act)
+{
+       struct thread *thread = act->thread;
+
+       if (browser->hists->thread_filter) {
+               pstack__remove(browser->pstack, &browser->hists->thread_filter);
+               perf_hpp__set_elide(HISTC_THREAD, false);
+               thread__zput(browser->hists->thread_filter);
+               ui_helpline__pop();
+       } else {
+               ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
+                                  thread->comm_set ? thread__comm_str(thread) : "",
+                                  thread->tid);
+               browser->hists->thread_filter = thread__get(thread);
+               perf_hpp__set_elide(HISTC_THREAD, false);
+               pstack__push(browser->pstack, &browser->hists->thread_filter);
+       }
+
+       hists__filter_by_thread(browser->hists);
+       hist_browser__reset(browser);
+       return 0;
+}
+
+static int
+add_thread_opt(struct hist_browser *browser, struct popup_action *act,
+              char **optstr, struct thread *thread)
+{
+       if (thread == NULL)
+               return 0;
+
+       if (asprintf(optstr, "Zoom %s %s(%d) thread",
+                    browser->hists->thread_filter ? "out of" : "into",
+                    thread->comm_set ? thread__comm_str(thread) : "",
+                    thread->tid) < 0)
+               return 0;
+
+       act->thread = thread;
+       act->fn = do_zoom_thread;
+       return 1;
+}
+
+static int
+do_zoom_dso(struct hist_browser *browser, struct popup_action *act)
+{
+       struct dso *dso = act->dso;
+
+       if (browser->hists->dso_filter) {
+               pstack__remove(browser->pstack, &browser->hists->dso_filter);
+               perf_hpp__set_elide(HISTC_DSO, false);
+               browser->hists->dso_filter = NULL;
+               ui_helpline__pop();
+       } else {
+               if (dso == NULL)
+                       return 0;
+               ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
+                                  dso->kernel ? "the Kernel" : dso->short_name);
+               browser->hists->dso_filter = dso;
+               perf_hpp__set_elide(HISTC_DSO, true);
+               pstack__push(browser->pstack, &browser->hists->dso_filter);
+       }
+
+       hists__filter_by_dso(browser->hists);
+       hist_browser__reset(browser);
+       return 0;
+}
+
+static int
+add_dso_opt(struct hist_browser *browser, struct popup_action *act,
+           char **optstr, struct dso *dso)
+{
+       if (dso == NULL)
+               return 0;
+
+       if (asprintf(optstr, "Zoom %s %s DSO",
+                    browser->hists->dso_filter ? "out of" : "into",
+                    dso->kernel ? "the Kernel" : dso->short_name) < 0)
+               return 0;
+
+       act->dso = dso;
+       act->fn = do_zoom_dso;
+       return 1;
+}
+
+static int
+do_browse_map(struct hist_browser *browser __maybe_unused,
+             struct popup_action *act)
+{
+       map__browse(act->ms.map);
+       return 0;
+}
+
+static int
+add_map_opt(struct hist_browser *browser __maybe_unused,
+           struct popup_action *act, char **optstr, struct map *map)
+{
+       if (map == NULL)
+               return 0;
+
+       if (asprintf(optstr, "Browse map details") < 0)
+               return 0;
+
+       act->ms.map = map;
+       act->fn = do_browse_map;
+       return 1;
+}
+
+static int
+do_run_script(struct hist_browser *browser __maybe_unused,
+             struct popup_action *act)
+{
+       char script_opt[64];
+       memset(script_opt, 0, sizeof(script_opt));
+
+       if (act->thread) {
+               scnprintf(script_opt, sizeof(script_opt), " -c %s ",
+                         thread__comm_str(act->thread));
+       } else if (act->ms.sym) {
+               scnprintf(script_opt, sizeof(script_opt), " -S %s ",
+                         act->ms.sym->name);
+       }
+
+       script_browse(script_opt);
+       return 0;
+}
+
+static int
+add_script_opt(struct hist_browser *browser __maybe_unused,
+              struct popup_action *act, char **optstr,
+              struct thread *thread, struct symbol *sym)
+{
+       if (thread) {
+               if (asprintf(optstr, "Run scripts for samples of thread [%s]",
+                            thread__comm_str(thread)) < 0)
+                       return 0;
+       } else if (sym) {
+               if (asprintf(optstr, "Run scripts for samples of symbol [%s]",
+                            sym->name) < 0)
+                       return 0;
+       } else {
+               if (asprintf(optstr, "Run scripts for all samples") < 0)
+                       return 0;
+       }
+
+       act->thread = thread;
+       act->ms.sym = sym;
+       act->fn = do_run_script;
+       return 1;
+}
+
+static int
+do_switch_data(struct hist_browser *browser __maybe_unused,
+              struct popup_action *act __maybe_unused)
+{
+       if (switch_data_file()) {
+               ui__warning("Won't switch the data files due to\n"
+                           "no valid data file get selected!\n");
+               return 0;
+       }
+
+       return K_SWITCH_INPUT_DATA;
+}
+
+static int
+add_switch_opt(struct hist_browser *browser,
+              struct popup_action *act, char **optstr)
+{
+       if (!is_report_browser(browser->hbt))
+               return 0;
+
+       if (asprintf(optstr, "Switch to another data file in PWD") < 0)
+               return 0;
+
+       act->fn = do_switch_data;
+       return 1;
+}
+
+static int
+do_exit_browser(struct hist_browser *browser __maybe_unused,
+               struct popup_action *act __maybe_unused)
+{
+       return 0;
+}
+
+static int
+add_exit_opt(struct hist_browser *browser __maybe_unused,
+            struct popup_action *act, char **optstr)
+{
+       if (asprintf(optstr, "Exit") < 0)
+               return 0;
+
+       act->fn = do_exit_browser;
+       return 1;
+}
+
 static void hist_browser__update_nr_entries(struct hist_browser *hb)
 {
        u64 nr_entries = 0;
@@ -1421,14 +1693,14 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
                                    struct perf_session_env *env)
 {
        struct hists *hists = evsel__hists(evsel);
-       struct hist_browser *browser = hist_browser__new(hists);
+       struct hist_browser *browser = hist_browser__new(hists, hbt, env);
        struct branch_info *bi;
-       struct pstack *fstack;
-       char *options[16];
+#define MAX_OPTIONS  16
+       char *options[MAX_OPTIONS];
+       struct popup_action actions[MAX_OPTIONS];
        int nr_options = 0;
        int key = -1;
        char buf[64];
-       char script_opt[64];
        int delay_secs = hbt ? hbt->refresh : 0;
        struct perf_hpp_fmt *fmt;
 
@@ -1463,23 +1735,29 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
        "t             Zoom into current Thread\n"
        "V             Verbose (DSO names in callchains, etc)\n"
        "z             Toggle zeroing of samples\n"
+       "f             Enable/Disable events\n"
        "/             Filter symbol by name";
 
        if (browser == NULL)
                return -1;
 
+       /* reset abort key so that it can get Ctrl-C as a key */
+       SLang_reset_tty();
+       SLang_init_tty(0, 0, 0);
+
        if (min_pcnt) {
                browser->min_pcnt = min_pcnt;
                hist_browser__update_nr_entries(browser);
        }
 
-       fstack = pstack__new(2);
-       if (fstack == NULL)
+       browser->pstack = pstack__new(2);
+       if (browser->pstack == NULL)
                goto out;
 
        ui_helpline__push(helpline);
 
        memset(options, 0, sizeof(options));
+       memset(actions, 0, sizeof(actions));
 
        perf_hpp__for_each_format(fmt)
                perf_hpp__reset_width(fmt, hists);
@@ -1489,16 +1767,12 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
 
        while (1) {
                struct thread *thread = NULL;
-               const struct dso *dso = NULL;
-               int choice = 0,
-                   annotate = -2, zoom_dso = -2, zoom_thread = -2,
-                   annotate_f = -2, annotate_t = -2, browse_map = -2;
-               int scripts_comm = -2, scripts_symbol = -2,
-                   scripts_all = -2, switch_data = -2;
+               struct dso *dso = NULL;
+               int choice = 0;
 
                nr_options = 0;
 
-               key = hist_browser__run(browser, hbt);
+               key = hist_browser__run(browser, helpline);
 
                if (browser->he_selection != NULL) {
                        thread = hist_browser__selected_thread(browser);
@@ -1526,17 +1800,25 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
                            browser->selection->sym == NULL ||
                            browser->selection->map->dso->annotate_warned)
                                continue;
-                       goto do_annotate;
+
+                       actions->ms.map = browser->selection->map;
+                       actions->ms.sym = browser->selection->sym;
+                       do_annotate(browser, actions);
+                       continue;
                case 'P':
                        hist_browser__dump(browser);
                        continue;
                case 'd':
-                       goto zoom_dso;
+                       actions->dso = dso;
+                       do_zoom_dso(browser, actions);
+                       continue;
                case 'V':
                        browser->show_dso = !browser->show_dso;
                        continue;
                case 't':
-                       goto zoom_thread;
+                       actions->thread = thread;
+                       do_zoom_thread(browser, actions);
+                       continue;
                case '/':
                        if (ui_browser__input_window("Symbol to show",
                                        "Please enter the name of symbol you want to see",
@@ -1548,12 +1830,18 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
                        }
                        continue;
                case 'r':
-                       if (is_report_browser(hbt))
-                               goto do_scripts;
+                       if (is_report_browser(hbt)) {
+                               actions->thread = NULL;
+                               actions->ms.sym = NULL;
+                               do_run_script(browser, actions);
+                       }
                        continue;
                case 's':
-                       if (is_report_browser(hbt))
-                               goto do_data_switch;
+                       if (is_report_browser(hbt)) {
+                               key = do_switch_data(browser, actions);
+                               if (key == K_SWITCH_INPUT_DATA)
+                                       goto out_free_stack;
+                       }
                        continue;
                case 'i':
                        /* env->arch is NULL for live-mode (i.e. perf top) */
@@ -1583,7 +1871,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
                case K_LEFT: {
                        const void *top;
 
-                       if (pstack__empty(fstack)) {
+                       if (pstack__empty(browser->pstack)) {
                                /*
                                 * Go back to the perf_evsel_menu__run or other user
                                 */
@@ -1591,11 +1879,17 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
                                        goto out_free_stack;
                                continue;
                        }
-                       top = pstack__pop(fstack);
-                       if (top == &browser->hists->dso_filter)
-                               goto zoom_out_dso;
+                       top = pstack__peek(browser->pstack);
+                       if (top == &browser->hists->dso_filter) {
+                               /*
+                                * No need to set actions->dso here since
+                                * it's just to remove the current filter.
+                                * Ditto for thread below.
+                                */
+                               do_zoom_dso(browser, actions);
+                       }
                        if (top == &browser->hists->thread_filter)
-                               goto zoom_out_thread;
+                               do_zoom_thread(browser, actions);
                        continue;
                }
                case K_ESC:
@@ -1607,7 +1901,12 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
                case 'q':
                case CTRL('c'):
                        goto out_free_stack;
+               case 'f':
+                       if (!is_report_browser(hbt))
+                               goto out_free_stack;
+                       /* Fall thru */
                default:
+                       helpline = "Press '?' for help on key bindings";
                        continue;
                }
 
@@ -1623,196 +1922,71 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
                        if (bi == NULL)
                                goto skip_annotation;
 
-                       if (bi->from.sym != NULL &&
-                           !bi->from.map->dso->annotate_warned &&
-                           asprintf(&options[nr_options], "Annotate %s", bi->from.sym->name) > 0) {
-                               annotate_f = nr_options++;
-                       }
-
-                       if (bi->to.sym != NULL &&
-                           !bi->to.map->dso->annotate_warned &&
-                           (bi->to.sym != bi->from.sym ||
-                            bi->to.map->dso != bi->from.map->dso) &&
-                           asprintf(&options[nr_options], "Annotate %s", bi->to.sym->name) > 0) {
-                               annotate_t = nr_options++;
-                       }
+                       nr_options += add_annotate_opt(browser,
+                                                      &actions[nr_options],
+                                                      &options[nr_options],
+                                                      bi->from.map,
+                                                      bi->from.sym);
+                       if (bi->to.sym != bi->from.sym)
+                               nr_options += add_annotate_opt(browser,
+                                                       &actions[nr_options],
+                                                       &options[nr_options],
+                                                       bi->to.map,
+                                                       bi->to.sym);
                } else {
-                       if (browser->selection->sym != NULL &&
-                           !browser->selection->map->dso->annotate_warned) {
-                               struct annotation *notes;
-
-                               notes = symbol__annotation(browser->selection->sym);
-
-                               if (notes->src &&
-                                   asprintf(&options[nr_options], "Annotate %s",
-                                                browser->selection->sym->name) > 0) {
-                                       annotate = nr_options++;
-                               }
-                       }
+                       nr_options += add_annotate_opt(browser,
+                                                      &actions[nr_options],
+                                                      &options[nr_options],
+                                                      browser->selection->map,
+                                                      browser->selection->sym);
                }
 skip_annotation:
-               if (thread != NULL &&
-                   asprintf(&options[nr_options], "Zoom %s %s(%d) thread",
-                            (browser->hists->thread_filter ? "out of" : "into"),
-                            (thread->comm_set ? thread__comm_str(thread) : ""),
-                            thread->tid) > 0)
-                       zoom_thread = nr_options++;
-
-               if (dso != NULL &&
-                   asprintf(&options[nr_options], "Zoom %s %s DSO",
-                            (browser->hists->dso_filter ? "out of" : "into"),
-                            (dso->kernel ? "the Kernel" : dso->short_name)) > 0)
-                       zoom_dso = nr_options++;
-
-               if (browser->selection != NULL &&
-                   browser->selection->map != NULL &&
-                   asprintf(&options[nr_options], "Browse map details") > 0)
-                       browse_map = nr_options++;
+               nr_options += add_thread_opt(browser, &actions[nr_options],
+                                            &options[nr_options], thread);
+               nr_options += add_dso_opt(browser, &actions[nr_options],
+                                         &options[nr_options], dso);
+               nr_options += add_map_opt(browser, &actions[nr_options],
+                                         &options[nr_options],
+                                         browser->selection->map);
 
                /* perf script support */
                if (browser->he_selection) {
-                       struct symbol *sym;
-
-                       if (asprintf(&options[nr_options], "Run scripts for samples of thread [%s]",
-                                    thread__comm_str(browser->he_selection->thread)) > 0)
-                               scripts_comm = nr_options++;
-
-                       sym = browser->he_selection->ms.sym;
-                       if (sym && sym->namelen &&
-                               asprintf(&options[nr_options], "Run scripts for samples of symbol [%s]",
-                                               sym->name) > 0)
-                               scripts_symbol = nr_options++;
+                       nr_options += add_script_opt(browser,
+                                                    &actions[nr_options],
+                                                    &options[nr_options],
+                                                    thread, NULL);
+                       nr_options += add_script_opt(browser,
+                                                    &actions[nr_options],
+                                                    &options[nr_options],
+                                                    NULL, browser->selection->sym);
                }
-
-               if (asprintf(&options[nr_options], "Run scripts for all samples") > 0)
-                       scripts_all = nr_options++;
-
-               if (is_report_browser(hbt) && asprintf(&options[nr_options],
-                               "Switch to another data file in PWD") > 0)
-                       switch_data = nr_options++;
+               nr_options += add_script_opt(browser, &actions[nr_options],
+                                            &options[nr_options], NULL, NULL);
+               nr_options += add_switch_opt(browser, &actions[nr_options],
+                                            &options[nr_options]);
 add_exit_option:
-               options[nr_options++] = (char *)"Exit";
-retry_popup_menu:
-               choice = ui__popup_menu(nr_options, options);
-
-               if (choice == nr_options - 1)
-                       break;
-
-               if (choice == -1) {
-                       free_popup_options(options, nr_options - 1);
-                       continue;
-               }
-
-               if (choice == annotate || choice == annotate_t || choice == annotate_f) {
-                       struct hist_entry *he;
-                       struct annotation *notes;
-                       struct map_symbol ms;
-                       int err;
-do_annotate:
-                       if (!objdump_path && perf_session_env__lookup_objdump(env))
-                               continue;
-
-                       he = hist_browser__selected_entry(browser);
-                       if (he == NULL)
-                               continue;
-
-                       if (choice == annotate_f) {
-                               ms.map = he->branch_info->from.map;
-                               ms.sym = he->branch_info->from.sym;
-                       } else if (choice == annotate_t) {
-                               ms.map = he->branch_info->to.map;
-                               ms.sym = he->branch_info->to.sym;
-                       } else {
-                               ms = *browser->selection;
-                       }
+               nr_options += add_exit_opt(browser, &actions[nr_options],
+                                          &options[nr_options]);
 
-                       notes = symbol__annotation(ms.sym);
-                       if (!notes->src)
-                               continue;
-
-                       err = map_symbol__tui_annotate(&ms, evsel, hbt);
-                       /*
-                        * offer option to annotate the other branch source or target
-                        * (if they exists) when returning from annotate
-                        */
-                       if ((err == 'q' || err == CTRL('c'))
-                           && annotate_t != -2 && annotate_f != -2)
-                               goto retry_popup_menu;
-
-                       ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
-                       if (err)
-                               ui_browser__handle_resize(&browser->b);
-
-               } else if (choice == browse_map)
-                       map__browse(browser->selection->map);
-               else if (choice == zoom_dso) {
-zoom_dso:
-                       if (browser->hists->dso_filter) {
-                               pstack__remove(fstack, &browser->hists->dso_filter);
-zoom_out_dso:
-                               ui_helpline__pop();
-                               browser->hists->dso_filter = NULL;
-                               perf_hpp__set_elide(HISTC_DSO, false);
-                       } else {
-                               if (dso == NULL)
-                                       continue;
-                               ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
-                                                  dso->kernel ? "the Kernel" : dso->short_name);
-                               browser->hists->dso_filter = dso;
-                               perf_hpp__set_elide(HISTC_DSO, true);
-                               pstack__push(fstack, &browser->hists->dso_filter);
-                       }
-                       hists__filter_by_dso(hists);
-                       hist_browser__reset(browser);
-               } else if (choice == zoom_thread) {
-zoom_thread:
-                       if (browser->hists->thread_filter) {
-                               pstack__remove(fstack, &browser->hists->thread_filter);
-zoom_out_thread:
-                               ui_helpline__pop();
-                               thread__zput(browser->hists->thread_filter);
-                               perf_hpp__set_elide(HISTC_THREAD, false);
-                       } else {
-                               ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
-                                                  thread->comm_set ? thread__comm_str(thread) : "",
-                                                  thread->tid);
-                               browser->hists->thread_filter = thread__get(thread);
-                               perf_hpp__set_elide(HISTC_THREAD, false);
-                               pstack__push(fstack, &browser->hists->thread_filter);
-                       }
-                       hists__filter_by_thread(hists);
-                       hist_browser__reset(browser);
-               }
-               /* perf scripts support */
-               else if (choice == scripts_all || choice == scripts_comm ||
-                               choice == scripts_symbol) {
-do_scripts:
-                       memset(script_opt, 0, 64);
+               do {
+                       struct popup_action *act;
 
-                       if (choice == scripts_comm)
-                               sprintf(script_opt, " -c %s ", thread__comm_str(browser->he_selection->thread));
+                       choice = ui__popup_menu(nr_options, options);
+                       if (choice == -1 || choice >= nr_options)
+                               break;
 
-                       if (choice == scripts_symbol)
-                               sprintf(script_opt, " -S %s ", browser->he_selection->ms.sym->name);
+                       act = &actions[choice];
+                       key = act->fn(browser, act);
+               } while (key == 1);
 
-                       script_browse(script_opt);
-               }
-               /* Switch to another data file */
-               else if (choice == switch_data) {
-do_data_switch:
-                       if (!switch_data_file()) {
-                               key = K_SWITCH_INPUT_DATA;
-                               break;
-                       } else
-                               ui__warning("Won't switch the data files due to\n"
-                                       "no valid data file get selected!\n");
-               }
+               if (key == K_SWITCH_INPUT_DATA)
+                       break;
        }
 out_free_stack:
-       pstack__delete(fstack);
+       pstack__delete(browser->pstack);
 out:
        hist_browser__delete(browser);
-       free_popup_options(options, nr_options - 1);
+       free_popup_options(options, MAX_OPTIONS);
        return key;
 }
 
index b77e1d7713637c711e144886c9914fe02cb110ca..60d1f29b4b50a9fedf0a163855056edfce1ed22b 100644 (file)
@@ -129,7 +129,7 @@ int ui__init(void)
        err = SLsmg_init_smg();
        if (err < 0)
                goto out;
-       err = SLang_init_tty(0, 0, 0);
+       err = SLang_init_tty(-1, 0, 0);
        if (err < 0)
                goto out;
 
index 797490a40075600c47e0341378e4ad9e24ef225e..586a59d46022a9fc8901807f5c02be4e2551db25 100644 (file)
@@ -68,12 +68,15 @@ libperf-y += rblist.o
 libperf-y += intlist.o
 libperf-y += vdso.o
 libperf-y += stat.o
+libperf-y += stat-shadow.o
 libperf-y += record.o
 libperf-y += srcline.o
 libperf-y += data.o
 libperf-$(CONFIG_X86) += tsc.o
 libperf-y += cloexec.o
 libperf-y += thread-stack.o
+libperf-$(CONFIG_AUXTRACE) += auxtrace.o
+libperf-y += parse-branch-options.o
 
 libperf-$(CONFIG_LIBELF) += symbol-elf.o
 libperf-$(CONFIG_LIBELF) += probe-event.o
@@ -101,23 +104,23 @@ CFLAGS_exec_cmd.o += -DPERF_EXEC_PATH="BUILD_STR($(perfexecdir_SQ))" -DPREFIX="B
 
 $(OUTPUT)util/parse-events-flex.c: util/parse-events.l $(OUTPUT)util/parse-events-bison.c
        $(call rule_mkdir)
-       @$(call echo-cmd,flex)$(FLEX) -o $@ --header-file=$(OUTPUT)util/parse-events-flex.h $(PARSER_DEBUG_FLEX) util/parse-events.l
+       $(Q)$(call echo-cmd,flex)$(FLEX) -o $@ --header-file=$(OUTPUT)util/parse-events-flex.h $(PARSER_DEBUG_FLEX) util/parse-events.l
 
 $(OUTPUT)util/parse-events-bison.c: util/parse-events.y
        $(call rule_mkdir)
-       @$(call echo-cmd,bison)$(BISON) -v util/parse-events.y -d $(PARSER_DEBUG_BISON) -o $@ -p parse_events_
+       $(Q)$(call echo-cmd,bison)$(BISON) -v util/parse-events.y -d $(PARSER_DEBUG_BISON) -o $@ -p parse_events_
 
 $(OUTPUT)util/pmu-flex.c: util/pmu.l $(OUTPUT)util/pmu-bison.c
        $(call rule_mkdir)
-       @$(call echo-cmd,flex)$(FLEX) -o $@ --header-file=$(OUTPUT)util/pmu-flex.h util/pmu.l
+       $(Q)$(call echo-cmd,flex)$(FLEX) -o $@ --header-file=$(OUTPUT)util/pmu-flex.h util/pmu.l
 
 $(OUTPUT)util/pmu-bison.c: util/pmu.y
        $(call rule_mkdir)
-       @$(call echo-cmd,bison)$(BISON) -v util/pmu.y -d -o $@ -p perf_pmu_
+       $(Q)$(call echo-cmd,bison)$(BISON) -v util/pmu.y -d -o $@ -p perf_pmu_
 
 CFLAGS_parse-events-flex.o  += -w
 CFLAGS_pmu-flex.o           += -w
-CFLAGS_parse-events-bison.o += -DYYENABLE_NLS=0 -DYYLTYPE_IS_TRIVIAL=0 -w
+CFLAGS_parse-events-bison.o += -DYYENABLE_NLS=0 -w
 CFLAGS_pmu-bison.o          += -DYYENABLE_NLS=0 -DYYLTYPE_IS_TRIVIAL=0 -w
 
 $(OUTPUT)util/parse-events.o: $(OUTPUT)util/parse-events-flex.c $(OUTPUT)util/parse-events-bison.c
index 7f5bdfc9bc87d1d1828efeb9f57755071b378e7a..03b7bc70eb66032d4502ec8bfda2e15a9d44cd57 100644 (file)
@@ -506,6 +506,17 @@ static int __symbol__inc_addr_samples(struct symbol *sym, struct map *map,
        return 0;
 }
 
+static struct annotation *symbol__get_annotation(struct symbol *sym)
+{
+       struct annotation *notes = symbol__annotation(sym);
+
+       if (notes->src == NULL) {
+               if (symbol__alloc_hist(sym) < 0)
+                       return NULL;
+       }
+       return notes;
+}
+
 static int symbol__inc_addr_samples(struct symbol *sym, struct map *map,
                                    int evidx, u64 addr)
 {
@@ -513,13 +524,9 @@ static int symbol__inc_addr_samples(struct symbol *sym, struct map *map,
 
        if (sym == NULL)
                return 0;
-
-       notes = symbol__annotation(sym);
-       if (notes->src == NULL) {
-               if (symbol__alloc_hist(sym) < 0)
-                       return -ENOMEM;
-       }
-
+       notes = symbol__get_annotation(sym);
+       if (notes == NULL)
+               return -ENOMEM;
        return __symbol__inc_addr_samples(sym, map, notes, evidx, addr);
 }
 
@@ -647,14 +654,15 @@ struct disasm_line *disasm__get_next_ip_line(struct list_head *head, struct disa
 }
 
 double disasm__calc_percent(struct annotation *notes, int evidx, s64 offset,
-                           s64 end, const char **path)
+                           s64 end, const char **path, u64 *nr_samples)
 {
        struct source_line *src_line = notes->src->lines;
        double percent = 0.0;
+       *nr_samples = 0;
 
        if (src_line) {
                size_t sizeof_src_line = sizeof(*src_line) +
-                               sizeof(src_line->p) * (src_line->nr_pcnt - 1);
+                               sizeof(src_line->samples) * (src_line->nr_pcnt - 1);
 
                while (offset < end) {
                        src_line = (void *)notes->src->lines +
@@ -663,7 +671,8 @@ double disasm__calc_percent(struct annotation *notes, int evidx, s64 offset,
                        if (*path == NULL)
                                *path = src_line->path;
 
-                       percent += src_line->p[evidx].percent;
+                       percent += src_line->samples[evidx].percent;
+                       *nr_samples += src_line->samples[evidx].nr;
                        offset++;
                }
        } else {
@@ -673,8 +682,10 @@ double disasm__calc_percent(struct annotation *notes, int evidx, s64 offset,
                while (offset < end)
                        hits += h->addr[offset++];
 
-               if (h->sum)
+               if (h->sum) {
+                       *nr_samples = hits;
                        percent = 100.0 * hits / h->sum;
+               }
        }
 
        return percent;
@@ -689,8 +700,10 @@ static int disasm_line__print(struct disasm_line *dl, struct symbol *sym, u64 st
 
        if (dl->offset != -1) {
                const char *path = NULL;
+               u64 nr_samples;
                double percent, max_percent = 0.0;
                double *ppercents = &percent;
+               u64 *psamples = &nr_samples;
                int i, nr_percent = 1;
                const char *color;
                struct annotation *notes = symbol__annotation(sym);
@@ -703,8 +716,10 @@ static int disasm_line__print(struct disasm_line *dl, struct symbol *sym, u64 st
                if (perf_evsel__is_group_event(evsel)) {
                        nr_percent = evsel->nr_members;
                        ppercents = calloc(nr_percent, sizeof(double));
-                       if (ppercents == NULL)
+                       psamples = calloc(nr_percent, sizeof(u64));
+                       if (ppercents == NULL || psamples == NULL) {
                                return -1;
+                       }
                }
 
                for (i = 0; i < nr_percent; i++) {
@@ -712,9 +727,10 @@ static int disasm_line__print(struct disasm_line *dl, struct symbol *sym, u64 st
                                        notes->src->lines ? i : evsel->idx + i,
                                        offset,
                                        next ? next->offset : (s64) len,
-                                       &path);
+                                       &path, &nr_samples);
 
                        ppercents[i] = percent;
+                       psamples[i] = nr_samples;
                        if (percent > max_percent)
                                max_percent = percent;
                }
@@ -752,8 +768,14 @@ static int disasm_line__print(struct disasm_line *dl, struct symbol *sym, u64 st
 
                for (i = 0; i < nr_percent; i++) {
                        percent = ppercents[i];
+                       nr_samples = psamples[i];
                        color = get_percent_color(percent);
-                       color_fprintf(stdout, color, " %7.2f", percent);
+
+                       if (symbol_conf.show_total_period)
+                               color_fprintf(stdout, color, " %7" PRIu64,
+                                             nr_samples);
+                       else
+                               color_fprintf(stdout, color, " %7.2f", percent);
                }
 
                printf(" :      ");
@@ -763,6 +785,9 @@ static int disasm_line__print(struct disasm_line *dl, struct symbol *sym, u64 st
                if (ppercents != &percent)
                        free(ppercents);
 
+               if (psamples != &nr_samples)
+                       free(psamples);
+
        } else if (max_lines && printed >= max_lines)
                return 1;
        else {
@@ -1096,7 +1121,7 @@ static void insert_source_line(struct rb_root *root, struct source_line *src_lin
                ret = strcmp(iter->path, src_line->path);
                if (ret == 0) {
                        for (i = 0; i < src_line->nr_pcnt; i++)
-                               iter->p[i].percent_sum += src_line->p[i].percent;
+                               iter->samples[i].percent_sum += src_line->samples[i].percent;
                        return;
                }
 
@@ -1107,7 +1132,7 @@ static void insert_source_line(struct rb_root *root, struct source_line *src_lin
        }
 
        for (i = 0; i < src_line->nr_pcnt; i++)
-               src_line->p[i].percent_sum = src_line->p[i].percent;
+               src_line->samples[i].percent_sum = src_line->samples[i].percent;
 
        rb_link_node(&src_line->node, parent, p);
        rb_insert_color(&src_line->node, root);
@@ -1118,9 +1143,9 @@ static int cmp_source_line(struct source_line *a, struct source_line *b)
        int i;
 
        for (i = 0; i < a->nr_pcnt; i++) {
-               if (a->p[i].percent_sum == b->p[i].percent_sum)
+               if (a->samples[i].percent_sum == b->samples[i].percent_sum)
                        continue;
-               return a->p[i].percent_sum > b->p[i].percent_sum;
+               return a->samples[i].percent_sum > b->samples[i].percent_sum;
        }
 
        return 0;
@@ -1172,7 +1197,7 @@ static void symbol__free_source_line(struct symbol *sym, int len)
        int i;
 
        sizeof_src_line = sizeof(*src_line) +
-                         (sizeof(src_line->p) * (src_line->nr_pcnt - 1));
+                         (sizeof(src_line->samples) * (src_line->nr_pcnt - 1));
 
        for (i = 0; i < len; i++) {
                free_srcline(src_line->path);
@@ -1204,7 +1229,7 @@ static int symbol__get_source_line(struct symbol *sym, struct map *map,
                        h_sum += h->sum;
                }
                nr_pcnt = evsel->nr_members;
-               sizeof_src_line += (nr_pcnt - 1) * sizeof(src_line->p);
+               sizeof_src_line += (nr_pcnt - 1) * sizeof(src_line->samples);
        }
 
        if (!h_sum)
@@ -1224,10 +1249,10 @@ static int symbol__get_source_line(struct symbol *sym, struct map *map,
 
                for (k = 0; k < nr_pcnt; k++) {
                        h = annotation__histogram(notes, evidx + k);
-                       src_line->p[k].percent = 100.0 * h->addr[i] / h->sum;
+                       src_line->samples[k].percent = 100.0 * h->addr[i] / h->sum;
 
-                       if (src_line->p[k].percent > percent_max)
-                               percent_max = src_line->p[k].percent;
+                       if (src_line->samples[k].percent > percent_max)
+                               percent_max = src_line->samples[k].percent;
                }
 
                if (percent_max <= 0.5)
@@ -1267,7 +1292,7 @@ static void print_summary(struct rb_root *root, const char *filename)
 
                src_line = rb_entry(node, struct source_line, node);
                for (i = 0; i < src_line->nr_pcnt; i++) {
-                       percent = src_line->p[i].percent_sum;
+                       percent = src_line->samples[i].percent_sum;
                        color = get_percent_color(percent);
                        color_fprintf(stdout, color, " %7.2f", percent);
 
index cadbdc90a5cbf319385cb67aa5a88c06cdf107dc..7e78e6c270783475acb6dc897109254d6d266b35 100644 (file)
@@ -72,23 +72,24 @@ struct disasm_line *disasm__get_next_ip_line(struct list_head *head, struct disa
 int disasm_line__scnprintf(struct disasm_line *dl, char *bf, size_t size, bool raw);
 size_t disasm__fprintf(struct list_head *head, FILE *fp);
 double disasm__calc_percent(struct annotation *notes, int evidx, s64 offset,
-                           s64 end, const char **path);
+                           s64 end, const char **path, u64 *nr_samples);
 
 struct sym_hist {
        u64             sum;
        u64             addr[0];
 };
 
-struct source_line_percent {
+struct source_line_samples {
        double          percent;
        double          percent_sum;
+       double          nr;
 };
 
 struct source_line {
        struct rb_node  node;
        char            *path;
        int             nr_pcnt;
-       struct source_line_percent p[1];
+       struct source_line_samples samples[1];
 };
 
 /** struct annotated_source - symbols with hits have this attached as in sannotation
diff --git a/tools/perf/util/auxtrace.c b/tools/perf/util/auxtrace.c
new file mode 100644 (file)
index 0000000..df66966
--- /dev/null
@@ -0,0 +1,1352 @@
+/*
+ * auxtrace.c: AUX area trace support
+ * Copyright (c) 2013-2015, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <stdbool.h>
+
+#include <linux/kernel.h>
+#include <linux/perf_event.h>
+#include <linux/types.h>
+#include <linux/bitops.h>
+#include <linux/log2.h>
+#include <linux/string.h>
+
+#include <sys/param.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <limits.h>
+#include <errno.h>
+#include <linux/list.h>
+
+#include "../perf.h"
+#include "util.h"
+#include "evlist.h"
+#include "cpumap.h"
+#include "thread_map.h"
+#include "asm/bug.h"
+#include "auxtrace.h"
+
+#include <linux/hash.h>
+
+#include "event.h"
+#include "session.h"
+#include "debug.h"
+#include "parse-options.h"
+
+int auxtrace_mmap__mmap(struct auxtrace_mmap *mm,
+                       struct auxtrace_mmap_params *mp,
+                       void *userpg, int fd)
+{
+       struct perf_event_mmap_page *pc = userpg;
+
+#if BITS_PER_LONG != 64 && !defined(HAVE_SYNC_COMPARE_AND_SWAP_SUPPORT)
+       pr_err("Cannot use AUX area tracing mmaps\n");
+       return -1;
+#endif
+
+       WARN_ONCE(mm->base, "Uninitialized auxtrace_mmap\n");
+
+       mm->userpg = userpg;
+       mm->mask = mp->mask;
+       mm->len = mp->len;
+       mm->prev = 0;
+       mm->idx = mp->idx;
+       mm->tid = mp->tid;
+       mm->cpu = mp->cpu;
+
+       if (!mp->len) {
+               mm->base = NULL;
+               return 0;
+       }
+
+       pc->aux_offset = mp->offset;
+       pc->aux_size = mp->len;
+
+       mm->base = mmap(NULL, mp->len, mp->prot, MAP_SHARED, fd, mp->offset);
+       if (mm->base == MAP_FAILED) {
+               pr_debug2("failed to mmap AUX area\n");
+               mm->base = NULL;
+               return -1;
+       }
+
+       return 0;
+}
+
+void auxtrace_mmap__munmap(struct auxtrace_mmap *mm)
+{
+       if (mm->base) {
+               munmap(mm->base, mm->len);
+               mm->base = NULL;
+       }
+}
+
+void auxtrace_mmap_params__init(struct auxtrace_mmap_params *mp,
+                               off_t auxtrace_offset,
+                               unsigned int auxtrace_pages,
+                               bool auxtrace_overwrite)
+{
+       if (auxtrace_pages) {
+               mp->offset = auxtrace_offset;
+               mp->len = auxtrace_pages * (size_t)page_size;
+               mp->mask = is_power_of_2(mp->len) ? mp->len - 1 : 0;
+               mp->prot = PROT_READ | (auxtrace_overwrite ? 0 : PROT_WRITE);
+               pr_debug2("AUX area mmap length %zu\n", mp->len);
+       } else {
+               mp->len = 0;
+       }
+}
+
+void auxtrace_mmap_params__set_idx(struct auxtrace_mmap_params *mp,
+                                  struct perf_evlist *evlist, int idx,
+                                  bool per_cpu)
+{
+       mp->idx = idx;
+
+       if (per_cpu) {
+               mp->cpu = evlist->cpus->map[idx];
+               if (evlist->threads)
+                       mp->tid = evlist->threads->map[0];
+               else
+                       mp->tid = -1;
+       } else {
+               mp->cpu = -1;
+               mp->tid = evlist->threads->map[idx];
+       }
+}
+
+#define AUXTRACE_INIT_NR_QUEUES        32
+
+static struct auxtrace_queue *auxtrace_alloc_queue_array(unsigned int nr_queues)
+{
+       struct auxtrace_queue *queue_array;
+       unsigned int max_nr_queues, i;
+
+       max_nr_queues = UINT_MAX / sizeof(struct auxtrace_queue);
+       if (nr_queues > max_nr_queues)
+               return NULL;
+
+       queue_array = calloc(nr_queues, sizeof(struct auxtrace_queue));
+       if (!queue_array)
+               return NULL;
+
+       for (i = 0; i < nr_queues; i++) {
+               INIT_LIST_HEAD(&queue_array[i].head);
+               queue_array[i].priv = NULL;
+       }
+
+       return queue_array;
+}
+
+int auxtrace_queues__init(struct auxtrace_queues *queues)
+{
+       queues->nr_queues = AUXTRACE_INIT_NR_QUEUES;
+       queues->queue_array = auxtrace_alloc_queue_array(queues->nr_queues);
+       if (!queues->queue_array)
+               return -ENOMEM;
+       return 0;
+}
+
+static int auxtrace_queues__grow(struct auxtrace_queues *queues,
+                                unsigned int new_nr_queues)
+{
+       unsigned int nr_queues = queues->nr_queues;
+       struct auxtrace_queue *queue_array;
+       unsigned int i;
+
+       if (!nr_queues)
+               nr_queues = AUXTRACE_INIT_NR_QUEUES;
+
+       while (nr_queues && nr_queues < new_nr_queues)
+               nr_queues <<= 1;
+
+       if (nr_queues < queues->nr_queues || nr_queues < new_nr_queues)
+               return -EINVAL;
+
+       queue_array = auxtrace_alloc_queue_array(nr_queues);
+       if (!queue_array)
+               return -ENOMEM;
+
+       for (i = 0; i < queues->nr_queues; i++) {
+               list_splice_tail(&queues->queue_array[i].head,
+                                &queue_array[i].head);
+               queue_array[i].priv = queues->queue_array[i].priv;
+       }
+
+       queues->nr_queues = nr_queues;
+       queues->queue_array = queue_array;
+
+       return 0;
+}
+
+static void *auxtrace_copy_data(u64 size, struct perf_session *session)
+{
+       int fd = perf_data_file__fd(session->file);
+       void *p;
+       ssize_t ret;
+
+       if (size > SSIZE_MAX)
+               return NULL;
+
+       p = malloc(size);
+       if (!p)
+               return NULL;
+
+       ret = readn(fd, p, size);
+       if (ret != (ssize_t)size) {
+               free(p);
+               return NULL;
+       }
+
+       return p;
+}
+
+static int auxtrace_queues__add_buffer(struct auxtrace_queues *queues,
+                                      unsigned int idx,
+                                      struct auxtrace_buffer *buffer)
+{
+       struct auxtrace_queue *queue;
+       int err;
+
+       if (idx >= queues->nr_queues) {
+               err = auxtrace_queues__grow(queues, idx + 1);
+               if (err)
+                       return err;
+       }
+
+       queue = &queues->queue_array[idx];
+
+       if (!queue->set) {
+               queue->set = true;
+               queue->tid = buffer->tid;
+               queue->cpu = buffer->cpu;
+       } else if (buffer->cpu != queue->cpu || buffer->tid != queue->tid) {
+               pr_err("auxtrace queue conflict: cpu %d, tid %d vs cpu %d, tid %d\n",
+                      queue->cpu, queue->tid, buffer->cpu, buffer->tid);
+               return -EINVAL;
+       }
+
+       buffer->buffer_nr = queues->next_buffer_nr++;
+
+       list_add_tail(&buffer->list, &queue->head);
+
+       queues->new_data = true;
+       queues->populated = true;
+
+       return 0;
+}
+
+/* Limit buffers to 32MiB on 32-bit */
+#define BUFFER_LIMIT_FOR_32_BIT (32 * 1024 * 1024)
+
+static int auxtrace_queues__split_buffer(struct auxtrace_queues *queues,
+                                        unsigned int idx,
+                                        struct auxtrace_buffer *buffer)
+{
+       u64 sz = buffer->size;
+       bool consecutive = false;
+       struct auxtrace_buffer *b;
+       int err;
+
+       while (sz > BUFFER_LIMIT_FOR_32_BIT) {
+               b = memdup(buffer, sizeof(struct auxtrace_buffer));
+               if (!b)
+                       return -ENOMEM;
+               b->size = BUFFER_LIMIT_FOR_32_BIT;
+               b->consecutive = consecutive;
+               err = auxtrace_queues__add_buffer(queues, idx, b);
+               if (err) {
+                       auxtrace_buffer__free(b);
+                       return err;
+               }
+               buffer->data_offset += BUFFER_LIMIT_FOR_32_BIT;
+               sz -= BUFFER_LIMIT_FOR_32_BIT;
+               consecutive = true;
+       }
+
+       buffer->size = sz;
+       buffer->consecutive = consecutive;
+
+       return 0;
+}
+
+static int auxtrace_queues__add_event_buffer(struct auxtrace_queues *queues,
+                                            struct perf_session *session,
+                                            unsigned int idx,
+                                            struct auxtrace_buffer *buffer)
+{
+       if (session->one_mmap) {
+               buffer->data = buffer->data_offset - session->one_mmap_offset +
+                              session->one_mmap_addr;
+       } else if (perf_data_file__is_pipe(session->file)) {
+               buffer->data = auxtrace_copy_data(buffer->size, session);
+               if (!buffer->data)
+                       return -ENOMEM;
+               buffer->data_needs_freeing = true;
+       } else if (BITS_PER_LONG == 32 &&
+                  buffer->size > BUFFER_LIMIT_FOR_32_BIT) {
+               int err;
+
+               err = auxtrace_queues__split_buffer(queues, idx, buffer);
+               if (err)
+                       return err;
+       }
+
+       return auxtrace_queues__add_buffer(queues, idx, buffer);
+}
+
+int auxtrace_queues__add_event(struct auxtrace_queues *queues,
+                              struct perf_session *session,
+                              union perf_event *event, off_t data_offset,
+                              struct auxtrace_buffer **buffer_ptr)
+{
+       struct auxtrace_buffer *buffer;
+       unsigned int idx;
+       int err;
+
+       buffer = zalloc(sizeof(struct auxtrace_buffer));
+       if (!buffer)
+               return -ENOMEM;
+
+       buffer->pid = -1;
+       buffer->tid = event->auxtrace.tid;
+       buffer->cpu = event->auxtrace.cpu;
+       buffer->data_offset = data_offset;
+       buffer->offset = event->auxtrace.offset;
+       buffer->reference = event->auxtrace.reference;
+       buffer->size = event->auxtrace.size;
+       idx = event->auxtrace.idx;
+
+       err = auxtrace_queues__add_event_buffer(queues, session, idx, buffer);
+       if (err)
+               goto out_err;
+
+       if (buffer_ptr)
+               *buffer_ptr = buffer;
+
+       return 0;
+
+out_err:
+       auxtrace_buffer__free(buffer);
+       return err;
+}
+
+static int auxtrace_queues__add_indexed_event(struct auxtrace_queues *queues,
+                                             struct perf_session *session,
+                                             off_t file_offset, size_t sz)
+{
+       union perf_event *event;
+       int err;
+       char buf[PERF_SAMPLE_MAX_SIZE];
+
+       err = perf_session__peek_event(session, file_offset, buf,
+                                      PERF_SAMPLE_MAX_SIZE, &event, NULL);
+       if (err)
+               return err;
+
+       if (event->header.type == PERF_RECORD_AUXTRACE) {
+               if (event->header.size < sizeof(struct auxtrace_event) ||
+                   event->header.size != sz) {
+                       err = -EINVAL;
+                       goto out;
+               }
+               file_offset += event->header.size;
+               err = auxtrace_queues__add_event(queues, session, event,
+                                                file_offset, NULL);
+       }
+out:
+       return err;
+}
+
+void auxtrace_queues__free(struct auxtrace_queues *queues)
+{
+       unsigned int i;
+
+       for (i = 0; i < queues->nr_queues; i++) {
+               while (!list_empty(&queues->queue_array[i].head)) {
+                       struct auxtrace_buffer *buffer;
+
+                       buffer = list_entry(queues->queue_array[i].head.next,
+                                           struct auxtrace_buffer, list);
+                       list_del(&buffer->list);
+                       auxtrace_buffer__free(buffer);
+               }
+       }
+
+       zfree(&queues->queue_array);
+       queues->nr_queues = 0;
+}
+
+static void auxtrace_heapify(struct auxtrace_heap_item *heap_array,
+                            unsigned int pos, unsigned int queue_nr,
+                            u64 ordinal)
+{
+       unsigned int parent;
+
+       while (pos) {
+               parent = (pos - 1) >> 1;
+               if (heap_array[parent].ordinal <= ordinal)
+                       break;
+               heap_array[pos] = heap_array[parent];
+               pos = parent;
+       }
+       heap_array[pos].queue_nr = queue_nr;
+       heap_array[pos].ordinal = ordinal;
+}
+
+int auxtrace_heap__add(struct auxtrace_heap *heap, unsigned int queue_nr,
+                      u64 ordinal)
+{
+       struct auxtrace_heap_item *heap_array;
+
+       if (queue_nr >= heap->heap_sz) {
+               unsigned int heap_sz = AUXTRACE_INIT_NR_QUEUES;
+
+               while (heap_sz <= queue_nr)
+                       heap_sz <<= 1;
+               heap_array = realloc(heap->heap_array,
+                                    heap_sz * sizeof(struct auxtrace_heap_item));
+               if (!heap_array)
+                       return -ENOMEM;
+               heap->heap_array = heap_array;
+               heap->heap_sz = heap_sz;
+       }
+
+       auxtrace_heapify(heap->heap_array, heap->heap_cnt++, queue_nr, ordinal);
+
+       return 0;
+}
+
+void auxtrace_heap__free(struct auxtrace_heap *heap)
+{
+       zfree(&heap->heap_array);
+       heap->heap_cnt = 0;
+       heap->heap_sz = 0;
+}
+
+void auxtrace_heap__pop(struct auxtrace_heap *heap)
+{
+       unsigned int pos, last, heap_cnt = heap->heap_cnt;
+       struct auxtrace_heap_item *heap_array;
+
+       if (!heap_cnt)
+               return;
+
+       heap->heap_cnt -= 1;
+
+       heap_array = heap->heap_array;
+
+       pos = 0;
+       while (1) {
+               unsigned int left, right;
+
+               left = (pos << 1) + 1;
+               if (left >= heap_cnt)
+                       break;
+               right = left + 1;
+               if (right >= heap_cnt) {
+                       heap_array[pos] = heap_array[left];
+                       return;
+               }
+               if (heap_array[left].ordinal < heap_array[right].ordinal) {
+                       heap_array[pos] = heap_array[left];
+                       pos = left;
+               } else {
+                       heap_array[pos] = heap_array[right];
+                       pos = right;
+               }
+       }
+
+       last = heap_cnt - 1;
+       auxtrace_heapify(heap_array, pos, heap_array[last].queue_nr,
+                        heap_array[last].ordinal);
+}
+
+size_t auxtrace_record__info_priv_size(struct auxtrace_record *itr)
+{
+       if (itr)
+               return itr->info_priv_size(itr);
+       return 0;
+}
+
+static int auxtrace_not_supported(void)
+{
+       pr_err("AUX area tracing is not supported on this architecture\n");
+       return -EINVAL;
+}
+
+int auxtrace_record__info_fill(struct auxtrace_record *itr,
+                              struct perf_session *session,
+                              struct auxtrace_info_event *auxtrace_info,
+                              size_t priv_size)
+{
+       if (itr)
+               return itr->info_fill(itr, session, auxtrace_info, priv_size);
+       return auxtrace_not_supported();
+}
+
+void auxtrace_record__free(struct auxtrace_record *itr)
+{
+       if (itr)
+               itr->free(itr);
+}
+
+int auxtrace_record__snapshot_start(struct auxtrace_record *itr)
+{
+       if (itr && itr->snapshot_start)
+               return itr->snapshot_start(itr);
+       return 0;
+}
+
+int auxtrace_record__snapshot_finish(struct auxtrace_record *itr)
+{
+       if (itr && itr->snapshot_finish)
+               return itr->snapshot_finish(itr);
+       return 0;
+}
+
+int auxtrace_record__find_snapshot(struct auxtrace_record *itr, int idx,
+                                  struct auxtrace_mmap *mm,
+                                  unsigned char *data, u64 *head, u64 *old)
+{
+       if (itr && itr->find_snapshot)
+               return itr->find_snapshot(itr, idx, mm, data, head, old);
+       return 0;
+}
+
+int auxtrace_record__options(struct auxtrace_record *itr,
+                            struct perf_evlist *evlist,
+                            struct record_opts *opts)
+{
+       if (itr)
+               return itr->recording_options(itr, evlist, opts);
+       return 0;
+}
+
+u64 auxtrace_record__reference(struct auxtrace_record *itr)
+{
+       if (itr)
+               return itr->reference(itr);
+       return 0;
+}
+
+int auxtrace_parse_snapshot_options(struct auxtrace_record *itr,
+                                   struct record_opts *opts, const char *str)
+{
+       if (!str)
+               return 0;
+
+       if (itr)
+               return itr->parse_snapshot_options(itr, opts, str);
+
+       pr_err("No AUX area tracing to snapshot\n");
+       return -EINVAL;
+}
+
+struct auxtrace_record *__weak
+auxtrace_record__init(struct perf_evlist *evlist __maybe_unused, int *err)
+{
+       *err = 0;
+       return NULL;
+}
+
+static int auxtrace_index__alloc(struct list_head *head)
+{
+       struct auxtrace_index *auxtrace_index;
+
+       auxtrace_index = malloc(sizeof(struct auxtrace_index));
+       if (!auxtrace_index)
+               return -ENOMEM;
+
+       auxtrace_index->nr = 0;
+       INIT_LIST_HEAD(&auxtrace_index->list);
+
+       list_add_tail(&auxtrace_index->list, head);
+
+       return 0;
+}
+
+void auxtrace_index__free(struct list_head *head)
+{
+       struct auxtrace_index *auxtrace_index, *n;
+
+       list_for_each_entry_safe(auxtrace_index, n, head, list) {
+               list_del(&auxtrace_index->list);
+               free(auxtrace_index);
+       }
+}
+
+static struct auxtrace_index *auxtrace_index__last(struct list_head *head)
+{
+       struct auxtrace_index *auxtrace_index;
+       int err;
+
+       if (list_empty(head)) {
+               err = auxtrace_index__alloc(head);
+               if (err)
+                       return NULL;
+       }
+
+       auxtrace_index = list_entry(head->prev, struct auxtrace_index, list);
+
+       if (auxtrace_index->nr >= PERF_AUXTRACE_INDEX_ENTRY_COUNT) {
+               err = auxtrace_index__alloc(head);
+               if (err)
+                       return NULL;
+               auxtrace_index = list_entry(head->prev, struct auxtrace_index,
+                                           list);
+       }
+
+       return auxtrace_index;
+}
+
+int auxtrace_index__auxtrace_event(struct list_head *head,
+                                  union perf_event *event, off_t file_offset)
+{
+       struct auxtrace_index *auxtrace_index;
+       size_t nr;
+
+       auxtrace_index = auxtrace_index__last(head);
+       if (!auxtrace_index)
+               return -ENOMEM;
+
+       nr = auxtrace_index->nr;
+       auxtrace_index->entries[nr].file_offset = file_offset;
+       auxtrace_index->entries[nr].sz = event->header.size;
+       auxtrace_index->nr += 1;
+
+       return 0;
+}
+
+static int auxtrace_index__do_write(int fd,
+                                   struct auxtrace_index *auxtrace_index)
+{
+       struct auxtrace_index_entry ent;
+       size_t i;
+
+       for (i = 0; i < auxtrace_index->nr; i++) {
+               ent.file_offset = auxtrace_index->entries[i].file_offset;
+               ent.sz = auxtrace_index->entries[i].sz;
+               if (writen(fd, &ent, sizeof(ent)) != sizeof(ent))
+                       return -errno;
+       }
+       return 0;
+}
+
+int auxtrace_index__write(int fd, struct list_head *head)
+{
+       struct auxtrace_index *auxtrace_index;
+       u64 total = 0;
+       int err;
+
+       list_for_each_entry(auxtrace_index, head, list)
+               total += auxtrace_index->nr;
+
+       if (writen(fd, &total, sizeof(total)) != sizeof(total))
+               return -errno;
+
+       list_for_each_entry(auxtrace_index, head, list) {
+               err = auxtrace_index__do_write(fd, auxtrace_index);
+               if (err)
+                       return err;
+       }
+
+       return 0;
+}
+
+static int auxtrace_index__process_entry(int fd, struct list_head *head,
+                                        bool needs_swap)
+{
+       struct auxtrace_index *auxtrace_index;
+       struct auxtrace_index_entry ent;
+       size_t nr;
+
+       if (readn(fd, &ent, sizeof(ent)) != sizeof(ent))
+               return -1;
+
+       auxtrace_index = auxtrace_index__last(head);
+       if (!auxtrace_index)
+               return -1;
+
+       nr = auxtrace_index->nr;
+       if (needs_swap) {
+               auxtrace_index->entries[nr].file_offset =
+                                               bswap_64(ent.file_offset);
+               auxtrace_index->entries[nr].sz = bswap_64(ent.sz);
+       } else {
+               auxtrace_index->entries[nr].file_offset = ent.file_offset;
+               auxtrace_index->entries[nr].sz = ent.sz;
+       }
+
+       auxtrace_index->nr = nr + 1;
+
+       return 0;
+}
+
+int auxtrace_index__process(int fd, u64 size, struct perf_session *session,
+                           bool needs_swap)
+{
+       struct list_head *head = &session->auxtrace_index;
+       u64 nr;
+
+       if (readn(fd, &nr, sizeof(u64)) != sizeof(u64))
+               return -1;
+
+       if (needs_swap)
+               nr = bswap_64(nr);
+
+       if (sizeof(u64) + nr * sizeof(struct auxtrace_index_entry) > size)
+               return -1;
+
+       while (nr--) {
+               int err;
+
+               err = auxtrace_index__process_entry(fd, head, needs_swap);
+               if (err)
+                       return -1;
+       }
+
+       return 0;
+}
+
+static int auxtrace_queues__process_index_entry(struct auxtrace_queues *queues,
+                                               struct perf_session *session,
+                                               struct auxtrace_index_entry *ent)
+{
+       return auxtrace_queues__add_indexed_event(queues, session,
+                                                 ent->file_offset, ent->sz);
+}
+
+int auxtrace_queues__process_index(struct auxtrace_queues *queues,
+                                  struct perf_session *session)
+{
+       struct auxtrace_index *auxtrace_index;
+       struct auxtrace_index_entry *ent;
+       size_t i;
+       int err;
+
+       list_for_each_entry(auxtrace_index, &session->auxtrace_index, list) {
+               for (i = 0; i < auxtrace_index->nr; i++) {
+                       ent = &auxtrace_index->entries[i];
+                       err = auxtrace_queues__process_index_entry(queues,
+                                                                  session,
+                                                                  ent);
+                       if (err)
+                               return err;
+               }
+       }
+       return 0;
+}
+
+struct auxtrace_buffer *auxtrace_buffer__next(struct auxtrace_queue *queue,
+                                             struct auxtrace_buffer *buffer)
+{
+       if (buffer) {
+               if (list_is_last(&buffer->list, &queue->head))
+                       return NULL;
+               return list_entry(buffer->list.next, struct auxtrace_buffer,
+                                 list);
+       } else {
+               if (list_empty(&queue->head))
+                       return NULL;
+               return list_entry(queue->head.next, struct auxtrace_buffer,
+                                 list);
+       }
+}
+
+void *auxtrace_buffer__get_data(struct auxtrace_buffer *buffer, int fd)
+{
+       size_t adj = buffer->data_offset & (page_size - 1);
+       size_t size = buffer->size + adj;
+       off_t file_offset = buffer->data_offset - adj;
+       void *addr;
+
+       if (buffer->data)
+               return buffer->data;
+
+       addr = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, file_offset);
+       if (addr == MAP_FAILED)
+               return NULL;
+
+       buffer->mmap_addr = addr;
+       buffer->mmap_size = size;
+
+       buffer->data = addr + adj;
+
+       return buffer->data;
+}
+
+void auxtrace_buffer__put_data(struct auxtrace_buffer *buffer)
+{
+       if (!buffer->data || !buffer->mmap_addr)
+               return;
+       munmap(buffer->mmap_addr, buffer->mmap_size);
+       buffer->mmap_addr = NULL;
+       buffer->mmap_size = 0;
+       buffer->data = NULL;
+       buffer->use_data = NULL;
+}
+
+void auxtrace_buffer__drop_data(struct auxtrace_buffer *buffer)
+{
+       auxtrace_buffer__put_data(buffer);
+       if (buffer->data_needs_freeing) {
+               buffer->data_needs_freeing = false;
+               zfree(&buffer->data);
+               buffer->use_data = NULL;
+               buffer->size = 0;
+       }
+}
+
+void auxtrace_buffer__free(struct auxtrace_buffer *buffer)
+{
+       auxtrace_buffer__drop_data(buffer);
+       free(buffer);
+}
+
+void auxtrace_synth_error(struct auxtrace_error_event *auxtrace_error, int type,
+                         int code, int cpu, pid_t pid, pid_t tid, u64 ip,
+                         const char *msg)
+{
+       size_t size;
+
+       memset(auxtrace_error, 0, sizeof(struct auxtrace_error_event));
+
+       auxtrace_error->header.type = PERF_RECORD_AUXTRACE_ERROR;
+       auxtrace_error->type = type;
+       auxtrace_error->code = code;
+       auxtrace_error->cpu = cpu;
+       auxtrace_error->pid = pid;
+       auxtrace_error->tid = tid;
+       auxtrace_error->ip = ip;
+       strlcpy(auxtrace_error->msg, msg, MAX_AUXTRACE_ERROR_MSG);
+
+       size = (void *)auxtrace_error->msg - (void *)auxtrace_error +
+              strlen(auxtrace_error->msg) + 1;
+       auxtrace_error->header.size = PERF_ALIGN(size, sizeof(u64));
+}
+
+int perf_event__synthesize_auxtrace_info(struct auxtrace_record *itr,
+                                        struct perf_tool *tool,
+                                        struct perf_session *session,
+                                        perf_event__handler_t process)
+{
+       union perf_event *ev;
+       size_t priv_size;
+       int err;
+
+       pr_debug2("Synthesizing auxtrace information\n");
+       priv_size = auxtrace_record__info_priv_size(itr);
+       ev = zalloc(sizeof(struct auxtrace_info_event) + priv_size);
+       if (!ev)
+               return -ENOMEM;
+
+       ev->auxtrace_info.header.type = PERF_RECORD_AUXTRACE_INFO;
+       ev->auxtrace_info.header.size = sizeof(struct auxtrace_info_event) +
+                                       priv_size;
+       err = auxtrace_record__info_fill(itr, session, &ev->auxtrace_info,
+                                        priv_size);
+       if (err)
+               goto out_free;
+
+       err = process(tool, ev, NULL, NULL);
+out_free:
+       free(ev);
+       return err;
+}
+
+static bool auxtrace__dont_decode(struct perf_session *session)
+{
+       return !session->itrace_synth_opts ||
+              session->itrace_synth_opts->dont_decode;
+}
+
+int perf_event__process_auxtrace_info(struct perf_tool *tool __maybe_unused,
+                                     union perf_event *event,
+                                     struct perf_session *session __maybe_unused)
+{
+       enum auxtrace_type type = event->auxtrace_info.type;
+
+       if (dump_trace)
+               fprintf(stdout, " type: %u\n", type);
+
+       switch (type) {
+       case PERF_AUXTRACE_UNKNOWN:
+       default:
+               return -EINVAL;
+       }
+}
+
+s64 perf_event__process_auxtrace(struct perf_tool *tool,
+                                union perf_event *event,
+                                struct perf_session *session)
+{
+       s64 err;
+
+       if (dump_trace)
+               fprintf(stdout, " size: %#"PRIx64"  offset: %#"PRIx64"  ref: %#"PRIx64"  idx: %u  tid: %d  cpu: %d\n",
+                       event->auxtrace.size, event->auxtrace.offset,
+                       event->auxtrace.reference, event->auxtrace.idx,
+                       event->auxtrace.tid, event->auxtrace.cpu);
+
+       if (auxtrace__dont_decode(session))
+               return event->auxtrace.size;
+
+       if (!session->auxtrace || event->header.type != PERF_RECORD_AUXTRACE)
+               return -EINVAL;
+
+       err = session->auxtrace->process_auxtrace_event(session, event, tool);
+       if (err < 0)
+               return err;
+
+       return event->auxtrace.size;
+}
+
+#define PERF_ITRACE_DEFAULT_PERIOD_TYPE                PERF_ITRACE_PERIOD_NANOSECS
+#define PERF_ITRACE_DEFAULT_PERIOD             100000
+#define PERF_ITRACE_DEFAULT_CALLCHAIN_SZ       16
+#define PERF_ITRACE_MAX_CALLCHAIN_SZ           1024
+
+void itrace_synth_opts__set_default(struct itrace_synth_opts *synth_opts)
+{
+       synth_opts->instructions = true;
+       synth_opts->branches = true;
+       synth_opts->transactions = true;
+       synth_opts->errors = true;
+       synth_opts->period_type = PERF_ITRACE_DEFAULT_PERIOD_TYPE;
+       synth_opts->period = PERF_ITRACE_DEFAULT_PERIOD;
+       synth_opts->callchain_sz = PERF_ITRACE_DEFAULT_CALLCHAIN_SZ;
+}
+
+/*
+ * Please check tools/perf/Documentation/perf-script.txt for information
+ * about the options parsed here, which is introduced after this cset,
+ * when support in 'perf script' for these options is introduced.
+ */
+int itrace_parse_synth_opts(const struct option *opt, const char *str,
+                           int unset)
+{
+       struct itrace_synth_opts *synth_opts = opt->value;
+       const char *p;
+       char *endptr;
+
+       synth_opts->set = true;
+
+       if (unset) {
+               synth_opts->dont_decode = true;
+               return 0;
+       }
+
+       if (!str) {
+               itrace_synth_opts__set_default(synth_opts);
+               return 0;
+       }
+
+       for (p = str; *p;) {
+               switch (*p++) {
+               case 'i':
+                       synth_opts->instructions = true;
+                       while (*p == ' ' || *p == ',')
+                               p += 1;
+                       if (isdigit(*p)) {
+                               synth_opts->period = strtoull(p, &endptr, 10);
+                               p = endptr;
+                               while (*p == ' ' || *p == ',')
+                                       p += 1;
+                               switch (*p++) {
+                               case 'i':
+                                       synth_opts->period_type =
+                                               PERF_ITRACE_PERIOD_INSTRUCTIONS;
+                                       break;
+                               case 't':
+                                       synth_opts->period_type =
+                                               PERF_ITRACE_PERIOD_TICKS;
+                                       break;
+                               case 'm':
+                                       synth_opts->period *= 1000;
+                                       /* Fall through */
+                               case 'u':
+                                       synth_opts->period *= 1000;
+                                       /* Fall through */
+                               case 'n':
+                                       if (*p++ != 's')
+                                               goto out_err;
+                                       synth_opts->period_type =
+                                               PERF_ITRACE_PERIOD_NANOSECS;
+                                       break;
+                               case '\0':
+                                       goto out;
+                               default:
+                                       goto out_err;
+                               }
+                       }
+                       break;
+               case 'b':
+                       synth_opts->branches = true;
+                       break;
+               case 'x':
+                       synth_opts->transactions = true;
+                       break;
+               case 'e':
+                       synth_opts->errors = true;
+                       break;
+               case 'd':
+                       synth_opts->log = true;
+                       break;
+               case 'c':
+                       synth_opts->branches = true;
+                       synth_opts->calls = true;
+                       break;
+               case 'r':
+                       synth_opts->branches = true;
+                       synth_opts->returns = true;
+                       break;
+               case 'g':
+                       synth_opts->callchain = true;
+                       synth_opts->callchain_sz =
+                                       PERF_ITRACE_DEFAULT_CALLCHAIN_SZ;
+                       while (*p == ' ' || *p == ',')
+                               p += 1;
+                       if (isdigit(*p)) {
+                               unsigned int val;
+
+                               val = strtoul(p, &endptr, 10);
+                               p = endptr;
+                               if (!val || val > PERF_ITRACE_MAX_CALLCHAIN_SZ)
+                                       goto out_err;
+                               synth_opts->callchain_sz = val;
+                       }
+                       break;
+               case ' ':
+               case ',':
+                       break;
+               default:
+                       goto out_err;
+               }
+       }
+out:
+       if (synth_opts->instructions) {
+               if (!synth_opts->period_type)
+                       synth_opts->period_type =
+                                       PERF_ITRACE_DEFAULT_PERIOD_TYPE;
+               if (!synth_opts->period)
+                       synth_opts->period = PERF_ITRACE_DEFAULT_PERIOD;
+       }
+
+       return 0;
+
+out_err:
+       pr_err("Bad Instruction Tracing options '%s'\n", str);
+       return -EINVAL;
+}
+
+static const char * const auxtrace_error_type_name[] = {
+       [PERF_AUXTRACE_ERROR_ITRACE] = "instruction trace",
+};
+
+static const char *auxtrace_error_name(int type)
+{
+       const char *error_type_name = NULL;
+
+       if (type < PERF_AUXTRACE_ERROR_MAX)
+               error_type_name = auxtrace_error_type_name[type];
+       if (!error_type_name)
+               error_type_name = "unknown AUX";
+       return error_type_name;
+}
+
+size_t perf_event__fprintf_auxtrace_error(union perf_event *event, FILE *fp)
+{
+       struct auxtrace_error_event *e = &event->auxtrace_error;
+       int ret;
+
+       ret = fprintf(fp, " %s error type %u",
+                     auxtrace_error_name(e->type), e->type);
+       ret += fprintf(fp, " cpu %d pid %d tid %d ip %#"PRIx64" code %u: %s\n",
+                      e->cpu, e->pid, e->tid, e->ip, e->code, e->msg);
+       return ret;
+}
+
+void perf_session__auxtrace_error_inc(struct perf_session *session,
+                                     union perf_event *event)
+{
+       struct auxtrace_error_event *e = &event->auxtrace_error;
+
+       if (e->type < PERF_AUXTRACE_ERROR_MAX)
+               session->evlist->stats.nr_auxtrace_errors[e->type] += 1;
+}
+
+void events_stats__auxtrace_error_warn(const struct events_stats *stats)
+{
+       int i;
+
+       for (i = 0; i < PERF_AUXTRACE_ERROR_MAX; i++) {
+               if (!stats->nr_auxtrace_errors[i])
+                       continue;
+               ui__warning("%u %s errors\n",
+                           stats->nr_auxtrace_errors[i],
+                           auxtrace_error_name(i));
+       }
+}
+
+int perf_event__process_auxtrace_error(struct perf_tool *tool __maybe_unused,
+                                      union perf_event *event,
+                                      struct perf_session *session)
+{
+       if (auxtrace__dont_decode(session))
+               return 0;
+
+       perf_event__fprintf_auxtrace_error(event, stdout);
+       return 0;
+}
+
+static int __auxtrace_mmap__read(struct auxtrace_mmap *mm,
+                                struct auxtrace_record *itr,
+                                struct perf_tool *tool, process_auxtrace_t fn,
+                                bool snapshot, size_t snapshot_size)
+{
+       u64 head, old = mm->prev, offset, ref;
+       unsigned char *data = mm->base;
+       size_t size, head_off, old_off, len1, len2, padding;
+       union perf_event ev;
+       void *data1, *data2;
+
+       if (snapshot) {
+               head = auxtrace_mmap__read_snapshot_head(mm);
+               if (auxtrace_record__find_snapshot(itr, mm->idx, mm, data,
+                                                  &head, &old))
+                       return -1;
+       } else {
+               head = auxtrace_mmap__read_head(mm);
+       }
+
+       if (old == head)
+               return 0;
+
+       pr_debug3("auxtrace idx %d old %#"PRIx64" head %#"PRIx64" diff %#"PRIx64"\n",
+                 mm->idx, old, head, head - old);
+
+       if (mm->mask) {
+               head_off = head & mm->mask;
+               old_off = old & mm->mask;
+       } else {
+               head_off = head % mm->len;
+               old_off = old % mm->len;
+       }
+
+       if (head_off > old_off)
+               size = head_off - old_off;
+       else
+               size = mm->len - (old_off - head_off);
+
+       if (snapshot && size > snapshot_size)
+               size = snapshot_size;
+
+       ref = auxtrace_record__reference(itr);
+
+       if (head > old || size <= head || mm->mask) {
+               offset = head - size;
+       } else {
+               /*
+                * When the buffer size is not a power of 2, 'head' wraps at the
+                * highest multiple of the buffer size, so we have to subtract
+                * the remainder here.
+                */
+               u64 rem = (0ULL - mm->len) % mm->len;
+
+               offset = head - size - rem;
+       }
+
+       if (size > head_off) {
+               len1 = size - head_off;
+               data1 = &data[mm->len - len1];
+               len2 = head_off;
+               data2 = &data[0];
+       } else {
+               len1 = size;
+               data1 = &data[head_off - len1];
+               len2 = 0;
+               data2 = NULL;
+       }
+
+       /* padding must be written by fn() e.g. record__process_auxtrace() */
+       padding = size & 7;
+       if (padding)
+               padding = 8 - padding;
+
+       memset(&ev, 0, sizeof(ev));
+       ev.auxtrace.header.type = PERF_RECORD_AUXTRACE;
+       ev.auxtrace.header.size = sizeof(ev.auxtrace);
+       ev.auxtrace.size = size + padding;
+       ev.auxtrace.offset = offset;
+       ev.auxtrace.reference = ref;
+       ev.auxtrace.idx = mm->idx;
+       ev.auxtrace.tid = mm->tid;
+       ev.auxtrace.cpu = mm->cpu;
+
+       if (fn(tool, &ev, data1, len1, data2, len2))
+               return -1;
+
+       mm->prev = head;
+
+       if (!snapshot) {
+               auxtrace_mmap__write_tail(mm, head);
+               if (itr->read_finish) {
+                       int err;
+
+                       err = itr->read_finish(itr, mm->idx);
+                       if (err < 0)
+                               return err;
+               }
+       }
+
+       return 1;
+}
+
+int auxtrace_mmap__read(struct auxtrace_mmap *mm, struct auxtrace_record *itr,
+                       struct perf_tool *tool, process_auxtrace_t fn)
+{
+       return __auxtrace_mmap__read(mm, itr, tool, fn, false, 0);
+}
+
+int auxtrace_mmap__read_snapshot(struct auxtrace_mmap *mm,
+                                struct auxtrace_record *itr,
+                                struct perf_tool *tool, process_auxtrace_t fn,
+                                size_t snapshot_size)
+{
+       return __auxtrace_mmap__read(mm, itr, tool, fn, true, snapshot_size);
+}
+
+/**
+ * struct auxtrace_cache - hash table to implement a cache
+ * @hashtable: the hashtable
+ * @sz: hashtable size (number of hlists)
+ * @entry_size: size of an entry
+ * @limit: limit the number of entries to this maximum, when reached the cache
+ *         is dropped and caching begins again with an empty cache
+ * @cnt: current number of entries
+ * @bits: hashtable size (@sz = 2^@bits)
+ */
+struct auxtrace_cache {
+       struct hlist_head *hashtable;
+       size_t sz;
+       size_t entry_size;
+       size_t limit;
+       size_t cnt;
+       unsigned int bits;
+};
+
+struct auxtrace_cache *auxtrace_cache__new(unsigned int bits, size_t entry_size,
+                                          unsigned int limit_percent)
+{
+       struct auxtrace_cache *c;
+       struct hlist_head *ht;
+       size_t sz, i;
+
+       c = zalloc(sizeof(struct auxtrace_cache));
+       if (!c)
+               return NULL;
+
+       sz = 1UL << bits;
+
+       ht = calloc(sz, sizeof(struct hlist_head));
+       if (!ht)
+               goto out_free;
+
+       for (i = 0; i < sz; i++)
+               INIT_HLIST_HEAD(&ht[i]);
+
+       c->hashtable = ht;
+       c->sz = sz;
+       c->entry_size = entry_size;
+       c->limit = (c->sz * limit_percent) / 100;
+       c->bits = bits;
+
+       return c;
+
+out_free:
+       free(c);
+       return NULL;
+}
+
+static void auxtrace_cache__drop(struct auxtrace_cache *c)
+{
+       struct auxtrace_cache_entry *entry;
+       struct hlist_node *tmp;
+       size_t i;
+
+       if (!c)
+               return;
+
+       for (i = 0; i < c->sz; i++) {
+               hlist_for_each_entry_safe(entry, tmp, &c->hashtable[i], hash) {
+                       hlist_del(&entry->hash);
+                       auxtrace_cache__free_entry(c, entry);
+               }
+       }
+
+       c->cnt = 0;
+}
+
+void auxtrace_cache__free(struct auxtrace_cache *c)
+{
+       if (!c)
+               return;
+
+       auxtrace_cache__drop(c);
+       free(c->hashtable);
+       free(c);
+}
+
+void *auxtrace_cache__alloc_entry(struct auxtrace_cache *c)
+{
+       return malloc(c->entry_size);
+}
+
+void auxtrace_cache__free_entry(struct auxtrace_cache *c __maybe_unused,
+                               void *entry)
+{
+       free(entry);
+}
+
+int auxtrace_cache__add(struct auxtrace_cache *c, u32 key,
+                       struct auxtrace_cache_entry *entry)
+{
+       if (c->limit && ++c->cnt > c->limit)
+               auxtrace_cache__drop(c);
+
+       entry->key = key;
+       hlist_add_head(&entry->hash, &c->hashtable[hash_32(key, c->bits)]);
+
+       return 0;
+}
+
+void *auxtrace_cache__lookup(struct auxtrace_cache *c, u32 key)
+{
+       struct auxtrace_cache_entry *entry;
+       struct hlist_head *hlist;
+
+       if (!c)
+               return NULL;
+
+       hlist = &c->hashtable[hash_32(key, c->bits)];
+       hlist_for_each_entry(entry, hlist, hash) {
+               if (entry->key == key)
+                       return entry;
+       }
+
+       return NULL;
+}
diff --git a/tools/perf/util/auxtrace.h b/tools/perf/util/auxtrace.h
new file mode 100644 (file)
index 0000000..a171abb
--- /dev/null
@@ -0,0 +1,643 @@
+/*
+ * auxtrace.h: AUX area trace support
+ * Copyright (c) 2013-2015, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#ifndef __PERF_AUXTRACE_H
+#define __PERF_AUXTRACE_H
+
+#include <sys/types.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <linux/list.h>
+#include <linux/perf_event.h>
+#include <linux/types.h>
+
+#include "../perf.h"
+#include "event.h"
+#include "session.h"
+#include "debug.h"
+
+union perf_event;
+struct perf_session;
+struct perf_evlist;
+struct perf_tool;
+struct option;
+struct record_opts;
+struct auxtrace_info_event;
+struct events_stats;
+
+enum auxtrace_type {
+       PERF_AUXTRACE_UNKNOWN,
+};
+
+enum itrace_period_type {
+       PERF_ITRACE_PERIOD_INSTRUCTIONS,
+       PERF_ITRACE_PERIOD_TICKS,
+       PERF_ITRACE_PERIOD_NANOSECS,
+};
+
+/**
+ * struct itrace_synth_opts - AUX area tracing synthesis options.
+ * @set: indicates whether or not options have been set
+ * @inject: indicates the event (not just the sample) must be fully synthesized
+ *          because 'perf inject' will write it out
+ * @instructions: whether to synthesize 'instructions' events
+ * @branches: whether to synthesize 'branches' events
+ * @transactions: whether to synthesize events for transactions
+ * @errors: whether to synthesize decoder error events
+ * @dont_decode: whether to skip decoding entirely
+ * @log: write a decoding log
+ * @calls: limit branch samples to calls (can be combined with @returns)
+ * @returns: limit branch samples to returns (can be combined with @calls)
+ * @callchain: add callchain to 'instructions' events
+ * @callchain_sz: maximum callchain size
+ * @period: 'instructions' events period
+ * @period_type: 'instructions' events period type
+ */
+struct itrace_synth_opts {
+       bool                    set;
+       bool                    inject;
+       bool                    instructions;
+       bool                    branches;
+       bool                    transactions;
+       bool                    errors;
+       bool                    dont_decode;
+       bool                    log;
+       bool                    calls;
+       bool                    returns;
+       bool                    callchain;
+       unsigned int            callchain_sz;
+       unsigned long long      period;
+       enum itrace_period_type period_type;
+};
+
+/**
+ * struct auxtrace_index_entry - indexes a AUX area tracing event within a
+ *                               perf.data file.
+ * @file_offset: offset within the perf.data file
+ * @sz: size of the event
+ */
+struct auxtrace_index_entry {
+       u64                     file_offset;
+       u64                     sz;
+};
+
+#define PERF_AUXTRACE_INDEX_ENTRY_COUNT 256
+
+/**
+ * struct auxtrace_index - index of AUX area tracing events within a perf.data
+ *                         file.
+ * @list: linking a number of arrays of entries
+ * @nr: number of entries
+ * @entries: array of entries
+ */
+struct auxtrace_index {
+       struct list_head        list;
+       size_t                  nr;
+       struct auxtrace_index_entry entries[PERF_AUXTRACE_INDEX_ENTRY_COUNT];
+};
+
+/**
+ * struct auxtrace - session callbacks to allow AUX area data decoding.
+ * @process_event: lets the decoder see all session events
+ * @flush_events: process any remaining data
+ * @free_events: free resources associated with event processing
+ * @free: free resources associated with the session
+ */
+struct auxtrace {
+       int (*process_event)(struct perf_session *session,
+                            union perf_event *event,
+                            struct perf_sample *sample,
+                            struct perf_tool *tool);
+       int (*process_auxtrace_event)(struct perf_session *session,
+                                     union perf_event *event,
+                                     struct perf_tool *tool);
+       int (*flush_events)(struct perf_session *session,
+                           struct perf_tool *tool);
+       void (*free_events)(struct perf_session *session);
+       void (*free)(struct perf_session *session);
+};
+
+/**
+ * struct auxtrace_buffer - a buffer containing AUX area tracing data.
+ * @list: buffers are queued in a list held by struct auxtrace_queue
+ * @size: size of the buffer in bytes
+ * @pid: in per-thread mode, the pid this buffer is associated with
+ * @tid: in per-thread mode, the tid this buffer is associated with
+ * @cpu: in per-cpu mode, the cpu this buffer is associated with
+ * @data: actual buffer data (can be null if the data has not been loaded)
+ * @data_offset: file offset at which the buffer can be read
+ * @mmap_addr: mmap address at which the buffer can be read
+ * @mmap_size: size of the mmap at @mmap_addr
+ * @data_needs_freeing: @data was malloc'd so free it when it is no longer
+ *                      needed
+ * @consecutive: the original data was split up and this buffer is consecutive
+ *               to the previous buffer
+ * @offset: offset as determined by aux_head / aux_tail members of struct
+ *          perf_event_mmap_page
+ * @reference: an implementation-specific reference determined when the data is
+ *             recorded
+ * @buffer_nr: used to number each buffer
+ * @use_size: implementation actually only uses this number of bytes
+ * @use_data: implementation actually only uses data starting at this address
+ */
+struct auxtrace_buffer {
+       struct list_head        list;
+       size_t                  size;
+       pid_t                   pid;
+       pid_t                   tid;
+       int                     cpu;
+       void                    *data;
+       off_t                   data_offset;
+       void                    *mmap_addr;
+       size_t                  mmap_size;
+       bool                    data_needs_freeing;
+       bool                    consecutive;
+       u64                     offset;
+       u64                     reference;
+       u64                     buffer_nr;
+       size_t                  use_size;
+       void                    *use_data;
+};
+
+/**
+ * struct auxtrace_queue - a queue of AUX area tracing data buffers.
+ * @head: head of buffer list
+ * @tid: in per-thread mode, the tid this queue is associated with
+ * @cpu: in per-cpu mode, the cpu this queue is associated with
+ * @set: %true once this queue has been dedicated to a specific thread or cpu
+ * @priv: implementation-specific data
+ */
+struct auxtrace_queue {
+       struct list_head        head;
+       pid_t                   tid;
+       int                     cpu;
+       bool                    set;
+       void                    *priv;
+};
+
+/**
+ * struct auxtrace_queues - an array of AUX area tracing queues.
+ * @queue_array: array of queues
+ * @nr_queues: number of queues
+ * @new_data: set whenever new data is queued
+ * @populated: queues have been fully populated using the auxtrace_index
+ * @next_buffer_nr: used to number each buffer
+ */
+struct auxtrace_queues {
+       struct auxtrace_queue   *queue_array;
+       unsigned int            nr_queues;
+       bool                    new_data;
+       bool                    populated;
+       u64                     next_buffer_nr;
+};
+
+/**
+ * struct auxtrace_heap_item - element of struct auxtrace_heap.
+ * @queue_nr: queue number
+ * @ordinal: value used for sorting (lowest ordinal is top of the heap) expected
+ *           to be a timestamp
+ */
+struct auxtrace_heap_item {
+       unsigned int            queue_nr;
+       u64                     ordinal;
+};
+
+/**
+ * struct auxtrace_heap - a heap suitable for sorting AUX area tracing queues.
+ * @heap_array: the heap
+ * @heap_cnt: the number of elements in the heap
+ * @heap_sz: maximum number of elements (grows as needed)
+ */
+struct auxtrace_heap {
+       struct auxtrace_heap_item       *heap_array;
+       unsigned int            heap_cnt;
+       unsigned int            heap_sz;
+};
+
+/**
+ * struct auxtrace_mmap - records an mmap of the auxtrace buffer.
+ * @base: address of mapped area
+ * @userpg: pointer to buffer's perf_event_mmap_page
+ * @mask: %0 if @len is not a power of two, otherwise (@len - %1)
+ * @len: size of mapped area
+ * @prev: previous aux_head
+ * @idx: index of this mmap
+ * @tid: tid for a per-thread mmap (also set if there is only 1 tid on a per-cpu
+ *       mmap) otherwise %0
+ * @cpu: cpu number for a per-cpu mmap otherwise %-1
+ */
+struct auxtrace_mmap {
+       void            *base;
+       void            *userpg;
+       size_t          mask;
+       size_t          len;
+       u64             prev;
+       int             idx;
+       pid_t           tid;
+       int             cpu;
+};
+
+/**
+ * struct auxtrace_mmap_params - parameters to set up struct auxtrace_mmap.
+ * @mask: %0 if @len is not a power of two, otherwise (@len - %1)
+ * @offset: file offset of mapped area
+ * @len: size of mapped area
+ * @prot: mmap memory protection
+ * @idx: index of this mmap
+ * @tid: tid for a per-thread mmap (also set if there is only 1 tid on a per-cpu
+ *       mmap) otherwise %0
+ * @cpu: cpu number for a per-cpu mmap otherwise %-1
+ */
+struct auxtrace_mmap_params {
+       size_t          mask;
+       off_t           offset;
+       size_t          len;
+       int             prot;
+       int             idx;
+       pid_t           tid;
+       int             cpu;
+};
+
+/**
+ * struct auxtrace_record - callbacks for recording AUX area data.
+ * @recording_options: validate and process recording options
+ * @info_priv_size: return the size of the private data in auxtrace_info_event
+ * @info_fill: fill-in the private data in auxtrace_info_event
+ * @free: free this auxtrace record structure
+ * @snapshot_start: starting a snapshot
+ * @snapshot_finish: finishing a snapshot
+ * @find_snapshot: find data to snapshot within auxtrace mmap
+ * @parse_snapshot_options: parse snapshot options
+ * @reference: provide a 64-bit reference number for auxtrace_event
+ * @read_finish: called after reading from an auxtrace mmap
+ */
+struct auxtrace_record {
+       int (*recording_options)(struct auxtrace_record *itr,
+                                struct perf_evlist *evlist,
+                                struct record_opts *opts);
+       size_t (*info_priv_size)(struct auxtrace_record *itr);
+       int (*info_fill)(struct auxtrace_record *itr,
+                        struct perf_session *session,
+                        struct auxtrace_info_event *auxtrace_info,
+                        size_t priv_size);
+       void (*free)(struct auxtrace_record *itr);
+       int (*snapshot_start)(struct auxtrace_record *itr);
+       int (*snapshot_finish)(struct auxtrace_record *itr);
+       int (*find_snapshot)(struct auxtrace_record *itr, int idx,
+                            struct auxtrace_mmap *mm, unsigned char *data,
+                            u64 *head, u64 *old);
+       int (*parse_snapshot_options)(struct auxtrace_record *itr,
+                                     struct record_opts *opts,
+                                     const char *str);
+       u64 (*reference)(struct auxtrace_record *itr);
+       int (*read_finish)(struct auxtrace_record *itr, int idx);
+};
+
+#ifdef HAVE_AUXTRACE_SUPPORT
+
+/*
+ * In snapshot mode the mmapped page is read-only which makes using
+ * __sync_val_compare_and_swap() problematic.  However, snapshot mode expects
+ * the buffer is not updated while the snapshot is made (e.g. Intel PT disables
+ * the event) so there is not a race anyway.
+ */
+static inline u64 auxtrace_mmap__read_snapshot_head(struct auxtrace_mmap *mm)
+{
+       struct perf_event_mmap_page *pc = mm->userpg;
+       u64 head = ACCESS_ONCE(pc->aux_head);
+
+       /* Ensure all reads are done after we read the head */
+       rmb();
+       return head;
+}
+
+static inline u64 auxtrace_mmap__read_head(struct auxtrace_mmap *mm)
+{
+       struct perf_event_mmap_page *pc = mm->userpg;
+#if BITS_PER_LONG == 64 || !defined(HAVE_SYNC_COMPARE_AND_SWAP_SUPPORT)
+       u64 head = ACCESS_ONCE(pc->aux_head);
+#else
+       u64 head = __sync_val_compare_and_swap(&pc->aux_head, 0, 0);
+#endif
+
+       /* Ensure all reads are done after we read the head */
+       rmb();
+       return head;
+}
+
+static inline void auxtrace_mmap__write_tail(struct auxtrace_mmap *mm, u64 tail)
+{
+       struct perf_event_mmap_page *pc = mm->userpg;
+#if BITS_PER_LONG != 64 && defined(HAVE_SYNC_COMPARE_AND_SWAP_SUPPORT)
+       u64 old_tail;
+#endif
+
+       /* Ensure all reads are done before we write the tail out */
+       mb();
+#if BITS_PER_LONG == 64 || !defined(HAVE_SYNC_COMPARE_AND_SWAP_SUPPORT)
+       pc->aux_tail = tail;
+#else
+       do {
+               old_tail = __sync_val_compare_and_swap(&pc->aux_tail, 0, 0);
+       } while (!__sync_bool_compare_and_swap(&pc->aux_tail, old_tail, tail));
+#endif
+}
+
+int auxtrace_mmap__mmap(struct auxtrace_mmap *mm,
+                       struct auxtrace_mmap_params *mp,
+                       void *userpg, int fd);
+void auxtrace_mmap__munmap(struct auxtrace_mmap *mm);
+void auxtrace_mmap_params__init(struct auxtrace_mmap_params *mp,
+                               off_t auxtrace_offset,
+                               unsigned int auxtrace_pages,
+                               bool auxtrace_overwrite);
+void auxtrace_mmap_params__set_idx(struct auxtrace_mmap_params *mp,
+                                  struct perf_evlist *evlist, int idx,
+                                  bool per_cpu);
+
+typedef int (*process_auxtrace_t)(struct perf_tool *tool,
+                                 union perf_event *event, void *data1,
+                                 size_t len1, void *data2, size_t len2);
+
+int auxtrace_mmap__read(struct auxtrace_mmap *mm, struct auxtrace_record *itr,
+                       struct perf_tool *tool, process_auxtrace_t fn);
+
+int auxtrace_mmap__read_snapshot(struct auxtrace_mmap *mm,
+                                struct auxtrace_record *itr,
+                                struct perf_tool *tool, process_auxtrace_t fn,
+                                size_t snapshot_size);
+
+int auxtrace_queues__init(struct auxtrace_queues *queues);
+int auxtrace_queues__add_event(struct auxtrace_queues *queues,
+                              struct perf_session *session,
+                              union perf_event *event, off_t data_offset,
+                              struct auxtrace_buffer **buffer_ptr);
+void auxtrace_queues__free(struct auxtrace_queues *queues);
+int auxtrace_queues__process_index(struct auxtrace_queues *queues,
+                                  struct perf_session *session);
+struct auxtrace_buffer *auxtrace_buffer__next(struct auxtrace_queue *queue,
+                                             struct auxtrace_buffer *buffer);
+void *auxtrace_buffer__get_data(struct auxtrace_buffer *buffer, int fd);
+void auxtrace_buffer__put_data(struct auxtrace_buffer *buffer);
+void auxtrace_buffer__drop_data(struct auxtrace_buffer *buffer);
+void auxtrace_buffer__free(struct auxtrace_buffer *buffer);
+
+int auxtrace_heap__add(struct auxtrace_heap *heap, unsigned int queue_nr,
+                      u64 ordinal);
+void auxtrace_heap__pop(struct auxtrace_heap *heap);
+void auxtrace_heap__free(struct auxtrace_heap *heap);
+
+struct auxtrace_cache_entry {
+       struct hlist_node hash;
+       u32 key;
+};
+
+struct auxtrace_cache *auxtrace_cache__new(unsigned int bits, size_t entry_size,
+                                          unsigned int limit_percent);
+void auxtrace_cache__free(struct auxtrace_cache *auxtrace_cache);
+void *auxtrace_cache__alloc_entry(struct auxtrace_cache *c);
+void auxtrace_cache__free_entry(struct auxtrace_cache *c, void *entry);
+int auxtrace_cache__add(struct auxtrace_cache *c, u32 key,
+                       struct auxtrace_cache_entry *entry);
+void *auxtrace_cache__lookup(struct auxtrace_cache *c, u32 key);
+
+struct auxtrace_record *auxtrace_record__init(struct perf_evlist *evlist,
+                                             int *err);
+
+int auxtrace_parse_snapshot_options(struct auxtrace_record *itr,
+                                   struct record_opts *opts,
+                                   const char *str);
+int auxtrace_record__options(struct auxtrace_record *itr,
+                            struct perf_evlist *evlist,
+                            struct record_opts *opts);
+size_t auxtrace_record__info_priv_size(struct auxtrace_record *itr);
+int auxtrace_record__info_fill(struct auxtrace_record *itr,
+                              struct perf_session *session,
+                              struct auxtrace_info_event *auxtrace_info,
+                              size_t priv_size);
+void auxtrace_record__free(struct auxtrace_record *itr);
+int auxtrace_record__snapshot_start(struct auxtrace_record *itr);
+int auxtrace_record__snapshot_finish(struct auxtrace_record *itr);
+int auxtrace_record__find_snapshot(struct auxtrace_record *itr, int idx,
+                                  struct auxtrace_mmap *mm,
+                                  unsigned char *data, u64 *head, u64 *old);
+u64 auxtrace_record__reference(struct auxtrace_record *itr);
+
+int auxtrace_index__auxtrace_event(struct list_head *head, union perf_event *event,
+                                  off_t file_offset);
+int auxtrace_index__write(int fd, struct list_head *head);
+int auxtrace_index__process(int fd, u64 size, struct perf_session *session,
+                           bool needs_swap);
+void auxtrace_index__free(struct list_head *head);
+
+void auxtrace_synth_error(struct auxtrace_error_event *auxtrace_error, int type,
+                         int code, int cpu, pid_t pid, pid_t tid, u64 ip,
+                         const char *msg);
+
+int perf_event__synthesize_auxtrace_info(struct auxtrace_record *itr,
+                                        struct perf_tool *tool,
+                                        struct perf_session *session,
+                                        perf_event__handler_t process);
+int perf_event__process_auxtrace_info(struct perf_tool *tool,
+                                     union perf_event *event,
+                                     struct perf_session *session);
+s64 perf_event__process_auxtrace(struct perf_tool *tool,
+                                union perf_event *event,
+                                struct perf_session *session);
+int perf_event__process_auxtrace_error(struct perf_tool *tool,
+                                      union perf_event *event,
+                                      struct perf_session *session);
+int itrace_parse_synth_opts(const struct option *opt, const char *str,
+                           int unset);
+void itrace_synth_opts__set_default(struct itrace_synth_opts *synth_opts);
+
+size_t perf_event__fprintf_auxtrace_error(union perf_event *event, FILE *fp);
+void perf_session__auxtrace_error_inc(struct perf_session *session,
+                                     union perf_event *event);
+void events_stats__auxtrace_error_warn(const struct events_stats *stats);
+
+static inline int auxtrace__process_event(struct perf_session *session,
+                                         union perf_event *event,
+                                         struct perf_sample *sample,
+                                         struct perf_tool *tool)
+{
+       if (!session->auxtrace)
+               return 0;
+
+       return session->auxtrace->process_event(session, event, sample, tool);
+}
+
+static inline int auxtrace__flush_events(struct perf_session *session,
+                                        struct perf_tool *tool)
+{
+       if (!session->auxtrace)
+               return 0;
+
+       return session->auxtrace->flush_events(session, tool);
+}
+
+static inline void auxtrace__free_events(struct perf_session *session)
+{
+       if (!session->auxtrace)
+               return;
+
+       return session->auxtrace->free_events(session);
+}
+
+static inline void auxtrace__free(struct perf_session *session)
+{
+       if (!session->auxtrace)
+               return;
+
+       return session->auxtrace->free(session);
+}
+
+#else
+
+static inline struct auxtrace_record *
+auxtrace_record__init(struct perf_evlist *evlist __maybe_unused,
+                     int *err __maybe_unused)
+{
+       *err = 0;
+       return NULL;
+}
+
+static inline
+void auxtrace_record__free(struct auxtrace_record *itr __maybe_unused)
+{
+}
+
+static inline int
+perf_event__synthesize_auxtrace_info(struct auxtrace_record *itr __maybe_unused,
+                                    struct perf_tool *tool __maybe_unused,
+                                    struct perf_session *session __maybe_unused,
+                                    perf_event__handler_t process __maybe_unused)
+{
+       return -EINVAL;
+}
+
+static inline
+int auxtrace_record__options(struct auxtrace_record *itr __maybe_unused,
+                            struct perf_evlist *evlist __maybe_unused,
+                            struct record_opts *opts __maybe_unused)
+{
+       return 0;
+}
+
+#define perf_event__process_auxtrace_info              0
+#define perf_event__process_auxtrace                   0
+#define perf_event__process_auxtrace_error             0
+
+static inline
+void perf_session__auxtrace_error_inc(struct perf_session *session
+                                     __maybe_unused,
+                                     union perf_event *event
+                                     __maybe_unused)
+{
+}
+
+static inline
+void events_stats__auxtrace_error_warn(const struct events_stats *stats
+                                      __maybe_unused)
+{
+}
+
+static inline
+int itrace_parse_synth_opts(const struct option *opt __maybe_unused,
+                           const char *str __maybe_unused,
+                           int unset __maybe_unused)
+{
+       pr_err("AUX area tracing not supported\n");
+       return -EINVAL;
+}
+
+static inline
+int auxtrace_parse_snapshot_options(struct auxtrace_record *itr __maybe_unused,
+                                   struct record_opts *opts __maybe_unused,
+                                   const char *str)
+{
+       if (!str)
+               return 0;
+       pr_err("AUX area tracing not supported\n");
+       return -EINVAL;
+}
+
+static inline
+int auxtrace__process_event(struct perf_session *session __maybe_unused,
+                           union perf_event *event __maybe_unused,
+                           struct perf_sample *sample __maybe_unused,
+                           struct perf_tool *tool __maybe_unused)
+{
+       return 0;
+}
+
+static inline
+int auxtrace__flush_events(struct perf_session *session __maybe_unused,
+                          struct perf_tool *tool __maybe_unused)
+{
+       return 0;
+}
+
+static inline
+void auxtrace__free_events(struct perf_session *session __maybe_unused)
+{
+}
+
+static inline
+void auxtrace_cache__free(struct auxtrace_cache *auxtrace_cache __maybe_unused)
+{
+}
+
+static inline
+void auxtrace__free(struct perf_session *session __maybe_unused)
+{
+}
+
+static inline
+int auxtrace_index__write(int fd __maybe_unused,
+                         struct list_head *head __maybe_unused)
+{
+       return -EINVAL;
+}
+
+static inline
+int auxtrace_index__process(int fd __maybe_unused,
+                           u64 size __maybe_unused,
+                           struct perf_session *session __maybe_unused,
+                           bool needs_swap __maybe_unused)
+{
+       return -EINVAL;
+}
+
+static inline
+void auxtrace_index__free(struct list_head *head __maybe_unused)
+{
+}
+
+int auxtrace_mmap__mmap(struct auxtrace_mmap *mm,
+                       struct auxtrace_mmap_params *mp,
+                       void *userpg, int fd);
+void auxtrace_mmap__munmap(struct auxtrace_mmap *mm);
+void auxtrace_mmap_params__init(struct auxtrace_mmap_params *mp,
+                               off_t auxtrace_offset,
+                               unsigned int auxtrace_pages,
+                               bool auxtrace_overwrite);
+void auxtrace_mmap_params__set_idx(struct auxtrace_mmap_params *mp,
+                                  struct perf_evlist *evlist, int idx,
+                                  bool per_cpu);
+
+#endif
+
+#endif
index 61867dff5d5aa6dea4079d060514f1bd57a63630..1f6fc2323ef97d5e9fdea6f70a9028db1d14a37e 100644 (file)
@@ -43,6 +43,7 @@ int build_id__mark_dso_hit(struct perf_tool *tool __maybe_unused,
        if (al.map != NULL)
                al.map->dso->hit = 1;
 
+       thread__put(thread);
        return 0;
 }
 
@@ -59,8 +60,10 @@ static int perf_event__exit_del_thread(struct perf_tool *tool __maybe_unused,
        dump_printf("(%d:%d):(%d:%d)\n", event->fork.pid, event->fork.tid,
                    event->fork.ppid, event->fork.ptid);
 
-       if (thread)
+       if (thread) {
                machine__remove_thread(machine, thread);
+               thread__put(thread);
+       }
 
        return 0;
 }
@@ -159,15 +162,20 @@ static int write_buildid(const char *name, size_t name_len, u8 *build_id,
        return write_padded(fd, name, name_len + 1, len);
 }
 
-static int __dsos__write_buildid_table(struct list_head *head,
-                                      struct machine *machine,
-                                      pid_t pid, u16 misc, int fd)
+static int machine__write_buildid_table(struct machine *machine, int fd)
 {
+       int err = 0;
        char nm[PATH_MAX];
        struct dso *pos;
+       u16 kmisc = PERF_RECORD_MISC_KERNEL,
+           umisc = PERF_RECORD_MISC_USER;
 
-       dsos__for_each_with_build_id(pos, head) {
-               int err;
+       if (!machine__is_host(machine)) {
+               kmisc = PERF_RECORD_MISC_GUEST_KERNEL;
+               umisc = PERF_RECORD_MISC_GUEST_USER;
+       }
+
+       dsos__for_each_with_build_id(pos, &machine->dsos.head) {
                const char *name;
                size_t name_len;
 
@@ -186,32 +194,12 @@ static int __dsos__write_buildid_table(struct list_head *head,
                        name_len = pos->long_name_len + 1;
                }
 
-               err = write_buildid(name, name_len, pos->build_id,
-                                   pid, misc, fd);
+               err = write_buildid(name, name_len, pos->build_id, machine->pid,
+                                   pos->kernel ? kmisc : umisc, fd);
                if (err)
-                       return err;
-       }
-
-       return 0;
-}
-
-static int machine__write_buildid_table(struct machine *machine, int fd)
-{
-       int err;
-       u16 kmisc = PERF_RECORD_MISC_KERNEL,
-           umisc = PERF_RECORD_MISC_USER;
-
-       if (!machine__is_host(machine)) {
-               kmisc = PERF_RECORD_MISC_GUEST_KERNEL;
-               umisc = PERF_RECORD_MISC_GUEST_USER;
+                       break;
        }
 
-       err = __dsos__write_buildid_table(&machine->kernel_dsos.head, machine,
-                                         machine->pid, kmisc, fd);
-       if (err == 0)
-               err = __dsos__write_buildid_table(&machine->user_dsos.head,
-                                                 machine, machine->pid, umisc,
-                                                 fd);
        return err;
 }
 
@@ -244,13 +232,7 @@ static int __dsos__hit_all(struct list_head *head)
 
 static int machine__hit_all_dsos(struct machine *machine)
 {
-       int err;
-
-       err = __dsos__hit_all(&machine->kernel_dsos.head);
-       if (err)
-               return err;
-
-       return __dsos__hit_all(&machine->user_dsos.head);
+       return __dsos__hit_all(&machine->dsos.head);
 }
 
 int dsos__hit_all(struct perf_session *session)
@@ -490,9 +472,7 @@ static int __dsos__cache_build_ids(struct list_head *head,
 
 static int machine__cache_build_ids(struct machine *machine)
 {
-       int ret = __dsos__cache_build_ids(&machine->kernel_dsos.head, machine);
-       ret |= __dsos__cache_build_ids(&machine->user_dsos.head, machine);
-       return ret;
+       return __dsos__cache_build_ids(&machine->dsos.head, machine);
 }
 
 int perf_session__cache_build_ids(struct perf_session *session)
@@ -517,11 +497,7 @@ int perf_session__cache_build_ids(struct perf_session *session)
 
 static bool machine__read_build_ids(struct machine *machine, bool with_hits)
 {
-       bool ret;
-
-       ret  = __dsos__read_build_ids(&machine->kernel_dsos.head, with_hits);
-       ret |= __dsos__read_build_ids(&machine->user_dsos.head, with_hits);
-       return ret;
+       return __dsos__read_build_ids(&machine->dsos.head, with_hits);
 }
 
 bool perf_session__read_build_ids(struct perf_session *session, bool with_hits)
index fbcca21d66ab9b6887f084b19bdff98109201612..c861373aaed33dafd233a7bf5eebefa4dfd9874b 100644 (file)
@@ -30,7 +30,6 @@ extern const char *perf_config_dirname(const char *, const char *);
 
 /* pager.c */
 extern void setup_pager(void);
-extern const char *pager_program;
 extern int pager_in_use(void);
 extern int pager_use_color;
 
index 6033a0a212ca5c255434ae2cf34c7370d785d858..679c2c6d8ade7daeace3d55300049aca7fe68a0b 100644 (file)
@@ -72,6 +72,10 @@ extern struct callchain_param callchain_param;
 struct callchain_list {
        u64                     ip;
        struct map_symbol       ms;
+       struct /* for TUI */ {
+               bool            unfolded;
+               bool            has_children;
+       };
        char                   *srcline;
        struct list_head        list;
 };
index 88f7be3994321f8ef717262909ac6c38853fc691..32e12ecfe9c576767f18a3cb42e6c5dedfc3f048 100644 (file)
@@ -115,23 +115,19 @@ static int add_cgroup(struct perf_evlist *evlist, char *str)
                        goto found;
                n++;
        }
-       if (cgrp->refcnt == 0)
+       if (atomic_read(&cgrp->refcnt) == 0)
                free(cgrp);
 
        return -1;
 found:
-       cgrp->refcnt++;
+       atomic_inc(&cgrp->refcnt);
        counter->cgrp = cgrp;
        return 0;
 }
 
 void close_cgroup(struct cgroup_sel *cgrp)
 {
-       if (!cgrp)
-               return;
-
-       /* XXX: not reentrant */
-       if (--cgrp->refcnt == 0) {
+       if (cgrp && atomic_dec_and_test(&cgrp->refcnt)) {
                close(cgrp->fd);
                zfree(&cgrp->name);
                free(cgrp);
index 89acd6debdc5fc06d28889cb95eb30d7af98642f..b4b8cb42fe5e04b7458fe3b57f764b0bda6c18db 100644 (file)
@@ -1,12 +1,14 @@
 #ifndef __CGROUP_H__
 #define __CGROUP_H__
 
+#include <linux/atomic.h>
+
 struct option;
 
 struct cgroup_sel {
        char *name;
        int fd;
-       int refcnt;
+       atomic_t refcnt;
 };
 
 
index b2bb59df65e10c10cfe6dd1aacb495d4804a6a4f..21b7ff382c3f0dfb2e0bff43074adae3c0b10973 100644 (file)
@@ -2,24 +2,27 @@
 #include "util.h"
 #include <stdlib.h>
 #include <stdio.h>
+#include <linux/atomic.h>
 
 struct comm_str {
        char *str;
        struct rb_node rb_node;
-       int ref;
+       atomic_t refcnt;
 };
 
 /* Should perhaps be moved to struct machine */
 static struct rb_root comm_str_root;
 
-static void comm_str__get(struct comm_str *cs)
+static struct comm_str *comm_str__get(struct comm_str *cs)
 {
-       cs->ref++;
+       if (cs)
+               atomic_inc(&cs->refcnt);
+       return cs;
 }
 
 static void comm_str__put(struct comm_str *cs)
 {
-       if (!--cs->ref) {
+       if (cs && atomic_dec_and_test(&cs->refcnt)) {
                rb_erase(&cs->rb_node, &comm_str_root);
                zfree(&cs->str);
                free(cs);
@@ -40,6 +43,8 @@ static struct comm_str *comm_str__alloc(const char *str)
                return NULL;
        }
 
+       atomic_set(&cs->refcnt, 0);
+
        return cs;
 }
 
index dd17c9a32fbcfcf3c55c8cc33a44349582f4bb89..5bfc1198ab465c1873c7a112eefaa97bf286ee6f 100644 (file)
@@ -14,6 +14,7 @@
 #include <babeltrace/ctf-writer/event.h>
 #include <babeltrace/ctf-writer/event-types.h>
 #include <babeltrace/ctf-writer/event-fields.h>
+#include <babeltrace/ctf-ir/utils.h>
 #include <babeltrace/ctf/events.h>
 #include <traceevent/event-parse.h>
 #include "asm/bug.h"
@@ -38,12 +39,21 @@ struct evsel_priv {
        struct bt_ctf_event_class *event_class;
 };
 
+#define MAX_CPUS       4096
+
+struct ctf_stream {
+       struct bt_ctf_stream *stream;
+       int cpu;
+       u32 count;
+};
+
 struct ctf_writer {
        /* writer primitives */
-       struct bt_ctf_writer            *writer;
-       struct bt_ctf_stream            *stream;
-       struct bt_ctf_stream_class      *stream_class;
-       struct bt_ctf_clock             *clock;
+       struct bt_ctf_writer             *writer;
+       struct ctf_stream               **stream;
+       int                               stream_cnt;
+       struct bt_ctf_stream_class       *stream_class;
+       struct bt_ctf_clock              *clock;
 
        /* data types */
        union {
@@ -65,6 +75,9 @@ struct convert {
 
        u64                     events_size;
        u64                     events_count;
+
+       /* Ordered events configured queue size. */
+       u64                     queue_size;
 };
 
 static int value_set(struct bt_ctf_field_type *type,
@@ -153,6 +166,43 @@ get_tracepoint_field_type(struct ctf_writer *cw, struct format_field *field)
                return cw->data.u32;
 }
 
+static unsigned long long adjust_signedness(unsigned long long value_int, int size)
+{
+       unsigned long long value_mask;
+
+       /*
+        * value_mask = (1 << (size * 8 - 1)) - 1.
+        * Directly set value_mask for code readers.
+        */
+       switch (size) {
+       case 1:
+               value_mask = 0x7fULL;
+               break;
+       case 2:
+               value_mask = 0x7fffULL;
+               break;
+       case 4:
+               value_mask = 0x7fffffffULL;
+               break;
+       case 8:
+               /*
+                * For 64 bit value, return it self. There is no need
+                * to fill high bit.
+                */
+               /* Fall through */
+       default:
+               /* BUG! */
+               return value_int;
+       }
+
+       /* If it is a positive value, don't adjust. */
+       if ((value_int & (~0ULL - value_mask)) == 0)
+               return value_int;
+
+       /* Fill upper part of value_int with 1 to make it a negative long long. */
+       return (value_int & value_mask) | ~value_mask;
+}
+
 static int add_tracepoint_field_value(struct ctf_writer *cw,
                                      struct bt_ctf_event_class *event_class,
                                      struct bt_ctf_event *event,
@@ -164,7 +214,6 @@ static int add_tracepoint_field_value(struct ctf_writer *cw,
        struct bt_ctf_field *field;
        const char *name = fmtf->name;
        void *data = sample->raw_data;
-       unsigned long long value_int;
        unsigned long flags = fmtf->flags;
        unsigned int n_items;
        unsigned int i;
@@ -172,6 +221,7 @@ static int add_tracepoint_field_value(struct ctf_writer *cw,
        unsigned int len;
        int ret;
 
+       name = fmtf->alias;
        offset = fmtf->offset;
        len = fmtf->size;
        if (flags & FIELD_IS_STRING)
@@ -208,11 +258,6 @@ static int add_tracepoint_field_value(struct ctf_writer *cw,
        type = get_tracepoint_field_type(cw, fmtf);
 
        for (i = 0; i < n_items; i++) {
-               if (!(flags & FIELD_IS_STRING))
-                       value_int = pevent_read_number(
-                                       fmtf->event->pevent,
-                                       data + offset + i * len, len);
-
                if (flags & FIELD_IS_ARRAY)
                        field = bt_ctf_field_array_get_field(array_field, i);
                else
@@ -226,12 +271,21 @@ static int add_tracepoint_field_value(struct ctf_writer *cw,
                if (flags & FIELD_IS_STRING)
                        ret = bt_ctf_field_string_set_value(field,
                                        data + offset + i * len);
-               else if (!(flags & FIELD_IS_SIGNED))
-                       ret = bt_ctf_field_unsigned_integer_set_value(
-                                       field, value_int);
-               else
-                       ret = bt_ctf_field_signed_integer_set_value(
-                                       field, value_int);
+               else {
+                       unsigned long long value_int;
+
+                       value_int = pevent_read_number(
+                                       fmtf->event->pevent,
+                                       data + offset + i * len, len);
+
+                       if (!(flags & FIELD_IS_SIGNED))
+                               ret = bt_ctf_field_unsigned_integer_set_value(
+                                               field, value_int);
+                       else
+                               ret = bt_ctf_field_signed_integer_set_value(
+                                               field, adjust_signedness(value_int, len));
+               }
+
                if (ret) {
                        pr_err("failed to set file value %s\n", name);
                        goto err_put_field;
@@ -346,12 +400,6 @@ static int add_generic_values(struct ctf_writer *cw,
                        return -1;
        }
 
-       if (type & PERF_SAMPLE_CPU) {
-               ret = value_set_u32(cw, event, "perf_cpu", sample->cpu);
-               if (ret)
-                       return -1;
-       }
-
        if (type & PERF_SAMPLE_PERIOD) {
                ret = value_set_u64(cw, event, "perf_period", sample->period);
                if (ret)
@@ -381,6 +429,129 @@ static int add_generic_values(struct ctf_writer *cw,
        return 0;
 }
 
+static int ctf_stream__flush(struct ctf_stream *cs)
+{
+       int err = 0;
+
+       if (cs) {
+               err = bt_ctf_stream_flush(cs->stream);
+               if (err)
+                       pr_err("CTF stream %d flush failed\n", cs->cpu);
+
+               pr("Flush stream for cpu %d (%u samples)\n",
+                  cs->cpu, cs->count);
+
+               cs->count = 0;
+       }
+
+       return err;
+}
+
+static struct ctf_stream *ctf_stream__create(struct ctf_writer *cw, int cpu)
+{
+       struct ctf_stream *cs;
+       struct bt_ctf_field *pkt_ctx   = NULL;
+       struct bt_ctf_field *cpu_field = NULL;
+       struct bt_ctf_stream *stream   = NULL;
+       int ret;
+
+       cs = zalloc(sizeof(*cs));
+       if (!cs) {
+               pr_err("Failed to allocate ctf stream\n");
+               return NULL;
+       }
+
+       stream = bt_ctf_writer_create_stream(cw->writer, cw->stream_class);
+       if (!stream) {
+               pr_err("Failed to create CTF stream\n");
+               goto out;
+       }
+
+       pkt_ctx = bt_ctf_stream_get_packet_context(stream);
+       if (!pkt_ctx) {
+               pr_err("Failed to obtain packet context\n");
+               goto out;
+       }
+
+       cpu_field = bt_ctf_field_structure_get_field(pkt_ctx, "cpu_id");
+       bt_ctf_field_put(pkt_ctx);
+       if (!cpu_field) {
+               pr_err("Failed to obtain cpu field\n");
+               goto out;
+       }
+
+       ret = bt_ctf_field_unsigned_integer_set_value(cpu_field, (u32) cpu);
+       if (ret) {
+               pr_err("Failed to update CPU number\n");
+               goto out;
+       }
+
+       bt_ctf_field_put(cpu_field);
+
+       cs->cpu    = cpu;
+       cs->stream = stream;
+       return cs;
+
+out:
+       if (cpu_field)
+               bt_ctf_field_put(cpu_field);
+       if (stream)
+               bt_ctf_stream_put(stream);
+
+       free(cs);
+       return NULL;
+}
+
+static void ctf_stream__delete(struct ctf_stream *cs)
+{
+       if (cs) {
+               bt_ctf_stream_put(cs->stream);
+               free(cs);
+       }
+}
+
+static struct ctf_stream *ctf_stream(struct ctf_writer *cw, int cpu)
+{
+       struct ctf_stream *cs = cw->stream[cpu];
+
+       if (!cs) {
+               cs = ctf_stream__create(cw, cpu);
+               cw->stream[cpu] = cs;
+       }
+
+       return cs;
+}
+
+static int get_sample_cpu(struct ctf_writer *cw, struct perf_sample *sample,
+                         struct perf_evsel *evsel)
+{
+       int cpu = 0;
+
+       if (evsel->attr.sample_type & PERF_SAMPLE_CPU)
+               cpu = sample->cpu;
+
+       if (cpu > cw->stream_cnt) {
+               pr_err("Event was recorded for CPU %d, limit is at %d.\n",
+                       cpu, cw->stream_cnt);
+               cpu = 0;
+       }
+
+       return cpu;
+}
+
+#define STREAM_FLUSH_COUNT 100000
+
+/*
+ * Currently we have no other way to determine the
+ * time for the stream flush other than keep track
+ * of the number of events and check it against
+ * threshold.
+ */
+static bool is_flush_needed(struct ctf_stream *cs)
+{
+       return cs->count >= STREAM_FLUSH_COUNT;
+}
+
 static int process_sample_event(struct perf_tool *tool,
                                union perf_event *_event __maybe_unused,
                                struct perf_sample *sample,
@@ -390,6 +561,7 @@ static int process_sample_event(struct perf_tool *tool,
        struct convert *c = container_of(tool, struct convert, tool);
        struct evsel_priv *priv = evsel->priv;
        struct ctf_writer *cw = &c->writer;
+       struct ctf_stream *cs;
        struct bt_ctf_event_class *event_class;
        struct bt_ctf_event *event;
        int ret;
@@ -424,9 +596,93 @@ static int process_sample_event(struct perf_tool *tool,
                        return -1;
        }
 
-       bt_ctf_stream_append_event(cw->stream, event);
+       cs = ctf_stream(cw, get_sample_cpu(cw, sample, evsel));
+       if (cs) {
+               if (is_flush_needed(cs))
+                       ctf_stream__flush(cs);
+
+               cs->count++;
+               bt_ctf_stream_append_event(cs->stream, event);
+       }
+
        bt_ctf_event_put(event);
-       return 0;
+       return cs ? 0 : -1;
+}
+
+/* If dup < 0, add a prefix. Else, add _dupl_X suffix. */
+static char *change_name(char *name, char *orig_name, int dup)
+{
+       char *new_name = NULL;
+       size_t len;
+
+       if (!name)
+               name = orig_name;
+
+       if (dup >= 10)
+               goto out;
+       /*
+        * Add '_' prefix to potential keywork.  According to
+        * Mathieu Desnoyers (https://lkml.org/lkml/2015/1/23/652),
+        * futher CTF spec updating may require us to use '$'.
+        */
+       if (dup < 0)
+               len = strlen(name) + sizeof("_");
+       else
+               len = strlen(orig_name) + sizeof("_dupl_X");
+
+       new_name = malloc(len);
+       if (!new_name)
+               goto out;
+
+       if (dup < 0)
+               snprintf(new_name, len, "_%s", name);
+       else
+               snprintf(new_name, len, "%s_dupl_%d", orig_name, dup);
+
+out:
+       if (name != orig_name)
+               free(name);
+       return new_name;
+}
+
+static int event_class_add_field(struct bt_ctf_event_class *event_class,
+               struct bt_ctf_field_type *type,
+               struct format_field *field)
+{
+       struct bt_ctf_field_type *t = NULL;
+       char *name;
+       int dup = 1;
+       int ret;
+
+       /* alias was already assigned */
+       if (field->alias != field->name)
+               return bt_ctf_event_class_add_field(event_class, type,
+                               (char *)field->alias);
+
+       name = field->name;
+
+       /* If 'name' is a keywork, add prefix. */
+       if (bt_ctf_validate_identifier(name))
+               name = change_name(name, field->name, -1);
+
+       if (!name) {
+               pr_err("Failed to fix invalid identifier.");
+               return -1;
+       }
+       while ((t = bt_ctf_event_class_get_field_by_name(event_class, name))) {
+               bt_ctf_field_type_put(t);
+               name = change_name(name, field->name, dup++);
+               if (!name) {
+                       pr_err("Failed to create dup name for '%s'\n", field->name);
+                       return -1;
+               }
+       }
+
+       ret = bt_ctf_event_class_add_field(event_class, type, name);
+       if (!ret)
+               field->alias = name;
+
+       return ret;
 }
 
 static int add_tracepoint_fields_types(struct ctf_writer *cw,
@@ -457,14 +713,14 @@ static int add_tracepoint_fields_types(struct ctf_writer *cw,
                if (flags & FIELD_IS_ARRAY)
                        type = bt_ctf_field_type_array_create(type, field->arraylen);
 
-               ret = bt_ctf_event_class_add_field(event_class, type,
-                               field->name);
+               ret = event_class_add_field(event_class, type, field);
 
                if (flags & FIELD_IS_ARRAY)
                        bt_ctf_field_type_put(type);
 
                if (ret) {
-                       pr_err("Failed to add field '%s\n", field->name);
+                       pr_err("Failed to add field '%s': %d\n",
+                                       field->name, ret);
                        return -1;
                }
        }
@@ -508,7 +764,7 @@ static int add_generic_types(struct ctf_writer *cw, struct perf_evsel *evsel,
        do {                                                            \
                pr2("  field '%s'\n", n);                               \
                if (bt_ctf_event_class_add_field(cl, t, n)) {           \
-                       pr_err("Failed to add field '%s;\n", n);        \
+                       pr_err("Failed to add field '%s';\n", n);       \
                        return -1;                                      \
                }                                                       \
        } while (0)
@@ -528,9 +784,6 @@ static int add_generic_types(struct ctf_writer *cw, struct perf_evsel *evsel,
        if (type & PERF_SAMPLE_STREAM_ID)
                ADD_FIELD(event_class, cw->data.u64, "perf_stream_id");
 
-       if (type & PERF_SAMPLE_CPU)
-               ADD_FIELD(event_class, cw->data.u32, "perf_cpu");
-
        if (type & PERF_SAMPLE_PERIOD)
                ADD_FIELD(event_class, cw->data.u64, "perf_period");
 
@@ -604,6 +857,39 @@ static int setup_events(struct ctf_writer *cw, struct perf_session *session)
        return 0;
 }
 
+static int setup_streams(struct ctf_writer *cw, struct perf_session *session)
+{
+       struct ctf_stream **stream;
+       struct perf_header *ph = &session->header;
+       int ncpus;
+
+       /*
+        * Try to get the number of cpus used in the data file,
+        * if not present fallback to the MAX_CPUS.
+        */
+       ncpus = ph->env.nr_cpus_avail ?: MAX_CPUS;
+
+       stream = zalloc(sizeof(*stream) * ncpus);
+       if (!stream) {
+               pr_err("Failed to allocate streams.\n");
+               return -ENOMEM;
+       }
+
+       cw->stream     = stream;
+       cw->stream_cnt = ncpus;
+       return 0;
+}
+
+static void free_streams(struct ctf_writer *cw)
+{
+       int cpu;
+
+       for (cpu = 0; cpu < cw->stream_cnt; cpu++)
+               ctf_stream__delete(cw->stream[cpu]);
+
+       free(cw->stream);
+}
+
 static int ctf_writer__setup_env(struct ctf_writer *cw,
                                 struct perf_session *session)
 {
@@ -713,7 +999,7 @@ static void ctf_writer__cleanup(struct ctf_writer *cw)
        ctf_writer__cleanup_data(cw);
 
        bt_ctf_clock_put(cw->clock);
-       bt_ctf_stream_put(cw->stream);
+       free_streams(cw);
        bt_ctf_stream_class_put(cw->stream_class);
        bt_ctf_writer_put(cw->writer);
 
@@ -725,8 +1011,9 @@ static int ctf_writer__init(struct ctf_writer *cw, const char *path)
 {
        struct bt_ctf_writer            *writer;
        struct bt_ctf_stream_class      *stream_class;
-       struct bt_ctf_stream            *stream;
        struct bt_ctf_clock             *clock;
+       struct bt_ctf_field_type        *pkt_ctx_type;
+       int                             ret;
 
        /* CTF writer */
        writer = bt_ctf_writer_create(path);
@@ -767,14 +1054,15 @@ static int ctf_writer__init(struct ctf_writer *cw, const char *path)
        if (ctf_writer__init_data(cw))
                goto err_cleanup;
 
-       /* CTF stream instance */
-       stream = bt_ctf_writer_create_stream(writer, stream_class);
-       if (!stream) {
-               pr("Failed to create CTF stream.\n");
+       /* Add cpu_id for packet context */
+       pkt_ctx_type = bt_ctf_stream_class_get_packet_context_type(stream_class);
+       if (!pkt_ctx_type)
                goto err_cleanup;
-       }
 
-       cw->stream = stream;
+       ret = bt_ctf_field_type_structure_add_field(pkt_ctx_type, cw->data.u32, "cpu_id");
+       bt_ctf_field_type_put(pkt_ctx_type);
+       if (ret)
+               goto err_cleanup;
 
        /* CTF clock writer setup */
        if (bt_ctf_writer_add_clock(writer, clock)) {
@@ -791,6 +1079,28 @@ err:
        return -1;
 }
 
+static int ctf_writer__flush_streams(struct ctf_writer *cw)
+{
+       int cpu, ret = 0;
+
+       for (cpu = 0; cpu < cw->stream_cnt && !ret; cpu++)
+               ret = ctf_stream__flush(cw->stream[cpu]);
+
+       return ret;
+}
+
+static int convert__config(const char *var, const char *value, void *cb)
+{
+       struct convert *c = cb;
+
+       if (!strcmp(var, "convert.queue-size")) {
+               c->queue_size = perf_config_u64(var, value);
+               return 0;
+       }
+
+       return perf_default_config(var, value, cb);
+}
+
 int bt_convert__perf2ctf(const char *input, const char *path, bool force)
 {
        struct perf_session *session;
@@ -817,6 +1127,8 @@ int bt_convert__perf2ctf(const char *input, const char *path, bool force)
        struct ctf_writer *cw = &c.writer;
        int err = -1;
 
+       perf_config(convert__config, &c);
+
        /* CTF writer */
        if (ctf_writer__init(cw, path))
                return -1;
@@ -826,6 +1138,11 @@ int bt_convert__perf2ctf(const char *input, const char *path, bool force)
        if (!session)
                goto free_writer;
 
+       if (c.queue_size) {
+               ordered_events__set_alloc_size(&session->ordered_events,
+                                              c.queue_size);
+       }
+
        /* CTF writer env/clock setup  */
        if (ctf_writer__setup_env(cw, session))
                goto free_session;
@@ -834,9 +1151,14 @@ int bt_convert__perf2ctf(const char *input, const char *path, bool force)
        if (setup_events(cw, session))
                goto free_session;
 
+       if (setup_streams(cw, session))
+               goto free_session;
+
        err = perf_session__process_events(session);
        if (!err)
-               err = bt_ctf_stream_flush(cw->stream);
+               err = ctf_writer__flush_streams(cw);
+       else
+               pr_err("Error during conversion.\n");
 
        fprintf(stderr,
                "[ perf data convert: Converted '%s' into CTF data '%s' ]\n",
@@ -847,11 +1169,15 @@ int bt_convert__perf2ctf(const char *input, const char *path, bool force)
                (double) c.events_size / 1024.0 / 1024.0,
                c.events_count);
 
-       /* its all good */
-free_session:
        perf_session__delete(session);
+       ctf_writer__cleanup(cw);
+
+       return err;
 
+free_session:
+       perf_session__delete(session);
 free_writer:
        ctf_writer__cleanup(cw);
+       pr_err("Error during conversion setup.\n");
        return err;
 }
index bb39a3ffc70b3951f88f9260f01836cc4e959fc9..1c9689e4cc179a3e931e1b3e0b427accb7553cba 100644 (file)
@@ -122,6 +122,7 @@ int db_export__machine(struct db_export *dbe, struct machine *machine)
 int db_export__thread(struct db_export *dbe, struct thread *thread,
                      struct machine *machine, struct comm *comm)
 {
+       struct thread *main_thread;
        u64 main_thread_db_id = 0;
        int err;
 
@@ -131,8 +132,6 @@ int db_export__thread(struct db_export *dbe, struct thread *thread,
        thread->db_id = ++dbe->thread_last_db_id;
 
        if (thread->pid_ != -1) {
-               struct thread *main_thread;
-
                if (thread->pid_ == thread->tid) {
                        main_thread = thread;
                } else {
@@ -144,14 +143,16 @@ int db_export__thread(struct db_export *dbe, struct thread *thread,
                        err = db_export__thread(dbe, main_thread, machine,
                                                comm);
                        if (err)
-                               return err;
+                               goto out_put;
                        if (comm) {
                                err = db_export__comm_thread(dbe, comm, thread);
                                if (err)
-                                       return err;
+                                       goto out_put;
                        }
                }
                main_thread_db_id = main_thread->db_id;
+               if (main_thread != thread)
+                       thread__put(main_thread);
        }
 
        if (dbe->export_thread)
@@ -159,6 +160,10 @@ int db_export__thread(struct db_export *dbe, struct thread *thread,
                                          machine);
 
        return 0;
+
+out_put:
+       thread__put(main_thread);
+       return err;
 }
 
 int db_export__comm(struct db_export *dbe, struct comm *comm,
@@ -229,7 +234,7 @@ int db_export__symbol(struct db_export *dbe, struct symbol *sym,
 static struct thread *get_main_thread(struct machine *machine, struct thread *thread)
 {
        if (thread->pid_ == thread->tid)
-               return thread;
+               return thread__get(thread);
 
        if (thread->pid_ == -1)
                return NULL;
@@ -309,12 +314,12 @@ int db_export__sample(struct db_export *dbe, union perf_event *event,
 
        err = db_export__thread(dbe, thread, al->machine, comm);
        if (err)
-               return err;
+               goto out_put;
 
        if (comm) {
                err = db_export__comm(dbe, comm, main_thread);
                if (err)
-                       return err;
+                       goto out_put;
                es.comm_db_id = comm->db_id;
        }
 
@@ -322,7 +327,7 @@ int db_export__sample(struct db_export *dbe, union perf_event *event,
 
        err = db_ids_from_al(dbe, al, &es.dso_db_id, &es.sym_db_id, &es.offset);
        if (err)
-               return err;
+               goto out_put;
 
        if ((evsel->attr.sample_type & PERF_SAMPLE_ADDR) &&
            sample_addr_correlates_sym(&evsel->attr)) {
@@ -332,20 +337,22 @@ int db_export__sample(struct db_export *dbe, union perf_event *event,
                err = db_ids_from_al(dbe, &addr_al, &es.addr_dso_db_id,
                                     &es.addr_sym_db_id, &es.addr_offset);
                if (err)
-                       return err;
+                       goto out_put;
                if (dbe->crp) {
                        err = thread_stack__process(thread, comm, sample, al,
                                                    &addr_al, es.db_id,
                                                    dbe->crp);
                        if (err)
-                               return err;
+                               goto out_put;
                }
        }
 
        if (dbe->export_sample)
-               return dbe->export_sample(dbe, &es);
+               err = dbe->export_sample(dbe, &es);
 
-       return 0;
+out_put:
+       thread__put(main_thread);
+       return err;
 }
 
 static struct {
index fc0ddd5792a97f884e7ed142ad12fe87c2bfe408..7c0c08386a1d9d6fb5e8cba6c838e5c3c1949bd7 100644 (file)
@@ -4,6 +4,7 @@
 #include "symbol.h"
 #include "dso.h"
 #include "machine.h"
+#include "auxtrace.h"
 #include "util.h"
 #include "debug.h"
 
@@ -165,12 +166,28 @@ bool is_supported_compression(const char *ext)
        return false;
 }
 
-bool is_kernel_module(const char *pathname)
+bool is_kernel_module(const char *pathname, int cpumode)
 {
        struct kmod_path m;
-
-       if (kmod_path__parse(&m, pathname))
-               return NULL;
+       int mode = cpumode & PERF_RECORD_MISC_CPUMODE_MASK;
+
+       WARN_ONCE(mode != cpumode,
+                 "Internal error: passing unmasked cpumode (%x) to is_kernel_module",
+                 cpumode);
+
+       switch (mode) {
+       case PERF_RECORD_MISC_USER:
+       case PERF_RECORD_MISC_HYPERVISOR:
+       case PERF_RECORD_MISC_GUEST_USER:
+               return false;
+       /* Treat PERF_RECORD_MISC_CPUMODE_UNKNOWN as kernel */
+       default:
+               if (kmod_path__parse(&m, pathname)) {
+                       pr_err("Failed to check whether %s is a kernel module or not. Assume it is.",
+                                       pathname);
+                       return true;
+               }
+       }
 
        return m.kmod;
 }
@@ -214,12 +231,33 @@ int __kmod_path__parse(struct kmod_path *m, const char *path,
 {
        const char *name = strrchr(path, '/');
        const char *ext  = strrchr(path, '.');
+       bool is_simple_name = false;
 
        memset(m, 0x0, sizeof(*m));
        name = name ? name + 1 : path;
 
+       /*
+        * '.' is also a valid character for module name. For example:
+        * [aaa.bbb] is a valid module name. '[' should have higher
+        * priority than '.ko' suffix.
+        *
+        * The kernel names are from machine__mmap_name. Such
+        * name should belong to kernel itself, not kernel module.
+        */
+       if (name[0] == '[') {
+               is_simple_name = true;
+               if ((strncmp(name, "[kernel.kallsyms]", 17) == 0) ||
+                   (strncmp(name, "[guest.kernel.kallsyms", 22) == 0) ||
+                   (strncmp(name, "[vdso]", 6) == 0) ||
+                   (strncmp(name, "[vsyscall]", 10) == 0)) {
+                       m->kmod = false;
+
+               } else
+                       m->kmod = true;
+       }
+
        /* No extension, just return name. */
-       if (ext == NULL) {
+       if ((ext == NULL) || is_simple_name) {
                if (alloc_name) {
                        m->name = strdup(name);
                        return m->name ? 0 : -ENOMEM;
@@ -264,6 +302,7 @@ int __kmod_path__parse(struct kmod_path *m, const char *path,
  */
 static LIST_HEAD(dso__data_open);
 static long dso__data_open_cnt;
+static pthread_mutex_t dso__data_open_lock = PTHREAD_MUTEX_INITIALIZER;
 
 static void dso__list_add(struct dso *dso)
 {
@@ -433,18 +472,12 @@ static void check_data_close(void)
  */
 void dso__data_close(struct dso *dso)
 {
+       pthread_mutex_lock(&dso__data_open_lock);
        close_dso(dso);
+       pthread_mutex_unlock(&dso__data_open_lock);
 }
 
-/**
- * dso__data_fd - Get dso's data file descriptor
- * @dso: dso object
- * @machine: machine object
- *
- * External interface to find dso's file, open it and
- * returns file descriptor.
- */
-int dso__data_fd(struct dso *dso, struct machine *machine)
+static void try_to_open_dso(struct dso *dso, struct machine *machine)
 {
        enum dso_binary_type binary_type_data[] = {
                DSO_BINARY_TYPE__BUILD_ID_CACHE,
@@ -453,11 +486,8 @@ int dso__data_fd(struct dso *dso, struct machine *machine)
        };
        int i = 0;
 
-       if (dso->data.status == DSO_DATA_STATUS_ERROR)
-               return -1;
-
        if (dso->data.fd >= 0)
-               goto out;
+               return;
 
        if (dso->binary_type != DSO_BINARY_TYPE__NOT_FOUND) {
                dso->data.fd = open_dso(dso, machine);
@@ -477,10 +507,38 @@ out:
                dso->data.status = DSO_DATA_STATUS_OK;
        else
                dso->data.status = DSO_DATA_STATUS_ERROR;
+}
+
+/**
+ * dso__data_get_fd - Get dso's data file descriptor
+ * @dso: dso object
+ * @machine: machine object
+ *
+ * External interface to find dso's file, open it and
+ * returns file descriptor.  It should be paired with
+ * dso__data_put_fd() if it returns non-negative value.
+ */
+int dso__data_get_fd(struct dso *dso, struct machine *machine)
+{
+       if (dso->data.status == DSO_DATA_STATUS_ERROR)
+               return -1;
+
+       if (pthread_mutex_lock(&dso__data_open_lock) < 0)
+               return -1;
+
+       try_to_open_dso(dso, machine);
+
+       if (dso->data.fd < 0)
+               pthread_mutex_unlock(&dso__data_open_lock);
 
        return dso->data.fd;
 }
 
+void dso__data_put_fd(struct dso *dso __maybe_unused)
+{
+       pthread_mutex_unlock(&dso__data_open_lock);
+}
+
 bool dso__data_status_seen(struct dso *dso, enum dso_data_status_seen by)
 {
        u32 flag = 1 << by;
@@ -494,10 +552,12 @@ bool dso__data_status_seen(struct dso *dso, enum dso_data_status_seen by)
 }
 
 static void
-dso_cache__free(struct rb_root *root)
+dso_cache__free(struct dso *dso)
 {
+       struct rb_root *root = &dso->data.cache;
        struct rb_node *next = rb_first(root);
 
+       pthread_mutex_lock(&dso->lock);
        while (next) {
                struct dso_cache *cache;
 
@@ -506,10 +566,12 @@ dso_cache__free(struct rb_root *root)
                rb_erase(&cache->rb_node, root);
                free(cache);
        }
+       pthread_mutex_unlock(&dso->lock);
 }
 
-static struct dso_cache *dso_cache__find(const struct rb_root *root, u64 offset)
+static struct dso_cache *dso_cache__find(struct dso *dso, u64 offset)
 {
+       const struct rb_root *root = &dso->data.cache;
        struct rb_node * const *p = &root->rb_node;
        const struct rb_node *parent = NULL;
        struct dso_cache *cache;
@@ -528,17 +590,20 @@ static struct dso_cache *dso_cache__find(const struct rb_root *root, u64 offset)
                else
                        return cache;
        }
+
        return NULL;
 }
 
-static void
-dso_cache__insert(struct rb_root *root, struct dso_cache *new)
+static struct dso_cache *
+dso_cache__insert(struct dso *dso, struct dso_cache *new)
 {
+       struct rb_root *root = &dso->data.cache;
        struct rb_node **p = &root->rb_node;
        struct rb_node *parent = NULL;
        struct dso_cache *cache;
        u64 offset = new->offset;
 
+       pthread_mutex_lock(&dso->lock);
        while (*p != NULL) {
                u64 end;
 
@@ -550,10 +615,17 @@ dso_cache__insert(struct rb_root *root, struct dso_cache *new)
                        p = &(*p)->rb_left;
                else if (offset >= end)
                        p = &(*p)->rb_right;
+               else
+                       goto out;
        }
 
        rb_link_node(&new->rb_node, parent, p);
        rb_insert_color(&new->rb_node, root);
+
+       cache = NULL;
+out:
+       pthread_mutex_unlock(&dso->lock);
+       return cache;
 }
 
 static ssize_t
@@ -568,19 +640,33 @@ dso_cache__memcpy(struct dso_cache *cache, u64 offset,
 }
 
 static ssize_t
-dso_cache__read(struct dso *dso, u64 offset, u8 *data, ssize_t size)
+dso_cache__read(struct dso *dso, struct machine *machine,
+               u64 offset, u8 *data, ssize_t size)
 {
        struct dso_cache *cache;
+       struct dso_cache *old;
        ssize_t ret;
 
        do {
                u64 cache_offset;
 
-               ret = -ENOMEM;
-
                cache = zalloc(sizeof(*cache) + DSO__DATA_CACHE_SIZE);
                if (!cache)
+                       return -ENOMEM;
+
+               pthread_mutex_lock(&dso__data_open_lock);
+
+               /*
+                * dso->data.fd might be closed if other thread opened another
+                * file (dso) due to open file limit (RLIMIT_NOFILE).
+                */
+               try_to_open_dso(dso, machine);
+
+               if (dso->data.fd < 0) {
+                       ret = -errno;
+                       dso->data.status = DSO_DATA_STATUS_ERROR;
                        break;
+               }
 
                cache_offset = offset & DSO__DATA_CACHE_MASK;
 
@@ -590,11 +676,20 @@ dso_cache__read(struct dso *dso, u64 offset, u8 *data, ssize_t size)
 
                cache->offset = cache_offset;
                cache->size   = ret;
-               dso_cache__insert(&dso->data.cache, cache);
+       } while (0);
 
-               ret = dso_cache__memcpy(cache, offset, data, size);
+       pthread_mutex_unlock(&dso__data_open_lock);
 
-       } while (0);
+       if (ret > 0) {
+               old = dso_cache__insert(dso, cache);
+               if (old) {
+                       /* we lose the race */
+                       free(cache);
+                       cache = old;
+               }
+
+               ret = dso_cache__memcpy(cache, offset, data, size);
+       }
 
        if (ret <= 0)
                free(cache);
@@ -602,16 +697,16 @@ dso_cache__read(struct dso *dso, u64 offset, u8 *data, ssize_t size)
        return ret;
 }
 
-static ssize_t dso_cache_read(struct dso *dso, u64 offset,
-                             u8 *data, ssize_t size)
+static ssize_t dso_cache_read(struct dso *dso, struct machine *machine,
+                             u64 offset, u8 *data, ssize_t size)
 {
        struct dso_cache *cache;
 
-       cache = dso_cache__find(&dso->data.cache, offset);
+       cache = dso_cache__find(dso, offset);
        if (cache)
                return dso_cache__memcpy(cache, offset, data, size);
        else
-               return dso_cache__read(dso, offset, data, size);
+               return dso_cache__read(dso, machine, offset, data, size);
 }
 
 /*
@@ -619,7 +714,8 @@ static ssize_t dso_cache_read(struct dso *dso, u64 offset,
  * in the rb_tree. Any read to already cached data is served
  * by cached data.
  */
-static ssize_t cached_read(struct dso *dso, u64 offset, u8 *data, ssize_t size)
+static ssize_t cached_read(struct dso *dso, struct machine *machine,
+                          u64 offset, u8 *data, ssize_t size)
 {
        ssize_t r = 0;
        u8 *p = data;
@@ -627,7 +723,7 @@ static ssize_t cached_read(struct dso *dso, u64 offset, u8 *data, ssize_t size)
        do {
                ssize_t ret;
 
-               ret = dso_cache_read(dso, offset, p, size);
+               ret = dso_cache_read(dso, machine, offset, p, size);
                if (ret < 0)
                        return ret;
 
@@ -647,21 +743,44 @@ static ssize_t cached_read(struct dso *dso, u64 offset, u8 *data, ssize_t size)
        return r;
 }
 
-static int data_file_size(struct dso *dso)
+static int data_file_size(struct dso *dso, struct machine *machine)
 {
+       int ret = 0;
        struct stat st;
        char sbuf[STRERR_BUFSIZE];
 
-       if (!dso->data.file_size) {
-               if (fstat(dso->data.fd, &st)) {
-                       pr_err("dso mmap failed, fstat: %s\n",
-                               strerror_r(errno, sbuf, sizeof(sbuf)));
-                       return -1;
-               }
-               dso->data.file_size = st.st_size;
+       if (dso->data.file_size)
+               return 0;
+
+       if (dso->data.status == DSO_DATA_STATUS_ERROR)
+               return -1;
+
+       pthread_mutex_lock(&dso__data_open_lock);
+
+       /*
+        * dso->data.fd might be closed if other thread opened another
+        * file (dso) due to open file limit (RLIMIT_NOFILE).
+        */
+       try_to_open_dso(dso, machine);
+
+       if (dso->data.fd < 0) {
+               ret = -errno;
+               dso->data.status = DSO_DATA_STATUS_ERROR;
+               goto out;
        }
 
-       return 0;
+       if (fstat(dso->data.fd, &st) < 0) {
+               ret = -errno;
+               pr_err("dso cache fstat failed: %s\n",
+                      strerror_r(errno, sbuf, sizeof(sbuf)));
+               dso->data.status = DSO_DATA_STATUS_ERROR;
+               goto out;
+       }
+       dso->data.file_size = st.st_size;
+
+out:
+       pthread_mutex_unlock(&dso__data_open_lock);
+       return ret;
 }
 
 /**
@@ -673,23 +792,17 @@ static int data_file_size(struct dso *dso)
  */
 off_t dso__data_size(struct dso *dso, struct machine *machine)
 {
-       int fd;
-
-       fd = dso__data_fd(dso, machine);
-       if (fd < 0)
-               return fd;
-
-       if (data_file_size(dso))
+       if (data_file_size(dso, machine))
                return -1;
 
        /* For now just estimate dso data size is close to file size */
        return dso->data.file_size;
 }
 
-static ssize_t data_read_offset(struct dso *dso, u64 offset,
-                               u8 *data, ssize_t size)
+static ssize_t data_read_offset(struct dso *dso, struct machine *machine,
+                               u64 offset, u8 *data, ssize_t size)
 {
-       if (data_file_size(dso))
+       if (data_file_size(dso, machine))
                return -1;
 
        /* Check the offset sanity. */
@@ -699,7 +812,7 @@ static ssize_t data_read_offset(struct dso *dso, u64 offset,
        if (offset + size < offset)
                return -1;
 
-       return cached_read(dso, offset, data, size);
+       return cached_read(dso, machine, offset, data, size);
 }
 
 /**
@@ -716,10 +829,10 @@ static ssize_t data_read_offset(struct dso *dso, u64 offset,
 ssize_t dso__data_read_offset(struct dso *dso, struct machine *machine,
                              u64 offset, u8 *data, ssize_t size)
 {
-       if (dso__data_fd(dso, machine) < 0)
+       if (dso->data.status == DSO_DATA_STATUS_ERROR)
                return -1;
 
-       return data_read_offset(dso, offset, data, size);
+       return data_read_offset(dso, machine, offset, data, size);
 }
 
 /**
@@ -751,13 +864,13 @@ struct map *dso__new_map(const char *name)
        return map;
 }
 
-struct dso *dso__kernel_findnew(struct machine *machine, const char *name,
-                   const char *short_name, int dso_type)
+struct dso *machine__findnew_kernel(struct machine *machine, const char *name,
+                                   const char *short_name, int dso_type)
 {
        /*
         * The kernel dso could be created by build_id processing.
         */
-       struct dso *dso = __dsos__findnew(&machine->kernel_dsos, name);
+       struct dso *dso = machine__findnew_dso(machine, name);
 
        /*
         * We need to run this in all cases, since during the build_id
@@ -776,8 +889,8 @@ struct dso *dso__kernel_findnew(struct machine *machine, const char *name,
  * Either one of the dso or name parameter must be non-NULL or the
  * function will not work.
  */
-static struct dso *dso__findlink_by_longname(struct rb_root *root,
-                                            struct dso *dso, const char *name)
+static struct dso *__dso__findlink_by_longname(struct rb_root *root,
+                                              struct dso *dso, const char *name)
 {
        struct rb_node **p = &root->rb_node;
        struct rb_node  *parent = NULL;
@@ -824,10 +937,10 @@ static struct dso *dso__findlink_by_longname(struct rb_root *root,
        return NULL;
 }
 
-static inline struct dso *
-dso__find_by_longname(const struct rb_root *root, const char *name)
+static inline struct dso *__dso__find_by_longname(struct rb_root *root,
+                                                 const char *name)
 {
-       return dso__findlink_by_longname((struct rb_root *)root, NULL, name);
+       return __dso__findlink_by_longname(root, NULL, name);
 }
 
 void dso__set_long_name(struct dso *dso, const char *name, bool name_allocated)
@@ -935,6 +1048,8 @@ struct dso *dso__new(const char *name)
                RB_CLEAR_NODE(&dso->rb_node);
                INIT_LIST_HEAD(&dso->node);
                INIT_LIST_HEAD(&dso->data.open_entry);
+               pthread_mutex_init(&dso->lock, NULL);
+               atomic_set(&dso->refcnt, 1);
        }
 
        return dso;
@@ -961,12 +1076,27 @@ void dso__delete(struct dso *dso)
        }
 
        dso__data_close(dso);
-       dso_cache__free(&dso->data.cache);
+       auxtrace_cache__free(dso->auxtrace_cache);
+       dso_cache__free(dso);
        dso__free_a2l(dso);
        zfree(&dso->symsrc_filename);
+       pthread_mutex_destroy(&dso->lock);
        free(dso);
 }
 
+struct dso *dso__get(struct dso *dso)
+{
+       if (dso)
+               atomic_inc(&dso->refcnt);
+       return dso;
+}
+
+void dso__put(struct dso *dso)
+{
+       if (dso && atomic_dec_and_test(&dso->refcnt))
+               dso__delete(dso);
+}
+
 void dso__set_build_id(struct dso *dso, void *build_id)
 {
        memcpy(dso->build_id, build_id, sizeof(dso->build_id));
@@ -1033,14 +1163,41 @@ bool __dsos__read_build_ids(struct list_head *head, bool with_hits)
        return have_build_id;
 }
 
-void dsos__add(struct dsos *dsos, struct dso *dso)
+void __dsos__add(struct dsos *dsos, struct dso *dso)
 {
        list_add_tail(&dso->node, &dsos->head);
-       dso__findlink_by_longname(&dsos->root, dso, NULL);
+       __dso__findlink_by_longname(&dsos->root, dso, NULL);
+       /*
+        * It is now in the linked list, grab a reference, then garbage collect
+        * this when needing memory, by looking at LRU dso instances in the
+        * list with atomic_read(&dso->refcnt) == 1, i.e. no references
+        * anywhere besides the one for the list, do, under a lock for the
+        * list: remove it from the list, then a dso__put(), that probably will
+        * be the last and will then call dso__delete(), end of life.
+        *
+        * That, or at the end of the 'struct machine' lifetime, when all
+        * 'struct dso' instances will be removed from the list, in
+        * dsos__exit(), if they have no other reference from some other data
+        * structure.
+        *
+        * E.g.: after processing a 'perf.data' file and storing references
+        * to objects instantiated while processing events, we will have
+        * references to the 'thread', 'map', 'dso' structs all from 'struct
+        * hist_entry' instances, but we may not need anything not referenced,
+        * so we might as well call machines__exit()/machines__delete() and
+        * garbage collect it.
+        */
+       dso__get(dso);
+}
+
+void dsos__add(struct dsos *dsos, struct dso *dso)
+{
+       pthread_rwlock_wrlock(&dsos->lock);
+       __dsos__add(dsos, dso);
+       pthread_rwlock_unlock(&dsos->lock);
 }
 
-struct dso *dsos__find(const struct dsos *dsos, const char *name,
-                      bool cmp_short)
+struct dso *__dsos__find(struct dsos *dsos, const char *name, bool cmp_short)
 {
        struct dso *pos;
 
@@ -1050,15 +1207,24 @@ struct dso *dsos__find(const struct dsos *dsos, const char *name,
                                return pos;
                return NULL;
        }
-       return dso__find_by_longname(&dsos->root, name);
+       return __dso__find_by_longname(&dsos->root, name);
+}
+
+struct dso *dsos__find(struct dsos *dsos, const char *name, bool cmp_short)
+{
+       struct dso *dso;
+       pthread_rwlock_rdlock(&dsos->lock);
+       dso = __dsos__find(dsos, name, cmp_short);
+       pthread_rwlock_unlock(&dsos->lock);
+       return dso;
 }
 
-struct dso *dsos__addnew(struct dsos *dsos, const char *name)
+struct dso *__dsos__addnew(struct dsos *dsos, const char *name)
 {
        struct dso *dso = dso__new(name);
 
        if (dso != NULL) {
-               dsos__add(dsos, dso);
+               __dsos__add(dsos, dso);
                dso__set_basename(dso);
        }
        return dso;
@@ -1066,9 +1232,18 @@ struct dso *dsos__addnew(struct dsos *dsos, const char *name)
 
 struct dso *__dsos__findnew(struct dsos *dsos, const char *name)
 {
-       struct dso *dso = dsos__find(dsos, name, false);
+       struct dso *dso = __dsos__find(dsos, name, false);
 
-       return dso ? dso : dsos__addnew(dsos, name);
+       return dso ? dso : __dsos__addnew(dsos, name);
+}
+
+struct dso *dsos__findnew(struct dsos *dsos, const char *name)
+{
+       struct dso *dso;
+       pthread_rwlock_wrlock(&dsos->lock);
+       dso = dso__get(__dsos__findnew(dsos, name));
+       pthread_rwlock_unlock(&dsos->lock);
+       return dso;
 }
 
 size_t __dsos__fprintf_buildid(struct list_head *head, FILE *fp,
@@ -1130,12 +1305,15 @@ size_t dso__fprintf(struct dso *dso, enum map_type type, FILE *fp)
 enum dso_type dso__type(struct dso *dso, struct machine *machine)
 {
        int fd;
+       enum dso_type type = DSO__TYPE_UNKNOWN;
 
-       fd = dso__data_fd(dso, machine);
-       if (fd < 0)
-               return DSO__TYPE_UNKNOWN;
+       fd = dso__data_get_fd(dso, machine);
+       if (fd >= 0) {
+               type = dso__type_fd(fd);
+               dso__data_put_fd(dso);
+       }
 
-       return dso__type_fd(fd);
+       return type;
 }
 
 int dso__strerror_load(struct dso *dso, char *buf, size_t buflen)
index e0901b4ed8de0d08b1c6ad93f20ca371b9adef5f..2fe98bb0e95b0d4b88fb58f2fc28daee875a13bb 100644 (file)
@@ -1,9 +1,11 @@
 #ifndef __PERF_DSO
 #define __PERF_DSO
 
+#include <linux/atomic.h>
 #include <linux/types.h>
 #include <linux/rbtree.h>
 #include <stdbool.h>
+#include <pthread.h>
 #include <linux/types.h>
 #include <linux/bitops.h>
 #include "map.h"
@@ -124,9 +126,13 @@ struct dso_cache {
 struct dsos {
        struct list_head head;
        struct rb_root   root;  /* rbtree root sorted by long name */
+       pthread_rwlock_t lock;
 };
 
+struct auxtrace_cache;
+
 struct dso {
+       pthread_mutex_t  lock;
        struct list_head node;
        struct rb_node   rb_node;       /* rbtree node sorted by long name */
        struct rb_root   symbols[MAP__NR_TYPES];
@@ -156,6 +162,7 @@ struct dso {
        u16              long_name_len;
        u16              short_name_len;
        void            *dwfl;                  /* DWARF debug info */
+       struct auxtrace_cache *auxtrace_cache;
 
        /* dso data file */
        struct {
@@ -173,7 +180,7 @@ struct dso {
                void     *priv;
                u64      db_id;
        };
-
+       atomic_t         refcnt;
        char             name[0];
 };
 
@@ -200,6 +207,17 @@ void dso__set_long_name(struct dso *dso, const char *name, bool name_allocated);
 
 int dso__name_len(const struct dso *dso);
 
+struct dso *dso__get(struct dso *dso);
+void dso__put(struct dso *dso);
+
+static inline void __dso__zput(struct dso **dso)
+{
+       dso__put(*dso);
+       *dso = NULL;
+}
+
+#define dso__zput(dso) __dso__zput(&dso)
+
 bool dso__loaded(const struct dso *dso, enum map_type type);
 
 bool dso__sorted_by_name(const struct dso *dso, enum map_type type);
@@ -216,7 +234,7 @@ char dso__symtab_origin(const struct dso *dso);
 int dso__read_binary_type_filename(const struct dso *dso, enum dso_binary_type type,
                                   char *root_dir, char *filename, size_t size);
 bool is_supported_compression(const char *ext);
-bool is_kernel_module(const char *pathname);
+bool is_kernel_module(const char *pathname, int cpumode);
 bool decompress_to_file(const char *ext, const char *filename, int output_fd);
 bool dso__needs_decompress(struct dso *dso);
 
@@ -236,7 +254,8 @@ int __kmod_path__parse(struct kmod_path *m, const char *path,
 
 /*
  * The dso__data_* external interface provides following functions:
- *   dso__data_fd
+ *   dso__data_get_fd
+ *   dso__data_put_fd
  *   dso__data_close
  *   dso__data_size
  *   dso__data_read_offset
@@ -253,8 +272,11 @@ int __kmod_path__parse(struct kmod_path *m, const char *path,
  * The current usage of the dso__data_* interface is as follows:
  *
  * Get DSO's fd:
- *   int fd = dso__data_fd(dso, machine);
- *   USE 'fd' SOMEHOW
+ *   int fd = dso__data_get_fd(dso, machine);
+ *   if (fd >= 0) {
+ *       USE 'fd' SOMEHOW
+ *       dso__data_put_fd(dso);
+ *   }
  *
  * Read DSO's data:
  *   n = dso__data_read_offset(dso_0, &machine, 0, buf, BUFSIZE);
@@ -273,7 +295,8 @@ int __kmod_path__parse(struct kmod_path *m, const char *path,
  *
  * TODO
 */
-int dso__data_fd(struct dso *dso, struct machine *machine);
+int dso__data_get_fd(struct dso *dso, struct machine *machine);
+void dso__data_put_fd(struct dso *dso __maybe_unused);
 void dso__data_close(struct dso *dso);
 
 off_t dso__data_size(struct dso *dso, struct machine *machine);
@@ -285,14 +308,16 @@ ssize_t dso__data_read_addr(struct dso *dso, struct map *map,
 bool dso__data_status_seen(struct dso *dso, enum dso_data_status_seen by);
 
 struct map *dso__new_map(const char *name);
-struct dso *dso__kernel_findnew(struct machine *machine, const char *name,
-                               const char *short_name, int dso_type);
+struct dso *machine__findnew_kernel(struct machine *machine, const char *name,
+                                   const char *short_name, int dso_type);
 
+void __dsos__add(struct dsos *dsos, struct dso *dso);
 void dsos__add(struct dsos *dsos, struct dso *dso);
-struct dso *dsos__addnew(struct dsos *dsos, const char *name);
-struct dso *dsos__find(const struct dsos *dsos, const char *name,
-                      bool cmp_short);
+struct dso *__dsos__addnew(struct dsos *dsos, const char *name);
+struct dso *__dsos__find(struct dsos *dsos, const char *name, bool cmp_short);
+struct dso *dsos__find(struct dsos *dsos, const char *name, bool cmp_short);
 struct dso *__dsos__findnew(struct dsos *dsos, const char *name);
+struct dso *dsos__findnew(struct dsos *dsos, const char *name);
 bool __dsos__read_build_ids(struct list_head *head, bool with_hits);
 
 size_t __dsos__fprintf_buildid(struct list_head *head, FILE *fp,
index c34e024020c7c58602a9459f407283faea884b20..57f3ef41c2bc3e6261c03f54dc7f99791a003108 100644 (file)
@@ -139,10 +139,26 @@ int cu_walk_functions_at(Dwarf_Die *cu_die, Dwarf_Addr addr,
 bool die_compare_name(Dwarf_Die *dw_die, const char *tname)
 {
        const char *name;
+
        name = dwarf_diename(dw_die);
        return name ? (strcmp(tname, name) == 0) : false;
 }
 
+/**
+ * die_match_name - Match diename and glob
+ * @dw_die: a DIE
+ * @glob: a string of target glob pattern
+ *
+ * Glob matching the name of @dw_die and @glob. Return false if matching fail.
+ */
+bool die_match_name(Dwarf_Die *dw_die, const char *glob)
+{
+       const char *name;
+
+       name = dwarf_diename(dw_die);
+       return name ? strglobmatch(name, glob) : false;
+}
+
 /**
  * die_get_call_lineno - Get callsite line number of inline-function instance
  * @in_die: a DIE of an inlined function instance
@@ -417,6 +433,43 @@ struct __addr_die_search_param {
        Dwarf_Die       *die_mem;
 };
 
+static int __die_search_func_tail_cb(Dwarf_Die *fn_die, void *data)
+{
+       struct __addr_die_search_param *ad = data;
+       Dwarf_Addr addr = 0;
+
+       if (dwarf_tag(fn_die) == DW_TAG_subprogram &&
+           !dwarf_highpc(fn_die, &addr) &&
+           addr == ad->addr) {
+               memcpy(ad->die_mem, fn_die, sizeof(Dwarf_Die));
+               return DWARF_CB_ABORT;
+       }
+       return DWARF_CB_OK;
+}
+
+/**
+ * die_find_tailfunc - Search for a non-inlined function with tail call at
+ * given address
+ * @cu_die: a CU DIE which including @addr
+ * @addr: target address
+ * @die_mem: a buffer for result DIE
+ *
+ * Search for a non-inlined function DIE with tail call at @addr. Stores the
+ * DIE to @die_mem and returns it if found. Returns NULL if failed.
+ */
+Dwarf_Die *die_find_tailfunc(Dwarf_Die *cu_die, Dwarf_Addr addr,
+                                   Dwarf_Die *die_mem)
+{
+       struct __addr_die_search_param ad;
+       ad.addr = addr;
+       ad.die_mem = die_mem;
+       /* dwarf_getscopes can't find subprogram. */
+       if (!dwarf_getfuncs(cu_die, __die_search_func_tail_cb, &ad, 0))
+               return NULL;
+       else
+               return die_mem;
+}
+
 /* die_find callback for non-inlined function search */
 static int __die_search_func_cb(Dwarf_Die *fn_die, void *data)
 {
@@ -832,19 +885,17 @@ Dwarf_Die *die_find_member(Dwarf_Die *st_die, const char *name,
 /**
  * die_get_typename - Get the name of given variable DIE
  * @vr_die: a variable DIE
- * @buf: a buffer for result type name
- * @len: a max-length of @buf
+ * @buf: a strbuf for result type name
  *
- * Get the name of @vr_die and stores it to @buf. Return the actual length
- * of type name if succeeded. Return -E2BIG if @len is not enough long, and
- * Return -ENOENT if failed to find type name.
+ * Get the name of @vr_die and stores it to @buf. Return 0 if succeeded.
+ * and Return -ENOENT if failed to find type name.
  * Note that the result will stores typedef name if possible, and stores
  * "*(function_type)" if the type is a function pointer.
  */
-int die_get_typename(Dwarf_Die *vr_die, char *buf, int len)
+int die_get_typename(Dwarf_Die *vr_die, struct strbuf *buf)
 {
        Dwarf_Die type;
-       int tag, ret, ret2;
+       int tag, ret;
        const char *tmp = "";
 
        if (__die_get_real_type(vr_die, &type) == NULL)
@@ -855,8 +906,8 @@ int die_get_typename(Dwarf_Die *vr_die, char *buf, int len)
                tmp = "*";
        else if (tag == DW_TAG_subroutine_type) {
                /* Function pointer */
-               ret = snprintf(buf, len, "(function_type)");
-               return (ret >= len) ? -E2BIG : ret;
+               strbuf_addf(buf, "(function_type)");
+               return 0;
        } else {
                if (!dwarf_diename(&type))
                        return -ENOENT;
@@ -867,39 +918,156 @@ int die_get_typename(Dwarf_Die *vr_die, char *buf, int len)
                else if (tag == DW_TAG_enumeration_type)
                        tmp = "enum ";
                /* Write a base name */
-               ret = snprintf(buf, len, "%s%s", tmp, dwarf_diename(&type));
-               return (ret >= len) ? -E2BIG : ret;
-       }
-       ret = die_get_typename(&type, buf, len);
-       if (ret > 0) {
-               ret2 = snprintf(buf + ret, len - ret, "%s", tmp);
-               ret = (ret2 >= len - ret) ? -E2BIG : ret2 + ret;
+               strbuf_addf(buf, "%s%s", tmp, dwarf_diename(&type));
+               return 0;
        }
+       ret = die_get_typename(&type, buf);
+       if (ret == 0)
+               strbuf_addf(buf, "%s", tmp);
+
        return ret;
 }
 
 /**
  * die_get_varname - Get the name and type of given variable DIE
  * @vr_die: a variable DIE
- * @buf: a buffer for type and variable name
- * @len: the max-length of @buf
+ * @buf: a strbuf for type and variable name
  *
  * Get the name and type of @vr_die and stores it in @buf as "type\tname".
  */
-int die_get_varname(Dwarf_Die *vr_die, char *buf, int len)
+int die_get_varname(Dwarf_Die *vr_die, struct strbuf *buf)
 {
-       int ret, ret2;
+       int ret;
 
-       ret = die_get_typename(vr_die, buf, len);
+       ret = die_get_typename(vr_die, buf);
        if (ret < 0) {
                pr_debug("Failed to get type, make it unknown.\n");
-               ret = snprintf(buf, len, "(unknown_type)");
+               strbuf_addf(buf, "(unknown_type)");
        }
-       if (ret > 0) {
-               ret2 = snprintf(buf + ret, len - ret, "\t%s",
-                               dwarf_diename(vr_die));
-               ret = (ret2 >= len - ret) ? -E2BIG : ret2 + ret;
+
+       strbuf_addf(buf, "\t%s", dwarf_diename(vr_die));
+
+       return 0;
+}
+
+/**
+ * die_get_var_innermost_scope - Get innermost scope range of given variable DIE
+ * @sp_die: a subprogram DIE
+ * @vr_die: a variable DIE
+ * @buf: a strbuf for variable byte offset range
+ *
+ * Get the innermost scope range of @vr_die and stores it in @buf as
+ * "@<function_name+[NN-NN,NN-NN]>".
+ */
+static int die_get_var_innermost_scope(Dwarf_Die *sp_die, Dwarf_Die *vr_die,
+                               struct strbuf *buf)
+{
+       Dwarf_Die *scopes;
+       int count;
+       size_t offset = 0;
+       Dwarf_Addr base;
+       Dwarf_Addr start, end;
+       Dwarf_Addr entry;
+       int ret;
+       bool first = true;
+       const char *name;
+
+       ret = dwarf_entrypc(sp_die, &entry);
+       if (ret)
+               return ret;
+
+       name = dwarf_diename(sp_die);
+       if (!name)
+               return -ENOENT;
+
+       count = dwarf_getscopes_die(vr_die, &scopes);
+
+       /* (*SCOPES)[1] is the DIE for the scope containing that scope */
+       if (count <= 1) {
+               ret = -EINVAL;
+               goto out;
        }
+
+       while ((offset = dwarf_ranges(&scopes[1], offset, &base,
+                               &start, &end)) > 0) {
+               start -= entry;
+               end -= entry;
+
+               if (first) {
+                       strbuf_addf(buf, "@<%s+[%" PRIu64 "-%" PRIu64,
+                               name, start, end);
+                       first = false;
+               } else {
+                       strbuf_addf(buf, ",%" PRIu64 "-%" PRIu64,
+                               start, end);
+               }
+       }
+
+       if (!first)
+               strbuf_addf(buf, "]>");
+
+out:
+       free(scopes);
        return ret;
 }
 
+/**
+ * die_get_var_range - Get byte offset range of given variable DIE
+ * @sp_die: a subprogram DIE
+ * @vr_die: a variable DIE
+ * @buf: a strbuf for type and variable name and byte offset range
+ *
+ * Get the byte offset range of @vr_die and stores it in @buf as
+ * "@<function_name+[NN-NN,NN-NN]>".
+ */
+int die_get_var_range(Dwarf_Die *sp_die, Dwarf_Die *vr_die, struct strbuf *buf)
+{
+       int ret = 0;
+       Dwarf_Addr base;
+       Dwarf_Addr start, end;
+       Dwarf_Addr entry;
+       Dwarf_Op *op;
+       size_t nops;
+       size_t offset = 0;
+       Dwarf_Attribute attr;
+       bool first = true;
+       const char *name;
+
+       ret = dwarf_entrypc(sp_die, &entry);
+       if (ret)
+               return ret;
+
+       name = dwarf_diename(sp_die);
+       if (!name)
+               return -ENOENT;
+
+       if (dwarf_attr(vr_die, DW_AT_location, &attr) == NULL)
+               return -EINVAL;
+
+       while ((offset = dwarf_getlocations(
+                               &attr, offset, &base,
+                               &start, &end, &op, &nops)) > 0) {
+               if (start == 0) {
+                       /* Single Location Descriptions */
+                       ret = die_get_var_innermost_scope(sp_die, vr_die, buf);
+                       return ret;
+               }
+
+               /* Location Lists */
+               start -= entry;
+               end -= entry;
+               if (first) {
+                       strbuf_addf(buf, "@<%s+[%" PRIu64 "-%" PRIu64,
+                               name, start, end);
+                       first = false;
+               } else {
+                       strbuf_addf(buf, ",%" PRIu64 "-%" PRIu64,
+                               start, end);
+               }
+       }
+
+       if (!first)
+               strbuf_addf(buf, "]>");
+
+       return ret;
+}
index af7dbcd5f929947cfdd0b146f20ad65d45059bb8..c42ec366f2a72fb7999e22f257ebc82765ee4919 100644 (file)
@@ -47,6 +47,9 @@ extern bool die_is_func_instance(Dwarf_Die *dw_die);
 /* Compare diename and tname */
 extern bool die_compare_name(Dwarf_Die *dw_die, const char *tname);
 
+/* Matching diename with glob pattern */
+extern bool die_match_name(Dwarf_Die *dw_die, const char *glob);
+
 /* Get callsite line number of inline-function instance */
 extern int die_get_call_lineno(Dwarf_Die *in_die);
 
@@ -82,6 +85,10 @@ extern Dwarf_Die *die_find_child(Dwarf_Die *rt_die,
 extern Dwarf_Die *die_find_realfunc(Dwarf_Die *cu_die, Dwarf_Addr addr,
                                    Dwarf_Die *die_mem);
 
+/* Search a non-inlined function with tail call at given address */
+Dwarf_Die *die_find_tailfunc(Dwarf_Die *cu_die, Dwarf_Addr addr,
+                                   Dwarf_Die *die_mem);
+
 /* Search the top inlined function including given address */
 extern Dwarf_Die *die_find_top_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr,
                                          Dwarf_Die *die_mem);
@@ -114,8 +121,10 @@ extern Dwarf_Die *die_find_member(Dwarf_Die *st_die, const char *name,
                                  Dwarf_Die *die_mem);
 
 /* Get the name of given variable DIE */
-extern int die_get_typename(Dwarf_Die *vr_die, char *buf, int len);
+extern int die_get_typename(Dwarf_Die *vr_die, struct strbuf *buf);
 
 /* Get the name and type of given variable DIE, stored as "type\tname" */
-extern int die_get_varname(Dwarf_Die *vr_die, char *buf, int len);
+extern int die_get_varname(Dwarf_Die *vr_die, struct strbuf *buf);
+extern int die_get_var_range(Dwarf_Die *sp_die, Dwarf_Die *vr_die,
+                       struct strbuf *buf);
 #endif
index 275b0ee345f5eab4b1dcaebc18d5068698d9e437..7405123692f14919bc1928c67f4819ef3503e1ba 100644 (file)
@@ -5,5 +5,4 @@
  */
 #include "cache.h"
 
-const char *pager_program;
 int pager_use_color = 1;
index ff866c4d2e2f09ea4abc1650d7c413776b3bb93f..d7d986d8f23e5f890cc295b3b830470343f34063 100644 (file)
@@ -23,12 +23,18 @@ static const char *perf_event__names[] = {
        [PERF_RECORD_FORK]                      = "FORK",
        [PERF_RECORD_READ]                      = "READ",
        [PERF_RECORD_SAMPLE]                    = "SAMPLE",
+       [PERF_RECORD_AUX]                       = "AUX",
+       [PERF_RECORD_ITRACE_START]              = "ITRACE_START",
+       [PERF_RECORD_LOST_SAMPLES]              = "LOST_SAMPLES",
        [PERF_RECORD_HEADER_ATTR]               = "ATTR",
        [PERF_RECORD_HEADER_EVENT_TYPE]         = "EVENT_TYPE",
        [PERF_RECORD_HEADER_TRACING_DATA]       = "TRACING_DATA",
        [PERF_RECORD_HEADER_BUILD_ID]           = "BUILD_ID",
        [PERF_RECORD_FINISHED_ROUND]            = "FINISHED_ROUND",
        [PERF_RECORD_ID_INDEX]                  = "ID_INDEX",
+       [PERF_RECORD_AUXTRACE_INFO]             = "AUXTRACE_INFO",
+       [PERF_RECORD_AUXTRACE]                  = "AUXTRACE",
+       [PERF_RECORD_AUXTRACE_ERROR]            = "AUXTRACE_ERROR",
 };
 
 const char *perf_event__name(unsigned int id)
@@ -212,10 +218,14 @@ int perf_event__synthesize_mmap_events(struct perf_tool *tool,
                                       pid_t pid, pid_t tgid,
                                       perf_event__handler_t process,
                                       struct machine *machine,
-                                      bool mmap_data)
+                                      bool mmap_data,
+                                      unsigned int proc_map_timeout)
 {
        char filename[PATH_MAX];
        FILE *fp;
+       unsigned long long t;
+       bool truncation = false;
+       unsigned long long timeout = proc_map_timeout * 1000000ULL;
        int rc = 0;
 
        if (machine__is_default_guest(machine))
@@ -234,6 +244,7 @@ int perf_event__synthesize_mmap_events(struct perf_tool *tool,
        }
 
        event->header.type = PERF_RECORD_MMAP2;
+       t = rdclock();
 
        while (1) {
                char bf[BUFSIZ];
@@ -247,6 +258,15 @@ int perf_event__synthesize_mmap_events(struct perf_tool *tool,
                if (fgets(bf, sizeof(bf), fp) == NULL)
                        break;
 
+               if ((rdclock() - t) > timeout) {
+                       pr_warning("Reading %s time out. "
+                                  "You may want to increase "
+                                  "the time limit by --proc-map-timeout\n",
+                                  filename);
+                       truncation = true;
+                       goto out;
+               }
+
                /* ensure null termination since stack will be reused. */
                strcpy(execname, "");
 
@@ -295,6 +315,10 @@ int perf_event__synthesize_mmap_events(struct perf_tool *tool,
                        event->header.misc |= PERF_RECORD_MISC_MMAP_DATA;
                }
 
+out:
+               if (truncation)
+                       event->header.misc |= PERF_RECORD_MISC_PROC_MAP_PARSE_TIMEOUT;
+
                if (!strcmp(execname, ""))
                        strcpy(execname, anonstr);
 
@@ -313,6 +337,9 @@ int perf_event__synthesize_mmap_events(struct perf_tool *tool,
                        rc = -1;
                        break;
                }
+
+               if (truncation)
+                       break;
        }
 
        fclose(fp);
@@ -324,8 +351,9 @@ int perf_event__synthesize_modules(struct perf_tool *tool,
                                   struct machine *machine)
 {
        int rc = 0;
-       struct rb_node *nd;
+       struct map *pos;
        struct map_groups *kmaps = &machine->kmaps;
+       struct maps *maps = &kmaps->maps[MAP__FUNCTION];
        union perf_event *event = zalloc((sizeof(event->mmap) +
                                          machine->id_hdr_size));
        if (event == NULL) {
@@ -345,10 +373,8 @@ int perf_event__synthesize_modules(struct perf_tool *tool,
        else
                event->header.misc = PERF_RECORD_MISC_GUEST_KERNEL;
 
-       for (nd = rb_first(&kmaps->maps[MAP__FUNCTION]);
-            nd; nd = rb_next(nd)) {
+       for (pos = maps__first(maps); pos; pos = map__next(pos)) {
                size_t size;
-               struct map *pos = rb_entry(nd, struct map, rb_node);
 
                if (pos->dso->kernel)
                        continue;
@@ -381,7 +407,9 @@ static int __event__synthesize_thread(union perf_event *comm_event,
                                      pid_t pid, int full,
                                          perf_event__handler_t process,
                                      struct perf_tool *tool,
-                                     struct machine *machine, bool mmap_data)
+                                     struct machine *machine,
+                                     bool mmap_data,
+                                     unsigned int proc_map_timeout)
 {
        char filename[PATH_MAX];
        DIR *tasks;
@@ -398,7 +426,8 @@ static int __event__synthesize_thread(union perf_event *comm_event,
                        return -1;
 
                return perf_event__synthesize_mmap_events(tool, mmap_event, pid, tgid,
-                                                         process, machine, mmap_data);
+                                                         process, machine, mmap_data,
+                                                         proc_map_timeout);
        }
 
        if (machine__is_default_guest(machine))
@@ -439,7 +468,7 @@ static int __event__synthesize_thread(union perf_event *comm_event,
                if (_pid == pid) {
                        /* process the parent's maps too */
                        rc = perf_event__synthesize_mmap_events(tool, mmap_event, pid, tgid,
-                                               process, machine, mmap_data);
+                                               process, machine, mmap_data, proc_map_timeout);
                        if (rc)
                                break;
                }
@@ -453,7 +482,8 @@ int perf_event__synthesize_thread_map(struct perf_tool *tool,
                                      struct thread_map *threads,
                                      perf_event__handler_t process,
                                      struct machine *machine,
-                                     bool mmap_data)
+                                     bool mmap_data,
+                                     unsigned int proc_map_timeout)
 {
        union perf_event *comm_event, *mmap_event, *fork_event;
        int err = -1, thread, j;
@@ -476,7 +506,7 @@ int perf_event__synthesize_thread_map(struct perf_tool *tool,
                                               fork_event,
                                               threads->map[thread], 0,
                                               process, tool, machine,
-                                              mmap_data)) {
+                                              mmap_data, proc_map_timeout)) {
                        err = -1;
                        break;
                }
@@ -502,7 +532,7 @@ int perf_event__synthesize_thread_map(struct perf_tool *tool,
                                                       fork_event,
                                                       comm_event->comm.pid, 0,
                                                       process, tool, machine,
-                                                      mmap_data)) {
+                                                      mmap_data, proc_map_timeout)) {
                                err = -1;
                                break;
                        }
@@ -519,7 +549,9 @@ out:
 
 int perf_event__synthesize_threads(struct perf_tool *tool,
                                   perf_event__handler_t process,
-                                  struct machine *machine, bool mmap_data)
+                                  struct machine *machine,
+                                  bool mmap_data,
+                                  unsigned int proc_map_timeout)
 {
        DIR *proc;
        char proc_path[PATH_MAX];
@@ -559,7 +591,8 @@ int perf_event__synthesize_threads(struct perf_tool *tool,
                 * one thread couldn't be synthesized.
                 */
                __event__synthesize_thread(comm_event, mmap_event, fork_event, pid,
-                                          1, process, tool, machine, mmap_data);
+                                          1, process, tool, machine, mmap_data,
+                                          proc_map_timeout);
        }
 
        err = 0;
@@ -692,6 +725,30 @@ int perf_event__process_lost(struct perf_tool *tool __maybe_unused,
        return machine__process_lost_event(machine, event, sample);
 }
 
+int perf_event__process_aux(struct perf_tool *tool __maybe_unused,
+                           union perf_event *event,
+                           struct perf_sample *sample __maybe_unused,
+                           struct machine *machine)
+{
+       return machine__process_aux_event(machine, event);
+}
+
+int perf_event__process_itrace_start(struct perf_tool *tool __maybe_unused,
+                                    union perf_event *event,
+                                    struct perf_sample *sample __maybe_unused,
+                                    struct machine *machine)
+{
+       return machine__process_itrace_start_event(machine, event);
+}
+
+int perf_event__process_lost_samples(struct perf_tool *tool __maybe_unused,
+                                    union perf_event *event,
+                                    struct perf_sample *sample,
+                                    struct machine *machine)
+{
+       return machine__process_lost_samples_event(machine, event, sample);
+}
+
 size_t perf_event__fprintf_mmap(union perf_event *event, FILE *fp)
 {
        return fprintf(fp, " %d/%d: [%#" PRIx64 "(%#" PRIx64 ") @ %#" PRIx64 "]: %c %s\n",
@@ -755,6 +812,21 @@ int perf_event__process_exit(struct perf_tool *tool __maybe_unused,
        return machine__process_exit_event(machine, event, sample);
 }
 
+size_t perf_event__fprintf_aux(union perf_event *event, FILE *fp)
+{
+       return fprintf(fp, " offset: %#"PRIx64" size: %#"PRIx64" flags: %#"PRIx64" [%s%s]\n",
+                      event->aux.aux_offset, event->aux.aux_size,
+                      event->aux.flags,
+                      event->aux.flags & PERF_AUX_FLAG_TRUNCATED ? "T" : "",
+                      event->aux.flags & PERF_AUX_FLAG_OVERWRITE ? "O" : "");
+}
+
+size_t perf_event__fprintf_itrace_start(union perf_event *event, FILE *fp)
+{
+       return fprintf(fp, " pid: %u tid: %u\n",
+                      event->itrace_start.pid, event->itrace_start.tid);
+}
+
 size_t perf_event__fprintf(union perf_event *event, FILE *fp)
 {
        size_t ret = fprintf(fp, "PERF_RECORD_%s",
@@ -774,6 +846,12 @@ size_t perf_event__fprintf(union perf_event *event, FILE *fp)
        case PERF_RECORD_MMAP2:
                ret += perf_event__fprintf_mmap2(event, fp);
                break;
+       case PERF_RECORD_AUX:
+               ret += perf_event__fprintf_aux(event, fp);
+               break;
+       case PERF_RECORD_ITRACE_START:
+               ret += perf_event__fprintf_itrace_start(event, fp);
+               break;
        default:
                ret += fprintf(fp, "\n");
        }
@@ -877,6 +955,10 @@ void thread__find_addr_location(struct thread *thread,
                al->sym = NULL;
 }
 
+/*
+ * Callers need to drop the reference to al->thread, obtained in
+ * machine__findnew_thread()
+ */
 int perf_event__preprocess_sample(const union perf_event *event,
                                  struct machine *machine,
                                  struct addr_location *al,
@@ -937,6 +1019,17 @@ int perf_event__preprocess_sample(const union perf_event *event,
        return 0;
 }
 
+/*
+ * The preprocess_sample method will return with reference counts for the
+ * in it, when done using (and perhaps getting ref counts if needing to
+ * keep a pointer to one of those entries) it must be paired with
+ * addr_location__put(), so that the refcounts can be decremented.
+ */
+void addr_location__put(struct addr_location *al)
+{
+       thread__zput(al->thread);
+}
+
 bool is_bts_event(struct perf_event_attr *attr)
 {
        return attr->type == PERF_TYPE_HARDWARE &&
index 09b9e8d3fcf7fae705afcc0d3ea688f2aca77665..c53f36384b64532abec852c9e6d7a496a0d982a6 100644 (file)
@@ -52,6 +52,11 @@ struct lost_event {
        u64 lost;
 };
 
+struct lost_samples_event {
+       struct perf_event_header header;
+       u64 lost;
+};
+
 /*
  * PERF_FORMAT_ENABLED | PERF_FORMAT_RUNNING | PERF_FORMAT_ID
  */
@@ -157,6 +162,8 @@ enum {
        PERF_IP_FLAG_IN_TX              = 1ULL << 10,
 };
 
+#define PERF_IP_FLAG_CHARS "bcrosyiABEx"
+
 #define PERF_BRANCH_MASK               (\
        PERF_IP_FLAG_BRANCH             |\
        PERF_IP_FLAG_CALL               |\
@@ -215,9 +222,17 @@ enum perf_user_event_type { /* above any possible kernel type */
        PERF_RECORD_HEADER_BUILD_ID             = 67,
        PERF_RECORD_FINISHED_ROUND              = 68,
        PERF_RECORD_ID_INDEX                    = 69,
+       PERF_RECORD_AUXTRACE_INFO               = 70,
+       PERF_RECORD_AUXTRACE                    = 71,
+       PERF_RECORD_AUXTRACE_ERROR              = 72,
        PERF_RECORD_HEADER_MAX
 };
 
+enum auxtrace_error_type {
+       PERF_AUXTRACE_ERROR_ITRACE  = 1,
+       PERF_AUXTRACE_ERROR_MAX
+};
+
 /*
  * The kernel collects the number of events it couldn't send in a stretch and
  * when possible sends this number in a PERF_RECORD_LOST event. The number of
@@ -225,6 +240,12 @@ enum perf_user_event_type { /* above any possible kernel type */
  * total_lost tells exactly how many events the kernel in fact lost, i.e. it is
  * the sum of all struct lost_event.lost fields reported.
  *
+ * The kernel discards mixed up samples and sends the number in a
+ * PERF_RECORD_LOST_SAMPLES event. The number of lost-samples events is stored
+ * in .nr_events[PERF_RECORD_LOST_SAMPLES] while total_lost_samples tells
+ * exactly how many samples the kernel in fact dropped, i.e. it is the sum of
+ * all struct lost_samples_event.lost fields reported.
+ *
  * The total_period is needed because by default auto-freq is used, so
  * multipling nr_events[PERF_EVENT_SAMPLE] by a frequency isn't possible to get
  * the total number of low level events, it is necessary to to sum all struct
@@ -234,6 +255,7 @@ struct events_stats {
        u64 total_period;
        u64 total_non_filtered_period;
        u64 total_lost;
+       u64 total_lost_samples;
        u64 total_invalid_chains;
        u32 nr_events[PERF_RECORD_HEADER_MAX];
        u32 nr_non_filtered_samples;
@@ -242,6 +264,8 @@ struct events_stats {
        u32 nr_invalid_chains;
        u32 nr_unknown_id;
        u32 nr_unprocessable_samples;
+       u32 nr_auxtrace_errors[PERF_AUXTRACE_ERROR_MAX];
+       u32 nr_proc_map_timeout;
 };
 
 struct attr_event {
@@ -280,6 +304,50 @@ struct id_index_event {
        struct id_index_entry entries[0];
 };
 
+struct auxtrace_info_event {
+       struct perf_event_header header;
+       u32 type;
+       u32 reserved__; /* For alignment */
+       u64 priv[];
+};
+
+struct auxtrace_event {
+       struct perf_event_header header;
+       u64 size;
+       u64 offset;
+       u64 reference;
+       u32 idx;
+       u32 tid;
+       u32 cpu;
+       u32 reserved__; /* For alignment */
+};
+
+#define MAX_AUXTRACE_ERROR_MSG 64
+
+struct auxtrace_error_event {
+       struct perf_event_header header;
+       u32 type;
+       u32 code;
+       u32 cpu;
+       u32 pid;
+       u32 tid;
+       u32 reserved__; /* For alignment */
+       u64 ip;
+       char msg[MAX_AUXTRACE_ERROR_MSG];
+};
+
+struct aux_event {
+       struct perf_event_header header;
+       u64     aux_offset;
+       u64     aux_size;
+       u64     flags;
+};
+
+struct itrace_start_event {
+       struct perf_event_header header;
+       u32 pid, tid;
+};
+
 union perf_event {
        struct perf_event_header        header;
        struct mmap_event               mmap;
@@ -287,6 +355,7 @@ union perf_event {
        struct comm_event               comm;
        struct fork_event               fork;
        struct lost_event               lost;
+       struct lost_samples_event       lost_samples;
        struct read_event               read;
        struct throttle_event           throttle;
        struct sample_event             sample;
@@ -295,6 +364,11 @@ union perf_event {
        struct tracing_data_event       tracing_data;
        struct build_id_event           build_id;
        struct id_index_event           id_index;
+       struct auxtrace_info_event      auxtrace_info;
+       struct auxtrace_event           auxtrace;
+       struct auxtrace_error_event     auxtrace_error;
+       struct aux_event                aux;
+       struct itrace_start_event       itrace_start;
 };
 
 void perf_event__print_totals(void);
@@ -310,10 +384,12 @@ typedef int (*perf_event__handler_t)(struct perf_tool *tool,
 int perf_event__synthesize_thread_map(struct perf_tool *tool,
                                      struct thread_map *threads,
                                      perf_event__handler_t process,
-                                     struct machine *machine, bool mmap_data);
+                                     struct machine *machine, bool mmap_data,
+                                     unsigned int proc_map_timeout);
 int perf_event__synthesize_threads(struct perf_tool *tool,
                                   perf_event__handler_t process,
-                                  struct machine *machine, bool mmap_data);
+                                  struct machine *machine, bool mmap_data,
+                                  unsigned int proc_map_timeout);
 int perf_event__synthesize_kernel_mmap(struct perf_tool *tool,
                                       perf_event__handler_t process,
                                       struct machine *machine);
@@ -330,6 +406,18 @@ int perf_event__process_lost(struct perf_tool *tool,
                             union perf_event *event,
                             struct perf_sample *sample,
                             struct machine *machine);
+int perf_event__process_lost_samples(struct perf_tool *tool,
+                                    union perf_event *event,
+                                    struct perf_sample *sample,
+                                    struct machine *machine);
+int perf_event__process_aux(struct perf_tool *tool,
+                           union perf_event *event,
+                           struct perf_sample *sample,
+                           struct machine *machine);
+int perf_event__process_itrace_start(struct perf_tool *tool,
+                                    union perf_event *event,
+                                    struct perf_sample *sample,
+                                    struct machine *machine);
 int perf_event__process_mmap(struct perf_tool *tool,
                             union perf_event *event,
                             struct perf_sample *sample,
@@ -358,6 +446,8 @@ int perf_event__preprocess_sample(const union perf_event *event,
                                  struct addr_location *al,
                                  struct perf_sample *sample);
 
+void addr_location__put(struct addr_location *al);
+
 struct thread;
 
 bool is_bts_event(struct perf_event_attr *attr);
@@ -381,12 +471,15 @@ int perf_event__synthesize_mmap_events(struct perf_tool *tool,
                                       pid_t pid, pid_t tgid,
                                       perf_event__handler_t process,
                                       struct machine *machine,
-                                      bool mmap_data);
+                                      bool mmap_data,
+                                      unsigned int proc_map_timeout);
 
 size_t perf_event__fprintf_comm(union perf_event *event, FILE *fp);
 size_t perf_event__fprintf_mmap(union perf_event *event, FILE *fp);
 size_t perf_event__fprintf_mmap2(union perf_event *event, FILE *fp);
 size_t perf_event__fprintf_task(union perf_event *event, FILE *fp);
+size_t perf_event__fprintf_aux(union perf_event *event, FILE *fp);
+size_t perf_event__fprintf_itrace_start(union perf_event *event, FILE *fp);
 size_t perf_event__fprintf(union perf_event *event, FILE *fp);
 
 u64 kallsyms__get_function_start(const char *kallsyms_filename,
index 080be93eea969f9893bd789cde845bf4a94a37bd..8366511b45f8327a65dc44e80544f6b08e0df24a 100644 (file)
@@ -297,6 +297,8 @@ void perf_evlist__disable(struct perf_evlist *evlist)
                                      PERF_EVENT_IOC_DISABLE, 0);
                }
        }
+
+       evlist->enabled = false;
 }
 
 void perf_evlist__enable(struct perf_evlist *evlist)
@@ -316,6 +318,13 @@ void perf_evlist__enable(struct perf_evlist *evlist)
                                      PERF_EVENT_IOC_ENABLE, 0);
                }
        }
+
+       evlist->enabled = true;
+}
+
+void perf_evlist__toggle_enable(struct perf_evlist *evlist)
+{
+       (evlist->enabled ? perf_evlist__disable : perf_evlist__enable)(evlist);
 }
 
 int perf_evlist__disable_event(struct perf_evlist *evlist,
@@ -634,11 +643,18 @@ static struct perf_evsel *perf_evlist__event2evsel(struct perf_evlist *evlist,
 union perf_event *perf_evlist__mmap_read(struct perf_evlist *evlist, int idx)
 {
        struct perf_mmap *md = &evlist->mmap[idx];
-       u64 head = perf_mmap__read_head(md);
+       u64 head;
        u64 old = md->prev;
        unsigned char *data = md->base + page_size;
        union perf_event *event = NULL;
 
+       /*
+        * Check if event was unmapped due to a POLLHUP/POLLERR.
+        */
+       if (!atomic_read(&md->refcnt))
+               return NULL;
+
+       head = perf_mmap__read_head(md);
        if (evlist->overwrite) {
                /*
                 * If we're further behind than half the buffer, there's a chance
@@ -695,19 +711,19 @@ union perf_event *perf_evlist__mmap_read(struct perf_evlist *evlist, int idx)
 
 static bool perf_mmap__empty(struct perf_mmap *md)
 {
-       return perf_mmap__read_head(md) == md->prev;
+       return perf_mmap__read_head(md) == md->prev && !md->auxtrace_mmap.base;
 }
 
 static void perf_evlist__mmap_get(struct perf_evlist *evlist, int idx)
 {
-       ++evlist->mmap[idx].refcnt;
+       atomic_inc(&evlist->mmap[idx].refcnt);
 }
 
 static void perf_evlist__mmap_put(struct perf_evlist *evlist, int idx)
 {
-       BUG_ON(evlist->mmap[idx].refcnt == 0);
+       BUG_ON(atomic_read(&evlist->mmap[idx].refcnt) == 0);
 
-       if (--evlist->mmap[idx].refcnt == 0)
+       if (atomic_dec_and_test(&evlist->mmap[idx].refcnt))
                __perf_evlist__munmap(evlist, idx);
 }
 
@@ -721,17 +737,46 @@ void perf_evlist__mmap_consume(struct perf_evlist *evlist, int idx)
                perf_mmap__write_tail(md, old);
        }
 
-       if (md->refcnt == 1 && perf_mmap__empty(md))
+       if (atomic_read(&md->refcnt) == 1 && perf_mmap__empty(md))
                perf_evlist__mmap_put(evlist, idx);
 }
 
+int __weak auxtrace_mmap__mmap(struct auxtrace_mmap *mm __maybe_unused,
+                              struct auxtrace_mmap_params *mp __maybe_unused,
+                              void *userpg __maybe_unused,
+                              int fd __maybe_unused)
+{
+       return 0;
+}
+
+void __weak auxtrace_mmap__munmap(struct auxtrace_mmap *mm __maybe_unused)
+{
+}
+
+void __weak auxtrace_mmap_params__init(
+                       struct auxtrace_mmap_params *mp __maybe_unused,
+                       off_t auxtrace_offset __maybe_unused,
+                       unsigned int auxtrace_pages __maybe_unused,
+                       bool auxtrace_overwrite __maybe_unused)
+{
+}
+
+void __weak auxtrace_mmap_params__set_idx(
+                       struct auxtrace_mmap_params *mp __maybe_unused,
+                       struct perf_evlist *evlist __maybe_unused,
+                       int idx __maybe_unused,
+                       bool per_cpu __maybe_unused)
+{
+}
+
 static void __perf_evlist__munmap(struct perf_evlist *evlist, int idx)
 {
        if (evlist->mmap[idx].base != NULL) {
                munmap(evlist->mmap[idx].base, evlist->mmap_len);
                evlist->mmap[idx].base = NULL;
-               evlist->mmap[idx].refcnt = 0;
+               atomic_set(&evlist->mmap[idx].refcnt, 0);
        }
+       auxtrace_mmap__munmap(&evlist->mmap[idx].auxtrace_mmap);
 }
 
 void perf_evlist__munmap(struct perf_evlist *evlist)
@@ -759,6 +804,7 @@ static int perf_evlist__alloc_mmap(struct perf_evlist *evlist)
 struct mmap_params {
        int prot;
        int mask;
+       struct auxtrace_mmap_params auxtrace_mp;
 };
 
 static int __perf_evlist__mmap(struct perf_evlist *evlist, int idx,
@@ -777,7 +823,7 @@ static int __perf_evlist__mmap(struct perf_evlist *evlist, int idx,
         * evlist layer can't just drop it when filtering events in
         * perf_evlist__filter_pollfd().
         */
-       evlist->mmap[idx].refcnt = 2;
+       atomic_set(&evlist->mmap[idx].refcnt, 2);
        evlist->mmap[idx].prev = 0;
        evlist->mmap[idx].mask = mp->mask;
        evlist->mmap[idx].base = mmap(NULL, evlist->mmap_len, mp->prot,
@@ -789,6 +835,10 @@ static int __perf_evlist__mmap(struct perf_evlist *evlist, int idx,
                return -1;
        }
 
+       if (auxtrace_mmap__mmap(&evlist->mmap[idx].auxtrace_mmap,
+                               &mp->auxtrace_mp, evlist->mmap[idx].base, fd))
+               return -1;
+
        return 0;
 }
 
@@ -853,6 +903,9 @@ static int perf_evlist__mmap_per_cpu(struct perf_evlist *evlist,
        for (cpu = 0; cpu < nr_cpus; cpu++) {
                int output = -1;
 
+               auxtrace_mmap_params__set_idx(&mp->auxtrace_mp, evlist, cpu,
+                                             true);
+
                for (thread = 0; thread < nr_threads; thread++) {
                        if (perf_evlist__mmap_per_evsel(evlist, cpu, mp, cpu,
                                                        thread, &output))
@@ -878,6 +931,9 @@ static int perf_evlist__mmap_per_thread(struct perf_evlist *evlist,
        for (thread = 0; thread < nr_threads; thread++) {
                int output = -1;
 
+               auxtrace_mmap_params__set_idx(&mp->auxtrace_mp, evlist, thread,
+                                             false);
+
                if (perf_evlist__mmap_per_evsel(evlist, thread, mp, 0, thread,
                                                &output))
                        goto out_unmap;
@@ -960,10 +1016,8 @@ static long parse_pages_arg(const char *str, unsigned long min,
        return pages;
 }
 
-int perf_evlist__parse_mmap_pages(const struct option *opt, const char *str,
-                                 int unset __maybe_unused)
+int __perf_evlist__parse_mmap_pages(unsigned int *mmap_pages, const char *str)
 {
-       unsigned int *mmap_pages = opt->value;
        unsigned long max = UINT_MAX;
        long pages;
 
@@ -980,20 +1034,32 @@ int perf_evlist__parse_mmap_pages(const struct option *opt, const char *str,
        return 0;
 }
 
+int perf_evlist__parse_mmap_pages(const struct option *opt, const char *str,
+                                 int unset __maybe_unused)
+{
+       return __perf_evlist__parse_mmap_pages(opt->value, str);
+}
+
 /**
- * perf_evlist__mmap - Create mmaps to receive events.
+ * perf_evlist__mmap_ex - Create mmaps to receive events.
  * @evlist: list of events
  * @pages: map length in pages
  * @overwrite: overwrite older events?
+ * @auxtrace_pages - auxtrace map length in pages
+ * @auxtrace_overwrite - overwrite older auxtrace data?
  *
  * If @overwrite is %false the user needs to signal event consumption using
  * perf_mmap__write_tail().  Using perf_evlist__mmap_read() does this
  * automatically.
  *
+ * Similarly, if @auxtrace_overwrite is %false the user needs to signal data
+ * consumption using auxtrace_mmap__write_tail().
+ *
  * Return: %0 on success, negative error code otherwise.
  */
-int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages,
-                     bool overwrite)
+int perf_evlist__mmap_ex(struct perf_evlist *evlist, unsigned int pages,
+                        bool overwrite, unsigned int auxtrace_pages,
+                        bool auxtrace_overwrite)
 {
        struct perf_evsel *evsel;
        const struct cpu_map *cpus = evlist->cpus;
@@ -1013,6 +1079,9 @@ int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages,
        pr_debug("mmap size %zuB\n", evlist->mmap_len);
        mp.mask = evlist->mmap_len - page_size - 1;
 
+       auxtrace_mmap_params__init(&mp.auxtrace_mp, evlist->mmap_len,
+                                  auxtrace_pages, auxtrace_overwrite);
+
        evlist__for_each(evlist, evsel) {
                if ((evsel->attr.read_format & PERF_FORMAT_ID) &&
                    evsel->sample_id == NULL &&
@@ -1026,6 +1095,12 @@ int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages,
        return perf_evlist__mmap_per_cpu(evlist, &mp);
 }
 
+int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages,
+                     bool overwrite)
+{
+       return perf_evlist__mmap_ex(evlist, pages, overwrite, 0, false);
+}
+
 int perf_evlist__create_maps(struct perf_evlist *evlist, struct target *target)
 {
        evlist->threads = thread_map__new_str(target->pid, target->tid,
index b5cce95d644e0c3af3c04d5edd91cdaf3aabb20d..a8489b9d2812baecf1ba4709ff3b6da9787adda0 100644 (file)
@@ -1,6 +1,7 @@
 #ifndef __PERF_EVLIST_H
 #define __PERF_EVLIST_H 1
 
+#include <linux/atomic.h>
 #include <linux/list.h>
 #include <api/fd/array.h>
 #include <stdio.h>
@@ -8,6 +9,7 @@
 #include "event.h"
 #include "evsel.h"
 #include "util.h"
+#include "auxtrace.h"
 #include <unistd.h>
 
 struct pollfd;
@@ -26,8 +28,9 @@ struct record_opts;
 struct perf_mmap {
        void             *base;
        int              mask;
-       int              refcnt;
+       atomic_t         refcnt;
        u64              prev;
+       struct auxtrace_mmap auxtrace_mmap;
        char             event_copy[PERF_SAMPLE_MAX_SIZE] __attribute__((aligned(8)));
 };
 
@@ -37,6 +40,8 @@ struct perf_evlist {
        int              nr_entries;
        int              nr_groups;
        int              nr_mmaps;
+       bool             overwrite;
+       bool             enabled;
        size_t           mmap_len;
        int              id_pos;
        int              is_pos;
@@ -45,7 +50,6 @@ struct perf_evlist {
                int     cork_fd;
                pid_t   pid;
        } workload;
-       bool             overwrite;
        struct fdarray   pollfd;
        struct perf_mmap *mmap;
        struct thread_map *threads;
@@ -122,16 +126,21 @@ int perf_evlist__start_workload(struct perf_evlist *evlist);
 
 struct option;
 
+int __perf_evlist__parse_mmap_pages(unsigned int *mmap_pages, const char *str);
 int perf_evlist__parse_mmap_pages(const struct option *opt,
                                  const char *str,
                                  int unset);
 
+int perf_evlist__mmap_ex(struct perf_evlist *evlist, unsigned int pages,
+                        bool overwrite, unsigned int auxtrace_pages,
+                        bool auxtrace_overwrite);
 int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages,
                      bool overwrite);
 void perf_evlist__munmap(struct perf_evlist *evlist);
 
 void perf_evlist__disable(struct perf_evlist *evlist);
 void perf_evlist__enable(struct perf_evlist *evlist);
+void perf_evlist__toggle_enable(struct perf_evlist *evlist);
 
 int perf_evlist__disable_event(struct perf_evlist *evlist,
                               struct perf_evsel *evsel);
index 33e3fd8c2e682d19c8c8dbda12b9815199ef47bf..33449decf7bd2c24d981fdf30c10042063a503ee 100644 (file)
@@ -26,6 +26,7 @@
 #include "perf_regs.h"
 #include "debug.h"
 #include "trace-event.h"
+#include "stat.h"
 
 static struct {
        bool sample_id_all;
@@ -851,19 +852,6 @@ int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads)
        return 0;
 }
 
-void perf_evsel__reset_counts(struct perf_evsel *evsel, int ncpus)
-{
-       memset(evsel->counts, 0, (sizeof(*evsel->counts) +
-                                (ncpus * sizeof(struct perf_counts_values))));
-}
-
-int perf_evsel__alloc_counts(struct perf_evsel *evsel, int ncpus)
-{
-       evsel->counts = zalloc((sizeof(*evsel->counts) +
-                               (ncpus * sizeof(struct perf_counts_values))));
-       return evsel->counts != NULL ? 0 : -ENOMEM;
-}
-
 static void perf_evsel__free_fd(struct perf_evsel *evsel)
 {
        xyarray__delete(evsel->fd);
@@ -891,11 +879,6 @@ void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads)
                }
 }
 
-void perf_evsel__free_counts(struct perf_evsel *evsel)
-{
-       zfree(&evsel->counts);
-}
-
 void perf_evsel__exit(struct perf_evsel *evsel)
 {
        assert(list_empty(&evsel->node));
@@ -1058,7 +1041,7 @@ static void __p_read_format(char *buf, size_t size, u64 value)
 
 #define BUF_SIZE               1024
 
-#define p_hex(val)             snprintf(buf, BUF_SIZE, "%"PRIx64, (uint64_t)(val))
+#define p_hex(val)             snprintf(buf, BUF_SIZE, "%#"PRIx64, (uint64_t)(val))
 #define p_unsigned(val)                snprintf(buf, BUF_SIZE, "%"PRIu64, (uint64_t)(val))
 #define p_signed(val)          snprintf(buf, BUF_SIZE, "%"PRId64, (int64_t)(val))
 #define p_sample_type(val)     __p_sample_type(buf, BUF_SIZE, val)
@@ -1121,6 +1104,7 @@ int perf_event_attr__fprintf(FILE *fp, struct perf_event_attr *attr,
        PRINT_ATTRf(sample_stack_user, p_unsigned);
        PRINT_ATTRf(clockid, p_signed);
        PRINT_ATTRf(sample_regs_intr, p_hex);
+       PRINT_ATTRf(aux_watermark, p_unsigned);
 
        return ret;
 }
@@ -2148,7 +2132,9 @@ int perf_evsel__open_strerror(struct perf_evsel *evsel, struct target *target,
        case EMFILE:
                return scnprintf(msg, size, "%s",
                         "Too many events are opened.\n"
-                        "Try again after reducing the number of events.");
+                        "Probably the maximum number of open file descriptors has been reached.\n"
+                        "Hint: Try again after reducing the number of events.\n"
+                        "Hint: Try increasing the limit with 'ulimit -n <limit>'");
        case ENODEV:
                if (target->cpu_list)
                        return scnprintf(msg, size, "%s",
index e486151b03089720eed68436bbae7a454defb3ec..bb0579e8a10a4556119c5aa313cd22ddacdbc220 100644 (file)
@@ -73,7 +73,6 @@ struct perf_evsel {
        char                    *name;
        double                  scale;
        const char              *unit;
-       bool                    snapshot;
        struct event_format     *tp_format;
        union {
                void            *priv;
@@ -86,6 +85,7 @@ struct perf_evsel {
        unsigned int            sample_size;
        int                     id_pos;
        int                     is_pos;
+       bool                    snapshot;
        bool                    supported;
        bool                    needs_swap;
        bool                    no_aux_samples;
@@ -93,11 +93,11 @@ struct perf_evsel {
        bool                    system_wide;
        bool                    tracking;
        bool                    per_pkg;
-       unsigned long           *per_pkg_mask;
        /* parse modifier helper */
        int                     exclude_GH;
        int                     nr_members;
        int                     sample_read;
+       unsigned long           *per_pkg_mask;
        struct perf_evsel       *leader;
        char                    *group_name;
 };
@@ -170,9 +170,6 @@ const char *perf_evsel__group_name(struct perf_evsel *evsel);
 int perf_evsel__group_desc(struct perf_evsel *evsel, char *buf, size_t size);
 
 int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads);
-int perf_evsel__alloc_counts(struct perf_evsel *evsel, int ncpus);
-void perf_evsel__reset_counts(struct perf_evsel *evsel, int ncpus);
-void perf_evsel__free_counts(struct perf_evsel *evsel);
 void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads);
 
 void __perf_evsel__set_sample_bit(struct perf_evsel *evsel,
index 918fd8ae2d80bca007a8b6f7e2f8488fed0771a3..21a77e7a171e8aa0664d5caf0737f141bc96e62f 100644 (file)
@@ -869,6 +869,20 @@ static int write_branch_stack(int fd __maybe_unused,
        return 0;
 }
 
+static int write_auxtrace(int fd, struct perf_header *h,
+                         struct perf_evlist *evlist __maybe_unused)
+{
+       struct perf_session *session;
+       int err;
+
+       session = container_of(h, struct perf_session, header);
+
+       err = auxtrace_index__write(fd, &session->auxtrace_index);
+       if (err < 0)
+               pr_err("Failed to write auxtrace index\n");
+       return err;
+}
+
 static void print_hostname(struct perf_header *ph, int fd __maybe_unused,
                           FILE *fp)
 {
@@ -1151,6 +1165,12 @@ static void print_branch_stack(struct perf_header *ph __maybe_unused,
        fprintf(fp, "# contains samples with branch stack\n");
 }
 
+static void print_auxtrace(struct perf_header *ph __maybe_unused,
+                          int fd __maybe_unused, FILE *fp)
+{
+       fprintf(fp, "# contains AUX area data (e.g. instruction trace)\n");
+}
+
 static void print_pmu_mappings(struct perf_header *ph, int fd __maybe_unused,
                               FILE *fp)
 {
@@ -1218,9 +1238,8 @@ static int __event_process_build_id(struct build_id_event *bev,
                                    struct perf_session *session)
 {
        int err = -1;
-       struct dsos *dsos;
        struct machine *machine;
-       u16 misc;
+       u16 cpumode;
        struct dso *dso;
        enum dso_kernel_type dso_type;
 
@@ -1228,39 +1247,37 @@ static int __event_process_build_id(struct build_id_event *bev,
        if (!machine)
                goto out;
 
-       misc = bev->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
+       cpumode = bev->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
 
-       switch (misc) {
+       switch (cpumode) {
        case PERF_RECORD_MISC_KERNEL:
                dso_type = DSO_TYPE_KERNEL;
-               dsos = &machine->kernel_dsos;
                break;
        case PERF_RECORD_MISC_GUEST_KERNEL:
                dso_type = DSO_TYPE_GUEST_KERNEL;
-               dsos = &machine->kernel_dsos;
                break;
        case PERF_RECORD_MISC_USER:
        case PERF_RECORD_MISC_GUEST_USER:
                dso_type = DSO_TYPE_USER;
-               dsos = &machine->user_dsos;
                break;
        default:
                goto out;
        }
 
-       dso = __dsos__findnew(dsos, filename);
+       dso = machine__findnew_dso(machine, filename);
        if (dso != NULL) {
                char sbuild_id[BUILD_ID_SIZE * 2 + 1];
 
                dso__set_build_id(dso, &bev->build_id);
 
-               if (!is_kernel_module(filename))
+               if (!is_kernel_module(filename, cpumode))
                        dso->kernel = dso_type;
 
                build_id__sprintf(dso->build_id, sizeof(dso->build_id),
                                  sbuild_id);
                pr_debug("build id event received for %s: %s\n",
                         dso->long_name, sbuild_id);
+               dso__put(dso);
        }
 
        err = 0;
@@ -1821,6 +1838,22 @@ out_free:
        return ret;
 }
 
+static int process_auxtrace(struct perf_file_section *section,
+                           struct perf_header *ph, int fd,
+                           void *data __maybe_unused)
+{
+       struct perf_session *session;
+       int err;
+
+       session = container_of(ph, struct perf_session, header);
+
+       err = auxtrace_index__process(fd, section->size, session,
+                                     ph->needs_swap);
+       if (err < 0)
+               pr_err("Failed to process auxtrace index\n");
+       return err;
+}
+
 struct feature_ops {
        int (*write)(int fd, struct perf_header *h, struct perf_evlist *evlist);
        void (*print)(struct perf_header *h, int fd, FILE *fp);
@@ -1861,6 +1894,7 @@ static const struct feature_ops feat_ops[HEADER_LAST_FEATURE] = {
        FEAT_OPA(HEADER_BRANCH_STACK,   branch_stack),
        FEAT_OPP(HEADER_PMU_MAPPINGS,   pmu_mappings),
        FEAT_OPP(HEADER_GROUP_DESC,     group_desc),
+       FEAT_OPP(HEADER_AUXTRACE,       auxtrace),
 };
 
 struct header_print_data {
index 3bb90ac172a1ba0c8429494f4ebf501909a55274..d4d57962c59129d6121b61921b1c1bf58fe2d12a 100644 (file)
@@ -30,6 +30,7 @@ enum {
        HEADER_BRANCH_STACK,
        HEADER_PMU_MAPPINGS,
        HEADER_GROUP_DESC,
+       HEADER_AUXTRACE,
        HEADER_LAST_FEATURE,
        HEADER_FEAT_BITS        = 256,
 };
index cc22b9158b93c41fd0d44cd451ba189c56bc19dd..6f28d53d4e46093293e71363d9aa5e7c1e0b23f5 100644 (file)
@@ -313,8 +313,7 @@ static struct hist_entry *hist_entry__new(struct hist_entry *template,
                                memset(&he->stat, 0, sizeof(he->stat));
                }
 
-               if (he->ms.map)
-                       he->ms.map->referenced = true;
+               map__get(he->ms.map);
 
                if (he->branch_info) {
                        /*
@@ -324,6 +323,7 @@ static struct hist_entry *hist_entry__new(struct hist_entry *template,
                         */
                        he->branch_info = malloc(sizeof(*he->branch_info));
                        if (he->branch_info == NULL) {
+                               map__zput(he->ms.map);
                                free(he->stat_acc);
                                free(he);
                                return NULL;
@@ -332,17 +332,13 @@ static struct hist_entry *hist_entry__new(struct hist_entry *template,
                        memcpy(he->branch_info, template->branch_info,
                               sizeof(*he->branch_info));
 
-                       if (he->branch_info->from.map)
-                               he->branch_info->from.map->referenced = true;
-                       if (he->branch_info->to.map)
-                               he->branch_info->to.map->referenced = true;
+                       map__get(he->branch_info->from.map);
+                       map__get(he->branch_info->to.map);
                }
 
                if (he->mem_info) {
-                       if (he->mem_info->iaddr.map)
-                               he->mem_info->iaddr.map->referenced = true;
-                       if (he->mem_info->daddr.map)
-                               he->mem_info->daddr.map->referenced = true;
+                       map__get(he->mem_info->iaddr.map);
+                       map__get(he->mem_info->daddr.map);
                }
 
                if (symbol_conf.use_callchain)
@@ -362,10 +358,10 @@ static u8 symbol__parent_filter(const struct symbol *parent)
        return 0;
 }
 
-static struct hist_entry *add_hist_entry(struct hists *hists,
-                                        struct hist_entry *entry,
-                                        struct addr_location *al,
-                                        bool sample_self)
+static struct hist_entry *hists__findnew_entry(struct hists *hists,
+                                              struct hist_entry *entry,
+                                              struct addr_location *al,
+                                              bool sample_self)
 {
        struct rb_node **p;
        struct rb_node *parent = NULL;
@@ -407,9 +403,8 @@ static struct hist_entry *add_hist_entry(struct hists *hists,
                         * the history counter to increment.
                         */
                        if (he->ms.map != entry->ms.map) {
-                               he->ms.map = entry->ms.map;
-                               if (he->ms.map)
-                                       he->ms.map->referenced = true;
+                               map__put(he->ms.map);
+                               he->ms.map = map__get(entry->ms.map);
                        }
                        goto out;
                }
@@ -468,7 +463,7 @@ struct hist_entry *__hists__add_entry(struct hists *hists,
                .transaction = transaction,
        };
 
-       return add_hist_entry(hists, &entry, al, sample_self);
+       return hists__findnew_entry(hists, &entry, al, sample_self);
 }
 
 static int
@@ -548,9 +543,9 @@ iter_finish_mem_entry(struct hist_entry_iter *iter,
 
 out:
        /*
-        * We don't need to free iter->priv (mem_info) here since
-        * the mem info was either already freed in add_hist_entry() or
-        * passed to a new hist entry by hist_entry__new().
+        * We don't need to free iter->priv (mem_info) here since the mem info
+        * was either already freed in hists__findnew_entry() or passed to a
+        * new hist entry by hist_entry__new().
         */
        iter->priv = NULL;
 
@@ -851,19 +846,15 @@ const struct hist_iter_ops hist_iter_cumulative = {
 };
 
 int hist_entry_iter__add(struct hist_entry_iter *iter, struct addr_location *al,
-                        struct perf_evsel *evsel, struct perf_sample *sample,
                         int max_stack_depth, void *arg)
 {
        int err, err2;
 
-       err = sample__resolve_callchain(sample, &iter->parent, evsel, al,
-                                       max_stack_depth);
+       err = sample__resolve_callchain(iter->sample, &iter->parent,
+                                       iter->evsel, al, max_stack_depth);
        if (err)
                return err;
 
-       iter->evsel = evsel;
-       iter->sample = sample;
-
        err = iter->ops->prepare_entry(iter, al);
        if (err)
                goto out;
@@ -937,8 +928,20 @@ hist_entry__collapse(struct hist_entry *left, struct hist_entry *right)
 void hist_entry__delete(struct hist_entry *he)
 {
        thread__zput(he->thread);
-       zfree(&he->branch_info);
-       zfree(&he->mem_info);
+       map__zput(he->ms.map);
+
+       if (he->branch_info) {
+               map__zput(he->branch_info->from.map);
+               map__zput(he->branch_info->to.map);
+               zfree(&he->branch_info);
+       }
+
+       if (he->mem_info) {
+               map__zput(he->mem_info->iaddr.map);
+               map__zput(he->mem_info->daddr.map);
+               zfree(&he->mem_info);
+       }
+
        zfree(&he->stat_acc);
        free_srcline(he->srcline);
        free_callchain(he->callchain);
@@ -1163,7 +1166,7 @@ static void hists__remove_entry_filter(struct hists *hists, struct hist_entry *h
                return;
 
        /* force fold unfiltered entry for simplicity */
-       h->ms.unfolded = false;
+       h->unfolded = false;
        h->row_offset = 0;
        h->nr_rows = 0;
 
index 9f31b89a527a2e8f9d02da1993cdff13709b84c1..5ed8d9c229814d9c6942ce3528898bbd9de1cb79 100644 (file)
@@ -111,7 +111,6 @@ struct hist_entry *__hists__add_entry(struct hists *hists,
                                      u64 weight, u64 transaction,
                                      bool sample_self);
 int hist_entry_iter__add(struct hist_entry_iter *iter, struct addr_location *al,
-                        struct perf_evsel *evsel, struct perf_sample *sample,
                         int max_stack_depth, void *arg);
 
 int64_t hist_entry__cmp(struct hist_entry *left, struct hist_entry *right);
diff --git a/tools/perf/util/include/linux/poison.h b/tools/perf/util/include/linux/poison.h
deleted file mode 100644 (file)
index fef6dbc..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include "../../../../include/linux/poison.h"
index 2a030c5af3aa2062082d87b1604b9980a1006844..f06d89f0b8678d8407c91db17e8c26250376c5a1 100644 (file)
@@ -1,2 +1,16 @@
+#ifndef __TOOLS_LINUX_PERF_RBTREE_H
+#define __TOOLS_LINUX_PERF_RBTREE_H
 #include <stdbool.h>
 #include "../../../../include/linux/rbtree.h"
+
+/*
+ * Handy for checking that we are not deleting an entry that is
+ * already in a list, found in block/{blk-throttle,cfq-iosched}.c,
+ * probably should be moved to lib/rbtree.c...
+ */
+static inline void rb_erase_init(struct rb_node *n, struct rb_root *root)
+{
+       rb_erase(n, root);
+       RB_CLEAR_NODE(n);
+}
+#endif /* __TOOLS_LINUX_PERF_RBTREE_H */
index 527e032e24f6e648e258b08b379e55fc6dcf8e5a..4744673aff1b287de3a091a40edade2a709a8e52 100644 (file)
 #include "unwind.h"
 #include "linux/hash.h"
 
+static void __machine__remove_thread(struct machine *machine, struct thread *th, bool lock);
+
 static void dsos__init(struct dsos *dsos)
 {
        INIT_LIST_HEAD(&dsos->head);
        dsos->root = RB_ROOT;
+       pthread_rwlock_init(&dsos->lock, NULL);
 }
 
 int machine__init(struct machine *machine, const char *root_dir, pid_t pid)
 {
        map_groups__init(&machine->kmaps, machine);
        RB_CLEAR_NODE(&machine->rb_node);
-       dsos__init(&machine->user_dsos);
-       dsos__init(&machine->kernel_dsos);
+       dsos__init(&machine->dsos);
 
        machine->threads = RB_ROOT;
+       pthread_rwlock_init(&machine->threads_lock, NULL);
        INIT_LIST_HEAD(&machine->dead_threads);
        machine->last_match = NULL;
 
@@ -54,6 +57,7 @@ int machine__init(struct machine *machine, const char *root_dir, pid_t pid)
 
                snprintf(comm, sizeof(comm), "[guest/%d]", pid);
                thread__set_comm(thread, comm, 0);
+               thread__put(thread);
        }
 
        machine->current_tid = NULL;
@@ -78,37 +82,50 @@ out_delete:
        return NULL;
 }
 
-static void dsos__delete(struct dsos *dsos)
+static void dsos__purge(struct dsos *dsos)
 {
        struct dso *pos, *n;
 
+       pthread_rwlock_wrlock(&dsos->lock);
+
        list_for_each_entry_safe(pos, n, &dsos->head, node) {
                RB_CLEAR_NODE(&pos->rb_node);
-               list_del(&pos->node);
-               dso__delete(pos);
+               list_del_init(&pos->node);
+               dso__put(pos);
        }
+
+       pthread_rwlock_unlock(&dsos->lock);
+}
+
+static void dsos__exit(struct dsos *dsos)
+{
+       dsos__purge(dsos);
+       pthread_rwlock_destroy(&dsos->lock);
 }
 
 void machine__delete_threads(struct machine *machine)
 {
-       struct rb_node *nd = rb_first(&machine->threads);
+       struct rb_node *nd;
 
+       pthread_rwlock_wrlock(&machine->threads_lock);
+       nd = rb_first(&machine->threads);
        while (nd) {
                struct thread *t = rb_entry(nd, struct thread, rb_node);
 
                nd = rb_next(nd);
-               machine__remove_thread(machine, t);
+               __machine__remove_thread(machine, t, false);
        }
+       pthread_rwlock_unlock(&machine->threads_lock);
 }
 
 void machine__exit(struct machine *machine)
 {
        map_groups__exit(&machine->kmaps);
-       dsos__delete(&machine->user_dsos);
-       dsos__delete(&machine->kernel_dsos);
-       vdso__exit(machine);
+       dsos__exit(&machine->dsos);
+       machine__exit_vdso(machine);
        zfree(&machine->root_dir);
        zfree(&machine->current_tid);
+       pthread_rwlock_destroy(&machine->threads_lock);
 }
 
 void machine__delete(struct machine *machine)
@@ -303,7 +320,7 @@ static void machine__update_thread_pid(struct machine *machine,
        if (th->pid_ == th->tid)
                return;
 
-       leader = machine__findnew_thread(machine, th->pid_, th->pid_);
+       leader = __machine__findnew_thread(machine, th->pid_, th->pid_);
        if (!leader)
                goto out_err;
 
@@ -325,7 +342,7 @@ static void machine__update_thread_pid(struct machine *machine,
                if (!map_groups__empty(th->mg))
                        pr_err("Discarding thread maps for %d:%d\n",
                               th->pid_, th->tid);
-               map_groups__delete(th->mg);
+               map_groups__put(th->mg);
        }
 
        th->mg = map_groups__get(leader->mg);
@@ -336,9 +353,9 @@ out_err:
        pr_err("Failed to join map groups for %d:%d\n", th->pid_, th->tid);
 }
 
-static struct thread *__machine__findnew_thread(struct machine *machine,
-                                               pid_t pid, pid_t tid,
-                                               bool create)
+static struct thread *____machine__findnew_thread(struct machine *machine,
+                                                 pid_t pid, pid_t tid,
+                                                 bool create)
 {
        struct rb_node **p = &machine->threads.rb_node;
        struct rb_node *parent = NULL;
@@ -356,7 +373,7 @@ static struct thread *__machine__findnew_thread(struct machine *machine,
                        return th;
                }
 
-               thread__zput(machine->last_match);
+               machine->last_match = NULL;
        }
 
        while (*p != NULL) {
@@ -364,7 +381,7 @@ static struct thread *__machine__findnew_thread(struct machine *machine,
                th = rb_entry(parent, struct thread, rb_node);
 
                if (th->tid == tid) {
-                       machine->last_match = thread__get(th);
+                       machine->last_match = th;
                        machine__update_thread_pid(machine, th, pid);
                        return th;
                }
@@ -392,7 +409,8 @@ static struct thread *__machine__findnew_thread(struct machine *machine,
                 * leader and that would screwed the rb tree.
                 */
                if (thread__init_map_groups(th, machine)) {
-                       rb_erase(&th->rb_node, &machine->threads);
+                       rb_erase_init(&th->rb_node, &machine->threads);
+                       RB_CLEAR_NODE(&th->rb_node);
                        thread__delete(th);
                        return NULL;
                }
@@ -400,22 +418,36 @@ static struct thread *__machine__findnew_thread(struct machine *machine,
                 * It is now in the rbtree, get a ref
                 */
                thread__get(th);
-               machine->last_match = thread__get(th);
+               machine->last_match = th;
        }
 
        return th;
 }
 
+struct thread *__machine__findnew_thread(struct machine *machine, pid_t pid, pid_t tid)
+{
+       return ____machine__findnew_thread(machine, pid, tid, true);
+}
+
 struct thread *machine__findnew_thread(struct machine *machine, pid_t pid,
                                       pid_t tid)
 {
-       return __machine__findnew_thread(machine, pid, tid, true);
+       struct thread *th;
+
+       pthread_rwlock_wrlock(&machine->threads_lock);
+       th = thread__get(__machine__findnew_thread(machine, pid, tid));
+       pthread_rwlock_unlock(&machine->threads_lock);
+       return th;
 }
 
 struct thread *machine__find_thread(struct machine *machine, pid_t pid,
                                    pid_t tid)
 {
-       return __machine__findnew_thread(machine, pid, tid, false);
+       struct thread *th;
+       pthread_rwlock_rdlock(&machine->threads_lock);
+       th =  thread__get(____machine__findnew_thread(machine, pid, tid, false));
+       pthread_rwlock_unlock(&machine->threads_lock);
+       return th;
 }
 
 struct comm *machine__thread_exec_comm(struct machine *machine,
@@ -434,6 +466,7 @@ int machine__process_comm_event(struct machine *machine, union perf_event *event
                                                        event->comm.pid,
                                                        event->comm.tid);
        bool exec = event->header.misc & PERF_RECORD_MISC_COMM_EXEC;
+       int err = 0;
 
        if (exec)
                machine->comm_exec = true;
@@ -444,10 +477,12 @@ int machine__process_comm_event(struct machine *machine, union perf_event *event
        if (thread == NULL ||
            __thread__set_comm(thread, event->comm.comm, sample->time, exec)) {
                dump_printf("problem processing PERF_RECORD_COMM, skipping event.\n");
-               return -1;
+               err = -1;
        }
 
-       return 0;
+       thread__put(thread);
+
+       return err;
 }
 
 int machine__process_lost_event(struct machine *machine __maybe_unused,
@@ -458,17 +493,27 @@ int machine__process_lost_event(struct machine *machine __maybe_unused,
        return 0;
 }
 
-static struct dso*
-machine__module_dso(struct machine *machine, struct kmod_path *m,
-                   const char *filename)
+int machine__process_lost_samples_event(struct machine *machine __maybe_unused,
+                                       union perf_event *event, struct perf_sample *sample)
+{
+       dump_printf(": id:%" PRIu64 ": lost samples :%" PRIu64 "\n",
+                   sample->id, event->lost_samples.lost);
+       return 0;
+}
+
+static struct dso *machine__findnew_module_dso(struct machine *machine,
+                                              struct kmod_path *m,
+                                              const char *filename)
 {
        struct dso *dso;
 
-       dso = dsos__find(&machine->kernel_dsos, m->name, true);
+       pthread_rwlock_wrlock(&machine->dsos.lock);
+
+       dso = __dsos__find(&machine->dsos, m->name, true);
        if (!dso) {
-               dso = dsos__addnew(&machine->kernel_dsos, m->name);
+               dso = __dsos__addnew(&machine->dsos, m->name);
                if (dso == NULL)
-                       return NULL;
+                       goto out_unlock;
 
                if (machine__is_host(machine))
                        dso->symtab_type = DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE;
@@ -483,11 +528,30 @@ machine__module_dso(struct machine *machine, struct kmod_path *m,
                dso__set_long_name(dso, strdup(filename), true);
        }
 
+       dso__get(dso);
+out_unlock:
+       pthread_rwlock_unlock(&machine->dsos.lock);
        return dso;
 }
 
-struct map *machine__new_module(struct machine *machine, u64 start,
-                               const char *filename)
+int machine__process_aux_event(struct machine *machine __maybe_unused,
+                              union perf_event *event)
+{
+       if (dump_trace)
+               perf_event__fprintf_aux(event, stdout);
+       return 0;
+}
+
+int machine__process_itrace_start_event(struct machine *machine __maybe_unused,
+                                       union perf_event *event)
+{
+       if (dump_trace)
+               perf_event__fprintf_itrace_start(event, stdout);
+       return 0;
+}
+
+struct map *machine__findnew_module_map(struct machine *machine, u64 start,
+                                       const char *filename)
 {
        struct map *map = NULL;
        struct dso *dso;
@@ -501,7 +565,7 @@ struct map *machine__new_module(struct machine *machine, u64 start,
        if (map)
                goto out;
 
-       dso = machine__module_dso(machine, &m, filename);
+       dso = machine__findnew_module_dso(machine, &m, filename);
        if (dso == NULL)
                goto out;
 
@@ -519,13 +583,11 @@ out:
 size_t machines__fprintf_dsos(struct machines *machines, FILE *fp)
 {
        struct rb_node *nd;
-       size_t ret = __dsos__fprintf(&machines->host.kernel_dsos.head, fp) +
-                    __dsos__fprintf(&machines->host.user_dsos.head, fp);
+       size_t ret = __dsos__fprintf(&machines->host.dsos.head, fp);
 
        for (nd = rb_first(&machines->guests); nd; nd = rb_next(nd)) {
                struct machine *pos = rb_entry(nd, struct machine, rb_node);
-               ret += __dsos__fprintf(&pos->kernel_dsos.head, fp);
-               ret += __dsos__fprintf(&pos->user_dsos.head, fp);
+               ret += __dsos__fprintf(&pos->dsos.head, fp);
        }
 
        return ret;
@@ -534,8 +596,7 @@ size_t machines__fprintf_dsos(struct machines *machines, FILE *fp)
 size_t machine__fprintf_dsos_buildid(struct machine *m, FILE *fp,
                                     bool (skip)(struct dso *dso, int parm), int parm)
 {
-       return __dsos__fprintf_buildid(&m->kernel_dsos.head, fp, skip, parm) +
-              __dsos__fprintf_buildid(&m->user_dsos.head, fp, skip, parm);
+       return __dsos__fprintf_buildid(&m->dsos.head, fp, skip, parm);
 }
 
 size_t machines__fprintf_dsos_buildid(struct machines *machines, FILE *fp,
@@ -575,12 +636,16 @@ size_t machine__fprintf(struct machine *machine, FILE *fp)
        size_t ret = 0;
        struct rb_node *nd;
 
+       pthread_rwlock_rdlock(&machine->threads_lock);
+
        for (nd = rb_first(&machine->threads); nd; nd = rb_next(nd)) {
                struct thread *pos = rb_entry(nd, struct thread, rb_node);
 
                ret += thread__fprintf(pos, fp);
        }
 
+       pthread_rwlock_unlock(&machine->threads_lock);
+
        return ret;
 }
 
@@ -594,9 +659,8 @@ static struct dso *machine__get_kernel(struct machine *machine)
                if (!vmlinux_name)
                        vmlinux_name = "[kernel.kallsyms]";
 
-               kernel = dso__kernel_findnew(machine, vmlinux_name,
-                                            "[kernel]",
-                                            DSO_TYPE_KERNEL);
+               kernel = machine__findnew_kernel(machine, vmlinux_name,
+                                                "[kernel]", DSO_TYPE_KERNEL);
        } else {
                char bf[PATH_MAX];
 
@@ -606,9 +670,9 @@ static struct dso *machine__get_kernel(struct machine *machine)
                        vmlinux_name = machine__mmap_name(machine, bf,
                                                          sizeof(bf));
 
-               kernel = dso__kernel_findnew(machine, vmlinux_name,
-                                            "[guest.kernel]",
-                                            DSO_TYPE_GUEST_KERNEL);
+               kernel = machine__findnew_kernel(machine, vmlinux_name,
+                                                "[guest.kernel]",
+                                                DSO_TYPE_GUEST_KERNEL);
        }
 
        if (kernel != NULL && (!kernel->has_build_id))
@@ -713,7 +777,6 @@ void machine__destroy_kernel_maps(struct machine *machine)
                                kmap->ref_reloc_sym = NULL;
                }
 
-               map__delete(machine->vmlinux_maps[type]);
                machine->vmlinux_maps[type] = NULL;
        }
 }
@@ -970,7 +1033,7 @@ static int machine__create_module(void *arg, const char *name, u64 start)
        struct machine *machine = arg;
        struct map *map;
 
-       map = machine__new_module(machine, start, name);
+       map = machine__findnew_module_map(machine, start, name);
        if (map == NULL)
                return -1;
 
@@ -1062,7 +1125,7 @@ static bool machine__uses_kcore(struct machine *machine)
 {
        struct dso *dso;
 
-       list_for_each_entry(dso, &machine->kernel_dsos.head, node) {
+       list_for_each_entry(dso, &machine->dsos.head, node) {
                if (dso__is_kcore(dso))
                        return true;
        }
@@ -1093,8 +1156,8 @@ static int machine__process_kernel_mmap_event(struct machine *machine,
                                strlen(kmmap_prefix) - 1) == 0;
        if (event->mmap.filename[0] == '/' ||
            (!is_kernel_mmap && event->mmap.filename[0] == '[')) {
-               map = machine__new_module(machine, event->mmap.start,
-                                         event->mmap.filename);
+               map = machine__findnew_module_map(machine, event->mmap.start,
+                                                 event->mmap.filename);
                if (map == NULL)
                        goto out_problem;
 
@@ -1109,23 +1172,48 @@ static int machine__process_kernel_mmap_event(struct machine *machine,
                struct dso *kernel = NULL;
                struct dso *dso;
 
-               list_for_each_entry(dso, &machine->kernel_dsos.head, node) {
-                       if (is_kernel_module(dso->long_name))
+               pthread_rwlock_rdlock(&machine->dsos.lock);
+
+               list_for_each_entry(dso, &machine->dsos.head, node) {
+
+                       /*
+                        * The cpumode passed to is_kernel_module is not the
+                        * cpumode of *this* event. If we insist on passing
+                        * correct cpumode to is_kernel_module, we should
+                        * record the cpumode when we adding this dso to the
+                        * linked list.
+                        *
+                        * However we don't really need passing correct
+                        * cpumode.  We know the correct cpumode must be kernel
+                        * mode (if not, we should not link it onto kernel_dsos
+                        * list).
+                        *
+                        * Therefore, we pass PERF_RECORD_MISC_CPUMODE_UNKNOWN.
+                        * is_kernel_module() treats it as a kernel cpumode.
+                        */
+
+                       if (!dso->kernel ||
+                           is_kernel_module(dso->long_name,
+                                            PERF_RECORD_MISC_CPUMODE_UNKNOWN))
                                continue;
 
+
                        kernel = dso;
                        break;
                }
 
+               pthread_rwlock_unlock(&machine->dsos.lock);
+
                if (kernel == NULL)
-                       kernel = __dsos__findnew(&machine->kernel_dsos,
-                                                kmmap_prefix);
+                       kernel = machine__findnew_dso(machine, kmmap_prefix);
                if (kernel == NULL)
                        goto out_problem;
 
                kernel->kernel = kernel_type;
-               if (__machine__create_kernel_maps(machine, kernel) < 0)
+               if (__machine__create_kernel_maps(machine, kernel) < 0) {
+                       dso__put(kernel);
                        goto out_problem;
+               }
 
                if (strstr(kernel->long_name, "vmlinux"))
                        dso__set_short_name(kernel, "[kernel.vmlinux]", false);
@@ -1197,11 +1285,15 @@ int machine__process_mmap2_event(struct machine *machine,
                        event->mmap2.filename, type, thread);
 
        if (map == NULL)
-               goto out_problem;
+               goto out_problem_map;
 
        thread__insert_map(thread, map);
+       thread__put(thread);
+       map__put(map);
        return 0;
 
+out_problem_map:
+       thread__put(thread);
 out_problem:
        dump_printf("problem processing PERF_RECORD_MMAP2, skipping event.\n");
        return 0;
@@ -1244,31 +1336,46 @@ int machine__process_mmap_event(struct machine *machine, union perf_event *event
                        type, thread);
 
        if (map == NULL)
-               goto out_problem;
+               goto out_problem_map;
 
        thread__insert_map(thread, map);
+       thread__put(thread);
+       map__put(map);
        return 0;
 
+out_problem_map:
+       thread__put(thread);
 out_problem:
        dump_printf("problem processing PERF_RECORD_MMAP, skipping event.\n");
        return 0;
 }
 
-void machine__remove_thread(struct machine *machine, struct thread *th)
+static void __machine__remove_thread(struct machine *machine, struct thread *th, bool lock)
 {
        if (machine->last_match == th)
-               thread__zput(machine->last_match);
+               machine->last_match = NULL;
 
-       rb_erase(&th->rb_node, &machine->threads);
+       BUG_ON(atomic_read(&th->refcnt) == 0);
+       if (lock)
+               pthread_rwlock_wrlock(&machine->threads_lock);
+       rb_erase_init(&th->rb_node, &machine->threads);
+       RB_CLEAR_NODE(&th->rb_node);
        /*
         * Move it first to the dead_threads list, then drop the reference,
         * if this is the last reference, then the thread__delete destructor
         * will be called and we will remove it from the dead_threads list.
         */
        list_add_tail(&th->node, &machine->dead_threads);
+       if (lock)
+               pthread_rwlock_unlock(&machine->threads_lock);
        thread__put(th);
 }
 
+void machine__remove_thread(struct machine *machine, struct thread *th)
+{
+       return __machine__remove_thread(machine, th, true);
+}
+
 int machine__process_fork_event(struct machine *machine, union perf_event *event,
                                struct perf_sample *sample)
 {
@@ -1278,10 +1385,13 @@ int machine__process_fork_event(struct machine *machine, union perf_event *event
        struct thread *parent = machine__findnew_thread(machine,
                                                        event->fork.ppid,
                                                        event->fork.ptid);
+       int err = 0;
 
        /* if a thread currently exists for the thread id remove it */
-       if (thread != NULL)
+       if (thread != NULL) {
                machine__remove_thread(machine, thread);
+               thread__put(thread);
+       }
 
        thread = machine__findnew_thread(machine, event->fork.pid,
                                         event->fork.tid);
@@ -1291,10 +1401,12 @@ int machine__process_fork_event(struct machine *machine, union perf_event *event
        if (thread == NULL || parent == NULL ||
            thread__fork(thread, parent, sample->time) < 0) {
                dump_printf("problem processing PERF_RECORD_FORK, skipping event.\n");
-               return -1;
+               err = -1;
        }
+       thread__put(thread);
+       thread__put(parent);
 
-       return 0;
+       return err;
 }
 
 int machine__process_exit_event(struct machine *machine, union perf_event *event,
@@ -1307,8 +1419,10 @@ int machine__process_exit_event(struct machine *machine, union perf_event *event
        if (dump_trace)
                perf_event__fprintf_task(event, stdout);
 
-       if (thread != NULL)
+       if (thread != NULL) {
                thread__exited(thread);
+               thread__put(thread);
+       }
 
        return 0;
 }
@@ -1331,6 +1445,13 @@ int machine__process_event(struct machine *machine, union perf_event *event,
                ret = machine__process_exit_event(machine, event, sample); break;
        case PERF_RECORD_LOST:
                ret = machine__process_lost_event(machine, event, sample); break;
+       case PERF_RECORD_AUX:
+               ret = machine__process_aux_event(machine, event); break;
+       case PERF_RECORD_ITRACE_START:
+               ret = machine__process_itrace_start_event(machine, event);
+       case PERF_RECORD_LOST_SAMPLES:
+               ret = machine__process_lost_samples_event(machine, event, sample); break;
+               break;
        default:
                ret = -1;
                break;
@@ -1769,14 +1890,36 @@ int machine__for_each_thread(struct machine *machine,
        return rc;
 }
 
+int machines__for_each_thread(struct machines *machines,
+                             int (*fn)(struct thread *thread, void *p),
+                             void *priv)
+{
+       struct rb_node *nd;
+       int rc = 0;
+
+       rc = machine__for_each_thread(&machines->host, fn, priv);
+       if (rc != 0)
+               return rc;
+
+       for (nd = rb_first(&machines->guests); nd; nd = rb_next(nd)) {
+               struct machine *machine = rb_entry(nd, struct machine, rb_node);
+
+               rc = machine__for_each_thread(machine, fn, priv);
+               if (rc != 0)
+                       return rc;
+       }
+       return rc;
+}
+
 int __machine__synthesize_threads(struct machine *machine, struct perf_tool *tool,
                                  struct target *target, struct thread_map *threads,
-                                 perf_event__handler_t process, bool data_mmap)
+                                 perf_event__handler_t process, bool data_mmap,
+                                 unsigned int proc_map_timeout)
 {
        if (target__has_task(target))
-               return perf_event__synthesize_thread_map(tool, threads, process, machine, data_mmap);
+               return perf_event__synthesize_thread_map(tool, threads, process, machine, data_mmap, proc_map_timeout);
        else if (target__has_cpu(target))
-               return perf_event__synthesize_threads(tool, process, machine, data_mmap);
+               return perf_event__synthesize_threads(tool, process, machine, data_mmap, proc_map_timeout);
        /* command specified */
        return 0;
 }
@@ -1820,6 +1963,7 @@ int machine__set_current_tid(struct machine *machine, int cpu, pid_t pid,
                return -ENOMEM;
 
        thread->cpu = cpu;
+       thread__put(thread);
 
        return 0;
 }
@@ -1845,3 +1989,8 @@ int machine__get_kernel_start(struct machine *machine)
        }
        return err;
 }
+
+struct dso *machine__findnew_dso(struct machine *machine, const char *filename)
+{
+       return dsos__findnew(&machine->dsos, filename);
+}
index 6d64cedb9d1e8f6fb255068b27761f6c52735b5e..887798e511e9f3dd382109927860269b03744246 100644 (file)
@@ -30,11 +30,11 @@ struct machine {
        bool              comm_exec;
        char              *root_dir;
        struct rb_root    threads;
+       pthread_rwlock_t  threads_lock;
        struct list_head  dead_threads;
        struct thread     *last_match;
        struct vdso_info  *vdso_info;
-       struct dsos       user_dsos;
-       struct dsos       kernel_dsos;
+       struct dsos       dsos;
        struct map_groups kmaps;
        struct map        *vmlinux_maps[MAP__NR_TYPES];
        u64               kernel_start;
@@ -81,6 +81,12 @@ int machine__process_fork_event(struct machine *machine, union perf_event *event
                                struct perf_sample *sample);
 int machine__process_lost_event(struct machine *machine, union perf_event *event,
                                struct perf_sample *sample);
+int machine__process_lost_samples_event(struct machine *machine, union perf_event *event,
+                                       struct perf_sample *sample);
+int machine__process_aux_event(struct machine *machine,
+                              union perf_event *event);
+int machine__process_itrace_start_event(struct machine *machine,
+                                       union perf_event *event);
 int machine__process_mmap_event(struct machine *machine, union perf_event *event,
                                struct perf_sample *sample);
 int machine__process_mmap2_event(struct machine *machine, union perf_event *event,
@@ -147,8 +153,10 @@ static inline bool machine__is_host(struct machine *machine)
        return machine ? machine->pid == HOST_KERNEL_ID : false;
 }
 
-struct thread *machine__findnew_thread(struct machine *machine, pid_t pid,
-                                      pid_t tid);
+struct thread *__machine__findnew_thread(struct machine *machine, pid_t pid, pid_t tid);
+struct thread *machine__findnew_thread(struct machine *machine, pid_t pid, pid_t tid);
+
+struct dso *machine__findnew_dso(struct machine *machine, const char *filename);
 
 size_t machine__fprintf(struct machine *machine, FILE *fp);
 
@@ -181,8 +189,8 @@ struct symbol *machine__find_kernel_function_by_name(struct machine *machine,
                                                 filter);
 }
 
-struct map *machine__new_module(struct machine *machine, u64 start,
-                               const char *filename);
+struct map *machine__findnew_module_map(struct machine *machine, u64 start,
+                                       const char *filename);
 
 int machine__load_kallsyms(struct machine *machine, const char *filename,
                           enum map_type type, symbol_filter_t filter);
@@ -208,16 +216,22 @@ size_t machine__fprintf_vmlinux_path(struct machine *machine, FILE *fp);
 int machine__for_each_thread(struct machine *machine,
                             int (*fn)(struct thread *thread, void *p),
                             void *priv);
+int machines__for_each_thread(struct machines *machines,
+                             int (*fn)(struct thread *thread, void *p),
+                             void *priv);
 
 int __machine__synthesize_threads(struct machine *machine, struct perf_tool *tool,
                                  struct target *target, struct thread_map *threads,
-                                 perf_event__handler_t process, bool data_mmap);
+                                 perf_event__handler_t process, bool data_mmap,
+                                 unsigned int proc_map_timeout);
 static inline
 int machine__synthesize_threads(struct machine *machine, struct target *target,
-                               struct thread_map *threads, bool data_mmap)
+                               struct thread_map *threads, bool data_mmap,
+                               unsigned int proc_map_timeout)
 {
        return __machine__synthesize_threads(machine, NULL, target, threads,
-                                            perf_event__process, data_mmap);
+                                            perf_event__process, data_mmap,
+                                            proc_map_timeout);
 }
 
 pid_t machine__get_current_tid(struct machine *machine, int cpu);
index a14f08f416863944527412b82a6b3cfaeda3c603..b5a5e9c024379652fccd722d7da2739169e0ab5d 100644 (file)
@@ -16,6 +16,8 @@
 #include "machine.h"
 #include <linux/string.h>
 
+static void __maps__insert(struct maps *maps, struct map *map);
+
 const char *map_type__name[MAP__NR_TYPES] = {
        [MAP__FUNCTION] = "Functions",
        [MAP__VARIABLE] = "Variables",
@@ -130,13 +132,13 @@ void map__init(struct map *map, enum map_type type,
        map->end      = end;
        map->pgoff    = pgoff;
        map->reloc    = 0;
-       map->dso      = dso;
+       map->dso      = dso__get(dso);
        map->map_ip   = map__map_ip;
        map->unmap_ip = map__unmap_ip;
        RB_CLEAR_NODE(&map->rb_node);
        map->groups   = NULL;
-       map->referenced = false;
        map->erange_warned = false;
+       atomic_set(&map->refcnt, 1);
 }
 
 struct map *map__new(struct machine *machine, u64 start, u64 len,
@@ -175,9 +177,9 @@ struct map *map__new(struct machine *machine, u64 start, u64 len,
 
                if (vdso) {
                        pgoff = 0;
-                       dso = vdso__dso_findnew(machine, thread);
+                       dso = machine__findnew_vdso(machine, thread);
                } else
-                       dso = __dsos__findnew(&machine->user_dsos, filename);
+                       dso = machine__findnew_dso(machine, filename);
 
                if (dso == NULL)
                        goto out_delete;
@@ -195,6 +197,7 @@ struct map *map__new(struct machine *machine, u64 start, u64 len,
                        if (type != MAP__FUNCTION)
                                dso__set_loaded(dso, map->type);
                }
+               dso__put(dso);
        }
        return map;
 out_delete:
@@ -221,11 +224,24 @@ struct map *map__new2(u64 start, struct dso *dso, enum map_type type)
        return map;
 }
 
+static void map__exit(struct map *map)
+{
+       BUG_ON(!RB_EMPTY_NODE(&map->rb_node));
+       dso__zput(map->dso);
+}
+
 void map__delete(struct map *map)
 {
+       map__exit(map);
        free(map);
 }
 
+void map__put(struct map *map)
+{
+       if (map && atomic_dec_and_test(&map->refcnt))
+               map__delete(map);
+}
+
 void map__fixup_start(struct map *map)
 {
        struct rb_root *symbols = &map->dso->symbols[map->type];
@@ -292,6 +308,11 @@ int map__load(struct map *map, symbol_filter_t filter)
        return 0;
 }
 
+int __weak arch__compare_symbol_names(const char *namea, const char *nameb)
+{
+       return strcmp(namea, nameb);
+}
+
 struct symbol *map__find_symbol(struct map *map, u64 addr,
                                symbol_filter_t filter)
 {
@@ -413,48 +434,49 @@ u64 map__objdump_2mem(struct map *map, u64 ip)
        return ip + map->reloc;
 }
 
+static void maps__init(struct maps *maps)
+{
+       maps->entries = RB_ROOT;
+       pthread_rwlock_init(&maps->lock, NULL);
+}
+
 void map_groups__init(struct map_groups *mg, struct machine *machine)
 {
        int i;
        for (i = 0; i < MAP__NR_TYPES; ++i) {
-               mg->maps[i] = RB_ROOT;
-               INIT_LIST_HEAD(&mg->removed_maps[i]);
+               maps__init(&mg->maps[i]);
        }
        mg->machine = machine;
-       mg->refcnt = 1;
+       atomic_set(&mg->refcnt, 1);
 }
 
-static void maps__delete(struct rb_root *maps)
+static void __maps__purge(struct maps *maps)
 {
-       struct rb_node *next = rb_first(maps);
+       struct rb_root *root = &maps->entries;
+       struct rb_node *next = rb_first(root);
 
        while (next) {
                struct map *pos = rb_entry(next, struct map, rb_node);
 
                next = rb_next(&pos->rb_node);
-               rb_erase(&pos->rb_node, maps);
-               map__delete(pos);
+               rb_erase_init(&pos->rb_node, root);
+               map__put(pos);
        }
 }
 
-static void maps__delete_removed(struct list_head *maps)
+static void maps__exit(struct maps *maps)
 {
-       struct map *pos, *n;
-
-       list_for_each_entry_safe(pos, n, maps, node) {
-               list_del(&pos->node);
-               map__delete(pos);
-       }
+       pthread_rwlock_wrlock(&maps->lock);
+       __maps__purge(maps);
+       pthread_rwlock_unlock(&maps->lock);
 }
 
 void map_groups__exit(struct map_groups *mg)
 {
        int i;
 
-       for (i = 0; i < MAP__NR_TYPES; ++i) {
-               maps__delete(&mg->maps[i]);
-               maps__delete_removed(&mg->removed_maps[i]);
-       }
+       for (i = 0; i < MAP__NR_TYPES; ++i)
+               maps__exit(&mg->maps[i]);
 }
 
 bool map_groups__empty(struct map_groups *mg)
@@ -464,8 +486,6 @@ bool map_groups__empty(struct map_groups *mg)
        for (i = 0; i < MAP__NR_TYPES; ++i) {
                if (maps__first(&mg->maps[i]))
                        return false;
-               if (!list_empty(&mg->removed_maps[i]))
-                       return false;
        }
 
        return true;
@@ -489,32 +509,10 @@ void map_groups__delete(struct map_groups *mg)
 
 void map_groups__put(struct map_groups *mg)
 {
-       if (--mg->refcnt == 0)
+       if (mg && atomic_dec_and_test(&mg->refcnt))
                map_groups__delete(mg);
 }
 
-void map_groups__flush(struct map_groups *mg)
-{
-       int type;
-
-       for (type = 0; type < MAP__NR_TYPES; type++) {
-               struct rb_root *root = &mg->maps[type];
-               struct rb_node *next = rb_first(root);
-
-               while (next) {
-                       struct map *pos = rb_entry(next, struct map, rb_node);
-                       next = rb_next(&pos->rb_node);
-                       rb_erase(&pos->rb_node, root);
-                       /*
-                        * We may have references to this map, for
-                        * instance in some hist_entry instances, so
-                        * just move them to a separate list.
-                        */
-                       list_add_tail(&pos->node, &mg->removed_maps[pos->type]);
-               }
-       }
-}
-
 struct symbol *map_groups__find_symbol(struct map_groups *mg,
                                       enum map_type type, u64 addr,
                                       struct map **mapp,
@@ -538,20 +536,28 @@ struct symbol *map_groups__find_symbol_by_name(struct map_groups *mg,
                                               struct map **mapp,
                                               symbol_filter_t filter)
 {
+       struct maps *maps = &mg->maps[type];
+       struct symbol *sym;
        struct rb_node *nd;
 
-       for (nd = rb_first(&mg->maps[type]); nd; nd = rb_next(nd)) {
+       pthread_rwlock_rdlock(&maps->lock);
+
+       for (nd = rb_first(&maps->entries); nd; nd = rb_next(nd)) {
                struct map *pos = rb_entry(nd, struct map, rb_node);
-               struct symbol *sym = map__find_symbol_by_name(pos, name, filter);
+
+               sym = map__find_symbol_by_name(pos, name, filter);
 
                if (sym == NULL)
                        continue;
                if (mapp != NULL)
                        *mapp = pos;
-               return sym;
+               goto out;
        }
 
-       return NULL;
+       sym = NULL;
+out:
+       pthread_rwlock_unlock(&maps->lock);
+       return sym;
 }
 
 int map_groups__find_ams(struct addr_map_symbol *ams, symbol_filter_t filter)
@@ -571,73 +577,54 @@ int map_groups__find_ams(struct addr_map_symbol *ams, symbol_filter_t filter)
        return ams->sym ? 0 : -1;
 }
 
-size_t __map_groups__fprintf_maps(struct map_groups *mg, enum map_type type,
-                                 FILE *fp)
+static size_t maps__fprintf(struct maps *maps, FILE *fp)
 {
-       size_t printed = fprintf(fp, "%s:\n", map_type__name[type]);
+       size_t printed = 0;
        struct rb_node *nd;
 
-       for (nd = rb_first(&mg->maps[type]); nd; nd = rb_next(nd)) {
+       pthread_rwlock_rdlock(&maps->lock);
+
+       for (nd = rb_first(&maps->entries); nd; nd = rb_next(nd)) {
                struct map *pos = rb_entry(nd, struct map, rb_node);
                printed += fprintf(fp, "Map:");
                printed += map__fprintf(pos, fp);
                if (verbose > 2) {
-                       printed += dso__fprintf(pos->dso, type, fp);
+                       printed += dso__fprintf(pos->dso, pos->type, fp);
                        printed += fprintf(fp, "--\n");
                }
        }
 
-       return printed;
-}
+       pthread_rwlock_unlock(&maps->lock);
 
-static size_t map_groups__fprintf_maps(struct map_groups *mg, FILE *fp)
-{
-       size_t printed = 0, i;
-       for (i = 0; i < MAP__NR_TYPES; ++i)
-               printed += __map_groups__fprintf_maps(mg, i, fp);
        return printed;
 }
 
-static size_t __map_groups__fprintf_removed_maps(struct map_groups *mg,
-                                                enum map_type type, FILE *fp)
+size_t __map_groups__fprintf_maps(struct map_groups *mg, enum map_type type,
+                                 FILE *fp)
 {
-       struct map *pos;
-       size_t printed = 0;
-
-       list_for_each_entry(pos, &mg->removed_maps[type], node) {
-               printed += fprintf(fp, "Map:");
-               printed += map__fprintf(pos, fp);
-               if (verbose > 1) {
-                       printed += dso__fprintf(pos->dso, type, fp);
-                       printed += fprintf(fp, "--\n");
-               }
-       }
-       return printed;
+       size_t printed = fprintf(fp, "%s:\n", map_type__name[type]);
+       return printed += maps__fprintf(&mg->maps[type], fp);
 }
 
-static size_t map_groups__fprintf_removed_maps(struct map_groups *mg,
-                                              FILE *fp)
+size_t map_groups__fprintf(struct map_groups *mg, FILE *fp)
 {
        size_t printed = 0, i;
        for (i = 0; i < MAP__NR_TYPES; ++i)
-               printed += __map_groups__fprintf_removed_maps(mg, i, fp);
+               printed += __map_groups__fprintf_maps(mg, i, fp);
        return printed;
 }
 
-size_t map_groups__fprintf(struct map_groups *mg, FILE *fp)
+static int maps__fixup_overlappings(struct maps *maps, struct map *map, FILE *fp)
 {
-       size_t printed = map_groups__fprintf_maps(mg, fp);
-       printed += fprintf(fp, "Removed maps:\n");
-       return printed + map_groups__fprintf_removed_maps(mg, fp);
-}
-
-int map_groups__fixup_overlappings(struct map_groups *mg, struct map *map,
-                                  FILE *fp)
-{
-       struct rb_root *root = &mg->maps[map->type];
-       struct rb_node *next = rb_first(root);
+       struct rb_root *root;
+       struct rb_node *next;
        int err = 0;
 
+       pthread_rwlock_wrlock(&maps->lock);
+
+       root = &maps->entries;
+       next = rb_first(root);
+
        while (next) {
                struct map *pos = rb_entry(next, struct map, rb_node);
                next = rb_next(&pos->rb_node);
@@ -651,7 +638,7 @@ int map_groups__fixup_overlappings(struct map_groups *mg, struct map *map,
                        map__fprintf(pos, fp);
                }
 
-               rb_erase(&pos->rb_node, root);
+               rb_erase_init(&pos->rb_node, root);
                /*
                 * Now check if we need to create new maps for areas not
                 * overlapped by the new map:
@@ -661,11 +648,11 @@ int map_groups__fixup_overlappings(struct map_groups *mg, struct map *map,
 
                        if (before == NULL) {
                                err = -ENOMEM;
-                               goto move_map;
+                               goto put_map;
                        }
 
                        before->end = map->start;
-                       map_groups__insert(mg, before);
+                       __maps__insert(maps, before);
                        if (verbose >= 2)
                                map__fprintf(before, fp);
                }
@@ -675,28 +662,31 @@ int map_groups__fixup_overlappings(struct map_groups *mg, struct map *map,
 
                        if (after == NULL) {
                                err = -ENOMEM;
-                               goto move_map;
+                               goto put_map;
                        }
 
                        after->start = map->end;
-                       map_groups__insert(mg, after);
+                       __maps__insert(maps, after);
                        if (verbose >= 2)
                                map__fprintf(after, fp);
                }
-move_map:
-               /*
-                * If we have references, just move them to a separate list.
-                */
-               if (pos->referenced)
-                       list_add_tail(&pos->node, &mg->removed_maps[map->type]);
-               else
-                       map__delete(pos);
+put_map:
+               map__put(pos);
 
                if (err)
-                       return err;
+                       goto out;
        }
 
-       return 0;
+       err = 0;
+out:
+       pthread_rwlock_unlock(&maps->lock);
+       return err;
+}
+
+int map_groups__fixup_overlappings(struct map_groups *mg, struct map *map,
+                                  FILE *fp)
+{
+       return maps__fixup_overlappings(&mg->maps[map->type], map, fp);
 }
 
 /*
@@ -705,20 +695,28 @@ move_map:
 int map_groups__clone(struct map_groups *mg,
                      struct map_groups *parent, enum map_type type)
 {
-       struct rb_node *nd;
-       for (nd = rb_first(&parent->maps[type]); nd; nd = rb_next(nd)) {
-               struct map *map = rb_entry(nd, struct map, rb_node);
+       int err = -ENOMEM;
+       struct map *map;
+       struct maps *maps = &parent->maps[type];
+
+       pthread_rwlock_rdlock(&maps->lock);
+
+       for (map = maps__first(maps); map; map = map__next(map)) {
                struct map *new = map__clone(map);
                if (new == NULL)
-                       return -ENOMEM;
+                       goto out_unlock;
                map_groups__insert(mg, new);
        }
-       return 0;
+
+       err = 0;
+out_unlock:
+       pthread_rwlock_unlock(&maps->lock);
+       return err;
 }
 
-void maps__insert(struct rb_root *maps, struct map *map)
+static void __maps__insert(struct maps *maps, struct map *map)
 {
-       struct rb_node **p = &maps->rb_node;
+       struct rb_node **p = &maps->entries.rb_node;
        struct rb_node *parent = NULL;
        const u64 ip = map->start;
        struct map *m;
@@ -733,20 +731,38 @@ void maps__insert(struct rb_root *maps, struct map *map)
        }
 
        rb_link_node(&map->rb_node, parent, p);
-       rb_insert_color(&map->rb_node, maps);
+       rb_insert_color(&map->rb_node, &maps->entries);
+       map__get(map);
 }
 
-void maps__remove(struct rb_root *maps, struct map *map)
+void maps__insert(struct maps *maps, struct map *map)
 {
-       rb_erase(&map->rb_node, maps);
+       pthread_rwlock_wrlock(&maps->lock);
+       __maps__insert(maps, map);
+       pthread_rwlock_unlock(&maps->lock);
 }
 
-struct map *maps__find(struct rb_root *maps, u64 ip)
+static void __maps__remove(struct maps *maps, struct map *map)
 {
-       struct rb_node **p = &maps->rb_node;
-       struct rb_node *parent = NULL;
+       rb_erase_init(&map->rb_node, &maps->entries);
+       map__put(map);
+}
+
+void maps__remove(struct maps *maps, struct map *map)
+{
+       pthread_rwlock_wrlock(&maps->lock);
+       __maps__remove(maps, map);
+       pthread_rwlock_unlock(&maps->lock);
+}
+
+struct map *maps__find(struct maps *maps, u64 ip)
+{
+       struct rb_node **p, *parent = NULL;
        struct map *m;
 
+       pthread_rwlock_rdlock(&maps->lock);
+
+       p = &maps->entries.rb_node;
        while (*p != NULL) {
                parent = *p;
                m = rb_entry(parent, struct map, rb_node);
@@ -755,22 +771,25 @@ struct map *maps__find(struct rb_root *maps, u64 ip)
                else if (ip >= m->end)
                        p = &(*p)->rb_right;
                else
-                       return m;
+                       goto out;
        }
 
-       return NULL;
+       m = NULL;
+out:
+       pthread_rwlock_unlock(&maps->lock);
+       return m;
 }
 
-struct map *maps__first(struct rb_root *maps)
+struct map *maps__first(struct maps *maps)
 {
-       struct rb_node *first = rb_first(maps);
+       struct rb_node *first = rb_first(&maps->entries);
 
        if (first)
                return rb_entry(first, struct map, rb_node);
        return NULL;
 }
 
-struct map *maps__next(struct map *map)
+struct map *map__next(struct map *map)
 {
        struct rb_node *next = rb_next(&map->rb_node);
 
index ec19c59ca38e07deba4a8c2c254ec3a4c71c6c91..d73e687b224e4e0d3b427f1244695ea4e19c4fcb 100644 (file)
@@ -1,9 +1,11 @@
 #ifndef __PERF_MAP_H
 #define __PERF_MAP_H
 
+#include <linux/atomic.h>
 #include <linux/compiler.h>
 #include <linux/list.h>
 #include <linux/rbtree.h>
+#include <pthread.h>
 #include <stdio.h>
 #include <stdbool.h>
 #include <linux/types.h>
@@ -32,7 +34,6 @@ struct map {
        u64                     start;
        u64                     end;
        u8 /* enum map_type */  type;
-       bool                    referenced;
        bool                    erange_warned;
        u32                     priv;
        u32                     prot;
@@ -50,6 +51,7 @@ struct map {
 
        struct dso              *dso;
        struct map_groups       *groups;
+       atomic_t                refcnt;
 };
 
 struct kmap {
@@ -57,11 +59,15 @@ struct kmap {
        struct map_groups       *kmaps;
 };
 
+struct maps {
+       struct rb_root   entries;
+       pthread_rwlock_t lock;
+};
+
 struct map_groups {
-       struct rb_root   maps[MAP__NR_TYPES];
-       struct list_head removed_maps[MAP__NR_TYPES];
+       struct maps      maps[MAP__NR_TYPES];
        struct machine   *machine;
-       int              refcnt;
+       atomic_t         refcnt;
 };
 
 struct map_groups *map_groups__new(struct machine *machine);
@@ -70,7 +76,8 @@ bool map_groups__empty(struct map_groups *mg);
 
 static inline struct map_groups *map_groups__get(struct map_groups *mg)
 {
-       ++mg->refcnt;
+       if (mg)
+               atomic_inc(&mg->refcnt);
        return mg;
 }
 
@@ -124,7 +131,7 @@ struct thread;
  */
 #define __map__for_each_symbol_by_name(map, sym_name, pos, filter)     \
        for (pos = map__find_symbol_by_name(map, sym_name, filter);     \
-            pos && strcmp(pos->name, sym_name) == 0;           \
+            pos && arch__compare_symbol_names(pos->name, sym_name) == 0;       \
             pos = symbol__next_by_name(pos))
 
 #define map__for_each_symbol_by_name(map, sym_name, pos)               \
@@ -132,6 +139,7 @@ struct thread;
 
 typedef int (*symbol_filter_t)(struct map *map, struct symbol *sym);
 
+int arch__compare_symbol_names(const char *namea, const char *nameb);
 void map__init(struct map *map, enum map_type type,
               u64 start, u64 end, u64 pgoff, struct dso *dso);
 struct map *map__new(struct machine *machine, u64 start, u64 len,
@@ -141,6 +149,24 @@ struct map *map__new(struct machine *machine, u64 start, u64 len,
 struct map *map__new2(u64 start, struct dso *dso, enum map_type type);
 void map__delete(struct map *map);
 struct map *map__clone(struct map *map);
+
+static inline struct map *map__get(struct map *map)
+{
+       if (map)
+               atomic_inc(&map->refcnt);
+       return map;
+}
+
+void map__put(struct map *map);
+
+static inline void __map__zput(struct map **map)
+{
+       map__put(*map);
+       *map = NULL;
+}
+
+#define map__zput(map) __map__zput(&map)
+
 int map__overlap(struct map *l, struct map *r);
 size_t map__fprintf(struct map *map, FILE *fp);
 size_t map__fprintf_dsoname(struct map *map, FILE *fp);
@@ -159,11 +185,11 @@ void map__reloc_vmlinux(struct map *map);
 
 size_t __map_groups__fprintf_maps(struct map_groups *mg, enum map_type type,
                                  FILE *fp);
-void maps__insert(struct rb_root *maps, struct map *map);
-void maps__remove(struct rb_root *maps, struct map *map);
-struct map *maps__find(struct rb_root *maps, u64 addr);
-struct map *maps__first(struct rb_root *maps);
-struct map *maps__next(struct map *map);
+void maps__insert(struct maps *maps, struct map *map);
+void maps__remove(struct maps *maps, struct map *map);
+struct map *maps__find(struct maps *maps, u64 addr);
+struct map *maps__first(struct maps *maps);
+struct map *map__next(struct map *map);
 void map_groups__init(struct map_groups *mg, struct machine *machine);
 void map_groups__exit(struct map_groups *mg);
 int map_groups__clone(struct map_groups *mg,
@@ -198,7 +224,7 @@ static inline struct map *map_groups__first(struct map_groups *mg,
 
 static inline struct map *map_groups__next(struct map *map)
 {
-       return maps__next(map);
+       return map__next(map);
 }
 
 struct symbol *map_groups__find_symbol(struct map_groups *mg,
@@ -230,6 +256,4 @@ int map_groups__fixup_overlappings(struct map_groups *mg, struct map *map,
 struct map *map_groups__find_by_name(struct map_groups *mg,
                                     enum map_type type, const char *name);
 
-void map_groups__flush(struct map_groups *mg);
-
 #endif /* __PERF_MAP_H */
index 31ee02d4e988a7ea4b0101c0ef4e1ae74f349023..53ef006a951c3f3c90ce8c62e9a5cd73b7750a74 100644 (file)
@@ -50,11 +50,6 @@ void setup_pager(void)
 
        if (!isatty(1))
                return;
-       if (!pager) {
-               if (!pager_program)
-                       perf_config(perf_default_config, NULL);
-               pager = pager_program;
-       }
        if (!pager)
                pager = getenv("PAGER");
        if (!(pager || access("/usr/bin/pager", X_OK)))
diff --git a/tools/perf/util/parse-branch-options.c b/tools/perf/util/parse-branch-options.c
new file mode 100644 (file)
index 0000000..a3b1e13
--- /dev/null
@@ -0,0 +1,94 @@
+#include "perf.h"
+#include "util/util.h"
+#include "util/debug.h"
+#include "util/parse-options.h"
+#include "util/parse-branch-options.h"
+
+#define BRANCH_OPT(n, m) \
+       { .name = n, .mode = (m) }
+
+#define BRANCH_END { .name = NULL }
+
+struct branch_mode {
+       const char *name;
+       int mode;
+};
+
+static const struct branch_mode branch_modes[] = {
+       BRANCH_OPT("u", PERF_SAMPLE_BRANCH_USER),
+       BRANCH_OPT("k", PERF_SAMPLE_BRANCH_KERNEL),
+       BRANCH_OPT("hv", PERF_SAMPLE_BRANCH_HV),
+       BRANCH_OPT("any", PERF_SAMPLE_BRANCH_ANY),
+       BRANCH_OPT("any_call", PERF_SAMPLE_BRANCH_ANY_CALL),
+       BRANCH_OPT("any_ret", PERF_SAMPLE_BRANCH_ANY_RETURN),
+       BRANCH_OPT("ind_call", PERF_SAMPLE_BRANCH_IND_CALL),
+       BRANCH_OPT("abort_tx", PERF_SAMPLE_BRANCH_ABORT_TX),
+       BRANCH_OPT("in_tx", PERF_SAMPLE_BRANCH_IN_TX),
+       BRANCH_OPT("no_tx", PERF_SAMPLE_BRANCH_NO_TX),
+       BRANCH_OPT("cond", PERF_SAMPLE_BRANCH_COND),
+       BRANCH_OPT("ind_jmp", PERF_SAMPLE_BRANCH_IND_JUMP),
+       BRANCH_END
+};
+
+int
+parse_branch_stack(const struct option *opt, const char *str, int unset)
+{
+#define ONLY_PLM \
+       (PERF_SAMPLE_BRANCH_USER        |\
+        PERF_SAMPLE_BRANCH_KERNEL      |\
+        PERF_SAMPLE_BRANCH_HV)
+
+       uint64_t *mode = (uint64_t *)opt->value;
+       const struct branch_mode *br;
+       char *s, *os = NULL, *p;
+       int ret = -1;
+
+       if (unset)
+               return 0;
+
+       /*
+        * cannot set it twice, -b + --branch-filter for instance
+        */
+       if (*mode)
+               return -1;
+
+       /* str may be NULL in case no arg is passed to -b */
+       if (str) {
+               /* because str is read-only */
+               s = os = strdup(str);
+               if (!s)
+                       return -1;
+
+               for (;;) {
+                       p = strchr(s, ',');
+                       if (p)
+                               *p = '\0';
+
+                       for (br = branch_modes; br->name; br++) {
+                               if (!strcasecmp(s, br->name))
+                                       break;
+                       }
+                       if (!br->name) {
+                               ui__warning("unknown branch filter %s,"
+                                           " check man page\n", s);
+                               goto error;
+                       }
+
+                       *mode |= br->mode;
+
+                       if (!p)
+                               break;
+
+                       s = p + 1;
+               }
+       }
+       ret = 0;
+
+       /* default to any branch */
+       if ((*mode & ~ONLY_PLM) == 0) {
+               *mode = PERF_SAMPLE_BRANCH_ANY;
+       }
+error:
+       free(os);
+       return ret;
+}
diff --git a/tools/perf/util/parse-branch-options.h b/tools/perf/util/parse-branch-options.h
new file mode 100644 (file)
index 0000000..b9d9470
--- /dev/null
@@ -0,0 +1,5 @@
+#ifndef _PERF_PARSE_BRANCH_OPTIONS_H
+#define _PERF_PARSE_BRANCH_OPTIONS_H 1
+struct option;
+int parse_branch_stack(const struct option *opt, const char *str, int unset);
+#endif /* _PERF_PARSE_BRANCH_OPTIONS_H */
index be0655388b38e4238d69782ee0e9c8357276a574..2a4d1ec028464757d6723bbd08c2d0ce0a010a14 100644 (file)
@@ -17,6 +17,7 @@
 #include "parse-events-flex.h"
 #include "pmu.h"
 #include "thread_map.h"
+#include "asm/bug.h"
 
 #define MAX_NAME_LEN 100
 
@@ -538,16 +539,40 @@ int parse_events_add_breakpoint(struct list_head *list, int *idx,
        return add_event(list, idx, &attr, NULL);
 }
 
+static int check_type_val(struct parse_events_term *term,
+                         struct parse_events_error *err,
+                         int type)
+{
+       if (type == term->type_val)
+               return 0;
+
+       if (err) {
+               err->idx = term->err_val;
+               if (type == PARSE_EVENTS__TERM_TYPE_NUM)
+                       err->str = strdup("expected numeric value");
+               else
+                       err->str = strdup("expected string value");
+       }
+       return -EINVAL;
+}
+
 static int config_term(struct perf_event_attr *attr,
-                      struct parse_events_term *term)
+                      struct parse_events_term *term,
+                      struct parse_events_error *err)
 {
-#define CHECK_TYPE_VAL(type)                                   \
-do {                                                           \
-       if (PARSE_EVENTS__TERM_TYPE_ ## type != term->type_val) \
-               return -EINVAL;                                 \
+#define CHECK_TYPE_VAL(type)                                              \
+do {                                                                      \
+       if (check_type_val(term, err, PARSE_EVENTS__TERM_TYPE_ ## type)) \
+               return -EINVAL;                                            \
 } while (0)
 
        switch (term->type_term) {
+       case PARSE_EVENTS__TERM_TYPE_USER:
+               /*
+                * Always succeed for sysfs terms, as we dont know
+                * at this point what type they need to have.
+                */
+               return 0;
        case PARSE_EVENTS__TERM_TYPE_CONFIG:
                CHECK_TYPE_VAL(NUM);
                attr->config = term->val.num;
@@ -582,18 +607,20 @@ do {                                                              \
 }
 
 static int config_attr(struct perf_event_attr *attr,
-                      struct list_head *head, int fail)
+                      struct list_head *head,
+                      struct parse_events_error *err)
 {
        struct parse_events_term *term;
 
        list_for_each_entry(term, head, list)
-               if (config_term(attr, term) && fail)
+               if (config_term(attr, term, err))
                        return -EINVAL;
 
        return 0;
 }
 
-int parse_events_add_numeric(struct list_head *list, int *idx,
+int parse_events_add_numeric(struct parse_events_evlist *data,
+                            struct list_head *list,
                             u32 type, u64 config,
                             struct list_head *head_config)
 {
@@ -604,10 +631,10 @@ int parse_events_add_numeric(struct list_head *list, int *idx,
        attr.config = config;
 
        if (head_config &&
-           config_attr(&attr, head_config, 1))
+           config_attr(&attr, head_config, data->error))
                return -EINVAL;
 
-       return add_event(list, idx, &attr, NULL);
+       return add_event(list, &data->idx, &attr, NULL);
 }
 
 static int parse_events__is_name_term(struct parse_events_term *term)
@@ -626,8 +653,9 @@ static char *pmu_event_name(struct list_head *head_terms)
        return NULL;
 }
 
-int parse_events_add_pmu(struct list_head *list, int *idx,
-                        char *name, struct list_head *head_config)
+int parse_events_add_pmu(struct parse_events_evlist *data,
+                        struct list_head *list, char *name,
+                        struct list_head *head_config)
 {
        struct perf_event_attr attr;
        struct perf_pmu_info info;
@@ -647,7 +675,7 @@ int parse_events_add_pmu(struct list_head *list, int *idx,
 
        if (!head_config) {
                attr.type = pmu->type;
-               evsel = __add_event(list, idx, &attr, NULL, pmu->cpus);
+               evsel = __add_event(list, &data->idx, &attr, NULL, pmu->cpus);
                return evsel ? 0 : -ENOMEM;
        }
 
@@ -658,13 +686,14 @@ int parse_events_add_pmu(struct list_head *list, int *idx,
         * Configure hardcoded terms first, no need to check
         * return value when called with fail == 0 ;)
         */
-       config_attr(&attr, head_config, 0);
+       if (config_attr(&attr, head_config, data->error))
+               return -EINVAL;
 
-       if (perf_pmu__config(pmu, &attr, head_config))
+       if (perf_pmu__config(pmu, &attr, head_config, data->error))
                return -EINVAL;
 
-       evsel = __add_event(list, idx, &attr, pmu_event_name(head_config),
-                           pmu->cpus);
+       evsel = __add_event(list, &data->idx, &attr,
+                           pmu_event_name(head_config), pmu->cpus);
        if (evsel) {
                evsel->unit = info.unit;
                evsel->scale = info.scale;
@@ -1019,11 +1048,13 @@ int parse_events_terms(struct list_head *terms, const char *str)
        return ret;
 }
 
-int parse_events(struct perf_evlist *evlist, const char *str)
+int parse_events(struct perf_evlist *evlist, const char *str,
+                struct parse_events_error *err)
 {
        struct parse_events_evlist data = {
-               .list = LIST_HEAD_INIT(data.list),
-               .idx  = evlist->nr_entries,
+               .list  = LIST_HEAD_INIT(data.list),
+               .idx   = evlist->nr_entries,
+               .error = err,
        };
        int ret;
 
@@ -1044,16 +1075,87 @@ int parse_events(struct perf_evlist *evlist, const char *str)
        return ret;
 }
 
+#define MAX_WIDTH 1000
+static int get_term_width(void)
+{
+       struct winsize ws;
+
+       get_term_dimensions(&ws);
+       return ws.ws_col > MAX_WIDTH ? MAX_WIDTH : ws.ws_col;
+}
+
+static void parse_events_print_error(struct parse_events_error *err,
+                                    const char *event)
+{
+       const char *str = "invalid or unsupported event: ";
+       char _buf[MAX_WIDTH];
+       char *buf = (char *) event;
+       int idx = 0;
+
+       if (err->str) {
+               /* -2 for extra '' in the final fprintf */
+               int width       = get_term_width() - 2;
+               int len_event   = strlen(event);
+               int len_str, max_len, cut = 0;
+
+               /*
+                * Maximum error index indent, we will cut
+                * the event string if it's bigger.
+                */
+               int max_err_idx = 10;
+
+               /*
+                * Let's be specific with the message when
+                * we have the precise error.
+                */
+               str     = "event syntax error: ";
+               len_str = strlen(str);
+               max_len = width - len_str;
+
+               buf = _buf;
+
+               /* We're cutting from the beggining. */
+               if (err->idx > max_err_idx)
+                       cut = err->idx - max_err_idx;
+
+               strncpy(buf, event + cut, max_len);
+
+               /* Mark cut parts with '..' on both sides. */
+               if (cut)
+                       buf[0] = buf[1] = '.';
+
+               if ((len_event - cut) > max_len) {
+                       buf[max_len - 1] = buf[max_len - 2] = '.';
+                       buf[max_len] = 0;
+               }
+
+               idx = len_str + err->idx - cut;
+       }
+
+       fprintf(stderr, "%s'%s'\n", str, buf);
+       if (idx) {
+               fprintf(stderr, "%*s\\___ %s\n", idx + 1, "", err->str);
+               if (err->help)
+                       fprintf(stderr, "\n%s\n", err->help);
+               free(err->str);
+               free(err->help);
+       }
+
+       fprintf(stderr, "Run 'perf list' for a list of valid events\n");
+}
+
+#undef MAX_WIDTH
+
 int parse_events_option(const struct option *opt, const char *str,
                        int unset __maybe_unused)
 {
        struct perf_evlist *evlist = *(struct perf_evlist **)opt->value;
-       int ret = parse_events(evlist, str);
+       struct parse_events_error err = { .idx = 0, };
+       int ret = parse_events(evlist, str, &err);
+
+       if (ret)
+               parse_events_print_error(&err, str);
 
-       if (ret) {
-               fprintf(stderr, "invalid or unsupported event: '%s'\n", str);
-               fprintf(stderr, "Run 'perf list' for a list of valid events\n");
-       }
        return ret;
 }
 
@@ -1460,7 +1562,7 @@ int parse_events__is_hardcoded_term(struct parse_events_term *term)
 
 static int new_term(struct parse_events_term **_term, int type_val,
                    int type_term, char *config,
-                   char *str, u64 num)
+                   char *str, u64 num, int err_term, int err_val)
 {
        struct parse_events_term *term;
 
@@ -1472,6 +1574,8 @@ static int new_term(struct parse_events_term **_term, int type_val,
        term->type_val  = type_val;
        term->type_term = type_term;
        term->config = config;
+       term->err_term = err_term;
+       term->err_val  = err_val;
 
        switch (type_val) {
        case PARSE_EVENTS__TERM_TYPE_NUM:
@@ -1490,17 +1594,29 @@ static int new_term(struct parse_events_term **_term, int type_val,
 }
 
 int parse_events_term__num(struct parse_events_term **term,
-                          int type_term, char *config, u64 num)
+                          int type_term, char *config, u64 num,
+                          void *loc_term_, void *loc_val_)
 {
+       YYLTYPE *loc_term = loc_term_;
+       YYLTYPE *loc_val = loc_val_;
+
        return new_term(term, PARSE_EVENTS__TERM_TYPE_NUM, type_term,
-                       config, NULL, num);
+                       config, NULL, num,
+                       loc_term ? loc_term->first_column : 0,
+                       loc_val ? loc_val->first_column : 0);
 }
 
 int parse_events_term__str(struct parse_events_term **term,
-                          int type_term, char *config, char *str)
+                          int type_term, char *config, char *str,
+                          void *loc_term_, void *loc_val_)
 {
+       YYLTYPE *loc_term = loc_term_;
+       YYLTYPE *loc_val = loc_val_;
+
        return new_term(term, PARSE_EVENTS__TERM_TYPE_STR, type_term,
-                       config, str, 0);
+                       config, str, 0,
+                       loc_term ? loc_term->first_column : 0,
+                       loc_val ? loc_val->first_column : 0);
 }
 
 int parse_events_term__sym_hw(struct parse_events_term **term,
@@ -1514,18 +1630,20 @@ int parse_events_term__sym_hw(struct parse_events_term **term,
        if (config)
                return new_term(term, PARSE_EVENTS__TERM_TYPE_STR,
                                PARSE_EVENTS__TERM_TYPE_USER, config,
-                               (char *) sym->symbol, 0);
+                               (char *) sym->symbol, 0, 0, 0);
        else
                return new_term(term, PARSE_EVENTS__TERM_TYPE_STR,
                                PARSE_EVENTS__TERM_TYPE_USER,
-                               (char *) "event", (char *) sym->symbol, 0);
+                               (char *) "event", (char *) sym->symbol,
+                               0, 0, 0);
 }
 
 int parse_events_term__clone(struct parse_events_term **new,
                             struct parse_events_term *term)
 {
        return new_term(new, term->type_val, term->type_term, term->config,
-                       term->val.str, term->val.num);
+                       term->val.str, term->val.num,
+                       term->err_term, term->err_val);
 }
 
 void parse_events__free_terms(struct list_head *terms)
@@ -1535,3 +1653,15 @@ void parse_events__free_terms(struct list_head *terms)
        list_for_each_entry_safe(term, h, terms, list)
                free(term);
 }
+
+void parse_events_evlist_error(struct parse_events_evlist *data,
+                              int idx, const char *str)
+{
+       struct parse_events_error *err = data->error;
+
+       if (!err)
+               return;
+       err->idx = idx;
+       err->str = strdup(str);
+       WARN_ONCE(!err->str, "WARNING: failed to allocate error string");
+}
index 52a2dda4f954a7682d43376bd3ac2ee91d127447..131f29b2f13258d647276820026207526a08ef02 100644 (file)
@@ -12,6 +12,7 @@
 struct list_head;
 struct perf_evsel;
 struct perf_evlist;
+struct parse_events_error;
 
 struct option;
 
@@ -29,7 +30,8 @@ const char *event_type(int type);
 
 extern int parse_events_option(const struct option *opt, const char *str,
                               int unset);
-extern int parse_events(struct perf_evlist *evlist, const char *str);
+extern int parse_events(struct perf_evlist *evlist, const char *str,
+                       struct parse_events_error *error);
 extern int parse_events_terms(struct list_head *terms, const char *str);
 extern int parse_filter(const struct option *opt, const char *str, int unset);
 
@@ -72,12 +74,23 @@ struct parse_events_term {
        int type_term;
        struct list_head list;
        bool used;
+
+       /* error string indexes for within parsed string */
+       int err_term;
+       int err_val;
+};
+
+struct parse_events_error {
+       int   idx;      /* index in the parsed string */
+       char *str;      /* string to display at the index */
+       char *help;     /* optional help string */
 };
 
 struct parse_events_evlist {
-       struct list_head list;
-       int idx;
-       int nr_groups;
+       struct list_head           list;
+       int                        idx;
+       int                        nr_groups;
+       struct parse_events_error *error;
 };
 
 struct parse_events_terms {
@@ -85,10 +98,12 @@ struct parse_events_terms {
 };
 
 int parse_events__is_hardcoded_term(struct parse_events_term *term);
-int parse_events_term__num(struct parse_events_term **_term,
-                          int type_term, char *config, u64 num);
-int parse_events_term__str(struct parse_events_term **_term,
-                          int type_term, char *config, char *str);
+int parse_events_term__num(struct parse_events_term **term,
+                          int type_term, char *config, u64 num,
+                          void *loc_term, void *loc_val);
+int parse_events_term__str(struct parse_events_term **term,
+                          int type_term, char *config, char *str,
+                          void *loc_term, void *loc_val);
 int parse_events_term__sym_hw(struct parse_events_term **term,
                              char *config, unsigned idx);
 int parse_events_term__clone(struct parse_events_term **new,
@@ -99,21 +114,24 @@ int parse_events__modifier_group(struct list_head *list, char *event_mod);
 int parse_events_name(struct list_head *list, char *name);
 int parse_events_add_tracepoint(struct list_head *list, int *idx,
                                char *sys, char *event);
-int parse_events_add_numeric(struct list_head *list, int *idx,
+int parse_events_add_numeric(struct parse_events_evlist *data,
+                            struct list_head *list,
                             u32 type, u64 config,
                             struct list_head *head_config);
 int parse_events_add_cache(struct list_head *list, int *idx,
                           char *type, char *op_result1, char *op_result2);
 int parse_events_add_breakpoint(struct list_head *list, int *idx,
                                void *ptr, char *type, u64 len);
-int parse_events_add_pmu(struct list_head *list, int *idx,
-                        char *pmu , struct list_head *head_config);
+int parse_events_add_pmu(struct parse_events_evlist *data,
+                        struct list_head *list, char *name,
+                        struct list_head *head_config);
 enum perf_pmu_event_symbol_type
 perf_pmu__parse_check(const char *name);
 void parse_events__set_leader(char *name, struct list_head *list);
 void parse_events_update_lists(struct list_head *list_event,
                               struct list_head *list_all);
-void parse_events_error(void *data, void *scanner, char const *msg);
+void parse_events_evlist_error(struct parse_events_evlist *data,
+                              int idx, const char *str);
 
 void print_events(const char *event_glob, bool name_only);
 
index 8895cf3132ab242c078c70c6f7713f52030c9f6a..09e738fe9ea2790a1c304f2015cdb20c03c20614 100644 (file)
@@ -3,6 +3,8 @@
 %option bison-bridge
 %option prefix="parse_events_"
 %option stack
+%option bison-locations
+%option yylineno
 
 %{
 #include <errno.h>
@@ -51,6 +53,18 @@ static int str(yyscan_t scanner, int token)
        return token;
 }
 
+#define REWIND(__alloc)                                \
+do {                                                           \
+       YYSTYPE *__yylval = parse_events_get_lval(yyscanner);   \
+       char *text = parse_events_get_text(yyscanner);          \
+                                                               \
+       if (__alloc)                                            \
+               __yylval->str = strdup(text);                   \
+                                                               \
+       yycolumn -= strlen(text);                               \
+       yyless(0);                                              \
+} while (0)
+
 static int pmu_str_check(yyscan_t scanner)
 {
        YYSTYPE *yylval = parse_events_get_lval(scanner);
@@ -85,6 +99,13 @@ static int term(yyscan_t scanner, int type)
        return PE_TERM;
 }
 
+#define YY_USER_ACTION                                 \
+do {                                                   \
+       yylloc->last_column  = yylloc->first_column;    \
+       yylloc->first_column = yycolumn;                \
+       yycolumn += yyleng;                             \
+} while (0);
+
 %}
 
 %x mem
@@ -119,6 +140,12 @@ modifier_bp        [rwx]{1,3}
 
                if (start_token) {
                        parse_events_set_extra(NULL, yyscanner);
+                       /*
+                        * The flex parser does not init locations variable
+                        * via the scan_string interface, so we need do the
+                        * init in here.
+                        */
+                       yycolumn = 0;
                        return start_token;
                }
          }
@@ -127,24 +154,30 @@ modifier_bp       [rwx]{1,3}
 <event>{
 
 {group}                {
-                       BEGIN(INITIAL); yyless(0);
+                       BEGIN(INITIAL);
+                       REWIND(0);
                }
 
 {event_pmu}    |
 {event}                {
-                       str(yyscanner, PE_EVENT_NAME);
-                       BEGIN(INITIAL); yyless(0);
+                       BEGIN(INITIAL);
+                       REWIND(1);
                        return PE_EVENT_NAME;
                }
 
 .              |
 <<EOF>>                {
-                       BEGIN(INITIAL); yyless(0);
+                       BEGIN(INITIAL);
+                       REWIND(0);
                }
 
 }
 
 <config>{
+       /*
+        * Please update formats_error_string any time
+        * new static term is added.
+        */
 config                 { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CONFIG); }
 config1                        { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CONFIG1); }
 config2                        { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CONFIG2); }
index 72def077dbbfda149dfe893135dd3940ca2ed648..591905a02b926b6029447a372f2e5f7b7d34864a 100644 (file)
@@ -2,6 +2,7 @@
 %parse-param {void *_data}
 %parse-param {void *scanner}
 %lex-param {void* scanner}
+%locations
 
 %{
 
@@ -14,8 +15,6 @@
 #include "parse-events.h"
 #include "parse-events-bison.h"
 
-extern int parse_events_lex (YYSTYPE* lvalp, void* scanner);
-
 #define ABORT_ON(val) \
 do { \
        if (val) \
@@ -208,7 +207,7 @@ PE_NAME '/' event_config '/'
        struct list_head *list;
 
        ALLOC_LIST(list);
-       ABORT_ON(parse_events_add_pmu(list, &data->idx, $1, $3));
+       ABORT_ON(parse_events_add_pmu(data, list, $1, $3));
        parse_events__free_terms($3);
        $$ = list;
 }
@@ -219,7 +218,7 @@ PE_NAME '/' '/'
        struct list_head *list;
 
        ALLOC_LIST(list);
-       ABORT_ON(parse_events_add_pmu(list, &data->idx, $1, NULL));
+       ABORT_ON(parse_events_add_pmu(data, list, $1, NULL));
        $$ = list;
 }
 |
@@ -232,11 +231,11 @@ PE_KERNEL_PMU_EVENT sep_dc
 
        ALLOC_LIST(head);
        ABORT_ON(parse_events_term__num(&term, PARSE_EVENTS__TERM_TYPE_USER,
-                                       $1, 1));
+                                       $1, 1, &@1, NULL));
        list_add_tail(&term->list, head);
 
        ALLOC_LIST(list);
-       ABORT_ON(parse_events_add_pmu(list, &data->idx, "cpu", head));
+       ABORT_ON(parse_events_add_pmu(data, list, "cpu", head));
        parse_events__free_terms(head);
        $$ = list;
 }
@@ -252,7 +251,7 @@ PE_PMU_EVENT_PRE '-' PE_PMU_EVENT_SUF sep_dc
 
        ALLOC_LIST(head);
        ABORT_ON(parse_events_term__num(&term, PARSE_EVENTS__TERM_TYPE_USER,
-                                       &pmu_name, 1));
+                                       &pmu_name, 1, &@1, NULL));
        list_add_tail(&term->list, head);
 
        ALLOC_LIST(list);
@@ -275,8 +274,7 @@ value_sym '/' event_config '/'
        int config = $1 & 255;
 
        ALLOC_LIST(list);
-       ABORT_ON(parse_events_add_numeric(list, &data->idx,
-                                         type, config, $3));
+       ABORT_ON(parse_events_add_numeric(data, list, type, config, $3));
        parse_events__free_terms($3);
        $$ = list;
 }
@@ -289,8 +287,7 @@ value_sym sep_slash_dc
        int config = $1 & 255;
 
        ALLOC_LIST(list);
-       ABORT_ON(parse_events_add_numeric(list, &data->idx,
-                                         type, config, NULL));
+       ABORT_ON(parse_events_add_numeric(data, list, type, config, NULL));
        $$ = list;
 }
 
@@ -389,7 +386,15 @@ PE_NAME ':' PE_NAME
        struct list_head *list;
 
        ALLOC_LIST(list);
-       ABORT_ON(parse_events_add_tracepoint(list, &data->idx, $1, $3));
+       if (parse_events_add_tracepoint(list, &data->idx, $1, $3)) {
+               struct parse_events_error *error = data->error;
+
+               if (error) {
+                       error->idx = @1.first_column;
+                       error->str = strdup("unknown tracepoint");
+               }
+               return -1;
+       }
        $$ = list;
 }
 
@@ -400,7 +405,7 @@ PE_VALUE ':' PE_VALUE
        struct list_head *list;
 
        ALLOC_LIST(list);
-       ABORT_ON(parse_events_add_numeric(list, &data->idx, (u32)$1, $3, NULL));
+       ABORT_ON(parse_events_add_numeric(data, list, (u32)$1, $3, NULL));
        $$ = list;
 }
 
@@ -411,8 +416,7 @@ PE_RAW
        struct list_head *list;
 
        ALLOC_LIST(list);
-       ABORT_ON(parse_events_add_numeric(list, &data->idx,
-                                         PERF_TYPE_RAW, $1, NULL));
+       ABORT_ON(parse_events_add_numeric(data, list, PERF_TYPE_RAW, $1, NULL));
        $$ = list;
 }
 
@@ -450,7 +454,7 @@ PE_NAME '=' PE_NAME
        struct parse_events_term *term;
 
        ABORT_ON(parse_events_term__str(&term, PARSE_EVENTS__TERM_TYPE_USER,
-                                       $1, $3));
+                                       $1, $3, &@1, &@3));
        $$ = term;
 }
 |
@@ -459,7 +463,7 @@ PE_NAME '=' PE_VALUE
        struct parse_events_term *term;
 
        ABORT_ON(parse_events_term__num(&term, PARSE_EVENTS__TERM_TYPE_USER,
-                                       $1, $3));
+                                       $1, $3, &@1, &@3));
        $$ = term;
 }
 |
@@ -477,7 +481,7 @@ PE_NAME
        struct parse_events_term *term;
 
        ABORT_ON(parse_events_term__num(&term, PARSE_EVENTS__TERM_TYPE_USER,
-                                       $1, 1));
+                                       $1, 1, &@1, NULL));
        $$ = term;
 }
 |
@@ -494,7 +498,7 @@ PE_TERM '=' PE_NAME
 {
        struct parse_events_term *term;
 
-       ABORT_ON(parse_events_term__str(&term, (int)$1, NULL, $3));
+       ABORT_ON(parse_events_term__str(&term, (int)$1, NULL, $3, &@1, &@3));
        $$ = term;
 }
 |
@@ -502,7 +506,7 @@ PE_TERM '=' PE_VALUE
 {
        struct parse_events_term *term;
 
-       ABORT_ON(parse_events_term__num(&term, (int)$1, NULL, $3));
+       ABORT_ON(parse_events_term__num(&term, (int)$1, NULL, $3, &@1, &@3));
        $$ = term;
 }
 |
@@ -510,7 +514,7 @@ PE_TERM
 {
        struct parse_events_term *term;
 
-       ABORT_ON(parse_events_term__num(&term, (int)$1, NULL, 1));
+       ABORT_ON(parse_events_term__num(&term, (int)$1, NULL, 1, &@1, NULL));
        $$ = term;
 }
 
@@ -520,7 +524,9 @@ sep_slash_dc: '/' | ':' |
 
 %%
 
-void parse_events_error(void *data __maybe_unused, void *scanner __maybe_unused,
+void parse_events_error(YYLTYPE *loc, void *data,
+                       void *scanner __maybe_unused,
                        char const *msg __maybe_unused)
 {
+       parse_events_evlist_error(data, loc->last_column, "parser error");
 }
index 59561fd86278276040fcde6c075b334008e13d67..367d8b816cc7e7ae0a99bb8200e0bd81b7999546 100644 (file)
@@ -123,6 +123,10 @@ struct option {
 #define OPT_LONG(s, l, v, h)        { .type = OPTION_LONG, .short_name = (s), .long_name = (l), .value = check_vtype(v, long *), .help = (h) }
 #define OPT_U64(s, l, v, h)         { .type = OPTION_U64, .short_name = (s), .long_name = (l), .value = check_vtype(v, u64 *), .help = (h) }
 #define OPT_STRING(s, l, v, a, h)   { .type = OPTION_STRING,  .short_name = (s), .long_name = (l), .value = check_vtype(v, const char **), (a), .help = (h) }
+#define OPT_STRING_OPTARG(s, l, v, a, h, d) \
+       { .type = OPTION_STRING,  .short_name = (s), .long_name = (l), \
+         .value = check_vtype(v, const char **), (a), .help = (h), \
+         .flags = PARSE_OPT_OPTARG, .defval = (intptr_t)(d) }
 #define OPT_STRING_NOEMPTY(s, l, v, a, h)   { .type = OPTION_STRING,  .short_name = (s), .long_name = (l), .value = check_vtype(v, const char **), (a), .help = (h), .flags = PARSE_OPT_NOEMPTY}
 #define OPT_DATE(s, l, v, h) \
        { .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), .value = (v), .argh = "time", .help = (h), .callback = parse_opt_approxidate_cb }
index 48411674da0f9cef6c87ba74a08bec513b112d6c..0fcc624eb76767b1c3fe211f678a981a71b744ce 100644 (file)
@@ -112,7 +112,11 @@ static int perf_pmu__parse_scale(struct perf_pmu_alias *alias, char *dir, char *
        if (sret < 0)
                goto error;
 
-       scale[sret] = '\0';
+       if (scale[sret - 1] == '\n')
+               scale[sret - 1] = '\0';
+       else
+               scale[sret] = '\0';
+
        /*
         * save current locale
         */
@@ -154,7 +158,10 @@ static int perf_pmu__parse_unit(struct perf_pmu_alias *alias, char *dir, char *n
 
        close(fd);
 
-       alias->unit[sret] = '\0';
+       if (alias->unit[sret - 1] == '\n')
+               alias->unit[sret - 1] = '\0';
+       else
+               alias->unit[sret] = '\0';
 
        return 0;
 error:
@@ -442,6 +449,10 @@ static struct perf_pmu *pmu_lookup(const char *name)
        LIST_HEAD(aliases);
        __u32 type;
 
+       /* No support for intel_bts or intel_pt so disallow them */
+       if (!strcmp(name, "intel_bts") || !strcmp(name, "intel_pt"))
+               return NULL;
+
        /*
         * The pmu data we store & need consists of the pmu
         * type value and format definitions. Load both right
@@ -579,6 +590,38 @@ static int pmu_resolve_param_term(struct parse_events_term *term,
        return -1;
 }
 
+static char *formats_error_string(struct list_head *formats)
+{
+       struct perf_pmu_format *format;
+       char *err, *str;
+       static const char *static_terms = "config,config1,config2,name,period,branch_type\n";
+       unsigned i = 0;
+
+       if (!asprintf(&str, "valid terms:"))
+               return NULL;
+
+       /* sysfs exported terms */
+       list_for_each_entry(format, formats, list) {
+               char c = i++ ? ',' : ' ';
+
+               err = str;
+               if (!asprintf(&str, "%s%c%s", err, c, format->name))
+                       goto fail;
+               free(err);
+       }
+
+       /* static terms */
+       err = str;
+       if (!asprintf(&str, "%s,%s", err, static_terms))
+               goto fail;
+
+       free(err);
+       return str;
+fail:
+       free(err);
+       return NULL;
+}
+
 /*
  * Setup one of config[12] attr members based on the
  * user input data - term parameter.
@@ -587,7 +630,7 @@ static int pmu_config_term(struct list_head *formats,
                           struct perf_event_attr *attr,
                           struct parse_events_term *term,
                           struct list_head *head_terms,
-                          bool zero)
+                          bool zero, struct parse_events_error *err)
 {
        struct perf_pmu_format *format;
        __u64 *vp;
@@ -611,6 +654,11 @@ static int pmu_config_term(struct list_head *formats,
        if (!format) {
                if (verbose)
                        printf("Invalid event/parameter '%s'\n", term->config);
+               if (err) {
+                       err->idx  = term->err_term;
+                       err->str  = strdup("unknown term");
+                       err->help = formats_error_string(formats);
+               }
                return -EINVAL;
        }
 
@@ -636,9 +684,14 @@ static int pmu_config_term(struct list_head *formats,
                val = term->val.num;
        else if (term->type_val == PARSE_EVENTS__TERM_TYPE_STR) {
                if (strcmp(term->val.str, "?")) {
-                       if (verbose)
+                       if (verbose) {
                                pr_info("Invalid sysfs entry %s=%s\n",
                                                term->config, term->val.str);
+                       }
+                       if (err) {
+                               err->idx = term->err_val;
+                               err->str = strdup("expected numeric value");
+                       }
                        return -EINVAL;
                }
 
@@ -654,12 +707,13 @@ static int pmu_config_term(struct list_head *formats,
 int perf_pmu__config_terms(struct list_head *formats,
                           struct perf_event_attr *attr,
                           struct list_head *head_terms,
-                          bool zero)
+                          bool zero, struct parse_events_error *err)
 {
        struct parse_events_term *term;
 
        list_for_each_entry(term, head_terms, list) {
-               if (pmu_config_term(formats, attr, term, head_terms, zero))
+               if (pmu_config_term(formats, attr, term, head_terms,
+                                   zero, err))
                        return -EINVAL;
        }
 
@@ -672,12 +726,14 @@ int perf_pmu__config_terms(struct list_head *formats,
  * 2) pmu format definitions - specified by pmu parameter
  */
 int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr,
-                    struct list_head *head_terms)
+                    struct list_head *head_terms,
+                    struct parse_events_error *err)
 {
        bool zero = !!pmu->default_config;
 
        attr->type = pmu->type;
-       return perf_pmu__config_terms(&pmu->format, attr, head_terms, zero);
+       return perf_pmu__config_terms(&pmu->format, attr, head_terms,
+                                     zero, err);
 }
 
 static struct perf_pmu_alias *pmu_find_alias(struct perf_pmu *pmu,
index 6b1249fbdb5f3a736c7c55a4005bf2c679addfa7..7b9c8cf8ae3e590578abb0f71a41739cb8fc968a 100644 (file)
@@ -4,6 +4,7 @@
 #include <linux/bitmap.h>
 #include <linux/perf_event.h>
 #include <stdbool.h>
+#include "parse-events.h"
 
 enum {
        PERF_PMU_FORMAT_VALUE_CONFIG,
@@ -47,11 +48,12 @@ struct perf_pmu_alias {
 
 struct perf_pmu *perf_pmu__find(const char *name);
 int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr,
-                    struct list_head *head_terms);
+                    struct list_head *head_terms,
+                    struct parse_events_error *error);
 int perf_pmu__config_terms(struct list_head *formats,
                           struct perf_event_attr *attr,
                           struct list_head *head_terms,
-                          bool zero);
+                          bool zero, struct parse_events_error *error);
 int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms,
                          struct perf_pmu_info *info);
 struct list_head *perf_pmu__alias(struct perf_pmu *pmu,
index d05b77cf35f77051354b9d08acc035cf4575dd5b..076527b639bdbcab38b4e196f1d388352f7e22c1 100644 (file)
@@ -51,6 +51,7 @@
 #define PERFPROBE_GROUP "probe"
 
 bool probe_event_dry_run;      /* Dry run flag */
+struct probe_conf probe_conf;
 
 #define semantic_error(msg ...) pr_err("Semantic error :" msg)
 
@@ -161,18 +162,18 @@ static u64 kernel_get_symbol_address_by_name(const char *name, bool reloc)
 
 static struct map *kernel_get_module_map(const char *module)
 {
-       struct rb_node *nd;
        struct map_groups *grp = &host_machine->kmaps;
+       struct maps *maps = &grp->maps[MAP__FUNCTION];
+       struct map *pos;
 
        /* A file path -- this is an offline module */
        if (module && strchr(module, '/'))
-               return machine__new_module(host_machine, 0, module);
+               return machine__findnew_module_map(host_machine, 0, module);
 
        if (!module)
                module = "kernel";
 
-       for (nd = rb_first(&grp->maps[MAP__FUNCTION]); nd; nd = rb_next(nd)) {
-               struct map *pos = rb_entry(nd, struct map, rb_node);
+       for (pos = maps__first(maps); pos; pos = map__next(pos)) {
                if (strncmp(pos->dso->short_name + 1, module,
                            pos->dso->short_name_len - 2) == 0) {
                        return pos;
@@ -194,52 +195,11 @@ static void put_target_map(struct map *map, bool user)
 {
        if (map && user) {
                /* Only the user map needs to be released */
-               dso__delete(map->dso);
-               map__delete(map);
+               map__put(map);
        }
 }
 
 
-static struct dso *kernel_get_module_dso(const char *module)
-{
-       struct dso *dso;
-       struct map *map;
-       const char *vmlinux_name;
-
-       if (module) {
-               list_for_each_entry(dso, &host_machine->kernel_dsos.head,
-                                   node) {
-                       if (strncmp(dso->short_name + 1, module,
-                                   dso->short_name_len - 2) == 0)
-                               goto found;
-               }
-               pr_debug("Failed to find module %s.\n", module);
-               return NULL;
-       }
-
-       map = host_machine->vmlinux_maps[MAP__FUNCTION];
-       dso = map->dso;
-
-       vmlinux_name = symbol_conf.vmlinux_name;
-       if (vmlinux_name) {
-               if (dso__load_vmlinux(dso, map, vmlinux_name, false, NULL) <= 0)
-                       return NULL;
-       } else {
-               if (dso__load_vmlinux_path(dso, map, NULL) <= 0) {
-                       pr_debug("Failed to load kernel map.\n");
-                       return NULL;
-               }
-       }
-found:
-       return dso;
-}
-
-const char *kernel_get_module_path(const char *module)
-{
-       struct dso *dso = kernel_get_module_dso(module);
-       return (dso) ? dso->long_name : NULL;
-}
-
 static int convert_exec_to_group(const char *exec, char **result)
 {
        char *ptr1, *ptr2, *exec_copy;
@@ -286,7 +246,55 @@ static void clear_probe_trace_events(struct probe_trace_event *tevs, int ntevs)
                clear_probe_trace_event(tevs + i);
 }
 
+static bool kprobe_blacklist__listed(unsigned long address);
+static bool kprobe_warn_out_range(const char *symbol, unsigned long address)
+{
+       /* Get the address of _etext for checking non-probable text symbol */
+       if (kernel_get_symbol_address_by_name("_etext", false) < address)
+               pr_warning("%s is out of .text, skip it.\n", symbol);
+       else if (kprobe_blacklist__listed(address))
+               pr_warning("%s is blacklisted function, skip it.\n", symbol);
+       else
+               return false;
+
+       return true;
+}
+
 #ifdef HAVE_DWARF_SUPPORT
+
+static int kernel_get_module_dso(const char *module, struct dso **pdso)
+{
+       struct dso *dso;
+       struct map *map;
+       const char *vmlinux_name;
+       int ret = 0;
+
+       if (module) {
+               list_for_each_entry(dso, &host_machine->dsos.head, node) {
+                       if (!dso->kernel)
+                               continue;
+                       if (strncmp(dso->short_name + 1, module,
+                                   dso->short_name_len - 2) == 0)
+                               goto found;
+               }
+               pr_debug("Failed to find module %s.\n", module);
+               return -ENOENT;
+       }
+
+       map = host_machine->vmlinux_maps[MAP__FUNCTION];
+       dso = map->dso;
+
+       vmlinux_name = symbol_conf.vmlinux_name;
+       dso->load_errno = 0;
+       if (vmlinux_name)
+               ret = dso__load_vmlinux(dso, map, vmlinux_name, false, NULL);
+       else
+               ret = dso__load_vmlinux_path(dso, map, NULL);
+found:
+       *pdso = dso;
+       return ret;
+}
+
 /*
  * Some binaries like glibc have special symbols which are on the symbol
  * table, but not in the debuginfo. If we can find the address of the
@@ -344,15 +352,14 @@ out:
 
 static int get_alternative_probe_event(struct debuginfo *dinfo,
                                       struct perf_probe_event *pev,
-                                      struct perf_probe_point *tmp,
-                                      const char *target)
+                                      struct perf_probe_point *tmp)
 {
        int ret;
 
        memcpy(tmp, &pev->point, sizeof(*tmp));
        memset(&pev->point, 0, sizeof(pev->point));
        ret = find_alternative_probe_point(dinfo, tmp, &pev->point,
-                                          target, pev->uprobes);
+                                          pev->target, pev->uprobes);
        if (ret < 0)
                memcpy(&pev->point, tmp, sizeof(*tmp));
 
@@ -390,16 +397,25 @@ static int get_alternative_line_range(struct debuginfo *dinfo,
 static struct debuginfo *open_debuginfo(const char *module, bool silent)
 {
        const char *path = module;
-       struct debuginfo *ret;
+       char reason[STRERR_BUFSIZE];
+       struct debuginfo *ret = NULL;
+       struct dso *dso = NULL;
+       int err;
 
        if (!module || !strchr(module, '/')) {
-               path = kernel_get_module_path(module);
-               if (!path) {
+               err = kernel_get_module_dso(module, &dso);
+               if (err < 0) {
+                       if (!dso || dso->load_errno == 0) {
+                               if (!strerror_r(-err, reason, STRERR_BUFSIZE))
+                                       strcpy(reason, "(unknown)");
+                       } else
+                               dso__strerror_load(dso, reason, STRERR_BUFSIZE);
                        if (!silent)
-                               pr_err("Failed to find path of %s module.\n",
-                                      module ?: "kernel");
+                               pr_err("Failed to find the path for %s: %s\n",
+                                       module ?: "kernel", reason);
                        return NULL;
                }
+               path = dso->long_name;
        }
        ret = debuginfo__new(path);
        if (!ret && !silent) {
@@ -413,6 +429,41 @@ static struct debuginfo *open_debuginfo(const char *module, bool silent)
        return ret;
 }
 
+/* For caching the last debuginfo */
+static struct debuginfo *debuginfo_cache;
+static char *debuginfo_cache_path;
+
+static struct debuginfo *debuginfo_cache__open(const char *module, bool silent)
+{
+       if ((debuginfo_cache_path && !strcmp(debuginfo_cache_path, module)) ||
+           (!debuginfo_cache_path && !module && debuginfo_cache))
+               goto out;
+
+       /* Copy module path */
+       free(debuginfo_cache_path);
+       if (module) {
+               debuginfo_cache_path = strdup(module);
+               if (!debuginfo_cache_path) {
+                       debuginfo__delete(debuginfo_cache);
+                       debuginfo_cache = NULL;
+                       goto out;
+               }
+       }
+
+       debuginfo_cache = open_debuginfo(module, silent);
+       if (!debuginfo_cache)
+               zfree(&debuginfo_cache_path);
+out:
+       return debuginfo_cache;
+}
+
+static void debuginfo_cache__exit(void)
+{
+       debuginfo__delete(debuginfo_cache);
+       debuginfo_cache = NULL;
+       zfree(&debuginfo_cache_path);
+}
+
 
 static int get_text_start_address(const char *exec, unsigned long *address)
 {
@@ -474,12 +525,11 @@ static int find_perf_probe_point_from_dwarf(struct probe_trace_point *tp,
        pr_debug("try to find information at %" PRIx64 " in %s\n", addr,
                 tp->module ? : "kernel");
 
-       dinfo = open_debuginfo(tp->module, verbose == 0);
-       if (dinfo) {
+       dinfo = debuginfo_cache__open(tp->module, verbose == 0);
+       if (dinfo)
                ret = debuginfo__find_probe_point(dinfo,
                                                 (unsigned long)addr, pp);
-               debuginfo__delete(dinfo);
-       } else
+       else
                ret = -ENOENT;
 
        if (ret > 0) {
@@ -558,7 +608,7 @@ static int post_process_probe_trace_events(struct probe_trace_event *tevs,
 {
        struct ref_reloc_sym *reloc_sym;
        char *tmp;
-       int i;
+       int i, skipped = 0;
 
        if (uprobe)
                return add_exec_to_probe_trace_events(tevs, ntevs, module);
@@ -574,31 +624,40 @@ static int post_process_probe_trace_events(struct probe_trace_event *tevs,
        }
 
        for (i = 0; i < ntevs; i++) {
-               if (tevs[i].point.address && !tevs[i].point.retprobe) {
+               if (!tevs[i].point.address || tevs[i].point.retprobe)
+                       continue;
+               /* If we found a wrong one, mark it by NULL symbol */
+               if (kprobe_warn_out_range(tevs[i].point.symbol,
+                                         tevs[i].point.address)) {
+                       tmp = NULL;
+                       skipped++;
+               } else {
                        tmp = strdup(reloc_sym->name);
                        if (!tmp)
                                return -ENOMEM;
-                       free(tevs[i].point.symbol);
-                       tevs[i].point.symbol = tmp;
-                       tevs[i].point.offset = tevs[i].point.address -
-                                              reloc_sym->unrelocated_addr;
                }
+               /* If we have no realname, use symbol for it */
+               if (!tevs[i].point.realname)
+                       tevs[i].point.realname = tevs[i].point.symbol;
+               else
+                       free(tevs[i].point.symbol);
+               tevs[i].point.symbol = tmp;
+               tevs[i].point.offset = tevs[i].point.address -
+                                      reloc_sym->unrelocated_addr;
        }
-       return 0;
+       return skipped;
 }
 
 /* Try to find perf_probe_event with debuginfo */
 static int try_to_find_probe_trace_events(struct perf_probe_event *pev,
-                                         struct probe_trace_event **tevs,
-                                         int max_tevs, const char *target)
+                                         struct probe_trace_event **tevs)
 {
        bool need_dwarf = perf_probe_event_need_dwarf(pev);
        struct perf_probe_point tmp;
        struct debuginfo *dinfo;
        int ntevs, ret = 0;
 
-       dinfo = open_debuginfo(target, !need_dwarf);
-
+       dinfo = open_debuginfo(pev->target, !need_dwarf);
        if (!dinfo) {
                if (need_dwarf)
                        return -ENOENT;
@@ -608,13 +667,12 @@ static int try_to_find_probe_trace_events(struct perf_probe_event *pev,
 
        pr_debug("Try to find probe point from debuginfo.\n");
        /* Searching trace events corresponding to a probe event */
-       ntevs = debuginfo__find_trace_events(dinfo, pev, tevs, max_tevs);
+       ntevs = debuginfo__find_trace_events(dinfo, pev, tevs);
 
        if (ntevs == 0) {  /* Not found, retry with an alternative */
-               ret = get_alternative_probe_event(dinfo, pev, &tmp, target);
+               ret = get_alternative_probe_event(dinfo, pev, &tmp);
                if (!ret) {
-                       ntevs = debuginfo__find_trace_events(dinfo, pev,
-                                                            tevs, max_tevs);
+                       ntevs = debuginfo__find_trace_events(dinfo, pev, tevs);
                        /*
                         * Write back to the original probe_event for
                         * setting appropriate (user given) event name
@@ -629,12 +687,15 @@ static int try_to_find_probe_trace_events(struct perf_probe_event *pev,
        if (ntevs > 0) {        /* Succeeded to find trace events */
                pr_debug("Found %d probe_trace_events.\n", ntevs);
                ret = post_process_probe_trace_events(*tevs, ntevs,
-                                                       target, pev->uprobes);
-               if (ret < 0) {
+                                               pev->target, pev->uprobes);
+               if (ret < 0 || ret == ntevs) {
                        clear_probe_trace_events(*tevs, ntevs);
                        zfree(tevs);
                }
-               return ret < 0 ? ret : ntevs;
+               if (ret != ntevs)
+                       return ret < 0 ? ret : ntevs;
+               ntevs = 0;
+               /* Fall through */
        }
 
        if (ntevs == 0) {       /* No error but failed to find probe point. */
@@ -809,8 +870,7 @@ int show_line_range(struct line_range *lr, const char *module, bool user)
 
 static int show_available_vars_at(struct debuginfo *dinfo,
                                  struct perf_probe_event *pev,
-                                 int max_vls, struct strfilter *_filter,
-                                 bool externs, const char *target)
+                                 struct strfilter *_filter)
 {
        char *buf;
        int ret, i, nvars;
@@ -824,13 +884,12 @@ static int show_available_vars_at(struct debuginfo *dinfo,
                return -EINVAL;
        pr_debug("Searching variables at %s\n", buf);
 
-       ret = debuginfo__find_available_vars_at(dinfo, pev, &vls,
-                                               max_vls, externs);
+       ret = debuginfo__find_available_vars_at(dinfo, pev, &vls);
        if (!ret) {  /* Not found, retry with an alternative */
-               ret = get_alternative_probe_event(dinfo, pev, &tmp, target);
+               ret = get_alternative_probe_event(dinfo, pev, &tmp);
                if (!ret) {
                        ret = debuginfo__find_available_vars_at(dinfo, pev,
-                                               &vls, max_vls, externs);
+                                                               &vls);
                        /* Release the old probe_point */
                        clear_perf_probe_point(&tmp);
                }
@@ -877,8 +936,7 @@ end:
 
 /* Show available variables on given probe point */
 int show_available_vars(struct perf_probe_event *pevs, int npevs,
-                       int max_vls, const char *module,
-                       struct strfilter *_filter, bool externs)
+                       struct strfilter *_filter)
 {
        int i, ret = 0;
        struct debuginfo *dinfo;
@@ -887,7 +945,7 @@ int show_available_vars(struct perf_probe_event *pevs, int npevs,
        if (ret < 0)
                return ret;
 
-       dinfo = open_debuginfo(module, false);
+       dinfo = open_debuginfo(pevs->target, false);
        if (!dinfo) {
                ret = -ENOENT;
                goto out;
@@ -896,8 +954,7 @@ int show_available_vars(struct perf_probe_event *pevs, int npevs,
        setup_pager();
 
        for (i = 0; i < npevs && ret >= 0; i++)
-               ret = show_available_vars_at(dinfo, &pevs[i], max_vls, _filter,
-                                            externs, module);
+               ret = show_available_vars_at(dinfo, &pevs[i], _filter);
 
        debuginfo__delete(dinfo);
 out:
@@ -907,6 +964,10 @@ out:
 
 #else  /* !HAVE_DWARF_SUPPORT */
 
+static void debuginfo_cache__exit(void)
+{
+}
+
 static int
 find_perf_probe_point_from_dwarf(struct probe_trace_point *tp __maybe_unused,
                                 struct perf_probe_point *pp __maybe_unused,
@@ -916,9 +977,7 @@ find_perf_probe_point_from_dwarf(struct probe_trace_point *tp __maybe_unused,
 }
 
 static int try_to_find_probe_trace_events(struct perf_probe_event *pev,
-                               struct probe_trace_event **tevs __maybe_unused,
-                               int max_tevs __maybe_unused,
-                               const char *target __maybe_unused)
+                               struct probe_trace_event **tevs __maybe_unused)
 {
        if (perf_probe_event_need_dwarf(pev)) {
                pr_warning("Debuginfo-analysis is not supported.\n");
@@ -937,10 +996,8 @@ int show_line_range(struct line_range *lr __maybe_unused,
 }
 
 int show_available_vars(struct perf_probe_event *pevs __maybe_unused,
-                       int npevs __maybe_unused, int max_vls __maybe_unused,
-                       const char *module __maybe_unused,
-                       struct strfilter *filter __maybe_unused,
-                       bool externs __maybe_unused)
+                       int npevs __maybe_unused,
+                       struct strfilter *filter __maybe_unused)
 {
        pr_warning("Debuginfo-analysis is not supported.\n");
        return -ENOSYS;
@@ -980,6 +1037,18 @@ static int parse_line_num(char **ptr, int *val, const char *what)
        return 0;
 }
 
+/* Check the name is good for event, group or function */
+static bool is_c_func_name(const char *name)
+{
+       if (!isalpha(*name) && *name != '_')
+               return false;
+       while (*++name != '\0') {
+               if (!isalpha(*name) && !isdigit(*name) && *name != '_')
+                       return false;
+       }
+       return true;
+}
+
 /*
  * Stuff 'lr' according to the line range described by 'arg'.
  * The line range syntax is described by:
@@ -1048,10 +1117,15 @@ int parse_line_range_desc(const char *arg, struct line_range *lr)
                        goto err;
                }
                lr->function = name;
-       } else if (strchr(name, '.'))
+       } else if (strchr(name, '/') || strchr(name, '.'))
                lr->file = name;
-       else
+       else if (is_c_func_name(name))/* We reuse it for checking funcname */
                lr->function = name;
+       else {  /* Invalid name */
+               semantic_error("'%s' is not a valid function name.\n", name);
+               err = -EINVAL;
+               goto err;
+       }
 
        return 0;
 err:
@@ -1059,24 +1133,13 @@ err:
        return err;
 }
 
-/* Check the name is good for event/group */
-static bool check_event_name(const char *name)
-{
-       if (!isalpha(*name) && *name != '_')
-               return false;
-       while (*++name != '\0') {
-               if (!isalpha(*name) && !isdigit(*name) && *name != '_')
-                       return false;
-       }
-       return true;
-}
-
 /* Parse probepoint definition. */
 static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev)
 {
        struct perf_probe_point *pp = &pev->point;
        char *ptr, *tmp;
        char c, nc = 0;
+       bool file_spec = false;
        /*
         * <Syntax>
         * perf probe [EVENT=]SRC[:LN|;PTN]
@@ -1095,7 +1158,7 @@ static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev)
                        semantic_error("Group name is not supported yet.\n");
                        return -ENOTSUP;
                }
-               if (!check_event_name(arg)) {
+               if (!is_c_func_name(arg)) {
                        semantic_error("%s is bad for event name -it must "
                                       "follow C symbol-naming rule.\n", arg);
                        return -EINVAL;
@@ -1107,6 +1170,23 @@ static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev)
                arg = tmp;
        }
 
+       /*
+        * Check arg is function or file name and copy it.
+        *
+        * We consider arg to be a file spec if and only if it satisfies
+        * all of the below criteria::
+        * - it does not include any of "+@%",
+        * - it includes one of ":;", and
+        * - it has a period '.' in the name.
+        *
+        * Otherwise, we consider arg to be a function specification.
+        */
+       if (!strpbrk(arg, "+@%") && (ptr = strpbrk(arg, ";:")) != NULL) {
+               /* This is a file spec if it includes a '.' before ; or : */
+               if (memchr(arg, '.', ptr - arg))
+                       file_spec = true;
+       }
+
        ptr = strpbrk(arg, ";:+@%");
        if (ptr) {
                nc = *ptr;
@@ -1117,10 +1197,9 @@ static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev)
        if (tmp == NULL)
                return -ENOMEM;
 
-       /* Check arg is function or file and copy it */
-       if (strchr(tmp, '.'))   /* File */
+       if (file_spec)
                pp->file = tmp;
-       else                    /* Function */
+       else
                pp->function = tmp;
 
        /* Parse other options */
@@ -1762,8 +1841,7 @@ static int find_perf_probe_point_from_map(struct probe_trace_point *tp,
 
 out:
        if (map && !is_kprobe) {
-               dso__delete(map->dso);
-               map__delete(map);
+               map__put(map);
        }
 
        return ret;
@@ -1877,6 +1955,7 @@ static void clear_probe_trace_event(struct probe_trace_event *tev)
        free(tev->event);
        free(tev->group);
        free(tev->point.symbol);
+       free(tev->point.realname);
        free(tev->point.module);
        for (i = 0; i < tev->nargs; i++) {
                free(tev->args[i].name);
@@ -1954,7 +2033,7 @@ static int open_probe_events(const char *trace_file, bool readwrite)
        if (ret >= 0) {
                pr_debug("Opening %s write=%d\n", buf, readwrite);
                if (readwrite && !probe_event_dry_run)
-                       ret = open(buf, O_RDWR, O_APPEND);
+                       ret = open(buf, O_RDWR | O_APPEND, 0);
                else
                        ret = open(buf, O_RDONLY, 0);
 
@@ -2095,9 +2174,31 @@ kprobe_blacklist__find_by_address(struct list_head *blacklist,
        return NULL;
 }
 
-/* Show an event */
-static int show_perf_probe_event(struct perf_probe_event *pev,
-                                const char *module)
+static LIST_HEAD(kprobe_blacklist);
+
+static void kprobe_blacklist__init(void)
+{
+       if (!list_empty(&kprobe_blacklist))
+               return;
+
+       if (kprobe_blacklist__load(&kprobe_blacklist) < 0)
+               pr_debug("No kprobe blacklist support, ignored\n");
+}
+
+static void kprobe_blacklist__release(void)
+{
+       kprobe_blacklist__delete(&kprobe_blacklist);
+}
+
+static bool kprobe_blacklist__listed(unsigned long address)
+{
+       return !!kprobe_blacklist__find_by_address(&kprobe_blacklist, address);
+}
+
+static int perf_probe_event__sprintf(const char *group, const char *event,
+                                    struct perf_probe_event *pev,
+                                    const char *module,
+                                    struct strbuf *result)
 {
        int i, ret;
        char buf[128];
@@ -2108,30 +2209,67 @@ static int show_perf_probe_event(struct perf_probe_event *pev,
        if (!place)
                return -EINVAL;
 
-       ret = e_snprintf(buf, 128, "%s:%s", pev->group, pev->event);
+       ret = e_snprintf(buf, 128, "%s:%s", group, event);
        if (ret < 0)
-               return ret;
+               goto out;
 
-       pr_info("  %-20s (on %s", buf, place);
+       strbuf_addf(result, "  %-20s (on %s", buf, place);
        if (module)
-               pr_info(" in %s", module);
+               strbuf_addf(result, " in %s", module);
 
        if (pev->nargs > 0) {
-               pr_info(" with");
+               strbuf_addstr(result, " with");
                for (i = 0; i < pev->nargs; i++) {
                        ret = synthesize_perf_probe_arg(&pev->args[i],
                                                        buf, 128);
                        if (ret < 0)
-                               break;
-                       pr_info(" %s", buf);
+                               goto out;
+                       strbuf_addf(result, " %s", buf);
                }
        }
-       pr_info(")\n");
+       strbuf_addch(result, ')');
+out:
        free(place);
        return ret;
 }
 
-static int __show_perf_probe_events(int fd, bool is_kprobe)
+/* Show an event */
+static int show_perf_probe_event(const char *group, const char *event,
+                                struct perf_probe_event *pev,
+                                const char *module, bool use_stdout)
+{
+       struct strbuf buf = STRBUF_INIT;
+       int ret;
+
+       ret = perf_probe_event__sprintf(group, event, pev, module, &buf);
+       if (ret >= 0) {
+               if (use_stdout)
+                       printf("%s\n", buf.buf);
+               else
+                       pr_info("%s\n", buf.buf);
+       }
+       strbuf_release(&buf);
+
+       return ret;
+}
+
+static bool filter_probe_trace_event(struct probe_trace_event *tev,
+                                    struct strfilter *filter)
+{
+       char tmp[128];
+
+       /* At first, check the event name itself */
+       if (strfilter__compare(filter, tev->event))
+               return true;
+
+       /* Next, check the combination of name and group */
+       if (e_snprintf(tmp, 128, "%s:%s", tev->group, tev->event) < 0)
+               return false;
+       return strfilter__compare(filter, tmp);
+}
+
+static int __show_perf_probe_events(int fd, bool is_kprobe,
+                                   struct strfilter *filter)
 {
        int ret = 0;
        struct probe_trace_event tev;
@@ -2149,24 +2287,31 @@ static int __show_perf_probe_events(int fd, bool is_kprobe)
        strlist__for_each(ent, rawlist) {
                ret = parse_probe_trace_command(ent->s, &tev);
                if (ret >= 0) {
+                       if (!filter_probe_trace_event(&tev, filter))
+                               goto next;
                        ret = convert_to_perf_probe_event(&tev, &pev,
                                                                is_kprobe);
-                       if (ret >= 0)
-                               ret = show_perf_probe_event(&pev,
-                                                           tev.point.module);
+                       if (ret < 0)
+                               goto next;
+                       ret = show_perf_probe_event(pev.group, pev.event,
+                                                   &pev, tev.point.module,
+                                                   true);
                }
+next:
                clear_perf_probe_event(&pev);
                clear_probe_trace_event(&tev);
                if (ret < 0)
                        break;
        }
        strlist__delete(rawlist);
+       /* Cleanup cached debuginfo if needed */
+       debuginfo_cache__exit();
 
        return ret;
 }
 
 /* List up current perf-probe events */
-int show_perf_probe_events(void)
+int show_perf_probe_events(struct strfilter *filter)
 {
        int kp_fd, up_fd, ret;
 
@@ -2178,7 +2323,7 @@ int show_perf_probe_events(void)
 
        kp_fd = open_kprobe_events(false);
        if (kp_fd >= 0) {
-               ret = __show_perf_probe_events(kp_fd, true);
+               ret = __show_perf_probe_events(kp_fd, true, filter);
                close(kp_fd);
                if (ret < 0)
                        goto out;
@@ -2192,7 +2337,7 @@ int show_perf_probe_events(void)
        }
 
        if (up_fd >= 0) {
-               ret = __show_perf_probe_events(up_fd, false);
+               ret = __show_perf_probe_events(up_fd, false, filter);
                close(up_fd);
        }
 out:
@@ -2266,6 +2411,10 @@ static int get_new_event_name(char *buf, size_t len, const char *base,
                              struct strlist *namelist, bool allow_suffix)
 {
        int i, ret;
+       char *p;
+
+       if (*base == '.')
+               base++;
 
        /* Try no suffix */
        ret = e_snprintf(buf, len, "%s", base);
@@ -2273,6 +2422,10 @@ static int get_new_event_name(char *buf, size_t len, const char *base,
                pr_debug("snprintf() failed: %d\n", ret);
                return ret;
        }
+       /* Cut off the postfixes (e.g. .const, .isra)*/
+       p = strchr(buf, '.');
+       if (p && p != buf)
+               *p = '\0';
        if (!strlist__has_entry(namelist, buf))
                return 0;
 
@@ -2328,10 +2481,9 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,
        int i, fd, ret;
        struct probe_trace_event *tev = NULL;
        char buf[64];
-       const char *event, *group;
+       const char *event = NULL, *group = NULL;
        struct strlist *namelist;
-       LIST_HEAD(blacklist);
-       struct kprobe_blacklist_node *node;
+       bool safename;
 
        if (pev->uprobes)
                fd = open_uprobe_events(true);
@@ -2347,34 +2499,26 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,
        namelist = get_probe_trace_event_names(fd, false);
        if (!namelist) {
                pr_debug("Failed to get current event list.\n");
-               return -EIO;
-       }
-       /* Get kprobe blacklist if exists */
-       if (!pev->uprobes) {
-               ret = kprobe_blacklist__load(&blacklist);
-               if (ret < 0)
-                       pr_debug("No kprobe blacklist support, ignored\n");
+               ret = -ENOMEM;
+               goto close_out;
        }
 
+       safename = (pev->point.function && !strisglob(pev->point.function));
        ret = 0;
        pr_info("Added new event%s\n", (ntevs > 1) ? "s:" : ":");
        for (i = 0; i < ntevs; i++) {
                tev = &tevs[i];
-               /* Ensure that the address is NOT blacklisted */
-               node = kprobe_blacklist__find_by_address(&blacklist,
-                                                        tev->point.address);
-               if (node) {
-                       pr_warning("Warning: Skipped probing on blacklisted function: %s\n", node->symbol);
+               /* Skip if the symbol is out of .text or blacklisted */
+               if (!tev->point.symbol)
                        continue;
-               }
 
                if (pev->event)
                        event = pev->event;
                else
-                       if (pev->point.function)
+                       if (safename)
                                event = pev->point.function;
                        else
-                               event = tev->point.symbol;
+                               event = tev->point.realname;
                if (pev->group)
                        group = pev->group;
                else
@@ -2399,15 +2543,12 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,
                /* Add added event name to namelist */
                strlist__add(namelist, event);
 
-               /* Trick here - save current event/group */
-               event = pev->event;
-               group = pev->group;
-               pev->event = tev->event;
-               pev->group = tev->group;
-               show_perf_probe_event(pev, tev->point.module);
-               /* Trick here - restore current event/group */
-               pev->event = (char *)event;
-               pev->group = (char *)group;
+               /* We use tev's name for showing new events */
+               show_perf_probe_event(tev->group, tev->event, pev,
+                                     tev->point.module, false);
+               /* Save the last valid name */
+               event = tev->event;
+               group = tev->group;
 
                /*
                 * Probes after the first probe which comes from same
@@ -2421,26 +2562,34 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,
                warn_uprobe_event_compat(tev);
 
        /* Note that it is possible to skip all events because of blacklist */
-       if (ret >= 0 && tev->event) {
+       if (ret >= 0 && event) {
                /* Show how to use the event. */
                pr_info("\nYou can now use it in all perf tools, such as:\n\n");
-               pr_info("\tperf record -e %s:%s -aR sleep 1\n\n", tev->group,
-                        tev->event);
+               pr_info("\tperf record -e %s:%s -aR sleep 1\n\n", group, event);
        }
 
-       kprobe_blacklist__delete(&blacklist);
        strlist__delete(namelist);
+close_out:
        close(fd);
        return ret;
 }
 
-static int find_probe_functions(struct map *map, char *name)
+static int find_probe_functions(struct map *map, char *name,
+                               struct symbol **syms)
 {
        int found = 0;
        struct symbol *sym;
+       struct rb_node *tmp;
+
+       if (map__load(map, NULL) < 0)
+               return 0;
 
-       map__for_each_symbol_by_name(map, name, sym) {
-               found++;
+       map__for_each_symbol(map, sym, tmp) {
+               if (strglobmatch(sym->name, name)) {
+                       found++;
+                       if (syms && found < probe_conf.max_probes)
+                               syms[found - 1] = sym;
+               }
        }
 
        return found;
@@ -2449,42 +2598,52 @@ static int find_probe_functions(struct map *map, char *name)
 #define strdup_or_goto(str, label)     \
        ({ char *__p = strdup(str); if (!__p) goto label; __p; })
 
+void __weak arch__fix_tev_from_maps(struct perf_probe_event *pev __maybe_unused,
+                               struct probe_trace_event *tev __maybe_unused,
+                               struct map *map __maybe_unused) { }
+
 /*
  * Find probe function addresses from map.
  * Return an error or the number of found probe_trace_event
  */
 static int find_probe_trace_events_from_map(struct perf_probe_event *pev,
-                                           struct probe_trace_event **tevs,
-                                           int max_tevs, const char *target)
+                                           struct probe_trace_event **tevs)
 {
        struct map *map = NULL;
        struct ref_reloc_sym *reloc_sym = NULL;
        struct symbol *sym;
+       struct symbol **syms = NULL;
        struct probe_trace_event *tev;
        struct perf_probe_point *pp = &pev->point;
        struct probe_trace_point *tp;
        int num_matched_functions;
-       int ret, i;
+       int ret, i, j, skipped = 0;
 
-       map = get_target_map(target, pev->uprobes);
+       map = get_target_map(pev->target, pev->uprobes);
        if (!map) {
                ret = -EINVAL;
                goto out;
        }
 
+       syms = malloc(sizeof(struct symbol *) * probe_conf.max_probes);
+       if (!syms) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
        /*
         * Load matched symbols: Since the different local symbols may have
         * same name but different addresses, this lists all the symbols.
         */
-       num_matched_functions = find_probe_functions(map, pp->function);
+       num_matched_functions = find_probe_functions(map, pp->function, syms);
        if (num_matched_functions == 0) {
                pr_err("Failed to find symbol %s in %s\n", pp->function,
-                       target ? : "kernel");
+                       pev->target ? : "kernel");
                ret = -ENOENT;
                goto out;
-       } else if (num_matched_functions > max_tevs) {
+       } else if (num_matched_functions > probe_conf.max_probes) {
                pr_err("Too many functions matched in %s\n",
-                       target ? : "kernel");
+                       pev->target ? : "kernel");
                ret = -E2BIG;
                goto out;
        }
@@ -2507,7 +2666,9 @@ static int find_probe_trace_events_from_map(struct perf_probe_event *pev,
 
        ret = 0;
 
-       map__for_each_symbol_by_name(map, pp->function, sym) {
+       for (j = 0; j < num_matched_functions; j++) {
+               sym = syms[j];
+
                tev = (*tevs) + ret;
                tp = &tev->point;
                if (ret == num_matched_functions) {
@@ -2524,16 +2685,24 @@ static int find_probe_trace_events_from_map(struct perf_probe_event *pev,
                }
                /* Add one probe point */
                tp->address = map->unmap_ip(map, sym->start) + pp->offset;
-               if (reloc_sym) {
+               /* If we found a wrong one, mark it by NULL symbol */
+               if (!pev->uprobes &&
+                   kprobe_warn_out_range(sym->name, tp->address)) {
+                       tp->symbol = NULL;      /* Skip it */
+                       skipped++;
+               } else if (reloc_sym) {
                        tp->symbol = strdup_or_goto(reloc_sym->name, nomem_out);
                        tp->offset = tp->address - reloc_sym->addr;
                } else {
                        tp->symbol = strdup_or_goto(sym->name, nomem_out);
                        tp->offset = pp->offset;
                }
+               tp->realname = strdup_or_goto(sym->name, nomem_out);
+
                tp->retprobe = pp->retprobe;
-               if (target)
-                       tev->point.module = strdup_or_goto(target, nomem_out);
+               if (pev->target)
+                       tev->point.module = strdup_or_goto(pev->target,
+                                                          nomem_out);
                tev->uprobes = pev->uprobes;
                tev->nargs = pev->nargs;
                if (tev->nargs) {
@@ -2555,10 +2724,16 @@ static int find_probe_trace_events_from_map(struct perf_probe_event *pev,
                                        strdup_or_goto(pev->args[i].type,
                                                        nomem_out);
                }
+               arch__fix_tev_from_maps(pev, tev, map);
+       }
+       if (ret == skipped) {
+               ret = -ENOENT;
+               goto err_out;
        }
 
 out:
        put_target_map(map, pev->uprobes);
+       free(syms);
        return ret;
 
 nomem_out:
@@ -2569,27 +2744,34 @@ err_out:
        goto out;
 }
 
+bool __weak arch__prefers_symtab(void) { return false; }
+
 static int convert_to_probe_trace_events(struct perf_probe_event *pev,
-                                         struct probe_trace_event **tevs,
-                                         int max_tevs, const char *target)
+                                        struct probe_trace_event **tevs)
 {
        int ret;
 
        if (pev->uprobes && !pev->group) {
                /* Replace group name if not given */
-               ret = convert_exec_to_group(target, &pev->group);
+               ret = convert_exec_to_group(pev->target, &pev->group);
                if (ret != 0) {
                        pr_warning("Failed to make a group name.\n");
                        return ret;
                }
        }
 
+       if (arch__prefers_symtab() && !perf_probe_event_need_dwarf(pev)) {
+               ret = find_probe_trace_events_from_map(pev, tevs);
+               if (ret > 0)
+                       return ret; /* Found in symbol table */
+       }
+
        /* Convert perf_probe_event with debuginfo */
-       ret = try_to_find_probe_trace_events(pev, tevs, max_tevs, target);
+       ret = try_to_find_probe_trace_events(pev, tevs);
        if (ret != 0)
                return ret;     /* Found in debuginfo or got an error */
 
-       return find_probe_trace_events_from_map(pev, tevs, max_tevs, target);
+       return find_probe_trace_events_from_map(pev, tevs);
 }
 
 struct __event_package {
@@ -2598,8 +2780,7 @@ struct __event_package {
        int                             ntevs;
 };
 
-int add_perf_probe_events(struct perf_probe_event *pevs, int npevs,
-                         int max_tevs, bool force_add)
+int add_perf_probe_events(struct perf_probe_event *pevs, int npevs)
 {
        int i, j, ret;
        struct __event_package *pkgs;
@@ -2619,20 +2800,24 @@ int add_perf_probe_events(struct perf_probe_event *pevs, int npevs,
        /* Loop 1: convert all events */
        for (i = 0; i < npevs; i++) {
                pkgs[i].pev = &pevs[i];
+               /* Init kprobe blacklist if needed */
+               if (!pkgs[i].pev->uprobes)
+                       kprobe_blacklist__init();
                /* Convert with or without debuginfo */
                ret  = convert_to_probe_trace_events(pkgs[i].pev,
-                                                    &pkgs[i].tevs,
-                                                    max_tevs,
-                                                    pkgs[i].pev->target);
+                                                    &pkgs[i].tevs);
                if (ret < 0)
                        goto end;
                pkgs[i].ntevs = ret;
        }
+       /* This just release blacklist only if allocated */
+       kprobe_blacklist__release();
 
        /* Loop 2: add all events */
        for (i = 0; i < npevs; i++) {
                ret = __add_probe_trace_events(pkgs[i].pev, pkgs[i].tevs,
-                                               pkgs[i].ntevs, force_add);
+                                              pkgs[i].ntevs,
+                                              probe_conf.force_add);
                if (ret < 0)
                        break;
        }
@@ -2684,40 +2869,39 @@ error:
        return ret;
 }
 
-static int del_trace_probe_event(int fd, const char *buf,
-                                                 struct strlist *namelist)
+static int del_trace_probe_events(int fd, struct strfilter *filter,
+                                 struct strlist *namelist)
 {
-       struct str_node *ent, *n;
-       int ret = -1;
+       struct str_node *ent;
+       const char *p;
+       int ret = -ENOENT;
 
-       if (strpbrk(buf, "*?")) { /* Glob-exp */
-               strlist__for_each_safe(ent, n, namelist)
-                       if (strglobmatch(ent->s, buf)) {
-                               ret = __del_trace_probe_event(fd, ent);
-                               if (ret < 0)
-                                       break;
-                               strlist__remove(namelist, ent);
-                       }
-       } else {
-               ent = strlist__find(namelist, buf);
-               if (ent) {
+       if (!namelist)
+               return -ENOENT;
+
+       strlist__for_each(ent, namelist) {
+               p = strchr(ent->s, ':');
+               if ((p && strfilter__compare(filter, p + 1)) ||
+                   strfilter__compare(filter, ent->s)) {
                        ret = __del_trace_probe_event(fd, ent);
-                       if (ret >= 0)
-                               strlist__remove(namelist, ent);
+                       if (ret < 0)
+                               break;
                }
        }
 
        return ret;
 }
 
-int del_perf_probe_events(struct strlist *dellist)
+int del_perf_probe_events(struct strfilter *filter)
 {
-       int ret = -1, ufd = -1, kfd = -1;
-       char buf[128];
-       const char *group, *event;
-       char *p, *str;
-       struct str_node *ent;
+       int ret, ret2, ufd = -1, kfd = -1;
        struct strlist *namelist = NULL, *unamelist = NULL;
+       char *str = strfilter__string(filter);
+
+       if (!str)
+               return -EINVAL;
+
+       pr_debug("Delete filter: \'%s\'\n", str);
 
        /* Get current event names */
        kfd = open_kprobe_events(true);
@@ -2730,49 +2914,23 @@ int del_perf_probe_events(struct strlist *dellist)
 
        if (kfd < 0 && ufd < 0) {
                print_both_open_warning(kfd, ufd);
+               ret = kfd;
                goto error;
        }
 
-       if (namelist == NULL && unamelist == NULL)
+       ret = del_trace_probe_events(kfd, filter, namelist);
+       if (ret < 0 && ret != -ENOENT)
                goto error;
 
-       strlist__for_each(ent, dellist) {
-               str = strdup(ent->s);
-               if (str == NULL) {
-                       ret = -ENOMEM;
-                       goto error;
-               }
-               pr_debug("Parsing: %s\n", str);
-               p = strchr(str, ':');
-               if (p) {
-                       group = str;
-                       *p = '\0';
-                       event = p + 1;
-               } else {
-                       group = "*";
-                       event = str;
-               }
-
-               ret = e_snprintf(buf, 128, "%s:%s", group, event);
-               if (ret < 0) {
-                       pr_err("Failed to copy event.");
-                       free(str);
-                       goto error;
-               }
-
-               pr_debug("Group: %s, Event: %s\n", group, event);
-
-               if (namelist)
-                       ret = del_trace_probe_event(kfd, buf, namelist);
-
-               if (unamelist && ret != 0)
-                       ret = del_trace_probe_event(ufd, buf, unamelist);
-
-               if (ret != 0)
-                       pr_info("Info: Event \"%s\" does not exist.\n", buf);
-
-               free(str);
+       ret2 = del_trace_probe_events(ufd, filter, unamelist);
+       if (ret2 < 0 && ret2 != -ENOENT) {
+               ret = ret2;
+               goto error;
        }
+       if (ret == -ENOENT && ret2 == -ENOENT)
+               pr_debug("\"%s\" does not hit any event.\n", str);
+               /* Note that this is silently ignored */
+       ret = 0;
 
 error:
        if (kfd >= 0) {
@@ -2784,6 +2942,7 @@ error:
                strlist__delete(unamelist);
                close(ufd);
        }
+       free(str);
 
        return ret;
 }
@@ -2837,8 +2996,7 @@ int show_available_funcs(const char *target, struct strfilter *_filter,
        dso__fprintf_symbols_by_name(map->dso, map->type, stdout);
 end:
        if (user) {
-               dso__delete(map->dso);
-               map__delete(map);
+               map__put(map);
        }
        exit_symbol_maps();
 
index d6b783447be95d6b8764187f05692e0559eed371..31db6ee7db5478139dabfc97537568c0ec104736 100644 (file)
@@ -6,10 +6,20 @@
 #include "strlist.h"
 #include "strfilter.h"
 
+/* Probe related configurations */
+struct probe_conf {
+       bool    show_ext_vars;
+       bool    show_location_range;
+       bool    force_add;
+       bool    no_inlines;
+       int     max_probes;
+};
+extern struct probe_conf probe_conf;
 extern bool probe_event_dry_run;
 
 /* kprobe-tracer and uprobe-tracer tracing point */
 struct probe_trace_point {
+       char            *realname;      /* function real name (if needed) */
        char            *symbol;        /* Base symbol */
        char            *module;        /* Module name */
        unsigned long   offset;         /* Offset from symbol */
@@ -121,20 +131,18 @@ extern void line_range__clear(struct line_range *lr);
 /* Initialize line range */
 extern int line_range__init(struct line_range *lr);
 
-/* Internal use: Return kernel/module path */
-extern const char *kernel_get_module_path(const char *module);
-
-extern int add_perf_probe_events(struct perf_probe_event *pevs, int npevs,
-                                int max_probe_points, bool force_add);
-extern int del_perf_probe_events(struct strlist *dellist);
-extern int show_perf_probe_events(void);
+extern int add_perf_probe_events(struct perf_probe_event *pevs, int npevs);
+extern int del_perf_probe_events(struct strfilter *filter);
+extern int show_perf_probe_events(struct strfilter *filter);
 extern int show_line_range(struct line_range *lr, const char *module,
                           bool user);
 extern int show_available_vars(struct perf_probe_event *pevs, int npevs,
-                              int max_probe_points, const char *module,
-                              struct strfilter *filter, bool externs);
+                              struct strfilter *filter);
 extern int show_available_funcs(const char *module, struct strfilter *filter,
                                bool user);
+bool arch__prefers_symtab(void);
+void arch__fix_tev_from_maps(struct perf_probe_event *pev,
+                            struct probe_trace_event *tev, struct map *map);
 
 /* Maximum index number of event-name postfix */
 #define MAX_EVENT_INDEX        1024
index 2a76e14db73289d196a0171f4830693b46445e23..2da65a7108932857bd585681eb0fac195f0c850b 100644 (file)
@@ -130,7 +130,7 @@ struct debuginfo *debuginfo__new(const char *path)
                        continue;
                dinfo = __debuginfo__new(buf);
        }
-       dso__delete(dso);
+       dso__put(dso);
 
 out:
        /* if failed to open all distro debuginfo, open given binary */
@@ -177,7 +177,7 @@ static int convert_variable_location(Dwarf_Die *vr_die, Dwarf_Addr addr,
        Dwarf_Word offs = 0;
        bool ref = false;
        const char *regs;
-       int ret;
+       int ret, ret2 = 0;
 
        if (dwarf_attr(vr_die, DW_AT_external, &attr) != NULL)
                goto static_var;
@@ -187,9 +187,19 @@ static int convert_variable_location(Dwarf_Die *vr_die, Dwarf_Addr addr,
                return -EINVAL; /* Broken DIE ? */
        if (dwarf_getlocation_addr(&attr, addr, &op, &nops, 1) <= 0) {
                ret = dwarf_entrypc(sp_die, &tmp);
-               if (ret || addr != tmp ||
-                   dwarf_tag(vr_die) != DW_TAG_formal_parameter ||
-                   dwarf_highpc(sp_die, &tmp))
+               if (ret)
+                       return -ENOENT;
+
+               if (probe_conf.show_location_range &&
+                       (dwarf_tag(vr_die) == DW_TAG_variable)) {
+                       ret2 = -ERANGE;
+               } else if (addr != tmp ||
+                       dwarf_tag(vr_die) != DW_TAG_formal_parameter) {
+                       return -ENOENT;
+               }
+
+               ret = dwarf_highpc(sp_die, &tmp);
+               if (ret)
                        return -ENOENT;
                /*
                 * This is fuzzed by fentry mcount. We try to find the
@@ -210,7 +220,7 @@ found:
        if (op->atom == DW_OP_addr) {
 static_var:
                if (!tvar)
-                       return 0;
+                       return ret2;
                /* Static variables on memory (not stack), make @varname */
                ret = strlen(dwarf_diename(vr_die));
                tvar->value = zalloc(ret + 2);
@@ -220,7 +230,7 @@ static_var:
                tvar->ref = alloc_trace_arg_ref((long)offs);
                if (tvar->ref == NULL)
                        return -ENOMEM;
-               return 0;
+               return ret2;
        }
 
        /* If this is based on frame buffer, set the offset */
@@ -250,14 +260,14 @@ static_var:
        }
 
        if (!tvar)
-               return 0;
+               return ret2;
 
        regs = get_arch_regstr(regn);
        if (!regs) {
                /* This should be a bug in DWARF or this tool */
                pr_warning("Mapping for the register number %u "
                           "missing on this architecture.\n", regn);
-               return -ERANGE;
+               return -ENOTSUP;
        }
 
        tvar->value = strdup(regs);
@@ -269,7 +279,7 @@ static_var:
                if (tvar->ref == NULL)
                        return -ENOMEM;
        }
-       return 0;
+       return ret2;
 }
 
 #define BYTES_TO_BITS(nb)      ((nb) * BITS_PER_LONG / sizeof(long))
@@ -517,10 +527,12 @@ static int convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf)
 
        ret = convert_variable_location(vr_die, pf->addr, pf->fb_ops,
                                        &pf->sp_die, pf->tvar);
-       if (ret == -ENOENT || ret == -EINVAL)
-               pr_err("Failed to find the location of %s at this address.\n"
-                      " Perhaps, it has been optimized out.\n", pf->pvar->var);
-       else if (ret == -ENOTSUP)
+       if (ret == -ENOENT || ret == -EINVAL) {
+               pr_err("Failed to find the location of the '%s' variable at this address.\n"
+                      " Perhaps it has been optimized out.\n"
+                      " Use -V with the --range option to show '%s' location range.\n",
+                      pf->pvar->var, pf->pvar->var);
+       } else if (ret == -ENOTSUP)
                pr_err("Sorry, we don't support this variable location yet.\n");
        else if (ret == 0 && pf->pvar->field) {
                ret = convert_variable_fields(vr_die, pf->pvar->var,
@@ -662,9 +674,15 @@ static int call_probe_finder(Dwarf_Die *sc_die, struct probe_finder *pf)
        /* If not a real subprogram, find a real one */
        if (!die_is_func_def(sc_die)) {
                if (!die_find_realfunc(&pf->cu_die, pf->addr, &pf->sp_die)) {
-                       pr_warning("Failed to find probe point in any "
-                                  "functions.\n");
-                       return -ENOENT;
+                       if (die_find_tailfunc(&pf->cu_die, pf->addr, &pf->sp_die)) {
+                               pr_warning("Ignoring tail call from %s\n",
+                                               dwarf_diename(&pf->sp_die));
+                               return 0;
+                       } else {
+                               pr_warning("Failed to find probe point in any "
+                                          "functions.\n");
+                               return -ENOENT;
+                       }
                }
        } else
                memcpy(&pf->sp_die, sc_die, sizeof(Dwarf_Die));
@@ -719,7 +737,7 @@ static int find_best_scope_cb(Dwarf_Die *fn_die, void *data)
        }
        /* If the function name is given, that's what user expects */
        if (fsp->function) {
-               if (die_compare_name(fn_die, fsp->function)) {
+               if (die_match_name(fn_die, fsp->function)) {
                        memcpy(fsp->die_mem, fn_die, sizeof(Dwarf_Die));
                        fsp->found = true;
                        return 1;
@@ -922,13 +940,14 @@ static int probe_point_search_cb(Dwarf_Die *sp_die, void *data)
 
        /* Check tag and diename */
        if (!die_is_func_def(sp_die) ||
-           !die_compare_name(sp_die, pp->function))
+           !die_match_name(sp_die, pp->function))
                return DWARF_CB_OK;
 
        /* Check declared file */
        if (pp->file && strtailcmp(pp->file, dwarf_decl_file(sp_die)))
                return DWARF_CB_OK;
 
+       pr_debug("Matched function: %s\n", dwarf_diename(sp_die));
        pf->fname = dwarf_decl_file(sp_die);
        if (pp->line) { /* Function relative line */
                dwarf_decl_line(sp_die, &pf->lno);
@@ -945,10 +964,20 @@ static int probe_point_search_cb(Dwarf_Die *sp_die, void *data)
                        /* TODO: Check the address in this function */
                        param->retval = call_probe_finder(sp_die, pf);
                }
-       } else
+       } else if (!probe_conf.no_inlines) {
                /* Inlined function: search instances */
                param->retval = die_walk_instances(sp_die,
                                        probe_point_inline_cb, (void *)pf);
+               /* This could be a non-existed inline definition */
+               if (param->retval == -ENOENT && strisglob(pp->function))
+                       param->retval = 0;
+       }
+
+       /* We need to find other candidates */
+       if (strisglob(pp->function) && param->retval >= 0) {
+               param->retval = 0;      /* We have to clear the result */
+               return DWARF_CB_OK;
+       }
 
        return DWARF_CB_ABORT; /* Exit; no same symbol in this CU. */
 }
@@ -977,7 +1006,7 @@ static int pubname_search_cb(Dwarf *dbg, Dwarf_Global *gl, void *data)
                if (dwarf_tag(param->sp_die) != DW_TAG_subprogram)
                        return DWARF_CB_OK;
 
-               if (die_compare_name(param->sp_die, param->function)) {
+               if (die_match_name(param->sp_die, param->function)) {
                        if (!dwarf_offdie(dbg, gl->cu_offset, param->cu_die))
                                return DWARF_CB_OK;
 
@@ -1030,7 +1059,7 @@ static int debuginfo__find_probes(struct debuginfo *dbg,
                return -ENOMEM;
 
        /* Fastpath: lookup by function name from .debug_pubnames section */
-       if (pp->function) {
+       if (pp->function && !strisglob(pp->function)) {
                struct pubname_callback_param pubname_param = {
                        .function = pp->function,
                        .file     = pp->file,
@@ -1089,6 +1118,7 @@ found:
 struct local_vars_finder {
        struct probe_finder *pf;
        struct perf_probe_arg *args;
+       bool vars;
        int max_args;
        int nargs;
        int ret;
@@ -1103,7 +1133,7 @@ static int copy_variables_cb(Dwarf_Die *die_mem, void *data)
 
        tag = dwarf_tag(die_mem);
        if (tag == DW_TAG_formal_parameter ||
-           tag == DW_TAG_variable) {
+           (tag == DW_TAG_variable && vf->vars)) {
                if (convert_variable_location(die_mem, vf->pf->addr,
                                              vf->pf->fb_ops, &pf->sp_die,
                                              NULL) == 0) {
@@ -1129,26 +1159,28 @@ static int expand_probe_args(Dwarf_Die *sc_die, struct probe_finder *pf,
        Dwarf_Die die_mem;
        int i;
        int n = 0;
-       struct local_vars_finder vf = {.pf = pf, .args = args,
+       struct local_vars_finder vf = {.pf = pf, .args = args, .vars = false,
                                .max_args = MAX_PROBE_ARGS, .ret = 0};
 
        for (i = 0; i < pf->pev->nargs; i++) {
                /* var never be NULL */
-               if (strcmp(pf->pev->args[i].var, "$vars") == 0) {
-                       pr_debug("Expanding $vars into:");
-                       vf.nargs = n;
-                       /* Special local variables */
-                       die_find_child(sc_die, copy_variables_cb, (void *)&vf,
-                                      &die_mem);
-                       pr_debug(" (%d)\n", vf.nargs - n);
-                       if (vf.ret < 0)
-                               return vf.ret;
-                       n = vf.nargs;
-               } else {
+               if (strcmp(pf->pev->args[i].var, PROBE_ARG_VARS) == 0)
+                       vf.vars = true;
+               else if (strcmp(pf->pev->args[i].var, PROBE_ARG_PARAMS) != 0) {
                        /* Copy normal argument */
                        args[n] = pf->pev->args[i];
                        n++;
+                       continue;
                }
+               pr_debug("Expanding %s into:", pf->pev->args[i].var);
+               vf.nargs = n;
+               /* Special local variables */
+               die_find_child(sc_die, copy_variables_cb, (void *)&vf,
+                              &die_mem);
+               pr_debug(" (%d)\n", vf.nargs - n);
+               if (vf.ret < 0)
+                       return vf.ret;
+               n = vf.nargs;
        }
        return n;
 }
@@ -1176,6 +1208,10 @@ static int add_probe_trace_event(Dwarf_Die *sc_die, struct probe_finder *pf)
        if (ret < 0)
                return ret;
 
+       tev->point.realname = strdup(dwarf_diename(sc_die));
+       if (!tev->point.realname)
+               return -ENOMEM;
+
        pr_debug("Probe point found: %s+%lu\n", tev->point.symbol,
                 tev->point.offset);
 
@@ -1213,15 +1249,15 @@ end:
 /* Find probe_trace_events specified by perf_probe_event from debuginfo */
 int debuginfo__find_trace_events(struct debuginfo *dbg,
                                 struct perf_probe_event *pev,
-                                struct probe_trace_event **tevs, int max_tevs)
+                                struct probe_trace_event **tevs)
 {
        struct trace_event_finder tf = {
                        .pf = {.pev = pev, .callback = add_probe_trace_event},
-                       .mod = dbg->mod, .max_tevs = max_tevs};
+                       .max_tevs = probe_conf.max_probes, .mod = dbg->mod};
        int ret;
 
        /* Allocate result tevs array */
-       *tevs = zalloc(sizeof(struct probe_trace_event) * max_tevs);
+       *tevs = zalloc(sizeof(struct probe_trace_event) * tf.max_tevs);
        if (*tevs == NULL)
                return -ENOMEM;
 
@@ -1237,14 +1273,11 @@ int debuginfo__find_trace_events(struct debuginfo *dbg,
        return (ret < 0) ? ret : tf.ntevs;
 }
 
-#define MAX_VAR_LEN 64
-
 /* Collect available variables in this scope */
 static int collect_variables_cb(Dwarf_Die *die_mem, void *data)
 {
        struct available_var_finder *af = data;
        struct variable_list *vl;
-       char buf[MAX_VAR_LEN];
        int tag, ret;
 
        vl = &af->vls[af->nvls - 1];
@@ -1255,11 +1288,38 @@ static int collect_variables_cb(Dwarf_Die *die_mem, void *data)
                ret = convert_variable_location(die_mem, af->pf.addr,
                                                af->pf.fb_ops, &af->pf.sp_die,
                                                NULL);
-               if (ret == 0) {
-                       ret = die_get_varname(die_mem, buf, MAX_VAR_LEN);
-                       pr_debug2("Add new var: %s\n", buf);
-                       if (ret > 0)
-                               strlist__add(vl->vars, buf);
+               if (ret == 0 || ret == -ERANGE) {
+                       int ret2;
+                       bool externs = !af->child;
+                       struct strbuf buf;
+
+                       strbuf_init(&buf, 64);
+
+                       if (probe_conf.show_location_range) {
+                               if (!externs) {
+                                       if (ret)
+                                               strbuf_addf(&buf, "[INV]\t");
+                                       else
+                                               strbuf_addf(&buf, "[VAL]\t");
+                               } else
+                                       strbuf_addf(&buf, "[EXT]\t");
+                       }
+
+                       ret2 = die_get_varname(die_mem, &buf);
+
+                       if (!ret2 && probe_conf.show_location_range &&
+                               !externs) {
+                               strbuf_addf(&buf, "\t");
+                               ret2 = die_get_var_range(&af->pf.sp_die,
+                                                       die_mem, &buf);
+                       }
+
+                       pr_debug("Add new var: %s\n", buf.buf);
+                       if (ret2 == 0) {
+                               strlist__add(vl->vars,
+                                       strbuf_detach(&buf, NULL));
+                       }
+                       strbuf_release(&buf);
                }
        }
 
@@ -1302,9 +1362,9 @@ static int add_available_vars(Dwarf_Die *sc_die, struct probe_finder *pf)
        die_find_child(sc_die, collect_variables_cb, (void *)af, &die_mem);
 
        /* Find external variables */
-       if (!af->externs)
+       if (!probe_conf.show_ext_vars)
                goto out;
-       /* Don't need to search child DIE for externs. */
+       /* Don't need to search child DIE for external vars. */
        af->child = false;
        die_find_child(&pf->cu_die, collect_variables_cb, (void *)af, &die_mem);
 
@@ -1324,17 +1384,16 @@ out:
  */
 int debuginfo__find_available_vars_at(struct debuginfo *dbg,
                                      struct perf_probe_event *pev,
-                                     struct variable_list **vls,
-                                     int max_vls, bool externs)
+                                     struct variable_list **vls)
 {
        struct available_var_finder af = {
                        .pf = {.pev = pev, .callback = add_available_vars},
                        .mod = dbg->mod,
-                       .max_vls = max_vls, .externs = externs};
+                       .max_vls = probe_conf.max_probes};
        int ret;
 
        /* Allocate result vls array */
-       *vls = zalloc(sizeof(struct variable_list) * max_vls);
+       *vls = zalloc(sizeof(struct variable_list) * af.max_vls);
        if (*vls == NULL)
                return -ENOMEM;
 
@@ -1535,7 +1594,7 @@ static int line_range_search_cb(Dwarf_Die *sp_die, void *data)
                return DWARF_CB_OK;
 
        if (die_is_func_def(sp_die) &&
-           die_compare_name(sp_die, lr->function)) {
+           die_match_name(sp_die, lr->function)) {
                lf->fname = dwarf_decl_file(sp_die);
                dwarf_decl_line(sp_die, &lr->offset);
                pr_debug("fname: %s, lineno:%d\n", lf->fname, lr->offset);
index ebf8c8c814531ff4efaf5a7461c2ef6a7414df69..bed82716e1b44960a0ebc435d0ba1e94ed30730d 100644 (file)
@@ -10,6 +10,9 @@
 #define MAX_PROBES              128
 #define MAX_PROBE_ARGS          128
 
+#define PROBE_ARG_VARS         "$vars"
+#define PROBE_ARG_PARAMS       "$params"
+
 static inline int is_c_varname(const char *name)
 {
        /* TODO */
@@ -37,8 +40,7 @@ extern void debuginfo__delete(struct debuginfo *dbg);
 /* Find probe_trace_events specified by perf_probe_event from debuginfo */
 extern int debuginfo__find_trace_events(struct debuginfo *dbg,
                                        struct perf_probe_event *pev,
-                                       struct probe_trace_event **tevs,
-                                       int max_tevs);
+                                       struct probe_trace_event **tevs);
 
 /* Find a perf_probe_point from debuginfo */
 extern int debuginfo__find_probe_point(struct debuginfo *dbg,
@@ -52,8 +54,7 @@ extern int debuginfo__find_line_range(struct debuginfo *dbg,
 /* Find available variables */
 extern int debuginfo__find_available_vars_at(struct debuginfo *dbg,
                                             struct perf_probe_event *pev,
-                                            struct variable_list **vls,
-                                            int max_points, bool externs);
+                                            struct variable_list **vls);
 
 /* Find a src file from a DWARF tag path */
 int get_real_path(const char *raw_path, const char *comp_dir,
@@ -96,7 +97,6 @@ struct available_var_finder {
        struct variable_list    *vls;           /* Found variable lists */
        int                     nvls;           /* Number of variable lists */
        int                     max_vls;        /* Max no. of variable lists */
-       bool                    externs;        /* Find external vars too */
        bool                    child;          /* Search child scopes */
 };
 
index a126e6cc6e73ad8554e21a611b373a25599f5a9a..b234a6e3d0d4f378ff2fd4899159bd3190956a99 100644 (file)
@@ -74,3 +74,10 @@ void *pstack__pop(struct pstack *pstack)
        pstack->entries[pstack->top] = NULL;
        return ret;
 }
+
+void *pstack__peek(struct pstack *pstack)
+{
+       if (pstack->top == 0)
+               return NULL;
+       return pstack->entries[pstack->top - 1];
+}
index c3cb6584d52763f24c3074886de50c9ff741c71c..ded7f2e36624a9ff2d6ebe49c92aed2018c097dc 100644 (file)
@@ -10,5 +10,6 @@ bool pstack__empty(const struct pstack *pstack);
 void pstack__remove(struct pstack *pstack, void *key);
 void pstack__push(struct pstack *pstack, void *key);
 void *pstack__pop(struct pstack *pstack);
+void *pstack__peek(struct pstack *pstack);
 
 #endif /* _PERF_PSTACK_ */
index 4d28624a1ecaa6c6dc5d70fb2f0d43e91c56d0cf..5925fec90562fc355514489ae3d33814800f3896 100644 (file)
@@ -16,6 +16,7 @@ util/util.c
 util/xyarray.c
 util/cgroup.c
 util/rblist.c
+util/stat.c
 util/strlist.c
 util/trace-event.c
 ../../lib/rbtree.c
index 8acd0df88b5c4b75063d7ad2f83a55ef4f4f6286..d457c523a33d8bb7d00669dbd09e589eecf6de4b 100644 (file)
@@ -20,7 +20,7 @@ static int perf_do_probe_api(setup_probe_fn_t fn, int cpu, const char *str)
        if (!evlist)
                return -ENOMEM;
 
-       if (parse_events(evlist, str))
+       if (parse_events(evlist, str, NULL))
                goto out_delete;
 
        evsel = perf_evlist__first(evlist);
@@ -119,7 +119,16 @@ void perf_evlist__config(struct perf_evlist *evlist, struct record_opts *opts)
                        evsel->attr.comm_exec = 1;
        }
 
-       if (evlist->nr_entries > 1) {
+       if (opts->full_auxtrace) {
+               /*
+                * Need to be able to synthesize and parse selected events with
+                * arbitrary sample types, which requires always being able to
+                * match the id.
+                */
+               use_sample_identifier = perf_can_sample_identifier();
+               evlist__for_each(evlist, evsel)
+                       perf_evsel__set_sample_id(evsel, use_sample_identifier);
+       } else if (evlist->nr_entries > 1) {
                struct perf_evsel *first = perf_evlist__first(evlist);
 
                evlist__for_each(evlist, evsel) {
@@ -207,7 +216,7 @@ bool perf_evlist__can_select_event(struct perf_evlist *evlist, const char *str)
        if (!temp_evlist)
                return false;
 
-       err = parse_events(temp_evlist, str);
+       err = parse_events(temp_evlist, str, NULL);
        if (err)
                goto out_delete;
 
index 0c74012575ac925648c3f2eb350d7fb95bd154ce..aa482c10469d748fb2c6379ff854ba80f53b2b73 100644 (file)
 #include "cpumap.h"
 #include "perf_regs.h"
 #include "asm/bug.h"
+#include "auxtrace.h"
+#include "thread-stack.h"
 
-static int machines__deliver_event(struct machines *machines,
-                                  struct perf_evlist *evlist,
-                                  union perf_event *event,
-                                  struct perf_sample *sample,
-                                  struct perf_tool *tool, u64 file_offset);
+static int perf_session__deliver_event(struct perf_session *session,
+                                      union perf_event *event,
+                                      struct perf_sample *sample,
+                                      struct perf_tool *tool,
+                                      u64 file_offset);
 
 static int perf_session__open(struct perf_session *session)
 {
@@ -105,8 +107,8 @@ static int ordered_events__deliver_event(struct ordered_events *oe,
                return ret;
        }
 
-       return machines__deliver_event(&session->machines, session->evlist, event->event,
-                                      &sample, session->tool, event->file_offset);
+       return perf_session__deliver_event(session, event->event, &sample,
+                                          session->tool, event->file_offset);
 }
 
 struct perf_session *perf_session__new(struct perf_data_file *file,
@@ -119,6 +121,7 @@ struct perf_session *perf_session__new(struct perf_data_file *file,
 
        session->repipe = repipe;
        session->tool   = tool;
+       INIT_LIST_HEAD(&session->auxtrace_index);
        machines__init(&session->machines);
        ordered_events__init(&session->ordered_events, ordered_events__deliver_event);
 
@@ -185,6 +188,8 @@ static void perf_session_env__delete(struct perf_session_env *env)
 
 void perf_session__delete(struct perf_session *session)
 {
+       auxtrace__free(session);
+       auxtrace_index__free(&session->auxtrace_index);
        perf_session__destroy_kernel_maps(session);
        perf_session__delete_threads(session);
        perf_session_env__delete(&session->header.env);
@@ -262,6 +267,49 @@ static int process_id_index_stub(struct perf_tool *tool __maybe_unused,
        return 0;
 }
 
+static int process_event_auxtrace_info_stub(struct perf_tool *tool __maybe_unused,
+                               union perf_event *event __maybe_unused,
+                               struct perf_session *session __maybe_unused)
+{
+       dump_printf(": unhandled!\n");
+       return 0;
+}
+
+static int skipn(int fd, off_t n)
+{
+       char buf[4096];
+       ssize_t ret;
+
+       while (n > 0) {
+               ret = read(fd, buf, min(n, (off_t)sizeof(buf)));
+               if (ret <= 0)
+                       return ret;
+               n -= ret;
+       }
+
+       return 0;
+}
+
+static s64 process_event_auxtrace_stub(struct perf_tool *tool __maybe_unused,
+                                      union perf_event *event,
+                                      struct perf_session *session
+                                      __maybe_unused)
+{
+       dump_printf(": unhandled!\n");
+       if (perf_data_file__is_pipe(session->file))
+               skipn(perf_data_file__fd(session->file), event->auxtrace.size);
+       return event->auxtrace.size;
+}
+
+static
+int process_event_auxtrace_error_stub(struct perf_tool *tool __maybe_unused,
+                                     union perf_event *event __maybe_unused,
+                                     struct perf_session *session __maybe_unused)
+{
+       dump_printf(": unhandled!\n");
+       return 0;
+}
+
 void perf_tool__fill_defaults(struct perf_tool *tool)
 {
        if (tool->sample == NULL)
@@ -278,6 +326,12 @@ void perf_tool__fill_defaults(struct perf_tool *tool)
                tool->exit = process_event_stub;
        if (tool->lost == NULL)
                tool->lost = perf_event__process_lost;
+       if (tool->lost_samples == NULL)
+               tool->lost_samples = perf_event__process_lost_samples;
+       if (tool->aux == NULL)
+               tool->aux = perf_event__process_aux;
+       if (tool->itrace_start == NULL)
+               tool->itrace_start = perf_event__process_itrace_start;
        if (tool->read == NULL)
                tool->read = process_event_sample_stub;
        if (tool->throttle == NULL)
@@ -298,6 +352,12 @@ void perf_tool__fill_defaults(struct perf_tool *tool)
        }
        if (tool->id_index == NULL)
                tool->id_index = process_id_index_stub;
+       if (tool->auxtrace_info == NULL)
+               tool->auxtrace_info = process_event_auxtrace_info_stub;
+       if (tool->auxtrace == NULL)
+               tool->auxtrace = process_event_auxtrace_stub;
+       if (tool->auxtrace_error == NULL)
+               tool->auxtrace_error = process_event_auxtrace_error_stub;
 }
 
 static void swap_sample_id_all(union perf_event *event, void *data)
@@ -390,6 +450,26 @@ static void perf_event__read_swap(union perf_event *event, bool sample_id_all)
                swap_sample_id_all(event, &event->read + 1);
 }
 
+static void perf_event__aux_swap(union perf_event *event, bool sample_id_all)
+{
+       event->aux.aux_offset = bswap_64(event->aux.aux_offset);
+       event->aux.aux_size   = bswap_64(event->aux.aux_size);
+       event->aux.flags      = bswap_64(event->aux.flags);
+
+       if (sample_id_all)
+               swap_sample_id_all(event, &event->aux + 1);
+}
+
+static void perf_event__itrace_start_swap(union perf_event *event,
+                                         bool sample_id_all)
+{
+       event->itrace_start.pid  = bswap_32(event->itrace_start.pid);
+       event->itrace_start.tid  = bswap_32(event->itrace_start.tid);
+
+       if (sample_id_all)
+               swap_sample_id_all(event, &event->itrace_start + 1);
+}
+
 static void perf_event__throttle_swap(union perf_event *event,
                                      bool sample_id_all)
 {
@@ -438,19 +518,42 @@ void perf_event__attr_swap(struct perf_event_attr *attr)
 {
        attr->type              = bswap_32(attr->type);
        attr->size              = bswap_32(attr->size);
-       attr->config            = bswap_64(attr->config);
-       attr->sample_period     = bswap_64(attr->sample_period);
-       attr->sample_type       = bswap_64(attr->sample_type);
-       attr->read_format       = bswap_64(attr->read_format);
-       attr->wakeup_events     = bswap_32(attr->wakeup_events);
-       attr->bp_type           = bswap_32(attr->bp_type);
-       attr->bp_addr           = bswap_64(attr->bp_addr);
-       attr->bp_len            = bswap_64(attr->bp_len);
-       attr->branch_sample_type = bswap_64(attr->branch_sample_type);
-       attr->sample_regs_user   = bswap_64(attr->sample_regs_user);
-       attr->sample_stack_user  = bswap_32(attr->sample_stack_user);
 
-       swap_bitfield((u8 *) (&attr->read_format + 1), sizeof(u64));
+#define bswap_safe(f, n)                                       \
+       (attr->size > (offsetof(struct perf_event_attr, f) +    \
+                      sizeof(attr->f) * (n)))
+#define bswap_field(f, sz)                     \
+do {                                           \
+       if (bswap_safe(f, 0))                   \
+               attr->f = bswap_##sz(attr->f);  \
+} while(0)
+#define bswap_field_32(f) bswap_field(f, 32)
+#define bswap_field_64(f) bswap_field(f, 64)
+
+       bswap_field_64(config);
+       bswap_field_64(sample_period);
+       bswap_field_64(sample_type);
+       bswap_field_64(read_format);
+       bswap_field_32(wakeup_events);
+       bswap_field_32(bp_type);
+       bswap_field_64(bp_addr);
+       bswap_field_64(bp_len);
+       bswap_field_64(branch_sample_type);
+       bswap_field_64(sample_regs_user);
+       bswap_field_32(sample_stack_user);
+       bswap_field_32(aux_watermark);
+
+       /*
+        * After read_format are bitfields. Check read_format because
+        * we are unable to use offsetof on bitfield.
+        */
+       if (bswap_safe(read_format, 1))
+               swap_bitfield((u8 *) (&attr->read_format + 1),
+                             sizeof(u64));
+#undef bswap_field_64
+#undef bswap_field_32
+#undef bswap_field
+#undef bswap_safe
 }
 
 static void perf_event__hdr_attr_swap(union perf_event *event,
@@ -478,6 +581,40 @@ static void perf_event__tracing_data_swap(union perf_event *event,
        event->tracing_data.size = bswap_32(event->tracing_data.size);
 }
 
+static void perf_event__auxtrace_info_swap(union perf_event *event,
+                                          bool sample_id_all __maybe_unused)
+{
+       size_t size;
+
+       event->auxtrace_info.type = bswap_32(event->auxtrace_info.type);
+
+       size = event->header.size;
+       size -= (void *)&event->auxtrace_info.priv - (void *)event;
+       mem_bswap_64(event->auxtrace_info.priv, size);
+}
+
+static void perf_event__auxtrace_swap(union perf_event *event,
+                                     bool sample_id_all __maybe_unused)
+{
+       event->auxtrace.size      = bswap_64(event->auxtrace.size);
+       event->auxtrace.offset    = bswap_64(event->auxtrace.offset);
+       event->auxtrace.reference = bswap_64(event->auxtrace.reference);
+       event->auxtrace.idx       = bswap_32(event->auxtrace.idx);
+       event->auxtrace.tid       = bswap_32(event->auxtrace.tid);
+       event->auxtrace.cpu       = bswap_32(event->auxtrace.cpu);
+}
+
+static void perf_event__auxtrace_error_swap(union perf_event *event,
+                                           bool sample_id_all __maybe_unused)
+{
+       event->auxtrace_error.type = bswap_32(event->auxtrace_error.type);
+       event->auxtrace_error.code = bswap_32(event->auxtrace_error.code);
+       event->auxtrace_error.cpu  = bswap_32(event->auxtrace_error.cpu);
+       event->auxtrace_error.pid  = bswap_32(event->auxtrace_error.pid);
+       event->auxtrace_error.tid  = bswap_32(event->auxtrace_error.tid);
+       event->auxtrace_error.ip   = bswap_64(event->auxtrace_error.ip);
+}
+
 typedef void (*perf_event__swap_op)(union perf_event *event,
                                    bool sample_id_all);
 
@@ -492,11 +629,17 @@ static perf_event__swap_op perf_event__swap_ops[] = {
        [PERF_RECORD_THROTTLE]            = perf_event__throttle_swap,
        [PERF_RECORD_UNTHROTTLE]          = perf_event__throttle_swap,
        [PERF_RECORD_SAMPLE]              = perf_event__all64_swap,
+       [PERF_RECORD_AUX]                 = perf_event__aux_swap,
+       [PERF_RECORD_ITRACE_START]        = perf_event__itrace_start_swap,
+       [PERF_RECORD_LOST_SAMPLES]        = perf_event__all64_swap,
        [PERF_RECORD_HEADER_ATTR]         = perf_event__hdr_attr_swap,
        [PERF_RECORD_HEADER_EVENT_TYPE]   = perf_event__event_type_swap,
        [PERF_RECORD_HEADER_TRACING_DATA] = perf_event__tracing_data_swap,
        [PERF_RECORD_HEADER_BUILD_ID]     = NULL,
        [PERF_RECORD_ID_INDEX]            = perf_event__all64_swap,
+       [PERF_RECORD_AUXTRACE_INFO]       = perf_event__auxtrace_info_swap,
+       [PERF_RECORD_AUXTRACE]            = perf_event__auxtrace_swap,
+       [PERF_RECORD_AUXTRACE_ERROR]      = perf_event__auxtrace_error_swap,
        [PERF_RECORD_HEADER_MAX]          = NULL,
 };
 
@@ -921,6 +1064,8 @@ static int machines__deliver_event(struct machines *machines,
        case PERF_RECORD_MMAP:
                return tool->mmap(tool, event, sample, machine);
        case PERF_RECORD_MMAP2:
+               if (event->header.misc & PERF_RECORD_MISC_PROC_MAP_PARSE_TIMEOUT)
+                       ++evlist->stats.nr_proc_map_timeout;
                return tool->mmap2(tool, event, sample, machine);
        case PERF_RECORD_COMM:
                return tool->comm(tool, event, sample, machine);
@@ -932,18 +1077,44 @@ static int machines__deliver_event(struct machines *machines,
                if (tool->lost == perf_event__process_lost)
                        evlist->stats.total_lost += event->lost.lost;
                return tool->lost(tool, event, sample, machine);
+       case PERF_RECORD_LOST_SAMPLES:
+               if (tool->lost_samples == perf_event__process_lost_samples)
+                       evlist->stats.total_lost_samples += event->lost_samples.lost;
+               return tool->lost_samples(tool, event, sample, machine);
        case PERF_RECORD_READ:
                return tool->read(tool, event, sample, evsel, machine);
        case PERF_RECORD_THROTTLE:
                return tool->throttle(tool, event, sample, machine);
        case PERF_RECORD_UNTHROTTLE:
                return tool->unthrottle(tool, event, sample, machine);
+       case PERF_RECORD_AUX:
+               return tool->aux(tool, event, sample, machine);
+       case PERF_RECORD_ITRACE_START:
+               return tool->itrace_start(tool, event, sample, machine);
        default:
                ++evlist->stats.nr_unknown_events;
                return -1;
        }
 }
 
+static int perf_session__deliver_event(struct perf_session *session,
+                                      union perf_event *event,
+                                      struct perf_sample *sample,
+                                      struct perf_tool *tool,
+                                      u64 file_offset)
+{
+       int ret;
+
+       ret = auxtrace__process_event(session, event, sample, tool);
+       if (ret < 0)
+               return ret;
+       if (ret > 0)
+               return 0;
+
+       return machines__deliver_event(&session->machines, session->evlist,
+                                      event, sample, tool, file_offset);
+}
+
 static s64 perf_session__process_user_event(struct perf_session *session,
                                            union perf_event *event,
                                            u64 file_offset)
@@ -980,6 +1151,15 @@ static s64 perf_session__process_user_event(struct perf_session *session,
                return tool->finished_round(tool, event, oe);
        case PERF_RECORD_ID_INDEX:
                return tool->id_index(tool, event, session);
+       case PERF_RECORD_AUXTRACE_INFO:
+               return tool->auxtrace_info(tool, event, session);
+       case PERF_RECORD_AUXTRACE:
+               /* setup for reading amidst mmap */
+               lseek(fd, file_offset + event->header.size, SEEK_SET);
+               return tool->auxtrace(tool, event, session);
+       case PERF_RECORD_AUXTRACE_ERROR:
+               perf_session__auxtrace_error_inc(session, event);
+               return tool->auxtrace_error(tool, event, session);
        default:
                return -EINVAL;
        }
@@ -1034,7 +1214,7 @@ int perf_session__peek_event(struct perf_session *session, off_t file_offset,
                return -1;
 
        if (lseek(fd, file_offset, SEEK_SET) == (off_t)-1 ||
-           readn(fd, &buf, hdr_sz) != (ssize_t)hdr_sz)
+           readn(fd, buf, hdr_sz) != (ssize_t)hdr_sz)
                return -1;
 
        event = (union perf_event *)buf;
@@ -1042,12 +1222,12 @@ int perf_session__peek_event(struct perf_session *session, off_t file_offset,
        if (session->header.needs_swap)
                perf_event_header__bswap(&event->header);
 
-       if (event->header.size < hdr_sz)
+       if (event->header.size < hdr_sz || event->header.size > buf_sz)
                return -1;
 
        rest = event->header.size - hdr_sz;
 
-       if (readn(fd, &buf, rest) != (ssize_t)rest)
+       if (readn(fd, buf, rest) != (ssize_t)rest)
                return -1;
 
        if (session->header.needs_swap)
@@ -1096,8 +1276,8 @@ static s64 perf_session__process_event(struct perf_session *session,
                        return ret;
        }
 
-       return machines__deliver_event(&session->machines, evlist, event,
-                                      &sample, tool, file_offset);
+       return perf_session__deliver_event(session, event, &sample, tool,
+                                          file_offset);
 }
 
 void perf_event_header__bswap(struct perf_event_header *hdr)
@@ -1138,6 +1318,18 @@ static void perf_session__warn_about_errors(const struct perf_session *session)
                            stats->nr_events[PERF_RECORD_LOST]);
        }
 
+       if (session->tool->lost_samples == perf_event__process_lost_samples) {
+               double drop_rate;
+
+               drop_rate = (double)stats->total_lost_samples /
+                           (double) (stats->nr_events[PERF_RECORD_SAMPLE] + stats->total_lost_samples);
+               if (drop_rate > 0.05) {
+                       ui__warning("Processed %" PRIu64 " samples and lost %3.2f%% samples!\n\n",
+                                   stats->nr_events[PERF_RECORD_SAMPLE] + stats->total_lost_samples,
+                                   drop_rate * 100.0);
+               }
+       }
+
        if (stats->nr_unknown_events != 0) {
                ui__warning("Found %u unknown events!\n\n"
                            "Is this an older tool processing a perf.data "
@@ -1168,6 +1360,32 @@ static void perf_session__warn_about_errors(const struct perf_session *session)
 
        if (oe->nr_unordered_events != 0)
                ui__warning("%u out of order events recorded.\n", oe->nr_unordered_events);
+
+       events_stats__auxtrace_error_warn(stats);
+
+       if (stats->nr_proc_map_timeout != 0) {
+               ui__warning("%d map information files for pre-existing threads were\n"
+                           "not processed, if there are samples for addresses they\n"
+                           "will not be resolved, you may find out which are these\n"
+                           "threads by running with -v and redirecting the output\n"
+                           "to a file.\n"
+                           "The time limit to process proc map is too short?\n"
+                           "Increase it by --proc-map-timeout\n",
+                           stats->nr_proc_map_timeout);
+       }
+}
+
+static int perf_session__flush_thread_stack(struct thread *thread,
+                                           void *p __maybe_unused)
+{
+       return thread_stack__flush(thread);
+}
+
+static int perf_session__flush_thread_stacks(struct perf_session *session)
+{
+       return machines__for_each_thread(&session->machines,
+                                        perf_session__flush_thread_stack,
+                                        NULL);
 }
 
 volatile int session_done;
@@ -1256,10 +1474,17 @@ more:
 done:
        /* do the final flush for ordered samples */
        err = ordered_events__flush(oe, OE_FLUSH__FINAL);
+       if (err)
+               goto out_err;
+       err = auxtrace__flush_events(session, tool);
+       if (err)
+               goto out_err;
+       err = perf_session__flush_thread_stacks(session);
 out_err:
        free(buf);
        perf_session__warn_about_errors(session);
        ordered_events__free(&session->ordered_events);
+       auxtrace__free_events(session);
        return err;
 }
 
@@ -1402,10 +1627,17 @@ more:
 out:
        /* do the final flush for ordered samples */
        err = ordered_events__flush(oe, OE_FLUSH__FINAL);
+       if (err)
+               goto out_err;
+       err = auxtrace__flush_events(session, tool);
+       if (err)
+               goto out_err;
+       err = perf_session__flush_thread_stacks(session);
 out_err:
        ui_progress__finish();
        perf_session__warn_about_errors(session);
        ordered_events__free(&session->ordered_events);
+       auxtrace__free_events(session);
        session->one_mmap = false;
        return err;
 }
@@ -1488,7 +1720,13 @@ size_t perf_session__fprintf_dsos_buildid(struct perf_session *session, FILE *fp
 
 size_t perf_session__fprintf_nr_events(struct perf_session *session, FILE *fp)
 {
-       size_t ret = fprintf(fp, "Aggregated stats:\n");
+       size_t ret;
+       const char *msg = "";
+
+       if (perf_header__has_feat(&session->header, HEADER_AUXTRACE))
+               msg = " (excludes AUX area (e.g. instruction trace) decoded / synthesized events)";
+
+       ret = fprintf(fp, "Aggregated stats:%s\n", msg);
 
        ret += events_stats__fprintf(&session->evlist->stats, fp);
        return ret;
index d5fa7b7916ef40dd5216e533a5ce04bfdfc8a7d2..b44afc75d1cc51feb05f943973d0813e8556122c 100644 (file)
 struct ip_callchain;
 struct thread;
 
+struct auxtrace;
+struct itrace_synth_opts;
+
 struct perf_session {
        struct perf_header      header;
        struct machines         machines;
        struct perf_evlist      *evlist;
+       struct auxtrace         *auxtrace;
+       struct itrace_synth_opts *itrace_synth_opts;
+       struct list_head        auxtrace_index;
        struct trace_event      tevent;
        bool                    repipe;
        bool                    one_mmap;
index 4593f36ecc4c651bef30112b008979b336894e0c..4c65a143a34c96747ab7c6d39284f264f0f8d41e 100644 (file)
@@ -89,14 +89,14 @@ static int64_t
 sort__comm_cmp(struct hist_entry *left, struct hist_entry *right)
 {
        /* Compare the addr that should be unique among comm */
-       return comm__str(right->comm) - comm__str(left->comm);
+       return strcmp(comm__str(right->comm), comm__str(left->comm));
 }
 
 static int64_t
 sort__comm_collapse(struct hist_entry *left, struct hist_entry *right)
 {
        /* Compare the addr that should be unique among comm */
-       return comm__str(right->comm) - comm__str(left->comm);
+       return strcmp(comm__str(right->comm), comm__str(left->comm));
 }
 
 static int64_t
@@ -182,18 +182,16 @@ static int64_t _sort__addr_cmp(u64 left_ip, u64 right_ip)
 
 static int64_t _sort__sym_cmp(struct symbol *sym_l, struct symbol *sym_r)
 {
-       u64 ip_l, ip_r;
-
        if (!sym_l || !sym_r)
                return cmp_null(sym_l, sym_r);
 
        if (sym_l == sym_r)
                return 0;
 
-       ip_l = sym_l->start;
-       ip_r = sym_r->start;
+       if (sym_l->start != sym_r->start)
+               return (int64_t)(sym_r->start - sym_l->start);
 
-       return (int64_t)(ip_r - ip_l);
+       return (int64_t)(sym_r->end - sym_l->end);
 }
 
 static int64_t
index 846036a921dc9152623fd5e70671b5f196e4b3de..e97cd476d336f2a9cad2a1eeb9daba34d08ed3a4 100644 (file)
@@ -58,15 +58,16 @@ struct he_stat {
 
 struct hist_entry_diff {
        bool    computed;
+       union {
+               /* PERF_HPP__DELTA */
+               double  period_ratio_delta;
 
-       /* PERF_HPP__DELTA */
-       double  period_ratio_delta;
-
-       /* PERF_HPP__RATIO */
-       double  period_ratio;
+               /* PERF_HPP__RATIO */
+               double  period_ratio;
 
-       /* HISTC_WEIGHTED_DIFF */
-       s64     wdiff;
+               /* HISTC_WEIGHTED_DIFF */
+               s64     wdiff;
+       };
 };
 
 /**
@@ -92,21 +93,28 @@ struct hist_entry {
        s32                     cpu;
        u8                      cpumode;
 
-       struct hist_entry_diff  diff;
-
        /* We are added by hists__add_dummy_entry. */
        bool                    dummy;
 
-       /* XXX These two should move to some tree widget lib */
-       u16                     row_offset;
-       u16                     nr_rows;
-
-       bool                    init_have_children;
        char                    level;
        u8                      filtered;
+       union {
+               /*
+                * Since perf diff only supports the stdio output, TUI
+                * fields are only accessed from perf report (or perf
+                * top).  So make it an union to reduce memory usage.
+                */
+               struct hist_entry_diff  diff;
+               struct /* for TUI */ {
+                       u16     row_offset;
+                       u16     nr_rows;
+                       bool    init_have_children;
+                       bool    unfolded;
+                       bool    has_children;
+               };
+       };
        char                    *srcline;
        struct symbol           *parent;
-       unsigned long           position;
        struct rb_root          sorted_chain;
        struct branch_info      *branch_info;
        struct hists            *hists;
diff --git a/tools/perf/util/stat-shadow.c b/tools/perf/util/stat-shadow.c
new file mode 100644 (file)
index 0000000..53e8bb7
--- /dev/null
@@ -0,0 +1,434 @@
+#include <stdio.h>
+#include "evsel.h"
+#include "stat.h"
+#include "color.h"
+
+enum {
+       CTX_BIT_USER    = 1 << 0,
+       CTX_BIT_KERNEL  = 1 << 1,
+       CTX_BIT_HV      = 1 << 2,
+       CTX_BIT_HOST    = 1 << 3,
+       CTX_BIT_IDLE    = 1 << 4,
+       CTX_BIT_MAX     = 1 << 5,
+};
+
+#define NUM_CTX CTX_BIT_MAX
+
+static struct stats runtime_nsecs_stats[MAX_NR_CPUS];
+static struct stats runtime_cycles_stats[NUM_CTX][MAX_NR_CPUS];
+static struct stats runtime_stalled_cycles_front_stats[NUM_CTX][MAX_NR_CPUS];
+static struct stats runtime_stalled_cycles_back_stats[NUM_CTX][MAX_NR_CPUS];
+static struct stats runtime_branches_stats[NUM_CTX][MAX_NR_CPUS];
+static struct stats runtime_cacherefs_stats[NUM_CTX][MAX_NR_CPUS];
+static struct stats runtime_l1_dcache_stats[NUM_CTX][MAX_NR_CPUS];
+static struct stats runtime_l1_icache_stats[NUM_CTX][MAX_NR_CPUS];
+static struct stats runtime_ll_cache_stats[NUM_CTX][MAX_NR_CPUS];
+static struct stats runtime_itlb_cache_stats[NUM_CTX][MAX_NR_CPUS];
+static struct stats runtime_dtlb_cache_stats[NUM_CTX][MAX_NR_CPUS];
+static struct stats runtime_cycles_in_tx_stats[NUM_CTX][MAX_NR_CPUS];
+static struct stats runtime_transaction_stats[NUM_CTX][MAX_NR_CPUS];
+static struct stats runtime_elision_stats[NUM_CTX][MAX_NR_CPUS];
+
+struct stats walltime_nsecs_stats;
+
+static int evsel_context(struct perf_evsel *evsel)
+{
+       int ctx = 0;
+
+       if (evsel->attr.exclude_kernel)
+               ctx |= CTX_BIT_KERNEL;
+       if (evsel->attr.exclude_user)
+               ctx |= CTX_BIT_USER;
+       if (evsel->attr.exclude_hv)
+               ctx |= CTX_BIT_HV;
+       if (evsel->attr.exclude_host)
+               ctx |= CTX_BIT_HOST;
+       if (evsel->attr.exclude_idle)
+               ctx |= CTX_BIT_IDLE;
+
+       return ctx;
+}
+
+void perf_stat__reset_shadow_stats(void)
+{
+       memset(runtime_nsecs_stats, 0, sizeof(runtime_nsecs_stats));
+       memset(runtime_cycles_stats, 0, sizeof(runtime_cycles_stats));
+       memset(runtime_stalled_cycles_front_stats, 0, sizeof(runtime_stalled_cycles_front_stats));
+       memset(runtime_stalled_cycles_back_stats, 0, sizeof(runtime_stalled_cycles_back_stats));
+       memset(runtime_branches_stats, 0, sizeof(runtime_branches_stats));
+       memset(runtime_cacherefs_stats, 0, sizeof(runtime_cacherefs_stats));
+       memset(runtime_l1_dcache_stats, 0, sizeof(runtime_l1_dcache_stats));
+       memset(runtime_l1_icache_stats, 0, sizeof(runtime_l1_icache_stats));
+       memset(runtime_ll_cache_stats, 0, sizeof(runtime_ll_cache_stats));
+       memset(runtime_itlb_cache_stats, 0, sizeof(runtime_itlb_cache_stats));
+       memset(runtime_dtlb_cache_stats, 0, sizeof(runtime_dtlb_cache_stats));
+       memset(runtime_cycles_in_tx_stats, 0,
+                       sizeof(runtime_cycles_in_tx_stats));
+       memset(runtime_transaction_stats, 0,
+               sizeof(runtime_transaction_stats));
+       memset(runtime_elision_stats, 0, sizeof(runtime_elision_stats));
+       memset(&walltime_nsecs_stats, 0, sizeof(walltime_nsecs_stats));
+}
+
+/*
+ * Update various tracking values we maintain to print
+ * more semantic information such as miss/hit ratios,
+ * instruction rates, etc:
+ */
+void perf_stat__update_shadow_stats(struct perf_evsel *counter, u64 *count,
+                                   int cpu)
+{
+       int ctx = evsel_context(counter);
+
+       if (perf_evsel__match(counter, SOFTWARE, SW_TASK_CLOCK))
+               update_stats(&runtime_nsecs_stats[cpu], count[0]);
+       else if (perf_evsel__match(counter, HARDWARE, HW_CPU_CYCLES))
+               update_stats(&runtime_cycles_stats[ctx][cpu], count[0]);
+       else if (perf_stat_evsel__is(counter, CYCLES_IN_TX))
+               update_stats(&runtime_transaction_stats[ctx][cpu], count[0]);
+       else if (perf_stat_evsel__is(counter, TRANSACTION_START))
+               update_stats(&runtime_transaction_stats[ctx][cpu], count[0]);
+       else if (perf_stat_evsel__is(counter, ELISION_START))
+               update_stats(&runtime_elision_stats[ctx][cpu], count[0]);
+       else if (perf_evsel__match(counter, HARDWARE, HW_STALLED_CYCLES_FRONTEND))
+               update_stats(&runtime_stalled_cycles_front_stats[ctx][cpu], count[0]);
+       else if (perf_evsel__match(counter, HARDWARE, HW_STALLED_CYCLES_BACKEND))
+               update_stats(&runtime_stalled_cycles_back_stats[ctx][cpu], count[0]);
+       else if (perf_evsel__match(counter, HARDWARE, HW_BRANCH_INSTRUCTIONS))
+               update_stats(&runtime_branches_stats[ctx][cpu], count[0]);
+       else if (perf_evsel__match(counter, HARDWARE, HW_CACHE_REFERENCES))
+               update_stats(&runtime_cacherefs_stats[ctx][cpu], count[0]);
+       else if (perf_evsel__match(counter, HW_CACHE, HW_CACHE_L1D))
+               update_stats(&runtime_l1_dcache_stats[ctx][cpu], count[0]);
+       else if (perf_evsel__match(counter, HW_CACHE, HW_CACHE_L1I))
+               update_stats(&runtime_ll_cache_stats[ctx][cpu], count[0]);
+       else if (perf_evsel__match(counter, HW_CACHE, HW_CACHE_LL))
+               update_stats(&runtime_ll_cache_stats[ctx][cpu], count[0]);
+       else if (perf_evsel__match(counter, HW_CACHE, HW_CACHE_DTLB))
+               update_stats(&runtime_dtlb_cache_stats[ctx][cpu], count[0]);
+       else if (perf_evsel__match(counter, HW_CACHE, HW_CACHE_ITLB))
+               update_stats(&runtime_itlb_cache_stats[ctx][cpu], count[0]);
+}
+
+/* used for get_ratio_color() */
+enum grc_type {
+       GRC_STALLED_CYCLES_FE,
+       GRC_STALLED_CYCLES_BE,
+       GRC_CACHE_MISSES,
+       GRC_MAX_NR
+};
+
+static const char *get_ratio_color(enum grc_type type, double ratio)
+{
+       static const double grc_table[GRC_MAX_NR][3] = {
+               [GRC_STALLED_CYCLES_FE] = { 50.0, 30.0, 10.0 },
+               [GRC_STALLED_CYCLES_BE] = { 75.0, 50.0, 20.0 },
+               [GRC_CACHE_MISSES]      = { 20.0, 10.0, 5.0 },
+       };
+       const char *color = PERF_COLOR_NORMAL;
+
+       if (ratio > grc_table[type][0])
+               color = PERF_COLOR_RED;
+       else if (ratio > grc_table[type][1])
+               color = PERF_COLOR_MAGENTA;
+       else if (ratio > grc_table[type][2])
+               color = PERF_COLOR_YELLOW;
+
+       return color;
+}
+
+static void print_stalled_cycles_frontend(FILE *out, int cpu,
+                                         struct perf_evsel *evsel
+                                         __maybe_unused, double avg)
+{
+       double total, ratio = 0.0;
+       const char *color;
+       int ctx = evsel_context(evsel);
+
+       total = avg_stats(&runtime_cycles_stats[ctx][cpu]);
+
+       if (total)
+               ratio = avg / total * 100.0;
+
+       color = get_ratio_color(GRC_STALLED_CYCLES_FE, ratio);
+
+       fprintf(out, " #  ");
+       color_fprintf(out, color, "%6.2f%%", ratio);
+       fprintf(out, " frontend cycles idle   ");
+}
+
+static void print_stalled_cycles_backend(FILE *out, int cpu,
+                                        struct perf_evsel *evsel
+                                        __maybe_unused, double avg)
+{
+       double total, ratio = 0.0;
+       const char *color;
+       int ctx = evsel_context(evsel);
+
+       total = avg_stats(&runtime_cycles_stats[ctx][cpu]);
+
+       if (total)
+               ratio = avg / total * 100.0;
+
+       color = get_ratio_color(GRC_STALLED_CYCLES_BE, ratio);
+
+       fprintf(out, " #  ");
+       color_fprintf(out, color, "%6.2f%%", ratio);
+       fprintf(out, " backend  cycles idle   ");
+}
+
+static void print_branch_misses(FILE *out, int cpu,
+                               struct perf_evsel *evsel __maybe_unused,
+                               double avg)
+{
+       double total, ratio = 0.0;
+       const char *color;
+       int ctx = evsel_context(evsel);
+
+       total = avg_stats(&runtime_branches_stats[ctx][cpu]);
+
+       if (total)
+               ratio = avg / total * 100.0;
+
+       color = get_ratio_color(GRC_CACHE_MISSES, ratio);
+
+       fprintf(out, " #  ");
+       color_fprintf(out, color, "%6.2f%%", ratio);
+       fprintf(out, " of all branches        ");
+}
+
+static void print_l1_dcache_misses(FILE *out, int cpu,
+                                  struct perf_evsel *evsel __maybe_unused,
+                                  double avg)
+{
+       double total, ratio = 0.0;
+       const char *color;
+       int ctx = evsel_context(evsel);
+
+       total = avg_stats(&runtime_l1_dcache_stats[ctx][cpu]);
+
+       if (total)
+               ratio = avg / total * 100.0;
+
+       color = get_ratio_color(GRC_CACHE_MISSES, ratio);
+
+       fprintf(out, " #  ");
+       color_fprintf(out, color, "%6.2f%%", ratio);
+       fprintf(out, " of all L1-dcache hits  ");
+}
+
+static void print_l1_icache_misses(FILE *out, int cpu,
+                                  struct perf_evsel *evsel __maybe_unused,
+                                  double avg)
+{
+       double total, ratio = 0.0;
+       const char *color;
+       int ctx = evsel_context(evsel);
+
+       total = avg_stats(&runtime_l1_icache_stats[ctx][cpu]);
+
+       if (total)
+               ratio = avg / total * 100.0;
+
+       color = get_ratio_color(GRC_CACHE_MISSES, ratio);
+
+       fprintf(out, " #  ");
+       color_fprintf(out, color, "%6.2f%%", ratio);
+       fprintf(out, " of all L1-icache hits  ");
+}
+
+static void print_dtlb_cache_misses(FILE *out, int cpu,
+                                   struct perf_evsel *evsel __maybe_unused,
+                                   double avg)
+{
+       double total, ratio = 0.0;
+       const char *color;
+       int ctx = evsel_context(evsel);
+
+       total = avg_stats(&runtime_dtlb_cache_stats[ctx][cpu]);
+
+       if (total)
+               ratio = avg / total * 100.0;
+
+       color = get_ratio_color(GRC_CACHE_MISSES, ratio);
+
+       fprintf(out, " #  ");
+       color_fprintf(out, color, "%6.2f%%", ratio);
+       fprintf(out, " of all dTLB cache hits ");
+}
+
+static void print_itlb_cache_misses(FILE *out, int cpu,
+                                   struct perf_evsel *evsel __maybe_unused,
+                                   double avg)
+{
+       double total, ratio = 0.0;
+       const char *color;
+       int ctx = evsel_context(evsel);
+
+       total = avg_stats(&runtime_itlb_cache_stats[ctx][cpu]);
+
+       if (total)
+               ratio = avg / total * 100.0;
+
+       color = get_ratio_color(GRC_CACHE_MISSES, ratio);
+
+       fprintf(out, " #  ");
+       color_fprintf(out, color, "%6.2f%%", ratio);
+       fprintf(out, " of all iTLB cache hits ");
+}
+
+static void print_ll_cache_misses(FILE *out, int cpu,
+                                 struct perf_evsel *evsel __maybe_unused,
+                                 double avg)
+{
+       double total, ratio = 0.0;
+       const char *color;
+       int ctx = evsel_context(evsel);
+
+       total = avg_stats(&runtime_ll_cache_stats[ctx][cpu]);
+
+       if (total)
+               ratio = avg / total * 100.0;
+
+       color = get_ratio_color(GRC_CACHE_MISSES, ratio);
+
+       fprintf(out, " #  ");
+       color_fprintf(out, color, "%6.2f%%", ratio);
+       fprintf(out, " of all LL-cache hits   ");
+}
+
+void perf_stat__print_shadow_stats(FILE *out, struct perf_evsel *evsel,
+                                  double avg, int cpu, enum aggr_mode aggr)
+{
+       double total, ratio = 0.0, total2;
+       int ctx = evsel_context(evsel);
+
+       if (perf_evsel__match(evsel, HARDWARE, HW_INSTRUCTIONS)) {
+               total = avg_stats(&runtime_cycles_stats[ctx][cpu]);
+               if (total) {
+                       ratio = avg / total;
+                       fprintf(out, " #   %5.2f  insns per cycle        ", ratio);
+               } else {
+                       fprintf(out, "                                   ");
+               }
+               total = avg_stats(&runtime_stalled_cycles_front_stats[ctx][cpu]);
+               total = max(total, avg_stats(&runtime_stalled_cycles_back_stats[ctx][cpu]));
+
+               if (total && avg) {
+                       ratio = total / avg;
+                       fprintf(out, "\n");
+                       if (aggr == AGGR_NONE)
+                               fprintf(out, "        ");
+                       fprintf(out, "                                                  #   %5.2f  stalled cycles per insn", ratio);
+               }
+
+       } else if (perf_evsel__match(evsel, HARDWARE, HW_BRANCH_MISSES) &&
+                       runtime_branches_stats[ctx][cpu].n != 0) {
+               print_branch_misses(out, cpu, evsel, avg);
+       } else if (
+               evsel->attr.type == PERF_TYPE_HW_CACHE &&
+               evsel->attr.config ==  ( PERF_COUNT_HW_CACHE_L1D |
+                                       ((PERF_COUNT_HW_CACHE_OP_READ) << 8) |
+                                       ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16)) &&
+                       runtime_l1_dcache_stats[ctx][cpu].n != 0) {
+               print_l1_dcache_misses(out, cpu, evsel, avg);
+       } else if (
+               evsel->attr.type == PERF_TYPE_HW_CACHE &&
+               evsel->attr.config ==  ( PERF_COUNT_HW_CACHE_L1I |
+                                       ((PERF_COUNT_HW_CACHE_OP_READ) << 8) |
+                                       ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16)) &&
+                       runtime_l1_icache_stats[ctx][cpu].n != 0) {
+               print_l1_icache_misses(out, cpu, evsel, avg);
+       } else if (
+               evsel->attr.type == PERF_TYPE_HW_CACHE &&
+               evsel->attr.config ==  ( PERF_COUNT_HW_CACHE_DTLB |
+                                       ((PERF_COUNT_HW_CACHE_OP_READ) << 8) |
+                                       ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16)) &&
+                       runtime_dtlb_cache_stats[ctx][cpu].n != 0) {
+               print_dtlb_cache_misses(out, cpu, evsel, avg);
+       } else if (
+               evsel->attr.type == PERF_TYPE_HW_CACHE &&
+               evsel->attr.config ==  ( PERF_COUNT_HW_CACHE_ITLB |
+                                       ((PERF_COUNT_HW_CACHE_OP_READ) << 8) |
+                                       ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16)) &&
+                       runtime_itlb_cache_stats[ctx][cpu].n != 0) {
+               print_itlb_cache_misses(out, cpu, evsel, avg);
+       } else if (
+               evsel->attr.type == PERF_TYPE_HW_CACHE &&
+               evsel->attr.config ==  ( PERF_COUNT_HW_CACHE_LL |
+                                       ((PERF_COUNT_HW_CACHE_OP_READ) << 8) |
+                                       ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16)) &&
+                       runtime_ll_cache_stats[ctx][cpu].n != 0) {
+               print_ll_cache_misses(out, cpu, evsel, avg);
+       } else if (perf_evsel__match(evsel, HARDWARE, HW_CACHE_MISSES) &&
+                       runtime_cacherefs_stats[ctx][cpu].n != 0) {
+               total = avg_stats(&runtime_cacherefs_stats[ctx][cpu]);
+
+               if (total)
+                       ratio = avg * 100 / total;
+
+               fprintf(out, " # %8.3f %% of all cache refs    ", ratio);
+
+       } else if (perf_evsel__match(evsel, HARDWARE, HW_STALLED_CYCLES_FRONTEND)) {
+               print_stalled_cycles_frontend(out, cpu, evsel, avg);
+       } else if (perf_evsel__match(evsel, HARDWARE, HW_STALLED_CYCLES_BACKEND)) {
+               print_stalled_cycles_backend(out, cpu, evsel, avg);
+       } else if (perf_evsel__match(evsel, HARDWARE, HW_CPU_CYCLES)) {
+               total = avg_stats(&runtime_nsecs_stats[cpu]);
+
+               if (total) {
+                       ratio = avg / total;
+                       fprintf(out, " # %8.3f GHz                    ", ratio);
+               } else {
+                       fprintf(out, "                                   ");
+               }
+       } else if (perf_stat_evsel__is(evsel, CYCLES_IN_TX)) {
+               total = avg_stats(&runtime_cycles_stats[ctx][cpu]);
+               if (total)
+                       fprintf(out,
+                               " #   %5.2f%% transactional cycles   ",
+                               100.0 * (avg / total));
+       } else if (perf_stat_evsel__is(evsel, CYCLES_IN_TX_CP)) {
+               total = avg_stats(&runtime_cycles_stats[ctx][cpu]);
+               total2 = avg_stats(&runtime_cycles_in_tx_stats[ctx][cpu]);
+               if (total2 < avg)
+                       total2 = avg;
+               if (total)
+                       fprintf(out,
+                               " #   %5.2f%% aborted cycles         ",
+                               100.0 * ((total2-avg) / total));
+       } else if (perf_stat_evsel__is(evsel, TRANSACTION_START) &&
+                  avg > 0 &&
+                  runtime_cycles_in_tx_stats[ctx][cpu].n != 0) {
+               total = avg_stats(&runtime_cycles_in_tx_stats[ctx][cpu]);
+
+               if (total)
+                       ratio = total / avg;
+
+               fprintf(out, " # %8.0f cycles / transaction   ", ratio);
+       } else if (perf_stat_evsel__is(evsel, ELISION_START) &&
+                  avg > 0 &&
+                  runtime_cycles_in_tx_stats[ctx][cpu].n != 0) {
+               total = avg_stats(&runtime_cycles_in_tx_stats[ctx][cpu]);
+
+               if (total)
+                       ratio = total / avg;
+
+               fprintf(out, " # %8.0f cycles / elision       ", ratio);
+       } else if (runtime_nsecs_stats[cpu].n != 0) {
+               char unit = 'M';
+
+               total = avg_stats(&runtime_nsecs_stats[cpu]);
+
+               if (total)
+                       ratio = 1000.0 * avg / total;
+               if (ratio < 0.001) {
+                       ratio *= 1000;
+                       unit = 'K';
+               }
+
+               fprintf(out, " # %8.3f %c/sec                  ", ratio, unit);
+       } else {
+               fprintf(out, "                                   ");
+       }
+}
index 6506b3dfb6059f71aa6c345df21fcbc16e651604..4014b709f956b96b86dab3526f3b35fec6d8c166 100644 (file)
@@ -1,6 +1,6 @@
 #include <math.h>
-
 #include "stat.h"
+#include "evsel.h"
 
 void update_stats(struct stats *stats, u64 val)
 {
@@ -61,3 +61,72 @@ double rel_stddev_stats(double stddev, double avg)
 
        return pct;
 }
+
+bool __perf_evsel_stat__is(struct perf_evsel *evsel,
+                          enum perf_stat_evsel_id id)
+{
+       struct perf_stat *ps = evsel->priv;
+
+       return ps->id == id;
+}
+
+#define ID(id, name) [PERF_STAT_EVSEL_ID__##id] = #name
+static const char *id_str[PERF_STAT_EVSEL_ID__MAX] = {
+       ID(NONE,                x),
+       ID(CYCLES_IN_TX,        cpu/cycles-t/),
+       ID(TRANSACTION_START,   cpu/tx-start/),
+       ID(ELISION_START,       cpu/el-start/),
+       ID(CYCLES_IN_TX_CP,     cpu/cycles-ct/),
+};
+#undef ID
+
+void perf_stat_evsel_id_init(struct perf_evsel *evsel)
+{
+       struct perf_stat *ps = evsel->priv;
+       int i;
+
+       /* ps->id is 0 hence PERF_STAT_EVSEL_ID__NONE by default */
+
+       for (i = 0; i < PERF_STAT_EVSEL_ID__MAX; i++) {
+               if (!strcmp(perf_evsel__name(evsel), id_str[i])) {
+                       ps->id = i;
+                       break;
+               }
+       }
+}
+
+struct perf_counts *perf_counts__new(int ncpus)
+{
+       int size = sizeof(struct perf_counts) +
+                  ncpus * sizeof(struct perf_counts_values);
+
+       return zalloc(size);
+}
+
+void perf_counts__delete(struct perf_counts *counts)
+{
+       free(counts);
+}
+
+static void perf_counts__reset(struct perf_counts *counts, int ncpus)
+{
+       memset(counts, 0, (sizeof(*counts) +
+              (ncpus * sizeof(struct perf_counts_values))));
+}
+
+void perf_evsel__reset_counts(struct perf_evsel *evsel, int ncpus)
+{
+       perf_counts__reset(evsel->counts, ncpus);
+}
+
+int perf_evsel__alloc_counts(struct perf_evsel *evsel, int ncpus)
+{
+       evsel->counts = perf_counts__new(ncpus);
+       return evsel->counts != NULL ? 0 : -ENOMEM;
+}
+
+void perf_evsel__free_counts(struct perf_evsel *evsel)
+{
+       perf_counts__delete(evsel->counts);
+       evsel->counts = NULL;
+}
index 5667fc3e39cf45fe31f099e702a68eeeb202d16f..093dc3cb28dd3f62cb593095dfdc9c59317e0446 100644 (file)
@@ -2,6 +2,7 @@
 #define __PERF_STATS_H
 
 #include <linux/types.h>
+#include <stdio.h>
 
 struct stats
 {
@@ -9,6 +10,27 @@ struct stats
        u64 max, min;
 };
 
+enum perf_stat_evsel_id {
+       PERF_STAT_EVSEL_ID__NONE = 0,
+       PERF_STAT_EVSEL_ID__CYCLES_IN_TX,
+       PERF_STAT_EVSEL_ID__TRANSACTION_START,
+       PERF_STAT_EVSEL_ID__ELISION_START,
+       PERF_STAT_EVSEL_ID__CYCLES_IN_TX_CP,
+       PERF_STAT_EVSEL_ID__MAX,
+};
+
+struct perf_stat {
+       struct stats            res_stats[3];
+       enum perf_stat_evsel_id id;
+};
+
+enum aggr_mode {
+       AGGR_NONE,
+       AGGR_GLOBAL,
+       AGGR_SOCKET,
+       AGGR_CORE,
+};
+
 void update_stats(struct stats *stats, u64 val);
 double avg_stats(struct stats *stats);
 double stddev_stats(struct stats *stats);
@@ -22,4 +44,28 @@ static inline void init_stats(struct stats *stats)
        stats->min  = (u64) -1;
        stats->max  = 0;
 }
+
+struct perf_evsel;
+bool __perf_evsel_stat__is(struct perf_evsel *evsel,
+                          enum perf_stat_evsel_id id);
+
+#define perf_stat_evsel__is(evsel, id) \
+       __perf_evsel_stat__is(evsel, PERF_STAT_EVSEL_ID__ ## id)
+
+void perf_stat_evsel_id_init(struct perf_evsel *evsel);
+
+extern struct stats walltime_nsecs_stats;
+
+void perf_stat__reset_shadow_stats(void);
+void perf_stat__update_shadow_stats(struct perf_evsel *counter, u64 *count,
+                                   int cpu);
+void perf_stat__print_shadow_stats(FILE *out, struct perf_evsel *evsel,
+                                  double avg, int cpu, enum aggr_mode aggr);
+
+struct perf_counts *perf_counts__new(int ncpus);
+void perf_counts__delete(struct perf_counts *counts);
+
+void perf_evsel__reset_counts(struct perf_evsel *evsel, int ncpus);
+int perf_evsel__alloc_counts(struct perf_evsel *evsel, int ncpus);
+void perf_evsel__free_counts(struct perf_evsel *evsel);
 #endif
index 79a757a2a15c22db5eec8494d662071cde57a12e..bcae659b65462cddff5c03c2c41a8fc675ad05bc 100644 (file)
@@ -170,6 +170,46 @@ struct strfilter *strfilter__new(const char *rules, const char **err)
        return filter;
 }
 
+static int strfilter__append(struct strfilter *filter, bool _or,
+                            const char *rules, const char **err)
+{
+       struct strfilter_node *right, *root;
+       const char *ep = NULL;
+
+       if (!filter || !rules)
+               return -EINVAL;
+
+       right = strfilter_node__new(rules, &ep);
+       if (!right || *ep != '\0') {
+               if (err)
+                       *err = ep;
+               goto error;
+       }
+       root = strfilter_node__alloc(_or ? OP_or : OP_and, filter->root, right);
+       if (!root) {
+               ep = NULL;
+               goto error;
+       }
+
+       filter->root = root;
+       return 0;
+
+error:
+       strfilter_node__delete(right);
+       return ep ? -EINVAL : -ENOMEM;
+}
+
+int strfilter__or(struct strfilter *filter, const char *rules, const char **err)
+{
+       return strfilter__append(filter, true, rules, err);
+}
+
+int strfilter__and(struct strfilter *filter, const char *rules,
+                  const char **err)
+{
+       return strfilter__append(filter, false, rules, err);
+}
+
 static bool strfilter_node__compare(struct strfilter_node *node,
                                    const char *str)
 {
@@ -197,3 +237,70 @@ bool strfilter__compare(struct strfilter *filter, const char *str)
                return false;
        return strfilter_node__compare(filter->root, str);
 }
+
+static int strfilter_node__sprint(struct strfilter_node *node, char *buf);
+
+/* sprint node in parenthesis if needed */
+static int strfilter_node__sprint_pt(struct strfilter_node *node, char *buf)
+{
+       int len;
+       int pt = node->r ? 2 : 0;       /* don't need to check node->l */
+
+       if (buf && pt)
+               *buf++ = '(';
+       len = strfilter_node__sprint(node, buf);
+       if (len < 0)
+               return len;
+       if (buf && pt)
+               *(buf + len) = ')';
+       return len + pt;
+}
+
+static int strfilter_node__sprint(struct strfilter_node *node, char *buf)
+{
+       int len = 0, rlen;
+
+       if (!node || !node->p)
+               return -EINVAL;
+
+       switch (*node->p) {
+       case '|':
+       case '&':
+               len = strfilter_node__sprint_pt(node->l, buf);
+               if (len < 0)
+                       return len;
+       case '!':
+               if (buf) {
+                       *(buf + len++) = *node->p;
+                       buf += len;
+               } else
+                       len++;
+               rlen = strfilter_node__sprint_pt(node->r, buf);
+               if (rlen < 0)
+                       return rlen;
+               len += rlen;
+               break;
+       default:
+               len = strlen(node->p);
+               if (buf)
+                       strcpy(buf, node->p);
+       }
+
+       return len;
+}
+
+char *strfilter__string(struct strfilter *filter)
+{
+       int len;
+       char *ret = NULL;
+
+       len = strfilter_node__sprint(filter->root, NULL);
+       if (len < 0)
+               return NULL;
+
+       ret = malloc(len + 1);
+       if (ret)
+               strfilter_node__sprint(filter->root, ret);
+
+       return ret;
+}
index fe611f3c9e3965007a7ea5ed9f3fbbc768b5f271..cff5eda88728b20e9ebae22d7476ed14f367869c 100644 (file)
@@ -28,6 +28,32 @@ struct strfilter {
  */
 struct strfilter *strfilter__new(const char *rules, const char **err);
 
+/**
+ * strfilter__or - Append an additional rule by logical-or
+ * @filter: Original string filter
+ * @rules: Filter rule to be appended at left of the root of
+ *         @filter by using logical-or.
+ * @err: Pointer which points an error detected on @rules
+ *
+ * Parse @rules and join it to the @filter by using logical-or.
+ * Return 0 if success, or return the error code.
+ */
+int strfilter__or(struct strfilter *filter,
+                 const char *rules, const char **err);
+
+/**
+ * strfilter__add - Append an additional rule by logical-and
+ * @filter: Original string filter
+ * @rules: Filter rule to be appended at left of the root of
+ *         @filter by using logical-and.
+ * @err: Pointer which points an error detected on @rules
+ *
+ * Parse @rules and join it to the @filter by using logical-and.
+ * Return 0 if success, or return the error code.
+ */
+int strfilter__and(struct strfilter *filter,
+                  const char *rules, const char **err);
+
 /**
  * strfilter__compare - compare given string and a string filter
  * @filter: String filter
@@ -45,4 +71,13 @@ bool strfilter__compare(struct strfilter *filter, const char *str);
  */
 void strfilter__delete(struct strfilter *filter);
 
+/**
+ * strfilter__string - Reconstruct a rule string from filter
+ * @filter: String filter to reconstruct
+ *
+ * Reconstruct a rule string from @filter. This will be good for
+ * debug messages. Note that returning string must be freed afterward.
+ */
+char *strfilter__string(struct strfilter *filter);
+
 #endif
index a7ab6063e0389488a420680db46b96743366c1c2..65f7e389ae0996cae131cbfbcd2196181f15f1e7 100644 (file)
@@ -630,6 +630,11 @@ void symsrc__destroy(struct symsrc *ss)
        close(ss->fd);
 }
 
+bool __weak elf__needs_adjust_symbols(GElf_Ehdr ehdr)
+{
+       return ehdr.e_type == ET_EXEC || ehdr.e_type == ET_REL;
+}
+
 int symsrc__init(struct symsrc *ss, struct dso *dso, const char *name,
                 enum dso_binary_type type)
 {
@@ -678,6 +683,7 @@ int symsrc__init(struct symsrc *ss, struct dso *dso, const char *name,
                }
 
                if (!dso__build_id_equal(dso, build_id)) {
+                       pr_debug("%s: build id mismatch for %s.\n", __func__, name);
                        dso->load_errno = DSO_LOAD_ERRNO__MISMATCHING_BUILDID;
                        goto out_elf_end;
                }
@@ -711,8 +717,7 @@ int symsrc__init(struct symsrc *ss, struct dso *dso, const char *name,
                                                     ".gnu.prelink_undo",
                                                     NULL) != NULL);
        } else {
-               ss->adjust_symbols = ehdr.e_type == ET_EXEC ||
-                                    ehdr.e_type == ET_REL;
+               ss->adjust_symbols = elf__needs_adjust_symbols(ehdr);
        }
 
        ss->name   = strdup(name);
@@ -771,6 +776,8 @@ static bool want_demangle(bool is_kernel_sym)
        return is_kernel_sym ? symbol_conf.demangle_kernel : symbol_conf.demangle;
 }
 
+void __weak arch__elf_sym_adjust(GElf_Sym *sym __maybe_unused) { }
+
 int dso__load_sym(struct dso *dso, struct map *map,
                  struct symsrc *syms_ss, struct symsrc *runtime_ss,
                  symbol_filter_t filter, int kmodule)
@@ -935,6 +942,8 @@ int dso__load_sym(struct dso *dso, struct map *map,
                    (sym.st_value & 1))
                        --sym.st_value;
 
+               arch__elf_sym_adjust(&sym);
+
                if (dso->kernel || kmodule) {
                        char dso_name[PATH_MAX];
 
@@ -963,8 +972,10 @@ int dso__load_sym(struct dso *dso, struct map *map,
                                        map->unmap_ip = map__unmap_ip;
                                        /* Ensure maps are correctly ordered */
                                        if (kmaps) {
+                                               map__get(map);
                                                map_groups__remove(kmaps, map);
                                                map_groups__insert(kmaps, map);
+                                               map__put(map);
                                        }
                                }
 
@@ -1005,7 +1016,7 @@ int dso__load_sym(struct dso *dso, struct map *map,
                                curr_map = map__new2(start, curr_dso,
                                                     map->type);
                                if (curr_map == NULL) {
-                                       dso__delete(curr_dso);
+                                       dso__put(curr_dso);
                                        goto out_elf_end;
                                }
                                if (adjust_kernel_syms) {
@@ -1020,11 +1031,7 @@ int dso__load_sym(struct dso *dso, struct map *map,
                                }
                                curr_dso->symtab_type = dso->symtab_type;
                                map_groups__insert(kmaps, curr_map);
-                               /*
-                                * The new DSO should go to the kernel DSOS
-                                */
-                               dsos__add(&map->groups->machine->kernel_dsos,
-                                         curr_dso);
+                               dsos__add(&map->groups->machine->dsos, curr_dso);
                                dso__set_loaded(curr_dso, map->type);
                        } else
                                curr_dso = curr_map->dso;
index 201f6c4ca738ddffb46d5270a876cf96e8da9322..504f2d73b7eefe2699349ac75c5cc3b875961375 100644 (file)
@@ -85,8 +85,17 @@ static int prefix_underscores_count(const char *str)
        return tail - str;
 }
 
-#define SYMBOL_A 0
-#define SYMBOL_B 1
+int __weak arch__choose_best_symbol(struct symbol *syma,
+                                   struct symbol *symb __maybe_unused)
+{
+       /* Avoid "SyS" kernel syscall aliases */
+       if (strlen(syma->name) >= 3 && !strncmp(syma->name, "SyS", 3))
+               return SYMBOL_B;
+       if (strlen(syma->name) >= 10 && !strncmp(syma->name, "compat_SyS", 10))
+               return SYMBOL_B;
+
+       return SYMBOL_A;
+}
 
 static int choose_best_symbol(struct symbol *syma, struct symbol *symb)
 {
@@ -134,13 +143,7 @@ static int choose_best_symbol(struct symbol *syma, struct symbol *symb)
        else if (na < nb)
                return SYMBOL_B;
 
-       /* Avoid "SyS" kernel syscall aliases */
-       if (na >= 3 && !strncmp(syma->name, "SyS", 3))
-               return SYMBOL_B;
-       if (na >= 10 && !strncmp(syma->name, "compat_SyS", 10))
-               return SYMBOL_B;
-
-       return SYMBOL_A;
+       return arch__choose_best_symbol(syma, symb);
 }
 
 void symbols__fixup_duplicate(struct rb_root *symbols)
@@ -199,18 +202,18 @@ void symbols__fixup_end(struct rb_root *symbols)
 
 void __map_groups__fixup_end(struct map_groups *mg, enum map_type type)
 {
-       struct map *prev, *curr;
-       struct rb_node *nd, *prevnd = rb_first(&mg->maps[type]);
+       struct maps *maps = &mg->maps[type];
+       struct map *next, *curr;
 
-       if (prevnd == NULL)
-               return;
+       pthread_rwlock_wrlock(&maps->lock);
 
-       curr = rb_entry(prevnd, struct map, rb_node);
+       curr = maps__first(maps);
+       if (curr == NULL)
+               goto out_unlock;
 
-       for (nd = rb_next(prevnd); nd; nd = rb_next(nd)) {
-               prev = curr;
-               curr = rb_entry(nd, struct map, rb_node);
-               prev->end = curr->start;
+       for (next = map__next(curr); next; next = map__next(curr)) {
+               curr->end = next->start;
+               curr = next;
        }
 
        /*
@@ -218,6 +221,9 @@ void __map_groups__fixup_end(struct map_groups *mg, enum map_type type)
         * last map final address.
         */
        curr->end = ~0ULL;
+
+out_unlock:
+       pthread_rwlock_unlock(&maps->lock);
 }
 
 struct symbol *symbol__new(u64 start, u64 len, u8 binding, const char *name)
@@ -397,7 +403,7 @@ static struct symbol *symbols__find_by_name(struct rb_root *symbols,
                                            const char *name)
 {
        struct rb_node *n;
-       struct symbol_name_rb_node *s;
+       struct symbol_name_rb_node *s = NULL;
 
        if (symbols == NULL)
                return NULL;
@@ -408,7 +414,7 @@ static struct symbol *symbols__find_by_name(struct rb_root *symbols,
                int cmp;
 
                s = rb_entry(n, struct symbol_name_rb_node, rb_node);
-               cmp = strcmp(name, s->sym.name);
+               cmp = arch__compare_symbol_names(name, s->sym.name);
 
                if (cmp < 0)
                        n = n->rb_left;
@@ -426,7 +432,7 @@ static struct symbol *symbols__find_by_name(struct rb_root *symbols,
                struct symbol_name_rb_node *tmp;
 
                tmp = rb_entry(n, struct symbol_name_rb_node, rb_node);
-               if (strcmp(tmp->sym.name, s->sym.name))
+               if (arch__compare_symbol_names(tmp->sym.name, s->sym.name))
                        break;
 
                s = tmp;
@@ -653,14 +659,14 @@ static int dso__split_kallsyms_for_kcore(struct dso *dso, struct map *map,
                curr_map = map_groups__find(kmaps, map->type, pos->start);
 
                if (!curr_map || (filter && filter(curr_map, pos))) {
-                       rb_erase(&pos->rb_node, root);
+                       rb_erase_init(&pos->rb_node, root);
                        symbol__delete(pos);
                } else {
                        pos->start -= curr_map->start - curr_map->pgoff;
                        if (pos->end)
                                pos->end -= curr_map->start - curr_map->pgoff;
                        if (curr_map != map) {
-                               rb_erase(&pos->rb_node, root);
+                               rb_erase_init(&pos->rb_node, root);
                                symbols__insert(
                                        &curr_map->dso->symbols[curr_map->type],
                                        pos);
@@ -780,7 +786,7 @@ static int dso__split_kallsyms(struct dso *dso, struct map *map, u64 delta,
 
                        curr_map = map__new2(pos->start, ndso, map->type);
                        if (curr_map == NULL) {
-                               dso__delete(ndso);
+                               dso__put(ndso);
                                return -1;
                        }
 
@@ -1167,20 +1173,23 @@ static int dso__load_kcore(struct dso *dso, struct map *map,
        /* Add new maps */
        while (!list_empty(&md.maps)) {
                new_map = list_entry(md.maps.next, struct map, node);
-               list_del(&new_map->node);
+               list_del_init(&new_map->node);
                if (new_map == replacement_map) {
                        map->start      = new_map->start;
                        map->end        = new_map->end;
                        map->pgoff      = new_map->pgoff;
                        map->map_ip     = new_map->map_ip;
                        map->unmap_ip   = new_map->unmap_ip;
-                       map__delete(new_map);
                        /* Ensure maps are correctly ordered */
+                       map__get(map);
                        map_groups__remove(kmaps, map);
                        map_groups__insert(kmaps, map);
+                       map__put(map);
                } else {
                        map_groups__insert(kmaps, new_map);
                }
+
+               map__put(new_map);
        }
 
        /*
@@ -1205,8 +1214,8 @@ static int dso__load_kcore(struct dso *dso, struct map *map,
 out_err:
        while (!list_empty(&md.maps)) {
                map = list_entry(md.maps.next, struct map, node);
-               list_del(&map->node);
-               map__delete(map);
+               list_del_init(&map->node);
+               map__put(map);
        }
        close(fd);
        return -EINVAL;
@@ -1355,7 +1364,7 @@ static bool dso__is_compatible_symtab_type(struct dso *dso, bool kmod,
        case DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE_COMP:
                /*
                 * kernel modules know their symtab type - it's set when
-                * creating a module dso in machine__new_module().
+                * creating a module dso in machine__findnew_module_map().
                 */
                return kmod && dso->symtab_type == type;
 
@@ -1380,12 +1389,22 @@ int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter)
        struct symsrc *syms_ss = NULL, *runtime_ss = NULL;
        bool kmod;
 
-       dso__set_loaded(dso, map->type);
+       pthread_mutex_lock(&dso->lock);
+
+       /* check again under the dso->lock */
+       if (dso__loaded(dso, map->type)) {
+               ret = 1;
+               goto out;
+       }
 
-       if (dso->kernel == DSO_TYPE_KERNEL)
-               return dso__load_kernel_sym(dso, map, filter);
-       else if (dso->kernel == DSO_TYPE_GUEST_KERNEL)
-               return dso__load_guest_kernel_sym(dso, map, filter);
+       if (dso->kernel) {
+               if (dso->kernel == DSO_TYPE_KERNEL)
+                       ret = dso__load_kernel_sym(dso, map, filter);
+               else if (dso->kernel == DSO_TYPE_GUEST_KERNEL)
+                       ret = dso__load_guest_kernel_sym(dso, map, filter);
+
+               goto out;
+       }
 
        if (map->groups && map->groups->machine)
                machine = map->groups->machine;
@@ -1398,18 +1417,18 @@ int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter)
                struct stat st;
 
                if (lstat(dso->name, &st) < 0)
-                       return -1;
+                       goto out;
 
                if (st.st_uid && (st.st_uid != geteuid())) {
                        pr_warning("File %s not owned by current user or root, "
                                "ignoring it.\n", dso->name);
-                       return -1;
+                       goto out;
                }
 
                ret = dso__load_perf_map(dso, map, filter);
                dso->symtab_type = ret > 0 ? DSO_BINARY_TYPE__JAVA_JIT :
                                             DSO_BINARY_TYPE__NOT_FOUND;
-               return ret;
+               goto out;
        }
 
        if (machine)
@@ -1417,7 +1436,7 @@ int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter)
 
        name = malloc(PATH_MAX);
        if (!name)
-               return -1;
+               goto out;
 
        kmod = dso->symtab_type == DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE ||
                dso->symtab_type == DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE_COMP ||
@@ -1498,23 +1517,32 @@ int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter)
 out_free:
        free(name);
        if (ret < 0 && strstr(dso->name, " (deleted)") != NULL)
-               return 0;
+               ret = 0;
+out:
+       dso__set_loaded(dso, map->type);
+       pthread_mutex_unlock(&dso->lock);
+
        return ret;
 }
 
 struct map *map_groups__find_by_name(struct map_groups *mg,
                                     enum map_type type, const char *name)
 {
-       struct rb_node *nd;
+       struct maps *maps = &mg->maps[type];
+       struct map *map;
 
-       for (nd = rb_first(&mg->maps[type]); nd; nd = rb_next(nd)) {
-               struct map *map = rb_entry(nd, struct map, rb_node);
+       pthread_rwlock_rdlock(&maps->lock);
 
+       for (map = maps__first(maps); map; map = map__next(map)) {
                if (map->dso && strcmp(map->dso->short_name, name) == 0)
-                       return map;
+                       goto out_unlock;
        }
 
-       return NULL;
+       map = NULL;
+
+out_unlock:
+       pthread_rwlock_unlock(&maps->lock);
+       return map;
 }
 
 int dso__load_vmlinux(struct dso *dso, struct map *map,
@@ -1802,6 +1830,7 @@ static void vmlinux_path__exit(void)
 {
        while (--vmlinux_path__nr_entries >= 0)
                zfree(&vmlinux_path[vmlinux_path__nr_entries]);
+       vmlinux_path__nr_entries = 0;
 
        zfree(&vmlinux_path);
 }
index 09561500164a07997b49ca744f7f557ee200f043..bef47ead1d9bd1efc5e9620f04b13714286ff616 100644 (file)
@@ -158,8 +158,6 @@ struct ref_reloc_sym {
 struct map_symbol {
        struct map    *map;
        struct symbol *sym;
-       bool          unfolded;
-       bool          has_children;
 };
 
 struct addr_map_symbol {
@@ -303,4 +301,14 @@ int setup_list(struct strlist **list, const char *list_str,
 int setup_intlist(struct intlist **list, const char *list_str,
                  const char *list_name);
 
+#ifdef HAVE_LIBELF_SUPPORT
+bool elf__needs_adjust_symbols(GElf_Ehdr ehdr);
+void arch__elf_sym_adjust(GElf_Sym *sym);
+#endif
+
+#define SYMBOL_A 0
+#define SYMBOL_B 1
+
+int arch__choose_best_symbol(struct symbol *syma, struct symbol *symb);
+
 #endif /* __PERF_SYMBOL */
index 9ed59a452d1ff82966fd4eb99697d9bc3d9dd604..679688e70ae7e72e73d14cc7659cb965cc4d7016 100644 (file)
@@ -219,7 +219,7 @@ static int thread_stack__call_return(struct thread *thread,
        return crp->process(&cr, crp->data);
 }
 
-static int thread_stack__flush(struct thread *thread, struct thread_stack *ts)
+static int __thread_stack__flush(struct thread *thread, struct thread_stack *ts)
 {
        struct call_return_processor *crp = ts->crp;
        int err;
@@ -242,6 +242,14 @@ static int thread_stack__flush(struct thread *thread, struct thread_stack *ts)
        return 0;
 }
 
+int thread_stack__flush(struct thread *thread)
+{
+       if (thread->ts)
+               return __thread_stack__flush(thread, thread->ts);
+
+       return 0;
+}
+
 int thread_stack__event(struct thread *thread, u32 flags, u64 from_ip,
                        u64 to_ip, u16 insn_len, u64 trace_nr)
 {
@@ -264,7 +272,7 @@ int thread_stack__event(struct thread *thread, u32 flags, u64 from_ip,
         */
        if (trace_nr != thread->ts->trace_nr) {
                if (thread->ts->trace_nr)
-                       thread_stack__flush(thread, thread->ts);
+                       __thread_stack__flush(thread, thread->ts);
                thread->ts->trace_nr = trace_nr;
        }
 
@@ -297,7 +305,7 @@ void thread_stack__set_trace_nr(struct thread *thread, u64 trace_nr)
 
        if (trace_nr != thread->ts->trace_nr) {
                if (thread->ts->trace_nr)
-                       thread_stack__flush(thread, thread->ts);
+                       __thread_stack__flush(thread, thread->ts);
                thread->ts->trace_nr = trace_nr;
        }
 }
@@ -305,7 +313,7 @@ void thread_stack__set_trace_nr(struct thread *thread, u64 trace_nr)
 void thread_stack__free(struct thread *thread)
 {
        if (thread->ts) {
-               thread_stack__flush(thread, thread->ts);
+               __thread_stack__flush(thread, thread->ts);
                zfree(&thread->ts->stack);
                zfree(&thread->ts);
        }
@@ -689,7 +697,7 @@ int thread_stack__process(struct thread *thread, struct comm *comm,
 
        /* Flush stack on exec */
        if (ts->comm != comm && thread->pid_ == thread->tid) {
-               err = thread_stack__flush(thread, ts);
+               err = __thread_stack__flush(thread, ts);
                if (err)
                        return err;
                ts->comm = comm;
index b843bbef8ba2177a66d3b2a3cebb8ac56d8b6f44..e1528f1374c3e5131efe8c2293ef9a6736ea3ed4 100644 (file)
@@ -96,6 +96,7 @@ int thread_stack__event(struct thread *thread, u32 flags, u64 from_ip,
 void thread_stack__set_trace_nr(struct thread *thread, u64 trace_nr);
 void thread_stack__sample(struct thread *thread, struct ip_callchain *chain,
                          size_t sz, u64 ip);
+int thread_stack__flush(struct thread *thread);
 void thread_stack__free(struct thread *thread);
 
 struct call_return_processor *
index 1c8fbc9588c5fddc978e8a07562aecc167a2a752..28c4b746baa19bef9830814c4fe7c69f1be0b06b 100644 (file)
@@ -18,7 +18,7 @@ int thread__init_map_groups(struct thread *thread, struct machine *machine)
        if (pid == thread->tid || pid == -1) {
                thread->mg = map_groups__new(machine);
        } else {
-               leader = machine__findnew_thread(machine, pid, pid);
+               leader = __machine__findnew_thread(machine, pid, pid);
                if (leader)
                        thread->mg = map_groups__get(leader->mg);
        }
@@ -53,7 +53,8 @@ struct thread *thread__new(pid_t pid, pid_t tid)
                        goto err_thread;
 
                list_add(&comm->list, &thread->comm_list);
-
+               atomic_set(&thread->refcnt, 0);
+               RB_CLEAR_NODE(&thread->rb_node);
        }
 
        return thread;
@@ -67,6 +68,8 @@ void thread__delete(struct thread *thread)
 {
        struct comm *comm, *tmp;
 
+       BUG_ON(!RB_EMPTY_NODE(&thread->rb_node));
+
        thread_stack__free(thread);
 
        if (thread->mg) {
@@ -84,13 +87,14 @@ void thread__delete(struct thread *thread)
 
 struct thread *thread__get(struct thread *thread)
 {
-       ++thread->refcnt;
+       if (thread)
+               atomic_inc(&thread->refcnt);
        return thread;
 }
 
 void thread__put(struct thread *thread)
 {
-       if (thread && --thread->refcnt == 0) {
+       if (thread && atomic_dec_and_test(&thread->refcnt)) {
                list_del_init(&thread->node);
                thread__delete(thread);
        }
index 9b8a54dc34a81963d8026e226bcd3334713b1606..a0ac0317affb5ffc46f69dc00c4c258d0c40c684 100644 (file)
@@ -1,6 +1,7 @@
 #ifndef __PERF_THREAD_H
 #define __PERF_THREAD_H
 
+#include <linux/atomic.h>
 #include <linux/rbtree.h>
 #include <linux/list.h>
 #include <unistd.h>
@@ -21,12 +22,12 @@ struct thread {
        pid_t                   tid;
        pid_t                   ppid;
        int                     cpu;
-       int                     refcnt;
+       atomic_t                refcnt;
        char                    shortname[3];
        bool                    comm_set;
+       int                     comm_len;
        bool                    dead; /* if set thread has exited */
        struct list_head        comm_list;
-       int                     comm_len;
        u64                     db_id;
 
        void                    *priv;
index f93b9734735b9478d3b8da0a2edf6be03403079d..f4822bd03709af52aba3eed89d50f06bc53e4f86 100644 (file)
@@ -20,6 +20,15 @@ static int filter(const struct dirent *dir)
                return 1;
 }
 
+static struct thread_map *thread_map__realloc(struct thread_map *map, int nr)
+{
+       size_t size = sizeof(*map) + sizeof(pid_t) * nr;
+
+       return realloc(map, size);
+}
+
+#define thread_map__alloc(__nr) thread_map__realloc(NULL, __nr)
+
 struct thread_map *thread_map__new_by_pid(pid_t pid)
 {
        struct thread_map *threads;
@@ -33,7 +42,7 @@ struct thread_map *thread_map__new_by_pid(pid_t pid)
        if (items <= 0)
                return NULL;
 
-       threads = malloc(sizeof(*threads) + sizeof(pid_t) * items);
+       threads = thread_map__alloc(items);
        if (threads != NULL) {
                for (i = 0; i < items; i++)
                        threads->map[i] = atoi(namelist[i]->d_name);
@@ -49,7 +58,7 @@ struct thread_map *thread_map__new_by_pid(pid_t pid)
 
 struct thread_map *thread_map__new_by_tid(pid_t tid)
 {
-       struct thread_map *threads = malloc(sizeof(*threads) + sizeof(pid_t));
+       struct thread_map *threads = thread_map__alloc(1);
 
        if (threads != NULL) {
                threads->map[0] = tid;
@@ -65,8 +74,8 @@ struct thread_map *thread_map__new_by_uid(uid_t uid)
        int max_threads = 32, items, i;
        char path[256];
        struct dirent dirent, *next, **namelist = NULL;
-       struct thread_map *threads = malloc(sizeof(*threads) +
-                                           max_threads * sizeof(pid_t));
+       struct thread_map *threads = thread_map__alloc(max_threads);
+
        if (threads == NULL)
                goto out;
 
@@ -185,8 +194,7 @@ static struct thread_map *thread_map__new_by_pid_str(const char *pid_str)
                        goto out_free_threads;
 
                total_tasks += items;
-               nt = realloc(threads, (sizeof(*threads) +
-                                      sizeof(pid_t) * total_tasks));
+               nt = thread_map__realloc(threads, total_tasks);
                if (nt == NULL)
                        goto out_free_namelist;
 
@@ -216,7 +224,7 @@ out_free_threads:
 
 struct thread_map *thread_map__new_dummy(void)
 {
-       struct thread_map *threads = malloc(sizeof(*threads) + sizeof(pid_t));
+       struct thread_map *threads = thread_map__alloc(1);
 
        if (threads != NULL) {
                threads->map[0] = -1;
@@ -253,7 +261,7 @@ static struct thread_map *thread_map__new_by_tid_str(const char *tid_str)
                        continue;
 
                ntasks++;
-               nt = realloc(threads, sizeof(*threads) + sizeof(pid_t) * ntasks);
+               nt = thread_map__realloc(threads, ntasks);
 
                if (nt == NULL)
                        goto out_free_threads;
index 51d9e56c0f841d89f730f00e72e984a867b9f4fc..c307dd4382863dd7f68314885115138f489c385b 100644 (file)
@@ -3,6 +3,8 @@
 
 #include <stdbool.h>
 
+#include <linux/types.h>
+
 struct perf_session;
 union perf_event;
 struct perf_evlist;
@@ -29,6 +31,9 @@ typedef int (*event_op2)(struct perf_tool *tool, union perf_event *event,
 typedef int (*event_oe)(struct perf_tool *tool, union perf_event *event,
                        struct ordered_events *oe);
 
+typedef s64 (*event_op3)(struct perf_tool *tool, union perf_event *event,
+                        struct perf_session *session);
+
 struct perf_tool {
        event_sample    sample,
                        read;
@@ -38,13 +43,19 @@ struct perf_tool {
                        fork,
                        exit,
                        lost,
+                       lost_samples,
+                       aux,
+                       itrace_start,
                        throttle,
                        unthrottle;
        event_attr_op   attr;
        event_op2       tracing_data;
        event_oe        finished_round;
        event_op2       build_id,
-                       id_index;
+                       id_index,
+                       auxtrace_info,
+                       auxtrace_error;
+       event_op3       auxtrace;
        bool            ordered_events;
        bool            ordering_requires_timestamps;
 };
index 25d6c737be3e673db1904104a83286c5fbda0b40..d4957418657ec3ab88cce7f77b70095ff21d62c8 100644 (file)
@@ -173,7 +173,7 @@ void parse_ftrace_printk(struct pevent *pevent,
        char *line;
        char *next = NULL;
        char *addr_str;
-       char *fmt;
+       char *fmt = NULL;
 
        line = strtok_r(file, "\n", &next);
        while (line) {
index 7b09a443a280429d3de68f03ced731f66b36d3fc..4c00507ee3fd2ad488642def35226711cba821fe 100644 (file)
@@ -269,13 +269,14 @@ static int read_unwind_spec_eh_frame(struct dso *dso, struct machine *machine,
        u64 offset = dso->data.eh_frame_hdr_offset;
 
        if (offset == 0) {
-               fd = dso__data_fd(dso, machine);
+               fd = dso__data_get_fd(dso, machine);
                if (fd < 0)
                        return -EINVAL;
 
                /* Check the .eh_frame section for unwinding info */
                offset = elf_section_offset(fd, ".eh_frame_hdr");
                dso->data.eh_frame_hdr_offset = offset;
+               dso__data_put_fd(dso);
        }
 
        if (offset)
@@ -294,13 +295,14 @@ static int read_unwind_spec_debug_frame(struct dso *dso,
        u64 ofs = dso->data.debug_frame_offset;
 
        if (ofs == 0) {
-               fd = dso__data_fd(dso, machine);
+               fd = dso__data_get_fd(dso, machine);
                if (fd < 0)
                        return -EINVAL;
 
                /* Check the .debug_frame section for unwinding info */
                ofs = elf_section_offset(fd, ".debug_frame");
                dso->data.debug_frame_offset = ofs;
+               dso__data_put_fd(dso);
        }
 
        *offset = ofs;
@@ -353,10 +355,13 @@ find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi,
 #ifndef NO_LIBUNWIND_DEBUG_FRAME
        /* Check the .debug_frame section for unwinding info */
        if (!read_unwind_spec_debug_frame(map->dso, ui->machine, &segbase)) {
-               int fd = dso__data_fd(map->dso, ui->machine);
+               int fd = dso__data_get_fd(map->dso, ui->machine);
                int is_exec = elf_is_exec(fd, map->dso->name);
                unw_word_t base = is_exec ? 0 : map->start;
 
+               if (fd >= 0)
+                       dso__data_put_fd(map->dso);
+
                memset(&di, 0, sizeof(di));
                if (dwarf_find_debug_frame(0, &di, ip, base, map->dso->name,
                                           map->start, map->end))
index 4ee6d0d4c9931752e76abe15dd098468c4e0c01f..edc2d633b33224530e9dcb7780877bf7d1be08b3 100644 (file)
@@ -72,20 +72,60 @@ int mkdir_p(char *path, mode_t mode)
        return (stat(path, &st) && mkdir(path, mode)) ? -1 : 0;
 }
 
-static int slow_copyfile(const char *from, const char *to, mode_t mode)
+int rm_rf(char *path)
+{
+       DIR *dir;
+       int ret = 0;
+       struct dirent *d;
+       char namebuf[PATH_MAX];
+
+       dir = opendir(path);
+       if (dir == NULL)
+               return 0;
+
+       while ((d = readdir(dir)) != NULL && !ret) {
+               struct stat statbuf;
+
+               if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
+                       continue;
+
+               scnprintf(namebuf, sizeof(namebuf), "%s/%s",
+                         path, d->d_name);
+
+               ret = stat(namebuf, &statbuf);
+               if (ret < 0) {
+                       pr_debug("stat failed: %s\n", namebuf);
+                       break;
+               }
+
+               if (S_ISREG(statbuf.st_mode))
+                       ret = unlink(namebuf);
+               else if (S_ISDIR(statbuf.st_mode))
+                       ret = rm_rf(namebuf);
+               else {
+                       pr_debug("unknown file: %s\n", namebuf);
+                       ret = -1;
+               }
+       }
+       closedir(dir);
+
+       if (ret < 0)
+               return ret;
+
+       return rmdir(path);
+}
+
+static int slow_copyfile(const char *from, const char *to)
 {
        int err = -1;
        char *line = NULL;
        size_t n;
        FILE *from_fp = fopen(from, "r"), *to_fp;
-       mode_t old_umask;
 
        if (from_fp == NULL)
                goto out;
 
-       old_umask = umask(mode ^ 0777);
        to_fp = fopen(to, "w");
-       umask(old_umask);
        if (to_fp == NULL)
                goto out_fclose_from;
 
@@ -102,42 +142,81 @@ out:
        return err;
 }
 
+int copyfile_offset(int ifd, loff_t off_in, int ofd, loff_t off_out, u64 size)
+{
+       void *ptr;
+       loff_t pgoff;
+
+       pgoff = off_in & ~(page_size - 1);
+       off_in -= pgoff;
+
+       ptr = mmap(NULL, off_in + size, PROT_READ, MAP_PRIVATE, ifd, pgoff);
+       if (ptr == MAP_FAILED)
+               return -1;
+
+       while (size) {
+               ssize_t ret = pwrite(ofd, ptr + off_in, size, off_out);
+               if (ret < 0 && errno == EINTR)
+                       continue;
+               if (ret <= 0)
+                       break;
+
+               size -= ret;
+               off_in += ret;
+               off_out -= ret;
+       }
+       munmap(ptr, off_in + size);
+
+       return size ? -1 : 0;
+}
+
 int copyfile_mode(const char *from, const char *to, mode_t mode)
 {
        int fromfd, tofd;
        struct stat st;
-       void *addr;
        int err = -1;
+       char *tmp = NULL, *ptr = NULL;
 
        if (stat(from, &st))
                goto out;
 
-       if (st.st_size == 0) /* /proc? do it slowly... */
-               return slow_copyfile(from, to, mode);
-
-       fromfd = open(from, O_RDONLY);
-       if (fromfd < 0)
+       /* extra 'x' at the end is to reserve space for '.' */
+       if (asprintf(&tmp, "%s.XXXXXXx", to) < 0) {
+               tmp = NULL;
                goto out;
+       }
+       ptr = strrchr(tmp, '/');
+       if (!ptr)
+               goto out;
+       ptr = memmove(ptr + 1, ptr, strlen(ptr) - 1);
+       *ptr = '.';
 
-       tofd = creat(to, mode);
+       tofd = mkstemp(tmp);
        if (tofd < 0)
-               goto out_close_from;
+               goto out;
+
+       if (fchmod(tofd, mode))
+               goto out_close_to;
+
+       if (st.st_size == 0) { /* /proc? do it slowly... */
+               err = slow_copyfile(from, tmp);
+               goto out_close_to;
+       }
 
-       addr = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fromfd, 0);
-       if (addr == MAP_FAILED)
+       fromfd = open(from, O_RDONLY);
+       if (fromfd < 0)
                goto out_close_to;
 
-       if (write(tofd, addr, st.st_size) == st.st_size)
-               err = 0;
+       err = copyfile_offset(fromfd, 0, tofd, 0, st.st_size);
 
-       munmap(addr, st.st_size);
+       close(fromfd);
 out_close_to:
        close(tofd);
-       if (err)
-               unlink(to);
-out_close_from:
-       close(fromfd);
+       if (!err)
+               err = link(tmp, to);
+       unlink(tmp);
 out:
+       free(tmp);
        return err;
 }
 
index 1ff23e04ad2730b99f78dbeef3af56ba1a45e9ec..8bce58b47a826918db8f395e3e6c2711e9bbba8e 100644 (file)
@@ -249,14 +249,20 @@ static inline int sane_case(int x, int high)
 }
 
 int mkdir_p(char *path, mode_t mode);
+int rm_rf(char *path);
 int copyfile(const char *from, const char *to);
 int copyfile_mode(const char *from, const char *to, mode_t mode);
+int copyfile_offset(int fromfd, loff_t from_ofs, int tofd, loff_t to_ofs, u64 size);
 
 s64 perf_atoll(const char *str);
 char **argv_split(const char *str, int *argcp);
 void argv_free(char **argv);
 bool strglobmatch(const char *str, const char *pat);
 bool strlazymatch(const char *str, const char *pat);
+static inline bool strisglob(const char *str)
+{
+       return strpbrk(str, "*?[") != NULL;
+}
 int strtailcmp(const char *s1, const char *s2);
 char *strxfrchar(char *s, char from, char to);
 unsigned long convert_unit(unsigned long value, char *unit);
index 5c7dd796979d0d625df236c82c30b5e22f5ac7d9..4b89118f158db458ae29cca6d20b093d81392cf6 100644 (file)
@@ -101,7 +101,7 @@ static char *get_file(struct vdso_file *vdso_file)
        return vdso;
 }
 
-void vdso__exit(struct machine *machine)
+void machine__exit_vdso(struct machine *machine)
 {
        struct vdso_info *vdso_info = machine->vdso_info;
 
@@ -120,14 +120,14 @@ void vdso__exit(struct machine *machine)
        zfree(&machine->vdso_info);
 }
 
-static struct dso *vdso__new(struct machine *machine, const char *short_name,
-                            const char *long_name)
+static struct dso *__machine__addnew_vdso(struct machine *machine, const char *short_name,
+                                         const char *long_name)
 {
        struct dso *dso;
 
        dso = dso__new(short_name);
        if (dso != NULL) {
-               dsos__add(&machine->user_dsos, dso);
+               __dsos__add(&machine->dsos, dso);
                dso__set_long_name(dso, long_name, false);
        }
 
@@ -230,27 +230,31 @@ static const char *vdso__get_compat_file(struct vdso_file *vdso_file)
        return vdso_file->temp_file_name;
 }
 
-static struct dso *vdso__findnew_compat(struct machine *machine,
-                                       struct vdso_file *vdso_file)
+static struct dso *__machine__findnew_compat(struct machine *machine,
+                                            struct vdso_file *vdso_file)
 {
        const char *file_name;
        struct dso *dso;
 
-       dso = dsos__find(&machine->user_dsos, vdso_file->dso_name, true);
+       pthread_rwlock_wrlock(&machine->dsos.lock);
+       dso = __dsos__find(&machine->dsos, vdso_file->dso_name, true);
        if (dso)
-               return dso;
+               goto out_unlock;
 
        file_name = vdso__get_compat_file(vdso_file);
        if (!file_name)
-               return NULL;
+               goto out_unlock;
 
-       return vdso__new(machine, vdso_file->dso_name, file_name);
+       dso = __machine__addnew_vdso(machine, vdso_file->dso_name, file_name);
+out_unlock:
+       pthread_rwlock_unlock(&machine->dsos.lock);
+       return dso;
 }
 
-static int vdso__dso_findnew_compat(struct machine *machine,
-                                   struct thread *thread,
-                                   struct vdso_info *vdso_info,
-                                   struct dso **dso)
+static int __machine__findnew_vdso_compat(struct machine *machine,
+                                         struct thread *thread,
+                                         struct vdso_info *vdso_info,
+                                         struct dso **dso)
 {
        enum dso_type dso_type;
 
@@ -267,10 +271,10 @@ static int vdso__dso_findnew_compat(struct machine *machine,
 
        switch (dso_type) {
        case DSO__TYPE_32BIT:
-               *dso = vdso__findnew_compat(machine, &vdso_info->vdso32);
+               *dso = __machine__findnew_compat(machine, &vdso_info->vdso32);
                return 1;
        case DSO__TYPE_X32BIT:
-               *dso = vdso__findnew_compat(machine, &vdso_info->vdsox32);
+               *dso = __machine__findnew_compat(machine, &vdso_info->vdsox32);
                return 1;
        case DSO__TYPE_UNKNOWN:
        case DSO__TYPE_64BIT:
@@ -281,35 +285,37 @@ static int vdso__dso_findnew_compat(struct machine *machine,
 
 #endif
 
-struct dso *vdso__dso_findnew(struct machine *machine,
-                             struct thread *thread __maybe_unused)
+struct dso *machine__findnew_vdso(struct machine *machine,
+                                 struct thread *thread __maybe_unused)
 {
        struct vdso_info *vdso_info;
-       struct dso *dso;
+       struct dso *dso = NULL;
 
+       pthread_rwlock_wrlock(&machine->dsos.lock);
        if (!machine->vdso_info)
                machine->vdso_info = vdso_info__new();
 
        vdso_info = machine->vdso_info;
        if (!vdso_info)
-               return NULL;
+               goto out_unlock;
 
 #if BITS_PER_LONG == 64
-       if (vdso__dso_findnew_compat(machine, thread, vdso_info, &dso))
-               return dso;
+       if (__machine__findnew_vdso_compat(machine, thread, vdso_info, &dso))
+               goto out_unlock;
 #endif
 
-       dso = dsos__find(&machine->user_dsos, DSO__NAME_VDSO, true);
+       dso = __dsos__find(&machine->dsos, DSO__NAME_VDSO, true);
        if (!dso) {
                char *file;
 
                file = get_file(&vdso_info->vdso);
-               if (!file)
-                       return NULL;
-
-               dso = vdso__new(machine, DSO__NAME_VDSO, file);
+               if (file)
+                       dso = __machine__addnew_vdso(machine, DSO__NAME_VDSO, file);
        }
 
+out_unlock:
+       dso__get(dso);
+       pthread_rwlock_unlock(&machine->dsos.lock);
        return dso;
 }
 
index d97da1616f0c5b658d8c1846b0285835b3a3478c..cdc4fabfc2124efa9c2c497dc35a5f6b77840a85 100644 (file)
@@ -23,7 +23,7 @@ bool dso__is_vdso(struct dso *dso);
 struct machine;
 struct thread;
 
-struct dso *vdso__dso_findnew(struct machine *machine, struct thread *thread);
-void vdso__exit(struct machine *machine);
+struct dso *machine__findnew_vdso(struct machine *machine, struct thread *thread);
+void machine__exit_vdso(struct machine *machine);
 
 #endif /* __PERF_VDSO__ */
index 22afbf6c536adbb291f4ee87a35edf970722fc5a..c10ba41ef3f6298eb77e624f7f1b14f41555c36c 100644 (file)
@@ -9,11 +9,19 @@ struct xyarray *xyarray__new(int xlen, int ylen, size_t entry_size)
        if (xy != NULL) {
                xy->entry_size = entry_size;
                xy->row_size   = row_size;
+               xy->entries    = xlen * ylen;
        }
 
        return xy;
 }
 
+void xyarray__reset(struct xyarray *xy)
+{
+       size_t n = xy->entries * xy->entry_size;
+
+       memset(xy->contents, 0, n);
+}
+
 void xyarray__delete(struct xyarray *xy)
 {
        free(xy);
index c488a07275dd2783f13360a341063de1d87eab6c..7f30af371b7ee692f23b9bf4aeb354584f453722 100644 (file)
@@ -6,11 +6,13 @@
 struct xyarray {
        size_t row_size;
        size_t entry_size;
+       size_t entries;
        char contents[];
 };
 
 struct xyarray *xyarray__new(int xlen, int ylen, size_t entry_size);
 void xyarray__delete(struct xyarray *xy);
+void xyarray__reset(struct xyarray *xy);
 
 static inline void *xyarray__entry(struct xyarray *xy, int x, int y)
 {
index 90a8c4f071e73e462270057c41b6632609d6395d..c83f1606970b922af7e4ce8a2420ed57dd51b8b2 100644 (file)
@@ -135,7 +135,7 @@ static int mperf_get_count_percent(unsigned int id, double *percent,
                dprint("%s: TSC Ref - mperf_diff: %llu, tsc_diff: %llu\n",
                       mperf_cstates[id].name, mperf_diff, tsc_diff);
        } else if (max_freq_mode == MAX_FREQ_SYSFS) {
-               timediff = timespec_diff_us(time_start, time_end);
+               timediff = max_frequency * timespec_diff_us(time_start, time_end);
                *percent = 100.0 * mperf_diff / timediff;
                dprint("%s: MAXFREQ - mperf_diff: %llu, time_diff: %llu\n",
                       mperf_cstates[id].name, mperf_diff, timediff);
@@ -176,7 +176,7 @@ static int mperf_get_count_freq(unsigned int id, unsigned long long *count,
        dprint("%s: Average freq based on %s maximum frequency:\n",
               mperf_cstates[id].name,
               (max_freq_mode == MAX_FREQ_TSC_REF) ? "TSC calculated" : "sysfs read");
-       dprint("%max_frequency: %lu", max_frequency);
+       dprint("max_frequency: %lu\n", max_frequency);
        dprint("aperf_diff: %llu\n", aperf_diff);
        dprint("mperf_diff: %llu\n", mperf_diff);
        dprint("avg freq:   %llu\n", *count);
@@ -279,6 +279,7 @@ use_sysfs:
                return -1;
        }
        max_freq_mode = MAX_FREQ_SYSFS;
+       max_frequency /= 1000; /* Default automatically to MHz value */
        return 0;
 }
 
index 4039854560d0d5dffe32150562a6f11b2791033f..e367b1a85d70b3e0b7d82c5766d83a31ce006d2a 100644 (file)
@@ -9,7 +9,7 @@ endif
 
 turbostat : turbostat.c
 CFLAGS +=      -Wall
-CFLAGS +=      -DMSRHEADER='"../../../../arch/x86/include/uapi/asm/msr-index.h"'
+CFLAGS +=      -DMSRHEADER='"../../../../arch/x86/include/asm/msr-index.h"'
 
 %: %.c
        @mkdir -p $(BUILD_OUTPUT)
index 5ad042345ab9b66ea2e884824ba407897cb56833..03ca2e64b3fcd291c58311848c18794b5514d1d9 100644 (file)
@@ -12,7 +12,7 @@ CFLAGS := -Wall -O2 -flto -Wall -Werror -DGIT_VERSION='"$(GIT_VERSION)"' -I$(CUR
 
 export CFLAGS
 
-SUB_DIRS = pmu copyloops mm tm primitives stringloops vphn switch_endian
+SUB_DIRS = pmu copyloops mm tm primitives stringloops vphn switch_endian dscr
 
 endif
 
diff --git a/tools/testing/selftests/powerpc/dscr/.gitignore b/tools/testing/selftests/powerpc/dscr/.gitignore
new file mode 100644 (file)
index 0000000..b585c6c
--- /dev/null
@@ -0,0 +1,7 @@
+dscr_default_test
+dscr_explicit_test
+dscr_inherit_exec_test
+dscr_inherit_test
+dscr_sysfs_test
+dscr_sysfs_thread_test
+dscr_user_test
diff --git a/tools/testing/selftests/powerpc/dscr/Makefile b/tools/testing/selftests/powerpc/dscr/Makefile
new file mode 100644 (file)
index 0000000..49327ee
--- /dev/null
@@ -0,0 +1,14 @@
+TEST_PROGS := dscr_default_test dscr_explicit_test dscr_user_test      \
+             dscr_inherit_test dscr_inherit_exec_test dscr_sysfs_test  \
+             dscr_sysfs_thread_test
+
+dscr_default_test: LDLIBS += -lpthread
+
+all: $(TEST_PROGS)
+
+$(TEST_PROGS): ../harness.c
+
+include ../../lib.mk
+
+clean:
+       rm -f $(TEST_PROGS) *.o
diff --git a/tools/testing/selftests/powerpc/dscr/dscr.h b/tools/testing/selftests/powerpc/dscr/dscr.h
new file mode 100644 (file)
index 0000000..a36af1b
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * POWER Data Stream Control Register (DSCR)
+ *
+ * This header file contains helper functions and macros
+ * required for all the DSCR related test cases.
+ *
+ * Copyright 2012, Anton Blanchard, IBM Corporation.
+ * Copyright 2015, Anshuman Khandual, IBM Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+#ifndef _SELFTESTS_POWERPC_DSCR_DSCR_H
+#define _SELFTESTS_POWERPC_DSCR_DSCR_H
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <pthread.h>
+#include <sched.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#include "utils.h"
+
+#define SPRN_DSCR      0x11    /* Privilege state SPR */
+#define SPRN_DSCR_USR  0x03    /* Problem state SPR */
+#define THREADS                100     /* Max threads */
+#define COUNT          100     /* Max iterations */
+#define DSCR_MAX       16      /* Max DSCR value */
+#define LEN_MAX                100     /* Max name length */
+
+#define DSCR_DEFAULT   "/sys/devices/system/cpu/dscr_default"
+#define CPU_PATH       "/sys/devices/system/cpu/"
+
+#define rmb()  asm volatile("lwsync":::"memory")
+#define wmb()  asm volatile("lwsync":::"memory")
+
+#define ACCESS_ONCE(x) (*(volatile typeof(x) *)&(x))
+
+/* Prilvilege state DSCR access */
+inline unsigned long get_dscr(void)
+{
+       unsigned long ret;
+
+       asm volatile("mfspr %0,%1" : "=r" (ret): "i" (SPRN_DSCR));
+
+       return ret;
+}
+
+inline void set_dscr(unsigned long val)
+{
+       asm volatile("mtspr %1,%0" : : "r" (val), "i" (SPRN_DSCR));
+}
+
+/* Problem state DSCR access */
+inline unsigned long get_dscr_usr(void)
+{
+       unsigned long ret;
+
+       asm volatile("mfspr %0,%1" : "=r" (ret): "i" (SPRN_DSCR_USR));
+
+       return ret;
+}
+
+inline void set_dscr_usr(unsigned long val)
+{
+       asm volatile("mtspr %1,%0" : : "r" (val), "i" (SPRN_DSCR_USR));
+}
+
+/* Default DSCR access */
+unsigned long get_default_dscr(void)
+{
+       int fd = -1, ret;
+       char buf[16];
+       unsigned long val;
+
+       if (fd == -1) {
+               fd = open(DSCR_DEFAULT, O_RDONLY);
+               if (fd == -1) {
+                       perror("open() failed");
+                       exit(1);
+               }
+       }
+       memset(buf, 0, sizeof(buf));
+       lseek(fd, 0, SEEK_SET);
+       ret = read(fd, buf, sizeof(buf));
+       if (ret == -1) {
+               perror("read() failed");
+               exit(1);
+       }
+       sscanf(buf, "%lx", &val);
+       close(fd);
+       return val;
+}
+
+void set_default_dscr(unsigned long val)
+{
+       int fd = -1, ret;
+       char buf[16];
+
+       if (fd == -1) {
+               fd = open(DSCR_DEFAULT, O_RDWR);
+               if (fd == -1) {
+                       perror("open() failed");
+                       exit(1);
+               }
+       }
+       sprintf(buf, "%lx\n", val);
+       ret = write(fd, buf, strlen(buf));
+       if (ret == -1) {
+               perror("write() failed");
+               exit(1);
+       }
+       close(fd);
+}
+
+double uniform_deviate(int seed)
+{
+       return seed * (1.0 / (RAND_MAX + 1.0));
+}
+#endif /* _SELFTESTS_POWERPC_DSCR_DSCR_H */
diff --git a/tools/testing/selftests/powerpc/dscr/dscr_default_test.c b/tools/testing/selftests/powerpc/dscr/dscr_default_test.c
new file mode 100644 (file)
index 0000000..df17c3b
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * POWER Data Stream Control Register (DSCR) default test
+ *
+ * This test modifies the system wide default DSCR through
+ * it's sysfs interface and then verifies that all threads
+ * see the correct changed DSCR value immediately.
+ *
+ * Copyright 2012, Anton Blanchard, IBM Corporation.
+ * Copyright 2015, Anshuman Khandual, IBM Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+#include "dscr.h"
+
+static unsigned long dscr;             /* System DSCR default */
+static unsigned long sequence;
+static unsigned long result[THREADS];
+
+static void *do_test(void *in)
+{
+       unsigned long thread = (unsigned long)in;
+       unsigned long i;
+
+       for (i = 0; i < COUNT; i++) {
+               unsigned long d, cur_dscr, cur_dscr_usr;
+               unsigned long s1, s2;
+
+               s1 = ACCESS_ONCE(sequence);
+               if (s1 & 1)
+                       continue;
+               rmb();
+
+               d = dscr;
+               cur_dscr = get_dscr();
+               cur_dscr_usr = get_dscr_usr();
+
+               rmb();
+               s2 = sequence;
+
+               if (s1 != s2)
+                       continue;
+
+               if (cur_dscr != d) {
+                       fprintf(stderr, "thread %ld kernel DSCR should be %ld "
+                               "but is %ld\n", thread, d, cur_dscr);
+                       result[thread] = 1;
+                       pthread_exit(&result[thread]);
+               }
+
+               if (cur_dscr_usr != d) {
+                       fprintf(stderr, "thread %ld user DSCR should be %ld "
+                               "but is %ld\n", thread, d, cur_dscr_usr);
+                       result[thread] = 1;
+                       pthread_exit(&result[thread]);
+               }
+       }
+       result[thread] = 0;
+       pthread_exit(&result[thread]);
+}
+
+int dscr_default(void)
+{
+       pthread_t threads[THREADS];
+       unsigned long i, *status[THREADS];
+       unsigned long orig_dscr_default;
+
+       orig_dscr_default = get_default_dscr();
+
+       /* Initial DSCR default */
+       dscr = 1;
+       set_default_dscr(dscr);
+
+       /* Spawn all testing threads */
+       for (i = 0; i < THREADS; i++) {
+               if (pthread_create(&threads[i], NULL, do_test, (void *)i)) {
+                       perror("pthread_create() failed");
+                       goto fail;
+               }
+       }
+
+       srand(getpid());
+
+       /* Keep changing the DSCR default */
+       for (i = 0; i < COUNT; i++) {
+               double ret = uniform_deviate(rand());
+
+               if (ret < 0.0001) {
+                       sequence++;
+                       wmb();
+
+                       dscr++;
+                       if (dscr > DSCR_MAX)
+                               dscr = 0;
+
+                       set_default_dscr(dscr);
+
+                       wmb();
+                       sequence++;
+               }
+       }
+
+       /* Individual testing thread exit status */
+       for (i = 0; i < THREADS; i++) {
+               if (pthread_join(threads[i], (void **)&(status[i]))) {
+                       perror("pthread_join() failed");
+                       goto fail;
+               }
+
+               if (*status[i]) {
+                       printf("%ldth thread failed to join with %ld status\n",
+                                                               i, *status[i]);
+                       goto fail;
+               }
+       }
+       set_default_dscr(orig_dscr_default);
+       return 0;
+fail:
+       set_default_dscr(orig_dscr_default);
+       return 1;
+}
+
+int main(int argc, char *argv[])
+{
+       return test_harness(dscr_default, "dscr_default_test");
+}
diff --git a/tools/testing/selftests/powerpc/dscr/dscr_explicit_test.c b/tools/testing/selftests/powerpc/dscr/dscr_explicit_test.c
new file mode 100644 (file)
index 0000000..ad9c3ec
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * POWER Data Stream Control Register (DSCR) explicit test
+ *
+ * This test modifies the DSCR value using mtspr instruction and
+ * verifies the change with mfspr instruction. It uses both the
+ * privilege state SPR and the problem state SPR for this purpose.
+ *
+ * When using the privilege state SPR, the instructions such as
+ * mfspr or mtspr are priviledged and the kernel emulates them
+ * for us. Instructions using problem state SPR can be exuecuted
+ * directly without any emulation if the HW supports them. Else
+ * they also get emulated by the kernel.
+ *
+ * Copyright 2012, Anton Blanchard, IBM Corporation.
+ * Copyright 2015, Anshuman Khandual, IBM Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+#include "dscr.h"
+
+int dscr_explicit(void)
+{
+       unsigned long i, dscr = 0;
+
+       srand(getpid());
+       set_dscr(dscr);
+
+       for (i = 0; i < COUNT; i++) {
+               unsigned long cur_dscr, cur_dscr_usr;
+               double ret = uniform_deviate(rand());
+
+               if (ret < 0.001) {
+                       dscr++;
+                       if (dscr > DSCR_MAX)
+                               dscr = 0;
+
+                       set_dscr(dscr);
+               }
+
+               cur_dscr = get_dscr();
+               if (cur_dscr != dscr) {
+                       fprintf(stderr, "Kernel DSCR should be %ld but "
+                                       "is %ld\n", dscr, cur_dscr);
+                       return 1;
+               }
+
+               ret = uniform_deviate(rand());
+               if (ret < 0.001) {
+                       dscr++;
+                       if (dscr > DSCR_MAX)
+                               dscr = 0;
+
+                       set_dscr_usr(dscr);
+               }
+
+               cur_dscr_usr = get_dscr_usr();
+               if (cur_dscr_usr != dscr) {
+                       fprintf(stderr, "User DSCR should be %ld but "
+                                       "is %ld\n", dscr, cur_dscr_usr);
+                       return 1;
+               }
+       }
+       return 0;
+}
+
+int main(int argc, char *argv[])
+{
+       return test_harness(dscr_explicit, "dscr_explicit_test");
+}
diff --git a/tools/testing/selftests/powerpc/dscr/dscr_inherit_exec_test.c b/tools/testing/selftests/powerpc/dscr/dscr_inherit_exec_test.c
new file mode 100644 (file)
index 0000000..8265504
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ * POWER Data Stream Control Register (DSCR) fork exec test
+ *
+ * This testcase modifies the DSCR using mtspr, forks & execs and
+ * verifies that the child is using the changed DSCR using mfspr.
+ *
+ * When using the privilege state SPR, the instructions such as
+ * mfspr or mtspr are priviledged and the kernel emulates them
+ * for us. Instructions using problem state SPR can be exuecuted
+ * directly without any emulation if the HW supports them. Else
+ * they also get emulated by the kernel.
+ *
+ * Copyright 2012, Anton Blanchard, IBM Corporation.
+ * Copyright 2015, Anshuman Khandual, IBM Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+#include "dscr.h"
+
+static char prog[LEN_MAX];
+
+static void do_exec(unsigned long parent_dscr)
+{
+       unsigned long cur_dscr, cur_dscr_usr;
+
+       cur_dscr = get_dscr();
+       cur_dscr_usr = get_dscr_usr();
+
+       if (cur_dscr != parent_dscr) {
+               fprintf(stderr, "Parent DSCR %ld was not inherited "
+                               "over exec (kernel value)\n", parent_dscr);
+               exit(1);
+       }
+
+       if (cur_dscr_usr != parent_dscr) {
+               fprintf(stderr, "Parent DSCR %ld was not inherited "
+                               "over exec (user value)\n", parent_dscr);
+               exit(1);
+       }
+       exit(0);
+}
+
+int dscr_inherit_exec(void)
+{
+       unsigned long i, dscr = 0;
+       pid_t pid;
+
+       for (i = 0; i < COUNT; i++) {
+               dscr++;
+               if (dscr > DSCR_MAX)
+                       dscr = 0;
+
+               if (dscr == get_default_dscr())
+                       continue;
+
+               if (i % 2 == 0)
+                       set_dscr_usr(dscr);
+               else
+                       set_dscr(dscr);
+
+               /*
+                * XXX: Force a context switch out so that DSCR
+                * current value is copied into the thread struct
+                * which is required for the child to inherit the
+                * changed value.
+                */
+               sleep(1);
+
+               pid = fork();
+               if (pid == -1) {
+                       perror("fork() failed");
+                       exit(1);
+               } else if (pid) {
+                       int status;
+
+                       if (waitpid(pid, &status, 0) == -1) {
+                               perror("waitpid() failed");
+                               exit(1);
+                       }
+
+                       if (!WIFEXITED(status)) {
+                               fprintf(stderr, "Child didn't exit cleanly\n");
+                               exit(1);
+                       }
+
+                       if (WEXITSTATUS(status) != 0) {
+                               fprintf(stderr, "Child didn't exit cleanly\n");
+                               return 1;
+                       }
+               } else {
+                       char dscr_str[16];
+
+                       sprintf(dscr_str, "%ld", dscr);
+                       execlp(prog, prog, "exec", dscr_str, NULL);
+                       exit(1);
+               }
+       }
+       return 0;
+}
+
+int main(int argc, char *argv[])
+{
+       if (argc == 3 && !strcmp(argv[1], "exec")) {
+               unsigned long parent_dscr;
+
+               parent_dscr = atoi(argv[2]);
+               do_exec(parent_dscr);
+       } else if (argc != 1) {
+               fprintf(stderr, "Usage: %s\n", argv[0]);
+               exit(1);
+       }
+
+       strncpy(prog, argv[0], strlen(argv[0]));
+       return test_harness(dscr_inherit_exec, "dscr_inherit_exec_test");
+}
diff --git a/tools/testing/selftests/powerpc/dscr/dscr_inherit_test.c b/tools/testing/selftests/powerpc/dscr/dscr_inherit_test.c
new file mode 100644 (file)
index 0000000..4e414ca
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * POWER Data Stream Control Register (DSCR) fork test
+ *
+ * This testcase modifies the DSCR using mtspr, forks and then
+ * verifies that the child process has the correct changed DSCR
+ * value using mfspr.
+ *
+ * When using the privilege state SPR, the instructions such as
+ * mfspr or mtspr are priviledged and the kernel emulates them
+ * for us. Instructions using problem state SPR can be exuecuted
+ * directly without any emulation if the HW supports them. Else
+ * they also get emulated by the kernel.
+ *
+ * Copyright 2012, Anton Blanchard, IBM Corporation.
+ * Copyright 2015, Anshuman Khandual, IBM Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+#include "dscr.h"
+
+int dscr_inherit(void)
+{
+       unsigned long i, dscr = 0;
+       pid_t pid;
+
+       srand(getpid());
+       set_dscr(dscr);
+
+       for (i = 0; i < COUNT; i++) {
+               unsigned long cur_dscr, cur_dscr_usr;
+
+               dscr++;
+               if (dscr > DSCR_MAX)
+                       dscr = 0;
+
+               if (i % 2 == 0)
+                       set_dscr_usr(dscr);
+               else
+                       set_dscr(dscr);
+
+               /*
+                * XXX: Force a context switch out so that DSCR
+                * current value is copied into the thread struct
+                * which is required for the child to inherit the
+                * changed value.
+                */
+               sleep(1);
+
+               pid = fork();
+               if (pid == -1) {
+                       perror("fork() failed");
+                       exit(1);
+               } else if (pid) {
+                       int status;
+
+                       if (waitpid(pid, &status, 0) == -1) {
+                               perror("waitpid() failed");
+                               exit(1);
+                       }
+
+                       if (!WIFEXITED(status)) {
+                               fprintf(stderr, "Child didn't exit cleanly\n");
+                               exit(1);
+                       }
+
+                       if (WEXITSTATUS(status) != 0) {
+                               fprintf(stderr, "Child didn't exit cleanly\n");
+                               return 1;
+                       }
+               } else {
+                       cur_dscr = get_dscr();
+                       if (cur_dscr != dscr) {
+                               fprintf(stderr, "Kernel DSCR should be %ld "
+                                       "but is %ld\n", dscr, cur_dscr);
+                               exit(1);
+                       }
+
+                       cur_dscr_usr = get_dscr_usr();
+                       if (cur_dscr_usr != dscr) {
+                               fprintf(stderr, "User DSCR should be %ld "
+                                       "but is %ld\n", dscr, cur_dscr_usr);
+                               exit(1);
+                       }
+                       exit(0);
+               }
+       }
+       return 0;
+}
+
+int main(int argc, char *argv[])
+{
+       return test_harness(dscr_inherit, "dscr_inherit_test");
+}
diff --git a/tools/testing/selftests/powerpc/dscr/dscr_sysfs_test.c b/tools/testing/selftests/powerpc/dscr/dscr_sysfs_test.c
new file mode 100644 (file)
index 0000000..17fb1b4
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * POWER Data Stream Control Register (DSCR) sysfs interface test
+ *
+ * This test updates to system wide DSCR default through the sysfs interface
+ * and then verifies that all the CPU specific DSCR defaults are updated as
+ * well verified from their sysfs interfaces.
+ *
+ * Copyright 2015, Anshuman Khandual, IBM Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+#include "dscr.h"
+
+static int check_cpu_dscr_default(char *file, unsigned long val)
+{
+       char buf[10];
+       int fd, rc;
+
+       fd = open(file, O_RDWR);
+       if (fd == -1) {
+               perror("open() failed");
+               return 1;
+       }
+
+       rc = read(fd, buf, sizeof(buf));
+       if (rc == -1) {
+               perror("read() failed");
+               return 1;
+       }
+       close(fd);
+
+       buf[rc] = '\0';
+       if (strtol(buf, NULL, 16) != val) {
+               printf("DSCR match failed: %ld (system) %ld (cpu)\n",
+                                       val, strtol(buf, NULL, 16));
+               return 1;
+       }
+       return 0;
+}
+
+static int check_all_cpu_dscr_defaults(unsigned long val)
+{
+       DIR *sysfs;
+       struct dirent *dp;
+       char file[LEN_MAX];
+
+       sysfs = opendir(CPU_PATH);
+       if (!sysfs) {
+               perror("opendir() failed");
+               return 1;
+       }
+
+       while ((dp = readdir(sysfs))) {
+               if (!(dp->d_type & DT_DIR))
+                       continue;
+               if (!strcmp(dp->d_name, "cpuidle"))
+                       continue;
+               if (!strstr(dp->d_name, "cpu"))
+                       continue;
+
+               sprintf(file, "%s%s/dscr", CPU_PATH, dp->d_name);
+               if (access(file, F_OK))
+                       continue;
+
+               if (check_cpu_dscr_default(file, val))
+                       return 1;
+       }
+       closedir(sysfs);
+       return 0;
+}
+
+int dscr_sysfs(void)
+{
+       unsigned long orig_dscr_default;
+       int i, j;
+
+       orig_dscr_default = get_default_dscr();
+       for (i = 0; i < COUNT; i++) {
+               for (j = 0; j < DSCR_MAX; j++) {
+                       set_default_dscr(j);
+                       if (check_all_cpu_dscr_defaults(j))
+                               goto fail;
+               }
+       }
+       set_default_dscr(orig_dscr_default);
+       return 0;
+fail:
+       set_default_dscr(orig_dscr_default);
+       return 1;
+}
+
+int main(int argc, char *argv[])
+{
+       return test_harness(dscr_sysfs, "dscr_sysfs_test");
+}
diff --git a/tools/testing/selftests/powerpc/dscr/dscr_sysfs_thread_test.c b/tools/testing/selftests/powerpc/dscr/dscr_sysfs_thread_test.c
new file mode 100644 (file)
index 0000000..ad97b59
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * POWER Data Stream Control Register (DSCR) sysfs thread test
+ *
+ * This test updates the system wide DSCR default value through
+ * sysfs interface which should then update all the CPU specific
+ * DSCR default values which must also be then visible to threads
+ * executing on individual CPUs on the system.
+ *
+ * Copyright 2015, Anshuman Khandual, IBM Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+#define _GNU_SOURCE
+#include "dscr.h"
+
+static int test_thread_dscr(unsigned long val)
+{
+       unsigned long cur_dscr, cur_dscr_usr;
+
+       cur_dscr = get_dscr();
+       cur_dscr_usr = get_dscr_usr();
+
+       if (val != cur_dscr) {
+               printf("[cpu %d] Kernel DSCR should be %ld but is %ld\n",
+                                       sched_getcpu(), val, cur_dscr);
+               return 1;
+       }
+
+       if (val != cur_dscr_usr) {
+               printf("[cpu %d] User DSCR should be %ld but is %ld\n",
+                                       sched_getcpu(), val, cur_dscr_usr);
+               return 1;
+       }
+       return 0;
+}
+
+static int check_cpu_dscr_thread(unsigned long val)
+{
+       cpu_set_t mask;
+       int cpu;
+
+       for (cpu = 0; cpu < CPU_SETSIZE; cpu++) {
+               CPU_ZERO(&mask);
+               CPU_SET(cpu, &mask);
+               if (sched_setaffinity(0, sizeof(mask), &mask))
+                       continue;
+
+               if (test_thread_dscr(val))
+                       return 1;
+       }
+       return 0;
+
+}
+
+int dscr_sysfs_thread(void)
+{
+       unsigned long orig_dscr_default;
+       int i, j;
+
+       orig_dscr_default = get_default_dscr();
+       for (i = 0; i < COUNT; i++) {
+               for (j = 0; j < DSCR_MAX; j++) {
+                       set_default_dscr(j);
+                       if (check_cpu_dscr_thread(j))
+                               goto fail;
+               }
+       }
+       set_default_dscr(orig_dscr_default);
+       return 0;
+fail:
+       set_default_dscr(orig_dscr_default);
+       return 1;
+}
+
+int main(int argc, char *argv[])
+{
+       return test_harness(dscr_sysfs_thread, "dscr_sysfs_thread_test");
+}
diff --git a/tools/testing/selftests/powerpc/dscr/dscr_user_test.c b/tools/testing/selftests/powerpc/dscr/dscr_user_test.c
new file mode 100644 (file)
index 0000000..77d16b5
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * POWER Data Stream Control Register (DSCR) SPR test
+ *
+ * This test modifies the DSCR value through both the SPR number
+ * based mtspr instruction and then makes sure that the same is
+ * reflected through mfspr instruction using either of the SPR
+ * numbers.
+ *
+ * When using the privilege state SPR, the instructions such as
+ * mfspr or mtspr are priviledged and the kernel emulates them
+ * for us. Instructions using problem state SPR can be exuecuted
+ * directly without any emulation if the HW supports them. Else
+ * they also get emulated by the kernel.
+ *
+ * Copyright 2013, Anton Blanchard, IBM Corporation.
+ * Copyright 2015, Anshuman Khandual, IBM Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+#include "dscr.h"
+
+static int check_dscr(char *str)
+{
+       unsigned long cur_dscr, cur_dscr_usr;
+
+       cur_dscr = get_dscr();
+       cur_dscr_usr = get_dscr_usr();
+       if (cur_dscr != cur_dscr_usr) {
+               printf("%s set, kernel get %lx != user get %lx\n",
+                                       str, cur_dscr, cur_dscr_usr);
+               return 1;
+       }
+       return 0;
+}
+
+int dscr_user(void)
+{
+       int i;
+
+       check_dscr("");
+
+       for (i = 0; i < COUNT; i++) {
+               set_dscr(i);
+               if (check_dscr("kernel"))
+                       return 1;
+       }
+
+       for (i = 0; i < COUNT; i++) {
+               set_dscr_usr(i);
+               if (check_dscr("user"))
+                       return 1;
+       }
+       return 0;
+}
+
+int main(int argc, char *argv[])
+{
+       return test_harness(dscr_user, "dscr_user_test");
+}
index 081473db22b779759b1fec4e809570510a0a322e..e21d10674e54135637b540ae8b0457e0ea3075cd 100644 (file)
@@ -1,9 +1,8 @@
-CC := $(CROSS_COMPILE)gcc
-PROGS := switch_endian_test
+TEST_PROGS := switch_endian_test
 
 ASFLAGS += -O2 -Wall -g -nostdlib -m64
 
-all: $(PROGS)
+all: $(TEST_PROGS)
 
 switch_endian_test: check-reversed.S
 
@@ -13,12 +12,7 @@ check-reversed.o: check.o
 check-reversed.S: check-reversed.o
        hexdump -v -e '/1 ".byte 0x%02X\n"' $< > $@
 
-run_tests: all
-       @-for PROG in $(PROGS); do \
-               ./$$PROG; \
-       done;
+include ../../lib.mk
 
 clean:
-       rm -f $(PROGS) *.o check-reversed.S
-
-.PHONY: all run_tests clean
+       rm -f $(TEST_PROGS) *.o check-reversed.S
index 6bff955e1d55ac6cccb526f5a00355ac4e904973..4bea62a319dcaf96ee85f58aaa3cf6160d0428d5 100644 (file)
@@ -1,11 +1,11 @@
-TEST_PROGS := tm-resched-dscr
+TEST_PROGS := tm-resched-dscr tm-syscall
 
 all: $(TEST_PROGS)
 
 $(TEST_PROGS): ../harness.c
 
 tm-syscall: tm-syscall-asm.S
-tm-syscall: CFLAGS += -mhtm
+tm-syscall: CFLAGS += -mhtm -I../../../../../usr/include
 
 include ../../lib.mk
 
index 3ed8d4b252fac440b6dd2db56a84a7fcf330fe0c..1276e23da63bbdf91d328472f581e32e8979a0fa 100644 (file)
@@ -82,7 +82,8 @@ int tm_syscall(void)
        unsigned count = 0;
        struct timeval end, now;
 
-       SKIP_IF(!((long)get_auxv_entry(AT_HWCAP2) & PPC_FEATURE2_HTM));
+       SKIP_IF(!((long)get_auxv_entry(AT_HWCAP2)
+                 & PPC_FEATURE2_HTM_NOSC));
        setbuf(stdout, NULL);
 
        printf("Testing transactional syscalls for %d seconds...\n", TEST_DURATION);
index e539f775fd8f0a7459f0df0bb33b7bb5de5cb6e5..a485f2e286ae22cae0706c7cd0fc735d4c7206e3 100644 (file)
@@ -1,15 +1,12 @@
-PROG := test-vphn
+TEST_PROGS := test-vphn
 
 CFLAGS += -m64
 
-all: $(PROG)
+all: $(TEST_PROGS)
 
-$(PROG): ../harness.c
+$(TEST_PROGS): ../harness.c
 
-run_tests: all
-       ./$(PROG)
+include ../../lib.mk
 
 clean:
-       rm -f $(PROG)
-
-.PHONY: all run_tests clean
+       rm -f $(TEST_PROGS)
index 15f1a17ca96e69695f5f266cc58198102ba1a0c9..3f81a109520693ec6ebac0abe78c73388b8fc334 100755 (executable)
@@ -66,7 +66,7 @@ make $buildloc $TORTURE_DEFCONFIG > $builddir/Make.defconfig.out 2>&1
 mv $builddir/.config $builddir/.config.sav
 sh $T/upd.sh < $builddir/.config.sav > $builddir/.config
 cp $builddir/.config $builddir/.config.new
-yes '' | make $buildloc oldconfig > $builddir/Make.modconfig.out 2>&1
+yes '' | make $buildloc oldconfig > $builddir/Make.oldconfig.out 2> $builddir/Make.oldconfig.err
 
 # verify new config matches specification.
 configcheck.sh $builddir/.config $c
index 4f5b20f367a944f07f0443a480dd79d061031d87..d86bdd6b6cc2df3148adf7bacff4c6a014edc3cc 100755 (executable)
@@ -43,6 +43,10 @@ do
                if test -f "$i/console.log"
                then
                        configcheck.sh $i/.config $i/ConfigFragment
+                       if test -r $i/Make.oldconfig.err
+                       then
+                               cat $i/Make.oldconfig.err
+                       fi
                        parse-build.sh $i/Make.out $configfile
                        parse-torture.sh $i/console.log $configfile
                        parse-console.sh $i/console.log $configfile
index dd2812ceb0baba2bb4f39343e428582e9619179f..fbe2dbff1e210c2f4711d2f581823d6b14c85799 100755 (executable)
@@ -55,7 +55,7 @@ usage () {
        echo "       --bootargs kernel-boot-arguments"
        echo "       --bootimage relative-path-to-kernel-boot-image"
        echo "       --buildonly"
-       echo "       --configs \"config-file list\""
+       echo "       --configs \"config-file list w/ repeat factor (3*TINY01)\""
        echo "       --cpus N"
        echo "       --datestamp string"
        echo "       --defconfig string"
@@ -178,13 +178,26 @@ fi
 touch $T/cfgcpu
 for CF in $configs
 do
-       if test -f "$CONFIGFRAG/$CF"
+       case $CF in
+       [0-9]\**|[0-9][0-9]\**|[0-9][0-9][0-9]\**)
+               config_reps=`echo $CF | sed -e 's/\*.*$//'`
+               CF1=`echo $CF | sed -e 's/^[^*]*\*//'`
+               ;;
+       *)
+               config_reps=1
+               CF1=$CF
+               ;;
+       esac
+       if test -f "$CONFIGFRAG/$CF1"
        then
-               cpu_count=`configNR_CPUS.sh $CONFIGFRAG/$CF`
-               cpu_count=`configfrag_boot_cpus "$TORTURE_BOOTARGS" "$CONFIGFRAG/$CF" "$cpu_count"`
-               echo $CF $cpu_count >> $T/cfgcpu
+               cpu_count=`configNR_CPUS.sh $CONFIGFRAG/$CF1`
+               cpu_count=`configfrag_boot_cpus "$TORTURE_BOOTARGS" "$CONFIGFRAG/$CF1" "$cpu_count"`
+               for ((cur_rep=0;cur_rep<$config_reps;cur_rep++))
+               do
+                       echo $CF1 $cpu_count >> $T/cfgcpu
+               done
        else
-               echo "The --configs file $CF does not exist, terminating."
+               echo "The --configs file $CF1 does not exist, terminating."
                exit 1
        fi
 done
index 49701218dc620481ff32344863411413e208da66..f824b4c9d9d9132d9681c5fe51c26caa76d4d8de 100644 (file)
@@ -1,3 +1,5 @@
 CONFIG_RCU_TORTURE_TEST=y
 CONFIG_PRINTK_TIME=y
+CONFIG_RCU_TORTURE_TEST_SLOW_CLEANUP=y
 CONFIG_RCU_TORTURE_TEST_SLOW_INIT=y
+CONFIG_RCU_TORTURE_TEST_SLOW_PREINIT=y
index 9fbb41b9b3149ed62cb0e97aabe6731d7bdc3c6c..1a087c3c8bb861584c069142bfe1fd8b02415956 100644 (file)
@@ -5,3 +5,4 @@ CONFIG_HOTPLUG_CPU=y
 CONFIG_PREEMPT_NONE=y
 CONFIG_PREEMPT_VOLUNTARY=n
 CONFIG_PREEMPT=n
+CONFIG_RCU_EXPERT=y
index 4b6f272dba27f8483f45c99a4a28f419e1b9d69a..4837430a71c0c3456979eb02a635ac0b9a3e1318 100644 (file)
@@ -5,3 +5,4 @@ CONFIG_HOTPLUG_CPU=y
 CONFIG_PREEMPT_NONE=n
 CONFIG_PREEMPT_VOLUNTARY=n
 CONFIG_PREEMPT=y
+#CHECK#CONFIG_RCU_EXPERT=n
index 238bfe3bd0cccf5096cace6c1012dac22d111728..84a7d51b7481e7a4173b0e87f9d46b51b65ef1eb 100644 (file)
@@ -1 +1 @@
-rcutorture.torture_type=srcu
+rcutorture.torture_type=srcud
index 97f0a0b27ef7293ed4cb9fa364fbee81629807c2..2cc0e60eba6eed66c1ebf6f5d0c85a19b0564ec3 100644 (file)
@@ -5,5 +5,6 @@ CONFIG_PREEMPT_NONE=n
 CONFIG_PREEMPT_VOLUNTARY=n
 CONFIG_PREEMPT=y
 CONFIG_DEBUG_LOCK_ALLOC=y
-CONFIG_PROVE_RCU=y
-CONFIG_TASKS_RCU=y
+CONFIG_PROVE_LOCKING=n
+#CHECK#CONFIG_PROVE_RCU=n
+CONFIG_RCU_EXPERT=y
index 696d2ea74d13bb92ba91ed45a0f0df951d7234b9..ad2be91e5ee7624e95df63885f70dbbc833afb64 100644 (file)
@@ -2,4 +2,3 @@ CONFIG_SMP=n
 CONFIG_PREEMPT_NONE=y
 CONFIG_PREEMPT_VOLUNTARY=n
 CONFIG_PREEMPT=n
-CONFIG_TASKS_RCU=y
index 9c60da5b5d1ddae021ef28ed3b5f5a008186dc11..c70c51d5ded15af901e1a2c267b144a9cc88ba4d 100644 (file)
@@ -6,8 +6,8 @@ CONFIG_HIBERNATION=n
 CONFIG_PREEMPT_NONE=n
 CONFIG_PREEMPT_VOLUNTARY=n
 CONFIG_PREEMPT=y
-CONFIG_TASKS_RCU=y
 CONFIG_HZ_PERIODIC=n
 CONFIG_NO_HZ_IDLE=n
 CONFIG_NO_HZ_FULL=y
 CONFIG_NO_HZ_FULL_ALL=y
+#CHECK#CONFIG_RCU_EXPERT=n
index 36e41df3d27aa6ad7abdf56dba609fd51bb1a9b1..f1892e0371c954bd5cfc800a6b9cc87297e9433e 100644 (file)
@@ -8,7 +8,7 @@ CONFIG_NO_HZ_IDLE=n
 CONFIG_NO_HZ_FULL=n
 CONFIG_RCU_TRACE=y
 CONFIG_PROVE_LOCKING=y
-CONFIG_PROVE_RCU=y
+#CHECK#CONFIG_PROVE_RCU=y
 CONFIG_DEBUG_LOCK_ALLOC=y
 CONFIG_DEBUG_OBJECTS_RCU_HEAD=n
 CONFIG_PREEMPT_COUNT=y
index 0f0802730014c675b6f830601d4aed340aa1f4d3..6c1a292a65fb499968bd2495fa046aedb02b39f8 100644 (file)
@@ -1,2 +1,3 @@
 rcupdate.rcu_self_test=1
 rcupdate.rcu_self_test_bh=1
+rcutorture.torture_type=rcu_bh
index f8a10a7500c64f057987e3d5e3ab765d322973fb..8e9137f66831f2fa6f7ea1c2de7aba6fd8fa99c2 100644 (file)
@@ -16,3 +16,4 @@ CONFIG_DEBUG_LOCK_ALLOC=n
 CONFIG_RCU_CPU_STALL_INFO=n
 CONFIG_RCU_BOOST=n
 CONFIG_DEBUG_OBJECTS_RCU_HEAD=n
+CONFIG_RCU_EXPERT=y
index 629122fb8b4a12323e800f09453a3a06718927d8..aeea6a204d14b1ea87737d6d5024f75668f6dd68 100644 (file)
@@ -14,10 +14,10 @@ CONFIG_SUSPEND=n
 CONFIG_HIBERNATION=n
 CONFIG_RCU_FANOUT=3
 CONFIG_RCU_FANOUT_LEAF=3
-CONFIG_RCU_FANOUT_EXACT=n
 CONFIG_RCU_NOCB_CPU=n
 CONFIG_DEBUG_LOCK_ALLOC=y
 CONFIG_PROVE_LOCKING=n
 CONFIG_RCU_CPU_STALL_INFO=n
 CONFIG_RCU_BOOST=n
 CONFIG_DEBUG_OBJECTS_RCU_HEAD=n
+CONFIG_RCU_EXPERT=y
index a25de47888a4fc11371adc7bc99a93fe2dfcf8d6..2ac9e68ea3d1481764b82eaba427039ed45fa992 100644 (file)
@@ -14,7 +14,6 @@ CONFIG_SUSPEND=n
 CONFIG_HIBERNATION=n
 CONFIG_RCU_FANOUT=3
 CONFIG_RCU_FANOUT_LEAF=3
-CONFIG_RCU_FANOUT_EXACT=n
 CONFIG_RCU_NOCB_CPU=n
 CONFIG_DEBUG_LOCK_ALLOC=y
 CONFIG_PROVE_LOCKING=n
index 53f24e0a0ab618c8470e1727ecec0a8d78ce5db3..72aa7d87ea99159db02b4f07976abdc99da3e71b 100644 (file)
@@ -1,5 +1,5 @@
 CONFIG_SMP=y
-CONFIG_NR_CPUS=8
+CONFIG_NR_CPUS=16
 CONFIG_PREEMPT_NONE=n
 CONFIG_PREEMPT_VOLUNTARY=n
 CONFIG_PREEMPT=y
@@ -9,12 +9,12 @@ CONFIG_NO_HZ_IDLE=n
 CONFIG_NO_HZ_FULL=n
 CONFIG_RCU_TRACE=y
 CONFIG_HOTPLUG_CPU=y
-CONFIG_RCU_FANOUT=4
-CONFIG_RCU_FANOUT_LEAF=4
-CONFIG_RCU_FANOUT_EXACT=n
+CONFIG_RCU_FANOUT=2
+CONFIG_RCU_FANOUT_LEAF=2
 CONFIG_RCU_NOCB_CPU=n
 CONFIG_DEBUG_LOCK_ALLOC=n
 CONFIG_RCU_CPU_STALL_INFO=n
 CONFIG_RCU_BOOST=y
 CONFIG_RCU_KTHREAD_PRIO=2
 CONFIG_DEBUG_OBJECTS_RCU_HEAD=n
+CONFIG_RCU_EXPERT=y
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/TREE03.boot b/tools/testing/selftests/rcutorture/configs/rcu/TREE03.boot
new file mode 100644 (file)
index 0000000..120c0c8
--- /dev/null
@@ -0,0 +1 @@
+rcutorture.onoff_interval=1 rcutorture.onoff_holdoff=30
index 0f84db35b36d6221b05a1a25d001f459b0870a08..3f5112751cda0d571af0967395d0b80e18e8543a 100644 (file)
@@ -13,10 +13,10 @@ CONFIG_RCU_TRACE=y
 CONFIG_HOTPLUG_CPU=n
 CONFIG_SUSPEND=n
 CONFIG_HIBERNATION=n
-CONFIG_RCU_FANOUT=2
-CONFIG_RCU_FANOUT_LEAF=2
-CONFIG_RCU_FANOUT_EXACT=n
+CONFIG_RCU_FANOUT=4
+CONFIG_RCU_FANOUT_LEAF=4
 CONFIG_RCU_NOCB_CPU=n
 CONFIG_DEBUG_LOCK_ALLOC=n
-CONFIG_RCU_CPU_STALL_INFO=y
+CONFIG_RCU_CPU_STALL_INFO=n
 CONFIG_DEBUG_OBJECTS_RCU_HEAD=n
+CONFIG_RCU_EXPERT=y
index 212e3bfd2b2ac0b8d21bc261e247600733e3c5cc..c04dfea6fd217e5ddb45cb7b46257b4979454a44 100644 (file)
@@ -12,11 +12,11 @@ CONFIG_RCU_TRACE=n
 CONFIG_HOTPLUG_CPU=y
 CONFIG_RCU_FANOUT=6
 CONFIG_RCU_FANOUT_LEAF=6
-CONFIG_RCU_FANOUT_EXACT=n
 CONFIG_RCU_NOCB_CPU=y
 CONFIG_RCU_NOCB_CPU_NONE=y
 CONFIG_DEBUG_LOCK_ALLOC=y
 CONFIG_PROVE_LOCKING=y
-CONFIG_PROVE_RCU=y
+#CHECK#CONFIG_PROVE_RCU=y
 CONFIG_RCU_CPU_STALL_INFO=n
 CONFIG_DEBUG_OBJECTS_RCU_HEAD=n
+CONFIG_RCU_EXPERT=y
index 7eee63b442181267b6ae4ca0c723af59bcb2ac37..f51d2c73a68ec221f45d26d93e0a2743751447b7 100644 (file)
@@ -14,10 +14,10 @@ CONFIG_SUSPEND=n
 CONFIG_HIBERNATION=n
 CONFIG_RCU_FANOUT=6
 CONFIG_RCU_FANOUT_LEAF=6
-CONFIG_RCU_FANOUT_EXACT=y
 CONFIG_RCU_NOCB_CPU=n
 CONFIG_DEBUG_LOCK_ALLOC=y
 CONFIG_PROVE_LOCKING=y
-CONFIG_PROVE_RCU=y
+#CHECK#CONFIG_PROVE_RCU=y
 CONFIG_RCU_CPU_STALL_INFO=n
 CONFIG_DEBUG_OBJECTS_RCU_HEAD=y
+CONFIG_RCU_EXPERT=y
index da9a03a398db148effea02957c0e52cad42f0224..dd90f28ed700b31e897ef5f2eff1ed577f031d05 100644 (file)
@@ -1,3 +1,4 @@
 rcupdate.rcu_self_test=1
 rcupdate.rcu_self_test_bh=1
 rcupdate.rcu_self_test_sched=1
+rcutree.rcu_fanout_exact=1
index 92a97fa97dec1c4b06dabea9dcbea5e614bad965..f422af4ff5a31bf0c497c7d652b1c0b6e7d30a34 100644 (file)
@@ -15,8 +15,8 @@ CONFIG_RCU_TRACE=y
 CONFIG_HOTPLUG_CPU=y
 CONFIG_RCU_FANOUT=2
 CONFIG_RCU_FANOUT_LEAF=2
-CONFIG_RCU_FANOUT_EXACT=n
 CONFIG_RCU_NOCB_CPU=n
 CONFIG_DEBUG_LOCK_ALLOC=n
-CONFIG_RCU_CPU_STALL_INFO=y
+CONFIG_RCU_CPU_STALL_INFO=n
 CONFIG_DEBUG_OBJECTS_RCU_HEAD=n
+CONFIG_RCU_EXPERT=y
index 5812027d6f9ff043fecdcb2558a0c4befe83b50c..a24d2ca30646c3afe48417cdbb47e085a09d7486 100644 (file)
@@ -1,5 +1,5 @@
 CONFIG_SMP=y
-CONFIG_NR_CPUS=16
+CONFIG_NR_CPUS=8
 CONFIG_PREEMPT_NONE=n
 CONFIG_PREEMPT_VOLUNTARY=n
 CONFIG_PREEMPT=y
@@ -13,13 +13,13 @@ CONFIG_HOTPLUG_CPU=n
 CONFIG_SUSPEND=n
 CONFIG_HIBERNATION=n
 CONFIG_RCU_FANOUT=3
-CONFIG_RCU_FANOUT_EXACT=y
 CONFIG_RCU_FANOUT_LEAF=2
 CONFIG_RCU_NOCB_CPU=y
 CONFIG_RCU_NOCB_CPU_ALL=y
 CONFIG_DEBUG_LOCK_ALLOC=n
 CONFIG_PROVE_LOCKING=y
-CONFIG_PROVE_RCU=y
+#CHECK#CONFIG_PROVE_RCU=y
 CONFIG_RCU_CPU_STALL_INFO=n
 CONFIG_RCU_BOOST=n
 CONFIG_DEBUG_OBJECTS_RCU_HEAD=n
+CONFIG_RCU_EXPERT=y
index 3eaeccacb08389117d7d2ace6e4381049a2e5e57..b2b8cea69dc9935cadf032524c64406a4aa92ef7 100644 (file)
@@ -13,7 +13,6 @@ CONFIG_HOTPLUG_CPU=n
 CONFIG_SUSPEND=n
 CONFIG_HIBERNATION=n
 CONFIG_RCU_FANOUT=3
-CONFIG_RCU_FANOUT_EXACT=y
 CONFIG_RCU_FANOUT_LEAF=2
 CONFIG_RCU_NOCB_CPU=y
 CONFIG_RCU_NOCB_CPU_ALL=y
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/TREE08-T.boot b/tools/testing/selftests/rcutorture/configs/rcu/TREE08-T.boot
new file mode 100644 (file)
index 0000000..883149b
--- /dev/null
@@ -0,0 +1 @@
+rcutree.rcu_fanout_exact=1
index 2561daf605ad5f9a09920e98b18ab451d3ddada3..fb066dc82769fe4f910f4e86ec3c4e1541071adb 100644 (file)
@@ -1,3 +1,4 @@
 rcutorture.torture_type=sched
 rcupdate.rcu_self_test=1
 rcupdate.rcu_self_test_sched=1
+rcutree.rcu_fanout_exact=1
index 6076b36f6c0b452c27ffb0c80e40fdacc4014eca..aa4ed08d999d9dbe8b438e39841ab8093d157c34 100644 (file)
@@ -16,3 +16,4 @@ CONFIG_DEBUG_LOCK_ALLOC=n
 CONFIG_RCU_CPU_STALL_INFO=n
 CONFIG_RCU_BOOST=n
 CONFIG_DEBUG_OBJECTS_RCU_HEAD=n
+#CHECK#CONFIG_RCU_EXPERT=n
index ec03c883db005192c3d5c644ff2595f3e3c6f673..b24c0004fc499868046eee81993caba27b5f1827 100644 (file)
@@ -12,13 +12,12 @@ CONFIG_NO_HZ_IDLE -- Do those not otherwise specified. (Groups of two.)
 CONFIG_NO_HZ_FULL -- Do two, one with CONFIG_NO_HZ_FULL_SYSIDLE.
 CONFIG_NO_HZ_FULL_SYSIDLE -- Do one.
 CONFIG_PREEMPT -- Do half.  (First three and #8.)
-CONFIG_PROVE_LOCKING -- Do all but two, covering CONFIG_PROVE_RCU and not.
-CONFIG_PROVE_RCU -- Do all but one under CONFIG_PROVE_LOCKING.
+CONFIG_PROVE_LOCKING -- Do several, covering CONFIG_DEBUG_LOCK_ALLOC=y and not.
+CONFIG_PROVE_RCU -- Hardwired to CONFIG_PROVE_LOCKING.
 CONFIG_RCU_BOOST -- one of PREEMPT_RCU.
 CONFIG_RCU_KTHREAD_PRIO -- set to 2 for _BOOST testing.
-CONFIG_RCU_CPU_STALL_INFO -- Do one.
-CONFIG_RCU_FANOUT -- Cover hierarchy as currently, but overlap with others.
-CONFIG_RCU_FANOUT_EXACT -- Do one.
+CONFIG_RCU_CPU_STALL_INFO -- Now default, avoid at least twice.
+CONFIG_RCU_FANOUT -- Cover hierarchy, but overlap with others.
 CONFIG_RCU_FANOUT_LEAF -- Do one non-default.
 CONFIG_RCU_FAST_NO_HZ -- Do one, but not with CONFIG_RCU_NOCB_CPU_ALL.
 CONFIG_RCU_NOCB_CPU -- Do three, see below.
@@ -27,28 +26,19 @@ CONFIG_RCU_NOCB_CPU_NONE -- Do one.
 CONFIG_RCU_NOCB_CPU_ZERO -- Do one.
 CONFIG_RCU_TRACE -- Do half.
 CONFIG_SMP -- Need one !SMP for PREEMPT_RCU.
+!RCU_EXPERT -- Do a few, but these have to be vanilla configurations.
 RCU-bh: Do one with PREEMPT and one with !PREEMPT.
 RCU-sched: Do one with PREEMPT but not BOOST.
 
 
-Hierarchy:
-
-TREE01.        CONFIG_NR_CPUS=8, CONFIG_RCU_FANOUT=8, CONFIG_RCU_FANOUT_EXACT=n.
-TREE02.        CONFIG_NR_CPUS=8, CONFIG_RCU_FANOUT=3, CONFIG_RCU_FANOUT_EXACT=n,
-       CONFIG_RCU_FANOUT_LEAF=3.
-TREE03.        CONFIG_NR_CPUS=8, CONFIG_RCU_FANOUT=4, CONFIG_RCU_FANOUT_EXACT=n,
-       CONFIG_RCU_FANOUT_LEAF=4.
-TREE04.        CONFIG_NR_CPUS=8, CONFIG_RCU_FANOUT=2, CONFIG_RCU_FANOUT_EXACT=n,
-       CONFIG_RCU_FANOUT_LEAF=2.
-TREE05.        CONFIG_NR_CPUS=8, CONFIG_RCU_FANOUT=6, CONFIG_RCU_FANOUT_EXACT=n
-       CONFIG_RCU_FANOUT_LEAF=6.
-TREE06.        CONFIG_NR_CPUS=8, CONFIG_RCU_FANOUT=6, CONFIG_RCU_FANOUT_EXACT=y
-       CONFIG_RCU_FANOUT_LEAF=6.
-TREE07.        CONFIG_NR_CPUS=16, CONFIG_RCU_FANOUT=2, CONFIG_RCU_FANOUT_EXACT=n,
-       CONFIG_RCU_FANOUT_LEAF=2.
-TREE08.        CONFIG_NR_CPUS=16, CONFIG_RCU_FANOUT=3, CONFIG_RCU_FANOUT_EXACT=y,
-       CONFIG_RCU_FANOUT_LEAF=2.
-TREE09.        CONFIG_NR_CPUS=1.
+Boot parameters:
+
+nohz_full - do at least one.
+maxcpu -- do at least one.
+rcupdate.rcu_self_test_bh -- Do at least one each, offloaded and not.
+rcupdate.rcu_self_test_sched -- Do at least one each, offloaded and not.
+rcupdate.rcu_self_test -- Do at least one each, offloaded and not.
+rcutree.rcu_fanout_exact -- Do at least one.
 
 
 Kconfig Parameters Ignored:
index b8272e6c4b3b19aa2ec92790b3b6f6cd4addd11b..fb46ad6ac92cadc71254c68c80953eeae51b218b 100644 (file)
@@ -44,6 +44,7 @@
 #include <time.h>
 #include <sys/time.h>
 #include <sys/timex.h>
+#include <sys/errno.h>
 #include <string.h>
 #include <signal.h>
 #include <unistd.h>
@@ -63,6 +64,9 @@ static inline int ksft_exit_fail(void)
 #define NSEC_PER_SEC 1000000000ULL
 #define CLOCK_TAI 11
 
+time_t next_leap;
+int error_found;
+
 /* returns 1 if a <= b, 0 otherwise */
 static inline int in_order(struct timespec a, struct timespec b)
 {
@@ -134,6 +138,35 @@ void handler(int unused)
        exit(0);
 }
 
+void sigalarm(int signo)
+{
+       struct timex tx;
+       int ret;
+
+       tx.modes = 0;
+       ret = adjtimex(&tx);
+
+       if (tx.time.tv_sec < next_leap) {
+               printf("Error: Early timer expiration! (Should be %ld)\n", next_leap);
+               error_found = 1;
+               printf("adjtimex: %10ld sec + %6ld us (%i)\t%s\n",
+                                       tx.time.tv_sec,
+                                       tx.time.tv_usec,
+                                       tx.tai,
+                                       time_state_str(ret));
+       }
+       if (ret != TIME_WAIT) {
+               printf("Error: Timer seeing incorrect NTP state? (Should be TIME_WAIT)\n");
+               error_found = 1;
+               printf("adjtimex: %10ld sec + %6ld us (%i)\t%s\n",
+                                       tx.time.tv_sec,
+                                       tx.time.tv_usec,
+                                       tx.tai,
+                                       time_state_str(ret));
+       }
+}
+
+
 /* Test for known hrtimer failure */
 void test_hrtimer_failure(void)
 {
@@ -144,12 +177,19 @@ void test_hrtimer_failure(void)
        clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &target, NULL);
        clock_gettime(CLOCK_REALTIME, &now);
 
-       if (!in_order(target, now))
+       if (!in_order(target, now)) {
                printf("ERROR: hrtimer early expiration failure observed.\n");
+               error_found = 1;
+       }
 }
 
 int main(int argc, char **argv)
 {
+       timer_t tm1;
+       struct itimerspec its1;
+       struct sigevent se;
+       struct sigaction act;
+       int signum = SIGRTMAX;
        int settime = 0;
        int tai_time = 0;
        int insert = 1;
@@ -191,6 +231,12 @@ int main(int argc, char **argv)
        signal(SIGINT, handler);
        signal(SIGKILL, handler);
 
+       /* Set up timer signal handler: */
+       sigfillset(&act.sa_mask);
+       act.sa_flags = 0;
+       act.sa_handler = sigalarm;
+       sigaction(signum, &act, NULL);
+
        if (iterations < 0)
                printf("This runs continuously. Press ctrl-c to stop\n");
        else
@@ -201,7 +247,7 @@ int main(int argc, char **argv)
                int ret;
                struct timespec ts;
                struct timex tx;
-               time_t now, next_leap;
+               time_t now;
 
                /* Get the current time */
                clock_gettime(CLOCK_REALTIME, &ts);
@@ -251,10 +297,27 @@ int main(int argc, char **argv)
 
                printf("Scheduling leap second for %s", ctime(&next_leap));
 
+               /* Set up timer */
+               printf("Setting timer for %ld -  %s", next_leap, ctime(&next_leap));
+               memset(&se, 0, sizeof(se));
+               se.sigev_notify = SIGEV_SIGNAL;
+               se.sigev_signo = signum;
+               se.sigev_value.sival_int = 0;
+               if (timer_create(CLOCK_REALTIME, &se, &tm1) == -1) {
+                       printf("Error: timer_create failed\n");
+                       return ksft_exit_fail();
+               }
+               its1.it_value.tv_sec = next_leap;
+               its1.it_value.tv_nsec = 0;
+               its1.it_interval.tv_sec = 0;
+               its1.it_interval.tv_nsec = 0;
+               timer_settime(tm1, TIMER_ABSTIME, &its1, NULL);
+
                /* Wake up 3 seconds before leap */
                ts.tv_sec = next_leap - 3;
                ts.tv_nsec = 0;
 
+
                while (clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &ts, NULL))
                        printf("Something woke us up, returning to sleep\n");
 
@@ -276,6 +339,7 @@ int main(int argc, char **argv)
                while (now < next_leap + 2) {
                        char buf[26];
                        struct timespec tai;
+                       int ret;
 
                        tx.modes = 0;
                        ret = adjtimex(&tx);
@@ -308,8 +372,13 @@ int main(int argc, char **argv)
                /* Note if kernel has known hrtimer failure */
                test_hrtimer_failure();
 
-               printf("Leap complete\n\n");
-
+               printf("Leap complete\n");
+               if (error_found) {
+                       printf("Errors observed\n");
+                       clear_time_state();
+                       return ksft_exit_fail();
+               }
+               printf("\n");
                if ((iterations != -1) && !(--iterations))
                        break;
        }
index 5bdb781163d1f2d0eb6a69fea8a976fa2873f3b1..caa60d56d7d1f56d00c6cf087a5c48e863ea7be9 100644 (file)
@@ -4,9 +4,11 @@ include ../lib.mk
 
 .PHONY: all all_32 all_64 warn_32bit_failure clean
 
-TARGETS_C_BOTHBITS := sigreturn single_step_syscall
+TARGETS_C_BOTHBITS := sigreturn single_step_syscall sysret_ss_attrs
+TARGETS_C_32BIT_ONLY := entry_from_vm86
 
-BINARIES_32 := $(TARGETS_C_BOTHBITS:%=%_32)
+TARGETS_C_32BIT_ALL := $(TARGETS_C_BOTHBITS) $(TARGETS_C_32BIT_ONLY)
+BINARIES_32 := $(TARGETS_C_32BIT_ALL:%=%_32)
 BINARIES_64 := $(TARGETS_C_BOTHBITS:%=%_64)
 
 CFLAGS := -O2 -g -std=gnu99 -pthread -Wall
@@ -32,7 +34,7 @@ all_64: $(BINARIES_64)
 clean:
        $(RM) $(BINARIES_32) $(BINARIES_64)
 
-$(TARGETS_C_BOTHBITS:%=%_32): %_32: %.c
+$(TARGETS_C_32BIT_ALL:%=%_32): %_32: %.c
        $(CC) -m32 -o $@ $(CFLAGS) $(EXTRA_CFLAGS) $^ -lrt -ldl
 
 $(TARGETS_C_BOTHBITS:%=%_64): %_64: %.c
@@ -55,3 +57,6 @@ warn_32bit_failure:
        echo "  yum install glibc-devel.*i686";                         \
        exit 0;
 endif
+
+# Some tests have additional dependencies.
+sysret_ss_attrs_64: thunks.S
diff --git a/tools/testing/selftests/x86/entry_from_vm86.c b/tools/testing/selftests/x86/entry_from_vm86.c
new file mode 100644 (file)
index 0000000..5c38a18
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ * entry_from_vm86.c - tests kernel entries from vm86 mode
+ * Copyright (c) 2014-2015 Andrew Lutomirski
+ *
+ * This exercises a few paths that need to special-case vm86 mode.
+ *
+ * GPL v2.
+ */
+
+#define _GNU_SOURCE
+
+#include <assert.h>
+#include <stdlib.h>
+#include <sys/syscall.h>
+#include <sys/signal.h>
+#include <sys/ucontext.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+#include <sys/mman.h>
+#include <err.h>
+#include <stddef.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <sys/vm86.h>
+
+static unsigned long load_addr = 0x10000;
+static int nerrs = 0;
+
+asm (
+       ".pushsection .rodata\n\t"
+       ".type vmcode_bound, @object\n\t"
+       "vmcode:\n\t"
+       "vmcode_bound:\n\t"
+       ".code16\n\t"
+       "bound %ax, (2048)\n\t"
+       "int3\n\t"
+       "vmcode_sysenter:\n\t"
+       "sysenter\n\t"
+       ".size vmcode, . - vmcode\n\t"
+       "end_vmcode:\n\t"
+       ".code32\n\t"
+       ".popsection"
+       );
+
+extern unsigned char vmcode[], end_vmcode[];
+extern unsigned char vmcode_bound[], vmcode_sysenter[];
+
+static void do_test(struct vm86plus_struct *v86, unsigned long eip,
+                   const char *text)
+{
+       long ret;
+
+       printf("[RUN]\t%s from vm86 mode\n", text);
+       v86->regs.eip = eip;
+       ret = vm86(VM86_ENTER, v86);
+
+       if (ret == -1 && errno == ENOSYS) {
+               printf("[SKIP]\tvm86 not supported\n");
+               return;
+       }
+
+       if (VM86_TYPE(ret) == VM86_INTx) {
+               char trapname[32];
+               int trapno = VM86_ARG(ret);
+               if (trapno == 13)
+                       strcpy(trapname, "GP");
+               else if (trapno == 5)
+                       strcpy(trapname, "BR");
+               else if (trapno == 14)
+                       strcpy(trapname, "PF");
+               else
+                       sprintf(trapname, "%d", trapno);
+
+               printf("[OK]\tExited vm86 mode due to #%s\n", trapname);
+       } else if (VM86_TYPE(ret) == VM86_UNKNOWN) {
+               printf("[OK]\tExited vm86 mode due to unhandled GP fault\n");
+       } else {
+               printf("[OK]\tExited vm86 mode due to type %ld, arg %ld\n",
+                      VM86_TYPE(ret), VM86_ARG(ret));
+       }
+}
+
+int main(void)
+{
+       struct vm86plus_struct v86;
+       unsigned char *addr = mmap((void *)load_addr, 4096,
+                                  PROT_READ | PROT_WRITE | PROT_EXEC,
+                                  MAP_ANONYMOUS | MAP_PRIVATE, -1,0);
+       if (addr != (unsigned char *)load_addr)
+               err(1, "mmap");
+
+       memcpy(addr, vmcode, end_vmcode - vmcode);
+       addr[2048] = 2;
+       addr[2050] = 3;
+
+       memset(&v86, 0, sizeof(v86));
+
+       v86.regs.cs = load_addr / 16;
+       v86.regs.ss = load_addr / 16;
+       v86.regs.ds = load_addr / 16;
+       v86.regs.es = load_addr / 16;
+
+       assert((v86.regs.cs & 3) == 0); /* Looks like RPL = 0 */
+
+       /* #BR -- should deliver SIG??? */
+       do_test(&v86, vmcode_bound - vmcode, "#BR");
+
+       /* SYSENTER -- should cause #GP or #UD depending on CPU */
+       do_test(&v86, vmcode_sysenter - vmcode, "SYSENTER");
+
+       return (nerrs == 0 ? 0 : 1);
+}
diff --git a/tools/testing/selftests/x86/sysret_ss_attrs.c b/tools/testing/selftests/x86/sysret_ss_attrs.c
new file mode 100644 (file)
index 0000000..ce42d5a
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * sysret_ss_attrs.c - test that syscalls return valid hidden SS attributes
+ * Copyright (c) 2015 Andrew Lutomirski
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * On AMD CPUs, SYSRET can return with a valid SS descriptor with with
+ * the hidden attributes set to an unusable state.  Make sure the kernel
+ * doesn't let this happen.
+ */
+
+#define _GNU_SOURCE
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <err.h>
+#include <stddef.h>
+#include <stdbool.h>
+#include <pthread.h>
+
+static void *threadproc(void *ctx)
+{
+       /*
+        * Do our best to cause sleeps on this CPU to exit the kernel and
+        * re-enter with SS = 0.
+        */
+       while (true)
+               ;
+
+       return NULL;
+}
+
+#ifdef __x86_64__
+extern unsigned long call32_from_64(void *stack, void (*function)(void));
+
+asm (".pushsection .text\n\t"
+     ".code32\n\t"
+     "test_ss:\n\t"
+     "pushl $0\n\t"
+     "popl %eax\n\t"
+     "ret\n\t"
+     ".code64");
+extern void test_ss(void);
+#endif
+
+int main()
+{
+       /*
+        * Start a busy-looping thread on the same CPU we're on.
+        * For simplicity, just stick everything to CPU 0.  This will
+        * fail in some containers, but that's probably okay.
+        */
+       cpu_set_t cpuset;
+       CPU_ZERO(&cpuset);
+       CPU_SET(0, &cpuset);
+       if (sched_setaffinity(0, sizeof(cpuset), &cpuset) != 0)
+               printf("[WARN]\tsched_setaffinity failed\n");
+
+       pthread_t thread;
+       if (pthread_create(&thread, 0, threadproc, 0) != 0)
+               err(1, "pthread_create");
+
+#ifdef __x86_64__
+       unsigned char *stack32 = mmap(NULL, 4096, PROT_READ | PROT_WRITE,
+                                     MAP_32BIT | MAP_ANONYMOUS | MAP_PRIVATE,
+                                     -1, 0);
+       if (stack32 == MAP_FAILED)
+               err(1, "mmap");
+#endif
+
+       printf("[RUN]\tSyscalls followed by SS validation\n");
+
+       for (int i = 0; i < 1000; i++) {
+               /*
+                * Go to sleep and return using sysret (if we're 64-bit
+                * or we're 32-bit on AMD on a 64-bit kernel).  On AMD CPUs,
+                * SYSRET doesn't fix up the cached SS descriptor, so the
+                * kernel needs some kind of workaround to make sure that we
+                * end the system call with a valid stack segment.  This
+                * can be a confusing failure because the SS *selector*
+                * is the same regardless.
+                */
+               usleep(2);
+
+#ifdef __x86_64__
+               /*
+                * On 32-bit, just doing a syscall through glibc is enough
+                * to cause a crash if our cached SS descriptor is invalid.
+                * On 64-bit, it's not, so try extra hard.
+                */
+               call32_from_64(stack32 + 4088, test_ss);
+#endif
+       }
+
+       printf("[OK]\tWe survived\n");
+
+#ifdef __x86_64__
+       munmap(stack32, 4096);
+#endif
+
+       return 0;
+}
diff --git a/tools/testing/selftests/x86/thunks.S b/tools/testing/selftests/x86/thunks.S
new file mode 100644 (file)
index 0000000..ce8a995
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * thunks.S - assembly helpers for mixed-bitness code
+ * Copyright (c) 2015 Andrew Lutomirski
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * These are little helpers that make it easier to switch bitness on
+ * the fly.
+ */
+
+       .text
+
+       .global call32_from_64
+       .type call32_from_64, @function
+call32_from_64:
+       // rdi: stack to use
+       // esi: function to call
+
+       // Save registers
+       pushq %rbx
+       pushq %rbp
+       pushq %r12
+       pushq %r13
+       pushq %r14
+       pushq %r15
+       pushfq
+
+       // Switch stacks
+       mov %rsp,(%rdi)
+       mov %rdi,%rsp
+
+       // Switch to compatibility mode
+       pushq $0x23  /* USER32_CS */
+       pushq $1f
+       lretq
+
+1:
+       .code32
+       // Call the function
+       call *%esi
+       // Switch back to long mode
+       jmp $0x33,$1f
+       .code64
+
+1:
+       // Restore the stack
+       mov (%rsp),%rsp
+
+       // Restore registers
+       popfq
+       popq %r15
+       popq %r14
+       popq %r13
+       popq %r12
+       popq %rbp
+       popq %rbx
+
+       ret
+
+.size call32_from_64, .-call32_from_64
index e9c3a7a83833bf2ef058cfd407b92bafe20da073..e661e7fb9d91878a1d624b67be10d069cf248db0 100644 (file)
@@ -76,8 +76,6 @@ static bool handle_mmio_ctlr(struct kvm_vcpu *vcpu,
        vgic_reg_access(mmio, &reg, offset,
                        ACCESS_READ_VALUE | ACCESS_WRITE_VALUE);
        if (mmio->is_write) {
-               if (reg & GICD_CTLR_ENABLE_SS_G0)
-                       kvm_info("guest tried to enable unsupported Group0 interrupts\n");
                vcpu->kvm->arch.vgic.enabled = !!(reg & GICD_CTLR_ENABLE_SS_G1);
                vgic_update_state(vcpu->kvm);
                return true;
@@ -173,6 +171,32 @@ static bool handle_mmio_clear_pending_reg_dist(struct kvm_vcpu *vcpu,
        return false;
 }
 
+static bool handle_mmio_set_active_reg_dist(struct kvm_vcpu *vcpu,
+                                           struct kvm_exit_mmio *mmio,
+                                           phys_addr_t offset)
+{
+       if (likely(offset >= VGIC_NR_PRIVATE_IRQS / 8))
+               return vgic_handle_set_active_reg(vcpu->kvm, mmio, offset,
+                                                  vcpu->vcpu_id);
+
+       vgic_reg_access(mmio, NULL, offset,
+                       ACCESS_READ_RAZ | ACCESS_WRITE_IGNORED);
+       return false;
+}
+
+static bool handle_mmio_clear_active_reg_dist(struct kvm_vcpu *vcpu,
+                                             struct kvm_exit_mmio *mmio,
+                                             phys_addr_t offset)
+{
+       if (likely(offset >= VGIC_NR_PRIVATE_IRQS / 8))
+               return vgic_handle_clear_active_reg(vcpu->kvm, mmio, offset,
+                                                   vcpu->vcpu_id);
+
+       vgic_reg_access(mmio, NULL, offset,
+                       ACCESS_READ_RAZ | ACCESS_WRITE_IGNORED);
+       return false;
+}
+
 static bool handle_mmio_priority_reg_dist(struct kvm_vcpu *vcpu,
                                          struct kvm_exit_mmio *mmio,
                                          phys_addr_t offset)
@@ -428,13 +452,13 @@ static const struct vgic_io_range vgic_v3_dist_ranges[] = {
                .base           = GICD_ISACTIVER,
                .len            = 0x80,
                .bits_per_irq   = 1,
-               .handle_mmio    = handle_mmio_raz_wi,
+               .handle_mmio    = handle_mmio_set_active_reg_dist,
        },
        {
                .base           = GICD_ICACTIVER,
                .len            = 0x80,
                .bits_per_irq   = 1,
-               .handle_mmio    = handle_mmio_raz_wi,
+               .handle_mmio    = handle_mmio_clear_active_reg_dist,
        },
        {
                .base           = GICD_IPRIORITYR,
@@ -561,6 +585,26 @@ static bool handle_mmio_clear_enable_reg_redist(struct kvm_vcpu *vcpu,
                                      ACCESS_WRITE_CLEARBIT);
 }
 
+static bool handle_mmio_set_active_reg_redist(struct kvm_vcpu *vcpu,
+                                             struct kvm_exit_mmio *mmio,
+                                             phys_addr_t offset)
+{
+       struct kvm_vcpu *redist_vcpu = mmio->private;
+
+       return vgic_handle_set_active_reg(vcpu->kvm, mmio, offset,
+                                         redist_vcpu->vcpu_id);
+}
+
+static bool handle_mmio_clear_active_reg_redist(struct kvm_vcpu *vcpu,
+                                               struct kvm_exit_mmio *mmio,
+                                               phys_addr_t offset)
+{
+       struct kvm_vcpu *redist_vcpu = mmio->private;
+
+       return vgic_handle_clear_active_reg(vcpu->kvm, mmio, offset,
+                                            redist_vcpu->vcpu_id);
+}
+
 static bool handle_mmio_set_pending_reg_redist(struct kvm_vcpu *vcpu,
                                               struct kvm_exit_mmio *mmio,
                                               phys_addr_t offset)
@@ -674,13 +718,13 @@ static const struct vgic_io_range vgic_redist_ranges[] = {
                .base           = SGI_base(GICR_ISACTIVER0),
                .len            = 0x04,
                .bits_per_irq   = 1,
-               .handle_mmio    = handle_mmio_raz_wi,
+               .handle_mmio    = handle_mmio_set_active_reg_redist,
        },
        {
                .base           = SGI_base(GICR_ICACTIVER0),
                .len            = 0x04,
                .bits_per_irq   = 1,
-               .handle_mmio    = handle_mmio_raz_wi,
+               .handle_mmio    = handle_mmio_clear_active_reg_redist,
        },
        {
                .base           = SGI_base(GICR_IPRIORITYR0),
index 78fb8201014f74f2172baf8a48acc5173a450f4d..bc40137a022d51e451913f3011639e196ddd135b 100644 (file)
@@ -26,8 +26,6 @@
 #include <linux/of_irq.h>
 #include <linux/uaccess.h>
 
-#include <linux/irqchip/arm-gic.h>
-
 #include <asm/kvm_emulate.h>
 #include <asm/kvm_arm.h>
 #include <asm/kvm_mmu.h>
@@ -1561,7 +1559,7 @@ int kvm_vgic_inject_irq(struct kvm *kvm, int cpuid, unsigned int irq_num,
                        goto out;
        }
 
-       if (irq_num >= kvm->arch.vgic.nr_irqs)
+       if (irq_num >= min(kvm->arch.vgic.nr_irqs, 1020))
                return -EINVAL;
 
        vcpu_id = vgic_update_irq_pending(kvm, cpuid, irq_num, level);
@@ -2128,9 +2126,6 @@ int kvm_vgic_hyp_init(void)
                goto out_free_irq;
        }
 
-       /* Callback into for arch code for setup */
-       vgic_arch_setup(vgic);
-
        on_each_cpu(vgic_init_maintenance_interrupt, NULL, 1);
 
        return 0;
@@ -2161,10 +2156,7 @@ int kvm_set_irq(struct kvm *kvm, int irq_source_id,
 
        BUG_ON(!vgic_initialized(kvm));
 
-       if (spi > kvm->arch.vgic.nr_irqs)
-               return -EINVAL;
        return kvm_vgic_inject_irq(kvm, 0, spi, level);
-
 }
 
 /* MSI not implemented yet */
index e7ef6447cb82378f90c22a3dcf5bdabe9917055d..ec4cfa278f04a50989d680f0f4de2c38ca8cab07 100644 (file)
@@ -29,8 +29,8 @@ void kvm_async_pf_deinit(void);
 void kvm_async_pf_vcpu_init(struct kvm_vcpu *vcpu);
 #else
 #define kvm_async_pf_init() (0)
-#define kvm_async_pf_deinit() do{}while(0)
-#define kvm_async_pf_vcpu_init(C) do{}while(0)
+#define kvm_async_pf_deinit() do {} while (0)
+#define kvm_async_pf_vcpu_init(C) do {} while (0)
 #endif
 
 #endif
index b280c20444d1546434e1381a52c723098d564f6c..5cbf190d238cd083e128ae9ec78da52b745d6338 100644 (file)
@@ -24,9 +24,9 @@ struct kvm_coalesced_mmio_dev {
 int kvm_coalesced_mmio_init(struct kvm *kvm);
 void kvm_coalesced_mmio_free(struct kvm *kvm);
 int kvm_vm_ioctl_register_coalesced_mmio(struct kvm *kvm,
-                                       struct kvm_coalesced_mmio_zone *zone);
+                                                                                                                                                               struct kvm_coalesced_mmio_zone *zone);
 int kvm_vm_ioctl_unregister_coalesced_mmio(struct kvm *kvm,
-                                         struct kvm_coalesced_mmio_zone *zone);
+                                                                                                                                                               struct kvm_coalesced_mmio_zone *zone);
 
 #else
 
index 1d56a901e791788d9f2c855dcf3e96a9b650df77..21c14244f4c4fd1c3c8ffade7f3265df91c89efb 100644 (file)
@@ -33,7 +33,6 @@
 
 struct kvm_irq_routing_table {
        int chip[KVM_NR_IRQCHIPS][KVM_IRQCHIP_NUM_PINS];
-       struct kvm_kernel_irq_routing_entry *rt_entries;
        u32 nr_rt_entries;
        /*
         * Array indexed by gsi. Each entry contains list of irq chips
@@ -118,11 +117,32 @@ int kvm_set_irq(struct kvm *kvm, int irq_source_id, u32 irq, int level,
        return ret;
 }
 
+static void free_irq_routing_table(struct kvm_irq_routing_table *rt)
+{
+       int i;
+
+       if (!rt)
+               return;
+
+       for (i = 0; i < rt->nr_rt_entries; ++i) {
+               struct kvm_kernel_irq_routing_entry *e;
+               struct hlist_node *n;
+
+               hlist_for_each_entry_safe(e, n, &rt->map[i], link) {
+                       hlist_del(&e->link);
+                       kfree(e);
+               }
+       }
+
+       kfree(rt);
+}
+
 void kvm_free_irq_routing(struct kvm *kvm)
 {
        /* Called only during vm destruction. Nobody can use the pointer
           at this stage */
-       kfree(kvm->irq_routing);
+       struct kvm_irq_routing_table *rt = rcu_access_pointer(kvm->irq_routing);
+       free_irq_routing_table(rt);
 }
 
 static int setup_routing_entry(struct kvm_irq_routing_table *rt,
@@ -173,25 +193,29 @@ int kvm_set_irq_routing(struct kvm *kvm,
 
        nr_rt_entries += 1;
 
-       new = kzalloc(sizeof(*new) + (nr_rt_entries * sizeof(struct hlist_head))
-                     + (nr * sizeof(struct kvm_kernel_irq_routing_entry)),
+       new = kzalloc(sizeof(*new) + (nr_rt_entries * sizeof(struct hlist_head)),
                      GFP_KERNEL);
 
        if (!new)
                return -ENOMEM;
 
-       new->rt_entries = (void *)&new->map[nr_rt_entries];
-
        new->nr_rt_entries = nr_rt_entries;
        for (i = 0; i < KVM_NR_IRQCHIPS; i++)
                for (j = 0; j < KVM_IRQCHIP_NUM_PINS; j++)
                        new->chip[i][j] = -1;
 
        for (i = 0; i < nr; ++i) {
+               struct kvm_kernel_irq_routing_entry *e;
+
+               r = -ENOMEM;
+               e = kzalloc(sizeof(*e), GFP_KERNEL);
+               if (!e)
+                       goto out;
+
                r = -EINVAL;
                if (ue->flags)
                        goto out;
-               r = setup_routing_entry(new, &new->rt_entries[i], ue);
+               r = setup_routing_entry(new, e, ue);
                if (r)
                        goto out;
                ++ue;
@@ -209,6 +233,7 @@ int kvm_set_irq_routing(struct kvm *kvm,
        r = 0;
 
 out:
-       kfree(new);
+       free_irq_routing_table(new);
+
        return r;
 }
index 90977418aeb6edfbf274c2260b48296638fd0762..848af90b8091a9a3bd8ed7e18d6ace2692210c36 100644 (file)
@@ -103,8 +103,7 @@ static void hardware_disable_all(void);
 static void kvm_io_bus_destroy(struct kvm_io_bus *bus);
 
 static void kvm_release_pfn_dirty(pfn_t pfn);
-static void mark_page_dirty_in_slot(struct kvm *kvm,
-                                   struct kvm_memory_slot *memslot, gfn_t gfn);
+static void mark_page_dirty_in_slot(struct kvm_memory_slot *memslot, gfn_t gfn);
 
 __visible bool kvm_rebooting;
 EXPORT_SYMBOL_GPL(kvm_rebooting);
@@ -440,13 +439,60 @@ static int kvm_init_mmu_notifier(struct kvm *kvm)
 
 #endif /* CONFIG_MMU_NOTIFIER && KVM_ARCH_WANT_MMU_NOTIFIER */
 
-static void kvm_init_memslots_id(struct kvm *kvm)
+static struct kvm_memslots *kvm_alloc_memslots(void)
 {
        int i;
-       struct kvm_memslots *slots = kvm->memslots;
+       struct kvm_memslots *slots;
 
+       slots = kvm_kvzalloc(sizeof(struct kvm_memslots));
+       if (!slots)
+               return NULL;
+
+       /*
+        * Init kvm generation close to the maximum to easily test the
+        * code of handling generation number wrap-around.
+        */
+       slots->generation = -150;
        for (i = 0; i < KVM_MEM_SLOTS_NUM; i++)
                slots->id_to_index[i] = slots->memslots[i].id = i;
+
+       return slots;
+}
+
+static void kvm_destroy_dirty_bitmap(struct kvm_memory_slot *memslot)
+{
+       if (!memslot->dirty_bitmap)
+               return;
+
+       kvfree(memslot->dirty_bitmap);
+       memslot->dirty_bitmap = NULL;
+}
+
+/*
+ * Free any memory in @free but not in @dont.
+ */
+static void kvm_free_memslot(struct kvm *kvm, struct kvm_memory_slot *free,
+                             struct kvm_memory_slot *dont)
+{
+       if (!dont || free->dirty_bitmap != dont->dirty_bitmap)
+               kvm_destroy_dirty_bitmap(free);
+
+       kvm_arch_free_memslot(kvm, free, dont);
+
+       free->npages = 0;
+}
+
+static void kvm_free_memslots(struct kvm *kvm, struct kvm_memslots *slots)
+{
+       struct kvm_memory_slot *memslot;
+
+       if (!slots)
+               return;
+
+       kvm_for_each_memslot(memslot, slots)
+               kvm_free_memslot(kvm, memslot, NULL);
+
+       kvfree(slots);
 }
 
 static struct kvm *kvm_create_vm(unsigned long type)
@@ -472,17 +518,12 @@ static struct kvm *kvm_create_vm(unsigned long type)
        BUILD_BUG_ON(KVM_MEM_SLOTS_NUM > SHRT_MAX);
 
        r = -ENOMEM;
-       kvm->memslots = kvm_kvzalloc(sizeof(struct kvm_memslots));
-       if (!kvm->memslots)
-               goto out_err_no_srcu;
-
-       /*
-        * Init kvm generation close to the maximum to easily test the
-        * code of handling generation number wrap-around.
-        */
-       kvm->memslots->generation = -150;
+       for (i = 0; i < KVM_ADDRESS_SPACE_NUM; i++) {
+               kvm->memslots[i] = kvm_alloc_memslots();
+               if (!kvm->memslots[i])
+                       goto out_err_no_srcu;
+       }
 
-       kvm_init_memslots_id(kvm);
        if (init_srcu_struct(&kvm->srcu))
                goto out_err_no_srcu;
        if (init_srcu_struct(&kvm->irq_srcu))
@@ -523,7 +564,8 @@ out_err_no_srcu:
 out_err_no_disable:
        for (i = 0; i < KVM_NR_BUSES; i++)
                kfree(kvm->buses[i]);
-       kvfree(kvm->memslots);
+       for (i = 0; i < KVM_ADDRESS_SPACE_NUM; i++)
+               kvm_free_memslots(kvm, kvm->memslots[i]);
        kvm_arch_free_vm(kvm);
        return ERR_PTR(r);
 }
@@ -540,40 +582,6 @@ void *kvm_kvzalloc(unsigned long size)
                return kzalloc(size, GFP_KERNEL);
 }
 
-static void kvm_destroy_dirty_bitmap(struct kvm_memory_slot *memslot)
-{
-       if (!memslot->dirty_bitmap)
-               return;
-
-       kvfree(memslot->dirty_bitmap);
-       memslot->dirty_bitmap = NULL;
-}
-
-/*
- * Free any memory in @free but not in @dont.
- */
-static void kvm_free_physmem_slot(struct kvm *kvm, struct kvm_memory_slot *free,
-                                 struct kvm_memory_slot *dont)
-{
-       if (!dont || free->dirty_bitmap != dont->dirty_bitmap)
-               kvm_destroy_dirty_bitmap(free);
-
-       kvm_arch_free_memslot(kvm, free, dont);
-
-       free->npages = 0;
-}
-
-static void kvm_free_physmem(struct kvm *kvm)
-{
-       struct kvm_memslots *slots = kvm->memslots;
-       struct kvm_memory_slot *memslot;
-
-       kvm_for_each_memslot(memslot, slots)
-               kvm_free_physmem_slot(kvm, memslot, NULL);
-
-       kvfree(kvm->memslots);
-}
-
 static void kvm_destroy_devices(struct kvm *kvm)
 {
        struct list_head *node, *tmp;
@@ -607,7 +615,8 @@ static void kvm_destroy_vm(struct kvm *kvm)
 #endif
        kvm_arch_destroy_vm(kvm);
        kvm_destroy_devices(kvm);
-       kvm_free_physmem(kvm);
+       for (i = 0; i < KVM_ADDRESS_SPACE_NUM; i++)
+               kvm_free_memslots(kvm, kvm->memslots[i]);
        cleanup_srcu_struct(&kvm->irq_srcu);
        cleanup_srcu_struct(&kvm->srcu);
        kvm_arch_free_vm(kvm);
@@ -670,8 +679,6 @@ static void update_memslots(struct kvm_memslots *slots,
        WARN_ON(mslots[i].id != id);
        if (!new->npages) {
                WARN_ON(!mslots[i].npages);
-               new->base_gfn = 0;
-               new->flags = 0;
                if (mslots[i].npages)
                        slots->used_slots--;
        } else {
@@ -711,7 +718,7 @@ static void update_memslots(struct kvm_memslots *slots,
        slots->id_to_index[mslots[i].id] = i;
 }
 
-static int check_memory_region_flags(struct kvm_userspace_memory_region *mem)
+static int check_memory_region_flags(const struct kvm_userspace_memory_region *mem)
 {
        u32 valid_flags = KVM_MEM_LOG_DIRTY_PAGES;
 
@@ -726,9 +733,9 @@ static int check_memory_region_flags(struct kvm_userspace_memory_region *mem)
 }
 
 static struct kvm_memslots *install_new_memslots(struct kvm *kvm,
-               struct kvm_memslots *slots)
+               int as_id, struct kvm_memslots *slots)
 {
-       struct kvm_memslots *old_memslots = kvm->memslots;
+       struct kvm_memslots *old_memslots = __kvm_memslots(kvm, as_id);
 
        /*
         * Set the low bit in the generation, which disables SPTE caching
@@ -737,7 +744,7 @@ static struct kvm_memslots *install_new_memslots(struct kvm *kvm,
        WARN_ON(old_memslots->generation & 1);
        slots->generation = old_memslots->generation + 1;
 
-       rcu_assign_pointer(kvm->memslots, slots);
+       rcu_assign_pointer(kvm->memslots[as_id], slots);
        synchronize_srcu_expedited(&kvm->srcu);
 
        /*
@@ -747,7 +754,7 @@ static struct kvm_memslots *install_new_memslots(struct kvm *kvm,
         */
        slots->generation++;
 
-       kvm_arch_memslots_updated(kvm);
+       kvm_arch_memslots_updated(kvm, slots);
 
        return old_memslots;
 }
@@ -761,7 +768,7 @@ static struct kvm_memslots *install_new_memslots(struct kvm *kvm,
  * Must be called holding kvm->slots_lock for write.
  */
 int __kvm_set_memory_region(struct kvm *kvm,
-                           struct kvm_userspace_memory_region *mem)
+                           const struct kvm_userspace_memory_region *mem)
 {
        int r;
        gfn_t base_gfn;
@@ -769,6 +776,7 @@ int __kvm_set_memory_region(struct kvm *kvm,
        struct kvm_memory_slot *slot;
        struct kvm_memory_slot old, new;
        struct kvm_memslots *slots = NULL, *old_memslots;
+       int as_id, id;
        enum kvm_mr_change change;
 
        r = check_memory_region_flags(mem);
@@ -776,36 +784,36 @@ int __kvm_set_memory_region(struct kvm *kvm,
                goto out;
 
        r = -EINVAL;
+       as_id = mem->slot >> 16;
+       id = (u16)mem->slot;
+
        /* General sanity checks */
        if (mem->memory_size & (PAGE_SIZE - 1))
                goto out;
        if (mem->guest_phys_addr & (PAGE_SIZE - 1))
                goto out;
        /* We can read the guest memory with __xxx_user() later on. */
-       if ((mem->slot < KVM_USER_MEM_SLOTS) &&
+       if ((id < KVM_USER_MEM_SLOTS) &&
            ((mem->userspace_addr & (PAGE_SIZE - 1)) ||
             !access_ok(VERIFY_WRITE,
                        (void __user *)(unsigned long)mem->userspace_addr,
                        mem->memory_size)))
                goto out;
-       if (mem->slot >= KVM_MEM_SLOTS_NUM)
+       if (as_id >= KVM_ADDRESS_SPACE_NUM || id >= KVM_MEM_SLOTS_NUM)
                goto out;
        if (mem->guest_phys_addr + mem->memory_size < mem->guest_phys_addr)
                goto out;
 
-       slot = id_to_memslot(kvm->memslots, mem->slot);
+       slot = id_to_memslot(__kvm_memslots(kvm, as_id), id);
        base_gfn = mem->guest_phys_addr >> PAGE_SHIFT;
        npages = mem->memory_size >> PAGE_SHIFT;
 
        if (npages > KVM_MEM_MAX_NR_PAGES)
                goto out;
 
-       if (!npages)
-               mem->flags &= ~KVM_MEM_LOG_DIRTY_PAGES;
-
        new = old = *slot;
 
-       new.id = mem->slot;
+       new.id = id;
        new.base_gfn = base_gfn;
        new.npages = npages;
        new.flags = mem->flags;
@@ -828,17 +836,21 @@ int __kvm_set_memory_region(struct kvm *kvm,
                                goto out;
                        }
                }
-       } else if (old.npages) {
+       } else {
+               if (!old.npages)
+                       goto out;
+
                change = KVM_MR_DELETE;
-       } else /* Modify a non-existent slot: disallowed. */
-               goto out;
+               new.base_gfn = 0;
+               new.flags = 0;
+       }
 
        if ((change == KVM_MR_CREATE) || (change == KVM_MR_MOVE)) {
                /* Check for overlaps */
                r = -EEXIST;
-               kvm_for_each_memslot(slot, kvm->memslots) {
+               kvm_for_each_memslot(slot, __kvm_memslots(kvm, as_id)) {
                        if ((slot->id >= KVM_USER_MEM_SLOTS) ||
-                           (slot->id == mem->slot))
+                           (slot->id == id))
                                continue;
                        if (!((base_gfn + npages <= slot->base_gfn) ||
                              (base_gfn >= slot->base_gfn + slot->npages)))
@@ -867,13 +879,13 @@ int __kvm_set_memory_region(struct kvm *kvm,
        slots = kvm_kvzalloc(sizeof(struct kvm_memslots));
        if (!slots)
                goto out_free;
-       memcpy(slots, kvm->memslots, sizeof(struct kvm_memslots));
+       memcpy(slots, __kvm_memslots(kvm, as_id), sizeof(struct kvm_memslots));
 
        if ((change == KVM_MR_DELETE) || (change == KVM_MR_MOVE)) {
-               slot = id_to_memslot(slots, mem->slot);
+               slot = id_to_memslot(slots, id);
                slot->flags |= KVM_MEMSLOT_INVALID;
 
-               old_memslots = install_new_memslots(kvm, slots);
+               old_memslots = install_new_memslots(kvm, as_id, slots);
 
                /* slot was deleted or moved, clear iommu mapping */
                kvm_iommu_unmap_pages(kvm, &old);
@@ -898,18 +910,18 @@ int __kvm_set_memory_region(struct kvm *kvm,
        if (r)
                goto out_slots;
 
-       /* actual memory is freed via old in kvm_free_physmem_slot below */
+       /* actual memory is freed via old in kvm_free_memslot below */
        if (change == KVM_MR_DELETE) {
                new.dirty_bitmap = NULL;
                memset(&new.arch, 0, sizeof(new.arch));
        }
 
        update_memslots(slots, &new);
-       old_memslots = install_new_memslots(kvm, slots);
+       old_memslots = install_new_memslots(kvm, as_id, slots);
 
-       kvm_arch_commit_memory_region(kvm, mem, &old, change);
+       kvm_arch_commit_memory_region(kvm, mem, &old, &new, change);
 
-       kvm_free_physmem_slot(kvm, &old, &new);
+       kvm_free_memslot(kvm, &old, &new);
        kvfree(old_memslots);
 
        /*
@@ -931,14 +943,14 @@ int __kvm_set_memory_region(struct kvm *kvm,
 out_slots:
        kvfree(slots);
 out_free:
-       kvm_free_physmem_slot(kvm, &new, &old);
+       kvm_free_memslot(kvm, &new, &old);
 out:
        return r;
 }
 EXPORT_SYMBOL_GPL(__kvm_set_memory_region);
 
 int kvm_set_memory_region(struct kvm *kvm,
-                         struct kvm_userspace_memory_region *mem)
+                         const struct kvm_userspace_memory_region *mem)
 {
        int r;
 
@@ -952,24 +964,29 @@ EXPORT_SYMBOL_GPL(kvm_set_memory_region);
 static int kvm_vm_ioctl_set_memory_region(struct kvm *kvm,
                                          struct kvm_userspace_memory_region *mem)
 {
-       if (mem->slot >= KVM_USER_MEM_SLOTS)
+       if ((u16)mem->slot >= KVM_USER_MEM_SLOTS)
                return -EINVAL;
+
        return kvm_set_memory_region(kvm, mem);
 }
 
 int kvm_get_dirty_log(struct kvm *kvm,
                        struct kvm_dirty_log *log, int *is_dirty)
 {
+       struct kvm_memslots *slots;
        struct kvm_memory_slot *memslot;
-       int r, i;
+       int r, i, as_id, id;
        unsigned long n;
        unsigned long any = 0;
 
        r = -EINVAL;
-       if (log->slot >= KVM_USER_MEM_SLOTS)
+       as_id = log->slot >> 16;
+       id = (u16)log->slot;
+       if (as_id >= KVM_ADDRESS_SPACE_NUM || id >= KVM_USER_MEM_SLOTS)
                goto out;
 
-       memslot = id_to_memslot(kvm->memslots, log->slot);
+       slots = __kvm_memslots(kvm, as_id);
+       memslot = id_to_memslot(slots, id);
        r = -ENOENT;
        if (!memslot->dirty_bitmap)
                goto out;
@@ -1018,17 +1035,21 @@ EXPORT_SYMBOL_GPL(kvm_get_dirty_log);
 int kvm_get_dirty_log_protect(struct kvm *kvm,
                        struct kvm_dirty_log *log, bool *is_dirty)
 {
+       struct kvm_memslots *slots;
        struct kvm_memory_slot *memslot;
-       int r, i;
+       int r, i, as_id, id;
        unsigned long n;
        unsigned long *dirty_bitmap;
        unsigned long *dirty_bitmap_buffer;
 
        r = -EINVAL;
-       if (log->slot >= KVM_USER_MEM_SLOTS)
+       as_id = log->slot >> 16;
+       id = (u16)log->slot;
+       if (as_id >= KVM_ADDRESS_SPACE_NUM || id >= KVM_USER_MEM_SLOTS)
                goto out;
 
-       memslot = id_to_memslot(kvm->memslots, log->slot);
+       slots = __kvm_memslots(kvm, as_id);
+       memslot = id_to_memslot(slots, id);
 
        dirty_bitmap = memslot->dirty_bitmap;
        r = -ENOENT;
@@ -1091,6 +1112,11 @@ struct kvm_memory_slot *gfn_to_memslot(struct kvm *kvm, gfn_t gfn)
 }
 EXPORT_SYMBOL_GPL(gfn_to_memslot);
 
+struct kvm_memory_slot *kvm_vcpu_gfn_to_memslot(struct kvm_vcpu *vcpu, gfn_t gfn)
+{
+       return __gfn_to_memslot(kvm_vcpu_memslots(vcpu), gfn);
+}
+
 int kvm_is_visible_gfn(struct kvm *kvm, gfn_t gfn)
 {
        struct kvm_memory_slot *memslot = gfn_to_memslot(kvm, gfn);
@@ -1166,6 +1192,12 @@ unsigned long gfn_to_hva(struct kvm *kvm, gfn_t gfn)
 }
 EXPORT_SYMBOL_GPL(gfn_to_hva);
 
+unsigned long kvm_vcpu_gfn_to_hva(struct kvm_vcpu *vcpu, gfn_t gfn)
+{
+       return gfn_to_hva_many(kvm_vcpu_gfn_to_memslot(vcpu, gfn), gfn, NULL);
+}
+EXPORT_SYMBOL_GPL(kvm_vcpu_gfn_to_hva);
+
 /*
  * If writable is set to false, the hva returned by this function is only
  * allowed to be read.
@@ -1188,6 +1220,13 @@ unsigned long gfn_to_hva_prot(struct kvm *kvm, gfn_t gfn, bool *writable)
        return gfn_to_hva_memslot_prot(slot, gfn, writable);
 }
 
+unsigned long kvm_vcpu_gfn_to_hva_prot(struct kvm_vcpu *vcpu, gfn_t gfn, bool *writable)
+{
+       struct kvm_memory_slot *slot = kvm_vcpu_gfn_to_memslot(vcpu, gfn);
+
+       return gfn_to_hva_memslot_prot(slot, gfn, writable);
+}
+
 static int get_user_page_nowait(struct task_struct *tsk, struct mm_struct *mm,
        unsigned long start, int write, struct page **page)
 {
@@ -1355,9 +1394,8 @@ exit:
        return pfn;
 }
 
-static pfn_t
-__gfn_to_pfn_memslot(struct kvm_memory_slot *slot, gfn_t gfn, bool atomic,
-                    bool *async, bool write_fault, bool *writable)
+pfn_t __gfn_to_pfn_memslot(struct kvm_memory_slot *slot, gfn_t gfn, bool atomic,
+                          bool *async, bool write_fault, bool *writable)
 {
        unsigned long addr = __gfn_to_hva_many(slot, gfn, NULL, write_fault);
 
@@ -1376,65 +1414,59 @@ __gfn_to_pfn_memslot(struct kvm_memory_slot *slot, gfn_t gfn, bool atomic,
        return hva_to_pfn(addr, atomic, async, write_fault,
                          writable);
 }
+EXPORT_SYMBOL_GPL(__gfn_to_pfn_memslot);
 
-static pfn_t __gfn_to_pfn(struct kvm *kvm, gfn_t gfn, bool atomic, bool *async,
-                         bool write_fault, bool *writable)
+pfn_t gfn_to_pfn_prot(struct kvm *kvm, gfn_t gfn, bool write_fault,
+                     bool *writable)
 {
-       struct kvm_memory_slot *slot;
-
-       if (async)
-               *async = false;
-
-       slot = gfn_to_memslot(kvm, gfn);
-
-       return __gfn_to_pfn_memslot(slot, gfn, atomic, async, write_fault,
-                                   writable);
+       return __gfn_to_pfn_memslot(gfn_to_memslot(kvm, gfn), gfn, false, NULL,
+                                   write_fault, writable);
 }
+EXPORT_SYMBOL_GPL(gfn_to_pfn_prot);
 
-pfn_t gfn_to_pfn_atomic(struct kvm *kvm, gfn_t gfn)
+pfn_t gfn_to_pfn_memslot(struct kvm_memory_slot *slot, gfn_t gfn)
 {
-       return __gfn_to_pfn(kvm, gfn, true, NULL, true, NULL);
+       return __gfn_to_pfn_memslot(slot, gfn, false, NULL, true, NULL);
 }
-EXPORT_SYMBOL_GPL(gfn_to_pfn_atomic);
+EXPORT_SYMBOL_GPL(gfn_to_pfn_memslot);
 
-pfn_t gfn_to_pfn_async(struct kvm *kvm, gfn_t gfn, bool *async,
-                      bool write_fault, bool *writable)
+pfn_t gfn_to_pfn_memslot_atomic(struct kvm_memory_slot *slot, gfn_t gfn)
 {
-       return __gfn_to_pfn(kvm, gfn, false, async, write_fault, writable);
+       return __gfn_to_pfn_memslot(slot, gfn, true, NULL, true, NULL);
 }
-EXPORT_SYMBOL_GPL(gfn_to_pfn_async);
+EXPORT_SYMBOL_GPL(gfn_to_pfn_memslot_atomic);
 
-pfn_t gfn_to_pfn(struct kvm *kvm, gfn_t gfn)
+pfn_t gfn_to_pfn_atomic(struct kvm *kvm, gfn_t gfn)
 {
-       return __gfn_to_pfn(kvm, gfn, false, NULL, true, NULL);
+       return gfn_to_pfn_memslot_atomic(gfn_to_memslot(kvm, gfn), gfn);
 }
-EXPORT_SYMBOL_GPL(gfn_to_pfn);
+EXPORT_SYMBOL_GPL(gfn_to_pfn_atomic);
 
-pfn_t gfn_to_pfn_prot(struct kvm *kvm, gfn_t gfn, bool write_fault,
-                     bool *writable)
+pfn_t kvm_vcpu_gfn_to_pfn_atomic(struct kvm_vcpu *vcpu, gfn_t gfn)
 {
-       return __gfn_to_pfn(kvm, gfn, false, NULL, write_fault, writable);
+       return gfn_to_pfn_memslot_atomic(kvm_vcpu_gfn_to_memslot(vcpu, gfn), gfn);
 }
-EXPORT_SYMBOL_GPL(gfn_to_pfn_prot);
+EXPORT_SYMBOL_GPL(kvm_vcpu_gfn_to_pfn_atomic);
 
-pfn_t gfn_to_pfn_memslot(struct kvm_memory_slot *slot, gfn_t gfn)
+pfn_t gfn_to_pfn(struct kvm *kvm, gfn_t gfn)
 {
-       return __gfn_to_pfn_memslot(slot, gfn, false, NULL, true, NULL);
+       return gfn_to_pfn_memslot(gfn_to_memslot(kvm, gfn), gfn);
 }
+EXPORT_SYMBOL_GPL(gfn_to_pfn);
 
-pfn_t gfn_to_pfn_memslot_atomic(struct kvm_memory_slot *slot, gfn_t gfn)
+pfn_t kvm_vcpu_gfn_to_pfn(struct kvm_vcpu *vcpu, gfn_t gfn)
 {
-       return __gfn_to_pfn_memslot(slot, gfn, true, NULL, true, NULL);
+       return gfn_to_pfn_memslot(kvm_vcpu_gfn_to_memslot(vcpu, gfn), gfn);
 }
-EXPORT_SYMBOL_GPL(gfn_to_pfn_memslot_atomic);
+EXPORT_SYMBOL_GPL(kvm_vcpu_gfn_to_pfn);
 
-int gfn_to_page_many_atomic(struct kvm *kvm, gfn_t gfn, struct page **pages,
-                                                                 int nr_pages)
+int gfn_to_page_many_atomic(struct kvm_memory_slot *slot, gfn_t gfn,
+                           struct page **pages, int nr_pages)
 {
        unsigned long addr;
        gfn_t entry;
 
-       addr = gfn_to_hva_many(gfn_to_memslot(kvm, gfn), gfn, &entry);
+       addr = gfn_to_hva_many(slot, gfn, &entry);
        if (kvm_is_error_hva(addr))
                return -1;
 
@@ -1468,6 +1500,16 @@ struct page *gfn_to_page(struct kvm *kvm, gfn_t gfn)
 }
 EXPORT_SYMBOL_GPL(gfn_to_page);
 
+struct page *kvm_vcpu_gfn_to_page(struct kvm_vcpu *vcpu, gfn_t gfn)
+{
+       pfn_t pfn;
+
+       pfn = kvm_vcpu_gfn_to_pfn(vcpu, gfn);
+
+       return kvm_pfn_to_page(pfn);
+}
+EXPORT_SYMBOL_GPL(kvm_vcpu_gfn_to_page);
+
 void kvm_release_page_clean(struct page *page)
 {
        WARN_ON(is_error_page(page));
@@ -1530,13 +1572,13 @@ static int next_segment(unsigned long len, int offset)
                return len;
 }
 
-int kvm_read_guest_page(struct kvm *kvm, gfn_t gfn, void *data, int offset,
-                       int len)
+static int __kvm_read_guest_page(struct kvm_memory_slot *slot, gfn_t gfn,
+                                void *data, int offset, int len)
 {
        int r;
        unsigned long addr;
 
-       addr = gfn_to_hva_prot(kvm, gfn, NULL);
+       addr = gfn_to_hva_memslot_prot(slot, gfn, NULL);
        if (kvm_is_error_hva(addr))
                return -EFAULT;
        r = __copy_from_user(data, (void __user *)addr + offset, len);
@@ -1544,8 +1586,25 @@ int kvm_read_guest_page(struct kvm *kvm, gfn_t gfn, void *data, int offset,
                return -EFAULT;
        return 0;
 }
+
+int kvm_read_guest_page(struct kvm *kvm, gfn_t gfn, void *data, int offset,
+                       int len)
+{
+       struct kvm_memory_slot *slot = gfn_to_memslot(kvm, gfn);
+
+       return __kvm_read_guest_page(slot, gfn, data, offset, len);
+}
 EXPORT_SYMBOL_GPL(kvm_read_guest_page);
 
+int kvm_vcpu_read_guest_page(struct kvm_vcpu *vcpu, gfn_t gfn, void *data,
+                            int offset, int len)
+{
+       struct kvm_memory_slot *slot = kvm_vcpu_gfn_to_memslot(vcpu, gfn);
+
+       return __kvm_read_guest_page(slot, gfn, data, offset, len);
+}
+EXPORT_SYMBOL_GPL(kvm_vcpu_read_guest_page);
+
 int kvm_read_guest(struct kvm *kvm, gpa_t gpa, void *data, unsigned long len)
 {
        gfn_t gfn = gpa >> PAGE_SHIFT;
@@ -1566,15 +1625,33 @@ int kvm_read_guest(struct kvm *kvm, gpa_t gpa, void *data, unsigned long len)
 }
 EXPORT_SYMBOL_GPL(kvm_read_guest);
 
-int kvm_read_guest_atomic(struct kvm *kvm, gpa_t gpa, void *data,
-                         unsigned long len)
+int kvm_vcpu_read_guest(struct kvm_vcpu *vcpu, gpa_t gpa, void *data, unsigned long len)
 {
-       int r;
-       unsigned long addr;
        gfn_t gfn = gpa >> PAGE_SHIFT;
+       int seg;
        int offset = offset_in_page(gpa);
+       int ret;
+
+       while ((seg = next_segment(len, offset)) != 0) {
+               ret = kvm_vcpu_read_guest_page(vcpu, gfn, data, offset, seg);
+               if (ret < 0)
+                       return ret;
+               offset = 0;
+               len -= seg;
+               data += seg;
+               ++gfn;
+       }
+       return 0;
+}
+EXPORT_SYMBOL_GPL(kvm_vcpu_read_guest);
 
-       addr = gfn_to_hva_prot(kvm, gfn, NULL);
+static int __kvm_read_guest_atomic(struct kvm_memory_slot *slot, gfn_t gfn,
+                                  void *data, int offset, unsigned long len)
+{
+       int r;
+       unsigned long addr;
+
+       addr = gfn_to_hva_memslot_prot(slot, gfn, NULL);
        if (kvm_is_error_hva(addr))
                return -EFAULT;
        pagefault_disable();
@@ -1584,25 +1661,63 @@ int kvm_read_guest_atomic(struct kvm *kvm, gpa_t gpa, void *data,
                return -EFAULT;
        return 0;
 }
-EXPORT_SYMBOL(kvm_read_guest_atomic);
 
-int kvm_write_guest_page(struct kvm *kvm, gfn_t gfn, const void *data,
-                        int offset, int len)
+int kvm_read_guest_atomic(struct kvm *kvm, gpa_t gpa, void *data,
+                         unsigned long len)
+{
+       gfn_t gfn = gpa >> PAGE_SHIFT;
+       struct kvm_memory_slot *slot = gfn_to_memslot(kvm, gfn);
+       int offset = offset_in_page(gpa);
+
+       return __kvm_read_guest_atomic(slot, gfn, data, offset, len);
+}
+EXPORT_SYMBOL_GPL(kvm_read_guest_atomic);
+
+int kvm_vcpu_read_guest_atomic(struct kvm_vcpu *vcpu, gpa_t gpa,
+                              void *data, unsigned long len)
+{
+       gfn_t gfn = gpa >> PAGE_SHIFT;
+       struct kvm_memory_slot *slot = kvm_vcpu_gfn_to_memslot(vcpu, gfn);
+       int offset = offset_in_page(gpa);
+
+       return __kvm_read_guest_atomic(slot, gfn, data, offset, len);
+}
+EXPORT_SYMBOL_GPL(kvm_vcpu_read_guest_atomic);
+
+static int __kvm_write_guest_page(struct kvm_memory_slot *memslot, gfn_t gfn,
+                                 const void *data, int offset, int len)
 {
        int r;
        unsigned long addr;
 
-       addr = gfn_to_hva(kvm, gfn);
+       addr = gfn_to_hva_memslot(memslot, gfn);
        if (kvm_is_error_hva(addr))
                return -EFAULT;
        r = __copy_to_user((void __user *)addr + offset, data, len);
        if (r)
                return -EFAULT;
-       mark_page_dirty(kvm, gfn);
+       mark_page_dirty_in_slot(memslot, gfn);
        return 0;
 }
+
+int kvm_write_guest_page(struct kvm *kvm, gfn_t gfn,
+                        const void *data, int offset, int len)
+{
+       struct kvm_memory_slot *slot = gfn_to_memslot(kvm, gfn);
+
+       return __kvm_write_guest_page(slot, gfn, data, offset, len);
+}
 EXPORT_SYMBOL_GPL(kvm_write_guest_page);
 
+int kvm_vcpu_write_guest_page(struct kvm_vcpu *vcpu, gfn_t gfn,
+                             const void *data, int offset, int len)
+{
+       struct kvm_memory_slot *slot = kvm_vcpu_gfn_to_memslot(vcpu, gfn);
+
+       return __kvm_write_guest_page(slot, gfn, data, offset, len);
+}
+EXPORT_SYMBOL_GPL(kvm_vcpu_write_guest_page);
+
 int kvm_write_guest(struct kvm *kvm, gpa_t gpa, const void *data,
                    unsigned long len)
 {
@@ -1624,6 +1739,27 @@ int kvm_write_guest(struct kvm *kvm, gpa_t gpa, const void *data,
 }
 EXPORT_SYMBOL_GPL(kvm_write_guest);
 
+int kvm_vcpu_write_guest(struct kvm_vcpu *vcpu, gpa_t gpa, const void *data,
+                        unsigned long len)
+{
+       gfn_t gfn = gpa >> PAGE_SHIFT;
+       int seg;
+       int offset = offset_in_page(gpa);
+       int ret;
+
+       while ((seg = next_segment(len, offset)) != 0) {
+               ret = kvm_vcpu_write_guest_page(vcpu, gfn, data, offset, seg);
+               if (ret < 0)
+                       return ret;
+               offset = 0;
+               len -= seg;
+               data += seg;
+               ++gfn;
+       }
+       return 0;
+}
+EXPORT_SYMBOL_GPL(kvm_vcpu_write_guest);
+
 int kvm_gfn_to_hva_cache_init(struct kvm *kvm, struct gfn_to_hva_cache *ghc,
                              gpa_t gpa, unsigned long len)
 {
@@ -1681,7 +1817,7 @@ int kvm_write_guest_cached(struct kvm *kvm, struct gfn_to_hva_cache *ghc,
        r = __copy_to_user((void __user *)ghc->hva, data, len);
        if (r)
                return -EFAULT;
-       mark_page_dirty_in_slot(kvm, ghc->memslot, ghc->gpa >> PAGE_SHIFT);
+       mark_page_dirty_in_slot(ghc->memslot, ghc->gpa >> PAGE_SHIFT);
 
        return 0;
 }
@@ -1739,8 +1875,7 @@ int kvm_clear_guest(struct kvm *kvm, gpa_t gpa, unsigned long len)
 }
 EXPORT_SYMBOL_GPL(kvm_clear_guest);
 
-static void mark_page_dirty_in_slot(struct kvm *kvm,
-                                   struct kvm_memory_slot *memslot,
+static void mark_page_dirty_in_slot(struct kvm_memory_slot *memslot,
                                    gfn_t gfn)
 {
        if (memslot && memslot->dirty_bitmap) {
@@ -1755,10 +1890,19 @@ void mark_page_dirty(struct kvm *kvm, gfn_t gfn)
        struct kvm_memory_slot *memslot;
 
        memslot = gfn_to_memslot(kvm, gfn);
-       mark_page_dirty_in_slot(kvm, memslot, gfn);
+       mark_page_dirty_in_slot(memslot, gfn);
 }
 EXPORT_SYMBOL_GPL(mark_page_dirty);
 
+void kvm_vcpu_mark_page_dirty(struct kvm_vcpu *vcpu, gfn_t gfn)
+{
+       struct kvm_memory_slot *memslot;
+
+       memslot = kvm_vcpu_gfn_to_memslot(vcpu, gfn);
+       mark_page_dirty_in_slot(memslot, gfn);
+}
+EXPORT_SYMBOL_GPL(kvm_vcpu_mark_page_dirty);
+
 static int kvm_vcpu_check_block(struct kvm_vcpu *vcpu)
 {
        if (kvm_arch_vcpu_runnable(vcpu)) {
@@ -2487,6 +2631,10 @@ static long kvm_vm_ioctl_check_extension_generic(struct kvm *kvm, long arg)
 #ifdef CONFIG_HAVE_KVM_IRQ_ROUTING
        case KVM_CAP_IRQ_ROUTING:
                return KVM_MAX_IRQ_ROUTES;
+#endif
+#if KVM_ADDRESS_SPACE_NUM > 1
+       case KVM_CAP_MULTI_ADDRESS_SPACE:
+               return KVM_ADDRESS_SPACE_NUM;
 #endif
        default:
                break;
@@ -2882,18 +3030,12 @@ static int hardware_enable_all(void)
 static int kvm_cpu_hotplug(struct notifier_block *notifier, unsigned long val,
                           void *v)
 {
-       int cpu = (long)v;
-
        val &= ~CPU_TASKS_FROZEN;
        switch (val) {
        case CPU_DYING:
-               pr_info("kvm: disabling virtualization on CPU%d\n",
-                      cpu);
                hardware_disable();
                break;
        case CPU_STARTING:
-               pr_info("kvm: enabling virtualization on CPU%d\n",
-                      cpu);
                hardware_enable();
                break;
        }